summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/extensions/SemanticMediaWiki
first commit
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/.gitignore14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/.jshintignore9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/.jshintrc32
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/.scrutinizer.yml26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/.travis.yml86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/COPYING355
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/DefaultSettings.php2352
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/README.md81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/SemanticMediaWiki.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/composer.json111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/elastic/default-profile.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-icu.json283
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-standard.json274
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-lookup.json17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/default.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.homepage.txt4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.knows.txt4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.name.txt4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/properties/owl.differentFrom.txt4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/foaf.txt13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/owl.txt48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/skos.txt35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/schema/link-format-schema.v1.json119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/data/schema/search-form-schema.v1.json34
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/COMPATIBILITY.md370
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/CONTRIBUTING.md51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/INSTALL.md138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/ISSUE_TEMPLATE.md21
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/PULL_REQUEST_TEMPLATE.md12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/README.md23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/RELEASE-NOTES.md24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/README.md36
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.1.md10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.md50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.1.md24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.md56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.8.md79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.1.md19
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.2.md11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.1.md11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.md44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.2.md37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.md142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.0.md127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.0.md92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.1.md9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.2.md9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.3.md7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.0.md64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.1.md12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.2.md8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.3.md7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.0.md133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.1.md12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.0.md200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.1.md14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.2.md13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.3.md9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.4.md8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.5.md11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.6.md7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.0.md209
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.1.md51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.2.md22
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.3.md35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.4.md15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.5.md11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.6.md10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.7.md11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.8.md13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.0.md375
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.1.md39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.2.md24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-old.md723
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/README.md37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/api.md175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/README.md10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/approve.update.md90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.datatype.inittypes.md59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.fileupload.beforeupdate.md28
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.pagecontentsavecomplete.md99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/match.property.values.md63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/phpunit.test.property.md69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.description.md40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.someproperty.of.type.number.md71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/register.datatype.md66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/semanticdata.access.md71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/store.subobject.md73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.installer.md1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.serializers.md133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/doxygen.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/hooks.md405
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.categories.json83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/docs/technical/migration-guide-3.0.md67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/extension.json29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ace.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ady-cyrl.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/aeb-latn.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/af.json51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/aln.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/am.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/an.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ang.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/anp.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ar.json856
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/arc.json51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/arq.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ary.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/arz.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/as.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ast.json659
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/avk.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/awa.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/az.json18
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/azb.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ba.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bar.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bcc.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bcl.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/be-tarask.json420
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/be.json12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bg.json68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bgn.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bho.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bjn.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bn.json41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/br.json243
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bs.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/bug.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ca.json736
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/cdo.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ce.json84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ch.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ckb.json43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/crh-cyrl.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/crh-latn.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/cs.json283
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/cu.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/cv.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/cy.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/da.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/de-ch.json19
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/de-formal.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/de.json859
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/din.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/diq.json41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/dsb.json268
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/dty.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ee.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/el.json567
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/en-gb.json19
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/en.json840
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/eo.json107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/es-formal.json60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/es.json816
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/et.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/eu.json328
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ext.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.alias.php836
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.magic.php409
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/ar.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/arz.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/ca.json200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-at.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-ch.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-formal.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/de.json258
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/en.json304
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/es.json202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/fi.json183
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/fr.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/he.json188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/hu.json181
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/id.json187
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/it.json191
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/ja.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/nb.json201
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/nl.json193
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/pl.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/pt.json209
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/ru.json192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/sk.json188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-cn.json192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hans.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hant.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-tw.json192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fa.json391
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fi.json362
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fo.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fr.json881
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/frp.json114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/frr.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fur.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/fy.json21
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ga.json17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gan-hans.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gan-hant.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gd.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gl.json772
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/glk.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gom-deva.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gom-latn.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/grc.json29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gsw.json194
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/gu.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hak.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/haw.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/he.json517
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hi.json222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hif-latn.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hil.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hr.json190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hrx.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hsb.json272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ht.json27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hu.json238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/hy.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ia.json299
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/id.json214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ig.json15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ilo.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/io.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/is.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/it.json436
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ja.json427
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/jam.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/jbo.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/jut.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/jv.json98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ka.json90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kab.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/khw.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kiu.json7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kk-arab.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kk-cyrl.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kk-latn.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/km.json87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kn.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ko.json609
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/krc.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kri.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/krj.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ksh.json235
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ku-latn.json17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/kw.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ky.json20
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/la.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lad.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lb.json328
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lez.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/li.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lij.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lki.json15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lrc.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lt.json188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/luz.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lv.json26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/lzh.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mai.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mdf.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mg.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/min.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mk.json383
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ml.json34
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mn.json16
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mr.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ms.json56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mt.json17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mwl.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/my.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/myv.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/mzn.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nah.json15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nan.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nap.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nb.json456
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nds-nl.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nds.json12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ne.json12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/niu.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nl.json506
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/nn.json160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/oc.json232
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/olo.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/om.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/or.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/os.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pa.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pag.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pam.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pdc.json20
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pfl.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pl.json556
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pms.json241
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pnb.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/prg.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ps.json42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pt-br.json863
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/pt.json857
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/qqq.json867
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/qu.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/rif.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/rm.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ro.json129
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/roa-tara.json58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ru.json821
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/rue.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sa.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sah.json12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sc.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/scn.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sco.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sd.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sdc.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sgs.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sh.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/shn.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/si.json152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sk.json183
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sl.json133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sli.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sma.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sq.json23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sr-ec.json343
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sr-el.json263
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/stq.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/su.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sv.json555
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/sw.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/szl.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ta.json59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tcy.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/te.json66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tet.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tg-cyrl.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tg-latn.json5
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/th.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tk.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tl.json246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tly.json12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tr.json156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tt-cyrl.json34
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tt-latn.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/tyv.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ug-arab.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ug-latn.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/uk.json762
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/ur.json35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/uz.json17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/vec.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/vep.json30
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/vi.json185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/vo.json85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/vro.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/wa.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/war.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/wo.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/wuu.json8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/xal.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/xmf.json10
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/yi.json21
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/yo.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/yue.json9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/zh-hans.json804
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/i18n/zh-hant.json863
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php251
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php370
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php374
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php666
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php225
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php355
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php379
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php782
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Settings.php613
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Setup.php441
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php509
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php609
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php299
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php602
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php300
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php699
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php379
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php751
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php897
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php328
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php768
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php675
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php333
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php277
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php294
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php295
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php552
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php530
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php275
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php250
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php187
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php280
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php387
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php462
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php282
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php653
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php501
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php825
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php1296
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/README.md106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/dumpRDF.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/populateHashField.php222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildConceptCache.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildData.php225
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildElasticIndex.php366
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildFulltextSearchTable.php177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildPropertyStatistics.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/removeDuplicateEntities.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/setupStore.php265
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/maintenance/updateEntityCollation.php167
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/phpcs.xml93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/phpmd.xml29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/phpunit.xml.dist74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/DataURI.php14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/Resources.php664
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/docs/jsduck.external.js37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.async.js69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.css74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.js1212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.autocomplete.js998
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.caret.js436
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.jstorage.js996
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.md5.js275
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.css485
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.js2967
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.css854
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.js1597
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/api/ext.smw.api.js163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.data.js183
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.geo.js88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.number.js100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.property.js117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.text.js104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.time.js246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.unknown.js91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.uri.js106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.wikiPage.js211
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataValue.quantity.js116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.css61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.js242
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.skin.css3
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.css1053
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.dropdown.css153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.factbox.css214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.js375
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.page.css420
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.skin.css79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.table.css78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.tabs.css85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.css291
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js335
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.skin.css41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/query/ext.smw.query.js198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/smw.schema.css134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/smw.selectmenu.css119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/smw.ui.js59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.css327
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.form.js267
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.input.js52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.namespace.js73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.admin.js207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.css661
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.js372
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.css259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.js224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.skin.css11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.css111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/README.md66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.js259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.textInput.js206
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.css133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.js63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.personal.js44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.js157
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.page.js107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.property.js127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertysubject.js128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertyvalue.js134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.postproc.js97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.purge.js42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.css124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.js314
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.css160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.js100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/res/smw/util/smw.property.page.js50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Aliases.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ApplicationFactory.php591
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/CacheFactory.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/CachedPropertyValuesPrefetcher.php177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ChangePropListener.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/CompatibilityMode.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Connection/CallbackConnectionProvider.php53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionManager.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProvider.php30
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProviderRef.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataItemFactory.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataModel/ContainerSemanticData.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataModel/SubSemanticData.php287
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataTypeRegistry.php616
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataUpdater.php430
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValueFactory.php403
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/AbstractMultiValue.php171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsListValue.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsPatternValue.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsValue.php27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/BooleanValue.php256
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ErrorMsgTextValue.php111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalFormatterUriValue.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalIdentifierValue.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ImportValue.php231
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/InfoLinksProvider.php284
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/KeywordValue.php287
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/LanguageCodeValue.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/MonolingualTextValue.php363
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/IntlNumberFormatter.php384
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/UnitConverter.php261
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyChainValue.php220
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyValue.php501
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ReferenceValue.php294
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/StringValue.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/TelephoneUriValue.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/TemperatureValue.php221
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/CalendarModel.php29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Components.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/IntlTimeFormatter.php217
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/JulianDay.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Timezone.php388
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/TypesValue.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/UniquenessConstraintValue.php41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/CodeStringValueFormatter.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DataValueFormatter.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DispatchingDataValueFormatter.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/MonolingualTextValueFormatter.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NoValueFormatter.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NumberValueFormatter.php162
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/PropertyValueFormatter.php337
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ReferenceValueFormatter.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/StringValueFormatter.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/TimeValueFormatter.php402
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ValueFormatter.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsListValueParser.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsPatternValueParser.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ImportValueParser.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/MonolingualTextValueParser.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/PropertyValueParser.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/TimeValueParser.php369
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ValueParser.php29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/AllowsListConstraintValueValidator.php269
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/CompoundConstraintValueValidator.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/ConstraintValueValidator.php27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PatternConstraintValueValidator.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PropertySpecificationConstraintValueValidator.php105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/UniquenessConstraintValueValidator.php205
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Defines.php300
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DescriptionDeserializer.php180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializer.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializer.php126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializer.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializer.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializer.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializer.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializerRegistry.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/ExpDataDeserializer.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Deserializers/SemanticDataDeserializer.php198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/ElasticClientTaskHandler.php240
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/IndicesInfoProvider.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/InfoProviderHandler.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/MappingsInfoProvider.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/NodesInfoProvider.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/SettingsInfoProvider.php89
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Config.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/Client.php889
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/ConnectionProvider.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/DummyClient.php266
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticFactory.php426
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticStore.php353
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ClientBuilderNotFoundException.php22
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/InvalidJSONException.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/NoConnectionException.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ReplicationException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/AttachmentAnnotator.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Bulk.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIndexer.php479
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIngestJob.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Indexer.php777
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/IndexerRecoveryJob.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rebuilder.php421
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/ReplicationStatus.php128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rollover.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/Lookup/ProximityPropertyValueLookup.php310
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Aggregations.php114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Condition.php126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php464
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php464
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreter.php538
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php241
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Excerpts.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/FieldMapper.php676
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/QueryEngine.php369
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SearchResult.php219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SortBuilder.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup.php37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/CachingTermsLookup.php393
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/Parameters.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/TermsLookup.php415
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/README.md519
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Encoder.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/EntityLookup.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Enum.php28
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/EventHandler.php103
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/EventListenerRegistry.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemDeserializationException.php13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/DataTypeLookupException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotReadableException.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotWritableException.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/ParameterNotFoundException.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/PredefinedPropertyLabelMismatchException.php13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyLabelNotResolvedException.php13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/RedirectTargetUnresolvableException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/SemanticDataImportException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/SettingNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/StoreNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exception/SubSemanticDataException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ConceptMapper.php306
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/DataItemMatchFinder.php180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element.php46
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpElement.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpLiteral.php145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpNsResource.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpResource.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ElementFactory.php176
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/Escaper.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ExpResourceMapper.php295
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilder.php37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilder.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilder.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/DispatchingResourceBuilder.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilder.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilder.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilder.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilder.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilder.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilder.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilder.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyValueResourceBuilder.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilder.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/SortPropertyValueResourceBuilder.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilder.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Exporter/XsdValueMapper.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Factbox/CachedFactbox.php321
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Factbox/Factbox.php461
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Factbox/FactboxFactory.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/GlobalFunctions.php282
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/HashBuilder.php166
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/HierarchyLookup.php355
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreator.php31
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/DispatchingContentCreator.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/TextContentCreator.php150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/XmlContentCreator.php121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentIterator.php29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentModeller.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/ImportContents.php234
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/Importer.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonContentIterator.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonImportContentsFileDirReader.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Importer/README.md151
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/InMemoryPoolCache.php168
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/IteratorFactory.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Iterators/AppendIterator.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Iterators/ChunkedIterator.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Iterators/CsvFileIterator.php157
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Iterators/MappingIterator.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Iterators/ResultIterator.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Lang/FallbackFinder.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Lang/JsonContentsFileReader.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Lang/Lang.php600
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Lang/LanguageContents.php172
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Lang/README.md103
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Localizer.php394
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ConceptCacheRebuilder.php273
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DataRebuilder.php532
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DistinctEntityDataRebuilder.php294
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DuplicateEntitiesDisposer.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ExceptionFileLogger.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceFactory.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceHelper.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceLogger.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Maintenance/PropertyStatisticsRebuilder.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiQueryResultFormatter.php214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiRequestParameterFormatter.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Ask.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/AskArgs.php124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse.php396
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleAugmentor.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleLookup.php193
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/CachingLookup.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListAugmentor.php147
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListLookup.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/Lookup.php29
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PSubjectLookup.php219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PValueLookup.php145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/SubjectLookup.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseByProperty.php197
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseBySubject.php238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Info.php181
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/PropertyListByApiRequest.php297
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Query.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php431
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Collator.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/ConnectionProvider.php154
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Database.php907
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/LoadBalancerConnectionProvider.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/OptionsBuilder.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Query.php411
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Sequence.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/TransactionProfiler.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/DeepRedirectTargetResolver.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/CallableUpdate.php339
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/ChangeTitleUpdate.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/TransactionalCallableUpdate.php275
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/EditInfoProvider.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Exception/ExtendedPermissionsError.php28
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleDelete.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleFromTitle.php64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleProtectComplete.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticlePurge.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleViewHeader.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BaseTemplateToolbox.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforeDisplayNoArticleText.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforePageDisplay.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/EditPageForm.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionSchemaUpdates.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionTypes.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/FileUpload.php128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/GetPreferences.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookHandler.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookListener.php840
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookRegistry.php383
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/InternalParseBeforeLinks.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/NewRevisionFromEditComplete.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/OutputPageParserOutput.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ParserAfterTidy.php258
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/PersonalUrls.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/README.md39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/RejectParserCacheValue.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderGetConfigVars.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderTestModules.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinAfterContent.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinTemplateNavigation.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialSearchResultsPrepend.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialStatsAddExtra.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsAlwaysKnown.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsMovable.php60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleMoveComplete.php120
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/UserChange.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Job.php259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobFactory.php224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobQueue.php213
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationClassUpdateJob.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationDispatchJob.php424
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationUpdateJob.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/EntityIdDisposerJob.php124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableRebuildJob.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableUpdateJob.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/NullJob.php41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ParserCachePurgeJob.php264
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/PropertyStatisticsRebuildJob.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/RefreshJob.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateDispatcherJob.php368
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateJob.php322
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/LocalTime.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MagicWordsFinder.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/ManualEntryLogger.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MediaWikiNsContentReader.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MessageBuilder.php129
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MwCollaboratorFactory.php229
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageCreator.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageInfoProvider.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageUpdater.php357
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/RedirectTargetFinder.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlColumnListRenderer.php296
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlFormRenderer.php513
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTableRenderer.php290
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTemplateRenderer.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/WikitextTemplateRenderer.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/CustomForm.php189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/Field.php242
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsBuilder.php358
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFactory.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFinder.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/NamespaceForm.php192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/OpenForm.php173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/SortForm.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/QueryBuilder.php290
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/README.md161
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Search.php474
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchProfileForm.php433
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResult.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResultSet.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandler.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/ConfigurationListTaskHandler.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DataRefreshJobTaskHandler.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandler.php281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DisposeJobTaskHandler.php174
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DuplicateLookupTaskHandler.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/EntityLookupTaskHandler.php325
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandler.php162
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandler.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OutputFormatter.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandler.php162
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/SupportListTaskHandler.php124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TableSchemaTaskHandler.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandler.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandlerFactory.php240
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/DownloadLinksWidget.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ErrorWidget.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/FormatListWidget.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HelpWidget.php103
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HtmlForm.php381
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/LinksWidget.php375
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/NavigationLinksWidget.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParameterInput.php326
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersProcessor.php273
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersWidget.php359
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/QueryInputWidget.php53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/SortWidget.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/UrlArgs.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/FieldBuilder.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/GroupFormatter.php237
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/HtmlBuilder.php947
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/ValueFormatter.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PageProperty/PageBuilder.php180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilder.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageBuilder.php395
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageRequestOptions.php172
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/QueryResultLookup.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAdmin.php260
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAsk.php714
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialBrowse.php224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialDeferredRequestDispatcher.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPageProperty.php173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialProcessingErrorList.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPropertyLabelSimilarity.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialSearchByProperty.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialURIResolver.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/StripMarkerDecoder.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleFactory.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleLookup.php202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Message.php254
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/NamespaceExaminer.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/NamespaceManager.php268
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/NamespaceUriFinder.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Options.php181
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/ConceptPage.php207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ListBuilder.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ValueListBuilder.php392
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/ListPager.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/Page.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/PageFactory.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Page/PropertyPage.php380
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PageInfo.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParameterListDocBuilder.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParameterProcessorFactory.php41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parameters.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parser/InTextAnnotationParser.php462
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksEncoder.php235
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksProcessor.php206
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parser/RecursiveTextProcessor.php360
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Parser/SemanticLinksParser.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserData.php512
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctionFactory.php521
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/AskParserFunction.php450
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ConceptParserFunction.php198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DeclareParserFunction.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DocumentationParserFunction.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ExpensiveFuncExecutionWatcher.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/InfoParserFunction.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/RecurringEventsParserFunction.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SectionTag.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SetParserFunction.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ShowParserFunction.php60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SubobjectParserFunction.php336
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ParserParameterProcessor.php336
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PermissionPthValidator.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PostProcHandler.php362
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/ProcessingErrorMsgHandler.php246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAliasFinder.php191
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotator.php35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotatorFactory.php226
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/CategoryPropertyAnnotator.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/DisplayTitlePropertyAnnotator.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/EditProtectedPropertyAnnotator.php159
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/MandatoryTypePropertyAnnotator.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/NullPropertyAnnotator.php53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PredefinedPropertyAnnotator.php114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PropertyAnnotatorDecorator.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/RedirectPropertyAnnotator.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SchemaPropertyAnnotator.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SortKeyPropertyAnnotator.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/TranslationPropertyAnnotator.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyLabelFinder.php242
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyRegistry.php499
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertyRestrictionExaminer.php199
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php507
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqExaminer.php295
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqMsgBuilder.php323
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Protection/EditProtectionUpdater.php207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Protection/ProtectionValidator.php222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/DebugFormatter.php261
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Deferred.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/DescriptionFactory.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/FingerprintNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/ResultFormatNotFoundException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Excerpts.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ExportPrinter.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ClassDescription.php210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ConceptDescription.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Conjunction.php182
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Description.php207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Disjunction.php239
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/NamespaceDescription.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/SomeProperty.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ThingDescription.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ValueDescription.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Parser.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/DescriptionProcessor.php307
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php847
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/TermParser.php253
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/Tokenizer.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest.php362
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Deserializer.php160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Formatter.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Serializer.php164
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequestFactory.php65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/DefaultParamDefinition.php161
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/ParamListProcessor.php263
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/QueryCreator.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotator.php37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotatorFactory.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DescriptionProfileAnnotator.php65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DurationProfileAnnotator.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/FormatProfileAnnotator.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/NullProfileAnnotator.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ParametersProfileAnnotator.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ProfileAnnotatorDecorator.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SchemaLinkProfileAnnotator.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SourceProfileAnnotator.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/StatusCodeProfileAnnotator.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QueryComparator.php193
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QueryContext.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QueryLinker.php120
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QuerySourceFactory.php111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QueryStringifier.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/QueryToken.php166
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/RemoteRequest.php336
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Result/CachedQueryResultPrefetcher.php552
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResolverJournal.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php357
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/Result/StringResult.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinter.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CategoryResultPrinter.php322
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CsvFileExportPrinter.php180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FeedExportPrinter.php453
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FileExportPrinter.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ListResultBuilder.php266
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionary.php53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryUser.php35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/RowBuilder.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/SimpleRowBuilder.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRendererFactory.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRowBuilder.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ValueTextsBuilder.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/NullResultPrinter.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ResultPrinter.php749
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TableResultPrinter.php380
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TemplateFileExportPrinter.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Query/ScoreSet.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/QueryEngine.php35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/QueryFactory.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/RequestOptions.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Rule/Rule.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/BadHttpEndpointResponseException.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/HttpEndpointConnectionException.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/XmlParserException.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseErrorMapper.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseParser.php25
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/Condition.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FalseCondition.php25
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FilterCondition.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/SingletonCondition.php55
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/TrueCondition.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/WhereCondition.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/ConditionBuilder.php629
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreter.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreterFactory.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php248
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php256
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php268
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/EngineOptions.php28
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryResultFactory.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/RepositoryResult.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/XmlResponseParser.php234
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/README.md101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/ReplicationDataTruncator.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryClient.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnection.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectionProvider.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnector.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnector.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/GenericRepositoryConnector.php594
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnector.php175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryRedirectLookup.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php486
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStoreFactory.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/TurtleTriplesBuilder.php333
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeDiff.php255
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeOp.php371
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/FieldChangeOp.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/TableChangeOp.php159
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangePropagationEntityFinder.php189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ConceptCache.php270
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityRebuildDispatcher.php512
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingEntityLookup.php437
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBlobHandler.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBooleanHandler.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIConceptHandler.php126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIGeoCoordinateHandler.php128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DINumberHandler.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DITimeHandler.php136
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIUriHandler.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIWikiPageHandler.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandler.php211
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandlerDispatcher.php123
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/Exception/DataItemHandlerException.php16
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdCacheManager.php226
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdChanger.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdEntityFinder.php202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/NativeEntityLookup.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertiesLookup.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertySubjectsLookup.php318
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SemanticDataLookup.php478
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/StubSemanticData.php384
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SubobjectListFinder.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/TraversalPropertyLookup.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/UniquenessLookup.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityValueUniquenessConstraintChecker.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/PropertyStatisticsInvalidArgumentException.php15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/TableMissingIdFieldException.php22
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php463
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/CachedListLookup.php200
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ListLookup.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyLabelSimilarityLookup.php318
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyUsageListLookup.php150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ProximityPropertyValueLookup.php266
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/RedirectTargetLookup.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UndeclaredPropertyListLookup.php176
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UnusedPropertyListLookup.php154
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UsageStatisticsListLookup.php352
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyStatisticsStore.php369
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinition.php175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinitionBuilder.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceDisposer.php284
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceFinder.php267
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableInfoFetcher.php276
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowDiffer.php313
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php310
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php231
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTypeFinder.php111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksTableUpdater.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksUpdateJournal.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilter.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryDependencyLinksStore.php567
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryReferenceBacklinks.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryResultDependencyListResolver.php285
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependencyLinksStoreFactory.php150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/ConceptQuerySegmentBuilder.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreter.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreterFactory.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapper.php64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php197
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreter.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php319
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/EngineOptions.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilder.php147
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilder.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTable.php281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableRebuilder.php335
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableUpdater.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextChangeUpdater.php312
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextSanitizer.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilder.php129
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/FulltextSearchTableFactory.php177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/HierarchyTempTableBuilder.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QueryEngine.php573
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegment.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuildManager.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuilder.php276
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListProcessor.php349
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/README.md61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngineFactory.php175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/README.md12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RedirectStore.php274
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RequestOptionsProc.php304
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/SQLStoreFactory.php762
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Examiner/HashField.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/FieldType.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/MySQLTableBuilder.php402
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/PostgresTableBuilder.php423
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/SQLiteTableBuilder.php373
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Table.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TableBuilder.php246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TemporaryTableBuilder.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableFieldUpdater.php89
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php344
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php346
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/Content.php275
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentFormatter.php270
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentHandler.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/HtmlBuilder.php217
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaConstructionFailedException.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaTypeNotFoundException.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/README.md26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/Schema.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaDefinition.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaFactory.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaValidator.php54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SerializerFactory.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Serializers/ExpDataSerializer.php65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Serializers/FlatSemanticDataSerializer.php32
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Serializers/QueryResultSerializer.php290
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Serializers/SemanticDataSerializer.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServiceFactory.php243
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServices.php310
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/Exception/ServiceNotFoundException.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServiceFactory.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServices.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/MediaWikiServices.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/README.md27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/ServicesContainer.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Services/SharedServicesContainer.php705
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Site.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Store.php582
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/StoreAware.php22
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/StoreFactory.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/StringCondition.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/TypesRegistry.php310
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/BufferedStatsdCollector.php235
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/CharArmor.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/CharExaminer.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/CircularReferenceGuard.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Csv.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/ErrorCodeFormatter.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/File.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HmacSerializer.php174
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlColumns.php335
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlDivTable.php151
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlModal.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTable.php179
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTabs.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlVTabs.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Image.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/JsonSchemaValidator.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Logger.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Lru.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Normalizer.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/StatsFormatter.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/TempFile.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Timer.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/README.md126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/autoloader.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/bootstrap.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpUnitEnvironment.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkJsonScriptRunnerTest.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkReporter.php22
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Benchmarker.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/CliOutputFormatter.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-001.xml342
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-002.xml214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/JobQueueBenchmarkRunner.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/MaintenanceBenchmarkRunner.php114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageContentCopyBenchmarkRunner.php164
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageEditCopyBenchmarkRunner.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageImportBenchmarkRunner.php120
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/QueryBenchmarkRunner.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/README.md56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-001.json53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-002.json186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/phpunit.quick.xml.dist39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/DatabaseTestCase.php234
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/ExecutionTimeTestListener.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/EncodingIntegrationTest.php109
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/content.json26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/foaf.txt13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/content.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/test.xml68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/ImporterIntegrationTest.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/InterwikiDBIntegrationTest.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ApiTestCaseProcessor.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/P106.txt24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.0.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.1.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.2.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.3.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.4.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.5.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.6.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/file-upload.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-480.pngbin0 -> 97253 bytes
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-88.pngbin0 -> 4024 bytes
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/numeric-sorting.txt20
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.1.txt20
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.2.txt27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.3.txt11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0303.txt7
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.de.txt11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.fr.txt13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0444.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0459-keyword-formatter-schema.json15
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-error.txt2
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-other-license.json4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502.txt49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.friends.txt109
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.persons.txt46
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.visits.txt27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-1.txt3
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-2.txt3
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.10.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.11.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.7.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.8.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.9.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.0.json1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.1.json1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.2.json1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.3.json1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.0.csv5
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.1.csv4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.2.csv5
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.3.csv4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.4.csv4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.5.csv5
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.6.csv4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.7.csv4
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.0.txt12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.1.txt13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.0.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.1.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.2.txt1
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon-intro.txt11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon.txt2
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/skos-import.txt33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php510
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserHtmlTestCaseProcessor.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserTestCaseProcessor.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseInterpreter.php297
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseProcessor.php272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/README.md612
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/RdfTestCaseProcessor.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/SpecialPageTestCaseProcessor.php199
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0001.json301
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0002.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0003.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0001.json108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0101.json125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0102.json81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0103.json124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0104.json159
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0105.json125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0201.json178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0202.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0203.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0204.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0205.json93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0206.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0207.json202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0208.json180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0209.json73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0210.json76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0211.json230
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0301.json212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0302.json108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0303.json150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0304.json198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0305.json190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0306.json234
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0307.json150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0308.json93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0401.json68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0402.json540
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0801.json247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0802.json81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0803.json169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0804.json83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0805.json63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0101.json174
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0102.json258
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0106.json67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0107.json145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0108.json117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0109.json54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0110.json43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0111.json35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0112.json58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0113.json126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0114.json85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0115.json130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0202.json84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0203.json104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0204.json54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0205.json87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0206.json105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0207.json89
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0208.json236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0209.json247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0210.json90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0212.json102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0301.json62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0302.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0303.json70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0401.json153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0402.json239
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0403.json118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0404.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0405.json212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0406.json60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0407.json61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0408.json45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0409.json217
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0410.json458
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0411.json151
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0412.json76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0413.json882
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0414.json395
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0415.json65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0416.json281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0417.json88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0418.json85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0419.json285
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0420.json108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0421.json55
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0422.json124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0423.json173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0424.json115
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0425.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0426.json104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0427.json146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0428.json47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0429.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0430.json133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0431.json84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0432.json186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0433.json87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0434.json215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0435.json66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0436.json114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0437.json42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0438.json61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0439.json122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0440.json187
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0441.json47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0442.json50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0443.json198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0444.json207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0445.json63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0446.json257
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0447.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0448.json74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0449.json156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0450.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0451.json105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0452.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0453.json41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0454.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0455.json244
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0456.json108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0457.json70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0458.json72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0459.json119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0460.json218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0461.json92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0501.json35
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0502.json194
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0503.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0504.json49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0701.json76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0702.json99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0703.json64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0704.json56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0706.json183
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0707.json45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0708.json61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0709.json104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0710.json66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0711.json149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0712.json64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0901.json112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0902.json65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0903.json70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0904.json98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0905.json103
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0906.json69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0907.json81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0908.json48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0909.json265
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0910.json134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0911.json64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0912.json171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0913.json221
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0914.json88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0915.json112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0916.json62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1000.json127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1001.json97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1002.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1003.json72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1004.json59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1005.json53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1006.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1007.json57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0101.json127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0102.json263
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0103.json223
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0104.json274
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0105.json338
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0106.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0201.json130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0202.json123
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0203.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0204.json109
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0301.json134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0401.json163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0402.json248
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0501.json130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0502.json84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0503.json87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0601.json77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0602.json48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0603.json230
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0604.json192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0605.json141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0606.json249
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0607.json116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0608.json123
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0609.json78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0610.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0611.json62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0612.json100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0613.json182
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0614.json109
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0615.json148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0616.json128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0617.json127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0618.json133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0619.json80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0620.json134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0621.json105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0622.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0623.json85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0701.json186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0702.json177
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0703.json77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0704.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0801.json47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0802.json160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0803.json140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0804.json34
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0901.json379
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0902.json65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0903.json347
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0904.json142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0905.json209
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0906.json163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0907.json111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0908.json219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0909.json180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0910.json113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0911.json54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1002.json167
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1003.json165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1004.json175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1101.json189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1102.json95
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1103.json100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1104.json86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1105.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1106.json116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1107.json84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1108.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1200.json210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1201.json114
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1202.json186
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1203.json271
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1204.json204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1205.json102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1206.json142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1300.json80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0001.json205
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0002.json86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0003.json208
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0004.json92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0005.json47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0006.json127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0007.json125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0008.json86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0009.json85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0010.json104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0011.json112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0012.json100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0013.json105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0014.json76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0015.json77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0016.json105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0017.json104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0018.json46
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0019.json52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0020.json45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0001.json68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0002.json586
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0003.json131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0004.json49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0005.json83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0006.json50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0007.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0008.json161
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0009.json67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0010.json56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0011.json136
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0012.json66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0013.json48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0014.json240
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0015.json71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0016.json128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0017.json79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0018.json93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0019.json40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0020.json121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0021.json81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0022.json248
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0023.json44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0024.json49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0025.json106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0026.json47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0027.json179
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0028.json94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0029.json185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0030.json50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0031.json95
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0032.json65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0033.json59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/bootstrap.json53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/readmeContentsBuilder.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/version2TestCaseConverter.php222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/en.json24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-fallback.json14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-partial.json11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/LanguageContentTest.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/PartialLanguageFallbackTest.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/Fixtures/test-import-19.7.xml270
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/PopulateHashFieldTest.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/RemoveDuplicateEntitiesTest.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/SetupStoreMaintenanceTest.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/ApiBrowseBySubjectDBIntegrationTest.php150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/FileUploadIntegrationTest.php135
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/InternalParseBeforeLinksIntegrationTest.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserAfterTidyIntegrationTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserFirstCallInitIntegrationTest.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/TitleMoveCompleteIntegrationTest.php206
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/CategoryInstanceAndCategoryHierarchyTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/CategoryInstanceAndCategoryHierarchyTest-Mw-1-19-7.xml175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/GenericLoremIpsumTest-Mw-1-19-7.xml270
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/PageWithTemplateInclusionTest-Mw-1-19-7.xml112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RecordDataTypeTest-Mw-1-19-7.xml279
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RedirectPageTest-Mw-1-19-7.xml113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/TimeDataTypeTest-Mw-1-19-7.xml159
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/cicero-de-finibus.xml69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/dwc-import-example.xml307
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/DumpRdfMaintenanceTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildConceptCacheMaintenanceTest.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildDataMaintenanceTest.php175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildFulltextSearchTableTest.php60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildPropertyStatisticsMaintenanceTest.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/UpdateEntityCollationTest.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/PageWithTemplateInclusionTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RecordDataTypeTest.php228
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RedirectPageTest.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/TimeDataTypeTest.php281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php179
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/UpdateJobRoundtripTest.php231
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateEmptyParserOutputDBIntegrationTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateSQLStoreDBIntegrationTest.php249
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateTest.php216
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MWNamespaceCanonicalNameMatchTest.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MediaWikiIntegrationForRegisteredHookTest.php176
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/PredefinedPropertyAnnotationDBIntegrationTest.php105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/RedirectTargetFinderIntegrationTest.php260
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/SearchInPageDBIntegrationTest.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Parser/InTextAnnotationParserTemplateTransclusionTest.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/PropertyLabelCanonicalMatchTest.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/CategoryClassQueryDBIntegrationTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ComparatorFilterConditionQueryDBIntegrationTest.php335
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ConjunctionQueryDBIntegrationTest.php246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DatePropertyValueQueryDBIntegrationTest.php173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DisjunctionQueryDBIntegrationTest.php238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/GeneralQueryDBIntegrationTest.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/InversePropertyRelationshipDBIntegrationTest.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NamespaceQueryDBIntegrationTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NullQueryResultTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ProfileAnnotators/ProfileAnnotatorWithQueryProcessorIntegrationTest.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QueryResultPrinterIntegrationTest.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QuerySourceIntegrationTest.php192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/RandomQueryResultOrderIntegrationTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SortableQueryDBIntegrationTest.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SpecialCharactersQueryDBIntegrationTest.php164
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/QueryResultQueryProcessorIntegrationTest.php214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/RdfFileResourceTest.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/QueryResultLookupWithoutBaseStoreIntegrationTest.php219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/RepositoryRedirectLookupActiveConnectionTest.php111
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/RefreshSQLStoreDBIntegrationTest.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/SubSemanticDataDBIntegrationTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/TableBuilder/TableBuilderIntegrationTest.php318
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializationDBIntegrationTest.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializerDeserializerRoundtripTest.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSortKeyUpdateDBIntegrationTest.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataStorageDBIntegrationTest.php327
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticMediaWikiProvidedHookInterfaceIntegrationTest.php471
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SpecialAskTest.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nJsonFileIntegrityTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nMsgKeyIntegrityTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/InstallationConfigurationIntegrityTest.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/LanguagesAccessibilityAndIntegrityTest.php107
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/ResourcesAccessibilityTest.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/TempFileRoundTripTest.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseContentHandler.php192
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseFileHandler.php466
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseScriptRunner.php322
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/PHPUnitCompat.php31
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterRegistryTestCase.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterTestCase.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SemanticMediaWikiTestCase.php260
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SpecialPageTestCase.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestConfig.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestEnvironment.php248
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ApplicationFactoryTest.php328
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CacheFactoryTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CachedPropertyValuesPrefetcherTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ChangePropListenerTest.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CompatibilityModeTest.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/CallbackConnectionProviderTest.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionManagerTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionProviderRefTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataItemFactoryTest.php164
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/ContainerSemanticDataTest.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/SubSemanticDataTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataTypeRegistryTest.php454
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataUpdaterTest.php352
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValueFactoryTest.php341
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsListValueTest.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsPatternValueTest.php220
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/BooleanValueTest.php205
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ErrorMsgTextValueTest.php121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalFormatterUriValueTest.php169
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalIdentifierValueTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ImportValueTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/InfoLinksProviderTest.php251
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/KeywordValueTest.php163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/LanguageCodeValueTest.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/MonolingualTextValueTest.php259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/IntlNumberFormatterTest.php348
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/UnitConverterTest.php271
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyChainValueTest.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyValueTest.php65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ReferenceValueTest.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/StringValueTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TelephoneUriValueTest.php30
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TemperatureValueTest.php207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/ComponentsTest.php46
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/IntlTimeFormatterTest.php272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/JulianDayTest.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/TimezoneTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TypesValueTest.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/UniquenessConstraintValueTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/CodeStringValueFormatterTest.php153
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/DispatchingDataValueFormatterTest.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/MonolingualTextValueFormatterTest.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NoValueFormatterTest.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NumberValueFormatterTest.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/PropertyValueFormatterTest.php521
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/ReferenceValueFormatterTest.php224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/StringValueFormatterTest.php225
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/TimeValueFormatterTest.php548
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsListValueParserTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsPatternValueParserTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/ImportValueParserTest.php223
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/MonolingualTextValueParserTest.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/PropertyValueParserTest.php171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/TimeValueParserTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/AllowsListConstraintValueValidatorTest.php411
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/CompoundConstraintValueValidatorTest.php61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PatternConstraintValueValidatorTest.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PropertySpecificationConstraintValueValidatorTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/UniquenessConstraintValueValidatorTest.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DefinesTest.php37
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializerTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializerTest.php159
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializerTest.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializerTest.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializerTest.php201
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializerTest.php124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializerRegistryTest.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/ExpDataDeserializerTest.php163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/SemanticDataDeserializerTest.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/ElasticClientTaskHandlerTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/IndicesInfoProviderTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/MappingsInfoProviderTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/NodesInfoProviderTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/SettingsInfoProviderTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ConfigTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ClientTest.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ConnectionProviderTest.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ElasticFactoryTest.php244
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Exception/ClientBuilderNotFoundExceptionTest.php27
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/FileIndexerTest.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerRecoveryJobTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerTest.php208
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RebuilderTest.php264
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/ReplicationStatusTest.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RolloverTest.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Lookup/ProximityPropertyValueLookupTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/AggregationsTest.php115
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ConditionTest.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreterTest.php369
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php129
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ExcerptsTest.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/FieldMapperTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EncoderTest.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EnumTest.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventHandlerTest.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventListenerRegistryTest.php229
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemDeserializationExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataTypeLookupExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotReadableExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotWritableExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/ParameterNotFoundExceptionTest.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PredefinedPropertyLabelMismatchExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyLabelNotResolvedExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/RedirectTargetUnresolvableExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SemanticDataImportExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SettingNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/StoreNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SubSemanticDataExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ConceptMapperTest.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/DataItemMatchFinderTest.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpElementTest.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpLiteralTest.php272
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpNsResourceTest.php259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpResourceTest.php193
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ElementFactoryTest.php136
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/EscaperTest.php160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ExpResourceMapperTest.php266
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilderTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilderTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/DispatchingResourceBuilderTest.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilderTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilderTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilderTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilderTest.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilderTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilderTest.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilderTest.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyValueResourceBuilderTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilderTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/SortPropertyValueResourceBuilderTest.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilderTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/XsdValueMapperTest.php162
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/CachedFactboxTest.php535
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxFactoryTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxMagicWordsTest.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxTest.php559
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/GlobalFunctionsTest.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HashBuilderTest.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HierarchyLookupTest.php421
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/DispatchingContentCreatorTest.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/TextContentCreatorTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/XmlContentCreatorTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentModellerTest.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/InvalidJsonContent/invalid.json26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/MissingSections/error.json16
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/NoImportFormat/error.json6
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/content.json26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/foaf.txt13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImportContentsTest.php126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImporterTest.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonContentIteratorTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonImportContentsFileDirReaderTest.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/InMemoryPoolCacheTest.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/IteratorFactoryTest.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/AppendIteratorTest.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ChunkedIteratorTest.php69
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/CsvFileIteratorTest.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/MappingIteratorTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ResultIteratorTest.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/FallbackFinderTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/JsonContentsFileReaderTest.php173
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LangTest.php320
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LanguageContentsTest.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/LocalizerTest.php372
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ConceptCacheRebuilderTest.php224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DataRebuilderTest.php420
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DistinctEntityDataRebuilderTest.php290
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DuplicateEntitiesDisposerTest.php187
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ExceptionFileLoggerTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceFactoryTest.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceHelperTest.php105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceLoggerTest.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/PropertyStatisticsRebuilderTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiQueryResultFormatterTest.php344
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiRequestParameterFormatterTest.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskArgsTest.php269
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskTest.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleAugmentorTest.php154
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleLookupTest.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/CachingLookupTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListAugmentorTest.php194
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListLookupTest.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PSubjectLookupTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PValueLookupTest.php281
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/SubjectLookupTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseByPropertyTest.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseBySubjectTest.php238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseTest.php230
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/InfoTest.php180
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/PropertyListByApiRequestTest.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/QueryTest.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/TaskTest.php252
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/CollatorTest.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/ConnectionProviderTest.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/DatabaseTest.php609
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/LoadBalancerConnectionProviderTest.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/OptionsBuilderTest.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/QueryTest.php321
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/SequenceTest.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/TransactionProfilerTest.php64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/DeepRedirectTargetResolverTest.php142
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/CallableUpdateTest.php237
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/ChangeTitleUpdateTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/TransactionalCallableUpdateTest.php395
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/EditInfoProviderTest.php274
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Exception/ExtendedPermissionsErrorTest.php31
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleDeleteTest.php121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleFromTitleTest.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleProtectCompleteTest.php207
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticlePurgeTest.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleViewHeaderTest.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BaseTemplateToolboxTest.php237
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforeDisplayNoArticleTextTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforePageDisplayTest.php182
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/EditPageFormTest.php161
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionSchemaUpdatesTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionTypesTest.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/FileUploadTest.php168
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/GetPreferencesTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookHandlerTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookListenerTest.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookRegistryTest.php1733
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/InternalParseBeforeLinksTest.php459
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/LinksUpdateConstructedTest.php238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/NewRevisionFromEditCompleteTest.php214
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/OutputPageParserOutputTest.php360
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ParserAfterTidyTest.php412
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/PersonalUrlsTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/RejectParserCacheValueTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderGetConfigVarsTest.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderTestModulesTest.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinAfterContentTest.php357
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinTemplateNavigationTest.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialSearchResultsPrependTest.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialStatsAddExtraTest.php164
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsAlwaysKnownTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsMovableTest.php95
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleMoveCompleteTest.php121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/UserChangeTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobFactoryTest.php179
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobQueueTest.php244
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationClassUpdateJobTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php229
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationUpdateJobTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/EntityIdDisposerJobTest.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableRebuildJobTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableUpdateJobTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/NullJobTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ParserCachePurgeJobTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/PropertyStatisticsRebuildJobTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/RefreshJobTest.php175
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateDispatcherJobTest.php421
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateJobTest.php325
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/LocalTimeTest.php115
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MagicWordsFinderTest.php160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/ManualEntryLoggerTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MediaWikiNsContentReaderTest.php56
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MessageBuilderTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MwCollaboratorFactoryTest.php263
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageCreatorTest.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageInfoProviderTest.php368
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageUpdaterTest.php311
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/RedirectTargetFinderTest.php76
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlColumnListRendererTest.php261
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlFormRendererTest.php225
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTableRendererTest.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTemplateRendererTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/WikitextTemplateRendererTest.php54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/CustomFormTest.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FieldTest.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsBuilderTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFactoryTest.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFinderTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/NamespaceFormTest.php64
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/OpenFormTest.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/SortFormTest.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/QueryBuilderTest.php224
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchProfileFormTest.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultSetTest.php222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultTest.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchTest.php635
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandlerTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/ConfigurationListTaskHandlerTest.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DataRefreshJobTaskHandlerTest.php151
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandlerTest.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DisposeJobTaskHandlerTest.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DuplicateLookupTaskHandlerTest.php73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/EntityLookupTaskHandlerTest.php222
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandlerTest.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandlerTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OutputFormatterTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandlerTest.php152
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/SupportListTaskHandlerTest.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TableSchemaTaskHandlerTest.php109
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TaskHandlerFactoryTest.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/DownloadLinksWidgetTest.php40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ErrorWidgetTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/FormatListWidgetTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HelpWidgetTest.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HtmlFormTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/LinksWidgetTest.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/NavigationLinksWidgetTest.php147
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParameterInputTest.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersProcessorTest.php242
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersWidgetTest.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/QueryInputWidgetTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/SortWidgetTest.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/UrlArgsTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/FieldBuilderTest.php36
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/GroupFormatterTest.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/HtmlBuilderTest.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/ValueFormatterTest.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PageProperty/PageBuilderTest.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilderTest.php95
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageBuilderTest.php178
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageRequestOptionsTest.php286
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/QueryResultLookupTest.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAdminTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAskTest.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialBrowseTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialDeferredRequestDispatcherTest.php197
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPagePropertyTest.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialProcessingErrorListTest.php77
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPropertyLabelSimilarityTest.php54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialSearchByPropertyTest.php124
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialURIResolverTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/StripMarkerDecoderTest.php108
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleFactoryTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleLookupTest.php258
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MessageTest.php211
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceExaminerTest.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceManagerTest.php315
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceUriFinderTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/OptionsTest.php233
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ConceptPageTest.php36
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ListBuilderTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ValueListBuilderTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilderTest.php157
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PageFactoryTest.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PropertyPageTest.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterListDocBuilderTest.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterProcessorFactoryTest.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParametersTest.php52
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/InTextAnnotationParserTest.php703
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksEncoderTest.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksProcessorTest.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/RecursiveTextProcessorTest.php345
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/SemanticLinksParserTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserDataTest.php488
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctionFactoryTest.php210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/AskParserFunctionTest.php585
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ConceptParserFunctionTest.php247
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DeclareParserFunctionTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DocumentationParserFunctionTest.php58
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ExpensiveFuncExecutionWatcherTest.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/InfoParserFunctionTest.php54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/RecurringEventsParserFunctionTest.php429
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SectionTagTest.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SetParserFunctionTest.php239
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ShowParserFunctionTest.php287
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SubobjectParserFunctionTest.php738
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserParameterProcessorTest.php409
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PermissionPthValidatorTest.php484
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PostProcHandlerTest.php395
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ProcessingErrorMsgHandlerTest.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAliasFinderTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotatorFactoryTest.php146
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/CategoryPropertyAnnotatorTest.php387
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/ChainablePropertyAnnotatorTest.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/DisplayTitlePropertyAnnotatorTest.php236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/EditProtectedPropertyAnnotatorTest.php232
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/MandatoryTypePropertyAnnotatorTest.php234
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/NullPropertyAnnotatorTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/PredefinedPropertyAnnotatorTest.php366
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/RedirectPropertyAnnotatorTest.php116
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SchemaPropertyAnnotatorTest.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SortKeyPropertyAnnotatorTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/TranslationPropertyAnnotatorTest.php160
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyChangePropagationNotifierTest.php209
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyLabelFinderTest.php206
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRegistryTest.php572
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRestrictionExaminerTest.php148
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationLookupTest.php486
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqExaminerTest.php331
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqMsgBuilderTest.php236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/EditProtectionUpdaterTest.php233
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/ProtectionValidatorTest.php246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DebugFormatterTest.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DeferredTest.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DescriptionFactoryTest.php371
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Exception/ResultFormatNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ExcerptsTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ClassDescriptionTest.php280
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConceptDescriptionTest.php100
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConjunctionTest.php254
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/DisjunctionTest.php297
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/NamespaceDescriptionTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/SomePropertyTest.php506
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ThingDescriptionTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ValueDescriptionTest.php223
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/DescriptionProcessorTest.php258
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/LegacyParserTest.php380
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TermParserTest.php219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TokenizerTest.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/DeserializerTest.php210
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/FormatterTest.php97
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/SerializerTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestFactoryTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestTest.php191
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/DefaultParamDefinitionTest.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/ParamListProcessorTest.php406
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/QueryCreatorTest.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotatorFactoryTest.php229
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DescriptionProfileAnnotatorTest.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DurationProfileAnnotatorTest.php84
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/FormatProfileAnnotatorTest.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/NullProfileAnnotatorTest.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/ParametersProfileAnnotatorTest.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SchemaLinkProfileAnnotatorTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SourceProfileAnnotatorTest.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/StatusCodeProfileAnnotatorTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryComparatorTest.php191
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryLinkerTest.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QuerySourceFactoryTest.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryStringifierTest.php302
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTest.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTokenTest.php219
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/RemoteRequestTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/CachedQueryResultPrefetcherTest.php349
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResolverJournalTest.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResultFieldMatchFinderTest.php373
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/StringResultTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CategoryResultPrinterTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CsvFileExportPrinterTest.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/FeedExportPrinterTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinterTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/NullResultPrinterTest.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TableResultPrinterTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TemplateFileExportPrinterTest.php70
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ScoreSetTest.php93
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/QueryFactoryTest.php127
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/RequestOptionsTest.php126
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Rule/RuleTest.php28
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/BadHttpEndpointResponseExceptionTest.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/HttpEndpointConnectionExceptionTest.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/HttpResponseErrorMapperTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FalseConditionTest.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FilterConditionTest.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/SingletonConditionTest.php54
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/TrueConditionTest.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/WhereConditionTest.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/ConditionBuilderTest.php923
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php240
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php369
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreterTest.php483
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php552
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php593
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/EngineOptionsTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryEngineTest.php526
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryResultFactoryTest.php201
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/RepositoryResultTest.php129
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/XmlResponseParserTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/ReplicationDataTruncatorTest.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryClientTest.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectionProviderTest.php223
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/ElementaryRepositoryConnectorTest.php190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnectorTest.php25
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnectorTest.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/GenericRepositoryConnectorTest.php24
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/RepositoryConnectorsExceptionTest.php147
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnectorTest.php25
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryRedirectLookupTest.php292
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreFactoryTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreTest.php358
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/TurtleTriplesBuilderTest.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeDiffTest.php163
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeOpTest.php412
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/FieldChangeOpTest.php66
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/TableChangeOpTest.php226
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangePropagationEntityFinderTest.php185
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ConceptCacheTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityRebuildDispatcherTest.php239
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingEntityLookupTest.php387
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingSemanticDataLookupTest.php238
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIBlobHandlerTest.php246
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIUriHandlerTest.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIWikiPageHandlerTest.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DataItemHandlerDispatcherTest.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdCacheManagerTest.php150
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdChangerTest.php202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdEntityFinderTest.php232
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/NativeEntityLookupTest.php212
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertiesLookupTest.php138
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertySubjectsLookupTest.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SemanticDataLookupTest.php356
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/StubSemanticDataTest.php162
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SubobjectListFinderTest.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/TraversalPropertyLookupTest.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/UniquenessLookupTest.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityValueUniquenessConstraintCheckerTest.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/PropertyStatisticsInvalidArgumentExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/TableMissingIdFieldExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/InstallerTest.php348
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/CachedListLookupTest.php168
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyLabelSimilarityLookupTest.php147
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyUsageListLookupTest.php216
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/ProximityPropertyValueLookupTest.php202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/RedirectTargetLookupTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UndeclaredPropertyListLookupTest.php285
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UnusedPropertyListLookupTest.php217
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UsageStatisticsListLookupTest.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyStatisticsStoreTest.php378
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionBuilderTest.php182
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionTest.php72
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceDisposerTest.php362
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceFinderTest.php209
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableInfoFetcherTest.php208
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowDifferTest.php167
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowMapperTest.php206
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableUpdaterTest.php167
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksTableUpdaterTest.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksUpdateJournalTest.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilterTest.php253
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryDependencyLinksStoreTest.php1034
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryReferenceBacklinksTest.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryResultDependencyListResolverTest.php602
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependencyLinksStoreFactoryTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/ConceptQuerySegmentBuilderTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapperTest.php86
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php241
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreterTest.php139
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php511
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php167
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/EngineOptionsTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilderTest.php213
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilderTest.php236
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableRebuilderTest.php218
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableTest.php137
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableUpdaterTest.php215
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextChangeUpdaterTest.php204
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextSanitizerTest.php136
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilderTest.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/FulltextSearchTableFactoryTest.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/HierarchyTempTableBuilderTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/OrderConditionTest.php130
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QueryEngineTest.php119
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuildManagerTest.php74
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuilderTest.php293
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListProcessorTest.php65
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentTest.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngineFactoryTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RedirectStoreTest.php265
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RequestOptionsProcTest.php343
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/SQLStoreFactoryTest.php438
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/Examiner/HashFieldTest.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/FieldTypeTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/MySQLTableBuilderTest.php396
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/PostgresTableBuilderTest.php385
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/SQLiteTableBuilderTest.php331
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableBuilderTest.php83
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableTest.php145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TemporaryTableBuilderTest.php134
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableFieldUpdaterTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableIntegrityExaminerTest.php329
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableSchemaManagerTest.php115
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentFormatterTest.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentHandlerTest.php26
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentTest.php183
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/HtmlBuilderTest.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaConstructionFailedExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaTypeNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaDefinitionTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaFactoryTest.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaValidatorTest.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SerializerFactoryTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/ExpDataSerializerTest.php174
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/QueryResultSerializerTest.php286
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/SemanticDataSerializerTest.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServiceFactoryTest.php189
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServicesContainerBuildTest.php171
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/Exception/ServiceNotFoundExceptionTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServiceFactoryTest.php133
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServicesContainerBuildTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/MediaWikiServicesContainerBuildTest.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ServicesContainerTest.php149
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/SharedServicesContainerTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SiteTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StoreTest.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StringConditionTest.php51
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/TypesRegistryTest.php96
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/BufferedStatsdCollectorTest.php196
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharArmorTest.php104
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharExaminerTest.php75
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CircularReferenceGuardTest.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CsvTest.php227
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ErrorCodeFormatterTest.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/FileTest.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HmacSerializerTest.php132
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlColumnsTest.php297
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlDivTableTest.php71
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlModalTest.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTableTest.php118
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTabsTest.php78
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlVTabsTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ImageTest.php38
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/JsonSchemaValidatorTest.php121
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LoggerTest.php62
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LruTest.php80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/NormalizerTest.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/StatsFormatterTest.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TempFileTest.php33
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TimerTest.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TokenizerTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ByPageSemanticDataFinder.php144
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/ConnectionProvider.php49
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/TestDatabaseTableBuilder.php297
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/BulkFileProvider.php89
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/ContentsReader.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/DummyFileCreator.php101
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/JsonFileReader.php122
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/LocalFileUpload.php179
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/BerlinFactsheet.php273
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/FranceFactsheet.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/ParisFactsheet.php254
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.json13
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.txt3
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesCleaner.php57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFactory.php40
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFileProvider.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesProvider.php165
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/InvalidCustomRespositoryConnector.php12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/AreaProperty.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/BookRecordProperty.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CapitalOfProperty.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CityCategory.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CoordinatesProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CountryCategory.php59
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/DescriptionProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/EmailProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FixtureProperty.php50
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FoundedProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/LocatedInProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationDensityProperty.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/StatusProperty.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TelephoneNumberProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TemperatureProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TitleProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/UrlProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/YearProperty.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/FakeRawResultProvider.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/boolean-sparql-result.xml6
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/empty-sparql-result.xml9
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/integer-type-literal-sparql-result.xml11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/invalid-sparql-result.xml8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result-utf8.xml17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result.xml17
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/nontype-literal-sparql-result.xml12
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/string-type-literal-sparql-result.xml11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/uri-resource-sparql-result.xml11
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/GlobalsProvider.php90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/InSemanticDataFetcher.php61
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/CoreMockObjectRepository.php661
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/FakeQueryStore.php42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/IteratorMockBuilder.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MediaWikiMockObjectRepository.php517
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectBuilder.php195
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectRepository.php23
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockSuperUser.php39
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockTitle.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwApiFactory.php87
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwHooksHandler.php145
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Page/PageEditor.php106
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageCreator.php161
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageDeleter.php67
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageReader.php105
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageRefresher.php92
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ParserFactory.php53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/JobQueueRunner.php156
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/MaintenanceRunner.php125
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/RunnerFactory.php81
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/XmlImportRunner.php172
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SemanticDataFactory.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SpyLogger.php60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/StringBuilder.php42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/UtilityFactory.php248
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ExportDataValidator.php91
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/HtmlValidator.php141
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/IncomingSemanticDataValidator.php99
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/NumberValidator.php63
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QueryResultValidator.php202
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QuerySegmentValidator.php79
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/SemanticDataValidator.php436
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/StringValidator.php113
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/TitleValidator.php45
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ValidatorFactory.php98
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/ContentParserTest.php330
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/RecordValueTest.php266
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/UriValueTest.php397
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/WikiPageValueTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/HighlighterTest.php117
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/InfolinkTest.php243
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryPrinterFactoryTest.php199
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryProcessorTest.php158
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/RecurringEventsTest.php410
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/ChangeTitleTest.php329
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DataUpdateTest.php409
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DeleteSubjectTest.php259
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SemanticDataTest.php710
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SettingsTest.php184
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SetupTest.php344
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SubobjectTest.php436
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/CommonDataItemTest.php41
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIConceptTest.php82
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIPropertyTest.php227
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DITimeTest.php168
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIWikiPageTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BlobTest.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BoolTest.php47
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_GeoCoordTest.php68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_NumberTest.php44
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DataItemTest.php110
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExpDataTest.php143
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExportSemanticDataTest.php344
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/SMWExporterTest.php102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/formatters/MessageFormatterTest.php282
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/PropertiesQueryPageTest.php188
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/QueryPageTest.php140
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/UnusedPropertiesQueryPageTest.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/WantedPropertiesQueryPageTest.php112
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/AggregatablePrinterTest.php335
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/DsvResultPrinterTest.php43
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/EmbeddedResultPrinterTest.php42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/JsonResultPrinterTest.php128
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RawResultPrinterTest.php48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RdfResultPrinterTest.php42
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/ResultPrintersTest.php88
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialConceptsTest.php94
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialsTest.php155
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/QueryResultTest.php131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreFactoryTest.php85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreTest.php198
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreSmwIdsTest.php546
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreTest.php170
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/api/ext.smw.api.test.js190
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.data.test.js333
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.number.test.js73
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.property.test.js60
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.text.test.js80
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.time.test.js131
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.unknown.test.js68
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.uri.test.js90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.wikiPage.test.js240
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataValue.quantity.test.js90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/ext.smw.test.js216
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/query/ext.smw.query.test.js213
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/util/ext.smw.util.tooltip.test.js102
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/README.md57
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/blazegraph-store.properties53
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/install-mediawiki.sh48
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/install-semantic-mediawiki.sh85
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/install-services.sh239
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/openrdf-sesame-memory-repository.txt8
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/run-tests.sh20
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/update-configuration-settings.sh90
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/upload-coverage-report.sh14
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/tests/travis/virtuoso-sparql-permission.sql6
2491 files changed, 397327 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/.gitignore b/www/wiki/extensions/SemanticMediaWiki/.gitignore
new file mode 100644
index 00000000..5df3656a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/.gitignore
@@ -0,0 +1,14 @@
+*~
+*.kate-swp
+
+vendor/
+extensions/
+build/logs/
+build/release/build-*
+
+composer.phar
+composer.lock
+
+!.*
+.idea/
+.smw.json
diff --git a/www/wiki/extensions/SemanticMediaWiki/.jshintignore b/www/wiki/extensions/SemanticMediaWiki/.jshintignore
new file mode 100644
index 00000000..93416ca9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/.jshintignore
@@ -0,0 +1,9 @@
+# third-party libs
+res/jquery/
+
+# Those files need overall re-factoring and qunit tests
+res/smw/special/ext.smw.special.ask.js
+res/smw/special/ext.smw.special.browse.js
+res/smw/special/ext.smw.special.property.js
+res/smw/util/ext.smw.util.autocomplete.js
+res/smw/util/ext.smw.util.tooltip.js \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/.jshintrc b/www/wiki/extensions/SemanticMediaWiki/.jshintrc
new file mode 100644
index 00000000..c33a3da5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/.jshintrc
@@ -0,0 +1,32 @@
+{
+ "predef": [
+ "mediaWiki",
+ "semanticMediaWiki",
+ "jQuery",
+ "QUnit"
+ ],
+
+ "bitwise": false, // prohibits the use of bitwise operators such as ^ (XOR), | (OR)
+ "camelcase": false, // allows you to force all variable names to use either camelCase style
+ "curly": true, // requires you to always put curly braces around blocks in loops and conditionals
+ "eqeqeq": true, // prohibits the use of == and != in favor of === and !==
+ "forin": false, // requires all for in loops to filter object's items
+ "immed": true, // prohibits the use of immediate function invocations without wrapping them
+ "latedef": true, // prohibits the use of a variable before it was defined
+ "newcap": false, // requires you to capitalize names of constructor functions
+ "noarg": true, // prohibits the use of arguments.caller and arguments.callee
+ "noempty": true, // warns when you have an empty block in your code
+ "nonew": false, // prohibits the use of constructor functions for side-effects
+ "quotmark": "single", // enforces the consistency of quotation marks used throughout your code
+ "regexp": false, // prohibits the use of unsafe . in regular expressions
+ "undef": false, // prohibits the use of explicitly undeclared variables
+ "unused": true, // warns when you define and never use your variables
+ "strict": true, // requires all functions to run in EcmaScript 5's strict mode
+ "trailing": true, // makes it an error to leave a trailing whitespace in your code
+
+ "laxbreak": true, // suppresses most of the warnings about possibly unsafe line breakings
+ "smarttabs": true, // suppresses warnings about mixed tabs
+ "multistr": true, // suppresses warnings about multi-line strings
+
+ "browser": true // defines globals exposed by modern browsers
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/.scrutinizer.yml b/www/wiki/extensions/SemanticMediaWiki/.scrutinizer.yml
new file mode 100644
index 00000000..80ca05e1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/.scrutinizer.yml
@@ -0,0 +1,26 @@
+filter:
+ excluded_paths:
+ - 'vendor/*'
+
+tools:
+ php_mess_detector:
+ config:
+ controversial_rules: { superglobals: false }
+ php_cpd: true
+ php_pdepend: true
+ php_code_coverage: false
+ php_code_sniffer: true
+ php_cs_fixer: true
+ php_loc: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
+ external_code_coverage:
+ timeout: 1800 # Timeout in seconds
+
+checks:
+ php:
+ code_rating: true
+ duplication: true
+ sql_injection_vulnerabilities: true
+ security_vulnerabilities: true
+ no_eval: true
diff --git a/www/wiki/extensions/SemanticMediaWiki/.travis.yml b/www/wiki/extensions/SemanticMediaWiki/.travis.yml
new file mode 100644
index 00000000..3c1bcd43
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/.travis.yml
@@ -0,0 +1,86 @@
+# https://docs.travis-ci.com/user/languages/php
+language: php
+sudo: required
+
+# https://docs.travis-ci.com/user/trusty-ci-environment/
+dist: trusty
+
+matrix:
+ fast_finish: true
+ include:
+ - env: DB=mysql; MW=REL1_29; PHPUNIT=5.7.*
+ php: 7.1
+ - env: DB=mysql; MW=REL1_28; FUSEKI=2.4.0; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=mysql; MW=REL1_27; VIRTUOSO=6.1; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=mysql; MW=REL1_27; SESAME=2.8.7; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=sqlite; MW=REL1_27; SITELANG=ja; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=postgres; MW=REL1_31; PHPUNIT=6.5.*
+ php: 7.2
+ - env: DB=mysql; MW=REL1_27; BLAZEGRAPH=1.5.2; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=mysql; MW=REL1_30; ES=5.6.6; PHPUNIT=5.7.*
+ php: 7.1
+ - env: DB=mysql; MW=REL1_27; TYPE=benchmark; PHPUNIT=5.7.*
+ php: hhvm-3.18
+ - env: DB=sqlite; MW=master; PHPUNIT=6.5.*
+ php: 7.2
+ - env: DB=sqlite; MW=REL1_27; TYPE=composer; PHPUNIT=4.8.*
+ php: 5.6
+ - env: DB=mysql; MW=REL1_27; TYPE=relbuild; PHPUNIT=4.8.*
+ php: 5.6
+ allow_failures:
+ # Can fail when pushed/used with newly Composer dependencies
+ - env: DB=mysql; MW=REL1_27; TYPE=relbuild; PHPUNIT=4.8.*
+ # Can fail when pushed/used with newly Composer dependencies
+ - env: DB=sqlite; MW=REL1_27; TYPE=composer; PHPUNIT=4.8.*
+ # This is MW master, you never know what WMF developers have put in for easter eggs
+ - env: DB=sqlite; MW=master; PHPUNIT=6.5.*
+ # May take a moment and is non-critical therefore allow it to run without delaying the status
+ - env: DB=mysql; MW=REL1_27; TYPE=benchmark; PHPUNIT=5.7.*
+ - env: DB=mysql; MW=REL1_27; SESAME=2.8.7
+ - env: DB=mysql; MW=REL1_30; ES=5.6.6; PHPUNIT=5.7.*
+
+
+# Dec 16, 2015 (GCE switch): Travis support wrote (Tomcat + Java):
+# bug in the JDK: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7089443.
+# The hostname for the precise-gce platform is longer than 64 characters on the
+# VM your job is running on
+#- sudo hostname "$(hostname | cut -c1-63)"
+#- sed -e "s/^\\(127\\.0\\.0\\.1.*\\)/\\1 $(hostname | cut -c1-63)/" /etc/hosts | sudo tee /etc/hosts
+#- cat /etc/hosts # optionally check the content *after*
+
+# Aug 12, 2016: (incident:2p40l49r3yxd) Travis support asks to add `sudo sysctl ...`
+# After investigation, removed the lines from Dec 16, 2015 as it emptied the hosts file
+before_install:
+ - sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
+ - sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
+ - ip addr # for confirmation. can be skipped
+ - cat /etc/hosts # optionally check the content *before*
+
+install:
+ - travis_retry composer self-update
+ - bash ./tests/travis/install-services.sh
+ - bash ./tests/travis/install-mediawiki.sh
+ - bash ./tests/travis/install-semantic-mediawiki.sh
+
+before_script:
+ - bash ./tests/travis/update-configuration-settings.sh
+
+script:
+ - bash ./tests/travis/run-tests.sh
+
+after_script:
+ - if [ ls /tmp/stacktrace* 1> /dev/null 2>&1 ] ; then cat /tmp/stacktrace*.log ; fi
+ - if [ -f php.log ] ; then cat php.log ; fi
+ - if [ -f /tmp/mediawiki-debug.log ] ; then cat /tmp/mediawiki-debug.log ; fi
+
+after_success:
+ - bash ./tests/travis/upload-coverage-report.sh
+
+cache:
+ directories:
+ - $HOME/.composer/cache
diff --git a/www/wiki/extensions/SemanticMediaWiki/COPYING b/www/wiki/extensions/SemanticMediaWiki/COPYING
new file mode 100644
index 00000000..0c88f390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/COPYING
@@ -0,0 +1,355 @@
+Semantic MediaWiki (SMW) is a free, open-source extension to MediaWiki
+that lets you store and query data within the wiki's pages.
+
+Copyright (C) 2005
+
+
+The license text below "====" applies to all files within this distribution,
+other than those that are in a directory which contains files named "LICENSE"
+or "COPYING", or a subdirectory thereof. For those files, the license text
+contained in said file overrides any license information contained in directories
+of smaller depth. Alternative licenses are typically used for software that
+is provided by external parties, and merely packaged with the Semantic MediaWiki
+release for convenience.
+
+====
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/www/wiki/extensions/SemanticMediaWiki/DefaultSettings.php b/www/wiki/extensions/SemanticMediaWiki/DefaultSettings.php
new file mode 100644
index 00000000..8229b848
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/DefaultSettings.php
@@ -0,0 +1,2352 @@
+<?php
+
+/**
+ * DO NOT EDIT!
+ *
+ * The following default settings are to be used by the extension itself,
+ * please modify settings in the LocalSettings file.
+ *
+ * Most settings should be make between including this file and the call
+ * to enableSemantics(). Exceptions that need to be set before are
+ * documented below.
+ *
+ * @codeCoverageIgnore
+ */
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( "This file is part of the Semantic MediaWiki extension. It is not a valid entry point.\n" );
+}
+
+return [
+
+ ###
+ # This is the path to your installation of Semantic MediaWiki as seen on your
+ # local filesystem. Used against some PHP file path issues.
+ #
+ # @since 1.0
+ ##
+ 'smwgIP' => __DIR__ . '/',
+ #
+ # @since 2.5
+ ##
+ 'smwgExtraneousLanguageFileDir' => __DIR__ . '/i18n/extra',
+ 'smwgServicesFileDir' => __DIR__ . '/src/Services',
+ 'smwgResourceLoaderDefFiles' => [ 'smw' => __DIR__ . '/res/Resources.php' ],
+ 'smwgMaintenanceDir' => __DIR__ . '/maintenance',
+ ##
+
+ ###
+ # Configuration directory
+ # @see #3506
+ #
+ # The maintained directory needs to be writable in order for configuration
+ # information to be stored persistently and be accessible for Semantic
+ # MediaWiki throughout its operation.
+ #
+ # You may assign the same directory as in `wgUploadDirectory` (e.g
+ # $smwgConfigFileDir = $wgUploadDirectory;) or select an entire different
+ # location. The default location is the Semantic MediaWiki extension root.
+ #
+ # @since 3.0
+ ##
+ 'smwgConfigFileDir' => __DIR__,
+ ##
+
+ ###
+ # Upgrade key
+ #
+ # This key verifies that a correct upgrade (update.php/setupStore.php) path
+ # was selected and hereby ensures a consistent DB setup.
+ #
+ # Whenever a DB table change occurs, modify the key value (e.g. `smw:20...`)
+ # to reflect the requirement for the client to follow the processes as
+ # outlined in the installation manual.
+ #
+ # Once the installer is run, the `.smw.json` will be updated and no longer
+ # cause any exception.
+ #
+ # @since 3.0
+ ##
+ 'smwgUpgradeKey' => 'smw:2018-09-01',
+ ##
+
+ ###
+ # Content import
+ #
+ # Controls the content import directory and version that is expected to be
+ # imported during the setup process.
+ #
+ # For all legitimate files in `smwgImportFileDirs`, the import is initiated
+ # if the `smwgImportReqVersion` compares with the declared version in the file.
+ #
+ # In case `smwgImportReqVersion` is maintained with `false` then the import
+ # is going to be disabled.
+ #
+ # @since 2.5
+ ##
+ 'smwgImportFileDirs' => [ 'default' => __DIR__ . '/data/import' ],
+ 'smwgImportReqVersion' => 1,
+ ##
+
+ ###
+ # Semantic MediaWiki's operational state
+ #
+ # It is expected that enableSemantics() is used to enable SMW otherwise it is
+ # disabled by default. disableSemantics() will also set the state to disabled.
+ #
+ # @since 2.4
+ ##
+ 'smwgSemanticsEnabled' => false,
+ ##
+
+ ###
+ # CompatibilityMode is to force SMW to work with other extensions that may impact
+ # performance in an unanticipated way or may contain potential incompatibilities.
+ #
+ # @since 2.4
+ ##
+ 'smwgEnabledCompatibilityMode' => false,
+ ##
+
+ ###
+ # Use another storage backend for Semantic MediaWiki. The default is suitable
+ # for most uses of SMW.
+ #
+ # @since 0.7
+ ##
+ 'smwgDefaultStore' => "SMWSQLStore3",
+ ##
+
+ ##
+ # Debug logger role
+ #
+ # A role (developer, user, production) defines the detail of information
+ # (granularity) that are expected to be logged. Roles include:
+ #
+ # - `developer` outputs any loggable event produced by SMW
+ # - `user` outputs certain events deemed important
+ # - `production` outputs a minimal set of events produced by SMW
+ #
+ # Logging only happens in case `$wgDebugLogFile` or `$wgDebugLogGroups`
+ # are actively maintained.
+ #
+ # @see https://www.mediawiki.org/wiki/Manual:How_to_debug#Logging
+ #
+ # @since 3.0
+ # @default production
+ ##
+ 'smwgDefaultLoggerRole' => 'production',
+ ##
+
+ ###
+ # Local connection configurations
+ #
+ # Allows to modify connection characteristics for providers that are used by
+ # Semantic MediaWiki.
+ #
+ # Changes to these settings should ONLY be made by trained professionals to
+ # avoid unexpected or unanticipated results when using connection handlers.
+ #
+ # Available DB index as provided by MediaWiki:
+ #
+ # - DB_SLAVE or DB_REPLICA (1.28+)
+ # - DB_MASTER
+ #
+ # @since 2.5.3
+ ##
+ 'smwgLocalConnectionConf' => [
+ 'mw.db' => [
+ 'read' => DB_SLAVE,
+ 'write' => DB_MASTER
+ ],
+ 'mw.db.queryengine' => [
+ 'read' => DB_SLAVE,
+ 'write' => DB_MASTER
+ ]
+ ],
+ ##
+
+ ###
+ # Configure SPARQL database connection for Semantic MediaWiki. This is used
+ # when SPARQL-based features are enabled, e.g. when using SMWSparqlStore as
+ # the $smwgDefaultStore.
+ #
+ # The default class SMWSparqlDatabase works with many databases that support
+ # SPARQL and SPARQL Update. Three different endpoints (service URLs) are given
+ # - query (reading queries like SELECT)
+ # - update (SPARQL Update queries), and
+ # - data (SPARQL HTTP Protocol for Graph Management).
+ #
+ # The query endpoint is necessary, but the update and data endpoints can be
+ # omitted if not supported.
+ #
+ # This will lead to reduced functionality (e.g. the SMWSparqlStore will not
+ # work if Update is not available). The data endpoint is always optional, but
+ # in some SPARQL databases this method is more efficient than update.
+ #
+ # @since 1.6
+ ##
+ 'smwgSparqlEndpoint' => [
+ 'query' => 'http://localhost:8080/sparql/',
+ 'update' => 'http://localhost:8080/update/',
+ 'data' => 'http://localhost:8080/data/'
+ ],
+ ##
+
+ ###
+ #
+ # The default graph is similar to a database name in relational databases. It
+ # can be set to any URI (e.g. the main page uri of your wiki with
+ # " #graph" appended). Leaving the default graph URI empty only works if the
+ # store is configure to use some default default graph or if it generally
+ # supports this. Different wikis should normally use different default graphs
+ # unless there is a good reason to share one graph.
+ #
+ # @since 1.7
+ ##
+ 'smwgSparqlDefaultGraph' => '',
+ ##
+
+ ##
+ # Sparql repository connector
+ #
+ # Identifies a pre-deployed repository connector that is ought to be used together
+ # with the SPARQLStore.
+ #
+ # List of standard connectors ($smwgSparqlCustomConnector will have no effect):
+ # - '4store'
+ # - 'blazegraph'
+ # - 'fuseki'
+ # - 'sesame'
+ # - 'virtuoso'
+ #
+ # In case `$smwgSparqlRepositoryConnector` is maintained with 'custom',
+ # the `$smwgSparqlCustomConnector` is expected to contain a custom class
+ # implementing the ncessary interface (see `SMWSparqlDatabase`).
+ #
+ # `$smwgSparqlCustomConnector` is only used for the definition of a custom
+ # connector.
+ #
+ # @since 2.0
+ # @default default, meaning that the default (aka generic) connector is used
+ ##
+ 'smwgSparqlRepositoryConnector' => 'default',
+ ##
+
+ ##
+ # Sparql cutstom connector
+ #
+ # In case `$smwgSparqlRepositoryConnector` is maintained with 'custom',
+ # the `$smwgSparqlCustomConnector` is expected to contain a custom class
+ # implementing the ncessary interface (see `SMWSparqlDatabase`).
+ #
+ # `$smwgSparqlCustomConnector` is only used for the definition of a custom
+ # connector.
+ #
+ # @since 2.0
+ ##
+ 'smwgSparqlCustomConnector' => 'SMWSparqlDatabase',
+ ##
+
+ ##
+ # Sparql query features that are expected to be supported by the repository:
+ #
+ # - SMW_SPARQL_QF_NONE does not support any features (as required by SPARQL 1.1)
+ # - SMW_SPARQL_QF_REDI to support finding redirects using inverse property paths,
+ # can only be used for repositories with full SPARQL 1.1 support (e.g. Fuseki,
+ # Sesame)
+ # - SMW_SPARQL_QF_SUBP to resolve subproperties
+ # - SMW_SPARQL_QF_SUBC to resolve subcategories
+ #
+ # - SMW_SPARQL_QF_COLLATION allows to add support for the sorting collation as
+ # maintained in $smwgEntityCollation. It is not enabled by default as the
+ # `uca-*` collation generates a UTF-8 string that contains unrecognized
+ # UTF codepoints that may not be understood by the back-end hence the
+ # Collator prevents and armors those unrecognized characters by replacing
+ # them with a ? to avoid a cURL communication failure but of course this
+ # means that not all elements of the sort string can be transfered to the
+ # back-end and can therefore cause a sorting distortion for close matches
+ # as in case of for example "Ennis, Ennis Hill, Ennis Jones, Ennis-Hill,
+ # Ennis-London"
+ #
+ # - SMW_SPARQL_QF_NOCASE to support case insensitive pattern matches
+ #
+ # Please check with your repository provider whether SPARQL 1.1 is fully
+ # supported or not, and if not SMW_SPARQL_QF_NONE should be set.
+ #
+ # @since 2.3
+ ##
+ 'smwgSparqlQFeatures' => SMW_SPARQL_QF_REDI | SMW_SPARQL_QF_SUBP | SMW_SPARQL_QF_SUBC,
+ ##
+
+ ##
+ # @see https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1306
+ #
+ # Setting to explicitly force a CURLOPT_HTTP_VERSION for the endpoint communication
+ # and should not be changed unless an error as in #1306 was encountered.
+ #
+ # @see http://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html reads "... libcurl
+ # to use the specific HTTP versions. This is not sensible to do unless you have
+ # a good reason.""
+ #
+ # @since 2.3
+ # @default false === means to use the default as determined by cURL
+ ##
+ 'smwgSparqlRepositoryConnectorForcedHttpVersion' => false,
+ ##
+
+ ##
+ # Property replication exemption list
+ #
+ # Listed properties will be exempted from the replication process for a
+ # registered SPARQL repository.
+ #
+ # @since 2.5
+ # @default array
+ ##
+ 'smwgSparqlReplicationPropertyExemptionList' => [],
+ ##
+
+ ###
+ # If you already have custom namespaces on your site, insert
+ # 'smwgNamespaceIndex' => ???,
+ # into your LocalSettings.php *before* including this file. The number ??? must
+ # be the smallest even namespace number that is not in use yet. However, it
+ # must not be smaller than 100.
+ #
+ # @since 1.6
+ ##
+ # 'smwgNamespaceIndex' => 100,
+ ##
+
+ ###
+ # Overwriting the following array, you can define for which namespaces
+ # the semantic links and annotations are to be evaluated. On other
+ # pages, annotations can be given but are silently ignored. This is
+ # useful since, e.g., talk pages usually do not have attributes and
+ # the like. In fact, is is not obvious what a meaningful attribute of
+ # a talk page could be. Pages without annotations will also be ignored
+ # during full RDF export, unless they are referred to from another
+ # article.
+ #
+ # @since 0.7
+ ##
+ 'smwgNamespacesWithSemanticLinks' => [
+ NS_MAIN => true,
+ NS_TALK => false,
+ NS_USER => true,
+ NS_USER_TALK => false,
+ NS_PROJECT => true,
+ NS_PROJECT_TALK => false,
+ NS_FILE => true,
+ NS_FILE_TALK => false,
+ NS_MEDIAWIKI => false,
+ NS_MEDIAWIKI_TALK => false,
+ NS_TEMPLATE => false,
+ NS_TEMPLATE_TALK => false,
+ NS_HELP => true,
+ NS_HELP_TALK => false,
+ NS_CATEGORY => true,
+ NS_CATEGORY_TALK => false,
+ ],
+ ##
+
+ ###
+ # Specifies features supported by the in-page factbox
+ #
+ # - SMW_FACTBOX_CACHE to use the main cache to avoid reparsing the content on
+ # each page view (replaced smwgFactboxUseCache)
+ #
+ # - SMW_FACTBOX_PURGE_REFRESH to refresh the faxtbox content on the purge
+ # event (replaced smwgFactboxCacheRefreshOnPurge)
+ #
+ # - SMW_FACTBOX_DISPLAY_SUBOBJECT displays subobject references
+ #
+ # @since 3.0
+ ##
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+
+ ###
+ # This setting allows you to select in which cases you want to have a factbox
+ # appear below an article and includes the following options:
+ #
+ # - SMW_FACTBOX_NONEMPTY show only those factboxes that have some content
+ # - SMW_FACTBOX_SPECIAL show only if special properties were set
+ # - SMW_FACTBOX_HIDDEN hide always
+ # - SMW_FACTBOX_SHOWN show always
+ #
+ # @note The Magic Words __SHOWFACTBOX__ and __HIDEFACTBOX__ can be used to
+ # control Factbox display for individual pages.
+ #
+ # @since 0.7
+ ##
+ 'smwgShowFactbox' => SMW_FACTBOX_HIDDEN,
+ ##
+
+ ###
+ # Same as $smwgShowFactbox but for the edit mode with same possible values.
+ #
+ # @since 1.0
+ ##
+ 'smwgShowFactboxEdit' => SMW_FACTBOX_NONEMPTY,
+ ##
+
+ ###
+ # Compact infolink support
+ #
+ # Special:Browse, Special:Ask, and Special:SearchByProperty links can contain
+ # arbitrary text elements and therefore become difficult to transfer when its
+ # length exceeds a certain character length.
+ #
+ # The experimental feature of a compact link will be encoded and compressed to
+ # ensure that it can be handled more easily when referring to it as an URL
+ # representation.
+ #
+ # It is not expected to be used as a short-url service, yet in some instances
+ # the generate URL can be comparatively shorter than the plain URL.
+ #
+ # The generated link has no security relevance therefore is is not
+ # cryptographically hashed or secure and should not be seen as such, it is
+ # foremost to "compact" an URL address.
+ #
+ # @since 3.0
+ # @default true
+ ##
+ 'smwgCompactLinkSupport' => false,
+ ##
+
+ ###
+ #
+ # - SMW_CAT_NONE
+ #
+ # - SMW_CAT_REDIRECT: resolves redirects and errors in connection with categories
+ #
+ # - SMW_CAT_INSTANCE: Should category pages that use some [[Category:Foo]]
+ # statement be treated as elements of the category Foo? If disabled, then
+ # it is not possible to make category pages elements of other categories.
+ # See also SMW_CAT_HIERARCHY. (was $smwgCategoriesAsInstances)
+ #
+ # - SMW_CAT_HIERARCHY: Should a subcategory be considered a hierarchy element
+ # in the annotation process? If set to true, subcategories will always be
+ # interpreted as subclasses and automatically annotated with
+ # `Subcategory of`. (was $smwgUseCategoryHierarchy)
+ #
+ # @since 3.0
+ ##
+ 'smwgCategoryFeatures' => SMW_CAT_REDIRECT | SMW_CAT_INSTANCE | SMW_CAT_HIERARCHY,
+ ##
+
+ ###
+ # Settings for recurring events, created with the #set_recurring_event parser
+ # function: the default number of instances defined, if no end date is set,
+ # and the maximum number that can be defined, regardless of end date.
+ #
+ # @since 1.4.3
+ ##
+ 'smwgDefaultNumRecurringEvents' => 100,
+ 'smwgMaxNumRecurringEvents' => 500,
+ ##
+
+ ###
+ # Special:Browse related settings
+ #
+ # - SMW_BROWSE_NONE
+ #
+ # - SMW_BROWSE_TLINK: Should the toolbox of each content page show a link
+ # to browse the properties of that page using Special:Browse? This is a
+ # useful way to access properties and it is somewhat more subtle than
+ # showing a Factbox on every page. (was $smwgToolboxBrowseLink)
+ #
+ # - SMW_BROWSE_SHOW_INVERSE: Should the browse view for incoming links show
+ # the incoming links via its inverses, or shall they be displayed on the
+ # other side? (was $smwgBrowseShowInverse)
+ #
+ # - SMW_BROWSE_SHOW_INCOMING: Should the browse view always show the incoming links
+ # as well, and more of the incoming values? (was $smwgBrowseShowAll)
+ #
+ # - SMW_BROWSE_SHOW_GROUP: Should the browse view create group sections for
+ # properties that belong to the same property group?
+ #
+ # - SMW_BROWSE_SHOW_SORTKEY: Should the sortkey be displayed?
+ #
+ # - SMW_BROWSE_USE_API: Whether the browse display is to be generated using
+ # an API request or not. (was $smwgBrowseByApi)
+ #
+ # @since 3.0
+ ##
+ 'smwgBrowseFeatures' => SMW_BROWSE_TLINK | SMW_BROWSE_SHOW_INCOMING | SMW_BROWSE_SHOW_GROUP | SMW_BROWSE_USE_API,
+ ##
+
+ ###
+ # Should the search by property special page display nearby results when there
+ # are only a few results with the exact value? Switch this off if this page has
+ # performance problems.
+ #
+ # @since 2.1 enabled default types, to disable the functionality either set the
+ # variable to array() or false
+ ##
+ 'smwgSearchByPropertyFuzzy' => [ '_num', '_txt', '_dat', '_mlt_rec' ],
+ ##
+
+ ###
+ # Number of results shown in the listings on pages in the Property and Concept
+ # namespaces as well as other services that require a limit.
+ #
+ # If a value of 0 is given, the respective listings are hidden completely.
+ #
+ # - `type` used for `Special:Types` (was $smwgTypePagingLimit)
+ # - `errorlist` used for `Special:ProcessingErrorList`
+ # - `concept` (was $smwgConceptPagingLimit)
+ # - `property` (was $smwgPropertyPagingLimit)
+ #
+ # Special:Browse
+ # - `valuelist.outgoingt` outgoing value list count
+ # - `valuelist.incoming` incoming value list count
+ #
+ # @since 3.0
+ ##
+ 'smwgPagingLimit' => [
+ 'type' => 50,
+ 'concept' => 250,
+ 'property' => 20,
+ 'errorlist' => 20,
+
+ // Special:Browse
+ 'browse' => [
+ 'valuelist.outgoing' => 200,
+ 'valuelist.incoming' => 20,
+ ]
+ ],
+ ##
+
+ ###
+ # Property page to limit the query request for individual values
+ #
+ # How many values should at most be displayed for a page on the Property
+ # page and if large values are desired, consider reducing
+ # $smwgPropertyPagingLimit for better performance.
+ #
+ # @since 1.3
+ ##
+ 'smwgMaxPropertyValues' => 3,
+ ##
+
+ ###
+ # Property page list limits
+ #
+ # 'subproperty' limit the query request on subproperties
+ # 'redirect' limit the query request on redirects
+ # 'error' limit the query request on improper assignments
+ #
+ # `false` as value assignment will disable the display of a selected list
+ #
+ # @since 3.0
+ ##
+ 'smwgPropertyListLimit' => [
+ 'subproperty' => 25,
+ 'redirect' => 25,
+ 'error' => 10
+ ],
+ ##
+
+ ###
+ # Settings for inline queries ({{#ask:...}}) and for semantic queries in
+ # general. This can especially be used to prevent overly high server-load due
+ # to complex queries. The following settings affect all queries, wherever they
+ # occur.
+ #
+ # @since 1.0
+ ##
+ 'smwgQEnabled' => true, // (De)activates all query related features and interfaces
+ 'smwgQMaxLimit' => 10000, // Max number of results *ever* retrieved, even when using special query pages.
+ #
+ # @since 1.5
+ ##
+ 'smwgIgnoreQueryErrors' => true, // Should queries be executed even if some errors were detected?
+ // A hint that points out errors is shown in any case.
+ ##
+ #
+ # @since 1.0
+ ##
+ 'smwgQSubcategoryDepth' => 10, // Restrict level of sub-category inclusion (steps within category hierarchy)
+ 'smwgQSubpropertyDepth' => 10, // Restrict level of sub-property inclusion (steps within property hierarchy)
+ // (Use 0 to disable hierarchy-inferencing in queries)
+ 'smwgQEqualitySupport' => SMW_EQ_SOME, // Evaluate #redirects as equality between page names, with possible
+ // performance-relevant restrictions depending on the storage engine
+ // 'smwgQEqualitySupport' => SMW_EQ_FULL, // Evaluate #redirects as equality between page names in all cases
+ // 'smwgQEqualitySupport' => SMW_EQ_NONE, // Never evaluate #redirects as equality between page names
+ 'smwgQDefaultNamespaces' => null, // Which namespaces should be searched by default?
+ // (value NULL switches off default restrictions on searching -- this is faster)
+ // Example with namespaces: 'smwgQDefaultNamespaces' => array(NS_MAIN, NS_FILE)
+
+ ###
+ # Sort features
+ #
+ # - SMW_QSORT_NONE
+ #
+ # - SMW_QSORT: General sort support for query results (was
+ # $smwgQSortingSupport)
+ #
+ # - SMW_QSORT_RANDOM: Random sorting support for query results (was
+ # $smwgQRandSortingSupport)
+ #
+ # @since 3.0
+ ##
+ 'smwgQSortFeatures' => SMW_QSORT | SMW_QSORT_RANDOM,
+ ##
+
+ ###
+ # List of comparator characters
+ #
+ # Comparators supported by queries with available entries being:
+ #
+ # < (smaller than) if $smwStrictComparators is false, it's actually smaller
+ # than or equal to
+ # > (greater than) if $smwStrictComparators is false, it's actually bigger
+ # than or equal to
+ # ! (unequal to)
+ # ~ (pattern with '*' as wildcard)
+ # !~ (not a pattern with '*' as wildcard, only for Type:String, need to be
+ # placed before ! and ~ to work correctly)
+ # ≤ (smaller than or equal to)
+ # ≥ (greater than or equal to)
+ #
+ # Extra compartors that in case of an enabled full-text index uses the primary
+ # LIKE/NLIKE match operation with operators being:
+ #
+ # like: to express LIKE use
+ # nlike: to express NLIKE use
+ #
+ # If unsupported comparators are used, they are treated as part of the
+ # queried value.
+ #
+ # @since 1.0
+ ##
+ 'smwgQComparators' => '<|>|!~|!|~|≤|≥|<<|>>|~=|like:|nlike:|in:|not:|phrase:',
+ ##
+
+ ###
+ # Sets whether the > and < comparators should be strict or not. If they are strict,
+ # values that are equal will not be accepted.
+ #
+ # @since 1.5.3
+ ##
+ 'smwStrictComparators' => false,
+
+ // To be used starting with 3.x (due to misspelling)
+ 'smwgQStrictComparators' => false,
+ ##
+
+ ###
+ # Further settings for queries. The following settings affect inline queries
+ # and querying special pages. Essentially they should mirror the kind of
+ # queries that should immediately be answered by the wiki, using whatever
+ # computations are needed.
+ #
+ # @since 1.0
+ ##
+ 'smwgQMaxSize' => 16, // Maximal number of conditions in queries, use format=debug for example sizes
+ 'smwgQMaxDepth' => 4, // Maximal property depth of queries, e.g. [[rel::<q>[[rel2::Test]]</q>]] has depth 2
+ ##
+
+ ###
+ # Expensive threshold
+ #
+ # The threshold defined in seconds denotes the ceiling as to when a #ask or
+ # #show call is classified as expensive and will count towards the
+ # $smwgQExpensiveExecutionLimit setting.
+ #
+ # @since 3.0
+ # @default 10
+ ##
+ 'smwgQExpensiveThreshold' => 10,
+ ##
+
+ ###
+ # Limit of expensive #ask/#show functions
+ #
+ # The limit will count all classified #ask/#show parser functions and restricts
+ # further use on pages that exceed that limit.
+ #
+ # @since 3.0
+ # @default false (== no limit)
+ ##
+ 'smwgQExpensiveExecutionLimit' => false,
+ ##
+
+ ###
+ # The below setting defines which query features should be available by
+ # default.
+ #
+ # Examples:
+ # only cateory intersections: 'smwgQFeatures' => SMW_CATEGORY_QUERY | SMW_CONJUNCTION_QUERY,
+ # only single concepts: 'smwgQFeatures' => SMW_CONCEPT_QUERY,
+ # anything but disjunctions: 'smwgQFeatures' => SMW_ANY_QUERY & ~SMW_DISJUNCTION_QUERY,
+ # The default is to support all basic features.
+ #
+ # @since 1.2
+ ##
+ 'smwgQFeatures' => SMW_PROPERTY_QUERY | SMW_CATEGORY_QUERY | SMW_CONCEPT_QUERY | SMW_NAMESPACE_QUERY | SMW_CONJUNCTION_QUERY | SMW_DISJUNCTION_QUERY,
+ ##
+
+ ###
+ # Filter duplicate query segments
+ #
+ # Experimental feature that allows to filter duplicate query segments from the
+ # query build process to eliminate computational effort for segments that
+ # represent that same query signature.
+ #
+ # @since 2.5
+ # @default: false
+ ##
+ 'smwgQFilterDuplicates' => false,
+ ##
+
+ ###
+ # Settings about printout of (especially inline) queries:
+ #
+ # @since 1.0
+ ##
+ 'smwgQDefaultLimit' => 50, // Default number of rows returned in a query. Can be increased with limit=num in #ask
+ 'smwgQMaxInlineLimit' => 500, // Max number of rows ever printed in a single inline query on a single page.
+ 'smwgQPrintoutLimit' => 100, // Max number of supported printouts (added columns in result table, ?-statements)
+ 'smwgQDefaultLinking' => 'all', // Default linking behavior. Can be one of "none", "subject" (first column), "all".
+ #
+ # @since 2.1
+ ##
+ 'smwgQUpperbound' => 5000, // Max number of rows ever printed in a single inline query on a single page with an offset.
+ ##
+
+ ###
+ # Further settings for queries. The following settings affect queries that are
+ # part of concept pages. These are usually chosen to be les restricted than
+ # inline queries, since there are two other means for controling their use:
+ # (1) Concept queries that would not be allowed as normal queries will not be
+ # executed directly, but can use pre-computed results instead. This is the
+ # default.
+ # (2) The whole Concept: namespace can be restricted (using some suitable
+ # MediaWiki extension) to an experienced user group that may create more
+ # complex queries responably. Other users can employ thus defined concepts in
+ # their queries.
+ ##
+ 'smwgQConceptCaching' => CONCEPT_CACHE_HARD, // Which concepts should be displayed only if available from cache?
+ // CONCEPT_CACHE_ALL -- show concept elements anywhere only if they are cached
+ // CONCEPT_CACHE_HARD -- show without cache if concept is not harder than permitted inline queries
+ // CONCEPT_CACHE_NONE -- show all concepts even without any cache
+ // In any cases, caches will always be used if available.
+ 'smwgQConceptMaxSize' => 20, // Same as $smwgQMaxSize, but for concepts
+ 'smwgQConceptMaxDepth' => 8, // Same as $smwgQMaxDepth, but for concepts
+
+ // Same as $smwgQFeatures but for concepts
+ 'smwgQConceptFeatures' => SMW_PROPERTY_QUERY | SMW_CATEGORY_QUERY | SMW_NAMESPACE_QUERY |
+ SMW_CONJUNCTION_QUERY | SMW_DISJUNCTION_QUERY | SMW_CONCEPT_QUERY,
+
+ // Cache life time in minutes. If a concept cache exists but is older than
+ // this, SMW tries to recompute it, and will only use the cache if this is not
+ // allowed due to settings above:
+ 'smwgQConceptCacheLifetime' => 24 * 60,
+ ##
+
+ ##
+ # Predefined result formats for queries
+ #
+ # Array of available formats for formatting queries. Can be redefined in
+ # the settings to disallow certain formats or to register extension formats.
+ # To disable a format, do "unset($smwgResultFormats['template'])," Disabled
+ # formats will be treated like if the format parameter had been omitted. The
+ # formats 'table' and 'list' are defaults that cannot be disabled. The format
+ # 'broadtable' should not be disabled either in order not to break Special:ask.
+ ##
+ 'smwgResultFormats' => [
+ 'table' => 'SMW\Query\ResultPrinters\TableResultPrinter',
+ 'broadtable' => 'SMW\Query\ResultPrinters\TableResultPrinter',
+ 'list' => 'SMW\Query\ResultPrinters\ListResultPrinter',
+ 'plainlist' => 'SMW\Query\ResultPrinters\ListResultPrinter',
+ 'ol' => 'SMW\Query\ResultPrinters\ListResultPrinter',
+ 'ul' => 'SMW\Query\ResultPrinters\ListResultPrinter',
+ 'category' => 'SMW\Query\ResultPrinters\CategoryResultPrinter',
+ 'embedded' => 'SMW\EmbeddedResultPrinter',
+ 'template' => 'SMW\Query\ResultPrinters\ListResultPrinter',
+ 'count' => 'SMW\Query\ResultPrinters\NullResultPrinter',
+ 'debug' => 'SMW\Query\ResultPrinters\NullResultPrinter',
+ 'feed' => 'SMW\Query\ResultPrinters\FeedExportPrinter',
+ 'csv' => 'SMW\Query\ResultPrinters\CsvFileExportPrinter',
+ 'templatefile' => 'SMW\Query\ResultPrinters\TemplateFileExportPrinter',
+ 'dsv' => 'SMW\DsvResultPrinter',
+ 'json' => 'SMW\JsonResultPrinter',
+ 'rdf' => 'SMW\RdfResultPrinter'
+ ],
+ ##
+
+ ##
+ # Predefined aliases for result formats
+ #
+ # Array of available aliases for result formats. Can be redefined in
+ # the settings to disallow certain aliases or to register extension aliases.
+ # To disable an alias, do "unset($smwgResultAliases['alias'])," Disabled
+ # aliases will be treated like if the alias parameter had been omitted.
+ #
+ # @since 1.8
+ ##
+ 'smwgResultAliases' => [
+ 'feed' => [ 'rss' ],
+ 'templatefile' => [ 'template file' ],
+ 'plainlist' => [ 'plain' ]
+ ],
+ ##
+
+ ##
+ # Result printer features
+ #
+ # - SMW_RF_NONE
+ # - SMW_RF_TEMPLATE_OUTSEP, #2022 (use the sep parameter as outer separator)
+ #
+ # @since 2.3
+ ##
+ 'smwgResultFormatsFeatures' => SMW_RF_TEMPLATE_OUTSEP,
+ ##
+
+ ###
+ # Handling of `RemoteRequest` features
+ #
+ # - SMW_REMOTE_REQ_SEND_RESPONSE allows Special:Ask to respond to remote requests in
+ # combination with $smwgQuerySources and the `RemoteRequest`.
+ #
+ # - SMW_REMOTE_REQ_SHOW_NOTE shows a note for each remote requests so users are aware
+ # that results retrieved from an external source.
+ #
+ # If `$smwgQuerySources` contains no entries then a remote request to a source
+ # is not supported and only sources that are available through the setting
+ # can be selected as remote source.
+ #
+ # @since 3.0
+ # @default: SMW_REMOTE_REQ_SEND_RESPONSE | SMW_REMOTE_REQ_SHOW_NOTE
+ ##
+ 'smwgRemoteReqFeatures' => SMW_REMOTE_REQ_SEND_RESPONSE | SMW_REMOTE_REQ_SHOW_NOTE,
+ ##
+
+ ###
+ #
+ # Predefined list of sources that can return query results
+ #
+ # Array of available sources for answering queries. Can be redefined in
+ # the settings to register new sources (usually an extension will do so
+ # on installation). Unknown source will be rerouted to the local wiki.
+ # Note that the basic installation comes with no additional source besides
+ # the local source (which in turn cannot be disabled or set explicitly).
+ #
+ # A query class handler is required to implement the `QueryEngine` interface
+ # and if it needs to be aware of the store, it should also implement the
+ # `StoreAware` interface.
+ #
+ # @since 1.4.3
+ ##
+ 'smwgQuerySources' => [
+ // 'local' => '',
+ // 'mw-wiki-foo' => [ '\SMW\Query\RemoteRequest', 'url' => 'http://example.org/wiki/index.php' ],
+ ],
+ ##
+
+ ### Default property type
+ # Undefined properties (those without pages or whose pages have no "has type"
+ # statement) will be assumed to be of this type. This is an internal type id.
+ # See the file languages/SMW_LanguageXX.php to find what IDs to use for
+ # datatpyes in your language. The default corresponds to "Type:Page".
+ #
+ # @since 1.1.2
+ ##
+ 'smwgPDefaultType' => '_wpg',
+ ##
+
+ ###
+ # The maximal number that SMW will normally display without using scientific exp
+ # notation. The deafult is rather large since some users have problems understanding
+ # exponents. Scineitfic applications may prefer a smaller value for concise display.
+ #
+ # @since 1.4.3
+ ##
+ 'smwgMaxNonExpNumber' => 1000000000000000,
+ ##
+
+ ###
+ # SMW defers some tasks until after a page was edited by using the MediaWiki
+ # job queueing system (see http://www.mediawiki.org/wiki/Manual:Job_queue).
+ # For example, when the type of a property is changed, all affected pages will
+ # be scheduled for (later) update. If a wiki generates too many jobs in this
+ # way (Special:Statistics and "showJobs.php" can be used to check that), the
+ # following setting can be used to disable jobs. Note that this will cause some
+ # parts of the semantic data to get out of date, so that manual modifications
+ # or the use of SMW_refreshData.php might be needed.
+ #
+ # @since 1.1.2
+ ##
+ 'smwgEnableUpdateJobs' => true,
+ ##
+
+ ###
+ # JobQueue watchlist
+ #
+ # This setting allows to display a personal bar link that shows the queue
+ # sizes for listed jobs. The information presented is fetched from the
+ # MediaWiki API and might be slightly inaccurate but should allow to make
+ # assumptions as to where the system needs attention.
+ #
+ # @see https://www.mediawiki.org/wiki/Manual:Job_queue#Special:Statistics
+ #
+ # To make this feature available, assign a simple list to the setting as in:
+ #
+ # $GLOBALS['smwgJobQueueWatchlist'] = [
+ # 'smw.update',
+ # 'smw.parserCachePurge',
+ # 'smw.fulltextSearchTableUpdate',
+ # 'smw.changePropagationUpdate'
+ # ]
+ #
+ # Information are not displayed unless a user enables the setting in his or
+ # her preference setting.
+ #
+ # @since 3.0
+ # @default disabled (empty array)
+ ##
+ 'smwgJobQueueWatchlist' => [],
+ ##
+
+ ###
+ # List of enabled special page properties.
+ #
+ # - `_MDAT` Modification date is enabled by default for backward compatibility.
+ # - `_TRANS` Add annotations (language, source etc. ) when a page is
+ # indentified as translation page (as done by the Translation extension)
+ #
+ # Extend array to enable other properties:
+ # $smwgPageSpecialProperties[ => '_CDAT',
+ # Or:
+ # array_merge( $smwgPageSpecialProperties, array( '_CDAT' ) ),
+ # Or rewrite entire array:
+ # 'smwgPageSpecialProperties' => array( '_MDAT', '_CDAT' ),
+ #
+ # However, DO NOT use `+=' operator! This DOES NOT work:
+ # $smwgPageSpecialProperties += array( '_MDAT' ),
+ #
+ # @since 1.7
+ ##
+ 'smwgPageSpecialProperties' => [ '_MDAT' ],
+ ##
+
+ ###
+ # Change propagation watchlist
+ #
+ # Properties (usually given as internal ids or DB key versions of property
+ # titles) that are relevant for declaring the behavior of a property P on a
+ # property page in the sense that changing their values requires that all
+ # pages that use P must be processed again.
+ #
+ # For example, if _PVAL (allowed values) for a property change, then pages
+ # must be processed again. This setting is not normally changed by users but
+ # by extensions that add new types that have their own additional declaration
+ # properties.
+ #
+ # @since 1.5
+ ##
+ 'smwgChangePropagationWatchlist' => [
+ '_PVAL', '_LIST', '_PVAP', '_PVUC', '_PDESC', '_PPLB', '_PREC', '_PDESC',
+ '_SUBP', '_SUBC', '_PVALI'
+ ],
+ ##
+
+ ##
+ # Change propagation protection
+ #
+ # An administrative intervention to disable the protection for an active change
+ # propagation.
+ #
+ # @since 3.0
+ # @default true
+ ##
+ 'smwgChangePropagationProtection' => true,
+ ##
+
+ ###
+ # By default, DataTypes (Date, URL etc.) are registered with a corresponding
+ # property of the same name to match the expected semantics. Yet, users can
+ # decide to change the behaviour by exempting listed DataTypes from the property
+ # registration process.
+ #
+ # @since 2.5
+ ##
+ 'smwgDataTypePropertyExemptionList' => [
+ 'Record',
+ 'Reference',
+ 'Keyword'
+ ],
+ ##
+
+ ##
+ # Default output formatter
+ #
+ # Users who want to alter the default output for a specific type can do so by
+ # setting a specify default formatter.
+ #
+ # The expected form is:
+ #
+ # [ <_typeID> => '<Formatter>' ] OR
+ # [ <typeName> => '<Formatter>' ] OR
+ # [ <propertyName> => '<Formatter>' ]
+ #
+ # Only valid formatters will be considered for an individual type, no
+ # errors or exceptions are raised in case of an improper formatter.
+ #
+ # The formatter is applied to values displayed on special pages
+ # as well.
+ #
+ # @since 3.0
+ # @default: []
+ ##
+ 'smwgDefaultOutputFormatters' => [
+ // '_dat' => 'LOCL',
+ // 'Boolean' => 'tick',
+ ],
+ ##
+
+ // some default settings which usually need no modification
+
+ ###
+ # -- FEATURE IS DISABLED --
+ # Setting this to true allows to translate all the labels within
+ # the browser GIVEN that they have interwiki links.
+ #
+ # @since 0.7
+ ##
+ 'smwgTranslate' => false,
+ ##
+
+ ###
+ # -- FEATURE IS DISABLED --
+ # If you want to import ontologies, you need to install RAP,
+ # a free RDF API for PHP, see
+ # http://wifo5-03.informatik.uni-mannheim.de/bizer/rdfapi/index.html
+ # The following is the path to your installation of RAP
+ # (the directory where you extracted the files to) as seen
+ # from your local filesystem. Note that ontology import is
+ # highly experimental at the moment, and may not do what you
+ # extect.
+ #
+ # @since 1.0
+ ##
+ // 'smwgRAPPath' => $smwgIP . 'libs/rdfapi-php',
+ // 'smwgRAPPath' => '/another/example/path/rdfapi-php',
+ ##
+
+ ###
+ # List of Special:SemanticMediaWiki (or Special:SMWAdmin) features
+ #
+ # - SMW_ADM_REFRESH: to initiate the repairing or updating of all wiki data
+ # - SMW_ADM_SETUP: restrict to "Database installation and upgrade"
+ # - SMW_ADM_DISPOSAL: restrict access to the "Object ID lookup and disposal"
+ # feature and the "Outdated entities disposal"
+ # - SMW_ADM_PSTATS: Property statistics update
+ # - SMW_ADM_FULLT:
+ #
+ # @since 2.5
+ ##
+ 'smwgAdminFeatures' => SMW_ADM_REFRESH | SMW_ADM_SETUP | SMW_ADM_DISPOSAL | SMW_ADM_PSTATS | SMW_ADM_FULLT,
+ ##
+
+ ###
+ # Sets whether or not to refresh the pages of which semantic data is stored.
+ #
+ # @since 1.5.6
+ ##
+ 'smwgAutoRefreshSubject' => true,
+ ##
+
+ ###
+ # Semantic MediaWiki uses various cache instances and types to improve access
+ # and re-access to objects. `smwgMainCacheType` identifies the "main" type
+ # to be used for a persitent storage to a vendor (SQL, memcache, redis etc.)
+ # specific solution.
+ #
+ # `CACHE_ANYTHING` refers to settings available in `$wgMessageCacheType` or
+ # `$wgParserCacheType` if they are set.
+ #
+ # @see https://www.semantic-mediawiki.org/wiki/Help:Caching
+ # @see http://www.mediawiki.org/wiki/$wgMainCacheType
+ #
+ # @since 3.0
+ # @default CACHE_ANYTHING
+ ##
+ 'smwgMainCacheType' => CACHE_ANYTHING,
+ ##
+
+ ###
+ # Separate cache type to allow for adding a more responsive cache layer
+ # (redis, riak) when requesting value lookups from the SQLStore.
+ #
+ # CACHE_NONE = disabled, uses the standard SQLStore DB access for all
+ # lookups
+ #
+ # @since 2.3 (experimental)
+ #
+ # @default: CACHE_NONE, users need to actively enable it in order
+ # to make use of it
+ ##
+ 'smwgEntityLookupCacheType' => CACHE_NONE,
+ ##
+
+ ###
+ # Declares a lifetime of a cached item for `smwgEntityLookupCacheType` until it
+ # is removed if not invalidated before.
+ #
+ # @since 2.3
+ ##
+ 'smwgEntityLookupCacheLifetime' => 60 * 60 * 24 * 7, // a week
+ ##
+
+ ##
+ # Features expected to be enabled in CachedValueLookupStore
+ #
+ # Flags that declare a enable/disable state of a supported functionality. If a
+ # feature is disabled then a connection is always established to the standard
+ # Repository/DB backend.
+ #
+ # The settings are only relevant for cases where `smwgEntityLookupCacheType` is
+ # set.
+ #
+ # - SMW_VL_SD: corresponds to Store::getSemanticData
+ # - SMW_VL_PL: corresponds to Store::getProperties
+ # - SMW_VL_PV: corresponds to Store::getPropertyValues
+ # - SMW_VL_PS: corresponds to Store::getPropertySubjects
+ #
+ # @since 2.3
+ #
+ # @default: all features are enabled
+ ##
+ 'smwgEntityLookupFeatures' => SMW_VL_SD | SMW_VL_PL | SMW_VL_PV | SMW_VL_PS,
+ ##
+
+ ###
+ # CacheTTL settings
+ #
+ # Defines time to live for in Semantic MediaWiki used cache instances and
+ # requires $smwgMainCacheType to be set otherwise related settings will have
+ # no effect.
+ #
+ # - special.wantedproperties TTL (in sec, or false to disable it) for caching
+ # the lookup on wanted property usage
+ #
+ # - special.unusedproperties TTL (in sec, or false to disable it) for caching
+ # the lookup on unused property usage
+ #
+ # - special.properties TTL (in sec, or false to disable it) for caching the
+ # lookup on property usage
+ #
+ # - special.statistics TTL (in sec, or false to disable it) for caching the
+ # lookup on statistics
+ #
+ # - api.browse TTL (in sec, or false to disable it) for the API browse module
+ # as general cache
+ #
+ # - api.browse.pvalue TTL (in sec, or false to disable it) for the API browse
+ # pvalue module when requesting property values
+ #
+ # - api.browse.psubject TTL (in sec, or false to disable it) for the API browse
+ # psubject module when requesting property subjects
+ #
+ # - api.task TTL (in sec, or false to disable it) for the API task module
+ #
+ # @since 1.9
+ ##
+ 'smwgCacheUsage' => [
+ 'special.wantedproperties' => 3600,
+ 'special.unusedproperties' => 3600,
+ 'special.properties' => 3600,
+ 'special.statistics' => 3600,
+ 'api.browse' => 3600,
+ 'api.browse.pvalue' => 3600,
+ 'api.browse.psubject' => 3600,
+ 'api.task' => 3600
+ ],
+ ##
+
+ ###
+ # Sets whether or not to refresh semantic data in the store when a page is
+ # manually purged
+ #
+ # @since 1.9
+ #
+ # @requires $smwgMainCacheType be set
+ # @default true
+ ##
+ 'smwgAutoRefreshOnPurge' => true,
+ ##
+
+ ###
+ # Sets whether or not to refresh semantic data in the store when a page was
+ # moved
+ #
+ # @since 1.9
+ #
+ # @requires $smwgMainCacheType be set
+ # @default true
+ ##
+ 'smwgAutoRefreshOnPageMove' => true,
+ ##
+
+ ##
+ # List of user-defined fixed properties
+ #
+ # Listed properties are managed by its own fixed table (instad of a
+ # shared one) to allow for sharding large datasets with value assignments.
+ #
+ # The type definition is talen from the property page `[[Has type::...]]` and
+ # by default (if no type is defined) then the `smwgPDefaultType` is returned.
+ #
+ # Any change to the property type requires to run the `setupStore.php` script
+ # or `Special:SMWAdmin` table update.
+ #
+ # 'smwgFixedProperties' => array(
+ # 'Age',
+ # 'Has population'
+ # ),
+ #
+ # @see https://semantic-mediawiki.org/wiki/Fixed_properties
+ # @since 1.9
+ #
+ # @default array()
+ ##
+ 'smwgFixedProperties' => [],
+
+ ###
+ # Sets a threshold value for when a property is being highlighted as "hardly
+ # begin used" on Special:Properties
+ #
+ # @since 1.9
+ #
+ # default = 5
+ ##
+ 'smwgPropertyLowUsageThreshold' => 5,
+ ##
+
+ ###
+ # Hide properties where the usage count is zero on Special:Properties
+ #
+ # @since 1.9
+ #
+ # default = true (legacy behaviour)
+ ##
+ 'smwgPropertyZeroCountDisplay' => true,
+ ##
+
+ ###
+ # QueryProfiler related settings
+ #
+ # @note If these settings are changed, please ensure to run update.php/rebuildData.php
+ #
+ # - smwgQueryProfiler can be set false to disable its functionality but it
+ # may impact secondary processes that rely on profile information to be
+ # available (Notification system etc.)
+ #
+ # - SMW_QPRFL_DUR to record query duration (the time
+ # between the query result selection and output its)
+ #
+ # - SMW_QPRFL_PARAMS to record query parameters that are necessary
+ # for allowing to generate a query result using a background job
+ #
+ # $smwgQueryProfiler = SMW_QPRFL_DUR | SMW_QPRFL_PARAMS;
+ #
+ # @since 1.9
+ # @default true
+ ##
+ 'smwgQueryProfiler' => true,
+ ##
+
+ ###
+ # Enables SMW specific annotation and content processing for listed SpecialPages
+ #
+ # @since 1.9
+ ##
+ 'smwgEnabledSpecialPage' => [ 'Ask' ],
+ ##
+
+ ###
+ # Search engine to fall back to in case SMWSearch is used as custom search
+ # engine but is unable to interpret the search term as an SMW query
+ #
+ # Leave as null to select the default search engine for the selected database
+ # type (e.g. SearchMySQL, SearchPostgres or SearchOracle), or set to a class
+ # name to override to a custom search engine.
+ #
+ # @since 2.1
+ ##
+ 'smwgFallbackSearchType' => null,
+ ##
+
+ ###
+ # If enabled it will display help information on the edit page to support users
+ # unfamiliar with SMW when extending page content.
+ #
+ # @since 2.1
+ ##
+ 'smwgEnabledEditPageHelp' => true,
+ ##
+
+ ###
+ # Various MediaWiki update operations in MW 1.26+ started to use DeferredUpdates
+ # and to ensure that the Store update follows in queue of updates made to a page
+ # this setting should be enabled by default for MW 1.26 onwards.
+ #
+ # It will improve page responsiveness for purge and move action significantly.
+ #
+ # @since 2.4
+ ##
+ 'smwgEnabledDeferredUpdate' => true,
+ ##
+
+ ###
+ # Regulates task specific settings for the post-edit process.
+ #
+ # The main objective is to defer secondary updates until after the GET request
+ # has been finalized so that resource requirements are part of an API request
+ # (and not a GET) and hereby ensures that a client remains responsive
+ # independent of the update workload.
+ #
+ # `run-jobs` specifies jobs that should be executed on a post-edit to run in a
+ # timely manner independent of a users job scheduler environment. The number
+ # indicates the expected number of jobs to be executed per request.
+ #
+ # @experimental
+ #
+ # `check-query` The display of query results and the storage of entities that
+ # make up the results of a query are two distinct processes. The display
+ # normally happens before the storage due to how the MW parser works meaning
+ # that a query can only display the most recent results after a page has
+ # been processed and rendered while the storage is being deferred (or in case
+ # of an external store is influenced by the network lag).
+ #
+ # The `check-query` uses the `post-edit` event to run registered queries and
+ # if necessary reloads the page (hereby refreshes the results) in case the
+ # result is different by comparing the `result_hash` from before and after.
+ # To determine the query state, the `post-edit` has to invoke the API (as
+ # background task) which has to probe the query and to only run the query once
+ # for the page that embeds the query, it is strongly recommended that this
+ # option is only enabled together with:
+ # - the query cache (@see $smwgQueryResultCacheType) and
+ # - the query links store (@see $smwgEnabledQueryDependencyLinksStore)
+ #
+ # @since 3.0
+ ##
+ 'smwgPostEditUpdate' => [
+ 'check-query' => false,
+ 'run-jobs' => [
+ 'smw.fulltextSearchTableUpdate' => 1,
+ 'smw.parserCachePurge' => 5
+ ]
+ ],
+ ##
+
+ ###
+ # Query dependency and parser cache invalidation
+ #
+ # If enabled it will store dependencies for queries allowing it to purge
+ # the ParserCache on subjects with embedded queries that contain altered entities.
+ #
+ # The setting requires to run `update.php` (it creates an extra table). Also
+ # as noted in #1117, `SMW\ParserCachePurgeJob` should be scheduled accordingly.
+ #
+ # @since 2.3 (experimental)
+ # @default false
+ ##
+ 'smwgEnabledQueryDependencyLinksStore' => false,
+ ##
+
+ ###
+ # Relates to `smwgEnabledQueryDependencyLinksStore` and defines property keys
+ # to be excluded from the dependency detection.
+ #
+ # For example, to avoid a purge process being triggered for each altered subobject
+ # '_SOBJ' is excluded from the processing but it will not exclude any properties
+ # defined by a subobject (given that it is not part of an extended exclusion list).
+ #
+ # `_MDAT` is excluded to avoid a purge on each page edit with a `Modification date`
+ # change that would otherwise trigger a dependency update.
+ #
+ # '_ASKDU' changes to the duration of a query should not trigger an update of
+ # possible query dependencies (as this has no bearing on the result list).
+ #
+ # @since 2.3 (experimental)
+ ##
+ 'smwgQueryDependencyPropertyExemptionList' => [ '_MDAT', '_SOBJ', '_ASKDU' ],
+ ##
+
+ ###
+ # Listed properties are marked as affiliate, meaning that when an alteration to
+ # a property value occurs query dependencies for the related entity are recorded
+ # as well. For example, _DTITLE is most likely such property where a change would
+ # normally not be reflected in query results (as it not directly linked to a
+ # query) but when added as an affiliated, changes to its content will be
+ # handled as if it is linked to an embedded entity.
+ #
+ # @since 2.4 (experimental)
+ ##
+ 'smwgQueryDependencyAffiliatePropertyDetectionList' => [],
+ ##
+
+ ###
+ # Settings for OWL/RDF export
+ #
+ # Whether or not "normal" users can request an recursive export.
+ #
+ # @since 0.7
+ # @default = false
+ ##
+ 'smwgAllowRecursiveExport' => false,
+ ##
+
+ ###
+ # Settings for OWL/RDF export
+ #
+ # Whether or not backlinks should be included by default.
+ #
+ # @since 0.7
+ # @default = true
+ ##
+ 'smwgExportBacklinks' => true,
+ ##
+
+ ###
+ # OWL/RDF export namespace for URIs/IRIs
+ #
+ # Will be set automatically if nothing is given, but in order to make pretty
+ # URIs you will need to set this to something nice and adapt your Apache
+ # configuration appropriately.
+ #
+ # @see https://www.semantic-mediawiki.org/wiki/Help:$smwgNamespace
+ # @see https://www.semantic-mediawiki.org/wiki/Help:EnableSemantics
+ # @see https://www.semantic-mediawiki.org/wiki/Help:Pretty_URIs
+ #
+ # @since 1.0
+ # @default = ''
+ ##
+ // 'smwgNamespace' => "http://example.org/id/",
+ ##
+
+ ###
+ # The setting is introduced the keep backwards compatibility with existing Rdf/Turtle
+ # exports. The `aux` marker is expected only used to be used for selected properties
+ # to generate a helper value and not for any other predefined property.
+ #
+ # Any property that does not explicitly require an auxiliary value (such `_dat`/
+ # `_geo` type values) now uses its native as condition descriptor (`Has_subobject`
+ # instead of `Has_subobject-23aux`)
+ #
+ # For SPARQL repository users that don't want to run an a `rebuildData.php`,
+ # the setting has to be TRUE.
+ #
+ # This BC setting is planned to vanish with 3.x.
+ #
+ # @since 2.3
+ ##
+ 'smwgExportBCAuxiliaryUse' => false,
+ ##
+
+ ##
+ # The preferred form is to use canonical identifiers (Category:, Property:)
+ # instead of localized names to ensure that RDF/Query statements are language
+ # agnostic and do work even after the site/content language changes.
+ #
+ # This BC setting is planned to vanish with 3.x.
+ #
+ # @since 2.3
+ ##
+ 'smwgExportBCNonCanonicalFormUse' => false,
+ ##
+
+ ##
+ # Export resources using IRIs
+ #
+ # Instead of ASCII encoded URI's, allow resources to be exported as IRI's (RFC
+ # 3987).
+ #
+ # @see https://www.w3.org/TR/rdf11-concepts/#section-IRIs
+ #
+ # @since 2.5
+ # @default true
+ ##
+ 'smwgExportResourcesAsIri' => true,
+ ##
+
+ ###
+ # Features related to text and annotion parsing
+ #
+ # - SMW_PARSER_NONE
+ #
+ # - SMW_PARSER_STRICT: The default interpretation (strict) is to find a single
+ # triple such as [[property::value:partOfTheValue::alsoPartOfTheValue]] where
+ # in case the strict mode is disabled multiple properties can be assigned
+ # using a [[property1::property2::value]] notation but may cause value
+ # strings to be interpret unanticipated in case of additional colons.
+ #
+ # - SMW_PARSER_UNSTRIP: Support decoding (unstripping) of hidden text elements
+ # (e.g. `<nowiki>` as in `[[Has description::<nowiki>{{#ask: HasStripMarkers
+ # }}</nowiki>]]` etc.) within an annotation value (can only be stored together
+ # with a `_txt` type property).
+ #
+ # - SMW_PARSER_INL_ERROR: Should warnings be displayed in wikitexts right after
+ # the problematic input? This affects only semantic annotations, not warnings
+ # that are displayed by inline queries or other features. (was $smwgInlineErrors)
+ #
+ # - SMW_PARSER_HID_CATS: Switch to omit hidden categories (marked with
+ # __HIDDENCAT__) from the annotation process. Changing the setting requires
+ # to run a full rebuild to ensure hidden categories are discarded during
+ # the parsing process. (was $smwgShowHiddenCategories 1.9)
+ #
+ # - SMW_PARSER_LINV: Support parsing of "links in values" for annotations like
+ # [[SomeProperty::Foo [[link]] in [[Bar::AnotherValue]]]] (was $smwgLinksInValues
+ # with SMW_LINV_OBFU, SMW_LINV_PCRE is no longer available)
+ #
+ # @since 3.0
+ ##
+ 'smwgParserFeatures' => SMW_PARSER_STRICT | SMW_PARSER_INL_ERROR | SMW_PARSER_HID_CATS,
+ ##
+
+ ##
+ # Features or restrictions for specific DataValue types
+ #
+ # - SMW_DV_NONE
+ #
+ # - SMW_DV_PROV_REDI (PropertyValue) If a property is redirected to a different
+ # target (Foo -> Bar) then follow it by default in order to allow query results
+ # to be displayed equivalent for both queries without having to adjust
+ # (or change) a query. This flag is mainly provided to restore backwards
+ # compatibility where behaviour is not expected to be altered, nevertheless it is
+ # recommended that the setting is enabled to improve user friendliness in terms
+ # of query execution.
+ #
+ # - SMW_DV_MLTV_LCODE (MonolingualTextValue) is to require a language code in order
+ # for a DV to be completed otherwise a MLTV can operate without a language code
+ #
+ # - SMW_DV_PVAP (Allows pattern) to allow regular expression pattern matching
+ # when `Allows pattern` property is assigned to user-defined property
+ #
+ # - SMW_DV_WPV_DTITLE (WikiPageValue) is to allow requesting a lookup for a display
+ # title and if present will be used as caption for the invoked subject
+ #
+ # - SMW_DV_PROV_DTITLE (PropertyValue) in combination with SMW_DV_WPV_DTITLE, If
+ # enabled it will attempt to resolve a property label by matching it against a
+ # possible assigned property "Display title of" value. For example, property
+ # "Foo" has "Display title of" "hasFoolishFoo" where "hasFoolishFoo" is being
+ # resolved as "Foo" when creating annotations. Currently, this uses an
+ # uncached lookup and therefore is disabled by default to avoid a possible
+ # performance impact (which has not been established or analyzed).
+ #
+ # - SMW_DV_PVUC (Uniqueness constraint) to specify that a property can only
+ # assign a value that is unique in its literal representation (the state of
+ # uniqueness for a value is established by the fact that it is assigned before
+ # any other value of the same representation to a property).
+ #
+ # - SMW_DV_TIMEV_CM (TimeValue) to indicate the CalendarModel if is not a
+ # CM_GREGORIAN
+ #
+ # - SMW_DV_NUMV_USPACE (Number/QuantityValue) to preserve spaces within
+ # unit labels
+ #
+ # - SMW_DV_PPLB to support the use of preferred property labels
+ #
+ # - SMW_DV_PROV_LHNT (PropertyValue) to output a <sup>p</sup> hint marker on
+ # properties that use a preferred label
+ #
+ # @since 2.4
+ ##
+ 'smwgDVFeatures' => SMW_DV_PROV_REDI | SMW_DV_MLTV_LCODE | SMW_DV_PVAP | SMW_DV_WPV_DTITLE | SMW_DV_TIMEV_CM | SMW_DV_PPLB | SMW_DV_PROV_LHNT,
+ ##
+
+ ##
+ # Fulltext search support
+ #
+ # If enabled, it will store text elements using a separate table in order for
+ # the SQL back-end to use the special fulltext index operations provided by
+ # the SQL engine.
+ #
+ # - Tested with MySQL/MariaDB
+ # - Tested with SQLite
+ #
+ # @since 2.5
+ # @default: false
+ ##
+ 'smwgEnabledFulltextSearch' => false,
+ ##
+
+ ##
+ # Throttle index updates
+ #
+ # The objective is to postpone an update by relying on a deferred process that
+ # runs the index update decoupled from the storage back-end update.
+ #
+ #
+ # If a user wants to push updates to the updater immediately then this setting needs
+ # to be disabled but by disabling this setting update lag may increase due to having
+ # the process being executed synchronously to the wikipage update.
+ #
+ # @since 2.5
+ # @default: true
+ ##
+ 'smwgFulltextDeferredUpdate' => true,
+ ##
+
+ ##
+ # Fulltext search table options
+ #
+ # This setting directly influences how a ft table is created therefore change
+ # the content with caution.
+ #
+ # - MySQL version 5.5 or later with only MyISAM and InnoDB storage engines
+ # to support full-text search (according to sources)
+ #
+ # - MariaDB full-text indexes can be used only with MyISAM and Aria tables,
+ # from MariaDB 10.0.5 with InnoDB tables and from MariaDB 10.0.15
+ # with Mroonga tables (according to sources)
+ #
+ # - SQLite FTS3 has been available since version 3.5, FTS4 were added with
+ # version 3.7.4, and FTS5 is available with version 3.9.0 (according to
+ # sources), The setting allows to specify extra arguments after the module
+ # engine such as array( 'FTS4', 'tokenize=porter' ).
+ #
+ # It is possible to extend the option description (MySQL 5.7+) with
+ # 'mysql' => array( 'ENGINE=MyISAM, DEFAULT CHARSET=utf8', 'WITH PARSER ngram' )
+ #
+ # @since 2.5
+ ##
+ 'smwgFulltextSearchTableOptions' => [
+ 'mysql' => [ 'ENGINE=MyISAM, DEFAULT CHARSET=utf8' ],
+ 'sqlite' => [ 'FTS4' ]
+ ],
+ ##
+
+ ##
+ # Exempted properties
+ #
+ # List of property keys for which index and fulltext match operations are
+ # exempted because there are either insignificant, mostly represent single
+ # terms, or contain other characteristics that make them non preferable when
+ # searching via the fulltext index.
+ #
+ # Listed properties will use the standard LIKE/NLIKE match operation when used
+ # in connection with the ~/!~ expression.
+ #
+ # @since 2.5
+ ##
+ 'smwgFulltextSearchPropertyExemptionList' => [
+ '_ASKFO', '_ASKST', '_ASKPA','_IMPO', '_LCODE', '_UNIT', '_CONV',
+ '_TYPE', '_ERRT', '_INST', '_ASK', '_SOBJ', '_PVAL', '_PVALI',
+ '_REDI', '_CHGPRO'
+ ],
+ ##
+
+ ##
+ # List of indexable DataTypes
+ #
+ # - SMW_FT_BLOB property values of type Blob (Text)
+ # - SMW_FT_URI property values of type URI
+ # - SMW_FT_WIKIPAGE property values of type Page
+ #
+ # SMW_FT_WIKIPAGE has not been added as default value as no performance
+ # impact analysis is available as to how indexing and search performance would
+ # be affected by a wiki with a large pool of pages (10K+) or extended page
+ # type value assignments on a full-text index.
+ #
+ # Enabling SMW_FT_WIKIPAGE will support the same search features (case
+ # insensitivity, phrase matching etc.) as available for Text or URI values
+ # when searches are executed using the ~/!~.
+ #
+ # @since 2.5
+ # @default: SMW_FT_BLOB | SMW_FT_URI
+ ##
+ 'smwgFulltextSearchIndexableDataTypes' => SMW_FT_BLOB | SMW_FT_URI,
+ ##
+
+ ##
+ # Describes the minimum word/token length to help to decide whether MATCH or LIKE
+ # operators are to be used for a condition statement.
+ #
+ # For MySQL it is expected it corresponds to either innodb_ft_min_token_size or
+ # ft_min_word_len
+ #
+ # @since 2.5
+ ##
+ 'smwgFulltextSearchMinTokenSize' => 3,
+ ##
+
+ ##
+ # To detect a possible language candidate from an indexable text element.
+ #
+ # TextCatLanguageDetector, a large list of languages does have a detrimental
+ # influence on the performance when trying to detect a language from a free text.
+ #
+ # Stopwords are only applied after language detection has been enabled.
+ #
+ # @see https://github.com/wikimedia/wikimedia-textcat
+ #
+ # @since 2.5
+ # @default empty list (language detection is disabled by default)
+ ##
+ 'smwgFulltextLanguageDetection' => [
+ // 'TextCatLanguageDetector' => array( 'en', 'de', 'fr', 'es', 'ja', 'zh' )
+ // 'CdbNGramLanguageDetector' => array( 'en', 'de', 'fr', 'es', 'ja', 'zh' )
+ ],
+ ##
+
+ ##
+ # MySQL's "Global Transaction Identifier" will create issues when executing
+ # queries that rely on temporary tables, according to the documentation "... the
+ # operations listed here cannot be used ... CREATE TEMPORARY TABLE statements
+ # inside transactions".
+ #
+ # MySQL Global transaction identifier is a unique transaction ID assigned to
+ # every transaction that happens in the MySQL database.
+ #
+ # Issue is encountered when @@GLOBAL.ENFORCE_GTID_CONSISTENCY = 1
+ #
+ # @see https://dev.mysql.com/doc/refman/5.6/en/replication-options-gtids.html
+ # @see https://support.software.dell.com/kb/184275
+ #
+ # This setting (if enabled) will force an auto commit operation for temporary
+ # tables to avoid the described limitation.
+ #
+ # @since 2.5
+ # @default false
+ ##
+ 'smwgQTemporaryTablesAutoCommitMode' => false,
+ ##
+
+ ###
+ # Support to store a computed subject list that were fetched from the QueryEngine
+ # (not the string result generated from a result printer) and improve general
+ # page-loading time for articles that contain embedded queries and decrease
+ # server load on query requests.
+ #
+ # It is recommended that `smwgEnabledQueryDependencyLinksStore` is enabled
+ # to make use of automatic query results cache eviction.
+ #
+ # Enabling this option will render queries with a new ID to optimize queries
+ # that result in the same signature (fingerprint) to lower the rate of cache
+ # fragmentation.
+ #
+ # @since 2.5 (experimental)
+ # @default: CACHE_NONE (== feature is disabled)
+ ##
+ 'smwgQueryResultCacheType' => CACHE_NONE,
+ ##
+
+ ###
+ # Specifies the lifetime of embedded query and their results fetched from a
+ # QueryEngine for when `smwgQueryResultCacheType` is enabled.
+ #
+ # @since 2.5
+ ##
+ 'smwgQueryResultCacheLifetime' => 60 * 60 * 24 * 7, // a week
+ ##
+
+ ###
+ # Specifies the lifetime of non-embedded queries (Special:Ask, API etc.) and their
+ # results that are fetched from a QueryEngine for when `smwgQueryResultCacheType` is
+ # enabled.
+ #
+ # This setting can also be used to minimize a possible DoS vector by preventing
+ # an advisory to make unlimited query requests from either Special:Ask or the
+ # API that may lock the DB due to complex query answering and instead being
+ # rerouted to the cache once a result has been computed.
+ #
+ # @note Non-embedded queries cannot not be tracked using the `QueryDependencyLinksStore`
+ # (subject is being missing that would identify the entity) therefore
+ # an auto-purge mechanism as in case of an embedded entity is not possible hence
+ # the lifetime should be carefully selected to provide the necessary means for a
+ # user and the application.
+ #
+ # 0/false as setting to disable caching of non-embedded queries.
+ #
+ # @since 2.5
+ ##
+ 'smwgQueryResultNonEmbeddedCacheLifetime' => 60 * 10, // 10 min
+ ##
+
+ ###
+ # Enables the manual refresh for embedded queries when the action=purge event is
+ # triggered.
+ #
+ # @since 2.5
+ ##
+ 'smwgQueryResultCacheRefreshOnPurge' => true,
+ ##
+
+ ##
+ # Property create protection
+ #
+ # If enabled, users are able to annotate property values using existing properties
+ # but new properties can only be created by those with the assigned "authority"
+ # (aka user right).
+ #
+ # Changes to a property specification requires the permission as well.
+ #
+ # @since 3.0
+ # @default false
+ ##
+ 'smwgCreateProtectionRight' => false,
+ ##
+
+ ###
+ # Page edit protection
+ #
+ # To prevent accidental changes of content especially to those of property
+ # definitions, this setting allows with the help of the `Is edit protected`
+ # property to prevent editing on pages that have annotate the property with
+ # `true`.
+ #
+ # Once the property is set, only users with the listed user right are able
+ # to edit and/or revoke the restriction on the selected page.
+ #
+ # `smw-pageedit` has been deployed as extra right to be distinct from existing
+ # edit protections
+ #
+ # To enable this functionality either assign `smw-pageedit` or any other
+ # right to the variable to activate an edit protection.
+ #
+ # @since 2.5
+ # @default false
+ ##
+ 'smwgEditProtectionRight' => false,
+ ##
+
+ ##
+ # Similarity lookup exemption property
+ #
+ # The listed property is used to exclude a property from the similarity
+ # lookup in case the comparing property contains an annotation value with the
+ # exemption property.
+ #
+ # For example, the property `Governance level` may define
+ # [[owl:differentFrom::Governance level of]] which would result in a suppressed
+ # similarity lookup for both `Governance level` and `Governance level of`
+ # property when compared to each other.
+ #
+ # @since 2.5
+ ##
+ 'smwgSimilarityLookupExemptionProperty' => 'owl:differentFrom',
+ ##
+
+ ##
+ # Property label invalid characters
+ #
+ # Listed characters are categorized as invalid for a property label and will
+ # result in an error.
+ #
+ # @see #1568, #1638, 3134
+ #
+ # @since 2.5
+ ##
+ 'smwgPropertyInvalidCharacterList' => [ '[', ']' , '|' , '<' , '>', '{', '}', '+', '–', '%', "\r", "\n" ],
+ ##
+
+ ##
+ # Reserved property names
+ #
+ # Listed names are reserved as they may interfere with Semantic MediaWiki or
+ # MediaWiki itself.
+ #
+ # Removing default names from the list is not recommended (extending the list
+ # is supported and may be used to customize use cases for an individual wiki).
+ #
+ # The list can contain simple names or identifiers that start with
+ # `smw-property-reserved-` to link to a translatable representation that
+ # matches a string in a content language.
+ #
+ # @see #2835, #2840
+ #
+ # @since 3.0
+ ##
+ 'smwgPropertyReservedNameList' => [ 'Category', 'smw-property-reserved-category' ],
+ ##
+
+ ##
+ # Entity specific collation
+ #
+ # This should correspond to the $wgCategoryCollation setting (also in regards
+ # to selected argument values), yet it is kept separate to have a better
+ # control over changes in regards to the collation, sorting, and display of
+ # values.
+ #
+ # This setting is "global" and applies to any entity that is maintained for
+ # a wiki. In being global means that it cannot be selective (use one collation
+ # for one query and use another collation for a different query) because the
+ # field (smw_sort) contains a computed representation of the sort value.
+ #
+ # ANY change to this setting requires to run the `updateEntityCollation.php`
+ # maintenance script.
+ #
+ # @since 3.0
+ # @default identity (as legacy setting)
+ ##
+ 'smwgEntityCollation' => 'identity',
+ ##
+
+ ##
+ # Experimental settings
+ #
+ # Features enabled are considered stable but for any unforeseen behaviour, the
+ # feature can be disabled to return to a previous working state avoiding
+ # the need for hot-patching a system.
+ #
+ # After a certain in-production period, features will be moved permanently
+ # to the desired target state and hereby automatically retires the related
+ # setting.
+ #
+ # @since 3.0
+ ##
+ 'smwgExperimentalFeatures' => false,
+ ##
+
+ ##
+ # SQLStore specific field type features
+ #
+ # SMW_FIELDT_NONE
+ #
+ # SMW_FIELDT_CHAR_NOCASE - Modifies selected search fields to use a case
+ # insensitive collation and may require additional extension (e.g. Postgres
+ # requires `citext`) on non MySQL related systems therefore it is disabled
+ # by default.
+ #
+ # Furthermore, no extensive analysis has been performed on how the switch
+ # from VARBINARY to a collated VARCHAR field type affects the search
+ # performance.
+ #
+ # If enabled, the setting will replace selected `FieldType::FIELD_TITLE`
+ # types with `FieldType::TYPE_CHAR_NOCASE`.
+ #
+ # `FieldType::TYPE_CHAR_NOCASE` has been defined as:
+ #
+ # - MySQL: VARCHAR(255) CHARSET utf8 COLLATE utf8_general_ci
+ # - Postgres: citext NOT NULL
+ # - SQLite: VARCHAR(255) NOT NULL COLLATE NOCASE but according to [0] this may
+ # not work and need a special solution as hinted in [0]
+ #
+ # [0] https://techblog.dorogin.com/case-insensitive-like-in-sqlite-504f594dcdc3
+ #
+ # SMW_FIELDT_CHAR_LONG - Extends the size to 300 chars for text pattern
+ # match (DIBlob and DIUri) fields.
+ #
+ # 300 has been selected to be able to build an index prefix with the available
+ # default setting of MySQL/MariaDB which restricts the prefix length to 767
+ # bytes for InnoDB tables [1]. The index length can be lifted [2] to up to
+ # 3072 bytes for InnoDB tables that use the DYNAMIC or COMPRESSED row format but
+ # that requires custom intervention.
+ #
+ # [1] https://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html
+ # [2] https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_large_prefix
+ #
+ # By default, the SQLStore has restricted the DIBlob and DIUri fields to a
+ # 72 chars search depth in exchange for index size and performance.
+ # Extending fields to 300 allows to run LIKE/NLIKE matching on a larger text
+ # body without relying on a full-text index but an increased index size could
+ # potentially carry a performance penalty when the index cannot be kept in
+ # memory.
+ #
+ # No analysis has been performed on how performance is impacted. Selecting
+ # this option requires to run `rebuildData.php` to adjust the field content
+ # to the new length.
+ #
+ # SMW_FIELDT_CHAR_NOCASE | SMW_FIELDT_CHAR_LONG can be combined to build a
+ # case insensitive long field type.
+ #
+ # @since 3.0
+ # @default false
+ ##
+ 'smwgFieldTypeFeatures' => false,
+ ##
+
+ ##
+ # Subobject content hash !! BC setting ONLY !!
+ #
+ # Normalized content hash is enabled by default to ensure that a content
+ # declaration like:
+ #
+ # {{#subobject:
+ # |Has text=Foo,Bar|+sep=,
+ # }}
+ #
+ # yields the same hash as:
+ #
+ # {{#subobject:
+ # |Has text=Bar,Foo|+sep=,
+ # }}
+ #
+ # The setting is only provided to allow for a temporary backwards compatibility
+ # and will be removed with 3.1 at which point the functionality is enabled
+ # without any constraint.
+ #
+ # @since 3.0
+ # @default true
+ ##
+ 'smwgUseComparableContentHash' => true,
+ ##
+
+ ##
+ # List of supported schemes for a URI typed property
+ #
+ # @see https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
+ # @see https://www.w3.org/wiki/UriSchemes
+ #
+ # @since 3.0
+ ##
+ 'smwgURITypeSchemeList' => [
+ 'http', 'https', 'mailto', 'tel', 'ftp', 'sftp', 'news', 'file', 'urn',
+ 'telnet', 'ldap', 'gopher', 'ssh', 'git', 'irc', 'ircs'
+ ],
+ ##
+
+ ##
+ # Special:Ask form submit method
+ #
+ # - SMW_SASK_SUBMIT_POST uses `post` as submit method, allows to jump
+ # directly to the search result but will not produce any copyable URL
+ # string (use the result bookmark button instead)
+ #
+ # - SMW_SASK_SUBMIT_GET uses `get` as submit method and was the default
+ # until 2.5, is not able to jump the search result directly after a submit
+ #
+ # - SMW_SASK_SUBMIT_GET_REDIRECT uses `get` as submit method and provides
+ # the means to directly jump to the search result after a submit but
+ # requires an extra HTTP request to follow a redirect
+ #
+ # @since 3.0
+ # @default SMW_SASK_SUBMIT_POST
+ ##
+ 'smwgSpecialAskFormSubmitMethod' => SMW_SASK_SUBMIT_POST,
+ ##
+
+ ##
+ # Enable/disable <section> ... </section> support
+ #
+ # @since 3.0
+ # @default true
+ ##
+ 'smwgSupportSectionTag' => true,
+ ##
+
+ ##
+ # THE FOLLOWING SETTINGS AND SUPPORT FUNCTIONS ARE EXPERIMENTAL!
+ #
+ # Please make you read the Readme.md (see the Elastic folder) file first
+ # before enabling the ElasticStore and its settings!
+ ##
+
+ ##
+ # Schema types
+ #
+ # The mapping defines the relation between a specific type, group and
+ # a possible interpreter which validates the expected syntax.
+ #
+ # Each type will have its own interpretation about elements and how to
+ # define and enact requirements.
+ #
+ # @since 3.0
+ ##
+ 'smwgSchemaTypes' => [
+ 'LINK_FORMAT_SCHEMA' => [
+ 'validation_schema' => __DIR__ . '/data/schema/link-format-schema.v1.json',
+ 'group' => SMW_SCHEMA_GROUP_FORMAT,
+ 'type_description' => 'smw-schema-description-link-format-schema',
+ // '__factory' => [ 'SMW\Schema\SchemaFactory', 'newTest' ]
+ ],
+ 'SEARCH_FORM_SCHEMA' => [
+ 'validation_schema' => __DIR__ . '/data/schema/search-form-schema.v1.json',
+ 'group' => SMW_SCHEMA_GROUP_SEARCH_FORM,
+ 'type_description' => 'smw-schema-description-search-form-schema'
+ ]
+ ],
+ ##
+
+ ##
+ # ElasticStore settings
+ #
+ # Supported options and settings required by the ElasticStore.
+ #
+ # This setting provides documentation and default values for the ElasticStore
+ # and its use in an ES cluster.
+ #
+ # @since 3.0
+ ##
+ 'smwgElasticsearchConfig' => [
+ 'index_def' => [
+ // The complete name will be auto-generated from "smw-...-" + wfWikiID()
+ // to avoid indicies among different wikis being corrupted when sharing
+ // an ES instance.
+ 'data' => __DIR__ . '/data/elastic/smw-data-standard.json',
+ 'lookup' => __DIR__ . '/data/elastic/smw-lookup.json'
+ ],
+ 'connection' =>[
+ 'quick_ping' => true,
+ // Number of times the client tries to reconnect before throwing an
+ // exception
+ 'retries' => 2,
+
+ // Controls how long curl should wait for the entire request to finish
+ 'timeout' => 30,
+
+ // Controls how long curl should wait for the "connect" phase to finish
+ 'connect_timeout' => 30
+ ],
+ 'settings' => [
+ 'data' => [
+ // Setting names match those from ES, any misspelling or
+ // incorrect setting will cause an error in ES
+ 'index.mapping.total_fields.limit' => 9000,
+ 'index.max_result_window' => 50000
+ ]
+ ],
+ 'indexer' => [
+
+ // Allows to index unstructured, unprocessed raw text from a revision
+ 'raw.text' => false,
+
+ // Experimental feature to investigate the use of the ingest pipline
+ // to index uploaded files and make that content available to
+ // QueryEngine during a wide proximity search (e.g. [[in:fox jumps]])
+ // Requires https://www.elastic.co/guide/en/elasticsearch/plugins/master/ingest-attachment.html
+ 'experimental.file.ingest' => false,
+
+ // If the replication encounters an `illegal_argument_exception` from
+ // the ES cluster, rethrow it as exception to ensure it doesn't get
+ // unnoticed as it is likely to require intervention due to issues
+ // like "Limit of total fields ... has been exceeded" etc.
+ 'throw.exception.on.illegal.argument.error' => true,
+
+ // Number of max. retries before the recovery job will resign from
+ // trying any more attempts to update the ES cluster. This is to
+ // prevent jobs from invoking themselves indefinitely.
+ 'job.recovery.retries' => 5,
+
+ // Number of max. retries before the file ingest
+ 'job.file.ingest.retries' => 3
+ ],
+ 'query' => [
+
+ // If for some reason no connection to the ES cluster could be
+ // established, use the SQLStore QueryEngine as fallback.
+ 'fallback.no.connection' => false,
+
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/_profiling_queries.html
+ 'profiling' => false,
+
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-validate.html
+ // Part of the `format=debug` to validate potentially expensive queries
+ // without executing them.
+ 'debug.explain' => true,
+
+ // During the debug output, display details about which description was
+ // when resolved in connection with a query
+ 'debug.description.log' => true,
+
+ // When `!...` is used make sure that the condition is only applied
+ // on entities where the property exists together with the negated
+ // value condition otherwise the ! condition becomes unrestricted
+ 'must_not.property.exists' => true,
+
+ // When sorting on a particular property, enforce that the property
+ // exists as an assignment to a selected entity. The default setting
+ // matches the SQLStore behaviour. (See also #2823)
+ 'sort.property.must.exists' => true,
+
+ // Sort by score (aka relevancy)
+ //
+ // Defines the name of the sortkey (as covention) that is used to sort
+ // results by scores computed by ES (based on Term Frequency, Inverse
+ // Document Frequency etc.).
+ //
+ // Output results with the highest score first:
+ //
+ // {{#ask: [[Category:Foo]]
+ // |sort=es.score
+ // |order=desc
+ // }}
+ //
+ // @see https://www.compose.com/articles/how-scoring-works-in-elasticsearch/
+ 'score.sortfield' => 'es.score',
+
+ // The `+` and `-` symbols will be interpret as boolean operators so that
+ // things like `+foo, -bar` mean `+` (this term must be present) and
+ // `-`(this term must not be present). If they need to be part of
+ // the search string itself then it is required to escape them like
+ // `\+`.
+ 'query_string.boolean.operators' => true,
+
+ // ES works different with text elements compared to the SQL interface
+ // (and its DSL query logic) therefore we try to modify some
+ // common scenarios and alter strings (and boolean operators) to pass
+ // most use cases from the SQLStore integration test suite and hereby
+ // allows to be compatible with the SMW SQL answering behaviour.
+ //
+ // ES has reserved characters `+ - = && || > < ! ( ) { } [ ] ^ " ~ *
+ // ? : \ /` and this mode will automatically encode them which of course
+ // limits the ability to use them as part of the native boolean operators
+ // within the query expression.
+ //
+ // In case the mode is disabled, the user has to make sure to follow
+ // the rules set forth by ES in connection with the query_string
+ // parser.
+ //
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-query-string-query.html
+ 'compat.mode' => true,
+
+ // Size (limit) of the subquery construct which is executed as separated
+ // search request.
+ 'subquery.size' => 10000,
+
+ // Sorting and scoring of subqueries is generally not necessary therefore
+ // we use the `constant_score` filter context to executed queries which
+ // helps to elevate the use of the filter caching.
+ //
+ // @see https://www.elastic.co/guide/en/elasticsearch/guide/current/filter-caching.html
+ 'subquery.constant.score' => true,
+
+ // Defines the threshold for a result size as to when those should be
+ // stored in a special lookup index to facilitate the ES terms lookup
+ // feature (which requires to write and refresh the specific index
+ // element ad-hoc before it can be used by the "source" query).
+ //
+ // The more often subqueries are used (or reused) the lower the threshold
+ // should be set as it directly impacts performance.
+ //
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html
+ 'subquery.terms.lookup.result.size.index.write.threshold' => 200,
+
+ // Intermediary optimization to allow for a subquery (executed as part
+ // of a "source" query and uses the special lookup index) to be reused
+ // (aka cached) for any succeeding query executions for the time frame
+ // set and hereby avoids fetching subquery results on repeating
+ // requests especially when the source query changes its limit and
+ // offset parameters frequently.
+ //
+ // Only subqueries with `subquery.terms.lookup.result.size.index.write.threshold`
+ // will be available to make use of this index cache.
+ //
+ // The cache is static and will not apply any eviction strategy which
+ // means that in case of additional values that would normally invalidate
+ // a subquery result, results remain static for the time frame set.
+ //
+ // Caching becomes crucial when a subqery terms lookup contains 1000+
+ // of matching documents as we avoid executing the subquery and if
+ // available directly resuse the terms lookup index.
+ //
+ // @default 1h
+ 'subquery.terms.lookup.cache.lifetime' => 60 * 60,
+
+ // A concept as (aka dynamix category) is defined using a query and
+ // will return a list of matchable IDs therefore to improve performance
+ // during the lookup of those entities use the lookup index to retrieve
+ // precomputed results.
+ 'concept.terms.lookup' => true,
+
+ // Threshold when to store and use the index lookup
+ 'concept.terms.lookup.result.size.index.write.threshold' => 10,
+
+ // Threshold when to store and use the index lookup
+ // It is similar to `$smwgQConceptCacheLifetime`
+ //
+ // @default 1h
+ 'concept.terms.lookup.cache.lifetime' => 60 * 60,
+
+ // In case search terms contains CJK terms, remove `*` prefix/affix
+ // from a search request in an effort to best match single characters
+ // that created as part of the standard analyzer. Use a phrase match
+ // instead of a wildcard proximity.
+ //
+ // This setting may be disabled when using a different index definition
+ // (e.g. ICU).
+ 'cjk.best.effort.proximity.match' => true,
+
+ // Specifies that a wide proximity search (e.g. [[~~Foo bar]] or
+ // [[in:Foo bar]]) is executed as a match_phrase search meaning that
+ // that all elements of the query string need to be present in the
+ // order of the query
+ 'wide.proximity.as.match_phrase' => true,
+
+ // Fields to be used for a wide proximity search with some being
+ // boosted to weight higher in relevance. For example, when `Foo` is
+ // part of the title it relevance is boosted by 8 (i.e. count more
+ // towards the relevance score) as when it would only appear in one
+ // of the text fields.
+ //
+ // It means that [[in:Foo]] where titles match `Foo` will be
+ // assigned a 5 times higher score and therefore appear higher in a
+ // list when sorted by relevancy.
+ 'wide.proximity.fields' => [
+ 'subject.title^8',
+ 'text_copy^5',
+ 'text_raw',
+ 'attachment.title^3',
+ 'attachment.content'
+ ],
+
+ // By default, the URI fields are considered case sensitive similar to
+ // what RFC 3986 " ... the scheme and host are case-insensitive and
+ // therefore should be normalized to lowercase ... the other generic
+ // syntax components are assumed to be case-sensitive unless ",
+ // RFC 2616 " ... comparing two URIs to decide if they match or
+ // not, a client SHOULD use a case-sensitive octet-by-octet
+ // comparison of the entire URIs ..."
+ //
+ // The setting applies to the EQ, LIKE, and NLIKE match.
+ 'uri.field.case.insensitive' => false,
+
+ // By default, equality searches (e.g. [[Has text:Foo]], [Has text:foo]])
+ // are case senstive but as in case of the SMW_FIELDT_CHAR_NOCASE,
+ // the search can be altered to find case insensitive matches as well.
+ //
+ // The default setting matches the SQLStore standard behaviour and
+ // uses the faster ES `term` search instead of a `match` variant
+ // which would become necessary for any analyzed field.
+ //
+ // @see https://www.elastic.co/guide/en/elasticsearch/guide/current/term-vs-full-text.html
+ 'text.field.case.insensitive.eq.match' => false,
+
+ // [[~Foo/Bar/*]] vs. [[~FOO/bar/*]]
+ // [[Has page::~Foo bar/bar/*]]
+ 'page.field.case.insensitive.proximity.match' => true,
+
+ // Allows to retrieve text fragments from ES for query request and
+ // depending on the type selected can require more query time.
+ //
+ // Available types are `plain`, `unified`, and `fvh`. The `fvh` type
+ // requires text fields to have the `term_vector` with `with_positions_offsets`
+ // enabled.
+ //
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html#plain-highlighter
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/term-vector.html
+ 'highlight.fragment' => [ 'number' => 1, 'size' => 250, 'type' => false ]
+ ]
+ ],
+ ##
+
+ ##
+ # ElasticStore profile
+ #
+ # Maintaining various settings using an array notation in `LocalSettinga.php`
+ # can become challenging hence we provide a JSON profile that can be assigned
+ # instead. Settings will be merged together with the default
+ # `smwgElasticsearchConfig` where a profile will override any previous value
+ # assignments.
+ #
+ # @since 3.0
+ ##
+ 'smwgElasticsearchProfile' => false, // __DIR__ . '/data/elastic/default-profile.json',
+
+ ##
+ # ElasticStore connection settings
+ #
+ # @since 3.0
+ ##
+ 'smwgElasticsearchEndpoints' => [
+ // [ 'host' => '127.0.0.1', 'port' => 9200, 'scheme' => 'http' ],
+ // [ 'host' => '127.0.0.1', 'port' => 9200, 'scheme' => 'http', 'user' => 'username', 'pass' => 'password!#$?*abc' ]
+ // 'localhost:9200'
+ ]
+
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/README.md b/www/wiki/extensions/SemanticMediaWiki/README.md
new file mode 100644
index 00000000..86005da9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/README.md
@@ -0,0 +1,81 @@
+# Semantic MediaWiki
+
+[![Build Status](https://secure.travis-ci.org/SemanticMediaWiki/SemanticMediaWiki.svg?branch=master)](http://travis-ci.org/SemanticMediaWiki/SemanticMediaWiki)
+[![Code Coverage](https://scrutinizer-ci.com/g/SemanticMediaWiki/SemanticMediaWiki/badges/coverage.png?s=f3501ede0bcc98824aa51501eb3647ecf71218c0)](https://scrutinizer-ci.com/g/SemanticMediaWiki/SemanticMediaWiki/)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/SemanticMediaWiki/SemanticMediaWiki/badges/quality-score.png?s=d9aac7e68e6554f95b0a89608cbc36985429d819)](https://scrutinizer-ci.com/g/SemanticMediaWiki/SemanticMediaWiki/)
+[![Latest Stable Version](https://poser.pugx.org/mediawiki/semantic-media-wiki/version.png)](https://packagist.org/packages/mediawiki/semantic-media-wiki)
+[![Packagist download count](https://poser.pugx.org/mediawiki/semantic-media-wiki/d/total.png)](https://packagist.org/packages/mediawiki/semantic-media-wiki)
+
+Semantic MediaWiki (a.k.a. SMW) is a free, open-source extension to [MediaWiki](https://www.semantic-mediawiki.org/wiki/MediaWiki) – the wiki software that
+powers Wikipedia – that lets you store and query data within the wiki's pages.
+
+Semantic MediaWiki is also a full-fledged framework, in conjunction with
+many spinoff extensions, that can turn a wiki into a powerful and flexible
+knowledge management system. All data created within SMW can easily be
+published via the [Semantic Web](https://www.semantic-mediawiki.org/wiki/Semantic_Web),
+allowing other systems to use this data seamlessly.
+
+For a better understanding of how Semantic MediaWiki works, have a look at [deployed in 5 min](https://vimeo.com/82255034) and the [Sesame](https://vimeo.com/126392433), [Fuseki ](https://vimeo.com/118614078) triplestore video, or
+browse the [wiki](https://www.semantic-mediawiki.org) for a more comprehensive introduction.
+
+## Requirements
+
+- PHP 5.6 or later
+- MediaWiki 1.27 or later
+- MySQL 5+, SQLite 3+ or PostgreSQL 9.x
+
+A list of supported PHP versions, MediaWiki versions and database systems per Semantic MediaWiki
+release can be found in the [compatibility matrix](docs/COMPATIBILITY.md).
+
+## Installation
+
+The recommended way to install Semantic MediaWiki is by using [Composer][composer]. See the detailed
+[installation guide](docs/INSTALL.md) as well as the information on [compatibility](docs/COMPATIBILITY.md).
+
+## Documentation
+
+Most of the documentation can be found on the [wiki for Semantic MediaWiki](https://www.semantic-mediawiki.org).
+A small core of documentation also comes bundled with the software itself. This documentation is minimalistic
+and less explanatory then what can be found on the SMW wiki. It is however always kept up to date, and applies
+to the version of the code it comes bundled with. The most important files are linked below.
+
+* [User documentation](docs/README.md)
+* [Technical documentation](docs/technical/README.md)
+
+## Contribution and support
+
+[![Twitter](https://www.semantic-mediawiki.org/w/images/c/c9/Twitter_icon.jpg)](https://twitter.com/#!/semanticmw)
+[![Facebook](https://www.semantic-mediawiki.org/w/images/thumb/7/77/677166248.png/30px-677166248.png)](https://www.facebook.com/pages/Semantic-MediaWiki/160459700707245)
+[![Google+](https://www.semantic-mediawiki.org/w/images/a/ae/30px-Google%2B.png)](https://plus.google.com/115301028320198614441/posts)
+
+Many people have contributed to SMW. A list of people who have made contributions in the past can
+be found [here][contributors] or on the [wiki for Semantic MediaWiki](https://www.semantic-mediawiki.org/wiki/Help:SMW_Project#Contributors).
+The overview on [how to contribute](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/CONTRIBUTING.md)
+provides information on the different ways available to do so.
+
+If you have remarks, questions, or suggestions, please send them to <semediawiki-users@lists.sourceforge.net>.
+You can subscribe to this list [here](https://sourceforge.net/projects/semediawiki/lists/semediawiki-user).
+
+If you want to contribute work to the project please subscribe to the developers mailing list and
+have a look at the contribution guideline.
+
+* [File an issue](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues)
+* [Submit a pull request](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pulls)
+* Ask a question on [the mailing list](https://www.semantic-mediawiki.org/wiki/Mailing_list)
+* Ask a question on the #semantic-mediawiki IRC channel on Freenode.
+
+## Tests
+
+This extension provides unit and integration tests and are normally run by a [continues integration platform][travis]
+but can also be executed locally using the shortcut command `composer phpunit` from the extension base directory. A more comprehensive introduction can be found under the [test section](/tests/README.md#running-tests).
+
+## License
+
+[GNU General Public License, version 2 or later][gpl-licence]. The COPYING file explains SMW's copyright and license.
+
+[contributors]: https://github.com/SemanticMediaWiki/SemanticMediaWiki/graphs/contributors
+[travis]: https://travis-ci.org/SemanticMediaWiki/SemanticMediaWiki
+[mw-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing
+[gpl-licence]: https://www.gnu.org/copyleft/gpl.html
+[composer]: https://getcomposer.org/
+[smw-installation]: https://www.semantic-mediawiki.org/wiki/Help:Installation
diff --git a/www/wiki/extensions/SemanticMediaWiki/SemanticMediaWiki.php b/www/wiki/extensions/SemanticMediaWiki/SemanticMediaWiki.php
new file mode 100644
index 00000000..f74224b5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/SemanticMediaWiki.php
@@ -0,0 +1,108 @@
+<?php
+
+use SMW\NamespaceManager;
+use SMW\ApplicationFactory;
+use SMW\Setup;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * ExtensionRegistry only maps classes and functions after all extensions have
+ * been queued from the LocalSettings.php resulting in DefaultSettings not being
+ * loaded in-time.
+ *
+ * When changing the load order, please ensure that this function is run either
+ * via Composer's autoloading or as part of your internal registration.
+ */
+SemanticMediaWiki::load();
+
+/**
+ * @codeCoverageIgnore
+ *
+ * This documentation group collects source code files belonging to Semantic
+ * MediaWiki.
+ *
+ * For documenting extensions of SMW, please do not use groups starting with
+ * "SMW" but make your own groups instead. Browsing at
+ * https://doc.semantic-mediawiki.org/ is assumed to be easier this way.
+ *
+ * @defgroup SMW Semantic MediaWiki
+ */
+class SemanticMediaWiki {
+
+ /**
+ * @since 2.5
+ *
+ * @note It is expected that this function is loaded before LocalSettings.php
+ * to ensure that settings and global functions are available by the time
+ * the extension is activated.
+ */
+ public static function load() {
+
+ if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
+ include_once __DIR__ . '/vendor/autoload.php';
+ }
+
+ include_once __DIR__ . '/src/Aliases.php';
+ include_once __DIR__ . '/src/Defines.php';
+ include_once __DIR__ . '/src/GlobalFunctions.php';
+
+ // If the function is called more than once then this will fail on
+ // purpose
+ foreach ( include __DIR__ . '/DefaultSettings.php' as $key => $value ) {
+ if ( !isset( $GLOBALS[$key] ) ) {
+ $GLOBALS[$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function initExtension( $credits = [] ) {
+
+ define( 'SMW_VERSION', isset( $credits['version'] ) ? $credits['version'] : 'N/A' );
+
+ // Registration point for required early registration
+ Setup::initExtension( $GLOBALS );
+ }
+
+ /**
+ * Setup and initialization
+ *
+ * @note $wgExtensionFunctions variable is an array that stores
+ * functions to be called after most of MediaWiki initialization
+ * has finalized
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:$wgExtensionFunctions
+ *
+ * @since 1.9
+ */
+ public static function onExtensionFunction() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $namespace = new NamespaceManager();
+ $namespace->init( $GLOBALS );
+
+ $setup = new Setup( $applicationFactory );
+
+ $setup->loadSchema( $GLOBALS );
+ $setup->init( $GLOBALS, __DIR__ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string|null
+ */
+ public static function getVersion() {
+
+ if ( !defined( 'SMW_VERSION' ) ) {
+ return null;
+ }
+
+ return SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/composer.json b/www/wiki/extensions/SemanticMediaWiki/composer.json
new file mode 100644
index 00000000..7723c614
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/composer.json
@@ -0,0 +1,111 @@
+{
+ "name": "mediawiki/semantic-media-wiki",
+ "type": "mediawiki-extension",
+ "description": "An extension to MediaWiki that lets you store and query structured data within wiki pages",
+ "keywords": [
+ "SMW",
+ "Semantic MediaWiki",
+ "Wiki",
+ "MediaWiki"
+ ],
+ "homepage": "https://www.semantic-mediawiki.org/wiki/",
+ "license": "GPL-2.0-or-later",
+ "authors": [
+ {
+ "name": "Markus Krötzsch",
+ "homepage": "http://korrekt.org/",
+ "role": "Original author"
+ },
+ {
+ "name": "Jeroen De Dauw",
+ "email": "jeroendedauw@gmail.com",
+ "homepage": "https://www.entropywins.wtf/",
+ "role": "Core developer"
+ },
+ {
+ "name": "James Hong Kong",
+ "homepage": "https://www.semantic-mediawiki.org/wiki/User:MWJames",
+ "role": "Core developer"
+ }
+ ],
+ "support": {
+ "email": "semediawiki-user@lists.sourceforge.net",
+ "issues": "https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues",
+ "forum": "https://www.semantic-mediawiki.org/wiki/semantic-mediawiki.org_talk:Community_portal",
+ "wiki": "https://www.semantic-mediawiki.org/wiki/",
+ "source": "https://github.com/SemanticMediaWiki/SemanticMediaWiki"
+ },
+ "require": {
+ "php": ">=5.6.0",
+ "ext-mbstring": "*",
+ "composer/installers": "1.*,>=1.0.1",
+ "psr/log": "~1.0",
+ "mediawiki/parser-hooks": "~1.4",
+ "param-processor/param-processor": "~1.2",
+ "serialization/serialization": "~3.2",
+ "onoi/message-reporter": "~1.0",
+ "onoi/cache": "~1.2",
+ "onoi/event-dispatcher": "~1.0",
+ "onoi/blob-store": "~1.2",
+ "onoi/http-request": "~1.1",
+ "onoi/callback-container": "~2.0",
+ "onoi/tesa": "~0.1",
+ "onoi/shared-resources": "~0.3",
+ "symfony/css-selector": "^3.3",
+ "elasticsearch/elasticsearch": "^5.3|^6.0"
+ },
+ "require-dev": {
+ "squizlabs/php_codesniffer": "~2.1",
+ "phpmd/phpmd": "~2.1"
+ },
+ "replace": {
+ "mediawiki/semantic-mediawiki": "*"
+ },
+ "suggest": {
+ "mediawiki/semantic-result-formats": "Provides additional result formats for queries of structured data"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SMW\\": "src/",
+ "SMW\\Maintenance\\": "maintenance/"
+ },
+ "files" : [
+ "SemanticMediaWiki.php"
+ ],
+ "classmap" : [
+ "includes/"
+ ]
+ },
+ "config": {
+ "process-timeout": 0
+ },
+ "scripts": {
+ "test": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist",
+ "phpunit": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist",
+ "phpdbg": "phpdbg -qrr ../../tests/phpunit/phpunit.php -c phpunit.xml.dist",
+ "unit": [
+ "composer dump-autoload",
+ "composer phpunit -- --testsuite=semantic-mediawiki-unit"
+ ],
+ "integration": "composer phpunit -- --testsuite=semantic-mediawiki-integration",
+ "benchmark": "composer phpunit -- --group semantic-mediawiki-benchmark",
+ "quick-benchmark": "php ../../tests/phpunit/phpunit.php -c ./tests/phpunit/Benchmark/phpunit.quick.xml.dist --group semantic-mediawiki-benchmark",
+ "cs-standalone": [
+ "composer validate --no-interaction",
+ "vendor/bin/phpcs src/* includes/* tests/* --standard=phpcs.xml --extensions=php -sp"
+ ],
+ "cs": [
+ "composer cs-standalone",
+ "vendor/bin/phpmd src/,includes/,tests/ text phpmd.xml"
+ ],
+ "ci": [
+ "composer phpunit",
+ "composer cs"
+ ]
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/elastic/default-profile.json b/www/wiki/extensions/SemanticMediaWiki/data/elastic/default-profile.json
new file mode 100644
index 00000000..113ce05b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/elastic/default-profile.json
@@ -0,0 +1,52 @@
+{
+ "connection": {
+ "retries": 2,
+ "timeout": 30,
+ "connect_timeout": 30,
+ "quick_ping": true,
+ "retries": 2
+ },
+ "settings": {
+ "data": {
+ "index.mapping.total_fields.limit": 9000,
+ "index.max_result_window": 50000
+ }
+ },
+ "indexer": {
+ "raw.text": false,
+ "experimental.file.ingest": false,
+ "throw.exception.on.illegal.argument.error": true,
+ "job.recovery.retries": 5,
+ "job.file.ingest.retries": 3
+ },
+ "query": {
+ "fallback.no.connection": false,
+ "profiling": false,
+ "debug.explain": true,
+ "debug.description.log": true,
+ "must_not.property.exists": true,
+ "sort.property.must.exists": true,
+ "score.sortfield": "elastic.score",
+ "query_string.boolean.operators": true,
+ "compat.mode": true,
+ "subquery.size":10000,
+ "subquery.constant.score": false,
+ "subquery.terms.lookup.result.size.index.write.threshold": 100,
+ "subquery.terms.lookup.cache.lifetime": 3600,
+ "concept.terms.lookup": true,
+ "concept.terms.lookup.result.size.index.write.threshold": 10,
+ "concept.terms.lookup.cache.lifetime": 3600,
+ "wide.proximity.as.match_phrase": true,
+ "wide.proximity.fields": [
+ "subject.title^8",
+ "text_copy^5",
+ "text_raw",
+ "attachment.title^3"
+ "attachment.content"
+ ],
+ "uri.field.case.insensitive": false,
+ "text.field.case.insensitive.eq.match": false,
+ "page.field.case.insensitive.proximity.match": true,
+ "highlight.fragment.type": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-icu.json b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-icu.json
new file mode 100644
index 00000000..a2bb335d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-icu.json
@@ -0,0 +1,283 @@
+{
+ "settings": {
+ "number_of_shards": 2,
+ "number_of_replicas": 2,
+ "refresh_interval" : "1s",
+ "index.mapping.total_fields.limit": "9000",
+ "index.max_result_window": "50000",
+ "analysis": {
+ "filter": {
+ "uri_stopwords_filter": {
+ "type": "stop",
+ "stopwords": [ "http", "https", "ftp", "www" ]
+ }
+ },
+ "char_filter": {
+ "nfd_normalizer": {
+ "type": "icu_normalizer",
+ "name": "nfc",
+ "mode": "decompose"
+ },
+ "nfkc_normalizer": {
+ "type": "icu_normalizer",
+ "name": "nfkc",
+ "mode": "decompose"
+ },
+ "wiki_char_filter": {
+ "type": "mapping",
+ "mappings": [
+ "[ => ",
+ "] => ",
+ "/ => "
+ ]
+ }
+ },
+ "analyzer": {
+ "uri_lowercase_with_stopwords": {
+ "type": "custom",
+ "tokenizer": "lowercase",
+ "filter": [ "uri_stopwords_filter" ]
+ },
+ "nfkc_cf_normalized": {
+ "tokenizer": "icu_tokenizer",
+ "char_filter": [
+ "icu_normalizer"
+ ]
+ },
+ "nfkc_cf_normalized_lowercase": {
+ "tokenizer": "icu_tokenizer",
+ "char_filter": [
+ "icu_normalizer"
+ ],
+ "filter": [ "lowercase" ]
+ },
+ "nfd_normalized": {
+ "tokenizer": "icu_tokenizer",
+ "char_filter": [
+ "nfd_normalizer"
+ ]
+ },
+ "nfkc_normalized": {
+ "tokenizer": "icu_tokenizer",
+ "char_filter": [
+ "nfkc_normalizer"
+ ]
+ }
+ }
+ }
+ },
+ "mappings": {
+ "data": {
+ "dynamic_templates": [
+ {
+ "text_fields": {
+ "path_match": "P:*.txtField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "analyzer": "nfkc_cf_normalized",
+ "fields": {
+ "sort": {
+ "type": "icu_collation_keyword",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 2000
+ }
+ }
+ }
+ }
+ },
+ {
+ "uri_fields": {
+ "path_match": "P:*.uriField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "icu_collation_keyword",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 2000
+ },
+ "lowercase": {
+ "type": "text",
+ "analyzer": "uri_lowercase_with_stopwords"
+ }
+ }
+ }
+ }
+ },
+ {
+ "page_fields_text": {
+ "path_match": "P:*.wpgField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "analyzer": "nfkc_cf_normalized",
+ "fields": {
+ "sort": {
+ "type": "icu_collation_keyword",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 500
+ },
+ "lowercase": {
+ "type": "keyword",
+ "analyzer": "nfkc_cf_normalized_lowercase"
+ }
+ }
+ }
+ }
+ },
+ {
+ "page_fields_identifier": {
+ "path_match": "P:*.wpgID",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "long"
+ }
+ }
+ },
+ {
+ "numeric_fields": {
+ "path_match": "P:*.numField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "double",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ },
+ {
+ "date_fields": {
+ "path_match": "P:*.datField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "double",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ },
+ {
+ "date_fields_raw": {
+ "path_match": "P:*.dat_raw",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "keyword"
+ }
+ }
+ },
+ {
+ "geo_fields": {
+ "path_match": "P:*.geoField",
+ "match_mapping_type": "string",
+ "mapping": {
+ "type": "text",
+ "fields": {
+ "point": {
+ "type": "geo_point"
+ }
+ }
+ }
+ }
+ },
+ {
+ "boolean_fields": {
+ "path_match": "P:*.booField",
+ "match_mapping_type": "boolean",
+ "mapping": {
+ "type": "boolean"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "noop": {
+ "type": "integer"
+ },
+ "text_copy": {
+ "type": "text",
+ "analyzer": "nfkc_cf_normalized",
+ "doc_values": false
+ },
+ "text_raw": {
+ "type": "text",
+ "analyzer": "nfkc_cf_normalized",
+ "doc_values": false
+ },
+ "subject.title": {
+ "type": "text",
+ "fields": {
+ "sort": {
+ "type": "icu_collation_keyword",
+ "index": false
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.interwiki": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.subobject": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.sortkey": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "icu_collation_keyword",
+ "index": false
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ },
+ "lowercase": {
+ "type": "keyword",
+ "analyzer": "nfkc_cf_normalized_lowercase"
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-standard.json b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-standard.json
new file mode 100644
index 00000000..cda448ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-data-standard.json
@@ -0,0 +1,274 @@
+{
+ "settings": {
+ "number_of_shards": 2,
+ "number_of_replicas": 2,
+ "refresh_interval" : "1s",
+ "index.mapping.total_fields.limit": "9000",
+ "index.max_result_window": "50000",
+ "analysis": {
+ "filter": {
+ "uri_stopwords_filter": {
+ "type": "stop",
+ "stopwords": [ "http", "https", "ftp", "www" ]
+ }
+ },
+ "char_filter": {
+ "wiki_char_filter": {
+ "type": "mapping",
+ "mappings": [
+ "[ => ",
+ "] => ",
+ "/ => "
+ ]
+ }
+ },
+ "analyzer": {
+ "uri_lowercase_with_stopwords": {
+ "type": "custom",
+ "tokenizer": "lowercase",
+ "filter": [ "uri_stopwords_filter" ]
+ },
+ "exact_text_lowercase": {
+ "type": "custom",
+ "tokenizer": "keyword",
+ "filter": [ "lowercase", "asciifolding" ]
+ }
+ },
+ "normalizer": {
+ "standard_sort_normalizer": {
+ "type": "custom",
+ "char_filter": [ "wiki_char_filter" ]
+ },
+ "char_normalizer": {
+ "type": "custom",
+ "char_filter": [ "wiki_char_filter" ]
+ },
+ "lowercase_normalizer": {
+ "type": "custom",
+ "char_filter": [],
+ "filter": [ "lowercase" ]
+ },
+ "case_insensitive_sort_normalizer": {
+ "type": "custom",
+ "char_filter": [ "wiki_char_filter" ],
+ "filter": [ "lowercase", "asciifolding" ]
+ }
+ }
+ }
+ },
+ "mappings": {
+ "data": {
+ "dynamic_templates": [
+ {
+ "text_fields": {
+ "path_match": "P:*.txtField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "keyword",
+ "normalizer": "standard_sort_normalizer",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 2000
+ }
+ }
+ }
+ }
+ },
+ {
+ "uri_fields": {
+ "path_match": "P:*.uriField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "keyword",
+ "normalizer": "standard_sort_normalizer",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 2000
+ },
+ "lowercase": {
+ "type": "text",
+ "analyzer": "uri_lowercase_with_stopwords"
+ }
+ }
+ }
+ }
+ },
+ {
+ "page_fields_text": {
+ "path_match": "P:*.wpgField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "keyword",
+ "normalizer": "standard_sort_normalizer",
+ "index": false,
+ "ignore_above": 256
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 500
+ },
+ "lowercase": {
+ "type": "keyword",
+ "normalizer": "lowercase_normalizer"
+ }
+ }
+ }
+ }
+ },
+ {
+ "page_fields_identifier": {
+ "path_match": "P:*.wpgID",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "long"
+ }
+ }
+ },
+ {
+ "numeric_fields": {
+ "path_match": "P:*.numField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "double",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ },
+ {
+ "date_fields": {
+ "path_match": "P:*.datField",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "double",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ },
+ {
+ "date_fields_raw": {
+ "path_match": "P:*.dat_raw",
+ "match_mapping_type": "*",
+ "mapping": {
+ "type": "keyword"
+ }
+ }
+ },
+ {
+ "geo_fields": {
+ "path_match": "P:*.geoField",
+ "match_mapping_type": "string",
+ "mapping": {
+ "type": "text",
+ "fields": {
+ "point": {
+ "type": "geo_point"
+ }
+ }
+ }
+ }
+ },
+ {
+ "boolean_fields": {
+ "path_match": "P:*.booField",
+ "match_mapping_type": "boolean",
+ "mapping": {
+ "type": "boolean"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "noop": {
+ "type": "integer"
+ },
+ "text_copy": {
+ "type": "text",
+ "doc_values": false
+ },
+ "text_raw": {
+ "type": "text",
+ "doc_values": false
+ },
+ "subject.title": {
+ "type": "text",
+ "fields": {
+ "sort": {
+ "type": "keyword",
+ "normalizer": "standard_sort_normalizer",
+ "index": false
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.interwiki": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.subobject": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "subject.sortkey": {
+ "type": "text",
+ "copy_to": "text_copy",
+ "fields": {
+ "sort": {
+ "type": "keyword",
+ "normalizer": "standard_sort_normalizer",
+ "index": false
+ },
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ },
+ "lowercase": {
+ "type": "keyword",
+ "normalizer": "lowercase_normalizer"
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-lookup.json b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-lookup.json
new file mode 100644
index 00000000..b8a227af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/elastic/smw-lookup.json
@@ -0,0 +1,17 @@
+{
+ "settings": {
+ "number_of_shards": 1,
+ "refresh_interval" : "1s",
+ "index.mapping.total_fields.limit": "3000",
+ "index.max_result_window": "50000"
+ },
+ "mappings": {
+ "lookup": {
+ "properties": {
+ "id": {
+ "type": "long"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/default.json b/www/wiki/extensions/SemanticMediaWiki/data/import/default.json
new file mode 100644
index 00000000..7e874df9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/default.json
@@ -0,0 +1,78 @@
+{
+ "description": "Semantic MediaWiki default vocabulary import",
+ "import": [
+ {
+ "page": "Smw import skos",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "/vocabularies/skos.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Smw import foaf",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "/vocabularies/foaf.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Smw import owl",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "/vocabularies/owl.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Foaf:knows",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": {
+ "importFrom": "/properties/foaf.knows.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Foaf:name",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": {
+ "importFrom": "/properties/foaf.name.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Foaf:homepage",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": {
+ "importFrom": "/properties/foaf.homepage.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Owl:differentFrom",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": {
+ "importFrom": "/properties/owl.differentFrom.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.homepage.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.homepage.txt
new file mode 100644
index 00000000..95a2b322
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.homepage.txt
@@ -0,0 +1,4 @@
+* [[Imported from::foaf:homepage]]
+* [[Property description::URL of the homepage of something, which is a general web resource.@en]]
+
+[[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:homepage}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.knows.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.knows.txt
new file mode 100644
index 00000000..997db97c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.knows.txt
@@ -0,0 +1,4 @@
+* [[Imported from::foaf:knows]]
+* [[Property description::A person known by this person (indicating some level of reciprocated interaction between the parties).@en]]
+
+[[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:knows}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.name.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.name.txt
new file mode 100644
index 00000000..2baf7a00
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/foaf.name.txt
@@ -0,0 +1,4 @@
+* [[Imported from::foaf:name]]
+* [[Property description::A name for some thing or agent.@en]]
+
+[[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:name}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/properties/owl.differentFrom.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/owl.differentFrom.txt
new file mode 100644
index 00000000..35db6030
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/properties/owl.differentFrom.txt
@@ -0,0 +1,4 @@
+* [[Imported from::owl:differentFrom]]
+* [[Property description::The property that determines that two given individuals are different.@en]]
+
+[[Category:Imported vocabulary]] {{DISPLAYTITLE:owl:differentFrom}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/foaf.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/foaf.txt
new file mode 100644
index 00000000..15106fc0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/foaf.txt
@@ -0,0 +1,13 @@
+http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]
+ name|Type:Text
+ homepage|Type:URL
+ mbox|Type:Email
+ mbox_sha1sum|Type:Text
+ depiction|Type:URL
+ phone|Type:Text
+ Person|Category
+ Organization|Category
+ knows|Type:Page
+ member|Type:Page
+
+[[Category:Imported vocabulary]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/owl.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/owl.txt
new file mode 100644
index 00000000..72bf262d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/owl.txt
@@ -0,0 +1,48 @@
+http://www.w3.org/2002/07/owl#|[http://www.w3.org/2002/07/owl Web Ontology Language (OWL)]
+ AllDifferent|Category
+ allValuesFrom|Type:Page
+ AnnotationProperty|Category
+ backwardCompatibleWith|Type:Page
+ cardinality|Type:Number
+ Class|Category
+ comment|Type:Page
+ complementOf|Type:Page
+ DataRange|Category
+ DatatypeProperty|Category
+ DeprecatedClass|Category
+ DeprecatedProperty|Category
+ differentFrom|Type:Page
+ disjointWith|Type:Page
+ distinctMembers|Type:Page
+ equivalentClass|Type:Page
+ equivalentProperty|Type:Page
+ FunctionalProperty|Category
+ hasValue|Type:Page
+ imports|Type:Page
+ incompatibleWith|Type:Page
+ intersectionOf|Type:Page
+ InverseFunctionalProperty|Category
+ inverseOf|Type:Page
+ isDefinedBy|Type:Page
+ label|Type:Page
+ maxCardinality|Type:Number
+ minCardinality|Type:Number
+ Nothing|Category
+ ObjectProperty|Category
+ oneOf|Type:Page
+ onProperty|Type:Page
+ Ontology|Category
+ OntologyProperty|Category
+ owl|Type:Page
+ priorVersion|Type:Page
+ Restriction|Category
+ sameAs|Type:Page
+ seeAlso|Type:Page
+ someValuesFrom|Type:Page
+ SymmetricProperty|Category
+ Thing|Category
+ TransitiveProperty|Category
+ unionOf|Type:Page
+ versionInfo|Type:Page
+
+[[Category:Imported vocabulary]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/skos.txt b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/skos.txt
new file mode 100644
index 00000000..a0973831
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/import/vocabularies/skos.txt
@@ -0,0 +1,35 @@
+http://www.w3.org/2004/02/skos/core#|[http://www.w3.org/TR/skos-reference/skos.rdf Simple Knowledge Organization System (SKOS)]
+ altLabel|Type:Monolingual text
+ broader|Type:Annotation URI
+ broaderTransitive|Type:Annotation URI
+ broadMatch|Type:Annotation URI
+ changeNote|Type:Text
+ closeMatch|Type:Annotation URI
+ Collection|Class
+ Concept|Class
+ ConceptScheme|Class
+ definition|Type:Text
+ editorialNote|Type:Text
+ exactMatch|Type:Annotation URI
+ example|Type:Text
+ hasTopConcept|Type:Page
+ hiddenLabel|Type:String
+ historyNote|Type:Text
+ inScheme|Type:Page
+ mappingRelation|Type:Page
+ member|Type:Page
+ memberList|Type:Page
+ narrower|Type:Annotation URI
+ narrowerTransitive|Type:Annotation URI
+ narrowMatch|Type:Annotation URI
+ notation|Type:Text
+ note|Type:Text
+ OrderedCollection|Class
+ prefLabel|Type:String
+ related|Type:Annotation URI
+ relatedMatch|Type:Annotation URI
+ scopeNote|Type:Text
+ semanticRelation|Type:Page
+ topConceptOf|Type:Page
+
+[[Category:Imported vocabulary]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/schema/link-format-schema.v1.json b/www/wiki/extensions/SemanticMediaWiki/data/schema/link-format-schema.v1.json
new file mode 100644
index 00000000..60681829
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/schema/link-format-schema.v1.json
@@ -0,0 +1,119 @@
+{
+ "$id": "http://example.com/example.json",
+ "type": "object",
+ "properties": {
+ "description": {
+ "$id": "/properties/description",
+ "type": "string",
+ "title": "The Description Schema",
+ "default": "",
+ "examples": [
+ "Define ..."
+ ]
+ },
+ "type": {
+ "$id": "/properties/type",
+ "type": "string",
+ "title": "The Type Schema",
+ "default": "",
+ "examples": [
+ "LINK_FORMAT_SCHEMA"
+ ],
+ "enum": [
+ "LINK_FORMAT_SCHEMA"
+ ]
+ },
+ "rule": {
+ "$id": "/properties/rule",
+ "type": "object",
+ "minProperties": 1,
+ "properties": {
+ "link_to": {
+ "$id": "/properties/rule/properties/link_to",
+ "type": "string",
+ "title": "The link_to Schema",
+ "default": "",
+ "examples": [
+ "SPECIAL_ASK"
+ ],
+ "minLength": 1,
+ "enum": [
+ "SPECIAL_ASK",
+ "SPECIAL_SEARCH_BY_PROPERTY"
+ ]
+ },
+ "parameters": {
+ "$id": "/properties/rule/properties/parameters",
+ "type": "object",
+ "properties": {
+ "format": {
+ "$id": "/properties/rule/properties/parameters/properties/format",
+ "type": "string",
+ "title": "The Format Schema",
+ "default": "",
+ "examples": [
+ "category"
+ ],
+ "enum": [
+ "category",
+ "broadtable",
+ "table",
+ "list"
+ ]
+ },
+ "printouts": {
+ "$id": "/properties/rule/properties/parameters/properties/printouts",
+ "type": "array",
+ "items": {
+ "$id": "/properties/rule/properties/parameters/properties/printouts/items",
+ "type": "string",
+ "title": "The 0 Schema",
+ "default": "",
+ "examples": [
+ "Has description"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "required": [
+ "link_to"
+ ],
+ "anyOf": [{
+ "properties": {
+ "link_to": {
+ "enum": ["SPECIAL_ASK"]
+ }
+ },
+ "required": [ "parameters" ]
+ },
+ {
+ "properties": {
+ "link_to": {
+ "enum": ["SPECIAL_SEARCH_BY_PROPERTY" ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "tags": {
+ "$id": "/properties/tags",
+ "type": "array",
+ "items": {
+ "$id": "/properties/tags/items",
+ "type": "string",
+ "title": "The 0 Schema",
+ "default": "",
+ "examples": [
+ "formatter"
+ ]
+ }
+ }
+ },
+ "required": [
+ "type",
+ "rule"
+ ]
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/data/schema/search-form-schema.v1.json b/www/wiki/extensions/SemanticMediaWiki/data/schema/search-form-schema.v1.json
new file mode 100644
index 00000000..27b80b7e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/data/schema/search-form-schema.v1.json
@@ -0,0 +1,34 @@
+{
+ "$id": "https://www.semantic-mediawiki.org/search-profile-form.json",
+ "type": "object",
+ "properties": {
+ "type": {
+ "$id": "/properties/type",
+ "type": "string",
+ "title": "The Type Schema",
+ "default": "",
+ "examples": [
+ "SEARCH_FORM_SCHEMA"
+ ],
+ "enum": [
+ "SEARCH_FORM_SCHEMA"
+ ]
+ },
+ "forms": {
+ "$id": "/properties/forms",
+ "type": "object"
+ },
+ "namespaces": {
+ "$id": "/properties/namespaces",
+ "type": "object"
+ },
+ "descriptions": {
+ "$id": "/properties/descriptions",
+ "type": "object"
+ }
+ },
+ "required": [
+ "type",
+ "forms"
+ ]
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/COMPATIBILITY.md b/www/wiki/extensions/SemanticMediaWiki/docs/COMPATIBILITY.md
new file mode 100644
index 00000000..0bcf6486
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/COMPATIBILITY.md
@@ -0,0 +1,370 @@
+For a full list of changes in each release, see the [release notes](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/docs/releasenotes). For instructions
+on how to install the latest version of Semantic MediaWiki (SMW), see the [installation instructions](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md).
+
+
+## Release versions
+
+<table>
+ <tr>
+ <th></th>
+ <th>Status</th>
+ <th>Release date</th>
+ <th>Git branch</th>
+ </tr>
+ <tr>
+ <th><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/milestone/6">SMW 3.1.x</a></th>
+ <td>Future release</td>
+ <td>Q2 2019</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master">master</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/milestone/6">SMW 3.0.x</a></th>
+ <td>Stable release</td>
+ <td>2018-10-11</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/3.0.x">3.0.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.5.0">SMW 2.5.x</a></th>
+ <td>Obsolete release</td>
+ <td>2017-03-14</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.5.x">2.5.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.4.0">SMW 2.4.x</a></th>
+ <td>Obsolete release</td>
+ <td>2016-07-09</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.4.x">2.4.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.3.0">SMW 2.3.x</a></th>
+ <td>Obsolete release</td>
+ <td>2015-10-25</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.3.x">2.3.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.2.0">SMW 2.2.x</a></th>
+ <td>Obsolete release</td>
+ <td>2015-05-09</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.2.x">2.2.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.1.x">SMW 2.1.x</a></th>
+ <td>Obsolete release</td>
+ <td>2015-01-19</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.1.x">2.1.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_2.0">SMW 2.0.x</a></th>
+ <td>Obsolete release</td>
+ <td>2014-08-04</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/2.0.x">2.0.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_1.9.2">SMW 1.9.2</a></th>
+ <td>Obsolete release</td>
+ <td>2014-04-18</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/1.9.2">1.9.2</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_1.9.1">SMW 1.9.1</a></th>
+ <td>Obsolete release</td>
+ <td>2014-02-09</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/1.9.1">1.9.1</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_1.9.0">SMW 1.9.0</a></th>
+ <td>Obsolete release</td>
+ <td>2014-01-03</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/1.9">1.9</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_1.8.0">SMW 1.8.x</a></th>
+ <td>Obsolete release</td>
+ <td>2012-12-02</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/1.8.x">1.8.x</a></td>
+ </tr>
+ <tr>
+ <th><a href="https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_1.7.1">SMW 1.7.1</a></th>
+ <td>Obsolete release</td>
+ <td>2012-03-05</td>
+ <td><a href="https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/1.7.1">1.7.1</a></td>
+ </tr>
+</table>
+
+
+## Platform compatibility
+
+The PHP and MediaWiki version ranges listed are those in which SMW is known to work. It might also
+work with more recent versions of PHP and MediaWiki, though this is not guaranteed. Increases of
+minimum requirements are indicated in bold.
+
+Note that HHVM is only required if you do not use PHP.
+
+<table>
+ <tr>
+ <th></th>
+ <th>PHP</th>
+ <th>HHVM</th>
+ <th>MediaWiki</th>
+ </tr>
+ <tr>
+ <th>SMW 3.1.x</th>
+ <td><strong><a href="https://php.net/supported-versions.php">7.1.0</a></strong> - latest</td>
+ <td>No support</td>
+ <td><strong><a href="https://www.mediawiki.org/wiki/Version_lifecycle">1.31.0</a></strong> - ?</td>
+ </tr>
+ <tr>
+ <th>SMW 3.0.x</th>
+ <td><strong><a href="https://php.net/supported-versions.php">5.6.0</a></strong> - latest</td>
+ <td>3.5.0 - latest</td>
+ <td><strong><a href="https://www.mediawiki.org/wiki/Version_lifecycle">1.27.0</a></strong> - 1.31.x</td>
+ </tr>
+ <tr>
+ <th>SMW 2.5.x</th>
+ <td><strong><a href="https://php.net/supported-versions.php">5.5.0</a></strong> - 7.1.x</td>
+ <td>3.5.0 - 3.12.x</td>
+ <td><strong><a href="https://www.mediawiki.org/wiki/Version_lifecycle">1.23.0</a></strong> - 1.30.x</td>
+ </tr>
+ <tr>
+ <th>SMW 2.4.x</th>
+ <td>5.3.2 - 7.0.x</td>
+ <td>3.5.0 - 3.9.x</td>
+ <td>1.19 - 1.27</td>
+ </tr>
+ <tr>
+ <th>SMW 2.3.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td><strong>3.5.0</strong> - 3.9.x</td>
+ <td>1.19 - 1.25</td>
+ </tr>
+ <tr>
+ <th>SMW 2.2.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>3.3.0 - 3.9.x</td>
+ <td>1.19 - 1.25</td>
+ </tr>
+ <tr>
+ <th>SMW 2.1.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>3.3.0 - 3.5.x</td>
+ <td>1.19 - 1.24</td>
+ </tr>
+ <tr>
+ <th>SMW 2.0.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>No support</td>
+ <td>1.19 - 1.23</td>
+ </tr>
+ <tr>
+ <th>SMW 1.9.x</th>
+ <td>5.3.2 - 5.6.x</td>
+ <td>No support</td>
+ <td>1.19 - 1.22</td>
+ </tr>
+</table>
+
+* *Note:* It is strongly recommended to also always upgrade the underlying MediaWiki software to supported versions. See the [version lifecycle](https://www.mediawiki.org/wiki/Version_lifecycle) for current information on supported versions.
+* For the 1.28 MediaWiki release branch, 1.28.1 or later is recommended due to [T154428](https://phabricator.wikimedia.org/T154428).
+* For the 1.27 MediaWiki release branch, 1.27.4 or later is recommended due to [T100085](https://phabricator.wikimedia.org/T100085).
+* PHP 7.1+ requires at least MediaWiki 1.29 due to [T153505](https://phabricator.wikimedia.org/T153505) and [T143788](https://phabricator.wikimedia.org/T143788) (at the time of this writing). Please also consult the official MediaWiki release documentation.
+
+**Releases before Composer support:**
+
+<table>
+ <tr>
+ <th></th>
+ <th>PHP</th>
+ <th>HHVM</th>
+ <th>MediaWiki</th>
+ <th>Validator</th>
+ </tr>
+ <tr>
+ <th>SMW 1.8.x</th>
+ <td>5.2.0 - 5.5.x</td>
+ <td>No support</td>
+ <td>1.17 - 1.22</td>
+ <td>0.5.1</td>
+ </tr>
+ <tr>
+ <th>SMW 1.7.1</th>
+ <td>5.2.0 - 5.4.x</td>
+ <td>No support</td>
+ <td>1.16 - 1.19</td>
+ <td>0.4.13 or 0.4.14</td>
+ </tr>
+</table>
+
+
+## Database compatibility
+
+### SQL support
+
+<table>
+ <tr>
+ <th></th>
+ <th>MySQL</th>
+ <th>SQLite</th>
+ <th>PostgreSQL</th>
+ </tr>
+ <tr>
+ <th>SMW 3.0.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.5.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.4.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.3.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.2.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.1.x</th>
+ <td>Full support (5.x)</td>
+ <td>Full support (3.x)</td>
+ <td>Full support (9.x)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.0.x</th>
+ <td>Full support</td>
+ <td>Full support</td>
+ <td>Beta support</td>
+ </tr>
+ <tr>
+ <th>SMW 1.9.x</th>
+ <td>Full support</td>
+ <td>Full support</td>
+ <td>Beta support</td>
+ </tr>
+ <tr>
+ <th>SMW 1.8.x</th>
+ <td>Full support</td>
+ <td>Full support</td>
+ <td>Experimental support</td>
+ </tr>
+ <tr>
+ <th>SMW 1.7.1</th>
+ <td>Full support</td>
+ <td>Experimental support</td>
+ <td>No support</td>
+ </tr>
+</table>
+
+Note that MS SQL Server and Oracle are not supported as database backends.
+
+### Triple store support
+
+<table>
+ <tr>
+ <th></th>
+ <th><a href="https://jena.apache.org/">Fuseki</a></th>
+ <th><a href="https://github.com/openlink/virtuoso-opensource">Virtuoso</a></th>
+ <th><a href="https://github.com/garlik/4store">4store</a></th>
+ <th><a href="http://rdf4j.org/">Sesame</a></th>
+ <th><a href="https://wiki.blazegraph.com/">Blazegraph</a></th>
+ </tr>
+ <tr>
+ <th>SMW 3.0.x</th>
+ <td>Full support<br />(1.x >=1.1) + 2.4.0</td>
+ <td>Full support<br />(6.x >=6.1) + 7.2<sup>[t.1]</sup></td>
+ <td>Beta support<br />(1.x >=1.1)<sup>[t.2]</sup></td>
+ <td>Full support<br />(2.8.x)</td>
+ <td>Full support<br />(1.5.2) + 2.1.0<sup>[t.3]</sup></td>
+ </tr>
+ <tr>
+ <th>SMW 2.5.x</th>
+ <td>Full support<br />(1.x >=1.1) + 2.4.0</td>
+ <td>Full support<br />(6.x >=6.1) + 7.2<sup>[t.1]</sup></td>
+ <td>Beta support<br />(1.x >=1.1)<sup>[t.2]</sup></td>
+ <td>Full support<br />(2.8.x)</td>
+ <td>Full support<br />(1.5.2) + 2.1.0<sup>[t.3]</sup></td>
+ </tr>
+ <tr>
+ <th>SMW 2.4.x</th>
+ <td>Full support<br />(1.x >=1.1) + 2.4.0</td>
+ <td>Full support<br />(6.x >=6.1) + 7.2<sup>[t.1]</sup></td>
+ <td>Beta support<br />(1.x >=1.1)<sup>[t.2]</sup></td>
+ <td>Full support<br />(2.8.x)</td>
+ <td>Full support<br />(1.5.2) + 2.1.0<sup>[t.3]</sup></td>
+ </tr>
+ <tr>
+ <th>SMW 2.3.x</th>
+ <td>Full support<br />(1.x >=1.1)</td>
+ <td>Full support<br />(6.x >=6.1) + 7.1<sup>[t.1]</sup></td>
+ <td>Beta support<br />(1.x >=1.1)<sup>[t.2]</sup></td>
+ <td>Full support<br />(2.7.x)</td>
+ <td>Full support<br />(1.5.2)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.2.x</th>
+ <td>Full support<br />(1.x >=1.1)</td>
+ <td>Full support<br />(6.x >=6.1) + 7.1<sup>[t.1]</sup></td>
+ <td>Beta support<br />(1.x >=1.1)<sup>[t.2]</sup></td>
+ <td>Full support<br />(2.7.x)</td>
+ <td>Beta support<br />(1.5.2)</td>
+ </tr>
+ <tr>
+ <th>SMW 2.1.x</th>
+ <td>Full support<br />(1.x >=1.1)</td>
+ <td>Full support<br />(6.x >=6.1)</td>
+ <td>Beta support<br />(1.x >=1.1)</td>
+ <td>Full support<br />(2.7.x)</td>
+ <td>Not tested</td>
+ </tr>
+ <tr>
+ <th>SMW 2.0.x</th>
+ <td>Full support</td>
+ <td>Full support</td>
+ <td>Beta support</td>
+ <td>Beta support</td>
+ <td>Not tested</td>
+ </tr>
+ <tr>
+ <th>SMW 1.9.x</th>
+ <td>No support</td>
+ <td>Beta support</td>
+ <td>Beta support</td>
+ <td>Beta support</td>
+ <td>Not tested</td>
+ </tr>
+ <tr>
+ <th>SMW &lt; 1.9</th>
+ <td>No support</td>
+ <td>Experimental support</td>
+ <td>Experimental support</td>
+ <td>No support</td>
+ <td>Not tested</td>
+ </tr>
+</table>
+
+- "Full support" means that all functionality has been verified to work and that it can be used in production
+- "Beta support" means that most functionality has been verified to work, though stability is still low, and things might be buggy
+- "Experimental support" means there is some preliminary support which is still much too immature for use in production
+
+The information in brackets denotes the versions with which SMW is known to work. SMW might also
+work with different versions, especially more recent ones, though this is not guaranteed.
+
+## Notes
+
+- <sup>[t.1]</sup> On an irregular test plan with [Virtuoso 7.2](https://travis-ci.org/mwjames/SemanticMediaWiki/builds/97294290)
+- <sup>[t.2]</sup> On an irregular test plan with [4store 1.1.4](https://travis-ci.org/mwjames/SemanticMediaWiki/builds/61200454)
+- <sup>[t.3]</sup> [#1583](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1583)
+- [#2688](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2688) Removed HHVM support
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/CONTRIBUTING.md b/www/wiki/extensions/SemanticMediaWiki/docs/CONTRIBUTING.md
new file mode 100644
index 00000000..32afad72
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/CONTRIBUTING.md
@@ -0,0 +1,51 @@
+<!-- Note that the headers in this file are being linked to via bitly.com. In case the headers change these links must be changed, too. Just ping kghbln on changes. -->
+There are different ways to make a contribution to [Semantic MediaWiki][smw] (SMW) while a few guidelines are necessary to keep the workflow and review process most efficient.
+
+### Report bugs
+
+A bug is the occurrence of an unintended or unanticipated behaviour that causes a vulnerability or fatal error.
+
+* You may help us by reporting bugs and feature requests via the [issue tracker][smw-issues]. See the help page on [reporting bugs (environment, reproducing)][smw-bugs1] as well as on [identifying bugs (stack-trace)][smw-bugs2] for information on how to do this best. Please remember to always provide information about your environment as well as a stack-trace.
+* You may help us to do [pre-release testing][smw-testing] by joining the [team of testers][smw-testers] on GitHub.
+
+### Feature requests
+
+A feature request is a new, or altered behaviour of an existing functionality.
+
+A request should contain a detailed description of the expected behaviour in order to avoid misconceptions in the process of an implementation. The following notes are provided to aid the process and help prioritize a request for project members.
+
+* Why is the feature requested or relevant?
+* What does the feature solve? (e.g. it addresses a generalizable behaviour, it solves a specific use case etc.)
+* Examples demonstrate the expected behaviour by:
+ * Being simple, clear, and targeted towards the feature and not rely on any other external functionality (such as other parser functions or extensions).
+ * In case of a modified behaviour, examples demonstrate the old and new behaviour together with an explanation of the expected difference
+ * In case of a new behaviour, examples are written down to outline the expected new behaviour (best practice is a step-by-step description) together with a [use case](https://en.wikipedia.org/wiki/Use_case)
+* Examples made available via the [sandbox](https://sandbox.semantic-mediawiki.org).
+* Examples should be adaptable so they can be used as part of an [integration test](https://www.semantic-mediawiki.org/wiki/Help:Integration_tests).
+
+### Improve documentation
+
+* You may help us by providing software translations via [translatewiki.net][twn]. See their [progress-statistics][twn-smw] to find out if there is still work to do for your language.
+* You may help us by creating, updating or amending the documentation of the software on [Semantic-MediaWiki.org][smw].
+
+### Provide patches
+
+You may help us by providing patches or additional features via a pull request but in order to swiftly co-ordinate your code contribution, the following should suffice:
+
+* Please ensure that pull requests are based on the current master. See also the [developer documentation overview][smw-ddo] for further information.
+* Code should be easily readable (see [NPath complexity][smw-npath], `if else` nesting etc.) and if necessary be put into separate components (or classes)
+* Newly added features should not alter existing tests but instead provide additional test coverage to verify the expected new behaviour. For a description on how to write and run PHPUnit test, please consult the [manual][mw-testing].
+
+Thank you for contributing to Semantic MediaWiki!
+
+[smw]: https://github.com/SemanticMediaWiki/SemanticMediaWiki
+[smw-issues]: https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues
+[smw-bugs1]: https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs
+[smw-bugs2]: https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs
+[smw-testing]: https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs#Pre-release_testing
+[smw-testers]: https://github.com/orgs/SemanticMediaWiki/teams/testers
+[twn]: https://translatewiki.net/
+[twn-smw]: https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=ext-semanticmediawiki&suppressempty=1
+[smw-ddo]: https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/README.md
+[mw-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing
+[smw-npath]: https://www.semantic-mediawiki.org/wiki/Code_coverage#NPath_complexity
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/INSTALL.md b/www/wiki/extensions/SemanticMediaWiki/docs/INSTALL.md
new file mode 100644
index 00000000..aeb04a88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/INSTALL.md
@@ -0,0 +1,138 @@
+# Installation guide (brief)
+
+This is a brief installation and configuration guide for [Semantic MediaWiki](../README.md) (SMW)
+that only contains the core steps. More verbose installation instructions with additional explanation
+and upgrading instructions can be found [here](https://www.semantic-mediawiki.org/wiki/Help:Installation).
+
+A list of supported PHP versions, MediaWiki versions and databases per SMW release can be found
+in the [compatibility matrix](COMPATIBILITY.md).
+
+
+## Download and installation
+
+### Installation with Composer
+
+The strongly recommended way to install Semantic MediaWiki is with [Composer](http://getcomposer.org) using
+[MediaWiki's built-in support for Composer](https://www.mediawiki.org/wiki/Composer).
+
+#### Step 1
+
+Change to the base directory of your MediaWiki installation. This is where the "LocalSettings.php"
+file is located. If you have not yet installed Composer do it now by running the following command
+in your shell:
+
+ wget https://getcomposer.org/composer.phar
+
+#### Step 2
+
+If you do not have a "composer.local.json" file yet, create one and add the following content to it:
+
+```
+{
+ "require": {
+ "mediawiki/semantic-media-wiki": "~3.0"
+ }
+}
+```
+
+If you already have a "composer.local.json" file add the following line to the end of the "require"
+section in your file:
+
+ "mediawiki/semantic-media-wiki": "~3.0"
+
+Remember to add a comma to the end of the preceding line in this section.
+
+#### Step 3
+
+Run the following command in your shell:
+
+ php composer.phar update --no-dev
+
+Note if you have Git installed on your system add the `--prefer-source` flag to the above command. Also
+note that it may be necessary to run this command twice. If unsure do it twice right away.
+
+#### Step 4
+
+Run the MediaWiki [update script](https://www.mediawiki.org/wiki/Manual:Update.php). The location of
+this script is `maintenance/update.php`. It can be run as follows in your shell:
+
+ php maintenance/update.php
+
+#### Step 5
+
+Add the following line to the end of your "LocalSettings.php" file:
+
+ enableSemantics( 'example.org' );
+
+Note that "example.org" should be replaced by your wiki's domain.
+
+#### Step 6
+
+If you are installing SMW on a freshly installed wiki continue to the next step. If the wiki already has content
+pages run the Semantic MediaWiki [data rebuild script](https://www.semantic-mediawiki.org/wiki/Help:Maintenance_script_"rebuildData.php"). The location of this script
+is `extensions/SemanticMediaWiki/maintenance/rebuildData.php`. It can be run as follows in your shell:
+
+ php extensions/SemanticMediaWiki/maintenance/rebuildData.php -v
+
+#### Verify installation success
+
+As final step, you can verify SMW got installed by looking at the "Special:Version" page on your wiki and check that
+the Semantic MediaWiki section is listed.
+
+### Installation without shell access
+
+As an alternative to installing via Composer, you can obtain the SMW code by creating your own [individual file release](https://github.com/SemanticMediaWiki/IndividualFileRelease) most likely if command line access to the webspace is not available or if the hoster imposes restrictions on required functionality.
+
+Note that SMW no longer provides file releases [(See #3347).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1732)
+
+#### Step 1
+
+Create your [individual file release](https://github.com/SemanticMediaWiki/IndividualFileRelease) using the respective script. Please pay attention to the MediaWiki version used in the script and adapt to your setup if necessary.
+
+#### Step 2
+
+Transfer the code thus compiled to the appropriate folders on your webspace.
+
+#### Step 3
+
+Add the following lines to the end of your "LocalSettings.php" file:
+
+ enableSemantics( 'example.org' );
+
+Note that "example.org" should be replaced by your wiki's domain.
+
+#### Step 4
+
+Log in as a user with administrator permission to your wiki and go to the "Maintenance" tab on special page "Special:SemanticMediaWiki":
+
+Click on the "Initialise or upgrade tables" button in the "Database maintenance" section to setup the
+database.
+
+#### Step 5
+
+If you are installing SMW on a freshly installed wiki continue to the next step. If the wiki already has content
+pages also do the following on page "Special:SemanticMediaWiki":
+
+Click on the "Start updating data" button in the "Data rebuild" subsection of "Maintenance" tab
+to activate the [automatic data update](https://www.semantic-mediawiki.org/wiki/Help:Repairing_SMW's_data).
+
+#### Verify installation success
+
+As final step, you can now verify SMW got installed by looking at the "Special:Version" page on your wiki and check that
+the Semantic MediaWiki section is listed.
+
+### Installation of development versions and release candidates
+
+If you would like to install a development version or release candidate then replace the lines as stated in step 3 of the
+"Installation with Composer" section with the following line
+
+* master: `"mediawiki/semantic-media-wiki": "@dev"`
+* legacy branch: `"mediawiki/semantic-media-wiki": "3.0.x@dev"`
+* release candidate: `"mediawiki/semantic-media-wiki": "~3.0@rc"`
+
+## More instructions
+
+* [Verbose installation instructions](https://www.semantic-mediawiki.org/wiki/Help:Installation)
+* [Upgrading instructions](https://www.semantic-mediawiki.org/wiki/Help:Installation#Upgrading)
+* [Configuration instructions](https://www.semantic-mediawiki.org/wiki/Help:Configuration)
+* [Administrator manual](https://www.semantic-mediawiki.org/wiki/Help:Administrator_manual)
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/ISSUE_TEMPLATE.md b/www/wiki/extensions/SemanticMediaWiki/docs/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..087e9e86
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/ISSUE_TEMPLATE.md
@@ -0,0 +1,21 @@
+### Setup and configuration
+
+- SMW version:
+- MW version:
+- PHP version:
+- DB system (MySQL, Blazegraph, etc.) and version:
+
+_Note that it is **required** to provide the setup and configuration information. If you believe to have detected a regression after updating your software please also provide information about your previous setup as well. **In any case only this information will enable us to allocate appropriate review time.** Thank you for you help._
+
+### Issue
+_Please describe the issue as best as you can and also fill in the following two sections if applicable._
+
+### Stack trace
+Produces a [stack trace](https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs) and/or outputs:
+
+```
+Placeholder for the stack trace or code!
+```
+
+### Steps to reproduce
+_It is recommend to use the [sandbox](https://sandbox.semantic-mediawiki.org) to demonstrate the observed issue, otherwise provide us with a simple reproducible use case._
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/PULL_REQUEST_TEMPLATE.md b/www/wiki/extensions/SemanticMediaWiki/docs/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..574b18c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,12 @@
+This PR is made in reference to: #
+
+This PR addresses or contains:
+- ...
+- ...
+- ...
+
+This PR includes:
+- [ ] Tests (unit/integration)
+- [ ] CI build passed
+
+Fixes # \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/README.md b/www/wiki/extensions/SemanticMediaWiki/docs/README.md
new file mode 100644
index 00000000..963fdfeb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/README.md
@@ -0,0 +1,23 @@
+
+The documentation for Semantic MediaWiki (SMW) can primarily be found on the [SMW homepage](https://www.semantic-mediawiki.org).
+Some core documentation is included together with the source code, and can mostly be found in the
+`docs/` directory. This documentation includes basic installation instructions, release notes and
+technical documentation.
+
+* [Installation instructions](INSTALL.md) (see also the [upgrade guide](https://www.semantic-mediawiki.org/wiki/Help:Installation))
+* [Compatibility matrix](COMPATIBILITY.md)
+
+## Release notes
+
+* [Latest release notes](RELEASE-NOTES.md)
+* [Other release notes](releasenotes/README.md)
+
+## Technical documentation
+
+* [Technical documentation](technical/README.md)
+
+## Repository documentation
+
+* [Contributing](CONTRIBUTING.md)
+* [Issue template](ISSUE_TEMPLATE.md)
+* [Pull request template](PULL_REQUEST_TEMPLATE.md)
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/RELEASE-NOTES.md b/www/wiki/extensions/SemanticMediaWiki/docs/RELEASE-NOTES.md
new file mode 100644
index 00000000..36557b5c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/RELEASE-NOTES.md
@@ -0,0 +1,24 @@
+# Semantic MediaWiki 3.0.2
+
+Released on April 11, 2019.
+
+## Enhancements
+
+* [#3682](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3682) as `f5d0cab`: Removed `IsFileCacheable` hook and improves file caching
+* [#3856](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3854) as `767c981`: Switched setting of configuration parameter "$smwgCompactLinkSupport" to "false"
+
+## Bug fixes and internal code changes
+
+* [#3742](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3742): Fixed raw output of templated message particles
+* [#3771](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3771) as `b7a78e0`: Removed `doPostOutputShutdown`
+* [#3772](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3772) as `79e4adb`: Fixed "... expects parameter 1 to be a valid callback ..."
+* [#3775](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3775) as `3b0d83c`: Made data type "Equivalent URI" non declarative
+* [#3847](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3847) as `d088204`: Fixed warning "a non-numeric value" was encountered
+* [#3854](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3854) as `d05671e`: Provided a new test case for "display title"
+* [#3859](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3859): Updated "InfolinkTest"
+* [#3863](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3863): Fixed resource loading by replacing `localpath` with `localBasePath`
+* [0ed4bbf7](https://github.com/SemanticMediaWiki/SemanticMediaWiki/commit/0ed4bbf75e7ee9989d2ac84437d3733b52885eb8): Updated "EventHandlerTest"
+
+## See also
+* [RELEASE NOTES](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/3.0.x/docs/releasenotes/RELEASE-NOTES-3.0.0.md) for Semantic MediaWiki 3.0.0
+* [RELEASE NOTES](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/3.0.x/docs/releasenotes/RELEASE-NOTES-3.0.1.md) for Semantic MediaWiki 3.0.1
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/README.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/README.md
new file mode 100644
index 00000000..8f1df375
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/README.md
@@ -0,0 +1,36 @@
+## Release notes
+
+* [SMW 3.0.2 release notes](RELEASE-NOTES-3.0.2.md)
+* [SMW 3.0.1 release notes](RELEASE-NOTES-3.0.1.md)
+* [SMW 3.0.0 release notes](RELEASE-NOTES-3.0.0.md)
+* [SMW 2.5.8 release notes](RELEASE-NOTES-2.5.8.md)
+* [SMW 2.5.7 release notes](RELEASE-NOTES-2.5.7.md)
+* [SMW 2.5.6 release notes](RELEASE-NOTES-2.5.6.md)
+* [SMW 2.5.5 release notes](RELEASE-NOTES-2.5.5.md)
+* [SMW 2.5.4 release notes](RELEASE-NOTES-2.5.4.md)
+* [SMW 2.5.3 release notes](RELEASE-NOTES-2.5.3.md)
+* [SMW 2.5.2 release notes](RELEASE-NOTES-2.5.2.md)
+* [SMW 2.5.1 release notes](RELEASE-NOTES-2.5.1.md)
+* [SMW 2.5.0 release notes](RELEASE-NOTES-2.5.0.md)
+* [SMW 2.4.6 release notes](RELEASE-NOTES-2.4.6.md)
+* [SMW 2.4.5 release notes](RELEASE-NOTES-2.4.5.md)
+* [SMW 2.4.4 release notes](RELEASE-NOTES-2.4.4.md)
+* [SMW 2.4.3 release notes](RELEASE-NOTES-2.4.3.md)
+* [SMW 2.4.2 release notes](RELEASE-NOTES-2.4.2.md)
+* [SMW 2.4.1 release notes](RELEASE-NOTES-2.4.1.md)
+* [SMW 2.4.0 release notes](RELEASE-NOTES-2.4.0.md)
+* [SMW 2.3.1 release notes](RELEASE-NOTES-2.3.1.md)
+* [SMW 2.3.0 release notes](RELEASE-NOTES-2.3.0.md)
+* [SMW 2.2.3 release notes](RELEASE-NOTES-2.2.3.md)
+* [SMW 2.2.2 release notes](RELEASE-NOTES-2.2.2.md)
+* [SMW 2.2.1 release notes](RELEASE-NOTES-2.2.1.md)
+* [SMW 2.2.0 release notes](RELEASE-NOTES-2.2.0.md)
+* [SMW 2.1.3 release notes](RELEASE-NOTES-2.1.3.md)
+* [SMW 2.1.2 release notes](RELEASE-NOTES-2.1.2.md)
+* [SMW 2.1.1 release notes](RELEASE-NOTES-2.1.1.md)
+* [SMW 2.1.0 release notes](RELEASE-NOTES-2.1.0.md)
+* [SMW 2.0.0 release notes](RELEASE-NOTES-2.0.md)
+* [SMW 1.9.2 release notes](RELEASE-NOTES-1.9.2.md)
+* [SMW 1.9.1.1 release notes](RELEASE-NOTES-1.9.1.1.md)
+* [SMW 1.9.1 release notes](RELEASE-NOTES-1.9.1.md)
+* [SMW 1.9.0 release notes](RELEASE-NOTES-1.9.md)
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.1.md
new file mode 100644
index 00000000..af3f8620
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.1.md
@@ -0,0 +1,10 @@
+Released on August 20, 2011.
+
+* #ask queries now allow printouts (table columns) that are exactly the same.
+ In previous versions, multiple printouts with the same label would be
+ collapsed (e.g. {{#ask: [[Catgegory:City]] |?|?|?}} now shows four columns).
+* Fixed continue and limit escaping issues on Special:Ask.
+* Fixed error occurring for invalid queries with count or debug formats.
+* Fixed several small issues with the table format and cleaned it up somewhat.
+* Fixed parsing of years for data properties.
+* Internationalization improvements and layout tweaks to #smwdoc. \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.md
new file mode 100644
index 00000000..2655c60d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.6.md
@@ -0,0 +1,50 @@
+Released on July 30, 2011.
+
+* Full support for synchronizing RDF stores with SMW, and for answering #ask
+ queries based on this data. The communication happens via SPARQL (1.1), and
+ all SPARQL-capable stores should be supported. The following settings are
+ needed in LocalSettings.php:
+ $smwgDefaultStore = 'SMWSparqlStore';
+ $smwgSparqlDatabase = 'SMWSparqlDatabase';
+ // The following should be set to the URLs to reach the store:
+ $smwgSparqlQueryEndpoint = 'http://localhost:8080/sparql/';
+ $smwgSparqlUpdateEndpoint = 'http://localhost:8080/update/';
+ $smwgSparqlDataEndpoint = 'http://localhost:8080/data/'; // can be empty
+ The specific support that SMW used to have for the RAP RDF store has been
+ discontinued.
+* The Type namespace has been abolished. Builtin types now are displayed by the
+ special page Special:Types, and there are no "custom types" any longer.
+ By default, the Type namespace is gone and existing pages in this namespace
+ can no longer be accessed. This can be changed by setting
+ $smwgHistoricTypeNamespace = true in LocalSettings.php before including SMW.
+* Changed the way in which units of measurement work. Type:Number now does not
+ accept any units, and a new type "Quantity" is used for numbers with units.
+ Units must be declared on the property page (not on the Type page as before),
+ and only units that have a declared conversion factor are accepted.
+* The declaration of Type:Record properties has changed. Instead of a list of
+ datatypes, the declaration now requires a list of properties that are to be
+ used for the fields of the record. The declaration is still done with the
+ property "has fields" as before. Properties must not be used more than once
+ in has_fields, or the order of values will be random.
+* Introduced pre-defined builtin properties for every datatype. For example,
+ the property String is always of type String and available on all (English)
+ wikis. This helps to keep some of the old Tpe:Record declarations valid.
+* Changed the way parameters in query printers are specified and handled
+ using the Validator extension. This includes improvements to the parameter
+ options in the Special:Ask GUI and better error reporting for ask queries.
+* Added UNIX-style DSV (Delimiter-separated values) result format.
+* Reworked internal data model, cleaning up and re-implementing SMWDataValue and
+ all of its subclasses, and introducing new data item classes to handle data.
+ The class SMWCompatibilityHelpers provides temporary help for extensions that
+ still depend on the old format and APIs.
+* Fixed PostgreSQL issues with the installation and upgrade code.
+* Added API module (smwinfo) via which statistics about the semantic data can
+ be obtained.
+* Added second parameter to #info that allows chosing either the info or warning
+ icon.
+* Added #smwdoc parser hook that displays a table with parameter documentation for
+ a single specified result format.
+* Fixed escaping issues in the JSON result format. A compatibility breaking change
+ is that per property an array of values will be returned, even if there is only
+ one.
+* Added SMWStore::updateDataBefore and SMWStore::updateDataAfter hooks. \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.1.md
new file mode 100644
index 00000000..ac55e9d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.1.md
@@ -0,0 +1,24 @@
+Released on March 5, 2012.
+
+* Added basic support for OpenLink Virtuoso RDF database. In addition to
+ $smwgDefaultStore = 'SMWSparqlStore', users should set
+ $smwgSparqlDatabase = 'SMWSparqlDatabaseVirtuoso' and use Virtuoso's SPARQL
+ endpoint at ./sparql/ for query and update alike. for further remarks and known
+ limitations, see the file ./includes/sparql/SMW_SparqlDatabaseVirtuoso.php .
+* Added ability to sort dates as dates in tables generated by SMW (bug 25768).
+* Added "Last editor is" and "Is a new page" special properties (bug 34359).
+* When there are only invalid query conditions, query answering is stopped (bug 33177).
+* Fixed display of nearby values on Special:SearchByProperty (bug 34178).
+* Fixed display of URL values (bug 34312, 34044).
+* Fixed warning when browsing certain property pages (bug 34306).
+* Fixed failure of SMW_setup --delete when using postgresql (bug 31153).
+* Fixed division by 0 error when setting the "Corresponds to" property to 0 (bug 32594).
+* Fixed accept header send with SPARQL query requests (bug 32280).
+* Fixed unresolved prefixed name in SPARQL queries (bug 33687).
+* Fixed issues with modification date property occurring when using SMWSparqlStore (bug 30989).
+* Fixed erroneous SPARQL for property value comparison queries (bug 30993).
+* Fixed broken +index=x for records (bug 30284).
+* Fixed querying of subobjects using 4store as a datastore.
+* Fixed issue with namespace internationalization (bug 34383).
+
+Also see the [SMW 1.7 release notes](RELEASE-NOTES-1.7.md) \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.md
new file mode 100644
index 00000000..ab46062a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.7.md
@@ -0,0 +1,56 @@
+Released on January 1, 2012.
+
+* Added native "internal objects" that can be set using #subobject.
+* Added support for selecting a default graph when using RDF stores,
+ configuration called $smwgSparqlDefaultGraph
+* Use of native MediaWiki sortable tables for the table formats.
+* Added value distribution functionality that can be used by result formats.
+* Added validation and manipulation of the 'format' parameter using Validator.
+* Added 'class' parameter to the 'table' format, which allows setting the CSS
+ class.
+* Added automatically-generated CSS classes for rows and cells in 'table' format.
+* Added alpha version of native SMW Ask API with modules 'ask' and 'askargs'.
+* Added alpha version of a new ask query interface: Special:QueryCreator.
+* Added setting to choose which optional special properties are enabled ($smwgPageSpecialProperties).
+* Added creation date special property (disabled by default) (bug 32165).
+* Added support for altitudes in geographic coordinates (bug 32698).
+* Added definitions for missing params (sort, order, searchlabel) to the base query printer.
+* Added automatic invocation of the SMW_setup maintenance script when running
+ update.php (for MW >= 1.19).
+* Added support for the Page Schemas extension.
+* Removed defunct "SMWLight" files.
+* Fixed separator and filename parameters for the DSV format.
+* Fixed display of properties of type URL (bug 30912).
+* Fixed hide query functionality on Special:Ask (bug 30768).
+* Fixed display of internal SMW helper constants in certain queries (bug 30969).
+* Fixed some issues with the category result format (including bug 30761).
+* Fixed email validation issue (bug 32295).
+* Fixed incorrect handling of sort and order parameters on Special:Ask (bug 32706).
+* Fixed display of images to old behavior after a recent regression.
+* Fixed fatal error in the concept cache maintenance script (bug 32592).
+* Fixed factbox links to Special:SearchByProperty containing numerical numbers for
+ wikis in languages with the comma as decimal separator instead of a dot.
+* Fixed the "hide incoming properties" link on Special:Browse.
+* Fixed several more issues not listed here.
+* Dropped compatibility with MediaWiki 1.15.x (and earlier).
+* Dropped compatibility with old-style (SMW < 1.6) query printers.
+* Full compatibility with MediaWiki 1.18 and foreward-compatibility with 1.19.
+
+== SMW 1.7.0.2 ==
+
+Released on January 16, 2012.
+
+* Fixed creation of concept cache and display of matching objects on concept pages (bug 32592, 32718).
+* Fixed fatal error occurring for some invalid property definitions (bug 33652).
+* Fixed error in RSS when using creator or date parameters (bug 33721).
+* Fixed incorrect offset of export formats (bug 33726).
+
+== SMW 1.7.0.1 ==
+
+Released on January 9, 2012.
+
+* Fixed bug in "further results" links causing the main column to be displayed twice on Special:Ask (bug 33473).
+* Fixed incorrect case-sensitivity of the format parameter (bug 31138).
+* Fixed internationalization of magic words.
+* Fixed offset and limit value in further results links (bug 33575).
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.8.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.8.md
new file mode 100644
index 00000000..cf3f358b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.8.md
@@ -0,0 +1,79 @@
+Semantic MediaWiki 1.8 is a stable release.
+
+Released December 2nd, 2012.
+
+=== Compatibility changes ===
+
+* Changed minimum MediaWiki version from 1.16 to 1.17.
+* Full compatibility with MediaWiki 1.19 and forward-compatibility with 1.20.
+* Changed minimum Validator version from 0.4 to 0.5.
+* The structure of the JSON result format has been altered (bug 37418).
+* The default database layout has changed. Please read the upgrade instructions for existing sites.
+
+=== New features ===
+
+* The #subobject function now supports the use of empty subobject names ("-" has the same effect). A new anonymous subobject will be created in this case. (bug 36634)
+* Added atom printer (bug 38636)
+* SMW now collects data about all queries, and this data can be queried to get an overview of queries used on the wiki. Information about queries is stored in subobjects of the page they are on. The property [[Property:Has query]] links a page to a query. The query subobject currently holds values for the following properties: [[Property:Query string]], [[Property:Query format]], [[Property:Query size]], [[Property:Query depth]].
+* Added new info tooltip (bug 37361)
+* Added two new output formats for properties of type Time, "Gr" and "Jl", to set the desired calendar model for display in #ask results. If nothing is given, SMW will select the calendar based on the date as before (Julian calendar defaults from until 4th Oct 1582). As before. the same behavior is used when entering dates, and "Jl" and "Gr" also work in this case.
+* Added new output format "nowiki" to properties of type URL, which will format a URL in such a way that it will not be autolinked by MediaWiki.
+
+=== Enhancements ===
+
+* Significanlty less database write activity on wiki edits that do not change semantic data
+* Less database read activity (esp. for displaying long query results and special pages) due to new and improved caching mechanisms
+* Improved database table layout for more efficient access and better use of storage space
+* Significant performance improvements for Special:Properties and Special:UnusedProperties
+* Support for configuring dedicated tables for more efficient handling of frequently used properties, see http://semantic-mediawiki.org/wiki/Fixed_properties
+* Improved JSON export (bug 37418).
+* Improved Special:Ask UI.
+** Enabled Special:Ask parameters form to be collapsible.
+** Added individual format help links to enable access to online help manuals.
+* Added continuation support to the Ask API.
+* Added possibility to change the appearance of the service link icon via CSS (bug 40644)
+
+=== Dropped features ===
+
+* Special:QueryCreator got disabled pending fixes.
+* Values of type Time can no longer be selected by string pattern matching (the string was unspecified and has an internal format that is not useful to match against). This safes database memory (fewer indexes).
+
+=== Bug fixes ===
+
+* Fixed automatic running of SMW updating script when running maitenance/update.php.
+* Fixed page parameter handling on Special:ExportPDF (bug 35180).
+* Fixed handling of #set_recurring_event for 'month' and 'year' time units.
+* Fixed error on Special:URIResolver (bug 35643).
+* Fixed 'badtitle' error on Special:URIResolver (bug 39967).
+* Fixed alternative query source ($smwgQuerySources) (bug 38292).
+* Fixed getMaxIncludeSize() on a non-object error in Special:Ask (bug 40650)
+* Fixed markup issue with the display of service links (bug 39445)
+* Fixed tooltip size issue (bug 29764)
+* Fixed Special:Ask autocomplete issue (bug 40676)
+* Fixed serialisation of Category namespace queries (bug 37065)
+* Fixed non-linked output of URL values in query results (bug 39392)
+* Avoid malformed SPARQL when default graph is not set (bug 37575)
+* Do not batch process pages when using SMW_refreshData.php with delay option (bug 38136)
+* Avoid use of hash_init PHP functions; use md5() instead (bug 38185)
+* Fixed parsing of malformed printout requests that could cause exception (bug 38489)
+* Don't confuse Julian an Gregorian dates internally (bug 41126)
+* Avoid MediaWiki problem that can cause errors when refreshing data and using _LEDT (last editor is) (bug 35962)
+* Fixed Special:PageProperty, which was broken
+
+=== Technical improvements and code quality ===
+
+* Added tests for the query processor, the store, data items and result printers, including base test classes for the later two that can be used by extensions.
+* Refactored large portions of the SQLStore implementation (SMWSQLStore3).
+ * More, shorter files for various task groups (SQLStore3_Writers, SQLStore3_Readers, SQLStore3_SpecialPageHandlers, SQLStore_SetupHandler, and others)
+ * Different types of data now handled modularly by DIHandler classes
+ * Completely rewritten writing methods that compute a full diff on a database level to decide which write activities are needed (if any)
+* No more inline CSS and JavaScript on special pages (now all resource loader modules).
+* Added SMWExportPrinter class to facilitate creating export printers and simplify handling code.
+* Added SMWIResultPrinter interface to type hint result printers against.
+* Migrated Validator parameter definitions to new Validator 0.5 array style.
+* Transformed SMW_custom.css, SMW_tooltip.js, and the skin folder to comply with the ResourceLoader environment
+* Made query link generation completely generic. Links for all formats will now contain all provided arguments.
+
+=== Extended translations ===
+
+As usual, translations have been extended thanks to the [[Translatewiki.net|Translatewiki.net project]].
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.1.md
new file mode 100644
index 00000000..ae0ea7e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.1.md
@@ -0,0 +1,19 @@
+# Semantic MediaWiki 1.9.0.1
+
+Released January 6th, 2014.
+
+### New features
+
+* (Issue 82) It is now possible to view all SMW settings via Special:SMWAdmin.
+
+### Bug fixes
+
+* (Bug 59204) Fixed removal of properties on deletion of pages in custom namespaces.
+* (Issue 73) Fixed double table prefixing issue causing problems when using SQLite.
+* (Issue 84) Fixed the link to the INSTALL file on Special:SMWAmin.
+* (9ac5288) Fixed reference to DataTypeRegistry in the SPARQL store.
+
+### New configuration parameters
+
+* [$smwgOnDeleteAction](https://semantic-mediawiki.org/wiki/Help:$smwgOnDeleteAction)
+(incl. $smwgDeleteSubjectAsDeferredJob, $smwgDeleteSubjectWithAssociatesRefresh)
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.2.md
new file mode 100644
index 00000000..c915fdda
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.0.2.md
@@ -0,0 +1,11 @@
+# Semantic MediaWiki 1.9.0.2
+
+Released January 17th, 2014.
+
+### Bug fixes
+
+* #85 Fixed compatibility issue with PHP 5.3 on Special:SMWAdmin and added regression test
+* #86 Fixed compatibility with older MediaWiki versions by supporting legacy job definitions
+* #89 The resource paths will now be correct event if SMW is put on a non-standard location
+* #97 Fixed strict standards notice in the SQLStore
+* #99 Fixed issue occurring in the SQLStore for people using mysqli and MediaWiki 1.22 or later \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.1.md
new file mode 100644
index 00000000..200da60a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.1.md
@@ -0,0 +1,11 @@
+# Semantic MediaWiki 1.9.1.1
+
+Released February 28th, 2014.
+
+### Bug fixes
+
+* #183 Fixed 1.9.1 regression in resource paths that caused resouces not to load
+
+### Internal enhancements
+
+* #200 Improved the usage of "InternalParseBeforeLinks" to indicate whether semantic data have been processed or not
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.md
new file mode 100644
index 00000000..0b1a6f18
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.1.md
@@ -0,0 +1,44 @@
+# Semantic MediaWiki 1.9.1
+
+Released February 9th, 2014.
+
+### New features
+
+* #162 Added possibility to create `_MEDIA` [(special property "Media type")](https://semantic-mediawiki.org/wiki/Help:Special_property_Media_type) and `_MIME`
+[(special property "MIME type")](https://semantic-mediawiki.org/wiki/Help:Special_property_MIME_type)
+property annotation when uploading a file (This feature can only be used with appropriate
+[`$smwgPageSpecialProperties`](https://www.semantic-mediawiki.org/wiki/Help:$smwgPageSpecialProperties)
+settings after running the "update.php" script.)
+* #173 Extended the [factbox](https://semantic-mediawiki.org/wiki/Factbox#The_factbox) in
+order to display "historical" data when used by `action=history`
+* #180 Added further CSS classes for improved customization of special page
+["Ask"](https://semantic-mediawiki.org/wiki/Help:Special:Ask)
+
+### Bug fixes
+
+* #80 Fixed issue with running the system tests on a MySQL based setup
+* #110 Fixed type mismatch issue in the caching code of SQLStore3
+* #121 (Bug 60336) Fixed sortkey issue for multibyte characters in ListResultPrinter
+* #144 (Bug 60284) Fixed record data type issue when using #set/#subobject
+* #145 Fixed PHP strict standards notice in SMWParamFormat::formatValue
+* #146 Fixed 1.9.0.2 regression in resource paths that caused the SMW badge and JS+CSS to
+not be loaded on some wikis
+* #148 Fixed regression that made data type labels case sensitive
+* #151 (Bug 50155) Fixed issue with category hierarchies on SQLite
+* #164 (Bug 19487) Fixed update of predefined properties when uploading a file
+* #166 Fixed Factbox display issue during preview and edit mode
+* #170 Fixed 1.9.0 regression of [special property "Is a new page"](https://semantic-mediawiki.org/wiki/Help:Special_property_Is_a_new_page) (`_NEWP`)
+(Run ["SMW_refreshData.php"](https://semantic-mediawiki.org/wiki/Help:SMW_refreshData.php)
+to amend falsely set values.)
+
+### Internal enhancements
+
+* #112 Added a date data type regression test
+* #118 Added a possibility to inject a Revision into the ContentParser
+* #119 Added a LinksUpdate integration test
+* #131 Added internationalization support for special property "Query duration"
+* #132 Added smoke test for the special language files
+* #142 Changed the Norwegian language code (Bokmål variant) from "no" to "nb" for
+L10n of datatypes, special properties, etc.
+* #144 Added a record data type regression test
+* #151 Added a category hierarchy regression test
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.2.md
new file mode 100644
index 00000000..f1c58119
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.2.md
@@ -0,0 +1,37 @@
+# Semantic MediaWiki 1.9.2
+
+Released April 18th, 2014.
+
+### New features
+
+* #199 Added [object-Id lookup](https://www.semantic-mediawiki.org/wiki/Help:Object_ID_lookup) to SMWAdmin
+* #217 Extended ListResultPrinter to make `userparam` parameter available to intro and outro templates and introduced [additional parameters](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/217)
+* #243 Added `--query` parameter to the `SMW_refreshData.php` maintenance script
+
+### Bug fixes
+
+* #203 Fixed undefined index in connection with `$smwgQuerySources`
+* #215 (Bug 62150) Fixed malformed query order in SparqlQueryEngine
+* #272 #276 Fixed ConceptCache on SQLite
+* #273 Fixed footer logo path for windows based installations
+
+### Deprecated
+
+* #187 Use of `SMW_conceptCache.php` has been deprecated in favour of `rebuildConceptCache.php`
+* #244 Use of `SMW_refreshData.php` has been deprecated in favour of `rebuildData.php`
+
+### Internal enhancements
+
+* #195 Improvement of handling configuration settings in getPropertyTables
+* #204 Added update job for new redirects
+* #207 Removed SMWParamSource and added a `$smwgQuerySources` integration test
+* #218 Extended SparqlStore to inject a SparqlDatabase to enable basic test coverage
+* #226 Added ExecutionTimeTestListener to report long running tests
+* #227 Moved all job related classes into the `SMW\MediaWiki\Jobs\` namespace
+* #234 Added redirects regression test
+* #236, #265 Removed wfGetDB from SMWSQLStore3Writers and extended test coverage
+* #244, #253, #256, #267, #268 Refactored and migrate `SMW_refreshData.php` to `rebuildData.php`
+* #248 Added support for the new MediaWiki i18n JSON system
+* #256 Improved data selection for DataRebuilder filters
+* #262 Improved SQLStore upgrading for Postgres
+* #270 Removed SQLStore3::getPropertyTables as static caller
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.md
new file mode 100644
index 00000000..ef5dd772
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-1.9.md
@@ -0,0 +1,142 @@
+# Semantic MediaWiki 1.9
+
+Released January 3rd, 2014.
+
+### Platform compatibility changes
+
+* Changed minimum PHP version from 5.2. to 5.3.2.
+* Changed minimum MediaWiki version from 1.17 to 1.19.
+* Full compatibility with MediaWiki 1.19, 1.20, 1.21, 1.22 and forward-compatibility with 1.23.
+* Changed minimum Validator version from 0.5 to 1.0.
+* Added beta support for PostgreSQL.
+
+### New features
+
+* SMW (and its dependencies) can now be installed via [Composer](https://getcomposer.org/)
+* Added maintenance script [rebuildPropertyStatistics.php](https://semantic-mediawiki.org/wiki/Help:RebuildPropertyStatistics.php) to rebuild the property statistics
+* (271864f) The property type String is now an alias for Text and has no more length restrictions
+* (38499a8) Special:Statistics now shows a "semantic statistics" version (only when using a recent version of MediaWiki)
+* (ed52df7) (Bug 50844) Special:Properties now provides a form to search user-defined properties
+* (a949f04) (Bug 33181) Add page [Special:Concepts](https://semantic-mediawiki.org/wiki/Help:Special:Concepts) that lists available concepts
+* (0c971f8) (Bug 46458) Extend smwinfo API module to provide additional information
+* (b8aea6c) (Bug 48840) Add a smw-admin right to enable restricted access to Special:SMWAdmin
+* (9714d04) (a33411f) Add new "browsebysubject" API module to fetch semantic data for a given subject
+
+### Enhancements
+
+* (Bug 36309) and (Bug 39019) Add +sep= as multiple value separator for #set and #subobject parser function
+* (6dd845e) (Bug 34477) Add cache information to concept pages
+* (Bug 34172) Add [individual CSS class](https://www.semantic-mediawiki.org/wiki/Help:Further_results) injection for further result links
+* (I2e509e) Improved efficiency of property statistics rebuilding script
+* (8bcee83) (Bug 44696) Fix XML output for AskApi
+* (bb35e8a) (Bug 47123) Aggregate numbers based on the label/group
+* (5cda766) (Bug 46930) SMWSQLStore3Writers::changeTitle only create redirects when appropriate
+conditions are met
+* (e4a5fb8) (Bug 31880) Formatting numbers in tables based based on a customizable typeId
+* (e4a2035) Modify SMW\RecurringEvents to use a subobject as datamodel to represent
+individual events within a page
+* (7d1e2ad) (Bug 34782) Add note parameter to #info parser function
+
+#### New configuration parameters
+
+* [$smwgEnabledSpecialPage](https://semantic-mediawiki.org/wiki/Help:$smwgEnabledSpecialPage)
+* [$smwgQueryProfiler](https://semantic-mediawiki.org/wiki/Help:$smwgQueryProfiler)
+* [$smwgShowHiddenCategories](https://semantic-mediawiki.org/wiki/Help:$smwgShowHiddenCategories)
+* [$smwgFactboxUseCache](https://semantic-mediawiki.org/wiki/Help:$smwgFactboxUseCache), [$smwgFactboxCacheRefreshOnPurge](https://semantic-mediawiki.org/wiki/Help:$smwgFactboxCacheRefreshOnPurge)
+* [$smwgPropertyZeroCountDisplay](https://semantic-mediawiki.org/wiki/Help:$smwgPropertyZeroCountDisplay), [$smwgPropertyLowUsageThreshold](https://semantic-mediawiki.org/wiki/Help:$smwgPropertyLowUsageThreshold)
+* [$smwgFixedProperties](https://semantic-mediawiki.org/wiki/Help:$smwgFixedProperties)
+* [$smwgAutoRefreshOnPageMove](https://semantic-mediawiki.org/wiki/Help:$smwgAutoRefreshOnPageMove), [$smwgAutoRefreshOnPurge](https://semantic-mediawiki.org/wiki/Help:$smwgAutoRefreshOnPurge)
+* [$smwgCacheType](https://semantic-mediawiki.org/wiki/Help:$smwgCacheType), [$smwgCacheUsage](https://semantic-mediawiki.org/wiki/Help:$smwgCacheUsage)
+
+### Bug fixes
+
+* The property statistics rebuilding is no longer done whenever you run update.php.
+* (Bug 42321) Fixed issue frequently causing notices in SQLStore3
+* (5fdbb83) Fix offset display in Special:Ask
+* (9113ad1) (Bug 47010) SMWInfoLink
+* (af0cbe0) Fix escaping issue on Special:Ask
+* (ba74804) Fix construction of SMWExpLiteral
+* (d16a103) (Bug 45053) Fix quantity display support in SMW\ListResultPrinter
+* (9b2b5c7) (Bug 44518) Do not display &#60;li&#62; elements for |format=list
+* (Bug 43932) Fix html tag support for non-list results in SMW\ListResultPrinter
+* (Bug 44275) Fix .data( 'sortkey' ) support in SMW\ListResultPrinter
+* (fcb7da9) (Bug 42324) fix SQlite support in sqlstore3
+* (3507f84) (Bug 21893) Fixed queries that use the like comparator for properties with a restricted
+set of values
+
+### Compatibility changes
+
+* Deleted pre SMW 1.5.1 entry point (includes/SMW_Settings.php), the main entry point is SemanticMediaWiki.php
+* (I17a3e0) Support for quantity export via API and JSON format
+* (50c5109) Removed old storage implementation SMWSQLStore2, superseded by SMWSQLStore3 in SMW 1.8
+* (I5db911) #set_recurring_event using subobjects (changes query behavior
+for recurring events; for more see the help page on [recurring events](https://semantic-mediawiki.org/wiki/Help:Recurring_events "Recurring events")
+
+### Deprecated classes
+
+If not noted otherwise, deprecated methods or classes will be removed in SMW 1.11.
+
+* (b4664be) smwfIsSemanticsProcessed was replaced by SMW\NamespaceExaminer
+* (3ba701f) smwfEncodeMessages was replaced by SMW\Highlighter, SMW\MessageFormatter
+* SMWParseData was replaced by a non-static SMW\ParserData class
+* SMWListResultPrinter, SMWResultPrinter, SMWSubobject, SMWSet
+* SMWFeedResultPrinter, SMWDISerializer
+* SMWDIString, SMWStringLengthException, SMWSetRecurringEvent
+
+### Added or changed classes
+
+* (eb764db) Add SMW\PropertyAnnotatorDecorator for handling individual "standard" properties
+* (f33fd12) Add SMW\ExtensionContext and \SMW\ContextAware
+* (40e7572) Renamed SMWDISerializer to \SMW\Serializers\QueryResultSerializer
+* (a0b08fe) Add SMW\Serializes\SemanticDataSerializer in order for SemanticData to be serializable
+* (02635a1) Replace SkinTemplateToolboxEnd hook with SMW\BaseTemplate
+* (ec5dd46) Add SMW\SimpleDependencyBuilder and SMW\SharedDependencyContainer as simple framework that
+allows for individual object factoring and dependency injection
+* (92b67bd) Add SMW\TableFormatter for the table query printer
+* (5a33d2d) Add SMW\CacheHandler to separate MediaWiki specific cache injection
+* (395b584) Add ResourceLoaderGetConfigVars to populate SMW related configuration details for JavaScript
+* (7c60e50) Add SMW\ApiResultPrinter to support query printers to use Ajax/WebApi interface for
+query result updates
+* (cb6c6ad) SMW\ResultPrinter class turn RequestContext aware
+
+The following classes and interfaces were re-factored and/or added in order to promote testability:
+
+* (e0f3f4d) Rename and re-factor \SMW\RefreshJob
+* (I3b41d4) Rename and re-factor \SMW\UpdateJob
+* (6d5a3c5) Add SMW\JobBase to enable dependency injection
+* (058c2fc) Add SMW\Setup to separate extension registration and initialization
+* (87b214f) Add SMW\Settings class to remove GLOBAL state and enable injection of individual configuration
+details during runtime
+* (5a82da8) Re-factor SMW\Factbox and add SMW\FactboxCache to minimize content parsing
+* (24cca37) Add SMW\Test\MockObjectBuilder to easily manage MW and SMW mock objects
+* (71dbba1) Add SMW\ObservableDispatcher to enable Observes to act as an observable subject itself
+* (dc28899) (18d17a5) Add SMW\StoreUpdater, SMW\UpdateDispatcherJob, and SMW\PropertyTypeComparator
+to separate responsibilities during the update
+* (6c06567) Add SMW\SQLStore\PropertyTableDefinitionBuilder to separate build definition
+* (2164a25) Add \SMW\ResultCollector interface to support cacheable results when executing Special:Statistics or Special:Properties
+* (c8a2f97) (Bug 51091) Rename and re-factor SMW\Api\Ask and SMW\Api\AskArgs
+
+### Removed features
+
+* (6f7625f) Remove Special:QueryCreator
+* (5a3f6ed) (Bug 50755) Remove MigrationJob/SMWMigrate
+* (f9cff2b) Remove smwfLoadExtensionMessages
+* (a957596) SMW\JsonResultPrinter remove obsolete serialization
+
+
+### Platform stability
+
+* Over 130 PHPUnit tests have been added
+* Over 10 QUnit tests have been added
+* The tests now [run on TravisCI](https://travis-ci.org/SemanticMediaWiki/SemanticMediaWiki)
+ * Compatibility is now ensured against all supported MediaWiki and PHP versions
+ * Compatibility is now ensured for all supported databases
+
+### Documentation
+
+The documentation bundled with the SMW source code has been updated. It can be found in the docs folder.
+
+### Extended translations
+
+As usual, translations have been extended thanks to the [Translatewiki.net project](https://translatewiki.net).
+In addition, the core strings (SMW properties and datatypes) for Slovak have been updated.
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.0.md
new file mode 100644
index 00000000..9a435394
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.0.md
@@ -0,0 +1,127 @@
+# Semantic MediaWiki 2.0
+
+Released August 4th, 2014.
+
+## Compatibility changes
+
+Semantic MediaWiki 2.0 is compatible with MediaWiki 1.19 up to MediaWiki 1.23, and possibly later
+versions. Support for both MediaWiki 1.23 and MediaWiki 1.24 was improved compared to SMW 1.9.
+
+PHP compatibility remains the same as in SMW 1.9: all versions from PHP 5.3.2 to PHP 5.6.x.
+
+
+For a full overview, see our [compatibility matrix](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/COMPATIBILITY.md).
+
+## Quality and stability improvements
+
+A great deal of effort has been put into ensuring both existing and new features work well.
+Not just at present, but also in future releases. And not just with MySQL and one version of
+MediaWiki, but on all platforms we support. This dedication to quality has resulted in many
+bugs being discovered and fixed, and makes future regressions much less likely.
+
+Continuous integration is now an integral part of the development process behind SMW. As of
+the 2.0 release, SMW has over 2300 automated tests, which cover two thirds of the codebase.
+These tests are run automatically for every change made to the code, on machines with different
+databases, different versions of PHP, different SPARQL stores and different versions of MediaWiki.
+
+## Semantic Versioning
+
+As of the 2.0 release, Semantic MediaWiki adheres to the [Semantic Versioning standard](http://semver.org/).
+This makes our version numbers more meaningful and makes it easier for administrators to determine
+if a new release is relevant to them.
+
+## Improved SPARQLStore support
+
+[Semantic MediaWiki 1.6](http://www.semantic-mediawiki.org/wiki/SMW_1.6#Synchronizing_SMW_with_RDF_stores)
+introduced support for data synchronization with RDF back-ends. SMW 2.0 makes this functionality a
+first class citizen through many enhancements and stability improvements.
+
+* New and full support for [Jena Fuseki](http://jena.apache.org/) 1.0
+* Enhanced and full support for [Virtuoso](https://github.com/openlink/virtuoso-opensource) 6.1
+* Enhanced support for [4store](https://github.com/garlik/4store) 1.1
+
+The [`smwgSparqlDatabase`](https://www.semantic-mediawiki.org/wiki/Help:$smwgSparqlDatabase) setting
+introduced in 1.6 has been deprecated in favour of
+[`$smwgSparqlDatabaseConnector`](https://www.semantic-mediawiki.org/wiki/Help:$smwgSparqlDatabaseConnector)
+(#342) to avoid arbitrary class assignments in `$smwgSparqlDatabase` (now only used to assign custom
+connectors).
+
+Unit and integration tests were given extra focus together with a continuous integration of
+[Jena Fuseki](http://jena.apache.org/) (1.0.2) (#337) and [Virtuoso opensource 6.1](https://github.com/openlink/virtuoso-opensource) (#394) to ensure that compatibility and functional
+parity are going hand in hand with the rest of SMW. (Unfortunately `4Store` currently does not run
+on the continuous integration platform, for details see [garlik#110](https://github.com/garlik/4store/issues/110)
+but tests have been run successfully with a local `4store` instance).
+
+At this moment, the only RDF store to be tested and to support [SPARQL 1.1](http://www.w3.org/TR/sparql11-query/)
+is `Jena Fuseki` therefore other stores may not support all `query features`. For details to
+the testing environment and its configuration, see the [README](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/SPARQLStore/README.md)
+file.
+
+## Improved subobject support
+
+Support for subobjects has been added to the RDF export. This new capability is used by the RDF
+store functionality to also synchronize subobjects. (#344)
+
+Subobjects now support sorting via the `@sortkey` annotation that stores an individual sortkey
+per subobject. (#225)
+
+## Notable bug fixes
+
+* #279 Fixed undefined index in `DataTypeRegistry::getDefaultDataItemTypeId`
+* #282 Output a message instead of an exception in `Special:WantedProperties` for unknown predefined properties
+* #308 Fixed caching issue in `DataRebuilder` for duplicate title objects
+* #312 Fixed fatal error in `CategoryResultPrinter` for when a mainlabel is hidden
+* #322 Fixed file names containing spaces or non-ASCII characters for for downloadable result formats (csv, excel)
+* #379 Modernized `dumpRDF.php` while deprecating the use of `SMW_dumpRDF.php` (bug 35679)
+* #425 Deprecated `SMW_setup.php` in favour of `setupStore.php`
+* #444 Fixed language namespace alias issue
+* #420 Extended `ContentParser` to mitigate issues caused by the 62856 bug in MW 1.24+
+* #405 Added a compatibility fix to mitigate issues caused by the `RefreshLinksJob` in MW 1.23+
+
+### SPARQLStore
+
+- #291 Fixed call to undefined method in `SPARQLStore`
+- #338 Fixed exception in `ResultParser` for an invalid datatype (bug 62218)
+- #385 Fixed '#' encoding for subobjects in `SMWExporter::findDataItemForExpElement` to enable `SPARQLStore` result display
+- #387 Fixed `SPARQLStore` namespace query support (e.g `[[:+]]` )
+- #415 Fixed `SPARQLStore` usage for `rebuildConceptCache.php` and `rebuildPropertyStatistics.php`
+- #460 Fixed `SPARQLStore` subobject sub query and pre-defined property query support
+
+## Behind the scenes
+
+SMW 2.0 continues to convert its classes to use PHP namespaces in order to separate responsibilities
+(#398, #404, #407, #409, #410, #411, #412, #416, #417, #418, #419, #421) and to be able to support
+[PSR-4](http://www.php-fig.org/psr/psr-4/) in future.
+
+* All `job` related classes of been moved to `SMW\MediaWiki\Jobs`
+* All `hook` related classes of been moved to `SMW\MediaWiki\Hooks`
+* All `api` related classes of been moved to `SMW\MediaWiki\Api`
+* All `SPARQLStore` related classes now reside in `SMW\SPARQLStore`
+* `SMWSparqlStore` and `SMWSparqlDatabase` where moved into the `SMW\SPARQLStore` namespace
+
+Other internal enhancements or changes include:
+
+* #278 Changed the `PropertyStatisticsTable` interface
+* #289 Added [`CONTRIBUTING.md`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/CONTRIBUTING.md) for better contributor guidance
+* #307 Added `removeDuplicates` option to `UpdateJob`
+* #310 Fixed autoloading for `QueryPrinterRegistryTestCase`
+* #311 Removed `MediaWikiTestCase` dependency
+* #315 Updated jquery.qTip2 from v2.0.0 to v2.2.0 (Mar 17 2014)
+* #332 Added the number of pages and percentage done to report messages when rebuilding selected pages
+* #366 Extended `Sql3StubSemanticData` to load suobjects on request and introduced a `__sob` datatype for subobjects
+* #382 Extended interface to support `format=count` information in `QueryResult`
+* #453 Added [`COMPATIBILITY.md`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/COMPATIBILITY.md) for better user guidance
+
+Deprecated classes or scripts:
+
+* `SMW_conceptCache.php`
+* `SMW_dumpRDF.php`
+* `SMW_refreshData.php`
+* `SMW_setup.php`
+* `SMWSparqlStore`
+* `SMWSparqlDatabase`
+* `SMWIResultPrinter`
+
+Removed classes or scripts:
+
+* `SMWParseData`
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.0.md
new file mode 100644
index 00000000..ccf1dc36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.0.md
@@ -0,0 +1,92 @@
+# Semantic MediaWiki 2.1
+
+Released on January 19th, 2015.
+
+## Highlights
+
+### Support for semantic queries in Special:Search
+
+This release adds support for semantic queries run directly from MediaWiki's standard search. You
+can enable this feature by setting `$wgSearchType` to ["SMWSearch"](https://semantic-mediawiki.org/wiki/Help:SMWSearch).
+The related configuration parameter [``$smwgFallbackSearchType``](https://semantic-mediawiki.org/wiki/Help:$smwgFallbackSearchType)
+allows specifying which search engine to fall back to in case "SMWSearch" returns no results. (#450, #496, #505)
+
+### SPARQLStore improvements
+
+The SPARQLStore now supports concept queries (#696) and regex like queries (`[[Url::~http://*query=*]] OR [[Url::~*ccc*]]`) for Page and URL values (#679).
+
+Notable performance improvements and many other fixes (can be found in the bug fix list) have been
+made to broaden the SPARQLStore support.
+
+### Enhanced platform support
+
+SMW has partially supported PostgreSQL for a long time. This new release brings SMW's PostgreSQL
+support to the same level as MySQL and SQLite, making it the third fully supported relational database.
+
+HHVM (HipHop Virtual Machine) 3.3 or above is now supported along with all previously supported PHP
+versions.
+
+Although earlier versions of SMW probably work with MediaWiki 1.24, this new release officially
+supports it.
+
+## New features
+
+* #546 Concepts can now be nested (bug 15316)
+* #537 Modernized [`Special:SearchByProperty`](https://semantic-mediawiki.org/wiki/Help:Special:Search_by_property) interface
+* #613 Added `subobject` parameter to the `BrowseBySubject` API module and imporved resolving of circular redirects
+* #620 Added `--page` as export option to the [`dumpRDF.php`](https://semantic-mediawiki.org/wiki/Help:DumpRDF.php) maintenance script
+* #633 Made ouput decoding for uri's human readable (bug 35452)
+* #643 Added `--runtime` option to [`rebuildData.php`](https://semantic-mediawiki.org/wiki/Help:RebuildData.php). It allows you to see how much time was spend and how much memory was used.
+* #659 Added [``$smwgEnabledEditPageHelp``](https://semantic-mediawiki.org/wiki/Help:$smwgEnabledEditPageHelp) option that enables showing a contextual help text on the edit page
+* #664 Enabled semicolon escaping for record-type values (`\;`) (bug T17732)
+* #672 Added `Special:Log` support for events enabled in `smwgLogEventTypes`
+
+## Bug fixes
+
+* #500 Fixed the `SPAPRQLStore` to return a `FalseCondition` instead of an exception for not supported data types (e.g `Geo`)
+* #520 Fixed the `SPAPRQLStore` query selection for subobjects used with a namespace condition
+* #543 Removes invalid category value links to `SearchByProperty` on `Special:Browse` (bug 33449)
+* #537 Fixed parameter encoding in `Special:SearchByProperty` for hyphens and spaces (bug 16150)
+* #554 Enhanced concept pages to provide time and date of the last update
+* #566 Fixed the `SPARQLStore` query result display for moved pages (a.k.a. "gost" pages)
+* #601 Fixed movability for predefined property pages
+* #615 Fixed data display inconsistency for pre-existing redirects
+* #617 Fixed circular `UpdateJob` caused by redirects
+* #619 Fixed exception in `dumpRDF.php` caused by resolving a subobject for a redirect
+* #622 Fixed cache id mismatch for redirects in `SQLStore`
+* #622 Fixed exception for when a `null` is returned by `ExportController::getSemanticData`
+* #627 Enhanced `SPARQLStore` XML result parser to support `Virtuoso` singelton response
+* #618 Fixed subobject disjunctive/conjunctive subquery handling
+* #628 Fixed named subobject encoding in the `Exporter` to support accented characters
+* #630 Fixed browse link generation for wikipages in `Special:Browse`
+* #638 Fixed the hard-coded upper bound for the offset option of an inline query by replacing it with configuration parameter [```$smwgQUpperbound```](https://semantic-mediawiki.org/wiki/Help:$smwgQUpperbound)
+* #638 Fixed `postgres` temporary table generation issue (bug 34855, #455, #462)
+* #640 Fixed `QueryProcessor` to allow query conditions to contain `=` (bug 32955)
+* #641 Removes service info links from the `Factbox`
+* #654 Fixed broken field detection in record-type caused by html encoded strings (bug T23926)
+* #656 Fixed `#REDIRECT` detection in MW 1.24+
+* #661 Fixed regex search `(~/!)` for page-type property values (bug T36665, T49073, T33151, T35854)
+* #674 Fixed regex search support for uri-type property values
+* #683 Fixed invalid `:smw-redi` marker when `#REDIRECT` is removed manually
+* #694 Fixed probable race condition for `SQLStore`(`postgres`) when creating temporary tables
+* #702 Fixed http header in `SPARQLStore` to be Sesame complaint
+
+## Internal changes
+
+* #486 Added continuous integration support for for `Jena Fuseki` 1.1.1
+* #487, #576, #600 Added an internal cache to improve `SPARQLStore` redirect and export lookup performance
+* #512, #521 Added benchmark tests for different components such as job-queue, maintenance script, queries etc.
+* #523 Disabled the Factbox display for a `delete action` and re-enable the Factbox for an undeleted page
+* #532 Added `UrlEncoder` to recognize all special characters when creating a manual link to `Special:Browse`
+* #534 Added a value hash to `SQLStore::fetchSemanticData` to ensure that only distinct values are displayed
+* #557 Added `SMW::Store::BeforeQueryResultLookupComplete` and `SMW::Store::AfterQueryResultLookupComplete` hook
+* #590, #596 Added `CompoundConditionBuilder` and `ConditionBuilderStrategyFinder` to the `SPARQLStore`
+* #645 Added `RedirectInfoStore` to isolate access to redirect information and cache info requests
+* #646 Improved error message handling for the `_num` data type
+* #665 Replaced arbitrary DB access in `Store::updateData` with `PageUpdater::doPurgeParserCache`
+* #667 Added `Database::beginTransaction` and `Database::commitTransaction`
+* #670 Added `SMW::SQLStore::BeforeChangeTitleComplete` hook
+* #673 Extended `DataValueFactory` to ignore `$wgCapitalLinks` settings for the property namespace
+* #678 Added `PropertyRegistry` to remove global state from `DIProperty`
+* #668 Changed `SQLStore` `iw` table field specification from `VARCHAR(32) binary` to `VARBINARY(32)`
+* #707 Added continuous integration support for `openrdf-sesame` 2.7.14
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.1.md
new file mode 100644
index 00000000..130daeb4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.1.md
@@ -0,0 +1,9 @@
+# Semantic MediaWiki 2.1.1
+
+Released on March 2nd, 2015.
+
+## Bug fixes
+
+* #861 Fixed owl property export declaration
+* #863 Fixed missing interwiki encoding for the RDF export
+* #864 Fixed empty searchlabel raw wikitext display for a QueryResultPrinter with limit=0
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.2.md
new file mode 100644
index 00000000..e62e56c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.2.md
@@ -0,0 +1,9 @@
+# Semantic MediaWiki 2.1.2
+
+Released on March 28th, 2015.
+
+## Bug fixes
+
+* #882 Fixed exception in `SMWExportController` caused by an empty reference
+* #885 Fixed pre tag rendering in template output
+* #896 Fixed empty string for boolean x-format output
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.3.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.3.md
new file mode 100644
index 00000000..26dd2a96
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.1.3.md
@@ -0,0 +1,7 @@
+# Semantic MediaWiki 2.1.3
+
+Released on March 30th, 2015.
+
+## Bug fix
+
+* #946 Fixed variable name regression for the "list" format introduced with version 2.1.2
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.0.md
new file mode 100644
index 00000000..ba5ee37c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.0.md
@@ -0,0 +1,64 @@
+# Semantic MediaWiki 2.2
+
+Released on May 9th, 2015.
+
+## New features
+
+* #770 Added the `--no-cache` option to `rebuildData.php` and the `--debug` option to `rebuildData.php` and `rebuildConceptCache.php` (refs #749, #766)
+* #756 Added template support to the `#set` parser function
+* #783 Added support for `wgCategoryCollation` setting in `CategoryResultPrinter` (#699, T40853)
+* #829 Added the `--report-runtime` option to `rebuildConceptCache.php`
+* #886 Extended rules on how an `ImportValue` need to contain a valid type definition
+* #891 Enforced strict type declaration for properties with import references (type of the imported reference takes precedence over any user-defined type declaration)
+* #892 Added support for `<swivt:file>` in order for a RDF export to point to a "real" file resource
+* #893 Added information about "improper value for" to the statistics overview and as `API` parameter (`&errorcount`)
+* #913 Fixed exception for usage of `Has subobject` as free annotation
+* #914 Added restriction for "manual" annotation use to some pre-defined properties (e.g. `Has subobject`, `Has query`)
+* #974 Added `named args` as parameter to `CategoryResultPrinter`
+* #988 Added `sep` as parameter to the `TableResultPrinter` to define a separator for cell values
+
+## Enhancements
+
+* #958 Replaced defunct SMWRegsitry wiki registration on Special:SMWAdmin with a link to WikiApiary
+* #976 Reduced the number of pages on which SMW JavaScript and CSS is loaded
+* #994, #995 Made small performance improvements
+
+## Bug fixes
+
+* #556 Fixed Missing parentheses in `SQL` for disjunctions when temporary tables are not used by the `SQLStore`
+* #764 Fixed DB error when a `#ask` query contains `order=random` for a `sqlite` or `postgres` DB platform (disabled `smwgQRandSortingSupport` for `postgres`)
+* #860 Fixed escape character usage in `SPARQLStore`, `SQLStore`
+* #860 Fixed handling of an empty result send by the `SPARQLStore` Sesame connector
+* #861 Fixed owl property export declaration
+* #863 Fixed missing interwiki encoding for the RDF export
+* #864 Fixed empty searchlabel raw wikitext display for a `QueryResultPrinter` with `limit=0`
+* #869 Fixed data update when moving a page to a non-enabled semantic namespace
+* #870 Fixed `#set` where an error from a previous parse call could have affected succeeding `#set` error messages
+* #882 Fixed exception in `SMWExportController` caused by an empty property reference
+* #884 Fixed fetching of import vocabularies to be language independent
+* #887 Fixed `pre` element rendering for the template "furtherresult" output link
+* #896 Fixed empty caption for `false` when using the`#x` format option
+* #928 Fixed exception in `SMWExportController` for use of `&stats` option
+* #945 Fixed nesting of self-referenced concepts in a concept query
+* #974 Fixed inconsistency when using `headers=plain` in `CategoryResultPrinter` also fixed the `__NOTOC__` hack
+
+## Internal changes
+
+* #373 Update `jquery.jstorage.js` (0.3.2 => 0.4.12)
+* #494 Changes to the `SQLStore\QueryEngine` interface
+* #711 Fetching annotations made by an `#ask` transcluded template
+* #725 Moved psr-4 complaint classes into the top level 'src' folder
+* #740 Added `serialization/serialization:~3.2` component dependency
+* #771 Added `doctrine/dbal:~2.5` component dependency
+* #772 Added `onoi/message-reporter:~1.0` component dependency
+* #777 Moved all concept related code into a separate `ConceptCache` class
+* #831 Added `onoi/cache:~1.0` component dependency
+* #884, #983 Added `ControlledVocabularyImportContentFetcher`, `ImportValueParser` to isolate import vocabulary parsing from the `ImportValue` object
+* #883 Added `CircularReferenceGuard` to monitor possible self/circular references in ask templates
+* #914 Added `DIProperty::isUnrestrictedForUse`
+* #924 Added `onoi/event-dispatcher:~1.0` component dependency
+* #929 Added serialization to `ExpElement` objects
+* #950 Renamed `PropertyTypeDiffFinder` to `PropertySpecDiffFinder`
+* #951 Extended `UpdateDispatcherJob` to create `UpdateJob` chunks to avoid memory exhaustion
+* #959 Added `ExpDataSerializer` and `ExpDataDeserializer`
+* #984 Moved remaining part of `SMWSQLStore3QueryEngine` to `SMW\SQLStore\QueryEngine\QueryEngine`
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.1.md
new file mode 100644
index 00000000..e44ba851
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.1.md
@@ -0,0 +1,12 @@
+# Semantic MediaWiki 2.2.1
+
+Released on June 2nd, 2015.
+
+## Bug fixes
+
+* Fixed "Notice: Undefined variable: dataItem" in `QueryEngine`
+* #1031 CategoryResultPrinter to recognize offset for further results
+* #1033 Fixed assumption that always an array is sent to `JobBase` for booleans
+* #1038 Fixed Fatal error: Call to undefined method `SMWDIError::getString`
+* #1046 Fixed RuntimeException in `UndeclaredPropertyListLookup` when a DB prefix is used
+* #1051 Fixed call to `DIWikiPage::getText` in `ConceptDescriptionInterpreter`
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.2.md
new file mode 100644
index 00000000..c6956d2f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.2.md
@@ -0,0 +1,8 @@
+# Semantic MediaWiki 2.2.2
+
+Released on July 7th, 2015.
+
+## Bug fixes
+
+* #1067 Fixed return value of the `#set` parser
+* #1081 Fixed mismatch of `owl:Class` for categories when used in connection with a vocabulary import
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.3.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.3.md
new file mode 100644
index 00000000..3e16a10f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.2.3.md
@@ -0,0 +1,7 @@
+# Semantic MediaWiki 2.2.3
+
+Released on October 11th, 2015.
+
+## Bug fixes
+
+* #1201 Fixed php-serialization issue for `SemanticData`
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.0.md
new file mode 100644
index 00000000..91afe686
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.0.md
@@ -0,0 +1,133 @@
+# Semantic MediaWiki 2.3
+
+Released on October 25th, 2015.
+
+
+## Highlight: Improved SPARQLStore support
+
+SMWs SPARQLStore has now reached full feature parity with the SQLStore. On top of that, various performance improvements where made to the SPARQLStore.
+
+The `$GLOBALS['smwgSparqlQFeatures']` configuration setting now supports these additional values:
+
+* #1001 `SMW_SPARQL_QF_REDI`: enable property/value redirects support in queries
+* #1003 `SMW_SPARQL_QF_SUBP`: enable subproperty hierarchy support
+* #1012 `SMW_SPARQL_QF_SUBC`: enable subcategory hierarchy support
+
+If your TDB back-end does not support SPARQL 1.1, this setting needs to be set to `SMW_SPARQL_QF_NONE`.
+
+* #1152 Added preference for use of canonical identifiers to support language agnostic category/property statements, (use `$GLOBALS['smwgExportBCNonCanonicalFormUse'] = true` to keep backwards compatibility until 3.x)
+* #1158 Added basic support for `_geo` queries to the `SPARQLStore`
+* #1159 Added limitation of the `aux` property usage in the Exporter (use `$GLOBALS['smwgExportBCAuxiliaryUse'] = true;` to keep backwards compatibility until 3.x)
+
+## New features and enhancements
+
+* #1042 Added progress indicator to `rebuildData.php`
+* #1047 Extended context help displayed on `Special:Types` and subsequent type pages
+* #1049 Improved MobileFrontend support
+* #1053 Added a `CSS` rule to visually distinguish subobject links from "normal" links
+* #1063 Added `$GLOBALS['smwgValueLookupCacheType']` to improve DB lookup performance though the use of a responsive cache layer (such as `redis`) and buffer repeated requests either from the API or page view to the back-end.
+* #1066, #1075 It is now possible to use extra double colons in annotations. For instance `[[DOI::10.1002/123::abc]]` or `[[Foo:::123]]`
+* #1097 Predefined property aliases are redirected to the base property
+* #1107 The template support of #set now includes an automatically added `last-element` parameter
+* #1106 Added `--skip-properties` flag to `rebuildData.php`
+* #1106 `rebuildData.php` now first removes items marked for deletion
+* #1129 Extended `~*` search pattern for `_ema` and `_tel` to allow for searches like `[[Has telephone number::~*0123*]]` and `[[Has email::~*123.org]]`
+* #1147 The category result format now supports `columns=0`, which results in automatic column count selection
+* #1171 Added SQL EXPLAIN output to the debug result format
+* #1172 Added `@category` as parameter with a fixed assignment (`_INST`) to `#subobject`
+* #1178 Added `~` and `!~` comparator support for values of type date
+
+## New experimental features
+
+These features are disabled by default and can be turned on using configuration. Additional logging
+happens for these features until they mature from being an experimental feature in a future release.
+
+* #1035, #1063 Added `CachedValueLookupStore` as post-cached layer to improve DB read access (`$GLOBALS['smwgValueLookupCacheType']`, $GLOBALS['smwgValueLookupCacheLifetime'])
+* #1116 Added $GLOBALS['smwgValueLookupFeatures'] setting to fain grain the cache access level, default is set to `SMW_VL_SD | SMW_VL_PL | SMW_VL_PV | SMW_VL_PS;`
+* #1117 Added `EmbeddedQueryDependencyLinksStore` to track query dependencies and update altered queries using `ParserCachePurgeJob` for when `$GLOBALS['smwgEnabledQueryDependencyLinksStore']` is enabled
+* #1135 Added `$GLOBALS['smwgPropertyDependencyDetectionBlacklist']` to exclude properties from dependency detection
+* #1141 Added detection of property and category hierarchy dependency in `EmbeddedQueryDependencyLinksStore`
+
+## Bug fixes
+
+* #400 (#1222) Fixed `RuntimeException` in `SQLStore` caused by a DI type mismatch during a lookup operation
+* #682 Fixed id mismatch in `SQLStore`
+* #1005 Fixed syntax error in `SQLStore`(`SQLite`) for temporary tables on disjunctive category/subcategory queries
+* #1033 Fixed PHP notice in `JobBase` for non-array parameters
+* #1038 Fixed Fatal error: Call to undefined method `SMWDIError::getString`
+* #1046 Fixed RuntimeException in `UndeclaredPropertyListLookup` for when a DB prefix is used
+* #1051 Fixed call to undefined method in `ConceptDescriptionInterpreter` in `SQLStore`
+* #1054 Fixed behavior for `#REDIRECT` to create the same data reference as `Special:MovePage`
+* #1059 Fixed usage of `[[Has page::~*a*||~*A*]]` for `SPARQLStore` when `Has page` is declared as page type
+* #1060 Fixed usage of `(a OR b) AND (c OR d)` as query pattern for the `SQLStore`
+* #1067 Fixed return value of the `#set` parser
+* #1074 Fixed duplicated error message for a `_dat` DataValue
+* #1081 Fixed mismatch of `owl:Class` for categories when used in connection with a vocabulary import
+* #1090 Fixed error on Special:Ask when using a format provided by Semantic Maps
+* #1126 Fixed silent annotations added by the `Factbox` when content contains `[[ ... ]]`
+* #1120 Fixed resource loading issue on Windows when using `$wgResourceLoaderDebug=true`
+* #233 Fixed disabling of `$GLOBALS['wgFooterIcons']['poweredby']['semanticmediawiki']`
+* #1137 Fixed re-setting of `smw-admin` user group permission to its default
+* #1146 Fixed #set rendering of template supported output (refs #1067)
+* #1096 Fixed inverse prefix for predefined properties that caused misinterpret `Concept` queries
+* #1166 Fixed context awareness of `ParserAfterTidy` in connection with the `purge` action
+* #1165 Fixed "duplicate key value violates unique constraint" for PostgreSQL on conjunctive and disjunctive queries
+* #1182 Fixed further link to use the format parameter as specified by `#ask`
+* #1207 Fixed usage of the `!~` comparator for properties that have a limited set of allowed values
+
+### Improved handling of removed entities in SQLStore
+
+In previous releases it could happen that deleted entities (subject, property) reappeared in queries even though they have been removed. This release introduces several changes to eliminate some of the issues identified.
+
+* #1100 introduced a deletion marker on entities that got deleted, making them no longer available to queries or special page display.
+* #1127 Added `--shallow-update` to `rebuildData.php`, to only parse those entities that have a different last modified timestamp compared to that of the last revision. This enables to run `rebuildData.php` updates on deleted, redirects, and other out of sync entities.
+* Solved #701 where an unconditional namespace query `[[Help:+]]` would display deleted subjects (in case those subjects were deleted)
+* #1105 Added filter to mark deleted redirect targets with `SMW_SQL3_SMWDELETEIW`
+* #1112 Added filter to mark outdated subobjects with `SMW_SQL3_SMWDELETEIW`
+* #1151 Added removal of unmatched "ghost" pages in the ID_TABLE
+
+## Internal changes
+
+* #1018 Added `PropertyTableRowDiffer` to simplify computation of `SemanticData` diff's (relates to #682)
+* #1039 Added `SemanticData::getLastModified`
+* #1041 Added `ByIdDataRebuildDispatcher` to isolate `SMWSQLStore3SetupHandlers::refreshData`
+* #1071 Added `SMW::SQLStore::AddCustomFixedPropertyTables` hook to simplify registration of fixed property tables by extensions
+* #1068 Added setting to support recursive annotation for selected result formats (refs #1055, #711)
+* #1086 Changed redirect update logic to accommodate the manual #REDIRECT (refs #895, #1054)
+* Added `SMW::Browse::AfterIncomingPropertiesLookupComplete` which allows to extend the incoming properties display for `Special:Browse`
+* Added `SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate` which allows to replace the further result incoming link in `Special:Browse`
+* #1078 Renamed `ParserParameterFormatter` to `ParserParameterProcessor` and `ParameterFormatterFactory` to `ParameterProcessorFactory`
+* #1102 Added `onoi/http-request:~1.0` dependency
+* Decrease chunk size in `UpdateDispatcherJob` (refs #951)
+* #1110 Extended `TurtleTriplesBuilder` to split larger turtle sets into chunks
+* #1111 Added support for the atomic DB transaction mode to improve the rollback process in case of a DB transaction failure
+* #1108 Added `CompositePropertyTableDiffIterator` which for the added `'SMW::SQLStore::AfterDataUpdateComplete'` returns ids that have been updated only (as diff of the update)
+* #1119 Added `RequestOptionsProcessor`
+* #1130 Added `DeferredRequestDispatchManager` to decouple jobs during an update
+* #1133 Fixed MW 1.25/1.26 API tests
+* #1145 Added `onoi/callback-container:~1.0` and removes all custom DIC code from SMW-core
+* (964155) Added removal of whitespace for `DIBlob` values (" Foo " becomes "Foo")
+* #1149 Added `InMemoryPoolCache` to improve performance for the `SPARQLStore` during turtle serialization
+
+## Contributors
+
+**Code contributors**
+
+* MWJames
+* Jeroen De Dauw
+* Karsten Hoffmeyer (kghbln)
+* Felipe de Jong (jongfeli)
+* Vitaliy Filippov (vitalif)
+* paladox
+* Amir E. Aharoni
+* Joel K. Pettersson
+* umherirrender
+* Kunal Mehta (legoktm)
+* TranslateWiki.net
+
+**Other contributors**
+
+* yoonghm
+* cicalese
+* bogota
+* plegault3397
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.1.md
new file mode 100644
index 00000000..b1698e26
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.3.1.md
@@ -0,0 +1,12 @@
+# Semantic MediaWiki 2.3.1
+
+Released on January 4th, 2016.
+
+## Bug fixes
+
+* #1248 Fixed misplaced replacement of `_` in the `ImportValueParser`
+* #1252 Added [`$smwgEnabledInTextAnnotationParserStrictMode`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEnabledInTextAnnotationParserStrictMode) allowing to reenable (by disabling strict mode which by default is enabled) multi-property assignments in `[[ :: ]]`
+* #1256 Added creation of object ID's that are not yet available in `EmbeddedQueryDependencyLinksStore`
+* #1268 Fixed 1.26/1.27 API/RawMode MediaWiki output changes
+* #1255 Fixed output regression (T121761) in connection with `#ask` and generated template HTML output
+* #1321 Added [`$smwgSparqlRepositoryConnectorForcedHttpVersion`](https://semantic-mediawiki.org/wiki/Help:$smwgSparqlRepositoryConnectorForcedHttpVersion) setting to set a specific HTTP version in case of a observed cURL issue (#1306)
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.0.md
new file mode 100644
index 00000000..abcfe992
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.0.md
@@ -0,0 +1,200 @@
+# Semantic MediaWiki 2.4
+
+Released on July 9th, 2016.
+
+## Highlights
+
+### Support for multiple languages
+
+Added support for [multilingual content](https://www.semantic-mediawiki.org/wiki/Localization_and_multilingual_content).
+This includes the introduction of the [monolongual text datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Monolingual_text),
+a new [special property to describe properties](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_property_description)
+and the new [Semantic Interlanguage Links extension](https://www.semantic-mediawiki.org/wiki/Extension:Semantic_Interlanguage_Links).
+
+### Pattern based constraints
+
+Added support for constraint specification using regular expressions (#1417). The use of `regular
+expressions` and thus the `Allows pattern` property to express a constraint assignment is restricted
+to users with the [`smw-patternedit`](https://www.semantic-mediawiki.org/wiki/Help:Permissions_and_user_rights) right.
+
+### Positional units
+
+It is now possible to specify which position a [custom unit](https://www.semantic-mediawiki.org/wiki/Help:Custom_units)
+should have in [Corresponds to](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Corresponds_to) annotations.
+This means you can specify `[[Corresponds to::€ 1]]` instead of `[[Corresponds to::1 €]]`. You can find a
+[small example](http://sandbox.semantic-mediawiki.org/wiki/Issue/1329_(Positional_unit_preference)) on the Sandbox.
+
+### Display precision
+
+You can now specify the precision used for display of numeric properties (i.e. those of type Number,
+Quantity, Temperature). This is done using the
+[Display precision of](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Display_precision_of)
+property. You can override this display precision per `#ask` query, by using `-p<digit>`.
+You can [view the examples](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1335).
+
+### Enhanced date and time formatting
+
+Extended [date and time formatting](https://www.semantic-mediawiki.org/wiki/Help:Type_Date)
+by supporting PHP's `DateTime` format rules.
+
+### Display Title storage
+
+MediaWiki's `{{DISPLAYTITLE:...}}` can now be stored as the
+[Display title of](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Display_title_of)
+special property, so it can be used in queries.
+
+
+## Compatibility changes
+
+Support was added for MediaWiki 1.26 and MediaWiki 1.27. SMW 2.3 has know issues with these versions
+of MediaWiki, so you are highly encouraged to upgrade SMW if you plan to use one of them. While SMW
+2.3 already had beta support for PHP 7, this release fully supports it.
+
+This release does not drop support for anything. It is however the last release to support PHP older
+than 5.5 and MediaWiki older than 1.25.
+
+For more information, see the [compatibility overview](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/COMPATIBILITY.md).
+
+
+## New features and enhancements
+
+* [#498](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/498) Extended `rebuildData.php` to remove outdated entity references (see `PropertyTableIdReferenceDisposer`)
+* [#1243](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1243) Made failed queries discoverable
+* [#1246](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1246) Added support for `~`/`!~` on single value queries (example: `{{#ask: [[~Foo/*]] }}`)
+* [#1267](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1267) Added the `browseByProperty` API module to fetch a property list or individual properties via the WebAPI
+* [#1268](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1268) Restored compliance with MediaWiki's 1.26/1.27 WebAPI interface to ensure continued support for the `ask` and `askargs` output serialization
+* [#1257](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1257) Changed import of recursive annotations (#1068) from the format to a query level using the `import-annotation` parameter
+* [#1291](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1291) Added support for range queries such as `[[>AAA]] [[<AAD]]`
+* [#1293](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1293) Added `_ERRC` and `_ERRT` as pre-defined properties to aid error analysis
+* [#1299](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1299) Added dot named identifier restriction for subobject names containing a dot (`fooba.bar` reserved for extensions)
+* [#1313](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1313) Added usage count information to property pages
+* [#1321](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1321) Added [`$smwgSparqlRepositoryConnectorForcedHttpVersion`](https://semantic-mediawiki.org/wiki/Help:$smwgSparqlRepositoryConnectorForcedHttpVersion) setting to force a specific HTTP version in case of a #1306 cURL issue
+* [#1290](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1290) Added support for properties and `prinrequests` to be forwarded to a redirect target if one exists
+* [#1329](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1329) Added positional preference for units when declared in `Corresponds to` (Â¥ 500 vs 500 JPY)
+* [#1350](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1350) Enlarged input field on special page "Browse"
+* [#1335](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1335) Added possibility to specify a display precision for a numeric `datatype` by either denoting a [`Display precision of`](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Display_precision_of) or using `-p<number of digits>` as `#ask` printout option
+* [#1344](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1344) Added `MonolingualTextValue` and `LanguageCodeValue`
+* [#1361](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1361) Added `--with-maintenance-log` option to `rebuildData.php`, `rebuildPropertyStatistics.php`, and `rebuildConceptCache.php`
+* [#1381](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1381) Added localizable context help for properties using the predefined property `Has property description` (which is specified as `MonolingualText` type)
+* [#1389](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1389) Added free date/time formatting support using the `-F[ ... ]` option
+* [#1391](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1391) Made subobject directly browsable from/in the Factbox
+* [#1396](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1396) Explicitly annotated years now have an `AC/CE` era indication
+* [#1397](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1397) Added support for microseconds in `DITime`
+* [#1401](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1401) Added support for parsing `年/月/日` date format in `DITime`
+* [#1407](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1407) Added quick result download links to `Special:Ask`
+* [#1410](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1410) Added support for `{{DISPLAYTITLE:title}}` caption using the [`Display title of`](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Display_title_of) property
+* [#1417](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1417) Added [`Allows pattern`](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_pattern) property to define a value constraint using regular expressions and the required `smw-patternedit`right to add those expressions
+* [#1433](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1433) Added `--ignore-exceptions` and `exception-log` options to `rebuildData.php` while option `-v` is showing additional information about the processed entities
+* [#1440](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1440) Added various changes to accommodate MW 1.27
+* [#1463](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1463) Added support for the [`Has uniqueness constraint`](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_uniqueness_constraint) property trait
+* [#1474](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1474) Added a search link for zero properties to the `Special:Properties`
+* [#1483](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1483) Added statistics about [outdated entities](https://www.semantic-mediawiki.org/wiki/Help:Outdated_entities) to the `Special:Statistics`
+* [#1542](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1542) Extended the query parser to support conditions with object values that contain `=` (#640)
+* [#1545](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1545) Added `#LOCL` as `TimeValue` output format
+* [#1570](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1570) Added ["Object ID disposal"](https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal) `to Special:SMWAdmin`
+* [#1572](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1572) Extended the query parser to support property chaining on subtypes
+* [#1580](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1580) Added `#LOCL` as `BooleanValue` output format
+* [#1591](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1591) Added `#LOCL` as `NumberValue` output format
+* [#1626](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1626) Added `$GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionlist']` to monitor affiliate properties required for initiating a query dependency update
+
+
+## Bug fixes
+
+* [#541](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/541) Fixed duplicate column when "further results ..." are redirected to `Special:Ask`
+* [#753](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/753) Fixed number parsing of non-zero lead decimal numbers (.1 vs 0.1) / (T40476)
+* [#1244](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1244) Find redirect for a property when specified as a record field (in `PropertyListValue`)
+* [#1248](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1248) Fixed misplaced replacement of `_` in the `ImportValueParser`
+* [#1270](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1270) Fixed printout display of inverse properties
+* [#1272](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1272) Fixed serialization of `_rec` type in the `QueryResultSerializer`
+* [#1275](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1275) Fixed export of record type data when embedded in a subobject
+* [#1286](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1286) Fixed support for sorting by category
+* [#1287](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1287) Fixed exception for when `$smwgFixedProperties` contains property keys with spaces
+* [#1289](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1289) Fixed redirect statement for resources matched to an import vocabulary (`SPARQL` query)
+* [#1301](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1301) Fixed `count` query result discrepancy (to exclude redirect and deleted entities)
+* [#1314](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1314) Fixed hidden annotation copy of `[[ :: ]]` text values when embedded in query results
+* [#1318](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1318) Fixed possible `null` object in `AskParserFunction` when creating a `QueryProfile`
+* [#1357](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1357) Fixed `|+align=...` usage for `format=table`
+* [#1358](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1358) Fixed recognition of multi-byte boolean value
+* [#1348](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1348) Fixed single year detection in `TimeValue`
+* [#1414](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1414) Fixed exception caused by a missing message page on a `Service link` annotation
+* [#1449](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1449) Fixed mapping of imported URI to an internal `DataItem`
+* [#1450](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1450) Fixed export of concept
+* [#1453](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1453) Fixed off/on display in text value
+* [#1459](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1459) Fixed column display regression in `CategoryResultPrinter` for subobjects
+* [#1466](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1466) Fixed remote resource path detection that appeared in connection with a non-default extension setup
+* [#1473](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1473) Fixed exception caused by `ParameterInput` due to "HTML attribute value can not contain a list of values"
+* [#1477](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1477) Fixed query result from `SPARQLStore` to filter redirects natively
+* [#1489](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1489) Fixed fatal error in `RdfResultPrinter` due to namespace mismatch
+* [#1496](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1496) Fixed concept handling for `postgres`
+* [#1513](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1513) Fixed rendering of text properties containing wikitext lists
+* [#1526](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1526) Fixed `_` handling for value strings submitted to the `Special:SearchByProperty`
+* [#1550](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1550) Fixed `SPARQLStore` `XML` response parsing for strings that contain UTF-8 characters
+* [#1562](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1562) Fixed fatal error in `FeedResultPrinter` due to usage of an interwiki assignment
+* [#1568](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1568) Fixed usage of invalid characters/tags in property name
+* [#1594](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1594) Fixed handling of numbers with scientific notation in `Special:SearchByProperty`
+* [#1597](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1597) Fixed possible ID collision in `DependencyLinksTableUpdater`
+* [#1598](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1598) Fixed content language setting for `InfoLinks`
+* [#1589](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1589) Fixed display precision constraint during condition building
+* [#1608](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1608) Fixed that a `#info` without a message will create an empty tooltip or when used as `<info />` causing a failure
+* [#1610](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1610) Fixed a potential exception in the `postgres` implementation when creating temporary tables
+* [#1628](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1628) Fixed exception when `NumberValue` tries to use a `NULL` as numeric value.
+* [#1638](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1638) Fixed possible invalid property in case the label contains `[`
+
+
+## Internal changes
+
+* [#1235](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1235) Improve query performance in `PropertyUsageListLookup`
+* [#1023](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1023) Split the `DocumentationParserFunction`
+* [#1264](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1264) Removed `pg_unescape_bytea` special handling for `postgres` in the `ResultPrinter`
+* [#1276](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1276) Extended `QueryResultSerializer` (relevant for the API output) to export the raw output of a time related value
+* [#1281](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1281) Extended `QueryResultSerializer` to export the internal property key
+* [#1291](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1291) Added `DescriptionProcessor` to isolate code path from the `SMWQueryParser`
+* [#1319](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1319) Switched from Sesame 2.7.14 to 2.8.7 in the CI environment
+* [#1382](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1382) Added `DispatchingDataValueFormatter` and `ValueFormatterRegistry`
+* [#1385](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1385) Added `StringValueFormatter` and `CodeStringValueFormatter`
+* [#1388](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1388) Added `TimeValueFormatter`
+* [#1421](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1421) Added `DeferredDependencyLinksUpdater` to avoid violations reported by `TransactionProfiler` in MW 1.26+
+* [#1417](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1417) Added `PermissionPthValidator` together with new the `smwcurator` group and `smw-patternedit` right
+* [#1435](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1435) Added `DeferredCallableUpdate` (together with `$GLOBALS['smwgEnabledDeferredUpdate']`) to support MW's `DeferrableUpdate` interface (i.e. to support queuing DB related transactions)
+* [#1445](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1445) Added `userlang` as `ParserOutput` option
+* [#1451](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1451) Added `ExtraneousLanguage` interface
+* [#1460](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1460) Requires PHP extension mbstring in `composer.json`
+* [#1482](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1482) Added the `SMW::FileUpload::BeforeUpdate` hook
+* [#1512](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1512) Fixed test suite to support PHP7
+* [#1575](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1575) Removed `smw_subobject` from `PropertyListLookup` query
+* [#1591](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1591) Added `IntlNumberFormatter`
+* [#1593](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1593) Added `NumberValueFormatter`
+* [#1601](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1601) Added `InfoLinksProvider`
+* [#1606](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1606) Disabled DB transactions in `QueryEngine` to avoid potential issues when creating temporary tables
+* [#1626](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1626) Added `EntityIdListRelevanceDetectionFilter` and `TemporaryEntityListAccumulator` in #1627
+* [#1635](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1635) Switched from Fuseki 1.1.0 to 2.4.0 in the CI environment
+* Most updates now occur in [deferred mode](https://www.semantic-mediawiki.org/wiki/Deferred_updates)
+to conform with [T92357](https://phabricator.wikimedia.org/T92357) Extensions that wish to extend
+data objects are encouraged to use hooks and avoid conflicts when updates are queued.
+
+
+## Contributors
+
+* James Hong Kong
+* Jeroen De Dauw
+* Karsten Hoffmeyer
+* Felipe de Jong
+* Florian Schmidt
+* Niklas Laxström
+* Ahmad Gharbeia
+* Stephan Gambke
+* Amir E. Aharoni
+* Siebrand Mazeland
+* Cindy Cicalese
+* Hangya
+* Sébastien Beyou
+* Aaron Schulz
+* Jaider Andrade Ferreira
+* Kunal Mehta
+* Ori Livneh
+* Peter Grassberger
+* Reedy
+* Vitaliy Filippov
+* Wolfgang Fahl
+* Alexander Gesinn
+* TranslateWiki.net translators
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.1.md
new file mode 100644
index 00000000..7012cd49
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.1.md
@@ -0,0 +1,14 @@
+# Semantic MediaWiki 2.4.1
+
+Released on July 12th, 2016.
+
+## Bug fixes
+
+* #1328 Fixed a "Undefined index: language" error in `#smwdoc` parser function
+* #1713 Fixed a "Segmentation fault" when `QueryResultDependencyListResolver` tries to resolve a category/property hierarchy with a circular reference
+* #1715 Fixed decoding of a single quotation mark in `DisplayTitlePropertyAnnotator`
+* #1724 Fixed a possible `InvalidArgumentException` in connection with `SMW_DV_PVUC` by updating the `CachedPropertyValuesPrefetcher` version number
+* #1726 Allows `QueryDependencyLinksStore` to execute `getDependencyListByLateRetrieval` even in cases of an intial empty list
+* #1727 Fixed an issue when property names contain `<` or `>` symbols
+* #1728 Fixed fatal error in `Special:SearchByProperty` on when the property name contains invalid characters
+* #1731 Fixed possible error in the `SkinAfterContent` hook when a null object is used
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.2.md
new file mode 100644
index 00000000..c4e38615
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.2.md
@@ -0,0 +1,13 @@
+# Semantic MediaWiki 2.4.2
+
+Released on November 13th, 2016.
+
+## Bug fixes
+
+* #1829 Only have the `DisplayTitlePropertyAnnotator` create an annotation in case `SMW_DV_WPV_DTITLE` is enabled
+* #1883 Avoided mismatch in case `hasSubSemanticData` has been overridden as by `Sql3StubSemanticData`
+* #1885 Fixed postgres bytea escape/unescape on blob fields
+* #1887 Moved `Hooks:CanonicalNamespaces` to an earlier execution point
+* #1897 Worked around deprecated/removed `DatabaseBase::getSearchEngine`
+* #1901 Made `enableSemantics` call `NamespaceManager`
+* #1911 Improved compatibility with MediaWiki 1.28+
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.3.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.3.md
new file mode 100644
index 00000000..832ef7fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.3.md
@@ -0,0 +1,9 @@
+# Semantic MediaWiki 2.4.3
+
+Released on November 28nd, 2016.
+
+## Bug fixes
+
+* #1975 Fixed alias definitions for namspace "Type" causing notices due to the namespace being undefined
+* <code>fd6b4cf</code> Fixed a compatibility breaking short array syntax for PHP 5.3
+* <code>0ae3a3e</code> Added missing internal file loading
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.4.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.4.md
new file mode 100644
index 00000000..27e1dcea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.4.md
@@ -0,0 +1,8 @@
+# Semantic MediaWiki 2.4.4
+
+Released on December 14, 2016.
+
+## Bug fixes
+
+* #2078 Fixed error for MySQL 5.7 causing "SELECT list; this is incompatible with DISTINCT"
+* #2089 Fixed error for MySQL 5.7 causing "UPDATE - SET; Data too long for column"
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.5.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.5.md
new file mode 100644
index 00000000..5480b95f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.5.md
@@ -0,0 +1,11 @@
+# Semantic MediaWiki 2.4.5
+
+Released on January 16th, 2017.
+
+## Bug fixes
+
+* <code>e3689e6</code> Fixed datatypes not being recognized on property pages
+* #2124 Fixed to use `wfCgiToArray` to avoid deprecation notice for `SMWInfolink`
+* #2156 Fixed Javascript error caused by `wikiScript` being undefined
+* #2160 Fixed `ParserCachePurgeJob` to be avoided on an empty request
+* #2166 Fixed `QueryDependencyLinksStore` to check for a null title
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.6.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.6.md
new file mode 100644
index 00000000..a049b061
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.4.6.md
@@ -0,0 +1,7 @@
+# Semantic MediaWiki 2.4.6
+
+Released on February 15th, 2017.
+
+## Enhancement
+
+* #2235 Backport of #1758 - Add configuration parameter `$smwgQTemporaryTablesAutoCommitMode` allowing to create MySQL temp tables in auto commit mode
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.0.md
new file mode 100644
index 00000000..17085429
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.0.md
@@ -0,0 +1,209 @@
+# Semantic MediaWiki 2.5
+
+Released on March 14, 2017.
+
+## Highlights
+
+### Full-text search support
+
+Support for [full-text search](https://www.semantic-mediawiki.org/wiki/Help:Full-text_search) was added using the native capabilities of the SQL backends "MySQL"/"MariaDB" (#1481) and "SQLite" (#1801) for the "Text", "URL" and "Page" datatypes.
+
+### Provenance data recording
+
+Qualifying facts using a simple [provenance model](https://www.semantic-mediawiki.org/wiki/Reference_and_provenance_data) is now supported (#1808) using existing mechanisms in defining a property specification together with a new ["Reference" datatype](https://www.semantic-mediawiki.org/wiki/Help:Type_Reference) ([video](https://youtu.be/t045qkf4YAo)).
+
+### Property chain and language filter support in print request
+
+[Property chain](https://www.semantic-mediawiki.org/wiki/Property_chains_and_paths) for conditions (e.g `[[Located in.Capital of::Foo]]`) was provided for some time, and now got extended (#1824) to supporting the syntax on print requests to retrieve values of a chain member that represent a page node. Values of datatype "Monolingual Text" can now use a language filter (#2037) to restrict the display of a value in a print request.
+
+### Edit protection
+
+[Edit protection](https://www.semantic-mediawiki.org/wiki/Edit_protection) to help avoid changes to properties or other data sensitive pages from alterations that may cause data invalidations (e.g. change of a property type, inconsistent specifications etc.) or process disruptions. This feature integrates with MediaWiki's page protection functionality.
+
+### Preferred property label support
+
+Semantic MediaWiki now supports the declaration of [preferred property labels](https://www.semantic-mediawiki.org/wiki/Preferred_property_label) (#1865) with the objective to show labels in a user context on special pages, query results, and factboxes instead of the canonical property label.
+
+### Query result cache
+
+[Caching of query results](https://www.semantic-mediawiki.org/wiki/Query_result_cache) (#1251) was added as experimental feature to minimize a possible impact of query processing during and after a page view. This change also includes a reevaluation (#2099, #2176) of the query hash (used as identifier) to ensure that cache fragmentation is reduced and duplicate queries can share the same cache across different pages.
+
+### Links in values
+
+Support for [links in values](https://www.semantic-mediawiki.org/wiki/Help:$smwgLinksInValues) for datatype "Text" was extended by use-cases and improved in performance as well as avoiding the former error-prone "PCRE-approach".
+
+### Fixed properties
+
+Support for [fixed properties](https://www.semantic-mediawiki.org/wiki/Help:Fixed_properties) was overhauled, fixed (#2135) and is no longer experimental.
+
+### Special page "SemanticMediaWiki"
+
+Special page ["SemanticMediaWiki"](https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki) formerly known as special page "SMWAdmin" was modernized and extended (#2044, etc.) including a new [configuration setting](https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures) allowing for a more fine-granded control over feature accessibilty (#2142).
+
+## Compatibility changes
+
+* Minimum requirement for PHP changed to version 5.5 and later
+* Minimum requirement for MediaWiki changed to version 1.23 and later (1.27 and later recommended)
+* Forward comatibility with MediaWiki 1.29+ adjustments which include #2149, #2198
+
+## Upgrading
+
+This release requires to run `update.php` or `setupStore.php` to add an extra table column for the URI table (#1872) and a new table for the preferred label property (#1865).
+
+## New features and enhancements
+
+* [#1251](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1251) Added support to cache query results
+* [#1418](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1418) Added recognition for image formatting options in query results
+* [#1481](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1481) Added full-text `MySQL`/`MariaDB` search support to the `SQLStore` (see #1481 for limitations and features supported)
+* [#1652](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1652) Added support for approximate search queries that contain a namespace `[[Help:~Abc*]]`
+* [#1691](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1691) Added language fallback for special properties
+* [#1708](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1708) Added the [External identifier](https://www.semantic-mediawiki.org/wiki/Help:Type_External_identifier) type
+* [#1718](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1718) Added feature flag `SMW_DV_NUMV_USPACE` to allow preserving spaces in unit labels
+* [#1747](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1747) Extended `InTextAnnotationParser` to check for a possible pipe syntax in combination with `::`
+* [#1757](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1757) Added the [`$smwgQTemporaryTablesAutoCommitMode`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQTemporaryTablesAutoCommitMode) setting to mitigate possible issues with temporary tables in `MySQL` for when `enforce_gtid_consistency=true` is set
+* [#1756](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1756) Extended the display characteristics of `Special:Browse` to load content via the API back-end (legacy display can be retained by maintaining [`$smwgBrowseByApi`](https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi) with `false`)
+* [#1759](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1759) Improved `Special:Ask` error output
+* [#1760](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1760) Improved handling of `MonolingualTextValue` in `Special:SearchByProperty`
+* [#1761](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1761) Added language context support in a property page to recognize localized property type `[[Has type ...]]` annotations
+* [#1768](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1768) Extended error display to be shown in a user language context
+* [#1778](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1778) Export the canonical form of a special page (e.g. `Special:ExportRDF`, `Special:URIResolver`)
+* [#1779](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1779) Added [`Special:ProcessingErrorList`](https://www.semantic-mediawiki.org/wiki/Help:Special:ProcessingErrorList)
+* [#1793](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1793) Extended date type (`TimeValue`) with an `#LOCL@lang` output format to recognize a specific language tag
+* [#1801](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1801) Added `SQLStore` full-text search support for `SQLite`
+* [#1802](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1802) Extended parsing in `#set_recurring_event` to avoid a `00:00:00` time display
+* [#1809](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1809) Added support for using a property name as index identifier in a print request for the `Record` type
+* [#1808](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1808) Added support for recording [provenance data](https://www.semantic-mediawiki.org/wiki/Referenced_statement)
+* [#1824](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1824) Added support for the [property chain](https://www.semantic-mediawiki.org/wiki/Property_chain) syntax (e.g. `?SomeProperty.Foo`) in a print request
+* [#1838](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1838) Added time zone support in `TimeValue` together with the new `#LOCL#TZ` output format
+* [#1854](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1854) Added unescaped output option for `format=json`
+* [#1855](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1855) Added `@@@` as special annotation syntax to generate a link to a property (e.g `[[Foo::@@@]]` or `[[Foo::@@@en]]`)
+* [#1865](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1865) Added support for [preferred property labels](https://www.semantic-mediawiki.org/wiki/Preferred_property_label)
+* [#1872](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1872) Added support for retrieving and storing URIs longer than 255 characters
+* [#1875](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1875) Added support for displaying a `title` attribute on tooltips for non JS environments
+* [#1891](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1891) Added support for `JSON` typed annotation in `#set` and `#subobject` using the `@json` marker
+* [#1927](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1927) Added [`$smwgSubPropertyListLimit`](https://www.semantic-mediawiki.org/wiki/Help:$smwgSubPropertyListLimit) to restrict selection of subproperties on the property page
+* [#2007](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2007) Extended the `intro` and `outro` parameter to correctly display parsed links in `Special:Ask`
+* [#2024](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2024) Added option `template arguments` in `format=template` to define the type of used parameters
+* [#2027](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2027) Extended `format=table` to display an image (instead of a link) in `Special:Ask`
+* [#2036](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2036) Added print request option for text values to reduce the length of a text output (e.g. `|?Has text#20`)
+* [#2037](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2037) Added `|+lang=` as print request filter to specify a language for a `Monolingual text` result instance
+* [#2068](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2068) Extended the `#info` tooltip to work on multiple form sections
+* [#2108](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2108) Extended the `smw.dataItem.time` JS component to support historic dates
+* [#2109](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2109) Extended `Special:Browse` to distinguish between machine and human generate links
+* [#2113](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2113) Extended the [uniqueness constraint](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_uniqueness_constraint) to apply a stricter validation on competing annotations
+* [#2118](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2118) Added a button to `Special:Ask` to copy the query to the clipboard
+* [#2135](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2135) Changed and fixed the behaviour of the [`$smwgFixedProperties`](https://www.semantic-mediawiki.org/wiki/Help:$smwgFixedProperties) setting for [fixed properties](https://www.semantic-mediawiki.org/wiki/Help:Fixed_properties) to ensure consistent typing
+* [#2137](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2137) Extended the display of statistics in `Special:Statistics`
+* [#2139](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2139) Added the display of Semantic MediaWiki related job statistics under the subsection of the [`Special:SemanticMediaWiki`](https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki) page
+* [#2142](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2142) Added `$smwgAdminFeatures` to support `PropertyStatisticsRebuildJob` and `FulltextSearchTableRebuildJob` from the `Special:SemanticMediaWiki` (formally known as `Special:SMWAdmin`) page, the `smwgAdminRefreshStore` setting was deprecated
+* [#2153](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2153) Changed the behaviour of the [`$smwgLinksInValues`](https://www.semantic-mediawiki.org/wiki/Help:$smwgLinksInValues) setting to allow using the `Obfuscator` (`SMW_LINV_OBFU`) approach instead of `PCRE` to match links in values (e.g. `[[Has text::[[Lorem ipsum]] dolor sit amet, [[Has page::consectetur adipiscing elit]]]]`)
+* [#2157](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2157) Extended the property page to show [redirects (synonyms)](https://www.semantic-mediawiki.org/wiki/Redirects) directly
+* [#2173](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2173) Added support for prettified `JSON` output in the `CodeStringValueFormatter`
+* [#2176](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2176) Added an experimental feature [`smwgQFilterDuplicates`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQFilterDuplicates) to filter duplicate query segments
+* [#2204](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2204) Extended `Special:UnusedProperties` and `Special:WantedProperties` to provide an input form
+* [#2207](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2207) Added [`smwgExportResourcesAsIri`](https://www.semantic-mediawiki.org/wiki/Help:$smwgExportResourcesAsIri) to allow exporting resources as IRIs
+* [#2209](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2209) Extended parsing of interface messages to support additional `smwgEnabledSpecialPage` pages
+* [#2221](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2221) Added possibility to show a general message on each property page (`smw-property-introductory-message`) or for a specific type of property (`smw-property-introductory-message-user`, `smw-property-introductory-message-special`)
+* [#2227](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2227) Added warning, error, and info messages for incomplete requirements on a property page
+* [#2232](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2232) Added support for [Is edit protected](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Is_edit_protected) property together with `$wgRestrictionLevels` (#2249)
+* [#2243](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2243) Added property and concept namespace to the `$wgContentNamespaces` and `$wgNamespacesToBeSearchedDefault` setting
+* [#2244](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2244) Added [`Special:PropertyLabelSimilarity`](https://www.semantic-mediawiki.org/wiki/Help:Special:PropertyLabelSimilarity) to help reporting syntactic similarities between property labels
+* [#2253](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2253) Added `#-hl` output formatting option to highlight search tokens within a result set
+* [#2270](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2270) Added query parameters recording in the [query profiler](https://www.semantic-mediawiki.org/wiki/Help:Query_profiler)
+* [#2281](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2281) Added check to detect a divergent type specification for an imported vocabulary
+* [#2282](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2282) Added [`$smwgPropertyInvalidCharacterList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgPropertyInvalidCharacterList) to define character validation rules for property labels
+* [#2285](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2285) Added [`SMW_HTTP_DEFERRED_SYNC_JOB`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEnabledHttpDeferredJobRequest) option to execute secondary updates synchronously
+* [#2289](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2289) Added a [contents importer](https://www.semantic-mediawiki.org/wiki/Help:Contents_importer) to support importing of additional data during the setup process
+* [#2290](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2290) Added [query reference](https://www.semantic-mediawiki.org/wiki/Query_reference) links section to `Special:Browse`
+* [#2295](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2295) Added [`Allows value list`](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list) to maintain a list of allowed values using a `NS_MEDIAWIKI` reference page
+* [#2301](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2301) Added [`$smwgSparqlReplicationPropertyExemptionList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgSparqlReplicationPropertyExemptionList) to suppress replication for selected properties to a `SPARQL` endpoint
+* [#2325](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2325) Added `#-ia` as print request output option for the text datatype
+* [#2331](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2331) Added [`$smwgResultFormatsFeatures`](https://www.semantic-mediawiki.org/wiki/Help:$smwgResultFormatsFeatures) to control available features for specific `ResultFormatter` and includes (`SMW_RF_TEMPLATE_OUTSEP` to support the #2022 changes)
+* Many new translations for numerous languages by the communtity of [translatewiki.net](https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=mwgithub-semanticmediawiki&suppressempty=1)
+* New translation for special properties, datatypes, magic words, date formats and aliases for Catalan and German by Semantic MediaWiki community members
+
+## Bug fixes
+
+* [#1258](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1258) Fixed "named args" parameter use in further results link
+* [#1328](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1328) Fixed a "Undefined index: language" error in `#smwdoc` parser function
+* [#1419](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1419) Fixed Feed result printer ouput for empty results
+* [#1709](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1709) Fixed a potential "Lock wait timeout exceeded; try restarting transaction" in connection with `--procs`
+* [#1713](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1713) Fixed a "Segmentation fault" for when `QueryResultDependencyListResolver` tries to resolve a category/property hierarchy with a circular reference
+* [#1715](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1715) Fixed decoding of a single quotation mark in `DisplayTitlePropertyAnnotator`
+* #1724 Fixed a possible `InvalidArgumentException` in connection with `SMW_DV_PVUC` by updating the `CachedPropertyValuesPrefetcher` version number
+* #1727 Fixed an issue when property names contain `<` or `>` symbols
+* #1728 Fixed fatal error in `Special:SearchByProperty` on when the property name contains invalid characters
+* #1731 Fixed possible error in the `SkinAfterContent` hook when a null object is used
+* #1744 Fixed special page "Searchbyproperty" not working correctly with "-" sign
+* #1775 Fixed time offset recognition
+* #1817 Disabled `DataValue` constraint validation when used in a query context
+* #1823 Fixed annotation of `Display title of` when `SMW_DV_WPV_DTITLE` is disabled
+* #1880 Fixed handling of the `bytea` type in `postgres` for a blob field
+* #1886 Fixed disappearance of the `Property` namespace in connection with extensions that use `wfLoadExtension`
+* #1922 Fixed `InfoLinksProvider` to avoid `LOCL` info links
+* #1926 Fixed `PrintRequest` to recognize the spant tag in labels
+* #1935 Fixed "Error: 42P10 ERROR: ... ORDER BY expressions must appear in select list" for PostgreSQL
+* #1957 Fixed `SMWSQLStore3Writers::getSubobjects` using the wrong DBKey in case of predefined properties
+* [#1963](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1963) Fixed by relying on #2153
+* #1977 Fixed Unexpected general modules for Resource Loader
+* #1978 Fixed `Tablebuilder` to avoid index creation on an unaltered schema definition
+* #1985 Fixed a potential fatal error in `MaintenanceLogger` for when `$wgMaxNameChars` doesn't match an expected name length
+* [#2000](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2000) Fixed label and caption sanitization
+* [#2022](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2022) Fixed the usage of the sep parameter for format "template"
+* #2061 Fixed strict comparison `===` for strings in `PropertyTableRowDiffer`
+* #2070 Filter invalid entity display from `Special:Concepts`
+* #2071 Prevent extensions to register already known canonical property labels and hereby avoid a possible ID mismatch
+* #2076 Fixed issue for Gregorian and Julian calendars having a year 0
+* #2078 Fixed issue with "SELECT list; this is incompatible with DISTINCT" for MySQL 5.7+
+* #2089 Fixed issue with "UPDATE - SET; Data too long for column" for MySQL 5.7+
+* #2093 Avoid removal of existing data by #REDIRECT in target
+* #2107 Fixed `NamespaceManager::init` to set SMW_NS* default settings
+* #2127 Fixed a call to a the member function `getHash()` on nulll
+* [#2182](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2182) Fixed display of special properties in `Special:UnusedProperties`
+* [#2183](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2183) Fixed display of properties with no explicit datatype in `Special:UnusedProperties`
+* #2188 Fixed error in special page "RDFExport" with non-latin instance names
+* #2202 Added guard against error "Invalid or virtual namespace -1 given"
+* [#2228](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2228) Fixed text output for the table format in `Special:Ask`
+* [#2294](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2294) Avoid a possible `Parser::lock` during an `UpdateJob`
+
+## Internal changes
+
+* #1511 Removed I18n shim originally required for MediaWiki < 1.23
+* #1726 Allows `QueryDependencyLinksStore` to execute `getDependencyListByLateRetrieval` even in cases of an intial empty list
+* #1750 Added `TableBuilder` to replace `SMWSQLHelpers`
+* #1780 Added `ResourceBuilder` and `DispatchingResourceBuilder`
+* #1791 Added `PropertyRegistry::registerPropertyDescriptionByMsgKey`
+* #1776 Added `QueryEngine` and `StoreAware` interface
+* [#1848](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1848) Added `ExtraneousLanguage` to handle Semantic MediaWiki specific `i18n` content in a `JSON` format, removed the `PHP` language files
+* #1940 Added `Installer` and `TableSchemaManager` to replace `SMWSQLStore3SetupHandlers`
+* [#2118](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2118) Added the `onoi/shared-resources~0.3` dependency
+* [#2201](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2201) Changed normalization of spaces to `_` instead of `%20` in `DIUri`
+* [#2214](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2214) Added `LinksProcessor` and `SemanticLinksParser`
+* [#2217](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2217) Added `QuerySegmentListBuildManager`
+* [#2275](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2275) Added the `onoi/callback-container:~2.0` dependency
+* [#2282](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2282) Added `DataValueServiceFactory` and `DataValueServices.php` to allow injection of services into a `DataValue` instance
+
+## Settings and configurations
+
+[Settings and configurations](https://www.semantic-mediawiki.org/w/index.php?title=Special:Ask&x=-5B-5BHas-20configuration%3A%3A%2B-5D-5D-20-5B-5BHas-20minimum-20version%3A%3A2.5.0-5D-5D%2F-3FHas-20configuration-20parameter-20name%3DConfiguration-20parameter%2F-3FHas-20description%3DDescription&format=broadtable&limit=50&link=all&headers=show&mainlabel=-&searchlabel=...%20further%20results&class=sortable%20wikitable%20smwtable&offset=) added with 2.5.0.
+
+## Contributors
+
+* 688 - James Hong Kong
+* 59 - Karsten Hoffmeyer
+* 51 - Jeroen De Dauw
+* 37 - Niklas Laxström
+* 14 - translatewiki.net
+* 5 - Maciej Brencz
+* 4 - Felipe de Jong
+* 4 - Siebrand Mazeland
+* 2 - Alex Winkler
+* 2 - Stephan Gambke
+* 2 - Toni Hermoso Pulido
+* 1 - Amir E. Aharoni
+* 1 - Felipe Schenone
+* 1 - Jaider Andrade Ferreira
+* 1 - James Forrester
+* 1 - Justin Du
+* 1 - Sébastien Beyou
+* 1 - Virginia Cepeda
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.1.md
new file mode 100644
index 00000000..4d1f1e1c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.1.md
@@ -0,0 +1,51 @@
+# Semantic MediaWiki 2.5.1
+
+Released on April 22, 2017.
+
+## New feature
+
+* [#2357](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2357) as `ec3810d` Added [deprecation notices](https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Deprecation_notices) system (#2357, #2384, #2401) to `Special:SemanticMediaWiki` in support for the upcoming 3.0 release
+
+## Enhancements
+
+* [#2356](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2356) as `c781b02` Extended [`smwgEnabledHttpDeferredJobRequest`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEnabledHttpDeferredJobRequest) to allows `SMW_HTTP_DEFERRED_LAZY_JOB` (#2356)
+* [#2358](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2358) as `769ca88` Enforces "Property" and "Concept" canonical namespaces
+* [#2367](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2367) as `ec6d5c1` Added a more verbose error message for failed [allows values](https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list)
+* [#2386](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2386) as `cd31a79` Extended the [contents importer](https://www.semantic-mediawiki.org/wiki/Help:Contents_importer) to support the MediaWiki's XML format
+* [#2387](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2387) as `6d11e5a` Improved the display of `Special:Browse` in connection with mobile devices and the `MobileFrontend` extension
+* [#2388](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2388) as `74afabe` Ensured the content for the full-text search is in sync with the "SemanticData" primary data update
+* [#2414](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2414) as `3e32ad3` Add support for the display of [query references](https://www.semantic-mediawiki.org/wiki/Help:Query_reference) on a subobject
+* [#2417](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2417) as `55b3d99` Add a more verbose error message to the "WikiPageValue"
+
+## Bug fixes and internal code changes
+
+* [#2351](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2351) as `8a9b94d` Fixed `[` encoding in `Highlighter` to allows for some `#info` post-processing
+* [#2353](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2353) as `2414cb8` Fixed "Undefined index: HTTP_ACCEPT" in Special:URIResolver
+* [#2354](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2354) as `21ee86c` Fixed a "Out of range value ..." in DB strict mode caused by the "PropertyStatisticsTable"
+* [#2359](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2359) as `ed5686a` Fixed a "SubSemanticData::copyDataFrom ... null given" message
+* [#2361](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2361) as `85b2386` Fixed `EntityIdDisposerJob::dispose` to use an int value
+* [#2363](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2363) as `bad1460` Fixed pre-process of title content in the `Highlighter`
+* [#2365](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2365) as `f5a30dd` Added update marker to track and avoid having `refreshLinksPrioritized` (MW 1.29+) to issue store updates
+* [#2373](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2373) as `8a37d42` Added detection of `SMW off/on` for annotations within system messages
+* [#2374](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2374) as `6ddb4c6` Added detection of property max count to `Special:PropertyLabelSimilarity`
+* [#2377](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2377) as `5d51d2c` Fixed "Uncaught Error: Unknown dependency: jquery.ui.autocomplete" in `Special:Browse` when displayed by the `MobileFrontend` extension
+* [#2385](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2385) as `727b825` Fixed display if unparsed error text in wikitext display
+* [#2389](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2389) as `b1800eb` Fixed counting of links in `ParserCachePurgeJob`
+* [#2393](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2393) as `013da5a` Added `PageUpdater::isHtmlCacheUpdate` to disable `HTMLCacheUpdateJob ` due to [T154427](https://phabricator.wikimedia.org/T154427)
+* [#2397](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2397) as `2af43cb` Fixed `SemanticData::getPropertyValues` to always return an indexed array
+* [#2405](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2405) as `982f1dc` Fixed normalization of error messages in the `API` output
+* [#2406](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2406) as `7d4a0f5` Fixed duplicate detection for sort conditions in `prop.chain` notations in connection with [`$smwgQFilterDuplicates`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQFilterDuplicates)
+* [#2410](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2410) as `d2cb5b7` Fixed the appearance of an `index` parameter in the `further results` link in connection with the `+|lang` printout parameter
+* [#2412](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2412) as `d6aca45` Fixed order of parameters in `Special:Ask` on the event of a `further results` link that contains `+|...` parameters
+* [#2413](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2413) as `d017a15` Fixed ID creation of temporary queries in `UniquenessConstraintValueValidator` when a uniqueness constraint isn't cached
+* [#2415](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2415) as `88e8884` Fixed URI value encoding for the [`External identifier`](https://www.semantic-mediawiki.org/wiki/Help:Type_External_identifier) type
+
+## Deprecations
+
+* [#2362](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2362) as `4c004e4` Deprecated [`$smwgAdminRefreshStore`](https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminRefreshStore) in favor of
+[`$smwgAdminFeatures`](https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures) to be removed with SMW 3.1.0
+* [#2364](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2364) as `aba22d8` Fixed inconsistent list name parameter settings :
+ * [`$smwgQueryDependencyPropertyExemptionList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQueryDependencyPropertyExemptionList)
+instead of now deprecated `$smwgQueryDependencyPropertyExemptionlist` to be removed with SMW 3.1.0 and
+ * [`$smwgQueryDependencyAffiliatePropertyDetectionList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQueryDependencyAffiliatePropertyDetectionList)
+instead of now deprecated `$smwgQueryDependencyAffiliatePropertyDetectionlist` to be removed with SMW 3.1.0
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.2.md
new file mode 100644
index 00000000..1d073c8f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.2.md
@@ -0,0 +1,22 @@
+# Semantic MediaWiki 2.5.2
+
+Released on May 17, 2017.
+
+## Enhancements
+
+* [#2449](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2449) as `8783268` Made property pages show the source name of the redirect (synonym) without a `DisplayTitle` formatter
+* Many new translations for numerous languages by the communtity of [translatewiki.net](https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=mwgithub-semanticmediawiki&suppressempty=1)
+
+## Bug fixes and internal code changes
+
+* [#2413](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2413) as `313d08e` Enforced `NO_DEPENDENCY_TRACE` on queries with namespace `NS_SPECIAL`
+* [#2426](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2426) as `595efea` Removed duplicate entry for `$smwgFulltextSearchPropertyExemptionList`
+* [#2434](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2434) as `bb6ef9a` Made `ParserAfterTidy` to check "readOnly" mode
+* [#2438](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2438) as `ba2c6e7` Made `ArticlePurge` add a safeguard to flush query result cache
+* [#2444](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2444) as `8c9c4c3` Fixed `NamespaceManager` to avoid reset of user settings
+* [#2446](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2446) as `6697da4` Added safeguard against duplicate ID creation
+* [#2448](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2448) as `60cd466` Added usage of `forcedUpdate` on redirect jobs
+* [#2450](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2450) as `4adfd2d` Fixed `QueryDependencyLinksStore` to avoid `ORDER BY/GROUP BY` on select
+* [#2451](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2451) as `8a9bef2` Fixed "ext.smw.dataItem.time.js" to construct a UTC date object
+* [#2457](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2457) as `5619b55` Fixed `JulianDay` values to use a consistent format
+* [#2463](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2457) as `3f7f47e` Made `SMWSql3SmwIds` set legacy cache only on success
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.3.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.3.md
new file mode 100644
index 00000000..e65bdad2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.3.md
@@ -0,0 +1,35 @@
+# Semantic MediaWiki 2.5.3
+
+Released on July 8, 2017.
+
+## Enhancements
+
+* [#2534](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2534) as `d7077b8` Added [`$smwgLocalConnectionConf`](https://www.semantic-mediawiki.org/wiki/Help:$smwgLocalConnectionConf) configuration parameter together with respective functionality allowing for modifications on connection providers in environments with multiple relational databases
+* Many new translations for numerous languages by the communtity of [translatewiki.net](https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=mwgithub-semanticmediawiki&suppressempty=1)
+
+## Bug fixes and internal code changes
+
+* [#2379](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2379) as `7c98b4a` Removed `ContentParser::forceToUseParser` from tests
+* [#2459](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2459) as `a7b3f00` Switched Travis CI integration test to use Ubuntu Trusty operating system environment
+* [#2460](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2460) as `3b6e30d` Made `ArticleDelete` restrict the pool of properties in update dispatcher
+* [#2472](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2472) as `03e0b8c` Added debug output to Travis CI integration tests
+* [#2473](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2473) as `9f78042` Replaced `isSupportedLanguage` with `isKnownLanguageTag` to allow for any known language usage
+* [#2474](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2474) as `d1ba666` Fixed limit when the number of results is greater as the `$smwgQMaxLimit` or in `$smwgQMaxInlineLimit` where it is reset to the default value despite the global limitation
+* [#2475](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2475) as `a3499b6` Fixed behavior in case of `$wgCapitalLinks = false;` by restricting property name uppercase conversion to special properties only
+* [#2477](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2477) as `c12fec7` Fixed `UpdateDispatcherJob` to check for null title
+* [#2478](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2478) as `681b0fc` Tidyed `QueryToken`
+* [#2481](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2481) as `7c3900f` Made `RequestOptions` cast "int" on `limit` and `offset`
+* [#2482](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2482) as `2ff92bd` Added TransactionalDeferredCallableUpdate
+* [#2491](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2491) as `ca36069` Provided `ChunkedIterator` to avoid possible out of memory situations in cases where outdated entities reach a unhandable level
+* [#2493](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2493) as `409025d` Prevended unintended override of `PropertyTablePrefix` in hook
+* [#2496](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2496) as `fb3d604` Normalized message value arguments
+* [#2500](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2500) as `3edb303` Made "Special:Browse" avoid API request on legacy setting
+* [#2502](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2502) as `a527bbe` Provided POST purge link to avoid confirmation by users using action "purge"
+* [#2512](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2512) as `86f9733` Made `DataRebuilder` to report progress on disposed entities
+* [#2518](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2518) as `a851f8d` Prevended "PHP Notice: A non well formed numeric value encountered" on `Title::touched`
+* [#2522](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2522) as `36cec82` Set a comma as default for `valuesep` with the "template" format
+* [#2524](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2524) as `36cec82` Ensured that only marked `isDeferrableUpdate` can use a `transactionTicket`
+* [#2526](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2526) as `9d3e0f2` Prevented failing test in `QueryDependencyLinksStoreTest`
+* [#2527](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2527) as `f72df04` Made `BooleanValue` always recognize canonical boolean string
+* [#2530](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2530) as `ad32a26` Made `InternalParseBeforeLinks` cast `$smwgEnabledSpecialPage` setting late
+* `2bf07c3` Removed update marker on delete event
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.4.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.4.md
new file mode 100644
index 00000000..b6a3d67b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.4.md
@@ -0,0 +1,15 @@
+# Semantic MediaWiki 2.5.4
+
+Released on August 7, 2017.
+
+## Enhancements
+
+* [#2547](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2547) as `b527e3c` Added type `parser-html` to `JSONScript` testing to allow assertions on HTML structure
+* Many new translations for numerous languages by the communtity of [translatewiki.net](https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=mwgithub-semanticmediawiki&suppressempty=1)
+
+## Bug fixes and internal code changes
+
+* [#2563](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2563) as `f17f90f` Made `'HtmlValidator'` check for `'CssSelectorConverter'`
+* [#2568](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2568) as `c8d6718` Made each parameter of the template calls created by the template format start on a new line
+* [#2579](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2579) as `39b074b` Fixed class `'SMW\DataItemException'` not found
+* [#2590](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2590) as `61ea7e0` **SECURITY** Made "Special:SemanticMediaWiki" ("Special:SMWAdmin") to check `'wpEditToken'`
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.5.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.5.md
new file mode 100644
index 00000000..979e5e88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.5.md
@@ -0,0 +1,11 @@
+# Semantic MediaWiki 2.5.5
+
+Released on October 25, 2017.
+
+## Bug fixes and internal code changes
+
+* [#2672](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2672) as `e17160e` Changes obfuscator to use `&#x005B;` instead of `&#91;` for the links in values detection
+* [#2692](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2692) as `5ddd9ca` Fixes "EventListenerRegistry.php: Call to a member function getArticleID() on null"
+* [#2767](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2767) as `848ed0c` Forces data updates on template refreshs
+* [#2773](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2773) as `55b6b72` Adds in MediaWiki's default script parameters in non-standard setups
+* [#2780](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2780) as `4bc0d5f` Brings additional checks for namespaces that are enabled for links and annotations
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.6.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.6.md
new file mode 100644
index 00000000..8eb1e719
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.6.md
@@ -0,0 +1,10 @@
+# Semantic MediaWiki 2.5.6
+
+Released on February 14, 2018.
+
+## Bug fixes and internal code changes
+
+* [#2855](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2855) as `5acfd1a` Fixes link type relating to special page "Browse"
+* [#2856](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2856) as `2db4d30` Fixes link type relating to special page "ExportRDF"
+* [#2941](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2941) as `4ebb529` Removes deprecated `ApiBase::dieUsage`
+* [#2986](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2986) as `9a8d1c8` Makes the "External identifier" (`_eid`) datatype use HTML format in references
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.7.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.7.md
new file mode 100644
index 00000000..d988628d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.7.md
@@ -0,0 +1,11 @@
+# Semantic MediaWiki 2.5.7
+
+Released on August 9, 2018.
+
+## Bug fixes and internal code changes
+
+* [#3038](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2855) as `4ebb125` Removes deprecated `ParserOptions::setEditSection()`
+* [#3176](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3176) as `6de5e42` Removes deprecated `wfBCP47()`
+* [#3211](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3211) as `6196919` Makes `-3D` decode in parameter list to allow the usage of equal signs
+* [#3221](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3221) as `954ff20` Converts `log-show-hide-{$type}` system messages to `logeventslist-{$type}-log`
+* [#3222](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3222) as `dae4dd5` Fixes the `Title` class to check for the NULL case for "illegal" characters
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.8.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.8.md
new file mode 100644
index 00000000..a1492bba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-2.5.8.md
@@ -0,0 +1,13 @@
+# Semantic MediaWiki 2.5.8
+
+Released on September 7, 2018.
+
+## Enhancements
+
+* #[3322](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3322) as `d9b3bcf` Improves `UpdateDispatcherJob` selection of subjects on `forcedUpdate`
+* #[3341](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3341) as `726f54b` Adds support for installation via MediaWiki's "install.php" script
+
+## Bug fixes and internal code changes
+
+* #[3177](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3177) as `ad23b16` Removes deprecated "jquery.json" module
+* #[3386](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3386) as `a24965c` Fixes encode/decode issues for special property "External formatter URI"
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.0.md
new file mode 100644
index 00000000..3a4a3bb9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.0.md
@@ -0,0 +1,375 @@
+# Semantic MediaWiki 3.0
+
+Released on October 11, 2018.
+
+## Highlights
+
+This release brings many highlights:
+
+### User interface changes
+
+Several user interface changes are deployed to make user facing front-end components more intutive and mobile-friendly by improving the responsiveness on small screens including:
+
+* Special page "Ask" ([#2891](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2891), [#2893](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2893), [#2898](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2898), [#3415](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3415)) – including further enhancements, most notably input assistance on input fields ([#2699](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2699)), comprehensive input help ([#2907](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2907)) and compact links ([#3017](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3017))
+* Special page "Browse" ([#2891](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2891), [#2875](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2875)) – including further enhancements, grouping of properties ([#2874](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2874)) and compact links ([#3017](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3017))
+* Special page "SemanticMediaWiki" ([#3218](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3218))
+* Property pages – boxed pagination ([#3236](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3236)), tabbed navigation ([#3308](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3308)) including usage count information ([#3440](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3440)) and custom tabs ([#3416](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3416))
+* Concept pages – boxed pagination ([#3236](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3236)), tabbed navigation ([#3308](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3308)) and custom tabs ([#3416](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3416))
+* Factbox ([#2906](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2906))
+* Special page "Concepts" ([#3333](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3333))
+
+### List formats and template format rework
+
+The "list" formats (`list`, `ol` and `ul`) and the `template` format were completely reworked with the latter being renamed to `plainlist` [(#3130)](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3130) now being the default result format if no result format was explicitly specifed for the query. Most notably dedicated separators for values, properties and result "rows" (`sep`, `propsep`, `valuesep`) were introduced as well as class attributes to HTML elements of "list", "ol" and "ul" formats were added to facilitate easy indidual styling. Note that the `plainlist` format does not apply these additional class attributes.
+
+**See the [migration guide](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/migration-guide-3.0.md#list-formats-incl-list-ol-ul-template) for a comprehensive overview of the changes done.**
+
+### Search and query
+
+Local-specific (ICU) sorting and collation is now possible for pages as well as values of datatype "Page" [(#2065)](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2065) facilitated via configuration parameter [`$smwgEntityCollation`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation) [(#2429).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2429)
+
+Special page "Search" now provides and additional search form accessible via the "Extended" selector in case the ["SMWSearch" feature](https://www.semantic-mediawiki.org/wiki/Help:SMWSearch) was enabled [(#3126).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3126) with custom search forms definable in the new "smw/schema" namespace [(#3431).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3431)
+
+It is now possible to define [remote sources which can be queried](https://www.semantic-mediawiki.org/wiki/Help:Remote_request) using special page "Ask" or doing inline queries [(#3167).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3167)
+
+### Performance
+
+Various effort have been put into improving the performance of the software, most notably with these three code changes:
+[#3142](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3142), [#3261](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3261) and [#3286](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3286) with the latter facilitating less expensive paging limits on various user facing special pages via configuration parameter [`$smwgPagingLimit`](https://www.semantic-mediawiki.org/wiki/Help:$smwgPagingLimit).
+
+## Upgrading
+
+Even though Semantic MediaWiki now supports the extension registration approach with "extension.json" (#1732), `enableSemantics` remains the sole point of activiation for SMW itself to ensure that data and objects are prepared in advanced and users do not have to modify any existing settings in their "LocalSettings.php" file.
+
+This release requires (#2065, #2461, #2499) to run the "setupStore.php" or "update.php" script and a missing upgrade process will redirect users to an [error message](https://www.semantic-mediawiki.org/wiki/Help:Upgrade) to remind him or her of a required action. Note that running the schema update may take quite long (minutes on a medium sized site, many hours on a large site).
+
+**Note that SMW requires write access to the code directory meaning that you currently cannot update. This will be fixed in the following relase allowing to configure an alternative directory for this purpose.**
+
+After the upgrade, please check the "Deprecation notices" section on special page "SemanticMediaWiki" to adapt and modify listed deprecated settings.
+
+If you are still using maintenance scripts identifiable by the "SMW_" prefix you must now migrate to the new maintenance script names. See the help pages on [maintenance scrips](https://www.semantic-mediawiki.org/wiki/Help:Maintenance_scripts) for further information.
+
+[#3198](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3198) switched to PHP 5.6 as minimum requirement as well as to MediaWiki 1.27 as minimum requirement.
+
+**Please also carefully read the section on breaking changes and deprecations further down in these release notes. We have also prepared a [migration guide](https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_3.0.0/Migration_guide) for you.**
+
+## Miscellaneous
+
+Semantic MediaWiki no longer provides file releases [(See #3347).](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3347) If command line access to the webspace is not available or if the hoster imposes restrictions on required functionality an [individual file release](https://github.com/SemanticMediaWiki/IndividualFileRelease) will have to be created.
+
+## New features and enhancements
+
+### Setup
+
+* [#1732](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1732) Added support for "extension.json"
+* [#2916](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2916) Added supplements jobs during the installation process
+* [#3095](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3095) Added database upgrade check with ".smw.json"
+
+### Store
+
+* [#2461](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2461) Improved performance on fetching incoming properties
+* [#2882](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2882) Added detection of duplicate entities upon storage
+* [#2516](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2516) Added an optimization run during the installation process (`setupStore.php`) for SQL tables managed by Semantic MediaWiki
+* [#2065](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2065) Added entity specific collation support with help of the [`$smwgEntityCollation`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation) setting
+* [#2499](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2499) Added [`$smwgFieldTypeFeatures`](https://www.semantic-mediawiki.org/wiki/Help:$smwgFieldTypeFeatures) with `SMW_FIELDT_CHAR_NOCASE` to enable case insensitive search queries
+* [#2536](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2536) Added `SMW_FIELDT_CHAR_LONG` as flag for `$smwgFieldTypeFeatures` to extend the indexable length of blob and uri fields to max of 300 chars
+* [#2823](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2823) Added `SMW_QSORT_UNCONDITIONAL`
+* [#3080](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3080) Added warm up caching for the ID lookup
+* [#3142](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3142) Replaced `DISTINCT` with `GROUP BY` in `SQLStore::getPropertySubjects`
+* [#3261](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3261) Added support for index hint in `DataItemHandler` to enforce specific index selection
+* [#3314](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3314) Moved the `FIXED_PROPERTY_ID_UPPERBOUND` from 50 to 500 to increase the range for fixed property IDs
+* [#3353](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3353) Added support in SQLite to drop fields without the need to delete and restore the entire store
+* [#3360](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3360) In MySQL/MariaDB increase ID field size from "int(8)" to "int(11)". Postgres and SQLite have no size restriction.
+* [#3390](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3390) Adds the `smw_rev` field to the `smw_object_ids` table to track an entity instance and its associated revision ID (represents the raw content)
+* [#3397](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3397) MediaWiki removed `Database::nextSequenceValue` in commit wikimedia/mediawiki@0a9c55b#diff-278465351b7c14bbcadac82036080e9f. SMW added this functionality back for the sake of Postgres.
+
+#### ElasticStore
+
+* [#3054](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3054) Added `ElasticStore` to use Elasticsearch as query backend
+ - #3237, #3241, #3245, #3247, #3249, #3250, #3253
+* [#3152](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3152) Added extra debug query parameter (score_set, q_engine) to special page "Ask"
+
+### Search
+
+* [#2738](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2738) Added information whether `SMWSearch` search mode is enabled or not for special page "Search"
+* [#3006](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3006) Disabled default autocompletion for terms starting with `[[` in special page "Search" for the `SMWSearch` type
+* [#3096](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3096) Added section title display support to indicate subobjects
+* [#3126](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3126) Added extended power profile form
+* [#3143](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3143) Hides namespace section and add auto-discovery
+* [#3145](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3145) Added simplified term parser to `SMWSearch` (see #3157, #3281)
+* [#3234](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3234) Added support for displaytitle in `SearchResult`
+* [#3237](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3237) Added support for highlights from external search engine, if available
+* [#3419](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3419) Add search autocompletion options when `$wgSearchType = 'SMWSearch';`:
+ * `in:Foo bar` equivalent to `[[~~*Foo bar*]]`
+ * `phrase:Foo bar` equivalent to `[[~~"Foo bar"]]`
+ * `has:Foo bar` equivalent to `[[Foo bar::+]]`
+
+### Query
+
+* [#2398](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2398) Added `#ask` and `#show` parser function support for `@deferred` output mode (see also #3257)
+* [#2476](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2476) Added [`$smwgQExpensiveThreshold`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveThreshold) and [`$smwgQExpensiveExecutionLimit`](https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit) to count and restrict expensive `#ask` and `#show` functions on a per page basis
+* [#2953](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2953) Added support for natural sort (`n-asc`, `n-desc`) of printout column values
+* [#2662](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2662) Added `+depth` as syntax component for a condition to restrict the depth of class and property hierarchy queries
+* [#2558](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2558) Added `like:` and `nlike:` comparator operator for approximate queries
+* [#2572](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2572) Added `@annotation` as special processing mode to embedded `#ask` queries
+* [#2673](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2673) Added the `Query state` special property to be able to track an internal state when a `#ask` uses `@annotation` or `@deferred` as special execution mode. In addition to internal usage, one can also now find all deferred queries with `{{#ask: [[Query state::200]] |format=ul }}`
+* [#2873](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2873) Added support for `in:` as expression to the #ask syntax
+* [#3125](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3125) Added support for `phrase:` as expression
+
+#### Result formats
+
+* [#2420](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2420) Added support for a datatable output in the `format=table` (and `broadtable`) result printer
+* [#2515](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2515) Added support for `#LOCL#TO` date formatting to display a [local time](https://www.semantic-mediawiki.org/wiki/Local_time) offset according to a user preferrence
+* [#2677](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2677) Added `+width` as parameter to the `format=table` (and `broadtable`) result printer
+* [#2690](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2690) Added the `type` parameter to `format=json` in support for a simple list export
+* [#2718](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2718) Added ad-hoc export for the `format=table` datatable
+* [#2824](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2824) Added `bom` as parameter to `format=csv`
+* [#2826](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2826) Added `valuesep` as parameter to `format=csv` to define a value separator
+* [#2822](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2822) Added add `merge` parameter to `format=csv`
+* [#2844](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2844) Renamed output formatter `#-ia` to `#-raw`
+* [#3024](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3024) Added `format=templatefile` to support individual export formats defined using MediaWiki templates
+* [#3009](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3009) Added `#tick` and `#num` output formatter to boolean value type
+* [#3011](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3011) Added the [`$smwgDefaultOutputFormatters`](https://www.semantic-mediawiki.org/wiki/Help:$smwgDefaultOutputFormatters) setting to declare default output formatter for a type or property
+* [#1315](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1315) Added support for media files to the `feed` printer
+* [#3130](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3130) Reworked `list` format
+* [#3162](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3162) Added support for `{{DISPLAYTITLE}}` to the `feed` printer
+* [#3136](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3136) Added `class` parameter to `list` format
+
+### API
+
+* [#2696](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2696) Added a new `smwbrowse` API module ([#2717](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2717), [#2719](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2719), [#2721](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2721))
+* [#3052](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3052) Added `api_version` to ask, askargs API
+* [#3129](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3129) Added API `pvalue` browse module
+* [#3381](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3381) Added API `psubject` browse module
+
+### Misc
+
+* [#794](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/794) Added `SMW_PARSER_UNSTRIP` to [`$smwgParserFeatures`](https://www.semantic-mediawiki.org/wiki/Help:$smwgParserFeatures) enabling to use unstripped content on a text annotation
+* [#2348](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2348) Allow showing annotations even if they are improper for datatype "Text"
+* [#2435](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2435) Added filtering of invisible characters (non-printable, shyness etc.) to armor against incorrect annotations
+* [#2453](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2453) Changed the approach on how referenced properties during an article delete are generated to optimize the update dispatcher
+* [#2471](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2471) Added [`SMW_CAT_REDIRECT`](https://www.semantic-mediawiki.org/wiki/Help:$smwgCategoryFeatures) option to allow finding redirects on categories
+* [#2494](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2494) Added [`$smwgChangePropagationProtection`](https://www.semantic-mediawiki.org/wiki/Help:$smwgChangePropagationProtection) and changed the approach on how property modifications are propagated
+* [#2543](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2543) Extended [`EditPageHelp`](https://www.semantic-mediawiki.org/wiki/Help:$smwgEnabledEditPageHelp) to be disabled using a user preference
+* [#2561](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2561) Added listing of improper assignments to the property page for an easier visual control
+* [#2595](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2595) Improved the content navigation in special page "SemanticMediaWiki"
+* [#2600](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2600) Added [`$smwgCreateProtectionRight`](https://www.semantic-mediawiki.org/wiki/Help:$smwgCreateProtectionRight) setting to control the creation of new properties and hereby annotations as part of the [authority mode](https://www.semantic-mediawiki.org/wiki/Help:Authority_mode)
+* [#2615](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2615) Added `filter=unapprove` to special page "WantedProperties"
+* [#2632](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2632) Added [uniqueness violation](https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness) check on the property page for the property label used
+* [#2699](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2699) Added an [input assistance](https://www.semantic-mediawiki.org/wiki/Help:Input_assistance) for the condition textbox on special page "Ask"
+* [#2726](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2726) Added entity [input assistance](https://www.semantic-mediawiki.org/wiki/Help:Input_assistance) for editors and the input field on special page "Search" ([#2756](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2756))
+* [#2776](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2776) Added tracking of changes to categories (see 2495)
+* [#2785](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2785) Added new styling to property page value list
+* [#2796](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2796) Allows "rendering of HTML" on special page "Ask" when using `|headers=plain` in queries
+* [#2801](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2801) Added `--skip-optimize` and `--skip-import` to `setupStore.php` (see 2516)
+* [#2803](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2803) Filter categories from transcluded content in `format=embedded`
+* [#2815](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2815) Added `#nowiki` support for external identifier type
+* [#2820](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2820) Added check on declarative property usage
+* [#2840](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2840) Added [`$smwgPropertyReservedNameList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgPropertyReservedNameList) to define reserved property names
+* [#2842](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2842) Added [`$smwgURITypeSchemeList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgURITypeSchemeList) to restrict valid URI scheme
+* [#2861](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2861) Added restriction for a property name that contains a CR, LF
+* [#2867](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2867) Added singular, plural category canonical check
+* [#2874](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2874) Added grouping support for properties to special page "Browse"
+* [#2875](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2875) Changed the theme on special page "Browse" to `smwb-theme-light`
+* [#2878](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2878) Added value filter to the property page
+* [#2883](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2883) Added function to special page "SemanticMediaWiki" to find duplicate entities
+* [#2889](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2889) Added method to make subobject sortkeys distinguishable
+* [#2891](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2891) Added flex (responsive) mode to special page "Ask" and special page "Browse" div table
+* [#2893](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2893) Changed special page "Ask" appearance ([#2898](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2898))
+* [#2895](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2895) Changed display of named subobject caption to appear without an underscore
+* [#2906](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2906) Added flex (responsive) mode to the factbox
+* [#2907](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2907) Added modal help to special page "Ask"
+* [#2913](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2913) Added a job queue watchlist feature and the [`$smwgJobQueueWatchlist`](https://www.semantic-mediawiki.org/wiki/Help:$smwgJobQueueWatchlist) setting
+* [#2922](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2922) Added `SMW_BROWSE_SHOW_SORTKEY` flag to the [`$smwgBrowseFeatures`](https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseFeatures) setting
+* [#2930](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2930) Added limit to value selection on the property page
+* [#2932](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2932) Added ["removeDuplicateEntities.php"](https://www.semantic-mediawiki.org/wiki/Help:Maintenance_script_removeDuplicateEntities.php) script to remove duplicate entities
+* [#2933](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2933) Added [`$smwgDefaultLoggerRole`](https://www.semantic-mediawiki.org/wiki/Help:$smwgDefaultLoggerRole) setting to define logging granularity for Semantic MediaWiki
+* [#2973](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2973) Set initial stats entry for non-fixed predefined properties
+* [#3017](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3017) Added the [`$smwgCompactLinkSupport`](https://www.semantic-mediawiki.org/wiki/Help:$smwgCompactLinkSupport) setting to compact links produced by special page "Ask" and special page "Browse"
+* [#3019](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3019) Added experimental support for the `SMW_NS_RULE` namespace
+* [#3020](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3020) Added the [keyword](https://www.semantic-mediawiki.org/wiki/Help:Type_Keyword) (`_keyw`) type
+* [#3029](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3029) Added function to keep updated entities in-memory to improve rebuild performance
+* [#3088](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3088) Modernized special page "Page property"
+* [#3167](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3167) Added support for `RemoteRequest` to share and consolidate query results from remote sources
+* [#3284](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3284) Added the `--dispose-outdated` flag to the "rebuildData.php" maintenance script
+* [#3289](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3289) Added support for the JSON format in the `Allows value list` definition
+* [#3292](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3292) Added support for bounded intervals, ranges in `Allows value` for number and quantity types
+* [#3293](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3293) Added tanslation page annotation (`_TRANS`) support
+* [#3308](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3308) Extended content representation on property and concept pages using tabs
+* [#3318](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3318) Added `smwgPostEditUpdate` to manage post edit event handling for seconday updates via the API interface
+* [#3319](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3319) Sets an extra parser key for queries that contain a self-reference to improve the result display after an edit event
+* [#3339](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3339) Added support for uniqueness validation in records/references
+* [#3416](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3416) Added support for `<section>` on property pages to put user-defined content into SMW-defined tabs
+* [#3415](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3415) Added "compact view" to hide query on special page "Ask"
+* [#3429](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3429) Changed default submit method of special page "Ask" to POST. Submit method can be modified by setting `$smwgSpecialAskFormSubmitMethod` to `SMW_SASK_SUBMIT_GET`, `SMW_SASK_SUBMIT_REDIRECT`, or explicitly setting the default `SMW_SASK_SUBMIT_POST`.
+* [#3431](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3431) Moved namespace "Rule" to namespace "smw/schema"
+* [#3436](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3436) When an entity is deleted, check for possible open references and keep the ID in case it has a residual reference by turning it into a simple object instance (setting `smw_rev` and `smw_proptable_hash` to null)
+* [#3440](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3440) Changed property pages to show the property usage count in the tab
+* [#3441](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3441) Added flags to maintenance script "rebuildData.php":
+ * `--revision-mode`: Skip entities where its associated revision matches the latests referenced revision of an associated page
+ * `--force-update`: Force an update even when an associated revision is known
+* [#3443](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3443) Changed job queue job names from `SMW\` prefix to `smw.` prefix. Example: `SMW\UpdateJob` -> `smw.update`
+
+## Bug fixes
+
+* [#481](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/481) Fixed "further results" link with special page "Ask" and templates
+* [#502](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/502) Fixed template with named arguments use in #show
+* [#839](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/839) Fixed and extended special page "Ask" to be more maintainable
+* [#2001](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2001) Fixed issue with `smw_subobject` and the generation of duplicate entities
+* [#2505](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2505) Fixed hard-coded default value for `format=csv`
+* [#2586](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2586) Fixed class assignments for empty cells in `format=table`
+* [#2621](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2621) Fixed sort/order field behaviour in special page "Ask"
+* [#2652](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2652) Fixed handling of multiple checkbox parameter in special page "Ask"
+* [#2817](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2817) Fixed Fix preg_replace ... unmatched parentheses
+* [#2871](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2871) Fixed PHP 7.2 `each` use in `SearchResultSet`
+* [#2881](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2881) Fixed display of display dispatched ID in `DataRebuilder`
+* [#2884](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2884) Fixed "Cannot use object of type MappingIterator as array"
+* [#2896](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2896) Fixed display of inverse indicator for translated predefined properties
+* [#2902](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2902) Fixed "LBFactory::getEmptyTransactionTicket ... does not have outer scope"
+* [#2909](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2909) Fixed use of `LBFactory::getEmptyTransactionTicket`
+* [#2915](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2915) Fixed connection instantiation
+* [#2917](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2917) Fixed "DataItemException ... Unserialization failed: the string ..."
+* [#2919](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2919) Fixed fetching all entities during a delete
+* [#2958](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2958) Fixed to mark subobject entities as done in `ExportController`
+* [#2963](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2963) Fixed recognition of `$wgDBadminuser` in maintenance script "setupStore.php"
+* [#2969](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2969) Fixed PHP 7.2 "Warning: count(): Parameter must be an array or an object that implements Countable" issue
+* [#3000](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3000) Fixed fetching namespace aliases
+* [#3010](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3010) Fixed breaking links in an abbreviated text by the `StringValueFormatter`
+* [#3025](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3025) Fixed storage of query information during a `preview` activity
+* [#3026](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3026) Fixed replacement of `smw_proptable_hash` during setup
+* [#3031](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3031) Fixed duplicate entry `smw_prop_stats` exception
+* [#3033](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3033) Fixed "The supplied ParserOptions are not safe ... set $forceParse = true" during the upload of files
+* [#3049](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3049) Fixed concept selection
+* [#3067](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3067) Fixed processing of simple links containing `|` during the in-text annotation parsing
+* [#3076](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3076) Fixed factbox magic works
+* [#3082](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3082) Fixed use of `ParserOptions::setEditSection` for MW 1.31
+* [#3107](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3107) Fixed recognition of `::=` in `LinksProcessor `
+* [#3134](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3134) Escape `/` in property names
+* [#3144](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3144) Return IDs as integer when matching all entities
+* [#3336](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3336) Fixed issue in special page "Ask" with sort parameter where the first parameter is left empty
+* [#3322](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3322) Fixed issue in `UpdateDispatcherJob` with selecting unrelated entities
+* [#3336](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3336) Fixed special page "Ask" to recognize first empty sort parameter as page title, e.g. `|sort=,Has foo`
+* [#3375](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3375) Fixed error "Invalid sort: title. Must be one of: relevance". MediaWiki default sort type is only `relevance`. SMW added `title`, `recent`, and `best`.
+* [#3389](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3389) Fixed "Error: 23505 ERROR: duplicate key value violates unique constraint "smw_new_pkey"" by setting SQL temporary table `id` field to type `SERIAL` instead of `INTEGERY PRIMARY KEY`
+* [#3393](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3393) Fixed MW 1.31+ highlighter issue causing extra inline `<p>` which added newlines to display
+* [#3413](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/3413) Fixed a performance issue for maintenance script "rebuildData.php" by doing `SELECT` on pages+namespaces rather than just pages.
+
+## Breaking changes and deprecations
+
+* [#1345](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/1345) Setting multiple values to the `#set` and `#subobject` paser functions using pipe `|` is deprecated. Use the `+sep` parameter instead.
+* [#2495](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2495) `Store::getPropertySubjects` and `Store::getAllPropertySubjects` will return an `Iterator` instead of just an array
+* [#2588](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2588) Removed special page "SemanticStatistics"
+* [#2611](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2611) Removed the user preference `smw-ask-otheroptions-collapsed-info`
+* [#2640](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2640) Removed `$smwgAutocompleteInSpecialAsk`
+* [#2659](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2659) Removed deprecated constant `SMWDataItem::TYPE_STRING` (replaced by `SMWDataItem::TYPE_BLOB`)
+* [#2696](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2696) Soft deprecate the `browsebyproperty` API module, the new `smwbrowse` should be used instead
+* [#2705](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2705) Removed usages of deprecated `ResultPrinter::getParameters`
+* [#2724](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2724) Added `$smwgUseComparableContentHash` and will be removed with 3.1 to help migrating subobject hash generation
+* [#2730](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2730) Replaced `$smwgCacheUsage` settings
+* [#2732](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2732) Replaced `$smwgQueryProfiler` settings
+* [#2748](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2748) Removed `ContextSource` from `ResultPrinter` instances
+* [#2750](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2750) Removed `$smwgSparqlDatabaseMaster` and `$smwfGetSparqlDatabase`
+* [#2752](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2752) Renamed `$smwgSparqlDatabaseConnector` to `$smwgSparqlRepositoryConnector`
+* [#2761](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2761) Renamed `$smwgDeclarationProperties`
+* [#2768](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2768) Changed default setting for `$smwgSparqlRepositoryConnector`
+* [#2788](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2788) Resources are now being exported as Internationalized Resource Identifiers (IRI) by default.
+* [#2790](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2790) Removed deprecated entry points for maintenance scripts
+* [#2802](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2802) Consolidated `$smwgParserFeatures` setting
+* [#2806](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2806) Consolidated `$smwgCategoryFeatures` setting
+* [#2821](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2821) Consolidated `smwgQSortFeatures` setting
+* [#2841](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2841) Replaced `$smwgLinksInValues` with the `SMW_PARSER_LINV` flag now maintained in `$smwgParserFeatures`, PCRE option has been removed
+* [#2880](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2880) Migrated special property message keys to new naming schema
+* [#2899](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2899) Removed `$smwgScriptPath`
+* [#2927](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2927) Removed `SEMANTIC_EXTENSION_TYPE` flag
+* [#2944](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2944) Removed deprecated methods in `SMW\DIProperty`
+* [#2961](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2961) Renamed `smwAddToRDFExport` hook
+* [#2995](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2995) Updated old namespace in Spanish
+* [#3164](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3164) Removed `SMW_NS_TYPE` ns and `$smwgHistoricTypeNamespace`
+* [#3231](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3231) Consolidated `$smwgPagingLimit` setting
+* [#3267](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3267) Removed `SMWQueryProcessor::getSortKeys`
+* [#3285](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3285) Deprecated API module `BrowseBySubject`, use `smwbrowse` instead
+* [#3307](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3307) Replaced `smwgCacheType` with `smwgMainCacheType`
+* [#3315](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3315) Consolidated `smwgSparqlEndpoint` sparql endpoint setting
+* [#3366](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3366) Replaced deprecated alias `SMWDIProperty` with `DIProperty` in `SMWDataValue`
+* [#3364](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3364) Removed long-deprecated static functions `SMWWikiPageValue::makePage` and `SMWWikiPageValue::makePageFromTitle`
+* [#3363](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3363) Removed deprecated `ResultPrinter::$m_params`. Use `ResultPrinter::$params` instead.
+* [#3399](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3399) Removed several functions deprecated since SMW 1.9 from `SMW\DataValueFactory`
+* [#3401](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3401) Removed long-deprecated functions `ResultPrinter::textDisplayParameters` and `ResultPrinter::exportFormatParameters`
+* [#3403](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3403) Removed long-deprecated function `SMWResultArray::getNextObject`
+* [#3405](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3405) Removed long-deprecated SMWDIString
+* [#3406](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3406) Removed long-deprecated function `SMWRecordValue::getDV`
+* [#3407](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3407) Removed deprecated global function `smwfIsSemanticsProcessed`
+
+## Other changes
+
+* [#2342](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2342) Added the display of invalid data value annotations for datatype "Text"
+* [#2485](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2485) Disabled updates by the `QueryDependencyLinksStore` on a 'stashedit' activity
+* [#2491](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2491) Added `ChunkedIterator` to `DataRebuilder` to avoid OOM situations in case of a large update queue
+* [#2535](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2535) Fixed property namespace (`_wpp`) display in `WikiPageValue`
+* [#2540](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2540) Added type `parser-html` to [`JSONScript`](https://www.semantic-mediawiki.org/wiki/Help:Integration_tests) testing to allow assertions on HTML structure
+* [#2591](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2591) Discontinued reading MediaWiki `job` table, use the `JobQueue::getQueueSizes` instead
+* [#2609](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2609) Added check to special page "Ask" to require JavaScript
+* [#2631](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2631) Disabled purge button while JS resources are still loaded
+* [#2650](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2650) Replaced some styles in special page "Ask"
+* [#2653](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2653) Fixed `broadtable` width with the "MobileFrontend" extension
+* [#2676](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2676) Added support for column default values in the `TableBuilder`
+* [#2680](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2680) Added `null_count` column to `PropertyStatisticsTable`
+* [#2691](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2691) Replaced `#info` icon set
+* [#2698](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2698) Added persistent caching to the `HierarchyLookup`
+* [#2714](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2714) Added `SMW::GetPreferences` hook
+* [#2727](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2727) Moved parameter processing from `QueryProcessor` to `ParamListProcessor`
+* [#2745](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2745) Moved `ResultPrinter` base class
+* [#2747](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2747) Moved `TableResultPrinter`
+* [#2751](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2751) Added `RecursiveTextProcessor` to isolate `$wgParser` access in `ResultPrinter`
+* [#2765](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2765) Added `SMW::Setup::AfterInitializationComplete` hook
+* [#2774](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2774) Moved `SMWQueryParser` to `SMW\Query\Parser`
+* [#2783](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2783) Added `JsonSchemaValidator`
+* [#2785](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2785) Moved `PropertyPage` and `ConceptPage`
+* [#2845](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2845) Extended use of cached hierarchy instance
+* [#2847](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2847) Introduced different approach to update query dependencies
+* [#2888](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2888) Introduced `Setup::initExtension` to allow an early registration of `SpecialPage_initList` and `ApiMain::moduleManager` hooks
+* [#2908](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2908) Refactored the `ConnectionProvider`
+* [#2928](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2928) Moved `SQLStore::fetchSemanticData` to `SemanticDataLookup`
+* [#2972](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/2972) Added `SMW::SQLStore::EntityReferenceCleanUpComplete` hook
+* [#3032](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3032) Added the `SMW::LinksUpdate::ApprovedUpdate` and `SMW::Parser::ChangeRevision` hook
+* [#3061](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3061) Added detection of changes emitted by the `BlockIpComplete`, `UnblockUserComplete`, and `UserGroupsChanged` hook
+* [#3063](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3063) Moved import files to data folder
+* [#3070](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3070) Added `SMW::Admin::TaskHandlerFactory` hook
+* [#3131](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3131) Added `CONTENT_MODEL_RULE` to be able to do schema validation before a save sometime in the future. Switching to an alternate model at a later stage would only create headaches.
+* [#3138](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3138) Fixed use of `$wgExtensionDirectory` to find SMW's "extension.json"
+* [#3146](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3147) Moved table hash cache handling
+* [#3160](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3160) Moved `FeedExportPrinter` and added integration test
+* [#3260](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3260) Moved `SMWSQLStore3::changeSMWPageID`
+* [#3275](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3275) Moved `SMWSQLStore3Readers::getPropertySubjects`
+* [#3282](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3282) Moved `SMWSQLStore3Readers::getProperties`
+* [#3384](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3384) Isolated the handling of "ALTER SEQUENCE ..." for Postgres
+* [#3432](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3432) Moved `SMW\CategoryResultPrinter` to `SMW\Query\ResultPrinters\CategoryResultPrinter`
+
+## Contributors
+
+- 1036 - James Hong Kong
+- 147 - translatewiki.net for the translator community
+- 120 - Karsten Hoffmeyer
+- 50 - Jeroen De Dauw
+- 13 - Stephan Gambke
+- 7 - Kumioko
+- 6 - Iván
+- 6 - Zoran Dori
+- 4 - James Montalvo
+- 4 - Máté Szabó
+- 2 - Jaider Andrade Ferreira
+- 2 - Josef Konrad
+- 2 - TK-999
+- 1 - Amir E. Aharoni
+- 1 - C. Scott Ananian
+- 1 - Kunal Mehta
+- 1 - Peter Grassberger
+- 1 - Prateek Saxena
+- 1 - Stephan
+- 1 - Thiemo Kreuz
+- 1 - Timo Tijhof
+- 1 - Toni Hermoso Pulido
+- 1 - ka7
+- 1 - matthew-a-thompson
+- 1 - salle
+- 1 - غلامحسین حق دوست
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.1.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.1.md
new file mode 100644
index 00000000..7cdbfea1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.1.md
@@ -0,0 +1,39 @@
+# Semantic MediaWiki 3.0.1
+
+Released on January 25, 2019.
+
+## Enhancements
+* [#3566](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3566) as `af04255`: Extended the array of permissive URI schemes of [configuration parameter `$smwgURITypeSchemeList`](https://www.semantic-mediawiki.org/wiki/Help:$smwgURITypeSchemeList)
+* [#3596](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3596) as `a6ccc2a`: Added [`$smwgConfigFileDir` configuration parameter](https://www.semantic-mediawiki.org/wiki/Help:$smwgConfigFileDir) allowing to specify the location for the [setup information file](https://www.semantic-mediawiki.org/wiki/Help:Setup_information_file)
+* [#3597](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3597) as `c92b2ca`: Extended and improved information on the ["Upgrade Error Screen"](https://www.semantic-mediawiki.org/wiki/Help:Upgrade/Upgrade_and_setup_consistency) and made it localizable
+* [#3611](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3611) as `8f1177a`: Added ["populateHashField.php" maintenance script](https://www.semantic-mediawiki.org/wiki/Help:Maintenance_script_populateHashField.php) to decouple mass conversions of database field "smw_hash" in the "smw_objects_ids" database table when upgrading the database for large wikis
+* Many new translations for numerous languages by the communtity of [translatewiki.net](https://translatewiki.net/w/i.php?title=Special%3AMessageGroupStats&x=D&group=mwgithub-semanticmediawiki&suppressempty=1)
+
+## Bug fixes and internal code changes
+* [#3565](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3565) as `6f24bf6`: Added missing system message for the "templatefile" format
+* [#3572](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3572) as `70f629e`: Fixed `HtmlForm::getForm` to support a string as result on special page "Ask"
+* [#3573](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3573) as `a59c76c`: Modified tests to avoid "Call to a member function getSchema() on null" for MediaWiki 1.32 and later
+* [#3578](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3578) as `484a4b5`: Made indexer apply `pg_unescape_bytea` for bytea/blob values on postgres
+* [#3584](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3584) as `1205b87`: Added pipe detection in printrequest labels (`[[ ... | ... ]]`)
+* [#3585](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3585) as `5d6d6ff`: Fixed "`strpos()`: Non-string needles ..." for PHP 7.3 and later
+* [#3586](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3586) as `53655ed`: Fixed `#set_recurring_event` parser function to respect related configuration parameters and their settings
+* [#3595](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3595) as `e9ed65e`: Fixed invalid user names using the mandatory interwiki prefix for MediaWiki 1.31 and later by unlinking them
+* [#3599](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3599) as `732ef23`: Fixed "`fputcsv` ... delimiter must be a single character" for the "csv" format
+* [#3607](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3607) as `4d9e5a7`: Fixed `#set_recurring_event` parser function to cause "Call to undefined method `SMWDIError::getJD()`"
+* [#3608](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3608) as `b17526d`: Fixed "QueryResultSerializer" to handle `_qty` on chained properties
+* [#3609](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3609) as `c005c6f`: Restored use of `$wgDBTableOptions` configuration parameter
+* [#3616](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3616) as `4b0cfb7`: Made `isCapitalLinks` be set in `_wpg` description context
+* [#3617](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3617) as `9152f94`: Made "SemanticDataLookup", use `DISTINCT` for non subject items
+* [#3622](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3622) as `cfbd338`: Fixed `#set_recurring_event` parser function to allow monthly events start on a 30th and 31st of a month
+* [#3628](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3628) as `e587291`: Improved commandline prompts for maintenance script "populateHashField.php"
+* [#3630](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3630) as `79aee30`: Added extra _uri validation for `http:///`
+* [#3631](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3631) as `0903c1b`: Fixed `ResultFormatNotFoundException` on untrimmed format strings
+* [#3632](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3632) as `24d8bae`: Changed to using `0x003D` instead of `-3D` to encode `=`
+* [#3633](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3633) as `f70339a`: Made the container subject be used as context to check uniqueness constaints
+* [#3634](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3634) as `711e365`: Made "WikiPageValue" use the provided fixed namespace
+* [a5a4a0d](https://github.com/SemanticMediaWiki/SemanticMediaWiki/commit/a5a4a0d1b05eb622749fe59a1d2be4be699aaed4) as `bea16a5`: Fixed "PHP Notice: Uncommitted DB writes (transaction from ...)"
+* [8bc4443](https://github.com/SemanticMediaWiki/SemanticMediaWiki/commit/8bc4443a6a48682e74e94a014adfcd91cb6104a5) as `5a729d4`: Fixed `get_headers` can return `false`
+* [8ca1ec0](https://github.com/SemanticMediaWiki/SemanticMediaWiki/commit/8ca1ec05ef56144b1991c0381696a52687e39ed4) as `93cf100`: Made "PHP Warning: Class '`SMW\CategoryResultPrinter`' not found in ... Aliases.php" be avoided
+
+## See also
+* [RELEASE NOTES](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/3.0.x/docs/releasenotes/RELEASE-NOTES-3.0.0.md) for Semantic MediaWiki 3.0.0
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.2.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.2.md
new file mode 100644
index 00000000..36557b5c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-3.0.2.md
@@ -0,0 +1,24 @@
+# Semantic MediaWiki 3.0.2
+
+Released on April 11, 2019.
+
+## Enhancements
+
+* [#3682](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3682) as `f5d0cab`: Removed `IsFileCacheable` hook and improves file caching
+* [#3856](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3854) as `767c981`: Switched setting of configuration parameter "$smwgCompactLinkSupport" to "false"
+
+## Bug fixes and internal code changes
+
+* [#3742](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3742): Fixed raw output of templated message particles
+* [#3771](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3771) as `b7a78e0`: Removed `doPostOutputShutdown`
+* [#3772](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3772) as `79e4adb`: Fixed "... expects parameter 1 to be a valid callback ..."
+* [#3775](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3775) as `3b0d83c`: Made data type "Equivalent URI" non declarative
+* [#3847](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3847) as `d088204`: Fixed warning "a non-numeric value" was encountered
+* [#3854](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3854) as `d05671e`: Provided a new test case for "display title"
+* [#3859](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3859): Updated "InfolinkTest"
+* [#3863](https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/3863): Fixed resource loading by replacing `localpath` with `localBasePath`
+* [0ed4bbf7](https://github.com/SemanticMediaWiki/SemanticMediaWiki/commit/0ed4bbf75e7ee9989d2ac84437d3733b52885eb8): Updated "EventHandlerTest"
+
+## See also
+* [RELEASE NOTES](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/3.0.x/docs/releasenotes/RELEASE-NOTES-3.0.0.md) for Semantic MediaWiki 3.0.0
+* [RELEASE NOTES](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/3.0.x/docs/releasenotes/RELEASE-NOTES-3.0.1.md) for Semantic MediaWiki 3.0.1
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-old.md b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-old.md
new file mode 100644
index 00000000..25c3381d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/releasenotes/RELEASE-NOTES-old.md
@@ -0,0 +1,723 @@
+These are the release notes of SMW up to version 1.5.6.
+For version 1.6 and later, see the dedicated files in this directory.
+
+== SMW 1.5.6 ==
+
+* Automatic refresh of pages after parsing their semantic data.
+* Fixed several bugs including non-functional concept caching,
+ correct internationalization support for categories and concepts
+ in queries and types display on Special:Types for MW >= 1.18.
+
+== SMW 1.5.5 ==
+
+* Support for Turtle syntax (e.g. use "syntax=turtle" as a parameter to
+ when calling Special:ExportRDF.
+* New query format "rdf" to export query results to RDF; the "syntax"
+ parameter can be set to "rdfxml" or "turtle" to specify a syntax.
+* Fixed several bugs, including whitespace problems in queries using the
+ list format, an issue with large offset values and DatatypeProperties
+ being declared as ObjectProperties.
+* More modular export code (split one file into 3), simplifies
+ manipulation and maintenance.
+
+== SMW 1.5.4 ==
+
+* Fixed several bugs, including problems with #declare, the extra options
+ on Special:Ask and the style of the factbox and tables when using MW 1.17.
+* Various style and documentation improvements and translation updates.
+
+== SMW 1.5.3 ==
+
+* Compatibility with MediaWiki 1.17
+* Added << and >> comparators for strict comparisons. Also added ≤ and ≥ for
+ non-strict comparisons. And introduced $smwStrictComparators setting that
+ allows changing the behavior of < and > to doing strict comparisons.
+* Various style and documentation improvements and translation updates.
+
+== SMW 1.5.2 ==
+
+* 'Printouts' textarea in Special:Ask now has autocompletion on property
+ names.
+* SMW now has it's own copy of the jQuery and jQuery UI libraries.
+* Added a number of hooks to allow further customization by extensions.
+* 'Semantic' extension type that can be used by extensions to SMW to be
+ grouped together on Special:Version and other pages listing extensions.
+* Various bugfixes, style and documentation improvements and translation updates.
+
+== SMW 1.5.1 ==
+
+* Added query comparator "!~" that works like the negation of "~", i.e. that
+ retrieves exactly those values to which the provided pattern (with * and ?)
+ does not match.
+* Updated OWL/RDF export to include more data (modification dates, and page
+ namespaces). Changed export ontology to no longer use OWL AnnoationProperties
+ since OWL 2 DL supports property values also for classes/properties.
+* Improvements in code style and structure, following general MediaWiki
+ conventions. Most notably, the file to include for loading SMW now is
+ SemanticMediaWiki.php in the base directory (including the old Settings.php
+ will still work).
+* Preparation of future "SMWLight" release for wikis that want to enable users
+ to add property values to pages, but which do not want to include complex
+ query features.
+* Added a nmber of hooks to improve compatibility with extensions.
+* Various bugfixes, and translation updates.
+
+== SMW 1.5.0 ==
+
+* The former multi-valued/n-ary properties are now supported by a dedicated
+ datatype Record. Wikis that use the old style of these properties need to
+ update affected property pages as described at
+ http://semantic-mediawiki.org/wiki/SMW_1.5.0
+* The Type:Geographic_coordinates has been moved from SMW to the SemanticMaps
+ extension. Wikis that use this datatype need to install this extension to get
+ it back.
+* The "like" comparator ~ in inline queries is now enabled by default. To use
+ the former setting, use $smwgQComparators = '<|>|!'; in your LocalSettings.php.
+* A new datatype, "Telephone number", has been introduced for validating phone
+ numbers based on RFC 3966. Only global phone numbers are accepted, and no
+ vanity numbers (those containing letters) are allowed. Use Type:String if
+ you want to store more general strings as telephone numbers.
+* Support for additional parameters for ?printouts in inline queries, specified
+ by |+parameter=value after the printout. Currently supported are
+ * limit: set the maximum number of values for this printout
+ * order: order values ascending or descending
+ * align: right/center/left alignment for printouts in tables
+ * index: directly address one value of a multi-valued (n-ary) property
+* Support for inverse properties: use "-" in front of property names anywhere to
+ refer to the inverse direction of a property. Works in browsing interfaces,
+ queries, and query output directives. In queries only for properties of Type:Page.
+* New configuration options $smwgUseCategoryHierarchy and $smwgCategoriesAsInstances
+ to configure how MediaWiki categories should be interpreted by SMW.
+* Compatibility with new MediaWiki skin "Vector" and with MediaWiki 1.16.
+* Removed support for backwards compatibility to SMW <1.0 ($smwgSMWBetaCompatible).
+* The page Special:Ask was overhauled to include inputs for entering values
+ for the selected format's parameters, and to use Ajax where possible
+* To that end, each format's query printer now includes a getParameters()
+ function that supplies the name and attributes of each the format's parameters
+* "before" and "after" hooks for updateData() and deleteSubject() were added
+* A hook was added for deleteSemanticData() as well
+
+== SMW 1.4.3 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.4.3
+
+* A new query format, 'category', that displays values in the style of a
+ MediaWiki category page
+* A new 'columns' parameter for the 'ol' and 'ul' formats
+* The 'csv' format now prints a header row by default
+* The "::~" comparator in queries can now also be used for properties of type
+ Geographical coordinate, to find points within a certain distance
+* Using "-" as an output format for query printouts, or leaving the formatting
+ string empty now leads to printout values being returned as plain, unformatted
+ values. Recall that the general format for printouts in #ask is as follows:
+ ?propertyname # format = label
+* New special property "Has improper value for" can be used to track input errors
+ (links pages where error happened to properties for which improper data was given).
+* New configuration parameter $smwgMaxNonExpNumber to set the maximal number that
+ SMW will normally display without using scientific exp notation. Defaults to
+ 1000000000000000.
+* New configuration parameter $smwgMaxPropertyValues to control number of values
+ that are shown for each page in the listing on Property pages. Defaults to 3.
+* Now a set of stores can be added, e.g. virtual stores, and then the new #ask
+ parameter "source" is used to rout to a specific store.
+* Labels for inverse properties can be used to improve the output of the Browse
+ special page.
+* The #ask parameter "headers" can now be given the value "plain" to get query
+ results that show the printout label (e.g. property name) but without a link.
+* Special:PageProperty now shows all values of a property when omitting the subject.
+* Numerous bugfixes
+
+== SMW 1.4.2 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.4.2
+
+* Improved performance. Significantly improved memory usage on common
+ operations.
+* Initial support for running SMW on PostgreSQL (testing needed).
+* The query formats 'vcard' and 'icalendar' have been moved to the Semantic
+ Result Formats extension to make the SMW core more light-weight
+* SMW_refreshData.php now supports parameter --page to specify one or more
+ pages to be updated (instead of using ID ranges)
+* Various bugfixes
+* Extended translations
+
+== SMW 1.4.1 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.4.1
+
+* Improved parsing support for Type:Date, fixing some open issues
+* Various bugfixes
+* Extended translations
+
+== SMW 1.4.0 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.4.0
+
+* Easier installation, upgrade, repair:
+ Special:SMWAdmin now has a control for refreshing the wiki data online.
+* Better Type:Date
+ ** much larger range of dates, covering all of human history
+ ** internationalisation, support for localised date formats in input
+ ** support for single year numbers in dates
+ ** incomplete dates handled properly
+* Query for page modification date using Property:Modification_date
+* Full integration of "special properties" such as Property:Has_type
+ ** can be queried in #ask, and printed in printouts
+ ** usable in all browsing interfaces
+ ** display uses of properties on property page
+* Improved parsing process
+ ** avoid data loss in unusual circumstances
+ ** much better compatibility with other parser extensions
+* Shorter and cleaner code
+* Extended translations
+* Many bugfixes
+
+== Semantic MediaWiki 1.3 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.3
+
+* New design of Special:Browse: more concise and less cluttered
+* Fuzzy search on Special:SearchByProperty: if an exact value is not found
+ or yields only few results, pages with close-by values are also shown.
+* Better result browsing on concept pages. Such pages now look like category
+ pages and show results in columns.
+* New mechanism for pre-computing concepts for very fast result display even
+ fomr complicated queries. Relevant for wikis with large amounts of data.
+ Details are documented at
+ http://semantic-mediawiki.org/wiki/Help:Concept_caching
+* Extended translations, support for aliases for special pages (translated
+ names used in URLs).
+* Various bugfixes and internal improvements for performance and robustness.
+
+== Semantic MediaWiki 1.2.2 ==
+
+Minor release with bugfixes:
+* in rare cases undelete would create errors on pages
+* inverse links for binary properties were displayed wrongly
+* some additional checks to make parsing more reliable
+
+== Semantic MediaWiki 1.2.1 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.2.1
+
+* New sub-property display for property pages
+* Improved number parsing, admitting arbitrary spaces in numbers
+ (required for French)
+* Extended translations
+* Update job generation actually works now
+* Query format "csv" to export data as file of comma-separated values
+* Prevent crashes for very long property values (due to PHP PCRE)
+* Various other bugfixes
+
+
+== Semantic MediaWiki 1.2 ==
+
+See http://semantic-mediawiki.org/wiki/SMW_1.2
+
+* New SMW storage backend (SMWSQLStore2)
+ ** faster for queries and page display/rendering
+ ** full equality support built-in, no performance impact
+ ** support for disjunctions in queries (keyword "OR")
+* vCard export for query results
+* Improved semantic query syntax and processing
+ ** shortcut query syntax #show for displaying properties of
+ single pages, e.g. {{#show: Berlin | ?population}}
+ ** property chains like [[property1.property2::value]]
+ ** more detailed control of which query features to support
+ (see setting $smwgQFeatures in SMW_Settings.php)
+* Support for custom sortkey to control alphabetic sorting of
+ all pages, using MediaWiki's {{DEFAULTSORTKEY: custom key}}
+* Support for semantic interwiki links (e.g. [[property::meta:Test]])
+* Stored queries on Concept: pages (concepts as "dynamic categories"),
+ see http://semantic-mediawiki.org/wiki/Help:Concepts
+* Automated updates: changes in templates and property definitions
+ are automatically applied to affected pages (after some time)
+* Extended maintenance scripts
+ ** delete an existing (now unused) SMW store with SMW_setup --delete
+ ** select SMW storage engine to use for scripts with option -b <Store>
+ ** SMW_dumpRDF now supports restriction to concepts or concepts+categories
+* SMW <1.0 features disabled by default (remove obsolete features),
+ can be re-enabled with $smwgSMWBetaCompatible.
+* Compatible with Semantic Forms 1.2.3 and MediaWiki 1.13 (current devel
+ version)
+
+
+== Semantic MediaWiki 1.1.2 ==
+
+* Security update to avoid vulnerabilities on sites that use PHP
+ superglobals
+
+== Semantic MediaWiki 1.1.1 ==
+
+* Improved iCalendar support: more efficient processing, better heuristic
+ for giving dates, formatting bugfixes
+* Bugfix for Special:SearchByProperty (form-based search failed)
+* Minor bugfixes for RSS generation
+* Bugfix for Special_Ask in MediaWiki 1.11 with template formatting
+* Maintenance scripts now accept the MW_INSTALL_PATH environment variable
+
+== Semantic MediaWiki 1.1 ==
+
+* Support for formatted results on Special:Ask. "Further results" links
+ from inline queries now preserve format.
+* New iCalendar export for inline queries (format=icalendar)
+* Query results can now be sorted by more than one property (just separate
+ property names with "," in sort parameter)
+* Initial support (beta) for synching external RDF stores with SMW.
+ This also provides support for wiki-based SPARQL query services, see
+ http://semantic-mediawiki.org/wiki/Help:SPARQL_endpoint
+* More robust link generation code; even long query texts and links
+ that contain very special characters are built properly.
+* Extended translations. Completely new Arab translation.
+* New SMW registry http://semantic-mediawiki.org/wiki/Special:SMWRegistry
+ to replace hand-crafted list of "sites using SMW".
+* Various bugfixes. For example:
+ ** Enumerated properties (allows value) for Type:Page works now.
+ ** Page moves are handled more reliably
+
+== Semantic MediaWiki 1.0.1 ==
+
+* Extended translations, new Italian translation
+* minor bugfixes, fixed RSS links for long description texts
+
+== Changes in SMW1.0 as compared to SMW0.7 ==
+
+* Simplified semantic annotations: just one kind of annotation ("Property").
+* Significant speedup (both server and network load substantially reduced,
+ faster RDF export, more efficient query result formatting).
+* Prettier and easier to understand interfaces:
+ ** New tooltips for warnings and additional information.
+ ** Simplified factbox layout, with all properties in alphabetic order.
+ ** Inline warnings to simplify trouble shooting with annotations.
+ ** Improved, more helpful and informative warning and error messages.
+ ** Highlighting for built-in elements. E.g. built-in types are visually
+ distinguished from arbitrary types; useful as visual feedback.
+ ** Error/warning reporting for (inline) queries.
+* More powerful output formatting for semantic querying:
+ ** new {{#ask:...}} parser function syntax for inline queries, fully
+ compatible with MediaWiki templates, template parameters, and parser
+ functions of other extension
+ ** more readable inline query structure in #ask parser function,
+ printouts separated from query
+ ** semantic RSS feeds making feeds from query results via "format=rss"
+ ** new printout format "?Category:Name" for #ask
+ ** option to hide main column by setting "mainlabel=-", and reinserting
+ via print request "?" (only for #ask)
+* More expressivity for semantic querying:
+ ** support for subproperties,
+ ** improved equality resolution (redirects),
+ ** support for disjunctions,
+ ** inequality check for datavalues ("[[property::!value]]")
+ ** optional pattern matching for string values ("[[property::~Semant*]]")
+ ** automatic sorting on sort-parameter (no additional condition needed)
+* New/improved datatypes:
+ ** Type:Page for explicitly specifying properties that are "relations"
+ ** better media support in Type:Page: special treatment of Image: and Media:
+ ** Type:Number as universal replacement for Type:Integer and Type:Float
+ ** Type:URL as universal replacement for old Type:URL and Type:URI
+ ** Type:Geographic coordinates completely rewritten. More input formats
+ supported, more liberal parsing to accept most inputs
+ ** special property "allows value" works for all types
+ ** display units are now easier to select via property "display units"
+ ** Improved data display: URL-links and tooltips work for queries results
+ and on special pages
+* Improved special pages:
+ ** simpler interface for Special:Ask, hide query when using "further results" link
+ ** hints and warning for property usage/declaration in Special:Properties
+ ** extra information and warnings for types on Special:Types
+ ** Special:SemanticStatistics as faster replacement for earlier "ExtendedStatistics"
+* Better internationalisation:
+ ** updates in all translation files
+ ** new translations to Dutch, Chinese (tw/ch), Korean (beta)
+ ** alias strings for all SMW elements; English labels are allowed in all
+ languages, names of old SMW elements still work as aliases for their
+ replacements.
+* New experimental n-ary properties, allowing property values to consist of
+ a list of entries.
+* Ontology import re-enabled (simple annotation import)
+* Maintenance script SMW_refreshData now can rebuild all SMW data structures, fixing
+ even exotic database problems on most sites.
+* New maintenance script for announcing site to Semantic Web crawlers.
+* Support for upcoming MediaWiki 1.12
+* Improved APIs and various new hooks to simplify the life of SMW extension developers.
+* Many bugfixes.
+
+Other changes for SMW1.0 include:
+* Type:Enum became obsolete, since all types now suppport "allows value", but it
+ remains an alias for Type:String.
+* Some configuration options for LocalSettings.php have changed. Read INSTALL
+ for details on how to upgrade from your old installation.
+
+
+== Semantic MediaWiki post 1.0RC3 ==
+
+* Support for dynamic, query-generated RSS-feeds via query format "rss".
+* Optional query feature for pattern matching in Type:String property values.
+* Correct dynamic sorting of result tables, even for dates and numerical values.
+* Thumbnail images when displaying property values from Image namespace.
+* Simplified use of "sort" parameter in queries.
+* Support for upcoming MediaWiki 1.12 (major parser changes).
+* More efficient link generation in query results. Link all query results by
+ default now.
+* Maintenance script SMW_refreshData now can rebuild all SMW data structures,
+ fixing even exotic database problems on most sites.
+* New maintenance script for announcing site to Semantic Web crawlers.
+* Various bugfixes.
+
+== Semantic MediaWiki 1.0RC3 ==
+
+* New method for integrating inline queries via #ask parser function, separation of
+ query and printout requests, full compatibility with templates.
+* New layout for Special:Ask to reflect #ask structure.
+* New printout option: "?Category:Name" to ask for membership in that category.
+* Re-enabled service links (e.g. use [[provides service::online maps]] on any page of
+ a property to Type:Geographic coordinates).
+* Re-enabled Type:Boolean.
+* Prototype translation for Korean (still alpha).
+* Various minor bugfixes.
+
+== Semantic MediaWiki 1.0RC2 ==
+
+* Experimental Postgres support.
+* More liberal parsing for geographic coordinates, most user inputs accepted now.
+* Improved URL datatype: better linking behavior, tolerant towards Unicode-URLs.
+* Significantly improved performance for RDF export.
+* Complete translations for Fr, Zh-tw, and Zh-ch added.
+* Various minor bugfixes.
+
+== Semantic MediaWiki 1.0RC1 ==
+
+* Simplified semantic annotations: just one kind of annotation ("Property").
+* Significant speedup (both server and network load substantially reduced).
+* Prettier and easier to understand interfaces:
+ ** New tooltips that work on both normal and special pages.
+ ** Simplified factbox layout, with all properties in alphabetic order.
+ ** Inline warnings to simplify trouble shooting with annotations.
+ ** Improved, more helpful and informative warning and error messages.
+ ** Highlighting for built-in elements. E.g. built-in types are visually
+ distinguished from arbitrary types; useful as visual feedback.
+ ** Error/warning reporting for (inline) queries.
+* Alias strings for all SMW elements. English labels are allowed in all
+ languages, names of old SMW elements still work as aliases for their
+ replacements.
+* More expressivity for semantic querying:
+ ** support for subproperties,
+ ** improved equality resolution (redirects),
+ ** support for disjunctions,
+ ** inequality check for datavalues ("[[property::!value]]")
+* New/improved datatypes:
+ ** Type:Page for explicitly specifying properties that are "relations"
+ ** Type:Number as universal replacement for Type:Integer and Type:Float
+ ** Type:URL as universal replacement for old Type:URL and Type:URI
+ ** Type:Geographic coordinates completely rewritten. More input formats
+ supported now (e.g. coordinates without "," separating Lat and Long)
+ ** special property "allows value" works for all types
+ ** display units are now easier to select via property "display units"
+ ** Improved data display: linked URLs and tooltips work for queries and
+ special pages
+* Improved maintenance special pages:
+ ** Hints and warning for property usage/declaration in Special:Properties
+ ** Extra information and warnings for types on Special:Types
+ ** Special:SemanticStatistics as faster replacement for earlier "ExtendedStatistics"
+* New experimental n-ary properties, allowing property values to consist of
+ a list of entries.
+* Ontology import re-enabled (simple annotation import)
+* Dutch translation added (by Siebrand Mazeland)
+* Improved APIs and various new hooks to simplify the life of SMW extension developers.
+* Many bugfixes.
+
+Other changes for the RC1 include:
+* No more support for Type:Boolean. Will be re-enabled later.
+* Type:Enum became obsolete, since all types now suppport "allows value", but it
+ remains an alias for Type:String.
+* Service links are not working in this Release Candidate yet, especially coordinate
+ values do not link to maps yet. This will reappear before SMW1.0 final.
+
+
+== Semantic MediaWiki 0.7 ==
+
+* New browsing interface for semantic data: Special:Browse
+* Improved simple searching interfaces, making the old Special:Searchtriple
+ obsolete by various new interlinked special pages.
+* New formatting options for inline queries:
+** Template-based formatting for formats "list" and "template"
+** Transclusion of result articles with format "embedded"
+** Counting query results with format count.
+* New datatype for enumerated string values (Type:Enum).
+* Pages of attributes and relations now list all uses of these properties.
+* Pages of types now list all attributes using a type.
+* New Special:WantedRelations showing relations that are used but have no page.
+* Improved support for arbitrary symbols in string values, including wiki links
+ and HTML entities (now correct in RDF).
+* Improved headers for query tables, with sort icon and link to attribute/relation
+ separated.
+* Added maintenance script to rebuild semantic data, thus fixing any inconsistencies
+ in the semantic database that may have occurred earlier or due to text-only imports
+ of pages.
+* Translations to further languages, including Hebrew (right-to-left).
+* New cleaner storage implementation, allowing to run MediaWiki parsertests with the
+ option $smwgDefaultStore = SMW_STORE_TESTING; in LocalSettings.php.
+* MediaWiki-1.10-Ready ;-)
+* Simplified installation (no more manual patching with MediaWiki 1.10).
+* Many bugfixes.
+
+== Semantic MediaWiki 0.6 ==
+
+* New Special:Ask for directly browsing query results and for testing queries.
+* New output format "timeline" for inline queries that deal with dates. Available
+ parameters are: timelinestart (name of start date attribute), timelineend (name
+ of end date attribute, if any), tiemlinesize (CSS-encoded height), timelinebands
+ (comma-separated list of bands such as DAY, WEEK, MONTH, YEAR, ...), and
+ timelineposition (one of start, end, today, middle).
+* Complete RDF export is now possible with a maintenance script, which can e.g. be
+ run periodically on a server to create RDF files.
+* New "service links" feature: any attribute can provide configurable links to
+ online services. As a special case, the map-services of geo-coordinates are now
+ fully configurable.
+* Inline queries now link to life search for further results if not all results
+ were shown inline.
+* The formatting code for inline queries was rewritten to become more powerful.
+ For instance, multi-property outputs in list format will never produce empty
+ parentheses now.
+* RDF-export code is cleaner and some further OWL DL incompatibilities are caught.
+* RDF-export now can generate browsable RDF (with backlinks) even for Category
+ pages.
+* Improved headers for sorting tables. Sort icon now visible even if no text is
+ shown in header.
+* Many bugfixes.
+
+== Semantic MediaWiki 0.5 ==
+
+* Customised datatypes for unit conversion: it is now possible to create customised
+ linear unit conversions by appropriate statements on type articles. This also
+ enables full localisation of all units of measurement.
+* Customized display of units: every attribute can now decide which units to display
+ in factbox and query results. Internally, values are still normalised, but users
+ can adjust the view to the most common description of some attribute.
+* Support for importing vocabularies from external ontologies. For instance, elements
+ of the wiki can now be mapped to the FOAF ontology during export. The import is
+ controlled by whitelist-like message articles.
+* New attribute datatypes for URLs and URIs, some of which can be exported in RDF as
+ ObjectProperties. A blacklist is used to prevent technically problematic URIs from
+ being used there (e.g. most don't want to use OWL language elements as data).
+* New attribute datatype for temperature, since this cannot be defined by a linear
+ custom unit conversion.
+* Improved Special:Relations and Special:Attributes, including a quicklink to searching
+ occurrences of some annotation.
+* Unit support for inline queries. Desired output unit can be adjusted through query.
+* Improved code layout, using object-orientation features of PHP5.
+* Many bugfixes.
+
+== Semantic MediaWiki 0.4.3.2 ==
+
+This fixes another small bug that prevented the limit parameter in queries to work
+properly.
+
+== Semantic MediaWiki 0.4.3.1 ==
+
+This fixes a small but critical bug that prevented queries for non-numeric attribute
+values to work properly.
+
+== Semantic MediaWiki 0.4.3 ==
+
+Semantic MediaWiki 0.4.3 is an intermediate release that greatly improves the
+performance and features of inline queries. It also includes some major code
+cleanups and various bugfixes.
+
+An updated documentation of the current inline queries is (soon) to be found at
+http://semantic-mediawiki.org/wiki/Help:Inline_queries
+
+== Semantic MediaWiki 0.4 ==
+
+Semantic MediaWiki 0.4 includes the following new features:
+
+* Support for inline queries: it is now possible to <ask> queries in
+ articles, the answers of which are included into the displayed page.
+ Conjunctions and nesting of queries is supported. Datatype queries
+ for values above or below some threshold are possible. Outputs can be
+ displayed in many different formats, including bulleted and numbered
+ lists, tables with intercative (JScript) sorting (credits go to
+ Stuart Langridge for www.kryogenix.org/code/browser/sorttable/), and
+ plain text. See http://semantic-mediawiki.org/wiki/Help:Inline_queries
+ for documentation.
+* Improved output for Special:Relations and Special:Attributes: usage of
+ relations and attributes is now counted
+* Improved ontology import feature, allowing to import ontologies and to
+ update existing pages with new ontological information
+* Experimental support for date/time datatype
+* More datatypes with units: mass and time duration
+* Support for EXP-notation with numbers, as e.g. 2.345e13. Improved number
+ formatting in infobox.
+* Configurable infobox: infobox can be hidden if empty, or switched off
+ completely. This also works around a bug with MediaWiki galeries.
+* Prototype version of Special:Types, showing all available datatypes with
+ their names in the current language setting.
+* "[[:located in::Paris]]" will now be rendered as "located in [[Paris]]"
+* More efficient storage: changed database layout, indexes for fast search
+* Code cleaned up, new style guidelines
+* Bugfixes, bugfixes, and some more bugfixes
+
+Semantic MediaWiki 0.4 has not been tested on MediaWiki below 1.6.1 and might
+fail to operate correctly in this case. Some functions explicitly use code
+that was introduced in 1.6.
+
+== Semantic MediaWiki 0.3 ==
+
+Changes by mak (0.3, 06 Apr 2006):
+
+* Compatibility updates for MediaWiki 1.6
+
+Changes by denny/mak (0.3, 25 Mar 2006):
+
+* Internal: improved management of special properties
+* RDF export: OWL conformant export of all available content data, including category information
+* RDF export: recursive export, "streaming"
+* UI: further internationalization, internationalized float number format (decimal separator)
+* UI: new infobox section for recognized special properties
+* new Specials to show all relations/attibutes
+* new experimental Special to import data from existing OWL/RDF ontologies
+* new special property "equivalent URI" that allows to map wiki concepts to URIs in other ontologies
+
+Changes by kai/mak (0.3preview, 15 Mar 2006):
+
+* Internal: new internal storage management; cleaner, more flexible, and more efficient
+* Internal: new internal type registration API
+* Internal: new internal management for special properties (e.g. 'has type')
+* Internationalization: almost complete; namespaces, special properties (e.g. 'has type'), datatype labels
+* RDF export: support for multiple mimetypes (rdf+xml and xml); needed for Piggybank
+* RDF export: support for bulk export
+* RDF export: XSD datatypes and correct instance classification (rdf:type)
+* UI: extended Special:SMWAdmin to convert data from old internal datatable to new format
+* UI: duplicate attribute values eliminated in infobox
+* UI: types can switch off quicksearch links
+* UI: more human-oriented error mesages ;-)
+* UI: service links for infobox and search
+* new datatype for geographic coordinates, accepting many kinds of coordinate inputs, and providing links to standard mapsources
+* Simple semantic search supports imprecise search again
+
+Changes by mak (0.2c, 9 Mar 2006):
+
+* added basic language support functionality
+* improved installation process (SMW_LocalSettings.php, simpler patching for Setup.php)
+
+Changes by mak (0.2c, 1 Mar 2006):
+
+* RDF Export enabled
+* more CSS and an icon to show RDF download link on pages
+
+Changes by kai/mak (0.2c, Feb 2006):
+
+* Added new custom stylesheet and JScript (kai/mak)
+* New JScript tooltips (kai)
+* New style for infobox search items (mak)
+
+Changes by mak (0.2c, Feb 2006):
+
+* Added new Special:SMWAdmin that allows relatively painless upgrade
+ from versions <=0.2 where no namespaces were used.
+* Added support for moving pages with its stored triples.
+* BUGFIX: triples in articles with SQL-hostile symbols (e.g. ') are
+ now working.
+
+== Semantic MediaWiki 0.2b ==
+
+Changes by mak (0.2b, Jan 2006):
+* Changed directory structure for more clarity, easier installation
+ and upgrade.
+* Now using custom namespaces for Relations, Attributes, and Types,
+ and their talks.
+* Semantic features can be switched on or off for each namespace
+ individually.
+* Registered extension for MediaWiki's "Special:Version".
+* BUGFIX: Configuration now takes fixed servername to use in storing
+ URIs. Before, different access methods (e.g. direct IP vs. servername)
+ generated different URIs.
+* BUGFIX: Attributes that could not be parsed now do not generate
+ triples with empty object in our database.
+
+Changes by mak (0.2a, 4 Dec 2005):
+* Attribute values are now correctly stored and retrieved.
+* Special SearchTriple greatly enhanced, such that queries for
+ attributes become possible (including unit conversion).
+* Links from attributes in infobox to the new search form.
+* Major code cleanup: SMW_AttributeStore.php now is called
+ SMW_SemanticData.php and managemes all types of semantic
+ data, including printout and storage. SMW_Hooks.php was freed
+ of all code with similar purpose. Look-up of attribute types
+ was moved from SemanticData to Datatype.
+
+Changes by mak (0.1b, 1 Dec 2005):
+* Reworked internal data representation. All information now is
+ properly encoded in URIs and decoded for display. This is an
+ important prerequisite for storing attributes and auxilliary
+ triples, which otherwise could not be distinguished from the
+ relational information.
+* New Special SearchTriple to replace the current SearchSemantic,
+ which is currently only half functional since it believes that
+ the database contains only simple names for articles, but not
+ full URIs.
+* Minor adjustments in handling of namespaces: namespaced aritcles
+ now properly work as subjects and are displayed with namespace in
+ in the infobox.
+
+Changes by mak (0.1b, 19 Nov 2005):
+
+* New type management; attributes can now be declared by creating
+ relations of type "has type" inside their articles (Attribute:X).
+ Possible targets are the builtin types (Type:String, Type:Geographic
+ length, etc.).
+* New internal method SMWGetTriples for directly retrieving triples
+ from the storage. Accepts subject, predicate, object pairs, where
+ any two can be left out.
+* Improved layout for infoboxes.
+
+Changes by mak (0.1b, 13 Nov 2005):
+
+* added support for separator "," in data numbers,
+* added tooltips for unit conversion.
+
+Changes by mak (0.1b, 17 Oct 2005):
+
+* added attribute support [[attribute name:=value|alternative text]];
+ currently, parsing these within the article works, including an
+ info box at the bottom; however, assignment from attributes to
+ datatypes is still hardcoded and attribute-annotations are neither
+ stored as triples nor are they supported in search,
+* added basic type support for STRING, INTEGER, and FLOAT,
+* added framework for unit conversion and first unit support: unit
+ conversion is achieved by callback functions, so that adding types
+ for new units boils down to writing a single unit conversion function,
+* code split into several files for easier colaboration of developers,
+* new naming convention "SMW"-prefix for all top level code elements of
+ the extension,
+* moved main storage methods to SMW_Storage.php, this should simplify
+ the conversion to another storage backend (triplestore),
+* moved stripping of semantic relations to SMW_Stripsemantics.php;
+ if this feature is desired, this file needs to be updated slightly
+ (also to include semantic attributes) and its methods connected to
+ their appropriate hooks as done in 0.1
+
+Changes by mak (0.1a, 4 Oct 2005):
+
+* moved parsing process to ParserAfterStrip to support <nowiki>; it
+ has to be done even later to support template inclusion properly,
+* changed process of storing/retrieving: no more stripping of semantic
+ relations before saving -- the annotations now appear exactly where
+ the user has put them, keeping them easier to read and maintain,
+* parse only once: saving is based on the relations that were retrieved
+ during the earlier call of parse(); for this to work, saving needs to
+ be deferred -- it is currently done at ArticleSaveComplete [should
+ there be a dedicated hook for deferred saving?],
+* enabled removal of semantic links on article deletion,
+* changed layout of semantic links factsheet, including some neat grouping
+ feature.
+
+== Semantic MediaWiki 0.1 ==
+
+This is a pre-alpha version of the Semantic MediaWiki extensions.
+It includes:
+
+* support for typed links [[link type::link target|link label]],
+* rendering of fact sheet on semantic relations at article bottom,
+* Special:SearchSemantic (alpha), featuring autocompletion for
+ link types.
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/README.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/README.md
new file mode 100644
index 00000000..e2d68464
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/README.md
@@ -0,0 +1,37 @@
+This document contains resources that can improve the understanding on "How Semantic MediaWiki
+works" from an implementation and development point of view.
+
+If you are new to SMW development, have a look at the:
+
+* [Programmer's guide](https://www.semantic-mediawiki.org/wiki/Programmer%27s_guide)
+* [Architecture guide](https://www.semantic-mediawiki.org/wiki/Architecture_guide)
+* [Developer hub](https://www.semantic-mediawiki.org/wiki/Developer_hub) and [Coding conventions](https://www.semantic-mediawiki.org/wiki/Coding_conventions)
+* [Code snippets](code-snippets/README.md)
+
+## Overview
+
+- [List of hooks](hooks.md) provided by Semantic MediaWiki
+- Running [unit](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/tests/phpunit/README.md), integration, and [JSONScript](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript) tests
+- [api.md](api.md) provides an overview for available API modules
+- [`Importer`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/Importer/README.md) contains a summary about the process and technical background of the content importer
+- [`Lang`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/Lang/README.md)
+- [`Serializers`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/doc.serializers.md) contains information about the Semantic MediaWiki serializers
+- [`ext.smw.suggester`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/res/smw/suggester/README.md) on how to register additional tokens or context objects
+- [`SMWSearch`](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/MediaWiki/Search/README.md) `Special:Search` integration
+
+### Store implementations
+
+- SQLStore
+ - [Overview](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/SQLStore/README.md)
+ - [QueryEngine](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/SQLStore/QueryEngine/README.md)
+ - [Installer](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/doc.installer.md) contains a simplified schema about the `Installer` and `TableBuilder` interface
+
+- SPARQLStore
+ - [Overview](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/SPARQLStore/README.md)
+
+- ElasticStore
+ - [Overview](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/Elastic/README.md)
+
+### Migration guide
+
+- [2.5.x to 3.0.0](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/migration-guide-3.0.md) contains information about the migration from 2.x to 3.x
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/api.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/api.md
new file mode 100644
index 00000000..1f6c6dec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/api.md
@@ -0,0 +1,175 @@
+This file contains details about [Semantic MediaWiki's API][smwapi] for external use with a description
+of available interfaces. For more details on MediaWiki's WebAPI, it is recommended to read this [website][mwapi].
+
+## api.php?action=ask
+The `Ask API` allows you to do ask queries against Semantic MediaWiki using the MediaWiki API and get results back serialized in one of the formats it supports.
+
+The ask module supports one parameter, query, which takes the same string you'd feed into an `#ask` parser function, but urlencoded.
+
+> api.php?action=ask&query=[[Modification date::%2B]]|%3FModification date|sort%3DModification date|order%3Ddesc&format=jsonfm
+
+## api.php?action=askargs
+The `Askargs API` module aims to take arguments in un-serialized form, so with as little ask-specific syntax as possible. It supports 3 arguments:
+
+* `conditions`: The query conditions, ie the requirements for a subject to be included
+* `printouts`: The query printeouts, ie the properties to show per subject
+* `parameters`: The query parameters, ie all non-condition and non-printeout arguments
+
+> api.php?action=askargs&conditions=Modification date::%2B&printouts=Modification date&parameters=|sort%3DModification date|order%3Ddesc&format=jsonfm
+
+#### Output serialization
+```php
+{
+ "query-continue-offset": 50,
+ "query": {
+ "printrequests": [
+ {
+ "label": "",
+ "typeid": "_wpg",
+ "mode": 2,
+ "format": false
+ },
+ {
+ "label": "Modification date",
+ "typeid": "_dat",
+ "mode": 1,
+ "format": ""
+ }
+ ],
+ "results": {
+ "Main Page": {
+ "printouts": {
+ "Modification date": [
+ "1381456128"
+ ]
+ },
+ "fulltext": "Main Page",
+ "fullurl": "http:\/\/localhost:8080\/mw\/index.php\/Main_Page",
+ "namespace": 0,
+ "exists": true
+ },
+ ...
+ },
+ "meta": {
+ "hash": "a9abdb34024fa8735f6b044305a48619",
+ "count": 50,
+ "offset": 0
+ }
+ }
+}
+```
+
+## api.php?action=smwinfo
+An interface to access statistical information about the properties, values etc..
+
+> api.php?action=smwinfo&format=json&info=proppagecount|propcount
+
+The following parameters are available and may be concatenate using the "|" character.
+* `proppagecount`: The total number of properties registered with a page.
+* `declaredpropcount`: The total number of properties with a datatype assigned.
+* `usedpropcount`: The total number of properties with at least one values assigned.
+* `propcount`: The total number of property values assinged.
+* `errorcount`: The total number of property values invalidly assinged.
+* `querycount`: The total number of queries.
+* `querysize`: The total size of all queries.
+* `formatcount`: The query formats and their respective total usage count.
+* `subobjectcount`: The total number of subobjects.
+* `conceptcount`: The total number of concepts.
+
+#### Output serialization
+
+```php
+{
+ "info": {
+ "proppagecount": 40,
+ "formatcount": {
+ "table": 14,
+ "list": 3,
+ "broadtable": 1
+ }
+ }
+}
+```
+The parameter "formatcount" will output an array of used formats together with its count information.
+
+## api.php?action=browsebysubject
+An interface to browse facts (the equivalent of `Special:Browse`) of a subject (wikipage) including special properties and subobjects.
+
+> api.php?action=browsebysubject&subject=Main%20Page
+> api.php?action=browsebysubject&subject=Main%20Page&subobject=_QUERYa0856d9fbd9e495af0963ecc75fcef14
+
+#### Output serialization
+```php
+{
+ "query": {
+ "subject": "Main_Page#0##",
+ "data": [
+ {
+ "property": "Foo",
+ "dataitem": [
+ {
+ "type": 2,
+ "item": "Bar"
+ }
+ ]
+ }
+ ...
+ ],
+ "sobj": [
+ {
+ "subject": "Main_Page#0##_QUERYa0856d9fbd9e495af0963ecc75fcef14",
+ "data": [
+ {
+ "property": "_ASKDE",
+ "dataitem": [
+ {
+ "type": 1,
+ "item": "1"
+ }
+ ]
+ },
+ ...
+ ]
+ ...
+ }
+ ],
+ "serializer": "SMW\Serializers\SemanticDataSerializer",
+ "version": 0.1
+ }
+}
+```
+
+## api.php?action=browsebyproperty
+An interface to browse properties (the equivalent of `Special:Properties`).
+
+> api.php?action=browsebyproperty
+> api.php?action=browsebyproperty&limit=100&format=json&property=name
+
+#### Output serialization
+
+```php
+{
+ "xmlns:Foaf": "http://xmlns.com/foaf/0.1/",
+ "query": {
+ "Foaf:name": {
+ "label": "Foaf:name",
+ "isUserDefined": true,
+ "usageCount": 2
+ },
+ "Has_common_name": {
+ "label": "Has common name",
+ "isUserDefined": true,
+ "usageCount": 1
+ }
+ },
+ "version": 0.1,
+ "meta": {
+ "limit": 100,
+ "count": 2,
+ "isCached": true
+ }
+}
+```
+
+[mwapi]: https://www.mediawiki.org/wiki/API:Main_page "API:Main_page"
+[smwapi]: https://www.semantic-mediawiki.org/wiki/Help:API "Help:API"
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/README.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/README.md
new file mode 100644
index 00000000..21362938
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/README.md
@@ -0,0 +1,10 @@
+
+## Examples and code snippets
+
+* [register.datatype.md](register.datatype.md) Shows how to register a new dataType/dataValue
+* [approve.update.md](approve.update.md) Shows how to alter the data representation in Semantic MediaWiki with the help of selected hooks in connection with the `ApprovedRevs` extension
+
+### Using hooks
+
+* [hook.datatype.inittypes.md](hook.datatype.inittypes.md) Shows how to register a new dataType/dataValue using the the `SMW::DataType::initTypes` hook
+* [hook.pagecontentsavecomplete.md](hook.pagecontentsavecomplete.md) Creating subobjects using the `PageContentSaveComplete` hook (see [#2974](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2974))
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/approve.update.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/approve.update.md
new file mode 100644
index 00000000..1be387b5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/approve.update.md
@@ -0,0 +1,90 @@
+For certain user scenarios it may be necessary to refuse or alter an update by changing the revision used as basis for what the semantic data should represent.
+
+Please ensure that when changes are applied to [log][log] those to make them recoverable. See [`$wgLogTypes`][wgLogTypes] on how to add and use an appropriate type and action description.
+
+## Example
+
+The following can be used in connection with the `ApprovedRevs` extension to alter the data representation in Semantic MediaWiki.
+
+### SMW::LinksUpdate::ApprovedUpdate
+
+Check whether the current revision ID used by SMW is the one expected as approved version and if not, refuse its usage by returning `false` to signal that the update should be declined.
+
+```php
+use Hooks;
+use ApprovedRevs;
+use LogPage;
+use Xml;
+
+Hooks::register( 'SMW::LinksUpdate::ApprovedUpdate', function( $title, $latestRevID ) {
+
+ if ( !class_exists( 'ApprovedRevs' ) || ( $approvedRevID = ApprovedRevs::getApprovedRevID( $title ) ) === null ) {
+ return true;
+ }
+
+ if ( $approvedRevID != $latestRevID ) {
+ static $logged = [];
+
+ // Only log the action once in case LinksUpdate is called several
+ // times by MediaWiki::preOutputCommit/RefreshLinksJob
+ if ( isset( $logged[$latestRevID . ':' . $approvedRevID] ) ) {
+ return false;
+ }
+
+ $log = new LogPage( 'myType' );
+ $rev_url = $title->getFullURL( [ 'oldid' => $approvedRevID, 'diff' => $latestRevID ] );
+
+ $rev_link = Xml::element(
+ 'a',
+ [ 'href' => $rev_url ],
+ $approvedRevID
+ );
+
+ $log->addEntry( 'myType', $title, '', [ $rev_link ] );
+ $logged[$latestRevID . ':' . $approvedRevID] = true;
+
+ return false;
+ }
+
+ return true;
+} );
+```
+
+### SMW::Parser::ChangeRevision
+
+During a `UpdateJob` or `rebuildData.php` script execution, SMW always chooses the latest available revision to represent its data. Use the hook to change the revision when the approved revision is different from what the parser is going to use as basis for the update process.
+
+```php
+use Hooks;
+use ApprovedRevs;
+use LogPage;
+use Xml;
+use Revision;
+
+Hooks::register( 'SMW::Parser::ChangeRevision', function( $title, &$revision ) {
+
+ if ( !class_exists( 'ApprovedRevs' ) || ( $approvedRevID = ApprovedRevs::getApprovedRevID( $title ) ) === null ) {
+ return true;
+ }
+
+ // Forcibly change the revision to match the approved version
+ $currentRevision = $revision;
+ $revision = Revision::newFromId( $approvedRevID );
+
+ $log = new LogPage( 'myType' );
+ $rev_url = $title->getFullURL( array( 'oldid' => $approvedRevID ) );
+
+ $rev_link = Xml::element(
+ 'a',
+ [ 'href' => $rev_url ],
+ $approvedRevID
+ );
+
+ $log->addEntry( 'myType', $title, '', [ $rev_link ] );
+
+ return true;
+} );
+```
+
+[log]: https://www.mediawiki.org/wiki/Manual:Logging_to_Special:Log
+[wgLogTypes]: https://www.mediawiki.org/wiki/Manual:$wgLogTypes
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.datatype.inittypes.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.datatype.inittypes.md
new file mode 100644
index 00000000..67316e65
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.datatype.inittypes.md
@@ -0,0 +1,59 @@
+## Register new datatype
+
+This example shows how to register a new dataType/dataValue in Semantic MediaWiki and the convention for the datatype key is to use `___` as leading identifer to distinguish them from those defined by Semantic MediaWiki itself.
+
+### SMW::DataType::initTypes
+
+```php
+use Hooks;
+use Foo\DataValues\FooValue;
+
+Hooks::register( 'SMW::DataType::initTypes', function ( $dataTypeRegistry ) {
+
+ $dataTypeRegistry->registerDatatype(
+ FooValue::TYPE_ID,
+ FooValue::class,
+ DataItem::TYPE_BLOB
+ );
+
+ $dataTypeRegistry->setOption(
+ 'foovalue.SomeSetting',
+ 42
+ );
+
+ return true;
+};
+```
+
+### DataValue representation
+
+```php
+class FooValue extends DataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '___foo_bar';
+
+ /**
+ * @see DataValue::parseUserValue
+ * @note called by DataValue::setUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $userValue ) {
+ ...
+ }
+}
+```
+
+### Usage
+
+```php
+$fooValue = DataValueFactory::getInstance()->newTypeIdValue(
+ '___foo_bar',
+ 'Bar'
+)
+
+$fooValue->getShortWikiText();
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.fileupload.beforeupdate.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.fileupload.beforeupdate.md
new file mode 100644
index 00000000..04c7c55b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.fileupload.beforeupdate.md
@@ -0,0 +1,28 @@
+## SMW::FileUpload::BeforeUpdate hook
+
+SMW 2.4
+
+```php
+use SMW\DataItemFactory
+
+$GLOBALS['wgHooks']['SMW::FileUpload::BeforeUpdate'][] = function ( $filePage, $semanticData ) {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $property = $dataItemFactory->newDIProperty( '___ext_file_sha1' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ $dataItemFactory->newDIBlob( $filePage->getFile()->getSha1() )
+ );
+
+ $property = $dataItemFactory->newDIProperty( '___ext_file_size' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ $dataItemFactory->newDIBlob( $filePage->getFile()->getSize() )
+ );
+
+ return true;
+};
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.pagecontentsavecomplete.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.pagecontentsavecomplete.md
new file mode 100644
index 00000000..59d68ce6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/hook.pagecontentsavecomplete.md
@@ -0,0 +1,99 @@
+
+## PageContentSaveComplete hook
+
+[#2974](https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/2974) Creating subobjects using the `PageContentSaveComplete` hook
+
+```php
+use Hooks;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+use SMW\DIWikiPage;
+use SMW\DIProperty;
+
+Hooks::register( 'PageContentSaveComplete', function( $wikiPage, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, $baseRevId, $undidRevId ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $mwCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
+
+ /**
+ * Initialize the ParserOuput object
+ */
+ $editInfoProvider = $mwCollaboratorFactory->newEditInfoProvider(
+ $wikiPage,
+ $revision,
+ $user
+ );
+
+ $parserOutput = $editInfoProvider->fetchEditInfo()->getOutput();
+
+ if ( !$parserOutput instanceof ParserOutput ) {
+ return true;
+ }
+
+ $parserData = $applicationFactory->newParserData(
+ $wikiPage->getTitle(),
+ $parserOutput
+ );
+
+ // Subject Foo ...
+ $subject = $parserData->getSubject();
+
+ // Contains the wikitext, JSON or whatever is stored for this content model
+ $nativeData = $content->getNativeData();
+
+ // Identify the content as unique
+ $subobjectName = '_MYCUSTOMPREFIX' . md5( $nativeData );
+
+ $subject = new DIWikiPage(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subobjectName
+ );
+
+ // Build the subobject by using a separate container object
+ $containerSemanticData = new ContainerSemanticData(
+ $subject
+ );
+
+ /**
+ * Start doing all the work required after this
+ */
+
+ // If one knows the details you can add it directly
+ $containerSemanticData->addPropertyObjectValue(
+ new DIProperty( 'PropertyIWantToUse' ),
+ new DIWikiPage( 'SomeTextItem', NS_MAIN )
+ );
+
+ // If you don't know the type, use the DataValueFactory (see available methods)
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ 'AnotherPropertyIWantToUse',
+ '123'
+ );
+
+ $containerSemanticData->addDataValue(
+ $dataValue
+ );
+
+ /**
+ * Done
+ */
+
+ // This part is used to add the subobject the the main subject
+ // Page: Foo -> gets a _MYCUSTOMPREFIX.... attached
+ $parserData->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( DIProperty::TYPE_SUBOBJECT ),
+ new DIContainer( $containerSemanticData )
+ );
+
+ $parserData->pushSemanticDataToParserOutput();
+
+ return true;
+
+} );
+
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/match.property.values.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/match.property.values.md
new file mode 100644
index 00000000..87fd6761
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/match.property.values.md
@@ -0,0 +1,63 @@
+Examples listed in this document require SMW 2.5.
+
+```php
+use SMW\ApplicationFactory;
+
+$applicationFactory = ApplicationFactory::getInstance();
+$queryFactory = $applicationFactory->getQueryFactory();
+
+$dataItemFactory = $applicationFactory->getDataItemFactory();
+
+$dataValue = $applicationFactory->getDataValueFactory()->newDataValueByProperty(
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ 'Bar'
+);
+```
+```php
+$requestOptions = $queryFactory->newRequestOptions();
+$requestOptions->setLimit( 42 );
+
+// Find subjects that match [[Foo::Bar]] and limit the return results to 42
+$subjectList = $applicationFactory->getStore()->getPropertySubjects(
+ $dataValue->getProperty(),
+ $dataValue->getDataItem(),
+ $requestOptions
+);
+```
+```php
+$requestOptions = $queryFactory->newRequestOptions();
+$requestOptions->setLimit( 42 );
+
+// Find all subjects that have a Property:Foo assigned and limit the return results to 42
+$subjectList = $applicationFactory->getStore()->getAllPropertySubjects(
+ $dataValue->getProperty(),
+ $requestOptions
+);
+```
+```php
+$descriptionFactory = $queryFactory->newDescriptionFactory();
+
+// Query [[Foo::+]] with a limit of 42 matches
+$description = $descriptionFactory->newSomeProperty(
+ $dataValue->getProperty(),
+ $descriptionFactory->newThingDescription()
+);
+
+$query = $queryFactory->newQuery( $description );
+$query->setLimit( 42 );
+
+$queryResult = $applicationFactory->getStore()->getQueryResult( $query );
+```
+```php
+$descriptionFactory = $queryFactory->newDescriptionFactory();
+
+// [[Foo::Bar]] with a limit of 42 matches
+$description = $descriptionFactory->newFromDataValue(
+ $dataValue
+);
+
+$query = $queryFactory->newQuery( $description );
+$query->setLimit( 42 );
+
+$queryResult = $applicationFactory->getStore()->getQueryResult( $query );
+```
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/phpunit.test.property.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/phpunit.test.property.md
new file mode 100644
index 00000000..c20eb457
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/phpunit.test.property.md
@@ -0,0 +1,69 @@
+## Setup a "Text" type property
+
+```php
+$property = new DIProperty( 'Foo' );
+$property->setPropertyTypeId( '_txt' );
+
+// Using a dedicated property to created a DV
+$dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ 'Some text'
+);
+
+$this->assertInstanceof(
+ '\SMWStringValue',
+ $dataValue
+);
+
+$this->assertEquals(
+ 'Some text',
+ $dataValue->getDataItem()->getString()
+);
+```
+
+## Using the MockBuilder
+
+```php
+$store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+$store->expects( $this->at( 0 ) )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( array(
+ new DIWikiPage( 'SomePropertyOfTypeBlob', SMW_NS_PROPERTY ) ) ) );
+
+$store->expects( $this->at( 1 ) )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( new DIWikiPage( 'SomePropertyOfTypeBlob', SMW_NS_PROPERTY ) ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( array(
+ \SMWDIUri::doUnserialize( 'http://semantic-mediawiki.org/swivt/1.0#_txt' ) ) ) );
+
+// Inject the store as a mock object due to DIProperty::findPropertyTypeID finding the
+// type dynamically when called without explicit declaration
+ApplicationFactory::getInstance()->registerObject( 'Store', $store );
+
+// Create a DV from a string value instead of using a dedicated typed
+// property (used by #set, #subobject since the user input is a string and not
+// a typed object)
+$dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ 'SomePropertyOfTypeBlob',
+ 'Some text'
+);
+
+$this->assertInstanceof(
+ '\SMWStringValue',
+ $dataValue
+);
+
+$this->assertEquals(
+ 'Some text',
+ $dataValue->getDataItem()->getString()
+);
+
+// Reset instance to avoid issues with tests that follow hereafter
+ApplicationFactory::getInstance()->clear();
+```
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.description.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.description.md
new file mode 100644
index 00000000..a816bbfb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.description.md
@@ -0,0 +1,40 @@
+```php
+$descriptionFactory = new DescriptionFactory();
+
+// Equivalent to [[Category:Foo]]
+$classDescription = $descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+);
+```
+
+```php
+// Equivalent to [[:+]]
+$namespaceDescription = $descriptionFactory->newNamespaceDescription(
+ NS_MAIN
+);
+```
+
+```php
+// Equivalent to [[Foo::+]]
+$anyValueForSomeProperty = $descriptionFactory->newSomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+);
+```
+
+```php
+// Equivalent to [[:+]][[Category:Foo]][[Foo::+]]
+$description = $descriptionFactory->newConjunction( array(
+ $namespaceDescription,
+ $classDescription,
+ $anyValueForSomeProperty
+) );
+```
+
+```php
+// Equivalent to [[Category:Foo]] OR [[Foo::+]]
+$description = $descriptionFactory->newDisjunction( array(
+ $classDescription,
+ $anyValueForSomeProperty
+) );
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.someproperty.of.type.number.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.someproperty.of.type.number.md
new file mode 100644
index 00000000..e3ea8cc5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/query.someproperty.of.type.number.md
@@ -0,0 +1,71 @@
+## Match distinct numeric property value
+
+```php
+{{#ask: [[NumericProperty::1111]]
+ |?NumericProperty
+}}
+```
+
+```php
+// Create property instance
+$property = new DIProperty( 'NumericProperty' );
+$property->setPropertyTypeId( '_num' );
+
+$dataItem = new DINumber( 1111 );
+
+$dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+);
+```
+
+```php
+// Create and store the SemanticData object in order to be able to match a value
+$subject = new DIWikiPage( 'Foo', NS_MAIN )
+
+$semanticData = new SemanticData( $subject );
+
+$semanticData->addDataValue(
+ $dataValue
+);
+
+ApplicationFactory::getInstance()->getStore()->updateData(
+ $semanticData
+);
+```
+
+```php
+// Create a description that represents the condition
+$descriptionFactory = new DescriptionFactory();
+
+$description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newValueDescription( $dataItem )
+);
+
+$propertyValue = DataValueFactory::getInstance()->newPropertyValueByLabel(
+ 'NumericProperty'
+);
+
+$description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+);
+
+// Create query object
+$query = new Query(
+ $description
+);
+
+$query->querymode = Query::MODE_INSTANCES;
+```
+
+```php
+// Try to match condition against the store
+$queryResult = ApplicationFactory::getInstance()->getStore()->getQueryResult( $query );
+
+// PHPUnit
+$this->assertEquals(
+ 1,
+ $queryResult->getCount()
+);
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/register.datatype.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/register.datatype.md
new file mode 100644
index 00000000..2f6649a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/register.datatype.md
@@ -0,0 +1,66 @@
+## Register new datatype
+
+This example shows how to register a new dataType/dataValue in Semantic MediaWiki.
+
+* [Datatype](https://www.semantic-mediawiki.org/wiki/Help:Datatype)
+* [DataValue](https://www.semantic-mediawiki.org/wiki/Help:DataValue)
+
+### Type registration
+
+All IDs must start with an underscore, two underscores indicate a truly internal
+(non user-interacted type), three underscores should be used by an extension.
+
+`TypesRegistry::getDataTypeList` expects that the following information are provided:
+
+* A type id (e.g. `FooValue::TYPE_ID`)
+* An associated class
+* An item type (storage type)
+* A declaration whether it is a subData type (e.g subobject) or not
+* Whether a type is browsable or not
+
+<pre>
+return array(
+ // ...
+ FooValue::TYPE_ID => [ FooValue::class, DataItem::TYPE_WIKIPAGE, false, false ],
+);
+</pre>
+
+<pre>
+class FooValue extends DataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_foo';
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $userValue ) {
+ ...
+ }
+
+}
+</pre>
+
+### Label registration
+
+By default, DataTypes (Date, URL etc.) are registered with a corresponding property
+of the same name to match the expected semantics. For an exemption, see
+`smwgDataTypePropertyExemptionList`.
+
+Find `i18n/extra/en.json` and extend the canonical description (which is English) with something like:
+
+<pre>
+ "dataTypeLabels":{
+ "_foo": "SomeType"
+ ...
+ },
+ "dataTypeAliases":{
+ "SomeType": "_foo"
+ "ExtraAlias": "_foo"
+ ...
+ },
+</pre>
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/semanticdata.access.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/semanticdata.access.md
new file mode 100644
index 00000000..adb14ed6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/semanticdata.access.md
@@ -0,0 +1,71 @@
+## Add semantic data to in-memory (#1202)
+
+To avoid having competing data being stored at a different point in time
+during a request aimed for the same subject, property value assignments are collected
+and stored in-memory before the finale `Store::updateData` process will
+be invoked once.
+
+
+```php
+// Create in-memory ParserOutput transfer object
+$parserData = ApplicationFactory::getInstance()->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+);
+
+$subject = $parserData->getSubject();
+$property = new DIProperty( 'SomeProperty' );
+
+// Add individual instances of a value for a known property
+foreach ( $values as $value ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ trim( $value ), // Text value
+ false,
+ $subject
+ );
+
+ $parserData->addDataValue( $dataValue );
+}
+
+// Add individual instances of a value for a property only known by
+// its textual representation
+foreach ( $values as $value ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $property,
+ trim( $value ), // Text value
+ false,
+ $subject
+ );
+
+ // Adds the object to the SemanticData container you could also use
+ // $parserData->getSemanticData()->addPropertyObjectValue( ...)
+ $parserData->addDataValue( $dataValue );
+}
+
+// Ensures that objects are pushed to the ParserOutput
+$parserData->pushSemanticDataToParserOutput();
+```
+
+## Access semantic data currently stored in-memory
+
+```php
+// Create in-memory ParserOutput transfer object
+$parserData = ApplicationFactory::getInstance()->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+);
+
+// Access to the data store in-memory
+$semanticData = $parserData->getSemanticData();
+```
+
+## Read semantic data from DB
+
+```php
+$subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+$semanticData = ApplicationFactory::getInstance()->getStore->getSemanticData(
+ $subject
+);
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/store.subobject.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/store.subobject.md
new file mode 100644
index 00000000..6d967737
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/code-snippets/store.subobject.md
@@ -0,0 +1,73 @@
+## Using Subobject
+
+```php
+$subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+$subobject = new Subobject( $subject->getTitle() );
+$subobject->setEmptyContainerForId( 'SomeSubobject' );
+
+$dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ 'SomeProperty,
+ 'SomeValue'
+);
+
+$subobject->addDataValue(
+ $dataValue
+);
+```
+
+```php
+// Create and store SemanticData object and add the subobject
+$semanticData = new SemanticData( $subject );
+
+$semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+);
+
+ApplicationFactory::getInstance()->getStore()->updateData(
+ $semanticData
+);
+```
+
+## Using DIContainer
+
+```php
+$subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+// Internal subobject reference
+$subobjectName = 'SomeSubobject';
+
+$containerSubject = new DIWikiPage(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subobjectName
+);
+
+// Create container to host property values assignments
+// for the particular subobjectName
+$containerSemanticData = new ContainerSemanticData( $containerSubject );
+
+$dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ 'SomeProperty,
+ 'SomeValue'
+);
+
+$containerSemanticData->addDataValue(
+ $dataValue
+);
+```
+```php
+// Create and store SemanticData object and add the subobject
+$semanticData = new SemanticData( $subject );
+
+$semanticData->addPropertyObjectValue(
+ new DIProperty( '_SOBJ' ),
+ new DIContainer( $containerSemanticData )
+);
+
+ApplicationFactory::getInstance()->getStore()->updateData(
+ $semanticData
+);
+``` \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.installer.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.installer.md
new file mode 100644
index 00000000..4c5efa85
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.installer.md
@@ -0,0 +1 @@
+![image](https://cloud.githubusercontent.com/assets/1245473/20027799/8a8e06b4-a31e-11e6-8b03-aaa1859e6937.png) \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.serializers.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.serializers.md
new file mode 100644
index 00000000..58ded396
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doc.serializers.md
@@ -0,0 +1,133 @@
+This document contains information about Semantic MediaWiki serializers.
+
+## Components
+
+### Serializer and Deserializer
+Interfaces provided by the [Serialization extension][serialization] which describes specific serialize/deserialze public methods.
+
+### SerializerFactory
+A factory class that assigns registered serializers to an object and identifies an unserializer based on the invoked array. A serialized record has a reference to the generator (serializer) class which will automatically be used during unserialization. Each record includes a version number to compare the data model used and enable a consistency check before an attempt to unserialize a record.
+
+```php
+$foo = new Foo( ... );
+$serialized = SerializerFactory::serialize( $foo );
+$unserialized = SerializerFactory::deserialize( $serialized );
+```
+
+### SemanticDataSerializer
+Implements the Serializer interface for the SMW\SemanticData object.
+
+#### Data model
+```php
+"subject": -> Subject serialization,
+"data": [
+ {
+ "property": -> Property serialization,
+ "dataitem": [
+ {
+ "type": -> DataItemType,
+ "item": -> DataItem serialization
+ }
+ ]
+ }
+]
+"sobj": [
+ {
+ "subject": ...,
+ "data": [
+ {
+ "property": ...,
+ "dataitem": [
+ {
+ "type": ...,
+ "item": ...
+ }
+ ]
+ },
+ },
+],
+"serializer": -> Class of the generator and entry point for the un-serializer,
+"version": -> Number to compare structural integrity between serialization and un-serialization
+```
+#### Example
+For a page called "Foo" that contains <code>[[Has property::Bar]]</code>, <code>{{#subobject:|Has subobjects=Bam}}</code>, <code>{{#ask:[[Has subobjects::Bam]]}}</code>, the Serializer will output:
+
+```php
+"subject": "Foo#0##",
+"data": [
+ {
+ "property": "Has_property",
+ "dataitem": [
+ {
+ "type": 9,
+ "item": "Bar#0##"
+ }
+ ]
+ },
+ {
+ "property": "_ASK",
+ "dataitem": [
+ {
+ "type": 9,
+ "item": "Foo#0##_QUERYc8606da8f325fc05aa8e8b958821c3b4"
+ }
+ ]
+ },
+ ...
+ {
+ "property": "_SOBJ",
+ "dataitem": [
+ {
+ "type": 9,
+ "item": "Foo#0##_fc4b104aabf80eb06429e946aa8f7070"
+ }
+ ]
+ }
+],
+"sobj": [
+ {
+ "subject": "Foo#0##_QUERYc8606da8f325fc05aa8e8b958821c3b4",
+ "data": [
+ {
+ "property": "_ASKDE",
+ "dataitem": [
+ {
+ "type": 1,
+ "item": "1"
+ }
+ ]
+ },
+ },
+ ...
+ {
+ "subject": "Foo#0##_fc4b104aabf80eb06429e946aa8f7070",
+ "data": [
+ {
+ "property": "Has_subobjects",
+ "dataitem": [
+ {
+ "type": 9,
+ "item": "Bam#0##"
+ }
+ ]
+ },
+ {
+ "property": "_SKEY",
+ "dataitem": [
+ {
+ "type": 2,
+ "item": "Foo"
+ }
+ ]
+ }
+ ]
+ }
+],
+"serializer": "SMW\\Serializers\\SemanticDataSerializer",
+"version": 0.1
+```
+
+### QueryResultSerializer
+Implements the SerializerInterface for the SMWQueryResult object.
+
+[serialization]: [https://github.com/wikimedia/mediawiki-extensions-Serialization]
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/doxygen.php b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doxygen.php
new file mode 100644
index 00000000..8f9192b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/doxygen.php
@@ -0,0 +1,196 @@
+<?php
+
+die("Not a valid entry point\n");
+
+/**
+ * This file does not hold any code. It is used mainly during the doxygen
+ * auto-documentation process
+ *
+ * @file
+ */
+
+/**
+ * @mainpage Introduction
+ *
+ * Welcome to the auto-generated Semantic MediaWiki code documentation
+ *
+ * For information about how to install or configure Semantic MediaWiki,
+ * please visit the main site:
+ *
+ * https://www.semantic-mediawiki.org/
+ */
+
+/**
+ * This file does not hold any code. It is used mainly during the doxygen
+ * auto-documentation process
+ *
+ * @file
+ */
+
+/**
+ * This group contains members that are related to the Semantic MediaWiki Api
+ *
+ * @defgroup Api Api
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to the Store interface
+ *
+ * @defgroup Store Store
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to the SQLStore implementation
+ *
+ * @defgroup SQLStore SQLStore
+ * @ingroup Store
+ */
+
+/**
+ * This group contains members that are related to the Collector interface
+ *
+ * @defgroup Collector Collector
+ * @ingroup Store
+ */
+
+/**
+ * This group contains members that are related to message, error, and parameter
+ * formatting
+ *
+ * @defgroup Formatter Formatter
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to hooks, hook events, and
+ * hook registrations
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks
+ * @see https://semantic-mediawiki.org/wiki/Hooks‎
+ *
+ * @defgroup FunctionHook FunctionHook
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to parser hooks and functions
+ *
+ * @defgroup ParserFunction ParserFunction
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to special pages
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Special_pages
+ *
+ * @defgroup SpecialPage SpecialPage
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to jobs
+ *
+ * @defgroup Job Job
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to maintenance scripts
+ *
+ * @defgroup Maintenance Maintenance
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to utility classes and support
+ * functions
+ *
+ * @defgroup Utility Utility
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to supporting the Observer
+ *
+ * @defgroup Observer Observer
+ * @ingroup Utility
+ */
+
+/**
+ * This group contains members that are related to supporting the Dependency
+ * Injection Framework
+ *
+ * @defgroup DependencyInjection Dependency Injection
+ * @ingroup Utility
+ */
+
+/**
+ * This group contains members that are related to building of Dependency
+ * Injection objects
+ *
+ * @defgroup DependencyBuilder Dependency Builder
+ * @ingroup DependencyInjection
+ */
+
+/**
+ * This group contains members that are related for requesting a Dependency
+ * Injection
+ *
+ * @defgroup DependencyRequestor Dependency Requestor
+ * @ingroup DependencyInjection
+ */
+
+/**
+ * This group contains members that are related for speficcation of Dependency
+ * Objects
+ *
+ * @defgroup DependencyContainer Dependency Container
+ * @ingroup DependencyInjection
+ */
+
+/**
+ * This group contains members that are related to handler classes (including
+ * object handler, hooks handler, instance handler etc.)
+ *
+ * @defgroup Handler Handler
+ * @ingroup Utility
+ */
+
+/**
+ * This group contains members that are related to unit tests
+ *
+ * @defgroup Test Test
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to the QueryPrinter unit tests
+ *
+ * @defgroup QueryPrinterTest QueryPrinterTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the maintenance script unit
+ * tests
+ *
+ * @defgroup MaintenanceTest MaintenanceTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the Store unit tests
+ *
+ * @defgroup StoreTest StoreTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the SQLStore unit tests
+ *
+ * @defgroup SQLStoreTest SQLStoreTest
+ * @ingroup StoreTest
+ */
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/hooks.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/hooks.md
new file mode 100644
index 00000000..b89bfefb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/hooks.md
@@ -0,0 +1,405 @@
+This document contains details about event handlers (also known as [Hooks][hooks]) provided by Semantic MediaWiki to enable users to extent and integrate custom specific solutions.
+
+Implementing a hook should be made in consideration of the expected performance impact for the front-end (additional DB read/write transactions etc.) and/or the back-end (prolonged job backlog etc.) process.
+
+# List of available hooks
+
+## 1.9
+
+- `SMW::Factbox::BeforeContentGeneration` to replace or amend text elements shown in a Factbox. See also `$smwgFactboxUseCache` settings.<sup>Use of `smwShowFactbox` was deprecated with 1.9</sup>
+- `SMW::Job::updatePropertyJobs` to add additional update jobs for a property and related subjects.<sup>Use of `smwUpdatePropertySubjects` was deprecated with 1.9</sup>
+- `SMW::DataType::initTypes` to add additional DataType support.<sup>Use of `smwInitDatatypes` was deprecated with 1.9</sup>
+- `SMW::SQLStore::updatePropertyTableDefinitions` to add additional table definitions during initialization.
+
+## 2.1
+
+### SMW::Store::BeforeQueryResultLookupComplete
+
+* Version: 2.1
+* Description: Hook to return a `QueryResult` object before the standard selection process is started and allows to suppress the standard selection process completely by returning `false`.
+* Reference class: `SMW_SQLStore3.php`
+
+<pre>
+\Hooks::register( 'SMW::Store::AfterQueryResultLookupComplete', function( $store, $query, &$queryResult, $queryEngine ) {
+
+ // Allow default processing
+ return true;
+
+ // Stop further processing
+ return false;
+} );
+</pre>
+
+### SMW::Store::AfterQueryResultLookupComplete
+
+* Version: 2.1
+* Description: Hook to manipulate a `QueryResult` after the selection process.
+* Reference class: `SMW_SQLStore3.php`
+
+<pre>
+\Hooks::register( 'SMW::Store::AfterQueryResultLookupComplete', function( $store, &$queryResult ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::Property::initProperties
+
+* Version: 2.1
+* Description: Hook to add additional predefined properties (`smwInitProperties` was deprecated with 2.1)
+* Reference class: `SMW\PropertyRegistry`
+
+<pre>
+\Hooks::register( 'SMW::Property::initProperties', function( $propertyRegistry ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::BeforeDeleteSubjectComplete
+
+* Version: 2.1
+* Description: Hook is called before the deletion of a subject is completed
+* Reference class: `SMW_SQLStore3_Writers.php`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::BeforeDeleteSubjectComplete', function( $store, $title ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::AfterDeleteSubjectComplete
+
+* Version: 2.1
+* Description: Hook is called after the deletion of a subject is completed
+* Reference class: `SMW_SQLStore3_Writers.php`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::AfterDeleteSubjectComplete', function( $store, $title ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::BeforeChangeTitleComplete
+
+* Version: 2.1
+* Description: Hook is called before change to a subject is completed
+* Reference class: `SMW_SQLStore3_Writers.php`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::BeforeChangeTitleComplete', function( $store, $oldTitle, $newTitle, $pageId, $redirectId ) {
+
+ return true;
+} );
+</pre>
+
+## 2.2
+
+### SMW::Parser::BeforeMagicWordsFinder
+
+* Version: 2.2
+* Description: Hook allowing to extend the magic words list that the `InTextAnnotationParser` should search for the wikitext.
+* Reference class: `\SMW\InTextAnnotationParser`
+
+<pre>
+\Hooks::register( 'SMW::Parser::BeforeMagicWordsFinder', function( array &$magicWords ) {
+
+ return true;
+} );
+</pre>
+
+## 2.3
+
+### SMW::SQLStore::BeforeDataRebuildJobInserts
+
+* Version: 2.3
+* Description: Hook to add update jobs while running the rebuild process.<sup>Use of `smwRefreshDataJobs` was deprecated with 2.3</sup>
+* Reference class: `\SMW\SQLStore\EntityRebuildDispatcher`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::BeforeDataRebuildJobInsert', function( $store, array &$jobs ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::AddCustomFixedPropertyTables
+
+* Version: 2.3
+* Description: Hook to add fixed property table definitions
+* Reference class: `\SMW\MediaWiki\Specials\Browse\ContentsBuilder`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::AddCustomFixedPropertyTables', function( array &$customFixedProperties, &$propertyTablePrefix ) {
+ $customFixedProperties['Foo'] = '_Bar';
+
+ return true;
+} );
+</pre>
+
+### SMW::Browse::AfterIncomingPropertiesLookupComplete
+
+* Version: 2.3
+* Description: Hook to extend the incoming properties display for `Special:Browse`
+* Reference class: `\SMW\MediaWiki\Specials\Browse\ContentsBuilder`
+
+<pre>
+\Hooks::register( 'SMW::Browse::AfterIncomingPropertiesLookupComplete', function( $store, $semanticData, $requestOptions ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate
+
+* Version: 2.3
+* Description: Hook to replace the standard `SearchByProperty` with a custom link to an extended list of results (return `false` to replace the link)
+* Reference class: `\SMW\MediaWiki\Specials\Browse\ContentsBuilder`
+
+<pre>
+\Hooks::register( 'SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate', function( $property, $subject, &$propertyValue ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::AfterDataUpdateComplete
+
+* Version: 2.3
+* Description: Hook to add processing after the update has been completed and provides `ChangeOp` to identify entities that have been added/removed during the update. (`SMWSQLStore3::updateDataAfter` was deprecated with 2.3)
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::AfterDataUpdateComplete', function( $store, $semanticData, $changeOp ) {
+
+ return true;
+} );
+</pre>
+
+## 2.4
+
+### SMW::FileUpload::BeforeUpdate
+
+* Version: 2.4
+* Description: Hook to add extra annotations before the `Store` update is triggered
+
+<pre>
+\Hooks::register( 'SMW::FileUpload::BeforeUpdate', function( $filePage, $semanticData ) {
+
+ return true;
+} );
+</pre>
+
+## 2.5
+
+### SMW::Job::AfterUpdateDispatcherJobComplete
+
+* Version: 2.5
+* Description: Hook allows to add extra jobs after `UpdateDispatcherJob` has been processed.
+* Reference class: `\SMW\MediaWiki\Jobs\UpdateDispatcherJob`
+
+<pre>
+\Hooks::register( 'SMW::Job::AfterUpdateDispatcherJobComplete', function( $job ) {
+
+ // Find related dependencies
+ $title = $job->getTitle();
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::Installer::AfterCreateTablesComplete
+
+* Version: 2.5
+* Description: Hook allows to add extra tables after the creation process as been finalized.
+* Reference class: `\SMW\SQLStore\Installer`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::Installer::AfterCreateTablesComplete', function( $tableBuilder, $messageReporter ) {
+
+ // Output details on the activity
+ $messageReporter->reportMessage( '...' );
+
+ // See documentation in the available TableBuilder interface
+ $tableBuilder->create( ... );
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::Installer::AfterDropTablesComplete
+
+* Version: 2.5
+* Description: Hook allows to remove extra tables after the drop process as been finalized.
+* Reference class: `\SMW\SQLStore\Installer`
+
+<pre>
+\Hooks::register( 'SMW::SQLStore::Installer::AfterDropTablesComplete', function( $tableBuilder, $messageReporter ) {
+
+ // Output details on the activity
+ $messageReporter->reportMessage( '...' );
+
+ // See documentation in the available TableBuilder interface
+ $tableBuilder->drop( ... );
+
+ return true;
+} );
+</pre>
+
+## 3.0
+
+### SMW::GetPreferences
+
+* Version: 3.0
+* Description: Hook allows to add extra preferences that are ordered on the Semantic MediaWiki user preference tab
+* Reference class: `\SMW\MediaWiki\Hooks\GetPreferences`
+
+<pre>
+\Hooks::register( 'SMW::GetPreferences', function( $user, &$preferences ) {
+
+
+ return true;
+} );
+</pre>
+
+### SMW::Setup::AfterInitializationComplete
+
+* Version: 3.0
+* Description: Hook allows to modify global configuration after initialization of Semantic MediaWiki is completed
+* Reference class: `\SMW\Setup`
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::Setup::AfterInitializationComplete', function( &$vars ) {
+
+ // #2565
+ unset( $GLOBALS['wgGroupPermissions']['smwcurator'] );
+
+ return true;
+} );
+</pre>
+
+### SMW::Exporter::Controller::AddExpData
+
+* Version: 3.0
+* Description: Hook allows to add additional RDF data for a selected page (was `smwAddToRDFExport`)
+* Reference class: `SMWExportController`
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::Exporter::Controller::AddExpData', function( DIWikiPage $subject, &$expDataList, $hasRecursionDepth, $withBacklinks ) {
+
+ // $expData = new ExpData( ... );
+ // $expDataList[] = $expData;
+
+ return true;
+} );
+</pre>
+
+### SMW::SQLStore::EntityReferenceCleanUpComplete
+
+* Version: 3.0
+* Description: Hook allows to get information about which entities have been removed
+* Reference class: `PropertyTableIdReferenceDisposer`
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::SQLStore::EntityReferenceCleanUpComplete', function( $store, $id, $subject, $isRedirect ) {
+
+ return true;
+} );
+</pre>
+
+### SMW::LinksUpdate::ApprovedUpdate
+
+* Version: 3.0
+* Description: Hook allows to suppress an update where for example the `latestRevID` is not the revision that is approved an should not be used for the `SemanticData` representation.
+* Reference class: `SMW\MediaWiki\Hooks\LinksUpdateConstructed`
+
+If you do suppress a revision, please log the event and make it visible to a user (or administrator) that an update was refused.
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::LinksUpdate::ApprovedUpdate', function( $title, $latestRevID ) {
+
+ // If you need to decline an update
+ // return false;
+
+ return true;
+} );
+</pre>
+
+### SMW::Parser::ChangeRevision
+
+* Version: 3.0
+* Description: Hook allows to forcibly change a revision used during content parsing as in case of the `UpdateJob` execution or when running `rebuildData.php`.
+* Reference class: `SMW\ContentParser`
+
+If you do alter a revision, please log the event and make it visible to a user (or administrator) that it was changed.
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::Parser::ChangeRevision', function( $title, &$revision ) {
+
+ // Set a revision
+ // $revision = \Revision::newFromId( $id );
+
+ return true;
+} );
+</pre>
+
+### SMW::Admin::TaskHandlerFactory
+
+* Version: 3.0
+* Description: Hook allows to extend available `TaskHandler` in `Special:SemanticMediaWiki`
+* Reference class: `SMW\MediaWiki\Specials\Admin\TaskHandlerFactory`
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::Admin::TaskHandlerFactory', function( &$taskHandlers, $store, $outputFormatter, $user ) {
+
+ // Instance of TaskHandler
+ // $taskHandlers[] = new FooTaskHandler();
+
+ return true;
+} );
+</pre>
+
+### SMW::DataUpdater::ContentProcessor
+
+* Version: 3.0
+* Description: Hook allows to extend the `SemanticData` with information from the `Content` object
+* Reference class: `SMW\DataUpdater`
+
+<pre>
+use Hooks;
+
+Hooks::register( 'SMW::DataUpdater::ContentProcessor', function( $semanticData, $content ) {
+
+ if ( $content->getModel() === ' ... ' ) {
+ // $data = $content->getNativeData();
+ // ...
+ // $semanticData->addPropertyObjectValue( ... );
+ }
+
+ return true;
+} );
+</pre>
+
+## Other available hooks
+
+Subsequent hooks should be renamed to follow a common naming practice that help distinguish them from other hook providers. In any case this list needs details and examples.
+
+* `SMWParamFormat`, SMWResultFormat
+* `\SMW\Store`, SMWStore::updateDataBefore (SMW::Store::BeforeDataUpdateComplete)
+* `\SMW\Store`, SMWStore::updateDataAfter (SMW::Store::AfterDataUpdateComplete)
+* `SMWSQLStore3Writers`, SMWSQLStore3::updateDataBefore (SMW::SQLStore::BeforeDataUpdateComplete)
+
+[hooks]: https://www.mediawiki.org/wiki/Hooks "Manual:Hooks"
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.categories.json b/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.categories.json
new file mode 100644
index 00000000..ee644218
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.categories.json
@@ -0,0 +1,83 @@
+[
+ {
+ "name": "General",
+ "groups": [
+ {
+ "name": "Instance",
+ "classes": ["smw"]
+ }
+ ]
+ },
+ {
+ "name": "DataModel",
+ "groups": [
+ {
+ "name": "Factory",
+ "classes": [
+ "smw.Data"
+ ]
+ },
+ {
+ "name": "General",
+ "classes": [
+ "smw.dataItem*",
+ "smw.dataValue*"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Api",
+ "groups": [
+ {
+ "name": "General",
+ "classes": ["smw.Api*"]
+ },
+ {
+ "name": "Query",
+ "classes": ["smw.Query*"]
+ }
+ ]
+ },
+ {
+ "name": "Utilities",
+ "groups": [
+ {
+ "name": "General",
+ "classes": ["smw.util*"]
+ },
+ {
+ "name": "Support",
+ "classes": ["smw.setting*","smw.async*"]
+ },
+ {
+ "name": "Formats",
+ "classes": ["smw.formats*"]
+ }
+ ]
+ },
+ {
+ "name": "Test",
+ "groups": [
+ {
+ "name": "QUnit",
+ "classes": ["QUnit", "QUnit.assert"]
+ }
+ ]
+ },
+ {
+ "name": "Upstream",
+ "groups": [
+ {
+ "name": "jQuery",
+ "classes": ["jQuery", "jQuery.Event", "jQuery.Promise", "jQuery.Deferred", "jQuery.jqXHR", "QUnit"]
+ },
+ {
+ "name": "JavaScript",
+ "classes": [
+ "Array", "Boolean", "Date", "Function", "Number", "Object", "RegExp", "String"
+ ]
+ }
+ ]
+ }
+] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.json b/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.json
new file mode 100644
index 00000000..824916dc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/jsduck.json
@@ -0,0 +1,11 @@
+{
+ "--title": "Semantic MediaWiki Code Documentation",
+ "--categories": "../docs/jsduck.categories.json",
+ "--warnings": ["-no_doc"],
+ "--builtin-classes": true,
+ "--external": "HTMLDocument,Window",
+ "--":[
+ "../resources/docs",
+ "../resources/smw"
+ ]
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/docs/technical/migration-guide-3.0.md b/www/wiki/extensions/SemanticMediaWiki/docs/technical/migration-guide-3.0.md
new file mode 100644
index 00000000..590bd815
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/docs/technical/migration-guide-3.0.md
@@ -0,0 +1,67 @@
+# Migration guide
+
+This document contains migration information both for users and administratiors as well as developers when upgrading from Semantic MediaWiki 2.5.x to Semantic MediaWiki 3.0.x. Please consult the respective RELEASE-NOTES when upgrading from an earlier version of Semantic MediaWiki.
+
+## Users and administrators
+
+### List formats (incl. list, ol, ul, template)
+
+* Wrapped components of the `list` format in HTML elements
+* Added class attributes to HTML elements of `list`, `ol` and `ul` formats to facilitate styling
+* Added `plainlist`format
+* `template` format becomes alias of the `plainlist` format
+* `template` parameter is used when present, even if format is not `template`
+* Standardized parameters to templates: All standard parameters start with a `#`
+* Dedicated separators for values, properties and result "rows": `sep`, `propsep`, `valuesep`
+* Removed final list separator (", and")
+* Removed `?` as prefix for template arguments
+* Removed `template arguments` parameter
+* Removed `columns` parameter
+
+For details also see the [change description.](https://gist.github.com/s7eph4n/277e7804fe04954df7d1e15ae874b0d0)
+
+### Maintenance scripts
+
+- If you are still using maintenance scripts starting with the `SMW_` prefix you must now migrate to the new maintenance spript names. See the help pages on [maintenance scrips](https://www.semantic-mediawiki.org/wiki/Help:Maintenance_scripts) for further information.
+
+### Resources export
+
+- Resources are now being exported as Internationalized Resource Identifiers (IRI).
+ This means that the URIs are now being exported using Universal Coded Character Set (UCS) instead of American Standard Code for Information Interchange (ASCII). See the help page on configuration parameter [`$smwgExportResourcesAsIri`](https://www.semantic-mediawiki.org/wiki/Help:$smwgExportResourcesAsIri) for further information.
+
+## Developers
+
+### Removed classes and methods
+
+- Removed `DIProperty::findPropertyID`, deprecated since 2.1, use PropertyRegistry::findPropertyIdByLabel
+- Removed `DIProperty::getPredefinedPropertyTypeId`, deprecated since 2.1, use PropertyRegistry::getPropertyValueTypeById
+- Removed `DIProperty::findPropertyLabel`, deprecated since 2.1, use PropertyRegistry::findPropertyLabelById
+- Removed `DIProperty::registerProperty`, deprecated since 2.1, use PropertyRegistry::registerProperty
+- Removed `DIProperty::registerPropertyAlias`, deprecated since 2.1, use PropertyRegistry::registerPropertyAlias
+- Deprecated `PropertyValue::makeUserProperty`, use DataValueFactory::getInstance()->newPropertyValueByLabel;
+- Removed `PropertyValue::makeProperty`, use DataValueFactory
+
+### Hooks
+
+- Renamed `smwAddToRDFExport` to `SMW::Exporter::Controller::AddExpData`
+
+### Store
+
+- `Store::getPropertySubjects` is to return an `Iterator` hence an `array` type check should be avoided and if necessary
+use `iterator_to_array` to transform a result instance into a standard array
+
+#### Register predefined property
+
+<pre>
+\Hooks::register( 'SMW::Property::initProperties', function( $propertyRegistry ) {
+
+ $propertyRegistry->registerProperty( '__FOO', '_txt', 'Foo' );
+
+ $propertyRegistry->registerPropertyDescriptionByMsgKey(
+ '__FOO',
+ 'a-mediawiki-msg-key-with-a-description'
+ );
+
+ return true;
+} );
+</pre>
diff --git a/www/wiki/extensions/SemanticMediaWiki/extension.json b/www/wiki/extensions/SemanticMediaWiki/extension.json
new file mode 100644
index 00000000..6224eaa3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/extension.json
@@ -0,0 +1,29 @@
+{
+ "name": "SemanticMediaWiki",
+ "version": "3.0.2",
+ "author": [
+ "[http://korrekt.org Markus Krötzsch]",
+ "[https://www.mediawiki.org/wiki/User:Jeroen_De_Dauw Jeroen De Dauw]",
+ "James Hong Kong",
+ "[https://www.semantic-mediawiki.org/wiki/Contributors ...]"
+ ],
+ "url": "https://www.semantic-mediawiki.org",
+ "descriptionmsg": "smw-desc",
+ "namemsg": "smw-title",
+ "license-name": "GPL-2.0-or-later",
+ "type": "semantic",
+ "requires": {
+ "MediaWiki": ">= 1.27"
+ },
+ "MessagesDirs": {
+ "SemanticMediaWiki": [
+ "i18n"
+ ]
+ },
+ "callback": "SemanticMediaWiki::initExtension",
+ "ExtensionFunctions": [
+ "SemanticMediaWiki::onExtensionFunction"
+ ],
+ "load_composer_autoloader": true,
+ "manifest_version": 1
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ace.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ace.json
new file mode 100644
index 00000000..e90fe78a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ace.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Si Gam Acèh"
+ ]
+ },
+ "browse": "Rawôn wiki",
+ "smw-livepreview-loading": "Pumasoë..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ady-cyrl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ady-cyrl.json
new file mode 100644
index 00000000..9c65efbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ady-cyrl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "SamGamgee",
+ "Inyzh"
+ ]
+ },
+ "browse": "ÐÑкIÑƒÐ±Ð³ÑŠÐ¾Ð¼Ñ Ð°ÑˆÑŠÑ…ÑŒÑ‹Ñ€Ñ‹Ð¿Ð»ÑŠ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/aeb-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/aeb-latn.json
new file mode 100644
index 00000000..ef514cc5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/aeb-latn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Csisc"
+ ]
+ },
+ "browse": "Navīgī il-wīkī"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/af.json b/www/wiki/extensions/SemanticMediaWiki/i18n/af.json
new file mode 100644
index 00000000..e056f39a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/af.json
@@ -0,0 +1,51 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arnobarnard",
+ "Naudefj"
+ ]
+ },
+ "smw_finallistconjunct": ", en",
+ "smw_factbox_head": "Feite oor $1",
+ "smw_isspecprop": "Hierdie eienskap is 'n spesiale eienskap van hierdie wiki.",
+ "smw_printername_template": "Sjabloon",
+ "smw_parseerror": "Die gegewe waarde is onverstaanbaar.",
+ "smw_manytypes": "Meer as een tipe gedefinieer vir eienskap.",
+ "smw_emptystring": "Leë stringe word nie aanvaar nie.",
+ "smw_true_words": "waar,w,ja,j",
+ "smw_false_words": "vals,v,nee,n",
+ "smw_nofloat": "\"$1\" is nie 'n nommer nie.",
+ "smw_nodatetime": "Die datum \"$1\" kon nie verwerk word nie.",
+ "smw_emptysubquery": "'n Sub-navraag het geen geldige kondisie.",
+ "smw_exportrdf_submit": "Eksporteer",
+ "smw_propertyhardlyused": "Hierdie eienskap word skaars gebruik in hierdie wiki!",
+ "smw_unusedproperty_template": "$1 van tipe $2",
+ "smw_wantedproperty_template": "$1 ($2 gebruike)",
+ "smw_purge": "Verfris",
+ "types": "Tipes",
+ "smw_ask_sortby": "Sorteer volgens kolom (optioneel)",
+ "smw_ask_ascorder": "Toenemend",
+ "smw_ask_descorder": "Afnemend",
+ "smw_ask_submit": "Vind resultate",
+ "smw_ask_editquery": "Wysig navraag",
+ "smw_ask_hidequery": "Versteek navraag",
+ "smw_ask_help": "Navraag hulp",
+ "smw_ask_queryhead": "Navraag",
+ "smw_ask_printhead": "Addisionele uitdrukke (optioneel)",
+ "smw_ask_defaultformat": "standaard",
+ "searchbyproperty": "Soek volgens eienskap",
+ "smw_sbv_property": "Eienskap:",
+ "smw_sbv_value": "Waarde:",
+ "smw_sbv_submit": "Vind resultate",
+ "browse": "Blaai deur wiki",
+ "smw_browse_go": "Laat waai",
+ "smw_pp_from": "Vanaf bladsy",
+ "smw_pp_type": "Eienskap",
+ "smw_pp_submit": "Vind resultate",
+ "smw_result_prev": "Vorige",
+ "smw_result_next": "Volgende",
+ "smw_result_results": "Resultate",
+ "smw_result_noresults": "Geen resultate.",
+ "smw_unknowntype": "Onondersteunde tipe \"$1\" gedefinieer vir eienskap.",
+ "smw-livepreview-loading": "Laai tans…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/aln.json b/www/wiki/extensions/SemanticMediaWiki/i18n/aln.json
new file mode 100644
index 00000000..6b3dcb9b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/aln.json
@@ -0,0 +1,44 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mdupont"
+ ]
+ },
+ "smw_isspecprop": "Kjo pronë është një veti të veçantë në këtë wiki.",
+ "smw_concept_description": "Përshkrimi i konceptit \"$1\"",
+ "smw_no_concept_namespace": "Konceptet mund të përcaktohet në faqet në Konceptin: hapësira.",
+ "smw_multiple_concepts": "Çdo faqe koncept mund të ketë vetëm një përkufizim koncept.",
+ "smw_concept_cache_miss": "Koncepti \"$1\" nuk mund të përdoret në këtë moment, që nga konfigurimi Wiki kërkon që ajo të jetë llogaritur off-line. Nëse problemi nuk zhduken pas disa kohë, pyesni administratorin e faqes tuaj për të bërë këtë koncept në dispozicion.",
+ "smw_noinvannot": "Vlerat nuk mund të vendoset në pronat e anasjelltas.",
+ "smw_baduri": "URI e formës \"$1\" nuk lejohen.",
+ "smw_printername_count": "Rezultatet Count",
+ "smw_printername_csv": "eksport CSV",
+ "smw_printername_debug": "query Debug (për ekspertët)",
+ "smw_printername_embedded": "Embed faqe Përmbajtja",
+ "smw_printername_json": "eksport JSON",
+ "smw_printername_list": "Listë",
+ "smw_printername_ol": "Renditje",
+ "smw_printername_ul": "Itemization",
+ "smw_printername_table": "Tryezë",
+ "smw_printername_broadtable": "Tabela Broad",
+ "smw_printername_template": "Shabllon",
+ "smw-paramdesc-limit": "Numri maksimal i rezultateve të kthehen",
+ "smw-paramdesc-headers": "Afisho e titullit / emrat e pronës",
+ "smw-paramdesc-mainlabel": "Etiketë për të dhënë për emrin kryesore faqe",
+ "smw-paramdesc-link": "Show vlerat si link",
+ "smw-paramdesc-intro": "Teksti që do shfaqet para se rezultatet e pyetjes, nëse ka ndonjë",
+ "smw-paramdesc-outro": "Teksti që do shfaqet pas rezultatet e pyetjes, nëse ka ndonjë",
+ "smw-paramdesc-default": "Teksti që do shfaqet në qoftë se nuk ka rezultate query",
+ "smw-paramdesc-sep": "Ndarës për vlerat e",
+ "smw-paramdesc-template": "Emri i një template me të cilin për të shfaqur shkresat e",
+ "smw-paramdesc-columns": "Numri i rreshtave në të cilat për të shfaqur rezultate (default është $1)",
+ "smw-paramdesc-embedformat": "HTML tag përdoret për të definuar tituj",
+ "smw-paramdesc-embedonly": "Afisho nuk ka tituj",
+ "smw-paramdesc-searchlabel": "Teksti në lidhje me rezultatet e",
+ "smw_iq_disabled": "pyetje Semantic kanë qenë të paaftë për këtë wiki.",
+ "smw_iq_moreresults": "Rezultatet e ... më tej",
+ "smw_parseerror": "Vlera e dhënë nuk është kuptuar.",
+ "smw_notitle": "\"$1\" nuk mund të përdoret si një emër faqe në këtë wiki.",
+ "smw_wrong_namespace": "Vetëm faqet në hapësirën \"$1\" lejohen këtu.",
+ "smw_unknowntype": "Lloj i pasuportuar \"$1\" të përcaktuara për pronën."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/am.json b/www/wiki/extensions/SemanticMediaWiki/i18n/am.json
new file mode 100644
index 00000000..3280612e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/am.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Codex Sinaiticus"
+ ]
+ },
+ "smw_result_next": "ቀጥሎ",
+ "smw-livepreview-loading": "በመጫን ላይ áŠá‹..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/an.json b/www/wiki/extensions/SemanticMediaWiki/i18n/an.json
new file mode 100644
index 00000000..ef6ee797
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/an.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Juanpabl"
+ ]
+ },
+ "smw_printername_template": "Plantilla",
+ "smw_nodatetime": "No s'ha entendito a calendata \"$1\".",
+ "smw-livepreview-loading": "Cargando…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ang.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ang.json
new file mode 100644
index 00000000..657612d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ang.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gott wisst"
+ ]
+ },
+ "browse": "SÄ“can geond wiki"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/anp.json b/www/wiki/extensions/SemanticMediaWiki/i18n/anp.json
new file mode 100644
index 00000000..2287fab9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/anp.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Angpradesh"
+ ]
+ },
+ "browse": "विकि देखऽ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ar.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ar.json
new file mode 100644
index 00000000..793e0974
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ar.json
@@ -0,0 +1,856 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mahmoud Zouari mahmoudzouari@yahoo.fr http://www.cri.ensmp.fr",
+ "Meno25",
+ "OsamaK",
+ "Ouda",
+ "أحمد",
+ "الهميان",
+ "روخو",
+ "محمد أحمد عبد الÙتاح",
+ "أحمد غربية Ahmad Gharbeia <ahmad@arabdigitalexpression.org>",
+ "Kghbln",
+ "Alaa",
+ "Moud hosny",
+ "ديÙيد",
+ "Mido",
+ "20عبد العزيز",
+ "Yasser Yousssef"
+ ]
+ },
+ "smw-desc": "تقرّب الويكي إلى كلÙÙ‘ من النّظم الحاسوبية ''Ùˆ'' البشر ([https://www.semantic-mediawiki.org/wiki/Help:User_manual دليل المستخدم])",
+ "smw-title": "ميدياويكي الدلالية",
+ "smw-upgrade-error": "تم تثبيت وتمكين ميدياويكي الدلالي لكنه ÙŠÙتقد إلى [https://www.semantic-mediawiki.org/wiki/Help:Upgrade Ù…Ùتاح ترقية] مناسب يتطابق مع: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "لماذا أرى هذا الخطأ؟",
+ "smw-upgrade-error-why-explain": "لقد تغير هيكل قاعدة البيانات الداخلية ÙÙŠ ميدياويكي ويتطلب بعض التعديلات لكي تعمل بكامل طاقتها، يمكن أن يكون هناك العديد من الأسباب بما Ùيها: \n* تمت إضاÙØ© خصائص مرممة إضاÙية (يتطلب إعداد جدول إضاÙÙŠ) \n* تحتوي الترقية على بعض التغييرات على الجداول أو الÙهارس التي تقوم بإجراء اعتراض إلزامي قبل الوصول إلى البيانات",
+ "smw-upgrade-error-how-title": "كي٠أقوم بإصلاح هذا الخطأ؟",
+ "smw-upgrade-error-how-explain": "يجب على الإداري (أو أي شخص لديه صلاحيات الإداري) أن يدير سكريبت الصيانة [https://www.mediawiki.org/wiki/Manual:Update.php update.php] بميدياويكي أو [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] بميدياويكي الدلالي، يمكنك أيضا الرجوع إلى الصÙحات التالية للمزيد من المساعدة:\n* تعليمات [https://www.semantic-mediawiki.org/wiki/Help:Installation التثبيت]\n* صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting استكشا٠الأخطاء وإصلاحها]",
+ "smw-semantics-not-enabled": "لم يتم تمكين وظائ٠ميدياويكي الدلالي لهذا الويكي.",
+ "smw_viewasrdf": "تلقيمة RDF",
+ "smw_finallistconjunct": " Ùˆ",
+ "smw-factbox-head": "...المزيد عن \"$1\"",
+ "smw-factbox-facts": "حقائق",
+ "smw-factbox-facts-help": "يعرض البيانات والحقائق التي تم إنشاؤها بواسطة مستخدم",
+ "smw-factbox-attachments": "المرÙقات",
+ "smw-factbox-attachments-help": "يعرض المرÙقات المتاحة",
+ "smw-factbox-facts-derived": "حقائق مشتقة",
+ "smw-factbox-facts-derived-help": "يعرض الحقائق التي تم استخلاصها من القواعد أو بمساعدة تقنيات التÙكير الأخرى",
+ "smw_isspecprop": "هذه خصيصة مميَّزة ÙÙŠ هذه الويكي.",
+ "smw-concept-cache-header": "استخدام ذاكرة التخزين المؤقت",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count مخبأ Ù…Ùهوم] يحتوي على {{PLURAL:$1|كيان '''واحد'''|'''$1''' كيانات}} ($2).",
+ "smw-concept-no-cache": "لا يوجد مخبأ متاح.",
+ "smw_concept_description": "وص٠المÙهوم \"$1\"",
+ "smw_no_concept_namespace": "المÙاهيم لا يمكن تعريÙها إلا ÙÙŠ الصÙحات ضمن Ù…Ùهوم : نطاق التسمية",
+ "smw_multiple_concepts": "كل صÙحة Ù…Ùهوم لا يمكن أن تضم أكثر من تعري٠مÙهوم واحد.",
+ "smw_concept_cache_miss": "المبدأ \"$1\" لا يمكن استخدامه حاليا إذ أن كيÙية ضبط الويكي تقضي بتعطيلها. إذا لم تنته المشكلة بعد برهة، Ùسَل إداري الموقع ليتيح هذا المبدأ.",
+ "smw_noinvannot": "لا يمكن تعيين قيم لخصائص معكوسة.",
+ "version-semantic": "ملحقات دلالية",
+ "smw_baduri": "المسارات ÙÙŠ الصيغة \"$1\" غير مسموح بها هنا.",
+ "smw_csv_link": "CVS",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "عدّ النتائج",
+ "smw_printername_csv": "تصدير ÙÙŠ صيغة CSV",
+ "smw_printername_dsv": "تصدير ÙÙŠ صيغة DSV",
+ "smw_printername_debug": "استعلام صيانة (للخبراء)",
+ "smw_printername_embedded": "تضمين محتويات الصÙحة",
+ "smw_printername_json": "تصدير ÙÙŠ صيغة JSON",
+ "smw_printername_list": "قائمة",
+ "smw_printername_plainlist": "قائمة عادية",
+ "smw_printername_ol": "عناصر Ù…Ùرقَّمة",
+ "smw_printername_ul": "عناصر مسرودة",
+ "smw_printername_table": "جدول",
+ "smw_printername_broadtable": "جدول عريض",
+ "smw_printername_template": "قالب",
+ "smw_printername_templatefile": "مل٠قالب",
+ "smw_printername_rdf": "تصدير ÙÙŠ صيغة RDF",
+ "smw_printername_category": "تصنيÙ",
+ "validator-type-class-SMWParamSource": "نصّ",
+ "smw-paramdesc-limit": "العدد الأقصى للنتائج المÙرجعة",
+ "smw-paramdesc-offset": "إزاحة النتيجة الأولى",
+ "smw-paramdesc-headers": "اعرض العناوين/أسماء الخصائص",
+ "smw-paramdesc-mainlabel": "العنوان الذي سيÙعيّن للصÙحة الرئيسية",
+ "smw-paramdesc-link": "أظهر القيم كروابط",
+ "smw-paramdesc-intro": "النّصّ المعروض قبل نتائج الاستعلام (إنْ ÙˆÙجدت)",
+ "smw-paramdesc-outro": "النّصّ المعروض بعد نتائج الاستعلام (إنْ ÙˆÙجدت)",
+ "smw-paramdesc-default": "النّصّ المعروض إذا لم توجد نتائج للاستعلام",
+ "smw-paramdesc-sep": "الÙاصل بين النتائج",
+ "smw-paramdesc-propsep": "الÙاصل بين خصائص إدخال النتيجة",
+ "smw-paramdesc-valuesep": "الÙاصل بين قيم خاصية نتيجة",
+ "smw-paramdesc-showsep": "أظهر الÙاصلة ÙÙŠ رأس مل٠CSV على النحو (\"sep=<الرمز>\")",
+ "smw-paramdesc-distribution": "بدلاً من عرض كل القيم، اسرد عدد مرات ورودها.",
+ "smw-paramdesc-distributionsort": "رتّب توزيعات القيم حسب عدد مرات ورودها.",
+ "smw-paramdesc-distributionlimit": "حدّ توزيع القيم بتواتر بعض القيم لا كلّها.",
+ "smw-paramdesc-aggregation": "حدد ما يجب أن يرتبط به التجميع",
+ "smw-paramdesc-template": "اسم القالب المستخدم لعرض المخرجات به",
+ "smw-paramdesc-columns": "عدد الأعمدة لعرض النتائج بها",
+ "smw-paramdesc-userparam": "قيمة تÙمرَّر ÙÙŠ كل استدعاء قالب إذا كان القالب سيÙستخدَم.",
+ "smw-paramdesc-class": "Ùئة CSS إضاÙية لتعيين للقائمة",
+ "smw-paramdesc-introtemplate": "اسم قالب ÙŠÙعرض قبل نتائج الاستعلام، إنْ ÙˆÙجÙدَت.",
+ "smw-paramdesc-outrotemplate": "اسم قالب ÙŠÙعرض بعد نتائج الاستعلام، إنْ ÙˆÙجÙدَت.",
+ "smw-paramdesc-embedformat": "وسم HTML المستخدم لتعري٠الترويسة",
+ "smw-paramdesc-embedonly": "لا تظهر الترويسات",
+ "smw-paramdesc-table-class": "Ùئة CSS إضاÙية تÙعيَّن للجدول",
+ "smw-paramdesc-table-transpose": "عرض رؤوس الجدول عموديا والنتائج Ø£Ùقيا",
+ "smw-paramdesc-rdfsyntax": "نَحو٠RDF المÙستَخدم",
+ "smw-paramdesc-csv-sep": "تحديد Ùاصل الأعمدة",
+ "smw-paramdesc-csv-valuesep": "تحديد Ùاصل قيمة",
+ "smw-paramdesc-csv-merge": "دمج الصÙو٠وقيم العمود مع معر٠موضوع متطابق (ÙŠÙعرَ٠أيضا باسم العمود الأول)",
+ "smw-paramdesc-csv-bom": "إضاÙØ© بوم (حر٠لإشارة إنديانيس) ÙÙŠ الجزء العلوي من مل٠الإخراج",
+ "smw-paramdesc-dsv-separator": "الÙاصلة المÙستَخدَمة",
+ "smw-paramdesc-dsv-filename": "اسم مل٠DSV",
+ "smw-paramdesc-filename": "الاسم للمل٠المستخرج",
+ "smw-smwdoc-description": "يعرض جدولا يضمّ كل المعامÙلات التي يمكن استخدامها مع صيغة النتائج المختارة مع قيمها المبدئية Ùˆ وصÙها.",
+ "smw-smwdoc-default-no-parameter-list": "لا يوÙر تنسيق النتيجة هذا وسائط تنسيق محددة.",
+ "smw-smwdoc-par-format": "صيغة النتائج التي تÙعرض تÙاصيل معامÙلاتها.",
+ "smw-smwdoc-par-parameters": "أي المعاملات تÙعرَض. \"specific\" تعني الخاصة بالصيغة المختارة Ùˆ \"base\" تعني المشتركة بين كل الصيغ Ùˆ \"all\" تعني كلّها.",
+ "smw-paramdesc-sort": "الخصيصة التي تÙرتّب عليها نتائج الاستعلام",
+ "smw-paramdesc-order": "رتبة ترتيب الاستعلام",
+ "smw-paramdesc-searchlabel": "نص مواصلة عرض النتائج",
+ "smw-paramdesc-named_args": "المعاملات الممررة بأسمائها إلى القالب",
+ "smw-paramdesc-template-arguments": "لتعيين كيÙية تمرير الوسائط المسماة إلى القالب",
+ "smw-paramdesc-import-annotation": "يجب نسخ بيانات مشروحة إضاÙية أثناء تحليل موضوع ما",
+ "smw-paramdesc-export": "خيار التصدير",
+ "smw-paramdesc-prettyprint": "مخرجات تجميلية لإدخال مساÙات Ùˆ سطور Ùارغة",
+ "smw-paramdesc-json-unescape": "الإخراج لاحتواء شرطات مائلة غير منÙذة وأحر٠يونيكود متعددة البايت",
+ "smw-paramdesc-json-type": "نوع التسلسل",
+ "smw-paramdesc-source": "مصدر بديل للاستعلام",
+ "smw-paramdesc-jsonsyntax": "نحو٠JSON المÙستَخدَم",
+ "smw-printername-feed": "تلقيمات Atom و RSS",
+ "smw-paramdesc-feedtype": "نوع التلقيمة",
+ "smw-paramdesc-feedtitle": "نصÙÙ‘ عنوان التلقيمة",
+ "smw-paramdesc-feeddescription": "نصÙÙ‘ وص٠التلقيمة",
+ "smw-paramdesc-feedpagecontent": "محتوى الصÙحة المعروض مع التلقيمة",
+ "smw-label-feed-description": "تلقيمة $2 $1",
+ "smw-paramdesc-mimetype": "نوع الوسائط (نوع MIME) لمل٠الإخراج",
+ "smw_iq_disabled": "الاستعلامات الدلالية Ù…Ùعطّلة ÙÙŠ هذه الويكي.",
+ "smw_iq_moreresults": "…مزيد من النتائج",
+ "smw_parseerror": "القيمة المعطاة لم يتم Ùهمها.",
+ "smw_decseparator": "Ù«",
+ "smw_kiloseparator": "،",
+ "smw_notitle": "\"$1\" لا يمكن أن يكون اسم صÙحة ÙÙŠ هذه الويكي.",
+ "smw_noproperty": "\"$1\" لا يمكن أن يكون اسم خصيصة ÙÙŠ هذه الويكي.",
+ "smw_wrong_namespace": "وحدها الصÙحات ÙÙŠ النطاق \"$1\" مسموح بها هنا.",
+ "smw_manytypes": "عÙرّ٠أكثر من نوع بيانات للخصيصة ذاتها.",
+ "smw_emptystring": "النصوص الÙارغة غير مقبولة.",
+ "smw_notinenum": "\"$1\" ليست ÙÙŠ قائمة ($2) [[Property:Allows value|للقيم المسموح بها]] للخاصية \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" ليست ضمن القائمة ($2) [[Property:Allows value|القيم المسموح بها]] للخاصية \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" ليست ضمن نطاق \"$2\" المحدد بواسطة قيد [[Property:Allows value|القيم المسموح بها]] للخاصية \"$3\".",
+ "smw_noboolean": "\"$1\" لم ÙŠÙتعرّ٠عليها كقيمة منطقية (صواب/بطلان).",
+ "smw_true_words": "صواب،ص،نعم،ن",
+ "smw_false_words": "بطلان،ب،لا،ل",
+ "smw_nofloat": "ليس عددا \"$1\".",
+ "smw_infinite": "الأرقام الكبيرة مثل \"$1\" غير مدعومة.",
+ "smw_unitnotallowed": "\"$1\" ليس Ù…ÙعرَّÙا كوحدة قياس صحيحة لهذه الخصيصة.",
+ "smw_nounitsdeclared": "لم تÙعرّ٠لهذه الخصيصة وحدات قياس.",
+ "smw_novalues": "لم تÙعيّن أي Ù‚ÙÙŠÙŽÙ….",
+ "smw_nodatetime": "التاريخ \"$1\" لم يتم Ùهمه.",
+ "smw_toomanyclosing": "يبدو أن \"$1\" ترد كثيرا للغاية ÙÙŠ الاستعلام.",
+ "smw_noclosingbrackets": "بعض استخدامات \"<nowiki>[[</nowiki>\" ÙÙŠ الاستعلام لم تÙغلَق بوضع \"]]\" مقابلة.",
+ "smw_misplacedsymbol": "الرمز \"$1\" استÙخدÙÙ… ÙÙŠ موضع لا ÙŠÙيد Ùيه.",
+ "smw_unexpectedpart": "الجزء \"$1\" من الاستعلام لم ÙŠÙهم.\nالنتائج قد لا تكون كالمتوقعة.",
+ "smw_emptysubquery": "يوجد استعلام Ùرعي بلا شرط صحيح.",
+ "smw_misplacedsubquery": "استÙخدÙÙ… استعلام Ùرعي ÙÙŠ موضع غير مسموح Ùيه بالاستعلامات الÙرعية.",
+ "smw_valuesubquery": "الاستعلامات الÙرعية غير مدعومة لقيم الخصيصة \"$1\".",
+ "smw_badqueryatom": "جزء ما \"<nowiki>[[…]]</nowiki>\" من الاستعلام لم يتم Ùهمه.",
+ "smw_propvalueproblem": "قيمة الخصيصة \"$1\" لم يتم Ùهمها.",
+ "smw_noqueryfeature": "تÙصيلة ما ÙÙŠ الاستعلام ليست مدعومة ÙÙŠ هذه الويكي ÙأغÙÙÙ„ جزء من الاستعلام ($1).",
+ "smw_noconjunctions": "الوصل ÙÙŠ الاستعلامات غير مدعوم ÙÙŠ هذه الويكي ÙØ£ÙغÙÙÙ„ جزء من الاستعلام ($1).",
+ "smw_nodisjunctions": "المÙارق ÙÙŠ الاستعلامات ليست مدعومة ÙÙŠ هذه الويكي ÙØ£ÙغÙÙÙ„ جزء من الاستعلام $1.",
+ "smw_querytoolarge": "{{PLURAL:$2|شروط الاستعلام}} التالية لم يمكن أخذها ÙÙŠ الاعتبار بسبب قيود من الويكي على حجم أو عمق الاستعلام: <code>$1</code>.",
+ "smw_notemplategiven": "أدخل قيمة للمعامل \"template\" لكي تعمل صيغة الاستعلام هذه.",
+ "smw_db_sparqlqueryproblem": "تعذّر إيجاد نتيجة للاستعلام من قاعدة بيانات SPARQL. هذه العطل قد يكون مؤقتا أو قد يدّل على علّة ÙÙŠ برمجية قاعدة البيانات.",
+ "smw_db_sparqlqueryincomplete": "الإجابة على هذا الاستعلام اتّضح أنها صعبة للغاية Ùˆ قد تم إغÙالها. بعض النتائج قد تكون ناقصة. استخدم استعلاما أبسط لو أمكن.",
+ "smw_type_header": "الخصائص من النوع \"$1\"",
+ "smw_typearticlecount": "التالية {{PLURAL:$1||خصيصة واحدة|خصيصتان|$1 خصائص|$1 خصيصة}} من هذا النوع.",
+ "smw_attribute_header": "الصÙحات التي تستخدم الخصيصة \"$1\"",
+ "smw_attributearticlecount": "التالية {{PLURAL:$1||الصÙحة التي لها|الصÙحتان اللتان لهما|الصÙحات التي لها}} هذه الخصيصة.",
+ "smw-propertylist-subproperty-header": "خصائص Ùرعية",
+ "smw-propertylist-redirect-header": "مرادÙات",
+ "smw-propertylist-error-header": "صÙحات بمهام غير لائقة",
+ "smw-propertylist-count": "عرض $1 {{PLURAL:$1|كيان|كيانات}} مرتبط.",
+ "smw-propertylist-count-with-restricted-note": "عرض $1 {{PLURAL:$1|كيان|كيانات}} مرتبط (يتوÙر المزيد ولكن يقتصر العرض على \"$2\").",
+ "smw-propertylist-count-more-available": "عرض $1 {{PLURAL:$1|كيان|كيانات}} مرتبط (يتوÙر المزيد)",
+ "specialpages-group-smw_group": "سيمانتيك ميدياويكي",
+ "exportrdf": "تصدير الصÙحات إلى RDF",
+ "smw_exportrdf_docu": " هذه الصÙحة تتيح لك الحصول على بيانات من صÙحة ÙÙŠ صيغة RDF.\nلتصدير صÙحات، أدخل عناوينها ÙÙŠ حقل النص أدناه، عنوانا واحدا ÙÙŠ السطر.",
+ "smw_exportrdf_recursive": "صدّر كل الصÙحات ذات الصلة على نحو عودي.\nلاحظ أن النتيجة قد تكون كبيرة.",
+ "smw_exportrdf_backlinks": "كذلك صدّر كل الصÙحات التي تشير إلى صÙحات تم تصديرها.\nهذا يولد RDF قابلا للتصÙØ­.",
+ "smw_exportrdf_lastdate": "لا تصدّر الصÙحات التي لم تتغير منذ لحظة معينة ÙÙŠ الزمن.",
+ "smw_exportrdf_submit": "صدّر",
+ "uriresolver": "حال_المسارات",
+ "properties": "الخصائص",
+ "smw_properties_docu": "الخصائص التالية مستخدمة ÙÙŠ هذه الويكي.",
+ "smw_property_template": "$1 من نوع $2 ({{PLURAL:$3|مرة|مرّتان|$3 مرات}})",
+ "smw_propertylackspage": "كل خصيصة يجب أن توص٠بصÙحة!",
+ "smw_propertylackstype": "لم يحدد نوع لهذه الخصيصة (اÙتÙرÙضَ النوع $1 مؤقتا).",
+ "smw_propertyhardlyused": "هذه الخصيصة تكاد لا تÙستخدم ÙÙŠ الويكي!",
+ "smw-property-name-invalid": "الخصيصة $1 لا يمكن استخدامها (اسم الخصيصة غير صحيح).",
+ "smw-property-name-reserved": "تم إدراج \"$1\" كاسم محجوز ولا يجب استخدامه كخاصية، قد تحتوي صÙحة [https://www.semantic-mediawiki.org/wiki/Help:Property_naming صÙحة المساعدة] التالية على معلومات عن سبب حجز هذا الاسم.",
+ "smw-sp-property-searchform": "عرض الخصائص التي تحتوي على:",
+ "smw-sp-property-searchform-inputinfo": "الإدخال حساس لحالة الأحرÙØŒ Ùˆ عندما يستخدم كمرشّح Ùإنّ الخصائص التي تتطابق مع الشرط وحدها تÙعرض.",
+ "smw-special-property-searchform": "عرض الخصائص التي تحتوي على:",
+ "smw-special-property-searchform-inputinfo": "الإدخال حساس لحالة الأحر٠وعند استخدامه لتصÙية، يتم عرض الخصائص التي تتناسب مع الحالة Ùقط.",
+ "smw-special-property-searchform-options": "خيارات",
+ "smw-special-wantedproperties-filter-label": "مرشح:",
+ "smw-special-wantedproperties-filter-none": "لا شيء",
+ "smw-special-wantedproperties-filter-unapproved": "غير مواÙÙ‚ عليها",
+ "smw-special-wantedproperties-filter-unapproved-desc": "خيار التصÙية المستخدم ÙÙŠ اتصال مع وضع السلطة.",
+ "concepts": "المÙاهيم",
+ "smw-special-concept-docu": "يمكن عدّ [https://www.semantic-mediawiki.org/wiki/Help:Concepts المÙهوم] تصنيÙا ديناميًّا، أي تعدادًا لمجموعة من الصÙحات لم ÙŠÙنشّأ يدويا، بل تÙولّده ميدياويكي الدلالية من وص٠استعلام Ù…Ùعطى.",
+ "smw-special-concept-header": "قائمة Ù…Ùاهيم",
+ "smw-special-concept-count": "{{PLURAL:$1|المÙهوم|المÙهومان|$1 المÙاهيم}} التالية {{PLURAL:$1|مسرود|مسرودان|مسرودة}}",
+ "smw-special-concept-empty": "لا توجد Ù…Ùاهيم.",
+ "unusedproperties": "خصائص غير مستخدمة",
+ "smw-unusedproperties-docu": "تسرد هذه الصÙحة [https://www.semantic-mediawiki.org/wiki/Unused_properties الخصائص غير المستخدمة] التي تم تعريÙها على الرغم من عدم استÙادة صÙحات أخرى منها، للحصول على عرض متباين; انظر الصÙحات الخاصة [[Special:Properties|كل الخصائص]] أو [[Special:WantedProperties|خصائص مطلوبة]].",
+ "smw-unusedproperty-template": "$1 من نوع $2",
+ "wantedproperties": "خصائص مطلوبة",
+ "smw-wantedproperties-docu": "تسرد هذه الصÙحة [https://www.semantic-mediawiki.org/wiki/Wanted_properties خصائص مطلوبة] مستخدمة ÙÙŠ الويكي ولكن لا توجد صÙحات تصÙها، للحصول على عرض متباين; انظر الصÙحات الخاصة [[Special:Properties|كل الخصائص]] أو [[Special:UnusedProperties|خصائص غير مستخدمة]].",
+ "smw-wantedproperty-template": "$1 ({{PLURAL:$2|استخدام|استخدامان|$2 استخدامات}})",
+ "smw-special-wantedproperties-docu": "تسرد هذه الصÙحة [https://www.semantic-mediawiki.org/wiki/Wanted_properties الخصائص المطلوبة] المستخدمة ÙÙŠ الويكي ولكن ليست لديها صÙحة تصÙها، للحصول على عرض متباين; انظر الصÙحات الخاصة [[Special:Properties|كل الخصائص]] أو[[Special:UnusedProperties|خصائص غير مستخدمة]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|استخدام|استخدامات}})",
+ "smw_purge": "أنعش",
+ "smw-purge-failed": "Ùشل التحديث",
+ "types": "الأنواع",
+ "smw_types_docu": "قائمة [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes أنواع البيانات المتاحة] بكل [https://www.semantic-mediawiki.org/wiki/Help: نوع] يمثل مجموعة Ùريدة من السمات لوص٠قيمة من حيث التخزين وعرض الخصائص التي هي وراثية إلى الخاصية المعينة.",
+ "smw-special-types-no-such-type": "\"$1\" غير معرو٠أو لم يتم تحديده كنوع بيانات صحيح.",
+ "smw-statistics": "إحصائيات دلالية",
+ "smw-statistics-property-instance": "{{PLURAL:$1|قيمة|قيمتان|قيمة}} لخصائص (الإجمالي)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|خصيصة|خصيصتان|$1 خصائص}}]] (الإجمالي)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|خصيصة|خصيصتان|$1 خصائص}} (الإجمالي)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|خاصية|خصائص}}]] (تÙستخدَم مع قيمة واحدة على الأقل)",
+ "smw-statistics-property-page": "{{PLURAL:$1|خصيصة|خصيصتان|$1 خصائص}} (موصوÙØ© بصÙحات)",
+ "smw-statistics-property-type": "{{PLURAL:$1|خصيصة|خصيصتان|$1 خصائص}} (معيّن لها نوع بيانات)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|استعلام|استعلامات}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|استعلام|استعلامان|$1 استعلامات}}]]",
+ "smw-statistics-query-size": "حجم الاستعلام",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Ù…Ùهوما|Ù…Ùهومان|$1 Ù…Ùهوما}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Ù…Ùهوما|Ù…Ùهومان|$1 Ù…Ùهوما}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|كيانا Ùرعيا|كيانان Ùرعيان|$1 كيانا Ùرعيا}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|كائن Ùرعي|كائنات Ùرعية}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|نوع بيانات|نوعي بيانات|$1 نوع بيانات}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|قيمة خاصية|قيم خواص}} ([[Special:ProcessingErrorList|{{PLURAL:$1|شرح غير مناسب|شروح غير مناسبة}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|قيمة خاصية|قيم خواص}} ({{PLURAL:$1|شرح مناسب|شروح مناسبة}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|كيان|كيانات}} قديم (تم وضع علامة للإزالة)",
+ "smw_uri_doc": "حالّّ المسارات ÙŠÙطبّق مواصÙØ© [$1 W3C TAG finding on httpRange-14]. إنه يعمل على ألا يتحوّل البشر إلى مواقع ÙˆÙب.",
+ "ask": "بحث دلالي",
+ "smw-ask-help": "يحتوي هذا القسم على بعض الروابط للمساعدة ÙÙŠ شرح كيÙية استخدام البحث الدلالي بناء جملة <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages اختيار الصÙحات] يص٠كيÙية اختيار الصÙحات وبناء الشروط\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators مشغلو البحث] قوائم عوامل البحث المتاحة بما ÙÙŠ ذلك تلك الخاصة باستعلامات النطاق والحرو٠البدلية\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_informationعرض المعلومات] توضح استخدام عبارات الطباعة وخيارات التنسيق",
+ "smw_ask_sortby": "الترتيب حسب العمود (اختياري)",
+ "smw_ask_ascorder": "تصاعدي",
+ "smw_ask_descorder": "تنازلي",
+ "smw-ask-order-rand": "عشوائي",
+ "smw_ask_submit": "جÙد نتائجًا",
+ "smw_ask_editquery": "[تعديل الاستعلام]",
+ "smw_add_sortcondition": "[أض٠شرط ترتيب]",
+ "smw-ask-sort-add-action": "أض٠شرط الÙرز",
+ "smw_ask_hidequery": "إخÙاء استعلام (عرض مدمج)",
+ "smw_ask_help": "مساعدة ÙÙŠ الاستعلامات",
+ "smw_ask_queryhead": "شرط",
+ "smw_ask_printhead": "اختيار النسخة المطبوعة",
+ "smw_ask_printdesc": "(أدخل اسم خصيصة واحدة ÙÙŠ كل سطر)",
+ "smw_ask_format_as": "أخرجه ÙÙŠ صيغة:",
+ "smw_ask_defaultformat": "المبدئية",
+ "smw_ask_otheroptions": "خيارات أخرى",
+ "smw-ask-otheroptions-info": "يضمّ هذا القسم خيارات لتعديل شكل مخرجات البيانات. وص٠المعاملات تمكن مطالعته بالتحويم Ùوق كل منها.",
+ "smw-ask-otheroptions-collapsed-info": "انقر علامة + لمطالعة كل الخيارات المتاحة",
+ "smw_ask_show_embed": "اعرض رموز التضمين",
+ "smw_ask_hide_embed": "أخÙ٠رموز التضمين",
+ "smw_ask_embed_instr": "لتضمين هذا الاستعلام ÙÙŠ صÙحة ويكي استخدم الرموز التالية.",
+ "smw-ask-delete": "إزالة",
+ "smw-ask-sorting": "الترتيب",
+ "smw-ask-options": "خيارات",
+ "smw-ask-options-sort": "خيارات الÙرز",
+ "smw-ask-format-options": "التنسيق والخيارات",
+ "smw-ask-parameters": "وسائط",
+ "smw-ask-search": "بحث",
+ "smw-ask-debug": "تصحيح",
+ "smw-ask-debug-desc": "يولد معلومات تصحيح الاستعلام",
+ "smw-ask-no-cache": "تعطيل ذاكرة التخزين المؤقت للاستعلام",
+ "smw-ask-no-cache-desc": "النتائج بدون ذاكرة التخزين المؤقت للاستعلام",
+ "smw-ask-result": "النتيجة",
+ "smw-ask-empty": "محو جميع الإدخالات",
+ "smw-ask-download-link-desc": "تحميل النتائج الاستعلام بصيغة $1",
+ "smw-ask-format": "الصيغة",
+ "smw-ask-format-selection-help": "المساعدة ÙÙŠ التنسيق المحدد: $1",
+ "smw-ask-condition-change-info": "تم تغيير الشرط ويتطلب محرك البحث إعادة تشغيل الاستعلام لإنتاج نتائج تتطابق مع المتطلبات الجديدة.",
+ "smw-ask-input-assistance": "مساعدة الإدخال",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance مساعدة الإدخال] يتم توÙيرها للطباعة، والÙرز، وحقل الشرط، يتطلب حقل الشرط استخدام إحدى البادئات التالية:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> لجلب اقتراحات خاصية (على سبيل المثال <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> لجلب اقتراحات تصنيÙ",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> لجلب اقتراحات Ù…Ùهوم",
+ "smw-ask-format-change-info": "تم تعديل الصيغة وهي مطلوبة لتنÙيذ الاستعلام مرة أخرى لمطابقة الوسائط وخيارات التصور الجديدة.",
+ "smw-ask-format-export-info": "الصيغة المحددة هي صيغة التصدير التي ليس لها تمثيل مرئي وبالتالي يتم توÙير النتائج Ùقط كتنزيل.",
+ "smw-ask-query-search-info": "الاستعلام <code><nowiki>$1</nowiki></code> was تم الرد عليه من قبل {{PLURAL:$3|1=<code>$2</code> (من ذاكرة التخزين المؤقت)|<code>$2</code> (من ذاكرة التخزين المؤقت)|<code>$2</code>}} ÙÙŠ $4 {{PLURAL:$4|ثانية|ثواني}}.",
+ "searchbyproperty": "البحث بالخصائص",
+ "processingerrorlist": "قائمة معالجة الخطأ",
+ "propertylabelsimilarity": "تقرير تشابه تسمية الخواص",
+ "smw-processingerrorlist-intro": "توÙر القائمة التالية نظرة عامة حول معالجة الأخطاء التي ظهرت ÙÙŠ اتصال ب[https://www.semantic-mediawiki.org/ ميدياويكي الدلالي]ØŒ من المستحسن مراقبة هذه القائمة على أساس منتظم، وتصحيح شروح القيمة غير الصالحة.",
+ "smw_sbv_docu": "البحث عن كل الصÙحات ذات خصيصة معينة بقيمتها.",
+ "smw_sbv_novalue": "أدخل قيمة صحيحة للخصيصة، أو اعرض كل قيم الخصيصة \"$1\"",
+ "smw_sbv_displayresult": "كل الصÙحات ذات الخصيصة \"$1\" بالقيمة \"$2\"",
+ "smw_sbv_displayresultfuzzy": "كل الصÙحات ذات الخصيصة \"$1\" بالقيمة \"$2\".\nبما أنه وجد عدد قليل من النتائج Ùإن القيم المقاربة معروضة كذلك.",
+ "smw_sbv_property": "الخصيصة:",
+ "smw_sbv_value": "القيمة:",
+ "smw_sbv_submit": "جÙد نتائجًا",
+ "browse": "تصÙّح الويكي",
+ "smw_browselink": "تصÙّح الخصائص",
+ "smw_browse_article": "أدخل اسم صÙحة لبدء التصÙØ­ منها.",
+ "smw_browse_go": "اذهب",
+ "smw_browse_show_incoming": "عرض الخصائص الواردة",
+ "smw_browse_hide_incoming": "إخÙاء الخصائص الواردة",
+ "smw_browse_no_outgoing": "هذه الصÙحة ليست بها خصائص.",
+ "smw_browse_no_incoming": "لا خصائص تربط إلى هذه الصÙحة.",
+ "smw-browse-from-backend": "يجري حاليًا استرجاع المعلومات من الواجهة الخلÙية.",
+ "smw-browse-intro": "توÙر هذه الصÙحة تÙاصيل حول نموذج موضوع أو كيان، ÙŠÙرجَى إدخال اسم كائن ليتم تÙتيشه.",
+ "smw-browse-invalid-subject": "التحقق من هذا الموضوع عاد مع الخطأ \"$1\" .",
+ "smw-browse-api-subject-serialization-invalid": "هذا الموضوع به صيغة تسلسل غير صالحة.",
+ "smw-browse-js-disabled": "ÙŠÙشتبَه ÙÙŠ أن تكون جاÙا سكريبت معطلة أو غير متوÙرة، ونحن نوصي باستخدام متصÙØ­ حيث يتم دعمها، تÙناقَش خيارات أخرى ÙÙŠ صÙحة وسيط التكوين [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "عرض المجموعات",
+ "smw-browse-hide-group": "إخÙاء المجموعات",
+ "smw-noscript": "هذه الصÙحة أو الÙعل تتطلب جاÙا سكريبت لتعمل; يرجى تمكين جاÙا سكريبت ÙÙŠ متصÙحك أو استخدام متصÙØ­ يدعمها حتى يمكن تقديم هذه الوظيÙØ© ويتم توÙيرها حسب الطلب، لمزيد من المساعدة; ÙŠÙرجَى إلقاء نظرة على صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Noscript نوسكريبت].",
+ "smw_inverse_label_default": "$1 من",
+ "smw_inverse_label_property": "عنوان الخصيصة العكسية",
+ "pageproperty": "البحث ÙÙŠ خصائص صÙحة",
+ "smw_pp_docu": "إما أن تدخل صÙحة أو خاصية، أو خاصية Ùقط لاسترداد جميع القيم المحددة.",
+ "smw_pp_from": "من صÙحة:",
+ "smw_pp_type": "الخاصية:",
+ "smw_pp_submit": "جÙد نتائجًا",
+ "smw_result_prev": "السابق",
+ "smw_result_next": "اللاحق",
+ "smw_result_results": "النتائج",
+ "smw_result_noresults": "لا توجد نتائج.",
+ "smwadmin": "وظائ٠إدارية وصيانة",
+ "smw-admin-statistics-job-title": "إحصاءات العمل",
+ "smw-admin-statistics-job-docu": "إحصاءات العمل تعرض معلومات حول وظائ٠ميدياويكي الدلالي المقررة التي لم يتم تنÙيذها حتى الآن. عدد الوظائ٠قد يكون غير دقيق قليلًا أو يحتوي على المحاولات الÙاشلة، ÙŠÙرجَى الرجوع إلى [https://www.mediawiki.org/wiki/Manual:Job_queue الدليل] لمزيد من المعلومات.",
+ "smw-admin-statistics-querycache-title": "إحصاءات استعلام الذاكرة المخبأة",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache استعلام الذاكرة المخبأة] لم يتم تمكين ÙÙŠ هذا الويكي ولهذا لا توجد إحصاءات متاح.",
+ "smw-admin-statistics-querycache-explain": "إحصائيات ذاكرة التخزين المؤقت هي احتواء البيانات التراكمية المؤقتة وكذلك البيانات المشتقة بما ÙÙŠ ذلك: \n* \"يخطئ\" كمجموع محاولات لاسترداد البيانات من ذاكرة التخزين المؤقت مع ردود غير قابلة للتحقيق، مما اضطر مستودع مباشر (دب، أو مخزن ثلاثي إلخ) للاسترجاع \n* \"حذÙ\" الإجمالي كمية عمليات إخلاء ذاكرة التخزين المؤقت (إما من خلال التطهير أو الاستعلام التبعية) \n*\" الزيارات \"يحتوي على كمية استرجاع ذاكرة التخزين المؤقت من إما جزءا لا يتجزأ من (الاستعلامات المسماه من داخل صÙحة ويكي) أو غير المضمنة (إذا تم تمكينها، يتم طلبها بواسطة صÙحات مثل المصادر Special:Ask أو API) \n* \"ميديانرتريÙالريسبونزيتيمي\" هي قيمة توجيهية لوقت الاستجابة الوسيط (ÙÙŠ ثانية) لطلبات الاسترجاع المخزنة مؤقتا وغير المخزنة مؤقتا خلال الÙترة الزمنية لعملية التجميع \n* \"نوكاش\" يشير إلى كمية أي طلبات محاولة (الحد = 0 استعلام، خيار 'لا توجد ذاكرة تخزين مؤقت' إلخ) لاسترداد النتائج من ذاكرة التخزين المؤقت",
+ "smw-admin-permission-missing": "الوصول إلى هذه الصÙحة قد تم حظره بسبب صلاحيات Ù…Ùقودة; ÙŠÙرجَى الرجوع إلى صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Permissions صلاحيات] لمزيد من التÙاصيل حول الإعدادات اللازمة.",
+ "smw-admin-setupsuccess": "تم تنصيب محرك تخزين",
+ "smw_smwadmin_return": "ارجع إلى $1",
+ "smw_smwadmin_updatestarted": "إجراء تحديث البيانات الدلالية قد بدأ.\nكل البيانات المخزنة ستجري إعادة بنائها أو إصلاحها حسب الحاجة.\nيمكنك متابعة تطور التحديث على هذه الصÙحة الخاصة.",
+ "smw_smwadmin_updatenotstarted": "توجد بالÙعل عملية تحديث جارية.\nلا تبدأ غيرها.",
+ "smw_smwadmin_updatestopped": "كل عمليات التحديث الجارية تم إيقاÙها",
+ "smw_smwadmin_updatenotstopped": "لوق٠عملية التحديث الجارية يجب عليك التأشير ÙÙŠ الصندوق للتوكيد.",
+ "smw-admin-docu": "هذه الصÙحة الخاصة تساعدك خلال تنصيب وترقية وصيانة واستخدام < href=\"https://www.semantic-mediawiki.org\">ميدياويكي الدلالية</a>\nوتوÙر أيضًا المزيد من الوظائ٠والمهام الإدارية وكذلك إحصاءات.\nتذكر أن تحÙظ احتياطيا البيانات القيمة قبل إجراء وظائ٠إدارية",
+ "smw-admin-environment": "بيئة البرمجيات",
+ "smw-admin-db": "إعداد قاعدة البيانات",
+ "smw-admin-db-preparation": "تهيئة الجدول مستمرة وربما تستغرق ثانية قبل عرض نتائج بانتظار الحجم والتحسينات الممكنة للجدول.",
+ "smw-admin-dbdocu": "يتطلب ميدياويكي الدلالي بنية قاعدة البيانات الخاصة به (وهي مستقلة عن ميدياويكي وبالتالي لا تؤثر على بقية تثبيت ميدياويكي) من أجل تخزين البيانات الدلالية. \nيمكن تنÙيذ وظيÙØ© الإعداد هذه عدة مرات دون إلحاق أي ضرر، ولكنها مطلوبة مرة واحدة Ùقط عند التثبيت أو الترقية.",
+ "smw-admin-permissionswarn": "إذا Ùشلت العملية بأخطاء SQLØŒ مستخدم قاعدة البيانات المستخدمة من قبل الويكي الخاص بك (تحقق من مل٠\"LocalSettings.php\" الخاص بك) ربما ليست لديه صلاحيات كاÙية;\nامنح هذا المستخدم صلاحيات إضاÙية لإنشاء الجداول وحذÙها أو إدخال تسجيل الدخول لجذر قاعدة البيانات مؤقتا ÙÙŠ مل٠\"LocalSettings.php\"ØŒ أو استخدام كود الصيانة <code>setupStore.php</code>ØŒ والذي يمكنه استخدام بيانات الاعتماد من إداري.",
+ "smw-admin-dbbutton": "تهيئة أو ترقية الجداول",
+ "smw-admin-announce": "أعلن عن هذه الويكي",
+ "smw-admin-announce-text": "إذا الويكي الخاص بك عامًا، يمكنك تسجيله على <a href=\"https://wikiapiary.com\">WikiApiary</a>،\nويكي تتبع الويكي.",
+ "smw-admin-deprecation-notice-title": "إشعارات الانتقاص",
+ "smw-admin-deprecation-notice-docu": "القسم التالي يحتوي على الإعدادات التي تم إيقاÙها أو إزالتها ولكن تم الكش٠عنها لتكون نشطة ÙÙŠ هذا الويكي، من المتوقع أن أي إصدار مستقبلي سيزيل الدعم لهذه التكوينات.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> تم إهماله وستتم إزالته ÙÙŠ $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> سيزيل (أو يستبدل) {{PLURAL:$2|الخيار التالي|الخيارات التالية}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> يتم إهماله وستتم إزالته ÙÙŠ $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> تم استبداله ب<code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "{{PLURAL:$2|خيار|خيارات}} <code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> :",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "يتم استبدال <code>$1</code> بـ<code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> Ø£Ùزيل ÙÙŠ $2",
+ "smw-admin-deprecation-notice-title-notice": "التغييرات القادمة",
+ "smw-admin-deprecation-notice-title-notice-explanation": "تم اكتشا٠الإعدادات التالية ليتم استخدامها على هذا الويكي ومن المقرر إزالتها أو تغييرها ÙÙŠ إصدار مستقبلي.",
+ "smw-admin-deprecation-notice-title-replacement": "الإعدادات التي تم استبدالها أو إعادة تسميتها",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "يحتوي القسم التالي على الإعدادات التي تمت إعادة تسميتها أو تعديلها بطريقة أخرى، ويÙوصَى بتحديث اسمها أو تنسيقها Ùورا.",
+ "smw-admin-deprecation-notice-title-removal": "تمت إزالة الإعدادات",
+ "smw-admin-deprecation-notice-title-removal-explanation": "تمت إزالة الإعدادات المدرجة ÙÙŠ إصدار سابق ولكن تم اكتشاÙها حتى الآن لاستخدامها على هذا الويكي.",
+ "smw-smwadmin-refresh-title": "إصلاح وتحديث البيانات",
+ "smw_smwadmin_datarefresh": "إعادة بناء البيانات",
+ "smw_smwadmin_datarefreshdocu": "من الممكن استرجاع كل بيانات ميدياويكي الدلالية بناء على المحتويات الحالية للويكي.\nقد يكون هذا Ù…Ùيدا لإصلاح البيانات المعطوبة أو لتحديث البيانات ÙÙŠ حال تغيّرت الصيغة الداخلية بسبب ترقية برمجية.\nالتحديث يتم إجراؤه صÙحة بصÙحة ولن يتم لحظيا.\nالتالي يعرض ما إذا كان التحديث يجري Ùˆ يسمح لك ببدء أو وق٠التحديثات (إلا لو كانت هذه الوظيÙØ© قد عطّلها مدير الموقع).",
+ "smw_smwadmin_datarefreshprogress": "<strong>يوجد تحديث جار٠بالÙعل.</strong>\nمن الطبيعي أن تتقدم عمليات التحديث ببطء بما أنها تجري على البيانات بكميات صغيرة كل مرة يستخدم Ùيها شخص ما الويكي.\nلإتمام التحديث سريعا يمكنك تشغيل سكربت صيانة ميدياويكي <code>runJobs.php</code> (استخدم الخيار <code>--maxjobs 1000</code> لتحديد عدد التحديثات المجراة ÙÙŠ كل دÙعة).\nالتقدم التقديري للتحديث الحالي:",
+ "smw_smwadmin_datarefreshbutton": "إعادة بناء بيانات الجدول الزمني",
+ "smw_smwadmin_datarefreshstop": "أوق٠هذا التحديث",
+ "smw_smwadmin_datarefreshstopconfirm": "نعم، أنا {{GENDER:$1|متأكد|متأكدة}}.",
+ "smw-admin-job-scheduler-note": "يتم تنÙيذ المهام (التي تم تمكينها) ÙÙŠ هذا القسم من خلال قائمة انتظار المهام لتجنب حالات توق٠تام أثناء تنÙيذها، [https://www.mediawiki.org/wiki/Manual:Job_queue قائمة انتظار المهام] هي المسئولة عن المعالجة ومن الأهمية بمكان أن سكريبت الصيانة <code>runJobs.php</code> لديه سعة مناسبة (راجع أيضا وسيط التكوين <code>$wgRunJobsAsync</code>).",
+ "smw-admin-outdateddisposal-title": "التخلص من الكيانات التي عÙا عليها الزمن",
+ "smw-admin-outdateddisposal-intro": "بعض الأنشطة (تغيير نوع الخاصية، أو إزالة صÙحات الويكي، أو تصحيح قيم خاطئة) ينتج ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Outdated_entities الكيانات التي عÙا عليها الزمن] وتÙقترَح إزالتها بشكل دوري لتحرير مساحة الجدول المرتبط.",
+ "smw-admin-outdateddisposal-active": "لقد تمت جدولة مهمة التخلص من الكيانات القديمة.",
+ "smw-admin-outdateddisposal-button": "التخلص من الجدول الزمني",
+ "smw-admin-feature-disabled": "تم تعطيل هذه الميزة ÙÙŠ هذا الويكي; يرجى الرجوع إلى صÙحة المساعدة الإعدادات <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">settings</a> أو اتصل بإداري النظام.",
+ "smw-admin-propertystatistics-title": "إعادة بناء إحصاءات الخاصية",
+ "smw-admin-propertystatistics-intro": "يعيد بناء إحصائيات استخدام الخاصية بأكملها بما Ùيها التحديثات ويصحح [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count عدد استخدام] الخصائص.",
+ "smw-admin-propertystatistics-active": "تمت جدولة مهمة إعادة بناء إحصائيات الخاصية.",
+ "smw-admin-propertystatistics-button": "إعادة بناء إحصاءات الجدول الزمني",
+ "smw-admin-fulltext-title": "بحث إعادة بناء النص الكامل",
+ "smw-admin-fulltext-intro": "إعادة إنشاء Ùهرس البحث من جداول الخصائص مع تمكين [https://www.semantic-mediawiki.org/wiki/Full-text البحث عن النص الكامل]ØŒ التغييرات التي تطرأ على قواعد الÙهرس (ستوبوردز المتغيرة، ستيمر الجديدة إلخ) Ùˆ/أو الجدول المضا٠أو المتبدل حديثا يتطلب تشغيل هذه المهمة مرة أخرى.",
+ "smw-admin-fulltext-active": "تمت جدولة مهمة إعادة بناء البحث عن نص كامل.",
+ "smw-admin-fulltext-button": "إعادة بناء جدولة النص الكامل",
+ "smw-admin-support": "الحصول على دعم",
+ "smw-admin-supportdocu": "يتم توÙير موارد مختلÙØ© لمساعدتك ÙÙŠ حالة حدوث مشاكل:",
+ "smw-admin-installfile": "لو واجهتك مشاكل مع تنصيبتك Ùابدأ بمراجعة الإرشادات ÙÙŠ https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL file</a> and the <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">صÙحة التثبيت</a>.",
+ "smw-admin-smwhomepage": "وثائق الاستخدام الكاملة لميدياويكي الدلالية موجودة ÙÙŠ <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "الأخطاء يمكن الإبلاغ عنها ÙÙŠ <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/\"متعقب المشاكل</a>ØŒ ØŒ توÙر صÙحة <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">الإبلاغ عن الأخطاء</a> بعض الإرشادات حول كيÙية كتابة تقرير مشكلة Ùعال.",
+ "smw-admin-questions": "إذا كانت لديك أسئلة أو اقتراحات أخرى، Ùيمكنك الانضمام إلى مناقشة <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">القائمة البريدية للمستخدم</a> أو <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">غر٠الدردشة</a> ÙÙŠ ميدياويكي الدلالي.",
+ "smw-admin-other-functions": "وظائ٠أخرى",
+ "smw-admin-supplementary-section-title": "وظائ٠إضاÙية",
+ "smw-admin-supplementary-section-subtitle": "الوظائ٠الأساسية",
+ "smw-admin-supplementary-section-intro": "يوÙر هذا القسم وظائ٠إضاÙية خارج نطاق الصيانة ومن الممكن أن تكون بعض الوظائ٠المدرجة ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions التوثيق] مقيدة أو غير متاحة وبالتالي لا يمكن الوصول إليها ÙÙŠ هذا الويكي.",
+ "smw-admin-supplementary-settings-title": "إعدادات التكوين",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> مخرجات القائمة الجماعية بالإعدادات المتاحة المستخدمة ÙÙŠ ميدياويكي الدلالي",
+ "smw-admin-supplementary-operational-statistics-title": "الإحصاءات التشغيلية",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> يعرض مجموعة موسعة من الإحصاءات",
+ "smw-admin-supplementary-idlookup-title": "البحث عن الكيانات والتخلص منها",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> يحتوي على وظائ٠للبحث عن والتخلص من كيانات Ùردية",
+ "smw-admin-supplementary-duplookup-title": "كيانات مكررة",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> لإدراج الإدخالات التي تم تصنيÙها على أنها تحتوي على نسخ مكررة ÙÙŠ جدول الكيان",
+ "smw-admin-supplementary-duplookup-docu": "تسرد هذه الصÙحة إدخالات من [https://www.semantic-mediawiki.org/wiki/Help:Entity_table جدول الكيان] التي تم تصنيÙها على أنها نسخ مكررة. يجب أن تحدث الإدخالات المكررة (على كل حال) Ùقط ÙÙŠ حالات نادرة ÙŠÙحتمَل أن تكون ناتجة عن عملية تم إنهاؤها أثناء تحديث قاعدة بيانات أو إجراء استرجاع غير ناجح.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "إحصائيات ذاكرة التخزين المؤقت",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "يعرض <u>$1</u> إحصائيات متعلقة بذاكرة التخزين المؤقت",
+ "smw-admin-supplementary-elastic-title": "بحث مرن",
+ "smw-admin-supplementary-elastic-section-subtitle": "وظائ٠إلاستيك سيرش",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> يظهر عن إعدادات وإحصائيات الÙهرس",
+ "smw-admin-supplementary-elastic-docu": "تحتوي هذه الصÙحة على معلومات حول الإعدادات والتعيينات والصحة وإحصائيات الÙهرس المتعلقة بمجموعة البحث المرن المرتبطة بميدياويكي الدلالي Ùˆ[https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "الوظائ٠المدعومة",
+ "smw-admin-supplementary-elastic-settings-title": "إعدادات",
+ "smw-admin-supplementary-elastic-settings-intro": "تÙستخدَم <u>$1</u> بواسطة البحث المرن لإدارة Ùهارس ميدياكيكي الدلالي",
+ "smw-admin-supplementary-elastic-mappings-title": "التعيينات",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> لسرد الÙهارس وتعيينات الحقل",
+ "smw-admin-supplementary-elastic-mappings-docu": "تحتوي هذه الصÙحة على تÙاصيل تعيين الحقول نظرا لاستخدامها مع الÙهارس الحالية، تجب مراقبة ملخص الخرائط بالارتباط مع <code>index.mapping.total_fields.limit</code> الذي يحدد الحد الأقصى لعدد الحقول ÙÙŠ الÙهرس.",
+ "smw-admin-supplementary-elastic-mappings-summary": "الملخص",
+ "smw-admin-supplementary-elastic-mappings-fields": "خريطة الحقول",
+ "smw-admin-supplementary-elastic-nodes-title": "العقد",
+ "smw-admin-supplementary-elastic-nodes-intro": "يعرض <u>$1</u> إحصائيات العقدة",
+ "smw-admin-supplementary-elastic-indices-title": "الÙهارس",
+ "smw-admin-supplementary-elastic-indices-intro": "يقدم <u>$1</u> نظرة عامة على الÙهارس المتاحة وإحصاءاتها",
+ "smw-admin-supplementary-elastic-statistics-title": "الإحصاءات",
+ "smw-admin-supplementary-elastic-statistics-intro": "يعرض <u>$1</u> مستوى الÙهرس",
+ "smw-admin-supplementary-elastic-statistics-docu": "تقدم هذه الصÙحة إحصاءات حول إحصائيات الÙهارس الخاصة بالعمليات المختلÙØ© التي تحدث على مستوى الÙهرس، حيث يتم تجميع الإحصائيات المرتجعة مع المجموعات التمهيدية والإجمالية، تحتوي صÙحة [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html صÙحة المساعدة] على وص٠مÙصل لإحصاءات الÙهارس المتاحة.",
+ "smw-admin-supplementary-elastic-status-replication": "حالة النسخ المتماثل",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "آخر عملية نسخ متماثل نشطة: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Ùاصل التحديث: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "تراكم وظيÙØ© الاسترداد: $1 (تقدير)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "تراكم (ملÙ) العمل: $1 (تقدير)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "تم Ù‚ÙÙ„ النسخ المتماثل: $1 (إعادة البناء قيد التقدم)",
+ "smw-list-count": "تحتوي القائمة على $1 {{PLURAL:$1|إدخال واحد|إدخالات}}.",
+ "smw-list-count-from-cache": "تحتوي القائمة على $1 {{PLURAL:$1|إدخال واحد|إدخالات}} وتم استرجاعها من ذاكرة التخزين المؤقت (ت ع م): $2.",
+ "smw-property-label-uniqueness": "تمت مطابقة التصني٠\"$1\" مع تمثيل خاصية أخرى واحدة على الأقل، ÙŠÙرجَى الرجوع إلى [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness صÙحة المساعدة] حول كيÙية حل هذه المشكلة.",
+ "smw-property-label-similarity-title": "تقرير تشابه تسمية الخواص",
+ "smw-property-label-similarity-intro": "<u>$1</u> يحسب تشابهات تسميات الخواص الموجودة",
+ "smw-property-label-similarity-threshold": "عتبة:",
+ "smw-property-label-similarity-type": "عرض معر٠النوع",
+ "smw-property-label-similarity-noresult": "لم يتم العثور على نتائج الخيارات المحددة.",
+ "smw-property-label-similarity-docu": "يقارن ويبلغ عن [https://www.semantic-mediawiki.org/wiki/Property_s التشابه النحوي] (وليس التشابه الدلالي) بين تسميتي خاصيتين يمكن أن تساعد ÙÙŠ تصÙية الأخطاء الإملائية أو ما يعادلها من الخصائص التي تمثل Ù†Ùس المÙهوم (انظر الصÙحة الخاصة [[Special:Properties|خصائص]] لتوضيح Ù…Ùهوم واستخدام الخصائص المبلغ عنها)ØŒ ويمكن تعديل العتبة إما لتوسيع أو تضييق مساÙØ© التشابه، <code>[[Property:$1|$1]]</code> ÙŠÙستخدَم لإعÙاء الخصائص من التحليل.",
+ "smw-admin-operational-statistics": "تحتوي هذه الصÙحة على إحصاءات تشغيلية تم جمعها ÙÙŠ أو من المهام ذات الصلة بميدياويكي الدلالي، يمكن الاطلاع على قائمة طويلة بالإحصاءات المحددة [[Special:Statistics|<b>هنا</b>]].",
+ "smw_adminlinks_datastructure": "هيكل البيانات",
+ "smw_adminlinks_displayingdata": "عرض البيانات",
+ "smw_adminlinks_inlinequerieshelp": "المساعدة المضمنة للاستعلامات",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count عدد الاستخدام] المقدر: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|مستخدم|نظام}} حدد خاصية",
+ "smw-property-indicator-last-count-update": "عدد الاستخدام المقدر\nآخر تحديث: $1",
+ "smw-concept-indicator-cache-update": "عدد ذاكرة التخزين المؤقت\nآخر تحديث: $1",
+ "smw-createproperty-isproperty": "هذه خاصية من نوع $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1||القيمة المسموح بها لهذه الخصيصة هي|القيمتان المسموح بهما لهذه الخصيصة هما|القيم المسموح بها لهذه الخصيصة هي}}:",
+ "smw-paramdesc-category-delim": "الÙاصلة",
+ "smw-paramdesc-category-template": "قالب يستخدم لتنسيق العناصر به",
+ "smw-paramdesc-category-userparam": "معامل يمرر إلى القالب",
+ "smw-info-par-message": "رسالة لعرضها",
+ "smw-info-par-icon": "أيقونة تظهر، إما \"info\" أو \"warning\".",
+ "prefs-smw": "ميدياويكي الدلالية",
+ "prefs-general-options": "خيارات عامة",
+ "prefs-ask-options": "خاص:خيارات السؤال",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ ميدياويكي الدلالي] (والملحقات ذات الصلة) يوÙر التخصيص الÙردي لبعض الوظائ٠المحددة، ÙŠÙرجَى الرجوع إلى [https://www.semantic-mediawiki.org/wiki/Help:User_preferences صÙحة المساعدة] للحصول على وص٠تÙصيلي.",
+ "smw-prefs-ask-options-tooltip-display": "اعرض نص المعامل كبالون تلميحة",
+ "smw-prefs-ask-options-compact-view-basic": "تمكين العرض المضغوط الأساسي",
+ "smw-prefs-help-ask-options-compact-view-basic": "ÙÙŠ حالة التمكين ØŒ تعرض مجموعة من الروابط ÙÙŠ عرض خاص:سؤال المضغوط.",
+ "smw-prefs-general-options-time-correction": "تمكين تصحيح الوقت لصÙحات خاصة باستخدام تÙضيل [[Special:Preferences#mw-prefsection-rendering|تعويض الوقت]] المحلي.",
+ "smw-prefs-general-options-jobqueue-watchlist": "عرض قائمة انتظار قائمة مراقبة الوظائ٠ÙÙŠ شريطي الشخصي",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "ÙÙŠ حالة التمكين، يعرض [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist قائمة] انتظار المهام المحددة مع أحجام قوائم انتظارهم المقدرة.",
+ "smw-prefs-general-options-disable-editpage-info": "تعطيل النص التمهيدي ÙÙŠ صÙحة التعديل",
+ "smw-prefs-general-options-disable-search-info": "تعطيل معلومات دعم بناء الجملة ÙÙŠ صÙحة البحث القياسية",
+ "smw-prefs-general-options-suggester-textinput": "تمكين مساعدة المدخلات لاقتراحات الكيانات الدلالية",
+ "smw-prefs-help-general-options-suggester-textinput": "ÙÙŠ حالة التمكين، يسمح باستخدام [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance مساعدة المدخلات] للبحث عن الخصائص والمÙاهيم والتصنيÙات من سياق المدخلات.",
+ "smw-ui-tooltip-title-property": "الخصيصة",
+ "smw-ui-tooltip-title-quantity": "تحويل الوحدة",
+ "smw-ui-tooltip-title-info": "معلومات",
+ "smw-ui-tooltip-title-service": "روابط خدمية",
+ "smw-ui-tooltip-title-warning": "تحذير",
+ "smw-ui-tooltip-title-error": "خطأ",
+ "smw-ui-tooltip-title-parameter": "معامل",
+ "smw-ui-tooltip-title-event": "حدث",
+ "smw-ui-tooltip-title-note": "ملاحظة",
+ "smw-ui-tooltip-title-legend": "Ù…Ùتاح الشكل",
+ "smw-ui-tooltip-title-reference": "مرجع",
+ "smw_unknowntype": "نوع هذه الخاصية \"$1\" غير صالح",
+ "smw-concept-cache-text": "المÙهوم لديه ما مجموعه $1 {{PLURAL:$1|صÙحة|صÙحات}}ØŒ وكان آخر تحديث $3ØŒ $2.",
+ "smw_concept_header": "صÙحات المÙهوم \"$1\"",
+ "smw_conceptarticlecount": "عرض {{PLURAL:$1||صÙحة واحدة تنتمي|صÙحتين تنتميان|$1 صÙحات تنتمي|$1 صÙحة تنتمي}} أسÙله.",
+ "smw-qp-empty-data": "لا يمكن عرض البيانات المطلوبة لأن بعض معايير الاختيار غير كاÙية.",
+ "right-smw-admin": "الوصول للمهام الإدارية (ميدياويكي الدلالي)",
+ "right-smw-patternedit": "تحرير الوصول إلى الحÙاظ على التعبيرات النمطية والأنماط المسموح بها (ميدياويكي الدلالي)",
+ "right-smw-pageedit": "تحرير الوصول للصÙحات المشروحة <code>هل التحرير محمي</code> (ميدياويكي الدلالي)",
+ "right-smw-ruleedit": "تحرير صÙحات القاعدة (ميدياويكي الدلالي)",
+ "restriction-level-smw-pageedit": "محمية (المستخدمون المؤهلون Ùقط)",
+ "action-smw-patternedit": "تعديل التعابير النمطية المستخدمة من قبل ميدياويكي الدلالي",
+ "action-smw-pageedit": "تحرير الصÙحات المشروحة ب<code>هل التحرير محمي</code> (ميدياويكي الدلالي)",
+ "group-smwadministrator": "الإداريون (ميدياويكي الدلالي)",
+ "group-smwadministrator-member": "{{GENDER:$1|إداري (ميدياويكي الدلالي)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:الإداريون (ميدياويكي الدلالي)",
+ "group-smwcurator": "القيمون (ميدياويكي الدلالي)",
+ "group-smwcurator-member": "{{GENDER:$1|قيمون (ميدياويكي الدلالي)}}",
+ "grouppage-smwcurator": "{{ns:project}}:قيمون (ميدياويكي الدلالي)",
+ "action-smw-admin": "الوصول للمهام الإدارية ÙÙŠ ميدياويكي الدلالي",
+ "action-smw-ruleedit": "تحرير صÙحات القاعدة (ميدياويكي الدلالي)",
+ "smw-property-predefined-default": "\"$1\" هي خاصية محددة مسبقا.",
+ "smw-property-predefined-common": "هذه الخاصية سبق نشرها (معروÙØ© أيضًا باسم [https://www.semantic-mediawiki.org/wiki/Help:Special_properties خاصية خاصة])ØŒ وتأتي مع امتيازات إدارية إضاÙية ولكن يمكن استخدامها تمامًا مثل أية [https://www.semantic-mediawiki.org/wiki/Property خاصية معرÙØ© من قبل المستخدم].",
+ "smw-property-predefined-ask": "\"$1\" هي خاصية معرÙØ© مسبقًا تمثل المعلومات الوصÙية (ÙÙŠ شكل [https://www.semantic-mediawiki.org/wiki/Subobject كائن Ùرعي]) حول استعلامات Ùردية ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-asksi": "\"$1\" هي خاصية معرÙØ© مسبقًا تجمع عددًا من الشروط المستخدمة ÙÙŠ استعلام ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-askde": "\"$1\" هي خاصية معرÙØ© مسبقًا تخبر بشأن عمق الاستعلام ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-long-askde": "عبارة عن قيمة رقمية يتم حسابها على أساس تعشيش الاستعلام الÙرعي وسلاسل الخاصية وعناصر الوص٠المتاحة مع تنÙيذ استعلام مقيد بواسطة تكوين وسيط <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "\"$1\" هي خاصية محددة مسبقًا تص٠الوسائط التي تؤثر على نتيجة الاستعلام ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-long-askpa": "هو جزء من مجموعة الخصائص التي تحدد [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler المل٠الشخصي للاستعلام].",
+ "smw-sp-properties-docu": "تسرد هذه الصÙحة [https://www.semantic-mediawiki.org/wiki/Property الخصائص] وعدد استخداماتها المتاحة لهذا الويكي، للحصول على إحصاءات حديثة Ùمن المستحسن أن يتم تشغيل سكريبت الصيانة [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics إحصاءات الخاصية] على أساس منتظم، للاطلاع على عرض مختلÙ; راجع الصÙحات الخاصة [[Special:UnusedProperties|خصائص غير مستخدمة]] أو [[Special:WantedProperties|مطلوبة]].",
+ "smw-sp-properties-cache-info": "تم استرداد البيانات المدرجة من [https://www.semantic-mediawiki.org/wiki/Caching النسخة المخبأة]، وكان آخر تحديث $1.",
+ "smw-sp-properties-header-label": "قائمة خصائص",
+ "smw-admin-settings-docu": "يعرض قائمة بكاÙØ© الإعدادات الاÙتراضية والمحلية ذات الصلة بيئة ميدياويكي الدلالية، للحصول على تÙاصيل حول الإعدادات الÙردية; ÙŠÙرجىَ الرجوع إلى صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Configuration تكوين].",
+ "smw-sp-admin-settings-button": "توليد قائمة إعدادات",
+ "smw-admin-idlookup-title": "ابحث عن",
+ "smw-admin-idlookup-docu": "يعرض هذا القسم التÙاصيل الÙنية عن كيان Ùردي (صÙحة ويكي، كائن Ùرعي، خاصية، إلخ) ÙÙŠ ميدياويكي الدلالي، يمكن أن يكون الإدخال معرÙا رقميا أو قيمة سلسلة ليتطابق مع حقل البحث ذي الصلة، ومع ذلك، Ùإن أي مرجع معر٠يتعلق بميدياويكي الدلالي وليس بصÙحة أو معر٠مراجعة ميدياويكي.",
+ "smw-admin-iddispose-title": "تصرÙ",
+ "smw-admin-iddispose-docu": "تجدر الإشارة إلى أن عملية التخلص غير مقيدة وستقوم بإزالة الكيان من محرك التخزين بجميع مراجعه ÙÙŠ الجداول المعلقة، إذا تم تأكيدها، الرجاء تنÙيذ هذه المهمة '''بحذر''' ÙˆÙقط بعد الاطلاع على [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal التوثيق].",
+ "smw-admin-iddispose-done": "تمت إزالة المعر٠\"$1\" من الواجهة الخلÙية للتخزين.",
+ "smw-admin-iddispose-references": "المعر٠\"$1\" {{PLURAL:$2|لا يحتوي على مرجع نشط|يحتوي على مرجع نشط واحد على الأقل}}:",
+ "smw-admin-iddispose-references-multiple": "قائمة المطابقات مع سجل مرجع نشط واحد على الأقل.",
+ "smw-admin-iddispose-no-references": "لم يتمكن البحث من مطابقة \"$1\" إلى إدخال جدول.",
+ "smw-admin-idlookup-input": "بحث:",
+ "smw-admin-objectid": "معرÙ:",
+ "smw-admin-tab-general": "نظرة عامة",
+ "smw-admin-tab-notices": "إشعارات انتقاص",
+ "smw-admin-tab-maintenance": "الصيانة",
+ "smw-admin-tab-supplement": "وظائ٠إضاÙية",
+ "smw-admin-tab-registry": "سجل",
+ "smw-admin-maintenance-no-description": "بدون وصÙ.",
+ "smw-admin-maintenance-script-section-title": "قائمة سكريبتات الصيانة المتاحة",
+ "smw-admin-maintenance-script-section-intro": "تتطلب سكريبتات الصيانة التالية إداريا والوصول إلى سطر الأوامر لتتمكن من تنÙيذ السكريبتات المدرجة.",
+ "smw-admin-maintenance-script-description-dumprdf": "تصدير RDF الثلاثي الحالي.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "يتم استخدام هذا السكريبت ÙÙŠ إدارة مخابئ المÙاهيم لميدياويكي الدلالي حيث يمكن إنشاء، وإزالة ØŒ وتحديث ذاكرات مختارة.",
+ "smw-admin-maintenance-script-description-rebuilddata": "يعيد إنشاء جميع البيانات الدلالية ÙÙŠ قاعدة البيانات، عن طريق ركوب الدراجات من خلال جميع الصÙحات التي قد تحتوي على بيانات دلالية.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "يعيد إنشاء Ùهرس إلاستيك سيرش (للمنشآت التي تستخدم <code>ElasticStore</code>)ØŒ عن طريق ركوب الدراجات من خلال جميع الكيانات التي لديها بيانات دلالية.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "يعيد بناء Ùهرس البحث ÙÙŠ النص الكامل <code>SQLStore</code> (لعمليات التثبيت حيث تم تمكين الإعداد.",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "يعيد بناء إحصائيات الاستخدام لجميع كيانات الخواص.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "يزيل الكيانات المكررة الموجودة ÙÙŠ الجداول المحددة التي لا تحتوي على مراجع نشطة.",
+ "smw-admin-maintenance-script-description-setupstore": "يقوم بإعداد خلÙية التخزين المحددة ÙÙŠ <code>LocalSettings.php</code>.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "يحدث الحقل <code>smw_sort</code> ÙÙŠ <code>SQLStore</code> (ÙˆÙقا لإعداد [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "يعبئ حقل <code>smw_hash</code> للصÙو٠التي تÙتقد إلى القيمة.",
+ "smw-livepreview-loading": "جار٠التحميل...",
+ "smw-sp-searchbyproperty-description": "توÙر هذه الصÙحة [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces واجهة تصÙØ­] بسيطة للعثور على كيانات تم وصÙها بواسطة خاصية وقيمة مسماة، تتضمن واجهات البحث الأخرى المتاحة [[Special:PageProperty|صÙحة بحث الخصائص]]ØŒ Ùˆ[[Special:Ask|اطلب منشئ استعلام]].",
+ "smw-sp-searchbyproperty-resultlist-header": "قائمة النتائج",
+ "smw-sp-searchbyproperty-nonvaluequery": "قائمة القيم التي لديها الخاصية المعينة \"$1\".",
+ "smw-sp-searchbyproperty-valuequery": "قائمة الصÙحات التي تحتوي على الخاصية \"$1\" مع القيمة \"$2\" المشروحة.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" لا يمكن أن تÙسنَد إلى نوع العدد المعلن بقيمة $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" عاد مع \"NULL\" غير المسموح به كرقم.",
+ "smw-editpage-annotation-enabled": "تدعم هذه الصÙحة التعليقات التوضيحية الدلالية ÙÙŠ النص (على سبيل المثال <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) لإنشاء محتوى منظم وقابل للاستعلام من قبل ميدياويكي الدلالي، للحصول على وص٠شامل حول كيÙية استخدام التعليقات التوضيحية أو وظيÙØ© محلل #ask; ÙŠÙرجَى إلقاء نظرة على صÙحات المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Getting_started الخطوات الأولى] أو [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation ÙÙŠ النص annotation] أو [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries استعلامات مضمنة].",
+ "smw-editpage-annotation-disabled": "لم يتم تمكين هذه الصÙحة للتعليقات التوضيحية ÙÙŠ النص الدلالي بسبب قيود النطاق، يمكن العثور على تÙاصيل حول كيÙية تمكين النطاق ÙÙŠ صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Configuration].",
+ "smw-editpage-property-annotation-enabled": "يمكن تمديد هذه الخاصية باستخدام شروح دلالية لتحديد نوع البيانات (على سبيل المثال <nowiki>\"[[Has type::Page]]\"</nowiki>) أو غيرها من الإعلانات الداعمة (على سبيل المثال <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>)ØŒ للحصول على وص٠حول كيÙية زيادة هذه الصÙحة; راجع صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaration of a property] or the [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes قائمة أنواع البيانات المتوÙرة].",
+ "smw-editpage-property-annotation-disabled": "لا يمكن تمديد هذه الخاصية بتعليق توضيحي لنوع البيانات (على سبيل المثال <nowiki>\"[[Has type::Page]]\"</nowiki>) كما هو محدد بالÙعل (انظر صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties خصائص خاصة] لمزيد من المعلومات).",
+ "smw-editpage-concept-annotation-enabled": "يمكن تمديد هذا المÙهوم باستخدام محلل دالة #conceptØŒ للحصول على وص٠حول كيÙية استخدام #concept; راجع صÙحة المساعدة [https://www.semantic-mediawiki.org/wiki/Help:Concepts Ù…Ùهوم].",
+ "smw-search-syntax-support": "مدخلات البحث يدعم استخدام [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search بناء جملة الاستعلام] الدلالي للمساعدة ÙÙŠ مطابقة النتائج باستخدام ميدياويكي الدلالي.",
+ "smw-search-input-assistance": "يتم تمكين [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance مساعد المدخلات] أيضا لتسهيل الاختيار المسبق للخصائص والتصنيÙات المتاحة.",
+ "smw-search-help-intro": "سيشير إدخال <code><nowiki>[[ ... ]]</nowiki></code> iإلى معالج الإدخال لاستخدام واجهة بحث الخلÙية لميدياكي الدلالي، تجدر الإشارة إلى أن الجمع بين <code><nowiki>[[ ... ]]</nowiki></code> مع بحث نصي غير منظم مثل <code><nowiki>[[ ... ]] أو لوريم إيبسوم</nowiki></code> غير مدعوم.",
+ "smw-search-help-structured": "عمليات بحث منظمة:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> ([https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context كسياق تمت تصÙيته])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (مع [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context سياق استعلام])",
+ "smw-search-help-proximity": "عمليات البحث عن قرب (خاصية غير معروÙØ©ØŒ متوÙرة '''Ùقط''' للجهات الخلÙية التي توÙر تكامل بحث عن نص كامل):\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (ابحث ÙÙŠ كل الوثائق عن \"lorem\" Ùˆ\"ipsum\" التي تمت Ùهرستها)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (طابق \"لوريم إيبسوم\" كعبارة)",
+ "smw-search-help-ask": "ستوضح الروابط التالية كيÙية استخدام بناء جملة <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages تحديد الصÙحات] يص٠كيÙية تحديد الصÙحات وبناء الشروط\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators بحث المشغلين] عوامل تشغيل البحث المتاحة بما ÙÙŠ ذلك تلك الخاصة باستعلامات النطاق والبدل",
+ "smw-search-input": "المدخلات والبحث",
+ "smw-search-help-input-assistance": "يتم توÙير [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance مساعدة الإدخال] لحقل الإدخال ويتطلب استخدام إحدى البادئات التالية:\n\n*<code>p:</code> لتمكين اقتراحات الخاصية (مثل <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> لتمكين اقتراحات التصنيÙات\n\n*<code>con:</code> لتمكين اقتراحات المÙهوم",
+ "smw-search-syntax": "بناء الجملة",
+ "smw-search-profile": "موسع",
+ "smw-search-profile-tooltip": "وظائ٠البحث Ùيما يتعلق بميدياويكي الدلالي",
+ "smw-search-profile-sort-best": "Ø£Ùضل مطابقة",
+ "smw-search-profile-sort-recent": "الأحدث",
+ "smw-search-profile-sort-title": "العنوان",
+ "smw-search-profile-extended-help-intro": "[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile المل٠الشخصي الموسع] ÙÙŠ خاص:بحث توÙر الوصول إلى وظائ٠البحث الخاصة بميدياويكي الدلالي والواجهة الخلÙية لاستعلامه المدعوم.",
+ "smw-search-profile-extended-help-sort": "يحدد تÙضيل الÙرز لعرض النتيجة به:",
+ "smw-search-profile-extended-help-sort-title": "* \"العنوان\" باستخدام عنوان الصÙحة (أو عنوان العرض) كمعايير Ùرز",
+ "smw-search-profile-extended-help-sort-recent": "* ستعرض \"الأحدث\" أحدث الكيانات المعدلة أولا (سيتم منع الكيانات الÙرعية لأن هذه الكيانات لا يتم تعليقها على [[Property:Modification date|تاريخ التعديل]])",
+ "smw-search-profile-extended-help-sort-best": "*\"Ø£Ùضل تطابق\" سيتم ترتيب الكيانات حسب [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy الصلة] استنادا إلى النتائج التي توÙرها الواجهة الخلÙية",
+ "smw-search-profile-extended-help-form": "يتم توÙير النماذج (إذا تمت صيانتها) لتتطابق مع حالات استخدام محددة عن طريق عرض حقول خصائص وقيم مختلÙØ© لتضييق نطاق عملية الإدخال وتسهيل عملية متابعة طلب البحث للمستخدمين. (راجع $1)",
+ "smw-search-profile-extended-help-namespace": "سيتم إخÙاء صندوق اختيار النطاق بمجرد تحديد نموذج ولكن يمكن جعله مرئيا بمساعدة الزر \"إظهار/إخÙاء\".",
+ "smw-search-profile-extended-help-search-syntax": "يدعم حقل إدخال البحث استخدام الصيغة <code>#ask</code> لتعري٠سياق بحث ميدياويكي دلالي معين، تشمل التعبيرات المÙيدة ما يلي:",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code> للعثور على أي شيء يحتوي على \"...\" ويكون Ù…Ùيدا بشكل خاص عندما يكون سياق البحث أو الخصائص المعنية غير معروÙØ© (على سبيل المثال <code>in:(lorem && ipsum)</code> تعادل <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>).",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code> للعثور على أي شيء يحتوي على \"...\" بنÙس الترتيب بالضبط",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code> لمطابقة أي كيان مع خاصية \"...\" (على سبيل المثال <code>has:(Foo && Bar)</code> تعادل <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> لعدم تطابق أي كيان يتضمن \"...\"",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* تكون البادئات المخصصة الإضاÙية متاحة ومحددة مثل: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* يتم حجز بعض التعبيرات مثل: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''بعض العمليات المسردة ليست Ù…Ùيدة إلا Ùيما يتعلق بÙهرس النص الكامل الممكَّن أو ElasticStore.''",
+ "smw-search-profile-extended-help-query": "تم استخدام <code><nowiki>$1</nowiki></code> كاستعلام.",
+ "smw-search-profile-extended-help-query-link": "(للمزيد من التÙاصيل $1).",
+ "smw-search-profile-extended-help-find-forms": "النماذج المتوÙرة",
+ "smw-search-profile-extended-section-sort": "الترتيب حسب",
+ "smw-search-profile-extended-section-form": "نماذج",
+ "smw-search-profile-extended-section-search-syntax": "مدخلة البحث",
+ "smw-search-profile-extended-section-namespace": "النطاق",
+ "smw-search-profile-extended-section-query": "الاستعلام",
+ "smw-search-profile-link-caption-query": "انظر",
+ "smw-search-show": "عرض",
+ "smw-search-hide": "إخÙاء",
+ "log-name-smw": "سجل ميدياويكي الدلالي",
+ "log-show-hide-smw": "$1 سجل ميدياويكي الدلالي",
+ "logeventslist-smw-log": "سجل ميدياويكي الدلالي",
+ "log-description-smw": "أنشطة ل[https://www.semantic-mediawiki.org/wiki/Help:Logging enabled أنواع الأحداث] التي تم الإبلاغ عنها من قبل ميدياويكي الدلالي ومكوناته.",
+ "logentry-smw-maintenance": "أحداث الصيانة المتعلقة المنبعثة من ميدياويكي الدلالي",
+ "smw-datavalue-import-unknown-namespace": "نطاق الاستيراد \"$1\" غير معروÙ; ÙŠÙرجَى التأكد من أن تÙاصيل استيراد OWL متاحة عبر [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "غير قادر على العثور على مسار النطاق \"$1\" ÙÙŠ [[MediaWiki:Smw import $1|$1 استيراد]].",
+ "smw-datavalue-import-missing-type": "لم يتم العثور على تعري٠نوع Ù„\"$1\" ÙÙŠ [[MediaWiki:Smw import $2|$2 استيراد]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1 استيراد]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" ليس تنسيقا صالحا ويÙتوقَّع أن يتأل٠من \"النطاق\":\"معرÙ\" (على سبيل المثال \"foaf:اسم\").",
+ "smw-datavalue-import-invalid-format": "من المتوقع تقسيم السلسلة \"$1\" إلى أربعة أجزاء ولكن لم يتم Ùهم الصيغة.",
+ "smw-property-predefined-impo": "\"$1\" هي خاصية محددة مسبقا تص٠علاقة [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary المÙردات المستوردة] ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-type": "\"$1\" هي خاصية محددة مسبقا تصÙ[[Special:Types|نوع بيانات]] الخاصية ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-sobj": "\"$1\" عبارة عن خاصية محددة مسبقا تمثل [https://www.semantic-mediawiki.org/wiki/Help:Container بناء] حاوية ويتم توÙيرها من قبل\n [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-long-sobj": "تسمح الحاوية بتجميع تخصيصات قيمة خواص مماثلة لتخصيصات صÙحة ويكي عادية ولكن داخل حيز كيان مختل٠بينما تكون مرتبطة بموضوع التضمين.",
+ "smw-property-predefined-errp": "\"$1\" عبارة عن خاصية محددة مسبقا تتتبع أخطاء الإدخال ÙÙŠ التعليقات التوضيحية للقيمة غير المنتظمة ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-long-errp": "ÙÙŠ معظم الحالات هو سبب عدم تطابق نوع أو [[Property:Allows value|قيمة]] تقييد.",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] عبارة عن خاصية محددة مسبقا يمكنها تحديد قائمة بالقيم المسموح بها لتقييد تخصيصات القيمة لخاصية ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] عبارة عن خاصية محددة مسبقا يمكنها تحديد مرجع لقائمة تحمل القيم المسموح بها لتقييد تخصيصات القيمة لخاصية ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-datavalue-property-restricted-annotation-use": "الخاصية \"$1\" لها مجال تطبيق مقيد ولا يمكن استخدامها كموقع تعليق توضيحي من قبل المستخدم.",
+ "smw-datavalue-property-restricted-declarative-use": "الخاصية \"$1\" هي خاصية إعلانية ولا يمكن استخدامها إلا على صÙحة خاصية أو تصنيÙ.",
+ "smw-datavalue-property-create-restriction": "الخاصية \"$1\" غير موجودة، ويÙتقد المستخدم الإذن \"$2\" (راجع [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode وضع السلطة]) لإنشاء أو إضاÙØ© تعليقات توضيحية إلى القيم مع خاصية غير معتمدة.",
+ "smw-datavalue-property-invalid-character": "\"$1\" تحتوي على الحر٠\"$2\" مدرجا كجزء من تسمية الخاصية; وبالتالي تم تصنيÙها على أنها غير صالحة.",
+ "smw-datavalue-property-invalid-chain": "لا ÙŠÙسمَح باستخدام \"$1\" كسلسلة خاصية أثناء عملية التعليق التوضيحي.",
+ "smw-datavalue-restricted-use": "تم وضع علامة على داتاÙالو \"$1\" للاستخدام المقيد.",
+ "smw-datavalue-invalid-number": "\"$1\" لا يمكن تÙسيره على أنه رقم.",
+ "smw-query-condition-circular": "تم اكتشا٠حالة دائرية محتملة ÙÙŠ \"$1\".",
+ "smw-query-condition-empty": "وص٠الاستعلام يحتوي على شرط Ùارغ.",
+ "smw-types-list": "قائمة أنواع البيانات",
+ "smw-types-default": "\"$1\" نوع بيانات مدمج.",
+ "smw-types-help": "يمكن العثور على مزيد من المعلومات والأمثلة ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 صÙحة المساعدة هذه].",
+ "smw-type-anu": "\"$1\" هو بديل نوع البيانات [[Special:Types/URL|مسار]] ويÙستخدَم ÙÙŠ الغالب لإعلان تصدير \"owl:AnnotationProperty\".",
+ "smw-type-boo": "\"$1\" نوع بيانات أساسي لوص٠قيمة صحيحة/خاطئة.",
+ "smw-type-cod": "\"$1\" بديل نوع البيانات [[Special:Types/Text|نص]] ليتم استخدامه للنصوص التقنية ذات الطول التعسÙÙŠØŒ مثل قوائم رموز المصدر.",
+ "smw-type-geo": "\"$1\" هو نوع بيانات يص٠المواقع الجغراÙية ويتطلب [https://www.semantic-mediawiki.org/wiki/Extension:Maps تمديد \"الخرائط\"].",
+ "smw-type-tel": "\"$1\" هو نوع بيانات خاص لوص٠أرقام الهات٠الدولية ÙˆÙقا لـRFC 3966.",
+ "smw-type-txt": "\"$1\" نوع بيانات أساسي لوص٠سلاسل الطول التعسÙÙŠ.",
+ "smw-type-dat": "\"$1\" نوع بيانات أساسي لتمثيل النقاط ÙÙŠ الوقت بصيغة موحدة.",
+ "smw-type-ema": "\"$1\" هو نوع بيانات خاص لتمثيل بريد إلكتروني.",
+ "smw-type-tem": "\"$1\" هو نوع بيانات رقمي خاص لتمثيل درجة حرارة.",
+ "smw-type-qty": "\"$1\" هو نوع بيانات لوص٠الكميات ذات التمثيل الرقمي ووحدة القياس.",
+ "smw-type-rec": "\"$1\" هو نوع بيانات حاوية يحدد قائمة بالخصائص المكتوبة بترتيب ثابت.",
+ "smw-type-extra-tem": "يتضمن مخطط التحويل وحدات مدعومة مثل كلÙÙ† وسيلزيوس ÙˆÙهرنهايت ورانكين.",
+ "smw-type-tab-properties": "الخصائص",
+ "smw-type-tab-types": "الأنواع",
+ "smw-type-tab-errors": "أخطاء",
+ "smw-type-primitive": "أساسي",
+ "smw-type-contextual": "قريني",
+ "smw-type-compound": "مركب",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "توÙر هذه الصÙحة واجهة تصÙØ­ للعثور على جميع قيم خاصية وصÙحة معينة، تتضمن واجهات البحث الأخرى المتاحة [[Special:SearchByProperty|بحث الخاصية]]ØŒ Ùˆ[[Special:Ask|اطلب باني استعلام]].",
+ "smw-property-predefined-errc": "\"$1\" عبارة عن خاصية محددة مسبقا يوÙرها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] وتمثل الأخطاء التي ظهرت Ùيما يتعلق بتعليقات أو معالجة مدخلات قيمة غير صحيحة.",
+ "smw-property-predefined-long-errc": "يتم جمع الأخطاء ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Container حاوية] التي قد تتضمن مرجعا إلى الخاصية التي تسببت ÙÙŠ التناقض.",
+ "smw-property-predefined-errt": "\"$1\" عبارة عن خاصية محددة مسبقا تحتوي على وص٠نصي لخطأ ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-subobject-parser-invalid-naming-scheme": "يحتوي الكائن الÙرعي المعر٠من قبل المستخدم على نظام تسمية غير صالح، يتم تخصيص تدوين النقطة ($1) المستخدم ÙÙŠ الأحر٠الخمسة الأولى للإضاÙات، يمكنك تعيين [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier معر٠مسمى].",
+ "smw-datavalue-record-invalid-property-declaration": "يحتوي تعري٠السجل على الخاصية \"$1\" التي يتم الإعلان عنها ÙÙŠ حد ذاتها كنوع سجل وذلك غير مسموح به.",
+ "smw-property-predefined-mdat": "\"$1\" خاصية محددة مسبقا تتواÙÙ‚ مع تاريخ آخر تعديل للموضوع ويتم توÙيرها من خلال [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-cdat": "\"$1\" خاصية محددة مسبقا تتواÙÙ‚ مع تاريخ التنقيح الأول للموضوع ويتم توÙيرها من خلال [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-newp": "\"$1\" خاصية محددة مسبقا تشير إلى ما إذا كان الموضوع جديدا أم لا، ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-ledt": "\"$1\" خاصية محددة مسبقا تحتوي على اسم الصÙحة للمستخدم الذي أنشأ آخر مراجعة ويتم توÙيره من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-mime": "\"$1\" خاصية محددة مسبقا تص٠نوع MIME للمل٠المرÙوع ويتم توÙيره بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-media": "\"$1\" خاصية محددة مسبقا تص٠نوع وسائط المل٠المرÙوع ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-askfo": "\"$1\" خاصية محددة مسبقا تحمل اسم صيغة النتيجة المستخدم ÙÙŠ الاستعلام ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-askst": "\"$1\" هي خاصية محددة مسبقا تص٠شروط الاستعلام كسلسلة ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-askdu": "\"$1\" خاصية محددة مسبقا تحتوي على قيمة زمنية (بالثواني) كانت مطلوبة لإكمال تنÙيذ الاستعلام ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-asksc": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] التي تحدد مصادر الاستعلام البديلة (مثل البعيد، المتحد).",
+ "smw-property-predefined-askco": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لوص٠حالة الاستعلام أو مكوناته.",
+ "smw-property-predefined-long-askco": "يمثل الرقم أو الأرقام المعينة حالة مقننة داخلية يتم شرحها ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler صÙحة المساعدة].",
+ "smw-property-predefined-prec": "\"$1\" خاصية محددة مسبقا تص٠[https://www.semantic-mediawiki.org/wiki/Help:Display_precision دقة العرض] (بالأرقام العشرية) لأنواع البيانات الرقمية.",
+ "smw-property-predefined-attch-link": "\"$1\" هي خاصية محددة مسبقا تجمع روابط الملÙات والملÙات المضمنة الموجودة ÙÙŠ الصÙحة ويتم توÙيرها من خلال [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-types-extra-geo-not-available": " لم يتم الكش٠عن [https://www.semantic-mediawiki.org/wiki/Extension:Maps تمديد \"الخرائط\"] وبالتالي يقتصر \"$1\" على قدرته على العمل.",
+ "smw-datavalue-monolingual-dataitem-missing": "عنصر متوقع لبناء قيمة مجمع أحادي اللغة Ù…Ùقود.",
+ "smw-datavalue-languagecode-missing": "بالنسبة للتعليق التوضيحي \"$1\"، لم يتمكن المحلل من تحديد رمز اللغة (أي \"foo@en\").",
+ "smw-datavalue-languagecode-invalid": "لم يتم التعر٠على \"$1\" كرمز لغة مدعوم.",
+ "smw-property-predefined-lcode": "\"$1\" عبارة عن خاصية محددة مسبقا تمثل رمز لغة BCP47 مهيأ ويتم توÙيره بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-type-mlt-rec": "\"$1\" هو [https://www.semantic-mediawiki.org/wiki/Help:Container حاوية] بيانات يربط قيمة نصية [[Property:Language code|رمز لغة]] خاص.",
+ "smw-types-extra-mlt-lcode": "نوع البيانات {{PLURAL:$2|يتطلب|لا يتطلب }} رمز لغة (أي أن {{PLURAL:$2|a value الشرح بدون رمز لغة غير مقبول|الشرح بدون رمز لغة غير مقبول}}).",
+ "smw-property-predefined-text": "\"$1\" خاصية محددة مسبقا تمثل نص الطول التعسÙÙŠ ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-pdesc": "\"$1\" خاصية محددة مسبقا تسمح بوص٠خاصية ÙÙŠ سياق لغة ويتم توÙيرها من قبل [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-list": "\"$1\" خاصية محددة مسبقا لتحديد قائمة الخصائص المستخدمة خاصية من نوع [[Special:Types/Record|سجل]] ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-limitreport-intext-parsertime": "[SMW] وقت تحليل الشرح ÙÙŠ النص",
+ "smw-limitreport-intext-postproctime": "[SMW] بعد وقت المعالجة",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|ثانية واحدة|ثانية}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|ثانية واحدة|ثانية}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] وقت تحديث المستودع (ÙÙŠ تطهير الصÙحة)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|ثانية واحدة|ثانية}}",
+ "smw_allows_pattern": "من المتوقع أن تحتوي هذه الصÙحة على قائمة بالمراجع (متبوعة [https://ar.wikipedia.org/wiki/تعبير نمطي بتعبيرات نمطية]) يتم توÙيرها بواسطة خاصية [[Property:Allows pattern|يسمح بنمط]] لتعديل هذه الصÙحة; مطلوب إدخال حق <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "تم تصني٠\"$1\" على أنه غير صالح بواسطة التعبير النمطي \"$2\".",
+ "smw-datavalue-allows-pattern-reference-unknown": "لا يمكن مطابقة مرجع النمط \"$1\" بإدخال ÙÙŠ [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "لم يكن مرجع قائمة \"$1\" متطابقا مع [[MediaWiki:Smw allows list $1]] page.",
+ "smw-datavalue-allows-value-list-missing-marker": "محتوى قائمة \"$1\" ÙŠÙتقد العناصر التي تحتوي على علامة قائمة *.",
+ "smw-datavalue-feature-not-supported": "الميزة \"$1\" غير مدعومة أو تم تعطيلها ÙÙŠ موقع الويكي هذا.",
+ "smw-property-predefined-pvap": "\"$1\" خاصية محددة مسبقا يمكن أن تحدد [[MediaWiki:Smw allows pattern|مرجع نمط\n]] لتطبيق [https://en.wikipedia.org/wiki/تعبير_نمطي تعبير نمطي] مطابقة ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-dtitle": "\"$1\" خاصية محددة مسبقا يمكنها تعيين عنوان عرض مميز إلى كيان ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-pvuc": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتقييد تخصيصات القيمة لكل مثيل لتكون Ùريدة (أو واحدة على الأكثر).",
+ "smw-property-predefined-long-pvuc": "يتحدد التÙرد عندما لا تكون القيمتان متساويتين ÙÙŠ تمثيلهما الحرÙÙŠØŒ وسيÙصنَّ٠أي انتهاك لهذا القيد على أنه خطأ.",
+ "smw-datavalue-uniqueness-constraint-error": "الخاصية \"$1\" تسمح Ùقط بتخصيصات القيمة الÙريدة Ùˆ\"$2\" تم التعليق عليها بالÙعل ÙÙŠ الموضوع \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "تسمح الخاصية \"$1\" Ùقط بالتعليقات التوضيحية للقيمة الÙريدة، حيث يحتوي \"$2\" على قيمة معينة، \"$3\" ينتهك قيود التÙرد.",
+ "smw-property-predefined-boo": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل القيم المنطقية.",
+ "smw-property-predefined-num": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل القيم الرقمية.",
+ "smw-property-predefined-dat": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل قيم التاريخ.",
+ "smw-property-predefined-uri": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل قيم URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل القيم الكمية.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "يحتوي \"$1\" على معر٠إزاحة ومنطقة غير معتمد.",
+ "smw-datavalue-time-invalid-values": "تحتوي القيمة \"$1\" على معلومات غير قابلة للتÙسير ÙÙŠ شكل \"$2\".",
+ "smw-datavalue-time-invalid-date-components-common": "يحتوي \"$1\" على بعض المعلومات غير القابلة للتÙسير.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\" يحتوي على شرطة خارجية أو حرو٠أخرى غير صالحة لتÙسير التاريخ.",
+ "smw-datavalue-time-invalid-date-components-empty": "يحتوي \"$1\" على بعض المكونات الÙارغة.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" يحتوي على أكثر من ثلاثة مكونات مطلوبة لتÙسير التاريخ.",
+ "smw-datavalue-time-invalid-date-components-sequence": "يحتوي \"$1\" على تسلسل لا يمكن تÙسيره مقابل مصÙÙˆÙØ© مطابقة متاحة لمكونات التاريخ.",
+ "smw-datavalue-time-invalid-ampm": "يحتوي \"$1\" على \"$2\" كعنصر ساعة غير صالح لعقد مدته 12 ساعة.",
+ "smw-datavalue-time-invalid-jd": "يتعذر تÙسير قيمة إدخال \"$1\" كمعر٠صالح (التاريخ اليولياني) مع الإبلاغ عن \"$2\".",
+ "smw-datavalue-time-invalid-prehistoric": "يتعذر تÙسير قيمة إدخال \"ما قبل التاريخ\" بقيمة $1ØŒ على سبيل المثال، بعد تحديد أكثر من سنة أو نموذج تقويم قد تعود نتائج غير متوقعة ÙÙŠ سياق ما قبل التاريخ.",
+ "smw-datavalue-time-invalid": "يتعذر تÙسير قيمة الإدخال \"$1\" كمكون تاريخ أو وقت صالح مع الإبلاغ عن \"$2\".",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "ÙŠÙتقد URI المنسق العنصر النائب ''$1''.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" مسار غير صالح.",
+ "smw-datavalue-external-identifier-formatter-missing": "الخاصية تÙتقد تعيين [[Property:External formatter uri|\"URI المنسق الخارجي\"]].",
+ "smw-datavalue-keyword-maximum-length": "تجاوزت الكلمة الرئيسية الحد الأقصى لطول {{PLURAL:$1|حرÙ|حروÙ}}..",
+ "smw-property-predefined-eid": "\"$1\" هو [[Special:Types/Boolean|نوع]] وخاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتمثيل المعرÙات الخارجية.",
+ "smw-property-predefined-peid": "\"$1\" خاصية محددة مسبقا تحدد معرÙا خارجيا ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-pefu": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] للتحديد مورد خارجي به عنصر نائب.",
+ "smw-property-predefined-long-pefu": "من المتوقع أن يحتوي URI على عنصر نائب سيتم تعديله باستخدام قيمة [[Special:Types/External identifier|معر٠خارجي]] لتكوين مرجع مرجعي صالح.",
+ "smw-type-eid": "\"$1\" هو بديل نوع البيانات [[Special:Types/Text|نص]] الذي يتطلب خصائص معينة للإعلان عن [[Property:External formatter uri|URI المنسق الخارجي]].",
+ "smw-property-predefined-keyw": "\"$1\" خاصية Ùˆ[[Special:Types/Keyword|نوع]] محددة مسبقا مقدمة من [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] تقوم بتطبيع النص ولها قيود طول الحرÙ.",
+ "smw-type-keyw": "\"$1\" هو بديل نوع البيانات [[Special:Types/Text|النص]] الذي يحتوي على طول حر٠مقيد ويطبع تمثيل محتواه.",
+ "smw-datavalue-stripmarker-parse-error": "القيمة المعطاة \"$1\" تحتوي على [https://en.wikipedia.org/wiki/Help:Strip_markers علامات الشريط] وبالتالي Ùإنه لا يمكن تحليلها بما Ùيه الكÙاية.",
+ "smw-datavalue-parse-error": "القيمة المعطاة \"$1\" لم يتم Ùهمها.",
+ "smw-datavalue-propertylist-invalid-property-key": "تحتوي قائمة الخاصية \"$1\" على Ù…Ùتاح خاصية غير صالح \"$2\".",
+ "smw-datavalue-type-invalid-typeuri": "تعذر تحويل نوع \"$1\" إلى تمثيل URI صالح.",
+ "smw-datavalue-wikipage-missing-fragment-context": "لا يمكن استخدام قيمة إدخال صÙحة الويكي \"$1\" بدون صÙحة سياق.",
+ "smw-datavalue-wikipage-invalid-title": "تحتوي قيمة إدخال نوع الصÙحة \"$1\" على أحر٠غير صالحة أو غير مكتملة، وبالتالي يمكن أن تسبب نتائج غير متوقعة أثناء عملية استعلام أو تعليق توضيحي.",
+ "smw-datavalue-wikipage-property-invalid-title": "الخاصية \"$1\" (كنوع الصÙحة) بقيمة الإدخال \"$2\" تحتوي على أحر٠غير صالحة أو غير مكتملة وبالتالي يمكن أن تسبب نتائج غير متوقعة أثناء الاستعلام أو عملية التعليق التوضيحي.",
+ "smw-datavalue-wikipage-empty": "قيمة إدخال صÙحة الويكي Ùارغة (على سبيل المثال <code>[[SomeProperty::]]ØŒ [[]]</code>) وبالتالي لا يمكن استخدامها كاسم أو كجزء من شرط الاستعلام.",
+ "smw-type-ref-rec": "\"$1\" هو نوع [https://www.semantic-mediawiki.org/wiki/Container الحاوية] الذي يسمح بتسجيل معلومات إضاÙية (مثل بيانات المصدر) حول تعيين قيمة.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "يتوقع نوع [[Special:Types/Reference|المرجع]] قائمة بالخصائص التي سيتم الإعلان عنها باستخدام خاصية [mediawiki.org/wiki/Help:Special_property_Has_fields لديه حقول].",
+ "smw-parser-invalid-json-format": "عاد محلل JSON بـ\"$1\".",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "لا يمكن استخدام \"$1\" كعلامة Ù…Ùضلة لأن اللغة \"$2\" تم تعيينها بالÙعل للتسمية \"$3\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "نسخ الرابط إلى الحاÙظة",
+ "smw-property-userdefined-fixedtable": "تم تكوين \"$1\" [https://www.semantic-mediawiki.org/wiki/Fixed_properties كخاصية ثابتة] وأي تعديل ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Type_declaration إعلان نوعها] يتطلب إما تشغيل <code>setupStore.php</code> أو إكمال المهمة الخاصة [[Special:SemanticMediaWiki|\"\"تثبيت وترقية قاعدة البيانات\"]].",
+ "smw-data-lookup": "جلب البيانات...",
+ "smw-data-lookup-with-wait": "تتم معالجة الطلب وقد يستغرق بعض الوقت.",
+ "smw-no-data-available": "لا تتوÙر بيانات.",
+ "smw-property-req-violation-missing-fields": "تÙتقد الخاصية \"$1\" تÙاصيل التصريح لنوع \"$2\" المشروح عن طريق عدم تحديد الخاصية <code>لديه حقول</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "تÙتقد الخاصية \"$1\" تÙاصيل التصريح للنوع المشروح عن طريق عدم تحديد الخاصية <code>URI المنسق الخارجي</code>.",
+ "smw-property-req-violation-predefined-type": "الخاصية \"$1\" كخاصية معرÙØ© مسبقا تحتوي على إعلان \"من نوع $2\" غير متواÙÙ‚ مع النوع الاÙتراضي لهذه الخاصية.",
+ "smw-property-req-violation-import-type": "تم الكش٠عن إعلان نوع غير متواÙÙ‚ مع نوع معر٠مسبقا من المÙردات \"$1\" المستوردة، بشكل عام، ليس من الضروري الإعلان عن نوع لأن المعلومات يتم استرجاعها من تعري٠الاستيراد.",
+ "smw-property-req-violation-change-propagation-locked-error": "تم تغيير الخاصية \"$1\" وتتطلب إعادة تقييم الكيانات المعينة باستخدام عملية [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير]ØŒ تمت حماية صÙحة الخاصية حتى يتم استكمال تحديث المواصÙات الأساسية لمنع انقطاع وسيط أو مواصÙات متناقضة، قد تستغرق هذه العملية بعض الوقت قبل أن يتم إلغاء حماية الصÙحة لأنها تعتمد على حجم وتواتر جدولة [https://www.mediawiki.org/wiki/Manual:Job_queue طابور العمل].",
+ "smw-property-req-violation-change-propagation-locked-warning": "تم تغيير الخاصية \"$1\" وتتطلب إعادة تقييم الكيانات المعينة باستخدام عملية [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير]ØŒ تمت حماية صÙحة الخاصية حتى يتم استكمال تحديث المواصÙات الأساسية لمنع انقطاع وسيط أو مواصÙات متناقضة، قد تستغرق هذه العملية بعض الوقت قبل أن يتم إلغاء حماية الصÙحة لأنها تعتمد على حجم وتواتر جدولة [https://www.mediawiki.org/wiki/Manual:Job_queue طابور العمل] ويÙقترَح تأجيل التغييرات إلى الخاصية لمنع انقطاع وسيط أو مواصÙات متناقضة.",
+ "smw-property-req-violation-change-propagation-pending": "تحديثات [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير] معلقة ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|وظيÙØ© مقدرة|وظائة مقدرة}}]) ويÙوصَى بالانتظار مع التعديلات على الخاصية حتى يتم الانتهاء من العملية لمنع انقطاع وسيط أو مواصÙات متناقضة.",
+ "smw-property-req-violation-missing-maps-extension": "تعذر على ميدياويكي الدلالي اكتشا٠ملحق [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"خرائط\"] الذي يعد متطلبا أساسيا للنوع المحدد ونتيجة لذلك يحد من وظائ٠هذه الخاصية.",
+ "smw-property-req-violation-type": "تحتوي الخاصية على مواصÙات نوع مناÙسة قد تؤدي إلى تعليقات توضيحية ذات قيمة غير صالحة لذلك Ùمن المتوقع أن يقوم المستخدم بتعيين نوع واحد مناسب.",
+ "smw-change-propagation-protection": "تمت حماية هذه الصÙحة لمنع تعديل البيانات العرضي أثناء تشغيل تحديث [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير]ØŒ قد تستغرق العملية بعض الوقت قبل أن يتم إلغاء حماية الصÙحة لأنها تعتمد على حجم وتواتر جدولة [https://www.mediawiki.org/wiki/Manual:Job_queue طابور العمل].",
+ "smw-category-change-propagation-locked-error": "تم تغيير التصني٠\"$1\" ويتطلب إعادة تقييم الكيانات المعينة باستخدام عملية [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير]ØŒ تمت حماية صÙحة التصني٠حتى يتم استكمال تحديث المواصÙات الأساسية لمنع انقطاع وسيط أو مواصÙات متناقضة، قد تستغرق هذه العملية بعض الوقت قبل أن يتم إلغاء حماية الصÙحة لأنها تعتمد على حجم وتواتر جدولة [https://www.mediawiki.org/wiki/Manual:Job_queue طابور العمل].",
+ "smw-category-change-propagation-locked-warning": "تم تغيير التصني٠\"$1\" ويتطلب إعادة تقييم الكيانات المعينة باستخدام عملية [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير]ØŒ تمت حماية صÙحة التصني٠حتى يتم استكمال تحديث المواصÙات الأساسية لمنع انقطاع وسيط أو مواصÙات متناقضة، قد تستغرق هذه العملية بعض الوقت قبل أن يتم إلغاء حماية الصÙحة لأنها تعتمد على حجم وتواتر جدولة [https://www.mediawiki.org/wiki/Manual:Job_queue طابور العمل] ويÙقترَح تأجيل التغييرات إلى التصني٠لمنع انقطاع وسيط أو مواصÙات متناقضة.",
+ "smw-category-change-propagation-pending": "تحديثات [https://www.semantic-mediawiki.org/wiki/Change_propagation انتشار التغيير] معلقة ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|وظيÙØ© مقدرة|وظائة مقدرة}}]) ويÙوصَى بالانتظار مع التعديلات على التصني٠حتى يتم الانتهاء من العملية لمنع انقطاع وسيط أو مواصÙات متناقضة.",
+ "smw-category-invalid-value-assignment": "\"$1\" غير متعر٠عليها كتصني٠أو شرح قيمة صحيح.",
+ "protect-level-smw-pageedit": "السماح للمستخدمين Ùقط بإذن تعديل الصÙحة (ميدياويكي الدلالي)",
+ "smw-create-protection": "يقتصر إنشاء الخاصية \"$1\" على المستخدمين الذين لديهم الصلاحية المناسبة \"$2\" (أو [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups مجموعة المستخدمين]) أثناء تمكين [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode وضع السلطة].",
+ "smw-create-protection-exists": "تقتصر التغييرات على الخاصية \"$1\" على المستخدمين الذين لديهم الصلاحية المناسبة \"$2\" (أو [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups مجموعة المستخدمين]) أثناء تمكين [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode وضع السلطة].",
+ "smw-edit-protection": "هذه الصÙحة [[Property:Is edit protected|محمية]] لمنع تعديل البيانات العرضي ولا يمكن تعديلها إلا بواسطة المستخدمين الذين لديهم الصلاحية المناسبة \"$1\" (أو [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups مجموعة المستخدمين]).",
+ "smw-edit-protection-disabled": "تم تعطيل حماية التعديل ولذلك لا يمكن استخدام \"$1\" لحماية صÙحات الكيان من التعديل غير المصرح به.",
+ "smw-edit-protection-auto-update": "قام ميدياويكي الدلالي بتحديث حالة الحماية ÙˆÙقا لخاصية \"محمية من التعديل\".",
+ "smw-edit-protection-enabled": "محمية من التعديل (ميدياويكي الدلالي)",
+ "smw-patternedit-protection": "هذه الصÙحة محمية ولا يمكن تعديلها إلا بواسطة المستخدمين ذوي <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions الصلاحية] المناسبة.",
+ "smw-property-predefined-edip": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] للإشارة إلى ما إذا كان التحرير محمي أم لا",
+ "smw-property-predefined-long-edip": "ÙÙŠ حين أن أي مستخدم مؤهل لإضاÙØ© هذه الخاصية إلى موضوع ما، يمكن Ùقط للمستخدم الذي لديه صلاحية مخصصة تعديل أو إلغاء الحماية لكيان بعد إضاÙته.",
+ "smw-query-reference-link-label": "مرجع الاستعلام",
+ "smw-format-datatable-emptytable": "لا تتوÙر بيانات ÙÙŠ الجدول",
+ "smw-format-datatable-info": "إظهار _START_ ل_END_ من _TOTAL_ إدخالات_TOTAL_",
+ "smw-format-datatable-infoempty": "عرض 0 إلى 0 من 0 إدخالات",
+ "smw-format-datatable-infofiltered": "(تمت تصÙيتها من _MAX_ إجمالي الإدخالات)",
+ "smw-format-datatable-infothousands": "،",
+ "smw-format-datatable-lengthmenu": "إظهار إدخالات _MENU_",
+ "smw-format-datatable-loadingrecords": "جار٠التحميل...",
+ "smw-format-datatable-processing": "تجري المعالجة...",
+ "smw-format-datatable-search": "بحث:",
+ "smw-format-datatable-zerorecords": "لم يتم العثور على سجلات مطابقة",
+ "smw-format-datatable-first": "الأول",
+ "smw-format-datatable-last": "الأخير",
+ "smw-format-datatable-next": "التالي",
+ "smw-format-datatable-previous": "السابق",
+ "smw-format-datatable-sortascending": ": تÙعيل Ù„Ùرز العمود تصاعديا",
+ "smw-format-datatable-sortdescending": ": تÙعيل Ù„Ùرز العمود تنازليا",
+ "smw-format-datatable-toolbar-export": "تصدير",
+ "smw-format-list-other-fields-open": " )",
+ "smw-format-list-other-fields-close": " )",
+ "smw-category-invalid-redirect-target": "يحتوي التصني٠\"$1\" على هد٠تحويلة غير صالح إلى نطاق غير التصنيÙ.",
+ "smw-parser-function-expensive-execution-limit": "وصلت وظيÙØ© المحلل إلى الحد الأقصى لعمليات التنÙيذ المكلÙØ© (انظر وسيط التكوين \n [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "ميدياويكي الدلالي يقوم بتحديث الصÙحة الحالية على شرط معالجة بعض طلبات البحث المطلوبة.",
+ "apihelp-smwinfo-summary": "وحدة API لاسترداد المعلومات حول إحصاءات ميدياويكي الدلالي والمعلومات الوصÙية الأخرى.",
+ "apihelp-ask-summary": "وحدة API لاستعلام ميدياويكي الدلالي باستخدام لغة الطلب.",
+ "apihelp-askargs-summary": "وحدة API لاستعلام ميدياويكي الدلالي باستخدام لغة الطلب كقائمة من الشروط والمطبوعات والوسائط.",
+ "apihelp-browsebyproperty-summary": "وحدة API لاسترداد المعلومات حول خاصية أو قائمة من الخصائص.",
+ "apihelp-browsebysubject-summary": "وحدة API لاسترداد المعلومات حول موضوع.",
+ "apihelp-smwtask-summary": "وحدة API لتنÙيذ المهام ذات الصلة بميدياويكي الدلالي",
+ "apihelp-smwbrowse-summary": "وحدة API لدعم أنشطة التصÙØ­ لأنواع الكيانات المختلÙØ© ÙÙŠ ميدياويكي.",
+ "apihelp-ask-parameter-api-version": "تنسيق الإخراج:\n;2: تنسيق متواÙÙ‚ مع الإصدارات السابقة يستخدم {} لقائمة النتائج.\n;3:التنسيق التجريبي الذي يستخدم [] كقائمة نتائج.",
+ "smw-api-invalid-parameters": "وسائط غير صالحة، \"$1\"",
+ "smw-parser-recursion-level-exceeded": "تم تجاوز مستوى العوائد $1 خلال عملية تحليل، ÙŠÙقترَح التحقق من صحة بنية القالب، أو إذا لزم الأمر ضبط وسيط التكوين <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "إظهار $1 {{PLURAL:$1|صÙحة|صÙحات}} باستخدام هذه الخاصية.",
+ "smw-property-page-list-search-count": "إظهار $1 {{PLURAL:$1|صÙحة|صÙحات}} باستخدام هذه الخاصية بمطابقة قيمة $2.",
+ "smw-property-reserved-category": "تصنيÙ",
+ "smw-category": "التصنيÙ",
+ "smw-datavalue-uri-invalid-scheme": "لم يتم إدراج \"$1\" كمخطط URI صالح.",
+ "smw-datavalue-uri-invalid-authority-path-component": "\"$1\" تم التعر٠عليه كمحتوي على مكون سلطة أو مسار غير صحيح \"$2\".",
+ "smw-browse-property-group-title": "مجموعة الخصائص",
+ "smw-browse-property-group-label": "تسمية مجموعة الخصائص",
+ "smw-browse-property-group-description": "وص٠مجموعة الخصائص",
+ "smw-property-predefined-ppgr": "\"$1\" خاصية محددة مسبقا تحدد الكيانات (بشكل رئيسي التصنيÙات) التي يتم استخدامها كمثال تجميع للخصائص ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-filter": "مرشح",
+ "smw-section-expand": "وسع هذا القسم",
+ "smw-section-collapse": "اطو هذا القسم",
+ "smw-ask-format-help-link": "صيغة [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "مساعدة",
+ "smw-cheat-sheet": "صحيÙØ© الغش",
+ "smw-personal-jobqueue-watchlist": "قائمة مراقبة (طابور الشغل)",
+ "smw-property-predefined-label-skey": "Ù…Ùتاح الÙرز",
+ "smw-processing": "تجري المعالجة...",
+ "smw-redirect-target-unresolvable": "الهد٠غير قابل للحل بسبب \"$1\"",
+ "smw-types-title": "النوع: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "غير مسموح بتغيير نموذج محتوى [https://www.semantic-mediawiki.org/wiki/Help:Schema صÙحة مخطط].",
+ "smw-schema-namespace-edit-protection": "هذه الصÙحة محمية ولا يمكن تحريرها إلا بواسطة المستخدمين الذين لديهم [https://www.semantic-mediawiki.org/wiki/Help:Permissions صلاحية] <code>smw-schemaedit</code> المناسبة.",
+ "smw-schema-error": "خطأ ÙÙŠ التحقق",
+ "smw-schema-error-schema": "وجدت مواصÙات '''$1''' والتحقق من صحتها للمخطط الحالي حالات عدم التواÙÙ‚ أو الانتهاكات التالية:",
+ "smw-schema-error-violation": "الانتهاك (\"$1\"، \"$2\")",
+ "smw-schema-error-type-missing": "ÙŠÙتقد المحتوى إلى نوع لكي يتم التعر٠عليه واستخدامه ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Schema نطاق المخطط].",
+ "smw-schema-error-type-unknown": "النوع \"$1\" غير مسجل وبالتالي لا يمكن استخدامه للمحتوى ÙÙŠ [https://www.semantic-mediawiki.org/wiki/Help:Schema نطاق المخطط].",
+ "smw-schema-title": "مخطط",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "النوع",
+ "smw-schema-description-link-format-schema": "من المتوقع أن يقوم نوع المخطط هذا بتعري٠الخصائص الخاصة بإنشاء روابط حساسة للسياق Ùيما يتعلق بخاصية [[Property:Formatter schema|مخطط المنسق]].",
+ "smw-schema-description-search-form-schema": "من المتوقع استخدام نوع المخطط هذا لتحديد نماذج المدخلات والخصائص لمل٠تعري٠[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch extended search] حيث يحتوي على إرشادات حول كيÙية إنشاء حقول الإدخال وتحديد النطاقات الاÙتراضية، أو بيان تعبيرات بادئة لطلب بحث.",
+ "smw-schema-tag": "{{PLURAL:$1|وسم|وسوم}}",
+ "smw-property-predefined-schema-desc": "\"$1\" خاصية محددة مسبقا تقوم بتخزين وص٠المخطط ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-schema-def": "\"$1\" خاصية محددة مسبقا تقوم بتخزين محتويات المخطط ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-schema-tag": "\"$1\" خاصية محددة مسبقا يقدمها [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي] لتحديد مجموعة من المخططات.",
+ "smw-property-predefined-long-schema-tag": "تصني٠يحدد مخططات ذات محتوى أو خصائص مشابهة.",
+ "smw-property-predefined-schema-type": "\"$1\" خاصية محددة مسبقا تص٠نوعا من المخطط القابل للتمييز ويتم توÙيرها بواسطة [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ميدياويكي الدلالي].",
+ "smw-property-predefined-long-schema-type": "من المتوقع أن يقدم كل [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type النوع] تÙسيره الخاص لعناصر وقيود بناء الجملة ويعبر عن ذلك من خلال [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation نموذج التحقق من الصحة].",
+ "smw-ask-title-keyword-type": "بحث بكلمة Ù…Ùتاحية",
+ "smw-ask-message-keyword-type": "تطابق هذا البحث مع الشرط <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "يتعذر الاتصال بالهد٠\"$1\" البعيد.",
+ "smw-remote-source-disabled": "لقد عطل المصدر '''$1''' دعم الطلب عن بعد!",
+ "smw-remote-source-unmatched-id": "لا يطابق المصدر '''$1''' إصدارا من ميدياويكي الدلالي يمكنه دعم طلب بعيد.",
+ "smw-remote-request-note": "يتم جلب النتيجة من المصدر البعيد '''$1''' ومن المحتمل أن يحتوي المحتوى المÙنشأ على معلومات غير متوÙرة ضمن الويكي الحالي.",
+ "smw-remote-request-note-cached": "يتم '''التخزين المؤقت''' للنتيجة من المصدر البعيد '''$1''' ومن المحتمل أن يحتوي المحتوى المÙنشأ على معلومات غير متوÙرة ضمن الويكي الحالي.",
+ "smw-parameter-missing": "الوسيط \"$1\" Ù…Ùقود.",
+ "smw-property-tab-usage": "الاستخدام",
+ "smw-property-tab-redirects": "مرادÙات",
+ "smw-property-tab-subproperties": "خصائص Ùرعية",
+ "smw-property-tab-errors": "مهام غير مناسبة",
+ "smw-property-tab-specification": "... المزيد",
+ "smw-concept-tab-list": "قائمة",
+ "smw-concept-tab-errors": "أخطاء",
+ "smw-ask-tab-result": "النتيجة",
+ "smw-ask-tab-extra": "إضاÙات",
+ "smw-ask-tab-debug": "تصحيح",
+ "smw-ask-tab-code": "كود",
+ "smw-install-incomplete-intro": "تنصيب (أو ترقية) <b>Semantic MediaWiki</b> لم ينتهي وينبغي على أحد الإداريين تنÙيذ المهام التالية لمنع عدم تناسق البيانات قبل أن يواصل المستخدمون إنشاء أو تعديل المحتوى.",
+ "smw-install-incomplete-populate-hash-field": "ملء حقل <code>smw_hash</code> تم تجاهله أثناء التنصيب، سكريبت [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php] مطلوب للتنÙيذ.",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/arc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/arc.json
new file mode 100644
index 00000000..cafc9d55
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/arc.json
@@ -0,0 +1,51 @@
+{
+ "@metadata": {
+ "authors": [
+ "Basharh"
+ ]
+ },
+ "smw_finallistconjunct": ", ܘ",
+ "smw_printername_list": "ܡܟܬܒܘܬÜ",
+ "smw_printername_table": "ܠܘܚÜܬÜ",
+ "smw_printername_template": "Ü©Ü Ü’Ü",
+ "smw_printername_category": "ܣܕܪÜ",
+ "smw_iq_moreresults": "ÜܬÜܪ Ü¦Ü Ü›ÌˆÜ â€¦",
+ "smw_true_words": "ܬܪÜܨÜ,ܬ,ÜÜÜ¢,Ü",
+ "smw_false_words": "ܦܘܕÜ,ܦ,Ü Ü,Ü ",
+ "smw_nofloat": "\"$1\" Ü Ü ÜÜܬÜÜ— Ü¡Ü¢ÜÜ¢Ü.",
+ "smw_type_header": "ܕ̈ÜÜ ÜÜ¬Ü Ü•ÜÜ•Ü«Ü \"$1\"",
+ "exportrdf": "Üܦܩ ܦÜÜ¬Ü¬ÌˆÜ Ü  RDF",
+ "smw_exportrdf_submit": "Üܦܩ",
+ "properties": "ܕ̈ÜÜ ÜܬÜ",
+ "smw_properties_docu": "ܕ̈ÜÜ ÜÜ¬Ü Ü•ÜܬÜÜ¢ ܡܬܦܠܚÜÜ¢ ܒܘÜÜ©Ü",
+ "smw_property_template": "$1 Ü¡Ü¢ ÜÜ•Ü«Ü Ü• $2 ($3 {{PLURAL:$3|ܦܘܠܚÜ|ܦܘܠܚ̈Ü}})",
+ "unusedproperties": "ܕ̈ÜÜ ÜÜ¬Ü Ü Ü Ü¦Ü Üܚܬ̈Ü",
+ "smw_unusedproperty_template": "$1 Ü¡Ü¢ ÜÜ•Ü«Ü Ü• $2",
+ "wantedproperties": "ܕ̈ÜÜ ÜÜ¬Ü Ü£Ü¢Üܩܬ̈Ü",
+ "smw_purge": "ܦܪܓÜ",
+ "types": "Üܕܫ̈Ü",
+
+ "ask": "ܒܨÜÜ Ü•Ü£ÜÜ¡ÜܢܛÜÜ© (ܣܘ̈ܟܠÜ)",
+ "smw_ask_ascorder": "ܡܣܩÜÜܬ",
+ "smw_ask_descorder": "ܡܚܬÜÜܬ",
+ "smw_ask_otheroptions": "Ü“Ü’ÜÜ¬ÌˆÜ Üܚܪ̈ܢÜܬÜ",
+ "searchbyproperty": "ܒܨÜÜ Ü Ü¦Ü˜Ü¬ ܕ̈ÜÜ ÜܬÜ",
+ "smw_sbv_property": "Ü•ÜÜ ÜܬÜ:",
+ "smw_sbv_value": "Ü›ÜÜ¡Ü:",
+ "smw_sbv_submit": "ÜÜ«ÜŸÜš ܦܠܛ̈Ü",
+ "browse": "ܦÜܬ ܘÜÜ©Ü",
+ "smw_browselink": "ܕ̈ÜÜ ÜÜ¬Ü Ü•Ü¡Ü¦ÜܬܢܘܬÜ",
+ "smw_browse_article": "Üܥܠ Ü«Ü¡Ü Ü•Ü¦ÜÜ¬Ü Ü Ü«Ü˜ÜªÜ Ü¦ÜÜ¬Ü¢Ü˜Ü¬Ü Ü¡Ü¢Ü—.",
+ "smw_browse_go": "ܙܠ",
+ "smw_browse_show_incoming": "ÜšÜ˜Ü Ü•ÌˆÜÜ ÜÜ¬Ü Ü•ÜÜ£ÜܪÜÜ¢ ܠܗܪܟÜ",
+ "smw_browse_hide_incoming": "Ü›Ü«Ü Ü•ÌˆÜÜ ÜÜ¬Ü Ü•ÜÜ£ÜܪÜÜ¢ ܠܗܪܟÜ",
+ "smw_browse_no_outgoing": "ܦÜÜ¬Ü Ü—Ü•Ü Ü Üܬ Ü Ü— ܕ̈ÜÜ ÜܬÜ.",
+ "smw_browse_no_incoming": "Ü Üܬ ܕ̈ÜÜ ÜÜ¬Ü Ü•ÜÜ£ÜܪÜÜ¢ ܥܡ ܦÜÜ¬Ü Ü—Ü•Ü.",
+ "smw_inverse_label_default": "$1 Ü¡Ü¢",
+ "smw_pp_from": "Ü¡Ü¢ ܦÜܬÜ",
+ "smw_pp_type": "Ü•ÜÜ ÜܬÜ",
+ "smw_result_prev": "Ü•Ü©Ü•Ü¡",
+ "smw_result_next": "ܕܒܬܪ",
+ "smw_result_results": "ܦܠܛ̈Ü",
+ "smw_result_noresults": "Ü Üܬ ܦܠܛ̈Ü."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/arq.json b/www/wiki/extensions/SemanticMediaWiki/i18n/arq.json
new file mode 100644
index 00000000..e1b428f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/arq.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Oldstoneage"
+ ]
+ },
+ "browse": "صÙّح ÙÙ„ ويكي"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ary.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ary.json
new file mode 100644
index 00000000..2ac1fcce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ary.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "tssna wa7d chwiya kaytcharja ...."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/arz.json b/www/wiki/extensions/SemanticMediaWiki/i18n/arz.json
new file mode 100644
index 00000000..f4af7c44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/arz.json
@@ -0,0 +1,189 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ghaly",
+ "Meno25",
+ "Ouda",
+ "Ramsis II",
+ "ì•„ë¼",
+ "Kghbln"
+ ]
+ },
+ "smw-desc": "جعل الويكى الخاص بك أكثر قابليه للوصول - للآلات ''و'' البشر ([https://www.semantic-mediawiki.org/wiki/Help:User_manual توثيق على الإنترنت])",
+ "smw_viewasrdf": "تلقيم RDF",
+ "smw_finallistconjunct": "، و",
+ "smw_factbox_head": "حقائق عن $1",
+ "smw_isspecprop": "هذه الخاصيه هى خاصيه خاصه ÙÙ‰ هذا الويكى.",
+ "smw_concept_description": "وص٠المبدأ \"$1\"",
+ "smw_no_concept_namespace": "المبادئ يمكن تعريÙها Ùقط ÙÙ‰ الصÙحات ÙÙ‰ نطاق Concept:",
+ "smw_multiple_concepts": "كل صÙحه مبدأ يمكن أن تحتوى على تعري٠مبدأ واحد.",
+ "smw_concept_cache_miss": "المبدأ \"$1\" لا يمكن استخدامه حاليا، بما أن ضبط الويكى يحتاجه إلى أن يتم حسابه خارجيا. لو أن المشكله لم تنته بعد بعض الوقت، سل إدارى موقعك ليجعل هذا المبدأ متوÙرا.",
+ "smw_noinvannot": "القيم لا يمكن تعيينها لخصائص معكوسه.",
+ "smw_baduri": "URIs من النوع \"$1\" غير مسموح بها.",
+ "smw_csv_link": "سى ÙÙ‰ إس",
+ "smw_printername_count": "عد النتائج",
+ "smw_printername_csv": "تصدير CSV",
+ "smw_printername_debug": "استعلام التصليح (للخبراء)",
+ "smw_printername_embedded": "تضمين محتويات الصÙحة",
+ "smw_printername_json": "تصدير JSON",
+ "smw_printername_list": "قائمة",
+ "smw_printername_ol": "ترقيم",
+ "smw_printername_ul": "ترتيب ÙÙ‰ عناصر",
+ "smw_printername_table": "جدول",
+ "smw_printername_broadtable": "جدول عريض",
+ "smw_printername_template": "قالب",
+ "smw-paramdesc-limit": "الرقم الأقصى للعناصر المÙرجعة",
+ "smw-paramdesc-headers": "اعرض العناوين/أسماء الخصائص",
+ "smw-paramdesc-mainlabel": "العلامه للإعطاء لاسم الصÙحه الرئيسية",
+ "smw-paramdesc-link": "أظهر القيم كوصلات",
+ "smw-paramdesc-intro": "النص للعرض قبل نتائج الاستعلام، لو كانت هناك أي",
+ "smw-paramdesc-outro": "النص المطلوب عرضه بعد نتائج الاستعلام، إن ÙˆÙجدت",
+ "smw-paramdesc-default": "النص المطلوب عرضه إذا لم تتوÙر نتائج للاستعلام",
+ "smw-paramdesc-sep": "Ùاصل القيم",
+ "smw-paramdesc-template": "اسم القالب لعرض الخرج به",
+ "smw-paramdesc-columns": "رقم الأعمده لعرض النتائج بها (اÙتراضيا $1)",
+ "smw-paramdesc-embedformat": "وسم HTML المستخدم لتعري٠الترويسة",
+ "smw-paramdesc-embedonly": "لا تظهر الترويسات",
+ "smw-paramdesc-searchlabel": "نص وصله النتائج",
+ "smw_iq_disabled": "استعلامات السيمانتيك تم تعطيلها ÙÙ‰ هذا الويكى.",
+ "smw_iq_moreresults": "… مزيد من النتائج",
+ "smw_parseerror": "القيمه المعطاه لم يتم Ùهمها.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": "،",
+ "smw_notitle": "\"$1\" لا يمكن أن تستخدم مثل هذا الاسم ÙÙ‰ صÙحه ويكى.",
+ "smw_wrong_namespace": "Ùقط الصÙحات ÙÙ‰ النطاق \"$1\" مسموح بها هنا.",
+ "smw_manytypes": "أكثر من نوع واحد لتعري٠الخاصيه.",
+ "smw_emptystring": "السلاسل الÙارغه غير مقبوله.",
+ "smw_notinenum": "\"$1\" ليس ضمن قائمه القيم الممكنه ($2) لهذه الخاصيه.",
+ "smw_noboolean": "\"$1\" غير متعر٠عليها كقيمه بووليان (صواب/خطأ).",
+ "smw_true_words": "صحيح،ص،نعم،ن",
+ "smw_false_words": "خطأ،خ،لا،ل",
+ "smw_nofloat": "ليس عددا \"$1\".",
+ "smw_infinite": "الأرقام الكبيره مثل \"$1\" غير مدعومه.",
+ "smw_nodatetime": "التاريخ \"$1\" لم يتم Ùهمه.",
+ "smw_toomanyclosing": "يبدو أنه هناك الكثير من \"$1\" ÙÙ‰ الاستعلام.",
+ "smw_noclosingbrackets": "\"]]\" ÙÙ‰ استعلامك لم تكن مغلقه باستخدام \"<nowiki>[[</nowiki>\" بعض استخدام",
+ "smw_misplacedsymbol": "الرمز \"$1\" تم استخدامه ÙÙ‰ مكان حيث هو ليس Ù…Ùيدا.",
+ "smw_unexpectedpart": "الجزء \"$1\" من الاستعلام لم ÙŠÙهم.\nالنتائج قد لا تكون كما هو متوقع.",
+ "smw_emptysubquery": "بعض الاستعلامات الÙرعيه ليس لها شرط صحيح.",
+ "smw_misplacedsubquery": "بعض الاستعلامات الÙرعيه تم استخدمها ÙÙ‰ مكان غير مسموح Ùيه بالاستعلامات الÙرعيه.",
+ "smw_valuesubquery": "الاستعلامات الÙرعيه غير مدعومه لقيم الخاصيه \"$1\".",
+ "smw_badqueryatom": "جزء ما \"<nowiki>[[…]]</nowiki>\" من الاستعلام لم يتم Ùهمه.",
+ "smw_propvalueproblem": "قيمه الخاصيه \"$1\" لم يتم Ùهمها.",
+ "smw_noqueryfeature": "ميزه استعلام ما لم يتم دعمها ÙÙ‰ هذا الويكى وجزء من الاستعلام تم إسقاطه ($1).",
+ "smw_noconjunctions": "الوقوÙات ÙÙ‰ الاستعلامات غير مدعومه ÙÙ‰ هذا الويكى وجزء من الاستعلام تم إسقاطه ($1).",
+ "smw_nodisjunctions": "المÙارق ÙÙ‰ استعلامات ليست مدعومه ÙÙ‰ هذا الويكى وجزء من الاستعلام رÙض $1.",
+ "smw_querytoolarge": "شروط الاستÙسار التاليه لم يمكن اعتبارها نتيجه لقيود الويكى ÙÙ‰ حجم أو عمق الاستعلام: $1.",
+ "smw_notemplategiven": "ÙˆÙر قيمه للمحدد \"template\" لتعمل صيغه الاستعلام هذه.",
+ "smw_type_header": "خصائص النوع \"$1\"",
+ "smw_typearticlecount": "عرض {{PLURAL:$1||خاصيه واحده تستخدم|خاصيتين تستخدمان|$1 خصائص تستخدم|$1 خاصيه تستخدم}} هذا النوع.",
+ "smw_attribute_header": "الصÙحات التى تستخدم الخاصيه \"$1\"",
+ "smw_attributearticlecount": "عرض {{PLURAL:$1||الصÙحه التى تستخدم|الصÙحتين اللتين تستخدمان|الصÙحات التى تستخدم}} هذه الخاصيه.",
+ "smw_subproperty_header": "خواص Ùرعية",
+ "smw-subpropertylist-count": "لهذه الخاصيه {{PLURAL:$1||الخاصيه الÙرعيه التالية|الخاصيتين الÙرعيتين التاليتين|الخصائص الÙرعيه التالية}}:",
+ "specialpages-group-smw_group": "سيمانتيك ميدياويكى",
+ "exportrdf": "آر دی‌ إ٠إلى صÙحات تصدير",
+ "smw_exportrdf_docu": " هذه الصÙحه تتيح لك الحصول على بيانات من صÙحه ÙÙ‰ شكل آر دی‌ Ø¥Ù.\nالتصدير إلى صÙحات، أدخل العناوين ÙÙ‰ مربع النص أدناه، عنوان واحد لكل سطر.",
+ "smw_exportrdf_recursive": "تصدير جميع الصÙحات ذات الصله بشكل تكرارى.\nعلما أنه يمكن أن تكون النتيجه كبيرة!",
+ "smw_exportrdf_backlinks": "أيضا تصدير كل الصÙحات التى تشير إلى الصÙحات تم تصديرها.\nيولد آر دى إ٠قابل للتصÙØ­.",
+ "smw_exportrdf_lastdate": "لا تصدر الصÙحات التى لم تتغير منذ نقطه زمنيه محدده.",
+ "smw_exportrdf_submit": "تصدير",
+ "uriresolver": "محلل URI",
+ "properties": "الخصائص",
+ "smw_properties_docu": "الخصائص التاليه تستخدم ÙÙ‰ الويكى.",
+ "smw_property_template": "$1 من نوع $2 ($3)",
+ "smw_propertylackspage": "جميع الخصائص ينبغى أن توص٠بصÙحة!",
+ "smw_propertylackstype": "لا نوع تم تحديده لهذه الخاصيه (اÙتراض النوع $1 حاليا).",
+ "smw_propertyhardlyused": "هذه الخاصيه لا تكاد تستخدم داخل الويكي!",
+ "unusedproperties": "خصائص غير مستخدمة",
+ "smw_unusedproperties_docu": "الخصائص التاليه موجوده على الرغم من أنه لا صÙحه أخرى تستخدمها.",
+ "smw_unusedproperty_template": "$1 من نوع $2",
+ "wantedproperties": "خصائص مطلوبة",
+ "smw_wantedproperties_docu": "الخصائص التاليه مستخدمه ÙÙ‰ الويكى ولكن ليس لديها حتى الآن صÙحه لوصÙها.",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|استخدام|استخدام}})",
+ "smw_purge": "تحديث",
+ "types": "أنواع",
+ "smw_types_docu": "التالى قائمه بجميع أنواع البيانات التى يمكن أن تسند إلى الخصائص.\nكل نوع بيانات له صÙحه حيث يمكن توÙير معلومات إضاÙيه.",
+
+
+ "smw_uri_doc": "محلل URI ينÙØ° [$1 W3C على httpRange-14]. إنه يتأكد من أن البشر لا يتحولون إلى مواقع ويب.",
+ "ask": "بحث سيمانتيك",
+ "smw_ask_sortby": "الترتيب حسب العمود (اختياري)",
+ "smw_ask_ascorder": "تصاعدي",
+ "smw_ask_descorder": "تنازلي",
+ "smw_ask_submit": "جÙد نتائجًا",
+ "smw_ask_editquery": "عدل الاستعلام",
+ "smw_add_sortcondition": "[أض٠شرط ترتيب]",
+ "smw_ask_hidequery": "إخÙاء الاستعلام",
+ "smw_ask_help": "مساعده الاستعلام",
+ "smw_ask_queryhead": "استعلام",
+ "smw_ask_printhead": "بيانات إضاÙيه للعرض",
+ "smw_ask_printdesc": "(أض٠اسمًا مناسبًا واحدًا ÙÙ‰ كل سطر)",
+ "smw_ask_format_as": "صياغه ك:",
+ "smw_ask_defaultformat": "اÙتراضي",
+ "smw_ask_otheroptions": "خيارات أخرى",
+ "smw_ask_show_embed": "عرض الكود المضمن",
+ "smw_ask_hide_embed": "إخÙاء الكود المضمن",
+ "smw_ask_embed_instr": "لتضمين هذا الاستعلام ÙÙ‰ صÙحه ويكى، استخدم الكود أدناه",
+ "searchbyproperty": "البحث حسب الخصائص",
+ "smw_sbv_docu": "البحث عن كل الصÙحات التى لها خصائص معينه وقيمة",
+ "smw_sbv_novalue": "أدخل قيمه صحيحه للخاصيه، أو انظر كل قيم الخصائص ل\"$1\"",
+ "smw_sbv_displayresult": "قائمه بكل الصÙحات التى بها الخاصيه \"$1\" بالقيمه \"$2\"",
+ "smw_sbv_displayresultfuzzy": "قائمه بكل الصÙحات التى لديها الخاصيه \"$1\" بالقيمه \"$2\".\nبما أنه كانت هناك Ùقط عده نتائج، Ùالقيم المقاربه معروضه أيضا.",
+ "smw_sbv_property": "خاصية:",
+ "smw_sbv_value": "قيمة:",
+ "smw_sbv_submit": "جÙد نتائجًا",
+ "browse": "استعرض ويكى",
+ "smw_browselink": "خصائص التصÙØ­",
+ "smw_browse_article": "أدخل اسم الصÙحه لبدء التصÙØ­ منها.",
+ "smw_browse_go": "اذهب",
+ "smw_browse_show_incoming": "أظهر الخواص التى تصل هنا",
+ "smw_browse_hide_incoming": "أخÙ٠الخواص التى تصل هنا",
+ "smw_browse_no_outgoing": "هذه الصÙحه ليس لديها خصائص.",
+ "smw_browse_no_incoming": "لا خصائص تصل إلى هذه الصÙحه.",
+ "smw_inverse_label_default": "$1 من",
+ "smw_inverse_label_property": "اعكس علامه الخاصية",
+ "pageproperty": "بحث خصائص الصÙحة",
+ "smw_pp_docu": "البحث عن جميع قيم خاصيه على صÙحه معينه.\nأدخل صÙحه وخاصيه.",
+ "smw_pp_from": "من صÙحة",
+ "smw_pp_type": "الخاصية",
+ "smw_pp_submit": "جÙد نتائجًا",
+ "smw_result_prev": "السابق",
+ "smw_result_next": "التالي",
+ "smw_result_results": "النتائج",
+ "smw_result_noresults": "لا توجد نتائج.",
+ "smwadmin": "وظائ٠إداريه لسيمانتيك ميدياويكي",
+ "smw_smwadmin_setupsuccess": "تم تنصيب محرك التخزين بنجاح",
+ "smw_smwadmin_return": "أرجع إلى $1",
+ "smw_smwadmin_updatestarted": "عمليه تحديث جديده لتحديث بيانات سيمانتيك بدأت.\nكل البيانات المخزنه ستتم إعاده بنائها أو إصلاحها عند الحاجه.\nأنت يمكنك متابعه تطور التحديث على هذه الصÙحه الخاصه.",
+ "smw_smwadmin_updatenotstarted": "يوجد بالÙعل عمليه تحديث جاريه.\nلا تنشئ واحده أخرى.",
+ "smw_smwadmin_updatestopped": "كل عمليات التحديث الموجوده تم إيقاÙها",
+ "smw_smwadmin_updatenotstopped": "لوق٠عمليه التحديث الجاريه، يجب عليك تÙعيل الصندوق لتعبر عن أنك متأكد حقا.",
+ "smw_smwadmin_docu": "هذه الصÙحه الخاصه تساعدك خلال تنصيب وترقيه <a href=\"https://www.semantic-mediawiki.org\">سيمانتيك ميدياويكي</a>.\nتذكر أن تخزن احتياطيا البيانات القيمه قبل تنÙيذ وظائ٠إداريه.",
+ "smw_smwadmin_db": "تثبيت و تحديث قاعده البيانات",
+ "smw_smwadmin_dbdocu": "سيمانتيك ميدياويكى يتطلب بعض الامتدادات لقاعده بيانات ميدياويكى حتى يخزن البيانات السيمانتيك.\nالوظيÙÙ‡ التاليه تؤكد أن قاعده بياناتك منصبه بشكل صحيح.\nالتغييرات المعموله ÙÙ‰ هذه الخطوه لا تؤثر على بقيه قاعده بيانات ميدياويكى، ويمكن استرجاعها بسهوله ÙÙ‰ حاله الرغبه ÙÙ‰ ذلك.\nوظيÙÙ‡ التنصيب هذه يمكن تنÙيذها عده مرات بدون عمل أى ضرر، لكنها مطلوبه مره واحده عند التنصيب أو الترقيه.",
+ "smw_smwadmin_permissionswarn": "لو أن العمليه Ùشلت مع أخطاء SQLØŒ Ùقاعده البيانات التى طبقها المستخدم بواسطه الويكى الخاص بك (تحقق من LocalSettings.php الخاص بك) على الأرجح لا تمتلك سماحات كاÙيه.\nإما أن تمنح هذا المستخدم سماحات إضاÙيه لإنشاء وحذ٠الجداول، مؤقتا أدخل تسجيل الدخول للroot الخاص بقاعده بياناتك ÙÙ‰ LocalSettings.phpØŒ أو استخدم سكريبت الصيانه <code>setupStore.php</code> الذى يمكنه استخدام تأكيدات AdminSettings.php.",
+ "smw_smwadmin_dbbutton": "جداول البدء أو الترقية",
+ "smw_smwadmin_announce": "أعلن عن الويكى الخاص بك",
+ "smw_smwadmin_datarefresh": "إصلاح البيانات وتحديثها",
+ "smw_smwadmin_datarefreshdocu": "من الممكن استرجاع كل بيانات سيمانتيك ميدياويكى بناء على المحتويات الحاليه للويكى.\nهذا يمكن أن يكون Ù…Ùيدا لإصلاح البيانات المكسوره أو لتحديث البيانات لو أن الصيغه الداخليه تغيرت بسبب ترقيه برنامج.\nالتحديث يتم تنÙيذه صÙحه بصÙحه ولن يتم إكماله حالا.\nالتالى يعرض ما إذا كان التحديث يجرى ويسمح لك ببدء أو إنهاء التحديثات (إلا لو كانت هذه الخاصيه تم تعطيلها بواسطه إدارى الموقع).",
+ "smw_smwadmin_datarefreshprogress": "<strong>تحديث يجرى بالÙعل.</strong>\nمن الطبيعى أن تتقدم عمليات التحديث ببطء Ùقط بما أنها تحدث البيانات Ùقط ÙÙ‰ كميات صغيره كل مره مستخدم ما يصل إلى الويكى.\nللانتهاء من هذا التحديث سريعا، يمكنك تشغيل سكريبت صيانه ميدياويكى <code>runJobs.php</code> (استخدم الخيار <code>--maxjobs 1000</code> لتحديد عدد التحديثات المعموله كل مرة).\nالتقدم المقدر للتحديث الحالي:",
+ "smw_smwadmin_datarefreshbutton": "أبدا تحديث البيانات",
+ "smw_smwadmin_datarefreshstop": "أوق٠هذا التحديث",
+ "smw_smwadmin_datarefreshstopconfirm": "نعم، أنا متأكد",
+ "smw_smwadmin_support": "أحصل على دعم",
+ "smw_smwadmin_supportdocu": ":مصادر متعدده يمكن أن تساعده ÙÙ‰ حاله مشاكل",
+ "smw_smwadmin_installfile": "لو أنك تواجه مشاكل مع تنصيبك، ابدأ بالتحقق من الإرشادات ÙÙ‰ <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">مل٠INSTALL</a>.",
+ "smw_smwadmin_smwhomepage": "وثائق المستخدم الكامله لسيمانتيك ميدياويكى موجوده ÙÙ‰ <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw_smwadmin_mediazilla": "العلل يمكن إبلاغها إلى <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/\">ميديازيللا</a>.",
+ "smw_smwadmin_questions": "لو لديك أسئله أو اقتراحات أخرى، انضم إلى النقاش ÙÙ‰ <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">منتدى مستخدمى سيمانتيك ميدياويكي</a>.",
+ "smw_adminlinks_datastructure": "هيكل البيانات",
+ "smw_adminlinks_displayingdata": "عرض البيانات",
+ "smw_adminlinks_inlinequerieshelp": "مساعده الاستعلامات الداخلية",
+ "smw-createproperty-isproperty": "هذه خاصية من نوع $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1||القيمة المسموح بها لهذه الخاصية هي|القيمتان المسموح بهما لهذه الخاصية هما|القيم المسموح بها لهذه الخاصية هي}}:",
+ "smw_unknowntype": "نوع غير مدعوم \"$1\" لتعري٠الممتلكات.",
+ "smw_concept_header": "صÙحات المبدأ \"$1\"",
+ "smw_conceptarticlecount": "عرض {{PLURAL:$1||صÙحه واحده تنتمي|صÙحتين تنتميان|$1 صÙحات تنتمي|$1 صÙحه تنتمي}} إلى هذا المبدأ.",
+ "smw-livepreview-loading": "تحميل…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/as.json b/www/wiki/extensions/SemanticMediaWiki/i18n/as.json
new file mode 100644
index 00000000..a84e857c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/as.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gitartha.bordoloi",
+ "IKHazarika"
+ ]
+ },
+ "browse": "বà§à§°à¦¾à¦‰à¦œ ৱিকি",
+ "smw_result_results": "ফলাফল",
+ "smw-livepreview-loading": "ল'ড হৈ আছে…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ast.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ast.json
new file mode 100644
index 00000000..0f234264
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ast.json
@@ -0,0 +1,659 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tifinaghes",
+ "Xuacu",
+ "Macofe"
+ ]
+ },
+ "smw-desc": "Facer la to wiki más accesible - pa les máquines ''y'' pa los humanos ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentación en llinia])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "Nun s'activó la función de MediaWiki Semántica pa esta wiki.",
+ "smw_viewasrdf": "Canal RDF",
+ "smw_finallistconjunct": ", â´·",
+ "smw-factbox-head": "… más sobre «$1»",
+ "smw-factbox-facts": "Fechos",
+ "smw-factbox-facts-help": "Amosar les declaraciones y fechos que se crearon por un usuariu",
+ "smw-factbox-facts-derived": "Fechos derivaos",
+ "smw-factbox-facts-derived-help": "Amosar fechos que se derivaron de regles o cola ayuda d'otres técniques de razonamientu",
+ "smw_isspecprop": "Esta propiedá ye una propiedá especial nesta wiki.",
+ "smw-concept-cache-header": "Usu de la caché",
+ "smw-concept-cache-count": "La [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count caché de conceptos] contien {{PLURAL:$1|'''una''' entidá|'''$1''' entidaes}} ($2).",
+ "smw-concept-no-cache": "Nun hai caché disponible.",
+ "smw_concept_description": "Descripción del conceptu \"$1\"",
+ "smw_no_concept_namespace": "Los conceptos sólo puen definise nes páxines del espaciu de nomes Conceptu:.",
+ "smw_multiple_concepts": "Cada páxina de conceptu sólo pue tener una definición d'un conceptu.",
+ "smw_concept_cache_miss": "El conceptu \"$1\" nun pue usase de momentu, porque la configuración de la wiki requier que se calcule desconectáu. Si'l problema nun desapaez dempués d'un tiempu, pida al alministrador del sitiu que faiga disponible esti conceptu.",
+ "smw_noinvannot": "Nun pueden dase valores a propiedaes inverses.",
+ "version-semantic": "Estensiones semántiques",
+ "smw_baduri": "Les URIs de la forma «$1» nun tan permitíes.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Resultaos del recuentu",
+ "smw_printername_csv": "Esportación a CSV",
+ "smw_printername_dsv": "Esportación a DSV",
+ "smw_printername_debug": "Consulta de depuración (pa espertos)",
+ "smw_printername_embedded": "Incrustar conteníu de páxina",
+ "smw_printername_json": "Esportación a JSON",
+ "smw_printername_list": "Llista",
+ "smw_printername_plainlist": "Llista simple",
+ "smw_printername_ol": "Enumeración",
+ "smw_printername_ul": "Itemización",
+ "smw_printername_table": "Tabla",
+ "smw_printername_broadtable": "Tabla ancha",
+ "smw_printername_template": "Plantía",
+ "smw_printername_templatefile": "Ficheru de plantía",
+ "smw_printername_rdf": "Esportación a RDF",
+ "smw_printername_category": "Categoría",
+ "validator-type-class-SMWParamSource": "testu",
+ "smw-paramdesc-limit": "El númberu máximu de resultaos a devolver",
+ "smw-paramdesc-offset": "El desplazamientu del primer resultáu",
+ "smw-paramdesc-headers": "Ver les cabeceres/nomes de propiedá",
+ "smw-paramdesc-mainlabel": "La etiqueta a dar al nome de la páxina principal",
+ "smw-paramdesc-link": "Ver los valores como enllaces",
+ "smw-paramdesc-intro": "El testu que s'amuesa antes de los resultaos de la consulta, si los hai",
+ "smw-paramdesc-outro": "El testu que s'amuesa dempués de los resultaos de la consulta, si los hai",
+ "smw-paramdesc-default": "El testu que s'amuesa si nun hai resultaos na consulta",
+ "smw-paramdesc-sep": "El separador ente resultancies",
+ "smw-paramdesc-propsep": "El separador ente les propiedaes d'una entrada de resultancia",
+ "smw-paramdesc-valuesep": "El separador ente los valores d'una propiedá d'un resultáu",
+ "smw-paramdesc-showsep": "Amosar el separador al principiu del ficheru CSV (\"sep=<valor>\")",
+ "smw-paramdesc-distribution": "En llugar d'amosar tolos valores, contar les veces qu'ocurren y amosales.",
+ "smw-paramdesc-distributionsort": "Ordenar la distribución de valores pol númberu d'ocurrencies.",
+ "smw-paramdesc-distributionlimit": "Llendar la distribución de valores a la cuenta d'algunos valores solamente.",
+ "smw-paramdesc-aggregation": "Especifica con qué tien de rellacionase'l conxuntu",
+ "smw-paramdesc-template": "El nome d'una plantía cola qu'amosar los llistaos",
+ "smw-paramdesc-columns": "El númberu de columnes p'amosar los resultaos",
+ "smw-paramdesc-userparam": "Un valor pasáu en cada llamada de plantía,si s'usa una plantía",
+ "smw-paramdesc-class": "Una clase CSS estra qu'establecer pa la tabla",
+ "smw-paramdesc-introtemplate": "El nome d'una plantía que s'amuesa antes de los resultaos de la consulta, si los hai",
+ "smw-paramdesc-outrotemplate": "El nome d'una plantía que s'amuesa dempués de los resultaos de la consulta, si los hai",
+ "smw-paramdesc-embedformat": "La etiqueta HTML que s'usa pa definir los encabezaos",
+ "smw-paramdesc-embedonly": "Nun amosar cabeceres",
+ "smw-paramdesc-table-class": "Una clase CSS estra qu'establecer pa la tabla",
+ "smw-paramdesc-table-transpose": "Ver les testeres de tabla en vertical y los resultaos n'horizontal",
+ "smw-paramdesc-rdfsyntax": "La sintaxis RDF a usar",
+ "smw-paramdesc-csv-sep": "Especifica un separador de columnes",
+ "smw-paramdesc-csv-valuesep": "Especifica un separador de valores",
+ "smw-paramdesc-csv-merge": "Mecer los valores de fileres y columnes col mesmu identificador de suxetu (aka primera columna)",
+ "smw-paramdesc-csv-bom": "Añadir un BOM (caráuter pa indicar la triba «endian») al principiu del ficheru de salida",
+ "smw-paramdesc-dsv-separator": "El separador a usar",
+ "smw-paramdesc-dsv-filename": "El nome del ficheru DSV",
+ "smw-paramdesc-filename": "El nome del ficheru de salida",
+ "smw-smwdoc-description": "Amuesa una tabla con tolos parámetros que se pueden usar pal formatu de resultaos especificáu xunto colos valores predeterminaos y les descripciones.",
+ "smw-smwdoc-default-no-parameter-list": "Esti formatu de resultáu nun ufre parámetros específicos de formatu.",
+ "smw-smwdoc-par-format": "El formatu de los resultaos pal qu'amosar la documentación de parámetros.",
+ "smw-smwdoc-par-parameters": "Qué parámetros amosar. \"specific\" pa los amestaos pol formatu, \"base\" pa los disponibles en tolos formatos y \"all\" pa ambos.",
+ "smw-paramdesc-sort": "Propiedá pola qu'ordenar la consulta",
+ "smw-paramdesc-order": "Orde d'ordenación de la consulta",
+ "smw-paramdesc-searchlabel": "Testu pa siguir la busca",
+ "smw-paramdesc-named_args": "Nome de los argumentos que se pasan a la plantía",
+ "smw-paramdesc-template-arguments": "Configura cómo se pasen los argumentos con nome a la plantía",
+ "smw-paramdesc-import-annotation": "Se copiaran otros datos anotaos más demientres l'análisis d'un tema",
+ "smw-paramdesc-export": "Opción d'esportación",
+ "smw-paramdesc-prettyprint": "Una salida con formatu axeitáu qu'amuesa más sangríes y llinies nueves",
+ "smw-paramdesc-json-unescape": "Salida pa contener barres sin escapar y carácteres Unicode multibyte",
+ "smw-paramdesc-json-type": "Tipu de serialización",
+ "smw-paramdesc-source": "Fonte alternativa de consulta",
+ "smw-paramdesc-jsonsyntax": "Sintaxis JSON a usar",
+ "smw-printername-feed": "Canal RSS y Atom",
+ "smw-paramdesc-feedtype": "Tipu de canal",
+ "smw-paramdesc-feedtitle": "El testu a usar como títulu de la canal",
+ "smw-paramdesc-feeddescription": "El testu a usar como descripción de la canal",
+ "smw-paramdesc-feedpagecontent": "Conteníu de la páxina que s'amosará cola canal",
+ "smw-label-feed-description": "Canal $2 $1",
+ "smw_iq_disabled": "Les consultes semántiques tan desactivaes nesta wiki.",
+ "smw_iq_moreresults": "… más resultaos",
+ "smw_parseerror": "Nun s'entendió el valor dau.",
+ "smw_notitle": "\"$1\" nun se pue usar como nome de páxina nesta wiki.",
+ "smw_noproperty": "\"$1\" nun se pue usar como nome de propiedá nesta wiki.",
+ "smw_wrong_namespace": "Equí sólo se permiten les páxines del espaciu de nomes \"$1\".",
+ "smw_manytypes": "Definiose más d'un tipu pa la propiedá.",
+ "smw_emptystring": "Nun s'aceuten les cadenes baleres.",
+ "smw_notinenum": "«$1» nun ta na llista ($2) de [[Property:Allows value|valores posibles]] de la propiedá «$3».",
+ "smw_noboolean": "«$1» nun ta reconocíu como valor booleanu (verdaderu/falsu).",
+ "smw_true_words": "verdaderu,v,si,s,true,t",
+ "smw_false_words": "falsu,false,f,non,n",
+ "smw_nofloat": "\"$1\" nun ye un númberu.",
+ "smw_infinite": "Nun hai sofitu pa númberos tán grandes como \"$1\".",
+ "smw_unitnotallowed": "\"$1\" nun ta declarada como unidá de midida válida pa esta propiedá.",
+ "smw_nounitsdeclared": "Nun se declaró nenguna unidá de midida pa esta propiedá.",
+ "smw_novalues": "Nun s'especificó nengún valor.",
+ "smw_nodatetime": "Nun s'entendió la data «$1».",
+ "smw_toomanyclosing": "Paez qu'hai demasiaes apaiciones de \"$1\" na consulta.",
+ "smw_noclosingbrackets": "Algún usu de \"<nowiki>[[</nowiki>\" na consulta nun ta zarráu coles \"]]\" correspondientes.",
+ "smw_misplacedsymbol": "El símbolu \"$1\" usose nun llugar onde nun ye útil.",
+ "smw_unexpectedpart": "La parte \"$1\" de la consulta nun s'entendió.\nLos resultaos podríen nun ser los esperaos.",
+ "smw_emptysubquery": "Alguna subconsulta nun tien una condición válida.",
+ "smw_misplacedsubquery": "Alguna subconsulta s'usó nun llugar onde nun se permiten les subconsultes.",
+ "smw_valuesubquery": "Nun hai sofitu de subconsultes pa los valores de la propiedá \"$1\".",
+ "smw_badqueryatom": "Una parte «<nowiki>[[…]]</nowiki>» de la consulta nun s'entendió.",
+ "smw_propvalueproblem": "El valor de la propiedá «$1» nun s'entendió.",
+ "smw_noqueryfeature": "Alguna carauterística de la consulta nun tien sofitu nesta wiki y parte de la consulta nun se tuvo en cuenta ($1).",
+ "smw_noconjunctions": "Les conxunciones nes consultes nun tienen sofitu nesta wiki y parte de la consulta nun se tuvo en cuenta ($1)",
+ "smw_nodisjunctions": "Les dixunciones nes consultes nun tienen sofitu nesta wiki y parte de la consulta nun se tuvo en cuenta ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|La siguiente condición|Les siguientes condiciones}} de consulta nun {{PLURAL:$2|pudo|pudieron}} considerase poles torgues de les wikis nel tamañu o fondura de les consultes: <code>$1</code>",
+ "smw_notemplategiven": "Dar un valor al parámetru \"plantía\" pa qu'esti formatu de consulta funcione.",
+ "smw_db_sparqlqueryproblem": "Nun pudieron algamase resultaos de la consulta de la base de datos SPARQL. Esti error pue ser temporal o indicar un error nel programa de la base de datos.",
+ "smw_db_sparqlqueryincomplete": "Resultó demasiao difícil responder la consulta y s'encaboxó. Pueden faltar algunos resultaos. Si ye posible, intente facer una consulta más cenciella.",
+ "smw_type_header": "Propiedaes de tipu \"$1\"",
+ "smw_typearticlecount": "Amosando $1 {{PLURAL:$1|propiedá qu'usa|propiedaes qu'usen}} esti tipu.",
+ "smw_attribute_header": "Páxines qu'usen la propiedá \"$1\"",
+ "smw_attributearticlecount": "Amosando $1 {{PLURAL:$1|páxina qu'usa|páxines qu'usen}} esta propiedá.",
+ "smw-propertylist-subproperty-header": "Subpropiedaes",
+ "smw-propertylist-redirect-header": "Sinónimos",
+ "smw-propertylist-error-header": "Páxines con atribuciones inapropiaes",
+ "smw-propertylist-count": "S'{{PLURAL:$1|amuesa $1 entidá rellacionada|amuesen $1 entidaes rellacionaes}}.",
+ "smw-propertylist-count-with-restricted-note": "S'{{PLURAL:$1|amuesa $1 entidá rellacionada|amuesen $1 entidaes rellacionaes}} (hai más disponibles, pero la salida ta llendada a «$2»).",
+ "smw-propertylist-count-more-available": "S'{{PLURAL:$1|amuesa $1 entidá rellacionada|amuesen $1 entidaes rellacionaes}} (hai más disponibles).",
+ "exportrdf": "Esportar les páxines a RDF",
+ "smw_exportrdf_docu": "Esta páxina permite algamar datos d'una páxina en formatu RDF.\nPa esportar les páxines, escriba los títulos nel cuadru de testu d'abaxo, un títulu por llinia.",
+ "smw_exportrdf_recursive": "Esportar recursivamente toles páxines rellacionaes.\n¡Tenga en cuenta que'l resultáu pue ser grande!",
+ "smw_exportrdf_backlinks": "Esportar tamién toles páxines que se refieren a les páxines esportaes.\nXenera un RDF navegable.",
+ "smw_exportrdf_lastdate": "Nun esportar les páxines que nun camudaron dende cierta data.",
+ "smw_exportrdf_submit": "Esportar",
+ "uriresolver": "URIResolver",
+ "properties": "Propiedaes",
+ "smw_properties_docu": "Les siguientes propiedaes s'usen na wiki.",
+ "smw_property_template": "$1 de tipu $2 ($3 {{PLURAL:$3|usu|usos}})",
+ "smw_propertylackspage": "¡Toles propiedaes habríen de tar descrites por una páxina!",
+ "smw_propertylackstype": "Nun s'especificó nengún tipu pa esta propiedá (asumiendo tipu $1 de momentu).",
+ "smw_propertyhardlyused": "¡Esta propiedá apenes tien usu nesta wiki!",
+ "smw-property-name-invalid": "Nun se pue usar la propiedá $1 (nome de propiedá inválidu).",
+ "smw-property-name-reserved": "«$1» marcóse como nome acutáu y nun tendría d'utilizase como propiedá. La siguiente [https://www.semantic-mediawiki.org/wiki/Help:Property_naming páxina d'ayuda] podría tener información sobre'l motivu pa qu'esti nome tea acutáu.",
+ "smw-sp-property-searchform": "Amosar les propiedaes que contengan:",
+ "smw-sp-property-searchform-inputinfo": "La entrada ye sensible a mayúscules y, cuando s'usa pa filtriar, namái apaecen les propiedaes que casen cola condición.",
+ "smw-special-property-searchform": "Amosar les propiedaes que contengan:",
+ "smw-special-property-searchform-inputinfo": "La entrada ye sensible a mayúscules y, cuando s'usa pa filtriar, namái apaecen les propiedaes que casen cola condición.",
+ "smw-special-property-searchform-options": "Opciones",
+ "smw-special-wantedproperties-filter-label": "Filtru:",
+ "smw-special-wantedproperties-filter-none": "Nengunu",
+ "smw-special-wantedproperties-filter-unapproved": "Ensin aprobar",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Opción de filtru usada en conexón col módulu d'autoridá.",
+ "concepts": "Conceptos",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceptu] pue vese como una \"categoría dinámica\", esto ye, una coleición de páxines que nun tan creaes manualmente, sinón que se calculen por Semantic MediaWiki dende una descripción d'una consulta dada.",
+ "smw-special-concept-header": "Llista de conceptos",
+ "smw-special-concept-count": "{{PLURAL:$1|Ta llistáu el siguiente conceptu|Tan llistaos los siguientes $1 conceptos}}.",
+ "smw-special-concept-empty": "Nun s'alcontró nengún conceptu.",
+ "unusedproperties": "Propiedaes non usaes",
+ "smw-unusedproperties-docu": "Nesta páxina ta la llista de [https://www.semantic-mediawiki.org/wiki/Unused_properties propiedaes sin usu] que tán declaraes anque nenguna páxina fai usu d'elles. Pa una vista distinta, visita les páxines especiales de [[Special:Properties|propiedaes]] o [[Special:WantedProperties|propiedaes solicitaes]]",
+ "smw-unusedproperty-template": "$1 de tipu $2",
+ "wantedproperties": "Propiedaes deseaes",
+ "smw-wantedproperties-docu": "Nesta páxina ta la llista de [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedaes solicitaes] que tán declaraes anque nenguna páxina fai usu d'elles. Pa una vista distinta, visita les páxines especiales de [[Special:Properties|propiedaes]] o [[Special:UnusedProperties|propiedaes sin usu]]",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|usu|usos}})",
+ "smw-special-wantedproperties-docu": "Nesta páxina ta la llista de [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedaes solicitaes] que s'usen nesta wiki anque nenguna páxina les describe. Pa una vista distinta, visita les páxines especiales de [[Special:Properties|propiedaes]] o [[Special:UnusedProperties|propiedaes sin usu]]",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|usu|usos}})",
+ "smw_purge": "Refrescar",
+ "smw-purge-failed": "Falló la recarga",
+ "types": "Tipos",
+ "smw_types_docu": "Llista de [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipos de datos disponibles] con cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipu] que representa un conxuntu únicu d'atributos pa describir un valor en términos de carauterístiques d'almacenamientu y visualización que s'herieden pa una propiedá atribuyida.",
+ "smw-special-types-no-such-type": "El tipu de datos especificáu nun esiste",
+ "smw-statistics": "Estadístiques semántiques",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor|Valores}} de propiedá (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propiedá|Propiedaes}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propiedá|Propiedaes}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propiedá|Propiedaes}}]] (úsase con un valor polo menos)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propiedá|Propiedaes}} ({{PLURAL:$1|rexistrada|rexistraes}} con una páxina)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propiedá|Propiedaes}} ({{PLURAL:$1|asociada|asociaes}} a un tipu de datos)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultes}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Consulta|Consultes}}]]",
+ "smw-statistics-query-size": "Tamañu de la consulta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Conceptu|Conceptos}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Conceptu|Conceptos}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Sub-oxetu|Sub-oxetos}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Suboxetu|Suboxetos}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipu|Tipos}} de datos]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valor de propiedá|Valores de propiedá}} ([[Special:ProcessingErrorList|{{PLURAL:$1|anotación incorreuta|anotaciones incorreutes}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor de propiedá|Valores de propiedá}} ({{PLURAL:$1|anotación incorreuta|anotaciones incorreutes}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entidá anticuada (marcada pa desaniciar)|Entidaes anticuaes (marcaes pa desaniciar)}}",
+ "smw_uri_doc": "El resolvedor d'URI encadarma [$1 W3C TAG finding on httpRange-14].\nS'encarga de metadatos que los humanos nun ven nos sitios web.",
+ "ask": "Gueta semántica",
+ "smw-ask-help": "Esta sección contien dellos enllaces p'ayudar a esplicar cómo utilizar la sintaxis <code>#ask</code>.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selección de páxines] describe cómo escoyer páxines y construir condiciones.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de gueta] amuesa los operadores de gueta disponibles, incluyendo los de consulta d'intervalu y de caráuteres comodín.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Amuesa d'información] espon l'usu de les declaraciones de salida y les opciones de formatu.",
+ "smw_ask_sortby": "Ordenar por columna (opcional)",
+ "smw_ask_ascorder": "Ascendiente",
+ "smw_ask_descorder": "Descendiente",
+ "smw-ask-order-rand": "Al debalu",
+ "smw_ask_submit": "Alcontrar resultaos",
+ "smw_ask_editquery": "Editar consulta",
+ "smw_add_sortcondition": "[Añadir una condición d'ordenación]",
+ "smw-ask-sort-add-action": "Añadir una condición d'ordenación",
+ "smw_ask_hidequery": "Anubrir consulta (vista compacta)",
+ "smw_ask_help": "Ayuda pa consultes",
+ "smw_ask_queryhead": "Condición",
+ "smw_ask_printhead": "Selección de datos a imprimir",
+ "smw_ask_printdesc": "(añadir un nome de propiedá por llinia)",
+ "smw_ask_format_as": "Dar formatu de:",
+ "smw_ask_defaultformat": "predetermináu",
+ "smw_ask_otheroptions": "Otres opciones",
+ "smw-ask-otheroptions-info": "Esta seición contien opciones qu'alteren los formatos de salida. Les descripciones de los parámetros pueden vese pasando'l mur perriba d'ellos.",
+ "smw-ask-otheroptions-collapsed-info": "Use l'iconu «más» pa ver toles opciones disponibles",
+ "smw_ask_show_embed": "Amosar el códigu incrustáu",
+ "smw_ask_hide_embed": "Anubrir el códigu incrustáu",
+ "smw_ask_embed_instr": "Pa incrustar en llinia esta consulta nuna páxina wiki use'l códigu siguiente.",
+ "smw-ask-delete": "Desaniciar",
+ "smw-ask-sorting": "Ordenación",
+ "smw-ask-options": "Opciones",
+ "smw-ask-options-sort": "Opciones d'ordenación",
+ "smw-ask-format-options": "Formatu y opciones",
+ "smw-ask-parameters": "Parámetros",
+ "smw-ask-search": "Guetar",
+ "smw-ask-debug": "Depurar",
+ "smw-ask-debug-desc": "Xenera información pa la depuración de consultes",
+ "smw-ask-no-cache": "Desactivar la caxé de consultes",
+ "smw-ask-no-cache-desc": "Resultancies ensin caché de consulta",
+ "smw-ask-result": "Resultáu",
+ "smw-ask-empty": "Borrar toles entraes",
+ "smw-ask-download-link-desc": "Descargar les resultancies de la consulta en formatu $1",
+ "smw-ask-format": "Formatu",
+ "smw-ask-format-selection-help": "Ayuda pal formatu seleicionáu: $1",
+ "smw-ask-condition-change-info": "Alteróse la condición, ya'l buscador precisa executar otra vegada la consulta pa producir resultancies que casen colos requisitos nuevos.",
+ "smw-ask-input-assistance": "Asistencia d'entrada",
+ "smw-ask-condition-input-assistance": "Bríndase [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistencia d'entrada] pa los campos de salida, d'ordenación y de condición. El campu de condición rique emplegar dalgunu d'estos prefixos:",
+ "smw-ask-condition-input-assistance-property": "<code>p</code>: pa recibir suxerencies de propiedaes (p.&nbsp;ex., <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c</code>: pa recibir suxerencies de categoríes",
+ "smw-ask-condition-input-assistance-concept": "<code>con</code>: pa recibir suxerencies de conceutos",
+ "smw-ask-format-change-info": "Cambió'l formatu y ríquese executar nuevamente la consulta pa que correspuenda colos nuevos parámetros y opciones de vista.",
+ "smw-ask-format-export-info": "El formatu d'esportación escoyíu ye unu que nun tien representación visual, por eso los resultaos sólo s'ufren como descarga.",
+ "smw-ask-query-search-info": "{{PLURAL:$3|1=<code>$2</code> (de la caché) respondió|<code>$2</code> (de la caché) respondieron|<code>$2</code> respondió}} la consulta <code><nowiki>$1</nowiki></code> en $4 {{PLURAL:$4|segundu|segundos}}.",
+ "searchbyproperty": "Buscar por propiedá",
+ "processingerrorlist": "Procesando la llista d'errores",
+ "propertylabelsimilarity": "Informe de semeyanza na etiqueta Propiedá",
+ "smw-processingerrorlist-intro": "La llista siguiente ufre una güeyada de los errores de procesáu qu'apaecieron venceyaos con [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Recomiéndase vixilar esta llista de manera regular y correxir les anotaciones de valor inválidu.",
+ "smw_sbv_docu": "Buscar toles páxines que tienen una propiedá y valor daos.",
+ "smw_sbv_novalue": "Escribi un valor válidu pa la propiedá, o mira tolos valores de la propiedá pa «$1».",
+ "smw_sbv_displayresult": "Una llista de toles páxines que tienen la propiedá \"$1\" col valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Llista de toles páxines que tienen la propiedá \"$1\" con valor \"$2\".\nComo namái hebo unos pocos resultaos, tamién s'amuesen los valores aproximaos.",
+ "smw_sbv_property": "Propiedá:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Alcontrar resultaos",
+ "browse": "Esplorar la wiki",
+ "smw_browselink": "Esplorar les propiedaes",
+ "smw_browse_article": "Escriba'l nome de la páxina dende la qu'empezar la navegación.",
+ "smw_browse_go": "Dir",
+ "smw_browse_show_incoming": "Ver les propiedaes entrantes",
+ "smw_browse_hide_incoming": "Tapecer les propiedaes entrantes",
+ "smw_browse_no_outgoing": "Esta páxina nun tien propiedaes.",
+ "smw_browse_no_incoming": "Nun hai propiedaes qu'enllacen a esta páxina.",
+ "smw-browse-from-backend": "Ta recuperándose la información del motor.",
+ "smw-browse-intro": "Esta páxina apurre detalles sobre una instancia d'asuntu o d'entidá, escribi'l nome d'un oxetu a inspeicionar.",
+ "smw-browse-invalid-subject": "La validación del asuntu devolvió l'error «$1».",
+ "smw-browse-api-subject-serialization-invalid": "L'asuntu tien un formatu de serialización inválidu.",
+ "smw-browse-js-disabled": "Albídrase que Javascript ta desactiváu o nun ta disponible, y recomendamos usar un navegador con encontu pal mesmu. Otres opciones discútense na páxina del parámetru de configuración [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Amosar grupos",
+ "smw-browse-hide-group": "Tapecer grupos",
+ "smw-noscript": "Esta páxina o aición rique Javascript pa funcionar. Activa Javascript nel navegador, o utiliza un navegador con encontu, pa que pueda sirvise y proporcionase esta carauterística como se solicitó. Pa más información, consulta la páxina d'ayuda [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiqueta de propiedá inversa",
+ "pageproperty": "Gueta de propiedá de paxina",
+ "smw_pp_docu": "Escribe una páxina y propiedá,o sólo una propiedá pa llograr tolos valores venceyaos.",
+ "smw_pp_from": "De la páxina:",
+ "smw_pp_type": "Propiedá:",
+ "smw_pp_submit": "Alcontrar resultaos",
+ "smw_result_prev": "Anterior",
+ "smw_result_next": "Siguiente",
+ "smw_result_results": "Resultaos",
+ "smw_result_noresults": "Nun hai resultaos.",
+ "smwadmin": "Funciones alministratives y de caltenimientu",
+ "smw-admin-statistics-job-title": "Estadístiques de trabayos",
+ "smw-admin-statistics-job-docu": "Les estadístiques de trabayu amuesen información de los trabayos programaos de Semantic MediaWiki qu'entá nun s'executaron. El númberu de trabayos puede ser llixeramente inexautu o contener intentos fallíos, consulta esti [https://www.mediawiki.org/wiki/Manual:Job_queue manual] pa información detallada.",
+ "smw-admin-statistics-querycache-title": "Estadístiques de la caché de consultes",
+ "smw-admin-statistics-querycache-disabled": "La [https://www.semantic-mediawiki.org/wiki/QueryCache Caché de consultes] nun s'activó nesta wiki y, darréu, nun hai estadístiques disponibles.",
+ "smw-admin-statistics-querycache-explain": "Les estadístiques de cache van contener datos acumulaos y derivaos provisionales incluyendo:\n* \"misses\" como la cantidá total d'intentos de llograr datos de la cache ensin respuesta, forzando un llogru direutu del repositoriu (DB, triple-store etc.)\n* \"deletes\" como la cantidá total d'operaciones de vaciáu de cache (bien al traviés d'una purga o una dependencia de consulta)\n* \"hits\" contien la cantidá de llogros de cache dende fontes incrustaes (consultes dende una páxina de la wiki) o nun incrustaes (en casu de tar activaes, pidíes por páxines como Special:Ask o la API).\n* \"medianRetrievalResponseTime\" ye un valor orientativu sobre'l tiempu de respuesta mediu (en segundos) pa pidimientos en cache y fora de cache a lo llargo del periodu de recoyía.\n* \"noCache\" indica la cantidá de pidimientos ensin intentu de llograr resultancies de la cache (consultes con limit=0, opción de 'non-cache')",
+ "smw-admin-permission-missing": "Bloquióse l'accesu a esta páxina por falta de permisos; consulta la páxina d'ayuda sobro [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] pa llograr detalles de la configuración necesaria.",
+ "smw-admin-setupsuccess": "Configuróse'l motor d'almacenamientu.",
+ "smw_smwadmin_return": "Tornar a $1.",
+ "smw_smwadmin_updatestarted": "Anicióse un nuevu procesu d'actualización pa refrescar los datos semánticos.\nTolos datos almacenaos se reconstruirán o repararán onde faiga falta.\nPues siguir el procesu d'actualización nesta páxina especial.",
+ "smw_smwadmin_updatenotstarted": "Yá ta executando un procesu d'actualización.\nNun se creará otru.",
+ "smw_smwadmin_updatestopped": "Pararon tolos procesos d'actualización esistentes.",
+ "smw_smwadmin_updatenotstopped": "Pa parar el procesu d'actualización n'execución, tienes d'activar el cuadru de marca pa indicar que daveres tas seguru.",
+ "smw-admin-docu": "Esta páxina especial ayudaráte demientres la instalación, anovamientu, mantenimientu y usu de <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> y ufre tamién otres funciones y xeres alministratives, lo mesmo qu'estadístiques.\nRecuerda facer copia de seguridá de los datos importantes antes d'executar funciones alministratives.",
+ "smw-admin-environment": "Entornu de software",
+ "smw-admin-db": "Caltenimientu de la base de datos",
+ "smw-admin-db-preparation": "Ta en cursu l'aniciu de la tabla y puede tardar un tiempu ata que s'amuesen les resultancies, dependiendo del tamañu de la tabla y de posibles optimizaciones de la mesma.",
+ "smw-admin-dbdocu": "Semantic MediaWiki rique dalgunes estensiones de la base de datos de MediaWiki pa poder almacenar los datos semánticos.\nLa función d'abaxo asegura que la base de datos ta correutamente configurada.\nLos cambios fechos nesti pasu nun afeuten al restu de la base de datos de MediaWiki, y puen desfacese fácilmente si se quier.\nEsta función de configuración pue executase múltiples veces ensin peligru, pero namái ye necesaria una vez na instalación o anovamientu.",
+ "smw-admin-permissionswarn": "Si la operación falla con errores SQL, probablemente l'usuariu de la base de datos qu'emplega la wiki (comprueba'l ficheru LocalSettings.php) nun tien permisos bastantes.\nO bien da-y a esti usuariu más permisos pa crear y desaniciar tables, o pon temporalmente los datos de conexón del usuariu root de la base de datos nel ficheru LocalSettings.php, o usa'l script de mantenimientu <code>setupStore.php</code>, que puede usar les credenciales d'un alministrador.",
+ "smw-admin-dbbutton": "Aniciar o anovar les tables",
+ "smw-admin-announce": "Anuncia la to wiki",
+ "smw-admin-announce-text": "Si la to wiki ye pública, puedes rexistrala en <a href=\"https://wikiapiary.com\">WikiApiary</a>, la wiki de siguimientu wikis.",
+ "smw-admin-deprecation-notice-title": "Avisos d'obsolescencia",
+ "smw-admin-deprecation-notice-docu": "La siguiente sección contien axustes que se quedaron anticuaos o se desaniciaron, pero que detectóse que tán activos nesta wiki. Espérase que cualquier versión futura desanicie l'encontu d'estes configuraciones.",
+ "smw-admin-deprecation-notice-config-notice": "[https://www.semantic-mediawiki.org/wiki/Help:$1 $1] ta anticuáu y va desaniciase na versión $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> desaniciará (o reemplazará) {{PLURAL:$2|la opción siguiente|les opciones siguientes}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> ta anticuáu y va desaniciase'n $2",
+ "smw-admin-deprecation-notice-config-replacement": "[https://www.semantic-mediawiki.org/wiki/Help:$1 $1] sustituyóse por [https://www.semantic-mediawiki.org/wiki/Help:$2 $2]",
+ "smw-admin-deprecation-notice-config-replacement-option": "{{PLURAL:$2|Opción|Opciones}} de <code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> ta trocándose por <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "[https://www.semantic-mediawiki.org/wiki/Help:$1 $1] desanicióse na versión $2",
+ "smw-admin-deprecation-notice-title-notice": "Próximos cambios",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Detectóse qu'esta wiki usa les siguientes configuraciones que ta previsto desaniciar o camudar nuna versión futura.",
+ "smw-admin-deprecation-notice-title-replacement": "Opciones sustituyíes o cambiaes de nome",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "La sección siguiente contien opciones que cambiaron el nome o recibieron otros cambeos. Encaméntase anovar darréu'l so nome o formatu.",
+ "smw-admin-deprecation-notice-title-removal": "Opciones desaniciaes",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Les opciones siguientes desaniciáronse nuna versión anterior, pero detectóse qu'entá s'utilicen nesta wiki.",
+ "smw-smwadmin-refresh-title": "Reparación y anovamientu de los datos",
+ "smw_smwadmin_datarefresh": "Reconstrucción de los datos",
+ "smw_smwadmin_datarefreshdocu": "Ye posible restaurar tolos datos de Semantic MediaWiki basandose nel conteníu actual de la wiki.\nEsto pue ser afayadizo pa reparar datos rotos o pa refrescar os datos si'l formatu internu cambió por un anovamientu del software.\nEsti anovamientu executase páxina a páxina y nun se completará nel intre.\nLo siguiente amuesa si un anovamientu ta en cursu y permiti-y principialos o detenelos (esceuto si esta carauterística ta desactivada pol alministrador del sitiu).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Yá hai una anovamientu en cursu.</strong>\nYe normal que l'anovamientu avance sólo de mou lentu, porque sólo refresca los datos en pequeños trozos cada vez qu'un usuariu entra na wiki.\nP'acabar esti anovamientu más rápido, pue llamar al script de mantenimientu de MediaWiki <code>runJobs.php</code> (use la opción <code>--maxjobs 1000</code> para restrinxir el númberu d'anovamientos fechos nun llote).\nProgresu estimáu del anovamientu actual:",
+ "smw_smwadmin_datarefreshbutton": "Programar la reconstrucción de los datos",
+ "smw_smwadmin_datarefreshstop": "Detener esti anovamientu",
+ "smw_smwadmin_datarefreshstopconfirm": "Sí, toi {{GENDER:$1|seguru|segura}}.",
+ "smw-admin-job-scheduler-note": "La mayoría de les actividaes d'esta seición fácense como trabayu pa evitar situaciones de bloquéu demientres la execución. El [https://www.mediawiki.org/wiki/Manual:Job_queue programador de xeres] ye'l responsable del procesamientu, y ye crítico que'l script de caltenimientu <code>runJobs.php</code> (ver tamién el parámetru de configuración $<code>wgRunJobsAsync</code>) tenga una capacidá afayadiza.",
+ "smw-admin-outdateddisposal-title": "Desaniciu d'entidaes anticuaes",
+ "smw-admin-outdateddisposal-intro": "Delles actividaes (un cambéu a un tipu de propiedá, el desaniciu de páxines wiki o la correición de valores d'error) van producir [https://www.semantic-mediawiki.org/wiki/Outdated_entities entidaes anticuaes] y suxerse desaniciales dacuando pa lliberar l'espaciu de tables venceyáu.",
+ "smw-admin-outdateddisposal-active": "Programóse un trabayu de desaniciu d'entidaes anticuaes.",
+ "smw-admin-outdateddisposal-button": "Programar un desaniciu",
+ "smw-admin-feature-disabled": "Esta carauterística ta desactivada nesta wiki, pa más información, consulta la páxina d'ayuda de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">configuración</a> o comunícate col alministrador del sistema.",
+ "smw-admin-propertystatistics-title": "Reconstruir les estadístiques de la propiedá",
+ "smw-admin-propertystatistics-intro": "Reconstruye toles estadístiques d'usu de la propiedá y actualiza y corrixe la [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count cuenta d'usu] de propiedaes.",
+ "smw-admin-propertystatistics-active": "Programóse un trabayu de reconstrucción d'estadístiques de propiedaes.",
+ "smw-admin-propertystatistics-button": "Programar reconstrucción d'estadístiques",
+ "smw-admin-fulltext-title": "Reconstrucción de gueta de testu completu",
+ "smw-admin-fulltext-intro": "Reconstruye l'índiz de gueta dende les tables de propiedá con un tipu de datu activáu [https://@www.semantic-mediawiki.org/wiki/full-text gueta de testu completu]. Los cambeos nes normes d'índices (cambios nes palabres saltaes, nuevu algoritmu de radicales etc.) y/o una tabla nueva añadida o camudada riquen repetir esta xera.",
+ "smw-admin-fulltext-active": "Programóse un trabayu de reconstrucción de guetes de testu completu.",
+ "smw-admin-fulltext-button": "Programar reconstrucción de testu completu",
+ "smw-admin-support": "Algamar asistencia",
+ "smw-admin-supportdocu": "Úfrense dellos recursos p'ayudate si tienes problemes:",
+ "smw-admin-installfile": "Si alcuentres problemes cola instalación, principia revisando les indicaciones del <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">ficheru INSTALL</a> y la <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">páxina d'instalación</a>.",
+ "smw-admin-smwhomepage": "La documentación d'usuariu completa de Semantic MediaWiki ta en <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Puedes informar de los errores nel <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">siguimientu de problemes</a>, la páxina de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">reporte de fallos</a> ufre dellos conseyos pa escribir un reporte de fallos eficaz.",
+ "smw-admin-questions": "Si tienes más preguntes o suxerencies, xúntate a la conversación de la <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">llista de corréu d'usuarios</a> de Semantic MediaWiki o na <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">sala del chat</a>.",
+ "smw-admin-other-functions": "Otres funciones",
+ "smw-admin-supplementary-section-title": "Funciones suplementaries",
+ "smw-admin-supplementary-section-subtitle": "Funciones disponibles",
+ "smw-admin-supplementary-section-intro": "Esta sección apurre otres funciones más allá del ámbitu del caltenimientu y ye posible que delles funciones qu'apaecen na [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentación] tean torgaes o nun tean disponibles y polo tanto sin accesu nesta wiki.",
+ "smw-admin-supplementary-settings-title": "Axustes de configuración",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> xenera una llista coleutiva de configuraciones que s'usen na MediaWiki Semántica",
+ "smw-admin-supplementary-operational-statistics-title": "Estadístiques operatives",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> amuesa un conxuntu enantáu d'estadístiques",
+ "smw-admin-supplementary-idlookup-title": "Busca y desaniciu d'entidá",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contien funciones pa buscar y desaniciar entidaes individuales",
+ "smw-admin-supplementary-duplookup-title": "Entidaes duplicaes",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> pa una llista d'entraes que tán na categoría de les que contienen duplicaos na tabla d'entidaes",
+ "smw-admin-supplementary-duplookup-docu": "Esta páxina amuesa la llista d'entraes de la [https://www.semantic-mediawiki.org/wiki/Help:Entity_table tabla d'entidaes] que punxeron na categoría de duplicaes. Les entraes duplicaes solo tendríen d'asoceder (como muncho) en rares ocasiones potencialmente causaes por un procesu termináu mientres una actualización de la base de datos o una transaición de reversión fallida.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estadístiques de la caché",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> amuesa estadístiques rellacionaes cola caché",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informa sobro les preferencies y les estadístiques d'índices",
+ "smw-admin-supplementary-elastic-docu": "Esta páxina contien información sobro axustes, mapeos, salú y estadístiques d'índices rellacionaos con un cluster d'Elasticsearch que ta conectáu a Semantic MediaWiki ya'l so [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Funciones disponibles",
+ "smw-admin-supplementary-elastic-settings-title": "Configuración",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> usáu por Elasticsearch p'alministrar los índices de MediaWiki Semántica",
+ "smw-admin-supplementary-elastic-mappings-title": "Asignaciones",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> pa la llista d'índices y asihnaciones de campos",
+ "smw-admin-supplementary-elastic-mappings-docu": "Esta páxina contien detalles de la correspondencia de campos porque s'utilicen colos índices actuales. El resume de correspondencies tendría de controlase en conexón col <code>index.mapping.total_fields.limit</code> qu'especifica'l númberu máximu de campos nun índiz.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resume",
+ "smw-admin-supplementary-elastic-mappings-fields": "Correspondencies de campos",
+ "smw-admin-supplementary-elastic-nodes-title": "Nuedos",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> amuesa les de estadístiques de nuedos",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> ufre un resume de los índices disponibles y les sos estadístiques",
+ "smw-admin-supplementary-elastic-statistics-title": "Estadístiques",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> amuesa les estadístiques a nivel d'índiz",
+ "smw-admin-supplementary-elastic-statistics-docu": "Esta páxina apurre una visión de les estadístiques de los índices de les distintes operaciones que tán asocediendo a nivel d'índiz, les estadístiques devueltes combínense coles primaries y les totales. La [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html páxina d'ayuda] contien una descripción detallada de les estadístiques d'índices disponibles.",
+ "smw-admin-supplementary-elastic-status-replication": "Estáu de replicación",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Última replicación activa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalu d'enfrescáu: $1",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replicación bloquiada: $1 (en procesu de reconstrucción)",
+ "smw-list-count": "La llista contien $1{{PLURAL:$1|entrada|entraes}}.",
+ "smw-list-count-from-cache": "La llista contien $1 {{PLURAL:$1|entrada|entraes}} y recuperóse de la caché (UTC: $2).",
+ "smw-property-label-uniqueness": "La etiqueta «$1» casa polo menos con otra representación de propiedá. Consulta la [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness páxina d'ayuda] pa saber cómo iguar esti problema.",
+ "smw-property-label-similarity-title": "Informe de semeyanza na etiqueta Propiedá",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula la semeyanza de les etiquetes de propiedá qu'esisten",
+ "smw-property-label-similarity-threshold": "Estragal:",
+ "smw-property-label-similarity-type": "Amosar l'identificador de tipu",
+ "smw-property-label-similarity-noresult": "Nun s'atoparon resultancies pa les opciones escoyíes.",
+ "smw-property-label-similarity-docu": "Compara y informa de la [https://www.semantic-mediawiki.org/wiki/Property_similarity semeyanza sintáutica] (non la semeyanza semántica) ente dos etiquetes de propiedá, lo que puede ayudar a filtriar propiedaes mal escrites o equivalientes que representen el mesmu conceutu (visita la páxina especial [[Special:Properties|Propiedaes]] p'aclarar los conceutos y los usos de les propiedaes del informe). Puede afaese l'estragal p'aumentar o menguar la distancia de semeyanza. <code>[[Property:$1|$1]]</code> utilízase pa quitar propiedaes del analís.",
+ "smw-admin-operational-statistics": "Esta páxina contien estadístiques d'operación recoyíes en o dende funciones rellacionaes cola MediaWiki Semántica. Puedes alcontrar [[Special:Statistics|<b>equí</b>]] una llista enantada d'estadístiques específiques de la wiki.",
+ "smw_adminlinks_datastructure": "Estructura de datos",
+ "smw_adminlinks_displayingdata": "Visualización de los datos",
+ "smw_adminlinks_inlinequerieshelp": "Ayuda de les consultes en llinia",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Númberu d'usos] albidráu: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propiedá definida pol {{PLURAL:$1|usuariu|sistema}}",
+ "smw-property-indicator-last-count-update": "Cuenta estimada d'usu\nAnovada por última vegada: $1",
+ "smw-concept-indicator-cache-update": "Cuenta de caché\nÚltima actualización: $1",
+ "smw-createproperty-isproperty": "Ye una propiedá de tipu $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|El valor permitíu|Los valores permitíos}} pa esta propiedá {{PLURAL:$1|ye|son}}:",
+ "smw-paramdesc-category-delim": "El delimitador",
+ "smw-paramdesc-category-template": "Una plantía cola que dar formatu a los elementos",
+ "smw-paramdesc-category-userparam": "Un parámetru pa pasar a la plantía",
+ "smw-info-par-message": "Mensaxe a amosar.",
+ "smw-info-par-icon": "Iconu a amosar; o \"info\" o bien \"warning\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Opciones xenerales",
+ "prefs-ask-options": "Especial:Opciones d'entrugues",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (y les estensiones rellacionaes) ufren una personalización individual pa delles funciones seleicionaes. Consulta la [https://www.semantic-mediawiki.org/wiki/Help:User_preferences páxina d'ayuda] pa ver una descripción detallada.",
+ "smw-prefs-ask-options-tooltip-display": "Amosar el testu del parámetru como información d'ayuda",
+ "smw-prefs-ask-options-compact-view-basic": "Activar vista compacta básica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Si s'activa, amuesa un conxuntu amenorgáu d'enllaces na vista compacta de Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Activar la correición horaria pa les páxines especiales usando la configuración llocal d'[[Special:Preferences#mw-prefsection-rendering|estaya horaria]]",
+ "smw-prefs-general-options-jobqueue-watchlist": "Amosar la llista de siguimientu de la cola de xeres na mio barra personal",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Si s'activa, amuesa una [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist llista de] xeres escoyíes pendientes xunto colos tamaños de cola albidraos.",
+ "smw-prefs-general-options-disable-editpage-info": "Desactivar el testu d'introducción de la páxina d'edición",
+ "smw-prefs-general-options-disable-search-info": "Desactivar la información d'ayuda de sintaxis na páxina estándar de busques",
+ "smw-prefs-general-options-suggester-textinput": "Activar l'ayuda d'entrada pa les entidaes semántiques",
+ "smw-ui-tooltip-title-property": "Propiedá",
+ "smw-ui-tooltip-title-quantity": "Conversión d'unidaes",
+ "smw-ui-tooltip-title-info": "Información",
+ "smw-ui-tooltip-title-service": "Enllaces de serviciu",
+ "smw-ui-tooltip-title-warning": "Avisu",
+ "smw-ui-tooltip-title-error": "Error",
+ "smw-ui-tooltip-title-parameter": "Parámetru",
+ "smw-ui-tooltip-title-event": "Actividá",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Lleenda",
+ "smw-ui-tooltip-title-reference": "Referencia",
+ "smw_unknowntype": "El tipu «$1» d'esta propiedá ye inválidu",
+ "smw-concept-cache-text": "El conceutu tien un total de $1 {{PLURAL:$1|páxina|páxines}}, y l'últimu anovamientu foi el $3 a les $2.",
+ "smw_concept_header": "Páxines del conceptu \"$1\"",
+ "smw_conceptarticlecount": "Abaxo {{PLURAL:$1|s'amuesa $1 páxina|s'amuesen $1 páxines}}.",
+ "smw-qp-empty-data": "Los datos solicitaos nun se pudieron amosar por unos criterios de seleición insuficientes.",
+ "right-smw-admin": "Entrar nes xeres alministratives (Semantic MediaWiki)",
+ "right-smw-patternedit": "Accesu d'edición pa caltener espresiones regulares y patrones permitíos (Semantic MediaWiki)",
+ "right-smw-pageedit": "Accesu d'edición pa páxines con anotación <code>is edit protected</code> (MediaWiki Semántica)",
+ "right-smw-ruleedit": "Editar les páxines de regles (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "protexida (sólo usuarios permitíos)",
+ "action-smw-patternedit": "editar les espresiones regulares qu'usa Semantic MediaWiki",
+ "action-smw-pageedit": "editar páxines cola anotación <code>is edit protected</code> (MediaWiki Semántica)",
+ "group-smwadministrator": "Alministradores (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|alministrador|alministradora}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Alministradores (Semantic MediaWiki)",
+ "group-smwcurator": "Conservadores (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|conservador|conservadora}} (Semantic MediaWiki)",
+ "grouppage-smwcurator": "{{ns:project}}:Conservadores (Semantic MediaWiki)",
+ "action-smw-admin": "entrar nes xeres alministratives de Semantic MediaWiki",
+ "action-smw-ruleedit": "editar les páxines de regles (Semantic MediaWiki)",
+ "smw-property-predefined-default": "«$1» ye una propiedá predefinida.",
+ "smw-property-predefined-common": "Esta propiedá ta pre-desplegada (tamién conocida como [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedá especial]) y vien con otros privilexos alministrativos más, pero puede usase como cualquier otra [https://www.semantic-mediawiki.org/wiki/Property propiedá definida pol usuariu].",
+ "smw-property-predefined-ask": "«$1» ye una propiedá predefinida que representa meta-información (en forma de [https://www.semantic-mediawiki.org/wiki/Subobject suboxetu]) sobro consultes individuales y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "«$1» ye una propiedá predefinida que recueye'l númberu de condiciones utilizaes n'una consulta y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "«$1» ye una propiedá predefinida qu'informa sobre la fondura d'una consulta, y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "Ye un valor numbéricu calculáu en base al añeramientu de subconsultes, cadenes de propiedaes, y elementos de descripción disponibles cola execución d'una consulta torgada pol parámetru de configuración <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "«$1» ye una propiedá predefinida, que describe parámetros qu'inflúen na resultancia d'una consulta, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askpa": "Ye parte d'una colección de\npropiedaes qu'especifiquen un [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Esta páxina amuesa les [https://www.semantic-mediawiki.org/wiki/Property propiedaes] y la cuenta d'usos disponible pa esta Wiki. Pa unes estadístiques actualizaes de cuentes recomiéndase executar el script de mantenimientu d'[https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics estadístiques de propiedá] de mou regular . Pa una vista diferenciada, mira les páxines especiales [[Special:UnusedProperties|propiedaes ensin usu]] o [[Special:WantedProperties|propiedaes buscaes]].",
+ "smw-sp-properties-cache-info": "Los datos de la llista recuperaronse de la [https://www.semantic-mediawiki.org/wiki/Caching cache], y anovaronse por última vez el $1.",
+ "smw-sp-properties-header-label": "Llista de propiedaes",
+ "smw-admin-settings-docu": "Amuesa la llista de tolos axustes predeterminaos y llocalizaos que tan rellacionaos col entornu de MediaWiki Semántica. Pa los detalles de los axustes individuales, consulta la páxina d'ayuda de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-sp-admin-settings-button": "Xenerar la llista d'axustes",
+ "smw-admin-idlookup-title": "Gueta",
+ "smw-admin-idlookup-docu": "Esta seición amuesa detalles téunicos sobro una entidá individual (páxina wiki, suboxetu, propiedá, etc.) en Semantic MediaWiki. La entrada puede ser un númberu ID o un valor de cadena que case col campu de gueta relevante, anque cualquier referencia ID ta rellacionada con Semantic MediaWiki y non col ID de páxina o de revisión de MediaWiki.",
+ "smw-admin-iddispose-title": "Desaniciu",
+ "smw-admin-iddispose-docu": "Repara en que la operación de desaniciu nun tien torga y desaniciará la entidá del motor d'almacenamientu xunto con toles referencies en tables pendientes, si se confirma. Fai esta xera con '''procuru''' y sólo después de consultar la [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentación].",
+ "smw-admin-iddispose-done": "Desanicióse la ID «$1» del motor d'almacenamientu.",
+ "smw-admin-iddispose-references": "ID «$1» {{PLURAL:$2|nun tien|tien polo menos}} una referencia activa:",
+ "smw-admin-iddispose-references-multiple": "Llista de correspondencies con polo menos un rexistru de referencia activu.",
+ "smw-admin-iddispose-no-references": "La gueta nun atopó «$1» en nenguna entrada de tabla.",
+ "smw-admin-idlookup-input": "Gueta:",
+ "smw-admin-objectid": "Identificador:",
+ "smw-admin-tab-general": "Supervisión",
+ "smw-admin-tab-notices": "Avisos de desusu",
+ "smw-admin-tab-supplement": "Funciones suplementaries",
+ "smw-admin-tab-registry": "Rexistru",
+ "smw-livepreview-loading": "Cargando...",
+ "smw-sp-searchbyproperty-description": "Esta páxina apurre una [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interfaz de navegación] simple p'atopar entidaes descrites por una propiedá y un valor con nome. Otres interfaces de busca disponibles incluyen la [[Special:PageProperty|busca de propiedaes de páxina]] y el [[Special:Ask|constructor de consultes]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Llista de resultaos",
+ "smw-sp-searchbyproperty-nonvaluequery": "Una llista de valores que tienen atribuída la propiedá «$1».",
+ "smw-sp-searchbyproperty-valuequery": "Una llista de páxines que tienen la propiedá \"$1\" col valor \"$2\" anotáu.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" nun puede atribuise a un tipu de númberu declaráu con valor $2.",
+ "smw-datavalue-number-nullnotallowed": "«$1» devolvió un \"NULL\" que nun se permite como númberu.",
+ "smw-editpage-annotation-enabled": "Esta páxina almite anotaciones semántiques nel testu (p. ex. <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) pa construir conteníu estructuráu y consultable facilitáu por Semantic MediaWiki. Pa una descripción completa sobre cómo usar anotaciones o la función analizadora #ask, consulta les páxines d'ayuda [https://www.semantic-mediawiki.org/wiki/Help:Getting_started primeros pasos], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation anotaciones nel testu] o [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries consultes en llinia].",
+ "smw-editpage-annotation-disabled": "Esta páxina nun ta activada pa les anotaciones semántiques nel testu por torgues nel espaciu de nomes. Los detalles de como activar l'espaciu de nomes pueden alcontrase na páxina d'ayuda de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-editpage-property-annotation-enabled": "Esta propiedá puede estendese usando anotaciones semántiques pa especificar un tipu de datu (p. ex. <nowiki>\"[[Has type::Page]]\"</nowiki>) o otres declaraciones d'asistencia (p. ex. <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). Pa saber cómo aumentar esta páxina, consulta les páxines d'ayuda [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaración d'una propiedá] o la [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes llista de los tipos de datos disponibles].",
+ "smw-editpage-property-annotation-disabled": "Esta propiedá nun puede estendese con una anotación de tipu de datu (p. ex. <nowiki>\"[[Has type::Page]]\"</nowiki>) porque yá ta predefinida (pa más información, consulta la páxina d'ayuda [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedaes especiales]).",
+ "smw-editpage-concept-annotation-enabled": "Esti conceptu puede ampliase usando la función d'analís #concept. Pa una descripción de como usar #concept, consulta la páxina d'ayuda [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceptos].",
+ "smw-search-syntax-support": "La entrada de la gueta permite usar la [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search sintaxis de consulta] semántica p'atopar resultancies al usar Semantic MediaWiki.",
+ "smw-search-input-assistance": "El [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistente d'entrada] tamién ta activáu pa facilitar la pre-selección de les propiedaes y categoríes disponibles.",
+ "smw-search-input": "Entrada y gueta",
+ "smw-search-syntax": "Sintaxis",
+ "smw-search-profile": "Estendida",
+ "smw-search-profile-tooltip": "Funciones de gueta en conexón con Semantic MediaWiki",
+ "smw-search-profile-sort-recent": "Más nuevu",
+ "smw-search-profile-sort-title": "Títulu",
+ "smw-search-profile-extended-help-query": "Utilizóse <code><nowiki>$1</nowiki></code> como consulta.",
+ "smw-search-profile-extended-help-find-forms": "formes disponibles",
+ "smw-search-profile-extended-section-sort": "Ordenar por",
+ "smw-search-profile-extended-section-form": "Formes",
+ "smw-search-profile-extended-section-namespace": "Espaciu de nomes",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "log-name-smw": "Rexistru de MediaWiki Semántica",
+ "log-show-hide-smw": "$1 el rexistru de MediaWiki Semántica",
+ "logeventslist-smw-log": "Rexistru de MediaWiki Semántica",
+ "log-description-smw": "Actividaes de los [https://www.semantic-mediawiki.org/wiki/Help:Logging tipos d'actividá activaos] de los qu'informaron MediaWiki Semántica y los sos componentes.",
+ "logentry-smw-maintenance": "Actividaes rellacionaes col mantenimientu emitíes por MediaWiki Semántica",
+ "smw-datavalue-import-unknown-namespace": "L'espaciu de nomes d'importación «$1» ye desconocíu. Comprueba que los detalles d'importación de OWL tean disponibles al traviés de [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "Nun pudo alcontrase la URI del espaciu de nomes «$1» na [[MediaWiki:Smw import $1|importación de $1]].",
+ "smw-datavalue-import-missing-type": "Nun s'alcontró la definición de tipu pa «$1» na [[MediaWiki:Smw import $2|importación de $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Importación de $1]]",
+ "smw-datavalue-import-invalid-value": "«$1» nun ye un formatu válidu, habría de tener el formatu «espaciu de nomes»:«identificador» (p.ex. «foaf:nome»).",
+ "smw-datavalue-import-invalid-format": "Esperábase que la cadena «$1» tuviera xebrada en cuatro partes, pero nun s'entendió'l so formatu.",
+ "smw-property-predefined-impo": "«$1» ye una propiedá predefinida que describe la rellación con un [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulariu importáu] y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "«$1» ye una propiedá predefinida que describe'l [[Special:Types|tipu de datu]] d'una propiedá y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "«$1» ye una propiedá predefinida que representa una construcción [https://www.semantic-mediawiki.org/wiki/Help:Container contenedor] y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "El contenedor permite acumular pares propiedá-valor asemeyaes a les d'una páxina wiki normal pero dientro d'un espaciu d'entidaes distintu mentanto ta enllazáu al tema que lu incluye.",
+ "smw-property-predefined-errp": "«$1» ye una propiedá predefinida pa siguir los errores d'entrada por anotaciones de valores irregulares, y ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-errp": "Na mayoría de los casos ta provocao porque nun casen los tipos o pola torga d'un [[Property:Allows value|valor]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value «$1»] ye una propiedá predefinida que puede definir una llista de valores posibles pa torgar designaciones de valores pa una propiedá y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list «$1»] ye una propiedá predefinida que puede especificar una referencia a una llista que contien valores permitíos pa torgar designaciones de valores para una propiedá, y que ta proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-datavalue-property-restricted-annotation-use": "La propiedá «$1» tien un campu d'aplicación llindáu y nun puede utilizase como propiedá d'anotación polos usuarios.",
+ "smw-datavalue-property-restricted-declarative-use": "La propiedá «$1» ye declarativa y solo puede utilizase nes páxines de propiedá o de categoría.",
+ "smw-datavalue-property-create-restriction": "Nun esiste la propiedá «$1» y l'usuariu nun tien el permisu «$2» (consulta [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode mou d'autoridá]) pa crear o anotar valores con una propiedá nun aprobada.",
+ "smw-datavalue-property-invalid-character": "«$1» contien el caráuter «$2» d'una llista como parte de la etiqueta de propiedá y, poro, clasificóse como inválidu.",
+ "smw-datavalue-property-invalid-chain": "Nun se permite usar «$1» como cadena de propiedaes mientres el procesu d'anotación.",
+ "smw-datavalue-restricted-use": "Esti valor de datos marcóse pa usu acutáu. $1",
+ "smw-datavalue-invalid-number": "Nun puede interpretase «$1» como númberu.",
+ "smw-query-condition-circular": "Detectóse una posible condición circular en «$1».",
+ "smw-types-list": "Llista de tipos de datos",
+ "smw-types-default": "«$1» ye un tipu de datos integráu.",
+ "smw-types-help": "Hai más información y exemplos na [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 páxina d'ayuda].",
+ "smw-type-anu": "«$1» ye una variante del tipu de datos [[Special:Types/URL|URL]] que s'usa principalmente pa la declaración d'esportación de ''owl:AnnotationProperty''.",
+ "smw-type-boo": "«$1» ye un tipu de datu primitivu pa describir un valor verdadero/falso.",
+ "smw-type-cod": "«$1» ye una variante del tipu de datu [[Special:Types/Text|Testu]] pa usar en testos técnicos de llonxitú variable, como llistaos de códigu fonte.",
+ "smw-type-geo": "«$1» ye un tipu de datu que describe llugares xeográficos y rique la [https://www.semantic-mediawiki.org/wiki/Extension:Maps estensión «Mapes»].",
+ "smw-type-tel": "«$1» ye un tipu de datos especial pa describir númberos de teléfonu internacionales acordies con RFC 3966.",
+ "smw-type-txt": "«$1» ye un tipu de datos primitivu pa describir cadenes de testu de llonxitú arbitraria.",
+ "smw-type-dat": "«$1» ye un tipu de datos pa representar puntos nel tiempu nun formatu unificáu.",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-property-predefined-errc": "«$1» ye una propiedá predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] que representa errores qu'apaecieron venceyaos a anotaciones de valores o procesamientu d'entraes inapropiaos.",
+ "smw-property-predefined-long-errc": "Los errores recuéyense nun [https://www.semantic-mediawiki.org/wiki/Help:Container contenedor] que puede incluir una referencia a la propiedá que causó'l desacuerdu.",
+ "smw-property-predefined-errt": "«$1» ye una propiedá predefinida que contien el testu de descripción d'un error y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "Un suboxetu definíu pol usuariu tenía un esquema de nomes inválidu. Un nome con un puntu ($1) usáu nos cinco primeros caráuteres ta acutáu pa les estensiones. Puedes definir un [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador con nome].",
+ "smw-datavalue-record-invalid-property-declaration": "La definición del rexistru contien la propiedá «$1» declarada ella mesma como de tipu rexistru, lo que nun ta permitío.",
+ "smw-property-predefined-mdat": "«$1» ye una propiedá predefinida que correspuende cola data del últimu cambiu d'un tema y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "«$1» ye una propiedá predefinida que correspuende cola data de la primera revisión d'un tema y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "«$1» ye una propiedá predefinida qu'indica si un tema ye nuevu o non y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "«$1» ye una propiedá predefinida que contien el nome de páxina del usuariu que creó la última revisión y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "«$1» ye una propiedá predefinida que describe'l tipu MIME d'un ficheru xubíu y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "«$1» ye una propiedá predefinida que describe'l tipu de multimedia d'un ficheru xubíu y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "«$1» ye una propiedá predefinida que contien el nome del formatu del resultáu usáu n'una consulta y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "«$1» ye una propiedá predefinida que describe les condiciones d'una consulta como cadena y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "«$1» ye una propiedá predefinida que contien un valor de tiempu (en segundos) que la consulta necesitó pa completar la execución y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksc": "«$1» ye una propiedá predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], qu'identifica fontes alternatives de consultes (por casu, fontes remotes o federaes).",
+ "smw-property-predefined-prec": "\"$1\" ye una propiedá predefinida que describe una [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisión de representación] (en cifres decimales) pa tipos de datos numbéricos.",
+ "smw-types-extra-geo-not-available": "Nun se detectó la [https://www.semantic-mediawiki.org/wiki/Extension:Maps estensión «Mapes»] y, darréu, «$1» tien torgada la capacidá d'operar.",
+ "smw-datavalue-monolingual-dataitem-missing": "Falta un elementu esperáu pa construir un valor compuestu monollingüe.",
+ "smw-datavalue-languagecode-missing": "L'analizador nun pudo determinar el códigu de llingua (p.ex. \"fulanu@ast\") pa l'anotación «$1».",
+ "smw-datavalue-languagecode-invalid": "Nun se reconoció «$1» como códigu de llingua válidu.",
+ "smw-property-predefined-lcode": "«$1» ye una propiedá predefinida que representa un códigu de llingua en formatu BCP47 y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "«$1» ye un tipu de datos [https://@www.semantic-mediawiki.org/wiki/Help:Container contenedor] qu'asocia un valor de testu con un [[Property:Language code|códigu de llingua]] determináu.",
+ "smw-types-extra-mlt-lcode": "El tipu de datos {{PLURAL:$2|rique|nun rique}} un códigu de llingua (por casu, {{PLURAL:$2|nun s'acepta|s'acepta}} una anotación de valor sin un códigu de llingua).",
+ "smw-property-predefined-text": "«$1» ye una propiedá predefinida que representa testu de cualquier llargor y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "\"$1\" ye una propiedá predefinida que permite describir una propiedá nel contestu d'una llingua, y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "«$1» ye una propiedá predefinida pa definir una llista de propiedaes que s'usen con una propiedá de tipu [[Special:Types/Record|rexistru]] y que ta proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] Tiempu d'analís d'anotaciones nel testu",
+ "smw-limitreport-intext-postproctime": "[SMW] duración del pos-procesamientu",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segundu|segundos}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundu|segundos}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Tiempu d'actualización del almacenamientu (na purga de páxines)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segundu|segundos}}",
+ "smw_allows_pattern": "Espérase qu'esta páxina contenga una llista de referencies (siguía por [https://en.wikipedia.org/wiki/Regular_expression espresiones regulares]) pa que s'ufra pola propiedá [[Property:Allows pattern|Permite patrón]]. Pa editar esta páxina, ríquese'l permisu <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "La espresión regular «$2» clasificó a «$1» como inválida.",
+ "smw-datavalue-allows-pattern-reference-unknown": "La referencia del patrón «$1» nun correspuende a nenguna entrada de [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "La referencia de llista «$1» nun correspuende con una páxina en [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Nel conteníu de la llista «$1» falten elementos con un marcador de llista *.",
+ "smw-datavalue-feature-not-supported": "La función «$1» nun tien encontu o se desactivó nesta wiki.",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» ye una URL inválida.",
+ "smw-datavalue-external-identifier-formatter-missing": "Falta atribuir una [[Property:External formatter uri|\"URI de formateador esternu\"]] a la propiedá.",
+ "smw-datavalue-parse-error": "Nun s'entendió el valor dau «$1».",
+ "smw-datavalue-propertylist-invalid-property-key": "La llista de propiedaes «$1» contenía una clave de propiedá non válida, «$2».",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "L'analizador JSON devolvió «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "Nun puede utilizase «$1» como etiqueta preferida porque la llingua «$2» yá se venceyó a la etiqueta «$3».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copiar l'enllaz al cartafueyu",
+ "smw-data-lookup": "Recuperando los datos...",
+ "smw-data-lookup-with-wait": "La solicitú ta procesándose y puede tardar un momentu.",
+ "smw-no-data-available": "Nun hai datos disponibles.",
+ "smw-property-req-violation-missing-fields": "A la propiedá «$1» fálten-y los detalles de declaración pal tipu «$2» anotáu al nun definir la propiedá <code>Tien campos</code>.",
+ "smw-edit-protection-enabled": "Protexíu contra ediciones (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Esta páxina ta protexida y sólo pueden editala los usuarios que tengan los [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] <code>smw-patternedit</code> afayadizos.",
+ "smw-query-reference-link-label": "Referencia de consulta",
+ "smw-format-datatable-emptytable": "Nun hai datos disponibles na tabla",
+ "smw-format-datatable-info": "Amosando de la _START_ a la _END_ de _TOTAL_ entraes",
+ "smw-format-datatable-infoempty": "Amosando de 0 a 0 de 0 entraes",
+ "smw-format-datatable-infofiltered": "(filtriaes d'un total de _MAX_ entraes)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Amosar _MENU_ entraes",
+ "smw-format-datatable-loadingrecords": "Cargando...",
+ "smw-format-datatable-processing": "Procesando...",
+ "smw-format-datatable-search": "Buscar:",
+ "smw-format-datatable-zerorecords": "Nun s'atoparon rexistros que concasen",
+ "smw-format-datatable-first": "Primer",
+ "smw-format-datatable-last": "Postrer",
+ "smw-format-datatable-next": "Siguiente",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": activar pa ordenar la columna de mou ascendiente",
+ "smw-format-datatable-sortdescending": ": activar pa ordenar la columna de mou descendiente",
+ "smw-format-datatable-toolbar-export": "Esportar",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "La categoría «$1» contien un destín de redireición inválidu a un espaciu de nomes que nun ye de categoría.",
+ "apihelp-ask-summary": "Módulu de la API pa consultar Semantic MediaWiki usando'l llinguaxe Ask.",
+ "apihelp-askargs-summary": "Módulu de la API pa consultar Semantic MediaWiki usando'l llinguaxe Ask como llista de condiciones, visualizaciones y parámetros.",
+ "apihelp-browsebyproperty-summary": "Módulu de la API pa recuperar información sobre dalguna propiedá o llista de propiedaes.",
+ "apihelp-browsebysubject-summary": "Módulu de la API pa recuperar información sobre dalguna tema.",
+ "apihelp-smwtask-summary": "Módulu de la API pa executar xeres rellacionaes con Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Módulu de la API pa encontu de les actividaes de navegación pa distintos tipos d'entidaes de Semantic MediaWiki.",
+ "smw-property-page-list-count": "Amosando $1 {{PLURAL:$1|páxina qu'usa|páxines qu'usen}} esta propiedá.",
+ "smw-property-page-list-search-count": "Amosando $1 {{PLURAL:$1|páxina qu'usa|páxines qu'usen}} esta propiedá con una correspondencia de valor «$2».",
+ "smw-property-reserved-category": "Categoría",
+ "smw-category": "Categoría",
+ "smw-datavalue-uri-invalid-scheme": "Nun s'incluyó «$1» nos esquemes d'URI válidos.",
+ "smw-browse-property-group-title": "Grupu de propiedaes",
+ "smw-browse-property-group-label": "Etiqueta de grupu de propiedaes",
+ "smw-browse-property-group-description": "Descripción de grupu de propiedaes",
+ "smw-section-expand": "Espander la sección",
+ "smw-section-collapse": "Contraer la sección",
+ "smw-ask-format-help-link": "Formatu [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Ayuda",
+ "smw-cheat-sheet": "Chuleta",
+ "smw-personal-jobqueue-watchlist": "Llista de siguimientu de cola de xeres",
+ "smw-property-predefined-label-skey": "Clave d'ordenación",
+ "smw-processing": "Procesando...",
+ "smw-redirect-target-unresolvable": "El destín nun puede resolvese pol motivu «$1»",
+ "smw-types-title": "Tipu: $1",
+ "smw-ask-title-keyword-type": "Busca de palabres clave",
+ "smw-ask-message-keyword-type": "Esta busca casa cola condición <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Nun pudo coneutase col destín remotu «$1».",
+ "smw-remote-source-disabled": "L'orixe '''$1''' desactivó l'encontu de solicitúes remotes."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/avk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/avk.json
new file mode 100644
index 00000000..264a26ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/avk.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Vajas…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/awa.json b/www/wiki/extensions/SemanticMediaWiki/i18n/awa.json
new file mode 100644
index 00000000..4868e6ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/awa.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "1AnuraagPandey"
+ ]
+ },
+ "smw_purge": "रिफà¥à¤°à¥‡à¤¶",
+ "browse": "विकि देखा जाय",
+ "smw_browselink": "खाश जानकारी",
+ "prefs-smw": "शबà¥à¤¦à¤¾à¤°à¥à¤¥à¤—त मीडियाविकी"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/az.json b/www/wiki/extensions/SemanticMediaWiki/i18n/az.json
new file mode 100644
index 00000000..8b490824
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/az.json
@@ -0,0 +1,18 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cekli829",
+ "Wertuose",
+ "Dağlı95"
+ ]
+ },
+ "smw_finallistconjunct": ", vÉ™",
+ "smw_dsv_link": "DSV",
+ "smw_printername_list": "Siyahı",
+ "smw_printername_table": "Cədvəl",
+ "smw_printername_template": "Åžablon",
+ "browse": "Vikiyə baxış",
+ "smw_result_results": "Nəticələr",
+ "smw_smwadmin_mediazilla": "Səhvlər haqda bildirişi <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> saytında yerləşdirmək olar.",
+ "smw-livepreview-loading": "Yüklənir…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/azb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/azb.json
new file mode 100644
index 00000000..f2dc3546
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/azb.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ebrahimi-amir",
+ "Amir a57"
+ ]
+ },
+ "browse": "ویکی یه باخیش",
+ "smw_browselink": "اوزللیکلری گؤزدن کئچیر",
+ "smw-livepreview-loading": "یوکلنیر..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ba.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ba.json
new file mode 100644
index 00000000..24490ea5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ba.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Roustammr",
+ "РуÑтам Ðурыев",
+ "ÐйÑар"
+ ]
+ },
+ "smw_ask_editquery": "Һорауҙы мөхәррирләү",
+ "browse": "Вики күҙәтеү",
+ "smw_browselink": "ҮҙенÑәлектәрҙе ҡарарға",
+ "smw_smwadmin_setupsuccess": "Һаҡлау ÑиÑтемаһы уңышлы ҡуйылды",
+ "smw-livepreview-loading": "Сығарыу..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bar.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bar.json
new file mode 100644
index 00000000..36859d5e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bar.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mucalexx"
+ ]
+ },
+ "smw_purge": "Neich loon",
+ "smw_browselink": "D' Attributt åzoang",
+ "smw-livepreview-loading": "Loon ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bcc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bcc.json
new file mode 100644
index 00000000..eb6cd18b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bcc.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baloch Afghanistan"
+ ]
+ },
+ "smw_printername_category": "تهر",
+ "smw-livepreview-loading": "...بار بیت"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bcl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bcl.json
new file mode 100644
index 00000000..95288aa4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bcl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Geopoet"
+ ]
+ },
+ "smw-paramdesc-table-transpose": "Ipahiling an mga pamayuhan kan lamesa sa paaging patindog asin an mga resulta sa paaging pahigda",
+ "smw-livepreview-loading": "Pigkakarga…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/be-tarask.json b/www/wiki/extensions/SemanticMediaWiki/i18n/be-tarask.json
new file mode 100644
index 00000000..8a13b673
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/be-tarask.json
@@ -0,0 +1,420 @@
+{
+ "@metadata": {
+ "authors": [
+ "EugeneZelenko",
+ "Jim-by",
+ "Red Winged Duck",
+ "Renessaince",
+ "Wizardist",
+ "Zedlik",
+ "ì•„ë¼",
+ "Nerogaf"
+ ]
+ },
+ "smw-desc": "Робіць {{GRAMMAR:вінавальны|{{SITENAME}}}} больш зручнай Ð´Ð»Ñ ÐºÐ°Ð¼Ð¿ÑƒÑ‚Ð°Ñ€Ð°Ñž ''Ñ–'' людзей ([https://www.semantic-mediawiki.org/wiki/Help:User_manual дакумÑнтацыÑ])",
+ "smw-title": "СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ MediaWiki",
+ "smw_viewasrdf": "RDF-крыніца",
+ "smw_finallistconjunct": " Ñ–",
+ "smw-factbox-head": "… болей пра «$1»",
+ "smw-factbox-facts": "Факты",
+ "smw-factbox-facts-derived": "ÐÑ‚Ñ€Ñ‹Ð¼Ð°Ð½Ñ‹Ñ Ñ„Ð°ÐºÑ‚Ñ‹",
+ "smw_isspecprop": "ГÑÑ‚Ð°Ñ ÑžÐ»Ð°ÑьціваÑьць зьÑўлÑецца ÑпÑцыÑльнай у {{GRAMMAR:меÑны|{{SITENAME}}}}.",
+ "smw-concept-cache-header": "ВыкарыÑтаньне кÑшу",
+ "smw-concept-no-cache": "КÑш адÑутнічае.",
+ "smw_concept_description": "ÐпіÑаньне канцÑпцыі «$1»",
+ "smw_no_concept_namespace": "КанцÑпцыі могуць быць Ð²Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ñ‹Ñ Ñ‚Ð¾Ð»ÑŒÐºÑ– на Ñтаронках праÑторы назваў «КанцÑпцыÑ:».",
+ "smw_multiple_concepts": "ÐšÐ¾Ð¶Ð½Ð°Ñ Ñтаронка канцÑпцыі можа мець толькі адно вызначÑньне канцÑпцыі.",
+ "smw_concept_cache_miss": "КанцÑÐ¿Ñ†Ñ‹Ñ Â«$1» Ð½Ñ Ð¼Ð¾Ð¶Ð° быць выкарыÑÑ‚Ð°Ð½Ð°Ñ Ñž гÑÑ‚Ñ‹ момант, таму што ÐºÐ°Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ‹Ñ {{GRAMMAR:родны|{{SITENAME}}}} патрабуе, каб Ñна вылічалаÑÑ Ð°Ñ„-лайн. Калі гÑта праблема Ð½Ñ Ð·ÑŒÐ½Ñ–ÐºÐ½Ðµ празь некаторы чаÑ, запытайце Вашага адмініÑтратара Ñайта ўключыць гÑтую канцÑпцыю.",
+ "smw_noinvannot": "ЗначÑньні Ð½Ñ Ð¼Ð¾Ð¶Ð° быць Ð¿Ñ€Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ñ‹Ñ Ñž Ð°Ð´Ð²Ð°Ñ€Ð¾Ñ‚Ð½Ñ‹Ñ ÑƒÐ»Ð°ÑьціваÑьці.",
+ "version-semantic": "СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ñ‹Ñ Ð¿Ð°ÑˆÑ‹Ñ€Ñньні",
+ "smw_baduri": "СпаÑылкі на форму «$1» не дазволеныÑ.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Падлік вынікаў",
+ "smw_printername_csv": "ÑкÑпарт у фармаце CSV",
+ "smw_printername_dsv": "ЭкÑпарт у фармат DSV",
+ "smw_printername_debug": "Запыт Ð´Ð»Ñ Ð½Ð°Ð»Ð°Ð´ÐºÑ– (Ð´Ð»Ñ ÑкÑпÑртаў)",
+ "smw_printername_embedded": "УÑтаўлÑць зьмеÑÑ‚ Ñтаронак",
+ "smw_printername_json": "ÑкÑпарт у фармаце JSON",
+ "smw_printername_list": "СьпіÑ",
+ "smw_printername_plainlist": "Звычайны ÑьпіÑ",
+ "smw_printername_ol": "Пералік",
+ "smw_printername_ul": "ДÑталізацыÑ",
+ "smw_printername_table": "Табліца",
+ "smw_printername_broadtable": "Ð¨Ñ‹Ñ€Ð¾ÐºÐ°Ñ Ñ‚Ð°Ð±Ð»Ñ–Ñ†Ð°",
+ "smw_printername_template": "Шаблён",
+ "smw_printername_templatefile": "Файл шаблёну",
+ "smw_printername_rdf": "ЭкÑпарт у фармат RDF",
+ "smw_printername_category": "КатÑгорыÑ",
+ "validator-type-class-SMWParamSource": "Ñ‚ÑкÑÑ‚",
+ "smw-paramdesc-limit": "МакÑÑ‹Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð»ÑŒÐºÐ°Ñьць вынікаў Ð´Ð»Ñ Ð²ÑртаньнÑ",
+ "smw-paramdesc-offset": "Ðдхіленьне першага выніку",
+ "smw-paramdesc-headers": "Паказваць назвы загалоўкаў/улаÑьціваÑьцÑÑž",
+ "smw-paramdesc-mainlabel": "Метка Ð´Ð»Ñ Ð½Ð°Ð·Ð²Ñ‹ галоўнай Ñтаронкі",
+ "smw-paramdesc-link": "Паказваць значÑньні Ñк ÑпаÑылкі",
+ "smw-paramdesc-intro": "ТÑкÑÑ‚ Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ перад вынікамі запыту, калі Ñны Ñ‘Ñьць",
+ "smw-paramdesc-outro": "ТÑкÑÑ‚ Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ паÑÑŒÐ»Ñ Ð²Ñ‹Ð½Ñ–ÐºÐ°Ñž запыту, калі Ñны Ñ‘Ñьць",
+ "smw-paramdesc-default": "ТÑкÑÑ‚ Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ Ñž выпадку адÑутнаÑьці вынікаў",
+ "smw-paramdesc-sep": "РазьдзÑлÑльнік вынікаў",
+ "smw-paramdesc-propsep": "Межнік паміж улаÑьціваÑьцÑмі Ñž выніковым запіÑе",
+ "smw-paramdesc-distribution": "ЗамеÑÑ‚ адлюÑÑ‚Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ ÑžÑÑ–Ñ… значÑньнÑÑž падлічыць ужываньні Ñ– паказаць Ñ–Ñ….",
+ "smw-paramdesc-distributionsort": "ÐдÑартаваць разьмеркаваньне значÑньнÑÑž паводле колькаÑьці ўжываньнÑ.",
+ "smw-paramdesc-template": "Ðазва шаблёну, Ñкі будзе выкарыÑтоўвацца Ð´Ð»Ñ Ð²Ñ‹Ð²Ð°Ð´Ñƒ вынікаў",
+ "smw-paramdesc-columns": "КолькаÑьць Ñлупкоў, у Ñкіх будуць паказвацца вынікі",
+ "smw-paramdesc-userparam": "ЗначÑньне, Ñкое перадаецца Ñž кожны выклік шаблёну, калі ён выкарыÑтоўваецца",
+ "smw-paramdesc-class": "Ð”Ð°Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð°Ñ ÐºÐ»ÑÑа CSS, каб задаць ÑÑŒÐ¿Ñ–Ñ Ñ‚Ð°Ð±Ð»Ñ–Ñ†Ñ‹",
+ "smw-paramdesc-introtemplate": "Ðазва шаблёну Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ перад вынікамі запыту, калі Ñны Ñ‘Ñьць",
+ "smw-paramdesc-outrotemplate": "Ðазва шаблёну Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ паÑÑŒÐ»Ñ Ð²Ñ‹Ð½Ñ–ÐºÐ°Ñž запыту, калі Ñны Ñ‘Ñьць",
+ "smw-paramdesc-embedformat": "ТÑг HTML, Ñкі выкарыÑтоўваецца Ð´Ð»Ñ Ð²Ñ‹Ð·Ð½Ð°Ñ‡ÑÐ½ÑŒÐ½Ñ Ð·Ð°Ð³Ð°Ð»Ð¾ÑžÐºÐ°Ñž",
+ "smw-paramdesc-embedonly": "Ðе паказваць загалоўкі",
+ "smw-paramdesc-table-class": "Ð”Ð°Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð°Ñ ÐºÐ»ÑÑа CSS Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ñ–Ñ†Ñ‹",
+ "smw-paramdesc-rdfsyntax": "СынтакÑÑ–Ñ RDF Ð´Ð»Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹ÑтаньнÑ",
+ "smw-paramdesc-csv-sep": "Вызначае разьдзÑлÑльнік Ñлупкоў",
+ "smw-paramdesc-csv-valuesep": "Вызначае межнік значÑньнÑÑž",
+ "smw-paramdesc-dsv-separator": "РазьдзÑлÑльнік",
+ "smw-paramdesc-dsv-filename": "Ðазва DSV-файла",
+ "smw-paramdesc-filename": "Ðазва зыходнага файлу",
+ "smw-smwdoc-description": "Паказвае табліцу з уÑімі парамÑтрамі, ÑÐºÑ–Ñ Ð¼Ð¾Ð³ÑƒÑ†ÑŒ ужывацца Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð°Ð³Ð° фармату вынікаў разам з значÑньнÑмі па змоўчваньні Ñ– апіÑаньнÑмі.",
+ "smw-smwdoc-default-no-parameter-list": "ГÑÑ‚Ñ‹ фармат выніку не забÑÑьпечвае ÑпÑÑ†Ñ‹Ñ„Ñ–Ñ‡Ð½Ñ‹Ñ Ð¿Ð°Ñ€Ð°Ð¼Ñтры.",
+ "smw-smwdoc-par-format": "Фармат вынікаў Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð·Ñƒ дакумÑнтацыі пра парамÑтры.",
+ "smw-paramdesc-sort": "УлаÑьціваÑьць, паводле Ñкой Ñартаваць запыт",
+ "smw-paramdesc-order": "Парадак ÑÐ°Ñ€Ñ‚Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Ð·Ð°Ð¿Ñ‹Ñ‚Ñƒ",
+ "smw-paramdesc-searchlabel": "ТÑкÑÑ‚ Ð´Ð»Ñ Ð¿Ñ€Ð°Ñ†Ñгу пошуку",
+ "smw-paramdesc-named_args": "Ðазвы аргумÑнтаў, перададзеных у шаблён",
+ "smw-paramdesc-export": "Ðалады імпарту",
+ "smw-paramdesc-json-type": "Тып ÑÑрыÑлізацыі",
+ "smw-paramdesc-source": "ÐльтÑÑ€Ð½Ð°Ñ‚Ñ‹ÑžÐ½Ð°Ñ ÐºÑ€Ñ‹Ð½Ñ–Ñ†Ð° запыту",
+ "smw-paramdesc-jsonsyntax": "СынтакÑÑ‹Ñ JSON Ð´Ð»Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹ÑтаньнÑ",
+ "smw-printername-feed": "Канал RSS і Atom",
+ "smw-paramdesc-feedtype": "Тып каналу",
+ "smw-paramdesc-feedtitle": "ТÑкÑÑ‚, Ñкі будзе ÑкарыÑтаны Ñž ÑкаÑьці назвы каналу",
+ "smw-paramdesc-feeddescription": "ТÑкÑÑ‚, Ñкі будзе ÑкарыÑтаны Ñž ÑкаÑьці апіÑÐ°Ð½ÑŒÐ½Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ",
+ "smw-paramdesc-feedpagecontent": "ЗьмеÑÑ‚ Ñтаронкі, Ñкі будзе адлюÑтроўвацца на канале",
+ "smw-label-feed-description": "Канал $1 $2",
+ "smw-paramdesc-mimetype": "Тып мÑÐ´Ñ‹Ñ (MIME-тып) Ð´Ð»Ñ Ð·Ñ‹Ñ…Ð¾Ð´Ð½Ð°Ð³Ð° файлу",
+ "smw_iq_disabled": "СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ñ‹Ñ Ð·Ð°Ð¿Ñ‹Ñ‚Ñ‹ былі выключаны Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}.",
+ "smw_iq_moreresults": "… наÑÑ‚ÑƒÐ¿Ð½Ñ‹Ñ Ð²Ñ‹Ð½Ñ–ÐºÑ–",
+ "smw_parseerror": "Пададзенае значÑньне не было раÑпазнанае.",
+ "smw_notitle": "«$1» Ð½Ñ Ð¼Ð¾Ð¶Ð° выкарыÑтоўвацца Ñк назва Ñтаронкі Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}.",
+ "smw_noproperty": "«$1» Ð½Ñ Ð¼Ð¾Ð¶Ð° выкарыÑтоўвацца Ñк улаÑьціваÑьць Ñтаронкі Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}.",
+ "smw_wrong_namespace": "Тут дазволены толькі Ñтаронкі з праÑторы назваў «$1».",
+ "smw_manytypes": "Ð”Ð»Ñ ÑžÐ»Ð°ÑьціваÑьці вызначана болей аднаго тыпу.",
+ "smw_emptystring": "ПуÑÑ‚Ñ‹Ñ Ñ€Ð°Ð´ÐºÑ– не дазволены.",
+ "smw_notinenum": "«$1» не ўваходзіць у ÑÑŒÐ¿Ñ–Ñ ($2) [[Property:Allows value|дазволеных значÑньнÑÑž]] Ð´Ð»Ñ ÑžÐ»Ð°ÑьціваÑьці «$3».",
+ "smw_noboolean": "«$1» не зьÑўлÑецца лÑгічным значÑньнем (праўда/нÑпраўда).",
+ "smw_true_words": "праўда,п,так,т",
+ "smw_false_words": "нÑпраўда,н,не",
+ "smw_nofloat": "«$1» не зьÑўлÑецца лікам.",
+ "smw_infinite": "Лікі, Ð±Ð¾Ð»ÑŒÑˆÑ‹Ñ Ð·Ð° «$1», не падтрымліваюцца.",
+ "smw_unitnotallowed": "«$1» не аб’ÑÑžÐ»ÐµÐ½Ð°Ñ Ñк Ð´Ð°Ð¿ÑƒÑˆÑ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð°Ð´Ð·Ñ–Ð½ÐºÐ° вымÑÑ€ÑньнÑÑž Ð´Ð»Ñ Ð³Ñтай улаÑьціваÑьці.",
+ "smw_nounitsdeclared": "ÐÑма вызначаных адзінак вымÑÑ€ÑÐ½ÑŒÐ½Ñ Ð´Ð»Ñ Ð³Ñтай улаÑьціваÑьці.",
+ "smw_novalues": "ЗначÑньні не пазначаныÑ",
+ "smw_nodatetime": "Дата «$1» не была раÑпазнанаÑ.",
+ "smw_toomanyclosing": "Зашмат уваходжаньнÑÑž «$1» у запыце.",
+ "smw_noclosingbrackets": "ВыкарыÑÑ‚Ð°Ð½Ñ‹Ñ Ð´ÑƒÐ¶ÐºÑ– «<nowiki>[[</nowiki>» у Вашым запыце не былі зачынены адпаведнымі дужкамі «]]».",
+ "smw_misplacedsymbol": "Сымбаль «$1» быў выкарыÑтаны Ñž меÑцы, дзе ён Ð½Ñ Ð¼Ð°Ðµ ÑÑнÑу.",
+ "smw_unexpectedpart": "ЧаÑтка запыту «$1» не была раÑпазнанаÑ.\nВынікі могуць быць нечаканымі.",
+ "smw_emptysubquery": "Ðейкі падзапыт Ð½Ñ Ð¼Ð°Ðµ Ñлушных умоваў.",
+ "smw_misplacedsubquery": "Ðейкі падзапыт быў выкарыÑтаны Ñž меÑцы, дзе выкарыÑтаньне падзапытаў не дазволена.",
+ "smw_valuesubquery": "Падзапыты не падтрымліваюцца Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÑньнÑÑž улаÑьціваÑьці «$1».",
+ "smw_badqueryatom": "ЧаÑтка запыту «<nowiki>[[…]]</nowiki>» не была раÑпазнанаÑ.",
+ "smw_propvalueproblem": "ЗначÑньне ўлаÑьціваÑьці «$1» не было раÑпазнанае.",
+ "smw_noqueryfeature": "ÐÐµÐ¹ÐºÐ°Ñ ÑžÐ»Ð°ÑьціваÑьць не падтрымліваецца Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}, Ñ– чаÑтка запыту была Ñ–Ð³Ð½Ð°Ñ€Ð°Ð²Ð°Ð½Ð°Ñ ($1).",
+ "smw_noconjunctions": "ЗлучÑньні Ñž запытах не падтрымліваюцца Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}, Ñ– чаÑтка запыту была Ñ–Ð³Ð½Ð°Ñ€Ð°Ð²Ð°Ð½Ð°Ñ ($1).",
+ "smw_nodisjunctions": "ПадзÑленьні Ñž запытах не падтрымліваюцца Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}, Ñ– чаÑтка запыту была Ñ–Ð³Ð½Ð°Ñ€Ð°Ð²Ð°Ð½Ð°Ñ ($1).",
+ "smw_querytoolarge": "$2 {{PLURAL:$2|наÑÑ‚ÑƒÐ¿Ð½Ð°Ñ ÑžÐ¼Ð¾Ð²Ð° запыту Ð½Ñ Ð¼Ð¾Ð¶Ð° ўлічвацца|наÑÑ‚ÑƒÐ¿Ð½Ñ‹Ñ ÑžÐ¼Ð¾Ð²Ñ‹ запыту Ð½Ñ Ð¼Ð¾Ð³ÑƒÑ†ÑŒ улічвацца|наÑтупных умоваў запыту Ð½Ñ Ð¼Ð¾Ð³ÑƒÑ†ÑŒ улічвацца}} праз абмежаваньні гÑтай вікі на глыбіню Ñ– памер запыту: <code>$1</code>.",
+ "smw_notemplategiven": "Ð”Ð»Ñ Ð²Ñ‹ÐºÐ°Ð½Ð°Ð½ÑŒÐ½Ñ Ð³Ñтага запыту, неабходна ўвеÑьці значÑньне парамÑтру «template».",
+ "smw_db_sparqlqueryproblem": "Ðемагчыма атрымаць вынік запыту да базы зьвеÑтак SPARQL. Магчыма гÑÑ‚Ð°Ñ Ñ‡Ð°ÑÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¼Ñ‹Ð»ÐºÐ° ці праблема праграмнага забеÑьпÑчÑÐ½ÑŒÐ½Ñ Ð±Ð°Ð·Ñ‹ зьвеÑтак.",
+ "smw_db_sparqlqueryincomplete": "Выкананьне запыту было перарванае, з-за таго, што ён зьÑўлÑецца занадта Ñкладаным. ÐÐµÐºÐ°Ñ‚Ð¾Ñ€Ñ‹Ñ Ð²Ñ‹Ð½Ñ–ÐºÑ– могуць адÑутнічаць. Калі магчыма, паÑпрабуйце ÑпраÑьціць запыт.",
+ "smw_type_header": "УлаÑьціваÑьці тыпу «$1»",
+ "smw_typearticlecount": "{{PLURAL:$1|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ð°Ñ $1 улаÑьціваÑьць, ÑÐºÐ°Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўвае|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 улаÑьціваÑьці, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 улаÑьціваÑьцÑÑž, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць}} гÑÑ‚Ñ‹ тып.",
+ "smw_attribute_header": "Старонкі, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць улаÑьціваÑьць «$1»",
+ "smw_attributearticlecount": "{{PLURAL:$1|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ð°Ñ $1 Ñтаронка, ÑÐºÐ°Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўвае|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 Ñтаронкі, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць|ÐŸÐ°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 Ñтаронак, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюць}} гÑтую ўлаÑьціваÑьць.",
+ "smw-propertylist-subproperty-header": "ПадулаÑьціваÑьці",
+ "smw-propertylist-redirect-header": "Сынонімы",
+ "smw-propertylist-error-header": "Старонкі зь нÑправільнымі прызначÑньнÑмі",
+ "specialpages-group-smw_group": "СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ MediaWiki",
+ "exportrdf": "ЭкÑпарт Ñтаронак у фармаце RDF",
+ "smw_exportrdf_docu": "ГÑта Ñтаронка дазвалÑе Вам атрымліваць зьвеÑткі Ñа Ñтаронкі Ñž фармаце RDF.\nКаб ÑкÑпартаваць Ñтаронкі, увÑдзіце Ñ–Ñ… назвы ніжÑй у Ñ‚ÑкÑтавым полі, па адной назьве на радок.",
+ "smw_exportrdf_recursive": "РÑкурÑіўны ÑкÑпарт уÑÑ–Ñ… зьвÑзаных Ñтаронак.\nЗьвÑрніце ўвагу на тое, што вынік можа быць вÑлікім!",
+ "smw_exportrdf_backlinks": "ТакÑама ÑкÑпартаваць уÑе Ñтаронкі, ÑÐºÑ–Ñ ÑпаÑылаюцца на ÑкÑÐ¿Ð°Ñ€Ñ‚Ð°Ð²Ð°Ð½Ñ‹Ñ Ñтаронкі.\nСтварае RDF з поўнай навігацыÑй.",
+ "smw_exportrdf_lastdate": "Ðе ÑкÑпартаваць Ñтаронкі, ÑÐºÑ–Ñ Ð½Ðµ зьмÑнÑліÑÑ Ð· пададзенага чаÑу.",
+ "smw_exportrdf_submit": "ЭкÑпартаваць",
+ "uriresolver": "Пераўтваральнік URI",
+ "properties": "УлаÑьціваÑьці",
+ "smw_properties_docu": "ÐаÑÑ‚ÑƒÐ¿Ð½Ñ‹Ñ ÑžÐ»Ð°ÑьціваÑьці выкарыÑтоўваюцца Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}.",
+ "smw_property_template": "$1 тыпу $2 ($3 {{PLURAL:$3|выкарыÑтаньне|выкарыÑтаньні|выкарыÑтаньнÑÑž}})",
+ "smw_propertylackspage": "УÑе ўлаÑьціваÑьці павінны мець Ñтаронку апіÑаньнÑ!",
+ "smw_propertylackstype": "Ð”Ð»Ñ Ð³Ñтай улаÑьціваÑьці не пазначаны ніÑкі тып (пакуль будзе выкарыÑтоўвацца тып $1).",
+ "smw_propertyhardlyused": "ГÑÑ‚Ð°Ñ ÑžÐ»Ð°ÑьціваÑьць наўрад ці выкарыÑтоўваецца Ñž {{GRAMMAR:меÑны|{{SITENAME}}}}!",
+ "smw-sp-property-searchform": "ÐдлюÑтраваць улаÑьціваÑьці, ÑÐºÑ–Ñ ÑžÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ†ÑŒ:",
+ "smw-special-property-searchform": "Ð’Ñ‹Ñвіць улаÑьціваÑьці, ÑÐºÑ–Ñ ÑžÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ†ÑŒ:",
+ "smw-special-property-searchform-options": "Ðалады",
+ "smw-special-wantedproperties-filter-label": "Фільтар:",
+ "smw-special-wantedproperties-filter-none": "ÐÑма",
+ "smw-special-wantedproperties-filter-unapproved": "Ðезацьверджана",
+ "unusedproperties": "УлаÑьціваÑьці, ÑÐºÑ–Ñ Ð½Ðµ выкарыÑтоўваюцца",
+ "smw-unusedproperties-docu": "ГÑÑ‚Ð°Ñ Ñтаронка ўтрымлівае [https://www.semantic-mediawiki.org/wiki/Unused_properties Ð½ÐµÑžÐ¶Ñ‹Ð²Ð°Ð½Ñ‹Ñ ÑžÐ»Ð°ÑьціваÑьці], ÑÐºÑ–Ñ Ð±Ñ‹Ð»Ñ– аб’ÑўленыÑ, Ñ…Ð°Ñ†Ñ Ð½Ñ–Ð²Ð¾Ð´Ð½Ð°Ñ Ñтаронка Ñ–Ñ… не выкарыÑтоўвае. Ð”Ð»Ñ Ð´Ñ‹Ñ„ÑÑ€Ñнцаванага выглÑду, глÑдзіце [[Special:Properties|агульную ÑпÑцыÑльную Ñтаронку]] або [[Special:WantedProperties|Ñтаронку запатрабаваных улаÑьціваÑьцÑÑž]].",
+ "smw-unusedproperty-template": "$1 тыпу $2",
+ "wantedproperties": "Ð—Ð°Ð¿Ð°Ñ‚Ñ€Ð°Ð±Ð°Ð²Ð°Ð½Ñ‹Ñ ÑžÐ»Ð°ÑьціваÑьці",
+ "smw-wantedproperties-docu": "ГÑÑ‚Ð°Ñ Ñтаронка ўтрымлівае [https://www.semantic-mediawiki.org/wiki/Wanted_properties Ð·Ð°Ð¿Ð°Ñ‚Ñ€Ð°Ð±Ð°Ð²Ð°Ð½Ñ‹Ñ ÑžÐ»Ð°ÑьціваÑьці], ÑÐºÑ–Ñ ÑžÐ¶Ñ‹Ð²Ð°ÑŽÑ†Ñ†Ð° Ñž вікі, але Ð½Ñ Ð¼Ð°ÑŽÑ†ÑŒ Ñтаронкі з апіÑаньнем. Ð”Ð»Ñ Ð´Ñ‹Ñ„ÑÑ€Ñнцаванага выглÑду, глÑдзіце [[Special:Properties|агульную ÑпÑцыÑльную Ñтаронку]] або [[Special:UnusedProperties|Ñтаронку неўжываных улаÑьціваÑьцÑÑž]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|выкарыÑтаньне|выкарыÑтаньні|выкарыÑтаньнÑÑž}})",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|выкарыÑтаньне|выкарыÑтаньні|выкарыÑтаньнÑÑž}})",
+ "smw_purge": "Ðбнавіць",
+ "types": "Тыпы",
+ "smw_types_docu": "Ð¡ÑŒÐ¿Ñ–Ñ [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes даÑтупных тыпаў зьвеÑтак], у Ñкім кожны [https://www.semantic-mediawiki.org/wiki/Help:Datatype тып] зьÑўлÑецца наборам атрыбутаў Ð´Ð»Ñ Ð°Ð¿Ñ–ÑÐ°Ð½ÑŒÐ½Ñ Ð·Ð½Ð°Ñ‡ÑньнÑÑž з пункту Ð³Ð»ÐµÐ´Ð¶Ð°Ð½ÑŒÐ½Ñ Ð·Ð°Ñ…Ð¾ÑžÐ²Ð°Ð½ÑŒÐ½Ñ Ñ– адлюÑÑ‚Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Ñ…Ð°Ñ€Ð°ÐºÑ‚Ð°Ñ€Ñ‹Ñтык, ÑÐºÑ–Ñ Ð¿Ð°Ñ…Ð¾Ð´Ð·Ñць ад прызначанай улаÑьціваÑьці.",
+ "smw-statistics-property-instance": "{{PLURAL:$1|1=ЗначÑньне|ЗначÑньні|ЗначÑньнÑÑž}} улаÑьціваÑьці (агулам)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|1=ЗначÑньне|ЗначÑньні|ЗначÑньнÑÑž}}]] (агулам)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|1=УлаÑьціваÑьць|УлаÑьціваÑьці|УлаÑьціваÑьцÑÑž}} (агулам)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|1=УлаÑьціваÑьць|УлаÑьціваÑьці|УлаÑьціваÑьцÑÑž}} (агулам)]] (ÑƒÐ¶Ñ‹Ñ‚Ð°Ñ Ð· прынамÑÑ– адным значÑньнем)",
+ "smw-statistics-property-page": "{{PLURAL:$1|1=УлаÑьціваÑьць|УлаÑьціваÑьці|УлаÑьціваÑьцÑÑž}} ({{PLURAL:$1|1=зарÑгіÑтраванаÑ|зарÑгіÑтраваныÑ|зарÑгіÑтраваных}} Ñа Ñтаронкай)",
+ "smw-statistics-property-type": "{{PLURAL:$1|1=УлаÑьціваÑьць|УлаÑьціваÑьці|УлаÑьціваÑьцÑÑž}} ({{PLURAL:$1|1=прыÑвоенаÑ|прыÑвоеныÑ|прыÑвоеных}} тыпу зьвеÑтак)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Запыт|Запыты|Запытаў}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Запыт|Запыты|Запытаў}}]]",
+ "smw-statistics-query-size": "Памер запыту",
+ "smw_uri_doc": "Пераўтваральнік URI ажыцьцÑўлÑе [$1 пошук W3C TAG у httpRange-14].\nГÑта забÑÑьпечвае ўпÑўненаÑьць, што людзі не заходзÑць на Ñайты.",
+ "ask": "СÑмантычны пошук",
+ "smw_ask_sortby": "Сартаваць па Ñлупку (неабавÑзкова)",
+ "smw_ask_ascorder": "Па павелічÑньні",
+ "smw_ask_descorder": "Па зьмÑншÑньні",
+ "smw-ask-order-rand": "Выпадковы",
+ "smw_ask_submit": "Шукаць",
+ "smw_ask_editquery": "РÑдагаваць запыт",
+ "smw_add_sortcondition": "[Дадаць умовы ÑартаваньнÑ]",
+ "smw-ask-sort-add-action": "Дадаць умовы ÑартаваньнÑ",
+ "smw_ask_hidequery": "Схаваць запыт (кампактны выглÑд)",
+ "smw_ask_help": "Дапамога па ÑтварÑньні запытаў",
+ "smw_ask_queryhead": "Умова",
+ "smw_ask_printhead": "Друкаваць абранае",
+ "smw_ask_printdesc": "(дадавайце адну назву ўлаÑьціваÑьці на радок)",
+ "smw_ask_format_as": "Фарматаваць Ñк:",
+ "smw_ask_defaultformat": "па змоўчваньні",
+ "smw_ask_otheroptions": "Ð†Ð½ÑˆÑ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹",
+ "smw_ask_show_embed": "Паказаць убудаваны код",
+ "smw_ask_hide_embed": "Схаваць убудаваны код",
+ "smw_ask_embed_instr": "Каб убудаваць гÑÑ‚Ñ‹ запыт у Ñтаронку, выкарыÑтоўвайце код пададзены ніжÑй.",
+ "smw-ask-delete": "Выдаліць",
+ "smw-ask-sorting": "Сартаваньне",
+ "smw-ask-options": "Ðалады",
+ "smw-ask-options-sort": "Ðалады ÑартаваньнÑ",
+ "smw-ask-format-options": "Фармат і налады",
+ "smw-ask-parameters": "ПарамÑтры",
+ "smw-ask-search": "Пошук",
+ "smw-ask-debug-desc": "ГенÑруе інфармацыю пра адладку запыту",
+ "smw-ask-no-cache": "Ðдлучыць кÑш запыту",
+ "smw-ask-result": "Вынік",
+ "smw-ask-empty": "Прыбраць уÑе запіÑÑ‹",
+ "smw-ask-download-link-desc": "Запампаваць запыты вынікаў у фармаце $1",
+ "smw-ask-format": "Фармат",
+ "smw-ask-input-assistance": "Дапамога пры ўводзе",
+ "searchbyproperty": "Шукаць па ўлаÑьціваÑьці",
+ "processingerrorlist": "Ð¡ÑŒÐ¿Ñ–Ñ Ð¿Ð°Ð¼Ñ‹Ð»Ð°Ðº апрацоўкі",
+ "propertylabelsimilarity": "Паведамленьне аб падобнаÑьці назваў улаÑьціваÑьцÑÑž",
+ "smw-processingerrorlist-intro": "ÐаÑтупны ÑÑŒÐ¿Ñ–Ñ Ð·Ð°Ð±ÑÑьпечвае агульны выглÑд памылак апрацоўваньнÑ, ÑÐºÑ–Ñ ÑžÐ·ÑŒÐ½Ñ–ÐºÐ»Ñ– Ñž ÑувÑзі з [https://www.semantic-mediawiki.org/ СÑмантычнае MediaWiki]. Раіцца Ñ€ÑгулÑрна назіраць за гÑтым ÑьпіÑам на звыклай аÑнове й выпраўлÑць нÑдзейÑÐ½Ñ‹Ñ Ð°Ð½Ð°Ñ‚Ð°Ñ†Ñ‹Ñ– значÑньнÑÑž.",
+ "smw_sbv_docu": "Шукаць уÑе Ñтаронкі, ÑÐºÑ–Ñ ÑžÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ†ÑŒ пададзеную ўлаÑьціваÑьць Ñ– значÑньне.",
+ "smw_sbv_novalue": "УвÑдзіце значÑньне Ð´Ð»Ñ ÑžÐ»Ð°ÑьціваÑьці, ці паглÑдзіце ÑžÑе значÑньні ўлаÑьціваÑьцÑÑž Ð´Ð»Ñ Â«$1».",
+ "smw_sbv_displayresult": "Ð¡ÑŒÐ¿Ñ–Ñ ÑƒÑÑ–Ñ… Ñтаронак, ÑÐºÑ–Ñ ÑžÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ†ÑŒ улаÑьціваÑьць «$1» Ñа значÑньнем «$2»",
+ "smw_sbv_displayresultfuzzy": "Ð¡ÑŒÐ¿Ñ–Ñ ÑƒÑÑ–Ñ… Ñтаронак, ÑÐºÑ–Ñ ÑžÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ†ÑŒ улаÑьціваÑьць «$1» Ñа значÑньнем «$2».\nЗ-за таго, што Ñупала толькі некалькі вынікаў, паказаны Ñтаронкі, ÑÐºÑ–Ñ Ð¼Ð°ÑŽÑ†ÑŒ Ð±Ð»Ñ–Ð·ÐºÑ–Ñ Ð·Ð½Ð°Ñ‡Ñньні.",
+ "smw_sbv_property": "УлаÑьціваÑьць:",
+ "smw_sbv_value": "ЗначÑньне:",
+ "smw_sbv_submit": "ЗнайÑьці",
+ "browse": "ПраглÑд {{GRAMMAR:родны|{{SITENAME}}}}",
+ "smw_browselink": "ПраглÑдзець улаÑьціваÑьці",
+ "smw_browse_article": "УвÑдзіце назву Ñтаронкі Ð´Ð»Ñ Ð¿Ð°Ñ‡Ð°Ñ‚ÐºÑƒ праглÑду.",
+ "smw_browse_go": "ПерайÑьці",
+ "smw_browse_show_incoming": "Паказаць улаÑьціваÑьці, ÑÐºÑ–Ñ ÑпаÑылаюцца Ñюды",
+ "smw_browse_hide_incoming": "Схаваць улаÑьціваÑьці, ÑÐºÑ–Ñ ÑпаÑылаюцца Ñюды",
+ "smw_browse_no_outgoing": "ГÑта Ñтаронка Ð½Ñ Ð¼Ð°Ðµ ўлаÑьціваÑьцÑÑž.",
+ "smw_browse_no_incoming": "Ðа гÑту Ñтаронку не ÑпаÑылаюцца ніÑÐºÑ–Ñ ÑžÐ»Ð°ÑьціваÑьці.",
+ "smw-browse-from-backend": "У Ñапраўдны Ñ‡Ð°Ñ Ñ–Ð´Ð·Ðµ атрыманьне інфармацыі з ÑÑрвÑрнай прылады.",
+ "smw-browse-intro": "Ð”Ð°Ð´Ð·ÐµÐ½Ð°Ñ Ñтаронка прадаÑтаўлÑе падрабÑзнаÑьці аб Ñ‚Ñме або ÑкзÑмплÑры ÑутнаÑьці, калі лаÑка, увÑдзіце назву аб'екту, Ñкі патрабуе праверкі.",
+ "smw-browse-invalid-subject": "Праверка Ñуб'екта зьвÑрнулаÑÑ Ð· памылкай «$1».",
+ "smw-browse-api-subject-serialization-invalid": "Суб’ект мае некарÑктны фармат ÑÑрыÑлізацыі.",
+ "smw-browse-show-group": "ÐдлюÑтраваць групы",
+ "smw-browse-hide-group": "Схаваць групы",
+ "smw_inverse_label_default": "$1 з",
+ "smw_inverse_label_property": "Паметка адваротнай улаÑьціваÑьці",
+ "pageproperty": "Старонка пошуку ўлаÑьціваÑьцÑÑž",
+ "smw_pp_docu": "УвÑдзіце Ñтаронку Ñ– ўлаÑьціваÑьць, ці толькі ўлаÑьціваÑьць, каб атрымаць уÑе Ð¿Ð°Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ Ð·Ð½Ð°Ñ‡Ñньні.",
+ "smw_pp_from": "З Ñтаронкі:",
+ "smw_pp_type": "УлаÑьціваÑьць:",
+ "smw_pp_submit": "Шукаць",
+ "smw_result_prev": "ПапÑÑ€ÑдніÑ",
+ "smw_result_next": "ÐаÑтупныÑ",
+ "smw_result_results": "Вынікі",
+ "smw_result_noresults": "Вынікаў нÑма.",
+ "smwadmin": "Функцыі адмініÑÑ‚Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Ñ– абÑлугоўваньнÑ",
+ "smw-admin-statistics-job-title": "СтатыÑтыка заданьнÑÑž",
+ "smw-admin-statistics-querycache-title": "СтатыÑтыка кÑшу запытаў",
+ "smw-admin-setupsuccess": "Рухавік Ñховішча наладжаны.",
+ "smw_smwadmin_return": "Ð’Ñрнуцца да $1",
+ "smw_smwadmin_updatestarted": "Быў раÑпачаты новы працÑÑ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ ÑÑмантычных зьвеÑтак.\nУÑе зьвеÑткі будуць пераўтвораны ці зьменены, калі гÑта будзе неабходна.\nÐ’Ñ‹ можаце назіраць за працÑÑам Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð½Ð° гÑтай ÑпÑцыÑльнай Ñтаронцы.",
+ "smw_smwadmin_updatenotstarted": "ПрацÑÑ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ ÑžÐ¶Ð¾ раÑпачаты.\nÐе Ñтвараем новы.",
+ "smw_smwadmin_updatestopped": "УÑе працÑÑÑ‹ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð±Ñ‹Ð»Ñ– ÑпыненыÑ.",
+ "smw_smwadmin_updatenotstopped": "Каб Ñпыніць працÑÑ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ, паÑтаўце Ñž полі адзнаку Ð´Ð·ÐµÐ»Ñ Ð¿Ð°Ñ†ÑŒÐ²ÐµÑ€Ð´Ð¶Ð°Ð½ÑŒÐ½Ñ Ð²Ð°ÑˆÑ‹Ñ… намераў.",
+ "smw-admin-docu": "ГÑÑ‚Ð°Ñ ÑпÑцыÑÐ»ÑŒÐ½Ð°Ñ Ñтаронка дапамагае вам Ð¿Ð°Ð´Ñ‡Ð°Ñ ÑƒÑталÑваньнÑ, абнаўленьнÑ, абÑÐ»ÑƒÐ³Ð¾ÑžÐ²Ð°Ð½ÑŒÐ½Ñ Ñ– выкарыÑÑ‚Ð°Ð½ÑŒÐ½Ñ <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a>, а такÑама забÑÑьпечвае Ð´Ð°Ð»ÐµÐ¹ÑˆÑ‹Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–ÑÑ‚Ñ€Ð°Ñ†Ñ‹Ð¹Ð½Ñ‹Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ– Ñ– задачы разам з ÑтатыÑтыкай.\nÐе забывайце захоўваць ÐºÐ°ÑˆÑ‚Ð¾ÑžÐ½Ñ‹Ñ Ð·ÑŒÐ²ÐµÑткі перад выкананьнем адмініÑтратарÑкіх функцыÑÑž.",
+ "smw-admin-environment": "Ðбалонка праграмнага забеÑьпÑчÑньнÑ",
+ "smw-admin-db": "Ðаладка базы зьвеÑтак",
+ "smw-admin-dbdocu": "Semantic MediaWiki патрабуе ўлаÑную Ñтруктуру базы зьвеÑтак (Ñна залежыць ад MediaWiki Ñ– не ўплывае на аÑтатнюю чаÑтку ÑžÑталÑÐ²Ð°Ð½ÑŒÐ½Ñ installation) Ð´Ð»Ñ Ð·Ð°Ñ…Ð¾ÑžÐ²Ð°Ð½ÑŒÐ½Ñ ÑÑмантычных зьвеÑтак.\nГÑта ÑžÑÑ‚Ð°Ð»Ñ‘Ð²Ð°Ñ‡Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ Ð¼Ð¾Ð¶Ð° быць Ð²Ñ‹ÐºÐ°Ð½Ð°Ð½Ð°Ñ Ð½ÐµÐºÐ°Ð»ÑŒÐºÑ– разоў без аніÑкай шкоды, але Ð´Ð»Ñ ÑžÑталÑÐ²Ð°Ð½ÑŒÐ½Ñ Ñ†Ñ– Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð´Ð°Ñтаткова будзе аднаго разу.",
+ "smw-admin-permissionswarn": "Калі адбываюцца SQL-памылкі Ð¿Ð°Ð´Ñ‡Ð°Ñ Ð²Ñ‹ÐºÐ°Ð½Ð°Ð½ÑŒÐ½Ñ Ð°Ð¿Ñрацыі, гÑта значыць, што рахунак базы зьвеÑтак, Ñкі выкарыÑтоўвае вікі (праверце ваш файл «LocalSettings.php»), магчыма, Ð½Ñ Ð¼Ð°Ðµ адпаведных правоў.\nÐадайце рахунку Ð´Ð°Ð´Ð°Ñ‚ÐºÐ¾Ð²Ñ‹Ñ Ð¿Ñ€Ð°Ð²Ñ‹ на ÑтварÑньне Ñ– выдаленьне табліцаў, чаÑова ўвÑдзіце рахунак адмініÑтратара базы зьвеÑтак у файл «LocalSettings.php», ці карыÑтайцеÑÑ Ñкрыптом падтрымкі <code>setupStore.php</code>, Ñкі можа выкарыÑтоўваць рахунак адмініÑтратара.",
+ "smw-admin-dbbutton": "УÑталÑваць ці абнавіць табліцы",
+ "smw-admin-announce": "ÐнанÑуйце вашую вікі",
+ "smw-admin-deprecation-notice-title": "Паведамленьні аб ÑтарÑньні",
+ "smw-admin-deprecation-notice-title-notice": "Ð£Ð²Ð°Ñ…Ð¾Ð´Ð½Ñ‹Ñ Ð·ÑŒÐ¼ÐµÐ½Ñ‹",
+ "smw-admin-deprecation-notice-title-replacement": "Ð—Ð°Ð¼ÐµÐ½ÐµÐ½Ñ‹Ñ Ñ†Ñ– Ð¿ÐµÑ€Ð°Ð¹Ð¼ÐµÐ½Ð°Ð²Ð°Ð½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹",
+ "smw-admin-deprecation-notice-title-removal": "ÐŸÑ€Ñ‹Ð±Ñ€Ð°Ð½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹",
+ "smw-smwadmin-refresh-title": "Выпраўленьне й абнаўленьне зьвеÑтак",
+ "smw_smwadmin_datarefresh": "Перабудова зьвеÑтак",
+ "smw_smwadmin_datarefreshdocu": "ІÑнуе магчымаÑьць Ð°Ð´Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ ÑžÑÑ–Ñ… зьвеÑтак Semantic MediaWiki на аÑнове цÑперашнÑга зьмеÑту {{GRAMMAR:родны|{{SITENAME}}}}.\nГÑта можа быць карыÑным Ð´Ð»Ñ Ð°Ð´Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð¿Ð°Ñ€ÑƒÑˆÐ°Ð½Ñ‹Ñ… зьвеÑтак ці Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð·ÑŒÐ²ÐµÑтак, калі ўнутраны фармат зьмÑнÑецца пры абнаўленьні праграмнага забеÑьпÑчÑньнÑ.\nÐбнаўленьне будзе выконвацца Ñтаронка за Ñтаронкай Ñ– можа занÑць пÑўны чаÑ.\nÐіжÑй паказваецца інфармацыÑ, ці адбываецца абнаўленьне, ÑÐºÐ°Ñ Ð´Ð°Ð·Ð²Ð°Ð»Ñе Вам пачынаць ці ÑпынÑць абнаўленьне (за выключÑньнем выпадкаў, калі гÑта магчымаÑьць Ð·Ð°Ð±Ð°Ñ€Ð¾Ð½ÐµÐ½Ð°Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–Ñтратарам Ñайта).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Ідзе працÑÑ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ.</strong>\nГÑта нармальна, што працÑÑ Ð°Ð±Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð·Ð°Ð¿Ð°Ð²Ð¾Ð»ÑŒÐ²Ð°ÐµÑ†Ñ†Ð°, Ñ– зьвеÑткі абнаўлÑюцца невÑлікімі кавалкамі.\nКаб Ñкончыць абнаўленьне хутчÑй, Ð’Ñ‹ можаце ÑкарыÑтацца Ñкрыптом падтрымкі ÐœÑдыÑВікі <code>runJobs.php</code> (выкарыÑтоўвайце парамÑтар <code>--maxjobs 1000</code>, каб абмежаваць колькаÑьць выкананых абнаўленьнÑÑž у адным пакеце).\nПрыкладны Ñ‡Ð°Ñ ÑканчÑÐ½ÑŒÐ½Ñ Ñ†ÑперашнÑга абнаўленьнÑ:",
+ "smw_smwadmin_datarefreshbutton": "ЗаплÑнаваць перабудову зьвеÑтак",
+ "smw_smwadmin_datarefreshstop": "Спыніць гÑта абнаўленьне",
+ "smw_smwadmin_datarefreshstopconfirm": "Так, Ñ {{GENDER:$1|ўпÑўнены|ўпÑўненаÑ}}.",
+ "smw-admin-outdateddisposal-title": "Ð›Ñ–ÐºÐ²Ñ–Ð´Ð°Ñ†Ñ‹Ñ ÑаÑтарÑлых ÑутнаÑьцÑÑž",
+ "smw-admin-outdateddisposal-active": "Прызначана праца па ліквідацыі ÑаÑтарÑлых аб'ектаў.",
+ "smw-admin-outdateddisposal-button": "ЗаплÑнаваць ліквідацыю",
+ "smw-admin-propertystatistics-title": "Перабудова ÑтатыÑтыкі ўлаÑьціваÑьці",
+ "smw-admin-propertystatistics-button": "ЗаплÑнаваць перабудову ÑтатыÑтыкі",
+ "smw-admin-fulltext-title": "Перабудова паўнатÑкÑтавага пошуку",
+ "smw-admin-fulltext-button": "Запланаваць паўнатÑкÑтавую перабудову",
+ "smw-admin-support": "Падтрымка",
+ "smw-admin-supportdocu": "ÐÑьць Ñ€Ð¾Ð·Ð½Ñ‹Ñ ÐºÑ€Ñ‹Ð½Ñ–Ñ†Ñ‹, каб дапамагчы вам у выпадку праблемаў:",
+ "smw-admin-installfile": "Калі ўзьнікнуць праблемы з вашым уÑталÑваньнем, пачніце з Ñ€ÑкамÑндацыÑÑž у <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">файле INSTALL</a> Ñ– <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">на Ñтаронцы ÑžÑталёўкі</a>.",
+ "smw-admin-smwhomepage": "ÐŸÐ¾ÑžÐ½Ð°Ñ Ð´Ð°ÐºÑƒÐ¼ÑÐ½Ñ‚Ð°Ñ†Ñ‹Ñ Semantic MediaWiki знаходзіцца на <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Можна паведамлÑць пра памылкі Ñž <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">ÑÑ‹ÑÑ‚Ñму ÑачÑÐ½ÑŒÐ½Ñ Ð·Ð° праблемамі</a>, Ñтаронка <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">паведамленьнÑÑž пра памылкі</a> мае даведку пра тое, Ñк ÑÑ„Ñктыўна напіÑаць Ñправаздачу пра праблему.",
+ "smw-admin-questions": "Калі вы маеце пытаньні ці прапановы, далучайцеÑÑ Ð´Ð° дыÑкуÑÑ–Ñ– Ñž <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">паштовай раÑÑылцы</a> Semantic MediaWiki ці Ñž <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">чаце</a>.",
+ "smw-admin-other-functions": "Ð†Ð½ÑˆÑ‹Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ–",
+ "smw-admin-supplementary-section-title": "Ð”Ð°Ð¿Ð°Ð¼Ð¾Ð¶Ð½Ñ‹Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ–",
+ "smw-admin-supplementary-section-subtitle": "Функцыі Ñдра",
+ "smw-admin-supplementary-settings-title": "ПарамÑтры канфігурацыі",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> выводзіць Ñупольны ÑÑŒÐ¿Ñ–Ñ Ð´Ð°Ñтупных наладак, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюцца Ñž СÑмантычнай ÐœÑдыÑВікі",
+ "smw-admin-supplementary-operational-statistics-title": "ÐпÑÑ€Ð°Ñ†Ñ‹Ð¹Ð½Ð°Ñ ÑтатыÑтыка",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> адлюÑтроўвае пашыраны набор ÑтатыÑтыкі",
+ "smw-admin-supplementary-idlookup-title": "Пошук Ñ– вылучÑньне ÑутнаÑьці",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> зьмÑшчае функцыі Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ й меÑÑ†Ð°Ð·Ð½Ð°Ñ…Ð¾Ð´Ð¶Ð°Ð½ÑŒÐ½Ñ Ð°Ñобных ÑутнаÑьцÑÑž",
+ "smw-admin-supplementary-duplookup-title": "ДублÑÐ²Ð°Ð½Ñ‹Ñ ÑутнаÑьці",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</span> каб пералічыць запіÑÑ‹, ÑÐºÑ–Ñ ÐºÐ»Ð°Ñыфікуюцца Ñк ÑƒÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°ÑŽÑ‡Ñ‹Ñ Ð´ÑƒÐ±Ð»Ñ–ÐºÐ°Ñ‚Ñ‹ Ñž табліцы ÑутнаÑьцÑÑž",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "СтатыÑтыка кÑшу",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> паказвае ÑтатыÑтыку дакранальна кішÑні",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> паказвае налады й індÑкÑную ÑтатыÑтыку",
+ "smw-admin-supplementary-elastic-functions": "ÐŸÐ°Ð´Ñ‚Ñ€Ñ‹Ð¼Ð²Ð°Ð½Ñ‹Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ–",
+ "smw-admin-supplementary-elastic-settings-title": "Ðалады",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> выкарыÑтоўваецца ''Elasticsearch'' длÑÂ ÐºÑ–Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Ñ–Ð½Ð´ÑкÑамі ''СÑмантычнай ÐœÑдыÑВікі''",
+ "smw-admin-supplementary-elastic-mappings-title": "Мапаваньні",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u>, каб адлюÑтраваць індÑкÑÑ‹ Ñ– меÑцазнаходжаньні палёў",
+ "smw-admin-supplementary-elastic-mappings-summary": "ЗьмеÑÑ‚",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> паказвае ÑтатыÑтыку вузла",
+ "smw-admin-supplementary-elastic-indices-title": "ІндÑкÑÑ‹",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> забÑÑьпечвае аглÑд даÑтупных індÑкÑаў Ñ– Ñ–Ñ… ÑтатыÑтыку",
+ "smw-admin-supplementary-elastic-statistics-title": "СтатыÑтыка",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> паказвае ÑтатыÑтыку ўзроўню індÑкÑу",
+ "smw-admin-supplementary-elastic-status-replication": "Стан Ñ€Ñплікацыі",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "ÐпошнÑÑ Ð°ÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ñ€ÑплікацыÑ: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Прамежак абнаўленьнÑ: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Затрымка ÑžÐ·Ð½Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð¿Ñ€Ð°Ñ†Ñ‹: $1 (ацÑнка)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "ЧаÑÐ¾Ð¿Ñ–Ñ (файл) нÑвыкананай працы: $1 (ацÑнка)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "РÑÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ‹Ñ Ð·Ð°Ð±Ð»Ñкавана: $1 (перабудова Ñž працÑÑе)",
+ "smw-list-count": "Ð¡ÑŒÐ¿Ñ–Ñ ÑƒÑ‚Ñ€Ñ‹Ð¼Ð»Ñ–Ð²Ð°Ðµ $1 {{PLURAL:$1|уваход|уваходы}}.",
+ "smw-list-count-from-cache": "ЛіÑÑ‚ утрымлівае $1 {{PLURAL:$1|уваход|уваходы}} Ñ– быў атрыманы з кішÑню (UTC: $2).",
+ "smw-property-label-similarity-title": "Справаздача аб падобнаÑьці назваў улаÑьціваÑьцÑÑž",
+ "smw-property-label-similarity-intro": "<u>$1</u> вылічвае падобнаÑьці Ð´Ð»Ñ Ñ–Ñных назваў улаÑьціваÑьцÑÑž",
+ "smw-property-label-similarity-threshold": "Парог:",
+ "smw-property-label-similarity-type": "ÐдлюÑтраваць тып ідÑнтыфікатару",
+ "smw-property-label-similarity-noresult": "Ð”Ð»Ñ Ð°Ð±Ñ€Ð°Ð½Ñ‹Ñ… парамÑтраў Ð½Ñ Ð·Ð½Ð¾Ð¹Ð´Ð·ÐµÐ½Ð° вынікаў.",
+ "smw_adminlinks_datastructure": "Структура зьвеÑтак",
+ "smw_adminlinks_displayingdata": "Паказ зьвеÑтак",
+ "smw_adminlinks_inlinequerieshelp": "Дапамога па ўбудаваных запытах",
+ "smw-property-indicator-type-info": "УлаÑьціваÑьць, Ð²Ñ‹Ð·Ð½Ð°Ñ‡Ð°Ð½Ð°Ñ {{PLURAL:$1|ўдзельнікам|ÑÑ‹ÑÑ‚Ñмай}}",
+ "smw-property-indicator-last-count-update": "ÐŸÑ€Ñ‹ÐºÐ»Ð°Ð´Ð½Ð°Ñ ÐºÐ¾Ð»ÑŒÐºÐ°Ñьць выкарыÑтоўваньнÑÑž\nÐпошні раз абноўлена: $1",
+ "smw-concept-indicator-cache-update": "Лічыльнік кішÑню\nÐпошні раз абноўлены: $1",
+ "smw-createproperty-isproperty": "ГÑта ўлаÑьціваÑьць тыпу $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|1=Дазволенае значÑньне|Ð”Ð°Ð·Ð²Ð¾Ð»ÐµÐ½Ñ‹Ñ Ð·Ð½Ð°Ñ‡Ñньні}} Ð´Ð»Ñ Ð³Ñтай улаÑьціваÑьці:",
+ "smw-paramdesc-category-delim": "Межнік",
+ "smw-paramdesc-category-template": "Шаблён, з дапамогай Ñкога фарматуюцца ÑлÑмÑнты",
+ "smw-paramdesc-category-userparam": "ПарамÑтар Ð´Ð»Ñ Ð¿ÐµÑ€Ð°Ð´Ð°Ñ‡Ñ‹ шаблёну",
+ "smw-info-par-message": "Паведамленьне Ð´Ð»Ñ Ð°Ð´Ð»ÑŽÑтраваньнÑ.",
+ "smw-info-par-icon": "Значак Ð´Ð»Ñ Ð°Ð´Ð»ÑŽÑÑ‚Ñ€Ð°Ð²Ð°Ð½ÑŒÐ½Ñ Â«info» або «warning».",
+ "prefs-smw": "СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ MediaWiki",
+ "prefs-general-options": "ÐÐ³ÑƒÐ»ÑŒÐ½Ñ‹Ñ Ð½Ð°Ð»Ð°Ð´Ñ‹",
+ "prefs-ask-options": "СпÑцыÑльныÑ:ПарамÑтры запыту",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (Ñ– зьвÑÐ·Ð°Ð½Ñ‹Ñ Ð¿Ð°ÑˆÑ‹Ñ€Ñньні) дазвалÑе пÑÑ€Ñанальную наладку Ð´Ð»Ñ Ð½ÐµÐºÐ°Ñ‚Ð¾Ñ€Ñ‹Ñ… функцыÑÑž. Калі лаÑка, глÑдзіце [https://www.semantic-mediawiki.org/wiki/Help:User_preferences Ñтаронку дапамогі] Ð´Ð»Ñ Ð¿Ð°Ð´Ñ€Ð°Ð±Ñзнага апіÑаньнÑ.",
+ "smw-prefs-ask-options-tooltip-display": "ÐдлюÑтроўваць парамÑтар у выглÑдзе інфармацыйнай падказкі",
+ "smw-prefs-ask-options-compact-view-basic": "Дазволіць аÑноўны кампактны выглÑд",
+ "smw-prefs-help-ask-options-compact-view-basic": "Калі ўключана, адлюÑтроўвае Ñкарочаны набор ÑпаÑылак на Special:Ask у ÑьціÑнутым фармаце",
+ "smw-prefs-general-options-jobqueue-watchlist": "ÐдлюÑтраваць ÑÑŒÐ¿Ñ–Ñ Ð½Ð°Ð·Ñ–Ñ€Ð°Ð½ÑŒÐ½ÑÑž за чÑргай задачаў на аÑабіÑтым панÑлю",
+ "smw-prefs-general-options-disable-editpage-info": "Ðдлучыць уÑтупны Ñ‚ÑкÑÑ‚ на Ñтаронцы Ñ€ÑдагаваньнÑ",
+ "smw-prefs-general-options-disable-search-info": "Ðдлучыць інфармацыю аб падтрымцы ÑынтакÑÑ‹Ñу на змоўчнай Ñтаронцы пошуку",
+ "smw-prefs-general-options-suggester-textinput": "Улучыць дапамогу Ð´Ð»Ñ ÑžÐ²Ð¾Ð´Ñƒ ÑÑмантычных аб’ектаў",
+ "smw-ui-tooltip-title-property": "УлаÑьціваÑьць",
+ "smw-ui-tooltip-title-quantity": "КанвÑÑ€ÑÑ–Ñ Ð°Ð´Ð·Ñ–Ð½ÐºÑ–",
+ "smw-ui-tooltip-title-info": "ІнфармацыÑ",
+ "smw-ui-tooltip-title-service": "Ð¡Ð»ÑƒÐ¶Ð±Ð¾Ð²Ñ‹Ñ ÑпаÑылкі",
+ "smw-ui-tooltip-title-warning": "ПапÑÑ€Ñджаньне",
+ "smw-ui-tooltip-title-error": "Памылка",
+ "smw-ui-tooltip-title-parameter": "ПарамÑтар",
+ "smw-ui-tooltip-title-event": "ПадзеÑ",
+ "smw-ui-tooltip-title-note": "КамÑнтар",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw-ui-tooltip-title-reference": "ЗноÑка",
+ "smw_unknowntype": "Тып «$1» гÑтай улаÑьціваÑьці зьÑўлÑецца нÑÑлушным",
+ "smw-concept-cache-text": "Задумка мае $1 {{PLURAL:$1|Ñтаронку|Ñтаронкі|Ñтаронак}}, Ñ– была апошні раз абноўлена $3, $2.",
+ "smw_concept_header": "Старонкі канцÑпцыі «$1»",
+ "smw_conceptarticlecount": "ÐіжÑй {{PLURAL:$1|Ð¿Ð°ÐºÐ°Ð·Ð°Ð½Ð°Ñ $1 Ñтаронка|Ð¿Ð°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 Ñтаронкі|Ð¿Ð°ÐºÐ°Ð·Ð°Ð½Ñ‹Ñ $1 Ñтаронак}}.",
+ "smw-qp-empty-data": "Ð—Ð°Ð¿Ñ‹Ñ‚Ð°Ð½Ñ‹Ñ Ð·ÑŒÐ²ÐµÑткі Ð½Ñ Ð¼Ð¾Ð³ÑƒÑ†ÑŒ быць адлюÑÑ‚Ñ€Ð°Ð²Ð°Ð½Ñ‹Ñ Ñž ÑувÑзі зь недаÑтатковаÑьцю крытÑраў адбору.",
+ "right-smw-admin": "ДоÑтуп да адмініÑтрацыйных задачаў (СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ MediaWiki)",
+ "right-smw-ruleedit": "РÑдагаваць Ñтаронкі правілаў (СÑÐ¼Ð°Ð½Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ ÐœÑдыÑВікі)",
+ "restriction-level-smw-pageedit": "абаронена (толькі Ð°Ð´Ð¿Ð°Ð²ÐµÐ´Ð½Ñ‹Ñ ÑžÐ´Ð·ÐµÐ»ÑŒÐ½Ñ–ÐºÑ–)",
+ "action-smw-patternedit": "Ñ€Ñдагаваць Ð·Ð²Ñ‹Ñ‡Ð°Ð¹Ð½Ñ‹Ñ Ð²Ñ‹Ñ€Ð°Ð·Ñ‹, ÑÐºÑ–Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтоўваюцца СÑмантычнай ÐœÑдыÑВікі",
+ "smw-sp-properties-header-label": "Ð¡ÑŒÐ¿Ñ–Ñ ÑƒÐ»Ð°ÑьціваÑьцÑÑž",
+ "smw-admin-idlookup-title": "Шукаць паводле",
+ "smw-admin-iddispose-title": "Пазбаўленьне",
+ "smw-admin-idlookup-input": "Пошук:",
+ "smw-admin-objectid": "ІдÑнтыфікатар:",
+ "smw-admin-tab-general": "ÐглÑд",
+ "smw-admin-tab-notices": "Заўвагі аб ÑаÑтарÑньні",
+ "smw-admin-tab-supplement": "Ð”Ð°Ð´Ð°Ñ‚ÐºÐ¾Ð²Ñ‹Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ‹Ñ–",
+ "smw-admin-tab-registry": "РÑеÑтар",
+ "smw-livepreview-loading": "Загрузка…",
+ "smw-sp-searchbyproperty-resultlist-header": "Ð¡ÑŒÐ¿Ñ–Ñ Ð²Ñ‹Ð½Ñ–ÐºÐ°Ñž",
+ "smw-search-syntax": "СынтакÑÑ‹Ñ",
+ "smw-search-profile": "Пашырана",
+ "smw-search-profile-sort-recent": "Ð¡Ð°Ð¼Ñ‹Ñ Ð½ÑдаўніÑ",
+ "smw-search-profile-sort-title": "Ðазва",
+ "smw-search-profile-extended-help-find-forms": "даÑÑ‚ÑƒÐ¿Ð½Ñ‹Ñ Ñ„Ð¾Ñ€Ð¼Ñ‹",
+ "smw-search-profile-extended-section-sort": "Сартаваць па",
+ "smw-search-profile-extended-section-form": "Формы",
+ "smw-search-profile-extended-section-search-syntax": "Пошук увахода",
+ "smw-search-profile-extended-section-namespace": "ПраÑтора назваў",
+ "smw-search-profile-extended-section-query": "Запыт",
+ "smw-search-profile-link-caption-query": "убачыць",
+ "smw-search-show": "Паказаць",
+ "smw-search-hide": "Схаваць",
+ "log-name-smw": "Журнал СÑмантычнай MediaWiki",
+ "log-show-hide-smw": "$1 Ð»ÐµÑ‚Ð°Ð¿Ñ–Ñ Ð¡Ñмантычнай ÐœÑдыÑВікі",
+ "logeventslist-smw-log": "Ð›ÐµÑ‚Ð°Ð¿Ñ–Ñ Ð¡Ñмантычнай ÐœÑÐ´Ñ‹Ñ Ð’Ñ–ÐºÑ–",
+ "logentry-smw-maintenance": "Падзеі, зьвÑÐ·Ð°Ð½Ñ‹Ñ Ð· падтрымкай, ÑÐºÑ–Ñ Ð°Ð´Ð±Ñ‹Ð»Ñ–ÑÑ Ñž СÑмантычнай ÐœÑдыÑВікі",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|імпарт $1]]",
+ "smw-datavalue-restricted-use": "ЗначÑньне зьвеÑтак «$1» было пазначана Ð´Ð»Ñ Ð°Ð±Ð¼ÐµÐ¶Ð°Ð²Ð°Ð½Ð°Ð³Ð° карыÑтаньнÑ.",
+ "smw-datavalue-invalid-number": "«$1» Ð½Ñ Ð¼Ð¾Ð¶Ð° быць інтÑрпрÑтавана Ñк лічба.",
+ "smw-query-condition-circular": "ÐœÐ°Ð³Ñ‡Ñ‹Ð¼Ð°Ñ Ñ†Ñ‹ÐºÐ»Ñ–Ñ‡Ð½Ð°Ñ ÑžÐ¼Ð¾Ð²Ð° была знойдзена Ñž «$1».",
+ "smw-query-condition-empty": "У апіÑаньні запыту Ñ–Ñнуе пуÑÑ‚Ð°Ñ ÑžÐ¼Ð¾Ð²Ð°.",
+ "smw-types-list": "Ð¡ÑŒÐ¿Ñ–Ñ Ñ‚Ñ‹Ð¿Ð°Ñž зьвеÑтак",
+ "smw-types-default": "«$1» зьÑўлÑецца ўбудаваным тыпам зьвеÑтак.",
+ "smw-type-boo": "«$1» зьÑўлÑецца звычайным тыпам зьвеÑтак Ð´Ð»Ñ Ð°Ð¿Ñ–ÑÐ°Ð½ÑŒÐ½Ñ Ð·Ð½Ð°Ñ‡ÑÐ½ÑŒÐ½Ñ Ð¿Ñ€Ð°ÑžÐ´Ð°/хлуÑьнÑ.",
+ "smw-type-ema": "«$1» зьÑўлÑецца тыпам зьвеÑтак Ð´Ð»Ñ Ð¿Ñ€Ð°Ð´Ð°ÑÑ‚Ð°ÑžÐ»ÐµÐ½ÑŒÐ½Ñ Ð°Ð´Ñ€Ð°Ñу ÑлÑктроннай пошты.",
+ "smw-type-tab-properties": "УлаÑьціваÑьці",
+ "smw-type-tab-types": "Тыпы",
+ "smw-type-tab-errors": "Памылкі",
+ "smw-type-primitive": "ÐÑноўнаÑ",
+ "smw-type-contextual": "ЗалежныÑ",
+ "smw-limitreport-intext-parsertime": "[SMW] Ð§Ð°Ñ Ñ€Ð°Ð·Ð±Ð¾Ñ€Ñƒ ўнутрытÑкÑтавай анатацыі",
+ "smw-limitreport-intext-postproctime": "[SMW] Ñ‡Ð°Ñ Ð¿Ð¾ÑÑ‚-апрацоўкі",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|ÑÑкунда|ÑÑкундаў}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|ÑÑкунда|ÑÑкундаў}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|ÑÑкунда|ÑÑкунды|ÑÑкундаў}}",
+ "smw-datavalue-time-invalid-date-components-empty": "«$1» зьмÑшчае Ð½ÐµÐºÐ°Ñ‚Ð¾Ñ€Ñ‹Ñ Ð¿ÑƒÑÑ‚Ñ‹Ñ ÐºÐ°Ð¼Ð¿Ð°Ð½Ñнты.",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» зьÑўлÑецца нÑправільным URL-адраÑам.",
+ "smw-datavalue-parse-error": "Дадзенае значÑньне «$1» не было раÑпазнанае.",
+ "smw-no-data-available": "ÐÑма даÑтупных зьвеÑтак."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/be.json b/www/wiki/extensions/SemanticMediaWiki/i18n/be.json
new file mode 100644
index 00000000..900d92ba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/be.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "ТеÑÑ‚",
+ "Чаховіч УладзіÑлаў"
+ ]
+ },
+ "smw_printername_template": "Шаблон",
+ "types": "Тыпы",
+ "browse": "ПраглÑдзець {{GRAMMAR:родны|{{SITENAME}}}}",
+ "smw-livepreview-loading": "Счытваем…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bg.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bg.json
new file mode 100644
index 00000000..65d3e1bc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bg.json
@@ -0,0 +1,68 @@
+{
+ "@metadata": {
+ "authors": [
+ "DCLXVI",
+ "පසිඳු කà·à·€à·’න්ද",
+ "StanProg"
+ ]
+ },
+ "smw_viewasrdf": "RDF емиÑиÑ",
+ "smw_factbox_head": "Факти за $1",
+ "version-semantic": "Семантични разширениÑ",
+ "smw_printername_list": "СпиÑък",
+ "smw_printername_template": "Шаблон",
+ "smw_printername_category": "КатегориÑ",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw_iq_disabled": "СъжалÑваме. Семантичните заÑвки Ñа изключени в това уики.",
+ "smw_notitle": "„$1“ не може да Ñе използва като име на Ñтраница в това уики.",
+ "smw_wrong_namespace": "Тук Ñа позволени Ñамо Ñтраници от именното проÑтранÑтво „$1“.",
+ "smw_emptystring": "Празните низове Ñа неприемливи.",
+ "smw_nofloat": "„$1“ не е чиÑло.",
+ "exportrdf": "ИзнаÑÑне на Ñтраниците към RDF",
+ "smw_exportrdf_docu": "Тази Ñтраница позволÑва да Ñе извлечат данните опт Ñтраница в RDF формат.\nЗа изнаÑÑне на Ñтраници, въведете заглавиÑта в текÑтовата ÐºÑƒÑ‚Ð¸Ñ Ð¿Ð¾-долу (по едно заглавие на ред).",
+ "smw_exportrdf_recursive": "РекурÑивно изнаÑÑне на вÑички Ñвързани Ñтраници.\nОбърнете внимание, че резултатът може да е огромен!",
+ "smw_exportrdf_lastdate": "Без изнаÑÑне на Ñтраници, които не Ñа променÑни от поÑоченото време.",
+ "smw_exportrdf_submit": "ИзнаÑÑне",
+ "properties": "СвойÑтва",
+ "smw_property_template": "$1 от типа $2 ($3)",
+ "unusedproperties": "Ðеизползвани ÑвойÑтва",
+ "smw_unusedproperty_template": "$1 от типа $2",
+ "wantedproperties": "Желани ÑвойÑтва",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|използване|използваниÑ}})",
+ "smw_purge": "ОпреÑнÑване",
+ "types": "Типове",
+
+ "smw-statistics": "Семантични ÑтатиÑтики",
+ "ask": "Семантично Ñ‚ÑŠÑ€Ñене",
+ "smw_ask_sortby": "Сортиране по колона (незадължително)",
+ "smw_ask_ascorder": "ВъзходÑщ",
+ "smw_ask_descorder": "ÐизходÑщ",
+ "smw_ask_editquery": "Редактиране на заÑвката",
+ "smw_add_sortcondition": "[ДобавÑне на уÑловие за Ñортиране]",
+ "smw_ask_hidequery": "Скриване на заÑвката",
+ "smw_ask_queryhead": "ЗаÑвка",
+ "smw_ask_defaultformat": "по подразбиране",
+ "smw_ask_show_embed": "Показване на кода за вграждане",
+ "smw_ask_hide_embed": "Скриване на кода за вграждане",
+ "smw-ask-delete": "[Изтриване]",
+ "searchbyproperty": "ТърÑене по ÑвойÑтво",
+ "smw_sbv_docu": "ТърÑене на вÑички Ñтраници, които Ñъдържат даденото ÑвойÑтво и ÑтойноÑÑ‚.",
+ "smw_sbv_property": "СвойÑтво:",
+ "smw_sbv_value": "СтойноÑÑ‚:",
+ "smw_sbv_submit": "ТърÑене",
+ "browse": "Преглед на Ñтраница",
+ "smw_browse_go": "ОтварÑне",
+ "smw_pp_from": "От Ñтраница",
+ "smw_result_prev": "Предишни",
+ "smw_result_next": "Следващи",
+ "smw_result_results": "Резултати",
+ "smw_result_noresults": "СъжалÑваме, нÑма резултати.",
+ "smwadmin": "ÐдминиÑтраторÑки функции за Semantic MediaWiki",
+ "smw-ui-tooltip-title-info": "ИнформациÑ",
+ "smw-ui-tooltip-title-warning": "Грешка",
+ "smw-ui-tooltip-title-parameter": "Параметър",
+ "smw-ui-tooltip-title-event": "Събитие",
+ "smw-ui-tooltip-title-note": "Бележка",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw-livepreview-loading": "Зарежда Ñе…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bgn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bgn.json
new file mode 100644
index 00000000..60b97392
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bgn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Baloch Afghanistan"
+ ]
+ },
+ "browse": "ویکی بروز"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bho.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bho.json
new file mode 100644
index 00000000..d641c126
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bho.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Nepaboy",
+ "SatyamMishra"
+ ]
+ },
+ "browse": "विकि बà¥à¤°à¤¾à¤‰à¤œ करी",
+ "smw-datavalue-property-restricted-use": "समà¥à¤ªà¤¤à¤¿ $1 के वरà¥à¤œà¤¿à¤¤ पà¥à¤°à¤¯à¥‹à¤— के रà¥à¤ª में चिनà¥à¤¹à¤¿à¤¤ करल गईल बा।"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bjn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bjn.json
new file mode 100644
index 00000000..2fc54e7b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bjn.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ma'unggah..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bn.json
new file mode 100644
index 00000000..8a654cbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bn.json
@@ -0,0 +1,41 @@
+{
+ "@metadata": {
+ "authors": [
+ "Wikitanvir",
+ "Aftab1995",
+ "Aftabuzzaman"
+ ]
+ },
+ "smw_viewasrdf": "আরডিà¦à¦« ফিড",
+ "smw_finallistconjunct": ", à¦à¦¬à¦‚",
+ "smw_factbox_head": "\"$1\" সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯",
+ "smw_dsv_link": "ডিà¦à¦¸à¦­à¦¿",
+ "smw_printername_csv": "সিà¦à¦¸à¦­à¦¿ রপà§à¦¤à¦¾à¦¨à¦¿",
+ "smw_printername_dsv": "ডিà¦à¦¸à¦­à¦¿ রপà§à¦¤à¦¾à¦¨à¦¿",
+ "smw_printername_debug": "ডিবাগ কোয়েরি (বিশেষজà§à¦žà¦¦à§‡à¦° জনà§à¦¯)",
+ "smw_printername_json": "জেà¦à¦¸à¦…ন রপà§à¦¤à¦¾à¦¨à¦¿",
+ "smw_printername_list": "তালিকা",
+ "smw_printername_table": "ছক",
+ "smw_printername_broadtable": "পà§à¦°à¦¶à¦¸à§à¦¤ ছক",
+ "smw_printername_template": "টেমপà§à¦²à§‡à¦Ÿ",
+ "smw_printername_rdf": "আরডিà¦à¦« রপà§à¦¤à¦¾à¦¨à¦¿",
+ "smw_printername_category": "বিষয়শà§à¦°à§‡à¦£à§€",
+ "validator-type-class-SMWParamSource": "লেখা",
+ "smw_exportrdf_submit": "রপà§à¦¤à¦¾à¦¨à¦¿",
+ "smw_purge": "পà§à¦¨à¦ƒà¦¸à¦¤à§‡à¦œ",
+ "smw_ask_queryhead": "কোয়েরি",
+ "smw_sbv_value": "মান:",
+ "smw_sbv_submit": "ফলাফল খà§à¦à¦œà§à¦¨",
+ "browse": "উইকি বà§à¦°à¦¾à¦‰à¦œ",
+ "smw_browse_go": "চলো",
+ "smw_pp_from": "পাতা থেকে",
+ "smw_pp_submit": "ফলাফল খà§à¦à¦œà§à¦¨",
+ "smw_result_prev": "পূরà§à¦¬à¦¬à¦°à§à¦¤à§€",
+ "smw_result_next": "পরবরà§à¦¤à§€",
+ "smw_result_results": "ফলাফল",
+ "smw_result_noresults": "ফলাফল নেই।",
+ "smw_smwadmin_datarefreshbutton": "উপাতà§à¦¤ হালনাগাদ শà§à¦°à§",
+ "smw_smwadmin_datarefreshstop": "à¦à¦‡ হালানাগাদ বনà§à¦§ করà§à¦¨",
+ "smw_smwadmin_datarefreshstopconfirm": "হà§à¦¯à¦¾à¦, আমি {{GENDER:$1|নিশà§à¦šà¦¿à¦¤}}।",
+ "smw-livepreview-loading": "লোডিং..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/br.json b/www/wiki/extensions/SemanticMediaWiki/i18n/br.json
new file mode 100644
index 00000000..2c63a90d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/br.json
@@ -0,0 +1,243 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fohanno",
+ "Fulup",
+ "Gwendal",
+ "Y-M D",
+ "Dishual"
+ ]
+ },
+ "smw-desc": "Aesaat monedusted ho wiki - evit ar mekanikoù ''hag'' an dud ([https://www.semantic-mediawiki.org/wiki/Help:User_manual diellerezh enlinenn])",
+ "smw-title": "MediaWiki stervaoù",
+ "smw_viewasrdf": "Gwelet evel RDF",
+ "smw_finallistconjunct": ", ha",
+ "smw-factbox-head": "... muioc'h a-zivout \"$1\"",
+ "smw-factbox-facts": "Fedoù",
+ "smw_isspecprop": "Ar perzh-mañ zo ur perzh arbennik er wiki-mañ",
+ "smw_concept_description": "Deskrivadur eus ar meizad « $1 »",
+ "smw_no_concept_namespace": "N'hall ar meizadoù bezañ termenet nemet e pajennoù an esaouenn anv Concept:.",
+ "smw_multiple_concepts": "Pep pajenn meizad na c'hell kaout nemet un dermenadur.",
+ "smw_noinvannot": "Ne c'hell ket an talvoudennoù bezañ derannet evit eilpennañ perzhioù.",
+ "version-semantic": "Astennoù steroniezh",
+ "smw_baduri": "An URLoù er stumm « $1 » n\"int ket aotreet.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Kontañ an disoc'hoù",
+ "smw_printername_csv": "Ezporzh CSV",
+ "smw_printername_dsv": "Ezporzhiañ e DSV",
+ "smw_printername_debug": "Reked dizreinañ (evit an arbennigourien)",
+ "smw_printername_embedded": "Danvez ar pajennoù ensoc'het",
+ "smw_printername_json": "ezporzhiadur JSON",
+ "smw_printername_list": "Roll",
+ "smw_printername_plainlist": "Listenn simpl",
+ "smw_printername_ol": "Niverenniñ",
+ "smw_printername_ul": "Munudoù",
+ "smw_printername_table": "Taolenn",
+ "smw_printername_broadtable": "Taolenn ledan",
+ "smw_printername_template": "Patrom",
+ "smw_printername_templatefile": "Restr ar patrom",
+ "smw_printername_rdf": "Ezporzh RDF",
+ "smw_printername_category": "Rummad",
+ "validator-type-class-SMWParamSource": "testenn",
+ "smw-paramdesc-limit": "An niver uhelañ a zisoc'hoù da adkas",
+ "smw-paramdesc-headers": "Diskwel an talbennoù / anvioù ar perzhioù",
+ "smw-paramdesc-mainlabel": "Al label hag a ro anv ar bajenn degemer",
+ "smw-paramdesc-link": "Diskouez an talvoudoù evel liammoù",
+ "smw-paramdesc-intro": "An destenn da ziskouez a-raok disoc'hoù ar reked, ma 'z eus",
+ "smw-paramdesc-outro": "An destenn da ziskouez goude disoc'hoù ar reked, mar bez",
+ "smw-paramdesc-default": "An destenn da ziskwel ma n'eus disoc'h ebet d'ar reked",
+ "smw-paramdesc-sep": "An disranner evit an talvoudoù",
+ "smw-paramdesc-template": "Anv ar patrom d'ober gantañ evit diskouez ar moulladurioù",
+ "smw-paramdesc-columns": "An niver a golonennoù evit pere ez eus da ziskouez an disoc'hoù ($1 dre ziouer)",
+ "smw-paramdesc-userparam": "Un dalvoudenn tremenet dre pep galvadenn patromoù, pa vez implijet ur patrom",
+ "smw-paramdesc-introtemplate": "Anv ar patrom da ziskouez a-raok disoc'hoù ar reked, mar bez",
+ "smw-paramdesc-outrotemplate": "Anv ar patrom da ziskouez goude disoc'hoù ar reked, mar bez",
+ "smw-paramdesc-embedformat": "An dikedenn HTML a servij da dermeniñ an talbennoù",
+ "smw-paramdesc-embedonly": "Na ziskwel talbenn ebet",
+ "smw-paramdesc-rdfsyntax": "An ereadur RDF da vezañ implijet",
+ "smw-paramdesc-csv-sep": "An dispartier d'ober gantañ",
+ "smw-paramdesc-dsv-separator": "An dispartier d'ober gantañ",
+ "smw-paramdesc-dsv-filename": "Anv ar restr DSV",
+ "smw-paramdesc-searchlabel": "Testenn evit kenderc'hel an enklask",
+ "smw-paramdesc-export": "Dibaboù ezporzhiañ",
+ "smw-paramdesc-jsonsyntax": "Ereadur JSON da implijout",
+ "smw_iq_disabled": "Diweredekaet eo bet ar goulennoù semantek evit ar wiki-mañ.",
+ "smw_iq_moreresults": "... disoc'hoù all",
+ "smw_parseerror": "An talvoud merket n'eo ket bet komprenet.",
+ "smw_notitle": "« $1 » ne c'hall ket bezañ implijet da anv pajenn er wiki-mañ.",
+ "smw_wrong_namespace": "N'eo aotreet amañ nemet ar pajennoù eus an esaouenn \"$1\".",
+ "smw_manytypes": "Ouzhpenn ur seurt zo bet termenet evit ar perzh.",
+ "smw_emptystring": "Ne zegemerer ket an neudennadoù goullo.",
+ "smw_notinenum": "« $1 » n'emañ ket er roll talvoudoù posupl ($2) evit ar perzh-mañ.",
+ "smw_noboolean": "N'eo ket anavezet \"$1\" evel un talvoudenn Boolean (gwir/gaou).",
+ "smw_true_words": "gwir,g,ya,true",
+ "smw_false_words": "gaou,g,nann,n,false",
+ "smw_nofloat": "\"$1\" n'eo ket un niver.",
+ "smw_infinite": "Niveroù ken bras ha « $1 » n'int ket skoret.",
+ "smw_novalues": "N'eus bet diferet talvoudoù ebet.",
+ "smw_nodatetime": "An deiziad « $1 » n’eo ket bet komprenet.",
+ "smw_toomanyclosing": "Re a reveziadennoù eus « $1 » zo er reked.",
+ "smw_noclosingbrackets": "Implijout 'zo eus \"<nowiki>[[</nowiki>\" en ho reked n'int ket bet serret gant ar \"]]\" a glot.",
+ "smw_misplacedsymbol": "Implijet eo bet an arouezenn « $1 » en ul lec'h ma n'eo ket talvoudus.",
+ "smw_unexpectedpart": "Al lodenn « $1 » eus ar reked n'eo ket bet komprenet.\nDic'hortoz e c'hall bezañ an disoc'hoù.",
+ "smw_emptysubquery": "Is-rekedoù 'zo o deus un amplegad direizh.",
+ "smw_misplacedsubquery": "Is-rekedoù 'zo a zo bet implijet en ul lec'h ma n'eo aotreet is-reked ebet.",
+ "smw_valuesubquery": "Isreked n'eo ket skoret evit talvoudoù ar perzh « $1 ».",
+ "smw_badqueryatom": "Al lodennoù « <nowiki>[[…]]</nowiki> » eus ar reked n'int ket bet komprenet.",
+ "smw_propvalueproblem": "Talvoud ar perzh « $1 » n'eo ket bet komprenet.",
+ "smw_noqueryfeature": "Un nebeut arc'hweladurioù rekedoù n'int ket skoret war ar wiki-mañ hag ul lodenn outo n'int ket bet tennet ($1).",
+ "smw_notemplategiven": "Ret eo deoc'h merkañ un talvoud evit an arventenn \"patrom\" evit ma c'hallfe mont en-dro ar furmad goulenn-mañ.",
+ "smw_type_header": "Perzhioù eus ar seurt « $1 »",
+ "smw_typearticlecount": "Diskouez $1 {{PLURAL:$1|perzh|perzh}} a implij ar seurt-mañ.",
+ "smw_attribute_header": "Pajennoù a implij ar perzh « $1 »",
+ "smw_attributearticlecount": "Afficher {{PLURAL:$1|ar bajenn|an $1 pajenn}} a implij ar perzh-mañ.",
+ "smw-propertylist-subproperty-header": "Isperzhioù",
+ "smw-propertylist-redirect-header": "Heñvelsterioù",
+ "exportrdf": "Ezporzhiañ pajennoù en RDF",
+ "smw_exportrdf_lastdate": "Arabat ezporzhiañ ar pajennoù n'int ket bet kemmet abaoe ar c'houlz merket.",
+ "smw_exportrdf_submit": "Ezporzhiañ",
+ "uriresolver": "Diskoulmer URI",
+ "properties": "Perzhioù",
+ "smw_properties_docu": "Ar perzhioù-mañ zo implijet er wiki.",
+ "smw_property_template": "$1 eus ar seurt $2 ($3)",
+ "smw_propertylackspage": "An holl berzhioù a rank bezañ deskrivet gant ur bajenn !",
+ "smw_propertyhardlyused": "Ar perzh-mañ a vez implijet a-boan er wiki !",
+ "smw-special-property-searchform-options": "Dibarzhioù",
+ "smw-special-wantedproperties-filter-label": "Sil :",
+ "smw-special-wantedproperties-filter-none": "Hini ebet",
+ "smw-special-wantedproperties-filter-unapproved": "Dizaprouet",
+ "concepts": "Meizadoù",
+ "smw-special-concept-header": "Roll ar meizadoù",
+ "unusedproperties": "Perzhioù dizimplij",
+ "smw-unusedproperties-docu": "Ar perzhioù-mañ zo anezho daoust ma ne vezont implijet gant pajenn all ebet.",
+ "smw-unusedproperty-template": "$1 eus ar seurt $2",
+ "wantedproperties": "Perzhioù c'hoantaet",
+ "smw-wantedproperties-docu": "Ar perzhioù-mañ zo implijet er wiki met n'eus pajenn ebet c'hoazh evit o deskrivañ.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|implij|implij}})",
+ "smw_purge": "Freskaat",
+ "types": "Seurtoù roadennoù",
+ "smw-statistics": "Stadegoù ster",
+ "smw-statistics-query-size": "Ment ar reked",
+ "ask": "Klask semantek",
+ "smw_ask_sortby": "Urzhiañ dre vannoù (diret)",
+ "smw_ask_ascorder": "War gresk",
+ "smw_ask_descorder": "War zigresk",
+ "smw-ask-order-rand": "Dargouezhek",
+ "smw_ask_submit": "Kavout disoc'hoù",
+ "smw_ask_editquery": "Kemmañ ar reked",
+ "smw_add_sortcondition": "[ouzhpennañ un diviz urzhiañ]",
+ "smw-ask-sort-add-action": "Ouzhpennañ un diviz urzhiañ",
+ "smw_ask_hidequery": "Kuzhat ar reked",
+ "smw_ask_help": "Skoazell evit ar rekedoù",
+ "smw_ask_queryhead": "Reked",
+ "smw_ask_printhead": "Roadennoù ouzhpenn da ziskwel",
+ "smw_ask_printdesc": "(ouzhpennañ un anv perzh dre linenn)",
+ "smw_ask_format_as": "Furmadiñ evel :",
+ "smw_ask_defaultformat": "dre ziouer",
+ "smw_ask_otheroptions": "Dibarzhioù all",
+ "smw_ask_show_embed": "Diskouez ar c'hod enframmet",
+ "smw_ask_hide_embed": "Kuzhat ar c'hod enframmet",
+ "smw_ask_embed_instr": "Evit enklozañ ar reked-mañ enlinenn e-barzh ur bajenn wiki, implijit ar c'hod amañ dindan.",
+ "smw-ask-delete": "[Diverkañ]",
+ "smw-ask-sorting": "O urzhiañ",
+ "smw-ask-options": "Dibarzhioù",
+ "smw-ask-parameters": "Arventennoù",
+ "smw-ask-search": "Klask",
+ "smw-ask-result": "Disoc'h",
+ "smw-ask-format": "Furmad",
+ "searchbyproperty": "Klask dre berzh",
+ "smw_sbv_docu": "Klask an holl bajennoù dezho ur perzh hag un dalvoudenn roet.",
+ "smw_sbv_novalue": "Merkañ un talvoud evit ar perzh, pe gwelet holl dalvoudoù ar perzhioù evit \"$1\".",
+ "smw_sbv_displayresult": "Roll eus an holl bajennoù gant ar perzh « $1 » gant an talvoud « $2 ».",
+ "smw_sbv_property": "Perzh :",
+ "smw_sbv_value": "Talvoud :",
+ "smw_sbv_submit": "Kavout disoc'hoù",
+ "browse": "Furchal ar wiki",
+ "smw_browselink": "Furchal ar perzhioù",
+ "smw_browse_article": "Merkit anv ar bajenn ma vo loc'het da verdeiñ diouti.",
+ "smw_browse_go": "Mont",
+ "smw_browse_show_incoming": "diskouez ar pezhioù a gas betek amañ",
+ "smw_browse_hide_incoming": "kuzhat ar perzhioù a gas betek amañ",
+ "smw_browse_no_outgoing": "Ar bajenn-mañ n'he deus perzh ebet.",
+ "smw_browse_no_incoming": "Perzh ebet ne gas d'ar bajenn-mañ.",
+ "smw_inverse_label_default": "$1 eus",
+ "smw_inverse_label_property": "Label ar perzh eilpennet",
+ "pageproperty": "Klask e perzhioù ar bajenn",
+ "smw_pp_from": "Adalek ar bajenn",
+ "smw_pp_type": "Perzh",
+ "smw_pp_submit": "Kavout disoc'hoù",
+ "smw_result_prev": "Kent",
+ "smw_result_next": "War-lerc'h",
+ "smw_result_results": "Disoc'hoù",
+ "smw_result_noresults": "Disoc'h ebet.",
+ "smwadmin": "Arc'hwelioù merañ evit Semantic MediaWiki",
+ "smw-admin-statistics-job-title": "Stadegoù an trevelloù",
+ "smw-admin-setupsuccess": "Staliet-mat eo ar c'heflusker stokañ.",
+ "smw_smwadmin_return": "Distreiñ da $1",
+ "smw_smwadmin_updatenotstarted": "Emeur krog dija gant un argerzh hizivaat.\nArabat krouiñ unan all.",
+ "smw_smwadmin_updatestopped": "Ehanet eo bet an holl argerzhoù hizivaat.",
+ "smw-admin-db": "Staliañ ha lakaat an diaz roadennoù a-live",
+ "smw-admin-dbbutton": "Deraouekaat pe lakaat taolennoù a-live",
+ "smw-admin-announce": "Brudañ ho wiki",
+ "smw-admin-deprecation-notice-title-notice": "Kemmoù da zont",
+ "smw_smwadmin_datarefresh": "Dresañ ha lakaat ar roadennoù a-live",
+ "smw_smwadmin_datarefreshbutton": "Kregiñ da hizivaat ar roadennoù",
+ "smw_smwadmin_datarefreshstop": "Paouez gant an hizivadenn-mañ",
+ "smw_smwadmin_datarefreshstopconfirm": "Ya, sur on.",
+ "smw-admin-support": "Kaout skoazell",
+ "smw-admin-supportdocu": "Dafar a bep seurt a c'hallfe ho skoazellañ ma vez kudennoù :",
+ "smw-admin-smwhomepage": "Emañ teul titouriñ klok implijerien Semantic MediaWiki e <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "An drein a c'hall bezañ danevellet da <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Ma chom goulennoù ganeoc'h pe kinnigoù, kemerit perzh er gaoz war <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">forom implijerien Semantic MediaWiki</a>.",
+ "smw-admin-supplementary-settings-title": "Arventennoù kefluniañ",
+ "smw-admin-supplementary-elastic-statistics-title": "Stadegoù",
+ "smw_adminlinks_datastructure": "Framm ar roadennoù",
+ "smw_adminlinks_displayingdata": "Diskwel roadennoù",
+ "smw_adminlinks_inlinequerieshelp": "Skoazell evit ar rekedoù enlinenn",
+ "smw-createproperty-isproperty": "Ar perzh-mañ zo eus ar seurt $1.",
+ "smw-createproperty-allowedvals": "An talvoudenn{{PLURAL:$1||où}} aotreet evt ar perzh-se a zo :",
+ "smw-paramdesc-category-delim": "Ar bevenner",
+ "smw-info-par-message": "Kemennadenn da ziskwel.",
+ "smw-ui-tooltip-title-property": "Perzh",
+ "smw-ui-tooltip-title-quantity": "Kementad",
+ "smw-ui-tooltip-title-info": "Titouroù",
+ "smw-ui-tooltip-title-service": "Liammoù servij",
+ "smw-ui-tooltip-title-warning": "Diwallit",
+ "smw-ui-tooltip-title-error": "Fazi",
+ "smw-ui-tooltip-title-parameter": "Arventenn",
+ "smw-ui-tooltip-title-event": "Darvoud",
+ "smw-ui-tooltip-title-note": "Notenn",
+ "smw-ui-tooltip-title-legend": "Alc'hwez",
+ "smw-ui-tooltip-title-reference": "Daveenn",
+ "smw_unknowntype": "Ar seurt roadennoù \"$1\" nann skoret a zo distroet d'an doareenn",
+ "smw_concept_header": "Pajennoù ar meizad « $1 »",
+ "smw_conceptarticlecount": "Diskouez {{PLURAL:$1|ar bajenn|ar $1 pajenn}} diazezet war ar meizad-se",
+ "restriction-level-smw-pageedit": "gwarezet (implijerien dilennus nemetken)",
+ "smw-admin-idlookup-input": "Klask :",
+ "smw-admin-tab-registry": "Marilh",
+ "smw-livepreview-loading": "O kargañ...",
+ "smw-sp-searchbyproperty-resultlist-header": "Roll an disoc'hoù",
+ "smw-search-syntax": "Ereadur",
+ "smw-datavalue-external-formatter-invalid-uri": "Un URL direizh eo \"$1\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Kopiañ al liamm er pouez paper",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-loadingrecords": "O kargañ...",
+ "smw-format-datatable-processing": "O plediñ...",
+ "smw-format-datatable-search": "Klask :",
+ "smw-format-datatable-zerorecords": "Enroladenn klotaus ebet!",
+ "smw-format-datatable-first": "Kentañ",
+ "smw-format-datatable-last": "Diwezhañ",
+ "smw-format-datatable-next": "War-lerc'h",
+ "smw-format-datatable-previous": "Kent",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-property-reserved-category": "Rummad",
+ "smw-category": "Rummad",
+ "smw-help": "Skoazell",
+ "smw-processing": "O tretañ...",
+ "smw-types-title": "Doare: $1",
+ "smw-rule-page-definition": "Termenadur",
+ "smw-rule-page-description": "Deskrivadur :",
+ "smw-rule-page-type": "Seurt :"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bs.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bs.json
new file mode 100644
index 00000000..1b239f10
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bs.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "CERminator",
+ "DzWiki",
+ "Sociologist",
+ "Palapa"
+ ]
+ },
+ "smw_viewasrdf": "RDF fid",
+ "smw_finallistconjunct": ", i",
+ "smw_factbox_head": "ÄŒinjenice o $1",
+ "smw_isspecprop": "Ovo svojstvo je posebna osobina na ovoj wiki.",
+ "smw_concept_description": "Opis koncepta \"$1\"",
+ "version-semantic": "SemantiÄka proÅ¡irenja",
+ "smw_printername_count": "Rezultati brojanja",
+ "smw_printername_csv": "CSV izvoz",
+ "smw_printername_json": "JSON izvoz",
+ "smw_printername_list": "Spisak",
+ "smw_printername_ul": "Ispit stavki",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Å iroka tabela",
+ "smw_printername_template": "Å ablon",
+ "smw-paramdesc-link": "Prikaži vrijednosti kao linkove",
+ "smw-paramdesc-embedonly": "Ne prikazuj zaglavlja",
+ "smw-paramdesc-filename": "Ime za izlaznu datoteku",
+ "smw_iq_disabled": "SemantiÄki upiti su onemogućeni na ovoj wiki.",
+ "smw_iq_moreresults": "...ostali rezultati",
+ "smw_parseerror": "Navedena vrijednost nije razumljiva.",
+ "smw_emptystring": "Prazni izrazi nisu prihvatljivi.",
+ "smw_nofloat": "\"$1\" nije broj.",
+ "smw_nodatetime": "Datum \"$1\" nije razumljiv.",
+ "smw_exportrdf_submit": "Izvezi",
+ "properties": "Svojstva",
+ "unusedproperties": "Neiskorištene osobine",
+ "smw_purge": "Osvježi",
+ "types": "Tipovi",
+
+ "smw_ask_hidequery": "Sakrij upit",
+ "smw_sbv_property": "Svojstvo:",
+ "smw_sbv_value": "Vrijednost:",
+ "browse": "Pretražuj wiki",
+ "smw_browselink": "Pregled svojstava",
+ "smw_browse_go": "Idi",
+ "smw_inverse_label_default": "$1 od",
+ "smw_result_prev": "Prethodno",
+ "smw_result_next": "Slijedeće",
+ "smw_result_noresults": "Nema rezultata.",
+ "smw_smwadmin_return": "Natrag na $1",
+ "smw_smwadmin_datarefreshstopconfirm": "Da, {{GENDER:$1|siguran|sigurna}} sam.",
+ "smw-livepreview-loading": "Punjenje…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/bug.json b/www/wiki/extensions/SemanticMediaWiki/i18n/bug.json
new file mode 100644
index 00000000..15995118
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/bug.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Mallise…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ca.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ca.json
new file mode 100644
index 00000000..bf719b24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ca.json
@@ -0,0 +1,736 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dvdgmz",
+ "Loupeter",
+ "Pitort",
+ "SMP",
+ "Sociologist",
+ "Solde",
+ "Toniher",
+ "ì•„ë¼",
+ "Hiperpobla",
+ "Fitoschido",
+ "Macofe",
+ "Kghbln",
+ "Jaumeortola",
+ "Nemo bis",
+ "Abella"
+ ]
+ },
+ "smw-desc": "Fem el vostre wiki més accessible, per a les màquines ''i també'' per als humans ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentació en línia])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "No s'ha habilitat la funcionalitat de Semantic MediaWiki en aquest wiki.",
+ "smw_viewasrdf": "Canal RDF",
+ "smw_finallistconjunct": ", i",
+ "smw-factbox-head": "... més sobre «$1»",
+ "smw-factbox-facts": "Fets",
+ "smw-factbox-facts-help": "Mostra declaracions i fets creats per un usuari",
+ "smw-factbox-facts-derived": "Fets derivats",
+ "smw-factbox-facts-derived-help": "Mostra fets que s'han derivat de regles amb l'ajuda d'altres tècniques de raonament",
+ "smw_isspecprop": "Aquesta és una propietat especial en aquest wiki.",
+ "smw-concept-cache-header": "Ús de la memòria cau",
+ "smw-concept-cache-count": "La [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count memòria cau de conceptes] conté {{PLURAL:$1|'''una''' entitat|'''$1''' entitats}} ($2).",
+ "smw-concept-no-cache": "No hi ha memòria cau disponible.",
+ "smw_concept_description": "Descripció del concepte «$1».",
+ "smw_no_concept_namespace": "Els conceptes només poden ser definits en pàgines dins l'espai de noms (namespace) Concept:",
+ "smw_multiple_concepts": "Cada pàgina de concepte només pot tenir una definició de concepte.",
+ "smw_concept_cache_miss": "El concepte «$1» no pot ser utilitzat per ara, atès que la configuració del wiki s'ha de computar fora de línia.\nSi el problema persisteix passat cert temps, demaneu a l'administrador del vostre wiki que posi disponible aquest concepte.",
+ "smw_noinvannot": "No es poden assignar valors a propietats inverses.",
+ "version-semantic": "Extensions semàntiques",
+ "smw_baduri": "Les URI del formulari «$1» no són permeses.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Resultats del recompte",
+ "smw_printername_csv": "exportació a CSV",
+ "smw_printername_dsv": "Exportació DSV",
+ "smw_printername_debug": "Consulta de depuració (per a experts)",
+ "smw_printername_embedded": "Continguts de pàgina incrustada",
+ "smw_printername_json": "exportació a JSON",
+ "smw_printername_list": "Llista",
+ "smw_printername_plainlist": "Llista plana",
+ "smw_printername_ol": "Enumeració",
+ "smw_printername_ul": "Detall",
+ "smw_printername_table": "Taula",
+ "smw_printername_broadtable": "Taula ampla",
+ "smw_printername_template": "Plantilla",
+ "smw_printername_templatefile": "Fitxer de plantilla",
+ "smw_printername_rdf": "Exportació RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "text",
+ "smw-paramdesc-limit": "El nombre màxim de resultats a retornar",
+ "smw-paramdesc-offset": "El desplaçament (offset) del primer resultat",
+ "smw-paramdesc-headers": "Mostra les capçaleres / noms de les propietats",
+ "smw-paramdesc-mainlabel": "L'etiqueta que es donarà al nom de la pàgina principal",
+ "smw-paramdesc-link": "Mostra els valors com a enllaços",
+ "smw-paramdesc-intro": "El text que apareixerà abans que el resultat de la consulta, si n'hi hagués",
+ "smw-paramdesc-outro": "El text que apareixerà després que els resultats de la consulta, si n'hi hagués",
+ "smw-paramdesc-default": "El text que es mostrarà si no hi ha resultats de la consulta",
+ "smw-paramdesc-sep": "El separador entre els resultats",
+ "smw-paramdesc-propsep": "El separador entre les propietats d'una entrada de resultat",
+ "smw-paramdesc-valuesep": "El separador entre els valors d'una propietat d'un resultat",
+ "smw-paramdesc-showsep": "Mostra el separador a la part superior del fitxer CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "En comptes de mostrar tots els valors, compta les coincidències i mostra-ho.",
+ "smw-paramdesc-distributionsort": "Ordena la distribució de valor pel recompte d'aparició.",
+ "smw-paramdesc-distributionlimit": "Limita la distribució de valor al recompte de només alguns valors.",
+ "smw-paramdesc-aggregation": "Especifica amb què s'ha de relacionar l'agregació",
+ "smw-paramdesc-template": "El nom d'una plantilla amb la qual mostrar els llistats",
+ "smw-paramdesc-columns": "El nombre de columnes amb les quals mostrar els resultats",
+ "smw-paramdesc-userparam": "Es passa un valor a cada crida de la plantilla, sempre que s'utilitzi una plantilla",
+ "smw-paramdesc-class": "Una classe addicional CSS per definir a la llista",
+ "smw-paramdesc-introtemplate": "El nom d'una plantilla que es mostrarà abans dels resultats de la consulta, si n'hi ha cap",
+ "smw-paramdesc-outrotemplate": "El nom d'una plantilla que es mostrarà després dels resultats de la consulta, si n'hi ha cap",
+ "smw-paramdesc-embedformat": "L'etiqueta HTML que s'utilitza per a definir les capçaleres",
+ "smw-paramdesc-embedonly": "No mostris capçaleres",
+ "smw-paramdesc-table-class": "Una classe CSS addicional per definir per a la taula",
+ "smw-paramdesc-table-transpose": "Mostra les capçaleres de la taula verticalment i els resultats horitzontalment",
+ "smw-paramdesc-rdfsyntax": "La sintaxi RDF que s'utilitzarà",
+ "smw-paramdesc-csv-sep": "Especifica una separador de columnes",
+ "smw-paramdesc-csv-valuesep": "Especifica un separador de valors",
+ "smw-paramdesc-csv-merge": "Fusiona els valors de files i columnes amb un identificador de subjecte idèntic (p. ex., la primera columna)",
+ "smw-paramdesc-csv-bom": "Afegeix un BOM (caràcter per assenyalar l'ordre dels bytes -endianness-) a la part superior del fitxer de sortida",
+ "smw-paramdesc-dsv-separator": "El separador que s'utilitzarà",
+ "smw-paramdesc-dsv-filename": "El nom del fitxer DSV",
+ "smw-paramdesc-filename": "El nom del fitxer de sortida",
+ "smw-smwdoc-description": "Mostra una taula de tots els paràmetres que es poden utilitzar per al format del resultat especificat juntament amb els valors per defecte i les descripcions.",
+ "smw-smwdoc-default-no-parameter-list": "El format de resultat no proporciona paràmetres específics de format.",
+ "smw-smwdoc-par-format": "El format del resultat per mostrar la documentació del paràmetre.",
+ "smw-smwdoc-par-parameters": "Quins paràmetres mostrar. «specific» per a aquells afegits per format, «base» per a aquells disponibles en tots els formats, i «all» per als dos.",
+ "smw-paramdesc-sort": "Propietat per ordenar la consulta per",
+ "smw-paramdesc-order": "Ordre de la ordenació de la consulta",
+ "smw-paramdesc-searchlabel": "Text per continuar la cerca",
+ "smw-paramdesc-named_args": "Anomena els arguments que es passen a la plantilla",
+ "smw-paramdesc-template-arguments": "Especifica com els arguments amb nom es passen a la plantilla",
+ "smw-paramdesc-import-annotation": "Es copiaran dades anotades addicionals durant l'anàlisi d'un subjecte",
+ "smw-paramdesc-export": "Opció d'exportació",
+ "smw-paramdesc-prettyprint": "Una impressió agradable que mostra sagnies addicionals i salts de línia",
+ "smw-paramdesc-json-unescape": "La sortida contindrà barres sense escapar i caràcters Unicode d’octets múltiples.",
+ "smw-paramdesc-json-type": "Tipus de serialització",
+ "smw-paramdesc-source": "Font de consulta alternativa",
+ "smw-paramdesc-jsonsyntax": "Sintaxi JSON que s'utilitzarà",
+ "smw-printername-feed": "Canal RSS i Atom",
+ "smw-paramdesc-feedtype": "Tipus de canal",
+ "smw-paramdesc-feedtitle": "El text que s'utilitzarà com a títol del canal",
+ "smw-paramdesc-feeddescription": "El text que s'utilitzarà com a descripció del canal",
+ "smw-paramdesc-feedpagecontent": "Contingut de la pàgina que es mostrarà amb el canal",
+ "smw-label-feed-description": "Canal $2 $1",
+ "smw-paramdesc-mimetype": "El tipus mèdia (tipus MIME) per al fitxer de sortida",
+ "smw_iq_disabled": "Les consultes semàntiques estan inhabilitades en aquest wiki.",
+ "smw_iq_moreresults": "... més resultats",
+ "smw_parseerror": "El valor donat no s'ha entès.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "«$1» no es pot fer servir com a nom de pàgina en aquest wiki.",
+ "smw_noproperty": "No es pot utilitzar «$1» com a nom de propietat en aquest wiki.",
+ "smw_wrong_namespace": "Aquí només es poden fer servir pàgines en l'espai de noms \"$1\".",
+ "smw_manytypes": "S'ha definit més d'un tipus per la propietat.",
+ "smw_emptystring": "No s'accepten cadenes buides.",
+ "smw_notinenum": "«$1» no és a la llista ($2) de [[Property:Allows value|valors permesos]] de la propietat «$3».",
+ "smw-datavalue-constraint-error-allows-value-list": "«$1» no es troba a la llista ($2) de [[Property:Allows value|valors permesos]] de la propietat «$3».",
+ "smw-datavalue-constraint-error-allows-value-range": "«$1» no es troba en el rang «$2» especificat per la restricció «[[Property:Allows value|permet valor]]» de la propietat «$3».",
+ "smw_noboolean": "«$1» no es pot reconèixer com un valor booleà (cert/fals).",
+ "smw_true_words": "verdader,vertader,veritat,cert,true,t,sí,s,yes,y",
+ "smw_false_words": "fals,f,no,n,false",
+ "smw_nofloat": "\"$1\" no és un nombre.",
+ "smw_infinite": "No es permeten nombres tan llargs com «$1».",
+ "smw_unitnotallowed": "«$1» no està declarada com una unitat de mesura vàlida per a aquesta propietat.",
+ "smw_nounitsdeclared": "No s'ha declarat cap unitat de mesura d'aquesta propietat.",
+ "smw_novalues": "No s'ha especificat cap valor.",
+ "smw_nodatetime": "No s'ha entès la data «$1».",
+ "smw_toomanyclosing": "Sembla ser que «$1» apareix massa vegades a la consulta.",
+ "smw_noclosingbrackets": "Algun ús de \"<nowiki>[[</nowiki>\" en la vostra consulta no es clou amb els \"]]\" corresponents.",
+ "smw_misplacedsymbol": "El símbol «$1» es fa servir en un lloc on no és útil.",
+ "smw_unexpectedpart": "La part «$1» de la consulta no s'ha entès.\nEls resultats podrien no ser els esperats.",
+ "smw_emptysubquery": "Alguna subconsulta no té una condició vàlida.",
+ "smw_misplacedsubquery": "Alguna subconsulta es fa servir en un lloc on les subconsultes no són permeses.",
+ "smw_valuesubquery": "Subconsultes no suportades per valors de la propietat \"$1\".",
+ "smw_badqueryatom": "Una part «<nowiki>[[…]]</nowiki>» de la consulta no s'ha pogut entendre.",
+ "smw_propvalueproblem": "El valor de la propietat «$1» no s'ha pogut entendre.",
+ "smw_noqueryfeature": "Algun aspecte d'aquesta consulta no està suportat en aquest wiki i part de la consulta no s'ha tingut en compte ($1).",
+ "smw_noconjunctions": "Les conjuncions en consultes no són suportades en aquest wiki i part de la consulta no s'ha tingut en compte ($1)",
+ "smw_nodisjunctions": "Les disjuncions en consultes no són suportades en aquest wiki i part de la consulta no s'ha tingut en compte ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|La condició de consulta següent podria no ser considerada|Les $2 condicions de consulta següents podrien no ser considerades}} degut a les restriccions de mida o profunditat per consultes en el wiki: $1.",
+ "smw_notemplategiven": "Per fer que això funcioni, doneu un valor al paràmetre «template» (plantilla) per al format d'aquesta consulta.",
+ "smw_db_sparqlqueryproblem": "No s'ha pogut obtenir el resultat de la consulta de la base de dades SPARQL. Aquest error podria ser temporal o indicar alguna mena d'error en el programari de la base de dades.",
+ "smw_db_sparqlqueryincomplete": "Respondre la consulta ha acabat essent massa difícil i s'ha interromput. Podrien mancar-hi alguns resultats. Si fos possible, proveu millor una consulta més simple.",
+ "smw_type_header": "Propietats de tipus «$1»",
+ "smw_typearticlecount": "Es {{PLURAL:$1|mostra|mostren}} $1 {{PLURAL:$1|propietat que fa servir|propietats que fan servir}} aquesta propietat.",
+ "smw_attribute_header": "Pàgines que fan servir la propietat \"$1\"",
+ "smw_attributearticlecount": "Es {{PLURAL:$1|mostra|mostren}} $1 {{PLURAL:$1|pàgina que fa servir|pàgines que fan servir}} aquesta propietat.",
+ "smw-propertylist-subproperty-header": "Subpropietats",
+ "smw-propertylist-redirect-header": "Sinònims",
+ "smw-propertylist-error-header": "Pàgines amb assignacions no apropiades",
+ "smw-propertylist-count": "Es {{PLURAL:$1|mostra $1 entitat relacionada|mostren $1 entitats relacionades}}.",
+ "smw-propertylist-count-with-restricted-note": "Es {{PLURAL:$1|mostra $1 entitat relacionada|mostren $1 entitats relacionades}} (hi ha més disponibles però la visualització està restringida a «$2»).",
+ "smw-propertylist-count-more-available": "Es {{PLURAL:$1|mostra $1 entitat relacionada|mostren $1 entitats relacionades}} (hi ha més disponibles).",
+ "exportrdf": "Exporta les pàgines a RDF",
+ "smw_exportrdf_docu": "Aquesta pàgina permet obtenir dades en format RDF d'una pàgina del wiki.\nPer exportar pàgines, entra els títols a la caixa de text següent, un títol per línia.",
+ "smw_exportrdf_recursive": "Exporta recursivament totes les pàgines relacionades.\nTingueu en compte que el resultat pot ser molt gran!",
+ "smw_exportrdf_backlinks": "També exporta totes les pàgines que es refereixen a les pàgines exportades.\nGenera un RDF que es pot navegar.",
+ "smw_exportrdf_lastdate": "No exportis pàgines que no s'han canviat des del punt donat en el temps.",
+ "smw_exportrdf_submit": "Exporta",
+ "uriresolver": "Resolutor d'URI",
+ "properties": "Propietats",
+ "smw_properties_docu": "S'utilitzen les propietats següents al wiki.",
+ "smw_property_template": "$1 de tipus $2 ($3 {{PLURAL:$3|ús|usos}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Cal descriure totes les propietats amb una pàgina!",
+ "smw_propertylackstype": "No s'ha especificat cap tipus per a la propietat (s'assumeix el tipus $1 per ara).",
+ "smw_propertyhardlyused": "Propietats pràcticament no utilitzades al llarg del wiki!",
+ "smw-property-name-invalid": "La propietat $1 no pot fer-se servir (nom de propietat no vàlid).",
+ "smw-property-name-reserved": "«$1» està llista com a nom reservat i no s'hauria d'utilitzar com a propietat. La següent [https://www.semantic-mediawiki.org/wiki/Help:Property_naming pàgina d'ajuda] pot contenir informació sobre el motiu perquè aquest nom és reservat.",
+ "smw-sp-property-searchform": "Mostra propietats que contenen:",
+ "smw-sp-property-searchform-inputinfo": "L'entrada és sensible a les majúscules i, quan s'utilitza per al filtratge, només es mostren les propietats que coincideixen amb la condició.",
+ "smw-special-property-searchform": "Mostreu les propietats que contenen:",
+ "smw-special-property-searchform-inputinfo": "L'entrada distingeix entre majúscules i minúscules i, quan s'utilitza per filtrar, només es mostren les propietats que coincideixen amb la condició.",
+ "smw-special-property-searchform-options": "Opcions",
+ "smw-special-wantedproperties-filter-label": "Filtre:",
+ "smw-special-wantedproperties-filter-none": "Cap",
+ "smw-special-wantedproperties-filter-unapproved": "Sense aprovar",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Opció de filtratge utilitzada en connexió amb el mode d'autoritat.",
+ "concepts": "Conceptes",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts concepte] pot entendre's com una «categoria dinàmica», una col·lecció de pàgines que no es creen manualment, sinó que el Semantic MediaWiki les computa a partir de la descripció d'una consulta.",
+ "smw-special-concept-header": "Llista de conceptes",
+ "smw-special-concept-count": "Es {{PLURAL:$1|llista|llisten}} {{PLURAL:$1|el concepte|els $1 conceptes}} següents.",
+ "smw-special-concept-empty": "No s'ha trobat cap concepte.",
+ "unusedproperties": "Propietats no utilitzades",
+ "smw-unusedproperties-docu": "Aquesta pàgina llista les [https://www.semantic-mediawiki.org/wiki/Unused_properties propietats no utilitzades], que són declarades però que cap pàgina les fa servir. Per a una vista diferenciada, visiteu les pàgines especials de [[Special:Properties|totes les propietats]] o de les [[Special:WantedProperties|propietats per definir]].",
+ "smw-unusedproperty-template": "$1 del tipus $2",
+ "wantedproperties": "Propietats per definir",
+ "smw-wantedproperties-docu": "Aquesta pàgina llista les [https://www.semantic-mediawiki.org/wiki/Wanted_properties propietats per definir] que s'utilitzen en aquest wiki però que no tenen cap pàgina que les descrigui. Per a una vista diferenciada, visiteu les pàgines especials de [[Special:Properties|totes les propietats]] o de les [[Special:UnusedProperties|propietats no utilitzades]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|ús|usos}})",
+ "smw-special-wantedproperties-docu": "Aquesta pàgina llista les [https://www.semantic-mediawiki.org/wiki/Wanted_properties propietats demanades] que s'utilitzen en el wiki però que no tenen una pàgina que les descriu. Per a una vista diferenciada, vegeu les pàgines especials de [[Special:Properties|totes les propietats]] o de les [[Special:UnusedProperties|no utilitzades]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|utilització|utilitzacions}})",
+ "smw_purge": "Refresca",
+ "smw-purge-failed": "El refrescament ha fallat",
+ "types": "Tipus",
+ "smw_types_docu": "Llista de [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipus de dades disponibles], on cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipus] representa un conjunt únic d'atributs per descriure un valor en termes d'emmagatzematge i característiques de visualització que són hereditaris per a una propietat assignada.",
+ "smw-special-types-no-such-type": "El tipus de dades «$1» és desconegut o no s'ha especificat.",
+ "smw-statistics": "Estadístiques semàntiques",
+ "smw-statistics-property-instance": "Propietat {{PLURAL:$1|valor|valors}} (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propietat|Propietats}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propietat|Propietats}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propietat|Propietats}}]] (utilitzades amb com a mínim un valor)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propietat|Propietats}} (registrat amb una pàgina)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propietat|Propietats}} (assignat a un tipus de dades)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultes}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Consulta|Consultes}}]]",
+ "smw-statistics-query-size": "Mida de la consulta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concepte|Conceptes}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concepte|Conceptes}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|Té {{PLURAL:$1|subobjecte|subobjectes}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobjecte|Subobjectes}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipus de dades}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|El valor de la propietat|Els valors de la propietat}} ([[Special:ProcessingErrorList|{{PLURAL:$1|anotació impròpia|anotacions impròpies}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor de propietat|Valors de propietat}} ({{PLURAL:$1|anotació inadequada|anotacions indadequades}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entitat desactualitzada|Entitats desactualitzades}} (marcades per suprimir)",
+ "smw_uri_doc": "El resolutor d'URI implementa la [$1 cerca de W3C TAG en httpRange-14].\nTé cura que els humans no es tornin en llocs web.",
+ "ask": "Cerca semàntica",
+ "smw-ask-help": "Aquesta secció conté alguns enllaços per ajudar a explicar com utilitzar la sintaxi <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecció de pàgines]: descriu com seleccionar les pàgines i construir condicions\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadors de cerca]: llista els operadors de cerca disponibles, incloent-hi els de les consultes d'intèrval i de caràcters comodí\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Impressió d'informació]: resumeix l'ús de les opcions de declaració i formatatge",
+ "smw_ask_sortby": "Ordena per columna (opcional)",
+ "smw_ask_ascorder": "Ascendent",
+ "smw_ask_descorder": "Descendent",
+ "smw-ask-order-rand": "A l’atzar",
+ "smw_ask_submit": "Cerca'n resultats",
+ "smw_ask_editquery": "Edita la consulta",
+ "smw_add_sortcondition": "Afegeix una condició d'ordenació",
+ "smw-ask-sort-add-action": "Afegeix una condició d'ordenació",
+ "smw_ask_hidequery": "Amaga la consulta (vista compacta)",
+ "smw_ask_help": "Consulta de l'ajuda",
+ "smw_ask_queryhead": "Condició",
+ "smw_ask_printhead": "Selecció de dades a imprimir",
+ "smw_ask_printdesc": "(afegeix un nom de propietat per línia)",
+ "smw_ask_format_as": "Formata com:",
+ "smw_ask_defaultformat": "per defecte",
+ "smw_ask_otheroptions": "Altres opcions",
+ "smw-ask-otheroptions-info": "Aquesta secció conté les opcions que alteren les declaracions d'impressió. Poden veure's les descripcions dels paràmetres en passar-hi el ratolí sobre ells.",
+ "smw-ask-otheroptions-collapsed-info": "Utilitzeu la icona 'més' per mostrar totes les opcions disponibles",
+ "smw_ask_show_embed": "Mostra el codi incrustat",
+ "smw_ask_hide_embed": "Amaga el codi incrustat",
+ "smw_ask_embed_instr": "Per a incrustar aquesta consulta en línia a una pàgina wiki utilitzeu el codi a continuació.",
+ "smw-ask-delete": "Suprimeix",
+ "smw-ask-sorting": "Ordenació",
+ "smw-ask-options": "Opcions",
+ "smw-ask-options-sort": "Opcions d'ordenació",
+ "smw-ask-format-options": "Formatació i opcions",
+ "smw-ask-parameters": "Paràmetres",
+ "smw-ask-search": "Cerca",
+ "smw-ask-debug": "Depura",
+ "smw-ask-debug-desc": "Genera informació de depuració de consultes",
+ "smw-ask-no-cache": "Inhabilita la cau de consulta",
+ "smw-ask-no-cache-desc": "Resultats sense memòria cau de consulta",
+ "smw-ask-result": "Resultat",
+ "smw-ask-empty": "Buida totes les entrades",
+ "smw-ask-download-link-desc": "Baixa els resultats consultats en format $1",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Ajuda amb el format seleccionat: $1",
+ "smw-ask-condition-change-info": "S'ha alterat la condició i el motor de cerca necessita que es torni a executar la consulta per a produir resultats que coincideixin amb els nous requisits.",
+ "smw-ask-input-assistance": "Assistència d'entrada",
+ "smw-ask-condition-input-assistance": "Es proporciona [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance assistència d'entrada] per als camps d'impressió, d'ordenació i de condició. El camp de condició requereix un dels prefixos següents:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> per a recollir suggeriments de propietats (p. ex., <code>[[p:Has …</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> per a recollir suggeriments de categories",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> per a recollir suggeriments de conceptes",
+ "smw-ask-format-change-info": "S'ha modificat el format i cal tornar a executar la consulta perquè coincideixin els nous paràmetres i opcions de visualització.",
+ "smw-ask-format-export-info": "El format seleccionat és un format d'exportació que no té representació visual. Per tant, els resultats només es proporcionen com a baixada.",
+ "smw-ask-query-search-info": "La consulta <code><nowiki>$1</nowiki></code> va ser resposta per {{PLURAL:$3|1=<code>$2</code> (de la memòria cau)|<code>$2</code> (de la memòria cau)|<code>$2</code>}} en $4 {{PLURAL:$4|segon|segons}}.",
+ "searchbyproperty": "Cerca per propietat",
+ "processingerrorlist": "Llista de processament d'errors",
+ "propertylabelsimilarity": "Informe de similitud d'etiquetes de propietat",
+ "smw-processingerrorlist-intro": "La llista següent proporciona un resum dels errors de processament que han aparegut en relació amb [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Es recomana monitoritzar aquesta llista de forma regular i corregir les anotacions amb valors no vàlids.",
+ "smw_sbv_docu": "Cerca totes les pàgines que tenen una propietat i valor donats.",
+ "smw_sbv_novalue": "Introduïu un valor vàlid per a la propietat, o vegeu tots els valors de la propietat «$1».",
+ "smw_sbv_displayresult": "Una llista de totes les pàgines que tenen la propietat «$1» amb el valor «$2»",
+ "smw_sbv_displayresultfuzzy": "Una llista de totes les pàgines que tenen la propietat «$1» amb el valor «$2».\nCom hi ha hagut només uns pocs resultats, també es mostren valors propers.",
+ "smw_sbv_property": "Propietat:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Troba els resultats",
+ "browse": "Explora el wiki",
+ "smw_browselink": "Explora les propietats",
+ "smw_browse_article": "Introduïu el nom de la pàgina des d'on començar a navegar.",
+ "smw_browse_go": "Vés-hi",
+ "smw_browse_show_incoming": "Mostra les propietats entrants",
+ "smw_browse_hide_incoming": "Amaga les propietats entrants",
+ "smw_browse_no_outgoing": "La pàgina no té cap propietat.",
+ "smw_browse_no_incoming": "No enllaça cap propietat a la pàgina.",
+ "smw-browse-from-backend": "S'està recuperant la informació del servidor ara mateix.",
+ "smw-browse-intro": "Aquesta pàgina proporciona informació sobre un tema o instància d'entitat. Introduïu el nom d'un objecte per ser inspeccionat.",
+ "smw-browse-invalid-subject": "La validació del subjecte ha retornat amb un error «$1».",
+ "smw-browse-api-subject-serialization-invalid": "El subjecte té un format de serialització no vàlid.",
+ "smw-browse-js-disabled": "Se sospita que el Javascript està inhabilitat o no està disponible. Recomanem que feu servir un navegador que el pugui fer servir. Es discuteixen altres opcions a la pàgina del paràmetre de configuració [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Mostra els grups",
+ "smw-browse-hide-group": "Amaga els grups",
+ "smw-noscript": "Aquesta pàgina o acció necessita Javascript per funcionar. Habiliteu el Javascript al navegador o utilitzeu-ne un que el permeti per tal que la funcionalitat funcioni com es demana. Per a més ajuda, consulteu la pàgina d'ajuda [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiqueta de propietat inversa",
+ "pageproperty": "Cerca de les propietats de la pàgina",
+ "smw_pp_docu": "Introduïu una pàgina i una propietat, o bé només una propietat, per a recuperar-ne tots els valors assignats.",
+ "smw_pp_from": "De la pàgina:",
+ "smw_pp_type": "Propietat:",
+ "smw_pp_submit": "Troba els resultats",
+ "smw_result_prev": "Anterior",
+ "smw_result_next": "Següent",
+ "smw_result_results": "Resultats",
+ "smw_result_noresults": "No hi ha resultats.",
+ "smwadmin": "Funcions administratives i de manteniment",
+ "smw-admin-statistics-job-title": "Estadístiques de tasques",
+ "smw-admin-statistics-job-docu": "Les estadístiques de tasques mostren informació de les tasques programades al Semantic MediaWiki que encara no s'han executat. El nombre de tasques podria ser lleugerament no acurat o contenir intents fallits. Consulteu el [https://www.mediawiki.org/wiki/Manual:Job_queue manual] per a més informació.",
+ "smw-admin-statistics-querycache-title": "Estadístiques de la cau de consulta",
+ "smw-admin-statistics-querycache-disabled": "No s'ha habilitat el [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] en aquest wiki i, per tant, les estadístiques no són disponibles.",
+ "smw-admin-permission-missing": "L'accés a la pàgina ha estat blocat perquè manquen permisos. Consulteu la pàgina d'ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] per a més detalls dels paràmetres necessaris.",
+ "smw-admin-setupsuccess": "S'ha configurat el motor d'emmagatzematge.",
+ "smw_smwadmin_return": "Torna a $1",
+ "smw_smwadmin_updatestarted": "S'ha iniciat un nou procés d'actualització per a refrescar les dades semàntiques.\nEs reconstruiran totes les dades emmagatzemades o bé es repararan quan calgui.\nPodeu seguir el progrés de l'actualització en aquesta pàgina especial.",
+ "smw_smwadmin_updatenotstarted": "Ja hi ha un procés d'actualització executant-se.\nNo es crearà cap altre.",
+ "smw_smwadmin_updatestopped": "S'han aturat tots els processos d'actualització existents.",
+ "smw_smwadmin_updatenotstopped": "Per a aturar l'execució del procés d'actualització, heu de marcar la casella per a indicar que n'esteu plenament segur.",
+ "smw-admin-docu": "Aquesta pàgina especial us ajuda durant la instal·lació i l'actualització del <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> i també proporciona altres funcions i tasques administratives juntament amb estadístiques.\nRecordeu fer una còpia de seguretat de les dades importants abans d'executar cap funció administrativa.",
+ "smw-admin-environment": "Entorn de programari",
+ "smw-admin-db": "Manteniment de la base de dades",
+ "smw-admin-db-preparation": "La inicialització de la taula és en procés i pot trigar una estona fins que els resultats es mostrin segons la mida de la taula i les optimitzacions que s'hi facin.",
+ "smw-admin-dbdocu": "El Semantic MediaWiki necessita algunes extensions a la base de dades del MediaWiki per a poder emmagatzemar les dades semàntiques.\nLa funció a continuació garanteix que la vostra base de dades està configurada correctament.\nEls canvis fets en aquest pas no afecten la resta de la base de dades del MediaWiki, i poden desfer-se fàcilment si així es vol.\nLa funció de configuració pot executar-se moltes vegades sense que es faci cap mal, però només cal una vegada per a la instal·lació o l'actualització.",
+ "smw-admin-permissionswarn": "Si l'operació falla amb errors SQL, l'usuari de la base de dades que utilitza el vostre wiki (comproveu el LocalSettings.php) probablement no té suficients permisos.\nPodeu atorgar l'usuari permisos addicionals per a crear o suprimir taules, introduïu temporalment les dades d'inici de l'administrador (root) de la base de dades al LocalSettings.php, o bé feu servir l'script de manteniment <code>setupStore.php</code>, que pot utilitzar les credencials d'un administrador.",
+ "smw-admin-dbbutton": "Inicialitza o actualitza les taules",
+ "smw-admin-announce": "Feu conèixer el vostre wiki",
+ "smw-admin-announce-text": "Si el vostre wiki és públic, el podeu registrar a <a href=\"https://wikiapiary.com\">WikiApiary</a>, el catàleg wiki de wikis.",
+ "smw-admin-deprecation-notice-title": "Avisos d'obsolescència",
+ "smw-admin-deprecation-notice-docu": "La secció següent conté paràmetres que són obsolets o en desús però que s'han detectat com a actius en aquest wiki. S'espera que en versions futures se suprimirà el suport per a aquestes configuracions.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> és obsolet i se suprimirà en $2",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> és obsolet i se suprimirà en $2",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> està essent reemplaçat per <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> va ser suprimit a $2",
+ "smw-admin-deprecation-notice-title-notice": "Canvis propers",
+ "smw-admin-deprecation-notice-title-notice-explanation": "S'han detectat els paràmetres següents en aquest wiki, els quals s'ha planejat que se suprimiran o canviaran en una versió futura.",
+ "smw-admin-deprecation-notice-title-replacement": "Paràmetres substituïts o canviats de nom",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "La secció següent conté paràmetres que van ser reanomenats o bé que s'han modificat. Es recomana que se n'actualitzi el seu nom o format immediatament.",
+ "smw-admin-deprecation-notice-title-removal": "Paràmetres suprimits",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Els paràmetres llistats van ser suprimits en una versió anterior, però s'ha detectat que encara s'utilitzen en aquest wiki.",
+ "smw-smwadmin-refresh-title": "Reparació de les dades i actualització",
+ "smw_smwadmin_datarefresh": "Reconstrucció de les dades",
+ "smw_smwadmin_datarefreshdocu": "És possible restaurar totes les dades del Semantic MediaWiki a partir dels continguts actuals del wiki.\nAixò pot ser útil per a reparar dades inconsistents o refrescar les dades si el format intern ha canviat per alguna actualització de programari.\nL'actualització s'executa pàgina per pàgina i no es completarà immediatament.\nA continuació és mostra si l'actualització és en curs i us permet iniciar o aturar les actualitzacions (a menys que aquesta característica estigui inhabilitada per l'administrador del lloc).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Ja hi ha una actualització en curs.</strong>\nÉs normal que l'actualització progressi només lentament perquè només refresca les dades en trossos petits cada vegada que l'usuari accedeix al wiki.\nPer a finalitzar l'actualització més ràpidament, podeu cridar l'script de manteniment del MediaWiki <code>runJobs.php</code> (empreu l'opció <code>--maxjobs 1000</code> per a restringir el nombre d'actualitzacions en un lot).\nProgrés estimat de l'actualització actual:",
+ "smw_smwadmin_datarefreshbutton": "Programa la reconstrucció de les dades",
+ "smw_smwadmin_datarefreshstop": "Atura l'actualització",
+ "smw_smwadmin_datarefreshstopconfirm": "Sí, estic {{GENDER:$1|segur|segura}}.",
+ "smw-admin-job-scheduler-note": "La majoria d'activitats d'aquesta secció es realitzen com a tasques per tal d'evitar situacions de bloqueig durant l'execució. El [https://www.mediawiki.org/wiki/Manual:Job_queue planificador de tasques] és responsable de processar-les i és crític que l'script de manteniment <code>runJobs.php</code> (vegeu també el paràmetre de configuració <code>$wgRunJobsAsync</code>) tingui una capacitat adequada.",
+ "smw-admin-outdateddisposal-title": "Eliminació d'entitats obsoletes",
+ "smw-admin-outdateddisposal-intro": "Algunes activitats (un canvi de tipus de propietat, l'eliminació de pàgines wiki o la correcció de valors erronis) resultarà en [https://www.semantic-mediawiki.org/wiki/Outdated_entities entitats obsoletes] i se suggereix suprimir-les periòdicament per tal d'alliberar l'espai de taula associat.",
+ "smw-admin-outdateddisposal-active": "S'ha posat en cua una tasca d'eliminació d'entitats obsoletes.",
+ "smw-admin-outdateddisposal-button": "Programa una eliminació",
+ "smw-admin-feature-disabled": "S'ha inhabilitat aquesta funcionalitat en el wiki, consulteu la pàgina d'ajuda dels <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">paràmetres de configuració</a> o contacteu amb l'administrador del sistema.",
+ "smw-admin-propertystatistics-title": "Reconstrucció de les estadístiques de les propietats",
+ "smw-admin-propertystatistics-active": "S'ha programat una tasca de reconstrucció de les estadístiques.",
+ "smw-admin-propertystatistics-button": "Programa la reconstrucció de les estadístiques",
+ "smw-admin-fulltext-title": "Reconstrucció de la cerca en text complet",
+ "smw-admin-fulltext-intro": "Reconstrueix l'índex de cerca de les taules de propietats amb un tipus de dades [https://www.semantic-mediawiki.org/wiki/Full-text cerca de text complet] habilitat. Els canvis a les regles d'indexació (separadors modificats, noves arrels, etc.) i/o taules noves o afegides necessiten que es torni a executar la tasca de nou.",
+ "smw-admin-fulltext-active": "S'ha programat una tasca de reconstrucció de cerca de text complet.",
+ "smw-admin-fulltext-button": "Programa una reconstrucció de text sencer",
+ "smw-admin-support": "Com obtenir assistència",
+ "smw-admin-supportdocu": "Es proporcionen diferents recursos per ajudar-vos en cas de problemes:",
+ "smw-admin-installfile": "Si us trobeu amb problemes amb la vostra instal·lació, comenceu comprovant les instruccions del <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">fitxer INSTALL</a> i la <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">pàgina d'instal·lació</a>.",
+ "smw-admin-smwhomepage": "Podeu trobar la documentació d'usuari completa del Semantic MediaWiki a <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Es poden enviar informes d'error al <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">gestor d'incidències del Github</a>. La pàgina d'<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">informe d'errors</a> proporciona una guia de com escriure un informe d'error d'una forma eficient.",
+ "smw-admin-questions": "Si teniu més preguntes o suggeriments, uniu-vos a la discussió a la <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">llista de correu d'usuaris del Semantic MediaWiki</a> o a la <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">sala de xat</a>.",
+ "smw-admin-other-functions": "Altres funcions",
+ "smw-admin-supplementary-section-title": "Funcions suplementàries",
+ "smw-admin-supplementary-section-subtitle": "Funcions disponibles",
+ "smw-admin-supplementary-section-intro": "Aquesta secció proporciona funcions addicionals més enllà de l'abast de les tasques de manteniment i és possible que algunes de les funcions llistades en la [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentació] estiguin restringides o no disponibles i, per tant, siguin inaccessibles en aquest wiki.",
+ "smw-admin-supplementary-settings-title": "Paràmetres de configuració",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> retorna una llista col·lectiva de paràmetres disponibles utilitzats a Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Estadístiques operacionals",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> mostra un conjunt estès d'estadístiques",
+ "smw-admin-supplementary-idlookup-title": "Cerca i eliminació d'entitats",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> conté funcions per a cercar i eliminar entitats individuals",
+ "smw-admin-supplementary-duplookup-title": "Entitats duplicades",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> per a llistar les entrades que estan categoritzades per contenir duplicats en la taula d'entitats",
+ "smw-admin-supplementary-duplookup-docu": "Aquesta pàgina llista entrades de [https://www.semantic-mediawiki.org/wiki/Help:Entity_table taules d'entitats] que s'han categoritzat com a duplicades. Les entrades duplicades, si mai n'hi ha, haurien de tenir lloc només en rares ocasions a causa d'un procés terminat durant una actualització de base dades o d'una transacció de reversió que ha fallat.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estadístiques de la memòria cau",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> mostra estadístiques relacionades amb la memòria cau",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informa de paràmetres i estadístiques d'indexació",
+ "smw-admin-supplementary-elastic-docu": "Aquesta pàgina conté informació de configuració, correspondència, salut i estadístiques d'indexació relacionades amb un clúster d'Elasticsearch que està connectat a Semantic MediaWiki i el seu [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Funcions disponibles",
+ "smw-admin-supplementary-elastic-settings-title": "Paràmetres",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> utilitzat per Elasticsearch per gestionar els índexs de Semantic MediaWiki",
+ "smw-admin-supplementary-elastic-mappings-title": "Correspondències",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> per llistar índex i correspondències de camps",
+ "smw-admin-supplementary-elastic-mappings-docu": "Aquesta pàgina conté detalls de la correspondència de camps que s'utilitzen amb els índexs actuals. Caldria monitoritzar el resum de la correspondència en connexió amb <code>index.mapping.total_fields.limit</code>, que especifica el nombre màxim de camps en un índex.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resum",
+ "smw-admin-supplementary-elastic-mappings-fields": "Correspondències de camp",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodes",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> mostra les estadístiques de node",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndexs",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> proporciona un resum dels índexs disponibles i llurs estadístiques",
+ "smw-admin-supplementary-elastic-statistics-title": "Estadístiques",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> mostra estadístiques de nivell d'índex",
+ "smw-admin-supplementary-elastic-status-replication": "Estat de replicació",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Darrera replicació activa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Interval de refrescament: $1",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replicació bloquejada: $1 (reconstrucció en procés)",
+ "smw-list-count": "La llista conté $1 {{PLURAL:$1|entrada|entrades}}.",
+ "smw-list-count-from-cache": "La llista conté $1 {{PLURAL:$1|entrada|entrades}} i s'ha recuperat de la memòria cau (UTC: $2).",
+ "smw-property-label-similarity-title": "Informe de similitud d'etiquetes de propietat",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula les similituds de les etiquetes de propietats existents",
+ "smw-property-label-similarity-threshold": "Llindar:",
+ "smw-property-label-similarity-type": "Mostra l'ID de tipus",
+ "smw-property-label-similarity-noresult": "No s'han trobat resultats per a les opcions seleccionades.",
+ "smw_adminlinks_datastructure": "Estructura de dades",
+ "smw_adminlinks_displayingdata": "Visualització de les dades",
+ "smw_adminlinks_inlinequerieshelp": "Ajuda de les consultes en línia",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Recompte d'ús] estimat: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propietat definida {{PLURAL:$1|per l'usuari|pel sistema}}",
+ "smw-property-indicator-last-count-update": "Recompte d'ús estimat\nDarrera actualització: $1",
+ "smw-concept-indicator-cache-update": "Recompte de la memòria cau\nDarrera actualització: $1",
+ "smw-createproperty-isproperty": "És una propietat del tipus $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|El valor permès per a aquestapropietat és|Els valors permesos per a aquestes propietats són}}:",
+ "smw-paramdesc-category-delim": "El delimitador",
+ "smw-paramdesc-category-template": "Una plantilla per donar format als elements amb",
+ "smw-paramdesc-category-userparam": "Un paràmetre per passar a la plantilla",
+ "smw-info-par-message": "Missatge per mostrar.",
+ "smw-info-par-icon": "La icona que es mostrarà, o bé «info», o bé «avís».",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Opcions generals",
+ "prefs-ask-options": "Opcions de Special:Ask",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (i extensions relacionades) proporcionen personalització individual per a algunes functions seleccionades. Per obtenir més informació, feu una ullada a aquesta [https://www.semantic-mediawiki.org/wiki/Help:User_preferences secció d'ajuda].",
+ "smw-prefs-ask-options-tooltip-display": "Mostra el text del paràmetre en un rètol flotant informatiu",
+ "smw-prefs-ask-options-compact-view-basic": "Habilita la vista compacta bàsica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Si s'habilita, mostra un conjunt reduït d'enllaços en la vista compacta d'Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Habilita la correcció horària per a les pàgines especials que utilitzin la preferència de [[Special:Preferences#mw-prefsection-rendering|decalatge horari]] local",
+ "smw-prefs-general-options-jobqueue-watchlist": "Mostra la llista de seguiment de la cua de tasques a la meva barra personal",
+ "smw-prefs-general-options-disable-editpage-info": "Inhabilita el text introductori en la pàgina d'edició",
+ "smw-prefs-general-options-disable-search-info": "Inhabilita la informació d'ajuda a la sintaxi en la pàgina estàndard de cerca",
+ "smw-prefs-general-options-suggester-textinput": "Habilita l'assistència d'entrada per a les entitats semàntiques",
+ "smw-ui-tooltip-title-property": "Propietat",
+ "smw-ui-tooltip-title-quantity": "Conversió d'unitat",
+ "smw-ui-tooltip-title-info": "Informació",
+ "smw-ui-tooltip-title-service": "Enllaços de servei",
+ "smw-ui-tooltip-title-warning": "Avís",
+ "smw-ui-tooltip-title-error": "Error",
+ "smw-ui-tooltip-title-parameter": "Paràmetre",
+ "smw-ui-tooltip-title-event": "Esdeveniment",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Llegenda",
+ "smw-ui-tooltip-title-reference": "Referència",
+ "smw_unknowntype": "El tipus «$1» d'aquesta propietat no és vàlid",
+ "smw-concept-cache-text": "El concepte té un total {{PLURAL:$1|d'$1 pàgina|de $1 pàgines}} i es va actualitzar per darrera vegada el $3, $2.",
+ "smw_concept_header": "Pàgines del concepte \"$1\"",
+ "smw_conceptarticlecount": "Es {{PLURAL:$1|mostra|mostren}} a continuació $1 {{PLURAL:$1|pàgina|pàgines}}.",
+ "smw-qp-empty-data": "No s'han pogut mostrar les dades sol·licitades per manca de criteris de selecció suficients.",
+ "right-smw-admin": "Accedeix a les tasques d'administració (Semantic MediaWiki)",
+ "right-smw-patternedit": "Modifica l'accés per a mantenir les expressions regulars i patrons permesos (Semantic MediaWiki)",
+ "right-smw-pageedit": "Accés d'edició per a pàgines anotades amb <code>Is edit protected</code> (Semantic MediaWiki)",
+ "right-smw-ruleedit": "Edita les pàgines de regles (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "protegit (només usuaris elegibles)",
+ "action-smw-patternedit": "edita les expressions regulars que utilitza Semantic MediaWiki",
+ "action-smw-pageedit": "modifica pàgines anotades amb <code>Is edit protected</code> (Semantic MediaWiki)",
+ "group-smwadministrator": "Administradors (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrador|administratora}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administrators (Semantic MediaWiki)",
+ "group-smwcurator": "Curadors (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|conservador (Semantic MediaWiki)|conservadora (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Conservadors (Semantic MediaWiki)",
+ "action-smw-admin": "accedeix a les tasques d'administració de Semantic MediaWiki",
+ "action-smw-ruleedit": "edita les pàgines de regla (Semantic MediaWiki)",
+ "smw-property-predefined-default": "«$1» és una propietat predefinida.",
+ "smw-property-predefined-common": "Aquesta propietat està predesplegada (també coneguda com a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propietat especial]) i ve amb privilegis administratius addicionals, però pot utilitzar-se com qualsevol altra [https://www.semantic-mediawiki.org/wiki/Property propietat definida per l'usuari].",
+ "smw-property-predefined-ask": "«$1» és una propietat predefinida que representa metainformació (en la forma de [https://www.semantic-mediawiki.org/wiki/Subobject subobjectes]) sobre consultes individuals i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "«$1» és una propietat predefinida que recull el nombre de condicions que s'utilitzen en una consulta i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "«$1» és una propietat predefinida que informa de la profunditat d'una consulta i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "És un valor numèric computat en la base de la subconsulta niuada, cadenes de propietat, i elements de descripció disponibles amb l'execució d'una consulta que és restringida pel paràmetre de configuració <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-long-askpa": "És part d'una col·lecció de propietats que especifiquen un [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Aquesta pàgina llista les [https://www.semantic-mediawiki.org/wiki/Property propietats] i el seu recompte d'ús en el wiki. Per a una estadística al dia, és recomanable córrer l'script de manteniment [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics rebuildPropertyStatistics] de forma regular. Per a una visió diferenciada, vegeu les pàgines especials de les [[Special:UnusedProperties|propietats no utilitzades]] o les [[Special:WantedProperties|propietats per definir]].",
+ "smw-sp-properties-cache-info": "Les dades llistades s'han recuperat de la [https://www.semantic-mediawiki.org/wiki/Caching memòria cau], i s'han actualitzat el $1.",
+ "smw-sp-properties-header-label": "Llista de propietats",
+ "smw-admin-settings-docu": "Mostra una llista de tots els paràmetres per defecte i localitzats que són rellevants per a un entorn de Semantic MediaWiki. Per a més detalls sobre cada paràmetre en particular, consulteu la pàgina d'ajuda de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuració].",
+ "smw-sp-admin-settings-button": "Genera la llista de paràmetres",
+ "smw-admin-idlookup-title": "Consulta",
+ "smw-admin-idlookup-docu": "Aquesta secció mostra els detalls tècnics d'una entitat individual (pàgina wiki, subobjecte, propietat, etc.) a Semantic MediaWiki. L'entrada pot ser un ID numèric o un valor de cadena per coincidir amb el camp de cerca rellevant, tot i que qualsevol referència ID està relacionada amb Semantic MediaWiki i no la pàgina de MediaWiki o l'ID de la revisió.",
+ "smw-admin-iddispose-title": "Alliberament",
+ "smw-admin-iddispose-done": "L'ID «$1» s'ha suprimit del servidor d'emmagatzematge.",
+ "smw-admin-iddispose-references": "L'ID «$1» {{PLURAL:$2|no té cap|té com a mínim una}} referència activa:",
+ "smw-admin-iddispose-references-multiple": "Llista de coincidències amb com a mínim un registre de referència actiu.",
+ "smw-admin-iddispose-no-references": "La cerca no ha pogut trobar «$1» a cap entrada de taula.",
+ "smw-admin-idlookup-input": "Cerca:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Resum",
+ "smw-admin-tab-notices": "Avisos d’obsolescència",
+ "smw-admin-tab-supplement": "Funcions suplementàries",
+ "smw-admin-tab-registry": "Registre",
+ "smw-livepreview-loading": "S'està carregant…",
+ "smw-sp-searchbyproperty-description": "Aquesta pàgina proporciona una simple [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interfície de navegació] per trobar entitats descrites per una propietat i un valor anomenat. Altres interfícies de cerca disponibles inclouen la [[Special:PageProperty|cerca de propietats de pàgina]] i el [[Special:Ask|generador de consultes ask]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Llista de resultats",
+ "smw-sp-searchbyproperty-nonvaluequery": "Una llista de valors que tenen la propietat «$1» assignada.",
+ "smw-sp-searchbyproperty-valuequery": "Una llista de pàgines que tenen la propietat «$1» amb el valor «$2» anotat.",
+ "smw-datavalue-number-textnotallowed": "«$1» no pot assignar-se a un tipus de nombre declarat amb el valor $2.",
+ "smw-datavalue-number-nullnotallowed": "«$1» ha tornat «NULL», que no és un nombre vàlid.",
+ "smw-editpage-annotation-enabled": "Aquesta pàgina permet anotacions semàntiques en el text (p. ex., <nowiki>«[[Is specified as::World Heritage Site]]» (es considera com patrimoni de la Humanitat)</nowiki>) per a crear contingut estructurat i consultable proporcionat per Semantic MediaWiki. Per a una descripció detallada de com utilitzar les anotacions o la funció d'anàlisi #ask consulteu les pàgines d'ajuda [https://www.semantic-mediawiki.org/wiki/Help:Getting_started Primers passos], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation Anotació en el text] o [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries Consultes en el text].",
+ "smw-editpage-annotation-disabled": "Aquesta pàgina no permet incloure anotacions semàntiques en el text per restriccions de l'espai de noms. Teniu més detalls de com habilitar l'espai de noms a la pàgina d'ajuda de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuració].",
+ "smw-editpage-property-annotation-enabled": "Aquesta propietat pot ampliar-se mitjançant anotacions semàntiques per a especificar un tipus de dades (p. ex.,<nowiki>«[[</nowiki>Té tipus::Pàgina]]») o d'altres declaracions d'aquesta mena (p. ex.,<nowiki>«[[</nowiki>Subpropietat de::dc:date]]»). Per a una descripció sobre com augmentar aquesta pàgina, consulteu les pàgines d'ajuda de [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration Declaració d'una propietat] o [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes Llista de tipus de dades].<!--[[Has type::Page]], [[Subproperty of::dc:date]]-->",
+ "smw-editpage-property-annotation-disabled": "Aquesta propietat no pot ampliar-se amb una anotació de tipus de dades (p. ex., <nowiki>«[[</nowiki>Té tipus::Pàgina]]») perquè ja està predefinida (consulteu la pàgina d'ajuda de les [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propietats especials] per a més informació).<!-- [[Has type::Page]]-->",
+ "smw-editpage-concept-annotation-enabled": "Aquest concepte pot ser ampliat fent servir la funció d'anàlisi #concept. Per a una descripció de com utilitzar-la, consulteu la pàgina d'ajuda sobre els [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceptes].",
+ "smw-search-help-structured": "Cerques estructurades:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (comme [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context context filtrat])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (avec un [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context context de cerca])",
+ "smw-search-help-proximity": "Cerques de proximitat (una propietat que sigui desconeguda, '''només''' disponible per a aquells sistemes que proporcionen integració de cerca de text complet):\n\n* <code><nowiki>[[in:lorem ipsum]]</nowiki></code> (cerca a tots els documents «lorem» i «ipsum» que han estat indexats)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (troba la coincidència «lorem ipsum» com a frase)",
+ "smw-search-input": "Entrada i cerca",
+ "smw-search-syntax": "Sintaxi",
+ "smw-search-profile": "Estès",
+ "smw-search-profile-tooltip": "Funcions de cerca en connexió amb Semantic MediaWiki",
+ "smw-search-profile-sort-best": "Millor coincidència",
+ "smw-search-profile-sort-recent": "Més recent",
+ "smw-search-profile-sort-title": "Títol",
+ "smw-search-profile-extended-help-intro": "El [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile perfil estès] Special:Search proporciona accés a les funcions de cerca específiques de Semantic MediaWiki i del seu motor de consulta.",
+ "smw-search-profile-extended-help-sort": "Especifica una preferència d'ordenació per a la mostra de resultats amb:",
+ "smw-search-profile-extended-help-sort-title": "* «Títol» fent servir el títol de la pàgina (o el títol mostrat) com a criteri d'ordenació",
+ "smw-search-profile-extended-help-sort-recent": "* «Més recent» mostrà les entrades modificades més recentment abans (les entitats que són subobjectes se suprimiran perquè no estan anotades amb una[[Property:Modification date|data de modificació]])",
+ "smw-search-profile-extended-help-form": "Els formularis es proporcionen (sempre que es mantinguin al dia) per contenir correspondències de casos d'ús específics tot exposant diferents propietats i camps de valor per ajustar els processos d'entrada i facilitar als usuaris procedir amb la sol·licitud de cerca (vegeu $1).",
+ "smw-search-profile-extended-help-namespace": "El quadre de selecció d'espai de noms s'amagarà tan aviat com se seleccioni el formulari, però es pot fer visible amb l'ajuda del botó «mostra/amaga».",
+ "smw-search-profile-extended-help-search-syntax": "El camp d'entrada de cerca permet l'ús de sintaxi <code>#ask</code> per definir un context de cerca específic de Semantic MediaWiki. Les expressions útils inclouen:",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> per no fer correspondència amb cap entitat que inclou \"...\"",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Prefixos personalitzats addicionals són disponibles i definits com: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Algunes expressions són reservades, com ara: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "«Algunes de les operacions llistades només són útils en connexió amb un índex de text complet habilitat o bé amb l'ElasticStore.»",
+ "smw-search-profile-extended-help-query": "S'ha utilitzat <code><nowiki>$1</nowiki></code> com a consulta.",
+ "smw-search-profile-extended-help-query-link": "(Per a més detalls $1).",
+ "smw-search-profile-extended-help-find-forms": "formularis disponibles",
+ "smw-search-profile-extended-section-sort": "Ordena per",
+ "smw-search-profile-extended-section-form": "Formularis",
+ "smw-search-profile-extended-section-search-syntax": "Entrada de cerca",
+ "smw-search-profile-extended-section-namespace": "Espai de noms",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "smw-search-profile-link-caption-query": "vegeu",
+ "smw-search-show": "Mostra",
+ "smw-search-hide": "Amaga",
+ "log-name-smw": "Registre de Semantic MediaWiki",
+ "log-show-hide-smw": "Registre de Semantic MediaWiki $1",
+ "logeventslist-smw-log": "Registre de Semantic MediaWiki",
+ "log-description-smw": "Activitats dels [https://www.semantic-mediawiki.org/wiki/Help:Logging tipus d'esdeveniments habilitats] que han estat informats per Semantic MediaWiki i els seus components.",
+ "logentry-smw-maintenance": "Esdeveniments relacionats amb el manteniment emesos per Semantic MediaWiki",
+ "smw-datavalue-import-unknown-namespace": "L'espai de noms d'importació «$1» és desconegut. Assegureu-vos que els detalls d'importació OWL són disponibles a través de [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "No s'ha pogut trobat un URI de l'espai de noms «$1» en [[MediaWiki:Smw import $1|la importació de $1]].",
+ "smw-datavalue-import-missing-type": "No s'ha trobat definició de tipus per a «$1» a la [[MediaWiki:Smw import $2|importació de $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|importació de $1]]",
+ "smw-datavalue-import-invalid-value": "«$1» no és format vàlid i s'espera que consisteixi de «espai de noms»:«identificador» (p. ex. «foaf:name»).",
+ "smw-datavalue-import-invalid-format": "S'esperava que la cadena «$1» estigués dividida en quatre parts però no s'ha entès el format.",
+ "smw-property-predefined-impo": "«$1» és una propietat predefinida que descriu una relació amb un [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulari importat] i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "«$1» és una propietat predefinida que descriu el [[Special:Types|tipus de dades]] d'una propietat i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "«$1» és una propietat predefinida que representa un constructe de [https://www.semantic-mediawiki.org/wiki/Help:Container contenidor] i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "El contenidor permet acumular assignacions de propietat-valor similars al d'una pàgina wiki normal però en un espai d'entitat diferent mentre que està enllaçat al subjecte que l'inclou.",
+ "smw-property-predefined-errp": "«$1» és una propietat predefinida per a resseguir errors d'entrada de valors d'anotacions irregulars i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-errp": "En la major part dels casos es deu a un error de correspondència de tipus o una restricció de [[Property:Allows value|valor]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value «$1»] és una propietat predefinida que defineix una llista de valors permesos per tal de restringir les assignacions de valor d'una propietat i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-property-restricted-annotation-use": "La propietat «$1» té una àrea d'aplicació restringida i els usuaris no poden utilitzar-la com a propietat d'anotació.",
+ "smw-datavalue-property-restricted-declarative-use": "La propietat «$1» és una propietat declarativa i només pot utilitzar-se en una pàgina de propietat o categoria.",
+ "smw-datavalue-property-create-restriction": "La propietat «$1» no existeix i l'usuari no té el permís «$2» (consulteu [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode el mode d'autoritat]) per crear o anotar valors amb una propietat no aprovada.",
+ "smw-datavalue-property-invalid-character": "«$1» conté un caràcter «$2» llistat com a part d'una etiqueta de propietat i que s'ha classificat llavors com a no vàlid.",
+ "smw-datavalue-property-invalid-chain": "L'ús de «$1» com a cadena de propietat no està permès durant el procés d'anotació.",
+ "smw-datavalue-restricted-use": "El valor de dades s'ha marcat per a ús restringit «$1».",
+ "smw-datavalue-invalid-number": "«$1» no es pot interpretar com un nombre.",
+ "smw-query-condition-circular": "S'ha detectat una possible condició circular a «$1».",
+ "smw-query-condition-empty": "La descripció de la consulta té una condició en blanc.",
+ "smw-types-list": "Llista de tipus de dades",
+ "smw-types-default": "«$1» és un tipus de dades integrat.",
+ "smw-types-help": "Es poden trobar més informació i exemples a la [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 pàgina d'ajuda].",
+ "smw-type-anu": "«$1» és una variant del tipus de dades [[Special:Types/URL|URL]] i s'utilitza sobretot per a una declaració d'exportació de ''owl:AnnotationProperty''.",
+ "smw-type-boo": "«$1» és un tipus de dades bàsic per descriure un valor de veritable/fals.",
+ "smw-type-cod": "«$1» és una variant del tipus de dades [[Special:Types/Text|Text]] per utilitzar-lo amb texts tècnics de longitud arbitrària, com ara llistats de codi font.",
+ "smw-type-geo": "«$1» és un tipus de dades que descriu ubicacions geogràfiques i que necessita l'extensió [https://www.semantic-mediawiki.org/wiki/Extension:Maps Maps].",
+ "smw-type-tel": "«$1» és un tipus de dades especial per a descriure números de telèfon internacionals d'acord amb el RFC 3966.",
+ "smw-type-txt": "«$1» és un tipus de dades bàsic per descriure cadenes de longitud arbitrària.",
+ "smw-type-dat": "«$1» és un tipus de dades bàsic per representar punts en el temps en un format unificat.",
+ "smw-type-ema": "«$1» és un tipus de dades especial que representa una adreça electrònica.",
+ "smw-type-tem": "«$1» és un tipus de dades numèric especial que representa una temperatura.",
+ "smw-type-qty": "«$1» és un tipus de dades per descriure quantitats amb una representació numèrica i una unitat de mesura.",
+ "smw-type-rec": "«$1» és un tipus de dades de contenidor que especifica una llista de propietats amb tipus en un ordre fix.",
+ "smw-type-extra-tem": "L'esquema de conversió inclou unitats permeses, com ara Kelvin, Celsius, Fahrenheit i Rankine.",
+ "smw-type-tab-properties": "Propietats",
+ "smw-type-tab-types": "Tipus",
+ "smw-type-tab-errors": "Errors",
+ "smw-type-primitive": "Bàsic",
+ "smw-type-contextual": "Contextual",
+ "smw-type-compound": "Compost",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-description": "Aquesta pàgina proporciona una interfície de navegació por trobar tots els valors d'una propietat i una pàgina donada. Altres interfícies de cerca disponibles inclouen la [[Special:SearchByProperty|cerca de propietat]] i el [[Special:Ask|constructor de consultes]].",
+ "smw-property-predefined-errc": "«$1» és una propietat predefinida proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] i que representa errors que van aparèixer en connexió amb anotacions de valors o processament d'entrades no adequats.",
+ "smw-property-predefined-long-errc": "Els errors es recullen en un [https://www.semantic-mediawiki.org/wiki/Help:Container contenidor] que pot incloure una referència a la propietat que ha causat la discrepància.",
+ "smw-property-predefined-errt": "«$1» és una propietat predefinida que conté una descripció textual d'un error i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "Un subobjecte definit per l'usuari que conté un esquema de noms no vàlid. La notació de punt ($1) utilitzada en els primers cinc caràcters està reservada per a extensions. Podeu definir un [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador amb nom].",
+ "smw-datavalue-record-invalid-property-declaration": "La definició del registre conté la propietat «$1» que és declarada ella mateixa com a de tipus registre, i això no és permès.",
+ "smw-property-predefined-mime": "«$1» és una propietat predefinida que descriu el tipus MIME d'un fitxer carregat i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-monolingual-dataitem-missing": "Manca un element esperat per a construir un valor compost monolingüe.",
+ "smw-datavalue-languagecode-missing": "Per a l'anotació «$1», l'analitzador no ha pogut determinar el codi de la llengua (p. ex., «foo@en»).",
+ "smw-datavalue-languagecode-invalid": "No s'ha reconegut «$1» com a codi de llengua permès.",
+ "smw-property-predefined-lcode": "«$1» una propietat predefinida que representa un codi de llengua formatat d'acord amb BCP47 i està proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] Temps d'anàlisi de l'anotació en línia",
+ "smw-limitreport-intext-postproctime": "[SMW] durada de postprocessament",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segon|segons}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segon|segons}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Temps d'actualització de l'emmagatzematge (en la purga de la pàgina)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segon|segons}}",
+ "smw_allows_pattern": "S'espera que aquesta pàgina contingui una llista de referències (seguit per unes [https://ca.wikipedia.org/wiki/Expressi%C3%B3_regular expressions regulars]) que seran disponibles per a la propietat [[Property:Allows pattern|Permet patró]]. Per editar la pàgina cal el permís <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "S'ha classificat «$1» com a no vàlid per l'expressió regular «$2».",
+ "smw-datavalue-allows-pattern-reference-unknown": "La referència de patró «$1» no ha pogut coincidir amb una entrada en [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "La referència de llista «$1» no correspon a una pàgina de [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Al contingut de la llista «$1» hi manquen elements amb un marcador de llista *.",
+ "smw-datavalue-feature-not-supported": "La característica «$1» no és compatible amb el wiki o bé se n'ha inhabilitat.",
+ "smw-property-predefined-long-pvuc": "La unicitat s'estableix quan dos valors no són iguals en llur representació literal. Llavors qualsevol violació d'aquesta restricció es categoritzarà com error.",
+ "smw-datavalue-uniqueness-constraint-error": "La propietat «$1» només permet assignacions de valor únic i «$2» ja estava anotada en el subjecte «$3».",
+ "smw-datavalue-uniqueness-constraint-isknown": "La propietat «$1» només permet anotacions de valor úniques. «$2» ja conté un valor assignat. «$3» viola la restricció d'unicitat.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "«$1» conté un decalatge i un fus horari no permesos.",
+ "smw-datavalue-time-invalid-values": "El valor «$1» conté informació no interpretable en la forma de «$2».",
+ "smw-datavalue-time-invalid-date-components-common": "«$1» conté informació que no és interpretable.",
+ "smw-datavalue-time-invalid-date-components-dash": "«$1» conté un guió extrínsec o altres caràcters que no són vàlids per a interpretar una data.",
+ "smw-datavalue-time-invalid-date-components-empty": "«$1» conté alguns components buits.",
+ "smw-datavalue-time-invalid-date-components-three": "«$1» conté més que no tres components necessaris per a la interpretació de les dates.",
+ "smw-datavalue-time-invalid-date-components-sequence": "«$1» conté una seqüència que no s'ha pogut interpretar amb una matriu de coincidències de components de dates.",
+ "smw-datavalue-time-invalid-ampm": "«$1» conté «$2» com element d'hora, que no és vàlid per a la convenció de 12 hores.",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» és un URL no vàlid.",
+ "smw-datavalue-external-identifier-formatter-missing": "A la propietat li manca una assignació d'[[Property:External formatter uri|«URI de formatador extern»]]",
+ "smw-datavalue-keyword-maximum-length": "La paraula clau ha excedit la longitud màxima de $1 {{PLURAL:$1|caràcter|caràcters}}.",
+ "smw-datavalue-parse-error": "El valor donat «$1» no s'ha entès.",
+ "smw-datavalue-propertylist-invalid-property-key": "La llista de propietats «$1» contenia una clau de propietat «$2» no vàlida.",
+ "smw-datavalue-type-invalid-typeuri": "El tipus «$1» no s'ha pogut transformar en una representació URI vàlida.",
+ "smw-datavalue-wikipage-missing-fragment-context": "No es pot utilitzar el valor d'entrada wikipage «$1» sense una pàgina de context.",
+ "smw-datavalue-wikipage-invalid-title": "El valor d'entrada de tipus pàgina «$1» conté caràcters no vàlids o és incomplet. Això pot causar resultats no esperats en una consulta o en el procés d'anotació.",
+ "smw-datavalue-wikipage-property-invalid-title": "La propietat «$1» (com a tipus de pàgina) amb un valor d'entrada «$2» conté caràcters no vàlids o és incomplet. Això pot causar resultats inesperats durant un procés de consulta o d'anotació.",
+ "smw-datavalue-wikipage-empty": "El valor d'entrada de pàgina wiki és buit (p. ex., <code>[[SomeProperty::]], [[]]</code>) i llavors no es pot utilitzar com a nom o part d'una condició de consulta.",
+ "smw-type-ref-rec": "«$1» és un tipus de [https://www.semantic-mediawiki.org/wiki/Container contenidor] que permet enregistrar informació addicional (p. ex., provinència de les dades) de l'assignació d'un valor.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "L'analitzador de JSON ha retornat amb «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "No es pot utilitzar «$1» com etiqueta preferida perquè la llengua «$2» ja està assignada a l'etiqueta «$3».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copia l'enllaç al porta-retalls",
+ "smw-data-lookup": "Recuperant les dades ...",
+ "smw-data-lookup-with-wait": "S'està processant la sol·licitud i pot trigar una estona.",
+ "smw-no-data-available": "No hi ha cap dada disponible.",
+ "smw-property-req-violation-type": "La propietat conté tipus d'especificacions que entren en conflicte entre elles, que pot resultar en anotacions de valors no vàlids. S'espera que un usuari hi assigni un tipus apropiat.",
+ "smw-change-propagation-protection": "Es bloqueja la pàgina per tal de prevenir modificacions de dades accidentals mentre s'executa una actualització de la [https://www.semantic-mediawiki.org/wiki/Change_propagation propagació de canvis]. Aquest procés pot trigar una estona abans que es torni a desbloquejar la pàgina i depèn de la mida i de la freqüència del programador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cua de tasques].",
+ "smw-category-invalid-value-assignment": "«$1» no és reconeguda com a categoria o anotació de valor vàlida.",
+ "protect-level-smw-pageedit": "Permet només usuaris amb permisos d'edició de pàgines (Semantic MediaWiki)",
+ "smw-edit-protection-disabled": "S'ha inhabilitat la protecció de modificació; per tant, «$1» no pot utilitzar-se per protegir pàgines d'entitat de modificacions no autoritzades.",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki ha actualitzat l'estat de protecció d'acord amb la propietat «Té protecció de modificació».",
+ "smw-edit-protection-enabled": "Modifica protegit (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Aquesta pàgina està protegida i només pot ser modificada per usuaris amb els [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] <code>smw-patternedit</code> adequats.",
+ "smw-property-predefined-edip": "«$1» és una propietat predeterminada proporcionada per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per a indicar si la modificació està protegida o no.",
+ "smw-property-predefined-long-edip": "Tot i que qualsevol usuaris pot afegir aquesta propietat al subjecte, només un usuari amb un permís dedicat pot modificar o revocar la protecció a una entitat després que s'hagi afegit.",
+ "smw-query-reference-link-label": "Referència de consulta",
+ "smw-format-datatable-emptytable": "No hi ha cap disponible a la taula",
+ "smw-format-datatable-info": "Es mostren les entrades_START_ a _END_ de _TOTAL_",
+ "smw-format-datatable-infoempty": "Es mostren 0 de 0 de 0 entrades",
+ "smw-format-datatable-infofiltered": "(filtrades d'un total de _MAX_ entrades)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Mostra les entrades de _MENU_",
+ "smw-format-datatable-loadingrecords": "S'està carregant...",
+ "smw-format-datatable-processing": "S'està processant...",
+ "smw-format-datatable-search": "Cerca:",
+ "smw-format-datatable-zerorecords": "No s'ha trobat cap registre que concideixi",
+ "smw-format-datatable-first": "Primer",
+ "smw-format-datatable-last": "Últim",
+ "smw-format-datatable-next": "Següent",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": activa per ordenar la columna de forma ascendent",
+ "smw-format-datatable-sortdescending": ": activa per ordenar la columna de forma descendent",
+ "smw-format-datatable-toolbar-export": "Exporta",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "La categoria «$1» conté un objectiu de redirecció no vàlid a un pàgina que no és un espai de noms de categoria.",
+ "smw-postproc-queryref": "Semantic MediaWiki està refrescant la pàgina actual d'acord amb la condició d'unes consultes de postprocessament obligatòries.",
+ "apihelp-smwinfo-summary": "Mòdul API per recuperar informació d'estadístiques de Semantic MediaWiki i d'altres metadades.",
+ "apihelp-ask-summary": "Mòdul API per consultar Semantic MediaWiki fent servir el llenguatge Ask.",
+ "apihelp-askargs-summary": "Mòdul API per fer consultes a Semantic MediaWiki utilitzant el llenguatge Ask com una llista de condicions, impressions i paràmetres.",
+ "apihelp-browsebyproperty-summary": "Mòdul API per recuperar informació d'una propietat o d'una llista de propietats.",
+ "apihelp-browsebysubject-summary": "Mòdul API per recuperar informació d'un tema.",
+ "apihelp-smwtask-summary": "Mòdul API per executar tasques relacionades amb Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Mòdul API per permetre les activitats de navegació de diferents tipus d'entitat a Semantic MediaWiki.",
+ "smw-api-invalid-parameters": "Paràmetres no vàlids, «$1»",
+ "smw-parser-recursion-level-exceeded": "S'ha excedit el nivell de $1 recurrències durant el procés d'anàlisi. Se suggereix que es valida l'estructura de la plantilla o, si s'escau, que s'ajusti el paràmetre de configuració <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "Es mostren $1 {{PLURAL:$1|pàgina que utilitza|pàgines que utilitzen}} aquesta propietat.",
+ "smw-property-page-list-search-count": "Es {{PLURAL:$1|mostra $1 pàgina que fa servir|mostren $1 pàgines que fan servir}} aquesta propietat amb una correspondència de valor «$2».",
+ "smw-property-reserved-category": "Categoria",
+ "smw-category": "Categoria",
+ "smw-datavalue-uri-invalid-scheme": "No s'ha llistat «$1» com un esquema URI vàlid.",
+ "smw-browse-property-group-title": "Grup de propietats",
+ "smw-browse-property-group-label": "Etiqueta del grup de propietats",
+ "smw-browse-property-group-description": "Descripció del grup de propietats",
+ "smw-filter": "Filtre",
+ "smw-section-expand": "Desplega la secció",
+ "smw-section-collapse": "Replega la secció",
+ "smw-ask-format-help-link": "Format [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Ajuda",
+ "smw-cheat-sheet": "Full de referència",
+ "smw-personal-jobqueue-watchlist": "Llista de seguiment (cua de tasques)",
+ "smw-property-predefined-label-skey": "Clau d'ordenació",
+ "smw-processing": "S’està processant…",
+ "smw-redirect-target-unresolvable": "La destinació no es pot resoldre pel motiu «$1».",
+ "smw-types-title": "Tipus: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "No es permet canviar el model de contingut d'una [https://www.semantic-mediawiki.org/wiki/Help:Schema pàgina d'esquema].",
+ "smw-schema-namespace-edit-protection": "Aquesta pàgina està protegida i només pot ser modificada per usuaris amb els [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] <code>smw-schemaedit</code> adequats.",
+ "smw-schema-error": "Error de validació",
+ "smw-schema-error-schema": "L'especificació «$1» i la seva validació per l'esquema actual han trobat les incompatibilitats o violacions següents:",
+ "smw-schema-error-violation": "Violació (\"$1\", \"$2\")",
+ "smw-schema-error-type-missing": "Al contingut li manca un tipus per tal que sigui reconegut i usable a l'[https://www.semantic-mediawiki.org/wiki/Help:Schema espai de noms de l'esquema].",
+ "smw-schema-error-type-unknown": "El tipus «$1» no està registrat i llavors not pot utilitzar-se com a contingut a l'[https://www.semantic-mediawiki.org/wiki/Help:Schema espai de noms de l'esquema].",
+ "smw-schema-title": "Esquema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Tipus",
+ "smw-schema-tag": "{{PLURAL:$1|Etiqueta|Etiquetes}}",
+ "smw-property-predefined-long-schema-tag": "Una etiqueta que identifica l'esquema de continguts o de característiques similars.",
+ "smw-ask-title-keyword-type": "Cerca per paraula clau",
+ "smw-ask-message-keyword-type": "Aquesta cerca coincideix amb la condició <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "No es pot connectar a l'objectiu remot «$1».",
+ "smw-remote-source-disabled": "La font «$1» ha inhabilitat les sol·licituds remotes.",
+ "smw-remote-source-unmatched-id": "La font «$1» no coincideix amb la versió de Semantic MediaWiki que pot acceptar sol·licituds remotes.",
+ "smw-remote-request-note": "El resultat s’obté des de l’origen remot '''$1'''. És probable que el contingut generat inclogui informació que no és al wiki actual.",
+ "smw-remote-request-note-cached": "El resultat s’'''emmagatzema dins la memòria cau''' des de l’origen remot '''$1'''. És probable que el contingut generat inclogui informació que no és al wiki actual.",
+ "smw-parameter-missing": "Manca el paràmetre «$1».",
+ "smw-property-tab-usage": "Ús",
+ "smw-property-tab-redirects": "Sinònims",
+ "smw-property-tab-subproperties": "Subpropietats",
+ "smw-property-tab-errors": "Assignacions no adequades",
+ "smw-property-tab-specification": "... més",
+ "smw-concept-tab-list": "Llista",
+ "smw-concept-tab-errors": "Errors",
+ "smw-ask-tab-result": "Resultat",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Depuració",
+ "smw-ask-tab-code": "Codi"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/cdo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/cdo.json
new file mode 100644
index 00000000..45baae81
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/cdo.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "載入..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ce.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ce.json
new file mode 100644
index 00000000..76b596af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ce.json
@@ -0,0 +1,84 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sasan700",
+ "Умар",
+ "Macofe",
+ "ИÑмаил Садуев"
+ ]
+ },
+ "smw_viewasrdf": "RDF хьоÑÑ‚",
+ "smw_finallistconjunct": "а,",
+ "smw_factbox_head": "Бакъдерш: $1",
+ "smw_printername_list": "МогӀа",
+ "smw_printername_ul": "МогӀа",
+ "smw_printername_table": "Таблица",
+ "smw_printername_broadtable": "Шуйра таблица",
+ "smw_printername_template": "Кеп",
+ "smw_printername_rdf": "RDF-ÑкÑпорт",
+ "smw_printername_category": "Категори",
+ "validator-type-class-SMWParamSource": "йоза",
+ "smw-paramdesc-export": "ЭкÑпортан параметраш",
+ "smw-label-feed-description": "$2-канал $1",
+ "smw_iq_moreresults": "… тӀаьхьара хиламаш",
+ "smw_nodatetime": "Дозушдоцу терахь «$1».",
+ "smw_attribute_header": "ХӀара “$1†лелош йолу хӀумнаш",
+ "smw_attributearticlecount": "{{PLURAL:$1|Гуш ю}} $1 {{PLURAL:$1|хӀара хӀума лелош йолу агӀо|хӀара хӀума лелош йолу агӀонаш}}.",
+ "smw_exportrdf_recursive": "МаÑÑо йихкина агӀонийн рекурÑиван ÑкÑпорт. Хилам боккха хила мега!",
+ "smw_exportrdf_submit": "ЭкÑпорт",
+ "properties": "Билгало",
+ "smw_property_template": "$1 ю тайп $2, ($3 {{PLURAL:$3|лелор}})",
+ "smw-sp-property-searchform": "Гайта хӀумнаш чулацам болу:",
+ "concepts": "Концепташ",
+ "smw-sp-concept-header": "Концептийн могӀам",
+ "smw-sp-concept-count": "МогӀам чохь ю {{PLURAL:$1|концепт|$1 концепташ}}",
+ "smw-sp-concept-empty": "Концепташ цакарий.",
+ "unusedproperties": "Лелош йоцу билгалонаш",
+ "smw_unusedproperties_docu": "ХӀара хӀумнаш билгалйина, амма уьш цхьам агӀонгахь лелош Ñц.",
+ "smw_unusedproperty_template": "$1 ю тайп $2",
+ "wantedproperties": "Хаам боца хӀумнаш",
+ "smw_wantedproperties_docu": "ХӀара хӀумнаш хӀокху Ñайтехь лелош ÑŽ, амма царах лаьцна хаам болу агӀонаш Ñц.",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|лелор}})",
+ "smw_purge": "КарлаÑккха",
+ "types": "Тайпаш",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|1=Концепт|Концепташ}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|1=Концепт|Концепташ}}]]",
+ "smw_ask_submit": "Хилам каро",
+ "smw_ask_editquery": "Дехар тадан",
+ "smw_ask_defaultformat": "Iад йитарца",
+ "smw-ask-delete": "[ДӀаÑккха]",
+ "searchbyproperty": "Билгалонца лахар",
+ "smw_sbv_property": "Билгало:",
+ "smw_sbv_value": "МаьӀна:",
+ "smw_sbv_submit": "Лахар",
+ "browse": "Сайте хьажа",
+ "smw_browselink": "Хьажа агӀонах дерг",
+ "smw_browse_go": "Дехьа гӀо",
+ "smw_inverse_label_default": "$1 чура",
+ "smw_pp_from": "ÐгӀона чура",
+ "smw_pp_type": "Билгало",
+ "smw_result_prev": "Хьалха йоьдург",
+ "smw_result_next": "ТӀаьхьа йогӀург",
+ "smw_result_noresults": "Бехк ма биллалаш, хӀума ца карийна.",
+ "smw_smwadmin_db": "Хааман база дӀахӀоттор а, керла Ñккхар а",
+ "smw_smwadmin_datarefresh": "Хаам меттахӀоттор а, керла баккхар а",
+ "smw_smwadmin_datarefreshbutton": "Доладе хаамаш карлабахар",
+ "smw-paramdesc-category-delim": "ДӀаÑакъаÑтораг",
+ "prefs-smw": "Семантикан МедиаВики",
+ "prefs-ask-options": "Семантикан лахаран параметраш",
+ "smw-prefs-ask-options-tooltip-display": "Гайта гучуболуш хьехаман йоза",
+ "smw-prefs-ask-options-collapsed-default": "Латае параметрийн панель хьулйина Ӏад йитарца",
+ "smw-ui-tooltip-title-property": "Билгало",
+ "smw-ui-tooltip-title-info": "Хаам",
+ "smw-ui-tooltip-title-warning": "ГӀалат",
+ "smw-ui-tooltip-title-note": "Билгалдаккхар",
+ "group-smwadministrator": "Куьйгалхой Semantic MediaWiki",
+ "group-smwadministrator-member": "{{GENDER:$1|куьйгалхо (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Куьйгалхой_SMW",
+ "smw-sp-properties-cache-info": "ХӀара хаамаш ÑхьаÑцна [https://www.semantic-mediawiki.org/wiki/Caching кÑша чура] уьш тӀаьххьара карлабаьхна $1.",
+ "smw-sp-properties-header-label": "ХӀумнийн могӀам",
+ "smw-sp-admin-idlookup-title": "Лахар ObjectId",
+ "smw-sp-admin-idlookup-objectid": "Цуна Id:",
+ "smw-livepreview-loading": "Чуйолуш…",
+ "smw-sp-searchbyproperty-resultlist-header": "Карийнарш"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ch.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ch.json
new file mode 100644
index 00000000..3c70b70c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ch.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jatrobat"
+ ]
+ },
+ "smw_browse_go": "HÃ¥nao"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ckb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ckb.json
new file mode 100644
index 00000000..6637f6d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ckb.json
@@ -0,0 +1,43 @@
+{
+ "@metadata": {
+ "authors": [
+ "Asoxor"
+ ]
+ },
+ "smw_finallistconjunct": "، و",
+ "smw_factbox_head": "ڕاستییەکان سەبارەت بە «$1»",
+ "smw_concept_description": "وەسÙÛŒ Ú†Û•Ù…Ú©ÛŒ «$1»",
+ "smw_printername_count": "ئاکامەکان بژمێرە",
+ "smw_printername_csv": "ھەناردەکردنی CSV",
+ "smw_printername_dsv": "ھەناردەکردنی DSV",
+ "smw_printername_json": "ھەناردەکردنی JSON",
+ "smw_printername_list": "لیست",
+ "smw_printername_table": "خشتە",
+ "smw_printername_broadtable": "خشتەی بەرین",
+ "smw_printername_template": "داڕێژە",
+ "smw_printername_rdf": "ھەناردەکردنی RDF",
+ "smw_printername_category": "Ù¾Û†Ù„",
+ "validator-type-class-SMWParamSource": "دەق",
+ "smw_nofloat": "«$1» ژمارە نییە.",
+ "smw_type_header": "تایبەتمەندییەکانی جۆری «$1»",
+ "smw_typearticlecount": "نیشاندانی $1 {{PLURAL:$1|تایبەتمەندی}} بە بەکارھێنانی ئەم جۆرەوە.",
+ "properties": "تایبەتمەندییەکان",
+ "smw_properties_docu": "تایبەتمەندییەکانی خوارەوە لەم ویکیەدا بە کار ھاتوون.",
+ "smw_propertylackspage": "ھەموو تایبەتمەندییەک دەبێت بە پەڕەیەک وەس٠بکرێت!",
+ "types": "جۆرەکان",
+ "smw-ask-delete": "[بیسڕەوە]",
+ "smw-ask-sorting": "بەڕیزکردن",
+ "browse": "بگەڕێ بە ویکیدا",
+ "smw_browse_go": "بڕۆ",
+ "smw_inverse_label_default": "$1 Ù„Û•",
+ "smw_result_next": "دواتر",
+ "smw_result_results": "ئاکامەکان",
+ "smw_result_noresults": "هیچ ئاکامێک نییە.",
+ "smwadmin": "کردەوە بەڕێوەبەرییانەکانی Semantic MediaWiki",
+ "smw_smwadmin_return": "بگەڕێوە بۆ $1.",
+ "smw_smwadmin_datarefreshstopconfirm": "بەڵێ، من {{GENDER:$1|دڵنیام}}.",
+ "smw-ui-tooltip-title-warning": "Ú¾Û•ÚµÛ•",
+ "smw-ui-tooltip-title-event": "ڕووداو",
+ "smw-ui-tooltip-title-note": "تێبینی",
+ "smw-livepreview-loading": "باركردن‌..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/crh-cyrl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/crh-cyrl.json
new file mode 100644
index 00000000..8f442f20
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/crh-cyrl.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Юкленмекте…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/crh-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/crh-latn.json
new file mode 100644
index 00000000..8ef7c9e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/crh-latn.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Yüklenmekte…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/cs.json b/www/wiki/extensions/SemanticMediaWiki/i18n/cs.json
new file mode 100644
index 00000000..459bf2b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/cs.json
@@ -0,0 +1,283 @@
+{
+ "@metadata": {
+ "authors": [
+ "DemonioCZ",
+ "Kjbenes",
+ "Matěj Grabovský",
+ "Mormegil",
+ "Vks",
+ "XenoPheX",
+ "ì•„ë¼",
+ "Matěj Suchánek",
+ "Macofe",
+ "Kghbln",
+ "Dvorapa",
+ "Marek Pavlica",
+ "Aktron",
+ "Martin Urbanec",
+ "Ilimanaq29",
+ "Patriccck",
+ "Korytaacheck",
+ "Danny B."
+ ]
+ },
+ "smw-desc": "Činíme vaší wiki přístupnější – pro stroje ''i'' lidi ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw-title": "Sémantická MediaWiki",
+ "smw_viewasrdf": "RDF kanál",
+ "smw_finallistconjunct": " a",
+ "smw-factbox-head": "...více o \"$1\"",
+ "smw-factbox-facts": "Fakta",
+ "smw_isspecprop": "Tato vlastnost je speciální vlastnost na této wiki.",
+ "smw_concept_description": "Popis pojmu „$1â€",
+ "smw_no_concept_namespace": "Pojmy je možné definovat pouze na stránkách ve jmenném prostoru Concept:",
+ "smw_multiple_concepts": "Každá stránka konceptu může mít jen jednu definici.",
+ "smw_concept_cache_miss": "Koncept „$1†není možné momentálnÄ› použít, protože konfigurace wiki vyžaduje, aby se vypoÄítal až dodateÄnÄ›. Pokud problém pÅ™etrvává delší dobu, požádejte správce, aby tento koncept zpřístupnil.",
+ "smw_noinvannot": "Inverzním vlastnostem nelze přiřazovat hodnoty.",
+ "version-semantic": "Sémantická rozšíření",
+ "smw_baduri": "Promiňte, URI z rozsahu „$1“ na tomto místě nejsou dostupné.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "SpoÄítat výsledky",
+ "smw_printername_csv": "Export do CSV",
+ "smw_printername_dsv": "Export do DSV",
+ "smw_printername_json": "Export do JSON",
+ "smw_printername_list": "Seznam",
+ "smw_printername_ol": "VýÄet",
+ "smw_printername_table": "Tabulka",
+ "smw_printername_broadtable": "Široká tabulka",
+ "smw_printername_template": "Å ablona",
+ "smw_printername_templatefile": "Soubor Å¡ablony",
+ "smw_printername_rdf": "Export do RDF",
+ "smw_printername_category": "Kategorie",
+ "validator-type-class-SMWParamSource": "text",
+ "smw-paramdesc-limit": "Maximální poÄet vrácených výsledků",
+ "smw-paramdesc-link": "Zobrazit hodnoty jako odkazy",
+ "smw-paramdesc-intro": "Text, který se má zobrazit před výsledky dotazu, pokud nějaké jsou",
+ "smw-paramdesc-outro": "Text, který se má zobrazit po výsledcích dotazu, pokud nějaké jsou",
+ "smw-paramdesc-default": "Text, který se má zobrazit, pokud dotaz nevrátí žádné výsledky",
+ "smw-paramdesc-sep": "OddÄ›lovaÄ hodnot",
+ "smw-paramdesc-showsep": "Zobrazit na zaÄátku CSV souboru oddÄ›lovaÄ (\"sep=<hodnota>\")",
+ "smw-paramdesc-distribution": "Místo zobrazení vÅ¡ech hodnot spoÄítat kolikrát se která vyskytuje a zobrazit tyto poÄty.",
+ "smw-paramdesc-template": "Název šablony, pomocí které zobrazit výpisy",
+ "smw-paramdesc-columns": "PoÄet sloupců, ve kterých se zobrazí výsledky (výchozí poÄet je $1)",
+ "smw-paramdesc-userparam": "Hodnota předávaná každému volání šablony, je-li užita šablona",
+ "smw-paramdesc-introtemplate": "Název šablony, která se má zobrazit před výsledkem dotazu, je-li nějaký",
+ "smw-paramdesc-outrotemplate": "Název šablony, která se má zobrazit po výsledku dotazu, je-li nějaký",
+ "smw-paramdesc-embedformat": "HTML tag, kterým jsou definovány nadpisy",
+ "smw-paramdesc-embedonly": "Nezobrazovat nadpisy",
+ "smw-paramdesc-table-class": "Další CSS třída, která bude nastavena tabulce",
+ "smw-paramdesc-csv-sep": "OddÄ›lovaÄ, který má být použit",
+ "smw-paramdesc-dsv-separator": "OddÄ›lovaÄ, který má být použit",
+ "smw-paramdesc-dsv-filename": "Název DSV souboru",
+ "smw-paramdesc-filename": "Název výstupního souboru",
+ "smw-smwdoc-description": "Zobrazí tabulku všech parametrů, které lze pro daný formát výsledků použít, spolu s výchozími hodnotami a popisy.",
+ "smw-smwdoc-par-format": "Formát výsledků, pro který se zobrazí dokumentace.",
+ "smw-paramdesc-sort": "Vlastnost, podle které výsledky řadit",
+ "smw-paramdesc-order": "Způsob řazení výsledků dotazu",
+ "smw-paramdesc-export": "Možnosti exportu",
+ "smw-printername-feed": "Kanál RSS a Atom",
+ "smw-paramdesc-feedtype": "Typ kanálu",
+ "smw-paramdesc-feedtitle": "Text, který bude užit jako titulek kanálu",
+ "smw-paramdesc-feeddescription": "Text, který bude užit jako popis kanálu",
+ "smw-paramdesc-feedpagecontent": "Obsah stránky, který bude zobrazen v kanálu",
+ "smw-label-feed-description": "$2 kanál $1",
+ "smw_iq_disabled": "Promiňtě, semantické dotazy byly pro tuto wiki zakázány.",
+ "smw_iq_moreresults": "…další výsledky",
+ "smw_parseerror": "Zadaná hodnota nebyla pochopená.",
+ "smw_notitle": "„$1“ není možné použít na této wiki jako název stránky.",
+ "smw_noproperty": "„$1“ nelze na této wiki použít jako název vlastnosti.",
+ "smw_wrong_namespace": "Zde jsou povoleny jen stránky ze jmenného prostoru \"$1\"",
+ "smw_manytypes": "Pro vlastnost byl definován více než jeden typ.",
+ "smw_emptystring": "Prázdné řetězce nejsou povolené.",
+ "smw_notinenum": "„$1“ není v seznamu možných hodnot ($2) této vlastnosti.",
+ "smw_noboolean": "„$1“ nebylo rozpoznáno jako platná hodnota typu boolean (ano/ne).",
+ "smw_true_words": "ano,a,yes,y",
+ "smw_false_words": "ne,no,n",
+ "smw_nofloat": "“$1†není Äíslem.",
+ "smw_infinite": "Tak dlouhá Äísla jako $1 nejsou podporována.",
+ "smw_unitnotallowed": "\"$1\" není platnou měrnou jednotkou této vlastnosti.",
+ "smw_nounitsdeclared": "Pro tuto vlastnost nebyly definovány žádné měrné jednotky.",
+ "smw_novalues": "Žádné hodnoty nebyly zadané.",
+ "smw_nodatetime": "Datum \"$1\" nedává smysl.",
+ "smw_toomanyclosing": "Dotazovaný řetězec „$1“ má příliš mnoho výskytů.",
+ "smw_noclosingbrackets": "NÄ›který výskyt „<nowiki>[[</nowiki>“ ve vaÅ¡em dotazu nebyl ukonÄen odpovídajícím „]]“.",
+ "smw_misplacedsymbol": "Symbol „$1“ byl užitý na místě, kde nemá význam.",
+ "smw_unexpectedpart": "Část dotazu „$1“ nedává smysl.\nVýsledky pravdÄ›podobnÄ› nesplní oÄekávání.",
+ "smw_emptysubquery": "Některý poddotaz nemá platné podmínky.",
+ "smw_misplacedsubquery": "Některý poddotaz byl použitý na místě, kde nejsou poddotazy povoleny.",
+ "smw_valuesubquery": "Poddotazy nejsou podporovány pro hodnoty vlastnosti „$1“.",
+ "smw_badqueryatom": "NÄ›která Äást „<nowiki>[[…]]</nowiki>“ nebyla srozumitelná.",
+ "smw_propvalueproblem": "Hodnota vlastnosti „$1“ nedávala smysl.",
+ "smw_noqueryfeature": "UrÄitá Äást dotazu není na této wiki podporovaná a bude proto ignorována ($1).",
+ "smw_noconjunctions": "Konjunkce v dotazu není na této wiki podporována a bude proto ignorována ($1).",
+ "smw_nodisjunctions": "Disjunkce nejsou v dotazech na této wiki podporované a Äást dotazu byla ignorována ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Následující podmínka|$2 následující podmínky}} dotazu nebudou zohledněné z důvodu omezení této wiki na délku nebo hĺoubku dotazu: <code>$1</code>.",
+ "smw_notemplategiven": "Aby tento formát dotazu fungoval, uveÄte hodnotu parametru „templateâ€.",
+ "smw_db_sparqlqueryproblem": "Výsledek dotazu se nepodaÅ™ilo získat z databáze SPARQL. Tato chyba může být doÄasná nebo může indikovat chybu v softwaru databáze.",
+ "smw_db_sparqlqueryincomplete": "Zodpovězení dotazu se ukázalo být příliš obtížným a bylo stornováno. Výsledky nemusí být úplné. Je-li to možné, zkuste použít jednodušší dotaz.",
+ "smw_type_header": "Vlastností typu „$1“",
+ "smw_typearticlecount": "Zobrazeno je $1 {{PLURAL:$1|vlastnost|vlastnosti|vlastností}} tohoto typu.",
+ "smw_attribute_header": "Stránek používajících vlastnost „$1“",
+ "smw_attributearticlecount": "Zobrazeno je $1 {{PLURAL:$1|stránka používající|stránky používající|stránek používajících}} tuto vlastnost.",
+ "smw-propertylist-redirect-header": "Synonyma",
+ "exportrdf": "Export stránek do RDF",
+ "smw_exportrdf_docu": "Tato stránka vám umožňuje exportovat Äásti stránek do formátu RDF. Po zadaní názvů stránek do spodního textového pole (jeden název na řádek) můžete exportovat stránky.",
+ "smw_exportrdf_recursive": "Rekurzívně exportovat všechny související stránky. Pozor, výsledek může být velmi rozsáhlý!",
+ "smw_exportrdf_backlinks": "Exportovat také stránky, které odkazují na exportované stránky. Vytvoří přehledné RDF.",
+ "smw_exportrdf_lastdate": "Neexportovat stránky, které nebyly zmÄ›nÄ›né od zadaného Äasu.",
+ "smw_exportrdf_submit": "Exportovat",
+ "uriresolver": "PÅ™ekladaÄ URI",
+ "properties": "Vlastnosti",
+ "smw_properties_docu": "Na této wiki se používají následující vlastnosti.",
+ "smw_property_template": "$1 typu $2 ($3 {{PLURAL:$3|použití}})",
+ "smw_propertylackspage": "Všechny vlastnosti by měly mít stránku s popisem!",
+ "smw_propertylackstype": "Této vlastnosti nebyl definován žádný typ (předpokládá se prozatím typ $1)",
+ "smw_propertyhardlyused": "Tato vlastnost se na wiki téměř nepoužívá!",
+ "smw-sp-property-searchform": "Zobrazit vlastnosti, které obsahují:",
+ "smw-sp-property-searchform-inputinfo": "Vstup je citlivý na velikost písmen a je-li použit pro filtrování, zobrazí se jen vlastnosti, které splňují danou podmínku.",
+ "smw-special-property-searchform-options": "Nastavení",
+ "smw-special-wantedproperties-filter-label": "Filtr:",
+ "smw-special-wantedproperties-filter-unapproved": "Neschváleno",
+ "concepts": "Koncepty",
+ "smw-special-concept-header": "Seznam konceptů",
+ "smw-special-concept-empty": "Nebyl nalezen žádný koncept.",
+ "unusedproperties": "Nepoužité vlastnosti",
+ "smw-unusedproperties-docu": "Tato stránka obsahuje [https://www.semantic-mediawiki.org/wiki/Unused_properties nepoužívané vlastnosti], které jsou deklarované i přesto, že je žádná jiná stránka nepoužívá. K porovnání se můžete podívat také na speciální stránky [[Special:Properties|všech]] nebo [[Special:WantedProperties|chybějících vlastností]].",
+ "smw-unusedproperty-template": "$1 typu $2",
+ "wantedproperties": "Požadované vlastnosti",
+ "smw-wantedproperties-docu": "Tato stránka obsahuje [https://www.semantic-mediawiki.org/wiki/Wanted_properties chybějící vlastnosti], které jsou používané na wiki i přesto, že k nim neexistuje stránka s jejich popisem. K porovnání se můžete podívat také na speciální stránky [[Special:Properties|všech]] nebo [[Special:UnusedProperties|nepoužívaných vlastností]].",
+ "smw-wantedproperty-template": "$1 ($2 užití)",
+ "smw_purge": "Obnovit",
+ "types": "Typy",
+ "smw_types_docu": "Následuje seznam vÅ¡ech údajových typů, které je možné pÅ™iÅ™adit vlastnostem. Každý údajový typ má stránku, kde je možné uvést dodateÄné informace.",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Vlastnost|Vlastnosti}} (celkem)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Vlastnost (registrovaná|Vlastnosti (registrované}} se stránkou)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Vlastnost (přiřazená|Vlastnosti (přiřazené}} datovému typu)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Dotaz|Dotazy}}",
+ "smw-statistics-query-size": "Velikost dotazu",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Koncept|Koncepty}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Koncept|Koncepty}}]]",
+ "smw-statistics-subobject-count": "{{PLURAL:$1|Podobjekt|Podobjekty}}",
+ "smw_uri_doc": "URI resolver se stará o implementaci [$1 W3C TAG hledání na httpRange-14].\nStará se o to, aby se lidé nestali webstránkami.",
+ "ask": "Sémantické vyhledávání",
+ "smw_ask_sortby": "Řadit podle sloupce",
+ "smw_ask_ascorder": "VzestupnÄ›",
+ "smw_ask_descorder": "SestupnÄ›",
+ "smw_ask_submit": "Najdi výsledky",
+ "smw_ask_editquery": "Upravit dotaz",
+ "smw_add_sortcondition": "[Přidat podmínku na řazení]",
+ "smw_ask_hidequery": "Skrýt dotaz",
+ "smw_ask_help": "Pomocník pro dotazy",
+ "smw_ask_queryhead": "Dotaz",
+ "smw_ask_printhead": "Přídavné výpisy (volitelné)",
+ "smw_ask_printdesc": "(každý název vlastnosti na samostatný řádek)",
+ "smw_ask_format_as": "Formát výstupu:",
+ "smw_ask_defaultformat": "výchozí",
+ "smw_ask_otheroptions": "Další možnosti",
+ "smw-ask-otheroptions-collapsed-info": "Pro zobrazení všech dostupných možností prosím použijte ikonu plus",
+ "smw_ask_show_embed": "Zobrazit kód pro vložení do stránky",
+ "smw_ask_hide_embed": "Skrýt kód pro vložení do stránky",
+ "smw_ask_embed_instr": "Tento kód použijte ke vložení tohoto dotazu do textu stránky na wiki.",
+ "smw-ask-delete": "Odebrat",
+ "smw-ask-sorting": "Řazení",
+ "smw-ask-empty": "Odstranit všechny záznamy",
+ "smw-ask-format-selection-help": "Detailní popis najdete na stránce nápovědy $1.",
+ "searchbyproperty": "Hledat podle hodnoty vlastnosti",
+ "processingerrorlist": "Chyba při zpracovávání seznamu",
+ "smw_sbv_docu": "Hledat na wiki Älánek, který má vlastnost s jistou hodnotou.",
+ "smw_sbv_novalue": "Nebyla uvedena hodnota. Prosím, vložte ji ve formuláři nebo zobrazte všechny atributy typu $1",
+ "smw_sbv_displayresult": "Seznam vÅ¡ech Älánků, které mají vlastnost $1 s hodnotou $2.",
+ "smw_sbv_displayresultfuzzy": "Seznam všech stránek, jejichž vlastnost „$1“ má hodnotu „$2“. Jelikož bylo výsledků málo, jsou zobrazeny i blízké hodnoty.",
+ "smw_sbv_property": "Vlastnost:",
+ "smw_sbv_value": "Hodnota:",
+ "smw_sbv_submit": "Hledat výsledky",
+ "browse": "Prohledat wiki",
+ "smw_browselink": "Projít vlastnosti",
+ "smw_browse_article": "Zadejte název stránky, od které chcete zaÄít prohledávat.",
+ "smw_browse_go": "Provést",
+ "smw_browse_no_outgoing": "Tato stránka nemá žádné vlastnosti.",
+ "smw_browse_no_incoming": "Na tuto stránku neodkazují žádné vlastnosti.",
+ "smw-browse-show-group": "Zobrazit skupiny",
+ "smw-browse-hide-group": "Skrýt skupiny",
+ "smw_inverse_label_default": "$1 z/ze",
+ "pageproperty": "Hledání vlastností stránky",
+ "smw_pp_docu": "BuÄ zadejte stránku a vlastnost nebo pouze vlastnost pro naÄtení vÅ¡ech pÅ™iÅ™azených hodnot.",
+ "smw_pp_from": "Od stránky",
+ "smw_pp_type": "Vlastnictví:",
+ "smw_pp_submit": "Výsledky hledání",
+ "smw_result_prev": "Zpět",
+ "smw_result_next": "Dále",
+ "smw_result_results": "Výsledky",
+ "smw_result_noresults": "Bohužel nejsou žádné výsledky.",
+ "smwadmin": "Funkce pro administraci",
+ "smw_smwadmin_return": "Zpět na $1",
+ "smw_smwadmin_updatestarted": "Byl spuštěn nový proces aktualizace sémantických dat.\nVšechna uložená data budou zkontrolována a, bude-li to nutné, znovu vytvořena nebo opravena.\nPrůběh aktualizace můžete sledovat na této speciální stránce.",
+ "smw_smwadmin_updatenotstarted": "Proces updatu již probíhá.\nNelze vytvořit další.",
+ "smw_smwadmin_updatestopped": "VÅ¡echny procesy aktualizace byly zastaveny.",
+ "smw_smwadmin_updatenotstopped": "Abyste zastavili běžící proces aktualizace, musíte zaÅ¡krtnout políÄko, Äímž potvrdíte, že si jste opravdu jist{{GENDER:||a|(a)}}.",
+ "smw-admin-docu": "Tato speciální stránka vám pomůže s instalací a upgradem <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a>.\nPřed spuštěním administrativních funkcí nezapomeňte zálohovat cenná data.",
+ "smw-admin-db": "Instalace a upgrade databáze",
+ "smw-admin-dbdocu": "Aby mohla Semantic MediaWiki ukládat sémantická data, potřebuje rozšířit databázi MediaWiki.\nFunkce níže zajišťuje, že je vaše databáze správně nastavena.\nZměny provedené v tomto kroku nemají žádný vliv na zbytek databáze MediaWiki, a lze je snadno vzít zpět.\nTuto přípravnou funkci můžete spustit vícekrát, aniž by došlo k jakémukoli poškození, ale je to nutné pouze jednou při instalaci nebo upgradu.",
+ "smw-admin-permissionswarn": "Selže-li operace s SQL chybami, pak uživatel databáze, kterého vaÅ¡e wiki používá (zkontrolujte soubor „LocalSettings.php“) pravdÄ›podobnÄ› nemá dostateÄná oprávnÄ›ní.\nPÅ™idÄ›lte tomuto uživateli dostateÄná oprávnÄ›ní k tvorbÄ› a mazání tabulek, doÄasnÄ› do souboru „LocalSettings.php“ zadejte údaje rootu databáze, nebo použijte údržbový skript <code>setupStore.php</code>, který používá údaje správce.",
+ "smw-admin-dbbutton": "Inicializovat Äi upgradovat tabulky",
+ "smw-admin-announce": "Nahlásit tuto wiki",
+ "smw_smwadmin_datarefresh": "Nové vytvoření dat",
+ "smw_smwadmin_datarefreshdocu": "Na základÄ› obsahu stránek wiki je možné obnovit vÅ¡echna data Semantic MediaWiki.\nTo může být užiteÄné pÅ™i opravÄ› poÅ¡kozených dat nebo pÅ™i jejich úpravÄ›, pokud se v důsledku upgradu softwaru zmÄ›ní vnitÅ™ní formát.\nUpdate se spouÅ¡tí stránku po stránce a nebude dokonÄen ihned.\nNásledující se zobrazuje pokud update právÄ› probíhá a umožňuje vám updaty spouÅ¡tÄ›t Äi zastavovat (pokud tuto možnost nezakázal administrátor).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Probíhá update.</strong>\nJe normální, že update postupuje pomalu, protože data zpracovává v malých dávkách vždy, když wiki navÅ¡tíví nÄ›jaký uživatel.\nChcete-li update dokonÄit rychleji, můžete spustit údržbový skript MediaWiki <code>runJobs.php</code> (k omezení poÄtu updatů provedených v jedné dávce použijte nastavení <code>--maxjobs 1000</code>).\nOdhadovaný průbÄ›h updatu:",
+ "smw_smwadmin_datarefreshbutton": "Naplánovat znovusestavení dat",
+ "smw_smwadmin_datarefreshstop": "Zastavit update",
+ "smw_smwadmin_datarefreshstopconfirm": "Ano, jsem si {{GENDER:$1|jistý|jistá|jistý/á}}.",
+ "smw-admin-support": "Získání podpory",
+ "smw-admin-supportdocu": "V případě problémů je k pomoci připraveno několik zdrojů:",
+ "smw-admin-installfile": "Máte-li problémy s instalací, pÅ™eÄtÄ›te si návod k instalaci v <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">souboru INSTALL</a> a na <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">instalaÄní stránce</a>.",
+ "smw-admin-smwhomepage": "Kompletní uživatelská dokumentace Semantic MediaWiki je dostupná na <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Chyby lze hlásit na <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHubu</a>.",
+ "smw-admin-questions": "Další otázky Äi návrhy můžete pokládat do diskuse na Semantic MediaWiki v <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">uživatelském komunikaÄním vláknÄ›</a> nebo <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">chatovací\n místnosti</a>.",
+ "smw_adminlinks_datastructure": "Datová struktura",
+ "smw_adminlinks_inlinequerieshelp": "Nápověda k inline dotazům",
+ "smw-createproperty-isproperty": "Toto je vlastnost typu $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Povolená hodnota této vlastnosti je|Povolené hodnoty této vlastnosti jsou}}:",
+ "smw-paramdesc-category-delim": "OddÄ›lovaÄ",
+ "smw-paramdesc-category-userparam": "Parametr předávaný do šablony",
+ "smw-info-par-message": "Jaká zpráva se má zobrazit.",
+ "smw-info-par-icon": "Jaká ikona se má zobrazit, buÄ â€žinfo“ nebo „warning“ (varování).",
+ "prefs-smw": "Sémantická MediaWiki",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Sémantická MediaWiki] (a podobná rozšíření) poskytuje individuální úpravy pro nějaké zvolené funkce. Podívejte se prosím na [https://www.semantic-mediawiki.org/wiki/Help:User_preferences nápovědu] pro další informace.",
+ "smw-prefs-ask-options-tooltip-display": "Zobrazit parametr textu jako informaÄní nástroj",
+ "smw-ui-tooltip-title-property": "Vlastnost",
+ "smw-ui-tooltip-title-quantity": "Množství",
+ "smw-ui-tooltip-title-warning": "Varování",
+ "smw-ui-tooltip-title-parameter": "Parametr",
+ "smw-ui-tooltip-title-event": "Událost",
+ "smw-ui-tooltip-title-note": "Poznámka",
+ "smw_unknowntype": "Pro vlastnost je definován nepodporovaný typ „$1“.",
+ "smw_concept_header": "Stran konceptu \"$1\"",
+ "smw_conceptarticlecount": "{{PLURAL:$1|zobrazena je|zobrazeny jsou|zobrazeno je}} $1 {{PLURAL:$1|stránka náležející|stránky náležející|stránek náležejících}} tomuto konceptu.",
+ "right-smw-ruleedit": "Editovat pravidla (Sémantická MediaWiki)",
+ "group-smwadministrator": "Správci (Sémantická MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|správce|správkyně|správce}} (Semantic MediaWiki)",
+ "action-smw-ruleedit": "editovat pravidla (Sémantická MediaWiki)",
+ "smw-sp-properties-cache-info": "Uvedená data pocházejí z [https://www.semantic-mediawiki.org/wiki/Caching cache] a byla naposledy aktualizována $1.",
+ "smw-sp-properties-header-label": "Seznam vlastností",
+ "smw-livepreview-loading": "Nahrávám…",
+ "smw-sp-searchbyproperty-resultlist-header": "Seznam výsledků",
+ "smw-editpage-annotation-enabled": "Tato stránka podporuje sémantické anotace uvnitÅ™ textu (napÅ™. <nowiki>„[[Is specified as::World Heritage Site]]“</nowiki>), které slouží k budování strukturovaného a prohledávatelného obsahu prostÅ™ednictvím Semantic MediaWiki. Podrobný popis, jak používat anotace a funkci parseru #ask, naleznete na stránkách nápovÄ›dy [https://www.semantic-mediawiki.org/wiki/Help:Getting_started ''ZaÄínáme''], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation ''Anotace uvnitÅ™ textu''] Äi [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries ''inline dotazy''].",
+ "smw-editpage-annotation-disabled": "Na této stránce nejsou povoleny sémantické anotace uvnitř textu kvůli omezením jmenného prostoru. Podrobný popis, jak tento jmenný prostor povolit, najdete v [https://www.semantic-mediawiki.org/wiki/Help:Configuration nápovědě ke konfiguraci].",
+ "smw-search-syntax-support": "Vyhledávací vstup podporuje použití sémantické [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search syntaxové otázky], která pomůže získávání výsledků s použitím Semantic MediaWiki.",
+ "smw-search-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Vstupní asistent] je také aktivní pro zjednodušení zvolení možných vlastností a kategorií.",
+ "smw-search-profile-extended-section-sort": "Řadit podle",
+ "smw-search-profile-extended-section-form": "Vybrat formulář",
+ "smw-type-geo": "\"$1\" je typ dat, který popisuje geografické lokace a požaduje [https://www.semantic-mediawiki.org/wiki/Extension:Maps mapové rozšíření].",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Extension:Maps Rozšíření map] nebylo zjištěno, protože \"$1\" je omezeno v kapacitě, takže nemůže fungovat.",
+ "smw-datavalue-keyword-maximum-length": "KlíÄové slovo je delší než maximální délku {{PLURAL:$1|$1 znaku|$1 znaků}}.",
+ "smw-type-eid": "\"$1\" je varianta datatypu [[Special:Types/Text|textu]], která požaduje pÅ™idÄ›lené vlastnosti, aby deklarovala [[Property:External formatter uri|Externí formátovaÄ URI]].",
+ "smw-property-predefined-keyw": "\"$1\" je předdefinovaná vlastnost a [[Special:Types/Keyword|druh]] poskytnutý [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Sémantickou MediaWiki], který normalizuje text a má omezenou délku.",
+ "smw-type-keyw": "\"$1\" je druh [[Special:Types/Text|textu]], který má omezenou délku a normalizuje reprezentaci obsahu",
+ "smw-change-propagation-protection": "Tato stránka je zamÄena kvůli prevenci náhodných editací dat bÄ›hem [https://www.semantic-mediawiki.org/wiki/Change_propagation zmÄ›ny anotací hodnot]. Proces může trvat chvíli, než bude stránka odemÄena, což záleží na velikosti a frekvenci [https://www.mediawiki.org/wiki/Manual:Job_queue plánovaÄe].",
+ "smw-types-title": "Typ: $1",
+ "smw-ask-title-keyword-type": "Hledání klíÄových slov",
+ "smw-ask-message-keyword-type": "Toto vyhledává shody se stavem <code><nowiki>$1</nowiki></code>.",
+ "smw-property-tab-usage": "Použití",
+ "smw-property-tab-redirects": "Synonyma",
+ "smw-concept-tab-list": "Seznam",
+ "smw-concept-tab-errors": "Chyby"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/cu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/cu.json
new file mode 100644
index 00000000..ad8a2c2f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/cu.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "ОйЛ"
+ ]
+ },
+ "smw-ui-tooltip-title-warning": "блаê™Ð½Ð°"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/cv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/cv.json
new file mode 100644
index 00000000..0c30caf8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/cv.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chuvash2014"
+ ]
+ },
+ "smw_printername_template": "Шаблон",
+ "smw_purge": "Çĕнĕлет",
+ "smw_browse_go": "Куç",
+ "smw-livepreview-loading": "Тултаратпăр…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/cy.json b/www/wiki/extensions/SemanticMediaWiki/i18n/cy.json
new file mode 100644
index 00000000..59c66127
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/cy.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ham II"
+ ]
+ },
+ "browse": "Pori'r wici",
+ "smw-livepreview-loading": "Wrthi'n llwytho…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/da.json b/www/wiki/extensions/SemanticMediaWiki/i18n/da.json
new file mode 100644
index 00000000..6e43f7de
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/da.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Christian List"
+ ]
+ },
+ "validator-type-class-SMWParamSource": "tekst",
+ "browse": "Gennemse wiki",
+ "smw-ui-tooltip-title-legend": "Forklaring",
+ "smw-livepreview-loading": "Indlæser …"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/de-ch.json b/www/wiki/extensions/SemanticMediaWiki/i18n/de-ch.json
new file mode 100644
index 00000000..d44a825c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/de-ch.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Emwiemaikel",
+ "Kghbln"
+ ]
+ },
+ "smw_concept_cache_miss": "Das Konzept „$1“ kann im Moment nicht genutzt werden, da es, gemäss der Konfiguration des Wikis, offline zu berechnen ist.\nFalls sich das Problem nicht in angemessener Zeit von selbst erledigt, bitte deinen Websiteadministrator, die Daten dieses Konzepts zu aktualisieren.",
+ "smw-paramdesc-columns": "Die Anzahl der Spalten, die für die Anzeige der Suchergebnisse verwendet werden sollen (standardmässig $1).",
+ "smw-smwdoc-description": "Zeigt eine Übersicht aller Parameter inkl. Beschreibung und Standardwerten, die im Zusammenhang mit dem angegebenen Ergebnisformat genutzt werden können.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": "'",
+ "smw_unitnotallowed": "„$1“ wurde nicht als gültige Masseinheit für dieses Attribut festgelegt.",
+ "smw_querytoolarge": "Die folgenden Abfragebedingungen konnten, wegen der auf diesem Wiki gültigen Beschränkungen, bezüglich Grösse und Tiefe von Abfragen, nicht berücksichtigt werden: $1.",
+ "smw_exportrdf_recursive": "Exportiere auch alle relevanten Seiten rekursiv. Diese Einstellung kann zu sehr grossen Ergebnismengen führen!",
+ "smw-sp-property-searchform-inputinfo": "Bei der Eingabe wird zwischen Gross- und Kleinschreibung unterschieden. Bei der Filterung werden nur Attribute angezeigt, die der Bedingung entsprechen.",
+ "smw-statistics-query-size": "Abfragegrösse",
+ "smw-type-tel": "„$1“ ist ein Datentyp der für die Ziffernfolge zur Anwahl eines Zielteilnehmers (Rufnummer) gemäss RFC 3966 genutzt wird."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/de-formal.json b/www/wiki/extensions/SemanticMediaWiki/i18n/de-formal.json
new file mode 100644
index 00000000..709f7988
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/de-formal.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kghbln"
+ ]
+ },
+ "smw-browse-intro": "Mit dieser Spezialseite können die auf Seiten oder mit Subobjekten gespeicherten Daten und Informationen eingesehen werden. Bitte geben Sie den Namen einer Seite ein, die eingesehen werden soll.",
+ "smw-noscript": "Diese Seite oder Aktion erfordert JavaScript, damit sie funktioniert. Bitte aktivieren Sie JavaScript in Ihrem Browser oder verwenden Sie einen Browser, der dies unterstützt, damit diese Funktionalität wie gewünscht angeboten werden kann. Weitere Informationen sind auf der entsprechenden [https://www.semantic-mediawiki.org/wiki/Help:Noscript Hilfeseite] vorhanden.",
+ "smw-admin-announce-text": "Sofern dieses Wiki öffentlich zugänglich ist, kann es auf <a href=\"https://wikiapiary.com\">WikiApiary</a>, dem Wiki zum Tracken von Wikis, registriert werden."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/de.json b/www/wiki/extensions/SemanticMediaWiki/i18n/de.json
new file mode 100644
index 00000000..051c46cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/de.json
@@ -0,0 +1,859 @@
+{
+ "@metadata": {
+ "authors": [
+ "Als-Holder",
+ "DaSch",
+ "Denny",
+ "Imre",
+ "Jan Luca",
+ "Jens Liebenau",
+ "Kghbln",
+ "MF-Warburg",
+ "Markus Krötzsch",
+ "Melancholie",
+ "Metalhead64",
+ "Michawiki",
+ "Omnipaedista",
+ "Pill",
+ "Purodha",
+ "Revolus",
+ "The Evil IP address",
+ "Umherirrender",
+ "Wnme",
+ "Das Schäfchen",
+ "FriedhelmW",
+ "Nemo bis"
+ ]
+ },
+ "smw-desc": "Ermöglicht es, das Wiki zugänglicher zu machen – für Menschen ''und'' Maschinen ([https://www.semantic-mediawiki.org/wiki/Help:User_manual/de Dokumentation])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error": "Semantic MediaWiki wurde installiert und aktiviert, es fehlt jedoch der zugehörige [https://www.semantic-mediawiki.org/wiki/Help:Upgrade Aktualisierungsschlüssel]: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Warum wird diese Fehlermeldung angezeigt?",
+ "smw-upgrade-error-why-explain": "Die interne Struktur der von Semantic MediaWiki genutzten Datenbank hat sich verändert und erfordert Anpassungen, um störungsfrei funktionieren zu können. Für die Änderung kann es unter anderem mehrere Gründe geben:\n* ein feststehendes Attribut wurde hinzugefügt, das die Erstellung einer neuen Datenbanktabelle erfordert, bevor wieder auf die Datenbank zugegriffen werden kann\n* eine Softwareanpassung bedingt Änderungen an Tabellen oder Indizes der Datenbank, die eine Schemaaktualisierung bedingen, bevor wieder auf die Datenbank zugegriffen werden kann",
+ "smw-upgrade-error-how-title": "Wie kann dieser Fehler behoben werden?",
+ "smw-upgrade-error-how-explain": "Ein Administrator oder eine andere Person mit Administratorberechtigung muss entweder das Wartungsskript [https://www.mediawiki.org/wiki/Manual:Update.php „update.php“] von MediaWiki oder [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php „setupStore.php“] von Semantic MediaWiki ausführen. Weitergehende Informationen sind auf den folgenden Seiten verfügbar:\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation Installationsanweisungen]\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting Hilfeseite zu häufigen Problemen]",
+ "smw-semantics-not-enabled": "Momentan kann Semantic MediaWiki nicht auf diesem Wiki genutzt werden.",
+ "smw_viewasrdf": "RDF-Feed",
+ "smw_finallistconjunct": " und",
+ "smw-factbox-head": "… weitere Daten zur Seite „$1“",
+ "smw-factbox-facts": "Fakten",
+ "smw-factbox-facts-help": "Zeigt Aussagen und Fakten an, die von Benutzern erstellt wurden.",
+ "smw-factbox-facts-derived": "Abgeleitete Fakten",
+ "smw-factbox-facts-derived-help": "Zeigt Fakten an, die aufgrund von Regeln oder mithilfe von Folgerungen ermittelt bzw. abgeleitet wurden.",
+ "smw_isspecprop": "Dieses Attribut ist ein Spezialattribut in diesem Wiki.",
+ "smw-concept-cache-header": "Cacheverwendung",
+ "smw-concept-cache-count": "Der [https://www.semantic-mediawiki.org/wiki/Help:Konzeptcaching Konzeptcache] enthält {{PLURAL:$1|'''ein''' Objekt|'''$1''' Objekte}} (Stand: $2).",
+ "smw-concept-no-cache": "Kein Cache verfügbar.",
+ "smw_concept_description": "Beschreibung des Konzepts „$1“",
+ "smw_no_concept_namespace": "Konzepte können nur im Konzeptnamensraum (''Konzept:'') erstellt werden.",
+ "smw_multiple_concepts": "Jede Konzeptseite kann nur eine Konzeptdefinition beinhalten.",
+ "smw_concept_cache_miss": "Das Konzept „$1“ kann im Moment nicht genutzt werden, da es, gemäß der Konfiguration des Wikis, offline zu berechnen ist.\nFalls sich das Problem nicht in angemessener Zeit von selbst erledigt, bitte deinen Websiteadministrator, die Daten dieses Konzepts zu aktualisieren.",
+ "smw_noinvannot": "Inversen Attributen können keine Werte zugewiesen werden.",
+ "version-semantic": "Semantische Erweiterungen",
+ "smw_baduri": "URIs der Form „$1“ sind nicht zulässig.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Ergebnisse zählen",
+ "smw_printername_csv": "Export (CSV)",
+ "smw_printername_dsv": "Export (DSV)",
+ "smw_printername_debug": "Debug-Abfrage (für Experten)",
+ "smw_printername_embedded": "Einbettung (Seiteninhalt)",
+ "smw_printername_json": "Export (JSON)",
+ "smw_printername_list": "Liste",
+ "smw_printername_plainlist": "Liste (einfach)",
+ "smw_printername_ol": "Liste (Aufzählung)",
+ "smw_printername_ul": "Liste (Auflistung)",
+ "smw_printername_table": "Tabelle",
+ "smw_printername_broadtable": "Tabelle (breit)",
+ "smw_printername_template": "Vorlage",
+ "smw_printername_templatefile": "Vorlagendatei",
+ "smw_printername_rdf": "Export (RDF)",
+ "smw_printername_category": "Kategorie",
+ "validator-type-class-SMWParamSource": "Text",
+ "smw-paramdesc-limit": "Legt fest, wie viele Ergebnisse bei der Ausgabe der Abfrageergebnisse maximal angezeigt werden sollen",
+ "smw-paramdesc-offset": "Legt fest, ab dem wievielten Ergebnis mit der Ausgabe der Abfrageergebnisse begonnen werden soll",
+ "smw-paramdesc-headers": "Legt fest, ob Ãœberschriften bzw. Attributbezeichnungen bei der Ausgabe der Abfrageergebnisse verwendet werden sollen",
+ "smw-paramdesc-mainlabel": "Legt fest, welche Überschrift oder Bezeichnung für die Hauptergebnisspalte bei der Ausgabe der Abfrageergebnisse angezeigt werden soll",
+ "smw-paramdesc-link": "Legt fest, ob die Datenwerte der Ergebnisse bei der Ausgabe der Abfrageergebnisse als Link angezeigt werden sollen",
+ "smw-paramdesc-intro": "Legt fest, welcher Text vor der Ausgabe der Abfrageergebnisse angezeigt werden soll",
+ "smw-paramdesc-outro": "Legt fest, welcher Text nach der Ausgabe der Abfrageergebnisse angezeigt werden soll",
+ "smw-paramdesc-default": "Legt fest, welcher Text angezeigt werden soll, sofern keine Abfrageergebnisse vorhanden sind",
+ "smw-paramdesc-sep": "Legt fest, welches Trennzeichen bei der Ausgabe der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-propsep": "Legt fest, welches Trennzeichen zwischen den Attributen der Ergebnisse genutzt werden soll",
+ "smw-paramdesc-valuesep": "Legt fest, welches Trennzeichen zwischen den Attributwerten der Ergebnisse genutzt werden soll",
+ "smw-paramdesc-showsep": "Legt fest, welches Trennzeichen im Kopfbereich der .csv-Datei angezeigt werden soll",
+ "smw-paramdesc-distribution": "Die Anzahl der Vorkommen von Werten zählen und anzeigen, anstatt sie alle anzuzeigen.",
+ "smw-paramdesc-distributionsort": "Die Werteverteilung nach Anzahl der Vorkommen sortieren.",
+ "smw-paramdesc-distributionlimit": "Die Anzahl der Vorkommen von Werten auf bestimmte Werte beschränken.",
+ "smw-paramdesc-aggregation": "Legt fest, auf was sich die Aggregation der Daten beziehen soll",
+ "smw-paramdesc-template": "Legt fest, welche Vorlage zum Anzeigen der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-columns": "Legt fest, wie viele Spalten zur Anzeige der Abfrageergebnisse verwendet werden sollen",
+ "smw-paramdesc-userparam": "Legt fest, welcher Wert jedem Vorlagenaufruf übergeben wird, sofern eine Vorlage genutzt wird",
+ "smw-paramdesc-class": "Legt fest, welche zusätzliche CSS-Klasse genutzt werden soll",
+ "smw-paramdesc-introtemplate": "Legt fest, welche Vorlage vor der Ausgabe von Abfrageergebnissen eingefügt werden soll",
+ "smw-paramdesc-outrotemplate": "Legt fest, welche Vorlage nach der Ausgabe von Abfrageergebnissen eingefügt werden soll",
+ "smw-paramdesc-embedformat": "Legt fest, welches HTML-Tag für die Überschriften bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-embedonly": "Legt fest, ob Ãœberschriften bei der Ausgabe der Abfrageergebnisse verwendet werden sollen",
+ "smw-paramdesc-table-class": "Legt fest, welche zusätzliche CSS-Klasse genutzt werden soll",
+ "smw-paramdesc-table-transpose": "Legt fest, ob der Spalten- und Zeilenkopf bei der Anzeige der Abfrageergebnisse transponiert werden soll",
+ "smw-paramdesc-rdfsyntax": "Die zu verwendende RDF-Syntax",
+ "smw-paramdesc-csv-sep": "Legt fest, welches Trennzeichen zwischen den Spalten bei der Ausgabe der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-csv-valuesep": "Legt fest, welches Trennzeichen zwischen den Datenwerten bei der Ausgabe der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-csv-merge": "Legt fest, ob die Zeilen- und Spaltenwerte zu einem identischen Seitenidentifikator (erste Spalte) zusammengeführt werden sollen",
+ "smw-paramdesc-csv-bom": "Legt fest, ob eine BOM (Byte-Reihenfolge-Markierung) als Zeichen zur Angabe der Byte-Reihenfolge zum Anfang der ausgegebenen Datei hinzugefügt werden soll",
+ "smw-paramdesc-dsv-separator": "Legt fest, welches Trennzeichen bei der Ausgabe der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-dsv-filename": "Legt fest, welcher Name der Datei bei der Ausgabe der Abfrageergebnisse gegeben werden soll",
+ "smw-paramdesc-filename": "Legt fest, welcher Name für die Ausgabedatei verwendet werden soll",
+ "smw-smwdoc-description": "Zeigt eine Übersicht aller Parameter mitsamt deren Standardwerten einschließlich ihrer Beschreibung, die im Zusammenhang mit dem angegebenen Ergebnisformat genutzt werden können.",
+ "smw-smwdoc-default-no-parameter-list": "Dieses Ergebnisformat bietet keine formatspezifischen Parameter.",
+ "smw-smwdoc-par-format": "Das Ergebnisformat zu dessen Parametern die Dokumentation angezeigt werden soll.",
+ "smw-smwdoc-par-parameters": "Die anzuzeigenden Parameter: „specific“ für die vom jeweiligen Ergebnisformat bereitgestellten, „base“ für die für alle Ergebnisformate verfügbaren und „all“ zur Anzeige aller.",
+ "smw-paramdesc-sort": "Legt fest, nach welchem Attribut sortiert die Ergebnisse bei der Ausgabe der Abfrageergebnisse angezeigt werden sollen",
+ "smw-paramdesc-order": "Legt fest, in welcher Sortierreihenfolge die Ergebnisse bei der Ausgabe der Abfrageergebnisse angezeigt werden sollen",
+ "smw-paramdesc-searchlabel": "Legt fest, welcher Text als Link zur Ausgabe weiterer Abfrageergebnisse angezeigt werden soll",
+ "smw-paramdesc-named_args": "Legt fest, ob Bezeichnungen für die Parameter an die Vorlage bei der Ausgabe der Abfrageergebnisse weitergegeben werden sollen",
+ "smw-paramdesc-template-arguments": "Legt fest, wie die Argumente an die Vorlage übergeben werden sollen",
+ "smw-paramdesc-import-annotation": "Legt fest, ob die zusätzlich annotierten Daten während des Parsens einer Seite kopiert werden sollen",
+ "smw-paramdesc-export": "Die Exportoption",
+ "smw-paramdesc-prettyprint": "Legt fest, ob zusätzliche Einzüge und neue Zeilen bei der Ausgabe der Abfrageergebnisse verwendet werden sollen",
+ "smw-paramdesc-json-unescape": "Legt fest, ob maskierte Schrägstriche und aus mehreren Bytes bestehende Unicode-Zeichen bei der Ausgabe der Abfrageergebnisse verwendet werden sollen",
+ "smw-paramdesc-json-type": "Legt fest, welcher Serialisierungstyp bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-source": "Legt fest, welche alternative Datenquelle für die Ermittlung der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-jsonsyntax": "Die zu verwendende JSON-Syntax",
+ "smw-printername-feed": "Feed (RSS/Atom)",
+ "smw-paramdesc-feedtype": "Legt fest, welcher Feedtyp bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-feedtitle": "Legt fest, welcher Text als Titel des Feeds bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-feeddescription": "Legt fest, welcher Text als Beschreibung des Feeds bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-feedpagecontent": "Legt fest, ob der Seiteninhalt zusammen mit dem Feed bei der Ausgabe der Abfrageergebnisse angezeigt werden soll",
+ "smw-label-feed-description": "$2-Feed: $1",
+ "smw-paramdesc-mimetype": "Legt fest, welcher Medientyp (MIME-Typ) für die Ausgabedatei verwendet werden soll",
+ "smw_iq_disabled": "Semantische Abfragen wurden in diesem Wiki deaktiviert.",
+ "smw_iq_moreresults": "… weitere Ergebnisse",
+ "smw_parseerror": "Der eingegebene Wert wurde nicht verstanden.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "„$1“ kann nicht als Seitenname in diesem Wiki verwendet werden.",
+ "smw_noproperty": "„$1“ kann nicht als Attribut in diesem Wiki verwendet werden.",
+ "smw_wrong_namespace": "Nur Seiten im Namensraum „$1“ sind hier zulässig.",
+ "smw_manytypes": "Dem Attribut wurden mehrere Datentypen zugewiesen.",
+ "smw_emptystring": "Leere Zeichenfolgen werden nicht akzeptiert.",
+ "smw_notinenum": "„$1“ gehört nicht zu den [[Property:Allows value|zulässigen Werten]] ($2) für das Attribut „$3“.",
+ "smw-datavalue-constraint-error-allows-value-list": "„$1“ befindet sich nicht in der Liste ($2) [[Property:Allows value|zulässiger Werte]] für das Attribut „$3“.",
+ "smw-datavalue-constraint-error-allows-value-range": "„$1“ liegt nicht innerhalb des zulässigen Bereichs „$2“, der durch die Einschränkung mit „[[Property:Allows value|zulässigen Werten]]“ für das Attribut „$3“ festgelegt wurde.",
+ "smw_noboolean": "„$1“ ist kein Wahrheitswert (wahr/falsch).",
+ "smw_true_words": "wahr,w,ja,j",
+ "smw_false_words": "falsch,f,nein,n",
+ "smw_nofloat": "„$1“ ist keine Zahl.",
+ "smw_infinite": "Die Zahl „$1“ ist zu lang.",
+ "smw_unitnotallowed": "„$1“ wurde nicht als gültige Maßeinheit für dieses Attribut festgelegt.",
+ "smw_nounitsdeclared": "Es wurden keine Maßeinheiten für dieses Attribut angegeben.",
+ "smw_novalues": "Es wurden keine Werte angegeben.",
+ "smw_nodatetime": "Das Datum „$1“ wurde nicht verstanden.",
+ "smw_toomanyclosing": "In der Abfrage kommen zu viele „$1“ vor.",
+ "smw_noclosingbrackets": "Ein Vorkommen von „<nowiki>[[</nowiki>“ in der Abfrage wurde nicht durch ein entsprechendes „]]“ abgeschlossen.",
+ "smw_misplacedsymbol": "Das Symbol „$1“ wurde an einer Stelle verwendet, an der es nicht sinnvoll ist.",
+ "smw_unexpectedpart": "Der Teil „$1“ der Abfrage konnte nicht interpretiert werden. Die Ergebnisse entsprechen möglicherweise nicht den Erwartungen.",
+ "smw_emptysubquery": "Eine Unterabfrage enthält eine ungültige Bedingung.",
+ "smw_misplacedsubquery": "Eine Unterabfrage wurde an einer Stelle eingesetzt, an der sie nicht vorkommen darf.",
+ "smw_valuesubquery": "Teilabfragen werden für Werte des Attributs „$1“ nicht unterstützt.",
+ "smw_badqueryatom": "Ein Teil „<nowiki>[[…]]</nowiki>“ der Abfrage konnte nicht interpretiert werden.",
+ "smw_propvalueproblem": "Der Wert des Attributs „$1“ wurde nicht verstanden.",
+ "smw_noqueryfeature": "Einige Abfragebestandteile werden nicht von diesem Wiki unterstützt. Der entsprechende Teil der Abfrage wurde ignoriert ($1).",
+ "smw_noconjunctions": "UND-Verknüpfungen in den Abfragen werden nicht von diesem Wiki unterstützt. Der entsprechende Teil der Abfrage wurde ignoriert ($1).",
+ "smw_nodisjunctions": "Disjunktionen (ODER) in Abfragen werden nicht von diesem Wiki unterstützt. Der entsprechende Teil der Abfrage wurde ignoriert ($1).",
+ "smw_querytoolarge": "Die {{PLURAL:$2|folgende Abfragebedingung konnte|folgenden $2 Abfragebedingungen konnten}} aufgrund der auf diesem Wiki gültigen Beschränkungen bezüglich Größe und Tiefe von Abfragen nicht berücksichtigt werden: <code>$1</code>.",
+ "smw_notemplategiven": "Der Parameter „template“ muss angegeben werden, damit diese Abfrage durchgeführt werden kann.",
+ "smw_db_sparqlqueryproblem": "Das Abfrageergebnis konnte nicht aus der SPARQL-Datenbank abgerufen werden. Dieser Fehler kann vorübergehend sein oder auf einen Fehler Datenbank hinweisen.",
+ "smw_db_sparqlqueryincomplete": "Das Ausführen der Abfrage erwies sich als zu umfangreich und wurde abgebrochen. Einige der möglichen Ergebnisse könnten daher fehlen. Sofern möglich, sollte stattdessen eine einfachere Abfrage genutzt werden.",
+ "smw_type_header": "Attribute mit dem Datentyp „$1“",
+ "smw_typearticlecount": "Es {{PLURAL:$1|wird ein Attribut|werden $1 Attribute}} mit diesem Datentyp angezeigt:",
+ "smw_attribute_header": "Seiten mit dem Attribut „$1“",
+ "smw_attributearticlecount": "Es {{PLURAL:$1|wird eine Seiten|werden $1 Seiten}} angezeigt, die dieses Attribut {{PLURAL:$1|verwendet|verwenden}}:",
+ "smw-propertylist-subproperty-header": "Unterattribute",
+ "smw-propertylist-redirect-header": "Synonyme",
+ "smw-propertylist-error-header": "Seiten mit fehlerhaften Annotationen",
+ "smw-propertylist-count": "Es {{PLURAL:$1|wird ein ähnliches Objekt|werden $1 ähnliche Objekte}} angezeigt.",
+ "smw-propertylist-count-with-restricted-note": "Es {{PLURAL:$1|wird ein ähnliches Objekt|werden $1 ähnliche Objekte}} angezeigt. Weitere sind vorhanden, jedoch ist die Anzeige auf „$2“ beschränkt.",
+ "smw-propertylist-count-more-available": "Es {{PLURAL:$1|wird ein ähnliches Objekt|werden $1 ähnliche Objekte}} angezeigt. Weitere sind vorhanden.",
+ "exportrdf": "Seiten als RDF exportieren",
+ "smw_exportrdf_docu": "Hier können Informationen zu einzelnen Seiten im RDF-Format abgerufen werden. Bitte die Namen der gewünschten Seiten <i>zeilenweise</i> angeben.",
+ "smw_exportrdf_recursive": "Exportiere auch alle relevanten Seiten rekursiv. Diese Einstellung kann zu sehr großen Ergebnismengen führen!",
+ "smw_exportrdf_backlinks": "Exportiere auch alle Seiten, die auf zu exportierende Seiten verweisen. Diese Einstellung erzeugt RDF, das leichter durchsucht werden kann.",
+ "smw_exportrdf_lastdate": "Exportiere keine Seiten, die seit dem angegebenen Zeitpunkt nicht mehr verändert wurden.",
+ "smw_exportrdf_submit": "Exportieren",
+ "uriresolver": "URI-Auflöser",
+ "properties": "Attribute",
+ "smw_properties_docu": "In diesem Wiki werden die folgenden Attribute genutzt:",
+ "smw_property_template": "$1 mit Datentyp $2 ($3 {{PLURAL:$3|Vorkommen}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Dieses Attribut sollte auf seiner Seite mit Datentyp usw. beschrieben werden!",
+ "smw_propertylackstype": "Für dieses Attribut wurde kein Datentyp festgelegt ($1 wird daher als Datentyp angenommen).",
+ "smw_propertyhardlyused": "Dieses Attribut wird im Wiki kaum verwendet!",
+ "smw-property-name-invalid": "Das Attribut „$1“ kann nicht verwendet werden (ungültiger Attributname).",
+ "smw-property-name-reserved": "„$1“ ist als reservierter Name gelistet und sollte nicht als Attributname verwendet werden. Die folgende [https://www.semantic-mediawiki.org/wiki/Help:Property_naming Hilfeseite] könnte Informationen darüber enthalten, warum dieser Name reserviert ist.",
+ "smw-sp-property-searchform": "Attribute anzeigen, die Folgendes enthalten:",
+ "smw-sp-property-searchform-inputinfo": "Bei der Eingabe wird zwischen Groß- und Kleinschreibung unterschieden. Bei der Filterung werden nur Attribute angezeigt, die der Bedingung entsprechen.",
+ "smw-special-property-searchform": "Attribute anzeigen, die Folgendes enthalten:",
+ "smw-special-property-searchform-inputinfo": "Bei der Eingabe wird zwischen Groß- und Kleinschreibung unterschieden. Bei der Filterung werden nur Attribute angezeigt, die der Bedingung entsprechen.",
+ "smw-special-property-searchform-options": "Optionen",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "Keiner",
+ "smw-special-wantedproperties-filter-unapproved": "Unbestätigte",
+ "smw-special-wantedproperties-filter-unapproved-desc": "In Verbindung mit dem Berechtigungsmodus verwendete Filteroption.",
+ "concepts": "Konzepte",
+ "smw-special-concept-docu": "Ein [https://www.semantic-mediawiki.org/wiki/Help:Konzept Konzept] kann als „dynamische Kategorie“ verstanden werden. Es ist eine Zusammenstellung von Seiten die nicht manuell sondern aufgrund einer vorgegebenen Abfrage von Semantic MediaWiki maschinell erstellt wurde.",
+ "smw-special-concept-header": "Liste der Konzepte",
+ "smw-special-concept-count": "{{PLURAL:$1|Das folgende Konzept ist|Die folgenden Konzepte sind}} vorhanden.",
+ "smw-special-concept-empty": "Es wurde kein Konzept gefunden.",
+ "unusedproperties": "Verwaiste Attribute",
+ "smw-unusedproperties-docu": "Diese Seite listet alle [https://www.semantic-mediawiki.org/wiki/Unused_properties verwaisten Attribute] auf. Sie wurden auf einer Seite im Namensraum „Attribut:“ beschrieben, werden indes nicht im Wiki verwendet. Für eine differenzierte Übersicht, siehe auch die weiteren Spezialseiten „[[Special:Properties|Attribute]]“ und „[[Special:WantedProperties|Gewünschte Attribute]]“.",
+ "smw-unusedproperty-template": "$1 mit Datentyp $2",
+ "wantedproperties": "Gewünschte Attribute",
+ "smw-wantedproperties-docu": "Diese Seite listet alle [https://www.semantic-mediawiki.org/wiki/Wanted_properties gewünschten Attribute] auf. Sie werden im Wiki verwendet, wurden indes noch nicht auf einer Seite im Namensraum „Attribut:“ beschrieben. Für eine differenzierte Übersicht, siehe auch die weiteren Spezialseiten „[[Special:Properties|Attribute]]“ und „[[Special:UnusedProperties|Verwaiste Attribute]]“.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|Vorkommen}})",
+ "smw-special-wantedproperties-docu": "Diese Seite listet alle [https://www.semantic-mediawiki.org/wiki/Wanted_properties gewünschten Attribute] auf. Sie werden im Wiki verwendet, wurden indes noch nicht auf einer Seite im Namensraum „Attribut:“ beschrieben. Für eine differenzierte Übersicht, siehe auch die weiteren Spezialseiten „[[Special:Properties|Attribute]]“ und „[[Special:UnusedProperties|Verwaiste Attribute]]“.",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|Vorkommen}})",
+ "smw_purge": "Neu laden",
+ "smw-purge-failed": "Das Neuladen ist fehlgeschlagen.",
+ "types": "Datentypen",
+ "smw_types_docu": "Dies ist eine [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes Liste aller Datentypen], die Attributen zugewiesen werden können. Jeder [https://www.semantic-mediawiki.org/wiki/Help:Datatype Datentyp] beschreibt eindeutige Merkmale zur Datenspeicherung und -anzeige eines Datenwerts für ein Attribut, dem der entsprechende Datentyp zugeordnet wurde.",
+ "smw-special-types-no-such-type": "Der Datentyp „$1“ ist unbekannt oder ungültig.",
+ "smw-statistics": "Statistik zu semantischen Daten",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Attributwert|Attributwerte}} (gesamt)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Attribut|Attribute}}]] (gesamt)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Attribut|Attribute}} (gesamt)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Attribut|Attribute}}]] (genutzt)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Attribut|Attribute}} (auf einer Seite beschrieben)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Attribut|Attribute}} (einem Datentyp zugewiesen)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Abfrage|Abfragen}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Abfrage|Abfragen}}]]",
+ "smw-statistics-query-size": "Abfragegröße",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Konzept|Konzepte}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Konzept|Konzepte}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Unterobjekt|Unterobjekte}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Unterobjekt|Unterobjekte}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datentyp|Datentypen}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Attributwert|Attributwerte}} (mit [[Special:ProcessingErrorList|{{PLURAL:$1|fehlerhafter Annotation|fehlerhaften Annotationen}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Attributwert|Attributwerte}} (fehlerhafte {{PLURAL:$1|Annotation|Annotationen}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Veraltetes Objekt|Veraltete Objekte}} (zum Entfernen markiert)",
+ "smw_uri_doc": "Der URI-Auflöser setzt die Empfehlungen „[$1 W3C TAG finding on httpRange-14]“ um. Er sorgt dafür, dass Menschen nicht zu Webseiten werden.",
+ "ask": "Semantische Suche",
+ "smw-ask-help": "Dieser Abschnitt enthält einige Links zur Erläuterung der Nutzung der von Semantic MediaWiki bereitgestellten Abfragesyntax (<code>#ask:</code>-Syntax):\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Seiten auswählen] beschreibt, wie Seiten ausgewählt und Abfragebedingungen erstellt werden\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Search_operators Suchoperatoren] listet verfügbare Suchoperatoren inklusive solcher für Bereichs- und Platzhalterabfragen auf\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Informationen anzeigen] erläutert die Nutzung der Ausgabeanweisungen und Formatierungsoptionen",
+ "smw_ask_sortby": "Sortiere nach Spalte (optional)",
+ "smw_ask_ascorder": "Aufsteigend",
+ "smw_ask_descorder": "Absteigend",
+ "smw-ask-order-rand": "Zufällig",
+ "smw_ask_submit": "Ergebnisse suchen",
+ "smw_ask_editquery": "Abfrage bearbeiten",
+ "smw_add_sortcondition": "[Sortieranweisung hinzufügen]",
+ "smw-ask-sort-add-action": "Sortierbedingung hinzufügen",
+ "smw_ask_hidequery": "Code der Abfrage ausblenden (kompakte Ansicht)",
+ "smw_ask_help": "Hilfe zu Abfragen",
+ "smw_ask_queryhead": "Abfrageanweisungen (Bedingungen)",
+ "smw_ask_printhead": "Ausgabeanweisungen (Anzeige)",
+ "smw_ask_printdesc": "(ein Attribut je Zeile angeben)",
+ "smw_ask_format_as": "Formatiert als:",
+ "smw_ask_defaultformat": "Standard",
+ "smw_ask_otheroptions": "Weitere Optionen",
+ "smw-ask-otheroptions-info": "Dieser Abschnitt enthält Optionen, mit denen die Ergebnisausgabe gesteuert werden kann. Die Beschreibungen der Ausgabeparameter werden angezeigt, wenn man die Maus über deren Namen führt.",
+ "smw-ask-otheroptions-collapsed-info": "Bitte auf das kleine Plus-Symbol klicken, um sich die weiteren verfügbaren Optionen anzeigen zu lassen.",
+ "smw_ask_show_embed": "Code der Abfrage anzeigen",
+ "smw_ask_hide_embed": "Code der Abfrage verstecken",
+ "smw_ask_embed_instr": "Zum Einbetten dieser Abfrage in eine Wikiseite kann der unten angegebene Code verwendet werden:",
+ "smw-ask-delete": "Entfernen",
+ "smw-ask-sorting": "Sortierung",
+ "smw-ask-options": "Optionen",
+ "smw-ask-options-sort": "Sortierbedingungen",
+ "smw-ask-format-options": "Ausgabeformate und Optionen",
+ "smw-ask-parameters": "Parameter",
+ "smw-ask-search": "Suche",
+ "smw-ask-debug": "Fehleranalyse",
+ "smw-ask-debug-desc": "Erzeugt Informationen zur Fehleranalyse von Abfragen",
+ "smw-ask-no-cache": "Abfragecache deaktivieren",
+ "smw-ask-no-cache-desc": "Ergebnisse ohne Verwendung des Abfragecaches anzeigen",
+ "smw-ask-result": "Ergebnis",
+ "smw-ask-empty": "Alle Einträge löschen",
+ "smw-ask-download-link-desc": "Ermittelte Ergebnisse im Format $1 herunterladen",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Hilfe zum Ausgabeformat: $1",
+ "smw-ask-condition-change-info": "Die Abfragebedingungen wurden geändert. Es ist daher eine erneute Ausführung der Abfrage notwendig, um die den neuen Abfragebedingungen entsprechenden Ergebnisse zu ermitteln.",
+ "smw-ask-input-assistance": "Eingabeunterstützung",
+ "smw-ask-condition-input-assistance": "Es wird eine [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Eingabeunterstützung] in den Feldern für die Ausgabeanweisungen, Sortieranweisungen und Abfragebedingungen angeboten. Das Feld für Abfragebedingungen erfordert die Nutzung eines der folgenden Präfixe:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> zur Aktivierung einer Attributsuche (z.&nbsp;B. <code>[[p:Hat …</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> zur Aktivierung einer Kategoriesuche",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> zur Aktivierung einer Konzeptsuche",
+ "smw-ask-format-change-info": "Das Ausgabeformat wurde geändert. Es ist daher eine erneute Ausführung der Abfrage notwendig, um die den neuen Darstellungsoptionen entsprechenden Ergebnisse anzeigen zu können.",
+ "smw-ask-format-export-info": "Das ausgewählte Ergebnisformat dient dem Datenexport und verfügt über keine visuelle Darstellung. Die Ergebnisse werden nur zum Herunterladen bereitgestellt.",
+ "smw-ask-query-search-info": "Die Abfrage <code><nowiki>$1</nowiki></code> wurde von der Datenbank {{PLURAL:$3|1=„$2“ (aus dem Cache)|„$2“ (aus dem Cache)|„$2“}} in {{PLURAL:$4|einer Sekunde|$4 Sekunden}} beantwortet.",
+ "searchbyproperty": "Suche mittels Attribut",
+ "processingerrorlist": "Liste der Verarbeitungsfehler",
+ "propertylabelsimilarity": "Bericht zur Ähnlichkeit von Attributbezeichungen",
+ "smw-processingerrorlist-intro": "Die folgende Liste bietet eine Übersicht über die Verarbeitungsfehler, die beim Nutzen von [https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_–_Startseite Semantic MediaWiki] aufgetreten sind. Es wird empfohlen, diese Liste regelmäßig einzusehen und ungültige Attribut-Datenwert-Zuweisungen (Annotationen) zu korrigieren.",
+ "smw_sbv_docu": "Diese Spezialseite ermittelt alle Seiten, die einen bestimmten Wert für das angegebene Attribut haben.",
+ "smw_sbv_novalue": "Bitte den gewünschten Wert eingeben oder alle Werte für das Attribut $1 ansehen.",
+ "smw_sbv_displayresult": "Eine Liste aller Seiten, die ein Attribut $1 mit dem Wert $2 haben.",
+ "smw_sbv_displayresultfuzzy": "Eine Liste aller Seiten, die das Attribut „$1“ mit dem Wert „$2“ haben.\nWeil nur wenige Ergebnisse gefunden wurden, werden auch ähnliche Werte aufgelistet.",
+ "smw_sbv_property": "Attribut:",
+ "smw_sbv_value": "Wert:",
+ "smw_sbv_submit": "Ergebnisse suchen",
+ "browse": "Semantisches Browsen",
+ "smw_browselink": "Attribute anzeigen",
+ "smw_browse_article": "Bitte den Namen einer Seite angeben, um mit dem Browsen zu beginnen.",
+ "smw_browse_go": "Los",
+ "smw_browse_show_incoming": "Attribute anzeigen, die hierhin verlinken",
+ "smw_browse_hide_incoming": "Attribute ausblenden, die hierhin verlinken",
+ "smw_browse_no_outgoing": "Diese Seite enthält keine Attribute.",
+ "smw_browse_no_incoming": "Keine Attribute verlinken auf diese Seite.",
+ "smw-browse-from-backend": "Die Informationen werden momentan aus der Datenbank abgerufen.",
+ "smw-browse-intro": "Mit dieser Spezialseite können die auf Seiten oder mit Subobjekten gespeicherten Daten und Informationen eingesehen werden. Bitte gib den Namen einer Seite ein, die eingesehen werden soll.",
+ "smw-browse-invalid-subject": "Die Validierung der Seite ergab den Fehler „$1“.",
+ "smw-browse-api-subject-serialization-invalid": "Die Seite hat ein ungültiges Serialisierungsformat.",
+ "smw-browse-js-disabled": "JavaScript ist entweder deaktiviert oder nicht verfügbar. Es wird empfohlen, JavaScript zu aktivieren oder einen Browser zu nutzen, der JavaScript unterstützt. Weitere Optionen sind auf der Seite bezüglich des Konfigurationsparameters [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>] dokumentiert.",
+ "smw-browse-show-group": "Attributgruppen anzeigen",
+ "smw-browse-hide-group": "Attributgruppen ausblenden",
+ "smw-noscript": "Diese Seite oder Aktion erfordert JavaScript, damit sie funktioniert. Bitte aktiviere JavaScript in deinem Browser oder verwende einen Browser, der dies unterstützt, damit diese Funktionalität wie gewünscht angeboten werden kann. Weitere Informationen sind auf der entsprechenden [https://www.semantic-mediawiki.org/wiki/Help:Noscript Hilfeseite] vorhanden.",
+ "smw_inverse_label_default": "$1 von",
+ "smw_inverse_label_property": "Bezeichnung des inversen Attributs",
+ "pageproperty": "Attributwerte einer Seite",
+ "smw_pp_docu": "Entweder eine Seite und ein Attribut angeben oder nur ein Attribut, um alle zugewiesenen Werte zu erhalten.",
+ "smw_pp_from": "Von Seite:",
+ "smw_pp_type": "Attribut:",
+ "smw_pp_submit": "Ergebnisse anzeigen",
+ "smw_result_prev": "Vorherige",
+ "smw_result_next": "Nächste",
+ "smw_result_results": "Ergebnisse",
+ "smw_result_noresults": "Es wurden keine Ergebnisse gefunden.",
+ "smwadmin": "Administration und Wartung",
+ "smw-admin-statistics-job-title": "Statistiken zu Aufträgen",
+ "smw-admin-statistics-job-docu": "Die Statistiken zu Aufträgen zeigen Informationen zu noch nicht ausgeführten Aufträgen an, die von Semantic MediaWiki geplant wurden. Die Anzahl der Aufträge kann geringfügig ungenau sein oder auch Fehlversuche enthalten. Weitere Informationen befinden sind in der [https://www.mediawiki.org/wiki/Manual:Job_queue Dokumentation].",
+ "smw-admin-statistics-querycache-title": "Statistiken zum Abfragencache",
+ "smw-admin-statistics-querycache-disabled": "Das [https://www.semantic-mediawiki.org/wiki/QueryCache Cachen von Abfragen] wurde für dieses Wiki nicht aktiviert. Daher sind keine diesbezüglichen Statistiken verfügbar.",
+ "smw-admin-statistics-querycache-explain": "Die Statistiken zum Abfragencache enthalten veränderliche wie auch abgeleitete Daten, darunter:\n* Fehlversuche („<tt>misses</tt>“) – Die Zahl der direkten Abrufe von Abfrageergebnissen aus der Datenbank, da sie nicht im Cache verfügbar waren.\n* Löschungen („<tt>deletes</tt>“) – Die Zahl der Löschungen des Caches, entweder durch den Nutzer oder durch entsprechende Mechanismen der Software.\n* Treffer („<tt>hits</tt>“) – Die Zahl der Abrufe von Abfrageergebnissen aus dem Cache, da sie dort verfügbar waren, aufgeteilt nach auf Seiten eingebetteten Abfragen („<tt>embedded</tt>“) und nicht eingebetteten Abfragen („<tt>nonEmbedded</tt>“) auf der Spezialseite „Semantische Suche“ oder über die API.\n* Mittlere Antwortzeit für Abfrageergebnisse („<tt>medianRetrievalResponseTime</tt>“) – Die mittlere Antwortzeit für gecachte und nicht gecachte Abrufe von Abfrageergebnissen in Sekunden während des Erhebungszeitraums der Statistiken zum Abfragencache.\n* Kein Cache („<tt>noCache</tt>“) – Die Zahl der Abrufe von Abfrageergebnissen aus der Datenbank aufgrund entsprechender Vorgaben, d. h. durch die Nutzung der Abfrageoptionen <code>limit=0</code> oder <code>no-cache</code>.",
+ "smw-admin-permission-missing": "Der Zugriff auf diese Seite wurde aufgrund fehlender Berechtigungen verwehrt. Bitte lies die Hilfeseite zu den [https://www.semantic-mediawiki.org/wiki/Help:Permissions „Berechtigungen“], um Informationen bezüglich der hierfür notwendigen Einstellungen zu erhalten.",
+ "smw-admin-setupsuccess": "Die Datenbank wurde eingerichtet.",
+ "smw_smwadmin_return": "Zurück zur Seite $1.",
+ "smw_smwadmin_updatestarted": "Ein Aktualisierungsprozess zur Erneuerung der semantischen Daten wurde gestartet.\nAlle gespeicherten Daten werden, sofern notwendig, entweder neu erstellt oder repariert.\nDer Fortschritt der Aktualisierung kann auf dieser Spezialseite eingesehen werden.",
+ "smw_smwadmin_updatenotstarted": "Es läuft bereits ein Aktualisierungsprozess.\nEs wird kein neuer begonnen.",
+ "smw_smwadmin_updatestopped": "Alle laufenden Aktualisierungsprozesse wurden vorzeitig beendet.",
+ "smw_smwadmin_updatenotstopped": "Um den laufenden Aktualisierungsprozess vorzeitig zu beenden, muss das Kontrollkästchen markiert werden, um dadurch anzuzeigen, dass man sich diesbezüglich tatsächlich sicher ist.",
+ "smw-admin-docu": "Diese Spezialseite bietet während Installation, Aktualisierung, Wartung und Nutzung sowie bei weiteren administrativen Funktionen und Aufgaben von <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_–_Startseite\">Semantic MediaWiki</a> Unterstützung. Ebenso werden Statistiken bereitgestellt. Wichtige Daten sollten vor der Ausführung administrativer Funktionen gesichert werden.",
+ "smw-admin-environment": "Softwareumgebung",
+ "smw-admin-db": "Datenbankeinrichtung",
+ "smw-admin-db-preparation": "Die Initialisierung der Datenbanktabellen wird gerade durchgeführt. Es kann etwas dauern, bis die Ergebnisse angezeigt werden. Der Zeitbedarf ist abhängig von der Größe und ggf. dem Optimierungsbedarf der Datenbanktabellen.",
+ "smw-admin-dbdocu": "Semantic MediaWiki benötigt weitere Datentabellen in der von MediaWiki genutzten Datenbank, um die semantischen Daten speichern zu können. Die folgende Sonderaufgabe gewährleistet, dass die Datenbank richtig eingerichtet wird. Die Datenbankänderungen, die in diesem Schritt durchgeführt werden, beeinträchtigen den übrigen Teil der von MediaWiki genutzten Datenbank nicht. Sie können, sofern dies gewünscht ist, leicht rückgängig gemacht werden.\nDiese Sonderaufgabe zur Einrichtung der Datentabellen kann mehrere Male ausgeführt werden, ohne Schaden zu verursachen. Indes ist es lediglich notwendig, sie einmal während der Installation oder der Aktualisierung von Semantic MediaWiki durchzuführen.",
+ "smw-admin-permissionswarn": "Sofern diese Aktion mit einem SQL-Fehler abbricht, könnte es sein, dass der Datenbankbenutzer, mit dem das Wiki auf die Datenbank zugreift (siehe die Datei „LocalSettings.php“), über keine ausreichenden Rechte verfügt.\nUm das Problem zu beheben, ist es notwendig, entweder dem Datenbankbenutzer zusätzliche Rechte zur Erstellung und Löschung von Datenbanktabellen einzuräumen, den Datenbankadministrator zeitweilig in die Datei „LocalSettings.php“ einzutragen, oder aber das Wartungsskript „setupStore.php“ zu nutzen, das von einem Serveradministrator mit Administrationsberechtigung ausgeführt werden kann.",
+ "smw-admin-dbbutton": "Tabellen initialisieren oder aktualisieren",
+ "smw-admin-announce": "Wiki bekannt geben",
+ "smw-admin-announce-text": "Sofern dieses Wiki öffentlich zugänglich ist, kann es auf <a href=\"https://wikiapiary.com\">WikiApiary</a>, dem Wiki zum Tracken von Wikis, registriert werden.",
+ "smw-admin-deprecation-notice-title": "Änderungshinweise zu Konfigurationsparametern",
+ "smw-admin-deprecation-notice-docu": "In diesem Abschnitt werden Änderungen bezüglich Konfigurationsparametern angezeigt, die als veraltet gelten und/oder auf diesem Wiki aktiv konfiguriert sind. Die angegebenen Konfigurationsparameter werden mit künftigen Softwareversionen entfallen.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> ist veraltet und wird mit Version $2 entfernt.",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> – Die {{PLURAL:$2|folgende Option wird|folgenden Optionen werden}} entfernt oder ersetzt:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> ist veraltet und wird mit Version $2 entfernt.",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> wird durch <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code> ersetzt.",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> – Die {{PLURAL:$2|folgende Option wird|folgenden Optionen werden}} ersetzt:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> wird ersetzt durch <code>$2</code>.",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> wurde mit Version $2 entfernt.",
+ "smw-admin-deprecation-notice-title-notice": "Zukünftige Änderungen an Konfigurationsparametern",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Die im folgenden Abschnitt angegebenen Konfigurationsparameter werden in der Konfigurationsdatei des Wikis verwendet und werden mit künftigen Softwareveröffentlichungen verändert oder entfernt.",
+ "smw-admin-deprecation-notice-title-replacement": "Ersetzte oder umbenannte Konfigurationsparameter",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Die im folgenden Abschnitt angegebenen Konfigurationsparameter wurden umbenannt oder anderweitig verändert. Sie sollten umgehend in der Konfigurationsdatei des Wikis angepasst werden.",
+ "smw-admin-deprecation-notice-title-removal": "Entfallene Konfigurationsparameter",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Die im folgenden Abschnitt angegebenen Konfigurationsparameter wurden entfernt. Sie sollten umgehend aus der Konfigurationsdatei des Wikis entfernt werden.",
+ "smw-smwadmin-refresh-title": "Datenreparatur und -aktualisierung",
+ "smw_smwadmin_datarefresh": "Daten neu erstellen",
+ "smw_smwadmin_datarefreshdocu": "Es ist möglich alle Datenbanken von Semantic MediaWiki auf Basis des aktuellen Inhalts des Wikis wiederherzustellen.\nDies kann hilfreich sein, um eine fehlerhafte Datenbank zu erneuern oder um Daten zu aktualisieren, sofern bspw. aufgrund einer Softwareaktualisierung die Datenbankstruktur geändert wurde.\nDie Datenaktualisierung wird für jede Seite ausgeführt und daher einige Zeit in Anspruch nehmen.\nNachfolgend wird angezeigt, ob eine momentan Aktualisierung läuft bzw. ermöglicht es eine Aktualisierung zu starten oder zu stoppen (sofern diese Funktion nicht vom Websiteadministrator deaktiviert wurde).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Eine Aktualisierung wird bereits durchgeführt.</strong>\nEine Aktualisierung geht normalerweise nur langsam voran, da die Daten lediglich in kleinen Schritten erneuert werden, und zwar jedes Mal, wenn ein Benutzer auf das Wiki zugreift.\nUm diese Aktualisierung schneller zu Ende zu führen, kann man das MediaWiki-Wartungsskript <code>runJobs.php</code> einsetzen. Mit dem Parameter <code>--maxjobs 1000</code> beschränkt man dabei die Anzahl der Aktualisierungsschritte, die in einem Durchgang durchgeführt werden, um Probleme bei der Nutzung des hierfür erforderlichen Arbeitsspeichers zu vermeiden.\nGeschätzter Fortschritt der laufenden Aktualisierung:",
+ "smw_smwadmin_datarefreshbutton": "Datenaktualisierung planen",
+ "smw_smwadmin_datarefreshstop": "Diese Datenaktualisierung beenden",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, ich bin mir {{GENDER:$1|sicher}}.",
+ "smw-admin-job-scheduler-note": "Die meisten in diesem Abschnitt angegebenen Aktivitäten werden als Hintergrundauftrag ausgeführt, um Probleme während deren Ausführung zu vermeiden. Die [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist für deren Verarbeitung verantwortlich und es ist wichtig, dass das hierfür genutzte Wartungsskript „runJobs.php“ (siehe auch Konfigurationsparameter <code>$wgRunJobsAsync</code>) angemessen eingerichtet wird.",
+ "smw-admin-outdateddisposal-title": "Veraltete Objekte bereinigen",
+ "smw-admin-outdateddisposal-intro": "Einige Aktivitäten, wie bspw. das Ändern des Datentyps eines Attributs, das Löschen von Seiten oder das Korrigieren fehlerhafter Datenwerte, führen zu [https://www.semantic-mediawiki.org/wiki/Outdated_entities veralteten Datenobjekten]. Es ist empfehlenswert, diese periodisch in der Datenbank zu bereinigen. Es kann etwas dauern, bis der Auftrag zur Bereinigung ausgeführt und abgeschlossen ist. Dies ist davon abhängig, in welchem zeitlichen Intervall Aufträge ausgeführt werden.",
+ "smw-admin-outdateddisposal-active": "Ein Auftrag zur Bereinigung veralteter Objekte wurde erzeugt und eingeplant.",
+ "smw-admin-outdateddisposal-button": "Auftrag zur Bereinigung erzeugen",
+ "smw-admin-feature-disabled": "Diese Funktion wurde auf diesem Wiki deaktiviert. Hierzu können Informationen auf der <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">entsprechenden Hilfeseite</a> eingesehen werden.",
+ "smw-admin-propertystatistics-title": "Statistiken zu Attributen neu erstellen",
+ "smw-admin-propertystatistics-intro": "Erstellt die gesamten Statistiken zu Attributen neu und aktualisiert sowie korrigiert die [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Anzahl zugeordneter Attributwerte] auf den Seiten der Attribute.",
+ "smw-admin-propertystatistics-active": "Ein Auftrag zur Neuerstellung der Attributstatistik wurde erzeugt und eingeplant.",
+ "smw-admin-propertystatistics-button": "Auftrag zur Neuerstellung der Attributstatistiken erzeugen",
+ "smw-admin-fulltext-title": "Index der Volltextsuche neu erstellen",
+ "smw-admin-fulltext-intro": "Erstellt den Suchindex der Attributtabellen mit einem für die [https://www.semantic-mediawiki.org/wiki/Full-text Volltextsuche] vorgesehenen Datentyp neu. Änderungen an den Indexregeln wie bspw. geänderte Stoppwörter, neuer Stemmer usw., und/oder das Hinzufügen oder Ändern einer Tabelle erfordern das erneute Ausführen dieser Spezialaufgabe.",
+ "smw-admin-fulltext-active": "Ein Auftrag zur Neuerstellung des Suchindexes für die Volltextsuche wurde erzeugt und eingeplant.",
+ "smw-admin-fulltext-button": "Auftrag zur Neuerstellung des Suchindexes erzeugen",
+ "smw-admin-support": "Unterstützung erhalten",
+ "smw-admin-supportdocu": "Verschiedene Hilfen und Hinweise wurden erstellt, um im Fall eines Problems zu helfen:",
+ "smw-admin-installfile": "Sofern beim Installieren Probleme auftreten, könnten die in der Datei <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">„INSTALL“</a> und die auf der Seite mit der <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">Installationsdokumentation</a> enthaltenen Informationen weiterhelfen.",
+ "smw-admin-smwhomepage": "Die Dokumentation für die Nutzer von Semantic MediaWiki ist auf <b><a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_%E2%80%93_Startseite\">semantic-mediawiki.org</a></b> verfügbar.",
+ "smw-admin-bugsreport": "Softwarefehler können auf <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> gemeldet werden. Die Dokumentation zum <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">Melden von Softwarefehlern</a> bietet eine Anleitung zum Verfassen eines brauchbaren Fehlerberichts.",
+ "smw-admin-questions": "Im Fall von Fragen oder Anregungen kann man sich an den Diskussionen auf der <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">Mailingliste für Nutzer</a> oder im <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">Chatroom</a> beteiligen.",
+ "smw-admin-other-functions": "Weitere Statistiken",
+ "smw-admin-supplementary-section-title": "Zusätzliche Funktionen",
+ "smw-admin-supplementary-section-subtitle": "Allgemeine Funktionen",
+ "smw-admin-supplementary-section-intro": "In diesem Abschnitt werden weitere Funktionen zusätzlich zu denen der Wartung bereitgestellt. Es ist möglich, dass einige in der [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions Dokumentation] gelisteten Funktionen entweder zugriffsbeschränkt oder nicht verfügbar sind und deshalb nicht auf diesem Wiki genutzt werden können.",
+ "smw-admin-supplementary-settings-title": "Konfigurationseinstellungen",
+ "smw-admin-supplementary-settings-intro": "$1 – Zeigt eine Liste der verfügbaren von Semantic MediaWiki verwendeten Konfigurationseinstellungen",
+ "smw-admin-supplementary-operational-statistics-title": "Statistiken zu Nutzung und Betrieb",
+ "smw-admin-supplementary-operational-statistics-intro": "$1 – Zeigt zu Semantic MediaWiki verfügbare Statistiken (semantische Daten, Aufträge, Abfragecache)",
+ "smw-admin-supplementary-idlookup-title": "Objekte ermitteln und bereinigen",
+ "smw-admin-supplementary-idlookup-intro": "$1 – Bietet Funktionen zum Ermitteln und Löschen einzelner Objekte",
+ "smw-admin-supplementary-duplookup-title": "Doppelte Datenobjekte",
+ "smw-admin-supplementary-duplookup-intro": "$1 – Listet die Datenobjekte in der Datenbank auf, die als doppelt vorhanden markiert sind",
+ "smw-admin-supplementary-duplookup-docu": "Diese Seite listet Datenobjekte in der [https://www.semantic-mediawiki.org/wiki/Help:Entity_table Datenbank] auf, die als doppelt vorhanden markiert wurden. Doppelte Datenobjekte sollten, wenn überhaupt, nur in seltenen Fällen auftreten. Dies kann bspw. der Fall sein, sofern ein Datenaktualisierungsvorgang vorzeitig beendet wurde oder bei einem nicht erfolgreichen Datenwiederherstellungsvorgang.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Statistiken zum Cache",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "$1 – Zeigt Statistiken zur Nutzung des Caches an",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Funktionen zu Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "$1 – informiert über Einstellungen und Indexstatistiken",
+ "smw-admin-supplementary-elastic-docu": "Diese Seite bietet Informationen zu Einstellungen, Mappings, Betriebsstatus sowie Indexstatistiken des Elasticsearch-Clusters, der von Semantic MediaWiki über dessen [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore „ElasticStore“] genutzt wird.",
+ "smw-admin-supplementary-elastic-functions": "Unterstützte Funktionen",
+ "smw-admin-supplementary-elastic-settings-title": "Einstellungen",
+ "smw-admin-supplementary-elastic-settings-intro": "$1 – Zeigt eine Übersicht der Einstellungen, die von Elasticsearch verwendet werden, um die von Semantic MediaWiki genutzten Indizes zu verwalten",
+ "smw-admin-supplementary-elastic-mappings-title": "Mappings",
+ "smw-admin-supplementary-elastic-mappings-intro": "$1 – Zeigt die Liste der genutzten Indizes und Feldmappings",
+ "smw-admin-supplementary-elastic-mappings-docu": "Diese Seite enthält Einzelheiten zu Feldmappings, die von den aktuellen Indizes verwendet werden. Die Zusammenfassung zu den Mappings sollte in Verbindung mit dem Konfigurationsparameter <code>index.mapping.total_fields.limit</code> überwacht werden, der die maximale Anzahl der zulässigen Felder eines Index angibt.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Zusammenfassung",
+ "smw-admin-supplementary-elastic-mappings-fields": "Feldmappings",
+ "smw-admin-supplementary-elastic-nodes-title": "Knoten",
+ "smw-admin-supplementary-elastic-nodes-intro": "$1 – Zeigt Statistiken des genutzten Knotens",
+ "smw-admin-supplementary-elastic-indices-title": "Indizes",
+ "smw-admin-supplementary-elastic-indices-intro": "$1 – Bietet einen Überblick zu den genutzten Indizes mitsamt zugehöriger Statistiken",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistiken",
+ "smw-admin-supplementary-elastic-statistics-intro": "$1 – Zeigt Statistiken der Indexebenen",
+ "smw-admin-supplementary-elastic-statistics-docu": "Diese Seite bietet Einblick in die Statistiken zu den verschiedenen Betriebsabläufen auf der Betriebsebene der genutzten Indizes. Die Statistiken werden für die Gesamtebene des jeweiligen Index sowie für dessen primäre Unterebenen zusammengefasst ausgegeben. Die [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html Hilfeseite] enthält eine ausführliche Beschreibung der verfügbaren Indexstatistiken.",
+ "smw-admin-supplementary-elastic-status-replication": "Status der Datenreplikation",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Zuletzt durchgeführte Datenreplikation: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Aktualisierungszeitspanne: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Rückstand an Datenwiederherstellungsaufträgen: $1 (Schätzung)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Rückstand an Dateiindexierungsaufträgen: $1 (Schätzung)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replikation gesperrt: $1 (Datenwiederherstellung wird durchgeführt)",
+ "smw-list-count": "Die Liste enthält {{PLURAL:$1|einen Eintrag|$1 Einträge}}.",
+ "smw-list-count-from-cache": "Die Liste enthält {{PLURAL:$1|einen Eintrag|$1 Einträge}} aus dem Cache. (Zuletzt aktualisiert: $2 UTC)",
+ "smw-property-label-uniqueness": "Die Bezeichnung „$1“ entspricht mindestens der eines weiteren Attributs. Siehe hierzu die [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness Hilfeseite] mit Informationen zur Lösung dieses Problems.",
+ "smw-property-label-similarity-title": "Bericht zur Ähnlichkeit von Attributbezeichungen",
+ "smw-property-label-similarity-intro": "<u>$1</u> ermittelt Ähnlichkeiten zwischen vorhandenen Attributbezeichnungen",
+ "smw-property-label-similarity-threshold": "Schwellenwert:",
+ "smw-property-label-similarity-type": "Bezeichner des Datentyps anzeigen",
+ "smw-property-label-similarity-noresult": "Den Auswahlkriterien entsprechend keine Ergebnisse.",
+ "smw-property-label-similarity-docu": "Diese Spezialseite vergleicht die [https://www.semantic-mediawiki.org/wiki/Property_similarity syntaktische Ähnlichkeit], nicht aber die semantische Ähnlichkeit, zwischen zwei Attributbezeichnungen und gibt diese an. Diese Analyse kann beim Ermitteln von orthografisch falsch geschriebenen oder gleichwertigen Attributen helfen, die konzeptionell gleichbedeutend sind. Siehe auch die Spezialseite [[Special:Properties|„Attribute“]], um Konzeption und Nutzung der hier angegebenen Attribute weiter zu klären. Der Schwellenwert für die Analyse kann angepasst werden, um die Ähnlichkeits- und Distanzmaße entweder zu erweitern oder zu verringern. Zudem kann das Attribut [[Property:$1|„$1“]] dazu genutzt werden, um bestimmte Attribute von der hier durchgeführten Analyse auszuschließen.",
+ "smw-admin-operational-statistics": "Diese Seite zeigt Statistiken, die bezüglich der Nutzung oder zum Betrieb von Semantic MediaWiki ermittelt wurden. Eine erweiterte Übersicht wikispezifischer Statistiken ist auf der Spezialseite [[Special:Statistics|<b>Statistik</b>]] verfügbar.",
+ "smw_adminlinks_datastructure": "Datenstruktur",
+ "smw_adminlinks_displayingdata": "Datenanzeige",
+ "smw_adminlinks_inlinequerieshelp": "Hilfe zu eingebetteten Abfragen",
+ "smw-page-indicator-usage-count": "Ungefähre [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Anzahl zugeordneter Attributwerte]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|Von einem Benutzer festgelegtes Attribut|Vom System vorgegebenes Attribut}}",
+ "smw-property-indicator-last-count-update": "Ungefähre Anzahl der Datenwertzuordnungen zu diesem Attribut\nZuletzt aktualisiert: $1",
+ "smw-concept-indicator-cache-update": "Anzahl der Seiten dieses Konzepts (gecachter Wert)\n\nZuletzt aktualisiert: $1",
+ "smw-createproperty-isproperty": "Dies ist ein Attribut des Datentyps $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Der mögliche Wert für dieses Attribut ist|Die möglichen Werte für dieses Attribut sind}}:",
+ "smw-paramdesc-category-delim": "Legt fest, welches Trennzeichen bei der Ausgabe der Abfrageergebnisse genutzt werden soll",
+ "smw-paramdesc-category-template": "Legt fest, welche Vorlage bei der Ausgabe der Abfrageergebnisse verwendet werden soll",
+ "smw-paramdesc-category-userparam": "Legt fest, welcher Parameter an die Vorlage bei der Ausgabe der Abfrageergebnisse weitergegeben werden soll",
+ "smw-info-par-message": "Die anzuzeigende Nachricht",
+ "smw-info-par-icon": "Das anzuzeigende Symbol, entweder „Info“ (<code>info<code>) oder „Warnung“(<code>warning</code>)",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Allgemeine Optionen",
+ "prefs-ask-options": "Optionen für die Spezialseite „Semantische Suche“",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_–_Startseite Semantic MediaWiki] und zugehörige Erweiterungen bieten die Möglichkeit individueller Einstellungen für ausgewählte Funktionen. Siehe hierzu auch die entsprechende [https://www.semantic-mediawiki.org/wiki/Help:Benutzereinstellungen Hilfeseite] für eine ausführliche Beschreibung.",
+ "smw-prefs-ask-options-tooltip-display": "Beschreibung der möglichen Ausgabeparameter zu Abfragen als Tooltip anzeigen",
+ "smw-prefs-ask-options-compact-view-basic": "Kompakte Ansicht aktivieren",
+ "smw-prefs-help-ask-options-compact-view-basic": "Falls aktiviert, wird auf der Spezialseite „Semantische Suche“ in der kompakten Ansicht eine reduzierte Anzahl von Links angezeigt.",
+ "smw-prefs-general-options-time-correction": "Zeitkorrektur für Spezialseiten mithilfe der lokalen [[Special:Preferences#mw-prefsection-rendering|Einstellung zum Zeitunterschied]] aktivieren",
+ "smw-prefs-general-options-jobqueue-watchlist": "Auftragswarteschlangen-Beobachtungsliste in meiner persönlichen Navigationsleiste anzeigen",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Falls aktiviert, wird eine [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist Liste ausstehender Softwareaufträge] zusammen mit dem geschätzten Umfang der entsprechenden Auftragswarteschlange angezeigt.",
+ "smw-prefs-general-options-disable-editpage-info": "Hinweise während des Bearbeitens von Seiten deaktivieren",
+ "smw-prefs-general-options-disable-search-info": "Hinweise zur für die Suche nutzbaren Wikisyntax auf der Standard-Suchseite deaktivieren",
+ "smw-prefs-general-options-suggester-textinput": "Eingabehilfen für semantische Objekte aktivieren",
+ "smw-prefs-help-general-options-suggester-textinput": "Falls aktiviert, ist es von Eingabefeldern aus möglich, eine [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Eingabehilfe] zu Attributen, Konzepten und Kategorien aufzurufen.",
+ "smw-ui-tooltip-title-property": "Attribut",
+ "smw-ui-tooltip-title-quantity": "Einheitenumrechnung",
+ "smw-ui-tooltip-title-info": "Information",
+ "smw-ui-tooltip-title-service": "Servicelinks",
+ "smw-ui-tooltip-title-warning": "Warnung",
+ "smw-ui-tooltip-title-error": "Fehler",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Ereignis",
+ "smw-ui-tooltip-title-note": "Hinweis",
+ "smw-ui-tooltip-title-legend": "Legende",
+ "smw-ui-tooltip-title-reference": "Referenzierung",
+ "smw_unknowntype": "Der Datentyp „$1“ dieses Attributs ist ungültig.",
+ "smw-concept-cache-text": "Das Konzept enthält {{PLURAL:$1|eine Seite|$1 Seiten}} und wurde letztmalig am $2 um $3 Uhr aktualisiert.",
+ "smw_concept_header": "Seiten mit dem Konzept „$1“",
+ "smw_conceptarticlecount": "Unterhalb {{PLURAL:$1|wird eine Seite|werden $1 Seiten}} angezeigt.",
+ "smw-qp-empty-data": "Die angeforderten Daten konnten aufgrund unzureichender Auswahlkriterien nicht angezeigt werden.",
+ "right-smw-admin": "Semantic MediaWiki administrieren",
+ "right-smw-patternedit": "Zulässige Textmuster anhand regulärer Ausdrücke erstellen und bearbeiten",
+ "right-smw-pageedit": "Seiten bearbeiten, die mit einem positiven Wahrheitswert für das Spezialattribut „Ist bearbeitungsgeschützt“ annotiert wurden",
+ "right-smw-ruleedit": "Regeln erstellen und bearbeiten",
+ "restriction-level-smw-pageedit": "geschützt (nur berechtigte Benutzer)",
+ "action-smw-patternedit": "reguläre Ausdrücke zu erstellen und/oder zu bearbeiten, die von Semantic MediaWiki verwendet werden",
+ "action-smw-pageedit": "Seiten zu bearbeiten, die mit einem positiven Wahrheitswert für das Spezialattribut „Ist bearbeitungsgeschützt“ annotiert wurden",
+ "group-smwadministrator": "SMW-Administratoren",
+ "group-smwadministrator-member": "{{GENDER:$1|SMW-Administrator|SMW-Administratorin}}",
+ "grouppage-smwadministrator": "{{ns:project}}:SMW-Administratoren",
+ "group-smwcurator": "SMW-Kuratoren",
+ "group-smwcurator-member": "{{GENDER:$1|SMW-Kurator|SMW-Kuratorin}}",
+ "grouppage-smwcurator": "{{ns:project}}:SMW-Kuratoren",
+ "action-smw-admin": "Semantic MediaWiki zu administrieren",
+ "action-smw-ruleedit": "Regeln zu erstellen und zu bearbeiten",
+ "smw-property-predefined-default": "„$1“ ist ein Spezialattribut.",
+ "smw-property-predefined-common": "Dieses Attribut ist softwareseitig fest definiert und auch bekannt als [https://www.semantic-mediawiki.org/wiki/Help:Spezialattribute Spezialattribut]. Es erfüllt eine besondere Funktion, kann aber wie jedes andere [https://www.semantic-mediawiki.org/wiki/Help:Attribut benutzerdefinierte Attribut] verwendet werden.",
+ "smw-property-predefined-ask": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Metainformationen einer Abfrage als [https://www.semantic-mediawiki.org/wiki/Subobject Subobjekt] speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-asksi": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Anzahl der von einer Abfrage verwendeten Bedingungen speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-askde": "„$1“ ist ein softwareseitig fest definiertes Attribut, das über die Tiefe einer Abfrage informiert. Dieses Attribut wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-askde": "Es handelt sich um einen numerischen Wert, der der Summe verschachtelter Unterabfragen, verarbeiteten Attributketten sowie vorhandenen Elementen zur Beschreibung einer Abfrage entspricht. Er wird durch den mit dem Konfigurationsparameter [https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth <code>$smwgQMaxDepth</code>] angegebenen Wert beschränkt.",
+ "smw-property-predefined-askpa": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Parameter einer Abfrage speichert, die deren Ergebnis beeinflussen. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-askpa": "Es ist Teil einer Sammlung von Spezialattributen, mit denen das [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler Profil einer Abfrage] erstellt wird.",
+ "smw-sp-properties-docu": "Diese Spezialseite listet die [https://www.semantic-mediawiki.org/wiki/Property Attribute] und den Umfang ihrer Nutzung in diesem Wiki auf. Es wird empfohlen regelmäßig das [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics Wartungsskript zur Aktualisierung der Nutzungsstatistik] auszuführen, um stets eine aktuelle Übersicht zu haben. Für eine differenzierte Ansicht gibt es zudem die Spezialseiten für [[Special:UnusedProperties|nicht genutzte]] und [[Special:WantedProperties|gewünschte]] Attribute.",
+ "smw-sp-properties-cache-info": "Die aufgelisteten Daten stammen aus dem [https://www.semantic-mediawiki.org/wiki/Caching Zwischenspeicher]. Letzte Aktualisierung: $1.",
+ "smw-sp-properties-header-label": "Attributliste",
+ "smw-admin-settings-docu": "Diese Seite zeigt eine Liste der Standardeinstellungen sowie der für dieses Wiki angepassten Einstellungen zur Konfiguration von Semantic MediaWiki an. Weitergehende Informationen sind auf der Hilfeseite zu den [https://www.semantic-mediawiki.org/wiki/Help:Configuration Konfigurationseinstellungen] verfügbar.",
+ "smw-sp-admin-settings-button": "Liste erstellen",
+ "smw-admin-idlookup-title": "Ermittlung",
+ "smw-admin-idlookup-docu": "In diesem Abschnitt werden die technischen Einzelheiten zu einem Objekt (Wikiseite, Unterobjekt, Attribut usw.) in Semantic MediaWiki angezeigt. Die Eingabe kann eine zutreffende Kennung oder Zeichenkette sein. Diese Kennungen und Zeichenketten sind nicht zu verwechseln mit den von MediaWiki verwendeten Seitenbezeichnungen oder Versionskennungen.",
+ "smw-admin-iddispose-title": "Bereinigung",
+ "smw-admin-iddispose-docu": "Die Bereinigung einer Objektkennung wird nach der Bestätigung unbeschränkt durchgeführt, d. h. das Objekt wird nach erfolgter Bestätigung direkt mitsamt allen Verknüpfungen aus der Datenbank entfernt. Diese Aktion sollte daher nur mit großer '''Vorsicht''' und nur nach Einsicht in die [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal entsprechende Dokumentation] durchgeführt werden.",
+ "smw-admin-iddispose-done": "Die Objektkennung „$1“ wurde aus der Datenbank entfernt.",
+ "smw-admin-iddispose-references": "Die Kennung „$1“ hat {{PLURAL:$2|keinen|mindestens einen}} aktiven Verweis auf eine Datenbanktabelle:",
+ "smw-admin-iddispose-references-multiple": "Liste der Kennungen mit mindestens einem aktiven Verweis auf eine Datenbanktabelle.",
+ "smw-admin-iddispose-no-references": "Der Eintrag „$1“ wurde in keiner Datenbanktabelle gefunden.",
+ "smw-admin-idlookup-input": "Suche:",
+ "smw-admin-objectid": "Kennung:",
+ "smw-admin-tab-general": "Ãœbersicht",
+ "smw-admin-tab-notices": "Hinweise zur Konfiguration",
+ "smw-admin-tab-maintenance": "Wartung",
+ "smw-admin-tab-supplement": "Zusätzliche Funktionen",
+ "smw-admin-tab-registry": "Wikiregistrierung",
+ "smw-admin-maintenance-no-description": "Es ist keine Beschreibung vorhanden.",
+ "smw-admin-maintenance-script-section-title": "Liste verfügbarer Wartungsskripte",
+ "smw-admin-maintenance-script-section-intro": "Die folgenden Wartungsskripte können nur von einem Systemadministrator über die Kommandozeile des Servers ausgeführt werden:",
+ "smw-admin-maintenance-script-description-dumprdf": "Ermöglicht den RDF-Export der vorhandenen semantische Tripel.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "Ermöglicht die Verwaltung (Erstellung, Löschung und Aktualisierung) semantischer Konzepte.",
+ "smw-admin-maintenance-script-description-rebuilddata": "Ermöglicht das Erstellen oder Aktualisieren aller in der Datenbank gespeicherten semantischen Daten.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "Ermöglicht das Erstellen oder Aktualisieren aller im Elasticsearch-Index gespeicherten semantischen Daten, sofern Elasticsearch genutzt wird.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "Ermöglicht das Erstellen oder Aktualisieren aller im Suchindex der Volltextsuche gespeicherten Daten, sofern die Volltextsuche genutzt wird.",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "Ermöglicht das Aktualisieren der Statistik zur Attributnutzung.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "Ermöglicht das Entfernen von Datenobjektduplikaten, die in der Datenbank über keine aktiven Verknüpfungen mehr verfügen.",
+ "smw-admin-maintenance-script-description-setupstore": "Ermöglicht das Erstellen oder Aktualisieren der für die Speicherung semantischer Daten konfigurierten Datenbank.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "Ermöglicht das Aktualisieren des Datenbankfelds <code>smw_sort</code> in der relationalen Datenbank (in Übereinstimmung mit der [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation Konfiguration des Parameters <code>$smwgEntityCollation</code>]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "Ermöglicht das Befüllen des Datenbankfelds <code>smw_hash</code> in der relationalen Datenbank.",
+ "smw-livepreview-loading": "Lade …",
+ "smw-sp-searchbyproperty-description": "Diese Seite stellt eine einfache [https://www.semantic-mediawiki.org/wiki/Help:Semantisches_Browsen Suchoberfläche] zum Finden von Objekten bereit, die ein Attribut mit einem bestimmten Datenwert enthalten. Andere verfügbare Suchoberflächen sind die [[Special:PageProperty|Attributsuche]] sowie der [[Special:Ask|Abfragengenerator]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Liste der Ergebnisse",
+ "smw-sp-searchbyproperty-nonvaluequery": "Eine Liste der Datenwerte des Attributs „$1“.",
+ "smw-sp-searchbyproperty-valuequery": "Eine Liste der Seiten, die das Attribut „$1“ mit dem Datenwert „$2“ enthalten.",
+ "smw-datavalue-number-textnotallowed": "Der Datenwert „$1“ kann einem Attribut des Datentyps Zahl nicht zugeordnet werden sondern bspw. der Datenwert „$2“.",
+ "smw-datavalue-number-nullnotallowed": "Der Datenwert „$1“ wurde mit „NULL“ zurückgegeben, was als Wert für eine Zahl nicht zulässig ist.",
+ "smw-editpage-annotation-enabled": "Diese Seite kann mit semantischen Annotationen in Form von bspw. <code><nowiki>[[</nowiki>Gehört zu::Dokumentation]]</code> versehen werden, um strukturierte wie abfragbare Inhalte zu erfassen. Ausführliche Hinweise zum [https://www.semantic-mediawiki.org/wiki/Help:Attribute_und_Datentypen#Attribute Einfügen von Annotationen] oder [https://www.semantic-mediawiki.org/wiki/Help:Eingebettete_Abfrage Erstellen von Abfragen] sind auf der Website zu Semantic MediaWiki verfügbar.<!--[[Is specified as::World Heritage Site]]-->",
+ "smw-editpage-annotation-disabled": "Diese Seite kann nicht mit semantischen Annotationen versehen werden, da der Namensraum hierfür nicht konfiguriert wurde.",
+ "smw-editpage-property-annotation-enabled": "Dieses Attribut kann noch mit einem Datentyp in Form von bspw. <code><nowiki>[[</nowiki>Datentyp::Seite]]</code> oder weiteren unterstützten Deklarationen, z. B. <code><nowiki>[[</nowiki>Unterattribut von::Dokumentation]]</code>, versehen werden. Ausführliche Hinweise zu [https://www.semantic-mediawiki.org/wiki/Help:Datentyp#Datentypen Datentypen] sowie eine [https://www.semantic-mediawiki.org/wiki/Help:Datentyp#Liste_der_Datentypen Liste der Datentypen] sind auf der Website zu Semantic MediaWiki verfügbar.<!--[[Has type::Page]], [[Subproperty of::dc:date]]-->",
+ "smw-editpage-property-annotation-disabled": "Dieses Attribut kann nicht mit einem Datentyp versehen werden, da dieser bereits systemseitig festgelegt ist (Spezialattribut). Ausführliche Hinweise zu [https://www.semantic-mediawiki.org/wiki/Help:Spezialattribute Spezialattributen] sind auf der Website zu Semantic MediaWiki verfügbar.<!--[[Has type::Page]]-->",
+ "smw-editpage-concept-annotation-enabled": "Dieses Konzept kann mithilfe der Parserfunktion #concept erweitert werden. Für eine Beschreibung zur Verwendung von #concept, siehe die Hilfeseite zu [https://www.semantic-mediawiki.org/wiki/Help:Concepts Konzepten].",
+ "smw-search-syntax-support": "Die Sucheingabe unterstützt die Nutzung der von Semantic MediaWiki bereitgestellten [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search Abfragesyntax] (<code>#ask:</code>-Syntax) zum Ermitteln der Suchergebnisse.",
+ "smw-search-input-assistance": "Die [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Eingabehilfe] ist ebenfalls aktiviert, um die Auswahl von Attributen und Kategorien zu vereinfachen.",
+ "smw-search-help-intro": "Eine Eingabe von <code><nowiki>[[ ... ]]</nowiki></code> bewirkt, dass die strukturierte Suche von Semantic MediaWiki genutzt wird. Die Kombination von <code><nowiki>[[ ... ]]</nowiki></code> mit einer unstrukturierten Textsuche wie bspw. <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code> wird nicht unterstützt.",
+ "smw-search-help-structured": "Strukturierte Suchanfragen:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (als [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context Filterung])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (mit einer [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context Abfrage])",
+ "smw-search-help-proximity": "Näherungssuchanfragen (ein Attribut ist unbekannt, '''nur''' verfügbar für solche Datenbanken, die eine Volltextsuche bereitstellen):\n\n* <code><nowiki>[[in:lorem ipsum]]</nowiki></code> (sucht in allen indexierten Dokumenten nach „lorem“ und „ipsum“)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (sucht nach „lorem ipsum“ als Ausdruck)",
+ "smw-search-help-ask": "Die folgenden Links verweisen auf Seiten, auf denen die semantische Abfragesyntax (<code>#ask:</code>-Syntax) erläutert wird:\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Seiten auswählen] beschreibt, wie Seiten ausgewählt und Abfragebedingungen erstellt werden\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Search_operators Suchoperatoren] listet verfügbare Suchoperatoren inklusive solcher für Bereichs- und Platzhalterabfragen auf",
+ "smw-search-input": "Eingabe und Suche",
+ "smw-search-help-input-assistance": "Für das Eingabefeld wird eine [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Eingabeunterstützung] angeboten. Diese erfordert die Nutzung eines der folgenden Präfixe:\n\n* <code>p:</code> zur Aktivierung einer Attributsuche (z.&nbsp;B. <code><nowiki>[[p:Has ...</nowiki></code>)\n\n* <code>c:</code> zur Aktivierung einer Kategoriesuche\n\n* <code>con:</code> zur Aktivierung einer Konzeptsuche",
+ "smw-search-syntax": "Syntax",
+ "smw-search-profile": "Extra",
+ "smw-search-profile-tooltip": "Suchfunktionen in Verbindung mit Semantic MediaWiki",
+ "smw-search-profile-sort-best": "Beste Treffer",
+ "smw-search-profile-sort-recent": "Aktuellste Treffer",
+ "smw-search-profile-sort-title": "Titel",
+ "smw-search-profile-extended-help-intro": "Das [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile erweiterte Suchprofil] bietet auf der Seite „Spezial:Suche“ Zugriff auf Suchfunktionen, die spezifisch für die Erweiterung „Semantic MediaWiki“ sind. Die Suchfunktionen bestehen aus:",
+ "smw-search-profile-extended-help-sort": "Ermöglicht die Anpassung der Anzeige von Suchergebnissen mit den folgenden Auswahlmöglichkeiten:",
+ "smw-search-profile-extended-help-sort-title": "* „Titel“ – verwendet den Seiten- oder Anzeigetitel als Sortierkriterium",
+ "smw-search-profile-extended-help-sort-recent": "* „Aktuellste Treffer“ – verwendet den Zeitpunkt der letzten Änderung als Sortierkriterium. Ergebnisse aus Unterobjekten werden indes nicht angezeigt, da sie generell nicht mit dem notwendigen Attribut „[[Property:Modification date|Zuletzt geändert]]“ annotiert werden.",
+ "smw-search-profile-extended-help-sort-best": "* „Beste Treffer“ – verwendet die [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy Relevanz von Seiten] als Sortierkriterium. Die Relevanz stützt sich auf sogenannte „Scores“, die von Elasticsearch ermittelt und bereitgestellt werden.",
+ "smw-search-profile-extended-help-form": "Formulare werden Benutzern zur Verfügung gestellt, um spezielle Nutzungsfälle bei der Suche abzudecken. Formulare können unterschiedliche Attribut- und Werteingabefelder enthalten, um es Benutzern leichter zu machen, eine Suche durchzuführen. (siehe $1)",
+ "smw-search-profile-extended-help-namespace": "Der Auswahlkasten für Namensräume wird ausgeblendet, sobald ein Formular ausgewählt wurde. Dieser kann jedoch mithilfe der Schaltflächen „Anzeigen“ wieder sichtbar gemacht werden.",
+ "smw-search-profile-extended-help-search-syntax": "Das Eingabefeld der Suche unterstützt die Nutzung der von Semantic MediaWiki bereitgestellten Abfragesyntax (<code>#ask:</code>-Syntax) zur Durchführung einer semantischen Suche. Nützliche Suchausdrücke sind:",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code>, um alles zu finden, was die Eingabe \"…\" enthält. Dies ist insbesondere nützlich, wenn die gesuchten Begriffe nicht eindeutig und/oder die Attribute unbekannt sind (z.&nbsp;B.:<code>in:(lorem && ipsum)</code> entspricht <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code>, um alles zu finden, was exakt der Eingabe \"…\" entspricht",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code>, um alle Objekte eines angegebenen Attributs \"…\" zu finden (z.&nbsp;B.: <code>has:(Foo && Bar)</code> entspricht <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code>, um keines der Objekte zu finden, das die Eingabe \"…\" enthält",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Zusätzliche benutzerdefinierte Suchausdrücke sind verfügbar und definiert wie beispielsweise: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Einige Suchausdrücke sind reserviert wie beispielsweise: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''Einige der gelisteten Suchausdrücke sind nur in Verbindung mit einem aktivierten Volltextsuchindex oder einer ElasticSearch-Datenbank nützlich.''",
+ "smw-search-profile-extended-help-query": "<code><nowiki>$1</nowiki></code> wurde für die Abfrage verwendet.",
+ "smw-search-profile-extended-help-query-link": "(Für weitere Einzelheiten $1).",
+ "smw-search-profile-extended-help-find-forms": "verfügbare Formulare",
+ "smw-search-profile-extended-section-sort": "Sortieren nach",
+ "smw-search-profile-extended-section-form": "Formulare",
+ "smw-search-profile-extended-section-search-syntax": "Sucheingabe",
+ "smw-search-profile-extended-section-namespace": "Namensraum",
+ "smw-search-profile-extended-section-query": "Abfrage",
+ "smw-search-profile-link-caption-query": "siehe",
+ "smw-search-show": "Anzeigen",
+ "smw-search-hide": "Ausblenden",
+ "log-name-smw": "Semantic-MediaWiki-Logbuch",
+ "log-show-hide-smw": "Semantic-MediaWiki-Logbuch $1",
+ "logeventslist-smw-log": "Semantic-MediaWiki-Logbuch",
+ "log-description-smw": "Dies ist das Logbuch, mit dem die Aktivitäten von Semantic MediaWiki bezüglich der hierfür [https://www.semantic-mediawiki.org/wiki/Help:Logging aktivierten Ereignisarten] protokolliert werden.",
+ "logentry-smw-maintenance": "Von Semantic MediaWiki durchgeführte wartungsbezogene Ereignisse",
+ "smw-datavalue-import-unknown-namespace": "Der für den Import vorgesehene Namensraum „$1“ ist unbekannt. Es muss sichergestellt werden, dass die Einzelheiten zum OWL-Import auf der Seite [[MediaWiki:Smw import $1]] angegeben sind.",
+ "smw-datavalue-import-missing-namespace-uri": "Es wurde keine URI für den Namensraum „$1“ auf der Seite mit den Definitionen für den [[MediaWiki:Smw import $1|$1-Import]] gefunden.",
+ "smw-datavalue-import-missing-type": "Es wurde keine Definition des Datentyps für „$1“ im [[MediaWiki:Smw import $2|$2-Import]] gefunden.",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1-Import]]",
+ "smw-datavalue-import-invalid-value": "„$1“ ist kein gültiges Format. Es muss dem Schema „Namensraum“:„Kennung“ (z.&nbsp;B. „foaf:name“) entsprechen.",
+ "smw-datavalue-import-invalid-format": "„$1“ ist keine gültige Zeichenfolge für dieses Schema. Sie muss in vier Teile gegliedert sein.",
+ "smw-property-predefined-impo": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Zusammenhang mit einem [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary importierten Vokabular] beschreibt. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-type": "„$1“ ist ein softwareseitig fest definiertes Attribut, mit dem der [[Special:Types|Datentyp]] eines Attributs festgelegt wird. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-sobj": "„$1“ ist ein softwareseitig fest definiertes Attribut und stellt einen [https://www.semantic-mediawiki.org/wiki/Help:Container Datenverbund] dar. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-sobj": "Ein Subobjekt erlaubt die Speicherung mehrerer Attribut-Datenwert-Zuweisungen (Annotationen), ähnlich dem Vorgehen auf einer regulären Wikiseite. Die Speicherung erfolgt dabei in einem anderen Objektraum als dem der Wikiseite selbst. Dieser Objektraum ist indes mit der entsprechenden Wikiseite, auf der sich das Subobjekt befindet, verknüpft.",
+ "smw-property-predefined-errp": "„$1“ ist ein softwareseitig fest definiertes Attribut, das fehlerhafte Attributwerte speichert. Fehlerhafte Attributwerte entsprechen nicht dem [https://semantic-mediawiki.org/wiki/Help:Datentyp Datentyp] des Attributs. Das Attribut wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-errp": "In den meisten Fällen wird dieses Problem aufgrund nicht übereinstimmender Datentypangaben oder durch Einschränkungen aufgrund ausschließlich [[Property:Allows value|zulässiger Werte]] verursacht.",
+ "smw-property-predefined-pval": "„$1“ ist ein softwareseitig fest definiertes Attribut, mit dem die für ein Attribut zulässigen Datenwerte festgelegt werden. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-pvali": "„$1“ ist ein softwareseitig fest definiertes Attribut, mit dem ein Verweis auf eine Liste mit für ein Attribut zulässigen Datenwerten festgelegt wird. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-datavalue-property-restricted-annotation-use": "Das Attribut „$1“ hat einen eingeschränkten Anwendungsbereich und kann nicht als Attribut zum Annotieren von Daten verwendet werden.",
+ "smw-datavalue-property-restricted-declarative-use": "Das Attribut „$1“ dient dem Festlegen von Eigenschaften (Definition derselben) und kann nur auf der Seite eines Attributs oder einer Kategorie verwendet werden.",
+ "smw-datavalue-property-create-restriction": "Das Attribut „$1“ ist nicht vorhanden und kann nur von Benutzern der hierfür vorgesehenen [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups Benutzergruppe] oder mit der hierfür benötigten Berechtigung („$2“) erstellt werden, während der [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode Berechtigungsmodus] aktiviert ist. Erst wenn dieses unbestätigte Attribut erstellt wurde, kann es für Annotationen genutzt werden.",
+ "smw-datavalue-property-invalid-character": "Der Name des Attributs „$1“ enthält das ungültige Zeichen „$2“, das nicht hierfür verwendet werden kann.",
+ "smw-datavalue-property-invalid-chain": "Die Verwendung von „$1“ als Attributkette ist während des Hinzufügens von Annotationen nicht möglich.",
+ "smw-datavalue-restricted-use": "Der Datenwert „$1“ ist nicht uneingeschränkt nutzbar und kann daher nicht gespeichert werden.",
+ "smw-datavalue-invalid-number": "„$1“ ist keine gültige Zahl.",
+ "smw-query-condition-circular": "In Vorlage „$1“ wurde ein möglicher Zirkelbezug festgestellt.",
+ "smw-query-condition-empty": "Die Abfrage enthält hat eine leere Bedingung.",
+ "smw-types-list": "Liste der Datentypen",
+ "smw-types-default": "„$1“ ist ein softwareseitig fest definierter Datentyp.",
+ "smw-types-help": "Weitere Informationen sowie Beispiele sind auf der zugehörigen [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 Hilfeseite] vorhanden.",
+ "smw-type-anu": "„$1“ ist eine Variante des Datentyps [[Special:Types/URL|„URL“]], der zumeist für Exportdeklarationen zu ''owl:AnnotationProperty'' genutzt wird.",
+ "smw-type-boo": "„$1“ ist ein Datentyp, der für Wahrheitswerte („wahr“ oder „falsch“) genutzt wird.",
+ "smw-type-cod": "„$1“ ist eine Variante des Datentyps [[Special:Types/Text|„Text“]], der für technische Texte beliebiger Länge wie bspw. Softwarecode genutzt wird.",
+ "smw-type-geo": "„$1“ ist ein Datentyp, der zum Speichern der Lage eines Punktes in einer Ebene oder in einem Raum genutzt wird. Er wird von der Softwareerweiterung [https://www.semantic-mediawiki.org/wiki/Extension:Maps „Maps“] bereitgestellt.",
+ "smw-type-tel": "„$1“ ist ein Datentyp, der für die Ziffernfolge zur Anwahl eines Teilnehmers (Rufnummer) gemäß RFC 3966 genutzt wird.",
+ "smw-type-txt": "„$1“ ist ein Datentyp, der für Zeichenketten beliebiger Länge genutzt wird.",
+ "smw-type-dat": "„$1“ ist ein Datentyp, der für genau bestimmte Momente in einem zeitlichen Bezugssystem (Zeitskala) genutzt wird.",
+ "smw-type-ema": "„$1“ ist ein besonderer Datentyp für E-Mail-Adressen.",
+ "smw-type-tem": "„$1“ ist ein besonderer numerischer Datentyp für Temperaturen.",
+ "smw-type-qty": "„$1“ ist ein besonderer numerischer Datentyp für Mengenangaben mit Maßeinheiten.",
+ "smw-type-rec": "„$1“ ist ein besonderer Datentyp für Attributsequenzen in einer festen Reihenfolge.",
+ "smw-type-extra-tem": "Das Konvertierungsschema unterstützt Temperatureinheiten wie Kelvin, Celsius, Fahrenheit und Rankine.",
+ "smw-type-tab-properties": "Attribute",
+ "smw-type-tab-types": "Datentypen",
+ "smw-type-tab-errors": "Fehler",
+ "smw-type-primitive": "Einfach",
+ "smw-type-contextual": "Kontextbezogen",
+ "smw-type-compound": "Verbundbezogen",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Diese Seite stellt eine einfache Suchoberfläche zum Ermitteln aller Attribute auf einer bestimmten Seite bereit. Andere verfügbare Suchoberflächen sind die [[Special:SearchByProperty|Attributsuche]] sowie der [[Special:Ask|Abfragengenerator]].",
+ "smw-property-predefined-errc": "„$1“ ist ein softwareseitig fest definiertes Attribut, das Fehler im Zusammenhang mit der Verarbeitung von Eingaben sowie bei fehlerhaften Attribut-Datenwert-Zuweisungen (Annotationen) speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-errc": "Fehler werden in einem [https://www.semantic-mediawiki.org/wiki/Help:Container Container] gesammelt, der eine Referenz zu dem Attribut beinhalten kann, das die Diskrepanz verursacht hat.",
+ "smw-property-predefined-errt": "„$1“ ist ein softwareseitig fest definiertes Attribut, das Beschreibungen zu Fehlern enthält, die in Verbindung mit fehlerhaften Attribut-Datenwert-Zuweisungen (Annotationen) oder bei deren Verarbeitung aufgetreten sind. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-subobject-parser-invalid-naming-scheme": "Ein benutzerdefiniertes Unterobjekt verwendet ein ungültiges Namensschema. Die Verwendung eines Punkts innerhalb der ersten fünf Zeichen ($1) ist ausschließlich Softwareerweiterungen vorbehalten. Es kann manuell ein [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier Bezeichner] festgelegt werden.",
+ "smw-datavalue-record-invalid-property-declaration": "Die Definition des Datensatzes enthält das Attribut „$1“, das selbst wiederum dem Datentyp „Verbund“ zugeordnet wurde. Dies ist nicht zulässig.",
+ "smw-property-predefined-mdat": "„$1“ ist ein softwareseitig fest definiertes Attribut, das das Datum der letzten Änderung einer Seite speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-cdat": "„$1“ ist ein softwareseitig fest definiertes Attribut, das das Datum der ersten Version einer Seite speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-newp": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Status speichert, ob eine Seite eine neue Seite ist. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-ledt": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Benutzerseite des Benutzers speichert, der die letzte Version einer Seite erstellt hat. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-mime": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den MIME-Typ einer hochgeladenen Datei speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-media": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Medientyp einer hochgeladenen Datei speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-askfo": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Namen des in einer Abfrage verwendeten Ergebnisformats speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-askst": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Bedingungen der Abfrage als Zeichenfolge speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-askdu": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Dauer in Sekunden speichert, die die Abfrage zur Ausführung benötigt hat. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-asksc": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die alternative Quelle einer Abfrage enthält, bspw. die einer wikifremden Fernabfrage. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-askco": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Query_status_code Statuscode von Abragen] speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-askco": "Die gespeicherte Zahl oder gespeicherten Zahlen stehen für den softwareintern genutzten Code des Status, in dem sich die jeweilige Abfrage befindet. Weitere Informationen sind auf der entsprechenden [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler Hilfeseite] vorhanden.",
+ "smw-property-predefined-prec": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die [https://www.semantic-mediawiki.org/wiki/Help:Display_precision Anzeigegenauigkeit] für numerische Datentypen dezimal speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-attch-link": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Links zu den auf einer Seite eingebetteten Dateien speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-types-extra-geo-not-available": "Die Softwareerweiterung [https://www.semantic-mediawiki.org/wiki/Extension:Maps „Maps“] ist nicht installiert. Daher ist das Attribut „$1“ in seiner Nutzbarkeit eingeschränkt.",
+ "smw-datavalue-monolingual-dataitem-missing": "Ein erwartetes Element zum Erstellen eines einsprachigen zusammengesetzten Wertes fehlt.",
+ "smw-datavalue-languagecode-missing": "Zur Annotation „$1“ konnte kein Sprachcode festgestellt werden (z.&nbsp;B. „xyz@de“).",
+ "smw-datavalue-languagecode-invalid": "„$1“ ist kein zulässiger Sprachcode.",
+ "smw-property-predefined-lcode": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den einem Datenwert zugeordneten „BCP47“-formatierten Sprachcode speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-type-mlt-rec": "„$1“ ist der Datentyp für einen [https://www.semantic-mediawiki.org/wiki/Help:Container Datenverbund], der einen Text mit dem [[Property:Language code|Sprachcode]] verknüpft und so dessen Sprache festlegt.",
+ "smw-types-extra-mlt-lcode": "Der Datentyp erfordert {{PLURAL:$2|einen|keinen}} Sprachcode (bspw. wird eine Attribut-Datenwert-Zuweisung (Annotation) ohne einen Sprachcode {{PLURAL:$2|nicht akzeptiert|akzeptiert}}).",
+ "smw-property-predefined-text": "„$1“ ist ein softwareseitig fest definiertes Attribut, das einen Text mit einer beliebigen Länge speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-pdesc": "„$1“ ist ein softwareseitig fest definiertes Attribut, das es erlaubt, ein Attribut im Kontext einer Sprache zu beschreiben. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-list": "„$1“ ist ein softwareseitig fest definiertes Attribut zur Definition einer Attributliste in einem [[Special:Types/Record|Attributverbund]]. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-limitreport-intext-parsertime": "[SMW] Zeitverbrauch des Parsers für die direkten Annotationen",
+ "smw-limitreport-intext-postproctime": "[SMW] Datennachverarbeitungzeit",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Sekunde|Sekunden}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|Sekunde|Sekunden}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Dauer der Datenbankaktualisierung (beim Leeren des Seitencaches)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Sekunde|Sekunden}}",
+ "smw_allows_pattern": "Auf dieser Seite werden Referenzobjekte mit entsprechenden auf [https://de.wikipedia.org/wiki/Regulärer_Ausdruck regulären Ausdrücken] gestützten Musterreferenzen gespeichert (<code>Referenzobjekt|Musterreferenz</code>) und durch das Spezialattribut „[[Property:Allows pattern|Erlaubt Muster]]“ verfügbar gemacht. Um diese Seite bearbeiten zu können, ist das Benutzerrecht <code>smw-patternedit</code> erforderlich.",
+ "smw-datavalue-allows-pattern-mismatch": "„$1“ wurde vom regulären Ausdruck „$2“ als ungültig klassifiziert.",
+ "smw-datavalue-allows-pattern-reference-unknown": "Die Musterreferenz „$1“ entspricht keinem Eintrag in [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "Die der Referenz „$1“ zugehörige Seite [[MediaWiki:Smw allows list $1]] für das Speichern von Datenwerten ist nicht vorhanden.",
+ "smw-datavalue-allows-value-list-missing-marker": "In der Liste „$1“ fehlen Einträge mit der Markierung „*“.",
+ "smw-datavalue-feature-not-supported": "Die Funktion „$1“ ist auf diesem Wiki deaktiviert oder wird nicht unterstützt.",
+ "smw-property-predefined-pvap": "„$1“ ist ein softwareseitig fest definiertes Attribut, das eine [[MediaWiki:Smw allows pattern|Musterreferenz]] speichert anhand derer Attributwertzuweisungen mit Hilfe [https://de.wikipedia.org/wiki/Regulärer_Ausdruck regulärer Ausdrücke] überprüft werden. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-dtitle": "„$1“ ist ein softwareseitig fest definiertes Attribut, das einen eindeutigen Anzeigetitel zu einem Objekt speichert und ihm zuweist. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-pvuc": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Information speichert, ob eine bestimmte Attribut-Datenwert-Zuweisung (Annotation) im Wiki einzigartig sein muss oder gar nur einmalig erfolgen darf. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-pvuc": "Eindeutigkeit bedeutet, dass zwei Datenwerte für ein bestimmtes Attribut bei durchgeführten Attribut-Datenwert-Zuweisungen (Annotationen) nicht identisch sein dürfen. Jeder Verstoß dieser Anforderung wird als Fehler eingestuft.",
+ "smw-datavalue-uniqueness-constraint-error": "Das Attribut „$1“ erlaubt nur die einmalige Zuordnung des Datenwerts ''$2''. Dieser Datenwert ist bereits auf der Seite „$3“ vorhanden.",
+ "smw-datavalue-uniqueness-constraint-isknown": "Das Attribut „$1“ erlaubt nur die einmalige Zuordnung eines bestimmten Datenwerts. Der entsprechende Datenwert ist bereits auf der Seite „$2“ vorhanden. Der Datenwert „$3“ verstößt somit gegen das Gebot der einmaligen Zuordnung eines bestimmten Datenwerts.",
+ "smw-property-predefined-boo": "„$1“ ist ein [[Special:Types/Boolean|Datentyp]] für Wahrheitswerte („wahr“ oder „falsch“). Er wird Attributen mit Hilfe eines von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] bereitgestellten, softwareseitig fest definierten Attributs (Spezialattribut), zugeordnet.",
+ "smw-property-predefined-num": "„$1“ ist ein [[Special:Types/Number|Datentyp]] für Zahlenwerte. Er wird Attributen mit Hilfe eines von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] bereitgestellten, softwareseitig fest definierten Attributs (Spezialattribut), zugeordnet.",
+ "smw-property-predefined-dat": "„$1“ ist ein [[Special:Types/Date|Datentyp]] für Datumswerte. Er wird Attributen mit Hilfe eines von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] bereitgestellten, softwareseitig fest definierten Attributs (Spezialattribut), zugeordnet.",
+ "smw-property-predefined-uri": "„$1“ ist ein [[Special:Types/URL|Datentyp]] für URI-/URL-Werte. Er wird Attributen mit Hilfe eines von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] bereitgestellten, softwareseitig fest definierten Attributs (Spezialattribut), zugeordnet.",
+ "smw-property-predefined-qty": "„$1“ ist ein [[Special:Types/Quantity|Datentyp]] für Datenwerte mit Maßangaben. Er wird Attributen mit Hilfe eines von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] bereitgestellten, softwareseitig fest definierten Attributs (Spezialattribut), zugeordnet.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "Der Datenwert „$1“ enthält eine Angabe zur Zeitverschiebung und einen Zeitzonenidentifikator, die nicht unterstützt werden.",
+ "smw-datavalue-time-invalid-values": "Der Datenwert „$1“ enthält nicht interpretierbare Informationen in Form von „$2“.",
+ "smw-datavalue-time-invalid-date-components-common": "Der Datenwert „$1“ enthält einige nicht interpretierbare Informationen.",
+ "smw-datavalue-time-invalid-date-components-dash": "Der Datenwert „$1“ enthält einen Gedankenstrich oder andere für die Interpretation der Datumsangabe ungültige Zeichen.",
+ "smw-datavalue-time-invalid-date-components-empty": "Der Datenwert „$1“ enthält nicht alle Bestandteile.",
+ "smw-datavalue-time-invalid-date-components-three": "Der Datenwert „$1“ besteht aus mehr als den drei für die Interpretation der Datumsangabe erforderlichen Bestandteilen.",
+ "smw-datavalue-time-invalid-date-components-sequence": "Der Datenwert „$1“ enthält eine für die Interpretation einer Datumsangabe ungültige Sequenz.",
+ "smw-datavalue-time-invalid-ampm": "Der Datenwert „$1“ enthält „$2“ als Stundenangabe, die für die 2-mal-12-Stunden-Zählung ungültig ist.",
+ "smw-datavalue-time-invalid-jd": "Der Datenwert „$1“ entspricht keiner gültigen Zahl für ein julianisches Datum. „$2“ wurde ausgegeben.",
+ "smw-datavalue-time-invalid-prehistoric": "Der Datenwert „$1“ entspricht keiner gültigen Angabe für ein prähistorisches Datum. Es könnte bspw. mehr als nur eine Jahreszahl oder ein unzutreffendes Kalendermodell angegeben worden sein.",
+ "smw-datavalue-time-invalid": "Der Datenwert „$1“ entspricht keiner gültigen Angabe für ein Datum oder einen Zeitpunkt. „$2“ wurde ausgegeben.",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Der für das Erstellen der URI notwendige Datenwert für den Platzhalter „$1“ ist nicht vorhanden.",
+ "smw-datavalue-external-formatter-invalid-uri": "Der Datenwert „$1“ bescheibt eine ungültige URL.",
+ "smw-datavalue-external-identifier-formatter-missing": "Dem Attribut wurde keine [[Property:External formatter uri|Anweisung zur Generierung einer URI]] zugewiesen.",
+ "smw-datavalue-keyword-maximum-length": "Das Stichwort hat die maximale Länge von {{PLURAL:$1|einem|$1}} Zeichen überschritten.",
+ "smw-property-predefined-eid": "„$1“ ist ein [[Special:Types/External identifier|Datentyp]] für Anweisungen zum Generieren einer URI sowie ein softwareseitig fest definiertes Attribut. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-peid": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die externe Kennung zum Generieren einer URI speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-pefu": "„$1“ ist ein softwareseitig fest definiertes Attribut, das Anweisungen zum Generieren einer URI zusammen mit einem Platzhalter speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-pefu": "Die URI muss einen Platzhalter enthalten, damit [[Special:Types/External identifier|externe Identifikatoren]] korrekt zur Erstellung einer gültigen Referenzierung positioniert werden können.",
+ "smw-type-eid": "„$1“ ist eine Variante des Datentyps [[Special:Types/Text|„Text“]]. Die zugewiesenen Attribute müssen [[Property:External formatter uri|Anweisungen zum Generieren einer URI]] enthalten.",
+ "smw-property-predefined-keyw": "„$1“ ist ein [[Special:Types/Keyword|Datentyp]], der einen in seiner maximalen Zeichenlänge beschränkten Text normalisiert. Es ist zudem ein softwareseitig fest definiertes Attribut. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-type-keyw": "„$1“ ist eine Variante des Datentyps [[Special:Types/Text|„Text“]], der einen in seiner maximalen Zeichenlänge beschränkten Text normalisiert.",
+ "smw-datavalue-stripmarker-parse-error": "Der angegebene Wert „$1“ enthält [https://en.wikipedia.org/wiki/Help:Strip_markers „Strip markers“ (Markierungen für vom Softwareparser genutzte Platzhalter)] und kann deshalb nicht ausreichend verarbeitet werden.",
+ "smw-datavalue-parse-error": "Der angegebene Wert „$1“ wurde nicht verstanden.",
+ "smw-datavalue-propertylist-invalid-property-key": "Die Attributfolge „$1“ enthielt die ungültige Attributkennung „$2“.",
+ "smw-datavalue-type-invalid-typeuri": "Mit dem Typ „$1“ konnte keine gültige URI generiert werden.",
+ "smw-datavalue-wikipage-missing-fragment-context": "„$1“ kann nicht als Wert für eine Seite ohne eine im Kontext vorhandene Seite verwendet werden.",
+ "smw-datavalue-wikipage-invalid-title": "Der angegebene Wert „$1“ enthält für den Datentyp Seite ungültige Zeichen oder ist unvollständig. Er kann deshalb während einer Abfrage oder bei einer Annotation unerwartete Ergebnisse verursachen.",
+ "smw-datavalue-wikipage-property-invalid-title": "Der für das Attribut „$1“ des Datentyps Seite angegebene Wert „$2“ enthält ungültige Zeichen oder ist unvollständig. Er kann deshalb während einer Abfrage oder bei einer Annotation unerwartete Ergebnisse verursachen.",
+ "smw-datavalue-wikipage-empty": "Es ist kein Wert für den Titel einer Seite vorhanden (z.&nbsp;B. <code>[[SomeProperty::]], [[]]</code>) und kann somit nicht als Name oder Teil einer Abfragebedingung verwendet werden.",
+ "smw-type-ref-rec": "„$1“ ist der Datentyp für einen [https://www.semantic-mediawiki.org/wiki/Help:Container Datenverbund], mit dem zusätzliche Informationen, bspw. Quellenangaben, zu einer Attribut-Datenwert-Zuweisung (Annotation) gespeichert werden können.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "Der Datentyp [[Special:Types/Reference|Referenzierung]] muss aus einer Folge von Attributen bestehen, die das Spezialattribut [https://www.semantic-mediawiki.org/wiki/Help:Spezialattribut_Hat_Komponenten „Hat Komponenten“] verwenden.",
+ "smw-parser-invalid-json-format": "Der JSON-Parser hat den Fehler „$1“ ausgegeben.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "„$1“ kann nicht als bevorzugte Bezeichnung verwendet werden, da der Sprache „$2“ bereits die Bezeichnung „$3“ zugewiesen wurde.",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Link in die Zwischenablage kopieren",
+ "smw-property-userdefined-fixedtable": "„$1“ wurde als [https://www.semantic-mediawiki.org/wiki/Fixed_properties fest definiertes Attribut] erstellt und konfiguriert. Jede Änderung des zugewiesenen [https://www.semantic-mediawiki.org/wiki/Type_declaration Datentyps] erfordert zwingend entweder die Ausführung des Wartungsskripts \"setupStore.php\" oder die Durchführung der Spezialaufgabe [[Special:SemanticMediaWiki|„Daten neu erstellen“]].",
+ "smw-data-lookup": "Rufe Daten ab ...",
+ "smw-data-lookup-with-wait": "Es kann einen Moment dauern, bis die Abfrage ausgeführt wurde.",
+ "smw-no-data-available": "Es sind keine Daten verfügbar.",
+ "smw-property-req-violation-missing-fields": "Die Annotation des Attribut „$1“ ist aufgrund des zugewiesenen Datentyps „$2“ unvollständig. Das Spezialattribut <code>Hat Komponenten</code> wurde nicht angegeben.",
+ "smw-property-req-violation-missing-formatter-uri": "Die Annotation des Attributs „$1“ ist aufgrund des zugewiesenen Datentyps unvollständig. Das Spezialattribut „Formatierungsanweisung zur externen URI“ mit einem entsprechenden Wert fehlt.",
+ "smw-property-req-violation-predefined-type": "„$1“ ist ein softwareseitig fest definiertes Attribut. Ihm wurde der Datentyp „$2“ zugewiesen, der nicht mit dem Standardtyp dieses Attributs vereinbar ist.",
+ "smw-property-req-violation-import-type": "Es wurde ein Datentyp zugeordnet, der mit dem vom importierten Vokabular „$1“ für dieses Attribut definierten Datentyp nicht kompatibel ist. Es ist allgemein nicht erforderlich, einen Datentyp festzulegen, da diese Information aus der importierten Definition des Attributs abgerufen wird.",
+ "smw-property-req-violation-change-propagation-locked-error": "Das Attribut „$1“ wurde solcherart verändert, dass die zugeordneten Datenobjekte mit einem [https://www.semantic-mediawiki.org/wiki/Change_propagation Datenänderungsvorgang] neu verarbeitet werden müssen. Daher ist die Seite dieses Attributs bis zu dem Zeitpunkt für Bearbeitungen gesperrt, an dem dieser Vorgang abgeschlossen wurde. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert. Da der Datenänderungsvorgang abhängig vom Umfang sowie der Abarbeitungsfrequenz der [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist, kann es einen Moment dauern, bis die Seite freigegeben wird und wieder bearbeitet werden kann.",
+ "smw-property-req-violation-change-propagation-locked-warning": "Das Attribut „$1“ wurde solcherart verändert, dass die zugeordneten Datenobjekte mit einem [https://www.semantic-mediawiki.org/wiki/Change_propagation Datenänderungsvorgang] neu verarbeitet werden müssen. Daher sollte die Seite dieses Attributs nicht bearbeitet werden, bis dieser Vorgang abgeschlossen wurde. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert. Da der Datenänderungsvorgang abhängig vom Umfang sowie der Abarbeitungsfrequenz der [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist, kann es einen Moment dauern, bis die Seite wieder bearbeitet werden kann.",
+ "smw-property-req-violation-change-propagation-pending": "Es {{PLURAL:$1|muss|müssen}} noch ungefähr [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|ein Datenänderungsauftrag|$1 Datenänderungsaufträge}}] durchgeführt werden. Daher sollte die Seite dieses Attributs so lange nicht bearbeitet werden, bis kein Datenänderungsauftrag mehr vorhanden ist. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert.",
+ "smw-property-req-violation-missing-maps-extension": "Semantic MediaWiki konnte nicht erkennen, ob die Softwareerweiterung [https://www.semantic-mediawiki.org/wiki/Extension:Maps „Maps“] installiert ist. Da sie eine Voraussetzung für das Funktionieren von Attributen mit diesem Datentyp ist, ist dieses Attribut in seiner Nutzbarkeit eingeschränkt.",
+ "smw-property-req-violation-type": "Das Attribut enthält konkurrierende Datentypangaben, die zu ungültigen Annotationen von Datenwerten führen könnten. Es muss sichergestellt werden, dass dem Attribut nur ein zutreffender Datentyp zugewiesen wird.",
+ "smw-change-propagation-protection": "Diese Seite ist gesperrt, um unbeabsichtigte Änderungen an Daten zu verhindern, während ein [https://www.semantic-mediawiki.org/wiki/Change_propagation Datenänderungsvorgang] ausgeführt wird. Da der Datenänderungsvorgang abhängig vom Umfang sowie der Abarbeitungsfrequenz der [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist, kann es einen Moment dauern, bis die Seite freigegeben wird und wieder bearbeitet werden kann.",
+ "smw-category-change-propagation-locked-error": "Die Kategorie „$1“ wurde solcherart verändert, dass die zugeordneten Datenobjekte mit einem [https://www.semantic-mediawiki.org/wiki/Change_propagation Datenänderungsvorgang] neu verarbeitet werden müssen. Daher ist die Seite der Kategorie bis zu dem Zeitpunkt für Bearbeitungen gesperrt, an dem dieser Vorgang abgeschlossen wurde. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert. Da der Datenänderungsvorgang abhängig vom Umfang sowie der Abarbeitungsfrequenz der [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist, kann es einen Moment dauern, bis die Seite freigegeben wird und wieder bearbeitet werden kann.",
+ "smw-category-change-propagation-locked-warning": "Die Kategorie „$1“ wurde solcherart verändert, dass die zugeordneten Datenobjekte mit einem [https://www.semantic-mediawiki.org/wiki/Change_propagation Datenänderungsvorgang] neu verarbeitet werden müssen. Daher sollte die Seite dieser Kategorie nicht bearbeitet werden, bis dieser Vorgang abgeschlossen wurde. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert. Da der Datenänderungsvorgang abhängig vom Umfang sowie der Abarbeitungsfrequenz der [https://www.mediawiki.org/wiki/Manual:Job_queue Auftragswarteschlange] ist, kann es einen Moment dauern, bis die Seite wieder bearbeitet werden kann.",
+ "smw-category-change-propagation-pending": "Es {{PLURAL:$1|muss|müssen}} noch ungefähr [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|ein Datenänderungsauftrag|$1 Datenänderungsaufträge}}] durchgeführt werden. Daher sollte die Seite dieser Kategorie so lange nicht bearbeitet werden, bis kein Datenänderungsauftrag mehr vorhanden ist. Hierdurch werden ggf. mögliche Funktionsstörungen oder widersprüchliche Datenanzeigen verhindert.",
+ "smw-category-invalid-value-assignment": "„$1“ wurde nicht als gültige Kategorie oder als Annotation eines gültigen Wertes erkannt.",
+ "protect-level-smw-pageedit": "Nur Benutzern mit der Berechtigung zum Bearbeiten von Seiten erlauben (Semantic MediaWiki)",
+ "smw-create-protection": "Das Erstellen des Attributs „$1“ ist auf Benutzer der hierfür vorgesehenen [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups Benutzergruppe] oder mit der hierfür benötigten Berechtigung „$2“ beschränkt, während der [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode Berechtigungsmodus] aktiviert ist.",
+ "smw-create-protection-exists": "Das Ändern des Attributs „$1“ ist auf Benutzer der hierfür vorgesehenen [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups Benutzergruppe] oder mit der hierfür benötigten Berechtigung „$2“ beschränkt, während der [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode Berechtigungsmodus] aktiviert ist.",
+ "smw-edit-protection": "Diese Seite ist [[Property:Is edit protected|bearbeitungsgeschützt]], um unbeabsichtigte Veränderungen an Inhalten und Daten zu verhindern. Sie kann nur von Benutzern der hierfür vorgesehenen [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups Benutzergruppe] oder mit der hierfür benötigten Berechtigung („$1“) bearbeitet werden.",
+ "smw-edit-protection-disabled": "Der Bearbeitungsschutz mit Hilfe einer Annotation wurde deaktiviert. Daher kann das Spezialattribut „$1“ nicht verwendet werden, um diese Seite vor unerwünschten Bearbeitungen zu schützen.",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki hat den Schutzstatus der Seite übereinstimmend mit dem Wert des Attributs „Ist bearbeitungsgeschützt“ aktualisiert.",
+ "smw-edit-protection-enabled": "Diese Seite wurde mit Semantic MediaWiki vor Bearbeitungen geschützt.",
+ "smw-patternedit-protection": "Diese Seite ist bearbeitungsgeschützt, um unbeabsichtigte Veränderungen an Inhalten zu verhindern. Sie kann deshalb nur von Benutzern mit der hierfür notwendigen Berechtigung „smw-patternedit“ bearbeitet werden.",
+ "smw-property-predefined-edip": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Status des Bearbeitungsschutzes einer Seite speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-edip": "Jeder Benutzer darf dieses Attribut zu einer Seite hinzufügen und so den Bearbeitungsschutz festlegen. Indes dürfen hernach nur Benutzer mit einer entsprechenden Berechtigung die auf diese Weise geschützte Seite bearbeiten und den Bearbeitungsschutz ggf. aufheben.",
+ "smw-query-reference-link-label": "Abfragereferenz",
+ "smw-format-datatable-emptytable": "Es sind keine Ergebnisse vorhanden.",
+ "smw-format-datatable-info": "Es werden die Ergebnisse _START_ bis _END_ von insgesamt _TOTAL_ Ergebnissen angezeigt.",
+ "smw-format-datatable-infoempty": "Es werden keine Ergebnisse angezeigt.",
+ "smw-format-datatable-infofiltered": "(aus insgesamt _MAX_ Ergebnissen gefiltert)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Zeige _MENU_ Einträge",
+ "smw-format-datatable-loadingrecords": "Lade …",
+ "smw-format-datatable-processing": "Verarbeite …",
+ "smw-format-datatable-search": "Suche:",
+ "smw-format-datatable-zerorecords": "Es wurden keine übereinstimmenden Ergebnisse gefunden.",
+ "smw-format-datatable-first": "Erste",
+ "smw-format-datatable-last": "Letzte",
+ "smw-format-datatable-next": "Nächste",
+ "smw-format-datatable-previous": "Vorherige",
+ "smw-format-datatable-sortascending": ": aktivieren, um die Spalte aufsteigend zu sortieren",
+ "smw-format-datatable-sortdescending": ": aktivieren, um die Spalte absteigend zu sortieren",
+ "smw-format-datatable-toolbar-export": "Exportieren",
+ "smw-format-list-other-fields-open": "&#32;(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "Die Kategorie „$1“ enthält ein Weiterleitungsziel, das sich nicht im Namensraum Kategorie befindet und daher ungültig ist.",
+ "smw-parser-function-expensive-execution-limit": "Die Parserfunktion hat die Grenze für aufwendige Ausführungen erreicht. Siehe hierzu die Seite zum [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit Konfigurationsparameter <code>$smwgQExpensiveExecutionLimit</code>].",
+ "smw-postproc-queryref": "Semantic MediaWiki wird aufgrund erforderlicher abfragebedingter Nachverarbeitungen eine Aktualisierung dieser Seite anstoßen.",
+ "apihelp-smwinfo-summary": "API-Modul zum Abrufen statistischer Daten und weiterer Metainformationen bezüglich der Nutzung von Semantic MediaWiki.",
+ "apihelp-ask-summary": "API-Modul zum Abfragen eines Wikis mit der Abfragesprache von Semantic MediaWiki.",
+ "apihelp-askargs-summary": "API-Modul zum Abfragen eines Wikis mit der Abfragesprache von Semantic MediaWiki als Liste von Bedingungen, Ausgabeanweisungen und Parametern.",
+ "apihelp-browsebyproperty-summary": "API-Modul zum Abrufen von Informationen zu einem Attribut oder einer Liste von Attributen.",
+ "apihelp-browsebysubject-summary": "API-Modul zum Abrufen von Informationen zu einem Objekt (Seite oder Unterobjekt).",
+ "apihelp-smwtask-summary": "API-Modul zum Ausführen Semantic MediaWiki betreffender Aufgaben.",
+ "apihelp-smwbrowse-summary": "API-Modul zur Unterstützung von Aktivitäten beim Browsen für unterschiedliche Objekttypen in Semantic MediaWiki.",
+ "apihelp-ask-parameter-api-version": "Ausgabeformat:\n;2:Rückwärtskompatibles Format, das {} für Ergebnislisten verwendet.\n;3:Experimentelles Format, das [] für Ergebnislisten verwendet.",
+ "smw-api-invalid-parameters": "Ungültige Parameter: „$1“",
+ "smw-parser-recursion-level-exceeded": "Die Obergrenze von $1 Rekursionen wurde während des Parsens überschritten. Es wird empfohlen, die Vorlagenstruktur zu validieren oder – falls notwendig, den Konfigurationsparameter „<code>$maxRecursionDepth</code>“ anzupassen.",
+ "smw-property-page-list-count": "Unterhalb {{PLURAL:$1|wird eine Seite|werden $1 Seiten}} angezeigt, auf denen für dieses Attribut ein Datenwert gespeichert wurde.",
+ "smw-property-page-list-search-count": "Es {{PLURAL:$1|wird eine Seite|werden $1 Seiten}} angezeigt, auf denen für dieses Attribut der Datenwert „$2“ gespeichert wurde.",
+ "smw-property-reserved-category": "Kategorie",
+ "smw-category": "Kategorie",
+ "smw-datavalue-uri-invalid-scheme": "„$1“ ist nicht als zulässiges URI-Schema gelistet.",
+ "smw-datavalue-uri-invalid-authority-path-component": "„$1“ enthält mit „$2“ eine ungültige Zuständigkeits- oder Pfadkomponente.",
+ "smw-browse-property-group-title": "Attributgruppe",
+ "smw-browse-property-group-label": "Attributgruppenbezeichnung",
+ "smw-browse-property-group-description": "Attributgruppenbeschreibung",
+ "smw-property-predefined-ppgr": "„$1“ ist ein softwareseitig fest definiertes Attribut, das Seiten (hauptsächlich Kategorien) bestimmt, die zum Gruppieren von Attributen verwendet werden. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-filter": "Filter",
+ "smw-section-expand": "Abschnitt ausklappen",
+ "smw-section-collapse": "Abschnitt einklappen",
+ "smw-ask-format-help-link": "Ausgabeformat [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Hilfe",
+ "smw-cheat-sheet": "Hilfestellung",
+ "smw-personal-jobqueue-watchlist": "Beobachtungsliste (Auftragswarteschlange)",
+ "smw-property-predefined-label-skey": "Sortierschlüssel",
+ "smw-processing": "Verarbeite Daten …",
+ "smw-redirect-target-unresolvable": "Das Weiterleitungsziel ist nicht auflösbar. Grund: „$1“",
+ "smw-types-title": "Typ: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "Das Ändern des Inhaltsmodells der [https://www.semantic-mediawiki.org/wiki/Help:Schema Seite zu einem Schema] ist nicht möglich.",
+ "smw-schema-namespace-edit-protection": "Diese Seite ist bearbeitungsgeschützt, um unbeabsichtigte Veränderungen an Inhalten zu verhindern. Sie kann deshalb nur von Benutzern mit der hierfür notwendigen Berechtigung „smw-schemaedit“ bearbeitet werden.",
+ "smw-schema-error": "Validierungsfehler",
+ "smw-schema-error-schema": "Die Angabe der Spezifikation '''$1''' und dessen Validierung für das aktuelle Schema hat die folgenden Inkompatibilitäten oder Nichteinhaltungen ergeben:",
+ "smw-schema-error-violation": "Nichteinhaltung („$1“, „$2“)",
+ "smw-schema-error-type-missing": "Dem Inhalt fehlt die Angabe eines Typs, um im [https://www.semantic-mediawiki.org/wiki/Help:Schema Namensraum für Schemata] erkannt und verwendet werden zu können.",
+ "smw-schema-error-type-unknown": "Der Typ „$1“ ist unbekannt und kann daher nicht im [https://www.semantic-mediawiki.org/wiki/Help:Schema Namensraum für Schemata] verwendet werden.",
+ "smw-schema-title": "Schema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Typ",
+ "smw-schema-description-link-format-schema": "Dieser Schematyp definiert, entsprechend der Festlegung mit dem Attribut [[Property:Formatter schema|Formatierungsregel]], kontextsensitive Links.",
+ "smw-schema-description-search-form-schema": "Dieser Schematyp definiert Eingabeformulare und Inhaltsausprägungen für das [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch erweiterte Suchprofil]. Es enthält Festlegungen zur Erstellung von Eingabefeldern, zu Standardnamensräumen oder zu Suchausdrücken für Suchanfragen.",
+ "smw-schema-tag": "{{PLURAL:$1|Schemakennzeichen}}",
+ "smw-property-predefined-schema-desc": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Schemabeschreibung speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-schema-def": "„$1“ ist ein softwareseitig fest definiertes Attribut, das die Definition des Schemas speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-schema-tag": "„$1“ ist ein softwareseitig fest definiertes Attribut, das das Kennzeichen des Schemas speichert. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-schema-tag": "Eine Bezeichnung, die Schemata ähnlicher Inhalte oder Ausprägungen kennzeichnet.",
+ "smw-property-predefined-schema-type": "„$1“ ist ein softwareseitig fest definiertes Attribut, das den Typ des Schemas speichert, gemäß dem eine Regel oder ein Regelgefüge interpretiert wird. Es wird von [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] zur Verfügung gestellt.",
+ "smw-property-predefined-long-schema-type": "Jeder [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type Schematyp] muss seine eigene Struktur zur Interpretation von Syntaxelementen einschließlich möglicher Einschränkungen darstellen und diese durch ein [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation Modell zur Validierung] beschreiben.",
+ "smw-ask-title-keyword-type": "Stichwortsuche",
+ "smw-ask-message-keyword-type": "Diese Suche entspricht der Abfragebedingung <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Es konnte keine Verbindung zur externen Datenquelle „$1“ hergestellt werden.",
+ "smw-remote-source-disabled": "Die externe Datenquelle „$1“ gestattet keine Fernabfrage.",
+ "smw-remote-source-unmatched-id": "Die externe Datenquelle „$1“ nutzt keine Version von Semantic MediaWiki, die eine Fernabfrage unterstützt.",
+ "smw-remote-request-note": "Das Ergebnis wird von der externen Datenquelle „$1“ abgefragt. Es ist wahrscheinlich, dass der erstellte Inhalt Informationen enthält, die nicht innerhalb des aktuellen Wikis verfügbar sind.",
+ "smw-remote-request-note-cached": "Das aus der externen Datenquelle „$1“ abgefragte Ergebnis ist '''gecacht'''. Es ist wahrscheinlich, dass der erstellte Inhalt Informationen enthält, die nicht innerhalb des aktuellen Wikis verfügbar sind.",
+ "smw-parameter-missing": "Der Parameter „$1“ fehlt.",
+ "smw-property-tab-usage": "Annotationen",
+ "smw-property-tab-redirects": "Synonyme",
+ "smw-property-tab-subproperties": "Unterattribute",
+ "smw-property-tab-errors": "Fehlerhafte Annotationen",
+ "smw-property-tab-specification": "… mehr",
+ "smw-concept-tab-list": "Liste",
+ "smw-concept-tab-errors": "Fehler",
+ "smw-ask-tab-result": "Ergebnis",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Fehleranalyse",
+ "smw-ask-tab-code": "Code",
+ "smw-install-incomplete-intro": "Die Installation oder Aktualisierung von <b>Semantic MediaWiki</b> wurde noch nicht vollständig abgeschlossen. Ein Administrator oder eine andere Person mit Administratorberechtigung muss daher die folgenden Aufgaben ausführen, um Dateninkonsistenzen zu vermeiden, bevor die Nutzer des Wikis bestehende Inhalte ändern oder weitere Inhalte erstellen:",
+ "smw-install-incomplete-populate-hash-field": "Die Befüllung des Datenbankfeldes <code>smw_hash</code> wurde während des Setups aufgrund des Umfangs der Datenbank abgebrochen. Das Wartungsskript [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php „populateHashField.php“] muss ausgeführt werden, um diese Aufgabe zu beenden.",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/din.json b/www/wiki/extensions/SemanticMediaWiki/i18n/din.json
new file mode 100644
index 00000000..856eae9f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/din.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dinkawiki"
+ ]
+ },
+ "browse": "Cäärë wiki"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/diq.json b/www/wiki/extensions/SemanticMediaWiki/i18n/diq.json
new file mode 100644
index 00000000..a27b5d08
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/diq.json
@@ -0,0 +1,41 @@
+{
+ "@metadata": {
+ "authors": [
+ "Erdemaslancan",
+ "Marmase",
+ "Mirzali"
+ ]
+ },
+ "smw_finallistconjunct": ", u",
+ "smw_printername_csv": "CSV Teberdayış",
+ "smw_printername_dsv": "DSV Teberdayış",
+ "smw_printername_json": "JSON teberdayış",
+ "smw_printername_list": "Liste",
+ "smw_printername_table": "Tablo",
+ "smw_printername_template": "Åžablon",
+ "smw_printername_rdf": "RDF teberdayış",
+ "smw_printername_category": "Kategoriye",
+ "smw-paramdesc-export": "Weçenegê teberdayışi",
+ "smw_true_words": "raÅŸtay,r,eya,e",
+ "smw_false_words": "zuray,z,nê,n",
+ "specialpages-group-smw_group": "Fahm kerdışê MediaWiki",
+ "exportrdf": "Pela ahûlnê RDF",
+ "smw_exportrdf_submit": "Teber de",
+ "properties": "Xısusiyey",
+ "concepts": "Konsepti",
+ "unusedproperties": "Xısusiyetê ke nêguriyenê",
+ "wantedproperties": "Xısusiyetê ke waziyenê",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|vıraştene|vıraştene}})",
+ "smw_purge": "Newe ke",
+ "types": "Babeti",
+
+ "ask": "Fahmyayış cı geyrayış",
+ "smw_ask_defaultformat": "hesabiyaye",
+ "smw-ask-delete": "[Bestere]",
+ "searchbyproperty": "Xısusiyeti heta cıgeyrê",
+ "smw_sbv_value": "Qıymet:",
+ "browse": "Çım berze ra wiki",
+ "smw_browselink": "Xısusiyetan bıvêne",
+ "smw_browse_go": "Åžo",
+ "smw-livepreview-loading": "Bar beno..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/dsb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/dsb.json
new file mode 100644
index 00000000..daffb43f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/dsb.json
@@ -0,0 +1,268 @@
+{
+ "@metadata": {
+ "authors": [
+ "Derbeth",
+ "Michawiki",
+ "ì•„ë¼",
+ "Matěj Suchánek"
+ ]
+ },
+ "smw-desc": "Twój wiki pśistupnjejšy cyniś - za mašiny ''a'' luźi\n([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentacija online])",
+ "smw_viewasrdf": "Kanal RDF",
+ "smw_finallistconjunct": "a",
+ "smw_isspecprop": "Toś ta kakosć jo specialna kakosć w toś tom wikiju.",
+ "smw_concept_description": "Wopisanje koncepta \"$1\"",
+ "smw_no_concept_namespace": "Koncepty daju se jano na bokach w mjenjowem rumje Concept: definěrowaś.",
+ "smw_multiple_concepts": "Kuždy konceptowy bok móžo jano jadnu konceptowu definiciju měś.",
+ "smw_concept_cache_miss": "Koncept \"$1\" njedajo se we wokugnuśu wužywaś, dokulaž wikijowa konfiguracija trjeba jón za pśeźěłowanje off-line.\nJolic problem se njezgubujo pó wěstem casu, pšos swójogo sedłowego administratora, aby wón toś ten koncept k dispoziciji stajił.",
+ "smw_noinvannot": "Gódnoty njedaju se nawopacnym kakosćam pśipokazaś.",
+ "version-semantic": "Semantiske rozšyrjenja",
+ "smw_baduri": "URI formy \"$1\" njejsu dowólone.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Wuslědki licyś",
+ "smw_printername_csv": "Ako CSV eksportěrowaś",
+ "smw_printername_dsv": "DSV-eksport",
+ "smw_printername_debug": "Wótpšašanje za zmólkami pśepytaś (za ekspertow)",
+ "smw_printername_embedded": "Bokowe wopśimjeśe zasajźiś",
+ "smw_printername_json": "Ako JSON eksportěrowaś",
+ "smw_printername_list": "Lisćina",
+ "smw_printername_ol": "Nalicenje",
+ "smw_printername_ul": "Nalistowanje",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Å yroka tabela",
+ "smw_printername_template": "Pśedłoga",
+ "smw_printername_rdf": "RDF-eksport",
+ "smw_printername_category": "Kategorija",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Maksimalna licba wuslědkow, kótarež maju se wrośiś",
+ "smw-paramdesc-offset": "Pozicija prědnego wuslědka",
+ "smw-paramdesc-headers": "Mjenja głowow resp. atributow zwobrazniś",
+ "smw-paramdesc-mainlabel": "Pomjenjenje, kótarež ma se głownemu bokoju daś",
+ "smw-paramdesc-link": "Gódnoty ako wótkaze pokazaś",
+ "smw-paramdesc-intro": "Tekst, kótaryž ma se pśed napšašowańskimi wuslědkami zwobrazniś, jolic take su",
+ "smw-paramdesc-outro": "Tekst, kótaryž ma se za napšašowańskimi wuslědkami zwobrazniś, jolic take su",
+ "smw-paramdesc-default": "Tekst, kótaryž ma se zwobrazniś, jolic napšašowańske wuslědki njejsu",
+ "smw-paramdesc-sep": "Źěleńske znamuško za gódnoty",
+ "smw-paramdesc-showsep": "Źěleńske znamuško górjejce w CSV-dataji pokazaś (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Město aby se wšykne gódnoty zwobraznili, jich wustupowanja licyś a te pokazaś.",
+ "smw-paramdesc-distributionsort": "Rozdźělenje gódnotow pó licbje wustupowanjow sortěrowaś.",
+ "smw-paramdesc-distributionlimit": "Rozdźělenje gódnotow na wěste gódnoty wobgranicowaś.",
+ "smw-paramdesc-template": "Mjenja pśedłogi, z kótarejuž wuśišće maju se zwobrazniś",
+ "smw-paramdesc-columns": "Licba słupow, w kótarychž maju se wuslědki zwobrazniś (standard jo $1)",
+ "smw-paramdesc-userparam": "Gódnota jo se do zawołanje pśedłogi pśepódała, jolic pśedłoga se wužywa",
+ "smw-paramdesc-introtemplate": "Mě pśedłogi, kótarež ma se zwobrazniś pśed wuslědkami napšašowanja, joli take su",
+ "smw-paramdesc-outrotemplate": "Mě pśedłogi, kótarež ma se zwobrazniś za wuslědkami napšašowanja, joli take su",
+ "smw-paramdesc-embedformat": "HTML-toflicka, kótaraž wužywa se, aby definěrowała nadpisma",
+ "smw-paramdesc-embedonly": "Žedne nadpisma zwobraznis",
+ "smw-paramdesc-table-class": "Pśidatna CSS-klasa za tabelu",
+ "smw-paramdesc-rdfsyntax": "RDF-syntaksa, kótaraž ma se wužywaś",
+ "smw-paramdesc-csv-sep": "Źěleńske znamuško, kótarež ma se wužywaś",
+ "smw-paramdesc-dsv-separator": "Źěleńske znamuško, kótarež ma se wužywaś",
+ "smw-paramdesc-dsv-filename": "MÄ› za DSV-dataju",
+ "smw-paramdesc-filename": "Mě za wudawańsku dataju",
+ "smw-smwdoc-description": "Pokazujo tabelu wšych parametrow, kótarež daju se za pódany wuslědkowy format gromaźe ze standardnymi gódnotami a wopisanjami wužywaś.",
+ "smw-smwdoc-par-format": "Wuslědkowy format, za kótaregož parametry dokumentacija ma se zwobrazniś.",
+ "smw-smwdoc-par-parameters": "Parametry, kótarež maju se pokazaś: \"specific\" za toś te, kótarež pśidawaju se pśez format, \"base\" za toś te, kótarež stoje we wšych formatach k dispoziciji a \"all\" za wobej.",
+ "smw-paramdesc-sort": "Kakosć, pó kótarejž napšašowanje ma se sortěrowaś",
+ "smw-paramdesc-order": "Sortěrowański pórěd za napšašowanje",
+ "smw-paramdesc-searchlabel": "Tekst za dalšne pytańske wuslědki",
+ "smw-paramdesc-named_args": "Argumenty, kótarež maju se pśedłoze pśepódaś",
+ "smw-paramdesc-export": "Eksportowe nastajenje",
+ "smw-paramdesc-prettyprint": "Njeformatěrowane wudaśe, kótarež zwobraznja pśidatne zasunjenja a nowe smužki",
+ "smw-paramdesc-source": "Alternatiwne napšašowańske žrědło",
+ "smw-paramdesc-jsonsyntax": "JSON-syntaksa, kótaraž ma se wužywaś",
+ "smw-printername-feed": "RSS- a Atom-kanal",
+ "smw-paramdesc-feedtype": "Kanalowy typ",
+ "smw-paramdesc-feedtitle": "Tekst, kótaryž ma se ako titel kanala wužywaś",
+ "smw-paramdesc-feeddescription": "Tekst, kótaryž ma se ako wopisanje kanala wužywaś",
+ "smw-paramdesc-feedpagecontent": "Wopśimjeśe boka, kótarež ma se z komentarom zwobrazniś",
+ "smw-label-feed-description": "$2-kanal: $1",
+ "smw_iq_disabled": "Semantiske wótpšašanja su se znjemóžnili za toś ten wiki.",
+ "smw_iq_moreresults": "... dalšne wuslědki",
+ "smw_parseerror": "Zapódana gódnota njejo se rozměła.",
+ "smw_notitle": "\"$1\" njedajo se ako bokowe mě w toś tom wikiju wužywaś.",
+ "smw_noproperty": "\"$1\" njedajo se ako mě kakosći w toś tom wikiju wužywaś.",
+ "smw_wrong_namespace": "Jano boki w mjenjowem rumje \"$1\" su how dowólone.",
+ "smw_manytypes": "Wěcej ako jaden typ za kakosć definěrowany.",
+ "smw_emptystring": "Prozne znamješkowe rědy se njeakceptěruju.",
+ "smw_notinenum": "\"$1\" njejo w lisćinje móžnych gódnotow ($2) za toś tu kakosć.",
+ "smw_noboolean": "\"$1\" njejo dowólona gódnota typa Boolean (wěrny/njewěrny).",
+ "smw_true_words": "wěrny, w, jo, j",
+ "smw_false_words": "njewěrny, ně, n",
+ "smw_nofloat": "\"$1\" njejo licba.",
+ "smw_infinite": "Licby, kótarež su dłujke ako \"$1\", se njepódpěraju.",
+ "smw_unitnotallowed": "\"$1\" njejo ako płaśiwa měrjeńska jadnotka za toś ten atribut póstajił.",
+ "smw_nounitsdeclared": "Za toś tu kakosć njesu se žedne měrjeńške jadnotki namakali.",
+ "smw_novalues": "Žedne gódnoty pódane",
+ "smw_nodatetime": "Datum \"$1\" njejo se rozměł.",
+ "smw_toomanyclosing": "Zda se, až jo pśewjele wustupowanjow \"$1\" w napšašowanju.",
+ "smw_noclosingbrackets": "Jadne wustupowanje \"<nowiki>[[</nowiki>\" w twójom napšašanju njejo se pśez wótpowědnej \"]]\" wótzamknuło.",
+ "smw_misplacedsymbol": "Symbol \"$1\" jo se na městnje wužył, źož njejo wužytny,",
+ "smw_unexpectedpart": "Źěl \"$1\" wótpšašanja njejo se rozměł.\nWuslědki snaź njejsu ako wócakowane.",
+ "smw_emptysubquery": "Pódwótpšašanje njama płaśiwe wuměnjenje.",
+ "smw_misplacedsubquery": "Pódwótpšašanje wužywa se na městnje, źož pódwótpšašanja njejsu dowólone.",
+ "smw_valuesubquery": "Pódwótpšašanja njepódpěraju se za gódnoty kakosći \"$1\".",
+ "smw_badqueryatom": "Źěl wótpšašanja \"<nowiki>[[…]]</nowiki>\" njejo se rozměł.",
+ "smw_propvalueproblem": "Gódnota kakosći \"$1\" njejo se rozměła.",
+ "smw_noqueryfeature": "Někaka wótpšašowańska funkcije njejo se pódprěła a źěl wótpšašanja jo se wótpórał ($1).",
+ "smw_noconjunctions": "Konjunkcije (zwězanja A) we wótpšašanjach njepódpěraju se w toś tom wikiju a źěl wótpšašanja jo se wótpórał ($1).",
+ "smw_nodisjunctions": "Disjunkcije (zwězanja ABO) we wótpšašanjach njepódpěraju se w toś tom wikiju a źěl wótpšašanja jo se wótpórał ($1).",
+ "smw_querytoolarge": "Dla wikijowych wobgranicowanjow we wjelikosći abo dłymokosći njejo było móžno slědujuce wótpšašańske wuměnjenja zapśimjeś: $1",
+ "smw_notemplategiven": "Pódaj gódnotu za parameter \"template\", aby napšašowański format funkcioněrował.",
+ "smw_db_sparqlqueryproblem": "Napšašowański wuslědk njedajo se z datoweje banki SPARQL wótwołaś. Toś ta zmólka by mógła nachylna byś abo programowa zmólka w datowej bance byś.",
+ "smw_type_header": "Kakosći typa \"$1\"",
+ "smw_typearticlecount": "$1 {{PLURAL:$1|kakosć pokazujo|kakosći pokazujotej|kakosći pokazuju|kakosćow pokazujo}} se z pomocu toś togo typa.",
+ "smw_attribute_header": "Boki, kótarež wužywaju kakosć \"$1\"",
+ "smw_attributearticlecount": "$1 {{PLURAL:$1|bok pokazujo|boka pokazujotej|boki pokazuju|bokow pokazujo}} se z pomocu toś teje kakosći.",
+ "exportrdf": "Boki ako RDF eksportěrowaś",
+ "smw_exportrdf_docu": "Toś ten bok śi dowólujo, daty z boka w formaśe RDF dobyś.\nAby boki eksportěrował, zapódaj titele do slědujucego tekstowego póla, jaden titel na smužku.",
+ "smw_exportrdf_recursive": "Wšykne pśiwuzne boki rekursiwnje eksportěrowaś.\nŹiwaj na to, až wuslědk by mógł wjeliki byś!",
+ "smw_exportrdf_backlinks": "Teke wšykne boki eksportěrowaś, kótarež póśěgaju se na eksportěrowane boki.\nNapórajo RDF, kótaryž dajo se pśepytaś.",
+ "smw_exportrdf_lastdate": "Boki njeeksportěrowaś, kótarež njejsu se změnili wót pódanego casa.",
+ "smw_exportrdf_submit": "Eksportěrowaś",
+ "uriresolver": "Rezolwer URI",
+ "properties": "Kakosći",
+ "smw_properties_docu": "Slědujuce kakosći wužywaju se we wikiju.",
+ "smw_property_template": "$1 typa $2 ($3 {{PLURAL:$3|wužyśe|wužyśi|wužyśa|wužyśow}})",
+ "smw_propertylackspage": "Wše kakosći měli se pśez bok wopisaś!",
+ "smw_propertylackstype": "Za toś tu kakosć njejo se žeden typ pódał (mysli se tuchylu typ $1).",
+ "smw_propertyhardlyused": "Toś ta kakosć se lěbda we wikiju wužywa!",
+ "smw-property-name-invalid": "Kakosć $1 njedajo se wužywaś (njepłaśiwe kakostne mě)",
+ "smw-sp-property-searchform": "Kakosći zwobrazniś, kótarež wopśimuju:",
+ "smw-sp-property-searchform-inputinfo": "Wudaśe źiwa na wjelikopisanje. Za filtrowanje zwobraznjuju se jano kakosći, kótarež wótpowěduju wuměnjenjeju.",
+ "concepts": "Koncepty",
+ "smw-special-concept-header": "Lisćina konceptow",
+ "smw-special-concept-count": "{{PLURAL:$1|Slědujucy koncept|Slědujucej $1 koncepta|Slědujuce $1 koncepty|Slědujucych $1 konceptow}} {{PLURAL:$1|eksistěrujo|eksistěrujotej|eksistěruju|eksistěrujo}}.",
+ "smw-special-concept-empty": "Njejo se žeden koncept namakał.",
+ "unusedproperties": "Njewužywane kakosći",
+ "smw-unusedproperties-docu": "Slědujuce kakosći eksistěruju, lěcrownož žeden drugi bok je njewužywa.",
+ "smw-unusedproperty-template": "$1 typa $2",
+ "wantedproperties": "Póžedane kakosći",
+ "smw-wantedproperties-docu": "Slědujuce kakosći se we wikiju wužywaju, ale hyšći njamaju bok, kótaryž je wopisujo.",
+ "smw-wantedproperty-template": "$1 ({{PLURAL:$2|raz|dwójcy|$2 raze|$2 raz}} wužyta)",
+ "smw_purge": "Aktualizěrowaś",
+ "types": "Typy",
+ "smw_types_docu": "Slědujuca lisćina wšych datowych typow, kótarež daju se kakosćam pśipokazaś.",
+ "smw-special-types-no-such-type": "Pódany datowy typ njeeksistěrujo",
+ "smw-statistics": "Semantiska statistika",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Kakostna gódnota|Kakostnej gódnośe|Kakostne gódnoty}} (dogromady)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Kakosć|Kakosći}}]] (dogromady)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Kakosć|Kakosći}} (dogromady)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Kakosć|Kakosći}} (z bokom zregistrěrowane)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Kakosć|Kajkosći}} (datowemu typoju pśipokazane)",
+ "smw-statistics-query-inline": "{{PLURAL:$1|Napšašowanje|Napšašowani|Napšašowanja}}",
+ "smw-statistics-query-size": "Wjelikosć napšašowanja",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Koncept|Koncepta|Koncepty}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Koncept|Koncepta|Koncepty}}]]",
+ "smw-statistics-subobject-count": "{{PLURAL:$1|Podobjekt|Podobjekta|Podobjekty}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datowy typ|Datowej typa|Datowe typy}}]]",
+ "smw_uri_doc": "Rezolwer URI implementěrujo [$1 W3C TAG finding on httpRange-14].\nStara se za to, až luźe njebudu websedła.",
+ "ask": "Semantiske pytanje",
+ "smw_ask_sortby": "Pó słupje sortěrowaś (opcionalny)",
+ "smw_ask_ascorder": "Stupajucy",
+ "smw_ask_descorder": "Spadujucy",
+ "smw_ask_submit": "Wuslědki namakaś",
+ "smw_ask_editquery": "Wótpšašanje wobźěłaś",
+ "smw_add_sortcondition": "[Sortěrowańske wuměnjenje pśidaś]",
+ "smw_ask_hidequery": "[Wótpšašanje schowaś]",
+ "smw_ask_help": "Wótpšašowańska pomoc",
+ "smw_ask_queryhead": "Wótpšašanje",
+ "smw_ask_printhead": "Pśidatne daty, kótarež maju se zwobrazniś",
+ "smw_ask_printdesc": "(pśidaj jadne atributowe mě na smužku)",
+ "smw_ask_format_as": "Formatěrowaś ako:",
+ "smw_ask_defaultformat": "standard",
+ "smw_ask_otheroptions": "Druge opcije",
+ "smw-ask-otheroptions-collapsed-info": "Pšosym wužyj plusowy symbol (+), aby se wšykne k dispoziciji stojece opcije woglědał",
+ "smw_ask_show_embed": "Zasajźony kod pokazaś",
+ "smw_ask_hide_embed": "Zasajźony kod schowaś",
+ "smw_ask_embed_instr": "Aby toś to wótpšašanje do wikiboka zasajźił, wužyj slědujucy kod.",
+ "smw-ask-delete": "[Lašowaś]",
+ "smw-ask-sorting": "Sortěrowanje",
+ "smw-ask-format-selection-help": "Za nadrobne wopisanje glej bok $1 pomocy.",
+ "searchbyproperty": "Pó kakosći pytaś",
+ "smw_sbv_docu": "Wšykne boki pytaś, kótarež maju wěstu kakosć a gódnotu.",
+ "smw_sbv_novalue": "Zapódaj płaśiwu gódnotu za kakosć abo woglědaj se wšykne gódnoty za kakosć \"$1\".",
+ "smw_sbv_displayresult": "Lisćina wšych bokow, kótarež maju kakosć \"$1\" z gódnotu \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Lisćina wšych bokow, kótarež maju kakosć \"$1\" z gódnotu \"$2\".\nDokulaž jo jano mało wuslědkow było, zwobraznjuju se pódobne gódnoty.",
+ "smw_sbv_property": "Kakosć:",
+ "smw_sbv_value": "Gódnota:",
+ "smw_sbv_submit": "Wuslědki namakaś",
+ "browse": "Wiki pśepytaś",
+ "smw_browselink": "Kakosći pśepytaś",
+ "smw_browse_article": "Zapódaj mě boka, wót kótaregož ma se pytaś.",
+ "smw_browse_go": "PytaÅ›",
+ "smw_browse_show_incoming": "Kakosći pokazaś, kótarež sem wótkazuju",
+ "smw_browse_hide_incoming": "Kakosći schowaś, kótarež sem wótkazuju",
+ "smw_browse_no_outgoing": "Toś ten bok njama kakosći.",
+ "smw_browse_no_incoming": "Žedne kakosći njewótkazuju k toś tomu bokoju.",
+ "smw_inverse_label_default": "$1 wót",
+ "smw_inverse_label_property": "Pomjenjenje nawopacneje kakosći",
+ "pageproperty": "Pytanje za bokoweju kakosću",
+ "smw_pp_docu": "Wše gódnoty kakosći na pódanem boku pytaś.\nZapódaj ako bok tak teke kakosć.",
+ "smw_pp_from": "Wót boka",
+ "smw_pp_type": "Kakosć",
+ "smw_pp_submit": "Wuslědki namakaś",
+ "smw_result_prev": "Slědk",
+ "smw_result_next": "Pśiducy",
+ "smw_result_results": "Wuslědki",
+ "smw_result_noresults": "Žedne wuslědki.",
+ "smwadmin": "Administratorowe funkcije za Semantic MediaWiki",
+ "smw-admin-setupsuccess": "Składowańska jadnotka jo se wuspěšnje nastajiła.",
+ "smw_smwadmin_return": "Slědk k $1",
+ "smw_smwadmin_updatestarted": "Nowy aktualizěrowański proces za wótnowjenje semantiskich datow jo se startował.\nWšykne skłaźone daty budu se znowego natwarjaś abo pórěźaś, źož trjeba.\nMóžoš póstupoju aktualizacije na toś tom specialnem boku slědowaś.\n\nSlědk k $1.",
+ "smw_smwadmin_updatenotstarted": "Proces aktualizacije južo běžy.\nNowy se njestartujo.\n\nSlědk k $1.",
+ "smw_smwadmin_updatestopped": "Wše eksistěrujuce aktualizěrowańske procese su se zastajili.\n\nSlědk k $1.",
+ "smw_smwadmin_updatenotstopped": "Aby běžecy proces zastajił, musyš kontrolowy kašćik aktiwěrowaś, aby pódał, až sy napšawdu wěsty.\n\nSlědk k $1.",
+ "smw-admin-docu": "Toś ten specialny bok śi pomaga za instalaciju a aktualizaciju a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a>. Mysli se na zawěsćenje gódnotnych datow, nježli až wuwjeźoš administratiwne funkcije.",
+ "smw-admin-db": "Instalacija a aktualizacija datoweje banki",
+ "smw-admin-dbdocu": "Semantic MediaWiki trjeba rozšyrjenja za datowu banku MediaWiki, aby semantiske daty składował. Slědujuca funkcija zawěsćijo, až twója datowa banka se pórědnje zarědujo.\nZměny, kótarež cynje se w toś tom kšacu, njewobwliwuju zbytk datoweje banki MediaWiki a daju se lažko anulěrowaś, jolic póžedane.\nToś te zarědowańska funkcija móžo se někotare razy wugbaś, mimo až zawinuju škódu, ale jo trěbna jano jaden raz pśi instalaciji abo aktualizaciji.",
+ "smw-admin-permissionswarn": "Jolic operacija raźi se z SQL-zmólkami, wužywaŕ datoweje banki, kótaregož twój wiki wužywa (skontrolěruj twóju dataja LocalSettings.php), nejskerjej njama dosegajuce pšawa.\nPak zwól toś tomu wužywarjeju pśidatne pšawa za napóranje a lašowanje tabelow, zapódaj nachylu twójo admininistratorowe wužywarske mě w LocalSettings.php, pak wužyj wótglědowański skript <code>setupStore.php</code>, kótarež mógu wósobinske pódaśa administratora wužywaś.",
+ "smw-admin-dbbutton": "Tabele inicializěrowaś abo aktualizěrowaś",
+ "smw-admin-announce": "Twój wiki pśipowěźeś",
+ "smw_smwadmin_datarefresh": "Reparatura a aktualizacija datow",
+ "smw_smwadmin_datarefreshdocu": "Jo móžno, wšykne daty Semantic MediaWiki wótnowiś, kótarež bazěruju na aktualnem wopśimjeśu wikija.\nTo móžo wužytne byś, aby se wobškóźone daty pórěźili abo daty aktualizěrowali, jolic interny format jo se změnił pśez aktualizaciju softwara.\nAktualizacija pśewjedujo se bok pó boku a njebuźo se ned kóńcyś.\nSlědujuce pokazujo, lěc aktualizacija rowno wótběgujo a dowólujo śi aktualizacije startowaś abo zastajiś (snaźkuli toś ta funkcija jo se wót administratora sedła znjemóžniła).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Aktualizacija se rowno wótměwa.</strong>\nJo normalne, až aktualizacija jano pómałem póstupujo, dokulaž aktualizěrujo daty jano w małych porcijach kuždy raz, gaž wužywaŕ ma pśistup na wiki.\nAby skóńcył toś tu aktualizaciju malsnjeje, móžoš wótwardowański skript MediaWiki <code>runJobs.php</code> zawołaś (wužyj opciju <code>--maxjobs 1000</code>, aby wobgranicował licbu aktualizacijow, kótarež se naraz pśewjadu).\nLicony póstup aktualneje aktualizacije:",
+ "smw_smwadmin_datarefreshbutton": "Aktualizaciju datow zachopiÅ›",
+ "smw_smwadmin_datarefreshstop": "ToÅ› tu aktualizaciju zastajiÅ›",
+ "smw_smwadmin_datarefreshstopconfirm": "Jo, som se wěsty.",
+ "smw-admin-support": "Pódpěru wobstaraś",
+ "smw-admin-supportdocu": "Wšake resurse mógli śi w paźe problemow pomagaś:",
+ "smw-admin-installfile": "Joli maš problemy ze swójeju instalaciju, pśecytaj směrnice w dataji <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL</a>.",
+ "smw-admin-smwhomepage": "Dopołna wužywarska dokumentacija za Semantic MediaWiki jo na <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Zmólki daju se na <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> k wěsći daś.",
+ "smw-admin-questions": "Jolic maš dalšne pšašanja abo naraźenja, pśizamkni se diskusiji na <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">wužywarskem forumje Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Datowa struktura",
+ "smw_adminlinks_displayingdata": "Daty zwobrazniÅ›",
+ "smw_adminlinks_inlinequerieshelp": "Pomoc k wótpšašanjam",
+ "smw-createproperty-isproperty": "To jo kakosć typa $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Dowólona gódnota za toś tu kakosć jo|Dowólonej gódnośe za toś tu kakosć stej|Dowólone gódnoty za toś tu kakosć su|Dowólone gódnoty za toś tu kakosć su}}:",
+ "smw-paramdesc-category-delim": "Źěleńske znamuško",
+ "smw-paramdesc-category-template": "Pśedłoga, z kótarejuž zapiski maju se formatěrowaś",
+ "smw-paramdesc-category-userparam": "Parmeter, kótaryž ma se pśedłoze pśepódaś",
+ "smw-info-par-message": "Powěsć, kótaraž ma se zwobrazniś.",
+ "smw-info-par-icon": "Symbol, kótaryž ma se pokazaś, pak \"info\" pak \"warnowanje\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-ask-options": "Semantiske pytańske opcije",
+ "smw-prefs-ask-options-tooltip-display": "Parametrowy tekst ako spěšne info zwobrazniś",
+ "smw-ui-tooltip-title-property": "Kakosć",
+ "smw-ui-tooltip-title-quantity": "TÅ¡ocha",
+ "smw-ui-tooltip-title-info": "Informacije",
+ "smw-ui-tooltip-title-service": "Słužbne wótkaze",
+ "smw-ui-tooltip-title-warning": "Zmólka",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "TÅ¡ojenje",
+ "smw-ui-tooltip-title-note": "Pokazka",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw_unknowntype": "Typ toś teje kakosći jo njepłaśiwy",
+ "smw-concept-cache-text": "Koncept ma dogromady $1 {{PLURAL:$1|bok|boka|boki|bokow}} a jo se $2 slědny raz zaktualizěrował.",
+ "smw_concept_header": "Boki koncepta \"$1\"",
+ "smw_conceptarticlecount": "$1 {{PLURAL:$1|bok se pokazujo|boka se pokazujotej|boki se pokazuju|bokow se pokazujo}}, {{PLURAL:$1|kótaryž słuša|kótarejž słušatej|kótarež słušaju|kótarež słuša}} k tomu konceptoju.",
+ "right-smw-admin": "Administraciske nadawki (Semantic MediaWiki)",
+ "group-smwadministrator": "Administratory Semantic MediaWiki",
+ "group-smwadministrator-member": "{{GENDER:$1|SMW-administrator|SMW-administratorka}}",
+ "grouppage-smwadministrator": "{{ns:project}}:SMW-administratory",
+ "action-smw-admin": "administraciske nadawki Semantic MediaWiki",
+ "smw-sp-properties-header-label": "Lisćina kakosćow",
+ "smw-sp-admin-settings-button": "Lisćinu nastajenjow napóraś",
+ "smw-livepreview-loading": "Lodowanje …"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/dty.json b/www/wiki/extensions/SemanticMediaWiki/i18n/dty.json
new file mode 100644
index 00000000..2ce0fa27
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/dty.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "रमेश सिंह बोहरा"
+ ]
+ },
+ "browse": "बà¥à¤°à¤¾à¤‰à¤œ विकि"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ee.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ee.json
new file mode 100644
index 00000000..ab5d5250
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ee.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw_browse_go": "Yi"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/el.json b/www/wiki/extensions/SemanticMediaWiki/i18n/el.json
new file mode 100644
index 00000000..0dad029d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/el.json
@@ -0,0 +1,567 @@
+{
+ "@metadata": {
+ "authors": [
+ "Consta",
+ "Crazymadlover",
+ "Geraki",
+ "Glavkos",
+ "Lou",
+ "Omnipaedista",
+ "Protnet",
+ "ZaDiak",
+ "FocalPoint",
+ "Kghbln",
+ "Ανώνυμος Βικιπαιδιστής",
+ "Nemo bis",
+ "Thodoris",
+ "KATRINE1992",
+ "Tgkarounos",
+ "Nikosgranturismogt"
+ ]
+ },
+ "smw-desc": "Κάνοντας το wiki σας πιο Ï€Ïοσιτό - σε μηχανές ''και'' ανθÏώπους ([https://www.semantic-mediawiki.org/wiki/Help:User_manual ηλεκτÏονική τεκμηÏίωση])",
+ "smw-title": "Σημασιολογικό MediaWiki",
+ "smw-semantics-not-enabled": "Η λειτουÏγικότητα Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki δεν είναι ενεÏγοποιημένη για αυτό το wiki.",
+ "smw_viewasrdf": "Ροή RDF",
+ "smw_finallistconjunct": " και",
+ "smw-factbox-head": "... πεÏισσότεÏα σχετικά με το \"$1\"",
+ "smw-factbox-facts": "Γεγονότα",
+ "smw_isspecprop": "Αυτή η ιδιότητα είναι μια ειδική ιδιότητα σε αυτό το wiki.",
+ "smw-concept-cache-header": "ΧÏήση Ï€ÏοσωÏινής μνήμης",
+ "smw-concept-cache-count": "Η [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count Ï€ÏοσωÏινή μνήμη έννοιας] πεÏιέχει {{PLURAL:$1|'''μία''' οντότητα|'''$1''' οντότητες}} ($2).",
+ "smw-concept-no-cache": "Δεν υπάÏχει Ï€ÏοσωÏινή μνήμη διαθέσιμη.",
+ "smw_concept_description": "ΠεÏιγÏαφή της έννοιας «$1»",
+ "smw_no_concept_namespace": "Έννοιες μποÏοÏν να οÏιστοÏν μόνο σε σελίδες στον ονοματοχώÏο «Έννοια:».",
+ "smw_multiple_concepts": "Κάθε έννοια μποÏεί να έχει μόνο έναν οÏισμό έννοιας.",
+ "smw_concept_cache_miss": "Η έννοια «$1» δεν μποÏεί να χÏησιμοποιηθεί τη στιγμή, επειδή η ÏÏθμιση παÏαμέτÏων του wiki απαιτεί να υπολογιστεί εκτός σÏνδεσης.\nΕάν μετά την πάÏοδο κάποιου χÏόνου το Ï€Ïόβλημα δεν έχει λυθεί, ζητήστε από το διαχειÏιστή του ιστοχώÏου να κάνει αυτήν την έννοια διαθέσιμη.",
+ "smw_noinvannot": "Δεν μποÏοÏν να δοθοÏν τιμές σε αντίστÏοφες ιδιότητες.",
+ "version-semantic": "Σημασιολογικές επεκτάσεις",
+ "smw_baduri": "Δεν επιτÏέπονται URI της μοÏφής «$1».",
+ "smw_printername_count": "ΚαταμέτÏηση",
+ "smw_printername_csv": "Εξαγωγή σε CSV",
+ "smw_printername_dsv": "Εξαγωγή σε DSV",
+ "smw_printername_debug": "ΕÏώτημα αποσφαλμάτωσης (για ειδικοÏÏ‚)",
+ "smw_printername_embedded": "Ενσωμάτωση πεÏιεχομένων σελίδων",
+ "smw_printername_json": "Εξαγωγή σε JSON",
+ "smw_printername_list": "Λίστα",
+ "smw_printername_ol": "ΑÏιθμημένη λίστα",
+ "smw_printername_ul": "Λίστα με κουκκίδες",
+ "smw_printername_table": "Πίνακας",
+ "smw_printername_broadtable": "Πίνακας πλήÏους εÏÏους",
+ "smw_printername_template": "ΠÏότυπο",
+ "smw_printername_templatefile": "ΑÏχείο Ï€ÏοτÏπου",
+ "smw_printername_rdf": "Εξαγωγή σε RDF",
+ "smw_printername_category": "ΚατηγοÏία",
+ "validator-type-class-SMWParamSource": "κείμενο",
+ "smw-paramdesc-limit": "Ο μέγιστος αÏιθμός αποτελεσμάτων που θα επιστÏαφοÏν",
+ "smw-paramdesc-offset": "Η μετατόπιση του Ï€Ïώτου αποτελέσματος",
+ "smw-paramdesc-headers": "ΠÏοβολή των κεφαλίδων/ονομάτων ιδιοτήτων",
+ "smw-paramdesc-mainlabel": "Η ετικέτα που θα δοθεί στο όνομα της κÏÏιας σελίδας",
+ "smw-paramdesc-link": "Εμφάνιση τιμών ως σÏνδεσμοι",
+ "smw-paramdesc-intro": "Κείμενο που θα εμφανίζεται Ï€Ïιν από τα αποτελέσματα του εÏωτήματος, αν υπάÏχουν",
+ "smw-paramdesc-outro": "Κείμενο που θα εμφανίζεται μετά από τα αποτελέσματα του εÏωτήματος, αν υπάÏχουν",
+ "smw-paramdesc-default": "Κείμενο που θα εμφανίζεται αν δεν υπάÏχουν αποτελέσματα εÏωτήματος",
+ "smw-paramdesc-sep": "ΔιαχωÏιστικό για τις τιμές",
+ "smw-paramdesc-showsep": "Εμφάνιση διαχωÏÎ¹ÏƒÏ„Î¹ÎºÎ¿Ï ÏƒÏ„Î·Î½ αÏχή του αÏχείου CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Αντί για Ï€Ïοβολή όλων των τιμών, καταμέτÏηση των εμφανίσεών τους και Ï€Ïοβολή αυτών.",
+ "smw-paramdesc-distributionsort": "Ταξινόμηση της κατανομής τιμών βάσει πλήθους εμφανίσεων.",
+ "smw-paramdesc-distributionlimit": "ΠεÏιοÏισμός της κατανομής τιμών σε πλήθος μόνο κάποιων τιμών.",
+ "smw-paramdesc-template": "Το όνομα Ï€ÏοτÏπου με το οποίο να εμφανίζονται οι εκτυπώσεις",
+ "smw-paramdesc-columns": "Ο αÏιθμός των στηλών κειμένου που θα έχει το αποτέλεσμα (η Ï€Ïοεπιλογή είναι $1)",
+ "smw-paramdesc-userparam": "Τιμή που θα πεÏνιέται σε κάθε κλήση Ï€ÏoÏ„Ïπου, αν χÏησιμοποιείται Ï€Ïότυπο",
+ "smw-paramdesc-introtemplate": "Όνομα Ï€ÏοτÏπου που θα εμφανίζεται Ï€Ïιν από τα αποτελέσματα του εÏωτήματος, αν υπάÏχουν",
+ "smw-paramdesc-outrotemplate": "Όνομα Ï€ÏοτÏπου που θα εμφανίζεται μετά από τα αποτελέσματα του εÏωτήματος, αν υπάÏχουν",
+ "smw-paramdesc-embedformat": "Ετικέτα HTML που χÏησιμοποιείται για τον οÏισμό επικεφαλίδων",
+ "smw-paramdesc-embedonly": "Μη εμφάνιση επικεφαλίδων",
+ "smw-paramdesc-table-class": "Μια Ï€Ïόσθετη κλάση CSS για αυτόν τον πίνακα",
+ "smw-paramdesc-table-transpose": "ΠαÏουσίαση των κεφαλίδων του πίνακα κατακόÏυφα και των αποτελεσμάτων οÏιζόντια",
+ "smw-paramdesc-rdfsyntax": "ΣÏνταξη RDF που θα χÏησιμοποιηθεί",
+ "smw-paramdesc-csv-sep": "ΔιαχωÏιστικό που θα χÏησιμοποιηθεί",
+ "smw-paramdesc-csv-valuesep": "ΔιαχωÏίζει τις τιμές",
+ "smw-paramdesc-csv-merge": "ΣÏμπτυξη σειÏών και τιμών στηλών με έναν παÏόμοιο δείκτη αναγνώÏισης θέματος (aka Ï€Ïώτη στήλη)",
+ "smw-paramdesc-csv-bom": "ΠÏοσθέστε ένα ΒΟΜ (χαÏακτήÏα σήματος που στάλθηκε) στο πάνω μέÏος του φακέλλου παÏαγωγής",
+ "smw-paramdesc-dsv-separator": "ΔιαχωÏιστικό που θα χÏησιμοποιηθεί",
+ "smw-paramdesc-dsv-filename": "Όνομα αÏχείου DSV",
+ "smw-paramdesc-filename": "Το όνομα για το αÏχείο εξόδου",
+ "smw-smwdoc-description": "Εμφανίζει έναν πίνακα με όλες τις παÏαμέτÏους που μποÏοÏν να χÏησιμοποιηθοÏν για τη μοÏφή αποτελέσματος που έχει οÏιστεί μαζί με Ï€Ïοεπιλεγμένες τιμές και πεÏιγÏαφές.",
+ "smw-smwdoc-par-format": "ΜοÏφή αποτελέσματος για την οποία να Ï€Ïοβληθεί τεκμηÏίωση παÏαμέτÏων.",
+ "smw-smwdoc-par-parameters": "Ποιες παÏάμετÏοι να εμφανίζονται. «ειδικές» για όσες Ï€Ïοστίθενται από τη μοÏφή, «βασικές» για όσες είναι διαθέσιμες σε όλες τις μοÏφές και «όλες» και για τα δÏο.",
+ "smw-paramdesc-sort": "Ιδιότητα βάσει της οποίας να ταξινομηθεί το εÏώτημα",
+ "smw-paramdesc-order": "ΣειÏά ταξινόμησης του εÏωτήματος",
+ "smw-paramdesc-searchlabel": "Κείμενο για συνέχιση της αναζήτησης",
+ "smw-paramdesc-named_args": "Ονοματοδοσία στα οÏίσματα που πασάÏονται στο Ï€Ïότυπο",
+ "smw-paramdesc-template-arguments": "ΟÏίζει τον Ï„Ïόπο με τον οποίο θα μεταβιβάζονται στο Ï€Ïότυπο τα επώνυμα οÏίσματα.",
+ "smw-paramdesc-export": "Επιλογή εξαγωγής",
+ "smw-paramdesc-prettyprint": "ΌμοÏφα μοÏφοποιημένος κώδικας που εμφανίζει Ï€Ïόσθετες εσοχές και αλλαγές γÏαμμής",
+ "smw-paramdesc-source": "Εναλλακτική Ï€Ïοέλευση εÏωτήματος",
+ "smw-paramdesc-jsonsyntax": "ΣÏνταξη JSON που θα χÏησιμοποιηθεί",
+ "smw-printername-feed": "Ροές RSS και Atom",
+ "smw-paramdesc-feedtype": "ΤÏπος Ïοής",
+ "smw-paramdesc-feedtitle": "Κείμενο που θα χÏησιμοποιείται ως τίτλος της Ïοής",
+ "smw-paramdesc-feeddescription": "Κείμενο που θα χÏησιμοποιείται ως πεÏιγÏαφή της Ïοής",
+ "smw-paramdesc-feedpagecontent": "ΠεÏιεχόμενο σελίδων που θα εμφανίζεται με τη Ïοή",
+ "smw-label-feed-description": "Ïοή $2 για $1",
+ "smw_iq_disabled": "Τα σημασιολογικά εÏωτήματα έχουν απενεÏγοποιηθεί για αυτό το wiki.",
+ "smw_iq_moreresults": "... πεÏισσότεÏα αποτελέσματα",
+ "smw_parseerror": "Η τιμή που δόθηκε δεν έγινε κατανοητή.",
+ "smw_notitle": "Το «$1» δεν μποÏεί να χÏησιμοποιηθεί σαν όνομα σελίδας σε αυτό το wiki.",
+ "smw_noproperty": "Το «$1» δεν μποÏεί να χÏησιμοποιηθεί σαν όνομα ιδιότητας σε αυτό το wiki.",
+ "smw_wrong_namespace": "Μόνο οι σελίδες που ανήκουν στον ονοματοχώÏο «$1» επιτÏέπονται εδώ.",
+ "smw_manytypes": "Έχουν οÏιστεί πεÏισσότεÏοι από έναν Ï„Ïποι για την ιδιότητα.",
+ "smw_emptystring": "Κενές συμβολοσειÏές δεν γίνονται δεκτές.",
+ "smw_notinenum": "Το «$1» δεν είναι στη λίστα ($2) [[Property:Allows value|επιτÏεπτών τιμών]] για την ιδιότητα «$3».",
+ "smw_noboolean": "Το «$1» δεν αναγνωÏίζεται ως Μπουλιανή (αληθές/ψευδές) τιμή.",
+ "smw_true_words": "αληθές,α,ναι,ν",
+ "smw_false_words": "ψευδές,ψ,όχι,ο",
+ "smw_nofloat": "Το «$1» δεν είναι αÏιθμός.",
+ "smw_infinite": "Δεν υποστηÏίζονται τόσο μεγάλοι αÏιθμοί σαν τον «$1».",
+ "smw_unitnotallowed": "Το «$1» δεν έχει δηλωθεί ως έγκυÏη μονάδα μέτÏησης για αυτήν την ιδιότητα.",
+ "smw_nounitsdeclared": "Δεν έχουν δηλωθεί μονάδες μέτÏησης για αυτήν την ιδιότητα.",
+ "smw_novalues": "Δεν καθοÏίστηκαν τιμές.",
+ "smw_nodatetime": "Η ημεÏομηνία «$1» δεν έγινε κατανοητή.",
+ "smw_toomanyclosing": "Φαίνεται ότι το «$1» εμφανίζεται πάÏα πολλές φοÏές μέσα στο εÏώτημα.",
+ "smw_noclosingbrackets": "Κάποιες αγκÏλες «<nowiki>[[</nowiki>» στο εÏώτημά σας δεν έχουν κλείσει με αντίστοιχο «]]».",
+ "smw_misplacedsymbol": "Το σÏμβολο «$1» χÏησιμοποιήθηκε σε σημείο όπου δεν έχει κάποια χÏησιμότητα.",
+ "smw_unexpectedpart": "Το τμήμα «$1» του εÏωτήματος δεν έγινε κατανοητό.\nΤα αποτελέσματα ενδέχεται να μην είναι τα αναμενόμενα.",
+ "smw_emptysubquery": "Κάποιο υποεÏώτημα δεν έχει έγκυÏη συνθήκη.",
+ "smw_misplacedsubquery": "Κάποιο υποεÏώτημα χÏησιμοποιήθηκε σε σημείο όπου δεν επιτÏέπονται υποεÏωτήματα.",
+ "smw_valuesubquery": "Δεν υποστηÏίζονται υποεÏωτήματα για τιμές της ιδιότητας «$1».",
+ "smw_badqueryatom": "Κάποιο τμήμα «<nowiki>[[…]]</nowiki>» του εÏωτήματος δεν έγινε κατανοητό.",
+ "smw_propvalueproblem": "Η τιμή της ιδιότητας «$1» δεν έγινε κατανοητή.",
+ "smw_noqueryfeature": "Κάποια δυνατότητα των εÏωτημάτων δεν υποστηÏίζεται σε αυτό το wiki και ένα μέÏος του εÏωτήματος έχει κοπεί ($1).",
+ "smw_noconjunctions": "Η λογική σÏζευξη εÏωτημάτων δεν υποστηÏίζεται σε αυτό το wiki και ένα μέÏος του εÏωτήματος έχει κοπεί ($1).",
+ "smw_nodisjunctions": "Η λογική διάζευξη εÏωτημάτων δεν υποστηÏίζεται σε αυτό το wiki και ένα μέÏος του εÏωτήματος έχει κοπεί ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Η ακόλουθη συνθήκη του εÏωτήματος δεν μπόÏεσε να ληφθεί|Οι ακόλουθες συνθήκες του εÏωτήματος δεν μπόÏεσαν να ληφθοÏν}} υπόψιν λόγω των πεÏιοÏισμών μεγέθους ή βάθους εÏωτήματος Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… wiki: <code>$1</code>.",
+ "smw_notemplategiven": "Δώστε μια τιμή για την παÏάμετÏο «πÏότυπο» για αυτήν τη μοÏφή εÏωτήματος για να λειτουÏγήσει.",
+ "smw_db_sparqlqueryproblem": "Το αποτέλεσμα του εÏωτήματος δεν ήταν δυνατόν να ληφθεί από τη βάση δεδομένων SPARQL. Αυτό το σφάλμα μποÏεί να είναι Ï€ÏοσωÏινό ή να δηλώνει την ÏπαÏξη Ï€ÏογÏÎ±Î¼Î¼Î±Ï„Î¹ÏƒÏ„Î¹ÎºÎ¿Ï ÏƒÏ†Î¬Î»Î¼Î±Ï„Î¿Ï‚ στο λογισμικό της βάσης δεδομένων.",
+ "smw_db_sparqlqueryincomplete": "Το να απαντηθεί το εÏώτημα αποδείχτηκε Ï€Î¿Î»Ï Î´Ïσκολο και η εκτέλεση δεν ολοκληÏώθηκε. ΜεÏικά αποτελέσματα μποÏεί να είναι ελλειπή. Εάν είναι δυνατόν, Ï€Ïοσπαθήστε να χÏησιμοποιήσετε απλοÏστεÏο εÏώτημα.",
+ "smw_type_header": "Ιδιότητες Ï„Ïπου «$1»",
+ "smw_typearticlecount": "Εμφάνιση $1 {{PLURAL:$1|ιδιότητας|ιδιοτήτων}} που χÏησιμοποιοÏν αυτόν τον Ï„Ïπο.",
+ "smw_attribute_header": "Σελίδες που χÏησιμοποιοÏν την ιδιότητα «$1»",
+ "smw_attributearticlecount": "Εμφάνιση $1 {{PLURAL:$1|σελίδας που χÏησιμοποιεί|σελίδων που χÏησιμοποιοÏν}} αυτήν την ιδιότητα.",
+ "smw-propertylist-subproperty-header": "Υποϊδιότητες",
+ "smw-propertylist-redirect-header": "Συνώνυμα",
+ "smw-propertylist-count": "Εμφάνιση $1 {{PLURAL:$1|συσχετισμένης οντότητας|συσχετισμένων οντοτήτων}}.",
+ "smw-propertylist-count-with-restricted-note": "Εμφάνιση $1 {{PLURAL:$1|συσχετισμένης οντότητας|συσχετισμένων οντοτήτων}} (πεÏισσότεÏες είναι διαθέσιμες, αλλά η παÏουσίαση πεÏιοÏίζεται σε «$2»).",
+ "exportrdf": "Εξαγωγή σελίδων σε RDF",
+ "smw_exportrdf_docu": "Αυτή η σελίδα σας επιτÏέπει τη λήψη δεδομένων από μια σελίδα σε μοÏφή RDF.\nΓια να εξαγάγετε σελίδες, πληκτÏολογήστε τους τίτλους τους στο πλαίσιο κειμένου παÏακάτω, ένας τίτλος ανά γÏαμμή.",
+ "smw_exportrdf_recursive": "Εξαγωγή όλων των σχετικών σελίδων αναδÏομικά.\nÎα σημειωθεί ότι το αποτέλεσμα μποÏεί να είναι μεγάλο!",
+ "smw_exportrdf_backlinks": "Εξαγωγή επίσης όλων των σελίδων που αναφέÏονται στις εξαγόμενες σελίδες.\nΠαÏάγει RDF με δυνατότητα πεÏιήγησης (δηλαδή με συνδέσμους επιστÏοφής).",
+ "smw_exportrdf_lastdate": "Μην εξάγετε σελίδες που δεν έχουν αλλάξει από τη δεδομένη χÏονική στιγμή.",
+ "smw_exportrdf_submit": "Εξαγωγή",
+ "uriresolver": "ΕπιλÏτης URI",
+ "properties": "Ιδιότητες",
+ "smw_properties_docu": "Σε αυτό το wiki χÏησιμοποιοÏνται οι ακόλουθες ιδιότητες.",
+ "smw_property_template": "$1 Ï„Ïπου $2 ($3 {{PLURAL:$3|χÏήση|χÏήσεις}})",
+ "smw_propertylackspage": "Όλες οι ιδιότητες Ï€Ïέπει να πεÏιγÏάφονται από κάποια σελίδα!",
+ "smw_propertylackstype": "Δεν έχει καθοÏιστεί Ï„Ïπος για την ιδιότητα αυτή (Ï€Ïος το παÏόν θεωÏείται Ï„Ïπος $1).",
+ "smw_propertyhardlyused": "Αυτή η ιδιότητα δεν χÏησιμοποιείται σχεδόν καθόλου εντός του wiki!",
+ "smw-property-name-invalid": "Η ιδιότητα $1 δεν μποÏεί να χÏησιμοποιηθεί (μη έγκυÏο όνομα ιδιότητας).",
+ "smw-property-name-reserved": " Το\"$1\" ταξινομήθηκε ως όνομα καταχώÏησης από αποθήκευση και δεν Ï€Ïέπει να χÏησιμοποιείται για ιδία χÏήση. Η ακόλουθη ιστοσελίδα [https://www.semantic-mediawiki.org/wiki/Help:Property_naming help page] μποÏεί να πεÏιλαμβάνει πληÏοφοÏίες όπως το γιατί αυτό το όνομα αποθηκεÏτηκε (καταχωÏήθηκε).",
+ "smw-sp-property-searchform": "ΠÏοβολή ιδιοτήτων που πεÏιέχουν:",
+ "smw-sp-property-searchform-inputinfo": "Το πλαίσιο εισαγωγής δεδομένων κάνει διάκÏιση πεζών-κεφαλαίων και όταν χÏησιμοποιείται για φιλτÏάÏισμα, εμφανίζονται μόνο ιδιότητες που ικανοποιοÏν τη συνθήκη.",
+ "smw-special-property-searchform": "ΠαÏουσίαση ιδιοτήτων που πεÏιέχουν:",
+ "smw-special-property-searchform-options": "Επιλογές",
+ "smw-special-wantedproperties-filter-label": "ΦίλτÏο:",
+ "smw-special-wantedproperties-filter-none": "Κανένα",
+ "smw-special-wantedproperties-filter-unapproved": "Μη εγκεκÏιμένο",
+ "concepts": "Έννοιες",
+ "smw-special-concept-docu": "Μια [https://www.semantic-mediawiki.org/wiki/Help:Concepts έννοια] μποÏεί να θεωÏηθεί ως «δυναμική κατηγοÏία», δηλαδή ως μία συλλογή σελίδων που δεν δημιουÏγήθηκαν με το χέÏι αλλά μέσω υπολογισμών από το Σημασιολογικό MediaWiki από την πεÏιγÏαφή δεδομένου εÏωτήματος.",
+ "smw-special-concept-header": "Κατάλογος εννοιών",
+ "smw-special-concept-count": "{{PLURAL:$1|ΠαÏατίθεται η ακόλουθη έννοια|ΠαÏατίθενται οι ακόλουθες $1 έννοιες}}.",
+ "smw-special-concept-empty": "Δεν βÏέθηκε κάποια έννοια.",
+ "unusedproperties": "ΑχÏησιμοποίητες ιδιότητες",
+ "smw-unusedproperties-docu": "Αυτή η σελίδα παÏουσιάζει μια λίστα με [https://www.semantic-mediawiki.org/wiki/Unused_properties αχÏησιμοποίητες ιδιότητες] που ενώ έχουν οÏιστεί, δεν χÏησιμοποιοÏνται σε κάποια σελίδα. ΜποÏείτε σε αντιπαÏαβολή να δείτε την ειδική σελίδα με [[Special:Properties|όλες τις ιδιότητες]] ή αυτήν με τις [[Special:WantedProperties|ζητοÏμενες ιδιότητες]].",
+ "smw-unusedproperty-template": "$1 Ï„Ïπου $2",
+ "wantedproperties": "Επιθυμητές ιδιότητες",
+ "smw-wantedproperties-docu": "Αυτή η σελίδα παÏουσιάζει μια λίστα με [https://www.semantic-mediawiki.org/wiki/Wanted_properties ζητοÏμενες ιδιότητες] που ενώ χÏησιμοποιοÏνται στο wiki, δεν έχουν σελίδα που να τις οÏίζει. ΜποÏείτε σε αντιπαÏαβολή να δείτε την ειδική σελίδα με [[Special:Properties|όλες τις ιδιότητες]] ή αυτήν με τις [[Special:UnusedProperties|αχÏησιμοποίητες ιδιότητες]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|χÏήση|χÏήσεις}})",
+ "smw_purge": "Ανανέωση",
+ "smw-purge-failed": "Η ανανέωση απέτυχε",
+ "types": "ΤÏποι",
+ "smw_types_docu": "Λίστα [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes διαθέσιμων Ï„Ïπων δεδομένων] με κάθε [https://www.semantic-mediawiki.org/wiki/Help:Datatype Ï„Ïπο] να αντιπÏοσωπεÏει ένα μοναδικό σÏνολο χαÏακτηÏιστικών για να πεÏιγÏάψει μια τιμή από πλευÏάς χαÏακτηÏιστικών αποθήκευσης και εμφάνισης που είναι κληÏονομικά ως Ï€Ïος κάποια ανατεθειμένη ιδιότητα.",
+ "smw-special-types-no-such-type": "Ο καθοÏισμένος Ï„Ïπος δεδομένων δεν υπάÏχει.",
+ "smw-statistics": "Σημασιολογικά στατιστικά",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Τιμή ιδιότητας|Τιμές ιδιοτήτων}} (συνολικά)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Ιδιότητα|Ιδιότητες}}]] (συνολικά)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Ιδιότητα|Ιδιότητες}} (συνολικά)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Ιδιότητα (που χÏησιμοποιείται με τουλάχιστον μία τιμή)|Ιδιότητες (που χÏησιμοποιοÏνται με τουλάχιστον μία τιμή)}}]]",
+ "smw-statistics-property-page": "{{PLURAL:$1|Ιδιότητα (καταχωÏισμένη σε σελίδα)|Ιδιότητες (καταχωÏισμένες σε σελίδες)}}",
+ "smw-statistics-property-type": "{{PLURAL:$1|Ιδιότητα (εκχωÏισμένη σε Ï„Ïπο δεδομένων)|Ιδιότητες (εκχωÏισμένες σε Ï„Ïπους δεδομένων)}}",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|ΕÏώτημα|ΕÏωτήματα}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|ΕÏώτημα|ΕÏωτήματα}}]]",
+ "smw-statistics-query-size": "Μέγεθος εÏωτήματος",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Έννοια|Έννοιες}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Έννοια|Έννοιες}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Υποαντικείμενο|Υποαντικείμενα}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Υποαντικείμενο|Υποαντικείμενα}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|ΤÏπος δεδομένων|ΤÏποι δεδομένων}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Τιμή ιδιότητας|Τιμές ιδιοτήτων}} ([[Special:ProcessingErrorList|{{PLURAL:$1|ακατάλληλη σημειογÏαφία|ακατάλληλες σημειογÏαφίες}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Τιμή ιδιότητας|Τιμές ιδιοτήτων}} ({{PLURAL:$1|ακατάλληλη σημειογÏαφία|ακατάλληλες σημειογÏαφίες}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|ΠαÏωχημένη οντότητα (σημειωμένη για αφαίÏεση)|ΠαÏωχημένες οντότητες (σημειωμένες για αφαίÏεση)}}",
+ "smw_uri_doc": "Ο αναλυτής URI υλοποιεί το [$1 εÏÏημα της Ομάδας Τεχνικής ΑÏχιτεκτονικής του W3C σχετικά με το θέμα «httpRange-14»].\nΟυσιαστικά φÏοντίζει οι άνθÏωποι να μην μεταμοÏφωθοÏν σε ιστοσελίδες.",
+ "ask": "Σημασιολογική αναζήτηση",
+ "smw_ask_sortby": "Ταξινόμηση κατά στήλη (Ï€ÏοαιÏετικό)",
+ "smw_ask_ascorder": "ΑÏξουσα",
+ "smw_ask_descorder": "Φθίνουσα",
+ "smw-ask-order-rand": "Τυχαία",
+ "smw_ask_submit": "ΕÏÏεση αποτελεσμάτων",
+ "smw_ask_editquery": "ΕπεξεÏγασία εÏωτήματος",
+ "smw_add_sortcondition": "[ΠÏοσθήκη συνθήκης ταξινόμησης]",
+ "smw-ask-sort-add-action": "ΠÏοσθήκη συνθήκης ταξινόμησης",
+ "smw_ask_hidequery": "ΑπόκÏυψη εÏωτήματος",
+ "smw_ask_help": "Βοήθεια για τα εÏωτήματα",
+ "smw_ask_queryhead": "Συνθήκη",
+ "smw_ask_printhead": "Επιλογή ιδιοτήτων Ï€Ïος εμφάνιση",
+ "smw_ask_printdesc": "(βάλτε μια ιδιότητα ανά γÏαμμή)",
+ "smw_ask_format_as": "ΜοÏφοποίηση ως:",
+ "smw_ask_defaultformat": "Ï€Ïοεπιλογή",
+ "smw_ask_otheroptions": "Άλλες επιλογές",
+ "smw-ask-otheroptions-info": "Αυτή η ενότητα πεÏιέχει επιλογές που μεταβάλλουν καταστάσεις εκτυπώσεων. ΜποÏείτε να δείτε τις πεÏιγÏαφές παÏαμέτÏων πεÏνώντας το ποντίκι από πάνω τους.",
+ "smw-ask-otheroptions-collapsed-info": "ΠαÏακαλοÏμε χÏησιμοποιήστε το εικονίδιο με το σÏμβολο «συν» για να δείτε όλες τις διαθέσιμες επιλογές",
+ "smw_ask_show_embed": "Εμφάνιση ενσωματωμένου κώδικα",
+ "smw_ask_hide_embed": "ΑπόκÏυψη ενσωματωμένου κώδικα",
+ "smw_ask_embed_instr": "Για την ενσωμάτωση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… εÏωτήματος σε μια σελίδα wiki, χÏησιμοποιήστε τον παÏακάτω κώδικα.",
+ "smw-ask-delete": "ΔιαγÏαφή",
+ "smw-ask-sorting": "Ταξινόμηση",
+ "smw-ask-options": "Επιλογές",
+ "smw-ask-options-sort": "Επιλογές ταξινόμησης",
+ "smw-ask-format-options": "ΜοÏφή και επιλογές",
+ "smw-ask-parameters": "ΠαÏάμετÏοι",
+ "smw-ask-search": "Αναζήτηση",
+ "smw-ask-debug": "Εκσφαλμάτωση",
+ "smw-ask-result": "Αποτέλεσμα",
+ "smw-ask-format": "ΜοÏφοποίηση",
+ "smw-ask-format-selection-help": "Βοήθεια για την επιλεγμένη μοÏφή: $1",
+ "smw-ask-input-assistance": "Βοήθεια κατά την εισαγωγή",
+ "smw-ask-query-search-info": "Το εÏώτημα <code><nowiki>$1</nowiki></code> απαντήθηκε από το {{PLURAL:$3|1=<code>$2</code> (από Ï€ÏοσωÏινή μνήμη)|<code>$2</code> (από Ï€ÏοσωÏινή μνήμη)|<code>$2</code>}} σε $4 {{PLURAL:$4|δευτεÏόλεπτο|δευτεÏόλεπτα}}.",
+ "searchbyproperty": "Αναζήτηση κατά ιδιότητα",
+ "processingerrorlist": "Κατάλογος σφαλμάτων επεξεÏγασίας",
+ "smw-processingerrorlist-intro": "Η ακόλουθη λίστα παÏέχει μια επισκόπηση σχετικά με τα σφάλματα επεξεÏγασίας που εμφανίστηκαν σε σχέση με το [https://www.semantic-mediawiki.org/ Σημασιολογικό MediaWiki]. Συνιστάται η παÏακολοÏθηση αυτής της λίστας σε τακτική βάση και η διόÏθωση μη έγκυÏης σημειογÏαφίας τιμών.",
+ "smw_sbv_docu": "Αναζήτηση για όλες τις σελίδες που έχουν μια δεδομένη ιδιότητα και τιμή.",
+ "smw_sbv_novalue": "Εισαγάγετε μια έγκυÏη τιμή για την ιδιότητα, ή Ï€Ïοβάλετε όλες τις τιμές ιδιοτήτων για «$1».",
+ "smw_sbv_displayresult": "Μια λίστα όλων των σελίδων που έχουν την ιδιότητα «$1» με τιμή «$2»",
+ "smw_sbv_displayresultfuzzy": "Λίστα με όλες τις σελίδες που έχουν ιδιότητα «$1» με τιμή «$2».\nΔεδομένου ότι υπήÏξαν μόνο λίγα αποτελέσματα, εμφανίζονται επίσης κάποιες γειτονικές τιμές.",
+ "smw_sbv_property": "Ιδιότητα:",
+ "smw_sbv_value": "Τιμή:",
+ "smw_sbv_submit": "ΕÏÏεση αποτελεσμάτων",
+ "browse": "ΠεÏιήγηση στο wiki",
+ "smw_browselink": "ΠεÏιήγηση στις ιδιότητες",
+ "smw_browse_article": "Εισαγάγετε το όνομα της σελίδας από την οποία θέλετε να ξεκινήσετε την πεÏιήγηση.",
+ "smw_browse_go": "Μετάβαση",
+ "smw_browse_show_incoming": "Εμφάνιση ιδιοτήτων που συνδέουν εδώ",
+ "smw_browse_hide_incoming": "ΑπόκÏυψη ιδιοτήτων που συνδέουν εδώ",
+ "smw_browse_no_outgoing": "Αυτή η σελίδα δεν έχει ιδιότητες.",
+ "smw_browse_no_incoming": "Δεν υπάÏχει ιδιότητα που να έχει σÏνδεσμο Ï€Ïος αυτήν τη σελίδα.",
+ "smw-browse-from-backend": "Οι πληÏοφοÏίες ανακτώνται αυτήν τη στιγμή από τη βάση δεδομένων.",
+ "smw-browse-js-disabled": "ΥπάÏχει η υποψία ότι το Javascript είναι απενεÏγοποιημένο ή μη διαθέσιμο και σας συνιστοÏμε να χÏησιμοποιήσετε ένα Ï€ÏόγÏαμμα πεÏιήγησης που να υποστηÏίζεται. Άλλες επιλογές αναλÏονται στη σελίδα Ïυθμίσεων της παÏαμέτÏου [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi $smwgBrowseByApi].",
+ "smw-browse-show-group": "Εμφάνιση ομάδων",
+ "smw-browse-hide-group": "ΑπόκÏυψη ομάδων",
+ "smw_inverse_label_default": "$1 από",
+ "smw_inverse_label_property": "Ετικέτα αντίστÏοφης ιδιότητας",
+ "pageproperty": "Αναζήτηση ιδιότητας σελίδας",
+ "smw_pp_docu": "Εισαγάγετε ή μια σελίδα και ιδιότητα, ή απλά μια ιδιότητα για να ανακτήσετε όλες τις ανατεθειμένες τιμές.",
+ "smw_pp_from": "Από τη σελίδα:",
+ "smw_pp_type": "Ιδιότητα:",
+ "smw_pp_submit": "ΕÏÏεση αποτελεσμάτων",
+ "smw_result_prev": "ΠÏοηγοÏμενο",
+ "smw_result_next": "Επόμενο",
+ "smw_result_results": "Αποτελέσματα",
+ "smw_result_noresults": "ΛυποÏμαστε, δεν υπάÏχουν αποτελέσματα.",
+ "smwadmin": "ΔιαχειÏιστικές λειτουÏγίες",
+ "smw-admin-statistics-job-title": "Στατιστικά εÏγασιών",
+ "smw-admin-statistics-job-docu": "Τα στατιστικά εÏγασιών παÏουσιάζουν πληÏοφοÏίες σχετικά με Ï€ÏογÏαμματισμένες εÏγασίες του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki που δεν έχουν ακόμη εκτελεστεί. Ο αÏιθμός των εÏγασιών μποÏεί να είναι ελαφÏÏŽÏ‚ ανακÏιβής ή να πεÏιέχει αποτυχημένες Ï€Ïοσπάθειες, παÏακαλείσθε να συμβουλευτείτε το [https://www.mediawiki.org/wiki/Manual:Job_queue εγχειÏίδιο χÏήσης] για πεÏαιτέÏω πληÏοφοÏίες.",
+ "smw-admin-statistics-querycache-title": "Στατιστικά στοιχεία Ï€ÏοσωÏινής μνήμης εÏωτημάτων",
+ "smw-admin-statistics-querycache-disabled": "Η [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] (Ï€ÏοσωÏινή μνήμη εÏωτημάτων) δεν έχει ενεÏγοποιηθεί σε αυτό το wiki και δεν υπάÏχουν εδώ διαθέσιμα στατιστικά στοιχεία.",
+ "smw-admin-setupsuccess": "Η μηχανή αποθήκευσης αναδομήθηκε.",
+ "smw_smwadmin_return": "ΕπιστÏοφή στην $1",
+ "smw_smwadmin_updatestarted": "Ξεκίνησε μια νέα διαδικασία ενημέÏωσης για την ανανέωση των σημασιολογικών δεδομένων.\nΌλα τα αποθηκευμένα δεδομένα θα αναδομηθοÏν ή θα επιδιοÏθωθοÏν όπου χÏειάζεται.\nΜποÏείτε να παÏακολουθείτε την Ï€Ïόοδο της ενημέÏωσης από αυτήν την ειδική σελίδα.",
+ "smw_smwadmin_updatenotstarted": "ΥπάÏχει ήδη μια διαδικασία ενημέÏωσης σε εκτέλεση.\nΔεν Ï€Ïόκειται να δημιουÏγηθεί άλλη.",
+ "smw_smwadmin_updatestopped": "Όλες οι υπάÏχουσες διαδικασίες ενημέÏωσης έχουν σταματήσει.",
+ "smw_smwadmin_updatenotstopped": "Για να διακόψετε τη εκτελοÏμενη διεÏγασία ενημέÏωσης, Ï€Ïέπει να ενεÏγοποιήσετε το πλαίσιο ελέγχου για να δηλώσετε ότι είστε Ï€Ïαγματικά βέβαιοι.",
+ "smw-admin-docu": "Αυτή η ειδική σελίδα σας βοηθά κατά τη διάÏκεια της εγκατάστασης, αναβάθμισης, συντήÏησης και χÏήσης του <a href=\"https://www.semantic-mediawiki.org\">Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki</a> και επίσης παÏέχει πεÏαιτέÏω διαχειÏιστικές λειτουÏγίες και εÏγασίες καθώς επίσης και στατιστικά στοιχεία.\nÎα θυμάστε να παίÏνετε αντίγÏαφα ασφαλείας όλων των σημαντικών δεδομένων Ï€Ïιν από την εκτέλεση διαχειÏιστικών λειτουÏγιών.",
+ "smw-admin-db": "ΣυντήÏηση βάσεως δεδομένων",
+ "smw-admin-dbdocu": "Το Σημασιολογικό MediaWiki απαιτεί να γίνουν οÏισμένες επεκτάσεις στη βάση δεδομένων του MediaWiki για την αποθήκευση σημασιολογικών δεδομένων.\nΗ παÏακάτω λειτουÏγία εξασφαλίζει ότι η βάση δεδομένων σας θα στηθεί σωστά.\nΟι αλλαγές που γίνονται σε αυτό το βήμα δεν επηÏεάζουν την υπόλοιπη βάση δεδομένων του MediaWiki και μποÏοÏν εÏκολα να αναιÏεθοÏν εφόσον επιθυμείτε.\nΑυτή η λειτουÏγία μποÏεί να εκτελεσθεί πολλές φοÏές χωÏίς να Ï€Ïοκαλεί ζημιά, αλλά είναι αναγκαία μόνο μία φοÏά κατά την εγκατάσταση ή αναβάθμιση.",
+ "smw-admin-permissionswarn": "Εάν η επέμβαση αποτυγχάνει αναφέÏοντας σφάλματα SQL, ο χÏήστης της βάσης δεδομένων τον οποίο χÏησιμοποιεί το wiki σας (ελέγξτε το αÏχείο σας «LocalSettings.php») μάλλον δεν έχει επαÏκή δικαιώματα.\nΉ χοÏηγήστε σε αυτόν το χÏήστη επιπλέον δικαιώματα να δημιουÏγεί και να διαγÏάφει πίνακες, ή εισαγάγετε Ï€ÏοσωÏινά τα στοιχεία του διαχειÏιστή της βάσης δεδομένων σας στο αÏχείο «LocalSettings.php», ή χÏησιμοποιήστε τη δέσμη ενεÏγειών συντήÏησης <code>setupStore.php</code>, η οποία μποÏεί να χÏησιμοποιήσει διαπιστευτήÏια διαχειÏιστή.",
+ "smw-admin-dbbutton": "ΑÏχικοποίηση ή αναβάθμιση πινάκων",
+ "smw-admin-announce": "Αναγγελία του wiki σας",
+ "smw-admin-announce-text": "Αν το wiki σας είναι δημόσιο, μποÏείτε να το καταχωÏίσετε στο <a href=\"https://wikiapiary.com\">WikiApiary</a>, το wiki που παÏακολουθεί άλλα wiki.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> is deprecated and will be removed in $2",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> was removed in $2",
+ "smw-admin-deprecation-notice-title-notice": "ΕπεÏχόμενες αλλαγές",
+ "smw-smwadmin-refresh-title": "Επισκευή και ενημέÏωση δεδομένων",
+ "smw_smwadmin_datarefresh": "Αναδόμηση δεδομένων",
+ "smw_smwadmin_datarefreshdocu": "Είναι δυνατό να επαναφέÏετε όλα τα δεδομένα του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki βασιζόμενοι στα Ï„Ïέχοντα πεÏιεχόμενα του wiki.\nΑυτό μποÏεί να είναι χÏήσιμο στην πεÏίπτωση της επιδιόÏθωσης κατεστÏαμμένων δεδομένων ή για την ανανέωση των δεδομένων εάν έχει αλλάξει η εσωτεÏική τους μοÏφή λόγω κάποιας αναβάθμισης λογισμικοÏ.\nΗ ενημέÏωση εκτελείται σελίδα σελίδα και δεν θα ολοκληÏωθεί άμεσα.\nΤο ακόλουθο δείχνει εάν μια ενημέÏωση βÏίσκεται σε εξέλιξη και σας επιτÏέπει να ξεκινήσετε ή να διακόψετε ενημεÏώσεις (εκτός εάν αυτή η δυνατότητα έχει απενεÏγοποιηθεί από το διαχειÏιστή του ιστοχώÏου).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Η ενημέÏωση είναι ήδη σε εξέλιξη.</strong>\nΕίναι φυσικό η ενημέÏωση να εξελίσσεται αÏγά δεδομένου ότι ανανεώνει τα δεδομένα σε μικÏά κομμάτια και μόνο κάθε φοÏά που ένας χÏήστης έχει Ï€Ïόσβαση σε μια οποιαδήποτε σελίδα του wiki.\nΓια να ολοκληÏώσετε αυτήν την ενημέÏωση πιο γÏήγοÏα, μποÏείτε να εκτελέσετε τη δέσμη ενεÏγειών συντήÏησης <code>runJobs.php</code> του MediaWiki (χÏησιμοποιήστε την επιλογή <code>--maxjobs 1000</code> για να πεÏιοÏίσετε τον αÏιθμό των ενημεÏώσεων που θα γίνουν σε μία δέσμη ενεÏγειών).\nΕκτιμώμενη Ï€Ïόοδος της Ï„Ïέχουσας ενημέÏωσης:",
+ "smw_smwadmin_datarefreshbutton": "ΠÏογÏαμματισμός αναδόμησης δεδομένων",
+ "smw_smwadmin_datarefreshstop": "Σταμάτημα αυτής της ενημέÏωσης",
+ "smw_smwadmin_datarefreshstopconfirm": "Îαι, είμαι {{GENDER:$1|σίγουÏος|σίγουÏη}}.",
+ "smw-admin-job-scheduler-note": "Οι πεÏισσότεÏες δÏαστηÏιότητες σε αυτήν την ενότητα εκτελοÏνται ως εÏγασίες για την αποφυγή καταστάσεων αδιεξόδου κατά την εκτέλεση τους. Η [https://www.mediawiki.org/wiki/Manual:Job_queue λειτουÏγία χÏονοπÏογÏÎ±Î¼Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï ÎµÏγασιών] είναι υπεÏθυνη για την επεξεÏγασία και είναι καίÏιας σημασίας η δέσμη ενεÏγειών συντήÏησης <code>runJobs.php</code> (βλ. και παÏάμετÏο διαμόÏφωσης <code>$wgRunJobsAsync</code>) να έχει κατάλληλη χωÏητικότητα.",
+ "smw-admin-outdateddisposal-title": "ΑπόÏÏιψη παÏωχημένων οντοτήτων",
+ "smw-admin-outdateddisposal-intro": "ΟÏισμένες δÏαστηÏιότητες (κάποια αλλαγή στον Ï„Ïπο μιας ιδιότητας, διαγÏαφή σελίδων wiki ή διόÏθωση τιμών σφάλματος) θα έχουν ως αποτέλεσμα [https://www.semantic-mediawiki.org/wiki/Outdated_entities παÏωχημένες οντότητες] και Ï€Ïοτείνεται η πεÏιοδική διαγÏαφή τους για να απελευθεÏώνεται ο χώÏος που καταλαμβάνουν στους σχετικοÏÏ‚ πίνακες.",
+ "smw-admin-outdateddisposal-active": "Η εÏγασία απόÏÏιψης παÏωχημένων οντοτήτων έχει Ï€ÏογÏαμματιστεί.",
+ "smw-admin-outdateddisposal-button": "ΈναÏξη Ï€ÏογÏαμματισμένης απόÏÏιψης",
+ "smw-admin-feature-disabled": "Αυτή η δυνατότητα έχει απενεÏγοποιηθεί σε αυτό το wiki, παÏακαλοÏμε να συμβουλευτείτε τη σελίδα βοήθειας πεÏί <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">Ïυθμίσεων</a> ή επικοινωνήστε με το διαχειÏιστή του συστήματος.",
+ "smw-admin-propertystatistics-title": "Αναδόμηση στατιστικών στοιχείων ιδιοτήτων",
+ "smw-admin-propertystatistics-intro": "Αναδομεί το σÏνολο των στατιστικών στοιχείων χÏήσης των ιδιοτήτων και επ' Î±Ï…Ï„Î¿Ï ÎµÎ½Î·Î¼ÎµÏώνει και διοÏθώνει το [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count πλήθος χÏήσεων] κάθε ιδιότητας.",
+ "smw-admin-propertystatistics-active": "Η εÏγασία αναδόμησης των στατιστικών έχει Ï€ÏογÏαμματιστεί.",
+ "smw-admin-propertystatistics-button": "ΈναÏξης Ï€ÏογÏαμματισμένης αναδόμησης στατιστικών στοιχείων",
+ "smw-admin-fulltext-title": "Αναδόμηση αναζήτησης πλήÏους κειμένου",
+ "smw-admin-fulltext-intro": "Αναδομεί το ευÏετήÏιο αναζήτησης από πίνακες ιδιοτήτων με ενεÏγοποιημένο Ï„Ïπο δεδομένων [https://www.semantic-mediawiki.org/wiki/Full-text αναζήτησης πλήÏους κειμένου]. Αλλαγές στους κανόνες του ευÏετηÏίου (Ï„Ïοποποιημένες Ï€Ïοειδοποιητικές λέξεις κλειδιά, νέο στέλεχος κλπ.) ή/και σε έναν Ï€Ïόσφατα Ï€Ïοστεθέντα ή Ï„Ïοποποιημένο πίνακα απαιτοÏν εκ νέου την εκτέλεση αυτής της εÏγασίας.",
+ "smw-admin-fulltext-active": "Η εÏγασία αναδόμησης της αναζήτησης πλήÏους κειμένου έχει Ï€ÏογÏαμματιστεί.",
+ "smw-admin-fulltext-button": "ΈναÏξης Ï€ÏογÏαμματισμένης αναδόμησης πλήÏους κειμένου",
+ "smw-admin-support": "ΠαÏοχή υποστήÏιξης",
+ "smw-admin-supportdocu": "ΔιάφοÏοι πόÏοι παÏέχονται για να σας βοηθήσουν σε πεÏίπτωση Ï€Ïοβλημάτων:",
+ "smw-admin-installfile": "Εάν αντιμετωπίζετε Ï€Ïοβλήματα με την εγκατάσταση, ξεκινήστε Ïίχνοντας μια ματιά στις κατευθυντήÏιες γÏαμμές στο <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">αÏχείο με τίτλο INSTALL</a> και στη <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">σελίδα εγκατάστασης</a>.",
+ "smw-admin-smwhomepage": "Η πλήÏης τεκμηÏίωση χÏήσης του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki βÏίσκεται στο <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "ΠÏογÏαμματιστικά σφάλματα μποÏοÏν να αναφεÏθοÏν στη <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">σελίδα παÏακολοÏθησης Ï€Ïοβλημάτων</a>, η σελίδα <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">αναφοÏάς Ï€ÏογÏαμματιστικών σφαλμάτων</a> παÏέχει κάποιες οδηγίες για το πώς να γÏάψετε μια αποτελεσματική αναφοÏά για κάποιο Ï€Ïόβλημα.",
+ "smw-admin-questions": "Εάν έχετε πεÏαιτέÏω εÏωτήσεις ή Ï€Ïοτάσεις, λάβετε μέÏος στη συζήτηση στη <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">λίστα αλληλογÏαφίας χÏηστών</a> ή στο <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">χώÏο συνομιλίας</a> του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki.",
+ "smw-admin-other-functions": "Άλλες λειτουÏγείες",
+ "smw-admin-supplementary-section-title": "ΣυμπληÏωματικές λειτουÏγίες",
+ "smw-admin-supplementary-section-subtitle": "Διαθέσιμες λειτουÏγίες",
+ "smw-admin-supplementary-section-intro": "ΜεÏικές από τις λειτουÏγίες που αναφέÏονται σε αυτήν την ενότητα μποÏεί να υπόκεινται σε πεÏιοÏισμοÏÏ‚ και ως εκ τοÏτου να είναι μη Ï€Ïοσβάσιμες σε αυτό το wiki.",
+ "smw-admin-supplementary-settings-title": "Ρυθμίσεις διαμόÏφωσης",
+ "smw-admin-supplementary-settings-intro": "Οι <u>$1</u> εμφανίζουν μια συνολική λίστα με τις διαθέσιμες Ïυθμίσεις που χÏησιμοποιοÏνται στο Σημασιολογικό MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Στατιστικά λειτουÏγίας",
+ "smw-admin-supplementary-operational-statistics-intro": "Τα <u>$1</u> παÏουσιάζουν ένα εκτεταμένο σÏνολο στατιστικών",
+ "smw-admin-supplementary-idlookup-title": "Αναζήτηση και απόÏÏιψη οντότητας",
+ "smw-admin-supplementary-idlookup-intro": "Η <u>$1</u> πεÏιέχει λειτουÏγίες για αναζήτηση και απόÏÏιψη μεμονωμένων οντοτήτων.",
+ "smw-admin-supplementary-duplookup-title": "Διπλότυπες οντότητες",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> για να φτιάξετε λίστα εγγÏαφών που είναι ταξινομημένες καθώς και να πεÏιλάβετε διπλές εγγÏαφές στον πίνακα ενοτήτων",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Στατιστικά Ï€ÏοσωÏινής έκδοσης",
+ "smw-admin-supplementary-elastic-functions": "Διαθέσιμες λειτουÏγίες",
+ "smw-admin-supplementary-elastic-settings-title": "Ρυθμίσεις",
+ "smw-admin-supplementary-elastic-mappings-summary": "ΠεÏίληψη",
+ "smw-admin-supplementary-elastic-mappings-fields": "ΧαÏτογÏαφήσεις πεδίου",
+ "smw-admin-supplementary-elastic-nodes-title": "Κόμβοι",
+ "smw-admin-supplementary-elastic-statistics-title": "Στατιστικά",
+ "smw-property-label-similarity-threshold": "Κατώφλιο:",
+ "smw-property-label-similarity-noresult": "Δεν βÏέθηκαν αποτελέσματα για τις συγκεκÏιμένες επιλογές.",
+ "smw-admin-operational-statistics": "Αυτή η σελίδα πεÏιέχει στατιστικά στοιχεία λειτουÏγίας που συλλέγονται σε ή από συναφείς λειτουÏγίες του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki. Μια εκτεταμένη λίστα στατιστικών στοιχείων σχετικά με το wiki μποÏεί να βÏεθεί [[Special:Statistics|<b>εδώ</b>]].",
+ "smw_adminlinks_datastructure": "Δομή δεδομένων",
+ "smw_adminlinks_displayingdata": "ΠαÏουσίαση δεδομένων",
+ "smw_adminlinks_inlinequerieshelp": "Βοήθεια σχετικά με τα εÏωτήματα εντός κειμένου",
+ "smw-page-indicator-usage-count": "Εκτιμώμενο [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count πλήθος χÏήσεων]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Ιδιότητα οÏισμένη από {{PLURAL:$1|χÏήστη|το σÏστημα}}",
+ "smw-createproperty-isproperty": "Αυτή είναι ιδιότητα Ï„Ïπου $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Η επιτÏεπτή τιμή|Οι επιτÏεπτές τιμές}} για αυτήν την ιδιότητα είναι:",
+ "smw-paramdesc-category-delim": "Το διαχωÏιστικό",
+ "smw-paramdesc-category-template": "Ένα Ï€Ïότυπο για να μοÏφοποιήσετε με αυτό τα στοιχεία",
+ "smw-paramdesc-category-userparam": "ΠαÏάμετÏος για να την πεÏάσετε στο Ï€Ïότυπο",
+ "smw-info-par-message": "Μήνυμα Ï€Ïος εμφάνιση.",
+ "smw-info-par-icon": "Εικονίδιο για εμφάνιση είτε «πληÏοφοÏιών» είτε «πÏοειδοποίησης».",
+ "prefs-smw": "Σημασιολογικό MediaWiki",
+ "prefs-general-options": "Γενικές επιλογές",
+ "prefs-ask-options": "Ειδικό:Επιλογές σημασιολογικής αναζήτησης",
+ "smw-prefs-intro-text": "Το [https://www.semantic-mediawiki.org/ Σημασιολογικό MediaWiki] (και οι σχετικές επεκτάσεις) παÏέχουν εξατομικευμένη παÏαμετÏοποίηση για κάποιες επιλεγμένες λειτουÏγίες. ΠαÏακαλοÏμε συμβουλευτείτε τη [https://www.semantic-mediawiki.org/wiki/Help:User_preferences σελίδα βοηθείας] για λεπτομεÏή πεÏιγÏαφή.",
+ "smw-prefs-ask-options-tooltip-display": "Εμφάνιση κειμένου παÏαμέτÏου ως πληÏοφοÏίες στο δείκτη του ποντικιοÏ",
+ "smw-prefs-general-options-jobqueue-watchlist": "Δείξετε [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist job queue watchlist] στην Ï€Ïοσωπική μου σειÏά",
+ "smw-prefs-general-options-disable-editpage-info": "ΑπενεÏγοποίηση του ÎµÎ¹ÏƒÎ±Î³Ï‰Î³Î¹ÎºÎ¿Ï ÎºÎµÎ¹Î¼Î­Î½Î¿Ï… στη σελίδα επεξεÏγασίας",
+ "smw-prefs-general-options-suggester-textinput": "ΕνεÏγοποιήστε τη [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance βοήθεια κατά την εισαγωγή] για υποδείξεις σημασιολογικών οντοτήτων",
+ "smw-ui-tooltip-title-property": "Ιδιότητα",
+ "smw-ui-tooltip-title-quantity": "ΜετατÏοπή μονάδας",
+ "smw-ui-tooltip-title-info": "ΠληÏοφοÏίες",
+ "smw-ui-tooltip-title-service": "ΣÏνδεσμοι υπηÏεσίας",
+ "smw-ui-tooltip-title-warning": "ΠÏοειδοποίηση",
+ "smw-ui-tooltip-title-error": "Σφάλμα",
+ "smw-ui-tooltip-title-parameter": "ΠαÏάμετÏος",
+ "smw-ui-tooltip-title-event": "Συμβάν",
+ "smw-ui-tooltip-title-note": "Σημείωση",
+ "smw-ui-tooltip-title-legend": "Υπόμνημα",
+ "smw-ui-tooltip-title-reference": "ΑναφοÏά",
+ "smw_unknowntype": "Ο Ï„Ïπος αυτής της ιδιότητας δεν είναι έγκυÏος",
+ "smw-concept-cache-text": "Η έννοια διαθέτει συνολικά $1 {{PLURAL:$1|σελίδα|σελίδες}} και ενημεÏώθηκε τελευταία φοÏά στις $2, στις $3.",
+ "smw_concept_header": "Σελίδες της έννοιας «$1»",
+ "smw_conceptarticlecount": "ΠαÏακάτω {{PLURAL:$1|εμφανίζεται 1 σελίδα|εμφανίζονται $1 σελίδες}}.",
+ "smw-qp-empty-data": "Τα ζητοÏμενα δεδομένα δεν μποÏοÏν να Ï€ÏοβληθοÏν λόγω ανεπαÏκών κÏιτηÏίων επιλογής.",
+ "right-smw-admin": "ΕÏγασίες διαχείÏισης Ï€Ïόσβασης (Σημασιολογικό MediaWiki)",
+ "right-smw-patternedit": "ΠÏόσβαση επεξεÏγασίας για τη συντήÏηση των επιτÏεπτών κανονικών εκφÏάσεων και μοτίβων (Σημασιολογικό MediaWiki)",
+ "right-smw-ruleedit": "ΕπεξεÏγασία σελίδων κανόνων (Σημασιολογικό MediaWiki)",
+ "action-smw-patternedit": "επεξεÏγαστείτε κανονικές εκφÏάσεις που χÏησιμοποιοÏνται από το Σημασιολογικό MediaWiki",
+ "group-smwadministrator": "ΔιαχειÏιστές (Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|διαχειÏιστής|διαχειÏίστÏια}} (Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:ΔιαχειÏιστές (Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki)",
+ "group-smwcurator": "Επιμελητές (Σημασιολογικό MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|επιμελητής (Σημασιολογικό MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Επιμελητές (Σημασιολογικό MediaWiki)",
+ "action-smw-admin": "μεταβείτε στις διαχειÏιστικές εÏγασίες του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki",
+ "action-smw-ruleedit": "επεξεÏγασία σελίδων κανόνων (Semantic MediaWiki)",
+ "smw-property-predefined-default": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα.",
+ "smw-property-predefined-common": "Αυτή η ιδιότητα είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα (γνωστή επίσης ως [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ειδική ιδιότητα]) και συνοδεÏεται από επιπλέον διαχειÏιστικά Ï€Ïονόμια αλλά μποÏεί να χÏησιμοποιηθεί ακÏιβώς όπως οποιαδήποτε άλλη [https://www.semantic-mediawiki.org/wiki/Property ιδιότητα που οÏίζεται από χÏήστη].",
+ "smw-property-predefined-ask": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που αναπαÏιστά μεταπληÏοφοÏία (με τη μοÏφή [https://www.semantic-mediawiki.org/wiki/Subobject υποαντικειμένου]) σχετικά με μεμονωμένα εÏωτήματα και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-asksi": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία συλλέγει τον αÏιθμό των συνθηκών που χÏησιμοποιοÏνται σε ένα εÏώτημα και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-askde": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία παÏέχει πληÏοφοÏίες σχετικά με το βάθος ενός εÏωτήματος και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-long-askde": "Είναι μια αÏιθμητική τιμή που υπολογίζεται με βάση την εμφώλευση υποεÏωτημάτων, τις αλυσίδες ιδιοτήτων και τα διαθέσιμα στοιχεία πεÏιγÏαφής με την εκτέλεση εÏωτήματος που πεÏιοÏίζεται από τη ÏÏθμιση <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-sp-properties-docu": "Αυτή η ειδική σελίδα παÏέχει μια λίστα με τις διαθέσιμες [https://www.semantic-mediawiki.org/wiki/Property ιδιότητες] και το πλήθος χÏήσεών τους για αυτό το wiki. Για πιο επικαιÏοποιημένα στατιστικά μετÏήσεων συνιστάται η εκτέλεση της δέσμης ενεÏγειών συντήÏησης των [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics στατιστικών στοιχείων των ιδιοτήτων] σε τακτική βάση. Για μια διαφοÏοποιημένη Ï€Ïοβολή, δείτε τις ειδικές σελίδες με τις [[Special:UnusedProperties|μη χÏησιμοποιοÏμενες]] ή τις [[Special:WantedProperties|ζητοÏμενες ιδιότητες]].",
+ "smw-sp-properties-cache-info": "Τα δεδομένα που παÏατίθενται έχουν ανακτηθεί από την [https://www.semantic-mediawiki.org/wiki/Caching Ï€ÏοσωÏινή μνήμη] και ενημεÏώθηκαν τελευταία φοÏά στις $1.",
+ "smw-sp-properties-header-label": "Κατάλογος ιδιοτήτων",
+ "smw-admin-settings-docu": "ΠαÏουσιάζει μια λίστα όλων των Ï€Ïοεπιλεγμένων και τοπικοποιημένων Ïυθμίσεων που αφοÏοÏν στο πεÏιβάλλον του Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki. Για λεπτομέÏειες σχετικά με συγκεκÏιμένες Ïυθμίσεις, συμβουλευθείτε τη σελίδα βοήθειας πεÏί [https://www.semantic-mediawiki.org/wiki/Help:Configuration διαμόÏφωσης].",
+ "smw-sp-admin-settings-button": "ΔημιουÏγία λίστας Ïυθμίσεων",
+ "smw-admin-idlookup-title": "Αναζήτηση αναγνωÏÎ¹ÏƒÏ„Î¹ÎºÎ¿Ï Î±Î½Ï„Î¹ÎºÎµÎ¹Î¼Î­Î½Î¿Ï…",
+ "smw-admin-idlookup-docu": "Αυτή η ενότητα εμφανίζει τεχνικές λεπτομέÏειες σχετικά με ένα αναγνωÏιστικό εσωτεÏÎ¹ÎºÎ¿Ï Î±Î½Ï„Î¹ÎºÎµÎ¹Î¼Î­Î½Î¿Ï… που αντιπÏοσωπεÏει μια μεμονωμένη οντότητα (σελίδα wiki, υποαντικείμενο, ιδιότητα κλπ.) στο Σημασιολογικό MediaWiki. Η είσοδος μποÏεί να είναι ένα αναγνωÏιστικό ή μια συμβολοσειÏά που να ταιÏιάζει με το πεδίο επιλογής. Îα σημειωθεί ότι οποιαδήποτε αναφοÏά σε αναγνωÏιστικό δεν Ï€Ïέπει να συγχέεται με σελίδα του MediaWiki ή αναγνωÏιστικό αναθεώÏησης.",
+ "smw-admin-iddispose-title": "ΑπόÏÏιψη",
+ "smw-admin-iddispose-done": "Το αναγνωÏιστικό «$1» έχει αφαιÏεθεί από τη βάση δεδομένων.",
+ "smw-admin-idlookup-input": "Αναζήτηση:",
+ "smw-admin-objectid": "ΑναγνωÏιστικό:",
+ "smw-admin-tab-general": "Επισκόπηση",
+ "smw-admin-tab-supplement": "ΣυμπληÏωματικές λειτουÏγίες",
+ "smw-admin-tab-registry": "ΜητÏώο",
+ "smw-livepreview-loading": "ΦόÏτωση σε εξέλιξη...",
+ "smw-sp-searchbyproperty-description": "Αυτή η σελίδα παÏέχει μια απλή [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces διεπαφή πεÏιήγησης] για την εÏÏεση οντοτήτων που πεÏιγÏάφονται από κάποια ιδιότητα και κάποια ονομαστική τιμή. Άλλες διαθέσιμες διεπαφές αναζήτησης πεÏιλαμβάνουν την [[Special:PageProperty|αναζήτηση ιδιοτήτων σελίδας]] και το [[Special:Ask|εÏγαλείο κατασκευής εÏωτημάτων]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Λίστα αποτελεσμάτων",
+ "smw-sp-searchbyproperty-nonvaluequery": "Μια λίστα τιμών που έχουν ανατεθειμένη την ιδιότητα «$1».",
+ "smw-sp-searchbyproperty-valuequery": "Κατάλογος σελίδων που έχουν την ιδιότητα «$1» με σημειωμένη τιμή «$2».",
+ "smw-datavalue-number-textnotallowed": "Το «$1» δεν μποÏεί να εκχωÏηθεί σε Ï„Ïπο που έχει δηλωθεί ως αÏιθμητικός με τιμή $2.",
+ "smw-datavalue-number-nullnotallowed": "Το «$1» επέστÏεψε «NULL», το οποίο δεν επιτÏέπεται ως αÏιθμός.",
+ "smw-editpage-annotation-enabled": "Αυτή η σελίδα υποστηÏίζει ενδοκειμενική σημασιολογική σημειογÏαφία (Ï€.χ. <nowiki>«[[ΟÏίζεται ως::Μνημείο παγκόσμιας πολιτιστικής κληÏονομιάς]]»</nowiki>) για την κατασκευή δομημένου και ανακτήσιμου μέσω εÏωτημάτων πεÏιεχομένου που παÏέχεται από το Σημασιολογικό MediaWiki. Για μια ολοκληÏωμένη πεÏιγÏαφή για το πώς να χÏησιμοποιείτε αυτή τη σημειογÏαφία ή τη συνάÏτηση του ÏƒÏ…Î½Ï„Î±ÎºÏ„Î¹ÎºÎ¿Ï Î±Î½Î±Î»Ï…Ï„Î® #ask, παÏακαλοÏμε Ïίξτε μια ματιά στις σελίδες βοήθειας [https://www.semantic-mediawiki.org/wiki/Help:Getting_started ξεκινώντας], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation ενδοκειμενική σημειογÏαφία], ή [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries ενδοκειμενικά εÏωτήματα].<!--[[Is specified as::World Heritage Site]]-->",
+ "smw-editpage-annotation-disabled": "Σε αυτήν τη σελίδα δεν παÏέχεται η δυνατότητα ενδοκειμενικής σημασιολογικής σημειογÏαφίας λόγω πεÏιοÏισμών του ονοματοχώÏου. ΛεπτομέÏειες σχετικά με το πώς να ενεÏγοποιήσετε αυτήν τη δυνατότητα για τον ονοματοχώÏο μποÏοÏν να βÏεθοÏν στη σελίδα βοήθειας για τη [https://semantic-mediawiki.org/wiki/Help:Configuration ÏÏθμιση παÏαμέτÏων].",
+ "smw-editpage-property-annotation-enabled": "Αυτή η ιδιότητα μποÏεί να επεκταθεί χÏησιμοποιώντας σημασιολογική σημειογÏαφία για καθοÏισμό Ï„Ïπου δεδομένων (Ï€.χ. <nowiki>«[[Has type::Page]]»</nowiki>) ή άλλες υποστηÏικτικές δηλώσεις (Ï€.χ. <nowiki>«[[Subproperty of::dc:date]]»</nowiki>). Για πεÏιγÏαφή σχετικά με το πώς να εμπλουτίσετε αυτή τη σελίδα, δείτε τη [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration δήλωση μιας ιδιότητας] ή τη σελίδα βοήθειας με τη [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes λίστα διαθέσιμων Ï„Ïπων δεδομένων].",
+ "smw-editpage-property-annotation-disabled": "Αυτή η ιδιότητα δεν μποÏεί να επεκταθεί με σημειογÏαφία Ï„Ïπου δεδομένων (Ï€.χ. <nowiki>«[[Has type::Page]]»</nowiki>) καθώς είναι ήδη εκ των Ï€ÏοτέÏων οÏισμένη (βλ. σελίδα βοήθειας σχετικά με [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ειδικές ιδιότητες] για πεÏισσότεÏες πληÏοφοÏίες).",
+ "smw-editpage-concept-annotation-enabled": "Αυτή η έννοια μποÏεί να επεκταθεί χÏησιμοποιώντας τη συνάÏτηση του ÏƒÏ…Î½Ï„Î±ÎºÏ„Î¹ÎºÎ¿Ï Î±Î½Î±Î»Ï…Ï„Î® #concept. Για πεÏιγÏαφή σχετικά με το πώς να χÏησιμοποιήσετε την #concept, δείτε τη σελίδα βοήθειας για τις [https://www.semantic-mediawiki.org/wiki/Help:Concepts έννοιες].",
+ "smw-search-input": "Εισαγωγή και αναζήτηση",
+ "smw-search-syntax": "Επισήμανση",
+ "smw-search-profile-sort-best": "ΚαλÏτεÏη αντιστοιχία",
+ "smw-search-profile-sort-recent": "Πιο Ï€Ïόσφατο",
+ "smw-search-profile-sort-title": "Τίτλος",
+ "smw-search-profile-extended-help-query": "ΣÏνδεση Ï€Ïος: $1",
+ "smw-search-profile-extended-help-find-forms": "ΕÏÏεση μοÏφών",
+ "smw-search-profile-extended-section-sort": "Ταξινόμηση κατά",
+ "smw-search-profile-extended-section-form": "Επιλέξτε μοÏφή",
+ "smw-search-profile-extended-section-namespace": "Επιλογή ονοματοχώÏου",
+ "smw-search-profile-extended-section-query": "ΕÏώτημα",
+ "log-name-smw": "ΚαταγÏαφή συμβάτων Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki",
+ "log-show-hide-smw": "$1 μητÏώου Î£Î·Î¼Î±ÏƒÎ¹Î¿Î»Î¿Î³Î¹ÎºÎ¿Ï MediaWiki",
+ "log-description-smw": "ΔÏαστηÏιότητες για [https://www.semantic-mediawiki.org/wiki/Help:Logging ενεÏγοποιημένους Ï„Ïπους συμβάντων] που έχουν αναφεÏθεί από το Σημασιολογικό MediaWiki και τα συστατικά του.",
+ "logentry-smw-maintenance": "Γεγονότα σχετικά με συντήÏηση που Ï€ÏοέÏχονται από το Σημασιολογικό MediaWiki",
+ "smw-datavalue-import-unknown-namespace": "Ο ονοματοχώÏος εισαγωγής «$1» είναι άγνωστος. ΠαÏακαλοÏμε βεβαιωθείτε ότι οι πληÏοφοÏίες εισαγωγής OWL είναι διαθέσιμες μέσω του ονοματοχώÏου [[MediaWiki:Smw import $1]].",
+ "smw-datavalue-import-missing-namespace-uri": "Δεν ήταν δυνατόν να βÏεθεί URI του ονοματοχώÏου «$1» στην [[MediaWiki:Smw import $1|εισαγωγή $1]].",
+ "smw-datavalue-import-missing-type": "Δεν βÏέθηκε οÏισμός Ï„Ïπου του «$1» στην [[MediaWiki:Smw import $2|εισαγωγή $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|εισαγωγή $1]]",
+ "smw-datavalue-import-invalid-value": "Το «$1» δεν είναι έγκυÏη μοÏφή και αναμένεται να αποτελείται από «ονοματοχώÏο»:«αναγνωÏιστικό» (Ï€.χ. «foaf:name»).",
+ "smw-datavalue-import-invalid-format": "Η συμβολοσειÏά «$1» αναμένετο να ήταν χωÏισμένη σε τέσσεÏα μέÏη, αλλά η μοÏφή δεν έγινε κατανοητή.",
+ "smw-property-predefined-impo": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που πεÏιγÏάφει μια σχέση με ένα [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary εισηγμένο λεξιλόγιο] και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-type": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που πεÏιγÏάφει τον [[Special:Types|Ï„Ïπο δεδομένων]] μιας ιδιότητας και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-sobj": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που αντιπÏοσωπεÏει ένα κατασκεÏασμα σαν [https://www.semantic-mediawiki.org/wiki/Help:Container δοχείο] και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-errp": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα για την παÏακολοÏθηση σφαλμάτων εισόδου για παÏάτυπη σημειογÏαφία τιμών που πιθανώς Ï€Ïοκλήθηκε από πεÏιοÏισμοÏÏ‚ Ï„Ïπου ή [[Property:Allows value|επιτÏεπόμενης τιμής]] και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-long-errp": "Στις πεÏισσότεÏες πεÏιπτώσεις Ï€Ïοξενείται από έναν μη Ï€ÏοσαÏμοσμένο Ï„Ïπο ή πεÏιοÏισμό [[Property:Allows value|τιμής]].",
+ "smw-property-predefined-pval": "Η [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value\"$1\"] είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που οÏίζει μια λίστα από επιτÏεπτές τιμές για τον πεÏιοÏισμό των τιμών που μποÏεί να ανατίθενται σε μια ιδιότητα και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-datavalue-property-restricted-annotation-use": "Η ιδιότητα ''$1'' έχει πεÏιοÏισμένη πεÏιοχή εφαÏμογής και δεν μποÏεί να χÏησιμοποιηθεί ως ιδιότητα επισήμανσης από τον χÏήστη",
+ "smw-datavalue-property-restricted-declarative-use": "Η ιδιότητα ''$1'' είναι ιδιότητα που Ï€Ïέπει να δηλωθεί και μποÏεί να χÏησιμοποιηθεί μόνο σε μια σελίδα ιδιοτήτων ή κατηγοÏίας",
+ "smw-datavalue-restricted-use": "Η τιμή δεδομένων «$1» έχει σημανθεί για χÏήση με πεÏιοÏισμοÏÏ‚.",
+ "smw-datavalue-invalid-number": "Το «$1» δεν μποÏεί να εÏμηνευτεί ως αÏιθμός.",
+ "smw-query-condition-circular": "ΑνιχνεÏθηκε πιθανό Ï€Ïόβλημα κυκλικότητας στο «$1»",
+ "smw-types-list": "Λίστα Ï„Ïπων δεδομένων",
+ "smw-types-default": "Το «$1» είναι ενσωματωμένος Ï„Ïπος δεδομένων.",
+ "smw-types-help": "ΠεÏισσότεÏες πληÏοφοÏίες και παÏαδείγματα μποÏοÏν να βÏεθοÏν στη [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 σελίδα βοήθειας].",
+ "smw-type-anu": "Το «$1» είναι μια παÏαλλαγή του Ï„Ïπου δεδομένων [[Special:Types/URL|URL]] και χÏησιμοποιείται κυÏίως ως δηλωτικό εξαγωγής ενός «owl:AnnotationProperty».",
+ "smw-type-boo": "Το «$1» είναι Ï€Ïωτογενής Ï„Ïπος δεδομένων που πεÏιγÏάφει τιμή αληθές/ψευδές.",
+ "smw-type-cod": "Το «$1» είναι μια παÏαλλαγή του Ï„Ïπου δεδομένων [[Special:Types/Text|Text]] για χÏήση σε τεχνικά κείμενα αυθαίÏετου μήκους, όπως κείμενα πηγαίου κώδικα Ï€ÏογÏαμμάτων.",
+ "smw-type-geo": "Το «$1» είναι Ï„Ïπος δεδομένων που πεÏιγÏάφει γεωγÏαφικές τοποθεσίες και απαιτεί τους [https://www.semantic-mediawiki.org/wiki/Semantic_Maps ΣημασιολογικοÏÏ‚ ΧάÏτες].",
+ "smw-type-tel": "Το «$1» είναι ειδικός Ï„Ïπος δεδομένων που πεÏιγÏάφει διεθνείς τηλεφωνικοÏÏ‚ αÏιθμοÏÏ‚ σÏμφωνα με το RFC 3966.",
+ "smw-type-txt": "Το «$1» είναι Ï€Ïωτογενής Ï„Ïπος δεδομένων που πεÏιγÏάφει συμβολοσειÏές αυθαίÏετου μήκους.",
+ "smw-type-dat": "Το «$1» είναι Ï„Ïπος δεδομένων που αντιπÏοσωπεÏει σημεία στο χÏόνο σε μια ενιαία μοÏφή.",
+ "smw-type-tab-properties": "Ιδιότητες",
+ "smw-type-tab-types": "ΤÏπος",
+ "smw-type-tab-errors": "Σφάλματα",
+ "smw-type-primitive": "Βασικό",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-property-predefined-errc": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki] και αντιπÏοσωπεÏει σφάλματα που εμφανίστηκαν σε σχέση με σημειογÏαφίες ακατάλληλης τιμής ή επεξεÏγασία κάποιας εισόδου.",
+ "smw-property-predefined-errt": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιέχει κάποια λεκτική πεÏιγÏαφή σφάλματος και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-mdat": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία αντιστοιχεί στην ημεÏομηνία της τελευταίας Ï„Ïοποποίησης μιας σελίδας και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-cdat": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία αντιστοιχεί στην ημεÏομηνία της Ï€Ïώτης έκδοσης μιας σελίδας και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-newp": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία υποδηλώνει εάν μία σελίδα είναι νέα ή όχι και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-ledt": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιέχει το όνομα σελίδας του χÏήστη που δημιοÏÏγησε την τελευταία αναθεώÏηση και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-mime": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιγÏάφει τον Ï„Ïπο MIME κάποιου ανεβασμένου αÏχείου και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-media": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιγÏάφει τον Ï„Ïπο μέσου κάποιου ανεβασμένου αÏχείου και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-askfo": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία αποθηκεÏει το όνομα της μοÏφής αποτελεσμάτων που χÏησιμοποιείται σε κάποιο εÏώτημα και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-askst": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιγÏάφει τις συνθήκες του εÏωτήματος ως συμβολοσειÏά και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-askdu": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία πεÏιέχει τη χÏονική τιμή (σε δευτεÏόλεπτα) που απαιτείται για την ολοκλήÏωση της εκτέλεσης του εÏωτήματος και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-asksc": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki] η οποία αναγνωÏίζει εναλλακτικές (δηλαδή απομακÏυσμένες) πηγές εÏωτημάτων.",
+ "smw-property-predefined-prec": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που πεÏιγÏάφει την [https://www.semantic-mediawiki.org/wiki/Help:Display_precision απεικονιζόμενη ακÏίβεια] (σε δεκαδικά ψηφία) για αÏιθμητικοÏÏ‚ Ï„Ïπους δεδομένων.",
+ "smw-types-extra-geo-not-available": "Δεν εντοπίστηκαν [https://www.semantic-mediawiki.org/wiki/Semantic_Maps Σημασιολογικοί χάÏτες] οπότε η ιδιότητα «$1» έχει πεÏιοÏισμένες δυνατότητες λειτουÏγίας.",
+ "smw-datavalue-monolingual-dataitem-missing": "Απουσιάζει αναμενόμενο στοιχείο για τη δόμηση μονόγλωσσης σÏνθετης τιμής.",
+ "smw-datavalue-languagecode-missing": "Για τη σημειογÏαφία «$1», ο συντακτικός αναλυτής δεν ήταν σε θέση να Ï€ÏοσδιοÏίσει κωδικό γλώσσας (Ï€.χ. «foo@en»).",
+ "smw-datavalue-languagecode-invalid": "Το «$1» δεν αναγνωÏίστηκε ως κάποιος από τους υποστηÏιζόμενους κωδικοÏÏ‚ γλώσσας.",
+ "smw-property-predefined-lcode": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία αντιπÏοσωπεÏει κωδικό γλώσσας μοÏφοποιημένο κατά BCP47 και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-types-extra-mlt-lcode": "Ο Ï„Ïπος δεδομένων {{PLURAL:$2|απαιτεί|δεν απαιτεί}} κωδικό γλώσσας (δηλαδή μια σημειογÏαφία τιμής χωÏίς κωδικό γλώσσας {{PLURAL:$2|δεν|}} είναι αποδεκτή).",
+ "smw-property-predefined-text": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία αντιπÏοσωπεÏει κείμενο αυθαίÏετου μήκους και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-pdesc": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία επιτÏέπει τη λεκτική πεÏιγÏαφή μιας ιδιότητας σε κάποια γλώσσα και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-list": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που οÏίζει μια λίστα ιδιοτήτων που χÏησιμοποιοÏνται με ιδιότητα Ï„Ïπου [[Special:Types/Record|record]] και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] ΧÏόνος ÏƒÏ…Î½Ï„Î±ÎºÏ„Î¹ÎºÎ¿Ï Î±Î½Î±Î»Ï…Ï„Î® για ενδοκειμενική σημειογÏαφία",
+ "smw-limitreport-intext-postproctime": "[SMW] χÏόνος μετά την διαδικασία",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|δευτεÏόλεπτο|δευτεÏόλεπτα}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|second|seconds}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|δευτεÏόλεπτο|δευτεÏόλεπτα}}",
+ "smw_allows_pattern": "Αυτή η σελίδα αναμένεται να πεÏιλαμβάνει μια λίστα από παÏαπομπές (ακολουθοÏμενες από [https://en.wikipedia.org/wiki/Regular_expression κανονικές εκφÏάσεις]) και να γίνουν διαθέσιμες από την ιδιότητα [[Property:Allows pattern|Allows pattern]]. Για να επεξεÏγαστείτε αυτή τη σελίδα, απαιτείται το δικαίωμα <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "Η τιμή «$1» χαÏακτηÏίστηκε ως μη έγκυÏη από την κανονική έκφÏαση «$2».",
+ "smw-datavalue-allows-pattern-reference-unknown": "Για την αναφοÏά μοτίβου «$1» δεν βÏέθηκε ταίÏιασμα με κάποια καταχώÏιση στο [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-feature-not-supported": "Η λειτουÏγία «$1» δεν υποστηÏίζεται ή έχει απενεÏγοποιηθεί σε αυτό το wiki.",
+ "smw-datavalue-uniqueness-constraint-error": "Η ιδιότητα «$1» επιτÏέπει μόνο μοναδικές αναθέσεις τιμών και η τιμή ''$2'' έχει ήδη σημειογÏαφηθεί στο υποκείμενο «$3».",
+ "smw-datavalue-uniqueness-constraint-isknown": "Η ιδιότητα «$1» επιτÏέπει μόνο μοναδικές σημειογÏαφήσεις τιμών, η τιμή ''$2'' έχει ήδη ανατεθεί στο «$3».",
+ "smw-datavalue-time-invalid-values": "Η τιμή «$1» πεÏιέχει μη εÏμηνεÏσιμη πληÏοφοÏία της μοÏφής «$2».",
+ "smw-datavalue-time-invalid-date-components-common": "Η τιμή «$1» πεÏιέχει μη εÏμηνεÏσιμη πληÏοφοÏία.",
+ "smw-datavalue-time-invalid-date-components-dash": "Η τιμή «$1» πεÏιέχει άλλου Ï„Ïπου διαχωÏιστικό ή άλλους χαÏακτήÏες που δεν είναι έγκυÏοι για την αναγνώÏισή της ως ημεÏομηνία.",
+ "smw-datavalue-time-invalid-date-components-empty": "Η τιμή «$1» πεÏιέχει κάποια κενά στοιχεία.",
+ "smw-datavalue-time-invalid-date-components-three": "Η τιμή «$1» πεÏιέχει πεÏισσότεÏα από τα Ï„Ïία στοιχεία που απαιτοÏνται για την αναγνώÏισή της ως ημεÏομηνία.",
+ "smw-datavalue-time-invalid-date-components-sequence": "Το «$1» πεÏιέχει μια ακολουθία που δεν ήταν δυνατόν να εÏμηνευθεί βάσει του διαθέσιμου πίνακα με τα στοιχεία μοÏφής ημεÏομηνιών.",
+ "smw-datavalue-time-invalid-ampm": "Η τιμή «$1» πεÏιέχει ως ÏŽÏα το «$2» το οποίο δεν είναι έγκυÏο σε 12-ωÏη μοÏφή.",
+ "smw-datavalue-external-formatter-invalid-uri": "Το «$1» είναι μη έγκυÏη διεÏθυνση URL.",
+ "smw-property-predefined-peid": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα η οποία καθοÏίζει ένα εξωτεÏικό αναγνωÏιστικό και παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki].",
+ "smw-property-predefined-pefu": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki] για να καθοÏίσει μια εξωτεÏική διεÏθυνση URI με μεταβλητό τμήμα κειμένου.",
+ "smw-property-predefined-long-pefu": "Το URI αναμένεται να πεÏιέχει μια μεταβλητή κειμένου που θα λάβει μια τιμή [[Special:Types/External identifier|εξωτεÏÎ¹ÎºÎ¿Ï Î±Î½Î±Î³Î½Ï‰ÏιστικοÏ]] για να σχηματίσει μια έγκυÏη διεÏθυνση πόÏου.",
+ "smw-datavalue-parse-error": "Η τιμή «$1» που δόθηκε δεν ήταν κατανοητή.",
+ "smw-datavalue-propertylist-invalid-property-key": "Η λίστα ιδιοτήτων «$1» πεÏιείχε μη έγκυÏο κλειδί ιδιότητας «$2».",
+ "smw-datavalue-type-invalid-typeuri": "Ο Ï„Ïπος «$1» δεν θα μποÏοÏσε να μετατÏαπεί σε έγκυÏη αναπαÏάσταση URI.",
+ "smw-datavalue-wikipage-invalid-title": "The page type input value \"$1\" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.",
+ "smw-datavalue-wikipage-property-invalid-title": "Property \"$1\" (as page type) with input value \"$2\" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "Ο συντακτικός αναλυτής JSON επέστÏεψε το σφάλμα «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "ΑντιγÏαφή συνδέσμου στο Ï€ÏόχειÏο",
+ "smw-property-userdefined-fixedtable": "Η «$1» διαμοÏφώθηκε ως [https://www.semantic-mediawiki.org/wiki/Fixed_properties σταθεÏή ιδιότητα] και οποιαδήποτε Ï„Ïοποποίηση στη [https://www.semantic-mediawiki.org/wiki/Type_declaration δήλωση Ï„Ïπου] της απαιτεί είτε να εκτελέσετε το <code>setupStore.php</code> είτε να ολοκληÏώσετε την εÏγασία [[Special:SemanticMediaWiki|«Εγκατάσταση βάσεως δεδομένων και αναβάθμιση»]].",
+ "smw-data-lookup": "Γίνεται λήψη των δεδομένων...",
+ "smw-no-data-available": "Δεν υπάÏχουν διαθέσιμα δεδομένα.",
+ "smw-edit-protection-enabled": "ΠÏοστατευμένη από επεξεÏγασίες (Σημασιολογικό MediaWiki)",
+ "smw-patternedit-protection": "Αυτή η σελίδα Ï€ÏοστατεÏεται και μποÏοÏν να την επεξεÏγαστοÏν μόνο χÏήστες με την κατάλληλη [https://www.semantic-mediawiki.org/wiki/Help:Permissions άδεια] <code>smw-patternedit</code> .",
+ "smw-property-predefined-edip": "Η «$1» είναι μια εκ των Ï€ÏοτέÏων οÏισμένη ιδιότητα που παÏέχεται από το [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Σημασιολογικό MediaWiki] για να δηλώσει εάν η επεξεÏγασία είναι Ï€Ïοστατευμένη ή όχι.",
+ "smw-format-datatable-emptytable": "Δεν υπάÏχουν δεδομένα στον διαθέσιμο πίνακα",
+ "smw-format-datatable-info": "Showing _START_ to _END_ of _TOTAL_ entries",
+ "smw-format-datatable-infoempty": "Showing 0 to 0 of 0 entries",
+ "smw-format-datatable-infofiltered": "(filtered from _MAX_ total entries)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Show _MENU_ entries",
+ "smw-format-datatable-loadingrecords": "ΦόÏτωση",
+ "smw-format-datatable-processing": "Γίνεται επεξεÏγασία...",
+ "smw-format-datatable-search": "Ψάξε",
+ "smw-format-datatable-zerorecords": "Δεν βÏέθηκαν εγγÏαφές που να ταιÏιάζουν",
+ "smw-format-datatable-first": "ΠÏώτο",
+ "smw-format-datatable-last": "Τελευταίο",
+ "smw-format-datatable-next": "Επόμενο",
+ "smw-format-datatable-previous": "ΠÏοηγοÏμενο",
+ "smw-format-datatable-sortascending": ":ΕνεÏγοποιήστε για να ταξινομήσετε αÏξουσα στήλη",
+ "smw-format-datatable-sortdescending": ": ΕνεÏγοποιήστε για να ταξινομήσετε φθίνουσα στήλη",
+ "smw-format-datatable-toolbar-export": "Εξαγωγή",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-parser-function-expensive-execution-limit": "Η συνάÏτηση του ÏƒÏ…Î½Ï„Î±ÎºÏ„Î¹ÎºÎ¿Ï Î±Î½Î±Î»Ï…Ï„Î® έχει φτάσει στο ÏŒÏιό της για ακÏιβές εκτελέσεις (βλ. παÏάμετÏο διαμόÏφωσης [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit $smwgQExpensiveExecutionLimit]).",
+ "smw-postproc-queryref": "Το σημειολογικό πεδίο της MediaWiki ανανεώνει τη Ï„Ïέχουσα έκδοσης μιας σελίδας στην κατάσταση που αντιστοιχεί στη ζήτηση που απαιτείται μετά την διαδικασία.",
+ "smw-api-invalid-parameters": "ΆκυÏη παÏάμετÏος, \"$1\"",
+ "smw-property-reserved-category": "ΚατηγοÏία",
+ "smw-category": "ΚατηγοÏία",
+ "smw-datavalue-uri-invalid-scheme": "Το \"$1\" δεν έχει καταχωÏηθεί ως έγκυÏο σχήμα URI .",
+ "smw-browse-property-group-title": "Ομάδα ιδιοτήτων",
+ "smw-filter": "ΦιλτÏάÏισμα",
+ "smw-section-expand": "Επέκταση της ενότητας",
+ "smw-section-collapse": "ΣÏμπτυξη της ενότητας",
+ "smw-help": "Βοήθεια",
+ "smw-personal-jobqueue-watchlist": "Λίστα ΠαÏακολοÏθησης σειÏάς επαγγελμάτων",
+ "smw-types-title": "ΤÏπος: $1",
+ "smw-parameter-missing": "Η παÏάμετÏος \"$1\" λείπει.",
+ "smw-property-tab-usage": "ΧÏήση",
+ "smw-property-tab-redirects": "Συνώνυμα",
+ "smw-property-tab-subproperties": "Υποϊδιότητες",
+ "smw-property-tab-errors": "Λάθος αναθέσεις",
+ "smw-concept-tab-list": "Κατάλογος",
+ "smw-concept-tab-errors": "Σφάλματα"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/en-gb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/en-gb.json
new file mode 100644
index 00000000..c1279d35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/en-gb.json
@@ -0,0 +1,19 @@
+{
+ "@metadata": {
+ "authors": [
+ "Reedy",
+ "Shirayuki",
+ "Chase me ladies, I'm the Cavalry",
+ "Codynguyen1116",
+ "Pierpao"
+ ]
+ },
+ "smw_printername_ul": "Itemisation",
+ "smw_noboolean": "\"$1\" is not recognised as a Boolean (true/false) value.",
+ "smw_types_docu": "List of [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes available datatypes] with each [https://www.semantic-mediawiki.org/wiki/Help:Datatype type] representing a unique set of attributes to describe a value in terms of storage and display characteristics that are hereditary to an assigned property.",
+ "browse": "Browse wiki",
+ "smw-admin-permissionswarn": "If the operation fails with SQL errors, the database user employed by your wiki (check your LocalSettings.php) probably does not have sufficient permissions.\nEither grant this user additional permissions to create and delete tables, temporarily enter the login of your database root in LocalSettings.php, or use the maintenance script <code>setupStore.php</code> which can use the credentials of an administrator.",
+ "smw-admin-dbbutton": "Initialise or upgrade tables",
+ "smw-prefs-intro-text": "Options below are provided by [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (or related extensions) to enable individual customisation on selected functions. For more information, please have a look at this [https://www.semantic-mediawiki.org/wiki/Help:User_preferences help section].",
+ "smw-admin-settings-docu": "Displays a list of all default and localised settings that are relevant to the Semantic MediaWiki environment. For details on individual settings, please consult the [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] help page."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/en.json b/www/wiki/extensions/SemanticMediaWiki/i18n/en.json
new file mode 100644
index 00000000..a1375e24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/en.json
@@ -0,0 +1,840 @@
+{
+ "@metadata": {
+ "authors": [
+ "James Hong Kong",
+ "Jeroen De Dauw",
+ "Karsten Hoffmeyer",
+ "Markus Krötzsch"
+ ]
+ },
+ "smw-desc": "Making your wiki more accessible - for machines ''and'' humans ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error": "Semantic MediaWiki was installed and enabled but is missing an appropriate [https://www.semantic-mediawiki.org/wiki/Help:Upgrade upgrade key] that matches: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Why Do I see this error?",
+ "smw-upgrade-error-why-explain": "Semantic MediaWiki's internal database structure has changed and requires some adjustments to be fully functional. There can be several reasons including:\n* Additional fixed properties (requires additional table setup) were added \n* An upgrade contains some changes to tables or indices making an interception obligatory before accessing the data",
+ "smw-upgrade-error-how-title": "How Do I fix this error?",
+ "smw-upgrade-error-how-explain": "An administrator (or any person with administrator rights) has to run either MediaWiki's [https://www.mediawiki.org/wiki/Manual:Update.php update.php] or Semantic MediaWiki's [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] maintenance script. You may also consult the following pages for further assistance:\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation Installation] instructions\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting Troubleshooting] help page",
+ "smw-semantics-not-enabled": "Semantic MediaWiki functionality was not enabled for this wiki.",
+ "smw_viewasrdf": "RDF feed",
+ "smw_finallistconjunct": ", and",
+ "smw-factbox-head": "... more about \"$1\"",
+ "smw-factbox-facts": "Facts",
+ "smw-factbox-facts-help": "Shows statements and facts that have been created by a user",
+ "smw-factbox-facts-derived": "Derived facts",
+ "smw-factbox-facts-derived-help": "Shows facts that have been derived from rules or with the help of other reasoning techniques",
+ "smw_isspecprop": "This property is a special property in this wiki.",
+ "smw-concept-cache-header": "Cache usage",
+ "smw-concept-cache-count": "The [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count concept cache] contains {{PLURAL:$1|'''one''' entity|'''$1''' entities}} ($2).",
+ "smw-concept-no-cache": "No cache available.",
+ "smw_concept_description": "Description of concept \"$1\"",
+ "smw_no_concept_namespace": "Concepts can only be defined on pages in the Concept: namespace.",
+ "smw_multiple_concepts": "Each concept page can have only one concept definition.",
+ "smw_concept_cache_miss": "The concept \"$1\" can not be used at the moment, since the wiki configuration requires it to be computed off-line.\nIf the problem does not go away after some time, ask your site administrator to make this concept available.",
+ "smw_noinvannot": "Values cannot be assigned to inverse properties.",
+ "version-semantic": "Semantic extensions",
+ "smw_uri_blacklist": "https://www.w3.org/1999/02/22-rdf-syntax-ns#\n https://www.w3.org/2000/01/rdf-schema#\n https://www.w3.org/2002/07/owl#",
+ "smw_baduri": "URIs of the form \"$1\" are not allowed.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "Count results",
+ "smw_printername_csv": "CSV export",
+ "smw_printername_dsv": "DSV export",
+ "smw_printername_debug": "Debug query (for experts)",
+ "smw_printername_embedded": "Embed page contents",
+ "smw_printername_json": "JSON export",
+ "smw_printername_list": "List",
+ "smw_printername_plainlist": "Plain list",
+ "smw_printername_ol": "Enumeration",
+ "smw_printername_ul": "Itemization",
+ "smw_printername_table": "Table",
+ "smw_printername_broadtable": "Broad table",
+ "smw_printername_template": "Template",
+ "smw_printername_templatefile": "Template file",
+ "smw_printername_rdf": "RDF export",
+ "smw_printername_category": "Category",
+ "validator-type-class-SMWParamSource": "text",
+ "smw-paramdesc-limit": "The maximum number of results to return",
+ "smw-paramdesc-offset": "The offset of the first result",
+ "smw-paramdesc-headers": "Display the headers/property names",
+ "smw-paramdesc-mainlabel": "The label to give to the main page name",
+ "smw-paramdesc-link": "Show values as links",
+ "smw-paramdesc-intro": "The text to display before the query results, if there are any",
+ "smw-paramdesc-outro": "The text to display after the query results, if there are any",
+ "smw-paramdesc-default": "The text to display if there are no query results",
+ "smw-paramdesc-sep": "The separator between results",
+ "smw-paramdesc-propsep": "The separator between the properties of a result entry",
+ "smw-paramdesc-valuesep": "The separator between the values for a property of a result",
+ "smw-paramdesc-showsep": "Show separator in top of CSV file (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Instead of displaying all values, count their occurrences, and show these.",
+ "smw-paramdesc-distributionsort": "Sort the value distribution by occurrence count.",
+ "smw-paramdesc-distributionlimit": "Limit the value distribution to the count of only some values.",
+ "smw-paramdesc-aggregation": "Specify to what the aggregation should relate to",
+ "smw-paramdesc-template": "The name of a template with which to display the printouts",
+ "smw-paramdesc-columns": "The number of columns in which to display results",
+ "smw-paramdesc-userparam": "A value passed into each template call, if a template is used",
+ "smw-paramdesc-class": "An additional CSS class to set for the list",
+ "smw-paramdesc-introtemplate": "The name of a template to display before the query results, if there are any",
+ "smw-paramdesc-outrotemplate": "The name of a template to display after the query results, if there are any",
+ "smw-paramdesc-embedformat": "The HTML tag used to define headings",
+ "smw-paramdesc-embedonly": "Display no headings",
+ "smw-paramdesc-table-class": "An additional CSS class to set for the table",
+ "smw-paramdesc-table-transpose": "Display table headers vertically and results horizontally",
+ "smw-paramdesc-rdfsyntax": "The RDF syntax to be used",
+ "smw-paramdesc-csv-sep": "Specifies a column separator",
+ "smw-paramdesc-csv-valuesep": "Specifies a value separator",
+ "smw-paramdesc-csv-merge": "Merge rows and column values with an identical subject identifier (aka first column)",
+ "smw-paramdesc-csv-bom": "Add a BOM (character to signal endianness) at the top of the output file",
+ "smw-paramdesc-dsv-separator": "The separator to use",
+ "smw-paramdesc-dsv-filename": "The name for the DSV file",
+ "smw-paramdesc-filename": "The name for the output file",
+ "smw-smwdoc-description": "Shows a table of all parameters that can be used for the specified result format together with default values and descriptions.",
+ "smw-smwdoc-default-no-parameter-list": "This result format does not provide format specific parameters.",
+ "smw-smwdoc-par-format": "The result format to display parameter documentation for.",
+ "smw-smwdoc-par-parameters": "Which parameters to show. \"specific\" for those added by the format, \"base\" for those available in all formats, and \"all\" for both.",
+ "smw-paramdesc-sort": "Property to sort the query by",
+ "smw-paramdesc-order": "Order of the query sort",
+ "smw-paramdesc-searchlabel": "Text for continuing the search",
+ "smw-paramdesc-named_args": "Name the arguments passed to the template",
+ "smw-paramdesc-template-arguments": "Sets how the named arguments are passed to the template",
+ "smw-paramdesc-import-annotation": "Additional annotated data are to be copied during the parsing of a subject",
+ "smw-paramdesc-export": "Export option",
+ "smw-paramdesc-prettyprint": "A pretty-print output that displays additional indents and newlines",
+ "smw-paramdesc-json-unescape": "Output to contain unescaped slashes and multibyte Unicode characters",
+ "smw-paramdesc-json-type": "Serialization type",
+ "smw-paramdesc-source": "Alternative query source",
+ "smw-paramdesc-jsonsyntax": "JSON syntax to be used",
+ "smw-printername-feed": "RSS and Atom feed",
+ "smw-paramdesc-feedtype": "Feed type",
+ "smw-paramdesc-feedtitle": "The text to be used as the title of the feed",
+ "smw-paramdesc-feeddescription": "The text to be used as the description of the feed",
+ "smw-paramdesc-feedpagecontent": "Page content to be displayed with the feed",
+ "smw-label-feed-link": "RSS",
+ "smw-label-feed-description": "$1 $2 feed",
+ "smw-paramdesc-mimetype": "The media type (MIME type) for the output file",
+ "smw_iq_disabled": "Semantic queries have been disabled for this wiki.",
+ "smw_iq_moreresults": "... further results",
+ "smw_parseerror": "The given value was not understood.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": ",",
+ "smw_notitle": "\"$1\" cannot be used as a page name in this wiki.",
+ "smw_noproperty": "\"$1\" cannot be used as a property name in this wiki.",
+ "smw_wrong_namespace": "Only pages in namespace \"$1\" are allowed here.",
+ "smw_manytypes": "More than one type defined for property.",
+ "smw_emptystring": "Empty strings are not accepted.",
+ "smw_notinenum": "\"$1\" is not in the list ($2) of [[Property:Allows value|allowed values]] for the \"$3\" property.",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" is not in the list ($2) of [[Property:Allows value|allowed values]] for the \"$3\" property.",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" is not within that range of \"$2\" specified by the [[Property:Allows value|allows value]] constraint for the \"$3\" property.",
+ "smw_noboolean": "\"$1\" is not recognized as a Boolean (true/false) value.",
+ "smw_true_words": "true,t,yes,y",
+ "smw_false_words": "false,f,no,n",
+ "smw_nofloat": "\"$1\" is not a number.",
+ "smw_infinite": "Numbers as large as \"$1\" are not supported.",
+ "smw_unitnotallowed": "\"$1\" is not declared as a valid unit of measurement for this property.",
+ "smw_nounitsdeclared": "No units of measurement were declared for this property.",
+ "smw_novalues": "No values specified.",
+ "smw_nodatetime": "The date \"$1\" was not understood.",
+ "smw_toomanyclosing": "There appear to be too many occurrences of \"$1\" in the query.",
+ "smw_noclosingbrackets": "Some use of \"<nowiki>[[</nowiki>\" in your query was not closed by a matching \"]]\".",
+ "smw_misplacedsymbol": "The symbol \"$1\" was used in a place where it is not useful.",
+ "smw_unexpectedpart": "The part \"$1\" of the query was not understood.\nResults might not be as expected.",
+ "smw_emptysubquery": "Some subquery has no valid condition.",
+ "smw_misplacedsubquery": "Some subquery was used in a place where no subqueries are allowed.",
+ "smw_valuesubquery": "Subqueries not supported for values of property \"$1\".",
+ "smw_badqueryatom": "Some part \"<nowiki>[[...]]</nowiki>\" of the query was not understood.",
+ "smw_propvalueproblem": "The value of property \"$1\" was not understood.",
+ "smw_noqueryfeature": "Some query feature was not supported in this wiki and part of the query was dropped ($1).",
+ "smw_noconjunctions": "Conjunctions in queries are not supported in this wiki and part of the query was dropped ($1).",
+ "smw_nodisjunctions": "Disjunctions in queries are not supported in this wiki and part of the query was dropped ($1).",
+ "smw_querytoolarge": "The following {{PLURAL:$2|query condition|$2 query conditions}} could not be considered due to this wiki's restrictions on query size or depth: <code>$1</code>.",
+ "smw_notemplategiven": "Provide a value for the parameter \"template\" for this query format to work.",
+ "smw_db_sparqlqueryproblem": "The query result could not be obtained from the SPARQL database. This error might be temporary or indicate a bug in the database software.",
+ "smw_db_sparqlqueryincomplete": "Answering the query turned out to be too difficult and was aborted. Some results could be missing. If possible, try using a simpler query instead.",
+ "smw_type_header": "Properties of type \"$1\"",
+ "smw_typearticlecount": "Showing $1 {{PLURAL:$1|property|properties}} using this type.",
+ "smw_attribute_header": "Pages using the property \"$1\"",
+ "smw_attributearticlecount": "Showing $1 {{PLURAL:$1|page|pages}} using this property.",
+ "smw-propertylist-subproperty-header": "Subproperties",
+ "smw-propertylist-redirect-header": "Synonyms",
+ "smw-propertylist-error-header": "Pages with improper assignments",
+ "smw-propertylist-count": "Showing $1 related {{PLURAL:$1|entity|entities}}.",
+ "smw-propertylist-count-with-restricted-note": "Showing $1 related {{PLURAL:$1|entity|entities }} (more are available but the display is restricted to \"$2\").",
+ "smw-propertylist-count-more-available": "Showing $1 related {{PLURAL:$1|entity|entities}} (more are available).",
+ "specialpages-group-smw_group": "Semantic MediaWiki",
+ "exportrdf": "Export pages to RDF",
+ "smw_exportrdf_docu": "This page allows you to obtain data from a page in RDF format.\nTo export pages, enter the titles in the text box below, one title per line.",
+ "smw_exportrdf_recursive": "Recursively export all related pages.\nNote that the result could be large!",
+ "smw_exportrdf_backlinks": "Also export all pages that refer to the exported pages.\nGenerates browsable RDF.",
+ "smw_exportrdf_lastdate": "Do not export pages that were not changed since the given point in time.",
+ "smw_exportrdf_submit": "Export",
+ "uriresolver": "URIResolver",
+ "properties": "Properties",
+ "smw_properties_docu": "The following properties are used in the wiki.",
+ "smw_property_template": "$1 of type $2 ($3 {{PLURAL:$3|use|uses}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "All properties should be described by a page!",
+ "smw_propertylackstype": "No type was specified for this property (assuming type $1 for now).",
+ "smw_propertyhardlyused": "This property is hardly used within the wiki!",
+ "smw-property-name-invalid": "Property $1 can not be used (invalid property name).",
+ "smw-property-name-reserved": "\"$1\" was listed as reserved name and should not be used as a property. The following [https://www.semantic-mediawiki.org/wiki/Help:Property_naming help page] may contain information as to why this name was reserved.",
+ "smw-sp-property-searchform": "Display properties that contain:",
+ "smw-sp-property-searchform-inputinfo": "The input is case sensitive and when used for filtering, only properties that match the condition are displayed.",
+ "smw-special-property-searchform": "Display properties that contain:",
+ "smw-special-property-searchform-inputinfo": "The input is case sensitive and when used for filtering, only properties that match the condition are displayed.",
+ "smw-special-property-searchform-options": "Options",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "None",
+ "smw-special-wantedproperties-filter-unapproved": "Unapproved",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Filter option used in connection with the authority mode.",
+ "concepts": "Concepts",
+ "smw-special-concept-docu": "A [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] can be viewed as \"dynamic category\", i.e. as a collection of pages that are not created manually, but that are computed by Semantic MediaWiki from a description of a given query.",
+ "smw-special-concept-header": "List of concepts",
+ "smw-special-concept-count": "The following {{PLURAL:$1|concept is|$1 concepts are}} being listed.",
+ "smw-special-concept-empty": "No concept was found.",
+ "unusedproperties": "Unused properties",
+ "smw-unusedproperties-docu": "This page lists [https://www.semantic-mediawiki.org/wiki/Unused_properties unused properties] that are declared although no other page makes use of them. For a differentiated view, see the [[Special:Properties|entire]] or [[Special:WantedProperties|wanted properties]] special pages.",
+ "smw-unusedproperty-template": "$1 of type $2",
+ "wantedproperties": "Wanted properties",
+ "smw-wantedproperties-docu": "This page lists [https://www.semantic-mediawiki.org/wiki/Wanted_properties wanted properties] that are used in the wiki but do not have a page describing them. For a differentiated view, see the [[Special:Properties|entire]] or [[Special:UnusedProperties|unused properties]] special pages.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|use|uses}})",
+ "smw-special-wantedproperties-docu": "This page lists [https://www.semantic-mediawiki.org/wiki/Wanted_properties wanted properties] that are used in the wiki but do not have a page describing them. For a differentiated view, see the [[Special:Properties|entire]] or [[Special:UnusedProperties|unused properties]] special pages.",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|use|uses}})",
+ "smw_purge": "Refresh",
+ "smw-purge-failed": "Refreshing failed",
+ "types": "Types",
+ "smw_types_docu": "List of [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes available datatypes] with each [https://www.semantic-mediawiki.org/wiki/Help:Datatype type] representing a unique set of attributes to describe a value in terms of storage and display characteristics that are hereditary to an assigned property.",
+ "smw-special-types-no-such-type": "\"$1\" is unknown or has not been specified as valid datatype.",
+ "smw-statistics": "Semantic statistics",
+ "smw-statistics-property-instance": "Property {{PLURAL:$1|value|values}} (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Property|Properties}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Property|Properties}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Property|Properties}}]] (used with at least one value)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Property|Properties}} (registered with a page)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Property|Properties}} (assigned to a datatype)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Query|Queries}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Query|Queries}}]]",
+ "smw-statistics-query-size": "Query size",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concept|Concepts}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concept|Concepts}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobject|Subobjects}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobject|Subobjects}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datatype|Datatypes}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Property value|Property values}} ([[Special:ProcessingErrorList|{{PLURAL:$1|improper annotation|improper annotations}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Property value|Property values}} ({{PLURAL:$1|improper annotation|improper annotations}})",
+ "smw-statistics-delete-count": "Outdated {{PLURAL:$1|entity|entities}} (marked for removal)",
+ "smw_uri_doc": "The URI resolver implements the [$1 W3C TAG finding on httpRange-14].\nIt takes care that humans do not turn into websites.",
+ "ask": "Semantic search",
+ "smw_ask_doculink": "https://www.semantic-mediawiki.org/wiki/Help:Semantic_search",
+ "smw-ask-help": "This section contains some links to help explain how to use the <code>#ask</code> syntax.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecting pages] describes how to selected pages and build conditions\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Search operators] lists available search operators including those for range and wildcard queries\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Displaying information] outlines the use of printout statements and formatting options",
+ "smw_ask_sortby": "Sort by column (optional)",
+ "smw_ask_ascorder": "Ascending",
+ "smw_ask_descorder": "Descending",
+ "smw-ask-order-rand": "Random",
+ "smw_ask_submit": "Find results",
+ "smw_ask_editquery": "Edit query",
+ "smw_add_sortcondition": "[Add sorting condition]",
+ "smw-ask-sort-add-action": "Add sorting condition",
+ "smw_ask_hidequery": "Hide query (compact view)",
+ "smw_ask_help": "Querying help",
+ "smw_ask_queryhead": "Condition",
+ "smw_ask_printhead": "Printout selection",
+ "smw_ask_printdesc": "(add one property name per line)",
+ "smw_ask_format_as": "Format as:",
+ "smw_ask_defaultformat": "default",
+ "smw_ask_otheroptions": "Other options",
+ "smw-ask-otheroptions-info": "This section contains options that alter printout statements. Parameter descriptions can be viewed by hovering over them.",
+ "smw-ask-otheroptions-collapsed-info": "Please use the plus icon to view all available options",
+ "smw_ask_show_embed": "Show embed code",
+ "smw_ask_hide_embed": "Hide embed code",
+ "smw_ask_embed_instr": "To embed this query inline into a wiki page use the code below.",
+ "smw-ask-delete": "Remove",
+ "smw-ask-sorting": "Sorting",
+ "smw-ask-options": "Options",
+ "smw-ask-options-sort": "Sort options",
+ "smw-ask-format-options": "Format and options",
+ "smw-ask-parameters": "Parameters",
+ "smw-ask-search": "Search",
+ "smw-ask-debug": "Debug",
+ "smw-ask-debug-desc": "Generates query debug information",
+ "smw-ask-no-cache": "Disable query cache",
+ "smw-ask-no-cache-desc": "Results without query cache",
+ "smw-ask-result": "Result",
+ "smw-ask-empty": "Clear all entries",
+ "smw-ask-download-link-desc": "Download queried results in $1 format",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Help with the selected format: $1",
+ "smw-ask-condition-change-info": "The condition was altered and the search engine requires to rerun the query to produce results that matches the new requirements.",
+ "smw-ask-input-assistance": "Input assistance",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Input assistance] is provided for the printout, sort, and condition field. The condition field requires to use one of following prefixes:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> to fetch property suggestions (e.g. <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> to fetch category suggestions",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> to fetch concept suggestions",
+ "smw-ask-format-change-info": "The format was modified and it is required to execute the query again to match new parameters and visualization options.",
+ "smw-ask-format-export-info": "The selected format is an export format which has no visual representation therefore results are only provided as download.",
+ "smw-ask-query-search-info": "The query <code><nowiki>$1</nowiki></code> was answered by the {{PLURAL:$3|1=<code>$2</code> (from cache)|<code>$2</code> (from cache)|<code>$2</code>}} in $4 {{PLURAL:$4|second|seconds}}.",
+ "searchbyproperty": "Search by property",
+ "processingerrorlist": "Processing error list",
+ "propertylabelsimilarity": "Property label similarity report",
+ "smw-processingerrorlist-intro": "The following list provides an overview about the processing errors that appeared in connection with [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. It is recommended to monitor this list on a regular basis and correct invalid value annotations.",
+ "smw_sbv_docu": "Search for all pages that have a given property and value.",
+ "smw_sbv_novalue": "Enter a valid value for the property, or view all property values for \"$1\".",
+ "smw_sbv_displayresult": "A list of all pages that have property \"$1\" with value \"$2\"",
+ "smw_sbv_displayresultfuzzy": "A list of all pages that have property \"$1\" with value \"$2\".\nSince there have been only a few results, also nearby values are displayed.",
+ "smw_sbv_property": "Property:",
+ "smw_sbv_value": "Value:",
+ "smw_sbv_submit": "Find results",
+ "browse": "Browse wiki",
+ "smw_browselink": "Browse properties",
+ "smw_browse_article": "Enter the name of the page to start browsing from.",
+ "smw_browse_go": "Go",
+ "smw_browse_more": "...",
+ "smw_browse_show_incoming": "Show incoming properties",
+ "smw_browse_hide_incoming": "Hide incoming properties",
+ "smw_browse_no_outgoing": "This page has no properties.",
+ "smw_browse_no_incoming": "No properties link to this page.",
+ "smw-browse-from-backend": "Information is currently being retrieved from the backend.",
+ "smw-browse-intro": "This page provides details about a subject or entity instance, please enter the name of an object to be inspected.",
+ "smw-browse-invalid-subject": "The subject validation returned with a \"$1\" error.",
+ "smw-browse-api-subject-serialization-invalid" : "The subject has an invalid serialization format.",
+ "smw-browse-js-disabled": "It is suspected that Javascript is disabled or not available and we recommend to use a browser where this is supported. Other options are discussed on the [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>] configuration parameter page.",
+ "smw-browse-show-group": "Show groups",
+ "smw-browse-hide-group": "Hide groups",
+ "smw-noscript": "This page or action requires Javascript to work, please enable Javascript in your browser or use a browser where this is supported so that functionality can be served and is provided as requested. For further assistance, please have a look at the [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript] help page.",
+ "smw_inverse_label_default": "$1 of",
+ "smw_inverse_label_property": "Inverse property label",
+ "pageproperty": "Page property search",
+ "smw_pp_docu": "Either enter a page and property, or just a property to retrieve all assigned values.",
+ "smw_pp_from": "From page:",
+ "smw_pp_type": "Property:",
+ "smw_pp_submit": "Find results",
+ "smw_result_prev": "Previous",
+ "smw_result_next": "Next",
+ "smw_result_results": "Results",
+ "smw_result_noresults": "No results.",
+ "smwadmin": "Administrative and maintenance functions",
+ "smw-admin-statistics-job-title": "Job statistics",
+ "smw-admin-statistics-job-docu": "The job statistics displays information about scheduled Semantic MediaWiki jobs that have not yet been executed. The number of jobs may be slightly inaccurate or contains failed attempts, please consult the [https://www.mediawiki.org/wiki/Manual:Job_queue manual] for further information.",
+ "smw-admin-statistics-querycache-title": "Query cache statistics",
+ "smw-admin-statistics-querycache-disabled": "The [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] has not been enabled on this wiki and hereby no statistics are available.",
+ "smw-admin-statistics-querycache-explain": "The cache statistics is to contain provisional cumulative as well as derived data including: \n* \"misses\" as the total attempts to retrieve data from the cache with unattainable responses, forcing a direct repository (DB, triple-store etc.) retrieval \n* \"deletes\" as the total amount of cache eviction operations (either through a purge or query dependency) \n* \"hits\" contains the amount of cache retrievals from either embedded (queries called from within a wiki page) or non-embedded (if enabled, requested by pages like Special:Ask or the API) sources \n* \"medianRetrievalResponseTime\" is an orientation value of the median response time (in sec.) for cached and non-cached retrieval requests over the time span of the collection process \n* \"noCache\" indicates the amount of no attempt requests (limit=0 queries, 'no-cache' option etc.) to retrieve results from cache",
+ "smw-admin-permission-missing": "The access to this page has been blocked due to missing permissions, please consult the [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissions] help page for details about the necessary settings.",
+ "smw-admin-setupsuccess": "The storage engine was set up.",
+ "smw_smwadmin_return": "Return to $1",
+ "smw_smwadmin_updatestarted": "A new update process for refreshing the semantic data was started.\nAll stored data will be rebuilt or repaired where needed.\nYou can follow the progress of the update on this special page.",
+ "smw_smwadmin_updatenotstarted": "There is already an update process running.\nNot creating another one.",
+ "smw_smwadmin_updatestopped": "All existing update processes have been stopped.",
+ "smw_smwadmin_updatenotstopped": "To stop the running update process, you must activate the checkbox to indicate that you are really sure.",
+ "smw-admin-docu": "This special page helps you during installation, upgrade, maintenance and usage of <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> and also provides further administrative functions and tasks as well as statistics.\nRemember to back up valuable data before executing administrative functions.",
+ "smw-admin-environment": "Software environment",
+ "smw-admin-db": "Database maintenance",
+ "smw-admin-db-preparation": "The table initialization is ongoing and may take a moment before results are displayed pending the size and possible table optimizations.",
+ "smw-admin-dbdocu": "Semantic MediaWiki requires some extensions to the MediaWiki database in order to store the semantic data.\nThe below function ensures that your database is set up properly.\nThe changes made in this step do not affect the rest of the MediaWiki database, and can easily be undone if desired.\nThis setup function can be executed multiple times without doing any harm, but it is needed only once on installation or upgrade.",
+ "smw-admin-permissionswarn": "If the operation fails with SQL errors, the database user employed by your wiki (check your \"LocalSettings.php\" file) probably does not have sufficient permissions.\nEither grant this user additional permissions to create and delete tables, temporarily enter the login of your database root in the \"LocalSettings.php\" file, or use the maintenance script <code>setupStore.php</code>, which can use the credentials of an administrator.",
+ "smw-admin-dbbutton": "Initialize or upgrade tables",
+ "smw-admin-announce": "Announce your wiki",
+ "smw-admin-announce-text": "If your wiki is public, you can register it on <a href=\"https://wikiapiary.com\">WikiApiary</a>, the wiki tracking wiki.",
+ "smw-admin-deprecation-notice-title": "Deprecation notices",
+ "smw-admin-deprecation-notice-docu": "The following section contains settings that have been deprecated or removed but were detected to be active on this wiki. It is expected that any future release will remove support for these configurations.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> is deprecated and will be removed in $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> will remove (or replace) the following {{PLURAL:$2|option|options}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> is deprecated and will be removed in $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> was replaced by <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|option|options}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> is being replaced by <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> was removed in $2",
+ "smw-admin-deprecation-notice-title-notice": "Upcoming changes",
+ "smw-admin-deprecation-notice-title-notice-explanation": "The following settings have been detected to be used on this wiki and are planned to be removed or changed in a future release.",
+ "smw-admin-deprecation-notice-title-replacement": "Replaced or renamed settings",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "The following section contains settings that were renamed or otherwise modified and it is recommended to forthwith update their name or format.",
+ "smw-admin-deprecation-notice-title-removal": "Removed settings",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Listed settings were removed in a previous release but have yet been detected to be used on this wiki.",
+ "smw-smwadmin-refresh-title": "Data repair and update",
+ "smw_smwadmin_datarefresh": "Data rebuild",
+ "smw_smwadmin_datarefreshdocu": "It is possible to restore all Semantic MediaWiki data based on the current contents of the wiki.\nThis can be useful to repair broken data or to refresh the data if the internal format has changed due to some software upgrade.\nThe update is executed page by page and will not be completed immediately.\nThe following shows if an update is in progress and allows you to start or stop updates (unless this feature was disabled by the site administrator).",
+ "smw_smwadmin_datarefreshprogress": "<strong>An update is already in progress.</strong>\nIt is normal that the update progresses only slowly since it only refreshes data in small chunks each time a user accesses the wiki.\nTo finish this update more quickly, you can invoke the MediaWiki maintenance script <code>runJobs.php</code> (use the option <code>--maxjobs 1000</code> to restrict the number of updates done in one batch).\nEstimated progress of current update:",
+ "smw_smwadmin_datarefreshbutton": "Schedule data rebuild",
+ "smw_smwadmin_datarefreshstop": "Stop this update",
+ "smw_smwadmin_datarefreshstopconfirm": "Yes, I am {{GENDER:$1|sure}}.",
+ "smw-admin-job-scheduler-note": "Most activities in this section are performed as jobs to avoid deadlock situations during their execution. The [https://www.mediawiki.org/wiki/Manual:Job_queue job scheduler] is responsible for the processing and it is critical that maintenance script <code>runJobs.php</code> (see also configuration parameter <code>$wgRunJobsAsync</code>) has an appropriate capacity.",
+ "smw-admin-outdateddisposal-title": "Outdated entities disposal",
+ "smw-admin-outdateddisposal-intro": "Some activities (a change to a property type, the removal of wikipages, or the correction of error values) will result in [https://www.semantic-mediawiki.org/wiki/Outdated_entities outdated entities] and it is suggested to remove them periodically to free associated table space.",
+ "smw-admin-outdateddisposal-active": "An outdated entities disposal job has been scheduled.",
+ "smw-admin-outdateddisposal-button": "Schedule disposal",
+ "smw-admin-feature-disabled": "This feature has been disabled on this wiki, please consult the <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">settings</a> help page or contact the system administrator.",
+ "smw-admin-propertystatistics-title": "Property statistics rebuild",
+ "smw-admin-propertystatistics-intro": "Rebuilds the entire property usage statistics and therein updates and corrects the [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count usage count] of properties.",
+ "smw-admin-propertystatistics-active": "A property statistics rebuild job has been scheduled.",
+ "smw-admin-propertystatistics-button": "Schedule statistics rebuild",
+ "smw-admin-fulltext-title": "Full-text search rebuild",
+ "smw-admin-fulltext-intro": "Rebuilds the search index from property tables with an enabled [https://www.semantic-mediawiki.org/wiki/Full-text full-text search] datatype. Changes to the index rules (altered stopwords, new stemmer etc.) and/or a newly added or altered table does require to run this job again.",
+ "smw-admin-fulltext-active": "A full-text search rebuild job has been scheduled.",
+ "smw-admin-fulltext-button": "Schedule full-text rebuild",
+ "smw-admin-support": "Getting support",
+ "smw-admin-supportdocu": "Various resources are provided to help you in case of problems:",
+ "smw-admin-installfile": "If you experience problems with your installation, start by checking the guidelines in the <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL file</a> and the <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">installation page</a>.",
+ "smw-admin-smwhomepage": "The complete user documentation to Semantic MediaWiki is at <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Bugs can be reported to the <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">issue tracker</a>, the <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">reporting bugs</a> page provides some guidance on how to write an efficient issue report.",
+ "smw-admin-questions": "If you have further questions or suggestions, join the discussion on the Semantic MediaWiki <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">user mailing list</a> or the <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">chatroom</a>.",
+ "smw-admin-other-functions": "Other functions",
+ "smw-admin-supplementary-section-title": "Supplementary functions",
+ "smw-admin-supplementary-section-subtitle": "Available functions",
+ "smw-admin-supplementary-section-intro": "This section provides additional functions beyond the scope of maintenance and it is possible that some functions that are listed in the [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentation] are restricted or unavailable and therefore inaccessible on this wiki.",
+ "smw-admin-supplementary-settings-title": "Configuration settings",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> outputs a collective list of available settings used in Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Operational statistics",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> displays an extended set of statistics",
+ "smw-admin-supplementary-idlookup-title": "Entity lookup and disposal",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contains functions to lookup and dispose individual entities",
+ "smw-admin-supplementary-duplookup-title": "Duplicate entities",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> to list entries that are categorized as to contain duplicates in the entity table",
+ "smw-admin-supplementary-duplookup-docu": "This page lists entries from the [https://www.semantic-mediawiki.org/wiki/Help:Entity_table entity table] that have been categorized as duplicates. Duplicate entries should (if at all) only occur on rare occasions potentially caused by a terminated process during a database update or an unsuccessful rollback transaction.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Cache statistics",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> shows cache related statistics",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informs about settings and index statistics",
+ "smw-admin-supplementary-elastic-docu": "This page contains information about settings, mappings, health, and index statistics related to an Elasticsearch cluster that is connected to Semantic MediaWiki and its [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Available functions",
+ "smw-admin-supplementary-elastic-settings-title": "Settings",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> used by Elasticsearch to manage Semantic MediaWiki indices",
+ "smw-admin-supplementary-elastic-mappings-title": "Mappings",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> to list indices and field mappings",
+ "smw-admin-supplementary-elastic-mappings-docu": "This page contains field mapping details as there are used with the current indices. The mapping summary should be monitored in connection with the <code>index.mapping.total_fields.limit</code> that specifies the maximum number of fields in an index.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Summary",
+ "smw-admin-supplementary-elastic-mappings-fields": "Field mappings",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodes",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> shows node statistics",
+ "smw-admin-supplementary-elastic-indices-title": "Indices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> provides an overview of available indices and their statistics",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistics",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> shows index level statistics",
+ "smw-admin-supplementary-elastic-statistics-docu": "This page provides an insight on indices statistics for different operations that are happening on an index level, the returned stats are aggregated with primaries and total aggregations. The [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html help page] contains a detailed description of available indices stats.",
+ "smw-admin-supplementary-elastic-status-replication": "Replication status",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Last active replication: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Refresh interval: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Recovery job backlog: $1 (estimation)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Injest (file) job backlog: $1 (estimation)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replication locked: $1 (rebuild in-progress)",
+ "smw-list-count": "The list contains $1 entry (or entries).",
+ "smw-list-count-from-cache": "The list contains $1 entry (or entries) and was retrieved from cache (UTC: $2).",
+ "smw-property-label-uniqueness" : "The \"$1\" label was matched to at least one other property representation. Please consult the [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness help page] on how to resolve this issue.",
+ "smw-property-label-similarity-title": "Property label similarity report",
+ "smw-property-label-similarity-intro": "<u>$1</u> calculates similarities for existing property labels",
+ "smw-property-label-similarity-threshold": "Threshold:",
+ "smw-property-label-similarity-type": "Display Type ID",
+ "smw-property-label-similarity-noresult": "No results were found for the selected options.",
+ "smw-property-label-similarity-docu": "Compares and reports the [https://www.semantic-mediawiki.org/wiki/Property_similarity syntactic similarity] (not the semantic similarity) between two property labels which may help filter misspelled or equivalent properties that represent the same concept (see special page [[Special:Properties|Properties]] to clarify concept and usage of reported properties). The threshold can be adjusted to either widen or narrow the similarity distance. <code>[[Property:$1|$1]]</code> is used to exempt properties from the analysis.",
+ "smw-admin-operational-statistics": "This page contains operational statistics collected in or from Semantic MediaWiki related functions. An extended list of wiki specific statistics can be found [[Special:Statistics|<b>here</b>]].",
+ "smw_adminlinks_datastructure": "Data structure",
+ "smw_adminlinks_displayingdata": "Data display",
+ "smw_adminlinks_inlinequerieshelp": "Inline queries help",
+ "smw-page-indicator-usage-count": "Estimated [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count usage count]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|User|System}} defined property",
+ "smw-property-indicator-last-count-update": "Estimated usage count\nLast updated: $1",
+ "smw-concept-indicator-cache-update": "Cache count\nLast updated: $1",
+ "smw-createproperty-isproperty": "It is a property of type $1.",
+ "smw-createproperty-allowedvals": "The allowed {{PLURAL:$1|value for this property is|values for this property are}}:",
+ "smw-paramdesc-category-delim": "The delimiter",
+ "smw-paramdesc-category-template": "A template to format the items with",
+ "smw-paramdesc-category-userparam": "A parameter to pass to the template",
+ "smw-info-par-message": "Message to display.",
+ "smw-info-par-icon": "Icon to show, either \"info\" or \"warning\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "General options",
+ "prefs-ask-options": "Special:Ask options",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (and related extensions) provide individual customization for some selected functions. Please consult the [https://www.semantic-mediawiki.org/wiki/Help:User_preferences help page] for a detailed description.",
+ "smw-prefs-ask-options-tooltip-display": "Display parameter text as an info tooltip",
+ "smw-prefs-ask-options-compact-view-basic": "Enable basic compact view",
+ "smw-prefs-help-ask-options-compact-view-basic": "If enabled, displays a reduced set of links on the Special:Ask compact view.",
+ "smw-prefs-general-options-time-correction": "Enable time correction for special pages using the local [[Special:Preferences#mw-prefsection-rendering|time offset]] preference",
+ "smw-prefs-general-options-jobqueue-watchlist": "Show the job queue watchlist in my personal bar",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "If enabled, shows a [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist list of] pending selected jobs together with their estimated queue sizes.",
+ "smw-prefs-general-options-disable-editpage-info": "Disable the introductory text on the edit page",
+ "smw-prefs-general-options-disable-search-info": "Disable the syntax support information on the standard search page",
+ "smw-prefs-general-options-suggester-textinput": "Enable input assistance for semantic entities",
+ "smw-prefs-help-general-options-suggester-textinput": "If enabled, allows to use an [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance input assistance] to find properties, concepts, and categorties from an input context.",
+ "smw-ui-tooltip-title-property": "Property",
+ "smw-ui-tooltip-title-quantity": "Unit conversion",
+ "smw-ui-tooltip-title-info": "Information",
+ "smw-ui-tooltip-title-service": "Service links",
+ "smw-ui-tooltip-title-warning": "Warning",
+ "smw-ui-tooltip-title-error": "Error",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Event",
+ "smw-ui-tooltip-title-note": "Note",
+ "smw-ui-tooltip-title-legend": "Legend",
+ "smw-ui-tooltip-title-reference": "Reference",
+ "smw_unknowntype": "The \"$1\" type of this property is invalid",
+ "smw-concept-cache-text": "The concept has a total of $1 {{PLURAL:$1|page|pages}}, and was last updated $3, $2.",
+ "smw_concept_header": "Pages of concept \"$1\"",
+ "smw_conceptarticlecount": "Showing below $1 {{PLURAL:$1|page|pages}}.",
+ "smw-qp-empty-data": "Requested data could not be displayed due to some insufficient selection criteria.",
+ "right-smw-admin": "Access administration tasks (Semantic MediaWiki)",
+ "right-smw-patternedit": "Edit access to maintain allowed regular expressions and patterns (Semantic MediaWiki)",
+ "right-smw-pageedit": "Edit access for <code>Is edit protected</code> annotated pages (Semantic MediaWiki)",
+ "right-smw-ruleedit": "Edit rule pages (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "protected (only eligible users)",
+ "action-smw-patternedit": "edit regular expressions used by Semantic MediaWiki",
+ "action-smw-pageedit": "edit pages annotated with <code>Is edit protected</code> (Semantic MediaWiki)",
+ "group-smwadministrator": "Administrators (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrator (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Administrators (Semantic MediaWiki)",
+ "group-smwcurator": "Curators (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|curator (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Curators (Semantic MediaWiki)",
+ "action-smw-admin": "access Semantic MediaWiki administration tasks",
+ "action-smw-ruleedit": "edit rule pages (Semantic MediaWiki)",
+ "smw-property-predefined-default": "\"$1\" is a predefined property.",
+ "smw-property-predefined-common": "This property is pre-deployed (also known as [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property]) and comes with additional administrative privileges but can be used just like any other [https://www.semantic-mediawiki.org/wiki/Property user-defined property].",
+ "smw-property-predefined-ask": "\"$1\" is a predefined property that represents meta information (in form of a [https://www.semantic-mediawiki.org/wiki/Subobject subobject]) about individual queries and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "\"$1\" is a predefined property that collects the number of conditions used in a query and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" is a predefined property that informs about the depth of a query and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "It is a numerical value computed on the basis of subquery nesting, property chains, and available description elements with the execution of a query being restricted by the <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code> configuration parameter.",
+ "smw-property-predefined-askpa": "\"$1\" is a predefined property describing parameters that influence a query result and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askpa": "It is part of a collection of properties that specify a [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler query profile].",
+ "smw-sp-properties-docu": "This page lists [https://www.semantic-mediawiki.org/wiki/Property properties] and their usage counts available for this wiki. For up-to-date count statistics it is recommended that the [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics property statistics] maintenance script is run on a regular basis. For a differentiated view, see the [[Special:UnusedProperties|unused]] or [[Special:WantedProperties|wanted properties]] special pages.",
+ "smw-sp-properties-cache-info": "The listed data have been retrieved from [https://www.semantic-mediawiki.org/wiki/Caching cache], and were last updated $1.",
+ "smw-sp-properties-header-label": "List of properties",
+ "smw-admin-settings-docu": "Displays a list of all default and localized settings that are relevant to the Semantic MediaWiki environment. For details on individual settings, please consult the [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] help page.",
+ "smw-sp-admin-settings-button": "Generate settings list",
+ "smw-admin-idlookup-title": "Lookup",
+ "smw-admin-idlookup-docu": "This section shows technical details about an individual entity (wikipage, subobject, property, etc.) in Semantic MediaWiki. The input can be a numeric ID or a string value to match the relevant search field, yet any ID reference relates to Semantic MediaWiki and not to MediaWiki's page or revision ID.",
+ "smw-admin-iddispose-title": "Disposal",
+ "smw-admin-iddispose-docu": "It should be noted that the disposal operation is unrestricted and will remove the entity from the storage engine together with all its references in pending tables, if confirmed. Please perform this task with '''caution''' and only after the [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentation] has been consulted.",
+ "smw-admin-iddispose-done": "ID \"$1\" was removed from the storage backend.",
+ "smw-admin-iddispose-references": "ID \"$1\" has {{PLURAL:$2|no|at least one}} active reference:",
+ "smw-admin-iddispose-references-multiple": "List of matches with at least one active reference record.",
+ "smw-admin-iddispose-no-references": "The search was unable to match \"$1\" to a table entry.",
+ "smw-admin-idlookup-input": "Search:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Overview",
+ "smw-admin-tab-notices": "Deprecation notices",
+ "smw-admin-tab-rebuild": "Maintenance",
+ "smw-admin-tab-supplement": "Supplementary functions",
+ "smw-admin-tab-registry": "Registry",
+ "smw-livepreview-loading": "Loading...",
+ "smw-sp-searchbyproperty-description": "This page provides a simple [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces browsing interface] for finding entities described by a property and a named value. Other available search interfaces include the [[Special:PageProperty|page property search]], and the [[Special:Ask|ask query builder]].",
+ "smw-sp-searchbyproperty-resultlist-header": "List of results",
+ "smw-sp-searchbyproperty-nonvaluequery": "A list of values that have the property \"$1\" assigned.",
+ "smw-sp-searchbyproperty-valuequery": "A list of pages that have property \"$1\" with value \"$2\" annotated.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" can not be assigned to a declared number type with value $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" returned with a \"NULL\" which is not allowed as number.",
+ "smw-editpage-annotation-enabled":"This page supports semantic in-text annotations (e.g. <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) to build structured and queryable content provided by Semantic MediaWiki. For a comprehensive description on how to use annotations or the #ask parser function, please have a look at the [https://www.semantic-mediawiki.org/wiki/Help:Getting_started getting started], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation in-text annotation], or [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries] help pages.",
+ "smw-editpage-annotation-disabled":"This page is not enabled for semantic in-text annotations due to namespace restrictions. Details about how to enable the namespace can be found on the [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] help page.",
+ "smw-editpage-property-annotation-enabled":"This property can be extended using semantic annotations to specify a datatype (e.g. <nowiki>\"[[Has type::Page]]\"</nowiki>) or other supporting declarations (e.g. <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). For a description on how to augment this page, see the [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaration of a property] or the [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes list of available data types] help page.",
+ "smw-editpage-property-annotation-disabled":"This property cannot be extended with a datatype annotation (e.g. <nowiki>\"[[Has type::Page]]\"</nowiki>) as it is already predefined (see the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special properties] help page for more information).",
+ "smw-editpage-concept-annotation-enabled":"This concept can be extended using the #concept parser function. For a description on how to use #concept, see the [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] help page.",
+ "smw-search-syntax-support":"The search input supports the use of the semantic [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search query syntax] to help match results using Semantic MediaWiki.",
+ "smw-search-input-assistance": "The [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance input assistant] is also enabled to ease the pre-selection of available properties and categories.",
+ "smw-search-help-intro": "A <code><nowiki>[[ ... ]]</nowiki></code> input will signal to the input processor to use the Semantic MediaWiki search back-end. It should be noted that combining <code><nowiki>[[ ... ]]</nowiki></code> with an unstructured text search such as <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code> is not supported.",
+ "smw-search-help-structured": "Structured searches:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (as [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context filtered context])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (with a [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context query context])",
+ "smw-search-help-proximity": "Proximity searches (a property being unknown, '''only''' available for those back-ends that provide a full-text search integration):\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (search in all documents for \"lorem\" and \"ipsum\" that have been indexed)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (match \"lorem ipsum\" as phrase)",
+ "smw-search-help-ask": "The following links will explain how to use the <code>#ask</code> syntax.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecting pages] describes how to selected pages and build conditions\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Search operators] lists available search operators including those for range and wildcard queries",
+ "smw-search-input": "Input and search",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Input assistance] is provided for the input field and requires to use one of following prefixes:\n\n*<code>p:</code> to enable property suggestions (e.g. <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> to enable category suggestions\n\n*<code>con:</code> to enable concept suggestions",
+ "smw-search-syntax": "Syntax",
+ "smw-search-profile": "Extended",
+ "smw-search-profile-tooltip": "Search functions in connection with Semantic MediaWiki",
+ "smw-search-profile-sort-best": "Best match",
+ "smw-search-profile-sort-recent": "Most recent",
+ "smw-search-profile-sort-title": "Title",
+ "smw-search-profile-extended-help-intro": "The Special:Search [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile extended profile] provides access to search functions specific to Semantic MediaWiki and its supported query backend.",
+ "smw-search-profile-extended-help-sort": "Specifies a sorting preference for the result display with:",
+ "smw-search-profile-extended-help-sort-title": "* \"Title\" using the page title (or display title) as sort criteria",
+ "smw-search-profile-extended-help-sort-recent": "* \"Most recent\" will show the most recent modified entities first (subobject entities will be suppressed as those entities are not annotated with a [[Property:Modification date|Modification date]])",
+ "smw-search-profile-extended-help-sort-best": "* \"Best match\" will sort entities by [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy relevancy] based on scores provided by the backend",
+ "smw-search-profile-extended-help-form": "Forms are provided (if maintained) to match specific use cases by exposing different property and value fields to narrow down the input process and make it easy for users to proceed with a search request. (see $1)",
+ "smw-search-profile-extended-help-namespace": "The namespace selection box will be hidden as soon as a form is selected but can be made visible with the help of the \"show/hide\" button.",
+ "smw-search-profile-extended-help-search-syntax": "The search input field supports the use of the <code>#ask</code> syntax to define a Semantic MediaWiki specific search context. Useful expressions include:",
+ "smw-search-profile-extended-help-search-syntax-simplified-in":"* <code>in:</code> to find anything that contains \"...\" and is especially useful when the search context or properties involved are unknown (e.g. <code>in:(lorem && ipsum)</code> is equivalent to <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>).",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase":"* <code>phrase:</code> to find anything that contains \"...\" in the exact same order",
+ "smw-search-profile-extended-help-search-syntax-simplified-has":"* <code>has:</code> to match any entity with a property \"...\" (e.g. <code>has:(Foo && Bar)</code> is equivalent to <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not":"* <code>not:</code> to not match any entity that includes \"...\"",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Additional custom prefixes are available and defined such as: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Some expressions are reserved such as: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''Some of the listed operations are only useful in connection with an enabled full-text index or the ElasticStore.''",
+ "smw-search-profile-extended-help-query": "Used <code><nowiki>$1</nowiki></code> as query.",
+ "smw-search-profile-extended-help-query-link": "(For more details $1).",
+ "smw-search-profile-extended-help-find-forms": "available forms",
+ "smw-search-profile-extended-section-sort": "Sort by",
+ "smw-search-profile-extended-section-form": "Forms",
+ "smw-search-profile-extended-section-search-syntax": "Search input",
+ "smw-search-profile-extended-section-namespace": "Namespace",
+ "smw-search-profile-extended-section-query": "Query",
+ "smw-search-profile-link-caption-query": "see",
+ "smw-search-show": "Show",
+ "smw-search-hide": "Hide",
+ "log-name-smw": "Semantic MediaWiki log",
+ "log-show-hide-smw": "$1 Semantic MediaWiki log",
+ "logeventslist-smw-log": "Semantic MediaWiki log",
+ "log-description-smw": "Activities for [https://www.semantic-mediawiki.org/wiki/Help:Logging enabled event types] that have been reported by Semantic MediaWiki and its components.",
+ "logentry-smw-maintenance":"Maintenance related events emitted by Semantic MediaWiki",
+ "smw-datavalue-import-unknown-namespace":"The import namespace \"$1\" is unknown. Please ensure that OWL import details are available via [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri":"Unable to find a \"$1\" namespace URI in the [[MediaWiki:Smw import $1|$1 import]].",
+ "smw-datavalue-import-missing-type":"No type definition was found for \"$1\" in the [[MediaWiki:Smw import $2|$2 import]].",
+ "smw-datavalue-import-link":"[[MediaWiki:Smw import $1|$1 import]]",
+ "smw-datavalue-import-invalid-value":"\"$1\" is not a valid format and is expected to consist of \"namespace\":\"identifier\" (e.g. \"foaf:name\").",
+ "smw-datavalue-import-invalid-format": "Expected the string \"$1\" to be divided into four parts but the format was not understood.",
+ "smw-property-predefined-impo": "\"$1\" is a predefined property that describes a relation to an [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary imported vocabulary] and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "\"$1\" is a predefined property that describes the [[Special:Types|datatype]] of a property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "\"$1\" is a predefined property representing a [https://www.semantic-mediawiki.org/wiki/Help:Container container] construct and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "The container allows to accumulate property-value assignments similar to that of a normal wiki page but within a different entity space while being linked to the embedding subject.",
+ "smw-property-predefined-errp": "\"$1\" is a predefined property that tracks input errors for irregular value annotations and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-errp": "In most cases it is caused by a type mismatch or a [[Property:Allows value|value]] restriction.",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] is a predefined property that can define a list of permissible values to restrict value assignments for a property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] is a predefined property that can specify a reference to a list that holds permissible values to restrict value assignments for a property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-property-restricted-annotation-use": "Property \"$1\" has a restricted application area and cannot be used as annotation property by a user.",
+ "smw-datavalue-property-restricted-declarative-use": "Property \"$1\" is a declarative property and can only be used on a property or category page.",
+ "smw-datavalue-property-create-restriction": "Property \"$1\" doesn't exist and the user is missing the \"$2\" permission (see [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode]) to create or annotate values with an unapproved property.",
+ "smw-datavalue-property-invalid-character": "\"$1\" contains a listed \"$2\" character as part of the property label and has therefore been classified as invalid.",
+ "smw-datavalue-property-invalid-chain": "Using \"$1\" as property chain is not permitted during the annotation process.",
+ "smw-datavalue-restricted-use": "Datavalue \"$1\" has been marked for restricted use.",
+ "smw-datavalue-invalid-number": "\"$1\" can not be interpreted as a number.",
+ "smw-query-condition-circular": "A possible circular condition has been detected in \"$1\".",
+ "smw-query-condition-empty": "The query description has an empty condition.",
+ "smw-types-list": "List of datatypes",
+ "smw-types-default": "\"$1\" is a built-in datatype.",
+ "smw-types-help": "Further information and examples can be found on this [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 help page].",
+ "smw-type-anu": "\"$1\" is a variant of the [[Special:Types/URL|URL]] datatype and is mostly used for a ''owl:AnnotationProperty'' export declaration.",
+ "smw-type-boo": "\"$1\" is a basic datatype to describe a true/false value.",
+ "smw-type-cod": "\"$1\" is a variant of the [[Special:Types/Text|Text]] datatype to be used for technical texts of arbitrary length, such as source code listings.",
+ "smw-type-geo": "\"$1\" is a datatype that describes geographic locations and requires the [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"] extension.",
+ "smw-type-tel": "\"$1\" is a special datatype to describe international telephone numbers according to RFC 3966.",
+ "smw-type-txt": "\"$1\" is a basic datatype to describe strings of arbitrary length.",
+ "smw-type-dat": "\"$1\" is a basic datatype to represent points in time in a unified format.",
+ "smw-type-ema": "\"$1\" is a special datatype to represent an email.",
+ "smw-type-tem": "\"$1\" is a special numeric datatype to represent a temperature.",
+ "smw-type-qty": "\"$1\" is a datatype to describe quantities with a numeric representation and a unit of measurement.",
+ "smw-type-rec": "\"$1\" is a container datatype that specifies a list of typed properties in a fixed order.",
+ "smw-type-extra-tem": "The conversion schema includes supported units such as Kelvin, Celsius, Fahrenheit, and Rankine.",
+ "smw-type-tab-properties": "Properties",
+ "smw-type-tab-types": "Types",
+ "smw-type-tab-errors": "Errors",
+ "smw-type-primitive": "Basic",
+ "smw-type-contextual": "Contextual",
+ "smw-type-compound": "Compound",
+ "smw-specials-browse-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:Browse",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "This page provides a browsing interface for finding all values of a property and a given page. Other available search interfaces include the [[Special:SearchByProperty|property search]], and the [[Special:Ask|ask query builder]].",
+ "smw-property-predefined-errc": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] and represents errors that appeared in connection with improper value annotations or input processing.",
+ "smw-property-predefined-long-errc": "Errors are collected in a [https://www.semantic-mediawiki.org/wiki/Help:Container container] that may include a reference to the property that caused the discrepancy.",
+ "smw-property-predefined-errt": "\"$1\" is a predefined property containing a textual description of an error and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "A user-defined subobject contained an invalid naming scheme. The dot notation ($1) used within the first five characters is reserved for extensions. You may set a [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier named identifier].",
+ "smw-datavalue-record-invalid-property-declaration": "The record definition contains the \"$1\" property which itself is declared as record type and that is not permitted.",
+ "smw-property-predefined-mdat": "\"$1\" is a predefined property that corresponds to the date of the last modification of a subject and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "\"$1\" is a predefined property that corresponds to the date of the first revision of a subject and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "\"$1\" is a predefined property that indicates whether a subject is new or not and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "\"$1\" is a predefined property that contains the page name of the user who created the last revision and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "\"$1\" is a predefined property that describes the MIME type of an uploaded file and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "\"$1\" is a predefined property that describes the media type of an uploaded file and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "\"$1\" is a predefined property that holds the name of the result format used in a query and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "\"$1\" is a predefined property that describes the conditions of the query as a string and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "\"$1\" is a predefined property containing a time value (in seconds) that was required to complete the query execution and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksc": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] that identifies alternative (e.g. remote, federated) query sources.",
+ "smw-property-predefined-askco": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to describe the state of a query or its components.",
+ "smw-property-predefined-long-askco": "The number or numbers assigned represent an internal codified state that is explained on the [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler help page].",
+ "smw-property-predefined-prec": "\"$1\" is a predefined property that describes a [https://www.semantic-mediawiki.org/wiki/Help:Display_precision display precision] (in decimal digits) for numeric datatypes.",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Extension:Maps Extension \"Maps\"] was not detected therefore \"$1\" is restricted in its capacity to operate.",
+ "smw-datavalue-monolingual-dataitem-missing": "An expected item for building a monolingual compound value is missing.",
+ "smw-datavalue-monolingual-lcode-parenthesis":"($1)",
+ "smw-datavalue-languagecode-missing": "For the \"$1\" annotation, the parser was unable to determine a language code (i.e. \"foo@en\").",
+ "smw-datavalue-languagecode-invalid": "\"$1\" was not recognized as a supported language code.",
+ "smw-property-predefined-lcode": "\"$1\" is a predefined property that represents a BCP47 formatted language code and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "\"$1\" is a [https://www.semantic-mediawiki.org/wiki/Help:Container container] datatype that associates a text value with a specific [[Property:Language code|language code]].",
+ "smw-types-extra-mlt-lcode": "The datatype does {{PLURAL:$2|require|not require}} a language code (i.e. {{PLURAL:$2|a value annotation without a language code is not accepted|a value annotation without a language code is accepted}}).",
+ "smw-property-predefined-text": "\"$1\" is a predefined property that represents text of arbitrary length and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "\"$1\" is a predefined property that allows to describe a property in context of a language and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "\"$1\" is a predefined property to define a list of properties used with a [[Special:Types/Record|record]] typed property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] In-text annotation parser time",
+ "smw-limitreport-intext-postproctime": "[SMW] post processing time",
+ "smw-limitreport-intext-parsertime-value" : "$1 {{PLURAL:$1|second|seconds}}",
+ "smw-limitreport-intext-postproctime-value" : "$1 {{PLURAL:$1|second|seconds}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Store update time (on page purge)",
+ "smw-limitreport-pagepurge-storeupdatetime-value" : "$1 {{PLURAL:$1|second|seconds}}",
+ "smw_allows_pattern": "This page is expected to contain a list of references (followed by [https://en.wikipedia.org/wiki/Regular_expression regular expressions]) to be made available by the [[Property:Allows pattern|Allows pattern]] property. To edit this page, the <code>smw-patternedit</code> right is required.",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\" was classified as invalid by the \"$2\" regular expression.",
+ "smw-datavalue-allows-pattern-reference-unknown": "The \"$1\" pattern reference could not be matched to an entry in [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "The \"$1\" list reference was not matchable to a [[MediaWiki:Smw allows list $1]] page.",
+ "smw-datavalue-allows-value-list-missing-marker": "The \"$1\" list content is missing items with a * list marker.",
+ "smw-datavalue-feature-not-supported": "The \"$1\" feature is not supported or was disabled on this wiki.",
+ "smw-property-predefined-pvap": "\"$1\" is a predefined property that can specify a [[MediaWiki:Smw allows pattern|pattern reference]] to apply [https://en.wikipedia.org/wiki/Regular_expression regular expression] matching and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-dtitle": "\"$1\" is a predefined property that can assign a distinct display title to an entity and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvuc": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to restrict value assignments for each instance to be unique (or one at most).",
+ "smw-property-predefined-long-pvuc": "Uniqueness is established when two values are not equal in their literal representation and any violation of that constraint will be categorized as error.",
+ "smw-datavalue-uniqueness-constraint-error": "Property \"$1\" only permits unique value assignments and ''$2'' was already annotated in subject \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "Property \"$1\" only permits unique value annotations, ''$2'' already contains an assigned value. \"$3\" violates the uniqueness constraint.",
+ "smw-property-predefined-boo": "\"$1\" is a [[Special:Types/Boolean|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent boolean values.",
+ "smw-property-predefined-num": "\"$1\" is a [[Special:Types/Number|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent numeric values.",
+ "smw-property-predefined-dat": "\"$1\" is a [[Special:Types/Date|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent date values.",
+ "smw-property-predefined-uri": "\"$1\" is a [[Special:Types/URL|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent URI/URL values.",
+ "smw-property-predefined-qty": "\"$1\" is a [[Special:Types/Quantity|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent quantity values.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\" contains an offset and zone identifier which is not supported.",
+ "smw-datavalue-time-invalid-values": "The \"$1\" value contains uninterpretable information in form of \"$2\".",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" contains some uninterpretable information.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\" contains an extrinsic dash or other characters that are invalid for a date interpretation.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" contains some empty components.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" contains more than three components required for a date interpretation.",
+ "smw-datavalue-time-invalid-date-components-sequence": "\"$1\" contains a sequence that could not be interpreted against an available match matrix for date components.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" contains \"$2\" as hour element that is invalid for a 12-hour convention.",
+ "smw-datavalue-time-invalid-jd": "Unable to interpret the \"$1\" input value as valid JD (Julian day) number with \"$2\" being reported.",
+ "smw-datavalue-time-invalid-prehistoric": "Unable to interpret a prehistoric \"$1\" input value. For example, having specified more than years or a calendar model may return unexpected results in a prehistoric context.",
+ "smw-datavalue-time-invalid": "Unable to interpret the \"$1\" input value as valid date or time component with \"$2\" being reported.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\" contains an offset and zone identifier which is not supported.",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Formatter URI is missing the ''$1'' placeholder.",
+ "smw-datavalue-external-formatter-invalid-uri": " \"$1\" is an invalid URL.",
+ "smw-datavalue-external-identifier-formatter-missing": "The property is missing an [[Property:External formatter uri|\"External formatter URI\"]] assignment.",
+ "smw-datavalue-keyword-maximum-length": "The keyword exceeded the maximum length of $1 {{PLURAL:$1|character|characters}}.",
+ "smw-property-predefined-eid": "\"$1\" is a [[Special:Types/External identifier|type]] and predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to represent external identifiers.",
+ "smw-property-predefined-peid": "\"$1\" is a predefined property that specifies an external identifier and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pefu": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to specify an external resource with a placeholder.",
+ "smw-property-predefined-long-pefu": "The URI is expected to contain a placeholder that will be adjusted with an [[Special:Types/External identifier|external identifier]] value to form a valid resource reference.",
+ "smw-type-eid": "\"$1\" is a variant of the [[Special:Types/Text|Text]] datatype that requires assigned properties to declare an [[Property:External formatter uri|External formatter URI]].",
+ "smw-property-predefined-keyw": "\"$1\" is a predefined property and [[Special:Types/Keyword|type]] provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] that normalizes a text and has a restricted character length.",
+ "smw-type-keyw": "\"$1\" is a variant of the [[Special:Types/Text|Text]] datatype that has a restricted character length and normalizes its content representation.",
+ "smw-datavalue-stripmarker-parse-error": "The given value \"$1\" contains [https://en.wikipedia.org/wiki/Help:Strip_markers strip markers] and therefore it cannot be parsed sufficiently.",
+ "smw-datavalue-parse-error": "The given value \"$1\" was not understood.",
+ "smw-datavalue-propertylist-invalid-property-key": "The property list \"$1\" contained an invalid property key \"$2\".",
+ "smw-datavalue-type-invalid-typeuri": "The \"$1\" type could not be transformed into a valid URI representation.",
+ "smw-datavalue-wikipage-missing-fragment-context": "The wikipage input value \"$1\" cannot be used without a context page.",
+ "smw-datavalue-wikipage-invalid-title": "The page type input value \"$1\" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.",
+ "smw-datavalue-wikipage-property-invalid-title": "Property \"$1\" (as page type) with input value \"$2\" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process.",
+ "smw-datavalue-wikipage-empty": "The wikipage input value is empty (e.g. <code>[[SomeProperty::]], [[]]</code>) and therefore it cannot be used as a name or as part of a query condition.",
+ "smw-type-ref-rec": "\"$1\" is a [https://www.semantic-mediawiki.org/wiki/Container container] type that allows to record additional information (e.g. provenance data) about a value assignment.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "The [[Special:Types/Reference|Reference]] type expects a list of properties to be declared using the [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Has fields] property.",
+ "smw-parser-invalid-json-format": "The JSON parser returned with a \"$1\".",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" cannot be used as preferred label because the language \"$2\" is already assigned to the \"$3\" label.",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copy link to clipboard",
+ "smw-property-userdefined-fixedtable": "\"$1\" was configured as [https://www.semantic-mediawiki.org/wiki/Fixed_properties fixed property] and any modification to its [https://www.semantic-mediawiki.org/wiki/Type_declaration type declaration] requires to either run <code>setupStore.php</code> or to complete the special [[Special:SemanticMediaWiki|\"Database installation and upgrade\"]] task.",
+ "smw-data-lookup": "Fetching data ...",
+ "smw-data-lookup-with-wait": "The request is being processed and may take a moment.",
+ "smw-no-data-available": "No data available.",
+ "smw-property-req-violation-missing-fields": "Property \"$1\" is missing declaration details for the annotated \"$2\" type by failing to define the <code>Has fields</code> property.",
+ "smw-property-req-violation-missing-formatter-uri": "Property \"$1\" is missing declaration details for the annotated type by failing to define the <code>External formatter URI</code> property.",
+ "smw-property-req-violation-predefined-type": "Property \"$1\" as predefined property contains a \"$2\" type declaration that is incompatible with the default type of this property.",
+ "smw-property-req-violation-import-type": "A type declaration was detected that is incompatible with the predefined type of the imported \"$1\" vocabulary. In general, it is not necessary to declare a type because information are retrieved from the import definition.",
+ "smw-property-req-violation-change-propagation-locked-error": "Property \"$1\" was altered and requires assigned entities to be reevaluated using a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] process. The property page has been locked until the primary specification update is completed to prevent intermediary interruptions or contradictory specifications. The process may take moment before the page can be unlocked as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler.",
+ "smw-property-req-violation-change-propagation-locked-warning": "Property \"$1\" was altered and requires assigned entities to be reevaluated using a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] process. The update may take moment as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler and it is suggested to postpone changes to the property to prevent intermediary interruptions or contradictory specifications.",
+ "smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation Change propagation] updates are pending ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|job|jobs}}] estimated) and it is recommended to wait with modifications to a property until the process has been finalized to prevent intermediary interruptions or contradictory specifications.",
+ "smw-property-req-violation-missing-maps-extension": "Semantic MediaWiki was unable to detect the [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"] extension which is a prerequisite for the selected type and as consequence limits the functionality of this property.",
+ "smw-property-req-violation-type": "The property contains competing type specifications which may result in invalid value annotations therefore it is expected that a user assigns one appropriate type.",
+ "smw-change-propagation-protection": "This page is locked to prevent accidental data modification while a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] update is run. The process may take moment before the page is unlocked as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler.",
+ "smw-category-change-propagation-locked-error": "Category \"$1\" was altered and requires assigned entities to be reevaluated using a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] process. In the meantime, the category page has been locked until the primary specification update is completed to prevent intermediary interruptions or contradictory specifications. The process may take moment before the page can be unlocked as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler.",
+ "smw-category-change-propagation-locked-warning": "Category \"$1\" was altered and requires assigned entities to be reevaluated using a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] process. The update may take moment as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler and it is suggested to postpone changes to the category to prevent intermediary interruptions or contradictory specifications.",
+ "smw-category-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation Change propagation] updates are pending ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|job|jobs}}] estimated) and it is recommended to wait with modifications to a category until the process has been finalized to prevent intermediary interruptions or contradictory specifications.",
+ "smw-category-invalid-value-assignment": "\"$1\" is not recognized as valid category or value annotation.",
+ "protect-level-smw-pageedit":"Allow only users with page edit permission (Semantic MediaWiki)",
+ "smw-create-protection": "Creation of the \"$1\" property is restricted to users with the appropriate \"$2\" right (or [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group]) while the [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode] is enabled.",
+ "smw-create-protection-exists": "Changes to the \"$1\" property is restricted to users with the appropriate \"$2\" right (or [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group]) while the [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode] is enabled.",
+ "smw-edit-protection": "This page is [[Property:Is edit protected|protected]] to prevent accidental data modification and can only be edited by users with the appropriate edit right (\"$1\") or [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group].",
+ "smw-edit-protection-disabled": "The edit protection has been disabled therefore \"$1\" cannot be used to protect entity pages from unauthorized editing.",
+ "smw-edit-protection-auto-update":"Semantic MediaWiki has updated the protection status according to the \"Is edit protected\" property.",
+ "smw-edit-protection-enabled":"Edit protected (Semantic MediaWiki)",
+ "smw-patternedit-protection": "This page is protected and can only be edited by users with the appropriate <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions permission].",
+ "smw-property-predefined-edip": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to indicate whether editing is protected or not.",
+ "smw-property-predefined-long-edip": "While any user is qualified to add this property to a subject, only a user with a dedicated permission can edit or revoke the protection to an entity after it has been added.",
+ "smw-query-reference-link-label" : "Query reference",
+ "smw-format-datatable-emptytable": "No data available in table",
+ "smw-format-datatable-info": "Showing _START_ to _END_ of _TOTAL_ entries",
+ "smw-format-datatable-infoempty": "Showing 0 to 0 of 0 entries",
+ "smw-format-datatable-infofiltered": "(filtered from _MAX_ total entries)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "Show _MENU_ entries",
+ "smw-format-datatable-loadingrecords": "Loading...",
+ "smw-format-datatable-processing": "Processing...",
+ "smw-format-datatable-search": "Search:",
+ "smw-format-datatable-zerorecords": "No matching records found",
+ "smw-format-datatable-first": "First",
+ "smw-format-datatable-last": "Last",
+ "smw-format-datatable-next": "Next",
+ "smw-format-datatable-previous": "Previous",
+ "smw-format-datatable-sortascending": ": activate to sort column ascending",
+ "smw-format-datatable-sortdescending": ": activate to sort column descending",
+ "smw-format-datatable-toolbar-export": "Export",
+ "smw-format-list-separator": ", ",
+ "smw-format-list-property-separator": ", ",
+ "smw-format-list-value-separator": ", ",
+ "smw-format-list-field-label-separator": ": ",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "Category \"$1\" contains an invalid redirect target to a non-category namespace.",
+ "smw-parser-function-expensive-execution-limit": "The parser function has reached the limit for expensive executions (see configuration parameter [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "Semantic MediaWiki is refreshing the current page on the condition of some required query post processing.",
+ "apihelp-smwinfo-summary": "API module to retrieve information about Semantic MediaWiki statistics and other meta information.",
+ "apihelp-ask-summary": "API module to query Semantic MediaWiki using the ask language.",
+ "apihelp-askargs-summary": "API module to query Semantic MediaWiki using the ask language as list of conditions, printouts and parameters.",
+ "apihelp-browsebyproperty-summary": "API module to retrieve information about a property or list of properties.",
+ "apihelp-browsebysubject-summary": "API module to retrieve information about a subject.",
+ "apihelp-smwtask-summary": "API module to execute Semantic MediaWiki related tasks.",
+ "apihelp-smwbrowse-summary": "API module to support browse activties for different entity types in Semantic MediaWiki.",
+ "apihelp-ask-parameter-api-version": "Output formatting:\n;2:Backwards-compatible format using {} for the result list.\n;3:Experimental format using [] as result list.",
+ "smw-api-invalid-parameters": "Invalid parameters, \"$1\"",
+ "smw-parser-recursion-level-exceeded": "The level of $1 recursions was exceeded during a parse process. It is suggested to validated the template structure, or if necessary adjust configuration parameter <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "Showing $1 {{PLURAL:$1|page|pages}} using this property.",
+ "smw-property-page-list-search-count": "Showing $1 {{PLURAL:$1|page|pages}} using this property with a \"$2\" value match.",
+ "smw-property-reserved-category": "Category",
+ "smw-category": "Category",
+ "smw-datavalue-uri-invalid-scheme": " \"$1\" has not been listed as valid URI scheme.",
+ "smw-datavalue-uri-invalid-authority-path-component": "\"$1\" has been idendified to contain an invalid \"$2\" authority or path component.",
+ "smw-browse-property-group-title": "Property group",
+ "smw-browse-property-group-label": "Property group label",
+ "smw-browse-property-group-description": "Property group description",
+ "smw-property-predefined-ppgr": "\"$1\" is a predefined property that identifies entities (mainly categories) that are used as grouping instance for properties and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-filter": "Filter",
+ "smw-section-expand": "Expand the section",
+ "smw-section-collapse": "Collapse the section",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] format",
+ "smw-help": "Help",
+ "smw-cheat-sheet": "Cheat sheet",
+ "smw-personal-jobqueue-watchlist": "Watchlist (job queue)",
+ "smw-property-predefined-label-skey": "Sortkey",
+ "smw-processing": "Processing...",
+ "smw-redirect-target-unresolvable": "The target is unresolvable on the reason of \"$1\"",
+ "smw-types-title": "Type: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "Changing the content model of a [https://www.semantic-mediawiki.org/wiki/Help:Schema schema page] is not permitted.",
+ "smw-schema-namespace-edit-protection": "This page is protected and can only be edited by users with the appropriate <code>smw-schemaedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions permission].",
+ "smw-schema-error": "Validation error",
+ "smw-schema-error-schema": "'''$1''' specification and the validation thereof for the current schema has found the following incompatibilities or violations:",
+ "smw-schema-error-violation": "Violation (\"$1\", \"$2\")",
+ "smw-schema-error-type-missing": "The content is missing a type in order for it to be recognized and usable in the [https://www.semantic-mediawiki.org/wiki/Help:Schema schema namespace].",
+ "smw-schema-error-type-unknown": "The \"$1\" type is not registered and can therefore not be used for content in the [https://www.semantic-mediawiki.org/wiki/Help:Schema schema namespace].",
+ "smw-schema-title": "Schema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Type",
+ "smw-schema-description-link-format-schema": "This schema type is expected to define characteristics for creating context sensitive links in connection with a [[Property:Formatter schema|formatter schema]] assigned property.",
+ "smw-schema-description-search-form-schema": "This schema type is expected to be used to define input forms and characteristics for the [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch extended search] profile where it contains instructions on how to generate input fields, define default namespaces, or declare prefix expressions for a search request.",
+ "smw-schema-tag": "{{PLURAL:$1|Tag|Tags}}",
+ "smw-property-predefined-schema-desc": "\"$1\" is a predefined property that stores a schema description and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-schema-def": "\"$1\" is a predefined property that stores the schema content and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-schema-tag": "\"$1\" is a predefined property provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] to identify a collection of schemata.",
+ "smw-property-predefined-long-schema-tag": "A label that identifies schemata of similar content or characteristics.",
+ "smw-property-predefined-schema-type": "\"$1\" is a predefined property that describes a type of distinguishable schema and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-schema-type": "Each [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type type] is expected to provide its own interpretation of syntax elements and constraints and expresses those through a [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation validation model].",
+ "smw-ask-title-keyword-type":"Keyword search",
+ "smw-ask-message-keyword-type": "This search matches the <code><nowiki>$1</nowiki></code> condition.",
+ "smw-remote-source-unavailable": "Unable to connect to the remote \"$1\" target.",
+ "smw-remote-source-disabled": "The '''$1''' source has disabled the remote request support!",
+ "smw-remote-source-unmatched-id": "The '''$1''' source does not match a version of Semantic MediaWiki that can support a remote request.",
+ "smw-remote-request-note": "The result is fetched from the '''$1''' remote source and it is likely for generated content to contain information that is not available from within the current wiki.",
+ "smw-remote-request-note-cached": "The result is '''cached''' from the '''$1''' remote source and it is likely for generated content to contain information that is not available from within the current wiki.",
+ "smw-parameter-missing": "Parameter \"$1\" is missing.",
+ "smw-property-tab-usage": "Usage",
+ "smw-property-tab-redirects": "Synonyms",
+ "smw-property-tab-subproperties": "Subproperties",
+ "smw-property-tab-errors": "Improper assignments",
+ "smw-property-tab-specification": "... more",
+ "smw-concept-tab-list": "List",
+ "smw-concept-tab-errors": "Errors",
+ "smw-ask-tab-result": "Result",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Debug",
+ "smw-ask-tab-code": "Code",
+ "smw-helplink-concepts": "https://www.semantic-mediawiki.org/wiki/Help:Concepts",
+ "smw-install-incomplete-intro": "The installation (or upgrade) of <b>Semantic MediaWiki</b> has not been finalized and an administrator should run the following tasks to prevent data inconsistencies before users continue to create or alter content.",
+ "smw-install-incomplete-populate-hash-field": "The <code>smw_hash</code> field population was skipped during the setup, the [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php] script is required to be executed."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/eo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/eo.json
new file mode 100644
index 00000000..8cf9ac99
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/eo.json
@@ -0,0 +1,107 @@
+{
+ "@metadata": {
+ "authors": [
+ "Michawiki",
+ "Yekrats",
+ "Robin van der Vliet"
+ ]
+ },
+ "smw_viewasrdf": "RDF-fonto",
+ "smw_finallistconjunct": ", kaj",
+ "smw_factbox_head": "Faktoj pri $1",
+ "smw_isspecprop": "Ĉi tiu atributo estas speciala atributo en ĉi tiu vikio.",
+ "smw_concept_description": "Priskribo de koncepto \"$1\"",
+ "smw_no_concept_namespace": "Konceptoj povas nur esti difinita en paÄoj en la nomspaco Concept:.",
+ "smw_baduri": "BedaÅ­rinde, URI-oj de la kamparo \"$1\" ne estas permesita.",
+ "smw_printername_csv": "CSV-eksporto",
+ "smw_printername_list": "Listo",
+ "smw_printername_table": "Tabelo",
+ "smw_printername_broadtable": "LarÄa tabelo",
+ "smw_printername_template": "Åœablono",
+ "smw_printername_category": "Kategorio",
+ "smw_iq_disabled": "Bedaŭrinde, semantikaj informmendoj estis malebligitaj por ĉi tiu vikio.",
+ "smw_iq_moreresults": "… pluaj rezultoj",
+ "smw_parseerror": "La donata valoro ne estas komprenita.",
+ "smw_notitle": "\"$1\" ne eblas esti uzata kiel paÄnomo en ĉi tiu vikio.",
+ "smw_manytypes": "Pli ol unu tipo estas difinita por atributo.",
+ "smw_emptystring": "Malplenaj ĉenoj ne estas akceptitaj.",
+ "smw_notinenum": "\"$1\" ne estas en la listo de eblaj valoroj ($2) por ĉi tiu atributo.",
+ "smw_noboolean": "\"$1\" ne estas agnoskita kiel Bulea (vera/falsa) valoro.",
+ "smw_true_words": "vera,v,jes,j",
+ "smw_false_words": "falsa,f,ne,n",
+ "smw_nofloat": "\"$1\" ne estas nombro.",
+ "smw_nodatetime": "La dato \"$1\" ne estis komprenita.",
+ "smw_toomanyclosing": "VerÅajne estas tro da okazoj de \"$1\" en la mendo.",
+ "smw_misplacedsymbol": "La signo \"$1\" estis uzita en loko kie Äi ne estas utila.",
+ "smw_emptysubquery": "Iu submendo havas neniun validan kondiĉon.",
+ "smw_propvalueproblem": "La valoro de atributo \"$1\" ne estas komprenita.",
+ "smw_type_header": "Atributoj de tipo \"$1\"",
+ "smw_typearticlecount": "Montrante $1 {{PLURAL:$1|atributon|atributojn}} uzante ĉi tiun tipon.",
+ "smw_attribute_header": "PaÄoj uzantaj atrubuton \"$1\"",
+ "smw_attributearticlecount": "Jen $1 {{PLURAL:$1|paÄo havanta|paÄoj havantaj}} ĉi tiun atributon.",
+ "smw_subproperty_header": "Subatributoj",
+ "smw_subpropertyarticlecount": "Ĉi tiu atributo havas la $1 {{PLURAL:$1|jenan subatributon|jenajn subatributojn}}.",
+ "exportrdf": "Eksporti paÄojn al RDF",
+ "smw_exportrdf_submit": "Eksporti",
+ "properties": "Atributoj",
+ "smw_properties_docu": "La jenaj atributoj estas uzataj en la vikio.",
+ "smw_property_template": "$1 de datumtipo $2 ($3)",
+ "unusedproperties": "Neuzitaj atributoj",
+ "smw_unusedproperty_template": "$1 de datumtipo $2",
+ "wantedproperties": "Volitaj atributoj",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|uzo|uzoj}})",
+ "smw_purge": "RefreÅigi",
+ "types": "Datumtipoj",
+ "smw_types_docu": "Jen listo de ĉiuj datentipoj kiu povas esti asignitaj al atributoj.\nĈiu datentipo havas paÄon kie plua informo povas esti aldonita.",
+
+ "ask": "Semantika serĉo",
+ "smw_ask_sortby": "Ordigi laÅ­ kolumnoj (nedevige)",
+ "smw_ask_submit": "Trovi rezultojn",
+ "smw_ask_editquery": "Redakti serĉomendon",
+ "smw_add_sortcondition": "[Aldoni ordigan kondiĉon]",
+ "smw_ask_hidequery": "KaÅi serĉmendon",
+ "smw_ask_help": "Helpo pri serĉomendoj",
+ "smw_ask_queryhead": "Serĉomendo",
+ "smw_ask_format_as": "Formati kiel:",
+ "smw_ask_defaultformat": "defaÅ­lta",
+ "smw_ask_otheroptions": "Aliaj opcioj",
+ "smw_ask_show_embed": "Montri enkorpigitan kodon",
+ "smw_ask_hide_embed": "KaÅi enkorpigitan kodon",
+ "searchbyproperty": "Serĉi laŭ atributo",
+ "smw_sbv_property": "Atributo:",
+ "smw_sbv_value": "Valoro:",
+ "smw_sbv_submit": "Trovi rezultojn",
+ "browse": "Foliumi vikion",
+ "smw_browselink": "Trovidi atributojn",
+ "smw_browse_article": "Enigi la nomon de la paÄo por komenci retumadon.",
+ "smw_browse_go": "Ek",
+ "smw_browse_show_incoming": "malkaÅi atributojn kiu enligiÄas ĉi tien",
+ "smw_browse_hide_incoming": "kaÅi atributojn kiu enligiÄas ĉi tien",
+ "smw_browse_no_outgoing": "Ĉi tiu paÄo havas neniujn atributojn.",
+ "smw_browse_no_incoming": "Neniuj atributojn ligas al ĉi tiun paÄon.",
+ "smw_inverse_label_default": "$1 el",
+ "smw_inverse_label_property": "Inversa atributa etikedo",
+ "pageproperty": "Serĉo de paÄaj atributoj",
+ "smw_pp_from": "De paÄo",
+ "smw_pp_type": "Atributo",
+ "smw_pp_submit": "Trovi rezultojn",
+ "smw_result_prev": "AntaÅ­e",
+ "smw_result_next": "Sekve",
+ "smw_result_results": "Rezultoj",
+ "smw_result_noresults": "BedaÅ­rinde, neniuj rezultoj",
+ "smwadmin": "Administradaj funkcioj por Semantic MediaWiki",
+ "smw_smwadmin_return": "Reiri al $1",
+ "smw_smwadmin_db": "Datenbaza instalado kaj promociado",
+ "smw_smwadmin_announce": "Anonci vian vikion",
+ "smw_smwadmin_datarefreshstop": "Halti ĉi tiun Äisdatigon",
+ "smw_smwadmin_datarefreshstopconfirm": "Jes, mi certas.",
+ "smw_smwadmin_support": "Teni subtenon",
+ "smw_smwadmin_mediazilla": "Cimoj povas esti raportitaj al <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw_adminlinks_datastructure": "Datumstrukturo",
+ "smw-property-page-indicator-type-info": "{{PLURAL:$1|Uzanto|Sistemo}}",
+ "smw-createproperty-isproperty": "Ĉi tiu estas atributo de speco $1.",
+ "smw-createproperty-allowedvals": "La {{PLURAL:$1|permesita valoro por ĉi tiu atributo|permesitaj valoroj por ĉi tiuj atributoj}} estas:",
+ "smw_unknowntype": "Nesubtenita datumtipo \"$1\" difinita por atributo.",
+ "smw_concept_header": "PaÄoj de koncepto \"$1\"",
+ "smw-livepreview-loading": "ÅœarÄante..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/es-formal.json b/www/wiki/extensions/SemanticMediaWiki/i18n/es-formal.json
new file mode 100644
index 00000000..5f663b35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/es-formal.json
@@ -0,0 +1,60 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ivanhercaz"
+ ]
+ },
+ "smw-desc": "Hacemos que tu wiki sea más accesible para las máquinas ''y'' las personas ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentación en línea]).",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "No se habilitó el funcionamiento de Semantic MediaWiki para esta wiki.",
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": "y",
+ "smw-factbox-head": "… más sobre «$1»",
+ "smw-factbox-facts": "Datos",
+ "smw-factbox-facts-help": "Mostrar declaraciones y datos que han sido creados por un usuario",
+ "smw-factbox-facts-derived": "Datos derivados",
+ "smw-factbox-facts-derived-help": "Mostrar datos que han sido derivados a partir de reglas o con la ayuda de otras técnicas de razonamiento",
+ "smw_isspecprop": "Esta propiedad es una propiedad especial en esta wiki.",
+ "smw-concept-cache-header": "Uso de la antememoria",
+ "smw-concept-cache-count": "El [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count concepto de antememoria] contiene {{PLURAL:$1|'''una''' entidad|'''$1''' entidades}} ($2).",
+ "smw-concept-no-cache": "No hay antememoria disponible.",
+ "smw_concept_description": "Descripción del concepto «$1»",
+ "smw_no_concept_namespace": "Los conceptos solo pueden definirse en páginas del espacio de nombres Concepto:",
+ "smw_multiple_concepts": "Cada página de concepto solamente puede tener una definición de concepto.",
+ "smw_concept_cache_miss": "No se puede utilizar el concepto «$1» en este momento, ya que la configuración de la wiki exige que se compute sin conexión.\nSi el problema persiste después de que pase un tiempo, solicita al administrador del sitio que el concepto esté disponible.",
+ "smw_noinvannot": "Los valores no pueden asignarse a propiedades invertidas.",
+ "version-semantic": "Extensiones semánticas",
+ "smw_baduri": "No están permitidas las URIs con la forma «$1».",
+ "smw_printername_count": "Contar resultados",
+ "smw_printername_csv": "Exportar CSV",
+ "smw_printername_dsv": "Exportar DSV",
+ "smw_printername_debug": "Consulta de depuración (para expertos)",
+ "smw_printername_embedded": "Incrustar el contenido de la página",
+ "smw_printername_json": "Exportar JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Enumeración",
+ "smw_printername_ul": "Desglose",
+ "smw_printername_table": "Tabla",
+ "smw_printername_broadtable": "Tabla ancha",
+ "smw_printername_template": "Plantilla",
+ "smw_printername_rdf": "Exportar RDF",
+ "smw_printername_category": "Categoría",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "La cantidad máxima de resultados a devolver",
+ "smw-paramdesc-offset": "La compensación del primer resultado",
+ "smw-paramdesc-headers": "Muestra los nombres de los encabezados/propiedades",
+ "smw-paramdesc-mainlabel": "La etiqueta a asignar al nombre de la página principal",
+ "smw-paramdesc-link": "Muestra los valores como enlaces",
+ "smw-paramdesc-intro": "Si lo hubiese, el texto a mostrar antes de los resultados de la consulta",
+ "smw-paramdesc-outro": "Si lo hubiese, el texto a mostrar después de los resultados de la consulta",
+ "smw-paramdesc-default": "El texto a mostrar si la consulta no tiene resultados",
+ "smw-paramdesc-sep": "El separador para los valores",
+ "smw-paramdesc-showsep": "Mostrar el separador al comienzo del archivo CSV (\"sep=<valor>\")",
+ "smw-paramdesc-distribution": "En vez de mostrar sus valores, contar y mostrar sus ocurrencias.",
+ "smw-paramdesc-distributionsort": "Ordenar la distribución de valores por número de ocurrencias.",
+ "smw-paramdesc-distributionlimit": "Limitar la distribución de valores solo al conteo de algunos valores.",
+ "smw-paramdesc-aggregation": "Especifica con que debería relacionarse el conjunto",
+ "smw-paramdesc-template": "El nombre de una plantilla con la que mostrar el listado",
+ "smw-paramdesc-columns": "El número de columnas en las que mostrar los resultados (por defecto es $1)",
+ "smw-paramdesc-userparam": "Si se utiliza una plantilla, es un valor pasado en cada llamada de plantilla"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/es.json b/www/wiki/extensions/SemanticMediaWiki/i18n/es.json
new file mode 100644
index 00000000..cbcc424c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/es.json
@@ -0,0 +1,816 @@
+{
+ "@metadata": {
+ "authors": [
+ "Antur",
+ "Armando-Martin",
+ "Bola",
+ "Carmen Jorge García-Reyes",
+ "Crazymadlover",
+ "Dvdgmz",
+ "Fitoschido",
+ "Imre",
+ "Javier Calzada Prado",
+ "Locos epraix",
+ "MaxSem",
+ "Sanbec",
+ "Tifinaghes",
+ "Translationista",
+ "ì•„ë¼",
+ "Wifidel",
+ "Macofe",
+ "Felipe95a",
+ "Ryo567",
+ "AlvaroMolina",
+ "Indiralena",
+ "Lemondoge",
+ "Dgstranz",
+ "Nemo bis",
+ "Julián L",
+ "Luzcaru",
+ "Peter Bowman",
+ "Javiersanp",
+ "Josecurioso",
+ "MarcoAurelio",
+ "RicardoSGZ",
+ "KATRINE1992",
+ "Ivanhercaz",
+ "Cethos",
+ "Bfvale",
+ "Ohlila",
+ "Amitie 10g",
+ "Amaia",
+ "Yllelder",
+ "Sophivorus",
+ "Zarisi",
+ "AVIADOR71",
+ "Jelou",
+ "Caleidoscopic"
+ ]
+ },
+ "smw-desc": "Hace tu wiki más accesible para las máquinas para las máquinas y las personas ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentación en línea])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "No se ha habilitado la funcionalidad de Semantic MediaWiki para esta wiki.",
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": "y",
+ "smw-factbox-head": "… más sobre «$1»",
+ "smw-factbox-facts": "Datos",
+ "smw-factbox-facts-help": "Mostrar declaraciones y hechos que han sido creados por un usuario",
+ "smw-factbox-facts-derived": "Datos derivados",
+ "smw-factbox-facts-derived-help": "Mostrar datos que han sido derivados de reglas o con la ayuda de otras técnicas de razonamiento",
+ "smw_isspecprop": "Esta propiedad es una propiedad especial en esta wiki.",
+ "smw-concept-cache-header": "Uso de antememoria",
+ "smw-concept-cache-count": "La [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count antememoria de conceptos] contiene {{PLURAL:$1|'''una''' entidad|'''$1''' entidades}} ($2).",
+ "smw-concept-no-cache": "No hay antememoria disponible.",
+ "smw_concept_description": "Descripción del concepto «$1»",
+ "smw_no_concept_namespace": "Los conceptos solo pueden definirse en páginas del espacio de nombres Concepto:.",
+ "smw_multiple_concepts": "Cada página de concepto solamente puede tener una definición de concepto.",
+ "smw_concept_cache_miss": "El concepto «$1» no puede ser utilizado en este momento, ya que la configuración de la wiki exige que sea computado sin conexión.\nSi el problema persiste después de pasado algún tiempo, solicita al administrador de tu sitio que haga que este concepto esté disponible.",
+ "smw_noinvannot": "Los valores no pueden asignarse a propiedades inversas.",
+ "version-semantic": "Extensiones semánticas",
+ "smw_baduri": "Las URIs con la forma «$1» no están permitidas.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "Contar resultados",
+ "smw_printername_csv": "Exportar a CSV",
+ "smw_printername_dsv": "Exportar a DSV",
+ "smw_printername_debug": "Depurar consulta (para expertos)",
+ "smw_printername_embedded": "Incrustar el contenido de la página",
+ "smw_printername_json": "Exportar a JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Lista plana",
+ "smw_printername_ol": "Enumeración",
+ "smw_printername_ul": "Desglose",
+ "smw_printername_table": "Tabla",
+ "smw_printername_broadtable": "Tabla ancha",
+ "smw_printername_template": "Plantilla",
+ "smw_printername_templatefile": "Archivo de plantilla",
+ "smw_printername_rdf": "Exportar a RDF",
+ "smw_printername_category": "Categoría",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "La cantidad máxima de resultados a devolver",
+ "smw-paramdesc-offset": "El desplazamiento del primer resultado",
+ "smw-paramdesc-headers": "Muestra los nombres de encabezados/propiedades",
+ "smw-paramdesc-mainlabel": "La etiqueta a asignar al nombre de la página principal",
+ "smw-paramdesc-link": "Muestra los valores como enlaces",
+ "smw-paramdesc-intro": "Si lo hubiese, sería el texto que se muestra antes de los resultados de la consulta",
+ "smw-paramdesc-outro": "Si lo hubiese, sería el texto que se muestra después de los resultados de la consulta",
+ "smw-paramdesc-default": "El texto que mostrar si no hay resultados de consulta",
+ "smw-paramdesc-sep": "El separador entre resultados",
+ "smw-paramdesc-propsep": "El separator entre las propiedades de una entrada de resultado",
+ "smw-paramdesc-valuesep": "El separator entre los valores para una propiedad de un resultado",
+ "smw-paramdesc-showsep": "Muestra el separador al comienzo del archivo CSV («sep=<valor>»)",
+ "smw-paramdesc-distribution": "Cuenta sus ocurrencias y las muestra, en lugar de mostrar todos los valores.",
+ "smw-paramdesc-distributionsort": "Ordena la distribución de valores por número de ocurrencias.",
+ "smw-paramdesc-distributionlimit": "Limita la distribución de valores solamente al recuento de algunos de valores.",
+ "smw-paramdesc-aggregation": "Especifica con qué debe relacionarse el conjunto",
+ "smw-paramdesc-template": "El nombre de la plantilla con la que se mostrará el listado",
+ "smw-paramdesc-columns": "El número de columnas en las que se muestran los resultados",
+ "smw-paramdesc-userparam": "Si se utiliza una plantilla, es un valor pasado en cada llamada de la plantilla",
+ "smw-paramdesc-class": "Una clase adicional de CSS a establecer para la lista",
+ "smw-paramdesc-introtemplate": "Si hay alguna plantilla, es el nombre de la plantilla a mostrar antes de los resultados de la consulta",
+ "smw-paramdesc-outrotemplate": "Si hay alguna plantilla, es el nombre de una plantilla a mostrar después de los resultados de la consulta",
+ "smw-paramdesc-embedformat": "La etiqueta HTML utilizada para definir títulos",
+ "smw-paramdesc-embedonly": "No mostrar ningún título",
+ "smw-paramdesc-table-class": "Una clase CSS adicional a establecer para la tabla",
+ "smw-paramdesc-table-transpose": "Mostrar los encabezados de tabla verticalmente y los resultados horizontalmente",
+ "smw-paramdesc-rdfsyntax": "La sintaxis de RDF a usar",
+ "smw-paramdesc-csv-sep": "Especifica un separador de columnas",
+ "smw-paramdesc-csv-valuesep": "Especifica un separador de valores",
+ "smw-paramdesc-csv-merge": "Fusionar filas y valores de columnas con el mismo identificador de sujeto (aka primera columna)",
+ "smw-paramdesc-csv-bom": "Añadir un BOM (carácter para indicar la orientación «endian») en la parte superior del archivo de salida",
+ "smw-paramdesc-dsv-separator": "El separador a usar",
+ "smw-paramdesc-dsv-filename": "El nombre para el archivo DSV",
+ "smw-paramdesc-filename": "Nombre del archivo de salida",
+ "smw-smwdoc-description": "Muestra una tabla de todos los parámetros que se pueden usar para un formato de resultados especificado junto a los valores por defecto y las descripciones.",
+ "smw-smwdoc-default-no-parameter-list": "Este formato de resultado no brinda parámetros específicos de formato.",
+ "smw-smwdoc-par-format": "El formato de resultados en el que mostrar la documentación de un parámetro.",
+ "smw-smwdoc-par-parameters": "Los parámetros que mostrar. \"specific\" para los añadidos por el formato, \"base\" para los disponibles en todos los formatos, y \"all\" para ambos.",
+ "smw-paramdesc-sort": "Propiedad a partir de la que ordenar la petición",
+ "smw-paramdesc-order": "Orden de ordenación de la consulta",
+ "smw-paramdesc-searchlabel": "Texto para continuar la búsqueda",
+ "smw-paramdesc-named_args": "Nombre los argumentos que se pasan a la plantilla",
+ "smw-paramdesc-template-arguments": "Establece cómo se pasan los argumentos con nombre a la plantilla",
+ "smw-paramdesc-import-annotation": "Los datos anotados adicionales se deben copiar durante el análisis de un tema",
+ "smw-paramdesc-export": "Opción de exportación",
+ "smw-paramdesc-prettyprint": "Una salida con formato agradable que muestra sangrías y saltos de línea adicionales",
+ "smw-paramdesc-json-unescape": "La salida contendrá barras sin secuencias de escape y caracteres Unicode de varios bytes",
+ "smw-paramdesc-json-type": "Tipo de serialización",
+ "smw-paramdesc-source": "Fuente de consulta alternativa",
+ "smw-paramdesc-jsonsyntax": "Sintaxis JSON que utilizar",
+ "smw-printername-feed": "Suministro RSS o Atom",
+ "smw-paramdesc-feedtype": "Tipo de fuente de noticias",
+ "smw-paramdesc-feedtitle": "El texto que se empleará como título del suministro",
+ "smw-paramdesc-feeddescription": "El texto que se utilizará como descripción del suministro",
+ "smw-paramdesc-feedpagecontent": "Contenido de página que se mostrará con el suministro",
+ "smw-label-feed-link": "RSS",
+ "smw-label-feed-description": "Fuente de noticias $1 $2",
+ "smw-paramdesc-mimetype": "El tipo de medio (MIME) del archivo de salida",
+ "smw_iq_disabled": "Se han desactivado las consultas semánticas en este wiki.",
+ "smw_iq_moreresults": "... más resultados",
+ "smw_parseerror": "No se comprendió el valor provisto.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "\"$1\" no puede usarse como un nombre de página en este wiki.",
+ "smw_noproperty": "\"$1\" no puede usarse como un nombre de propiedad en este wiki.",
+ "smw_wrong_namespace": "Aquí se permiten solo las páginas en el espacio de nombres \"$1\".",
+ "smw_manytypes": "Más de un tipo definido para la propiedad.",
+ "smw_emptystring": "No se aceptan cadenas vacías.",
+ "smw_notinenum": "«$1» no se encuentra en la lista ($2) de [[Property:Allows value|valores permitidos]] de la propiedad «$3».",
+ "smw_noboolean": "\"$1\" no es reconocido como un valor booleano (verdadero/falso).",
+ "smw_true_words": "verdadero,t,si,s,true",
+ "smw_false_words": "falso,f,no,n,false",
+ "smw_nofloat": "«$1» no es un número.",
+ "smw_infinite": "No se admiten números del tamaño de «$1».",
+ "smw_unitnotallowed": "\"$1\" no está declarada como una unidad de medida válida para esta propiedad.",
+ "smw_nounitsdeclared": "No hay unidades de medida declaradas para esta propiedad.",
+ "smw_novalues": "No se han especificado valores.",
+ "smw_nodatetime": "La fecha «$1» no ha sido comprendida.",
+ "smw_toomanyclosing": "Parece haber demasiadas coincidencias de \"$1\" en la consulta.",
+ "smw_noclosingbrackets": "Algún uso de \"<nowiki>[[</nowiki>\" en tu consulta no se cerró con los correspondientes \"]]\".",
+ "smw_misplacedsymbol": "El símbolo \"$1\" se ha usado en un lugar donde no es de utilidad.",
+ "smw_unexpectedpart": "La parte \"$1\" de la consulta no fue entendida.\nLos resultados podrían no ser los esperados.",
+ "smw_emptysubquery": "Alguna subconsulta no tiene condición válida.",
+ "smw_misplacedsubquery": "Alguna subconsulta fue usada en un lugar donde las subconsultas no están permitidas.",
+ "smw_valuesubquery": "Los valores de la propiedad «$1» no admiten las subconsultas.",
+ "smw_badqueryatom": "Alguna parte \"<nowiki>[[...]]</nowiki>\" de la consulta no fue entendida.",
+ "smw_propvalueproblem": "El valor de propiedad \"$1\" no fue entendida.",
+ "smw_noqueryfeature": "Alguna característica de la consulta no se admite en esta wiki, y parte de ella se ha eliminado ($1).",
+ "smw_noconjunctions": "No se admiten las conjunciones en las consultas en este wiki, y por ello se ha eliminado parte de la consulta ($1).",
+ "smw_nodisjunctions": "Las disjunciones en las consultas no se admiten en esta wiki y parte de la consulta se ha eliminado ($1).",
+ "smw_querytoolarge": "No se {{PLURAL:$2|pudo|pudieron}} considerar {{PLURAL:$2|la siguiente condición|las siguientes $2 condiciones}} de consulta debido a restricciones en este wiki de tamaño o profundidad de consulta: <code>$1</code>.",
+ "smw_notemplategiven": "Proveer un valor para el parámetro \"plantilla\" para este formato de consulta para funcionar.",
+ "smw_db_sparqlqueryproblem": "No se pudieron obtener los resultados de la consulta de la base de datos SPARQL. Este error puede ser temporal o indicar un error en el programa de la base de datos.",
+ "smw_db_sparqlqueryincomplete": "Resultó demasiado difícil responder la consulta y fue abandonada. Es probable que falten algunos resultados. Si fuese posible, intente hacer una consulta más sencilla.",
+ "smw_type_header": "Propiedades de tipo \"$1\"",
+ "smw_typearticlecount": "Se {{PLURAL:$1|muestra $1 propiedad que usa|muestran $1 propiedades que usan}} este tipo.",
+ "smw_attribute_header": "Páginas que usan la propiedad \"$1\"",
+ "smw_attributearticlecount": "Se {{PLURAL:$1|muestra $1 página que usa|muestran $1 páginas que usan}} esta propiedad.",
+ "smw-propertylist-subproperty-header": "Subpropiedades",
+ "smw-propertylist-redirect-header": "Sinónimos",
+ "smw-propertylist-error-header": "Páginas con asignaciones no apropiadas",
+ "smw-propertylist-count": "Se {{PLURAL:$1|muestra $1 propiedad relacionada|muestran $1 propiedades relacionadas}}.",
+ "smw-propertylist-count-with-restricted-note": "Se {{PLURAL:$1|muestra $1 entidad relacionada|muestran $1 entidades relacionadas}}. Hay más disponibles pero la salida está restringida a \"$2\".",
+ "smw-propertylist-count-more-available": "Se {{PLURAL:$1|muestra $1 entidad relacionada|muestran $1 entidades relacionadas}} (hay más disponibles).",
+ "specialpages-group-smw_group": "Semantic MediaWiki",
+ "exportrdf": "Exportar páginas a RDF",
+ "smw_exportrdf_docu": "Esta página permite obtener datos de una página en formato RDF.\nPara exportar páginas, escribe los títulos en el cuadro de abajo, uno por línea.",
+ "smw_exportrdf_recursive": "Exportar igualmente todas las páginas pertinentes de forma recurrente. Esta posibilidad puede conseguir un gran número de resultados !",
+ "smw_exportrdf_backlinks": "Exportar igualmente todas las páginas que reenvían a páginas exportadas. Resulta un RDF en el que se facilita la navegación.",
+ "smw_exportrdf_lastdate": "No exportar páginas que no fueron cambiadas desde el punto dado en el tiempo.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Traductor de URI",
+ "properties": "Propiedades",
+ "smw_properties_docu": "Las siguientes propiedades se usan en el wiki.",
+ "smw_property_template": "$1 de tipo $2 ($3 {{PLURAL:$3|uso|usos}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Todas las propiedades deberían describirse por una página.",
+ "smw_propertylackstype": "Ningún tipo fue especificada para esta propiedad (asumiendo tipo $1 por ahora).",
+ "smw_propertyhardlyused": "¡Esta propiedad se usa escasamente dentro del wiki!",
+ "smw-property-name-invalid": "La propiedad $1 no puede usarse (nombre de propiedad no válido).",
+ "smw-property-name-reserved": "«$1» se marcó como nombre reservado y, por lo tanto, no debería utilizarse como una propiedad. Es posible que esta [https://www.semantic-mediawiki.org/wiki/Help:Property_naming página de ayuda] incluya información sobre el motivo por el que este nombre se reservó.",
+ "smw-sp-property-searchform": "Mostrar propiedades que contengan:",
+ "smw-sp-property-searchform-inputinfo": "La entrada distingue mayúsculas y minúsculas y, cuando se utiliza para el filtrado, solo se muestran las propiedades que coinciden con la condición.",
+ "smw-special-property-searchform": "Mostrar propiedades que contengan:",
+ "smw-special-property-searchform-inputinfo": "Se hace distinción de mayúsculas y minúsculas en la entrada. Cuando esta se emplee para filtrar, únicamente aparecerán las propiedades que coincidan con la condición.",
+ "smw-special-property-searchform-options": "Opciones",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Ninguno",
+ "smw-special-wantedproperties-filter-unapproved": "Sin aprobar",
+ "smw-special-wantedproperties-filter-unapproved-desc": "La opción de filtrado se utiliza en conjunto con el modo de autoridad.",
+ "concepts": "Conceptos",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts concepto] puede verse como una \"categoría dinámica\". Es decir, como una colección de páginas que no se crean manualmente, sino que se calculan a través de Semantic MediaWiki a partir de una descripción de una consulta dada.",
+ "smw-special-concept-header": "Lista de conceptos",
+ "smw-special-concept-count": "Se {{PLURAL:$1|lista el siguiente concepto|listan los siguientes $1 conceptos}}.",
+ "smw-special-concept-empty": "No se encontró ningún concepto.",
+ "unusedproperties": "Propiedades no utilizadas",
+ "smw-unusedproperties-docu": "Esta página lista las [https://www.semantic-mediawiki.org/wiki/Unused_properties propiedades no utilizadas] que se declaran aunque ninguna otra página hace uso de ellos. Para una visión diferenciada, vea [[Special:Properties|todo]] o la página especial de [[Special:WantedProperties|propiedades buscadas]].",
+ "smw-unusedproperty-template": "$1 de tipo $2",
+ "wantedproperties": "Propiedades requeridas",
+ "smw-wantedproperties-docu": "Esta página lista las [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedades buscadas] que se utilizan en el wiki pero no tienen una página que las describa. Para una visión diferenciada, vea [[Special:Properties|todo]] o la página especial de [[Special:UnusedProperties|propiedades no utilizadas]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw-special-wantedproperties-docu": "Esta página enumera las [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedades solicitadas] que se emplean en el wiki pero que no cuentan con una página descriptora. Para obtener una visualización diferenciada, consulta las listas de [[Special:Properties|todas las propiedades]] y de las [[Special:UnusedProperties|propiedades no utilizadas]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw_purge": "Actualizar",
+ "smw-purge-failed": "Recarga fallida",
+ "types": "Tipos",
+ "smw_types_docu": "Lista de [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipos de datos disponibles] donde cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipo] representa un conjunto único de atributos para describir un valor en términos de almacenamiento y características de muestra que son hereditarios a una propiedad asignada.",
+ "smw-special-types-no-such-type": "El tipo de datos especificado no existe",
+ "smw-statistics": "Estadísticas de semántica",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor|Valores}} de propiedad (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propiedad|Propiedades}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propiedad|Propiedades}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propiedad|Propiedades}}]] (utilizado con al menos un valor)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propiedad (registrada con una página)|Propiedades (registradas con una página)}}",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propiedad (asignada a un tipo de datos)|Propiedades (asignadas a un tipo de datos)}}",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultas}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Consulta|Consultas}}]]",
+ "smw-statistics-query-size": "Tamaño de la consulta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concepto|Conceptos}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concepto|Conceptos}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobjeto|Subobjetos}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobjeto|Subobjetos}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipo de datos|Tipos de datos}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valor|Valores}} de propiedad ([[Special:ProcessingErrorList|{{PLURAL:$1|anotación impropia|anotaciones impropias}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor|Valores}} de propiedad ({{PLURAL:$1|anotación impropia|anotaciones impropias}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entidad desactualizada (marcada para su eliminación)|Entidades desactualizadas (marcadas para su eliminación)}}",
+ "smw_uri_doc": "El traductor de URI implementa [$1 W3C TAG finding on httpRange-14].\nEsto se preocupa de cosas que los humanos no lo hacen en los sitios web..",
+ "ask": "Búsqueda semántica",
+ "smw-ask-help": "Esta sección contiene algunos enlaces para ayudar a explicar cómo utilizar la sintaxis <code>#ask</code>.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selección de páginas] describe cómo seleccionar páginas y construir condiciones.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de búsqueda] enumera los operadores de búsqueda disponibles, incluidos los de consulta de intervalo y de caracteres comodines.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Muestra de información] expone el uso de las declaraciones de salida y las opciones de formato.",
+ "smw_ask_sortby": "Ordenar por columna (opcional)",
+ "smw_ask_ascorder": "Ascendente",
+ "smw_ask_descorder": "Descendente",
+ "smw-ask-order-rand": "Al azar",
+ "smw_ask_submit": "Buscar resultados",
+ "smw_ask_editquery": "Editar consulta",
+ "smw_add_sortcondition": "[Agregar condición de orden]",
+ "smw-ask-sort-add-action": "Añadir condición de ordenamiento",
+ "smw_ask_hidequery": "Ocultar consulta (vista compacta)",
+ "smw_ask_help": "Ayuda sobre consultas",
+ "smw_ask_queryhead": "Condición",
+ "smw_ask_printhead": "Selección de datos para mostrar",
+ "smw_ask_printdesc": "(añade un nombre de propiedad por línea)",
+ "smw_ask_format_as": "Formatear como:",
+ "smw_ask_defaultformat": "predeterminado",
+ "smw_ask_otheroptions": "Otras opciones",
+ "smw-ask-otheroptions-info": "Esta sección contiene opciones que alteran los formatos de impresión. Las descripciones de dichas opciones pueden verse pasando el ratón sobre ellas.",
+ "smw-ask-otheroptions-collapsed-info": "Usa el icono de suma para ver todas las opciones disponibles",
+ "smw_ask_show_embed": "Mostrar código embebido",
+ "smw_ask_hide_embed": "Ocultar código embebido",
+ "smw_ask_embed_instr": "Para incluír esta consulta en línea dentro de una wiki usa el código siguiente:",
+ "smw-ask-delete": "Quitar",
+ "smw-ask-sorting": "Ordenación",
+ "smw-ask-options": "Opciones",
+ "smw-ask-options-sort": "Opciones de ordenamiento",
+ "smw-ask-format-options": "Formato y opciones",
+ "smw-ask-parameters": "Parámetros",
+ "smw-ask-search": "Buscar",
+ "smw-ask-debug": "Depuración",
+ "smw-ask-debug-desc": "Genera información para depurar consultas",
+ "smw-ask-no-cache": "Deshabilitar la caché de consultas",
+ "smw-ask-no-cache-desc": "Resultados sin antememoria de consulta",
+ "smw-ask-result": "Resultado",
+ "smw-ask-empty": "Borrar todas las entradas",
+ "smw-ask-download-link-desc": "Descargar los resultados de la consulta en el formato $1",
+ "smw-ask-format": "Formato",
+ "smw-ask-format-selection-help": "Ayuda con el formato seleccionado: $1",
+ "smw-ask-condition-change-info": "Se modificó la condición; por ello, el buscador necesita ejecutar otra vez la consulta para producir resultados adecuados a los requisitos nuevos.",
+ "smw-ask-input-assistance": "Asistencia de entrada",
+ "smw-ask-condition-input-assistance": "Se brinda [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistencia de entrada] para los campos de salida, de ordenación y de condición. Para utilizar este último, es necesario emplear alguno de estos prefijos:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> para buscar sugerencias de propiedades (p. ej., <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> para buscar sugerencias de categorías",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> para buscar sugerencias de conceptos",
+ "smw-ask-format-change-info": "Se modificó el formato y, por tanto, es necesario ejecutar de nuevo la consulta para que corresponda con los parámetros y las opciones de visualización nuevos.",
+ "smw-ask-format-export-info": "El formato de exportación seleccionado no tiene representación visual, por lo que los resultados se proporcionan solamente para descargar.",
+ "smw-ask-query-search-info": "{{PLURAL:$3|1=<code>$2</code> (de la caché)|<code>$2</code> (de la caché)|<code>$2</code>}} respondió la consulta <code><nowiki>$1</nowiki></code> en $4 {{PLURAL:$4|segundo|segundos}}.",
+ "searchbyproperty": "Buscar por propiedad",
+ "processingerrorlist": "Lista de errores de procesamiento",
+ "propertylabelsimilarity": "Informe de similitud de etiquetas de propiedades",
+ "smw-processingerrorlist-intro": "La siguiente lista proporciona una descripción general de los errores de procesamiento que aparecieron con [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Se recomienda monitorear esta lista de forma regular y corregir las anotaciones de valores no válidos.",
+ "smw_sbv_docu": "Buscar todas las páginas que tienen una propiedad y un valor determinados.",
+ "smw_sbv_novalue": "Escribe un valor válido para la propiedad, o ve todos los valores de propiedad para \"$1\".",
+ "smw_sbv_displayresult": "Una lista de todas las páginas que tienen una propiedad \"$1\" con el valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Lista de todas las páginas que tienen la propiedad \"$1\" con valor \"$2\".\nComo hay pocos resultados, también se muestran los valores aproximados.",
+ "smw_sbv_property": "Propiedad:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Buscar resultados",
+ "browse": "Explorar el wiki",
+ "smw_browselink": "Explorar propiedades",
+ "smw_browse_article": "Escribe el nombre de la página desde donde comenzar a navegar.",
+ "smw_browse_go": "Ir",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "Mostrar propiedades entrantes",
+ "smw_browse_hide_incoming": "Ocultar propiedades entrantes",
+ "smw_browse_no_outgoing": "Esta página no tiene propiedades.",
+ "smw_browse_no_incoming": "Ninguna propiedad vincula aquí.",
+ "smw-browse-from-backend": "Se está recuperando la información del motor.",
+ "smw-browse-intro": "Esta página proporciona detalles sobre un sujeto o una instancia de entidad, ingrese el nombre de un objeto a inspeccionar.",
+ "smw-browse-invalid-subject": "La validación del tema devolvió el error \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "El tema tiene un formato de serialización no válido.",
+ "smw-browse-js-disabled": "Es probable que JavaScript esté desactivado o no esté disponible y recomendamos utilizar un navegador que sea compatible. En la página de configuración [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi $smwgBrowseByApi] se discuten otras opciones.",
+ "smw-browse-show-group": "Mostrar grupos",
+ "smw-browse-hide-group": "Ocultar grupos",
+ "smw-noscript": "Esta pagina o accion necesita Javascript para funcionar, por favor activar el Javascript en su navigador o usar un navigador donde esto es suportado, asi puede funcionar bien y asi es suministrado como se ha solicitado. Para mas ayuda, por favor ver el enlace siguiente en la pagina de ayuda. [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript] .",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiqueta de propiedad inversa",
+ "pageproperty": "Búsqueda de propiedades de página",
+ "smw_pp_docu": "Proporciona una página y propiedad o solo una propiedad para recuperar todos los valores asignados.",
+ "smw_pp_from": "Desde la página:",
+ "smw_pp_type": "Propiedad:",
+ "smw_pp_submit": "Encontrar resultados",
+ "smw_result_prev": "Anterior",
+ "smw_result_next": "Siguiente",
+ "smw_result_results": "Resultados",
+ "smw_result_noresults": "No hay resultados.",
+ "smwadmin": "Funciones administrativas y de mantenimiento",
+ "smw-admin-statistics-job-title": "Estadísticas de trabajo",
+ "smw-admin-statistics-job-docu": "Las estadísticas de trabajo muestran información acerca de los trabajos para Semantic MediaWiki programados que aún no se han ejecutado. El número de trabajos puede ser ligeramente inexacto o contener intentos fallidos, consulte este [https://www.mediawiki.org/wiki/Manual:Job_queue manual] para información detallada.",
+ "smw-admin-statistics-querycache-title": "Estadísticas de antememoria de consultas",
+ "smw-admin-statistics-querycache-disabled": "El [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] no ha sido habilitado en este wiki y por lo tanto no hay estadísticas disponibles.",
+ "smw-admin-statistics-querycache-explain": "Las estadísticas de cache contendrán datos acumulados y derivados provisionales incluyendo:\n* \"misses\" como la cantidad total de intentos de obtener datos de la cache sin respuesta, forzando una obtención directa del repositorio (DB, triple-store etc.)\n* \"deletes\" como la cantidad total de operaciones de desahucio de cache (bien a través de una purga o una dependencia de query)\n* \"hits\" contiene la cantidad de obtenciones de cache desde fuentes incrustadas (queries desde una página de la wiki) o no incrustadas (en caso de estar activadas, pedidas por páginas como Special:Ask o la API).\n* \"medianRetrievalResponseTime\" es un valor orientativo sobre el tiempo de respuesta medio (en segundos) para peticiones en cache y fuera de este a lo largo del periodo de recolección.\n* \"noCache\" indica la cantidad de peticiones sin intento de obtener resultados de la cache (queries con limit=0, opción de 'no-cache')",
+ "smw-admin-permission-missing": "Debido a la falta de los permisos necesarios, se ha bloqueado el acceso a esta página. Consulta la página de ayuda sobre los [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] para obtener más información sobre las configuraciones necesarias.",
+ "smw-admin-setupsuccess": "El motor de almacenamiento se ha configurado.",
+ "smw_smwadmin_return": "Regresar a $1",
+ "smw_smwadmin_updatestarted": "Se ha iniciado un proceso de actualización nuevo para actualizar los datos semánticos.\nTodos los datos almacenados se reconstruirán o repararán donde haga falta.\nPuedes hacer un seguimiento del progreso de la actualización en esta página especial.",
+ "smw_smwadmin_updatenotstarted": "Ya hay un proceso de actualización ejecutándose.\nNo se creará otro.",
+ "smw_smwadmin_updatestopped": "Se han detenido todos los procesos de actualización existentes.",
+ "smw_smwadmin_updatenotstopped": "Para detener el proceso de actualización en ejecución, debes marcar la casilla para indicar que estás realmente seguro.",
+ "smw-admin-docu": "Esta página especial te ayudará durante la instalación, actualización, mantenimiento y uso de <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a>, además de proporcionar funciones administrativas y tareas, así como estadísticas.\n\nRecuerda respaldar cualquier información importante antes de ejecutar funciones administrativas.",
+ "smw-admin-environment": "Entorno de software",
+ "smw-admin-db": "Mantenimiento de la base de datos",
+ "smw-admin-db-preparation": "La inicialización de la tabla está en curso y se puede producir una demora antes de que se muestren los resultados, en función del tamaño y las optimizaciones que deban realizarse.",
+ "smw-admin-dbdocu": "Semantic MediaWiki necesita algunas extensiones en la base de datos de MediaWiki para poder almacenar la información semántica.\nLa función indicada abajo asegura que tu base de datos está configurada correctamente.\nLos cambios realizados en este paso no afectan el resto de la base de datos de MediaWiki y pueden ser revertidos fácilmente si así se desea.\nEsta función de configuración puede ejecutarse varias veces sin hacer ningún daño, pero es necesaria sólo una vez para la instalación o actualización.",
+ "smw-admin-permissionswarn": "Si la operación falla con errores SQL, la base de datos de usuarios empleada por su wiki (revise su archivo \"LocalSettings.php\") probablemente no tiene permisos suficientes.\nUd puede conceder a este usuario permisos adicionales para crear y borrar tablas, ingresando temporalmente la clave de acceso de su base de datos raíz en el archivo \"LocalSettings.php\", o bien usar el script de mantenimiento <code>setupStore.php</code>, que puede usar las credenciales de un administrador.",
+ "smw-admin-dbbutton": "Inicializar o modernizar tablas",
+ "smw-admin-announce": "Anuncia tu wiki",
+ "smw-admin-announce-text": "Si tu wiki es público, lo puedes registrar en <a href=\"https://wikiapiary.com\">WikiApiary</a>, el wiki de seguimiento de wikis.",
+ "smw-admin-deprecation-notice-title": "Alertas de obsolescencia",
+ "smw-admin-deprecation-notice-docu": "La siguiente sección contiene ajustes que se han quedado obsoletos o eliminados, pero que fueron detectados para estar activos en esta wiki. Se espera que en el futuro cualquier versión elimine el soporte para estas configuraciones.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> está obsoleto y se eliminará en $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> eliminará (o reemplazará) {{PLURAL:$2|la opción siguiente|las opciones siguientes}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> está desusado y se eliminará en la versión $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> fue reemplazado por <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> tiene {{PLURAL:$2|esta opción|estas opciones}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> se reemplazará por <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> fue eliminado en $2",
+ "smw-admin-deprecation-notice-title-notice": "Cambios inminentes",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Los siguientes ajustes se detectaron para ser usados en esta wiki y está planeado que sean eliminados o cambiados en versiones futuras.",
+ "smw-admin-deprecation-notice-title-replacement": "Opciones sustituidas o retituladas",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "La sección siguiente contiene opciones cuyo nombre se ha modificado o han recibido cambios de otro tipo. Se recomienda que se actualice inmediatamente su nombre o su formato.",
+ "smw-admin-deprecation-notice-title-removal": "Opciones eliminadas",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Las opciones siguientes se eliminaron en una versión anterior, pero se detectó que aún se utilizan en este wiki.",
+ "smw-smwadmin-refresh-title": "Reparación y actualización de datos",
+ "smw_smwadmin_datarefresh": "Reconstrucción de los datos",
+ "smw_smwadmin_datarefreshdocu": "Es posible restaurar toda la información de MediaWiki Semántica basándose en los contenidos actuales de la wiki.\nEsto puede ser útil para reparar la información destruida o para volver a cargar la información si el formato interno ha cambiado a causa de alguna actualización de software.\nLa actualización se ejecuta página a página y no se completará de inmediato.\nLo siguiente muestra es hay una actualización en progreso y le permite iniciar o detener actualizaciones (a menos que esta característica haya sido inhabilitada por el administrador del sitio).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Actualización en progreso.</strong>\nEs normal que el proceso de actualización se retrase ya que sólo actualiza datos en porciones pequeñas cada vez que un usuario ingresa a la wiki.\nPara finalizar esta actualización en forma más rápida, puedes invocar el script de matenimiento MediaWiki <code>runJobs.php</code> (usa la opción <code>--maxjobs 1000</code> para restringir el número de actualizaciones hechas en cada paquete).\nProgreso estimado de la actualización:",
+ "smw_smwadmin_datarefreshbutton": "Programar actualización de datos",
+ "smw_smwadmin_datarefreshstop": "Detener esta actualización",
+ "smw_smwadmin_datarefreshstopconfirm": "Sí, estoy {{GENDER:$1|seguro|segura}}.",
+ "smw-admin-job-scheduler-note": "La mayoría de las actividades de esta sección se realizan como trabajos para evitar situaciones de bloqueo durante su ejecución. El [https://www.mediawiki.org/wiki/Manual:Job_queue planificador de trabajos] es responsable del procesamiento y es fundamental que el script de mantenimiento <code>runJobs.php</code> (véase también el parámetro de configuración <code>$wgRunJobsAsync</code>) tenga la capacidad adecuada.",
+ "smw-admin-outdateddisposal-title": "Eliminación de entidades obsoletas",
+ "smw-admin-outdateddisposal-intro": "Algunas actividades (un cambio a un tipo de propiedad, la eliminación de wikipáginas o la corrección de valores de error) resultarán en [https://www.semantic-mediawiki.org/wiki/Outdated_entities entidades anticuadas] y se sugiere eliminarlas periódicamente para liberar el espacio de tablas asociado.",
+ "smw-admin-outdateddisposal-active": "Se ha puesto en cola un trabajo de eliminación de entidades obsoletas.",
+ "smw-admin-outdateddisposal-button": "Programar una eliminación",
+ "smw-admin-feature-disabled": "Se ha deshabilitado esta función en este wiki. Para más información, consulta la página de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">configuración</a> o contacta con el administrador del sistema.",
+ "smw-admin-propertystatistics-title": "Reconstruir las estadísticas de la propiedad",
+ "smw-admin-propertystatistics-intro": "Reconstruye todas las estadísticas de uso de la propiedad y actualiza y corrige el [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count recuento de uso] de propiedades.",
+ "smw-admin-propertystatistics-active": "Se ha programado una reconstrucción de estadísticas.",
+ "smw-admin-propertystatistics-button": "Programar reconstrucción de estadísticas",
+ "smw-admin-fulltext-title": "Reconstrucción de búsqueda de texto completo",
+ "smw-admin-fulltext-intro": "Recompila el índice de búsquedas a partir de tablas de propiedades mediante un tipo de datos para [https://www.semantic-mediawiki.org/wiki/Full-text búsquedas de texto completo] que se haya activado. Es necesario ejecutar esta tarea de nuevo si se producen modificaciones en las reglas de indización (cambios de palabras no significativas, un lematizador nuevo, etc.) o se añade o modifica una tabla.",
+ "smw-admin-fulltext-active": "Se ha programado una tarea de reconstrucción para búsquedas de texto completo.",
+ "smw-admin-fulltext-button": "Programar reconstrucción de texto completo",
+ "smw-admin-support": "Obtener asistencia",
+ "smw-admin-supportdocu": "Se ofrecen algunos recursos para ayudarte si se producen problemas:",
+ "smw-admin-installfile": "Si experimentas problemas con tu instalación, comienza revisando las directivas en el <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">archivo INSTALL</a>.",
+ "smw-admin-smwhomepage": "Puede encontrarse la documentación de usuario completa de Semantic MediaWiki en <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Se pueden enviar informes de error a <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Si tienes preguntas o sugerencias adicionales, únete a la discusión en la <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">lista de correo de usuarios de Semantic MediaWiki\n</a>.",
+ "smw-admin-other-functions": "Otras funciones",
+ "smw-admin-supplementary-section-title": "Funciones suplementarias",
+ "smw-admin-supplementary-section-subtitle": "Funciones disponibles",
+ "smw-admin-supplementary-section-intro": "Esta sección proporciona funciones adicionales más allá del alcance del mantenimiento y es posible que algunas funciones que están listadas en la [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentación] estén restringidas o no disponibles y por lo tanto inaccesibles en este wiki.",
+ "smw-admin-supplementary-settings-title": "Opciones de configuración",
+ "smw-admin-supplementary-settings-intro": "<U>$1</u> genera una lista colectiva de configuraciones disponibles en la MediaWiki Semántica",
+ "smw-admin-supplementary-operational-statistics-title": "Estadísticas operacionales",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> muestra un conjunto extendido de estadísticas",
+ "smw-admin-supplementary-idlookup-title": "Búsqueda y eliminación de la entidad",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contiene funciones para buscar y desechar entidades individuales",
+ "smw-admin-supplementary-duplookup-title": "Entidades duplicadas",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> para enumerar entradas que estén categorizadas por contener duplicados en la tabla de entidades",
+ "smw-admin-supplementary-duplookup-docu": "Esta página lista entradas que han sido categorizadas como duplicadas en la [https://www.semantic-mediawiki.org/wiki/Help:Entity_table tabla de entidades]. Las entradas duplicadas solo deberían ocurrir (como mucho) en raras ocasiones potencialmente causadas por un proceso terminado durante una actualización de la base de datos o una transacción de reversión fallida.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estadísticas de antememoria",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> musetra estadísticas relativas a la antememoria",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informa sobre estadísticas de ajustes e indices",
+ "smw-admin-supplementary-elastic-docu": "Esta página contiene información sobre ajustes, mapeos, salud y estadísticas de índices relacionados con un clúster de Elasticsearch que está conectado a Semantic MediaWiki y su [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Funciones disponibles",
+ "smw-admin-supplementary-elastic-settings-title": "Configuración",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> utilizado por Elasticsearch para administrar indices de MediaWiki Semántica",
+ "smw-admin-supplementary-elastic-mappings-title": "Asignaciones",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> para listar indices y mapeos de campo",
+ "smw-admin-supplementary-elastic-mappings-docu": "Esta página contiene detalles de la asignación de campos, ya que se utilizan con los índices actuales. El resumen de mapeo debe ser controlado en conexión con el <code>index.mapping.total_fields.limit</code> que especifica el número máximo de campos en un índice.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resumen",
+ "smw-admin-supplementary-elastic-mappings-fields": "Mapeos de campo",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodos",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> muestra estadísticas de nodo",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> proporciona una vista general de índices disponibles y sus estadísticas",
+ "smw-admin-supplementary-elastic-statistics-title": "Estadísticas",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> muestra estadísticas de nivel de índice",
+ "smw-admin-supplementary-elastic-statistics-docu": "Esta página proporciona una visión de las estadísticas de los índices de las diferentes operaciones que están ocurriendo a nivel de índice, las estadísticas devueltas se combinan con las primarias y las totales. La [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html página de ayuda] contiene una descripción detallada de las estadísticas de los índices disponibles.",
+ "smw-admin-supplementary-elastic-status-replication": "Estado de replicación",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Última replicación activa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalo de refrescado: $1",
+ "smw-list-count": "La lista contiene $1 {{PLURAL:$1|entrada|entradas}}.",
+ "smw-list-count-from-cache": "La lista contiene $1 {{PLURAL:$1|entrada|entradas}} y se recuperó de la antememoria (UTC: $2).",
+ "smw-property-label-uniqueness": "La etiqueta «$1» corresponde con por lo menos una representación de propiedad diferente. Consulta la [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness página de ayuda] para saber cómo solucionar este problema.",
+ "smw-property-label-similarity-title": "Informe de similitud de etiqueta de propiedad",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula las semejanzas para etiquetas de propiedades existentes",
+ "smw-property-label-similarity-threshold": "Umbral:",
+ "smw-property-label-similarity-type": "Mostrar identificador de tipo",
+ "smw-property-label-similarity-noresult": "No se encontró ningún resultado para las opciones seleccionadas.",
+ "smw-property-label-similarity-docu": "Compara y notifica la [https://www.semantic-mediawiki.org/wiki/Property_similarity semejanza sintáctica] (no la semántica) entre dos etiquetas de propiedades, lo que puede ayudar a filtrar propiedades mal escritas o equivalentes que representen el mismo concepto (consulta la página especial [[Special:Properties|Propiedades]] para clarificar los conceptos y los usos de las propiedades notificadas). Puede ajustarse el umbral para aumentar o disminuir la distancia de semejanza. <code>[[Property:$1|$1]]</code> se utiliza para eximir propiedades del análisis.",
+ "smw-admin-operational-statistics": "Esta página contiene estadísticas operacionales recogidas en o desde funciones relacionadas de la MediaWiki Semántica. Puede encontrar [[Special:Statistics|<b>aquí</b>]] una lista ampliada de estadísticas específicas del wiki.",
+ "smw_adminlinks_datastructure": "Estructura de datos",
+ "smw_adminlinks_displayingdata": "Mostrando datos",
+ "smw_adminlinks_inlinequerieshelp": "Ayuda de consultas en línea",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Número de usos] estimado: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propiedad definida por el {{PLURAL:$1|usuario|sistema}}",
+ "smw-property-indicator-last-count-update": "Cuenta estimada de uso\nActualizada por última vez el: $1",
+ "smw-concept-indicator-cache-update": "Conteo de antememoria\nÚltima actualización: $1",
+ "smw-createproperty-isproperty": "Es una propiedad de tipo $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|El valor permitido para esta propiedad es|Los valores permitidos para esta propiedad son}}:",
+ "smw-paramdesc-category-delim": "El delimitador",
+ "smw-paramdesc-category-template": "Una plantilla con la que dar formato a los elementos",
+ "smw-paramdesc-category-userparam": "Un parámetro que pasar a la plantilla",
+ "smw-info-par-message": "El mensaje que mostrar.",
+ "smw-info-par-icon": "Icono que mostrar: ya sea \"info\" (información) o bien \"warning\" (advertencia).",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Opciones generales",
+ "prefs-ask-options": "Especial:Opciones de preguntas",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (o extensiones relacionadas) suministran las opciones mostradas a continuación para permitir la personalización individual de las funciones seleccionadas. Para más información, consulta [https://www.semantic-mediawiki.org/wiki/Help:User_preferences esta página de ayuda].",
+ "smw-prefs-ask-options-tooltip-display": "Mostrar el texto del parámetro como ayuda informativa",
+ "smw-prefs-ask-options-compact-view-basic": "Habilitar vista compacta básica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Si se habilita, muestra un conjunto reducido de enlaces en la vista compacta Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Activar la corrección horaria en las páginas especiales mediante la preferencia local de [[Special:Preferences#mw-prefsection-rendering|desfase horario]]",
+ "smw-prefs-general-options-jobqueue-watchlist": "Mostrar la lista de seguimiento de la cola de tareas en mi barra personal",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Si está habilitado, muestra una [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist lista de] tareas seleccionadas pendientes junto con sus tamaños de cola estimados.",
+ "smw-prefs-general-options-disable-editpage-info": "Desactivar el texto introductorio de la página de edición",
+ "smw-prefs-general-options-disable-search-info": "Desactivar la información de sintaxis admitida en la página estándar de búsquedas",
+ "smw-prefs-general-options-suggester-textinput": "Habilitar la asistencia de entrada para entidades semánticas",
+ "smw-prefs-help-general-options-suggester-textinput": "Si está habilitado, permite utilizar una [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistencia de entrada] para encontrar propiedades, conceptos y categorías desde un contexto de entrada.",
+ "smw-ui-tooltip-title-property": "Propiedad",
+ "smw-ui-tooltip-title-quantity": "Conversión de unidad",
+ "smw-ui-tooltip-title-info": "Información",
+ "smw-ui-tooltip-title-service": "Enlaces de servivio",
+ "smw-ui-tooltip-title-warning": "Advertencia",
+ "smw-ui-tooltip-title-error": "Error",
+ "smw-ui-tooltip-title-parameter": "Parámetro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Leyenda",
+ "smw-ui-tooltip-title-reference": "Referencia",
+ "smw_unknowntype": "El tipo de esta propiedad no es válido",
+ "smw-concept-cache-text": "El concepto tiene un total de $1 {{PLURAL:$1|página|páginas}}, y se actualizó por última vez el $3 a las $2.",
+ "smw_concept_header": "Páginas de concepto \"$1\"",
+ "smw_conceptarticlecount": "Mostrando abajo $1 {{PLURAL:$1|página|páginas}}.",
+ "smw-qp-empty-data": "Los datos solicitados podrían no mostrarse, debido a algunos criterios de selección insuficientes.",
+ "right-smw-admin": "Acceder a tareas de administración (Semantic MediaWiki)",
+ "right-smw-patternedit": "Acceso de edición para mantener expresiones regulares y patrones permitidos (Semantic MediaWiki)",
+ "right-smw-pageedit": "Acceso de edición para páginas con anotación <code>is edit prrotected</code> (MediaWiki Semántica)",
+ "right-smw-ruleedit": "Editar páginas de regla (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "protegido (solamente usuarios elegibles)",
+ "action-smw-patternedit": "editar expresiones regulares utilizadas por Semantic MediaWiki",
+ "action-smw-pageedit": "editar páginas anotadas con <code>is edit protected</code> (MediaWiki Semántica)",
+ "group-smwadministrator": "Administradores (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrador|administradora}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administradores (Semantic MediaWiki)",
+ "group-smwcurator": "Curadores (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|curador|curadora}} (Semantic MediaWiki)",
+ "grouppage-smwcurator": "{{ns:project}}:Curadores (Semantic MediaWiki)",
+ "action-smw-admin": "acceder a tareas de administración de Semantic MediaWiki",
+ "action-smw-ruleedit": "editar páginas de regla (Semantic MediaWiki)",
+ "smw-property-predefined-default": "«$1» es una propiedad predefinida.",
+ "smw-property-predefined-common": "Esta propiedad está implementada previamente (también conocida como [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedad especial]) y viene con privilegios administrativos adicionales, pero puede usarse como cualquier otra [https://www.semantic-mediawiki.org/wiki/Property propiedad definida por usuario].",
+ "smw-property-predefined-ask": "\"$1\" es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que representa metainformación (en forma de un [https://www.semantic-mediawiki.org/wiki/Subobject subobjeto]) sobre consultas individuales.",
+ "smw-property-predefined-asksi": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que recopila el número de condiciones utilizadas en una consulta.",
+ "smw-property-predefined-askde": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que informa sobre la profundidad de una consulta.",
+ "smw-property-predefined-long-askde": "Es un valor numérico calculado con base en el anidamiento de subconsultas, en las cadenas de propiedades y en los elementos descriptores disponibles, con la ejecución de una consulta restringida por el parámetro de configuración <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe parámetros que influyen el resultado de una consulta.",
+ "smw-property-predefined-long-askpa": "Es parte de una colección de propiedades que especifican un [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Esta página muestra las [https://www.semantic-mediawiki.org/wiki/Property propiedades] y el número de usos disponible de cada una de ellas para este wiki. Para obtener estadísticas actualizadas, se recomienda ejecutar regularmente el <i>script</i> de mantenimiento de [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics estadísticas de propiedades]. Para una vista diferenciada, véanse las páginas especiales «[[Special:UnusedProperties|Propiedades sin uso]]» o «[[Special:WantedProperties|Propiedades requeridas]]».",
+ "smw-sp-properties-cache-info": "Los datos listados se han obtenido de la [https://www.semantic-mediawiki.org/wiki/Caching caché], y fueron actualizados por última vez el $1.",
+ "smw-sp-properties-header-label": "Lista de propiedades",
+ "smw-admin-settings-docu": "Muestra una lista de todas las preferencias predeterminadas y localizadas que son relevantes para el entorno de Semantic MediaWiki. Para más detalles sobre preferencias individuales, consulta la página de ayuda de [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-sp-admin-settings-button": "Generar lista de preferencias",
+ "smw-admin-idlookup-title": "Buscar",
+ "smw-admin-idlookup-docu": "Esta sección muestra detalles técnicos sobre una entidad individual (página de wiki, subobjeto, propiedad, etc.) de Semantic MediaWiki. La entrada puede ser un identificador numérico o una cadena que corresponda con el campo de búsqueda relevante. Observa que las referencias de los identificadores se relacionan con Semantic MediaWiki y no con los identificadores de página o de revisión de MediaWiki.",
+ "smw-admin-iddispose-title": "Eliminación",
+ "smw-admin-iddispose-docu": "Cabe resaltar que la operación de eliminación no está restringida y, si se confirma, quitará, del motor de almacenamiento, la entidad junto con todas sus referencias en las tablas pendientes. Por favor, realiza esta tarea con '''precaución''' y sólo después de haber consultado la [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentación].",
+ "smw-admin-iddispose-done": "El identificador «$1» se eliminó del motor de almacenamiento.",
+ "smw-admin-iddispose-references": "El ID «$1» {{PLURAL:$2|no tiene ninguna|tiene al menos una}} referencia activa:",
+ "smw-admin-iddispose-references-multiple": "Lista de correspondencias con al menos un registro de referencias activo.",
+ "smw-admin-iddispose-no-references": "No se pudo relacionar «$1» con ninguna entrada de la tabla.",
+ "smw-admin-idlookup-input": "Buscar:",
+ "smw-admin-objectid": "Identificador:",
+ "smw-admin-tab-general": "Visión general",
+ "smw-admin-tab-notices": "Avisos de obsolescencia",
+ "smw-admin-tab-supplement": "Funciones suplementarias",
+ "smw-admin-tab-registry": "Registro",
+ "smw-livepreview-loading": "Cargando…",
+ "smw-sp-searchbyproperty-description": "Esta página proporciona una [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interfaz de exploración] simple para encontrar entidades descritas por una propiedad y un valor con nombre. Otras interfaces de búsqueda disponibles incluyen la [[Special:PageProperty|búsqueda de propiedades de página]] y el [[Special:Ask|constructor de consultas]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista de resultados",
+ "smw-sp-searchbyproperty-nonvaluequery": "Una lista de valores que tienen asignada la propiedad \"$1\".",
+ "smw-sp-searchbyproperty-valuequery": "Una lista de las páginas que tienen la propiedad \"$1\" con el valor \"$2\" anotado.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" no puede asignarse a un tipo de número declarado con valor $2.",
+ "smw-datavalue-number-nullnotallowed": "«$1» devolvió «NULL», el cual no se permite como número.",
+ "smw-editpage-annotation-enabled": "Esta página admite anotaciones semánticas en el texto (p. ej. <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) para construir contenido estructurado y consultable provisto por Semantic MediaWiki. Para una descripción completa sobre cómo usar anotaciones o la función analizadora #ask, consulta a las páginas de ayuda [https://www.semantic-mediawiki.org/wiki/Help:Getting_started primeros pasos], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation anotaciones en el texto] o [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries consultas en línea].",
+ "smw-editpage-annotation-disabled": "Esta página no tiene habilitadas las anotaciones semánticas dentro del texto debido a restricciones en el espacio de nombres. Puedes encontrar instrucciones sobre cómo habilitar el espacio de nombres en la página de ayuda de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-editpage-property-annotation-enabled": "Esta propiedad puede extenderse usando anotaciones semánticas para especificar un tipo de dato (p. ej. <nowiki>\"[[Has type::Page]]\"</nowiki>) u otras declaraciones de asistencia (e.g. <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). Para saber cómo aumentar esta página, consulta las páginas de ayuda [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaración de una propiedad] o [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes lista de los tipos de datos disponibles].",
+ "smw-editpage-property-annotation-disabled": "Esta propiedad no puede extenderse con una anotación de tipo de dato (p. ej. <nowiki>\"[[Has type::Page]]\"</nowiki>) porque ya está predefinida (para más información, consulta la página de ayuda [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedades especiales]).",
+ "smw-editpage-concept-annotation-enabled": "Es posible extender este concepto por medio de la función #concept del analizador. Para saber cómo utilizar #concept, consulta la página de ayuda de los [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceptos].",
+ "smw-search-syntax-support": "En las búsquedas se puede utilizar la [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search sintaxis de consultas semánticas] para encontrar correspondencias mediante Semantic MediaWiki.",
+ "smw-search-input-assistance": "El [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistente de entrada] se activa también para facilitar la preselección de las entidades disponibles.",
+ "smw-search-help-ask": "Los siguientes enlaces explicarán cómo usar la sintaxis <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Seleccionar páginas] describe cómo seleccionar páginas y crear condiciones\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de búsqueda] enumera los operadores de búsqueda disponibles, incluidos los de consultas de rango y comodín",
+ "smw-search-input": "Entrada y busqueda",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Asistencia de entrada] se proporciona para el campo de entrada y requiere usar uno de los siguientes prefijos:\n\n*<code>p:</code> para habilitar sugerencias de propiedad (por ejemplo, <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> para habilitar sugerencias de categoría\n\n*<code>con:</code> para habilitar sugerencias de concepto",
+ "smw-search-syntax": "Sintaxis",
+ "smw-search-profile": "Extendido",
+ "smw-search-profile-tooltip": "Busca funciones en conexión con MediaWiki Semántica",
+ "smw-search-profile-sort-best": "Mejor coincidencia",
+ "smw-search-profile-sort-recent": "Más reciente",
+ "smw-search-profile-sort-title": "Título",
+ "smw-search-profile-extended-help-intro": "El [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile perfil extendido] Special:Search proporciona acceso para buscar funciones concretas a Semantic MediaWiki y su backend de consulta admitido.",
+ "smw-search-profile-extended-help-sort": "Especifica una preferencia de ordenamiento para mostrar los resultados con:",
+ "smw-search-profile-extended-help-sort-title": "* \"Título\" usando el título de la página (o el título en pantalla) como criterio de clasificación",
+ "smw-search-profile-extended-help-sort-recent": "* \"Más reciente\" mostrará primero las entidades modificadas más recientemente ( las entidades de subobjeto serán suprimidas así como las entidades que no están anotadas con una [[Propiedad:fecha de Modificación|de fecha de Modificación]])",
+ "smw-search-profile-extended-help-sort-best": "* \"Mejor coincidencia\" ordenará las entidades por [https://www.semantic-mediawiki.org/wiki/help:ElasticStore/Relevancy relevancia] basándose en las puntuaciones provistas por el backend",
+ "smw-search-profile-extended-help-form": "Se proporcionan formularios (si se actualizan) para que coincidan con casos de uso específicos, exponiendo diferentes campos de propiedades y valores para reducir el proceso de entrada y facilitar a los usuarios la tarea de realizar una solicitud de búsqueda. (ver $1)",
+ "smw-search-profile-extended-help-namespace": "La caja de selección del espacio de nombres estará oculta en cuanto se seleccione una forma, pero puede ser hecha visible con la ayuda del botón \"mostrar/ocultar\".",
+ "smw-search-profile-extended-help-search-syntax": "El campo de entrada de la búsqueda perimite el uso de la sintaxis <code>#preguntar</code> para definir una búsqueda concreta contextual con Semántica MediaWiki . Las expresiones útiles incluyen:",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Prefijos adicionales personalizados están disponibles y definidos cada uno como: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Algunas expresiones están reservadas como: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "' Algunas de las operaciones listadas son sólo útiles en conexión con un índice habilitado de texto lleno o el ElasticStore.''",
+ "smw-search-profile-extended-help-query": "Se utilizó <code><nowiki>$1</nowiki></code> como consulta.",
+ "smw-search-profile-extended-help-query-link": "(Para más detalles $1).",
+ "smw-search-profile-extended-help-find-forms": "formularios disponibles",
+ "smw-search-profile-extended-section-sort": "Ordenar por",
+ "smw-search-profile-extended-section-form": "Formularios",
+ "smw-search-profile-extended-section-search-syntax": "Entrada de búsqueda",
+ "smw-search-profile-extended-section-namespace": "Namespace",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "smw-search-profile-link-caption-query": "ver",
+ "smw-search-show": "Mostrar",
+ "smw-search-hide": "Ocultar",
+ "log-name-smw": "Registro de Semantic MediaWiki",
+ "log-show-hide-smw": "$1 el registro de Semantic MediaWiki",
+ "logeventslist-smw-log": "Registro de Semantic MediaWiki",
+ "log-description-smw": "Actividades para [https://www.semantic-mediawiki.org/wiki/Help:Logging tipos de evento habilitados] que han sido notificados por la MediaWiki Semántica y sus componentes.",
+ "logentry-smw-maintenance": "Sucesos relacionados con el mantenimiento emitidos por Semantic MediaWiki",
+ "smw-datavalue-import-unknown-namespace": "El espacio de nombres de importación «$1» es desconocido. Comprueba que los detalles de importación de OWL estén disponibles a través de [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "No se pudo encontrar el URI del espacio de nombres «$1» en la [[MediaWiki:Smw import $1|importación de $1]].",
+ "smw-datavalue-import-missing-type": "No se encontró ninguna definición de tipos para «$1» en la [[MediaWiki:Smw import $2|importación de $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Importación de $1]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" no tiene un formato válido. Se espera que conste de \"espacio de nombres\":\"identificador\" (por ejemplo, \"foaf:name\").",
+ "smw-datavalue-import-invalid-format": "Se esperaba que la cadena «$1» estuviese dividida en cuatro partes, pero no se comprendió su formato.",
+ "smw-property-predefined-impo": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe relaciones con [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulario importado].",
+ "smw-property-predefined-type": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe el [[Special:Types|tipo de datos]] de una propiedad.",
+ "smw-property-predefined-sobj": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que representa una construcción de [https://www.semantic-mediawiki.org/wiki/Help:Container contenedor].",
+ "smw-property-predefined-long-sobj": "El contenedor permite acumular pares de asignaciones propiedad-valor semejantes a los de páginas wiki normales pero ubicadas en un espacio de entidades diferente y con enlaces al sujeto que lo incluye.",
+ "smw-property-predefined-errp": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que permite rastrear anotaciones de valores irregulares en los errores de entrada.",
+ "smw-property-predefined-long-errp": "En la mayoría de los casos lo provoca un error de correspondencia de tipos o una restricción de [[Property:Allows value|valores]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value «$1»] es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que puede definir una lista de valores permitidos para restringir las asignaciones de valores de alguna propiedad.",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list «$1»] es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que permite especificar una referencia a una lista que contenga valores permitidos para restringir las asignaciones de valores en propiedades.",
+ "smw-datavalue-property-restricted-annotation-use": "La propiedad «$1» tiene aplicaciones limitadas y los usuarios no pueden utilizarla como propiedad de anotación.",
+ "smw-datavalue-property-restricted-declarative-use": "La propiedad «$1» es declarativa y solo puede utilizarse en páginas de propiedad o de categoría.",
+ "smw-datavalue-property-create-restriction": "No existe la propiedad «$1» y el usuario carece del permiso «$2» (consulta [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode Modo de autoridad]) para crear o anotar valores con una propiedad no aprobada.",
+ "smw-datavalue-property-invalid-character": "«$1» contiene el carácter registrado «$2» como parte de la etiqueta de una propiedad. Por ello, se ha clasificado como no válido.",
+ "smw-datavalue-property-invalid-chain": "No se permite el uso de «$1» como cadena de propiedades durante el proceso de anotación.",
+ "smw-datavalue-restricted-use": "El valor de datos \"$1\" ha sido marcado para uso restringido.",
+ "smw-datavalue-invalid-number": "No se puede interpretar a \"$1\" como un número.",
+ "smw-query-condition-circular": "Se detectó una posible condición circular en \"$1\".",
+ "smw-types-list": "Lista de tipos de datos",
+ "smw-types-default": "\"$1\" es un tipo de datos.",
+ "smw-types-help": "Más información y ejemplos en la [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 página de ayuda].",
+ "smw-type-anu": "«$1» es una variante del tipo de datos [[Special:Types/URL|URL]] y se utiliza en gran parte para declaraciones de exportación del tipo ''owl:AnnotationProperty''.",
+ "smw-type-boo": "«$1» es un tipo de datos primitivo para describir un valor de verdadero/falso.",
+ "smw-type-cod": "«$1» es una variante del tipo de datos [[Special:Types/Text|Text]] que está destinado para utilizarse con textos técnicos de cualquier longitud, tales como listados de código fuente.",
+ "smw-type-geo": "«$1» es un tipo de datos que describe ubicaciones geográficas y necesita la extensión [https://www.semantic-mediawiki.org/wiki/Extension:Maps Maps]",
+ "smw-type-tel": "«$1» es un tipo de datos especial para describir números telefónicos internacionales de conformidad con RFC 3966.",
+ "smw-type-txt": "\"$1\" es un tipo de dato primitivo que describe cadenas de longitud arbitraria.",
+ "smw-type-dat": "«$1» es un tipo de datos para representar puntos en el tiempo en un formato unificado.",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Esta página provee una interfaz de búsqueda para encontrar todos los valores de una propiedad o página dada. Otras interfazes de búsqueda incluyen [[Special:SearchByProperty|búsqueda de propiedad]] y el [[Special:Ask|constructor de preguntas]].",
+ "smw-property-predefined-errc": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que representa errores que aparecen en la conexión con anotaciones de valores o procesamiento de entradas inapropiados.",
+ "smw-property-predefined-long-errc": "Los errores se recopilan en un [https://www.semantic-mediawiki.org/wiki/Help:Container contenedor] que puede que incluya una referencia a la propiedad que provocó la discrepancia.",
+ "smw-property-predefined-errt": "«$1» es una propiedad predefinida, proporcionada [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que contiene una descripción textual de un error.",
+ "smw-subobject-parser-invalid-naming-scheme": "Un subobjeto definido por el usuario tenía un esquema de nombres invalido. La notación de punto ($1) utilizada en los primeros cinco caracteres está reservada para las extensiones. Puedes establecer un [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador].",
+ "smw-datavalue-record-invalid-property-declaration": "La definición del registro contiene la propiedad «$1», la cual está declarada ella misma como tipo de registro. Esto no se permite.",
+ "smw-property-predefined-mdat": "\"$1\" es una propiedad predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] que corresponde con la fecha de la última modificación de un sujeto.",
+ "smw-property-predefined-cdat": "\"$1\" es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que corresponde con la fecha de la primera revisión de un asunto.",
+ "smw-property-predefined-newp": "\"$1\" es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que indica si un sujeto es nuevo o no.",
+ "smw-property-predefined-ledt": "\"$1\" es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que contiene el nombre de la página del usuario que creó la última revisión.",
+ "smw-property-predefined-mime": "«$1» es una propiedad predefinida, proporcioada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe el tipo MIME de un archivo cargado.",
+ "smw-property-predefined-media": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe el tipo de multimedia de un archivo cargado.",
+ "smw-property-predefined-askfo": "\"$1\" es una propiedad predefinida, proporcionada [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que guarda el nombre del formato de resultado usado en una consulta.",
+ "smw-property-predefined-askst": "\"$1\" es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que describe las condiciones de la consulta como una cadena.",
+ "smw-property-predefined-askdu": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que contiene un valor de tiempo (en segundos) necesario para completar la ejecución de la consulta.",
+ "smw-property-predefined-asksc": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que identifica fuentes alternativas de consultas (por ejemplo, fuentes remotas o federadas).",
+ "smw-property-predefined-askco": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para describir el estado de una consulta o de sus componentes.",
+ "smw-property-predefined-long-askco": "El número o los números asignados representan un estado codificado interno que está explicado en la [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler página de ayuda].",
+ "smw-property-predefined-prec": "«$1» es una propiedad predefinida que describe una [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisión de muestra] (en dígitos decimales) para tipos de datos numéricos.",
+ "smw-types-extra-geo-not-available": "No se ha detectado la extensión [https://www.semantic-mediawiki.org/wiki/Extension:Maps Maps], por lo tanto, la capacidad para operar de «$1» está restringida.",
+ "smw-datavalue-monolingual-dataitem-missing": "Falta un elemento esperado para construir un valor compuesto monolingüe.",
+ "smw-datavalue-languagecode-missing": "El analizador sintáctico no pudo determinar el código de idioma de la anotación «$1» (p. ej., «algo@es»).",
+ "smw-datavalue-languagecode-invalid": "No se reconoce «$1» como un código de idioma admitido.",
+ "smw-property-predefined-lcode": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que representa un código de idioma con formato BCP47.",
+ "smw-type-mlt-rec": "\"$1\" es un tipo de datos [https://www.semantic-mediawiki.org/wiki/Help:Container contenedor] que asocia un valor de texto con un [[Property:Language code|código de idioma]] específico.",
+ "smw-types-extra-mlt-lcode": "El tipo de datos{{PLURAL:$2|requiere|no requiere}} un código de idioma (o sea, {{PLURAL:$2|no se acepta|se acepta}} una anotación de valor sin un código de idioma).",
+ "smw-property-predefined-text": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que representa un texto de longitud arbitraria.",
+ "smw-property-predefined-pdesc": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que permite describir una propiedad en el contexto de un idioma.",
+ "smw-property-predefined-list": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que define una lista de propiedades usadas con una propiedad de tipo [[Special:Types/Record|registro]].",
+ "smw-limitreport-intext-parsertime": "[SMW] Analizador de tiempo de la anotación en el texto",
+ "smw-limitreport-intext-postproctime": "[SMW] duración de posprocesamiento",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Tiempo de actualización del almacenaje (en la purga de la página)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw_allows_pattern": "Esta página debería contener una lista de referencias (seguido de [https://es.wikipedia.org/wiki/Expresi%C3%B3n_regular expresiones regulares]) para estar disponible por la propiedad [[Property:Allows pattern|Permite patrón]]. Para editar esta página, se requiere el permiso <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "La expresión regular \"$2\" clasificó a \"$1\" como no válida.",
+ "smw-datavalue-allows-pattern-reference-unknown": "La referencia del patrón \"$1\" no corresponde a ninguna entrada de [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "La referencia de lista «$1» no se corresponde con ninguna página de [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Al contenido de la lista «$1» le faltan elementos con un marcador de lista *.",
+ "smw-datavalue-feature-not-supported": "La funcionalidad \"$1\" no es compatible o se desactivó en este wiki.",
+ "smw-property-predefined-pvap": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que puede especificar una [[MediaWiki:Smw allows pattern|patrón de referencia]] para que coincida con una [https://es.wikipedia.org/wiki/Expresi%C3%B3n_regular expresión regular].",
+ "smw-property-predefined-dtitle": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que puede asignar un título de visualización diferente a una entidad.",
+ "smw-property-predefined-pvuc": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que restringe las asignaciones de valores para cada instancia a valores únicos.",
+ "smw-property-predefined-long-pvuc": "La singularidad se establece cuando dos valores no son iguales en su representación literal, cualquier violación de esta restricción se clasifica como errónea.",
+ "smw-datavalue-uniqueness-constraint-error": "La propiedad \"$1\" sólo permite que el único valor de las cesiones y \"$2\" ya fue anotado en el \" asunto \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "La propiedad \"$1\" sólo permite anotaciones de valores exclusivos. ''$2'' ya contiene un valor asignado. \"$3\" viola la restricción de exclusividad.",
+ "smw-property-predefined-boo": "«$1» es un [[Special:Types/Boolean|tipo]] y una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores boleanos.",
+ "smw-property-predefined-num": "«$1» es un [[Especial:Tipos/Número|tipo]] y propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores numéricos.",
+ "smw-property-predefined-dat": "«$1» es un [[Especial:Tipos/Fecha|tipo]] y propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores de fecha.",
+ "smw-property-predefined-uri": "«$1» es un [[Special:Types/URL|tipo]] y propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores URI/URL.",
+ "smw-property-predefined-qty": "«$1» es una [[Special:Types/Quantity|tipo]] y propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores de cantidad.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "«$1» contiene una diferencia y un identificador de huso no admitidos.",
+ "smw-datavalue-time-invalid-values": "El valor «$1» contiene información que no se puede interpretar en forma de «$2».",
+ "smw-datavalue-time-invalid-date-components-common": "«$1» contiene alguna información que no se puede interpretar.",
+ "smw-datavalue-time-invalid-date-components-dash": "«$1» contiene un guión extrínseco u otros caracteres que son inválidos para la interpretación de una fecha.",
+ "smw-datavalue-time-invalid-date-components-empty": "«$1» contiene algunos componentes vacíos.",
+ "smw-datavalue-time-invalid-date-components-three": "«$1» contiene más de tres componentes necesarios para la interpretación de una fecha.",
+ "smw-datavalue-time-invalid-date-components-sequence": "\"$1\" contiene una secuencia que no se ha podido interpretar contra una matriz disponible de correspondencias para componentes de una fecha.",
+ "smw-datavalue-time-invalid-ampm": "«$1» contiene «$2» como elemento de hora que no es válido en el formato convencional de 12 horas.",
+ "smw-datavalue-time-invalid-jd": "No se puede interpretar el valor de entrada «$1» como un DJ (día juliano) valido al ser reportado «$2».",
+ "smw-datavalue-time-invalid-prehistoric": "No se puede interpretar un valor de entrada prehistórico «$1». Por ejemplo, al haber especificado más años o un modelo de calendario que podría devolver resultados inesperados en un contexto prehistórico (anterior al año 10000).",
+ "smw-datavalue-time-invalid": "No se puede interpretar el valor de entrada «$1» como una fecha o componente temporal valido al ser reportado «$2».",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Falta el marcador de posición «$1» en el URL del formateador.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" es una dirección URL no válida.",
+ "smw-datavalue-external-identifier-formatter-missing": "A la propiedad le falta una asignación de [[Propiedad:URI del formateador externo|«URI del formateador externo»]].",
+ "smw-datavalue-keyword-maximum-length": "La palabra clave superó la longitud máxima de $1 {{PLURAL:$1|carácter|caracteres}}",
+ "smw-property-predefined-eid": "«$1» es una [[Special:Types/External identifier|tipo]] y propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar identificadores externos.",
+ "smw-property-predefined-peid": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que especifica un identificador externo.",
+ "smw-property-predefined-pefu": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para especificar un recurso externo con un marcador de posición.",
+ "smw-property-predefined-long-pefu": "Se espera que el URI contenga un marcador de posición que se ajustará con un valor de [[Special:Types/External identifier|identificador externo]] para formar una referencia de recurso válida.",
+ "smw-type-eid": "«$1» es una variante del tipo de dato [[Special:Types/Text/Texto]] que requiere propiedades asignadas para declarar una [[Propiedad:URI del formateador externo|URI del formateador externo]].",
+ "smw-property-predefined-keyw": "«$1» es una propiedad predefina y un [[Special:Types/Keyword|tipo]], proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que normaliza un texto y tiene una longitud de caracteres restringida.",
+ "smw-type-keyw": "«$1» es una variante del tipo de dato [[Special:Types/Text|texto]] que tiene una longitud de caracteres restringida y normaliza su representación del contenido.",
+ "smw-datavalue-stripmarker-parse-error": "El valor aportado «$1» contiene [https://en.wikipedia.org/wiki/Help:Strip_markers ''strip markers''] (marcadores de tira), por lo que no puede ser analizado suficientemente.",
+ "smw-datavalue-parse-error": "No se comprendió el valor «$1» proporcionado.",
+ "smw-datavalue-propertylist-invalid-property-key": "La lista de propiedades «$1» contenía una clave de propiedad no válida, «$2».",
+ "smw-datavalue-type-invalid-typeuri": "No se pudo transformar el tipo «$1» en una representación de URI válida.",
+ "smw-datavalue-wikipage-missing-fragment-context": "El valor de entrada wikipágina «$1» no puede ser utilizado sin una página de contexto.",
+ "smw-datavalue-wikipage-invalid-title": "El valor de entrada de tipo página «$1» contiene caracteres inválidos o está incompleto, por lo que puede causar resultados inesperados durante una consulta o proceso de anotación.",
+ "smw-datavalue-wikipage-property-invalid-title": "La propiedad «$1» (como tipo de página) con el valor de entrada «$2» contiene caracteres inválidos o está incompleto, por lo que puede causar resultados inesperados durante una consulta o proceso de anotación.",
+ "smw-datavalue-wikipage-empty": "El valor de entrada wikipágina está vacío (p. ej., <code>[[SomeProperty::]], [[]]</code>), por lo que no puede ser utilizado como un nombre o como parte de una condición de consulta.",
+ "smw-type-ref-rec": "«$1» es un tipo [https://www.semantic-mediawiki.org/wiki/Container contenedor] que permite registrar información adicional (p. ej., el origen de los datos) sobre la asignación de un valor.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "El tipo [[Special:Types/Reference|referencia]] espera una lista de propiedades para ser declaradas usando la propiedad [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Tiene campos].",
+ "smw-parser-invalid-json-format": "El analizador de JSON devolvió «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "No se puede utilizar «$1» como etiqueta preferida porque el idioma «$2» ya se ha asignado a la etiqueta «$3».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copiar enlace en el portapapeles",
+ "smw-property-userdefined-fixedtable": "«$1» fue configurada como una [https://www.semantic-mediawiki.org/wiki/Fixed_properties propiedad fijada] y cualquier modificación de su [https://www.semantic-mediawiki.org/wiki/Type_declaration tipo de declaración] requiere que se ejecute <code>setupStore.php</code> o completar la tarea especial [[Special:SemanticMediaWiki|«Instalación y actualización de la base de datos»]].",
+ "smw-data-lookup": "Obteniendo datos...",
+ "smw-data-lookup-with-wait": "La solicitud está procesándose y puede tomar algo de tiempo.",
+ "smw-no-data-available": "No hay datos disponibles.",
+ "smw-property-req-violation-missing-fields": "A la propiedad «$1» le faltan detalles de declaración para el tipo «$2» anotado al no definir la propiedad <code>Tiene campos</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "A la propiedad «$1» le faltan detalles de declaración para el tipo anotado al no definir la propiedad <code>URI del formateador externo</code>.",
+ "smw-property-req-violation-predefined-type": "La propiedad «$1», como propiedad predefinida, contiene un tipo de declaración «$2» que es incompatible con el tipo predeterminado de esta propiedad.",
+ "smw-property-req-violation-import-type": "Fue decteado un tipo de declaración que es incompatible con el tipo predefinido del vocabulario importado «$1». En general, no es necesario declarar un tipo porque la información se recupera de la definición de importación.",
+ "smw-property-req-violation-change-propagation-locked-error": "La propiedad «$1» fue alterada y necesita que las entidades asignadas sean reevaluadas usando un proceso de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. La página de la propiedad ha sido protegida hasta que la actualización de especificación primaria se haya completado para prevenir interrupciones intermedias o especificaciones contradictorias. El proceso puede tomar un momento antes de que la página pueda ser desprotegida, este dependerá del tamaño y la frecuencia del planificador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cola de trabajo] del planificador.",
+ "smw-property-req-violation-change-propagation-locked-warning": "La propiedad «$1» fue alterada y necesita que las entidades asignadas sean reevaluadas usando un proceso de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. La actualización puede tomar un momento que dependerá del tamaño y la frecuencia del planificador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cola de trabajo] del planificador. Se sugiere que se posponga cambios en la propiedad para prevenir interrupciones intermedias o especificaciones contradictorias.",
+ "smw-property-req-violation-change-propagation-pending": "La actualización de la [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios] está pendiente ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|trabajo|trabajos}}] {{PLURAL:$1|estimado|estimados}}) y se recomienda esperar a realizar modificaciones a una propiedad hasta que el proceso haya finalizado para prevenir interrupciones intermedias y especificaciones de contradictorias.",
+ "smw-property-req-violation-missing-maps-extension": "MediaWiki Semántica no fue capaz de detectar la extensión [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"], la cual es un prerequisito para el tipo seleccionado y como consecuencia limita la funcionalidad de esta propiedad.",
+ "smw-property-req-violation-type": "",
+ "smw-change-propagation-protection": "Esta página está bloqueada para evitar modificaciones accidentales de los datos durante la ejecución de una actualización de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. El proceso puede demorar unos momentos antes de que se desbloquee la página, ya que depende del tamaño y de la frecuencia del programador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cola de tareas].",
+ "smw-category-change-propagation-locked-error": "La categoría «$1» fue alterada y necesita que las entidades asignadas sean reevaluadas usando un proceso de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. Mientras tanto, la página de la categoría ha sido protegida hasta que se haya completado la actualización de especificación primaria para prevenir interrupciones intermedias o especificaciones contradictorias. El proceso puede tomar un momento antes de que la página pueda ser desprotegida, este dependerá del tamaño y la frecuencia del planificador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cola de trabajo] del planificador.",
+ "smw-category-change-propagation-locked-warning": "La categoría «$1» fue alterada y necesita que las entidades asignadas sean reevaluadas usando un proceso de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. La actualización puede tomar un momento que dependerá del tamaño y la frecuencia del planificador de la [https://www.mediawiki.org/wiki/Manual:Job_queue cola de trabajo] del planificador. Se sugiere que se posponga cambios en la propiedad para prevenir interrupciones intermedias o especificaciones contradictorias.",
+ "smw-category-change-propagation-pending": "Las actualizaciones de la [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios] están pendientes ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|trabajo|trabajos}}] {{PLURAL:$1|estimado|estimados}}) y se recomienda esperar a realizar modificaciones en la categoría hasta que el proceso haya finalizado para prevenir interrupciones intermedias o especificaciones contradictorias.",
+ "smw-category-invalid-value-assignment": "\"$1\" no es reconocido como una categoría válida o una anotación válida.",
+ "protect-level-smw-pageedit": "Permitir únicamente usuarios con el permiso de edición de páginas (Semantic MediaWiki)",
+ "smw-create-protection": "La creación de la propiedad «$1» está restringida a los usuarios que cuenten con el privilegio (o pertenezcan al [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios]) «$2» adecuado, siempre que esté activado el [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridad].",
+ "smw-create-protection-exists": "Las modificaciones a la propiedad «$1» están restringidas a los usuarios que cuenten con el privilegio (o pertenezcan al [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios]) «$2» adecuado, siempre que esté activado el [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridad].",
+ "smw-edit-protection": "Esta página está [[Propiedad:Está protegida de edición|protegida]] para prevenir la modificación accidental de datos y solo puede ser editada por usuarios con los derechos de edición apropiados («$1») o por un [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios].",
+ "smw-edit-protection-disabled": "Se desactivó la protección contra ediciones; por ello, no se puede utilizar «$1» para proteger las páginas de entidades de ediciones no autorizadas.",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki ha actualizado el estado de protección conforme a la propiedad «Está protegida de edición».",
+ "smw-edit-protection-enabled": "Edición protegida (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Esta página está protegida y solo pueden modificarla aquellos usuarios que cuenten con el [https://www.semantic-mediawiki.org/wiki/Help:Permissions permiso] <code>smw-patternedit</code> adecuado.",
+ "smw-property-predefined-edip": "«$1» es una propiedad predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para señalar si la edición está protegida o no.",
+ "smw-property-predefined-long-edip": "Aunque cualquier usuario puede añadir esta propiedad a un tema, solo aquellos usuarios que posean un privilegio especializado pueden editar o revocar la protección de una entidad después de haberse añadido esta.",
+ "smw-query-reference-link-label": "Referencia de consulta",
+ "smw-format-datatable-emptytable": "No hay datos disponibles en la tabla",
+ "smw-format-datatable-info": "Se muestran las entradas _START_ a _END_, de _TOTAL_",
+ "smw-format-datatable-infoempty": "Se muestran de 0 a 0 de 0 entradas",
+ "smw-format-datatable-infofiltered": "(filtradas de un total de _MAX_ entradas)",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-lengthmenu": "Mostrar las entradas de _MENU_",
+ "smw-format-datatable-loadingrecords": "Cargando…",
+ "smw-format-datatable-processing": "Procesando…",
+ "smw-format-datatable-search": "Buscar:",
+ "smw-format-datatable-zerorecords": "No se encontró ningún registro que coincidiera",
+ "smw-format-datatable-first": "Primer",
+ "smw-format-datatable-last": "Último",
+ "smw-format-datatable-next": "Siguiente",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": actívalo para ordenar la columna ascendentemente",
+ "smw-format-datatable-sortdescending": ": actívalo para ordenar la columna descendentemente",
+ "smw-format-datatable-toolbar-export": "Exportar",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "La categoría «$1» contiene un objetivo de redirección no válido a un espacio de nombres que no es de categoría.",
+ "smw-parser-function-expensive-execution-limit": "La función de análisis ha alcanzado el limite para ejecuciones costosas (véase el parámetro de configuración [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "Semantic MediaWiki está actualizando la página actual siempre y cuando haya consultas que necesiten posprocesamiento.",
+ "apihelp-smwinfo-summary": "Módulo de la API para recuperar información sobre las estadísticas de Semantic MediaWiki y otros metadatos.",
+ "apihelp-ask-summary": "Módulo de la API para realizar consultas en Semantic MediaWiki mediante el lenguaje Ask.",
+ "apihelp-askargs-summary": "Módulo de la API para realizar consultas en Semantic MediaWiki mediante el lenguaje Ask en forma de lista de condiciones, visualizaciones y parámetros.",
+ "apihelp-browsebyproperty-summary": "Módulo de la API para recuperar información sobre alguna propiedad o lista de propiedades.",
+ "apihelp-browsebysubject-summary": "Módulo de la API para recuperar información sobre algún tema.",
+ "apihelp-smwtask-summary": "Módulo de la API para ejecutar tareas relacionadas con Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Módulo API para añadir compatibilidad con las actividades de exploración en diferentes tipos de entidades de Semantic MediaWiki.",
+ "apihelp-ask-parameter-api-version": "Formato de salida:\n;2:Formato compatible con el formato antiguo utilizando {} en la lista de resultados.\n;3:Formato experimental utilizando [] en la lista de resultados.",
+ "smw-api-invalid-parameters": "Parámetros no válidos: \"$1\"",
+ "smw-parser-recursion-level-exceeded": "El nivel de recursiones $1 fue excedido durante el proceso de análisis. Se sugiere validar la estructura de la plantilla o, si fuese necesario, ajustar el parámetro de configuración <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "Se {{PLURAL:$1|muestra $1 página que utiliza|muestran $1 páginas que utilizan}} esta propiedad.",
+ "smw-property-page-list-search-count": "Se {{PLURAL:$1|muestra $1 página que utiliza|muestran $1 páginas que utilizan}} esta propiedad con una correspondencia de valor «$2».",
+ "smw-property-reserved-category": "Categoría",
+ "smw-category": "Categoría",
+ "smw-datavalue-uri-invalid-scheme": "No se incluyó «$1» en los esquemas de URI válidos.",
+ "smw-browse-property-group-title": "Grupo de propiedades",
+ "smw-browse-property-group-label": "Etiqueta de grupo de propiedades",
+ "smw-browse-property-group-description": "Descripción de grupo de propiedades",
+ "smw-property-predefined-ppgr": "«$1» es una propiedad predefinida, provista por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], que identifica entidades (principalmente categorías) empleadas como instancias de agrupamiento para las propiedades.",
+ "smw-filter": "Filtro",
+ "smw-section-expand": "Expandir la sección",
+ "smw-section-collapse": "Contraer la sección",
+ "smw-ask-format-help-link": "Formato [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Ayuda",
+ "smw-cheat-sheet": "Hoja de referencia",
+ "smw-personal-jobqueue-watchlist": "Lista de seguimiento (cola de tareas)",
+ "smw-property-predefined-label-skey": "Clave de ordenación",
+ "smw-processing": "Procesando…",
+ "smw-redirect-target-unresolvable": "El destino es irresoluble por el motivo «$1»",
+ "smw-types-title": "Tipo: $1",
+ "smw-ask-title-keyword-type": "Búsqueda de palabras clave",
+ "smw-ask-message-keyword-type": "Esta búsqueda coincide con la condición <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "No se puede conectar con el destino remoto «$1».",
+ "smw-remote-source-disabled": "El origen '''$1''' ha desactivado las solicitudes remotas.",
+ "smw-remote-source-unmatched-id": "La fuente '''$1''' no concuerda con la versión de MediaWiki Semántica que soporta solicitudes remotas.",
+ "smw-remote-request-note": "El resultado se recupera a partir del origen remoto '''$1'''. Es probable que el contenido generado contenga información no disponible en el wiki actual.",
+ "smw-remote-request-note-cached": "El resultado se '''almacena temporalmente''' a partir del origen remoto '''$1'''. Es probable que el contenido generado contenga información no disponible en el wiki actual.",
+ "smw-parameter-missing": "Falta el parámetro \"$1\".",
+ "smw-property-tab-usage": "Uso",
+ "smw-property-tab-redirects": "Sinónimos",
+ "smw-property-tab-subproperties": "Subpropiedades",
+ "smw-property-tab-errors": "Asignaciones incorrectas",
+ "smw-property-tab-specification": "... más",
+ "smw-concept-tab-list": "Lista",
+ "smw-concept-tab-errors": "Errores",
+ "smw-ask-tab-result": "Resultado",
+ "smw-ask-tab-extra": "Extras",
+ "smw-ask-tab-debug": "Depuración",
+ "smw-ask-tab-code": "Código"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/et.json b/www/wiki/extensions/SemanticMediaWiki/i18n/et.json
new file mode 100644
index 00000000..c1c630f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/et.json
@@ -0,0 +1,52 @@
+{
+ "@metadata": {
+ "authors": [
+ "Avjoska",
+ "Pikne",
+ "Roland"
+ ]
+ },
+ "smw_viewasrdf": "RDF-voog",
+ "smw_finallistconjunct": " ja",
+ "smw_factbox_head": "Fakte lehekülje \"$1\" kohta",
+ "smw_isspecprop": "See omadus on selles vikis eriomadus.",
+ "smw_concept_description": "Mõiste \"$1\" kirjeldus",
+ "version-semantic": "Semantilised lisad",
+ "smw_printername_list": "Loend",
+ "smw_printername_table": "Tabel",
+ "smw_printername_template": "Mall",
+ "smw_printername_category": "Kategooria",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-link": "Näita väärtuseid linkidena",
+ "smw-paramdesc-embedonly": "Näita ilma pealkirjadeta",
+ "smw-paramdesc-feedtype": "Vootüüp",
+ "smw-label-feed-description": "$2-voog: $1",
+ "smw_iq_disabled": "Semantilised päringud on siin vikis keelatud.",
+ "smw_iq_moreresults": "... rohkem tulemusi",
+ "smw_parseerror": "Sisestatud väärtusest ei saadud aru.",
+ "smw_notitle": "Väärtust \"$1\" ei saa siin vikis lehekülje pealkirjana kasutada.",
+ "smw_emptystring": "Tühjad sõned ei sobi.",
+ "smw_true_words": "tõene,t,jah,j",
+ "smw_false_words": "väär,v,ei,e",
+ "smw_nofloat": "\"$1\" pole number.",
+ "smw_purge": "Värskenda",
+ "smw_types_docu": "Järgnevalt on loetletud kõik [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes andmetüübid], mida omadustele saab omistada.",
+ "smw-special-types-no-such-type": "Soovitud andmetüüpi ei eksisteeri",
+ "smw-statistics": "Semantilised arvandmed",
+ "smw-statistics-property-instance": "Omaduse {{PLURAL:$1|väärtus|väärtusi}} (kokku)",
+ "smw-ask-delete": "[Kustuta]",
+ "smw_sbv_submit": "Otsi tulemusi",
+ "browse": "Viki sirvimine",
+ "smw_browse_go": "Mine",
+ "smw_pp_type": "Omadus",
+ "smw_result_prev": "Eelmised",
+ "smw_result_next": "Järgmised",
+ "smw_smwadmin_datarefreshstopconfirm": "Jah, olen {{GENDER:$1|kindel}}.",
+ "smw-createproperty-isproperty": "Selle omaduse tüüp on $1.",
+ "smw-ui-tooltip-title-property": "Omadus",
+ "smw-ui-tooltip-title-warning": "Viga",
+ "smw-ui-tooltip-title-event": "Sündmus",
+ "smw_unknowntype": "Selle omaduse tüüp on vigane.",
+ "smw-pa-property-predefined-default": "\"$1\" on eelmääratletud omadus.",
+ "smw-livepreview-loading": "Laadimine..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/eu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/eu.json
new file mode 100644
index 00000000..f5c1f4eb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/eu.json
@@ -0,0 +1,328 @@
+{
+ "@metadata": {
+ "authors": [
+ "An13sa",
+ "Kobazulo",
+ "පසිඳු කà·à·€à·’න්ද",
+ "Subi",
+ "Mikel Ibaiba",
+ "Sator",
+ "Theklan",
+ "Fitoschido",
+ "Amaia",
+ "EukeneFL"
+ ]
+ },
+ "smw-title": "MediaWiki semantikoa",
+ "smw-semantics-not-enabled": "MediaWiki Semantikoaren funtzionalitatea ez da wiki honetarako gaitu.",
+ "smw_viewasrdf": "RDF jarioa",
+ "smw_finallistconjunct": ", eta",
+ "smw-factbox-head": "... \"$1\"-ren inguruko gehiago",
+ "smw-factbox-facts": "Gertaerak",
+ "smw-factbox-facts-help": "Erabiltzaile batek sortutako adierazpen eta ekintzak erakusten ditu.",
+ "smw-factbox-facts-derived": "Ondoriozko gertaerak",
+ "smw-factbox-facts-derived-help": "Arau batetik edo beste arrazoimen teknika baten laguntzaz eratorritako ekintzak erakusten ditu.",
+ "smw_isspecprop": "Propietate hau wiki honetan propietate berezia da.",
+ "smw-concept-cache-header": "Katxearen erabilera",
+ "smw-concept-no-cache": "Ez da go katxerik.",
+ "smw_concept_description": "\"$1\" kontzeptuaren deskribapena",
+ "smw_multiple_concepts": "Kontzeptu orri bakoitzak kontzeptu definizio bat bakarrik izan dezake.",
+ "smw_concept_cache_miss": "\"$1\" kontzeptua ezin da momentu honetan erabili, wiki ezarpenek konexiorik gabe konputatzea eskatzen dutelako. \nDenbora pasa ahala arazoak badiau, eskatu zure guneko administratzaileari kontzeptu hau erabilgarri jartzeko.",
+ "smw_noinvannot": "Balioak ezin dira alderantzizko propietateetara egokitu.",
+ "version-semantic": "Luzapen semantikoak",
+ "smw_baduri": "\"$1\" formako URIak ez daude onartuak.",
+ "smw_printername_count": "Emaitzak zenbatu",
+ "smw_printername_csv": "CSV esportatu",
+ "smw_printername_dsv": "DSV esportatu",
+ "smw_printername_debug": "Araztu eskaera (adituentzat)",
+ "smw_printername_embedded": "Txertatu orriaren edukia",
+ "smw_printername_json": "JSON esportatu",
+ "smw_printername_list": "Zerrenda",
+ "smw_printername_plainlist": "Zerrenda laua",
+ "smw_printername_ol": "Zenbaketa",
+ "smw_printername_table": "Taula",
+ "smw_printername_broadtable": "Taula zabala",
+ "smw_printername_template": "Txantiloia",
+ "smw_printername_templatefile": "Txantiloi fitxategia",
+ "smw_printername_rdf": "RDF esportatu",
+ "smw_printername_category": "Kategoria",
+ "validator-type-class-SMWParamSource": "testua",
+ "smw-paramdesc-limit": "Bueltatzeko gehienezko emaitza kopurua.",
+ "smw-paramdesc-headers": "Erakutsi goiburu/propietate izenak",
+ "smw-paramdesc-link": "Balioak link moduan erakutsi",
+ "smw-paramdesc-intro": "Eskaera emaitzen aurretik erakusteko testua, baldin balego",
+ "smw-paramdesc-outro": "Eskaera emaitzen ondoren erakusteko testua, baldin balego",
+ "smw-paramdesc-default": "Eskaerak emaitzik ez izatekotan erakusteko testua",
+ "smw-paramdesc-sep": "Balioen arteko banaketa",
+ "smw-paramdesc-valuesep": "Emaitza baten propietate baten balioen arteko banatzailea.",
+ "smw-paramdesc-distributionlimit": "Balio distribuzioa balio batzuen zenbaketara mugatu.",
+ "smw-paramdesc-columns": "Emaitzak zenbat zutabetan erakutsi",
+ "smw-paramdesc-embedonly": "Goibururik ez erakutsi",
+ "smw-paramdesc-rdfsyntax": "Erabiltzeko RDF sintaxia",
+ "smw-paramdesc-csv-sep": "Zutabe bereizle bat zehazten du",
+ "smw-paramdesc-csv-valuesep": "Balio bereizle bat zehazten du",
+ "smw-paramdesc-dsv-separator": "Erabiliko den banaketa",
+ "smw-paramdesc-dsv-filename": "DSV fitxategirako izena",
+ "smw-paramdesc-filename": "Irtengo den fitxategirako izena",
+ "smw-paramdesc-sort": "Eskaera ordenatzeko erabili egingo den jabetza",
+ "smw-paramdesc-searchlabel": "Bilaketa jarraitzeko testua",
+ "smw-paramdesc-named_args": "Izendatu txantiloira pasatutako argudioak",
+ "smw-paramdesc-template-arguments": "Izendatutako argumentuak txantiloiara nola igarotzen diren ezartzen du",
+ "smw-paramdesc-export": "Esportatze aukera",
+ "smw-paramdesc-json-type": "Serializazio mota",
+ "smw-paramdesc-source": "Eskaera iturri alternatiboa",
+ "smw-paramdesc-jsonsyntax": "Erabiliko den JSON sintaxia",
+ "smw-printername-feed": "RSS eta Atom feed",
+ "smw-paramdesc-feedtype": "Feed mota",
+ "smw_iq_disabled": "Eskaera semantikoak desgaitu egin dira wiki honetarako.",
+ "smw_iq_moreresults": "... emaitza gehiago",
+ "smw_parseerror": "Emandako balioa ez da ulertu.",
+ "smw_notitle": "Wiki honetan \"$1\" ezin da erabili orrialdeko izen bezala.",
+ "smw_noproperty": "Wiki honetan \"$1\" ezin da erabili propietate izen gisa.",
+ "smw_wrong_namespace": "Hemen orriak \"$1\" izen-eremuan soilik onartzen dira.",
+ "smw_manytypes": "Propietaterako definitutako mota bat baino gehiago.",
+ "smw_emptystring": "Hutsik dauden kateak ez dira onartzen.",
+ "smw_true_words": "egia,e,bai,b",
+ "smw_false_words": "gezurra,g,ez,e",
+ "smw_nofloat": "\"$1\" ez da zenbakia.",
+ "smw_infinite": "\"$1\" bezain zenbaki handiak ez dira onartzen.",
+ "smw_unitnotallowed": "\"$1\" ez da jabetza honetarako baliozko neurri unitate bezala izendatzen.",
+ "smw_nounitsdeclared": "Neurketa unitaterik ez da deklaratu jabetza honetarako.",
+ "smw_novalues": "Ez da baliorik zehaztu.",
+ "smw_nodatetime": "$1 data ez da ulertu.",
+ "smw_misplacedsubquery": "Azpi-eskaeraren bat onartzen ez diren leku batean erabilia izan da.",
+ "smw_type_header": "$1 motatako propietateak",
+ "smw_typearticlecount": "{{PLURAL:$1|Propietate $1|$1 propietate}} mota hau darabiltenak erakusten.",
+ "smw_attribute_header": "\"$1\" protietatea erabiltzen ari diren orriak",
+ "smw_attributearticlecount": "Propietate hau {{PLURAL:$1|darabilen orri $1|darabilten $1 orri}} erakusten.",
+ "smw-propertylist-subproperty-header": "Azpipropietateak",
+ "smw-propertylist-redirect-header": "Sinonimoak",
+ "smw-propertylist-error-header": "Eleipen desegokia duten orrialdeak",
+ "smw-propertylist-count": "$1 erakusten {{PLURAL:$1|entity|entities}}rekin erlazionatuta.",
+ "smw-propertylist-count-with-restricted-note": "$1 erakusten {{PLURAL:$1|entity|entities}}rekin erlazionatuta (gehiago daude baina erakusketa \"$2\"ra murriztuta dago).",
+ "smw-propertylist-count-more-available": "$1 erakusten {{PLURAL:$1|entity|entities}}rekin erlazionatuta (erabilgarri gehiago).",
+ "exportrdf": "Orriak RDFra esportatu",
+ "smw_exportrdf_docu": "Orri honek RDF formatuan dauden datuak eskuratzen usten dizu. Orriak esportatzeko, idatzi izenburuan beheko testu-koadroan, izenburu bat lerro bakoitzeko.",
+ "smw_exportrdf_backlinks": "Esportatutako orriari erreferentzia egiten dioten orri guztiak ere esportatu. Bertan nabiga daitekeen RDFa sortzen du.",
+ "smw_exportrdf_lastdate": "Ez esportatu zehaztutako epearen ondoren aldatu ez diren orriak.",
+ "smw_exportrdf_submit": "Esportatu",
+ "uriresolver": "URIKonpontzailea",
+ "properties": "Propietateak",
+ "smw_properties_docu": "Hurrengo propietateak wikian erabiltzen dira.",
+ "smw_propertylackspage": "Propietate guztiak orri batek deskribatutakoak izan beharko lukete!",
+ "smw_propertylackstype": "Ez da motarik zehaztu propietate honetarako (oraingoz $1 mota onartuta).",
+ "smw_propertyhardlyused": "Propietate hau gutxi erabiltzen da wiki barruan!",
+ "smw-property-name-invalid": "Ezin da $1 propietatea erabili (propietate izen baliogabea).",
+ "smw-property-name-reserved": "\"$1\" erreserbatutako izen bezala zerrendatu da, beraz, ez litzateke propietate izen bezala erabili beharko. Izan liteke [https://www.semantic-mediawiki.org/wiki/Help:Property_naming help page] orrian izen hau zergatik erreserbatu den azaltzea.",
+ "smw-sp-property-searchform": "Hurrengoa daramaten propietateak aurkeztu:",
+ "smw-special-property-searchform": "Hurrengoa daramaten propietateak aurkeztu:",
+ "smw-special-property-searchform-options": "Aukerak",
+ "smw-special-wantedproperties-filter-label": "Iragazkia:",
+ "smw-special-wantedproperties-filter-none": "Ezer ez",
+ "smw-special-wantedproperties-filter-unapproved": "Ezetsia",
+ "concepts": "Kontzeptuak",
+ "smw-special-concept-header": "Kontzeptu-zerrenda",
+ "smw-special-concept-count": "Ondorengo {{PLURAL:$1|kontzeptua zerrendatzen da|$1 kontzeptuak zerrendatzen dira}}.",
+ "smw-special-concept-empty": "Ez da kontzepturik aurkitu.",
+ "unusedproperties": "Erabili gabeko propietateak",
+ "smw-unusedproperty-template": "$2 motatako $1",
+ "wantedproperties": "Eskatutako propietateak",
+ "smw-wantedproperty-template": "$1 ({{PLURAL:$2|behin|$2 aldiz}} erabilia)",
+ "smw-special-wantedproperties-template": "$1 ({{PLURAL:$2|behin|$2 aldiz}} erabilia)",
+ "smw_purge": "Eguneratu",
+ "smw-purge-failed": "Freskatzeak kale egin du",
+ "types": "Motak",
+ "smw-special-types-no-such-type": "Zehaztutako data mota ez da existitzen",
+ "smw-statistics": "Estatistika semantikoak",
+ "smw-statistics-property-instance": "Propietate {{PLURAL:$1|balioa|balioak}} (guztira)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propietatea|Propietateak}}]] (guztira)",
+ "smw-statistics-query-size": "Kontsultaren tamainia",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Kontzeptua|Kontzeptuak}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Kontzeptua|Kontzeptuak}}]]",
+ "smw-statistics-datatype-count": "[[Special:Motak|{{PLURAL:$1|Datu mota|Datu motak}}]]",
+ "ask": "Bilaketa semantikoa",
+ "smw_ask_sortby": "Zutabeka sailkatu (hautazkoa)",
+ "smw_ask_ascorder": "Gorantz",
+ "smw_ask_descorder": "Beherantz",
+ "smw-ask-order-rand": "Ausazkoa",
+ "smw_ask_submit": "Emaitzak bilatu",
+ "smw_ask_editquery": "Kontsulta aldatu",
+ "smw_add_sortcondition": "[Gehitu sailkapen baldintza]",
+ "smw-ask-sort-add-action": "Gehitu sailkapen baldintza",
+ "smw_ask_hidequery": "Ezkutuko eskaera",
+ "smw_ask_help": "Laguntza eskatuz",
+ "smw_ask_queryhead": "Baldintza",
+ "smw_ask_printhead": "Hautapena inprimatu",
+ "smw_ask_printdesc": "(gehitu propietate izen bat lerro bakoitzeko)",
+ "smw_ask_format_as": "Formatua honela:",
+ "smw_ask_defaultformat": "lehenetsia",
+ "smw_ask_otheroptions": "Beste aukerak",
+ "smw-ask-otheroptions-info": "Sekzio honek inprimatze formatuak aldatzen dituzten aukerak ditu. Parametroen deskribapenak arratoria gainetik igarota ikus daitezke.",
+ "smw-ask-otheroptions-collapsed-info": "Mesedez, erabili gehi ikurra eskuragarri dauden aukera guztiak ikusteko",
+ "smw-ask-delete": "Kendu",
+ "smw-ask-sorting": "Sailkapena",
+ "smw-ask-options": "Aukerak",
+ "smw-ask-options-sort": "Sailkapen aukerak",
+ "smw-ask-format-options": "Formatua eta aukerak",
+ "smw-ask-parameters": "Parametroak",
+ "smw-ask-search": "Bilatu",
+ "smw-ask-no-cache": "Cache-rik ez",
+ "smw-ask-no-cache-desc": "Eskaera cacherik gabeko emaitzak",
+ "smw-ask-result": "Emaitza",
+ "smw-ask-empty": "Sarrera guztiak ezabatu",
+ "smw-ask-format": "Formatua",
+ "smw-ask-format-selection-help": "Lagundu hautatutako formatuarekin: $1",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> propietate bilaketa",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> kategoria bilaketa bat aktibatzeko",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> kontzeptu bilaketa bay aktibatzeko",
+ "smw-ask-format-change-info": "Formatua aldatua izan da eta eskaera berriro exekutatu beharra dago parametro berri eta bistaratze aukera berriekin bat egiteko.",
+ "searchbyproperty": "Propietateen arabera bilatu",
+ "processingerrorlist": "Akats zerrenda prozesatzen",
+ "smw_sbv_property": "Propietatea:",
+ "smw_sbv_value": "Balioa:",
+ "smw_sbv_submit": "Emaitzak bilatu",
+ "browse": "Arakatu wikia",
+ "smw_browselink": "Propietateak arakatu",
+ "smw_browse_article": "Arakatzen hasiko den orriaren izena sartu.",
+ "smw_browse_go": "Joan",
+ "smw_browse_show_incoming": "Sarrera propietateak erakutsi",
+ "smw_browse_hide_incoming": "Sarrerako propietateak ezkutatu",
+ "smw_browse_no_outgoing": "Orrialde honek ez du propietaterik.",
+ "smw_browse_no_incoming": "Horri honetara propietaterik ez da linkatzen.",
+ "smw-browse-show-group": "Taldeak erakutsi",
+ "smw-browse-hide-group": "Taldeak ezkutatu",
+ "smw_inverse_label_default": "$1ren",
+ "pageproperty": "Orri propietate bilaketa",
+ "smw_pp_from": "Orritik:",
+ "smw_pp_type": "Propietatea:",
+ "smw_pp_submit": "Emaitzak bilatu",
+ "smw_result_prev": "Aurrekoa",
+ "smw_result_next": "Hurrengoa",
+ "smw_result_results": "Emaitzak",
+ "smw_result_noresults": "Ez dago emaitzarik.",
+ "smwadmin": "Funtzio administratiboak",
+ "smw-admin-statistics-job-title": "Lan estatistikak",
+ "smw-admin-statistics-querycache-title": "Kontsulta \"cache\" estatistikak",
+ "smw_smwadmin_return": "$1-(e)ra itzuli",
+ "smw-admin-announce": "Zure wikia iragarri",
+ "smw-admin-deprecation-notice-title-notice": "Datozten aldaketak",
+ "smw-admin-deprecation-notice-title-replacement": "Ordeztutako edo berriro izendatutako aukerak",
+ "smw-admin-deprecation-notice-title-removal": "Kendutako ezarpenak",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Aukera hauek ezabatuak izan ziren aurreko bertsioren batean, baina wiki honetan oraindik erabiltzen dira detektatu da.",
+ "smw-smwadmin-refresh-title": "Datuen konponketa eta eguneraketa",
+ "smw_smwadmin_datarefreshdocu": "Semantic MediaWiki-ko datuak berreskuratzeko aukera dago wikiaren oraingo edukian oinarrituta.\nHau erabilgarria izan daiteke arazoak dituzten datuak konpontzeko edo datuak freskatzeko, softwarearen eguneraketaren baten ondorioz barneko formatua aldatu bada.\nEguneraketa orriz orri exekutatzen da eta ez da berealakoan burutuko.\nOndokoak, eguneraketaren bat abian dagoen erakusten du eta eguneraketak abiaraztea eta gelditzea ahalbidetzen dizu (administratzaileak ezaugarri hau desgaitu ez badu behintzat).",
+ "smw_smwadmin_datarefreshbutton": "Datuak eguneratzen hasi",
+ "smw_smwadmin_datarefreshstop": "Igoera hau eten",
+ "smw_smwadmin_datarefreshstopconfirm": "Bai, {{GENDER:$1|ziur}} nago.",
+ "smw-admin-outdateddisposal-title": "Zaharkitutako entitateen ezabaketa",
+ "smw-admin-outdateddisposal-active": "Zaharkitutako entitateen ezabaketa lana programatua izan da.",
+ "smw-admin-outdateddisposal-button": "Ezabatze bat programatu",
+ "smw-admin-propertystatistics-title": "Propietatearen estatistikak berreraiki",
+ "smw-admin-propertystatistics-active": "Propietatearen estatistiken berreraiketa lan bat programatua izan da.",
+ "smw-admin-propertystatistics-button": "Estatistika berreraiketa programatu",
+ "smw-admin-support": "Laguntza eskatu",
+ "smw-admin-supportdocu": "Baliabide batzuk eskaintzen dira arazoak izatekotan zuri laguntzeko:",
+ "smw-admin-other-functions": "Beste funtzio batzuk",
+ "smw-admin-supplementary-section-title": "Funtzio osagarriak",
+ "smw-admin-supplementary-section-subtitle": "Erabilgarri dauden funtzioak",
+ "smw-admin-supplementary-section-intro": "Zerrendatutako funtzio batzuk mugatuak egon daitezke sekzio honetan eta beraz, ez daude eskuragarri wiki honetan.",
+ "smw-admin-supplementary-settings-title": "Konfigurazio ezarpenak",
+ "smw-admin-supplementary-operational-statistics-title": "Estatistika operatiboak",
+ "smw-admin-supplementary-idlookup-title": "Entitate bilaketa eta ezabaketa",
+ "smw-admin-supplementary-duplookup-title": "Entitateak bikoiztu",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Cachearen estatistikak",
+ "smw-admin-supplementary-elastic-title": "Bilaketa elastikoa",
+ "smw-admin-supplementary-elastic-functions": "Erabilgarri dauden funtzioak",
+ "smw-admin-supplementary-elastic-settings-title": "Ezarpenak",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodoak",
+ "smw-admin-supplementary-elastic-indices-title": "Indizeak",
+ "smw-admin-supplementary-elastic-statistics-title": "Estatistikak",
+ "smw-property-label-similarity-type": "ID mota erakutsi",
+ "smw-property-label-similarity-noresult": "Ez da emaitzarik aurkitu hautatutako aukerarentzat.",
+ "smw_adminlinks_datastructure": "Datuen egitura",
+ "smw_adminlinks_displayingdata": "Datu erakusketa",
+ "smw-paramdesc-category-delim": "Mugatzailea",
+ "smw-paramdesc-category-template": "Elementuei formatua emateko txantiloi bat",
+ "smw-paramdesc-category-userparam": "Txantiloira pasatzeko parametro bat",
+ "smw-info-par-message": "Erakusteko mezua.",
+ "prefs-smw": "MediaWiki semantikoa",
+ "prefs-general-options": "Aukera orokorrak",
+ "prefs-ask-options": "Special:Ask aukerak",
+ "smw-prefs-general-options-disable-editpage-info": "Ezgaitu orrialde informazioa",
+ "smw-ui-tooltip-title-property": "Propietatea",
+ "smw-ui-tooltip-title-quantity": "Unitate bihurketa",
+ "smw-ui-tooltip-title-info": "Informazioa",
+ "smw-ui-tooltip-title-service": "Zerbitzu linkak",
+ "smw-ui-tooltip-title-warning": "Abisua",
+ "smw-ui-tooltip-title-error": "Errorea",
+ "smw-ui-tooltip-title-parameter": "Parametroa",
+ "smw-ui-tooltip-title-event": "Gertaera",
+ "smw-ui-tooltip-title-note": "Oharra",
+ "smw-ui-tooltip-title-legend": "Azalpena",
+ "smw-ui-tooltip-title-reference": "Erreferentzia",
+ "smw_unknowntype": "Propietate honen mota ez du balio",
+ "smw_concept_header": "\"$1\" kontzeptuko orriak",
+ "restriction-level-smw-pageedit": "babestua (hautagaiak diren erabiltzaileak soilik)",
+ "group-smwadministrator": "Administratzaileak (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administratzaile (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Administratzaileak (Semantic MediaWiki)",
+ "group-smwcurator": "Komisarioak (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|komisario (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Komisarioak (Semantic MediaWiki)",
+ "smw-property-predefined-default": "\"$1\" lehenestutako propietatea da.",
+ "smw-sp-properties-header-label": "Propietate zerrenda",
+ "smw-sp-admin-settings-button": "Ezarpen zerrenda sortu",
+ "smw-admin-idlookup-docu": "Atal honetan, banako entitate bati buruzko xehetasun teknikoak (wikipagea, azpigaiak, propietatea eta abar) erakusten ditu Semantic MediaWiki-n. Sarrerak aukera ematen du IDa edo kate bat hautatzeko eremuarekin bat etortzeko. Kontutan izan ID erreferentzia bat ez dela MediaWiki orri edo berrikuspen ID batekin nahastu behar.",
+ "smw-admin-iddispose-title": "ID eskuratzea",
+ "smw-admin-idlookup-input": "Bilatu:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Ikuspegi orokorra",
+ "smw-admin-tab-supplement": "Funtzio osagarriak",
+ "smw-admin-tab-registry": "Erregistroa",
+ "smw-livepreview-loading": "Kargatzen…",
+ "smw-sp-searchbyproperty-resultlist-header": "Emaitzen zerrenda",
+ "smw-search-input": "Sarrera eta bilaketa",
+ "smw-search-syntax": "Sintaxia",
+ "smw-search-profile": "Hedatua",
+ "smw-search-profile-tooltip": "Bilatu funtzioak Semantic MediaWiki-rekin konexioan",
+ "log-name-smw": "MediaWiki sarrera semantikoa",
+ "smw-datavalue-invalid-number": "Ezin da \"$1\" zenbaki bezala interpretatu.",
+ "smw-types-list": "Datu-moten zerrenda",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-limitreport-intext-parsertime-value": "{{PLURAL:$1|segundo $1|$1 segundo}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundu bat|$1 segundu}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "{{PLURAL:$1|segundo $1|$1 segundo}}",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" baliogabeko URLa da.",
+ "smw-datavalue-parse-error": "Emandako \"$1\" balioa ez da ulertu.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-data-lookup-with-wait": "Eskaera abian dago eta apur bat luza daiteke.",
+ "smw-no-data-available": "Data ez dago eskuragarri.",
+ "smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation Change propagation] eguneratzearen zain ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|job|jobs}}] espero da) eta gomendagarria da aldaketei itxarotea prozesu osoa amaitu arte, modu honetan geldialdiak saihestuko dira edo nahi ez diren emaitzak.",
+ "smw-query-reference-link-label": "Kontsulta erreferentzia",
+ "smw-format-datatable-emptytable": "Ez dago daturik eskuragarri taulan",
+ "smw-format-datatable-info": "_START_ to _END_ of _TOTAL_ sarrerak erakusten",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "_MENU_ sarrerak erakutsi",
+ "smw-format-datatable-loadingrecords": "Kargatzen...",
+ "smw-format-datatable-processing": "Prozesatzen...",
+ "smw-format-datatable-search": "Bilatu:",
+ "smw-format-datatable-first": "Lehena",
+ "smw-format-datatable-last": "Azkena",
+ "smw-format-datatable-next": "Hurrengoa",
+ "smw-format-datatable-previous": "Aurrekoa",
+ "smw-format-datatable-toolbar-export": "Esportatu",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-property-reserved-category": "Kategoria",
+ "smw-category": "Kategoria",
+ "smw-browse-property-group-title": "Propietate taldea",
+ "smw-browse-property-group-label": "Talde propietate etiketa",
+ "smw-browse-property-group-description": "Propietate talde deskripzioa",
+ "smw-section-expand": "Zabaldu atala",
+ "smw-section-collapse": "Atala kolapsatu",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] formatua",
+ "smw-help": "Laguntza",
+ "smw-processing": "Prozesatzen..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ext.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ext.json
new file mode 100644
index 00000000..44035491
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ext.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Cargandu…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.alias.php b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.alias.php
new file mode 100644
index 00000000..7e51648f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.alias.php
@@ -0,0 +1,836 @@
+<?php
+/**
+ * Aliases for special pages
+ *
+ * @file
+ * @ingroup Extensions
+ * @ingroup SMWLanguage
+ */
+// @codingStandardsIgnoreFile
+
+$specialPageAliases = [];
+
+/** English (English) */
+$specialPageAliases['en'] = [
+ 'Ask' => [ 'Ask' ],
+ 'Browse' => [ 'Browse' ],
+ 'ExportRDF' => [ 'ExportRDF' ],
+ 'PageProperty' => [ 'PageProperty' ],
+ 'Properties' => [ 'Properties' ],
+ 'Concepts' => [ 'Concepts' ],
+ 'SMWAdmin' => [ 'SemanticMediaWiki', 'SMWAdmin' ],
+ 'SearchByProperty' => [ 'SearchByProperty' ],
+ 'ProcessingErrorList' => [ 'ProcessingErrorList' ],
+ 'PropertyLabelSimilarity' => [ 'PropertyLabelSimilarity' ],
+ 'Types' => [ 'Types' ],
+ 'URIResolver' => [ 'URIResolver' ],
+ 'UnusedProperties' => [ 'UnusedProperties' ],
+ 'WantedProperties' => [ 'WantedProperties' ],
+ 'DeferredRequestDispatcher' => [ 'DeferredRequestDispatcher' ],
+];
+
+/** Afrikaans (Afrikaans) */
+$specialPageAliases['af'] = [
+ 'Ask' => [ 'Vra' ],
+ 'Properties' => [ 'Eienskappe' ],
+ 'Concepts' => [ 'Konsepte' ],
+ 'Types' => [ 'Tipes' ],
+];
+
+/** Arabic (العربية) */
+$specialPageAliases['ar'] = [
+ 'Ask' => [ 'سؤال' ],
+ 'Browse' => [ 'تصÙØ­' ],
+ 'ExportRDF' => [ 'تصدير_RDF' ],
+ 'PageProperty' => [ 'صÙحة_خصيصة' ],
+ 'Properties' => [ 'خصائص' ],
+ 'Concepts' => [ 'Ù…Ùاهيم' ],
+ 'SMWAdmin' => [ 'إدارة_سمو', 'إدارة_ميدياويكي_دلالية' ],
+ 'SearchByProperty' => [ 'بحث_بالخصيصة' ],
+ 'Types' => [ 'أنواع' ],
+ 'URIResolver' => [ 'حال_URI' ],
+ 'UnusedProperties' => [ 'خصائص_غير_مستخدمة' ],
+ 'WantedProperties' => [ 'خصائص_مطلوبة' ],
+];
+
+/** Aramaic (ÜܪܡÜÜ) */
+$specialPageAliases['arc'] = [
+ 'Ask' => [ 'Ü«ÜÜ ' ],
+ 'Browse' => [ 'ܦÜܬ' ],
+ 'PageProperty' => [ 'ܦÜܬÜ_Ü•Ü•ÜÜ Üܘ̈ܬÜ' ],
+ 'Properties' => [ 'Ü•ÜÜ Üܘܬ̈Ü' ],
+ 'SearchByProperty' => [ 'ܒܨÜ_Ü’ÜÜ•_Ü•ÜÜ Üܘ̈ܬÜ' ],
+ 'Types' => [ 'Üܕ̈ܫÜ' ],
+ 'UnusedProperties' => [ 'Ü•ÜÜ Üܘ̈ܬÜ_Ü Ü_ܦܠÜܚ̈ܬÜ' ],
+ 'WantedProperties' => [ 'Ü•ÜÜ Üܘ̈ܬÜ_ܣܢÜܩ̈ܬÜ' ],
+];
+
+/** Egyptian Arabic (مصرى) */
+$specialPageAliases['arz'] = [
+ 'Ask' => [ 'سؤال' ],
+ 'Browse' => [ 'براوز' ],
+ 'ExportRDF' => [ 'تصدير_RDF' ],
+ 'PageProperty' => [ 'خاصية_الصÙحه' ],
+ 'Properties' => [ 'خصايص' ],
+ 'Concepts' => [ 'مبادئ' ],
+ 'SMWAdmin' => [ 'ادارى_SMW' ],
+ 'SearchByProperty' => [ 'دوّر_بالخاصيه' ],
+ 'Types' => [ 'انواع' ],
+ 'URIResolver' => [ 'محلل_يو_ار_اى' ],
+ 'UnusedProperties' => [ 'خصايص_مش_مستعمله' ],
+ 'WantedProperties' => [ 'خصايص_مطلوبه' ],
+];
+
+/** Assamese (অসমীয়া) */
+$specialPageAliases['as'] = [
+ 'Ask' => [ 'সোধক' ],
+ 'Browse' => [ 'বà§à§°à¦¾à¦‰à¦œ' ],
+ 'ExportRDF' => [ 'RDF_ৰপà§à¦¤à¦¾à¦¨à¦¿' ],
+ 'Types' => [ 'পà§à§°à¦•à¦¾à§°à¦¸à¦®à§‚হ' ],
+];
+
+/** Western Balochi (بلوچی رخشانی) */
+$specialPageAliases['bgn'] = [
+ 'Ask' => [ 'سوال_و_سوج_ئان' ],
+ 'Browse' => [ 'بروز' ],
+ 'ExportRDF' => [ 'آر_ÚˆÛŒ_اÙ_ئی_ÚˆÙ†_کورتین' ],
+ 'PageProperty' => [ 'تاکدیمی_مالومات' ],
+ 'Properties' => [ 'مالومات' ],
+ 'Concepts' => [ 'موّزو_ئان' ],
+ 'SMWAdmin' => [ 'اس_ام_ڈبلیو_ئی_مدیر' ],
+ 'SearchByProperty' => [ 'گشتین_به_مالوماتی_اساس_ئا' ],
+ 'Types' => [ 'ڈوّل_ئان' ],
+ 'URIResolver' => [ 'یو_آر_آی_هل_کنۆک' ],
+ 'UnusedProperties' => [ 'ایستیپاده_نه_بوته_ئین_مالومات' ],
+ 'WantedProperties' => [ 'لوٹی_ته_بوته_ئین_مالومات' ],
+];
+
+/** Banjar (Bahasa Banjar) */
+$specialPageAliases['bjn'] = [
+ 'Ask' => [ 'Takun' ],
+ 'Browse' => [ 'Ambahi' ],
+ 'Types' => [ 'Janis' ],
+];
+
+/** Breton (brezhoneg) */
+$specialPageAliases['br'] = [
+ 'Ask' => [ 'Goulenn' ],
+ 'Browse' => [ 'Furchal' ],
+ 'ExportRDF' => [ 'EzorzhiañRDF' ],
+ 'PageProperty' => [ 'PerzhioùPajenn' ],
+ 'Properties' => [ 'Perzhioù' ],
+ 'Types' => [ 'Seurtoù' ],
+];
+
+/** Bosnian (bosanski) */
+$specialPageAliases['bs'] = [
+ 'Ask' => [ 'Upit' ],
+ 'Browse' => [ 'Pregledanje' ],
+ 'ExportRDF' => [ 'IzvozRDF' ],
+ 'Properties' => [ 'Svojstva' ],
+];
+
+/** Min Dong Chinese (Mìng-dĕ̤ng-ngṳ̄) */
+$specialPageAliases['cdo'] = [
+ 'Ask' => [ 'å•' ],
+ 'Browse' => [ 'ç€è¦½' ],
+ 'ExportRDF' => [ '導出RDF' ],
+ 'PageProperty' => [ 'é é¢å±¬æ€§' ],
+];
+
+/** German (Deutsch) */
+$specialPageAliases['de'] = [
+ 'Ask' => [ 'Semantische_Suche' ],
+ 'Browse' => [ 'Durchsuchen', 'Browsen' ],
+ 'ExportRDF' => [ 'RDF_exportieren', 'Exportiere_RDF' ],
+ 'PageProperty' => [ 'Seitenattribut' ],
+ 'Properties' => [ 'Attribute' ],
+ 'Concepts' => [ 'Konzepte' ],
+ 'SMWAdmin' => [ 'SemanticMediaWiki', 'SMW-Administration', 'SMW-Einrichtung' ],
+ 'SearchByProperty' => [ 'Suche_mittels_Attribut' ],
+ 'ProcessingErrorList' => [ 'Liste_der_Verarbeitungsfehler' ],
+ 'PropertyLabelSimilarity' => [ 'Ähnliche_Attributbezeichnungen' ],
+ 'Types' => [ 'Datentypen' ],
+ 'URIResolver' => [ 'URI-Auflöser' ],
+ 'UnusedProperties' => [ 'Verwaiste_Attribute' ],
+ 'WantedProperties' => [ 'Gewünschte_Attribute' ],
+];
+
+/** Zazaki (Zazaki) */
+$specialPageAliases['diq'] = [
+ 'Ask' => [ 'Pers' ],
+ 'Browse' => [ 'Bıgeyré' ],
+ 'ExportRDF' => [ 'RDFTeberde' ],
+ 'PageProperty' => [ 'BeÄŸsaPeran' ],
+ 'Properties' => [ 'Bexsiyeti' ],
+ 'Concepts' => [ 'Konsepti' ],
+ 'SMWAdmin' => [ 'SMWXızmetkar' ],
+ 'SearchByProperty' => [ 'ĞısusiyetkarCıgeyrayış' ],
+ 'Types' => [ 'Babeti' ],
+ 'URIResolver' => [ 'URIAgozne' ],
+ 'UnusedProperties' => [ 'ĞısusiyetéNékaréné' ],
+ 'WantedProperties' => [ 'ĞısusiyetéWaşténé' ],
+];
+
+/** Lower Sorbian (dolnoserbski) */
+$specialPageAliases['dsb'] = [
+ 'Ask' => [ 'Semantiske pytanhje' ],
+ 'Browse' => [ 'Pśepytaś' ],
+ 'ExportRDF' => [ 'RDF eksportěrowaś' ],
+ 'PageProperty' => [ 'Bokowa kakosć' ],
+ 'Properties' => [ 'Kakosći' ],
+ 'SearchByProperty' => [ 'Z kakosću pytaś' ],
+ 'Types' => [ 'Datowe typy' ],
+ 'UnusedProperties' => [ 'Njewužywane kakosći' ],
+ 'WantedProperties' => [ 'Póžedane kakosći' ],
+];
+
+/** Divehi (Þ‹Þ¨ÞˆÞ¬Þ€Þ¨Þ„Þ¦ÞÞ°) */
+$specialPageAliases['dv'] = [
+ 'Ask' => [ 'Þ‡Þ¦Þ‡Þ°ÞÞ¦ÞˆÞ§' ],
+];
+
+/** Greek (Ελληνικά) */
+$specialPageAliases['el'] = [
+ 'Ask' => [ 'ΕÏώτημα' ],
+ 'Browse' => [ 'ΠεÏιήγηση' ],
+ 'ExportRDF' => [ 'ΕξαγωγήRDF' ],
+ 'PageProperty' => [ 'ΙδιότηταΣελίδας' ],
+ 'Properties' => [ 'Ιδιότητες' ],
+ 'Concepts' => [ 'Έννοιες' ],
+ 'SMWAdmin' => [ 'ΔιαχείÏισηSMW' ],
+ 'SearchByProperty' => [ 'ΑναζήτησηΜεΙδιότητα' ],
+ 'Types' => [ 'ΤÏποι' ],
+ 'URIResolver' => [ 'ΑναλυτήςURI' ],
+ 'UnusedProperties' => [ 'ΜηΧÏησιμοποιοÏμενεςΙδιότητες' ],
+ 'WantedProperties' => [ 'ΖητοÏμενεςΙδιότητες' ],
+];
+
+/** Esperanto (Esperanto) */
+$specialPageAliases['eo'] = [
+ 'Ask' => [ 'Peti' ],
+ 'Browse' => [ 'Foliumi' ],
+ 'ExportRDF' => [ 'Elporti_RDF', 'Eksporti_RDF' ],
+ 'PageProperty' => [ 'Ecoj_de_paÄo' ],
+ 'Properties' => [ 'Ecoj' ],
+ 'Types' => [ 'Specoj' ],
+];
+
+/** Spanish (español) */
+$specialPageAliases['es'] = [
+ 'Properties' => [ 'Propiedades' ],
+ 'SearchByProperty' => [ 'BuscarPorPropiedad' ],
+ 'Types' => [ 'Tipos' ],
+ 'UnusedProperties' => [ 'PropiedadesNoUsadas' ],
+ 'WantedProperties' => [ 'PropiedadesRequeridas' ],
+];
+
+/** Persian (Ùارسی) */
+$specialPageAliases['fa'] = [
+ 'Ask' => [ 'پرسش' ],
+ 'Browse' => [ 'مرور' ],
+ 'ExportRDF' => [ 'برونریزی_آردی‌اÙ' ],
+ 'PageProperty' => [ 'جزئیات_صÙحه' ],
+ 'Properties' => [ 'جزئیات' ],
+ 'Concepts' => [ 'Ù…Ùاهیم' ],
+ 'SMWAdmin' => [ 'مدیر_اس‌ام‌دبلیو' ],
+ 'SearchByProperty' => [ 'جستجو_بر_پایهٔ_جزئیات' ],
+ 'Types' => [ 'نوع‌ها' ],
+ 'URIResolver' => [ 'حل‌کننده_یو‌آر‌آی' ],
+ 'UnusedProperties' => [ 'جزئیات_استÙاده_نشده' ],
+ 'WantedProperties' => [ 'جزئیات_درخواستی' ],
+];
+
+/** Finnish (suomi) */
+$specialPageAliases['fi'] = [
+ 'Ask' => [ 'Kysy' ],
+ 'Browse' => [ 'Selaa' ],
+ 'ExportRDF' => [ 'RDF-vienti' ],
+ 'Properties' => [ 'Ominaisuudet' ],
+ 'SMWAdmin' => [ 'Semanttisen_Mediawikin_ylläpito' ],
+ 'Types' => [ 'Tyypit' ],
+ 'UnusedProperties' => [ 'Käyttämättömät_ominaisuudet' ],
+ 'WantedProperties' => [ 'Halutut_ominaisuudet' ],
+];
+
+/** French (français) */
+$specialPageAliases['fr'] = [
+ 'Ask' => [ 'Requêter' ],
+ 'Browse' => [ 'Parcourir' ],
+ 'ExportRDF' => [ 'Export_RDF' ],
+ 'PageProperty' => [ 'Propriétés_de_la_page' ],
+ 'Properties' => [ 'Propriétés' ],
+ 'Concepts' => [ 'Concepts' ],
+ 'SearchByProperty' => [ 'Recherche_par_propriété' ],
+ 'Types' => [ 'Types' ],
+ 'UnusedProperties' => [ 'Propriétés_inutilisées' ],
+ 'WantedProperties' => [ 'Propriétés_demandées' ],
+];
+
+/** Galician (galego) */
+$specialPageAliases['gl'] = [
+ 'Ask' => [ 'Preguntar' ],
+ 'Browse' => [ 'Navegar' ],
+ 'ExportRDF' => [ 'Exportar_RDF' ],
+ 'PageProperty' => [ 'Propiedades_da_páxina' ],
+ 'Properties' => [ 'Propiedades' ],
+ 'Concepts' => [ 'Conceptos' ],
+ 'SearchByProperty' => [ 'Procurar_por_propiedades' ],
+ 'Types' => [ 'Tipos' ],
+ 'UnusedProperties' => [ 'Propiedades_sen_uso' ],
+ 'WantedProperties' => [ 'Propiedades_requiridas' ],
+];
+
+/** Swiss German (Alemannisch) */
+$specialPageAliases['gsw'] = [
+ 'Ask' => [ 'Semantischi_Suech' ],
+ 'Browse' => [ 'Duresueche' ],
+ 'ExportRDF' => [ 'Exportier_ADF' ],
+ 'PageProperty' => [ 'Syteattribut' ],
+ 'Properties' => [ 'Attribut' ],
+ 'SMWAdmin' => [ 'SMW-Yyrichtig' ],
+ 'SearchByProperty' => [ 'Suech_no_Attribut' ],
+ 'Types' => [ 'Datetype' ],
+ 'URIResolver' => [ 'URI-Ufflöser' ],
+ 'UnusedProperties' => [ 'Verwaisti_Attribut' ],
+ 'WantedProperties' => [ 'Gwinschti_Attribut' ],
+];
+
+/** Hebrew (עברית) */
+$specialPageAliases['he'] = [
+ 'Ask' => [ 'ש×לה' ],
+ 'Browse' => [ 'עיון' ],
+ 'ExportRDF' => [ 'ייצור_RDF' ],
+ 'PageProperty' => [ 'מ×פיין_דף' ],
+ 'Properties' => [ 'מ×פייני×' ],
+ 'Concepts' => [ 'רעיונות' ],
+ 'SMWAdmin' => [ 'ניהול_SMW' ],
+ 'SearchByProperty' => [ 'חיפוש_לפי_ש×ילתה' ],
+ 'Types' => [ 'סוגי×' ],
+ 'URIResolver' => [ 'פותר_URI' ],
+ 'UnusedProperties' => [ 'מ×פייני×_ש××™× ×_בשימוש' ],
+ 'WantedProperties' => [ 'מ×פייני×_מבוקשי×' ],
+];
+
+/** Hindi (हिनà¥à¤¦à¥€) */
+$specialPageAliases['hi'] = [
+ 'Ask' => [ 'पूछो' ],
+];
+
+/** Upper Sorbian (hornjoserbsce) */
+$specialPageAliases['hsb'] = [
+ 'Ask' => [ 'Semantiske_pytanje' ],
+ 'Browse' => [ 'Přepytać' ],
+ 'ExportRDF' => [ 'RDF_eksportować' ],
+ 'PageProperty' => [ 'Kajkosć_strony' ],
+ 'Properties' => [ 'Kajkosće' ],
+ 'Concepts' => [ 'Koncepty' ],
+ 'SMWAdmin' => [ 'SMW-administracija' ],
+ 'SearchByProperty' => [ 'Pytanje_po_kajkosći' ],
+ 'Types' => [ 'Datowe_typy' ],
+ 'UnusedProperties' => [ 'Njewužiwane_kajkosće' ],
+ 'WantedProperties' => [ 'Falowace_kajkosće' ],
+];
+
+/** Haitian (Kreyòl ayisyen) */
+$specialPageAliases['ht'] = [
+ 'Ask' => [ 'Mande' ],
+ 'Browse' => [ 'Navige' ],
+ 'ExportRDF' => [ 'EkspòteRDF' ],
+ 'PageProperty' => [ 'ProprietePaj' ],
+ 'Properties' => [ 'Propriete' ],
+ 'SMWAdmin' => [ 'AdminSMW' ],
+ 'SearchByProperty' => [ 'ChachePaPropriete' ],
+ 'Types' => [ 'Tip' ],
+ 'UnusedProperties' => [ 'ProprietePaSèvi' ],
+ 'WantedProperties' => [ 'ProprieteKiMande' ],
+];
+
+/** Hungarian (magyar) */
+$specialPageAliases['hu'] = [
+ 'Ask' => [ 'Kérdez' ],
+ 'Browse' => [ 'Böngészés' ],
+ 'Properties' => [ 'Tulajdonságok' ],
+ 'Types' => [ 'Típusok' ],
+ 'URIResolver' => [ 'URI-feloldó' ],
+ 'UnusedProperties' => [ 'Nem_használt_tulajdonságok' ],
+ 'WantedProperties' => [ 'Keresett_tulajdonságok' ],
+];
+
+/** Interlingua (interlingua) */
+$specialPageAliases['ia'] = [
+ 'Ask' => [ 'Consultar' ],
+ 'Browse' => [ 'Percurrer' ],
+ 'ExportRDF' => [ 'Exportar_RDF' ],
+ 'PageProperty' => [ 'Proprietate_de_pagina' ],
+ 'Properties' => [ 'Proprietates' ],
+ 'SMWAdmin' => [ 'Admin_SMW' ],
+ 'SearchByProperty' => [ 'Cercar_per_proprietate' ],
+ 'Types' => [ 'Typos' ],
+ 'URIResolver' => [ 'Resolvitor_de_URIs' ],
+ 'UnusedProperties' => [ 'Proprietates_non_usate' ],
+ 'WantedProperties' => [ 'Proprietates_dsesirate' ],
+];
+
+/** Indonesian (Bahasa Indonesia) */
+$specialPageAliases['id'] = [
+ 'Ask' => [ 'Tanya' ],
+ 'Browse' => [ 'Jelajahi' ],
+ 'ExportRDF' => [ 'EksporRDF' ],
+ 'PageProperty' => [ 'PropertiHalaman' ],
+ 'Properties' => [ 'Properti' ],
+ 'SMWAdmin' => [ 'AdminSMW' ],
+ 'SearchByProperty' => [ 'PencarianProperti' ],
+ 'Types' => [ 'Tipe' ],
+ 'URIResolver' => [ 'PenguraiURI' ],
+ 'UnusedProperties' => [ 'PropertiTakDigunakan' ],
+ 'WantedProperties' => [ 'PropertiDiinginkan' ],
+];
+
+/** Italian (italiano) */
+$specialPageAliases['it'] = [
+ 'Ask' => [ 'Chiedi' ],
+ 'Browse' => [ 'Esplora' ],
+ 'ExportRDF' => [ 'EsportaRDF' ],
+ 'PageProperty' => [ 'ProprietàPagina' ],
+ 'Properties' => [ 'Proprietà' ],
+ 'Concepts' => [ 'Concetti' ],
+ 'SMWAdmin' => [ 'AdminSMW' ],
+ 'SearchByProperty' => [ 'CercaPerProprietà' ],
+ 'Types' => [ 'Tipi' ],
+ 'URIResolver' => [ 'RisolutoreURI' ],
+ 'UnusedProperties' => [ 'ProprietàNonUtilizzate' ],
+ 'WantedProperties' => [ 'ProprietàRichieste' ],
+];
+
+/** Japanese (日本語) */
+$specialPageAliases['ja'] = [
+ 'Ask' => [ 'å•ã„åˆã‚ã›', 'æ„味的検索' ],
+ 'Browse' => [ '閲覧' ],
+ 'ExportRDF' => [ 'RDF書ã出ã—', 'RDFエクスãƒãƒ¼ãƒˆ', 'RDFエクスãƒãƒ¼ãƒˆ' ],
+ 'PageProperty' => [ 'ページプロパティ' ],
+ 'Properties' => [ 'プロパティ一覧' ],
+ 'Concepts' => [ '概念' ],
+ 'SMWAdmin' => [ 'SMW管ç†', 'SMW管ç†' ],
+ 'SearchByProperty' => [ 'プロパティã«ã‚ˆã‚‹æ¤œç´¢' ],
+ 'Types' => [ '型一覧' ],
+ 'URIResolver' => [ 'URIリゾルãƒãƒ¼', 'URIリゾルãƒãƒ¼' ],
+ 'UnusedProperties' => [ '使ã‚ã‚Œã¦ã„ãªã„プロパティ' ],
+ 'WantedProperties' => [ '望ã¾ã‚Œã¦ã„るプロパティ' ],
+];
+
+/** Georgian (ქáƒáƒ áƒ—ული) */
+$specialPageAliases['ka'] = [
+ 'Types' => [ 'ტიპები' ],
+];
+
+/** Khmer (ភាសាážáŸ’មែរ) */
+$specialPageAliases['km'] = [
+ 'Browse' => [ 'រាវរក' ],
+ 'Properties' => [ 'លក្ážážŽáŸˆ' ],
+ 'Types' => [ 'ប្រភáŸáž‘' ],
+];
+
+/** Korean (한국어) */
+$specialPageAliases['ko'] = [
+ 'Ask' => [ '묻기' ],
+ 'Browse' => [ '찾아보기' ],
+ 'ExportRDF' => [ 'RDF내보내기' ],
+ 'PageProperty' => [ '문서ì†ì„±' ],
+ 'Properties' => [ 'ì†ì„±ëª©ë¡' ],
+ 'Concepts' => [ 'ê°œë…목ë¡' ],
+ 'SMWAdmin' => [ 'SMW관리ìž' ],
+ 'SearchByProperty' => [ 'ì†ì„±ë³„검색', 'ì†ì„±ë³„찾기' ],
+ 'Types' => [ '종류목ë¡' ],
+ 'URIResolver' => [ 'URIí•´ê²°' ],
+ 'UnusedProperties' => [ '안쓰는ì†ì„±', 'ì“°ì´ì§€ì•ŠëŠ”ì†ì„±' ],
+ 'WantedProperties' => [ '필요한ì†ì„±' ],
+];
+
+/** Colognian (Ripoarisch) */
+$specialPageAliases['ksh'] = [
+ 'Ask' => [ 'Froore' ],
+ 'Browse' => [ 'Bläddere' ],
+ 'ExportRDF' => [ 'RDF', 'RDF Äxpotteere' ],
+ 'PageProperty' => [ 'Eijeschaffte vun Sigge' ],
+ 'Properties' => [ 'Eijeschaffte' ],
+ 'SMWAdmin' => [ 'Semantesch MediaWiki Ennreschte' ],
+ 'SearchByProperty' => [ 'Noh Eijeschaffte söke' ],
+ 'Types' => [ 'Zoote vun Daate' ],
+ 'UnusedProperties' => [ 'Eijeschaffte di nit jebruch wääde' ],
+ 'WantedProperties' => [ 'Eijeschaffte di noch jebruch wääde' ],
+];
+
+/** Cornish (kernowek) */
+$specialPageAliases['kw'] = [
+ 'Ask' => [ 'Govyn' ],
+ 'Browse' => [ 'Peuri' ],
+ 'ExportRDF' => [ 'EsperthiRDF' ],
+ 'PageProperty' => [ 'GnasFolen' ],
+ 'Properties' => [ 'Gnasow' ],
+ 'SearchByProperty' => [ 'HwilasHerwydhGnas' ],
+];
+
+/** Luxembourgish (Lëtzebuergesch) */
+$specialPageAliases['lb'] = [
+ 'Ask' => [ 'Froen' ],
+ 'Browse' => [ 'Browsen' ],
+ 'ExportRDF' => [ 'RDF_exportéieren' ],
+ 'PageProperty' => [ 'Säiten-Eegeschaften' ],
+ 'Properties' => [ 'Eegeschaften' ],
+ 'Concepts' => [ 'Konzepter' ],
+ 'SMWAdmin' => [ 'SMW-Administratioun' ],
+ 'SearchByProperty' => [ 'No_Eegeschaft_sichen' ],
+ 'Types' => [ 'Datentypen' ],
+ 'UnusedProperties' => [ 'Netbenotzt_Eegeschaften' ],
+ 'WantedProperties' => [ 'Gewënscht_Eegeschaften' ],
+];
+
+/** Lombard (lumbaart) */
+$specialPageAliases['lmo'] = [
+ 'Ask' => [ 'Ciama' ],
+];
+
+/** Macedonian (македонÑки) */
+$specialPageAliases['mk'] = [
+ 'Ask' => [ 'Прашај' ],
+ 'Browse' => [ 'ПрелиÑтај' ],
+ 'ExportRDF' => [ 'ИзвезиRDF' ],
+ 'PageProperty' => [ 'СвојÑтваÐаСтраница' ],
+ 'Properties' => [ 'СвојÑтва' ],
+ 'Concepts' => [ 'Концепти' ],
+ 'SMWAdmin' => [ 'СМВÐдминиÑтратор' ],
+ 'SearchByProperty' => [ 'ПребарајПоСвојÑтво' ],
+ 'Types' => [ 'Типови' ],
+ 'URIResolver' => [ 'URIРешавач' ],
+ 'UnusedProperties' => [ 'ÐекориÑтениСвојÑтва' ],
+ 'WantedProperties' => [ 'ПотребниСвојÑтва' ],
+];
+
+/** Malayalam (മലയാളം) */
+$specialPageAliases['ml'] = [
+ 'Ask' => [ 'ചോദികàµà´•àµà´•' ],
+ 'Browse' => [ 'à´¬àµà´°àµ—à´¸àµ' ],
+ 'Types' => [ 'തരങàµà´™àµ¾' ],
+];
+
+/** Marathi (मराठी) */
+$specialPageAliases['mr'] = [
+ 'Ask' => [ 'विचारा' ],
+ 'Browse' => [ 'नà¥à¤¯à¤¾à¤¹à¤¾à¤³à¤¾' ],
+ 'ExportRDF' => [ 'आरडीà¤à¤«à¤¨à¤¿à¤°à¥à¤¯à¤¾à¤¤' ],
+ 'PageProperty' => [ 'पानवैशिषà¥à¤Ÿà¥à¤¯à¥‡' ],
+ 'Properties' => [ 'वैशिषà¥à¤Ÿà¥à¤¯à¥‡' ],
+ 'Concepts' => [ 'संकलà¥à¤ªà¤¨à¤¾' ],
+ 'SMWAdmin' => [ 'à¤à¤¸à¤à¤®à¤¡à¤¬à¥à¤²à¥à¤¯à¥‚पà¥à¤°à¤šà¤¾à¤²à¤•' ],
+ 'SearchByProperty' => [ 'वैशिषà¥à¤Ÿà¥à¤¯à¥‡à¤¨à¥à¤¸à¤¾à¤°à¤¶à¥‹à¤§' ],
+ 'Types' => [ 'पà¥à¤°à¤•à¤¾à¤°' ],
+ 'URIResolver' => [ 'यूआरायरिà¤à¥‰à¤²à¥à¤µà¥à¤¹à¤°' ],
+ 'UnusedProperties' => [ 'नवापरलेलीवैशिषà¥à¤Ÿà¥à¤¯à¥‡' ],
+ 'WantedProperties' => [ 'हवीअसलेलीवैशिषà¥à¤Ÿà¥à¤¯à¥‡' ],
+];
+
+/** Maltese (Malti) */
+$specialPageAliases['mt'] = [
+ 'Ask' => [ 'Staqsi' ],
+ 'Browse' => [ 'Esplora' ],
+];
+
+/** Norwegian Bokmål (norsk bokmål) */
+$specialPageAliases['nb'] = [
+ 'Ask' => [ 'Spør' ],
+ 'Browse' => [ 'Se_gjennom' ],
+ 'ExportRDF' => [ 'Eksporter_RDF' ],
+ 'PageProperty' => [ 'Sideegenskaper' ],
+ 'Properties' => [ 'Egenskaper' ],
+ 'Concepts' => [ 'Konsepter' ],
+ 'SMWAdmin' => [ 'SMW-administrasjon' ],
+ 'SearchByProperty' => [ 'Søk_etter_egenskap' ],
+ 'Types' => [ 'Typer' ],
+ 'URIResolver' => [ 'URI-løser' ],
+ 'UnusedProperties' => [ 'Ubrukte_egenskaper' ],
+ 'WantedProperties' => [ 'Ønskede_egenskaper' ],
+];
+
+/** Low Saxon (Netherlands) (Nedersaksies) */
+$specialPageAliases['nds-nl'] = [
+ 'Ask' => [ 'Vragen' ],
+ 'Browse' => [ 'Bekieken' ],
+ 'ExportRDF' => [ 'RDF_uutvoeren' ],
+ 'PageProperty' => [ 'Ziedeigenschap' ],
+ 'Properties' => [ 'Eigenschappen' ],
+ 'Concepts' => [ 'Konsepten' ],
+ 'SMWAdmin' => [ 'SMW-beheer' ],
+ 'SearchByProperty' => [ 'Op_eigenschap_zeuken' ],
+ 'Types' => [ 'Soorten' ],
+ 'URIResolver' => [ 'URI-oplosser' ],
+ 'UnusedProperties' => [ 'Ongebruukten_eigenschappen' ],
+ 'WantedProperties' => [ 'Gewunste_eigenschappen' ],
+];
+
+/** Dutch (Nederlands) */
+$specialPageAliases['nl'] = [
+ 'Ask' => [ 'Vragen' ],
+ 'Browse' => [ 'Bekijken' ],
+ 'ExportRDF' => [ 'RDFExporteren' ],
+ 'PageProperty' => [ 'Paginaeigenschap' ],
+ 'Properties' => [ 'Eigenschappen' ],
+ 'Concepts' => [ 'Concepten' ],
+ 'SMWAdmin' => [ 'SMWBeheer' ],
+ 'SearchByProperty' => [ 'OpEigenschapZoeken' ],
+ 'Types' => [ 'Typen' ],
+ 'UnusedProperties' => [ 'OngebruikteEigenschappen' ],
+ 'WantedProperties' => [ 'GewensteEigenschappen' ],
+];
+
+/** Occitan (occitan) */
+$specialPageAliases['oc'] = [
+ 'Browse' => [ 'Percórrer' ],
+ 'Properties' => [ 'Proprietats' ],
+ 'Types' => [ 'Tipes' ],
+ 'UnusedProperties' => [ 'Proprietats inutilizadas', 'ProprietatsInutilizadas' ],
+ 'WantedProperties' => [ 'Proprietats demandadas', 'ProprietatsDemandadas' ],
+];
+
+/** Oriya (ଓଡ଼ିଆ) */
+$specialPageAliases['or'] = [
+ 'Ask' => [ 'ପଚାରନà­à¬¤à­' ],
+ 'Browse' => [ 'ଖୋଜିବା' ],
+ 'PageProperty' => [ 'ପୃଷà­à¬ à¬¾à¬°_ଗà­à¬£' ],
+ 'Properties' => [ 'ଗà­à¬£' ],
+ 'UnusedProperties' => [ 'ବà­à­Ÿà¬¬à¬¹à¬¾à¬°_ହୋଇନଥିବା_ଗà­à¬£' ],
+ 'WantedProperties' => [ 'ଦରକାରୀ_ଗà­à¬£' ],
+];
+
+/** Punjabi (ਪੰਜਾਬੀ) */
+$specialPageAliases['pa'] = [
+ 'Ask' => [ 'ਪà©à©±à¨›à©‹' ],
+ 'Browse' => [ 'ਫੇਰੀ_ਪਾਓ' ],
+ 'Types' => [ 'ਕਿਸਮਾਂ' ],
+];
+
+/** Polish (polski) */
+$specialPageAliases['pl'] = [
+ 'Ask' => [ 'Pytanie' ],
+ 'Browse' => [ 'PrzeglÄ…d' ],
+ 'ExportRDF' => [ 'EksportRDF' ],
+ 'PageProperty' => [ 'WłasnośćStrony' ],
+ 'Properties' => [ 'Własności' ],
+ 'Concepts' => [ 'Koncepty' ],
+ 'SMWAdmin' => [ 'AdminSMW' ],
+ 'SearchByProperty' => [ 'SzukanieWgWłasności' ],
+ 'Types' => [ 'Typy' ],
+ 'URIResolver' => [ 'ResolverURI' ],
+ 'UnusedProperties' => [ 'NieużywaneWłasności' ],
+ 'WantedProperties' => [ 'PotrzebneWłasności' ],
+];
+
+/** Pashto (پښتو) */
+$specialPageAliases['ps'] = [
+ 'Ask' => [ 'پوښتل' ],
+ 'Browse' => [ 'سپړل' ],
+ 'PageProperty' => [ 'د مخ ÚانتياوÛ' ],
+ 'Properties' => [ 'ÚانتياوÛ' ],
+ 'Types' => [ 'ډولونه' ],
+ 'UnusedProperties' => [ 'ناکارÛØ¯Ù„Û ÚانتياوÛ' ],
+];
+
+/** Portuguese (português) */
+$specialPageAliases['pt'] = [
+ 'Ask' => [ 'Consultar' ],
+ 'Browse' => [ 'Navegar' ],
+ 'ExportRDF' => [ 'ExportarRDF' ],
+ 'PageProperty' => [ 'Propriedade_de_página' ],
+ 'Properties' => [ 'Propriedades' ],
+ 'Concepts' => [ 'Conceitos' ],
+ 'SearchByProperty' => [ 'Pesquisa_por_propriedade' ],
+ 'Types' => [ 'Tipos' ],
+ 'UnusedProperties' => [ 'Propriedades_não_utilizadas' ],
+ 'WantedProperties' => [ 'Propriedades_desejadas' ],
+];
+
+/** Brazilian Portuguese (português do Brasil) */
+$specialPageAliases['pt-br'] = [
+ 'Ask' => [ 'Consultar' ],
+ 'Browse' => [ 'Navegar' ],
+ 'Concepts' => [ 'Conceitos' ],
+ 'ExportRDF' => [ 'ExportarRDF' ],
+ 'PageProperty' => [ 'Propriedade_de_página' ],
+ 'ProcessingErrorList' => [ 'Lista_de_erro_de_processamento' ],
+ 'Properties' => [ 'Propriedades' ],
+ 'PropertyLabelSimilarity' => [ 'Similaridade_de_nome_de_propriedade' ],
+ 'SearchByProperty' => [ 'Pesquisar_por_propriedade' ],
+ 'SMWAdmin' => [ 'SemanticMediaWiki', 'SMWAdmin' ],
+ 'Types' => [ 'Tipos' ],
+ 'UnusedProperties' => [ 'Propriedades_não_utilizadas' ],
+ 'URIResolver' => [ 'URIResolver' ],
+ 'WantedProperties' => [ 'Propriedades_desejadas' ],
+];
+
+/** Romanian (română) */
+$specialPageAliases['ro'] = [
+ 'Browse' => [ 'Răsfoieşte' ],
+];
+
+/** Sicilian (sicilianu) */
+$specialPageAliases['scn'] = [
+ 'Ask' => [ 'Chiedi' ],
+ 'Browse' => [ 'Esplora' ],
+ 'ExportRDF' => [ 'EsportaRDF' ],
+ 'PageProperty' => [ 'ProprietàPagina' ],
+ 'Properties' => [ 'Proprietà' ],
+ 'SMWAdmin' => [ 'AdminSMW' ],
+ 'SearchByProperty' => [ 'CercaPerProprietà' ],
+ 'Types' => [ 'Tipi' ],
+ 'URIResolver' => [ 'RisolutoreURI' ],
+ 'UnusedProperties' => [ 'ProprietàNonUtilizzate' ],
+ 'WantedProperties' => [ 'ProprietàRichieste' ],
+];
+
+/** Slovak (slovenÄina) */
+$specialPageAliases['sk'] = [
+ 'Ask' => [ 'SpýtaťSa' ],
+ 'Browse' => [ 'Prehliadať' ],
+ 'PageProperty' => [ 'VlastnostiStránky' ],
+ 'Properties' => [ 'Vlastnosti' ],
+ 'SMWAdmin' => [ 'SprávcaSMW' ],
+ 'SearchByProperty' => [ 'HľadaniePodľaVlastností' ],
+ 'Types' => [ 'Typy' ],
+ 'URIResolver' => [ 'PrekladURI' ],
+ 'UnusedProperties' => [ 'NepoužívanéVlastnosti' ],
+ 'WantedProperties' => [ 'ŽiadanéVlastnosti' ],
+];
+
+/** Albanian (shqip) */
+$specialPageAliases['sq'] = [
+ 'Ask' => [ 'Pyet' ],
+ 'Browse' => [ 'Sille' ],
+];
+
+/** Serbian (Cyrillic script) (ÑрпÑки (ћирилица)‎) */
+$specialPageAliases['sr-ec'] = [
+ 'Ask' => [ 'Питај' ],
+ 'Browse' => [ 'Потражи' ],
+ 'Properties' => [ 'СвојÑтва' ],
+ 'Types' => [ 'Ð’Ñ€Ñте' ],
+ 'UnusedProperties' => [ 'ÐекоришћенаСвојÑтва', 'Ðекоришћена_ÑвојÑтва' ],
+ 'WantedProperties' => [ 'ТраженаСвојÑтва', 'Тражена_ÑвојÑтва' ],
+];
+
+/** Swedish (svenska) */
+$specialPageAliases['sv'] = [
+ 'Ask' => [ 'Fråga' ],
+ 'Browse' => [ 'Bläddra' ],
+ 'ExportRDF' => [ 'Exportera_RDF' ],
+ 'Properties' => [ 'Egenskaper' ],
+ 'Types' => [ 'Typer' ],
+];
+
+/** Swahili (Kiswahili) */
+$specialPageAliases['sw'] = [
+ 'Ask' => [ 'Uliza' ],
+ 'Browse' => [ 'Fungua' ],
+ 'Types' => [ 'Aina' ],
+];
+
+/** Telugu (తెలà±à°—à±) */
+$specialPageAliases['te'] = [
+ 'Ask' => [ 'à°…à°¡à±à°—à±' ],
+];
+
+/** Tagalog (Tagalog) */
+$specialPageAliases['tl'] = [
+ 'Ask' => [ 'Magtanong' ],
+ 'Browse' => [ 'Tumingin-tingin' ],
+ 'ExportRDF' => [ 'Iluwas_ang_RDF' ],
+ 'PageProperty' => [ 'Pag-aari_ng_pahina' ],
+ 'Properties' => [ 'Mga_pag-aari' ],
+ 'SMWAdmin' => [ 'Tagapangasiwa_ng_SMW' ],
+ 'SearchByProperty' => [ 'Maghanap_ayon_sa_pag-aari' ],
+ 'Types' => [ 'Mga_uri' ],
+ 'URIResolver' => [ 'Tagapaglutas_ng_URI' ],
+ 'UnusedProperties' => [ 'Mga_pag-aaring_hindi_ginagamit' ],
+ 'WantedProperties' => [ 'Mga_pag-aaring_ninanais' ],
+];
+
+/** Turkish (Türkçe) */
+$specialPageAliases['tr'] = [
+ 'Ask' => [ 'Sor' ],
+ 'Browse' => [ 'Gezin' ],
+ 'ExportRDF' => [ 'RDFAktar' ],
+ 'PageProperty' => [ 'SayfaÖzelliği' ],
+ 'Properties' => [ 'Özellikler' ],
+ 'SMWAdmin' => [ 'SMWHizmetlisi', 'SMWYöneticisi' ],
+ 'SearchByProperty' => [ 'ÖzelliğeGöreAra' ],
+ 'Types' => [ 'Türler', 'Tipler' ],
+ 'URIResolver' => [ 'URIÇözücü' ],
+ 'UnusedProperties' => [ 'KullanılmayanÖzellikler' ],
+ 'WantedProperties' => [ 'İstenenÖzellikler' ],
+];
+
+/** Ukrainian (українÑька) */
+$specialPageAliases['uk'] = [
+ 'Properties' => [ 'ВлаÑтивоÑÑ‚Ñ–' ],
+ 'Types' => [ 'Типи' ],
+];
+
+/** Urdu (اردو) */
+$specialPageAliases['ur'] = [
+ 'Ask' => [ 'پوچھیں' ],
+ 'Types' => [ 'اقسام' ],
+];
+
+/** Venetian (vèneto) */
+$specialPageAliases['vec'] = [
+ 'Browse' => [ 'Sfója' ],
+ 'Properties' => [ 'Proprietà' ],
+ 'Types' => [ 'Tipi' ],
+];
+
+/** Vietnamese (Tiếng Việt) */
+$specialPageAliases['vi'] = [
+ 'Ask' => [ 'Há»i' ],
+ 'Browse' => [ 'Duyệt' ],
+ 'ExportRDF' => [ 'Xuất_RDF' ],
+ 'PageProperty' => [ 'Thuộc_tính_trang' ],
+ 'Properties' => [ 'Thuộc_tính' ],
+ 'Concepts' => [ 'Khái_niệm' ],
+ 'SMWAdmin' => [ 'Quản_lý_SMW', 'Quản_lí_SMW' ],
+ 'SearchByProperty' => [ 'Tìm_theo_thuộc_tính' ],
+ 'Types' => [ 'Kiểu' ],
+ 'URIResolver' => [ 'Bộ_giải_URI' ],
+ 'UnusedProperties' => [ 'Thuộc_tính_không_dùng' ],
+ 'WantedProperties' => [ 'Thuộc_tính_cần_thiết' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$specialPageAliases['zh-hans'] = [
+ 'Ask' => [ '询问' ],
+ 'Browse' => [ 'æµè§ˆ' ],
+ 'ExportRDF' => [ '导出RDF' ],
+ 'PageProperty' => [ '页é¢å±žæ€§' ],
+ 'Properties' => [ '属性' ],
+ 'Concepts' => [ '概念' ],
+ 'SMWAdmin' => [ 'SMW管ç†' ],
+ 'SearchByProperty' => [ '按属性æœç´¢' ],
+ 'Types' => [ '类型' ],
+ 'URIResolver' => [ 'URI分解器' ],
+ 'UnusedProperties' => [ '未使用属性' ],
+ 'WantedProperties' => [ '尚缺属性' ],
+];
+
+/** Traditional Chinese (中文(ç¹é«”)‎) */
+$specialPageAliases['zh-hant'] = [
+ 'Ask' => [ 'è©¢å•' ],
+ 'Browse' => [ 'ç€è¦½' ],
+ 'ExportRDF' => [ '匯出_RDF' ],
+ 'PageProperty' => [ 'é é¢å±¬æ€§' ],
+ 'Properties' => [ '屬性' ],
+ 'Concepts' => [ '概念' ],
+ 'SMWAdmin' => [ 'SMW_管ç†å“¡' ],
+ 'SearchByProperty' => [ 'ä¾å±¬æ€§æœå°‹' ],
+ 'Types' => [ 'åž‹æ…‹' ],
+ 'URIResolver' => [ 'URI_分解器' ],
+ 'UnusedProperties' => [ '未使用屬性' ],
+ 'WantedProperties' => [ '需è¦çš„屬性' ],
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.magic.php b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.magic.php
new file mode 100644
index 00000000..c5308135
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/SemanticMediaWiki.magic.php
@@ -0,0 +1,409 @@
+<?php
+
+/**
+ * Magic words
+ *
+ * @author أحمد غربية <ahmad@arabdigitalexpression.org>
+ * @file
+ * @ingroup Extensions
+ * @ingroup SMWLanguage
+ */
+
+$magicWords = [];
+
+/** English (English) */
+$magicWords['en'] = [
+ 'ask' => [ 0, 'ask' ],
+ 'show' => [ 0, 'show' ],
+ 'info' => [ 0, 'info' ],
+ 'concept' => [ 0, 'concept' ],
+ 'subobject' => [ 0, 'subobject' ],
+ 'smwdoc' => [ 0, 'smwdoc' ],
+ 'set' => [ 0, 'set' ],
+ 'set_recurring_event' => [ 0, 'set_recurring_event' ],
+ 'declare' => [ 0, 'declare' ],
+ 'SMW_NOFACTBOX' => [ 0, '__NOFACTBOX__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__SHOWFACTBOX__' ],
+];
+
+/** Afrikaans (Afrikaans) */
+$magicWords['af'] = [
+ 'ask' => [ 0, 'vra', 'ask' ],
+ 'show' => [ 0, 'wys', 'show' ],
+ 'concept' => [ 0, 'konsep', 'concept' ],
+ 'set' => [ 0, 'stel', 'set' ],
+ 'declare' => [ 0, 'verklaar', 'declare' ],
+];
+
+/** Arabic (العربية) */
+$magicWords['ar'] = [
+ 'ask' => [ 0, 'سؤال' ],
+ 'show' => [ 0, 'عرض' ],
+ 'info' => [ 0, 'معلومات' ],
+ 'concept' => [ 0, 'Ù…Ùهوم' ],
+ 'subobject' => [ 0, 'كائن_Ùرعي' ],
+ 'smwdoc' => [ 0, 'وثائق_سمو', 'توثيق_سمو' ],
+ 'set' => [ 0, 'تعيين' ], //من تعيين القيمة للمتغير\الكائن
+ 'set_recurring_event' => [ 0, 'تعيين_حدث_متكرر' ],
+ 'declare' => [ 0, 'إقرار', 'إعلان' ],
+ 'SMW_NOFACTBOX' => [ 0, '__لا_صندوق_حقائق__', '__لا_صندوق_حقيقة__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__عرض_صندوق_الحقائق__', '__عرض_صندوق_الحقيقة__' ,]
+];
+
+/** Egyptian Arabic (مصرى) */
+$magicWords['arz'] = [
+ 'ask' => [ 0, 'سؤال' ],
+ 'show' => [ 0, 'عرض' ],
+ 'info' => [ 0, 'معلومات' ],
+ 'concept' => [ 0, 'مبدأ' ],
+ 'subobject' => [ 0, 'كائن_Ùرعى' ],
+ 'smwdoc' => [ 0, 'توثيق_سمو' ],
+ 'set' => [ 0, 'مجموعة' ],
+ 'set_recurring_event' => [ 0, 'ضبط_حدث_جارى' ],
+ 'declare' => [ 0, 'إعلان' ],
+ 'SMW_NOFACTBOX' => [ 0, '__لا_صندوق_حقيقة__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__عرض_صندوق_الحقيقة__' ],
+];
+
+/** Assamese (অসমীয়া) */
+$magicWords['as'] = [
+ 'ask' => [ 0, 'সোধক' ],
+ 'show' => [ 0, 'দেখà§à§±à¦¾à¦“ক' ],
+ 'info' => [ 0, 'তথà§à¦¯' ],
+];
+
+/** Breton (brezhoneg) */
+$magicWords['br'] = [
+ 'ask' => [ 0, 'goulenn' ],
+ 'show' => [ 0, 'diskouez' ],
+ 'info' => [ 0, 'keloù' ],
+ 'concept' => [ 0, 'meizad' ],
+ 'declare' => [ 0, 'disklêriañ' ],
+];
+
+/** Czech (ÄeÅ¡tina) */
+$magicWords['cs'] = [
+ 'ask' => [ 0, 'otázka' ],
+ 'show' => [ 0, 'zobrazit' ],
+ 'set' => [ 0, 'nastavit' ],
+];
+
+/** Chuvash (Чӑвашла) */
+$magicWords['cv'] = [
+ 'SMW_NOFACTBOX' => [ 0, '__NOFACTBOX__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__SHOWFACTBOX__' ],
+];
+
+/** German (Deutsch) */
+$magicWords['de'] = [
+ 'ask' => [ 0, 'frage' ],
+ 'show' => [ 0, 'zeige' ],
+ 'info' => [ 0, 'informiere' ],
+ 'concept' => [ 0, 'konzept' ],
+ 'subobject' => [ 0, 'unterobjekt' ],
+ 'smwdoc' => [ 0, 'smwdok' ],
+ 'set' => [ 0, 'setze' ],
+ 'set_recurring_event' => [ 0, 'setze_wiederholung' ],
+ 'declare' => [ 0, 'deklariere' ],
+ 'SMW_NOFACTBOX' => [ 0, '__KEINE_FAKTENANZEIGE__', '__KEINEFAKTENANZEIGE__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__FAKTENANZEIGE__' ],
+];
+
+/** Zazaki (Zazaki) */
+$magicWords['diq'] = [
+ 'ask' => [ 0, 'perske' ],
+ 'show' => [ 0, 'bımocne' ],
+ 'info' => [ 0, 'zanışe' ],
+ 'concept' => [ 0, 'konsept' ],
+ 'subobject' => [ 0, 'bınobce' ],
+ 'set' => [ 0, 'saz' ],
+ 'declare' => [ 0, 'ilaniye' ],
+ 'SMW_NOFACTBOX' => [ 0, '__DORARAŞTAYÇINİYA__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__DORARAÅžTAYBIMOCNE__' ],
+];
+
+/** Spanish (español) */
+$magicWords['es'] = [
+ 'ask' => [ 0, 'preguntar', 'pregunta' ],
+ 'show' => [ 0, 'muestra', 'mostrar' ],
+ 'info' => [ 0, 'informacion', 'información' ],
+ 'concept' => [ 0, 'concepto' ],
+ 'set' => [ 0, 'establecer', 'determinar' ],
+ 'set_recurring_event' => [ 0, 'establecer_evento_recurrente', 'determinar_evento_recurrente' ],
+ 'declare' => [ 0, 'declarar', 'declara' ],
+];
+
+/** Persian (Ùارسی) */
+$magicWords['fa'] = [
+ 'ask' => [ 0, 'پرسش','سوال' ],
+ 'show' => [ 0, 'نمایش' ],
+ 'info' => [ 0, 'اطلاع' ],
+ 'concept' => [ 0, 'Ù…Ùهوم' ],
+ 'subobject' => [ 0, 'جزءشیء' ],
+ 'smwdoc' => [ 0, 'smwdoc' ],
+ 'set' => [ 0, 'مجموعه' ],
+ 'set_recurring_event' => [ 0, 'set_recurring_event' ],
+ 'declare' => [ 0, 'declare' ],
+ 'SMW_NOFACTBOX' => [ 0, '__NOFACTBOX__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__SHOWFACTBOX__' ],
+];
+
+/** French (français) */
+$magicWords['fr'] = [
+ 'ask' => [ 0, 'demander' ],
+ 'show' => [ 0, 'afficher' ],
+ 'info' => [ 0, 'infos' ],
+ 'concept' => [ 0, 'concept' ],
+ 'subobject' => [ 0, 'sousobjet' ],
+ 'smwdoc' => [ 0, 'docsmw' ],
+ 'set' => [ 0, 'définit' ],
+ 'set_recurring_event' => [ 0, 'définit_périodique' ],
+ 'declare' => [ 0, 'déclare' ],
+ 'SMW_NOFACTBOX' => [ 0, '__SANSBOÃŽTEFAITS__', '__SANSBOITEFAITS__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__AFFICHERBOÃŽTEFAITS__', '__AFFICHERBOITEFAITS__' ],
+];
+
+/** Western Frisian (Frysk) */
+$magicWords['fy'] = [
+ 'info' => [ 0, 'ynfo' ],
+];
+
+/** Hebrew (עברית) */
+$magicWords['he'] = [
+ 'ask' => [ 0, 'ש×ל' ],
+];
+
+/** Indonesian (Bahasa Indonesia) */
+$magicWords['id'] = [
+ 'ask' => [ 0, 'tanya' ],
+ 'show' => [ 0, 'tampilkan' ],
+ 'info' => [ 0, 'info' ],
+ 'concept' => [ 0, 'konsep' ],
+ 'set' => [ 0, 'tetapkan' ],
+ 'declare' => [ 0, 'deklarasi' ],
+];
+
+/** Igbo (Igbo) */
+$magicWords['ig'] = [
+ 'ask' => [ 0, 'jüo', 'ask' ],
+];
+
+/** Georgian (ქáƒáƒ áƒ—ული) */
+$magicWords['ka'] = [
+ 'ask' => [ 0, 'კითხვáƒ' ],
+ 'show' => [ 0, 'ჩვენებáƒ' ],
+ 'info' => [ 0, 'ინფáƒ' ],
+];
+
+/** Korean (한국어) */
+$magicWords['ko'] = [
+ 'ask' => [ 0, '묻기' ],
+ 'show' => [ 0, 'ë³´ì´ê¸°' ],
+ 'info' => [ 0, 'ì •ë³´' ],
+ 'concept' => [ 0, 'ìƒê°' ],
+ 'subobject' => [ 0, '하위ê°ì²´' ],
+ 'smwdoc' => [ 0, 'smw문서' ],
+ 'set' => [ 0, '설정' ],
+ 'set_recurring_event' => [ 0, '반복_ì¼ì •_설정' ],
+ 'declare' => [ 0, 'ì„ ì–¸' ],
+ 'SMW_NOFACTBOX' => [ 0, '__사실ìƒìžìˆ¨ê¹€__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__사실ìƒìžë³´ì´ê¸°__', '__사실ìƒìží‘œì‹œ__' ],
+];
+
+/** Cornish (kernowek) */
+$magicWords['kw'] = [
+ 'ask' => [ 0, 'govyn' ],
+ 'show' => [ 0, 'diskwedhes' ],
+ 'info' => [ 0, 'kedhlow' ],
+ 'set' => [ 0, 'settya' ],
+];
+
+/** Luxembourgish (Lëtzebuergesch) */
+$magicWords['lb'] = [
+ 'ask' => [ 0, 'froen' ],
+ 'show' => [ 0, 'weisen' ],
+ 'concept' => [ 0, 'Konzept' ],
+];
+
+/** Macedonian (македонÑки) */
+$magicWords['mk'] = [
+ 'ask' => [ 0, 'прашај' ],
+ 'show' => [ 0, 'прикажи' ],
+ 'info' => [ 0, 'инфо' ],
+ 'concept' => [ 0, 'поим' ],
+ 'subobject' => [ 0, 'подобјект' ],
+ 'smwdoc' => [ 0, 'Ñмвдок' ],
+ 'set' => [ 0, 'поÑтави' ],
+ 'set_recurring_event' => [ 0, 'поÑтави_повторлив_наÑтан' ],
+ 'declare' => [ 0, 'изјави' ],
+ 'SMW_NOFACTBOX' => [ 0, '__БЕЗФÐКТКУТИЈÐ__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__ПРИКÐЖИФÐКТКУТИЈÐ__' ],
+];
+
+/** Malayalam (മലയാളം) */
+$magicWords['ml'] = [
+ 'ask' => [ 0, 'ചോദികàµà´•àµà´•' ],
+ 'show' => [ 0, 'à´ªàµà´°à´¦àµ¼à´¶à´¿à´ªàµà´ªà´¿à´•àµà´•àµà´•' ],
+ 'info' => [ 0, 'വിവരം' ],
+ 'concept' => [ 0, 'ആശയം' ],
+ 'set' => [ 0, 'ഗണം' ],
+ 'declare' => [ 0, 'à´ªàµà´°à´–àµà´¯à´¾à´ªà´¿à´•àµà´•àµà´•' ],
+];
+
+/** Marathi (मराठी) */
+$magicWords['mr'] = [
+ 'ask' => [ 0, 'विचारा' ],
+ 'show' => [ 0, 'दाखवा' ],
+ 'info' => [ 0, 'माहिती' ],
+ 'concept' => [ 0, 'कंसेपà¥à¤Ÿ', 'कलà¥à¤ªà¤¨à¤¾' ],
+ 'set' => [ 0, 'पà¥à¤°à¤¯à¥à¤•à¥à¤¤', 'सेट', 'सà¥à¤¥à¤¾à¤ªà¤¿à¤¤' ],
+ 'set_recurring_event' => [ 0, 'पà¥à¤°à¤¯à¥à¤•à¥à¤¤_पà¥à¤°à¥à¤¨_कारà¥à¤¯' ],
+ 'declare' => [ 0, 'पà¥à¤°à¤•à¤Ÿà¤•à¤°à¤¾' ],
+ 'SMW_NOFACTBOX' => [ 0, '__फॅकà¥à¤Ÿà¤¬à¥‰à¤•à¥à¤¸à¤¨à¤¾à¤¹à¥€__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__फॅकà¥à¤Ÿà¤¬à¥‰à¤•à¥à¤¸à¤¦à¤¾à¤–वा__' ],
+];
+
+/** Low Saxon (Netherlands) (Nedersaksies) */
+$magicWords['nds-nl'] = [
+ 'concept' => [ 0, 'konsept' ],
+ 'set_recurring_event' => [ 0, 'herhaolende_gebeurtenisse_instellen' ],
+ 'declare' => [ 0, 'deklareren' ],
+ 'SMW_NOFACTBOX' => [ 0, '__GIENFEITENKAODER__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__FEITENKAODERWEERGEVEN__' ],
+];
+
+/** Dutch (Nederlands) */
+$magicWords['nl'] = [
+ 'ask' => [ 0, 'vragen' ],
+ 'show' => [ 0, 'weergeven' ],
+ 'subobject' => [ 0, 'onderobject' ],
+ 'set' => [ 0, 'instellen' ],
+ 'set_recurring_event' => [ 0, 'herhalende_gebeurtenis_instellen' ],
+ 'declare' => [ 0, 'declareren' ],
+ 'SMW_NOFACTBOX' => [ 0, '__GEENFEITENKADER__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__FEITENKADERWEERGEVEN__' ],
+];
+
+/** Punjabi (ਪੰਜਾਬੀ) */
+$magicWords['pa'] = [
+ 'ask' => [ 0, 'ਪà©à©±à¨›à©‹' ],
+ 'show' => [ 0, 'ਵਿਖਾਓ' ],
+ 'info' => [ 0, 'ਜਾਣਕਾਰੀ' ],
+];
+
+/** Polish (polski) */
+$magicWords['pl'] = [
+ 'ask' => [ 0, 'pytanie' ],
+ 'show' => [ 0, 'pokaż' ],
+ 'info' => [ 0, 'informacja' ],
+ 'concept' => [ 0, 'koncept' ],
+ 'set' => [ 0, 'ustaw' ],
+ 'declare' => [ 0, 'zadeklaruj' ],
+];
+
+/** Pashto (پښتو) */
+$magicWords['ps'] = [
+ 'ask' => [ 0, 'پوښتل', 'ask' ],
+ 'show' => [ 0, 'ښکاره_کول', 'show' ],
+ 'info' => [ 0, 'مالومات', 'info' ],
+];
+
+/** Portuguese (português) */
+$magicWords['pt'] = [
+ 'ask' => [ 0, 'consultar' ],
+ 'show' => [ 0, 'mostrar' ],
+ 'concept' => [ 0, 'conceito' ],
+ 'subobject' => [ 0, 'subobjeto' ],
+ 'set' => [ 0, 'definir' ],
+ 'set_recurring_event' => [ 0, 'definir_evento_recorrente' ],
+ 'declare' => [ 0, 'declarar' ],
+ 'SMW_NOFACTBOX' => [ 0, '__SEMCAIXADEFATOS__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__EXIBIRCAIXADEFATOS__' ],
+];
+
+/** Brazilian Portuguese (português do Brasil) */
+$magicWords['pt-br'] = [
+ 'ask' => [ 0, 'consultar' ],
+ 'show' => [ 0, 'mostrar' ],
+ 'info' => [ 0, 'info' ],
+ 'concept' => [ 0, 'conceito' ],
+ 'subobject' => [ 0, 'subobjeto' ],
+ 'smwdoc' => [ 0, 'smwdoc' ],
+ 'set' => [ 0, 'definir' ],
+ 'set_recurring_event' => [ 0, 'definir_evento_recorrente' ],
+ 'declare' => [ 0, 'declarar' ],
+ 'SMW_NOFACTBOX' => [ 0, '__SEMCAIXADEFATOS__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__EXIBIRCAIXADEFATOS__' ],
+];
+
+/** Serbian (Cyrillic script) (ÑрпÑки (ћирилица)‎) */
+$magicWords['sr-ec'] = [
+ 'ask' => [ 0, 'питај' ],
+ 'show' => [ 0, 'прикажи' ],
+ 'info' => [ 0, 'подаци' ],
+ 'concept' => [ 0, 'концепт' ],
+ 'set' => [ 0, 'поÑтави' ],
+ 'set_recurring_event' => [ 0, 'поÑтави_периодични_догађај' ],
+ 'declare' => [ 0, 'одреди' ],
+];
+
+/** Serbian (Latin script) (srpski (latinica)‎) */
+$magicWords['sr-el'] = [
+ 'ask' => [ 0, 'pitaj' ],
+ 'show' => [ 0, 'prikaži' ],
+ 'info' => [ 0, 'podaci' ],
+ 'concept' => [ 0, 'koncept' ],
+ 'set' => [ 0, 'postavi' ],
+ 'set_recurring_event' => [ 0, 'postavi_periodiÄni_dogaÄ‘aj' ],
+ 'declare' => [ 0, 'odredi' ],
+ 'SMW_NOFACTBOX' => [ 0, '__BEZÄŒINJENICA__', '__BEZ_ÄŒINJENICA__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__PRIKAŽIČINJENICE__', '__PRIKAŽI_ČINJENICE__' ],
+];
+
+/** Swedish (svenska) */
+$magicWords['sv'] = [
+ 'ask' => [ 0, 'fråga', 'ask' ],
+ 'show' => [ 0, 'visa', 'show' ],
+ 'concept' => [ 0, 'koncept', 'concept' ],
+];
+
+/** Tatar (Cyrillic script) (татарча) */
+$magicWords['tt-cyrl'] = [
+ 'ask' => [ 0, 'Ñорау' ],
+ 'show' => [ 0, 'күрÑәт' ],
+ 'info' => [ 0, 'мәгълүмат' ],
+];
+
+/** Vietnamese (Tiếng Việt) */
+$magicWords['vi'] = [
+ 'ask' => [ 0, 'há»i' ],
+ 'show' => [ 0, 'hiển_thị' ],
+ 'info' => [ 0, 'thông_tin' ],
+ 'concept' => [ 0, 'khái_niệm' ],
+ 'set' => [ 0, 'đặt' ],
+];
+
+/** Simplified Chinese (中文(简体)‎) */
+$magicWords['zh-hans'] = [
+ 'ask' => [ 0, '询问' ],
+ 'show' => [ 0, '显示' ],
+ 'info' => [ 0, 'ä¿¡æ¯' ],
+ 'concept' => [ 0, '概念' ],
+ 'subobject' => [ 0, 'å­å¯¹è±¡' ],
+ 'smwdoc' => [ 0, 'SMW文档' ],
+ 'set' => [ 0, '设置' ],
+ 'set_recurring_event' => [ 0, '设置循环活动' ],
+ 'declare' => [ 0, '宣布' ],
+ 'SMW_NOFACTBOX' => [ 0, '__无实际内容框__' ],
+ 'SMW_SHOWFACTBOX' => [ 0, '__显示实际内容框__' ],
+];
+
+/** Traditional Chinese (中文(ç¹é«”)‎) */
+$magicWords['zh-hant'] = [
+ 'ask' => [ 0, '訪å•' ],
+ 'show' => [ 0, '顯示' ],
+ 'info' => [ 0, '資訊' ],
+ 'smwdoc' => [ 0, 'SMW檔案' ],
+ 'set' => [ 0, '設定' ],
+ 'set_recurring_event' => [ 0, '設定循環活動', '設置定期活動' ],
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ar.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ar.json
new file mode 100644
index 00000000..7d2dbfb1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ar.json
@@ -0,0 +1,189 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "خاصية",
+ "SMW_NS_PROPERTY_TALK": "نقاش_الخاصية",
+ "SMW_NS_CONCEPT": "مبدأ",
+ "SMW_NS_CONCEPT_TALK": "نقاش_المبدأ"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "الصÙحة",
+ "_txt": "نص",
+ "_cod": "كود",
+ "_boo": "منطقي",
+ "_num": "عدد",
+ "_geo": "الإحداثيات الجغراÙية",
+ "_tem": "الحرارة",
+ "_dat": "التاريخ",
+ "_ema": "البريد الإلكتروني",
+ "_uri": "مسار",
+ "_anu": "التعليق علي معر٠الموارد الموحد",
+ "_tel": "رقم الهاتÙ",
+ "_rec": "تسجيل",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Float": "_num",
+ "Integer": "_num",
+ "سلسلة": "_txt",
+ "Enumeration": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "لديه نوع",
+ "_URI": "معر٠الموارد الموحد معادلة",
+ "_SUBP": "الخاصية الÙرعية Ù„",
+ "_SUBC": "تصني٠Ùرعي من",
+ "_UNIT": "عرض الوحدات",
+ "_IMPO": "المستوردة من",
+ "_CONV": "يقابل",
+ "_SERV": "يوÙر الخدمة",
+ "_PVAL": "يسمح بالقيمة",
+ "_MDAT": "تاريخ التعديل",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "يمتلك قيمة غير صحيحة ل",
+ "_LIST": "يمتلك حقول",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "MIME type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "عرض الوحدة": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "يناير",
+ "يناير"
+ ],
+ [
+ "Ùبراير",
+ "Ùبراير"
+ ],
+ [
+ "مارس",
+ "مارس"
+ ],
+ [
+ "أبريل",
+ "أبريل"
+ ],
+ [
+ "مايو",
+ "مايو"
+ ],
+ [
+ "يونيو",
+ "يونيو"
+ ],
+ [
+ "يوليو",
+ "يوليو"
+ ],
+ [
+ "أغسطس",
+ "أغسطس"
+ ],
+ [
+ "سبتمبر",
+ "سبتمبر"
+ ],
+ [
+ "أكتوبر",
+ "أكتوبر"
+ ],
+ [
+ "نوÙمبر",
+ "نوÙمبر"
+ ],
+ [
+ "ديسمبر",
+ "ديسمبر"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/arz.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/arz.json
new file mode 100644
index 00000000..794d22c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/arz.json
@@ -0,0 +1,189 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "خاصية",
+ "SMW_NS_PROPERTY_TALK": "نقاش_الخاصية",
+ "SMW_NS_CONCEPT": "مبدأ",
+ "SMW_NS_CONCEPT_TALK": "نقاش_المبدأ"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "الصÙحة",
+ "_txt": "نص",
+ "_cod": "كود",
+ "_boo": "منطقى",
+ "_num": "عدد",
+ "_geo": "الإحداثيات الجغراÙية",
+ "_tem": "الحرارة",
+ "_dat": "التاريخ",
+ "_ema": "البريد الإلكترونى",
+ "_uri": "مسار",
+ "_anu": "التعليق علي معر٠الموارد الموحد",
+ "_tel": "Telephone number",
+ "_rec": "Record",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Float": "_num",
+ "Integer": "_num",
+ "سلسلة": "_txt",
+ "Enumeration": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "لديه نوع",
+ "_URI": "معر٠الموارد الموحد معادلة",
+ "_SUBP": "الخاصية الÙرعية Ù„",
+ "_SUBC": "Subcategory of",
+ "_UNIT": "عرض الوحدات",
+ "_IMPO": "المستوردة من",
+ "_CONV": "يقابل",
+ "_SERV": "يوÙر الخدمة",
+ "_PVAL": "يسمح بالقيمة",
+ "_MDAT": "Modification date",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Has improper value for",
+ "_LIST": "Has fields",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "MIME type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "عرض الوحدة": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "يناير",
+ "يناير"
+ ],
+ [
+ "Ùبراير",
+ "Ùبراير"
+ ],
+ [
+ "مارس",
+ "مارس"
+ ],
+ [
+ "أبريل",
+ "أبريل"
+ ],
+ [
+ "مايو",
+ "مايو"
+ ],
+ [
+ "يونيو",
+ "يونيو"
+ ],
+ [
+ "يوليو",
+ "يوليو"
+ ],
+ [
+ "أغسطس",
+ "أغسطس"
+ ],
+ [
+ "سبتمبر",
+ "سبتمبر"
+ ],
+ [
+ "أكتوبر",
+ "أكتوبر"
+ ],
+ [
+ "نوÙمبر",
+ "نوÙمبر"
+ ],
+ [
+ "ديسمبر",
+ "ديسمبر"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ca.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ca.json
new file mode 100644
index 00000000..8a566a95
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ca.json
@@ -0,0 +1,200 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Propietat",
+ "SMW_NS_PROPERTY_TALK": "Propietat_Discussió",
+ "SMW_NS_CONCEPT": "Concepte",
+ "SMW_NS_CONCEPT_TALK": "Concepte_Discussió"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Pàgina",
+ "_txt": "Text",
+ "_cod": "Codi",
+ "_boo": "Booleà",
+ "_num": "Nombre",
+ "_geo": "Coordenades geogràfiques",
+ "_tem": "Temperatura",
+ "_dat": "Data",
+ "_ema": "Adreça electrònica",
+ "_uri": "URL",
+ "_anu": "URI-Anotació",
+ "_tel": "Número de telèfon",
+ "_rec": "Registre",
+ "_qty": "Quantitat",
+ "_mlt_rec": "Text monolingüe",
+ "_eid": "Identificador extern",
+ "_keyw": "Paraula clau",
+ "_ref_rec": "Referència"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Decimal": "_num",
+ "Enter": "_num",
+ "Cadena": "_txt",
+ "Enumeració": "_txt",
+ "Número de telèfon": "_tel",
+ "Adreça electrònica": "_ema",
+ "Coordenada geogràfica": "_geo",
+ "Polígon geogràfic": "_gpo"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Té tipus",
+ "_URI": "URI equivalent",
+ "_SUBP": "Subpropietat de",
+ "_SUBC": "Subcategoria de",
+ "_UNIT": "Unitats de mesura",
+ "_IMPO": "Importat de",
+ "_CONV": "Correspon a",
+ "_SERV": "Proporciona servei",
+ "_PVAL": "Permet valor",
+ "_MDAT": "Data de modificació",
+ "_CDAT": "Data de creació",
+ "_NEWP": "És pàgina nova",
+ "_LEDT": "Darrer editor és",
+ "_ERRP": "Té valor incorrecte per a",
+ "_LIST": "Té camps",
+ "_SOBJ": "Té subobjecte",
+ "_ASK": "Té consulta",
+ "_ASKST": "Cadena de consulta",
+ "_ASKFO": "Format de consulta",
+ "_ASKSI": "Mida de consulta",
+ "_ASKDE": "Profunditat de consulta",
+ "_ASKDU": "Durada de consulta",
+ "_ASKSC": "Font de consulta",
+ "_ASKPA": "Paràmetres de consulta",
+ "_MEDIA": "Tipus Media",
+ "_MIME": "Tipus MIME",
+ "_ERRC": "Té error de processament",
+ "_ERRT": "Té text d'error de processament",
+ "_PREC": "Precisió a mostrar de",
+ "_LCODE": "Codi d'idioma",
+ "_TEXT": "Text",
+ "_PDESC": "Té descripció de propietat",
+ "_PVAP": "Permet patró",
+ "_PVALI": "Permet llista de valors",
+ "_DTITLE": "Títol a mostrar de",
+ "_PVUC": "Té restricció única",
+ "_PEID": "Identificador extern",
+ "_PEFU": "URI del formatador extern",
+ "_PPLB": "Té etiqueta de propietat preferida",
+ "_EDIP": "Té protecció de modificació",
+ "_CHGPRO": "Propagació de canvi",
+ "_PPGR": "És grup de propietat"
+ },
+ "aliases": {
+ "Unitat de mesura": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "gener",
+ "gen"
+ ],
+ [
+ "febrer",
+ "febr"
+ ],
+ [
+ "març",
+ "març"
+ ],
+ [
+ "abril",
+ "abr"
+ ],
+ [
+ "maig",
+ "maig"
+ ],
+ [
+ "juny",
+ "juny"
+ ],
+ [
+ "juliol",
+ "jul"
+ ],
+ [
+ "agost",
+ "ag"
+ ],
+ [
+ "setembre",
+ "set"
+ ],
+ [
+ "octubre",
+ "oct"
+ ],
+ [
+ "novembre",
+ "nov"
+ ],
+ [
+ "desembre",
+ "des"
+ ]
+ ],
+ "days": [
+ [
+ "dilluns",
+ "dl"
+ ],
+ [
+ "dimarts",
+ "dm"
+ ],
+ [
+ "dimecres",
+ "dc"
+ ],
+ [
+ "dijous",
+ "dj"
+ ],
+ [
+ "divendres",
+ "dv"
+ ],
+ [
+ "dissabte",
+ "ds"
+ ],
+ [
+ "diumenge",
+ "dg"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-at.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-at.json
new file mode 100644
index 00000000..53eef642
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-at.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "de"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-ch.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-ch.json
new file mode 100644
index 00000000..53eef642
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-ch.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "de"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-formal.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-formal.json
new file mode 100644
index 00000000..53eef642
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de-formal.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "de"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de.json
new file mode 100644
index 00000000..81fae9e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/de.json
@@ -0,0 +1,258 @@
+{
+ "@metadata": {
+ "authors": [
+ "Karsten Hoffmeyer",
+ "Markus Krötzsch"
+ ]
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Attribut",
+ "SMW_NS_PROPERTY_TALK": "Attribut_Diskussion",
+ "SMW_NS_CONCEPT": "Konzept",
+ "SMW_NS_CONCEPT_TALK": "Konzept_Diskussion",
+ "SMW_NS_SCHEMA": "SMW/Schema",
+ "SMW_NS_SCHEMA_TALK": "SMW/Schema_Diskussion"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Seite",
+ "_txt": "Text",
+ "_cod": "Quellcode",
+ "_boo": "Wahrheitswert",
+ "_num": "Zahl",
+ "_geo": "Geografische Koordinaten",
+ "_tem": "Temperatur",
+ "_dat": "Datum",
+ "_ema": "E-Mail",
+ "_uri": "URL",
+ "_anu": "URI-Annotation",
+ "_tel": "Telefonnummer",
+ "_rec": "Verbund",
+ "_qty": "Maß",
+ "_mlt_rec": "Einsprachiger Text",
+ "_eid": "Externe Kennung",
+ "_keyw": "Stichwort",
+ "_ref_rec": "Referenzierung"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Ganze Zahl": "_num",
+ "Dezimalzahl": "_num",
+ "Aufzählung": "_txt",
+ "Zeichenkette": "_txt",
+ "Menge": "_qty",
+ "Schlagwort": "_keyw"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Datentyp",
+ "_URI": "Gleichwertige URI",
+ "_SUBP": "Unterattribut von",
+ "_SUBC": "Unterkategorie von",
+ "_UNIT": "Einheiten",
+ "_IMPO": "Importiert aus",
+ "_CONV": "Entspricht",
+ "_SERV": "Bietet Service",
+ "_PVAL": "Erlaubt Wert",
+ "_MDAT": "Zuletzt geändert",
+ "_CDAT": "Erstellt",
+ "_NEWP": "Ist eine neue Seite",
+ "_LEDT": "Letzter Bearbeiter ist",
+ "_ERRP": "Hat unpassenden Wert für",
+ "_LIST": "Hat Komponenten",
+ "_SOBJ": "Hat Unterobjekt",
+ "_ASK": "Hat Abfrage",
+ "_ASKST": "Abfragetext",
+ "_ASKFO": "Abfrageformat",
+ "_ASKSI": "Abfragegröße",
+ "_ASKDE": "Abfragetiefe",
+ "_ASKDU": "Abfragedauer",
+ "_ASKPA": "Abfrageparameter",
+ "_ASKSC": "Abfragequelle",
+ "_ASKCO": "Abfragestatuscode",
+ "_MEDIA": "Medientyp",
+ "_MIME": "MIME-Typ",
+ "_ERRC": "Verarbeitungsfehler",
+ "_ERRT": "Verarbeitungsfehlerhinweis",
+ "_PREC": "Anzeigegenauigkeit",
+ "_LCODE": "Sprachcode",
+ "_TEXT": "Text",
+ "_PDESC": "Attributbeschreibung",
+ "_PVAP": "Erlaubt Muster",
+ "_DTITLE": "Anzeigetitel",
+ "_PVUC": "Erlaubt Wert einmal",
+ "_PVALI": "Erlaubt Werteliste",
+ "_PEID": "Kennung zur externen URI",
+ "_PEFU": "Formatierungsanweisung zur externen URI",
+ "_EDIP": "Ist bearbeitungsgeschützt",
+ "_CHGPRO": "Änderungsweitergabe",
+ "_PPGR": "Attributgruppe",
+ "_SCHEMA_DESC": "Schemabeschreibung",
+ "_SCHEMA_TAG": "Schemakennzeichen",
+ "_SCHEMA_TYPE": "Schematyp",
+ "_SCHEMA_DEF": "Schemadefinition",
+ "_SCHEMA_LINK": "Schemalink",
+ "_FORMAT_SCHEMA": "Formatierungsregel",
+ "_FILE_ATTCH": "Dateianhang",
+ "_CONT_TYPE": "Inhaltstyp",
+ "_CONT_AUTHOR": "Inhaltsautor",
+ "_CONT_LEN": "Inhaltslänge",
+ "_CONT_LANG": "Inhaltssprache",
+ "_CONT_TITLE": "Inhaltstitel",
+ "_CONT_DATE": "Inhaltsdatum",
+ "_CONT_KEYW": "Inhaltsstichwort",
+ "_TRANS": "Ãœbersetzung",
+ "_TRANS_SOURCE": "Ãœbersetzungsquelle",
+ "_TRANS_GROUP": "Ãœbersetzungsgruppe"
+ },
+ "aliases": {
+ "Hat Datentyp": "_TYPE",
+ "Hat erlaubten Wert": "_PVAL",
+ "Hat Einheiten": "_UNIT",
+ "Hat Medientyp": "_MEDIA",
+ "Hat MIME-Typ": "_MIME",
+ "Abfrage": "_ASK",
+ "Unterobjekt": "_SOBJ",
+ "Komponenten": "_LIST",
+ "Neue Seite": "_NEWP",
+ "Letzer Bearbeiter": "_LEDT",
+ "Ausgabeeinheit": "_UNIT",
+ "Gleichwertige URI von": "_URI",
+ "Unpassender Wert": "_ERRP",
+ "Hat Sprachcode": "_LCODE",
+ "Hat Anzeigegenauigkeit": "_PREC",
+ "Hat Attributbeschreibung": "_PDESC",
+ "Hat Anzeigetitel": "_DTITLE",
+ "Hat einmal erlaubten Wert": "_PVUC",
+ "Externe Kennung": "_PEID",
+ "Bearbeitungsgeschützt": "_EDIP",
+ "Ist Änderungsweitergabe": "_CHGPRO",
+ "Ist Attributgruppe": "_PPGR",
+ "Hat Schemabeschreibung": "_SCHEMA_DESC",
+ "Hat Schemakennzeichen": "_SCHEMA_TAG",
+ "Hat Schematyp": "_SCHEMA_TYPE",
+ "Hat Schemadefinition": "_SCHEMA_DEF",
+ "Hat Schemalink": "_SCHEMA_LINK",
+ "Hat Formatierungsregel": "_FORMAT_SCHEMA",
+ "Hat Dateianhang": "_FILE_ATTCH",
+ "Hat Inhaltstyp": "_CONT_TYPE",
+ "Hat Inhaltsautor": "_CONT_AUTHOR",
+ "Hat Inhaltslänge": "_CONT_LEN",
+ "Hat Inhaltssprache": "_CONT_LANG",
+ "Hat Inhaltstitel": "_CONT_TITLE",
+ "Hat Inhaltsdatum": "_CONT_DATE",
+ "Hat Inhaltsstichwort": "_CONT_KEYW",
+ "Hat Ãœbersetzung": "_TRANS",
+ "Hat Ãœbersetzungsquelle": "_TRANS_SOURCE",
+ "Hat Ãœbersetzungsgruppe": "_TRANS_GROUP"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "Januar",
+ "Jan"
+ ],
+ [
+ "Februar",
+ "Feb"
+ ],
+ [
+ "März",
+ "Mär"
+ ],
+ [
+ "April",
+ "Apr"
+ ],
+ [
+ "Mai",
+ "Mai"
+ ],
+ [
+ "Juni",
+ "Jun"
+ ],
+ [
+ "Juli",
+ "Jul"
+ ],
+ [
+ "August",
+ "Aug"
+ ],
+ [
+ "September",
+ "Sep"
+ ],
+ [
+ "Oktober",
+ "Okt"
+ ],
+ [
+ "November",
+ "Nov"
+ ],
+ [
+ "Dezember",
+ "Dez"
+ ]
+ ],
+ "days": [
+ [
+ "Montag",
+ "Mo"
+ ],
+ [
+ "Dienstag",
+ "Di"
+ ],
+ [
+ "Mittwoch",
+ "Mi"
+ ],
+ [
+ "Donnerstag",
+ "Do"
+ ],
+ [
+ "Freitag",
+ "Fr"
+ ],
+ [
+ "Samstag",
+ "Sa"
+ ],
+ [
+ "Sonntag",
+ "So"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "j. F Y",
+ "SMW_PREC_YMDT": "j. F Y, H:i:s"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/en.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/en.json
new file mode 100644
index 00000000..903eec46
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/en.json
@@ -0,0 +1,304 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": false,
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Property",
+ "SMW_NS_PROPERTY_TALK": "Property_talk",
+ "SMW_NS_CONCEPT": "Concept",
+ "SMW_NS_CONCEPT_TALK": "Concept_talk",
+ "SMW_NS_SCHEMA": "smw/schema",
+ "SMW_NS_SCHEMA_TALK": "smw/schema_talk"
+ },
+ "aliases": {
+ "Property": "SMW_NS_PROPERTY",
+ "Property_talk": "SMW_NS_PROPERTY_TALK",
+ "Concept": "SMW_NS_CONCEPT",
+ "Concept_talk": "SMW_NS_CONCEPT_TALK",
+ "smw/schema": "SMW_NS_SCHEMA",
+ "smw/schema_talk": "SMW_NS_SCHEMA_TALK"
+ }
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Page",
+ "_txt": "Text",
+ "_cod": "Code",
+ "_boo": "Boolean",
+ "_num": "Number",
+ "_geo": "Geographic coordinates",
+ "_tem": "Temperature",
+ "_dat": "Date",
+ "_ema": "Email",
+ "_uri": "URL",
+ "_anu": "Annotation URI",
+ "_tel": "Telephone number",
+ "_rec": "Record",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_keyw": "Keyword",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URL": "_uri",
+ "URI": "_uri",
+ "Page": "_wpg",
+ "Text": "_txt",
+ "Enumeration": "_txt",
+ "String": "_txt",
+ "Code": "_cod",
+ "Boolean": "_boo",
+ "Number": "_num",
+ "Float": "_num",
+ "Integer": "_num",
+ "Geographic coordinates": "_geo",
+ "Geographic coordinate": "_geo",
+ "Geographic polygon": "_gpo",
+ "Temperature": "_tem",
+ "Quantity": "_qty",
+ "Date": "_dat",
+ "Email": "_ema",
+ "E-mail": "_ema",
+ "Annotation URI": "_anu",
+ "Telephone number": "_tel",
+ "Phone number": "_tel",
+ "Record": "_rec",
+ "Monolingual text": "_mlt_rec",
+ "External identifier": "_eid",
+ "Keyword": "_keyw",
+ "Reference": "_ref_rec"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Has type",
+ "_URI": "Equivalent URI",
+ "_SUBP": "Subproperty of",
+ "_SUBC": "Subcategory of",
+ "_UNIT": "Display units",
+ "_IMPO": "Imported from",
+ "_CONV": "Corresponds to",
+ "_SERV": "Provides service",
+ "_PVAL": "Allows value",
+ "_MDAT": "Modification date",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Has improper value for",
+ "_LIST": "Has fields",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_ASKSC": "Query source",
+ "_ASKPA": "Query parameters",
+ "_ASKCO": "Query state",
+ "_MEDIA": "Media type",
+ "_MIME": "MIME type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Property description",
+ "_PVAP": "Allows pattern",
+ "_PVALI": "Allows value list",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter URI",
+ "_PPLB": "Preferred property label",
+ "_EDIP": "Is edit protected",
+ "_CHGPRO": "Change propagation",
+ "_PPGR": "Is property group",
+ "_SCHEMA_DESC": "Schema description",
+ "_SCHEMA_TAG": "Schema tag",
+ "_SCHEMA_TYPE": "Schema type",
+ "_SCHEMA_DEF": "Schema definition",
+ "_SCHEMA_LINK": "Schema link",
+ "_FORMAT_SCHEMA": "Formatter schema",
+ "_FILE_ATTCH": "File attachment",
+ "_CONT_TYPE": "Content type",
+ "_CONT_AUTHOR": "Content author",
+ "_CONT_LEN": "Content length",
+ "_CONT_LANG": "Content language",
+ "_CONT_TITLE": "Content title",
+ "_CONT_DATE": "Content date",
+ "_CONT_KEYW": "Content keyword",
+ "_TRANS": "Translation",
+ "_TRANS_SOURCE": "Translation source",
+ "_TRANS_GROUP": "Translation group"
+ },
+ "aliases": {
+ "Display unit": "_UNIT",
+ "Display units": "_UNIT",
+ "Has type": "_TYPE",
+ "Equivalent URI": "_URI",
+ "Subproperty of": "_SUBP",
+ "Subcategory of": "_SUBC",
+ "Imported from": "_IMPO",
+ "Corresponds to": "_CONV",
+ "Provides service": "_SERV",
+ "Allows value": "_PVAL",
+ "Has allows value list": "_PVALI",
+ "Modification date": "_MDAT",
+ "Creation date": "_CDAT",
+ "Is a new page": "_NEWP",
+ "Last editor is": "_LEDT",
+ "Has improper value for": "_ERRP",
+ "Has fields": "_LIST",
+ "Has subobject": "_SOBJ",
+ "Has query": "_ASK",
+ "Has query string": "_ASKST",
+ "Has query format": "_ASKFO",
+ "Has query size": "_ASKSI",
+ "Has query depth": "_ASKDE",
+ "Has query duration": "_ASKDU",
+ "Has query source": "_ASKSC",
+ "Has query parameters": "_ASKPA",
+ "Has query state": "_ASKCO",
+ "Has query status code": "_ASKCO",
+ "Has media type": "_MEDIA",
+ "Has mime type": "_MIME",
+ "Display precision of": "_PREC",
+ "Display precision": "_PREC",
+ "Language code": "_LCODE",
+ "Text": "_TEXT",
+ "Has property description": "_PDESC",
+ "Property description": "_PDESC",
+ "Has allows pattern": "_PVAP",
+ "Has display title of": "_DTITLE",
+ "Has uniqueness constraint": "_PVUC",
+ "Uniqueness constraint": "_PVUC",
+ "IsFunctionalProperty": "_PVUC",
+ "External identifier": "_PEID",
+ "External formatter URI": "_PEFU",
+ "External formatter uri": "_PEFU",
+ "External formatter URL": "_PEFU",
+ "Has preferred property label": "_PPLB",
+ "Is edit protected": "_EDIP",
+ "IsEditProtected": "_EDIP",
+ "Change propagation": "_CHGPRO",
+ "Property group": "_PPGR",
+ "Schema description": "_SCHEMA_DESC",
+ "Schema tag": "_SCHEMA_TAG",
+ "Schema type": "_SCHEMA_TYPE",
+ "Schema definition": "_SCHEMA_DEF",
+ "Schema link": "_SCHEMA_LINK",
+ "Formatter schema": "_FORMAT_SCHEMA",
+ "File attachment": "_FILE_ATTCH",
+ "Has file attachment": "_FILE_ATTCH",
+ "Has translation":"_TRANS"
+ }
+ },
+ "date": {
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y",
+ "SMW_PREC_YMDTZ": "H:i:s T, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ],
+ "months": [
+ [
+ "January",
+ "Jan"
+ ],
+ [
+ "February",
+ "Feb"
+ ],
+ [
+ "March",
+ "Mar"
+ ],
+ [
+ "April",
+ "Apr"
+ ],
+ [
+ "May",
+ "May"
+ ],
+ [
+ "June",
+ "Jun"
+ ],
+ [
+ "July",
+ "Jul"
+ ],
+ [
+ "August",
+ "Aug"
+ ],
+ [
+ "September",
+ "Sep"
+ ],
+ [
+ "October",
+ "Oct"
+ ],
+ [
+ "November",
+ "Nov"
+ ],
+ [
+ "December",
+ "Dec"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thrusday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/es.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/es.json
new file mode 100644
index 00000000..a5d0cda8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/es.json
@@ -0,0 +1,202 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Propiedad",
+ "SMW_NS_PROPERTY_TALK": "Propiedad_discusión",
+ "SMW_NS_CONCEPT": "Concepto",
+ "SMW_NS_CONCEPT_TALK": "Concepto_discusión"
+ },
+ "aliases": {
+ "Atributo": "SMW_NS_PROPERTY",
+ "Atributo_discusión": "SMW_NS_PROPERTY_TALK"
+ }
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Página",
+ "_txt": "Texto",
+ "_cod": "Código",
+ "_boo": "Booleano",
+ "_num": "Número",
+ "_geo": "Coordenadas geográficas",
+ "_tem": "Temperatura",
+ "_dat": "Fecha",
+ "_ema": "Dirección electrónica",
+ "_uri": "URL",
+ "_anu": "Anotación-URI",
+ "_tel": "Número de teléfono",
+ "_rec": "Registro",
+ "_qty": "Cantidad",
+ "_mlt_rec": "Texto monolingüe",
+ "_eid": "Identificador externo",
+ "_keyw": "Palabra clave",
+ "_ref_rec": "Referencia"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Número entero": "_num",
+ "Número con coma": "_num",
+ "Cadena de caracteres": "_txt",
+ "Enumeración": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Tiene tipo de datos",
+ "_URI": "URI equivalente",
+ "_SUBP": "Subpropiedad de",
+ "_SUBC": "Subcategoría de",
+ "_UNIT": "Unidades de medida",
+ "_IMPO": "Importado de",
+ "_CONV": "Corresponde a",
+ "_SERV": "Provee servicio",
+ "_PVAL": "Permite el valor",
+ "_MDAT": "Fecha de modificación",
+ "_CDAT": "Fecha de creación",
+ "_NEWP": "Es una página nueva",
+ "_LEDT": "Último editor es",
+ "_ERRP": "Tiene valor incorrecto para",
+ "_LIST": "Tiene campos",
+ "_SOBJ": "Tiene subobjeto",
+ "_ASK": "Tiene consulta",
+ "_ASKST": "Cadena de consulta",
+ "_ASKFO": "Formato de consulta",
+ "_ASKSI": "Tamaño de consulta",
+ "_ASKDE": "Profundidad de consulta",
+ "_ASKDU": "Duración de consulta",
+ "_ASKSC": "Fuente de consulta",
+ "_ASKPA": "Parámetros de consulta",
+ "_ASKCO": "Código de estado de consulta",
+ "_MEDIA": "Tipo Media",
+ "_MIME": "Tipo MIME",
+ "_ERRC": "Tiene error de procesamiento",
+ "_ERRT": "Tiene texto de error procesamiento",
+ "_PREC": "Mostrar precisión de",
+ "_LCODE": "Código de lenguaje",
+ "_TEXT": "Texto",
+ "_PDESC": "Tiene descripción de propiedad",
+ "_PVAP": "Permite patrón",
+ "_PVALI": "Permite lista de valores",
+ "_DTITLE": "Mostrar título de",
+ "_PVUC": "Tiene restricción de singularidad",
+ "_PEID": "Identificador externo",
+ "_PEFU": "URI del formateador externo",
+ "_PPLB": "Etiqueta de propiedad preferida",
+ "_EDIP": "Está protegida de edición",
+ "_CHGPRO": "Propagación de cambio",
+ "_PPGR": "Es un grupo de propiedad"
+ },
+ "aliases": {
+ "Es página nueva": "_NEWP",
+ "Tiene restricción de unicidad": "_PVUC",
+ "Unidad de medida": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "enero",
+ "ene"
+ ],
+ [
+ "febrero",
+ "feb"
+ ],
+ [
+ "marzo",
+ "mar"
+ ],
+ [
+ "abril",
+ "abr"
+ ],
+ [
+ "mayo",
+ "may"
+ ],
+ [
+ "junio",
+ "jun"
+ ],
+ [
+ "julio",
+ "jul"
+ ],
+ [
+ "agosto",
+ "ago"
+ ],
+ [
+ "septiembre",
+ "sep"
+ ],
+ [
+ "octubre",
+ "oct"
+ ],
+ [
+ "noviembre",
+ "nov"
+ ],
+ [
+ "diciembre",
+ "dic"
+ ]
+ ],
+ "days": [
+ [
+ "Lunes",
+ "Lun"
+ ],
+ [
+ "Martes",
+ "Mar"
+ ],
+ [
+ "Miércoles",
+ "Mie"
+ ],
+ [
+ "Jueves",
+ "Jue"
+ ],
+ [
+ "Viernes",
+ "Vie"
+ ],
+ [
+ "Sábado",
+ "Sab"
+ ],
+ [
+ "Domingo",
+ "Dom"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "M Y",
+ "SMW_PREC_YMD": "j M Y",
+ "SMW_PREC_YMDT": "H:i:s j M Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fi.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fi.json
new file mode 100644
index 00000000..f2970c39
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fi.json
@@ -0,0 +1,183 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Ominaisuus",
+ "SMW_NS_PROPERTY_TALK": "Keskustelu_ominaisuudesta",
+ "SMW_NS_CONCEPT": "Konsepti",
+ "SMW_NS_CONCEPT_TALK": "Keskustelu_konseptista"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Sivu",
+ "_txt": "Teksti",
+ "_cod": "Lähdekoodi",
+ "_boo": "Boolean",
+ "_num": "Luku",
+ "_geo": "Maantieteellinen koordinaatti",
+ "_tem": "Lämpötila",
+ "_dat": "Päiväys",
+ "_ema": "Sähköposti",
+ "_uri": "URL-osoite",
+ "_anu": "Annotation URI",
+ "_tel": "Puhelinnumero",
+ "_rec": "Tietue",
+ "_qty": "Määrä",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "Merkkijono": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "On tyypiltään",
+ "_URI": "Yhtäpitävä URI",
+ "_SUBP": "Yläominaisuus",
+ "_SUBC": "Yläluokka",
+ "_UNIT": "Tulostusyksikkö",
+ "_IMPO": "Tuotu sanastosta",
+ "_CONV": "Vastaa määrää",
+ "_SERV": "Tarjoaa palvelun",
+ "_PVAL": "Mahdollinen arvo",
+ "_MDAT": "Muokkausaika",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Sopimaton arvo kentälle",
+ "_LIST": "Koostuu kentistä",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": []
+ },
+ "date": {
+ "months": [
+ [
+ "tammikuu",
+ "tammikuu"
+ ],
+ [
+ "helmikuu",
+ "helmikuu"
+ ],
+ [
+ "maaliskuu",
+ "maaliskuu"
+ ],
+ [
+ "huhtikuu",
+ "huhtikuu"
+ ],
+ [
+ "toukokuu",
+ "toukokuu"
+ ],
+ [
+ "kesäkuu",
+ "kesäkuu"
+ ],
+ [
+ "heinäkuu",
+ "heinäkuu"
+ ],
+ [
+ "elokuu",
+ "elokuu"
+ ],
+ [
+ "syyskuu",
+ "syyskuu"
+ ],
+ [
+ "lokakuu",
+ "lokakuu"
+ ],
+ [
+ "marraskuu",
+ "marraskuu"
+ ],
+ [
+ "joulukuu",
+ "joulukuu"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fr.json
new file mode 100644
index 00000000..67cf4dd5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/fr.json
@@ -0,0 +1,189 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Attribut",
+ "SMW_NS_PROPERTY_TALK": "Discussion_attribut",
+ "SMW_NS_CONCEPT": "Concept",
+ "SMW_NS_CONCEPT_TALK": "Discussion_concept"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Page",
+ "_txt": "Texte",
+ "_cod": "Code",
+ "_boo": "Booléen",
+ "_num": "Nombre",
+ "_geo": "Coordonnées géographiques",
+ "_tem": "Température",
+ "_dat": "Date",
+ "_ema": "Adresse électronique",
+ "_uri": "URL",
+ "_anu": "Annotation-URI",
+ "_tel": "Numéro de téléphone",
+ "_rec": "Enregistrement",
+ "_qty": "Quantité",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Nombre entier": "_num",
+ "Nombre décimal": "_num",
+ "Chaîne de caractères": "_txt",
+ "Énumeration": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "A le type",
+ "_URI": "URI équivalente",
+ "_SUBP": "Sous-propriété de",
+ "_SUBC": "Sous-catégorie de",
+ "_UNIT": "Unités de mesure",
+ "_IMPO": "Importé de",
+ "_CONV": "Correspond à",
+ "_SERV": "Fournit le service",
+ "_PVAL": "Valeur possible",
+ "_MDAT": "Date de modification",
+ "_CDAT": "Date de création",
+ "_NEWP": "Est une nouvelle page",
+ "_LEDT": "Le dernier contributeur est",
+ "_ERRP": "A une valeur incorrecte pour",
+ "_LIST": "A le champ",
+ "_SOBJ": "Possède un sous-objet",
+ "_ASK": "Possède une requête",
+ "_ASKST": "Champ de requête",
+ "_ASKFO": "Format de requête",
+ "_ASKSI": "Taille de la requête",
+ "_ASKDE": "Profondeur de la requête",
+ "_ASKDU": "Durée de la requête",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Unité de mesure": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "janvier",
+ "jan"
+ ],
+ [
+ "février",
+ "fév"
+ ],
+ [
+ "mars",
+ "mar"
+ ],
+ [
+ "avril",
+ "avr"
+ ],
+ [
+ "mai",
+ "mai"
+ ],
+ [
+ "juin",
+ "jun"
+ ],
+ [
+ "juillet",
+ "jul"
+ ],
+ [
+ "août",
+ "aoû"
+ ],
+ [
+ "septembre",
+ "sep"
+ ],
+ [
+ "octobre",
+ "oct"
+ ],
+ [
+ "novembre",
+ "nov"
+ ],
+ [
+ "décembre",
+ "déc"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/he.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/he.json
new file mode 100644
index 00000000..4ad63d99
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/he.json
@@ -0,0 +1,188 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "תכונה",
+ "SMW_NS_PROPERTY_TALK": "שיחת_תכונה",
+ "SMW_NS_CONCEPT": "רעיון",
+ "SMW_NS_CONCEPT_TALK": "שיחת_רעיון"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "עמוד",
+ "_txt": "טקסט",
+ "_cod": "קוד",
+ "_boo": "נכוןל×נכון",
+ "_num": "מספר",
+ "_geo": "קורדינטות ×’×™×וגרפיות",
+ "_tem": "טמפרטורה",
+ "_dat": "ת×ריך",
+ "_ema": "דו×ל",
+ "_uri": "כתובת כללית",
+ "_anu": "מזהה יחודי לפירוש",
+ "_tel": "Telephone number",
+ "_rec": "Record",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "מזהה יחודי": "_uri",
+ "של×": "_num",
+ "נקודהצפה": "_num",
+ "מחרוזת": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "מטיפוס",
+ "_URI": "מזהה יחודי תו××",
+ "_SUBP": "רכוש כפוף ל",
+ "_SUBC": "תת קטגוריה של",
+ "_UNIT": "יחידות מידה",
+ "_IMPO": "×™×•×‘× ×ž",
+ "_CONV": "×ž×ª×•×¨×’× ×œ",
+ "_SERV": "מספק שירות",
+ "_PVAL": "ערך ×פשרי",
+ "_MDAT": "ת×ריך לשינוי",
+ "_CDAT": "ת×ריך יצירה",
+ "_NEWP": "×”×× ×”×“×£ חדש",
+ "_LEDT": "העורך ×”×חרון ",
+ "_ERRP": "יש ערך תקין בשביל",
+ "_LIST": "Has fields",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "מחרוזת הש×ילת×",
+ "_ASKFO": "פורמט הש×ילת×",
+ "_ASKSI": "גודל הש×ילת×",
+ "_ASKDE": "עומק ש×ילת×",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "יחידת הצגה": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "ינו×ר",
+ "ינו×ר"
+ ],
+ [
+ "פברו×ר",
+ "פברו×ר"
+ ],
+ [
+ "מרץ",
+ "מרץ"
+ ],
+ [
+ "×פריל",
+ "×פריל"
+ ],
+ [
+ "מ××™",
+ "מ××™"
+ ],
+ [
+ "יוני",
+ "יוני"
+ ],
+ [
+ "יולי",
+ "יולי"
+ ],
+ [
+ "×וגוסט",
+ "×וגוסט"
+ ],
+ [
+ "ספטמבר",
+ "ספטמבר"
+ ],
+ [
+ "×וקטובר",
+ "×וקטובר"
+ ],
+ [
+ "נובמבר",
+ "נובמבר"
+ ],
+ [
+ "דצמבר",
+ "דצמבר"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/hu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/hu.json
new file mode 100644
index 00000000..1f23e03b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/hu.json
@@ -0,0 +1,181 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Tulajdonság",
+ "SMW_NS_PROPERTY_TALK": "Tulajdonságvita",
+ "SMW_NS_CONCEPT": "Koncepció",
+ "SMW_NS_CONCEPT_TALK": "Koncepcióvita"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Oldal",
+ "_txt": "Szöveg",
+ "_cod": "Forráskód",
+ "_boo": "Logikai",
+ "_num": "Szám",
+ "_geo": "Földrajzi koordináta",
+ "_tem": "Hőmérséklet",
+ "_dat": "Dátum",
+ "_ema": "E-Mail",
+ "_uri": "URL",
+ "_anu": "Annotált URI",
+ "_tel": "Telefonszám",
+ "_rec": "Rekord",
+ "_qty": "Mennyiség",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Típusa",
+ "_URI": "Megegyező URL",
+ "_SUBP": "Altulajdonsága",
+ "_SUBC": "Alkategóriája",
+ "_UNIT": "Mértékegysége",
+ "_IMPO": "Importálva",
+ "_CONV": "Egyenértékű",
+ "_SERV": "Nyújtott szolgáltatása",
+ "_PVAL": "Lehetséges értéke",
+ "_MDAT": "Utolsó módosítása",
+ "_CDAT": "Létrehozva",
+ "_NEWP": "Új oldal",
+ "_LEDT": "Utolsó szerkesztője",
+ "_ERRP": "Érvénytelen értéke",
+ "_LIST": "Mezője",
+ "_SOBJ": "Alobjektuma",
+ "_ASK": "Lekérdezése",
+ "_ASKST": "Lekérdezése szövege",
+ "_ASKFO": "Lekérdezése formátuma",
+ "_ASKSI": "Lekérdezése nagysága",
+ "_ASKDE": "Lekérdezése mélysége",
+ "_ASKDU": "Lekérdezése időtartama",
+ "_MEDIA": "Médiatípusa",
+ "_MIME": "MIME-Típusa",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Adattípusa": "_TYPE"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "Január",
+ "Jan"
+ ],
+ [
+ "Február",
+ "Feb"
+ ],
+ [
+ "Március",
+ "Már"
+ ],
+ [
+ "Ãprilis",
+ "Ãpr"
+ ],
+ [
+ "Május",
+ "Máj"
+ ],
+ [
+ "Június",
+ "Jún"
+ ],
+ [
+ "Július",
+ "Júl"
+ ],
+ [
+ "Augusztus",
+ "Aug"
+ ],
+ [
+ "Szeptember",
+ "Sze"
+ ],
+ [
+ "Október",
+ "Okt"
+ ],
+ [
+ "November",
+ "Nov"
+ ],
+ [
+ "December",
+ "Dec"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_YM"
+ ],
+ [
+ "SMW_YMD"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/id.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/id.json
new file mode 100644
index 00000000..58c6bea0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/id.json
@@ -0,0 +1,187 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Properti",
+ "SMW_NS_PROPERTY_TALK": "Pembicaraan_Properti",
+ "SMW_NS_CONCEPT": "Konsep",
+ "SMW_NS_CONCEPT_TALK": "Pembicaraan_Konsep"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Halaman",
+ "_txt": "Teks",
+ "_cod": "Kode",
+ "_boo": "Boole",
+ "_num": "Angka",
+ "_geo": "Koordinat geografis",
+ "_tem": "Suhu",
+ "_dat": "Tanggal",
+ "_ema": "Surel",
+ "_uri": "URL",
+ "_anu": "URI anotasi",
+ "_tel": "Nomor telepon",
+ "_rec": "Rekaman",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Enumerasi": "_txt",
+ "Nomor telepon": "_tel"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Memiliki tipe",
+ "_URI": "URI ekuivalen",
+ "_SUBP": "Subproperti dari",
+ "_SUBC": "Subkategori dari",
+ "_UNIT": "Unit tampilan",
+ "_IMPO": "Diimpor dari",
+ "_CONV": "Berhubungan dengan",
+ "_SERV": "Memberikan layanan",
+ "_PVAL": "Mengizinkan nilai",
+ "_MDAT": "Tanggal modifikasi",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Memiliki nilai yang tidak tepat untuk",
+ "_LIST": "Memiliki bidang",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Unit tampilan": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "Januari",
+ "Jan"
+ ],
+ [
+ "Februari",
+ "Feb"
+ ],
+ [
+ "Maret",
+ "Mar"
+ ],
+ [
+ "April",
+ "Apr"
+ ],
+ [
+ "Mei",
+ "Mei"
+ ],
+ [
+ "Juni",
+ "Jun"
+ ],
+ [
+ "Juli",
+ "Jul"
+ ],
+ [
+ "Agustus",
+ "Agu"
+ ],
+ [
+ "September",
+ "Sep"
+ ],
+ [
+ "Oktober",
+ "Okt"
+ ],
+ [
+ "November",
+ "Nov"
+ ],
+ [
+ "Desember",
+ "Des"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/it.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/it.json
new file mode 100644
index 00000000..f88ec863
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/it.json
@@ -0,0 +1,191 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Proprietà",
+ "SMW_NS_PROPERTY_TALK": "Discussione proprietà",
+ "SMW_NS_CONCEPT": "Concetto",
+ "SMW_NS_CONCEPT_TALK": "Discussione concetto"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Pagina",
+ "_txt": "Testo",
+ "_cod": "Code",
+ "_boo": "Booleano",
+ "_num": "Numero",
+ "_geo": "Coordinate geografiche",
+ "_tem": "Temperatura",
+ "_dat": "Data",
+ "_ema": "Email",
+ "_uri": "URL",
+ "_anu": "Annotazione URI",
+ "_tel": "Telephone number",
+ "_rec": "Record",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Float": "_num",
+ "Integer": "_num",
+ "Intero": "_num",
+ "Enumeration": "_txt",
+ "Enumerazione": "_txt",
+ "Stringa": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Ha tipo",
+ "_URI": "URI equivalente",
+ "_SUBP": "Sottoproprietà di",
+ "_SUBC": "Subcategory of",
+ "_UNIT": "Visualizza unità",
+ "_IMPO": "Importato da",
+ "_CONV": "Corrisponde a ",
+ "_SERV": "Fornisce servizio",
+ "_PVAL": "Ammette valore",
+ "_MDAT": "Data di modifica",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Ha un valore improprio per",
+ "_LIST": "Has fields",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Display unit": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "gennaio",
+ "gen"
+ ],
+ [
+ "febbraio",
+ "feb"
+ ],
+ [
+ "marzo",
+ "mar"
+ ],
+ [
+ "aprile",
+ "apr"
+ ],
+ [
+ "maggio",
+ "mag"
+ ],
+ [
+ "giugno",
+ "giu"
+ ],
+ [
+ "luglio",
+ "lug"
+ ],
+ [
+ "agosto",
+ "ago"
+ ],
+ [
+ "settembre",
+ "set"
+ ],
+ [
+ "ottobre",
+ "ott"
+ ],
+ [
+ "novembre",
+ "nov"
+ ],
+ [
+ "dicembre",
+ "dic"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ja.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ja.json
new file mode 100644
index 00000000..94d03c94
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ja.json
@@ -0,0 +1,44 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": [],
+ "aliases": []
+ },
+ "datatype": {
+ "labels": [],
+ "aliases": []
+ },
+ "property": {
+ "labels": [],
+ "aliases": []
+ },
+ "date": {
+ "months": [],
+ "days": [],
+ "precision": {
+ "SMW_PREC_Y": "Yå¹´",
+ "SMW_PREC_YM": "Y年n月",
+ "SMW_PREC_YMD": "Y年n月j日 (D)",
+ "SMW_PREC_YMDT": "Y年n月j日 (D) H:i:s",
+ "SMW_PREC_YMDTZ": "Y年n月j日 (D) H:i:s T"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_YM",
+ "SMW_MY"
+ ],
+ [
+ "SMW_YMD",
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nb.json
new file mode 100644
index 00000000..3e036121
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nb.json
@@ -0,0 +1,201 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Egenskap",
+ "SMW_NS_PROPERTY_TALK": "Egenskap-diskusjon",
+ "SMW_NS_CONCEPT": "Konsept",
+ "SMW_NS_CONCEPT_TALK": "Konsept-diskusjon"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Side",
+ "_txt": "Tekst",
+ "_cod": "Kode",
+ "_boo": "Boolsk",
+ "_num": "Tall",
+ "_geo": "Geografisk koordinat",
+ "_tem": "Temperatur",
+ "_dat": "Dato",
+ "_ema": "E-post",
+ "_uri": "URL",
+ "_anu": "URI-merknad",
+ "_tel": "Telefonnummer",
+ "_rec": "Post",
+ "_qty": "Størrelse",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "Lang tekst": "_txt",
+ "Kort tekst": "_txt",
+ "Streng": "_txt",
+ "Linje": "_txt",
+ "Hellall": "_num",
+ "Desimaltall": "_num",
+ "Liste": "_txt",
+ "Kildekode": "_cod",
+ "Koordinat": "_geo",
+ "Epost": "_ema",
+ "URI": "_uri",
+ "Nettadresse": "_uri",
+ "MÃ¥l": "_qty",
+ "Record": "_rec"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Har type",
+ "_URI": "Ekvivalent URI",
+ "_SUBP": "Underegenskap av",
+ "_SUBC": "Underkategori av",
+ "_UNIT": "Visningsenhet",
+ "_IMPO": "Importert fra",
+ "_CONV": "Svarer til",
+ "_SERV": "Tilbyr tjeneste",
+ "_PVAL": "Tillater verdi",
+ "_MDAT": "Endringsdato",
+ "_CDAT": "Opprettelsesdato",
+ "_NEWP": "Er en ny side",
+ "_LEDT": "Siste redaktør er",
+ "_ERRP": "Feilaktig verdi for",
+ "_LIST": "Har feltene",
+ "_SOBJ": "Har underobjekt",
+ "_ASK": "Har spørring",
+ "_ASKST": "Spørringsstreng",
+ "_ASKFO": "Spørringsformat",
+ "_ASKSI": "Spørringsstørrelse",
+ "_ASKDE": "Spørringsdybde",
+ "_ASKDU": "Spørringsvarighet",
+ "_MEDIA": "Mediatype",
+ "_MIME": "MIME-type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Type": "_TYPE",
+ "Enhet": "_UNIT",
+ "Synonym URI": "_URI",
+ "Synonym adresse": "_URI"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "januar",
+ "jan."
+ ],
+ [
+ "februar",
+ "feb."
+ ],
+ [
+ "mars",
+ "mars"
+ ],
+ [
+ "april",
+ "april"
+ ],
+ [
+ "mai",
+ "mai"
+ ],
+ [
+ "juni",
+ "juni"
+ ],
+ [
+ "juli",
+ "juli"
+ ],
+ [
+ "august",
+ "aug."
+ ],
+ [
+ "september",
+ "sep."
+ ],
+ [
+ "oktober",
+ "okt."
+ ],
+ [
+ "november",
+ "nov."
+ ],
+ [
+ "desember",
+ "des."
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nl.json
new file mode 100644
index 00000000..4fdb3579
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/nl.json
@@ -0,0 +1,193 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Eigenschap",
+ "SMW_NS_PROPERTY_TALK": "Overleg_eigenschap",
+ "SMW_NS_CONCEPT": "Concept",
+ "SMW_NS_CONCEPT_TALK": "Overleg_concept"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Pagina",
+ "_txt": "Tekst",
+ "_cod": "Code",
+ "_boo": "Booleaans",
+ "_num": "Getal",
+ "_geo": "Geografische coördinaat",
+ "_tem": "Temperatuur",
+ "_dat": "Datum",
+ "_ema": "E-mail",
+ "_uri": "URL",
+ "_anu": "Annotatie-URI",
+ "_tel": "Telefoonnummer",
+ "_rec": "Record",
+ "_qty": "Hoeveelheid",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Drijvende komma": "_num",
+ "Integer": "_num",
+ "Opsomming": "_txt",
+ "String": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Heeft type",
+ "_URI": "Equivalent URI",
+ "_SUBP": "Subeigenschap van",
+ "_SUBC": "Subcategorie van",
+ "_UNIT": "Weergaveeenheden",
+ "_IMPO": "Geïmporteerd uit",
+ "_CONV": "Komt overeen met",
+ "_SERV": "Verleent dienst",
+ "_PVAL": "Geldige waarde",
+ "_MDAT": "Wijzigingsdatum",
+ "_CDAT": "Creatiedatum",
+ "_NEWP": "Is een nieuwe pagina",
+ "_LEDT": "Laatste redacteur is",
+ "_ERRP": "Heeft ongeldige waarde voor",
+ "_LIST": "Heeft velden",
+ "_SOBJ": "Heeft subobject",
+ "_ASK": "Heeft bevraging",
+ "_ASKST": "Zoekstring",
+ "_ASKFO": "Zoekopdracht-opmaak",
+ "_ASKSI": "Zoekopdracht-omvang",
+ "_ASKDE": "Zoekdiepte",
+ "_ASKDU": "Zoekduur",
+ "_MEDIA": "Heeft Mediatype",
+ "_MIME": "Heeft MIME-type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Weergave-eenheid": "_UNIT",
+ "Bevragingsstring": "_ASKST",
+ "Bevragingsopmaak": "_ASKFO",
+ "Bevragingsgrootte": "_ASKSI",
+ "Bevragingsdiepgang": "_ASKDE"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "januari",
+ "jan"
+ ],
+ [
+ "februari",
+ "feb"
+ ],
+ [
+ "maart",
+ "mar"
+ ],
+ [
+ "april",
+ "apr"
+ ],
+ [
+ "mei",
+ "mei"
+ ],
+ [
+ "juni",
+ "jun"
+ ],
+ [
+ "juli",
+ "jul"
+ ],
+ [
+ "augustus",
+ "aug"
+ ],
+ [
+ "september",
+ "sep"
+ ],
+ [
+ "oktober",
+ "okt"
+ ],
+ [
+ "november",
+ "nov"
+ ],
+ [
+ "december",
+ "dec"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pl.json
new file mode 100644
index 00000000..bfd71012
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pl.json
@@ -0,0 +1,189 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Atrybut",
+ "SMW_NS_PROPERTY_TALK": "Dyskusja_atrybutu",
+ "SMW_NS_CONCEPT": "Pojęcie",
+ "SMW_NS_CONCEPT_TALK": "Dyskusja pojęcia"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Strona",
+ "_txt": "Tekst",
+ "_cod": "Kod",
+ "_boo": "Wartość logiczna",
+ "_num": "Liczba",
+ "_geo": "Współrzędne geograficzne",
+ "_tem": "Temperatura",
+ "_dat": "Data",
+ "_ema": "Email",
+ "_uri": "URL",
+ "_anu": "URI adnotacji",
+ "_tel": "Telephone number",
+ "_rec": "Record",
+ "_qty": "Quantity",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Liczba zmiennoprzecinkowa": "_num",
+ "Liczba całkowita": "_num",
+ "Wyliczenie": "_txt",
+ "ÅaÅ„cuch znaków": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Ma typ",
+ "_URI": "Równoważne URI",
+ "_SUBP": "Jest podwłasnością",
+ "_SUBC": "Subcategory of",
+ "_UNIT": "Wyświetlane jednostki",
+ "_IMPO": "Zaimportowane z",
+ "_CONV": "Odpowiada",
+ "_SERV": "Zapewnia usługę",
+ "_PVAL": "Dopuszcza wartość",
+ "_MDAT": "Modification date",
+ "_CDAT": "Creation date",
+ "_NEWP": "Is a new page",
+ "_LEDT": "Last editor is",
+ "_ERRP": "Has improper value for",
+ "_LIST": "Has fields",
+ "_SOBJ": "Has subobject",
+ "_ASK": "Has query",
+ "_ASKST": "Query string",
+ "_ASKFO": "Query format",
+ "_ASKSI": "Query size",
+ "_ASKDE": "Query depth",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Wyświetlana jednostka": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "styczeń",
+ "sty"
+ ],
+ [
+ "luty",
+ "lut"
+ ],
+ [
+ "marsz",
+ "mar"
+ ],
+ [
+ "kwiecień",
+ "kwi"
+ ],
+ [
+ "maj",
+ "maj"
+ ],
+ [
+ "czerwiec",
+ "cze"
+ ],
+ [
+ "lipiec",
+ "lip"
+ ],
+ [
+ "sierpień",
+ "sie"
+ ],
+ [
+ "wrzesień",
+ "wrz"
+ ],
+ [
+ "październik",
+ "paź"
+ ],
+ [
+ "listopad",
+ "lis"
+ ],
+ [
+ "grudzień",
+ "gru"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pt.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pt.json
new file mode 100644
index 00000000..8160584e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/pt.json
@@ -0,0 +1,209 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Propriedade",
+ "SMW_NS_PROPERTY_TALK": "Discussão_propriedade",
+ "SMW_NS_CONCEPT": "Conceito",
+ "SMW_NS_CONCEPT_TALK": "Discussão_conceito"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Página",
+ "_txt": "Texto",
+ "_cod": "Código",
+ "_boo": "Booleano",
+ "_num": "Número",
+ "_geo": "Coordenadas geográficas",
+ "_tem": "Temperatura",
+ "_dat": "Data",
+ "_ema": "Email",
+ "_uri": "URL",
+ "_anu": "URI",
+ "_tel": "Número de telefone",
+ "_rec": "Registro",
+ "_qty": "Quantidade",
+ "_mlt_rec": "Texto monolingue",
+ "_eid": "Identificador externo",
+ "_ref_rec": "Referência"
+ },
+ "aliases": {
+ "Entidade": "_wpg",
+ "Recurso": "_wpg",
+ "Ponto flutuante": "_num",
+ "Inteiro": "_num",
+ "Coordenadas": "_geo",
+ "E-mail": "_ema",
+ "Anotação de URI": "_uri",
+ "Telefone": "_tel",
+ "URI": "_uri",
+ "Número inteiro": "_num",
+ "Folga": "_num",
+ "Variável Booléen": "_boo",
+ "Valor booleano": "_boo",
+ "Enumeração": "_txt",
+ "Cadeia": "_txt",
+ "ID externo": "_eid"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Possui tipo",
+ "_URI": "URI equivalente",
+ "_SUBP": "Subpropriedade de",
+ "_SUBC": "Subcategoria de",
+ "_UNIT": "Unidades de exibição",
+ "_IMPO": "Importada de",
+ "_CONV": "Corresponde a",
+ "_SERV": "Fornece serviço",
+ "_PVAL": "Permite valor",
+ "_MDAT": "Data de modificação",
+ "_CDAT": "Data de criação",
+ "_NEWP": "É uma nova página",
+ "_LEDT": "Último editor",
+ "_ERRP": "Possui valor impróprio para",
+ "_LIST": "Possui campos",
+ "_SOBJ": "Possui subobjeto",
+ "_ASK": "Possui consulta",
+ "_ASKST": "Texto da consulta",
+ "_ASKFO": "Formato da consulta",
+ "_ASKSI": "Tamanho da consulta",
+ "_ASKDE": "Profundidade da consulta",
+ "_ASKDU": "Duração da consulta",
+ "_MEDIA": "Tipo de mídia",
+ "_MIME": "Tipo MIME",
+ "_ERRC": "Possui erro de processamento",
+ "_ERRT": "Possui texto de erro de processamento",
+ "_PREC": "Exibe a precisão de",
+ "_LCODE": "Código de idioma",
+ "_TEXT": "Texto",
+ "_PDESC": "Possui a descrição da propriedade",
+ "_PVAP": "Permite o padrão",
+ "_DTITLE": "Possui título de exibição",
+ "_PVUC": "Possui restrição de unicidade",
+ "_PEID": "Identificador externo",
+ "_PEFU": "Formato de URI externo",
+ "_PPLB": "Rótulo preferido para a propriedade",
+ "_ASKSC": "Fonte de consulta"
+ },
+ "aliases": {
+ "Exibe unidade": "_UNIT",
+ "Exibe unidades": "_UNIT",
+ "É do tipo": "_TYPE",
+ "Possui URI equivalente": "_URI",
+ "Importado de": "_IMPO",
+ "Corresponde ao": "_CONV",
+ "Tem o tipo": "_TYPE",
+ "Unidade de amostra": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "Janeiro",
+ "Jan"
+ ],
+ [
+ "Fevereiro",
+ "Fev"
+ ],
+ [
+ "Março",
+ "Mar"
+ ],
+ [
+ "Abril",
+ "Abr"
+ ],
+ [
+ "Maio",
+ "Mai"
+ ],
+ [
+ "Junho",
+ "Jun"
+ ],
+ [
+ "Julho",
+ "Jul"
+ ],
+ [
+ "Agosto",
+ "Ago"
+ ],
+ [
+ "Setembro",
+ "Set"
+ ],
+ [
+ "Outubro",
+ "Out"
+ ],
+ [
+ "Novembro",
+ "Nov"
+ ],
+ [
+ "Dezembro",
+ "Dez"
+ ]
+ ],
+ "days": [
+ [
+ "Segunda-feira",
+ "Seg"
+ ],
+ [
+ "Terça-feira",
+ "Ter"
+ ],
+ [
+ "Quarta-feira",
+ "Qua"
+ ],
+ [
+ "Quinta-feira",
+ "Qui"
+ ],
+ [
+ "Sexta-feira",
+ "Sex"
+ ],
+ [
+ "Sábado",
+ "Sáb"
+ ],
+ [
+ "Domingo",
+ "Dom"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y",
+ "SMW_PREC_YMDTZ": "H:i:s T, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ru.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ru.json
new file mode 100644
index 00000000..34c2f98d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/ru.json
@@ -0,0 +1,192 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "СвойÑтво",
+ "SMW_NS_PROPERTY_TALK": "ОбÑуждение_ÑвойÑтва",
+ "SMW_NS_CONCEPT": "КонцепциÑ",
+ "SMW_NS_CONCEPT_TALK": "ОбÑуждение_концепции"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Страница",
+ "_txt": "ТекÑÑ‚",
+ "_cod": "ИÑходный код",
+ "_boo": "Булево",
+ "_num": "ЧиÑло",
+ "_geo": "ГеографичеÑкие координаты",
+ "_tem": "Температура",
+ "_dat": "Дата",
+ "_ema": "Почта",
+ "_uri": "URL",
+ "_anu": "URI аннотации",
+ "_tel": "Ðомер телефона",
+ "_rec": "ЗапиÑÑŒ",
+ "_qty": "КоличеÑтво",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Целое": "_num",
+ "ДеÑÑтичное": "_num",
+ "Плавающее": "_num",
+ "ПеречиÑление": "_txt",
+ "Строка": "_txt",
+ "Телефон": "_tel"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Имеет тип",
+ "_URI": "Эквивалентный URI",
+ "_SUBP": "Подчиненное ÑвойÑтву",
+ "_SUBC": "Ð’Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ",
+ "_UNIT": "Отображаемые единицы",
+ "_IMPO": "Импортировано из",
+ "_CONV": "ОтноÑитÑÑ Ðº",
+ "_SERV": "ПредоÑтавлÑет ÑервиÑ",
+ "_PVAL": "ДопуÑтимое значение",
+ "_MDAT": "Дата поÑледней правки",
+ "_CDAT": "Дата ÑозданиÑ",
+ "_NEWP": "ÐÐ¾Ð²Ð°Ñ Ñтраница",
+ "_LEDT": "ПоÑледний редактор",
+ "_ERRP": "Содержит некорректное значение",
+ "_LIST": "Имеет полÑ",
+ "_SOBJ": "Содержит Ñубобъект",
+ "_ASK": "Содержит запроÑ",
+ "_ASKST": "Строка запроÑа",
+ "_ASKFO": "Формат запроÑа",
+ "_ASKSI": "Размер запроÑа",
+ "_ASKDE": "Глубина запроÑа",
+ "_ASKDU": "ДлительноÑÑ‚ÑŒ запроÑа",
+ "_MEDIA": "Тип медиа",
+ "_MIME": "MIME-тип",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Тип данных": "_TYPE",
+ "ÐžÑ‚Ð¾Ð±Ñ€Ð°Ð¶Ð°ÐµÐ¼Ð°Ñ ÐµÐ´Ð¸Ð½Ð¸Ñ†Ð°": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "ÑнварÑ",
+ "Ñнв"
+ ],
+ [
+ "февралÑ",
+ "фев"
+ ],
+ [
+ "марта",
+ "мар"
+ ],
+ [
+ "апрелÑ",
+ "апр"
+ ],
+ [
+ "маÑ",
+ "маÑ"
+ ],
+ [
+ "июнÑ",
+ "июн"
+ ],
+ [
+ "июлÑ",
+ "июл"
+ ],
+ [
+ "авгуÑта",
+ "авг"
+ ],
+ [
+ "ÑентÑбрÑ",
+ "Ñен"
+ ],
+ [
+ "октÑбрÑ",
+ "окт"
+ ],
+ [
+ "ноÑбрÑ",
+ "ноÑ"
+ ],
+ [
+ "декабрÑ",
+ "дек"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/sk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/sk.json
new file mode 100644
index 00000000..bd9a2525
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/sk.json
@@ -0,0 +1,188 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "Atribút",
+ "SMW_NS_PROPERTY_TALK": "Diskusia o atribúte",
+ "SMW_NS_CONCEPT": "Concept",
+ "SMW_NS_CONCEPT_TALK": "Concept_talk"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "Stránka",
+ "_txt": "Text",
+ "_cod": "Kód",
+ "_boo": "Pravdivostná hodnota",
+ "_num": "Číslo",
+ "_geo": "Zemepisné súradnice",
+ "_tem": "Teplota",
+ "_dat": "Dátum",
+ "_ema": "Email",
+ "_uri": "URL",
+ "_anu": "URI anotácie",
+ "_tel": "Telefónne Äíslo",
+ "_rec": "Record",
+ "_qty": "Rozmer",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "URI": "_uri",
+ "Celé Äíslo": "_num",
+ "Desatinné Äíslo": "_num",
+ "Reťazec": "_txt"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "Má typ",
+ "_URI": "Ekvivalent URI",
+ "_SUBP": "Je podatribútom",
+ "_SUBC": "Je podkategóriou",
+ "_UNIT": "Zobrazovacie jednotky",
+ "_IMPO": "Importovaný z",
+ "_CONV": "Zodpovedá",
+ "_SERV": "Poskytuje službu",
+ "_PVAL": "Povolená hodnota",
+ "_MDAT": "Dátum zmeny",
+ "_CDAT": "Dátum vytvorenia",
+ "_NEWP": "Nová stránka",
+ "_LEDT": "Posledný editor",
+ "_ERRP": "Má nesprávnu hodnotu pre",
+ "_LIST": "Has fields",
+ "_SOBJ": "Má podobjekt",
+ "_ASK": "Má požiadavku",
+ "_ASKST": "Reťazec požiadavky",
+ "_ASKFO": "Formát požiadavky",
+ "_ASKSI": "Veľkosť požiadavky",
+ "_ASKDE": "Hĺbka požiadavky",
+ "_ASKDU": "Query duration",
+ "_MEDIA": "Media type",
+ "_MIME": "Mime type",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "Zobrazovacia jednotka": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "January",
+ "January"
+ ],
+ [
+ "February",
+ "February"
+ ],
+ [
+ "March",
+ "March"
+ ],
+ [
+ "April",
+ "April"
+ ],
+ [
+ "May",
+ "May"
+ ],
+ [
+ "June",
+ "June"
+ ],
+ [
+ "July",
+ "July"
+ ],
+ [
+ "August",
+ "August"
+ ],
+ [
+ "September",
+ "September"
+ ],
+ [
+ "October",
+ "October"
+ ],
+ [
+ "November",
+ "November"
+ ],
+ [
+ "December",
+ "December"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Y",
+ "SMW_PREC_YM": "F Y",
+ "SMW_PREC_YMD": "F j, Y",
+ "SMW_PREC_YMDT": "H:i:s, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_DMY",
+ "SMW_MDY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-cn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-cn.json
new file mode 100644
index 00000000..f29860c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-cn.json
@@ -0,0 +1,192 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "属性",
+ "SMW_NS_PROPERTY_TALK": "属性讨论",
+ "SMW_NS_CONCEPT": "概念",
+ "SMW_NS_CONCEPT_TALK": "概念讨论"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "页é¢åž‹",
+ "_txt": "文本型",
+ "_cod": "代ç åž‹",
+ "_boo": "布尔型",
+ "_num": "数值型",
+ "_geo": "地ç†å标型",
+ "_tem": "温度型",
+ "_dat": "日期型",
+ "_ema": "电å­é‚®ä»¶åœ°å€åž‹",
+ "_uri": "URLåž‹",
+ "_anu": "注释URI型",
+ "_tel": "电è¯å·ç åž‹",
+ "_rec": "记录型",
+ "_qty": "æ•°é‡åž‹",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "浮点型": "_num",
+ "æ•´æ•°åž‹": "_num",
+ "枚举型": "_txt",
+ "字符串型": "_txt",
+ "Float": "_num",
+ "Integer": "_num",
+ "Enumeration": "_txt",
+ "URI": "_uri"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "具有类型",
+ "_URI": "等价URI",
+ "_SUBP": "是……的å­å±žæ€§",
+ "_SUBC": "是……的å­åˆ†ç±»",
+ "_UNIT": "显示å•ä½",
+ "_IMPO": "导入自",
+ "_CONV": "对应于",
+ "_SERV": "æä¾›æœåŠ¡",
+ "_PVAL": "å…许å–值",
+ "_MDAT": "修改日期",
+ "_CDAT": "创建日期",
+ "_NEWP": "是一个新页é¢",
+ "_LEDT": "最åŽç¼–者为",
+ "_ERRP": "具有……的ä¸å½“å–值",
+ "_LIST": "具有字段",
+ "_SOBJ": "具有å­å¯¹è±¡",
+ "_ASK": "具有查询",
+ "_ASKST": "查询字符串",
+ "_ASKFO": "查询格å¼",
+ "_ASKSI": "查询大å°",
+ "_ASKDE": "查询深度",
+ "_ASKDU": "查询æŒç»­æ—¶é—´",
+ "_MEDIA": "媒体类型",
+ "_MIME": "MIME类型",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "显示计é‡å•ä½": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "1月",
+ "1月"
+ ],
+ [
+ "2月",
+ "2月"
+ ],
+ [
+ "3月",
+ "3月"
+ ],
+ [
+ "4月",
+ "4月"
+ ],
+ [
+ "5月",
+ "5月"
+ ],
+ [
+ "6月",
+ "6月"
+ ],
+ [
+ "7月",
+ "7月"
+ ],
+ [
+ "8月",
+ "8月"
+ ],
+ [
+ "9月",
+ "9月"
+ ],
+ [
+ "10月",
+ "10月"
+ ],
+ [
+ "11月",
+ "11月"
+ ],
+ [
+ "12月",
+ "12月"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Yå¹´",
+ "SMW_PREC_YM": "Y年n月",
+ "SMW_PREC_YMD": "Y年n月j日 (D)",
+ "SMW_PREC_YMDT": "Y年n月j日 (D) H:i:s"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hans.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hans.json
new file mode 100644
index 00000000..2dd06879
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hans.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "zh-cn"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hant.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hant.json
new file mode 100644
index 00000000..2dd06879
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-hant.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "zh-cn"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-tw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-tw.json
new file mode 100644
index 00000000..d567a185
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh-tw.json
@@ -0,0 +1,192 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "en",
+ "namespace": {
+ "labels": {
+ "SMW_NS_PROPERTY": "屬性",
+ "SMW_NS_PROPERTY_TALK": "屬性討論",
+ "SMW_NS_CONCEPT": "概念",
+ "SMW_NS_CONCEPT_TALK": "概念討論"
+ },
+ "aliases": []
+ },
+ "datatype": {
+ "labels": {
+ "_wpg": "é é¢åž‹",
+ "_txt": "文字型",
+ "_cod": "代碼型",
+ "_boo": "布爾型",
+ "_num": "數值型",
+ "_geo": "地ç†å標型",
+ "_tem": "溫度型",
+ "_dat": "日期型",
+ "_ema": "é›»å­éƒµä»¶åœ°å€åž‹",
+ "_uri": "URLåž‹",
+ "_anu": "注釋URI型",
+ "_tel": "電話號碼型",
+ "_rec": "記錄型",
+ "_qty": "數é‡åž‹",
+ "_mlt_rec": "Monolingual text",
+ "_eid": "External identifier",
+ "_ref_rec": "Reference"
+ },
+ "aliases": {
+ "浮點型": "_num",
+ "整數型": "_num",
+ "枚舉型": "_txt",
+ "字串型": "_txt",
+ "Float": "_num",
+ "Integer": "_num",
+ "Enumeration": "_txt",
+ "URI": "_uri"
+ }
+ },
+ "property": {
+ "labels": {
+ "_TYPE": "具有類型",
+ "_URI": "等價URI",
+ "_SUBP": "是……的å­å±¬æ€§",
+ "_SUBC": "是……的å­åˆ†é¡ž",
+ "_UNIT": "顯示單ä½",
+ "_IMPO": "導入自",
+ "_CONV": "å°æ‡‰æ–¼",
+ "_SERV": "æä¾›æœå‹™",
+ "_PVAL": "å…許å–值",
+ "_MDAT": "修改日期",
+ "_CDAT": "創建日期",
+ "_NEWP": "是一個新é é¢",
+ "_LEDT": "最後編者為",
+ "_ERRP": "具有……的ä¸ç•¶å–值",
+ "_LIST": "具有欄ä½",
+ "_SOBJ": "具有å­å°è±¡",
+ "_ASK": "具有查詢",
+ "_ASKST": "查詢字串",
+ "_ASKFO": "查詢格å¼",
+ "_ASKSI": "查詢大å°",
+ "_ASKDE": "查詢深度",
+ "_ASKDU": "查詢æŒçºŒæ™‚é–“",
+ "_MEDIA": "媒體類型",
+ "_MIME": "MIMEé¡žåž‹",
+ "_ERRC": "Has processing error",
+ "_ERRT": "Has processing error text",
+ "_PREC": "Display precision of",
+ "_LCODE": "Language code",
+ "_TEXT": "Text",
+ "_PDESC": "Has property description",
+ "_PVAP": "Allows pattern",
+ "_DTITLE": "Display title of",
+ "_PVUC": "Has uniqueness constraint",
+ "_PEID": "External identifier",
+ "_PEFU": "External formatter uri",
+ "_ASKSC": "Query source"
+ },
+ "aliases": {
+ "顯示計é‡å–®ä½": "_UNIT"
+ }
+ },
+ "date": {
+ "months": [
+ [
+ "1月",
+ "1月"
+ ],
+ [
+ "2月",
+ "2月"
+ ],
+ [
+ "3月",
+ "3月"
+ ],
+ [
+ "4月",
+ "4月"
+ ],
+ [
+ "5月",
+ "5月"
+ ],
+ [
+ "6月",
+ "6月"
+ ],
+ [
+ "7月",
+ "7月"
+ ],
+ [
+ "8月",
+ "8月"
+ ],
+ [
+ "9月",
+ "9月"
+ ],
+ [
+ "10月",
+ "10月"
+ ],
+ [
+ "11月",
+ "11月"
+ ],
+ [
+ "12月",
+ "12月"
+ ]
+ ],
+ "days": [
+ [
+ "Monday",
+ "Mon"
+ ],
+ [
+ "Tuesday",
+ "Tue"
+ ],
+ [
+ "Wednesday",
+ "Wed"
+ ],
+ [
+ "Thursday",
+ "Thu"
+ ],
+ [
+ "Friday",
+ "Fri"
+ ],
+ [
+ "Saturday",
+ "Sat"
+ ],
+ [
+ "Sunday",
+ "Sun"
+ ]
+ ],
+ "precision": {
+ "SMW_PREC_Y": "Yå¹´",
+ "SMW_PREC_YM": "Y年n月",
+ "SMW_PREC_YMD": "Y年n月j日 (D)",
+ "SMW_PREC_YMDT": "Y年n月j日 (D) H:i:s"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ],
+ [
+ "SMW_MY",
+ "SMW_YM"
+ ],
+ [
+ "SMW_MDY",
+ "SMW_DMY",
+ "SMW_YMD",
+ "SMW_YDM"
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh.json b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh.json
new file mode 100644
index 00000000..2312a2f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/extra/zh.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "isLanguageRedirect": true,
+ "fallback_language": "zh-cn"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fa.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fa.json
new file mode 100644
index 00000000..ecf72ec0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fa.json
@@ -0,0 +1,391 @@
+{
+ "@metadata": {
+ "authors": [
+ "Armin1392",
+ "Ebraminio",
+ "Huji",
+ "Mjbmr",
+ "Momeni",
+ "Reza1615",
+ "ZxxZxxZ",
+ "Alirezaaa",
+ "Mahdy Saffar",
+ "AzorAhai",
+ "DEXi",
+ "Ommmmid",
+ "Obzord",
+ "Alifakoor",
+ "Physicsch"
+ ]
+ },
+ "smw-desc": "ویکی خود را بیشترقابل دسترس کنید- برای ماشین‌ها ''و'' انسان‌ها ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw_viewasrdf": "خوراک RDF",
+ "smw_finallistconjunct": "، و",
+ "smw-factbox-head": "...بیشتر در مورد \"$1\"",
+ "smw_isspecprop": "این خصوصیت یک خصوصیت ویژه در این ویکی است.",
+ "smw-concept-cache-header": "استÙادهٔ حاÙظهٔ پنهان",
+ "smw-concept-no-cache": "هیچ حاÙظهٔ نهانی دردسترس نیست.",
+ "smw_concept_description": "توصی٠مÙهوم \"$1\"",
+ "smw_no_concept_namespace": "Ù…Ùاهیم Ùقط می‌توانند در صÙحه‌های داخل Ùضای نام Ù…Ùهوم: تعری٠شوند.",
+ "smw_multiple_concepts": "هر صÙحه Ù…Ùهوم Ùقط می‌تواند شامل یک تعری٠مÙهوم باشد.",
+ "smw_concept_cache_miss": "Ù…Ùهوم \"$1\" نمی‌تواند در حال حاضر استÙاده شود، زیرا تنظیمات ویکی نیاز به محاسبه آن به‌صورت برون‌خط دارد.\nاگر این مشکل پس از مدتی از بین نرÙت، از مدیر سایت بخواهید تا این Ù…Ùهوم را در دسترس قرار دهد.",
+ "smw_noinvannot": "مقادیر نمی‌توانند برای خواص معکوس گماشته شوند.",
+ "version-semantic": "گستره‌های معنایی",
+ "smw_baduri": "یوآر‌ال‌های Ùرم \"$1\" مجاز نیستند.",
+ "smw_printername_count": "شمردن نتایج",
+ "smw_printername_csv": "صادرات سی‌اس‌وی",
+ "smw_printername_dsv": "صادرات دی‌اس‌وی",
+ "smw_printername_debug": "اشکال زدایی سوال (برای کارشناسان)",
+ "smw_printername_embedded": "قرار گرÙته شدن محتویات صÙحه",
+ "smw_printername_json": "برون‌بری در قالب JSON",
+ "smw_printername_list": "Ùهرست",
+ "smw_printername_ol": "شمارش",
+ "smw_printername_ul": "جزء به جزء نوشتن",
+ "smw_printername_table": "جدول",
+ "smw_printername_broadtable": "جدول گسترده",
+ "smw_printername_template": "الگو",
+ "smw_printername_rdf": "برون‌بری در قالب RDF",
+ "smw_printername_category": "رده",
+ "validator-type-class-SMWParamSource": "متن",
+ "smw-paramdesc-limit": "حداکثر تعداد نتایج برای بازگشت",
+ "smw-paramdesc-offset": "تعدیل اولین نتیجه",
+ "smw-paramdesc-headers": "نمایش نام‌های سرÙصل‌ها/ خاصیت",
+ "smw-paramdesc-mainlabel": "جدول برای دادن نام صÙحهٔ اصلی",
+ "smw-paramdesc-link": "نمایش مقادیر به عنوان پیوندها",
+ "smw-paramdesc-intro": "متن برای نمایش قبل از نتایج پرس‌و‌جو، اگر وجود داشته باشند",
+ "smw-paramdesc-outro": "متن برای نمایش پس از نتایج پرس‌و‌جو، اگر وجود داشته باشد",
+ "smw-paramdesc-default": "متن برای نمایش اگر هیچ نتایج پرس‌وجویی وجود نداشته باشد",
+ "smw-paramdesc-sep": "جدا کننده میان نتایج",
+ "smw-paramdesc-showsep": "نمایش جدا کننده در بالای پوشه سی‌اس‌وی (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "به جای نمایش همه مقادیر، میزان وقوع آنها را حساب کنید،این‌ها را نشان دهید.",
+ "smw-paramdesc-distributionsort": "نوع توزیع مقدار توسط حساب میزان وقوع.",
+ "smw-paramdesc-distributionlimit": "محدود کردن توزیع مقدار به حساب Ùقط برخی از مقادیر.",
+ "smw-paramdesc-template": "نام یک الگو با آنی که چاپ‌ها را نمایش می‌دهد",
+ "smw-paramdesc-columns": "تعداد ستون‌هایی که نتایج در آن‌ها نمایش می‌یابند",
+ "smw-paramdesc-userparam": "اگر یک الگو استÙاده شده‌است، یک مقدار در هر تماس الگو عبور کرده",
+ "smw-paramdesc-introtemplate": "نام الگو برای نمایش قبل از نتایج پرس‌و‌جو، اگر وجود داشته باشد",
+ "smw-paramdesc-outrotemplate": "نام الگو برای نمایش پس از نتایج پرس‌و‌جو، اگر وجود داشته باشد",
+ "smw-paramdesc-embedformat": "برچسب های اچ‌تی‌ام‌ال برای تعری٠سرÙصل‌ها مورد استÙاده قرار می‌گیرند",
+ "smw-paramdesc-embedonly": "هیچ سرÙصلی نمایش داده نشد",
+ "smw-paramdesc-table-class": "کلاس سی‌اس‌اس اضاÙÛŒ برای تنظیم جدول",
+ "smw-paramdesc-table-transpose": "عناوین جدول را عمودی Ùˆ نتایج را اÙÙ‚ÛŒ نشان بده",
+ "smw-paramdesc-rdfsyntax": "نحو آر‌دی‌ا٠مورد استÙاده قرار می‌گیرد",
+ "smw-paramdesc-csv-sep": "تعری٠جدا کننده ای برای ستونها",
+ "smw-paramdesc-csv-valuesep": "جدا کننده مقادیر را مشخص می‌کند",
+ "smw-paramdesc-dsv-separator": "جدا کننده برای استÙاده",
+ "smw-paramdesc-dsv-filename": "نام برای پوشهٔ دی‌اس‌وی",
+ "smw-paramdesc-filename": "نام برای پوشهٔ خروجی",
+ "smw-smwdoc-description": "نمایش جدول همه پارامترهایی Ú©Ù‡ می‌توانند برای Ùرمت نتیجه مشخص شده همراه با مقادیر پیش‌Ùرض Ùˆ توصیÙات، مورد استÙاده قرار گیرد.",
+ "smw-smwdoc-par-format": "Ùرمت نتیجه برای نشان دادن مستندات پارامتر.",
+ "smw-smwdoc-par-parameters": "پارامترهایی برای نمایش دادن. \"مشخص\" برای آن اÙزوده شده‌ها توسط Ùرمت، \"اساس\" برای آن متغییرها در همه Ùرمت‌ها، Ùˆ \"همه\" برای هر دو.",
+ "smw-paramdesc-sort": "خاصیت برای نوع سوال توسط",
+ "smw-paramdesc-order": "منظور از نوع پرس‌و‌جو",
+ "smw-paramdesc-searchlabel": "متن برای ادامه جستجو",
+ "smw-paramdesc-named_args": "نام بحث‌های عبور داده‌ شده به الگو",
+ "smw-paramdesc-export": "گزینهٔ صادرات",
+ "smw-paramdesc-prettyprint": "خروجی pretty-print Ú©Ù‡ تورÙتگی‌های اضاÙÛŒ Ùˆ خط.Ø· تازه را نمایش می‌دهد",
+ "smw-paramdesc-json-type": "نوع سریالی‌کردن داده‌ها را مشخص می‌کند",
+ "smw-paramdesc-source": "منبع سوال متناوب",
+ "smw-paramdesc-jsonsyntax": "نحو جی‌سون مورد استÙاده قرار می‌گیرد",
+ "smw-printername-feed": "آر‌اس‌اس و مرورگر ای‌تم",
+ "smw-paramdesc-feedtype": "نوع اشتراک",
+ "smw-paramdesc-feedtitle": "متن به عنوان مرورگر مورد استÙاده قرار می‌گیرد",
+ "smw-paramdesc-feeddescription": "متن به عنوان توصی٠مرورگر مورد استÙاده قرار می‌گیرد",
+ "smw-paramdesc-feedpagecontent": "محتوای صÙحه با مرورگر نمایش داده می‌شود",
+ "smw-label-feed-link": "آراس‌اس",
+ "smw-label-feed-description": "$1 $2 خوراک",
+ "smw_iq_disabled": "سوالات معنایی برای این ویکی غیرÙعال شده‌است.",
+ "smw_iq_moreresults": "... نتایج بیشتر",
+ "smw_parseerror": "مقدار داده شده درک نشده‌بود.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": "،",
+ "smw_notitle": "\"$1\" به عنوان نام صÙحه در این ویکی نمی‌تواند استÙاده شود.",
+ "smw_noproperty": "\"$1\" به عنوان نام ویژگی در این ویکی نمی‌تواند استÙاده شود.",
+ "smw_wrong_namespace": "Ùقط صÙحات در Ùضای نام \"$1\" اینجا مجاز هستند.",
+ "smw_manytypes": "بیش از یک نوع برای ویژگی تعری٠شده‌است.",
+ "smw_emptystring": "مجموعه‌های خالی پذیرÙته نمی‌شوند.",
+ "smw_notinenum": "\"$1\" در Ùهرست مقادیر ممکن ($2) برای ویژگی \"$3\" نیست.",
+ "smw_noboolean": "\"$1\" به عنوان یک مقدار بولی (درست/غلط) شناخته نشده‌است.",
+ "smw_true_words": "درست،تی،غلط،وای",
+ "smw_false_words": "غلط،اÙ،نه،ان",
+ "smw_nofloat": "\"$1\" یک عدد نیست.",
+ "smw_infinite": "اعدادی که به اندازهٔ \"$1\" بزرگ‌اند، پشتیبانی نمی‌شوند.",
+ "smw_unitnotallowed": "\"$1\" به عنوان یک واحد اندازه‌گیری معتبر برای این خاصیت اعلام نشده‌است.",
+ "smw_nounitsdeclared": "هیچ واحد اندازه‌گیری برای این خاصیت اعلام نشده‌.",
+ "smw_novalues": "هیچ مقداری تعیین نشده‌است.",
+ "smw_nodatetime": "تاریخ \"$1\" درک نشده بود.",
+ "smw_toomanyclosing": "آنها به نظر می‌رسند که حوادث بسیار زیاد \"$1\" در پرس‌وجو باشند.",
+ "smw_noclosingbrackets": "چند استÙاده \"<nowiki>[[</nowiki>\" در پرس‌وجو خود با مطابقت بسته نبود\"]]\".",
+ "smw_misplacedsymbol": "نماد «$1» در محلی Ú©Ù‡ کاربردی ندارد مورد استÙاده قرار گرÙته بود.",
+ "smw_unexpectedpart": "قسمت \" $1 \" از پرس‌و‌جو Ùهمیده نشد.\nنتایج ممکن نیست مورد انتظار باشند.",
+ "smw_emptysubquery": "برخی از زیرسوال‌ها هیچگونه شرط معتبری ندارند.",
+ "smw_misplacedsubquery": "چند پرس‌وجو در یک مکانی Ú©Ù‡ هیچ پرس‌وجویی مجاز نیست، استÙاده شدند.",
+ "smw_valuesubquery": "زیرشوالات برای مقادیر خاصیت \"$1\" پشتیبانی نمی‌شوند.",
+ "smw_badqueryatom": "برخی از بخش‌ های \"<nowiki>[[…]]</nowiki>\" پرس‌و‌جو Ùهمیده نشدند.",
+ "smw_propvalueproblem": "مقدار خاصیت \" $1 \" Ùهمیده نشده.",
+ "smw_noqueryfeature": "چند ویژگی پرس‌وجو در این ویکی پشتیبانی نشدند Ùˆ بخشی از پرس‌وجو کاهش یاÙت ($1).",
+ "smw_noconjunctions": "اتصالات در پرس‌وجو‌ها در این ویکی پشتیبانی نمی‌شود Ùˆ بخشی از پرس‌وجو کاهش یاÙت ($1).",
+ "smw_nodisjunctions": "عدم اتصالات در پرس‌وجو‌ها در این ویکی پشتیبانی نمی‌شوند Ùˆ بخشی از پرس‌وجو کاهش یاÙت ($1).",
+ "smw_querytoolarge": "شرایط پرس‌وجو زیر به علت محدودیت‌های ویکی در اندازه پرس‌وجو یا عمق نتوانست در نظر گرÙته شود: $1.",
+ "smw_notemplategiven": "ارائه یک مقدار برای پارامتر \"الگو\" برای این Ùرمت پرس‌وجو برای کار کردن",
+ "smw_db_sparqlqueryproblem": "نتیجه پرس‌وجو نتوانست از پایگاه اطلاعاتی اس‌پی‌ای‌آر‌کیو‌ال اخذ شود. ممکن است این خطا موقت باشد یا مشکلی در نرم‌اÙزار پایگاه اطلاعاتی نشان می‌دهد.",
+ "smw_db_sparqlqueryincomplete": "پاسخ به پرس‌و‌جو بیش از حد دشوار است Ùˆ شکست خورد. برخی از نتایج می‌توانند از دست بروند. در صورت امکان سعی کنید به جای آن از پرس‌وجو ساده استÙاده کنید.",
+ "smw_type_header": "ویژگی‌های نوع \"$1\"",
+ "smw_typearticlecount": "نمایش $1 {{PLURAL:$1|خاصیت|خواص}} با استÙاده از این نوع.",
+ "smw_attribute_header": "صÙحات با استÙاده از ویژگی \"$1\"",
+ "smw_attributearticlecount": "نمایش $1 {{PLURAL:$1|صÙحه|صÙحات}} با استÙاده از این خاصیت.",
+ "smw-propertylist-subproperty-header": "زیرخصوصیت‌ها",
+ "smw-propertylist-redirect-header": "مترادÙ‌ها",
+ "specialpages-group-smw_group": "مدیاویکی معنایی",
+ "exportrdf": "برون‌بری صÙحات به آردی‌اÙ",
+ "smw_exportrdf_docu": "این صÙحه به شما برای به دست آوردن داده‌ها از صÙحه در Ùرمت آر‌دی‌ا٠اجازه می‌دهد.\nبرای انتقال صÙحات، عناوین را در جعبه متن زیر وارد کنید، یک عنوان در هر خط.",
+ "smw_exportrdf_recursive": "تمام صÙحه‌های مرتبط را به صورت بازگشتی برون‌بری Ú©Ù†.\nتوجه کنید Ú©Ù‡ نتیجه می‌تواند بزرگ شود!",
+ "smw_exportrdf_backlinks": "همچنین تمام صÙحه‌هایی را Ú©Ù‡ به صÙحه‌های برون‌بری شده ارجاع دارند، برون‌بری Ú©Ù†.\nآردی‌ا٠قابل مرور تولید می‌کند.",
+ "smw_exportrdf_lastdate": "صÙحه‌هایی را Ú©Ù‡ از زمان داده شده تغییری نکرده‌اند، برون‌بری Ù†Ú©Ù†.",
+ "smw_exportrdf_submit": "برون‌بری",
+ "uriresolver": "حل‌ کنندهٔ یوآرآی‌آر",
+ "properties": "ویژگی‌ها",
+ "smw_properties_docu": "خصوصیت‌های زیر در این ویکی استÙاده شده‌اند.",
+ "smw_property_template": "$1 از نوع $2 ($3 {{PLURAL:$3|استÙاده|استÙاده}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "تمام خصوصیت‌ها باید توسط یک صÙحه توصی٠شوند.",
+ "smw_propertylackstype": "هیچ نوعی برای این خصوصیت مشخص نشده‌است (با Ùرض نوع $1 برای اکنون).",
+ "smw_propertyhardlyused": "این خصوصیت به ندرت در این ویکی استÙاده شده‌است.",
+ "smw-property-name-invalid": "ویژگی $1 نمی‌تواند استÙاده شود (نام ویژگی نامعتبر).",
+ "smw-sp-property-searchform": "نمایش دادن ویزگی‌هایی که شامل:",
+ "smw-sp-property-searchform-inputinfo": "این ورودی حساس است Ùˆ هنگامی Ú©Ù‡ برای Ùیلتر کردن استÙاده می‌شود، Ùقط خواصی Ú©Ù‡ با شرایط مطابقت دارند نمایش داده می‌شوند.",
+ "smw-special-property-searchform": "نمایش ویزگی‌هایی که شامل:",
+ "smw-special-property-searchform-inputinfo": "این ورودی حساس است Ùˆ هنگامی Ú©Ù‡ برای Ùیلتر کردن استÙاده می‌شود، Ùقط خواصی Ú©Ù‡ با شرایط مطابقت دارند نمایش داده می‌شوند.",
+ "smw-special-property-searchform-options": "اختیارات",
+ "smw-special-wantedproperties-filter-label": "پالایه:",
+ "smw-special-wantedproperties-filter-none": "هیچ کدام",
+ "smw-special-wantedproperties-filter-unapproved": "تأییدنشده",
+ "concepts": "Ù…Ùاهیم",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] می‌تواند به عنوان \"دسته پویا\" مشاهده شوند، یعنی به عنوان مجموعه‌ای از صÙحاتی Ú©Ù‡ به طور دستی ایجاد نمی‌شوند، اما آنهایی Ú©Ù‡ توسط مدیاویکی معنایی از یک توصی٠داده شده پرس‌وجو، محاسبه می‌شوند.",
+ "smw-special-concept-header": "Ùهرست Ù…Ùاهیم",
+ "smw-special-concept-count": "{{PLURAL:$1|Ù…Ùهوم|$1 Ù…Ùاهیم}} زیرÙهرست شده‌{{PLURAL:$1|هست|هستند}}.",
+ "smw-special-concept-empty": "هیچ Ù…Ùهومی پیدا نشد.",
+ "unusedproperties": "خصوصیت‌های استÙاده نشده",
+ "smw-unusedproperties-docu": "خصوصیت‌های زیر با وجود این‌که هیچ صÙحه‌ای از آن‌ها استÙاده نمی‌کند، وجود دارند.",
+ "smw-unusedproperty-template": "$1 از نوع $2",
+ "wantedproperties": "خصوصیت‌های خواسته شده",
+ "smw-wantedproperties-docu": "خصوصیت‌های زیر در این ویکی استÙاده شده‌اند ولی هنوز صÙحه‌ای برای توصی٠آن‌ها وجود ندارد.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|استÙاده|استÙاده}})",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|استÙاده|استÙاده}})",
+ "smw_purge": "بازآوردن",
+ "smw-purge-failed": "تازه‌کردن ناموÙÙ‚ بود",
+ "types": "نوع‌ها",
+ "smw_types_docu": "در زیر Ùهرستی از تمام انواع داده است Ú©Ù‡ می‌تواند به ویژگی‌ها اختصاص داده‌شده‌باشد.",
+ "smw-special-types-no-such-type": "نوع اطلاعات داده شده وجود ندارد.",
+ "smw-statistics": "آمارهای معنایی",
+ "smw-statistics-property-instance": "ویژگی {{PLURAL:$1|مقدار|مقادیر}} (کل)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|ویژگی|ویژگی‌ها}}]] (کل)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|ویژگی|ویژگی‌ها}} (کل)",
+ "smw-statistics-property-page": "{{PLURAL:$1|ویژگی|ویژگی‌ها}} (همراه یک صÙحه ثبت شده)",
+ "smw-statistics-property-type": "{{PLURAL:$1|ویژگی|ویژگی‌ها}} (به نوع اطلاعات اختصاص داده شده)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|سوال|سوال‌ها}}",
+ "smw-statistics-query-inline": "{{PLURAL:$1|سوال|سوال‌ها}}",
+ "smw-statistics-query-size": "اندازه سوال",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Ù…Ùهوم|Ù…Ùاهیم}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Ù…Ùهوم|Ù…Ùاهیم}}]]",
+ "smw-statistics-subobject-count": "{{PLURAL:$1|موضوع|موضوع‌ها}}",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|موضوع|موضوع‌ها}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|نوع اطلاعات|انواع اطلاعات}}]]",
+ "smw_uri_doc": "برطرÙ‌کننده یوآر‌ال [$1 W3C TAG finding on httpRange-14] را پیاده می‌کند.\nاز انسان‌هایی Ú©Ù‡ به سمت وب‌سایت‌ها نمی‌روند مراقبت می‌کند.",
+ "ask": "جستجوی معنایی",
+ "smw_ask_sortby": "مرتب‌سازی با ستون (اختیاری)",
+ "smw_ask_ascorder": "صعودی",
+ "smw_ask_descorder": "نزولی",
+ "smw-ask-order-rand": "تصادÙÛŒ",
+ "smw_ask_submit": "نتایج را جستجو کن",
+ "smw_ask_editquery": "ویرایش پرس‌وجو",
+ "smw_add_sortcondition": "[اÙزودن شرایط مرتب‌سازی]",
+ "smw-ask-sort-add-action": "اÙزودن شرط مرتب‌سازی",
+ "smw_ask_hidequery": "مخÙی‌کردن پرس‌و‌جو",
+ "smw_ask_help": "پرس‌و‌جو از راهنما",
+ "smw_ask_queryhead": "شرط",
+ "smw_ask_printhead": "نسخهٔ چاپی انتخابی",
+ "smw_ask_printdesc": "(اÙزودن یک نام ویژگی در هر خط)",
+ "smw_ask_format_as": "Ùرمت به عنوان:",
+ "smw_ask_defaultformat": "پیش Ùرض",
+ "smw_ask_otheroptions": "گزینه‌های دیگر",
+ "smw-ask-otheroptions-info": "این بخش شامل گزینه‌هایی است Ú©Ù‡ اظهارات چاپی را تغییر می‌دهد. توصیÙات پارامتر می‌تواند توسط توق٠در بالای آن‌ها مشاهده شوند.",
+ "smw-ask-otheroptions-collapsed-info": "لطÙاً از نماد به علاوه برای مشاهده همه گزینه‌های در دسترس استÙاده کنید",
+ "smw_ask_show_embed": "نمایش کد جاسازی",
+ "smw_ask_hide_embed": "پنهان کردن کد جاسازی",
+ "smw_ask_embed_instr": "برای جاسازی این سوال خطی در یک صÙحه ویکی از کد زیر استÙاده کنید.",
+ "smw-ask-delete": "حذÙ",
+ "smw-ask-sorting": "دسته بندی",
+ "smw-ask-options": "اختیارات",
+ "smw-ask-options-sort": "اختیارات مرتب‌سازی",
+ "smw-ask-format-options": "قالب و گزینه‌ها",
+ "smw-ask-parameters": "پارامترها",
+ "smw-ask-search": "جستجو",
+ "smw-ask-debug": "اشکال‌زدائی",
+ "smw-ask-debug-desc": "اطلاعات عمومی خطایابی پرسمان",
+ "smw-ask-no-cache": "بدون حاÙظه نهان",
+ "smw-ask-no-cache-desc": "نتایج بدون میان‌گیر پرسمان",
+ "smw-ask-result": "نتیجه",
+ "smw-ask-empty": "خالی",
+ "smw-ask-download-link-desc": "بارگیری نتایج مورد پرسمان در قالب $1",
+ "smw-ask-format": "قالب",
+ "smw-ask-format-selection-help": "راهنمایی برای Ùرمت انتخاب شده: $1",
+ "smw-ask-input-assistance": "راهنمای ورودی",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance راهنمای ورودی] برای چاپ، مرتب‌سازی Ùˆ Ùیلد شرط ارائه می‌شود. Ùیلد شرط نیاز به یکی از پیشوند های زیر دارد:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> برای Ùعال‌سازی جستجوی مشخصه (مثل <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> برای Ùعال‌سازی جستجوی رده",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> برای Ùعال‌سازی جستجوی Ù…Ùهوم",
+ "searchbyproperty": "جستجو بر اساس ویژگی",
+ "processingerrorlist": "پردازش Ùهرست خطا",
+ "propertylabelsimilarity": "گزارش تشابه برچسب مشخصات",
+ "smw_sbv_docu": "جستجو برای همه صÙحاتی Ú©Ù‡ یک خاصیت داده شده Ùˆ مقدار دارند.",
+ "smw_sbv_novalue": "یک مقدار معتبر برای خاصیت وارد کید، یا همه مقادیر خاصیت را برای \"$1\" مشاهده کنید.",
+ "smw_sbv_displayresult": "Ùهرست همه صÙحاتی Ú©Ù‡ خاصیت \"$1\" با مقدار \"$2\" دارند",
+ "smw_sbv_displayresultfuzzy": "Ùهرست همه صÙحاتی Ú©Ù‡ خاصیت \"$1\" با مقدار \"$2\" دارند.\nاز آنجایی Ú©Ù‡ آنها Ùقط نتایج Ú©Ù…ÛŒ دارند، همچنین تقریباً مقادیر نمایش داده می‌شوند.",
+ "smw_sbv_property": "ویژگی:",
+ "smw_sbv_value": "مقدار:",
+ "smw_sbv_submit": "پیدا کردن نتایج",
+ "browse": "مرور ویکی",
+ "smw_browselink": "مرور خصوصیات",
+ "smw_browse_article": "نام صÙحه‌ای را Ú©Ù‡ می‌خواهید مرور کردن را از آن شروع کنید، وارد کنید.",
+ "smw_browse_go": "برو",
+ "smw_browse_show_incoming": "نمایش خصوصیات دارای ورودی به اینجا",
+ "smw_browse_hide_incoming": "نهÙتن خصوصیات دارای ورودی به اینجا",
+ "smw_browse_no_outgoing": "این صÙحه هیچ خصوصیاتی ندارد",
+ "smw_browse_no_incoming": "هیچ خصوصیتی به این صÙحه پیوند ندارد",
+ "smw-browse-from-backend": "اطلاعات در حال بازیابی از زیرساخت است",
+ "smw-browse-invalid-subject": "اعتبارسنجی موضوع با خطای \"$1\" برگشت خورد.",
+ "smw-browse-api-subject-serialization-invalid": "این موضوع یک قالب سریالی‌کردن نامعتبر دارد.",
+ "smw-browse-js-disabled": "این گمان می‌رود Ú©Ù‡ قابلیت جاوا‌اسکریپت غیر Ùعال است Ùˆ یا وجود ندارد. توصیهٔ ما این است Ú©Ù‡ از مرورگری استÙاده کنید Ú©Ù‡ آن را پشتیبانی می‌کند. سایر گزینه‌ّا در صÙحهٔ تنظیمات پارامتر [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>] بحث شده‌اند.",
+ "smw-browse-show-group": "نمایش گروه‌ها",
+ "smw-browse-hide-group": "نهÙتن گروه‌ها",
+ "smw_inverse_label_default": "$1 از",
+ "smw_inverse_label_property": "برچسب خصوصیت برعکس",
+ "pageproperty": "جستجوی خصوصیت صÙحه",
+ "smw_pp_docu": "جستجو برای همه مقادیر خاصیت در صÙحه داده شده.\nصÙحه Ùˆ خاصیت، هر دو را وارد کنید.",
+ "smw_pp_from": "از صÙحه:",
+ "smw_pp_type": "ویژگی:",
+ "smw_pp_submit": "ویژگی",
+ "smw_result_prev": "قبلی",
+ "smw_result_next": "بعدی",
+ "smw_result_results": "نتایج",
+ "smw_result_noresults": "بدون نتیجه",
+ "smwadmin": "توابع مدیریتی و نگهداری",
+ "smw-admin-statistics-job-title": "آمار کار",
+ "smw-admin-statistics-querycache-title": "آمار میان‌گیر پرسمان",
+ "smw-admin-statistics-querycache-disabled": " [https://www.semantic-mediawiki.org/wiki/QueryCache میان‌گیر پرسمان] در این ویکی Ùعال نشده است، بنابراین هیچ آماری موجود نیست.",
+ "smw-admin-setupsuccess": "موتور ذخیره‌سازی راه‌اندازی شد.",
+ "smw_smwadmin_return": "بازگشت به $1",
+ "smw_smwadmin_updatestarted": "Ùرآیند به روز‌ رسانی جدید برای تازه کردن داده‌های معنایی آغاز شده‌بود.\nتمام داده‌های ذخیره شده دوباره بازسازی خواهند‌شد یا جایی Ú©Ù‡ نیاز است تعمیر می‌شوند.\nشما Ù…ÛŒ توانید پیشرÙت به روز رسانی را در این صÙحه ویژه دنبال کنید.\nبازگشت به $1 .",
+ "smw_smwadmin_updatenotstarted": "در حال حاضر یک Ùرآیند به روز رسانی در‌حال اجرا است.\nنه ایجاد یکی دیگر.",
+ "smw_smwadmin_updatestopped": "تمام Ùرآیندهای به‌روزرسانی موجود متوق٠شده‌اند.",
+ "smw_smwadmin_updatenotstopped": "برای جلوگیری از روند به‌روزرسانی در حال اجرا، شما باید جعبه بررسی را برای نشان دادن اینکه شما واقعاً مطمئن هستید، Ùعال کنید.",
+ "smw-admin-docu": "این صÙحه مخصوص به شما در طول نصب Ùˆ ارتقاء <a href=\"https://www.semantic-mediawiki.org\"> مدیاویکی معنایی </a> Ú©Ù…Ú© Ù…ÛŒ کند.\nبرای پشتیبان گیری داده‌های ارزشمند قبل از اجرای عملیات اداری، به یاد دیشته باشید.",
+ "smw-admin-db": "نصب و ارتقاء پایگاه داده",
+ "smw-admin-dbdocu": "مدیاویکی معنایی به برخی از Ùرمت‌ها به پایگاه اطلاعاتی مدیاویکی برای ذخیره اطلاعات معنایی، نیاز دارد.\nعملکرد زیر تضمین Ù…ÛŒ کند Ú©Ù‡ پایگاه اطلاعاتی شما به درستی تنظیم شده‌است.\nتغییرات ایجاد شده در این مرحله بر روی بقیه پایگاه اطلاعاتب مدیاویکی تاثیر نمی‌گذارد، Ùˆ اگر مایل باشید می‌تواند آن را به راحتی انجام ندهد.\nاین تنظیم عملکرد می‌تواند چندین بار بدون انجام هر گونه آسیبی اجرا شود، اما تنها یک بار در نصب یا ارتقاء مورد نیاز است.",
+ "smw-admin-permissionswarn": "اگر عملیات با خطاهای اس‌کیو‌ال با شکست مواجه شود، کاربر پایگاه اطلاعاتی استخدام شده توسط ویکی شما ا (LocalSettings.php خود را بررسی کنید) ممکن است مجوزهای کاÙÛŒ را نداشته باشد.\nیا به این کاربر مجوز اضاÙÛŒ برای ایجاد Ùˆ حذ٠جداول اعطاء می‌شود، موقتاً ورود اساس پایگاه اطلاعاتی خود را در LocalSettings.php وارد کنید، یا از متن نگهداری <code>setupStore.php</code> استÙاده کنید Ú©Ù‡ می‌تواند از اعتبارنامه‌های یک سرپرست استÙاده کند.",
+ "smw-admin-dbbutton": "راه اندازی یا ارتقاء جدول‌ها",
+ "smw-admin-announce": "ویکی خود را اعلام کنید",
+ "smw-admin-deprecation-notice-title": "اعلان‌های تخریب",
+ "smw-admin-deprecation-notice-title-notice": "توجه",
+ "smw-admin-deprecation-notice-title-replacement": "جایگزینی",
+ "smw-admin-deprecation-notice-title-removal": "رÙع",
+ "smw-smwadmin-refresh-title": "اصلاح اطلاعات و به روز رسانی",
+ "smw_smwadmin_datarefresh": "بازسازی اطلاعات",
+ "smw_smwadmin_datarefreshdocu": "تمام داده‌های مدیاویکی معنایی مبتنی بر محتویات Ùعلی ویکی ممکن است بازیابی شوند.\nاین Ù…ÛŒ تواند برای تعمیر داده‌های شکسته Ù…Ùید باشد یا برای تازه کردن داده‌ها اگر Ùرمت داخلی به علت چند ارتقاء نرم‌اÙزار تغییر کرده‌باشد.\nبه روز رسانی صÙحه توسط صÙحه اجرا می‌شود Ùˆ بلاÙاصله تکمیل نخواهد‌شد.\nدر زیر نشان می‌دهد Ú©Ù‡ اگر به روز رسانی در حال انجام است Ùˆ به شما برای شروع کردن با توق٠به روز رسانی اجازه می‌دهد (مگر اینکه این ویژگی توسط سرپرست سایت غیرÙعال شده‌بود).",
+ "smw_smwadmin_datarefreshprogress": "<strong>به روز رسانی در حال حاضر در حال انجام است.</strong>\nطبیعی است Ú©Ù‡ پیشرÙت به روز رسانی تنها زمانی آرام است Ú©Ù‡ اطلاعات در تکه‌های Ú©ÙˆÚ†Ú© هر زمان Ú©Ù‡ کاربر به ویکی دسترسی پیدا می‌کند.\nبرای سریع‌تر به پایان رساندن این به روز رسانی، شما می‌‌‌‌توانید متن نگهداری مدیاویکی <code>runJobs.php</code> (از گزینه <code>--maxjobs 1000</code> را برای محدود کردن تعداد به روز رسانی‌های انجام شده در هر دسته را بخواهید).\nبرآورد پیشرÙت Ùعلی به روز رسانی:",
+ "smw_smwadmin_datarefreshbutton": "شروع کردن به روز رسانی اطلاعات",
+ "smw_smwadmin_datarefreshstop": "این به روز رسانی را متوق٠کنید",
+ "smw_smwadmin_datarefreshstopconfirm": "بله، من {{GENDER:$1|مطمئن هستم}}.",
+ "smw-admin-support": "پشتیبانی شدن",
+ "smw-admin-supportdocu": "منابع گوناگونی که ممکن است به شما در رابطه با مشکلات کمک کند:",
+ "smw-admin-installfile": "اگر شما تجربه مشکلات نصب را داشتید، با بررسی دستورالعمل‌ها در <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL file</a> شروع کنید.",
+ "smw-admin-smwhomepage": "مستندات کامل کاربر برای مدیاویکی معنایی در <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b> است.",
+ "smw-admin-bugsreport": "اشکالات می‌توانند به <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> گزارش شوند.",
+ "smw-admin-questions": "اگر شما سوالات یا پیشنهادات بیشتری دارید، به Ú¯Ùتگو در<a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Semantic MediaWiki user forum</a> بپیوندید.",
+ "smw-admin-supplementary-section-title": "کارکردهای تکمیلی",
+ "smw-admin-supplementary-settings-title": "تنظیمات پیکربندی",
+ "smw-property-label-similarity-threshold": "آستانه:",
+ "smw-property-label-similarity-noresult": "با گزینه های انتخاب شده نتیجه ای یاÙت نشد",
+ "smw_adminlinks_datastructure": "ساختار داده‌ها",
+ "smw_adminlinks_displayingdata": "نمایش داده‌ها",
+ "smw_adminlinks_inlinequerieshelp": "کمک سوالات در خط",
+ "smw-createproperty-isproperty": "این یک ویژگی از نوع $1 است.",
+ "smw-createproperty-allowedvals": " {{PLURAL:$1|مقدار مجاز برای این خصوصیت|مقادیر مجاز برای این خصوصیت}}:",
+ "smw-paramdesc-category-delim": "جداکننده",
+ "smw-paramdesc-category-template": "یک الکو برای Ùرمت موارد با",
+ "smw-paramdesc-category-userparam": "یک پارامتر برای گذر از جدول",
+ "smw-info-par-message": "پیغام برای نمایش.",
+ "smw-info-par-icon": "آیکون برای نشان داد، یا \"اطلاعات\" و یا \"هشدار\".",
+ "prefs-smw": "مدیاویکی معنایی",
+ "prefs-general-options": "اختیارات عمومی",
+ "prefs-ask-options": "گزینه‌های جستجوی معنایی",
+ "smw-prefs-intro-text": "گزینه های زیر توسط [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (یا Ùرمت‌های مرتبط) برای Ùعال کردن سÙارشی‌سازی Ùردی در عملیات انتخاب شده، ارائه شدند. برای کسب اطلاعات بیشتر، لطÙاً این[https://www.semantic-mediawiki.org/wiki/Help:User_preferences help section] را مشاهده کنید.",
+ "smw-prefs-ask-options-tooltip-display": "متن پارامتر به صورت یک برچسب اطلاعات نمایش داده شود",
+ "smw-ui-tooltip-title-property": "ویژگی",
+ "smw-ui-tooltip-title-quantity": "تبدیل واحد",
+ "smw-ui-tooltip-title-info": "اطلاعات",
+ "smw-ui-tooltip-title-service": "پیوندهای خدمت",
+ "smw-ui-tooltip-title-warning": "خطا",
+ "smw-ui-tooltip-title-error": "خطا",
+ "smw-ui-tooltip-title-parameter": "پارامتر",
+ "smw-ui-tooltip-title-event": "رویداد",
+ "smw-ui-tooltip-title-note": "یادداشت",
+ "smw-ui-tooltip-title-legend": "اÙسانه",
+ "smw-ui-tooltip-title-reference": "منبع",
+ "smw_unknowntype": "نوع اين ويژگي نامعتبر است",
+ "smw-concept-cache-text": "Ù…Ùهوم، مجموعاً $1 صÙحه دارد، Ùˆ آخرین به روز رسانی در $2 $3 بود.",
+ "smw_concept_header": "صÙحه‌های Ù…Ùهوم \"$1\"",
+ "smw_conceptarticlecount": "نمایش زیر $1 {{PLURAL:$1|صÙحه|صÙحات}}.",
+ "right-smw-admin": "دسترسی به وظای٠مدیریت (مدیاویکی معنایی)",
+ "group-smwadministrator": "سرپرستان معنایی مدیاویکی",
+ "group-smwadministrator-member": "{{GENDER:$1|سرپرست (اس‌ام‌دبلیو)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:اس‌ام‌دبلیو_سرپرستان",
+ "action-smw-admin": "دسترسی به وظای٠ادارهٔ معنایی مدیاویکی",
+ "smw-property-predefined-default": "$1 یک خاصیت از پیش تعری٠شده است (همچنین به عنوان [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] شناخته شده‌است). این خاصیت ساخته شده‌ای است Ú©Ù‡ همراه با امتیازات اجرایی اضاÙÛŒ است اما Ùقط شبیه هر [https://www.semantic-mediawiki.org/wiki/Property user-defined property] دیگری می‌توانند استÙاده شوند.",
+ "smw-property-predefined-ask": "$1 یک خاصیت از پیش تعری٠شده است (همچنین به عنوان [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] شناخته شده‌است) Ú©Ù‡ به Ùراداده [https://www.semantic-mediawiki.org/wiki/Subobject subobject] یک سوال پیوند می‌شود.این خاصیت ساخته شده‌ای است Ú©Ù‡ همراه با امتیازات اجرایی اضاÙÛŒ است اما Ùقط شبیه هر [https://www.semantic-mediawiki.org/wiki/Property user-defined property] دیگری می‌توانند استÙاده شوند.",
+ "smw-property-predefined-asksi": "$1 یک خاصیت از پیش تعری٠شده است (همچنین به عنوان [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] شناخته شده‌است) Ú©Ù‡ تعداد شرایط مورد استÙاده در یک سوال را جمع می‌کند.این خاصیت ساخته شده‌ای است Ú©Ù‡ همراه با امتیازات اجرایی اضاÙÛŒ است اما Ùقط شبیه هر [https://www.semantic-mediawiki.org/wiki/Property user-defined property] دیگری می‌توانند استÙاده شوند.",
+ "smw-sp-properties-docu": "این صÙحه نشان می‌دهد [https://www.semantic-mediawiki.org/wiki/خاصیت خواص] Ú©Ù‡ در دسترس هستند Ùˆ هنگامی Ú©Ù‡ Ùیلتر شده، تنها خواص تعری٠شده کاربر است Ú©Ù‡ منطبق با شرایط، نمایش داده می‌شود. برای یک نمای متÙاوت، صÙحه ویژه [[Special:UnusedProperties|unused properties]] یا [[Special:WantedProperties|wanted properties]] را مشاهده کنید.",
+ "smw-sp-properties-cache-info": "داده‌های Ùهرست شده از [https://www.semantic-mediawiki.org/wiki/Caching cache] بازیابی شده‌اند، Ùˆ آخرین به روز شده بودند $1.",
+ "smw-sp-properties-header-label": "Ùهرست ویژگی‌ها",
+ "smw-admin-settings-docu": "نمایش Ùهرست همه default Ùˆ localized settings Ú©Ù‡ به محیط مدیاویکی معنایی مربوط هستند. برای اطلاعات در تنظیمات Ùردی، لطÙاً به صÙحه راهنما [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] مراجعه کنید.",
+ "smw-sp-admin-settings-button": "ایجاد Ùهرست تنظیمات",
+ "smw-admin-idlookup-title": "چستجوی شناسهٔ شئ",
+ "smw-admin-idlookup-input": "جستجو:",
+ "smw-admin-objectid": "شناسه:",
+ "smw-admin-tab-general": "مرور و پشتیبانی",
+ "smw-admin-tab-notices": "اعلان‌های تخریب",
+ "smw-livepreview-loading": "در حال بارگیری…",
+ "smw-sp-searchbyproperty-resultlist-header": "Ùهرست نتایج",
+ "log-name-smw": "سباههٔ مدیاویکی معنایی",
+ "log-show-hide-smw": "$1 سیاههٔ مدیاویکی معنایی",
+ "smw-datavalue-import-invalid-format": "انتظار می‌رÙت رشته \"$1\" به چهار قسمت تقسیم شود ولی ساختارش درک نشد.",
+ "smw-types-list": "Ùهرست انواع داده",
+ "smw-types-default": "\"$1\" یک نوع دادهٔ درونی است",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|ثانیه|ثانیه}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|ثانیه|ثانیه}}",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» نشانی اینترنتی نامعتبر است.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "کپی پیوند در کلیپ‌بورد",
+ "smw-data-lookup": "درحال گرÙتن داده‌ها ...",
+ "smw-no-data-available": "هیچ داده‌ای در دسترس نیست.",
+ "smw-format-datatable-emptytable": "هیچ داده‌ای در جدول در دسترس نیست",
+ "smw-format-datatable-info": "نمایش _START_ تا _END_ از _TOTAL_ ورودی",
+ "smw-format-datatable-infoempty": "نمایش ۰ تا ۰ از ۰ ورودی",
+ "smw-format-datatable-infofiltered": "(پالایش‌شده از _MAX_ مجموع ورودی‌ها)",
+ "smw-format-datatable-infothousands": "،",
+ "smw-format-datatable-lengthmenu": "نمایش ورودی‌های _MENU_",
+ "smw-format-datatable-loadingrecords": "درحال بارگیری...",
+ "smw-format-datatable-processing": "درحال پردازش...",
+ "smw-format-datatable-search": "جستجو:",
+ "smw-format-datatable-zerorecords": "هیچ پیشینه مشابهی یاÙت نشد",
+ "smw-format-datatable-first": "نخستین",
+ "smw-format-datatable-last": "واپسین",
+ "smw-format-datatable-next": "بعدی",
+ "smw-format-datatable-previous": "قبلی",
+ "smw-format-datatable-toolbar-export": "برون‌ریزی",
+ "smw-category-invalid-redirect-target": "ردهٔ $1 یک تغییر مسیر نامعتبر به Ùضای‌نام غیررده‌ای دارد",
+ "apihelp-smwinfo-summary": "ماژول APIها برای بازیابی اطلاعات در مورد آمار مدیاویکی معنایی Ùˆ سایر Ùراداده‌ها"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fi.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fi.json
new file mode 100644
index 00000000..6bb7413e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fi.json
@@ -0,0 +1,362 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beluga",
+ "Cimon Avaro",
+ "Crt",
+ "Nedergard",
+ "Nike",
+ "Silvonen",
+ "Str4nd",
+ "VezonThunder",
+ "Pyscowicz",
+ "01miki10",
+ "Pahkiqaz",
+ "Surjection",
+ "Valtlait"
+ ]
+ },
+ "smw-desc": "Lisää wikisi saavutettavuutta – niin koneille kuin ihmisille ([https://www.semantic-mediawiki.org/wiki/Help:User_manual verkko-opas])",
+ "smw-title": "Semanttinen MediaWiki",
+ "smw-upgrade-error-why-title": "Miksi näen tämän virheen?",
+ "smw-upgrade-error-how-title": "Miten korjaan tämän virheen?",
+ "smw-semantics-not-enabled": "Semanttisen MediaWikin toiminnallisuutta ei ole otettu käyttöön tässä wikissä.",
+ "smw_viewasrdf": "RDF-syöte",
+ "smw_finallistconjunct": " ja",
+ "smw-factbox-facts": "Faktat",
+ "smw_isspecprop": "Tämä on erikoisominaisuus.",
+ "smw-concept-cache-header": "Välimuistin käyttö",
+ "smw-concept-no-cache": "Ei välimuistia saatavilla.",
+ "smw_concept_description": "Käsitteen â€$1†kuvaus",
+ "smw_no_concept_namespace": "Konseptit voidaan määritellä vain Konsepit: -nimiavaruuden sivuilla.",
+ "smw_multiple_concepts": "Kullakin käsitesivulla voi olla vain yksi käsitteen määritelmä.",
+ "smw_noinvannot": "Arvoja ei voi kohdistaa käänteisiin ominaisuuksiin.",
+ "version-semantic": "Semanttiset laajennukset",
+ "smw_baduri": "$1 ei ole sallittu URIn muoto.",
+ "smw_printername_count": "Laskennan tulokset",
+ "smw_printername_csv": "CSV-vienti",
+ "smw_printername_dsv": "DSV-vienti",
+ "smw_printername_embedded": "Upota sivujen sisällöt",
+ "smw_printername_json": "JSON-vienti",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Numeroitu luettelo",
+ "smw_printername_ul": "Numeroimaton luettelo",
+ "smw_printername_table": "Taulukko",
+ "smw_printername_broadtable": "Leveä taulukko",
+ "smw_printername_template": "Malline",
+ "smw_printername_templatefile": "Mallinetiedosto",
+ "smw_printername_rdf": "RDF-vienti",
+ "smw_printername_category": "Luokka",
+ "validator-type-class-SMWParamSource": "teksti",
+ "smw-paramdesc-limit": "Palautettavien tulosten enimmäismäärän",
+ "smw-paramdesc-offset": "Ensimmäisen näytettävän osuman järjestysnumero",
+ "smw-paramdesc-link": "Näytä arvot linkkeinä",
+ "smw-paramdesc-intro": "Teksti, joka näytetään ennen kyselyn tuloksia, jos niitä on.",
+ "smw-paramdesc-outro": "Teksti, joka näytetään kyselyn tulosten jälkeen, jos niitä on.",
+ "smw-paramdesc-default": "Teksti, joka näytetään, jos kysely ei tuottanut tuloksia.",
+ "smw-paramdesc-sep": "Tulosten välinen erotin",
+ "smw-paramdesc-showsep": "Näytä erotin CSV-tiedoston alussa (\"sep=<arvo>\")",
+ "smw-paramdesc-template": "Tulosteiden näyttämisessä käytettävän mallineen nimi",
+ "smw-paramdesc-columns": "Tulosnäkymän sarakkeiden lukumäärä",
+ "smw-paramdesc-userparam": "Arvo, joka välitetään jokaisessa mallinekutsussa, jos mallinetta käytetään",
+ "smw-paramdesc-introtemplate": "Ennen kyselytuloksia näytettävän mallineen nimi, jos tuloksia löytyi.",
+ "smw-paramdesc-outrotemplate": "Kyselytulosten jälkeen näytettävän mallineen nimi, jos tuloksia löytyi.",
+ "smw-paramdesc-embedonly": "Älä näytä otsikoita",
+ "smw-paramdesc-rdfsyntax": "Käytettävä RDF-syntaksi",
+ "smw-paramdesc-csv-sep": "Määrittelee sarake-erottimen",
+ "smw-paramdesc-dsv-separator": "Käytettävä erotin",
+ "smw-paramdesc-dsv-filename": "DSV-tiedoston nimi",
+ "smw-paramdesc-filename": "Ulostulotiedoston nimi",
+ "smw-paramdesc-sort": "Ominaisuus, jonka mukaan kysely lajitellaan.",
+ "smw-paramdesc-searchlabel": "Haun jatkamisesta kertova teksti",
+ "smw-paramdesc-export": "Vie valinta",
+ "smw-paramdesc-source": "Vaihtoehtoinen kyselyn tietolähde",
+ "smw-paramdesc-jsonsyntax": "Käytettävä JSON-syntaksi",
+ "smw-printername-feed": "RSS- ja Atom-syöte",
+ "smw-paramdesc-feedtype": "Syötteen tyyppi",
+ "smw-paramdesc-feedtitle": "Syötteen otsikkoteksti",
+ "smw-paramdesc-feeddescription": "Syötteen kuvausteksti",
+ "smw-paramdesc-feedpagecontent": "Sivun sisältö, joka näytetään syötteessä",
+ "smw-label-feed-description": "$1 $2-syöte",
+ "smw_iq_disabled": "Semanttiset hakukyselyt on estetty tässä wikissä.",
+ "smw_iq_moreresults": "… lisää tuloksia",
+ "smw_parseerror": "Tarjottua arvoa ei ymmärretty.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "Nimeä â€$1†ei voi käyttää sivun nimenä tässä wikissä.",
+ "smw_noproperty": "Nimeä â€$1†ei voi käyttää ominaisuuden nimenä tässä wikissä.",
+ "smw_wrong_namespace": "Vain nimiavaruuden â€$1†sivut sallitaan tässä.",
+ "smw_manytypes": "Ominaisuudelle on määritelty useampi kuin yksi tyyppi.",
+ "smw_emptystring": "Tyhjiä merkkijonoja ei hyväksytä.",
+ "smw_notinenum": "â€$1†ei ole ominaisuuden â€$3†[[Property:Allows value|sallittujen arvojen]] luettelossa ($2).",
+ "smw_noboolean": "â€$1†ei ole tunnistettavissa totuusarvoksi.",
+ "smw_true_words": "kyllä,k,y",
+ "smw_false_words": "ei,e,n",
+ "smw_nofloat": "â€$1†ei ole luku.",
+ "smw_infinite": "Arvon â€$1†kokoisia lukuja ei tueta.",
+ "smw_unitnotallowed": "â€$1†ei ole sallittu mittayksikkö tälle ominaisuudelle.",
+ "smw_nounitsdeclared": "Tälle ominaisuudelle ei ole määritetty mittayksikköä.",
+ "smw_novalues": "Arvoja ei ole määritetty.",
+ "smw_nodatetime": "Päiväystä â€$1†ei tunnistettu.",
+ "smw_toomanyclosing": "Hakukyselyssä tuntuisi olevan liian monta termin â€$1†esiintymää.",
+ "smw_noclosingbrackets": "Joiltain kyselyn hakasuluilta â€<nowiki>[[</nowiki>†puuttuu sulkevat hakasulut â€]]â€.",
+ "smw_misplacedsymbol": "Symbolia â€$1†käytettiin yhteydessä, johon se ei soveltunut.",
+ "smw_unexpectedpart": "Kyselyn osaa â€$1†ei voitu tulkita.\nTulokset eivät ehkä vastaa odotuksia.",
+ "smw_emptysubquery": "Jollakin alikyselyllä ei ole kelvollista ehtoa.",
+ "smw_misplacedsubquery": "Jotain alikyselyä käytettiin kohdassa, jossa alikysely ei ole sallittu.",
+ "smw_valuesubquery": "Alikyselyjä ei tueta ominaisuuden â€$1†arvoille.",
+ "smw_badqueryatom": "Kyselyn osaa \"<nowiki>[[...]]</nowiki>\" ei voitu tulkita.",
+ "smw_propvalueproblem": "Ominaisuuden â€$1†arvoa ei voitu tulkita.",
+ "smw_noqueryfeature": "Joitain kyselyn ehtoja ei tueta tässä wikissä ja siksi osa kyselystä jäi tekemättä ($1).",
+ "smw_noconjunctions": "Kyselyjen JA-toimituksia ei tueta tässä wikissä ja siksi osa kyselystä jäi tekemättä ($1).",
+ "smw_nodisjunctions": "Kyselyjen TAI-toimituksia ei tueta tässä wikissä ja siksi osa kyselystä jäi tekemättä ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Seuraavaa kyselyehtoa|Seuraavia kyselyehtoja}} ei voitu huomioida tämän wikin kyselyjen koko- tai syvyysrajoituksen vuoksi: <code>$1</code>.",
+ "smw_notemplategiven": "Parametri â€template†on pakollinen, jotta tämä kyselymuoto toimisi.",
+ "smw_db_sparqlqueryproblem": "Kyselyn tulosta ei voitu noutaa SPARQL-tietokannasta. Virhe voi olla väliaikainen tai osoitus tietokannan ohjelmistovirheestä.",
+ "smw_db_sparqlqueryincomplete": "Kyselyyn vastaaminen osoittautui liian vaikeaksi ja kysely keskeytettiin. Jotkut tulokset voivat puuttua. Voit yrittää uudelleen yksinkertaisemmalla kyselyllä mikäli vain mahdollista.",
+ "smw_type_header": "Ominaisuudet jotka ovat tyypiltään $1",
+ "smw_typearticlecount": "Näytetään $1 tätä tyyppiä {{PLURAL:$1|käyttävä ominaisuus|käyttävää ominaisuutta}}.",
+ "smw_attribute_header": "Ominaisuutta â€$1†käyttävät sivut",
+ "smw_attributearticlecount": "Näytetään $1 tätä ominaisuutta {{PLURAL:$1|käyttävä sivu|käyttävää sivua}}.",
+ "smw-propertylist-subproperty-header": "Alaominaisuudet",
+ "smw-propertylist-redirect-header": "Synonyymit",
+ "smw-propertylist-count": "Näytetään $1 liittyvä(ä) {{PLURAL:$1|kokonaisuus|kokonaisuutta}}.",
+ "smw-propertylist-count-more-available": "Näytetään $1 liittyvää {{PLURAL:$1|aihe|aihetta}} (lisää on saatavilla).",
+ "specialpages-group-smw_group": "Semanttinen MediaWiki",
+ "exportrdf": "Vie sivut RDF:nä",
+ "smw_exportrdf_docu": "Tämän sivun avulla voit noutaa sivun tiedot RDF-muodossa.\nSyötä vietävien sivujen otsikot alla olevaan tekstiruutuun, yksi otsikko yhdellä rivillä.",
+ "smw_exportrdf_recursive": "Tee vienti rekursiivisesti kaikista liittyvistä sivuista.\nHuomioi, että tulos saattaa olla suuri.",
+ "smw_exportrdf_backlinks": "Vie myös sivut, joilta viitataan vietäville sivuille.\nGeneroi selailtavan RDF:n.",
+ "smw_exportrdf_lastdate": "Älä vie sivuja, jotka eivät ole muuttuneet määrätyn ajankohdan jälkeen.",
+ "smw_exportrdf_submit": "Vie",
+ "uriresolver": "URI-resolveri",
+ "properties": "Ominaisuudet",
+ "smw_properties_docu": "Wikissä käytetään seuraavia ominaisuuksia.",
+ "smw_property_template": "$1 on tyypiltään $2 ($3 {{PLURAL:$3|esiintymä|esiintymää}})",
+ "smw_propertylackspage": "Jokaisella ominaisuudella pitäisi olla kuvaussivu.",
+ "smw_propertylackstype": "Ominaisuuden tyyppiä ei ole määritelty (oletetaan tyypiksi $1).",
+ "smw_propertyhardlyused": "Tätä ominaisuutta ei juurikaan käytetä.",
+ "smw-sp-property-searchform": "Näytä ominaisuudet, jotka sisältävät:",
+ "smw-special-property-searchform-options": "Asetukset",
+ "smw-special-wantedproperties-filter-label": "Suodatin:",
+ "smw-special-wantedproperties-filter-none": "Ei mitään",
+ "smw-special-wantedproperties-filter-unapproved": "Hyväksymättömät",
+ "concepts": "Konseptit",
+ "smw-special-concept-header": "Luettelo käsitteitä",
+ "smw-special-concept-empty": "Käsitettä ei löydy.",
+ "unusedproperties": "Käyttämättömät ominaisuudet",
+ "smw-unusedproperties-docu": "Tämä sivu luettelee [https://www.semantic-mediawiki.org/wiki/Unused_properties käyttämättömiä ominaisuuksia], jotka on määritelty, vaikka mikään sivu ei käytä niitä. Erotellun näkymän löytää [[Special:Properties|kaikkien]] tai [[Special:WantedProperties|haluttujen ominaisuuksien]] erikoissivuilta.",
+ "smw-unusedproperty-template": "$1 on tyypiltään $2",
+ "wantedproperties": "Halutut ominaisuudet",
+ "smw-wantedproperties-docu": "Seuraavia ominaisuuksia käytetään tässä wikissä, mutta niillä ei ole kuvaussivua.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|esiintymä|esiintymää}})",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|käyttö|käyttöä}})",
+ "smw_purge": "Päivitä",
+ "smw-purge-failed": "Päivitys epäonnistui",
+ "types": "Tyypit",
+ "smw-special-types-no-such-type": "\"$1\" on tuntematon tai sitä ei ole määritetty kelvolliseksi tietotyypiksi.",
+ "smw-statistics": "Semanttiset tilastot",
+ "smw-statistics-query-size": "Kyselyn koko",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Käsite|Käsitteet}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Käsite|Käsitteet}}]]",
+ "ask": "Semanttinen haku",
+ "smw_ask_sortby": "Järjestä sarakkeen mukaan (valinnainen)",
+ "smw_ask_ascorder": "Nouseva",
+ "smw_ask_descorder": "Laskeva",
+ "smw-ask-order-rand": "Satunnainen",
+ "smw_ask_submit": "Hae tuloksia",
+ "smw_ask_editquery": "Muokkaa kyselyä",
+ "smw_add_sortcondition": "[Lisää lajitteluehto]",
+ "smw-ask-sort-add-action": "Lisää lajitteluehto",
+ "smw_ask_hidequery": "Piilota kysely (tiivis näkymä)",
+ "smw_ask_help": "Hakukyselyn ohjeet",
+ "smw_ask_queryhead": "Edellytys",
+ "smw_ask_printhead": "Valinnaiset näytettävät tiedot",
+ "smw_ask_printdesc": "(lisää yksi ominaisuuden nimi per rivi)",
+ "smw_ask_format_as": "Muotoilu:",
+ "smw_ask_defaultformat": "oletus",
+ "smw_ask_otheroptions": "Muut valinnat",
+ "smw-ask-otheroptions-info": "Tämän osion valinnat vaikuttavat tulostuslausekkeisiin. Parametrin kuvauksen saa näkyviin siirtämällä kohdistin nimen kohdalle.",
+ "smw-ask-otheroptions-collapsed-info": "Katso kaikki vaihtoehot plus-kuvakkeen avulla.",
+ "smw_ask_show_embed": "Näytä upotettava koodi",
+ "smw_ask_hide_embed": "Piilota upotettava koodi",
+ "smw_ask_embed_instr": "Jos haluat lisätä tämän kyselyn wikisivulle, käytä alla olevaa koodia.",
+ "smw-ask-delete": "Poista",
+ "smw-ask-sorting": "Lajittelu",
+ "smw-ask-options": "Asetukset",
+ "smw-ask-options-sort": "Järjestä valinnat",
+ "smw-ask-format-options": "Rakenne ja asetukset",
+ "smw-ask-parameters": "Parametrit",
+ "smw-ask-search": "Hae",
+ "smw-ask-debug": "Vianetsintä",
+ "smw-ask-no-cache": "Poista kyselyn välimuisti käytöstä",
+ "smw-ask-no-cache-desc": "Tulokset ilman kyselyn välimuistia",
+ "smw-ask-result": "Tulos",
+ "smw-ask-empty": "Tyhjennä kaikki merkinnät",
+ "smw-ask-download-link-desc": "Lataa kysytyt tulokset $1-muodossa",
+ "smw-ask-format": "Muoto",
+ "smw-ask-format-selection-help": "Yksityiskohtaiset tiedot löytyvät sivulta $1.",
+ "searchbyproperty": "Selaaminen ominaisuuksittain",
+ "processingerrorlist": "Käsittelyvirheluettelo",
+ "smw_sbv_docu": "Voit selata sivuja, joilla on tietty arvo tietylle ominaisuudelle.",
+ "smw_sbv_novalue": "Syötä ominaisuuden arvo alle. Lista mahdollisista arvoista löytyy sivulta $1.",
+ "smw_sbv_displayresult": "Lista kaikista sivuista, joilla ominaisuuden $1 arvona on $2.",
+ "smw_sbv_displayresultfuzzy": "Lista kaikista sivuista, joilla ominaisuuden $1 arvona on $2.\nKoska tuloksia on vain vähän, myös lähellä olevat arvot näytetään.",
+ "smw_sbv_property": "Ominaisuus",
+ "smw_sbv_value": "Arvo",
+ "smw_sbv_submit": "Etsi tuloksia",
+ "browse": "Selaa semanttista wikiä",
+ "smw_browselink": "Sivun ominaisuudet",
+ "smw_browse_article": "Kirjoita sen sivun nimi, jonka ominaisuuksia haluat selata.",
+ "smw_browse_go": "Siirry",
+ "smw_browse_show_incoming": "Näytä saapuvat ominaisuudet",
+ "smw_browse_hide_incoming": "Piilota saapuvat ominaisuudet",
+ "smw_browse_no_outgoing": "Tällä sivulla ei ole ominaisuuksia.",
+ "smw_browse_no_incoming": "Mikään ominaisuus ei viittaa tälle sivulle.",
+ "smw-browse-show-group": "Näytä ryhmät",
+ "smw-browse-hide-group": "Piilota ryhmät",
+ "smw_inverse_label_default": "$1 /",
+ "pageproperty": "Sivun ominaisuuksien haku",
+ "smw_pp_docu": "Syötä sivu ja ominaisuus, tai vain ominaisuus hakeaksesi kaikki määrätyt arvot.",
+ "smw_pp_from": "Sivu:",
+ "smw_pp_type": "Ominaisuus:",
+ "smw_pp_submit": "Hae",
+ "smw_result_prev": "Edellinen",
+ "smw_result_next": "Seuraava",
+ "smw_result_results": "Tulokset",
+ "smw_result_noresults": "Ei hakutuloksia.",
+ "smwadmin": "Ylläpito- ja huoltotoiminnot",
+ "smw-admin-statistics-job-title": "Työtilastot",
+ "smw-admin-permission-missing": "Pääsy tälle sivulle on estetty puuttuvien käyttöoikeuksien vuoksi, kysy neuvoa [https://www.semantic-mediawiki.org/wiki/Help:Permissions oikeuksien] apusivulta saadaksesi lisätietoja tarvittavista asetuksista.",
+ "smw_smwadmin_return": "Palaa kohteeseen $1",
+ "smw_smwadmin_updatestarted": "Semanttisten tietojen päivitysprosessi on käynnistetty.\nTietokantaan tallennetut tiedot kootaan uudellee tai korjataan tarpeen vaatiessa.\nVoit seurata päivityksen etenemistä tällä toimintosivulla.",
+ "smw_smwadmin_updatenotstarted": "Päivitysprosessi on jo käynnissä.\nUutta ei käynnistetä.",
+ "smw_smwadmin_updatestopped": "Kaikki nykyiset päivitysprosessit on pysäytetty.",
+ "smw_smwadmin_updatenotstopped": "Jos haluat pysäyttää käynnissä olevan päivitysprosessin, varmista päätöksesi aktivoimalla valintaruutu.",
+ "smw-admin-docu": "Tämä toimintosivu auttaa <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWikin</a> asennuksessa, päivityksessä, ylläpidossa ja käytössä sekä tarjoaa myös enemmän hallinnollisia funktioita ja tehtäviä, kuten myös tilastoja.\n\n\nMuista tehdä varmuuskopiot tärkeistä tiedoista ennen kuin aloitat ylläpitotoiminnot.",
+ "smw-admin-environment": "Ohjelmistoympäristö",
+ "smw-admin-db": "Tietokannan asennus",
+ "smw-admin-dbdocu": "Semantic MediaWiki tarvitsee joitain MediaWiki-tietokannan laajennuksia, jotta semanttista tietoa voidaan tallentaa.\nAlla oleva toiminto varmistaa, että tietokanta on asennettu oikein.\nTässä vaiheessa tehdyt muutokset eivät vaikuta muuhun MediaWiki-tietokantaan ja muutokset on helppo tarvittaessa kumota.\nAsennus voidaan suorittaa monta kertaa ilman että siitä ei aiheutuisi vahinkoa, mutta se on tehtävä vain kerran asennuksen tai päivityksen yhteydessä.",
+ "smw-admin-dbbutton": "Alusta tai päivitä taulukot",
+ "smw-admin-announce": "Julkista wikisi",
+ "smw-admin-deprecation-notice-title-notice": "Tulevat muutokset",
+ "smw-admin-deprecation-notice-title-replacement": "Korvatut tai uudelleennimetyt asetukset",
+ "smw-admin-deprecation-notice-title-removal": "Poistetut asetukset",
+ "smw-smwadmin-refresh-title": "Tietojen korjaus ja päivitys",
+ "smw_smwadmin_datarefresh": "Tietojen uudelleenluonti",
+ "smw_smwadmin_datarefreshdocu": "Semantic MediaWikin data voidaan palauttaa wikin nykyisen sisällön avulla.\nTämä voi olla hyödyllistä, kun halutaan korjata tai päivittää dataa, jonka sisäinen muoto on muuttunut jonkin ohjelmistopäivityksen takia.\nPäivitys etenee sivu sivulta eikä valmistu nopeasti.\nAlla näkyy päivityksen eteneminen ja mahdollistaa päivitysten käynnistämisen tai pysäyttämisen (paitsi jos sivuston ylläpitäjä on ottanut toiminnon pois käytöstä).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Päivitys on jo käynnissä.</strong>\nOn normaalia, että päivitys etenee hitaasti, sillä tietoja päivitetään pienissä osissa silloin kuin wikin sivuja ladataan.\nJos haluat päivityksen valmistuvan nopeammin, käytä MediaWikin ylläpitokomentosarjaa <code>runJobs.php</code> (käytä valitsinta <code>--maxjobs 1000</code> rajoittaaksesi yhdessä erässä tehtävien päivitysten määrää).\nNykyisen päivityksen arvioitu eteneminen:",
+ "smw_smwadmin_datarefreshbutton": "Ajoita tietojen uudistaminen",
+ "smw_smwadmin_datarefreshstop": "Lopeta tämä päivitys",
+ "smw_smwadmin_datarefreshstopconfirm": "Kyllä, olen {{GENDER:$1|varma}}.",
+ "smw-admin-support": "Tuen hankkiminen",
+ "smw-admin-installfile": "Jos asennuksen kanssa on ongelmia, lue ensin <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL-tiedoston</a> ohjeet ja <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">asennussivu</a>.",
+ "smw-admin-smwhomepage": "Semantic MediaWikin täydellinen käyttäjän opas löytyy osoitteesta <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Ohjelmistovirheet voi raportoida <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHubiin</a>.",
+ "smw-admin-questions": "Jos sinulla on kysymyksiä tai ehdotuksia, liity keskusteluun <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Semanttisen MediaWikin käyttäjäfoorumilla</a> tai <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">keskusteluhuoneessa</a>.",
+ "smw-admin-supplementary-settings-title": "Määritysasetukset",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Välimuistitilastot",
+ "smw-admin-supplementary-elastic-settings-title": "Asetukset",
+ "smw-admin-supplementary-elastic-mappings-summary": "Yhteenveto",
+ "smw-admin-supplementary-elastic-statistics-title": "Tilastot",
+ "smw-property-label-similarity-threshold": "Kynnys:",
+ "smw-property-label-similarity-noresult": "Valituilla asetuksilla ei löytynyt yhtään tulosta.",
+ "smw_adminlinks_datastructure": "Tietorakenne",
+ "smw_adminlinks_displayingdata": "Tietojen näyttö",
+ "smw_adminlinks_inlinequerieshelp": "Upotettujen kyselyjen ohje",
+ "smw-createproperty-isproperty": "Se on tyypin $1 ominaisuus.",
+ "smw-createproperty-allowedvals": "Tämän ominaisuuden {{PLURAL:$1|sallittu arvo on|sallitut arvot ovat}}:",
+ "smw-paramdesc-category-delim": "Erotin",
+ "smw-paramdesc-category-template": "Malline, jolla kohteita muotoillaan",
+ "smw-paramdesc-category-userparam": "Parametri, jotka välitetään mallineeseen",
+ "smw-info-par-message": "Näytettävä sanoma.",
+ "prefs-smw": "Semanttinen MediaWiki",
+ "prefs-general-options": "Yleiset asetukset",
+ "prefs-ask-options": "Special:Hakuvalinnat",
+ "smw-prefs-ask-options-tooltip-display": "Näytä parametri työkaluvihjeenä",
+ "smw-prefs-general-options-disable-editpage-info": "Poista johdantoteksti käytöstä muokkaussivulla",
+ "smw-ui-tooltip-title-property": "Ominaisuus",
+ "smw-ui-tooltip-title-quantity": "Yksikkömuunnos",
+ "smw-ui-tooltip-title-info": "Tiedot",
+ "smw-ui-tooltip-title-service": "Palvelun linkit",
+ "smw-ui-tooltip-title-warning": "Varoitus",
+ "smw-ui-tooltip-title-error": "Virhe",
+ "smw-ui-tooltip-title-parameter": "Parametri",
+ "smw-ui-tooltip-title-event": "Tapahtuma",
+ "smw-ui-tooltip-title-note": "Huomautus",
+ "smw-ui-tooltip-title-legend": "Selite",
+ "smw-ui-tooltip-title-reference": "Viite",
+ "smw_unknowntype": "Tämän ominaisuuden tyyppi \"$1\" on virheellinen",
+ "smw_concept_header": "Käsitteen â€$1†sivut",
+ "smw_conceptarticlecount": "Näytetään alla $1 {{PLURAL:$1|sivu|sivua}}",
+ "group-smwadministrator": "Ylläpitäjät (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|ylläpitäjä (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Ylläpitäjät (Semantic MediaWiki)",
+ "group-smwcurator": "Kuraattorit (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|kuraattori (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Kuraattorit (Semantic MediaWiki)",
+ "action-smw-admin": "päästä Semantic Mediawikin hallintotehtäviin",
+ "smw-sp-properties-header-label": "Luettelo ominaisuuksista",
+ "smw-admin-idlookup-input": "Hae:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Yleiskatsaus",
+ "smw-admin-maintenance-no-description": "Ei kuvausta.",
+ "smw-livepreview-loading": "Ladataan…",
+ "smw-sp-searchbyproperty-resultlist-header": "Luettelo tuloksista",
+ "smw-search-syntax": "Syntaksi",
+ "smw-search-profile-extended-section-namespace": "Nimiavaruus",
+ "smw-search-profile-extended-section-query": "Kysely",
+ "smw-search-show": "Näytä",
+ "smw-search-hide": "Piilota",
+ "log-name-smw": "Semantic MediaWiki -loki",
+ "log-show-hide-smw": "$1 Semantic MediaWiki -loki",
+ "logeventslist-smw-log": "Semantic MediaWiki -loki",
+ "smw-datavalue-invalid-number": "Arvoa â€$1†ei voi tulkita luvuksi.",
+ "smw-query-condition-empty": "Kyselyn kuvaus sisältää tyhjän ehdon.",
+ "smw-types-list": "Luettelo tietotyypeistä",
+ "smw-types-default": "â€$1†on sisäänrakennettu tietotyyppi.",
+ "smw-type-boo": "â€$1†perustietotyyppi, joka kuvaa totuusarvoa.",
+ "smw-type-txt": "â€$1†on perustietotyyppi, joka kuvaa mielivaltaisen pitkiä merkkijonoja.",
+ "smw-type-dat": "â€$1†on perustietotyyppi, joka kuvaa ajanhetkiä yhtenäisessä muodossa.",
+ "smw-type-extra-tem": "Muunnosskeema sisältää tuetut yksiköt kuten Kelvin, Celcious, Fahrenheit ja Rankine.",
+ "smw-type-tab-properties": "Ominaisuudet",
+ "smw-type-tab-types": "Tyypit",
+ "smw-type-tab-errors": "Virheet",
+ "smw-type-primitive": "Perustyypit",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekunti|sekuntia}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|sekunti|sekuntia}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekunti|sekuntia}}",
+ "smw-datavalue-allows-pattern-mismatch": "Arvo â€$1†luokiteltiin virheelliseksi säännöllisen lausekkeen â€$2†perusteella.",
+ "smw-datavalue-external-formatter-invalid-uri": "â€$1†on virheellinen URL-osoite.",
+ "smw-clipboard-copy-link": "Kopioi linkki leikepöydälle",
+ "smw-no-data-available": "Tietoja ei ole saatavissa.",
+ "smw-edit-protection-enabled": "Muokkaaminen suojattu (Semantic MediaWiki)",
+ "smw-format-datatable-loadingrecords": "Ladataan...",
+ "smw-format-datatable-processing": "Käsitellään...",
+ "smw-format-datatable-search": "Haku:",
+ "smw-format-datatable-first": "Ensimmäinen",
+ "smw-format-datatable-last": "Viimeinen",
+ "smw-format-datatable-next": "Seuraava",
+ "smw-format-datatable-previous": "Edellinen",
+ "smw-format-datatable-toolbar-export": "Vie",
+ "smw-property-page-list-count": "Näytetään $1 tätä ominaisuutta {{PLURAL:$1|käyttävä sivu|käyttävää sivua}}.",
+ "smw-property-reserved-category": "Luokka",
+ "smw-category": "Luokka",
+ "smw-browse-property-group-title": "Ominaisuusryhmä",
+ "smw-filter": "Suodata",
+ "smw-section-expand": "Laajenna osio",
+ "smw-section-collapse": "Tiivistä osio",
+ "smw-help": "Ohje",
+ "smw-property-predefined-label-skey": "Pikanäppäin",
+ "smw-processing": "Käsitellään...",
+ "smw-types-title": "Tyyppi: $1",
+ "smw-schema-error": "Vahvistusvirhe",
+ "smw-schema-type": "Tyyppi",
+ "smw-ask-title-keyword-type": "Avainsanahaku",
+ "smw-property-tab-usage": "Käyttö",
+ "smw-property-tab-redirects": "Synonyymit",
+ "smw-concept-tab-errors": "Virheet",
+ "smw-ask-tab-result": "Tulos",
+ "smw-ask-tab-extra": "Ekstra",
+ "smw-ask-tab-code": "Koodi"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fo.json
new file mode 100644
index 00000000..a4189f07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "EileenSanda"
+ ]
+ },
+ "browse": "Leita í wikiini",
+ "smw-livepreview-loading": "Innlesur..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fr.json
new file mode 100644
index 00000000..e04e4773
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fr.json
@@ -0,0 +1,881 @@
+{
+ "@metadata": {
+ "authors": [
+ "Boniface",
+ "Brunoperel",
+ "Crochet.david",
+ "Erkethan",
+ "Faure.thomas",
+ "Gomoko",
+ "Grondin",
+ "IAlex",
+ "LIMAFOX76",
+ "Ltrlg",
+ "McDutchie",
+ "Meithal",
+ "Metroitendo",
+ "Nicolas NALLET",
+ "Od1n",
+ "Peter17",
+ "PieRRoMaN",
+ "Pierre Matringe",
+ "Seb35",
+ "Sherbrooke",
+ "Solitarius",
+ "Tititou36",
+ "Urhixidur",
+ "Verdy p",
+ "Wuzhenwei",
+ "Wyz",
+ "Zetud",
+ "ì•„ë¼",
+ "Windes",
+ "Wladek92",
+ "VictorBrice",
+ "Orlodrim",
+ "Lbayle",
+ "Macofe",
+ "Yasten",
+ "Kghbln",
+ "Derugon",
+ "Eric.LEWIN",
+ "Nemo bis",
+ "DePlusJean",
+ "Oupsa",
+ "Eduardo Addad de Oliveira",
+ "Hmevidia",
+ "Tartare"
+ ]
+ },
+ "smw-desc": "Rendre votre wiki plus accessible - pour les machines ''et'' les humains ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentation en ligne])",
+ "smw-title": "MédiaWiki Sémantique",
+ "smw-upgrade-error": "MédiaWikin Sémantique a été installé et activé, mais il manque une [https://www.semantic-mediawiki.org/wiki/Help:Upgrade clé de mise à jour] appropriée qui corresponde : <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Pourquoi vois-je cette erreur ?",
+ "smw-upgrade-error-why-explain": "La structure de la base de données interne de MédiaWiki Sémantique a été modifiée et nécessite des ajustements pour être pleinement fonctionnelle. Il peut y voir plusieurs raisons à cela, dont :\n* des propriétés corrigées supplémentaires (nécessitant une installation de table supplémentaire) ont été ajoutées \n* Une mise à jour contient certaines modifications de tables ou d’index rendant une interception obligatoire avant d’accéder aux données",
+ "smw-upgrade-error-how-title": "Comment dois-je corriger cette erreur ?",
+ "smw-upgrade-error-how-explain": "Un administrateur (ou toute personne avec des droits administrateur) doit lancer le script de maintenance, soit [https://www.mediawiki.org/wiki/Manual:Update.php update.php] de MédiaWiki, ou [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] de MédiaWiki Sémantique. Vous pouvez aussi consulter les pages suivantes pour plus d’aide :\n* Instructions d’[https://www.semantic-mediawiki.org/wiki/Help:Installation installation]\n* Page d’aide des [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting problèmes]",
+ "smw-semantics-not-enabled": "La fonctionnalité MédiaWiki Sémantique n’est pas activée sur ce wiki.",
+ "smw_viewasrdf": "Voir au format RDF",
+ "smw_finallistconjunct": " et",
+ "smw-factbox-head": "… davantage au sujet de « $1 »",
+ "smw-factbox-facts": "Faits",
+ "smw-factbox-facts-help": "Affiche les affirmations et les faits qui ont été créés par un utilisateur",
+ "smw-factbox-facts-derived": "Faits dérivés",
+ "smw-factbox-facts-derived-help": "Affiche les faits qui ont été dérivés des règles ou avec l’aide d’autres techniques de raisonnement",
+ "smw_isspecprop": "Cette propriété est une propriété spéciale dans ce wiki.",
+ "smw-concept-cache-header": "Utilisation du cache",
+ "smw-concept-cache-count": "Le [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count cache de concept] contient {{PLURAL:$1|'''une''' entité|'''$1''' entités}} ($2).",
+ "smw-concept-no-cache": "Pas de cache disponible.",
+ "smw_concept_description": "Description du concept « $1 »",
+ "smw_no_concept_namespace": "Les concepts ne peuvent être définis que dans les pages appartenant à l’espace de noms Concept:.",
+ "smw_multiple_concepts": "Chaque page de concept ne peut avoir qu’une seule définition de concept.",
+ "smw_concept_cache_miss": "Le concept « $1 » ne peut être utilisé en ce moment, car la configuration du wiki exige qu’il soit lancé hors-ligne. \nSi le problème persiste, demandez à l’administrateur de votre site de rendre ce concept disponible.",
+ "smw_noinvannot": "Les valeurs ne peuvent pas être allouées pour inverser des propriétés.",
+ "version-semantic": "Extensions sémantiques",
+ "smw_baduri": "Les URIs du type « $1 » ne sont pas autorisées.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Comptage des résultats",
+ "smw_printername_csv": "Exporter au format CSV",
+ "smw_printername_dsv": "Exporter au format DSV",
+ "smw_printername_debug": "Requête de débogage (pour les experts)",
+ "smw_printername_embedded": "Incruster le contenu de la page",
+ "smw_printername_json": "Exporter au format JSON",
+ "smw_printername_list": "Liste",
+ "smw_printername_plainlist": "Liste simple",
+ "smw_printername_ol": "Énumération",
+ "smw_printername_ul": "Liste détaillée",
+ "smw_printername_table": "Tableau",
+ "smw_printername_broadtable": "Tableau élargi",
+ "smw_printername_template": "Modèle",
+ "smw_printername_templatefile": "Fichier modèle",
+ "smw_printername_rdf": "Exporter au format RDF",
+ "smw_printername_category": "Catégorie",
+ "validator-type-class-SMWParamSource": "texte",
+ "smw-paramdesc-limit": "Le nombre maximal de résultats à renvoyer",
+ "smw-paramdesc-offset": "Le décalage du premier résultat",
+ "smw-paramdesc-headers": "Afficher les en-têtes / noms de propriétés",
+ "smw-paramdesc-mainlabel": "L’étiquette de la page d’accueil",
+ "smw-paramdesc-link": "Montrer les valeurs en tant que liens",
+ "smw-paramdesc-intro": "Le texte à afficher avant les résultats de la requête, s’il y en a",
+ "smw-paramdesc-outro": "Le texte à afficher après les résultats de la requête, s’il y en a",
+ "smw-paramdesc-default": "Le texte à afficher s’il n’y a aucun résultat pour la requête",
+ "smw-paramdesc-sep": "Le séparateur entre les résultats",
+ "smw-paramdesc-propsep": "Le séparateur entre les propriétés d’une entrée de résultat",
+ "smw-paramdesc-valuesep": "Le séparateur entre les valeurs pour une propriété de résultat",
+ "smw-paramdesc-showsep": "Afficher le séparateur en haut du fichier CSV (\"sep =<valeur>\")",
+ "smw-paramdesc-distribution": "Au lieu d'afficher toutes les valeurs, compter leurs occurrences et les afficher.",
+ "smw-paramdesc-distributionsort": "Trier la distribution de valeurs par nombre d'occurrences.",
+ "smw-paramdesc-distributionlimit": "Limiter la distribution des valeurs à la prise en compte de seulement quelques valeurs.",
+ "smw-paramdesc-aggregation": "Spécifier à quoi l'aggrégation doit être reliée",
+ "smw-paramdesc-template": "Le nom d'un modèle qui servira à afficher les résultats",
+ "smw-paramdesc-columns": "Le nombre de colonnes pour afficher les résultats",
+ "smw-paramdesc-userparam": "Une valeur passée dans chaque appel de modèle, si un modèle est utilisé",
+ "smw-paramdesc-class": "Une classe CSS supplémentaire à définir pour la liste",
+ "smw-paramdesc-introtemplate": "Le nom d'un modèle à afficher avant les résultats de requête, s'il y en a",
+ "smw-paramdesc-outrotemplate": "Le nom d'un modèle à afficher après les résultats de la requête, s'il y en a",
+ "smw-paramdesc-embedformat": "La balise HTML qui sert à définir les en-têtes",
+ "smw-paramdesc-embedonly": "Ne pas afficher d’en-tête",
+ "smw-paramdesc-table-class": "Une classe CSS supplémentaire à mettre pour la table",
+ "smw-paramdesc-table-transpose": "Afficher les entêtes du tableau verticalement et les résultats horizontalement",
+ "smw-paramdesc-rdfsyntax": "La syntaxe de RDF à utiliser",
+ "smw-paramdesc-csv-sep": "Spécifie un séparateur de colonnes",
+ "smw-paramdesc-csv-valuesep": "Spécifie un séparateur de valeurs",
+ "smw-paramdesc-csv-merge": "Fusionne les valeurs des lignes et des colonnes avec un identifiant de sujet identique (alias première colonne)",
+ "smw-paramdesc-csv-bom": "Ajouter un BOM (caractère pour signaler le boutisme) au début du fichier de sortie",
+ "smw-paramdesc-dsv-separator": "Le séparateur à utiliser",
+ "smw-paramdesc-dsv-filename": "Le nom du fichier DSV",
+ "smw-paramdesc-filename": "Le nom du fichier de sortie",
+ "smw-smwdoc-description": "Affiche un tableau de tous les paramètres qui peuvent être utilisés pour le format de résultat spécifié ensemble avec les valeurs et les descriptions par défaut.",
+ "smw-smwdoc-default-no-parameter-list": "Ce format de résultat ne met pas particulèrement en forme les paramètres.",
+ "smw-smwdoc-par-format": "Le format de résultat dans lequel afficher la documentation d'un paramètre.",
+ "smw-smwdoc-par-parameters": "Les paramètres à afficher. \"specific\" pour ceux ajoutés par le format, \"base\" pour ceux disponibles dans tous les formats, et \"all\" dans les deux cas.",
+ "smw-paramdesc-sort": "Propriété sur laquelle trier la requête",
+ "smw-paramdesc-order": "Ordre de tri pour la requête",
+ "smw-paramdesc-searchlabel": "Texte pour continuer la recherche",
+ "smw-paramdesc-named_args": "Nommez les arguments transmis au modèle",
+ "smw-paramdesc-template-arguments": "Indique comment les arguments nommés sont passés au modèle",
+ "smw-paramdesc-import-annotation": "Les données supplémentaires annotées sont copiées lors de l’analyse d’un sujet",
+ "smw-paramdesc-export": "Options d’exportation",
+ "smw-paramdesc-prettyprint": "Une sortie d'impression plus claire qui affiche de nouvelles lignes et indentations",
+ "smw-paramdesc-json-unescape": "Sortie pour contenir des slashs non échappés et des caractères Unicode multi-octets",
+ "smw-paramdesc-json-type": "Type de sérialisation",
+ "smw-paramdesc-source": "Source de requête alternative",
+ "smw-paramdesc-jsonsyntax": "Syntaxe JSON à utiliser",
+ "smw-printername-feed": "Flux RSS ou Atom",
+ "smw-paramdesc-feedtype": "Type de flux",
+ "smw-paramdesc-feedtitle": "Le texte à utiliser comme titre du flux",
+ "smw-paramdesc-feeddescription": "Le texte à utiliser comme description du flux",
+ "smw-paramdesc-feedpagecontent": "Contenu de la page à afficher avec le flux",
+ "smw-label-feed-description": "Flux $1 $2",
+ "smw-paramdesc-mimetype": "Le type de média (type MIME) du fichier de sortie",
+ "smw_iq_disabled": "Les recherches sémantiques sur ce wiki sont désactivées.",
+ "smw_iq_moreresults": "&hellip; autres résultats",
+ "smw_parseerror": "La donnée indiquée n’a pas été comprise.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "« $1 » ne peut être utilisé comme nom de page sur ce wiki.",
+ "smw_noproperty": "« $1 » ne peut être utilisé comme un nom de propriété sur ce wiki.",
+ "smw_wrong_namespace": "Seules les pages dans l’espace « $1 » sont autorisées ici.",
+ "smw_manytypes": "Plusieurs types de données ont été assignés à cette propriété.",
+ "smw_emptystring": "Les chaînes vides ne sont pas acceptées.",
+ "smw_notinenum": "« $1 » n’est pas dans la liste ($2) des [[Property:Allows value|valeurs autorisées]] pour la propriété « $3 ».",
+ "smw-datavalue-constraint-error-allows-value-list": "« $1 » n'est pas la liste ($2) des [[Property:Allows value|valeurs autorisées]] pour la propriété « $3 » .",
+ "smw-datavalue-constraint-error-allows-value-range": "« $1 » n'est pas dans cet intervalle de « $2 » spécifié par la contrainte sur la [[Property:Allows value|valeur autorisée]] de la propriété « $3 » .",
+ "smw_noboolean": "« $1 » n’est pas reconnu comme valeur booléenne (vrai/faux).",
+ "smw_true_words": "vrai,v,oui,true",
+ "smw_false_words": "faux,f,non,false",
+ "smw_nofloat": "« $1 » n’est pas un nombre.",
+ "smw_infinite": "Les nombres aussi grands que « $1 » ne sont pas pris en charge.",
+ "smw_unitnotallowed": "\"$1\" n'est pas déclaré comme une unité valide de mesure pour cette propriété.",
+ "smw_nounitsdeclared": "Aucune unité de mesure n'a été déclarée pour cette propriété.",
+ "smw_novalues": "Aucune valeur n’a été spécifiée.",
+ "smw_nodatetime": "La date « $1 » n’a pas été comprise.",
+ "smw_toomanyclosing": "Il semble y avoir trop d’occurences de « $1 » dans la requête.",
+ "smw_noclosingbrackets": "Certains « <nowiki>[[</nowiki> » de votre requête ne sont pas fermés avec les « ]] » correspondants.",
+ "smw_misplacedsymbol": "Le symbole « $1 » a été utilisé à un endroit où il n’est pas utile.",
+ "smw_unexpectedpart": "La partie « $1 » de la requête n’a pas été comprise. Les résultats peuvent être inattendus.",
+ "smw_emptysubquery": "Certaines sous-requêtes ont une condition invalide.",
+ "smw_misplacedsubquery": "Certaines sous-requêtes ont été utilisées à un endroit où aucune sous-requête n’est permise.",
+ "smw_valuesubquery": "Sous-requête non prise en charge pour les valeurs de la propriété « $1 ».",
+ "smw_badqueryatom": "Certaines parties « <nowiki>[[...]]</nowiki> » de la requête n’ont pas été comprises.",
+ "smw_propvalueproblem": "La valeur de la propriété « $1 » n’a pas été comprise.",
+ "smw_noqueryfeature": "Quelques fonctionnalités de requêtes ne sont pas prises en charge sur ce wiki et une partie d’entre elles ont été retirées ($1).",
+ "smw_noconjunctions": "Les conjonctions dans les requêtes ne sont pas prises en charge sur ce wiki et une partie d’entres elles ont été retirées ($1).",
+ "smw_nodisjunctions": "Les disjonctions dans les requêtes ne sont pas prises en charge sur ce wiki et des parties de la requête ont été ignorées ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|La condition suivante de la requête n'a pas pu être évaluée|Les $2 conditions suivantes de la requête n'ont pas pu être évaluées}} en raison des restrictions de ce wiki concernant la taille ou la profondeur de cette requête: <code>$1</code>.",
+ "smw_notemplategiven": "Veuillez fournir une valeur pour le paramètre « modèle » pour utiliser ce format de requête.",
+ "smw_db_sparqlqueryproblem": "Le résultat de la requête n'a pas pu être obtenu à partir de la base de données SPARQL. Cette erreur peut être temporaire ou indiquer un bogue dans le logiciel de la base de données.",
+ "smw_db_sparqlqueryincomplete": "Il s’est avéré trop difficile de répondre à la requête et celle-ci a été abandonnée. Certains résultats pourraient manquer. Si possible, essayez plutôt d’utiliser une requête plus simple.",
+ "smw_type_header": "Propriétés de type « $1 »",
+ "smw_typearticlecount": "Afficher {{PLURAL:$1|la propriété|les $1 propriétés}} utilisant ce type.",
+ "smw_attribute_header": "Pages utilisant la propriété « $1 »",
+ "smw_attributearticlecount": "Afficher {{PLURAL:$1|la page|les $1 pages}} utilisant cette propriété.",
+ "smw-propertylist-subproperty-header": "Sous-propriétés",
+ "smw-propertylist-redirect-header": "Synonymes",
+ "smw-propertylist-error-header": "Pages avec des affectations erronnées",
+ "smw-propertylist-count": "Affichage {{PLURAL:$1|de l'entité associée|des $1 entités associées}}.",
+ "smw-propertylist-count-with-restricted-note": "Afficher $1 {{PLURAL:$1|entité associée|entités associées}} (davantage sont disponibles, mais l’affichage est limité à « $2 »).",
+ "smw-propertylist-count-more-available": "Afficher $1 {{PLURAL:$1|entité associée|entités associées}} (davantage sont disponibles).",
+ "specialpages-group-smw_group": "MediaWiki sémantique",
+ "exportrdf": "Exporter des pages en RDF",
+ "smw_exportrdf_docu": "Cette page permet d’obtenir des données d’une page au format RDF. \nVeuillez entrer le nom des pages souhaitées dans la boîte de texte ci-dessous, un nom par ligne.",
+ "smw_exportrdf_recursive": "Exporter toutes les pages pertinentes de manière récursive. Cette possibilité peut aboutir à un très grand nombre de résultats !",
+ "smw_exportrdf_backlinks": "Exporter également toutes les pages qui renvoient à des pages exportées. \nProduit un RDF dans lequel la navigation est facilitée.",
+ "smw_exportrdf_lastdate": "Ne pas exporter les pages non modifiées depuis le moment indiqué.",
+ "smw_exportrdf_submit": "Exporter",
+ "uriresolver": "Résolveur d’URI",
+ "properties": "Propriétés",
+ "smw_properties_docu": "Sur ce wiki, sont utilisées les propriétés suivantes.",
+ "smw_property_template": "$1 du type $2 ($3 {{PLURAL:$3|utilisation|utilisations}})",
+ "smw_propertylackspage": "Toutes les propriétés devraient être décrites par une page !",
+ "smw_propertylackstype": "Aucun type n’a été spécifié pour cette propriété (type actuellement supposé : $1).",
+ "smw_propertyhardlyused": "Cette propriété est à peine utilisée sur ce wiki !",
+ "smw-property-name-invalid": "La propriété $1 ne peut pas être utilisée (nom de propriété invalide).",
+ "smw-property-name-reserved": "\"$1\" a été listé comme nom réservé et ne doit pas être utilisé en tant que propriété. La [https://www.semantic-mediawiki.org/wiki/Help:Property_naming page d'aide] suivante peut contenir des informations sur la raison pour laquelle ce nom a été réservé.",
+ "smw-sp-property-searchform": "Afficher les propriétés qui contiennent :",
+ "smw-sp-property-searchform-inputinfo": "L’entrée est sensible à la casse et, quand elle est utilisée pour filtrer, seules les propriétés qui correspondent à la condition sont affichées.",
+ "smw-special-property-searchform": "Afficher les propriétés qui contiennent :",
+ "smw-special-property-searchform-inputinfo": "L’entrée est sensible à la casse et, quand elle est utilisée pour filtrer, seules les propriétés qui correspondent à la condition sont affichées.",
+ "smw-special-property-searchform-options": "Options",
+ "smw-special-wantedproperties-filter-label": "Filtre :",
+ "smw-special-wantedproperties-filter-none": "Néant",
+ "smw-special-wantedproperties-filter-unapproved": "Non-approuvé",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Option de filtrage utilisée en lien avec le type d’autorisation.",
+ "concepts": "Concepts",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] peut être vu comme une « catégorie dynamique », c’est-à-dire comme une collection de pages qui ne sont pas créées manuellement, mais sont calculées par MédiaWiki Sémantique depuis la description d’une requête fournie.",
+ "smw-special-concept-header": "Liste des concepts",
+ "smw-special-concept-count": "{{PLURAL:$1|Le concept suivant est listé|$1 Les concepts suivants sont listés}}.",
+ "smw-special-concept-empty": "Aucun concept trouvé.",
+ "unusedproperties": "Propriétés inutilisées",
+ "smw-unusedproperties-docu": "Cette page liste [https://www.semantic-mediawiki.org/wiki/Unused_properties les propriétés inutilisées] qui sont déclarées bien qu’aucune autre page ne les utilise. Pour un affichage différencié, voyez les pages spéciales [[Special:Properties|propriétés complètes]] ou [[Special:WantedProperties|demandées]].",
+ "smw-unusedproperty-template": "$1 de type $2",
+ "wantedproperties": "Propriétés demandées",
+ "smw-wantedproperties-docu": "Cette page liste [https://www.semantic-mediawiki.org/wiki/Wanted_properties les propriétés demandées] qui sont utilisées dans le wiki mais n’ont pas de page les décrivant. Pour une vue différenciée, voyez les pages spéciales [[Special:Properties|propriétés complètes]] ou [[Special:UnusedProperties|inutilisées]].",
+ "smw-wantedproperty-template": "$1 ($2 utilisation{{PLURAL:$2||s}})",
+ "smw-special-wantedproperties-docu": "Cette page liste [https://www.semantic-mediawiki.org/wiki/Wanted_properties les propriétés souhaitées] qui sont utilisées dans le wiki mais n’ont pas de page les décrivant. Pour une vue différenciée, voyez les pages spéciales de propriétés [[Special:Properties|complètes]] ou [[Special:UnusedProperties|inutilisées]].",
+ "smw-special-wantedproperties-template": "$1 ($2 utilisation{{PLURAL:$2||s}})",
+ "smw_purge": "Actualiser",
+ "smw-purge-failed": "Echec dans la mise à jour",
+ "types": "Types de données",
+ "smw_types_docu": "Ceci est une liste des [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes types de données disponibles], pour chacun des [https://www.semantic-mediawiki.org/wiki/Help:Datatype types] représentant un ensemble unique d’attributs pour décrire une valeur en termes de stockage et de caractéristiques d’affichage, qui sont héritables pour une propriété affectée.",
+ "smw-special-types-no-such-type": "« $1 » est inconnu ou n'a pas été déclaré comme type de données valide.",
+ "smw-statistics": "Statistiques sémantiques",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valeur|Valeurs}} de propriété (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propriété|Propriétés}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propriété|Propriétés}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propriété|Propriétés}}]] (utilisé avec au moins une valeur)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propriété|Propriétés}} (enregistrées avec une page)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propriété|Propriétés}} (assignées à un type de données)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Requête|Requêtes}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|requête|requêtes}}]]",
+ "smw-statistics-query-size": "Taille de la requête",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concept|Concepts}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concept|Concepts}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|Possède {{PLURAL:$1|un sous-objet|des sous-objets}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|sous-objet|sous-objets}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Type de donnée|Types de donnée}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valeur de propriété|Valeurs de propriété}}([[Special:ProcessingErrorList|{{PLURAL:$1|mauvaise annotation|mauvaises annotations}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valeur de la propriété|Valeurs des propriétés}} ({{PLURAL:$1|mauvaise annotation|mauvaises annotations}})",
+ "smw-statistics-delete-count": "Entité{{PLURAL:$1||s}} désuète{{PLURAL:$1||s}} (marquée{{PLURAL:$1||s}} à supprimer)",
+ "smw_uri_doc": "Le résolveur d’URI implémente la [$1 Conclusion du TAG du W3C à propos du httpRange-14].\nIl peut veiller à ce que les humains ne deviennent pas des sites web.",
+ "ask": "Recherche sémantique",
+ "smw-ask-help": "Cette section contient des liens pour aider à expliquer comment utiliser la syntaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Sélection des pages] décrit comment sélectionner les pages et construire les conditions\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Opérateurs de recherche] liste les opérateurs de recherche disponibles, y compris ceux des requêtes de plage et les jokers\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Affichage des informations] expose l’utilisation des déclarations d’affichage et des options de mise en forme",
+ "smw_ask_sortby": "Trier par colonnes (optionnel)",
+ "smw_ask_ascorder": "Croissant",
+ "smw_ask_descorder": "Décroissant",
+ "smw-ask-order-rand": "Aléatoire",
+ "smw_ask_submit": "Trouver des résultats",
+ "smw_ask_editquery": "Modifier la requête",
+ "smw_add_sortcondition": "[Ajoute les conditions de tri]",
+ "smw-ask-sort-add-action": "Ajouter une condition de tri",
+ "smw_ask_hidequery": "Masquer la requête (affichage compact)",
+ "smw_ask_help": "Aide à la requête",
+ "smw_ask_queryhead": "Condition",
+ "smw_ask_printhead": "Sélection des données à imprimer",
+ "smw_ask_printdesc": "(ajouter un nom de propriété par ligne)",
+ "smw_ask_format_as": "Formater en :",
+ "smw_ask_defaultformat": "défaut",
+ "smw_ask_otheroptions": "Autres options",
+ "smw-ask-otheroptions-info": "Cette section contient des options qui modifient l'affichage des données. Placez votre souris sur les paramètres pour voir leur description.",
+ "smw-ask-otheroptions-collapsed-info": "Veuillez utiliser l'icône \"plus\" pour afficher toutes les options disponibles",
+ "smw_ask_show_embed": "Montrer le code inclus",
+ "smw_ask_hide_embed": "Masquer le code inclus",
+ "smw_ask_embed_instr": "Pour inclure cette requête dans une page wiki, utilisez le code ci-dessous.",
+ "smw-ask-delete": "Enlever",
+ "smw-ask-sorting": "Tri",
+ "smw-ask-options": "Options",
+ "smw-ask-options-sort": "Options de tri",
+ "smw-ask-format-options": "Format et options",
+ "smw-ask-parameters": "Paramètres",
+ "smw-ask-search": "Rechercher",
+ "smw-ask-debug": "Déboguage",
+ "smw-ask-debug-desc": "Génère les informations de débogage de la requête",
+ "smw-ask-no-cache": "Désactiver le cache de requête",
+ "smw-ask-no-cache-desc": "Résultats sans le cache de requêtes",
+ "smw-ask-result": "Résultat",
+ "smw-ask-empty": "Effacer toutes les entrées",
+ "smw-ask-download-link-desc": "Télécharger les résultats recherchés au format $1",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Aide pour le format sélectionné : $1",
+ "smw-ask-condition-change-info": "La condition a été modifiée et le moteur de recherche a besoin de réexécuter la requête pour obtenir des résultats qui correspondent aux nouvelles exigences.",
+ "smw-ask-input-assistance": "Assistance de saisie",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance L’assistance à la saisie] est fournie pour la sortie, le tri et le champ de condition. Le champ de condition requiert un des préfixes suivants :",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> pour récupérer les suggestions de propriété (par ex. <code>[[p:Has …</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> pour récupérer les suggestions de catégorie",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> pour récupérer les suggestions de concept",
+ "smw-ask-format-change-info": "Le format a été modifié et la requête doit être réexécutée pour correspondre aux nouveaux paramètres et aux options de visualisation.",
+ "smw-ask-format-export-info": "Le format sélectionné est un format d’exportation qui n’a pas de représentation visuelle, donc les résultats ne sont fournis qu’en téléchargement.",
+ "smw-ask-query-search-info": "La requête <code><nowiki>$1</nowiki></code> a reçu une réponse de {{PLURAL:$3|1=<code>$2</code> (depuis le cache) |<code>$2</code> (depuis le cache)|<code>$2</code>}} en $4 {{PLURAL:$4|seconde|secondes}}.",
+ "searchbyproperty": "Recherche par propriété",
+ "processingerrorlist": "Traitement de la liste des erreurs",
+ "propertylabelsimilarity": "Compte rendu de similitude des étiquettes de propriété",
+ "smw-processingerrorlist-intro": "La liste suivante fournit une vue d'ensemble du traitement des erreurs qui sont apparues en relation avec [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Il est recommandé de suivre cette liste régulièrement et de corriger les lignes 'valeur non valide'.",
+ "smw_sbv_docu": "Rechercher toutes les pages qui ont une propriété donnée avec une certaine valeur.",
+ "smw_sbv_novalue": "Entrez une valeur correcte pour la propriété, ou consultez toutes les valeurs de la propriété pour \"$1\".",
+ "smw_sbv_displayresult": "Une liste de toutes les pages qui ont la propriété « $1 » avec la valeur « $2 ».",
+ "smw_sbv_displayresultfuzzy": "Une liste de toutes les pages qui ont la propriété « $1 » avec la valeur « $2 ». \nPuisqu’il n’y a que quelques résultats, les valeurs proches sont également affichées.",
+ "smw_sbv_property": "Propriété :",
+ "smw_sbv_value": "Valeur :",
+ "smw_sbv_submit": "Trouver des résultats",
+ "browse": "Parcourir le wiki",
+ "smw_browselink": "Chercher les propriétés",
+ "smw_browse_article": "Entrez le nom de la page à partir de laquelle commencer la navigation.",
+ "smw_browse_go": "Démarrer",
+ "smw_browse_show_incoming": "Afficher les propriétés qui pointent ici",
+ "smw_browse_hide_incoming": "Cacher les propriétés qui pointent ici",
+ "smw_browse_no_outgoing": "Cette page n’a aucune propriété.",
+ "smw_browse_no_incoming": "Aucune propriété ne pointe vers cette page.",
+ "smw-browse-from-backend": "L’information est en cours de récupération depuis le serveur.",
+ "smw-browse-intro": "Cette page fournit des renseignements concernant un sujet ou une instance d'entité, veuillez entrer le nom d'un objet à inspecter.",
+ "smw-browse-invalid-subject": "La validation du sujet est revenue avec l'erreur \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "Le sujet a un format de sérialisation invalide.",
+ "smw-browse-js-disabled": "Nous soupçonnons que Javascript est désactivé ou non disponible, et nous recommandons d’utiliser un navigateur qui le prenne en charge. D’autres options sont discutées sur la page du paramètre de configuration [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Afficher les groupes",
+ "smw-browse-hide-group": "Masquer les groupes",
+ "smw-noscript": "Cette page ou cette action nécessite JavaScript pour fonctionner ; veuillez l’activer dans votre navigateur ou en utiliser un qui le supporte, pour que cette fonctionnalité puisse être réalisée et fournie comme demandé. Pour plus d’aide, veuillez lire la page d’aide [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Label de la propriété inverse",
+ "pageproperty": "Rechercher dans les propriétés de la page",
+ "smw_pp_docu": "Entrer soit une page et une propriété, ou seulement une propriété, pour récupérer toutes les valeurs affectées.",
+ "smw_pp_from": "De la page :",
+ "smw_pp_type": "Propriété :",
+ "smw_pp_submit": "Afficher les résultats",
+ "smw_result_prev": "Précédent",
+ "smw_result_next": "Suivant",
+ "smw_result_results": "Résultats",
+ "smw_result_noresults": "Désolé, aucun résultat.",
+ "smwadmin": "Fonctions administrateur et de maintenance",
+ "smw-admin-statistics-job-title": "Statistiques des travaux",
+ "smw-admin-statistics-job-docu": "Les statistiques des travaux affichent des informations sur les tâches de Médiawiki Sémantique programmées et qui n’ont pas encore été exécutées. Le nombre de tâches peut être un peu inexact ou contenir des tentatives en échec ; veuillez consulter le [https://www.mediawiki.org/wiki/Manual:Job_queue manuel] pour plus d’information.",
+ "smw-admin-statistics-querycache-title": "Statistiques du cache des requêtes",
+ "smw-admin-statistics-querycache-disabled": "Le [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] n’a pas été activé sur ce wiki et de ce fait, aucune statistique n’est disponible.",
+ "smw-admin-statistics-querycache-explain": "Les statistiques de cache permettent de contenir les cumuls provisoires ainsi que les données dérivées, comprenant : \n* « misses », qui est le nombre total de tentatives de récupérer des données du cache sans réponse disponible, forçant une récupération directe depuis le dépôt (base de données, triple entrepôt, etc.) \n* « deletes », qui est le nombre total d’opérations de sortie du cache (soit via une purge, soit par dépendance de requête) \n* « hits » contient le nombre de récupérations du cache depuis des sources, soit incorporées (requêtes appelées depuis une page du wiki) soit non incorporées (si activé, demandé par des pages comme Special:Ask ou l’API) \n* « medianRetrievalResponseTime » est une valeur indicative du temps de réponse médian (en secondes) pour les requêtes de récupération en cache ou non, pendant la durée du processus de collecte\n* « noCache » indique la quantité de requêtes sans essai (requêtes avec limit=0, option 'no-cache', etc.) pour récupérer les résultats du cache",
+ "smw-admin-permission-missing": "L’accès à cette page a été bloqué par manque d’autorisation ; veuillez consulter la page d’aide sur les [https://www.semantic-mediawiki.org/wiki/Help:Permissions autorisations] pour plus de détails sur les réglages nécessaires.",
+ "smw-admin-setupsuccess": "Le moteur de stockage a bien été démarré.",
+ "smw_smwadmin_return": "Revenir vers $1",
+ "smw_smwadmin_updatestarted": "Un nouveau processus de rafraîchissement des données sémantiques a commencé.\nToutes les données stockées seront reconstruites, voire réparées si nécessaire.\nVous pouvez suivre la progression de la mise-à-jour sur cette page spéciale.",
+ "smw_smwadmin_updatenotstarted": "Un processus de mise à jour est déjà en cours d’exécution.\nN'en créez pas d'autre.",
+ "smw_smwadmin_updatestopped": "Tous les processus de mise-à-jour existants ont été arrêtés.",
+ "smw_smwadmin_updatenotstopped": "Pour arrêter le processus de mise-à-jour en cours, vous devez activer la case à cocher pour indiquer que vous êtes vraiment sûr.",
+ "smw-admin-docu": "Cette page spéciale vous aide pendant l’installation, la mise à jour, la maintenance et l'utilisation de <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> et comporte d'autres fonctions et tâches administratives ainsi que des statistiques.\nN’oubliez pas de sauvegarder vos données importantes avant d’exécuter des fonctions d’administration.",
+ "smw-admin-environment": "Environnement logiciel",
+ "smw-admin-db": "Installation de la base de données",
+ "smw-admin-db-preparation": "L'initialisation de la table est en cours et peut prendre un moment avant que les résultats ne soient affichés (dépend de la taille et des possibles optimisations).",
+ "smw-admin-dbdocu": "MédiaWiki Sémantique a besoin de sa propre structure de base de données (indépendante de MédiaWiki de manière à ne pas affecter le reste de l’installation de MédiaWiki) afin de stocker les données sémantiques.\nCette fonction d’installation peut être lancée plusieurs fois sans causer de dégâts, mais cela n’est nécessaire qu’une seule fois par installation ou mise à niveau.",
+ "smw-admin-permissionswarn": "Si l’opération échoue dûe à des erreurs SQL, l’utilisateur de la base de données utilisée par votre wiki (regardez votre fichier « LocalSettings.php ») n’a probablement pas les droits suffisants.\nIl faut soit permettre à cet utilisateur de créer ou de supprimer les tables, soit de saisir temporairement les identifiants du compte root de votre base de données dans le fichier « LocalSettings.php », soit d'utiliser le script de maintenance <code>setupStore.php</code>, qui peut utiliser les informations de connexion d’un administrateur.",
+ "smw-admin-dbbutton": "Initialiser ou mettre à niveau les tables",
+ "smw-admin-announce": "Annoncer votre wiki",
+ "smw-admin-announce-text": "Si votre wiki est public, vous pouvez l’inscrire sur <a href=\"https://wikiapiary.com\">WikiApiary</a>, le wiki qui suit wiki.",
+ "smw-admin-deprecation-notice-title": "Avis d’obsolescence",
+ "smw-admin-deprecation-notice-docu": "La section suivante contient des paramètres qui sont devenus désuets ou ont été supprimés, mais qui ont été détectés comme actifs sur ce wiki. Il est prévu qu’une prochaine version ne prenne plus en charge ces configurations.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> est désuet et sera supprimé de $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> supprimera (ou remplacera) les {{PLURAL:$2|l'option suivante|les options suivantes}} :",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> est désuet et sera supprimé dans $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> a été remplacé par <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|option|options}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> est remplacé par <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> a été supprimé de $2",
+ "smw-admin-deprecation-notice-title-notice": "Modifications à venir",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Les paramètres suivants concernant le fonctionnement de ce wiki ont été détectés et vont être supprimés ou modifiés dans une prochaine version.",
+ "smw-admin-deprecation-notice-title-replacement": "Paramètres remplacés ou renommés",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "La section suivante contient des paramètres qui ont été renommés ou modifiés par ailleurs et il est recommandé de mettre à jour immédiatement leur nom ou leur format.",
+ "smw-admin-deprecation-notice-title-removal": "Paramètres supprimés",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Les paramètres affichés ont été supprimés dans une version antérieure mais sont encore utilisés sur ce wiki.",
+ "smw-smwadmin-refresh-title": "Réparation de données et mise à jour",
+ "smw_smwadmin_datarefresh": "Reconstruction des données",
+ "smw_smwadmin_datarefreshdocu": "Il est possible de restaurer toutes les données Semantic MediaWiki à partir du contenu courant de ce wiki.\nCeci peut être utile pour réparer des données corrompues ou pour rafraîchir les données si le format interne a changé lors des mises à niveau.\nLa mise à jour est exécutée page par page et se sera pas achevée immédiatement.\nLa page suivante spécifie si une mise à jour est en cours d’exécution et vous permet de commencer ou d’arrêter celle-ci (à moins que cette fonctionnalité n’ait été désactivée par l’administrateur du site).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Une mise à jour est déja en cours d’exécution.</strong>\nIl est normal qu’une mise à jour progresse lentement parce qu’elle ne rafraîchit les données que par petits tronçons, au fur et à mesure qu’un utilisateur accède au wiki.\nPour terminer cette mise à jour plus rapidement, vous pouvez invoquer le script d’entretien Mediawiki <code>runJobs.php</code> (utilisez l’option <code>--maxjobs 1000</code> pour restreindre le nombre de mises à jour par script lancé).\nProgression estimée de la mise à jour actuelle :",
+ "smw_smwadmin_datarefreshbutton": "Planifier la reconstruction des données",
+ "smw_smwadmin_datarefreshstop": "Arrêter cette mise à jour",
+ "smw_smwadmin_datarefreshstopconfirm": "Oui, j’en suis {{GENDER:$1|sûr|sûre}}.",
+ "smw-admin-job-scheduler-note": "Les tâches (celles activées) dans cette section sont effectuées via la file de travaux pour éviter des situations de verrou mortel pendant leur exécution. La [https://www.mediawiki.org/wiki/Manual:Job_queue file des travaux] est responsable de l’exécution et il est critique que le script de maintenance <code>runJobs.php</code> ait une capacité appropriée (voir aussi le paramètre de configuration <code>$wgRunJobsAsync</code>).",
+ "smw-admin-outdateddisposal-title": "Élimination des entités désuètes",
+ "smw-admin-outdateddisposal-intro": "Certaines activités (une modification d’un type de propriété, la suppression de pages wiki, ou la correction de valeurs erronées) créeront [https://www.semantic-mediawiki.org/wiki/Outdated_entities des entités désuètes] et il est conseillé de les supprimer périodiquement pour libérer l’espace occupé par leur tables.",
+ "smw-admin-outdateddisposal-active": "Une tâche d’élimination des entités désuètes a été planifiée.",
+ "smw-admin-outdateddisposal-button": "Programmer un nettoyage",
+ "smw-admin-feature-disabled": "Cette fonctionnalité a été désactivée sur ce wiki ; veuillez consulter la page d’aide <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">paramètres</a> ou contacter un administrateur système.",
+ "smw-admin-propertystatistics-title": "Reconstruction des statistiques de propriété",
+ "smw-admin-propertystatistics-intro": "Reconstruit toutes les statistiques d’utilisation des propriétés, et donc met à jour et corrige les [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count compteurs d’utilisation] des propriétés.",
+ "smw-admin-propertystatistics-active": "Une tâche de reconstruction de propriété a été planifiée.",
+ "smw-admin-propertystatistics-button": "Programmer une reconstruction de statistiques",
+ "smw-admin-fulltext-title": "Reconstruction de recherche de texte intégral",
+ "smw-admin-fulltext-intro": "Reconstruit l’index de recherche à partir des tables de propriété avec un type de données ayant « [https://www.semantic-mediawiki.org/wiki/Full-text recherche en texte intégral] » activé. Les modifications des règles d’indexation (mots ''séparateurs'' modifiés, nouvelles racines, etc...) ou d'une table récemment ajoutée ou modifiée nécessitent de relancer cette tâche.",
+ "smw-admin-fulltext-active": "Une tâche de reconstruction de la recherche en texte intégral a été planifiée.",
+ "smw-admin-fulltext-button": "Programmer une reconstruction de texte intégral",
+ "smw-admin-support": "Obtenir de l’aide",
+ "smw-admin-supportdocu": "Diverses ressources sont fournies pour vous aider en cas de problèmes :",
+ "smw-admin-installfile": "Si vous rencontrez des problèmes lors de votre installation, commencez par regarder le guide en ligne dans le <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">fichier INSTALL</a> et la <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">page d’installation</a>.",
+ "smw-admin-smwhomepage": "La documentation complète de l’utilisateur de Semantic MediaWiki se trouve sur <b><a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_-_Page_d%27accueil\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Les bogues peuvent être signalés sur le <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">suivi de problèmes</a>, la page de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">signalement des bogues</a> fournit des indications sur la manière d’écrire un rapport de problème efficace.",
+ "smw-admin-questions": "Si vous avez d’autres questions ou des suggestions, joignez-vous à la discussion sur <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">la liste de discussion des utilisateurs</a> de MediaWiki Sémantique ou la <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">zone de discussion</a>.",
+ "smw-admin-other-functions": "Autres fonctions",
+ "smw-admin-supplementary-section-title": "Fonctions supplémentaires",
+ "smw-admin-supplementary-section-subtitle": "Fonctions système",
+ "smw-admin-supplementary-section-intro": "Cette section fournit des fonctions supplémentaires au-delà du domaine de maintenance, et il est possible que certaines fonctions qui sont listées dans la [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentation] soient restreintes ou indisponibles, et donc inaccessibles sur ce wiki.",
+ "smw-admin-supplementary-settings-title": "Options de configuration",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> renvoie une liste collective de paramètres disponibles utilisés dans Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Statistiques opérationnelles",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> affiche un ensemble étendu de statistiques",
+ "smw-admin-supplementary-idlookup-title": "Recherche et libération d’objet",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contient des fonctions de recherche et de libération d'objets individuels",
+ "smw-admin-supplementary-duplookup-title": "Entités dupliquées",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> pour afficher les entrées qui sont marquées comme contenant des doublons dans la table des entités",
+ "smw-admin-supplementary-duplookup-docu": "Cette page répertorie les entrées de la [https://www.semantic-mediawiki.org/wiki/Help:Entity_table table des entités] classées comme doublons. Les entrées dupliquées ne doivent (le cas échéant) se produire que dans de rares occasions potentiellement provoquées soit par un processus interrompu lors d'une mise à jour de base de données, soit d'une transaction d'annulation infructueuse.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Statistiques du cache",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> affiche les statistiques relatives au cache",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Fonctions de Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> afiche les paramètres et les statistiques d’index",
+ "smw-admin-supplementary-elastic-docu": "Cette page contient des informations sur les paramètres, les correspondances, la santé et les statistiques d’index liées à une grappe Elasticsearch qui est connectée à MédiaWiki Sémantique et son [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Fonctions supportées",
+ "smw-admin-supplementary-elastic-settings-title": "Paramètres",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> utilisé par Elasticsearch pour gérer les index de MédiaWiki Sémantique",
+ "smw-admin-supplementary-elastic-mappings-title": "Correspondances",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> pour lister les index et les correspondances de champ",
+ "smw-admin-supplementary-elastic-mappings-docu": "Cette page contient les détails de correspondance des champs tels qu’utilisés avec les indices actuels. Le résumé de la correspondance doit être surveillé en lien avec <code>index.mapping.total_fields.limit</code> qui spécifie le nombre maximal de champs dans un index.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Résumé",
+ "smw-admin-supplementary-elastic-mappings-fields": "Correspondances de champ",
+ "smw-admin-supplementary-elastic-nodes-title": "NÅ“uds",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> affiche les statistiques de nœud",
+ "smw-admin-supplementary-elastic-indices-title": "Index",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> fournit une vue d’ensemble des index disponibles et de leurs statistiques",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistiques",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> affiche les statistiques de niveau index",
+ "smw-admin-supplementary-elastic-statistics-docu": "Cette page fournit une vision des statistiques d’index pour différentes opérations qui se produisent à un niveau index, les statistiques renvoyées sont agrégées avec les agrégats primaires et totaux. La [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html page d’aide] contient une description détaillée des statistiques d’index disponibles.",
+ "smw-admin-supplementary-elastic-status-replication": "Etat de réplication",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Dernière réplication active: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalle de mise à jour: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Carnet de commandes de tâches de récupération : $1 (estimation)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Intégrer (le fichier) de carnet de commande de tâches : $1 (estimation)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Réplication verrouillée : $1 (reconstruction en cours)",
+ "smw-list-count": "La liste contient $1 entrée{{PLURAL:$1||s}}.",
+ "smw-list-count-from-cache": "La liste contient $1 entrées{{PLURAL:$1||s}} venant du cache (UTC: $2).",
+ "smw-property-label-uniqueness": "L’étiquette « $1 » représente également au moins une autre propriété. Veuillez vous reporter à la [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness page d’aide] pour trouver comment résoudre ce problème.",
+ "smw-property-label-similarity-title": "Compte rendu de similitude des étiquettes de propriété",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcule les similitudes pour les étiquettes de propriété existantes",
+ "smw-property-label-similarity-threshold": "Seuil:",
+ "smw-property-label-similarity-type": "Afficher l'ID du type",
+ "smw-property-label-similarity-noresult": "Aucun résultat trouvé pour les options sélectionnées.",
+ "smw-property-label-similarity-docu": "Compare et affiche la [https://www.semantic-mediawiki.org/wiki/Property_similarity similarité syntaxique] (pas la ressemblance sémantique) entre les deux étiquettes de propriété ce qui peut aider à filtrer les propriétés mal orthographiées ou équivalentes qui représentent le même concept (voir la page spéciale [[Special:Properties|propriétés]] afin de clarifier le concept et l'utilisation des propriétés signalées). Le seuil peut être réglé soit pour élargir ou rétrécir le degré de similitude. <code>[[Property:$1|$1]]</code> est défini pour exclure les propriétés de l’analyse.",
+ "smw-admin-operational-statistics": "Cette page contient des statistiques opérationnelles collectées dans ou par l’intermédiaire des fonctions relatives à Semantic MediaWiki. Une liste étendue des statistiques spécifiques au wiki peut être trouvée [[Special:Statistics|<b>ici</b>]].",
+ "smw_adminlinks_datastructure": "Structure des données",
+ "smw_adminlinks_displayingdata": "Affichage des données",
+ "smw_adminlinks_inlinequerieshelp": "Aide sur les requêtes intégrées",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Nombre d’utilisations] estimé : {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propriété définie par l{{PLURAL:$1|'utilisateur|e système}}",
+ "smw-property-indicator-last-count-update": "Estimation du nombre d'utilisations\nDernière mise à jour: $1",
+ "smw-concept-indicator-cache-update": "Compteur de cache\nDernière mise à jour : $1",
+ "smw-createproperty-isproperty": "C’est une propriété de type $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|La valeur autorisée pour cette propriété est|Les valeurs autorisées pour cette propriété sont}} :",
+ "smw-paramdesc-category-delim": "Le délimiteur",
+ "smw-paramdesc-category-template": "Un modèle pour mettre en forme les éléments",
+ "smw-paramdesc-category-userparam": "Un paramètre à passer au modèle",
+ "smw-info-par-message": "Message à afficher.",
+ "smw-info-par-icon": "Icône à afficher, soit « info » ou « attention ».",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Options générales",
+ "prefs-ask-options": "Options de Special:Ask",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (et ses extensions) permettent d'adapter individuellement certaines fonctions sélectionnées. Veuillez consulter la [https://www.semantic-mediawiki.org/wiki/Help:User_preferences page d'aide] pour plus de détails.",
+ "smw-prefs-ask-options-tooltip-display": "Afficher le texte du paramètre comme info-bulle",
+ "smw-prefs-ask-options-compact-view-basic": "Activer l’affichage compact de base",
+ "smw-prefs-help-ask-options-compact-view-basic": "Si activé, affiche un ensemble réduit de liens sur l’affichage compact Special:Ask",
+ "smw-prefs-general-options-time-correction": "Activer la correction de l'horodatage pour les pages spéciales en utilisant la préférence du [[Special:Preferences#mw-prefsection-rendering|décalage horaire]]",
+ "smw-prefs-general-options-jobqueue-watchlist": "Afficher la liste de suivi de la file des tâches dans ma barre personnelle",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Si activé, affiche une [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist liste] de tâches sélectionnées en attente, avec la taille estimée de leur file.",
+ "smw-prefs-general-options-disable-editpage-info": "Désactiver le texte d'introduction de la page de modification",
+ "smw-prefs-general-options-disable-search-info": "Désactiver l’information d’aide à la syntaxe sur la page de recherche standard",
+ "smw-prefs-general-options-suggester-textinput": "Activer l’assistance à la saisie pour les entités sémantiques",
+ "smw-prefs-help-general-options-suggester-textinput": "Si activé, permet d’utiliser une [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance aide à la saisie] pour trouver des propriétés, des concepts et des catégories depuis un contexte de saisie.",
+ "smw-ui-tooltip-title-property": "Propriété",
+ "smw-ui-tooltip-title-quantity": "Conversion d’unité",
+ "smw-ui-tooltip-title-info": "Information",
+ "smw-ui-tooltip-title-service": "Liens de service",
+ "smw-ui-tooltip-title-warning": "Avertissement",
+ "smw-ui-tooltip-title-error": "Erreur",
+ "smw-ui-tooltip-title-parameter": "Paramètre",
+ "smw-ui-tooltip-title-event": "Évènement",
+ "smw-ui-tooltip-title-note": "Note",
+ "smw-ui-tooltip-title-legend": "Légende",
+ "smw-ui-tooltip-title-reference": "Référence",
+ "smw_unknowntype": "Le type « $1 » de cette propriété n'est pas valide",
+ "smw-concept-cache-text": "Le concept a un total de $1 {{PLURAL:$1|page|pages}}, et a été mis à jour la dernière fois le $2 à $3.",
+ "smw_concept_header": "Page du concept « $1 »",
+ "smw_conceptarticlecount": "Afficher ci-dessous $1 {{PLURAL:$1|page|pages}}.",
+ "smw-qp-empty-data": "Les données demandées n’ont pas pu être affichées à cause de quelques critères de sélection insuffisants.",
+ "right-smw-admin": "Tâches d’administration des accès (MédiaWiki Sémantique)",
+ "right-smw-patternedit": "Modifier l’accès pour gérer les expressions rationnelles et les motifs autorisés (Médiawiki Sémantique)",
+ "right-smw-pageedit": "Changer l'accès pour les pages annotées <code>Protégé contre la modification</code> (Semantic MediaWiki)",
+ "right-smw-ruleedit": "Modifier les pages de règle (MédiaWiki Sémantique)",
+ "restriction-level-smw-pageedit": "protégé (seulement les utilisateurs éligibles)",
+ "action-smw-patternedit": "modifier les expressions rationnelles utilisées par Médiawiki Sémantique",
+ "action-smw-pageedit": "modifier les pages annotées avec la propriété <code>Is edit protected</code> (Semantic MediaWiki)",
+ "group-smwadministrator": "Administrateurs (MédiaWiki Sémantique)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrateur|administratrice}} (MédiaWiki Sémantique)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administrators (MédiaWiki Sémantique)",
+ "group-smwcurator": "Conservateurs (Médiawiki Sémantique)",
+ "group-smwcurator-member": "{{GENDER:$1|conservateur|conservatrice}} (Médiawiki Sémantique)",
+ "grouppage-smwcurator": "{{ns:project}}:Curators (Semantic MediaWiki)",
+ "action-smw-admin": "tâches d’administration des accès à MédiaWiki Sémantique",
+ "action-smw-ruleedit": "modifier les pages de règle (MédiaWiki Sémantique)",
+ "smw-property-predefined-default": "« $1 » est une propriété prédéfinie.",
+ "smw-property-predefined-common": "Cette propriété est pré-déployée (appelée aussi [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriété spéciale]) et est accompagnée de droits d’administration supplémentaires, mais peut être utilisée exactement comme une autre [https://www.semantic-mediawiki.org/wiki/Property propriété définie par l’utilisateur].",
+ "smw-property-predefined-ask": "« $1 » est une propriété prédéfinie qui représente les méta-informations (sous la forme d'un [https://www.semantic-mediawiki.org/wiki/Subobject sous-objet]) des requêtes individuelles et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-asksi": "« $1 » est une propriété prédéfinie qui recueille le nombre de conditions utilisées dans une requête et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-askde": "« $1 » est une propriété prédéfinie qui informe sur la profondeur d’une requête et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-long-askde": "C’est une valeur numérique calculée d’après les sous-requêtes imbriquées, les chaînes de propriétés, et les éléments de description disponibles avec l’exécution d’une requête restreinte par le paramètre de configuration <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "« $1 » est une propriété prédéfinie décrivant les paramètres qui influencent un résultat de recherche et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Médiawiki Sémantique].",
+ "smw-property-predefined-long-askpa": "C’est une partie de la collection des propriétés qui spécifient un [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler profil de recherche].",
+ "smw-sp-properties-docu": "Cette page liste [https://www.semantic-mediawiki.org/wiki/Property les propriétés] et leur compteur d’utilisation disponible pour ce wiki. Pour avoir des statistiques de comptage à jour, il est recommandé que le script de maintenance [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics des statistiques de propriété] soit lancé régulièrement. Pour un affichage différencié, voyez les pages spéciales [[Special:UnusedProperties|propriétés non utilisées]] ou [[Special:WantedProperties|propriétés souhaitées]].",
+ "smw-sp-properties-cache-info": "Les données listées ont été récupérées depuis [https://www.semantic-mediawiki.org/wiki/Caching le cache], et ont été mises à jour le $1.",
+ "smw-sp-properties-header-label": "Liste des propriétés",
+ "smw-admin-settings-docu": "Affiche une liste de tous les paramètres par défaut et des paramètres localisés qui se rapportent à l’environnement Médiawiki Sémantique. Pour le détail des paramètres individuels, veuillez consulter la page d’aide de [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration].",
+ "smw-sp-admin-settings-button": "Générer la liste des paramètres",
+ "smw-admin-idlookup-title": "Recherche",
+ "smw-admin-idlookup-docu": "Cette section décrit les détails techniques concernant des entités individuelles (page wiki, sous-objet, propriété, etc.) dans MédiaWiki Sémantique. L’entrée peut être un ID numérique ou une valeur de chaîne correspondant au champ de recherche approprié, cependant tout ID de référence est relatif à MédiaWiki Sémantique et non à une page de MédiaWiki ou à un ID de révision.",
+ "smw-admin-iddispose-title": "Libéré",
+ "smw-admin-iddispose-docu": "À noter que l’opération d’élimination n’est pas restreinte et supprimera l’entité du moteur de stockage en même temps que toutes ses références dans les tables associées, si elle est confirmée. Veuillez effectuer cette tâche avec <strong>précaution</strong> et seulement après avoir consulté la [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentation].",
+ "smw-admin-iddispose-done": "L’ID « $1 » a été supprimé du serveur de stockage.",
+ "smw-admin-iddispose-references": "ID « $1 » possède {{PLURAL:$2|aucune|au moins une}} référence active:",
+ "smw-admin-iddispose-references-multiple": "Liste des concordances avec au moins un enregistrement de référence actif.",
+ "smw-admin-iddispose-no-references": "La recherche n'a pu trouver une entrée de table pour « $1 » .",
+ "smw-admin-idlookup-input": "Rechercher :",
+ "smw-admin-objectid": "ID :",
+ "smw-admin-tab-general": "Résumé",
+ "smw-admin-tab-notices": "Informations d'obsolescence",
+ "smw-admin-tab-maintenance": "Maintenance",
+ "smw-admin-tab-supplement": "Fonctions supplémentaires",
+ "smw-admin-tab-registry": "Registre",
+ "smw-admin-maintenance-no-description": "Aucune description.",
+ "smw-admin-maintenance-script-section-title": "Liste des scripts de maintenance disponibles",
+ "smw-admin-maintenance-script-section-intro": "Les scripts de maintenance suivants nécessitent un administrateur et l’accès à la ligne de commande pour pouvoir exécuter les scripts listés.",
+ "smw-admin-maintenance-script-description-dumprdf": "Export en RDF des triplets existants.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "Ce script est utilisé pour gérer les caches de concept pour MédiaWiki Sémantique, où il peut créer, supprimer et mettre à jour les caches sélectionnés.",
+ "smw-admin-maintenance-script-description-rebuilddata": "Recrée toutes les données sémantiques dans la base de données, en bouclant à travers toutes les pages qui ont des données sémantiques.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "Reconstruit l’index de Elasticsearch(pour les installations qui utilisent <code>ElasticStore</code>), en bouclant à travers toutes les entités qui ont des données sémantiques.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "Reconstruit l’index de recherche en texte brut <code>SQLStore</code> (pour les installations où le paramètre a été activé).",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "Reconstruit les statistiques d’utilisation pour toutes les entités de propriété.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "Supprime les entités en doublon trouvées dans les tables sélectionnées qui n’ont pas de références actives.",
+ "smw-admin-maintenance-script-description-setupstore": "Définit le serveur de stockage sélectionné dans <code>LocalSettings.php</code>.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "Met à jour le champ <code>smw_sort</code> dans <code>SQLStore</code> (en accord avec le paramètre [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "Remplit le champ <code>smw_hash</code> pour les lignes sans valeur.",
+ "smw-livepreview-loading": "Chargement en cours…",
+ "smw-sp-searchbyproperty-description": "Cette page fournit une simple [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interface de navigation] pour trouver des entités décrites par une propriété et une valeur nommée. D’autres interfaces de recherche disponibles comprennent la [[Special:PageProperty|page recherche de propriété]], et le [[Special:Ask|constructeur de requêtes ask]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Liste de résultats",
+ "smw-sp-searchbyproperty-nonvaluequery": "Une liste de valeurs qui ont la propriété « $1 » affectée.",
+ "smw-sp-searchbyproperty-valuequery": "Une liste de pages qui ont la propriété « $1 » avec la valeur « $2 » annotée.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" ne peut pas être attribué à un type de déclaration de numéro avec une valeur de $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" rendu avec \"NULL\" qui n'est pas une valeur numérique autorisée.",
+ "smw-editpage-annotation-enabled": "Cette page prend en charge les annotations sémantiques à l’intérieur d’un texte (par exemple <nowiki>« [[Is specified as::World Heritage Site]] »</nowiki>) afin de bâtir un contenu structuré et permettant d’y faire des requêtes, cette fonction étant assurée par Semantic MediaWiki. Pour une description complète sur la façon d’utiliser les annotations ou la fonction d’analyse #ask, veuillez voir les pages d’aide [https://www.semantic-mediawiki.org/wiki/Help:Getting_started Pour commencer], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation Annotation à l’intérieur d’un texte] et [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries Requêtes incorporées].",
+ "smw-editpage-annotation-disabled": "Cette page n’autorise pas les annotations de sémantique dans le texte en raison des restrictions de l’espace de noms. Les détails sur la façon d’activer l’espace de noms peuvent être trouvés sur la page d’aide de la [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration].",
+ "smw-editpage-property-annotation-enabled": "Cette propriété peut être étendue en utilisant des annotations sémantiques afin de spécifier un type de donnée (par exemple <nowiki> « [[Has type::Page]] » </nowiki>) ou d'autres déclarations secondaires (par exemple <nowiki> « [[Subproperty of::dc:date]] » </nowiki>). Pour une description sur la façon de faire pour enrichir cette page, veuillez consulter les pages d'aide concernant [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration la déclaration de propriété] ou [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes la liste des types de données disponibles].",
+ "smw-editpage-property-annotation-disabled": "Cette propriété ne peut pas être étendue avec une annotation de genre datatype (par exemple <nowiki> \"[[Has type::Page]]\"</nowiki>) parce qu'elle est déjà prédéfinie (voyez la page d'aide des [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriétés spéciales] pour plus d'information).",
+ "smw-editpage-concept-annotation-enabled": "Ce concept peut être étendu en utilisant la fonction #concept de l’analyseur. Pour une description sur la manière d'utiliser #concept, voyez la page d’aide [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept].",
+ "smw-search-syntax-support": "L’entrée recherchée accepte l’utilisation de la [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search syntaxe des requêtes] sémantiques pour aider à trouver des résultats via MédiaWiki Sémantique.",
+ "smw-search-input-assistance": "L'[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance assistant de saisie] est également activé pour permettre une présélection des propriétés et des catégories disponibles.",
+ "smw-search-help-intro": "Une entrée <code><nowiki>[[ ... ]]</nowiki></code> signalera au processeur de l’entrée d’utiliser le serveur de recherche MédiaWiki Sémantique. Il faut noter que combiner <code><nowiki>[[ ... ]]</nowiki></code> avec une recherche de texte non structuré comme <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code> n’est pas supporté.",
+ "smw-search-help-structured": "Recherches structurées :\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (comme [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context contexte filtré])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (avec un [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context contexte de recherche])",
+ "smw-search-help-proximity": "Les recherches de proximité (une propriété étant inconnue, disponible '''uniquement''' pour les serveurs qui fournisseur une intégration de recherche en texte intégral) :\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (rechercher tous les documents où « lorem » et « ipsum » ont été indexés)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (correspond à « lorem ipsum » en tant que phrase)",
+ "smw-search-help-ask": "Les liens suivants expliqueront comment utiliser la syntaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Sélection des pages] décrit comment sélectionner les pages et construire les conditions\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Opérateurs de recherche] liste les opérateurs de recherche disponibles, y compris ceux pour les requêtes de plage et de jokers",
+ "smw-search-input": "Entrée et recherche",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Aide à la saisie] est fournie pour le champ de saisie et nécessite d’utiliser un des préfixes suivants :\n\n*<code>p:</code> pour activer les suggestions de propriété (par ex. <code><nowiki>[[p:Has …</nowiki></code>)\n\n*<code>c:</code> pour activer les suggestions de catégorie\n\n*<code>con:</code> pour activer les suggestions de concept",
+ "smw-search-syntax": "Syntaxe",
+ "smw-search-profile": "Étendu",
+ "smw-search-profile-tooltip": "Rechercher des fonctions en lien avec MédiaWiki Sémantique",
+ "smw-search-profile-sort-best": "Meilleure correspondance",
+ "smw-search-profile-sort-recent": "Plus récent",
+ "smw-search-profile-sort-title": "Titre",
+ "smw-search-profile-extended-help-intro": "Le [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile profil étendu] Special:Search fournit un accès aux fonctions de recherche spécifiques à MédiaWiki Sémantique et au serveur de requête supporté.",
+ "smw-search-profile-extended-help-sort": "Spécifie une préférence de tri pour l’affichage des résultats avec :",
+ "smw-search-profile-extended-help-sort-title": "*« Titre »  utilisant le titre de la page (ou le titre affiché) comme critère de tri",
+ "smw-search-profile-extended-help-sort-recent": "*« Plus récent » affichera d’abord les entités les plus récemment modifiées (les sous-objets seront supprimés, car ces entités ne sont pas annotées avec une [[Property:Modification date|date de modification]])",
+ "smw-search-profile-extended-help-sort-best": "*« Meilleure correspondance » triera les entités par [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy pertinence] selon les scores fournis par le serveur",
+ "smw-search-profile-extended-help-form": "Les formulaires sont fournis (s’ils sont maintenus) pour correspondre à des cas d’utilisation spécifiques, en exposant différentes propriétés et champs de saisie de valeur pour limiter le processus de saisie et faciliter le traitement d’une requête de recherche pour les utilisateurs (voir $1)",
+ "smw-search-profile-extended-help-namespace": "Le champ de sélection de l’espace de noms est masqué dès que le formulaire est sélectionné, mais il peut être rendu visible à l’aide du bouton « afficher/masquer ».",
+ "smw-search-profile-extended-help-search-syntax": "Le champ de saisie de recherche supporte l’utilisation de la syntaxe <code>#ask</code> pour définir un contexte de recherche spécifique de MédiaWiki Sémantique. Les expressions utiles comprennent :",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code> pour trouver quelque chose qui contient « … » et est particulièrement utile quand le contexte de recherche ou les propriétés impliquées sont inconnus (par ex. <code>in:(lorem && ipsum)</code> est équivalent à <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>).",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code> pour trouver quelque chose qui contient « … » exactement dans le même ordre",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code> pour correspondre à une entité avec une propriété « … » (par ex. <code>has:(Foo && Bar)</code> est équivalent à <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> pour ne pas correspondre à une entité qui inclut « … »",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Des préfixes personnalisés supplémentaires sont disponibles et définis, comme : $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Certaines expressions sont réservées, comme : <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''Certaines des opérations listées ne sont utiles qu’en lien avec un index de plein texte activé ou l’ElasticStore.''",
+ "smw-search-profile-extended-help-query": "<code><nowiki>$1</nowiki></code> utilisé comme requête.",
+ "smw-search-profile-extended-help-query-link": "(Pour plus de détails $1).",
+ "smw-search-profile-extended-help-find-forms": "formulaires disponibles",
+ "smw-search-profile-extended-section-sort": "Trié par",
+ "smw-search-profile-extended-section-form": "Formulaires",
+ "smw-search-profile-extended-section-search-syntax": "Rechercher l'entrée",
+ "smw-search-profile-extended-section-namespace": "Espace de noms",
+ "smw-search-profile-extended-section-query": "Requête",
+ "smw-search-profile-link-caption-query": "voir",
+ "smw-search-show": "Afficher",
+ "smw-search-hide": "Masquer",
+ "log-name-smw": "Sémantique du journal MediaWiki",
+ "log-show-hide-smw": "Journal de MédiaWiki Sémantique $1",
+ "logeventslist-smw-log": "Journal de Semantic MediaWiki",
+ "log-description-smw": "Activités pour [https://www.semantic-mediawiki.org/wiki/Help:Logging les types d’événement activés] qui ont été signalés par MédiaWiki Sémantique et ses composants.",
+ "logentry-smw-maintenance": "Événements relatifs à la maintenance émis par MédiaWiki Sémantique",
+ "smw-datavalue-import-unknown-namespace": "L’espace de noms d’importation « $1 » est inconnu. Assurez-vous que les détails de l’importation OWL sont disponibles wia [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "Impossible de trouver un espace de nom d’URI « $1 » dans l’[[MediaWiki:Smw import $1|importation $1]].",
+ "smw-datavalue-import-missing-type": "Pas de définition de type trouvée pour \"$1\" dans le [[MediaWiki:Smw import $2|$2 import]]..",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|importer $1]]",
+ "smw-datavalue-import-invalid-value": "« $1 » n’est pas un format valide et doit consister en « espace_de_nom »:« identifiant » (par ex. « foaf:name »).",
+ "smw-datavalue-import-invalid-format": "La chaîne « $1 » doit pouvoir être divisée en quatre parties, mais le format n’a pas été compris.",
+ "smw-property-predefined-impo": "« $1 » est une propriété prédéfinie qui décrit une relation vers un [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulaire importé] et qui est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-type": "« $1 » est une propriété prédéfinie qui décrit le [[Special:Types|type de donnée]] d’une propriété et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-sobj": "« $1 » est une propriété prédéfinie représentant un constructeur de [https://www.semantic-mediawiki.org/wiki/Help:Container conteneur], et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-long-sobj": "Le conteneur permet d’accumuler les affectations des valeurs de propriété de la même façon qu’une page wiki normale, mais dans un espace d’entités différent qui sera lié au sujet incorporé.",
+ "smw-property-predefined-errp": "« $1 » est une propriété prédéfinie pour tracer les erreurs d’entrée pour les valeurs d’annotation incorrectes et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-long-errp": "Dans la plupart des cas, c’est causé par une différence de type ou une restriction sur la [[Property:Allows value|valeur]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value « $1 »] est une propriété prédéfinie qui peut définir une liste de valeurs autorisées pour limiter l'ensemble des valeurs possibles d'une propriété et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list « $1 »] est une propriété prédéfinie qui peut spécifier une référence à une liste contenant les valeurs permises pour limiter les affectations de valeur pour une propriété, et qui est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Médiawiki Sémantique].",
+ "smw-datavalue-property-restricted-annotation-use": "La propriété « $1 » a une zone d’application restreinte et ne peut pas être utilisée comme propriété d’annotation par un utilisateur.",
+ "smw-datavalue-property-restricted-declarative-use": "La propriété « $1 » est une propriété déclarative et peut être utilisée seulement sur une page de propriété ou de catégorie.",
+ "smw-datavalue-property-create-restriction": "La propriété « $1 » n’existe pas et l’utilisateur n’a pas le droit « $2 » (voir [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode mode d’autorisation]) pour créer ou annoter des valeurs avec une propriété non approuvée.",
+ "smw-datavalue-property-invalid-character": "« $1 » contient un caractère désigné « $2 » dans un libellé de propriété, et a été classé conséquemment comme non valide.",
+ "smw-datavalue-property-invalid-chain": "L’utilisation de « $1 » comme chaîne de propriété n’est pas autorisée lors du processus d’annotation.",
+ "smw-datavalue-restricted-use": "La valeur de donnée « $1 » est marquée à usage restreint.",
+ "smw-datavalue-invalid-number": "\"$1\" ne peut pas être interprété comme un nombre.",
+ "smw-query-condition-circular": "Une condition circulaire possible a été détectée dans \"$1\".",
+ "smw-query-condition-empty": "La description de la requête contient une condition vide.",
+ "smw-types-list": "Liste des types de données",
+ "smw-types-default": "\"$1\" est un type de données interne.",
+ "smw-types-help": "De plus amples informations et des exemples peuvent être trouvés sur la [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 page d'aide].",
+ "smw-type-anu": "« $1 » est une variante du type de données [[Special:Types/URL|URL]] principalement utilisée pour une déclaration d'exportation de type \"owl:AnnotationProperty\" .",
+ "smw-type-boo": "« $1 » est un type de données de base pour décrire une valeur vraie/fausse.",
+ "smw-type-cod": "« $1 » est une variante du type de données [[Special:Types/Text|Text]] à utiliser pour les textes techniques de longueur quelconque, comme du code source.",
+ "smw-type-geo": "« $1 » est un type de données qui décrit des emplacements géographiques et nécessite l’extension [https://www.semantic-mediawiki.org/wiki/Extension:Maps « Maps »] .",
+ "smw-type-tel": "« $1 » est un type de données spécial pour décrire les numéros de téléphone internationaux conformément à la RFC 3966.",
+ "smw-type-txt": "« $1 » est un type de données de base pour décrire les chaînes de caractères de longueur arbitraire.",
+ "smw-type-dat": "« $1 » est un type de données de base pour représenter des points dans le temps, dans un format unifié.",
+ "smw-type-ema": "« $1 » est un type de données spécial servant à représenter un courriel.",
+ "smw-type-tem": "« $1 » est un type de données numérique spécial servant à représenter une temperature.",
+ "smw-type-qty": "« $1 » est un type de données servant à décrire les quantités avec une représentation numérique et une unité de mesure.",
+ "smw-type-rec": "« $1 » est un type de données conteneur qui spécifie une liste de propriétés typées dans un ordre fixe.",
+ "smw-type-extra-tem": "Le schéma de conversion contient des unités supportées telles que Kelvin, Celsius, Fahrenheit, et Rankine.",
+ "smw-type-tab-properties": "Propriétés",
+ "smw-type-tab-types": "Types",
+ "smw-type-tab-errors": "Erreurs",
+ "smw-type-primitive": "De base",
+ "smw-type-contextual": "Contextuel",
+ "smw-type-compound": "Composé",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Cette page fournit une interface de navigation pour trouver toutes les valeurs d’une propriété et une page donnée. D’autres interfaces de recherche disponibles incluent la [[Special:SearchByProperty|recherche de propriété]], et le [[Special:Ask|constructeur de requête ask]].",
+ "smw-property-predefined-errc": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] et représentant les erreurs qui sont apparues lors de la connexion du fait de mauvaises annotations de valeurs ou de traitements de l’entrée.",
+ "smw-property-predefined-long-errc": "Les erreurs sont enregistrées dans un [https://www.semantic-mediawiki.org/wiki/Help:Container conteneur] qui peut inclure une référence vers la propriété qui a causé le problème.",
+ "smw-property-predefined-errt": "« $1 » est une propriété prédéfinie contenant une description textuelle d’une erreur et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-subobject-parser-invalid-naming-scheme": "Un sous-objet défini par l’utilisateur contenait un schéma de nommage invalide. L’utilisation d’un point ($1) dans les cinq premiers caractères est réservée aux extensions. Vous pouvez définir un [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identifiant nommé].",
+ "smw-datavalue-record-invalid-property-declaration": "La définition de l’enregistrement contient la propriété « $1 », elle-même est déclarée de type «  enregistrement  », ce qui est interdit.",
+ "smw-property-predefined-mdat": "« $1 » est une propriété prédéfinie qui correspond à la date de la dernière modification d’un sujet et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-cdat": "« $1 » est une propriété prédéfinie qui correspond à la date de première révision d’un sujet et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-newp": "« $1 » est une propriété prédéfinie qui indique si un sujet est nouveau ou non, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-ledt": "« $1 » est une propriété prédéfinie qui contient le nom de la page de l’utilisateur qui a créé la dernière révision, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-mime": "« $1 » est une propriété prédéfinie qui décrit le type de MIME d'un fichier téléchargé et qui est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-media": "« $1 » est une propriété prédéfinie qui décrit le type de média d’un fichier téléchargé, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-askfo": "« $1 » est une propriété prédéfinie qui contient le nom du format de résultat utilisé dans une requête, et est fourni par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-askst": "« $1 » est une propriété prédéfinie qui décrit les conditions de la requête sous forme de chaîne, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-askdu": "« $1 » est une propriété prédéfinie contenant une valeur temporelle (en secondes) représentant le temps pris par la requête pour achever son exécution, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-asksc": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] qui identifie des sources de requêtes alternatives (par ex. distantes, fédérées).",
+ "smw-property-predefined-askco": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour décrire l'état d'une requête ou de ses composants.",
+ "smw-property-predefined-long-askco": "Le nombre ou les chiffres attribués représentent un état interne codifié expliqué sur la [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler page d’aide].",
+ "smw-property-predefined-prec": "« $1 » est une propriété prédéfinie qui décrit une [https://www.semantic-mediawiki.org/wiki/Help:Display_precision précision d’affichage] (chiffres décimaux) pour les types de données numériques.",
+ "smw-property-predefined-attch-link": "« $1 » est une propriété prédéfinie qui recueille les liens des images et des fichiers inclus dans une page et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Extension:Maps L’extension « Maps »] n’a pas été détectée, donc « $1 » est restreint dans ses capacités de traitement.",
+ "smw-datavalue-monolingual-dataitem-missing": "Un élément attendu pour construire une valeur composée monolingue manque.",
+ "smw-datavalue-languagecode-missing": "Pour l’annotation « $1 », l’analyseur n’a pas pu déterminer un code de langue (par ex. « foo@en »).",
+ "smw-datavalue-languagecode-invalid": "« $1 » n’a pas été reconnu comme un code de langue pris en charge.",
+ "smw-property-predefined-lcode": "« $1 » est une propriété prédéfinie qui représente un code de langue au format BCP47 et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-type-mlt-rec": "« $1 » est un type de données [https://www.semantic-mediawiki.org/wiki/Help:Container conteneur] qui associe une valeur texte avec un [[Property:Language code|code de langue]] spécifique.",
+ "smw-types-extra-mlt-lcode": "Le type de données ne {{PLURAL:$2|nécessite un|nécessite pas de}} code de langue (par ex. {{PLURAL:$2|une annotation de valeur sans code de langue n’est pas acceptée|une annotation de valeur sans code de langue est acceptée}}).",
+ "smw-property-predefined-text": "« $1 » est une propriété prédéfinie qui représente un texte de longueur quelconque et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-pdesc": "« $1 » est une propriété prédéfinie qui permet de décrire une propriété dans le contexte d’une langue et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-list": "« $1 » est une propriété prédéfinie pour définir une liste de propriétés utilisée avec une propriété typée [[Special:Types/Record|enregistrement]], et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-limitreport-intext-parsertime": "[SMW] Temps de l’analyseur annoté dans le contenu",
+ "smw-limitreport-intext-postproctime": "[SMW] durée de post-traitement",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|seconde|secondes}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|seconde|secondes}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Temps de mise à jour de l’enregistrement (sur une purge de page)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|seconde|secondes}}",
+ "smw_allows_pattern": "Cette page est censée contenir une liste de références (suivie [https://fr.wikipedia.org/wiki/Expression_rationnelle d’expressions rationnelles]) et être mise à disposition via la propriété [[Property:Allows pattern|Permettre les motifs]]. Pour modifier cette page, le droit <code>smw-patternedit</code> est nécessaire.",
+ "smw-datavalue-allows-pattern-mismatch": "« $1 » a été classé comme non valide par l’expression rationnelle « $2 ».",
+ "smw-datavalue-allows-pattern-reference-unknown": "La référence de motif « $1 » ne peut être mise en correspondance avec aucune entrée dans [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "La référence de liste « $1 » ne correspondait pas à une page [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Il manque un élément de type liste avec le joker * pour définir le contenu de la liste « $1 ».",
+ "smw-datavalue-feature-not-supported": "La fonctionnalité « $1 » n’est pas prise en charge ou a été désactivée sur ce wiki.",
+ "smw-property-predefined-pvap": "« $1 » est une propriété prédéfinie qui peut spécifier une [[MediaWiki:Smw allows pattern|référence de motif]] pour appliquer [https://fr.wikipedia.org/wiki/Expression_rationnelle une expression rationnelle] correspondante, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Médiawiki Sémantique].",
+ "smw-property-predefined-dtitle": "« $1 » est une propriété prédéfinie qui peut affecter un titre affiché distinct à une entité, et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Médiawiki Sémantique].",
+ "smw-property-predefined-pvuc": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour limiter les affectations de valeur afin que chaque instance soit unique (ou une au plus).",
+ "smw-property-predefined-long-pvuc": "L’unicité est établie quand deux valeurs n’ont pas la même représentation littérale, et toute violation de cette contrainte sera catégorisée comme une erreur.",
+ "smw-datavalue-uniqueness-constraint-error": "La propriété « $1 » ne permet que des affectations de valeurs uniques, et « $2 » a déjà été annoté dans le sujet « $3 ».",
+ "smw-datavalue-uniqueness-constraint-isknown": "La propriété « $1 » ne permet que des annotations à valeur unique, « $2 » contient déjà une valeur affectée. « $3 » viole la contrainte d’unicité.",
+ "smw-property-predefined-boo": "« $1 » est un [[Special:Types/Boolean|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter les valeurs booléennes.",
+ "smw-property-predefined-num": "« $1 » est un [[Special:Types/Number|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter des valeurs numériques.",
+ "smw-property-predefined-dat": "« $1 » est un [[Special:Types/Date|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter des valeurs de date.",
+ "smw-property-predefined-uri": "« $1 » est un [[Special:Types/URL|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter des valeurs d’URI/URL.",
+ "smw-property-predefined-qty": "« $1 » est un [[Special:Types/Quantity|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter des valeurs de quantité.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "« $1 » contient un décalage et un identificateur de zone qui n'est pas pris en charge.",
+ "smw-datavalue-time-invalid-values": "La valeur « $1 » contient des informations non interprétables sous la forme « $2 ».",
+ "smw-datavalue-time-invalid-date-components-common": "« $1 » contient des informations non interprétables.",
+ "smw-datavalue-time-invalid-date-components-dash": "« $1 » contient un tiret superflu ou d’autres caractères qui ne sont pas valides pour interpréter une date.",
+ "smw-datavalue-time-invalid-date-components-empty": "« $1 » contient des composants vides.",
+ "smw-datavalue-time-invalid-date-components-three": "« $1 » contient plus de trois composants, qui sont nécessaires pour l’interprétation d’une date.",
+ "smw-datavalue-time-invalid-date-components-sequence": "« $1 » contient une séquence qui ne peut pas être interprétée avec une matrice de correspondance disponible pour les composants de date.",
+ "smw-datavalue-time-invalid-ampm": "« $1 » contient « $2 », en tant qu'élément de type heure et n'est pas valide d'après le format conventionnel sur 12 heures.",
+ "smw-datavalue-time-invalid-jd": "Impossible d’interpréter la valeur d’entrée « $1 » comme un nombre JD (jour julien) valide avec « $2 » en cours de signalement.",
+ "smw-datavalue-time-invalid-prehistoric": "Impossible d’interpréter une valeur d’entrée préhistorique « $1 ». Par exemple, avoir spécifié plus de tant d’années dans un modèle de calendrier peut renvoyer des résultats inattendus, dans un contexte préhistorique.",
+ "smw-datavalue-time-invalid": "Impossible d’interpréter la valeur d’entrée « $1 » comme une date ou un composant temporel valide « $2 » en cours de signalement.",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Il manque le paramètre « $1 » dans l’URI du formateur.",
+ "smw-datavalue-external-formatter-invalid-uri": "« $1 » est une URL non valide.",
+ "smw-datavalue-external-identifier-formatter-missing": "Il manque à la propriété l’affectation d’un [[Property:External formatter uri|« formateur externe d’URI »]] .",
+ "smw-datavalue-keyword-maximum-length": "La clé dépasse la valeur maximale de $1 {{PLURAL:$1|caractère|caractères}}.",
+ "smw-property-predefined-eid": "« $1 » est un [[Special:Types/External identifier|type]] et une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour représenter des identifiants externes.",
+ "smw-property-predefined-peid": "« $1 » est une propriété prédéfinie qui représente un identifiant externe et qui est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-pefu": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] pour spécifier une ressource externe avec un joker.",
+ "smw-property-predefined-long-pefu": "L’URI doit comprendre un élément à substituer qui sera complété avec une valeur d’[[Special:Types/External identifier|identifiant externe]] pour former une référence de ressource valide.",
+ "smw-type-eid": "« $1 » est une variante du type de données [[Special:Types/Text|Texte]] qui a besoin de propriétés particulières pour déclarer un [[Property:External formatter uri|formateur externe d’URI]].",
+ "smw-property-predefined-keyw": "« $1 » est une propriété prédéfinie et un [[Special:Types/Keyword|type]] fourni par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique] qui normalise un texte et a une longueur de chaîne réduite.",
+ "smw-type-keyw": "« $1 » est une variante du type de données [[Special:Types/Text|Texte]] qui a une longueur limitée et normalise la représentation de son contenu.",
+ "smw-datavalue-stripmarker-parse-error": "La valeur fournie « $1 » contient [https://en.wikipedia.org/wiki/Help:Strip_markers des marqueurs de bande] et ne peut donc pas être suffisamment analysée.",
+ "smw-datavalue-parse-error": "La valeur fournie « $1 » n’a pas été comprise.",
+ "smw-datavalue-propertylist-invalid-property-key": "La liste de propriétés « $1 » contient une clé de propriété « $2 » non valide.",
+ "smw-datavalue-type-invalid-typeuri": "Le type « $1 » ne peut pas être transformé en une représentation URI valide.",
+ "smw-datavalue-wikipage-missing-fragment-context": "La valeur d’entrée wikipage « $1 » ne peut pas être utilisée sans une page de contexte.",
+ "smw-datavalue-wikipage-invalid-title": "La valeur d’entrée du type de page « $1 » contient des caractères non valides ou est incomplète et peut donc provoquer des résultats inattendus lors d’une requête ou d’un processus d’annotation.",
+ "smw-datavalue-wikipage-property-invalid-title": "La propriété « $1 » (comme le type de page) avec la valeur d’entrée « $2 » contient des caractères non valides ou est incomplète, et donc peut provoquer des résultats inattendus lors d’une requête ou d’un processus d’annotation.",
+ "smw-datavalue-wikipage-empty": "La valeur d’entrée wikipage est vide (par ex. <code>[[SomeProperty::]], [[]]</code>) et ne peut donc pas être utilisée comme nom ou partie d’une condition de requête.",
+ "smw-type-ref-rec": "«$1» est un type de [https://www.semantic-mediawiki.org/wiki/Container conteneur] qui permet d'enregistrer des informations supplémentaires (par exemple, la provenance des données) lors d'une affectation de valeur.",
+ "smw-datavalue-reference-outputformat": "$1 : $2",
+ "smw-datavalue-reference-invalid-fields-definition": "Le type [[Special:Types/Reference|Référence]] attend la déclaration d'une liste de propriétés faite via l'attribut [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields possède des champs].",
+ "smw-parser-invalid-json-format": "L’analyseur JSON a renvoyé un « $1 ».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "« $1 » ne peut pas être utilisé comme libellé préféré parce que la langue « $2 » est déjà affectée au libellé « $3 ».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copier le lien dans le presse-papiers",
+ "smw-property-userdefined-fixedtable": "« $1 » a été configuré comme [https://www.semantic-mediawiki.org/wiki/Fixed_properties propriété fixe] et toute modification de la [https://www.semantic-mediawiki.org/wiki/Type_declaration déclacration de son type] a besoin soit d'exécuter <code>setupStore.php</code> soit de terminer la tâche spéciale [[Special:SemanticMediaWiki|\"Installation et de mise à niveau de la base de données\"]].",
+ "smw-data-lookup": "Récupération des données…",
+ "smw-data-lookup-with-wait": "La demande est en cours d'exécution et peut prendre un certain temps.",
+ "smw-no-data-available": "Aucune donnée disponible.",
+ "smw-property-req-violation-missing-fields": "Il manque les détails de déclaration pour la propriété « $1 » relatifs au type annoté « $2 » ce qui ne permet pas de définir la propriété <code>Comporte des champs</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "Il manque les détails de déclaration pour la propriété « $1 » relatifs au type annoté, ce qui ne permet pas de définir la propriété <code>URI du formateur externe</code>.",
+ "smw-property-req-violation-predefined-type": "La propriété « $1 », en tant que propriété prédéfinie, contient une déclaration de type « $2 » qui n’est pas compatible avec le type par défaut de cette propriété.",
+ "smw-property-req-violation-import-type": "Une déclaration de type a été détectée, qui est incompatible avec le type prédéfini du vocabulaire importé « $1 ». En général, il n’est pas nécessaire de déclarer un type, parce que l’information est récupérée lors de la définition de l’import.",
+ "smw-property-req-violation-change-propagation-locked-error": "La propriété « $1 » a été modifiée et les entités assignées doivent être réévaluées à l'aide d'un processus [https://www.semantic-mediawiki.org/wiki/Change_propagation de changement de propagation]. La page de propriétés a été verrouillée jusqu'à ce que la mise à jour de la spécification principale soit terminée pour éviter les interruptions intermédiaires ou les spécifications contradictoires. Le processus peut prendre un moment avant que la page puisse être déverrouillée car elle dépend de la taille et de la fréquence de l'ordonnanceur de la [https://www.mediawiki.org/wiki/Manual:Job_queue file d'attente des tâches].",
+ "smw-property-req-violation-change-propagation-locked-warning": "La propriété « $1 » a été modifiée et les entités assignées doivent être réévaluées à l'aide d'un processus de [https://www.semantic-mediawiki.org/wiki/Change_propagation changement de propagation]. La mise à jour peut prendre du temps car elle dépend de la taille et de la fréquence de l'ordonnanceur de la [https://www.mediawiki.org/wiki/Manual:Job_queue file d'attente des tâches] et il est suggéré de reporter les modifications apportées à la propriété pour éviter les interruptions intermédiaires ou les spécifications contradictoires.",
+ "smw-property-req-violation-change-propagation-pending": "Des mises à jour de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagation de modification] sont en attente ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tâche estimée|tâches estimées}}]) et il est recommandé d’attendre que le processus soit terminé avant de modifier une propriété, afin d'éviter des interruptions intermédiaires ou des spécifications contradictoires.",
+ "smw-property-req-violation-missing-maps-extension": "MédiaWiki Sémantique n’a pas pu détecter l’extension [https://www.semantic-mediawiki.org/wiki/Extension:Maps « Maps »], qui est un prérequis pour le type sélectionné et en conséquence limite la fonctionnalité de cette propriété.",
+ "smw-property-req-violation-type": "La propriété contient des spécifications de type concurrentes, ce qui peut aboutir à des annotations de valeur non valides ; il est donc attendu qu’un utilisateur affecte un type approprié.",
+ "smw-change-propagation-protection": "Cette page est bloquée pour éviter les modifications accidentelles des données pendant la mise à jour dûe aux [https://www.semantic-mediawiki.org/wiki/change_propagation propagations des changements]. Le processus peut prendre un moment avant que la page ne se débloque, puisqu'il dépend de la taille de la [https://www.mediawiki.org/wiki/manual:Job_queue file d'attente des tâches à exécuter] et de la fréquence du séquenceur.",
+ "smw-category-change-propagation-locked-error": "La catégorie « $1 » a été modifiée et nécessite que les entités affectées soient réévaluées en utilisant un traitement de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagation des modifications]. Pendant ce temps, la page de catégorie a été verrouillée jusqu’à ce que la mise à jour de la spécification principale soit terminée, de manière à éviter les interruptions intermédiaires ou les spécifications contradictoires. Le processus peut prendre un moment avant que la page puisse être déverrouillée, car il dépend de la taille et de la fréquence de l’ordonnanceur de la [https://www.mediawiki.org/wiki/Manual:Job_queue file des tâches].",
+ "smw-category-change-propagation-locked-warning": "La catégorie « $1 » a été modifiée et nécessite que les entités affectées soient réévaluées en utilisant un traitement de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagation de modification]. La mise à jour peut prendre un moment, car cela dépend de la taille et de la fréquence de l’ordonnanceur de la [https://www.mediawiki.org/wiki/Manual:Job_queue file des tâches], et il est conseillé de différer les modifications de la catégorie, pour éviter les interruptions intermédiaires ou les spécifications contradictoires.",
+ "smw-category-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation La propagation des modifications] de mise à jour sont en attente ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tache estimée|tâches estimées}}]), et il est recommandé d’attendre pour les modifications d’une catégorie que le traitement soit achevé, pour éviter les interruptions intermédiaires ou les spécifications contradictoires.",
+ "smw-category-invalid-value-assignment": "« $1 » n'est pas reconnu comme catégorie valide ni annotation de valeur.",
+ "protect-level-smw-pageedit": "N'autoriser que les utilisateurs qui ont le droit de modifier des pages (Semantic MediaWiki)",
+ "smw-create-protection": "La création de la propriété « $1 » est limitée aux utilisateurs ayant le droit approprié « $2 » (ou une affectation de [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups groupe utilisateur]) quand le [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode mode d’autorisation] est activé.",
+ "smw-create-protection-exists": "Les modifications de la propriété « $1 » sont limitées aux utilisateurs ayant le droit « $2 » approprié (ou le [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups groupe utilisateur]) quand le [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode mode d’autorisation] est activé.",
+ "smw-edit-protection": "Cette page est [[Property:Is edit protected|protégée]] pour éviter la modification de données accidentelle, et ne peut être modifiée que par des utilisateurs avec le droit de modification (« $1 ») ou le [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups groupe utilisateur].",
+ "smw-edit-protection-disabled": "La protection contre la modification a été désactivée, c'est pourquoi « $1 » ne peut pas être utilisé pour protéger les pages de l'entité contre la modification non autorisée.",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki a mis à jour l'état de la protection en fonction de la propriété « Est protégé contre la modification ».",
+ "smw-edit-protection-enabled": "Modifier la protection (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Cette page est protégée et ne peut être modifiée que par des utilisateurs avec le [https://www.semantic-mediawiki.org/wiki/Help:Permissions droit] <code>smw-patternedit</code> approprié.",
+ "smw-property-predefined-edip": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Médiawiki Sémantique] pour indiquer si la modification est protégée ou non.",
+ "smw-property-predefined-long-edip": "Bien que n’importe quel utilisateur soit habilité à ajouter cette propriété à un sujet, seul un utilisateur avec un droit spécifique peut modifier ou supprimer la protection d’une entité, une fois celle-ci ajoutée.",
+ "smw-query-reference-link-label": "Référence de requête",
+ "smw-format-datatable-emptytable": "Aucune donnée disponible dans la table",
+ "smw-format-datatable-info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
+ "smw-format-datatable-infoempty": "Affichage de 0 à 0 sur 0 entrée",
+ "smw-format-datatable-infofiltered": "(filtré sur _MAX_ entrées au total)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "Afficher les entrées de _MENU_",
+ "smw-format-datatable-loadingrecords": "Chargement…",
+ "smw-format-datatable-processing": "Traitement...",
+ "smw-format-datatable-search": "Chercher :",
+ "smw-format-datatable-zerorecords": "Aucun enregistrement correspondant trouvé",
+ "smw-format-datatable-first": "Premier",
+ "smw-format-datatable-last": "Dernier",
+ "smw-format-datatable-next": "Suivant",
+ "smw-format-datatable-previous": "Précédent",
+ "smw-format-datatable-sortascending": ": activer pour trier la colonne dans l'ordre croissant",
+ "smw-format-datatable-sortdescending": ": activer pour trier la colonne en ordre décroissant",
+ "smw-format-datatable-toolbar-export": "Exporter",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "La catégorie « $1 » contient une cible de redirection non valide vers un espace de noms hors catégorie.",
+ "smw-parser-function-expensive-execution-limit": "La fonction analyseur a atteint le temps limite autorisé pour son exécution (voir le paramètre de configuration [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit<code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "MédiaWiki Sémantique est en train de mettre à jour la page actuelle en fonction de certains traitements nécessaire après l’exécution des requêtes.",
+ "apihelp-smwinfo-summary": "Module d'API pour récupérer les informations concernant les statistiques de Semantic MediaWiki ainsi que d'autres informations meta.",
+ "apihelp-ask-summary": "Module d'API pour interroger Semantic MediaWiki avec le langage de requête.",
+ "apihelp-askargs-summary": "Module d'API pour interroger Semantic MediaWiki avec le langage de requête comportant une liste de conditions, d'affichages et de paramètres.",
+ "apihelp-browsebyproperty-summary": "Module d'API pour récupérer les informations concernant une propriété ou une liste de propriétés.",
+ "apihelp-browsebysubject-summary": "Module d'API pour récupérer les informations concernant un sujet.",
+ "apihelp-smwtask-summary": "Module d'API pour exécuter les tâches relatives à Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Module API pour supporter l'analyse des activités pour différents types d'entités de MédiaWiki Sémantique.",
+ "apihelp-ask-parameter-api-version": "Format de sortie :\n;2:Format rétro-compatible utilisant {} pour la liste des résultats.\n;3:Format expérimental utilisant [] pour la liste des résultats.",
+ "smw-api-invalid-parameters": "Paramètres invalides « $1 »",
+ "smw-parser-recursion-level-exceeded": "Le niveau de $1 récursions a été dépassé lors du processus d’analyse. Il est suggéré de valider la structure du modèle, ou si nécessaire d’ajuster le paramètre de configuration <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "Affichage de $1 {{PLURAL:$1|page|pages}} utilisant cette propriété.",
+ "smw-property-page-list-search-count": "Affichage de $1 {{PLURAL:$1|page|pages}} utilisant cette propriété avec une valeur correspondant à « $2 ».",
+ "smw-property-reserved-category": "Catégorie",
+ "smw-category": "Catégorie",
+ "smw-datavalue-uri-invalid-scheme": "« $1 » n'a pas été répertorié comme schéma d'URI valide.",
+ "smw-datavalue-uri-invalid-authority-path-component": "« $1 » a été identifié comme contenant une autorité « $2 » ou un chemin de composant non valides.",
+ "smw-browse-property-group-title": "Groupe de propriétés",
+ "smw-browse-property-group-label": "Etiquette du groupe de propriétés",
+ "smw-browse-property-group-description": "Description du groupe de propriétés",
+ "smw-property-predefined-ppgr": "« $1 » est une propriété prédéfinie qui identifie les entités (principalement les catégories) utilisées comme instance de regroupement pour les propriétés et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-filter": "Filtre",
+ "smw-section-expand": "Développer la section",
+ "smw-section-collapse": "Réduire la section",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] format",
+ "smw-help": "Aide",
+ "smw-cheat-sheet": "Aide-mémoire",
+ "smw-personal-jobqueue-watchlist": "Liste de suivi (file des tâches)",
+ "smw-property-predefined-label-skey": "Clé de tri",
+ "smw-processing": "Traitement...",
+ "smw-redirect-target-unresolvable": "Cette cible ne peut pas être résolue pour la raison \"$1\"",
+ "smw-types-title": "Type : $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "La modification du modèle de contenu d'une [https://www.semantic-mediawiki.org/wiki/Help:Schema page de schéma] n'est pas autorisée.",
+ "smw-schema-namespace-edit-protection": "Cette page est protégée et ne peut être modifiée que par des utilisateurs ayant le [https://www.semantic-mediawiki.org/wiki/Help:Permissions droit] <code>smw-schemaedit</code> approprié.",
+ "smw-schema-error": "Erreur de validation",
+ "smw-schema-error-schema": "La spécification '''$1''' et sa validation pour le schéma actuel ont trouvé les incompatibilités ou violations suivantes :",
+ "smw-schema-error-violation": "Violation (\"$1\", \"$2\")",
+ "smw-schema-error-type-missing": "Il manque un type au contenu pour qu’il soit reconnu et utilisable dans l’[https://www.semantic-mediawiki.org/wiki/Help:Schema espace de noms du schéma].",
+ "smw-schema-error-type-unknown": "Le type « $1 » n’est pas enregistré et ne peut donc pas être utilisé pour du contenu dans l’[https://www.semantic-mediawiki.org/wiki/Help:Schema espace de noms du schéma].",
+ "smw-schema-title": "Schéma",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Type",
+ "smw-schema-description-link-format-schema": "Ce type de schéma est censé définir des caractétistiques pour créer des liens dépendants du contexte, en rapport avec une propriété affectée du [[Property:Formatter schema|schéma de mise en forme]].",
+ "smw-schema-description-search-form-schema": "Ce type de schéma est sensé être utilisé pour définir des formulaires de saisie et des caractéristiques pour le profil de [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch recherche étendue], où il contient des instructions sur la manière de générer les champs de saisie, définir les espaces de noms par défaut, ou déclarer les expressions de préfixe pour une requête de recherche.",
+ "smw-schema-tag": "{{PLURAL:$1|balise|balises}}",
+ "smw-property-predefined-schema-desc": "« $1 » est une propriété prédéfinie qui stocke une description de schéma et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-schema-def": "« $1 » est une propriété prédéfinie qui stocke le contenu du schéma et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-schema-tag": "« $1 » est une propriété prédéfinie fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] pour identifier une collection de schémas.",
+ "smw-property-predefined-long-schema-tag": "Une étiquette qui identifie le schéma de contenus ou de caractéristiques similaires.",
+ "smw-property-predefined-schema-type": "« $1 » est une propriété prédéfinie qui décrit un type de schéma perceptible et est fournie par [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MédiaWiki Sémantique].",
+ "smw-property-predefined-long-schema-type": "Chaque [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type type] est supposé fournir sa propre interprétation des éléments de syntaxe et des contraintes et de les exprimer à travers un [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation modèle de validation].",
+ "smw-ask-title-keyword-type": "Recherche par mot-clé",
+ "smw-ask-message-keyword-type": "Ceci recherche les correspondances vérifiant la condition <code><nowiki>$1</nowiki></code> .",
+ "smw-remote-source-unavailable": "Impossible de se connecter à la cible distante « $1 ».",
+ "smw-remote-source-disabled": "La source « $1 » a désactivé le support des requêtes distantes !",
+ "smw-remote-source-unmatched-id": "La source '''$1''' ne correspond pas à une version de MédiaWiki Sémantique qui peut supporter une requête distante.",
+ "smw-remote-request-note": "Le résultat est récupéré depuis la source distante '''$1''' et il est probable que le contenu généré contienne des informations qui ne sont pas disponibles depuis l’intérieur du wiki actuel.",
+ "smw-remote-request-note-cached": "Le résultat est '''mis en cache''' depuis la source distante '''$1''' et il est probable que le contenu généré contienne des informations qui ne sont pas disponibles depuis l’intérieur du wiki actuel.",
+ "smw-parameter-missing": "Paramètre absent « $1 ».",
+ "smw-property-tab-usage": "Utilisation",
+ "smw-property-tab-redirects": "Synonymes",
+ "smw-property-tab-subproperties": "Sous-propriétés",
+ "smw-property-tab-errors": "Mauvaises affectations",
+ "smw-property-tab-specification": "... plus",
+ "smw-concept-tab-list": "Liste",
+ "smw-concept-tab-errors": "Erreurs",
+ "smw-ask-tab-result": "Résultat",
+ "smw-ask-tab-extra": "Suppléments",
+ "smw-ask-tab-debug": "Déboguage",
+ "smw-ask-tab-code": "Code",
+ "smw-install-incomplete-intro": "L’installation (ou la mise à jour) de <b>MédiaWiki Sémantique</b> n’a pas été achevée et un administrateur doit lancer les tâches suivantes pour éviter les incohérences de données avant que les utilisateurs ne continuent à créer ou modifier du contenu.",
+ "smw-install-incomplete-populate-hash-field": "Le remplissage du champ <code>smw_hash</code> a été sauté lors de l’installation, le script [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php] doit être exécuté.",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/frp.json b/www/wiki/extensions/SemanticMediaWiki/i18n/frp.json
new file mode 100644
index 00000000..dd6d08a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/frp.json
@@ -0,0 +1,114 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "ChrisPtDe"
+ ]
+ },
+ "smw_viewasrdf": "Flux RDF",
+ "smw_finallistconjunct": " et",
+ "smw_factbox_head": "Fêts sur $1",
+ "smw_concept_description": "Dèscripcion du concèpte « $1 »",
+ "version-semantic": "Èxtensions sèmantiques",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Comptar los rèsultats",
+ "smw_printername_csv": "èxportacion en CSV",
+ "smw_printername_dsv": "Èxportacion en DSV",
+ "smw_printername_debug": "Requéta d’èliminacion de les cofieries (por los èxpèrts)",
+ "smw_printername_embedded": "Entrebetar lo contegnu de les pâges",
+ "smw_printername_json": "èxportacion en JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Ènumèracion",
+ "smw_printername_ul": "Dètaly",
+ "smw_printername_table": "Grelye",
+ "smw_printername_broadtable": "Trâbla lârge",
+ "smw_printername_template": "Modèlo",
+ "smw_printername_rdf": "Èxportacion RDF",
+ "smw_printername_category": "Catègorie",
+ "smw-paramdesc-link": "Montrar les valors coment lims",
+ "smw-paramdesc-sep": "Lo sèparator de les valors",
+ "smw-paramdesc-embedonly": "Pas fâre vêre les en-tétes",
+ "smw-paramdesc-rdfsyntax": "La sintaxa RDF a utilisar",
+ "smw-paramdesc-csv-sep": "Lo sèparator a utilisar",
+ "smw-paramdesc-dsv-separator": "Lo sèparator a utilisar",
+ "smw-paramdesc-dsv-filename": "Lo nom du fichiér DSV",
+ "smw-paramdesc-searchlabel": "Lo tèxto du lim de vers los rèsultats",
+ "smw_iq_moreresults": "&hellip; ôtros rèsultats",
+ "smw_true_words": "veré,v,ouè,o",
+ "smw_false_words": "fôx,f,nan,n",
+ "smw_nofloat": "« $1 » est pas un nombro.",
+ "smw_novalues": "Gins de valor spècefiâ.",
+ "smw_type_header": "Propriètâts de tipo « $1 »",
+ "smw_typearticlecount": "Fâre vêre l{{PLURAL:$1|a propriètât qu’utilise|es $1 propriètâts qu’utilisont}} ceti tipo.",
+ "smw_attribute_header": "Pâges qu’utilisont la propriètât « $1 »",
+ "smw_attributearticlecount": "Fâre vêre l{{PLURAL:$1|a pâge qu’utilise|es $1 pâges qu’utilisont}} ceta propriètât.",
+ "smw_subproperty_header": "Sot-propriètâts",
+ "smw_subpropertyarticlecount": "Ceta propriètât at cet{{PLURAL:$1|a sot-propriètât|es $1 sot-propriètâts}} :",
+ "exportrdf": "Èxportar des pâges en RDF",
+ "smw_exportrdf_submit": "Èxportar",
+ "uriresolver": "Rèsolvior d’URI",
+ "properties": "Propriètâts",
+ "smw_property_template": "$1 du tipo $2 ($3)",
+ "unusedproperties": "Propriètâts inutilisâs",
+ "smw_unusedproperty_template": "$1 de tipo $2",
+ "wantedproperties": "Propriètâts demandâs",
+ "smw_wantedproperty_template": "$1 ($2 usâjo{{PLURAL:$2||s}})",
+ "smw_purge": "Rafrèchir",
+ "types": "Tipos",
+
+ "ask": "Rechèrche sèmantica",
+ "smw_ask_sortby": "Triyér per colones (u chouèx)",
+ "smw_ask_ascorder": "Crèssent",
+ "smw_ask_descorder": "Dècrèssent",
+ "smw_ask_submit": "Trovar des rèsultats",
+ "smw_ask_editquery": "Changiér la requéta",
+ "smw_add_sortcondition": "[Apondre les condicions de tri]",
+ "smw_ask_hidequery": "Cachiér la requéta",
+ "smw_ask_help": "Éde a la requéta",
+ "smw_ask_queryhead": "Requéta",
+ "smw_ask_printhead": "Balyês de ples a fâre vêre",
+ "smw_ask_printdesc": "(apondre yon nom de propriètât per legne)",
+ "smw_ask_format_as": "Formatar en :",
+ "smw_ask_defaultformat": "per dèfôt",
+ "smw_ask_otheroptions": "Ôtros chouèx",
+ "smw_ask_show_embed": "Fâre vêre lo code entrebetâ",
+ "smw_ask_hide_embed": "Cachiér lo code entrebetâ",
+ "smw-ask-delete": "[Suprimar]",
+ "searchbyproperty": "Rechèrchiér per propriètât",
+ "smw_sbv_property": "Propriètât :",
+ "smw_sbv_value": "Valor :",
+ "smw_sbv_submit": "Trovar des rèsultats",
+ "browse": "Navegar lo vouiqui",
+ "smw_browselink": "Navegar les propriètâts",
+ "smw_browse_go": "Emmodar",
+ "smw_browse_show_incoming": "fâre vêre les propriètâts que pouentont ique",
+ "smw_browse_hide_incoming": "cachiér les propriètâts que pouentont ique",
+ "smw_browse_no_outgoing": "Ceta pâge at gins de propriètât.",
+ "smw_browse_no_incoming": "Niona propriètât pouente vers ceta pâge.",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Lambél de la propriètât a l’envèrsa",
+ "pageproperty": "Rechèrche dens les propriètâts de la pâge",
+ "smw_pp_from": "De la pâge",
+ "smw_pp_type": "Propriètât",
+ "smw_pp_submit": "Trovar des rèsultats",
+ "smw_result_prev": "Devant",
+ "smw_result_next": "Aprés",
+ "smw_result_results": "Rèsultats",
+ "smw_result_noresults": "Dèsolâ, gins de rèsultat.",
+ "smw_smwadmin_return": "Tornar a $1",
+ "smw_smwadmin_dbbutton": "Inicialisar ou ben betar a nivél les trâbles",
+ "smw_smwadmin_announce": "Anonciér voutron vouiqui",
+ "smw_smwadmin_datarefresh": "Rèparacion et misa a nivél de les balyês",
+ "smw_smwadmin_datarefreshbutton": "Comenciér la misa a jorn de les balyês",
+ "smw_smwadmin_datarefreshstop": "Arrètar ceta misa a jorn",
+ "smw_smwadmin_datarefreshstopconfirm": "Ouè, je nen su de sûr.",
+ "smw_smwadmin_support": "Avêr d’assistance",
+ "smw_adminlinks_datastructure": "Structura de les balyês",
+ "smw_adminlinks_displayingdata": "Visualisacion de les balyês",
+ "smw_adminlinks_inlinequerieshelp": "Éde sur les requétes entrebetâs",
+ "smw-createproperty-isproperty": "Ceta propriètât est de tipo « $1 ».",
+ "smw-info-par-message": "Mèssâjo a fâre vêre.",
+ "smw_concept_header": "Pâges du concèpte « $1 »",
+ "smw_conceptarticlecount": "Fâre vêre l{{PLURAL:$1|a pâge que repôse|es $1 pâges que repôsont}} sur cél concèpte.",
+ "smw-livepreview-loading": "Chargement..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/frr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/frr.json
new file mode 100644
index 00000000..d30844c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/frr.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Murma174"
+ ]
+ },
+ "browse": "Browse wiki",
+ "smw-livepreview-loading": "Loose ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fur.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fur.json
new file mode 100644
index 00000000..496ffae1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fur.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tocaibon"
+ ]
+ },
+ "browse": "Esplore il sît",
+ "smw-livepreview-loading": "Daûr a cjamâ…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/fy.json b/www/wiki/extensions/SemanticMediaWiki/i18n/fy.json
new file mode 100644
index 00000000..c7d7208e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/fy.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "Robin0van0der0vliet",
+ "Robin van der Vliet"
+ ]
+ },
+ "smw_printername_category": "Kategory",
+ "properties": "Eigenskippen",
+ "smw-ask-delete": "[Fuortsmite]",
+ "smw_sbv_property": "Eigenskip:",
+ "smw_sbv_value": "Wearde:",
+ "browse": "Wiki blêdzje",
+ "smw_pp_type": "Eigenskip",
+ "smw_result_prev": "Foarige",
+ "smw_result_next": "Folgjende",
+ "smw-ui-tooltip-title-property": "Eigenskip",
+ "smw-ui-tooltip-title-warning": "Flater",
+ "smw-ui-tooltip-title-legend": "Leginda",
+ "smw-livepreview-loading": "Ynlade..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ga.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ga.json
new file mode 100644
index 00000000..25055d94
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ga.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alison",
+ "පසිඳු කà·à·€à·’න්ද"
+ ]
+ },
+ "smw_printername_category": "Catagóir",
+ "smw_exportrdf_submit": "Easportáil",
+ "smw_ask_defaultformat": "réamhshocrú",
+ "smw_sbv_property": "Airí",
+ "smw_browse_go": "Gabh",
+ "smw_pp_type": "Airí",
+ "smw_result_prev": "Siar",
+ "smw_result_next": "Ar aghaidh",
+ "smw-livepreview-loading": "Ag lódáil…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hans.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hans.json
new file mode 100644
index 00000000..1c14f20a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hans.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "加载中…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hant.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hant.json
new file mode 100644
index 00000000..afd00045
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gan-hant.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "載入中…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gd.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gd.json
new file mode 100644
index 00000000..ddb299ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gd.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "GunChleoc"
+ ]
+ },
+ "browse": "Brabhsaich an uicidh"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gl.json
new file mode 100644
index 00000000..a6c84e97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gl.json
@@ -0,0 +1,772 @@
+{
+ "@metadata": {
+ "authors": [
+ "Toliño",
+ "Elisardojm",
+ "Banjo",
+ "Macofe",
+ "Nemo bis",
+ "Athena in Wonderland",
+ "Navhy",
+ "Maria zaos"
+ ]
+ },
+ "smw-desc": "Fai o seu wiki máis accesible; para máquinas ''e'' humanos ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentación en liña])",
+ "smw-title": "MediaWiki Semántica",
+ "smw-semantics-not-enabled": "A funcionalidade de MediaWiki Semántica non está activada neste wiki.",
+ "smw_viewasrdf": "Fonte de novas RDF",
+ "smw_finallistconjunct": " e",
+ "smw-factbox-head": "... máis sobre \"$1\"",
+ "smw-factbox-facts": "Feitos",
+ "smw-factbox-facts-help": "Amosa as declaracións e feitos creados por un usuario",
+ "smw-factbox-facts-derived": "Feitos derivados",
+ "smw-factbox-facts-derived-help": "Amosa os feitos que foron derivados de regras ou coa axuda doutras técnicas de razoamento",
+ "smw_isspecprop": "Esta propiedade é especial neste wiki.",
+ "smw-concept-cache-header": "Uso da caché",
+ "smw-concept-cache-count": "A [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count caché de conceptos] contén {{PLURAL:$1|'''unha''' entidade|'''$1''' entidades}} ($2).",
+ "smw-concept-no-cache": "Caché non dispoñible.",
+ "smw_concept_description": "Descrición do concepto \"$1\"",
+ "smw_no_concept_namespace": "O conceptos só poden ser definidos nas páxinas que están no espazo de nomes Concepto:.",
+ "smw_multiple_concepts": "Cada páxina de conceptos só pode conter unha definición dun concepto.",
+ "smw_concept_cache_miss": "O concepto \"$1\" non pode ser usado desde que a configuración do wiki o require para calcular a desconexión. Se o problema non se resolve en breve, pregúntelle ao administrador do wiki para que o concepto poida estar dispoñible.",
+ "smw_noinvannot": "Non se poden asignar os valores para inverter as propiedades.",
+ "version-semantic": "Extensións semánticas",
+ "smw_baduri": "Sentímolo, os enderezos URI da forma \"$1\" non están permitidos.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Contador de resultados",
+ "smw_printername_csv": "Exportación en CSV",
+ "smw_printername_dsv": "Exportación en DSV",
+ "smw_printername_debug": "Depurar a pescuda (para expertos)",
+ "smw_printername_embedded": "Contidos incrustados na páxina",
+ "smw_printername_json": "Exportación en JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Lista simple",
+ "smw_printername_ol": "Enumeración",
+ "smw_printername_ul": "Lista detallada",
+ "smw_printername_table": "Táboa",
+ "smw_printername_broadtable": "Táboa ampla",
+ "smw_printername_template": "Modelo",
+ "smw_printername_templatefile": "Ficheiro de modelo",
+ "smw_printername_rdf": "Exportación en RDF",
+ "smw_printername_category": "Categoría",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "O número máximo de resultados a devolver",
+ "smw-paramdesc-offset": "O desprazamento do primeiro resultado",
+ "smw-paramdesc-headers": "Amosar as cabeceiras/os nomes de propiedade",
+ "smw-paramdesc-mainlabel": "A lapela para dar nome á páxina principal",
+ "smw-paramdesc-link": "Amosar os valores como ligazóns",
+ "smw-paramdesc-intro": "O texto a amosar antes dos resultados da pescuda, se houbese algún",
+ "smw-paramdesc-outro": "O texto a amosar despois dos resultados da pescuda, se houbese algún",
+ "smw-paramdesc-default": "O texto a amosar se non hai resultados para a pescuda",
+ "smw-paramdesc-sep": "O separador entre resultados",
+ "smw-paramdesc-propsep": "O separador entre as propiedades dunha entrada de resultado",
+ "smw-paramdesc-valuesep": "O separador entre entre os valores para unha propiedade de resultado",
+ "smw-paramdesc-showsep": "Amosar o separador ao comezo do ficheiro CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "No canto de amosar tódolos valores, contar as súas ocorrencias e amosalas.",
+ "smw-paramdesc-distributionsort": "Ordenar a distribución de valores por número de ocorrencias.",
+ "smw-paramdesc-distributionlimit": "Limitar a distribución de valores á contaxe dalgúns valores soamente.",
+ "smw-paramdesc-aggregation": "Especifique con que debe relacionarse o grupo",
+ "smw-paramdesc-template": "O nome dun modelo co que amosar as impresións",
+ "smw-paramdesc-columns": "O número de columnas nas que amosar os resultados",
+ "smw-paramdesc-userparam": "O valor pasado en cada chamada de modelo, se se empregase algún modelo",
+ "smw-paramdesc-class": "Unha clase adicional de CSS a definir para a lista",
+ "smw-paramdesc-introtemplate": "O nome dun modelo a amosar antes dos resultados da pescuda, se houbese algún",
+ "smw-paramdesc-outrotemplate": "O nome dun modelo a amosar despois dos resultados da pescuda, se houbese algún",
+ "smw-paramdesc-embedformat": "A etiqueta HTML utilizada para definir as cabeceiras",
+ "smw-paramdesc-embedonly": "Non amosar as cabeceiras",
+ "smw-paramdesc-table-class": "Unha clase CSS adicional que establecer para a táboa",
+ "smw-paramdesc-table-transpose": "Amosar as cabeceiras de táboa verticalmente e os resultados horizontalmente",
+ "smw-paramdesc-rdfsyntax": "A sintaxe de RDF a usar",
+ "smw-paramdesc-csv-sep": "Especifica un separador de columnas",
+ "smw-paramdesc-csv-valuesep": "Especifica un separador de valores",
+ "smw-paramdesc-csv-merge": "Fusionar filas e valores de columnas co mesmo identificador de suxeito (alias primeira columna)",
+ "smw-paramdesc-csv-bom": "Engadir un BOM (carácter para indicar a «endianness») na parte superior do ficheiro de saída",
+ "smw-paramdesc-dsv-separator": "O separador a empregar",
+ "smw-paramdesc-dsv-filename": "O nome para o ficheiro DSV",
+ "smw-paramdesc-filename": "O nome para o ficheiro de saída",
+ "smw-smwdoc-description": "Amosa unha táboa de todos os parámetros que se poden empregar para un formato de resultados especificado xunto aos valores por defecto e descricións.",
+ "smw-smwdoc-default-no-parameter-list": "Este formato de resultado non proporciona parámetros específicos de formato.",
+ "smw-smwdoc-par-format": "O formato dos resultados do que amosar a documentación do parámetro.",
+ "smw-smwdoc-par-parameters": "Os parámetros que amosar. \"specific\" para aqueles engadidos polo formato, \"base\" para aqueles dispoñibles en tódolos formatos e \"all\" para ambos.",
+ "smw-paramdesc-sort": "Propiedade a partir da que ordenar a pescuda",
+ "smw-paramdesc-order": "Orde da ordenación da pescuda",
+ "smw-paramdesc-searchlabel": "Texto para continuar a procura",
+ "smw-paramdesc-named_args": "Dea nome aos argumentos que se lle pasan ao modelo",
+ "smw-paramdesc-template-arguments": "Fixa a maneira na que se pasan os argumentos nomeados ó modelo",
+ "smw-paramdesc-import-annotation": "Os datos suplementarios anotados serán copiados durante a análise dun suxeito",
+ "smw-paramdesc-export": "Opción de exportación",
+ "smw-paramdesc-prettyprint": "Unha saída cun formato xeitoso que amosa sangrías e liñas novas adicionais",
+ "smw-paramdesc-json-unescape": "Saída para conter barras sen codificar e caracteres Unicode multibytes",
+ "smw-paramdesc-json-type": "Tipo de serialización",
+ "smw-paramdesc-source": "Fonte alternativa de pescuda",
+ "smw-paramdesc-jsonsyntax": "Sintaxe JSON a utilizar",
+ "smw-printername-feed": "Fonte de novas RSS e Atom",
+ "smw-paramdesc-feedtype": "Tipo de fonte de novas",
+ "smw-paramdesc-feedtitle": "O texto a usar como título da fonte de novas",
+ "smw-paramdesc-feeddescription": "O texto a usar como descrición da fonte de novas",
+ "smw-paramdesc-feedpagecontent": "Contido da páxina a amosar coa fonte de novas",
+ "smw-label-feed-description": "Fonte de novas $1 $2",
+ "smw_iq_disabled": "As preguntas semánticas están deshabilitadas neste wiki.",
+ "smw_iq_moreresults": "… máis resultados",
+ "smw_parseerror": "O valor dado non foi entendido.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "\"$1\" non pode usarse como nome de páxina neste wiki.",
+ "smw_noproperty": "\"$1\" non pode usarse como nome de propiedade neste wiki.",
+ "smw_wrong_namespace": "Aquí só están permitidas as páxinas no espazo de nomes \"$1\".",
+ "smw_manytypes": "Máis dun tipo definido para a propiedade.",
+ "smw_emptystring": "As cordas baleiras non están aceptadas.",
+ "smw_notinenum": "\"$1\" non está na lista de [[Property:Allows value|valores permitidos]] ($2) para a propiedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" non está na lista de [[Property:Allows value|valores permitidos]] ($2) para a propiedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" non está no intervalo de \"$2\" especificado pola restrición [[Property:Allows value|permite valor]] para a propiedade \"$3\".",
+ "smw_noboolean": "\"$1\" non é recoñecido como un valor booleano (verdadeiro/falso).",
+ "smw_true_words": "verdadeiro,v,si,s",
+ "smw_false_words": "falso,f,non,n",
+ "smw_nofloat": "\"$1\" non é un número.",
+ "smw_infinite": "Os números tan longos como \"$1\" non están soportados.",
+ "smw_unitnotallowed": "\"$1\" non está declarada como unha unidade de medida válida para esta propiedade.",
+ "smw_nounitsdeclared": "Non hai unidades de medida declaradas para esta propiedade.",
+ "smw_novalues": "Non se especificou ningún valor.",
+ "smw_nodatetime": "A data \"$1\" non foi entendida.",
+ "smw_toomanyclosing": "Parece que hai demasiados acontecementos de \"$1\" na pregunta.",
+ "smw_noclosingbrackets": "Algún uso de \"<nowiki>[[</nowiki>\" na súa pregunta non foi pechado polo seu \"]]\" correspondente.",
+ "smw_misplacedsymbol": "O símbolo \"$1\" foi usado nun lugar no que non era útil.",
+ "smw_unexpectedpart": "A parte \"$1\" da pregunta non foi entendida.\nPode que os resultados non sexan os agardados.",
+ "smw_emptysubquery": "Algunha subcuestión non ten unha condición válida.",
+ "smw_misplacedsubquery": "Algunha subcuestión foi usada nun lugar onde non están permitidas.",
+ "smw_valuesubquery": "As subcuestións non están soportadas para os valores da propiedade \"$1\".",
+ "smw_badqueryatom": "Unha parte \"<nowiki>[[…]]</nowiki>\" da pregunta non foi entendida.",
+ "smw_propvalueproblem": "O valor da propiedade \"$1\" non foi entendido.",
+ "smw_noqueryfeature": "Algunha característica da pregunta non está soportada neste wiki, polo que unha parte foi excluída ($1).",
+ "smw_noconjunctions": "As conxuncións nas preguntas non están soportadas neste wiki, polo que unha parte foi excluída ($1).",
+ "smw_nodisjunctions": "Non están soportadas as disxuncións nas preguntas neste wiki e parte desta foi ignorada ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|A seguinte condición da consulta <code>$1</code> non pode ser considerada|As seguintes $2 condicións da consulta <code>$1</code> non poden ser consideradas}} debido ás restricións do wiki para o tamaño ou profundidade das consultas.",
+ "smw_notemplategiven": "Por favor, proporcione un valor para o parámetro “modelo†do formato desta pregunta para poder funcionar.",
+ "smw_db_sparqlqueryproblem": "Non se puideron obter os resultado da pescuda da base de datos SPARQL. Este erro pode ser temporal ou indicar un erro no programa da base de datos.",
+ "smw_db_sparqlqueryincomplete": "Resultou demasiado difícil responder á pescuda e cancelouse. É probable que falten algúns resultados. Se fose posible, intente facer unha consulta máis sinxela.",
+ "smw_type_header": "Propiedades do tipo \"$1\"",
+ "smw_typearticlecount": "Amosando $1 {{PLURAL:$1|propiedade|propiedades}} que {{PLURAL:$1|usa|usan}} este tipo.",
+ "smw_attribute_header": "Páxinas que usan a propiedade \"$1\"",
+ "smw_attributearticlecount": "Amosando $1 {{PLURAL:$1|páxina|páxinas}} que {{PLURAL:$1|usa|usan}} esta propiedade.",
+ "smw-propertylist-subproperty-header": "Subpropiedades",
+ "smw-propertylist-redirect-header": "Sinónimos",
+ "smw-propertylist-error-header": "Páxinas con atribucións incorrectas",
+ "smw-propertylist-count": "{{PLURAL:$1|Móstrase $1 entidade relacionada|Móstranse $1 entidades relacionadas}}.",
+ "smw-propertylist-count-with-restricted-note": "{{PLURAL:$1|Móstrase a $1 entidade relacionada|Móstranse as $1 entidades relacionadas}} (hai máis dispoñibles pero a visualización está restrinxida a \"$2\").",
+ "smw-propertylist-count-more-available": "Amosando $1 {{PLURAL:$1|entidade asociada|entidades asociadas}} (hai máis dispoñibles).",
+ "exportrdf": "Exportar páxinas a RDF",
+ "smw_exportrdf_docu": "Esta páxina permítelle obter datos dunha páxina en formato RDF.\nPara exportar páxinas, insira os títulos na caixa de embaixo (un título por liña).",
+ "smw_exportrdf_recursive": "Exportar igualmente todas as páxinas relacionadas.\nDéase conta de que o resultado pode ser longo!",
+ "smw_exportrdf_backlinks": "Exportar tamén todas as páxinas que se refiren ás páxinas exportadas.\nXera un RDF que se pode navegar.",
+ "smw_exportrdf_lastdate": "Non exportar páxina que non tiveron cambios desde a data dada.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Solucionador de URI",
+ "properties": "Propiedades",
+ "smw_properties_docu": "Este wiki usa as seguintes propiedades.",
+ "smw_property_template": "\"$1\" de tipo \"$2\" ($3 {{PLURAL:$3|uso|usos}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Todas as propiedades deberían estar descritas nunha páxina!",
+ "smw_propertylackstype": "Non foi especificado ningún tipo para esta propiedade (asúmese o tipo \"$1\" polo de agora).",
+ "smw_propertyhardlyused": "Esta propiedade apenas ten uso neste wiki!",
+ "smw-property-name-invalid": "Non se pode utilizar a propiedade \"$1\" (nome de propiedade non válido).",
+ "smw-property-name-reserved": "«$1» marcouse como nome reservado e, polo tanto, non debe utilizarse como propiedade. É posible que esta [https://www.semantic-mediawiki.org/wiki/Help:Property_naming páxina de axuda] inclúa información sobre o motivo polo que este nome se reservou.",
+ "smw-sp-property-searchform": "Amosar as propiedades que conteñan:",
+ "smw-sp-property-searchform-inputinfo": "A entrada distingue entre maiúsculas e minúsculas ao utilizala para filtrar; só se amosan as propiedades que coinciden coa condición.",
+ "smw-special-property-searchform": "Amosar as propiedades que conteñenË",
+ "smw-special-property-searchform-inputinfo": "A entrada distingue entre maiúsculas e minúsculas ao utilizala para filtrar; só se amosan as propiedades que coinciden coa condición.",
+ "smw-special-property-searchform-options": "Opcións",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Ningún",
+ "smw-special-wantedproperties-filter-unapproved": "Sen aprobar",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Opción de filtro usado en conexión co módulo de autoridade.",
+ "concepts": "Conceptos",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts concepto] pódese ver como unha \"categoría dinámica\", é dicir, como unha colección de páxinas que non están creadas manualmente, pero que son computadas por Semantic MediaWiki desde a descrición dunha pescuda proporcionada.",
+ "smw-special-concept-header": "Lista de conceptos",
+ "smw-special-concept-count": "{{PLURAL:$1|Está listado o seguinte concepto|Están listados os seguintes $1 conceptos}}.",
+ "smw-special-concept-empty": "Non se atopou concepto ningún.",
+ "unusedproperties": "Propiedades non usadas",
+ "smw-unusedproperties-docu": "Esta páxina lista [https://www.semantic-mediawiki.org/wiki/Unused_properties propriedades non usada] que foron declaradas, aínda que ningunha páxina as utiliza. Para unha visión diferenciada, consulte as páxinas especiais con [[Special:Properties|tódalas propriedades]] ou coas [[Special:WantedProperties|propriedades solicitadas]].",
+ "smw-unusedproperty-template": "\"$1\" de tipo \"$2\"",
+ "wantedproperties": "Propiedades requiridas",
+ "smw-wantedproperties-docu": "Esta páxina lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedades solicitadas] que son utilizadas na wiki, pero que non posúen unha páxina describíndoas. Para unha visión diferenciada, consulte as páxinas especiais con [[Special:Properties|tódalas propiedades]] ou coas [[Special:UnusedProperties|propiedades non utilizadas]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw-special-wantedproperties-docu": "Esta páxina lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propiedades solicitadas] que son utilizadas na wiki, pero que non posúen unha páxina describíndoas. Para unha visión diferenciada, consulte as páxinas especiais con [[Special:Properties|tódalas propiedades]] ou coas [[Special:UnusedProperties|propiedades non utilizadas]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw_purge": "Refrescar",
+ "smw-purge-failed": "Fallou o refresco",
+ "types": "Tipos",
+ "smw_types_docu": "Lista dos [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipos de datos dispoñibles] con cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipo] representando un conxunto único de atributos para describir un valor en termos características de almacenamento e visualización que son heredadas nunha propiedade asignada.",
+ "smw-special-types-no-such-type": "\"$1\" é descoñecido ou non foi especificado como tipo de datos válido.",
+ "smw-statistics": "Estatísticas semánticas",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor|Valores}} de propiedade (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propiedade|Propiedades}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propiedade|Propiedades}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propiedade|Propiedades}}]] (utilizadas con polo menos un valor)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propiedade|Propiedades}} ({{PLURAL:$1|rexistrada|rexistradas}} cunha páxina)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propiedade|Propiedades}} ({{PLURAL:$1|asociada|asociadas}} a un tipo de datos)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultas}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Pescuda|Pescudas}}]]",
+ "smw-statistics-query-size": "Tamaño da pescuda",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concepto|Conceptos}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concepto|Conceptos}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobxecto|Subobxectos}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobxecto|Subobxectos}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipo|Tipos}} de datos]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valor de propiedade|Valores de propiedade}}\n([[Special:ProcessingErrorList|{{PLURAL:$1|anotación incorrecta|anotacións incorrectas}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor|Valores}} de propiedade ({{PLURAL:$1|anotación incorrecta|anotacións incorrectas}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entidade obsoleta|Entidades obsoletas}} (marcada para borrado)",
+ "smw_uri_doc": "O solucionador de URI pon en práctica o [$1 descubrimento da ETIQUETA de W3C en httpRange-14].\nVixía que os humanos non entren en sitios web.",
+ "ask": "Procura semántica",
+ "smw-ask-help": "Esta sección contén algunhas ligazóns para axudar a explicar como utilizar a sintaxe <code>#ask</code>.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selección de páxinas] describe como seleccionar páxinas e construír condicións.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de procura] enumera os operadores de procura dispoñibles, incluídos os de consulta de intervalo e de caracteres comodíns.\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Mostra de información] expón o uso das declaracións de saída e as opcións de formatado.",
+ "smw_ask_sortby": "Ordenar por columnas (opcional)",
+ "smw_ask_ascorder": "Ascendente",
+ "smw_ask_descorder": "Descendente",
+ "smw-ask-order-rand": "Ao chou",
+ "smw_ask_submit": "Atopar os resultados",
+ "smw_ask_editquery": "Editar a pregunta",
+ "smw_add_sortcondition": "[Engadir unha condición de ordenación]",
+ "smw-ask-sort-add-action": "Engadir condición de ordenación",
+ "smw_ask_hidequery": "Agochar a pescuda (vista compacta)",
+ "smw_ask_help": "Axuda sobre as pescudas",
+ "smw_ask_queryhead": "Condición",
+ "smw_ask_printhead": "Selección dos datos a imprimir",
+ "smw_ask_printdesc": "(engada un nome de propiedade por liña)",
+ "smw_ask_format_as": "Formato de:",
+ "smw_ask_defaultformat": "predeterminado",
+ "smw_ask_otheroptions": "Outras opcións",
+ "smw-ask-otheroptions-info": "Esta sección contén opcións que alteran os formatos de saída. As descricións dos parámetros pódense consultar pasando o rato por riba delas.",
+ "smw-ask-otheroptions-collapsed-info": "Use a icona do signo máis para ollar todas as opcións dispoñibles",
+ "smw_ask_show_embed": "Amosar o código incrustado",
+ "smw_ask_hide_embed": "Agochar o código incrustado",
+ "smw_ask_embed_instr": "Para incrustar esta pescuda en liña nunha páxina wiki use o seguinte código.",
+ "smw-ask-delete": "Quitar",
+ "smw-ask-sorting": "Ordenación",
+ "smw-ask-options": "Opcións",
+ "smw-ask-options-sort": "Opcións de ordenación",
+ "smw-ask-format-options": "Formato e opcións",
+ "smw-ask-parameters": "Parámetros",
+ "smw-ask-search": "Procurar",
+ "smw-ask-debug": "Depurar",
+ "smw-ask-debug-desc": "Xera información para a revisión de erros das consultas",
+ "smw-ask-no-cache": "Desactivar a caché de consultas",
+ "smw-ask-no-cache-desc": "Resultados sen caché de consulta",
+ "smw-ask-result": "Resultado",
+ "smw-ask-empty": "Borrar todas as entradas",
+ "smw-ask-download-link-desc": "Descargar os resultados da consulta en formato $1",
+ "smw-ask-format": "Formato",
+ "smw-ask-format-selection-help": "Axuda co formato seleccionadoË $1",
+ "smw-ask-condition-change-info": "A condición foi modificada e o motor de procuras precisa que a consulta volva ser executada para producir resultados que correspondan ós novos requisitos.",
+ "smw-ask-input-assistance": "Asistencia de entrada",
+ "smw-ask-condition-input-assistance": "Proporciónase [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistencia de entrada] para os campos de saída, de ordenación e de condición. O campo de condición precisa o uso dalgún dos seguintes prefixos:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> para obter as suxestións de propiedades (exemplo: <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> para obter as suxestións de categorías",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> para obter as suxestións de conceptos",
+ "smw-ask-format-change-info": "O formato foi modificado e é preciso para reexecutar a consulta para corresponderse ós novos parámetros e opcións de visualización.",
+ "smw-ask-format-export-info": "O formato seleccionado é un formato de exportación que non ten representación visual, polo tanto os resultados só se proporcionan como descarga.",
+ "smw-ask-query-search-info": "A consulta <code><nowiki>$1</nowiki></code> foi respondida polo {{PLURAL:$3|1=<code>$2</code> (desde a caché)|<code>$2</code> (desde a caché)|<code>$2</code>}} en $4 {{PLURAL:$4|segundo|segundos}}.",
+ "searchbyproperty": "Procurar por propiedade",
+ "processingerrorlist": "Tratatamento da lista de erros",
+ "propertylabelsimilarity": "Informe de similitude de etiquetas de propiedade",
+ "smw-processingerrorlist-intro": "A lista seguinte proporciona unha visión dos erros de tratamento que apareceron en relación con [https://www.semantic-mediawiki.org/ MediaWiki Semántica]. Recoméndase vixiar esta lista de forma regular e correxir as anotacións de valor incorrecto.",
+ "smw_sbv_docu": "Procurar todas as páxinas que teñen a propiedade e o valor dados.",
+ "smw_sbv_novalue": "Por favor, insira un valor válido para a propiedade ou vexa todos os valores das propiedades para \"$1\".",
+ "smw_sbv_displayresult": "Unha lista de todas as páxinas que teñen a propiedade \"$1\" co valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Unha lista con todas as páxinas que teñen a propiedade \"$1\" co valor \"$2\".\nComo houbo só uns poucos resultados, móstranse tamén os resultados próximos.",
+ "smw_sbv_property": "Propiedade:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Atopar os resultados",
+ "browse": "Explorar o wiki",
+ "smw_browselink": "Explorar as propiedades",
+ "smw_browse_article": "Insira o nome da páxina para comezar a navegación.",
+ "smw_browse_go": "Ir",
+ "smw_browse_show_incoming": "Amosar as propiedades entrantes",
+ "smw_browse_hide_incoming": "Agochar as propiedades entrantes",
+ "smw_browse_no_outgoing": "Esta páxina non ten propiedades.",
+ "smw_browse_no_incoming": "Ningunha propiedade liga con esta páxina.",
+ "smw-browse-from-backend": "Estase a recuperar a información do servidor.",
+ "smw-browse-intro": "Esta páxina proporciona detalles sobre un tema ou instancia de entidade, por favor, indique o nome dun obxecto para ser inspeccionado.",
+ "smw-browse-invalid-subject": "A validación do tema devolveu o erro \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "O tema ten un formato de serialización incorrecto.",
+ "smw-browse-js-disabled": "É probable que o Javascript estea desactivado ou non dispoñible, recomendamos usar un navegador que o soporte. Outras opcións coméntanse na páxina do parámetro de configuración [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Amosar grupos",
+ "smw-browse-hide-group": "Ocultar grupos",
+ "smw-noscript": "Esta páxina ou acción require Javascript para funcionar. Active o Javascript no seu navegador ou utilice un navegador que o suporte, para que esta funcionalidade poida ser servida e é proporcionada como solicitada. Para máis información, consulte a páxina de axuda [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiqueta da propiedade inversa",
+ "pageproperty": "Procura de propiedades de páxinas",
+ "smw_pp_docu": "Introduza unha páxina e propiedade, ou só unha propiedade, para obter tódolos valores asignados.",
+ "smw_pp_from": "Da páxinaË",
+ "smw_pp_type": "PropiedadeË",
+ "smw_pp_submit": "Atopar os resultados",
+ "smw_result_prev": "Anterior",
+ "smw_result_next": "Seguinte",
+ "smw_result_results": "Resultados",
+ "smw_result_noresults": "Non hai resultados.",
+ "smwadmin": "Funcións administrativas e de mantemento",
+ "smw-admin-statistics-job-title": "Estatísticas de tarefas",
+ "smw-admin-statistics-job-docu": "As estatísticas de tarefas amosan información sobre tarefas programadas do MediaWiki Semántico que aínda non foron executadas. O número de tarefas pode ter pequenas imprecisións ou conter intentos errados; consulte o [https://www.mediawiki.org/wiki/Manual:Job_queue manual] para máis información.",
+ "smw-admin-statistics-querycache-title": "Estatísticas da caché das procuras",
+ "smw-admin-statistics-querycache-disabled": "A [https://www.semantic-mediawiki.org/wiki/QueryCache ''cache'' de consultas] non foi activada nesta wiki, polo tanto non hai estatísticas dispoñíbeis.",
+ "smw-admin-statistics-querycache-explain": "As estatísticas de caché conteñen datos acumulados e derivados provisionais incluíndo:\n* \"misses\" como a cantidade total de intentos de obter datos da caché sen resposta dispoñible, forzando unha obtención directa do repositorio (DB, triple-store etc.)\n* \"deletes\" como a cantidade total de operacións de saída de caché (ben a través dunha purga ou unha dependencia de consulta)\n* \"hits\" contén a cantidade de obtencións de caché dende fontes incrustadas (consultas dende unha páxina da wiki) ou non incrustadas (en caso de estar activadas, solicitadas por páxinas como Special:Ask ou a API). \n* \"medianRetrievalResponseTime\" é un valor orientativo sobre o tempo de resposta medio (en segundos) para peticións en cache e fóra desta ó longo do período de recolección.\n* \"noCache\" indica a cantidade de peticións sen intento de obter resultados da caché (consultas con limit=0, opción de 'no-cache')",
+ "smw-admin-permission-missing": "Debido á falta dos permisos necesarios, bloqueouse o acceso a esta páxina, por favor, consulte a páxina de axuda sobre os [https://www.semantic-mediawiki.org/wiki/Help:Permissions permisos] para obter detalles das configuracións necesarias.",
+ "smw-admin-setupsuccess": "O motor de almacenamento foi configurado.",
+ "smw_smwadmin_return": "Volver a \"$1\"",
+ "smw_smwadmin_updatestarted": "Comezou un novo proceso de actualización para refrescar os datos semánticos.\nTodos os datos almacenados serán reconstruídos ou reparados onde sexa necesario.\nPode seguir o proceso da actualización nesta páxina especial.",
+ "smw_smwadmin_updatenotstarted": "Xa hai un proceso de actualización en curso.\nNon se pode crear outro.",
+ "smw_smwadmin_updatestopped": "Todos os procesos de actualización existentes foron detidos.",
+ "smw_smwadmin_updatenotstopped": "Para deter os procesos de actualización actuais, debe activar a caixa de verificación para indicar que está seguro de facelo.",
+ "smw-admin-docu": "Esta páxina especial serve de axuda durante a instalación, actualización, mantemento e uso do <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> e ademais de proporcionar funcións administrativas e tarefas, así como estatísticas.\nLembre facer unha copia de seguridade dos datos antes de executar calquera función administrativa.",
+ "smw-admin-environment": "Contorno de programación",
+ "smw-admin-db": "Mantemento da base de datos",
+ "smw-admin-db-preparation": "A inicialización da táboa está en curso e pode tardar algún tempo ata que se amosen os resultados, dependendo do tamaño da táboa e de posibles optimizacións da mesma.",
+ "smw-admin-dbdocu": "Semantic MediaWiki require algunhas extensións da base de datos MediaWiki para almacenar os datos semánticos.\nA función de embaixo asegúrase de que a súa base de datos está configurada apropiadamente.\nOs cambios feitos neste paso non afectarán ao resto da base de datos MediaWiki, e poden ser desfeitos de xeito sinxelo se se desexa.\nEsta función de configuración pode ser executada múltiples veces sen facer ningún dano, pero só é necesaria unha vez na instalación ou actualización.",
+ "smw-admin-permissionswarn": "Se a operación falla con erros SQL, probablemente o usuario da base de datos empregada polo seu wiki (comprobe o seu ficheiro \"LocalSettings.php\") non teña os permisos suficientes.\nFai falla conceder a este usuario os permisos para crear e eliminar táboas; temporalmente insira o rexistro da súa base de datos no ficheiro \"LocalSettings.php\", ou use o script de mantemento <code>setupStore.php</code>, que pode usar as credenciais dun administrador.",
+ "smw-admin-dbbutton": "Inicializar ou actualizar as táboas",
+ "smw-admin-announce": "Anuncia o teu wiki",
+ "smw-admin-announce-text": "Se a súa wiki é pública, pode rexistrala en <a href=\"https://wikiapiary.com\">WikiApiary</a>, o catálogo wiki de wikis.",
+ "smw-admin-deprecation-notice-title": "Avisos de obsolescencia",
+ "smw-admin-deprecation-notice-docu": "A seguinte sección contén axustes que quedaron obsoletos ou están eliminados, pero que foron detectados activos neste wiki. Espérase que nunha versión futura se elimine o soporte para estas configuracións.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> está obsoleta e será eliminada de $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> eliminará (ou substituirá) {{PLURAL:$2|a opción seguinte|as opcións seguintes}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> está obsoleto e será eliminado na versión $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi substituído por <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|opción|opcións}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> está a ser substituído por <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi eliminado de $2",
+ "smw-admin-deprecation-notice-title-notice": "Modificacións pendentes",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Os seguintes axustes, que están a ser usados nesta wiki, está planeado que sexan eliminados ou modificados en versións futuras.",
+ "smw-admin-deprecation-notice-title-replacement": "Opcións substituídas ou renomeadas",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "A sección seguinte contén parámetros que foron renomeados ou recibiron cambios doutro tipo e recoméndase que se actualice inmediatamente o seu nome ou o seu formato.",
+ "smw-admin-deprecation-notice-title-removal": "Parámetros eliminados",
+ "smw-admin-deprecation-notice-title-removal-explanation": "As opcións seguintes elimináronse nunha versión anterior, pero detectouse que aínda se utilizan neste wiki.",
+ "smw-smwadmin-refresh-title": "Reparación e actualización de datos",
+ "smw_smwadmin_datarefresh": "Reparación dos datos",
+ "smw_smwadmin_datarefreshdocu": "É posible restaurar todos os datos de Semantic MediaWiki baseados nos contidos actuais do wiki.\nIsto pode ser útil para reparar datos rotos ou para refrescar os datos se o formato interno cambiou debido a actualizacións do software.\nEsta actualización é executada páxina a páxina e non será completada inmediatamente.\nO seguinte amosa se unha actualización está en curso e permítelle comezalas ou detelas (a non ser que esta característica fose deshabilitada polo administrador do sitio).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Xa hai unha actualización en curso.</strong>\nÉ normal que os progresos de actualización vaian lentos, xa que só se refrescan os datos nos pequenos anacos nos que un usuario accede ao wiki.\nPara finalizar esta actualización máis rápido, pode invocar a escritura <code>runJobs.php</code> de mantemento de MediaWiki (use a opción <code>--maxjobs 1000</code> para restrinxir o número de actualizacións feitas nunha quenda).\nProgreso estimado da actualización actual:",
+ "smw_smwadmin_datarefreshbutton": "Programar a reconstrución dos datos",
+ "smw_smwadmin_datarefreshstop": "Deter esta actualización",
+ "smw_smwadmin_datarefreshstopconfirm": "Si, estou {{GENDER:$1|seguro|segura}}.",
+ "smw-admin-job-scheduler-note": "A maioría das actividades prográmanse como traballos para que as tarefas se executen por lotes no planificador de tarefas para planificar e completar o procesamento, polo que é crítico que os scripts de mantemento <code>runJobs.php</code> ou <code>$wgRunJobsAsync</code> se xestionen en consecuencia.",
+ "smw-admin-outdateddisposal-title": "Eliminación das entidades obsoletas",
+ "smw-admin-outdateddisposal-intro": "Algunhas actividades (o cambio dun tipo de propiedade, a eliminación de páxinas da wiki, ou a corrección de valores erróneos) provocarán [https://www.semantic-mediawiki.org/wiki/Outdated_entities entidades desactualizadas] e recoméndase que estas sexa eliminadas periodicamente para liberar o seu espazo nas táboas.",
+ "smw-admin-outdateddisposal-active": "Planificouse unha tarefa de eliminación de entidades obsoletas.",
+ "smw-admin-outdateddisposal-button": "Programar unha eliminación",
+ "smw-admin-feature-disabled": "Esta funcionalidade está desactivada neste wiki, por favor consulte a páxina de axuda de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">configuración</a> ou contacte cun administrador de sistemas.",
+ "smw-admin-propertystatistics-title": "Reconstrución das estatísticas das propiedade",
+ "smw-admin-propertystatistics-intro": "Reconstrúe tódalas estatísticas de uso das propiedades e actualiza e corrixe os [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count contadores de uso] das propiedades.",
+ "smw-admin-propertystatistics-active": "Planificouse unha tarefa de reconstrución de propiedades.",
+ "smw-admin-propertystatistics-button": "Programa unha reconstrución das estatísticas",
+ "smw-admin-fulltext-title": "Reconstrución da procura de texto completo",
+ "smw-admin-fulltext-intro": "Reconstrúe o índice de procura en base a táboas de propiedade cun tipo de dato activado [https://www.semantic-mediawiki.org/wiki/full-text procura de texto completo]. Cambios nas regras de indexado (stopwords modificadas, novo stemmer etc.) e/ou unha táboa nova ou cambiada requiren que esta tarefa se execute de novo.",
+ "smw-admin-fulltext-active": "Planificouse unha tarefa de reconstrución da procura de texto completo.",
+ "smw-admin-fulltext-button": "Programa unha reconstrución de texto completo",
+ "smw-admin-support": "Obter asistencia",
+ "smw-admin-supportdocu": "Diversos recursos proporciónanse para axudalo en caso de problemas:",
+ "smw-admin-installfile": "Se experimenta problemas coa súa instalación, comece comprobando a guía no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">ficheiro INSTALL</a> e a <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">páxina de instalación</a>.",
+ "smw-admin-smwhomepage": "A documentación completa de usuario de Semantic MediaWiki está en <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Pode informar dos erros no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">rexistro de problemas</a>, onde a páxina <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">para informar de erros</a> proporciona instrucións para crear informes de problemas eficaces.",
+ "smw-admin-questions": "Se ten máis preguntas ou suxestións, únase á conversa na <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">lista de correo de usuarios</a>de Semantic MediaWiki ou a <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">sala de conversa</a>.",
+ "smw-admin-other-functions": "Outras funcións",
+ "smw-admin-supplementary-section-title": "Funcións suplementarias",
+ "smw-admin-supplementary-section-subtitle": "Funcións dispoñibles",
+ "smw-admin-supplementary-section-intro": "Algunhas das funcións listadas nesta sección poden estar restrinxidas e, polo tanto, son inaccesibles neste wiki.",
+ "smw-admin-supplementary-settings-title": "Opcións de configuración",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> devolve unha lista colectiva de parámetros dispoñibles usados no MediaWiki Semántico",
+ "smw-admin-supplementary-operational-statistics-title": "Estatísticas de funcionamento",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> amosa un conxunto estendido de estatísticas",
+ "smw-admin-supplementary-idlookup-title": "Procura e liberación da entidade",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contén funcións para procurar e liberar entidades individuais",
+ "smw-admin-supplementary-duplookup-title": "Entidades duplicadas",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> para listar entradas que están categorizadas porque teñen duplicados na táboa de entidades",
+ "smw-admin-supplementary-duplookup-docu": "Esta páxina lista entradas que foron categorizadas como duplicadas na [https://www.semantic-mediawiki.org/wiki/Help:Entity_table táboa de entidades]. As entradas duplicadas só deberían ocurrir (como moito) en raras ocasións potencialmente causadas por un proceso terminado durante unha actualización da base de datos ou unha transacción de reversión non concluída.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estatísticas da caché",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> amosa estatísticas relacionadas coa caché",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informa sobre configuración e estatísticas de indexación",
+ "smw-admin-supplementary-elastic-functions": "Funcións dispoñibles",
+ "smw-admin-supplementary-elastic-settings-title": "Configuración",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> usado por Elasticsearch para administrar índices de MediaWiki Semántica",
+ "smw-admin-supplementary-elastic-mappings-title": "Correspondencias",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> para listar os índices e as correspondencias de campo",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resumo",
+ "smw-admin-supplementary-elastic-mappings-fields": "Correspondencias de campos",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodos",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> amosa estatísticas de nodo",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> proporciona unha vista xeral de índices dispoñibles e das súas estatísticas",
+ "smw-admin-supplementary-elastic-statistics-title": "Estatísticas",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> amosa estatísticas de nivel de índice",
+ "smw-admin-supplementary-elastic-status-replication": "Estado de replicación",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Última replicación activa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalo de actualización: $1",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replicación bloqueada: $1 (reconstrución en proceso)",
+ "smw-list-count": "A lista contén $1 {{PLURAL:$1|entrada|entradas}}.",
+ "smw-list-count-from-cache": "A lista contén $1 {{PLURAL:$1|entrada|entradas}} e recuperouse da caché (UTC: $2).",
+ "smw-property-label-uniqueness": "Atopouse unha correspondencia entre a etiqueta \"$1\" e polo menos outra representación de propiedade. Consulte a [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness páxina de axuda] sobre a resolución deste problema, por favor.",
+ "smw-property-label-similarity-title": "Informe de similitude de etiquetas de propiedade",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula a similitude das etiquetas de propiedade existentes",
+ "smw-property-label-similarity-threshold": "LímiteË",
+ "smw-property-label-similarity-type": "Amosa o identificador do tipo",
+ "smw-property-label-similarity-noresult": "Non se atoparon resultados para as opcións seleccionadas.",
+ "smw-property-label-similarity-docu": "Compara e notifica a [https://www.semantic-mediawiki.org/wiki/Property_similarity semellanza sintáctica] (non a semellanza semántica) entre dúas etiquetas de propiedades, o que pode axudar a filtrar propiedades mal escritas ou equivalentes que representen o mesmo concepto (consulte a páxina especial [[Special:Properties|Propiedades]] para clarificar os conceptos e os usos das propiedades notificadas). Pode axustarse o limiar para aumentar ou diminuír a distancia de semellanza. <code>[[Property:$1|$1]]</code> utilízase para excluír propiedades da análise.",
+ "smw-admin-operational-statistics": "Esta páxina contén estatísticas operacionais recollidas en, ou por, funcións relacionadas coa MediaWiki Semántica. Pode atopar unha lista expandida de estatísticas específicas á wiki [[Special:Statistics|<b>aquí</b>]].",
+ "smw_adminlinks_datastructure": "Estrutura dos datos",
+ "smw_adminlinks_displayingdata": "Amosando os datos",
+ "smw_adminlinks_inlinequerieshelp": "Axuda coas pescudas",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Número de usos] estimado: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propiedade definida polo {{PLURAL:$1|usuario|sistema}}",
+ "smw-property-indicator-last-count-update": "Estimación do número de usos\nÚltima actualización: $1",
+ "smw-concept-indicator-cache-update": "Contador de cache\nÚltima actualización: $1",
+ "smw-createproperty-isproperty": "Esta é unha propiedade de clase $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|O valor permitido|Os valores permitidos}} para esta propiedade {{PLURAL:$1|é|son}}:",
+ "smw-paramdesc-category-delim": "O delimitador",
+ "smw-paramdesc-category-template": "Un modelo co que dar formato aos elementos",
+ "smw-paramdesc-category-userparam": "Un parámetro que pasar ao modelo",
+ "smw-info-par-message": "A mensaxe a amosar.",
+ "smw-info-par-icon": "A icona a amosar; ou ben \"info\" ou ben \"warning\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Opcións xerais",
+ "prefs-ask-options": "Especial:Opcións de preguntas",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/?uselang=gl Semantic MediaWiki] (e as extensións relacionadas) permiten adaptar individualmente algunhas funcións seleccionadas. Por favor, consulte a [https://www.semantic-mediawiki.org/wiki/Help:User_preferences?uselang=gl páxina de axuda] para unha descrición detallada.",
+ "smw-prefs-ask-options-tooltip-display": "Amosar o texto do parámetro como unha axuda informativa",
+ "smw-prefs-ask-options-compact-view-basic": "Habilita a vista compacta básica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Se se activa, amosa un conxunto reducido de ligazóns na vista compacta Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Activar a corrección da hora para as páxinas especiais usando a preferencia do [[Special:Preferences#mw-prefsection-rendering|fuso horario]] local.",
+ "smw-prefs-general-options-jobqueue-watchlist": "Amosar a lista de vixilancia da cola de tarefas na miña barra persoal",
+ "smw-prefs-general-options-disable-editpage-info": "Desactivar o texto introdutorio da páxina de edición",
+ "smw-prefs-general-options-suggester-textinput": "Activar a [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistencia de entrada] para as suxestións das entidades semánticas",
+ "smw-ui-tooltip-title-property": "Propiedade",
+ "smw-ui-tooltip-title-quantity": "Conversión de unidade",
+ "smw-ui-tooltip-title-info": "Información",
+ "smw-ui-tooltip-title-service": "Ligazóns de servizo",
+ "smw-ui-tooltip-title-warning": "Aviso",
+ "smw-ui-tooltip-title-error": "Erro",
+ "smw-ui-tooltip-title-parameter": "Parámetro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Lenda",
+ "smw-ui-tooltip-title-reference": "Referencia",
+ "smw_unknowntype": "O tipo desta propiedade non é válido",
+ "smw-concept-cache-text": "O concepto ten {{PLURAL:$1|unha páxina|un total de $1 páxinas}}. A súa última actualización foi o $3, $2.",
+ "smw_concept_header": "Páxinas do concepto \"$1\"",
+ "smw_conceptarticlecount": "A continuación {{PLURAL:$1|móstrase $1 páxina|móstranse $1 páxinas}}.",
+ "smw-qp-empty-data": "Os datos solicitados poderían non amosarse debido a algúns criterios de selección insuficientes.",
+ "right-smw-admin": "Acceder ás tarefas administrativas (Semantic MediaWiki)",
+ "right-smw-patternedit": "Acceso de edición para manter expresións regulares e patróns permitidos (MediaWiki Semántica)",
+ "right-smw-pageedit": "Acceso de edición para páxinas con anotación <code>Protexida contra a edición</code> (MediaWiki Semántica)",
+ "right-smw-ruleedit": "Editar páxinas de regra (MediaWiki Semántica)",
+ "restriction-level-smw-pageedit": "protexida (só os usuarios elexibles)",
+ "action-smw-patternedit": "editar expresións regulares utilizadas por MediaWiki Semántica",
+ "action-smw-pageedit": "editar páxinas anotadas con <code>Protexida contra a edición</code> (MediaWiki Semántica)",
+ "group-smwadministrator": "Administradores (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrador|administradora}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administradores (Semantic MediaWiki)",
+ "group-smwcurator": "Conservadores (MediaWiki Semántica)",
+ "group-smwcurator-member": "{{GENDER:$1|conservador|conservadora}} (MediaWiki Semántica)",
+ "grouppage-smwcurator": "{{ns:project}}:Conservadores (MediaWiki Semántica)",
+ "action-smw-admin": "acceder ás tarefas administrativas de Semantic MediaWiki",
+ "action-smw-ruleedit": "editar páxinas de regra (MediaWiki Semántica)",
+ "smw-property-predefined-default": "\"$1\" é unha propiedade predefinida.",
+ "smw-property-predefined-common": "Esta propiedade está pre-implantada (tamén coñecida como [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedade especial]) e ven con privilexios administrativos adicionais pero só pode ser usada como calquera outra [https://www.semantic-mediawiki.org/wiki/Property propiedade definida polo usuario].",
+ "smw-property-predefined-ask": "\"$1\" é unha propiedade predefinida que representa a meta información (na forma dun [https://www.semantic-mediawiki.org/wiki/Subobject subobxecto]) sobre pescudas individuais. E está proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-asksi": "\"$1\" é unha propiedade predefinida que recolle o número de condicións utilizadas nunha pescuda. E está proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-askde": "\"$1\" é unha propiedade predefinida que informa sobre a profundidade (valor numérico calculado na base dun aniñamento de consultas e a resolución de cadeas de propiedade) dunha consulta, e é proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-long-askde": "É un valor numérico calculado en base ó aniñamento de subconsultas, cadeas de propiedades, e os elementos de descrición dispoñibles coa execución dunha consulta baseada no parámetro de configuración <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "«$1» é unha propiedade predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica], que describe parámetros que inflúen no resultado dunha consulta.",
+ "smw-property-predefined-long-askpa": "É parte dunha colección de propiedades que especifican un [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Esta páxina lista as [https://www.semantic-mediawiki.org/wiki/Property propiedades] e o seu número de usos dispoñible para este wiki. Para ver as estatísticas actualizadas recoméndase que o script de mantemento das [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics estatísticas das propiedades] se execute de forma regular. Para ver unha vista diferenciada, consulte as páxinas especiais das [[Special:UnusedProperties|propiedades sen uso]] ou as [[Special:WantedProperties|propiedades requiridas]].",
+ "smw-sp-properties-cache-info": "Os datos da lista obtivéronse da [https://www.semantic-mediawiki.org/wiki/Caching caché], e actualizáronse o $1.",
+ "smw-sp-properties-header-label": "Lista de propiedades",
+ "smw-admin-settings-docu": "Amosa unha lista de tódolos parámetros predeterminados e localizados relevantes para o contorno de Semantic MediaWiki. Para obter os detalles de configuración individuais, consulte a páxina de axuda sobre a [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-sp-admin-settings-button": "Xerar a lista de opcións",
+ "smw-admin-idlookup-title": "Consulta",
+ "smw-admin-idlookup-docu": "Solicita os detalles sobre un identificador interno de obxecto que representa unha entidade individual (páxina wiki, subobxecto, etc.) de MediaWiki Semántica. O identificador non debe confundirse co identificador da páxina de MediaWiki ou co identificador de revisión.",
+ "smw-admin-iddispose-title": "Liberado",
+ "smw-admin-iddispose-docu": "A operación de eliminación non ten restricións e eliminará un identificador de obxecto interno do motor de almacenamento, se se confirma. Por favor, realice esta tarefa con \"precaución\" e só despois de consultar a [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentación].",
+ "smw-admin-iddispose-done": "O ID \"$1\" foi eliminado do servidor de almacenamento.",
+ "smw-admin-iddispose-references": "Identificador \"$1\" con polo menos un rexistro activo de referencia de táboa.",
+ "smw-admin-iddispose-references-multiple": "Lista de correspondencias con polo menos un rexistro de referencia activo.",
+ "smw-admin-iddispose-no-references": "O identificador \"$1\" non foi atopado ou non contiña ningunha referencia.",
+ "smw-admin-idlookup-input": "Procura:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Resumo",
+ "smw-admin-tab-notices": "Avisos de obsolescencia",
+ "smw-admin-tab-supplement": "Funcións suplementarias",
+ "smw-admin-tab-registry": "Rexistro",
+ "smw-livepreview-loading": "Cargando...",
+ "smw-sp-searchbyproperty-description": "Esta páxina proporciona unha [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interfase de navegación] simple para atopar entidades descritas por unha propiedade e un valor nomeado. Outras interfases de busca dispoñibles inclúen a [[Special:PageProperty|busca de propiedades de páxina ]], e o [[Special:Ask|xerador de consultas ask]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista de resultados",
+ "smw-sp-searchbyproperty-nonvaluequery": "Unha lista de valores que teñen a propiedade \"$1\" asignada.",
+ "smw-sp-searchbyproperty-valuequery": "Unha lista de páxinas que teñen a propiedade \"$1\" co valor \"$2! anotado.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" non pode asignarse a un tipo de número declarado con valor $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" devolveu \"NULL\" que non está permitido como número.",
+ "smw-editpage-annotation-enabled": "Esta páxina permite anotacións semánticas no texto (p. ex. <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) para crear contido estruturado e consultable proporcionado para a MediaWiki Semántica. Para unha descrición detallada de como utilizar as anotacións ou a función de análise #ask consulte as páxinas de axuda [https://www.semantic-mediawiki.org/wiki/Help:Getting_started primeiros pasos], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation anotación no texto] ou [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries consultas no texto].",
+ "smw-editpage-annotation-disabled": "Esta páxina non permite incluír anotacións semánticas no texto por restriccións do espazo de nomes. Ten máis detalles de como habilitar o espazo de nomes na páxina de axuda da [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuración].",
+ "smw-editpage-property-annotation-enabled": "Esta propiedade pode ampliarse mediante anotacións semánticas para especificar un tipo de datos (p. ex.,<nowiki>\"[[</nowiki>Ten tipo::Páxina]]\") ou outras declaracións deste tipo (p. ex.,<nowiki>\"[[</nowiki>Subpropiedade de::dc:data]]\"). Para unha descrición sobre como aumentar esta páxina, consulte as páxinas de axuda de [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaración dunha propiedade] ou [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes lista de tipos de datos].<!--[[Has type::Page]], [[Subproperty of::dc:date]]-->",
+ "smw-editpage-property-annotation-disabled": "Esta propiedade non pode ampliarse cunha anotación de tipo de dato (p.ex.<nowiki>\"[[</nowiki>Ten tipo::Páxina]]\") porque xa está predefinida (vexa a páxina de axuda das [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propiedades especiais] para máis información). <!-- [[Has type::Page]]-->",
+ "smw-editpage-concept-annotation-enabled": "Este concepto pode ampliarse usando a función de análise #concept. Para unha descrición de como usar #concept, consulte a páxina de axuda sobre os [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceptos].",
+ "smw-search-syntax-support": "Nas procuras pódese utilizar a [https://www.semantic-mediawiki.org/wiki/Help:Semantic_searchsintaxe de consultas semánticas] para axudar a atopar correspondencias usando a MediaWiki Semántica.",
+ "smw-search-input-assistance": "O [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance asistente de entrada] está activado tamén, para permitir unha preselección de entidades dispoñibles.",
+ "smw-search-input": "Entrada e procura",
+ "smw-search-syntax": "Sintaxe",
+ "smw-search-profile": "Extendida",
+ "smw-search-profile-tooltip": "Busca funcións en conexión con MediaWiki Semántica",
+ "smw-search-profile-sort-best": "Mellor correspondencia",
+ "smw-search-profile-sort-recent": "Máis recente",
+ "smw-search-profile-sort-title": "Título",
+ "smw-search-profile-extended-help-sort": "Especifica unha preferencia de ordenamento para amosar os resultados con:",
+ "smw-search-profile-extended-help-sort-title": "* \"Título\" usando o título da páxina (ou o título en pantalla) como criterio de ordenación",
+ "smw-search-profile-extended-help-sort-best": "* \"Mellor correspondencia\" ordenará os resultados por [https://www.semantic-mediawiki.org/wiki/help:ElasticStore/Relevancy relevancia] baseándose nas puntuacións provistas por Elasticsearch",
+ "smw-search-profile-extended-help-namespace": "A caixa de selección do espazo de nomes estará oculta en canto se seleccione un formulario, pero pode facerse visible de novo coa axuda do botón \"amosar/ocultar\".",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> para non coincidir con ningunha entidade que inclúa \"...\"",
+ "smw-search-profile-extended-help-query": "<code><nowiki>$1</nowiki></code> usado como consulta.",
+ "smw-search-profile-extended-help-query-link": "(Para máis detalles $1).",
+ "smw-search-profile-extended-help-find-forms": "formularios dispoñibles",
+ "smw-search-profile-extended-section-sort": "Ordenar por",
+ "smw-search-profile-extended-section-form": "Formularios",
+ "smw-search-profile-extended-section-search-syntax": "Entrada de procura",
+ "smw-search-profile-extended-section-namespace": "Espazo de nomes",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "smw-search-profile-link-caption-query": "ver",
+ "smw-search-show": "Amosar",
+ "smw-search-hide": "Agochar",
+ "log-name-smw": "Rexisto de MediaWiki Semántica",
+ "log-show-hide-smw": "Rexistro de MediaWiki Semántica $1",
+ "logeventslist-smw-log": "Rexisto de MediaWiki Semántica",
+ "log-description-smw": "Actividades dos [https://www.semantic-mediawiki.org/wiki/Help:Logging tipos de evento habilitados] que foron informados pola MediaWiki Semántica e os seus compoñentes.",
+ "logentry-smw-maintenance": "Eventos relacionados co mantemento emitidos por MediaWiki Semántica",
+ "smw-datavalue-import-unknown-namespace": "O espazo de nomes de importación \"$1\" é descoñecido. Por favor, asegúrese que os detalles de importación OWL están dispoñibles mediante [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "Non se puido atopar un URI do espazo de nomes de \"$1\" na [[MediaWiki:Smw import $1|importación de $1]].",
+ "smw-datavalue-import-missing-type": "Non se atopou definición de tipo para \"$1\" na [[MediaWiki:Smw import $2|importación de $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|importación de $1]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" non é un formato válido, debería ter o formato \"espazo de nome\":\"identificador\" (p.ex. \"foaf:nome\").",
+ "smw-datavalue-import-invalid-format": "A cadea \"$1\" debía estar dividida en catro partes pero o formato non é correcto.",
+ "smw-property-predefined-impo": "\"$1\" é unha propiedade predefinida que describe a relación cun [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulario importado]. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-type": "\"$1\" é unha propiedade predefinida que describe o [[Special:Types|tipo de datos]] dunha propiedade. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-sobj": "\"$1\" é unha propiedade predefinida que representa un construtor [https://www.semantic-mediawiki.org/wiki/Help:Container contedor]. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-long-sobj": "O contedor permite acumular asignacións dos valores de propiedade do mesmo xeito a como se fai nunha páxina wiki normal pero cun espazo de entidades diferente que será ligado ó suxeito incorporado.",
+ "smw-property-predefined-errp": "\"$1\" é unha propiedade predefinida que rastrexa erros de entrada na procura de valores de anotación incorrectos. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-long-errp": "Na maioría dos casos está provocado por un erro de correspondencia de tipos ou unha restrición de [[Property:Allows value|valore]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value\"$1\"] é unha propiedade predefinida que pode definir unha lista de valores posibles para restrinxir a asignación de valores dunha propiedade. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] é unha propiedade predefinida que pode especificar unha referencia a unha lista que contén valores permitidos para restrinxir as asignacións de valor para unha propiedade, é proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-datavalue-property-restricted-annotation-use": "A propiedade «$1» ten unha zona de aplicación limitada e os usuarios non poden utilizala como propiedade de anotación.",
+ "smw-datavalue-property-restricted-declarative-use": "A propiedade «$1» é unha propiedade declarativa e só pode utilizarse en páxinas de propiedade ou de categoría.",
+ "smw-datavalue-property-create-restriction": "A propiedade \"$1\" non existe e o usuario non ten o permiso \"$2\" (consulte [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade]) para crear ou anotar valores cunha propiedade non aprobada.",
+ "smw-datavalue-property-invalid-character": "«$1» contén un caracter listado «$2» como unha parte da etiqueta de propiedade e polo tanto foi clasificado como non válido.",
+ "smw-datavalue-property-invalid-chain": "O uso de «$1» como unha cadea de propiedade non está permitido durante o proceso de anotación.",
+ "smw-datavalue-restricted-use": "O valor de datos \"$1\" foi marcado para uso restrinxido.",
+ "smw-datavalue-invalid-number": "Non se pode interpretar \"$1\" coma un número.",
+ "smw-query-condition-circular": "Detectouse unha posible condición circular en \"$1\".",
+ "smw-query-condition-empty": "A descrición da consulta ten unha condición baleira.",
+ "smw-types-list": "Lista de tipos de datos",
+ "smw-types-default": "\"$1\" é un tipo de datos predefinido.",
+ "smw-types-help": "Máis información e exemplos poden atoparse nesta [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 páxina de axuda].",
+ "smw-type-anu": "\"$1\" é unha variante do tipo de datos [[Special:Types/URL|URL]] que se usa principalmente para declaración de exportación de ''owl:AnnotationProperty''.",
+ "smw-type-boo": "\"$1\" é un tipo de dato básico para describir un valor certo/falso.",
+ "smw-type-cod": "\"$1\" é unha variante do tipo de dato [[Special:Types/Text|Texto]] que é usada para textos técnicos de lonxitude variable, como listados de código fonte.",
+ "smw-type-geo": "\"$1\" é un tipo de dato que describe localizacións xeográficas e require a [https://www.semantic-mediawiki.org/wiki/Extension:Maps extensión \"Maps\"].",
+ "smw-type-tel": "\"$1\" é un tipo de datos especial para describir números de teléfono internacionais segundo o RFC 3966.",
+ "smw-type-txt": "\"$1\" é un antigo tipo de datos para describir cadeas de texto de lonxitude variable.",
+ "smw-type-dat": "\"$1\" é un tipo de dato para representar puntos no tempo nun formato unificado.",
+ "smw-type-ema": "\"$1\" é un tipo de datos especial para representar un correo electrónico.",
+ "smw-type-tem": "\"$1\" é un tipo de datos numérico especial para representar unha temperatura.",
+ "smw-type-qty": "\"$1\" é un tipo de datos para describir cantidades con unha representación numérica e unha unidade de medida.",
+ "smw-type-rec": "\"$1\" é un tipo de datos contedor que especifica unha lista de propiedades con tipos, nunha orde fixa.",
+ "smw-type-extra-tem": "O esquema de conversión inclúe as unidades soportadas, como Kelvin, Celsius, Fahrenheit e Rankine.",
+ "smw-type-tab-properties": "Propiedades",
+ "smw-type-tab-types": "Tipos",
+ "smw-type-tab-errors": "Erros",
+ "smw-type-primitive": "Básico",
+ "smw-type-contextual": "Contextual",
+ "smw-type-compound": "Composto",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Esta páxina proporciona unha interface de navegación para atopar tódolos valores dunha propiedade nunha páxina dada. Outras interfaces de procura dispoñibles inclúen a [[Special:SearchByProperty|procura de propiedades]] e o [[Special:Ask|construtor de preguntas]].",
+ "smw-property-predefined-errc": "\"$1\" é unha propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] e representa erros que aparecen en conexión con anotacións de valores impropios ou do procesamento da entrada.",
+ "smw-property-predefined-long-errc": "Os erros son recollidos nun [https://www.semantic-mediawiki.org/wiki/Help:Container contedor] que pode incluír unha referencia á propiedade que causou o problema.",
+ "smw-property-predefined-errt": "\"$1\" é unha propiedade predefinida que contén unha descrición textual dun erro. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-subobject-parser-invalid-naming-scheme": "Un subobxecto definido polo usuario contiña un esquema de nomeado incorrecto. O uso dun punto ($1) dentro dos primeiros cinco caracteres está reservado para as extensións. Vostede pode definir un [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador nomeado].",
+ "smw-datavalue-record-invalid-property-declaration": "A definición de rexistro contén a propiedade \"$1\" que está declarada como tipo rexistro e iso non está permitido.",
+ "smw-property-predefined-mdat": "\"$1\" é unha propiedade predefinida que corresponde á data da última modificación dun asunto. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-cdat": "\"$1\" é unha propiedade predefinida que corresponde á data da primeira modificación dun asunto. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-newp": "\"$1\" é unha propiedade predefinida que indica se un asunto é novo ou non. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-ledt": "\"$1\" é unha propiedade predefinida que contén o nome da páxina do usuario que fixo a última modificación. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-mime": "\"$1\" é unha propiedade predefinida que describe o tipo MIME dun ficheiro subido. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-media": "\"$1\" é unha propiedade predefinida que describe o tipo multimedia dun ficheiro subido. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-askfo": "\"$1\" é unha propiedade predefinida que contén o nome do formato de resultado usado nunha consulta. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-askst": "\"$1\" é unha propiedade predefinida que describe as condicións da consulta como unha cadea. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-askdu": "\"$1\" é unha propiedade predefinida que contén o valor de tempo (en segundos) que a consulta requiriu para completar a súa execución. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksc": "«$1» é unha propiedade predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica], que identifica fontes de consulta alternativas (por exemplo, fontes remotas ou federadas).",
+ "smw-property-predefined-askco": "\"$1\" é unha propiedade predefinida proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para describir o estado dunha consulta ou dos seus compoñentes.",
+ "smw-property-predefined-long-askco": "O número ou números asignados representan un estado codificado interno que está explicado na [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler páxina de axuda].",
+ "smw-property-predefined-prec": "\"$1\" é unha propiedade predefinida que describe unha [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisión de mostra] (en díxitos decimais) para tipos de datos numéricos.",
+ "smw-types-extra-geo-not-available": "A [https://www.semantic-mediawiki.org/wiki/Extension:Maps extensión \"Maps\"] non foi detectada polo que \"$1\" está restrinxido nas súas capacidades de funcionamento.",
+ "smw-datavalue-monolingual-dataitem-missing": "Falta un elemento esperado para construír un valor composto monolingüe.",
+ "smw-datavalue-languagecode-missing": "Para a anotación \"$1\", o analizador non foi capaz de determinar un código de lingua (p.ex. \"foo@en\").",
+ "smw-datavalue-languagecode-invalid": "Non se recoñece \"$1\" como un código de lingua admitido.",
+ "smw-property-predefined-lcode": "\"$1\" é unha propiedade predefinida que representa un código de lingua con formato BCP47. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-type-mlt-rec": "\"$1\" é un tipo de datos [https://www.semantic-mediawiki.org/wiki/Help:Container contedor] que asocia un valor de texto cun [[Property:Language code|código de lingua]] específico.",
+ "smw-types-extra-mlt-lcode": "O tipo de datos{{PLURAL:$2|require|non require}} un código de lingua (ou sexa, {{PLURAL:$2|non se acepta| acéptase}} unha anotación de valor sen un código de lingua).",
+ "smw-property-predefined-text": "\"$1\" é unha propiedade predefinida que representa texto de lonxitude arbitraria. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-pdesc": "\"$1\" é unha propiedade predefinida que permite describir unha propiedade en contexto dun idioma. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "\"$1\" é unha propiedade predefinida para definir unha lista de propiedades usadas cunha propiedade de tipo [[Special:Types/Record|rexistro]]. É proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] Tempo de analise de notas en texto",
+ "smw-limitreport-intext-postproctime": "[SMW] duración de post-procesamento",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Tempo de actualización de almacenamento (na purga de páxinas)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw_allows_pattern": "Esta páxina debería conter unha lista de referencias (seguidas por [https://gl.wikipedia.org/wiki/Expresi%C3%B3n_regular expresións regulares]) para ser postas a disposición vía a propiedade [[Property:Allows pattern|Permitir as modificacións]]. Para editar esta páxina é preciso o permiso <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "A expresión regular \"$2\" clasificou a \"$1\" como non válida.",
+ "smw-datavalue-allows-pattern-reference-unknown": "A referencia de patrón \"$1\" non pode corresponderse cunha entrada en [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "A referencia de lista \"$1\" non se corresponde cunha páxina de [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "O contido da lista \"$1\" non contén elementos co marcador de lista *.",
+ "smw-datavalue-feature-not-supported": "A funcionalidade \"$1\" non é compatible ou foi desactivada neste wiki.",
+ "smw-property-predefined-pvap": "\"$1\" é unha propiedade predefinida que pode especificar unha [[MediaWiki:Smw allows pattern|referencia de patrón]] para facer coincidir [https://gl.wikipedia.org/wiki/Expresi%C3%B3n_regular expresións regulares]. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-dtitle": "\"$1\" é unha propiedade predefinida que pode asignar un título de visualización diferente a unha entidade. É proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-pvuc": "\"$1\" é unha propiedade predefinida, proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica], que restrinxe as asignacións de valores para cada instancia a valores únicos (ou un polo menos).",
+ "smw-property-predefined-long-pvuc": "A unicidade está establecida cando dous valores non son iguais na súa representación literal, e toda violación desta constante será considerada coma un erro.",
+ "smw-datavalue-uniqueness-constraint-error": "A propiedade \"$1\" só permite a asignación de valores únicos e \"$2\" xa está anotado no campo \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "A propiedade ''$1'' só permite anotacións de valores únicos, ''$2'' xa contén un valor asignado. ''$3'' viola a restrición de exclusividade.",
+ "smw-property-predefined-boo": "\"$1\" é un [[Special:Types/Boolean|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar valores booleanos.",
+ "smw-property-predefined-num": "\"$1\" é un [[Special:Types/Number|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar valores numéricos.",
+ "smw-property-predefined-dat": "\"$1\" é un [[Special:Types/Date|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar valores de datas.",
+ "smw-property-predefined-uri": "\"$1\" é un [[Special:Types/URL|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar valores de URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" é un [[Special:Types/Quantity|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar valores de cantidade.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\" contén un desprazamento e un identificador de zona que non está soportado.",
+ "smw-datavalue-time-invalid-values": "O valor \"$1\" contén información non interpretable na forma \"$2\".",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" contén algunha información non interpretable.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\" contén un guión innecesario ou outros caracteres que non son válidos para a interpretación dunha data.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" contén algúns componentes baleiros.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" contén máis de tres componentes, que son necesarios para a interpretación dunha data.",
+ "smw-datavalue-time-invalid-date-components-sequence": "\"$1\" contén unha secuencia que non puido interpretarse contra unha matriz de correspondencia dispoñible para os compoñentes dunha data.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" contén \"$2\" como un elemento horario que é inválido para o formato convencional de 12 horas.",
+ "smw-datavalue-time-invalid-jd": "Imposible interpretar o valor de entrada \"$1\" como un número válido JD (día xuliano) con \"$2\" sendo informado.",
+ "smw-datavalue-time-invalid-prehistoric": "Imposible interpretar un valor de entrada prehistórico \"$1\". Por exemplo, se especifica máis que os anos nun modelo de calendario pode devolver resultados incorrectos nun contexto prehistórico.",
+ "smw-datavalue-time-invalid": "Imposible interpretar o valor da entrada \"$1\" como unha data ou un compoñente de tempo válidos con \"$2\" sendo informado.",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Falta o espazo reservado \"$1\" no URI do formatador.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" é un enderezo URL non válido.",
+ "smw-datavalue-external-identifier-formatter-missing": "à propiedade fáltalle a atribución dun [[Property:External formatter uri|\"formatador externo de URI\"]].",
+ "smw-datavalue-keyword-maximum-length": "A palabra chave superou a lonxitud máxima de $1 {{PLURAL:$1|carácter|caracteres}}",
+ "smw-property-predefined-eid": "\"$1\" é un [[Special:Types/External identifier|tipo]] e propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para representar identificadores externos.",
+ "smw-property-predefined-peid": "\"$1\" é unha propiedade predefinida que especifica un identificador externo. E está proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica].",
+ "smw-property-predefined-pefu": "\"$1\" é unha propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica] para especificar un recurso externo cun espazo reservado.",
+ "smw-property-predefined-long-pefu": "O URI espérase que conteña un parámetro posicional que será axustado cun valor dun [[Special:Types/External identifier|external identifier|identificador externo]] para formar unha referencia de recurso válida.",
+ "smw-type-eid": "\"$1\" é unha variante do tipo de dato [[Special:Types/Text|Texto]] que require propriedades particulares para declarar un [[Property:External formatter uri|URI de formatador externo]].",
+ "smw-property-predefined-keyw": "«$1» é unha propiedade predefina e un [[Special:Types/Keyword|tipo]], proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica], que normaliza un texto e ten unha lonxitude de caracteres reducida.",
+ "smw-type-keyw": "«$1» é unha variante do tipo de dato [[Special:Types/Text|texto]] que ten unha lonxitude de caracteres limitada e normaliza a representación do seu contido.",
+ "smw-datavalue-stripmarker-parse-error": "O valor indicado \"$1\" contén [https://en.wikipedia.org/wiki/Help:Strip_markers marcadores de banda] e polo tanto non pode ser analizado abondo.",
+ "smw-datavalue-parse-error": "O valor dado \"$1\" non foi entendido.",
+ "smw-datavalue-propertylist-invalid-property-key": "A lista de propiedades \"$1\" contén unha clave de propiedade \"$2\" non válida.",
+ "smw-datavalue-type-invalid-typeuri": "O tipo \"$1\" non pode ser transformado nunha representación URI válida.",
+ "smw-datavalue-wikipage-missing-fragment-context": "O valor de entrada da wikipáxina \"$1\" non ser usado sen unha páxina de contexto.",
+ "smw-datavalue-wikipage-invalid-title": "O valor de entrada do tipo de páxina \"$1\" contén caracteres non válidos ou está incompleta e polo tanto pode provocar resultados inesperados nunha busca ou nun proceso de anotación.",
+ "smw-datavalue-wikipage-property-invalid-title": "A propiedade \"$1\" (como o tipo de páxina) co valor de entrada \"$2\" contén caracteres incorrectos ou está incompleta, e por iso pode producir resultados inesperados durante unha consulta ou nun proceso de anotación.",
+ "smw-datavalue-wikipage-empty": "O valor de entrada da wikipáxina está baleiro (p.ex. <code>[[SomeProperty::]], [[]]</code>) e non pode usarse como nome ou como parte dunha condición de busca.",
+ "smw-type-ref-rec": "\"$1\" é un tipo de [https://www.semantic-mediawiki.org/wiki/Container contedor] que permite gardar información adicional (p.ex. procedencia dos datos) sobre a asignación dun valor.",
+ "smw-datavalue-reference-outputformat": "$1 : $2",
+ "smw-datavalue-reference-invalid-fields-definition": "O tipo [[Special:Types/Reference|Referencia]] espera unha lista de propiedades para ser declarado usando a propiedade [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Ten campos].",
+ "smw-parser-invalid-json-format": "O analizador de JSON devolveu un \"$1\".",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" non pode usarse como etiqueta preferida por a lingua \"$2\" xa está asignada á etiqueta \"$3\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copiar ligazón no portapapeis",
+ "smw-property-userdefined-fixedtable": "\"$1\" estaba configurada como [https://www.semantic-mediawiki.org/wiki/Fixed_properties propiedade fixa] e calquera modificación da [https://www.semantic-mediawiki.org/wiki/Type_declaration declaración do seu tipo] precisa executar <code>setupStore.php</code> ou completar a tarefa especial [[Special:SemanticMediaWiki|\"Instalación e actualización da base de datos\"]].",
+ "smw-data-lookup": "Obtendo datos...",
+ "smw-data-lookup-with-wait": "A solicitude está a procesarse e pode levar algo de tempo.",
+ "smw-no-data-available": "Non hai datos dispoñibles.",
+ "smw-property-req-violation-missing-fields": "à propiedade «$1» fáltanlle detalles de declaración para o tipo anotado «$2» ó non definir a propiedade <code>Ten campos</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "à propiedade «$1» fáltanlle detalles de declaración para o tipo anotado porque non foi defina a propiedade <code>URI do formatador externo</code>.",
+ "smw-property-req-violation-predefined-type": "A propiedade \"$1\" é unha propiedade predefinida que contén unha declaración de tipo \"$2\" que é incompatible co tipo por defecto desta propiedade.",
+ "smw-property-req-violation-import-type": "Detectouse unha declaración de tipo que non é compatible co tipo predefinido do vocabulario importado \"$1\". En xeral, non é necesario declarar un tipo porque a información obtense da definición da importación.",
+ "smw-property-req-violation-change-propagation-locked-error": "A propiedade «$1» foi alterada e precisa que as entidades asignadas sexan reavaliadas usando un proceso de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagación de cambios]. A páxina da propiedade foi protexida ata que a actualización de especificación primaria estea completada para previr interrupcións intermedias ou especificacións contraditorias. O proceso pode levar un momento antes de que a páxina poida ser desprotexida, isto dependerá do tamaño e a frecuencia do planificador da [https://www.mediawiki.org/wiki/Manual:Job_queue cola de traballo] do planificador.",
+ "smw-category-invalid-value-assignment": "\"$1\" non é recoñecido como unha categoría válida ou unha anotación válida.",
+ "protect-level-smw-pageedit": "Permitir unicamente usuarios co permiso de edición de páxinas (Semantic MediaWiki)",
+ "smw-create-protection": "A creación da propiedade «$1» está restrinxida ós usuarios que conten co dereito «$2» axeitado (ou pertenzan ó [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios]), mentres estea activado o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade].",
+ "smw-create-protection-exists": "As modificacións á propiedade «$1» están restrinxidas ós usuarios que conten co dereito «$2» axeitado (ou pertenzan ó [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios]), mentres estea activado o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade].",
+ "smw-edit-protection": "Esta páxina está [[Property:Is edit protected|protexida]] para impedir a modificación accidental de datos e só pode ser editada por usuarios co dereito de edición (\"$1\") ou que estean no [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuarios] axeitado.",
+ "smw-edit-protection-disabled": "A protección contra edicións foi desactivada, polo tanto \"$1\" non pode ser usada para protexer páxinas de entidades contra edicións non autorizadas.",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki actualizou o estado de protección de acordo coa propiedade «Ten protección de modificación».",
+ "smw-edit-protection-enabled": "Protexido contra modificación (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Esta páxina está protexida e só pode ser editada por usuarios co <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions permiso] axeitado.",
+ "smw-property-predefined-edip": "\"$1\" é unha propiedade predefinida proporcionada por [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] para indicar se a modificación está protexida ou non.",
+ "smw-property-predefined-long-edip": "Aínda que calquera usuario pode engadir esta propiedade a un tema, só aqueles que posúan un privilexio especial poden editar ou revocar a protección dunha entidade despois de que lle sexa engadida.",
+ "smw-query-reference-link-label": "Referencia de consulta",
+ "smw-format-datatable-emptytable": "Non hai datos dispoñibles na táboa",
+ "smw-format-datatable-info": "Amosando _START_ to _END_ of _TOTAL_ entradas",
+ "smw-format-datatable-infoempty": "Amosando de 0 a 0 de 0 entradas",
+ "smw-format-datatable-infofiltered": "(filtradas dun _MAX_ entradas do total)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "Amosar as entradas de _MENU_",
+ "smw-format-datatable-loadingrecords": "Cargando...",
+ "smw-format-datatable-processing": "Procesando...",
+ "smw-format-datatable-search": "Procurar:",
+ "smw-format-datatable-zerorecords": "Non se atoparon rexistros que coincidisen",
+ "smw-format-datatable-first": "Primeiro",
+ "smw-format-datatable-last": "Último",
+ "smw-format-datatable-next": "Seguinte",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": activar para ordenar a columna ascendentemente",
+ "smw-format-datatable-sortdescending": ": activar para ordenar a columna descendentemente",
+ "smw-format-datatable-toolbar-export": "Exportar",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "A categoría «$1» contén un obxectivo de redirección non válido a un espazo de nomes que non é de categoría.",
+ "smw-parser-function-expensive-execution-limit": "A función do analizador sintáctico alcanzou o tempo límite autorizado para a súa execución (vexa o parámetro de configuración [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "MediaWiki Semántica lanzará unha actualización desta páxina porque son precisos algúns procesamentos antes da consulta.",
+ "apihelp-smwinfo-summary": "Módulo da API para obter información sobre estatísticas e outra meta información do MediaWiki Semántico.",
+ "apihelp-ask-summary": "Módulo da API para consultar a MediaWiki Semántica usando a linguaxe de preguntas.",
+ "apihelp-askargs-summary": "Módulo da API para realizar consultas en MediaWiki Semántica mediante a lenguaxe Ask en forma de lista de condicións, visualizacións e parámetros.",
+ "apihelp-browsebyproperty-summary": "Módulo da API para obter información sobre unha propiedade ou lista de propiedades.",
+ "apihelp-browsebysubject-summary": "Módulo da API para obter informaións sobre un tema.",
+ "apihelp-smwtask-summary": "Módulo da API para executar tarefas relacionadas co MediaWiki Semántico.",
+ "apihelp-smwbrowse-summary": "Módulo da API de MediaWiki Semántica para engadir compatibilidade a certas actividades de navegación.",
+ "smw-api-invalid-parameters": "Parámetros non válidos: \"$1\"",
+ "smw-property-page-list-count": "{{PLURAL:$1|Móstrase $1 páxina que utiliza|Móstranse $1 páxinas que utilizan}} esta propiedade.",
+ "smw-property-page-list-search-count": "{{PLURAL:$1|Móstrase $1 páxina que utiliza|Móstranse $1 páxinas que utilizan}} esta propiedade cunha correspondencia de valor «$2».",
+ "smw-property-reserved-category": "Categoría",
+ "smw-category": "Categoría",
+ "smw-datavalue-uri-invalid-scheme": "Non se incluíu «$1» nos esquemas de URI válidos.",
+ "smw-browse-property-group-title": "Grupo de propiedades",
+ "smw-browse-property-group-label": "Etiqueta do grupo de propiedades",
+ "smw-browse-property-group-description": "Descrición do grupo de propiedades",
+ "smw-property-predefined-ppgr": "«$1» é unha propiedade predefinida, proporcionada pola [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semántica], que identifica entidades (categorías, principalmente) empregadas como instancias de agrupamento para as propiedades.",
+ "smw-filter": "Filtro",
+ "smw-section-expand": "Expandir a sección",
+ "smw-section-collapse": "Contraer a sección",
+ "smw-ask-format-help-link": "Formato [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Axuda",
+ "smw-cheat-sheet": "Folla de referencia",
+ "smw-personal-jobqueue-watchlist": "Lista de vixilancia (cola de tarefas)",
+ "smw-property-predefined-label-skey": "Chave de ordenación",
+ "smw-processing": "Procesando...",
+ "smw-redirect-target-unresolvable": "O destino é irresoluble polo motivo «$1»",
+ "smw-types-title": "Tipo: $1",
+ "smw-schema-error": "Erro de validación.",
+ "smw-schema-error-violation": "Violación (\"$1\", \"$2\")",
+ "smw-schema-title": "Esquema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Tipo",
+ "smw-schema-tag": "{{PLURAL:$1|Etiqueta|Etiquetas}}",
+ "smw-ask-title-keyword-type": "Procura de palabras clave",
+ "smw-ask-message-keyword-type": "Esta procura coincide coa condición <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Non foi posible establecer conexión co destino remoto \"$1\".",
+ "smw-remote-source-disabled": "A fonte '''$1''' desactivou as solicitudes remotas.",
+ "smw-remote-source-unmatched-id": "A fonte '''$1''' non concorda coa versión de MediaWiki Semántica que pode soportar solicitudes remotas.",
+ "smw-remote-request-note": "O resultado recupérase a partir da fonte remota '''$1''' e é probable que o contido xerado conteña información non dispoñible na wiki actual.",
+ "smw-parameter-missing": "Falta o parámetro \"$1\".",
+ "smw-property-tab-usage": "Uso",
+ "smw-property-tab-redirects": "Sinónimos",
+ "smw-property-tab-subproperties": "Subpropiedades",
+ "smw-property-tab-errors": "Asignacións incorrectas",
+ "smw-property-tab-specification": "... mais",
+ "smw-concept-tab-list": "Lista",
+ "smw-concept-tab-errors": "Erros",
+ "smw-ask-tab-result": "Resultado",
+ "smw-ask-tab-extra": "Extras",
+ "smw-ask-tab-debug": "Depuración",
+ "smw-ask-tab-code": "Código"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/glk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/glk.json
new file mode 100644
index 00000000..16e5fcd4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/glk.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "V6rg",
+ "شیخ"
+ ]
+ },
+ "browse": "ويکيه مٚتٚن"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gom-deva.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gom-deva.json
new file mode 100644
index 00000000..cc941a37
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gom-deva.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Konknni mogi 24",
+ "The Discoverer"
+ ]
+ },
+ "browse": "विकींत भोवडी मार"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gom-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gom-latn.json
new file mode 100644
index 00000000..21141939
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gom-latn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "The Discoverer"
+ ]
+ },
+ "browse": "Wikint bhovdi mar"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/grc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/grc.json
new file mode 100644
index 00000000..d28b24de
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/grc.json
@@ -0,0 +1,29 @@
+{
+ "@metadata": {
+ "authors": [
+ "Crazymadlover",
+ "Omnipaedista",
+ "Gts-tg"
+ ]
+ },
+ "smw_finallistconjunct": ", καὶ",
+ "smw_printername_template": "ΠÏότυπον",
+ "smw_nofloat": "Τὸ \"$1\" οá½Îº ἔστιν á¼€Ïιθμός.",
+ "smw_exportrdf_submit": "Ἐξάγειν",
+ "properties": "Ἰδιότητες",
+ "types": "ΤÏποι",
+ "smw_ask_queryhead": "Πεῦσις",
+ "smw_ask_format_as": "ΜοÏφοποιεῖν ὡς:",
+ "smw_ask_defaultformat": "Ï€ÏοκαθωÏισμένη",
+ "smw_sbv_property": "Ἰδιότης:",
+ "smw_sbv_value": "Τιμή:",
+ "browse": "Îέμεσθαι βίκι",
+ "smw_browselink": "Ἲδιότηται πλοηγήσεως",
+ "smw_browse_go": "Ἱέναι",
+ "smw_inverse_label_default": "$1 τοῦ",
+ "smw_pp_type": "Ἰδιότης",
+ "smw_result_prev": "ΠÏοηγουμέναι",
+ "smw_result_next": "Ἑπόμεναι",
+ "smw_result_noresults": "Οá½Î´á½²Î½ ἀποτέλεσμα.",
+ "smw-livepreview-loading": "ΦοÏτίζειν…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gsw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gsw.json
new file mode 100644
index 00000000..8594b8f3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gsw.json
@@ -0,0 +1,194 @@
+{
+ "@metadata": {
+ "authors": [
+ "Als-Chlämens",
+ "Als-Holder",
+ "ì•„ë¼"
+ ]
+ },
+ "smw-desc": "Dyy Wiki zuegängliger mache - fir Maschine ''un'' Mänsche ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online-Dokumäntation])",
+ "smw_viewasrdf": "RDF-Feed",
+ "smw_finallistconjunct": " un",
+ "smw_factbox_head": "Fakte zue $1",
+ "smw_isspecprop": "Die Eigeschaft isch e Spezialeigeschaft in däm Wiki.",
+ "smw_concept_description": "Bschryybig vum Konzäpt „$1“",
+ "smw_no_concept_namespace": "Konzäpt chenne nume im ''Konzäpt:'' Namensruum aagleit wäre.",
+ "smw_multiple_concepts": "In jedwädere Konzäptsyte cha s nume ei Konzäptdefinition din haa.",
+ "smw_concept_cache_miss": "S Konzäpt „$1“ cha im Momänt nit aagwändet wäre, wel d Wiki-Konfiguration offline grächnet muess wäre.\nWänn s Probläm imfall noch eme Rung nit verschwindet, no bitt Dyy Syteverwalter, des Konzäpt megli z mache.",
+ "smw_noinvannot": "Wärt chenne nit mit umgchehrte Eigeschafte gchännzeichnet wäre",
+ "version-semantic": "Semantischi Erwyterige",
+ "smw_baduri": "URI mit dr Form „$1“ sin nit zuelässig.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Zellerergebnis",
+ "smw_printername_csv": "CSV-Export",
+ "smw_printername_dsv": "DSV-Export",
+ "smw_printername_debug": "Debug-Abfrog (fir Experte)",
+ "smw_printername_embedded": "Inhalt vu yyböute Syte",
+ "smw_printername_json": "JSON-Export",
+ "smw_printername_list": "Lischt",
+ "smw_printername_ol": "Uflischtig",
+ "smw_printername_ul": "Ufgliderig",
+ "smw_printername_table": "Tabälle",
+ "smw_printername_broadtable": "Breiti Tabälle",
+ "smw_printername_template": "Vorlag",
+ "smw_printername_rdf": "RDF-Export",
+ "smw-paramdesc-limit": "Di maximal Aazahl vu Ergebnis, wu zruck gee solle wäre",
+ "smw-paramdesc-headers": "Chopfzyyle bzw. d Nämme vu dr Eigeschafte aazeige",
+ "smw-paramdesc-mainlabel": "S Label vu Hauptsytename",
+ "smw-paramdesc-link": "Wärt as Links aazeige",
+ "smw-paramdesc-intro": "Dr Text, wu vor dr Suechergebnis soll aazeigt wäre, wänn s het",
+ "smw-paramdesc-outro": "Dr Text, wu no dr Suechergebnis soll aazeigt wäre, wänn s het",
+ "smw-paramdesc-default": "Dr Text, wu soll aazeigt wäre, wänn s kei Suechergebnis het",
+ "smw-paramdesc-sep": "S Trännzeiche fir Wärt",
+ "smw-paramdesc-template": "Dr Name vu dr Vorlag, wu Uusdruck dermit solle aazeigt wäre",
+ "smw-paramdesc-columns": "D Aazahl vu dr Spalte go Ergebnis aazeige (Standard isch $1)",
+ "smw-paramdesc-userparam": "E Wärt, wu bi jedem Vorlageufruef ibergee wird, wänn e Vorlag brucht wird",
+ "smw-paramdesc-introtemplate": "Dr Vorlagename, wu vor dr Abfrogergebnis aazeigt wird, wänn s git",
+ "smw-paramdesc-outrotemplate": "Dr Vorlagename, wu no dr Abfrogergebnis soll aazeigt wäre, wänn s het",
+ "smw-paramdesc-embedformat": "Dr HTML-Befähl zum Iberschrifte definiere",
+ "smw-paramdesc-embedonly": "Kei Iberschrifte aazeige",
+ "smw-paramdesc-rdfsyntax": "D RDF-Syntax, wu brucht wäre soll",
+ "smw-paramdesc-csv-sep": "Trännzeiche, wu sott brucht wäre",
+ "smw-paramdesc-dsv-separator": "Trännzeiche, wu sott brucht wäre",
+ "smw-paramdesc-dsv-filename": "Dr Name fir d DSV-Datei",
+ "smw-paramdesc-searchlabel": "De Teggst für de Link zue de Ergebniss",
+ "smw_iq_disabled": "Semantischi Aafroge sin in däm Wiki zur Zyt nit megli.",
+ "smw_iq_moreresults": "… meh Ergebniss",
+ "smw_parseerror": "Dr Wärt, wu Du yygee hesch, isch nit verstande wore.",
+ "smw_notitle": "„$1“ cha nit as Sytename in däm Wiki bruucht wäre.",
+ "smw_wrong_namespace": "Nume Syten im Namensruum „$1“ sin doo zuelässig.",
+ "smw_manytypes": "In dr Eigeschaft sin e paar Datetype zuegwise wore.",
+ "smw_emptystring": "Lääri Zeichefolge wäre nit akzeptiert.",
+ "smw_notinenum": "„$1“ ghert nit zue dr meglige Wärt vu däre Eigeschaft ($2).",
+ "smw_noboolean": "„$1“ isch kei Wohretswärt (wohr/falsch).",
+ "smw_true_words": "wohr,w,jo,j",
+ "smw_false_words": "falsch,f,nei,n",
+ "smw_nofloat": "„$1“ isch kei Zahl.",
+ "smw_infinite": "D Zahl „$1“ isch z lang.",
+ "smw_novalues": "Kei Wärt spezifiziert.",
+ "smw_nodatetime": "S Datum „$1“ isch nit verstande wore.",
+ "smw_toomanyclosing": "In dr Aafrog chemme z vyyl „$1“ vor.",
+ "smw_noclosingbrackets": "In dr Aafrog chunnt e „<nowiki>[[</nowiki>“ vor, isch aber nit mit eme „]]“ abgschlosse.",
+ "smw_misplacedsymbol": "S Symbol „$1“ isch an ere Stell bruucht wore, wu s nit sinnvoll isch.",
+ "smw_unexpectedpart": "Dr Teil „$1“ vu dr Aafrog isch nit verstande wore. D Ergebnis sin villicht nit wie s erwartet woren isch.",
+ "smw_emptysubquery": "In ere Teilaafrog het s kei Bedingig.",
+ "smw_misplacedsubquery": "E Teilaafrog isch an ere Stell bruucht wore, wu keini Teilaafroge derfe vorchu.",
+ "smw_valuesubquery": "Teilaafroge wäre fir Wärt vudr Eigeschaft „$1“ nit unterstitzt.",
+ "smw_badqueryatom": "E Teil „<nowiki>[[…]]</nowiki>“ vu dr Aafrog isch nit verstande wore.",
+ "smw_propvalueproblem": "Dr Wärt vu dr Eigeschaft „$1“ isch nit verstande wore.",
+ "smw_noqueryfeature": "E Teil Aafrog-Feature sin im Momänt mit däm Wiki nit megli un dää Teil vu dr Frog isch glescht wore ($1).",
+ "smw_noconjunctions": "UND-Verchnipfige in dr Aafroge wäre in däm Wiki nit unterstitzt un dää Teil vu dr Aafrog isch glescht wore ($1).",
+ "smw_nodisjunctions": "ODER-Verchnipfige wäre in däm Wiki nit unterstitzt un e Teil vu dr Aafrog isch glescht wore ($1).",
+ "smw_querytoolarge": "Die Aafrogbedingige chenne für Greßi un Tiefi vu Aafroge nit berucksichtigt wäre wäge dr Beschränkige, wu in däm Wiki giltig sin: $1.",
+ "smw_notemplategiven": "Ass die Aafrog cha bearbeitet wäre, muess im Parameter „template“ e Wärt yytrage syy.",
+ "smw_type_header": "Eigeschafte mit em Datetyp „$1“",
+ "smw_typearticlecount": "S {{PLURAL:$1|wird ei Eigeschaft|wäre $1 Eigeschafte}} mit däm Datetyp aazeigt:",
+ "smw_attribute_header": "Syte mit dr Eigeschaft „$1“",
+ "smw_attributearticlecount": "S {{PLURAL:$1|wird ei Syten|wäre $1 Syten}} aazeigt, wu die Eigeschaft {{PLURAL:$1|bruucht|bruuche}}:",
+ "smw_subproperty_header": "Untereigeschafte",
+ "smw-subpropertylist-count": "Die Eigeschaft het {{PLURAL:$1|die Untereigeschaft|die $1 Untereigeschafte}}:",
+ "exportrdf": "Syten as RDF exportiere",
+ "smw_exportrdf_docu": "Doo chenne Informationen iber einzelni Syten im RDF-Format abgruefe wäre. Bitte gib d Name vu dr Syte <i>zyylewyys</i> yy.",
+ "smw_exportrdf_recursive": "Exportier au alli relevante Syte rekursiv. Dänk dra, ass d Ergebnis chenne arg groß syy!",
+ "smw_exportrdf_backlinks": "Exportier au alli Syte, wu uf exportierti Syte verwyye. Leit e RDF aa, wu lyychter cha dursuecht wäre.",
+ "smw_exportrdf_lastdate": "Exportier keini Syte, wu nimi gänderet wore sin syt em Zytpunkt, wu aagee isch.",
+ "smw_exportrdf_submit": "Exportiere",
+ "uriresolver": "URI-Ufleser",
+ "properties": "Eigeschafte",
+ "smw_properties_docu": "In däm Wiki git s die Eigeschafte:",
+ "smw_property_template": "$1 mit Datetyp $2 ($3)",
+ "smw_propertylackspage": "Alli Eigeschafte sotte uf ere Syte bschribe syy!",
+ "smw_propertylackstype": "Fir die Eigeschaft isch kei Datetyp aagee wore ($1 wird vorerscht as Typ aagnuh).",
+ "smw_propertyhardlyused": "Die Eigeschaft wird im Wiki chuum bruucht!",
+ "unusedproperties": "Verwaisti Eigeschafte",
+ "smw_unusedproperties_docu": "Die Eigeschafte git s, trotz ass si nit bruucht wäre.",
+ "smw_unusedproperty_template": "$1 mit Datetyp $2",
+ "wantedproperties": "Gwinschti Eigeschafte",
+ "smw_wantedproperties_docu": "Die Attribut wäre no uf keinere Syte bschribe, trotz ass si scho in däm Wiki bruucht wäre.",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|Vorchu|Vorchu}})",
+ "smw_purge": "nej lade",
+ "types": "Datetype",
+ "smw_types_docu": "Die Datetype chenne Eigeschafte zuegwise wäre. E jede Datetyp het e eigeni Syte, wu gnaueri Informatione druf chenne yytrage wäre.",
+
+
+ "smw_uri_doc": "Dr URI-Ufleser setzt d Empfählige »[$1 W3C TAG finding on httpRange-14]« um.\nÄr sorgt defir, ass Mänsche nit zue Netzsyte wäre.",
+ "ask": "Semantischi Suechi",
+ "smw_ask_sortby": "Sortiere no Spalte (optional)",
+ "smw_ask_ascorder": "Obsgi",
+ "smw_ask_descorder": "Nidsgi",
+ "smw_ask_submit": "Ergebnis finde",
+ "smw_ask_editquery": "Aafrog bearbeite",
+ "smw_add_sortcondition": "[Sortieraawyysig zuefiege]",
+ "smw_ask_hidequery": "Aafrog uusblände",
+ "smw_ask_help": "Hilf",
+ "smw_ask_queryhead": "Aafrog",
+ "smw_ask_printhead": "Zuesätzligi Aagabe, wu solle aazeigt wäre",
+ "smw_ask_printdesc": "(ei Eigeschaftsname je Zyyle yyfiege)",
+ "smw_ask_format_as": "Formatiert as:",
+ "smw_ask_defaultformat": "Standard",
+ "smw_ask_otheroptions": "Andri Optione",
+ "smw_ask_show_embed": "Yyböute Code zeige",
+ "smw_ask_hide_embed": "Yyböute Code verstecke",
+ "smw_ask_embed_instr": "Verwänd dää Code unte go die Abstimmig in e Wikisyte yyböue.",
+ "searchbyproperty": "Iber e Eigeschaft sueche",
+ "smw_sbv_docu": "Diese Spezialsyte findet alli Syte, wu e bstimmte Wärt hän fir d Eigeschaft, wu aagee woren isch.",
+ "smw_sbv_novalue": "Bitte dr gwinscht Wärt yygee oder alli Wärte fir d Eigeschaft $1 aaluege.",
+ "smw_sbv_displayresult": "E Lischt vu allene Syte, wu e Eigeschaft $1 mit em Wärt $2 hän.",
+ "smw_sbv_displayresultfuzzy": "E Lischt vu allene Syte, wu d Eigeschaft „$1“ mit em Wärt „$2“ hän.\nWel nume wenig Ergebnis gfunde wore sin, wäre au ähnligi Wärt ufglischtet.",
+ "smw_sbv_property": "Eigeschaft:",
+ "smw_sbv_value": "Wärt:",
+ "smw_sbv_submit": "Ergebnis finde",
+ "browse": "Wiki browse",
+ "smw_browselink": "Eigeschaften aazeige",
+ "smw_browse_article": "Bitte gib dr Titel vun ere Syte yy.",
+ "smw_browse_go": "Gang",
+ "smw_browse_show_incoming": "zeig Eigeschafte, wu do druff verwyyse",
+ "smw_browse_hide_incoming": "versteck Eigeschafte, wu do druff verwyyse",
+ "smw_browse_no_outgoing": "Die Syte het kei Eigeschafte.",
+ "smw_browse_no_incoming": "Kei Eigeschafte verwyyse uf die Syte.",
+ "smw_inverse_label_default": "$1 vu",
+ "smw_inverse_label_property": "Umgchehrti Eigeschaftbezeichnig",
+ "pageproperty": "Eigeschaftswärt vun ere Syte",
+ "smw_pp_docu": "No allene Wärt sueche, wu e bstimmt Attribut fir d Syte hän, wu aagee woren isch. Gib e Syte un au ne Attribut yy.",
+ "smw_pp_from": "Vu dr Syte",
+ "smw_pp_type": "Eigeschaft",
+ "smw_pp_submit": "Ergebnis aazeige",
+ "smw_result_prev": "Zrugg",
+ "smw_result_next": "Firschi",
+ "smw_result_results": "Ergebnis",
+ "smw_result_noresults": "Kei Ergebnis gfunde.",
+ "smwadmin": "Admin-Funktione fir Semantic MediaWiki",
+ "smw_smwadmin_setupsuccess": "D Spychereinheit isch erfolgryych yygrichtet wore.",
+ "smw_smwadmin_return": "Zrugg zue $1",
+ "smw_smwadmin_updatestarted": "E neje Update-Prozäss fir d Aktualisierig vu dr semantische Date isch gstartet wore.\nAlli gspycherete Date wäre nej aagleit oder repariert, wänn netig.\nDu chasch dr Fortschritt vum Update uf däre Spezialsyte verfolge.",
+ "smw_smwadmin_updatenotstarted": "S lauft scho ne Update-Prozäss.\nS wird kei neje aagfange.",
+ "smw_smwadmin_updatestopped": "Alli Aktualisierigsprozäss, wu s git, sin aaghalte wore.",
+ "smw_smwadmin_updatenotstopped": "Go Prozäss aahlate, wu am laufe sin, muesch Du s Kontrollchäschtli aktiviere zum aazeige, ass Du Dir wirkli sicher bisch.",
+ "smw_smwadmin_docu": "Die Spezialsyte hilft währed dr Installation un em Upgrade vu <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>.\nDänk dra, wichtigi Date vor dr Uusfierig vu adminischtrative Funktione z sichere.",
+ "smw_smwadmin_db": "Datebankinstallation un -aktualisierig",
+ "smw_smwadmin_dbdocu": "Semantic MediaWiki bruucht e paar Erwyterige an dr MediaWiki-Datebank go di semantische Date z spychere.\nDie Funktion gwährleischtet, ass d Datebank richtig yygrichtet isch.\nD Änderige, wu in däm Schritt gmacht wäre, stere dr Räscht vu dr MediaWiki-Datebank nit un chenne eifach zrugggsetzt wäre, wänn gwinscht.\nDie Setup-Funktion cha e paar Mol uusgfiert wäre ohni Schade z verursache, s isch aber nume eimol netig bi dr Installation oder em Update.",
+ "smw_smwadmin_permissionswarn": "Wänn d Aktion mit eme SQL-Fähler abbricht, chennt s syy, ass dr Datebankbenutzer, wu s Wiki iber en uf d Datenbank zuegryft (lueg d Datei LocalSettings.php), nit d Rächt dezue het.\nGo des Probläm lese isch s megli, em Benutzer zuesätzligi Rächt fir s Aalege und Lesche vu Tabälle z gee, dr Datebank-Adminischtrator zytwyys in d LocalSettings.php yyztrage oder s Wartigsskript <code>setupStore.php</code> z verwände, wu d Benutzerdate us AdminSettings.php cha verwände.",
+ "smw_smwadmin_dbbutton": "Tabällen initialisiere oder aktualisere",
+ "smw_smwadmin_announce": "Dyy Wiki aachinde",
+ "smw_smwadmin_datarefresh": "Datereparatur un -aktualisierig",
+ "smw_smwadmin_datarefreshdocu": "S isch megli alli Datebanke vu Semantic MediaWiki uf dr Grundlag vum aktuälle Inhalt vum Wiki widerhärzstelle.\nDes cha hälfe, ne kaputti Datebank nej z lade oder Date z aktualisiere, wänn wägen eme Softwareupgrade d Datebankstruktur gänderet wore isch.\nS Update wird fir jedi Syte uusgfiert un wird e Wyyli bruuche.\nDoo wird zeigt, eb e Update lauft un Di wird erlaubt e Update z starte oder z stoppe (usser wänn die Funktion vum Sytebetryyber deaktiviert woren isch).",
+ "smw_smwadmin_datarefreshprogress": "<strong>E Update lauft scho.</strong>\nE Update bruucht nromalerwyys lang, wel d Date nume in chleine Prtione aktualisiert wäre, jedes Mol wänn eber uf s Wiki zuegryft.\nGo des Update schnäller z beände, cha mer s MediaWiki-Wartigsskript <code>runJobs.php</code> bruuche (mit em Parameter <code>--maxjobs 1000</code> cha d Aazahl vu dr Updates, wu uf eimol durgfiert wäre, bschränkt wäre).\nGschätzte Fortschritt vum Update, wu grad lauft:",
+ "smw_smwadmin_datarefreshbutton": "Mit dr Datenaktualisierig aafange",
+ "smw_smwadmin_datarefreshstop": "Mit däre Aktualisierig ufhere",
+ "smw_smwadmin_datarefreshstopconfirm": "Jo, ich bi mer sicher.",
+ "smw_smwadmin_support": "Unterstitzig iberchu",
+ "smw_smwadmin_supportdocu": "Verschideni Quälle hälfe Dir villicht bi me Problämfall:",
+ "smw_smwadmin_installfile": "Wänn s Probläm mit dr Inschtallation git, chennt d Datei <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL</a> villicht wyterhälfe.",
+ "smw_smwadmin_smwhomepage": "Di vollständig Benutzerdokumentation vu Semantic MediaWiki findsch uf <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw_smwadmin_mediazilla": "Fähler chennte bi <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> berichtet wäre.",
+ "smw_smwadmin_questions": "Wänn Du meh Frogen oder Vorschleg hesch, mach mit an dr Diskussion uf <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Semantic MediaWiki user forum</a>.",
+ "smw_adminlinks_datastructure": "Datestruktur",
+ "smw_adminlinks_displayingdata": "Date abbilde",
+ "smw_adminlinks_inlinequerieshelp": "Hilf fir Inline-Abfroge",
+ "smw-createproperty-isproperty": "Des isch e Eigeschaft vum Typ $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Dr erlaubt Wärt fir die Eigeschaft isch|Di erlaubte Wärt fir die Eigeschaft sin}}:",
+ "smw_unknowntype": "Imn dr Eigeschaft isch dr nit bekannt Datetyp „$1“ zuegwise wore.",
+ "smw_concept_header": "Syte vum Konzäpt „$1“",
+ "smw_conceptarticlecount": "S {{PLURAL:$1|wird ei Syten|wäre $1 Syten}} aazeigt, wu zue däm Konzäpt {{PLURAL:$1|ghert|ghere}}:",
+ "smw-livepreview-loading": "Am Lade …"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/gu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/gu.json
new file mode 100644
index 00000000..6146324c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/gu.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dsvyas"
+ ]
+ },
+ "browse": "વિકિ વાંચો",
+ "smw-livepreview-loading": "લવાઇ રહà«àª¯à«àª‚ છે..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hak.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hak.json
new file mode 100644
index 00000000..de688f13
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hak.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Chang-chhai chai-ngiÌp…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/haw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/haw.json
new file mode 100644
index 00000000..1eabdf67
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/haw.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ke ho‘ouka nei…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/he.json b/www/wiki/extensions/SemanticMediaWiki/i18n/he.json
new file mode 100644
index 00000000..113f7117
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/he.json
@@ -0,0 +1,517 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amire80",
+ "Inkbug",
+ "Rotemliss",
+ "Udi Oron ×ודי ×ורון",
+ "YaronSh",
+ "×—×™×™×",
+ "ערן",
+ "ì•„ë¼",
+ "MojoMann",
+ "Guycn2",
+ "Or",
+ "Nemo bis",
+ "Drorsnir",
+ "שני×ור זלמן",
+ "המקיסט",
+ "Strayblues"
+ ]
+ },
+ "smw-desc": "הופכת ×ת הוויקי ×©×œ×›× ×œ× ×’×™×© יותר – עבור מכונות ×•×’× ×¢×‘×•×¨ בני ××“× ([https://www.semantic-mediawiki.org/wiki/Help:User_manual תיעוד מקוון])",
+ "smw-title": "מדיה־ויקי סמנטית",
+ "smw-semantics-not-enabled": "התכונה \"מדיה־ויקי סמנטית\" ×œ× ×”×•×¤×¢×œ×” ב×תר ×–×”.",
+ "smw_viewasrdf": "הזנת RDF",
+ "smw_finallistconjunct": " וג×",
+ "smw-factbox-head": "... מידע נוסף על \"$1\"",
+ "smw-factbox-facts": "עובדות",
+ "smw-factbox-facts-help": "הצגת קביעות ועובדות שנוצרו על־ידי משתמש",
+ "smw-factbox-facts-derived": "עובדות נגזרות",
+ "smw-factbox-facts-derived-help": "הצגת עובדות שנגזרו מתוך ×—×•×§×™× ×ו ×¢× ×¢×–×¨×” משיטות הנמקה ×חרות",
+ "smw_isspecprop": "זהו מ×פיין מיוחד ב×תר ויקי ×–×”.",
+ "smw-concept-cache-header": "שימוש במטמון",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count מטמון הרעיונות] מכיל {{PLURAL:$1|ישות ×חת|'''$1''' ישויות}} ($2)",
+ "smw-concept-no-cache": "×ין מטמון זמין.",
+ "smw_concept_description": "תי×ור הרעיון \"$1\"",
+ "smw_no_concept_namespace": "ניתן להגדיר רעיונות רק ×‘×“×¤×™× ×©×‘×ž×¨×—×‘ ×”×©× Concept:.",
+ "smw_multiple_concepts": "לכל דף רעיון יכולה להיות רק הגדרת רעיון ×חת.",
+ "smw_concept_cache_miss": "×œ× × ×™×ª×Ÿ להשתמש ברעיון \"$1\" כרגע, כיוון שתצורת הוויקי דורשת ×ת עיבודו ב×ופן בלתי־מקוון.\n×× ×”×ª×§×œ×” ××™× ×” נעלמת ל×חר זמן־מה, בקשו ממנהל ×”×תר להפוך רעיון ×–×” לזמין.",
+ "smw_noinvannot": "×¢×¨×›×™× ××™× ×™×›×•×œ×™× ×œ×”×™×•×ª ×ž×•×©×ž×™× ×œ×ž××¤×™×™× ×™× ×”×¤×•×›×™×.",
+ "version-semantic": "הרחבות סמנטיות",
+ "smw_baduri": "כתובות מהצורה \"$1\" ×ינן מורשות.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "ספירת התוצ×ות",
+ "smw_printername_csv": "×™×™×¦×•× ×œÖ¾CVS",
+ "smw_printername_dsv": "×™×¦×•× DSV",
+ "smw_printername_debug": "חיפוש שגי×ות (למומחי×)",
+ "smw_printername_embedded": "הטמעת תוכן הדף",
+ "smw_printername_json": "×™×™×¦×•× JSON",
+ "smw_printername_list": "רשימה",
+ "smw_printername_plainlist": "רשימה רגילה",
+ "smw_printername_ol": "ספירה",
+ "smw_printername_ul": "פירוט",
+ "smw_printername_table": "טבלה",
+ "smw_printername_broadtable": "טבלה רחבה",
+ "smw_printername_template": "תבנית",
+ "smw_printername_templatefile": "קובץ התבנית",
+ "smw_printername_rdf": "×™×¦×•× RDF",
+ "smw_printername_category": "קטגוריה",
+ "validator-type-class-SMWParamSource": "טקסט",
+ "smw-paramdesc-limit": "מספר התוצ×ות המרבי להחזרה",
+ "smw-paramdesc-offset": "ההיסט של התוצ××” הר×שונה",
+ "smw-paramdesc-headers": "הצגת הכותרות ×ו שמות המ×פייני×",
+ "smw-paramdesc-mainlabel": "התווית שתינתן ×œ×©× ×”×“×£ הר×שי",
+ "smw-paramdesc-link": "הצגת ×¢×¨×›×™× ×›×§×™×©×•×¨×™×",
+ "smw-paramdesc-intro": "הטקסט להצגה לפני תוצ×ות התש×ול, ×× ×™×© ×›×לו",
+ "smw-paramdesc-outro": "הטקסט להצגה ל×חר תוצ×ות התש×ול, ×× ×™×© ×›×לו",
+ "smw-paramdesc-default": "הטקסט להצגה ×× ×ין תוצ×ות תש×ול",
+ "smw-paramdesc-sep": "המפריד בין התוצ×ות",
+ "smw-paramdesc-propsep": "המפריד בין המ××¤×™×™× ×™× ×©×œ ערך התוצ××”",
+ "smw-paramdesc-valuesep": "המפריד בין ×”×¢×¨×›×™× ×‘×ž×פיין של תוצ××”",
+ "smw-paramdesc-showsep": "הצגת מפריד בתחילת קובץ CSV (\"מפריד=<ערך>\")",
+ "smw-paramdesc-distribution": "×‘×ž×§×•× ×œ×”×¦×™×’ ×ת כל הערכי×, לספור ×ת ×”×ž×•×¤×¢×™× ×©×œ×”× ×•×œ×”×¦×™×’ ×ות×.",
+ "smw-paramdesc-distributionsort": "למיין ×ת התפלגות ×”×¢×¨×›×™× ×œ×¤×™ מניין המופעי×.",
+ "smw-paramdesc-distributionlimit": "להגביל ×ת התפלגות ×”×¢×¨×›×™× ×œ×¤×™ מניין של ×¢×¨×›×™× ×ž×¡×•×™×ž×™× ×‘×œ×‘×“.",
+ "smw-paramdesc-template": "×©× ×”×ª×‘× ×™×ª להצגת התדפיסי×",
+ "smw-paramdesc-columns": "מספר העמודות להצגת התוצ×ות",
+ "smw-paramdesc-userparam": "הערך המועבר לכל קרי××” לתבנית, ×× × ×¢×©×” שימוש בתבנית",
+ "smw-paramdesc-class": "הגדרת מחלקת CSS נוספת עבור הרשימה",
+ "smw-paramdesc-introtemplate": "×©× ×”×ª×‘× ×™×ª שתוצג לפני תוצ×ות הש×ילתה, ×× ×™×© תוצ×ות",
+ "smw-paramdesc-outrotemplate": "×©× ×”×ª×‘× ×™×ª שתוצג ×חרי תוצ×ות הש×ילתה, ×× ×™×© תוצ×ות",
+ "smw-paramdesc-embedformat": "תג ה־HTML המשמש להגדרת כותרות",
+ "smw-paramdesc-embedonly": "×œ× ×œ×”×¦×™×’ כותרות",
+ "smw-paramdesc-table-class": "מחלקת CSS נוספת שתוחל על הטבלה",
+ "smw-paramdesc-table-transpose": "הצגת כותרות טבלה ×נכית ותוצ×ות ×ופקית",
+ "smw-paramdesc-rdfsyntax": "ב××™×–×” תחביר RDF להשתמש",
+ "smw-paramdesc-csv-sep": "מגדיר מפריד טורי×",
+ "smw-paramdesc-dsv-separator": "ב××™×–×” תו הפרדה להשתמש",
+ "smw-paramdesc-dsv-filename": "×©× ×§×•×‘×¥ DSV",
+ "smw-paramdesc-filename": "×©× ×§×•×‘×¥ הפלט",
+ "smw-smwdoc-description": "הצגת טבלה של כל ×”×¤×¨×ž×˜×¨×™× ×©×פשר להשתמש ×‘×”× ×œ×ª×‘× ×™×ª התוצ××” ×”×–×ת יחד ×¢× ×¢×¨×›×™ בררת מחדל ותי×ורי×.",
+ "smw-smwdoc-par-format": "ל××™×–×” תסדיר תוצ××” להציג ×ת תיעוד הפרמטרי×.",
+ "smw-smwdoc-par-parameters": "×ילו ×¤×¨×ž×˜×¨×™× ×œ×”×¦×™×’. \"specific\" למה שמוסיף התסדיר, \"base\" למה שזמין בכל התסדירי×, ו־\"all\" לשניה×.",
+ "smw-paramdesc-sort": "המ×פיין שהש×ילתה תמוין לפיו",
+ "smw-paramdesc-order": "סדר מיון הש×ילתה",
+ "smw-paramdesc-searchlabel": "טקסט עבור המשך החיפוש",
+ "smw-paramdesc-named_args": "שמות ×”×¤×¨×ž×˜×¨×™× ×©×™×•×¢×‘×¨×• לתבנית",
+ "smw-paramdesc-import-annotation": "× ×ª×•× ×™× ×ž×•×¢×¨×™× × ×•×¡×¤×™× ×¢×ž×•×“×™× ×œ×”×™×•×ª ×ž×•×¢×ª×§×™× ×‘×–×ž×Ÿ פענוח של נוש×",
+ "smw-paramdesc-export": "×פשרות ייצו×",
+ "smw-paramdesc-prettyprint": "פלט מעוצב יפה ×¢× ×™×•×ª×¨ עימוד וירידות שורה",
+ "smw-paramdesc-json-unescape": "הפלט יכיל ×œ×•×›×¡× ×™× ×‘×œ×ª×™Ö¾×ž×—×•×œ×¤×™× ×•×ª×•×•×™ יוניקוד מרובי־בתי×.",
+ "smw-paramdesc-source": "מקור ש×ילתה חלופי",
+ "smw-paramdesc-jsonsyntax": "ב××™×–×” תחביר JSON להשתמש",
+ "smw-printername-feed": "הזנת Atom ו־RSS",
+ "smw-paramdesc-feedtype": "סוג הזנה",
+ "smw-paramdesc-feedtitle": "טקסט שישמש לכותרת ההזנה",
+ "smw-paramdesc-feeddescription": "טקסט שישמש לתי×ור ×”×”×–× ×”",
+ "smw-paramdesc-feedpagecontent": "תוכן דף שיוצג ×¢× ×”×”×–× ×”",
+ "smw-label-feed-description": "הזנת $2 של $1",
+ "smw_iq_disabled": "ש×ילתות סמנטיות בוטלו ב×תר ויקי ×–×”.",
+ "smw_iq_moreresults": "... תוצ×ות נוספות",
+ "smw_parseerror": "הערך שניתן ×œ× ×”×•×‘×Ÿ.",
+ "smw_notitle": "\"$1\" ×ינו יכול לשמש ×©× ×“×£ ב×תר ויקי ×–×”.",
+ "smw_noproperty": "המחרוזת \"$1\" ×œ× ×™×›×•×œ×” להיות ×©× ×©×œ מ×פיין בוויקי ×”×–×”.",
+ "smw_wrong_namespace": "רק ×“×¤×™× ×ž×ž×¨×—×‘ ×”×©× \"$1\" ×ž×•×¨×©×™× ×›×ן.",
+ "smw_manytypes": "הוגדר יותר מסוג ×חד למ×פיין ×–×”.",
+ "smw_emptystring": "מחרוזות ריקות ×ינן מתקבלות.",
+ "smw_notinenum": "\"$1\" ×ינו ברשימה ($2) של [[Property:Allows value|×”×¢×¨×›×™× ×”×פשריי×]] למ×פיין \"$3\".",
+ "smw_noboolean": "[×ופס! \"$1\" ×ינו מ×פיין מסוג נכון-ל×נכון]",
+ "smw_true_words": "true,t,yes,y,כן,נכון,×מת,חיובי,×›",
+ "smw_false_words": "false,f,no,n,ל×,×œ× × ×›×•×Ÿ,ל×-נכון,ש,שקר,שלילי,ל",
+ "smw_nofloat": "\"$1\" ×ינו מספר.",
+ "smw_infinite": "×ž×¡×¤×¨×™× ×’×“×•×œ×™× ×›×ž×• \"$1\" ××™× × × ×ª×ž×›×™×.",
+ "smw_unitnotallowed": "\"$1\" ××™× ×” יחידה תקינה למדידת המ×פיין ×”×–×”",
+ "smw_nounitsdeclared": "×œ× ×”×•×’×“×¨×• יחידות מידה למ×פיין ×”×–×”.",
+ "smw_novalues": "×œ× ×¦×•×™× ×• ערכי×.",
+ "smw_nodatetime": "הת×ריך \"$1\" ×ינו מובן.",
+ "smw_toomanyclosing": "נר××” שיש ×ž×•×¤×¢×™× ×¨×‘×™× ×ž×“×™ של \"$1\" בש×ילתה.",
+ "smw_noclosingbrackets": "בכמה ×ž×”×¤×¢×ž×™× ×©×‘×”× ×”×©×ª×ž×©×ª ב־\"<nowiki>[[</nowiki>\" בש×ילתה ×œ× ×“×גת להציב \"]]\" תו×× ×œ×¡×’×™×¨×”.",
+ "smw_misplacedsymbol": "נעשה שימוש בסימן \"$1\" ×‘×ž×§×•× ×©×”×•× ×œ× ×ž×•×¢×™×œ בו.",
+ "smw_unexpectedpart": "החלק \"$1\" מהש×ילתה ×œ× ×”×•×‘×Ÿ.\nייתכן שהתוצ×ות ×œ× ×™×ª×ימו למצופה.",
+ "smw_emptysubquery": "ל×חת מש×ילתות־המשנה ×ין תנ××™× ×ª×§×™× ×™×.",
+ "smw_misplacedsubquery": "נעשה שימוש בש×ילתת־משנה כלשהי ×‘×ž×§×•× ×©×‘×• ש×ילתות־משנה ×ינן מורשות.",
+ "smw_valuesubquery": "ש×ילתות־משנה ×ינן נתמכות עבור ערכי המ×פיין \"$1\".",
+ "smw_badqueryatom": "כמה ×—×œ×§×™× \"<nowiki>[[…]]</nowiki>\" מהש×ילתה ×œ× ×”×•×‘× ×•.",
+ "smw_propvalueproblem": "ערך המ×פיין \"$1\" ×œ× ×”×•×‘×Ÿ.",
+ "smw_noqueryfeature": "חלק מהתכונות שבש×ילתה ×ינן נתמכות ב×תר ויקי ×–×” ×•×—×œ×§×™× ×ž×”×©×ילתה הושמטו ($1).",
+ "smw_noconjunctions": "מילות חיבור בש×ילתתות ×ינן נתמכות בוויקי ×”×–×” וחלק מהש×ילתה נזרק ($1).",
+ "smw_nodisjunctions": "מילות הפרדה בש×ילתות ×ינן נתמכות בוויקי ×–×” וחלק מהש×ילתה נזרק ($1).",
+ "smw_querytoolarge": "×œ× ×”×™×” ×פשר לשקול ×ת תנ××™ הש×ילתה {{PLURAL:$2|הב×|הב××™×}} בשל המגבלות ש×תר הוויקי ×”×–×” מטיל על עומק ×ו גודל של ש×ילתה: <code>$1</code>.",
+ "smw_notemplategiven": "חובה לתת ערך לפרמטר \"template\" כדי שתסדיר הש×ילתה ×”×–×” יעבוד.",
+ "smw_db_sparqlqueryproblem": "×œ× × ×™×ª×Ÿ ×”×™×” לקבל ×ת תוצ×ת הש×ילתה ממסד ×”× ×ª×•× ×™× SPARQL. השגי××” ×”×–×ת יכולה להיות זמנית ×ו להצביע על ב××’ בתכנת מסד הנתוני×.",
+ "smw_db_sparqlqueryincomplete": "הסתבר שמענה לש×ילתה ×”×–×ת קשה מדי ×•×”×•× ×‘×•×˜×œ. ייתכן שתוצ×ות ×חדות חסרות. ×× ×–×” ×פשרי, יש לנסות ש×ילתה פשוטה יותר.",
+ "smw_type_header": "מ××¤×™×™× ×™× ×ž×¡×•×’ \"$1\"",
+ "smw_typearticlecount": "הצגת {{PLURAL:$1|מ×פיין ×חד המשתמש|$1 מ××¤×™×™× ×™× ×”×ž×©×ª×ž×©×™×}} בסוג ×–×”.",
+ "smw_attribute_header": "×“×¤×™× ×”×ž×©×ª×ž×©×™× ×‘×ž×פיין \"$1\"",
+ "smw_attributearticlecount": "הצגת {{PLURAL:$1|דף ×חד המשתמש|$1 ×“×¤×™× ×”×ž×©×ª×ž×©×™×}} במ×פיין ×–×”.",
+ "smw-propertylist-subproperty-header": "מ×פייני־משנה",
+ "smw-propertylist-redirect-header": "שמות נרדפי×",
+ "smw-propertylist-count": "{{PLURAL:$1|מוצגת ישות קשורה ×חת|מוצגות $1 ישויות קשורות}}.",
+ "smw-propertylist-count-with-restricted-note": "{{PLURAL:$1|מוצגת ישות קשורה ×חת|מוצגות $1 ישויות קשורות}} (ישויות נוספות זמינות, ×ך התצוגה מוגבלת ל־$2).",
+ "smw-propertylist-count-more-available": "{{PLURAL:$1|מוצגת ישות קשורה ×חת|מוצגות $1 ישויות קשורות}} (ישויות נוספות זמינות).",
+ "specialpages-group-smw_group": "מדיה־ויקי סמנטית",
+ "exportrdf": "×™×™×¦×•× ×“×¤×™× ×œÖ¾RDF",
+ "smw_exportrdf_docu": "דף ×–×” מ×פשר ×œ×›× ×œ×חזר × ×ª×•× ×™× ×ž×“×£ בתסדיר RDF.\nכדי ×œ×™×™×¦× ×“×¤×™×, כתבו ×ת כותרות ×”×“×¤×™× ×‘×ª×™×‘×ª הטקסט שלהלן, כותרת ×חת בכל שורה.",
+ "smw_exportrdf_recursive": "×™×™×¦×•× ×¨×§×•×¨×¡×™×‘×™ של כל ×”×“×¤×™× ×”×§×©×•×¨×™×.\nשימו לב שהתוצ××” עלולה להיות גדולה!",
+ "smw_exportrdf_backlinks": "×œ×™×™×¦× ×’× ×ת כל ×”×“×¤×™× ×”×ž×¤× ×™× ×œ×“×¤×™× ×”×ž×™×•×¦××™×.\nייווצר RDF הניתן לעיון.",
+ "smw_exportrdf_lastdate": "×ין ×œ×™×™×¦× ×“×¤×™× ×©×œ× ×©×•× ×• מ××– נקודת הזמן שצוינה.",
+ "smw_exportrdf_submit": "ייצו×",
+ "uriresolver": "פותר הכתובות",
+ "properties": "מ×פייני×",
+ "smw_properties_docu": "בוויקי ×”×–×” נעשה שימוש במ××¤×™×™× ×™× ×”×‘××™×.",
+ "smw_property_template": "$1 מסוג $2 ($3 {{PLURAL:$3|×¤×¢× ×חת|פעמי×}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "כל המ××¤×™×™× ×™× ××ž×•×¨×™× ×œ×”×™×•×ª מתו××¨×™× ×¢×œÖ¾×™×“×™ דף!",
+ "smw_propertylackstype": "×œ× ×¦×•×™×Ÿ סוג עבור מ×פיין ×–×” (×‘×™× ×ª×™×™× × × ×™×— ×©×”×•× ×ž×¡×•×’ $1).",
+ "smw_propertyhardlyused": "בקושי נעשה שימוש במ×פיין ×”×–×” בוויקי ×”×–×”!",
+ "smw-property-name-invalid": "×œ× × ×™×ª×Ÿ להשתמש במ×פיין $1 (×©× ×ž×פיין בלתי־תקין)",
+ "smw-property-name-reserved": "\"$1\" × ×ž×¦× ×‘×¨×©×™×ž×ª השמות השמורי×, ולכן ×œ× ×™×›×•×œ לשמש כמ×פיין. ייתכן ש[https://www.semantic-mediawiki.org/wiki/Help:Property_naming דף העזרה ×”×–×”] מכיל מידע לגבי הסיבה שבגינה ×”×©× ×”×–×” שמור.",
+ "smw-sp-property-searchform": "להציג מ××¤×™×™× ×™× ×©×ž×›×™×œ×™×:",
+ "smw-sp-property-searchform-inputinfo": "הקלט תלוי־רישיות וכ×שר ×”×•× ×ž×©×ž×© לסינון, ×ž×•×¦×’×™× ×¨×§ מ××¤×™×™× ×™× ×©×ž×ª××™×ž×™× ×œ×ª× ××™.",
+ "smw-special-property-searchform": "הצגת מ××¤×™×™× ×™× ×©×ž×›×™×œ×™×:",
+ "smw-special-property-searchform-inputinfo": "הקלט תלוי־רישיות וכ×שר ×”×•× ×ž×©×ž×© לסינון, ×ž×•×¦×’×™× ×¨×§ מ××¤×™×™× ×™× ×©×ž×ª××™×ž×™× ×œ×ª× ××™.",
+ "smw-special-property-searchform-options": "×פשרויות",
+ "smw-special-wantedproperties-filter-label": "מסנן:",
+ "smw-special-wantedproperties-filter-none": "כלו×",
+ "smw-special-wantedproperties-filter-unapproved": "×œ× ×ž×ושר",
+ "concepts": "רעיונות",
+ "smw-special-concept-docu": "ניתן להציג[https://www.semantic-mediawiki.org/wiki/Help:Concepts רעיון] בתור \"קטגוריה דינמית\", כלומר בתור ×וסף של ×“×¤×™× ×©××™× × ×ž×™×•×¦×¨×™× ×וטומטית, ××œ× ×ž×—×•×©×‘×™× ×¢×œÖ¾×™×“×™ מדיה־ויקי סמנטית מתי×ור בש×ילתה נתונה.",
+ "smw-special-concept-header": "רשימת רעיונות",
+ "smw-special-concept-count": "{{PLURAL:$1|×¨×©×•× ×”×¨×¢×™×•×Ÿ הב×|×¨×©×•×ž×™× $1 הרעיונות הב××™×}}",
+ "smw-special-concept-empty": "×œ× × ×ž×¦× ×©×•× ×¨×¢×™×•×Ÿ.",
+ "unusedproperties": "מ××¤×™×™× ×™× ×©××™× × ×‘×©×™×ž×•×©",
+ "smw-unusedproperties-docu": "הדף ×”×–×” ×¨×•×©× [https://www.semantic-mediawiki.org/wiki/Unused_properties מ××¤×™×™× ×™× ×©××™× × ×‘×©×™×ž×•×©] ×©×ž×•×›×¨×–×™× ××£ ×©×©×•× ×“×£ ×ינו משתמש בה×. לתצוגה מובדלת, ר' ×ת ×”×“×¤×™× ×”×ž×™×•×—×“×™× ×©×œ [[Special:Properties|כל המ×פייני×]] ×ו של [[Special:WantedProperties|המ××¤×™×™× ×™× ×”×¨×¦×•×™×™×]].",
+ "smw-unusedproperty-template": "$1 מסוג $2",
+ "wantedproperties": "מ××¤×™×™× ×™× ×ž×‘×•×§×©×™×",
+ "smw-wantedproperties-docu": "דף ×–×” מציג [https://www.semantic-mediawiki.org/wiki/Unused_properties מ××¤×™×™× ×™× ×ž×‘×•×§×©×™×] ×©×ž×©×ž×©×™× ×‘×תר הוויקי, ×בל ×ין ×©×•× ×“×£ שמת×ר ×ות×. לתצוגה מובדלת, ר' ×ת ×”×“×¤×™× ×”×ž×™×•×—×“×™× ×©×œ [[Special:Properties|כל המ×פייני×]] ×ו של [[Special:UnusedProperties|המ××¤×™×™× ×™× ×©××™× × ×‘×©×™×ž×•×©]].",
+ "smw-wantedproperty-template": "$1 (בשימוש {{PLURAL:$2|×¤×¢× ×חת|$2 פעמי×}})",
+ "smw_purge": "רענון תבניות וש×ילתות",
+ "smw-purge-failed": "רענון נכשל",
+ "types": "סוגי×",
+ "smw_types_docu": "רשימה של [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes סוגי × ×ª×•× ×™× ×–×ž×™× ×™×] שבה כל [https://www.semantic-mediawiki.org/wiki/Help:Datatype סוג] מייצג ערכה ייחודית של מ××¤×™×™× ×™× ×©×ž×ª××¨×™× ×¢×¨×š ×‘×ž×•× ×—×™× ×©×œ מ×בחני ×חסון ותצוגה ×©×™×•×¨×©×™× ×ž×ž×פיין מוחל.",
+ "smw-special-types-no-such-type": "$1 ×ינו ידוע ×ו ×©×œ× ×”×•×’×“×¨ כנתון תקין",
+ "smw-statistics": "סטטיסטיקה סמנטית",
+ "smw-statistics-property-instance": "{{PLURAL:$1|ערך של מ×פיין|×¢×¨×›×™× ×©×œ מ×פייני×}} (סה\"×›)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|מ×פיין|מ×פייני×}}]] (סה\"×›)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|מ×פיין|מ×פייני×}} (סה\"×›)",
+ "smw-statistics-property-page": "{{PLURAL:$1|מ×פיין (×¨×©×•× ×¢× ×“×£)|מ××¤×™×™× ×™× (×¨×©×•×ž×™× ×¢× ×“×£)}}",
+ "smw-statistics-property-type": "{{PLURAL:$1|מ×פיין (משויך לסוג נתוני×)|מ××¤×™×™× ×™× (×ž×©×•×™×›×™× ×œ×¡×•×’ נתוני×)}}",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|ש×ילתה|ש×ילתות}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|ש×ילתה|ש×ילתות}}]]",
+ "smw-statistics-query-size": "גודל הש×ילתה",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|רעיון|רעיונות}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|רעיון|רעיונות}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|עצ×־משנה|עצמי־משנה}}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|סוג נתוני×|סוגי נתוני×}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|ערך מ×פיין|ערכי מ×פיין}} ([[Special:ProcessingErrorList|{{PLURAL:$1|הערה ×œ× ×ž×ª×ימה|הערות ×œ× ×ž×ª×ימות}}]])",
+ "smw-statistics-delete-count": "{{PLURAL:$1|ישות מיושנת (מסומנת להסרה)|ישויות מיושנות (מסומנות להסרה)}}",
+ "smw_uri_doc": "פותרן כתובות ×ž×™×™×©× ×ת [$1 חיפוש W3C TAG ב־httpRange-14]. ×”×•× ×“×•××’ לכך ש×× ×©×™× ×œ× ×™×”×¤×›×• ל×תרי ×ינטרנט.",
+ "ask": "חיפוש סמנטי",
+ "smw_ask_sortby": "מיון לפי טור (×œ× ×—×•×‘×”)",
+ "smw_ask_ascorder": "בסדר עולה",
+ "smw_ask_descorder": "בסדר יורד",
+ "smw-ask-order-rand": "×קר××™",
+ "smw_ask_submit": "חפש תוצ×ות",
+ "smw_ask_editquery": "עריכת הש×ילתה",
+ "smw_add_sortcondition": "[הוספת הגדרת מיון]",
+ "smw-ask-sort-add-action": "הוסף תנ××™ מיון",
+ "smw_ask_hidequery": "הסתרת הש×ילתה",
+ "smw_ask_help": "עזרה בכתיבת ש×ילתות",
+ "smw_ask_queryhead": "תנ××™",
+ "smw_ask_printhead": "× ×ª×•× ×™× × ×•×¡×¤×™× ×œ×”×¦×’×”",
+ "smw_ask_printdesc": "(יש להוסיף כל ×©× ×ž×פיין בשורה נפרדת)",
+ "smw_ask_format_as": "תסדיר:",
+ "smw_ask_defaultformat": "ברירת מחדל",
+ "smw_ask_otheroptions": "×פשרויות ×חרות",
+ "smw-ask-otheroptions-info": "הפסקה ×”×–×ת מכילה ×פשרויות שמשנות קביעות שהולכות להיות מודפסות. ×פשר לר×ות תי×ורי ×¤×¨×ž×˜×¨×™× ×‘×מצעות מעבר בעכבר.",
+ "smw-ask-otheroptions-collapsed-info": "× × ×œ×”×©×ª×ž×© בסמל הפלוס כדי להציג ×ת כל ×”×פשרויות הזמינות",
+ "smw_ask_show_embed": "הצגת קוד הטמעה",
+ "smw_ask_hide_embed": "הסתרת קוד הטמעה",
+ "smw_ask_embed_instr": "כדי לשבץ ×ת הש×ילתה ×”×–×ת ×ל תוך דף ויקי, השתמשו בקוד להלן.",
+ "smw-ask-delete": "מחיקה",
+ "smw-ask-sorting": "מיון",
+ "smw-ask-options": "×פשרויות",
+ "smw-ask-options-sort": "מיין ×פשרויות",
+ "smw-ask-format-options": "סוג ו×פשרויות",
+ "smw-ask-parameters": "פרמטרי×",
+ "smw-ask-search": "חיפוש",
+ "smw-ask-debug": "נתח",
+ "smw-ask-no-cache": "×ין מטמון",
+ "smw-ask-result": "תוצ××”",
+ "smw-ask-format": "סוג",
+ "smw-ask-format-selection-help": "עזרה ×¢× ×”×¡×•×’×™× ×”× ×‘×—×¨×™×:$1",
+ "smw-ask-input-assistance": "סיוע קלט",
+ "smw-ask-query-search-info": "הש×ילתה <code><nowiki>$1</nowiki></code> נענתה על־ידי {{PLURAL:$3|1=<code>$2</code> (ממטמון)|<code>$2</code> (ממטמון)|<code>$2</code>}} {{PLURAL:$4|בשנייה ×חת|ב־$4 שניות}}.",
+ "searchbyproperty": "חיפוש לפי מ×פיין",
+ "processingerrorlist": "רשימת שגי×ות עיבוד",
+ "propertylabelsimilarity": "דו\"×— דמיון בין תוויות מ×פייני×",
+ "smw-processingerrorlist-intro": "הרשימה הב××” מספקת סקירה כללית של שגי×ות עיבוד שהופיעו בהקשר של [https://www.semantic-mediawiki.org/ מדיה־ויקי סמנטית]. מומלץ לנטר ×ת הרשימה ×”×–×ת ב×ופן קבוע ולתקן הערות ×¢×¨×›×™× ×‘×œ×ª×™Ö¾×ª×§×™× ×•×ª.",
+ "smw_sbv_docu": "חיפוש כל ×”×“×¤×™× ×©×™×© ×œ×”× ×ž×פיין וערך מסוימי×.",
+ "smw_sbv_novalue": "יש לכתוב ערך תקין למ×פיין, ×ו ש×פשר לצפות בכל ערכי המ××¤×™×™× ×™× ×¢×‘×•×¨ \"$1\".",
+ "smw_sbv_displayresult": "רשימת כל ×”×“×¤×™× ×©×™×© ×œ×”× ×ž×פיין \"$1\" ×¢× ×”×¢×¨×š \"$2\"",
+ "smw_sbv_displayresultfuzzy": "רשימת כל ×”×“×¤×™× ×‘×¢×œ×™ המ×פיין \"$1\" ×¢× ×”×¢×¨×š \"$2\".\nמ×חר שיש רק מעט תוצ×ות, יוצגו ×’× ×¢×¨×›×™× ×¡×ž×•×›×™×.",
+ "smw_sbv_property": "מ×פיין:",
+ "smw_sbv_value": "ערך:",
+ "smw_sbv_submit": "חיפוש תוצ×ות",
+ "browse": "עיון בוויקי",
+ "smw_browselink": "עיון במ×פייני×",
+ "smw_browse_article": "כתבו ×ת ×©× ×”×“×£ שהעיון יתחיל ממנו.",
+ "smw_browse_go": "הצגה",
+ "smw_browse_show_incoming": "הצגת מ××¤×™×™× ×™× ×”×ž×§×©×¨×™× ×œ×›×ן",
+ "smw_browse_hide_incoming": "הסתרת מ××¤×™×™× ×™× ×”×ž×§×©×¨×™× ×œ×›×ן",
+ "smw_browse_no_outgoing": "לדף ×–×” ×ין מ×פייני×.",
+ "smw_browse_no_incoming": "×ין מ××¤×™×™× ×™× ×”×ž×§×©×¨×™× ×œ×“×£ ×–×”.",
+ "smw-browse-invalid-subject": "בדיקת תקינות ×”× ×•×©× ×—×–×¨×” ×¢× ×”×©×’×™××” \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "×œ× ×•×©× ×™×© תסדיר הסדרה בלתי־תקין.",
+ "smw-browse-js-disabled": "ישנו חשד ש־JavaScript כבוי ×ו ×ינו זמין ו×נחנו ×ž×ž×œ×™×¦×™× ×¢×œ שימוש בדפדפן שבו ×”×•× × ×ª×ž×š. ×פשרויות ×חרות נידונות בדף ההגדרות [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi $smwgBrowseByApi].",
+ "smw-browse-show-group": "הצג קבוצות",
+ "smw-browse-hide-group": "הצג קבוצות",
+ "smw_inverse_label_default": "$1 מתוך",
+ "smw_inverse_label_property": "תג מ×פיין הפוך.",
+ "pageproperty": "חיפוש במ×פייני דף",
+ "smw_pp_docu": "חיפוש כל ערכי מ×פיין בדף נתון.\nיש לכתוב ×’× ×ת הדף ×•×’× ×ת המ×פיין.",
+ "smw_pp_from": "מהדף:",
+ "smw_pp_type": "מ×פיין",
+ "smw_pp_submit": "חיפוש תוצ×ות",
+ "smw_result_prev": "הקוד×",
+ "smw_result_next": "הב×",
+ "smw_result_results": "תוצ×ות",
+ "smw_result_noresults": "×ין תוצ×ות.",
+ "smwadmin": "תכונות ניהול ותחזוקה",
+ "smw-admin-statistics-job-title": "סטטיסטיקות של משימה",
+ "smw-admin-setupsuccess": "מנוע ×”×חסון הוגדר.",
+ "smw_smwadmin_return": "חזרה ×ל $1",
+ "smw_smwadmin_updatestarted": "תהליך עדכון חדש לרענון ×”× ×ª×•× ×™× ×”×¡×ž× ×˜×™×™× ×”×•×¨×¥.\nכל ×”× ×ª×•× ×™× ×”×ž××•×—×¡× ×™× ×™×™×‘× ×• מחדש ×ו יתוקנו היכן שיש צורך.\nתוכלו לעקוב ×חר תהליך העדכון בדף מיוחד ×–×”.",
+ "smw_smwadmin_updatenotstarted": "יש כבר תהליך עדכון פעיל.\n×œ× ×™×™×•×•×¦×¨ ×חד נוסף.",
+ "smw_smwadmin_updatestopped": "כל תהליכי העדכון ×”×§×™×™×ž×™× × ×¢×¦×¨×•.\n\nלחזור ×ל $1",
+ "smw_smwadmin_updatenotstopped": "כדי לעצור תהליך עדכון פעיל, יש לסמן ×ת תיבת הסימון כדי לציין שזה ב×מת מה שרצית.\n\nחזרה ×ל $1.",
+ "smw-admin-docu": "דף מיוחד ×–×” יעזור ×œ×›× ×‘×ž×”×œ×š התקנה, שדרוג, תחזוקה ושימוש ב<a href=\"https://semantic-mediawiki.org\">מדיה־ויקי הסמנטית</a>. ×”×•× ×’× ×™×©×ž×© למטרות ניהוליות נוספות ויספק סטטיסטיקות.\nזִכרו לגבות ×ת ×”× ×ª×•× ×™× ×”×—×™×•× ×™×™× ×œ×›× ×‘×˜×¨× ×”×¤×¢×œ×ª התכונות הניהוליות.",
+ "smw-admin-db": "תחזוקת בסיס הנתוני×",
+ "smw-admin-dbdocu": "מדיה־ויקי סמנטית דורשת כמה הרחבות לבסיס ×”× ×ª×•× ×™× ×©×œ מדיה־ויקי כדי ל×חסן ×ת ×”× ×ª×•× ×™× ×”×¡×ž× ×˜×™×™×.\nהפונקציה שלהלן מווד×ת שמסד ×”× ×ª×•× ×™× ×©×œ×›× ×ž×•×’×“×¨ כר×וי.\n×”×©×™× ×•×™×™× ×©× ×¢×©×™× ×‘×©×œ×‘ ×–×” ××™× × ×ž×©×¤×™×¢×™× ×¢×œ ש×ר בסיס ×”× ×ª×•× ×™× ×©×œ מדיה־ויקי, וניתן ×œ×‘×˜×œ× ×‘×§×œ×•×ª ×× ×ª×¨×¦×• בכך.\nניתן להפעיל ×ת פונקציית ההגדרה ×”×–×ת מספר ×¤×¢×ž×™× ×ž×‘×œ×™ ×œ×’×¨×•× ×œ× ×–×§ כלשהו, ×ך ×”×™× × ×“×¨×©×ª רק ×¤×¢× ×חת ×¢× ×”×”×ª×§× ×” ×ו ×¢× ×©×“×¨×•×’.",
+ "smw-admin-permissionswarn": "×× ×”×¤×¢×•×œ×” נכשלת ×¢× ×©×’×™×ות SQL, למשתמש של בסיס ×”× ×ª×•× ×™× ×©×œ ×תר הוויקי ×©×œ×›× (תוכלו ×œ×ž×¦×•× ×ותו בקובץ ×”Ö¾LocalSettings.php שלכ×) ×ין הרש×ות מת×ימות.\n×× × ×”×•×¡×™×¤×• למשתמש ×–×” הרש×ות ליצירה ולמחיקה של טבל×ות, כִתבו ×ת פרטי גישת העל לבסיס ×”× ×ª×•× ×™× ×œ×§×•×‘×¥ LocalSetting.php ב×ופן זמני, ×ו השתמשו בסקריפט התחזוקה <code>setupStore.php</code> שיכול להשתמש בהרש×ות של מנהל.",
+ "smw-admin-dbbutton": "×תחול ×ו שדרוג של טבל×ות",
+ "smw-admin-announce": "×¤×¨×¡×•× ×”×•×•×™×§×™ שלך",
+ "smw-admin-announce-text": "×× ×”×•×•×™×§×™ שלך ציבורי, ×פשר ×œ×¨×©×•× ×ותו ב־<a href=\"https://wikiapiary.com\">WikiApiary</a>, ×תר ויקי שעוקב ×חרי ×תרי ויקי.",
+ "smw-admin-deprecation-notice-title-notice": "×©×™× ×•×™×™× × ×›× ×¡×™×",
+ "smw-admin-deprecation-notice-title-removal": "הגדרות מחוקות",
+ "smw_smwadmin_datarefresh": "בנייה מחדש של נתוני×",
+ "smw_smwadmin_datarefreshdocu": "ניתן לשחזר ×ת כל ×”× ×ª×•× ×™× ×©×œ מדיה־ויקי הסמנטי לפי התוכן הנוכחי של ×תר הוויקי.\nפעולה זו עשויה להיות שימושית לתיקון × ×ª×•× ×™× ×©×’×•×™×™× ×ו לעדכון ×”× ×ª×•× ×™× ×× ×”×ž×‘× ×” הפנימי שונה ל×ור שדרוג התוכנה.\nהעדכון מופעל דף־דף ×•×”×•× ×œ× ×™×•×©×œ× ×ž×™×“.\nהמידע להלן מר××” ×”×× ×”×¢×“×›×•×Ÿ מתבצע כעת ומ×פשר ×œ×›× ×œ×”×ª×—×™×œ ×ו להפסיק ×ת ×”×¢×“×›×•× ×™× (××œ× ×× ×›×Ÿ תכונה זו בוטלה על ידי מנהל ×”×תר).",
+ "smw_smwadmin_datarefreshprogress": "<strong>כבר מתבצע תהליך עדכון.</strong>\nמצב בו העדכון ×ž×ª×§×“× ×œ×ט ×”×•× ×ž×¦×‘ נורמלי, מ×חר ×©×”× ×ª×•× ×™× ×ž×ª×¢×“×›× ×™× ×‘×—×œ×§×™× ×§×˜× ×™× ×‘×›×œ ×¤×¢× ×©×ž×©×ª×ž×© ניגש לוויקי.\nכדי ×œ×¡×™×™× ×ת העדכון מהר יותר, תוכלו להפעיל ×ת סקריפט התחזוקה <code>runJobs.php</code> (השתמשו ב×פשרות <code>--maxjobs 1000</code> כדי להגביל ×ת מספר ×”×¢×“×›×•× ×™× ×©×ž×ª×‘×¦×¢×™× ×‘×‘×ª ×חת).\nהתקדמות משוערת של העדכון הנוכחי:",
+ "smw_smwadmin_datarefreshbutton": "קבע בנייה מחדש של נתוני×",
+ "smw_smwadmin_datarefreshstop": "עצירת עדכון זה",
+ "smw_smwadmin_datarefreshstopconfirm": "כן, ×× ×™ {{GENDER:$1|בטוח|בטוחה}}.",
+ "smw-admin-support": "קבלת תמיכה",
+ "smw-admin-supportdocu": "מגוון מש××‘×™× ×¢×•×ž×“×™× ×œ×¨×©×•×ª×›× ×‘×ž×§×¨×” של תקלה:",
+ "smw-admin-installfile": "×× ×™×© ×œ×›× ×‘×¢×™×•×ª בהתקנה, התחילו בבדיקת ההנחיות המופיעות ב<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">קובץ INSTALL</a>.",
+ "smw-admin-smwhomepage": "התיעוד ×”×ž×œ× ×œ×ž×©×ª×ž×©×™ המדיה־ויקי הסמנטי × ×ž×¦× ×‘×›×ª×•×‘×ª <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "ניתן לדווח על ב××’×™× ×‘Ö¾<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "×× ×™×© ×œ×›× ×”×¦×¢×•×ª ×ו ש×לות נוספות, הצטרפו לדיון שנערך ב<a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">×¤×•×¨×•× ×”×ž×©×ª×ž×©×™× ×©×œ Semantic MediaWiki</a>.",
+ "smw-admin-other-functions": "×ופציות נוספות",
+ "smw-admin-supplementary-section-subtitle": "תכונות זמינות",
+ "smw-admin-supplementary-section-intro": "פסקה זו מספקת תכונות נוספות מעבר ×œ×ª×—×•× ×”×ª×—×–×•×§×”, וייתכן שחלק מהתכונות הרשומות ב[https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions תיעוד] מוגבלות ×ו ×œ× ×–×ž×™× ×•×ª, ולכן ×ינן נגישות ב×תר הוויקי ×”×–×”.",
+ "smw-admin-supplementary-settings-title": "הגדרות תצורה",
+ "smw-admin-supplementary-duplookup-title": "ישויות כפולות",
+ "smw-admin-supplementary-elastic-title": "חיפוש גמיש (Elasticsearch)",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> – מידע על סטטיסטיקות של הגדרות ושל מפתוח",
+ "smw-admin-supplementary-elastic-docu": "דף ×–×” מכיל מידע על סטטיסטיקות של הגדרות, של מיפויי×, של ברי×ות ושל מפתוח, הקשורות ל×שכול החיפוש הגמיש (Elasticsearch) שמחובר להרחבה \"מדיה־ויקי סמנטית\" ול־[https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "פונקציות זמינות",
+ "smw-admin-supplementary-elastic-settings-title": "הגדרות",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> – הגדרות שנמצ×ות בשימוש על־ידי החיפוש הגמיש (Elasticsearch) לניהול ×ž×“×“×™× ×©×œ ההרחבה \"מדיה־ויקי סמנטית\"",
+ "smw-admin-supplementary-elastic-mappings-title": "מיפויי×",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> – רשימה של מיפויי ×”×ž×“×“×™× ×•×”×©×“×•×ª",
+ "smw-admin-supplementary-elastic-mappings-summary": "סיכו×",
+ "smw-admin-supplementary-elastic-nodes-title": "צמתי×",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> – הצגת סטטיסטיקות של צמתי×",
+ "smw-admin-supplementary-elastic-indices-title": "מדדי×",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> – סקירה כללית של ×”×ž×“×“×™× ×”×¨×œ×•×•× ×˜×™×™× ×•×©×œ הסטטיסטיקות שלה×",
+ "smw-admin-supplementary-elastic-statistics-title": "סטטיסטיקות",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> – הצגת סטטיסטיקות ברמת מפתוח",
+ "smw-admin-supplementary-elastic-statistics-docu": "דף ×–×” מספק תובנות לגבי ×”× ×ª×•× ×™× ×”×¡×˜×˜×™×¡×˜×™×™× ×©×œ פעולות שונות שמתרחשות ברמת מפתוח. הסטטיסטיקות המוחזרות הן קיבוץ של הבחירה המוקדמת ושל ההצטברות הכוללת. [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html דף העזרה] מכיל תי×ור מפורט של ×”× ×ª×•× ×™× ×”×¡×˜×˜×™×¡×˜×™×™× ×”×–×ž×™× ×™×.",
+ "smw-property-label-similarity-threshold": "סף:",
+ "smw-property-label-similarity-noresult": "×œ× × ×ž×¦×ו תוצ×ות",
+ "smw_adminlinks_datastructure": "מבנה הנתוני×",
+ "smw_adminlinks_displayingdata": "הצגת נתוני×",
+ "smw_adminlinks_inlinequerieshelp": "עזרה על ש×ילתות משובצות",
+ "smw-page-indicator-usage-count": "הערכת [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count מניין שימוש]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|משתמש|מערכת}}",
+ "smw-createproperty-isproperty": "זהו מ×פיין מסוג $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|הערך ×”×פשרי למ×פיין ×–×” הו×|×”×¢×¨×›×™× ×”××¤×©×¨×™×™× ×œ×ž×פיין ×–×” ×”×}}:",
+ "smw-paramdesc-category-delim": "מפריד",
+ "smw-paramdesc-category-template": "תסדיר לעיצוב הפריטי×",
+ "smw-paramdesc-category-userparam": "פרמטר שיועבר לתבנית",
+ "smw-info-par-message": "הודעה להצגה.",
+ "smw-info-par-icon": "××™×–×” סמל להר×ות \"info\" ×ו \"warning\".",
+ "prefs-smw": "מדיה־ויקי סמנטית",
+ "prefs-general-options": "×פשרויות כלליות",
+ "prefs-ask-options": "×פשרויות חיפוש סמנטי",
+ "smw-prefs-intro-text": "×”×פשרויות הב×ות מסופקות על־ידי ההרחבה \"[https://www.semantic-mediawiki.org/ מדיה־ויקי סמנטית]\" (×ו הרחבות קשורות) כדי ל×פשר הת×מה ×ישית של פונקציות נבחרות. לקבלת מידע נוסף, ×פשר לעיין ב[https://www.semantic-mediawiki.org/wiki/Help:User_preferences דף העזרה].",
+ "smw-prefs-ask-options-tooltip-display": "להציג ×ת טקסט הפרמטר בתור רמז צץ ×¢× ×ž×™×“×¢",
+ "smw-prefs-general-options-disable-search-info": "השבתת המידע על תמיכה בתחביר בדף החיפוש הרגיל",
+ "smw-ui-tooltip-title-property": "מ×פיין",
+ "smw-ui-tooltip-title-quantity": "המרת יחידות",
+ "smw-ui-tooltip-title-info": "מידע",
+ "smw-ui-tooltip-title-service": "קישורי שירות",
+ "smw-ui-tooltip-title-warning": "×זהרה",
+ "smw-ui-tooltip-title-error": "שגי××”",
+ "smw-ui-tooltip-title-parameter": "פרמטר",
+ "smw-ui-tooltip-title-event": "×ירוע",
+ "smw-ui-tooltip-title-note": "הערה",
+ "smw-ui-tooltip-title-legend": "מקר×",
+ "smw-ui-tooltip-title-reference": "מקור",
+ "smw_unknowntype": "הסוג של מ×פיין ×–×” ×ינו תקין",
+ "smw-concept-cache-text": "לרעיון ×”×–×” יש {{PLURAL:$1|דף ×חד|$1 דפי×}} ×•×”×•× ×¢×•×“×›×Ÿ ב־$2 ב־$3.",
+ "smw_concept_header": "×“×¤×™× ×ž×”×¨×¢×™×•×Ÿ \"$1\"",
+ "smw_conceptarticlecount": "{{PLURAL:$1|מוצג להלן דף ×חד|×ž×•×¦×’×™× ×œ×”×œ×Ÿ $1 דפי×}}.",
+ "smw-qp-empty-data": "×œ× × ×™×ª×Ÿ להציג ×ת ×”× ×ª×•× ×™× ×”×ž×‘×•×§×©×™× ×‘×’×œ×œ ×מות מידה בלתי־מספיקות לבחירה.",
+ "right-smw-admin": "גישה למשימות ניהול (מדיה־ויקי סמנטית)",
+ "right-smw-patternedit": "עריכת גישה כדי לתחזק ×‘×™×˜×•×™×™× ×¨×’×•×œ×¨×™×™× ×•×ª×‘× ×™×•×ª (מדיה־ויקי סמנטית)",
+ "restriction-level-smw-pageedit": "מוגן (רק ×œ×ž×©×ª×ž×©×™× ×ž×•×¨×©×™×)",
+ "action-smw-patternedit": "לערוך ×‘×™×˜×•×™×™× ×¨×’×•×œ×¨×™×™× ×©×‘×©×™×ž×•×© של מדיה־ויקי סמנטית",
+ "group-smwadministrator": "×ž× ×”×œ×™× (מדיה־ויקי סמנטית)",
+ "group-smwadministrator-member": "{{GENDER:$1|מנהל|מנהלת}} (מדיה־ויקי סמנטית)",
+ "grouppage-smwadministrator": "{{ns:project}}:×ž× ×”×œ×™× (מדיה־ויקי סמנטית)",
+ "group-smwcurator": "××•×¦×¨×™× (מדיה־ויקי סמנטית)",
+ "group-smwcurator-member": "{{GENDER:$1|×וצר|×וצרת}} (מדיה־ויקי סמנטית)",
+ "grouppage-smwcurator": "{{ns:project}}:××•×¦×¨×™× (מדיה־ויקי סמנטית)",
+ "action-smw-admin": "לגשת למשימות ניהול של מדיה־ויקי סמנטית",
+ "smw-property-predefined-default": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש.",
+ "smw-property-predefined-common": "המ×פיין ×”×–×” מותקן מר×ש (ידוע ×’× ×‘×ª×•×¨ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מ×פיין מיוחד]) ×•×ž×‘× ×¢× ×”×¨×©×ות ניהול נוספות, ×בל יכול לשמש כמו כל [https://www.semantic-mediawiki.org/wiki/Property מפ×יין שמוגדר על־ידי משתמש].",
+ "smw-property-predefined-ask": "$1 ×”×•× ×ž×פיין מוגדר מר×ש שמייצג מט×־מידע (בצורת [https://www.semantic-mediawiki.org/wiki/Subobject תת־עצ×]) על ש×ילתות פרטניות ×•×”×•× ×ž×¡×•×¤×§ על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית].",
+ "smw-property-predefined-asksi": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש ש×וסף ×ת מספר התנ××™× ×©×ž×©×ž×©×™× ×‘×©×ילתה ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שנותן מידע על עומק (ערך מספרי שמחושב על בסיס קינון תת־ש×ילתה ושרשר×ות מ××¤×™×™× ×™× ×¤×ª×•×¨×•×ª) של ש×ילתה בזמן שהרצת ש×ילתה מוגבלת ב×מצעות ההגדרה [https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth] ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-sp-properties-docu": "הדף ×”×–×” מציג רשימת [https://www.semantic-mediawiki.org/wiki/Property מ×פייני×] ומניין השימוש ×‘×”× ×›×¤×™ שזמין בוויקי ×”×–×”. לסטטיסטיקת ספירה מעודכנת מומלץ שתסריט התחזוקה ל[https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics סטטיסטיקות מ×פייני×] יורץ ב×ופן קבוע. לתצוגה מבודלת × × ×œ×¨×ות ×ת הדף המיוחד [[Special:UnusedProperties|מ××¤×™×™× ×™× ×©××™× × ×‘×©×™×ž×•×©]] ×ו [[Special:WantedProperties|מ××¤×™×™× ×™× ×ž×‘×•×§×©×™×]].",
+ "smw-sp-properties-cache-info": "×”× ×ª×•× ×™× ×”××©×•×ž×™× ×וחזרו מ[https://www.semantic-mediawiki.org/wiki/Caching מטמון] ועודכנו ל×חרונה $1.",
+ "smw-sp-properties-header-label": "רשימת מ×פייני×",
+ "smw-admin-settings-docu": "רשימה של כל ההגדרות ההתחלתיות והמקומיות שרלוונטיות לסביבת מדיה־ויקי סמנטית. ×œ×¤×¨×˜×™× ×¢×œ הגדרות פרטניות × × ×œ×§×¨×•× ×ת דף העזרה על [https://www.semantic-mediawiki.org/wiki/Help:Configuration הגדרות].",
+ "smw-sp-admin-settings-button": "יצירת רשימת הגדרות",
+ "smw-admin-idlookup-title": "חיפוש",
+ "smw-admin-idlookup-docu": "פסקה זו מציגה ×¤×¨×˜×™× ×˜×›× ×™×™× ×¢×œ ישות פרטנית (דף ויקי, עצ×־משנה, מ×פיין וכו') במדיה־ויקי סמנטית. הקלט יכול להיות מזהה מספרי ×ו מחרוזת המת××™×ž×™× ×œ×©×“×” החיפוש הרלוונטי, ×ך עליו להיות מזהה של הערת ×©×•×œ×™×™× ×›×œ×©×”×™ השייכת למדיה־ויקי סמנטית ×•×œ× ×œ×“×£ ×ו לגרסה של מדיה־ויקי.",
+ "smw-admin-iddispose-title": "זריקה",
+ "smw-admin-iddispose-docu": "יש לציין שפעולת הזריקה ××™× ×” מוגבלת ×•×”×™× ×ª×¡×™×¨ ×ת הישות ממנוע ×”×חסון יחד ×¢× ×›×œ הערות ×”×©×•×œ×™×™× ×©×œ×” בטבל×ות הממתינות, ×× ×ª×ושר. × × ×œ×¢×©×•×ª ×ת הפעולה ×”×–×ת '''בזהירות''' ורק ל×חר בדיקת [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal התיעוד].",
+ "smw-admin-iddispose-done": "המזהה \"$1\" הוסר משרת ×”×חסון.",
+ "smw-admin-iddispose-references": "למזהה \"$1\" {{PLURAL:$2|×ין הערת ×©×•×œ×™×™× ×¤×¢×™×œ×”|יש לפחות הערת ×©×•×œ×™×™× ×¤×¢×™×œ×” ×חת}}:",
+ "smw-admin-iddispose-no-references": "החיפוש ×œ× ×™×›×•×œ ×”×™×” להת××™× ×ת המזהה \"$1\" לישות בטבלה.",
+ "smw-admin-idlookup-input": "חיפוש:",
+ "smw-admin-objectid": "מזהה:",
+ "smw-admin-tab-general": "סקירה כללית",
+ "smw-admin-tab-registry": "רישו×",
+ "smw-livepreview-loading": "בטעינה…",
+ "smw-sp-searchbyproperty-description": "הדף ×”×–×” מספק [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces ממשק עיון] פשוט למצי×ת ישויות שמתו×רות על־ידי מ×פיין וערך בעל־ש×. ממשקי חיפוש ×–×ž×™× ×™× ××—×¨×™× ×”× [[Special:PageProperty|חיפוש מ×פיין דף]] ו[[Special:Ask|בונה ש×ילתות]].",
+ "smw-sp-searchbyproperty-resultlist-header": "רשימת תוצ×ות",
+ "smw-sp-searchbyproperty-nonvaluequery": "רשימה של תוצ×ות שהוקצה להן המ×פיין \"$1\".",
+ "smw-sp-searchbyproperty-valuequery": "רשימת ×“×¤×™× ×©×™×© להן המ×פיין \"$1\" ×¢× ×”×¢×¨×š \"$2\" מוער.",
+ "smw-datavalue-number-textnotallowed": "×œ× × ×™×ª×Ÿ להציב ×ת \"$1\" לסוג מספר מוצהר ×¢× ×”×¢×¨×š $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" הוחזר ×¢× \"NULL\" ש×ינו ערך מספרי מותר.",
+ "smw-editpage-annotation-enabled": "הדף ×”×–×” תומך בהערות סמנטיות בתוך הטקסט (למשל <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) לבניית תכן מובנה וש×יל שמספקת מדיה־ויקי סמנטית. לתי×ור מקיף של ×יך להשתמש בהערות ×ו בפונקציית המפענח #ask, × × ×œ×”×¡×ª×›×œ בדפי העזרה [https://www.semantic-mediawiki.org/wiki/Help:Getting_started getting started]â€, [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation in-text annotation], ×ו [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-editpage-annotation-disabled": "הדף ×”×–×” ×œ× ×ž×•×¤×¢×œ להערות בתוך הטקסט בשל מגבלות מרחב ש×. ×¤×¨×˜×™× ×¢×œ ×יך להפעיל ×ת מרחב ×”×©× ×ž×ª×•××¨×™× ×‘×“×£ העזרה [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration].",
+ "smw-editpage-property-annotation-enabled": "×פשר להחיב ×ת המ×פיין ×”×–×” ב×מצעות הערות סמנטיות כדי להגדיר סוג × ×ª×•× ×™× (למשל <nowiki>\"[[Has type::Page]]\"</nowiki>) ×ו הצהרות תומכות ×חרות (למשל <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). לתי×ור של ×יך להרחיב ×ת הדף ×”×–×”, ר' ×ת דפי העזרה [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaration of a property] ×ו [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes list of available data types].",
+ "smw-editpage-property-annotation-disabled": "×œ× × ×™×ª×Ÿ להרחיב ×ת המ×פיין ×”×–×” ×¢× ×”×¢×¨×ª סוג × ×ª×•× ×™× (למשל <nowiki>\"[[Has type::Page]]\"</nowiki>) ×ž×©×•× ×©×”×•× ×›×‘×¨ מוגדר מר×ש (ר' ×ת הדף העזרה [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special properties] למידע נוסף).",
+ "smw-editpage-concept-annotation-enabled": "×פשר להרחיב ×ת הרעיון ×”×–×” ב×מצעות פונקציית המפענח #concept. לתי×ור של השימוש ב־#concept, ר' ×ת דף העזרה [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept].",
+ "smw-search-syntax": "תחביר",
+ "smw-search-profile": "מורחב",
+ "smw-search-profile-tooltip": "×פשרויות חיפוש שמחוברות ×¢× ×”×”×¨×—×‘×” \"מדיה־ויקי סמנטית\"",
+ "smw-search-profile-sort-best": "ההת×מה הטובה ביותר",
+ "smw-search-profile-sort-recent": "×חרון ביותר",
+ "smw-search-profile-sort-title": "כותרת",
+ "smw-search-profile-extended-help-find-forms": "×ž×¡×ž×›×™× ×–×ž×™× ×™×",
+ "smw-search-profile-extended-section-sort": "מיין על פי",
+ "smw-search-profile-extended-section-form": "טפסי×",
+ "smw-search-profile-extended-section-search-syntax": "קלט לחיפוש",
+ "smw-search-profile-extended-section-namespace": "מרחב ש×",
+ "smw-search-profile-extended-section-query": "ש×ילתה",
+ "smw-search-profile-link-caption-query": "צפייה",
+ "smw-search-show": "הצגה",
+ "smw-search-hide": "להסתיר",
+ "log-name-smw": "יומן מדיה־ויקי סמנטית",
+ "log-show-hide-smw": "$1 יומן מדיה־ויקי סמנטית",
+ "logeventslist-smw-log": "יומן מדיה־ויקי סמנטית",
+ "log-description-smw": "פעולות עבור [https://www.semantic-mediawiki.org/wiki/Help:Logging סוגי ××™×¨×•×¢×™× ×ž×•×¤×¢×œ×™×] שדווחו על־ידי מדיה־ויקי סמנטית ×•×”×ž×¨×›×™×‘×™× ×©×œ×”.",
+ "logentry-smw-maintenance": "××™×¨×•×¢×™× ×©×œ תחזוקה שיוצ××™× ×ž×ª×•×š מדיה־ויקי סמנטית",
+ "smw-datavalue-import-missing-type": "×œ× × ×ž×¦× ×¡×•×’ × ×ª×•× ×™× ×¢×‘×•×¨ \"$1\" ב־[[MediaWiki:Smw import $2|$2 יצו×]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|×™×‘×•× $1]]",
+ "smw-property-predefined-impo": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמת×ר יחס ל[https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary מילון מיוב×] ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמת×ר ×ת [[Special:Types|סוג הנתוני×]] של מ×פיין ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמייצג [https://www.semantic-mediawiki.org/wiki/Help:Container מבנה] מכל שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "המכל מ×פשר ל×גור השמות מ×פיין־ערך בדומה לדף ויקי רגיל, ×בל ×¢× ×ž×¨×—×‘ ישויות נפרד ×¢× ×§×™×©×•×¨ ×œ× ×•×©× ×”×ž×˜×ž×™×¢.",
+ "smw-property-predefined-errp": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש למעקב ×חרי שגי×ות קלט להערות ×¢×¨×›×™× ×—×¨×™×’×•×ª שכנר××” נגרמו על־ידי מגבלות [[Property:Allows value|ערך מותר]] ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value\"$1\"] ×”×•× ×ž×פיין מוגדר מר×ש שיכול להגדיר רשימת ×¢×¨×›×™× ×©×פשר להתיר כדי להגביל ×ת השמת ×”×¢×¨×›×™× ×œ×ž×פיין ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-restricted-use": "ערך ×”× ×ª×•× ×™× \"$1\" סומן לשימוש מוגבל.",
+ "smw-datavalue-invalid-number": "×œ× × ×™×ª×Ÿ לפרש ×ת \"$1\" בתור מספר.",
+ "smw-query-condition-circular": "תנ××™ מעגלי ×פשרי זוהה בתבנית \"$1\".",
+ "smw-types-list": "רשימת סוגי נתוני×",
+ "smw-types-default": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×ž×•×‘× ×”.",
+ "smw-types-help": "מידע נוסף ודוגמ×ות ×פשר ×œ×ž×¦×•× ×‘[https://www.semantic-mediawiki.org/wiki/Help:Type_$1 דף העזרה].",
+ "smw-type-anu": "\"$1\" ×”×•× ×”×’×•×•×Ÿ של סוג ×”× ×ª×•× ×™× [[Special:Types/URL|URL]] ×•×”×•× ×ž×©×ž×© להצהרת ×”×™×¦×•× ''owl:AnnotationProperty''.",
+ "smw-type-boo": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×¤×¨×™×ž×™×˜×™×‘×™ לתי×ור ערך ×מת/שקר.",
+ "smw-type-cod": "\"$1\" ×”×•× ×”×’×•×•×Ÿ של סוג ×”× ×ª×•× ×™× [[Special:Types/Text|Text]] לשימוש ×‘×˜×§×¡×˜×™× ×˜×›× ×™×™× ×‘×ורך כלשהו, כגון קוד מקור.",
+ "smw-type-geo": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×©×ž×ª×ר מקומות ×’××•×’×¨×¤×™×™× ×•×“×•×¨×© ×ת [https://www.semantic-mediawiki.org/wiki/Semantic_Maps Semantic Maps].",
+ "smw-type-tel": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×ž×™×•×—×“ לתי×ור מספר טלפון בין־ל×ומי בהת×× ×œÖ¾RFC 3966.",
+ "smw-type-txt": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×¤×¨×™×ž×™×˜×™×‘×™ לתי×ור מחרוזות ב×ורך כלשהו.",
+ "smw-type-dat": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× ×œ×™×™×¦×•×’ נקודות בזמן בתסדיר מ×וחד.",
+ "smw-type-tab-errors": "שגי×ות",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-property-predefined-errc": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שסופק ×¢\"×™ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] ומייצג שגי×ות שמופיעות בחיבור ×¢× ×¢×¨×š ×œ× ×ž×ª××™× ×ו עיבוד קלט.",
+ "smw-property-predefined-long-errc": "שגי×ות × ×ספות ב[https://www.semantic-mediawiki.org/wiki/Help:Container מכל] שיכול לכלול הפניה למ×פיין ×©×’×¨× ×œ×—×•×¡×¨ העקביות.",
+ "smw-property-predefined-errt": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמכיל תי×ור טקסט של שגי××” ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "×ª×ªÖ¾×¢×¦× ×©×”×•×’×“×¨ על־ידי המשתמש עשה שימוש בשיטת מתן שמות בלתי־תקינה. כתיבת נקודה בחמשת ×”×ותיות הר×שונות ($1) שמורה להרחבות.",
+ "smw-datavalue-record-invalid-property-declaration": "הגדרת הרשומה כוללת ×ת המ×פיין \"$1\" שבעצמה מוגדרת בתור סוג רשומה ו××™× ×” מותרת.",
+ "smw-property-predefined-mdat": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמת×ימה לת×ריך השינוי ×”×חרון של ×”× ×•×©× ×•×ž×•×‘× ×™×ª ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמת×ימה לת×ריך השינוי הר×שון של ×”× ×•×©× ×•×ž×•×‘× ×™×ª ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת המגדירה ×”×× ×”× ×•×©× ×—×“×© ×ו ×œ× ×•×ž×•×‘× ×™×ª ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמכילה ×ת ×©× ×”×“×£ של המשתמש שערך ×ת הגרסה ×”×חרונה ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת סוג ×”-MIME של קובץ שהועלה ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת סוג המדיה של קובץ שהועלה ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת הפורמט של תוצ×ת הש×ילתה ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת התנ××™× ×©×œ הש×ילתה כמחרוזת ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת הזמן (בשניות) שנדרש להשלמת הש×ילתה ומובנית ב[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-prec": "\"$1\" ×”×™× ×ª×›×•× ×” מוגדרת שמציינת ×ת [https://www.semantic-mediawiki.org/wiki/Help:Display_precision דיוק התצוגה] (בספרות דצימליות) ×œ×¢×¨×›×™× ×ž×¡×¤×¨×™×™×.",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Semantic_Maps Semantic Maps] ×œ× × ×ž×¦× ×•×œ×›×Ÿ \"$1\" מוגבל ביכולת פעילותו.",
+ "smw-datavalue-monolingual-dataitem-missing": "חסר פריט דרוש לבניית ערך חד-לשוני.",
+ "smw-datavalue-languagecode-missing": "בשביל הערת \"$1\" המפענח ×œ× ×”×¦×œ×™×— להבין מה קוד השפה (למשל \"foo@en\").",
+ "smw-datavalue-languagecode-invalid": "\"$1\" ×œ× ×–×•×”×” בתור קוד שפה נתמך.",
+ "smw-property-predefined-lcode": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמייצג קוד שפה בתסדיר BCP47 ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "\"$1\" ×”×•× ×¡×•×’ × ×ª×•× ×™× [https://www.semantic-mediawiki.org/wiki/Help:Container מכיל] שמשייך ערך טקסט ×¢× [[Property:Language code|קוד שפה]] מסוי×.",
+ "smw-types-extra-mlt-lcode": "סוג ×”× ×ª×•× ×™× {{PLURAL:$2|דורש|×ינו דורש}} קוד שפה (כלומר, הערת ערך ×œ×œ× ×§×•×“ שפה {{PLURAL:$2|×œ× ×ª×ª×§×‘×œ|תתקבל}}).",
+ "smw-property-predefined-text": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמייצג טקסט מ×ורך שרירותי ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמ×פשר לת×ר מ×פיין בהקשר של שפה ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש כדי להגדיר רשימת מ××¤×™×™× ×™× ×©×ž×©×ž×©×™× ×¢× ×ž×פיין מסוג [[Special:Types/Record|רשומה]] (record) ומסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] זמן פענוח בתוך הטקסט",
+ "smw-limitreport-intext-parsertime-value": "{{PLURAL:$1|שנייה ×חת|$1 שניות}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] זמן עדכון ×חסון (ברענון עמוק)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "{{PLURAL:$1|שנייה ×חת|$1 שניות}}",
+ "smw_allows_pattern": "הדף ×”×–×” ×מור להכיל רשימת הפניות (ש×חריהן [https://en.wikipedia.org/wiki/Regular_expression ×‘×™×˜×•×™×™× ×¨×’×•×œ×¨×™×™×]) ושיהיו ×–×ž×™× ×™× ×‘×מצעות המ×פיין [[Property:Allows pattern|Allows pattern]]. כדי לערוך ×ת הדף ×”×–×”, דרושה ההרש××” <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\" מוין בתור בלתי־תקין ב×מצעות הביטוי הרגולרי \"$2\".",
+ "smw-datavalue-allows-pattern-reference-unknown": "התבנית \"$1\" ×œ× ×”×ª×ימה לעיול ב־[[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-feature-not-supported": "התכונה \"$1\" ××™× ×” נתמכת ×ו ×©×”×™× ×›×•×‘×ª×” בוויקי ×”×–×”.",
+ "smw-property-predefined-pvap": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שיכול להגדיר [[MediaWiki:Smw allows pattern|הפניה לתבנית]] כדי להחיל הת×מת [https://en.wikipedia.org/wiki/Regular_expression ביטוי רגולרי] ×•×”×•× ×ž×¡×•×¤×§ על־די [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-dtitle": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שיכול לשייך כותרת תצוגה ייחודית לישות ×•×”×•× ×ž×¡×•×¤×§ על־די [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvuc": "\"$1\" ×”×•× ×ž×פיין מוגדר מר×ש שמר××” ששיוך ×¢×¨×›×™× ×œ×ž××¤×™×™× ×™× ×מור להיות ייחודי ×•×”×•× ×ž×¡×•×¤×§ על־די [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-pvuc": "ייחודיות ×”×™× ×ž×¦×‘ שבו שני ×¢×¨×›×™× ××™× × ×©×•×•×™× ×‘×™×™×¦×•×’ המפורש שלה וכל הפרה של ×”×ילוץ ×”×–×” תסווג בתור שגי××”.",
+ "smw-datavalue-uniqueness-constraint-error": "המ×פיין \"$1\" מ×פשר רק שיוך ×¢×¨×›×™× ×™×™×—×•×“×™×™× ×•Ö¾\"$2\" כבר הוער ×‘× ×•×©× \"$3\".",
+ "smw-property-predefined-boo": "\"$1\" ×”×•× [[Special:Types/Boolean|סוג]] ומ×פיין מוגדר מר×ש שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית] כדי לייצג ×¢×¨×›×™× ×‘×•×œ×™×× ×™×™×.",
+ "smw-property-predefined-num": "\"$1\" ×”×•× [[Special:Types/Number|סוג]] ומ×פיין מוגדר מר×ש שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית] כדי לייצג ×¢×¨×›×™× ×ž×¡×¤×¨×™×™×.",
+ "smw-property-predefined-dat": "\"$1\" ×”×•× [[Special:Types/Date|סוג]] ומ×פיין מוגדר מר×ש שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית] כדי לייצג ערכי ת×ריכי×.",
+ "smw-property-predefined-uri": "\"$1\" ×”×•× [[Special:Types/URL|סוג]] ומ×פיין מוגדר מר×ש שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית] כדי לייצג ערכי URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" ×”×•× [[Special:Types/Quantity|סוג]] ומ×פיין מוגדר מר×ש שמסופק על־ידי [https://www.semantic-mediawiki.org/wiki/Help:Special_properties מדיה־ויקי סמנטית] כדי לייצג ערכי כמות.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" ××™× ×” כתובת URL תקינה.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-clipboard-copy-link": "העתקת קישור ללוח",
+ "smw-no-data-available": "×ין מידע זמין.",
+ "smw-format-datatable-loadingrecords": "טוען...",
+ "smw-format-datatable-processing": "מעבד...",
+ "smw-format-datatable-search": "חיפוש:",
+ "smw-format-datatable-first": "ר×שון",
+ "smw-format-datatable-last": "×חרון",
+ "smw-format-datatable-next": "הב×",
+ "smw-format-datatable-previous": "הקוד×",
+ "smw-format-datatable-toolbar-export": "ייצו×",
+ "apihelp-smwbrowse-summary": "מודול API לתמיכה בעיון ובחיפוש פעילויות עבור ישויות מתוחזקות של ההרחבה \"מדיה־ויקי סמנטית\".",
+ "smw-help": "עזרה",
+ "smw-processing": "מעבד...",
+ "smw-remote-source-unavailable": "×œ× × ×™×ª×Ÿ להתחבר ליעד החיצוני \"$1\".",
+ "smw-remote-source-disabled": "המקור '''$1''' ביטל ×ת התמיכה בבקשות חיצוניות!",
+ "smw-remote-source-unmatched-id": "המקור '''$1''' ×ינו תו×× ×œ×’×¨×¡×” של ההרחבה \"מדיה־ויקי סמנטית\" שתומכת בבקשות חיצוניות.",
+ "smw-remote-request-note": "התוצ××” מ×וחזרת מהמקור החיצוני '''$1''', והתוכן המיוצר עלול להכיל מידע ש×ינו זמין מתוך ×תר הוויקי הנוכחי.",
+ "smw-remote-request-note-cached": "התוצ××” '''מוטמנת''' מהמקור החיצוני '''$1''', והתוכן המיוצר עלול להכיל מידע ש×ינו זמין מתוך ×תר הוויקי הנוכחי."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hi.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hi.json
new file mode 100644
index 00000000..964517c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hi.json
@@ -0,0 +1,222 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kaustubh",
+ "Wikiuser13",
+ "à°°à°¹à±à°®à°¾à°¨à±à°¦à±à°¦à±€à°¨à±",
+ "Phoenix303",
+ "जनक राज भटà¥à¤Ÿ",
+ "Upendradutt93",
+ "Jayprakash12345",
+ "Rajatkatiyar10",
+ "Sachinkatiyar",
+ "Sfic",
+ "Shypoetess",
+ "चकà¥à¤°à¤ªà¤¾à¤£à¥€",
+ "Bhatakati aatma",
+ "Swapnil.Karambelkar",
+ "Nitin1485"
+ ]
+ },
+ "smw-title": "सिमेंटिक मीडियाविकि",
+ "smw-semantics-not-enabled": "सिमेंटिक मीडियाविकि की विशेषता इस विकि में सकà¥à¤°à¤¿à¤¯ नहीं की गई है।",
+ "smw_viewasrdf": "RDF फ़ीड",
+ "smw_finallistconjunct": ", और",
+ "smw-factbox-facts": "तथà¥à¤¯",
+ "smw-concept-cache-header": "कैश पà¥à¤°à¤¯à¥‹à¤—",
+ "smw-concept-no-cache": "कोई कैश उपलबà¥à¤§ नहीं।",
+ "smw_concept_description": "$1 संकलà¥à¤ªà¤¨à¤¾ का वरà¥à¤£à¤¨",
+ "version-semantic": "सिमेंटिक à¤à¤•à¥à¤¸à¤Ÿà¥‡à¤‚शन",
+ "smw_printername_count": "परिणाम गिनें",
+ "smw_printername_csv": "CSV निरà¥à¤¯à¤¾à¤¤",
+ "smw_printername_dsv": "DSV निरà¥à¤¯à¤¾à¤¤",
+ "smw_printername_debug": "डीबग कà¥à¤µà¥‡à¤°à¥€ (à¤à¤•à¥à¤¸à¤ªà¤°à¥à¤Ÿà¥à¤¸ हेतà¥)",
+ "smw_printername_embedded": "जà¥à¤¡à¤¼à¥‡ हà¥à¤ पेज कंटेंट",
+ "smw_printername_json": "JSON निरà¥à¤¯à¤¾à¤¤",
+ "smw_printername_list": "सूची",
+ "smw_printername_ol": "गिनने की कà¥à¤°à¤¿à¤¯à¤¾",
+ "smw_printername_ul": "आइटमीकरण",
+ "smw_printername_table": "सारणी",
+ "smw_printername_broadtable": "विसà¥à¤¤à¥ƒà¤¤ सारणी",
+ "smw_printername_template": "साà¤à¤šà¤¾",
+ "smw_printername_rdf": "RDF निरà¥à¤¯à¤¾à¤¤",
+ "smw_printername_category": "शà¥à¤°à¥‡à¤£à¥€",
+ "validator-type-class-SMWParamSource": "टेकà¥à¤¸à¥à¤Ÿ",
+ "smw-paramdesc-limit": "परिणाम दिखाने हेतॠअधिकतम संखà¥à¤¯à¤¾",
+ "smw-paramdesc-link": "मान को कड़ी के रूप में दिखाà¤à¤",
+ "smw-paramdesc-sep": "मान के लिठविभाजक",
+ "smw-paramdesc-showsep": "CSV फ़ाइल के शीरà¥à¤· पर विभाजक दिखाà¤à¤‚ (\"sep=<value>\")",
+ "smw-paramdesc-embedonly": "कोई शीरà¥à¤·à¤• नहीं पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करें",
+ "smw-paramdesc-table-class": "टेबल के लिठसेट करने के लिठà¤à¤• अतिरिकà¥à¤¤ सीà¤à¤¸à¤à¤¸ कà¥à¤²à¤¾à¤¸",
+ "smw-paramdesc-rdfsyntax": "RDF सिंटैकà¥à¤¸ का इसà¥à¤¤à¥‡à¤®à¤¾à¤² किया जायेगा",
+ "smw-paramdesc-csv-sep": "उपयोग करने के लिठविभाजक",
+ "smw-paramdesc-dsv-separator": "उपयोग करने के लिठविभाजक",
+ "smw-paramdesc-dsv-filename": "DSV फ़ाइल के लिठनाम",
+ "smw-paramdesc-filename": "आउटपà¥à¤Ÿ फाइल के लिठनाम",
+ "smw-paramdesc-searchlabel": "खोज को जारी रखने के लिठपाठ",
+ "smw-paramdesc-export": "निरà¥à¤¯à¤¾à¤¤ विकलà¥à¤ª",
+ "smw-paramdesc-source": "वैकलà¥à¤ªà¤¿à¤• कà¥à¤µà¥‡à¤°à¥€ सà¥à¤°à¥‹à¤¤",
+ "smw-printername-feed": "आरà¤à¤¸à¤à¤¸ और à¤à¤Ÿà¤® फीड",
+ "smw-paramdesc-feedtype": "फीड के पà¥à¤°à¤•à¤¾à¤°",
+ "smw-label-feed-description": "$1 $2 फीड",
+ "smw_iq_moreresults": "… आगे के रिज़लà¥à¤Ÿ",
+ "smw_wrong_namespace": "केवल \"$1\" नामसà¥à¤¥à¤¾à¤¨ के पनà¥à¤¨à¥‡ की यहाठलिठजाते हैं।",
+ "smw_manytypes": "गà¥à¤£à¥‹ के लिठà¤à¤• से अधिक पà¥à¤°à¤•à¤¾à¤° परिभाषित",
+ "smw_emptystring": "रिकà¥à¤¤ सà¥à¤Ÿà¥à¤°à¤¿à¤‚ग सà¥à¤µà¥€à¤•à¤¾à¤° नहीं किठजाते हैं।",
+ "smw_true_words": "सही,t,हां,y",
+ "smw_false_words": "गलत,f,ना,n",
+ "smw_nofloat": "“$1†यह संखà¥à¤¯à¤¾ नहीं हैं।",
+ "smw_novalues": "कोई मान नहीं दिया गया।",
+ "smw_nodatetime": "दिनांक \"$1\" समà¤à¤¨à¥‡ लायक नहीं है।",
+ "smw_type_header": "“$1†पà¥à¤°à¤•à¤¾à¤°à¤•à¥‡ गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw-propertylist-subproperty-header": "उपगà¥à¤£",
+ "smw-propertylist-redirect-header": "परà¥à¤¯à¤¾à¤¯à¤µà¤¾à¤šà¥€",
+ "exportrdf": "आरडीà¤à¤« को पृषà¥à¤  निरà¥à¤¯à¤¾à¤¤ करें",
+ "smw_exportrdf_submit": "निरà¥à¤¯à¤¾à¤¤",
+ "uriresolver": "यूआरà¤à¤²à¤°à¤¿à¤¸à¥‹à¤²à¥à¤µà¤°",
+ "properties": "गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw-special-property-searchform": "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ गà¥à¤£ जिसमें शामिल हैं:",
+ "smw-special-property-searchform-inputinfo": "निविषà¥à¤Ÿ कारक संवेदनशील है और जब निषà¥à¤ªà¤‚दन के लिठपà¥à¤°à¤¯à¥à¤•à¥à¤¤ होता है, केवल गà¥à¤£ जो सà¥à¤¥à¤¿à¤¤à¤¿ से मेल खाते हैं, पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ होते हैं।",
+ "smw-special-property-searchform-options": "विकलà¥à¤ª",
+ "smw-special-wantedproperties-filter-label": "निषà¥à¤ªà¤‚दन",
+ "smw-special-wantedproperties-filter-none": "कोई भी नहीं",
+ "smw-special-wantedproperties-filter-unapproved": "असà¥à¤µà¥€à¤•à¥ƒà¤¤",
+ "concepts": "अवधारणा",
+ "smw-special-concept-header": "अवधारणाओं की सूची",
+ "smw-special-concept-empty": "कोई अवधारणा नहीं मिली।",
+ "unusedproperties": "इसà¥à¤¤à¥‡à¤®à¤¾à¤² न किये हà¥à¤ गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw-unusedproperty-template": "$2 पà¥à¤°à¤•à¤¾à¤°à¤•à¥‡ $1",
+ "wantedproperties": "चाहिये होनेवाले गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_purge": "ताजा करें",
+ "smw-purge-failed": "ताज़ा करने में विफल रहा",
+ "types": "पà¥à¤°à¤•à¤¾à¤°",
+ "smw-special-types-no-such-type": "निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ डेटा पà¥à¤°à¤•à¤¾à¤° मौजूद नहीं है",
+ "smw-statistics": "सिमेंटिक आà¤à¤•à¤¡à¤¼à¥‡",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|गà¥à¤£}} (कà¥à¤²)",
+ "smw-statistics-property-page": "{{PLURAL:$1|गà¥à¤£}} (पृषà¥à¤  के साथ पंजीकृत)",
+ "smw-statistics-query-size": "कà¥à¤µà¥‡à¤°à¥€ आकार",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|उपवसà¥à¤¤à¥}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|डाटा पà¥à¤°à¤•à¤¾à¤°}}]]",
+ "ask": "सेमैंटिक खोज",
+ "smw_ask_sortby": "सà¥à¤¤à¤‚भ दà¥à¤µà¤¾à¤°à¤¾ कà¥à¤°à¤®à¤¬à¤¦à¥à¤§ करें (वैकलà¥à¤ªà¤¿à¤•)",
+ "smw_ask_ascorder": "बढ़ते कà¥à¤°à¤®",
+ "smw_ask_descorder": "घटते कà¥à¤°à¤®",
+ "smw-ask-order-rand": "कोई भी",
+ "smw_ask_submit": "परिणाम खोजें",
+ "smw_ask_editquery": "कà¥à¤µà¥‡à¤°à¥€ संपादित करें",
+ "smw_add_sortcondition": "[सॉरà¥à¤Ÿà¤¿à¤‚ग शरà¥à¤¤ जोड़ें]",
+ "smw-ask-sort-add-action": "चà¥à¤¨à¥€ गई शरà¥à¤¤ जोङें",
+ "smw_ask_hidequery": "कà¥à¤µà¥‡à¤°à¥€ छà¥à¤ªà¤¾à¤à¤",
+ "smw_ask_help": "कà¥à¤µà¥‡à¤°à¥€ सहायता",
+ "smw_ask_queryhead": "शरà¥à¤¤",
+ "smw_ask_printhead": "पà¥à¤°à¤¿à¤‚टआउट चयन",
+ "smw_ask_printdesc": "(पà¥à¤°à¤¤à¤¿ पंकà¥à¤¤à¤¿ à¤à¤• पà¥à¤°à¥‰à¤ªà¤°à¥à¤Ÿà¥€ नाम जोड़ें)",
+ "smw_ask_format_as": "इस रूप में सà¥à¤µà¤°à¥‚पित करें:",
+ "smw_ask_defaultformat": "पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤•",
+ "smw_ask_otheroptions": "अनà¥à¤¯ विकलà¥à¤ª",
+ "smw_ask_show_embed": "à¤à¤®à¥à¤¬à¥‡à¤¡ कोड दिखाà¤à¤‚",
+ "smw_ask_hide_embed": "à¤à¤®à¥à¤¬à¥‡à¤¡ कोड छà¥à¤ªà¤¾à¤à¤‚",
+ "smw-ask-delete": "हटाà¤à¤",
+ "smw-ask-sorting": "सोरà¥à¤Ÿà¤¿à¤‚ग",
+ "smw-ask-options": "विकलà¥à¤ª",
+ "smw-ask-options-sort": "चà¥à¤¨à¥‡ गठविकलà¥à¤ª",
+ "smw-ask-format-options": "पà¥à¤°à¤¾à¤°à¥‚प और विकलà¥à¤ª",
+ "smw-ask-parameters": "पैमाने",
+ "smw-ask-search": "खोजें",
+ "smw-ask-debug": "दोषमारà¥à¤œà¤¨",
+ "smw-ask-no-cache": "कैश नही",
+ "smw-ask-result": "परिणाम",
+ "smw-ask-empty": "रिकà¥à¤¤",
+ "smw-ask-format": "पà¥à¤°à¤¾à¤°à¥‚प",
+ "smw-ask-format-selection-help": "चयनित पà¥à¤°à¤¾à¤°à¥‚प के साथ सहायता के लिà¤: $1",
+ "smw-ask-format-change-info": "पà¥à¤°à¤¾à¤°à¥‚प संशोधित किया गया था और नठपैमाने और विज़à¥à¤…लाइजेशन विकलà¥à¤ªà¥‹à¤‚ से मिलान करने के लिठफिर से जाà¤à¤š को निषà¥à¤ªà¤¾à¤¦à¤¿à¤¤ करना आवशà¥à¤¯à¤• है।",
+ "searchbyproperty": "गà¥à¤£ अनà¥à¤¸à¤¾à¤° खोजें",
+ "smw_sbv_property": "गà¥à¤£:",
+ "smw_sbv_value": "मान:",
+ "smw_sbv_submit": "परिणाम खोजें",
+ "browse": "विकि देखे",
+ "smw_browselink": "गà¥à¤£ देखें",
+ "smw_browse_go": "आगे बढ़ें",
+ "smw_browse_no_outgoing": "इस पृषà¥à¤  में कोई गà¥à¤£ नहीं है।",
+ "smw_browse_no_incoming": "इस पृषà¥à¤  से कोई गà¥à¤£ जà¥à¤¡à¤¼à¤¾ नहीं है।",
+ "smw-browse-show-group": "समà¥à¤¹ दिखाये",
+ "smw-browse-hide-group": "समà¥à¤¹ छà¥à¤ªà¤¾à¤",
+ "smw-noscript": "इस पà¥à¤°à¤·à¥à¤  या कà¥à¤°à¤¿à¤¯à¤¾ पर काम करने के लिठजावासà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ आवशà¥à¤¯à¤• है, कृपया अपने बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° में जावासà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ सकà¥à¤·à¤® करें या जावासà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ समरà¥à¤¥à¤¿à¤¤ बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° का उपयोग करें ताकि कारà¥à¤¯à¤•à¥à¤·à¤®à¤¤à¤¾ का लाभ दिया जा सके और अनà¥à¤°à¥‹à¤§ पर उपलबà¥à¤§ कराया जाता है। आगे की सहायता के लिà¤, कृपया सहायता पà¥à¤°à¤·à¥à¤  पर नजर डालें [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript]।",
+ "smw_pp_docu": "या तो सभी सौंपे मूलà¥à¤¯à¥‹à¤‚ को पà¥à¤°à¤¾à¤ªà¥à¤¤ करने के लिठà¤à¤• पृषà¥à¤  और संपतà¥à¤¤à¤¿ या केवल à¤à¤• संपतà¥à¤¤à¤¿ दरà¥à¤œ करें।",
+ "smw_pp_from": "पृषà¥à¤  से:",
+ "smw_pp_type": "गà¥à¤£à¤§à¤°à¥à¤®:",
+ "smw_pp_submit": "परिणाम खोजें",
+ "smw_result_prev": "पिछला",
+ "smw_result_next": "अगला",
+ "smw_result_results": "परिणाम",
+ "smw_result_noresults": "कोई परिणाम नहीं",
+ "smw-admin-statistics-job-title": "कारà¥à¤¯ आà¤à¤•à¤¡à¤¼à¥‡",
+ "smw_smwadmin_return": "$1 पर वापस जायें।",
+ "smw-admin-deprecation-notice-title-notice": "आगामी बदलाव",
+ "smw-admin-deprecation-notice-title-removal": "हटाई गई सेटिंग",
+ "smw_smwadmin_datarefreshbutton": "अनà¥à¤¸à¥‚ची डेटा पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤®à¤¾à¤£",
+ "smw_smwadmin_datarefreshstop": "इस अदà¥à¤¯à¤¤à¤¨ को रोकें",
+ "smw-admin-job-scheduler-note": "निषà¥à¤ªà¤¾à¤¦à¤¨ के दौरान डेडलॉक परिसà¥à¤¥à¤¿à¤¤à¤¿à¤¯à¥‹à¤‚ से बचने के लिठइस खंड में अधिकांश गतिविधियां नौकरी के रूप में होती हैं। पà¥à¤°à¤¸à¤‚सà¥à¤•à¤°à¤£ के लिठ[https://www.mediawiki.org/wiki/Manual:Job_queue कारà¥à¤¯ अनà¥à¤¸à¥‚चक] जिमà¥à¤®à¥‡à¤¦à¤¾à¤° है और यह महतà¥à¤µà¤ªà¥‚रà¥à¤£ है कि रखरखाव <code>runJobs.php</code> सà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ (यह भी देखें <code>$wgRunJobsAynync</code>) में à¤à¤• उपयà¥à¤•à¥à¤¤ कà¥à¤·à¤®à¤¤à¤¾ है।",
+ "smw-admin-outdateddisposal-active": "à¤à¤• पà¥à¤°à¤¾à¤¨à¥€ संसà¥à¤¥à¤¾à¤“ं के निपटान की नौकरी निरà¥à¤§à¤¾à¤°à¤¿à¤¤ की गई है।",
+ "smw-admin-propertystatistics-active": "à¤à¤• संपतà¥à¤¤à¤¿ के आंकड़ों का पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤®à¤¾à¤£ कारà¥à¤¯ निरà¥à¤§à¤¾à¤°à¤¿à¤¤ किया गया है।",
+ "smw-admin-fulltext-active": "à¤à¤• पूरà¥à¤£-पाठ खोज पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤®à¤¾à¤£ कारà¥à¤¯ निरà¥à¤§à¤¾à¤°à¤¿à¤¤ किया गया है।",
+ "smw-admin-supplementary-duplookup-title": "पà¥à¤°à¤¤à¤¿à¤°à¥‚प पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚",
+ "smw-admin-supplementary-duplookup-docu": "यह पृषà¥à¤  [https://www.semantic-mediawiki.org/wiki/Help:Entity_table इकाई तालिका] से पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¥‹à¤‚ को सूचीबदà¥à¤§ करता है जिनà¥à¤¹à¥‡à¤‚ डà¥à¤ªà¥à¤²à¤¿à¤•à¥‡à¤Ÿ के रूप में वरà¥à¤—ीकृत किया गया है। पà¥à¤°à¤¤à¤¿à¤°à¥‚प पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚ (यदि सभी में हों) केवल à¤à¤• दà¥à¤°à¥à¤²à¤­ अवसरों पर होती हैं जो संभावित रूप से à¤à¤• डेटाबेस अदà¥à¤¯à¤¤à¤¨ या à¤à¤• असफल रोलबैक लेनदेन के दौरान समापà¥à¤¤ पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ के कारण होती हैं।",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:पà¥à¤°à¤¤à¤¿à¤°à¥‚प_पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚",
+ "smw-list-count": "सूची में $1 {{PLURAL:$1|पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿|पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚}} शामिल हैं|",
+ "smw-list-count-from-cache": "सूची में $1 {{PLURAL:$1|पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿|पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¤¾à¤‚}} शामिल हैं और दà¥à¤°à¥à¤¤à¤¿à¤•à¤¾ (यूटीसी: $2) से पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤ की गई थी।",
+ "smw_adminlinks_displayingdata": "डाटा डिसà¥à¤ªà¥à¤²à¥‡",
+ "smw-createproperty-isproperty": "यह $1 पà¥à¤°à¤•à¤¾à¤° का गà¥à¤£à¤§à¤°à¥à¤® हैं।",
+ "smw-info-par-message": "दिखाने हेतॠसंदेश",
+ "prefs-smw": "सिमेंटिक मीडियाविकि",
+ "prefs-ask-options": "सिमेंटिक खोज विकलà¥à¤ª",
+ "smw-ui-tooltip-title-property": "गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw-ui-tooltip-title-quantity": "इकाई परिवरà¥à¤¤à¤¨",
+ "smw-ui-tooltip-title-info": "जानकारी",
+ "smw-ui-tooltip-title-service": "सेवा कड़ी",
+ "smw-ui-tooltip-title-warning": "तà¥à¤°à¥à¤Ÿà¤¿",
+ "smw-ui-tooltip-title-parameter": "पà¥à¤°à¤¾à¤šà¤²",
+ "smw-ui-tooltip-title-event": "आयोजन",
+ "smw-ui-tooltip-title-note": "नोट",
+ "smw-ui-tooltip-title-legend": "कà¥à¤‚जी",
+ "smw-ui-tooltip-title-reference": "सनà¥à¤¦à¤°à¥à¤­",
+ "smw_unknowntype": "इस गà¥à¤£ का पà¥à¤°à¤•à¤¾à¤° अमानà¥à¤¯ है।",
+ "smw-sp-properties-header-label": "गà¥à¤£à¥‹à¤‚ की सूची",
+ "smw-admin-idlookup-title": "वसà¥à¤¤à¥ आईडी देखें",
+ "smw-admin-objectid": "वसà¥à¤¤à¥ आईडी:",
+ "smw-admin-tab-rebuild": "अपगà¥à¤°à¥‡à¤¡ और डेटा पà¥à¤¨à¤°à¥à¤¨à¤¿à¤°à¥à¤®à¤¾à¤£",
+ "smw-livepreview-loading": "लोड हो रहा है...",
+ "smw-sp-searchbyproperty-resultlist-header": "परिणामों की सूची",
+ "smw-search-input-assistance": "उपलबà¥à¤§ संपतà¥à¤¤à¤¿à¤¯à¥‹à¤‚ और शà¥à¤°à¥‡à¤£à¤¿à¤¯à¥‹à¤‚ के पूरà¥à¤µ चयन को सरल करने के लिठ[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance input assistant] भी सकà¥à¤·à¤® है।",
+ "log-name-smw": "सिमेंटिक मीडियाविकि लॉग",
+ "log-show-hide-smw": "$1 सिमेंटिक मीडियाविकि लॉग",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1 आयात]]",
+ "smw-datavalue-property-create-restriction": "संपतà¥à¤¤à¤¿ \"$1\" मौजूद नहीं है और उपयोगकरà¥à¤¤à¤¾ को \"$2\" (देखें [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode]) बनाने की या असमरà¥à¤¥à¤¿à¤¤ संपतà¥à¤¤à¤¿ के साथ कथित टिपà¥à¤ªà¤£à¥€ की अनà¥à¤®à¤¤à¤¿ नहीं है।",
+ "smw-types-list": "डाटा पà¥à¤°à¤•à¤¾à¤°à¥‹à¤‚ की सूची",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "यह पृषà¥à¤  à¤à¤• संपतà¥à¤¤à¤¿ के सभी मूलà¥à¤¯à¥‹à¤‚ और à¤à¤• दिठगठपृषà¥à¤  को खोजने के लिठà¤à¤• बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग इंटरफ़ेस पà¥à¤°à¤¦à¤¾à¤¨ करता है। अनà¥à¤¯ उपलबà¥à¤§ खोज इंटरफेस में [[Special:SearchByProperty|property search]], और [[Special:Ask|ask query builder]] शामिल है।",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|सेकंड}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|सेकंड}}",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" अमानà¥à¤¯ पता है।",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-data-lookup-with-wait": "अनà¥à¤°à¥‹à¤§ को संसाधित किया जा रहा है और कà¥à¤› समय लग सकता है।",
+ "smw-no-data-available": "कोई डाटा उपलबà¥à¤§ नहीं है।",
+ "smw-edit-protection": "यह पà¥à¤°à¤·à¥à¤  [[Property:Is edit protected|protected]] आकसà¥à¤®à¤¿à¤• डेटा संशोधन को रोकने के लिठहै और उपयà¥à¤•à¥à¤¤ संपादन अधिकार वाले उपयोगकरà¥à¤¤à¤¾à¤“ं दà¥à¤µà¤¾à¤°à¤¾ ही संपादित किया जा सकता है (\"$1\") या [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group]।",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-loadingrecords": "लोड हो रहा है...",
+ "smw-format-datatable-processing": "पà¥à¤°à¥‹à¤¸à¥‡à¤¸ हो रहा...",
+ "smw-format-datatable-search": "खोज:",
+ "smw-format-datatable-first": "पहला",
+ "smw-format-datatable-last": "आखिरी",
+ "smw-format-datatable-next": "अगला",
+ "smw-format-datatable-previous": "पिछला",
+ "smw-section-expand": "अंश का विसà¥à¤¤à¤¾à¤° करें",
+ "smw-section-collapse": "अंश को संकà¥à¤·à¤¿à¤ªà¥à¤¤ करें",
+ "smw-types-title": "पà¥à¤°à¤•à¤¾à¤°: $1",
+ "smw-rule-page-definition": "परिभाषा",
+ "smw-rule-page-description": "विवरण:",
+ "smw-rule-page-type": "पà¥à¤°à¤•à¤¾à¤°:",
+ "smw-rule-page-tag": "टैग:"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hif-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hif-latn.json
new file mode 100644
index 00000000..70d4fc06
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hif-latn.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "load karaa jaae hae..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hil.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hil.json
new file mode 100644
index 00000000..53a82353
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hil.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77"
+ ]
+ },
+ "smw_browse_go": "Lakat"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hr.json
new file mode 100644
index 00000000..ac3a7787
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hr.json
@@ -0,0 +1,190 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dalibor Bosits",
+ "Sociologist",
+ "Tivek",
+ "ì•„ë¼",
+ "SpeedyGonsales",
+ "Bugoslav",
+ "Kghbln",
+ "Ex13"
+ ]
+ },
+ "smw-desc": "Čini Vaš wiki dostupnijim - za strojeve ''i'' ljude ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentacija])",
+ "smw_viewasrdf": "RDF feed",
+ "smw_finallistconjunct": ", i",
+ "smw_factbox_head": "ÄŒinjenice o $1",
+ "smw_isspecprop": "Ovo svojstvo je specijalno na ovom wikiju.",
+ "smw_concept_description": "Opis koncepta \"$1\"",
+ "smw_no_concept_namespace": "Koncepti mogu biti definirani jedino u imenskom prostoru Concept: .",
+ "smw_multiple_concepts": "Stranica koncepta smije imati samo jednu definiciju koncepta.",
+ "smw_concept_cache_miss": "Koncept \"$1\" se trenutaÄno ne može se koristiti jer je wiki podeÅ¡en tako da se koncept mora proraÄunati i izraditi off-line. Ako problem ne nestane nakon nekog vremena, zamolite administratora da ovaj koncept uÄini dostupnim.",
+ "smw_noinvannot": "Vrijednosti se ne mogu dodijeliti inverznim svojstva.",
+ "version-semantic": "SemantiÄke ekstenzije",
+ "smw_baduri": "URI oblika \"$1\" nije dozvoljen.",
+ "smw_printername_count": "Broji rezultate",
+ "smw_printername_csv": "Izvoz u CSV",
+ "smw_printername_debug": "Debugiranje upita (za struÄnjake)",
+ "smw_printername_embedded": "Umetni sadržaj stranice",
+ "smw_printername_json": "Izvoz u JSON",
+ "smw_printername_list": "Popis",
+ "smw_printername_ol": "Nabrajanje",
+ "smw_printername_ul": "Ispis po predmetima",
+ "smw_printername_table": "Tablica",
+ "smw_printername_broadtable": "Å iroka tablica",
+ "smw_printername_template": "Predložak",
+ "smw-paramdesc-limit": "Maksimalni broj rezultata",
+ "smw-paramdesc-headers": "Prikaz zaglavlja/imena svojstava",
+ "smw-paramdesc-mainlabel": "Oznaka imenu glavne stranice",
+ "smw-paramdesc-link": "Prikaži vrijednosti kao poveznice",
+ "smw-paramdesc-intro": "Tekst za prikazati prije rezultata upita, ukoliko ih ima",
+ "smw-paramdesc-outro": "Tekst za prikazati nakon rezultata upita, ukoliko ih ima",
+ "smw-paramdesc-default": "Tekst koji će se prikazati ako nema rezultata upita",
+ "smw-paramdesc-sep": "Znak za razdvajanje vrijednosti",
+ "smw-paramdesc-template": "Naziv predloška kojim će se prikazati ispis",
+ "smw-paramdesc-columns": "Broj stupaca u kojima se prikazuje rezultat (default je $1)",
+ "smw-paramdesc-userparam": "Vrijednost koja se prenosi u svaki poziv predloška, ako se predložak koristi",
+ "smw-paramdesc-introtemplate": "Ime predloška koji će se prikazati prije rezultata upita, ako ih ima",
+ "smw-paramdesc-outrotemplate": "Ime predloška koji će se prikazati nakon rezultata upita, ako ih ima",
+ "smw-paramdesc-embedformat": "HTML tag koji definira zaglavlja",
+ "smw-paramdesc-embedonly": "Ne prikazuj zaglavlja",
+ "smw-paramdesc-searchlabel": "Tekst poveznice na rezultate",
+ "smw_iq_disabled": "SemantiÄki upiti su iskljuÄeni na ovom wikiju.",
+ "smw_iq_moreresults": "… daljnji rezultati",
+ "smw_parseerror": "Dana vrijednost nije razumljiva.",
+ "smw_notitle": "\"$1\" ne može biti ime stranice na ovom wikiju.",
+ "smw_wrong_namespace": "U ovom su sluÄaju dopuÅ¡tene samo stranice iz imenskoga prostora \"$1\".",
+ "smw_manytypes": "Za svojstvo je definirano više od jednog tipa.",
+ "smw_emptystring": "Prazni nizovi se ne prihvaćaju.",
+ "smw_notinenum": "\"$1\" nije na popisu dozvoljenih vrijednosti ($2) za ovo svojstvo.",
+ "smw_noboolean": "\"$1\" nije prepoznat kao booleovska vrijednost (\"true/false\" odn. istina/laž).",
+ "smw_true_words": "true,t,yes,y,istina,i,da,d",
+ "smw_false_words": "false,f,no,n,laž,l,ne",
+ "smw_nofloat": "\"$1\" nije broj.",
+ "smw_infinite": "Veliki brojevi poput \"$1\" nisu podržani.",
+ "smw_novalues": "Nisu naznaÄene vrijednosti.",
+ "smw_nodatetime": "Datum \"$1\" nije razumljiv.",
+ "smw_toomanyclosing": "Čini se da postoji previše pojava \"$1\" u upitu.",
+ "smw_noclosingbrackets": "Jedna od \"<nowiki>[[</nowiki>\" u Vašem upitu nije zatvorena odgovarajućom \"]]\".",
+ "smw_misplacedsymbol": "Simbol \"$1\" se javlja na mjestu gdje nije koristan.",
+ "smw_unexpectedpart": "Dio upita \"$1\" nije razumljiv.\nRezultati možda neće biti odgovarajući.",
+ "smw_emptysubquery": "Neki podupiti nemaju valjani uvjet.",
+ "smw_misplacedsubquery": "Podupit je korišten na nedozvoljenom mjestu.",
+ "smw_valuesubquery": "Podupiti ne mogu biti vrijednosti svojstva \"$1\".",
+ "smw_badqueryatom": "Jedan od dijelova upita \"<nowiki>[[…]]</nowiki>\" nije razumljiv.",
+ "smw_propvalueproblem": "Vrijednost svojstva \"$1\" nije razumljiva.",
+ "smw_noqueryfeature": "Ovaj wiki ne podržava neku od osobina upita, pa je dio upita odbaÄen ($1).",
+ "smw_noconjunctions": "Ovaj wiki ne podržava veznike u upitima, pa je dio upita odbaÄen ($1).",
+ "smw_nodisjunctions": "Ovaj wiki ne podržava razdvajanja u upitima, pa je dio upita odbaÄen ($1).",
+ "smw_querytoolarge": "Zbog ograniÄenja veliÄine ili dubine upita na ovom wikiju, sljedeći uvjeti upita nisu uzeti u obzir: $1.",
+ "smw_notemplategiven": "Navedite vrijednost parametra \"template\" (\"predložak\") za ovaj format upita.",
+ "smw_type_header": "Svojstva tipa \"$1\".",
+ "smw_typearticlecount": "{{PLURAL:$1|Prikazano je $1 svojstvo koje koristi|Prikazana su $1 svojstva koja koriste|Prikazano je $1 svojstava koja koriste}} ovaj tip.",
+ "smw_attribute_header": "Stranice koje koriste svojstvo \"$1\".",
+ "smw_attributearticlecount": "Prikazano {{PLURAL:$1|$1 stranica koja koristi|$1 stranice koje koriste|$1 stranica koje koriste}} ovo svojstvo.",
+ "smw_subproperty_header": "Podsvojstva",
+ "smw-subpropertylist-count": "Ovo svojstvo ima {{PLURAL:$1|sljedeće podsvojstvo|sljedeća $1 podsvojstva|sljedećih $1 podsvojstava}}:",
+ "exportrdf": "Izvezi stranice u RDF",
+ "smw_exportrdf_docu": "Na ovoj stranici možete dobiti podatke o nekoj stranici u RDF formatu.\nDa biste izvezli stranice unesite njihove naslove u prozor za uređivanje ispod, jedan naslov po retku.",
+ "smw_exportrdf_recursive": "Rekurzivno izvezi sve srodne stranice.\nImajte na umu da bi rezultat mogao biti velik!",
+ "smw_exportrdf_backlinks": "Također izvezi sve stranice koje se referenciraju na izvezene stranice.\nGenerira RDF za pregled i pretraživanje.",
+ "smw_exportrdf_lastdate": "Ne izvozi stranice koje nisu izmijenjene od navedenog datuma.",
+ "smw_exportrdf_submit": "Izvezi",
+ "uriresolver": "URI razrješitelj",
+ "properties": "Svojstva",
+ "smw_properties_docu": "U wikiju se koriste sljedeća svojstva.",
+ "smw_property_template": "$1 tipa $2 ($3)",
+ "smw_propertylackspage": "Svako svojstvo mora biti opisano na svojoj stranici!",
+ "smw_propertylackstype": "Za ovo svojstvo nije naveden tip (od sad na dalje pretpostavljam tip $1).",
+ "smw_propertyhardlyused": "Ovo se svojstvo gotovo ne koristi u wikiji!",
+ "unusedproperties": "Nekorištena svojstva",
+ "smw_unusedproperties_docu": "Sljedeća svojstva postoje, no ne koristi ih ni jedna stranica.",
+ "smw_unusedproperty_template": "$1 tipa $2",
+ "wantedproperties": "Tražena svojstva",
+ "smw_wantedproperties_docu": "Sljedeća svojstva se koriste u wikiju, no još nemaju svoje stranice koje bi ih opisale.",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|korištenje|korištenja|korištenja}})",
+ "smw_purge": "Osvježi",
+ "types": "Tipovi",
+ "smw_types_docu": "Slijedi popis svih tipova podataka koji mogu biti dodijeljeni svojstvima.\nSvaki tip podatka ima stranicu koja može pružiti dodatne informacije.",
+
+
+ "smw_uri_doc": "URI razrješitelj implementira [$1 W3C traženje TAGova na httpRange-14]\nkoje brine da se ljudi ne pretvore u web stranice.",
+ "ask": "SemantiÄka tražilica",
+ "smw_ask_sortby": "Razvrstavanje po stupcu (opcionalno)",
+ "smw_ask_ascorder": "Rastuće",
+ "smw_ask_descorder": "padajuće",
+ "smw_ask_submit": "Nađi rezultate",
+ "smw_ask_editquery": "Uredi upit",
+ "smw_add_sortcondition": "[Dodaj uvjet na razvrstavanje]",
+ "smw_ask_hidequery": "Sakrij upit",
+ "smw_ask_help": "Pomoć za upite",
+ "smw_ask_queryhead": "Upit",
+ "smw_ask_printhead": "Dodatni podaci za prikazati",
+ "smw_ask_printdesc": "(dodaj jedno svojstvo po retku)",
+ "smw_ask_format_as": "Formatiraj kao:",
+ "smw_ask_defaultformat": "prvotno",
+ "smw_ask_otheroptions": "Ostale opcije",
+ "smw_ask_show_embed": "Prikaži kôd za umetanje",
+ "smw_ask_hide_embed": "Sakrij kôd za umetanje",
+ "smw_ask_embed_instr": "Da biste umetnuli ovaj upit u stranicu koristite kôd dolje.",
+ "searchbyproperty": "Traži po svojstvu",
+ "smw_sbv_docu": "Traži sve stranice koje imaju zadano svojstvo i vrijednost.",
+ "smw_sbv_novalue": "Unesite valjanu vrijednost za svojstvo ili pogledajte sve vrijednosti za \"$1\".",
+ "smw_sbv_displayresult": "Popis svih stranica koje imaju svojstvo \"$1\" vrijednosti \"$2\".",
+ "smw_sbv_displayresultfuzzy": "Popis svih stranica koje imaju svojstvo \"$1\" vrijednosti \"$2\".\nBudući da je rezultata malo, također su prikazane i bliske vrijednosti.",
+ "smw_sbv_property": "Svojstvo:",
+ "smw_sbv_value": "Vrijednost:",
+ "smw_sbv_submit": "Nađi rezultate",
+ "browse": "Pregledaj wiki",
+ "smw_browselink": "Pregledaj svojstva",
+ "smw_browse_article": "Unesite ime stranice od koje ćete poÄeti pregledavanje.",
+ "smw_browse_go": "Kreni",
+ "smw_browse_show_incoming": "prikaži svojstva koja povezuju ovamo",
+ "smw_browse_hide_incoming": "sakrij svojstva koja povezuju ovamo",
+ "smw_browse_no_outgoing": "Ova stranica nema svojstva.",
+ "smw_browse_no_incoming": "Nijedno svojstvo ne povezuje na ovu stranicu.",
+ "smw_inverse_label_default": "$1 od",
+ "smw_inverse_label_property": "Oznaka inverznog svojstva",
+ "pageproperty": "Pretraživanje svojstava stranica",
+ "smw_pp_docu": "Pretraži sve vrijednosti nekog svojstva na zadanoj stranici.\nUnesite stranicu i svojstvo.",
+ "smw_pp_from": "Sa stranice",
+ "smw_pp_type": "Svojstvo",
+ "smw_pp_submit": "Nađi rezultate",
+ "smw_result_prev": "Prethodni",
+ "smw_result_next": "Sljedeći",
+ "smw_result_results": "Rezultati",
+ "smw_result_noresults": "Nema rezultata.",
+ "smwadmin": "Administratorske funkcije za Semantic MediaWiki",
+ "smw_smwadmin_setupsuccess": "Spremište je uspješno postavljeno.",
+ "smw_smwadmin_return": "Vrati se na $1",
+ "smw_smwadmin_updatestarted": "Pokrenut je novi proces osvježavanja semantiÄkih podataka.\nSvi spremljeni podaci će biti ponovno izgraÄ‘eni ili popravljeni ako je to potrebno.\nNapredak osvježavanja možete pratiti na ovoj posebnoj stranici.",
+ "smw_smwadmin_updatenotstarted": "Već se izvršava proces osvježavanja.\nNe pokrećem novi.",
+ "smw_smwadmin_updatestopped": "Svi postojeći procesi osvježavanja su zaustavljeni.",
+ "smw_smwadmin_updatenotstopped": "Da biste zaustavili proces osvježavanja, oznaÄite u potvrdnom okviru da ste zaista sigurni.",
+ "smw_smwadmin_docu": "Ova posebna stranica pomaže prilikom instalacije i nadogradnje <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>.\nNe zaboravite napraviti sigurnosnu kopiju važnih podataka prije izvršavanja administrativnih funkcija.",
+ "smw_smwadmin_db": "Instalacija i nadgradnja baze podataka",
+ "smw_smwadmin_dbdocu": "Semantic MediaWiki zahtijeva odreÄ‘ena proÅ¡irenja MediaWiki baze podataka za Äuvanje semantiÄkih podataka.\nFunkcija ispod osigurava da je vaÅ¡a baza pravilno postavljena.\nOvaj korak ne utjeÄe na ostatak MediaWiki baze podataka i lako se može poniÅ¡titi ukoliko to želite.\nOva se funkcija može izvrÅ¡iti viÅ¡e puta bez ikakvih Å¡tetnih posljedica, no potrebna je samo kod instalacije ili nadgradnje.",
+ "smw_smwadmin_permissionswarn": "Ako operacija ne uspije i vrati SQL greÅ¡ku, baza podataka koju koristi VaÅ¡ wiki (provjerite svoj LocalSettings.php) vjerojatno nema dovoljna prava.\nUÄinite jedno od sljedećeg: dodijelite ovom korisniku dodatna prava za stvaranje i brisanje tablica, privremeno unesite administratorsko korisniÄko ime i lozinku VaÅ¡e baze podataka u LocalSettings.php, ili pokrenite skriptu za održavanje <code>setupStore.php</code> koja će koristiti prava iz AdminSettings.php.",
+ "smw_smwadmin_dbbutton": "Inicijaliziraj ili nadogradi tablice",
+ "smw_smwadmin_announce": "Najavite svoj wiki",
+ "smw_smwadmin_datarefresh": "Popravljanje i nadgradnja podataka",
+ "smw_smwadmin_datarefreshdocu": "Moguće je povratiti sve semantiÄke podatke Semantic MediaWiki na osnovi trenutnog tekstualnog wikija, Å¡to možete koristiti za popravak pokvarenih podataka ili osvježivanje podataka u sluÄaju internih promjena softvera.\nOsvježavanje se izvrÅ¡ava stranicu po stranicu i neće biti gotovo odmah.\nPrikaz dolje sadrži popis svih osvježavanja u tijeku i omogućuje Vam da zaustavite ili pokrenete osvježavanje (osim ako to nije onemogućio administrator).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Osvježavanje je već u tijeku.</strong>\nNormalno je da osvježavanje napreduje sporo jer se podaci osvježavaju u malim obrocima svaki put kad korisnik pristupi wikiju.\nDa bi ovo osvježavanje zavrÅ¡ilo Äim prije, možete pokrenuti MediaWiki skriptu za održavanje <code>runJobs.php</code> (koristite opciju <code>--maxjobs 1000</code> kako biste ograniÄili broj zasebnih osvježavanja po jednoj seriji).\nProcjena napretka osvježavanja:",
+ "smw_smwadmin_datarefreshbutton": "Pokreni osvježavanje podataka",
+ "smw_smwadmin_datarefreshstop": "Zaustavi ovo osvježavanje",
+ "smw_smwadmin_datarefreshstopconfirm": "Da, {{GENDER:$1|siguran|sigurna}} sam.",
+ "smw_smwadmin_support": "Zatražite pomoć",
+ "smw_smwadmin_supportdocu": "U sluÄaju problema poslužite se sljedećim izvorima:",
+ "smw_smwadmin_installfile": "U sluÄaju problema s VaÅ¡om instalacijom pogledajte naputke u datoteci <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL</a>.",
+ "smw_smwadmin_smwhomepage": "Potpuna korisniÄka dokumentacija za Semantic MediaWiki nalazi se na <a href=\"http://semantic-mediawiki.org\"><b>semantic-mediawiki.org</b></a>.",
+ "smw_smwadmin_mediazilla": "Greške (bugove) možete prijaviti na <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw_smwadmin_questions": "Ukoliko imate pitanja ili prijedloge, pridružite se raspravi na <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">korisniÄkom forumu Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Struktura podataka",
+ "smw_adminlinks_displayingdata": "Prikaz podataka",
+ "smw_adminlinks_inlinequerieshelp": "Pomoć za umetnute upite",
+ "smw_unknowntype": "Tip ovog svojstva je neispravan",
+ "smw_concept_header": "Stranice koncepta \"$1\"",
+ "smw_conceptarticlecount": "Prikazano $1 {{PLURAL:$1|stranica koja pripada|stranice koje pripadaju|stranica koje pripadaju}} tom konceptu.",
+ "smw-livepreview-loading": "UÄitavam…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hrx.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hrx.json
new file mode 100644
index 00000000..62e0b651
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hrx.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Am loode …"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hsb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hsb.json
new file mode 100644
index 00000000..2e9b0a83
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hsb.json
@@ -0,0 +1,272 @@
+{
+ "@metadata": {
+ "authors": [
+ "Michawiki",
+ "ì•„ë¼"
+ ]
+ },
+ "smw-desc": "Twój wiki pÅ™istupniÅ¡i Äinić - za maÅ¡iny ''a'' ludźi ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentacija online])",
+ "smw_viewasrdf": "RDF-kanal",
+ "smw_finallistconjunct": "a",
+ "smw_isspecprop": "Tuta kajkosć je specialna kajkosć w tutym wikiju.",
+ "smw_concept_description": "Wopisanje koncepta \"$1\"",
+ "smw_no_concept_namespace": "Koncepty hodźa so jenož w mjenowym rumje ''Koncept:'' definować.",
+ "smw_multiple_concepts": "Kóžda konceptowa strona móže jenož jednu konceptowu definiciju měć.",
+ "smw_concept_cache_miss": "Koncept \"$1\" njeda so tuchwilu wužiwać, dokelž wikijowa konfiguracija jón za pÅ™edźěłowanje offline trjeba.\nJeli so problem po wÄ›stym Äasu njezhubi, wopraÅ¡ej so sydÅ‚oweho administratora, zo by wón tutón koncept k dispoziciji stajiÅ‚.",
+ "smw_noinvannot": "Hódnoty njedadźa so nawopaÄnym kajkosćam pÅ™ipokazać.",
+ "version-semantic": "Semantiske rozšěrjenja",
+ "smw_baduri": "URI formy \"$1\" njejsu dowolene.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "WuslÄ›dki liÄić",
+ "smw_printername_csv": "Eksport CSV",
+ "smw_printername_dsv": "DSV-eksport",
+ "smw_printername_debug": "Naprašowanje za zmylkami přepytać (za ekspertow)",
+ "smw_printername_embedded": "Wobsah strony zasadźić",
+ "smw_printername_json": "Eksport JSON",
+ "smw_printername_list": "Lisćina",
+ "smw_printername_ol": "NaliÄenje",
+ "smw_printername_ul": "Nalistowanje",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Šěroka tabela",
+ "smw_printername_template": "Předłoha",
+ "smw_printername_rdf": "RDF-eksport",
+ "smw_printername_category": "Kategorija",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Maksimalna liÄba wuslÄ›dkow, kotraž ma so wróćić",
+ "smw-paramdesc-offset": "Pozicija prěnjeho wuslědka",
+ "smw-paramdesc-headers": "Mjena hłowow abo atributow zwobraznić",
+ "smw-paramdesc-mainlabel": "Pomjenowanje, kotrež ma so hłownej stronje dać",
+ "smw-paramdesc-link": "Hódnoty jako wotkazy pokazać",
+ "smw-paramdesc-intro": "Tekst, kotryž ma so před naprašowanskimi wuslědkami zwobraznić, jeli tajke su",
+ "smw-paramdesc-outro": "Tekst, kotryž ma so za naprašowanskimi wuslědkami zwobraznić, jeli tajke su",
+ "smw-paramdesc-default": "Tekst, kotryž ma so zwobraznić, jeli žane naprašowanske wuslědki njejsu",
+ "smw-paramdesc-sep": "Dźělatko za hódnoty",
+ "smw-paramdesc-showsep": "Dźělatko horjeka w CSV-dataji pokazać (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "MÄ›sto toho zo so wÅ¡Ä› hódnoty zwobraznjeja, jich wustupowanja liÄić a te pokazać.",
+ "smw-paramdesc-distributionsort": "Rozdźělenje hódnotow po liÄbje wustupowanjow sortÄ›rować.",
+ "smw-paramdesc-distributionlimit": "Rozdźělenje hódnotow na wěste hódnoty wobmjezować.",
+ "smw-paramdesc-template": "Mjeno předłohi, z kotrejž maja so wućišće zwobraznić",
+ "smw-paramdesc-columns": "LiÄba Å¡paltow, w kotrychž maja so wuslÄ›dki zwobraznić (standard je $1)",
+ "smw-paramdesc-userparam": "Hódnota, kotraž so za kóždu namołwu předłohi přepodawa, jeli předłoha so wužiwa.",
+ "smw-paramdesc-introtemplate": "Mjeno předłohi, kotraž ma so před naprašowanskimi wuslědkami zwobraznić, jeli tajke su",
+ "smw-paramdesc-outrotemplate": "Mjeno předłohi, kotraž ma so za naprašowanskimi wuslědkami zwobraznić, jeli tajke su",
+ "smw-paramdesc-embedformat": "HTML-tafliÄki, kotrež so wužiwaja, zo bychu nadpisma definowali",
+ "smw-paramdesc-embedonly": "Žane nadpisma zwobraznić",
+ "smw-paramdesc-table-class": "Přidatna CSS-klasa za tabelu",
+ "smw-paramdesc-rdfsyntax": "RDF-syntaksa, kotraž ma so wužiwać",
+ "smw-paramdesc-csv-sep": "Dźělatko, kotrež ma so wužywać",
+ "smw-paramdesc-dsv-separator": "Dźělatko, kotrež ma so wužywać",
+ "smw-paramdesc-dsv-filename": "Mjeno za DSV-dataju",
+ "smw-paramdesc-filename": "Mjeno za wudawansku dataju",
+ "smw-smwdoc-description": "Pokazuje tabelu wšěch parametrow, kotrež dadźa so za podaty wuslědkowy format hromadźe ze standardnymi hódnotami a wopisanjemi wužiwać.",
+ "smw-smwdoc-par-format": "Wuslědkowy format, za kotrehož parametry dokumentacija ma so zwobraznić.",
+ "smw-smwdoc-par-parameters": "Parametry, kotrež maja so pokazać: \"specific\" za tute, kotrež so přez format přidawaja, \"base\" za tute, kotrež su we wšěch formatach k dispoziciji a \"all\" za wobaj.",
+ "smw-paramdesc-sort": "Kajkosć, po kotrejž naprašowanje ma so sortěrować",
+ "smw-paramdesc-order": "Sortěrowanski porjad za naprašowanje",
+ "smw-paramdesc-searchlabel": "Tekst za dalše pytanske wuslědki",
+ "smw-paramdesc-named_args": "Argumenty, kotrež maja so předłoze přepodać",
+ "smw-paramdesc-export": "Eksportowe nastajenje",
+ "smw-paramdesc-prettyprint": "Njeformatowane wudaće, kotrež přidatne zasunjenja a nowe linki zwobraznja",
+ "smw-paramdesc-source": "Alternatiwne naprašowanske žórło",
+ "smw-paramdesc-jsonsyntax": "JSON-syntaksa, kotraž ma so wužiwać",
+ "smw-printername-feed": "RSS- a Atom-kanal",
+ "smw-paramdesc-feedtype": "Kanalowy typ",
+ "smw-paramdesc-feedtitle": "Tekst, kotryž ma so jako titul kanala wužiwać",
+ "smw-paramdesc-feeddescription": "Tekst, kotryž ma so jako wopisanje kanala wužiwać",
+ "smw-paramdesc-feedpagecontent": "Wobsah strony, kotryž ma so z komentarom zwobraznić",
+ "smw-label-feed-description": "$2-kanal: $1",
+ "smw_iq_disabled": "Semantiske naprašowanja su w tutym wikiju znjemóžnjene.",
+ "smw_iq_moreresults": "... dalše wuslědki",
+ "smw_parseerror": "Podata hódnota njebu zrozumjena.",
+ "smw_notitle": "\"$1\" njeda so jako mjeno strony w tutym wikiju wužiwać.",
+ "smw_noproperty": "\"$1\" njeda so jako kajkosć w tutym wikiju wužiwać.",
+ "smw_wrong_namespace": "Jenož strony w mjenowym rumje \"$1\" su tu dowolene.",
+ "smw_manytypes": "Za kajkosć bu wjace haÄ jeden typ definowany.",
+ "smw_emptystring": "Prózdne znamješkowe slědy so njeakceptuja.",
+ "smw_notinenum": "\"$1\" w lisćinje móžnych hódnotow ($2) za tutu kajkosć njeje.",
+ "smw_noboolean": "\"$1\" płaćiwa hódnota typa boolean (wěrny/njewěrny) njeje.",
+ "smw_true_words": "wěrny, haj, true",
+ "smw_false_words": "wopak, njewěrny, ně, false",
+ "smw_nofloat": "\"$1\" liÄba njeje.",
+ "smw_infinite": "LiÄby, kotrež su tak wulke kaž \"$1\", so njepodpÄ›ruja.",
+ "smw_unitnotallowed": "\"$1\" njeje jako płaćiwa měrjenska jednotka za tutón atribut postajił.",
+ "smw_nounitsdeclared": "Žane měrjenske jednotki njejsu so za tutu kajkosć podali.",
+ "smw_novalues": "Žane hódnoty podate.",
+ "smw_nodatetime": "Datum \"$1\" njebu zrozumjeny.",
+ "smw_toomanyclosing": "Zda so, zo \"$1\" w tutym naprašowanju přehusto wustupuje.",
+ "smw_noclosingbrackets": "Wustupowanje pora róžkatych spinkow \"<nowiki>[[</nowiki>\" w twojim napraÅ¡owanju njeje pÅ™ez wotpowÄ›dny \"]]\" wukónÄene.",
+ "smw_misplacedsymbol": "Symbol \"$1\" so na městnje wužiwa, hdźež wužitny njeje.",
+ "smw_unexpectedpart": "Dźěl \"$1\" napraÅ¡owanja njebu zrozumjeny.\nWuslÄ›dki snano kaž woÄakowane njejsu.",
+ "smw_emptysubquery": "Někajke podnaprašowanje nima płaćiwe wuměnjenje.",
+ "smw_misplacedsubquery": "Někajke podnaprašowanje so na městnje wužiwa, hdźež podnaprašowanja njejsu dowolene.",
+ "smw_valuesubquery": "Podnaprašowanja so za hódnoty kajkosće \"$1\" njepodpěruja.",
+ "smw_badqueryatom": "Dźěl \"<nowiki>[[…]]</nowiki>\" naprašowanja njebu zrozumjeny.",
+ "smw_propvalueproblem": "Hódnota kajkosće \"$1\" njebu zrozumjena.",
+ "smw_noqueryfeature": "Wotprašowanska funkcija njepodpěruje so w tutym wikiju a dźěl wotprašowanja je so wotstronił ($1).",
+ "smw_noconjunctions": "A-zwjazanja we wotprašowanajch njepodpěruja so w tutym wikiju a dźěl wotprašowanja je so wotstronił ($1).",
+ "smw_nodisjunctions": "ABO-zwjazanja we wotprašowanjach njepodpěruja so w tutym wikiju a dźěl wotprašowanja je so wotstronił ($1).",
+ "smw_querytoolarge": "Slědowace naprašowanske wuměnjenja njedachu so dla wikijowych wobmjezowanjow za wulkosć abo hłubokosć naprašowanja wobkedźbować: $1.",
+ "smw_notemplategiven": "Podaj hódnotu za parameter \"template\", zo by tutón naprašowanski format fungował.",
+ "smw_db_sparqlqueryproblem": "Naprašowanski wuslědk njeda so z datoweje banki SPARQL wotwołać. Tutón zmylk móhł nachwilny być abo programowy zmylk w datowej bance być.",
+ "smw_db_sparqlqueryincomplete": "Wotmołwjenje na naprašowanje je so jako přećežko wukopało a je so přetorhnyło. Někotre wuslědki móhli falować. Jeli je móžno, spytaj jednoriše naprašowanje město toho wužiwać.",
+ "smw_type_header": "Kajkosće typa \"$1\"",
+ "smw_typearticlecount": "{{PLURAL:$1|Pokazuje so kajkosć, kotraž tutón typ wužiwa|Pokazujetej so $1 kajkosći, kotrejž tutón typ wužiwatej|Pokazuja so $1 kajkosće, kotrež tutón typ wužiwaja|Pokazuje so $1 kajkosćow, kotrež tutón typ wužiwa}}.",
+ "smw_attribute_header": "Strony, kotrež kajkosć \"$1\" wužiwaja",
+ "smw_attributearticlecount": "{{PLURAL:$1|Pokazuje so $1 strona, kotraž tutu kajkosć wužiwa|Pokazujetej $1 stronje, kotrejž tutu kajkosć wužiwatej|Pokazuja so $1 strony, kotrež tutu kajksć wužiwaja|Pokazuje so $1 stronow, kotrež tutu kajkosć wužiwa}}.",
+ "exportrdf": "Do RDF eksportować",
+ "smw_exportrdf_docu": "Tuta strona ći zmóžnja daty ze strony we formaće RDF wotwołać. Zo by strony eksportował, zapodaj titule w slědowacym kašćiku, jedyn titul na linku.",
+ "smw_exportrdf_recursive": "Eksportuj wšě piwuzne strony.\nWobkedźbuj, zo wuslědk móhł wulki być!",
+ "smw_exportrdf_backlinks": "Eksportuj tež wšě strony, kotrež so na eksportowane strony poćahuja.\nPłodźi přepytujomny RDF.",
+ "smw_exportrdf_lastdate": "Njeeksportuj strony, kotrež njejsu so wot podateho Äasoweho dypka zmÄ›nili.",
+ "smw_exportrdf_submit": "Eksportować",
+ "uriresolver": "Rezolwer URI",
+ "properties": "Kajkosće",
+ "smw_properties_docu": "Slědowace kajkosće so we wikiju wužiwaja.",
+ "smw_property_template": "$1 typa $2 ($3 {{PLURAL:$3|wužiće|wužići|wužića|wužićow}})",
+ "smw_propertylackspage": "Wšě kajkosće měli so přez stronu wopisać!",
+ "smw_propertylackstype": "Za tutu kajkosć njeje so žadyn typ podał (mjeztym so typ $1 předpokładuje).",
+ "smw_propertyhardlyused": "Tuta kajkosć so we wikiju lědma wužiwa!",
+ "smw-property-name-invalid": "Kajkosć $1 njeda so wužiwać (njepłaćiwe kajkostne mjeno)",
+ "smw-sp-property-searchform": "Kajkosće zwobraznić, kotrež wobsahuja:",
+ "smw-sp-property-searchform-inputinfo": "Wudaće dźiwa na wulkopisanje. Za filtrowanje so jenož kajkosće zwobraznjeja, kotrež wuměnjenju wotpowěduja.",
+ "concepts": "Koncepty",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts Koncept] hodźi so jako \"dynamiska kategorija\" wobhladać, t.r. jako zbÄ›rka stronow, kotrež so manuelnje njewutworjeja, ale so pÅ™ez Semantic mediaWiki z wopisanja dateho napraÅ¡owanja wobliÄuja.",
+ "smw-special-concept-header": "Lisćina konceptow",
+ "smw-special-concept-count": "{{PLURAL:$1|Slědowacy koncept|Slědowacej $1 konceptaj|Slědowace $1 koncepty|Slědowacych $1 konceptow}} {{PLURAL:$1|eksistuje|eksistujetej|eksistuja|eksistuje}}.",
+ "smw-special-concept-empty": "Njeje so žadyn koncept namakał.",
+ "unusedproperties": "Njewužiwane kajkosće",
+ "smw-unusedproperties-docu": "SlÄ›dowace kajkosće eksistuja, haÄrunjež žana druha strona je wužiwa.",
+ "smw-unusedproperty-template": "$1 typa $2",
+ "wantedproperties": "Požadane kajkosće",
+ "smw-wantedproperties-docu": "Slědowace kajkosće so we wikiju wužiwaja, ale nimaja stronu, kotraž je wopisuje.",
+ "smw-wantedproperty-template": "$1 ({{PLURAL:$2|jónu wužity|dwójce wužitej|$2 razy wužite|$2 razow wužite}})",
+ "smw_purge": "Aktualizować",
+ "types": "Typy",
+ "smw_types_docu": "Deleka je lisćina wšěch datowych typow, kotrež dadźa so kajkosćam připokazać.",
+ "smw-special-types-no-such-type": "Podaty datowy typ njeeksistuje",
+ "smw-statistics": "Semantiska statistika",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Kajkostna hódnota|Kajkostnej hódnoće|Kajkostne hódnoty}} (dohromady)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Kajkosć|Kajkosći|Kajkosće}}]] (dohromady)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Kajkosć|Kajkosći|Kajkosće}} (dohromady)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Kajkosć|Kajkosći|Kajkosće}} (ze stronu zregistrowane)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Kajkosć|Kajkosći|Kajkosće}} (datowemu typej připokazane)",
+ "smw-statistics-query-inline": "{{PLURAL:$1|Naprašowanje|Naprašowani|Naprašowanja}}",
+ "smw-statistics-query-size": "Wulkosć naprašowanja",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Koncept|Konceptaj|Koncepty}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Koncept|Konceptaj|Koncepty}}]]",
+ "smw-statistics-subobject-count": "{{PLURAL:$1|Podobjekt|Podobjektaj|Podobjekty}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datowy typ|Datowej typaj|Datowe typy}}]]",
+ "smw_uri_doc": "Rozpušćak URI implementuje [$1 W3C TAG finding on httpRange-14].\nStara so wo to, zo so ludźo z websydłami njestanu.",
+ "ask": "Semantiske pytanje",
+ "smw_ask_sortby": "Po špalće sortěrować (opcionalny)",
+ "smw_ask_ascorder": "Postupowacy",
+ "smw_ask_descorder": "Spadowacy",
+ "smw_ask_submit": "Wuslědki namakać",
+ "smw_ask_editquery": "Naprašowanje wobdźěłać",
+ "smw_add_sortcondition": "[Sortěrowanske wuměnjenje přidać]",
+ "smw_ask_hidequery": "Naprašowanje schować",
+ "smw_ask_help": "Pomoc za naprašowanja",
+ "smw_ask_queryhead": "Naprašowanje",
+ "smw_ask_printhead": "Přidatne daty, kotrež maja so zwobraznić",
+ "smw_ask_printdesc": "(přidaj jedne atributowe mjeno na linku)",
+ "smw_ask_format_as": "Formatowany jako:",
+ "smw_ask_defaultformat": "standard",
+ "smw_ask_otheroptions": "Druhe opcije",
+ "smw-ask-otheroptions-info": "Tutón wotrězk wobsahuje opcije, kotrež měnjeja ćišćerske wudaće. Parametrowe wopisanje dadźa so wobhladać, jeli so kursor myški nad nich znošuje.",
+ "smw-ask-otheroptions-collapsed-info": "Prošu wužij plusowy symbol (+), zo by sej wšě k dispoziciji stejace opcije wobhladał",
+ "smw_ask_show_embed": "Zasadźeny kod pokazać",
+ "smw_ask_hide_embed": "Zasadźeny kod schować",
+ "smw_ask_embed_instr": "Wužij slědowacy kod, zo by tute wotprašowanje do wikijoweje strony zasadźił.",
+ "smw-ask-delete": "[Zhašeć]",
+ "smw-ask-sorting": "Sortěrowanje",
+ "smw-ask-format-selection-help": "Za podrobnostne wopisanje wopytaj prošu stronu $1 pomocy.",
+ "searchbyproperty": "Po atribuće pytać",
+ "smw_sbv_docu": "Wšě strrony pytać, kotrež maja wěstu kajkosć a hódnotu.",
+ "smw_sbv_novalue": "Zapodaj płaćiwu hódnotu za kajkosć abo wobhladaj sej wšě hódnoty kajkosće za \"$1\".",
+ "smw_sbv_displayresult": "Lisćina wšěch stronow, kotrež maja kajkosć \"$1\" z hódnotu \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Lisćina wšěch stronow, kotrež maja kajkosć \"$1\" z hódnotu \"$2\".\nDokelž je jenož mało wuslědkow, so tež podobne hódnoty nalistuja.",
+ "smw_sbv_property": "Kajkosć:",
+ "smw_sbv_value": "Hódnota:",
+ "smw_sbv_submit": "Wuslědki namakać",
+ "browse": "Wiki přepytać",
+ "smw_browselink": "Kajkosće přepytać",
+ "smw_browse_article": "Zapodaj mjeno strony, wot kotrejež ma so pytanje zapoÄeć.",
+ "smw_browse_go": "Wotpósłać",
+ "smw_browse_show_incoming": "kajkosće pokazać, kotrež sem wotkazuja",
+ "smw_browse_hide_incoming": "kajkosće schować, kotrež sem wotkazuja",
+ "smw_browse_no_outgoing": "Tuta strona nima kajkosće.",
+ "smw_browse_no_incoming": "Žane kajkosće k tutej stronje njewotkazuja.",
+ "smw_inverse_label_default": "$1 z",
+ "smw_inverse_label_property": "Pomjenowanje nawopaÄneje kajkosće",
+ "pageproperty": "Pytanje kajkosćow strony",
+ "smw_pp_docu": "Pytaj wšě hódnoty kajkosće na datej stronje.\nZapodaj stronu kaž tež kajkosć.",
+ "smw_pp_from": "Ze strony",
+ "smw_pp_type": "Kajkosć",
+ "smw_pp_submit": "Wuslědki namakać",
+ "smw_result_prev": "Předchadny",
+ "smw_result_next": "Přichodny",
+ "smw_result_results": "Wuslědki",
+ "smw_result_noresults": "Žane wuslědki.",
+ "smwadmin": "Administraciske funkcije za Semantic MediaWiki",
+ "smw-admin-setupsuccess": "Składowanska jednotka je so wuspěšnje nastajiła.",
+ "smw_smwadmin_return": "Wróćo k $1",
+ "smw_smwadmin_updatestarted": "Nowy proces aktualizacije za wobnowjenje semantiskich datow bu startowany.\nWšě składowane daty budu so znowa tworić abo porjedźeć, hdźež je to trěbne.\nMóžeš proces aktualizacije na tutej specialnej stronje slědować.\n\nWróćo k $1.",
+ "smw_smwadmin_updatenotstarted": "Proces aktualizacije hižo běži.\nNowy so njezapoÄnje.\n\nWróćo k $1.",
+ "smw_smwadmin_updatestopped": "Wšě eksistowace aktualizaciske procesy su so zastajili.\n\nWróćo k $1.",
+ "smw_smwadmin_updatenotstopped": "Zo by běžny aktualizowanski proces zastajił, dyrbiš kontrolny kašćik aktiwizować, zo by podał, zo sy sej woprawdźe wěsty.\n\nWróćo k $1.",
+ "smw-admin-docu": "Tuta specialna strona ći za instalaciju a akutalizaciju <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a> pomha.\nNjezabudź hódnotne daty zawÄ›sćić, prjedy haÄ administratiwne funkcije wuwjedźeÅ¡.",
+ "smw-admin-db": "Instalacija a aktualizacija datoweje banki",
+ "smw-admin-dbdocu": "Semantic MediaWiki trjeba nÄ›kotre rozÅ¡Ä›rjenja na datowej bance MediaWiki, zo by semantiske daty skÅ‚adowaÅ‚.\nSlÄ›dowaca funkcija zawÄ›sćuje, zo twoja datowa banka je prawje zarjadowana.\nZmÄ›ny, kotrež so w tutym kroku Äinja, njewobwliwuja zbytk datowje banki MediaWiki a dadźa so lochko cofnyć, jeli požadane.\nTuta zarjadowanska funkcija da so wjacore razy wuwjesć, bjeztoho zo so Å¡koda zawinuje, je wÅ¡ak jenož jedyn raz pÅ™i instalaciji abo aktualizaciji trÄ›bna.",
+ "smw-admin-permissionswarn": "Jeli operacija so ze zmylkami SQL njeradźi, najskerje wužiwar datoweje banki, kotrehož twój wiki wužiwa (hlej twoju dataju LocalSettings.php), dosahace prawa nima.\nZaruÄ tute pÅ™idatne wužiwarske prawa za wutworjenje a wuÅ¡mórnjenje tabelow, zapodaj nachwilu pÅ™izjewjenje twojeho administratora datoweje banki do dataje LocalSettings.php abo wužij wothladowanski skript <code>setupStore.php</code>, kotryž móže wužiwarske daty administratora wužiwać.",
+ "smw-admin-dbbutton": "Tabele inicializować abo aktualizować",
+ "smw-admin-announce": "Twój wiki připowědźić",
+ "smw_smwadmin_datarefresh": "Porjedźenje a aktualizacija datow",
+ "smw_smwadmin_datarefreshdocu": "Je móžno wÅ¡Ä› daty ze Semantic MediaWiki na zakÅ‚adźe aktualneho wobsaha wikija wobnowić. To móže wužitne być, zo bychu so wobÅ¡kodźene daty porjedźili abo daty aktualizowali, jeli interny format je so aktualizacije softwary dla zmÄ›niÅ‚.\nAktualizacija so stronu po stronu pÅ™ewjedźe a njebudźe so hnydom kónÄić.\nNaslÄ›dne pokazuje, jeli aktualizacija so wotmÄ›wa a ći dowoluje aktualizacije startować abo zastajić (chibazo tuta funkcija bu wot administratora sydÅ‚a znjemóžnjena).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Aktualizacija so hižo wotmÄ›wa.</strong>\nJe normalnje, zo so aktualizacija jenož pomaÅ‚u wotmÄ›wa, dokelž daty jenož w maÅ‚ych porcijach kóždy raz, hdyž wužiwar ma pÅ™istup na wiki, aktualizuje.\nZo by tutu aktualizaciju spěšniÅ¡o skónÄiÅ‚, móžeÅ¡ wothladowanski skript MediaWiki <code>runJobs.php</code> zawoÅ‚ać (wužij opciju <code>--maxjobs 1000</code>, zo by liÄbu aktualizacijow, kotrež so z jednym wotmachom pÅ™ewjedu, wobmjezowaÅ‚).\nTrochowany staw aktualneje aktualizacije:",
+ "smw_smwadmin_datarefreshbutton": "Aktualizaciju datow zapoÄeć",
+ "smw_smwadmin_datarefreshstop": "Tutu aktualizaciju zastajić",
+ "smw_smwadmin_datarefreshstopconfirm": "Haj, sym wěsty.",
+ "smw-admin-support": "Podpěru dóstać",
+ "smw-admin-supportdocu": "Wšelake resursy móhli ći w problemowych padach pomhać:",
+ "smw-admin-installfile": "Jeli su problemy z twojej instalaciju, wobhladaj sej směrnicy w <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">dataji INSTALL</a>.",
+ "smw-admin-smwhomepage": "Kompletna wužiwarska dokumentacija za Semantic MediaWiki je na <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Zmylki móžeš w systemje <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a> zdźělić.",
+ "smw-admin-questions": "Jeli maš dalše prašenja abo namjety, wobdźěl so na diskusiji na <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">wužiwarskim forumje Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Struktura datow",
+ "smw_adminlinks_displayingdata": "Zwobraznjenje datow",
+ "smw_adminlinks_inlinequerieshelp": "Pomoc za rjadowe wotprašowanja",
+ "smw-createproperty-isproperty": "To je kajkosć typa $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Dowolena hódnota za tutu kajkosć je|Dowolenej hódnoće za tutu kajkosć stej|Dowolene hódnoty za tutu kajkosć su|Dowolene hódnoty za tutu kajkosć su}}:",
+ "smw-paramdesc-category-delim": "Dźělatko",
+ "smw-paramdesc-category-template": "Předłoha, z kotrejž zapiski maja so formatować",
+ "smw-paramdesc-category-userparam": "Parmeter, kotryž ma so předłoze přepodać",
+ "smw-info-par-message": "Powěsć, kotraž ma so zwobraznić.",
+ "smw-info-par-icon": "Symbol, kotryž ma so pokazać, pak \"info\" pak \"warnowanje\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-ask-options": "Semantiske pytanske opcije",
+ "smw-prefs-intro-text": "Slědowace nastajenja so wot [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (abo podobnych rozšěrjenjow) k dispoziciji steja, zo bychu indiwiduelne přiměrjenje za wubrane funkcije zmóžnili. Za dalše informacije hlej tutón [https://www.semantic-mediawiki.org/wiki/Help:User_preferences wotrězk pomocy]",
+ "smw-prefs-ask-options-tooltip-display": "Parametrowy tekst jako spěšne info zwobraznić",
+ "smw-ui-tooltip-title-property": "Kajkosć",
+ "smw-ui-tooltip-title-quantity": "Mnóstwo",
+ "smw-ui-tooltip-title-info": "Informacije",
+ "smw-ui-tooltip-title-service": "Słužbne wotkazy",
+ "smw-ui-tooltip-title-warning": "Zmylk",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Podawk",
+ "smw-ui-tooltip-title-note": "Přispomnjenka",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw_unknowntype": "Typ tuteje kajkosće je njepłaćiwy",
+ "smw-concept-cache-text": "Koncept ma dohromady $1 {{PLURAL:$1|stronu|stronje|strony|stronow}} a je so $2 posledni raz zaktualizował.",
+ "smw_concept_header": "Strony koncepta \"$1\"",
+ "smw_conceptarticlecount": "Deleka {{PLURAL:$1|so $1 strona pokazuje|so $1 stronje pokazujetej|so $1 strony pokazuja|so $1 stronow pokazuje}}.",
+ "right-smw-admin": "Administraciske nadawki (Semantic MediaWiki)",
+ "group-smwadministrator": "SMW-Administratorojo",
+ "group-smwadministrator-member": "{{GENDER:$1|SMW-administrator|SMW-administratorka}}",
+ "grouppage-smwadministrator": "{{ns:project}}:SMW-administratorojo",
+ "action-smw-admin": "administraciske nadawki Semantic MediaWiki",
+ "smw-sp-properties-cache-info": "Nalistowane daty pochadźeja z [https://www.semantic-mediawiki.org/wiki/Caching pufrowaka] a su so $1 posledni raz zaktualozowali.",
+ "smw-sp-properties-header-label": "Lisćina kajkosćow",
+ "smw-sp-admin-settings-button": "Lisćinu nastajenjow wutworić",
+ "smw-admin-objectid": "ID objekta:",
+ "smw-livepreview-loading": "Čita so…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ht.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ht.json
new file mode 100644
index 00000000..20c30f26
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ht.json
@@ -0,0 +1,27 @@
+{
+ "@metadata": {
+ "authors": [
+ "Boukman",
+ "Jvm",
+ "Masterches"
+ ]
+ },
+ "smw_viewasrdf": "Wè tankou fòma RDF",
+ "smw_finallistconjunct": ", epi",
+ "smw_factbox_head": "Bagay an relasyon ak $1",
+ "smw_isspecprop": "Pwopryete sa espesyal toutbon nan wiki sa",
+ "smw_baduri": "Eskize nou, URIs yo pou domèn \"$1\" pa otorize, oubyen li pa disponib nan plas isit la.",
+ "smw_iq_disabled": "Eskize nou. Rechèch nan atik wiki sa a pa otorize oubyen nou dezaktive l.",
+ "smw_iq_moreresults": "… lòt rezilta yo",
+ "smw_parseerror": "Valè ou bay oubyen sa li retounen pa klè, nou pa kapab konprann li.",
+ "smw_notitle": "Nou pa kapab itilize \"$1\" tankou non yon paj nan wiki sa.",
+ "smw_manytypes": "Plizyè tip done pase nan atribi.",
+ "smw_emptystring": "Chèn mo ou bay an pa dwe vid.",
+ "smw_notinenum": "\"$1\" pa nan lis valè posib pou ($2) pou atribi, pwopryete a.",
+ "smw_noboolean": "\"$1\" pa rekonèt tankou yon valè bouleyen (vre/fo).",
+ "smw_true_words": "vre,v,wi,w",
+ "smw_false_words": "fo,f,non,n",
+ "smw_nofloat": "\"$1\" pa yon nimewo.",
+ "smw_infinite": "Nimewo ki gwo tankou \"$1\" pa sipòte.",
+ "smw_unknowntype": "Tip done \"$1\" yo ki pa sipòte retounen nan atribi a."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hu.json
new file mode 100644
index 00000000..e7237380
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hu.json
@@ -0,0 +1,238 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dani",
+ "Glanthor Reviol",
+ "TK-999",
+ "ì•„ë¼",
+ "Tacsipacsi",
+ "Wolf Rex",
+ "Bencemac",
+ "Dj",
+ "David92003"
+ ]
+ },
+ "smw-desc": "A wiki elérhetőbbé tétele – gépek ''és'' emberek számára is ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online dokumentáció])",
+ "smw_viewasrdf": "RDF hírcsatorna",
+ "smw_finallistconjunct": ", és",
+ "smw_isspecprop": "Ez a tulajdonság különleges ebben a wikiben.",
+ "smw_concept_description": "A(z) „$1†koncepció leírása",
+ "smw_no_concept_namespace": "Koncepciókat csak a ''Koncepció:'' névtérben levő lapokon lehet megadni.",
+ "smw_multiple_concepts": "Minden koncepció lapon csak egy koncepció definíciója szerepelhet.",
+ "smw_concept_cache_miss": "A(z) „$1†koncepció nem használható pillanatnyilag, mivel a wiki konfigurációja szerint kapcsolat nélküli módban kell kiszámítani.\nHa a probléma nem szűnik meg bizonyos idő elteltével, kérd az oldal adminisztrátorát hogy tegye elérhetővé a koncepciót.",
+ "smw_noinvannot": "Inverz tulajdonságokhoz nem lehet értékeket rendelni.",
+ "version-semantic": "Szemantikai kiterjesztések",
+ "smw_baduri": "„$1†formájú URI-k nem engedélyezettek.",
+ "smw_printername_count": "Eredmények megszámlálása",
+ "smw_printername_csv": "CSV exportálás",
+ "smw_printername_dsv": "DSV formátumú exportálás",
+ "smw_printername_debug": "Lekérdezés hibakeresése (szakértőknek)",
+ "smw_printername_embedded": "Lap tartalmának beágyazása",
+ "smw_printername_json": "JSON exportálás",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Felsorolás",
+ "smw_printername_ul": "Részletezés",
+ "smw_printername_table": "Táblázat",
+ "smw_printername_broadtable": "Széles táblázat",
+ "smw_printername_template": "Sablon",
+ "smw_printername_rdf": "RDF formátumú exportálás",
+ "smw_printername_category": "Kategória",
+ "validator-type-class-SMWParamSource": "szöveg",
+ "smw-paramdesc-limit": "Legfeljebb ennyi eredmény megjelenítése",
+ "smw-paramdesc-headers": "Fejlécek/tulajdonságnevek megjelenítése",
+ "smw-paramdesc-mainlabel": "A kezdőlap nevének címkéje",
+ "smw-paramdesc-link": "Az értékek hivatkozásként jelenjenek meg",
+ "smw-paramdesc-intro": "A lekérdezés eredményei előtt megjelenő szöveg",
+ "smw-paramdesc-outro": "A lekérdezés eredményei után megjelenő szöveg",
+ "smw-paramdesc-default": "Megjelenítendő szöveg, ha a lekérdezésnek nincs eredménye",
+ "smw-paramdesc-sep": "Az eredmények közti elválasztó",
+ "smw-paramdesc-distribution": "Az összes érték megjelenítése helyett számolja meg az előfordulásukat, és mutassa azokat.",
+ "smw-paramdesc-distributionsort": "Értékszórás rendezése előfordulások száma szerint.",
+ "smw-paramdesc-distributionlimit": "Értékszórás korlátozása néhány érték előfordulásának számára",
+ "smw-paramdesc-template": "Egy sablon neve, amellyel megjelenítendő a kiírás",
+ "smw-paramdesc-columns": "A keresési eredmények oszlopainak száma",
+ "smw-paramdesc-introtemplate": "A lekérdezés eredményei előtt (ha vannak) megjelenítendő sablon neve",
+ "smw-paramdesc-outrotemplate": "A lekérdezés eredményei után (ha vannak) megjelenítendő sablon neve",
+ "smw-paramdesc-embedformat": "A címsorokhoz használt HTML-tag",
+ "smw-paramdesc-embedonly": "Fejlécek kikapcsolása",
+ "smw-paramdesc-table-class": "A táblázathoz beállítandó extra CSS osztály",
+ "smw-paramdesc-rdfsyntax": "A használandó RDF-változat",
+ "smw-paramdesc-csv-sep": "Add meg az oszlopelválasztó-jelet",
+ "smw-paramdesc-dsv-separator": "A használandó elválasztójel",
+ "smw-paramdesc-dsv-filename": "A DSV-fájl neve",
+ "smw-smwdoc-description": "Egy táblázatot jelenít meg azon összes paraméterrel, amelyek az alapértékekkel és -leírásokkal együtt használhatóak a megadott célformátumban.",
+ "smw-smwdoc-par-format": "A célfrmátum, amelynek paramétereinek dokumentációját meg kell jeleníteni.",
+ "smw-smwdoc-par-parameters": "A megjelenítendő paraméterek. \"specific\" a formátum által hozzáadottakhoz, \"base\" a minden formátumban elérhetőkhöz és \"all\" mindkét fajtához.",
+ "smw-paramdesc-sort": "A tulajdonság, amely szerint rendezni kell a lekérdezést",
+ "smw-paramdesc-order": "A lekérdezések rendezési sorrendje",
+ "smw-paramdesc-searchlabel": "Szöveg a keresés folytatásához",
+ "smw-paramdesc-named_args": "Nevezd meg a sablonba átküldendő argumentumokat",
+ "smw_iq_disabled": "A szemantikus lekérdezések le vannak tiltva ezen a wikin.",
+ "smw_iq_moreresults": "… további eredmények",
+ "smw_parseerror": "A megadott érték nem érthető.",
+ "smw_notitle": "A(z) „$1†nem használható lap neveként ebben a wikiben.",
+ "smw_noproperty": "A(z) \"$1\" nem használható tulajdonság neveként ebben a wikiben.",
+ "smw_wrong_namespace": "Csak a(z) „$1†névtérbeli lapok engedélyezettek itt.",
+ "smw_manytypes": "Egynél több megadott típus a tulajdonsághoz.",
+ "smw_emptystring": "Üres sztringek nem elfogadhatóak.",
+ "smw_notinenum": "„$1†nincs az engedélyezett értékek listájában ($2) a „$3†tulajdonságnak.",
+ "smw_noboolean": "„$1†nem értelmezhető mint logikai (igaz/hamis) érték",
+ "smw_true_words": "igaz,igen,i",
+ "smw_false_words": "hamis,nem,n",
+ "smw_nofloat": "„$1†nem egy szám.",
+ "smw_infinite": "Ilyen nagy számok („$1â€) nem támogatottak.",
+ "smw_unitnotallowed": "A(z) \"$1\" nem érvényes mértékegysége ennek a tulajdonságnak.",
+ "smw_nounitsdeclared": "Nem határozták meg mértékegységet ennek a tulajdonságnak.",
+ "smw_novalues": "Nincsenek megadva értékek.",
+ "smw_nodatetime": "A(z) „$1†dátum nem értelmezhető.",
+ "smw_toomanyclosing": "A(z) „$1†túl sokszor fordul elő a lekérdezésben.",
+ "smw_noclosingbrackets": "A lekérdezésben szerepelnek nyitó szögletes zárójelek „(<nowiki>[[</nowiki>)†a lezáró párjuk („]]â€) nélkül.",
+ "smw_misplacedsymbol": "A(z) „$1†szimbólum egy olyan helyen volt használva, ahol nincs haszna.",
+ "smw_unexpectedpart": "A lekérdezés „$1†része nem értelmezhető.\nAz eredmények eltérhetnek a várttól.",
+ "smw_emptysubquery": "Valamely allekérdezés nem tartalmaz érvényes feltételt.",
+ "smw_misplacedsubquery": "Allekérdezés volt egy olyan helyen, ahol nem engedélyezettek az allekérdezések.",
+ "smw_valuesubquery": "A(z) „$1†tulajdonság értékeinél nem támogatottak az allekérdezések.",
+ "smw_badqueryatom": "A lekérdezés egy részét („<nowiki>[[…]]</nowiki>â€) nem sikerült értelmezni.",
+ "smw_propvalueproblem": "A(z) „$1†tulajdonság értéke nem értelmezhető.",
+ "smw_noqueryfeature": "A lekérdezés egyes részei nem támogatottak ebben a wikiben, így a lekérdezés egy része el lett dobva ($1).",
+ "smw_noconjunctions": "A konjunkció a lekérdezésekben nem támogatott ebben a wikiben, így a lekérdezés egy része el lett dobva ($1).",
+ "smw_nodisjunctions": "A diszjunkció a lekérdezésekben nem támogatott ebben a wikiben, így a lekérdezés egy része el lett dobva ($1).",
+ "smw_querytoolarge": "A következő lekérdezés-feltételek nem lettek figyelembe véve a wikin érvényes lekérdezésméret vagy -mélység-korlátozások miatt: $1",
+ "smw_notemplategiven": "Adj értéket a lekérdezés „paramétersablonjánakâ€, hogy működjön ez a lekérdezésformátum.",
+ "smw_db_sparqlqueryproblem": "A lekérdezés eredméynét nem tudtuk begyűjteni a SPARQL adatbázisból. Ez a hiba ideiglenes, de az adatbázisszoftver hibáját is jelentheti.",
+ "smw_db_sparqlqueryincomplete": "A lekérdezés megválaszolása túl nehéznek bizonyult és megszakadt. Néhány eredmény hiányozhat. Ha lehet, próbálkozz egyszerűbb lekérdezéssel.",
+ "smw_type_header": "A(z) „$1†típus tulajdonságai",
+ "smw_typearticlecount": "{{PLURAL:$1|Egy|$1}} tulajdonság megjelenítése ezen típus használatával.",
+ "smw_attribute_header": "A(z) „$1†tulajdonságot használó lapok",
+ "smw_attributearticlecount": "{{PLURAL:$1|Egy|$1}} lap megjelenítése ezen tulajdonság használatával.",
+ "specialpages-group-smw_group": "Szemantikus MediaWiki",
+ "exportrdf": "Lapok exportálása RDF-be",
+ "smw_exportrdf_docu": "Ez a lap lehetőséget teremt adatok beszerzésére egy lapról RDF formátumban.\nA lapok exportálásához írd be a címeiket az alábbi szövegdobozba, soronként egyet.",
+ "smw_exportrdf_recursive": "Az összes kapcsolódó lap rekurzív exportálása.\nAz eredmény elég nagy lehet!",
+ "smw_exportrdf_backlinks": "Kimenti az összes lapot, ami hivatkozik az exportált lapokra.\nBöngészhető RDF-et készít.",
+ "smw_exportrdf_lastdate": "Ne exportáljon lapokat, amelyek nem változtak mióta a megadott időpont óta.",
+ "smw_exportrdf_submit": "Exportálás",
+ "uriresolver": "URI feloldó",
+ "properties": "Tulajdonságok",
+ "smw_properties_docu": "A wikiben az alábbi tulajdonságok használatosak.",
+ "smw_property_template": "$2 típusú $1 ($3 használat)",
+ "smw_propertylackspage": "Minden tulajdonságnak kell hogy legyen leírólapja!",
+ "smw_propertylackstype": "Nincs típus megadva ehhez a tulajdonsághoz ($1 típus feltételezése).",
+ "smw_propertyhardlyused": "Ez a tulajdonság aligha használatos a wikiben!",
+ "smw-sp-property-searchform": "A következőt tartalmazó tulajdonságok listázása:",
+ "unusedproperties": "Nem használt tulajdonságok",
+ "smw-unusedproperties-docu": "A következő tulajdonságok habár léteznek, de egyetlen lap sem használja őket.",
+ "smw-unusedproperty-template": "$2 típusú $1",
+ "wantedproperties": "Keresett tulajdonságok",
+ "smw-wantedproperties-docu": "A következő tulajdonságok használva vannak a wikiben, azoban még nem rendelkeznek leíró lappal.",
+ "smw-wantedproperty-template": "$1 ($2 helyen használva)",
+ "smw_purge": "Frissítés",
+ "types": "Típusok",
+ "smw_types_docu": "Ez a tulajdonságokhoz rendelhető adattípusok listáját tartalmazza.",
+ "smw-statistics": "Szemantikus statisztikák",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Adattípus|Adattípusok}}]]",
+ "smw_uri_doc": "Az URI-feloldó implementálja a [$1 W3C TAG finding on httpRange-14]-ben meghatározottakat.\nBiztosítja, hogy az emberek ne váljanak weboldalakká.",
+ "ask": "Szemantikus keresés",
+ "smw_ask_sortby": "Rendezés oszlopok szerint (nem kötelező)",
+ "smw_ask_ascorder": "Növekvő",
+ "smw_ask_descorder": "Csökkenő",
+ "smw_ask_submit": "Eredmények keresése",
+ "smw_ask_editquery": "Lekérdezés szerkesztése",
+ "smw_add_sortcondition": "[Rendezési feltétel hozzáadása]",
+ "smw_ask_hidequery": "Lekérdezés elrejtése",
+ "smw_ask_help": "Lekérdezések súgója",
+ "smw_ask_queryhead": "Feltétel",
+ "smw_ask_printhead": "További megjelenítendő adatok",
+ "smw_ask_printdesc": "(egy tulajdonságnevet adj meg soronként)",
+ "smw_ask_format_as": "Formázás mint:",
+ "smw_ask_defaultformat": "alapértelmezett",
+ "smw_ask_otheroptions": "Egyéb beállítások",
+ "smw_ask_show_embed": "Beágyazási kód megjelenítése",
+ "smw_ask_hide_embed": "Beágyazási kód elrejtése",
+ "smw_ask_embed_instr": "Ha egy wikilapra szeretnéd beilleszteni ezt a lekérdezést, másold be az alábbi kódot.",
+ "smw-ask-delete": "Eltávolítás",
+ "smw-ask-sorting": "Rendezés",
+ "smw-ask-search": "Keresés",
+ "searchbyproperty": "Keresés tulajdonság szerint",
+ "smw_sbv_docu": "Az összes olyan lap megkeresése, ami a megadott tulajdonsággal és értékkel rendelkezik.",
+ "smw_sbv_novalue": "Add meg a tulajdonság egy érvényes értékét, vagy nézd meg a(z) „$1†összes tulajdonság-értékét.",
+ "smw_sbv_displayresult": "Az össze lap listája, amelyeknek vagy van „$1†tulajdonsága „$2†értékkel",
+ "smw_sbv_displayresultfuzzy": "Az összes olyan lap listája, melyeknél a(z) „$1†tulajdonság a(z) „$2†értéket veszi fel.\nMivel csak néhány találat van, a közeli értékek is meg vannak jelenítve.",
+ "smw_sbv_property": "Tulajdonság:",
+ "smw_sbv_value": "Érték:",
+ "smw_sbv_submit": "Eredmények keresése",
+ "browse": "Wiki tallózása",
+ "smw_browselink": "Tulajdonságok böngészése",
+ "smw_browse_article": "Add meg a lap nevét, ahonnan el szeretnéd kezdeni a böngészést.",
+ "smw_browse_go": "Menj",
+ "smw_browse_show_incoming": "Bejövő tulajdonságok mutatása",
+ "smw_browse_hide_incoming": "Bejövő tulajdonságok elrejtése",
+ "smw_browse_no_outgoing": "Ehhez a laphoz nem tartoznak tulajdonságok.",
+ "smw_browse_no_incoming": "Egy tulajdonság sem hivatkozik erre a lapra.",
+ "smw_inverse_label_default": "$1",
+ "smw_inverse_label_property": "Tulajdonság címkéjének felcserélése",
+ "pageproperty": "Laptulajdonság keresés",
+ "smw_pp_docu": "Vagy írj be egy oldalt és egy tulajdonságot, vagy csak egy tulajdonságot ahhoz, hogy visszahozz minden hozzárendelt értéket-",
+ "smw_pp_from": "Ettől a laptól:",
+ "smw_pp_type": "Tulajdonság:",
+ "smw_pp_submit": "Eredmények keresése",
+ "smw_result_prev": "Előző",
+ "smw_result_next": "Következő",
+ "smw_result_results": "Eredmények",
+ "smw_result_noresults": "Nincs találat.",
+ "smwadmin": "Adminisztrációs és karbantartási funkciók",
+ "smw-admin-setupsuccess": "A tárolómotor be lett állítva.",
+ "smw_smwadmin_return": "Vissza ide: $1",
+ "smw_smwadmin_updatestarted": "Egy új frissítő folyamat indult a szemantikus adatok frissítése céljából.\nMinden tárolt adat újra elő lesz állítva, és javítva lesz, ahol szükséges.\nA frissítési folyamat előrehaladását ezen a speciális lapon követheted figyelemmel.",
+ "smw_smwadmin_updatenotstarted": "Már fut egy frissítő folyamat.\nNem kell újat készíteni.",
+ "smw_smwadmin_updatestopped": "Minden futó frissítési folyamat leállítva.",
+ "smw_smwadmin_updatenotstopped": "Az éppen futó frissítési folyamat megszakításához aktiválnod kell a jelölőnégyzetet, hogy jelezd, tényleg biztos vagy benne.",
+ "smw-admin-docu": "Ez a speciális lap segít a <a href=\"https://semantic-mediawiki.org\">Szemantikus MediaWiki</a> telepítése, karbantartása, használata és frissítése során.\nNe felejts el másolatot készíteni az értékes adatokról az adminisztratív funkciók használata előtt!",
+ "smw-admin-db": "Adatbázis-karbantartás",
+ "smw-admin-dbdocu": "A Szemantikus MediaWikinek szüksége van néhány kiterjesztésre az adatbázison a szemantikus adatok tárolásához.\nAz alábbi funkció ellenőrzi, hogy az adatbázis megfelelően be van-e állítva.\nAz ezen lépés során végrehajtott változások nincsenek hatással a MediaWiki adatbázisának többi részére, és egyszerűen visszavonható, amennyiben szükséges.\nEz a telepítési lépés többször is végrehajtható, anékül, hogy bármilyen kárt tenne, de csak egyszer szükséges a telepítés vagy frissítés során.",
+ "smw-admin-permissionswarn": "Ha a művelet SQL-hibákkal leáll, a wikid által használt adatbázis-felhasználó (amit a LocalSettings.php-ben adtál meg) valószínűleg nem rendelkezik a megfelelő jogosultságokkal.\nAdj a felhasználó számára táblák készítéséhez és törléséhez jogosultságot, ideiglenesen add meg a root fiók adatait, vagy használd az <code>setupStore.php</code> karbantartó parancsfájlt, ami egy adminisztrátor adataival lép be.",
+ "smw-admin-dbbutton": "Táblák inicializálása vagy frissítése",
+ "smw-admin-announce": "Wiki bejelentése",
+ "smw_smwadmin_datarefresh": "Adatok javítása és aktualizálása",
+ "smw_smwadmin_datarefreshdocu": "A wiki jelenlegi tartalma alapján lehetőség van az összes Szemantikus MediaWiki-adat helyreállítására.\nEz hasznos lehet a sérült adatok javításakor, vagy az adatok frissítésekor, ha a belső formátum megváltozott szoftverfrissítés miatt.\nA frissítés oldalról oldalra van végrehajtva, és nem lesz azonnal kész.\nAlább látható, hogy jelenleg folyamatban van-e ilyen frissítés, és elindíthatod vagy leállíthatod a frissítéseket (kivéve, ha az oldal adminisztrátora letiltotta ezt a lehetőséget).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Frissítés folyamatban.</strong>\nNormális, ha a frissítés lassan halad, hiszen az adatok csak egy kis részét frissíti minden alkalommal, ha egy felhasználó hozzáfér a wikihez.\nHa gyorsabban be szeretnéd fejezni a frissítést, hívd meg a <code>runJobs.php</code> karbantartó parancsfájlt (használd a <code>--maxjobs 1000</code> kapcsolót az egy menetben végrehajtott frissítések korlátozásához).\nA jelenlegi frissítés becsült előrehaladása:",
+ "smw_smwadmin_datarefreshbutton": "Adatok frissítésének ütemzése",
+ "smw_smwadmin_datarefreshstop": "Frissítés megszakítása",
+ "smw_smwadmin_datarefreshstopconfirm": "Igen, {{GENDER:$1|biztos}} vagyok benne.",
+ "smw-admin-support": "Segítséget kérek",
+ "smw-admin-supportdocu": "Különböző források, amelyek segíthetnek problémák esetén:",
+ "smw-admin-installfile": "Ha problémákat észlelsz a telepítéssel, ellenőrizd az útmutatót az <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL fájlban</a> és a <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">telepítési oldalon</a>!",
+ "smw-admin-smwhomepage": "A Szemantikus MediaWiki teljes felhasználói dokumentációja a <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b> címen található.",
+ "smw-admin-bugsreport": "A hibákat a <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHubon</a> lehet jelenteni.",
+ "smw-admin-questions": "Ha további kérdéseid vagy javaslataid vannak, csatlakozz a <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Szemantikus MediaWiki felhasználói fórumán</a> folyó beszélgetéshez.",
+ "smw_adminlinks_datastructure": "Adatstruktúra",
+ "smw_adminlinks_displayingdata": "Adatok megjelenítése",
+ "smw_adminlinks_inlinequerieshelp": "Szövegbeli lekérdezés-súgó",
+ "smw-createproperty-isproperty": "Ez egy „$1†típusú tulajdonság.",
+ "smw-createproperty-allowedvals": "A tulajdonság a következő {{PLURAL:$1|értéket|értékeket}} veheti fel:",
+ "smw-paramdesc-category-delim": "A határolójel",
+ "smw-paramdesc-category-template": "Az elemeket formázandó sablon",
+ "smw-info-par-message": "Megjelenítendő üzenet.",
+ "smw-info-par-icon": "Megjelenítendő jel, \"info\" vagy \"warning.\"",
+ "prefs-smw": "Szemantikus MediaWiki",
+ "prefs-ask-options": "Szemantikus keresési beállítások",
+ "smw-prefs-ask-options-tooltip-display": "Paraméterszöveg megjelenítése információs buborék formájában",
+ "smw-ui-tooltip-title-property": "Tulajdonság",
+ "smw-ui-tooltip-title-quantity": "Mértékegység-átváltás",
+ "smw-ui-tooltip-title-info": "Információ",
+ "smw-ui-tooltip-title-warning": "Hiba",
+ "smw-ui-tooltip-title-parameter": "Paraméter",
+ "smw-ui-tooltip-title-event": "Esemény",
+ "smw-ui-tooltip-title-note": "Megjegyzés",
+ "smw-ui-tooltip-title-legend": "Jelmagyarázat",
+ "smw_unknowntype": "A tulajdonság típusa érvénytelen",
+ "smw_concept_header": "A(z) „$1†koncepcióhoz tartozó lapok",
+ "smw_conceptarticlecount": "Alább $1 lap látszik.",
+ "group-smwadministrator": "Adminisztrátorok (Szemantikus MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Adminisztrátorok (Szemantikus MediaWiki)",
+ "smw-sp-properties-header-label": "Tulajdonságok listája",
+ "smw-admin-idlookup-input": "Keresés:",
+ "smw-admin-objectid": "Azonosító:",
+ "smw-livepreview-loading": "Betöltés…",
+ "smw-datavalue-allows-value-list-missing-marker": "A(z) „$1†lista tartalmában nincsenek elemek * listajelzővel."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/hy.json b/www/wiki/extensions/SemanticMediaWiki/i18n/hy.json
new file mode 100644
index 00000000..79fa9aba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/hy.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xelgen",
+ "GrigorGB"
+ ]
+ },
+ "browse": "Ô¿Õ¡Õ¿Õ¥Õ£Õ¸Ö€Õ«Õ¡Õ¶Õ¥Ö€",
+ "smw_smwadmin_permissionswarn": "ÔµÕ©Õ¥ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¨Õ¶Õ©Õ¡Õ¶Õ¸Ö‚Õ´ Õ§ SQL Õ½Õ­Õ¡Õ¬Õ¶Õ¥Ö€Õ¸Õ¾, Õ°Õ¡Õ¾Õ¡Õ¶Õ¡Õ¢Õ¡Ö€ Õ¿Õ¾ÕµÕ¡Õ¬ Õ´Õ¡Õ½Õ¶Õ¡Õ¯Õ«ÖÕ¨ Õ¹Õ¸Ö‚Õ¶Õ« Õ¢Õ¡Õ¾Õ¡Ö€Õ¡Ö€ Õ©Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¢Õ¡Õ¦Õ¡ÕµÕ« Õ°Õ¡Õ´Õ¡Ö€Ö‰ ÕÕ¿Õ¸Ö‚Õ£Õ¥Ö„ ÕÕ¥Ö€ LocalSettings.php Õ§Õ»Õ¨Ö‰\nÔ±ÕµÕ¬Õ¡ÕºÕ¥Õ½ Õ´Õ¡Õ½Õ¶Õ¡Õ¯ÖÕ«Õ¶ Õ¿Õ¾Õ¥Ö„ Õ°Õ¡Õ¾Õ¥Õ¬ÕµÕ¡Õ¬ Õ©Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬Õ¸Ö‚ Ö‡ Õ»Õ¶Õ»Õ¥Õ¬Õ¸Ö‚ Õ¡Õ²ÕµÕ¸Ö‚Õ½Õ¡Õ¯Õ¶Õ¥Ö€, ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯Õ¡Õ¾Õ¸Ö€Õ¡ÕºÕ¥Õ½ LocalSettings.php Õ§Õ»Õ¸Ö‚Õ´ Õ´Õ¸Ö‚Õ¿Ö„ Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¢Õ¡Õ¦Õ¡ÕµÕ« Õ°Õ«Õ´Ö„ Õ¯Õ¡Õ´ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ½ÕºÕ¡Õ½Õ¡Ö€Õ¯Õ´Õ¡Õ¶ <code>setupStore.php</code> Õ½Õ¯Ö€Õ«ÕºÕ¿, Õ¸Ö€Õ¨ Õ¯Õ¡Ö€Õ¸Õ² Õ§ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Ö€Õ®Õ¥Õ¬ Õ¡Õ¤Õ´Õ«Õ¶Õ«Õ½Õ¿Ö€Õ¡Õ¿Õ¸Ö€Õ« Õ«Ö€Õ¡Õ¾Õ¸Ö‚Õ¶Ö„Õ¶Õ¥Ö€.",
+ "smw-livepreview-loading": "Բեռնվում է…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ia.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ia.json
new file mode 100644
index 00000000..ee212624
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ia.json
@@ -0,0 +1,299 @@
+{
+ "@metadata": {
+ "authors": [
+ "McDutchie",
+ "ì•„ë¼",
+ "Fanjiayi"
+ ]
+ },
+ "smw-desc": "Pro render tu wiki plus accessibile – a machinas '''e''' a humanos ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentation in linea])",
+ "smw-title": "MediaWiki semantic",
+ "smw-semantics-not-enabled": "Le functionalitate de MediaWiki semantic non ha essite activate pro iste wiki.",
+ "smw_viewasrdf": "Syndication RDF",
+ "smw_finallistconjunct": ", e",
+ "smw-factbox-head": "... plus sur \"$1\"",
+ "smw-factbox-facts": "Factos",
+ "smw-factbox-facts-help": "Monstra declarationes e factos que ha essite create per un usator",
+ "smw-factbox-facts-derived": "Factos derivate",
+ "smw-factbox-facts-derived-help": "Monstra factos que ha essite derivate ab regulas o con le adjuta de altere technicas de rationamento",
+ "smw_isspecprop": "Iste proprietate es special in iste wiki.",
+ "smw-concept-cache-header": "Uso del cache",
+ "smw-concept-cache-count": "Le [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count cache de conceptos] contine {{PLURAL:$1|'''un''' entitate|'''$1''' entitates}} ($2).",
+ "smw-concept-no-cache": "Nulle cache disponibile.",
+ "smw_concept_description": "Description del concepto \"$1\"",
+ "smw_no_concept_namespace": "Le conceptos pote solmente esser definite in paginas in le spatio de nomines Concept:.",
+ "smw_multiple_concepts": "Cata pagina de concepto pote haber un sol definition de concepto.",
+ "smw_concept_cache_miss": "Le concepto \"$1\" non pote esser usate al momento, post que le configuration del wiki require que illo sia computate foras de linea. Si le problema non dispare post alcun tempore, demanda al administrator de tu sito de render disponibile iste concepto.",
+ "smw_noinvannot": "Le valores non pote esser assignate al proprietates inverse.",
+ "version-semantic": "Extensiones semantic",
+ "smw_baduri": "Le adresses URI del forma \"$1\" non es permittite.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Contar resultatos",
+ "smw_printername_csv": "Exportation in CSV",
+ "smw_printername_dsv": "Exportation in DSV",
+ "smw_printername_debug": "Consulta de debugging (pro expertos)",
+ "smw_printername_embedded": "Incastrar contento de pagina",
+ "smw_printername_json": "Exportation in JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Enumeration",
+ "smw_printername_ul": "Lista detaliate",
+ "smw_printername_table": "Tabella",
+ "smw_printername_broadtable": "Tabella large",
+ "smw_printername_template": "Patrono",
+ "smw_printername_rdf": "Exportation in RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "Le numero maxime de resultatos a retornar",
+ "smw-paramdesc-offset": "Le position del prime resultato",
+ "smw-paramdesc-headers": "Monstrar le capites/nomines de proprietate",
+ "smw-paramdesc-mainlabel": "Le etiquetta a dar al nomine del pagina principal",
+ "smw-paramdesc-link": "Monstrar valores como ligamines",
+ "smw-paramdesc-intro": "Le texto a monstrar ante le resultatos del consulta, si existe",
+ "smw-paramdesc-outro": "Le texto a monstrar post le resultatos del consulta, si existe",
+ "smw-paramdesc-default": "Le texto a monstrar si le consulta non produce resultatos",
+ "smw-paramdesc-sep": "Le separator inter resultatos",
+ "smw-paramdesc-showsep": "Monstrar separator al initio del file CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "In loco de presentar tote le valores, contar lor occurrentias, e monstrar istes.",
+ "smw-paramdesc-distributionsort": "Ordinar le distribution de valores per numero de occurrentias.",
+ "smw-paramdesc-distributionlimit": "Limitar le distribution de valores al numero de solmente alcun valores.",
+ "smw-paramdesc-aggregation": "Specifica a que le aggregation debe esser associate",
+ "smw-paramdesc-template": "Le nomine de un patrono con le qual presentar le impressiones",
+ "smw-paramdesc-columns": "Le numero de columnas in le quales presentar resultatos",
+ "smw-paramdesc-userparam": "Un valor passate a cata appello de patrono, si un patrono es usate",
+ "smw-paramdesc-introtemplate": "Le nomine de un patrono a monstrar ante le resultatos del consulta, si existe",
+ "smw-paramdesc-outrotemplate": "Le nomine de un patrono a monstrar post le resultatos del consulta, si existe",
+ "smw-paramdesc-embedformat": "Le etiquetta HTML usate pro definir capites",
+ "smw-paramdesc-embedonly": "Non monstrar capites",
+ "smw-paramdesc-table-class": "Un classe CSS additional a definir pro le tabella",
+ "smw-paramdesc-table-transpose": "Monstrar le capites del tabella verticalmente e le resultatos horizontalmente",
+ "smw-paramdesc-rdfsyntax": "Le syntaxe RDF a usar",
+ "smw-paramdesc-csv-sep": "Specifica un separator de columnas",
+ "smw-paramdesc-csv-valuesep": "Specifica un separator de valores",
+ "smw-paramdesc-csv-merge": "Fusionar le valores de lineas e columnas con un identificator de subjecto identic (i.e. le prime columna)",
+ "smw-paramdesc-csv-bom": "Adder un BOM (character que Marca le Ordine del Bytes) al initio del file producite",
+ "smw-paramdesc-dsv-separator": "Le separator a usar",
+ "smw-paramdesc-dsv-filename": "Le nomine del file DSV",
+ "smw-paramdesc-filename": "Le nomine del file producite",
+ "smw-smwdoc-description": "Presenta un tabella de tote le parametros que pote esser usate pro le formato de resultatos specificate, insimul con lor valores predefinite e lor descriptiones.",
+ "smw-smwdoc-default-no-parameter-list": "Iste formato de resultato non forni parametros specific al formato.",
+ "smw-smwdoc-par-format": "Le formato de resultatos pro le qual presentar le documentation de parametros.",
+ "smw-smwdoc-par-parameters": "Le parametros a monstrar: \"specific\" pro illos addite per le formato, \"base\" pro illos disponibile in tote le formatos, e \"all\" pro ambes.",
+ "smw-paramdesc-sort": "Proprietate per le qual ordinar le consulta",
+ "smw-paramdesc-order": "Ordination del consulta",
+ "smw-paramdesc-searchlabel": "Texto pro continuar le recerca",
+ "smw-paramdesc-named_args": "Nominar le parametros passate al patrono",
+ "smw-paramdesc-template-arguments": "Defini le maniera in que le argumentos nominate es passate al patrono",
+ "smw-paramdesc-import-annotation": "Altere datos annotate debe esser copiate durante le analyse syntactic del subjecto",
+ "smw-paramdesc-export": "Option de exportation",
+ "smw-paramdesc-prettyprint": "Imprimer in disposition plus nette con additional indentationes e saltos de linea",
+ "smw-paramdesc-json-unescape": "Le output continera barras oblique non escappate e characteres Unicode multibyte",
+ "smw-paramdesc-json-type": "Typo de serialisation",
+ "smw-paramdesc-source": "Fonte alternative de consultas",
+ "smw-paramdesc-jsonsyntax": "Syntaxe JSON a usar",
+ "smw-printername-feed": "Syndication RSS e Atom",
+ "smw-paramdesc-feedtype": "Typo de syndication",
+ "smw-paramdesc-feedtitle": "Le texto a usar como titulo del syndication",
+ "smw-paramdesc-feeddescription": "Le texto a usar como description del syndication",
+ "smw-paramdesc-feedpagecontent": "Le contento del pagina a monstrar con le syndication",
+ "smw-label-feed-description": "Syndication $2 $1",
+ "smw-paramdesc-mimetype": "Le typo de multimedia (typo MIME) pro le file resultante",
+ "smw_iq_disabled": "Le consultas semantic ha essite disactivate pro iste wiki.",
+ "smw_iq_moreresults": "… ulterior resultatos",
+ "smw_parseerror": "Le valor date non esseva comprendite.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "\"$1\" non pote esser usate como nomine de pagina in iste wiki.",
+ "smw_noproperty": "\"$1\" non pote esser usate como nomine de proprietate in iste wiki.",
+ "smw_wrong_namespace": "Solmente le paginas in le spatio de nomines \"$1\" es permittite hic.",
+ "smw_manytypes": "Plus de un typo definite pro proprietate.",
+ "smw_emptystring": "Series de characteres vacue non es acceptate.",
+ "smw_notinenum": "\"$1\" non es in le lista de [[Property:Allows value|valores permittite]] ($2) pro le proprietate \"$3\".",
+ "smw_noboolean": "\"$1\" non es recognoscite como un valor boolean (ver/false).",
+ "smw_true_words": "ver,v,si,s",
+ "smw_false_words": "false,f,no,n",
+ "smw_nofloat": "\"$1\" non es un numero.",
+ "smw_infinite": "Le numeros tanto grande como \"$1\" non es supportate.",
+ "smw_unitnotallowed": "\"$1\" non es declarate como unitate de mesura valide pro iste proprietate.",
+ "smw_nounitsdeclared": "Nulle unitate de mesura ha essite declarate pro iste proprietate.",
+ "smw_novalues": "Nulle valor specificate.",
+ "smw_nodatetime": "Le data \"$1\" non esseva comprendite.",
+ "smw_toomanyclosing": "Il pare haber troppo de occurrentias de \"$1\" in le consulta.",
+ "smw_noclosingbrackets": "Alcun uso de \"<nowiki>[[</nowiki>\" in tu consulta non esseva claudite per un correspondente \"]]\".",
+ "smw_misplacedsymbol": "Le symbolo \"$1\" esseva usate in un loco ubi illo non es utile.",
+ "smw_unexpectedpart": "Le parte \"$1\" del consulta non esseva comprendite.\nLe resultatos pote non esser como expectate.",
+ "smw_emptysubquery": "Alcun subconsulta ha nulle condition valide.",
+ "smw_misplacedsubquery": "Alcun subconsulta esseva usate in un loco ubi nulle subconsultas es permittite.",
+ "smw_valuesubquery": "Le subconsultas non es supportate pro valores del proprietate \"$1\".",
+ "smw_badqueryatom": "Alcun parte \"<nowiki>[[…]]</nowiki>\" del consulta non esseva comprendite.",
+ "smw_propvalueproblem": "Le valor del proprietate \"$1\" non esseva comprendite.",
+ "smw_noqueryfeature": "Alcun functionalitate de consulta non esseva supportate in iste wiki; un parte del consulta ha essite omittite ($1).",
+ "smw_noconjunctions": "Le conjunctiones in consultas non es supportate in iste wiki; un parte del consulta ha essite ommittite ($1).",
+ "smw_nodisjunctions": "Le disjunctiones in consultas non es supportate in iste wiki; un parte del consulta ha essite omittite ($1).",
+ "smw_querytoolarge": "Le sequente {{PLURAL:$2|condition|conditiones}} de consulta non poteva esser considerate a causa del restrictiones de iste wiki concernente le grandor o profunditate del consultas: <code>$1</code>.",
+ "smw_notemplategiven": "Forni un valor pro le parametro \"template\" pro facer functionar iste formato de consulta.",
+ "smw_db_sparqlqueryproblem": "Le resultato del consulta non poteva esser obtenite del base de datos SPARQL. Iste error pote esser temporari o pote indicar un defecto in le software del base de datos.",
+ "smw_db_sparqlqueryincomplete": "Responder al consulta provava troppo difficile e ha essite abortate. Alcun resultatos pote mancar. Si possibile, tenta usar un consulta plus simple.",
+ "smw_type_header": "Proprietates del typo \"$1\"",
+ "smw_typearticlecount": "Presentation de $1 {{PLURAL:$1|proprietate|proprietates}} que usa iste typo.",
+ "smw_attribute_header": "Paginas que usa le proprietate \"$1\"",
+ "smw_attributearticlecount": "Presentation de $1 {{PLURAL:$1|pagina|paginas}} que usa iste proprietate.",
+ "smw-propertylist-subproperty-header": "Subproprietates",
+ "smw-propertylist-redirect-header": "Synonymos",
+ "smw-propertylist-error-header": "Paginas con assignationes improprie",
+ "smw-propertylist-count": "Es monstrate $1 {{PLURAL:$1|entitate|entitates}} associate.",
+ "smw-propertylist-count-with-restricted-note": "Es monstrate $1 {{PLURAL:$1|entitate|entitates}} associate (il ha plus, ma le presentation es limitate a \"$2\").",
+ "smw-propertylist-count-more-available": "Es monstrate $1 {{PLURAL:$1|entitate|entitates}} associate (il ha alteres disponibile).",
+ "specialpages-group-smw_group": "MediaWiki semantic",
+ "exportrdf": "Exportar paginas verso RDF",
+ "smw_exportrdf_docu": "Iste pagina permitte obtener le datos de un pagina in formato RDF.\nPro exportar paginas, entra le titulos in le quadro de texto in basso, un titulo per linea.",
+ "smw_exportrdf_recursive": "Exportar recursivemente tote le paginas connexe.\nNota que le resultato pote esser grande!",
+ "smw_exportrdf_backlinks": "Exportar equalmente tote le paginas con referentias al paginas exportate.\nGenera un RDF navigabile.",
+ "smw_exportrdf_lastdate": "Non exportar paginas que non esseva cambiate post le momento specificate.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Resolvitor de URI",
+ "properties": "Proprietates",
+ "smw_properties_docu": "Le sequente proprietates es usate in le wiki.",
+ "smw_property_template": "$1 del typo $2 ($3 {{PLURAL:$3|uso|usos}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Tote le proprietates debe esser describite per un pagina!",
+ "smw_propertylackstype": "Nulle typo esseva specificate pro iste proprietate (es assumite le typo $1 pro le momento).",
+ "smw_propertyhardlyused": "Iste proprietate es a pena usate in iste wiki!",
+ "smw-property-name-invalid": "Le proprietate $1 non pote esser usate (nomine de proprietate invalide).",
+ "smw-property-name-reserved": "\"$1\" es listate como nomine reservate e non debe esser usate como proprietate. Iste [https://www.semantic-mediawiki.org/wiki/Help:Property_naming pagina de adjuta] pote continer information explicante proque iste nomine ha essite reservate.",
+ "smw-sp-property-searchform": "Monstrar proprietates que contine:",
+ "smw-sp-property-searchform-inputinfo": "Le entrata distingue inter majusculas e minusculas. Quando usate pro filtration, solmente le proprietates que corresponde al condition es monstrate.",
+ "smw-special-property-searchform": "Monstrar proprietates que contine:",
+ "smw-special-property-searchform-inputinfo": "Le entrata distingue inter majusculas e minusculas. Quando usate pro filtration, solmente le proprietates que corresponde al condition es monstrate.",
+ "smw-special-property-searchform-options": "Optiones",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Nulle",
+ "smw-special-wantedproperties-filter-unapproved": "Non approbate",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Option de filtrage usate in connexion con le modo de autoritate.",
+ "concepts": "Conceptos",
+ "smw-special-concept-docu": "Un [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] pote esser reguardate como \"categoria dynamic\", i.e. como collection de paginas que non es create manualmente, ma que es computate per Semantic MediaWiki a partir de un description de un consulta fornite.",
+ "smw-special-concept-header": "Lista de conceptos",
+ "smw-special-concept-count": "Le sequente {{PLURAL:$1|concepto|$1 conceptos}} es listate.",
+ "smw-special-concept-empty": "Nulle concepto trovate.",
+ "unusedproperties": "Proprietates non usate",
+ "smw-unusedproperties-docu": "Iste pagina lista le [https://www.semantic-mediawiki.org/wiki/Unused_properties proprietates non usate] que es declarate ben que nulle altere pagina los utilisa. Pro un vista differentiate, vide le paginas [[Special:Properties|tote le proprietates]] o [[Special:WantedProperties|proprietates desirate]].",
+ "smw-unusedproperty-template": "$1 del typo $2",
+ "wantedproperties": "Proprietates desirate",
+ "smw-wantedproperties-docu": "Iste pagina lista le [https://www.semantic-mediawiki.org/wiki/Wanted_properties proprietates desirate] que es usate in le wiki ma que non ha un pagina que los describe. Pro un vista differentiate, vide le paginas [[Special:Properties|tote le proprietates]] o [[Special:UnusedProperties|proprietates non usate]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw-special-wantedproperties-docu": "Iste pagina lista le [https://www.semantic-mediawiki.org/wiki/Wanted_properties proprietates desirate] que es usate in le wiki ma non ha un pagina que los describe. Pro un vista differentiate, vide le paginas special listante [[Special:Properties|tote le proprietates]] o le [[Special:UnusedProperties|proprietates non usate]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw_purge": "Refrescar",
+ "smw-purge-failed": "Refrescamento fallite",
+ "types": "Typos",
+ "smw_types_docu": "Lista del [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes typos de datos disponibile] in que cata [https://www.semantic-mediawiki.org/wiki/Help:Datatype typo] representa un insimul unic de attributos pro describer un valor in terminos de characteristicas de immagazinage e visualisation que se heredita in un proprietate assignate.",
+ "smw-special-types-no-such-type": "\"$1\" es incognite o non ha essite specificate como typo de datos valide.",
+ "smw-statistics": "Statisticas semantic",
+ "smw_uri_doc": "Le resolvitor de URL implementa le [$1 conclusion del Gruppo de Architectura Technic del W3C a proposito de httpRange-14].\nIllo assecura que le humanos non se transforma in sitos web.",
+ "ask": "Recerca semantic",
+ "smw_ask_sortby": "Ordinar per columna (optional)",
+ "smw_ask_ascorder": "Ascendente",
+ "smw_ask_descorder": "Descendente",
+ "smw-ask-order-rand": "Aleatori",
+ "smw_ask_submit": "Cercar resultatos",
+ "smw_ask_editquery": "Modificar consulta",
+ "smw_add_sortcondition": "[Adder condition de ordinamento]",
+ "smw_ask_hidequery": "Celar consulta (vista compacte)",
+ "smw_ask_help": "Adjuta super le consultas",
+ "smw_ask_queryhead": "Condition",
+ "smw_ask_printhead": "Selection de datos a monstrar",
+ "smw_ask_printdesc": "(adde un nomine de proprietate per linea)",
+ "smw_ask_format_as": "Formatar in:",
+ "smw_ask_defaultformat": "predefinition",
+ "smw_ask_otheroptions": "Altere optiones",
+ "smw_ask_show_embed": "Revelar codice de incastrar",
+ "smw_ask_hide_embed": "Celar codice de incastrar",
+ "smw_ask_embed_instr": "Pro incastrar iste consulta in linea in un pagina wiki usa le codice sequente.",
+ "smw-ask-delete": "Remover",
+ "smw-ask-options": "Optiones",
+ "smw-ask-parameters": "Parametros",
+ "smw-ask-search": "Recerca",
+ "smw-ask-debug": "Analysar problema",
+ "smw-ask-result": "Resultato",
+ "smw-ask-format": "Formato",
+ "searchbyproperty": "Cercar per proprietate",
+ "smw_sbv_docu": "Cercar tote le paginas que ha un proprietate e valor date.",
+ "smw_sbv_novalue": "Entra un valor valide pro le proprietate, o vide tote le valores possibile del proprietate \"$1\".",
+ "smw_sbv_displayresult": "Un lista de tote le paginas que ha le proprietate \"$1\" con valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Un lista de tote le paginas que ha le proprietate \"$1\" con valor \"$2\".\nPost que il ha pauc resultatos, le valores proxime es equalmente monstrate.",
+ "smw_sbv_property": "Proprietate:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Cercar resultatos",
+ "browse": "Percurrer le wiki",
+ "smw_browselink": "Percurrer proprietates",
+ "smw_browse_article": "Entra le nomine del pagina ab le qual tu vole initiar le exploration.",
+ "smw_browse_go": "Va",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "Monstrar proprietates entrante",
+ "smw_browse_hide_incoming": "Celar proprietates entrante",
+ "smw_browse_no_outgoing": "Iste pagina non ha proprietates.",
+ "smw_browse_no_incoming": "Nulle proprietate ha un ligamine a iste pagina.",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiquetta de proprietate inverse",
+ "pageproperty": "Recerca de proprietates de paginas",
+ "smw_pp_docu": "Entra un pagina e un proprietate, o entra solmente un proprietate pro obtener tote le valores assignate.",
+ "smw_pp_from": "Del pagina:",
+ "smw_pp_type": "Proprietate:",
+ "smw_pp_submit": "Cercar resultatos",
+ "smw_result_prev": "Precedente",
+ "smw_result_next": "Sequente",
+ "smw_result_results": "Resultatos",
+ "smw_result_noresults": "Nulle resultato.",
+ "smwadmin": "Functiones administrative e de mantenentia",
+ "smw-admin-setupsuccess": "Le motor de immagazinage ha essite configurate.",
+ "smw_smwadmin_return": "Retornar a $1",
+ "smw_smwadmin_updatestarted": "Un nove processo de actualisation pro le refrescamento del datos semantic ha essite comenciate.\nTote le datos immagazinate essera reconstruite o reparate ubi necessari.\nTu pote sequer le progresso del actualisation in iste pagina special.",
+ "smw_smwadmin_updatenotstarted": "Il ha jam un processo de actualisation in curso.\nNon es possibile crear un altere.",
+ "smw_smwadmin_updatestopped": "Tote le processos de actualisation existente ha essite stoppate.",
+ "smw_smwadmin_updatenotstopped": "Pro stoppar le processo de actualisation in curso, tu debe marcar le quadrato pro indicar que tu es absolutemente secur de voler facer isto.",
+ "smw-admin-docu": "Iste pagina special te adjuta durante le installation, actualisation, mantenentia e uso de <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> e forni anque altere functionalitate administrative assi como statisticas.\nNon oblida de facer un copia de reserva del datos importante ante de executar functiones administrative.",
+ "smw-admin-db": "Mantenentia del base de datos",
+ "smw-admin-dbdocu": "Semantic MediaWiki require certe extensiones pro le base de datos de MediaWiki pro poter immagazinar le datos semantic.\nLe function hic infra assecura que tu base de datos es installate correctemente.\nLe cambiamentos facite durante iste passo non afficera le resto del base de datos de MediaWiki, e pote esser facilemente disfacite si desirate.\nIste function de installation pote esser executate plure vices sin causar damno, ma es necessari solmente un vice post cata installation o actualisation.",
+ "smw-admin-permissionswarn": "Si le operation falle con errores SQL, le usator del base de datos que tu wiki usa (verifica tu file \"LocalSettings.php\") probabilemente non ha permissiones sufficiente.\nO concede a iste usator le permissiones additional de crear e deler tabellas, o insere temporarimente in le file \"LocalSettings.php\" le credentiales pro accesso \"root\" a tu base de datos, o usa le script de mantenentia <code>setupStore.php</code>, le qual pote usar le credentiales de un administrator.",
+ "smw-admin-dbbutton": "Initialisar o actualisar tabellas",
+ "smw-admin-announce": "Annunciar tu wiki",
+ "smw_smwadmin_datarefresh": "Reconstruction de datos",
+ "smw_smwadmin_datarefreshdocu": "Es possibile restaurar tote le datos de Semantic MediaWiki a base del conento actual del wiki.\nIsto pote esser utile pro reparar datos corrumpite o pro refrescar le datos si le formato interne ha cambiate a causa de alcun actualisation de software.\nLe actualisation es executate pagina a pagina e non essera completate immediatemente.\nLo sequente monstra si un actualisation es in progresso e permitte comenciar o stoppar le actualisationes (a minus que iste function ha essite disactivate per le administrator del sito).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Un actualisation es ja in curso.</strong>\nEs normal que le actualisation progrede lentemente post que illo refresca datos solmente in micre pecias cata vice que un usator accede al wiki.\nPro completar iste actualisation plus rapidemente, tu pote invocar le script de mantenentia de MediaWiki <code>runJobs.php</code> (usa le option <code>--maxjobs 1000</code> pro restringer le numero de actualisationes facite in un lot).\nProgresso estimate del actualisation currente:",
+ "smw_smwadmin_datarefreshbutton": "Planificar le reconstruction del datos",
+ "smw_smwadmin_datarefreshstop": "Stoppar iste actualisation",
+ "smw_smwadmin_datarefreshstopconfirm": "Si, io es {{GENDER:$1|secur}}.",
+ "smw-admin-support": "Obtener supporto",
+ "smw-admin-supportdocu": "Plure ressources es disponibile pro adjutar te in caso de problemas:",
+ "smw-admin-installfile": "Si tu incontra problemas con tu installation, comencia per verificar le directivas in le <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">file INSTALL</a> e le <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">pagina de installation</a>.",
+ "smw-admin-smwhomepage": "Le documentation de usator complete de Semantic MediaWiki se trova a <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Defectos pote esser reportate in le <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">traciator de problemas</a>. Le pagina <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">signalar problemas</a> forni un guida sur como scriber un reporto de problema efficiente.",
+ "smw-admin-questions": "Si tu ha altere questiones o suggestiones, participa al discussion sur Semantic MediaWiki in le <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">foro del usatores de Semantic MediaWiki</a> o in le <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">canal de chat</a>.",
+ "smw_adminlinks_datastructure": "Structura de datos",
+ "smw_adminlinks_displayingdata": "Monstrar datos",
+ "smw_adminlinks_inlinequerieshelp": "Adjuta super consultas incorporate",
+ "smw-createproperty-isproperty": "Es un proprietate del typo $1.",
+ "smw-createproperty-allowedvals": "Le {{PLURAL:$1|valor|valores}} permittite pro iste proprietate es:",
+ "smw-paramdesc-category-delim": "Le delimitator",
+ "smw-paramdesc-category-template": "Un patrono con le qual formatar le elementos",
+ "smw-paramdesc-category-userparam": "Un parametro a passar al patrono",
+ "smw-info-par-message": "Le message a monstrar.",
+ "smw-info-par-icon": "Le icone a monstrar, o \"info\" o \"attention!\".",
+ "prefs-smw": "MediaWiki semantic",
+ "prefs-general-options": "Optiones general",
+ "smw-ui-tooltip-title-property": "Proprietate",
+ "smw-ui-tooltip-title-info": "Information",
+ "smw-ui-tooltip-title-error": "Error",
+ "smw-ui-tooltip-title-parameter": "Parametro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-reference": "Referentia",
+ "smw_unknowntype": "Le typo \"$1\" de iste proprietate non es valide.",
+ "smw_concept_header": "Paginas del concepto \"$1\"",
+ "smw_conceptarticlecount": "Presentation de $1 {{PLURAL:$1|pagina|paginas}}.",
+ "smw-livepreview-loading": "Cargamento in curso…",
+ "smw-property-predefined-errc": "\"$1\" es un proprietate predefinite fornite per [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] que representa errores connexe a mal annotationes de valores o mal tractamento de entrata.",
+ "smw-property-predefined-long-errc": "Le errores se collige in un [https://www.semantic-mediawiki.org/wiki/Help:Container receptaculo] que pote includer un referentia al proprietate que ha causate le discrepantia.",
+ "smw-section-expand": "Displicar le section",
+ "smw-section-collapse": "Plicar le section"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/id.json b/www/wiki/extensions/SemanticMediaWiki/i18n/id.json
new file mode 100644
index 00000000..ffff7887
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/id.json
@@ -0,0 +1,214 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bennylin",
+ "Farras",
+ "Ilham151096",
+ "Irwangatot",
+ "IvanLanin",
+ "Naval Scene",
+ "Rex",
+ "පසිඳු කà·à·€à·’න්ද",
+ "ì•„ë¼",
+ "Arief",
+ "Rachmat.Wahidi",
+ "Presidenvolksraad",
+ "Rachmat04"
+ ]
+ },
+ "smw-desc": "Membuat wiki Anda lebih mudah diakses - oleh mesin ''dan'' manusia ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentasi daring])",
+ "smw_viewasrdf": "Umpan RDF",
+ "smw_finallistconjunct": ", dan",
+ "smw_isspecprop": "Sifat ini adalah sifat khusus di wiki ini.",
+ "smw_concept_description": "Deskripsi konsep \"$1\"",
+ "smw_no_concept_namespace": "Konsep hanya dapat didefinisikan pada halaman dalam ruang nama Konsep:.",
+ "smw_multiple_concepts": "Setiap halaman konsep hanya dapat memiliki satu konsep definisi.",
+ "smw_concept_cache_miss": "Konsep \"$1\" tidak dapat digunakan saat ini karena konfigurasi wiki membutuhkannya untuk dihitung secara luring.\nJika masalah ini tidak hilang setelah beberapa waktu, mintalah pengelola situs untuk membuat konsep ini tersedia.",
+ "smw_noinvannot": "Nilai tidak dapat diberikan pada properti inversi.",
+ "version-semantic": "Ekstensi semantik",
+ "smw_baduri": "URI berbentuk \"$1\" tidak diperbolehkan.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Hasil perhitungan",
+ "smw_printername_csv": "Expor CSV",
+ "smw_printername_dsv": "Ekspor DSV",
+ "smw_printername_debug": "Debug query (untuk ahli)",
+ "smw_printername_embedded": "Sertakan isi halaman",
+ "smw_printername_json": "Ekspor JSON",
+ "smw_printername_list": "Daftar",
+ "smw_printername_ol": "Enumerasi",
+ "smw_printername_ul": "Itemisasi",
+ "smw_printername_table": "Tabel",
+ "smw_printername_broadtable": "Tabel lebar",
+ "smw_printername_template": "Templat",
+ "smw_printername_rdf": "Ekspor RDF",
+ "smw_printername_category": "Kategori",
+ "validator-type-class-SMWParamSource": "teks",
+ "smw-paramdesc-limit": "Jumlah maksimum hasil yang diberikan",
+ "smw-paramdesc-headers": "Tampilkan nama kepala/properti",
+ "smw-paramdesc-mainlabel": "Label untuk nama halaman utama",
+ "smw-paramdesc-link": "Tampilkan nilai sebagai pranala",
+ "smw-paramdesc-intro": "Tulisan yang ditampilkan sebelum hasil kueri, jika ada",
+ "smw-paramdesc-outro": "Tulisan untuk ditampilkan setelah hasil kueri, jika ada",
+ "smw-paramdesc-default": "Tulisan untuk ditampilkan jika tidak ada hasil kueri",
+ "smw-paramdesc-sep": "Pemisah untuk nilai",
+ "smw-paramdesc-template": "Nama templat yang digunakan untuk menampilkan cetakan",
+ "smw-paramdesc-columns": "Jumlah kolom untuk menampilkan hasil (bawaannya adalah $1)",
+ "smw-paramdesc-userparam": "Sebuah nilai dimasukkan ke setiap pencarian templat, bila sebuah templat digunakan",
+ "smw-paramdesc-introtemplate": "Nama templat yang ditampilkan sebelum hasil pencarian, bila ada",
+ "smw-paramdesc-outrotemplate": "Nama templat yang ditampilkan setelah hasil pencarian, bila ada",
+ "smw-paramdesc-embedformat": "Tag HTML yang digunakan untuk menentukan judul",
+ "smw-paramdesc-embedonly": "Jangan tampilkan judul",
+ "smw-paramdesc-rdfsyntax": "Sintaks RDF untuk digunakan",
+ "smw-paramdesc-csv-sep": "Pemisah yang dipakai",
+ "smw-paramdesc-dsv-separator": "Pemisah yang dipakai",
+ "smw-paramdesc-dsv-filename": "Nama berkas DSV",
+ "smw-paramdesc-searchlabel": "Teks untuk melanjutkan pencarian",
+ "smw_iq_disabled": "Query semantik telah dinonaktifkan di wiki ini.",
+ "smw_iq_moreresults": "… hasil lebih lanjut",
+ "smw_parseerror": "Nilai yang diberikan tidak dipahami.",
+ "smw_notitle": "\"$1\" tidak dapat digunakan sebagai nama halaman di wiki ini.",
+ "smw_noproperty": "\"$1\" tidak dapat digunakan sebagai nama properti di wiki ini.",
+ "smw_wrong_namespace": "Hanya halaman pada ruang nama \"$1\" yang diizinkan di sini.",
+ "smw_manytypes": "Lebih dari satu tipe yang ditetapkan untuk properti.",
+ "smw_emptystring": "Untaian kosong tidak diterima.",
+ "smw_notinenum": "\"$1\" tidak ada dalam daftar ($2) dari [[Property:Allows value|nilai diizinkan]] untuk atribut \"$3\".",
+ "smw_noboolean": "\"$1\" tidak dikenali sebagai suatu nilai Boolean (true/false)",
+ "smw_true_words": "benar,t,ya,y",
+ "smw_false_words": "salah,f,tidak,n",
+ "smw_nofloat": "\"$1\" bukan angka.",
+ "smw_infinite": "Angka sebesar \"$1\" tidak didukung.",
+ "smw_unitnotallowed": "\"$1\" bukanlah satuan ukuran yang sah untuk properti ini.",
+ "smw_nounitsdeclared": "Tidak ada satuan pengukuran yang dinyatakan untuk properti ini.",
+ "smw_novalues": "Tidak ada nilai yang disebutkan.",
+ "smw_nodatetime": "Tanggal \"$1\" tidak dipahami.",
+ "smw_toomanyclosing": "Tampaknya ada terlalu banyak penyebutan \"$1\" pada query.",
+ "smw_noclosingbrackets": "Penggunaan beberapa \"<nowiki>[[</nowiki>\" pada query Anda tidak ditutup dengan pasangan \"]]\".",
+ "smw_misplacedsymbol": "Simbol \"$1\" digunakan pada tempat yang mubazir.",
+ "smw_unexpectedpart": "Bagian \"$1\" dari query tidak dipahami.\nHasilnya mungkin tidak seperti yang diharapkan.",
+ "smw_emptysubquery": "Beberapa subquery tidak memiliki kondisi yang valid.",
+ "smw_misplacedsubquery": "Beberapa subquery digunakan pada tempat yang tidak mengizinkan subquery.",
+ "smw_valuesubquery": "Subquery tidak didukung untuk nilai dari properti \"$1\".",
+ "smw_badqueryatom": "Beberapa bagian \"<nowiki>[[…]]</nowiki>\" pada query tidak dipahami.",
+ "smw_propvalueproblem": "Nilai properti \"$1\" tidak dipahami.",
+ "smw_noqueryfeature": "Beberapa fitur query belum didukung di wiki ini dan sebagian query diabaikan ($1).",
+ "smw_noconjunctions": "Konjungsi dalam query tidak didukung dalam wiki ini dan sebagian dari query diabaikan ($1).",
+ "smw_nodisjunctions": "Disjungsi dalam query tidak didukung dalam wiki ini dan sebagian dari query diabaikan ($1).",
+ "smw_querytoolarge": "Kondisi query berikut tidak dapat diproses karena pembatas wiki terhadap ukuran atau kedalaman query: $1.",
+ "smw_notemplategiven": "Berikan suatu nilai bagi parameter \"template\" agar format query ini dapat bekerja.",
+ "smw_db_sparqlqueryproblem": "Hasil kueri tidak dapat diambil dari basis data SPARQL. Galat ini mungkin sementara atau menandakan adanya bug dalam perangkat lunak basis data.",
+ "smw_type_header": "Properti bertipe \"$1\"",
+ "smw_typearticlecount": "Menampilkan $1 {{PLURAL:$1|properti|properti}} yang menggunakan tipe ini.",
+ "smw_attribute_header": "Halaman yang menggunakan properti \"$1\"",
+ "smw_attributearticlecount": "Menampilkan $1 {{PLURAL:$1|halaman|halaman}} yang menggunakan properti ini.",
+ "exportrdf": "Ekspor halaman ke RDF",
+ "smw_exportrdf_docu": "Halaman ini mengizinkan Anda untuk mendapatkan data dari suatu halaman dalam format RDF.\nUntuk mengekspor halaman, masukkan judul di kotak teks di bawah, satu judul per baris.",
+ "smw_exportrdf_recursive": "Ekspor semua halaman yang terkait secara rekursif.\nPerhatikan bahwa hasilnya bisa besar!",
+ "smw_exportrdf_backlinks": "Ekspor juga semua halaman yang merujuk ke halaman yang diekspor.\nMenghasilkan RDF yang dapat ditelurusi.",
+ "smw_exportrdf_lastdate": "Jangan mengekspor halaman yang tidak berubah sejak waktu yang diberikan.",
+ "smw_exportrdf_submit": "Ekspor",
+ "uriresolver": "Pengurai URI",
+ "properties": "Properti",
+ "smw_properties_docu": "Properti berikut digunakan dalam wiki ini.",
+ "smw_property_template": "$1 dari tipe $2 ($3 {{PLURAL:$3|digunakan|digunakan}})",
+ "smw_propertylackspage": "Semua properti harus dideskripsikan dengan suatu halaman!",
+ "smw_propertylackstype": "Tidak ada tipe yang dispesifikasikan untuk properti ini (untu saat ini diasumsikan bertipe $1)",
+ "smw_propertyhardlyused": "Properti ini hampir tak digunakan di wiki ini!",
+ "unusedproperties": "Properti yang tak digunakan",
+ "smw-unusedproperties-docu": "Properti berikut ada walaupun tidak ada halaman lain yang memanfaatkan mereka.",
+ "smw-unusedproperty-template": "$1 bertipe $2",
+ "wantedproperties": "Properti yang diinginkan",
+ "smw-wantedproperties-docu": "Properti berikut digunakan pada wiki ini tetapi belum memiliki sebuah halaman untuk mendeskripsikannya.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|penggunaan|penggunaan}})",
+ "smw_purge": "Segarkan",
+ "types": "Tipe",
+ "smw_types_docu": "Berikut ini adalah daftar semua jenis data yang dapat ditetapkan ke properti.",
+ "smw_uri_doc": "Pengurai URI menerapkan [$1 W3C pencarian TAG pada httpRange-14].\nIni memastikan bahwa manusia tidak berubah menjadi situs web.",
+ "ask": "Pencarian semantik",
+ "smw_ask_sortby": "Urut berdasar kolom (pilihan)",
+ "smw_ask_ascorder": "Menanjak",
+ "smw_ask_descorder": "Menurun",
+ "smw_ask_submit": "Temukan hasil",
+ "smw_ask_editquery": "Sunting query",
+ "smw_add_sortcondition": "[Tambahkan kondisi pengurutan]",
+ "smw_ask_hidequery": "Sembunyikan query",
+ "smw_ask_help": "Bantuan query",
+ "smw_ask_queryhead": "Query",
+ "smw_ask_printhead": "Data tambahan untuk ditampilkan",
+ "smw_ask_printdesc": "(tambahkan satu nama properti per baris)",
+ "smw_ask_format_as": "Format sebagai:",
+ "smw_ask_defaultformat": "baku",
+ "smw_ask_otheroptions": "Pilihan lain",
+ "smw_ask_show_embed": "Tampilkan kode sertaan",
+ "smw_ask_hide_embed": "Sembunyikan kode sertaan",
+ "smw_ask_embed_instr": "Untuk menyertakan query ini secara tersisip dalam suatu halaman wiki gunakan kode di bawah.",
+ "searchbyproperty": "Pencarian menurut properti",
+ "smw_sbv_docu": "Cari semua halaman yang memiliki properti dan nilai yang diberikan.",
+ "smw_sbv_novalue": "Masukkan nilai yang valid untuk properti, atau lihat semua nilai properti untuk \"$1\".",
+ "smw_sbv_displayresult": "Daftar semua halaman yang memiliki properti \"$1\" dengan nilai \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Daftar semua halaman yang memiliki properti \"$1\" dengan nilai \"$2\".\nKarena hanya ada sedikit hasil, nilai yang mendekati juga ditampilkan.",
+ "smw_sbv_property": "Properti:",
+ "smw_sbv_value": "Nilai:",
+ "smw_sbv_submit": "Temukan hasil",
+ "browse": "Jelajahi wiki",
+ "smw_browselink": "Jelajahi properti",
+ "smw_browse_article": "Masukkan nama halaman untuk memulai penelusuran",
+ "smw_browse_go": "Tuju ke",
+ "smw_browse_show_incoming": "tampilkan properti yang bertaut ke sini",
+ "smw_browse_hide_incoming": "sembunyikan properti yang bertaut ke sini",
+ "smw_browse_no_outgoing": "Halaman ini tidak memiliki properti.",
+ "smw_browse_no_incoming": "Tidak ada properti yang bertaut ke halaman ini.",
+ "smw_inverse_label_default": "$1 dari",
+ "smw_inverse_label_property": "Label properti inversi",
+ "pageproperty": "Pencarian properti halaman",
+ "smw_pp_docu": "Pencarian semua nilai suatu properti pada suatu halaman.\nMasukkan satu halaman dan satu properti.",
+ "smw_pp_from": "Dari halaman",
+ "smw_pp_type": "Properti",
+ "smw_pp_submit": "Temukan hasil",
+ "smw_result_prev": "Sebelumnya",
+ "smw_result_next": "Selanjutnya",
+ "smw_result_results": "Hasil",
+ "smw_result_noresults": "Tidak ada hasilnya.",
+ "smwadmin": "Fungsi administratif",
+ "smw-admin-setupsuccess": "Mesin penyimpanan telah disiapkan dengan sukses.",
+ "smw_smwadmin_return": "Kembali ke $1",
+ "smw_smwadmin_updatestarted": "Proses pemutakhiran baru untuk menyegarkan data semantik dimulai.\nSemua data yang tersimpan akan dibangun kembali atau diperbaiki ketika diperlukan.\nAnda dapat mengikuti kemajuan pembaruan pada halaman khusus ini.\n\nKembali ke $1.",
+ "smw_smwadmin_updatenotstarted": "Sudah ada suatu proses pembaruan yang sedang berjalan.\nJangan membuat proses lain.",
+ "smw_smwadmin_updatestopped": "Semua proses pembaruan yang ada telah dihentikan.",
+ "smw_smwadmin_updatenotstopped": "Untuk menghentikan proses pembaruan yang sedang berjalan, Anda harus memilih kotak centang untuk menunjukkan bahwa Anda benar-benar yakin.",
+ "smw-admin-docu": "Halaman istimewa ini akan membantu Anda selama instalasi dan peningkatan dari <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki.</a>\nJangan lupa untuk mencadangkan data Anda yang berharga sebelum melaksanakan fungsi administratif.",
+ "smw-admin-db": "Instalasi dan peningkatan basis data",
+ "smw-admin-dbdocu": "Semantic MediaWiki mensyaratkan beberapa pengembangan basis data MediaWiki untuk menyimpan data semantik.\nFungsi berikut memastikan bahwa basis data Anda dipersiapkan dengan baik.\nPerubahan yang dilakukan pada langkah ini tidak memengaruhi bagian lain dari basis data MediaWiki, dan dengan mudah dapat dibatalkan jika diingkan.\nFungsi persiapan ini dapat dilakukan berulang kali tanpa menyebabkan kerusaan, tapi hanya dibutuhkan sekali sewaktu instalasi atau peningkatan.",
+ "smw-admin-permissionswarn": "Jika operasi gagal karena kesalahan SQL, pengguna basis data yang digunakan oleh wiki Anda (cek LocalSettings.php Anda) mungkin tidak mempunyai hak akses yang cukup.\nAnda dapat memberikan hak akses tambahan untuk membuat dan menghapus tabel, memasukkan sementara pengguna root basis data Anda di LocalSettings.php, atau menggunakan skrip pemeliharaan <code>setupStore.php</code> yang dapat menggunakan hak akses dari AdminSettings.php.",
+ "smw-admin-dbbutton": "Inisiasi atau tingkatkan tabel",
+ "smw-admin-announce": "Umumkan wiki Anda",
+ "smw_smwadmin_datarefresh": "Perbaikan dan peningkatan data",
+ "smw_smwadmin_datarefreshdocu": "Dimungkinkan untuk memulihkan semua data Semantic MediaWiki berdasarkan isi yang ada sekarang di wiki ini.\nHal ini berguna untuk memperbaiki data yang rusak atau menyegarkan data jika format internal telah berubah karena suatu peningkatan perangkat lunak.\nPembaruan dilaksanakan halaman per halaman dan tidak akan langsung selesai.\nTampilan berikut menunjukkan jika suatu pembaruan sedang berlangsung dan memungkinkan Anda untuk memulai atau menghentikan proses (kecuali jika fitur ini dimatikan oleh pengelola situs).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Suatu pembaruan sedang dalam proses.</strong>\nAdalah wajar jika kemajuan pembaruan berjalan lambat karena ia hanya menyegarkan data sedikit demi sedikit setiap kali seorang pengguna mengakses wiki ini.\nUntuk menyelesaikan pembaruan ini dengan lebih cepat, Anda dapat menjalankan skrip pemeliharaan MediaWiki <code>runJobs.php</code> (gunakan opsi <code>--maxjobs 1000</code> untuk membatasi jumlah pembaruan yang dilakukan dalam satu saat).\nPerkiraan kemajuan pembaruan saat ini:",
+ "smw_smwadmin_datarefreshbutton": "Mulai pembaruan data",
+ "smw_smwadmin_datarefreshstop": "Hentikan pembaruan ini",
+ "smw_smwadmin_datarefreshstopconfirm": "Ya, saya {{GENDER:$1|yakin}}",
+ "smw-admin-support": "Cari dukungan",
+ "smw-admin-supportdocu": "Berbagai sumber dapat membantu Anda jika ada masalah:",
+ "smw-admin-installfile": "Jika Anda menghadapi masalah dengan instalasi Anda, mulailah dengan mengecek pedoman di <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">berkas INSTALL</a>.",
+ "smw-admin-smwhomepage": "Dokumentasi pengguna lengkap untuk Semantic MediaWiki tersedia di <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Bug dapat dilaporkan ke <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Jika Anda memiliki pertanyaan atau saran lain, bergabunglah dengan diskusi di <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">milis Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Struktur data",
+ "smw_adminlinks_displayingdata": "Menampilkan data",
+ "smw_adminlinks_inlinequerieshelp": "Bantuan query sisip",
+ "smw-createproperty-isproperty": "Ini adalah properti bertipe $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Nilai|Nilai}} yang diizinkan untuk properti ini adalah:",
+ "smw-prefs-intro-text": "Pilihan di bawah disediakan oleh [https://www.semantic-mediawiki.org/ MediaWiki semantik] (atau ekstensi terkait) untuk mengaktifkan kustomisasi individu pada fungsi terpilih. Untuk informasi lebih lanjut, silakan lihat ini [http://semantic-mediawiki.org/wiki/Help:User_preferences bagian bantuan].",
+ "smw-ui-tooltip-title-quantity": "satuan konversi",
+ "smw-ui-tooltip-title-info": "Informasi",
+ "smw-ui-tooltip-title-warning": "Kesalahan",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Peristiwa",
+ "smw-ui-tooltip-title-note": "Catatan",
+ "smw_unknowntype": "Tipe \"$1\" yang tidak didukung ditetapkan untuk properti.",
+ "smw_concept_header": "Halaman berkonsep \"$1\"",
+ "smw_conceptarticlecount": "Menampilkan $1 {{PLURAL:$1|halaman|halaman}} yang termasuk konsep ini.",
+ "smw-livepreview-loading": "Mengunggah...",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" kembali dengan sebuah \"NULL\" yang tidak mengizinkan sebagai nomor.",
+ "smw-parse": "$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ig.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ig.json
new file mode 100644
index 00000000..ee29cd89
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ig.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ukabia"
+ ]
+ },
+ "smw_viewasrdf": "ntá RDF",
+ "smw_finallistconjunct": ", na",
+ "smw_factbox_head": "Ómárí maka $1",
+ "smw_printername_count": "güó ihe futárá",
+ "smw_printername_list": "Ndetu",
+ "smw_printername_table": "Agada",
+ "smw_printername_template": "Àtụ",
+ "smw-livepreview-loading": "Ọ biágó..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ilo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ilo.json
new file mode 100644
index 00000000..cba7af66
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ilo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lam-ang"
+ ]
+ },
+ "browse": "Agbasabasa iti wiki",
+ "smw-livepreview-loading": "Maikarkarga…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/io.json b/www/wiki/extensions/SemanticMediaWiki/i18n/io.json
new file mode 100644
index 00000000..4065d744
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/io.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya"
+ ]
+ },
+ "smw_printername_template": "Shablono",
+ "smw_result_next": "Sequanta",
+ "smw-livepreview-loading": "Ol kargesas…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/is.json b/www/wiki/extensions/SemanticMediaWiki/i18n/is.json
new file mode 100644
index 00000000..fc42a3ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/is.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Sveinn í Felli"
+ ]
+ },
+ "browse": "Skoða wiki",
+ "smw-livepreview-loading": "Framkalla…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/it.json b/www/wiki/extensions/SemanticMediaWiki/i18n/it.json
new file mode 100644
index 00000000..1fc4bfcf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/it.json
@@ -0,0 +1,436 @@
+{
+ "@metadata": {
+ "authors": [
+ "Beta16",
+ "Candalua",
+ "Civvì",
+ "Cruccone",
+ "Darth Kule",
+ "Davide Eynard, David Laniado",
+ "F. Cosoleto",
+ "Gianfranco",
+ "Gmelfi",
+ "Melos",
+ "Nemo bis",
+ "Pietrodn",
+ "Ximo17",
+ "පසිඳු කà·à·€à·’න්ද",
+ "ì•„ë¼",
+ "FRacco",
+ "Giuseppe Forte",
+ "Alexmar983",
+ "Wikitoni",
+ "Macofe",
+ "Urielejh",
+ "Matteocng",
+ "Samuele2002",
+ "Statix64",
+ "Kaspo",
+ "G.garatti",
+ "Fitoschido",
+ "S4b1nuz E.656",
+ "Sarah Bernabei",
+ "McDutchie",
+ "OrbiliusMagister"
+ ]
+ },
+ "smw-desc": "Rende la tua wiki più accessibile - per le macchine ''e'' per gli umani ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentazione in linea])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "Le funzionalità di Semantic MediaWiki non sono abilitate per questo wiki.",
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": " e",
+ "smw-factbox-head": "... altro su \"$1\"",
+ "smw-factbox-facts": "Dati",
+ "smw-factbox-facts-help": "Mostra dichiarazioni e dati che sono stati creati da un utente",
+ "smw-factbox-facts-derived": "Dati derivati",
+ "smw-factbox-facts-derived-help": "Mostra dati che sono stati derivati da principi o con l'aiuto di altre tecniche di ragionamento",
+ "smw_isspecprop": "Questa proprietà è una proprietà speciale all'interno di questo wiki.",
+ "smw-concept-cache-header": "Utilizzo della cache",
+ "smw-concept-cache-count": "La [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count cache di concetti] contiene {{PLURAL:$1|'''una''' entità|'''$1''' entità}} ($2).",
+ "smw-concept-no-cache": "La cache non è disponibile.",
+ "smw_concept_description": "Descrizione del concetto \"$1\"",
+ "smw_no_concept_namespace": "I concetti possono essere definiti solo sulle pagine nel namespace Concept:.",
+ "smw_multiple_concepts": "Ogni pagina concetto può avere una sola definizione di concetto.",
+ "smw_concept_cache_miss": "Il concetto \"$1\" non può essere usato ora, siccome la configurazione della wiki richiede che sia elaborato off-line. Se il problema non si risolve dopo un po' di tempo, chiedi all'amministratore del tuo sito di rendere disponibile questo concetto.",
+ "smw_noinvannot": "Il valore non può essere assegnato alla proprietà inversa.",
+ "version-semantic": "Estensioni semantiche",
+ "smw_baduri": "Spiacenti. Gli URI del tipo “$1†non sono consentiti.",
+ "smw_printername_count": "Conta risultati",
+ "smw_printername_csv": "Esportazione CSV",
+ "smw_printername_dsv": "Esportazione DSV",
+ "smw_printername_debug": "Debug della query (per utenti esperti)",
+ "smw_printername_embedded": "Includi i contenuti della pagina",
+ "smw_printername_json": "Esportazione JSON",
+ "smw_printername_list": "Elenco",
+ "smw_printername_plainlist": "Lista semplice",
+ "smw_printername_ol": "Enumerazione",
+ "smw_printername_ul": "Creazione lista di elementi",
+ "smw_printername_table": "Tabella",
+ "smw_printername_broadtable": "Tabella estesa",
+ "smw_printername_template": "Template",
+ "smw_printername_rdf": "Esportazione RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "testo",
+ "smw-paramdesc-limit": "Il numero massimo di risultati da restituire",
+ "smw-paramdesc-headers": "Mostra i nomi di header/proprietà",
+ "smw-paramdesc-mainlabel": "L'etichetta da dare al nome della pagina principale",
+ "smw-paramdesc-link": "Mostra i valori come link",
+ "smw-paramdesc-intro": "Testo da mostrare prima dei risultati della query, se ce ne sono",
+ "smw-paramdesc-outro": "Testo da mostrare dopo i risultati della query, se ce ne sono",
+ "smw-paramdesc-default": "Testo da mostrare se la query non dà risultati",
+ "smw-paramdesc-sep": "Il separatore tra i risultati",
+ "smw-paramdesc-template": "Il nome di un template con cui mostrare quanto restituito",
+ "smw-paramdesc-columns": "Il numero di colonne in cui ordinare la visualizzazione dei risultati",
+ "smw-paramdesc-embedformat": "Il tag HTML usato per definire le intestazioni",
+ "smw-paramdesc-embedonly": "Non mostrare intestazioni",
+ "smw-paramdesc-rdfsyntax": "La sintassi RDF da utilizzare",
+ "smw-paramdesc-csv-sep": "Specifica un separatore di colonna",
+ "smw-paramdesc-csv-valuesep": "Specifica un separatore di valori",
+ "smw-paramdesc-dsv-separator": "Il separatore da usare",
+ "smw-paramdesc-dsv-filename": "Il nome per il file DSV",
+ "smw-paramdesc-searchlabel": "Testo per continuare la ricerca ai risultati successivi",
+ "smw-paramdesc-export": "Opzioni di esportazione",
+ "smw-printername-feed": "Feed RSS e Atom",
+ "smw-paramdesc-feedtype": "Tipo di feed",
+ "smw-paramdesc-feedtitle": "Il testo da utilizzare come titolo del feed",
+ "smw-paramdesc-feeddescription": "Il testo da utilizzare come descrizione del feed",
+ "smw-paramdesc-feedpagecontent": "Il contenuto della pagina da visualizzare con il feed",
+ "smw-label-feed-description": "Feed $2 $1",
+ "smw_iq_disabled": "Le query semantiche sono state disabilitate per questo wiki.",
+ "smw_iq_moreresults": "&hellip; risultati successivi",
+ "smw_parseerror": "Il valore dato non è stato compreso.",
+ "smw_notitle": "“$1†non pu&ograve; essere utilizzato come nome di una pagina all'interno di questo wiki.",
+ "smw_wrong_namespace": "Qui sono consentite solo pagine del namespace \"$1\".",
+ "smw_manytypes": "È stato definito più di un tipo per la proprietà.",
+ "smw_emptystring": "Le stringhe vuote non sono accettate.",
+ "smw_notinenum": "\"$1\" non è nell'elenco ($2) dei [[Property:Allows value|valori consentiti]] per la proprietà \"$3\".",
+ "smw_noboolean": "“$1†non &egrave; riconosciuto come valore Booleano (vero/falso).",
+ "smw_true_words": "vero,v,si,s,true,t,yes,y",
+ "smw_false_words": "falso,f,no,n,false",
+ "smw_nofloat": "“$1†non &egrave; un numero.",
+ "smw_infinite": "I numeri grandi come \"$1\" non sono supportati.",
+ "smw_novalues": "Nessun valore specificato",
+ "smw_nodatetime": "Non &egrave; stato possibile comprendere la data “$1â€.",
+ "smw_toomanyclosing": "Sembrano esserci troppe ripetizioni di “$1†all'interno della query.",
+ "smw_noclosingbrackets": "Alcune \"<nowiki>[[</nowiki>\" all'interno della query non sono state chiuse con le corrispondenti \"]]\".",
+ "smw_misplacedsymbol": "Il simbolo “$1†&grave; stato usato in un punto in cui &egrave; inutile.",
+ "smw_unexpectedpart": "Non &egrave; stato possibile comprendere la parte “$1†della query. Il risultato potrebbe essere diverso da quello atteso.",
+ "smw_emptysubquery": "Qualche subquery ha una condizione non valida.",
+ "smw_misplacedsubquery": "Qualche subquery &egrave; stata utilizzata in una posizione in cui non era consentito.",
+ "smw_valuesubquery": "Le subquery non sono supportate per i valori della proprietà \"$1\".",
+ "smw_badqueryatom": "Non è stato possibile comprendere parte \"<nowiki>[[…]]</nowiki>\" della query.",
+ "smw_propvalueproblem": "Non è stato possibile comprendere il valore della proprietà \"$1\".",
+ "smw_noqueryfeature": "Qualche funzionalità di query non è stata supportata in questa wiki e parte della query è stata rimossa ($1).",
+ "smw_noconjunctions": "Le congiunzioni nelle query non sono supportate in questa wiki e parte della query è stata rimossa ($1).",
+ "smw_nodisjunctions": "La disgiunzione all'interno delle query non &egrave; supportata in questo wiki, quindi parte della query &egrave; stata ignorata ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|La seguente condizione|Le seguenti condizioni}} all'interno della query <code>$1</code> non {{PLURAL:$2|è stata considerata|sono state considerate}} a causa delle restrizioni di dimensione o profondità delle query impostate per questo wiki: <code>$1</code>.",
+ "smw_notemplategiven": "Per favore fornisci un valore per il parametro \"template\" per far funzionare questo formato di query.",
+ "smw_type_header": "Proprietà del tipo \"$1\"",
+ "smw_typearticlecount": "Visualizzazione di $1 {{PLURAL:$1|proprietà|proprietà}} che usano questo tipo.",
+ "smw_attribute_header": "Pagine che usano la proprietà \"$1\"",
+ "smw_attributearticlecount": "Visualizzazione di $1 {{PLURAL:$1|pagina che usa|pagine che usano}} questa proprietà.",
+ "smw-propertylist-subproperty-header": "Sottoproprietà",
+ "smw-propertylist-redirect-header": "Sinonimi",
+ "exportrdf": "Esporta le pagine in RDF",
+ "smw_exportrdf_docu": "Questa pagina consente di ottenere dati da una pagina in formato RDF. Per esportare delle pagine, inseritene i titoli nella casella di testo sottostante, un titolo per riga.",
+ "smw_exportrdf_recursive": "Esporta ricorsivamente tutte le pagine correlate. Nota: il risultato potrebbe essere molto grande!",
+ "smw_exportrdf_backlinks": "Esporta anche le pagine che si riferiscono a quelle esportate. Genera un RDF navigabile.",
+ "smw_exportrdf_lastdate": "Non esportare le pagine che non hanno sub&igrave;to modifiche dal momento specificato.",
+ "smw_exportrdf_submit": "Esporta",
+ "uriresolver": "Risolutore di URI",
+ "properties": "Proprietà",
+ "smw_properties_docu": "Le seguenti proprietà sono utilizzate all'interno del wiki.",
+ "smw_property_template": "$1 di tipo $2 (usata $3 {{PLURAL:$3|volta|volte}})",
+ "smw_propertylackspage": "Tutte le proprietà dovrebbero essere descritte da una pagina!",
+ "smw_propertylackstype": "Non è stato specificato nessun tipo per questa proprietà (per il momento si suppone sia di tipo $1).",
+ "smw_propertyhardlyused": "Questa proprietà non è quasi mai usata nel wiki!",
+ "smw-special-property-searchform": "Mostra le proprietà che contengono:",
+ "smw-special-property-searchform-inputinfo": "L'input distingue le maiuscole dalle minuscole e quando viene usato per filtrare, solamente le proprietà che soddisfano le condizioni vengono mostrate.",
+ "smw-special-property-searchform-options": "Opzioni",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Nessuno",
+ "smw-special-wantedproperties-filter-unapproved": "Non approvato.",
+ "concepts": "Concetti",
+ "smw-special-concept-header": "Elenco di concetti",
+ "smw-special-concept-empty": "Nessun concetto trovato.",
+ "unusedproperties": "Propietà non utilizzate",
+ "smw-unusedproperties-docu": "Questa pagina elenca le [https://www.semantic-mediawiki.org/wiki/Unused_properties proprietà inutilizzate] che esistono nonostante nessun'altra pagina ne faccia uso. Per una vista differenziale, vedi le pagine speciali [[Special:Properties|intere]] o [[Special:WantedProperties|proprietà richieste]].",
+ "smw-unusedproperty-template": "$1 di tipo $2",
+ "wantedproperties": "Proprietà senza descrizione",
+ "smw-wantedproperties-docu": "Questa pagina elenca le [https://www.semantic-mediawiki.org/wiki/Wanted_properties proprietà richieste] che sono usate nel wiki ma non hanno ancora una pagina che le descriva. Per una vista differenziale, vedi le pagine speciali [[Special:Properties|intere]] o [[Special:UnusedProperties|proprietà inutilizzate]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usi}})",
+ "smw_purge": "Aggiorna",
+ "types": "Tipi",
+ "smw_types_docu": "Elenco dei [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipi di dati disponibili] con ogni [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipo] che rappresenta un unico insieme di attributi per descrivere un valore in termini di archiviazione e visualizzazione di caratteristiche che sono ereditari a una proprietà assegnata.",
+ "smw-special-types-no-such-type": "\"$1\" è sconosciuto o non è stato specificato come tipo di dati valido.",
+ "smw-statistics": "Statistiche semantiche",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Proprietà}}]] (totale)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Proprietà}} (totale)",
+ "smw-statistics-property-page": "Proprietà ({{PLURAL:$1|registrata|registrate}} con una pagina)",
+ "smw-statistics-property-type": "Proprietà ({{PLURAL:$1|assegnata|assegnate}} a un tipo di dati)",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concetto|Concetti}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concetto|Concetti}}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipo|Tipi}} di dato]]",
+ "smw-statistics-delete-count": "Entità {{PLURAL:$1|obsoleta|obsolete}} (contrassegnate per la rimozione)",
+ "smw_uri_doc": "Il risolutore di URI implementa il [$1 W3C TAG finding on httpRange-14]. Fa in modo che gli esseri umani non diventino siti Web.",
+ "ask": "Ricerca semantica",
+ "smw_ask_sortby": "Ordina per colonna (opzionale)",
+ "smw_ask_ascorder": "Crescente",
+ "smw_ask_descorder": "Decrescente",
+ "smw_ask_submit": "Trova risultati",
+ "smw_ask_editquery": "Modifica query",
+ "smw_add_sortcondition": "[Aggiungi condizione di ordinamento]",
+ "smw_ask_hidequery": "Nascondi query (vista compatta)",
+ "smw_ask_help": "Help sulle query",
+ "smw_ask_queryhead": "Condizione",
+ "smw_ask_printhead": "Selezione dei dati da mostrare",
+ "smw_ask_printdesc": "(aggiungi un nome di proprietà per riga)",
+ "smw_ask_format_as": "Formatta come:",
+ "smw_ask_defaultformat": "predefinito",
+ "smw_ask_otheroptions": "Altre opzioni",
+ "smw_ask_show_embed": "Mostra codice di inclusione",
+ "smw_ask_hide_embed": "Nascondi codice di inclusione",
+ "smw_ask_embed_instr": "Per includere questa query in linea in una pagina wiki, usa il codice qui sotto.",
+ "smw-ask-delete": "Rimuovi",
+ "smw-ask-sorting": "Ordinamento",
+ "smw-ask-options": "Opzioni",
+ "smw-ask-format-options": "Formato e opzioni",
+ "smw-ask-parameters": "Parametri",
+ "smw-ask-search": "Cerca",
+ "smw-ask-result": "Risultato",
+ "smw-ask-empty": "Pulisci tutte le voci",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Aiuto con il formato selezionato: $1",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> per recuperare i suggerimenti di proprietà (e.g. <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> per recuperare i suggerimenti di categoria",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> per recuperare i suggerimenti di categoria",
+ "smw-ask-query-search-info": "La query <code><nowiki>$1</nowiki></code> è stata eseguita da {{PLURAL:$3|1=<code>$2</code> (dalla cache)|<code>$2</code> (dalla cache)|<code>$2</code>}} in $4 {{PLURAL:$4|secondo|secondi}}.",
+ "searchbyproperty": "Cerca per proprietà",
+ "smw_sbv_docu": "Cerca tutte le pagine che hanno proprietà e valore specificati.",
+ "smw_sbv_novalue": "Inserire un valore valido per la proprietà, o visualizzare tutti i valori di proprietà per \"$1\".",
+ "smw_sbv_displayresult": "Lista di tutte le pagine che hanno proprietà \"$1\" con valore \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Una lista di tutte le pagine che hanno la proprietà \"$1\" col valore \"$2\".\nSiccome ci sono solo pochi risultati, sono visualizzati anche i valori vicini.",
+ "smw_sbv_property": "Proprietà:",
+ "smw_sbv_value": "Valore:",
+ "smw_sbv_submit": "Trova risultati",
+ "browse": "Esplora il sito",
+ "smw_browselink": "Sfoglia le proprietà",
+ "smw_browse_article": "Inserire il nome della pagina da cui iniziare l'esplorazione",
+ "smw_browse_go": "Vai",
+ "smw_browse_show_incoming": "Mostra proprietà in entrata",
+ "smw_browse_hide_incoming": "Nascondi proprietà in entrata",
+ "smw_browse_no_outgoing": "Questa pagina non ha proprietà.",
+ "smw_browse_no_incoming": "Nessuna proprietà ha collegamenti verso questa pagina.",
+ "smw-browse-show-group": "Mostra gruppi",
+ "smw-browse-hide-group": "Nascondi gruppi",
+ "smw-noscript": "La pagina o l'azione richiede Javascript per funzionare, per favore installa Javascript sul tuo browser o utilizza un browser dove è supportato così che la funzionalità possa essere utilizzata e sia a disposizione come richiesto. Per ulteriore assistenza, per favore dai un'occhiata alla pagina di supporto [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 di",
+ "smw_inverse_label_property": "Etichetta della proprietà inversa",
+ "pageproperty": "Ricerca proprietà della pagina",
+ "smw_pp_docu": "Inserisci una pagina e una proprietà o solo una proprietà per recuperare tutti i valori assegnati.",
+ "smw_pp_from": "Da pagina:",
+ "smw_pp_type": "Proprietà:",
+ "smw_pp_submit": "Trova risultati",
+ "smw_result_prev": "Precedente",
+ "smw_result_next": "Successivo",
+ "smw_result_results": "Risultati",
+ "smw_result_noresults": "Spiacenti, nessun risultato.",
+ "smwadmin": "Funzioni amministrative e di manutenzione",
+ "smw-admin-permission-missing": "L'accesso a questa pagina è stato bloccato a causa di autorizzazioni mancanti, consulta la pagina di aiuto sulle [https://www.semantic-mediawiki.org/wiki/Help:Permissions autorizzazioni] per i dettagli sulle impostazioni necessarie.",
+ "smw-admin-setupsuccess": "Il motore di storage è stato installato.",
+ "smw_smwadmin_return": "Torna a $1",
+ "smw_smwadmin_updatestarted": "È iniziato un nuovo processo di aggiornamento per ricaricare i dati semantici.\nTutti i dati registrati saranno ricostruiti o riparati se necessario.\nPuoi seguire i progressi dell'aggiornamento in questa pagina speciale.",
+ "smw_smwadmin_updatenotstarted": "Un processo di aggiornamento è già in corso.\nNon ne verrà avviato un altro.",
+ "smw_smwadmin_updatestopped": "Tutti i processi di aggiornamento in corso sono stati arrestati.",
+ "smw_smwadmin_updatenotstopped": "Per arrestare il processo di aggiornamento in corso, devi cliccare nel riquadro per confermare che sei davvero sicuro di volerlo fare.",
+ "smw-admin-docu": "Questa pagina speciale serve ad aiutarti durante l'installazione, aggiornamento, manutenzione e l'utilizzo di <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a> e fornirti anche ulteriori funzioni ed attività amministrative, nonché statistiche.\nRicordati di fare un backup dei dati importanti prima di eseguire funzioni amministrative.",
+ "smw-admin-environment": "Ambiente software",
+ "smw-admin-db": "Configurazione database",
+ "smw-admin-dbdocu": "Semantic MediaWiki richiede la propria struttura di database (ed è indipendente da MediaWiki, quindi non influisce sul resto dell'installazione di MediaWiki) per archiviare i dati semantici.\nQuesta funzione di impostazione può essere rieseguita più volte senza conseguenze indesiderate, ma è richiesta solo una volta all'atto dell'installazione o dell'aggiornamento.",
+ "smw-admin-permissionswarn": "Se l'operazione avrà esito negativo con errori SQL, l'utente del database usato dal tuo wiki (controlla il tuo file \"LocalSettings.php\") probabilmente non ha permessi sufficienti.\nPuoi assegnare a questo utente permessi aggiuntivi per creare e cancellare tabelle, oppure inserire temporaneamente nel file \"LocalSettings.php\" i dati di accesso root al database, oppure usare lo script di manutenzione <code>setupStore.php</code>, che può utilizzare le credenziali di un utente amministratore.",
+ "smw-admin-dbbutton": "Inizializza o aggiorna tabelle",
+ "smw-admin-announce": "Annuncia il tuo wiki",
+ "smw-admin-deprecation-notice-title-notice": "Prossimi cambiamenti",
+ "smw-admin-deprecation-notice-title-removal": "Impostazioni rimosse",
+ "smw_smwadmin_datarefresh": "Ricostruzione dati",
+ "smw_smwadmin_datarefreshdocu": "Si possono ripristinare tutti i dati Semantic MediaWiki basati sui contenuti attuali del wiki.\nQuesto può essere utile per riparare dati corrotti o per aggiornare i dati se il loro formato interno si è modificato per qualche aggiornamento di software.\nL'aggiornamento è eseguito pagina per pagina e non sarà completato immediatamente.\nQui di seguito si vede se un aggiornamento è in corso e potrai avviare od arrestare gli aggiornamenti (a meno che questa funzionalità non sia stata disattivata dall'amministratore del sito).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Un aggiornamento è già in corso.</strong>\nNon è anormale che l'aggiornamento proceda solo lentamente, poiché verifica i dati solo a piccoli pacchetti ogni volta che un utente accede al wiki.\nPer terminare il processo pià rapidamente, puoi invocare lo script di manutenzione MediaWiki <code>runJobs.php</code> (usa l'opzione <code>--maxjobs 1000</code> per restringere il numero di verifiche svolte in ciascuna tornata).\nProgresso stimato dell'aggiornamento in corso:",
+ "smw_smwadmin_datarefreshbutton": "Pianifica ricostruzione dati",
+ "smw_smwadmin_datarefreshstop": "Ferma questo aggiornamento",
+ "smw_smwadmin_datarefreshstopconfirm": "Sì, sono {{GENDER:$1|sicuro|sicura}}.",
+ "smw-admin-support": "Ottieni supporto",
+ "smw-admin-supportdocu": "Diverse risorse sono fornite per esserti d'aiuto in caso di problemi:",
+ "smw-admin-installfile": "Se hai problemi con la tua installazione, inizia a verificare le linee guida nel <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">file INSTALL</a> e la <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">pagina di installazione</a>.",
+ "smw-admin-smwhomepage": "La documentazione utente completa per Semantic MediaWiki si trova in <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Gli errori possono essere segnalati sul <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">tracker di anomalie</a>, la pagina <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">riportare errori</a> fornisce alcune guide su come scrivere una segnalazione efficiente.",
+ "smw-admin-questions": "Se hai altre domande o suggerimenti, unisciti alle discussioni sulla <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">mailing list</a> degli utenti di Semantic MediaWiki o la <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">chatroom</a>.",
+ "smw-admin-other-functions": "Altre funzioni",
+ "smw-admin-supplementary-section-title": "Funzioni supplementari",
+ "smw-admin-supplementary-settings-title": "Impostazioni di configurazione",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> restituisce un elenco collettivo delle impostazioni disponibili utilizzate in Semantic MediaWiki",
+ "smw-admin-supplementary-elastic-functions": "Funzioni supportate",
+ "smw-admin-supplementary-elastic-settings-title": "Impostazioni",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodi",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistiche",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervallo di aggiornamento: $1",
+ "smw-property-label-similarity-threshold": "Soglia:",
+ "smw_adminlinks_datastructure": "Struttura dei dati",
+ "smw_adminlinks_displayingdata": "Visualizzazione dati",
+ "smw_adminlinks_inlinequerieshelp": "Aiuto sulle query inline",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Numero di utilizzi] stimati: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Proprietà definita da {{PLURAL:$1|utente|sistema}}",
+ "smw-createproperty-isproperty": "È una proprietà di tipo $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Il valore permesso per questa proprità è|I valori permessi per questa proprietà sono}}:",
+ "smw-paramdesc-category-delim": "Il delimitatore",
+ "smw-info-par-message": "Messaggio da visualizzare.",
+ "prefs-general-options": "Opzioni generali",
+ "prefs-ask-options": "Opzioni Special:Ask",
+ "smw-prefs-general-options-disable-search-info": "Disabilita le informazioni del supporto sintattico sulla pagina di ricerca standard",
+ "smw-ui-tooltip-title-property": "Proprietà",
+ "smw-ui-tooltip-title-quantity": "Conversione unità",
+ "smw-ui-tooltip-title-info": "Informazione",
+ "smw-ui-tooltip-title-service": "Collegamento di servizio",
+ "smw-ui-tooltip-title-warning": "Attenzione",
+ "smw-ui-tooltip-title-error": "Errore",
+ "smw-ui-tooltip-title-parameter": "Parametro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Nota",
+ "smw_unknowntype": "Il tipo \"$1\" di questa proprietà non è valido",
+ "smw_concept_header": "Pagine del concetto \"$1\"",
+ "smw_conceptarticlecount": "Mostro {{PLURAL:$1|una pagina|$1 pagine}}.",
+ "right-smw-patternedit": "Modifica accesso per mantenere le espressioni regolari e i modelli consentiti (Semantic MediaWiki)",
+ "right-smw-pageedit": "Modifica l'accesso per <code>Is edit protected</code> nelle pagine di annotazione (Semantic MediaWiki)",
+ "action-smw-patternedit": "modificare le espressioni regolari utilizzate da Semantic MediaWiki",
+ "group-smwadministrator": "Amministratori (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|amministratore|amministratrice|amministratore/trice}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Amministratori (Semantic MediaWiki)",
+ "group-smwcurator": "Curatori (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|curatore|curatrice|curatore/trice}} (Semantic MediaWiki)",
+ "grouppage-smwcurator": "{{ns:project}}:Curatori (Semantic MediaWiki)",
+ "smw-property-predefined-ask": "\"$1\" è una proprietà predefinita che rappresenta meta informazioni (sotto forma di [https://www.semantic-mediawiki.org/wiki/Subobject sottoggetto]) su singole interrogazioni ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "\"$1\" è una proprietà predefinita che contiene il numero delle condizioni usate in una interrogazione ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" è una proprietà predefinita che informa sulla profondità di un'interrogazione ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-sp-properties-header-label": "Elenco delle proprietà",
+ "smw-admin-iddispose-title": "Smaltimento",
+ "smw-admin-iddispose-docu": "Da notare che l'operazione di smaltimento non ha restrizioni e rimuove l'entità dal motore di archiviazione insieme a tutti i suoi riferimenti nelle tabelle, se confermata. Avvia questa attività con '''cautela''' e solo dopo aver consultato la [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentazione].",
+ "smw-admin-objectid": "ID:",
+ "smw-livepreview-loading": "Caricamento in corso...",
+ "smw-sp-searchbyproperty-resultlist-header": "Elenco dei risultati",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" ha restituito un \"NULL\", che non è consentito come numero.",
+ "smw-search-help-intro": "Un <code><nowiki>[[ ... ]]</nowiki></code> input sarà segnalato al processore di input per usare la ricerca finale del Semantic MediaWiki. Si dovrà notare che la combinazione <code><nowiki>[[ ... ]]</nowiki></code> con una ricerca di testo non strutturato come <code><nowiki>[[ ... ]] O Lorem ipsum</nowiki></code> non è supportato.",
+ "smw-search-help-structured": "Ricerche strutturate:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (come [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context filtro sul contesto])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (con un [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context query del contesto])",
+ "smw-search-input": "Inserisci e cerca",
+ "smw-search-syntax": "Sintassi",
+ "smw-search-profile": "Esteso",
+ "smw-search-profile-tooltip": "Cerca le funzioni in connessione con Semantic MediaWiki",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Alcune espressioni sono riservate come: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-section-namespace": "Namespace",
+ "smw-search-show": "Mostra",
+ "smw-search-hide": "Nascondi",
+ "smw-property-predefined-impo": "\"$1\" è una proprietà predefinita che descrive una relazione ad un [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabolario importato] ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "\"$1\" è una proprietà predefinita che descrive il [[Special:Types|tipo di dati]] di una proprietà ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "\"$1\" è una proprietà predefinita che rappresenta un costrutto [https://www.semantic-mediawiki.org/wiki/Help:Container contenitore] ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "Il contenitore consente di accumulare assegnazioni proprietà-valore simili a quelli di una normale pagina wiki, ma all'interno di una diversa entità di spazio, pur essendo collegati al soggetto incorporato .",
+ "smw-property-predefined-errp": "\"$1\" è una proprietà predefinita che traccia gli errori di inserimento per valori di annotazioni irregolari ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] è una proprietà predefinita che può definire una lista di valori ammissibili per limitare assegnazioni di valori per una proprietà ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-property-create-restriction": "La proprietà \"$1\" non esiste e l'utente non possiede il premesso \"$2\" (vedi [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode]) per creare o annotare valori o proprietà non approvate.",
+ "smw-datavalue-restricted-use": "Il valore dei dati \"$1\" è stato contrassegnato per l'uso limitato.",
+ "smw-datavalue-invalid-number": "\"$1\" non può essere interpretato come un numero.",
+ "smw-query-condition-circular": "Una possibile condizione circolare è stata rilevata in \"$1\".",
+ "smw-types-list": "Elenco dei tipi di dati",
+ "smw-types-help": "Ulteriori informazioni ed esempi sono riportati su questa [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 pagina di aiuto].",
+ "smw-type-anu": "\"$1\" è una variante del tipo di dati [[Special:Types/URL|URL]] ed è in gran parte utilizzata per una dichiarazione di esportazione \"owl:AnnotationProperty\".",
+ "smw-type-tel": "\"$1\" è un particolare tipo di dati utilizzato per descrivere i numeri di telefono internazionali secondo la RFC 3966.",
+ "smw-type-dat": "\"$1\" è un tipo di dati base impiegato per rappresentare punti nel tempo in un formato unificato.",
+ "smw-type-ema": "\"$1\" è un tipo di dati speciale per rappresentare una email.",
+ "smw-type-tab-properties": "Proprietà",
+ "smw-type-tab-types": "Tipi",
+ "smw-type-tab-errors": "Errori",
+ "smw-type-primitive": "Base",
+ "smw-type-contextual": "Contestuale",
+ "smw-property-predefined-errc": "\"$1\" è una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] che rappresenta errori che appaiono in connessioni con valori di annotazioni o processi di inserimento impropri.",
+ "smw-property-predefined-long-errc": "Gli errori sono raccolti in un [https://www.semantic-mediawiki.org/wiki/Help:Container contenitore] che può includere un riferimento alla proprietà che ha causato la discrepanza.",
+ "smw-property-predefined-errt": "\"$1\" è una proprietà predefinita che contiene una descrizione testuale di un errore ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mdat": "\"$1\" è una proprietà predefinita che corrisponde alla data dell'ultima modifica ad un soggetto ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "\"$1\" è una proprietà predefinita che corrisponde alla data della prima versione di un soggetto ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "\"$1\" è una proprietà predefinita che indica se un soggetto è nuovo o no ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "\"$1\" è una proprietà predefinita che contiene il nome della pagina dell'utente che ha creato l'ultima versione ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "\"$1\" è una proprietà predefinita che descrive il tipo MIME di un file caricato ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "\"$1\" è una proprietà predefinita che descrive il tipo di media di un file caricato ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "\"$1\" è una proprietà predefinita che contiene il nome del formato del risultato usato in una interrogazione ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "\"$1\" è una proprietà predefinita che descrive le condizioni di un'interrogazione come stringa ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "\"$1\" è una proprietà predefinita che contiene il tempo (in secondi) che è stato richiesto per completare l'esecuzione di un'interrogazione ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-prec": "\"$1\" è una proprietà predefinita che descrive una [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisione di visualizzazione] (in cifre decimali) per tipi di dati numerici.",
+ "smw-types-extra-geo-not-available": "L'[https://www.semantic-mediawiki.org/wiki/Extension:Maps estensione \"Maps\"] non è stata rilevata, pertanto \"$1\" è limitato nella sua capacità di operare.",
+ "smw-datavalue-languagecode-invalid": "\"$1\" non è stato riconosciuto come un codice di lingua supportato.",
+ "smw-property-predefined-lcode": "\"$1\" è una proprietà predefinita che rappresenta un codice di lingua in formato BCP47 ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "\"$1\" è un tipo di dati [https://www.semantic-mediawiki.org/wiki/Help:Container contenitore] che associa un valore testuale con un specifico [[Property:Language code|codice lingua]].",
+ "smw-types-extra-mlt-lcode": "Il tipo di dati {{PLURAL:$2|richiede|non richiede}} un codice per la lingua (cioè {{PLURAL:$2|un'annotazione di un valore senza un codice per la lingua non è accetatta|un'annotazione di un valore senza un codice per la lingua è accetatta}}).",
+ "smw-property-predefined-text": "\"$1\" è una proprietà predefinita che rappresenta un testo di lunghezza arbitraria ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "\"$1\" è una proprietà predefinita che consente di descrivere una proprietà in contesto di una lingua ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "\"$1\" è una proprietà predefinita per definire un elenco di proprietà utilizzate con una proprietà tipo [[Special:Types/Record|record]] ed è fornito da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|secondo|secondi}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|secondo|secondi}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|secondo|secondi}}",
+ "smw_allows_pattern": "Questa pagina dovrebbe contenere un elenco di riferimenti (seguito da [https://it.wikipedia.org/wiki/Espressione_regolare espressioni regolari]) disponibile alla proprietà [[Property:Allows pattern|modello consentito]]. Per modificare questa pagina, è necessario il diritto <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\" è stato classificato come non valido dall'espressione regolare \"$2\".",
+ "smw-datavalue-allows-pattern-reference-unknown": "Il modello di riferimento \"$1\" non può aver corrispondenza ad un elemento in [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-feature-not-supported": "La funzionalità \"$1\" non è supportata o è stata disattivata su questo wiki.",
+ "smw-property-predefined-pvap": "\"$1\" è una proprietà predefinita che può specificare un [[MediaWiki:Smw allows pattern|modello di riferimento]] da applicare alle corrispondenze delle [https://it.wikipedia.org/wiki/Espressione_regolare espressioni regolari] ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-dtitle": "\"$1\" è una proprietà predefinita che può assegnare un titolo da visualizzare diverso ad un'entità ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvuc": "\"$1\" è una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per limitare i valori assegnabili a ciascuna istanza ad essere unici (o uno al massimo).",
+ "smw-property-predefined-long-pvuc": "L'unicità è stabilita quando i due valori non sono uguali nella loro rappresentazione letterale, e qualsiasi violazione di questo vincolo sarà classificato come errore.",
+ "smw-datavalue-uniqueness-constraint-error": "La proprietà \"$1\" consente l'assegnazione sono di valori unici, e ''$2'' è stato già annotato nel soggetto \"$3\".",
+ "smw-property-predefined-boo": "\"$1\" è un [[Special:Types/Boolean|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare valori booleani.",
+ "smw-property-predefined-num": "\"$1\" è un [[Special:Types/Number|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare valori numerici.",
+ "smw-property-predefined-dat": "\"$1\" è un [[Special:Types/Date|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare valori di date.",
+ "smw-property-predefined-uri": "\"$1\" è un [[Special:Types/URL|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare valori di URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" è un [[Special:Types/Quantity|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare valori di quantità.",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" contiene alcune informazioni non interpretabili.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" contiene alcuni componenti vuoti.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" contiene più di tre componenti necessari per un'interpretazione della data.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" è un URL non valido.",
+ "smw-property-predefined-eid": "\"$1\" è un [[Special:Types/External identifier|tipo]] e una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per rappresentare identificatori esterni.",
+ "smw-property-predefined-peid": "\"$1\" è una proprietà predefinita che rappresenta un identificatore esterno ed è fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pefu": "\"$1\" è una proprietà predefinita fornita da [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] per specificare una risorsa esterna con un segnaposto.",
+ "smw-datavalue-parse-error": "Il valore dato \"$1\" non è stato compreso.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" non può essere utilizzato come etichetta preferita perché la lingua \"$2\" è già stata assegnata all'etichetta \"$3\".",
+ "smw-clipboard-copy-link": "Copia il collegamento negli appunti",
+ "smw-no-data-available": "Nessun dato disponibile.",
+ "smw-property-req-violation-missing-fields": "Alla proprietà \"$1\" manca la dichiarazione di dettagli per le annotazioni di tipo \"$2\" non riuscendo a definire la proprietà <code>Has fields</code>.",
+ "smw-edit-protection": "Questa pagina è [[Property:Is edit protected|protetta]] per prevenire la modifica accidentale di dati e può essere modificata solo da utenti in possesso di un'appropriata autorizzazione (\"$1\") oppure [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups gruppo utente].",
+ "smw-edit-protection-disabled": "La modifica di protezione è stata disattivata, pertanto \"$1\" non può essere utilizzato per proteggere il contenuto della pagina dalle modifiche non autorizzate.",
+ "smw-property-predefined-long-edip": "Mentre qualsiasi utente è qualificato per aggiungere questa proprietà ad un soggetto, soltanto utenti con uno specifico permesso possono modificare o rimuovere la protezione ad un'entità, una volta che questa è stata aggiunta.",
+ "smw-format-datatable-emptytable": "Nessun dato disponibile nella tabella",
+ "smw-format-datatable-loadingrecords": "Caricamento in corso...",
+ "smw-format-datatable-processing": "Elaborazione...",
+ "smw-format-datatable-search": "Cerca:",
+ "smw-format-datatable-first": "Primo",
+ "smw-format-datatable-last": "Ultimo",
+ "smw-format-datatable-next": "Successivo",
+ "smw-format-datatable-previous": "Precedente",
+ "smw-format-datatable-toolbar-export": "Esporta",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "apihelp-smwbrowse-summary": "Modulo API per supportare le attività di navigazione per differenti tipi di entità in Semantic MediaWiki.",
+ "smw-property-reserved-category": "Categoria",
+ "smw-category": "Categoria",
+ "smw-filter": "Filtro",
+ "smw-section-expand": "Espandi la sezione",
+ "smw-section-collapse": "Compimi la sezione",
+ "smw-ask-format-help-link": "Formato [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Aiuto",
+ "smw-property-predefined-label-skey": "Chiave di ordinamento",
+ "smw-types-title": "Tipo: $1",
+ "smw-schema-error": "Errore di convalida.",
+ "smw-schema-type": "Tipo",
+ "smw-property-tab-usage": "Utilizzo",
+ "smw-property-tab-redirects": "Sinonimi",
+ "smw-property-tab-subproperties": "Sottoproprietà",
+ "smw-concept-tab-list": "Elenco",
+ "smw-concept-tab-errors": "Errori",
+ "smw-ask-tab-code": "Codice"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ja.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ja.json
new file mode 100644
index 00000000..128986a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ja.json
@@ -0,0 +1,427 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aotake",
+ "Fryed-peach",
+ "Hosiryuhosi",
+ "Likibp",
+ "Naohiro19",
+ "Schu",
+ "Shirayuki",
+ "Whym",
+ "é’å­å®ˆæ­Œ",
+ "ì•„ë¼",
+ "Rxy",
+ "SkyDaisy9",
+ "Mfuji",
+ "Sujiniku",
+ "Otokoume",
+ "Macofe",
+ "Ridmevo",
+ "Ochaochaocha3",
+ "Nemo bis",
+ "Puntti ja",
+ "Hinaloe",
+ "48pedoa",
+ "Suchichi02",
+ "Omotecho",
+ "Eduardo Addad de Oliveira",
+ "Yusuke1109",
+ "Zero 1 k",
+ "Suyama",
+ "Wat"
+ ]
+ },
+ "smw-desc": "ウィキを機械や人間ã«ã¨ã£ã¦ã‚ˆã‚Šã‚¢ã‚¯ã‚»ã‚¹ã—ã‚„ã™ã„ã‚‚ã®ã«ã™ã‚‹ ([https://www.semantic-mediawiki.org/wiki/Help:User_manual オンラインドキュメント])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "ã“ã® wiki ã§ã¯ Semantic MediaWiki ã®æ©Ÿèƒ½ãŒæœ‰åŠ¹åŒ–ã•ã‚Œã¦ã„ã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_viewasrdf": "RDF フィード",
+ "smw_finallistconjunct": ",",
+ "smw_isspecprop": "ã“ã®ãƒ—ロパティã¯ã“ã®ã‚¦ã‚£ã‚­å†…ã®ç‰¹åˆ¥ãªãƒ—ロパティã§ã™ã€‚",
+ "smw-concept-no-cache": "利用å¯èƒ½ãªã‚­ãƒ£ãƒƒã‚·ãƒ¥ã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smw_concept_description": "コンセプト「$1ã€ã®èª¬æ˜Ž",
+ "smw_no_concept_namespace": "コンセプトを定義ã§ãã‚‹ã®ã¯ã€Concept: åå‰ç©ºé–“内ã®ã¿ã§ã™ã€‚",
+ "smw_multiple_concepts": "å„コンセプトページãŒæŒã¦ã‚‹ã‚³ãƒ³ã‚»ãƒ—ト定義㯠1 ã¤ã®ã¿ã§ã™ã€‚",
+ "smw_concept_cache_miss": "コンセプト「$1ã€ã¯ã€ã‚¦ã‚£ã‚­ã®è¨­å®šã«ã‚ˆã‚Šã‚ªãƒ•ãƒ©ã‚¤ãƒ³ã§ã®å‡¦ç†ãŒå¿…è¦ãªãŸã‚ã€ç¾æ™‚点ã§ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。ã—ã°ã‚‰ã経ã£ã¦ã‚‚å•é¡ŒãŒè§£æ¶ˆã•ã‚Œãªã„å ´åˆã¯ã€ã“ã®ã‚³ãƒ³ã‚»ãƒ—トを利用å¯èƒ½ã«ã™ã‚‹ã‚ˆã†ã«ã‚µã‚¤ãƒˆç®¡ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。",
+ "smw_noinvannot": "逆プロパティã«ã¯å€¤ã‚’割り当ã¦ã‚‰ã‚Œã¾ã›ã‚“。",
+ "version-semantic": "æ„味的拡張機能",
+ "smw_baduri": "「$1ã€ã®å½¢å¼ã® URI ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "集計çµæžœ",
+ "smw_printername_csv": "CSV å½¢å¼ã§æ›¸ã出ã—",
+ "smw_printername_dsv": "DSV å½¢å¼ã§æ›¸ã出ã—",
+ "smw_printername_debug": "クエリをデãƒãƒƒã‚° (上級者å‘ã‘)",
+ "smw_printername_embedded": "ページã®å†…容を埋ã‚込む",
+ "smw_printername_json": "JSON å½¢å¼ã§æ›¸ã出ã—",
+ "smw_printername_list": "一覧",
+ "smw_printername_ol": "番å·ä»˜ã箇æ¡æ›¸ã",
+ "smw_printername_ul": "箇æ¡æ›¸ã",
+ "smw_printername_table": "表",
+ "smw_printername_broadtable": "幅広ã®è¡¨",
+ "smw_printername_template": "テンプレート",
+ "smw_printername_rdf": "RDF å½¢å¼ã§æ›¸ã出ã—",
+ "smw_printername_category": "カテゴリ",
+ "validator-type-class-SMWParamSource": "テキスト",
+ "smw-paramdesc-limit": "è¿”ã™çµæžœã®æœ€å¤§æ•°",
+ "smw-paramdesc-offset": "最åˆã®çµæžœã®ã‚ªãƒ•ã‚»ãƒƒãƒˆ",
+ "smw-paramdesc-headers": "ヘッダーåやプロパティåを表示",
+ "smw-paramdesc-mainlabel": "メインページåã«ä»˜ã‘るラベル",
+ "smw-paramdesc-link": "値をリンクã¨ã—ã¦è¡¨ç¤º",
+ "smw-paramdesc-intro": "å•ã„åˆã‚ã›çµæžœãŒã‚ã‚‹å ´åˆã€ãã®å‰ã«è¡¨ç¤ºã™ã‚‹æ–‡å­—列",
+ "smw-paramdesc-outro": "å•ã„åˆã‚ã›çµæžœãŒã‚ã‚‹å ´åˆã€ãã®å¾Œã«è¡¨ç¤ºã™ã‚‹æ–‡å­—列",
+ "smw-paramdesc-default": "å•ã„åˆã‚ã›çµæžœãŒãªã„å ´åˆã«è¡¨ç¤ºã™ã‚‹æ–‡å­—列",
+ "smw-paramdesc-sep": "çµæžœã®åŒºåˆ‡ã‚Šæ–‡å­—",
+ "smw-paramdesc-showsep": "CSV ファイルã®å…ˆé ­ã«åŒºåˆ‡ã‚Šæ–‡å­—を表示ã™ã‚‹ (\"sep=<値>\")",
+ "smw-paramdesc-distribution": "ã™ã¹ã¦ã®å€¤ã‚’表示ã™ã‚‹ä»£ã‚ã‚Šã«ã€å‡ºç¾æ•°ã‚’æ•°ãˆã¦è¡¨ç¤ºã—ã¾ã™ã€‚",
+ "smw-paramdesc-distributionsort": "出ç¾æ•°ã«ã‚ˆã£ã¦å€¤ã®åˆ†å¸ƒã‚’整列ã—ã¾ã™ã€‚",
+ "smw-paramdesc-template": "å°åˆ·å‡ºåŠ›ã¨ã¨ã‚‚ã«è¡¨ç¤ºã™ã‚‹ãƒ†ãƒ³ãƒ—レートã®åå‰",
+ "smw-paramdesc-columns": "çµæžœã‚’表示ã™ã‚‹åˆ—ã®æ•°",
+ "smw-paramdesc-userparam": "テンプレートãŒä½¿ç”¨ã•ã‚Œã¦ã„ã‚‹å ´åˆã«ã€å„テンプレート呼出ã—ã«æ¸¡ã•ã‚Œã‚‹å€¤",
+ "smw-paramdesc-introtemplate": "å•ã„åˆã‚ã›çµæžœãŒã‚ã‚‹å ´åˆã€ãã®å‰ã«è¡¨ç¤ºã™ã‚‹ãƒ†ãƒ³ãƒ—レートã®åå‰",
+ "smw-paramdesc-outrotemplate": "å•ã„åˆã‚ã›çµæžœãŒã‚ã‚‹å ´åˆã€ãã®å¾Œã«è¡¨ç¤ºã™ã‚‹ãƒ†ãƒ³ãƒ—レートã®åå‰",
+ "smw-paramdesc-embedformat": "見出ã—ã®å®šç¾©ã«ä½¿ç”¨ã™ã‚‹ HTML ã‚¿ã‚°",
+ "smw-paramdesc-embedonly": "見出ã—を表示ã—ãªã„",
+ "smw-paramdesc-table-class": "表ã«è¨­å®šã™ã‚‹è¿½åŠ  CSS クラス",
+ "smw-paramdesc-table-transpose": "表ã®è¦‹å‡ºã—を縦ã«ã€çµæžœã‚’横ã«è¡¨ç¤ºã™ã‚‹",
+ "smw-paramdesc-rdfsyntax": "使用ã™ã‚‹ RDF 構文",
+ "smw-paramdesc-csv-sep": "使用ã™ã‚‹åŒºåˆ‡ã‚Šæ–‡å­—ã®æŒ‡å®š",
+ "smw-paramdesc-csv-valuesep": "使用ã™ã‚‹åŒºåˆ‡ã‚Šæ–‡å­—ã®æŒ‡å®š",
+ "smw-paramdesc-csv-bom": "出力ã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã®å…ˆé ­ã«BOM (エンディアンãƒã‚¹è­˜åˆ¥æ–‡å­—) ã‚’ã¤ã‘ã‚‹",
+ "smw-paramdesc-dsv-separator": "使用ã™ã‚‹åŒºåˆ‡ã‚Šæ–‡å­—",
+ "smw-paramdesc-dsv-filename": "DSV ファイルã®åå‰",
+ "smw-paramdesc-filename": "出力ファイルã®åå‰",
+ "smw-paramdesc-sort": "クエリã®ä¸¦ã¹æ›¿ãˆã«ä½¿ç”¨ã™ã‚‹ãƒ—ロパティ",
+ "smw-paramdesc-order": "クエリã®ä¸¦ã¹æ›¿ãˆã§ã®ä¸¦ã³é †",
+ "smw-paramdesc-searchlabel": "検索çµæžœã®ç¶šãã¸ã®ãƒªãƒ³ã‚¯æ–‡å­—列",
+ "smw-paramdesc-named_args": "テンプレートã«æ¸¡ã—ãŸå¼•æ•°ã«åå‰ã‚’付ã‘ã‚‹",
+ "smw-paramdesc-export": "書ã出ã—オプション",
+ "smw-paramdesc-jsonsyntax": "JSON 構文を使用ã—ã¾ã™",
+ "smw-printername-feed": "RSS / Atom フィード",
+ "smw-paramdesc-feedtype": "フィードã®ç¨®é¡ž",
+ "smw-paramdesc-feedtitle": "フィードã®è¡¨é¡Œã¨ã—ã¦ä½¿ç”¨ã™ã‚‹æ–‡å­—列",
+ "smw-paramdesc-feeddescription": "フィードã®èª¬æ˜Žã¨ã—ã¦ä½¿ç”¨ã™ã‚‹æ–‡å­—列",
+ "smw-paramdesc-feedpagecontent": "フィードã«ãƒšãƒ¼ã‚¸ã®æœ¬æ–‡ã‚‚å«ã‚ã‚‹",
+ "smw-label-feed-description": "$1 㮠$2 フィード",
+ "smw_iq_disabled": "ã“ã®ã‚¦ã‚£ã‚­ã§ã¯ã€æ„味的クエリã¯ç„¡åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚",
+ "smw_iq_moreresults": "…ãã®ä»–ã®çµæžœ",
+ "smw_parseerror": "指定ã—ãŸå€¤ã‚’ç†è§£ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_notitle": "「$1ã€ã¯ã“ã®ã‚¦ã‚£ã‚­ã§ã¯ãƒšãƒ¼ã‚¸åã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。",
+ "smw_noproperty": "「$1ã€ã¯ã“ã®ã‚¦ã‚£ã‚­ã§ã¯ãƒ—ロパティåã¨ã—ã¦ä½¿ç”¨ã§ãã¾ã›ã‚“。",
+ "smw_wrong_namespace": "ã“ã“ã§ã¯ã€ã€Œ$1ã€åå‰ç©ºé–“ã®ãƒšãƒ¼ã‚¸ã®ã¿ãŒè¨±å¯ã•ã‚Œã¦ã„ã¾ã™ã€‚",
+ "smw_manytypes": "プロパティã«å¯¾ã—ã¦è¤‡æ•°ã®åž‹ã‚’定義ã—ã¾ã—ãŸã€‚",
+ "smw_emptystring": "空文字列ã¯å—ã‘入れられã¾ã›ã‚“。",
+ "smw_notinenum": "「$1ã€ã¯ã€ã€Œ$3ã€ãƒ—ロパティã«å¯¾ã—ã¦[[Property:Allows value|許å¯ã•ã‚ŒãŸå€¤]]ã®ä¸€è¦§ ($2) ã®ä¸­ã«å­˜åœ¨ã—ã¾ã›ã‚“。",
+ "smw_noboolean": "「$1ã€ã¯ãƒ–ール値 (真/å½) ã¨ã—ã¦èªè­˜ã•ã‚Œã¾ã›ã‚“。",
+ "smw_true_words": "真,true,t,yes,y",
+ "smw_false_words": "å½,false,f,no,n",
+ "smw_nofloat": "「$1ã€ã¯æ•°å€¤ã§ã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smw_infinite": "「$1ã€ã‚’超ãˆã‚‹æ•°å€¤ã«ã¯å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。",
+ "smw_unitnotallowed": "「$1ã€ã¯ã“ã®ãƒ—ロパティã®æ¸¬å®šå€¤ã®æœ‰åŠ¹ãªå˜ä½ã¨ã—ã¦å®£è¨€ã•ã‚Œã¦ã„ã¾ã›ã‚“。",
+ "smw_nounitsdeclared": "ã“ã®ãƒ—ロパティã®æ¸¬å®šã®å˜ä½ã¯ä½•ã‚‚宣言ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_novalues": "値ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。",
+ "smw_nodatetime": "日付「$1ã€ã‚’ç†è§£ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_toomanyclosing": "クエリ内ã®ã€Œ$1ã€ã®æ•°ãŒå¤šã™ãŽã‚‹ã‚ˆã†ã§ã™ã€‚",
+ "smw_noclosingbrackets": "クエリ内ã®ã€Œ<nowiki>[[</nowiki>ã€ã«ã€å¯¾å¿œã™ã‚‹ã€Œ]]ã€ã§é–‰ã˜ã‚‰ã‚Œã¦ã„ãªã„ã‚‚ã®ãŒã‚ã‚Šã¾ã—ãŸã€‚",
+ "smw_misplacedsymbol": "記å·ã€Œ$1ã€ãŒã€ç„¡æ„味ãªå ´æ‰€ã§ä½¿ç”¨ã•ã‚Œã¾ã—ãŸã€‚",
+ "smw_unexpectedpart": "クエリã®ä¸€éƒ¨ã€Œ$1ã€ã‚’ç†è§£ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚\nçµæžœã¯äºˆæœŸã—ãªã„ã‚‚ã®ã«ãªã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw_emptysubquery": "有効ãªæ¡ä»¶ãŒãªã„サブクエリãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw_misplacedsubquery": "許å¯ã•ã‚Œã¦ã„ãªã„場所ã§ä½¿ç”¨ã•ã‚Œã¦ã„るサブクエリãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw_valuesubquery": "プロパティ「$1ã€ã®å€¤ã¯ã€ã‚µãƒ–クエリã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。",
+ "smw_badqueryatom": "クエリã®ä¸€éƒ¨ã€Œ<nowiki>[[…]]</nowiki>ã€ã‚’ç†è§£ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_propvalueproblem": "プロパティ「$1ã€ã®å€¤ã‚’ç†è§£ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "smw_noqueryfeature": "ã“ã®ã‚¦ã‚£ã‚­ãŒå¯¾å¿œã—ã¦ã„ãªã„クエリ機能ãŒã‚ã‚‹ãŸã‚ã€ã‚¯ã‚¨ãƒªã®ä¸€éƒ¨ã‚’破棄ã—ã¾ã—㟠($1)。",
+ "smw_noconjunctions": "ã“ã®ã‚¦ã‚£ã‚­ã¯ã‚¯ã‚¨ãƒªã®é€£è¨€ã«å¯¾å¿œã—ã¦ã„ãªã„ãŸã‚ã€ã‚¯ã‚¨ãƒªã®ä¸€éƒ¨ã‚’破棄ã—ã¾ã—㟠($1)。",
+ "smw_nodisjunctions": "ã“ã®ã‚¦ã‚£ã‚­ã¯ã‚¯ã‚¨ãƒªã®é¸è¨€ã«å¯¾å¿œã—ã¦ã„ãªã„ãŸã‚ã€ã‚¯ã‚¨ãƒªã®ä¸€éƒ¨ã‚’破棄ã—ã¾ã—㟠($1)。",
+ "smw_querytoolarge": "クエリã®é•·ã•ã¾ãŸã¯æ·±ã•ã«é–¢ã™ã‚‹ã“ã®ã‚¦ã‚£ã‚­ã§ã®åˆ¶é™ã®ãŸã‚ã€ä»¥ä¸‹ã®{{PLURAL:$2|クエリæ¡ä»¶}}ã¯è€ƒæ…®ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ: <code>$1</code>",
+ "smw_notemplategiven": "ã“ã®ã‚¯ã‚¨ãƒªå½¢å¼ã‚’動作ã•ã›ã‚‹ã«ã¯ã€å¼•æ•°ã€Œãƒ†ãƒ³ãƒ—レートã€ã®å€¤ã‚’指定ã—ã¦ãã ã•ã„。",
+ "smw_db_sparqlqueryproblem": "クエリã®çµæžœã‚’ SPARQL データベースã‹ã‚‰å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ã‚¨ãƒ©ãƒ¼ã¯ä¸€æ™‚çš„ãªã‚‚ã®ã§ã‚ã‚‹å¯èƒ½æ€§ã€ã¾ãŸã¯ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ ソフトウェアã®ãƒã‚°ã§ã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw_db_sparqlqueryincomplete": "クエリã«å¯¾ã™ã‚‹å¿œç­”ãŒå›°é›£ã™ãŽã‚‹ã“ã¨ãŒåˆ¤æ˜Žã—ãŸãŸã‚ã€ä¸­æ­¢ã•ã‚Œã¾ã—ãŸã€‚ã„ãã¤ã‹ã®çµæžœãŒå¤±ã‚ã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚å¯èƒ½ãªã‚‰ã°ã€ä»£ã‚ã‚Šã«ã‚ˆã‚Šå˜ç´”ãªã‚¯ã‚¨ãƒªã®ä½¿ç”¨ã‚’試ã—ã¦ãã ã•ã„。",
+ "smw_type_header": "型「$1ã€ã®ãƒ—ロパティ",
+ "smw_typearticlecount": "ã“ã®åž‹ã‚’使用ã—ã¦ã„ã‚‹ $1 {{PLURAL:$1|プロパティ}}を表示ã—ã¦ã„ã¾ã™ã€‚",
+ "smw_attribute_header": "プロパティ「$1ã€ã‚’使用ã—ã¦ã„るページ",
+ "smw_attributearticlecount": "ã“ã®ãƒ—ロパティを使用ã—ã¦ã„ã‚‹ $1 {{PLURAL:$1|件ã®ãƒšãƒ¼ã‚¸}}を表示ã—ã¦ã„ã¾ã™ã€‚",
+ "smw-propertylist-redirect-header": "åŒç¾©èªž",
+ "specialpages-group-smw_group": "Semantic MediaWiki",
+ "exportrdf": "ページを RDF ã«æ›¸ã出ã—",
+ "smw_exportrdf_docu": "ã“ã®ãƒšãƒ¼ã‚¸ã‚’使用ã™ã‚‹ã¨ã€ãƒšãƒ¼ã‚¸ã‹ã‚‰ãƒ‡ãƒ¼ã‚¿ã‚’ RDF å½¢å¼ã§å–å¾—ã§ãã¾ã™ã€‚ページを書ã出ã™ã«ã¯ã€ä¸‹ã®ãƒ†ã‚­ã‚¹ãƒˆãƒœãƒƒã‚¯ã‚¹ã«ã€ãƒšãƒ¼ã‚¸åを一行ã«ä¸€ã¤ãšã¤å…¥åŠ›ã—ã¦ãã ã•ã„。",
+ "smw_exportrdf_recursive": "ã™ã¹ã¦ã®é–¢é€£ãƒšãƒ¼ã‚¸ã‚’å†å¸°çš„ã«æ›¸ã出ã—ã¾ã™ã€‚çµæžœãŒå¤§ãããªã‚‹å ´åˆãŒã‚ã‚‹ãŸã‚注æ„ã—ã¦ãã ã•ã„。",
+ "smw_exportrdf_backlinks": "書ã出ã—対象ã®ãƒšãƒ¼ã‚¸ã‚’å‚ç…§ã™ã‚‹ãƒšãƒ¼ã‚¸ã‚‚ã™ã¹ã¦æ›¸ã出ã™ã€‚\n閲覧ã§ãã‚‹RDFを生æˆã™ã‚‹ã€‚",
+ "smw_exportrdf_lastdate": "指定ã—ãŸæ™‚点以é™ã«å¤‰æ›´ãŒã•ã‚Œã¦ã„ãªã„ページを書ã出ã•ãªã„。",
+ "smw_exportrdf_submit": "書ã出ã—",
+ "uriresolver": "URI リゾルãƒãƒ¼",
+ "properties": "プロパティ",
+ "smw_properties_docu": "ã“ã®ã‚¦ã‚£ã‚­ã§ã¯ä»¥ä¸‹ã®ãƒ—ロパティãŒä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚",
+ "smw_property_template": "$1ã€$2 åž‹ ($3 {{PLURAL:$3|回使用}})",
+ "smw_property_template_notype": "$1 ($2 件)",
+ "smw_propertylackspage": "ã™ã¹ã¦ã®ãƒ—ロパティã«ã¤ã„ã¦ã€å„プロパティã®ãƒšãƒ¼ã‚¸ã§è§£èª¬ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™!",
+ "smw_propertylackstype": "ã“ã®ãƒ—ロパティã«ã¯åž‹ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“ (ç¾æ™‚点ã§ã¯åž‹ $1 ã¨æƒ³å®šã—ã¾ã™)。",
+ "smw_propertyhardlyused": "ã“ã®ãƒ—ロパティã¯ã‚¦ã‚£ã‚­å†…ã§ã»ã¨ã‚“ã©ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã›ã‚“!",
+ "smw-property-name-invalid": "プロパティ $1 ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“ (無効ãªãƒ—ロパティå)。",
+ "smw-sp-property-searchform": "以下をå«ã‚€ãƒ—ロパティを表示:",
+ "smw-sp-property-searchform-inputinfo": "入力ã—ãŸå†…容ã¯å¤§æ–‡å­—・å°æ–‡å­—ãŒåŒºåˆ¥ã•ã‚Œã€çµžã‚Šè¾¼ã¿ã«ä½¿ç”¨ã•ã‚Œã¾ã™ã€‚æ¡ä»¶ã«è©²å½“ã—ãŸãƒ—ロパティã®ã¿ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚",
+ "smw-special-property-searchform-options": "設定",
+ "smw-special-wantedproperties-filter-unapproved": "未承èª",
+ "concepts": "コンセプト",
+ "smw-special-concept-header": "コンセプト一覧",
+ "smw-special-concept-count": "以下ã«{{PLURAL:$1|コンセプト}}を列挙ã—ã¾ã™ã€‚",
+ "smw-special-concept-empty": "コンセプトãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚",
+ "unusedproperties": "使ã‚ã‚Œã¦ã„ãªã„プロパティ",
+ "smw-unusedproperties-docu": "以下ã®ãƒ—ロパティã¯å­˜åœ¨ã—ã¾ã™ãŒã€ä»–ã®ãƒšãƒ¼ã‚¸ã§ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã›ã‚“。",
+ "smw-unusedproperty-template": "$1: åž‹ $2",
+ "wantedproperties": "望ã¾ã‚Œã¦ã„るプロパティ",
+ "smw-wantedproperties-docu": "ã“ã®ã‚¦ã‚£ã‚­ã§ã¯ä»¥ä¸‹ã®ãƒ—ロパティãŒä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™ãŒã€ã“れらã®ãƒ—ロパティを説明ã™ã‚‹ãƒšãƒ¼ã‚¸ã¯ã¾ã ã‚ã‚Šã¾ã›ã‚“。",
+ "smw-wantedproperty-template": "$1 ($2{{PLURAL:$2|件}})",
+ "smw_purge": "æ›´æ–°",
+ "smw-purge-failed": "リフレッシュ失敗",
+ "types": "åž‹",
+ "smw_types_docu": "[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes 利用å¯èƒ½ãªãƒ‡ãƒ¼ã‚¿åž‹]ã®ä¸€è¦§ã§ã™ã€‚å„々ã®[https://www.semantic-mediawiki.org/wiki/Help:Datatype åž‹]ã¯ã€ä¿ç®¡ãŠã‚ˆã³ã€å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒ—ロパティã‹ã‚‰éºä¼ã™ã‚‹æ€§è³ªã®è¡¨ç¤ºã®ç‚¹ã§ã€å€¤ã‚’特徴付ã‘る属性ã®ä¸€æ„ãªé›†åˆã«ç›¸å½“ã—ã¾ã™ã€‚",
+ "smw-special-types-no-such-type": "\"$1\" ã¯é©åˆ‡ãªãƒ‡ãƒ¼ã‚¿åž‹ã§ã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smw-statistics": "æ„味的統計",
+ "smw-statistics-property-instance": "プロパティ{{PLURAL:$1|値}} (åˆè¨ˆ)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|プロパティ}}]] (åˆè¨ˆ)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|プロパティ}} (åˆè¨ˆ)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|プロパティ}}]] (å°‘ãªãã¨ã‚‚1ã¤ã®å€¤ã§ä½¿ç”¨)",
+ "smw-statistics-property-page": "{{PLURAL:$1|プロパティ}}(ページã«ç™»éŒ²)",
+ "smw-statistics-property-type": "{{PLURAL:$1|プロパティ}}(データ型ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã¾ã™)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|クエリ}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|クエリ}}]]",
+ "smw-statistics-query-size": "クエリã®ã‚µã‚¤ã‚º",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|コンセプト}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|コンセプト}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|下ä½ã‚ªãƒ–ジェクト}}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|データ型}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|プロパティ値}} ([[Special:ProcessingErrorList|{{PLURAL:$1|ä¸é©åˆ‡ãªæ³¨è¨˜}}]])",
+ "smw-statistics-delete-count": "期é™åˆ‡ã‚Œ{{PLURAL:$1|エンティティ}} (除去マーク済ã¿)",
+ "smw_uri_doc": "URI リゾルãƒãƒ¼ã¯ [$1 W3C TAG ã§ã® httpRange-14 ã«é–¢ã™ã‚‹è­°è«–æˆæžœ]を実装ã—ã¦ã„ã¾ã™ã€‚\nã“ã‚Œã¯äººé–“ãŒã‚¦ã‚§ãƒ–サイトã«å‘ã‹ã‚ãªã„よã†ã«å–り計らã„ã¾ã™ã€‚",
+ "ask": "æ„味的検索",
+ "smw_ask_sortby": "列ã”ã¨ã«ä¸¦ã¹æ›¿ãˆ (ä»»æ„é¸æŠž)",
+ "smw_ask_ascorder": "昇順",
+ "smw_ask_descorder": "é™é †",
+ "smw-ask-order-rand": "ãŠã¾ã‹ã›è¡¨ç¤º",
+ "smw_ask_submit": "çµæžœã‚’å–å¾—",
+ "smw_ask_editquery": "クエリを編集",
+ "smw_add_sortcondition": "[並ã¹æ›¿ãˆæ¡ä»¶ã‚’追加]",
+ "smw_ask_hidequery": "クエリをéžè¡¨ç¤º (折りãŸãŸã‚€)",
+ "smw_ask_help": "クエリã®ãƒ˜ãƒ«ãƒ—",
+ "smw_ask_queryhead": "æ¡ä»¶",
+ "smw_ask_printhead": "表示ã™ã‚‹è¿½åŠ ãƒ‡ãƒ¼ã‚¿",
+ "smw_ask_printdesc": "(プロパティåã‚’å„行㫠1 ã¤è¿½åŠ ã—ã¦ãã ã•ã„)",
+ "smw_ask_format_as": "書å¼:",
+ "smw_ask_defaultformat": "既定",
+ "smw_ask_otheroptions": "ãã®ä»–ã®ã‚ªãƒ—ション",
+ "smw-ask-otheroptions-collapsed-info": "利用ã§ãるオプションをã™ã¹ã¦è¡¨ç¤ºã™ã‚‹ã«ã¯ + アイコンを使用ã—ã¦ãã ã•ã„",
+ "smw_ask_show_embed": "埋ã‚è¾¼ã¿ç”¨ã‚³ãƒ¼ãƒ‰ã‚’表示",
+ "smw_ask_hide_embed": "埋ã‚è¾¼ã¿ç”¨ã‚³ãƒ¼ãƒ‰ã‚’éžè¡¨ç¤º",
+ "smw_ask_embed_instr": "ã“ã®ã‚¯ã‚¨ãƒªã‚’ウィキページã«ã‚¤ãƒ³ãƒ©ã‚¤ãƒ³ã§åŸ‹ã‚込むã«ã¯ã€ä¸‹è¨˜ã®ã‚³ãƒ¼ãƒ‰ã‚’使用ã—ã¾ã™ã€‚",
+ "smw-ask-delete": "除去",
+ "smw-ask-sorting": "並ã¹æ›¿ãˆ",
+ "smw-ask-format-options": "フォーマットã¨ã‚ªãƒ—ション",
+ "smw-ask-parameters": "パラメータ",
+ "smw-ask-search": "検索",
+ "smw-ask-debug": "デãƒãƒƒã‚°",
+ "smw-ask-format": "フォーマット",
+ "smw-ask-format-selection-help": "詳細ãªèª¬æ˜Žã¯ã€$1ã®ãƒ˜ãƒ«ãƒ— ページをå‚ç…§ã—ã¦ãã ã•ã„。",
+ "smw-ask-query-search-info": "å•ã„åˆã‚ã› <code><nowiki>$1</nowiki></code> ã«å¯¾ã—㦠{{PLURAL:$3|1=<code>$2</code>(キャッシュ)|<code>$2</code>(キャッシュ)|<code>$2</code>}} より $4 {{PLURAL:$4|秒}}ã§å¿œç­”ãŒè¿”ã‚Šã¾ã—ãŸã€‚",
+ "searchbyproperty": "プロパティã«ã‚ˆã‚‹æ¤œç´¢",
+ "processingerrorlist": "処ç†ã‚¨ãƒ©ãƒ¼ã®ä¸€è¦§",
+ "propertylabelsimilarity": "プロパティラベル類似性レãƒãƒ¼ãƒˆ",
+ "smw-processingerrorlist-intro": "以下ã®ä¸€è¦§ã¯ [https://www.semantic-mediawiki.org/ Semantic MediaWiki] ã«é–¢é€£ã—ã¦ç™ºç”Ÿã—ãŸå‡¦ç†ã‚¨ãƒ©ãƒ¼ã«ã¤ã„ã¦ã®æ¦‚è¦ã‚’æä¾›ã—ã¾ã™ã€‚定期的ã«ã“ã®ä¸€è¦§ã‚’監視ã—ã€ç„¡åŠ¹ãªå€¤ã®æ³¨è¨˜ã‚’訂正ã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚",
+ "smw_sbv_docu": "指定ã—ãŸãƒ—ロパティã¨å€¤ã‚’æŒã¤ã™ã¹ã¦ã®ãƒšãƒ¼ã‚¸ã‚’検索ã—ã¾ã™ã€‚",
+ "smw_sbv_novalue": "ãã®ãƒ—ロパティã«å¯¾ã—ã¦æœ‰åŠ¹ãªå€¤ã‚’入力ã™ã‚‹ã‹ã€ã€Œ$1ã€ã®ã™ã¹ã¦ã®ãƒ—ロパティ値を確èªã—ã¦ãã ã•ã„。",
+ "smw_sbv_displayresult": "プロパティ「$1ã€ã®å€¤ãŒã€Œ$2ã€ã§ã‚ã‚‹ã™ã¹ã¦ã®ãƒšãƒ¼ã‚¸ã®ä¸€è¦§",
+ "smw_sbv_displayresultfuzzy": "プロパティ「$1ã€ã®å€¤ãŒã€Œ$2ã€ã§ã‚ã‚‹ã™ã¹ã¦ã®ãƒšãƒ¼ã‚¸ã®ä¸€è¦§ã€‚\nçµæžœãŒã”ãå°‘æ•°ã ã£ãŸãŸã‚ã€è¿‘ã„値も表示ã—ã¦ã„ã¾ã™ã€‚",
+ "smw_sbv_property": "プロパティ:",
+ "smw_sbv_value": "値:",
+ "smw_sbv_submit": "çµæžœã‚’å–å¾—",
+ "browse": "ウィキã®é–²è¦§",
+ "smw_browselink": "プロパティを閲覧",
+ "smw_browse_article": "閲覧を開始ã™ã‚‹ãƒšãƒ¼ã‚¸ã®åå‰ã‚’入力ã—ã¦ãã ã•ã„。",
+ "smw_browse_go": "表示",
+ "smw_browse_show_incoming": "ã“ã®ãƒšãƒ¼ã‚¸ã«ãƒªãƒ³ã‚¯ã—ã¦ã„るプロパティを表示",
+ "smw_browse_hide_incoming": "ã“ã®ãƒšãƒ¼ã‚¸ã«ãƒªãƒ³ã‚¯ã—ã¦ã„るプロパティを隠ã™",
+ "smw_browse_no_outgoing": "ã“ã®ãƒšãƒ¼ã‚¸ã«ã¯ãƒ—ロパティã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smw_browse_no_incoming": "ã“ã®ãƒšãƒ¼ã‚¸ã«ãƒªãƒ³ã‚¯ã—ã¦ã„るプロパティã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smw_inverse_label_default": "$1ã§ã‚ã‚‹",
+ "smw_inverse_label_property": "逆プロパティã®ãƒ©ãƒ™ãƒ«",
+ "pageproperty": "ページã®ãƒ—ロパティã®æ¤œç´¢",
+ "smw_pp_docu": "指定ã—ãŸãƒšãƒ¼ã‚¸ã®ãƒ—ロパティã«ã¤ã„ã¦ã™ã¹ã¦ã®å€¤ã‚’検索ã—ã¾ã™ã€‚\nページã¨ãƒ—ロパティã®ä¸¡æ–¹ã‚’入力ã—ã¦ãã ã•ã„。",
+ "smw_pp_from": "対象ページ",
+ "smw_pp_type": "プロパティ",
+ "smw_pp_submit": "çµæžœã‚’å–å¾—",
+ "smw_result_prev": "å‰ã¸",
+ "smw_result_next": "次ã¸",
+ "smw_result_results": "çµæžœ",
+ "smw_result_noresults": "該当çµæžœã¯ã‚ã‚Šã¾ã›ã‚“。",
+ "smwadmin": "管ç†æ©Ÿèƒ½",
+ "smw-admin-statistics-querycache-title": "クエリーキャッシュ統計",
+ "smw-admin-setupsuccess": "ストレージエンジンãŒæ§‹ç¯‰ã•ã‚Œã¾ã—ãŸã€‚",
+ "smw_smwadmin_return": "$1 ã«æˆ»ã‚‹",
+ "smw_smwadmin_updatestarted": "æ„味的データを最新ã®çŠ¶æ…‹ã«ã™ã‚‹ãŸã‚ã®æ–°ã—ã„更新プロセスを開始ã—ã¾ã—ãŸã€‚\næ ¼ç´æ¸ˆã¿ã®ãƒ‡ãƒ¼ã‚¿ã¯ã™ã¹ã¦å¿…è¦ã«å¿œã˜ã¦å†æ§‹ç¯‰ã¾ãŸã¯ä¿®å¾©ã•ã‚Œã¾ã™ã€‚\nã“ã®ç‰¹åˆ¥ãƒšãƒ¼ã‚¸ã§æ›´æ–°ã®çŠ¶æ³ã‚’追ã†ã“ã¨ãŒã§ãã¾ã™ã€‚",
+ "smw_smwadmin_updatenotstarted": "æ—¢ã«å®Ÿè¡Œä¸­ã®æ›´æ–°ãƒ—ロセスãŒã‚ã‚Šã¾ã™ã€‚\næ–°ãŸã«ä½œæˆã—ãªã„ã§ãã ã•ã„。",
+ "smw_smwadmin_updatestopped": "既存ã®ã™ã¹ã¦ã®æ›´æ–°ãƒ—ロセスã¯åœæ­¢ã•ã‚Œã¾ã—ãŸã€‚",
+ "smw_smwadmin_updatenotstopped": "実行中ã®æ›´æ–°ãƒ—ロセスをåœæ­¢ã™ã‚‹ã«ã¯ã€æœ¬å½“ã«ç†è§£ã—ã¦ã„ã‚‹ã“ã¨ã‚’示ã™ãŸã‚ã«ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹ã‚’é¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw-admin-docu": "ã“ã®ç‰¹åˆ¥ãƒšãƒ¼ã‚¸ã¯ <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã€ã‚¢ãƒƒãƒ—グレードã€ä¿å®ˆãŠã‚ˆã³ä½¿ç”¨ã‚’支æ´ã—ã¾ã™ã€‚ã¾ãŸã€çµ±è¨ˆã¨åŒæ§˜ã«ã•ã‚‰ãªã‚‹ç®¡ç†è€…å‘ã‘機能ãŠã‚ˆã³ã‚¿ã‚¹ã‚¯ã‚’æä¾›ã—ã¾ã™ã€‚\n管ç†æ©Ÿèƒ½ã‚’実行ã™ã‚‹å‰ã«ã€é‡è¦ãªãƒ‡ãƒ¼ã‚¿ã‚’ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã™ã‚‹ã‚ˆã†ã«ã—ã¦ãã ã•ã„。",
+ "smw-admin-environment": "ソフトウェア環境",
+ "smw-admin-db": "データベースメンテナンス",
+ "smw-admin-dbdocu": "Semantic MediaWiki ã¯æ„味的データを格ç´ã™ã‚‹ãŸã‚ã€MediaWiki ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«å¯¾ã—ã¦ã„ãã¤ã‹ã®æ‹¡å¼µã‚’å¿…è¦ã¨ã—ã¾ã™ã€‚\n以下ã®æ©Ÿèƒ½ã¯ã‚ãªãŸã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒé©åˆ‡ã«æ§‹ç¯‰ã•ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確実ã«ã™ã‚‹ã‚‚ã®ã§ã™ã€‚\nã“ã®ä½œæ¥­ã§ãªã•ã‚ŒãŸå¤‰æ›´ã¯ MediaWiki ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®ãã®ä»–ã®éƒ¨åˆ†ã«ã¯å½±éŸ¿ã‚’与ãˆãšã€å¿…è¦ãªã‚‰ç°¡å˜ã«å–り消ã—ãŒã§ãã¾ã™ã€‚\nã“ã®æ©Ÿèƒ½ã¯è¤‡æ•°å›žå®Ÿè¡Œã—ã¦ã‚‚何ã®å®³ã‚‚ã‚ã‚Šã¾ã›ã‚“ãŒã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã‚‚ã—ãã¯ã‚¢ãƒƒãƒ—グレード時ã«ä¸€åº¦ã ã‘実行ã™ã‚Œã°å分ã§ã™ã€‚",
+ "smw-admin-permissionswarn": "SQL エラーを出ã—ã¦å‡¦ç†ãŒå¤±æ•—ã™ã‚‹å ´åˆã€ãŠãらãã‚ãªãŸã®ã‚¦ã‚£ã‚­ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ ユーザー (\"LocalSettings.php\" を確èªã—ã¦ãã ã•ã„) ã«å¿…è¦ãªæ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。\n\"LocalSettings.php\" ã«ä¸€æ™‚çš„ã«ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã® root ã®ãƒ­ã‚°ã‚¤ãƒ³æƒ…報を記入ã—ã¦ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãƒ†ãƒ¼ãƒ–ルを作æˆãŠã‚ˆã³å‰Šé™¤ã™ã‚‹ãŸã‚ã®è¿½åŠ æ¨©é™ã‚’与ãˆã‚‹ã‹ã€<code>setupStore.php</code> ã®èªè¨¼æƒ…報を使用ã§ãるメンテナンススクリプト <code>setupStore.php</code> を使用ã—ã¦ãã ã•ã„。",
+ "smw-admin-dbbutton": "テーブルをåˆæœŸåŒ–ã¾ãŸã¯ã‚¢ãƒƒãƒ—グレード",
+ "smw-admin-announce": "ウィキã®ç™ºè¡¨",
+ "smw-admin-announce-text": "ウィキãŒå…¬é–‹ã•ã‚Œã¦ã„ã‚‹å ´åˆã€ã‚¦ã‚£ã‚­ã‚’監視ã™ã‚‹ã‚¦ã‚£ã‚­ã§ã‚ã‚‹ <a href=\"https://wikiapiary.com\">WikiApiary</a> ã«ç™»éŒ²ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> ã¯éžæŽ¨å¥¨ã¨ãªã‚Šã€$2 ã§å‰Šé™¤ã•ã‚Œã‚‹äºˆå®šã§ã™ã€‚",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1<code> 㯠<code>$2<code> ã«ç½®ãæ›ãˆã‚‰ã‚Œã¾ã™",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> 㯠$2 ã§å‰Šé™¤ã•ã‚Œã¾ã™",
+ "smw-admin-deprecation-notice-title-replacement": "代替ã¾ãŸã¯åå‰ãŒå¤‰æ›´ã•ã‚ŒãŸè¨­å®š",
+ "smw-admin-deprecation-notice-title-removal": "削除ã•ã‚ŒãŸè¨­å®š",
+ "smw-admin-deprecation-notice-title-removal-explanation": "下記ã®è¨­å®šé …ç›®ã¯ä»¥å‰ã®ãƒªãƒªãƒ¼ã‚¹ã§å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚ã—ã‹ã—ã€ã“ã®ã‚¦ã‚£ã‚­ã§ã¾ã ä½¿ç”¨ã—ã¦ã„ã‚‹ã“ã¨ã‚’検出ã—ã¾ã—ãŸã€‚",
+ "smw-smwadmin-refresh-title": "データã®ä¿®å¾©ã¨æ›´æ–°",
+ "smw_smwadmin_datarefresh": "データã®å†æ§‹ç¯‰",
+ "smw_smwadmin_datarefreshdocu": "ウィキã®ç¾åœ¨ã®å†…容ã«åŸºã¥ã„ã¦ã€Semantic MediaWiki ã®å…¨ãƒ‡ãƒ¼ã‚¿ã‚’復旧ã§ãã¾ã™ã€‚\nã“ã®æ©Ÿèƒ½ã¯ç ´æã—ãŸãƒ‡ãƒ¼ã‚¿ã‚’修復ã™ã‚‹å ´åˆã‚„ã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®ã‚¢ãƒƒãƒ—グレードã§å†…部形å¼ãŒå¤‰ã‚ã£ãŸéš›ã«ãƒ‡ãƒ¼ã‚¿ã‚’移行ã™ã‚‹å ´åˆãªã©ã«æœ‰ç”¨ã§ã™ã€‚\nã“ã®æ›´æ–°ã¯1ページãšã¤å®Ÿè¡Œã•ã‚Œã€ç›´ã¡ã«ã¯å®Œäº†ã—ã¾ã›ã‚“。\n以下ã«æ›´æ–°ãŒé€²è¡Œä¸­ã‹ã©ã†ã‹ã‚’表示ã—ã¾ã™ã€‚更新を開始ã¾ãŸã¯åœæ­¢ã§ãã¾ã™ (サイト管ç†è€…ãŒæ©Ÿèƒ½ã‚’無効ã«ã—ã¦ã„ãªã„å ´åˆã®ã¿)。",
+ "smw_smwadmin_datarefreshprogress": "<strong>æ›´æ–°ãŒæ—¢ã«é€²è¡Œä¸­ã§ã™ã€‚</strong>\n利用者ãŒã‚¦ã‚£ã‚­ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ãŸã³ã«å°‘é‡ã®å¡Šã®ãƒ‡ãƒ¼ã‚¿ã‚’æ›´æ–°ã™ã‚‹ã®ã¿ã§ã‚ã‚‹ãŸã‚ã€æ›´æ–°ãŒã‚†ã£ãã‚Šã¨ã—ã‹é€²ã¾ãªã„ã®ã¯æ­£å¸¸ãªå‹•ä½œã§ã™ã€‚\nã“ã®æ›´æ–°ã‚’より早ã終ãˆã‚‹ã«ã¯ã€MediaWiki ã®ãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ スクリプト <code>runJobs.php</code> を実行ã—ã¦ãã ã•ã„ (1ã¤ã®ãƒãƒƒãƒã§è¡Œã‚れる更新ã®æ•°ã‚’制é™ã™ã‚‹ã«ã¯ <code>--maxjobs 1000</code> オプションを使用ã—ã¦ãã ã•ã„)。\nç¾åœ¨ã®æ›´æ–°ã®æŽ¨å®šé€²æ—:",
+ "smw_smwadmin_datarefreshbutton": "計画ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿å†æ§‹ç¯‰å‡¦ç†",
+ "smw_smwadmin_datarefreshstop": "ã“ã®æ›´æ–°ã‚’åœæ­¢",
+ "smw_smwadmin_datarefreshstopconfirm": "ã¯ã„ã€{{GENDER:$1|ã‚‚ã¡ã‚ã‚“}}ã§ã™ã€‚",
+ "smw-admin-job-scheduler-note": "ã»ã¨ã‚“ã©ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティã¯ã€ã‚¿ã‚¹ã‚¯ãŒä¸€æ‹¬ã§å®Ÿè¡Œã•ã‚Œã‚‹ã‚ˆã†ã«ã‚¸ãƒ§ãƒ–ã¨ã—ã¦ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã•ã‚Œã¾ã™ã€‚処ç†ã®è¨ˆç”»ã¨å®Œäº†ã¯ã‚¸ãƒ§ãƒ–スケジューラãŒæ‹…ã„ã¾ã™ã€‚ãã®ãŸã‚ã€ãƒ¡ãƒ³ãƒ†ãƒŠãƒ³ã‚¹ã‚¹ã‚¯ãƒªãƒ—ト <code>runJobs.php</code> ã‚ã‚‹ã„㯠<code>$wgRunJobsAsync</code> ã®é©åˆ‡ãªç®¡ç†ãŒé‡è¦ã§ã™ã€‚",
+ "smw-admin-propertystatistics-button": "計画ã•ã‚ŒãŸçµ±è¨ˆã®å†æ§‹ç¯‰å‡¦ç†",
+ "smw-admin-fulltext-title": "全文検索データã®å†æ§‹ç¯‰å‡¦ç†",
+ "smw-admin-fulltext-button": "計画ã•ã‚ŒãŸå…¨æ–‡æ¤œç´¢ãƒ‡ãƒ¼ã‚¿ã®å†æ§‹ç¯‰å‡¦ç†",
+ "smw-admin-support": "支æ´ã‚’å¾—ã‚‹",
+ "smw-admin-supportdocu": "å•é¡ŒãŒç™ºç”Ÿã—ãŸã¨ãã«ã•ã¾ã–ã¾ãªãƒªã‚½ãƒ¼ã‚¹ãŒåŠ©ã‘ã¨ãªã‚‹ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“:",
+ "smw-admin-installfile": "インストールã«å•é¡Œã‚’発見ã—ãŸå ´åˆã€<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL ファイル</a>ã«ã‚る指é‡ã‚’確èªã™ã‚‹ã“ã¨ã‹ã‚‰å§‹ã‚ã¦ãã ã•ã„。",
+ "smw-admin-smwhomepage": "Semantic MediaWiki 利用者用ã®å®Œå…¨ãªãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã¯ <b><a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_-_cover_page_(ja)\">semantic-mediawiki.org</a></b> ã«ã‚ã‚Šã¾ã™ã€‚",
+ "smw-admin-bugsreport": "ãƒã‚°ã¯ <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">å•é¡Œè¿½è·¡ãƒ„ール</a>ã€<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">ãƒã‚°ã®å ±å‘Š</a>ページã§å ±å‘Šã§ãã¾ã™ã€‚ã“ã®ãƒšãƒ¼ã‚¸ã«ã¯ã€åŠ¹çŽ‡çš„ãªèª²é¡Œãƒ¬ãƒãƒ¼ãƒˆã®ä½œæˆæ–¹æ³•ã«é–¢ã™ã‚‹ã‚¬ã‚¤ãƒ€ãƒ³ã‚¹ãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw-admin-questions": "ã•ã‚‰ãªã‚‹è³ªå•ã‚„æ案ãŒã‚ã‚‹å ´åˆã¯ã€<a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Semantic MediaWiki 利用者フォーラム</a>ã§ã®è­°è«–ã«å‚加ã—ã¦ãã ã•ã„。",
+ "smw-admin-supplementary-operational-statistics-cache-title": "キャッシュ統計",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-functions": "使用å¯èƒ½ãªé–¢æ•°",
+ "smw-admin-supplementary-elastic-settings-title": "設定",
+ "smw-admin-supplementary-elastic-statistics-title": "統計",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "最後ã«åŒæœŸã—ãŸæ™‚刻: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "æ›´æ–°é–“éš”: $1",
+ "smw-property-label-similarity-title": "プロパティラベル類似性レãƒãƒ¼ãƒˆ",
+ "smw-property-label-similarity-threshold": "閾値:",
+ "smw_adminlinks_datastructure": "データ構造",
+ "smw_adminlinks_displayingdata": "データã®è¡¨ç¤º",
+ "smw_adminlinks_inlinequerieshelp": "インラインクエリã®ãƒ˜ãƒ«ãƒ—",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|利用者|システム}}ãŒå®šç¾©ã—ãŸãƒ—ロパティ",
+ "smw-createproperty-isproperty": "ã“ã‚Œã¯åž‹ $1 ã®ãƒ—ロパティã§ã™ã€‚",
+ "smw-createproperty-allowedvals": "ã“ã®ãƒ—ロパティãŒå–れる{{PLURAL:$1|値}}:",
+ "smw-paramdesc-category-delim": "区切り文字",
+ "smw-paramdesc-category-template": "é …ç›®ã®æ•´å½¢ã«ä½¿ç”¨ã™ã‚‹ãƒ†ãƒ³ãƒ—レート",
+ "smw-paramdesc-category-userparam": "テンプレートã«æ¸¡ã™ãƒ‘ラメーター",
+ "smw-info-par-message": "表示ã™ã‚‹ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã§ã™ã€‚",
+ "smw-info-par-icon": "表示ã™ã‚‹ã‚¢ã‚¤ã‚³ãƒ³ (「infoã€ã¨ã€Œwarningã€ã®ã„ãšã‚Œã‹) ã§ã™ã€‚",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "全般オプション",
+ "prefs-ask-options": "æ„味的検索オプション",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki](ãŠã‚ˆã³é–¢é€£ã™ã‚‹æ‹¡å¼µæ©Ÿèƒ½ï¼‰ã¯ã€ä¸€éƒ¨ã®é¸æŠžã•ã‚ŒãŸæ©Ÿèƒ½ã®å€‹åˆ¥ã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚ºã‚’æä¾›ã—ã¾ã™ã€‚ 詳細ãªèª¬æ˜Žã«ã¤ã„ã¦ã¯ã€[https://www.semantic-mediawiki.org/wiki/Help:User_preferences ヘルプ]ã‚’å‚ç…§ã—ã¦ãã ã•ã„。",
+ "smw-prefs-ask-options-tooltip-display": "パラメーター文字列を情報ツールãƒãƒƒãƒ—ã¨ã—ã¦è¡¨ç¤º",
+ "smw-ui-tooltip-title-property": "プロパティ",
+ "smw-ui-tooltip-title-quantity": "å˜ä½ã®æ›ç®—",
+ "smw-ui-tooltip-title-info": "情報",
+ "smw-ui-tooltip-title-service": "サービスã®ãƒªãƒ³ã‚¯",
+ "smw-ui-tooltip-title-warning": "警告",
+ "smw-ui-tooltip-title-error": "エラー",
+ "smw-ui-tooltip-title-parameter": "パラメーター",
+ "smw-ui-tooltip-title-event": "イベント",
+ "smw-ui-tooltip-title-note": "注記",
+ "smw-ui-tooltip-title-legend": "凡例",
+ "smw_unknowntype": "ã“ã®ãƒ—ロパティã®åž‹ã¯ç„¡åŠ¹ã§ã™",
+ "smw-concept-cache-text": "ã“ã®ã‚³ãƒ³ã‚»ãƒ—トã«ã¤ã„ã¦ã¯åˆè¨ˆ $1 {{PLURAL:$1|件ã®ãƒšãƒ¼ã‚¸}}ãŒã‚ã‚Šã€æœ€çµ‚更新㯠$2 $3ã§ã™ã€‚",
+ "smw_concept_header": "コンセプト「$1ã€ã®ãƒšãƒ¼ã‚¸",
+ "smw_conceptarticlecount": "以下㫠$1 {{PLURAL:$1|件ã®ãƒšãƒ¼ã‚¸}}を表示ã—ã¦ã„ã¾ã™ã€‚",
+ "right-smw-admin": "アクセス管ç†ä½œæ¥­ (Semantic MediaWiki)",
+ "right-smw-patternedit": "許å¯ã•ã‚ŒãŸæ­£è¦è¡¨ç¾ãŠã‚ˆã³ãƒ‘ターンã®ç¶­æŒã®ãŸã‚ã®ç·¨é›†ã‚¢ã‚¯ã‚»ã‚¹ (Semantic MediaWiki)",
+ "right-smw-pageedit": "<code>Is edit protected</code> ãŒä»˜ä¸Žã•ã‚ŒãŸãƒšãƒ¼ã‚¸ã«å¯¾ã™ã‚‹ç·¨é›†ã‚¢ã‚¯ã‚»ã‚¹ (Semantic MediaWiki)",
+ "action-smw-pageedit": "<code>Is edit protected</code> ãŒä»˜ä¸Žã•ã‚ŒãŸãƒšãƒ¼ã‚¸ã®ç·¨é›† (Semantic MediaWiki)",
+ "group-smwadministrator": "管ç†è€… (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|管ç†è€… (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:管ç†è€… (Semantic MediaWiki)",
+ "group-smwcurator": "キュレーター (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|キュレーター (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:キュレーター (Semantic MediaWiki)",
+ "action-smw-admin": "Semantic MediaWiki 管ç†ä½œæ¥­ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹",
+ "smw-property-predefined-default": "「$1ã€ã¯å®šç¾©æ¸ˆã¿ã®ãƒ—ロパティã§ã™ã€‚",
+ "smw-sp-properties-docu": "ã“ã®ãƒšãƒ¼ã‚¸ã¯ã“ã®ã‚¦ã‚£ã‚­ã«ãŠã‘ã‚‹[https://www.semantic-mediawiki.org/wiki/Property プロパティ]ãŠã‚ˆã³ãã®ä½¿ç”¨å›žæ•°ã®ä¸€è¦§ã‚’表示ã—ã¾ã™ã€‚最新ã®å›žæ•°çµ±è¨ˆã‚’表示ã™ã‚‹ãŸã‚ã«ã€[https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics プロパティ統計]メンテナンススクリプトを定期的ã«å®Ÿè¡Œã™ã‚‹ã“ã¨ã‚’ãŠå‹§ã‚ã—ã¾ã™ã€‚ç•°ãªã‚‹è¡¨ç¤ºã‚’è¡Œã†ã«ã¯ã€[[Special:UnusedProperties|使ã‚ã‚Œã¦ã„ãªã„プロパティ]]ã‚ã‚‹ã„ã¯[[Special:WantedProperties|望ã¾ã‚Œã¦ã„るプロパティ]]特別ページをå‚ç…§ã—ã¦ãã ã•ã„。",
+ "smw-sp-properties-header-label": "プロパティ一覧",
+ "smw-admin-settings-docu": "Semantic MediaWiki 環境ã«é–¢é€£ã™ã‚‹ã™ã¹ã¦ã®æ—¢å®šã¨ãƒ­ãƒ¼ã‚«ãƒ«ã®è¨­å®šã®ä¸€è¦§ã‚’表示ã—ã¾ã™ã€‚個別ã®è¨­å®šã®è©³ç´°ã«ã¤ã„ã¦ã¯ã€[https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] ヘルプページをå‚ç…§ã—ã¦ãã ã•ã„。",
+ "smw-sp-admin-settings-button": "設定ã®ä¸€è¦§ã‚’生æˆ",
+ "smw-admin-idlookup-title": "オブジェクト ID 検索",
+ "smw-admin-idlookup-docu": "ã“ã®ç¯€ã§ã¯ã€Semantic MediaWikiã«ãŠã‘る個々ã®ã‚¨ãƒ³ãƒ†ã‚£ãƒ†ã‚£ï¼ˆã‚¦ã‚£ã‚­ãºãƒ¼ã‚¸ã€ã‚µãƒ–オブジェクトã€ãƒ—ロパティ等ã©ï¼‰ã«é–¢ã™ã‚‹æŠ€è¡“çš„ãªè©³ç´°ã‚’表示ã—ã¾ã™ã€‚入力ã§ã¯ã€é¸æŠžãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã«ä¸€è‡´ã™ã‚‹IDã¾ãŸã¯æ–‡å­—列を使用ã§ãã¾ã™ã€‚ã©ã®IDå‚照もMediaWikiページや版IDã¨é–“é•ãˆã¦ã¯ã„ã‘ãªã„ã“ã¨ã«æ³¨æ„ã—ã¦ãã ã•ã„。",
+ "smw-admin-iddispose-title": "IDã®ç ´æ£„",
+ "smw-admin-iddispose-docu": "破棄æ“作ã¯åˆ¶é™ã•ã‚Œã¦ãŠã‚‰ãšã€ç¢ºèªã•ã‚ŒãŸå ´åˆã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ã‹ã‚‰å†…部オブジェクト ID を消去ã—ã¾ã™ã€‚ã“ã®ã‚¿ã‚¹ã‚¯ã¯å¿…ãš[https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal ドキュメント]ã‚’å‚ç…§ã—ã¦ã‹ã‚‰'''注æ„ã—ã¦'''実行ã—ã¦ãã ã•ã„。",
+ "smw-admin-iddispose-done": "ID「$1ã€ã¯ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ã‹ã‚‰é™¤åŽ»ã•ã‚Œã¾ã—ãŸã€‚",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "概è¦",
+ "smw-livepreview-loading": "読ã¿è¾¼ã¿ä¸­...",
+ "smw-sp-searchbyproperty-description": "ã“ã®ãƒšãƒ¼ã‚¸ã¯ã€ãƒ—ロパティã¨åå‰ã‚’付ã‘られãŸå€¤ã«ã‚ˆã£ã¦è¨˜è¿°ã•ã‚ŒãŸã‚¨ãƒ³ãƒˆãƒªã‚’見ã¤ã‘ã‚‹ãŸã‚ã®ç°¡ç´ ãª[https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces 閲覧インターフェース]ã‚’æä¾›ã—ã¾ã™ã€‚[[Special:PageProperty|ページã®ãƒ—ロパティã®æ¤œç´¢]]ã‚„[[Special:Ask|å•ã„åˆã‚ã›ã‚¯ã‚¨ãƒªãƒ“ルダー]]ã¨ã„ã£ãŸä»–ã®æ¤œç´¢ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹ã‚‚利用å¯èƒ½ã§ã™ã€‚",
+ "smw-sp-searchbyproperty-resultlist-header": "çµæžœã®ä¸€è¦§",
+ "smw-sp-searchbyproperty-nonvaluequery": "プロパティ「$1ã€ãŒå‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„る値ã®ä¸€è¦§ã§ã™ã€‚",
+ "smw-sp-searchbyproperty-valuequery": "値「$2ã€ãŒä»˜ã‘られãŸãƒ—ロパティ「$1ã€ã®ã‚るページã®ä¸€è¦§ã§ã™ã€‚",
+ "smw-editpage-annotation-enabled": "ã“ã®ãƒšãƒ¼ã‚¸ã¯æ„味的テキスト内注記(例:<nowiki>[[Is specified as::World Heritage Site]]</nowiki>)をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã™ã€‚ã“ã‚Œã¯æ§‹é€ åŒ–ã•ã‚Œå•ã„åˆã‚ã›å¯èƒ½ãªã‚³ãƒ³ãƒ†ãƒ³ãƒ„を構築ã™ã‚‹ãŸã‚ã®ã‚‚ã®ã§ã€Semantic MediaWiki ãŒæä¾›ã—ã¦ã„ã¾ã™ã€‚注記や #ask パーサー関数ã«ã¤ã„ã¦ã®å®Œå…¨ãªèª¬æ˜Žã¯ [https://www.semantic-mediawiki.org/wiki/Help:Getting_started getting started]ã€[https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation in-text annotation]ã€ã¾ãŸã¯ [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries] ヘルプページをã”覧ãã ã•ã„。",
+ "smw-editpage-annotation-disabled": "åå‰ç©ºé–“ã®åˆ¶é™ã«ã‚ˆã‚Šã€ã“ã®ãƒšãƒ¼ã‚¸ã§ã¯æ„味的テキスト内注記を行ãˆã¾ã›ã‚“。ã“ã®åå‰ç©ºé–“ã«ãŠã„ã¦æœ‰åŠ¹åŒ–ã™ã‚‹æ–¹æ³•ã«ã¤ã„ã¦ã®è©³ç´°ã¯ [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuration] ヘルプページã«è¨˜è¼‰ã•ã‚Œã¦ã„ã¾ã™ã€‚",
+ "smw-search-syntax": "構文",
+ "smw-search-profile-sort-recent": "最新",
+ "smw-search-profile-extended-section-sort": "並ã³é †",
+ "smw-search-profile-extended-section-namespace": "åå‰ç©ºé–“",
+ "log-name-smw": "Semantic MediaWiki ログ",
+ "log-show-hide-smw": "Semantic MediaWiki ログを$1",
+ "log-description-smw": "Semantic MediaWiki ã¨ãã®æ§‹æˆè¦ç´ ã«ã‚ˆã£ã¦å ±å‘Šã•ã‚ŒãŸã€[https://www.semantic-mediawiki.org/wiki/Help:Logging 有効ãªã‚¤ãƒ™ãƒ³ãƒˆã®ç¨®é¡ž]ã«ã¤ã„ã¦ã®æ´»å‹•ã§ã™ã€‚",
+ "smw-datavalue-import-invalid-value": "「$1ã€ã¯æœ‰åŠ¹ãªæ›¸å¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“。\"åå‰ç©ºé–“\":\"識別å­\"(例:「foaf:nameã€ï¼‰ã‹ã‚‰æˆã‚‹ã“ã¨ãŒè¦æ±‚ã•ã‚Œã¦ã„ã¾ã™ã€‚",
+ "smw-property-predefined-sobj": "「$1ã€ã¯ã€[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]ã«ã‚ˆã£ã¦æä¾›ã•ã‚Œã‚‹ã€é€šå¸¸ã® Wiki ã®ãƒšãƒ¼ã‚¸ã¨åŒæ§˜ã®ãƒ—ロパティ値ã®å‰²ã‚Šå½“ã¦ã‚’溜ã‚ã‚‹ã“ã¨ãŒã§ãã€[https://www.semantic-mediawiki.org/wiki/Help:Container コンテナ]ã®æ§‹é€ ã‚’示ã™å®šç¾©æ¸ˆã¿ã®ãƒ—ロパティã§ã™ã€‚",
+ "smw-datavalue-invalid-number": "「$1ã€ã¯æ•°å€¤ã¨ã—ã¦è§£é‡ˆã§ãã¾ã›ã‚“。",
+ "smw-query-condition-circular": "「$1ã€ã«ãŠã„ã¦å¾ªç’°æ¡ä»¶ã®å¯èƒ½æ€§ãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚",
+ "smw-types-list": "データ型ã®ä¸€è¦§",
+ "smw-types-default": "「$1ã€ã¯çµ„ã¿è¾¼ã¿ã®ãƒ‡ãƒ¼ã‚¿åž‹ã§ã™ã€‚",
+ "smw-types-help": "追加ã®æƒ…å ±ã¨ä¾‹ã‚’[https://www.semantic-mediawiki.org/wiki/Help:Type_$1 ヘルプページ]ã§è¦‹ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚",
+ "smw-type-anu": "「$1ã€ã¯ [[Special:Types/URL|URL]] データ型ã®ç•°å½¢ã§ã™ã€‚主ã«ã€Œowl:AnnotationPropertyã€ã®å‡ºåŠ›å®šç¾©ã«ä½¿ã‚ã‚Œã¾ã™ã€‚",
+ "smw-type-boo": "「$1ã€ã¯çœŸå½å€¤ã‚’記述ã™ã‚‹ãŸã‚ã®ãƒ—リミティブデータ型ã§ã™ã€‚",
+ "smw-type-cod": "「$1ã€ã¯ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ãƒªã‚¹ãƒˆã®ã‚ˆã†ãªä»»æ„ã®é•·ã•ã®æŠ€è¡“的テキストã«ä½¿ã‚れる [[Special:Types/Text|Text]] データ型ã®ç•°å½¢ã§ã™ã€‚",
+ "smw-type-geo": "「$1ã€ã¯åœ°ç†çš„ãªå ´æ‰€ã‚’記述ã™ã‚‹ãƒ‡ãƒ¼ã‚¿åž‹ã§ã™ã€‚[https://www.semantic-mediawiki.org/wiki/Extension:Maps extention ‘’Maps’’] ã‚’å¿…è¦ã¨ã—ã¾ã™ã€‚",
+ "smw-type-tel": "「$1ã€ã¯ RFC 3966 ã«å¾“ã£ãŸå›½éš›é›»è©±ç•ªå·ã‚’記述ã™ã‚‹ãŸã‚ã®ç‰¹åˆ¥ãªãƒ‡ãƒ¼ã‚¿åž‹ã§ã™ã€‚",
+ "smw-type-txt": "「$1ã€ã¯ä»»æ„ã®é•·ã•ã®æ–‡å­—列を記述ã™ã‚‹ãŸã‚ã®ãƒ—リミティブデータ型ã§ã™ã€‚",
+ "smw-type-dat": "「$1ã€ã¯çµ±ä¸€ã•ã‚ŒãŸæ›¸å¼ã§æ™‚点を表ã™ãŸã‚ã®ãƒ‡ãƒ¼ã‚¿åž‹ã§ã™ã€‚",
+ "smw-property-predefined-errt": "「$1ã€ã¯ã€[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]ã«ã‚ˆã£ã¦æä¾›ã•ã‚Œã‚‹ã€ã‚¨ãƒ©ãƒ¼ã®èª¬æ˜Žã‚’å«ã‚“ã å®šç¾©æ¸ˆã¿ã®ãƒ—ロパティã§ã™ã€‚",
+ "smw-property-predefined-askst": "「$1ã€ã¯ã€[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] ã«ã‚ˆã£ã¦æä¾›ã•ã‚Œã‚‹ã€æ–‡å­—列ã¨ã—ã¦å•ã„åˆã‚ã›æ¡ä»¶ã‚’記述ã™ã‚‹å®šç¾©æ¸ˆã¿ã®ãƒ—ロパティã§ã™ã€‚",
+ "smw-limitreport-intext-parsertime": "[SMW] テキスト内注記パーサー時間",
+ "smw-limitreport-intext-parsertime-value": "$1{{PLURAL:$1|秒}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1{{PLURAL:$1|秒}}",
+ "smw-datavalue-external-formatter-invalid-uri": "「$1ã€ã¯ç„¡åŠ¹ãª URL ã§ã™ã€‚",
+ "smw-datavalue-stripmarker-parse-error": " \"$1 \"ã«ã¯ [https://ja.wikipedia.org/wiki/Help:Strip_markers strip markers] ãŒå«ã¾ã‚Œã¦ãŠã‚Šè§£æžã§ãã¾ã›ã‚“。",
+ "smw-datavalue-propertylist-invalid-property-key": "プロパティリスト「$1ã€ã¯ç„¡åŠ¹ãªãƒ—ロパティキー「$2ã€ã‚’å«ã‚“ã§ã„ã¾ã—ãŸã€‚",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-data-lookup-with-wait": "è¦æ±‚ã¯å‡¦ç†ä¸­ã§ã™ã€‚ã—ã°ã‚‰ã時間ãŒã‹ã‹ã‚‹ã“ã¨ãŒã‚ã‚Šã¾ã™ã€‚",
+ "smw-edit-protection": "ã“ã®ãƒšãƒ¼ã‚¸ã¯æ„図ã—ãªã„データã®å¤‰æ›´ã‚’防止ã™ã‚‹ãŸã‚[[Property:Is edit protected|ä¿è­·]]ã•ã‚Œã¦ãŠã‚Šã€é©åˆ‡ãªç·¨é›†æ¨©é™ï¼ˆã€Œ$1ã€ï¼‰ã‚’æŒã£ã¦ã„ã‚‹ã‹é©åˆ‡ãªåˆ©ç”¨è€…グループã«æ‰€å±žã—ã¦ã„る利用者ã®ã¿ç·¨é›†ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki ã¯ã€ŒIs edit protectedã€ãƒ—ロパティã«å¾“ã„ä¿è­·çŠ¶æ…‹ã‚’æ›´æ–°ã—ã¾ã—ãŸã€‚",
+ "smw-format-datatable-emptytable": "テーブルã«ãƒ‡ãƒ¼ã‚¿ãŒã‚ã‚Šã¾ã›ã‚“",
+ "smw-format-datatable-info": "_TOTAL_ 件中 _START_ ã‹ã‚‰ _END_ ã¾ã§è¡¨ç¤º",
+ "smw-format-datatable-infoempty": "0 件中 0 ã‹ã‚‰ 0 ã¾ã§è¡¨ç¤º",
+ "smw-format-datatable-infofiltered": "(全 _MAX_ 件より抽出)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "_MENU_ 件表示",
+ "smw-format-datatable-loadingrecords": "読ã¿è¾¼ã¿ä¸­...",
+ "smw-format-datatable-processing": "処ç†ä¸­...",
+ "smw-format-datatable-search": "検索:",
+ "smw-format-datatable-zerorecords": "一致ã™ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒã‚ã‚Šã¾ã›ã‚“",
+ "smw-format-datatable-first": "先頭",
+ "smw-format-datatable-last": "最終",
+ "smw-format-datatable-next": "次",
+ "smw-format-datatable-previous": "å‰",
+ "smw-format-datatable-sortascending": ": 列を昇順ã«ä¸¦ã¹æ›¿ãˆã‚‹ã«ã¯ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ã™ã‚‹",
+ "smw-format-datatable-sortdescending": ": 列をé™é †ã«ä¸¦ã¹æ›¿ãˆã‚‹ã«ã¯ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã«ã™ã‚‹",
+ "smw-format-datatable-toolbar-export": "エクスãƒãƒ¼ãƒˆ",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-api-invalid-parameters": "ä¸æ­£ãªãƒ‘ラメーター: 「$1ã€",
+ "smw-property-reserved-category": "カテゴリ",
+ "smw-section-expand": "ã™ã¹ã¦ã®ç¯€ã‚’展開",
+ "smw-section-collapse": "節を折りãŸãŸã‚€",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] å½¢å¼",
+ "smw-help": "ヘルプ",
+ "smw-cheat-sheet": "早見表",
+ "smw-processing": "処ç†ä¸­...",
+ "smw-parameter-missing": "パラメータ \"$1\" ãŒã‚ã‚Šã¾ã›ã‚“。",
+ "smw-concept-tab-errors": "エラー"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/jam.json b/www/wiki/extensions/SemanticMediaWiki/i18n/jam.json
new file mode 100644
index 00000000..e74daf59
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/jam.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Katxis"
+ ]
+ },
+ "browse": "Brouz wiki"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/jbo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/jbo.json
new file mode 100644
index 00000000..af45d694
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/jbo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gleki"
+ ]
+ },
+ "smw_purge": "vifnygau",
+ "smw_browselink": "zgana lo se ckaji"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/jut.json b/www/wiki/extensions/SemanticMediaWiki/i18n/jut.json
new file mode 100644
index 00000000..af27e462
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/jut.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jyllanj"
+ ]
+ },
+ "browse": "Djennemsie wiki"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/jv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/jv.json
new file mode 100644
index 00000000..88b1b0b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/jv.json
@@ -0,0 +1,98 @@
+{
+ "@metadata": {
+ "authors": [
+ "Meursault2004",
+ "Pras",
+ "ì•„ë¼"
+ ]
+ },
+ "smw_viewasrdf": "RDF feed",
+ "smw_finallistconjunct": ", lan",
+ "smw_factbox_head": "Fakta-fakta ngenani $1",
+ "smw_isspecprop": "Sifat iki iku sawijining sifat kusus ing wiki iki.",
+ "smw_baduri": "Nuwun sèwu, URI awujud “$1†ora diidinaké.",
+ "smw_iq_disabled": "Nuwun sèwu. Kwéri sémantik kanggo wiki iki dipatèni.",
+ "smw_iq_moreresults": "… pituwas sabanjuré",
+ "smw_parseerror": "Bijih sing diwènèhaké ora dimangertèni.",
+ "smw_notitle": "“$1†ora bisa dienggo minangka jeneng kaca ing wiki iki.",
+ "smw_manytypes": "Sifaté didéfinisi luwih saka sajenis.",
+ "smw_emptystring": "Rangkéan kosong ora ditampa.",
+ "smw_notinenum": "“$1†ora ana ing daftar bijih-bijih sing mungkin ($2) kanggo sifat iki.",
+ "smw_noboolean": "“$1†ora ditepungi minangka sawijining bijih Boolean (bener/salah).",
+ "smw_true_words": "bener,t,ya,y",
+ "smw_false_words": "salah,s,ora,o",
+ "smw_nofloat": "“$1†iku dudu angka.",
+ "smw_infinite": "Angka sing gedhéné nganti \"$1\" ora didhukung.",
+ "smw_nodatetime": "Tanggal “$1†ora dimangertèni.",
+ "smw_toomanyclosing": "Katoné ana kakèhan “$1†sajroning kwéri.",
+ "smw_noclosingbrackets": "Sawetara panrapan “<nowiki>[[</nowiki>†ing kwéri panjenengan ora ditutup déning “]]†sing cocog.",
+ "smw_misplacedsymbol": "Simbul “$1†dienggo ing sawijining panggonan sing ora miguna.",
+ "smw_unexpectedpart": "Bagéyan “$1†saka kwéri ora dimangertèni.\nPituwasé bisa-bisa ora kaya sing diarepaké.",
+ "smw_emptysubquery": "Sawetara subkwéri ora nduwé kondisi absah.",
+ "smw_misplacedsubquery": "Sawetara subkwéri dienggo ing panggonan sing ora diparengaké anané subkwéri.",
+ "smw_valuesubquery": "Subkwéri ora disengkuyung kanggo bijih saka sifat “$1â€.",
+ "smw_badqueryatom": "Ora mudheng “<nowiki>[[…]]</nowiki>†sawetara bagéyan kwéri.",
+ "smw_propvalueproblem": "Bijih sifat \"$1\" ora dimangertèni.",
+ "smw_nodisjunctions": "Pamisahan ing kwéri ora disengkuyung ing wiki iki lan sabagéyan saka kwéri dadi dilirwakaké ($1).",
+ "smw_querytoolarge": "Sarat kwéri sing kapacak ing ngisor iki ora bisa digalih amerga anané rèstriksi wiki ing ukuran utawa jeroné: $1.",
+ "smw_type_header": "Sifat-sifat saka jenis \"$1\"",
+ "smw_typearticlecount": "Nuduhaké $1 {{PLURAL:$1|sifat|sifat}} nganggo jinis iki.",
+ "smw_attribute_header": "Kaca-kaca sing nganggo sifat “$1â€",
+ "smw_attributearticlecount": "Nuduhaké $1 {{PLURAL:$1|kaca|kaca}} nganggo sifat iki.",
+ "exportrdf": "Èkspor kaca-kaca menyang RDF",
+ "smw_exportrdf_docu": "Kaca iki marengaké panjenengan kanggo olèh data saka sawijining kaca ing format RDF.\nKanggo ngèkspor kaca-kaca, mangga lebokna irah-irahan ing kothak tèks ing ngisor iki, sairah-irahan per baris.",
+ "smw_exportrdf_recursive": "Ngèkspor kabèh kaca sing ana gandhèngané minangka rékursif.\nPènget: pituwasé bisa gedhé!",
+ "smw_exportrdf_backlinks": "Uga ngèkspor kabèh kaca ing ngrujuk ing kaca-kaca sing dièkspor.\nNggawé berkas RDF sing bisa dijlajah.",
+ "smw_exportrdf_lastdate": "Aja ngèkspor kaca-kaca sing ora diowahi wiwit sawijining wektu tartamtu.",
+ "uriresolver": "URI Resolver",
+ "properties": "Sifat-sifat",
+ "smw_properties_docu": "Sifat-sifat ing ngisori iki dienggo ing wiki iki.",
+ "smw_property_template": "$1 saka jenis $2 ($3)",
+ "smw_propertylackspage": "Kabèh sifat kudu didèskripsi déning sawijining kaca!",
+ "smw_propertylackstype": "Ora ana jenis sing dispésifikasi kanggo sifat iki (ngasumsi jenis $1 kanggo saiki).",
+ "smw_propertyhardlyused": "Sifat iki mèh ora dienggo sajroning wiki iki!",
+ "unusedproperties": "Sifat-sifat sing ora dienggo",
+ "smw_unusedproperties_docu": "Sifat-sifat sing kapacak iki ana, senadyan ora ana kaca liya sing nganggo.",
+ "smw_unusedproperty_template": "$1 saka jenis $2",
+ "wantedproperties": "Sifat sing dipéngini",
+ "smw_wantedproperties_docu": "Sifat-sifat iki dienggo ing wiki, nanging durung duwé kaca sing ndéskripsi.",
+ "smw_wantedproperty_template": "$1 ({{PLURAL:$2|dianggo kaping|dianggo kaping }}$2)",
+ "smw_purge": "Anyarana",
+ "types": "Jenis-jenis",
+ "smw_types_docu": "Ing ngisor iki kapacak sawijining daftar kabèh jenis data sing bisa ditunjukaké menyang sifat-sifat.\nSaben jenis data duwé kaca ing ngendi informasi tambahan bisa diwènèhaké.",
+
+
+ "smw_uri_doc": "''URI resolver''-é ngimplèmèntasi [$1 W3C TAG finding on httpRange-14]. Iku ngurusi supaya manungsa ora owah lan dadi situs-situs wèb.",
+ "ask": "Panggolèkan sémantik",
+ "smw_ask_sortby": "Sortir miturut kolom (opsional)",
+ "smw_ask_ascorder": "Munggah saka ngisor menyang ndhuwur",
+ "smw_ask_descorder": "Mudhun saka ndhuwur menyang ngisor",
+ "smw_ask_submit": "Pituwas panggolèkan",
+ "smw_ask_editquery": "Sunting kwéri",
+ "smw_add_sortcondition": "[Tambah sarat nyortir]",
+ "smw_ask_hidequery": "Delikna kwéri",
+ "smw_ask_help": "Pitulung kwéri",
+ "smw_ask_queryhead": "Kwéri",
+ "smw_ask_printhead": "Cithakan tambahan (opsional)",
+ "searchbyproperty": "Golèk miturut sifat",
+ "smw_sbv_docu": "Golèk kabèh kaca sing duwé sifat lan bijih tartamtu.",
+ "smw_sbv_novalue": "Mangga lebokna bijih absah kanggo sifat iki, utawa tuduhna kabèh bijih-bijihé sifat kanggo “$1.â€",
+ "smw_sbv_displayresult": "Sawijining daftar kabèh kaca sing duwé sifat “$1†mawa bijih “$2â€",
+ "smw_sbv_property": "Sifat/properti:",
+ "smw_sbv_value": "Aji:",
+ "smw_sbv_submit": "Golèk pituwas (kasil)",
+ "browse": "Jlajaha wiki",
+ "smw_browse_article": "Lebokna irah-irahan kaca sing arep didadèkaké kaca awal olèhé panjenengan njlajah-njlajah.",
+ "smw_browse_go": "Tumuju",
+ "pageproperty": "Panggolèkan sifat kaca",
+ "smw_pp_docu": "Nggolèki kabèh pangisi sawijining sifat ing sawijining kaca.\nTulung isèkna loro-loroné sawijining kaca lan sifat.",
+ "smw_pp_from": "Saka kaca",
+ "smw_pp_type": "Sifat",
+ "smw_pp_submit": "Pituwas panggolèkan",
+ "smw_result_prev": "Sadurungé",
+ "smw_result_next": "Sabanjuré",
+ "smw_result_results": "Pituwas (kasil)",
+ "smw_result_noresults": "Nuwun sèwu, ora ana pituwasé (kasilé).",
+ "smw_unknowntype": "Jenis “$1†sing ora disengkuyung, didéfinisi kanggo sifat.",
+ "smw-livepreview-loading": "Ngunggahaké…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ka.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ka.json
new file mode 100644
index 00000000..f1323cea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ka.json
@@ -0,0 +1,90 @@
+{
+ "@metadata": {
+ "authors": [
+ "David1010",
+ "Malafaya",
+ "Otogi"
+ ]
+ },
+ "smw_finallistconjunct": ", დáƒ",
+ "version-semantic": "სემáƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ გáƒáƒ¤áƒáƒ áƒ—áƒáƒ”ბები",
+ "smw_printername_count": "დáƒáƒ—ვლის შედეგები",
+ "smw_printername_csv": "CSV-ის ექსპáƒáƒ áƒ¢áƒ˜",
+ "smw_printername_dsv": "DSV-ის ექსპáƒáƒ áƒ¢áƒ˜",
+ "smw_printername_json": "JSON-ის ექსპáƒáƒ áƒ¢áƒ˜",
+ "smw_printername_list": "სიáƒ",
+ "smw_printername_ol": "ჩáƒáƒ›áƒáƒ—ვლáƒ",
+ "smw_printername_ul": "სიáƒ",
+ "smw_printername_table": "ცხრილი",
+ "smw_printername_broadtable": "ფáƒáƒ áƒ—რცხრილი",
+ "smw_printername_template": "თáƒáƒ áƒ’ი",
+ "smw_printername_rdf": "RDF-ის ექსპáƒáƒ áƒ¢áƒ˜",
+ "smw_printername_category": "კáƒáƒ¢áƒ”გáƒáƒ áƒ˜áƒ",
+ "validator-type-class-SMWParamSource": "ტექსტი",
+ "smw-paramdesc-showsep": "გáƒáƒ›áƒ§áƒáƒ¤áƒ˜áƒ¡ ჩვენებრCSV ფáƒáƒ˜áƒšáƒ˜áƒ¡ ზედრნáƒáƒ¬áƒ˜áƒšáƒ¨áƒ˜ (\"sep=<value>\")",
+ "smw-paramdesc-csv-sep": "გáƒáƒ›áƒ§áƒáƒ¤áƒ˜áƒ¡ გáƒáƒ›áƒáƒ§áƒ”ნებáƒ",
+ "smw-paramdesc-dsv-separator": "გáƒáƒ›áƒ§áƒáƒ¤áƒ˜áƒ¡ გáƒáƒ›áƒáƒ§áƒ”ნებáƒ",
+ "smw-paramdesc-export": "ექსპáƒáƒ áƒ¢áƒ˜áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრები",
+ "smw_iq_moreresults": "… შემდეგი შედეგები",
+ "smw_true_words": "სიმáƒáƒ áƒ—ლე,ს,დიáƒáƒ®,დ",
+ "smw_false_words": "ტყუილი,ტ,áƒáƒ áƒ,áƒ",
+ "smw_nofloat": "„$1“ áƒáƒ  áƒáƒ áƒ˜áƒ¡ ნáƒáƒ›áƒ”რი.",
+ "exportrdf": "გვერდების ექსპáƒáƒ áƒ¢áƒ˜ RDF-ში",
+ "smw_exportrdf_submit": "ექსპáƒáƒ áƒ¢áƒ˜",
+ "properties": "პáƒáƒ áƒáƒ›áƒ”ტრები",
+ "smw_purge": "გáƒáƒœáƒáƒ®áƒšáƒ”ბáƒ",
+ "types": "ტიპები",
+
+ "ask": "სემáƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ ძიებáƒ",
+ "smw_ask_sortby": "სვეტების მáƒáƒ®áƒ”დვით დáƒáƒšáƒáƒ’ებრ(áƒáƒ áƒáƒ¡áƒáƒ•áƒáƒšáƒ“ებულáƒ)",
+ "smw_ask_ascorder": "ზრდის მიხედვით",
+ "smw_ask_descorder": "კლების მიხედვით",
+ "smw_ask_submit": "შედეგების ძიებáƒ",
+ "smw_ask_editquery": "მáƒáƒ—ხáƒáƒ•áƒœáƒ˜áƒ¡ რედáƒáƒ¥áƒ¢áƒ˜áƒ áƒ”ბáƒ",
+ "smw_add_sortcondition": "[სáƒáƒ áƒ¢áƒ˜áƒ áƒ”ბის პირáƒáƒ‘ის დáƒáƒ›áƒáƒ¢áƒ”ბáƒ]",
+ "smw_ask_hidequery": "მáƒáƒ—ხáƒáƒ•áƒœáƒ˜áƒ¡ დáƒáƒ›áƒáƒšáƒ•áƒ",
+ "smw_ask_queryhead": "მáƒáƒ—ხáƒáƒ•áƒœáƒ",
+ "smw_ask_defaultformat": "სტáƒáƒœáƒ“áƒáƒ áƒ¢áƒ£áƒšáƒ˜",
+ "smw_ask_otheroptions": "სხვრპáƒáƒ áƒáƒ›áƒ”ტრები",
+ "smw-ask-delete": "[წáƒáƒ¨áƒšáƒ]",
+ "searchbyproperty": "თვისებებით ძიებáƒ",
+ "smw_sbv_property": "თვისებáƒ:",
+ "smw_sbv_value": "მნიშვნელáƒáƒ‘áƒ:",
+ "smw_sbv_submit": "შედეგების ძიებáƒ",
+ "browse": "ვიკის მიმáƒáƒ®áƒ˜áƒšáƒ•áƒ",
+ "smw_browse_go": "მიდი",
+ "smw_pp_from": "გვერდიდáƒáƒœ",
+ "smw_pp_type": "თვისებáƒ",
+ "smw_pp_submit": "შედეგების ძიებáƒ",
+ "smw_result_prev": "წინáƒ",
+ "smw_result_next": "შემდეგი",
+ "smw_result_results": "შედეგები",
+ "smw_result_noresults": "შედეგები áƒáƒ  áƒáƒ áƒ˜áƒ¡.",
+ "smw_smwadmin_dbbutton": "ცხრილების ინიციáƒáƒšáƒ˜áƒ–áƒáƒªáƒ˜áƒ áƒáƒœ გáƒáƒœáƒáƒ®áƒšáƒ”ბáƒ",
+ "smw_smwadmin_datarefresh": "მáƒáƒœáƒáƒªáƒ”მების áƒáƒ¦áƒ“გენრდრგáƒáƒœáƒáƒ®áƒšáƒ”ბáƒ",
+ "smw_smwadmin_datarefreshbutton": "მáƒáƒœáƒáƒªáƒ”მების გáƒáƒœáƒáƒ®áƒšáƒ”ბის დáƒáƒ¬áƒ§áƒ”ბáƒ",
+ "smw_smwadmin_datarefreshstop": "áƒáƒ› გáƒáƒœáƒáƒ®áƒšáƒ”ბის შეჩერებáƒ",
+ "smw_smwadmin_datarefreshstopconfirm": "დიáƒáƒ®, დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული {{GENDER:$1|ვáƒáƒ }}.",
+ "smw_smwadmin_support": "დáƒáƒ®áƒ›áƒáƒ áƒ”ბის მიღებáƒ",
+ "smw_smwadmin_mediazilla": "შეცდáƒáƒ›áƒ”ბის შეტყáƒáƒ‘ინებრშესáƒáƒ«áƒšáƒ”ბელირ<a href=\"https://bugzilla.wikimedia.org/\">ბáƒáƒ’ზილáƒáƒ¨áƒ˜</a>.",
+ "smw_adminlinks_datastructure": "მáƒáƒœáƒáƒªáƒ”მების სტრუქტურáƒ",
+ "smw_adminlinks_displayingdata": "მáƒáƒœáƒáƒªáƒ”მთრგáƒáƒ›áƒáƒ¡áƒáƒ®áƒ•áƒ",
+ "smw-createproperty-isproperty": "ეს áƒáƒ áƒ˜áƒ¡ $1 ტიპის თვისებáƒ.",
+ "smw-paramdesc-category-delim": "გáƒáƒ›áƒ§áƒáƒ¤áƒ˜",
+ "prefs-smw": "სემáƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ მედიáƒáƒ•áƒ˜áƒ™áƒ˜",
+ "prefs-ask-options": "სემáƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ ძიების პáƒáƒ áƒáƒ›áƒ”ტრები",
+ "smw-prefs-intro-text": "პáƒáƒ áƒáƒ›áƒ”ტრები ქვემáƒáƒ— წáƒáƒ áƒ›áƒáƒ“გენილირ[https://www.semantic-mediawiki.org/ სემáƒáƒœáƒ¢áƒ˜áƒ™áƒ£áƒ  მედიáƒáƒ•áƒ˜áƒ™áƒ˜áƒ¡áƒ—áƒáƒœ] (áƒáƒœ მáƒáƒ¡áƒ—áƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბულ გáƒáƒ¤áƒáƒ áƒ—áƒáƒ”ბáƒáƒ¡áƒ—áƒáƒœ), იმისáƒáƒ—ვის, რáƒáƒ› ჩáƒáƒ˜áƒ áƒ—áƒáƒ¡ ცáƒáƒšáƒ™áƒ”ული პáƒáƒ áƒáƒ›áƒ”ტრები áƒáƒ›áƒáƒ áƒ©áƒ”ული ფუნქციებით. დáƒáƒ›áƒáƒ¢áƒ”ბითი ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ მისáƒáƒ¦áƒ”ბáƒáƒ“, გთხáƒáƒ•áƒ—, შეიხედეთ [https://www.semantic-mediawiki.org/wiki/Help:User_preferences დáƒáƒ®áƒ›áƒáƒ áƒ”ბის სექციáƒáƒ¨áƒ˜].",
+ "smw-prefs-ask-options-tooltip-display": "გáƒáƒ›áƒáƒáƒ¥áƒ•áƒ¡ პáƒáƒ áƒáƒ›áƒ”ტრი ტექსტი ეკრáƒáƒœáƒ˜áƒ¡ მინიშნების სáƒáƒ®áƒ˜áƒ—",
+ "smw-prefs-ask-options-collapsed-default": "პáƒáƒ áƒáƒ›áƒ”ტრების პáƒáƒœáƒ”ლის ჩáƒáƒ áƒ—ვრჩáƒáƒ™áƒ”ცილáƒáƒ“, ნáƒáƒ’ულისხმევáƒáƒ“",
+ "smw-ui-tooltip-title-property": "თვისებáƒ",
+ "smw-ui-tooltip-title-quantity": "რáƒáƒáƒ“ენáƒáƒ‘áƒ",
+ "smw-ui-tooltip-title-info": "ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ",
+ "smw-ui-tooltip-title-service": "სერვის-ბმულები",
+ "smw-ui-tooltip-title-warning": "შეცდáƒáƒ›áƒ",
+ "smw-ui-tooltip-title-parameter": "პáƒáƒ áƒáƒ›áƒ”ტრი",
+ "smw-ui-tooltip-title-event": "ღáƒáƒœáƒ˜áƒ¡áƒ«áƒ˜áƒ”ბáƒ",
+ "smw-ui-tooltip-title-note": "შენიშვნáƒ",
+ "smw-ui-tooltip-title-legend": "ლეგენდáƒ",
+ "smw_unknowntype": "áƒáƒ› თვისების ტიპი áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜áƒ",
+ "smw-livepreview-loading": "იტვირთებáƒâ€¦"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kab.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kab.json
new file mode 100644
index 00000000..335ed290
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kab.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Assisi…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/khw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/khw.json
new file mode 100644
index 00000000..a42fd5fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/khw.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Rachitrali"
+ ]
+ },
+ "browse": "ویکپیڈیا براوزر"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kiu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kiu.json
new file mode 100644
index 00000000..12544ec9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kiu.json
@@ -0,0 +1,7 @@
+{
+ "@metadata": {
+ "authors": [
+ "Erdemaslancan"
+ ]
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kk-arab.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-arab.json
new file mode 100644
index 00000000..04838f34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-arab.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "جۇكتەۋدە…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kk-cyrl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-cyrl.json
new file mode 100644
index 00000000..16b1d2b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-cyrl.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arystanbek"
+ ]
+ },
+ "smw_purge": "Жаңарту",
+ "browse": "Уикиді шолу",
+ "smw-livepreview-loading": "Жүктеуде…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kk-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-latn.json
new file mode 100644
index 00000000..66173aa5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kk-latn.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Jüktewde…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/km.json b/www/wiki/extensions/SemanticMediaWiki/i18n/km.json
new file mode 100644
index 00000000..4d421951
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/km.json
@@ -0,0 +1,87 @@
+{
+ "@metadata": {
+ "authors": [
+ "Lovekhmer",
+ "Thearith",
+ "គីមស៊្រុន"
+ ]
+ },
+ "smw_finallistconjunct": "áž“áž·áž„",
+ "smw_factbox_head": "áž áŸážáž»áž€áž¶ážšážŽáŸáž–áž·ážâ€‹áž¢áŸ†áž–ី $1",
+ "smw_isspecprop": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž“áŸáŸ‡ គឺជា​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž–ិសáŸážŸâ€‹áž“ៅក្នុង​វិគី​នáŸáŸ‡â€‹áŸ”",
+ "smw_baduri": "URIs នៃ​ទម្រង់ \"$1\" មិន​ážáŸ’រូវ​បាន​អនុញ្ញាážâ€‹áž‘áŸâ€‹áŸ”",
+ "smw_iq_moreresults": "... លទ្ធផល​ážáž¶áž„មុážâ€‹áž‘ៀáž",
+ "smw_parseerror": "ážáž˜áŸ’លៃ​ដែល​បាន​ផ្ដល់ឱ្យ មិន​អាច​យល់​បាន​ទáŸâ€‹áŸ”",
+ "smw_notitle": "\"$1\" មិន​អាច​ážáŸ’រូវ​បាន​ប្រើប្រាស់​ជា​ឈ្មោះ​ទំពáŸážšâ€‹áž“ៅក្នុង​វិគី​នáŸáŸ‡â€‹áž‘áŸâ€‹áŸ”",
+ "smw_wrong_namespace": "មានážáŸ‚​ទំពáŸážšâ€‹áž“ៅ​ក្នុង​លំហឈ្មោះ \"$1\" ប៉ុណ្ណោះ ដែល​ážáŸ’រូវ​បាន​អនុញ្ញាážâ€‹áž“ៅទីនáŸáŸ‡â€‹áŸ”",
+ "smw_noboolean": "\"$1\" មិន​ážáŸ’រូវ​បាន​ទទួលស្គាល់​ជា​ážáž˜áŸ’លៃ​ប៊ូលីន (ážáŸ’រូវ/ážáž»ážŸ) áž‘áŸâ€‹áŸ”",
+ "smw_true_words": "ážáŸ’រូវ,áž,បាទ,áž”",
+ "smw_false_words": "ážáž»ážŸ,áž,áž‘áŸ,áž‘",
+ "smw_nofloat": "\"$1\" មិនមែន​ជា​លáŸážâ€‹áž‘áŸâ€‹áŸ”",
+ "smw_nodatetime": "កាលបរិច្ឆáŸáž‘ \"$1\" មិន​អាច​យល់​បាន​ទáŸâ€‹áŸ”",
+ "smw_toomanyclosing": "ហាក់ដូចជា មាន​​ការគាប់ជួន​ច្រើនពáŸáž€â€‹áž“ៃ \"$1\" នៅក្នុង​សំណួរ​របស់​អ្នក​។",
+ "smw_noclosingbrackets": "ការប្រើប្រាស់​នៃ \"<nowiki>[[</nowiki>\" នៅក្នុង​សំណួរ​របស់​អ្នក​មិនážáŸ’រូវ​បាន​បិទ​ដោយ​សញ្ញា​ \"]]\" ជាគូ​។",
+ "smw_misplacedsymbol": "និមិážáŸ’ážážŸáž‰áŸ’ញា \"$1\" ážáŸ’រូវ​បាន​ប្រើ​ជំនួស​កន្លែង ដែល​មិន​ážáŸ’រូវ​បាន​ប្រើ​។",
+ "smw_propvalueproblem": "ážáž˜áŸ’លៃ​នៃ​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· \"$1\" មិន​អាច​យល់​បាន​ទáŸâ€‹áŸ”",
+ "smw_notemplategiven": "ផ្ដល់​ážáž˜áŸ’លៃ​សម្រាប់​ប៉ារ៉ាម៉ែážáŸ’ážš \"ទំពáŸážšáž‚ំរូ\" សម្រាប់​ទ្រង់ទ្រង់​សំណួរ​នáŸáŸ‡â€‹ážŠáž¾áž˜áŸ’បី​ធ្វើការ​។",
+ "smw_type_header": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž“ៃ​គំរូ \"$1\"",
+ "smw_typearticlecount": "បង្ហាញ $1 {{PLURAL:$1|លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·|លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·}} ដែល​កំពុង​ប្រើប្រាស់​គំរូនáŸáŸ‡â€‹áŸ”",
+ "smw_attribute_header": "ទំពáŸážšâ€‹ážŠáŸ‚ល​កំពុង​ប្រើប្រាស់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· \"$1\"",
+ "smw_attributearticlecount": "បង្ហាញ $1 {{PLURAL:$1|ទំពáŸážš|ទំពáŸážš}} ដែល​កំពុង​ប្រើប្រាស់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž“áŸáŸ‡â€‹áŸ”",
+ "smw_subproperty_header": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážšáž„",
+ "smw_subpropertyarticlecount": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž“áŸáŸ‡áž˜áž¶áž“ $1 {{PLURAL:$1|លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážšáž„|លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážšáž„}}​ ដូចážáž‘ៅ៖",
+ "exportrdf": "នាំចáŸáž‰â€‹áž‘ំពáŸážšâ€‹áž‘ៅ RDF",
+ "uriresolver": "អ្នកដោះស្រាយ URI",
+ "properties": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·",
+ "smw_properties_docu": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážŠáž¼áž…​ážáž¶áž„ក្រោម​ážáŸ’រូវ​បាន​ប្រើប្រាស់​នៅ​ក្នុង​វិគី​។",
+ "smw_property_template": "$1 នៃ​គំរូ $2 ($3)",
+ "smw_propertylackspage": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž‘ាំងអស់ គួរážáŸ‚​ážáŸ’រូវ​បាន​ពីពណ៌នា​ដោយ​ទំពáŸážšâ€‹áž˜áž½áž™â€‹!",
+ "smw_propertyhardlyused": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· ស្ទើរážáŸ‚​មិនážáŸ’រូវ​បាន​ប្រើប្រាស់​ជាមួយ​វិគី​!",
+ "unusedproperties": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž˜áž·áž“​ប្រើប្រាស់",
+ "smw_unusedproperty_template": "$1 នៃ​គំរូ $2",
+ "wantedproperties": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážŠáŸ‚ល​ចង់បាន",
+ "smw_wantedproperties_docu": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážŠáž¼áž…ážáž‘ៅនáŸáŸ‡ ážáŸ’រូវ​បាន​ប្រើប្រាស់​នោក្នុង​វិគី ប៉ុន្ដែ​នៅ​មិនទាំន់​មាន​ទំពáŸážšâ€‹ážŽáž¶áž˜áž½áž™â€‹ážŸáž˜áŸ’រាប់​ពិពណ៌នា​ពួកវា​ឡើយ​។",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|ប្រើប្រាស់|ប្រើប្រាស់}})",
+ "smw_purge": "ធ្វើឱ្យស្រស់",
+ "types": "ប្រភáŸáž‘",
+
+ "smw_ask_sortby": "ážáž˜áŸ’រៀប​ážáž¶áž˜â€‹áž‡áž½ážšážˆážš (ážáž¶áž˜áž”ំណង)",
+ "smw_ask_ascorder": "លំដាប់ឡើង",
+ "smw_ask_descorder": "លំដាប់ចុះ",
+ "smw_ask_submit": "ស្វែងរកលទ្ធផល",
+ "searchbyproperty": "ស្វែងរក​ážáž¶áž˜â€‹ážšáž™áŸˆâ€‹áž›áž€áŸ’ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·",
+ "smw_sbv_docu": "ស្វែងរក​គ្រប់​ទំពáŸážšâ€‹áž‘ាំងអស់ ដែល​ážáŸ’រូវ​បាន​ផ្ដល់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· និង​ážáž˜áŸ’លៃ​។",
+ "smw_sbv_novalue": "បញ្ចូល​ážáž˜áŸ’លៃ​ážáŸ’រឹមážáŸ’រូវ​សម្រាប់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· ឬ​ក០មើល​គ្រប់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž‘ាំងអស់​សម្រាប់ \"$1\"​។",
+ "smw_sbv_displayresult": "បញ្ជី​មួយ​នៃ​ទំពáŸážšâ€‹áž‘ាំងអស់ ដែល​មានន​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· \"$1\" ជាមួយ​ážáž˜áŸ’លៃ \"$2\"",
+ "smw_sbv_displayresultfuzzy": "បញ្ជី​មួយ​នៃ​ទំពáŸážšâ€‹áž‘ាំងអស់ ដែល​មានន​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· \"$1\" ជាមួយ​ážáž˜áŸ’លៃ \"$2\"​។\nដោយហáŸážáž»ážáŸ‚ មាន​លទ្ធផល​ážáž·áž…ážáž½áž…​ប៉ុណ្ណោះ កáŸáž”្រហាក់ប្រហែល​ជាមួយ​ážáž˜áŸ’លៃ​ដែល​ážáŸ’រូវ​បាន​បង្ហាញ​ផងដែរ​។",
+ "smw_sbv_property": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·áŸ–",
+ "smw_sbv_value": "ážáŸ†áž›áŸƒáž›áŸážáŸ–",
+ "smw_sbv_submit": "ស្វែងរក​លទ្ធផល",
+ "browse": "រាវរកវិគី",
+ "smw_browselink": "រកមើល​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·",
+ "smw_browse_article": "សូម​បញ្ចូល​ឈ្មោះ​នៃ​ទំពáŸážš ដើម្បី​ចាប់ផ្ដើម​រុករក​ពី​។",
+ "smw_browse_go": "ទៅ",
+ "smw_browse_show_incoming": "បង្ហាញ​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· ដែល​មាន​ážáŸ†ážŽáž—្ជាប់​នៅទីនáŸáŸ‡â€‹áŸ”",
+ "smw_browse_hide_incoming": "លាក់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· ដែល​មាន​ážáŸ†ážŽáž—្ជាប់​នៅទីនáŸáŸ‡â€‹áŸ”",
+ "smw_browse_no_outgoing": "ទំពáŸážšâ€‹áž“áŸáŸ‡â€‹áž˜áž·áž“មាន​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž‘áŸâ€‹áŸ”",
+ "smw_browse_no_incoming": "គ្មាន​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž· ដែល​ភ្ជាប់ទៅ​ទំពáŸážšâ€‹áž“áŸáŸ‡â€‹áž‘áŸâ€‹áŸ”",
+ "smw_inverse_label_default": "$1 នៃ",
+ "pageproperty": "ស្វែងរក​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž‘ំពáŸážš",
+ "smw_pp_from": "ពីទំពáŸážš",
+ "smw_pp_type": "លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·",
+ "smw_pp_submit": "ស្វែងរកលទ្ធផល",
+ "smw_result_prev": "មុន",
+ "smw_result_next": "បន្ទាប់",
+ "smw_result_results": "លទ្ធផល",
+ "smw_result_noresults": "សូមអភáŸáž™áž‘ោស! មិនមានលទ្ធផលទáŸáŸ”",
+ "smw_smwadmin_return": "ážáŸ’រឡប់​ទៅកាន់ $1",
+ "smw_smwadmin_db": "ការដំឡើង និង​ធ្វើឱ្យប្រសើរ​នូវ​មូលដ្ឋានទិន្ននáŸáž™",
+ "smw_smwadmin_announce": "ប្រកាស​វិគី​របស់​អ្នក",
+ "smw_smwadmin_datarefresh": "ជួសជុល និង​ធ្វើឱ្យ​ប្រសើរ​នូវ​ទិន្ននáŸáž™",
+ "smw_smwadmin_datarefreshbutton": "ចាប់ផ្ដើម​បន្ទាន់សមáŸáž™â€‹áž‘ិន្ននáŸáž™",
+ "smw_smwadmin_datarefreshstop": "បញ្ឈប់​ការ​បន្ទាន់សមáŸáž™â€‹áž“áŸáŸ‡",
+ "smw_smwadmin_datarefreshstopconfirm": "បាទ/ចាស, ážáŸ’ញុំ​ប្រាកដ​ហើយ​។",
+ "smw-createproperty-isproperty": "áž“áŸáŸ‡â€‹áž‚ឺជា​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·áž˜áž½áž™â€‹áž“ៃ​គំរូ $1 ។",
+ "smw-createproperty-allowedvals": "ážáž˜áŸ’លៃ​ចំនួន$1 ​សម្រាប់​លក្ážážŽáŸˆážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž“áŸáŸ‡áž‚ឺ​៖",
+ "smw-livepreview-loading": "កំពុងផ្ទុក…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kn.json
new file mode 100644
index 00000000..a98815d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kn.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Nayvik",
+ "VASANTH S.N.",
+ "Omshivaprakash"
+ ]
+ },
+ "smw_finallistconjunct": ", ಮತà³à²¤à³",
+ "smw_printername_template": "ಟೆಂಪà³à²²à³‡à²Ÿà³",
+ "browse": "ವಿಕಿ ಜಾಲಾಡಿ",
+ "smw_browse_go": "ಹೋಗà³",
+ "smw-livepreview-loading": "ತà³à²‚ಬಿಸಲಾಗà³à²¤à³à²¤à²¿à²¦à³†...."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ko.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ko.json
new file mode 100644
index 00000000..acad1184
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ko.json
@@ -0,0 +1,609 @@
+{
+ "@metadata": {
+ "authors": [
+ "Albamhandae",
+ "Kwj2772",
+ "Priviet",
+ "ì•„ë¼",
+ "Keysuck",
+ "SeoJeongHo",
+ "Hwangjy9",
+ "밥풀떼기",
+ "HDNua",
+ "Revi",
+ "Ykhwong",
+ "Jonghaya",
+ "Jerrykim306",
+ "Nemo bis",
+ "Kghbln",
+ "Ellif"
+ ]
+ },
+ "smw-desc": "위키를 기계와 ì‚¬ëžŒì´ ë” ì ‘ê·¼í•˜ê¸° 쉽게 합니다 ([https://www.semantic-mediawiki.org/wiki/Help:User_manual 온ë¼ì¸ 설명서])",
+ "smw-title": "시맨틱 미디어위키",
+ "smw-upgrade-error-why-title": "ì´ ì˜¤ë¥˜ë¥¼ 보게 ëœ ì´ìœ ëŠ” 무엇입니까?",
+ "smw-upgrade-error-how-title": "ì´ ì˜¤ë¥˜ë¥¼ 어떻게 수정합니까?",
+ "smw-semantics-not-enabled": "시맨틱 미디어위키 ê¸°ëŠ¥ì€ ì´ ìœ„í‚¤ì—ì„œ 사용할 수 없습니다.",
+ "smw_viewasrdf": "RDF 피드",
+ "smw_finallistconjunct": ", 그리고",
+ "smw-factbox-head": "... \"$1\"ì— ëŒ€í•˜ì—¬ ë” ì•Œì•„ë³´ê¸°",
+ "smw-factbox-facts": "ìƒì‹",
+ "smw_isspecprop": "ì´ ì†ì„±ì€ ì´ ìœ„í‚¤ì— íŠ¹ìˆ˜í•œ ì†ì„±ìž…니다.",
+ "smw-concept-cache-header": "ìºì‹œ 사용률",
+ "smw-concept-no-cache": "ì´ìš© 가능한 ìºì‹œê°€ 없습니다.",
+ "smw_concept_description": "\"$1\" ê°œë…ì˜ ì„¤ëª…",
+ "smw_no_concept_namespace": "ê°œë…ì€ Concept: ì´ë¦„ê³µê°„ì˜ ë¬¸ì„œì—만 지정할 수 있습니다.",
+ "smw_multiple_concepts": "ê° ê°œë… ë¬¸ì„œëŠ” ê°œë… ì •ì˜ í•˜ë‚˜ë§Œ í•  수 있습니다.",
+ "smw_concept_cache_miss": "\"$1\" ê°œë…ì€ ìœ„í‚¤ ì„¤ì •ì€ ì˜¤í”„ë¼ì¸ì—ì„œ 처리해야 하기 ë•Œë¬¸ì— í˜„ìž¬ëŠ” 사용할 수 없습니다.\n문제가 ìž ì‹œ 후 사ë¼ì§€ì§€ 않으면 ì´ ê°œë…ì„ ì‚¬ìš©í•  수 있ë„ë¡ ì‚¬ì´íŠ¸ 관리ìžì—게 문ì˜í•˜ì„¸ìš”.",
+ "smw_noinvannot": "ê°’ì€ ì—­ ì†ì„±ì— 할당할 수 없습니다.",
+ "version-semantic": "시맨틱 확장 기능",
+ "smw_baduri": "\"$1\" ì–‘ì‹ì˜ URI는 허용하지 않습니다.",
+ "smw_printername_count": "개수 결과",
+ "smw_printername_csv": "CSV 내보내기",
+ "smw_printername_dsv": "DSV 내보내기",
+ "smw_printername_debug": "(전문가용) 쿼리 디버그",
+ "smw_printername_embedded": "문서 ë‚´ìš© í¬í•¨",
+ "smw_printername_json": "JSON 내보내기",
+ "smw_printername_list": "목ë¡",
+ "smw_printername_plainlist": "단순 목ë¡",
+ "smw_printername_ol": "번호를 매긴 목ë¡",
+ "smw_printername_ul": "글머리 기호 목ë¡",
+ "smw_printername_table": "표",
+ "smw_printername_broadtable": "ë„“ì€ í‘œ",
+ "smw_printername_template": "í‹€",
+ "smw_printername_templatefile": "í‹€ 파ì¼",
+ "smw_printername_rdf": "PDF 내보내기",
+ "smw_printername_category": "분류",
+ "validator-type-class-SMWParamSource": "í…스트",
+ "smw-paramdesc-limit": "반환할 ê²°ê³¼ì˜ ìµœëŒ€ 수",
+ "smw-paramdesc-offset": "첫 ê²°ê³¼ì˜ ì˜¤í”„ì…‹",
+ "smw-paramdesc-headers": "머리글/ì†ì„± ì´ë¦„ 표시",
+ "smw-paramdesc-mainlabel": "대문 ì´ë¦„으로 주어지는 ë ˆì´ë¸”",
+ "smw-paramdesc-link": "ë§í¬ë¡œ ê°’ ë³´ì´ê¸°",
+ "smw-paramdesc-intro": "ì§ˆë¬¸ì— ë‹µì´ ìžˆìœ¼ë©´ 쿼리 ê²°ê³¼ ì•žì— í‘œì‹œí•  í…스트",
+ "smw-paramdesc-outro": "ì§ˆë¬¸ì— ë‹µì´ ìžˆìœ¼ë©´ 쿼리 ê²°ê³¼ ë’¤ì— í‘œì‹œí•  í…스트",
+ "smw-paramdesc-default": "ì§ˆë¬¸ì— ë‹µì´ ì—†ìœ¼ë©´ 표시할 í…스트",
+ "smw-paramdesc-sep": "ê²°ê³¼ ê°„ 구분ìž",
+ "smw-paramdesc-propsep": "ê²°ê³¼ 엔트리 ì†ì„± ê°„ 구분ìž",
+ "smw-paramdesc-valuesep": "ê²°ê³¼ ì†ì„± ê°’ ê°„ 구분ìž",
+ "smw-paramdesc-showsep": "CSV 파ì¼ì˜ ìœ„ì— êµ¬ë¶„ìžë¥¼ ë³´ì´ê¸° (\"sep=<ê°’>\")",
+ "smw-paramdesc-distribution": "모든 ê°’ì„ í‘œì‹œí•˜ëŠ” 대신 ê°’ì˜ ë°œìƒì„ 세서 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-paramdesc-distributionsort": "ë°œìƒ íšŸìˆ˜ë¡œ ê°’ 분í¬ë¥¼ 정렬합니다.",
+ "smw-paramdesc-distributionlimit": "ì¼ë¶€ ê°’ë§Œì˜ íšŸìˆ˜ë¡œ ê°’ 분í¬ë¥¼ 제한합니다.",
+ "smw-paramdesc-template": "ì¸ì‡„ 출력과 함께 표시할 í‹€ì˜ ì´ë¦„",
+ "smw-paramdesc-columns": "결과를 표시할 ì—´ì˜ ìˆ˜",
+ "smw-paramdesc-userparam": "í‹€ì´ ì‚¬ìš©ë˜ëŠ” 경우 ê° í‹€ í˜¸ì¶œì— ì „ë‹¬ë˜ëŠ” 값입니다",
+ "smw-paramdesc-class": "ë¦¬ìŠ¤íŠ¸ì— ì„¤ì •í•  추가ì ì¸ CSS í´ëž˜ìŠ¤",
+ "smw-paramdesc-introtemplate": "ì§ˆë¬¸ì— ë‹µì´ ìžˆìœ¼ë©´ 쿼리 ê²°ê³¼ ì•žì— í‘œì‹œí•  í‹€ì˜ ì´ë¦„",
+ "smw-paramdesc-outrotemplate": "ì§ˆë¬¸ì— ë‹µì´ ìžˆìœ¼ë©´ 쿼리 ê²°ê³¼ ë’¤ì— í‘œì‹œí•  í‹€ì˜ ì´ë¦„",
+ "smw-paramdesc-embedformat": "ë¨¸ë¦¬ê¸€ì„ ì •ì˜í•˜ëŠ” ë° ì‚¬ìš©í•˜ëŠ” HTML 태그",
+ "smw-paramdesc-embedonly": "ë¨¸ë¦¬ê¸€ì„ í‘œì‹œí•˜ì§€ ì•ŠìŒ",
+ "smw-paramdesc-table-class": "í‘œì— ì„¤ì •í•  추가ì ì¸ CSS í´ëž˜ìŠ¤",
+ "smw-paramdesc-table-transpose": "í‘œ 머리ë§ì€ 수ì§ìœ¼ë¡œ, 결과는 수í‰ìœ¼ë¡œ 표시합니다",
+ "smw-paramdesc-rdfsyntax": "사용할 RDF 구문",
+ "smw-paramdesc-csv-sep": "컬럼 구분ìžë¥¼ 지정합니다",
+ "smw-paramdesc-csv-valuesep": "ê°’ 구분ìžë¥¼ 지정합니다",
+ "smw-paramdesc-dsv-separator": "사용할 구분ìž",
+ "smw-paramdesc-dsv-filename": "DSV 파ì¼ì˜ ì´ë¦„",
+ "smw-paramdesc-filename": "출력 파ì¼ì˜ ì´ë¦„",
+ "smw-smwdoc-description": "기본 ê°’ê³¼ 설명과 함께 지정한 ê²°ê³¼ 형ì‹ì— 사용할 수 있는 모든 ë³€ìˆ˜ì˜ í‘œë¥¼ ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-smwdoc-default-no-parameter-list": "ì´ ê²°ê³¼ í¬ë§·ì€ í¬ë§·ì— íŠ¹í™”ëœ ë³€ìˆ˜ë¥¼ 제공하고 있지 않습니다.",
+ "smw-smwdoc-par-format": "변수 설명문서를 표시할 ê²°ê³¼ 형ì‹ìž…니다.",
+ "smw-smwdoc-par-parameters": "보여줄 ì–´ë–¤ 변수입니다. 형ì‹ì— 추가한 ë³€ìˆ˜ì— ëŒ€í•´ì„œëŠ” \"specific\", 모든 형ì‹ì„ 사용할 수 있는 ë³€ìˆ˜ì— ëŒ€í•´ì„œëŠ” \"base\", 둘 다는 \"all\"입니다.",
+ "smw-paramdesc-sort": "쿼리를 정렬하는 ì†ì„±",
+ "smw-paramdesc-order": "쿼리 ì •ë ¬ì˜ ìˆœì„œ",
+ "smw-paramdesc-searchlabel": "ê²€ìƒ‰ì„ ê³„ì†í•˜ê¸°ì— 대한 í…스트",
+ "smw-paramdesc-named_args": "í‹€ì— ì „ë‹¬í•œ ì¸ìˆ˜ì˜ ì´ë¦„ì„ ì§€ì •",
+ "smw-paramdesc-export": "내보내기 옵션",
+ "smw-paramdesc-prettyprint": "추가ì ì¸ 들여 쓰기와 줄 ë°”ê¿ˆì„ ë³´ì—¬ì£¼ëŠ” ì˜ˆìœ ì¸ì‡„ 출력",
+ "smw-paramdesc-json-type": "ì§ë ¬í™” 유형",
+ "smw-paramdesc-source": "대체 쿼리 소스",
+ "smw-paramdesc-jsonsyntax": "사용할 JSON 구문",
+ "smw-printername-feed": "RSS와 Atom 피드",
+ "smw-paramdesc-feedtype": "피드 유형",
+ "smw-paramdesc-feedtitle": "í”¼ë“œì˜ ì œëª©ìœ¼ë¡œ 사용할 í…스트",
+ "smw-paramdesc-feeddescription": "í”¼ë“œì˜ ì„¤ëª…ìœ¼ë¡œ 사용할 í…스트",
+ "smw-paramdesc-feedpagecontent": "í”¼ë“œì— ë³´ì—¬ì¤„ 문서 ë‚´ìš©",
+ "smw-label-feed-description": "$1 $2 피드",
+ "smw_iq_disabled": "시맨틱 쿼리는 ì´ ìœ„í‚¤ì— ë¹„í™œì„±í™”ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw_iq_moreresults": "… 다른 결과",
+ "smw_parseerror": "지정한 ê°’ì€ ì´í•´í•  수 없습니다.",
+ "smw_notitle": "\"$1\"ì€(는) ì´ ìœ„í‚¤ì—ì„œ 문서 ì´ë¦„으로 사용할 수 없습니다.",
+ "smw_noproperty": "\"$1\"ì€(는) ì´ ìœ„í‚¤ì—ì„œ ì†ì„± ì´ë¦„으로 사용할 수 없습니다.",
+ "smw_wrong_namespace": "\"$1\" ì´ë¦„ê³µê°„ì˜ ë¬¸ì„œë§Œ ì—¬ê¸°ì— í—ˆìš©í•©ë‹ˆë‹¤.",
+ "smw_manytypes": "ì†ì„±ì— ì •ì˜í•œ 하나 ì´ìƒì˜ 유형입니다.",
+ "smw_emptystring": "빈 문ìžì—´ì€ 허용하지 않습니다.",
+ "smw_notinenum": "\"$1\"ì€(는) \"$3\" ì†ì„±ì˜ [[Property:Allows value|í—ˆìš©ëœ ê°’]] 목ë¡($2)ì— ì—†ìŠµë‹ˆë‹¤.",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\"ì€(는) \"$3\" ì†ì„±ì˜ [[Property:Allows value|í—ˆìš©ëœ ê°’]] 목ë¡($2)ì— ì—†ìŠµë‹ˆë‹¤.",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\"ì€(는) \"$3\" ì†ì„±ì˜ [[Property:Allows value|허용값]] ì œí•œì„ í†µí•´ ì§€ì •ëœ \"$2\"ì˜ ë²”ìœ„ì— ì†í•˜ì§€ 않습니다.",
+ "smw_noboolean": "\"$1\"ì€(는) 불리언(ì°¸/거짓) 값으로 ì¸ì‹í•˜ì§€ 않습니다.",
+ "smw_true_words": "true,t,yes,y",
+ "smw_false_words": "false,f,no,n",
+ "smw_nofloat": "\"$1\"ì€(는) 숫ìžê°€ 아닙니다.",
+ "smw_infinite": "\"$1\" í¬ê¸°ì™€ ê°™ì€ ìˆ«ìžëŠ” 지ì›í•˜ì§€ 않습니다.",
+ "smw_unitnotallowed": "\"$1\"ì€(는) ì´ ì†ì„±ì— 대한 ì¸¡ì •ì˜ ì˜¬ë°”ë¥¸ 단위로 ì„ ì–¸ë˜ì–´ 있지 않습니다.",
+ "smw_nounitsdeclared": "ì´ ì†ì„±ì— 대해 선언한 ì¸¡ì •ì˜ ë‹¨ìœ„ê°€ 없습니다.",
+ "smw_novalues": "지정한 ê°’ì´ ì—†ìŠµë‹ˆë‹¤.",
+ "smw_nodatetime": "\"$1\" 날짜는 ì´í•´í•  수 없습니다.",
+ "smw_toomanyclosing": "ì¿¼ë¦¬ì— \"$1\"ì˜ ì‚¬ê±´ì´ ë„ˆë¬´ ë§Žì€ ê²ƒ 같습니다.",
+ "smw_noclosingbrackets": "ì¿¼ë¦¬ì— \"<nowiki>[[</nowiki>\"ì— í•´ë‹¹í•˜ëŠ” \"]]\"를 닫지 않았습니다.",
+ "smw_misplacedsymbol": "\"$1\" 기호는 유용하지 ì•Šì€ ê³³ì— ì‚¬ìš©í–ˆìŠµë‹ˆë‹¤.",
+ "smw_unexpectedpart": "ì¿¼ë¦¬ì˜ \"$1\" ë¶€ë¶„ì€ ì´í•´í•  수 없습니다.\n결과는 예ìƒí•˜ì§€ ì•Šì„ ìˆ˜ 있습니다.",
+ "smw_emptysubquery": "ì¼ë¶€ 하위 쿼리는 올바른 ì¡°ê±´ì´ ì—†ìŠµë‹ˆë‹¤.",
+ "smw_misplacedsubquery": "ì¼ë¶€ 하위 쿼리는 하위 쿼리를 허용하지 ì•Šì€ ê³³ì— ì‚¬ìš©í–ˆìŠµë‹ˆë‹¤.",
+ "smw_valuesubquery": "하위 쿼리는 \"$1\" ì†ì„±ì˜ ê°’ì— ì§€ì›í•˜ì§€ 않습니다.",
+ "smw_badqueryatom": "ì¿¼ë¦¬ì˜ ì¼ë¶€ \"<nowiki>[[…]]</nowiki>\" ë¶€ë¶„ì€ ì´í•´í•  수 없습니다.",
+ "smw_propvalueproblem": "\"$1\" ì†ì„±ì˜ ê°’ì€ ì´í•´í•  수 없습니다.",
+ "smw_noqueryfeature": "ì¼ë¶€ 쿼리 ê¸°ëŠ¥ì€ ì´ ìœ„í‚¤ì—ì„œ 지ì›í•˜ì§€ ì•Šì•„ ì¿¼ë¦¬ì˜ ë¶€ë¶„ì„ ì—†ì•´ìŠµë‹ˆë‹¤. ($1)",
+ "smw_noconjunctions": "ì¿¼ë¦¬ì˜ ì ‘ì†ì‚¬ëŠ” ì´ ìœ„í‚¤ì—ì„œ 지ì›í•˜ì§€ ì•Šì•„ ì¿¼ë¦¬ì˜ ë¶€ë¶„ì„ ì—†ì•´ìŠµë‹ˆë‹¤. ($1)",
+ "smw_nodisjunctions": "ì¿¼ë¦¬ì˜ ë¶„ë¦¬ì‚¬ëŠ” ì´ ìœ„í‚¤ì—ì„œ 지ì›í•˜ì§€ ì•Šì•„ ì¿¼ë¦¬ì˜ ë¶€ë¶„ì„ ì—†ì•´ìŠµë‹ˆë‹¤. ($1)",
+ "smw_querytoolarge": "ì¿¼ë¦¬ì˜ í¬ê¸°ë‚˜ 깊ì´ì— 대한 ìœ„í‚¤ì˜ ì œí•œìœ¼ë¡œ ì¸í•´ 다ìŒì˜ {{PLURAL:$2|쿼리 ì¡°ê±´ì€|쿼리 ì¡°ê±´ $2개는}} 고려하지 못했습니다: <code>$1</code>.",
+ "smw_notemplategiven": "ìž‘ë™í•  ì´ ì¿¼ë¦¬ 형ì‹ì— \"í‹€\" ë³€ìˆ˜ì— ëŒ€í•œ ê°’ì„ ì œê³µí•˜ì„¸ìš”.",
+ "smw_db_sparqlqueryproblem": "쿼리 결과는 SPARQL ë°ì´í„°ë² ì´ìŠ¤ì—ì„œ 가져올 수 없습니다. ì´ ì˜¤ë¥˜ëŠ” ì¼ì‹œì ì´ê±°ë‚˜ ë°ì´í„°ë² ì´ìŠ¤ ì†Œí”„íŠ¸ì›¨ì–´ì˜ ë²„ê·¸ì¼ ìˆ˜ 있습니다.",
+ "smw_db_sparqlqueryincomplete": "쿼리 ì‘ë‹µì´ ë„ˆë¬´ 어려운 것으로 ë°í˜€ì ¸ 중단ë˜ì—ˆìŠµë‹ˆë‹¤. ì¼ë¶€ 결과는 사ë¼ì¡Œì„ 수 있습니다. 가능하면 간단한 쿼리를 대신 사용하세요.",
+ "smw_type_header": "\"$1\" ìœ í˜•ì˜ ì†ì„±",
+ "smw_typearticlecount": "ì´ ìœ í˜•ì„ ì‚¬ìš©í•˜ì—¬ {{PLURAL:$1|ì†ì„±}} $1개를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw_attribute_header": "\"$1\" ìœ í˜•ì„ ì‚¬ìš©í•œ 문서",
+ "smw_attributearticlecount": "ì´ ì†ì„±ì„ 사용하는 {{PLURAL:$1|문서}} $1개를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-propertylist-subproperty-header": "하위 ì†ì„±",
+ "smw-propertylist-redirect-header": "ë™ì˜ì–´",
+ "smw-propertylist-count": "$1 관련 {{PLURAL:$1|엔티티}}를 표시합니다.",
+ "smw-propertylist-count-with-restricted-note": "$1 관련 {{PLURAL:$1|엔티티}}를 표시합니다. (ë” ë§Žì€ ì—”í‹°í‹°ë¥¼ ë³¼ 수 있으나 표시는 \"$2\"ì— êµ­í•œë©ë‹ˆë‹¤)",
+ "smw-propertylist-count-more-available": "$1 관련 {{PLURAL:$1|엔티티}}를 표시합니다. (ë” ë§Žì€ ì—”í‹°í‹°ë¥¼ ë³¼ 수 있습니다)",
+ "specialpages-group-smw_group": "시맨틱 미디어위키",
+ "exportrdf": "RDF로 문서 내보내기",
+ "smw_exportrdf_docu": "ì´ ë¬¸ì„œëŠ” RDF 형ì‹ì˜ 문서ì—ì„œ ë°ì´í„°ë¥¼ ì–»ì„ ìˆ˜ 있습니다.\n문서를 내보내려면 아래 í…스트 ìƒìžì— 줄마다 제목 하나를 입력하세요.",
+ "smw_exportrdf_recursive": "모든 ê´€ë ¨ëœ ë¬¸ì„œë¥¼ 재귀ì ìœ¼ë¡œ 내보냅니다.\n결과가 커질 수 있ìŒì„ 참고하세요!",
+ "smw_exportrdf_backlinks": "ë˜í•œ 내보낸 문서를 참고하는 모든 문서를 내보냅니다.\n찾아볼 수 있는 RDF를 ìƒì„±í•©ë‹ˆë‹¤.",
+ "smw_exportrdf_lastdate": "지정한 시간 ì´í›„ì— ë°”ë€Œì§€ ì•Šì€ ë¬¸ì„œëŠ” 내보내지 않습니다.",
+ "smw_exportrdf_submit": "내보내기",
+ "uriresolver": "URIí•´ê²°",
+ "properties": "ì†ì„± 목ë¡",
+ "smw_properties_docu": "ë‹¤ìŒ ì†ì„±ì€ 위키ì—ì„œ 사용합니다.",
+ "smw_property_template": "$2 ìœ í˜•ì˜ $1 ($3ê°œ {{PLURAL:$3|사용}})",
+ "smw_propertylackspage": "모든 ì†ì„±ì€ 문서ì—ì„œ 설명해야 합니다.",
+ "smw_propertylackstype": "ì´ ì†ì„±ì— 지정한 ìœ í˜•ì´ ì—†ìŠµë‹ˆë‹¤. (ì§€ê¸ˆì€ $1 유형으로 가정합니다)",
+ "smw_propertyhardlyused": "ì´ ì†ì„±ì€ ê±°ì˜ ìœ„í‚¤ ì•ˆì— ì‚¬ìš©í•˜ì§€ 않습니다!",
+ "smw-property-name-invalid": "$1 ì†ì„±ì€ 사용할 수 없습니다. (ìž˜ëª»ëœ ì†ì„± ì´ë¦„)",
+ "smw-property-name-reserved": "\"$1\"ì€(는) 예약어로 등재ë˜ì—ˆìœ¼ë¯€ë¡œ ì†ì„±ìœ¼ë¡œ 사용해서는 안 ë©ë‹ˆë‹¤. 다ìŒì˜ [https://www.semantic-mediawiki.org/wiki/Help:Property_naming ë„ì›€ë§ ë¬¸ì„œ]ì—는 ì´ ì´ë¦„ì´ ì˜ˆì•½ëœ ì´ìœ ì— 관한 ì •ë³´ê°€ í¬í•¨ë˜ì–´ ìžˆì„ ìˆ˜ 있습니다.",
+ "smw-sp-property-searchform": "다ìŒì„ í¬í•¨í•˜ëŠ” ì†ì„± 표시:",
+ "smw-sp-property-searchform-inputinfo": "ìž…ë ¥ì€ ëŒ€ì†Œë¬¸ìžë¥¼ 구분하여 í•„í„°ì— ì‚¬ìš©ë˜ë©°, ì¡°ê±´ê³¼ ì¼ì¹˜í•˜ëŠ” ì†ì„±ë§Œ 보입니다.",
+ "smw-special-property-searchform": "다ìŒì„ í¬í•¨í•˜ëŠ” ì†ì„± 표시:",
+ "smw-special-property-searchform-inputinfo": "ìž…ë ¥ì€ ëŒ€ì†Œë¬¸ìžë¥¼ 구분하여 í•„í„°ì— ì‚¬ìš©ë˜ë©°, ì¡°ê±´ê³¼ ì¼ì¹˜í•˜ëŠ” ì†ì„±ë§Œ 보입니다.",
+ "smw-special-property-searchform-options": "옵션",
+ "smw-special-wantedproperties-filter-label": "í•„í„°:",
+ "smw-special-wantedproperties-filter-none": "ì—†ìŒ",
+ "smw-special-wantedproperties-filter-unapproved": "미승ì¸",
+ "smw-special-wantedproperties-filter-unapproved-desc": "ì „ê±° 모드와 관련ë˜ì–´ 사용ë˜ëŠ” í•„í„° 옵션입니다.",
+ "concepts": "ê°œë…",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts ê°œë…]ì€ \"ë™ì  분류\", 즉 수ë™ìœ¼ë¡œ 만들어지지 않지만 주어진 ì¿¼ë¦¬ì˜ ì„¤ëª…ì—ì„œ 시맨틱 미디어위키가 계산한 ë¬¸ì„œì˜ ëª¨ìŒì§‘으로 ë³¼ 수 있습니다.",
+ "smw-special-concept-header": "ê°œë… ëª©ë¡",
+ "smw-special-concept-count": "ë‹¤ìŒ {{PLURAL:$1|ê°œë…|ê°œë… $1ê°œ}}{{PLURAL:$1|ì€|는}} 나열ë˜ì–´ 있습니다.",
+ "smw-special-concept-empty": "ê°œë…ì„ ì°¾ì„ ìˆ˜ 없습니다.",
+ "unusedproperties": "사용하지 않는 ì†ì„± 목ë¡",
+ "smw-unusedproperties-docu": "ì´ ë¬¸ì„œëŠ” ì„ ì–¸ì€ ë˜ì—ˆìœ¼ë‚˜ 다른 문서ì—ì„œ 사용하지 않는 [https://www.semantic-mediawiki.org/wiki/Unused_properties 미사용 ì†ì„±]ì„ ë‚˜ì—´í•©ë‹ˆë‹¤. ì°¨ë³„í™”ëœ ë·°ì— ëŒ€í•´ì„œëŠ” [[Special:Properties|ì „ì²´]] ë˜ëŠ” [[Special:WantedProperties|í•„ìš” ì†ì„±]] 특수 문서를 참고하십시오.",
+ "smw-unusedproperty-template": "$2 ìœ í˜•ì˜ $1",
+ "wantedproperties": "필요한 ì†ì„± 목ë¡",
+ "smw-wantedproperties-docu": "ì´ ë¬¸ì„œëŠ” 위키ì—는 ì“°ì´ì§€ë§Œ 설명하는 문서가 없는 [https://www.semantic-mediawiki.org/wiki/Wanted_properties í•„ìš” ì†ì„±]ì„ ë‚˜ì—´í•©ë‹ˆë‹¤. ì°¨ë³„í™”ëœ ë·°ì— ëŒ€í•´ì„œëŠ” [[Special:Properties|ì „ì²´]] ë˜ëŠ” [[Special:UnusedProperties|미사용 ì†ì„±]] 특수 문서를 참고하십시오.",
+ "smw-wantedproperty-template": "$1 ($2개 {{PLURAL:$2|사용}})",
+ "smw-special-wantedproperties-template": "$1 ($2개 {{PLURAL:$2|사용}})",
+ "smw_purge": "새로 고침",
+ "smw-purge-failed": "새로 고침 실패",
+ "types": "유형 목ë¡",
+ "smw_types_docu": "í• ë‹¹ëœ ì†ì„±ì— ìƒì†ë˜ëŠ” 저장 ë° í‘œì‹œ ê¸°ëŠ¥ì— ë”°ë¼ ê°’ì„ ê¸°ìˆ í•˜ê¸° 위해 ê°ê°ì˜ [https://www.semantic-mediawiki.org/wiki/Help:Datatype ìžë£Œí˜•]ì— ê³ ìœ í•œ ì†ì„± ì§‘í•©ì„ í‘œí˜„í•œ [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes 사용 가능한 ìžë£Œí˜•] 목ë¡ìž…니다.",
+ "smw-special-types-no-such-type": "\"$1\"ì€(는) ì•Œ 수 없거나 ì§€ì •ëœ ìžë£Œí˜•ìœ¼ë¡œ 지정ë˜ì–´ 있지 않습니다.",
+ "smw-statistics": "시맨틱 통계",
+ "smw-statistics-property-instance": "(ì´) ì†ì„± {{PLURAL:$1|ê°’}}",
+ "smw-statistics-property-total": "(ì´) [[Special:Properties|{{PLURAL:$1|ì†ì„±}}]]",
+ "smw-statistics-property-total-legacy": "(ì´) {{PLURAL:$1|ì†ì„±}}",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|ì†ì„±}}]] (ì ì–´ë„ í•˜ë‚˜ì˜ ê°’ê³¼ 함께 사용ë¨)",
+ "smw-statistics-property-page": "(ë¬¸ì„œì— ë“±ë¡ëœ) {{PLURAL:$1|ì†ì„±}}",
+ "smw-statistics-property-type": "(ë°ì´í„°ìœ í˜•ìœ¼ë¡œ 할당ëœ) {{PLURAL:$1|ì†ì„±}}",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|쿼리|쿼리}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|쿼리|쿼리}}]]",
+ "smw-statistics-query-size": "쿼리 í¬ê¸°",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|ê°œë…}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|ê°œë…}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|하위개체|하위개체}}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|ë°ì´í„°ìœ í˜•}}]]",
+ "smw-statistics-delete-count": "ì˜¤ëž˜ëœ {{PLURAL:$1|개체|개체들}} (삭제를 위한 표시)",
+ "smw_uri_doc": "URI 해결기는 [$1 W3C TAGì˜ httpRange-14ì— ì°¾ì€ í‘œê¸°]를 구현합니다.\nì´ê²ƒì€ ì‚¬ëžŒì´ ì›¹ì‚¬ì´íŠ¸ì— 향하지 ì•Šë„ë¡ ì£¼ì˜í•©ë‹ˆë‹¤.",
+ "ask": "시맨틱 검색",
+ "smw_ask_sortby": "ì—´ 순서로 ì •ë ¬ (ì„ íƒ ì‚¬í•­)",
+ "smw_ask_ascorder": "오름차순",
+ "smw_ask_descorder": "내림차순",
+ "smw-ask-order-rand": "ìž„ì˜ ìˆœì„œë¡œ",
+ "smw_ask_submit": "결과 찾기",
+ "smw_ask_editquery": "쿼리 편집",
+ "smw_add_sortcondition": "[정렬 조건 추가]",
+ "smw-ask-sort-add-action": "정렬 조건 추가",
+ "smw_ask_hidequery": "쿼리 숨기기 (간략히 보기)",
+ "smw_ask_help": "쿼리 ë„움ë§",
+ "smw_ask_queryhead": "ì¡°ê±´",
+ "smw_ask_printhead": "출력 ì„ íƒ",
+ "smw_ask_printdesc": "(줄마다 ì†ì„± ì´ë¦„ 하나를 추가하세요)",
+ "smw_ask_format_as": "ì„œì‹:",
+ "smw_ask_defaultformat": "기본값",
+ "smw_ask_otheroptions": "다른 옵션",
+ "smw-ask-otheroptions-info": "ì´ ë¬¸ë‹¨ì€ ì¶œë ¥ ë¬¸ì„ ë°”ê¾¸ëŠ” ì˜µì…˜ì´ ìžˆìŠµë‹ˆë‹¤. 변수 ì„¤ëª…ì€ ë³€ìˆ˜ 위로 가리켜 ë³¼ 수 있습니다.",
+ "smw-ask-otheroptions-collapsed-info": "사용 가능한 모든 ì˜µì…˜ì„ ë³´ë ¤ë©´ ë”하기 ì•„ì´ì½˜ì„ 사용하세요",
+ "smw_ask_show_embed": "í¬í•¨í•œ 코드 ë³´ì´ê¸°",
+ "smw_ask_hide_embed": "í¬í•¨í•œ 코드 숨기기",
+ "smw_ask_embed_instr": "ì´ ì¿¼ë¦¬ë¥¼ 위키 ë¬¸ì„œì— ì¸ë¼ì¸ìœ¼ë¡œ í¬í•¨í•˜ë ¤ë©´ ë‹¤ìŒ ì½”ë“œë¥¼ 사용하세요.",
+ "smw-ask-delete": "제거",
+ "smw-ask-sorting": "ì •ë ¬",
+ "smw-ask-options": "옵션",
+ "smw-ask-options-sort": "정렬 옵션",
+ "smw-ask-format-options": "í˜•ì‹ ë° ì˜µì…˜",
+ "smw-ask-parameters": "변수",
+ "smw-ask-search": "검색",
+ "smw-ask-debug": "디버그",
+ "smw-ask-debug-desc": "쿼리 디버그 정보를 ìƒì„±í•©ë‹ˆë‹¤",
+ "smw-ask-no-cache": "쿼리 ìºì‹œ 비활성화",
+ "smw-ask-no-cache-desc": "쿼리 ìºì‹œê°€ 없는 ê²°ê³¼",
+ "smw-ask-result": "ê²°ê³¼",
+ "smw-ask-empty": "모든 항목 비우기",
+ "smw-ask-download-link-desc": "ì¡°íšŒëœ ê²°ê³¼ë¥¼ $1 형ì‹ìœ¼ë¡œ 다운로드합니다",
+ "smw-ask-format": "형ì‹",
+ "smw-ask-format-selection-help": "ì„ íƒëœ 형ì‹ì˜ ë„움ë§: $1",
+ "smw-ask-condition-change-info": "ì¡°ê±´ì´ ë³€ê²½ë˜ì—ˆìœ¼ë¯€ë¡œ 검색 ì—”ì§„ì€ ìƒˆë¡œìš´ ìš”êµ¬ì‚¬í•­ì„ ì¶©ì¡±í•˜ëŠ” 결과를 출력하기 위해 쿼리를 다시 실행해야 합니다.",
+ "smw-ask-input-assistance": "ìž…ë ¥ ë³´ì¡°",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ìž…ë ¥ 지ì›]ì€ ì¶œë ¥, ì •ë ¬, ì¡°ê±´ 필드를 위해 제공ë©ë‹ˆë‹¤. ì´ ì¡°ê±´ 필드는 다ìŒì˜ ì ‘ë‘사 중 하나를 사용해야 합니다:",
+ "smw-ask-condition-input-assistance-property": "ì†ì„± ì œì•ˆì„ ê°€ì ¸ì˜¤ë ¤ë©´ <code>p:</code> (예: <code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "분류 ì œì•ˆì„ ê°€ì ¸ì˜¤ë ¤ë©´ <code>c:</code>",
+ "smw-ask-condition-input-assistance-concept": "ê°œë… ì œì•ˆì„ ê°€ì ¸ì˜¤ë ¤ë©´ <code>con:</code>",
+ "smw-ask-format-change-info": "í¬ë§·ì´ 수정ë˜ì—ˆìœ¼ë¯€ë¡œ 새로운 변수와 ì‹œê°í™” ì˜µì…˜ì„ ì¼ì¹˜ì‹œí‚¤ë ¤ë©´ 쿼리를 다시 실행해야 합니다.",
+ "smw-ask-format-export-info": "ì„ íƒëœ 형ì‹ì€ ì‹œê° í‘œí˜„ì´ ì—†ëŠ” 내보내기 형ì‹ì´ë¯€ë¡œ 결과는 다운로드로만 제공ë©ë‹ˆë‹¤.",
+ "searchbyproperty": "ì†ì„±ìœ¼ë¡œ 검색",
+ "processingerrorlist": "오류 목ë¡ì„ 처리합니다",
+ "propertylabelsimilarity": "ì†ì„± ë ˆì´ë¸” 유사성 ë³´ê³ ì„œ",
+ "smw_sbv_docu": "주어진 ì†ì„±ê³¼ ê°’ì„ ê°€ì§€ê³  모든 문서를 검색합니다.",
+ "smw_sbv_novalue": "ì†ì„±ì— 대한 올바른 ê°’ì„ ìž…ë ¥í•˜ê±°ë‚˜ \"$1\"ì— ëŒ€í•œ 모든 ì†ì„± ê°’ì„ ë³´ì„¸ìš”.",
+ "smw_sbv_displayresult": "\"$2\" 값으로 \"$1\" ì†ì„±ì„ 가진 모든 ë¬¸ì„œì˜ ëª©ë¡ìž…니다",
+ "smw_sbv_displayresultfuzzy": "\"$2\" ê°’ì˜ \"$1\" ì†ì„±ì„ 가진 모든 ë¬¸ì„œì˜ ëª©ë¡ìž…니다.\n결과가 얼마 안 ë˜ê¸° ë•Œë¬¸ì— ì£¼ë³€ì˜ ê°’ì„ í‘œì‹œí•©ë‹ˆë‹¤.",
+ "smw_sbv_property": "ì†ì„±:",
+ "smw_sbv_value": "ê°’:",
+ "smw_sbv_submit": "결과 찾기",
+ "browse": "위키 찾아보기",
+ "smw_browselink": "ì†ì„± 찾아보기",
+ "smw_browse_article": "찾아보기를 시작하려면 ë¬¸ì„œì˜ ì´ë¦„ì„ ìž…ë ¥í•˜ì„¸ìš”.",
+ "smw_browse_go": "보기",
+ "smw_browse_show_incoming": "들어오는 ì†ì„± 표시",
+ "smw_browse_hide_incoming": "들어오는 ì†ì„± 숨기기",
+ "smw_browse_no_outgoing": "ì´ ë¬¸ì„œì—는 ì†ì„±ì´ 없습니다.",
+ "smw_browse_no_incoming": "ì´ ë¬¸ì„œì— ë§í¬í•œ ì†ì„±ì´ 없습니다.",
+ "smw-browse-from-backend": "정보는 현재 백엔드ì—ì„œ 검색ë˜ê³  있습니다.",
+ "smw-browse-api-subject-serialization-invalid": "ì´ ì£¼ì œì—는 유효하지 ì•Šì€ ì§ë ¬í™” í¬ë§·ì´ 있습니다.",
+ "smw-browse-js-disabled": "ìžë°”스í¬ë¦½íŠ¸ê°€ 비활성화ë˜ì–´ 있거나 사용할 수 없는 것으로 ì˜ì‹¬ë˜ë©°, 지ì›í•˜ëŠ” 브ë¼ìš°ì €ë¥¼ 사용할 ê²ƒì„ ê¶Œê³ í•©ë‹ˆë‹¤. 다른 ì˜µì…˜ì€ [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>] 구성 변수 문서ì—ì„œ ë…¼ì˜ë©ë‹ˆë‹¤.",
+ "smw-browse-show-group": "그룹 표시",
+ "smw-browse-hide-group": "그룹 숨기기",
+ "smw_inverse_label_default": "$1입니다",
+ "smw_inverse_label_property": "ì—­ ì†ì„± ë ˆì´ë¸”",
+ "pageproperty": "문서 ì†ì„± 검색",
+ "smw_pp_docu": "문서와 ì†ì„±ì„ 입력하십시오. í• ë‹¹ëœ ëª¨ë“  ê°’ì„ ê°€ì ¸ì˜¤ë ¤ë©´ ì†ì„±ë§Œ 입력하십시오.",
+ "smw_pp_from": "문서ì—ì„œ:",
+ "smw_pp_type": "ì†ì„±:",
+ "smw_pp_submit": "결과 찾기",
+ "smw_result_prev": "ì´ì „",
+ "smw_result_next": "다ìŒ",
+ "smw_result_results": "ê²°ê³¼",
+ "smw_result_noresults": "결과가 없습니다.",
+ "smwadmin": "관리 ë° ìœ ì§€ë³´ìˆ˜ 기능",
+ "smw-admin-statistics-job-title": "작업 통계",
+ "smw-admin-statistics-querycache-title": "쿼리 ìºì‹œ 통계",
+ "smw-admin-setupsuccess": "저장소 ì—”ì§„ì„ ì„¤ì •í–ˆìŠµë‹ˆë‹¤.",
+ "smw_smwadmin_return": "$1 문서로 ëŒì•„갑니다",
+ "smw_smwadmin_updatestarted": "시맨틱 ë°ì´í„°ì˜ 최신화를 위한 새 ì—…ë°ì´íŠ¸ ê³¼ì •ì´ ì‹œìž‘ë˜ì—ˆìŠµë‹ˆë‹¤.\nì €ìž¥ëœ ëª¨ë“  ë°ì´í„°ëŠ” í•„ìš”ì— ë”°ë¼ ë‹¤ì‹œ 구성ë˜ê±°ë‚˜ 복구ë©ë‹ˆë‹¤.\nì´ íŠ¹ìˆ˜ ë¬¸ì„œì— ëŒ€í•œ ì—…ë°ì´íŠ¸ì˜ ì§„í–‰ì„ ë”°ë¥¼ 수 있습니다.",
+ "smw_smwadmin_updatenotstarted": "ì´ë¯¸ 실행 ì¤‘ì¸ ì—…ë°ì´íŠ¸ 프로세스가 있습니다.\n새로 만들지 않습니다.",
+ "smw_smwadmin_updatestopped": "ê¸°ì¡´ì˜ ëª¨ë“  ì—…ë°ì´íŠ¸ ê³¼ì •ì´ ì¤‘ë‹¨ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw_smwadmin_updatenotstopped": "실행하고 있는 ì—…ë°ì´íŠ¸ ê³¼ì •ì„ ì¤‘ì§€í•˜ë ¤ë©´ ì •ë§ í™•ì‹¤í•œì§€ 나타내는 확ì¸ëž€ì„ 활성화해야 합니다.",
+ "smw-admin-docu": "ì´ íŠ¹ìˆ˜ 문서는 <a href=\"https://www.semantic-mediawiki.org\">시맨틱 미디어위키</a>ì˜ ì„¤ì¹˜, 업그레ì´ë“œ, 정비, 사용 ì¤‘ì— ë„ì›€ì„ ì£¼ë©° 추가ì ì¸ 관리 기능과 ìž‘ì—…, 통계 ë˜í•œ 제공합니다. 관리 ê¸°ëŠ¥ì„ ì‹¤í–‰í•˜ê¸° ì „ì— ì¤‘ìš”í•œ ë°ì´í„°ëŠ” 백업해 주십시오.",
+ "smw-admin-environment": "소프트웨어 환경",
+ "smw-admin-db": "ë°ì´í„°ë² ì´ìŠ¤ 설정",
+ "smw-admin-db-preparation": "í…Œì´ë¸” 초기화가 진행 중ì´ë©° í¬ê¸° ë° ìž ìž¬ì ì¸ í…Œì´ë¸” 최ì í™”ê°€ 대기 중ì´ë¯€ë¡œ 결과가 표시ë˜ëŠ”ë° ì‹œê°„ì´ ê±¸ë¦´ 수 있습니다.",
+ "smw-admin-dbdocu": "시맨틱 미디어위키는 시멘틱 ë°ì´í„°ë¥¼ 저장하기 위해 미디어위키 ë°ì´í„°ë² ì´ìŠ¤ì— 몇 가지 확장 ê¸°ëŠ¥ì´ í•„ìš”í•©ë‹ˆë‹¤.\nì•„ëž˜ì˜ ê¸°ëŠ¥ì€ ë°ì´í„°ë² ì´ìŠ¤ê°€ 제대로 설정ë˜ì–´ 있는지 확ì¸í•©ë‹ˆë‹¤.\nì´ ë‹¨ê³„ì—ì„œ ë°”ë€ ë‚´ìš©ì€ ë¯¸ë””ì–´ìœ„í‚¤ ë°ì´í„°ë² ì´ìŠ¤ì˜ ë‚˜ë¨¸ì§€ì— ì˜í–¥ì„ 주지 ì•Šê³ , ì›í•œë‹¤ë©´ 쉽게 ë˜ëŒë¦´ 수 있습니다.\nì´ ì„¤ì • ê¸°ëŠ¥ì€ ì–´ë– í•œ 피해 ì—†ì´ ì—¬ëŸ¬ 번 ì‹¤í–‰ë  ìˆ˜ 있지만, 설치나 업그레ì´ë“œì—는 í•œ 번만 필요합니다.",
+ "smw-admin-permissionswarn": "SQL 오류로 ìž‘ì—…ì´ ì‹¤íŒ¨í•˜ë©´, 위키가 ê³ ìš©í•œ ë°ì´í„°ë² ì´ìŠ¤ 사용ìž(LocalSettings.php를 확ì¸í•˜ì„¸ìš”)는 ì•„ë§ˆë„ ì¶©ë¶„í•œ ê¶Œí•œì´ ì—†ì„ ê²ƒìž…ë‹ˆë‹¤.\ní…Œì´ë¸”ì„ ë§Œë“¤ê³  삭제할 추가 ê¶Œí•œì„ ì´ ì‚¬ìš©ìžì—게 부여하거나, ì¼ì‹œì ìœ¼ë¡œ LocalSettings.phpì— ë°ì´í„°ë² ì´ìŠ¤ ë£¨íŠ¸ì˜ ë¡œê·¸ì¸ì„ 입력하거나, 관리ìžì˜ ìžê²© ì¦ëª…ì„ ì‚¬ìš©í•  수 있는 <code>setupStore.php</code> 유지 관리 스í¬ë¦½íŠ¸ë¥¼ 사용하세요.",
+ "smw-admin-dbbutton": "표를 초기화하거나 업그레ì´ë“œ",
+ "smw-admin-announce": "내 위키 발표",
+ "smw-admin-deprecation-notice-title": "êµ¬ì‹ ì•Œë¦¼",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>ì€(는) 구ì‹ì´ë¯€ë¡œ $2ì—ì„œ 제거ë˜ì—ˆìŠµë‹ˆë‹¤",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>ì€(는) \n다ìŒì˜ {{PLURAL:$2|옵션}}ì„ ì œê±°(ë˜ëŠ” 대체)í•  것입니다:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code>ì€(는) 사용ë˜ì§€ 않으며 $2ì— ì œê±°ë©ë‹ˆë‹¤",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>ì€(는) <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>으(ë¡œ) 변경ë˜ì—ˆìŠµë‹ˆë‹¤",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|옵션}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code>ì€(는) <code>$2</code>(으)ë¡œ 대체ë˜ê³  있습니다",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>ì´(ê°€) 제거ë˜ì—ˆìŠµë‹ˆë‹¤ ($2)",
+ "smw-admin-deprecation-notice-title-notice": "들어오는 바뀜",
+ "smw-admin-deprecation-notice-title-notice-explanation": "다ìŒì˜ ì„¤ì •ì´ ì´ ìœ„í‚¤ì—ì„œ 사용ë˜ê³  있ìŒì„ 확ì¸í–ˆìœ¼ë©° 미래 출시íŒì—는 제거ë˜ê±°ë‚˜ ë³€ê²½ë  ì˜ˆì •ìž…ë‹ˆë‹¤.",
+ "smw-admin-deprecation-notice-title-replacement": "치환ë˜ê±°ë‚˜ ì´ë¦„ì´ ë³€ê²½ëœ ì„¤ì •",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "ì´ë¦„ì´ ë³€ê²½ë˜ê±°ë‚˜ ìˆ˜ì •ëœ ì„¤ì •ì— ëŒ€í•´ ì´ë¦„ì´ë‚˜ 형ì‹ì„ ì—…ë°ì´íŠ¸í•´ 주십시오.",
+ "smw-admin-deprecation-notice-title-removal": "ì œê±°ëœ ì„¤ì •",
+ "smw-admin-deprecation-notice-title-removal-explanation": "ë‚˜ì—´ëœ ì„¤ì •ì€ ì´ì „íŒì—서는 제거ë˜ì—ˆìœ¼ë‚˜ ì´ ìœ„í‚¤ì—서는 사용ë˜ê³  있ìŒì´ 확ì¸ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-smwadmin-refresh-title": "ë°ì´í„° 복구 ë° ì—…ë°ì´íŠ¸",
+ "smw_smwadmin_datarefresh": "ë°ì´í„° 재ìƒì„±",
+ "smw_smwadmin_datarefreshdocu": "ìœ„í‚¤ì˜ í˜„ìž¬ ë‚´ìš©ì— ë”°ë¼ ëª¨ë“  시맨틱 미디어위키 ë°ì´í„°ë¥¼ 복구할 수 있습니다.\n안쪽 형ì‹ì´ ì–´ë–¤ 소프트웨어 업그레ì´ë“œë¡œ ì¸í•´ 바뀌었으면 깨진 ë°ì´í„°ë¥¼ 복구하거나 ë°ì´í„°ë¥¼ 새로 고치는 ë° ìœ ìš©í•  수 있습니다.\nì—…ë°ì´íŠ¸ëŠ” 문서 단위로 실행ë˜ê³  즉시 완료ë˜ì§€ 않습니다.\n다ìŒì€ ì—…ë°ì´íŠ¸ê°€ 진행하고 있으면 보여주고 (ì´ ê¸°ëŠ¥ì´ ì‚¬ì´íŠ¸ 관리ìžê°€ 비활성화하지 않는 í•œ) ì—…ë°ì´íŠ¸ë¥¼ 시작하거나 멈출 수 있습니다.",
+ "smw_smwadmin_datarefreshprogress": "<strong>ì—…ë°ì´íŠ¸ê°€ ì´ë¯¸ 진행 중입니다.</strong>\n사용ìžê°€ ìœ„í‚¤ì— ì ‘ê·¼í•  때마다 ìž‘ì€ ë©ì–´ë¦¬ì˜ 새로 고침 ë°ì´í„° 진행만 있기 ë•Œë¬¸ì— ì—…ë°ì´íŠ¸ ì§„í–‰ì´ ëŠë¦° ê²ƒì€ ì •ìƒìž…니다.\në” ë¹ ë¥´ê²Œ ì´ ì—…ë°ì´íŠ¸ë¥¼ 마치려면 <code>runJobs.php</code> 미디어위키 유지 보수 스í¬ë¦½íŠ¸ë¥¼ 호출할 수 있습니다. (ì¼ê´„ 하나ì—ì„œ 수행하는 ì—…ë°ì´íŠ¸ì˜ 수를 제한하려면 <code>--maxjobs 1000</code> ì˜µì…˜ì„ ì‚¬ìš©í•˜ì„¸ìš”)\n현재 ì—…ë°ì´íŠ¸ì˜ 예ìƒëœ 진행:",
+ "smw_smwadmin_datarefreshbutton": "ë°ì´í„° 다시 빌드 예약",
+ "smw_smwadmin_datarefreshstop": "ì´ ì—…ë°ì´íŠ¸ 중지",
+ "smw_smwadmin_datarefreshstopconfirm": "예, {{GENDER:$1|확실합니다}}.",
+ "smw-admin-outdateddisposal-active": "ë§Œê¸°ëœ í•­ëª©ì˜ ì œê±° ìž‘ì—…ì´ ì˜ˆì•½ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-admin-feature-disabled": "ì´ ê¸°ëŠ¥ì€ ì´ ìœ„í‚¤ì—ì„œ 비활성화ë˜ì–´ 있으므로 <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">설정</a> ë„ì›€ë§ íŽ˜ì´ì§€ë¥¼ 참조하거나 시스템 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤.",
+ "smw-admin-propertystatistics-active": "ì†ì„± 통계 다시 빌드 ìž‘ì—…ì´ ì˜ˆì•½ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-admin-fulltext-title": "전문 검색 재구성",
+ "smw-admin-fulltext-active": "전문 검색 다시 빌드 ìž‘ì—…ì´ ì˜ˆì•½ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-admin-support": "ì§€ì› ì–»ê¸°",
+ "smw-admin-supportdocu": "문제가 ìžˆì„ ë•Œ ë„ì›€ì´ ë  ë§Œí•œ 다양한 ìžë£Œê°€ 제공ë©ë‹ˆë‹¤:",
+ "smw-admin-installfile": "설치하는 ë° ë¬¸ì œê°€ ë°œìƒí•˜ì˜€ë‹¤ë©´, <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL 파ì¼</a>ì—ì„œ ì§€ì¹¨ì„ í™•ì¸í•˜ê³  시작하세요.",
+ "smw-admin-smwhomepage": "시맨틱 ë¯¸ë””ì–´ìœ„í‚¤ì˜ ì™„ì „í•œ ì‚¬ìš©ìž ì„¤ëª…ì„œëŠ” <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>ì— ìžˆìŠµë‹ˆë‹¤.",
+ "smw-admin-bugsreport": "버그는 <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>ì— ë³´ê³ í•  수 있습니다.",
+ "smw-admin-questions": "만약 추가ì ì¸ 질문ì´ë‚˜ ì œì•ˆì´ ìžˆìœ¼ë©´, <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">시맨틱 미디어위키 ì‚¬ìš©ìž í¬ëŸ¼</a>ì—ì„œ í† ë¡ ì— ì°¸ì—¬í•˜ì„¸ìš”.",
+ "smw-admin-other-functions": "다른 기능",
+ "smw-admin-supplementary-section-title": "추가 기능",
+ "smw-admin-supplementary-section-subtitle": "사용 가능한 기능",
+ "smw-admin-supplementary-settings-title": "구성 설정",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u>ì€ ì‹œë§¨í‹± ë¯¸ë””ì–´ìœ„í‚¤ì— ì“°ì´ëŠ” 사용 가능한 ì„¤ì •ì˜ ê³µí†µ 목ë¡ì„ 출력합니다",
+ "smw-admin-supplementary-operational-statistics-title": "조작 통계",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u>는 í™•ìž¥ëœ í†µê³„ ì§‘í•©ì„ í‘œì‹œí•©ë‹ˆë‹¤",
+ "smw-admin-supplementary-idlookup-title": "엔티티 검색 ë° ì²˜ë¦¬",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u>는 ê°œê°œì˜ ì—”í‹°í‹°ë¥¼ 검색하고 처리하기 위한 ê¸°ëŠ¥ì„ í¬í•¨í•©ë‹ˆë‹¤",
+ "smw-admin-supplementary-duplookup-title": "중복 개체",
+ "smw-admin-supplementary-duplookup-docu": "ì´ ë¬¸ì„œëŠ” [https://www.semantic-mediawiki.org/wiki/Help:Entity_table 개체 í‘œ]ì—ì„œ ì¤‘ë³µëœ ê²ƒìœ¼ë¡œ ë¶„ë¥˜ëœ ê°œì²´ë“¤ì˜ ëª©ë¡ìž…니다. 중복 ê°œì²´ë“¤ì€ (존재한다면) ë°ì´í„°ë² ì´ìŠ¤ ì—…ë°ì´íŠ¸ ì¤‘ì— í”„ë¡œì„¸ìŠ¤ê°€ 종료ë˜ê±°ë‚˜ 롤백 í–‰ë™ì´ 성공스럽지 ëª»í–ˆì„ ë•Œì™€ ê°™ì´ ë§¤ìš° 드문 경우ì—만 ë°œìƒí•  수 있습니다.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "ìºì‹œ 통계",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u>ì€(는) ìºì‹œ 관련 통계를 표시합니다",
+ "smw-admin-supplementary-elastic-title": "ì¼ëž˜ìŠ¤í‹±ì„œì¹˜",
+ "smw-admin-supplementary-elastic-docu": "ì´ ë¬¸ì„œëŠ” 시맨틱 미디어위키와 [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ì¼ëž˜ìŠ¤í‹±ìŠ¤í† ì–´</code>]ì— ì—°ê²°ëœ ì¼ëž˜ìŠ¤í‹±ì„œì¹˜ í´ëŸ¬ìŠ¤í„° 관련 설정, 매핑, ìƒíƒœ, ìƒ‰ì¸ í†µê³„ì— ê´€í•œ 정보를 í¬í•¨í•©ë‹ˆë‹¤.",
+ "smw-admin-supplementary-elastic-functions": "사용 가능한 기능",
+ "smw-admin-supplementary-elastic-settings-title": "설정",
+ "smw-admin-supplementary-elastic-mappings-title": "매핑",
+ "smw-admin-supplementary-elastic-mappings-summary": "요약",
+ "smw-admin-supplementary-elastic-mappings-fields": "필드 매핑",
+ "smw-admin-supplementary-elastic-nodes-title": "노드",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u>ì€(는) 노드 통계를 표시합니다",
+ "smw-admin-supplementary-elastic-indices-title": "색ì¸",
+ "smw-admin-supplementary-elastic-statistics-title": "통계",
+ "smw-admin-supplementary-elastic-status-replication": "레플리케ì´ì…˜ ìƒíƒœ",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "새로 고침 주기: $1",
+ "smw-list-count": "ì´ ëª©ë¡ì€ {{PLURAL:$1|항목}} $1개를 ê°–ê³  있습니다.",
+ "smw-list-count-from-cache": "ì´ ëª©ë¡ì€ {{PLURAL:$1|항목}} $1개를 ê°–ê³  있으며 $2(UTC)ì— ìºì‹œì—ì„œ 검색ë습니다.",
+ "smw-property-label-similarity-title": "ì†ì„± ë ˆì´ë¸” 유사성 ë³´ê³ ì„œ",
+ "smw-property-label-similarity-intro": "<u>$1</u> 문서는 기존 ì†ì„± ë ˆì´ë¸”ì˜ ìœ ì‚¬ì„±ì„ ê³„ì‚°í•©ë‹ˆë‹¤",
+ "smw-property-label-similarity-threshold": "한계치:",
+ "smw-property-label-similarity-type": "표시 유형 ID",
+ "smw-property-label-similarity-noresult": "ì„ íƒëœ 옵션ì—서는 아무 ê²°ê³¼ë„ ë°œê²¬ë˜ì§€ 않았습니다.",
+ "smw_adminlinks_datastructure": "ë°ì´í„° 구조",
+ "smw_adminlinks_displayingdata": "ë°ì´í„° 표시",
+ "smw_adminlinks_inlinequerieshelp": "ì¸ë¼ì¸ 쿼리 ë„움ë§",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|사용ìž|시스템}} ì •ì˜ëœ ì†ì„±",
+ "smw-concept-indicator-cache-update": "ìºì‹œ 카운트\n마지막 ì—…ë°ì´íŠ¸: $1",
+ "smw-createproperty-isproperty": "$1 ìœ í˜•ì˜ ì†ì„±ìž…니다.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|ì´ ì†ì„±ì— 대해 허용하는 ê°’}}:",
+ "smw-paramdesc-category-delim": "구분ìž",
+ "smw-paramdesc-category-template": "í•­ëª©ì˜ ì„œì‹ì„ 사용할 í‹€",
+ "smw-paramdesc-category-userparam": "í‹€ì„ ì „ë‹¬í•  변수",
+ "smw-info-par-message": "표시할 메시지입니다.",
+ "smw-info-par-icon": "\"info\"나 \"warning\" 중 하나를 보여주는 ì•„ì´ì½˜ìž…니다.",
+ "prefs-smw": "시맨틱 미디어위키",
+ "prefs-general-options": "ì¼ë°˜ 옵션",
+ "prefs-ask-options": "특수:Ask 옵션",
+ "smw-prefs-intro-text": "ì•„ëž˜ì˜ ì˜µì…˜ì€ [https://www.semantic-mediawiki.org/ 시맨틱 미디어위키](ë˜ëŠ” ê´€ë ¨ëœ í™•ìž¥ 기능)ê°€ ì„ íƒëœ ê¸°ëŠ¥ì— ëŒ€í•œ 개별 ì‚¬ìš©ìž ì§€ì •ì„ í™œì„±í™”í•˜ê¸° 위해 제공ë©ë‹ˆë‹¤. ìžì„¸í•œ ì •ë³´ì— ëŒ€í•´ì„œëŠ” ì´ [http://semantic-mediawiki.org/wiki/Help:User_preferences ë„ì›€ë§ ë¬¸ë‹¨]ì—ì„œ 보시기 ë°”ëžë‹ˆë‹¤.",
+ "smw-prefs-ask-options-tooltip-display": "ì •ë³´ ë§í’선으로 변수 í…스트 표시",
+ "smw-prefs-general-options-time-correction": "선호하는 지역 [[Special:Preferences#mw-prefsection-rendering|시간 오프셋]]ì„ ì‚¬ìš©í•˜ì—¬ 특수 ë¬¸ì„œì˜ ì‹œê°„ ì¡°ì •ì„ ì‚¬ìš©í•©ë‹ˆë‹¤",
+ "smw-prefs-general-options-jobqueue-watchlist": "ìž‘ì—… 대기열 주시문서 목ë¡ì„ ë‚´ ê°œì¸ í‘œì‹œì¤„ì— í‘œì‹œí•©ë‚˜ë‹¤",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "활성화하면 보류 ì¤‘ì¸ ì„ íƒëœ ìž‘ì—…ì˜Â [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist 목ë¡]ì„ ì¿¼ë¦¬ í¬ê¸° 추정치와 함께 표시합니다.",
+ "smw-prefs-general-options-disable-editpage-info": "편집 ë¬¸ì„œì˜ ë„ìž…ë¬¸ì„ ì‚¬ìš©í•˜ì§€ ì•ŠìŒ",
+ "smw-prefs-general-options-disable-search-info": "표준 검색 문서ì—ì„œ 문법 ì§€ì› ì •ë³´ë¥¼ 비활성화합니다",
+ "smw-prefs-general-options-suggester-textinput": "시맨틱 엔티티를 위한 ìž…ë ¥ 지ì›ì„ 사용합니다",
+ "smw-prefs-help-general-options-suggester-textinput": "활성화하면 ìž…ë ¥ 문맥ì—ì„œ ì†ì„±, ê°œë…, 분류를 찾기 위해 [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ìž…ë ¥ 지ì›]ì„ ì‚¬ìš©í•  수 있게 합니다.",
+ "smw-ui-tooltip-title-property": "ì†ì„±",
+ "smw-ui-tooltip-title-quantity": "단위 변환",
+ "smw-ui-tooltip-title-info": "ì •ë³´",
+ "smw-ui-tooltip-title-service": "서비스 ë§í¬",
+ "smw-ui-tooltip-title-warning": "경고",
+ "smw-ui-tooltip-title-error": "오류",
+ "smw-ui-tooltip-title-parameter": "변수",
+ "smw-ui-tooltip-title-event": "사건",
+ "smw-ui-tooltip-title-note": "참고",
+ "smw-ui-tooltip-title-legend": "범례",
+ "smw-ui-tooltip-title-reference": "ê°ì£¼",
+ "smw_unknowntype": "ì´ ì†ì„±ì˜ \"$1\" 형ì‹ì´ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤",
+ "smw-concept-cache-text": "ê°œë…ì€ ì´ {{PLURAL:$1|문서}} $1개가 있으며, $2 $3ì— ë§ˆì§€ë§‰ìœ¼ë¡œ ì—…ë°ì´íŠ¸í–ˆìŠµë‹ˆë‹¤.",
+ "smw_concept_header": "\"$1\" ê°œë…ì˜ ë¬¸ì„œ",
+ "smw_conceptarticlecount": "ì•„ëž˜ì— {{PLURAL:$1|문서}} $1개를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-qp-empty-data": "ì¼ë¶€ ì„ íƒ ê¸°ì¤€ì´ ì¶©ë¶„í•˜ì§€ ì•Šì•„ ìš”ì²­ëœ ë°ì´í„°ë¥¼ 표시할 수 없습니다.",
+ "right-smw-admin": "관리 ìž‘ì—…ì— ì ‘ê·¼ (시맨틱 미디어위키)",
+ "right-smw-patternedit": "í—ˆìš©ëœ ì •ê·œ 표현ì‹ê³¼ íŒ¨í„´ì„ ì •ë¹„í•  편집 권한 (시맨틱 미디어위키)",
+ "right-smw-ruleedit": "규칙 문서 편집 (시맨틱 미디어위키)",
+ "action-smw-patternedit": "시맨틱 ë¯¸ë””ì–´ìœ„í‚¤ì— ì“°ì´ëŠ” ì •ê·œ í‘œí˜„ì‹ íŽ¸ì§‘",
+ "group-smwadministrator": "관리ìž(시맨틱 미디어위키)",
+ "group-smwadministrator-member": "{{GENDER:$1|ê´€ë¦¬ìž (시맨틱 미디어위키)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:ê´€ë¦¬ìž (시맨틱 미디어위키)",
+ "group-smwcurator": "íë ˆì´í„° (시맨틱 미디어위키)",
+ "group-smwcurator-member": "{{GENDER:$1|íë ˆì´í„° (시맨틱 미디어위키)}}",
+ "grouppage-smwcurator": "{{ns:project}}:íë ˆì´í„° (시맨틱 미디어위키)",
+ "action-smw-admin": "시맨틱 미디어위키 관리 ìž‘ì—…ì— ì ‘ê·¼",
+ "action-smw-ruleedit": "규칙 문서 편집 (시맨틱 미디어위키)",
+ "smw-property-predefined-default": "\"$1\"ì€(는) 미리 ì •ì˜ëœ ì†ì„±ìž…니다.",
+ "smw-property-predefined-ask": "\"$1\"ì€(는) ê°œê°œì˜ ì¿¼ë¦¬ì— ëŒ€í•œ ([https://www.semantic-mediawiki.org/wiki/Subobject 하위 ê°ì²´]ì˜ í˜•íƒœë¡œ) 메타 정보를 표현하는 미리 ì •ì˜ëœ ì†ì„±ì´ë©° [https://www.semantic-mediawiki.org/wiki/Help:Special_properties 시맨틱 미디어위키]를 통해 제공ë©ë‹ˆë‹¤.",
+ "smw-property-predefined-asksi": "\"$1\"ì€(는) 쿼리ì—ì„œ 사용ë˜ëŠ” ì¡°ê±´ì˜ ìˆ˜ë¥¼ 모으는 미리 ì •ì˜ëœ ì†ì„±ì´ë©° [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]를 통해 제공ë©ë‹ˆë‹¤.",
+ "smw-sp-properties-docu": "ì´ íŽ˜ì´ì§€ëŠ” ì´ ìœ„í‚¤ë¥¼ 위해 사용할 수 있는 [https://www.semantic-mediawiki.org/wiki/Property ì†ì„±]ê³¼ ê·¸ê²ƒì˜ ì‚¬ìš© 횟수를 나열합니다. 최신 횟수 í†µê³„ì— ëŒ€í•´ ê·¸ê²ƒì€ [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics ì†ì„± 통계] 유지 관리 스í¬ë¦½íŠ¸ê°€ 정기ì ìœ¼ë¡œ 실행ë˜ëŠ” ê²ƒì„ ê¶Œìž¥í•©ë‹ˆë‹¤. ì°¨ë³„í™”ëœ ë³´ê¸°ì— ëŒ€í•´ì„œëŠ” [[Special:UnusedProperties|사용하지 않는 ì†ì„± 목ë¡]]ì´ë‚˜ [[Special:WantedProperties|필요한 ì†ì„± 목ë¡]] 특수 문서를 보세요.",
+ "smw-sp-properties-cache-info": "ë‚˜ì—´ëœ ë°ì´í„°ëŠ” [https://www.semantic-mediawiki.org/wiki/Caching ìºì‹œ]ì—ì„œ 검색ë˜ì—ˆê³ , $1ì— ë§ˆì§€ë§‰ìœ¼ë¡œ ì—…ë°ì´íŠ¸í–ˆìŠµë‹ˆë‹¤.",
+ "smw-sp-properties-header-label": "ì†ì„± 목ë¡",
+ "smw-admin-settings-docu": "시맨틱 미디어위키 환경과 ê´€ë ¨ëœ ëª¨ë“  기본값과 ì§€ì—­í™”ëœ ì„¤ì •ì˜ ëª©ë¡ì„ ë³´ì—¬ì¤ë‹ˆë‹¤. 개별 ì„¤ì •ì— ëŒ€í•œ ìžì„¸í•œ ë‚´ìš©ì€ [https://www.semantic-mediawiki.org/wiki/Help:Configuration 구성] ë„ì›€ë§ ë¬¸ì„œë¥¼ 참조하세요.",
+ "smw-sp-admin-settings-button": "설정 ëª©ë¡ ìƒì„±",
+ "smw-admin-idlookup-title": "조회",
+ "smw-admin-idlookup-docu": "시맨틱 ë¯¸ë””ì–´ìœ„í‚¤ì— ê°œë³„ 개체(위키문서, 하위개체 등)를 나타내는 안쪽 개체 Idì— ëŒ€í•œ ìžì„¸í•œ 정보를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-admin-iddispose-done": "\"$1\" IDê°€ 스토리지 백엔드ì—ì„œ 제거ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-admin-iddispose-no-references": "\"$1\" ID는 ì°¾ì„ ìˆ˜ 없거나 ìž„ì˜ì˜ 참조를 í¬í•¨í•©ë‹ˆë‹¤.",
+ "smw-admin-idlookup-input": "검색:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "개요",
+ "smw-admin-tab-notices": "êµ¬ì‹ ì•Œë¦¼",
+ "smw-admin-tab-maintenance": "유지보수",
+ "smw-admin-tab-supplement": "추가 기능",
+ "smw-admin-maintenance-no-description": "ì„¤ëª…ì´ ì—†ìŠµë‹ˆë‹¤.",
+ "smw-livepreview-loading": "불러오는 중...",
+ "smw-sp-searchbyproperty-resultlist-header": "ê²°ê³¼ 목ë¡",
+ "smw-sp-searchbyproperty-nonvaluequery": "\"$1\" ì†ì„±ì´ í• ë‹¹ëœ ê°’ì˜ ëª©ë¡ìž…니다.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\"ê°€ 숫ìžë¥¼ 허용하지 않는 \"NULL\"ê³¼ 함께 반환ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-search-syntax-support": "검색 ìž…ë ¥ì€ ì‹œë§¨í‹± 미디어위키를 사용한 ê²°ê³¼ ì¼ì¹˜ë¥¼ ë„와주는 [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search 쿼리 문법]ì˜ ì‚¬ìš©ì„ ì§€ì›í•©ë‹ˆë‹¤.",
+ "smw-search-input": "입력과 검색",
+ "smw-search-syntax": "구문",
+ "smw-search-profile": "확장",
+ "smw-search-profile-tooltip": "시맨틱 위키와 ì—°ê³„ëœ ê²€ìƒ‰ 기능",
+ "smw-search-profile-sort-best": "최ì ì˜ ì¼ì¹˜",
+ "smw-search-profile-sort-recent": "가장 최근",
+ "smw-search-profile-sort-title": "제목",
+ "smw-search-profile-extended-help-intro": "[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile 확장 프로파ì¼]ì€ íŠ¹ìˆ˜:검색 문서 ë‚´ì˜ ì‹œë§¨í‹± 미디어위키 확장 ê¸°ëŠ¥ì— íŠ¹í™”ëœ ê²€ìƒ‰ ê¸°ëŠ¥ì— ì ‘ê·¼í•  수 있는 ê¶Œí•œì„ ì œê³µí•˜ë©° 다ìŒì„ í¬í•¨í•©ë‹ˆë‹¤:",
+ "smw-search-profile-extended-help-sort": "ê²°ê³¼ 표시를 위한 ì •ë ¬ 환경 ì„¤ì •ì„ ì§€ì •í•©ë‹ˆë‹¤:",
+ "smw-search-profile-extended-help-sort-title": "* \"제목\"ì€ ì •ë ¬ 기준으로 문서 제목 문서 제목(ë˜ëŠ” 표시 제목)ì„ ì‚¬ìš©í•©ë‹ˆë‹¤",
+ "smw-search-profile-extended-help-sort-best": "* \"최ì ì˜ ì¼ì¹˜\"는 백엔드가 제공하는 ì ìˆ˜ì— 기반한 [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy 관련성]ì— ë”°ë¥¸ 엔티티를 정렬합니다",
+ "smw-search-profile-extended-help-namespace": "í¼ì„ ì„ íƒí•˜ìžë§ˆìž ì´ë¦„공간 ì„ íƒ ìƒìžëŠ” 숨겨지지만 \"표시/숨김\" ë²„íŠ¼ì„ ì´ìš©í•˜ì—¬ ë³´ì´ê²Œ í•  수 있습니다.",
+ "smw-search-profile-extended-help-query": "다ìŒìœ¼ë¡œ ì—°ê²°: $1",
+ "smw-search-profile-extended-help-query-link": "(ë” ìžì„¸í•œ ì‚¬í•­ì˜ ê²½ìš° $1).",
+ "smw-search-profile-extended-help-find-forms": "사용 가능한 í¼",
+ "smw-search-profile-extended-section-sort": "정렬 기준",
+ "smw-search-profile-extended-section-form": "í¼",
+ "smw-search-profile-extended-section-search-syntax": "검색 입력",
+ "smw-search-profile-extended-section-namespace": "ì´ë¦„공간",
+ "smw-search-profile-extended-section-query": "쿼리",
+ "smw-search-profile-link-caption-query": "보기",
+ "smw-search-show": "보기",
+ "smw-search-hide": "숨기기",
+ "log-name-smw": "시맨틱 미디어위키 로그",
+ "log-show-hide-smw": "$1 시맨틱 미디어위키 로그",
+ "logeventslist-smw-log": "시맨틱 미디어위키 기ë¡",
+ "logentry-smw-maintenance": "유지보수 관련 ì´ë²¤íŠ¸ê°€ 시맨틱 ë¯¸ë””ì–´ìœ„í‚¤ì— ì˜í•´ 발행ë˜ì—ˆìŠµë‹ˆë‹¤",
+ "smw-datavalue-import-unknown-namespace": "\"$1\" 가져오기 ì´ë¦„ê³µê°„ì„ ì•Œ 수 없습니다. [[MediaWiki:Smw import $1]]를 통해 ìžì„¸í•œ OWL 가져오기 정보를 ì´ìš©í•  수 있는지 확ì¸í•´ 주십시오.",
+ "smw-datavalue-import-missing-namespace-uri": "[[MediaWiki:Smw import $1|$1 가져오기]]ì—ì„œ \"$1\" ì´ë¦„공간 URI를 ì°¾ì„ ìˆ˜ 없습니다.",
+ "smw-datavalue-import-missing-type": "[[MediaWiki:Smw import $2|$2 가져오기]]ì—ì„œ \"$1\"ì— ëŒ€í•œ 유형 ì •ì˜ë¥¼ ì°¾ì„ ìˆ˜ 없습니다.",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1 가져오기]]",
+ "smw-datavalue-import-invalid-value": "\"$1\"는 유효한 í¬ë§·ì´ 아니며 \"ì´ë¦„공간\":\"ì‹ë³„ìž\"ë¡œì˜ êµ¬ì„±ì„ ì˜ˆì¸¡í•©ë‹ˆë‹¤. (예: \"foaf:name\")",
+ "smw-datavalue-import-invalid-format": "\"$1\" 문ìžì—´ì´ 4ê°œì˜ ë¶€ë¶„ìœ¼ë¡œ 나뉠 것으로 예측ë˜ì—ˆìœ¼ë‚˜ í¬ë§·ì„ ì¸ì‹í•˜ì§€ 못했습니다.",
+ "smw-datavalue-property-create-restriction": "\"$1\" ì†ì„±ì€ 존재하지 않으며 사용ìžëŠ” 승ì¸ë˜ì§€ ì•Šì€ ì†ì„±ì´ 있는 ê°’ì„ ë§Œë“¤ê±°ë‚˜ 주ì„ì„ ì¶”ê°€í•  \"$2\" ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤. ([https://www.semantic-mediawiki.org/wiki/Help:Authority_mode ì¸ê°€\n ë°©ì‹] 참고)",
+ "smw-datavalue-property-invalid-character": "\"$1\" ì†ì„±ì€ \"$2\" 문ìžë¥¼ ì†ì„± ë ˆì´ë¸”ì˜ ì¼ë¶€ë¡œì„œ í¬í•¨í•˜ê³  있으므로 유효하지 ì•Šì€ ê²ƒìœ¼ë¡œ 분류ë©ë‹ˆë‹¤.",
+ "smw-datavalue-restricted-use": "ë°ì´í„° ê°’ $1ì€(는) ì´ìš©ì´ 제한ë˜ì–´ 있습니다.",
+ "smw-datavalue-invalid-number": "\"$1\"는 숫ìžë¡œ í•´ì„ë˜ì§€ 않습니다.",
+ "smw-query-condition-circular": "\"$1\" 안ì—ì„œ 잠재ì ì¸ 순환 ì¡°ê±´ì´ ë°œê²¬ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-query-condition-empty": "쿼리 ì„¤ëª…ì— ì¡°ê±´ì´ ë¹„ì–´ìžˆìŠµë‹ˆë‹¤.",
+ "smw-types-list": "ìžë£Œí˜• 목ë¡",
+ "smw-types-default": "\"$1\"ì€ ë‚´ìž¥ ìžë£Œí˜•ìž…니다.",
+ "smw-types-help": "ë” ë§Žì€ ì •ë³´ì™€ 예제는 ì´ [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 ë„ì›€ë§ ë¬¸ì„œ]ì—ì„œ ë³¼ 수 있습니다.",
+ "smw-type-ema": "\"$1\"ì€(는) ì´ë©”ì¼ì„ 표현하기 위한 특수 ìžë£Œí˜•ìž…니다.",
+ "smw-type-tem": "\"$1\"ì€(는) 온ë„를 표현하기 위한 특수 수치 ìžë£Œí˜•ìž…니다.",
+ "smw-type-qty": "\"$1\"ì€(는) 수치 표현과 ì˜¨ë„ ë‹¨ìœ„ê°€ 있는 수량ìžë¥¼ 기술하기 위한 ìžë£Œí˜•ìž…니다.",
+ "smw-type-extra-tem": "변환 스키마ì—는 켈빈, 섭씨, 화씨, ëž€ì”¨ë“±ì˜ ì§€ì› ë‹¨ìœ„ë¥¼ í¬í•¨í•©ë‹ˆë‹¤.",
+ "smw-type-tab-properties": "ì†ì„±",
+ "smw-type-tab-types": "유형",
+ "smw-type-tab-errors": "오류",
+ "smw-type-primitive": "기본",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "ì´ ë¬¸ì„œëŠ” ì†ì„±ê³¼ 주어진 ë¬¸ì„œì˜ ëª¨ë“  ê°’ì„ ì°¾ê¸° 위한 둘러보기 ì¸í„°íŽ˜ì´ìŠ¤ë¥¼ 제공합니다. ê·¸ ë°–ì˜ ì´ìš© 가능한 검색 ì¸í„°íŽ˜ì´ìŠ¤ì—는 [[Special:SearchByProperty|ì†ì„± 검색]], [[Special:Ask|ë¬¸ì˜ ì¿¼ë¦¬ 빌ë”]]ê°€ 있습니다.",
+ "smw-datavalue-record-invalid-property-declaration": "레코드 ì •ì˜ì—는 ê·¸ ìžì²´ê°€ 레코드 타입으로 ì„ ì–¸ëœ, 허용ë˜ì§€ 않는 \"$1\" 프로í¼í‹°ë¥¼ í¬í•¨í•˜ê³  있습니다.",
+ "smw-datavalue-languagecode-invalid": "\"$1\"는 지ì›ë˜ëŠ” 언어 코드로 ì¸ì‹ë˜ì§€ 않습니다.",
+ "smw-types-extra-mlt-lcode": "ìžë£Œí˜•ì—는 언어 코드가 {{PLURAL:$2|필요합니다|필요하지 않습니다}} (예: {{PLURAL:$2|언어 코드가 없는 ê°’ 애너테ì´ì…˜ì€ 받아들ì´ì§€ 않습니다|언어 코드가 없는 ê°’ 애너테ì´ì…˜ì€ 받아들입니다}})",
+ "smw-limitreport-intext-parsertime": "[SMW] í…스트 ë‚´ ì£¼ì„ íŒŒì„œ 시간",
+ "smw-limitreport-intext-postproctime": "[SMW] 후처리 시간",
+ "smw-limitreport-intext-parsertime-value": "$1ì´ˆ",
+ "smw-limitreport-intext-postproctime-value": "$1{{PLURAL:$1|ì´ˆ}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] 스토어 ì—…ë°ì´íŠ¸ 시간 (문서를 새로 ê³ ì¹  ë•Œ)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1ì´ˆ",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\"ì€(는) \"$2\" ì •ê·œ 표현ì‹ì— ì˜í•´ 유효하지 ì•Šì€ ê²ƒìœ¼ë¡œ 분류ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-datavalue-allows-pattern-reference-unknown": "\"$1\" 패턴 참조는 [[MediaWiki:Smw allows pattern]]ì˜ ì—”íŠ¸ë¦¬ì™€ ì¼ì¹˜ë˜ì§€ 못했습니다.",
+ "smw-datavalue-allows-value-list-unknown": "\"$1\" ëª©ë¡ ì°¸ì¡°ëŠ” [[MediaWiki:Smw allows list $1]] 문서와 ì¼ì¹˜ë˜ì§€ 못했습니다.",
+ "smw-datavalue-allows-value-list-missing-marker": "\"$1\" ëª©ë¡ ë‚´ìš©ì— * ëª©ë¡ í‘œì‹œê°€ 있는 í•­ëª©ì´ ì—†ìŠµë‹ˆë‹¤.",
+ "smw-datavalue-feature-not-supported": "\"$1\" ê¸°ëŠ¥ì€ ì´ ìœ„í‚¤ì—ì„œ 지ì›ë˜ì§€ 않거나 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\"ì—는 지ì›í•˜ì§€ 않는 오프셋과 구역 ì‹ë³„ìžê°€ í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-values": "\"$1\" ê°’ì—는 \"$2\" í˜•íƒœì˜ í•´ì„í•  수 없는 ì •ë³´ê°€ í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\"ì—는 ì¼ë¶€ í•´ì„í•  수 없는 ì •ë³´ê°€ í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\"ì—는 ë°ì´í„° í•´ì„ì— ìœ íš¨í•˜ì§€ ì•Šì€ ì™¸ì¸ì„± 대시나 기타 문ìžë“¤ì´ í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\"ì—는 ì¼ë¶€ 비어있는 구성 요소가 í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\"ì—는 ë°ì´í„° í•´ì„ì— í•„ìš”í•œ 세 ê°œ ì´ìƒì˜ 구성 요소가 í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\"ì—는 12시간 ë³€í™˜ì— ìœ íš¨í•˜ì§€ ì•Šì€ ì‹œê°„ 요소로서 \"$2\"ì´(ê°€) í¬í•¨ë˜ì–´ 있습니다.",
+ "smw-datavalue-time-invalid-jd": "ë³´ê³ ëœ \"$2\" ê°’ì„ ê°€ì§€ê³  \"$1\" ìž…ë ¥ê°’ì„ ìœ íš¨í•œ JD(율리우스ì¼) 숫ìžë¡œ í•´ì„í•  수 없습니다.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\"ì€(는) 유효하지 ì•Šì€ URL입니다.",
+ "smw-datavalue-keyword-maximum-length": "키워드가 $1ì˜ ìµœëŒ€ 길ì´ë¥¼ 초과했습니다.",
+ "smw-datavalue-parse-error": "지정한 \"$1\" ê°’ì„ ì¸ì‹í•˜ì§€ 못했습니다.",
+ "smw-datavalue-propertylist-invalid-property-key": "\"$1\" ì†ì„± 목ë¡ì€ 유효하지 ì•Šì€ \"$2\" ì†ì„± 키를 í¬í•¨í•©ë‹ˆë‹¤.",
+ "smw-datavalue-wikipage-empty": "위키페ì´ì§€ ìž…ë ¥ê°’ì´ ë¹„ì–´ìžˆìœ¼ë¯€ë¡œ(예: <code>[[SomeProperty::]], [[]]</code>) ì´ë¦„ì´ë‚˜ 쿼리 ì¡°ê±´ì˜ ì¼ë¶€ë¡œ 사용할 수 없습니다.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "JSON 파서가 \"$1\"로 반환했습니다.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$2\" 언어가 ì´ë¯¸ \"$3\" ë ˆì´ë¸”ì— í• ë‹¹ë˜ì–´ 있기 ë•Œë¬¸ì— \"$1\"ì„(를) 선호하는 ë ˆì´ë¸”ë¡œ 사용할 수 없습니다.",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "í´ë¦½ë³´ë“œì— ë§í¬ 복사하기",
+ "smw-data-lookup": "정보 수집 중...",
+ "smw-data-lookup-with-wait": "ì´ ìš”ì²­ì€ ì²˜ë¦¬ 중ì´ë©° ì‹œê°„ì´ ìž ì‹œ ì†Œìš”ë  ìˆ˜ 있습니다.",
+ "smw-no-data-available": "ì´ìš© 가능한 ë°ì´í„° ì—†ìŒ.",
+ "protect-level-smw-pageedit": "문서 편집 ê¶Œí•œì´ ìžˆëŠ” 사용ìžë§Œ 허용 (시맨틱 미디어위키)",
+ "smw-edit-protection-auto-update": "시맨틱 미디어위키는 \"ls edit protected\" ì†ì„±ì— ë”°ë¼ ë³´í˜¸ ìƒíƒœë¥¼ ì—…ë°ì´íŠ¸í•˜ì˜€ìŠµë‹ˆë‹¤.",
+ "smw-edit-protection-enabled": "편집 ë³´í˜¸ë¨ (시맨틱 미디어위키)",
+ "smw-patternedit-protection": "ì´ ë¬¸ì„œëŠ” 보호ë˜ì–´ 있으므로 ì ì ˆí•œ <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions 권한]ì´ ìžˆëŠ” 사용ìžë§Œ 편집할 수 있습니다.",
+ "smw-property-predefined-edip": "\"$1\" ì†ì„±ì€ íŽ¸ì§‘ì´ ë³´í˜¸ë˜ì–´ 있는지 ì•„ë‹Œì§€ì˜ ì—¬ë¶€ë¥¼ 지시하기 위해 [https://www.semantic-mediawiki.org/wiki/Help:Special_properties 시맨틱 미디어위키]ê°€ 제공하는 미리 ì •ì˜ëœ ì†ì„±ìž…니다.",
+ "smw-query-reference-link-label": "쿼리 참조",
+ "smw-format-datatable-emptytable": "í…Œì´ë¸”ì— ì‚¬ìš©í•  수 있는 ë°ì´í„°ê°€ 없습니다",
+ "smw-format-datatable-info": "_TOTAL_ê°œì˜ í•­ëª© 중 _START_부터 _END_까지 표시 중",
+ "smw-format-datatable-infoempty": "0ê°œì˜ í•­ëª© 중 0부터 0까지 표시 중",
+ "smw-format-datatable-infofiltered": "(_MAX_ê°œì˜ ì „ì²´ 항목ì—ì„œ í•„í„°ë¨)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "_MENU_ 엔트리 보기",
+ "smw-format-datatable-loadingrecords": "불러오는 중...",
+ "smw-format-datatable-processing": "처리 중...",
+ "smw-format-datatable-search": "검색:",
+ "smw-format-datatable-zerorecords": "ì¼ì¹˜í•˜ëŠ” 레코드가 없습니다",
+ "smw-format-datatable-first": "처ìŒ",
+ "smw-format-datatable-last": "마지막",
+ "smw-format-datatable-next": "다ìŒ",
+ "smw-format-datatable-previous": "ì´ì „",
+ "smw-format-datatable-toolbar-export": "내보내기",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "\"$1\" 분류는 분류가 ì•„ë‹Œ ì´ë¦„ê³µê°„ì— ëŒ€í•œ ìž˜ëª»ëœ ë„˜ê²¨ì£¼ê¸° 대ìƒì„ í¬í•¨í•˜ê³  있습니다.",
+ "smw-postproc-queryref": "ì¼ë¶€ 쿼리 후처리가 필요한 ìƒí™©ì—ì„œ 시맨틱 미디어위키는 현재 페ì´ì§€ë¥¼ 새로 고칩니다.",
+ "apihelp-browsebyproperty-summary": "ì†ì„±ì´ë‚˜ ì†ì„± 목ë¡ì— 관한 정보를 반환하기 위한 API 모듈입니다.",
+ "apihelp-smwtask-summary": "시맨틱 미디어위키 관련 ìž‘ì—…ì„ ì‹¤í–‰í•˜ê¸° 위한 API 모듈입니다.",
+ "apihelp-smwbrowse-summary": "시맨틱 ë¯¸ë””ì–´ìœ„í‚¤ì˜ ê°ê¸° 다른 엔티티 ìœ í˜•ì˜ íƒìƒ‰ 활ë™ì„ 지ì›í•˜ê¸° 위한 API 모듈입니다.",
+ "apihelp-ask-parameter-api-version": "출력 형ì‹:\n;2:ê²°ê³¼ 목ë¡ì„ 위해 {}를 사용하는 하위 호환 í¬ë§·\n;3:ê²°ê³¼ 목ë¡ì„ 위해 {}를 사용하는 실험ì ì¸ í¬ë§·.",
+ "smw-api-invalid-parameters": "\"$1\" 변수가 유효하지 않습니다",
+ "smw-property-page-list-count": "ì´ ì†ì„±ì„ 사용하는 {{PLURAL:$1|문서}} $1개를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-property-page-list-search-count": "\"$2\" ê°’ì´ ì¼ì¹˜í•˜ê³  ì´ ì†ì„±ì„ 사용하는 {{PLURAL:$1|문서}} $1개를 ë³´ì—¬ì¤ë‹ˆë‹¤.",
+ "smw-property-reserved-category": "분류",
+ "smw-category": "분류",
+ "smw-datavalue-uri-invalid-scheme": "\"$1\"ì€(는) 유효한 URI 스킴으로 등재ë˜ì§€ 않았습니다.",
+ "smw-browse-property-group-title": "ì†ì„± 그룹",
+ "smw-browse-property-group-label": "ì†ì„± 그룹 ë ˆì´ë¸”",
+ "smw-browse-property-group-description": "ì†ì„± 그룹 설명",
+ "smw-filter": "í•„í„°",
+ "smw-section-expand": "문단 펼치기",
+ "smw-section-collapse": "문단 접기",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] 형ì‹",
+ "smw-help": "ë„움ë§",
+ "smw-personal-jobqueue-watchlist": "주시문서 ëª©ë¡ (ìž‘ì—… 대기열)",
+ "smw-property-predefined-label-skey": "정렬키",
+ "smw-processing": "처리 중...",
+ "smw-types-title": "유형: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "[https://www.semantic-mediawiki.org/wiki/Help:Schema 스키마 문서]ì˜ ì½˜í…츠 모ë¸ì„ 변경할 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤.",
+ "smw-schema-error": "유효성 검사 오류",
+ "smw-schema-error-violation": "위반 (\"$1\", \"$2\")",
+ "smw-schema-title": "스키마",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "종류",
+ "smw-schema-tag": "{{PLURAL:$1|태그}}",
+ "smw-ask-title-keyword-type": "키워드 검색",
+ "smw-ask-message-keyword-type": "ì´ ê²€ìƒ‰ì€ <code><nowiki>$1</nowiki></code> ì¡°ê±´ê³¼ ì¼ì¹˜í•©ë‹ˆë‹¤.",
+ "smw-remote-source-unavailable": "ì›ê²© \"$1\" 대ìƒìœ¼ë¡œ ì—°ê²°í•  수 없습니다.",
+ "smw-remote-source-disabled": "'''$1''' ì›ë³¸ì€ ì›ê²© 요청 지ì›ì„ 비활성화하였습니다!",
+ "smw-parameter-missing": "\"$1\" 변수가 없습니다.",
+ "smw-property-tab-usage": "사용",
+ "smw-property-tab-redirects": "ë™ì˜ì–´",
+ "smw-property-tab-subproperties": "하위 ì†ì„±",
+ "smw-property-tab-specification": "... 기타",
+ "smw-concept-tab-list": "목ë¡",
+ "smw-concept-tab-errors": "오류",
+ "smw-ask-tab-result": "ê²°ê³¼",
+ "smw-ask-tab-extra": "기타",
+ "smw-ask-tab-debug": "디버그",
+ "smw-ask-tab-code": "코드"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/krc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/krc.json
new file mode 100644
index 00000000..d041943e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/krc.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Iltever",
+ "Ernác"
+ ]
+ },
+ "smw_purge": "Джангырт",
+ "browse": "Викиге кёз ат",
+ "smw-livepreview-loading": "Джюклениу..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kri.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kri.json
new file mode 100644
index 00000000..bbb588f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kri.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77",
+ "Protostar"
+ ]
+ },
+ "smw_browse_go": "Go"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/krj.json b/www/wiki/extensions/SemanticMediaWiki/i18n/krj.json
new file mode 100644
index 00000000..857f99d1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/krj.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77"
+ ]
+ },
+ "smw_browse_go": "Agto"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ksh.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ksh.json
new file mode 100644
index 00000000..e3a8c925
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ksh.json
@@ -0,0 +1,235 @@
+{
+ "@metadata": {
+ "authors": [
+ "Purodha",
+ "ì•„ë¼",
+ "Kghbln"
+ ]
+ },
+ "smw-desc": "Määt Ding Wikki bäßer ze bruche - för Minsche un Maschihne. ([https://www.semantic-mediawiki.org/wiki/Help:User_manual Handbohch])",
+ "smw_viewasrdf": "RDF Kannaal",
+ "smw_finallistconjunct": ", un",
+ "smw_isspecprop": "Di Eijeschaff es en schpezjälle Eijeschaff en heh dämm Wikki.",
+ "smw_concept_description": "Beschrievong för et Kunzäp: „$1“",
+ "smw_no_concept_namespace": "Kunzäpter kam_mer bloß em {{ns:concept}}-Appachtemang faßlääje.",
+ "smw_multiple_concepts": "Op jeede {{ns:concept}}-Sigg kann bloß ei Kunzäp faßjelaat wääde.",
+ "smw_concept_cache_miss": "Dat Kunzap „$1“ kann för der Momang noch nit jebruch wääde.\nDem Wiki sing Kumfijurazjuhn fordert, dat dat offline ußjeräshent weed.\nWann dat nit noh en jeweße Zick nit fun sellfs jedonn es, un hee di\nAanforderung dorschjeiht, dann froch Dinge Wiki-Köbes donoh, noh dämm Kunzäp.",
+ "smw_noinvannot": "För Eijeschaffte met ömjedriehte Reschtung künne kei Wääte verjovve wääde.",
+ "version-semantic": "Projrammzohsäz vum „Semantesch MehdijaWikki“",
+ "smw_baduri": "URIs met dämm Opbou „$1“ sin nit zojelohße.",
+ "smw_csv_link": "<i lang=\"en\">CSV</i>",
+ "smw_dsv_link": "<i lang=\"en\">DSV</i>-Dattei",
+ "smw_json_link": "<i lang=\"en\">JSON</i>",
+ "smw_rdf_link": "<i lang=\"en\">RDF</i>",
+ "smw_printername_count": "Zälle, wat erus kütt",
+ "smw_printername_csv": "Expoot em <i lang=\"en\">CSV</i>-Fommaat",
+ "smw_printername_dsv": "Äxpoot als en <i lang=\"en\">DSV</i>-Dattei",
+ "smw_printername_debug": "En dä Frooch noh Fählere söhke (joot för de Expächte)",
+ "smw_printername_embedded": "Sigge-Enhallt enföhje",
+ "smw_printername_json": "Expoot em <i lang=\"en\">JSON</i>-Fommaat",
+ "smw_printername_list": "Leß",
+ "smw_printername_ol": "Opzälle",
+ "smw_printername_ul": "Einzel met Einzelheite opföhre",
+ "smw_printername_table": "Tabäll",
+ "smw_printername_broadtable": "En breed Tabäll",
+ "smw_printername_template": "Schablohn",
+ "smw_printername_rdf": "Äxpoot als en <i lang=\"en\">RDF</i>-Dattei",
+ "smw-paramdesc-limit": "De hühßte Aanzahl vun Äjeebnesse för zeröck ze jävve",
+ "smw-paramdesc-headers": "Donn der Name vun de Övveschrefte un Eijeschaffte aanzeije",
+ "smw-paramdesc-mainlabel": "Wi dä <code>mainlabel</code> Parrameeter för inlain Affroore beschrevve weed",
+ "smw-paramdesc-link": "Donn de Wääte als Lengks aanzeije",
+ "smw-paramdesc-intro": "Wat för ene Täx aanjezeijsch wääde sull, für dämm, wat jefonge woodt",
+ "smw-paramdesc-outro": "Wat för ene Täx aanjezeijsch wääde sull, noh dämm, wat jefonge woodt",
+ "smw-paramdesc-default": "Wat för ene Täx aanjezeijsch wääde sull, wann nix jefonge woodt",
+ "smw-paramdesc-sep": "Wat zwesche de Wääte schtonn sull",
+ "smw-paramdesc-template": "Dä Name vun ene Schablohn, för de Ußjabe met aanzzeije",
+ "smw-paramdesc-columns": "De Aanzahl Schpallde för aanzezije, wad eruß kütt — der Schtandatt wöhr $1",
+ "smw-paramdesc-userparam": "Ene Wäät, dä aan jehde Schablohn wigger_jejovve weed, wann övverhoub en Schablohn jebruch weed",
+ "smw-paramdesc-introtemplate": "Dä Name vun en Schabloon, di aanjezeijsch wääde sull, für dämm, wat jefonge woodt",
+ "smw-paramdesc-outrotemplate": "Dä Nahme vun en Schablohn, di aanjezeijsch wähde sull, noh dämm, wat jefonge wohdt",
+ "smw-paramdesc-embedformat": "De <i lang=\"en\">html</i>-Befähle för de Övverschreffte ze makeere",
+ "smw-paramdesc-embedonly": "Kein Övverschreffte aanzeije",
+ "smw-paramdesc-rdfsyntax": "De Zoot Opbou (Syntax) vun dä <i lang=\"en\">RDF</i>-Dattei",
+ "smw-paramdesc-csv-sep": "Dat Zeishe för Fälder ze tränne",
+ "smw-paramdesc-dsv-separator": "Dat Zeishe för Fälder ze tränne",
+ "smw-paramdesc-dsv-filename": "Dä Name för di <i lang=\"en\">DSV</i>-Dattei",
+ "smw-paramdesc-searchlabel": "Dä Täx för em Lengk för wat eruß kütt",
+ "smw_iq_disabled": "Semantesche Frore sem em Wiki em Momang afjeschaldt.",
+ "smw_iq_moreresults": "…&nbsp;mih vun däm, wat jefonge woodt",
+ "smw_parseerror": "Dä aanjejovve Wäät kunnte mer nit vershtonn.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "„$1“ kam_mer en hee däm Wiki nit als ene Siggename bruche.",
+ "smw_wrong_namespace": "Bloß Sigge uß däm Appachtemang „$1“ sin hee zohjelohße.",
+ "smw_manytypes": "Di Eijeschaff es en mih wi ein Zoot dren.",
+ "smw_emptystring": "De jonn kei Reihe uß Boochstabe un Zeishe, woh nit ens ei dofun dren shteiht. Alsu nit leddesch lohße.",
+ "smw_notinenum": "„$1“ es nit bei dä zohjeloße Wääte för di Eijeschaff, dat wöhre: $2",
+ "smw_noboolean": "„$1“ es keine vun dä Wääte för „wohr“ un „verkeeht“.",
+ "smw_true_words": "wohr,wor,woo,woh,wo,w,j,jo,joh,true,t,yes,y",
+ "smw_false_words": "verkiehrt,verkihrt,verkirt,verkeht,verkeet,verket,vokiehrt,vokihrt,vokirt,vokeht,vokeet,voket,ferkiehrt,ferkihrt,ferkirt,ferkeht,ferkeet,ferket,fokiehrt,fokihrt,fokirt,fokeht,fokeet,foket,fallsch,falsch,näh,nää,nä,false,f,no,n",
+ "smw_nofloat": "„$1“ es kei Zahl.",
+ "smw_infinite": "Zahle esu jruuß udder esu lang wi „$1“ dom_mer hee nit ongershtöze.",
+ "smw_unitnotallowed": "„$1“ es nit als en jölteje Mohßeinheit för heh di Eijeschaff enjedraare.",
+ "smw_novalues": "Et sinn_er kein Wääte aanjejovvwe.",
+ "smw_nodatetime": "Dat Dattum „$1“ künne mer nit vershtonn.",
+ "smw_toomanyclosing": "En dä Frooch es zoh öff „$1“ enthallde.",
+ "smw_noclosingbrackets": "En <code><nowiki>[[</nowiki></code> en Dinge Frooch woh nit zohjemaat, un hät kei <code>]]</code> wat drop paßße deiht.",
+ "smw_misplacedsymbol": "Dat Sümbohl „$1“ wohr aan ene Shtell, woh et nix nöz.",
+ "smw_unexpectedpart": "Dä Aandeil „$1“ en dä Frooch kunnte mer nit vershtonn. Wat eruß kütt, künnt jet angersch sin, wi jedaach.",
+ "smw_emptysubquery": "En Ongerfrooch hät kein reschtejje Bedengung.",
+ "smw_misplacedsubquery": "En Ongerfrooch douch aan ene Shtäll op, woh kei Ongerfroore sin dörve.",
+ "smw_valuesubquery": "En Ongerfrooch noh de Wääte fun dä Eijeschaff „$1“ künne mer nit.",
+ "smw_badqueryatom": "Ene Aandeil „<nowiki>[[…]]</nowiki>“ en dä Anfrooch es nit ze vershtonn.",
+ "smw_propvalueproblem": "Dä Eijeschaff „$1“ ier Wäät es nit ze vershtonn.",
+ "smw_noqueryfeature": "Ene Deijl vun dä Aanfrooch iere Eijeschaffte sin hee en dämm Wiki nit\nmüjjelesch, un dä Aandeil ($1) es dröm us dä Frooch eruß jenumme woode.",
+ "smw_noconjunctions": "Konjunxjohne (e „un“ en Aanfroore) sin hee en dämm Wiki nit müjjelesch, un dä Aandeil ($1) es dröm us dä Frooch eruß jenumme woode.",
+ "smw_nodisjunctions": "Dißjunxjohne (e „of“ un „odder“ en Aanfroore) sin hee en dämm Wiki nit müjjelesch, un dä Aandeil ($1) es dröm us dä Frooch eruß jenumme woode.",
+ "smw_querytoolarge": "De Bedengunge hee noh kunnte mer nit mieh beärbeide, weil dem Wiki sing Jrenze för der Ömfang udder de Aanzahl Verschachtelunge vun Datebangk_Froore övverschredde wohre: $1.",
+ "smw_notemplategiven": "Jiv ene Wäät för dä Parrammeeter „Schablohn“ aan, domet di Aat Frohch jonn kann.",
+ "smw_db_sparqlqueryproblem": "De Antwood op di Aanfrooch kunnt nit vun dä Daatebangk för <i lang=\"en\">SPARQL</i> jehollt wääde.\nHeh dä Fähler künnt wider vörbei jonn, udder ene Fähler em Programm för de Daatebangk opzeije.",
+ "smw_type_header": "Eijeschaffte vun dä Aat „$1“",
+ "smw_typearticlecount": "Mer zeije {{PLURAL:$1|Ein Eijeschaff|$1 Eijeschaffte|kei Eijeschaff}} met dämm Datentüp aan:",
+ "smw_attribute_header": "Sigge met dä Eijeschaff „$1“",
+ "smw_attributearticlecount": "{{PLURAL:$1|Hee kütt ein Sigg|Hee kumme $1 Sigge|Et jitt kei Sigge}} met dä Eijeschaff dren{{PLURAL:$1|:|:|.}}",
+ "specialpages-group-smw_group": "Semantesch MehdijaWikki",
+ "exportrdf": "Sigge em Fommaat RDF expoteere",
+ "smw_exportrdf_docu": "Hee di Sigg määd et müjjelesch, Daate vun en Sigg em <i lang=\"en\">RDF</i>-Fommaat ze krijje. Öm Sigge ze Expoteere, doon dänne ier Tittele, jeeder_ein en en Reih fö sesch, onge en dat Täx-Feld erin schriive.",
+ "smw_exportrdf_recursive": "Donn all de Sigge expotteere, di domet zosamme hange, un di domet och, un esu wigger. Opjepaß, dat künnt reschtesch vill wääde!",
+ "smw_exportrdf_backlinks": "Donn och all de Sigge expoteere, di op expoteete Sigge verwiese donn, un maach en <i lang=\"en\">RDF</i> Dattei, woh me dren bläddere udder mem Brauser draan jonn kann.",
+ "smw_exportrdf_lastdate": "Donn kein Sigge äxpottehre, di noh däm aanjejovve Dattum un dä aanjejovve Uhrzigg nimmih verändert woodte.",
+ "smw_exportrdf_submit": "Lohß Jonn!",
+ "uriresolver": "Oplööser för <i lang=\"en\">URIs</i>",
+ "properties": "Eijeschaffte",
+ "smw_properties_docu": "Heh di Eijeschaffte wähde em Wikki jebruch:",
+ "smw_property_template": "$1 fun dä Zoot $2 ({{PLURAL:$3|ei|$3|kei}} mol)",
+ "smw_propertylackspage": "Alle Eijeschaffte sullte op en Sigg beschrevve sin!",
+ "smw_propertylackstype": "För de Eijeschaff es bes jetz kein oot Dahte aanjejovve, mer nämme för der Momang ens „$1“.",
+ "smw_propertyhardlyused": "Di Eijeschaff wehd em Wikki koum jebruch!",
+ "unusedproperties": "Eijeschaffte, di nit jebruch wähde",
+ "smw-unusedproperties-docu": "Heh di Eijeschaffte jidd_et un han Sigge, ävver de Eijeschaffte sällver wähde em Momang nörjenswoh jebruch.",
+ "smw-unusedproperty-template": "$1 vun de Aat $2",
+ "wantedproperties": "Nit aanjelaate Eijeschaffte",
+ "smw-wantedproperties-docu": "De Eijeschaffte heh noh wäde em Wikki jebruch, ävver se han noch kei Sigg, di se beschrievee un äklihjere deiht.",
+ "smw-wantedproperty-template": "$1 ({{PLURAL:$2|eimol|$2 mol|nit}} jebruch)",
+ "smw_purge": "Nöü Aanzeije!",
+ "types": "Date-Tüpe",
+ "smw_types_docu": "Hee kütt en [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes Leß] met all dä Dahte_Zoote, di för Eijeschaffte verjovve wähde künnte.",
+ "smw-statistics": "Schtatißtike övver de semanntesche Dahte",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Wäät|Wääte |Wääte}} för Eijeschaffte (ensjesamp)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Eijeschaff|Eijeschaffte |Eijeschaffte}}]] (ensjesamp)",
+ "smw-statistics-property-page": "[[Special:Properties|{{PLURAL:$1|Eijeschaff|Eijeschaffte |Eijeschaffte}}]] (di obb en Sigg ennjedrahe sin)",
+ "smw-statistics-property-type": "[[Special:Properties|{{PLURAL:$1|Eijeschaff|Eijeschaffte |Eijeschaffte}}]] (di ene beschtemmpte Zoot vun Dahte han)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Affrohch|Affrohre|Kein Affrohch}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Affrohch|Affrohre| Affrohre}}]]",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Konzäpp|Konzäppte|Konzäppte }}]]",
+ "smw-statistics-subobject-count": "[[Special:Properties|{{PLURAL:$1|Ongerobjäk|Ongerobjäkte|Ongerobjäkte }}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Zoot Dahte|Doote Dahte|Zoot Dahte}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Wäät|Wääte|Wäät}} vun ene Eijeschaff (met [[Property:Has improper value for|kappodde {{PLURAL:$1|Aanmärkong|Aanmärkonge| Aanmärkonge}}]])",
+ "smw_uri_doc": "Dä <i lang=\"en\">URI resolver</i> hät dä Vörschlaach <i lang=\"en\">[$1 W3C TAG finding on httpRange-14]</i> opjenumme\n(-: un sorresch esu doför, dat uß Minsche kei Websigge wääde :-)",
+ "ask": "Semantesch Söke",
+ "smw_ask_sortby": "Noh de Spallde zotteere (moß ävver nit)",
+ "smw_ask_ascorder": "Nommahl eröm zotteere",
+ "smw_ask_descorder": "Röckwääts zotteere",
+ "smw_ask_submit": "Lohß Jonn!",
+ "smw_ask_editquery": "Opdraach ändere",
+ "smw_add_sortcondition": "[Zoteere dobeidonn]",
+ "smw_ask_hidequery": "Opdrach nit aanzeije",
+ "smw_ask_help": "Hölp",
+ "smw_ask_queryhead": "Froore",
+ "smw_ask_printhead": "Zosäzlijje Ußdröcker för aanzezeije",
+ "smw_ask_printdesc": "(Donn jeede Eijeschaff op ein Reih för sesch enjävve)",
+ "smw_ask_format_as": "Fommatteere als:",
+ "smw_ask_defaultformat": "Schtandatt-Fommaat",
+ "smw_ask_otheroptions": "Ander Ußwahle",
+ "smw_ask_show_embed": "Donn dä Wiki_Kood zom ennfööje aanzeije",
+ "smw_ask_hide_embed": "Donn dä Wiki_Kood zom Ennfööje vershteishe",
+ "smw_ask_embed_instr": "Öm di Frohch en en Sigg em Wikki enzeboue, donn dä Wikki_Koode heh dronger en di Sigg enföhje.",
+ "smw-ask-query-search-info": "Op Aanfrohch <code><nowiki>$1</nowiki></code> hät de Dahtebangk („$2“) en {{PLURAL:$3|ener Sekonde|$3 Sekonde|nix aan Zigg}} jeantwoot.",
+ "searchbyproperty": "Övver de Eijeschaffte söke",
+ "smw_sbv_docu": "Donn noh all dä Sigge söhke, di en beschtemmpte Eijeschaff han, un medd_ennem beschtemmpte Wäät för se.",
+ "smw_sbv_novalue": "Donn ene jöltijje Wäät för di Eijeschavv aanjävve, udder donn Der all de müjjelesche Wääte för „$1“ aanlohre.",
+ "smw_sbv_displayresult": "Leß met alle Sigge met dä Eijeschaff „$1“, di do dä Wäät „$2“ hät.",
+ "smw_sbv_displayresultfuzzy": "Leß met alle Sigge met dä Eijeschaff „$1“, di doh dä Wäät „$2“ hät.<br />\n(Weil nur winnisch dovun do sin, dom_mer de ähnlesche Wääte metzeije)",
+ "smw_sbv_property": "Eijeschaff:",
+ "smw_sbv_value": "Wäät:",
+ "smw_sbv_submit": "Lohß Jonn!",
+ "browse": "Em Wiki bläddere",
+ "smw_browselink": "Eijeschaffte aanzeije",
+ "smw_browse_article": "Beß esu joot, un jif di Övverschreff vun dä Sigg aan, wo De met däm Bläddere aanfange wells.",
+ "smw_browse_go": "Lohß Jonn!",
+ "smw_browse_more": "&nbsp;…",
+ "smw_browse_show_incoming": "donn de Eijeschaffte aanzeije, di ene Lengk noh hee han",
+ "smw_browse_hide_incoming": "donn de Eijeschaffte vershteishe, di ene Lengk noh hee han",
+ "smw_browse_no_outgoing": "Di Sigg heh hät kei Eijeschaffte.",
+ "smw_browse_no_incoming": "Mer han kein Eijeschaffte em Wiki, di ene Lengk noh heh han.",
+ "smw_inverse_label_default": "$1 vun",
+ "smw_inverse_label_property": "Dä Name för di Eijeschaff, wann dä ier Reschtung ömjedrieht weed",
+ "pageproperty": "Söke noh Eijeschaffte vun Sigge",
+ "smw_pp_docu": "Söhk noh all dä Wääte, di en beschtemmpte Eijeschaff en dä aanjejovve Sigg han. Donn sowohl en Sigg aanjävve als och en Eijeschaff.",
+ "smw_pp_from": "Vun Sigg",
+ "smw_pp_type": "Eijeschaff",
+ "smw_pp_submit": "Lohß Jonn!",
+ "smw_result_prev": "Vörijje",
+ "smw_result_next": "Wigger",
+ "smw_result_results": "Erus jekumme",
+ "smw_result_noresults": "Nix jefonge.",
+ "smwadmin": "Verwalldung fum semantesche MehdijaWikki",
+ "smw-admin-setupsuccess": "Dä Schpeischerplaz es opjesaz för et semantesch MehdijaWikki.",
+ "smw_smwadmin_return": "Jangk retuhr noh $1",
+ "smw_smwadmin_updatestarted": "Ene Projrammlouf för de semantesche Date neu opzeboue es em Jang. All de jeshpeisherte Date wäde neu zosamme jebout un woh nüdesch repareet. Mer kann bei däm Louf hee op dä Söndersigg metloore.",
+ "smw_smwadmin_updatenotstarted": "Et es ald e Projramm för dat op der neuste Shtand ze bränge aam Loufe.\nMer donn jetz nit noch ein aanshtüße!",
+ "smw_smwadmin_updatestopped": "Alle Projramme för et op der neuste Shtand Bränge, sin jetz aanjehallde woode.",
+ "smw_smwadmin_updatenotstopped": "Öm dat Projramm anzehallde, wat ald am Loufe es, moß De dat Höcksche en dat Käßje maache, öm ze zeije, dat De Der janz sesher bes.",
+ "smw-admin-docu": "Hee di {{int:specialpage}} hellef beim Enschtallerhre un beim op der neue Schtand bränge vum <a href=\"http://semantic-mediawiki.org\">Semantesche MehdijaWikki</a>. Opjepaß: Donn Ding Dahte seschere, ih dat De di Funkßjohne aanschmiiße deihß!",
+ "smw-admin-db": "Datebangk inshtalleere un op der neuste Shatnd bränge",
+ "smw-admin-dbdocu": "Semantesch MehdijaWikki bruch eije zosätzlejje Plaz en de Dahtebangk vum MehdijaWikki, öm sing semantesche Dahte ongerzebränge. De Fonkßjuhne heh dronger sorje doför, dat de Dahtebangk em Wikki öhndlesch för et Semantesch MediaWiki opjesaz es. Wat heh verändert weet, dat hät keine Ennfloß op dä Rääß vun dä Dahtebangk vum MehdijaWikki, un mer kann et och eijfach widder retuhr maache, wann mer well. Di Fonkßjuhn zom Opsäze kam_mer esu öff loufe lohße, wi mer well, dat schahdt nix, ävver et es bloß eijmohl nühdesch bemm Ennreeschte, udder bem Ömschteije obb en neujere Projrammväsjohn.",
+ "smw-admin-permissionswarn": "Wann de Aufjab met enem Fähler em\n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Structured Query Language - „bedehnt Dahebangke met Bezösch dren“\">SQL</i> donävve jeiht, dann künnd et sin, dat dä Nahme för op de Dahtebangk zohzejriife un sesch draan aanzemällde en de <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> för Ding Wikki nit jenohch Rääschte en dä Datebangk hät. Änntwehder jiß De dämm dat Rääsch, Tabälle aanzelähje udder fott ze maache, udder donn för der Momang Dinge eije Aanmälldong udder di vum Datebank-Köbes en dä <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> enndraare, udder nemm dat Waadungsprojramm <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">setupStore.php</code> wat sesch op de Aanjabe en <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> beträke kann, un Opjepaß: Donn dat dernoh wider retuhr maache!",
+ "smw-admin-dbbutton": "Tabelle aanlääje udder op der neuste Shatnd bränge",
+ "smw-admin-announce": "Donn Ding Wiki annongßeere un bikannt maache",
+ "smw_smwadmin_datarefresh": "Date repareere un op der neuste Shtand bränge",
+ "smw_smwadmin_datarefreshdocu": "Et es müjjelesch, all Date vun Semantesch MehdijaWikki uß dä nommahle Dahte em Wikki widder neu zesamme ze krijje. Dat kann joot sin, öm kapodde Date ze repareere, udder de Date op der aktoälle Schtand ze bränge, nohdämm sesh jet draan jeändert hät, dorjen Änderong aan de Projramme. De Aanpaßong weed Sigg för Sigg jedonn un weed dröm nit paaftisch fähdesch wehde. Heenoh kanns De sin, ov en Aanpaßong em Jang es, un Do kanns ein aanfange udder aanhallde, ußer wann ene Wiki_Köbes di Müjjeleschkeit ußjeschalldt hät.",
+ "smw_smwadmin_datarefreshprogress": "<strong>En XXXXXXX es ald ungerwähß.</strong> Es es nomahl för ene XXXXXXXXX dat dat langsam föran jeiht, weil dobei de Date nur en klein Häppsche jeändert wääde, un nur dann, wann eine dat wiki bruch. Öm flöcker fähdesch ze wäde, kanns de dat Waadungs-Projramm <code>runJobs.php</code> vun MediaWiki aanschmiiße. Nemm dä Parrameeter <code>--maxjobs 1000</code> öm dä XXXXXXXXXX en jedem Rötsch faß_ze_lääje. Mer sen onjefähr esu wick:",
+ "smw_smwadmin_datarefreshbutton": "Aanfange met de Date op der neue Shtand ze brenge",
+ "smw_smwadmin_datarefreshstop": "Donn dat op der neue Schtand Brenge ophühre",
+ "smw_smwadmin_datarefreshstopconfirm": "Joh, esch ben mer sescher, esch well dat han.",
+ "smw-admin-support": "Hölp krijje",
+ "smw-admin-supportdocu": "Bei Probleme kriss De velleich Hölp övver en Aanzahl vun Müjjeleschkeite:",
+ "smw-admin-installfile": "Wann De Schwiireschkeite met Dinge Enshtallzjuhn häs, dann donn met de Reeschlinnije en dä Dattei <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL</a> anfange, donoh ze söke, wat De donn kanns.",
+ "smw-admin-smwhomepage": "De kumplätte Dokku för et <i lang=\"en\">Semantic Mediawiki</i> es op <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b> ze fenge.",
+ "smw-admin-bugsreport": "Fähler kanns De övver <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\" lang=\"en\">GitHub</a> melde.",
+ "smw-admin-questions": "Wann De noch Froore häß odder Vörschlääsch maache odder klaafe wells, jangk op et <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\"><i lang=\"en\">Semantic MediaWiki</i> Metmaacher Forum</a> un donn doh metschwaade.",
+ "smw_adminlinks_datastructure": "De Daateschtroktuur",
+ "smw_adminlinks_displayingdata": "Daate aanzeije",
+ "smw_adminlinks_inlinequerieshelp": "Hölp övver de em Wiki sing Sigge enjeboute Froore",
+ "smw-createproperty-isproperty": "Dat ess_en Eijeschaff vun dä Zoot $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Dä einzijje müjjelesche Wäät för di Eijeschaff es:|De müjjelesche Wääte för di Eijeschaff sin:|Mer han kein müjjelesche Wääte för di Eijeschaff.}}",
+ "smw-ui-tooltip-title-quantity": "De Moßeinheijd ömwandelle",
+ "smw_unknowntype": "De Zoot „$1“ för de Eijeschaff dom_mer nit ongershtöze.",
+ "smw_concept_header": "Sigge vun däm Kumzäp „$1“",
+ "smw_conceptarticlecount": "{{PLURAL:$1|Ein Sigg jehööt|$1 Sigge jehüüre|Et jehüüere kein Sigge}} zoh dämm Kumzäp{{PLURAL:$1|:|:|.}}",
+ "smw-property-predefined-default": "„$1“ es en Eijeschaff, di vum Wikkiprojramm ald faßjelahd es.",
+ "smw-property-predefined-asksi": "„$1“ es en Eijeschaff, di vum Wikkiprojramm ald faßjelahd es, woh de Nommer vun dä Bedengonge en ene Affrohch faßjehallde es.",
+ "smw-livepreview-loading": "Ben aam Lahde&nbsp;…",
+ "smw-editpage-property-annotation-disabled": "Heh di Eijeschaff löht sesch nit med ene Zoot vun de Dahte verbenge, weil di ald vum Wikki uß faßjelaat es. Alsu „<code lang=\"en\" xml:lang=\"en\" dir=\"ltr\"><nowiki>[[Has type::Page]]</nowiki></code>“ jeiht heh nit. Op dä Sigg met [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Hölp övver besöndere Eijeschaffte] fengk mer mih Enfommazjuhne.",
+ "smw-datavalue-import-missing-type": "Mer han de Zoot Dahte vun dä Eijeschaff „$1“ nidd em [[MediaWiki:Smw import $2|Empoot vum $2]] jefonge.",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Empoot vum $1]]",
+ "smw-property-predefined-errp": "„$1“ es en Eijeschaff, di vum Wikkiprojramm ald faßjelaad es un jebruch weed, öm kappode Wääte för Eijeschaffte faßzehallde, di wahscheinlesch vun Beschängkonge vun de Zoot udder Beschängkonge vun [[Property:Allows value|de zohjelohße Wääte]] kumme.",
+ "smw-property-predefined-pval": "[https://semantic-mediawiki.org/wiki/Help:Special_property_Allows_value\"$1\"] es en Eijeschaff, di vum Wikkiprojramm ald faßjelaad es un di en Leß met zohjelohße Wääte, fäßlähje kann, öm de Zohdeijlong vun Wääte för en Einjeschaff ze beschrängke.",
+ "smw-datavalue-restricted-use": "Dä Dahtewääd „$1“ wood för ene beschrängkte Jebruch makehrt.",
+ "smw-datavalue-invalid-number": "„$1“ km_mer nit als en Zahl verschtonn.",
+ "smw-query-condition-circular": "Müjelesch, dat en Bedengong en „$1“ em Kreis erm jeiht.",
+ "smw-types-list": "Leß vun de Dahte_Zoote",
+ "smw-types-default": "„$1“ es ene pä Projramm ald faßjelahte un ennjeboute Dahte_Zoot.",
+ "smw-types-help": "Mih Aanjahbe un Beijschpelle fengk mer op dä [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 Sigg met Hölp] derzoh.",
+ "smw-type-anu": "„$1“ ess_en beschtemmpte Zoot vun [[Special:Types/URL|\n<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Uniform Ressource Locator\">URL</i> ]] dä dermihts för ene Äxpocht noh <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">owl:AnnotationProperty</i> ze beschriehve jebruch weed.",
+ "smw-type-boo": "„$1“ ess_en eijfache Dahte_Zoot, di Joh/Nää, udder Wohr/Verkiehrt affbelde kann.",
+ "smw-type-cod": "„$1“ ess_en affjewandelte Dahte_Zoot vum [[Special:Types/Text|Täx]], öm täschnesche Täx_Krohm med esu vell Zeische henernander wi nühdesch daazeschtälle, zem Beijschpell Projrammleßte.",
+ "smw-type-geo": "„$1“ ess_en Dahte_Zoot, di Pläz op de Ääd beschriev un et Zohsazprojramm för [https://www.semantic-mediawiki.org/wiki/Semantic_Maps semantesch Lanndkaate] nühdesch hät.",
+ "smw-type-tel": "„$1“ ess_en Dahte_Zoot, beschtemmp derför, engenazjonahle Tellefohnnommere em Fommaht vum <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\">RFC 3966</i> daazeschtälle.",
+ "smw-type-txt": "„$1“ ess_en eijfache Dahte_Zoot, bechtemmp derför, esu vell Zeische hengerenander daazeschtälle, wi nühdesch.",
+ "smw-type-dat": "„$1“ ess_en Dahte_Zoot för Zigg_Pongkte en enem eijheijtlejje Fommaht daazeschtälle.",
+ "smw-limitreport-intext-parsertime": "[SMW] De Zigg di der Paaser för et Annotehre jebruch hät",
+ "smw-limitreport-intext-parsertime-value": "{{PLURAL:$1|eijn Sekond|$1 Sekonde|keijn Sekond}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] De Duur för et Ändere fum Zwescheschpeijscher beim Fottschmiiße",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "{{PLURAL:$1|eijn Sekond|$1 Sekond|keijn Sekond}}"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ku-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ku-latn.json
new file mode 100644
index 00000000..e48323c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ku-latn.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "George Animal",
+ "Bikarhêner"
+ ]
+ },
+ "smw_finallistconjunct": ", û",
+ "smw_printername_list": "Lîste",
+ "smw_printername_category": "Kategorî",
+ "smw_true_words": "rast, r, erê, e",
+ "smw_false_words": "nerast, n, na, n",
+ "smw_purge": "Nû bike",
+ "browse": "Li wîkîye binêre",
+ "smw_browselink": "Li taybetmendiyan binêre",
+ "smw-livepreview-loading": "Tê…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/kw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/kw.json
new file mode 100644
index 00000000..cc2f2a48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/kw.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ow karga..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ky.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ky.json
new file mode 100644
index 00000000..9f6a90b2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ky.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Growingup"
+ ]
+ },
+ "smw_purge": "Жаңыртуу",
+ "types": "Түрлөр",
+ "smw_ask_queryhead": "Суроо",
+ "smw-ask-delete": "[Өчүрүү]",
+ "smw_sbv_value": "Чоңдук:",
+ "smw_result_prev": "Мурунку",
+ "smw_result_next": "Кийинки",
+ "smw-ui-tooltip-title-property": "Өзгөчөлүк",
+ "smw-ui-tooltip-title-quantity": "Сан",
+ "smw-ui-tooltip-title-info": "Маалымат",
+ "smw-ui-tooltip-title-warning": "Ката",
+ "smw-ui-tooltip-title-parameter": "Параметр",
+ "smw-livepreview-loading": "Жүктөлүүдө..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/la.json b/www/wiki/extensions/SemanticMediaWiki/i18n/la.json
new file mode 100644
index 00000000..8f5b07cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/la.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Depromens…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lad.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lad.json
new file mode 100644
index 00000000..60f31697
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lad.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Cargando..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lb.json
new file mode 100644
index 00000000..2d0302e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lb.json
@@ -0,0 +1,328 @@
+{
+ "@metadata": {
+ "authors": [
+ "Les Meloures",
+ "Robby",
+ "Soued031"
+ ]
+ },
+ "smw-desc": "Är Wiki méi accessibel machen - fir Maschinnen ''a'' Menschen ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error-why-title": "Firwat gesinn ech dëse Feeler?",
+ "smw-upgrade-error-how-title": "Wéi kann ech dëse Feeler flécken?",
+ "smw-semantics-not-enabled": "D'Semantic MediaWiki Funktioun gouf op dëser Wiki net ageschalt.",
+ "smw_viewasrdf": "RDF-Feed",
+ "smw_finallistconjunct": ", an",
+ "smw-factbox-head": "... méi iwwer \"$1\"",
+ "smw-factbox-facts": "Fakten",
+ "smw_isspecprop": "Dës Eegenschaft ass eng Spezial-Eegenschaft an dëser Wiki.",
+ "smw-concept-cache-header": "Benotze vum Tëschespäicher",
+ "smw-concept-no-cache": "Keen Tëschespäicher disponibel.",
+ "smw_concept_description": "Beschreiwung vum Konzept \"$1\"",
+ "smw_multiple_concepts": "Op jiddwer Konzeptsäit ka just eng Definitioun vun engem Konzept stoen.",
+ "version-semantic": "Softwareerweiderungen (Semantic MediaWiki)",
+ "smw_baduri": "URIë vun der Form \"$1\" sinn net erlaabt.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Resultater zielen",
+ "smw_printername_csv": "Export als CSV",
+ "smw_printername_dsv": "DSV-Export",
+ "smw_printername_debug": "Debug-Ufro (fir Experten)",
+ "smw_printername_embedded": "Säiteninhalter abannen",
+ "smw_printername_json": "Export als JSON",
+ "smw_printername_list": "Lëscht",
+ "smw_printername_plainlist": "Lëscht (einfach)",
+ "smw_printername_ol": "Opzielung",
+ "smw_printername_ul": "Opzielung",
+ "smw_printername_table": "Tabell",
+ "smw_printername_broadtable": "Breed Tabell",
+ "smw_printername_template": "Schabloun",
+ "smw_printername_rdf": "RDF-Export",
+ "smw_printername_category": "Kategorie",
+ "validator-type-class-SMWParamSource": "Text",
+ "smw-paramdesc-limit": "D'maximal Zuel vu Resultater déi gewise gëtt",
+ "smw-paramdesc-mainlabel": "D'Etikett déi der Haaptsäit den Numm gëtt",
+ "smw-paramdesc-link": "D'Werter als Linke weisen",
+ "smw-paramdesc-intro": "Den Text dee virun de Resultater vun der Ufro gewise gëtt, wann et der gëtt",
+ "smw-paramdesc-outro": "Den Text deen no de Resultater vun der Ufro gewise gëtt, wann et der gëtt",
+ "smw-paramdesc-default": "Den Text den ugewise gëtt wann et keng Resultater beim Siche gëtt",
+ "smw-paramdesc-sep": "D'Trennzeeche fir Wäerter",
+ "smw-paramdesc-template": "Den Numm vun enger Schabloun mat där d'Drockversioune gewise ginn",
+ "smw-paramdesc-columns": "D?Zuel vu Kolonnen an deenen d'Resultater vun der Sich gewise ginn",
+ "smw-paramdesc-userparam": "E Wäert deen an all Opruff vun enger Schabloun dragesat gëtt, wann eng Schabloun benotzt gëtt",
+ "smw-paramdesc-introtemplate": "Den Numm vun enger Schabloun déi virun de Resultater vun der Ufro gewise gëtt, wann et der gëtt",
+ "smw-paramdesc-outrotemplate": "Den Numm vun enger Schabloun déi no de Resultater vun der Ufro gewise gëtt, wann et der gëtt",
+ "smw-paramdesc-embedformat": "Den HTML-Tag dee benotzt gëtt fir d'Iwwerschrëften ze definéieren",
+ "smw-paramdesc-embedonly": "Keng Iwwerschrëfte weisen",
+ "smw-paramdesc-table-class": "Eng zousätzlech CSS-Klass fir Tabellen",
+ "smw-paramdesc-table-transpose": "D'Iwwerschrëfte vun Tabelle vertikal an d'Resultater horizontal weisen",
+ "smw-paramdesc-rdfsyntax": "RDF-Syntax déi benotzt gi muss",
+ "smw-paramdesc-csv-sep": "Spezifizéiert d'Trennsymbol fir Kolonnen",
+ "smw-paramdesc-dsv-separator": "D'Trennsymbol dat benotzt gi muss",
+ "smw-paramdesc-dsv-filename": "Den Numm fir den DSV-Fichier",
+ "smw-paramdesc-searchlabel": "Den Text fir mam Siche virunzefueren",
+ "smw-paramdesc-export": "Export-Optiounen",
+ "smw-paramdesc-jsonsyntax": "JSON Syntax muss benotzt ginn",
+ "smw_iq_moreresults": "… weider Resultater",
+ "smw_parseerror": "De Wäert deen Dir aginn hutt gouf net verstan.",
+ "smw_notitle": "\"$1\" kann net als Numm vun enger Säit op dëser Wiki benotzt ginn.",
+ "smw_wrong_namespace": "Nëmme Säiten aus dem Nummraum \"$1\" sinn hei erlaabt.",
+ "smw_emptystring": "Eidel Zeeche ginn net akzeptéiert.",
+ "smw_notinenum": "\"$1\" ass net an der Lëscht ($2) vun den [[Property:Allows value|erlaabte Wäerter]] fir d'Eegeschaft \"$3\".",
+ "smw_true_words": "wouer,w,jo,j",
+ "smw_false_words": "falsch,f,neen,n",
+ "smw_nofloat": "\"$1\" ass keng Zuel.",
+ "smw_infinite": "Zuele sou grouss wéi \"$1\" ginn net ënnerstëtzt.",
+ "smw_novalues": "Keng Wäerter spezifizéiert.",
+ "smw_nodatetime": "Den Datum \"$1\" gouf net verstan.",
+ "smw_noclosingbrackets": "Eng oder méi \"<nowiki>[[</nowiki>\" an Ärer Ufro war net zou duerch eng entspriechent \"]]\".",
+ "smw_misplacedsymbol": "D'Symbol \"$1\" gouf op ener Plaz benotzt wou et net nëtzlech ass.",
+ "smw_badqueryatom": "Een Deel \"<nowiki>[[…]]</nowiki>\" vun der Ufro gouf net verstan.",
+ "smw_propvalueproblem": "De Wäert vun der Eegenschaft \"$1\" gouf net verstane.",
+ "smw_type_header": "Eegeschafte vum Typ \"$1\"",
+ "smw_attribute_header": "Säiten déi d'Eegeschaft \"$1\" benotzen",
+ "smw_attributearticlecount": "Et {{PLURAL:$1|gëtt eng Säit|gi(nn) $1 Säite}} gewisen, déi dës Eegenschaft {{PLURAL:$1|benotzt|benotzen}}:",
+ "smw-propertylist-redirect-header": "Synonymmen",
+ "exportrdf": "Säiten als RDF exportéieren",
+ "smw_exportrdf_backlinks": "Och all Säiten déi op déi exportéiert Säite referéieren exportéieren.\nEt gët en RDF ugeluecht dee liicht duerchsicht ka ginn.",
+ "smw_exportrdf_lastdate": "Keng Säiten exportéieren déi zënter dem Zäitpunkt deen uginn ass net geännert goufen.",
+ "smw_exportrdf_submit": "Exportéieren",
+ "properties": "Eegenschaften",
+ "smw_properties_docu": "Dës Eegeschafte ginn op dëser Wiki benotzt.",
+ "smw_property_template": "$1 vum Typ $2 ({{PLURAL:$3|eemol benotzt|$3 benotzt}})",
+ "smw_propertylackspage": "All Eegeschafte sollen op enger Säit beschriwwe sinn!",
+ "smw_propertyhardlyused": "Dës Eegenschaft gëtt an dëser Wiki kaum benotzt!",
+ "smw-special-property-searchform-options": "Optiounen",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "Keng",
+ "smw-special-wantedproperties-filter-unapproved": "Net confirméiert",
+ "concepts": "Konzepter",
+ "smw-special-concept-header": "Lëscht vun de Konzepter",
+ "smw-special-concept-empty": "Et gouf kee Konzept fonnt.",
+ "unusedproperties": "Onbenotzt Eegeschaften",
+ "smw-unusedproperties-docu": "OP dëser Säit stinn déi [https://www.semantic-mediawiki.org/wiki/Unused_properties net benotzt Eegenschaften] déi deklaréiert sinn och wa keng aner Säite se benotzen. Fir eng aner Vue kuckt d'Spezialsäite mat [[Special:Properties|allen]] oder de [[Special:WantedProperties|gewënschten Eegenschaften]].",
+ "smw-unusedproperty-template": "$1 vum Typ $2",
+ "wantedproperties": "Gewënschten Eegeschaften",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|mol benotzt|mol benotzt}})",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|mol benotzt}})",
+ "smw_purge": "Aktualiséieren",
+ "smw-purge-failed": "Aktualiséierung huet net funktionéiert",
+ "types": "Typen",
+ "smw-special-types-no-such-type": "Den Datentyp deen ugi gouf gëtt et net",
+ "smw-statistics": "Semantesch Statistiken",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Ufro|Ufroen}}",
+ "smw-statistics-query-size": "Gréisst vun der Ufro",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Konzept|Konzepter}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Konzept|Konzepter}}]]",
+ "ask": "Semantesch sichen",
+ "smw_ask_sortby": "No der Kolonn zortéieren (Optioun)",
+ "smw_ask_ascorder": "Vu kleng op grouss (croissant)",
+ "smw_ask_descorder": "vu grouss op kleng (décroissant)",
+ "smw-ask-order-rand": "Zoufall",
+ "smw_ask_submit": "Resultater sichen",
+ "smw_ask_editquery": "Ufro änneren",
+ "smw_add_sortcondition": "[Konditioun fir d'Zortéieren dobäisetzen]",
+ "smw_ask_hidequery": "Ufro verstoppen",
+ "smw_ask_help": "Hëllef ufroen",
+ "smw_ask_queryhead": "Konditioun",
+ "smw_ask_printhead": "Eraussiche fir ze drécken",
+ "smw_ask_printdesc": "(eng Eegenschaft pro Linn dobäisetzen)",
+ "smw_ask_format_as": "Formatéieren als:",
+ "smw_ask_defaultformat": "Standard",
+ "smw_ask_otheroptions": "Aner Optiounen",
+ "smw-ask-otheroptions-collapsed-info": "Benotzt w.e.g. de 'Plus-Icon' fir all disponibel Optiounen ze gesinn",
+ "smw_ask_show_embed": "Agebonnene Code weisen",
+ "smw_ask_hide_embed": "Agebonnene Code verstoppen",
+ "smw-ask-delete": "Ewechhuelen",
+ "smw-ask-sorting": "Zortéieren",
+ "smw-ask-options": "Optiounen",
+ "smw-ask-options-sort": "Zortéier-Optiounen",
+ "smw-ask-format-options": "Format an Optiounen",
+ "smw-ask-parameters": "Parameteren",
+ "smw-ask-search": "Sichen",
+ "smw-ask-debug": "Feeler analyséieren",
+ "smw-ask-no-cache": "Keen Tëschespäicher",
+ "smw-ask-no-cache-desc": "Resultater ouni Tëschespäicher vun den Ufroen",
+ "smw-ask-result": "Resultat",
+ "smw-ask-empty": "Komplett eidelmaachen",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Hëllef fir den erausgesichte Format: $1",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> Fir 'D'Sichen no enger Eegenschaft z'aktivéieren (z. Bsp. <code>[[p:Huet ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> fir Propose fir Kategorien z'aktivéieren",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> fir d'Konzeptsichen z'aktivéieren",
+ "searchbyproperty": "No Eegeschafte sichen",
+ "processingerrorlist": "Feelerlëscht gëtt verschafft",
+ "smw_sbv_docu": "Sichen no alle Säiten déi eng bestëmmten Eegenschaft mat engem bestëmmte Wäert hunn.",
+ "smw_sbv_displayresult": "Eng Lëscht vun alle Säiten déi d'Eegeschaft \"$1\" mam Wäert \"$2\" hunn",
+ "smw_sbv_property": "Eegenschaft:",
+ "smw_sbv_value": "Wäert:",
+ "smw_sbv_submit": "Resultater sichen",
+ "browse": "Duerch d'Wiki goen",
+ "smw_browse_go": "Lass",
+ "smw_browse_show_incoming": "Eegenschafte weisen déi heihi linken",
+ "smw_browse_hide_incoming": "Eegeschaften verstoppen déi hei hi linken",
+ "smw_browse_no_outgoing": "Dës Säit huet keng Eegeschaften.",
+ "smw_browse_no_incoming": "Et linke keng Eegeschaften op dës Säit.",
+ "smw-browse-from-backend": "Informatioune ginn elo vum Backend ofgeruff.",
+ "smw-browse-show-group": "Gruppe weisen",
+ "smw-browse-hide-group": "Gruppe verstoppen",
+ "smw_inverse_label_default": "$1 vu(n)",
+ "pageproperty": "An den Eegeschafte vun der Säit sichen",
+ "smw_pp_from": "Vun der Säit:",
+ "smw_pp_type": "Eegenschaft:",
+ "smw_pp_submit": "Resultater sichen",
+ "smw_result_prev": "Vireg",
+ "smw_result_next": "Nächst",
+ "smw_result_results": "Resultater",
+ "smw_result_noresults": "Pardon, et gouf näischt fonnt.",
+ "smwadmin": "Administrativ Funktiounen",
+ "smw-admin-statistics-job-title": "Job-Statistiken",
+ "smw-admin-statistics-querycache-title": "Statistike vun den Ufroen un den Tëschespäicher",
+ "smw_smwadmin_return": "Zréck op $1",
+ "smw_smwadmin_updatenotstarted": "Et ass schonn een Aktualiséiereungs-Prozess amgaang.\nEt ka keen neien ugefaang ginn.",
+ "smw_smwadmin_updatestopped": "All Aktualisatiouns-Prozesser goufe gestoppt.",
+ "smw-admin-docu": "Dës Spezialsäit hëlleft Iech während der Installatioun, der Aktualiséierung, der Maintenance an dem Gebrauch vu <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> an erméiglecht Iech weider administrativ Funktiounen an Aufgaben, geneesou wéi Statistiken.\nDenkt drun fir wichteg Donnéeën ze späicheren ier Dir administrativ Funktiounen ausféiert.",
+ "smw-admin-db": "Datebank-installatioun an -aktualiséierung",
+ "smw-admin-dbbutton": "Tabellen initialiséieren oder aktualiséieren",
+ "smw-admin-announce": "Är Wiki ukënnegen",
+ "smw-admin-announce-text": "Wann Är Wiki ëffentlech ass kënnt Dir se op der Wiki-Tracking-Wiki <a href=\"https://wikiapiary.com\">WikiApiary</a> registréieren.",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> ass vereelzt a gëtt a(n) $2 ewechgeholl",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> gouf schonn duerch <code>$2</code> ersat",
+ "smw-admin-deprecation-notice-title-notice": "Ännerungen déi virgesi sinn",
+ "smw-admin-deprecation-notice-title-replacement": "Ersat oder ëmbenannt Astellungen",
+ "smw-admin-deprecation-notice-title-removal": "Astellungen déi ewechgeholl goufen",
+ "smw_smwadmin_datarefresh": "Donnéeë goufe restauréiert",
+ "smw_smwadmin_datarefreshprogress": "<strong>Eng Aktualiséierung ass schonn am Gaang.</strong>\nEt ass normal datt d'Aktualiséierung nëmme lues virugeet well Donnéeë nëmmen a klenge Päck aktualiséiert ginn an zwar ëmmer da wann ee Benotzer op dës Wiki zougräift.\nFir dës Aktualiséierung méi séier fäerdeg ze maache, kann de MediaWiki Script <code>runJobs.php</code> (benotzt d'Optioun <code>--maxjobs 1000</code> fir d'Zuel vun den Aktualiséierungen déi beienee gemaach ginn ze limitéieren).\nGeschate Fortschrëtt vun der aktueller Aktualiséierung:",
+ "smw_smwadmin_datarefreshbutton": "Aktualiséiere vun den Date plangen",
+ "smw_smwadmin_datarefreshstop": "Dësn Update stoppen",
+ "smw_smwadmin_datarefreshstopconfirm": "Jo, ech si {{GENDER:$1|sécher}}.",
+ "smw-admin-support": "Ënnerstëtzung kréien",
+ "smw-admin-supportdocu": "Verschidde Quelle kënnen Iech bei Problemer hëllefen:",
+ "smw-admin-installfile": "Wann Dir Problemer bei der Installatioun hutt, da fänkt un andeem Dir d'Direktiven am <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL Fichier</a> an d'a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">Installatiouns-Säit</a> nokuckt.",
+ "smw-admin-smwhomepage": "Déi komplett Benotzerdokumentatioun vu Semantic MediaWiki fannt Dir op <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Feeler kënnen op dem <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">'Issue tracker'</a>, op <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">Feeler mellen</a> fannt Dir Informatioune wéi dir dat effikass maache kënnt.",
+ "smw-admin-questions": "Wann Dir nach Froen oder Propositiounen relativ zu Semantic MediaWiki hutt, da bedeelegt Iech un der Diskussioun op der <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">Semantic MediaWiki Benotzer-Mailinglëscht</a> oder am <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">Chatroom</a>.",
+ "smw-admin-other-functions": "Aner Funnktiounen",
+ "smw-admin-supplementary-section-title": "Zousätzlech Funktiounen",
+ "smw-admin-supplementary-section-subtitle": "Disponibel Funktiounen",
+ "smw-admin-supplementary-section-intro": "Verschidde Funktiounen déi an dësem Abschnitt opgezielt gi sinn eventuell limitéiert an dofir an dëser Wiki net disponibel.",
+ "smw-admin-supplementary-settings-title": "Astellunge vun der Konfiguratioun",
+ "smw-admin-supplementary-operational-statistics-title": "Operationell Statistiken",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> weist eng erweidert Sammlung vu Statistiken",
+ "smw-admin-supplementary-elastic-functions": "Disponibel Funktiounen",
+ "smw-admin-supplementary-elastic-settings-title": "Astellungen",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resumé",
+ "smw-admin-supplementary-elastic-indices-title": "Hiweiser",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistiken",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Aktualisatiounsintervall: $1",
+ "smw_adminlinks_datastructure": "Datestruktur",
+ "smw_adminlinks_displayingdata": "Donnéeë weisen",
+ "smw_adminlinks_inlinequerieshelp": "Hëllef fir intern Ufroen",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|Benotzer|System}} definéiert Eegenschaft",
+ "smw-createproperty-isproperty": "Et ass eng Eegenschaft vum Typ $1.",
+ "smw-paramdesc-category-template": "Eng Schabloun fir d'Objeten ze formatéieren",
+ "smw-paramdesc-category-userparam": "E Parameter deen der Schabloun iwwergi gëtt",
+ "smw-info-par-message": "Message fir ze weisen.",
+ "smw-info-par-icon": "Symbol fir entweder \"Info\" oder \"Warnung\" ze weisen.",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Allgemeng Optiounen",
+ "prefs-ask-options": "Spezial:Optioune Froen",
+ "smw-prefs-general-options-disable-editpage-info": "Den Aféierungstext op der Ännerungssäit ausschalten",
+ "smw-ui-tooltip-title-property": "Eegenschaft",
+ "smw-ui-tooltip-title-quantity": "Ëmwandlung vun der Eenheet",
+ "smw-ui-tooltip-title-info": "Informatioun",
+ "smw-ui-tooltip-title-service": "Service-Linken",
+ "smw-ui-tooltip-title-warning": "Warnung",
+ "smw-ui-tooltip-title-error": "Feeler",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Evenement",
+ "smw-ui-tooltip-title-note": "Notiz",
+ "smw-ui-tooltip-title-legend": "Erklärung",
+ "smw-ui-tooltip-title-reference": "Referenz",
+ "smw-concept-cache-text": "D'Konzept huet am Ganzen {{PLURAL:$1|eng Säit|$1 Säiten}} a gouf fir d'lescht den $2 ëm $3 aktualiséiert.",
+ "smw_concept_header": "Säite vum Konzept \"$1\"",
+ "smw_conceptarticlecount": "Déi $1 {{PLURAL:$1|Säit|Säite}} ginn hei drënner gewisen.",
+ "restriction-level-smw-pageedit": "gespaart (nëmme berechtegt Benotzer)",
+ "group-smwadministrator": "Administrateuren (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administrateuren (Semantic MediaWiki)",
+ "smw-sp-admin-settings-button": "Lëscht vun den Astellunge generéieren",
+ "smw-admin-idlookup-title": "Nosichen",
+ "smw-admin-idlookup-input": "Sichen:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Iwwersiicht",
+ "smw-admin-maintenance-no-description": "Keng Beschreiwung.",
+ "smw-livepreview-loading": "Lueden...",
+ "smw-sp-searchbyproperty-resultlist-header": "Lëscht vun de Resultater",
+ "smw-editpage-annotation-disabled": "Dës Säit ass net fir semantesch Notifikatiounen am Text konfiguréiert duerch d'Limitatiounen an dësem Nummraum. Detailer wéi dat fir den Nummraum ageschalt ka gi fannt Dir op der Hëllefssäit fir d'[https://www.semantic-mediawiki.org/wiki/Help:Configuration Astellungen].",
+ "smw-search-syntax": "Syntax",
+ "smw-search-profile": "Erweidert",
+ "smw-search-profile-sort-title": "Titel",
+ "smw-search-profile-extended-help-query": "Link op:$1",
+ "smw-search-profile-extended-help-query-link": "(fir méi Detailer $1).",
+ "smw-search-profile-extended-section-namespace": "Nummraum",
+ "smw-search-show": "Weisen",
+ "smw-search-hide": "Verstoppen",
+ "log-name-smw": "Semantic-MediaWiki-Logbuch",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1-Import]]",
+ "smw-datavalue-invalid-number": "\"$1\" kann net als Zuel interpretéiert ginn.",
+ "smw-types-list": "Lëscht vun Datentypen",
+ "smw-types-default": "\"$1\" ass en agebauten Datentyp.",
+ "smw-types-help": "Méi Informatiounen a Beispiller fannt Dir op dëser [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 Hëllef-Säit].",
+ "smw-type-tab-types": "Typen",
+ "smw-type-tab-errors": "Feeler",
+ "smw-type-contextual": "Kontextuel",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Sekonn|Sekonnen}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|Sekonn|Sekonnen}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Sekonn|Sekonnen}}",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" huet eng Informatioun déi net kann interpretéiert ginn.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" huet e puer eidel Komponenten",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" huet méi wéi dräi Komponenten, déi fir d'Interpretatioun vun engem Datum obligatoresch sinn.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" huet \"$2\" als Stonnenelement, dat ass fir eng 12-Stonne-Konventioun awer net valabel.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" ass keng valabel URL",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "Den JSON-Parser huet mat engem \"$1\" geäntwert.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Link an den Tëschespäicher kopéieren",
+ "smw-data-lookup": "Donnéeën ofruffen...",
+ "smw-data-lookup-with-wait": "D'Ufro gëtt verschafft an et kann een Ament daueren.",
+ "smw-no-data-available": "Keng Donnéeën disponibel.",
+ "smw-edit-protection-enabled": "Gespaart fir z'änneren (Semantic Mediawiki)",
+ "smw-format-datatable-emptytable": "An der Tabell si keng Donnéeën disponibel",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "Weis_MENU_Elementer",
+ "smw-format-datatable-loadingrecords": "Lueden...",
+ "smw-format-datatable-processing": "Verschaffen...",
+ "smw-format-datatable-search": "Sichen:",
+ "smw-format-datatable-zerorecords": "Et gouf keen esou en Enregistrement fonnt",
+ "smw-format-datatable-first": "Éischt",
+ "smw-format-datatable-last": "Lescht",
+ "smw-format-datatable-next": "Nächst",
+ "smw-format-datatable-previous": "Vireg",
+ "smw-format-datatable-toolbar-export": "Exportéieren",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-api-invalid-parameters": "Net valabel Parameteren, \"$1\"",
+ "smw-property-reserved-category": "Kategorie",
+ "smw-category": "Kategorie",
+ "smw-filter": "Filter",
+ "smw-section-expand": "Den Abschnitt opklappen",
+ "smw-section-collapse": "Den Abschnitt zesummeklappen",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1] Format",
+ "smw-help": "Hëllef",
+ "smw-property-predefined-label-skey": "Zortéierschlëssel",
+ "smw-processing": "Verschaffen...",
+ "smw-types-title": "Typ: $1",
+ "smw-schema-error": "Validéierungsfeeler",
+ "smw-schema-error-violation": "Violatioun (\"$1\", \"$2\")",
+ "smw-schema-title": "Schema",
+ "smw-schema-type": "Typ",
+ "smw-ask-title-keyword-type": "Schlësselwuert sichen",
+ "smw-parameter-missing": "De Parameter \"$1\" feelt.",
+ "smw-property-tab-usage": "Benotzung",
+ "smw-property-tab-redirects": "Synonymmen",
+ "smw-property-tab-specification": "... méi",
+ "smw-concept-tab-list": "Lëscht",
+ "smw-concept-tab-errors": "Feeler",
+ "smw-ask-tab-result": "Resultat"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lez.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lez.json
new file mode 100644
index 00000000..2304727d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lez.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ппарзава..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/li.json b/www/wiki/extensions/SemanticMediaWiki/i18n/li.json
new file mode 100644
index 00000000..fa60e081
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/li.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Laje…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lij.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lij.json
new file mode 100644
index 00000000..eb64c35f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lij.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Giromin Cangiaxo"
+ ]
+ },
+ "browse": "Esplora o scito",
+ "smw-livepreview-loading": "Camallando…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lki.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lki.json
new file mode 100644
index 00000000..b9b85c36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lki.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hosseinblue",
+ "Arash71",
+ "Lakzon"
+ ]
+ },
+ "smw_printername_category": "Ú•Ùزگ",
+ "smw-paramdesc-feeddescription": "متن به عنوان توصی٠مرورگر مورد استÙاده قرار می‌گیرد",
+ "smw-sp-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] می‌تواند به عنوان \"دسته پویا\" مشاهده شوند، یعنی به عنوان مجموعه‌ای از صÙحاتی Ú©Ù‡ به طور دستی ایجاد نمی‌شوند، اما آنهایی Ú©Ù‡ توسط مدیاویکی معنایی از یک توصی٠داده شده پرس‌وجو، محاسبه می‌شوند.",
+ "smw-ask-format-selection-help": "برای شرح Ù…Ùصل، لطÙاً صÙحه راهنما $1 را مشاهده کنید.",
+ "browse": "مرور ویکی",
+ "smw-ui-tooltip-title-note": "ویرنۆیسة-یادداشت"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lrc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lrc.json
new file mode 100644
index 00000000..5505e4dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lrc.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mogoeilor"
+ ]
+ },
+ "browse": "دوارته نئری سی ویکی"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lt.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lt.json
new file mode 100644
index 00000000..75e391c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lt.json
@@ -0,0 +1,188 @@
+{
+ "@metadata": {
+ "authors": [
+ "Vpovilaitis",
+ "Hugo.arg",
+ "Zygimantus",
+ "Eitvys200",
+ "Manvydasz",
+ "Nemo bis"
+ ]
+ },
+ "smw-desc": "Padaryti Jūsų wiki labiau prieinamą - mašinoms ''ir'' žmonėms ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw_viewasrdf": "RDF srautas",
+ "smw_finallistconjunct": ", ir",
+ "smw-factbox-head": "... daugiau apie \"$1\"",
+ "smw-factbox-facts": "Faktai",
+ "smw_isspecprop": "Å i savybÄ— yra speciali Å¡ioje wiki.",
+ "smw_concept_description": "Sąvokos \"$1\" aprašymas",
+ "smw_no_concept_namespace": "Sąvokos gali būti apibrėžtos tik Concept: vardų srities puslapiuose.",
+ "smw_multiple_concepts": "Kiekviename sąvokos aprašymo puslapyje gali turėti tik vieną sąvokos apibrėžimą.",
+ "smw_concept_cache_miss": "SÄ…voka \"$1\" negali bÅ«ti naudojamas Å¡iuo metu, nes wiki konfigÅ«racija reikalauja jÄ… apskaiÄiuoti off-line.\nJei problema neiÅ¡nyksta po tam tikro laiko, kreipkitÄ—s į svetainÄ—s administratorių, kad Å¡iÄ… sÄ…vokÄ… padarytų prieinamÄ….",
+ "smw_noinvannot": "Reikšmės negali būti priskiriamos atvirkštinei savybei.",
+ "version-semantic": "Semantiniai praplÄ—timai",
+ "smw_baduri": "URI formoje \"$1\" yra draudžiami.",
+ "smw_printername_count": "PaskaiÄiuoti rezultatus",
+ "smw_printername_csv": "CSV eksportavimas",
+ "smw_printername_dsv": "DSV eksportavimas",
+ "smw_printername_debug": "Derinti užklausą (ekspertams)",
+ "smw_printername_embedded": "Įstatyti puslapio turinį",
+ "smw_printername_json": "JSON eksportas",
+ "smw_printername_list": "Sąrašas",
+ "smw_printername_ol": "IÅ¡vardinimas",
+ "smw_printername_ul": "Sudarymas iš elementų",
+ "smw_printername_table": "LentelÄ—",
+ "smw_printername_broadtable": "Plati lentelÄ—",
+ "smw_printername_template": "Å ablonas",
+ "smw_printername_rdf": "RDF eksportavimas",
+ "smw_printername_category": "Kategorija",
+ "validator-type-class-SMWParamSource": "tekstas",
+ "smw-paramdesc-limit": "Maksimalus grąžinamas rezultatų skaiÄius",
+ "smw-paramdesc-headers": "Rodyti antraštes/savybių pavadinimus",
+ "smw-paramdesc-mainlabel": "EtiketÄ—je pateikti pagrindinio puslapio pavadinimÄ…",
+ "smw-paramdesc-link": "Rodyti reikšmes kaip nuorodas",
+ "smw-paramdesc-intro": "Tekstas būtų rodomas prieš užklausos rezultatus, jei yra kokių nors rezultatų",
+ "smw-paramdesc-outro": "Tekstas būtų rodomas po užklausos rezultatų, jei yra kokių nors rezultatų",
+ "smw-paramdesc-default": "Tekstas būtų rodomas, jei nėra užklausos rezultatų",
+ "smw-paramdesc-sep": "Reikšmių skirtukas",
+ "smw-paramdesc-template": "Šablono, su kuriuo būtų galima parodyti išvedimą, pavadinimas",
+ "smw-paramdesc-columns": "Stulpelių skaiÄius, kuriuose bus rodomas rezultatas (pagal nutylÄ—jimÄ… yra $1)",
+ "smw-paramdesc-userparam": "Reikšmė patenka į kiekvieną šabloną iškvietimą, jei šablonas yra naudojamas",
+ "smw-paramdesc-introtemplate": "Šablono pavadinimas, kuris būtų rodomas prieš užklausos rezultatus, jei yra kokių nors rezultatų",
+ "smw-paramdesc-outrotemplate": "Šablono pavadinimas, kuris būtų rodomas po užklausos rezultatų, jei yra kokių nors rezultatų",
+ "smw-paramdesc-embedformat": "HTML žymė naudojama apibrėžti antraštes",
+ "smw-paramdesc-embedonly": "Nerodyti antraÅ¡Äių",
+ "smw-paramdesc-dsv-filename": "DSV failo pavadinimas",
+ "smw-paramdesc-filename": "IÅ¡vesties failo pavadinimas",
+ "smw-paramdesc-searchlabel": "Tekstas tęsti paieškai",
+ "smw-paramdesc-export": "Eksportavimo nustatymas",
+ "smw-printername-feed": "RSS ir Atom srautas",
+ "smw-paramdesc-feedtype": "Srauto tipas",
+ "smw-label-feed-description": "$1 $2 srautas",
+ "smw_iq_disabled": "Semantinės užklausos yra išjungtos šioje wiki.",
+ "smw_iq_moreresults": "... tolesni rezultatai",
+ "smw_parseerror": "Nurodyta reikšmė yra nesuprantama.",
+ "smw_notitle": "\"$1\" negali būti naudojamas kaip puslapio pavadinimas šiame wiki.",
+ "smw_wrong_namespace": "ÄŒia leidžiami tik puslapiai, priklausantys vardų sriÄiai \"$1\".",
+ "smw_manytypes": "Savybei apibrėžtas daugiau negu vienas tipas.",
+ "smw_emptystring": "TuÅ¡Äios eilutÄ—s yra nepriimtinos.",
+ "smw_notinenum": "\"$1\" nėra šios savybės galimų reikšmių sąraše ($2).",
+ "smw_noboolean": "\"$1\" nėra pripažįstama kaip loginė (true / false) vertė.",
+ "smw_true_words": "tiesa,t,taip,yes,y",
+ "smw_false_words": "klaida,k,ne,n,false,f,no",
+ "smw_nofloat": "\"$1\" nÄ—ra skaiÄius.",
+ "smw_infinite": "Tokie dideli skaiÄiai, kaip \"$1\" yra nepalaikomi.",
+ "smw_novalues": "Nenurodyta reikšmė.",
+ "smw_nodatetime": "Data \"$1\" buvo neatpažinta.",
+ "smw_toomanyclosing": "Atrodo, kad yra per daug elementų \"$1\" užklausoje.",
+ "smw_noclosingbrackets": "Kažkuris panaudojimas \"<nowiki>[[</nowiki>\" jūsų užklausoje nebuvo uždaryta atitikimo \"]]\".",
+ "smw_misplacedsymbol": "Simbolis \"$1\" buvo panaudotas toje vietoje, kurioje jis nÄ—ra naudojamas.",
+ "smw_unexpectedpart": "Užklausos dalis \"$1\" nebuvo suprasta.\nRezultatas gali būti ne tas, kurio tikėtasi.",
+ "smw_emptysubquery": "Kai kurios užklausos dalys turi blogas sąlygas.",
+ "smw_propvalueproblem": "Savybės reikšmę „$1“ buvo nesuprasta.",
+ "smw_attribute_header": "Puslapiai naudojantys savybę „$1“",
+ "smw-propertylist-redirect-header": "Sinonimai",
+ "exportrdf": "Eksportuoti puslapius į RDF",
+ "smw_exportrdf_submit": "Eksportuoti",
+ "properties": "SavybÄ—s",
+ "concepts": "Koncepcijos",
+ "smw-sp-concept-header": "Koncepcijų sąrašas",
+ "unusedproperties": "Nenaudojamos savybÄ—s",
+ "smw-unusedproperty-template": "$1, $2 tipo",
+ "smw_purge": "Atnaujinti",
+ "types": "Tipai",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|SavybÄ—|SavybÄ—s}} (viso)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Užklausa|Užklausos}}",
+ "smw-statistics-query-size": "Užklausos dydis",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Pasenęs objektas|Pasenę objektai}} (pažymėti trynimui)",
+ "smw_ask_sortby": "Rūšiuoti pagal stulpelį (neprivaloma)",
+ "smw_ask_ascorder": "DidÄ—jimo tvarka",
+ "smw_ask_descorder": "Mažėjimo tvarka",
+ "smw_ask_submit": "Rasti rezultatus",
+ "smw_ask_editquery": "Redaguoti užklausą",
+ "smw_add_sortcondition": "[Pridėti rūšiavimo sąlygą]",
+ "smw_ask_hidequery": "Slėpti užklausą",
+ "smw_ask_help": "Užklausų pagalba",
+ "smw_ask_queryhead": "Užklausa",
+ "smw_ask_printhead": "Spausdinimo pasirinkimas",
+ "smw_ask_printdesc": "(pridÄ—kite vienÄ… savybÄ—s pavadinimÄ… eilutÄ—je)",
+ "smw_ask_format_as": "Formatuoti kaip:",
+ "smw_ask_defaultformat": "numatomas",
+ "smw_ask_otheroptions": "Kiti nustatymai",
+ "smw_ask_show_embed": "Rodyti įterpimo kodą",
+ "smw_ask_hide_embed": "Slėpti įterpimo kodą",
+ "smw-ask-delete": "IÅ¡trinti",
+ "smw-ask-sorting": "RÅ«Å¡iavimas",
+ "smw-ask-search": "Paieška",
+ "searchbyproperty": "Ieškoti pagal savybę",
+ "processingerrorlist": "Klaidų sąrašas apdorojamas",
+ "smw_sbv_property": "SavybÄ—:",
+ "smw_sbv_value": "Reikšmė:",
+ "smw_sbv_submit": "Rasti rezultatus",
+ "browse": "Naršyti viki",
+ "smw_browselink": "Naršyti savybes",
+ "smw_browse_go": "Eiti",
+ "smw_browse_show_incoming": "rodyti savybes, kurios Äia nukreipia",
+ "smw_browse_hide_incoming": "slÄ—pti savybes, kurios Äia nukreipia",
+ "smw_browse_no_outgoing": "Šis puslapis neturi savybių.",
+ "smw_browse_no_incoming": "Nėra savybių, kurios nukreipia į šį puslapį.",
+ "smw_inverse_label_default": "$1 iš",
+ "smw_pp_from": "IÅ¡ puslapio",
+ "smw_pp_type": "SavybÄ—",
+ "smw_pp_submit": "Rasti rezultatus",
+ "smw_result_prev": "Ankstesnis",
+ "smw_result_next": "Paskesnis",
+ "smw_result_results": "Rezultatai",
+ "smw_result_noresults": "Nėra rezultatų.",
+ "smw-admin-permission-missing": "Prieiga prie šio puslapio buvo užblokuota dėl teisių nebuvimo, prašome peržiūrėti [https://www.semantic-mediawiki.org/wiki/Help:Permissions teisių] pagalbinį puslapį, kur rasite informaciją apie reikiamus nustatymus.",
+ "smw_smwadmin_return": "Grįžti į $1",
+ "smw_smwadmin_updatestopped": "Visi egzistuojantys atnaujinimo procesai buvo sustabdyti.",
+ "smw-admin-announce": "Paskelbti savo viki",
+ "smw-admin-deprecation-notice-title-notice": "BÅ«simi pakeitimai",
+ "smw-admin-deprecation-notice-title-replacement": "Pakeisti ar pervadinti nustatymai",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Nustatymai buvo pervadinta ar kitaip pakeisti, prašome atnaujinti jų pavadinimą ar formatą.",
+ "smw-admin-deprecation-notice-title-removal": "Pašalinti nustatymai",
+ "smw_smwadmin_datarefreshbutton": "PradÄ—ti atnaujinti duomenis",
+ "smw_smwadmin_datarefreshstop": "Sustabdyti šį atnaujinimą",
+ "smw_smwadmin_datarefreshstopconfirm": "Taip, esu {{GENDER:$1|tikras|tikra}}.",
+ "smw-admin-support": "Gaunama parama",
+ "smw-admin-supplementary-duplookup-title": "Pasikartojantys įrašai",
+ "smw-list-count": "Šiame sąraše yra $1 {{PLURAL:$1|įrašas|įrašai}}.",
+ "smw_adminlinks_datastructure": "Duomenų struktūra",
+ "smw_adminlinks_displayingdata": "Duomenų rodinys",
+ "smw-property-indicator-type-info": "{{PLURAL: $1|Naudotojas|Sistema}}",
+ "smw-createproperty-isproperty": "Tai yra $1 tipo savybÄ—.",
+ "smw-createproperty-allowedvals": "Šios savybės leidžiamos reikšmės yra:",
+ "smw-paramdesc-category-template": "Å ablonas, kuris bus naudojamas formatuoti objektus",
+ "smw-info-par-message": "Žinutė rodymui.",
+ "smw-info-par-icon": "Piktograma rodymui, arba „info“ arba „warning“.",
+ "smw-ui-tooltip-title-property": "SavybÄ—",
+ "smw-ui-tooltip-title-quantity": "Vienetų konvertavimas",
+ "smw-ui-tooltip-title-info": "Informacija",
+ "smw-ui-tooltip-title-service": "Paslaugų nuorodos",
+ "smw-ui-tooltip-title-warning": "Įspėjimas",
+ "smw-ui-tooltip-title-parameter": "Parametras",
+ "smw-ui-tooltip-title-event": "Įvykis",
+ "smw-ui-tooltip-title-note": "Pastaba",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Å altinis",
+ "smw_unknowntype": "Savybei nustatytas nepalaikomas tipas",
+ "smw-sp-properties-header-label": "Savybių sąrašas",
+ "smw-sp-admin-settings-button": "Generuoti nustatymų sąrašą",
+ "smw-admin-objectid": "ID:",
+ "smw-livepreview-loading": "Įkeliama…",
+ "smw-sp-searchbyproperty-resultlist-header": "Rezultatų sąrašas",
+ "smw-sp-types-list": "Duomenų tipų sąrašas",
+ "smw-datavalue-languagecode-invalid": "„$1“ nebuvo atpažintas kaip palaikomas kalbos kodas.",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekundÄ—|sekundÄ—s}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|sekundė|sekundės|sekundžių}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekundÄ—|sekundÄ—s}}",
+ "smw-datavalue-external-formatter-invalid-uri": "„$1“ yra negalimas URL.",
+ "smw-datavalue-parse-error": "Nurodyta reikšmė „$1“ nebuvo suprasta.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-clipboard-copy-link": "Kopijuoti nuorodą į iškarpinę",
+ "smw-data-lookup-with-wait": "Prašymas apdorojamas ir tai gali užtrukti.",
+ "smw-section-expand": "Išskleisti dalį",
+ "smw-section-collapse": "Suskleisti dalį",
+ "smw-help": "Pagalba"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/luz.json b/www/wiki/extensions/SemanticMediaWiki/i18n/luz.json
new file mode 100644
index 00000000..ad061f73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/luz.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "علی ساکی لرستانی"
+ ]
+ },
+ "browse": "مۉروٙر ڤیکی"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lv.json
new file mode 100644
index 00000000..69b773b3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lv.json
@@ -0,0 +1,26 @@
+{
+ "@metadata": {
+ "authors": [
+ "Edgars2007",
+ "Papuass"
+ ]
+ },
+ "smw_purge": "AtjauninÄt",
+ "smw_sbv_value": "Vērtība:",
+ "browse": "PÄrlÅ«kot viki",
+ "smw_result_prev": "Iepriekšējie",
+ "smw_result_next": "NÄkamie",
+ "smw_result_results": "RezultÄti",
+ "smw_smwadmin_datarefreshstopconfirm": "JÄ, es esmu pÄrliecinÄts.",
+ "smw_adminlinks_datastructure": "Datu struktūra",
+ "smw-paramdesc-category-delim": "AtdalÄ«tÄjs",
+ "smw-ui-tooltip-title-info": "InformÄcija",
+ "smw-ui-tooltip-title-service": "Pakalpojumu saites",
+ "smw-ui-tooltip-title-warning": "Kļūda",
+ "smw-ui-tooltip-title-parameter": "Parametrs",
+ "smw-ui-tooltip-title-event": "Notikums",
+ "smw-ui-tooltip-title-note": "Piezīme",
+ "smw-ui-tooltip-title-legend": "Apzīmējumi",
+ "smw-sp-admin-idlookup-objectid": "Objekta Id:",
+ "smw-livepreview-loading": "IelÄdē…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/lzh.json b/www/wiki/extensions/SemanticMediaWiki/i18n/lzh.json
new file mode 100644
index 00000000..ac8fc679
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/lzh.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "éºè—…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mai.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mai.json
new file mode 100644
index 00000000..bf756e27
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mai.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "बिपà¥à¤²à¤¬ आननà¥à¤¦",
+ "Tulsi Bhagat"
+ ]
+ },
+ "browse": "बà¥à¤°à¤¾à¤‰à¤œ विकि",
+ "smw-livepreview-loading": "लोड भऽ रहल अछि..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mdf.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mdf.json
new file mode 100644
index 00000000..2f6ac12a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mdf.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ðноклакшни…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mg.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mg.json
new file mode 100644
index 00000000..93d333c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mg.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jagwar"
+ ]
+ },
+ "browse": "Hitety ny wiki",
+ "smw_browselink": "Hitady tondro",
+ "smw-livepreview-loading": "Am-pakàna…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/min.json b/www/wiki/extensions/SemanticMediaWiki/i18n/min.json
new file mode 100644
index 00000000..2337e9c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/min.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Iwan Novirion",
+ "Naval Scene"
+ ]
+ },
+ "smw_propertylackspage": "Sado properti musti dideskripsikan jo suatu laman!",
+ "smw_purge": "Pabaharui",
+ "smw_browse_go": "Tuju"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mk.json
new file mode 100644
index 00000000..741a82bc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mk.json
@@ -0,0 +1,383 @@
+{
+ "@metadata": {
+ "authors": [
+ "Bjankuloski06",
+ "Rancher",
+ "ì•„ë¼",
+ "Nemo bis",
+ "Vlad5250"
+ ]
+ },
+ "smw-desc": "Го прави вашето вики подоÑтапно - за машини ''и'' луѓе ([https://www.semantic-mediawiki.org/wiki/Help:User_manual?uselang=mk документација])",
+ "smw-title": "Семантички МедијаВики",
+ "smw-semantics-not-enabled": "Семантички МедијаВики не е овозможен на ова вики.",
+ "smw_viewasrdf": "RDF-тековник",
+ "smw_finallistconjunct": " и",
+ "smw-factbox-head": "... повеќе за „$1“",
+ "smw-factbox-facts": "Факти",
+ "smw-factbox-facts-derived": "Изведени факти",
+ "smw_isspecprop": "Ова ÑвојÑтво е Ñпецијално ÑвојÑтво во ова вики.",
+ "smw-concept-cache-header": "Употреба на меѓуÑклад",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count МеѓуÑкладот на концепти] Ñодржи {{PLURAL:$1|'''единица''' entity|'''$1''' единици}} ($2).",
+ "smw-concept-no-cache": "Ðема доÑтапен меѓуÑклад.",
+ "smw_concept_description": "ÐžÐ¿Ð¸Ñ Ð½Ð° концептот „$1“",
+ "smw_no_concept_namespace": "Концептите можат да Ñе определуваат Ñамо на Ñтраници во именÑкиот проÑтор „Концепт:“ („Concept:“).",
+ "smw_multiple_concepts": "Секоја концептна Ñтраница може да има Ñамо по една одредба на концепт.",
+ "smw_concept_cache_miss": "Концептот „$1“ во моментов не може да Ñе кориÑти, бидејќи меÑтењата на викито бараат тој да Ñе преÑмета вонÑемрежно.\nÐко проблемот не иÑчезне по извеÑно време, побарајте од вашиот админиÑтратор да го овозможи тој концепт.",
+ "smw_noinvannot": "Ðа обратните ÑвојÑтва не можат да им Ñе назначуваат вредноÑти.",
+ "version-semantic": "Семантички додатоци",
+ "smw_baduri": "URI-ја од обликот „$1“ не Ñе дозволени.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "ИÑход од пребројувањето",
+ "smw_printername_csv": "CSV извоз",
+ "smw_printername_dsv": "DSV-извоз",
+ "smw_printername_debug": "Поправање грешки во барања (за екÑперти)",
+ "smw_printername_embedded": "Вметни Ñодржина од Ñтраница",
+ "smw_printername_json": "JSON извоз",
+ "smw_printername_list": "СпиÑок",
+ "smw_printername_plainlist": "ПроÑÑ‚ ÑпиÑок",
+ "smw_printername_ol": "Ðабројување",
+ "smw_printername_ul": "СпиÑок",
+ "smw_printername_table": "Табела",
+ "smw_printername_broadtable": "Широка табела",
+ "smw_printername_template": "Шаблон",
+ "smw_printername_templatefile": "ШаблонÑка податотека",
+ "smw_printername_rdf": "RDF-извоз",
+ "smw_printername_category": "Категорија",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw-paramdesc-limit": "Ðајвеќе Ñтавки во иÑходот за прикажување",
+ "smw-paramdesc-offset": "ОтÑтапувањето на првата иÑходна Ñтавка.",
+ "smw-paramdesc-headers": "Прикажувај наÑлови/имиња на ÑвојÑтва",
+ "smw-paramdesc-mainlabel": "Ознаката која Ñе дава на името на главната Ñтраница",
+ "smw-paramdesc-link": "Прикажи ги вредноÑтите како врÑки",
+ "smw-paramdesc-intro": "ТекÑтот за прикажување пред иÑходот од барањето, ако го има",
+ "smw-paramdesc-outro": "ТекÑтот за прикажување по иÑходот од барањето, ако го има",
+ "smw-paramdesc-default": "ТекÑтот за прикажување ако нема иÑход од барањето",
+ "smw-paramdesc-sep": "Одделувач на иÑходни Ñтавки",
+ "smw-paramdesc-propsep": "Одделувачот помеѓу ÑвојÑтва во иÑходна Ñтавка",
+ "smw-paramdesc-showsep": "Прикажувај го одделувачот најгоре во CSV-податотеката (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "ÐамеÑто да Ñе прикажат Ñите вредноÑти, изброј колку пати Ñе јавуваат и прикажи ги нив.",
+ "smw-paramdesc-distributionsort": "Подреди ја раÑпределбата на вредноÑтите по број на јавувања.",
+ "smw-paramdesc-distributionlimit": "Ограничи ја раÑпределбата на вредноÑти Ñпоред бројот на Ñамо некои вредноÑти.",
+ "smw-paramdesc-template": "Името на шаблонот Ñо чија помош ќе Ñе прикажуваат податоците",
+ "smw-paramdesc-columns": "Бројот на Ñтолбови за приказ на иÑходни Ñтавки (по оÑновно: $1)",
+ "smw-paramdesc-userparam": "ВредноÑÑ‚ која Ñе дава при Ñекое повикување на шаблон, ако Ñе кориÑти",
+ "smw-paramdesc-introtemplate": "Име на шаблонот за приказ пред иÑходот од пребарувањето, ако го има",
+ "smw-paramdesc-outrotemplate": "Име на шаблонот за приказ по иÑходот од пребарувањето, ако го има",
+ "smw-paramdesc-embedformat": "HTML-ознаката која Ñе кориÑти за определување на наÑлови",
+ "smw-paramdesc-embedonly": "Ðе прикажувај наÑлови",
+ "smw-paramdesc-table-class": "Дополнителна CSS-клаÑа за табелата",
+ "smw-paramdesc-table-transpose": "Прикажувај ги заглавијата на табелите вертикално, а Ñтавките хоризонтално",
+ "smw-paramdesc-rdfsyntax": "RDF-ÑинтакÑата што ќе Ñе кориÑти",
+ "smw-paramdesc-csv-sep": "Укажува Ñтолбен разделник",
+ "smw-paramdesc-csv-valuesep": "Укажува вредноÑен разделник",
+ "smw-paramdesc-dsv-separator": "Кој разделник да Ñе кориÑти",
+ "smw-paramdesc-dsv-filename": "Име на DSV-податотеката",
+ "smw-paramdesc-filename": "Име на изводната податотека",
+ "smw-smwdoc-description": "Прикажува табела на Ñите параметри што можат да Ñе иÑкориÑтат за назначениот формат на иÑход заедно Ñо оÑновните вредноÑти и опиÑи.",
+ "smw-smwdoc-par-format": "Форматот на Ñтавките за кои Ñе прикажува параметарÑка документација.",
+ "smw-smwdoc-par-parameters": "Кои параметри да Ñе прикажуваат. „specific“ за оние што ги додава форматот, „base“ за оние доÑтапни во Ñите формати, а „all“ за обете.",
+ "smw-paramdesc-sort": "По кое ÑвојÑтво да Ñе подреди барањето",
+ "smw-paramdesc-order": "РедоÑлед на подредување на барањето",
+ "smw-paramdesc-searchlabel": "ТекÑÑ‚ за продолжување на пребарувањето",
+ "smw-paramdesc-named_args": "Именувајте ги аргументите што му Ñе предаваат на шаблонот.",
+ "smw-paramdesc-export": "МожноÑÑ‚ за извоз",
+ "smw-paramdesc-prettyprint": "Дали иÑпиÑот да Ñе форматира Ñо дополнителни вовлекувања и нови редови",
+ "smw-paramdesc-source": "Ðлтернативен извор за барање",
+ "smw-paramdesc-jsonsyntax": "JSON-ÑинтакÑа што ќе Ñе кориÑти",
+ "smw-printername-feed": "RSS- и Ðтом-тековник",
+ "smw-paramdesc-feedtype": "Вид тековник",
+ "smw-paramdesc-feedtitle": "ТекÑÑ‚ како наÑлов на тековникот",
+ "smw-paramdesc-feeddescription": "ТекÑÑ‚ како Ð¾Ð¿Ð¸Ñ Ð½Ð° тековникот",
+ "smw-paramdesc-feedpagecontent": "Содржина на Ñтраницата што Ñе прикажува Ñо тековникот",
+ "smw-label-feed-description": "$2-тековник на $1",
+ "smw_iq_disabled": "Ðа ова вики Ñе оневозможени Ñемантички барања.",
+ "smw_iq_moreresults": "... понатамошни Ñтавки",
+ "smw_parseerror": "Дадената вредноÑÑ‚ не беше разбрана.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "„$1“ не може да Ñе кориÑти како име на Ñтраница во ова вики.",
+ "smw_noproperty": "„$1“ не може да Ñе кориÑти како име на ÑвојÑтво во ова вики.",
+ "smw_wrong_namespace": "Тука Ñе дозволени Ñамо Ñтраници во именÑкиот проÑтор „$1“.",
+ "smw_manytypes": "За ÑвојÑтвото е определен повеќе од еден тип.",
+ "smw_emptystring": "Ðе Ñе прифаќаат празни низи.",
+ "smw_notinenum": "„$1“ не е на ÑпиÑокот ($2) на [[Property:Allows value|допуштени вредноÑти]] за ÑвојÑтвото „$3“.",
+ "smw_noboolean": "„$1“ не претÑтавува Булова вредноÑÑ‚ (точно/неточно).",
+ "smw_true_words": "виÑтина,да,в,д,true,t,yes,y",
+ "smw_false_words": "лага,л,невиÑтина,н,не,false,f,no,n",
+ "smw_nofloat": "„$1“ не претÑтавува број.",
+ "smw_infinite": "Броевите Ñо толкава должина како „$1“ не Ñе поддржани.",
+ "smw_unitnotallowed": "„$1“ не Ñе Ñмета за важечка мерна единица за ова ÑвојÑтво.",
+ "smw_nounitsdeclared": "Ðема наведено мерни единици за ова ÑвојÑтво.",
+ "smw_novalues": "Ðема назначено вредноÑти.",
+ "smw_nodatetime": "Датумот „$1“ не е разбран.",
+ "smw_toomanyclosing": "Во барањето има премногу јавувања на „$1“.",
+ "smw_noclosingbrackets": "Во вашето барање беа иÑкориÑтени загради „<nowiki>[[</nowiki>“ на кои им недоÑтатуваат Ñоодветни затворачки загради „]]“.",
+ "smw_misplacedsymbol": "Знакот „$1“ е додаден на меÑто кајшто не е кориÑен",
+ "smw_unexpectedpart": "Делот „$1“ од барањето не беше разбран.\nИÑходот може да Ñе разликува од очекуваното.",
+ "smw_emptysubquery": "Едно од подбарањата нема важечки уÑлов.",
+ "smw_misplacedsubquery": "КориÑтено е подбарање на некое меÑто кадешто не Ñе дозволени подбарања.",
+ "smw_valuesubquery": "Подбарањата не Ñе поддржани за вредноÑтите на ÑвојÑтвото „$1“.",
+ "smw_badqueryatom": "ИзвеÑен дел „<nowiki>[[…]]</nowiki>“ од барањето не беше разбран.",
+ "smw_propvalueproblem": "ВредноÑта на ÑвојÑтвото „$1“ не е разбрана.",
+ "smw_noqueryfeature": "Ðекои функции од барањето не Ñе поддржани на ова вики, и затоа е иÑпуштен дел од барањето ($1).",
+ "smw_noconjunctions": "Сврзници во барања не Ñе поддржани во ова вики, и затоа е иÑпуштен дел од барањето ($1).",
+ "smw_nodisjunctions": "Во ова вики не Ñе поддржани диÑјункции во барањата, и затоа еден дел од барањето е отфрлен ($1).",
+ "smw_querytoolarge": "Следниве уÑлови на барањето <code>$1</code> не можеа да бидат земени предвид заради ограничувањата на големината или длабочината на барањата во ова вики.",
+ "smw_notemplategiven": "Ðаведете вредноÑÑ‚ за параметарот „template“ за да може да работи овој формат на барање.",
+ "smw_db_sparqlqueryproblem": "Ðе можев да добијам иÑход за барањето од базата SPARQL. Грешката може да е привремена или да Ñе должи на бубачка во програмот на базата.",
+ "smw_db_sparqlqueryincomplete": "Одговарањето на барањето иÑпадна претешко и беше прекинато. Може да Ñе изоÑтавени некои иÑходни Ñтавки. Ðко можете, обидете Ñе Ñо поедноÑтавно барање.",
+ "smw_type_header": "СвојÑтва од типот „$1“",
+ "smw_typearticlecount": "{{PLURAL:$1|Прикажано е $1 ÑвојÑтво кое го кориÑти|Прикажани Ñе $1 ÑвојÑтва кои го кориÑтат}} овој тип.",
+ "smw_attribute_header": "Страници кои го кориÑтат ÑвојÑтвото „$1“",
+ "smw_attributearticlecount": "{{PLURAL:$1|Прикажана е $1 Ñтраница која го кориÑти ова ÑвојÑтво|Прикажани Ñе $1 Ñтраници кои го кориÑтат}} ова ÑвојÑтво.",
+ "smw-propertylist-subproperty-header": "ПотÑвојÑтва",
+ "smw-propertylist-redirect-header": "ИÑтозначници",
+ "smw-propertylist-count": "{{PLURAL:$1|Прикажана е $1 единица|Прикажани Ñе $1 единици}}.",
+ "smw-propertylist-count-with-restricted-note": "{{PLURAL:$1|Прикажана е $1 поврзана единица|Прикажани Ñе $1 поврзани единици}}. (доÑтапни Ñе повеќе, но ограничувањето е „$2“).",
+ "smw-propertylist-count-more-available": "{{PLURAL:$1|Прикажана е $1 поврзана единица|Прикажани Ñе $1 поврзани единици}}. (доÑтапни Ñе повеќе).",
+ "specialpages-group-smw_group": "Семантички МедијаВики",
+ "exportrdf": "Извези Ñтраници во RDF",
+ "smw_exportrdf_docu": "Оваа Ñтраница ви овозможува да преземете податоци од Ñтраница во RDF формат.\nЗа да ги извезете Ñтраниците, внеÑете ги наÑловите во кутијата подолу (по еден наÑлов во Ñекој ред).",
+ "smw_exportrdf_recursive": "Рекурзивно извези ги Ñите поврзани Ñтраници.\nИмајте на ум дека иÑходот може да биде мошне голем!",
+ "smw_exportrdf_backlinks": "Извези ги и Ñтраниците кои укажуваат на извезените Ñтраници.\nСоздава RDF Ñо поддршка за прелиÑтување.",
+ "smw_exportrdf_lastdate": "Ðе извезувај Ñтраници кои Ñе немаат променето од назначениот датум наваму.",
+ "smw_exportrdf_submit": "Извези",
+ "uriresolver": "URI претворач",
+ "properties": "СвојÑтва",
+ "smw_properties_docu": "Во викито Ñе кориÑтат Ñледниве ÑвојÑтва.",
+ "smw_property_template": "$1 од типот $2 ($3 {{PLURAL:$3|употреба|употреби}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Сите ÑвојÑтва треба да Ñе опишани во Ñтраница!",
+ "smw_propertylackstype": "Ðема назначено тип за ова ÑвојÑтво (заÑега по оÑновно ќе Ñе кориÑти типот $1)",
+ "smw_propertyhardlyused": "Ова ÑвојÑтво речиÑи не Ñе кориÑти на викито!",
+ "smw-property-name-invalid": "СвојÑтвото $1 не може да Ñе кориÑти (има неважечко име).",
+ "smw-sp-property-searchform": "Прикажи ÑвојÑтва што Ñодржат:",
+ "smw-sp-property-searchform-inputinfo": "Филтрирањето разликува големи и мали букви, па затоа ќе ви Ñе прикажат Ñамо ÑвојÑтва што одговараат точно на внеÑеното.",
+ "smw-special-property-searchform-options": "МожноÑти",
+ "smw-special-wantedproperties-filter-label": "Филтер:",
+ "smw-special-wantedproperties-filter-none": "никаков",
+ "smw-special-wantedproperties-filter-unapproved": "Ðеодобрени",
+ "concepts": "Концепти",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts?uselang=mk Концептот] може да Ñе гледа како „динамична категорија“, Ñ‚.е. збир Ñтраници што не Ñе Ñоздадени рачно, туку преÑметани од Семантички МедијаВики како Ð¾Ð¿Ð¸Ñ Ð½Ð° дадено барање.",
+ "smw-special-concept-header": "СпиÑок на концепти",
+ "smw-special-concept-count": "Во ÑпиÑокот {{PLURAL:$1|е наведен Ñледниов концепт|Ñе наведени Ñледниве $1 концепти}}.",
+ "smw-special-concept-empty": "Ðе пронајдов ниеден концепт.",
+ "unusedproperties": "ÐеиÑкориÑтени ÑвојÑтва",
+ "smw-unusedproperties-docu": "Следниве ÑвојÑтва поÑтојат, но ниедна друга Ñтраница не ги кориÑти.",
+ "smw-unusedproperty-template": "$1 од типот $2",
+ "wantedproperties": "Потребни ÑвојÑтва",
+ "smw-wantedproperties-docu": "Следниве ÑвојÑтва Ñе кориÑтат во викито, но Ñè уште немаат Ñвоја опиÑна Ñтраница.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|употреба|употреби}})",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|употреба|употреби}})",
+ "smw_purge": "Превчитај",
+ "smw-purge-failed": "Превчитувањето не уÑпеа",
+ "types": "Типови",
+ "smw_types_docu": "СпиÑок ма [[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes раÑположиви податочни типови] Ñо Ñекој [https://www.semantic-mediawiki.org/wiki/Help:Datatype тип] кои претÑтавуваат единÑтвен збир атрибути за опишување на вредноÑта во Ð¾Ð´Ð½Ð¾Ñ Ð½Ð° оÑобеноÑти за Ñкладирање и приказ кои Ñе наÑледени од доделено ÑвојÑтво.",
+ "smw-special-types-no-such-type": "Ðазначениот податочен тип не поÑтои",
+ "smw-statistics": "Семантички ÑтатиÑтики",
+ "smw-statistics-property-instance": "{{PLURAL:$1|ВредноÑÑ‚ на ÑвојÑтво|ВредноÑти на ÑвојÑтва}} (вкупно)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|СвојÑтво|СвојÑтва}}]] (вкупно)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|СвојÑтво|СвојÑтва}} (вкупно)",
+ "smw-statistics-property-page": "{{PLURAL:$1|СвојÑтво|СвојÑтва}} (заведени Ñо Ñтраница)",
+ "smw-statistics-property-type": "{{PLURAL:$1|СвојÑтво|СвојÑтва}} (назначени на податочен тип)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Барање|Барања}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Барање|Барања}}]]",
+ "smw-statistics-query-size": "Големина на барањето",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Концепт|Концепти}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Коцепт|Коцепти}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Подобјект|Подобјекти}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Подобјект|Подобјекти}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Податочен тип|Податочни типови}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|ВредноÑÑ‚ на ÑвојÑтвото|ВредноÑти на ÑвојÑтвото}} ([[Special:ProcessingErrorList|{{PLURAL:$1|неÑоодветна прибелешка|неÑоодветни прибелешки}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|ВредноÑÑ‚ за ÑвојÑтво|ВредноÑти за ÑвојÑтва}} ({{PLURAL:$1|неÑоодветен запиÑ|неÑоодветни запиÑи}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|ЗаÑтарена единица (означена за отÑтранување)|ЗаÑтарени единици (означени за отÑтранување)}}",
+ "smw_uri_doc": "URI-претварачот применува [$1 W3C пронаоѓање на ознаки на httpRange-14].\nТој има за задача да внимава луѓето да не Ñе претворат во мрежни меÑта.",
+ "ask": "Семантичко пребарување",
+ "smw_ask_sortby": "Подреди по Ñтолб (незадолжително)",
+ "smw_ask_ascorder": "Ðагорен",
+ "smw_ask_descorder": "Ðадолен",
+ "smw-ask-order-rand": "Случајна",
+ "smw_ask_submit": "Пронајди",
+ "smw_ask_editquery": "Уреди барање",
+ "smw_add_sortcondition": "(Додај уÑлов за подредување)",
+ "smw-ask-sort-add-action": "Додај уÑлов за подредување",
+ "smw_ask_hidequery": "Скриј барање (компактен изглед)",
+ "smw_ask_help": "Помош Ñо поÑтавување барања",
+ "smw_ask_queryhead": "УÑлов",
+ "smw_ask_printhead": "Дополнителни податоци за приказ",
+ "smw_ask_printdesc": "(додавајте едно име на ÑвојÑтво по ред)",
+ "smw_ask_format_as": "Форматирај како:",
+ "smw_ask_defaultformat": "по оÑновно",
+ "smw_ask_otheroptions": "Други нагодувања",
+ "smw-ask-otheroptions-info": "Овој поднаÑлов Ñодржи можноÑти за менување на иÑпиÑот. ОпиÑите на параметрите ќе ги видите ако отидете Ñо глувчето врз нив.",
+ "smw-ask-otheroptions-collapsed-info": "СтиÑнете на иконата „+“ (плуÑ) за да ги погледате Ñите раÑположиви можноÑти",
+ "smw_ask_show_embed": "Прикажи вграден код",
+ "smw_ask_hide_embed": "Скриј вметнат код",
+ "smw_ask_embed_instr": "За да го вметнете ова барање во вики-Ñтраницата меѓу редови, употребете го кодот подолу.",
+ "smw-ask-delete": "ОтÑтрани",
+ "smw-ask-sorting": "Подредување",
+ "smw-ask-options": "ПоÑтавки",
+ "smw-ask-options-sort": "Подредувања",
+ "smw-ask-format-options": "Формат и можноÑти",
+ "smw-ask-parameters": "Параметри",
+ "smw-ask-search": "Пребарај",
+ "smw-ask-debug": "ИÑправки",
+ "smw-ask-no-cache": "Ðема меѓуÑклад",
+ "smw-ask-result": "ИÑход",
+ "smw-ask-empty": "ИÑчиÑти ги Ñите Ñтавки",
+ "smw-ask-format": "Формат",
+ "smw-ask-format-selection-help": "Подробен Ð¾Ð¿Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚Ðµ да прочитате на Ñтраницата за помош за $1.",
+ "searchbyproperty": "Пребарај по ÑвојÑтво",
+ "processingerrorlist": "СпиÑок на грешки при обработка",
+ "smw_sbv_docu": "Пребарување на Ñите Ñтраници кои имаат дадено ÑвојÑтво и вредноÑÑ‚.",
+ "smw_sbv_novalue": "ВнеÑете важечка вредноÑÑ‚ за ÑвојÑтвото, или пак погледнете ги Ñите вредноÑти на ÑвојÑтвото „$1“.",
+ "smw_sbv_displayresult": "СпиÑок на Ñите Ñтраници што го Ñодржат ÑвојÑтвото „$1“ Ñо вредноÑÑ‚ „$2“",
+ "smw_sbv_displayresultfuzzy": "СпиÑок на Ñите Ñтраници кои имаат ÑвојÑтво „$1“ Ñо вредноÑÑ‚ „$2“.\nБидејќи има Ñамо неколку иÑходни Ñтавки, прикажани Ñе и приближни вредноÑти.",
+ "smw_sbv_property": "СвојÑтво:",
+ "smw_sbv_value": "ВредноÑÑ‚:",
+ "smw_sbv_submit": "Пронајди",
+ "browse": "ПрелиÑтај вики",
+ "smw_browselink": "ПрелиÑтај ÑвојÑтва",
+ "smw_browse_article": "ВнеÑете го името на Ñтраницата од која би почнале да прелиÑтувате.",
+ "smw_browse_go": "Дај",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "прикажи ÑвојÑтва кои водат овде",
+ "smw_browse_hide_incoming": "Ñкриј ÑвојÑтва што водат овде",
+ "smw_browse_no_outgoing": "Оваа Ñтраница нема ÑвојÑтва.",
+ "smw_browse_no_incoming": "До оваа Ñтраница не водат никакви ÑвојÑтва.",
+ "smw_inverse_label_default": "$1 од",
+ "smw_inverse_label_property": "Обратен наÑлов на ÑвојÑтвото",
+ "pageproperty": "Пребарување ÑвојÑтва на Ñтраница",
+ "smw_pp_docu": "Пребарај ги Ñите вредноÑти на едно ÑвојÑтво на дадена Ñтраница.\nВнеÑете и Ñтраница и ÑвојÑтво.",
+ "smw_pp_from": "Од Ñтраница",
+ "smw_pp_type": "СвојÑтво",
+ "smw_pp_submit": "Ðајди Ñтавки",
+ "smw_result_prev": "Претходно",
+ "smw_result_next": "Следно",
+ "smw_result_results": "ИÑход",
+ "smw_result_noresults": "Ðема иÑход.",
+ "smwadmin": "ÐдминиÑтраторÑки функции за Семантички МедијаВики",
+ "smw-admin-setupsuccess": "Складишниот погон е уÑпешно поÑтавен.",
+ "smw_smwadmin_return": "Ðазад на $1",
+ "smw_smwadmin_updatestarted": "Започнат е нов Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð·Ð° обнова на Ñемантичките податоци.\nСите Ñкладирани податоци ќе бидат преработени или поправени, каде што е потребно.\nÐа оваа Ñлужбена Ñтраница можете да го Ñледите процеÑот на поднова.\n\nÐазад на $1.",
+ "smw_smwadmin_updatenotstarted": "Веќе е во тек едно подновување.\nÐема да Ñе Ñоздаде друго.\n\nÐазад на $1.",
+ "smw_smwadmin_updatestopped": "Сите поÑтоечки процеÑи на поднова Ñе Ñопрени.\n\nÐазад на $1.",
+ "smw_smwadmin_updatenotstopped": "За да го запрете текот на подновата, мора да го активирате кутивчето за избор кое укажува на тоа дека Ñте навиÑтина Ñигурни дека Ñакате да направите така.\n\nÐазад на $1.",
+ "smw-admin-docu": "Оваа Ñлужбена Ñтраница ви помага во текот на воÑпоÑтавката и подновата на <a href=\"https://www.semantic-mediawiki.org/?uselang=mk\">Семантички МедијаВики</a>.\nÐе заборавајте да направите резервни примероци од значајни податоци пред да вршите админиÑтративни поÑтапки.",
+ "smw-admin-db": "ВоÑпоÑтавка и поднова на базата на податоци",
+ "smw-admin-dbdocu": "Семантички МедијаВики бара додавање на извеÑни додатоци кон базата на податоци на МедијаВики за да Ñкладира Ñемантички податоци.\nФункцијата подолу ви гарантира дека вашата база на податоци е правилно поÑтавена.\nПромените извршени во овој чекор немаат влијание врз оÑтатокот од базата на МедијаВики, и леÑно можат да Ñе вратат по Ñтаро ако Ñе јави потреба.\nОваа функција на поÑтавката може да Ñе врши повеќе пати без тоа да причини било каква штета, но потребна е Ñамо еднаш при воÑпоÑтавка или подновување.",
+ "smw-admin-permissionswarn": "Ðко поÑтапката не уÑпее Ñо грешки при иÑполнувањето на SQL-наредбите, тогаш кориÑникот на базата на податоци што го употребува вашето вики (проверете ги вашите LocalSettings.php) нема доволно дозволи.\nДоделете му доволно дозволи на кориÑникот за да може да Ñоздава и брише табели, привремено внеÑете го најавниот корен (root) на вашата база на податоци во LocalSettings.php, или пак употребете ја Ñкриптата за одржување <code>setupStore.php</code> која може да ги кориÑти акердитивите за приÑтап од кориÑник на SysOp.",
+ "smw-admin-dbbutton": "Започни или поднови табели",
+ "smw-admin-announce": "Објавете го вашето вики",
+ "smw-admin-announce-text": "Ðко вашето вики е јавно, можете да го региÑтрирате на <a href=\"https://wikiapiary.com\">WikiApiary</a> — викито за Ñледење на викија.",
+ "smw-admin-deprecation-notice-title-notice": "ПретÑтојни промени",
+ "smw_smwadmin_datarefresh": "Поправка и поднова на податоци",
+ "smw_smwadmin_datarefreshdocu": "ПоÑтои можноÑÑ‚ за враќање на Ñите податоци на Семантички МедијаВики врз оÑнова на тековните Ñодржини на викито.\nОва е кориÑно за поправка на оштетени податоци или за обнова на податоците ако внатрешниот формат Ñе има променето како поÑледица на некаква надградба на програмÑката опрема.\nПодновувањето Ñе врши Ñтраница по Ñтраница, и ќе треба да помине некое време за да профункционира.\nПодолу е прикажано дали има поднова во тек, и тоа ви овозможува да започнувате или запирате подновувања (оÑвен ако таа можноÑÑ‚ не е иÑклучена од админиÑтраторот на мрежното меÑто).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Веќе е во тек една поднова.</strong>\nÐормално е подновата да биде бавна, бидејќи податоците Ñе обновуваат во мали делчиња Ñекојпат кога кориÑникот ќе отиде на викито.\nЗа да ја убрзате подновата, можете да ја повикате Ñкриптата за одржување на MediaWiki <code>runJobs.php</code> (кориÑтете ја можноÑта <code>--maxjobs 1000</code> за да го ограничите бројот на поднови во една партија).\nПроценет напредок на тековната поднова:",
+ "smw_smwadmin_datarefreshbutton": "Започни Ñо подновување на податоци",
+ "smw_smwadmin_datarefreshstop": "Започни го ова подновување",
+ "smw_smwadmin_datarefreshstopconfirm": "Да, {{GENDER:$1|Ñигурен|Ñигурна}} Ñум.",
+ "smw-admin-support": "Како да добиете поддршка",
+ "smw-admin-supportdocu": "Различни реÑурÑи кои можат да ви помогнат во Ñлучај на проблеми:",
+ "smw-admin-installfile": "Ðко наидете на проблеми при воÑпоÑтавката, најпрвин прочитајте ги напатÑтвијата во <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">податотеката INSTALL</a>.",
+ "smw-admin-smwhomepage": "ЦелоÑната кориÑничка документација за Семантички МедијаВики ќе ја најдете на <b><a href=\"http://semantic-mediawiki.org?uselang=mk\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Бубачките (грешките) можат да Ñе пријават на <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Ðко имате некои други прашања или предлози, приклучете Ñ Ñе на диÑкуÑијата на <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">форумот за кориÑници на Семантички МедијаВики</a>.",
+ "smw_adminlinks_datastructure": "Структура на податоците",
+ "smw_adminlinks_displayingdata": "Приказ на податоци",
+ "smw_adminlinks_inlinequerieshelp": "Помош Ñо вметнати барања",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|КориÑник|СиÑтем}}",
+ "smw-createproperty-isproperty": "Ова е ÑвојÑтво од видот $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Дозволената вредноÑÑ‚ за ова ÑвојÑтво е|Дозволените вредноÑти за ова ÑвојÑтво Ñе}}:",
+ "smw-paramdesc-category-delim": "Разделувачот",
+ "smw-paramdesc-category-template": "Шаблон Ñо кој ќе Ñе форматираат елементите",
+ "smw-paramdesc-category-userparam": "Параметар што ќе му даде на шаблонот",
+ "smw-info-par-message": "Порака за приказ.",
+ "smw-info-par-icon": "Икона за приказ на „инфо“ или „предупредување“.",
+ "prefs-smw": "Семантички МедијаВики",
+ "prefs-ask-options": "МожноÑти за Ñемантичко пребарување",
+ "smw-prefs-intro-text": "Долунаведените можноÑти Ñе доÑтапни преку [https://www.semantic-mediawiki.org/ Семантички МедијаВики] (или поврзаните додтоци) Ñо цел да овозможи поединечни прилагодувања на извеÑни функции. Повеќе информации ќе најдете на оваа [https://www.semantic-mediawiki.org/wiki/Help:User_preferences Ñтраница за помош].",
+ "smw-prefs-ask-options-tooltip-display": "Прикажи го текÑтот на параметарот како информативен отÑкочен текÑÑ‚",
+ "smw-ui-tooltip-title-property": "СвојÑтво",
+ "smw-ui-tooltip-title-quantity": "Претворање на единици",
+ "smw-ui-tooltip-title-info": "Информации",
+ "smw-ui-tooltip-title-service": "УÑлужни врÑки",
+ "smw-ui-tooltip-title-warning": "Грешка",
+ "smw-ui-tooltip-title-parameter": "Параметар",
+ "smw-ui-tooltip-title-event": "ÐаÑтан",
+ "smw-ui-tooltip-title-note": "Белешка",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw_unknowntype": "Ова ÑвојÑтво е од неважечки тип",
+ "smw-concept-cache-text": "Концептот има вкупно {{PLURAL:$1|една Ñтраница|$1 Ñтраници}}, а подновен е на $3 во $2 ч.",
+ "smw_concept_header": "Страници на концептот „$1“",
+ "smw_conceptarticlecount": "{{PLURAL:$1|Прикажана е $1 Ñтраница која му припаѓа|Прикажани Ñе $1 Ñтраници кои му припаѓаат}} на тој концепт.",
+ "smw-qp-empty-data": "Ðе можам да ги прикажам побараните податоци поради извеÑни недоволни критеруми за делот.",
+ "right-smw-admin": "ПриÑтап до админиÑтративни задачи (Семантички МедијаВики)",
+ "group-smwadministrator": "ÐдминиÑтратори (Семантички МедијаВики)",
+ "group-smwadministrator-member": "{{GENDER:$1|админиÑтратор (Семантички МедијаВики)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:ÐдминиÑтратори (Семантички МедијаВики)",
+ "group-smwcurator": "Згрижувачи (Семантички МедијаВики)",
+ "group-smwcurator-member": "{{GENDER:$1|згрижувач (Семантички МедијаВики)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Згрижувачи (Семантички МедијаВики)",
+ "action-smw-admin": "приÑтап до админиÑтративните задачи на Семантички МедијаВики",
+ "smw-property-predefined-default": "„$1“ е предодредено ÑвојÑтво.",
+ "smw-property-predefined-common": "Ова ÑвојÑтво е однапред програмÑки зададено (наречено и [https://www.semantic-mediawiki.org/wiki/Help:Special_properties поÑебно ÑвојÑтво]) и Ñо него има дополнителни админиÑтративни привилегии, но може да Ñе кориÑти како Ñекое друго [https://www.semantic-mediawiki.org/wiki/Property кориÑнички-зададено ÑвојÑтво].",
+ "smw-property-predefined-ask": "„$1“ е предодредено ÑвојÑтво кое претÑтавува метаинформации (во облик на [https://www.semantic-mediawiki.org/wiki/Subobject subobject]) за одделни барања. Добиено е од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички МедијаВики].",
+ "smw-property-predefined-asksi": "„$1“ е предодредено ÑвојÑтво што го Ñобира бројот на уÑлови кои Ñе кориÑтат во барање. Добиено е од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички МедијаВики].",
+ "smw-property-predefined-askde": "„$1“ е програмÑки зададено ÑвојÑтво што укажува на ÑодржајноÑта (бројчена вредноÑÑ‚ преÑметана Ñпоред вгнездувањето на подбарањата и разложени ÑвојÑтвени ланци) на едно барање додека важи ограничување на неговото извршување наметнато од поÑтавката. Добиено е од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички МедијаВики].",
+ "smw-sp-properties-docu": "Ðа оваа Ñтраница Ñе наведени [https://www.semantic-mediawiki.org/wiki/Property?uselang=mk ÑвојÑтвата] и бројот на употреби доÑтапен на ова вики. За најнови ÑтатиÑтики за бројот на употреби Ñе препорачува редовно пуштање на Ñкриптата за [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics ÑтатиÑтики за ÑвојÑтва]. Друг преглед можете да добиете на Ñлужбените Ñтраници [[Special:UnusedProperties|неиÑкориÑтени ÑвојÑтва]] и [[Special:WantedProperties|потребни ÑвојÑтва]].",
+ "smw-sp-properties-cache-info": "Ðаведените податоци Ñе преземени од [https://www.semantic-mediawiki.org/wiki/Caching?uselang=mk меѓуÑкладот], a поÑледен пат изменети на $1.",
+ "smw-sp-properties-header-label": "СпиÑок на ÑвојÑтва",
+ "smw-admin-settings-docu": "Прикажува ÑпиÑок на Ñите Ñтандардни и меÑтоприлагодени и преведени поÑтавки што Ñе однеÑуваат на дадената околина во Семантички МедијВики. Повеќе за поединечните поÑтавки ќе најдете на Ñтраницата за [https://www.semantic-mediawiki.org/wiki/Help:Configuration помош Ñо поÑтавки].",
+ "smw-sp-admin-settings-button": "Создај ÑпиÑок на поÑтавки",
+ "smw-admin-idlookup-title": "Проверка на објектна назнака",
+ "smw-admin-idlookup-docu": "Прикажува подробноÑти за внатрешата објектна назнака што претÑтавува поединечни единици (викиÑтраница, подобјект и тн.) во Семантички МедијаВики.",
+ "smw-admin-iddispose-title": "ОтÑтранување на објектна назнака",
+ "smw-admin-objectid": "Објектна назнака:",
+ "smw-livepreview-loading": "Вчитувам...",
+ "smw-sp-searchbyproperty-description": "Страницава дава проÑÑ‚ [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces прелиÑтувачки поÑредник] за пронаоѓање на единици опишани Ñо ÑвојÑтво и именувана вредноÑÑ‚. Ðа раÑполагање ви Ñе и други пребарувачки поÑредници: [[Special:PageProperty|пребарување на ÑвојÑтва на Ñтраници]] и [[Special:Ask|Ñрочувачот на барања]].",
+ "smw-sp-searchbyproperty-resultlist-header": "СпиÑок на Ñтавки од иÑходот",
+ "smw-sp-searchbyproperty-nonvaluequery": "СпиÑок на вредноÑти Ñо ÑвојÑтво „$1“.",
+ "smw-sp-searchbyproperty-valuequery": "СпиÑок на вредноÑти Ñо ÑвојÑтво „$1“ Ñо вредноÑта „$2“.",
+ "smw-datavalue-number-textnotallowed": "„$1“ не може да Ñе додели на изјавен бројчен тип Ñо вредноÑÑ‚ $2.",
+ "smw-editpage-annotation-enabled": "Оваа Ñтраница поддржува Ñемантички прибелешки во текÑÑ‚ (на пр.<nowiki>„[[Is specified as::World Heritage Site]]“</nowiki>) за изградба на Ñтруктурирани пребарливи Ñодржини од Семантички МедијаВики. Поопфатен Ð¾Ð¿Ð¸Ñ Ð·Ð° употребата на прибелешки или раÑчленувачката функција #ask , погледајте ја помошната Ñтраница [https://www.semantic-mediawiki.org/wiki/Help:Getting_started Како да почнете], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation Прибелешки во текÑÑ‚] или [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries Барање во редови].",
+ "smw-editpage-annotation-disabled": "Ðа оваа Ñтраница не Ñе овозможени Ñемантички прибелешки во текÑÑ‚ поради ограничувањата на именÑкиот проÑтор. Ðко Ñакате да дознаете како да ги овозможите за овој именÑки проÑтор, прочитајте ја помошната Ñтраница [https://www.semantic-mediawiki.org/wiki/Help:Configuration ПоÑтавување].",
+ "smw-editpage-property-annotation-enabled": "Ова ÑвојÑтво може да Ñе дополнува кориÑтејќи Ñемантички прибелешки за укажување на податочниот тип (на пр.<nowiki>„[[Has type::Page]]“</nowiki>) или други поддржни изјави (како на пр.<nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). За да дознаете како да ја збогатите Ñтраницата, погледајте ја помошната Ñтраница [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration Изјава на ÑвојÑтво] или [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes ÑпиÑокот на раÑположиви податочни типови].",
+ "smw-editpage-property-annotation-disabled": "Ова ÑвојÑтво не може да Ñе дополни Ñо прибелешка за податочен тип (на пр.<nowiki>„[[Has type::Page]]“</nowiki>) бидејќи иÑтиот му е веќе е предзададен (повеќе информации на помошната Ñтраница [https://www.semantic-mediawiki.org/wiki/Help:Special_properties ПоÑебни ÑвојÑтва]).",
+ "smw-editpage-concept-annotation-enabled": "Овој концепт може да Ñе надополни кориÑтејќи ја раÑчленувачката функција #concept. За да дознаете како да кориÑтите #concept, погледајте ја помошната Ñтраница [https://www.semantic-mediawiki.org/wiki/Help:Concepts Концепти].",
+ "log-name-smw": "Дневник на Семантички МедијаВики",
+ "log-description-smw": "ÐктивноÑти за [https://www.semantic-mediawiki.org/wiki/Help:Logging овозможени типови на наÑтани] пријавени од Семантички МедијаВики и неговите ÑоÑтавни делови.",
+ "smw-datavalue-import-missing-type": "Ðе пронајдов типÑка определба за „$1“ во [[MediaWiki:Smw import $2|увозот на $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Увоз на $1]]",
+ "smw-property-predefined-impo": "„$1“ е предодредено ÑвојÑтво кое опишува поврзаноÑÑ‚ Ñо [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary увезен лекÑикон] и е преземено од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички МедијаВики].",
+ "smw-property-predefined-type": "„$1“ е предодредено ÑвојÑтво што го опишува [[Special:Types|податочниот тип]] на едно ÑвојÑтво и е преземено од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички Медијавики].",
+ "smw-property-predefined-sobj": "„$1“ е предодредено ÑвојÑтво што претÑтавува [https://www.semantic-mediawiki.org/wiki/Help:Container пополнувачки] конÑтрукт кој му овозможува да наÑобира задавања „ÑвојÑтво-вредноÑт“ Ñлично како на нормална викиÑтраница и е преземено од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички Медијавики]",
+ "smw-property-predefined-errp": "„$1“ е предодредено ÑвојÑтво за Ñледење на грешките во внеÑувањето кај прибелешките Ñо неправилни вредноÑти, веројатно предизвикани од ограничувањата на [[Property:Allows value|допуштени вредноÑти]] и е преземено од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички Медијавики].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value „$1“] е предодредено ÑвојÑтво што може да определи ÑпиÑок на допуштени вредноÑти за ограничување на вредноÑните доделувања за дадено ÑвојÑтво и е преземено од [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантички Медијавики].",
+ "smw-datavalue-restricted-use": "Податочната вредноÑÑ‚ „$1“ е означена за ограничена употреба.",
+ "smw-datavalue-invalid-number": "Ðе можам да го протолкувам „$1“ како број.",
+ "smw-query-condition-circular": "Во „$1“ е утврден можен кружен уÑлов.",
+ "smw-types-list": "СпиÑок на податочни типови",
+ "smw-types-default": "„$1“ е вграден податочен тип.",
+ "smw-types-help": "Повеќе информации и примери ќе најдете на [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 помошната Ñтраница].",
+ "smw-type-anu": "„$1“ е варијанта на податочниот тип [[Special:Types/URL|URL]] и претежно Ñе кориÑти за извозната изјава ''owl:AnnotationProperty''.",
+ "smw-type-boo": "„$1“ е примитивен податочен тип што опишува вредноÑÑ‚ точно/неточно.",
+ "smw-type-cod": "„$1“ е варијанта на податочниот тип [[Special:Types/Text|текÑÑ‚]] што Ñе корити во технички текÑтови Ñо произволна големина, како Ñтраници Ñо програмÑки кодови.",
+ "smw-type-geo": "„$1“ е податочен тип што опишува географÑка меÑтоположба и бара [https://www.semantic-mediawiki.org/wiki/Semantic_Maps Семантички карти].",
+ "smw-type-tel": "„$1“ е поÑебен податочен тип што го опишува меѓународните телефонÑки броеви Ñпоред RFC 3966.",
+ "smw-type-txt": "„$1“ е примитивен податочен тип што опишува низи Ñо произволна големина.",
+ "smw-type-dat": "„$1“ е податочен тип што претÑтавува моменти во времето во еднообразен формат.",
+ "smw-type-tab-properties": "СвојÑтва",
+ "smw-property-predefined-errc": "„$1“ е предодредено ÑвојÑтво кое претÑтавува [https://www.semantic-mediawiki.org/wiki/Help:Container пополнувач] за грешки што Ñе јавуваат во врÑка Ñо неÑоодветни вредноÑни прибелешки или неправилна обработка на вноÑот.",
+ "smw-property-predefined-errt": "„$1“ е предодредено ÑвојÑтво што Ñодржи текÑтуален Ð¾Ð¿Ð¸Ñ Ð½Ð° грешка.",
+ "smw-subobject-parser-invalid-naming-scheme": "КориÑнички-зададен подобјект кориÑти неважечка шема за именување. ЗапиÑот Ñо точки во првите пет знаци ($1) е резервиран за иÑклучива употреба на додатоците.",
+ "smw-datavalue-record-invalid-property-declaration": "Определбата на запиÑот го Ñодржи ÑвојÑтвото „$1“, кое Ñамото е зададено како тип на Ð·Ð°Ð¿Ð¸Ñ Ð¸ не Ñе допушта.",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунди}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунди}}",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-tab-specification": "... повеќе"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ml.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ml.json
new file mode 100644
index 00000000..b3eb53bc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ml.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Praveenp",
+ "Shijualex"
+ ]
+ },
+ "smw_finallistconjunct": "ഉം",
+ "smw_factbox_head": "$1നെ à´•àµà´±à´¿à´šàµà´šàµà´³àµà´³ സതàµà´¯à´™àµà´™àµ¾",
+ "smw_iq_moreresults": "… കൂടàµà´¤àµ½ ഫലങàµà´™àµ¾",
+ "smw_parseerror": "താങàµà´•àµ¾ ചേർതàµà´¤ മൂലàµà´¯à´‚ മനസàµà´¸à´¿à´²à´¾à´•àµà´•à´¾àµ» സാധിചàµà´šà´¿à´²àµà´².",
+ "smw_true_words": "ശരി,ശരി,അതെ,അതെ",
+ "smw_false_words": "തെറàµà´±àµ,തെറàµà´±àµ,à´…à´²àµà´²,à´…à´²àµà´²",
+ "smw_nofloat": "“$1†ഒരൠസംഖàµà´¯à´¯à´²àµà´².",
+ "smw_infinite": "“$1†എനàµà´¨ സംഖàµà´¯à´¯àµà´Ÿàµ†à´¯à´¤àµà´° വലിപàµà´ªà´®àµà´³àµà´³ സംഖàµà´¯à´•àµ¾ പിനàµà´¤àµà´£à´¯àµà´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´².",
+ "smw_misplacedsymbol": "“$1†എനàµà´¨ à´šà´¿à´¹àµà´¨à´‚ അതൠഉപയോഗികàµà´•à´¾àµ» പാടിലàµà´²à´¾à´¤àµà´¤ ഒരൠസàµà´¥à´²à´¤àµà´¤àµ ഉപയോഗിചàµà´šà´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ.",
+ "exportrdf": "RDFലേകàµà´•àµ താളàµà´•àµ¾ à´Žà´•àµà´¸àµâ€Œà´ªàµ‹àµ¼à´Ÿàµà´Ÿàµ ചെയàµà´¯àµà´•",
+ "smw_wantedproperty_template": "$1 ({{PLURAL:$2|à´’à´°àµà´ªà´¯àµ‹à´—à´‚|$2 ഉപയോഗങàµà´™àµ¾}})",
+ "types": "തരങàµà´™àµ¾",
+ "smw_ask_ascorder": "ആരോഹണം",
+ "smw_ask_descorder": "അവരോഹണം",
+ "smw_ask_submit": "ഫലങàµà´™àµ¾ കാണàµà´•",
+ "smw_sbv_value": "മൂലàµà´¯à´‚:",
+ "smw_sbv_submit": "ഫലങàµà´™àµ¾ കാണàµà´•",
+ "browse": "വികàµà´•à´¿à´¯à´¿àµ½ പരതàµà´•",
+ "smw_browse_go": "പോകൂ",
+ "smw_pp_from": "à´ˆ താളിൽ നിനàµà´¨àµ",
+ "smw_pp_submit": "ഫലങàµà´™àµ¾ കാണàµà´•",
+ "smw_result_prev": "à´®àµà´®àµà´ªà´¤àµà´¤àµ†",
+ "smw_result_next": "à´…à´Ÿàµà´¤àµà´¤à´¤àµ",
+ "smw_result_results": "ഫലങàµà´™àµ¾",
+ "smw_result_noresults": "à´•àµà´·à´®à´¿à´•àµà´•àµà´•, ഫലങàµà´™àµ¾ à´’à´¨àµà´¨àµà´®à´¿à´²àµà´²",
+ "smw-livepreview-loading": "ശേഖരികàµà´•àµà´¨àµà´¨àµ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mn.json
new file mode 100644
index 00000000..086af39d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mn.json
@@ -0,0 +1,16 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chinneeb",
+ "Wisdom",
+ "Munkhzaya.E"
+ ]
+ },
+ "smw-paramdesc-table-transpose": "table -ын толгойг боÑоогоор, үр дүнг Ñ…ÑвтÑÑгÑÑÑ€",
+ "smw_browse_go": "Явах",
+ "smw_smwadmin_datarefreshstopconfirm": "Тийм, би {{GENDER:$1|итгÑлтÑй байна}}.",
+ "smw-ui-tooltip-title-quantity": "ÐÑгжийн шилжүүлÑг",
+ "smw-livepreview-loading": "Уншиж байна...",
+ "smw-limitreport-intext-parsertime-value": "$1 Ñекунд",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 Ñекунд"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mr.json
new file mode 100644
index 00000000..3d617992
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mr.json
@@ -0,0 +1,106 @@
+{
+ "@metadata": {
+ "authors": [
+ "Htt",
+ "Kaustubh",
+ "V.narsikar",
+ "ì•„ë¼"
+ ]
+ },
+ "smw_viewasrdf": "RDF फीड",
+ "smw_finallistconjunct": ", व",
+ "smw_factbox_head": "$1 चà¥à¤¯à¤¾ फॅकà¥à¤Ÿà¥à¤¸",
+ "smw_isspecprop": "हा गà¥à¤£à¤§à¤°à¥à¤® या विकिवरील विशेष गà¥à¤£à¤§à¤°à¥à¤® आहे.",
+ "smw_baduri": "माफ करा, “$1†अरà¥à¤œà¤¾à¤šà¥‡ URI वापरणà¥à¤¯à¤¾à¤¸ परवानगी नाही.",
+ "smw_iq_disabled": "माफ करा. या विकिवर सिमà¤à¤Ÿà¤¿à¤• पॄचà¥à¤›à¤¾ करणà¥à¤¯à¤¾à¤¸ बंदी आहे.",
+ "smw_iq_moreresults": "… पà¥à¤¢à¤šà¥‡ निकाल",
+ "smw_parseerror": "दिलेली किंमत समजलेली नाही.",
+ "smw_notitle": "या विकिवर “$1†हे पानाचà¥à¤¯à¤¾ शीरà¥à¤·à¤•à¤¾à¤¤ वापरता येत नाही.",
+ "smw_manytypes": "गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤²à¤¾ à¤à¤•à¤¾à¤ªà¥‡à¤•à¥à¤·à¤¾ जासà¥à¤¤ पà¥à¤°à¤•à¤¾à¤° दिलेले आहेत.",
+ "smw_emptystring": "रिकामà¥à¤¯à¤¾ किंमती वापरता येत नाहीत.",
+ "smw_notinenum": "या गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥à¤¯à¤¾ योगà¥à¤¯ किंमतींचà¥à¤¯à¤¾ ($2) यादीत “$1†नाही आहे.",
+ "smw_noboolean": "“$1†ही योगà¥à¤¯ बà¥à¤²à¤¿à¤¯à¤¨ (बरोबर/चूक) किंमत नाही.",
+ "smw_true_words": "बरोबर,t,हो,y",
+ "smw_false_words": "चूक,f,नाही,n",
+ "smw_nofloat": "“$1†ही संखà¥à¤¯à¤¾ नाही.",
+ "smw_infinite": "{{SITENAME}} वर “$1†à¤à¤µà¤¢à¥à¤¯à¤¾ मोठà¥à¤¯à¤¾ संखà¥à¤¯à¤¾ वापरता येत नाहीत.",
+ "smw_nodatetime": "“$1†हा दिनांक समजला नाही.",
+ "smw_toomanyclosing": "या पृचà¥à¤›à¥‡à¤®à¤§à¥à¤¯à¥‡ “$1†खूप ठिकाणी आलेले आहे.",
+ "smw_noclosingbrackets": "तà¥à¤®à¤šà¥à¤¯à¤¾ पृचà¥à¤›à¥‡à¤¤ कà¥à¤ à¥‡à¤¤à¤°à¥€ वापरलेले “<nowiki>[[</nowiki>†हे योगà¥à¤¯ अशा जà¥à¤³à¤£à¤¾à¤°à¥â€à¤¯à¤¾ “]]†ने बंद केलेले नाही.",
+ "smw_misplacedsymbol": "“$1†चिनà¥à¤¹ जिथे उपयोगी नाही अशा ठिकाणी वापरलेले आहे.",
+ "smw_unexpectedpart": "पृचà¥à¤›à¥‡à¤šà¤¾ “$1†हा भाग समजलेला नाही.\nनिकाल योगà¥à¤¯ असतीलच याची खातà¥à¤°à¥€ नाही.",
+ "smw_emptysubquery": "कà¥à¤ à¤²à¥à¤¯à¤¾à¤¤à¤°à¥€ उपपृचà¥à¤›à¥‡à¤®à¤§à¥à¤¯à¥‡ योगà¥à¤¯ कंडिशन दिलेली नाही.",
+ "smw_misplacedsubquery": "à¤à¤• उपपृचà¥à¤›à¤¾ अशा ठिकाणी दिलेली आहे जिथे उपपृचà¥à¤›à¤¾ वापरायला परवानगी नाही.",
+ "smw_valuesubquery": "“$1†गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥à¤¯à¤¾ किंमतींसाठी उपपृचà¥à¤›à¤¾ वापरता येत नाहीत.",
+ "smw_badqueryatom": "पृचà¥à¤›à¥‡à¤šà¤¾ काही भाग “<nowiki>[[…]]</nowiki>†समजलेला नाही.",
+ "smw_propvalueproblem": "“$1†गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥€ किंमत समजलेली नाही.",
+ "smw_nodisjunctions": "पृचà¥à¤›à¥‡ मधà¥à¤¯à¥‡ तà¥à¤•à¤¡à¥‡ या विकिवर चालत नाहीत, तà¥à¤¯à¤¾à¤®à¥à¤³à¥‡ पृचà¥à¤›à¥‡à¤šà¤¾ तà¥à¤•à¤¡à¤¾ टाकून दिला ($1).",
+ "smw_querytoolarge": "पृचà¥à¤›à¥‡à¤šà¤¾ आकार अथवा खोलीवर असणारà¥â€à¤¯à¤¾ या विकिचà¥à¤¯à¤¾ मरà¥à¤¯à¤¾à¤¦à¤¾à¤‚मà¥à¤³à¥‡ खालील पृचà¥à¤›à¤¾ अटी लकà¥à¤·à¤¾à¤¤ घेता आलेलà¥à¤¯à¤¾ नाहीत: $1.",
+ "smw_type_header": "“$1†पà¥à¤°à¤•à¤¾à¤°à¤šà¥‡ गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_typearticlecount": "हा पà¥à¤°à¤•à¤¾à¤° {{PLURAL:$1|वापरणारा|वापरणारे}} $1 गà¥à¤£à¤§à¤°à¥à¤® दाखवित आहे.",
+ "smw_attribute_header": "“$1†गà¥à¤£à¤§à¤°à¥à¤® वापरणारी पृषà¥à¤ à¥‡",
+ "smw_attributearticlecount": "हा गà¥à¤£à¤§à¤°à¥à¤® वापरणारी $1 पाने दाखवित आहे..",
+ "specialpages-group-smw_group": "सिमà¤à¤Ÿà¤¿à¤• मिडियाविकि",
+ "exportrdf": "RDF कडे पाने निरà¥à¤¯à¤¾à¤¤ करा",
+ "smw_exportrdf_docu": "हे पान तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ à¤à¤–ादà¥à¤¯à¤¾ पानावरील मजकूर RDF मधà¥à¤¯à¥‡ घेणà¥à¤¯à¤¾à¤¸ मदत करते.\nपाने निरà¥à¤¯à¤¾à¤¤ करणà¥à¤¯à¤¾à¤•à¤°à¥€à¤¤à¤¾, खाली à¤à¤•à¤¾ ओळीत à¤à¤• यापà¥à¤°à¤®à¤¾à¤£à¥‡ पानांची शीरà¥à¤·à¤•à¥‡ लिहा.",
+ "smw_exportrdf_recursive": "सरà¥à¤µ जà¥à¤³à¤²à¥‡à¤²à¥€ पाने आपोआप निरà¥à¤¯à¤¾à¤¤ करा.\nकृपया नोंद घà¥à¤¯à¤¾ की निकाल खूप मोठा असू शकतो!",
+ "smw_exportrdf_backlinks": "निरà¥à¤¯à¤¾à¤¤ केलेलà¥à¤¯à¤¾ पानांचे संदरà¥à¤­ देणारी पाने सà¥à¤¦à¥à¤§à¤¾ निरà¥à¤¯à¤¾à¤¤ करा.\nबà¥à¤°à¤¾à¤‰à¤œà¥‡à¤¬à¤² RDF तयार होईल.",
+ "smw_exportrdf_lastdate": "दिलेलà¥à¤¯à¤¾ वेळेनंतर जà¥à¤¯à¤¾ पानांमधà¥à¤¯à¥‡ बदल à¤à¤¾à¤²à¥‡à¤²à¥‡ नाहीत अशी पाने निरà¥à¤¯à¤¾à¤¤ करू नका.",
+ "uriresolver": "URI रिà¤à¥‰à¤²à¥à¤µà¥à¤¹à¤°",
+ "properties": "गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_properties_docu": "या विकिवर खालील गà¥à¤£à¤§à¤°à¥à¤® वापरलेले आहेत.",
+ "smw_property_template": "$2 पà¥à¤°à¤•à¤¾à¤°à¤šà¥€ $1 ($3)",
+ "smw_propertylackspage": "à¤à¤•à¤¾ पानावर सरà¥à¤µ गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤‚ची माहिती असणे आवशà¥à¤¯à¤• आहे!",
+ "smw_propertylackstype": "या गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¤¾ पà¥à¤°à¤•à¤¾à¤° दिलेला नाही (आतà¥à¤¤à¤¾à¤ªà¥à¤°à¤¤à¤¾ $1 हा पà¥à¤°à¤•à¤¾à¤° गृहीत धरत आहे).",
+ "smw_propertyhardlyused": "या विकिवर हा गà¥à¤£à¤§à¤°à¥à¤® कà¥à¤µà¤šà¤¿à¤¤à¤š वापरला जातो!",
+ "unusedproperties": "न वापरलेले गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_unusedproperties_docu": "खालील गà¥à¤£à¤§à¤°à¥à¤® कà¥à¤ à¤²à¥à¤¯à¤¾à¤¹à¥€ पानावर वापरणà¥à¤¯à¤¾à¤¤ आलेले नाहीत.",
+ "smw_unusedproperty_template": "$2 पà¥à¤°à¤•à¤¾à¤°à¤šà¥‡ $1",
+ "wantedproperties": "पाहिजे असलेले गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_wantedproperties_docu": "या विकिवर वापरणà¥à¤¯à¤¾à¤¤ आलेलà¥à¤¯à¤¾ खालील गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤‚साठी माहिती पृषà¥à¤ à¥‡ असà¥à¤¤à¤¿à¤¤à¥à¤µà¤¾à¤¤ नाहीत.",
+ "smw_wantedproperty_template": "$1 ($2 वापरतो)",
+ "smw_purge": "ताजेतवाने करा",
+ "types": "पà¥à¤°à¤•à¤¾à¤°",
+ "smw_types_docu": "खाली गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤¶à¥€ जोडता येणारà¥â€à¤¯à¤¾ सरà¥à¤µ डाटा पà¥à¤°à¤•à¤¾à¤°à¤¾à¤‚ची यादी आहे.\nपà¥à¤°à¤¤à¥à¤¯à¥‡à¤• डाटा पà¥à¤°à¤•à¤¾à¤°à¤¾à¤šà¥‡ à¤à¤• पाने आहे जिथे जासà¥à¤¤à¥€à¤šà¥€ माहिती देता येईल.",
+
+
+ "smw_uri_doc": "URI रिà¤à¥‰à¤²à¥à¤µà¤° मधà¥à¤¯à¥‡ à¤à¤• [$1 W3C टॅग आहे जो httpRange-14 शोधणà¥à¤¯à¤¾à¤¸ मदत करतो].\nहा मनà¥à¤·à¥à¤¯à¤ªà¥à¤°à¤¾à¤£à¥€ संकेतसà¥à¤¥à¤³à¤¾à¤®à¤§à¥à¤¯à¥‡ बदलणार नाही याची काळजी घेतो.",
+ "ask": "सिमॅंटिक शोध",
+ "smw_ask_sortby": "रकानà¥à¤¯à¤¾ पà¥à¤°à¤®à¤¾à¤£à¥‡ लावा (वैकलà¥à¤ªà¤¿à¤•)",
+ "smw_ask_ascorder": "चढतà¥à¤¯à¤¾ शà¥à¤°à¥‡à¤£à¥€à¤¨à¥‡",
+ "smw_ask_descorder": "उतरतà¥à¤¯à¤¾ शà¥à¤°à¥‡à¤£à¥€à¤¨à¥‡",
+ "smw_ask_submit": "निकाल शोधा",
+ "smw_ask_editquery": "पृचà¥à¤›à¤¾ संपादा",
+ "smw_add_sortcondition": "[निवडणà¥à¤¯à¤¾à¤šà¥€ अट जोडा]",
+ "smw_ask_hidequery": "पृचà¥à¤›à¤¾ लपवा",
+ "smw_ask_help": "पृचà¥à¤›à¤¾ करणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ मदत",
+ "smw_ask_queryhead": "पॄचà¥à¤›à¤¾",
+ "smw_ask_printhead": "जासà¥à¤¤à¥€à¤šà¥à¤¯à¤¾ छापील पà¥à¤°à¤¤à¥€ (वैकलà¥à¤ªà¤¿à¤•)",
+ "smw_ask_show_embed": "यात टाकलेला संकेत दाखवा",
+ "searchbyproperty": "गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤ªà¥à¤°à¤®à¤¾à¤£à¥‡ शोधा",
+ "smw_sbv_docu": "दिलेला गà¥à¤£à¤§à¤°à¥à¤® व किंमत असणारी सरà¥à¤µ पाने शोधा.",
+ "smw_sbv_novalue": "कृपया या गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥€ à¤à¤• योगà¥à¤¯ किंमत दà¥à¤¯à¤¾, किंवा “$1†साठीचà¥à¤¯à¤¾ सरà¥à¤µ किंमती पहा.",
+ "smw_sbv_displayresult": "गà¥à¤£à¤§à¤°à¥à¤® “$1†व तà¥à¤¯à¤¾à¤šà¥€ किंमत “$2†असणारà¥â€ सरà¥à¤µ पानांची यादी",
+ "smw_sbv_property": "गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_sbv_value": "किंमत:",
+ "smw_sbv_submit": "निकाल शोधा",
+ "browse": "विकि नà¥à¤¯à¤¾à¤¹à¤¾à¤³à¤¾",
+ "smw_browse_article": "विकि जà¥à¤¯à¤¾ पानापासून नà¥à¤¯à¤¾à¤¹à¤¾à¤³à¤¾à¤¯à¤šà¤¾ तà¥à¤¯à¤¾ पानाचे नाव लिहा",
+ "smw_browse_go": "चला",
+ "pageproperty": "पान गà¥à¤£à¤§à¤°à¥à¤® शोध",
+ "smw_pp_docu": "दिलेलà¥à¤¯à¤¾ पानावरील à¤à¤–ादà¥à¤¯à¤¾ गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥à¤¯à¤¾ सरà¥à¤µ किंमतींचा शोध घà¥à¤¯à¤¾.\nकृपया पान व गà¥à¤£à¤§à¤°à¥à¤® दोनà¥à¤¹à¥€ दà¥à¤¯à¤¾.",
+ "smw_pp_from": "पानावरून",
+ "smw_pp_type": "गà¥à¤£à¤§à¤°à¥à¤®",
+ "smw_pp_submit": "निकाल शोधा",
+ "smw_result_prev": "मागील",
+ "smw_result_next": "पà¥à¤¢à¥€à¤²",
+ "smw_result_results": "निकाल",
+ "smw_result_noresults": "माफ करा, निकाल नाहीत.",
+ "smw-createproperty-isproperty": "तो $1 पà¥à¤°à¤•à¤¾à¤°à¤šà¤¾ गà¥à¤£à¤§à¤°à¥à¤® आहे.",
+ "smw-createproperty-allowedvals": "या गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¥à¤¯à¤¾ योगà¥à¤¯ किंमती पà¥à¤¢à¥€à¤²à¤ªà¥à¤°à¤®à¤¾à¤£à¥‡ आहेत:",
+ "prefs-smw": "सिमॅंटिक मिडियाविकि",
+ "smw-prefs-intro-text": "निवडक कà¥à¤°à¤¿à¤¯à¤¾à¤‚वर वैयकà¥à¤¤à¤¿à¤• अनà¥à¤•à¥à¤²à¤¨ सकà¥à¤·à¤® करणà¥à¤¯à¤¾à¤¸ [https://www.semantic-mediawiki.org/ सिमà¤à¤Ÿà¤¿à¤• मिडियाविकि] (किंवा संबंधित विसà¥à¤¤à¤¾à¤°à¤•à¤¾à¤¦à¥à¤µà¤¾à¤°à¥‡) खालील परà¥à¤¯à¤¾à¤¯ पà¥à¤°à¤µà¤¿à¤£à¥à¤¯à¤¾à¤¤ आले आहेत. अधिक माहितीसाठी [https://www.semantic-mediawiki.org/wiki/Help:User_preferences सहायà¥à¤¯ विभागाकडे] नजर टाका.",
+ "smw-ui-tooltip-title-quantity": "à¤à¤•à¤• परिवरà¥à¤¤à¤¨",
+ "smw_unknowntype": "गà¥à¤£à¤§à¤°à¥à¤®à¤¾à¤šà¤¾ पà¥à¤°à¤•à¤¾à¤° “$1†वापरता येत नाही.",
+ "smw-livepreview-loading": "चढवत आहे…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ms.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ms.json
new file mode 100644
index 00000000..93ff3ae7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ms.json
@@ -0,0 +1,56 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Pizza1016"
+ ]
+ },
+ "smw-desc": "Meningkatkan kebolehcapaian wiki anda – baik untuk mesin mahupun manusia ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentasi])",
+ "smw_viewasrdf": "Suapan RDF",
+ "smw_finallistconjunct": ", dan",
+ "smw_factbox_head": "Fakta-fakta tentang $1",
+ "smw_isspecprop": "Sifat ini merupakan sifat khas di wiki ini.",
+ "smw_concept_description": "Keterangan konsep \"$1\"",
+ "smw_no_concept_namespace": "Konsep hanya boleh ditakrifkan di laman-laman dalam ruang nama Concept:.",
+ "smw_multiple_concepts": "Setiap laman konsep hanya boleh ada satu takrifan konsep.",
+ "smw_concept_cache_miss": "Konsep \"$1\" tidak boleh digunakan buat masa ini kerana konfigurasi wiki memerlukannya untuk dikomput di luar talian.\nJika masalah ini masih berlarutan selepas beberapa ketika, mintalah penyelia tapak anda untuk menyediakan konsep ini.",
+ "smw_noinvannot": "Nilai tidak dapat diberikan kepada sifat songsang.",
+ "version-semantic": "Sambungan semantik",
+ "smw_baduri": "URI yang berbentuk \"$1\" tidak dibenarkan.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Hasil pengiraan",
+ "smw_printername_csv": "Eksport CSV",
+ "smw_printername_dsv": "Eksport DSV",
+ "smw_printername_debug": "Pertanyaan nyahpepijat (debug) (untuk pakar)",
+ "smw_printername_embedded": "Benamkan kandungan laman",
+ "smw_printername_json": "Eksport JSON",
+ "smw_printername_list": "Senarai",
+ "smw_printername_ol": "Penyenaraian dengan urutan",
+ "smw_printername_ul": "Penyenaraian tanpa urutan",
+ "smw_printername_table": "Jadual",
+ "smw_printername_broadtable": "Jadual lebar",
+ "smw_printername_template": "Templat",
+ "smw_printername_rdf": "Eksport RDF",
+ "smw_printername_category": "Kategori",
+ "smw-paramdesc-limit": "Bilangan hasil maksimum untuk diberikan",
+ "smw-paramdesc-offset": "Ofset hasil pertama",
+ "smw-paramdesc-headers": "Paparkan nama pengatas/sifat",
+ "smw-paramdesc-mainlabel": "Label untuk nama laman utama",
+ "smw_purge": "Muat semula",
+ "types": "Jenis",
+ "smw_ask_defaultformat": "asali",
+ "smw_sbv_value": "Nilai:",
+ "smw_sbv_submit": "Cari hasil",
+ "browse": "Semak seimbas wiki",
+ "smw_browselink": "Semak seimbas sifat-sifat",
+ "smw_browse_go": "Pergi",
+ "smw_inverse_label_default": "$1 daripada",
+ "smw_inverse_label_property": "Label sifat songsang",
+ "smw_result_prev": "Sebelumnya",
+ "smw_result_next": "Berikutnya",
+ "smw_result_results": "Hasil",
+ "smw_result_noresults": "Tiada hasil.",
+ "smwadmin": "Fungsi penyelia untuk Semantic MediaWiki",
+ "smw-concept-cache-text": "Konsep ini mempunyai sejumlah $1 laman, dan kali terakhir dikemaskinikan pada $3, $2.",
+ "smw-livepreview-loading": "Memuatkan..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mt.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mt.json
new file mode 100644
index 00000000..6dd2535c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mt.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "Chrisportelli",
+ "Roderick Mallia",
+ "පසිඳු කà·à·€à·’න්ද",
+ "Leli Forte"
+ ]
+ },
+ "smw_exportrdf_submit": "Esporta",
+ "smw_purge": "AÄ¡Ä¡orna",
+ "browse": "Esplora l-wiki",
+ "smw_browse_go": "Mur",
+ "smw_result_prev": "Ta' qabel",
+ "smw-livepreview-loading": "Tniżżil fil-progress…",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista ta' riżultati"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mwl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mwl.json
new file mode 100644
index 00000000..489d3f7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mwl.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "A cargar..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/my.json b/www/wiki/extensions/SemanticMediaWiki/i18n/my.json
new file mode 100644
index 00000000..ea9572c7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/my.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ninjastrikers"
+ ]
+ },
+ "browse": "á€á€®á€€á€®á€€á€­á€¯ လှန်လောလေ့လာ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/myv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/myv.json
new file mode 100644
index 00000000..8fcb6dc0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/myv.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Botuzhaleny-sodamo"
+ ]
+ },
+ "smw_sbv_property": "УликÑчиÑÑŒ:",
+ "smw_browse_go": "ÐдÑ",
+ "smw_pp_type": "УликÑчи",
+ "smw-livepreview-loading": "Йоракшны…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/mzn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/mzn.json
new file mode 100644
index 00000000..20205670
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/mzn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "محک"
+ ]
+ },
+ "browse": "ویکی ره بگردستن"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nah.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nah.json
new file mode 100644
index 00000000..4ae1c136
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nah.json
@@ -0,0 +1,15 @@
+{
+ "@metadata": {
+ "authors": [
+ "Fluence"
+ ]
+ },
+ "smw_finallistconjunct": " Ä«huÄn",
+ "smw_nofloat": "\"$1\" ahmo tlapÅhualli.",
+ "smw_purge": "TicyancuÄ«yÄz",
+ "smw_browse_go": "YÄuh",
+ "smw_result_prev": "Achtopa",
+ "smw_result_next": "Niman",
+ "smw_smwadmin_return": "TitocuepÄz Ä«huÄ«c $1",
+ "smw-livepreview-loading": "TÄ“mohua..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nan.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nan.json
new file mode 100644
index 00000000..090344ba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nan.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ianbu"
+ ]
+ },
+ "browse": "看維基"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nap.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nap.json
new file mode 100644
index 00000000..099a7cff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nap.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "C.R."
+ ]
+ },
+ "smw-paramdesc-table-transpose": "Fà vedé 'e cap' 'e tabella n verticale e li risultate n orizzontale",
+ "browse": "Ascìa wiki",
+ "smw_smwadmin_datarefreshstopconfirm": "Si, so' {{GENDER:$1|sicuro|sicura}}."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nb.json
new file mode 100644
index 00000000..f2478870
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nb.json
@@ -0,0 +1,456 @@
+{
+ "@metadata": {
+ "authors": [
+ "Audun",
+ "Event",
+ "Harald Khan",
+ "Laaknor",
+ "Nghtwlkr",
+ "Njardarlogar",
+ "ì•„ë¼",
+ "Emilbk",
+ "SuperPotato",
+ "Macofe",
+ "Jon Harald Søby",
+ "Nemo bis",
+ "Kghbln",
+ "Imre Eilertsen",
+ "Jeblad",
+ "Kingu"
+ ]
+ },
+ "smw-desc": "Gjøre wikien din mer tilgjengelig - for maskiner ''og'' mennesker ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentation])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "Semantic MediaWiki-funksjonalitet har ikke blitt slått på på denne wikien.",
+ "smw_viewasrdf": "RDF-kilde",
+ "smw_finallistconjunct": " og",
+ "smw-factbox-head": "… mer om \"$1\"",
+ "smw-factbox-facts": "Fakta",
+ "smw_isspecprop": "Denne egenskapen er en spesialegenskap på denne wikien.",
+ "smw-concept-cache-header": "Bruk av mellomlager",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count Konseptmellomlageret] inneholder {{PLURAL:$1|'''en''' enhet|'''$1''' enheter}} ($2).",
+ "smw-concept-no-cache": "Mellomlager ikke tilgjengelig.",
+ "smw_concept_description": "Beskrivelse av konseptet «$1»",
+ "smw_no_concept_namespace": "Konsepter kan kun defineres på sider i Konsept:-navnerommet.",
+ "smw_multiple_concepts": "Hver konseptside kan kun ha én konseptdefinisjon.",
+ "smw_concept_cache_miss": "Konseptet \"$1\" kan ikke brukes for øyeblikket da wikiens konfigurasjon krever at den blir satt opp frakoplet.\nHvis problemet ikke forsvinner etter en periode, spør sidens administratorer om å gjøre konseptet tilgjengelig.",
+ "smw_noinvannot": "Inverse egenskaper kan ikke tildeles verdier.",
+ "version-semantic": "Semantiske utvidelser",
+ "smw_baduri": "Beklager, URI-er på formen «$1» er ikke tillatt.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Antall resultater",
+ "smw_printername_csv": "CSV-eksport",
+ "smw_printername_dsv": "DSV-eksport",
+ "smw_printername_debug": "Feilsøkspørring (for eksperter)",
+ "smw_printername_embedded": "Innkapselert sideinnhold",
+ "smw_printername_json": "JSON-eksport",
+ "smw_printername_list": "Liste",
+ "smw_printername_ol": "Nummerert liste",
+ "smw_printername_ul": "Punktliste",
+ "smw_printername_table": "Tabell",
+ "smw_printername_broadtable": "Bred tabell",
+ "smw_printername_template": "Mal",
+ "smw_printername_rdf": "RDF-eksport",
+ "smw_printername_category": "Kategori",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Maksimalt antall resultater som skal returneres",
+ "smw-paramdesc-offset": "Forskyvningen av det første resultatet",
+ "smw-paramdesc-headers": "Viser overskriftene/egenskapsnavnene",
+ "smw-paramdesc-mainlabel": "Hovedsidenavnets etikett",
+ "smw-paramdesc-link": "Vis verdier som lenker",
+ "smw-paramdesc-intro": "Tekst som skal vises før spørringsresultat, dersom det er noen",
+ "smw-paramdesc-outro": "Tekst som skal vises etter spørringsresultat, dersom det er noen",
+ "smw-paramdesc-default": "Tekst som skal vises dersom det ikke er noen resultat for spørringen",
+ "smw-paramdesc-sep": "Skilletegn mellom resultater",
+ "smw-paramdesc-showsep": "Vis skilletegn i starten av CSV-filen (\"sep=<verdi>\")",
+ "smw-paramdesc-distribution": "Istedenfor å vise hver av verdiene vises antall verdier",
+ "smw-paramdesc-distributionsort": "Sorter verdifordelingen mot forekomstantallet",
+ "smw-paramdesc-distributionlimit": "Begrens verdifordelingen til antallet av visse verdier.",
+ "smw-paramdesc-template": "Navnet på malen som skal vise frem utskriftene",
+ "smw-paramdesc-columns": "Antall kolonner for å vise resultater",
+ "smw-paramdesc-userparam": "En verdi sendt til hvert malkall, hvis en mal brukes",
+ "smw-paramdesc-introtemplate": "Navnet på en mal som skal vises før spørringsresultatene, hvis det er noen",
+ "smw-paramdesc-outrotemplate": "Navnet på en mal som skal vises etter spørringsresultatene, hvis det er noen",
+ "smw-paramdesc-embedformat": "HTML-elementet brukt for å definere overskrifter",
+ "smw-paramdesc-embedonly": "Ikke vis overskrifter",
+ "smw-paramdesc-table-class": "En CSS-klasse til som kan brukes for tabellen",
+ "smw-paramdesc-table-transpose": "Vis tabelloverskrifter vertikalt og resultater horisontalt",
+ "smw-paramdesc-rdfsyntax": "RDF-syntaksen som blir brukt",
+ "smw-paramdesc-csv-sep": "Angir kolonneskilletegn",
+ "smw-paramdesc-dsv-separator": "Skilletegnet som blir brukt",
+ "smw-paramdesc-dsv-filename": "Navnet på DSV-filen",
+ "smw-paramdesc-filename": "Navnet på utdatafilen",
+ "smw-smwdoc-description": "Viser en tabell over alle parametre som kan brukes for det angitte resultatformatet sammen med standardverdier og beskrivelser.",
+ "smw-smwdoc-par-format": "Resultatformatet for å vise parameterdokumentasjon.",
+ "smw-smwdoc-par-parameters": "Hvilke parametre som skal vises. \"specific\" for dem som er lagt til av formatet, \"base\" for dem som er tilgjengelig i alle formater og \"all\" for begge.",
+ "smw-paramdesc-sort": "Egenskap som spørringen skal sorteres etter",
+ "smw-paramdesc-order": "Sorteringsrekkefølge for spørringen",
+ "smw-paramdesc-searchlabel": "Tekst for å fortsette søket",
+ "smw-paramdesc-named_args": "Navngi parameterne for malen",
+ "smw-paramdesc-template-arguments": "Sett opp hvordan navngitte argumenter overføres til malen",
+ "smw-paramdesc-import-annotation": "Noen flere annoterte data skal kopieres under parsing av et subjekt",
+ "smw-paramdesc-export": "Eksportvalg",
+ "smw-paramdesc-prettyprint": "En ryddig-utskrift som bruker ekstra innrykk og linjeskift",
+ "smw-paramdesc-json-unescape": "Utdata skal inneholde rene \"\\\"-tegn og multibyte Unicode-tegn",
+ "smw-paramdesc-source": "Alternativ spørringskilde",
+ "smw-paramdesc-jsonsyntax": "JSON-syntaks som brukes",
+ "smw-printername-feed": "RSS- og Atom-strøm",
+ "smw-paramdesc-feedtype": "Strømtype",
+ "smw-paramdesc-feedtitle": "Teksten som brukes som tittel på matingen",
+ "smw-paramdesc-feeddescription": "‎Teksten som brukes som beskrivelse på matingen",
+ "smw-paramdesc-feedpagecontent": "Sideinnholdet som vises av matingen",
+ "smw-label-feed-description": "$1 $2 mating",
+ "smw_iq_disabled": "Semantiske spørringer er slått av på denne wikien.",
+ "smw_iq_moreresults": "… flere resultater",
+ "smw_parseerror": "Oppgitt verdi ble ikke forstått.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "«$1» kan ikke brukes som sidenavn på denne wikien.",
+ "smw_noproperty": "\"$1\" kan ikke brukes som en egenskap i denne wikien.",
+ "smw_wrong_namespace": "Kun sider i navnerommet «$1» er tillatte her.",
+ "smw_manytypes": "Mer enn én type definert for egenskapen.",
+ "smw_emptystring": "Tomme tekststrenger godtas ikke.",
+ "smw_notinenum": "«$1» er ikke i listen ($2) over [[Property:Allows value|tillatte verdier]] for egenskapen «$3».",
+ "smw_noboolean": "«$1» gjenkjennes ikke som en Boolean-verdi (true/false).",
+ "smw_true_words": "true,t,yes,y,ja,j,sant,s",
+ "smw_false_words": "false,f,no,n,nei,usant,u",
+ "smw_nofloat": "«$1» er ikke et tall.",
+ "smw_infinite": "Tall så store som «$1» støttes ikke.",
+ "smw_unitnotallowed": "\"$1\" er ikke definert som en lovlig måleenhet for denne egenskapen.",
+ "smw_nounitsdeclared": "Ingen måleenhet ble definert for denne egenskapen.",
+ "smw_novalues": "Ingen verdier angitt.",
+ "smw_nodatetime": "Datoen «$1» ble ikke forstått.",
+ "smw_toomanyclosing": "«$1» opptrer for mange ganger i spørringen.",
+ "smw_noclosingbrackets": "Bruken av «<nowiki>[[</nowiki>» i spørringen din ble ikke stengt av «]]».",
+ "smw_misplacedsymbol": "Symbolet «$1» ble brukt på et sted hvor det ikke er nyttig.",
+ "smw_unexpectedpart": "«$1»-delen av spørringen var uforståelig.\nResultatene kan være uventede.",
+ "smw_emptysubquery": "En underspørring har ingen gyldige betingelser.",
+ "smw_misplacedsubquery": "En underspørring ble brukt på et sted hvor underspørringer ikke tillates.",
+ "smw_valuesubquery": "Underspørringer støttes ikke for verdier av egenskapen «$1».",
+ "smw_badqueryatom": "En del («<nowiki>[[…]]</nowiki>») av spørringen ble ikke forstått.",
+ "smw_propvalueproblem": "Verdien av egenskapen «$1» ble ikke forstått.",
+ "smw_noqueryfeature": "Noen spørringsfunksjoner ble ikke støttet i denne wikien, og deler av spørringen ble hoppet over ($1).",
+ "smw_noconjunctions": "Konjunksjoner i spørringer støttes ikke i denne wikien, og deler av spørringen ble hoppet over ($1).",
+ "smw_nodisjunctions": "Disjunksjoner i spørringer støttes ikke på denne wikien, og deler av spøringen ble utelatt ($1).",
+ "smw_querytoolarge": "Følgende {{PLURAL:$2|spørringsbetingelse|$2 spørringsbetingelser}} kunne ikke vurderes på grunn av wikiens restriksjoner på spørringsstørrelse eller dybde: <code>$1</code>.\n\nSpørringsbetingelsene <code>$1</code> kunne ikke tas hensyn til på grunn av wikiens begrensninger i spørringsstørrelse eller dybde.",
+ "smw_notemplategiven": "Oppgi en verdi for parameteret «mal» for at dette spørringsformatet skal fungere.",
+ "smw_db_sparqlqueryproblem": "Spørringen ga ikke noe resultat fra SPARQL-databasen. Denne feilen kan være midlertidig eller indikere en feil i database-programvaren.",
+ "smw_db_sparqlqueryincomplete": "Det viste seg å være for vanskelig å gi et svar på spørringen og denne ble derfor avbrutt. Noen resultater kan mangle. Hvis mulig, bruk en enklere spørring.",
+ "smw_type_header": "Egenskaper av typen «$1»",
+ "smw_typearticlecount": "Viser {{PLURAL:$1|én egenskap|$1 egenskaper}} som bruker denne typen.",
+ "smw_attribute_header": "Sider som bruker egenskapen «$1»",
+ "smw_attributearticlecount": "Viser {{PLURAL:$1|én side|$1 sider}} som bruker denne egenskapen.",
+ "smw-propertylist-subproperty-header": "Underegenskaper",
+ "smw-propertylist-redirect-header": "Synonymer",
+ "smw-propertylist-error-header": "Sider med ugyldige tilordninger",
+ "smw-propertylist-count": "Viser $1 {{PLURAL:$1|relatert element|relaterte elementer}}.",
+ "smw-propertylist-count-with-restricted-note": "Viser $1 {{PLURAL:$1|relatert element|relaterte elementer}} (flere er tilgjengelige, men visningen er begrenset til \"$2\").",
+ "smw-propertylist-count-more-available": "Viser $1 {{PLURAL:$1|relatert element|relaterte elementer}} (flere er tilgjengelige).",
+ "exportrdf": "Eksporter sider til RDF",
+ "smw_exportrdf_docu": "Denne siden lar deg skaffe data fra en side i RDF-format.\nSkriv inn titler i tekstboksten nedenfor for å eksportere sider, én tittel per linje.",
+ "smw_exportrdf_recursive": "Eksporter alle relaterte sider rekursivt.\nMerk at resultatet kan være stort.",
+ "smw_exportrdf_backlinks": "Eksporter også alle sider som refererer til de eksporterte sidene.\nLager en RDF som kan gås gjennom.",
+ "smw_exportrdf_lastdate": "Ikke eksporter sider som ikke ble endret siden gitte tidspunkt.",
+ "smw_exportrdf_submit": "Eksporter",
+ "uriresolver": "URI-løser",
+ "properties": "Egenskaper",
+ "smw_properties_docu": "Følgende egenskaper brukes på wikien.",
+ "smw_property_template": "$1 av type $2 ($3 {{PLURAL:$3|bruker}})",
+ "smw_propertylackspage": "Alle egenskaper burde beskrives av en side.",
+ "smw_propertylackstype": "Ingen type spesifisert for denne egenskapen (bruker foreløpig typen $1).",
+ "smw_propertyhardlyused": "Denne egenskapen brukes nesten ikke på wikien.",
+ "smw-property-name-invalid": "Egenskap $1 ikke ikke brukes (ugyldig egenskapsnavn).",
+ "smw-sp-property-searchform": "Vis egenskaper som inneholder:",
+ "smw-sp-property-searchform-inputinfo": "Det skilles mellom store og små bokstaver i inndata, og når disse brukes for filtrering, vil bare egenskaper vises som passer med betingelsen.",
+ "smw-special-property-searchform": "Vis egenskaper som inneholder:",
+ "smw-special-property-searchform-inputinfo": "Det skilles mellom store og små bokstaver i inndata, og når disse brukes for filtrering, vil bare egenskaper vises som oppfyller betingelsen.",
+ "smw-special-property-searchform-options": "Valg",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "Ingen",
+ "smw-special-wantedproperties-filter-unapproved": "Ikke godkjent",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Filtervalg brukt i forbindelse med autoritetsmodus.",
+ "concepts": "Begreper",
+ "smw-special-concept-docu": "Et [https://www.semantic-mediawiki.org/wiki/Help:Concepts konsept] kan betraktes som en \"dynamisk kategori\", dvs. som en samling sider som ikke opprettes manuelt, men som settes sammen av Semantic MediaWiki fra en beskrivelse av en gitt spørring.",
+ "smw-special-concept-header": "Liste av konsepter",
+ "smw-special-concept-count": "Følgende {{PLURAL:$1|konsept|$1 konsepter}} {{PLURAL:$1|}} vises.",
+ "smw-special-concept-empty": "Ingen konsepter ble funnet.",
+ "unusedproperties": "Ubrukte egenskaper",
+ "smw-unusedproperties-docu": "Denne siden lister opp [https://www.semantic-mediawiki.org/wiki/Unused_properties ubenyttede egenskaper] som er opprettet uten at andre sider bruker dem. For en annen visning, se spesialsidene [[Special:Properties|alle]] eller [[Special:WantedProperties|ønskede egenskaper]].",
+ "smw-unusedproperty-template": "$1 av type $2",
+ "wantedproperties": "Ønskede egenskaper",
+ "smw-wantedproperties-docu": "Denne siden lister opp [https://www.semantic-mediawiki.org/wiki/Wanted_properties ønskede egenskaper] som brukes i wikien, men har ennå ingen side som beskriver dem. For en annan visning, se spesialsidene [[Special:Properties|alle]] eller [[Special:UnusedProperties|ubrukte egenskaper]].",
+ "smw-wantedproperty-template": "$1 (brukt {{PLURAL:$2|én gang|$2 ganger}})",
+ "smw-special-wantedproperties-docu": "Denne siden lister opp [https://www.semantic-mediawiki.org/wiki/Wanted_properties ønskede egenskaper] som brukes i wikien, men som ikke har noen side som beskriver dem. For en annan visning, se spesialsidene [[Special:Properties|alle]] eller [[Special:UnusedProperties|ubrukte egenskaper]].",
+ "smw-special-wantedproperties-template": "$1 (er brukt {{PLURAL:$2|én gang|$2 ganger}})",
+ "smw_purge": "Gjenoppfrisk",
+ "smw-purge-failed": "Gjenoppretting mislyktes",
+ "types": "Typer",
+ "smw_types_docu": "Liste over [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tilgjengelige datatyper] der hver [https://www.semantic-mediawiki.org/wiki/Help:Datatype type] representerer et unikt sett av attributter for å beskrive en verdi med tanke på lagring og visningskarakteristikker som arves av en gitt egenskap.",
+ "smw-special-types-no-such-type": "Den angitte datatypen finnes ikke",
+ "smw-statistics": "Semantisk statistikk",
+ "smw-statistics-property-instance": "Egenskap {{PLURAL:$1|verdi|verdier}} (totalt)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Egenskap|Egenskaper}}]] (totalt)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Egenskap|Egenskaper}} (totalt)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|egenskap|egenskaper}}]] (brukt med minst en verdi)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Egenskap|Egenskaper}} (som har en egen side)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Egenskap|Egenskaper}} (som er tildelt en datatype)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Spørring|Spørringer}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Spørring|Spørringer}}]]",
+ "smw-statistics-query-size": "Størrelse på spørringen",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Konsept|Konsepter}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Konsept|Konsepter}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Underobjekt|Underobjekter}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Underobjekt|Underobjekter}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datatype|Datatyper}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Egenskapsverdi|Egenskapsverdier}} ([[Special:ProcessingErrorList|upassende {{PLURAL:$1|annotering|annoteringer}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Egenskapsverdi|Egenskapsverdier}} (upassende {{PLURAL:$1|annotering|annoteringer}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Utdatert enhet|Utdaterte enheter}} (merket for fjerning)",
+ "smw_uri_doc": "URI-løseren implementerer [$1 finning av W3C TAG-er på «httpRange-14»].\nDen sørger for at mennesker ikke gjøres til nettsteder.",
+ "ask": "Semantisk søk",
+ "smw_ask_sortby": "Sorter etter kolonne (valgfritt)",
+ "smw_ask_ascorder": "Stigende",
+ "smw_ask_descorder": "Synkende",
+ "smw-ask-order-rand": "Tilfeldig",
+ "smw_ask_submit": "Finn resultater",
+ "smw_ask_editquery": "Rediger spørring",
+ "smw_add_sortcondition": "[Legg til sorteringsbetingelse]",
+ "smw-ask-sort-add-action": "Legg til sorteringsbetingelse",
+ "smw_ask_hidequery": "Skjul spørring",
+ "smw_ask_help": "Spørringshjelp",
+ "smw_ask_queryhead": "Betingelse",
+ "smw_ask_printhead": "Utvalg for utskrift",
+ "smw_ask_printdesc": "(legg til et egenskapsnavn per linje)",
+ "smw_ask_format_as": "Formater som:",
+ "smw_ask_defaultformat": "standard",
+ "smw_ask_otheroptions": "Andre valgmuligheter",
+ "smw-ask-otheroptions-info": "Dette avsnittet innholder valg som påvirker utdata. Parameterbeskrivelser vises når musepekeren føres over dem.",
+ "smw-ask-otheroptions-collapsed-info": "Vennligst bruk pluss-ikonet for å vise alle valgmuligheter",
+ "smw_ask_show_embed": "Vis innkapslet kode",
+ "smw_ask_hide_embed": "Skjul innkapslet kode",
+ "smw_ask_embed_instr": "For å bygge inn denne spørringen i en wikiside, bruk koden nedenfor.",
+ "smw-ask-delete": "Fjern",
+ "smw-ask-sorting": "Sortering",
+ "smw-ask-options": "Valgmuligheter",
+ "smw-ask-options-sort": "Sorteringsmuligheter",
+ "smw-ask-parameters": "Parametre",
+ "smw-ask-search": "Søk",
+ "smw-ask-debug": "Feilsøking",
+ "smw-ask-result": "Resultat",
+ "smw-ask-empty": "Fjern alle oppføringer",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Hjelp med det valgte formatet: $1",
+ "smw-ask-format-export-info": "Det valgte formatet er et eksportformat uten visuell representasjon. Vær obs på at resultatene kommer som nedlastinger.",
+ "smw-ask-query-search-info": "Spørringen <code><nowiki>$1</nowiki></code> ble besvart av {{PLURAL:$3|1=<code>$2</code> (fra mellomlager)|<code>$2</code> (fra mellomlager)|<code>$2</code>}} på $4 {{PLURAL:$4|sekund|sekunder}}.",
+ "searchbyproperty": "Søk etter egenskap",
+ "processingerrorlist": "Prosesserer feilliste",
+ "propertylabelsimilarity": "Likhetsrapport for egenskapsetikett",
+ "smw-processingerrorlist-intro": "Følgende liste gir et overblikk over prosesseringsfeil som oppsto i forbindelse med [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Det er anbefalt å sjekke lista regelmessig og rette ugyldige verdiannoteringer.",
+ "smw_sbv_docu": "Søk etter alle sider som har en gitt egenskap og verdi.",
+ "smw_sbv_novalue": "Skriv inn en gyldig verdi for egenskapen, eller vis alle egenskapsverdier for «$1».",
+ "smw_sbv_displayresult": "En liste over alle sider som har egenskapen «$1» med verdien «$2»",
+ "smw_sbv_displayresultfuzzy": "En liste over alle sider som har egenskapen «$1» med verdien «$2».\nSiden det bare ble noen få resultater, vises også nære verdier.",
+ "smw_sbv_property": "Egenskap:",
+ "smw_sbv_value": "Verdi:",
+ "smw_sbv_submit": "Finn resultater",
+ "browse": "Se gjennom wikien",
+ "smw_browselink": "Bla gjennom egenskaper",
+ "smw_browse_article": "Skriv inn navnet på siden du vil starte å bla fra.",
+ "smw_browse_go": "GÃ¥",
+ "smw_browse_show_incoming": "vis egenskaper som lenker hit",
+ "smw_browse_hide_incoming": "skjul egenskaper som lenker hit",
+ "smw_browse_no_outgoing": "Denne siden har ingen egenskaper.",
+ "smw_browse_no_incoming": "Ingen egenskaper lenker til denne siden.",
+ "smw-browse-from-backend": "Informasjon hentes nå fra tjenermaskinen.",
+ "smw-browse-intro": "Denne siden viser detaljer om et element eller objektype, så vær vennlig å angi navnet på et objekt som skal sjekkes.",
+ "smw-browse-invalid-subject": "Elementvalideringen ga en \"$1\"-feil.",
+ "smw-browse-api-subject-serialization-invalid": "Elementet har et ugyldig serialiseringsformat.",
+ "smw-browse-js-disabled": "Det er mistanke om at JavaScript er deaktivert eller utilgjengelig av annen årsak, og vi anbefaler derfor å bruke en nettleser hvor dette støttes. Andre valgmuligheter diskuteres på innstillingssiden for [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Vis grupper",
+ "smw-browse-hide-group": "Skjul grupper",
+ "smw-noscript": "Denne siden eller funksjonen krevet aktivisert JavaScript. Aktiver derfor JavaScript in nettleseren du bruker eller bytt til en der denne virker. For nærmere assistanse se [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript]-hjelpsiden.",
+ "smw_inverse_label_default": "$1 av",
+ "smw_inverse_label_property": "Invers egenskapsmerkelapp",
+ "pageproperty": "Sideegenskapssøk",
+ "smw_pp_docu": "Søk etter alle egenskapsverdier på en gitt side.\nSkriv inn både sidenavn og egenskap.",
+ "smw_pp_from": "Fra side",
+ "smw_pp_type": "Egenskap",
+ "smw_pp_submit": "Finn resultater",
+ "smw_result_prev": "Forrige",
+ "smw_result_next": "Neste",
+ "smw_result_results": "Resultater",
+ "smw_result_noresults": "Beklager, ingen resultater funnet",
+ "smwadmin": "Administrative funksjoner",
+ "smw-admin-statistics-job-title": "Jobbstatistikk",
+ "smw-admin-statistics-job-docu": "Jobb-statistikken viser informasjon om planlagte Semantic MediaWiki-jobber som enda ikke har blitt kjørt. Antall jobber kan være unøyaktig angitt eller inneholde mislykkede forsøk, se [https://www.mediawiki.org/wiki/Manual:Job_queue manual] for mer informasjon.",
+ "smw-admin-statistics-querycache-title": "Statistikk for mellomlagring av spørringer",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] har ikke blitt aktivisert på denne wikien, så derfor er ingen statistikk tilgjengelig.",
+ "smw-admin-statistics-querycache-explain": "Mellomlager-statistikken skal inneholde foreløpige kumulative og avledede data som inneholder: \n* \"misses\" som er det samlede antall forsøk på å hente data fra mellomlagret med uoppnåelige svar, som fremtvinger direkte-henting fra repository (database, triple-store etc.)\n* \"deletes\" som er det totale antall mellomlager-evakueringer (enten gjennom en either gjennom en rensing- eller spørringsavhengighet) \n* \"hits\" som er det totale antall mellomlager-hentinger fra enten fra enten innebygde (spørringer kalt fra en wiki-side) eller ikke-innbygde (dersom aktivisert, etterspurt av sider som Special:Ask eller API)\n* \"medianRetrievalResponseTime\" er en orienteringsverdi for midlere responstid (under samleprosessen) \n* \"noCache\" angir antall ikke-prøvde forespørsler (limit=0-spørringer, 'no-cache'-valg etc.) for å hente resultater fra mellomlagret",
+ "smw-admin-permission-missing": "Tilgangen til denne siden har blitt blokkert på grunn av manglende tillatelser, se [https://www.semantic-mediawiki.org/wiki/Help:Permissions tillatelser]-hjelpesiden for detaljer om nødvendige innstillinger.",
+ "smw-admin-setupsuccess": "Lagringsmotoren har blitt initialisert.",
+ "smw_smwadmin_return": "GÃ¥ tilbake til $1",
+ "smw_smwadmin_updatestarted": "En ny oppdateringsprosess for oppfriskning av de semantiske dataene ble startet.\nAlle lagrede data vil bli bygd på nytt eller reparert der det trengs.\nDu kan følge med på fremgangen til oppdateringen på denne spesialsiden.",
+ "smw_smwadmin_updatenotstarted": "Det kjører allerede en oppdateringsprosess.\nStarter derfor ikke enda en.",
+ "smw_smwadmin_updatestopped": "Alle eksisterende oppdateringsprosesser har blitt stoppet.",
+ "smw_smwadmin_updatenotstopped": "For å stoppe den kjørende oppdateringsprosessen må du markere avkrysningsboksen for å vise at du er helt sikker.",
+ "smw-admin-docu": "Denne spesialsiden hjelper deg under innstallasjon, oppgradering og vedlikehold av <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a> og tilbyr flere administrative funksjoner og oppgaver i tillegg til statistikk. Husk å ta sikkerhetskopier av viktige data før du starter disse administrasjonsfunksjonene.",
+ "smw-admin-db": "Databaseinstallering og -oppgradering",
+ "smw-admin-db-preparation": "Tabellinitialiseringen er underveis og det kan ta noe tid før resultater vises avhengig av størrelse og tabelloptimalisering.",
+ "smw-admin-dbdocu": "Semantic MediaWiki krever enkelte utvidelser av MediaWiki-databasen for å kunne lagre semantiske data.\nFunksjonen under sikrer at databasen din blir riktig initialisert.\nEndringene gjort i dette steget har ingen effekt på resten av MediaWiki-databasen og kan enkelt reverseres om ønskelig.\nDenne initialiseringsfunksjonen kan kjøres flere ganger uten å gjøre noen skade, men den trenger kun å kjøres en gang i forbindelse med installering eller oppgradering.",
+ "smw-admin-permissionswarn": "Dersom operasjonen mislykkes med SQL-feil, har databasebrukeren som wikien din anvender sannsynligvis manglende rettigheter (sjekk LocalSettings.php).\nEnten må denne brukeren gis tilstrekkelige rettigheter til å opprette og slette tabeller, eller en må midlertidig skrive inn innloggingsinformasjonen til database-roten i LocalSettings.php, eller en må bruke vedlikeholdsskriptet <code>setupStore.php</code> som har rettighetene til LocalSettings.php.",
+ "smw-admin-dbbutton": "Initialiser eller oppgrader tabeller",
+ "smw-admin-announce": "Kunngjør din wiki",
+ "smw-admin-announce-text": "Dersom wikien er offentlig, kan du registrere den på <a href=\"https://wikiapiary.com\">WikiApiary</a>, wikien som holder oversikt over wikier.",
+ "smw-admin-deprecation-notice-title": "Meldinger om funksjonalitet som vil bli fjernet",
+ "smw-admin-deprecation-notice-docu": "Det følgende avsnittet inneholder innstillinger som har blitt satt som utgående eller utgått, men har blitt oppdaget å være aktive på denne wikien. Det må påregnes at i en fremtidig versjon vil støtte for disse innstillingsmulighetene være fjernet.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> er utgående og blir fjernet i $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> ble erstattet av <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> ble fjernet i $2",
+ "smw-admin-deprecation-notice-title-notice": "Kommende endringer",
+ "smw-admin-deprecation-notice-title-replacement": "Erstattede eller omdøpte innstillinger",
+ "smw-admin-deprecation-notice-title-removal": "Fjernede innstillinger",
+ "smw-smwadmin-refresh-title": "Datareparering og -oppdatering",
+ "smw_smwadmin_datarefresh": "Datagjenoppbygging",
+ "smw_smwadmin_datarefreshdocu": "Det er mulig å gjenopprette alle Semantic MediaWiki-data basert på det nåværende innholdet på wikien.\nDette kan være nyttig for å reparere ødelagte data eller for å oppfriske data hvis det interne formatet har blitt endret på grunn av programvareoppdateringer.\nOppdateringen blir utført side for side og vil ikke bli fullført umiddelbart.\nDet følgende viser om en oppdatering er i gang og lar deg starte eller stoppe oppdateringer (ved mindre denne muligheten er gjort utilgjengelig av sidens administratorer).",
+ "smw_smwadmin_datarefreshprogress": "<strong>En oppdatering er allerede i gang.</strong>\nDet er normalt at oppdateringen går tregt fremover siden den bare oppdaterer data i små biter hver gang en bruker besøker wikien.\nFor å fullføre denne oppdateringen raskere kan du starte MediaWiki-vedlikeholdsskriptet <code>runJobs.php</code> (bruk valget <code>--maxjobs 1000</code> for å avgrense antallet oppdateringer som gjøres sammenhengende).\nAnslått fremdrift for gjeldende oppdatering:",
+ "smw_smwadmin_datarefreshbutton": "Start oppdatering av data",
+ "smw_smwadmin_datarefreshstop": "Stopp denne oppdateringen",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, jeg er {{GENDER:$1|sikker}}.",
+ "smw-admin-job-scheduler-note": "De fleste aktivitetene er planlagt utført som en samlejobb definert gjennom jobbplanleggeren. Det er derfor kritisk at vedlikeholdsskriptet <code>runJobs.php</code> eller <code>$wgRunJobsAsync</code> administreres for dette formål.",
+ "smw-admin-outdateddisposal-title": "Forkasting av utdaterte elementer",
+ "smw-admin-outdateddisposal-intro": "Noen aktiviteter (som en endring av en egenskap sin type, fjerning av wiki-sider, eller verdikorreksjoner) resulterer i [https://www.semantic-mediawiki.org/wiki/Outdated_entities utdaterte elementer], og det anbefales å fjerne disse med jevne mellomrom for å frigjøre databaseplass.",
+ "smw-admin-outdateddisposal-active": "En ryddejobb av utdaterte elementer er satt i køen.",
+ "smw-admin-outdateddisposal-button": "Gjør klar rydding",
+ "smw-admin-feature-disabled": "Denne funksjonen har blitt slått av på denne wikien. Se den <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">tilknyttede</a> hjelpesiden for detaljer.",
+ "smw-admin-propertystatistics-title": "Ombygging av egenskapsstatistikk",
+ "smw-admin-propertystatistics-intro": "Ombygger hele statistikken for bruken av egenskaper og derigjennom oppdaterer og korrigerer [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count bruksantallet] av egenskaper.",
+ "smw-admin-propertystatistics-active": "En ombyggingsjobb for statistikk har blitt satt i køen.",
+ "smw-admin-propertystatistics-button": "Sett opp ombygging av statistikken",
+ "smw-admin-fulltext-title": "Ombygging av fulltekstsøk",
+ "smw-admin-fulltext-intro": "Gjenoppbygger søkeindeksen fra egenskapstabeller med en aktivisert [https://www.semantic-mediawiki.org/wiki/Full-text fulltekstsøkbasert] datatype.",
+ "smw-admin-fulltext-active": "En fulltekstsøkbasert gjenoppbyggingsjobb har blitt satt i køen.",
+ "smw-admin-fulltext-button": "Sett opp en fulltekstbasert gjenoppbygging",
+ "smw-admin-support": "Få støtte",
+ "smw-admin-supportdocu": "Diverse ressurser er tilgjengelige for å hjelpe deg om du skulle få problemer:",
+ "smw-admin-installfile": "Hvis du får problemer med installeringen, studer retningslinjene i <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL-filen</a> og <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">installeringssiden</a>.",
+ "smw-admin-smwhomepage": "Den komplette brukerdokumentasjonen til Semantic MediaWiki finnes på <b><a href=\"https://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Feil kan rapporteres til <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">saksflytverktøyet</a>. <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">Feilrapportsiden</a> gir retningslinjer om hvordan en skriver en effektiv feilrapport.",
+ "smw-admin-questions": "Hvis du har ytterligere spørsmål eller forslag, bli med i diskusjonen på <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">Semantic MediaWiki brukerforum</a> eller <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">chatroom</a>.",
+ "smw-admin-other-functions": "Andre funksjoner",
+ "smw-admin-supplementary-section-title": "Ekstra funksjoner",
+ "smw-admin-supplementary-section-subtitle": "Tilgjengelige funksjoner",
+ "smw-admin-supplementary-section-intro": "Dette avsnittet har tilleggsfunksjoner utover det som behøves for vedlikehold, og det er mulig at noen funksjoner som angis i https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentation] er begrenset eller utilgjengelig og derfor ikke kan brukes på denne wikien.",
+ "smw-admin-supplementary-settings-title": "Konfigurasjonsinnstillinger",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> inneholder ei liste over tilgjengelige innstillinger som brukes i Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Driftsstatistikk",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> viser en utvidet samling statistikker",
+ "smw-admin-supplementary-idlookup-title": "Elementoppslag og forkasting",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> inneholder funksjoner for oppslag og forkasting av enkeltelementer",
+ "smw-admin-supplementary-duplookup-title": "Dupliser elementer",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> for å angi elementer som er kategorisert å inneholde duplikater i elementtabellen",
+ "smw-admin-supplementary-duplookup-docu": "Denne siden angir elementer fra [https://www.semantic-mediawiki.org/wiki/Help:Entity_table entity table] som er kategorisert som duplikater. Duplikate elementer bør, om noensinne, bare opptre i sjeldne tilfeller, ev. forårsaket av en avsluttet prosess under en databaseoppdatering eller en mislykket tilbakerulling fra sikkerhetskopi.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-property-label-uniqueness": "\"$1\"-etiketten hadde treff på minst en egenskap til. Vær vennlig å studere [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness hjelpesiden] om hvordan du kan løse saken.",
+ "smw-property-label-similarity-title": "Likhetsrapport for egenskapsetikett",
+ "smw-property-label-similarity-intro": "<u>$1</u> påviser likheter innenfor eksisterende egenskapsetiketter",
+ "smw-property-label-similarity-threshold": "Terskelverdi:",
+ "smw-property-label-similarity-type": "Vis type-ID",
+ "smw-property-label-similarity-noresult": "Ingen resultater ble funnet for de valgte mulighetene.",
+ "smw-property-label-similarity-docu": "Sammenlikner og rapporterer [https://www.semantic-mediawiki.org/wiki/Property_similarity syntaks-samsvar] (i motsetning til semantikk-samsvar) mellom to egenskapsetiketter som kan bidra til å filtrere bort feilskrevne eller ekvivalente egenskaper som representerer det samme begrepet (se spesialsiden [[Special:Properties|Properties]] for å oppklare begrep og bruk av rapporterte egenskaper). Terskelverdien kan justeres i den hensikt å utvide eller innskrenke samsvarsavstanden. <code>[[Property:$1|$1]]</code> brukes for å unnta egenskaper fra analysen.",
+ "smw-admin-operational-statistics": "Denne siden inneholder driftsstatistikk samlet fra Semantic MediaWiki eller relaterte funksjoner. En utvidet liste av wikispesifikk statistikk kan finnes [[Special:Statistics|<b>her</b>]].",
+ "smw_adminlinks_datastructure": "Datastruktur",
+ "smw_adminlinks_displayingdata": "Datavisning",
+ "smw_adminlinks_inlinequerieshelp": "Hjelp for innebygde spørringer",
+ "smw-page-indicator-usage-count": "Estimert [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count bruksantall]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|Bruker|System}}-opprettet egenskap",
+ "smw-property-indicator-last-count-update": "Estimert bruksantall\nSist oppdatert: $1",
+ "smw-createproperty-isproperty": "Det er en egenskap av type $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Den tillatte verdien|De tillatte verdiene}} for denne egenskapen er:",
+ "smw-paramdesc-category-delim": "Skilletegnet",
+ "smw-paramdesc-category-template": "En mal til bruk for å formatere elementene",
+ "smw-paramdesc-category-userparam": "En parameter som gis til malen",
+ "smw-info-par-message": "Melding å vise frem.",
+ "smw-info-par-icon": "Ikon for å vise frem enten \"info\" eller \"advarsel\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Generelle valg",
+ "prefs-ask-options": "Valgmuligheter for Special:Ask",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (og relaterte utvidelser) for å muliggjøre individuelle tilpasninger for noen utvalgte funksjoner. Sjekk [https://www.semantic-mediawiki.org/wiki/Help:User_preferences hjelpesiden] for en detaljert beskrivelse.",
+ "smw-prefs-ask-options-tooltip-display": "Vis parameterteksten som hinttekst.",
+ "smw-prefs-general-options-time-correction": "Aktiver tidskorreksjonen for spesialsider ved å bruke den lokale [[Special:Preferences#mw-prefsection-rendering|klokkeslettforskyvnings]]-preferansen.",
+ "smw-prefs-general-options-disable-editpage-info": "Slå av introduksjonsteksten på redigeringssiden",
+ "smw-ui-tooltip-title-property": "Egenskap",
+ "smw-ui-tooltip-title-quantity": "Størrelseskonvertering",
+ "smw-ui-tooltip-title-info": "Informasjon",
+ "smw-ui-tooltip-title-service": "Tjenestelenker",
+ "smw-ui-tooltip-title-warning": "Advarsel",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Hendelse",
+ "smw-ui-tooltip-title-note": "Merknad",
+ "smw-ui-tooltip-title-legend": "Tegnforklaring",
+ "smw-ui-tooltip-title-reference": "Referanse",
+ "smw_unknowntype": "Typen som er brukt for denne egenskapen er ugyldig.",
+ "smw-concept-cache-text": "Konseptet består av totalt $1 {{PLURAL:$1|side|sider}}, og ble sist oppdatert $3, $2.",
+ "smw_concept_header": "Sider av konseptet «$1»",
+ "smw_conceptarticlecount": "Viser nedenfor {{PLURAL:$1|én side|$1 sider}}.",
+ "smw-qp-empty-data": "Forespurte data kan ikke vises på grunn av utilstrekkelige søkekriterier.",
+ "right-smw-admin": "Administrasjonsoppgaver for tilgang (Semantic MediaWiki)",
+ "right-smw-patternedit": "Redigeringstilgang for å vedlikeholde tillatte regulære uttrykk og mønstre (Semantic MediaWiki)",
+ "right-smw-pageedit": "Rediger tilgang til <code>beskyttede</code> annoteringssider (Semantic MediaWiki)",
+ "action-smw-patternedit": "rediger regular expressions brukt av Semantic MediaWiki",
+ "action-smw-pageedit": "rediger sider annotert med <code>Er sperret mot redigering</code> (Semantic MediaWiki)",
+ "group-smwadministrator": "Administratorer (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrator (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Administratorer (Semantic MediaWiki)",
+ "group-smwcurator": "Kuratorer (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|kurator (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Kuratorer (Semantic MediaWiki)",
+ "action-smw-admin": "gå til administrasjonsoppgaver for Semantic MediaWiki",
+ "smw-property-predefined-default": "$1 er en forhåndsdefinert egenskap.",
+ "smw-property-predefined-common": "Denne egenskapen er preinstallert (også kjent som [https://www.semantic-mediawiki.org/wiki/Help:Special_properties spesialegenskap]) og kommer med ekstra administrative privilegier, men kan ellers brukes på samme måte som vanlige [https://www.semantic-mediawiki.org/wiki/Property brukerdefinerte egenskaper].",
+ "smw-property-predefined-ask": "$1 er en predefinert egenskap som representerer metainformasjon (i formen [https://www.semantic-mediawiki.org/wiki/Subobject underobjekt]) om individuelle spørringer og fremskaffes av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "\"$1\" er en predefinert egenskap som samler antall betingelser brukt i en spørring og fremskaffes av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" er en predefinert egenskap som informerer om dybden i en spørring og fremskaffes av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "Det er en numerisk verdi beregnet på grunnlag av nøsting via en underspørring, egenskapskjeder og tilgjengelige beskrivelseselementer med utførelsen av en spørring som er begrenset av <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>-konfigurasjonsparameteren.",
+ "smw-property-predefined-askpa": "\"$1\" er en predefinert egenskap som beskriver parametre som påvirker resultatet fra en spørring fremskaffet av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askpa": "Det er en del av en samling egenskaper som angir en [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler spørreprofil].",
+ "smw-sp-properties-docu": "Denne siden viser [https://www.semantic-mediawiki.org/wiki/Property egenskapene] og hvordan de er brukt på denne wikien. For en oppdatert statistikk anbefales det at [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics statistikken] sitt vedlikeholdsskript kjøres regelmessig. For en variert visning, se [[Special:UnusedProperties|ubrukte]] eller [[Special:WantedProperties|ønskede egenskaper]] på spesialsidene.",
+ "smw-sp-properties-cache-info": "Dataene i listen er blitt hentet fra [https://www.semantic-mediawiki.org/wiki/Caching et mellomlager], og ble sist oppdatert $1.",
+ "smw-sp-properties-header-label": "Liste av egenskaper",
+ "smw-admin-settings-docu": "Viser en liste av alle standard og lokaliserte innstillinger som er relevante for omgivelsene til Semantic MediaWiki. For detaljer om individuelle innstillinger, vennligst slå opp på [https://www.semantic-mediawiki.org/wiki/Help:Configuration konfigurerings]-hjelpesiden.",
+ "smw-sp-admin-settings-button": "Generer innstillingslisten",
+ "smw-admin-idlookup-title": "Oppslag av objekt-id",
+ "smw-admin-idlookup-docu": "Dette ansnittet viser tekniske detaljer om individuelle elementer (wikiside, underobjekt, egenskap, etc.) i Semantic MediaWiki. Brukeren oppgir en id eller en string som sammenliknes med det valgte feltet. Merk at slike id-referanser ikke må forveksles med MediaWiki-sider eller redigerings-id-er.",
+ "smw-admin-iddispose-title": "Forkasting av id",
+ "smw-admin-objectid": "ID:",
+ "smw-livepreview-loading": "Laster…",
+ "smw-sp-searchbyproperty-resultlist-header": "Liste over resultater",
+ "smw-search-profile-sort-best": "Beste treff",
+ "smw-search-profile-sort-recent": "Nyligste",
+ "smw-search-profile-sort-title": "Tittel",
+ "smw-search-profile-extended-help-find-forms": "Finn former",
+ "smw-search-profile-extended-section-sort": "Sorter etter",
+ "smw-search-profile-extended-section-namespace": "Velg et navnerom",
+ "smw-search-profile-extended-section-query": "Spørring",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1-importering]]",
+ "smw-types-list": "Liste over datatyper",
+ "smw-types-default": "«$1» er ikke en innebygd datatype.",
+ "smw-type-boo": "«$1» er en primitiv datatype for å beskrive en verdi som er sann/usann.",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekund|sekunder}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekund|sekunder}}",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Formaterings-URI-en mangler plassholderen «$1».",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» er en ugyldig URL.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "«$1» kan ikke brukes som foretrukket etikett fordi språket «$2» allerede har etiketten «$3» tildelt.",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Kopier lenke til utklippstavlen",
+ "smw-data-lookup": "Henter data ...",
+ "smw-no-data-available": "Ingen data tilgjengelige.",
+ "smw-api-invalid-parameters": "Ugyldige parametere, «$1»",
+ "smw-parameter-missing": "Parameteren «$1» mangler.",
+ "smw-property-tab-usage": "Bruk",
+ "smw-property-tab-redirects": "Synonymer",
+ "smw-property-tab-subproperties": "Underegenskaper",
+ "smw-concept-tab-list": "Liste",
+ "smw-concept-tab-errors": "Feil"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nds-nl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nds-nl.json
new file mode 100644
index 00000000..40cd105a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nds-nl.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Servien"
+ ]
+ },
+ "smw_ask_descorder": "Aoflopend",
+ "browse": "Wiki bekieken",
+ "smw_browselink": "Eigenschappen bekieken",
+ "smw-livepreview-loading": "An t laojen…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nds.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nds.json
new file mode 100644
index 00000000..6503e839
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nds.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joachim Mos"
+ ]
+ },
+ "smw_printername_template": "Vörlaag",
+ "smw_printername_category": "Kategorie",
+ "smw_ask_defaultformat": "default",
+ "smw_pp_type": "Egenschop",
+ "smw-livepreview-loading": "Läädt…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ne.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ne.json
new file mode 100644
index 00000000..0d656a0a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ne.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "बिपà¥à¤²à¤¬ आननà¥à¤¦",
+ "जनक राज भटà¥à¤Ÿ"
+ ]
+ },
+ "browse": "बà¥à¤°à¤¾à¤‰à¤œ विकि",
+ "smw-livepreview-loading": "लोड हà¥à¤¦à¥ˆà¤›...",
+ "smw-limitreport-intext-parsertime-value": "$१ सेकेनà¥à¤¡à¤¸",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$१ सेकेनà¥à¤¡à¤¸"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/niu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/niu.json
new file mode 100644
index 00000000..c8c2e0f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/niu.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77"
+ ]
+ },
+ "smw_browse_go": "Fano"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nl.json
new file mode 100644
index 00000000..938b14e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nl.json
@@ -0,0 +1,506 @@
+{
+ "@metadata": {
+ "authors": [
+ "Arent",
+ "Hansmuller",
+ "Kghbln",
+ "McDutchie",
+ "Rcdeboer",
+ "SPQRobin",
+ "Saruman",
+ "Siebrand",
+ "Tjcool007",
+ "Wiki13",
+ "Sjoerddebruin",
+ "Macofe",
+ "Rangekill",
+ "Ecthelion3",
+ "Hex",
+ "Mainframe98",
+ "Nemo bis",
+ "DengroWikibase",
+ "Festina90",
+ "MarcoSwart",
+ "Mar(c)",
+ "Patio",
+ "KlaasZ4usV",
+ "Goefie",
+ "Robin van der Linde",
+ "Elroy"
+ ]
+ },
+ "smw-desc": "Uw wiki toegankelijker maken – voor machines ''en'' mensen ([https://www.semantic-mediawiki.org/wiki/Help:User_manual online documentatie])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "Semantic MediaWiki-functionaliteit is niet ingeschakeld voor deze wiki.",
+ "smw_viewasrdf": "RDF-feed",
+ "smw_finallistconjunct": " en",
+ "smw-factbox-head": "... meer over \"$1\"",
+ "smw-factbox-facts": "Feiten",
+ "smw-factbox-facts-help": "Toon statements en feiten die aangemaakt zijn door een gebruiker",
+ "smw-factbox-facts-derived": "Afgeleide feiten",
+ "smw_isspecprop": "Dit is een speciale eigenschap in deze wiki.",
+ "smw-concept-cache-header": "Cachegebruik",
+ "smw-concept-cache-count": "De [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count conceptcache] bevat {{PLURAL:$1|'''één''' element|'''$1''' elementen}} ($2).",
+ "smw-concept-no-cache": "Geen cache beschikbaar.",
+ "smw_concept_description": "Beschrijving van concept \"$1\"",
+ "smw_no_concept_namespace": "Concepten kunnen alleen gedefinieerd worden op pagina's in de naamruimte Concept.",
+ "smw_multiple_concepts": "Iedere conceptpagina kan maar één conceptdefinitie bevatten.",
+ "smw_concept_cache_miss": "Het concept \"$1\" kan op het moment niet gebruikt worden, omdat de wiki-configuratie vereist dat deze offline wordt berekend.\nAls het probleem over enige tijd nog niet verholpen is, vraag uw sitebeheerder dan om dit concept beschikbaar te maken.",
+ "smw_noinvannot": "Waarden kunnen niet toegekend worden aan omgekeerde eigenschappen.",
+ "version-semantic": "Semantische uitbreidingen",
+ "smw_baduri": "URI's van het formaat \"$1\" zijn niet toegestaan.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Resultaten tellen",
+ "smw_printername_csv": "Naar CSV exporteren",
+ "smw_printername_dsv": "Naar DSV exporteren",
+ "smw_printername_debug": "Query debuggen (voor experts)",
+ "smw_printername_embedded": "Paginainhoud insluiten",
+ "smw_printername_json": "Naar JSON exporteren",
+ "smw_printername_list": "Lijst",
+ "smw_printername_ol": "Opsomming",
+ "smw_printername_ul": "Specificering",
+ "smw_printername_table": "Tabel",
+ "smw_printername_broadtable": "Brede tabel",
+ "smw_printername_template": "Sjabloon",
+ "smw_printername_templatefile": "Sjabloonbestand",
+ "smw_printername_rdf": "Naar RDF exporteren",
+ "smw_printername_category": "Categorie",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Het maximaal aantal weer te geven resultaten",
+ "smw-paramdesc-offset": "De positie van het eerste resultaat",
+ "smw-paramdesc-headers": "De namen van koppen en eigenschappen weergeven",
+ "smw-paramdesc-mainlabel": "Het label voor de hoofdpaginanaam",
+ "smw-paramdesc-link": "Waarden als koppelingen weergeven",
+ "smw-paramdesc-intro": "De tekst die wordt weergegeven boven de zoekresultaten, als die er zijn",
+ "smw-paramdesc-outro": "De tekst die wordt weergegeven onder de zoekresultaten, als die er zijn",
+ "smw-paramdesc-default": "De tekst die wordt weergegeven als er geen zoekresultaten zijn",
+ "smw-paramdesc-sep": "Het scheidingsteken voor resultaten",
+ "smw-paramdesc-showsep": "Het scheidingsteken bovenaan het CSV-bestand weergeven (\"sep=<waarde>\")",
+ "smw-paramdesc-distribution": "Geef de aantallen waarden weer in plaats van alle waarden.",
+ "smw-paramdesc-distributionsort": "De waardeverdeling sorteren op aantal keer dat die voorkomt.",
+ "smw-paramdesc-distributionlimit": "Beperk de waardeverdeling tot het aantal van sommige waarden.",
+ "smw-paramdesc-aggregation": "Geef aan waaraan de aggregatie moet relateren",
+ "smw-paramdesc-template": "De naam van een sjabloon waarmee de afdrukken moeten worden weergegeven",
+ "smw-paramdesc-columns": "Het aantal kolommen waarin de resultaten weergegeven moeten worden (standaard $1)",
+ "smw-paramdesc-userparam": "Een in iedere sjabloonaanroep doorgegeven waarde, als een sjabloon wordt gebruikt",
+ "smw-paramdesc-introtemplate": "De naam van een boven de zoekresultaten weer te geven sjabloon als er zoekresultaten zijn",
+ "smw-paramdesc-outrotemplate": "De naam van een onder de zoekresultaten weer te geven sjabloon als er zoekresultaten zijn",
+ "smw-paramdesc-embedformat": "Het HTML-element voor het definiëren van sectiekoppen",
+ "smw-paramdesc-embedonly": "Geen koppen weergeven",
+ "smw-paramdesc-table-class": "Een extra CSS-klasse om voor de tabel in te stellen",
+ "smw-paramdesc-table-transpose": "Tabelkoppen verticaal en resultaten horizontaal weergeven",
+ "smw-paramdesc-rdfsyntax": "De te gebruiken RDF-syntaxis",
+ "smw-paramdesc-csv-sep": "Geeft een kolomscheidingsteken aan",
+ "smw-paramdesc-csv-valuesep": "Geeft een waardescheidingsteken aan",
+ "smw-paramdesc-csv-merge": "Voeg rijen en kolomwaardes met een identiek onderwerp-ID (oftewel eerste kolom) samen",
+ "smw-paramdesc-csv-bom": "Voeg een BOM (teken om endianness aan te geven) toe aan het begin van het uitvoerbestand",
+ "smw-paramdesc-dsv-separator": "Het te gebruiken scheidingsteken",
+ "smw-paramdesc-dsv-filename": "De naam voor het DSV-bestand",
+ "smw-paramdesc-filename": "De naam voor het uitvoerbestand",
+ "smw-smwdoc-description": "Geeft een tabel weer met alle parameters die kunnen worden gebruikt voor de opgegeven resultaatopmaak samen met standaardwaarden en beschrijvingen.",
+ "smw-smwdoc-par-format": "De resultaatopmaak waarvoor parameterdocumentatie weergegeven moet worden.",
+ "smw-smwdoc-par-parameters": "Welke parameters weer te geven; \"specific\" voor degene toegevoegd door de opmaak, \"base\" voor degene die beschikbaar zijn in alle opmaken, en \"all\" voor beide.",
+ "smw-paramdesc-sort": "Eigenschap om de zoekopdracht op te sorteren",
+ "smw-paramdesc-order": "Sorteervolgorde",
+ "smw-paramdesc-searchlabel": "Tekst voor het voortzetten van de zoekopdracht",
+ "smw-paramdesc-named_args": "Noem de aan de sjabloon door te geven parameters",
+ "smw-paramdesc-template-arguments": "Stelt in hoe de benoemde argumenten aan de sjabloon worden doorgegeven",
+ "smw-paramdesc-import-annotation": "Aanvullende geannoteerde gegevens zullen gekopieerd worden tijdens het parseren van een onderwerp",
+ "smw-paramdesc-export": "Exportoptie",
+ "smw-paramdesc-prettyprint": "Een netjes opgemaakte uitvoer inschakelen die tabs en nieuwe regels toevoegt",
+ "smw-paramdesc-json-unescape": "De uitvoer bevat slashes en multibyte Unicode-tekens die niet voorafgegaan worden door een escape",
+ "smw-paramdesc-json-type": "Serialisatietype",
+ "smw-paramdesc-source": "Alternatieve zoekopdrachtbron",
+ "smw-paramdesc-jsonsyntax": "Te gebruiken JSON-syntaxis",
+ "smw-printername-feed": "RSS- en Atom-feed",
+ "smw-paramdesc-feedtype": "Feedtype",
+ "smw-paramdesc-feedtitle": "De tekst die als titel van de feed moet worden gebruikt",
+ "smw-paramdesc-feeddescription": "De tekst die als beschrijving van de feed moet worden gebruikt",
+ "smw-paramdesc-feedpagecontent": "In de feed weer te geven paginainhoud",
+ "smw-label-feed-description": "$2-feed $1",
+ "smw_iq_disabled": "Zoekopdrachten binnen tekst zijn uitgeschakeld in deze wiki.",
+ "smw_iq_moreresults": "... meer resultaten",
+ "smw_parseerror": "De opgegeven waarde is niet begrepen.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "\"$1\" kan in deze wiki niet als paginanaam gebruikt worden.",
+ "smw_noproperty": "\"$1\" kan in deze wiki niet als eigenschapsnaam gebruikt worden.",
+ "smw_wrong_namespace": "Hier zijn alleen pagina's in naamruimte \"$1\" toegestaan.",
+ "smw_manytypes": "Meer dan één type gedefinieerd voor eigenschap.",
+ "smw_emptystring": "Lege strings zijn niet toegestaan.",
+ "smw_notinenum": "\"$1\" komt niet voor in de lijst ($2) van [[Property:Allows value|mogelijke waarden]] voor de eigenschap \"$3\".",
+ "smw_noboolean": "\"$1\" is niet herkend als een booleaanse waarde (waar/onwaar).",
+ "smw_true_words": "waar,w,ja,j,true",
+ "smw_false_words": "onwaar,o,nee,n,false",
+ "smw_nofloat": "\"$1\" is geen getal.",
+ "smw_infinite": "Getallen zo groot als \"$1\" worden niet ondersteund.",
+ "smw_unitnotallowed": "\"$1\" is niet ingesteld als een geldige meeteenheid voor deze eigenschap.",
+ "smw_nounitsdeclared": "Er zijn geen meeteenheden opgegeven voor deze eigenschap.",
+ "smw_novalues": "Geen waarden opgegeven.",
+ "smw_nodatetime": "De datum \"$1\" kon niet verwerkt worden.",
+ "smw_toomanyclosing": "\"$1\" lijkt te vaak voor te komen in de zoekopdracht.",
+ "smw_noclosingbrackets": "Ergens in uw zoekopdracht was \"<nowiki>[[</nowiki>\" niet afgesloten door een bijbehorende \"]]\".",
+ "smw_misplacedsymbol": "Het symbool \"$1\" is gebruikt op een plaats waar het niet gebruikt hoort te worden.",
+ "smw_unexpectedpart": "Het deel \"$1\" van de zoekopdracht is niet begrepen.\nDe resultaten kunnen afwijken van de verwachting.",
+ "smw_emptysubquery": "Er is een subzoekopdracht met een onjuiste conditie.",
+ "smw_misplacedsubquery": "Er is een subzoekopdracht gebruikt op een plaats waar subzoekopdrachten niet gebruikt mogen worden.",
+ "smw_valuesubquery": "Subzoekopdrachten worden niet ondersteund voor waarden van de eigenschap \"$1\".",
+ "smw_badqueryatom": "Een onderdeel \"<nowiki>[[...]]</nowiki>\" van de zoekopdracht is niet begrepen.",
+ "smw_propvalueproblem": "De waarde van eigenschap \"$1\" is niet begrepen.",
+ "smw_noqueryfeature": "Een bepaalde vraagoptie wordt niet ondersteund in deze wiki en een deel van de zoekopdracht is genegeerd ($1).",
+ "smw_noconjunctions": "Verbindingen in zoekopdrachten worden in deze wiki niet ondersteund en een deel van de zoekopdracht is genegeerd ($1).",
+ "smw_nodisjunctions": "Scheidingen in zoekopdrachten worden niet ondersteund in deze wiki, dus een deel van de zoekopdracht is genegeerd ($1).",
+ "smw_querytoolarge": "De volgende {{PLURAL:$2|zoekopdrachtconditie is|$2 zoekopdrachtcondities zijn}} niet in acht genomen vanwege beperkingen in de grootte of diepte van zoekopdrachten in deze wiki: <code>$1</code>.",
+ "smw_notemplategiven": "Geef een waarde voor de parameter \"sjabloon\" op om deze zoekopdracht te laten werken.",
+ "smw_db_sparqlqueryproblem": "Er is geen antwoord op de zoekopdracht gekomen uit de SPARQL-database. Deze fout kan tijdelijk zijn of wijzen op een bug in de databasesoftware.",
+ "smw_db_sparqlqueryincomplete": "De zoekopdracht beantwoorden bleek te moeilijk en deze is afgebroken. Sommige resultaten kunnen ontbreken. Probeer een eenvoudiger zoekopdracht te gebruiken.",
+ "smw_type_header": "Eigenschappen van het type \"$1\"",
+ "smw_typearticlecount": "{{PLURAL:$1|Eén eigenschap gebruikt|$1 eigenschappen gebruiken}} dit type.",
+ "smw_attribute_header": "Pagina's die de eigenschap \"$1\" gebruiken",
+ "smw_attributearticlecount": "{{PLURAL:$1|Eén pagina gebruikt|$1 pagina's gebruiken}} deze eigenschap.",
+ "smw-propertylist-subproperty-header": "Subeigenschappen",
+ "smw-propertylist-redirect-header": "Synoniemen",
+ "smw-propertylist-error-header": "Pagina's met onjuiste toewijzigen",
+ "smw-propertylist-count": "$1 gerelateerde {{PLURAL:$1|entiteit|entiteiten}} weergegeven.",
+ "smw-propertylist-count-with-restricted-note": "$1 gerelateerde {{PLURAL:$1|entiteit|entiteiten}} weergegeven (er zijn er meer beschikbaar, maar de weergave is beperkt tot \"$2\").",
+ "smw-propertylist-count-more-available": "$1 gerelateerde {{PLURAL:$1|entiteit|entiteiten}} weergegeven (er zijn er meer beschikbaar).",
+ "exportrdf": "Pagina's exporteren naar RDF",
+ "smw_exportrdf_docu": "Deze pagina maakt het mogelijk gegevens te verkrijgen van een pagina in RDF-formaat.\nOm pagina's te exporteren, voert u in het onderstaande invoerveld paginanamen in, één paginanaam per regel.",
+ "smw_exportrdf_recursive": "Alle gerelateerde pagina's recursief exporteren.\nHet resultaat kan groot zijn!",
+ "smw_exportrdf_backlinks": "Ook alle pagina's exporteren die verwijzen naar de te exporteren pagina's.\nGenereert doorbladerbare RDF.",
+ "smw_exportrdf_lastdate": "Pagina's die sinds het opgegeven tijdstip niet gewijzigd zijn, niet exporteren.",
+ "smw_exportrdf_submit": "Exporteren",
+ "uriresolver": "URI-resolver",
+ "properties": "Eigenschappen",
+ "smw_properties_docu": "De volgende eigenschappen worden in de wiki gebruikt.",
+ "smw_property_template": "$1 van type $2 ($3 keer {{PLURAL:$3|gebruikt}})",
+ "smw_propertylackspage": "Alle eigenschappen moeten op een pagina beschreven worden!",
+ "smw_propertylackstype": "Er is geen type opgegeven voor deze eigenschap (type $1 wordt verondersteld).",
+ "smw_propertyhardlyused": "Deze eigenschap wordt in de wiki vrijwel niet gebruikt!",
+ "smw-property-name-invalid": "Eigenschap $1 kan niet gebruikt worden (ongeldige eigenschapsnaam).",
+ "smw-property-name-reserved": "\"$1\" werd vermeld als gereserveerde naam en mag niet worden gebruikt als een eigenschap. De volgende [https://www.semantic-mediawiki.org/wiki/Help:Property_naming helppagina] kan informatie bevatten over waarom deze naam was gereserveerd.",
+ "smw-sp-property-searchform": "Eigenschappen weergeven die de volgende tekst bevatten:",
+ "smw-sp-property-searchform-inputinfo": "De invoer is hoofdlettergevoelig, en als deze wordt gebruikt voor filteren dan worden alleen eigenschappen weergegeven die overeenkomen met de voorwaarde.",
+ "smw-special-property-searchform": "Eigenschappen weergeven die de volgende waarde bevatten:",
+ "smw-special-property-searchform-inputinfo": "De invoer is hoofdlettergevoelig, en als deze wordt gebruikt voor filteren dan worden alleen eigenschappen weergegeven die overeenkomen met de voorwaarde.",
+ "smw-special-property-searchform-options": "Opties",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "Geen",
+ "smw-special-wantedproperties-filter-unapproved": "Niet goedgekeurd",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Filteroptie die wordt gebruikt in verband met de autorisatiemodus.",
+ "concepts": "Concepten",
+ "smw-special-concept-docu": "Een [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] kan gezien worden als \"dynamische categorie\", dat wil zeggen als een verzameling pagina's die niet handmatig zijn aangemaakt, maar zijn berekend door Semantic MediaWiki uit een beschrijving van een gegeven zoekopdracht.",
+ "smw-special-concept-header": "Lijst van concepten",
+ "smw-special-concept-count": "{{PLURAL:$1|Het volgende concept wordt|De volgende $1 concepten worden}} weergegeven.",
+ "smw-special-concept-empty": "Er is geen concept gevonden.",
+ "unusedproperties": "Ongebruikte eigenschappen",
+ "smw-unusedproperties-docu": "Op deze pagina vindt u [https://www.semantic-mediawiki.org/wiki/Unused_properties ongebruikte eigenschappen] die bestaan hoewel ze op geen andere pagina gebruikt worden. Voor een andere weergave, zie de speciale pagina's met [[Special:Properties|alle]] of [[Special:WantedProperties|gewenste eigenschappen]].",
+ "smw-unusedproperty-template": "$1 van type $2",
+ "wantedproperties": "Gewenste eigenschappen",
+ "smw-wantedproperties-docu": "Op deze pagina vindt u [https://www.semantic-mediawiki.org/wiki/Wanted_properties gewenste eigenschappen] die in de wiki worden gebruikt, maar geen pagina hebben waarop ze worden beschreven. Voor een andere weergave, zie de speciale pagina's met [[Special:Properties|alle]] of [[Special:UnusedProperties|ongebruikte eigenschappen]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|keer|keren}} gebruikt)",
+ "smw-special-wantedproperties-docu": "Op deze pagina vindt u [https://www.semantic-mediawiki.org/wiki/Wanted_properties gewenste eigenschappen] die in de wiki worden gebruikt, maar geen pagina hebben waarop ze worden beschreven. Voor een gedifferentieerde weergave, zie de speciale pagina's met [[Special:Properties|alle]] of [[Special:UnusedProperties|ongebruikte eigenschappen]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|keer|keren}} gebruikt)",
+ "smw_purge": "Vernieuwen",
+ "smw-purge-failed": "Vernieuwen is mislukt",
+ "types": "Types",
+ "smw_types_docu": "Lijst van [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes beschikbare datatypes] waarvan elk [https://www.semantic-mediawiki.org/wiki/Help:Datatype type] een unieke set attributen vertegenwoordigt om een waarde te beschrijven in termen van opslag en weergavekenmerken die erfelijk zijn voor een toegewezen eigenschap.",
+ "smw-special-types-no-such-type": "Het opgegeven datatype bestaat niet",
+ "smw-statistics": "Semantische statistieken",
+ "smw-statistics-property-instance": "Eigenschapswaarde{{PLURAL:$1||n}} (totaal)",
+ "smw-statistics-property-total": "[[Special:Properties|Eigenschap{{PLURAL:$1||pen}}]] (totaal)",
+ "smw-statistics-property-total-legacy": "Eigenschap{{PLURAL:$1||pen}} (totaal)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Eigenschap|Eigenschappen}}]] (gebruikt met ten minste één waarde)",
+ "smw-statistics-property-page": "Eigenschap{{PLURAL:$1||pen}} (geregistreerd bij een pagina)",
+ "smw-statistics-property-type": "Eigenschap{{PLURAL:$1||pen}} (toegewezen aan een datatype)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Zoekopdracht|Zoekopdrachten}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Zoekopdracht|Zoekopdrachten}}]]",
+ "smw-statistics-query-size": "Grootte van zoekopdracht",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concept|Concepten}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concept|Concepten}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobject|Subobjecten}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobject|Subobjecten}}",
+ "smw-statistics-datatype-count": "[[Special:Types|Datatype{{PLURAL:$1||s}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Eigenschapswaarde|Eigenschapswaarden}} ([[Special:ProcessingErrorList|{{PLURAL:$1|ongeldige annotatie|ongeldige annotaties}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Eigenschapswaarde|Eigenschapswaarden}} ({{PLURAL:$1|verkeerde annotatie|verkeerde annotaties}})",
+ "smw-statistics-delete-count": "Verouderde {{PLURAL:$1|entiteit|entiteiten}} (gemarkeerd voor verwijdering)",
+ "smw_uri_doc": "De URI-resolver implementeert de [$1 W3C TAG finding on httpRange-14].\nDit zorgt ervoor dat mensen niet veranderen in websites.",
+ "ask": "Semantisch zoeken",
+ "smw_ask_sortby": "Sorteren op kolom (optioneel)",
+ "smw_ask_ascorder": "Oplopend",
+ "smw_ask_descorder": "Aflopend",
+ "smw-ask-order-rand": "Willekeurig",
+ "smw_ask_submit": "Resultaten zoeken",
+ "smw_ask_editquery": "Zoekopdracht bewerken",
+ "smw_add_sortcondition": "[Sorteervoorwaarde toevoegen]",
+ "smw-ask-sort-add-action": "Sorteervoorwaarde toevoegen",
+ "smw_ask_hidequery": "Zoekopdracht verbergen",
+ "smw_ask_help": "Hulp bij zoekopdrachten",
+ "smw_ask_queryhead": "Voorwaarde",
+ "smw_ask_printhead": "Print selectie",
+ "smw_ask_printdesc": "(voeg één eigenschapsnaam per regel toe)",
+ "smw_ask_format_as": "Opmaken als:",
+ "smw_ask_defaultformat": "standaard",
+ "smw_ask_otheroptions": "Andere opties",
+ "smw-ask-otheroptions-info": "Dit onderdeel bevat opties die afdrukinstructies wijzigen. Parameterbeschrijvingen worden zichtbaar door de muisaanwijzer erover te bewegen.",
+ "smw-ask-otheroptions-collapsed-info": "Gebruik het plusicoon om alle beschikbare opties te bekijken",
+ "smw_ask_show_embed": "Insluitcode weergeven",
+ "smw_ask_hide_embed": "Insluitcode verbergen",
+ "smw_ask_embed_instr": "Gebruik de onderstaande code om deze zoekopdracht op te nemen in een wikipagina.",
+ "smw-ask-delete": "Verwijderen",
+ "smw-ask-sorting": "Sortering",
+ "smw-ask-options": "Opties",
+ "smw-ask-options-sort": "Sorteeropties",
+ "smw-ask-parameters": "Parameters",
+ "smw-ask-search": "Zoeken",
+ "smw-ask-debug": "Debuggen",
+ "smw-ask-debug-desc": "Genereert debuginformatie voor zoekopdrachten",
+ "smw-ask-no-cache": "Geen cache",
+ "smw-ask-no-cache-desc": "Resultaten zonder querycache",
+ "smw-ask-result": "Resultaat",
+ "smw-ask-empty": "Alle vermeldingen wissen",
+ "smw-ask-download-link-desc": "Download opgevraagde resultaten in $1-formaat",
+ "smw-ask-format": "Indeling",
+ "smw-ask-format-selection-help": "Hulp voor het geselecteerde formaat: $1.",
+ "smw-ask-condition-input-assistance": "Er is zoekondersteuning beschikbaar voor de volgende voorvoegsels:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> eigenschapbevraging",
+ "smw-ask-condition-input-assistance-category": "<code>p:</code> categoriebevraging",
+ "smw-ask-condition-input-assistance-concept": "<code>p:</code> conceptbevraging",
+ "smw-ask-query-search-info": "De zoekopdracht <code><nowiki>$1</nowiki></code> is beantwoord door de {{PLURAL:$3|1=<code>$2</code> (uit de cache)|<code>$2</code> (uit de cache)|<code>$2</code>}} in $4 {{PLURAL:$4|seconde|seconden}}.",
+ "searchbyproperty": "Op eigenschap zoeken",
+ "processingerrorlist": "Lijst met verwerkingsfouten",
+ "smw-processingerrorlist-intro": "De volgende lijst biedt een overzicht van verwerkingsfouten die in verband met [https://www.semantic-mediawiki.org/ Semantic MediaWiki] zijn opgetreden. Het is aanbevolen om deze lijst met enige regelmaat te controleren en om ongeldige waardeannotaties te corrigeren.",
+ "smw_sbv_docu": "Alle pagina's zoeken die een bepaalde eigenschap en waarde hebben.",
+ "smw_sbv_novalue": "Voer een geldige waarde in voor de eigenschap, of bekijk alle waarden voor eigenschap \"$1.\"",
+ "smw_sbv_displayresult": "Een lijst met alle pagina's waarop eigenschap \"$1\" de waarde \"$2\" heeft",
+ "smw_sbv_displayresultfuzzy": "Een lijst van alle pagina's met de eigenschap \"$1\" met waarde \"$2\".\nOmdat er een beperkt aantal resultaten is, worden ook nabije waarden weergegeven.",
+ "smw_sbv_property": "Eigenschap:",
+ "smw_sbv_value": "Waarde:",
+ "smw_sbv_submit": "Resultaten zoeken",
+ "browse": "Wiki bekijken",
+ "smw_browselink": "Eigenschappen bekijken",
+ "smw_browse_article": "Voer de naam in van de pagina waar u met browsen wilt beginnen.",
+ "smw_browse_go": "OK",
+ "smw_browse_show_incoming": "eigenschappen weergeven die hierheen verwijzen",
+ "smw_browse_hide_incoming": "eigenschappen verbergen die hierheen verwijzen",
+ "smw_browse_no_outgoing": "Deze pagina heeft geen eigenschappen.",
+ "smw_browse_no_incoming": "Er verwijzen geen eigenschappen naar deze pagina.",
+ "smw-browse-from-backend": "Informatie wordt momenteel uit de backend gehaald.",
+ "smw-browse-intro": "Deze pagina bevat informatie over een subject of instantie van een entiteit. Voer de naam in van een te inspecteren object.",
+ "smw-browse-invalid-subject": "De onderwerpvalidatie gaf een \"$1\"-fout terug.",
+ "smw-browse-api-subject-serialization-invalid": "Het onderwerp heeft een ongeldige serialisatie-formaat.",
+ "smw-browse-js-disabled": "Het vermoeden bestaat dat Javascript is uitgeschakeld of niet beschikbaar is. We raden aan gebruik te maken van een browser die JavaScript ondersteunt. Andere mogelijkheden worden besproken op de [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>]-configuratieparameterpagina.",
+ "smw-browse-show-group": "Toon groepen",
+ "smw-browse-hide-group": "Verberg groepen",
+ "smw_inverse_label_default": "$1 van",
+ "smw_inverse_label_property": "Tegenovergesteld eigenschapslabel",
+ "pageproperty": "Paginaeigenschap zoeken",
+ "smw_pp_docu": "Zoek naar alle fillers voor een eigenschap op een gegeven pagina.\nVoer zowel een pagina als een eigenschap in.",
+ "smw_pp_from": "Van pagina:",
+ "smw_pp_type": "Eigenschap:",
+ "smw_pp_submit": "Resultaten zoeken",
+ "smw_result_prev": "Vorige",
+ "smw_result_next": "Volgende",
+ "smw_result_results": "Resultaten",
+ "smw_result_noresults": "Geen resultaten.",
+ "smwadmin": "Beheer- en onderhoudsonderdelen",
+ "smw-admin-statistics-job-title": "Taakstatistieken",
+ "smw-admin-statistics-job-docu": "De taakstatistieken tonen informatie over geplande Semantic MediaWiki-taken die nog niet zijn uitgevoerd. Het aantal taken kan enigszins afwijken of mislukte pogingen bevatten. Raadpleeg de [https://www.mediawiki.org/wiki/Manual:Job_queue handleiding] voor meer informatie.",
+ "smw-admin-statistics-querycache-title": "Querycachestatistieken",
+ "smw-admin-statistics-querycache-disabled": "De [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] is niet ingeschakeld op deze wiki en derhalve zijn geen statistieken beschikbaar.",
+ "smw-admin-permission-missing": "De toegang tot deze pagina is geblokkeerd wegens het ontbreken van de juiste rechten. Raadpleeg de [https://www.semantic-mediawiki.org/wiki/Help:Permissions rechten] help-pagina voor meer informatie over de vereiste instellingen.",
+ "smw-admin-setupsuccess": "De opslagmodule is ingesteld.",
+ "smw_smwadmin_return": "Terug naar $1",
+ "smw_smwadmin_updatestarted": "Er is een proces gestart voor het bijwerken van de semantische gegevens.\nAlle opgeslagen gegevens worden opnieuw opgebouwd of gerepareerd als dat nodig is.\nU kunt de voortgang volgen via deze speciale pagina.",
+ "smw_smwadmin_updatenotstarted": "Er loopt al een bijwerkproces.\nEr wordt geen nieuw proces gestart.",
+ "smw_smwadmin_updatestopped": "Alle lopende bijwerkprocessen zijn afgebroken.",
+ "smw_smwadmin_updatenotstopped": "Om de lopende processen te stoppen, moet u het vinkje inschakelen om aan te geven dat u het zeker weet.",
+ "smw-admin-docu": "Deze speciale pagina assisteert u tijdens de installatie en het bijwerken van <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a> en biedt ook verdere administratieve functies en taken, alsook statistieken.\nDenk eraan een back-up te maken van uw waardevolle gegevens voordat u beheershandelingen uitvoert.",
+ "smw-admin-environment": "Software omgeving",
+ "smw-admin-db": "Database-onderhoud",
+ "smw-admin-dbdocu": "Semantic MediaWiki heeft een aantal uitbreidingen op de MediaWiki-database nodig om de semantische gegevens op te kunnen slaan.\nDe onderstaande functionaliteit zorgt ervoor dat uw database juist is ingesteld.\nDe wijzigingen die in deze stap worden gemaakt, hebben geen invloed op de rest van de MediaWiki-database en zijn desgewenst eenvoudig ongedaan te maken.\nDeze instelling kan meerdere keren worden uitgevoerd zonder schade aan te richten, maar is alleen nodig bij installatie of upgrade.",
+ "smw-admin-permissionswarn": "Als de handeling mislukt en er SQL-foutmeldingen worden weergegeven, heeft de databasegebruiker van uw wiki waarschijnlijk onvoldoende rechten (zie het \"LocalSettings.php\"-bestand).\nGeef deze gebruiker de benodigde extra rechten om tabellen aan te maken en te verwijderen, zet tijdelijk de aanmeldgegevens van de hoofdgebruiker (root) van uw databasesysteem in het \"LocalSettings.php\"-bestand, of gebruik het beheerscript <code>setupStore.php</code> dat de gebruikersnaam en wachtwoord van een beheerder kan gebruiken.",
+ "smw-admin-dbbutton": "Tabellen initialiseren of bijwerken",
+ "smw-admin-announce": "Uw wiki aankondigen",
+ "smw-admin-announce-text": "Als uw wiki openbaar is, kunt u deze registreren op <a href=\"https://wikiapiary.com\">WikiApiary</a>, de wiki tracking wiki.",
+ "smw-admin-deprecation-notice-title": "Verouderingsmeldingen",
+ "smw-admin-deprecation-notice-title-notice": "Toekomstinge veranderingen",
+ "smw-admin-deprecation-notice-title-replacement": "Vervangen of hernoemde instellingen",
+ "smw-admin-deprecation-notice-title-removal": "Gewijzigde instellingen",
+ "smw-smwadmin-refresh-title": "Gegevens repareren en bijwerken",
+ "smw_smwadmin_datarefresh": "Gegevens opnieuw opbouwen",
+ "smw_smwadmin_datarefreshdocu": "Het is mogelijk om alle gegevens van Semantic MediaWiki opnieuw te genereren op basis van de huidige inhoud van de wiki.\nDit kan handig zijn om gegevens te repareren of de gegevens te vernieuwen als de interne opmaak gewijzigd is bij een softwareupdate.\nDe gegevens worden pagina voor pagina bijgewerkt en het kan enige tijd duren voor de taak is afgerond.\nHieronder wordt weergegeven of er op dit moment een taak loopt en stelt u in staat een taak te stoppen of te starten (tenzij deze mogelijkheid door de sitebeheerder is uitgeschaked).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Er loopt een bijwerktaak</strong>\nHet is normaal dat de voortgang langzaam is omdat de gegevens ververst worden in kleine porties iedere keer als een gebruiker de wiki raadpleegt.\nOm het bijwerken sneller te laten verlopen, kunt u het beheerscript <code>runJobs.php</code> draaien. Gebruik de optie <code>--maxjobs 1000</code> om aan het aantal bij te werken pagina's per handeling te beperken.\nGeschatte voortgang van de huidige taak:",
+ "smw_smwadmin_datarefreshbutton": "Gegevensherstel plannen",
+ "smw_smwadmin_datarefreshstop": "Bijwerken afbreken",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, ik weet het {{GENDER:$1|zeker}}.",
+ "smw-admin-job-scheduler-note": "Meeste activiteiten in deze paragraaf worden als taak uitgevoerd om dodelijke omarmingen uit de weg te gaan. De [https://www.mediawiki.org/wiki/Manual:Job_queue werkvoorbereider] is verantwoordelijk voor de doorgang en het luistert nauw dat 't onderhoudsscript <code>runJobs.php</code> (zie ook <code>$wgRunJobsAsync</code>) voldoende capaciteit heeft.",
+ "smw-admin-outdateddisposal-active": "Verouderde zaken afvoertaak gepland.",
+ "smw-admin-propertystatistics-title": "Eigenschapstatistieken opnieuw opbouwen",
+ "smw-admin-propertystatistics-active": "Een taak om statistieken opnieuw op te bouwen is aan de wachtrij toegevoegd.",
+ "smw-admin-propertystatistics-button": "Inplannen statistieken opnieuw opbouwen",
+ "smw-admin-fulltext-title": "Opnieuw opbouwen van zoeken in volledige tekst",
+ "smw-admin-fulltext-intro": "Bouwt opnieuw de zoekindex op van eigenschaptabellen waarop een ''[https://www.semantic-mediawiki.org/wiki/Full-text zoeken in volledige tekst]''-datatype is ingeschakeld. Wijzigingen in de indexregels (gewijzigde stopwoorden, nieuwe ''stemmer'', enz.) en/of een nieuw toegevoegde of gewijzigde tabel vereisen het opnieuw uitvoeren van deze taak.",
+ "smw-admin-fulltext-active": "Een taak om het zoeken in volledige tekst opnieuw op te bouwen is aan de wachtrij toegevoegd.",
+ "smw-admin-fulltext-button": "Opnieuw opbouwen van zoeken in volledige tekst aan wachtrij toevoegen",
+ "smw-admin-support": "Ondersteuning krijgen",
+ "smw-admin-supportdocu": "Er zijn verscheidene bronnen beschikbaar om u te ondersteunen als u problemen ondervindt:",
+ "smw-admin-installfile": "Als u problemen ondervindt bij uw installatie, controleer dan eerst de richtlijnen in het bestand <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">INSTALL</a> en de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">installatiepagina</a>.",
+ "smw-admin-smwhomepage": "De volledige gebruikersdocumentatie voor Semantic MediaWiki is te vinden op <b><a href=\"http://semantic-mediawiki.org/wiki/Semantic_MediaWiki_-_startpagina\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Problemen en suggesties kunt u melden op de <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">issuetracker</a>, de <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">reporting bugs</a>-pagina geeft wat richlijnen om een efficiënte issuebeschrijving op te stellen.",
+ "smw-admin-questions": "Als u verdere vragen of suggesties hebt, neem dan deel aan het overleg op het <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">Semantic MediaWiki gebruikersforum</a>.",
+ "smw-admin-other-functions": "Andere functies",
+ "smw-admin-supplementary-section-title": "Aanvullende functies",
+ "smw-admin-supplementary-section-subtitle": "Beschikbare functies",
+ "smw-admin-supplementary-settings-title": "Configuratie-instellingen",
+ "smw-admin-supplementary-operational-statistics-title": "Operationele statistieken",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Cachestatistieken",
+ "smw-admin-supplementary-elastic-functions": "Beschikbare functies",
+ "smw-admin-supplementary-elastic-settings-title": "Instellingen",
+ "smw-admin-supplementary-elastic-mappings-summary": "Samenvatting",
+ "smw-admin-supplementary-elastic-indices-title": "Indices",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistieken",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Verversinterval: $1",
+ "smw-property-label-similarity-threshold": "Drempelwaarde:",
+ "smw-property-label-similarity-type": "Typenummer weergeven",
+ "smw-property-label-similarity-noresult": "Er zijn geen resultaten gevonden voor de geselecteerde opties.",
+ "smw_adminlinks_datastructure": "Gegevensstructuur",
+ "smw_adminlinks_displayingdata": "Gegevens weergeven",
+ "smw_adminlinks_inlinequerieshelp": "Hulp bij inline zoekopdrachten",
+ "smw-page-indicator-usage-count": "Geschatte [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count aantal keren gebruikt]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|Gebruiker|Systeem}} gedefinieerde eigenschap",
+ "smw-property-indicator-last-count-update": "Geschat gebruiksaantal\nLaatst bijgewerkt: $1",
+ "smw-createproperty-isproperty": "Het is een eigenschap van het type $1.",
+ "smw-createproperty-allowedvals": "De toegelaten {{PLURAL:$1|waarde voor deze eigenschap is|waarden voor deze eigenschap zijn}}:",
+ "smw-paramdesc-category-delim": "Het scheidingsteken",
+ "smw-paramdesc-category-template": "Een sjabloon om de items mee op te maken",
+ "smw-paramdesc-category-userparam": "Een aan de sjabloon door te geven parameter",
+ "smw-info-par-message": "Weer te geven bericht.",
+ "smw-info-par-icon": "Pictogram dat \"informatie\" of \"waarschuwing\" aangeeft.",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Algemene opties",
+ "prefs-ask-options": "Special:Ask opties",
+ "smw-prefs-intro-text": "De onderstaande opties zijn mogelijk via [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (of gerelateerde uitbreidingen) om individuele of aangepaste functies in te schakelen. Ga naar de [https://www.semantic-mediawiki.org/wiki/Help:User_preferences hulppagina] voor meer informatie.",
+ "smw-prefs-ask-options-tooltip-display": "Parametertekst weergeven als infotooltip",
+ "smw-ui-tooltip-title-property": "Eigenschap",
+ "smw-ui-tooltip-title-quantity": "Eenheidomzetting",
+ "smw-ui-tooltip-title-info": "Gegevens",
+ "smw-ui-tooltip-title-service": "Dienstenkoppelingen",
+ "smw-ui-tooltip-title-warning": "Waarschuwing",
+ "smw-ui-tooltip-title-error": "Fout",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Evenement",
+ "smw-ui-tooltip-title-note": "Opmerking",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Referentie",
+ "smw_unknowntype": "Het type \"$1\" van deze eigenschap is ongeldig",
+ "smw-concept-cache-text": "Het concept heeft in totaal $1 {{PLURAL:$1|pagina|pagina's}} en is voor het laatst bijgewerkt op $3, $2.",
+ "smw_concept_header": "Pagina's met het concept \"$1\"",
+ "smw_conceptarticlecount": "Hieronder {{PLURAL:$1|wordt $1 pagina|worden $1 pagina's}} weergegeven.",
+ "smw-qp-empty-data": "De gevraagde gegevens kunnen niet worden weergegeven vanwege enkele onvoldoende selectiecriteria.",
+ "right-smw-admin": "Semantic MediaWiki beheren",
+ "action-smw-patternedit": "reguliere expressies gebruikt door Semantic MediaWiki te bewerken",
+ "group-smwadministrator": "Beheerders (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|beheerder (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Beheerders_(Semantic_MediaWiki)",
+ "group-smwcurator": "Curatoren (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|curator (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Curatoren (Semantic MediaWiki)",
+ "action-smw-admin": "Semantic MediaWiki te beheren",
+ "smw-property-predefined-default": "\"$1\" is een vooraf gedefinieerde eigenschap.",
+ "smw-property-predefined-common": "Deze eigenschap is van te voren gedefinieerd (ook bekend als een [https://www.semantic-mediawiki.org/wiki/Help:Special_properties speciale eigenschap]) en komt met extra beheersprivileges, maar kan worden gebruikt net als elk andere [https://www.semantic-mediawiki.org/wiki/Property door de gebruiker gedefinieerde eigenschap].",
+ "smw-property-predefined-ask": "\"$1\" is een vooraf gedefinieerde eigenschap die staat voor meta-informatie (in de vorm van een [https://www.semantic-mediawiki.org/wiki/Subobject subobject]) over individuele zoekopdrachten en wordt geleverd bij [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "\"$1\" is een voorgedefinieerde eigenschap die het aantal voorwaarden dat wordt gebruikt in een zoekopdracht verzamelt en is beschikbaar gesteld door [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" is een voorgedefinieerde eigenschap die de diepte van een zoekopdracht aangeeft en is beschikbaar gesteld door [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "Het is een numerieke waarde die is berekend op basis van geneste zoekopdrachten, eigenschapreeksen, en beschikbare beschrijvingselementen waarbij de uitvoer van de zoekopdracht beperkt wordt door de instelling <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-sp-properties-docu": "Op deze pagina wordt een lijst van [https://www.semantic-mediawiki.org/wiki/Property eigenschappen] getoond die beschikbaar zijn op deze wiki, met tellingen van het aantal keren dat ze gebruikt worden. Voor actuele tellingen wordt aangeraden het onderhoudsscript voor [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics eigenschapstatistieken] regelmatig uit te voeren. Voor een andere weergave, zie de speciale pagina's [[Special:UnusedProperties|ongebruikte eigenschappen]] of [[Special:WantedProperties|gewenste eigenschappen]].",
+ "smw-sp-properties-cache-info": "Deze gegevens zijn overgenomen uit een [https://www.semantic-mediawiki.org/wiki/Caching cache] en zijn voor het laatst bijgewerkt op $1.",
+ "smw-sp-properties-header-label": "Lijst met eigenschappen",
+ "smw-admin-settings-docu": "Geeft een lijst van alle standaard en lokale instellingen die relevant zijn voor de Semantic MediaWiki-omgeving. Raadpleeg de help-pagina [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuratie] voor meer informatie over afzonderlijke instellingen.",
+ "smw-sp-admin-settings-button": "Genereren van de lijst met instellingen",
+ "smw-admin-idlookup-title": "Objectid opzoeken",
+ "smw-admin-idlookup-docu": "Geeft details weer over een intern objectid dat een individuele entiteit (wikipagina, subobject, eigenschap, enz.) in Semantic MediaWiki representeert. Het id dient niet te worden verward met het id van MediaWiki-pagina's of -revisies.",
+ "smw-admin-idlookup-input": "Zoeken:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Overzicht",
+ "smw-admin-tab-registry": "Register",
+ "smw-livepreview-loading": "Bezig met laden…",
+ "smw-sp-searchbyproperty-description": "Deze pagina biedt een eenvoudige [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces bladerinteractie] voor het vinden van entiteiten met een eigenschap met een bepaalde waarde. Andere beschikbare zoekinteracties zijn de [[Special:PageProperty|zoekpagina voor pagina-eigenschappen]] en de [[Special:Ask|querybouwer]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lijst van resultaten",
+ "smw-sp-searchbyproperty-nonvaluequery": "Een lijst van waarden die aan de eigenschap \"$1\" zijn toegekend.",
+ "smw-sp-searchbyproperty-valuequery": "Een lijst van pagina's waarop de eigenschap ''$1'' de waarde ''$2'' heeft.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" kan niet worden toegewezen aan een getaltype met waarde $2.",
+ "smw-search-profile": "Uitgebreid",
+ "smw-search-profile-sort-recent": "Meest recent",
+ "smw-search-profile-sort-title": "Titel",
+ "smw-search-profile-extended-help-query-link": "(Voor meer details $1).",
+ "smw-search-profile-extended-help-find-forms": "beschikbare formulieren",
+ "smw-search-profile-extended-section-sort": "Sorteren op",
+ "smw-search-profile-extended-section-form": "Formulieren",
+ "smw-search-profile-extended-section-namespace": "Naamruimte",
+ "smw-search-show": "Weergeven",
+ "smw-search-hide": "Verbergen",
+ "log-name-smw": "Logboek Semantic MediaWiki",
+ "log-show-hide-smw": "$1 Semantic MediaWiki-logboek",
+ "logeventslist-smw-log": "Logboek Semantic MediaWiki",
+ "smw-datavalue-invalid-number": "\"$1\" kan niet worden geïnterpreteerd als een getal.",
+ "smw-query-condition-circular": "Er is een mogelijke circulaire aanroep aangetroffen in \"$1\".",
+ "smw-types-list": "Lijst van gegevenstypen",
+ "smw-types-default": "\"$1\" is een ingebouwd gegevenstype.",
+ "smw-types-help": "Meer informatie en voorbeelden vindt u op de [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 help-pagina].",
+ "smw-type-anu": "\"$1\" is een variant van het [[Special:Types/URL|URL]] gegevenstype, dat meestal wordt gebruikt voor een \"owl:AnnotationProperty\" exportdeclaratie.",
+ "smw-type-boo": "\"$1\" is een primitief gegevenstype voor het beschrijven van een waar/onwaar waarde.",
+ "smw-type-cod": "\"$1\" is een variant van het [[Special:Types/Text|Tekst]] gegevenstype, dat kan worden gebruikt voor technische teksten van willekeurige lengte, zoals broncode.",
+ "smw-type-geo": "\"$1\" is een gegevenstype dat geografische locaties beschrijft. Gebruik ervan vereist [https://www.semantic-mediawiki.org/wiki/Semantic_Maps Semantic Maps].",
+ "smw-type-tel": "\"$1\" is een speciaal gegevenstype om internationale telefoonnummers te beschrijven conform RFC 3966.",
+ "smw-type-txt": "\"$1\" is een primitief gegevenstype om tekenreeksen van willekeurige lengte te beschrijven.",
+ "smw-type-dat": "\"$1\" is een gegevenstype om punten in de tijd te representeren in een uniform formaat.",
+ "smw-type-tab-properties": "Eigenschappen",
+ "smw-type-tab-types": "Typen",
+ "smw-type-tab-errors": "Fouten",
+ "smw-datavalue-languagecode-invalid": "\"$1\" wordt niet herkend als een ondersteunde taalcode.",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|seconde|seconden}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|seconde|seconden}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|seconde|seconden}}",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" is een ongeldige URL.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Verwijzing naar het klembord kopiëren",
+ "smw-data-lookup": "Gegevens ophalen ...",
+ "smw-no-data-available": "Geen gegevens beschikbaar",
+ "smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation Change propagation] updates in afwachting ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|job|jobs}}] estimated) En het wordt aangeraden om te wachten met wijzigingen van een eigendom tot het proces is afgerond om tussenpersonenonderbrekingen of tegenstrijdige specificaties te voorkomen.",
+ "smw-format-datatable-emptytable": "Geen data beschikbaar in de tabel",
+ "smw-format-datatable-infoempty": "Weergave 0 to 0 van 0 resultaten",
+ "smw-format-datatable-infofiltered": "(gefilterd uit _MAX_ resultaten)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Toon _MENU_ resultaten",
+ "smw-format-datatable-loadingrecords": "Bezig met laden…",
+ "smw-format-datatable-processing": "Verwerken...",
+ "smw-format-datatable-search": "Zoekopdracht:",
+ "smw-format-datatable-zerorecords": "Geen overeenkomende resultaten gevonden",
+ "smw-format-datatable-first": "Eerste",
+ "smw-format-datatable-last": "Laatste",
+ "smw-format-datatable-next": "Volgende",
+ "smw-format-datatable-previous": "Vorige",
+ "smw-format-datatable-toolbar-export": "Exporteren",
+ "smw-property-reserved-category": "Categorie",
+ "smw-category": "Categorie",
+ "smw-filter": "Filter",
+ "smw-section-expand": "De selectie uitvouwen",
+ "smw-section-collapse": "De selectie invouwen",
+ "smw-help": "Hulp",
+ "smw-processing": "Verwerken...",
+ "smw-types-title": "Type: $1",
+ "smw-parameter-missing": "Parameter \"$1\" ontbreekt.",
+ "smw-property-tab-usage": "Gebruik",
+ "smw-property-tab-redirects": "Synoniemen",
+ "smw-property-tab-subproperties": "Subeigenschappen",
+ "smw-property-tab-errors": "Onjuiste toewijzingen",
+ "smw-property-tab-specification": "... meer",
+ "smw-concept-tab-list": "Lijst",
+ "smw-concept-tab-errors": "Fouten",
+ "smw-ask-tab-result": "Resultaat",
+ "smw-ask-tab-extra": "Extra"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/nn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/nn.json
new file mode 100644
index 00000000..4ed4a1d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/nn.json
@@ -0,0 +1,160 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gunnernett",
+ "Harald Khan",
+ "Njardarlogar",
+ "Ranveig"
+ ]
+ },
+ "smw_viewasrdf": "RDF-mating",
+ "smw_finallistconjunct": " og",
+ "smw_factbox_head": "Fakta om $1",
+ "smw_isspecprop": "Denne eigenskapen er ein spesialeigenskap på denne wikien",
+ "smw_concept_description": "Skildring av konseptet «$1»",
+ "smw_no_concept_namespace": "Konsept kan berre bli definerte frå sider i Concept:-namnerommet.",
+ "smw_multiple_concepts": "Kvar konseptsida kan berre ha éin konseptdefinisjon.",
+ "smw_concept_cache_miss": "Konseptet «$1» kan ikkje bli nytta for augneblinken, då wikikonfigurasjonen krev at det blir sett saman fråkopla.\nOm problemet ikkje går vekk etter noko tid, be sideadministratoren om å gjera dette konseptet tilgjengleg.",
+ "smw_baduri": "URI-ar på forma «$1» er ikkje tillatne.",
+ "smw_printername_count": "Tel opp resultata",
+ "smw_printername_csv": "CSV eksport",
+ "smw_printername_json": "JSON eksport",
+ "smw_printername_list": "Liste",
+ "smw_printername_ol": "Nummerering",
+ "smw_printername_ul": "Objektisering",
+ "smw_printername_table": "Tabell",
+ "smw_printername_broadtable": "Brei tabell",
+ "smw_printername_template": "Mal",
+ "smw_iq_disabled": "Semantiske spørjingar har blitt slegne av på denne wikien.",
+ "smw_iq_moreresults": "… fleire resultat",
+ "smw_parseerror": "Den gjevne verdien blei ikkje forstått.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "«$1» kan ikkje bli nytta som sidenamn på denne wikien.",
+ "smw_wrong_namespace": "Berre sider i namnerommet «$1» er tillatne her.",
+ "smw_manytypes": "Meir enn éin type definert for eigenskapen.",
+ "smw_emptystring": "Tomme strenger blir ikkje godtekne.",
+ "smw_notinenum": "«$1» er ikkje i lista over moglege verdiar ($2) for denne eigenskapen.",
+ "smw_noboolean": "«$1» blir ikkje kjend att som ein Boolean-verdi (sant/usant).",
+ "smw_true_words": "true,t,yes,y,ja,j,sant,s",
+ "smw_false_words": "false,f,no,n,nei,usant,u",
+ "smw_nofloat": "«$1» er ikkje eit tal.",
+ "smw_infinite": "Tal so store som «$1» er ikkje støtta.",
+ "smw_nodatetime": "Datoen «$1» vart ikkje forstått.",
+ "smw_toomanyclosing": "«$1» finst for mange gonger i spørjinga.",
+ "smw_noclosingbrackets": "Nokre klammar i spørjinga di («<nowiki>[[</nowiki>») vart ikkje lukka påfølgjande klammar («]]»).",
+ "smw_misplacedsymbol": "Symbolet «$1» vart nytta på ein stad der det ikkje er gagnleg.",
+ "smw_unexpectedpart": "«$1»-delen av spørjinga var uforståeleg.\nResultata kan vera uventa.",
+ "smw_emptysubquery": "Ei underspørjing har ikkje gyldige føresetnader.",
+ "smw_misplacedsubquery": "Ei underspørjing vart nytta på ein stad der underspørjingar ikkje er tillatne.",
+ "smw_valuesubquery": "Underspørjingar er ikkje støtta for verdiar av eigenskapen «$1».",
+ "smw_badqueryatom": "Ein lut («<nowiki>[[…]]</nowiki>») av spørjinga vart ikkje forstått.",
+ "smw_propvalueproblem": "Verdien av eigenskapen «$1» vart ikkje forstått.",
+ "smw_noqueryfeature": "Nokre spørjefunksjonar er ikkje støtta på denne wikien, og luter av spørjinga vart difor hoppa over ($1).",
+ "smw_noconjunctions": "Konjunksjonar i spørjingar er ikkje støtta på denne wikien, og luter av spørjinga vart difor hoppa over ($1).",
+ "smw_nodisjunctions": "Disjunksjonar i spørjingar er ikkje støtta på denne wikien, og luter av spørjinga vart difor hoppa over ($1).",
+ "smw_querytoolarge": "Følgjande spørjeføresetnader kunne ikkje verta teke omsyn til grunna avgrensingane til wikien når det gjeld spørjestorleik eller djupn: $1",
+ "smw_notemplategiven": "Oppgje ein verdi for parameteren «mal» for at dette spørjeformatet skal verka.",
+ "smw_type_header": "Eigenskapar av typen «$1»",
+ "smw_typearticlecount": "Syner {{PLURAL:$1|éin eigenskap|$1 eigenskapar}} som nyttar denne typen.",
+ "smw_attribute_header": "Sider som nyttar eigenskapen «$1»",
+ "smw_attributearticlecount": "Syner {{PLURAL:$1|éi sida|$1 sider}} som nyttar denne eigenskapen.",
+ "smw_subproperty_header": "Undereigenskapar",
+ "smw_subpropertyarticlecount": "Denne eigenskapen har følgjande {{PLURAL:$|undereigenskap|$1 undereigenskapar}}:",
+ "exportrdf": "Eksporter sider til RDF",
+ "smw_exportrdf_docu": "Denne sida lèt deg skaffa data frå ei sida i RDF-format.\nSkriv inn titlar i tekstboksten nedanfor for å eksportera sider, éin tittel per linja.",
+ "smw_exportrdf_recursive": "Eksporter alle relaterte sider rekursivt.\nMerk at resultatet kan vera stort.",
+ "smw_exportrdf_backlinks": "Eksporter òg alle sider som refererer til dei eksporterte sidene.\nLagar ein RDF som ein kan gå gjennom.",
+ "smw_exportrdf_lastdate": "Ikkje eksporter sider som ikkje vart endra sidan det oppgjevne tidspunktet.",
+ "smw_exportrdf_submit": "Eksport",
+ "uriresolver": "URI-løysar",
+ "properties": "Eigenskapar",
+ "smw_properties_docu": "Følgjande eigenskapar er nytta på wikien.",
+ "smw_property_template": "$1 av typen $2 ($3)",
+ "smw_propertylackspage": "Alle eigenskapar burde vore skildra av ei sida.",
+ "smw_propertylackstype": "Ingen type blei oppgjeven for denne eigenskapen (reknar med at typen er $1 mellombels)",
+ "smw_propertyhardlyused": "Denne eigenskapen blir knapt nytta på wikien!",
+ "unusedproperties": "Unytta eigenskapar",
+ "smw_unusedproperties_docu": "Dei følgjande eigenskpane finst, sjølv om ingen andre sider nyttar dei.",
+ "smw_unusedproperty_template": "$1 av typen $2",
+ "wantedproperties": "Ynskte eigenskapar",
+ "smw_wantedproperties_docu": "Dei følgjande eigenskapen er nytta på wikien, men har ikkje sider som skildrar dei.",
+ "smw_wantedproperty_template": "$1 (nytta {{PLURAL:$2|éin gong|$2 gonger}})",
+ "smw_purge": "Oppfrisk",
+ "types": "Typar",
+ "smw_types_docu": "Fylgjande er ei lista over alle datatypar som eigenskapar kan ha.\nKvar datatype har ei sida der ekstra informasjon kan verta oppgjeven.",
+
+
+ "smw_uri_doc": "URI-løysaren implementerer [$1 finning av W3C TAG-ar på «httpRange-14»].\nHan syter for at menneske ikkje vert til nettstader.",
+ "ask": "Semantisk søk",
+ "smw_ask_sortby": "Sorter etter kolonne (valfritt)",
+ "smw_ask_ascorder": "Stigande",
+ "smw_ask_descorder": "Søkkjande",
+ "smw_ask_submit": "Finn resultat",
+ "smw_ask_editquery": "Brigd spørjing",
+ "smw_add_sortcondition": "[Legg til sorteringsføresetnader]",
+ "smw_ask_hidequery": "Løyn spørjing",
+ "smw_ask_help": "Spørjehjelp",
+ "smw_ask_queryhead": "Spørjing",
+ "smw_ask_printhead": "Ekstra utskrifter (valfritt)",
+ "smw_ask_format_as": "Formatér som:",
+ "smw_ask_defaultformat": "standard",
+ "searchbyproperty": "Søk etter eigenskap",
+ "smw_sbv_docu": "Søk etter alle sider som har ein viss eigenskap og verdi.",
+ "smw_sbv_novalue": "Skriv inn ein gyldig verdi for eigenskapen, eller sjå alle eigenskapsverdiar for «$1».",
+ "smw_sbv_displayresult": "Ei lista over alle sider som har eigenskapen «$1» med verdien «$2»",
+ "smw_sbv_displayresultfuzzy": "Ei lista over alle sider som har eigenskapen «$1» med verdien «$2».\nSidan det berre kom fram nokre få resultat er òg nære verdiar viste.",
+ "smw_sbv_property": "Eigenskap:",
+ "smw_sbv_value": "Verdi:",
+ "smw_sbv_submit": "Finn resultat",
+ "browse": "Bla gjennom wikien",
+ "smw_browselink": "Bla gjennom eigenskapar",
+ "smw_browse_article": "Skriv inn namnet på sida du vil starta å bla frå.",
+ "smw_browse_go": "GÃ¥",
+ "smw_browse_show_incoming": "syn eigenskapar som lenkjar hit",
+ "smw_browse_hide_incoming": "løyn eigenskapar som lenkjar hit",
+ "smw_browse_no_outgoing": "Denne sida har ingen eigenskapar.",
+ "smw_browse_no_incoming": "Ingen eigenskapar lenkjar til denne sida.",
+ "smw_inverse_label_default": "$1 av",
+ "smw_inverse_label_property": "Vrengd eigenskapsmerke",
+ "pageproperty": "Sideeigenskapssøk",
+ "smw_pp_docu": "Søk etter all eigenskapstekst på ei viss side.\nSkriv inn både sidenamn og eigenskap.",
+ "smw_pp_from": "Frå side",
+ "smw_pp_type": "Eigenskap",
+ "smw_pp_submit": "Finn resultat",
+ "smw_result_prev": "Førre",
+ "smw_result_next": "Neste",
+ "smw_result_results": "Resultat",
+ "smw_result_noresults": "Fann ingen resultat.",
+ "smwadmin": "Administratorfunksjonar for Semantic MediaWiki",
+ "smw_smwadmin_setupsuccess": "Lagringseininga blei sett opp.",
+ "smw_smwadmin_return": "Attende til $1",
+ "smw_smwadmin_updatestarted": "Ein ny oppdateringsprosses for å oppfriska dei semantiske dataa blei starta.\nAlle lagra data vil bygt om eller reparert der det trengst.\nDu kan fylgja med på framgangen til oppdateringa på denne spesialsida.",
+ "smw_smwadmin_updatenotstarted": "Ein oppdateringsprosses køyrer frå før.\nStartar ikkje ein ny ein.",
+ "smw_smwadmin_updatestopped": "Alle eksisterande oppdateringsprosessar har blitt stoppa.",
+ "smw_smwadmin_updatenotstopped": "For å stoppa oppdateringsprosessen som køyrer, må du markera boksen for å syna at du verkeleg meiner det.",
+ "smw_smwadmin_docu": "Denne spesialsida hjelper deg under installering og oppgradering av <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a>.\nHugs å ta kopiar av viktige data før du køyrer administrerande funksjonar.",
+ "smw_smwadmin_db": "Databaseinnstallering og -oppgradering",
+ "smw_smwadmin_dbdocu": "Semantic MediaWiki krev nokre utvidingar i MediaWiki-databasen for å kunna lagra semantiske data.\nFunksjonen under syter for at databasen din er sett opp rett.\nEndringar gjort i dette steget har ikkje nokon effekt på resten av MediaWiki-databasen og kan enkelt rullast attende om det skulle vera ynskjeleg.\nDenne oppsettsfunksjonen kan køyrast fleire gongen utan at ein gjer nokon skade, men han trengst berre éin gong under installering eller oppgradering.",
+ "smw_smwadmin_permissionswarn": "Om operasjonen mislukkast med SQL-feil, har sannsynlegvis ikkje databasebrukaren nytta av wikien din dei rette løyva (sjekk LocalSettings.php).\nAnten gje denne brukaren dei rette løyva til å oppretta og sletta tabellar og mellombels skriv inn innloggingsinformasjonen til database-rooten din i LocalSettings.php, eller køyr vedlikehaldsskriptet <code>setupStore.php</code> som kan nytta løyva til AdminSettings.php.",
+ "smw_smwadmin_dbbutton": "Set i verk eller oppgrader tabellar",
+ "smw_smwadmin_announce": "Kunngjer wikien din",
+ "smw_smwadmin_datarefresh": "Datareparasjon eller -oppgradering",
+ "smw_smwadmin_datarefreshdocu": "Det er mogleg å få attende alle Semantic MediaWiki-dataa basert på det noverande innahaldet til wikien.\nDette kan vera nyttig for å reparera øydelagde data eller for å oppfriska dataa om det interne formatet har blitt endra grunna programvareoppdatering.\nOppdateringa blir utført sida for sida og vil ikkje vera ferdig med ein gong.\nDet fylgjande syner om ei oppdatering er på gong og lèt deg byrja eller stoppa oppdateringar (viss ikkje denne moglegheita er deaktivert av sideadministratoren).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Ei oppdatering held allereie på.</strong>\nDet er normalt at oppdateringa går sakte då ho berre oppfriskar data i små bitar kvar gong ein brukar er innom wikien.\nFor å fullføra denne oppdateringa på ein kjappare måte, kan du setja i gang MediaWiki-vedlikehaldsskriptet <code>runJobs.php</code> (nytt valet <code>--maxjobs 1000</code> for å avgrensa talet på oppdetaringar som blir gjort i eitt stykke).\nEstimert framdrift på den noverande oppdateringa:",
+ "smw_smwadmin_datarefreshbutton": "Start oppdatering",
+ "smw_smwadmin_datarefreshstop": "Stopp denne oppdateringa",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, eg er sikker.",
+ "smw_smwadmin_support": "Får støtta",
+ "smw_smwadmin_supportdocu": "Diverse ressursar kan kanskje hjelpa deg om du skulle få problem:",
+ "smw_smwadmin_installfile": "Om du møter på problem under installeringa, start med å studera retningslinene i <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">INSTALL-fila</a>.",
+ "smw_smwadmin_smwhomepage": "Den komplette brukardokumentasjonen for Semantic MediaWiki finn du på <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw_smwadmin_mediazilla": "Feil kan bli rapporterte til <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw_smwadmin_questions": "Om du har fleire spørsmål eller forslag, bli med i diskusjonen på <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Semantic MediaWiki-brukarforumet</a>.",
+ "smw-createproperty-isproperty": "Dette er ein eigenskap av typen $1.",
+ "smw-createproperty-allowedvals": "Dei tillatne verdiane for denne eigenskapen er:",
+ "smw_unknowntype": "Typen «$1» som er definert for eigenskapen er ikkje støtta.",
+ "smw_concept_header": "Sider av konseptet «$1»",
+ "smw_conceptarticlecount": "Syner nedanfor {{PLURAL:$1|éi side|$1 sider}}",
+ "smw-livepreview-loading": "Lastar inn&nbsp;…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/oc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/oc.json
new file mode 100644
index 00000000..54cdf237
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/oc.json
@@ -0,0 +1,232 @@
+{
+ "@metadata": {
+ "authors": [
+ "Cedric31",
+ "ì•„ë¼",
+ "Kghbln",
+ "Nicolas Eynaud",
+ "Nemo bis",
+ "Unuaiga",
+ "Guilhelma"
+ ]
+ },
+ "smw-desc": "Rendre lo wiki mai accessible - per las maquinas ''e'' los umans ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentacion en linha])",
+ "smw_viewasrdf": "Veire coma RDF",
+ "smw_finallistconjunct": " e",
+ "smw_isspecprop": "Aquesta proprietat es una proprietat especiala sus aqueste wiki.",
+ "smw-concept-no-cache": "Cap de cache pas disponible.",
+ "smw_concept_description": "Descripcion del concèpte « $1 »",
+ "smw_no_concept_namespace": "Los concèptes pòt unicament èsser definits dins la pagina dins lo Concèpte : espaci de nom.",
+ "smw_multiple_concepts": "Cada pagina de concèpte pòt pas aver qu’una sola definicion.",
+ "smw_concept_cache_miss": "Lo concèpte « $1 » pòt pas èsser utilizat pel moment, perque la configuracion del wiki requerís que siá aviat fòra linha. Se lo problèma persistís aprèp qualques instants, demandatz a vòstre administrator del site de rendre disponible aqueste concèpte.",
+ "smw_noinvannot": "Las valors pòdon pas èsser allogadas per inversar de proprietats.",
+ "smw_baduri": "O planhèm. Las URIs del domeni $1 son pas disponiblas a aqueste emplaçament",
+ "smw_csv_link": "CSV",
+ "smw_printername_count": "Comptatge dels resultats",
+ "smw_printername_csv": "expòrt en CSV",
+ "smw_printername_dsv": "Exportar al format DSV",
+ "smw_printername_debug": "Requèste de debogatge (pels expèrts)",
+ "smw_printername_embedded": "Contengut de las paginas incrustadas",
+ "smw_printername_json": "expòrt en JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Enumeracion",
+ "smw_printername_ul": "Detalh",
+ "smw_printername_table": "Tablèu",
+ "smw_printername_broadtable": "Taula larga",
+ "smw_printername_template": "Modèl",
+ "smw_printername_rdf": "Exportar al format RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "tèxte",
+ "smw_iq_disabled": "O planhèm. Las recèrcas dins los articles d'aqueste wiki son pas autorizadas.",
+ "smw_iq_moreresults": "&hellip; autres resultats",
+ "smw_parseerror": "La donada indicada es pas estada compresa.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": ",",
+ "smw_notitle": "“$1†pòt pas èsser utilizat coma nom de pagina sus aqueste wiki.",
+ "smw_wrong_namespace": "Solas las paginas de l'espaci de noms « $1 » son autorizadas aicí.",
+ "smw_manytypes": "Maites tipes de donadas son estats assignats a l'atribut.",
+ "smw_emptystring": "Las cadenas voidas son pas acceptadas.",
+ "smw_notinenum": "« $1 » es pas dins la lista ($2) de las [[Property:Allows value|valors autorizadas]] per la proprietat « $3 ».",
+ "smw_noboolean": "\\\"$1\\\" es pas reconegut coma una valor boleana (verai/fals).",
+ "smw_true_words": "verai,v,òc,true",
+ "smw_false_words": "fals,f,non,false",
+ "smw_nofloat": "\"$1\" es pas un nombre.",
+ "smw_infinite": "Los nombres tant grands coma « $1 » son pas suportats.",
+ "smw_nodatetime": "La data \"$1\" es pas estada compresa.",
+ "smw_toomanyclosing": "Sembla que i a tròp d'ocuréncias de “$1†dins la requèsta.",
+ "smw_noclosingbrackets": "D'unes “<nowiki>[[</nowiki>†dins vòstra requèsta son pas estats clauses per de “]]†correspondents.",
+ "smw_misplacedsymbol": "Lo simbòl “$1†es estat utilizat a un endrech ont es pas util.",
+ "smw_unexpectedpart": "La partida “$1†de la requèsta es pas estada compresa. Los resultats pòdon èsser imprevists.",
+ "smw_emptysubquery": "D'unas sosrequèstas an una condicion invalida.",
+ "smw_misplacedsubquery": "D'unas sosrequèstas son estadas utilizadas a un endrech ont cap de sosrequèsta es pas permesa.",
+ "smw_valuesubquery": "Sosrequèsta pas suportada per las valors de la proprietat “$1â€.",
+ "smw_badqueryatom": "Las partidas “<nowiki>[[…]]</nowiki>†de la requèsta son pas estadas compresas.",
+ "smw_propvalueproblem": "La valor de la proprietat “$1†es pas estada compresa.",
+ "smw_noqueryfeature": "Qualques foncionalitats de requèstas son pas suportadas sus aqueste wiki e una partida d’entre elas es estada levada ($1).",
+ "smw_noconjunctions": "Las conjoncions dins las requèstas son pas suportadas sus aqueste wiki e una partida d’entre elas es estada levada ($1).",
+ "smw_nodisjunctions": "Las disjoncions dins las requèstas son pas suportadas sus aqueste wiki e de partidas de la requèsta son estadas ignoradas($1).",
+ "smw_querytoolarge": "Las condicions seguentas de la requèsta an pas pogut èsser evaluadas en rason de las restriccions d'aqueste wiki a la talha o a la prigondor de las requèstas : $1.",
+ "smw_notemplategiven": "Provesissètz una valor pel paramètre « modèl » per aqueste format de requèsta per trabalhar.",
+ "smw_type_header": "Atributs de tipe “$1â€",
+ "smw_typearticlecount": "Afichar {{PLURAL:$1|la proprietat qu'utiliza|las $1 proprietats qu'utilizan}} aqueste tipe.",
+ "smw_attribute_header": "Paginas utilizant l'atribut “$1â€",
+ "smw_attributearticlecount": "Afichar {{PLURAL:$1|la pagina qu'utiliza|las $1 paginas qu'utilizan}} aquesta proprietat.",
+ "exportrdf": "Exportar l'article en RDF",
+ "smw_exportrdf_docu": "Sus aquesta pagina, de partidas del contengut d'un article pòdon èsser exportadas dins lo format RDF. Picatz lo nom de las paginas desiradas dins la bóstia de tèxte çaijós, <i>un nom per linha </i>.",
+ "smw_exportrdf_recursive": "Exportar tanben totas las paginas pertinentas d'un biais recursiu. Aquesta possibilitat pòt abotir a un fòrt grand nombre de resultats !",
+ "smw_exportrdf_backlinks": "Exportar tanben totas las paginas que renvian a de paginas exportadas. Produtz un RDF dins lo qual la navigacion es mai aisida.",
+ "smw_exportrdf_lastdate": "Exportar pas las paginas pas modificadas dempuèi lo moment indicat.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Resolveire d'URI",
+ "properties": "Proprietats",
+ "smw_properties_docu": "Sus aqueste wiki, son utilizadas las proprietats seguentas.",
+ "smw_property_template": "$1 del tipe $2 ($3 {{PLURAL:$3|utilizacion|utilizacions}})",
+ "smw_propertylackspage": "Tota proprietat deuriá èsser descrita per una pagina !",
+ "smw_propertylackstype": "Cap de tipe es pas estat especificat per aquesta proprietat (tipe actualament supausat : $1).",
+ "smw_propertyhardlyused": "Aquesta proprietat es fòrça utilizada sus aqueste wiki !",
+ "concepts": "Concèptes",
+ "unusedproperties": "Proprietats inutilizadas",
+ "smw-unusedproperties-docu": "Las proprietats seguentas existisson, quitament se cap de pagina las utiliza pas.",
+ "smw-unusedproperty-template": "$1 de tipe $2",
+ "wantedproperties": "Proprietats demandadas",
+ "smw-wantedproperties-docu": "Las proprietats seguentas son utilizadas sus aqueste wiki mas an pas encara de pagina per las descriure.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|utilizacion|utilizacions}})",
+ "smw_purge": "Reactualizar",
+ "types": "Tipes",
+ "smw_types_docu": "Aquò es una lista de totes los tipes de donadas que pòdon èsser assignats a las proprietats.",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor|Valors}} de proprietat (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Proprietat|Proprietats}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Proprietat|Proprietats}} (total)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Proprietat|Proprietats}} (enregistradas amb una pagina)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Proprietat|Proprietats}} (assignadas a un tipe de donadas)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Requèsta|Requèstas}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|requèsta|requèstas}}]]",
+ "smw-statistics-query-size": "Talha de la requèsta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Concèpte|Concèptes}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Concèpte|Concèptes}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|Possedís {{PLURAL:$1|un sosobjècte|de sosobjèctes}}]]",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipe de donadas|Tipes de donadas}}]]",
+ "smw_uri_doc": "Lo resolveire d'URI implementa la [$1 conclusion del TAG del W3C a prepaus del httpRange-14]. Se pòt assegurar que los umans vengan pas de sites web.",
+ "ask": "Recèrca semantica",
+ "smw_ask_sortby": "Triar per colomnas (opcional)",
+ "smw_ask_ascorder": "Creissent",
+ "smw_ask_descorder": "Descreissent",
+ "smw_ask_submit": "Trobar de resultats",
+ "smw_ask_editquery": "Editar la requèsta",
+ "smw_add_sortcondition": "[Apond las condicions de triada]",
+ "smw_ask_hidequery": "Amagar la requèsta",
+ "smw_ask_help": "Ajuda a la requèsta",
+ "smw_ask_queryhead": "Condicion",
+ "smw_ask_printhead": "Seleccion de las donadas d'imprimir",
+ "smw_ask_printdesc": "(apondre un nom de proprietat per linha)",
+ "smw_ask_format_as": "Formatar en :",
+ "smw_ask_defaultformat": "defaut",
+ "smw_ask_otheroptions": "Autras opcions",
+ "smw-ask-otheroptions-collapsed-info": "Utilizatz l'icòna \"plus\" per afichar totas las opcions disponiblas",
+ "smw_ask_show_embed": "Far veire lo còdi incrustat",
+ "smw_ask_hide_embed": "Amagar lo còdi incrustat",
+ "smw_ask_embed_instr": "Per incrustar aquesta requèsta dins una pagina wiki, utilizatz lo còdi çaijós.",
+ "smw-ask-delete": "Suprimir",
+ "smw-ask-sorting": "Triada",
+ "smw-ask-search": "Recercar",
+ "searchbyproperty": "Recercar per atribut",
+ "smw_sbv_docu": "Recercar totas las paginas qu'an un atribut donat amb una cèrta valor.",
+ "smw_sbv_novalue": "Picatz una valor o consultatz totas las valors dels atributs per $1.",
+ "smw_sbv_displayresult": "Lista de totas las paginas qu'an un atribut $1 amb la valor $2.",
+ "smw_sbv_displayresultfuzzy": "Una lista de totas las paginas que son la proprietat « $1 » amb la valor « $2 ». Perque i a pas que qualques resultats, las valors pròchas tanben son afichadas.",
+ "smw_sbv_property": "Proprietat :",
+ "smw_sbv_value": "Valor :",
+ "smw_sbv_submit": "Trobar de resultats",
+ "browse": "Percórrer lo wiki",
+ "smw_browselink": "Cercar las proprietats",
+ "smw_browse_article": "Picatz lo nom de la pagina a partir de la quala volètz començar la navigacion.",
+ "smw_browse_go": "Validar",
+ "smw_browse_show_incoming": "afichar las proprietats que puntan aicí",
+ "smw_browse_hide_incoming": "amagar las proprietats que puntan aicí",
+ "smw_browse_no_outgoing": "Aquesta pagina a pas cap de proprietat.",
+ "smw_browse_no_incoming": "Cap de proprietat punta pas cap a aquesta pagina.",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Inversar lo labèl de proprietat",
+ "pageproperty": "Recercar dins las proprietats de la pagina",
+ "smw_pp_docu": "Picatz una pagina e una proprietat o solament una proprietat per recuperar totas las valors assignadas.",
+ "smw_pp_from": "De la pagina",
+ "smw_pp_type": "Proprietat :",
+ "smw_pp_submit": "Afichar los resultats",
+ "smw_result_prev": "Precedent",
+ "smw_result_next": "Seguent",
+ "smw_result_results": "Resultats",
+ "smw_result_noresults": "O planhèm, i a pas cap de resultats.",
+ "smwadmin": "Foncions d'administrator",
+ "smw-admin-setupsuccess": "Lo motor d'emmagazinatge es estat mes en plaça.",
+ "smw_smwadmin_return": "Tornar cap a $1",
+ "smw_smwadmin_updatestarted": "Un processús novèl pel refrescament de semantic data a començat.\nTotas las donadas estocadas seràn reconstruchas o quitament reparadas se necessari.\nPodètz seguir la progression de la mesa a jorn sus aquesta pagina especiala.",
+ "smw_smwadmin_updatenotstarted": "Un processús de mesa a jorn ja es en cors d’execucion.\nNe creetz pas d'autre.",
+ "smw_smwadmin_updatestopped": "Totes los processús de mesa a jorn son estats arrestats.",
+ "smw_smwadmin_updatenotstopped": "Per arrestar lo processús en cors de mesa a jorn, vos cal marcar la casa per indicar que ne sètz vertadièrament segur(a).\n\nTornar a $1 .",
+ "smw-admin-docu": "Aquesta pagina especiala vos ajuda pendent l’installacion, la mesa a jorn, la mantenença e l'utilizacion de <a href=\"https://semantic-mediawiki.org\">Semantic MediaWiki</a> e compòrta d'autras foncions e prètzfaits administratius e tanben d'estatisticas.\nDoblidetz pas de salvar vòstras donadas importantas abans d'executar de foncions d’administracion.",
+ "smw-admin-db": "Installacion e mesa a nivèl de la basa de donadas",
+ "smw-admin-dbdocu": "Semantic MediaWiki requerís maitas extensions per la basa de donadas MediaWiki en òrdre per emmagazinar las donadas de semantica.\nLa foncion çaijós verifica que vòstra basa de donadas es estada installada corrèctament.\nLas modificacions faitas al moment d'aquesta etapa afectaràn pas la rèsta de la basa de donadas Mediawiki, e pòt èsser desfaita a volontat.\nAquesta foncion d’installacion pòt èsser aviada mantun còp sens causar lo mendre degalh, mas una sola installacion o mesa e nivèl es necessària.",
+ "smw-admin-permissionswarn": "Se l’operacion fracassa amb d'errors SQL, l’utilizaire de la basa de donadas utilizada per vòstre wiki (agachatz vòstre LocalSettings.php) a probablament pas los dreits sufisents.\nCal siá permetre a aqueste utilizaire de dispausar de las permissions per crear e suprimir las taulas, siá entrar temporàriament la connexion en root a vòstra basa de donadas dins lo LocalSettings.php, siá utilizar l'escript de mantenença <code>setupStore.php</code> lo qual pòt utilizar las informacions de connexion d'un administrator.",
+ "smw-admin-dbbutton": "Inicializar o metre a nivèl las taulas",
+ "smw-admin-announce": "Anonciar vòstre wiki",
+ "smw_smwadmin_datarefresh": "Reconstruccion de las donadas",
+ "smw_smwadmin_datarefreshdocu": "Es possible de restablir totas las donadas Semantic MediaWiki basadas suls contenguts corrents d'aqueste wiki.\nAquò pòt èsser util per reparar de donadas rompudas o per refrescar las donadas se lo format intèrne a cambiat al moment de las remesas a nivèl.\nLa mesa a jorn es executada pagina per pagina e se serà pas acabada immediatament.\nLa pagina seguenta aficha se una mesa a jorn es en cors d’execucion e vos permet de començar o d’arrestar aquestas (levat s'aquesta foncionalitat est desactivada per l’administrator del site).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Una mesa a jorn es en cors d’execucion.</strong>\nEs normal qu’una mesa a jorn progresse lentament dempuèi que refresca unicament las donadas dins de troces pichons a cada còp qu’un utilizaire accedís al wiki.\nPer acabar aquesta mesa a jorn mai rapidament, podètz invocar l'escript de mantenença Mediawiki <code>runJobs.php</code> (utilizatz l’opcion <code>--maxjobs 1000</code> per restrénher lo nombre de las mesas a jorn per escript aviat).\nProgression estimada de la mesa a jorn actuala :",
+ "smw_smwadmin_datarefreshbutton": "Planificar la reconstruccion de las donadas",
+ "smw_smwadmin_datarefreshstop": "Arrestar aquesta mesa a jorn",
+ "smw_smwadmin_datarefreshstopconfirm": "Ã’c, ne soi {{GENDER:$1|segur|segura}}.",
+ "smw-admin-job-scheduler-note": "La màger part de las activitats dins aquesta seccion son faitas com de tascas per evitar las situacions de camin d'enlòc pendent l'execucion. Lo [https://www.mediawiki.org/wiki/Manual:Job_queue job scheduler] es responsable de l'execucion e es critic que l'escript de mantenença <code>runJobs.php</code> (veire tanben <code>$wgRunJobsAsync</code>) aja una capacitat adaptada.",
+ "smw-admin-outdateddisposal-active": "Una tasca de supression d'entitats obsoletas es estada planificada.",
+ "smw-admin-propertystatistics-active": "Una tasca de reconstruccion d'estatisticas de proprietat es estada planificada.",
+ "smw-admin-fulltext-active": "Una tasca de reconstruccion de la recèrca plen tèxte es estat planificat.",
+ "smw-admin-support": "Obténer d’ajuda",
+ "smw-admin-supportdocu": "Divèrsas ressorsas vos poirián ajudar en cas de problèmas :",
+ "smw-admin-installfile": "S'experimentatz de problèmas amb vòstra installacion, començatz per agachar lo guida en linha dins lo <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">fichièr INSTALL</a>.",
+ "smw-admin-smwhomepage": "La documentacion completa de l’utilizaire de Semantic MediaWiki se tròba sus <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Los bugs pòdon èsser senhalats sus <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Avètz d’autras questions o de suggestions, rejonhètz la discussion sul <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">forum dels utilizaires de Semantic MediaWiki</a>.",
+ "smw-admin-supplementary-settings-title": "Paramètres de configuracion",
+ "smw_adminlinks_datastructure": "Estructura de las donadas",
+ "smw_adminlinks_displayingdata": "Afichatge de las donadas",
+ "smw_adminlinks_inlinequerieshelp": "Ajuda sus las requèstas",
+ "smw-property-indicator-type-info": "Proprietat definida per l{{PLURAL:$1|'utilizaire|o sistèma}}",
+ "smw-createproperty-isproperty": "Aquò es una proprietat del tipe $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|La valor autorizada per aquesta proprietat es|Las valors autorizadas per aquesta proprietat son}} :",
+ "smw-paramdesc-category-delim": "Lo delimitador",
+ "smw-ui-tooltip-title-property": "Proprietat",
+ "smw-ui-tooltip-title-quantity": "Conversion d'unitat",
+ "smw-ui-tooltip-title-info": "Informacion",
+ "smw-ui-tooltip-title-service": "Ligams de servici",
+ "smw-ui-tooltip-title-warning": "Error",
+ "smw-ui-tooltip-title-parameter": "Paramètre",
+ "smw-ui-tooltip-title-event": "Eveniment",
+ "smw-ui-tooltip-title-note": "Nòta",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Referéncia",
+ "smw_unknowntype": "Lo tipe d'aquesta proprietat es invalid",
+ "smw_concept_header": "Paginas del concèpte « $1 »",
+ "smw_conceptarticlecount": "Afichar çaijós $1 {{PLURAL:$1|pagina|paginas}}.",
+ "group-smwadministrator": "Administrators (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrator|administratritz}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administrators (Semantic MediaWiki)",
+ "group-smwcurator": "Conservadors (Mediawiki Semantica)",
+ "smw-property-predefined-default": "\"$1\" es una proprietat predefinida.",
+ "smw-sp-properties-header-label": "Lista de las proprietats",
+ "smw-sp-admin-settings-button": "Generar la lista dels paramètres",
+ "smw-admin-objectid": "Id de l’objècte :",
+ "smw-admin-tab-rebuild": "Actualizacion e reconstruccion de las donadas",
+ "smw-livepreview-loading": "Cargament…",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista de resultats",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" tornat amb \"NULL\" qu'es pas una valor numerica autorizada.",
+ "log-name-smw": "Semantica del jornal MediaWiki",
+ "smw-types-list": "Lista dels tipes de donadas",
+ "smw-special-pageproperty-description": "Aquesta pagina provesís un interfaci de recerca per trobar totas las valors de la proprietat e una pagina especifica. D''autres interfacis de recerca comprenon [[Special:SearchByProperty|property search]] e [[Special:Ask|ask query builder]].",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segonda|segondas}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segonda|segondas}}",
+ "smw-datavalue-external-formatter-invalid-uri": "« $1 » es una URL invalida.",
+ "smw-datavalue-reference-outputformat": "$1 : $2",
+ "smw-parser-invalid-json-format": "L’analisador JSON a tornat un « $1 ».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-parse": "$1",
+ "smw-no-data-available": "Cap de donada pas disponibla."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/olo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/olo.json
new file mode 100644
index 00000000..4084338c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/olo.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Mashoi7"
+ ]
+ },
+ "browse": "Livua wikii"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/om.json b/www/wiki/extensions/SemanticMediaWiki/i18n/om.json
new file mode 100644
index 00000000..b67c6f73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/om.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Tumsaa"
+ ]
+ },
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:sekendii $1}}"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/or.json b/www/wiki/extensions/SemanticMediaWiki/i18n/or.json
new file mode 100644
index 00000000..f3707e22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/or.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jnanaranjan Sahu"
+ ]
+ },
+ "browse": "ଉଇକି ଦେଖିବେ",
+ "smw-livepreview-loading": "ଖୋଲà­à¬…ଛି..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/os.json b/www/wiki/extensions/SemanticMediaWiki/i18n/os.json
new file mode 100644
index 00000000..9b34fb9b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/os.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amikeco"
+ ]
+ },
+ "smw_printername_template": "Шаблон",
+ "smw-livepreview-loading": "Æвгæд цæуы..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pa.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pa.json
new file mode 100644
index 00000000..53117250
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pa.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Babanwalia"
+ ]
+ },
+ "browse": "ਵਿਕੀ ਫਰੋਲੋ",
+ "smw-livepreview-loading": "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...",
+ "smw-sp-searchbyproperty-resultlist-header": "ਨਤੀਜਿਆਂ ਦੀ ਸੂਚੀ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pag.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pag.json
new file mode 100644
index 00000000..1eb3bf36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pag.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Ilulugan…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pam.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pam.json
new file mode 100644
index 00000000..862b0dac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pam.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Máglulan…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pdc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pdc.json
new file mode 100644
index 00000000..e529c3a7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pdc.json
@@ -0,0 +1,20 @@
+{
+ "@metadata": {
+ "authors": [
+ "Xqt"
+ ]
+ },
+ "smw_finallistconjunct": " unn",
+ "smw_printername_list": "Lischt",
+ "smw_printername_template": "Moddel",
+ "smw_true_words": "ya,yes,true",
+ "smw_false_words": "nee,no,false",
+ "smw_nofloat": "„$1“ iss ken Zehl.",
+ "smw_browse_go": "Geh los",
+ "smw_inverse_label_default": "$1 vun",
+ "smw_pp_from": "Blatt",
+ "smw_result_prev": "zerick",
+ "smw_result_next": "vaerschich",
+ "smw_smwadmin_return": "Zerick zu $1",
+ "smw-livepreview-loading": "Laade…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pfl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pfl.json
new file mode 100644
index 00000000..53731ca5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pfl.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Manuae"
+ ]
+ },
+ "smw_true_words": "woah,w,ja,j",
+ "smw_false_words": "falsch,f,nä,n",
+ "browse": "Guggschd uff die Bedaidung",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, isch bin ma sischa"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pl.json
new file mode 100644
index 00000000..f28a53c4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pl.json
@@ -0,0 +1,556 @@
+{
+ "@metadata": {
+ "authors": [
+ "BeginaFelicysym",
+ "Chrumps",
+ "Maikking",
+ "Odie2",
+ "Rzuwig",
+ "Sociologist",
+ "Sovq",
+ "Sp5uhe",
+ "ToSter",
+ "Vuh",
+ "Woytecr",
+ "Åukasz Bolikowski",
+ "ì•„ë¼",
+ "Vengir",
+ "Alan ffm",
+ "WTM",
+ "Devwebtel",
+ "Nemo bis",
+ "InternerowyGołąb",
+ "Wojtas",
+ "Krottyianock",
+ "Railfail536",
+ "CiaPan"
+ ]
+ },
+ "smw-desc": "Poprawia dostępność wiki zarówno dla automatów jak i ludzi ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentacja online])",
+ "smw-title": "Semantyczna MediaWiki",
+ "smw-semantics-not-enabled": "Funkcjonalność Semantycznej MediaWiki dla tej wiki nie została włączona.",
+ "smw_viewasrdf": "Kanał RDF",
+ "smw_finallistconjunct": " i",
+ "smw-factbox-head": "... wiÄ™cej o „$1â€",
+ "smw-factbox-facts": "Fakty",
+ "smw-factbox-facts-help": "Wyświetla deklaracje i fakty, które zostały utworzone przez użytkownika",
+ "smw-factbox-facts-derived": "Pochodne fakty",
+ "smw-factbox-facts-derived-help": "Przedstawia fakty, które zostały wyprowadzone z zasad lub za pomocą innych technik wnioskowania",
+ "smw_isspecprop": "Ta właściwość jest unikalna dla tej wiki.",
+ "smw-concept-cache-header": "Użycie pamięci podręcznej",
+ "smw-concept-no-cache": "Brak dostępnej pamięci podręcznej.",
+ "smw_concept_description": "Opis koncepcji „$1â€",
+ "smw_no_concept_namespace": "Koncepcje można definiować tylko w przestrzeni nazw Koncepcja:",
+ "smw_multiple_concepts": "Każda strona konceptu może mieć tylko jedną definicję konceptu.",
+ "smw_concept_cache_miss": "Koncepcja „$1†w chwili obecnej nie może zostać użyta, ponieważ konfiguracja wiki wymaga aby została ona przeliczona offline. Jeśli problem nie ustąpi po jakimś czasie, skontaktuj się z administratorem witryny.",
+ "smw_noinvannot": "Nie można przypisać wartości właściwościom odwrotnym.",
+ "version-semantic": "Rozszerzenia semantyczne",
+ "smw_baduri": "Niestety, URI z przestrzeni „$1†nie są w tym miejscu dostępne.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Wyniki obliczeń",
+ "smw_printername_csv": "eksport CSV",
+ "smw_printername_dsv": "Eksport DSV",
+ "smw_printername_debug": "Śledzenie zapytania (dla specjalistów)",
+ "smw_printername_embedded": "Zagnieżdżone fragmenty strony",
+ "smw_printername_json": "eksport JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Zwykła lista",
+ "smw_printername_ol": "Lista numerowana",
+ "smw_printername_ul": "Lista nienumerowana",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Szeroka tabela",
+ "smw_printername_template": "Szablon",
+ "smw_printername_templatefile": "Plik szablonu",
+ "smw_printername_rdf": "Eksport RDF",
+ "smw_printername_category": "Kategoria",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Maksymalna liczba zwracanych wyników",
+ "smw-paramdesc-offset": "Przesunięcie pierwszego wyniku",
+ "smw-paramdesc-headers": "Wyświetlanie nazw nagłówków i właściwości",
+ "smw-paramdesc-mainlabel": "Etykieta dająca nazwę stronie głównej",
+ "smw-paramdesc-link": "Pokaż wartości jako linki",
+ "smw-paramdesc-intro": "Tekst wyświetlany przed wynikami zapytania, jeśli są jakieś",
+ "smw-paramdesc-outro": "Tekst wyświetlany poniżej wyników zapytania, jeśli są jakieś",
+ "smw-paramdesc-default": "Tekst wyświetlany jeśli brak jest wyników zapytania",
+ "smw-paramdesc-sep": "Separator oddzielajÄ…cy wyniki",
+ "smw-paramdesc-valuesep": "Separator między wartościami właściwości wyniku",
+ "smw-paramdesc-showsep": "Pokaż separator w górnej części pliku CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Zamiast wyświetlania wszystkich wartości, policz ich wystąpienia i pokaż to.",
+ "smw-paramdesc-distributionsort": "Sortuj rozkład wartości według liczby wystąpień.",
+ "smw-paramdesc-aggregation": "Określ, do czego powinna się odnosić agregacja",
+ "smw-paramdesc-template": "Nazwa szablonu, który zostanie wyświetlony na wydrukach",
+ "smw-paramdesc-columns": "Liczba kolumn, w których zostaną wyświetlone wyniki",
+ "smw-paramdesc-userparam": "Wartość przekazywana do szablonu przy każdym jego użyciu",
+ "smw-paramdesc-class": "Dodatkowa klasa CSS do ustawienia dla listy",
+ "smw-paramdesc-introtemplate": "Nazwa szablonu wyświetlana przed wynikami zapytania, jeśli są jakieś",
+ "smw-paramdesc-outrotemplate": "Nazwa szablonu wyświetlana poniżej wyników zapytania, jeśli są jakieś",
+ "smw-paramdesc-embedformat": "Znacznik HTML używany do definiowania nagłówków",
+ "smw-paramdesc-embedonly": "Nie wyświetlaj nagłówków",
+ "smw-paramdesc-table-class": "Dodatkowa klasa CSS do ustawienia dla tabeli",
+ "smw-paramdesc-table-transpose": "Wyświetlaj nagłówki tabeli pionowo, a wyniki poziomo",
+ "smw-paramdesc-rdfsyntax": "Wykorzystywana składnia RDF",
+ "smw-paramdesc-csv-sep": "Określa separator kolumn",
+ "smw-paramdesc-csv-valuesep": "Określa separator wartości",
+ "smw-paramdesc-csv-bom": "Dodaj BOM (znacznik kolejności bajtów) na początku pliku wynikowego",
+ "smw-paramdesc-dsv-separator": "Użyty separator",
+ "smw-paramdesc-dsv-filename": "Nazwa pliku DSV",
+ "smw-paramdesc-filename": "Nazwa pliku wyjściowego",
+ "smw-smwdoc-description": "Pokazuje tabelę wszystkich parametrów, które mogą być użyte do formatowania określonego wyniku wraz z wartościami domyślnymi i opisami.",
+ "smw-smwdoc-par-format": "Formatuj wynik, do wyświetlenia sparametryzowanej dokumentacji.",
+ "smw-smwdoc-par-parameters": "Które parametry wyświetlić. \"specific\" dla dodanych przez format, \"base\" dla dostępnych we wszystkich formatach oraz \"all\" do obu.",
+ "smw-paramdesc-sort": "Właściwość do sortowania tabeli przez",
+ "smw-paramdesc-order": "Kolejność sortowania zapytania",
+ "smw-paramdesc-searchlabel": "Treść linku do kolejnych wyników",
+ "smw-paramdesc-named_args": "Nazwa argumentu przekazana do szablonu",
+ "smw-paramdesc-template-arguments": "Ustawia sposób przekazywania nazwanych argumentów do szablonu",
+ "smw-paramdesc-export": "Opcje eksportu",
+ "smw-paramdesc-json-type": "Typ serializacji",
+ "smw-paramdesc-source": "Alternatywne źródło zapytania",
+ "smw-paramdesc-jsonsyntax": "Składnia JSON, która będzie używana",
+ "smw-printername-feed": "kanał RSS i Atom",
+ "smw_iq_disabled": "Niestety, w tym wiki wyłączono możliwość tworzenia zapytań w artykułach.",
+ "smw_iq_moreresults": "&hellip; dalsze wyniki",
+ "smw_parseerror": "Podana wartość jest niezrozumiała.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "„$1†nie może być użyte jako nazwa strony.",
+ "smw_noproperty": "„$1†nie może być użyte jako nazwa właściwości w tej wiki.",
+ "smw_wrong_namespace": "Dozwolone sÄ… tutaj tylko strony z przestrzeni nazw „$1â€.",
+ "smw_manytypes": "Zdefiniowano więcej niż jeden typ dla atrybutu.",
+ "smw_emptystring": "Puste łańcuchy znakowe są niedozwolone.",
+ "smw_notinenum": "„$1†nie znajduje siÄ™ na liÅ›cie ($2) [[Property:Allows value|dozwolonych wartoÅ›ci]] dla wÅ‚aÅ›ciwoÅ›ci „$3â€.",
+ "smw_noboolean": "“$1†nie zostało rozpoznane jako wartość logiczna (prawda/fałsz).",
+ "smw_true_words": "prawda,t,yes,y,tak,true",
+ "smw_false_words": "fałsz,f,no,n,nie,false",
+ "smw_nofloat": "„$1†nie jest liczbą.",
+ "smw_infinite": "Liczby tak duże jak „$1†nie są obsługiwane.",
+ "smw_unitnotallowed": "„$1†nie została zadeklarowana jako dopuszczalna jednostka miary dla tej właściwości.",
+ "smw_nounitsdeclared": "Dla tej właściwości nie zadeklarowano żadnych jednostek miary.",
+ "smw_novalues": "Nie określono wartości",
+ "smw_nodatetime": "Data „$1†nie została zrozumiana.",
+ "smw_toomanyclosing": "W zapytaniu jest zbyt wiele wystÄ…pieÅ„ „$1â€.",
+ "smw_noclosingbrackets": "W zapytaniu któryÅ› z podwójnych nawiasów „<nowiki>[[</nowiki>†nie zostaÅ‚ zamkniÄ™ty nawiasem „]]â€.",
+ "smw_misplacedsymbol": "Symbolu „$1†użyto w niewłaściwym miejscu.",
+ "smw_unexpectedpart": "Część „$1†zapytania jest niezrozumiała.\nWyniki mogą być inne od oczekiwanych.",
+ "smw_emptysubquery": "Podzapytanie nie ma prawidłowych warunków do wykonania.",
+ "smw_misplacedsubquery": "Podzapytanie zostało użyte w niedozwolonym miejscu.",
+ "smw_valuesubquery": "Podzapytania nie sÄ… dozwolone jako wartoÅ›ci wÅ‚aÅ›ciwoÅ›ci „$1â€.",
+ "smw_badqueryatom": "Część zapytania „<nowiki>[[…]]</nowiki>†nie została zrozumiana.",
+ "smw_propvalueproblem": "Wartość „$1†nie została rozpoznana.",
+ "smw_noqueryfeature": "Niektóre elementy zapytania nie są obsługiwane na tej wiki, pominięto część zapytania ($1).",
+ "smw_noconjunctions": "Koniunkcje w zapytaniach nie są obsługiwane na tej wiki, pominięto część zapytania ($1).",
+ "smw_nodisjunctions": "Alternatywy w zapytaniach nie są obsługiwane na tej wiki, pominięto część zapytania ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Następujący warunek zapytania nie mógł zostać uwzględniony|Następujące warunki zapytania nie mogły zostać uwzględnione}} ze względu na ograniczenia wiki w wielkości lub głębokości zapytania: <code>$1</code>.",
+ "smw_notemplategiven": "Należy podać parametr „templateâ€, aby to zapytanie zadziaÅ‚aÅ‚o.",
+ "smw_db_sparqlqueryproblem": "Nie można uzyskać wyniku na zapytanie z bazy danych SPARQL. Ten błąd może być tymczasowy lub wskazywać na błąd w oprogramowaniu bazy danych.",
+ "smw_db_sparqlqueryincomplete": "Odpowiedź na zapytanie okazała się zbyt trudna i została przerwana. Niektórych wyników może brakować. Jeśli to możliwe, spróbuj użyć prostszego zapytania.",
+ "smw_type_header": "Atrybuty typu “$1â€",
+ "smw_typearticlecount": "Pokazano $1 {{PLURAL:$1|atrybut używający|atrybuty używające|atrybutów używających}} tego typu.",
+ "smw_attribute_header": "Strony używajÄ…ce atrybutu “$1â€",
+ "smw_attributearticlecount": "Pokazano $1 {{PLURAL:$1|stronę używającą|strony używające|stron używających}} tego atrybutu.",
+ "smw-propertylist-subproperty-header": "Podwłaściwości",
+ "smw-propertylist-redirect-header": "Synonimy",
+ "smw-propertylist-error-header": "Strony z nieprawidłowymi przypisaniami",
+ "specialpages-group-smw_group": "Semantyczna MediaWiki",
+ "exportrdf": "Eksport stron do RDF",
+ "smw_exportrdf_docu": "Ta strona pozwala eksportować fragmenty artykułu w formacie RDF. Aby wyeksportować artykuły, wpisz ich tytuły w poniższym polu tekstowym, po jednym tytule w wierszu.",
+ "smw_exportrdf_recursive": "Rekursywny eksport wszystkich powiązanych stron. Zwróć uwagę, że wynik może być olbrzymi!",
+ "smw_exportrdf_backlinks": "Eksportuj także wszystkie strony, które odwołują się do eksportowanych stron. Tworzy przeglądalny RDF.",
+ "smw_exportrdf_lastdate": "Nie eksportuj stron, które nie były zmieniane od podanego czasu.",
+ "smw_exportrdf_submit": "Eksport",
+ "uriresolver": "Przekształcanie URI",
+ "properties": "Właściwości",
+ "smw_properties_docu": "Następujące właściwości są wykorzystywane w wiki.",
+ "smw_property_template": "$1 typu $2 ($3 {{PLURAL:$3|użycie|użycia}})",
+ "smw_propertylackspage": "Wszystkie właściwości powinny być opisane stroną!",
+ "smw_propertylackstype": "Nie określono żadnego typu dla tej właściwości (tymczasowo przypisano typ $1).",
+ "smw_propertyhardlyused": "Ta właściwość jest rzadko używana w przestrzeni wiki!",
+ "smw-property-name-invalid": "Właściwość $1 nie może być użyta (nieprawidłowa nazwa właściwości).",
+ "smw-sp-property-searchform": "Pokaż właściwości, które zawierają:",
+ "smw-special-property-searchform": "Wyświetl właściwości zawierające:",
+ "smw-special-property-searchform-options": "Opcje",
+ "smw-special-wantedproperties-filter-label": "Filtr:",
+ "smw-special-wantedproperties-filter-none": "Brak",
+ "smw-special-wantedproperties-filter-unapproved": "Niezatwierdzony",
+ "concepts": "Koncepty",
+ "smw-special-concept-header": "Lista konceptów",
+ "smw-special-concept-count": "Znaleziono {{PLURAL:$1|1 koncept|$1 koncepty|$1 konceptów}}.",
+ "smw-special-concept-empty": "Nie znaleziono konceptów.",
+ "unusedproperties": "Niewykorzystywane właściwości",
+ "smw-unusedproperties-docu": "Ta strona wymienia [https://www.semantic-mediawiki.org/wiki/Unused_properties niewykorzystywane właściwości], które są zadeklarowane, choć żadna inna strona z nich nie korzysta. Odmiennego spojrzenia dostarczają wykazy na stronach specjalnych [[Special:Properties|wszystkich]] oraz [[Special:WantedProperties|pożądanych właściwości]].",
+ "smw-unusedproperty-template": "$1 typu $2",
+ "wantedproperties": "Pożądane właściwości",
+ "smw-wantedproperties-docu": "Ta strona wymienia [https://www.semantic-mediawiki.org/wiki/Wanted_properties pożądane właściwości], które są używane w wiki, ale nie mają strony opisującej je. Odmiennego spojrzenia dostarczają wykazy na stronach specjalnych [[Special:Properties|wszystkich]] oraz [[Special:UnusedProperties|niewykorzystywanych właściwości]].",
+ "smw-wantedproperty-template": "$1 (wykorzystane {{PLURAL:$2|raz|$2 razy}})",
+ "smw-special-wantedproperties-docu": "Ta strona wymienia [https://www.semantic-mediawiki.org/wiki/Wanted_properties pożądane właściwości], które są używane w wiki, ale nie mają strony opisującej je. Odmiennego spojrzenia dostarczają wykazy na stronach specjalnych [[Special:Properties|wszystkich]] oraz [[Special:UnusedProperties|niewykorzystywanych właściwości]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|użycie|użycia|użyć}})",
+ "smw_purge": "Odśwież",
+ "smw-purge-failed": "Odświeżanie nie powiodło się",
+ "types": "Typy",
+ "smw_types_docu": "Wykaz [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes dostępnych typów danych]. Każdy [https://www.semantic-mediawiki.org/wiki/Help:Datatype typ] reprezentuje unikatowy zbiór atrybutów charakteryzujących wartości poprzez sposób ich składowania w pamięci i wyświetlania. Charakterystyki te są dziedziczone w przypisanych właściwościach.",
+ "smw-special-types-no-such-type": "\"$1\" jest nieznany lub nie został określony jako poprawny typ danych.",
+ "smw-statistics": "Statystyka semantyczna",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Wartość|Wartości}} właściwości (łącznie)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Właściwość|Właściwości}}]] (łącznie)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Właściwość|Właściwości}} (łącznie)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Właściwość|Właściwości}}]] (użycie z co najmniej jedną wartością)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Właściwość|Właściwości}} (zarejestrowane jako strona)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Właściwość (przypisana do typu danych)|Właściwości (przypisane do typu danych)}}",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Zapytanie|Zapytania}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Zapytanie|Zapytania}}]]",
+ "smw-statistics-query-size": "Wielkość zapytania",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Koncept|Koncepty}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Koncept|Koncepty}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Podobiekt|Podobiekty}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Podobiekt|Podobiekty}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Typ danych|Typy danych}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Wartość|Wartości}} właściwości ([[Special:ProcessingErrorList|{{PLURAL:$1|niewłaściwa adnotacja|niewłaściwe adnotacje}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Wartość|Wartości}} właściwości ({{PLURAL:$1|niewłaściwa adnotacja|niewłaściwe adnotacje}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Nieaktualna encja (oznaczona do usunięcia)|Nieaktualne encje (oznaczone do usunięcia)}}",
+ "smw_uri_doc": "Resolver URI implementuje [$1 W3C TAG finding on httpRange-14]. Dzięki temu ludzie nie zamieniają się w strony WWW.",
+ "ask": "Wyszukiwanie semantyczne",
+ "smw_ask_sortby": "Sortuj według kolumny (opcjonalnie)",
+ "smw_ask_ascorder": "RosnÄ…co",
+ "smw_ask_descorder": "MalejÄ…co",
+ "smw-ask-order-rand": "Losowy",
+ "smw_ask_submit": "Szukaj wyników",
+ "smw_ask_editquery": "Edytuj zapytanie",
+ "smw_add_sortcondition": "[Dodaj warunki sortowania]",
+ "smw-ask-sort-add-action": "Dodaj warunek sortowania",
+ "smw_ask_hidequery": "Ukryj zapytanie (widok skrócony)",
+ "smw_ask_help": "Pomoc dla tworzenia zapytań",
+ "smw_ask_queryhead": "Warunek",
+ "smw_ask_printhead": "Instrukcje wydruku",
+ "smw_ask_printdesc": "(dodaj w linii jedną nazwę właściwości)",
+ "smw_ask_format_as": "Formatuj jako",
+ "smw_ask_defaultformat": "domyślny",
+ "smw_ask_otheroptions": "Inne opcje",
+ "smw-ask-otheroptions-collapsed-info": "Aby wyświetlić wszystkie dostępne opcje użyj ikony plusa",
+ "smw_ask_show_embed": "Pokaż kod zagnieżdżony",
+ "smw_ask_hide_embed": "Ukryj zagnieżdżony kod",
+ "smw_ask_embed_instr": "Aby zagnieździć to zapytanie na stronie wiki, użyj poniższego kodu.",
+ "smw-ask-delete": "Usuń",
+ "smw-ask-sorting": "Sortowanie",
+ "smw-ask-options": "Opcje",
+ "smw-ask-options-sort": "Opcje sortowania",
+ "smw-ask-format-options": "Format i opcje",
+ "smw-ask-parameters": "Parametry",
+ "smw-ask-search": "Szukaj",
+ "smw-ask-debug": "Debugowanie",
+ "smw-ask-debug-desc": "Tworzy informacje o debugowaniu zapytań",
+ "smw-ask-no-cache": "Wyłącz pamięć podręczną",
+ "smw-ask-no-cache-desc": "Wyniki bez pamięci podręcznej zapytań",
+ "smw-ask-result": "Wynik",
+ "smw-ask-empty": "Wymaż wszystkie wpisy",
+ "smw-ask-download-link-desc": "Pobieranie wyników zapytań w formacie $1",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Pomoc w wybranym formacie: $1",
+ "smw-ask-condition-change-info": "Warunek został zmieniony i wyszukiwarka wymaga ponownego uruchomienia zapytania w celu uzyskania wyników odpowiadających nowym wymaganiom.",
+ "searchbyproperty": "Wyszukiwanie po atrybucie",
+ "processingerrorlist": "Lista błędów przetwarzania",
+ "propertylabelsimilarity": "Raport podobieństwa etykiet właściwości",
+ "smw_sbv_docu": "Wyszukiwanie wszystkich stron, które mają dany atrybut i wartość.",
+ "smw_sbv_novalue": "Wpisz wartość, lub zobacz wszystkie wartości atrybutów dla $1.",
+ "smw_sbv_displayresult": "Lista wszystkich stron, które mają atrybut $1 z wartością $2.",
+ "smw_sbv_displayresultfuzzy": "Lista wszystkich stron, które posiadajÄ… wÅ‚aÅ›ciwość „$1†o wartoÅ›ci „$2â€.\nPonieważ znaleziono tylko kilka wyników, pokazano również wyniki podobne.",
+ "smw_sbv_property": "Atrybut",
+ "smw_sbv_value": "Wartość:",
+ "smw_sbv_submit": "Znajdź wyniki",
+ "browse": "Przeglądanie artykułów",
+ "smw_browselink": "Przeglądaj właściwości",
+ "smw_browse_article": "Wpisz nazwę artykułu, od którego chcesz rozpocząć przeglądanie.",
+ "smw_browse_go": "Idź",
+ "smw_browse_show_incoming": "Pokaż właściwości linkujące tutaj",
+ "smw_browse_hide_incoming": "Ukryj właściwości linkujące tutaj",
+ "smw_browse_no_outgoing": "Ta strona nie ma żadnych właściwości.",
+ "smw_browse_no_incoming": "Żadne właściwości nie linkują do tej strony.",
+ "smw-browse-from-backend": "Informacje sÄ… obecnie pobierane z zaplecza.",
+ "smw-browse-api-subject-serialization-invalid": "Podmiot ma nieprawidłowy format serializacji.",
+ "smw-browse-js-disabled": "Prawdopodobnie JavaScript jest wyłączony lub niedostępny i zaleca się korzystanie z przeglądarki, w której jest on obsługiwany. Inne opcje omówione są na stronie parametrów konfiguracyjnych [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Pokaż grupy",
+ "smw-browse-hide-group": "Ukryj grupy",
+ "smw_inverse_label_default": "$1 z",
+ "smw_inverse_label_property": "Etykieta odwrotnej właściwości",
+ "pageproperty": "Szukanie właściwości stron",
+ "smw_pp_docu": "Wprowadź stronę i właściwość lub po prostu właściwość, aby pobrać wszystkie przypisane wartości.",
+ "smw_pp_from": "Od strony:",
+ "smw_pp_type": "Właściwość:",
+ "smw_pp_submit": "Znajdź wyniki",
+ "smw_result_prev": "Poprzednia",
+ "smw_result_next": "Następne",
+ "smw_result_results": "Wyniki",
+ "smw_result_noresults": "Niestety, brak wyników.",
+ "smwadmin": "Funkcje administracyjne i konserwacyjne",
+ "smw-admin-statistics-job-title": "Statystyki zadań",
+ "smw-admin-statistics-job-docu": "Statystyka zadań wyświetla informacje o zaplanowanych zadaniach Semantic MediaWiki, które nie zostały jeszcze wykonane. Liczba zadań może być trochę niedokładna lub zawierać nieudane próby. Więcej informacji można znaleźć na stronie [Manual:Job_queue/pl podręcznika].",
+ "smw-admin-statistics-querycache-disabled": "Usługa [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] nie jest włączona na tej wiki i żadne statystyki nie są dostępne.",
+ "smw-admin-permission-missing": "Dostęp do tej strony został zablokowany z powodu braku uprawnień. Na stronie pomocy na temat [https://www.semantic-mediawiki.org/wiki/Help:Permissions uprawnień] znajdziesz szczegółowe informacje o niezbędnych ustawieniach.",
+ "smw-admin-setupsuccess": "Silnik przestrzeni dyskowej został ustawiony.",
+ "smw_smwadmin_return": "Powrót do $1",
+ "smw_smwadmin_updatestarted": "Uruchomiono nowy proces aktualizacji danych semantycznych.\nWszystkie zapisane dane zostaną uporządkowane oraz naprawione w razie potrzeby.\nMożesz śledzić stan procesu aktualizacji na tej stronie specjalnej.",
+ "smw_smwadmin_updatenotstarted": "Jest już uruchomiony proces aktualizacji.\nKolejny nie zostanie utworzony.",
+ "smw_smwadmin_updatestopped": "Wszystkie istniejące procesy aktualizacji zostały zatrzymane.",
+ "smw_smwadmin_updatenotstopped": "W celu zatrzymania uruchomionego procesu aktualizacji należy zaznaczyć pole wyboru, aby potwierdzić decyzję.",
+ "smw-admin-docu": "Ta strona specjalna pomoże Ci w instalacji, modernizacji, utrzymaniu i użytkowaniu <a href=\"https://www.semantic-mediawiki.org\">Semantycznego MediaWiki</a> a także udostępnia dalsze funkcje i zadania administracyjne oraz statystyki.\nPamiętaj przed wykonaniem funkcji administracyjnych o utworzeniu kopii zapasowej ważnych danych.",
+ "smw-admin-db": "Konserwacja bazy danych",
+ "smw-admin-db-preparation": "Inicjalizacja tabeli trwa i może chwilę potrwać, zanim wyniki zostaną wyświetlone w oczekiwaniu na rozmiar i możliwe optymalizacje tabeli.",
+ "smw-admin-dbdocu": "Semantyczne MediaWiki wymaga rozszerzeń do bazy danych MediaWiki, które umożliwiają przechowywanie danych semantycznych.\nPoniższa funkcja zapewnia, że baza danych zostanie poprawnie przygotowana.\nZmiany wykonane w tym kroku nie mają wpływu na pozostałą część bazy danych MediaWiki i mogą łatwo zostać cofnięte w razie potrzeby.\nTa operacja może zostać wykonana wielokrotnie bez wyrządzenia szkód ale konieczna jest tylko raz w trakcie instalacji lub aktualizacji.",
+ "smw-admin-permissionswarn": "Jeśli operacja nie powiedzie się i wystąpią błędy SQL, najprawdopodobniej użytkownik bazy danych wykorzystywany przez Twoją wiki (sprawdź plik LocalSettings.php) nie ma wystarczających uprawnień.\nNadaj użytkownikowi dodatkowe uprawnienia do tworzenia i usuwania tabel, tymczasowo użyj konta root bazy danych w pliku LocalSettings.php lub użyj skryptu konserwacyjnego <code>setupStore.php</code>, który ma uprawnienia administratora.",
+ "smw-admin-dbbutton": "Inicjuj lub aktualizuj tabele",
+ "smw-admin-announce": "Ogłoś swoją wiki",
+ "smw-admin-deprecation-notice-docu": "Poniższa sekcja zawiera ustawienia, które zostały wycofane lub usunięte, ale zostały wykryte jako aktywne na tej wiki. Oczekuje się, że każda kolejna wersja usunie obsługę tych konfiguracji.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> jest przestarzały i zostanie usunięty w wersji $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> usunie (lub zastąpi) {{PLURAL:$2|następującą opcję|następujące opcje}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> jest przestarzały i zostanie usunięte w $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> został zastąpiony przez <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|opcja|opcje}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> zostaje zastÄ…piony przez <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> został usunięty w $2",
+ "smw-admin-deprecation-notice-title-notice": "NadchodzÄ…ce zmiany",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Wykryto w tej wiki następujące ustawienia, które planuje się usunąć lub zmienić w przyszłej wersji.",
+ "smw-admin-deprecation-notice-title-replacement": "ZastÄ…pione lub zmienione ustawienia",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Poniższa sekcja zawiera ustawienia, które zostały zmienione lub zmodyfikowane i zaleca się natychmiast zaktualizować ich nazwy lub format.",
+ "smw-admin-deprecation-notice-title-removal": "Usunięte ustawienia",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Wymienione ustawienia zostały usunięte w poprzedniej wersji, ale zostały wykryte na tej wiki.",
+ "smw-smwadmin-refresh-title": "Naprawa i aktualizacja danych",
+ "smw_smwadmin_datarefresh": "Odbudowa danych",
+ "smw_smwadmin_datarefreshdocu": "Istnieje możliwość przywrócenia wszystkich danych Semantic MediaWiki w oparciu o aktualną zawartość wiki.\nMoże to być przydatne do naprawy uszkodzonych danych lub odświeżenia danych, jeśli wewnętrzny format zmienił się z powodu jakichś aktualizacji oprogramowania.\nAktualizacja jest wykonywana strona po stronie i nie zostanie ukończona natychmiast.\nPoniżej przedstawiono, czy aktualizacja jest w toku. Można również uruchomić lub zatrzymać aktualizację (chyba, że ta funkcja została wyłączona przez administratora).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Aktualizacja trwa.</strong>\nTo normalne, że aktualizacja postępuje powoli, gdyż odświeżanie danych wykonywane jest w małymi porcjami za każdym razem, gdy użytkownik korzysta z wiki.\nAby szybciej zakończyć aktualizację można uruchomić skrypt MediaWiki <code>runJobs.php</code> (użyj opcji <code>--maxjobs 1000</code>, aby ograniczyć liczbę aktualizacji, które wykonywane są podczas jednego uruchomienia).\nPrzewidywany postęp bieżącej aktualizacji:",
+ "smw_smwadmin_datarefreshbutton": "Zaplanuj przebudowÄ™ danych",
+ "smw_smwadmin_datarefreshstop": "Zatrzymaj aktualizacjÄ™ danych",
+ "smw_smwadmin_datarefreshstopconfirm": "Tak, jestem {{GENDER:$1|pewny|pewna}}.",
+ "smw-admin-outdateddisposal-button": "Zaplanuj usuwanie",
+ "smw-admin-feature-disabled": "Ta funkcja została wyłączona w tej wiki. Aby uzyskać więcej szczegółów, przeczytaj <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">powiązaną</a> stronę pomocy.",
+ "smw-admin-support": "Uzyskiwanie wsparcia",
+ "smw-admin-supportdocu": "Różnorodne źródła informacji mogą okazać się przydatne, jeśli wystąpią problemy:",
+ "smw-admin-installfile": "Jeśli napotkasz trudności z instalacją, zacznij od sprawdzenia wskazówek w <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">pliku INSTALL</a> /a> i <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">na stronie instalacji</a>.",
+ "smw-admin-smwhomepage": "Kompletna dokumentacja Semantic MediaWiki znajduje siÄ™ na stronie <strong><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></strong>.",
+ "smw-admin-bugsreport": "Błędy możesz zgłaszać w <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Jeśli nadal masz pytania lub sugestie, przyłącz się do dyskusji na <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">forum użytkowników Semantic MediaWiki</a>.",
+ "smw-admin-other-functions": "Inne funkcje",
+ "smw-admin-supplementary-section-title": "Funkcje dodatkowe",
+ "smw-admin-supplementary-section-subtitle": "Dostępne funkcje",
+ "smw-admin-supplementary-section-intro": "Niektóre z wymienionych funkcji w tej sekcji mogą być ograniczone i dlatego są niedostępne na tej wiki.",
+ "smw-admin-supplementary-settings-title": "Ustawienia konfiguracji",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> wyprowadza zbiorczą listę dostępnych ustawień używanych w Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Statystyki operacyjne",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> pokazuje rozszerzony zestaw statystyk",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Statystyki pamięci cache",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> wyświetla statystyki dotyczące pamięci cache",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> informuje o ustawieniach i indeksie statystyk",
+ "smw-admin-supplementary-elastic-functions": "Dostępne funkcje",
+ "smw-admin-supplementary-elastic-settings-title": "Ustawienia",
+ "smw-admin-supplementary-elastic-mappings-title": "Mapowania",
+ "smw-admin-supplementary-elastic-mappings-summary": "Podsumowanie",
+ "smw-admin-supplementary-elastic-nodes-title": "Węzły",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> pokazuje statystyki węzła",
+ "smw-admin-supplementary-elastic-indices-title": "Wskaźniki",
+ "smw-admin-supplementary-elastic-statistics-title": "Statystyki",
+ "smw-admin-supplementary-elastic-status-replication": "Status replikacji",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Ostatnia aktywna replikacja: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Interwał odświeżania: $1",
+ "smw-list-count": "Lista zawiera $1 {{PLURAL:$1|pozycjÄ™|pozycje}}.",
+ "smw-list-count-from-cache": "Lista zawiera $1 {{PLURAL:$1|wpis|wpisy|wpisów}} i została pobrana z pamięci podręcznej (UTC: $2).",
+ "smw-property-label-similarity-title": "Raport podobieństwa etykiet właściwości",
+ "smw-property-label-similarity-intro": "<u>$1</u> oblicza podobieństwa dla istniejących etykiet właściwości",
+ "smw-property-label-similarity-threshold": "Próg:",
+ "smw-property-label-similarity-type": "Wyświetl ID typu",
+ "smw-property-label-similarity-noresult": "Nie znaleziono wyników dla wybranych opcji.",
+ "smw_adminlinks_datastructure": "Struktura danych",
+ "smw_adminlinks_displayingdata": "Pokaż dane",
+ "smw_adminlinks_inlinequerieshelp": "Pomoc dotycząca wewnętrznych zapytań",
+ "smw-property-indicator-type-info": "Właściwość zdefiniowana przez {{PLURAL:$1|użytkownika|system}}",
+ "smw-createproperty-isproperty": "To jest właściwość typu $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Dopuszczalna wartość dla tej własności to|Dopuszczalne wartości dla tej własności to:}}",
+ "smw-paramdesc-category-delim": "Ogranicznik",
+ "smw-paramdesc-category-template": "Szablon do formatowania elementów z",
+ "smw-paramdesc-category-userparam": "Parametr do przekazania do szablonu",
+ "smw-info-par-message": "Wiadomość do wyświetlenia.",
+ "prefs-smw": "Semantyczna MediaWiki",
+ "prefs-general-options": "Opcje ogólne",
+ "prefs-ask-options": "Opcje wyszukiwania semantycznego",
+ "smw-prefs-intro-text": "Opcje poniżej są dostarczane przez [https://www.semantic-mediawiki.org/ semantyczną MediaWiki] (lub powiązane rozszerzenia), aby umożliwić indywidualne dostosowanie do wybranych funkcji. Więcej informacji na ten temat można uzyskać w [https://www.semantic-mediawiki.org/wiki/Help:User_preferences sekcji pomocy].",
+ "smw-prefs-ask-options-tooltip-display": "Wyświetlaj opis parametru za pomocą „dymku†z informacją",
+ "smw-prefs-general-options-disable-editpage-info": "Wyłącz tekst wprowadzający na stronie edycji",
+ "smw-prefs-general-options-disable-search-info": "Wyłącz informacje dotyczące obsługi składni na standardowej stronie wyszukiwania",
+ "smw-ui-tooltip-title-property": "Właściwość",
+ "smw-ui-tooltip-title-quantity": "Konwersja jednostek",
+ "smw-ui-tooltip-title-info": "Informacja",
+ "smw-ui-tooltip-title-service": "Linki usługi",
+ "smw-ui-tooltip-title-warning": "Ostrzeżenie",
+ "smw-ui-tooltip-title-error": "BÅ‚Ä…d",
+ "smw-ui-tooltip-title-parameter": "Parametr",
+ "smw-ui-tooltip-title-event": "Wydarzenie",
+ "smw-ui-tooltip-title-note": "Notatka",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Przypis",
+ "smw_unknowntype": "Typ \"$1\" tego atrybutu jest nieprawidłowy.",
+ "smw_concept_header": "Strony koncepcji „$1â€",
+ "smw_conceptarticlecount": "{{PLURAL:$1|Wyświetlona jest jedna strona |Wyświetlone zostały $1 strony|Wyświetlonych zostało $1 stron}}.",
+ "smw-qp-empty-data": "Żądane dane nie mogły zostać wyświetlone ze względu na niewystarczające kryteria wyboru.",
+ "right-smw-admin": "Dostęp do zarządzania (Semantyczna MediaWiki)",
+ "right-smw-ruleedit": "Edycja strony reguł (Semantyczna MediaWiki)",
+ "restriction-level-smw-pageedit": "zabezpieczono (tylko użytkownicy z odpowiednimi uprawnieniami)",
+ "action-smw-patternedit": "edycji wyrażeń regularnych, używanych w Semantic MediaWiki",
+ "group-smwadministrator": "Administratorzy (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrator|administratorka}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administratorzy (Semantic MediaWiki)",
+ "group-smwcurator": "Kuratorzy (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|kurator (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Kuratorzy (Semantic MediaWiki)",
+ "action-smw-admin": "dostęp do zarządzania semantycznej MediaWiki",
+ "action-smw-ruleedit": "edytowania strony reguł (Semantic MediaWiki)",
+ "smw-property-predefined-default": "„$1†jest właściwością predefiniowaną.",
+ "smw-property-predefined-long-askde": "Jest to wartość numeryczna obliczana na podstawie zagnieżdżenia podkwerendy, łańcuchów własności i dostępnych elementów opisu z wykonaniem zapytania ograniczonego przez parametr konfiguracyjny <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-sp-properties-docu": "Ta strona wymienia [https://www.semantic-mediawiki.org/wiki/Property właściwości] dostępne dla tej wiki oraz liczniki ich użycia. Dla utrzymania aktualności statystyk liczników zaleca się regularne uruchamianie skryptu administracyjnego [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics statystyk właściwości]. Odmiennego spojrzenia dostarczają wykazy na stronach specjalnych [[Special:UnusedProperties|właściwości niewykorzystywanych]] oraz [[Special:WantedProperties|pożądanych]].",
+ "smw-sp-properties-cache-info": "Wymienione dane zostały pobrane z [https://www.semantic-mediawiki.org/wiki/Caching cache] i były ostatnio aktualizowane $1.",
+ "smw-sp-properties-header-label": "Lista właściwości",
+ "smw-sp-admin-settings-button": "Utwórz listę ustawień",
+ "smw-admin-idlookup-title": "Wyszukaj ID obiektu",
+ "smw-admin-idlookup-input": "Wyszukiwanie:",
+ "smw-admin-objectid": "Identyfikator:",
+ "smw-admin-tab-general": "PrzeglÄ…d",
+ "smw-admin-tab-supplement": "Funkcje dodatkowe",
+ "smw-admin-tab-registry": "Rejestr",
+ "smw-livepreview-loading": "Trwa ładowanie…",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista wyników",
+ "smw-sp-searchbyproperty-nonvaluequery": "Lista wartoÅ›ci, które majÄ… przypisanÄ… wÅ‚aÅ›ciwość „$1â€.",
+ "smw-sp-searchbyproperty-valuequery": "Lista stron, które majÄ… wÅ‚aÅ›ciwość „$1†z wartoÅ›ciÄ… „$2â€.",
+ "smw-datavalue-number-textnotallowed": "„$1†nie może być przypisane do zadeklarowanego typu liczbowego o wartości $2.",
+ "smw-datavalue-number-nullnotallowed": "„$1†zwróciÅ‚ wartość „NULLâ€, która nie jest dozwolona jako liczba.",
+ "smw-search-input": "Wprowadź i wyszukaj",
+ "smw-search-syntax": "Składnia",
+ "smw-search-profile-tooltip": "Funkcje wyszukiwania w połączeniu z Semantyczną MediaWiki",
+ "smw-search-profile-sort-recent": "Najnowsze",
+ "smw-search-profile-sort-title": "Tytuł",
+ "smw-search-profile-extended-help-namespace": "Pole wyboru przestrzeni nazw zostanie ukryte, gdy tylko zostanie wybrany formularz, ale można je pokazać za pomocÄ… przycisku „pokaż/ukryjâ€.",
+ "smw-search-profile-extended-help-query-link": "(Aby uzyskać więcej szczegółów $1).",
+ "smw-search-profile-extended-section-sort": "Sortuj według",
+ "smw-search-profile-extended-section-namespace": "Przestrzeń nazw",
+ "smw-search-profile-extended-section-query": "Zapytanie",
+ "smw-search-profile-link-caption-query": "zobacz",
+ "smw-search-show": "Pokaż",
+ "smw-search-hide": "Ukryj",
+ "log-name-smw": "Rejestr semantycznej MediaWiki",
+ "log-show-hide-smw": "$1 rejestr semantycznej MediaWiki",
+ "logeventslist-smw-log": "Rejestr Semantycznej MediaWiki",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Import $1]]",
+ "smw-property-predefined-long-errp": "W większości przypadków jest to spowodowane niedopasowaniem lub ograniczeniem [[Property:Allows value|wartości]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value „$1â€] jest predefiniowanÄ… wÅ‚aÅ›ciwoÅ›ciÄ…, która może zdefiniować listÄ™ dopuszczalnych wartoÅ›ci ograniczajÄ…cych przyporzÄ…dkowania wartoÅ›ci dla wÅ‚aÅ›ciwoÅ›ci.",
+ "smw-datavalue-property-restricted-annotation-use": "Właściwość „$1†ma ograniczony obszar zastosowania i nie może być używana przez użytkownika jako właściwość adnotacji.",
+ "smw-datavalue-property-restricted-declarative-use": "Właściwość „$1†jest właściwością deklaratywną i może być używana tylko na stronie właściwości lub kategorii.",
+ "smw-datavalue-property-invalid-character": "WÅ‚aÅ›ciwość „$1†zawiera znak „$2â€, bÄ™dÄ…cy część etykiety wÅ‚aÅ›ciwoÅ›ci i dlatego zostaÅ‚a zaklasyfikowana jako nieprawidÅ‚owa.",
+ "smw-datavalue-restricted-use": "Wartość danych „$1†została oznaczona do ograniczonego użytku.",
+ "smw-datavalue-invalid-number": "„$1†nie może być interpretowane jako liczba.",
+ "smw-types-list": "Lista typów danych",
+ "smw-types-default": "„$1†jest wbudowanym typem danych.",
+ "smw-types-help": "Więcej informacji i przykładów można znaleźć na tej [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 stronie pomocy].",
+ "smw-type-boo": "„$1†jest podstawowym typem danych opisującym wartość prawda/fałsz.",
+ "smw-type-tel": "„$1†to specjalny typ danych do opisywania międzynarodowych numerów telefonicznych zgodnie z RFC 3966.",
+ "smw-type-dat": "„$1†to podstawowy typ danych, reprezentujący punkty w czasie w ujednoliconym formacie.",
+ "smw-type-ema": "„$1†jest specjalnym typem danych reprezentującym adres e-mail.",
+ "smw-type-tem": "„$1†jest specjalnym typem danych reprezentującym temperaturę.",
+ "smw-type-qty": "„$1†jest typem danych opisującym wielkości z reprezentacją numeryczną i jednostką miary.",
+ "smw-type-tab-properties": "Właściwości",
+ "smw-type-tab-types": "Typy",
+ "smw-type-tab-errors": "Błędy",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-property-predefined-errt": "„$1†jest predefiniowaną właściwością, zawierającą tekstowy opis błędu i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-mdat": "„$1†jest predefiniowaną właściwością, która odpowiada dacie ostatniej modyfikacji tematu i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-cdat": "„$1†jest predefiniowaną właściwością, która odpowiada dacie pierwszej wersji tematu i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-newp": "„$1†jest predefiniowaną właściwością, wskazującą, czy temat jest nowy, czy nie, i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-mime": "„$1†jest predefiniowaną właściwością, opisującą typ MIME przesłanego pliku i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-prec": "„$1†jest predefiniowaną właściwością, opisującą [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precyzję wyświetlania] (w cyfrach dziesiętnych) dla liczbowych typów danych.",
+ "smw-datavalue-monolingual-dataitem-missing": "Brak oczekiwanego elementu do budowania jednojęzycznej wartości złożonej.",
+ "smw-datavalue-languagecode-missing": "W przypadku adnotacji „$1†parser nie byÅ‚ w stanie okreÅ›lić kodu jÄ™zyka (np. „foo@enâ€).",
+ "smw-datavalue-languagecode-invalid": "„$1†nie został rozpoznany jako obsługiwany kod języka.",
+ "smw-property-predefined-lcode": "„$1†to predefiniowana właściwość, reprezentująca kod języka sformatowany w BCP47 i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "„$1†to typ danych [https://www.semantic-mediawiki.org/wiki/Help:Container kontener], który wiąże wartość tekstową z określonym [[Property:Language code|kodem języka]].",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekunda|sekundy|sekund}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|sekunda|sekundy|sekund}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekunda|sekundy|sekund}}",
+ "smw-datavalue-allows-pattern-mismatch": "Wyrażenie regularne „$2†zaklasyfikowało wartość „$1†jako niepoprawną.",
+ "smw-datavalue-feature-not-supported": "Funkcja „$1†jest nieobsługiwana lub została wyłączona w tej wiki.",
+ "smw-property-predefined-boo": "„$1†jest [[Special:Types/Boolean|typem danych]] i predefiniowaną właściwością do reprezentowania wartości logicznych i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-num": "„$1†jest [[Special:Types/Number|typem danych]] i predefiniowaną właściwością do reprezentowania wartości liczbowych i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-dat": "„$1†jest [[Special:Types/Date|typem danych]] i predefiniowaną właściwością do reprezentowania wartości daty i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-uri": "„$1†jest [[Special:Types/URL|typem danych]] i predefiniowaną właściwością do reprezentowania wartości URI/URL i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-property-predefined-qty": "„$1†jest [[Special:Types/Quantity|typem danych]] i predefiniowaną właściwością do reprezentowania wartości ilościowych i pochodzi z [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantycznej MediaWiki].",
+ "smw-datavalue-time-invalid-date-components-common": "„$1†zawiera informacje, które nie jest interpretowalne.",
+ "smw-datavalue-time-invalid-date-components-empty": "„$1†zawiera puste elementy.",
+ "smw-datavalue-time-invalid-date-components-three": "„$1†zawiera więcej niż trzy komponenty potrzebne do interpretacji daty.",
+ "smw-datavalue-time-invalid-ampm": "„$1†zawiera „$2†jako element godziny, który jest nieprawidłowy dla konwencji 12-godzinnej.",
+ "smw-datavalue-external-formatter-invalid-uri": "„$1†jest nieprawidłowym adresem URL.",
+ "smw-datavalue-keyword-maximum-length": "Słowo kluczowe przekroczyło maksymalną długość $1 {{PLURAL:$1|znaku|znaków}}.",
+ "smw-datavalue-parse-error": "Podana wartość „$1†nie została niezrozumiała.",
+ "smw-datavalue-propertylist-invalid-property-key": "Lista wÅ‚aÅ›ciwoÅ›ci „$1†zawiera nieprawidÅ‚owy klucz wÅ‚aÅ›ciwoÅ›ci „$2â€.",
+ "smw-datavalue-type-invalid-typeuri": "Typu „$1†nie można przekształcić w prawidłową reprezentację URI.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-parser-invalid-json-format": "Parser JSON zwróciÅ‚ bÅ‚Ä…d „$1â€.",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "„$1†nie może być użyty jako preferowana etykieta, ponieważ jÄ™zyk „$2†jest już przypisany do etykiety „$3â€.",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Kopiuj link do schowka",
+ "smw-data-lookup": "Pobieranie danych...",
+ "smw-data-lookup-with-wait": "Żądanie jest przetwarzane i może zająć chwilę.",
+ "smw-no-data-available": "Brak dostępnych danych.",
+ "smw-property-req-violation-predefined-type": "WÅ‚aÅ›ciwość „$1†jako predefiniowana wÅ‚aÅ›ciwość zawiera deklaracjÄ™ typu „$2â€, która jest niezgodna z domyÅ›lnym typem tej wÅ‚aÅ›ciwoÅ›ci.",
+ "smw-property-req-violation-type": "Właściwość zawiera konkurencyjne specyfikacje typów, które mogą skutkować adnotacjami o nieprawidłowej wartości, dlatego oczekuje się, że użytkownik przydzieli jeden odpowiedni typ.",
+ "protect-level-smw-pageedit": "Zezwalaj tylko użytkownikom z uprawnieniami do edycji stron (Semantic MediaWiki)",
+ "smw-edit-protection": "Ta strona jest [[Property:Is edit protected|zabezpieczona]], aby zapobiec przypadkowej modyfikacji danych i może być edytowana tylko przez użytkowników z odpowiednimi uprawnieniami do edycji („$1â€) lub [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupÄ™ użytkowników].",
+ "smw-format-datatable-emptytable": "Brak danych dostępnych w tabeli",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-loadingrecords": "Åadowanie...",
+ "smw-format-datatable-processing": "Przetwarzanie...",
+ "smw-format-datatable-search": "Wyszukiwanie:",
+ "smw-format-datatable-zerorecords": "Nie znaleziono pasujÄ…cych pozycji",
+ "smw-format-datatable-first": "Pierwszy",
+ "smw-format-datatable-last": "Ostatni",
+ "smw-format-datatable-next": "Następny",
+ "smw-format-datatable-previous": "Poprzedni",
+ "smw-format-datatable-toolbar-export": "Eksportuj",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "Kategoria „$1†zawiera nieprawidłowy cel przekierowania do przestrzeni nazw innej niż kategoria.",
+ "smw-postproc-queryref": "Semantyczna MediaWiki zainicjuje odświeżanie strony z powodu wymaganego przetwarzania zapytania.",
+ "apihelp-smwinfo-summary": "Moduł API do pobierania informacji o statystykach Semantic MediaWiki i innych metainformacjach.",
+ "apihelp-browsebyproperty-summary": "Moduł API do pobierania informacji o właściwości lub liście właściwości.",
+ "apihelp-browsebysubject-summary": "Moduł API do pobierania informacji o temacie.",
+ "apihelp-smwtask-summary": "Moduł API do wykonywania zadań związanych z Semantic MediaWiki.",
+ "smw-api-invalid-parameters": "NieprawidÅ‚owe parametry, „$1â€",
+ "smw-property-page-list-count": "Wyświetlanie $1 {{PLURAL:$1|strony|stron}} przy użyciu tej właściwości.",
+ "smw-property-page-list-search-count": "WyÅ›wietlanie $1 {{PLURAL:$1|strony|stron}} przy użyciu tej wÅ‚aÅ›ciwoÅ›ci z dopasowaniem wartoÅ›ci „$2â€.",
+ "smw-property-reserved-category": "Kategoria",
+ "smw-category": "Kategoria",
+ "smw-browse-property-group-title": "Grupa właściwości",
+ "smw-browse-property-group-label": "Etykieta grypy właściwości",
+ "smw-browse-property-group-description": "Opis grupy właściwości",
+ "smw-filter": "Filtr",
+ "smw-section-expand": "Rozwiń sekcję",
+ "smw-section-collapse": "Zwiń sekcję",
+ "smw-ask-format-help-link": "format [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Pomoc",
+ "smw-cheat-sheet": "ÅšciÄ…gawka",
+ "smw-property-predefined-label-skey": "Klucz sortowania",
+ "smw-processing": "Przetwarzanie...",
+ "smw-redirect-target-unresolvable": "Cel jest nierozwiÄ…zalny z powodu „$1â€",
+ "smw-types-title": "Typ: $1",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Typ",
+ "smw-schema-tag": "{{PLURAL:$1|Znacznik|Znaczniki}}",
+ "smw-ask-title-keyword-type": "Wyszukiwanie słów kluczowych",
+ "smw-ask-message-keyword-type": "To wyszukiwanie pasuje do warunku <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Nie można poÅ‚Ä…czyć siÄ™ ze zdalnym celem „$1â€.",
+ "smw-parameter-missing": "Brak parametru „$1â€.",
+ "smw-property-tab-redirects": "Synonimy",
+ "smw-property-tab-subproperties": "Podwłaściwości",
+ "smw-property-tab-errors": "Niewłaściwe przypisania",
+ "smw-property-tab-specification": "... więcej",
+ "smw-concept-tab-errors": "Błędy",
+ "smw-ask-tab-result": "Wynik",
+ "smw-ask-tab-code": "Kod"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pms.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pms.json
new file mode 100644
index 00000000..d9eb89c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pms.json
@@ -0,0 +1,241 @@
+{
+ "@metadata": {
+ "authors": [
+ "Borichèt",
+ "Dragonòt"
+ ]
+ },
+ "smw-desc": "Rende soa wiki pì acessìbil - për le màchine ''e'' j'uman ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentassion an linia])",
+ "smw_viewasrdf": "Fluss RDF",
+ "smw_finallistconjunct": "e",
+ "smw_isspecprop": "Costa propietà a l'é na propietà special ëd costa wiki.",
+ "smw_concept_description": "Descrission dël concet «$1»",
+ "smw_no_concept_namespace": "Ij concet a peulo mach esse definì ant le pàgine dlë spassi nominal Concept:.",
+ "smw_multiple_concepts": "Minca pàgina ëd concet a peul avèj mach na definission.",
+ "smw_concept_cache_miss": "Ël concet «$1» a peul pa esse dovrà ant ës moment, përchè la configurassion ëd la wiki a ciama ch'a sia calcolà fòra ëd linia.\nSe ël problema a van nen via an chèich moment, ciamé a l'aministrator ëd sò sit ëd rende 's concet disponìbil.",
+ "smw_noinvannot": "Ij valor a peulo pa esse assignà a le proprietà anverse.",
+ "version-semantic": "Estension semàntiche",
+ "smw_baduri": "URI dla forma \"$1\" a son pa përmëttù.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Conta dj'arzultà",
+ "smw_printername_csv": "esportassion an CSV",
+ "smw_printername_dsv": "esportassion an DSV",
+ "smw_printername_debug": "Arcesta d'eliminassion dij bigat (për gent pràtica)",
+ "smw_printername_embedded": "Contù dle pàgine mojà",
+ "smw_printername_json": "esportassion an JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_ol": "Enumerassion",
+ "smw_printername_ul": "Detaj",
+ "smw_printername_table": "Tàula",
+ "smw_printername_broadtable": "Tàula estèisa",
+ "smw_printername_template": "Stamp",
+ "smw_printername_rdf": "Esportassion RDF",
+ "smw_printername_category": "Categorìa",
+ "validator-type-class-SMWParamSource": "test",
+ "smw-paramdesc-limit": "Ël màssim nùmer d'arzultà da smon-e",
+ "smw-paramdesc-offset": "La diferensa dël prim arzultà",
+ "smw-paramdesc-headers": "Mostré j'antestassion/ij nòm dle proprietà",
+ "smw-paramdesc-mainlabel": "L'etichëtta da dé al nòm ëd la pàgina prinsipal",
+ "smw-paramdesc-link": "Smon-e ij valor com dle liure",
+ "smw-paramdesc-intro": "Ël test da mostré prima dj'arzultà dl'arcesta, s'a-i na j'é",
+ "smw-paramdesc-outro": "Ël test da mostré apress a j'arzultà dl'arcesta, s'a-i na j'é",
+ "smw-paramdesc-default": "Ël test da mostré s'a-i é gnun arzultà a l'arcesta",
+ "smw-paramdesc-sep": "Ël separator për ij valor",
+ "smw-paramdesc-showsep": "Smon-e ël separator an testa a l'archivi CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Nopà ëd visualisé tùit ij valor, conté soe ocorense, e mostreje.",
+ "smw-paramdesc-distributionsort": "Ordiné la distribussion dij valor për nùmer d'ocorense.",
+ "smw-paramdesc-distributionlimit": "Limité la distribussion dij valor al cont ëd mach chèich valor.",
+ "smw-paramdesc-template": "Ël nòm ëd në stamp con ël qual visualisé j'arzultà",
+ "smw-paramdesc-columns": "Ël nùmer ëd colòne andoa mostré j'arzultà (lë stàndard a l'é $1)",
+ "smw-paramdesc-userparam": "Un valor passà an mincadun-a ciamà dë stamp, se në stamp a l'é dovrà",
+ "smw-paramdesc-introtemplate": "Ël nòm ëd në stamp da mostré prima dj'arzultà dl'arserca, s'a-i në j'é",
+ "smw-paramdesc-outrotemplate": "Ël nòm ëd në stamp da mostré apress ëd j'arzultà dl'arserca, s'a-i në j'é",
+ "smw-paramdesc-embedformat": "L'etichëtta HTML dovrà për definì j'antestassion",
+ "smw-paramdesc-embedonly": "Smon-e nen j'antestassion",
+ "smw-paramdesc-table-class": "Na classa CSS adissional da amposté për la tàula",
+ "smw-paramdesc-rdfsyntax": "La sintassi RDF da dovré",
+ "smw-paramdesc-csv-sep": "Ël separator da dovré",
+ "smw-paramdesc-dsv-separator": "Ël separator da dovré",
+ "smw-paramdesc-dsv-filename": "Ël nòm për l'archivi DSV",
+ "smw-smwdoc-description": "A mostra na tàula ëd tùit ij paràmetr che a peulo esse dovrà për ël formà d'arzultà specificà ansema con dij valor e dle descrission predefinì.",
+ "smw-smwdoc-par-format": "Ël formà dl'arzultà për mostré la documentassion d'un paràmetr.",
+ "smw-smwdoc-par-parameters": "Che paràmetr mostré. «specific» për coj giontà dal formà, «base» për coj disponìbij an tùit ij formà, e «all» për tùit doi.",
+ "smw-paramdesc-sort": "Propietà conforma a la qual ordiné l'arcesta",
+ "smw-paramdesc-order": "Órdin ëd la selession dl'arcesta",
+ "smw-paramdesc-searchlabel": "Ël test për continué l'arserca",
+ "smw-paramdesc-named_args": "Nòmina j'argoment passà a lë stamp",
+ "smw-paramdesc-export": "Opsion d'esportassion",
+ "smw-paramdesc-prettyprint": "Na stampa bin fàita che a visualisa ëd neuve righe e andentassion",
+ "smw-paramdesc-source": "Sorgiss d'arcesta alternativa",
+ "smw-paramdesc-jsonsyntax": "Sintassi JSON da dovré",
+ "smw-printername-feed": "Fluss RSS e Atom",
+ "smw-paramdesc-feedtype": "Sòrt ëd fluss",
+ "smw-paramdesc-feedtitle": "Ël test da mostré com tìtol dël fluss",
+ "smw-paramdesc-feeddescription": "Ël test da dovré com descrission dël fluss",
+ "smw-paramdesc-feedpagecontent": "Contnù ëd la pàgina da smon-e con ël fluss",
+ "smw-label-feed-description": "$1 $2 fluss",
+ "smw_iq_disabled": "J'arceste semàntiche a son ëstàite disabilità për sta wiki-sì.",
+ "smw_iq_moreresults": "... àutri arzultà",
+ "smw_parseerror": "Ël valor dàit a l'é pa stàit capì.",
+ "smw_notitle": "''$1'' a peul pa esse dovrà com nòm ëd pàgina an sta wiki-sì.",
+ "smw_noproperty": "''$1'' a peul pa esse dovrà com nòm ëd propietà an sta wiki-sì.",
+ "smw_wrong_namespace": "Mach le pàgine ant lë spassi nominal ''$1'' a son possìbij ambelessì.",
+ "smw_manytypes": "Pi che un tipo definì për proprietà.",
+ "smw_emptystring": "Stringhe veuide a son pa acetà.",
+ "smw_notinenum": "''$1'' a l'é pa ant la lista dij valor possìbij ($2) për sta proprietà-sì.",
+ "smw_noboolean": "''$1'' a l'é pa arconossù 'me valor Boolean (ver/fàuss).",
+ "smw_true_words": "ver,v,é!,é",
+ "smw_false_words": "fàuss,f,nò,n",
+ "smw_nofloat": "''$1'' a l'é pa un nùmer",
+ "smw_infinite": "Nùmer gròss com ''$1'' a son pa mantnù.",
+ "smw_unitnotallowed": "\"$1\" a l'é pa diciarà com unità dë mzura bon-a për costa proprietà.",
+ "smw_nounitsdeclared": "Gnun-e unità dë mzura a son stàite diciarà për costa propietà.",
+ "smw_novalues": "Pa gnun valor specificà.",
+ "smw_nodatetime": "La data ''$1'' a l'é pa stàita capìa.",
+ "smw_toomanyclosing": "A smija ch'a-i sio tròpe ocorense ëd ''$1'' ant l'arcesta.",
+ "smw_noclosingbrackets": "Chèich usagi ëd \"<nowiki>[[</nowiki>\" an soa arcesta a son pa stàit sarà da un corëspondent \"]]\".",
+ "smw_misplacedsymbol": "Ël sìmbol \"$1\" a l'era dovrà ant un pòst andoa a l'é pa ùtil.",
+ "smw_unexpectedpart": "La part \"$1\" ëd l'arcesta a l'é pa stàita capìa.\nJ'arzultà a peulo esse pa coma spetà.",
+ "smw_emptysubquery": "Chèich sot-arcesta a l'ha ëd condission pa bon-e.",
+ "smw_misplacedsubquery": "Chèich sot-arcesta a l'era dovrà ant un pòst andoa gnun-e sot-arceste a j'ero përmëttùe.",
+ "smw_valuesubquery": "Sot-arcesta pa mantnùa për ij valor ëd la proprietà \"$1\".",
+ "smw_badqueryatom": "Chèich part \"<nowiki>[[…]]</nowiki>\" ëd l'arcesta a l'é pa stàita capìa.",
+ "smw_propvalueproblem": "Ël valor ëd la proprietà \"$1\" a l'é pa stàit capì.",
+ "smw_noqueryfeature": "Chèica funsion ëd l'arcesta a l'é pa mantnù su sta wiki-sì e part ëd l'arcesta a l'é stàita sautà ($1).",
+ "smw_noconjunctions": "Le congiunsion ant j'arcesta a son pa mantnùe su sta wiki-sì e part ëd l'arcesta a l'é stàita sautà ($1).",
+ "smw_nodisjunctions": "Le disgiunsion ant j'arceste a son pa apogià su sta wiki-sì e part ëd l'arcesta a l'é stàita sautà ($1).",
+ "smw_querytoolarge": "Le condission ëd l'arcesta sì-sota a son pa stàite considerà an rason dla le restrission ëd la wiki ant la dimension o profondità dj'arceste: $1.",
+ "smw_notemplategiven": "Dà un valor për ël paràmetr \"stamp\" an manera che sto formà-sì d'arcesta a travaja.",
+ "smw_db_sparqlqueryproblem": "L'arzultà dl'anterogassion a l'ha pa podù esse otnù da la base ëd dàit SPARQL. S'eror a peul esse temporani o andiché n'eror ant ël programa dla base ëd dàit.",
+ "smw_db_sparqlqueryincomplete": "Rësponde a l'arcesta a l'é arzultà tròp complicà e a l'é stàit abortì. Chèich arzultà a podrìo manché. Se possìbil, ch'a preuva nopà a dovré n'arcesta pi sempia.",
+ "smw_type_header": "Proprietà dël tipo \"$1\".",
+ "smw_typearticlecount": "Mostré $1 {{PLURAL:$1|la proprietà|le proprietà}} ch'a deuvro sto tipo-sì.",
+ "smw_attribute_header": "Pàgine ch'a deuvro la proprietà \"$1\".",
+ "smw_attributearticlecount": "Smon-e $1 {{PLURAL:$1|la pàgine|le pàgine}} ch'a deuvro sta proprietà-sì.",
+ "exportrdf": "Espòrta pàgine an RDF",
+ "smw_exportrdf_docu": "Sta pàgina-sì a-j përmët d'oten-e dat da na pàgina an formà RDF.\nPër esporté dle pàgine, ch'a anserissa ij tìtoj ant la casela ëd test sì-sota, un tìtol për linia.",
+ "smw_exportrdf_recursive": "Esporté ricorsivament tute le pàgine corelà.\nNoté che l'arzultà a podrìa esse motobin gròss!",
+ "smw_exportrdf_backlinks": "Esporté ëdcò tute le pàgine ch'a s'arferisso a le pàgine esportà.\nA génera RDF navigàbij.",
+ "smw_exportrdf_lastdate": "Espòrta pa pàgine che a son pa stàite cangià dal moment specificà.",
+ "smw_exportrdf_submit": "Esporté",
+ "uriresolver": "Arzolvidor d'URI",
+ "properties": "Proprietà",
+ "smw_properties_docu": "Le proprietà sì-sota a son dovrà ant la wiki.",
+ "smw_property_template": "$1 ëd tipo $2 ($3 {{PLURAL:$3|utilisassion}})",
+ "smw_propertylackspage": "Tute le proprietà a dovrìo esse descrivùe da na pàgina!",
+ "smw_propertylackstype": "Gnun tipo a l'é stàit specificà për sta proprietà-sì (as fa cont ch'a sia ëd tipo $1, për adess).",
+ "smw_propertyhardlyused": "Sta proprietà-sì a l'é dovrà apen-a ant la wiki!",
+ "unusedproperties": "Proprietà pa dovrà",
+ "smw-unusedproperties-docu": "Le proprietà sì-sota a esisto, bele che gnun-e àutre pàgine a-j deuvro.",
+ "smw-unusedproperty-template": "$1 ëd tipo $2",
+ "wantedproperties": "Proprietà vorsùe",
+ "smw-wantedproperties-docu": "Le proprietà sì-sota a son dovrà ant la wiki ma a l'han ancó pa na pàgina për descrivje.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|usagi|usagi}})",
+ "smw_purge": "Rinfrësca",
+ "types": "Tipo",
+ "smw_types_docu": "Cola sì-sota a l'é na lista ëd tùit ij tipo ëd dat che a peulo esse assignà a la proprietà.",
+ "smw_uri_doc": "L'arzolvidor d'URI a realisa la [$1 conclusion dël TAG dël W3C a propòsit ëd httpRange-14].\nA fa an manera che j'uman a dvento pa ëd sit ëd la Ragnà.",
+ "ask": "Arserca semàntica",
+ "smw_ask_sortby": "Órdina për colòna (opsional)",
+ "smw_ask_ascorder": "Chërsent",
+ "smw_ask_descorder": "Calant",
+ "smw_ask_submit": "Trové d'arzultà",
+ "smw_ask_editquery": "Modifiché l'arcesta",
+ "smw_add_sortcondition": "[Gionta condission d'órdin]",
+ "smw_ask_hidequery": "Stërmé l'arcesta",
+ "smw_ask_help": "Agiut an sj'arceste",
+ "smw_ask_queryhead": "Anterogassion",
+ "smw_ask_printhead": "Dat adissionaj da smon-e",
+ "smw_ask_printdesc": "(gionta un nòm ëd proprietà për linia);",
+ "smw_ask_format_as": "Ampaginé com:",
+ "smw_ask_defaultformat": "stàndard",
+ "smw_ask_otheroptions": "Àutre opsion",
+ "smw-ask-otheroptions-info": "Costa session a conten dj'opsion che a modìfico j'istrussion dë stampa. Le descrission dij paràmetr a peulo esse vëddùe passandje dzora con ël rat.",
+ "smw-ask-otheroptions-collapsed-info": "Për piasì, ch'a deuvra la plancia «pi» për vëdde tute j'opsion disponìbij",
+ "smw_ask_show_embed": "Smon-e ël còdes antern",
+ "smw_ask_hide_embed": "Stërmé ël còdes antern",
+ "smw_ask_embed_instr": "Për anclude costa anterogassion an linia ant na pàgina wiki, ch'a deuvra ël còdes sì-sota.",
+ "smw-ask-delete": "[Scancelé]",
+ "smw-ask-sorting": "Ordiné",
+ "smw-ask-format-selection-help": "Për na descrission detajà, për piasì ch'a vìsita la pàgina d'agiut $1.",
+ "searchbyproperty": "Serca për proprietà",
+ "smw_sbv_docu": "Serca për tute le pàgine che a l'han proprietà e valor dàit.",
+ "smw_sbv_novalue": "Ch'a anserissa un valor bon për la proprietà, o ch'a varda tùit ij valor ëd le proprietà për \"$1\".",
+ "smw_sbv_displayresult": "Na lista ëd tute le pàgine che a l'han la proprietà \"$1\" con valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Na lista ëd tute le pàgine che a l'han la proprietà \"$1\" con valor \"$2\".\nDagià ch'a-i son ëstàje mach pòchi arzultà, a son smonù ëdcò ij valor davzin.",
+ "smw_sbv_property": "Propietà:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Trové dj'arzultà",
+ "browse": "Varda wiki",
+ "smw_browselink": "Varda propietà",
+ "smw_browse_article": "Ch'a anseriss ël nòm ëd la pàgina da andoa ancaminé a vardé.",
+ "smw_browse_go": "Va",
+ "smw_browse_show_incoming": "smon-e le propietà che a colego ambelessì",
+ "smw_browse_hide_incoming": "stërmé le propietà ch'a colego ambelessì",
+ "smw_browse_no_outgoing": "Sta pàgina-sì a l'ha pa ëd propietà.",
+ "smw_browse_no_incoming": "Gnun-a propietà a colega a sta pàgina-sì.",
+ "smw_inverse_label_default": "$1 ëd",
+ "smw_inverse_label_property": "Etichëtta dla propietà anversa",
+ "pageproperty": "Serca propietà dla pàgina",
+ "smw_pp_docu": "Serca tùit ij valor ëd na propietà su na pàgina dàita.",
+ "smw_pp_from": "Da pàgina",
+ "smw_pp_type": "Propietà",
+ "smw_pp_submit": "Smon-e j'arzultà",
+ "smw_result_prev": "Prima",
+ "smw_result_next": "Apress",
+ "smw_result_results": "Arzultà",
+ "smw_result_noresults": "Pa gnun arzultà.",
+ "smwadmin": "Funsion d'aministrator për Semantic MediaWiki",
+ "smw-admin-setupsuccess": "Ël motor ëd memorisassion a l'é stàit anstalà për da bin.",
+ "smw_smwadmin_return": "Torna a $1",
+ "smw_smwadmin_updatestarted": "Un process neuv për rinfrësché ij dat semàntich a l'é stàit fàit parte.\nTùit ij dat memorisà a saran rifàit o riparà andoa a-i é dabzògn.\nA peul trové l'andament ëd la modìfica an sta pàgina special-sì.\n\nArtorn a $1.",
+ "smw_smwadmin_updatenotstarted": "A-i é già un process ëd modìfica an camin.\nCreene nen n'àutr.\n\nArtorna a $1.",
+ "smw_smwadmin_updatestopped": "Tùit ij process ëd modìfica esistent a son ëstàit fërmà.\n\nArtorna a $1.",
+ "smw_smwadmin_updatenotstopped": "Për fërmé ël process ëd modìfica an cors, a dev ativé la casela për indiché ch'a l'é pròpi sicur.\n\nTorné andré a $1.",
+ "smw-admin-docu": "Sta pàgina special-sì a lo giuta durant l'instalassion e l'agiornament ëd <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>.\nCh'as visa ëd salvé ij dat ëd valor prima ëd fé le funsion aministrative.",
+ "smw-admin-db": "Anstalassion e agiornament dla base ëd dàit",
+ "smw-admin-dbdocu": "Semantic MediaWiki a l'ha dabzògn ëd chèiche estension a la base ëd dàit ëd MediaWiki për memorisé ij dat semàntich.\nLa funsion sì-sota a assicura che soa base ëd dàit a l'é ampostà për da bin.\nIj cangiament fàit an cost pass-sì a toco pa ël rest dla base ëd dàit ëd MediaWiki, e a l'é bel fé torné andré si un a veul.\nSta funsion d'ampostassion-sì a peul esse fàita marcé vàire vire sensa fé 'd dann, ma a-i n'a j'é dabzògn mach na vira për l'anstalassion o l'agiornament.",
+ "smw-admin-permissionswarn": "Se l'operassion a faliss con dj'eror SQL, l'utent dla base ëd dàit dovrà da soa wiki (ch'a contròla sò LocalSettings.php) probabilment a l'ha pa basta 'd përmess.\nO bin ch'a-j daga a st'utent dij përmess adissionaj për creé e scancelé 'd tàule, temporaneament ch'a anserissa l'identificativ dël cont rèis ëd soa base ëd dàit an LocalSettings.php, opura ch'a deuvra ël copion ëd manteniment <code>setupStore.php</code> ch'a peul dovré le credensiaj ëd n'aministrator.",
+ "smw-admin-dbbutton": "Inissialisé o agiorné le tàule",
+ "smw-admin-announce": "Anonsia toa wiki",
+ "smw_smwadmin_datarefresh": "Riparassion ëd dat e agiornament",
+ "smw_smwadmin_datarefreshdocu": "A l'é possìbil ripristiné tùit ij dat Semantic MediaWiki basà dzora al contnù corent ëd la wiki.\nSòn a peul ven-e a taj për riparé dat rot o për rinfrësché dat se ël formà antern a l'é cangià për chèich agiornament dël programa.\nLa modìfica a l'é fàita pàgina për pàgina e a sarà pa completa sùbit.\nLòn ch'a-i é sì-sota a mostra se na modìfica a l'é an cors e a-j përmët ëd fé parte o fërmé le modìfiche (gavà che sta possibilità a sia stàita disabilità da l'aministrator dël sit).",
+ "smw_smwadmin_datarefreshprogress": "<strong>na modìfica a l'é già an cors.</strong>\nA l'é normal che la modìfica a von-a anans mach pian përchè a rinfrësca dat an cite partìe minca vira che n'utent a intra ant la wiki.\nPër fé finì sta modìfica pi an pressa, a peul ciamé ël copion ëd manteniment MediaWiki <code>runJobs.php</code> (ch'a deuvra l'opsion <code>--maxjobs 1000</code> për strenze ël nùmer ëd modìfiche fàite ant un lòt).\nAvansament stimà dla modìfica corenta:",
+ "smw_smwadmin_datarefreshbutton": "Ancamin-a a agiorné ij dat",
+ "smw_smwadmin_datarefreshstop": "Ferma sto agiornament-sì",
+ "smw_smwadmin_datarefreshstopconfirm": "Bò, i son sicur.",
+ "smw-admin-support": "Oteniment ëd n'agiut",
+ "smw-admin-supportdocu": "Vàire arsorse a peulo giutelo an cas ëd problema:",
+ "smw-admin-installfile": "S'a treuva ëd problema con soa anstalassion, ch'a ancamin-a a controlé le linie guida ant l'<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">archivi INSTALL</a>.",
+ "smw-admin-smwhomepage": "La documentassion utent completa ëd Semantich WikiMedia a l'é a <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Ij bigat a peulo esse arportà a <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "S'a l'ha d'àutre chestion o sugeriment, ch'a vada a la discussion dzora a la <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">piassa dj'utent ëd Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Strutura dij dat",
+ "smw_adminlinks_displayingdata": "Visualisé ij dat",
+ "smw_adminlinks_inlinequerieshelp": "Agiut an sj'anterogassion an linia",
+ "smw-createproperty-isproperty": "Costa-sì a l'é na proprietà ëd sòrt $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Ël valor përmëttù për sta proprietà-sì a l'é|Ij valor përmëttù për sta proprietà-sì a son}}:",
+ "smw-paramdesc-category-delim": "Ël delimitador",
+ "smw-paramdesc-category-template": "Në stamp për formaté j'element con",
+ "smw-paramdesc-category-userparam": "Un paràmetr për passé a lë stamp",
+ "smw-info-par-message": "Mëssagi da visualisé.",
+ "smw-info-par-icon": "Plancia da mostré, o «info» opura «warning».",
+ "prefs-smw": "MediaWiki Semàntica",
+ "prefs-ask-options": "Opsion d'arserca Semàntica",
+ "smw-prefs-intro-text": "J'opsion sì-sota a son smonùe da [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (o soe estension) për abilité na përsonalisassion andividual ëd funsion selessionà. Për savèjne ëd pi, për piasì ch'a consulta sta [https://www.semantic-mediawiki.org/wiki/Help:User_preferences session d'agiut].",
+ "smw-prefs-ask-options-tooltip-display": "Smon-e ël test dël paràmetr com na nivolëtta d'anformassion",
+ "smw-prefs-ask-options-collapsed-default": "Abìlita la casela d'opsion a esse stenzùa per predefinission",
+ "smw-ui-tooltip-title-property": "Propietà",
+ "smw-ui-tooltip-title-quantity": "Quantità",
+ "smw-ui-tooltip-title-info": "Anformassion",
+ "smw-ui-tooltip-title-service": "Colegament ëd servissi",
+ "smw-ui-tooltip-title-warning": "Eror",
+ "smw-ui-tooltip-title-parameter": "Paràmetr",
+ "smw-ui-tooltip-title-event": "Eveniment",
+ "smw-ui-tooltip-title-note": "Nòta",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw_unknowntype": "La sòrt ëd costa propietà a l'é pa bon-a.",
+ "smw_concept_header": "Pàgine dël concet \"$1\"",
+ "smw_conceptarticlecount": "Smon-e $1 {{PLURAL:$1|pàgina|pàgine}} che a aparten-o a col concet.",
+ "smw-livepreview-loading": "Antramentr ch'as caria…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pnb.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pnb.json
new file mode 100644
index 00000000..bce15514
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pnb.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Khalid Mahmood"
+ ]
+ },
+ "browse": "وکی پھرولو",
+ "smw-livepreview-loading": "لوڈنگ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/prg.json b/www/wiki/extensions/SemanticMediaWiki/i18n/prg.json
new file mode 100644
index 00000000..3ea3c083
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/prg.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "KrausnÄ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ps.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ps.json
new file mode 100644
index 00000000..3046c5db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ps.json
@@ -0,0 +1,42 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ahmed-Najib-Biabani-Ibrahimkhel"
+ ]
+ },
+ "smw_finallistconjunct": "، او",
+ "smw_printername_list": "لړليک",
+ "smw_printername_template": "کينډÛ",
+ "smw_printername_category": "ÙˆÛشنيزه",
+ "validator-type-class-SMWParamSource": "متن",
+ "smw_iq_moreresults": "Ù†ÙˆØ±Û Ù¾Ø§ÙŠÙ„Û ...",
+ "smw_true_words": "سم، س، هو، هـ",
+ "smw_false_words": "ناسم، نا، نه، ن",
+ "smw_nodatetime": "دا \"$1\" Ù†Ûټه Ùˆ نه Ù¾Ûژندل شوه.",
+ "smw_exportrdf_submit": "صادرول",
+ "properties": "ÚانتياوÛ",
+ "unusedproperties": "ناکارÛØ¯Ù„Û ÚانتياوÛ",
+ "wantedproperties": "غوښتل Ø´ÙˆÛ ÚانتياوÛ",
+ "smw_ask_ascorder": "ختند",
+ "smw_ask_defaultformat": "تلواليز",
+ "smw-ask-delete": "[ړنگول]",
+ "smw_sbv_property": "Úانتيا:",
+ "smw_sbv_value": "ارزښت:",
+ "smw_sbv_submit": "Ù¾Ø§ÙŠÙ„Û Ù…ÙˆÙ†Ø¯Ù„",
+ "browse": "ويکي سپړل",
+ "smw_browselink": "د سپړلو ÚانتياوÛ",
+ "smw_browse_go": "ورÚÙ‡",
+ "smw_pp_type": "Úانتيا",
+ "smw_pp_submit": "Ù¾Ø§ÙŠÙ„Û Ù…ÙˆÙ†Ø¯Ù„",
+ "smw_result_prev": "پخواني",
+ "smw_result_next": "راتلونکي",
+ "smw_result_results": "پايلÛ",
+ "smw_result_noresults": "Ø¨Û Ù¾Ø§ÙŠÙ„Ùˆ.",
+ "smw_smwadmin_return": "$1 ته ورستنÛدل",
+ "smw_smwadmin_datarefreshstopconfirm": "هو، زه ډاډه يم.",
+ "smw-ui-tooltip-title-info": "مالومات",
+ "smw-ui-tooltip-title-warning": "تÛروتنه",
+ "smw-livepreview-loading": "رابرسÛرÛÚ–ÙŠ...",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|ثانيه|ثانيÛ}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|ثانيه|ثانيÛ}}"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pt-br.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pt-br.json
new file mode 100644
index 00000000..94258a64
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pt-br.json
@@ -0,0 +1,863 @@
+{
+ "@metadata": {
+ "authors": [
+ "Amgauna",
+ "Eduardo.mps",
+ "GKnedo",
+ "Giro720",
+ "Hamilton Abreu",
+ "Helder.wiki",
+ "Heldergeovane",
+ "Jaideraf",
+ "Luckas",
+ "Luckas Blade",
+ "Rafael Vargas",
+ "Waldir",
+ "555",
+ "Cainamarques",
+ "Agripinoduarte",
+ "Smmvinicius",
+ "He7d3r",
+ "Rhcastilhos",
+ "Opraco",
+ "Macofe",
+ "Eduardo Addad de Oliveira",
+ "Nemo bis",
+ "Felipe L. Ewald",
+ "Trigonometria87",
+ "Kghbln",
+ "Walesson",
+ "Fitoschido",
+ "Athena in Wonderland"
+ ]
+ },
+ "smw-desc": "Tornando seu wiki mais acessível - para máquinas ''e'' humanos ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentação online])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error": "O Semantic MediaWiki foi instalado e ativado mas está faltando um [https://www.semantic-mediawiki.org/wiki/Help:Upgrade código de atualização] apropriado que corresponda: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Por que eu vejo este erro?",
+ "smw-upgrade-error-why-explain": "A estrutura interna do banco de dados do Semantic MediaWiki foi alterada e necessita alguns ajustes para estar totalmente funcional. Pode haver vários motivos incluindo: \n* Propriedades fixas adicionais (requer configuração de tabelas) foram adicionadas\n* Uma atualização contém algumas mudanças nas tabelas ou índices tornando uma interceptação obrigatória antes de acessar os dados",
+ "smw-upgrade-error-how-title": "Como corrigir este erro?",
+ "smw-upgrade-error-how-explain": "Um administrador (ou qualquer pessoa com direitos administrativos) precisa executar o script de manutenção [https://www.mediawiki.org/wiki/Manual:Update.php update.php] do MediaWiki ou o [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] do Semantic MediaWiki. Você também pode consultar as seguintes páginas para mais informações:\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation Instruções de instalação] \n* [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting Solução de problemas]",
+ "smw-semantics-not-enabled": "A funcionalidade do Semantic MediaWiki não foi ativada neste wiki.",
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": " e",
+ "smw-factbox-head": "... mais sobre \"$1\"",
+ "smw-factbox-facts": "Fatos",
+ "smw-factbox-facts-help": "Mostra declarações e fatos que foram criados por um usuário",
+ "smw-factbox-facts-derived": "Fatos derivados",
+ "smw-factbox-facts-derived-help": "Mostra fatos que foram derivados de regras ou com a ajuda de outras técnicas de raciocínio",
+ "smw_isspecprop": "Esta propriedade é uma propriedade especial neste wiki.",
+ "smw-concept-cache-header": "Uso do cache",
+ "smw-concept-cache-count": "O [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count cache do conceito] contém {{PLURAL:$1|'''uma''' entidade|'''$1''' entidades}} ($2).",
+ "smw-concept-no-cache": "Sem cache disponível.",
+ "smw_concept_description": "Descrição do conceito \"$1\"",
+ "smw_no_concept_namespace": "Conceitos só podem ser definidos em páginas no domínio Conceito:.",
+ "smw_multiple_concepts": "Cada página de conceito pode ter apenas uma definição de conceito.",
+ "smw_concept_cache_miss": "O conceito \"$1\" não pode ser utilizado neste momento, uma vez que a configuração deste wiki necessita ser refeita off-line.\nCaso o problema não seja resolvido automaticamente dentro de algum tempo, peça a um administrador deste wiki que este conceito seja disponibilizado.",
+ "smw_noinvannot": "Valores não podem ser atribuídos a propriedades inversas.",
+ "version-semantic": "Extensões semânticas",
+ "smw_baduri": "URIs na forma de \"$1\" não são permitidos.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Contar resultados",
+ "smw_printername_csv": "Exportação em CSV",
+ "smw_printername_dsv": "Exportação em DSV",
+ "smw_printername_debug": "Depurar consulta (para especialistas)",
+ "smw_printername_embedded": "Incorporar o conteúdo da página",
+ "smw_printername_json": "Exportação em JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Lista simples",
+ "smw_printername_ol": "Enumeração",
+ "smw_printername_ul": "Discriminação",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Tabela ampla",
+ "smw_printername_template": "Predefinição",
+ "smw_printername_templatefile": "Arquivo da predefinição",
+ "smw_printername_rdf": "Exportação em RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "Número máximo de resultados na exibição",
+ "smw-paramdesc-offset": "Posição do primeiro resultado",
+ "smw-paramdesc-headers": "Mostrar os nomes dos cabeçalhos/propriedades",
+ "smw-paramdesc-mainlabel": "Nome dado a coluna das páginas principais",
+ "smw-paramdesc-link": "Apresentar os valores na forma de links",
+ "smw-paramdesc-intro": "Texto a apresentar antes dos resultados da consulta, caso existam",
+ "smw-paramdesc-outro": "Texto a apresentar após os resultados da consulta, caso existam",
+ "smw-paramdesc-default": "Texto a apresentar caso não haja resultados para a consulta",
+ "smw-paramdesc-sep": "O separador de resultados",
+ "smw-paramdesc-propsep": "O separador entre as propriedades da entrada de um resultado",
+ "smw-paramdesc-valuesep": "O separador entre os valores para uma propriedade de um resultado",
+ "smw-paramdesc-showsep": "Exibir separador no começo do arquivo CSV (\"sep=<valor>\")",
+ "smw-paramdesc-distribution": "Ao invés de exibir todos os valores, conte suas ocorrências e mostre-as.",
+ "smw-paramdesc-distributionsort": "Alfabete a distribuição do valor pela contagem de ocorrência.",
+ "smw-paramdesc-distributionlimit": "Limite a distribuição do valor para a contagem de apenas alguns valores.",
+ "smw-paramdesc-aggregation": "Especifique a que a agregação deve se relacionar",
+ "smw-paramdesc-template": "Nome da predefinição com a qual são exibidos os resultados",
+ "smw-paramdesc-columns": "O número de colunas nas quais exibir resultados",
+ "smw-paramdesc-userparam": "Valor fornecido a cada chamada da predefinição, se uma predefinição for utilizada",
+ "smw-paramdesc-class": "Uma classe CSS adicional para ser definida para a lista",
+ "smw-paramdesc-introtemplate": "Nome de uma predefinição para apresentar antes dos resultados da consulta, se existirem",
+ "smw-paramdesc-outrotemplate": "Nome de uma predefinição para apresentar após os resultados da consulta, se existirem",
+ "smw-paramdesc-embedformat": "Elemento HTML utilizado para definir cabeçalhos",
+ "smw-paramdesc-embedonly": "Não exibir cabeçalhos",
+ "smw-paramdesc-table-class": "Uma classe CSS adicional para colocar em uma tabela",
+ "smw-paramdesc-table-transpose": "Exibe verticalmente os cabeçalhos das tabelas e horizontalmente os resultados.",
+ "smw-paramdesc-rdfsyntax": "A sintaxe RDF a ser utilizada",
+ "smw-paramdesc-csv-sep": "Especifica um separador de colunas",
+ "smw-paramdesc-csv-valuesep": "Especifica um separador de valores",
+ "smw-paramdesc-csv-merge": "Fundir os valores de linhas e colunas que têm um identificador de assunto idêntico (aliás, primeira coluna)",
+ "smw-paramdesc-csv-bom": "Adicionar um BOM (carácter para indicar a \"endianness\") no topo do arquivo de saída",
+ "smw-paramdesc-dsv-separator": "Separado a ser utilizado",
+ "smw-paramdesc-dsv-filename": "Nome para o arquivo DSV",
+ "smw-paramdesc-filename": "O nome para o arquivo de saída",
+ "smw-smwdoc-description": "Mostra uma tabela com todos os parâmetros que podem ser utilizados para o formato de resultado específico junto aos valores padrão e as descrições.",
+ "smw-smwdoc-default-no-parameter-list": "Este formato de resultado não fornece parâmetros específicos do formato.",
+ "smw-smwdoc-par-format": "O formato de resultados para o qual será apresentada a documentação dos parâmetros.",
+ "smw-smwdoc-par-parameters": "Quais parâmetros mostrar. \"specific\" para aqueles adicionados pelo formato, \"base\" para aqueles disponíveis em todos os formatos e \"all\" para ambos.",
+ "smw-paramdesc-sort": "Propriedade para classificar a consulta",
+ "smw-paramdesc-order": "Ordem da classificação da consulta",
+ "smw-paramdesc-searchlabel": "Texto para continuar a busca",
+ "smw-paramdesc-named_args": "Nomeie os argumentos passados para a predefinição",
+ "smw-paramdesc-template-arguments": "Configura como os argumentos nomeados são passados à predefinição",
+ "smw-paramdesc-import-annotation": "Dados marcados adicionais serão copiados durante o parsing do sujeito.",
+ "smw-paramdesc-export": "Opção de exportação",
+ "smw-paramdesc-prettyprint": "Uma saída de pretty-print que exibe indentações e novas linhas adicionais",
+ "smw-paramdesc-json-unescape": "O resultado conterá barras não escapadas e caracteres Unicode multibyte",
+ "smw-paramdesc-json-type": "Tipo de serialização",
+ "smw-paramdesc-source": "Fonte alternativa de consulta",
+ "smw-paramdesc-jsonsyntax": "Sintaxe JSON para ser utilizada",
+ "smw-printername-feed": "Feeds RSS e Atom",
+ "smw-paramdesc-feedtype": "Tipo de feed",
+ "smw-paramdesc-feedtitle": "Texto a ser utilizado como título do feed",
+ "smw-paramdesc-feeddescription": "Texto a ser utilizado como descrição do feed",
+ "smw-paramdesc-feedpagecontent": "Conteúdo da página a ser exibido com o feed",
+ "smw-label-feed-description": "$1 $2 feed",
+ "smw-paramdesc-mimetype": "O tipo de mídia (MIME type) para o arquivo de saída.",
+ "smw_iq_disabled": "As consultas semânticas foram desativadas neste wiki",
+ "smw_iq_moreresults": "… mais resultados",
+ "smw_parseerror": "O valor fornecido não foi compreendido.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "\"$1\" não pode ser utilizado como nome de página neste wiki.",
+ "smw_noproperty": "\"$1\" não pode ser utilizado como um nome de propriedade neste wiki.",
+ "smw_wrong_namespace": "Apenas páginas no domínio \"$1\" são permitidas aqui.",
+ "smw_manytypes": "Mais de um tipo definido para a propriedade.",
+ "smw_emptystring": "Cadeias de caracteres vazias não são aceitas.",
+ "smw_notinenum": "\"$1\" não está na lista ($2) dos [[Property:Allows value|valores permitidos]] para a propriedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" não está na lista ($2) dos [[Property:Allows value|valores permitidos]] para a propriedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" não está no intervalo de \"$2\" especificado pela restrição [[Property:Allows value|permite valor]] para a propriedade \"$3\".",
+ "smw_noboolean": "\"$1\" não é reconhecido como um valor Booleano (verdadeiro/falso).",
+ "smw_true_words": "verdadeiro,v,sim,s,true,t,yes,y",
+ "smw_false_words": "falso,f,não,nao,n,false,no",
+ "smw_nofloat": "\"$1\" não é um número.",
+ "smw_infinite": "Números tão grandes como \"$1\" não são suportados.",
+ "smw_unitnotallowed": "\"$1\" não está declarado como unidade de medida válida para esta propriedade.",
+ "smw_nounitsdeclared": "Não foi declarada nenhuma unidade de medida para esta propriedade.",
+ "smw_novalues": "Não foram especificados valores.",
+ "smw_nodatetime": "A data “$1†não foi compreendida.",
+ "smw_toomanyclosing": "Parece haver muitas ocorrências de \"$1\" na consulta.",
+ "smw_noclosingbrackets": "Um uso de \"<nowiki>[[</nowiki>\" na sua consulta não foi fechada por um \"]]\" correspondente.",
+ "smw_misplacedsymbol": "O símbolo \"$1\" foi usado num local onde não é útil.",
+ "smw_unexpectedpart": "A parte \"$1\" da consulta não foi compreendida.\nOs resultados podem não ser os esperados.",
+ "smw_emptysubquery": "Alguma subconsulta não tem uma condição válida.",
+ "smw_misplacedsubquery": "Uma subconsulta foi utilizada em um local onde não são permitidas subconsultas.",
+ "smw_valuesubquery": "Subconsultas não suportadas para valores da propriedade \"$1\".",
+ "smw_badqueryatom": "Alguma parte \"<nowiki>[[…]]</nowiki>\" da consulta não foi compreendida.",
+ "smw_propvalueproblem": "O valor da propriedade \"$1\" não foi compreendido.",
+ "smw_noqueryfeature": "Uma característica da consulta não foi suportada neste wiki e parte da consulta foi descartada ($1).",
+ "smw_noconjunctions": "Conjunções em consultas não são suportadas neste wiki e parte da consulta foi descartada ($1).",
+ "smw_nodisjunctions": "Disjunções em consultas não são suportadas neste wiki e parte da consulta foi descartada ($1).",
+ "smw_querytoolarge": "Não foi possível considerar {{PLURAL:$2|a seguinte condição|as seguintes $2 condições}} da consulta, devido às restrições do wiki para o tamanho e profundidade das consultas: <code>$1</code>.",
+ "smw_notemplategiven": "Providencie um valor para o parâmetro \"predefinição\" para o formato desta consulta funcionar.",
+ "smw_db_sparqlqueryproblem": "O resultado da consulta não pôde ser obtido do banco de dados SPARQL. Este erro pode ser temporário ou indicar um defeito no software de banco de dados.",
+ "smw_db_sparqlqueryincomplete": "A consulta revelou-se difícil e foi interrompida. Alguns resultados podem estar faltando. Se possível, tente utilizar uma consulta mais simples.",
+ "smw_type_header": "Propriedades do tipo \"$1\"",
+ "smw_typearticlecount": "Exibindo $1 {{PLURAL:$1|propriedade que utiliza|propriedades que utilizam}} este tipo.",
+ "smw_attribute_header": "Páginas que utilizam a propriedade \"$1\"",
+ "smw_attributearticlecount": "Exibindo $1 {{PLURAL:$1|página que utiliza|páginas que utilizam}} esta propriedade.",
+ "smw-propertylist-subproperty-header": "Subpropriedades",
+ "smw-propertylist-redirect-header": "Sinônimos",
+ "smw-propertylist-error-header": "Páginas com atribuições indevidas",
+ "smw-propertylist-count": "Exibindo $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}}.",
+ "smw-propertylist-count-with-restricted-note": "Exibindo $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}} (um número maior existe, mas a exibição está restrita à \"$2\").",
+ "smw-propertylist-count-more-available": "Exibindo $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}} (há mais disponíveis).",
+ "specialpages-group-smw_group": "Semantic MediaWiki",
+ "exportrdf": "Exportar páginas para RDF",
+ "smw_exportrdf_docu": "Esta página permite que você obtenha dados de uma página no formato RDF.\nPara exportar páginas, introduza os seus títulos na caixa de texto abaixo, um título por linha.",
+ "smw_exportrdf_recursive": "Exportar recursivamente todas as páginas relacionadas.\nNote que o resultado pode ser volumoso!",
+ "smw_exportrdf_backlinks": "Também exporta todas as páginas que se referem as páginas exportadas.\nGera RDF navegável.",
+ "smw_exportrdf_lastdate": "Não exporte páginas que não foram alteradas desde o tempo dado.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Resolvedor de URIs",
+ "properties": "Propriedades",
+ "smw_properties_docu": "As seguintes propriedades são utilizadas neste wiki.",
+ "smw_property_template": "$1 do tipo $2 ($3 {{PLURAL:$3|uso|usos}})",
+ "smw_propertylackspage": "Todas as propriedades devem ser descritas por uma página!",
+ "smw_propertylackstype": "Nenhum tipo foi especificado para esta propriedade (assumindo tipo $1 por enquanto).",
+ "smw_propertyhardlyused": "Esta propriedade é muito pouco utilizada neste wiki!",
+ "smw-property-name-invalid": "A propriedade $1 não pode ser utilizada (nome de propriedade inválido).",
+ "smw-property-name-reserved": "\"$1\" estava listado como sendo um nome reservado e não deve ser usado como propriedade. Talvez a seguinte [https://www.semantic-mediawiki.org/wiki/Help:Property_naming página de ajuda] contenha informação sobre a razão pela qual o nome estava reservado.",
+ "smw-sp-property-searchform": "Exibir propriedades que contenham:",
+ "smw-sp-property-searchform-inputinfo": "O texto de entrada é sensível a maiúsculas e, quando utilizado para a filtragem, somente as propriedades que correspondem a condição serão exibidas.",
+ "smw-special-property-searchform": "Exibir propriedades que contenham:",
+ "smw-special-property-searchform-inputinfo": "O texto de entrada é sensível a maiúsculas e minúsculas, quando utilizado para a filtragem, somente as propriedades que correspondem a condição serão exibidas.",
+ "smw-special-property-searchform-options": "Opções",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Nenhuma",
+ "smw-special-wantedproperties-filter-unapproved": "Não aprovada",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Opção de filtro utilizada em relação ao modo de autoridade.",
+ "concepts": "Conceitos",
+ "smw-special-concept-docu": "Um [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceito] pode ser visto como uma \"categoria dinâmica\", isto é, uma coleção de páginas que não é criada manualmente, mas automaticamente computada pelo Semantic MediaWiki a partir de uma descrição de uma dada consulta.",
+ "smw-special-concept-header": "Lista de conceitos",
+ "smw-special-concept-count": "{{PLURAL:$1|O seguinte conceito|$1 conceitos}} {{PLURAL:$1|está sendo listado.|estão sendo listados}}.",
+ "smw-special-concept-empty": "Nenhum conceito foi encontrado.",
+ "unusedproperties": "Propriedades não utilizadas",
+ "smw-unusedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Unused_properties propriedades não utilizadas] que foram declaradas, embora nenhuma outra página as utilize. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:WantedProperties|propriedades em falta]].",
+ "smw-unusedproperty-template": "$1 de tipo $2",
+ "wantedproperties": "Propriedades pedidas",
+ "smw-wantedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propriedades em falta] que são utilizadas no wiki, mas que não possuem uma página que as descreva. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:UnusedProperties|propriedades não utilizadas]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw-special-wantedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propriedades em falta] que são utilizadas no wiki, mas que não possuem uma página que as descreva. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:UnusedProperties|propriedades não utilizadas]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw_purge": "Atualizar",
+ "smw-purge-failed": "A atualização falhou",
+ "types": "Tipos",
+ "smw_types_docu": "Lista de [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipos de dados disponíveis] com cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipo] representando um conjunto único de atributos para descrever um valor em termos de características de armazenamento e exibição que são hereditários a uma propriedade atribuída.",
+ "smw-special-types-no-such-type": "\"$1\" é desconhecido ou não foi especificado como tipo de dados válido.",
+ "smw-statistics": "Estatísticas de semântica",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor de propriedade|Valores de propriedade}} (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propriedade|Propriedades}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propriedade|Propriedades}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propriedade|Propriedades}}]] (utilizada(s) com pelo menos um valor)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propriedade|Propriedades}} (registrada com a página)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propriedade|Propriedades}} (atribuída ao tipo de dado)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultas}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Consulta|Consultas}}]]",
+ "smw-statistics-query-size": "Tamanho da consulta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Conceito|Conceitos}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Conceito|Conceitos}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobjeto|Subobjetos}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobjeto|Subobjetos}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipo de dados|Tipos de dados}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valor de propriedade|Valores de propriedade}} ([[Special:ProcessingErrorList|{{PLURAL:$1|marcação imprópria|marcações impróprias}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor de propriedade|Valores de propriedades}} ({{PLURAL:$1|marcação incorreta|marcações incorretas}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entidade desatualizada|Entidades desatualizadas}} ({{PLURAL:$1|marcada|marcadas}} para remoção)",
+ "smw_uri_doc": "O resolvedor de URIs implementa a [$1 descoberta TAG do W3C sobre o httpRange-14].\nEle certifica-se de que os humanos não se tornem sites da Web.",
+ "ask": "Busca semântica",
+ "smw-ask-help": "Esta secção contém algumas hiperligações que explicam como usar a sintaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecionar páginas] descreve como selecionar páginas e construir condições\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de pesquisa] lista os operadores de pesquisa disponíveis, incluindo os para consultas de intervalos e consultas com caracteres de substituição\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Apresentar informação] descreve o uso de instruções de impressão e de opções de formatação",
+ "smw_ask_sortby": "Ordenar por coluna (opcional)",
+ "smw_ask_ascorder": "Ascendente",
+ "smw_ask_descorder": "Descendente",
+ "smw-ask-order-rand": "Aleatório",
+ "smw_ask_submit": "Buscar resultados",
+ "smw_ask_editquery": "Editar consulta",
+ "smw_add_sortcondition": "[Adicionar condição de ordenação]",
+ "smw-ask-sort-add-action": "Adicionar condição de ordenação",
+ "smw_ask_hidequery": "Esconder consulta (visualização compacta)",
+ "smw_ask_help": "Ajuda sobre consultas",
+ "smw_ask_queryhead": "Condição",
+ "smw_ask_printhead": "Seleção de dados adicionais",
+ "smw_ask_printdesc": "(adicionar um nome de propriedade por linha)",
+ "smw_ask_format_as": "Formatar como:",
+ "smw_ask_defaultformat": "padrão",
+ "smw_ask_otheroptions": "Outras opções",
+ "smw-ask-otheroptions-info": "Esta seção contém opções que alteram as declarações de exibição. As descrições dos parâmetros podem ser visualizadas posicionando o cursor sobre elas.",
+ "smw-ask-otheroptions-collapsed-info": "Por favor, utilize o ícone de adição para visualizar todas as opções disponíveis",
+ "smw_ask_show_embed": "Mostrar código embutido",
+ "smw_ask_hide_embed": "Ocultar código embutido",
+ "smw_ask_embed_instr": "Para embutir esta consulta em uma página wiki, utilize o código abaixo.",
+ "smw-ask-delete": "Remover",
+ "smw-ask-sorting": "Alfabetação",
+ "smw-ask-options": "Opções",
+ "smw-ask-options-sort": "Opções de ordenação",
+ "smw-ask-format-options": "Formato e opções",
+ "smw-ask-parameters": "Parâmetros",
+ "smw-ask-search": "Busca",
+ "smw-ask-debug": "Depurar",
+ "smw-ask-debug-desc": "Gera informação de depuração da consulta",
+ "smw-ask-no-cache": "Desativar cache de consultas",
+ "smw-ask-no-cache-desc": "Resultados sem cache de consulta",
+ "smw-ask-result": "Resultado",
+ "smw-ask-empty": "Limpar todas as entradas",
+ "smw-ask-download-link-desc": "Baixar os resultados da consulta no formato $1",
+ "smw-ask-format": "Formato",
+ "smw-ask-format-selection-help": "Ajuda com o formato selecionado: $1",
+ "smw-ask-condition-change-info": "A condição foi alterada e o mecanismo de busca requer que a consulta volte a ser executada para produzir resultados que correspondam aos novos requisitos.",
+ "smw-ask-input-assistance": "Assistência de preenchimento",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Uma ajuda é fornecida para a introdução dos dados] nos campos de exibição, ordenação e condição. O campo de condição requer o uso de um dos seguintes prefixos:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> para obter as sugestões de propriedades (p. ex.: <code>[[p:Tem ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> para obter as sugestões de categorias",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> para obter as sugestões de conceitos",
+ "smw-ask-format-change-info": "O formato foi modificado e a consulta precisa ser executada novamente para produzir resultados que correspondam aos novos parâmetros e às opções de visualização.",
+ "smw-ask-format-export-info": "O formato selecionado é um formato de exportação que não tem representação visual, portanto, os resultados só são fornecidos como download.",
+ "smw-ask-query-search-info": "A consulta <code><nowiki>$1</nowiki></code> foi respondida pelo {{PLURAL:$3|1=<code>$2</code> (pelo cache)|<code>$2</code> (pelo cache)|<code>$2</code>}} em $4 {{PLURAL:$4|segundo|segundos}}.",
+ "searchbyproperty": "Busca por propriedade",
+ "processingerrorlist": "Lista de erros de processamento",
+ "propertylabelsimilarity": "Relatório de similaridade de nome de propriedade",
+ "smw-processingerrorlist-intro": "A seguinte lista fornece uma perspetiva geral dos erros de processamento relacionadas ao [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Recomenda-se monitorar esta lista regularmente e corrigir as anotações inválidas de valores.",
+ "smw_sbv_docu": "Busca por todas as páginas que possuem uma dada propriedade e um dado valor.",
+ "smw_sbv_novalue": "Introduza um valor válido para a propriedade, ou veja todos os valores da propriedade \"$1\".",
+ "smw_sbv_displayresult": "Segue-se uma lista de todas as páginas que possuem a propriedade \"$1\" com valor \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Uma lista de todas as páginas que têm a propriedade \"$1\" com valor \"$2\".\nUma vez que houve poucos resultados, também são apresentados valores aproximados.",
+ "smw_sbv_property": "Propriedade:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Procurar resultados",
+ "browse": "Navegar pelo wiki",
+ "smw_browselink": "Navegar pelas propriedades",
+ "smw_browse_article": "Introduza o nome da página a partir da qual deseja começar a navegar.",
+ "smw_browse_go": "Ir",
+ "smw_browse_show_incoming": "Mostrar propriedades recebidas",
+ "smw_browse_hide_incoming": "Ocultar propriedades recebidas",
+ "smw_browse_no_outgoing": "Esta página não possui propriedades.",
+ "smw_browse_no_incoming": "Nenhuma propriedade aponta para esta página.",
+ "smw-browse-from-backend": "As informações estão sendo carregadas.",
+ "smw-browse-intro": "Esta página fornece detalhes sobre um sujeito ou entidade, por favor, digite o nome de um objeto a ser inspecionado.",
+ "smw-browse-invalid-subject": "A validação do sujeito terminou com um erro \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "O sujeito tem um formato de serialização inválido.",
+ "smw-browse-js-disabled": "É provável que o JavaScript esteja desativado ou indisponível, e recomendamos que utilize um ''browser'' que o suporte. Talvez encontre outras opções na página de configuração do parâmetro [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Mostrar grupos",
+ "smw-browse-hide-group": "Ocultar grupos",
+ "smw-noscript": "Esta página ou ação requer Javascript para funcionar, por favor, habilite o Javascript em seu navegador ou utilize um navegador que o suporte para que essa funcionalidade possa ser servida como requisitada. Para mais informações, consulte a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Noscript Noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Nome de propriedade inversa",
+ "pageproperty": "Busca por propriedade de página",
+ "smw_pp_docu": "Introduza uma página e propriedade, ou só uma propriedade, para obter todos os valores atribuídos.",
+ "smw_pp_from": "Da página:",
+ "smw_pp_type": "Propriedade:",
+ "smw_pp_submit": "Buscar resultados",
+ "smw_result_prev": "Anterior",
+ "smw_result_next": "Próximo",
+ "smw_result_results": "Resultados",
+ "smw_result_noresults": "Sem resultados.",
+ "smwadmin": "Funções de administração e manutenção",
+ "smw-admin-statistics-job-title": "Estatísticas da fila de execução",
+ "smw-admin-statistics-job-docu": "As estatísticas de tarefas apresentam informação sobre tarefas agendadas do Semantic MediaWiki que ainda não foram executadas. O número de tarefas pode ter ligeiras imprecisões ou conter tentativas falhadas; consulte o [https://www.mediawiki.org/wiki/Manual:Job_queue manual] para mais informações.",
+ "smw-admin-statistics-querycache-title": "Estatísticas do cache de consultas",
+ "smw-admin-statistics-querycache-disabled": "O [https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] não está habilitado neste wiki e, portanto, não há estatísticas disponíveis.",
+ "smw-admin-statistics-querycache-explain": "As estatísticas da ''cache'' irão conter dados acumulados provisórios e derivados, incluindo:\n* \"misses\" (falhas) o número total de tentativas de obter dados da ''cache'' com respostas inatingíveis, forçando a obtenção direta do repositório (base de dados, repositório de triplas, etc.)\n* \"deletes\" (eliminações) o número total de operações de despejo da ''cache'' (por dependências de purga ou de consulta)\n* \"hits\" (acertos) contém o número de obtenções de dados da ''cache'', tendo por fonte quer consultas incorporadas (consultas feitas a partir de uma página da wiki) quer não incorporadas (pedidas por páginas como Special:Ask ou pela API, se permitido)\n* \"medianRetrievalResponseTime\" (mediana do tempo de resposta) um valor indicativo da mediana do tempo de resposta (em segundos) para pedidos de obtenção servidos, ou não, pela ''cache'' durante o período de execução do processo de recolha de dados\n* \"noCache\" (sem ''cache'') indica a quantidade de pedidos sem tentativa de obter dados da ''cache'' (consultas com limite=0, opção 'sem ''cache''', etc.)",
+ "smw-admin-permission-missing": "O acesso a esta página está bloqueado devido à falta de permissões. Por favor, consulte a página de ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissões] para detalhes sobre as configurações necessárias.",
+ "smw-admin-setupsuccess": "O mecanismo de armazenamento foi configurado.",
+ "smw_smwadmin_return": "Voltar para $1",
+ "smw_smwadmin_updatestarted": "Um novo processo de atualização para atualizar os dados semânticos foi iniciado.\nTodos os dados armazenados serão reconstruídos ou reparados quando necessário.\nVocê pode acompanhar o progresso de atualização nesta página especial.",
+ "smw_smwadmin_updatenotstarted": "Já existe um processo de atualização em execução.\nNão foi criado outro.",
+ "smw_smwadmin_updatestopped": "Todos os processos de atualização existentes foram parados.",
+ "smw_smwadmin_updatenotstopped": "Para parar o processo de atualização em execução, você precisa ativar a caixa de seleção para indicar que realmente tem certeza.",
+ "smw-admin-docu": "Esta página especial te ajuda durante a instalação, atualização ou manutenção do <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>. Ela também fornece funções administrativas, tarefas e estatísticas. \nLembre-se de efetuar cópias de segurança dos dados importantes antes de executar funções administrativas.",
+ "smw-admin-environment": "Ambiente de software",
+ "smw-admin-db": "Configuração do banco de dados",
+ "smw-admin-db-preparation": "A inicialização da tabela está em curso e pode demorar algum tempo até que os resultados sejam apresentados, dependendo do tamanho e de possíveis otimizações da tabela.",
+ "smw-admin-dbdocu": "O Semantic MediaWiki requer sua própria estrutura de banco de dados (e é independente do MediaWiki, portanto, não afeta o resto da instalação do MediaWiki), a fim de armazenar os dados semânticos.\nEsta função de instalação pode ser executada várias vezes sem causar quaisquer danos, mas é necessária apenas uma vez na instalação ou na atualização.",
+ "smw-admin-permissionswarn": "Se a operação falhar com erros de SQL, provavelmente o usuário da base de dados utilizado pelo seu wiki (consulte o seu arquivo LocalSettings.php) não possui permissões suficientes.\nConceda a esse usuário permissões adicionais para criar e eliminar tabelas, introduza temporariamente as credenciais do seu superusuário (<i>root</i>) da base de dados em LocalSettings.php, ou use o ''script'' de manutenção <code>setupStore.php</code>, o qual pode utilizar as credenciais de um administrador.",
+ "smw-admin-dbbutton": "Inicializar ou atualizar tabelas",
+ "smw-admin-announce": "Anuncie seu wiki",
+ "smw-admin-announce-text": "Se seu wiki é público, você pode registrá-lo no <a href=\"https://wikiapiary.com\">WikiApiary</a>, o wiki que acompanha outros wikis.",
+ "smw-admin-deprecation-notice-title": "Avisos de depreciação",
+ "smw-admin-deprecation-notice-docu": "A seção seguinte contém configurações que foram depreciadas ou removidas mas foram detectadas como estando ativas neste wiki. É esperado que qualquer versão futura remova o suporte para estas configurações.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> está depreciado e será removido em $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> irá remover (ou substituir) {{PLURAL:$2|a seguinte opção|as seguintes opções}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> foi descontinuado e será removido na versão $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi substituído por <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "{{PLURAL:$2|Opção|Opções}} de <code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> está para substituído por <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi removido em $2",
+ "smw-admin-deprecation-notice-title-notice": "Próximas alterações",
+ "smw-admin-deprecation-notice-title-notice-explanation": "As seguintes configurações foram detectadas neste wiki e estão planejadas para serem removidas ou alteradas em uma futura versão.",
+ "smw-admin-deprecation-notice-title-replacement": "Configurações substituídas ou renomeadas",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "A seção a seguir contém configurações que foram renomeadas ou de outro modo alteradas e é recomendado que sejam atualizadas com seus nomes ou formatos atuais.",
+ "smw-admin-deprecation-notice-title-removal": "Configurações removidas",
+ "smw-admin-deprecation-notice-title-removal-explanation": "As configurações listadas foram removidas em uma versão anterior mas foram detectadas neste wiki.",
+ "smw-smwadmin-refresh-title": "Reparação e atualização dos dados",
+ "smw_smwadmin_datarefresh": "Reparação dos dados",
+ "smw_smwadmin_datarefreshdocu": "É possível restaurar todos os dados do Semantic MediaWiki baseado no conteúdo atual do wiki.\nIsto pode ser útil para reparar dados corrompidos ou para atualizar os dados se o formato interno tiver sido alterado devido a alguma atualização do software.\nA atualização é executada página a página e não ficará completa de imediato.\nAbaixo é mostrado se uma atualização está em processo e permite-lhe iniciar ou parar atualizações (a menos que esta funcionalidade tenha sido desativada por um administrador do site).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Uma atualização já se encontra em progresso.</strong>\nÉ normal que a atualização progrida lentamente já que apenas atualiza dados em pequenos blocos de cada vez quando um usuário acessa o wiki.\nPara terminar esta atualização mais rapidamente, você pode executar o script de manutenção do MediaWiki <code>runJobs.php</code> (use a opção <code>--maxjobs 1000</code> para restringir o número de atualizações feitas em um bloco).\nProgresso estimado da atualização corrente:",
+ "smw_smwadmin_datarefreshbutton": "Programar reconstrução de dados",
+ "smw_smwadmin_datarefreshstop": "Parar esta atualização",
+ "smw_smwadmin_datarefreshstopconfirm": "Sim, tenho {{GENDER:$1|certeza}}.",
+ "smw-admin-job-scheduler-note": "Tarefas (aquelas habilitadas) nesta seção são executadas via fila de processos para evitar situações de travamento durante a sua execução. A [https://www.mediawiki.org/wiki/Manual:Job_queue fila de processos] é responsável pelo processamento, assim sendo, é crítico que o script de manutenção <code>runJobs.php</code> tenha uma capacidade adequada (veja também o parâmetro de configuração <code>$wgRunJobsAsync</code>).",
+ "smw-admin-outdateddisposal-title": "Eliminação de entidades obsoletas",
+ "smw-admin-outdateddisposal-intro": "Algumas atividades (a alteração de um tipo de propriedade, a remoção de páginas wiki ou a correção de valores em erro) resultam em [https://www.semantic-mediawiki.org/wiki/Outdated_entities entidades desatualizadas] e é recomendado que estas sejam removidas periodicamente para liberar espaço nas tabelas associadas.",
+ "smw-admin-outdateddisposal-active": "Foi agendado um processo de eliminação de entidades desatualizadas.",
+ "smw-admin-outdateddisposal-button": "Agendar eliminação",
+ "smw-admin-feature-disabled": "Esta funcionalidade foi desativada neste wiki. Consulte a página de ajuda das <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">configurações</a> ou contate o administrador do sistema.",
+ "smw-admin-propertystatistics-title": "Reparação de estatísticas de propriedades",
+ "smw-admin-propertystatistics-intro": "Reconstrói todas as estatísticas de utilização de propriedades e nesse processo atualiza e corrige a [https://www.semantic-mediawiki.org/wiki/help:Property_usage_count contagem de uso] das propriedades.",
+ "smw-admin-propertystatistics-active": "Uma tarefa de reconstrução de estatísticas de propriedade foi agendada.",
+ "smw-admin-propertystatistics-button": "Agendar reparação de estatísticas",
+ "smw-admin-fulltext-title": "Reparação de texto completo",
+ "smw-admin-fulltext-intro": "Reconstrói o índice de pesquisas com base nas tabelas de propriedade, usando um tipo de dados que suporta a [https://www.semantic-mediawiki.org/wiki/full-text pesquisa de texto completo]. Alterações nas normas de indexação (''stopwords'' alteradas, novo ''stemmer'', etc.) ou uma tabela nova ou alterada requerem que este processo volte a ser executado.",
+ "smw-admin-fulltext-active": "Uma tarefa de reconstrução de pesquisa de texto completo foi agendada.",
+ "smw-admin-fulltext-button": "Agendar reparação de texto completo",
+ "smw-admin-support": "Obtendo suporte",
+ "smw-admin-supportdocu": "Vários recursos estão disponíveis para lhe ajudar em caso de problemas:",
+ "smw-admin-installfile": "Se tiver problemas com a sua instalação, comece por rever as orientações no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">arquivo INSTALL</a> e na <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">página de instalação</a>.",
+ "smw-admin-smwhomepage": "A documentação completa para o usuário do Semantic MediaWiki está em <b><a href=\"https://www.semantic-mediawiki.org/wiki/P%C3%A1gina_principal\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Bugs podem ser reportados no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>, a página para <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">reportar bugs</a> fornece instruções para criar relatórios eficazes.",
+ "smw-admin-questions": "Se tiver mais questões ou sugestões, junte-se à <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">lista de discussão</a> ou a <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">sala de bate-papo</a> dos usuários do Semantic MediaWiki.",
+ "smw-admin-other-functions": "Outras funções",
+ "smw-admin-supplementary-section-title": "Funções suplementares",
+ "smw-admin-supplementary-section-subtitle": "Funções do núcleo",
+ "smw-admin-supplementary-section-intro": "Esta seção fornece funções adicionais além do escopo de manutenção e é possível que algumas funções listadas na [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentação] estejam restritas ou indisponíveis e, portanto, inacessível neste wiki.",
+ "smw-admin-supplementary-settings-title": "Configurações",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> contém uma lista das consigurações disponíveis utilizadas no Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Estatísticas operacionais",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> apresenta um conjunto extendido de estatísticas",
+ "smw-admin-supplementary-idlookup-title": "Pesquisa e eliminação de entidades",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contém funções para pesquisar e eliminar entidades individuais",
+ "smw-admin-supplementary-duplookup-title": "Entidades duplicadas",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> para listar entradas que estão categorizadas como tendo duplicados na tabela de entidades",
+ "smw-admin-supplementary-duplookup-docu": "Esta página lista as entradas da [https://www.semantic-mediawiki.org/wiki/Help:Entity_table tabela de entidades] que foram categorizadas como duplicados. Entradas duplicadas só devem ocorrer (se de todo) em ocasiões raras, possivelmente causadas por um processo terminado durante uma atualização da base de dados ou por uma transação de rollback não concluída.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estatísticas de Cache",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> mostra estatísticas relacionadas com a cache",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Funções Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> exibe configurações e estatísticas de índices",
+ "smw-admin-supplementary-elastic-docu": "Esta página contém informações sobre configurações, mapeamentos, situação e estatísticas de índices relacionados ao cluster do Elasticsearch que está conectado ao Semantic MediaWiki e seu [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Funções suportadas",
+ "smw-admin-supplementary-elastic-settings-title": "Configurações",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> utilizado pelo Elasticsearch para gerenciar os índices do Semantic MediaWiki",
+ "smw-admin-supplementary-elastic-mappings-title": "Mapeamentos",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> para listar índices e mapeamentos de campo",
+ "smw-admin-supplementary-elastic-mappings-docu": "Esta página contém detalhes do mapeamento de campos tal como são usados com os índices atuais. O resumo dos mapeamentos deve ser monitorizado em ligação com o limite <code>index.mapping.total_fields.limit</code> que especifica o número máximo de campos num índice.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resumo",
+ "smw-admin-supplementary-elastic-mappings-fields": "Mapeamentos de campos",
+ "smw-admin-supplementary-elastic-nodes-title": "Nodes",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> exibe estatísticas dos nodes",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> fornece uma visão geral dos índices disponíveis e suas estatísticas",
+ "smw-admin-supplementary-elastic-statistics-title": "Estatísticas",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> exibe o nível de estatística dos índices",
+ "smw-admin-supplementary-elastic-statistics-docu": "Esta página fornece dados sobre estatísticas de índices para diferentes operações que estão acontecendo em um nível de índice, os dados retornados são agrupados com agrupamentos primários e totais. A [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html página de ajuda] contém uma descrição detalhada das estatísticas de índice disponíveis.",
+ "smw-admin-supplementary-elastic-status-replication": "Status da replicação",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Última replicação ativa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalo de atualização: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Tarefas de recuperação em atraso: $1 (estimativa)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Tarefas de ingestão (de ficheiros) em atraso: $1 (estimativa)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replicação trancada: $1 (recriação em progresso)",
+ "smw-list-count": "A lista contém $1 {{PLURAL:$1|item|itens}}.",
+ "smw-list-count-from-cache": "A lista contém $1 {{PLURAL:$1|item e foi obtido|itens e foram obtidos}} do cache (UTC: $2).",
+ "smw-property-label-uniqueness": "O nome \"$1\" foi correspondido por pelo menos uma outra representação de propriedade. Por favor, consulte a [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness página de ajuda] sobre como resolver este problema.",
+ "smw-property-label-similarity-title": "Relatório de similaridade de nome de propriedade",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula as semelhanças de nomes de propriedade existentes",
+ "smw-property-label-similarity-threshold": "Limite:",
+ "smw-property-label-similarity-type": "Exibir o tipo de ID",
+ "smw-property-label-similarity-noresult": "Nenhum resultado foi encontrado para as opções selecionadas.",
+ "smw-property-label-similarity-docu": "Compara e reporta a [https://www.semantic-mediawiki.org/wiki/Property_similarity semelhança sintática] (não a semelhança semântica) entre dois nomes de propriedades, o que pode ajudar a filtrar aqueles com erros ortográficos ou propriedades equivalentes que representam o mesmo conceito (ver a página especial [[Special:Properties|Propriedades]] para clarificar o conceito e a utilização das propriedades reportadas). O limite pode ser ajustado para aumentar ou reduzir a distância da semelhança. <code>[[Property:$1|$1]]</code> é usado para isentar propriedades desta análise.",
+ "smw-admin-operational-statistics": "Esta página contém estatísticas operacionais coletadas em ou a partir de funções relacionadas ao Semantic MediaWiki. Uma extensa lista de estatísticas específicas do wiki pode ser encontrada [[Special:Statistics|<b>aqui</b>]].",
+ "smw_adminlinks_datastructure": "Estrutura de dados",
+ "smw_adminlinks_displayingdata": "Exibição de dados",
+ "smw_adminlinks_inlinequerieshelp": "Ajuda para consultas inline",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Número de utilizações] estimado: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propriedade definida pelo {{PLURAL:$1|usuário|sistema}}",
+ "smw-property-indicator-last-count-update": "Contagem estimada de uso\nÚltima atualização: $1",
+ "smw-concept-indicator-cache-update": "Contagem da cache\nÚltima atualização: $1",
+ "smw-createproperty-isproperty": "É uma propriedade do tipo $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|O valor permitido para esta propriedade é|Os valores permitidos para esta propriedade são}}:",
+ "smw-paramdesc-category-delim": "O delimitador",
+ "smw-paramdesc-category-template": "Uma predefinição para formatar os itens com",
+ "smw-paramdesc-category-userparam": "Um parâmetro para passar para a predefinição",
+ "smw-info-par-message": "Mensagem a ser exibida.",
+ "smw-info-par-icon": "Ãcone para mostrar, \"info\" ou \"aviso\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Opções gerais",
+ "prefs-ask-options": "Opções da página Especial:Consultar",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (e extensões relacionadas) fornecem personalização individual para algumas funções selecionadas. Consulte o [https://www.semantic-mediawiki.org/wiki/Help:User_preferences página de ajuda] para uma descrição detalhada.",
+ "smw-prefs-ask-options-tooltip-display": "Exibir o texto do parâmetro em forma de dica",
+ "smw-prefs-ask-options-compact-view-basic": "Ativar visão compacta básica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Se ativado, apresenta um conjunto reduzido de hiperligações na vista compacta Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Ativar a correção da hora para páginas especiais utilizando a preferência do [[Special:Preferences#mw-prefsection-rendering|fuso horário]] local",
+ "smw-prefs-general-options-jobqueue-watchlist": "Mostrar a lista de vigilância da fila de tarefas na minha barra pessoal",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Se ativado, mostra uma [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist lista] de tarefas selecionadas pendentes, com os respetivos tamanhos de fila estimados.",
+ "smw-prefs-general-options-disable-editpage-info": "Desativar o texto introdutório na edição da página",
+ "smw-prefs-general-options-disable-search-info": "Desativar as informações de suporte da sintaxe na página de pesquisa padrão",
+ "smw-prefs-general-options-suggester-textinput": "Ativar o auxiliar de preenchimento para as entidades semânticas",
+ "smw-prefs-help-general-options-suggester-textinput": "Se ativado, permite usar um [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance auxiliar de preenchimento] para encontrar propriedades, conceitos e categorias num contexto de entrada.",
+ "smw-ui-tooltip-title-property": "Propriedade",
+ "smw-ui-tooltip-title-quantity": "Conversão de unidade",
+ "smw-ui-tooltip-title-info": "Informação",
+ "smw-ui-tooltip-title-service": "Links de serviço",
+ "smw-ui-tooltip-title-warning": "Alerta",
+ "smw-ui-tooltip-title-error": "Erro",
+ "smw-ui-tooltip-title-parameter": "Parâmetro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Referência",
+ "smw_unknowntype": "O tipo \"$1\" desta propriedade é inválido",
+ "smw-concept-cache-text": "O conceito possui um total de $1 {{PLURAL:$1|página|páginas}} e foi atualizado pela última vez às $3 em $2.",
+ "smw_concept_header": "Páginas do conceito \"$1\"",
+ "smw_conceptarticlecount": "Exibindo {{PLURAL:$1|uma página|$1 páginas}}.",
+ "smw-qp-empty-data": "Os dados requisitados não puderam ser exibidos devido a algum critério de seleção insuficiente.",
+ "right-smw-admin": "Acessar as tarefas de administração (Semantic MediaWiki)",
+ "right-smw-patternedit": "Acesso de edição para manter expressões regulares e padrões permitidos (Semantic MediaWiki)",
+ "right-smw-pageedit": "Acesso de edição para páginas marcadas com <code>Is edit protected</code> (Semantic MediaWiki)",
+ "right-smw-ruleedit": "Editar páginas de regras (MediaWiki Semântico)",
+ "restriction-level-smw-pageedit": "protegida (somente usuários com permissão)",
+ "action-smw-patternedit": "editar expressões regulares utilizadas pelo Semantic MediaWiki",
+ "action-smw-pageedit": "editar páginas anotadas com <code>É protegida de edição</code> (Semantic MediaWiki)",
+ "group-smwadministrator": "Administradores (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrador|administradora|administrador(a)}} (Semantic MediaWiki)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administradores (Semantic MediaWiki)",
+ "group-smwcurator": "Curadores (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|curador|curadora}} (Semantic MediaWiki)",
+ "grouppage-smwcurator": "{{ns:project}}:Curators (Semantic MediaWiki)",
+ "action-smw-admin": "acessar as tarefas de administração do Semantic MediaWiki",
+ "action-smw-ruleedit": "editar páginas de regras (MediaWiki Semântico)",
+ "smw-property-predefined-default": "\"$1\" é uma propriedade predefinida.",
+ "smw-property-predefined-common": "Esta propriedade é predefinida (também chamada de [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriedade especial]) e vem com privilégios administrativos adicionais, mas pode ser usada como qualquer outra [https://www.semantic-mediawiki.org/wiki/Property propriedade definida pelo usuário].",
+ "smw-property-predefined-ask": "\"$1\" é uma propriedade predefinida que representa metadados (na forma de um [https://www.semantic-mediawiki.org/wiki/Subobject subobjeto]) sobre consultas individuais e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "\"$1\" é uma propriedade predefinida (também conhecida como [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriedade especial]) que coleta o número de condições utilizadas em uma consulta e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "\"$1\" é uma propriedade predefinida que informa sobre a profundidade de uma consulta e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "É um valor numérico calculado com base nas consultas intercaladas, nas cadeias de propriedades e nos elementos descritivos disponíveis, sendo que a execução de cada consulta está restringida pelo parâmetro de configuração <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "\"$1\" é uma propriedade predefinida que descreve parâmetros que influenciam um resultado de consulta e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askpa": "É parte de um conjunto de propriedades que especificam um [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Esta página lista as [https://www.semantic-mediawiki.org/wiki/Property propriedades] e suas frequências de uso neste wiki. Para uma estatística de contagem atualizada é recomendável que o script de manutenção de [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics estatísticas de propriedades] seja executado frequentemente. Para uma visualização diferenciada, veja a página especial [[Special:UnusedProperties|propriedades não utilizadas]] ou [[Special:WantedProperties|propriedades desejadas]].",
+ "smw-sp-properties-cache-info": "Os dados listados foram extraídos do [https://www.semantic-mediawiki.org/wiki/Caching cache] e atualizados pela última vez em $1.",
+ "smw-sp-properties-header-label": "Lista de propriedades",
+ "smw-admin-settings-docu": "Exibe uma lista contendo todas as configurações padrão e locais que são relevantes ao ambiente do Semantic MediaWiki.\nPara detalhes sobre configurações individuais, por favor, consulte a [https://www.semantic-mediawiki.org/wiki/Help:Configuration página de ajuda sobre configuração].",
+ "smw-sp-admin-settings-button": "Gerar lista de configurações",
+ "smw-admin-idlookup-title": "Procurar",
+ "smw-admin-idlookup-docu": "Esta seção mostra detalhes técnicos sobre uma entidade individual (wikipage, subobjeto, propriedade, etc.) em Semantic MediaWiki. A entrada pode ser um ID numérico ou um valor de cadeia para corresponder ao campo de pesquisa relevante, no entanto, qualquer referência de ID refere-se ao Semântica MediaWiki e não à página do MediaWiki ou ao ID de revisão.",
+ "smw-admin-iddispose-title": "Disposição",
+ "smw-admin-iddispose-docu": "Deve-se observar que a operação de descarte é irrestrita e removerá a entidade do mecanismo de armazenamento juntamente com todas as suas referências nas tabelas pendentes, realize esta operação com '''cuidado''' e somente após consultar a [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentação].",
+ "smw-admin-iddispose-done": "O ID \"$1\" foi removido do mecanismo de armazenamento.",
+ "smw-admin-iddispose-references": "O identificador \"$1\" {{PLURAL:$2|não tem nenhuma|tem pelo menos uma}} referência ativa.",
+ "smw-admin-iddispose-references-multiple": "Lista de correspondências com pelo menos um registro de referência ativo.",
+ "smw-admin-iddispose-no-references": "A pesquisa não conseguiu corresponder \"$1\" a uma entrada na tabela.",
+ "smw-admin-idlookup-input": "Pesquisar:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Visão geral",
+ "smw-admin-tab-notices": "Avisos de descontinuação",
+ "smw-admin-tab-maintenance": "Manutenção",
+ "smw-admin-tab-supplement": "Funções suplementares",
+ "smw-admin-tab-registry": "Registro",
+ "smw-admin-maintenance-no-description": "Sem descrição.",
+ "smw-admin-maintenance-script-section-title": "Lista de scripts de manutenção disponíveis",
+ "smw-admin-maintenance-script-section-intro": "Os scripts de manutenção a seguir requerem um administrador e acesso à linha de comando para poder executar scripts listados.",
+ "smw-admin-maintenance-script-description-dumprdf": "Exportação RDF de triplos existentes.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "Este script é usado para gerenciar as ''caches'' de conceitos do Semantic MediaWiki, sendo ele que pode criar, remover e atualizar ''caches'' selecionadas.",
+ "smw-admin-maintenance-script-description-rebuilddata": "Recria todos os dados semânticos na base de dados, percorrendo todas as páginas que possam ter dados semânticos.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "Reconstrói o índice do Elasticsearch (nas instalações que usam o <code>ElasticStore</code>), percorrendo todas as entidades que possuem dados semânticos.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "Reconstrói o índice de pesquisa de texto completo <code>SQLStore</code> (nas instalações onde a configuração foi ativada).",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "Recria as estatísticas de uso para todas as entidades de propriedade.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "Remove entidades duplicadas encontradas em tabelas selecionadas que não possuem referências ativas.",
+ "smw-admin-maintenance-script-description-setupstore": "Configura o modo de armazenamento selecionado em <code>LocalSettings.php</code>.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "Atualiza o campo <code>smw_sort</code> do <code>SQLStore</code> (de acordo com a configuração [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "Popula o campo <code>smw_hash</code> das linhas em que este não tem um valor.",
+ "smw-livepreview-loading": "Carregando...",
+ "smw-sp-searchbyproperty-description": "Esta página fornece uma simples [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interface de navegação] para encontrar entidades descritas por uma propriedade e um determinado valor. Outras interfaces de busca disponíveis incluem a página de [[Special:PageProperty|busca por propriedade]] e o [[Special:Ask|construtor de consultas]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista de resultados",
+ "smw-sp-searchbyproperty-nonvaluequery": "Uma lista de valores que possuem a propriedade \"$1\" atribuída.",
+ "smw-sp-searchbyproperty-valuequery": "Uma lista de páginas que possuem a propriedade \"$1\" com o valor \"$2\" atribuído.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" não pode ser atribuído a uma declaração do tipo número com o valor $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" retornou o valor \"NULL\" (nulo), que não é permitido como um número.",
+ "smw-editpage-annotation-enabled": "Esta página suporta marcações semânticas no texto (por exemplo, <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) para construir conteúdo estruturado e recuperável por consultas providas pelo Semantic MediaWiki. Para uma descrição completa sobre como utilizar marcações ou a função #ask, por favor, leia as páginas de ajuda: [https://www.semantic-mediawiki.org/wiki/Help:Getting_started primeiros passos], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation marcação no texto] ou [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries consultas embutidas].",
+ "smw-editpage-annotation-disabled": "Esta página não está habilitada para marcações semânticas no texto devido à restrições de namespace. Detalhes sobre como habilitar um namespace podem ser encontrados na página de ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuração].",
+ "smw-editpage-property-annotation-enabled": "Esta propriedade pode ser extendida utilizando marcações semânticas para especificar o tipo de dados (por exemplo <nowiki>\"[[Has type::Page]]\"</nowiki>) ou outras declarações suportadas (por exemplo <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). Para uma descrição sobre como melhorar esta página, veja a [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaração de uma propriedade] ou a página de ajuda que [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes lista os tipos de dados disponíveis].",
+ "smw-editpage-property-annotation-disabled": "Esta propriedade não pode ser extendida com a marcação de tipo de dados (por exemplo, <nowiki>\"[[Has type::Page]]\"</nowiki>) já que ela é predefinida (para mais informações, veja a página de ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriedades especiais]).",
+ "smw-editpage-concept-annotation-enabled": "Este conceito pode ser extendido utilizando a função #concept. Veja a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceitos].",
+ "smw-search-syntax-support": "A entrada da pesquisa permite o uso da sintaxe [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search query syntax] para encontrar correspondências usando o MediaWiki Semântico.",
+ "smw-search-input-assistance": "O [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ajudante de preenchimento] também é habilitado para facilitar a pré-seleção de propriedades e categorias disponíveis.",
+ "smw-search-help-intro": "Uma entrada com a forma <code><nowiki>[[ ... ]]</nowiki></code> assinala ao processador da entrada que deve usar o motor de pesquisa do MediaWiki Semântico. Note que a combinação de <code><nowiki>[[ ... ]]</nowiki></code> com uma pesquisa de texto não estruturado, como <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code>, não é suportada.",
+ "smw-search-help-structured": "Pesquisas estruturadas:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (como [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context contexto filtrado])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (com um [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context contexto de consulta])",
+ "smw-search-help-proximity": "Pesquisas aproximidas (uma propriedade ser desconhecida, '''só''' disponível para aqueles motores de pesquisa que fornecem uma integração com a pesquisa de texto completo):\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (pesquisar em todos os documentos os termos \"lorem\" e \"ipsum\" que tenham sido indexados)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (pesquisar correspondências com \"lorem ipsum\" como frase)",
+ "smw-search-help-ask": "As seguintes hiperligações explicam como usar a sintaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecionar páginas] descreve como selecionar páginas e construir condições\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de pesquisa] lista os operadores de pesquisa disponíveis, incluindo os para consultas de intervalos e consultas com caracteres de substituição",
+ "smw-search-input": "Introdução e pesquisa",
+ "smw-search-help-input-assistance": "É fornecida uma [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ajuda de introdução] para o campo de entrada e requer que seja usado um dos seguintes prefixos:\n\n*<code>p:</code> para ativar as sugestões de propriedades (p. ex.: <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> para ativar as sugestões de categorias\n\n*<code>con:</code> para ativar as sugestões de conceitos",
+ "smw-search-syntax": "Sintaxe",
+ "smw-search-profile": "Estendido",
+ "smw-search-profile-tooltip": "Funções de pesquisa em conexão com a Semantic MediaWiki",
+ "smw-search-profile-sort-best": "Melhor correspondência",
+ "smw-search-profile-sort-recent": "Mais recente",
+ "smw-search-profile-sort-title": "Título",
+ "smw-search-profile-extended-help-intro": "O [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile perfil alargado] da página Special:Search dá acesso a funções de pesquisa específicas da extensão MediaWiki Semântico e do seu servidor de consultas suportado.",
+ "smw-search-profile-extended-help-sort": "Especifica uma preferência de ordenação para a apresentação do resultado com:",
+ "smw-search-profile-extended-help-sort-title": "*\"Título\" usando o título da página (ou título de apresentação) como critério de ordenação",
+ "smw-search-profile-extended-help-sort-recent": "*\"Mais recente\" mostrará primeiro as entidades modificadas mais recentemente (as entidades subobjetos são suprimidas porque essas entidades não estão anotadas com uma [[Property:Modification date|data de modificação]])",
+ "smw-search-profile-extended-help-sort-best": "*\"Melhor correspondência\" ordenará as entidades por [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy relevância] com base em classificações fornecidas pelo servidor",
+ "smw-search-profile-extended-help-form": "São disponibilizados formulários (se estes forem mantidos) para certos casos específicos de utilização. Os formulários podem expor diferentes campos de propriedade e de entrada de valor, para limitar o processo de introdução e facilitar a criação de pedidos de pesquisa (ver $1).",
+ "smw-search-profile-extended-help-namespace": "A caixa de seleção do espaço nominal/domínio será ocultada logo que um formulário for selecionado mas pode ser tornada visível com a ajuda do botão \"mostrar/esconder\".",
+ "smw-search-profile-extended-help-search-syntax": "O campo de entrada da pesquisa permite o uso da sintaxe <code>#ask</code> para definir um contexto de pesquisa específico do MediaWiki Semântico. Algumas expressões úteis:",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code> para encontrar tudo o que contenha \"...\" e é especialmente útil quando não se conhece o contexto da pesquisa ou as propriedades envolvidas (por exemplo, <code>in:(lorem && ipsum)</code> é equivalente a <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>).",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code> para encontrar tudo o que contenha \"...\" exatamente na mesma ordem",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code> para corresponder com qualquer entidade com uma propriedade \"...\" (por exemplo, <code>has:(Foo && Bar)</code> é equivalente a <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> para não corresponder com nenhuma entidade que inclui \"...\"",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Estão disponíveis e definidos prefixos personalizados adicionais, como: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Algumas expressões estão reservadas, como: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''Algumas das operações listadas só são úteis em ligação com um índice ativado de texto completo ou com o ElasticStore.''",
+ "smw-search-profile-extended-help-query": "<code><nowiki>$1</nowiki></code> foi usado como consulta.",
+ "smw-search-profile-extended-help-query-link": "(Para mais detalhes $1).",
+ "smw-search-profile-extended-help-find-forms": "formulários disponíveis",
+ "smw-search-profile-extended-section-sort": "Ordenar por",
+ "smw-search-profile-extended-section-form": "Formulários",
+ "smw-search-profile-extended-section-search-syntax": "Entrada de pesquisa",
+ "smw-search-profile-extended-section-namespace": "Espaço nominal",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "smw-search-profile-link-caption-query": "ver",
+ "smw-search-show": "Exibir",
+ "smw-search-hide": "Esconder",
+ "log-name-smw": "Log do Semantic MediaWiki",
+ "log-show-hide-smw": "$1 o registo do Semantic MediaWiki",
+ "logeventslist-smw-log": "Registo do MediaWiki Semântico",
+ "log-description-smw": "Atividades para [https://www.semantic-mediawiki.org/wiki/Help:Logging tipos de eventos habilitados] que são reportados pelo Semantic MediaWiki e seus componentes.",
+ "logentry-smw-maintenance": "Eventos relacionados à manutenção emitidos pelo Semantic MediaWiki.",
+ "smw-datavalue-import-unknown-namespace": "O namespace \"$1\" a ser importado é desconhecido. Por favor, tenha certeza de que os detalhes da importação OWL estão disponíveis via [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "Não foi possível encontrar um URI de namespace \"$1\" na [[MediaWiki:Smw import $1|página de importação $1]].",
+ "smw-datavalue-import-missing-type": "Nenhuma definição de tipo foi encontrada para \"$1\" na [[MediaWiki:Smw import $2|página de importação $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|importado de $1]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" não é um formato válido, a formato esperado consiste em \"namespace\":\"identificador\" (por exemplo: \"foaf:name\").",
+ "smw-datavalue-import-invalid-format": "Era esperado que o texto \"$1\" estivesse dividido em quatro partes, mas o formato não foi compreendido.",
+ "smw-property-predefined-impo": "\"$1\" é uma propriedade predefinida que descreve uma relação a um [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulário importado] e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "\"$1\" é uma propriedade predefinida que descreve o [[Special:Types|tipo de dado]] de uma propriedade e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "\"$1\" é uma propriedade predefinida que representa um \"[https://www.semantic-mediawiki.org/wiki/Help:Container container]\" e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "A estrutura recipiente permite acumular atribuições de valores a propriedades e é semelhante a uma página wiki normal, porém, num espaço de entidades diferente, estando ligada ao sujeito que a incorpora.",
+ "smw-property-predefined-errp": "\"$1\" é uma propriedade predefinida que registra erros de entrada em anotações de valor irregular e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-errp": "Na maioria dos casos, a causa é uma não correspondência de tipos ou uma restrição do [[Property:Allows value|valor]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] é uma propriedade predefinida que pode definir uma lista de valores permitidos para restringir a atribuição de valores para uma determinada propriedade. É fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] é uma propriedade predefinida que pode especificar uma referência a uma lista de valores permitidos para restringir a atribuição de valores a uma propriedade, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-property-restricted-annotation-use": "A propriedade \"$1\" tem uma área de aplicação restrita e não pode ser usada como propriedade de anotação por um utilizador.",
+ "smw-datavalue-property-restricted-declarative-use": "A propriedade \"$1\" é uma propriedade declarativa e só pode ser usada numa propriedade ou página de categoria.",
+ "smw-datavalue-property-create-restriction": "A propriedade \"$1\" não existe e o usuário não possui a permissão \"$2\" (veja o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade]) para criar ou atribuir valores à uma propriedade não aprovada.",
+ "smw-datavalue-property-invalid-character": "\"$1\" contém um caractere \"$2\" listado como parte do nome da propriedade e, portanto, foi classificado como inválido.",
+ "smw-datavalue-property-invalid-chain": "Utilizar \"$1\" como uma sequência de propriedades não é permitido durante o processo de marcação.",
+ "smw-datavalue-restricted-use": "O valor de dado \"$1\" foi marcado para uso restrito.",
+ "smw-datavalue-invalid-number": "\"$1\" não pode ser interpretado como um número.",
+ "smw-query-condition-circular": "Uma possível condição circular foi detectada em \"$1\".",
+ "smw-query-condition-empty": "A descrição da consulta tem uma condição vazia.",
+ "smw-types-list": "Lista de tipos de dados",
+ "smw-types-default": "\"$1\" é um tipo de dado predefinido.",
+ "smw-types-help": "Mais informações e exemplos podem ser encontrados na [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 página de ajuda].",
+ "smw-type-anu": "\"$1\" é uma variante do tipo de dados [[Special:Types/URL|URL]] e é utilizada, na maior parte das vezes, para exportar uma declaração ''owl:AnnotationProperty''.",
+ "smw-type-boo": "\"$1\" é um tipo de dado básico para descrever um valor verdadeiro ou falso.",
+ "smw-type-cod": "\"$1\" é uma variante do tipo de dados [[Special:Types/Text|Texto]] para ser utilizada com textos técnicos de tamanho arbitrário, tais como listagens de código fonte.",
+ "smw-type-geo": "\"$1\" é um tipo de dados que descreve localizações geográficas e requer a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"].",
+ "smw-type-tel": "\"$1\" é um tipo de dado especial para descrever números internacionais de telefone de acordo com a RFC 3966.",
+ "smw-type-txt": "\"$1\" é um tipo de dado básico para descrever textos de tamanho arbitrário.",
+ "smw-type-dat": "\"$1\" é um tipo de dado básico para representar pontos no tempo em um formato unificado.",
+ "smw-type-ema": "\"$1\" é um tipo de dados especial para representar um email.",
+ "smw-type-tem": "\"$1\" é um tipo de dados numérico especial para representar uma temperatura.",
+ "smw-type-qty": "\"$1\" é um tipo de dados para descrever quantidades com uma representação numérica e uma unidade de medida.",
+ "smw-type-rec": "\"$1\" é um tipo de dados contêiner que especifica uma lista de propriedades digitadas em uma ordem fixa.",
+ "smw-type-extra-tem": "O esquema de conversão inclui unidades suportadas, como Kelvin, Celsius, Fahrenheit e Rankine.",
+ "smw-type-tab-properties": "Propriedades",
+ "smw-type-tab-types": "Tipos",
+ "smw-type-tab-errors": "Erros",
+ "smw-type-primitive": "Básico",
+ "smw-type-contextual": "Contextual",
+ "smw-type-compound": "Composto",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Esta página fornece uma interface de navegação para encontrar todos os valores de uma propriedade e uma determinada página. Entre as outras interfaces de pesquisa disponíveis, incluem-se a [[Special:SearchByProperty|pesquisa de propriedades]] e o [[Special:Ask|construtor de consultas ''ask'']].",
+ "smw-property-predefined-errc": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] que apresenta os erros que aparecem em relação às marcações impróprias de valores ou de erros de processamento de entrada desses valores.",
+ "smw-property-predefined-long-errc": "Os erros são coletados em uma [https://www.semantic-mediawiki.org/wiki/Help:Container estrutura recipiente] que pode incluir uma referência à propriedade que causou a discrepância.",
+ "smw-property-predefined-errt": "\"$1\" é uma propriedade predefinida que contém uma descrição textual de um erro e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "Um subobjeto definido por um usuário utilizou um esquema de nome inválido. A notação com ponto ($1) nos primeiros cinco caracteres está reservada para o uso exclusivo pelas extensões. Você pode configurar um [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador nomeado].",
+ "smw-datavalue-record-invalid-property-declaration": "O definição do registro contém a propriedade \"$1\" que está, por sua vez, declarada também como sendo do tipo registro e isso não é permitido.",
+ "smw-property-predefined-mdat": "\"$1\" é uma propriedade predefinida que corresponde à data da última modificação e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "\"$1\" é uma propriedade predefinida que corresponde à data da primeira revisão e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "\"$1\" é uma propriedade predefinida que indica se um sujeito é novo ou não, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "\"$1\" é uma propriedade predefinida que contém o nome da página do utilizador que criou a última revisão, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "\"$1\" é uma propriedade predefinida que descreve o tipo MIME de um arquivo carregado, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "\"$1\" é uma propriedade predefinida que descreve o tipo de mídia de um arquivo carregado, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "\"$1\" é uma propriedade predefinida que contém o nome do formato de resultado usado em uma consulta, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "\"$1\" é uma propriedade predefinida que descreve as condições da consulta em forma de texto, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "\"$1\" é uma propriedade predefinida que contém o tempo (em segundos) que a execução da consulta demorou, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksc": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki ], que identifica fontes alternativas de consulta (por exemplo, fontes remotas, federadas).",
+ "smw-property-predefined-askco": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para descrever o estado de uma consulta ou dos seus componentes.",
+ "smw-property-predefined-long-askco": "O número ou números atribuídos representam um estado interno codificado que é explicado na [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler página de ajuda].",
+ "smw-property-predefined-prec": "\"$1\" é uma propriedade predefinida que descreve a [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisão de apresentação] (em casas decimais) para os tipos de dados numéricos.",
+ "smw-types-extra-geo-not-available": "Não foi detetada a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Mapas\"], portanto, \"$1\" está restringido na sua capacidade operacional.",
+ "smw-datavalue-monolingual-dataitem-missing": "Um item esperado para construir um valor composto monolíngue está faltando.",
+ "smw-datavalue-languagecode-missing": "Para a marcação \"$1\" o analisador sintático não conseguiu determinar o código de idioma (exemplo: \"foo@en\").",
+ "smw-datavalue-languagecode-invalid": "\"$1\" não foi reconhecido como um código de idioma válido.",
+ "smw-property-predefined-lcode": "\"$1\" é uma propriedade predefinida que representa um código de idioma formatado BCP47 e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "\"$1\" é um tipo de dados [https://www.semantic-mediawiki.org/wiki/Help:Container recipiente] (''container'') que associa um texto a um [[Property:Language code|código de idioma]] específico.",
+ "smw-types-extra-mlt-lcode": "O tipo de dados {{PLURAL:$2|requer|não requer}} um código de idioma (ou seja, uma marcação de valor sem um código de idioma {{PLURAL:$2|não é|é}} aceito).",
+ "smw-property-predefined-text": "\"$1\" é uma propriedade predefinida que representa um texto de comprimento arbitrário, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "\"$1\" é uma propriedade predefinida que permite descrever uma propriedade no contexto de um idioma, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "\"$1\" é uma propriedade predefinida para definir uma lista de propriedades utilizadas com uma propriedade do tipo [[Special:Types/Record|registro]], é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] In-text annotation parser time",
+ "smw-limitreport-intext-postproctime": "[SMW] tempo pós-processamento",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Store update time (on page purge)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw_allows_pattern": "É esperado que esta página contenha uma lista de referências (seguidas por [https://pt.wikipedia.org/wiki/Express%C3%A3o_regular expressões regulares]) que serão disponibilizadas pela propriedade [[Property:Allows pattern|Allows pattern]]. Para editar esta página a permissão <code>smw-patternedit</code> é necessária.",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\" foi considerado inválido pela expressão regular \"$2\".",
+ "smw-datavalue-allows-pattern-reference-unknown": "O padrão de referência \"$1\" não foi encontrado na página [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "A lista de referência \"$1\" não foi correspondida em uma página [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Está faltando itens com o marcador * no conteúdo da lista \"$1\".",
+ "smw-datavalue-feature-not-supported": "A funcionalidade \"$1\" não é suportada ou foi desativada neste wiki.",
+ "smw-property-predefined-pvap": "\"$1\" é uma propriedade predefinida que pode especificar uma [[MediaWiki:Smw allows pattern|referência de padrão permitido]] para aplicar uma [https://pt.wikipedia.org/wiki/Express%C3%A3o_regular expressão regular], é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-dtitle": "\"$1\" é uma propriedade predefinida que pode atribuir a uma entidade um título de apresentação distinto, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvuc": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para restringir a atribuição de valores a valores únicos (ou a um, no máximo) para cada instância.",
+ "smw-property-predefined-long-pvuc": "A unicidade é estabelecida quando dois valores não são iguais em suas representações literais e qualquer violação desta restrição será categorizada como erro.",
+ "smw-datavalue-uniqueness-constraint-error": "A propriedade \"$1\" só permite a atribuição de valores únicos e \"$2\" já foi atribuído no sujeito \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "A propriedade \"$1\" só permite anotações de valores únicos, ''$2'' já contém um valor atribuído. \"$3\" viola a restrição de unicidade.",
+ "smw-property-predefined-boo": "\"$1\" é um [[Special:Types/Boolean|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] para representar valores booleanos.",
+ "smw-property-predefined-num": "\"$1\" é um [[Special:Types/Number|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores numéricos.",
+ "smw-property-predefined-dat": "\"$1\" é um [[Special:Types/Date|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores de datas.",
+ "smw-property-predefined-uri": "\"$1\" é um [[Special:Types/URL|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" é um [[Special:Types/Quantity|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar valores de quantidade.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\" contém um offset e um identificador de zona, o que não é suportado.",
+ "smw-datavalue-time-invalid-values": "O valor \"$1\" contém uma informação que não pode ser interpretada na forma de \"$2\".",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" contém alguma informação que não pode ser interpretada.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\" contém um travessão, traço de ligação extrínseco ou outros caracteres que são inválidos para a interpretação de uma data.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" contém alguns componentes vazios.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" contém mais de três componentes necessários para a interpretação de uma data.",
+ "smw-datavalue-time-invalid-date-components-sequence": "\"$1\" contém uma sequência que não foi possível interpretar contra uma matriz de correspondências disponível para componentes de datas.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" contém \"$2\" como elemento de hora, o que é inválido no formato de relógio de 12 horas.",
+ "smw-datavalue-time-invalid-jd": "Não foi possível interpretar o valor \"$1\" como um dia válido no calendário Juliano, \"$2\" pode apresentar o problema.",
+ "smw-datavalue-time-invalid-prehistoric": "Não é possível interpretar o valor de entrada pré-histórico \"$1\". Por exemplo, especificar mais do que anos, ou do que um modelo de calendário, poderá produzir resultados inesperados num contexto pré-histórico.",
+ "smw-datavalue-time-invalid": "Não foi possível interpretar o valor \"$1\" como uma data válida ou como um componente de tempo, \"$2\" pode apresentar o problema.",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "O espaço reservado \"$1\" está faltando no formatador do URI.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" é um URL inválido.",
+ "smw-datavalue-external-identifier-formatter-missing": "Falta à propriedade a atribuição de um [[Property:External formatter uri|\"Formatador de URI externo\"]].",
+ "smw-datavalue-keyword-maximum-length": "A palavra-chave excedeu o tamanho máximo de $1 {{PLURAL:$1|carácter|caracteres}}.",
+ "smw-property-predefined-eid": "\"$1\" é um [[Special:Types/External identifier|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para representar identificadores externos.",
+ "smw-property-predefined-peid": "\"$1\" é uma propriedade predefinida que especifica um identificador externo, é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pefu": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] para especificar um recurso externo com um espaço reservado.",
+ "smw-property-predefined-long-pefu": "Espera-se que o URI contenha um espaço reservado que será ajustado com um valor de [[Special:Types/External identifier|identificador externo]] para formar uma referência de recurso válida.",
+ "smw-type-eid": "\"$1\" é uma variante do tipo de dados [[Special:Types/Text|Texto]] que requer propriedades atribuídas para declarar um [[Property:External formatter uri|formatador de URI externo]].",
+ "smw-property-predefined-keyw": "\"$1\" é uma propriedade predefinida e um [[Special:Types/Keyword|tipo]], fornecidas pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico], que normalizam um texto e têm um tamanho restrito de caracteres.",
+ "smw-type-keyw": "\"$1\" é uma variante de tipo de dados [[Special:Types/Text|Texto]] que tem um tamanho restrito de caracteres e que normaliza a sua representação de conteúdo.",
+ "smw-datavalue-stripmarker-parse-error": "O valor fornecido, \"$1\", contém [https://en.wikipedia.org/wiki/Help:Strip_markers marcadores do analisador sintático] e, portanto, não pode ser suficientemente analisado.",
+ "smw-datavalue-parse-error": "O valor fornecido \"$1\" não foi compreendido.",
+ "smw-datavalue-propertylist-invalid-property-key": "A lista de propriedades \"$1\" continha uma chave de propriedade inválida \"$2\".",
+ "smw-datavalue-type-invalid-typeuri": "Não foi possível transformar \"$1\" numa representação válida de URI.",
+ "smw-datavalue-wikipage-missing-fragment-context": "O valor de entrada \"$1\" da página wiki não pode ser usado sem uma página de contexto.",
+ "smw-datavalue-wikipage-invalid-title": "O valor de entrada \"$1\" da página contém caracteres inválidos ou está incompleto e, portanto, pode causar resultados inesperados durante uma consulta ou processo de anotação.",
+ "smw-datavalue-wikipage-property-invalid-title": "Propriedade \"$1\" (como tipo de página) com valor de entrada \"$2\" contém caracteres inválidos ou está incompleto e, portanto, pode causar resultados inesperados durante uma consulta ou processo de anotação.",
+ "smw-datavalue-wikipage-empty": "O valor de entrada de página está vazio (por exemplo, <code>[[SomeProperty::]], [[]]</code>) e, portanto, não pode ser usado como nome ou como parte da condição de uma consulta.",
+ "smw-type-ref-rec": "\"$1\" é um tipo de dados [https://www.semantic-mediawiki.org/wiki/Container recipiente] (''container'') que permite registrar informação adicional sobre a atribuição de um valor (por exemplo, dados de proveniência).",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "O tipo [[Special:Types/Reference|Referência]] espera que seja declarada uma lista de propriedades utilizando a propriedade [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Has fields].",
+ "smw-parser-invalid-json-format": "O analisador JSON terminou com um erro \"$1\".",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" não pode ser usado como rótulo preferido porque o idioma \"$2\" já está atribuído ao rótulo \"$3\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copiar link para a área de transferência",
+ "smw-property-userdefined-fixedtable": "\"$1\" estava configurada como [https://www.semantic-mediawiki.org/wiki/Fixed_properties propriedade fixa] e qualquer modificação em sua [https://www.semantic-mediawiki.org/wiki/Type_declaration declaração de tipo] requer: ou que seja executado <code>setupStore.php</code>, ou que seja completada a tarefa especial [[Special:SemanticMediaWiki|\"Instalação e atualização da base de dados\"]].",
+ "smw-data-lookup": "Obtendo dados...",
+ "smw-data-lookup-with-wait": "A requisição está sendo processada e pode levar algum tempo.",
+ "smw-no-data-available": "Nenhum dado disponível.",
+ "smw-property-req-violation-missing-fields": "Faltam à propriedade \"$1\" detalhes de declaração para o tipo de dados \"$2\" porque não foi definida a propriedade <code>Has fields</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "Faltam à propriedade \"$1\" detalhes de declaração para o tipo de dados porque não foi definida a propriedade <code>External formatter URI</code>.",
+ "smw-property-req-violation-predefined-type": "A propriedade \"$1\", como propriedade predefinida, contém uma declaração de tipo \"$2\" que é incompatível com o tipo de dados padrão desta propriedade.",
+ "smw-property-req-violation-import-type": "Uma declaração de tipo de dados foi detectada, o que é incompatível com o tipo de dados predefinido importado pelo vocabulário \"$1\". Em geral, não é necessário declarar um tipo de dados porque essa informação provém da definição de importação.",
+ "smw-property-req-violation-change-propagation-locked-error": "A propriedade \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A página da propriedade foi trancada até que a atualização da especificação primária esteja finalizada para impedir interrupções intermediárias ou especificações contraditórias. O processo pode demorar algum tempo até que a página possa ser destrancada porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas].",
+ "smw-property-req-violation-change-propagation-locked-warning": "A propriedade \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A atualização pode demorar algum tempo porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas] e é sugerido que as alterações da propriedade sejam adiadas para impedir interrupções intermediárias ou especificações contraditórias.",
+ "smw-property-req-violation-change-propagation-pending": "Estão pendentes atualizações devidas à [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações] ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tarefa estimada|tarefas estimadas}}]) e é recomendado que propriedades não sejam alteradas até que o processo esteja finalizado para impedir interrupções intermediárias ou especificações contraditórias.",
+ "smw-property-req-violation-missing-maps-extension": "O MediaWiki Semântico não conseguiu detetar a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"], que é um pré-requisito para o tipo selecionado, e, em consequência, limita a funcionalidade desta propriedade.",
+ "smw-property-req-violation-type": "A propriedade contém especificações de tipo concorrentes, que podem resultar em anotações de valor inválidas; é, portanto, esperado que um utilizador atribua um tipo adequado.",
+ "smw-change-propagation-protection": "Esta página está trancada para evitar modificações acidentais dos dados durante uma atualização devida à [https://www.semantic-mediawiki.org/wiki/change_propagation propagação de alterações]. O processo pode demorar algum tempo a destrancar a página porque depende do tamanho e da frequência da gestão da [https://www.mediawiki.org/wiki/manual:Job_queue fila de tarefas].",
+ "smw-category-change-propagation-locked-error": "A categoria \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. Entretanto, a página da categoria foi trancada até que a atualização da especificação primária esteja finalizada para impedir interrupções intermediárias ou especificações contraditórias. O processo pode demorar algum tempo até que a página possa ser destrancada porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas].",
+ "smw-category-change-propagation-locked-warning": "A categoria \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A atualização pode demorar algum tempo porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas] e é sugerido que adie alterações à categoria para impedir interrupções intermediárias ou especificações contraditórias.",
+ "smw-category-change-propagation-pending": "Estão pendentes atualizações devidas à [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações] ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tarefa estimada|tarefas estimadas}}]) e é recomendado que aguarde até que o processo esteja finalizado antes de fazer alterações à categoria, para impedir interrupções intermediárias ou especificações contraditórias.",
+ "smw-category-invalid-value-assignment": "\"$1\" não é reconhecido como categoria válida ou anotação de valor.",
+ "protect-level-smw-pageedit": "Permitir somente usuários com a permissão de edição de página (Semantic MediaWiki)",
+ "smw-create-protection": "A criação da propriedade \"$1\" está restrita aos usuários que possuem a permissão \"$2\" (ou que estão em um [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuário] que possua tal permissão), enquanto o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade] estiver ativo.",
+ "smw-create-protection-exists": "A alteração da propriedade \"$1\" está restrita aos usuários com a permissão \"$2\" (ou que estão em um [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuário] que possua tal permissão), enquanto o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade] estiver ativo.",
+ "smw-edit-protection": "Esta página está [[Property:Is edit protected|protegida]] para impedir a modificação acidental de dados e só pode ser editada por usuários com a permissão de edição \"$1\" apropriada ou que esteja em um [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de usuário] com tal permissão.",
+ "smw-edit-protection-disabled": "A proteção contra edições foi desativada, portanto, a propriedade \"$1\" não pode ser usada para proteger páginas de entidades contra edições não autorizadas.",
+ "smw-edit-protection-auto-update": "O Semantic MediaWiki atualizou a situação de proteção de acordo com a propriedade \"Is edit protected\".",
+ "smw-edit-protection-enabled": "Protegida de edição (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Esta página está protegida e só pode ser editada pelos usuários com a [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissão] <code>smw-patternedit</code>.",
+ "smw-property-predefined-edip": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki], para indicar se a página está protegida contra edições ou não.",
+ "smw-property-predefined-long-edip": "Embora qualquer usuário possa adicionar esta propriedade a um sujeito, só um usuário com uma permissão específica pode editar ou revogar a proteção de uma entidade após essa proteção ter sido adicionada.",
+ "smw-query-reference-link-label": "Referência de consulta",
+ "smw-format-datatable-emptytable": "Nenhum dado disponível na tabela",
+ "smw-format-datatable-info": "Exibindo _START_ até _END_ de _TOTAL_ linhas",
+ "smw-format-datatable-infoempty": "Exibindo 0 até 0 de 0 linhas",
+ "smw-format-datatable-infofiltered": "(filtrado a partir de _MAX_ linhas totais)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Exibir _MENU_ linhas",
+ "smw-format-datatable-loadingrecords": "Carregando...",
+ "smw-format-datatable-processing": "Processando…",
+ "smw-format-datatable-search": "Pesquisar:",
+ "smw-format-datatable-zerorecords": "Nenhuma correspondência encontrada",
+ "smw-format-datatable-first": "Primeiro",
+ "smw-format-datatable-last": "Último",
+ "smw-format-datatable-next": "Próximo",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": ativar alfabetação ascendente da tabela",
+ "smw-format-datatable-sortdescending": ": ativar alfabetação descendente da tabela",
+ "smw-format-datatable-toolbar-export": "Exportar",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "A categoria \"$1\" contém um alvo de redirecionamento inválido para um namespace que não é de categoria.",
+ "smw-parser-function-expensive-execution-limit": "A função do analisador sintático atingiu o limite para execuções de funções exigentes (consulte o parâmetro de configuração [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "O MediaWiki Semântico está atualizando a página atual com base na condição de algum processamento pós-consulta necessário.",
+ "apihelp-smwinfo-summary": "Módulo da API para obter informação sobre estatísticas e outra meta informação do Semantic MediaWiki.",
+ "apihelp-ask-summary": "Módulo da API para consultar o Semantic MediaWiki usando a linguagem \"ask\".",
+ "apihelp-askargs-summary": "Módulo da API para consultar o Semantic MediaWiki usando a linguagem \"ask\" na forma de lista de condições, propriedades a serem mostradas e parâmetros.",
+ "apihelp-browsebyproperty-summary": "Módulo da API para obter informação sobre uma propriedade ou lista de propriedades.",
+ "apihelp-browsebysubject-summary": "Módulo da API para obter informação sobre um sujeito.",
+ "apihelp-smwtask-summary": "Módulo da API para executar tarefas relacionadas ao Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Módulo de API para dar suporte a atividades de navegação para diferentes tipos de entidade na Semantic MediaWiki.",
+ "apihelp-ask-parameter-api-version": "Formatação da saída:\n;2:Formato compatível com versões anteriores, usando {} para a lista de resultados.\n;3:Formato experimental, usando [] como lista de resultados.",
+ "smw-api-invalid-parameters": "Parâmetros inválidos, \"$1\"",
+ "smw-parser-recursion-level-exceeded": "O nível de $1 recursões foi excedido durante um processo de análise sintática. É sugerido que valide a estrutura de predefinições, ou que ajuste o parâmetro de configuração <code>$maxRecursionDepth</code> se necessário.",
+ "smw-property-page-list-count": "Exibindo $1 {{PLURAL:$1|página que utiliza|páginas que utilizam}} esta propriedade.",
+ "smw-property-page-list-search-count": "A apresentar {{PLURAL:$1|uma página que usa|$1 páginas que usam}} esta propriedade com uma correspondência de valor \"$2\".",
+ "smw-property-reserved-category": "Categoria",
+ "smw-category": "Categoria",
+ "smw-datavalue-uri-invalid-scheme": " \"$1\" não foi listado como um esquema URI válido.",
+ "smw-datavalue-uri-invalid-authority-path-component": "\"$1\" foi identificado para conter uma autoridade ou um componente de caminho \"$2\" inválido.",
+ "smw-browse-property-group-title": "Grupo de propriedades",
+ "smw-browse-property-group-label": "Nome do grupo de propriedades",
+ "smw-browse-property-group-description": "Descrição do grupo de propriedades",
+ "smw-property-predefined-ppgr": "\"$1\" é uma propriedade predefinida que identifica as entidades (principalmente as categorias) que são utilizadas como instâncias de agrupamento para as propriedades, e é fornecida pela extensão [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-filter": "Filtro",
+ "smw-section-expand": "Expandir a seção",
+ "smw-section-collapse": "Reduzir a seção",
+ "smw-ask-format-help-link": "Formato [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Ajuda",
+ "smw-cheat-sheet": "Auxiliar",
+ "smw-personal-jobqueue-watchlist": "Lista de vigiados (fila de trabalhos)",
+ "smw-property-predefined-label-skey": "Chave de ordenação",
+ "smw-processing": "Processando…",
+ "smw-redirect-target-unresolvable": "O destino é irresolúvel pela razão \"$1\"",
+ "smw-types-title": "Tipo: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "Não é permitido alterar o modelo de conteúdo de uma [https://www.semantic-mediawiki.org/wiki/Help:Schema página de esquema].",
+ "smw-schema-namespace-edit-protection": "Esta página está protegida e só pode ser editada por utilizadores com a [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissão] <code>smw-schemaedit</code> adequada.",
+ "smw-schema-error": "Erro de validação",
+ "smw-schema-error-schema": "A especificação '''$1''' e a sua validação para o esquema corrente identificou as seguintes incompatibilidades ou violações:",
+ "smw-schema-error-violation": "Violação (\"$1\", \"$2\")",
+ "smw-schema-error-type-missing": "Falta um tipo ao conteúdo para este ser reconhecido e utilizável no [https://www.semantic-mediawiki.org/wiki/Help:Schema espaço nominal/domínio do esquema].",
+ "smw-schema-error-type-unknown": "O tipo \"$1\" não está registado e, portanto, não pode ser usado para conteúdo no [https://www.semantic-mediawiki.org/wiki/Help:Schema espaço nominal/domínio do esquema].",
+ "smw-schema-title": "Esquema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Tipo",
+ "smw-schema-description-link-format-schema": "É esperado que este tipo de esquema defina características para criar hiperligações dependentes do contexto com relação a uma propriedade atribuída de [[Property:Formatter schema|esquema formatador]].",
+ "smw-schema-description-search-form-schema": "É esperado que este tipo de esquema seja usado para definir formulários de entrada, e características para o perfil de [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch pesquisa avançada], onde este contém instruções para gerar campos de introdução de dados, definir espaços nominais /domínios padrão, ou declarar expressões prefixo para um pedido de pesquisa.",
+ "smw-schema-tag": "{{PLURAL:$1|Etiqueta|Etiquetas}}",
+ "smw-property-predefined-schema-desc": "\"$1\" é uma propriedade predefinida que armazena uma descrição de esquema, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-schema-def": "\"$1\" é uma propriedade predefinida que que armazena o conteúdo do esquema, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-schema-tag": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para identificar um conjunto de esquemas.",
+ "smw-property-predefined-long-schema-tag": "Uma etiqueta que identifica esquemas de conteúdos ou características semelhantes.",
+ "smw-property-predefined-schema-type": "\"$1\" é uma propriedade predefinida que descreve um tipo de esquema identificável, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-schema-type": "É esperado que cada [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type tipo] forneça a sua própria interpretação dos elementos sintáticos e das restrições sintáticas, e os expresse através de um [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation modelo de validação].",
+ "smw-ask-title-keyword-type": "Procurar palavra-chave",
+ "smw-ask-message-keyword-type": "Esta pesquisa coincide com a condição <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Não foi possível conectar ao alvo remoto \"$1\".",
+ "smw-remote-source-disabled": "A fonte \"$1\" desabilitou o suporte às solicitações remotas!",
+ "smw-remote-source-unmatched-id": "A fonte '''$1''' não corresponde a uma versão do Semantic MediaWiki que suporte uma solicitação remota.",
+ "smw-remote-request-note": "O resultado é buscado a partir da fonte remota '''$1''' e é provável que o conteúdo gerado contenha informações que não estão disponíveis no wiki atual.",
+ "smw-remote-request-note-cached": "O resultado é '''armazenado em cache''' a partir da fonte remota '''$1''' e é provável que o conteúdo gerado contenha informações que não estão disponíveis no wiki atual.",
+ "smw-parameter-missing": "O parâmetro \"$1\" está ausente.",
+ "smw-property-tab-usage": "Uso",
+ "smw-property-tab-redirects": "Sinônimos",
+ "smw-property-tab-subproperties": "Subpropriedades",
+ "smw-property-tab-errors": "Atribuições impróprias",
+ "smw-property-tab-specification": "... mais",
+ "smw-concept-tab-list": "Lista",
+ "smw-concept-tab-errors": "Erros",
+ "smw-ask-tab-result": "Resultado",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Depurar",
+ "smw-ask-tab-code": "Código",
+ "smw-install-incomplete-intro": "A instalação (ou atualização) de <b>Semantic MediaWiki</b> não foi finalizado e um administrador deve executar as seguintes tarefas para evitar inconsistências de dados antes que os usuários continuem a criar ou alterar o conteúdo.",
+ "smw-install-incomplete-populate-hash-field": "O passo de preenchimento do campo <code>smw_hash</code> foi pulado durante a configuração; é necessário executar o ''script'' [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php].",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/pt.json b/www/wiki/extensions/SemanticMediaWiki/i18n/pt.json
new file mode 100644
index 00000000..35774a3e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/pt.json
@@ -0,0 +1,857 @@
+{
+ "@metadata": {
+ "authors": [
+ "Crazymadlover",
+ "Hamilton Abreu",
+ "Imperadeiro98",
+ "Lijealso",
+ "Luckas",
+ "Luckas Blade",
+ "Malafaya",
+ "SandroHc",
+ "Waldir",
+ "ì•„ë¼",
+ "555",
+ "Vitorvicentevalente",
+ "Jkb8",
+ "Diniscoelho",
+ "Fúlvio",
+ "Fitoschido",
+ "Ngl2016",
+ "Jaideraf",
+ "Nemo bis",
+ "MokaAkashiyaPT",
+ "Eduardo Addad de Oliveira",
+ "Athena in Wonderland",
+ "Mansil alfalb"
+ ]
+ },
+ "smw-desc": "Tornar a sua wiki mais inteligível - para máquinas ''e'' seres humanos ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentação ''online''])",
+ "smw-title": "MediaWiki Semântico",
+ "smw-upgrade-error": "O MediaWiki Semântico foi instalado e ativado, mas tem em falta uma [https://www.semantic-mediawiki.org/wiki/Help:Upgrade chave de atualização] apropriada que corresponda a: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Porque estou a ver este erro?",
+ "smw-upgrade-error-why-explain": "A estrutura interna da base de dados do MediaWiki Semântico mudou e necessita de alguns ajustamentos para ficar completamente funcional. Pode haver vários motivos para a mudança, incluindo: \n* Foram adicionadas propriedades fixas (requer configuração adicional de tabelas).\n* Uma atualização contém algumas mudanças nas tabelas ou índices, tornando obrigatória uma interceção antes de se aceder aos dados.",
+ "smw-upgrade-error-how-title": "Como corrijo este erro?",
+ "smw-upgrade-error-how-explain": "Um administrador (ou outra pessoa com privilégios de administrador) tem de executar o ficheiro de comandos de manutenção [https://www.mediawiki.org/wiki/Manual:Update.php update.php] do MediaWiki ou o [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] do MediaWiki Semântico. Para obter mais ajuda, pode também consultar as seguintes páginas:\n* Instruções de [https://www.semantic-mediawiki.org/wiki/Help:Installation instalação].\n* Página de ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting resolução de problemas].",
+ "smw-semantics-not-enabled": "A funcionalidade MediaWiki Semântico não foi ativada nesta wiki.",
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": "e",
+ "smw-factbox-head": "... mais sobre \"$1\"",
+ "smw-factbox-facts": "Factos",
+ "smw-factbox-facts-help": "Mostra as declarações e factos que foram criados por um utilizador",
+ "smw-factbox-facts-derived": "Factos derivados",
+ "smw-factbox-facts-derived-help": "Mostra os factos que foram derivados das regras ou com a ajuda de outras técnicas de raciocínio",
+ "smw_isspecprop": "Esta é uma propriedade especial nesta wiki.",
+ "smw-concept-cache-header": "Utilização da cache",
+ "smw-concept-cache-count": "A [https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count cache de conceitos] contém {{PLURAL:$1|'''uma''' entidade|'''$1''' entidades}} ($2).",
+ "smw-concept-no-cache": "Não há nenhuma cache disponível.",
+ "smw_concept_description": "Descrição do conceito \"$1\"",
+ "smw_no_concept_namespace": "Os conceitos só podem ser definidos em páginas do espaço nominal Concept:.",
+ "smw_multiple_concepts": "Cada página de conceito só pode conter a definição de um conceito.",
+ "smw_concept_cache_miss": "O conceito \"$1\" não pode ser usado neste momento, porque a configuração da wiki requer que ele seja calculado ''off-line''. Caso o problema não seja resolvido dentro de algum tempo, peça a um administrador da wiki que disponibilize este conceito.",
+ "smw_noinvannot": "Não podem ser atribuídos valores a propriedades inversas.",
+ "version-semantic": "Extensões semânticas",
+ "smw_baduri": "Não são permitidos identificadores URI na forma \"$1\".",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Contar resultados",
+ "smw_printername_csv": "Exportação para CSV",
+ "smw_printername_dsv": "Exportação para DSV",
+ "smw_printername_debug": "Depurar a consulta (para peritos)",
+ "smw_printername_embedded": "Incorporar o conteúdo da página",
+ "smw_printername_json": "Exportação para JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Lista simples",
+ "smw_printername_ol": "Enumeração",
+ "smw_printername_ul": "Discriminação",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Tabela ampla",
+ "smw_printername_template": "Predefinição",
+ "smw_printername_templatefile": "Ficheiro da predefinição",
+ "smw_printername_rdf": "Exportação para RDF",
+ "smw_printername_category": "Categoria",
+ "validator-type-class-SMWParamSource": "texto",
+ "smw-paramdesc-limit": "O número máximo de resultados a serem devolvidos",
+ "smw-paramdesc-offset": "A posição do primeiro resultado",
+ "smw-paramdesc-headers": "Apresentar os nomes dos cabeçalhos e propriedades",
+ "smw-paramdesc-mainlabel": "A etiqueta a atribuir ao nome da página principal",
+ "smw-paramdesc-link": "Apresentar os valores na forma de hiperligações",
+ "smw-paramdesc-intro": "O texto a apresentar antes dos resultados da consulta, caso existam",
+ "smw-paramdesc-outro": "O texto a apresentar após os resultados da consulta, caso existam",
+ "smw-paramdesc-default": "O texto a apresentar se a consulta não produzir resultados",
+ "smw-paramdesc-sep": "O separador de resultados",
+ "smw-paramdesc-propsep": "O separador entre as propriedades da entrada de um resultado",
+ "smw-paramdesc-valuesep": "O separador entre os valores para uma propriedade de um resultado",
+ "smw-paramdesc-showsep": "Mostrar separador no topo do ficheiro CSV (\"set=<value>\")",
+ "smw-paramdesc-distribution": "Em vez de apresentar todos os valores, contar as respetivas ocorrências e apresentá-las.",
+ "smw-paramdesc-distributionsort": "Ordenar a distribuição de valores pela contagem de ocorrências.",
+ "smw-paramdesc-distributionlimit": "Limitar a distribuição de valores à contagem de ocorrência de apenas alguns valores.",
+ "smw-paramdesc-aggregation": "Especifique com que é que a agregação se deve relacionar",
+ "smw-paramdesc-template": "O nome da predefinição com a qual são apresentadas as impressões",
+ "smw-paramdesc-columns": "O número de colunas em que os resultados serão apresentados",
+ "smw-paramdesc-userparam": "Um valor fornecido a cada chamada da predefinição, se for usada uma predefinição",
+ "smw-paramdesc-class": "Uma classe CSS adicional a ser definida para a lista",
+ "smw-paramdesc-introtemplate": "O nome de uma predefinição para apresentar antes dos resultados da consulta, se existirem",
+ "smw-paramdesc-outrotemplate": "O nome de uma predefinição para apresentar após os resultados da consulta, se existirem",
+ "smw-paramdesc-embedformat": "O elemento HTML usado para definir cabeçalhos",
+ "smw-paramdesc-embedonly": "Não mostrar cabeçalhos",
+ "smw-paramdesc-table-class": "Uma classe CSS adicional a definir para a tabela",
+ "smw-paramdesc-table-transpose": "Apresentar os cabeçalhos das tabelas na vertical e os resultados na horizontal",
+ "smw-paramdesc-rdfsyntax": "A sintaxe RDF a utilizar",
+ "smw-paramdesc-csv-sep": "Especifica um separador de colunas",
+ "smw-paramdesc-csv-valuesep": "Especifica um separador de valores",
+ "smw-paramdesc-csv-merge": "Fundir os valores de linhas e colunas que têm um identificador de assunto idêntico (aliás, primeira coluna)",
+ "smw-paramdesc-csv-bom": "Adicionar um BOM (carácter para indicar a \"endianness\") no topo do ficheiro de saída",
+ "smw-paramdesc-dsv-separator": "O separador a usar",
+ "smw-paramdesc-dsv-filename": "O nome para o ficheiro DSV",
+ "smw-paramdesc-filename": "O nome para o ficheiro de saída",
+ "smw-smwdoc-description": "Apresenta uma tabela de todos os parâmetros que podem ser usados para o formato de resultados especificado, com os respetivos valores por omissão e descrições",
+ "smw-smwdoc-default-no-parameter-list": "Este formato de resultado não fornece parâmetros específicos do formato.",
+ "smw-smwdoc-par-format": "O formato de resultados para o qual será apresentada a documentação dos parâmetros.",
+ "smw-smwdoc-par-parameters": "Os parâmetros a serem apresentados. \"specific\" (específicos) para aqueles adicionados pelo formato, \"base\" para aqueles disponíveis em todos os formatos, e \"all\" (todos) para ambos.",
+ "smw-paramdesc-sort": "A propriedade pela qual a consulta será ordenada",
+ "smw-paramdesc-order": "O sentido de ordenação da consulta",
+ "smw-paramdesc-searchlabel": "Texto para continuação da pesquisa",
+ "smw-paramdesc-named_args": "Nomear os argumentos passados à predefinição",
+ "smw-paramdesc-template-arguments": "Define a forma como os argumentos nomeados são passados à predefinição",
+ "smw-paramdesc-import-annotation": "Serão copiados dados anotados adicionais durante a análise sintática de um assunto",
+ "smw-paramdesc-export": "Opção de exportação",
+ "smw-paramdesc-prettyprint": "Um resultado com realce sintático que apresenta indentações e novas linhas adicionais",
+ "smw-paramdesc-json-unescape": "O resultado irá conter barras sem caracteres de escape, e caracteres Unicode multibytes",
+ "smw-paramdesc-json-type": "Tipo de seriação",
+ "smw-paramdesc-source": "Fonte alternativa de consulta",
+ "smw-paramdesc-jsonsyntax": "Sintaxe JSON a ser utilizada",
+ "smw-printername-feed": "Feed RSS e Atom",
+ "smw-paramdesc-feedtype": "Tipo de ''feed''",
+ "smw-paramdesc-feedtitle": "O texto a ser usado como título de ''feed''",
+ "smw-paramdesc-feeddescription": "O texto a ser usado como descrição do ''feed''",
+ "smw-paramdesc-feedpagecontent": "O conteúdo da página a ser apresentado com o ''feed''",
+ "smw-label-feed-description": "''Feed'' $2 $1",
+ "smw-paramdesc-mimetype": "O tipo de multimédia (tipo MIME) para o ficheiro de saída.",
+ "smw_iq_disabled": "As consultas semânticas foram impossibilitadas nesta wiki.",
+ "smw_iq_moreresults": "… mais resultados",
+ "smw_parseerror": "O valor fornecido não foi compreendido.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "&nbsp;",
+ "smw_notitle": "“$1†não pode ser usado como nome de uma página nesta wiki.",
+ "smw_noproperty": "“$1†não pode ser usado como nome de uma propriedade nesta wiki.",
+ "smw_wrong_namespace": "Só são permitidas aqui páginas do espaço nominal \"$1\".",
+ "smw_manytypes": "Foi definido mais de um tipo para a propriedade.",
+ "smw_emptystring": "Não são aceites textos vazios.",
+ "smw_notinenum": "\"$1\" não está na lista ($2) dos [[Property:Allows value|valores permitidos]] para a propriedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" não está na lista ($2) de [[Property:Allows value|valores permitidos]] para a propriedade \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" não está no intervalo de \"$2\" especificado pela restrição [[Property:Allows value|permite valor]] para a propriedade \"$3\".",
+ "smw_noboolean": "\"$1\" não é reconhecido como um valor booliano (verdadeiro/falso).",
+ "smw_true_words": "verdadeiro,v,sim,s",
+ "smw_false_words": "falso,f,não,nao,n",
+ "smw_nofloat": "“$1†não é um número.",
+ "smw_infinite": "Números da dimensão de “$1†não são suportados.",
+ "smw_unitnotallowed": "\"$1\" não está declarada como unidade de medida válida para esta propriedade.",
+ "smw_nounitsdeclared": "Não foi declarada nenhuma unidade de medida para esta propriedade.",
+ "smw_novalues": "Não foram especificados valores.",
+ "smw_nodatetime": "A data “$1†não foi compreendida.",
+ "smw_toomanyclosing": "Parece haver demasiadas ocorrências de \"$1\" na consulta.",
+ "smw_noclosingbrackets": "Um uso de \"<nowiki>[[</nowiki>\" na sua consulta não foi fechado com o \"]]\" correspondente.",
+ "smw_misplacedsymbol": "O símbolo “$1†foi usado num sítio onde não tem utilidade.",
+ "smw_unexpectedpart": "A parte \"$1\" da consulta não foi compreendida.\nOs resultados poderão não ser os esperados.",
+ "smw_emptysubquery": "Há uma subconsulta que não tem nenhuma condição válida.",
+ "smw_misplacedsubquery": "Foi usada uma subconsulta num local onde não são permitidas subconsultas.",
+ "smw_valuesubquery": "Não são suportadas subconsultas para valores da propriedade \"$1\".",
+ "smw_badqueryatom": "Uma parte \"<nowiki>[[…]]</nowiki>\" da consulta não foi compreendida.",
+ "smw_propvalueproblem": "O valor da propriedade “$1†não foi compreendido.",
+ "smw_noqueryfeature": "Uma característica da consulta não é suportada nesta wiki e parte da consulta foi descartada ($1).",
+ "smw_noconjunctions": "Conjunções em consultas não são suportadas nesta wiki e parte da consulta foi descartada ($1).",
+ "smw_nodisjunctions": "Disjunções em consultas não são suportadas nesta wiki e parte da consulta foi descartada ($1).",
+ "smw_querytoolarge": "Não foi possível considerar {{PLURAL:$2|a seguinte condição|as seguintes $2 condições}} da consulta (<code>$1</code>), devido às restrições da wiki para o tamanho e profundidade das consultas.",
+ "smw_notemplategiven": "Para este formato de consulta funcionar, forneça um valor para o parâmetro \"template\".",
+ "smw_db_sparqlqueryproblem": "Não foi possível obter da base de dados SPARQL o resultado da consulta. Este erro pode ser temporário ou indicativo de um defeito no ''software'' da base de dados.",
+ "smw_db_sparqlqueryincomplete": "A consulta revelou-se difícil e foi interrompida. Podem faltar alguns resultados. Se possível, tente usar uma consulta mais simples, por favor.",
+ "smw_type_header": "Propriedades do tipo “$1â€",
+ "smw_typearticlecount": "A apresentar {{PLURAL:$1|uma propriedade que usa|$1 propriedades que usam}} este tipo.",
+ "smw_attribute_header": "Páginas que usam a propriedade “$1â€",
+ "smw_attributearticlecount": "A apresentar {{PLURAL:$1|uma página que usa|$1 páginas que usam}} esta propriedade.",
+ "smw-propertylist-subproperty-header": "Subpropriedades",
+ "smw-propertylist-redirect-header": "Sinónimos",
+ "smw-propertylist-error-header": "Páginas com atribuições indevidas",
+ "smw-propertylist-count": "A mostrar $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}}.",
+ "smw-propertylist-count-with-restricted-note": "A mostrar $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}} (há mais disponíveis mas a apresentação está limitada a \"$2\").",
+ "smw-propertylist-count-more-available": "A mostrar $1 {{PLURAL:$1|entidade relacionada|entidades relacionadas}} (há mais disponíveis).",
+ "exportrdf": "Exportar páginas para RDF",
+ "smw_exportrdf_docu": "Esta página permite-lhe obter dados de uma página com o formato RDF.\nPara exportar páginas, introduza os seus títulos na caixa de texto abaixo, um título por linha.",
+ "smw_exportrdf_recursive": "Exportar recursivamente todas as páginas relacionadas.\nNote que o resultado pode ser volumoso!",
+ "smw_exportrdf_backlinks": "Exportar também todas as páginas que referem as páginas exportadas.\nGera um RDF navegável.",
+ "smw_exportrdf_lastdate": "Não exportar páginas que não tenham sido alteradas desde o ponto no tempo especificado.",
+ "smw_exportrdf_submit": "Exportar",
+ "uriresolver": "Resolvedor de identificadores URI",
+ "properties": "Propriedades",
+ "smw_properties_docu": "As seguintes propriedades são usadas nesta wiki.",
+ "smw_property_template": "$1 de tipo $2 ($3 {{PLURAL:$3|utilização|utilizações}})",
+ "smw_propertylackspage": "Todas as propriedades devem ser descritas por uma página!",
+ "smw_propertylackstype": "Não foi especificado nenhum tipo para esta propriedade (a presumir o tipo $1 por agora).",
+ "smw_propertyhardlyused": "Esta propriedade é muito pouco usada nesta wiki!",
+ "smw-property-name-invalid": "A propriedade $1 não pode ser usada (nome inválido).",
+ "smw-property-name-reserved": "\"$1\" estava listado como sendo um nome reservado e não deve ser usado como propriedade. Talvez a seguinte [https://www.semantic-mediawiki.org/wiki/Help:Property_naming página de ajuda] contenha informação sobre a razão pela qual o nome estava reservado.",
+ "smw-sp-property-searchform": "Apresentar as propriedades que contêm:",
+ "smw-sp-property-searchform-inputinfo": "O texto de entrada é sensível a maiúsculas e minúsculas e, quando é usado para a filtragem, só são apresentadas as propriedades que correspondem à condição.",
+ "smw-special-property-searchform": "Apresentar as propriedades que contêm:",
+ "smw-special-property-searchform-inputinfo": "O texto de entrada é sensível a maiúsculas e minúsculas e, quando é usado para a filtragem, só são apresentadas as propriedades que correspondem à condição.",
+ "smw-special-property-searchform-options": "Opções",
+ "smw-special-wantedproperties-filter-label": "Filtro:",
+ "smw-special-wantedproperties-filter-none": "Nenhum",
+ "smw-special-wantedproperties-filter-unapproved": "não aprovadas",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Opção de filtro usada em relação com o modo de autoridade.",
+ "concepts": "Conceitos",
+ "smw-special-concept-docu": "Um [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceito] pode ser considerado uma \"categoria dinâmica\"; por exemplo, um conjunto de páginas que não são criadas manualmente, mas sim calculadas pelo MediaWiki Semântico a partir da descrição de uma determinada consulta.",
+ "smw-special-concept-header": "Lista de conceitos",
+ "smw-special-concept-count": "{{PLURAL:$1|O seguinte conceito está a ser incluído|Os $1 conceitos seguintes estão a ser incluídos}} na lista.",
+ "smw-special-concept-empty": "Não foi encontrado nenhum conceito.",
+ "unusedproperties": "Propriedades não usadas",
+ "smw-unusedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Unused_properties propriedades não utilizadas] que foram declaradas, embora nenhuma outra página as utilize. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:WantedProperties|propriedades em falta]].",
+ "smw-unusedproperty-template": "$1 do tipo $2",
+ "wantedproperties": "Propriedades em falta",
+ "smw-wantedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propriedades em falta] que são utilizadas na wiki, mas que não têm uma página a descrevê-las. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:UnusedProperties|propriedades não utilizadas]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw-special-wantedproperties-docu": "Esta página lista [https://www.semantic-mediawiki.org/wiki/Wanted_properties propriedades em falta] que são utilizadas na wiki, mas que não têm uma página a descrevê-las. Para uma visão diferenciada, consulte as páginas especiais com [[Special:Properties|todas as propriedades]] ou com as [[Special:UnusedProperties|propriedades não utilizadas]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|uso|usos}})",
+ "smw_purge": "Atualizar",
+ "smw-purge-failed": "A atualização falhou",
+ "types": "Tipos",
+ "smw_types_docu": "Lista dos [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tipos de dados disponíveis] em que cada [https://www.semantic-mediawiki.org/wiki/Help:Datatype tipo] representa um conjunto único de atributos, para descrever um valor em termos das características de armazenamento e apresentação que são hereditárias para uma propriedade atribuída.",
+ "smw-special-types-no-such-type": "\"$1\" é desconhecido ou não foi especificado como tipo de dados válido.",
+ "smw-statistics": "Estatísticas de semântica",
+ "smw-statistics-property-instance": "{{PLURAL:$1|Valor|valores}} de propriedade (total)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Propriedade|Propriedades}}]] (total)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Propriedade|Propriedades}} (total)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Propriedade|Propriedades}}]] (utilizadas com pelo menos um valor)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Propriedade|Propriedades}} ({{PLURAL:$1|registada|registadas}} com uma página)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Propriedade|Propriedades}} ({{PLURAL:$1|atribuída|atribuídas}} a um tipo de dados)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Consulta|Consultas}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Consulta|Consultas}}]]",
+ "smw-statistics-query-size": "Tamanho da consulta",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Conceito|Conceitos}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Conceito|Conceitos}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Subobjeto|Subobjetos}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Subobjeto|Subobjetos}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Tipo de dados|Tipos de dados}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Valor de propriedade|Valores de propriedades}} ([[Special:ProcessingErrorList|{{PLURAL:$1|anotação incorreta|anotações incorretas}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Valor de propriedade|Valores de propriedades}} ({{PLURAL:$1|anotação incorreta|anotações incorretas}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Entidade desatualizada|Entidades desatualizadas}} ({{PLURAL:$1|marcada|marcadas}} para remoção)",
+ "smw_uri_doc": "O resolvedor de endereços URI implementa a [$1 descoberta TAG da W3C sobre o httpRange-14].\nCertifica-se de que os seres humanos não se tornem em sítios da Internet.",
+ "ask": "Pesquisa semântica",
+ "smw-ask-help": "Esta secção contém algumas hiperligações que explicam como usar a sintaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecionar páginas] descreve como selecionar páginas e construir condições\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de pesquisa] lista os operadores de pesquisa disponíveis, incluindo os para consultas de intervalos e consultas com caracteres de substituição\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Apresentar informação] descreve o uso de instruções de impressão e de opções de formatação",
+ "smw_ask_sortby": "Ordenar por coluna (opcional)",
+ "smw_ask_ascorder": "Ascendente",
+ "smw_ask_descorder": "Descendente",
+ "smw-ask-order-rand": "Aleatório",
+ "smw_ask_submit": "Procurar resultados",
+ "smw_ask_editquery": "Editar consulta",
+ "smw_add_sortcondition": "[Adicionar condição de ordenação]",
+ "smw-ask-sort-add-action": "Adicionar condição de ordenação",
+ "smw_ask_hidequery": "Esconder consulta (vista compacta)",
+ "smw_ask_help": "Ajuda sobre consultas",
+ "smw_ask_queryhead": "Condição",
+ "smw_ask_printhead": "Seleção de dados adicionais para apresentar",
+ "smw_ask_printdesc": "(adicionar um nome de propriedade por linha)",
+ "smw_ask_format_as": "Formatar como:",
+ "smw_ask_defaultformat": "padrão",
+ "smw_ask_otheroptions": "Outras opções",
+ "smw-ask-otheroptions-info": "Esta secção contém opções que alteram as instruções de apresentação. Pode ver as descrições dos parâmetros colocando o ponteiro do rato sobre eles.",
+ "smw-ask-otheroptions-collapsed-info": "Por favor, use o sinal de mais para ver todas as opções disponíveis",
+ "smw_ask_show_embed": "Mostrar código para incorporação",
+ "smw_ask_hide_embed": "Ocultar código de incorporação",
+ "smw_ask_embed_instr": "Para incorporar esta consulta numa página da wiki, use o código abaixo.",
+ "smw-ask-delete": "Remover",
+ "smw-ask-sorting": "Ordenação",
+ "smw-ask-options": "Opções",
+ "smw-ask-options-sort": "Opções de ordenação",
+ "smw-ask-format-options": "Formato e opções",
+ "smw-ask-parameters": "Parâmetros",
+ "smw-ask-search": "Pesquisa",
+ "smw-ask-debug": "Debug",
+ "smw-ask-debug-desc": "Gera informação para despistagem de erros das consultas",
+ "smw-ask-no-cache": "Desativar cache de consultas",
+ "smw-ask-no-cache-desc": "Resultados sem cache de consulta",
+ "smw-ask-result": "Resultado",
+ "smw-ask-empty": "Limpar todas as entradas",
+ "smw-ask-download-link-desc": "Descarregar os resultados da consulta no formato $1",
+ "smw-ask-format": "Formato",
+ "smw-ask-format-selection-help": "Ajude com o formato selecionado: $1",
+ "smw-ask-condition-change-info": "A condição foi alterada e o motor de pesquisa requer que a consulta volte a ser executada para produzir resultados que correspondem aos novos requisitos.",
+ "smw-ask-input-assistance": "Assistência de preenchimento",
+ "smw-ask-condition-input-assistance": "É fornecida [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ajuda de introdução] nos campos de impressão, ordenação e condição. O campo condição requer o uso de um dos seguintes prefixos:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> para obter as sugestões de propriedades (p. ex.: <code>[[p:Tem ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> para obter as sugestões de categorias",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> para obter as sugestões de conceitos",
+ "smw-ask-format-change-info": "O formato foi modificado e a consulta tem de voltar a ser executada para produzir resultados que correspondam aos novos parâmetros e opções de visualização.",
+ "smw-ask-format-export-info": "O formato selecionado é um formato de exportação que não tem representação visual, portanto os resultados só são fornecidos como download.",
+ "smw-ask-query-search-info": "A consulta <code><nowiki>$1</nowiki></code> foi realizada pelo servidor {{PLURAL:$3|1=<code>$2</code> (da cache)|<code>$2</code> (da cache)|<code>$2</code>}} em $4 {{PLURAL:$4|segundo|segundos}}.",
+ "searchbyproperty": "Pesquisa por propriedade",
+ "processingerrorlist": "Lista de erros de processamento",
+ "propertylabelsimilarity": "Relatório de semelhanças das etiquetas de propriedade",
+ "smw-processingerrorlist-intro": "A seguinte lista dá uma perspetiva geral das falhas de processamento relacionadas com o [https://www.semantic-mediawiki.org/ MediaWiki Semântico]. Recomenda-se monitorizar esta lista regularmente e corrigir as anotações de valores inválidos.",
+ "smw_sbv_docu": "Procurar todas as páginas que têm uma determinada propriedade e valor.",
+ "smw_sbv_novalue": "Introduza um valor válido para a propriedade, ou veja todos os valores válidos da propriedade “$1â€.",
+ "smw_sbv_displayresult": "Segue-se uma lista de todas as páginas que têm a propriedade “$1†com o valor “$2â€",
+ "smw_sbv_displayresultfuzzy": "Uma lista de todas as páginas que têm a propriedade \"$1\" com o valor \"$2\".\nComo houve poucos resultados, também são apresentados valores próximos.",
+ "smw_sbv_property": "Propriedade:",
+ "smw_sbv_value": "Valor:",
+ "smw_sbv_submit": "Procurar resultados",
+ "browse": "Navegar pela wiki",
+ "smw_browselink": "Navegar propriedades",
+ "smw_browse_article": "Introduza o nome da página a partir da qual deseja começar a navegar.",
+ "smw_browse_go": "Prosseguir",
+ "smw_browse_show_incoming": "Mostrar propriedades afluentes",
+ "smw_browse_hide_incoming": "Ocultar propriedades afluentes",
+ "smw_browse_no_outgoing": "Esta página não tem propriedades.",
+ "smw_browse_no_incoming": "Nenhuma das propriedades aponta para esta página.",
+ "smw-browse-from-backend": "Neste momento está a ser obtida informação do servidor.",
+ "smw-browse-intro": "Esta página fornece detalhes sobre um assunto ou uma instância de uma entidade. Introduza o nome de um objeto a ser inspecionado, por favor.",
+ "smw-browse-invalid-subject": "A validação do assunto terminou com um erro \"$1\".",
+ "smw-browse-api-subject-serialization-invalid": "O assunto tem um formato de seriação inválido.",
+ "smw-browse-js-disabled": "É provável que o JavaScript esteja desativado ou indisponível, e recomendamos que utilize um ''browser'' que o suporte. Talvez encontre outras opções na página de configuração do parâmetro [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Mostrar grupos",
+ "smw-browse-hide-group": "Ocultar grupos",
+ "smw-noscript": "Esta página ou ação requer o Javascript para funcionar. Ative o Javascript no seu browser ou utilize um browser que o suporte, para que esta funcionalidade possa ser fornecida como pedido. Para mais informações, consulte a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript].",
+ "smw_inverse_label_default": "$1 de",
+ "smw_inverse_label_property": "Etiqueta da propriedade inversa",
+ "pageproperty": "Pesquisa das propriedades das páginas",
+ "smw_pp_docu": "Introduza uma página e propriedade, ou só uma propriedade, para obter todos os valores atribuídos.",
+ "smw_pp_from": "Da página:",
+ "smw_pp_type": "Propriedade:",
+ "smw_pp_submit": "Procurar resultados",
+ "smw_result_prev": "Anteriores",
+ "smw_result_next": "Seguintes",
+ "smw_result_results": "Resultados",
+ "smw_result_noresults": "Não há resultados.",
+ "smwadmin": "Funções de administração e manutenção",
+ "smw-admin-statistics-job-title": "Estatísticas de tarefas",
+ "smw-admin-statistics-job-docu": "As estatísticas de tarefas apresentam informação sobre tarefas agendadas do MediaWiki Semântico que ainda não foram executadas. O número de tarefas pode ter ligeiras imprecisões ou conter tentativas falhadas; consulte o [https://www.mediawiki.org/wiki/Manual:Job_queue manual] para mais informações.",
+ "smw-admin-statistics-querycache-title": "Estatísticas da cache de consultas",
+ "smw-admin-statistics-querycache-disabled": "A [https://www.semantic-mediawiki.org/wiki/QueryCache ''cache'' de consultas] não foi ativada nesta wiki, portanto não há estatísticas disponíveis.",
+ "smw-admin-statistics-querycache-explain": "As estatísticas da cache irão conter dados acumulados provisórios e derivados, incluindo:\n* \"misses\" (falhas) o número total de tentativas de obter dados da cache com respostas inatingíveis, forçando a obtenção direta do repositório (base de dados, repositório de triplas, etc.)\n* \"deletes\" (eliminações) o número total de operações de despejo da cache (por dependências de purga ou de consulta)\n* \"hits\" (acertos) contém o número de obtenções de dados da cache, tendo por fonte quer consultas incorporadas (consultas feitas a partir de uma página da wiki) quer não incorporadas (pedidas por páginas como Special:Ask ou pela API, se permitido)\n* \"medianRetrievalResponseTime\" (mediana do tempo de resposta) um valor indicativo da mediana do tempo de resposta (em segundos) para pedidos de obtenção servidos, ou não, pela cache durante o período de execução do processo de recolha de dados\n* \"noCache\" (sem cache) indica a quantidade de pedidos sem tentativa de obter dados da cache (consultas com limite=0, opção 'sem cache', etc.)",
+ "smw-admin-permission-missing": "O acesso a esta página foi bloqueado devido à falta de permissões. Consulte a página de ajuda sobre [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissões] para detalhes sobre as definições necessárias, por favor.",
+ "smw-admin-setupsuccess": "O motor de armazenamento foi configurado.",
+ "smw_smwadmin_return": "Voltar a $1",
+ "smw_smwadmin_updatestarted": "Foi iniciado um novo processo de atualização para recarregar os dados semânticos.\nTodos os dados armazenados serão reconstruidos ou reparados, conforme for necessário.\nPode seguir o progresso da atualização nesta página especial.",
+ "smw_smwadmin_updatenotstarted": "Já existe um processo de atualização em curso.\nNão foi criado outro.",
+ "smw_smwadmin_updatestopped": "Todos os processos de atualização existentes foram parados.",
+ "smw_smwadmin_updatenotstopped": "Para parar o processo de atualização em curso, deve marcar a caixa de seleção para indicar que tem realmente a certeza.",
+ "smw-admin-docu": "Esta página especial auxilia-o durante a instalação, atualização, manutenção e utilização do <a href=\"https://www.semantic-mediawiki.org\">MediaWiki Semântico</a> e fornece outras funções e tarefas administrativas, assim como estatísticas.\nLembre-se de efetuar cópias de segurança dos dados importantes antes de executar funções administrativas.",
+ "smw-admin-environment": "Ambiente de ''software''",
+ "smw-admin-db": "Configuração da base de dados",
+ "smw-admin-db-preparation": "A inicialização da tabela está em curso e pode demorar algum tempo até os resultados serem apresentados, dependendo do tamanho da tabela e de possíveis otimizações da mesma.",
+ "smw-admin-dbdocu": "O MediaWiki Semântico requer uma estrutura própria de dados (que é independente do MediaWiki e, portanto, não afeta o resto da instalação do MediaWiki), para armazenar os dados semânticos.\nEsta função de preparação pode ser executada várias vezes sem causar quaisquer danos, mas é necessária apenas uma vez, na instalação ou durante uma atualização.",
+ "smw-admin-permissionswarn": "Se a operação falhar com erros de SQL, é provável que o utilizador da base de dados usado pela sua wiki (consulte o seu ficheiro \"LocalSettings.php\") não tenha as permissões necessárias.\nConceda a este utilizador permissões adicionais para criar e eliminar tabelas, introduza temporariamente as credenciais do seu super-utilizador (''root'') da base de dados no ficheiro \"LocalSettings.php\", ou use o ''script'' de manutenção <code>setupStore.php</code>, o qual pode usar as credenciais de um administrador.",
+ "smw-admin-dbbutton": "Inicializar ou atualizar tabelas",
+ "smw-admin-announce": "Anuncie a sua wiki",
+ "smw-admin-announce-text": "Se a sua wiki é pública, pode registá-la na <a href=\"https://wikiapiary.com\">Wiki Apiário</a>, a wiki que regista wikis.",
+ "smw-admin-deprecation-notice-title": "Avisos de descontinuação",
+ "smw-admin-deprecation-notice-docu": "A seguinte secção contém definições que já foram descontinuadas ou removidas mas que se detetou estarem ativadas nesta wiki. É esperado que qualquer atualização futura deixe de suportar estas configurações.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi descontinuado e será removido na versão $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> irá remover (ou substituir) {{PLURAL:$2|a seguinte opção|as seguintes opções}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> foi descontinuado e será removido na versão $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi substituído por <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "{{PLURAL:$2|Opção|Opções}} de <code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> está a ser substituído por <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> foi removido na versão $2",
+ "smw-admin-deprecation-notice-title-notice": "Alterações pendentes",
+ "smw-admin-deprecation-notice-title-notice-explanation": "As seguintes definições, que estão a ser usadas nesta wiki, deverão ser removidas ou alteradas numa versão próxima.",
+ "smw-admin-deprecation-notice-title-replacement": "Definições substituídas ou com nome alterado",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "A secção a seguir contém definições que sofreram alteração do nome ou outra modificação e é recomendado que seja atualizado de imediato o nome ou o formato das mesmas.",
+ "smw-admin-deprecation-notice-title-removal": "Definições removidas",
+ "smw-admin-deprecation-notice-title-removal-explanation": "As definições listadas foram removidas numa versão anterior mas foi detetado que continuam a ser usadas nesta wiki.",
+ "smw-smwadmin-refresh-title": "Reparação e atualização de dados",
+ "smw_smwadmin_datarefresh": "Reconstrução de dados",
+ "smw_smwadmin_datarefreshdocu": "É possível restaurar todos os dados do MediaWiki Semântico baseado no conteúdo atual da wiki.\nIsto pode ser útil para reparar dados corrompidos ou para refrescar os dados se o formato interno tiver sido alterado devido a alguma evolução do ''software''.\nA atualização é executada página a página e não ficará completa de imediato.\nO seguinte mostra se uma atualização está a decorrer e permite-lhe iniciar ou parar atualizações (a menos que esta funcionalidade tenha sido desativada pelo administrador do sítio).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Já se encontra em progresso uma atualização.</strong>\nÉ normal que a atualização progrida lentamente, já que apenas refresca dados em pequenos blocos de cada vez que um utilizador acede à wiki.\nPara terminar esta atualização mais rapidamente, pode executar o ''script'' de manutenção do MediaWiki <code>runJobs.php</code> (use a opção <code>--maxjobs 1000</code> para restringir o número de atualizações feitas em cada bloco).\nProgresso estimado da atualização em curso:",
+ "smw_smwadmin_datarefreshbutton": "Programar a reconstrução dos dados",
+ "smw_smwadmin_datarefreshstop": "Parar esta atualização",
+ "smw_smwadmin_datarefreshstopconfirm": "Sim, {{GENDER:$1|tenho}} a certeza.",
+ "smw-admin-job-scheduler-note": "As atividades (ativas) desta secção são executadas através da fila de tarefas para evitar situações de impasse durante a sua execução. A [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas] é responsável pelo processamento, pelo que é crítico que o ''script'' de manutenção <code>runJobs.php</code> tenha uma capacidade adequada (ver também o parâmetro de configuração $<code>wgRunJobsAsync</code>).",
+ "smw-admin-outdateddisposal-title": "Eliminação de entidades desatualizadas",
+ "smw-admin-outdateddisposal-intro": "Algumas atividades (a alteração de um tipo de propriedade, a remoção de páginas da wiki, ou a correção de valores em erro) resultam em [https://www.semantic-mediawiki.org/wiki/Outdated_entities entidades desatualizadas] e é recomendado que estas sejam removidas periodicamente para libertar o respetivo espaço nas tabelas.",
+ "smw-admin-outdateddisposal-active": "Foi agendado um processo de eliminação de entidades desatualizadas.",
+ "smw-admin-outdateddisposal-button": "Agendar eliminação",
+ "smw-admin-feature-disabled": "Esta funcionalidade foi desativada nesta wiki. Consulte a página de ajuda das <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">configurações</a> ou contacte o administrador do sistema.",
+ "smw-admin-propertystatistics-title": "Reconstrução das estatísticas das propriedades",
+ "smw-admin-propertystatistics-intro": "Reconstrói todas as estatísticas de utilização de propriedades e nesse processo atualiza e corrige a [https://www.semantic-mediawiki.org/wiki/help:Property_usage_count contagem de usos] de propriedades.",
+ "smw-admin-propertystatistics-active": "Foi agendado um processo de reconstrução das estatísticas de propriedades.",
+ "smw-admin-propertystatistics-button": "Agendar reconstrução das estatísticas",
+ "smw-admin-fulltext-title": "Reconstrução da pesquisa de texto completo",
+ "smw-admin-fulltext-intro": "Reconstrói o índice de pesquisas com base nas tabelas de propriedade, usando um tipo de dados que suporta a [https://www.semantic-mediawiki.org/wiki/full-text pesquisa de texto integral]. Alterações das normas de indexação (''stopwords'' mudadas, novo ''stemmer'', etc.) ou uma tabela nova ou mudada requerem que este processo volte a ser executado.",
+ "smw-admin-fulltext-active": "Foi agendado um processo de reconstrução de pesquisa de texto integral.",
+ "smw-admin-fulltext-button": "Agendar reconstrução de pesquisa de texto integral",
+ "smw-admin-support": "Obter suporte",
+ "smw-admin-supportdocu": "São fornecidos vários recursos para ajudar em caso de problemas:",
+ "smw-admin-installfile": "Se tiver problemas com a instalação, comece por verificar as linhas orientadoras no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">ficheiro INSTALL</a> e a <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">página de instalação</a>.",
+ "smw-admin-smwhomepage": "A documentação completa para o utilizador do MediaWiki Semântico está em <b><a href=\"https://www.semantic-mediawiki.org/wiki/P%C3%A1gina_principal_pt\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Pode reportar defeitos no <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">sistema de registo de defeitos</a>, onde a página para <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">reportar defeitos</a> fornece instruções para criar relatórios eficazes.",
+ "smw-admin-questions": "Se tem mais questões ou sugestões, junte-se à discussão na <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">lista de divulgação</a> do MediaWiki Semântico ou na <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">sala de conversação</a>.",
+ "smw-admin-other-functions": "Outras funções",
+ "smw-admin-supplementary-section-title": "Funções suplementares",
+ "smw-admin-supplementary-section-subtitle": "Funções centrais",
+ "smw-admin-supplementary-section-intro": "Esta secção disponibiliza funções adicionais fora do âmbito da manutenção e é possível que algumas das funções listadas na [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentação] estejam restringidas ou indisponíveis e, portanto, inacessíveis nesta wiki.",
+ "smw-admin-supplementary-settings-title": "Definições de configuração",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> contém uma lista das definições disponíveis que são usadas no MediaWiki Semântico",
+ "smw-admin-supplementary-operational-statistics-title": "Estatísticas operacionais",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> apresenta um conjunto alargado de estatísticas",
+ "smw-admin-supplementary-idlookup-title": "Pesquisa e eliminação de entidades",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> contém funções para pesquisar e eliminar entidades individuais",
+ "smw-admin-supplementary-duplookup-title": "Entidades duplicadas",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> para listar entradas que estão categorizadas como tendo duplicados na tabela de entidades",
+ "smw-admin-supplementary-duplookup-docu": "Esta página lista as entradas da [https://www.semantic-mediawiki.org/wiki/Help:Entity_table tabela de entidades] que foram categorizadas como duplicados. Entradas duplicadas só devem ocorrer (se de todo) em ocasiões raras, possivelmente causadas por um processo terminado durante uma atualização da base de dados ou por uma transação de rollback não concluída.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Estatísticas da cache",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> mostra estatísticas relacionadas com a cache",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Funções Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> mostra configurações e estatísticas de indexação",
+ "smw-admin-supplementary-elastic-docu": "Esta página contém informação sobre as configurações, os mapeamentos, o estado e as estatísticas dos índices, de um ''cluster'' Elasticsearch que está ligado ao MediaWiki Semântico e ao respetivo [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Funções suportadas",
+ "smw-admin-supplementary-elastic-settings-title": "Configurações",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> usadas pelo Elasticsearch para gerir os índices do MediaWiki Semântico",
+ "smw-admin-supplementary-elastic-mappings-title": "Mapeamentos",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> para listar índices e mapeamentos de campos",
+ "smw-admin-supplementary-elastic-mappings-docu": "Esta página contém detalhes do mapeamento de campos tal como são usados com os índices atuais. O resumo dos mapeamentos deve ser monitorizado em ligação com o limite <code>index.mapping.total_fields.limit</code> que especifica o número máximo de campos num índice.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Resumo",
+ "smw-admin-supplementary-elastic-mappings-fields": "Mapeamentos de campos",
+ "smw-admin-supplementary-elastic-nodes-title": "Nós",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> mostra estatísticas dos nós",
+ "smw-admin-supplementary-elastic-indices-title": "Ãndices",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> fornece uma visão geral dos índices disponíveis e das suas estatísticas",
+ "smw-admin-supplementary-elastic-statistics-title": "Estatísticas",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> mostra as estatísticas ao nível dos índices",
+ "smw-admin-supplementary-elastic-statistics-docu": "Esta página fornece uma perspetiva das estatísticas de índices para as diferentes operações que estão a ocorrer ao nível do índice. As estatísticas produzidas estão agrupadas por agregações primárias e totais. A [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html página de ajuda] contém uma descrição detalhada das estatísticas de índices disponíveis.",
+ "smw-admin-supplementary-elastic-status-replication": "Estado da replicação",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "Última replicação ativa: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Intervalo de atualização: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Tarefas de recuperação em atraso: $1 (estimativa)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Tarefas de ingestão (de ficheiros) em atraso: $1 (estimativa)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Replicação trancada: $1 (recriação em progresso)",
+ "smw-list-count": "A lista contém $1 {{PLURAL:$1|entrada|entradas}}.",
+ "smw-list-count-from-cache": "A lista contém $1 {{PLURAL:$1|entrada|entradas}} e foi obtida da cache (UTC: $2).",
+ "smw-property-label-uniqueness": "Foi encontrada uma correspondência entre a etiqueta \"$1\" e pelo menos uma outra representação de propriedade. Consulte a [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness página de ajuda] sobre a resolução deste problema, por favor.",
+ "smw-property-label-similarity-title": "Relatório de semelhanças das etiquetas de propriedade",
+ "smw-property-label-similarity-intro": "<u>$1</u> calcula as semelhanças das etiquetas de propriedade existentes",
+ "smw-property-label-similarity-threshold": "Patamar:",
+ "smw-property-label-similarity-type": "Apresentar o identificador do tipo",
+ "smw-property-label-similarity-noresult": "Não foram encontrados resultados para as opções selecionadas.",
+ "smw-property-label-similarity-docu": "Compara e reporta a [https://www.semantic-mediawiki.org/wiki/Property_similarity semelhança sintática] (não a semelhança semântica) entre duas etiquetas de propriedades, o que pode ajudar a filtrar aquelas com erros ortográficos ou as propriedades equivalentes que representam o mesmo conceito (ver a página especial [[Special:Properties|Propriedades]] para clarificar o conceito e a utilização das propriedades reportadas). O patamar pode ser ajustado para aumentar ou reduzir a distância da semelhança. <code>[[Property:$1|$1]]</code> é usado para isentar propriedades desta análise.",
+ "smw-admin-operational-statistics": "Esta página contém estatísticas operacionais recolhidas em (ou por) funções relacionadas com o MediaWiki Semântico. Encontra uma lista expandida de estatísticas específicas da wiki [[Special:Statistics|<b>aqui</b>]].",
+ "smw_adminlinks_datastructure": "Estrutura de dados",
+ "smw_adminlinks_displayingdata": "Apresentação de dados",
+ "smw_adminlinks_inlinequerieshelp": "Ajuda para consultas dinâmicas (''inline queries'')",
+ "smw-page-indicator-usage-count": "[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Número de utilizações] estimado: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "Propriedade definida pelo {{PLURAL:$1|utilizador|sistema}}",
+ "smw-property-indicator-last-count-update": "Número estimado de utilizações\nÚltima atualização: $1",
+ "smw-concept-indicator-cache-update": "Contagem da cache\nÚltima atualização: $1",
+ "smw-createproperty-isproperty": "É uma propriedade do tipo $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|O valor permitido para esta propriedade é|Os valores permitidos para esta propriedade são}}:",
+ "smw-paramdesc-category-delim": "O delimitador",
+ "smw-paramdesc-category-template": "Uma predefinição para formatação dos elementos",
+ "smw-paramdesc-category-userparam": "Um parâmetro para passar à predefinição",
+ "smw-info-par-message": "A mensagem a ser apresentada.",
+ "smw-info-par-icon": "Ãcone a ser mostrado, \"informação\" ou \"aviso\".",
+ "prefs-smw": "MediaWiki Semântico",
+ "prefs-general-options": "Opções gerais",
+ "prefs-ask-options": "Opções da página Special:Ask",
+ "smw-prefs-intro-text": "O [https://www.semantic-mediawiki.org/ MediaWiki Semântico] (e extensões relacionadas) permitem a personalização individualizada de certas funções. Para uma descrição detalhada, consulte a [https://www.semantic-mediawiki.org/wiki/Help:User_preferences página de ajuda], por favor.",
+ "smw-prefs-ask-options-tooltip-display": "Mostrar o texto do parâmetro na forma de dica",
+ "smw-prefs-ask-options-compact-view-basic": "Ativar a vista compacta básica",
+ "smw-prefs-help-ask-options-compact-view-basic": "Se ativado, apresenta um conjunto reduzido de hiperligações na vista compacta Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Ativar a correção da hora para as páginas especiais, usando a preferência do [[Special:Preferences#mw-prefsection-rendering|fuso horário]].",
+ "smw-prefs-general-options-jobqueue-watchlist": "Mostrar a lista de vigilância da fila de tarefas na minha barra pessoal",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Se ativado, mostra uma [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist lista] de tarefas selecionadas pendentes, com os respetivos tamanhos de fila estimados.",
+ "smw-prefs-general-options-disable-editpage-info": "Desativar o texto introdutório na página de edição",
+ "smw-prefs-general-options-disable-search-info": "Desativar as informações de suporte da sintaxe na página de pesquisa padrão",
+ "smw-prefs-general-options-suggester-textinput": "Ativar o auxiliar de preenchimento para as entidades semânticas",
+ "smw-prefs-help-general-options-suggester-textinput": "Se ativado, permite usar um [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance auxiliar de preenchimento] para encontrar propriedades, conceitos e categorias num contexto de entrada.",
+ "smw-ui-tooltip-title-property": "Propriedade",
+ "smw-ui-tooltip-title-quantity": "Conversão de unidades",
+ "smw-ui-tooltip-title-info": "Informação",
+ "smw-ui-tooltip-title-service": "Ligações de serviços",
+ "smw-ui-tooltip-title-warning": "Aviso",
+ "smw-ui-tooltip-title-error": "Erro",
+ "smw-ui-tooltip-title-parameter": "Parâmetro",
+ "smw-ui-tooltip-title-event": "Evento",
+ "smw-ui-tooltip-title-note": "Nota",
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-ui-tooltip-title-reference": "Referência",
+ "smw_unknowntype": "O tipo \"$1\" desta propriedade é inválido",
+ "smw-concept-cache-text": "O conceito tem no total $1 {{PLURAL:$1|página|páginas}}, e foi atualizado pela última vez a $3, às $2.",
+ "smw_concept_header": "Páginas do conceito \"$1\"",
+ "smw_conceptarticlecount": "A apresentar $1 {{PLURAL:$1|página|páginas}} abaixo.",
+ "smw-qp-empty-data": "Não foi possível apresentar os dados pedidos devido a alguns critérios de seleção insuficientes.",
+ "right-smw-admin": "Aceder às tarefas de administração (MediaWiki Semântico)",
+ "right-smw-patternedit": "Acesso de edição para manter as expressões regulares e os padrões permitidos (MediaWiki Semântico)",
+ "right-smw-pageedit": "Acesso para edição de páginas com a anotação <code>Está protegida contra edições</code> (MediaWiki Semântico)",
+ "right-smw-ruleedit": "Editar páginas de regras (MediaWiki Semântico)",
+ "restriction-level-smw-pageedit": "protegida (só utilizadores elegíveis)",
+ "action-smw-patternedit": "editar expressões regulares usadas pelo MediaWiki Semântico",
+ "action-smw-pageedit": "editar páginas anotadas com <code>Está protegida contra edições</code> (MediaWiki Semântico)",
+ "group-smwadministrator": "Administradores (MediaWiki Semântico)",
+ "group-smwadministrator-member": "{{GENDER:$1|administrador|administradora}} (MediaWiki Semântico)",
+ "grouppage-smwadministrator": "{{ns:project}}:Administradores (MediaWiki Semântico)",
+ "group-smwcurator": "Curadores (MediaWiki Semântico)",
+ "group-smwcurator-member": "{{GENDER:$1|curador|curadora}} (MediaWiki Semântico)",
+ "grouppage-smwcurator": "{{ns:project}}:Curadores (MediaWiki Semântico)",
+ "action-smw-admin": "aceder às tarefas de administração do MediaWiki Semântico",
+ "action-smw-ruleedit": "editar páginas de regras (MediaWiki Semântico)",
+ "smw-property-predefined-default": "\"$1\" é uma propriedade predefinida.",
+ "smw-property-predefined-common": "Esta propriedade é predefinida (também chamada [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriedade especial]) e vem com privilégios de administração adicionais, mas pode ser usada como qualquer outra [https://www.semantic-mediawiki.org/wiki/Property propriedade definida pelo utilizador].",
+ "smw-property-predefined-ask": "\"$1\" é uma propriedade predefinida que representa metainformação (na forma de [https://www.semantic-mediawiki.org/wiki/Subobject subobjeto]) acerca de consultas individuais, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-asksi": "\"$1\" é uma propriedade predefinida que recolhe o número de condições usadas numa consulta, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-askde": "\"$1\" é uma propriedade predefinida que fornece informação sobre a profundidade de uma consulta, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-askde": "É um valor numérico calculado com base nas consultas intercaladas, nas cadeias de propriedades e nos elementos descritivos disponíveis, sendo que a execução de cada consulta está restringida pelo parâmetro de configuração <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "\"$1\" é uma propriedade predefinida que descreve parâmetros que influenciam um resultado de consulta, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-askpa": "É parte de um conjunto de propriedades que especificam um [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler perfil de consulta].",
+ "smw-sp-properties-docu": "Esta página lista as [https://www.semantic-mediawiki.org/wiki/Property propriedades] e a respetiva contagem de utilizações nesta wiki. Para ter estatísticas de contagem atualizadas, é recomendado que o [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics ''script'' de manutenção das estatísticas de propriedades] seja executado com regularidade. Para obter uma perspetiva diferente, veja as páginas especiais das [[Special:UnusedProperties|propriedades não utilizadas]] e [[Special:WantedProperties|propriedades desejadas]].",
+ "smw-sp-properties-cache-info": "Os dados listados foram obtidos da [https://www.semantic-mediawiki.org/wiki/Caching ''cache''] e foram atualizados pela última vez a $1.",
+ "smw-sp-properties-header-label": "Lista de propriedades",
+ "smw-admin-settings-docu": "Apresenta uma lista de todas as configurações padrão e localizadas que são relevantes para o ambiente do MediaWiki Semântico. Para mais detalhes sobre as configurações individuais, consulte a página de ajuda da [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuração], por favor.",
+ "smw-sp-admin-settings-button": "Gerar lista de configurações",
+ "smw-admin-idlookup-title": "Consulta",
+ "smw-admin-idlookup-docu": "Esta secção mostra detalhes técnicos de uma entidade individual (página da wiki, subobjeto, propriedade, etc.) no MediaWiki Semântico. Os dados de entrada podem ser um identificador numérico ou um texto que corresponda ao campo de pesquisa relevante, mas qualquer referência a um identificador refere-se ao MediaWiki Semântico e não deve ser confundida com os identificadores de página ou de revisão do MediaWiki.",
+ "smw-admin-iddispose-title": "Eliminação",
+ "smw-admin-iddispose-docu": "Note que a operação de eliminação não tem restrições e, se for confirmada, irá remover a entidade do motor de armazenamento e todas as referências à mesma das tabelas pendentes. Realize esta operação com '''cuidado''' e só depois de consultar a [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentação], por favor.",
+ "smw-admin-iddispose-done": "O identificador \"$1\" foi removido do servidor de armazenamento.",
+ "smw-admin-iddispose-references": "O identificador \"$1\" {{PLURAL:$2|não tem nenhuma|tem pelo menos uma}} referência ativa.",
+ "smw-admin-iddispose-references-multiple": "Lista de correspondências com pelo menos um registo de referência ativo.",
+ "smw-admin-iddispose-no-references": "A pesquisa não encontrou correspondência entre \"$1\" e uma entrada da tabela.",
+ "smw-admin-idlookup-input": "Pesquisar:",
+ "smw-admin-objectid": "Identificador:",
+ "smw-admin-tab-general": "Visão geral",
+ "smw-admin-tab-notices": "Avisos de descontinuação",
+ "smw-admin-tab-maintenance": "Manutenção",
+ "smw-admin-tab-supplement": "Funções suplementares",
+ "smw-admin-tab-registry": "Registo",
+ "smw-admin-maintenance-no-description": "Sem descrição.",
+ "smw-admin-maintenance-script-section-title": "Lista dos ''scripts'' de manutenção disponíveis",
+ "smw-admin-maintenance-script-section-intro": "Os seguintes ''scripts'' de manutenção requerem um administrador e acesso à linha de comandos para poder executar os ''scripts'' listados.",
+ "smw-admin-maintenance-script-description-dumprdf": "Exportação para RDF das triplas existentes.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "Este ''script'' é usado para gerir as ''caches'' de conceitos do MediaWiki Semântico, sendo que pode criar, remover e atualizar ''caches'' selecionadas.",
+ "smw-admin-maintenance-script-description-rebuilddata": "Recria todos os dados semânticos na base de dados, percorrendo todas as páginas que possam ter dados semânticos.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "Reconstrói o índice Elasticsearch (nas instalações que usam o <code>ElasticStore</code>), percorrendo todas as entidades que têm dados semânticos.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "Reconstrói o índice de pesquisa de texto completo <code>SQLStore</code> (nas instalações onde a configuração foi ativada).",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "Reconstrói as estatísticas de utilização de todas as entidades propriedade.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "Remove as entidades duplicadas encontradas em tabelas selecionadas que não tenham referências ativas.",
+ "smw-admin-maintenance-script-description-setupstore": "Configura o servidor de armazenamento selecionado em <code>LocalSettings.php</code>.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "Atualiza o campo <code>smw_sort</code> do <code>SQLStore</code> (de acordo com a configuração [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "Popula o campo <code>smw_hash</code> das linhas em que este não tem um valor.",
+ "smw-livepreview-loading": "A carregar…",
+ "smw-sp-searchbyproperty-description": "Esta página fornece uma [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces interface de navegação] simples para encontrar entidades descritas por uma propriedade e pelo nome de um valor. Entre as outras interfaces de pesquisa disponíveis, incluem-se a [[Special:PageProperty|página de pesquisa de propriedades]] e o [[Special:Ask|construtor de consultas ''ask'']].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista de resultados",
+ "smw-sp-searchbyproperty-nonvaluequery": "Uma lista de valores que têm atribuída a propriedade \"$1\".",
+ "smw-sp-searchbyproperty-valuequery": "Uma lista de páginas que têm a propriedade \"$1\" com o valor \"$2\" anotado.",
+ "smw-datavalue-number-textnotallowed": "\"$1\" não pode ser atribuído a um tipo de número declarado com o valor $2.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" devolveu o valor \"NULL\" (nulo), que não é permitido como número.",
+ "smw-editpage-annotation-enabled": "Esta página suporta anotações semânticas de texto (por exemplo, <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) para construir conteúdo estruturado e consultável, e é fornecida pelo MediaWiki Semântico. Para uma descrição detalhada do uso de anotações, ou da função #ask do analisador sintático, consulte as páginas de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Getting_started começar], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation anotação de texto], ou [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries consultas em linha (''inline'')].",
+ "smw-editpage-annotation-disabled": "Não são permitidas anotações semânticas de texto nesta página, devido a restrições do espaço nominal. Para detalhes sobre como ativar o espaço nominal consulte a página de ajuda da [https://www.semantic-mediawiki.org/wiki/Help:Configuration configuração].",
+ "smw-editpage-property-annotation-enabled": "Esta propriedade pode ser expandida usando anotações semânticas para especificar um tipo de dados (por exemplo, <nowiki>\"[[Has type::Page]]\"</nowiki>) ou inserir outras declarações de apoio (por exemplo, <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). Para uma descrição das formas de ampliação desta página, consulte as páginas de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration declaração de uma propriedade] e [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes lista dos tipos de dados disponíveis].",
+ "smw-editpage-property-annotation-disabled": "Esta propriedade não pode ser expandida com uma anotação de tipo de dados (por exemplo, <nowiki>\"[[Has type::Page]]\"</nowiki>) porque o tipo já está predefinido (para mais informações, consulte a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Special_properties propriedades especiais]).",
+ "smw-editpage-concept-annotation-enabled": "Este conceito pode ser expandido usando a função #concept do analisador sintático. Para uma descrição do uso da função #concept, consulte a página de ajuda [https://www.semantic-mediawiki.org/wiki/Help:Concepts conceitos].",
+ "smw-search-syntax-support": "A entrada da pesquisa permite o uso da [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search sintaxe de consultas] semânticas para ajudar a encontrar correspondências usando o MediaWiki Semântico.",
+ "smw-search-input-assistance": "O [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ajudante de preenchimento] também está ativado para facilitar a pré-seleção das propriedades e categorias disponíveis.",
+ "smw-search-help-intro": "Uma entrada com a forma <code><nowiki>[[ ... ]]</nowiki></code> assinala ao processador da entrada que deve usar o motor de pesquisa do MediaWiki Semântico. Note que a combinação de <code><nowiki>[[ ... ]]</nowiki></code> com uma pesquisa de texto não estruturado, como <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code>, não é suportada.",
+ "smw-search-help-structured": "Pesquisas estruturadas:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (como [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context contexto filtrado])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (com um [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context contexto de consulta])",
+ "smw-search-help-proximity": "Pesquisas aproximidas (uma propriedade ser desconhecida, '''só''' disponível para aqueles motores de pesquisa que fornecem uma integração com a pesquisa de texto completo):\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (pesquisar em todos os documentos os termos \"lorem\" e \"ipsum\" que tenham sido indexados)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (pesquisar correspondências com \"lorem ipsum\" como frase)",
+ "smw-search-help-ask": "As seguintes hiperligações explicam como usar a sintaxe <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecionar páginas] descreve como selecionar páginas e construir condições\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Operadores de pesquisa] lista os operadores de pesquisa disponíveis, incluindo os para consultas de intervalos e consultas com caracteres de substituição",
+ "smw-search-input": "Introdução e pesquisa",
+ "smw-search-help-input-assistance": "É fornecida uma [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ajuda de introdução] para o campo de entrada e requer que seja usado um dos seguintes prefixos:\n\n*<code>p:</code> para ativar as sugestões de propriedades (p. ex.: <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> para ativar as sugestões de categorias\n\n*<code>con:</code> para ativar as sugestões de conceitos",
+ "smw-search-syntax": "Sintaxe",
+ "smw-search-profile": "Expandida",
+ "smw-search-profile-tooltip": "Funções de pesquisa relacionadas com o MediaWiki Semântico",
+ "smw-search-profile-sort-best": "Melhor correspondência",
+ "smw-search-profile-sort-recent": "Mais recente",
+ "smw-search-profile-sort-title": "Título",
+ "smw-search-profile-extended-help-intro": "O [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile perfil alargado] da página Special:Search dá acesso a funções de pesquisa específicas da extensão MediaWiki Semântico e do seu servidor de consultas suportado.",
+ "smw-search-profile-extended-help-sort": "Especifica uma preferência de ordenação para a apresentação do resultado com:",
+ "smw-search-profile-extended-help-sort-title": "*\"Título\" usando o título da página (ou título de apresentação) como critério de ordenação",
+ "smw-search-profile-extended-help-sort-recent": "*\"Mais recente\" mostrará primeiro as entidades modificadas mais recentemente (as entidades subobjetos são suprimidas porque essas entidades não estão anotadas com uma [[Property:Modification date|data de modificação]])",
+ "smw-search-profile-extended-help-sort-best": "*\"Melhor correspondência\" ordenará as entidades por [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy relevância] com base em classificações fornecidas pelo servidor",
+ "smw-search-profile-extended-help-form": "São disponibilizados formulários (se estes forem mantidos) para certos casos específicos de utilização. Os formulários podem expor diferentes campos de propriedade e de entrada de valor, para limitar o processo de introdução e facilitar a criação de pedidos de pesquisa (ver $1).",
+ "smw-search-profile-extended-help-namespace": "A caixa de seleção do espaço nominal/domínio será ocultada logo que um formulário for selecionado mas pode ser tornada visível com a ajuda do botão \"mostrar/esconder\".",
+ "smw-search-profile-extended-help-search-syntax": "O campo de entrada da pesquisa permite o uso da sintaxe <code>#ask</code> para definir um contexto de pesquisa específico do MediaWiki Semântico. Algumas expressões úteis:",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code> para encontrar tudo o que contenha \"...\" e é especialmente útil quando não se conhece o contexto da pesquisa ou as propriedades envolvidas (por exemplo, <code>in:(lorem && ipsum)</code> é equivalente a <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>).",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code> para encontrar tudo o que contenha \"...\" exatamente na mesma ordem",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code> para corresponder com qualquer entidade com uma propriedade \"...\" (por exemplo, <code>has:(Foo && Bar)</code> é equivalente a <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> para não corresponder com nenhuma entidade que inclui \"...\"",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* Estão disponíveis e definidos prefixos personalizados adicionais, como: $1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* Algumas expressões estão reservadas, como: <nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''Algumas das operações listadas só são úteis em ligação com um índice ativado de texto completo ou com o ElasticStore.''",
+ "smw-search-profile-extended-help-query": "<code><nowiki>$1</nowiki></code> foi usado como consulta.",
+ "smw-search-profile-extended-help-query-link": "(Para mais detalhes $1).",
+ "smw-search-profile-extended-help-find-forms": "formulários disponíveis",
+ "smw-search-profile-extended-section-sort": "Ordenar por",
+ "smw-search-profile-extended-section-form": "Formulários",
+ "smw-search-profile-extended-section-search-syntax": "Entrada a pesquisar",
+ "smw-search-profile-extended-section-namespace": "Espaço nominal/domínio",
+ "smw-search-profile-extended-section-query": "Consulta",
+ "smw-search-profile-link-caption-query": "ver",
+ "smw-search-show": "Mostrar",
+ "smw-search-hide": "Ocultar",
+ "log-name-smw": "Registo do MediaWiki Semântico",
+ "log-show-hide-smw": "$1 o registo do MediaWiki Semântico",
+ "logeventslist-smw-log": "Registo do MediaWiki Semântico",
+ "log-description-smw": "Atividades para os [https://www.semantic-mediawiki.org/wiki/Help:Logging tipos de eventos ativados] que foram reportados pelo MediaWiki Semântico e pelos seus componentes.",
+ "logentry-smw-maintenance": "Ocorrências relacionadas com manutenção, emitidas pelo MediaWiki Semântico",
+ "smw-datavalue-import-unknown-namespace": "O espaço nominal de importação \"$1\" é desconhecido. Verifique que os detalhes de importação OWL estão disponíveis via [[MediaWiki:Smw import $1]], por favor",
+ "smw-datavalue-import-missing-namespace-uri": "Não foi possível encontrar um URI do espaço nominal \"$1\" na [[MediaWiki:Smw import $1|importação de $1]].",
+ "smw-datavalue-import-missing-type": "Não foi encontrada nenhuma definição de tipo para \"$1\" na [[MediaWiki:Smw import $2|importação de $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|importação de $1]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" não é um formato válido e deve ter a forma \"espaço\":\"identificador\" (por exemplo, \"xpto:nome\").",
+ "smw-datavalue-import-invalid-format": "Era esperado que o texto \"$1\" estivesse dividido em quatro partes, mas o formato não foi compreendido.",
+ "smw-property-predefined-impo": "\"$1\" é uma propriedade predefinida que descreve uma relação com um [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary vocabulário importado] e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-type": "\"$1\" é uma propriedade predefinida que descreve o [[Special:Types|tipo de dados]] de uma propriedade e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-sobj": "\"$1\" é uma propriedade predefinida que representa uma estrutura [https://www.semantic-mediawiki.org/wiki/Help:Container recipiente] e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-sobj": "A estrutura recipiente permite acumular atribuições de valores a propriedades, e é semelhante a uma página wiki normal mas num espaço de entidades diferente, estando ligada ao assunto que a incorpora.",
+ "smw-property-predefined-errp": "\"$1\" é uma propriedade predefinida que regista erros de entrada em anotações de valor irregular e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-errp": "Na maioria dos casos, a causa é uma não correspondência de tipos ou uma restrição do [[Property:Allows value|valor]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] é uma propriedade predefinida que pode definir uma lista de valores permitidos, para restringir a atribuição de valores a uma propriedade, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] é uma propriedade predefinida que pode especificar uma referência para uma lista de valores permitidos, para restringir a atribuição de valores a uma propriedade, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-datavalue-property-restricted-annotation-use": "A propriedade \"$1\" tem uma área de aplicação restrita e não pode ser usada como propriedade de anotação por um utilizador.",
+ "smw-datavalue-property-restricted-declarative-use": "A propriedade \"$1\" é uma propriedade declarativa e só pode ser usada numa propriedade ou página de categoria.",
+ "smw-datavalue-property-create-restriction": "A propriedade \"$1\" não existe e o utilizador não tem a permissão \"$2\" (consulte [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade]) para criar ou anotar valores com uma propriedade não aprovada.",
+ "smw-datavalue-property-invalid-character": "\"$1\" contém um carácter \"$2\" listado como parte da etiqueta de propriedade e portanto foi classificado como inválido.",
+ "smw-datavalue-property-invalid-chain": "Usar \"$1\" como cadeia de propriedades não é permitido durante o processo de anotação.",
+ "smw-datavalue-restricted-use": "O valor de dados \"$1\" foi marcado para uso restrito.",
+ "smw-datavalue-invalid-number": "\"$1\" não pode ser interpretado como um número.",
+ "smw-query-condition-circular": "Foi detetada uma possível condição circular em \"$1\".",
+ "smw-query-condition-empty": "A descrição da consulta tem uma condição vazia.",
+ "smw-types-list": "Lista de tipos de dados",
+ "smw-types-default": "\"$1\" é um tipo de dados interno.",
+ "smw-types-help": "Mais informações e exemplos podem ser encontrados nesta [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 página de ajuda].",
+ "smw-type-anu": "\"$1\" é uma variante do tipo de dados [[Special:Types/URL|URL]], usada sobretudo para uma declaração de exportação ''owl:AnnotationProperty''.",
+ "smw-type-boo": "\"$1\" é um tipo de dados básico para descrever um valor verdadeiro ou falso.",
+ "smw-type-cod": "\"$1\" é uma variante do tipo de dados [[Special:Types/Text|Texto]], usado para textos técnicos de comprimento arbitrário, como listagens de código fonte.",
+ "smw-type-geo": "\"$1\" é um tipo de dados que descreve localizações geográficas e requer a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Mapas\"].",
+ "smw-type-tel": "\"$1\" é um tipo de dados especial para descrever números telefónicos internacionais segundo o RFC 3966.",
+ "smw-type-txt": "\"$1\" é um tipo de dados básico para descrever textos (''strings'') de comprimento arbitrário.",
+ "smw-type-dat": "\"$1\" é um tipo de dados básico para representar pontos no tempo num formato unificado.",
+ "smw-type-ema": "\"$1\" é um tipo de dados especial para representar um correio eletrónico.",
+ "smw-type-tem": "\"$1\" é um tipo de dados numérico especial para representar uma temperatura.",
+ "smw-type-qty": "\"$1\" é um tipo de dados para descrever quantidades com uma representação numérica e uma unidade de medida.",
+ "smw-type-rec": "\"$1\" é um tipo de dados recipiente que especifica uma lista de propriedades com tipos, numa ordem fixa.",
+ "smw-type-extra-tem": "O esquema de conversão inclui as unidades suportadas, como Kelvin, Celsius, Fahrenheit e Rankine.",
+ "smw-type-tab-properties": "Propriedades",
+ "smw-type-tab-types": "Tipos",
+ "smw-type-tab-errors": "Erros",
+ "smw-type-primitive": "Básico",
+ "smw-type-contextual": "Contextual",
+ "smw-type-compound": "Composto",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Esta página fornece uma interface de navegação para encontrar todos os valores de uma propriedade e uma determinada página. Entre as outras interfaces de pesquisa disponíveis, incluem-se a [[Special:SearchByProperty|pesquisa de propriedades]] e o [[Special:Ask|construtor de consultas ''ask'']].",
+ "smw-property-predefined-errc": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico], e representa erros relacionados com anotações de valor incorretas ou com o processamento de entradas.",
+ "smw-property-predefined-long-errc": "Os erros são recolhidos numa [https://www.semantic-mediawiki.org/wiki/Help:Container estrutura recipiente] que pode incluir uma referencia à propriedade que causou a discrepância.",
+ "smw-property-predefined-errt": "\"$1\" é uma propriedade predefinida que contém uma descrição textual de um erro, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-subobject-parser-invalid-naming-scheme": "Um subobjeto definido pelo utilizador continha um nome com formato inválido. O uso de um ponto ($1) nos primeiros cinco caracteres é uma notação reservada a extensões. Pode definir um [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier identificador nomeado].",
+ "smw-datavalue-record-invalid-property-declaration": "A definição do registo contém a propriedade \"$1\", que está definida como um tipo de registo, e isso não é permitido.",
+ "smw-property-predefined-mdat": "\"$1\" é uma propriedade predefinida que corresponde à data da última modificação de um assunto e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-cdat": "\"$1\" é uma propriedade predefinida que corresponde à data da primeira revisão de um assunto e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-newp": "\"$1\" é uma propriedade predefinida que indica se um assunto é novo ou não, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-ledt": "\"$1\" é uma propriedade predefinida que contém o nome da página do utilizador que criou a última revisão, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-mime": "\"$1\" é uma propriedade predefinida que descreve o tipo MIME de um ficheiro carregado, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-media": "\"$1\" é uma propriedade predefinida que descreve o tipo de multimédia de um ficheiro carregado, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-askfo": "\"$1\" é uma propriedade predefinida que contém o nome do formato de resultado usado numa consulta, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-askst": "\"$1\" é uma propriedade predefinida que descreve as condições da consulta na forma de texto, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-askdu": "\"$1\" é uma propriedade predefinida que contém o tempo (em segundos) necessário para a execução da consulta, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-asksc": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico], que identifica fontes de consulta alternativas (por exemplo, remotas, federadas).",
+ "smw-property-predefined-askco": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para descrever o estado de uma consulta ou dos seus componentes.",
+ "smw-property-predefined-long-askco": "O número ou números atribuídos representam um estado interno codificado que é explicado na [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler página de ajuda].",
+ "smw-property-predefined-prec": "\"$1\" é uma propriedade predefinida que descreve a [https://www.semantic-mediawiki.org/wiki/Help:Display_precision precisão de apresentação] (em casas decimais) para os tipos de dados numéricos.",
+ "smw-types-extra-geo-not-available": "Não foi detetada a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Mapas\"], portanto, \"$1\" está restringido na sua capacidade operacional.",
+ "smw-datavalue-monolingual-dataitem-missing": "Falta um elemento esperado para construir um valor composto monolingue.",
+ "smw-datavalue-languagecode-missing": "Para a anotação \"$1\", o analisador sintático não conseguiu determinar o código de língua (isto é, \"algo@pt\").",
+ "smw-datavalue-languagecode-invalid": "\"$1\" não foi reconhecido como um código de língua suportado.",
+ "smw-property-predefined-lcode": "\"$1\" é uma propriedade predefinida que representa um código de língua formatado BCP47 e é fornecido pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-type-mlt-rec": "\"$1\" é um tipo de dados [https://www.semantic-mediawiki.org/wiki/Help:Container recipiente] (<i>container</i>) que associa um valor em texto a um [[Property:Language code|código de língua]] específico.",
+ "smw-types-extra-mlt-lcode": "O tipo de dados {{PLURAL:$2|necessita|não necessita}} de um código de língua (ou seja, uma anotação de valor sem um código de língua {{PLURAL:$2|não é|é}} aceite).",
+ "smw-property-predefined-text": "\"$1\" é uma propriedade predefinida que representa texto de comprimento arbitrário, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-pdesc": "\"$1\" é uma propriedade predefinida que permite descrever uma propriedade no contexto de uma língua e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-list": "\"$1\" é uma propriedade predefinida, para definir uma lista de propriedades usadas com uma propriedade de tipo [[Special:Types/Record|registo]], e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-limitreport-intext-parsertime": "[SMW] Tempo de análise sintática das anotações de texto",
+ "smw-limitreport-intext-postproctime": "[SMW] tempo pós-processamento",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Tempo de atualização de armazenamento (na purga de páginas)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|segundo|segundos}}",
+ "smw_allows_pattern": "É esperado que esta página contenha uma lista de referências (seguidas por [https://en.wikipedia.org/wiki/Regular_expression expressões regulares]) que serão disponibilizadas pela propriedade [[Property:Allows pattern|Padrão permitido]]. Para editar esta página é necessária a permissão <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "\"$1\" foi considerada inválida pela expressão regular \"$2\".",
+ "smw-datavalue-allows-pattern-reference-unknown": "A referência \"$1\" do padrão permitido não foi encontrada na página [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "A referência de lista \"$1\" não tem correspondência com uma página [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "O conteúdo da lista \"$1\" não contém elementos com o marcador de lista *.",
+ "smw-datavalue-feature-not-supported": "A funcionalidade \"$1\" não é suportada, ou foi desativada, nesta wiki.",
+ "smw-property-predefined-pvap": "\"$1\" é uma propriedade predefinida, que pode especificar uma [[MediaWiki:Smw allows pattern|referência de padrão permitido]] para aplicar uma [https://en.wikipedia.org/wiki/Regular_expression expressão regular], e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-dtitle": "\"$1\" é uma propriedade predefinida que pode atribuir a uma entidade um título de apresentação distinto, e é fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-pvuc": "\"$1\" é uma propriedade predefinida, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico], para restringir a valores únicos (ou a um, no máximo) a atribuição de valores para cada instância.",
+ "smw-property-predefined-long-pvuc": "A unicidade é estabelecida quando as representações literais de dois valores não são iguais e qualquer violação desta restrição será categorizada como erro.",
+ "smw-datavalue-uniqueness-constraint-error": "A propriedade \"$1\" só permite a atribuição de valores únicos e ''$2'' já foi anotado no assunto \"$3\".",
+ "smw-datavalue-uniqueness-constraint-isknown": "A propriedade \"$1\" só permite anotações de valores únicos, ''$2'' já contém um valor atribuído. \"$3\" viola a restrição de unicidade.",
+ "smw-property-predefined-boo": "\"$1\" é um [[Special:Types/Boolean|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar valores boolianos.",
+ "smw-property-predefined-num": "\"$1\" é um [[Special:Types/Number|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar valores numéricos.",
+ "smw-property-predefined-dat": "\"$1\" é um [[Special:Types/Date|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar valores de datas.",
+ "smw-property-predefined-uri": "\"$1\" é um [[Special:Types/URL|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar valores URI/URL.",
+ "smw-property-predefined-qty": "\"$1\" é um [[Special:Types/Quantity|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar valores de quantidade.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "\"$1\" contém um desfasamento e um indicador de zona que não são suportados.",
+ "smw-datavalue-time-invalid-values": "O valor \"$1\" contém informação que não pode ser interpretada, na forma \"$2\".",
+ "smw-datavalue-time-invalid-date-components-common": "\"$1\" contém alguma informação que não pode ser interpretada.",
+ "smw-datavalue-time-invalid-date-components-dash": "\"$1\" contém um travessão ou traço de ligação extrínseco ou outros caracteres que são inválidos na interpretação de uma data.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" contém alguns componentes vazios.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" contém mais de três componentes necessários para a interpretação de uma data.",
+ "smw-datavalue-time-invalid-date-components-sequence": "\"$1\" contém uma sequência que não foi possível interpretar contra uma matriz de correspondências disponível para componentes de datas.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" contém \"$2\" como elemento hora, que é inválido no formato de relógio de 12 horas.",
+ "smw-datavalue-time-invalid-jd": "Não foi possível interpretar o valor de entrada \"$1\" como um dia válido no calendário Juliano, tendo sido reportado \"$2\".",
+ "smw-datavalue-time-invalid-prehistoric": "Não é possível interpretar o valor de entrada pré-histórico \"$1\". Por exemplo, especificar mais do que anos, ou do que um modelo de calendário, poderá produzir resultados inesperados num contexto pré-histórico.",
+ "smw-datavalue-time-invalid": "Não foi possível interpretar o valor de entrada \"$1\" como uma data válida ou como um componente para expressão de tempo, tendo sido reportado \"$2\".",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Falta o espaço reservado \"$1\" no URI do formatador.",
+ "smw-datavalue-external-formatter-invalid-uri": "\"$1\" é um URL inválido.",
+ "smw-datavalue-external-identifier-formatter-missing": "Falta à propriedade a atribuição de um [[Property:External formatter uri|\"URI de formatador externo\"]].",
+ "smw-datavalue-keyword-maximum-length": "A palavra-chave excedeu o tamanho máximo de $1 {{PLURAL:$1|carácter|caracteres}}.",
+ "smw-property-predefined-eid": "\"$1\" é um [[Special:Types/External identifier|tipo]] e uma propriedade predefinida, fornecidos pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para representar identificadores externos.",
+ "smw-property-predefined-peid": "\"$1\" é uma propriedade predefinida que especifica um identificador externo, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-pefu": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para especificar um recurso externo com um espaço reservado.",
+ "smw-property-predefined-long-pefu": "Espera-se que o URI contenha um espaço reservado que será ajustado com um valor de [[Special:Types/External identifier|identificador externo]] para formar uma referência de recursos válida.",
+ "smw-type-eid": "\"$1\" é uma variante do tipo de dados [[Special:Types/Text|texto]] que requer propriedades atribuídas para declarar um [[Property:External formatter uri|URI de formatador externo]].",
+ "smw-property-predefined-keyw": "\"$1\" é uma propriedade predefinida e um [[Special:Types/Keyword|tipo]], fornecidas pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico], que normalizam um texto e têm um tamanho restrito de caracteres.",
+ "smw-type-keyw": "\"$1\" é uma variante do tipo de dados de [[Special:Types/Text|texto]] que tem um tamanho restrito de caracteres e normaliza a sua representação de conteúdos.",
+ "smw-datavalue-stripmarker-parse-error": "O valor fornecido, \"$1\", contém [https://en.wikipedia.org/wiki/Help:Strip_markers marcadores do analisador sintático] e não pode ser suficientemente analisado.",
+ "smw-datavalue-parse-error": "O valor fornecido, \"$1\", não foi compreendido.",
+ "smw-datavalue-propertylist-invalid-property-key": "A lista de propriedades \"$1\" continha uma chave de propriedade inválida \"$2\".",
+ "smw-datavalue-type-invalid-typeuri": "Não foi possível transformar o tipo \"$1\" numa representação URI válida.",
+ "smw-datavalue-wikipage-missing-fragment-context": "O valor de entrada \"$1\" da página da wiki não pode ser usado sem uma página de contexto.",
+ "smw-datavalue-wikipage-invalid-title": "O valor de entrada \"$1\" do tipo de página contém caracteres inválidos ou está incompleto e, portanto, pode provocar resultados inesperados numa consulta ou num processo de anotação.",
+ "smw-datavalue-wikipage-property-invalid-title": "A propriedade \"$1\" (como tipo de página) com o valor de entrada \"$2\" contém caracteres inválidos ou está incompleta e, portanto, pode provocar resultados inesperados numa consulta ou num processo de anotação.",
+ "smw-datavalue-wikipage-empty": "O valor de entrada da página da wiki está vazio (por exemplo, <code>[[SomeProperty::]], [[]]</code>) e, portanto, não pode ser usado como nome ou como parte da condição de uma consulta.",
+ "smw-type-ref-rec": "\"$1\" é um tipo de dados [https://www.semantic-mediawiki.org/wiki/Container recipiente] (<i>container</i>) que permite registar informação adicional sobre a atribuição de um valor (por exemplo, dados de proveniência).",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "O tipo [[Special:Types/Reference|Referência]] espera que seja declarada uma lista de propriedades, usando a propriedade [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Tem campos].",
+ "smw-parser-invalid-json-format": "O analisador JSON terminou com \"$1\".",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" não pode ser usado como etiqueta preferida porque a língua \"$2\" já está atribuída à etiqueta \"$3\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Copiar hiperligação para a área de transferência",
+ "smw-property-userdefined-fixedtable": "\"$1\" estava configurada como [https://www.semantic-mediawiki.org/wiki/Fixed_properties propriedade fixa] e qualquer modificação da sua [https://www.semantic-mediawiki.org/wiki/Type_declaration declaração de tipo] requer: ou que seja executado <code>setupStore.php</code>, ou que seja completada a tarefa especial [[Special:SemanticMediaWiki|\"Instalação e atualização da base de dados\"]].",
+ "smw-data-lookup": "A obter dados...",
+ "smw-data-lookup-with-wait": "O pedido está a ser processado e pode levar algum tempo.",
+ "smw-no-data-available": "Não há dados disponíveis.",
+ "smw-property-req-violation-missing-fields": "Faltam, à propriedade \"$1\", detalhes de declaração para o tipo anotado \"$2\" porque não foi definida a propriedade <code>Tem campos</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "Faltam, à propriedade \"$1\", detalhes de declaração para o tipo anotado porque não foi definida a propriedade <code>URI do formatador externo</code>.",
+ "smw-property-req-violation-predefined-type": "A propriedade \"$1\", como propriedade predefinida, contém uma declaração de tipo \"$2\" que é incompatível com o tipo padrão desta propriedade.",
+ "smw-property-req-violation-import-type": "Foi detetada uma declaração de tipo que é incompatível com o tipo predefinido do vocabulário importado \"$1\". Em geral, não é necessário declarar um tipo porque são obtidas informações da definição da importação.",
+ "smw-property-req-violation-change-propagation-locked-error": "A propriedade \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A página da propriedade foi trancada até que a atualização da especificação primária esteja finalizada para impedir interrupções intermédias ou especificações contraditórias. O processo pode demorar algum tempo até que a página possa ser destrancada porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas].",
+ "smw-property-req-violation-change-propagation-locked-warning": "A propriedade \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A atualização pode demorar algum tempo porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas] e é sugerido que as alterações da propriedade sejam adiadas para impedir interrupções intermédias ou especificações contraditórias.",
+ "smw-property-req-violation-change-propagation-pending": "Estão pendentes atualizações devidas à [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações] ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tarefa estimada|tarefas estimadas}}]) e é recomendado que não altere propriedades até que o processo esteja finalizado, para impedir interrupções intermédias ou especificações contraditórias.",
+ "smw-property-req-violation-missing-maps-extension": "O MediaWiki Semântico não conseguiu detetar a extensão [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"], que é um pré-requisito para o tipo selecionado, e, em consequência, limita a funcionalidade desta propriedade.",
+ "smw-property-req-violation-type": "A propriedade contém especificações de tipo concorrentes, que podem resultar em anotações de valor inválidas; é, portanto, esperado que um utilizador atribua um tipo adequado.",
+ "smw-change-propagation-protection": "Esta página está trancada para evitar modificações acidentais dos dados durante uma atualização devida à [https://www.semantic-mediawiki.org/wiki/change_propagation propagação de alterações]. O processo pode demorar algum tempo a destrancar a página porque depende do tamanho e da frequência da gestão da [https://www.mediawiki.org/wiki/manual:Job_queue fila de tarefas].",
+ "smw-category-change-propagation-locked-error": "A categoria \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. Entretanto, a página da categoria foi trancada até que a atualização da especificação primária esteja finalizada para impedir interrupções intermédias ou especificações contraditórias. O processo pode demorar algum tempo até destrancar a página porque depende do tamanho e da frequência da gestão da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas].",
+ "smw-category-change-propagation-locked-warning": "A categoria \"$1\" foi alterada e requer que as entidades atribuídas sejam reavaliadas usando um processo de [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações]. A atualização pode demorar algum tempo porque depende do tamanho e frequência da [https://www.mediawiki.org/wiki/Manual:Job_queue fila de tarefas] e é sugerido que adie alterações à categoria para impedir interrupções intermédias ou especificações contraditórias.",
+ "smw-category-change-propagation-pending": "Estão pendentes atualizações devidas à [https://www.semantic-mediawiki.org/wiki/Change_propagation propagação de alterações] ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|tarefa estimada|tarefas estimadas}}]) e é recomendado que aguarde até que o processo esteja finalizado antes de fazer alterações à categoria, para impedir interrupções intermédias ou especificações contraditórias.",
+ "smw-category-invalid-value-assignment": "\"$1\" não é reconhecido como categoria válida ou anotação de valor.",
+ "protect-level-smw-pageedit": "Permitir só utilizadores com permissão de edição de páginas (MediaWiki Semântico)",
+ "smw-create-protection": "A criação da propriedade \"$1\" está restrita a utilizadores com o direito \"$2\" ou um [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de utilizadores] apropriado enquanto o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade] está ativado.",
+ "smw-create-protection-exists": "A alteração da propriedade \"$1\" está restrita a utilizadores com o direito \"$2\" ou um [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de utilizadores] apropriado enquanto o [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode modo de autoridade] está ativado.",
+ "smw-edit-protection": "Esta página está [[Property:Is edit protected|protegida]] para impedir a modificação acidental de dados e só pode ser editada por utilizadores com o direito de edição (\"$1\") ou que estão no [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups grupo de utilizadores] apropriado.",
+ "smw-edit-protection-disabled": "A proteção contra edições foi desativada, portanto, a propriedade \"$1\" não pode ser usada para proteger páginas de entidades contra edições não autorizadas.",
+ "smw-edit-protection-auto-update": "O MediaWiki Semântico atualizou o estado de proteção de acordo com a propriedade \"Está protegida contra edições\".",
+ "smw-edit-protection-enabled": "Protegida contra edições (MediaWiki Semântico)",
+ "smw-patternedit-protection": "Esta página está protegida e só pode ser editada por utilizadores com a <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissão] apropriada.",
+ "smw-property-predefined-edip": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para indicar se a página está protegida contra edições ou não.",
+ "smw-property-predefined-long-edip": "Embora qualquer utilizador possa adicionar esta propriedade a um assunto, só um utilizador com uma permissão específica pode editar ou revogar a proteção de uma entidade após essa proteção ter sido adicionada.",
+ "smw-query-reference-link-label": "Referência de consulta",
+ "smw-format-datatable-emptytable": "Não há dados disponíveis na tabela",
+ "smw-format-datatable-info": "A mostrar entradas _START_ a _END_, de _TOTAL_",
+ "smw-format-datatable-infoempty": "A mostrar entradas 0 a 0, de 0",
+ "smw-format-datatable-infofiltered": "(filtradas de _MAX_ entradas no total)",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-lengthmenu": "Mostrar _MENU_ entradas",
+ "smw-format-datatable-loadingrecords": "A carregar…",
+ "smw-format-datatable-processing": "A processar...",
+ "smw-format-datatable-search": "Pesquisar:",
+ "smw-format-datatable-zerorecords": "Não foram encontrados registos com correspondências",
+ "smw-format-datatable-first": "Primeiro",
+ "smw-format-datatable-last": "Último",
+ "smw-format-datatable-next": "Próximo",
+ "smw-format-datatable-previous": "Anterior",
+ "smw-format-datatable-sortascending": ": ativar para ordenar a coluna por ordem crescente",
+ "smw-format-datatable-sortdescending": ": ativar para ordenar a coluna por ordem decrescente",
+ "smw-format-datatable-toolbar-export": "Exportar",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "A categoria \"$1\" contém um destino inválido de redirecionamento para um espaço nominal que não é de categorias.",
+ "smw-parser-function-expensive-execution-limit": "A função do analisador sintático atingiu o limite para execuções de funções exigentes (consulte o parâmetro de configuração [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "O MediaWiki Semântico está a recarregar a página atual devido à necessidade de algum processamento após a consulta.",
+ "apihelp-smwinfo-summary": "Módulo da API para obter informação sobre estatísticas e outra metainformação do MediaWiki Semântico.",
+ "apihelp-ask-summary": "Módulo da API para consultar o MediaWiki Semântico usando a linguagem \"ask\".",
+ "apihelp-askargs-summary": "Módulo da API para consultar o MediaWiki Semântico usando a linguagem \"ask\" na forma de lista de condições, propriedades a mostrar e parâmetros.",
+ "apihelp-browsebyproperty-summary": "Módulo da API para obter informação sobre uma propriedade ou lista de propriedades.",
+ "apihelp-browsebysubject-summary": "Módulo da API para obter informação sobre um assunto.",
+ "apihelp-smwtask-summary": "Módulo da API para executar tarefas relacionadas com o MediaWiki Semântico.",
+ "apihelp-smwbrowse-summary": "Módulo da API para suportar atividades de navegação para diferentes tipos de entidades no MediaWiki Semântico.",
+ "apihelp-ask-parameter-api-version": "Formatação da saída:\n;2:Formato compatível com versões anteriores, usando {} para a lista de resultados.\n;3:Formato experimental, usando [] como lista de resultados.",
+ "smw-api-invalid-parameters": "Parâmetros inválidos, \"$1\"",
+ "smw-parser-recursion-level-exceeded": "O nível de $1 recursões foi excedido durante um processo de análise sintática. É sugerido que valide a estrutura de predefinições, ou que ajuste o parâmetro de configuração <code>$maxRecursionDepth</code> se necessário.",
+ "smw-property-page-list-count": "A apresentar {{PLURAL:$1|uma página que usa|$1 páginas que usam}} esta propriedade.",
+ "smw-property-page-list-search-count": "A apresentar {{PLURAL:$1|uma página que usa|$1 páginas que usam}} esta propriedade com uma correspondência de valor \"$2\".",
+ "smw-property-reserved-category": "Categoria",
+ "smw-category": "Categoria",
+ "smw-datavalue-uri-invalid-scheme": " \"$1\" não foi listado como um esquema URI válido.",
+ "smw-browse-property-group-title": "Grupo de propriedades",
+ "smw-browse-property-group-label": "Etiqueta do grupo de propriedades",
+ "smw-browse-property-group-description": "Descrição do grupo de propriedades",
+ "smw-property-predefined-ppgr": "\"$1\" é uma propriedade predefinida que identifica as entidades (principalmente as categorias) que são utilizadas como instâncias de agrupamento para as propriedades, e é fornecida pela extensão [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-filter": "Filtro",
+ "smw-section-expand": "Expandir a secção",
+ "smw-section-collapse": "Recolher a secção",
+ "smw-ask-format-help-link": "Formato [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Ajuda",
+ "smw-cheat-sheet": "Auxiliar",
+ "smw-personal-jobqueue-watchlist": "Lista de vigilância (fila de tarefas)",
+ "smw-property-predefined-label-skey": "Chave de ordenação",
+ "smw-processing": "A processar...",
+ "smw-redirect-target-unresolvable": "O destino é irresolúvel pela razão \"$1\"",
+ "smw-types-title": "Tipo: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "Não é permitido alterar o modelo de conteúdo de uma [https://www.semantic-mediawiki.org/wiki/Help:Schema página de esquema].",
+ "smw-schema-namespace-edit-protection": "Esta página está protegida e só pode ser editada por utilizadores com a [https://www.semantic-mediawiki.org/wiki/Help:Permissions permissão] <code>smw-schemaedit</code> adequada.",
+ "smw-schema-error": "Erro de validação",
+ "smw-schema-error-schema": "A especificação '''$1''' e a sua validação para o esquema corrente identificou as seguintes incompatibilidades ou violações:",
+ "smw-schema-error-violation": "Violação (\"$1\", \"$2\")",
+ "smw-schema-error-type-missing": "Falta um tipo ao conteúdo para este ser reconhecido e utilizável no [https://www.semantic-mediawiki.org/wiki/Help:Schema espaço nominal/domínio do esquema].",
+ "smw-schema-error-type-unknown": "O tipo \"$1\" não está registado e, portanto, não pode ser usado para conteúdo no [https://www.semantic-mediawiki.org/wiki/Help:Schema espaço nominal/domínio do esquema].",
+ "smw-schema-title": "Esquema",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "Tipo",
+ "smw-schema-description-link-format-schema": "É esperado que este tipo de esquema defina características para criar hiperligações dependentes do contexto com relação a uma propriedade atribuída de [[Property:Formatter schema|esquema formatador]].",
+ "smw-schema-description-search-form-schema": "É esperado que este tipo de esquema seja usado para definir formulários de entrada, e características para o perfil de [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch pesquisa avançada], onde este contém instruções para gerar campos de introdução de dados, definir espaços nominais /domínios padrão, ou declarar expressões prefixo para um pedido de pesquisa.",
+ "smw-schema-tag": "{{PLURAL:$1|Etiqueta|Etiquetas}}",
+ "smw-property-predefined-schema-desc": "\"$1\" é uma propriedade predefinida que armazena uma descrição de esquema, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-schema-def": "\"$1\" é uma propriedade predefinida que que armazena o conteúdo do esquema, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-schema-tag": "\"$1\" é uma propriedade predefinida fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico] para identificar um conjunto de esquemas.",
+ "smw-property-predefined-long-schema-tag": "Uma etiqueta que identifica esquemas de conteúdos ou características semelhantes.",
+ "smw-property-predefined-schema-type": "\"$1\" é uma propriedade predefinida que descreve um tipo de esquema identificável, fornecida pelo [https://www.semantic-mediawiki.org/wiki/Help:Special_properties MediaWiki Semântico].",
+ "smw-property-predefined-long-schema-type": "É esperado que cada [https://www.semantic-mediawiki.org/wiki/Help:Schema/Type tipo] forneça a sua própria interpretação dos elementos sintáticos e das restrições sintáticas, e os expresse através de um [https://www.semantic-mediawiki.org/wiki/Help:Schema#validation modelo de validação].",
+ "smw-ask-title-keyword-type": "Procurar palavra-chave",
+ "smw-ask-message-keyword-type": "Esta pesquisa coincide com a condição <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Não foi possível estabelecer ligação ao destino \"$1\" remoto.",
+ "smw-remote-source-disabled": "A fonte '''$1''' desativou o suporte de pedidos remotos!",
+ "smw-remote-source-unmatched-id": "A fonte '''$1''' não corresponde a nenhuma versão do MediaWiki Semântico que possa suportar um pedido remoto.",
+ "smw-remote-request-note": "O resultado é obtido da fonte remota '''$1''' e é provável que o conteúdo gerado contenha informação que não está disponível na wiki corrente.",
+ "smw-remote-request-note-cached": "O resultado é '''armazenado em cache''' a partir da fonte remota '''$1''' e é provável que o conteúdo gerado contenha informação que não está disponível na wiki corrente.",
+ "smw-parameter-missing": "O parâmetro \"$1\" está em falta.",
+ "smw-property-tab-usage": "Uso",
+ "smw-property-tab-redirects": "Sinónimos",
+ "smw-property-tab-subproperties": "Subpropriedades",
+ "smw-property-tab-errors": "Atribuições indevidas",
+ "smw-property-tab-specification": "... mais",
+ "smw-concept-tab-list": "Lista",
+ "smw-concept-tab-errors": "Erros",
+ "smw-ask-tab-result": "Resultado",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Despistagem de defeitos",
+ "smw-ask-tab-code": "Código",
+ "smw-install-incomplete-intro": "A instalação (ou atualização) do <b>MediaWiki Semântico</b> não foi finalizada e um administrador deve executar as seguintes tarefas para evitar inconsistências de dados antes que os utilizadores continuem a criar ou alterar conteúdos.",
+ "smw-install-incomplete-populate-hash-field": "O preenchimento do campo <code>smw_hash</code> foi saltado durante a configuração; é necessário executar o ''script'' [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php].",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/qqq.json b/www/wiki/extensions/SemanticMediaWiki/i18n/qqq.json
new file mode 100644
index 00000000..79fe615e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/qqq.json
@@ -0,0 +1,867 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kghbln",
+ "Liuxinyu970226",
+ "Purodha",
+ "Raymond",
+ "Shirayuki",
+ "Siebrand",
+ "Umherirrender",
+ "Felipe95a",
+ "Macofe",
+ "Nemo bis",
+ "Nike",
+ "Ochaochaocha3",
+ "Hamilton Abreu",
+ "Robby",
+ "McDutchie",
+ "Avatar6"
+ ]
+ },
+ "smw-desc": "{{desc|name=Semantic MediaWiki|url=https://www.mediawiki.org/wiki/Extension:Semantic_MediaWiki}}",
+ "smw-title": "{{notranslate}}\n\nName of the extension as it should be displayed.\n\n{{Identical|Semantic MediaWiki}}",
+ "smw-upgrade-error": "This is an error message.\n\nParameter:\n* $1 - holds the upgrade key",
+ "smw-upgrade-error-why-title": "This is a section header.",
+ "smw-upgrade-error-why-explain": "This is an error message.",
+ "smw-upgrade-error-how-title": "This is a section header.",
+ "smw-upgrade-error-how-explain": "This is an error message.",
+ "smw-semantics-not-enabled": "This is an informatory message.",
+ "smw_viewasrdf": "This is the text of the link that is triggering the results export when clicked. It appears on pages in namespace Concept: or within the [https://semantic-mediawiki.org/wiki/Help:Browsing_interfaces#The_factbox factbox].",
+ "smw_finallistconjunct": "The last separator in a list. For example a list could look like this: \"A, B, and C\". The comma might not be appropriate in your language.\n\t{{Identical|And}}",
+ "smw-factbox-head": "This is the intro of tab.\n\nParameter:\n* $1 - holds the name of a page",
+ "smw-factbox-facts": "This is the title of a tab.\n{{Identical|Fact}}",
+ "smw-factbox-facts-help": "This is a information shown as a tooltip when hovering over a tab.",
+ "smw-factbox-facts-derived": "This is the title of a tab.",
+ "smw-factbox-facts-derived-help": "This is a information shown as a tooltip when hovering over a tab.",
+ "smw_isspecprop": "Used on [[Special:Properties]] to identify a special (build in) property.",
+ "smw-concept-cache-header": "This is a section header on a page containing a concept.",
+ "smw-concept-cache-count": "This is an informatory message shown on the page containing a concept.\n\nParameter:\n* $1 holds the number of cached objects.\n* $2 holds the timestamp the cache was generated.",
+ "smw-concept-no-cache": "This is an informatory message.",
+ "smw_concept_description": "Title of the section containing the code of a [https://semantic-mediawiki.org/wiki/Help:Concepts concept] (special precomputable query).\n\nParameters:\n* $1 - the name of the concept",
+ "smw_no_concept_namespace": "This is an error/warning message that appears when a concept is added to a page in namespaces other than Concept:.",
+ "smw_multiple_concepts": "This is an error/warning message that appears when more than concept is added to a page in namespaces Concept:.",
+ "smw_concept_cache_miss": "This is an information message. Parameters:\n* $1 holds the name of the [https://semantic-mediawiki.org/wiki/Help:Concepts concept] (special precomputable query)",
+ "smw_noinvannot": "This is an information message.",
+ "version-semantic": "This is the name of the extension group on special page [[Special:Version|\"Version\"]].",
+ "smw_uri_blacklist": "{{notranslate}}",
+ "smw_baduri": "This is an information message. Parameters:\n* $1 holds the malformed URI.",
+ "smw_csv_link": "This is the text of the link that is triggering the results export to a CSV-file when clicked.\n\n{{optional}}",
+ "smw_dsv_link": "This is the text of the link that is triggering the results export to a DSV-file when clicked.\n\n{{optional}}",
+ "smw_json_link": "This is the text of the link that is triggering the results export to a JSON-file when clicked.\n\n{{optional}}",
+ "smw_rdf_link": "This is the text of the link that is triggering the results export to a RDF-file when clicked.\n\n{{optional}}",
+ "smw_printername_count": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Count_format count].",
+ "smw_printername_csv": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:CSV_format CSV].",
+ "smw_printername_dsv": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:DSV_format DSV].",
+ "smw_printername_debug": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Debug_format Debug].",
+ "smw_printername_embedded": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Embedded_format Embedded].",
+ "smw_printername_json": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:JSON_format JSON].",
+ "smw_printername_list": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:List_format List].\n{{Identical|List}}",
+ "smw_printername_plainlist": "This is the name of a result format.",
+ "smw_printername_ol": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Ol_format Enumeration (numbered list)].",
+ "smw_printername_ul": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Ul_format Itemization (bulleted list)].",
+ "smw_printername_table": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Table_format Table].\n\n{{Identical|Table}}",
+ "smw_printername_broadtable": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Broadtable_format Broadtable].",
+ "smw_printername_template": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Template_format Template].\n\n{{Identical|Template}}",
+ "smw_printername_templatefile": "This is the name of the result format [https://www.semantic-mediawiki.org/wiki/Help:Templatefile_format templatefile].",
+ "smw_printername_rdf": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:RDF_format RDF].",
+ "smw_printername_category": "This is the name of the result format [https://semantic-mediawiki.org/wiki/Help:Category_format Category].\n\n{{Identical|Category}}",
+ "validator-type-class-SMWParamSource": "This is the description of the \"source\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].\n{{Identical|Text}}",
+ "smw-paramdesc-limit": "This is the description of the \"limit\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-offset": "This is the description of the \"offset\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-headers": "This is the description of the \"headers\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-mainlabel": "This is the description of the \"mainlabel\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-link": "This is the description of the \"link\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-intro": "This is the description of the \"intro\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-outro": "This is the description of the \"outro\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-default": "This is the description of the \"default\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-sep": "This is the description of the \"sep\" (result separator) parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-propsep": "This is the description of the \"propsep\" (property separator) parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-valuesep": "This is the description of the \"valuesep\" (value separator) parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-showsep": "This is the description of the \"showsep\" (show value separator at top of csv file) parameter for the \"csv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].\n{{doc-important|Do not translate the parameter name \"sep\". However <value> may be translated.}}",
+ "smw-paramdesc-distribution": "This is the description of the \"distribution\" parameter for the \"jqplotbar\" and \"jqplotpie\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-distributionsort": "This is the description of the \"distributionsort\" parameter for the \"jqplotbar\" and \"jqplotpie\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-distributionlimit": "This is the description of the \"distributionlimit\" parameter for the \"jqplotbar\" and \"jqplotpie\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-aggregation": "This is the description of the \"aggregation\" parameter for the \"jqplotchart\" and \"sparkline\" [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-template": "This is the description of the \"introtemplate\" parameter of the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-columns": "This is the description of the \"columns\" parameter of the \"category\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].\n\nParameters:\n* $1 - default value",
+ "smw-paramdesc-userparam": "This is the description of the \"userparam\" parameter for the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-class": "This is the description of the \"css\" parameter of the \"list\"/\"ul\"/\"ol\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-introtemplate": "This is the description of the \"introtemplate\" parameter of the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-outrotemplate": "This is the description of the \"outrotemplate\" parameter of the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-embedformat": "This is the description of the \"embedformat\" parameter of the \"embedded\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-embedonly": "This is the description of the \"embedonly\" parameter of the \"embedded\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-table-class": "This is the description of the \"css\" parameter of the \"table\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-table-transpose": "This is the description of the \"transpose\" parameter of the \"table\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-rdfsyntax": "This is the description of the \"syntax\" parameter for the \"rdf\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-csv-sep": "This is the description of the \"separator\" parameter for the \"csv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-csv-valuesep": "This is the description of the \"valuesseparator\" parameter for the \"csv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-csv-merge": "This is the description of the \"merge\" parameter for the \"csv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-csv-bom": "This is the description of the \"bom\" parameter for the \"csv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries]. BOM as in [https://en.wikipedia.org/wiki/Byte_order_mark BOM].",
+ "smw-paramdesc-dsv-separator": "This is the description of the \"separator\" parameter for the \"dsv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-dsv-filename": "This is the description of the \"filename\" parameter for the \"dsv\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-filename": "This is the description of the \"filename\" parameter for the [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-smwdoc-description": "This is the short description of the [https://semantic-mediawiki.org/wiki/Help:Generating_documentation smwdoc parser function].",
+ "smw-smwdoc-default-no-parameter-list": "This is an informatory message.",
+ "smw-smwdoc-par-format": "This is the description of the parameter \"format\" of the [https://semantic-mediawiki.org/wiki/Help:Generating_documentation smwdoc parser function].",
+ "smw-smwdoc-par-parameters": "This is the description of the parameter \"parameters\" of the [https://semantic-mediawiki.org/wiki/Help:Generating_documentation smwdoc parser function]. {{doc-important|Do not translate the possible parameter values \"specific\", \"base\" and \"all\".}}",
+ "smw-paramdesc-sort": "This is the description of the \"sort\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-order": "This is the description of the \"order\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-searchlabel": "This is the description of the \"searchlabel\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries]. \"… further results\" must be identical to message {{msg-mw|Smw iq moreresults}}.",
+ "smw-paramdesc-named_args": "This is the description of the \"named args\" parameter of the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-template-arguments": "This is the description of the \"template arguments\" parameter of the \"template\", \"list\", \"ol\" and \"ul\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-import-annotation": "This is the description of the \"import-annotation\" parameter of the \"template\" and \"list\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-export": "This is the description of the \"export\" parameter of the \"json\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-prettyprint": "This is the description of the \"[[wikipedia:prettyprint|prettyprint]]\" parameter of the \"JSON\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-json-unescape": "This is the description of the \"unescaped\" parameter of the \"json\" [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-json-type": "This is the description of the \"type\" parameter for the \"json\" [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-source": "This is the description of the \"source\" parameter for [https://semantic-mediawiki.org/wiki/Help:Inline_queries#Standard_parameters_for_inline_queries inline queries].",
+ "smw-paramdesc-jsonsyntax": "This is the description of the \"syntax\" parameter used by [http://www.semantic-mediawiki.org/wiki/Help:JSON_format].",
+ "smw-printername-feed": "This is the name of the [https://semantic-mediawiki.org/wiki/Help:Feed_format Feed result format].",
+ "smw-paramdesc-feedtype": "This is the description of the feed type",
+ "smw-paramdesc-feedtitle": "This is the description of the \"title\" parameter of the \"feed\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-feeddescription": "This is the description of the \"description\" parameter for the \"feed\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-feedpagecontent": "This option allows to enable page content to be generated with the feed",
+ "smw-label-feed-link": "This is the text of the link that is triggering the results export to a RSS-file when clicked. {{optional}}",
+ "smw-label-feed-description": "This message is displayed as the feed description, e.g. \"News on translatewiki.net rss feed\".\n\nParameters:\n* $1 - a feed description, e.g. \"News on translatewiki.net\"\n* $2 - a feed type. either \"rss\" or \"atom\"\n{{Identical|Feed}}",
+ "smw-paramdesc-mimetype": "This is the description of the \"mimetype\" parameter of the \"templatefile\" [https://www.semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw_iq_disabled": "This is an information message.",
+ "smw_iq_moreresults": "This is the text of the link that points to further results in case the [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline query] has more than currently displayed on the page. See also message {{msg-mw|Smw-paramdesc-searchlabel}}.",
+ "smw_parseerror": "This is an error/warning message.",
+ "smw_decseparator": "{{optional}}\n{{Doc-important|In case you add a separator symbol here, make sure that you also add the corresponding symbol to {{Msg-mw|smw_kiloseparator}} or add an empty translation if no matching symbol is used in your language!!!}}\nThis message is as a separator symbol for decimal digits in numbers, like \".\" in English 1,234.23. It is used for formatting number output '''and''' for reading user input. Therefore it should be carefully considered whether to change an existing value, since existing installations may depend on this value for their content to be read properly.\n\nNote that spaces and space-like HTML entities are always ignored when reading numbers.",
+ "smw_kiloseparator": "{{optional}}\n{{Doc-important|In case you add a separator symbol here, make sure that you also add the corresponding symbol to {{Msg-mw|smw_decseparator}} or add an empty translation if no matching symbol is used in your language!!!}}\nThis message is as a separator symbol for thousands in numbers, like \",\" in English 1,234.23. It is used for formatting number output '''and''' for reading user input. Therefore it should be carefully considered whether to change an existing value, since existing installations may depend on this value for their content to be read properly.\n\nNote that spaces and space-like HTML entities are always ignored when reading numbers, whether or not a space symbol is used here.",
+ "smw_notitle": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_noproperty": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_wrong_namespace": "This is an error/warning message. Parameters:\n* $1 holds the name of the namespace that may be used.",
+ "smw_manytypes": "This is an error/warning message.",
+ "smw_emptystring": "This is an error/warning message.",
+ "smw_notinenum": "This is an error/warning message.\n\nParameters:\n* $1 holds the property value causing the error/warning.\n* $2 holds the property values that may be used.\n* $3 holds the property name.",
+ "smw-datavalue-constraint-error-allows-value-list": "This is an error message.\n\nParameters:\n* $1 - holds the property value\n* $2 - holds the name of the list containing property values\n* $3 - holds the name of the property",
+ "smw-datavalue-constraint-error-allows-value-range": "This is an error message.\n\nParameters:\n* $1 - holds the property value\n* $2 - holds the name of the range for property values\n* $3 - holds the name of the property",
+ "smw_noboolean": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_true_words": "These are the boolean values for \"true\" which may be assigned to properties of data type [https://semantic-mediawiki.org/wiki/Help:Type_Boolean Boolean], given as a comma separated list.\n\nSee also:\n* {{msg-mw|Smw false words}}",
+ "smw_false_words": "These are the boolean values for \"false\" which may be assigned to properties of data type [https://semantic-mediawiki.org/wiki/Help:Type_Boolean Boolean], given as a comma separated list.\n\nSee also:\n* {{msg-mw|Smw true words}}",
+ "smw_nofloat": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_infinite": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_unitnotallowed": "This is an error/warning message. Parameters:\n* $1 holds the unit causing the error/warning.",
+ "smw_nounitsdeclared": "This is an error/warning message.",
+ "smw_novalues": "This is an error/warning message.",
+ "smw_nodatetime": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_toomanyclosing": "This is an error/warning message. Parameters:\n* $1 holds the string causing the error/warning.",
+ "smw_noclosingbrackets": "This is an error/warning message.",
+ "smw_misplacedsymbol": "This is an error/warning message. Parameters:\n* $1 holds the string causing the error/warning.",
+ "smw_unexpectedpart": "This is an error/warning message. Parameters:\n* $1 holds the string causing the error/warning.",
+ "smw_emptysubquery": "This is an error/warning message.",
+ "smw_misplacedsubquery": "This is an error/warning message.",
+ "smw_valuesubquery": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_badqueryatom": "This is an error/warning message.",
+ "smw_propvalueproblem": "This is an error/warning message. Parameters:\n* $1 holds the property value causing the error/warning.",
+ "smw_noqueryfeature": "This is an error/warning message. Parameters:\n* $1 holds the part(s) of the query which were dropped.",
+ "smw_noconjunctions": "This is an error/warning message. Parameters:\n* $1 holds the part(s) of the query which were dropped.",
+ "smw_nodisjunctions": "This is an error/warning message. Parameters:\n* $1 holds the part(s) of the query which were dropped.",
+ "smw_querytoolarge": "This is an error/warning message.\n\nParameters:\n* $1 holds the part(s) of the query which were dropped.\n* $2 holds the number of parts of the query which were dropped.",
+ "smw_notemplategiven": "This is an error/warning message.",
+ "smw_db_sparqlqueryproblem": "This is an error message.",
+ "smw_db_sparqlqueryincomplete": "This is an error message.",
+ "smw_type_header": "This is the header on [[Special:Types]]. Parameters:\n* $1 holds the name of the respective data type.",
+ "smw_typearticlecount": "This is the introductory message below the header on [[Special:Types]]. Parameters:\n* $1 holds the number of property pages displayed in the current view.",
+ "smw_attribute_header": "This is the header on a page in namespace \"Property\". Parameters:\n* $1 holds the name of the respective property.",
+ "smw_attributearticlecount": "This is the introductory message below the header on a page in namespace \"Property\". Parameters:\n* $1 holds the number of property pages displayed in the current view.",
+ "smw-propertylist-subproperty-header": "This is a section header on a page.",
+ "smw-propertylist-redirect-header": "This is a section header on a page.",
+ "smw-propertylist-error-header": "This is a section header of a property page.",
+ "smw-propertylist-count": "This is an informatory message.\n\nParameter:\n\n* $1 Number of related entities",
+ "smw-propertylist-count-with-restricted-note": "This is an informatory message.\n\nParameters:\n\n* $1 - Number of related entities\n* $2 - Maximum number of related entities that can be shown.",
+ "smw-propertylist-count-more-available": "This is an informatory message.\n\nParameter:\n\n* $1 Number of related entities",
+ "specialpages-group-smw_group": "{{optional}}\n{{doc-special-group|that=are related to (or depend on) Semantic MediaWiki|like=[[Special:OfflineImportLexicon]], [[Special:Ask]], [[Special:QueryCreator]], [[Special:Browse]], [[Special:PageProperty]], [[Special:SearchByProperty]], [[Special:SMWAdmin]], [[Special:ExportRDF]], [[Special:ObjectEditor]], [[Special:WidgetAssembler]], [[Special:WidgetClone]], [[Special:BrowseWiki]], [[Special:SolrSearch]]}}\n{{Identical|Semantic MediaWiki}}",
+ "exportrdf": "{{doc-special|ExportRDF}}",
+ "smw_exportrdf_docu": "This is the introductory message at the top of [[Special:ExportRDF]].",
+ "smw_exportrdf_recursive": "This is the text describing an option available to choose from on [[Special:ExportRDF]].",
+ "smw_exportrdf_backlinks": "This is the text describing an option available to choose from on [[Special:ExportRDF]].",
+ "smw_exportrdf_lastdate": "This is the text describing an optional input field available on [[Special:ExportRDF]].",
+ "smw_exportrdf_submit": "This is the name of the submit button on [[Special:ExportRDF]] to trigger the export.\n\n{{Identical|Export}}",
+ "uriresolver": "This is the name of [[Special:URIResolver]].",
+ "properties": "{{doc-special|Properties}}\n{{Identical|Property}}",
+ "smw_properties_docu": "This is the introductory message at the top of [[Special:Properties]].",
+ "smw_property_template": "This message is used on [[Special:Properties]] to display each property listed. It holds the following parameters:\n* $1 link to the property's page\n* $2 link to the property's data type page\n* $3 number of times the property is used",
+ "smw_property_template_notype": "This is used on [[Special:Properties]].\n*$1 holds the link to the property page.\n*$2 holds the number of times the property is used within the wiki.\n{{optional}}",
+ "smw_propertylackspage": "This is an error/warning message.",
+ "smw_propertylackstype": "This is an error/warning message. Parameters:\n* $1 holds the name of the assumed data type which is in fact always data type Page.",
+ "smw_propertyhardlyused": "This is an error/warning message.",
+ "smw-property-name-invalid": "This is an information message. Parameters:\n* $1 - property name",
+ "smw-property-name-reserved": "This is an error message.\n\nParameter:\n* $1 - holds the reserved name",
+ "smw-sp-property-searchform": "Introductory text for the property search form",
+ "smw-sp-property-searchform-inputinfo": "Additional explanatory text about the filtering condition.",
+ "smw-special-property-searchform": "Introductory text for the property search form",
+ "smw-special-property-searchform-inputinfo": "Additional explanatory text about the filtering condition.",
+ "smw-special-property-searchform-options": "This is a section header on special page \"Ask\".\n\n{{Identical|Options}}",
+ "smw-special-wantedproperties-filter-label": "This is a field label on special page \"WantedProperties\".\n\n{{Identical|Filter}}",
+ "smw-special-wantedproperties-filter-none": "This is an option selectable via a drop-down menu for filtering options on special page \"WantedProperties\". Translate in a sense: Apply no filter.\n\n{{Identical|None}}",
+ "smw-special-wantedproperties-filter-unapproved": "This is an option selectable via a drop-down menu on special page \"WantedProperties\".\nTranslate in a sense: Apply filter for Unapproved.\n\n{{Identical|Unapproved}}",
+ "smw-special-wantedproperties-filter-unapproved-desc": "This is an informative message.",
+ "concepts": "{{doc-special|Concepts}}\n{{Identical|Concept}}",
+ "smw-special-concept-docu": "This is an introductory message at the top of [[Special:Concepts]].",
+ "smw-special-concept-header": "This is a header used on [[Special:Concepts]].",
+ "smw-special-concept-count": "Used on [[Special:Concepts]] and to display available concepts.\n\nIf there are no concepts, {{msg-mw|Smw-special-concept-empty}} is used.\n\nParameters:\n* $1 - number of concepts",
+ "smw-special-concept-empty": "This is used on [[Special:Concepts]] and to display that no concepts are available.\n\nSee also:\n* {{msg-mw|Smw-sp-concept-count}} - if there are concepts",
+ "unusedproperties": "{{doc-special|UnusedProperties}}",
+ "smw-unusedproperties-docu": "This is the introductory message at the top of [[Special:UnusedProperties]].",
+ "smw-unusedproperty-template": "This message is used on [[Special:UnusedProperties]] to display each property listed. It holds the following parameters:\n* $1 link to the property's page\n* $2 link to the property's data type page",
+ "wantedproperties": "{{doc-special|WantedProperties}}",
+ "smw-wantedproperties-docu": "This is the introductory message at the top of [[Special:WantedProperties]].",
+ "smw-wantedproperty-template": "This is the message on [[Special:WantedProperties]] showing the name of the property without an assigned data type and how often it is used in the wiki.\n\nParameters:\n* $1 holds the name of the wanted property.\n* $2 holds the number of values annotated with the wanted property.",
+ "smw-special-wantedproperties-docu": "This is the introductory message at the top of special page \"WantedProperties\".",
+ "smw-special-wantedproperties-template": "This is the message on [[Special:WantedProperties]] showing the name of the property without an assigned data type and how often it is used in the wiki.\n\nParameters:\n* $1 holds the name of the wanted property.\n* $2 holds the number of values annotated with the wanted property.",
+ "smw_purge": "{{doc-actionlink}}\nThis is the label of a tab of an action item for the content area.\n{{Identical|Refresh}}",
+ "smw-purge-failed": "This is an error/warning message. Please translate consistent to {{msg-mw|smw_purge}}.",
+ "types": "{{doc-special|Types}}\n{{Identical|Type}}",
+ "smw_types_docu": "This is the introductory message at the top of [[Special:Types]].",
+ "smw-special-types-no-such-type": "Error message shown on [[Special:Types]] when specifying an invalid data type.\n\nParameter:\n* $1 - holds the constant of the specified datatype",
+ "smw-statistics": "Is a label that is displayed on the [[Special:Statistics|Statistics]] page.\n{{Identical|Semantic statistics}}",
+ "smw-statistics-property-instance": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of property values",
+ "smw-statistics-property-total": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of properties",
+ "smw-statistics-property-total-legacy": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of properties",
+ "smw-statistics-property-used": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameter:\n* $1 - number of properties",
+ "smw-statistics-property-page": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of properties",
+ "smw-statistics-property-type": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of properties",
+ "smw-statistics-query-inline-legacy": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page. See also message {{msg-mw|Smw-statistics-query-inline}}.\n\nParameters:\n* $1 - number of queries\n{{Identical|Query}}",
+ "smw-statistics-query-inline": "Used as a label linking to the respective property that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of queries\n{{Identical|Query}}",
+ "smw-statistics-query-size": "Is a label that is displayed on the [[Special:Statistics|Statistics]] page",
+ "smw-statistics-concept-count-legacy": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - concepts\n{{Identical|Concept}}",
+ "smw-statistics-concept-count": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of concepts\n{{Identical|Concept}}",
+ "smw-statistics-subobject-count": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of subobjects",
+ "smw-statistics-subobject-count-legacy": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of subobjects",
+ "smw-statistics-datatype-count": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of datatypes",
+ "smw-statistics-error-count": "Used as a label linking to the respective property that is displayed on the [[Special:Statistics|Statistics]] page. See also message {{msg-mw|smw-statistics-property-instance}}.\n\nParameters:\n* $1 - number of property values",
+ "smw-statistics-error-count-legacy": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of property values and improper annotations respectively",
+ "smw-statistics-delete-count": "Used as a label that is displayed on the [[Special:Statistics|Statistics]] page.\n\nParameters:\n* $1 - number of marked entities",
+ "smw_uri_doc": "{{doc-important|Do not alter or translate link targets!}}\nThis message sketches the (very technical) function of this unlisted special page.\nIt probably does not need translation in most cases. The special page as such is used in all URIs used by Semantic MediaWiki.\nWhen somebody resolves such a URI, the special page will redirect to the according wiki page or to the according metadata export (OWL/RDF/XML).\nThis is controlled by the HTTP request header. Semantic Web crawlers and browsers can thus request more metadata on a particular subject, while humans are referred to readable pages.\nThis method is called ''content negotiation''.\n\nParameters:\n* $1 - the URL http://www.w3.org/2001/tag/issues.html#httpRange-14 (hard-coded)",
+ "ask": "{{doc-special|Ask}}",
+ "smw_ask_doculink": "{{notranslate}}\nThis message should be the URL of the semantic query documentation for that language. For English, this is https://semantic-mediawiki.org/wiki/Help:Semantic_search. For other languages, it should be an ''existing'' page on semantic-mediawiki.org. Note that only some languages have own documentations yet.\n\nThe SMW Project welcomes documentation translators! Please contact Markus Krötzsch (https://semantic-mediawiki.org/wiki/Markus) for getting a login at semantic-mediawiki.org if you wish to create SMW handbooks in another language.",
+ "smw-ask-help": "This is an informatory message.",
+ "smw_ask_sortby": "This is the text describing the textbox on [[Special:Ask]] available for optionally entering a printout statement for a query.",
+ "smw_ask_ascorder": "This is the value in a dropdown menu on [[Special:Ask]] available for selecting a printout statement for a query.\n{{Identical|Ascending}}",
+ "smw_ask_descorder": "This is the value in a dropdown menu on [[Special:Ask]] available for selecting a printout statement for a query.\n{{Identical|Descending}}",
+ "smw-ask-order-rand": "This is an option selectable via a drop-down menu on special page \"Ask\".\n\n{{Identical|Random}}",
+ "smw_ask_submit": "This is the name of the submit button on [[Special:Ask]] to start the semantic search.\n\n{{Identical|Find results}}",
+ "smw_ask_editquery": "This is the text of the action link on [[Special:Ask]] to edit a query.",
+ "smw_add_sortcondition": "{{doc-actionlink}}\nThis is the text of the action link on [[Special:Ask]] to add a printout statement to a query.",
+ "smw-ask-sort-add-action": "This is the label of a link on special page \"Ask\".",
+ "smw_ask_hidequery": "This is the text of the action link on [[Special:Ask]] to abandon a query.",
+ "smw_ask_help": "This is the text of a link on [[Special:Ask]] pointing to the help page on [https://semantic-mediawiki.org/wiki/Help:Semantic_search semantic search].",
+ "smw_ask_queryhead": "This is the header of the edit field available on [[Special:Ask]] to enter the [https://semantic-mediawiki.org/wiki/Help:Selecting_pages query conditions] of the query.\n{{Identical|Condition}}",
+ "smw_ask_printhead": "This is the header of the edit field available on [[Special:Ask]] to enter the [https://semantic-mediawiki.org/wiki/Help:Displaying_information printout statements] of the query.",
+ "smw_ask_printdesc": "This is an additional information message for the header of the edit field available on [[Special:Ask]] to enter the [https://semantic-mediawiki.org/wiki/Help:Displaying_information printout statements] of the query.",
+ "smw_ask_format_as": "This is the text describing a dropdown box on [[Special:Ask]] available for choosing the [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for a query.",
+ "smw_ask_defaultformat": "This is the label used for indicating the default [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] within a dropdown field.\n\n{{Identical|Default}}",
+ "smw_ask_otheroptions": "This is the text for the header of a section on [[Special:Ask]] that is containing various different options to choose or enter in order to specify printout statements of a query.",
+ "smw-ask-otheroptions-info": "This is the text describing the section ''Other options'' on [[Special:Ask]] that is containing various different options to choose or enter in order to specify printout statements of a query.",
+ "smw-ask-otheroptions-collapsed-info": "Please use the plus icon to view all available options",
+ "smw_ask_show_embed": "{{doc-actionlink}}\nThis is the text of the action link on [[Special:Ask]] to show the code of a query.",
+ "smw_ask_hide_embed": "{{doc-actionlink}}\nThis is the text of the action link on [[Special:Ask]] to hide the code of a query.",
+ "smw_ask_embed_instr": "This is an information message for the field on [[Special:Ask]] that contains the code of a query that may be copied and when pasted into a regular page on the wiki..",
+ "smw-ask-delete": "This is the text of the action link on [[Special:Ask]] to delete a printout statement from a query.\n{{Identical|Remove}}",
+ "smw-ask-sorting": "This is the text for the header of the section on [[Special:Ask]] that allows to specify sorting options for specific properties that are queried for.",
+ "smw-ask-options": "This is a link label on special page \"Ask\".\n\n{{Identical|Options}}",
+ "smw-ask-options-sort": "This is a section header on special page \"Ask\".",
+ "smw-ask-format-options": "This is the header of a section in a tooltip.",
+ "smw-ask-parameters": "This is the header of a section in a tooltip.\n{{Identical|Parameter}}",
+ "smw-ask-search": "This is the label of an info box on \"Special:Ask\". Translate as a noun.\n\n{{Identical|Search}}",
+ "smw-ask-debug": "This is the label of a link on special page \"Ask\".\nTranslate in a sense of \"start debugging the issue\".\n\n{{Identical|Debug}}",
+ "smw-ask-debug-desc": "This is an explanatory message shown in a tooltip.",
+ "smw-ask-no-cache": "This is the label of a button. Translate in a sense that no cache should be used after clicking.",
+ "smw-ask-no-cache-desc": "This is an explanatory message shown in a tooltip.",
+ "smw-ask-result": "This is a link label on special page \"Ask\".\n\n{{Identical|Result}}",
+ "smw-ask-empty": "This is a link label on special page \"Ask\". Translate as an action: to empty something.",
+ "smw-ask-download-link-desc": "This is an explanatory message shown in a tooltip.",
+ "smw-ask-format": "This is a link label on special page \"Ask\".\n\n{{Identical|Format}}",
+ "smw-ask-format-selection-help": "This is a text displayed on [[Special:Ask]] to inform about a help page on [https://semantic-mediawiki.org/wiki/ Semantic MediaWiki].\n\nParameters:\n* $1 - a link which points to Help: page on semantic-mediawiki.org displaying the name of the format.",
+ "smw-ask-condition-change-info": "This is an informatory message shown at the top of special page \"Ask\".",
+ "smw-ask-input-assistance": "This is the header of a section in a tooltip.",
+ "smw-ask-condition-input-assistance": "This is an informatory message shown in a tooltip.",
+ "smw-ask-condition-input-assistance-property": "This is an informatory message shown in a tooltip.\n\n{{Doc-important|Do not change the prefix <code>p:</code>}}",
+ "smw-ask-condition-input-assistance-category": "This is an informatory message shown in a tooltip.\n\n{{Doc-important|Do not change the prefix <code>c:</code>}}",
+ "smw-ask-condition-input-assistance-concept": "This is an informatory message shown in a tooltip.\n\n{{Doc-important|Do not change the prefix <code>con:</code>}}",
+ "smw-ask-format-change-info": "This is an informatory message shown at the top of special page \"Ask\".",
+ "smw-ask-format-export-info": "This is an informatory message shown at the top of special page \"Ask\".",
+ "smw-ask-query-search-info": "An informatory message shown on \"Special:Ask\":\n\nParameters:\n* $1 shows the code of the query, e.g. <code><nowiki>[[Has number::+]]</nowiki></code>\n* $2 shows the name of the database backend that returned the answer, e.g. \"SMWSQLStore3\"\n* $3 indicates whether it is retrieved from cache or not \n* $4 shows the elapsed time needed to return the answer, e.g. 0.00635",
+ "searchbyproperty": "{{doc-special|SearchByProperty}}",
+ "processingerrorlist": "This is a header used on [[Special:ProcessingErrorList]].",
+ "propertylabelsimilarity": "This is the title of a special page.\n\n{{Identical|Property label similarity report}}",
+ "smw-processingerrorlist-intro": "This is an introductory message at the top of [[Special:ProcessingErrorList]].",
+ "smw_sbv_docu": "This is the information message shown on [[Special:SearchByProperty]] in case no property and no property value were not yet provided to search for.",
+ "smw_sbv_novalue": "This is an information message shown on [[Special:SearchByProperty]] in case no property value was provided.\n\nParameters:\n* $1 - the name of the property",
+ "smw_sbv_displayresult": "This is an information message shown on [[Special:SearchByProperty]] in case more than 20 results were found.\n\nParameters:\n* $1 - the name of the property\n* $2 - the value of the property one searched for",
+ "smw_sbv_displayresultfuzzy": "This is an information message shown on [[Special:SearchByProperty]] in case less than 20 results were found.\n\nParameters:\n* $1 - the name of the property\n* $2 - the value of the property one searched for",
+ "smw_sbv_property": "This is the text describing a textbox on [[Special:SearchByProperty]] where the name of a property is expected to be typed in.\n\n{{Identical|Property}}",
+ "smw_sbv_value": "This is the text describing a textbox on [[Special:SearchByProperty]] where the value of a property is expected to be typed in.\n\n{{Identical|Value}}",
+ "smw_sbv_submit": "This is the name of the submit button on [[Special:SearchByProperty]] to trigger the search.\n\n{{Identical|Find results}}",
+ "browse": "{{doc-special|Browse}}",
+ "smw_browselink": "This is the text of the link shown within the toolbox in the sidebar pointing to [[Special:Browse]]",
+ "smw_browse_article": "This is the text describing a textbox on [[Special:Browse]] where the name of a page is expected to be typed in.",
+ "smw_browse_go": "This is the name of the submit button on [[Special:Browse]] to trigger the browsing of pages.\n\n{{Identical|Go}}",
+ "smw_browse_more": "{{optional}}",
+ "smw_browse_show_incoming": "{{doc-actionlink}}\nThis is the text of the link on [[Special:Browse]] that shows the properties linking to a page when clicked.\n\nSee also:\n* {{msg-mw|Smw browse hide incoming}}",
+ "smw_browse_hide_incoming": "{{doc-actionlink}}\nThis is the text of the link on [[Special:Browse]] that hides the properties linking to a page when clicked.\n\nSee also:\n* {{msg-mw|Smw browse show incoming}}",
+ "smw_browse_no_outgoing": "This is the information message shown on [[Special:Browse]] in case the browsed page does not contain any properties.",
+ "smw_browse_no_incoming": "This is the information message shown on [[Special:Browse]] in case no properties link to the browsed page.",
+ "smw-browse-from-backend": "This is an informatory message shown while the data are being retrieved for display on this special page.",
+ "smw-browse-intro": "This is an informatory message explaining the purpose of the special page.",
+ "smw-browse-invalid-subject": "This is an error/warning message.\n\nParameters:\n* $1 holds the error/warning",
+ "smw-browse-api-subject-serialization-invalid": "This is an error message.",
+ "smw-browse-js-disabled": "This is an error message.",
+ "smw-browse-show-group": "{{doc-actionlink}}\nThis is the text of the link on [[Special:Browse]] that shows the property groups defined for certain properties.\n\nSee also:\n* {{msg-mw|Smw-browse-hide-group}}",
+ "smw-browse-hide-group": "{{doc-actionlink}}\nThis is the text of the link on [[Special:Browse]] that hides the property groups defined for certain properties.\n\nSee also:\n* {{msg-mw|Smw-browse-show-group}}",
+ "smw-noscript": "This is an informatory message.",
+ "smw_inverse_label_default": "Inverse label default. Parameters:\n* $1 - a place marker\n{{Identical|Of}}",
+ "smw_inverse_label_property": "Can as well be translated as \"Name of\", or \"Denomination of the inverse propery\". The inverse property is one having both its direction, and its source/target sets inverted. For example, the property \"is child of\" is the inverse propery of \"is parent of\".",
+ "pageproperty": "This is the name of [[Special:PageProperty]].",
+ "smw_pp_docu": "This is the introductory message at the top of [[Special:PageProperty]].",
+ "smw_pp_from": "This is the text describing a textbox on [[Special:PageProperty]] where the name of a page is expected to be typed in.\n{{Identical|From page}}",
+ "smw_pp_type": "This is the text describing a textbox on [[Special:PageProperty]] where the name of a property is expected to be typed in.\n\n{{Identical|Property}}",
+ "smw_pp_submit": "This is the name of the submit button on [[Special:PageProperty]] to trigger the search.\n\n{{Identical|Find results}}",
+ "smw_result_prev": "This is the text of the link on [[Special:Ask]] or [[Special:SearchByProperty]] that allows to return to the previous page containing results for the query.\n\n{{Identical|Previous}}",
+ "smw_result_next": "This is the text of the link on [[Special:Ask]] or [[Special:SearchByProperty]] that allows to proceed to the next page containing results for the query.\n\n{{Identical|Next}}",
+ "smw_result_results": "This is the text on [[Special:Ask]] or [[Special:SearchByProperty]] preceding the shown range of results which were returned by the query, e.g. '''Results 1 - 50'''.\n\n{{Identical|Result}}",
+ "smw_result_noresults": "This is the text on [[Special:Ask]] or [[Special:SearchByProperty]] shown in case a query does not return any results.\n{{Identical|No result}}",
+ "smwadmin": "{{doc-special|SMWAdmin}}",
+ "smw-admin-statistics-job-title": "This is a header.",
+ "smw-admin-statistics-job-docu": "This is an informatory message.",
+ "smw-admin-statistics-querycache-title": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-statistics-querycache-disabled": "This is an informatory message.",
+ "smw-admin-statistics-querycache-explain": "This is an informatory message shown on a special page explaining data statistics relating to the cache usage.\n{{doc-important|Do not translate \"misses\", \"deletes\", \"hits\", \"medianRetrievalResponseTime\", and \"noCache\" tags, though you are free to change the style of quotation marks and to add aditional explanation}}",
+ "smw-admin-permission-missing": "This is an informatory message.",
+ "smw-admin-setupsuccess": "This is the confirmation message at the bottom of the script's report concerning setup or update of the SMW database tables (process triggered from [[Special:SMWAdmin]]).",
+ "smw_smwadmin_return": "This is the text of the link at the bottom of the report concerning setup or update of the SMW database tables pointing to [[Special:SMWAdmin]].\n\nParameters:\n* $1 - the link back to Special:SMWAdmin\n{{Identical|Return to $1}}",
+ "smw_smwadmin_updatestarted": "This is the message at the top of [[Special:SMWAdmin]] confirming that the update process has been started.\n\nParameters:\n* $1 - the link back to Special:SMWAdmin",
+ "smw_smwadmin_updatenotstarted": "This is the message at the top of [[Special:SMWAdmin]] informing that a new update process will not be started.\n\nParameters:\n* $1 - the link back to Special:SMWAdmin",
+ "smw_smwadmin_updatestopped": "This is the message at the top of [[Special:SMWAdmin]] confirming that the update process has been stopped.\n\nParameters:\n* $1 - the link back to Special:SMWAdmin",
+ "smw_smwadmin_updatenotstopped": "This is the message at the top of [[Special:SMWAdmin]] asking for reassurance concerning the termination of the current update process.\n\nParameters:\n* $1 - the link back to Special:SMWAdmin",
+ "smw-admin-docu": "This is the introductory message at the top of [[Special:SMWAdmin]].",
+ "smw-admin-environment": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-db": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-db-preparation": "This is an informatory message.",
+ "smw-admin-dbdocu": "This is the text describing an available administrative option on [[Special:SMWAdmin]].",
+ "smw-admin-permissionswarn": "This is the text describing a problem (including a solution) that may arise when using an available administrative option on [[Special:SMWAdmin]].",
+ "smw-admin-dbbutton": "This is the text of the submit button on special page \"Semantic MediaWiki\" to trigger the respective process.",
+ "smw-admin-announce": "This is the section header on special page \"Semantic MediaWiki\" as well as the name of the submit button to register the wiki at [https://www.wikiapiary.com wikiapiary.com].",
+ "smw-admin-announce-text": "Short description of what wiki registration on [[Special:SMWAdmin]] means.",
+ "smw-admin-deprecation-notice-title": "This is a section header on Special:SemanticMediaWiki.",
+ "smw-admin-deprecation-notice-docu": "This is an informatory message explaining the purpose of some information provided.",
+ "smw-admin-deprecation-notice-config-notice": "This is an informatory message.\n\nParameter:\n* $1 - holds the name of the depreciated configuration parameter\n* $2 - holds a version number",
+ "smw-admin-deprecation-notice-config-notice-option": "This is an informatory message on special page \"SemanticMediaWiki\".\n\nParameter:\n* $1 - holds the name of the configuration parameter\n* $2 - counts the number of options",
+ "smw-admin-deprecation-notice-config-notice-option-list": "This is an informatory message on special page \"SemanticMediaWiki\".\n\nParameter:\n* $1 - holds the name of the configuration parameter\n* $2 - holds the version number",
+ "smw-admin-deprecation-notice-config-replacement": "This is an informatory message.\n\nParameters:\n* $1 - holds the name of the depreciated configuration parameter\n* $2 - holds the name of the replacing configuration parameter",
+ "smw-admin-deprecation-notice-config-replacement-option": "This is an informatory message on special page \"SemanticMediaWiki\".\n\nParameter:\n* $1 - holds the name of the configuration parameter\n* $2 - counts the number of options\n{{Identical|Option}}",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "This is an informatory message on special page \"SemanticMediaWiki\".\n\nParameter:\n* $1 - holds the name of the current configuration parameter\n* $2 - holds the name of the future configuration parameter",
+ "smw-admin-deprecation-notice-config-removal": "This is an informatory message.\n\nParameter:\n* $1 - holds the name of the removed configuration parameter\n* $2 - holds a version number",
+ "smw-admin-deprecation-notice-title-notice": "This is a section header on special page \"Semantic MediaWiki\" informing about configuration parameters.",
+ "smw-admin-deprecation-notice-title-notice-explanation": "This is an informatory message on special page \"Semantic MediaWiki\".",
+ "smw-admin-deprecation-notice-title-replacement": "This is a section header on special page \"Semantic MediaWiki\" informing about configuration parameters.",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "This is an informatory message.",
+ "smw-admin-deprecation-notice-title-removal": "This is a section header on special page \"Semantic MediaWiki\" informing about configuration parameters.",
+ "smw-admin-deprecation-notice-title-removal-explanation": "This is an informatory message.",
+ "smw-smwadmin-refresh-title": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw_smwadmin_datarefresh": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw_smwadmin_datarefreshdocu": "This is the text describing an available administrative option on [[Special:SMWAdmin]].",
+ "smw_smwadmin_datarefreshprogress": "This is an information message on [[Special:SMWAdmin]].",
+ "smw_smwadmin_datarefreshbutton": "This is the name of the submit button on [[Special:SMWAdmin]] to trigger the respective process.",
+ "smw_smwadmin_datarefreshstop": "This is the name of the submit button on [[Special:SMWAdmin]] to abort the respective process.",
+ "smw_smwadmin_datarefreshstopconfirm": "This is the text next to the checkbox on [[Special:SMWAdmin]] reassuring that the respective process should indeed be cancelled.\nParameter:\n$1 - unused, or user who performed the action (to be used with GENDER)",
+ "smw-admin-job-scheduler-note": "This is an informatory message.",
+ "smw-admin-outdateddisposal-title": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw-admin-outdateddisposal-intro": "This is an informatory message shown on a special page explaining an administrative function. The last sentence refers to the scheduled interval of the cron job dealing with the MediaWiki job queue.",
+ "smw-admin-outdateddisposal-active": "This is an informatory message on special page \"Semantic MediaWiki.",
+ "smw-admin-outdateddisposal-button": "This is the text of the submit button on special page \"Semantic MediaWiki\" to trigger the respective process.",
+ "smw-admin-feature-disabled": "This is an informatory text on special page \"Semantic MediaWiki\".",
+ "smw-admin-propertystatistics-title": "This is a header.",
+ "smw-admin-propertystatistics-intro": "This is an informatory message.",
+ "smw-admin-propertystatistics-active": "This is an informatory message on special page \"Semantic MediaWiki.",
+ "smw-admin-propertystatistics-button": "This is the text of the submit button on special page \"Semantic MediaWiki\" to trigger the respective process.",
+ "smw-admin-fulltext-title": "This is a header.",
+ "smw-admin-fulltext-intro": "This is an informatory message.",
+ "smw-admin-fulltext-active": "This is an informatory message on special page \"Semantic MediaWiki.",
+ "smw-admin-fulltext-button": "This is the text of the submit button on special page \"Semantic MediaWiki\" to trigger the respective process.",
+ "smw-admin-support": "This is a section header on [[Special:SMWAdmin]].",
+ "smw-admin-supportdocu": "This is the introductory text on [[Special:SMWAdmin]] followed by a list of resources.",
+ "smw-admin-installfile": "This text on [[Special:SMWAdmin]] describes a resource for help.\n\n{{doc-important|Do not alter or translate link targets!}}",
+ "smw-admin-smwhomepage": "This text on [[Special:SMWAdmin]] describes a resource for help.\n\n{{doc-important|Do not alter or translate link targets!}}",
+ "smw-admin-bugsreport": "This text on [[Special:SMWAdmin]] describes where to report bugs.\n\n{{doc-important|Do not alter or translate link targets!}}",
+ "smw-admin-questions": "This text on [[Special:SMWAdmin]] describes a resource for help.\n\n{{doc-important|Do not alter or translate link targets!}}",
+ "smw-admin-other-functions": "This is a section header on special page \"SemanticMediaWiki.",
+ "smw-admin-supplementary-section-title": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-supplementary-section-subtitle": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-supplementary-section-intro": "This is an informatory message.",
+ "smw-admin-supplementary-settings-title": "This is a label of a link.",
+ "smw-admin-supplementary-settings-intro": "A message explaining a link.\n\nParameter:\n* $1 - Holds {{msg-mw|Smw-admin-supplementary-settings-title}}",
+ "smw-admin-supplementary-operational-statistics-title": "This is a label of a link.",
+ "smw-admin-supplementary-operational-statistics-intro": "A message explaining a link.\n\nParameter:\n* $1 - Holds {{msg-mw|Smw-admin-supplementary-operational-statistics-title}}",
+ "smw-admin-supplementary-idlookup-title": "This is a label of a link.",
+ "smw-admin-supplementary-idlookup-intro": "A message explaining a link.\n\nParameter:\n* $1 - Holds {{msg-mw|Smw-admin-supplementary-idlookup-title}}",
+ "smw-admin-supplementary-duplookup-title": "This is a label of a link.",
+ "smw-admin-supplementary-duplookup-intro": "A message explaining a link.\n\nParameter:\n* $1 - Holds {{msg-mw|Smw-admin-supplementary-duplookup-intro}}",
+ "smw-admin-supplementary-duplookup-docu": "This is an informatory message explaining the content of a special page.",
+ "smw-admin-supplementary-duplookup-helplink": "This is a link in a tooltip pointing to a help page. Only change the link in case a dedicated page in your language exists on <Semantic-MediaWiki.org> website.",
+ "smw-admin-supplementary-operational-statistics-cache-title": "This is the label of a link on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "This is an item shown on special page \"Semantic MediaWiki\"\n\nParameters:\n* Holds a link to a subpage. The lable of the link is defined with {{Msg-mw|Smw-admin-supplementary-operational-statistics-cache-title}}",
+ "smw-admin-supplementary-elastic-title": "This is the label of a link on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-section-subtitle": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-supplementary-elastic-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-title}}",
+ "smw-admin-supplementary-elastic-docu": "This is an informatory message describing a section on special page \"SemanticMediawiki\"",
+ "smw-admin-supplementary-elastic-functions": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-settings-title": "This is the label of a link on special page \"SemanticMediaWiki\".\n\n{{Identical|Settings}}",
+ "smw-admin-supplementary-elastic-settings-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-settings-title}}",
+ "smw-admin-supplementary-elastic-mappings-title": "This is the label of a link on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-mappings-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-mappings-title}}",
+ "smw-admin-supplementary-elastic-mappings-docu": "This is an informatory message on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-mappings-summary": "This is a section header on special page \"SemanticMediaWiki\".\n\n{{Identical|Summary}}",
+ "smw-admin-supplementary-elastic-mappings-fields": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-nodes-title": "This is the label of a link on special page \"SemanticMediaWiki\".\n\n{{Identical|Nodes}}",
+ "smw-admin-supplementary-elastic-nodes-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-nodes-title}}",
+ "smw-admin-supplementary-elastic-indices-title": "This is the label of a link on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-indices-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-indices-title}}",
+ "smw-admin-supplementary-elastic-statistics-title": "This is the label of a link on special page \"SemanticMediaWiki\".\n\n{{Identical|Statistics}}",
+ "smw-admin-supplementary-elastic-statistics-intro": "This is an informatory message that describes the link accessible via $1.\n\nParameters:\n* $1 - holds the name specified in {{Msg-mw|Smw-admin-supplementary-elastic-statistics-title}}",
+ "smw-admin-supplementary-elastic-statistics-docu": "This is an informatory message describing a subpage on special page \"SemanticMediaWiki\".",
+ "smw-admin-supplementary-elastic-status-replication": "This is a section header on special page \"Semantic MediaWiki\".",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "This is a label.\n\nParameter:\n* $1 - holds a timestamp",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "This is a label.\n\nParameter:\n* $1 - holds a timestamp",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "This is an item label. \n\nParameter:\n* $1 - holds the number of jobs",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "This is an item label.\n\nParameters:\n* $1 - holds the number of jobs",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "This is an item message. \n\nParameter:\n* $1 - holds the number of jobs",
+ "smw-list-count": "This is an informatory message.\n\nParameter:\n* $1 - holds the number of entries",
+ "smw-list-count-from-cache": "This is an informatory message.\n\nParameter:\n* $1 - holds the number of entries\n* $2 - holds a time stamp",
+ "smw-property-label-uniqueness": "This is an informatory message.\n\nParameters:\n* $1 - Holds the name of the property label",
+ "smw-property-label-similarity-title": "This is the section title on a special page.",
+ "smw-property-label-similarity-intro": "This is an link desctiption.\n\nParameter:\n* $1 - Holds the lable of a page and links to it",
+ "smw-property-label-similarity-threshold": "This is the label of an input field.\n{{Identical|Threshold}}",
+ "smw-property-label-similarity-type": "This is the label of a checkbox input field.",
+ "smw-property-label-similarity-noresult": "This is an informatory message shown if the report shows no results.",
+ "smw-property-label-similarity-docu": "This is the informatory message explaining the purpose of the special page reporing on the similarity of property labels.\n\nParameter:\n* $1 - holds the name of a property",
+ "smw-admin-operational-statistics": "This is an informatory message.",
+ "smw_adminlinks_datastructure": "This is a section header on [[Special:AdminLinks]] which is needed for the integration of the [[mw:Extension:Admin_Links|Admin Links extension]] into this extension.",
+ "smw_adminlinks_displayingdata": "This is a section header on [[Special:AdminLinks]] which is needed for the integration of the [[mw:Extension:Admin_Links|Admin Links extension]] into this extension.",
+ "smw_adminlinks_inlinequerieshelp": "This is the link text for a link on [[Special:AdminLinks]] which is needed for the integration of the [[mw:Extension:Admin_Links|Admin Links extension]] into this extension.",
+ "smw-page-indicator-usage-count": "This is an informatory message shown on property pages.\n\nParameter:\n* $2 holds the number from the usage count",
+ "smw-property-indicator-type-info": "This informatory message is shown on property pages. It informs if the property is either user defined (=User) or provided by the software (=System).",
+ "smw-property-indicator-last-count-update": "This informatory message is shown in a tooltip incicating the number of data values assigned to a property.\n\nParameter:\n* $1 - Holds the timestamp of the last update",
+ "smw-concept-indicator-cache-update": "This is the content of a tooltip on a concept page.\n\nParameter:\n* $1 - holds the timestamp of the update",
+ "smw-createproperty-isproperty": "This an information message at the top on a page in namespace \"Property\". Parameters:\n* $1 holds the name of the data type which was assigned to the respective property.",
+ "smw-createproperty-allowedvals": "This an information message at the top on a page in namespace \"Property\" which gets inserted in case only certain property values are [https://semantic-mediawiki.org/wiki/Help:Special_property_Allows_value allowed to be used] for the respective property.\n\nParameters:\n* $1 - number of allowed values",
+ "smw-paramdesc-category-delim": "This is the description of the \"delim\" parameter of the \"category\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-category-template": "This is the description of the \"template\" parameter of the \"category\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-paramdesc-category-userparam": "This is the description of the \"userparam\" parameter of the \"category\" [https://semantic-mediawiki.org/wiki/Help:Result_formats result format] for [https://semantic-mediawiki.org/wiki/Help:Inline_queries inline queries].",
+ "smw-info-par-message": "This is the description of the \"message\" parameter of the parser function \"[https://semantic-mediawiki.org/wiki/Help:Adding_tooltips #info]\".",
+ "smw-info-par-icon": "This is the description of the \"icon\" parameter of the parser function \"[https://semantic-mediawiki.org/wiki/Help:Adding_tooltips #info]\".\n\n{{doc-important|Do not translate the possible parameter values \"info\" and \"warning\".}}",
+ "prefs-smw": "This is the text of a section header on [[Special:Preferences#mw-prefsection-smw|Special:Preferences]].\n{{Identical|Semantic MediaWiki}}",
+ "prefs-general-options": "This is a section header so special page \"Preferences\".",
+ "prefs-ask-options": "This is the text of a header within a section on [[Special:Preferences#mw-prefsection-smw|Special:Preferences]].",
+ "smw-prefs-intro-text": "This is a user preference intro text on special page \"Preferences\".",
+ "smw-prefs-ask-options-tooltip-display": "This is the text describing a user option on [[Special:Preferences#mw-prefsection-smw|Special:Preferences]].",
+ "smw-prefs-ask-options-compact-view-basic": "This is a preference option on special page \"Preferences\"",
+ "smw-prefs-help-ask-options-compact-view-basic": "This is an informative message explaining a preference option on special page \"Preferences\"",
+ "smw-prefs-general-options-time-correction": "This is a message describing a user preference option selectable via special page \"Preferences\".",
+ "smw-prefs-general-options-jobqueue-watchlist": "This is a message describing a user preference option selectable via special page \"Preferences\".",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "This is an informative message explaining a preference option on special page \"Preferences\".",
+ "smw-prefs-general-options-disable-editpage-info": "This is a message describing a user preference option selectable via special page \"Preferences\". Relates to information shown only when editing a page.",
+ "smw-prefs-general-options-disable-search-info": "This message describes a user preference option on special page \"Preferences\".",
+ "smw-prefs-general-options-suggester-textinput": "This is the description of a user preference option selectable on special page \"Preferences\".",
+ "smw-prefs-help-general-options-suggester-textinput": "This is an informative message explaining a preference option on special page \"Preferences\". Input context means accessible from respective input fields.",
+ "smw-ui-tooltip-title-property": "A label that is displayed on the info tooltip.\n{{Identical|Property}}",
+ "smw-ui-tooltip-title-quantity": "A label that is displayed on the info tooltip.",
+ "smw-ui-tooltip-title-info": "A label that is displayed on the info tooltip.\n{{Identical|Information}}",
+ "smw-ui-tooltip-title-service": "A label that is displayed on the info tooltip.",
+ "smw-ui-tooltip-title-warning": "A label that is displayed on the info tooltip.\n{{Identical|Warning}}",
+ "smw-ui-tooltip-title-error": "A label that is displayed on the info tooltip.\n\n{{Identical|Error}}",
+ "smw-ui-tooltip-title-parameter": "A label that is displayed on the info tooltip.\n{{Identical|Parameter}}",
+ "smw-ui-tooltip-title-event": "A label that is displayed on the info tooltip.\n{{Identical|Event}}",
+ "smw-ui-tooltip-title-note": "A label that is displayed on the info tooltip.\n{{Identical|Note}}",
+ "smw-ui-tooltip-title-legend": "This is the name of a label.\n{{Identical|Legend}}",
+ "smw-ui-tooltip-title-reference": "{{Identical|Reference}}",
+ "smw_unknowntype": "Error message shown for properties that have a type unknown to the system.\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-concept-cache-text": "This is the message displays information about the cache status.\n\nParameters:\n* $1 - number of pages\n* $2 - date\n* $3 - time",
+ "smw_concept_header": "This is the header on a page in namespace \"Concept\". Parameters:\n* $1 holds the name of the respective concept.",
+ "smw_conceptarticlecount": "This is the introductory message below the header on pages in namespace \"Concept\". Parameters:\n* $1 holds the number of pages displayed in the current view.",
+ "smw-qp-empty-data": "An error message shown for insufficient data.",
+ "right-smw-admin": "{{doc-right|smw-admin}}",
+ "right-smw-patternedit": "{{doc-right|smw-patternedit}}",
+ "right-smw-pageedit": "{{doc-right}}",
+ "right-smw-ruleedit": "{{Doc-right}}",
+ "restriction-level-smw-pageedit": "This is an informatory message displaying the protection parameters on \"Special:ProtectedPages\". It is not possible to know the exact number of eligible users.",
+ "action-smw-patternedit": "{{doc-action|smw-patternedit}}",
+ "action-smw-pageedit": "{{doc-action|smw-pageedit}}",
+ "group-smwadministrator": "{{doc-group|smwadministrator|group}}",
+ "group-smwadministrator-member": "{{doc-group|smwadministrator|member}}",
+ "grouppage-smwadministrator": "{{doc-group|smwadministrator|page}}",
+ "group-smwcurator": "{{doc-group|smwcurator|group}}",
+ "group-smwcurator-member": "{{doc-group|smwcurator|member}}",
+ "grouppage-smwcurator": "{{doc-group|smwcurator|page}}",
+ "action-smw-admin": "{{doc-action|smw-admin}}",
+ "action-smw-ruleedit": "{{Doc-action}}",
+ "smw-property-predefined-default": "This message is the default introductory text for [https://semantic-mediawiki.org/wiki/Help:Special_properties special (predefined) properties].\n\nParameter:\n* $1 - Name of the invoked predefined property",
+ "smw-property-predefined-common": "This informatory message explains what [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] is.",
+ "smw-property-predefined-ask": "This informatory message describes a [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-asksi": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding the [https://semantic-mediawiki.org/wiki/Help:Special_property_Query_size size] of a query.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-askde": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding the [https://semantic-mediawiki.org/wiki/Help:Special_property_Query_depth depth] of a query.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-long-askde": "This informatory message describes the value added to a [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].",
+ "smw-property-predefined-askpa": "This informatory message describes a [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-long-askpa": "This is an informatory message.",
+ "smw-sp-properties-docu": "Extended introductory text for the [[Special:Properties]] page.",
+ "smw-sp-properties-cache-info": "Information about the current cache status on a special page\n\nParameters:\n* $1 - Accommodates a user formatted date",
+ "smw-sp-properties-header-label": "A header label",
+ "smw-admin-settings-docu": "This is an informatory message used as an introductory text for an action that may be performed via special page \"[[Special:SMWAdmin]]\".\n\nPreceded by the heading {{msg-mw|Smw-sp-admin-settings-title}}.",
+ "smw-sp-admin-settings-button": "A button text.\n\nPreceded by introduction:\n* {{msg-mw|Smw-sp-admin-settings-docu}}",
+ "smw-admin-idlookup-title": "A header title",
+ "smw-admin-idlookup-docu": "This is an informatory message used as an introductory text for an action that may be performed via special page \"[[Special:SMWAdmin]]\".\n\nPreceded by the heading {{msg-mw|Smw-sp-admin-idlookup-title}}.",
+ "smw-admin-iddispose-title": "This is a section header on special page \"Special:SMWAdmin\"",
+ "smw-admin-iddispose-docu": "This is an informatory message used as an introductory text for an action that may be performed via special page \"[[Special:SMWAdmin]]\".\n\nPreceded by the heading {{msg-mw|Smw-admin-iddispose-title}}.",
+ "smw-admin-iddispose-done": "This is an informatory message after an action was performed.\n\nParameter.:\n* $1 holds the ID of an object",
+ "smw-admin-iddispose-references": "This is an informatory message.\n\nParameters:\n* $1 holds the ID of an object\n* $2 holds the number of references",
+ "smw-admin-iddispose-references-multiple": "This is an introductiory message preceeding a table of results.",
+ "smw-admin-iddispose-no-references": "This is an informatory message on special page \"SemanticMediaWiki\".\n\nParameter:\n* $1 holds the ID of an object",
+ "smw-admin-idlookup-input": "This is a field label. Please translate as a noun.\n{{Identical|Search}}",
+ "smw-admin-objectid": "A label\n{{Identical|ID}}",
+ "smw-admin-tab-general": "This is the label of a tab.\n{{Identical|Overview}}",
+ "smw-admin-tab-notices": "This is the label of a tab.",
+ "smw-admin-tab-maintenance": "This is the label of a tab on special page \"Semantic MediaWiki\".",
+ "smw-admin-tab-supplement": "This is the label of a tab.",
+ "smw-admin-tab-registry": "This is the label of a tab.\n\n{{Identical|Registry}}",
+ "smw-admin-maintenance-no-description": "This is an informatory message.",
+ "smw-admin-maintenance-script-section-title": "This is a section header on special page \"SemanticMediaWiki\".",
+ "smw-admin-maintenance-script-section-intro": "This is an informatory text on special page \"SemanticMediaWiki\".",
+ "smw-admin-maintenance-script-description-dumprdf": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-rebuilddata": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-setupstore": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "This is the short description of a maintenance script.",
+ "smw-admin-maintenance-script-description-populatehashfield": "This is the short description of a maintenance script.",
+ "smw-livepreview-loading": "{{Identical|Loading}}Message displayed while data is loading.",
+ "smw-sp-searchbyproperty-description": "Extended description text for the [[Special:SearchByProperty]] page.",
+ "smw-sp-searchbyproperty-resultlist-header": "A header label",
+ "smw-sp-searchbyproperty-nonvaluequery": "A description for a query result without values.\n\nParameters:\n* $1 - Name of the property",
+ "smw-sp-searchbyproperty-valuequery": "A description for a query result with values.\n\nParameters:\n* $1 - Name of the property\n* $2 - Name of the value",
+ "smw-datavalue-number-textnotallowed": "This is an error/warning message.\n\nParameters:\n* $1 holds the text causing the error/warning.\n* $2 holds the property value supposed to be used.",
+ "smw-datavalue-number-nullnotallowed": "This is an error/warning message.\n\nParameter:\n* $1 holds the text causing the error/warning.",
+ "smw-editpage-annotation-enabled": "A description to be displayed on the edit page.",
+ "smw-editpage-annotation-disabled": "A description to be displayed on the edit page.",
+ "smw-editpage-property-annotation-enabled": "A description to be displayed on the edit page.",
+ "smw-editpage-property-annotation-disabled": "A description to be displayed on the edit page.",
+ "smw-editpage-concept-annotation-enabled": "A description to be displayed on the edit page.",
+ "smw-search-syntax-support": "This is an informatory message on special page \"Search\".",
+ "smw-search-input-assistance": "This is an informatory message on special page \"Search\".",
+ "smw-search-help-intro": "This informatory message is part of a large pop-up on special page \"Search\".",
+ "smw-search-help-structured": "This informatory message is part of a large pop-up on special page \"Search\".",
+ "smw-search-help-proximity": "This informatory message is part of a large pop-up on special page \"Search\".",
+ "smw-search-help-ask": "This informatory message is part of a large pop-up on special page \"Search\".\n\nSee {{msg-mw|Smw-ask-help}}",
+ "smw-search-input": "This is a section header in a large pop up on \"Special:Search\".",
+ "smw-search-help-input-assistance": "This informatory message is part of a large pop-up on special page \"Search\".\n\nSee the following messages for inspiration:\n* {{Msg-mw|smw-ask-condition-input-assistance}}\n* {{Msg-mw|smw-ask-condition-input-assistance-property}}\n* {{Msg-mw|smw-ask-condition-input-assistance-category}}\n* {{Msg-mw|smw-ask-condition-input-assistance-concept}}",
+ "smw-search-syntax": "This is a section header in a large pop up on \"Special:Search\".\n{{Identical|Syntax}}",
+ "smw-search-profile": "This is the label of a tab on special page \"Search\".",
+ "smw-search-profile-tooltip": "This is an informatory message provided via a tooltip.",
+ "smw-search-profile-sort-best": "This is the label of a drop-down menu item on special page \"Search\" and an item in a tooltip.",
+ "smw-search-profile-sort-recent": "This is the label of a drop-down menu item on special page \"Search\" and an item in a tooltip.",
+ "smw-search-profile-sort-title": "This is the label of a drop-down menu item on special page \"Search\" and an item in a tooltip.\n\n{{Identical|Title}}",
+ "smw-search-profile-extended-help-intro": "This is an informatory message shown in a tooltip on special page \"Search\".",
+ "smw-search-profile-extended-help-sort": "This is an informatory message shown in a tooltip on special page \"Search\".",
+ "smw-search-profile-extended-help-sort-title": "This is an informatory message shown in a tooltip on special page \"Search\".\n\nNote that the translation of the itmem should match {{Msg-mw|Smw-search-profile-sort-title}}.",
+ "smw-search-profile-extended-help-sort-recent": "This is an informatory message shown in a tooltip on special page \"Search\".\n\nNote that the translation of the itmem should match {{Msg-mw|Smw-search-profile-sort-recent}}.",
+ "smw-search-profile-extended-help-sort-best": "This is an informatory message shown in a tooltip on special page \"Search\".\n\nNote that the translation of the itmem should match {{Msg-mw|Smw-search-profile-sort-best}}.",
+ "smw-search-profile-extended-help-form": "This is an informatory message shown in a tooltip on special page \"Search\".\n\nParameters:\n* $1 - holds a link",
+ "smw-search-profile-extended-help-namespace": "This is an informatory message explaining namespace selection on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax": "This is an informatory message introducing several search parameters on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-search-syntax-prefix": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".\n\nParameter:\n* $1 - holds the name of a search parameter",
+ "smw-search-profile-extended-help-search-syntax-reserved": "This is an informatory message explaining a search parameter on the cheat sheet for special page \"Search\".\n\nParameter:\n* $1 - holds the name of a search parameter",
+ "smw-search-profile-extended-help-search-syntax-note": "This is an informatory message about search parameters on the cheat sheet for special page \"Search\".",
+ "smw-search-profile-extended-help-query": "This is an informatory message.\n\nParameter:\n* $1 - holds the code of the query.",
+ "smw-search-profile-extended-help-query-link": "This message links to a help page. \n\nParameter:\n* $1 - holds the link to the help page. The caption of the link is specified in message {{Msg-mw|Smw-search-profile-link-caption-query}}",
+ "smw-search-profile-extended-help-find-forms": "This the label for a link shown in a tooltip on special page \"Search\". See {{Msg-mw|Smw-search-profile-extended-help-form}} for the message holding the link.",
+ "smw-search-profile-extended-section-sort": "This is the label of a drop-down menu on special page \"Search\" and a section header in a tooltip.",
+ "smw-search-profile-extended-section-form": "This is the label of a drop-down menu on special page \"Search\" and a section header in a tooltip.",
+ "smw-search-profile-extended-section-search-syntax": "This is a label on special page \"Search\".",
+ "smw-search-profile-extended-section-namespace": "This is the label of a drop-down menu on special page \"Search\" and a section header in a tooltip.\n{{Identical|Namespace}}",
+ "smw-search-profile-extended-section-query": "This is the label of a button on special page \"Search\" / section \"Extended\"",
+ "smw-search-profile-link-caption-query": "This is the caption of a link.",
+ "smw-search-show": "This is the label of a button on special page \"Search\" / section \"Extended\"\n\n{{Identical|Show}}",
+ "smw-search-hide": "This is the label of a button on special page \"Search\" / section \"Extended\"\n\n{{Identical|Hide}}",
+ "log-name-smw": "A name for the [[Special:Log]] Semantic MediaWiki entry.",
+ "log-show-hide-smw": "This is the name for an entry added by Semantic MediaWiki to special page \"[[Special:Log]]\".\nParameters:\n* $1 - one of {{msg-mw|Show}} or {{msg-mw|Hide}}\n{{Related|Log-show-hide}}",
+ "logeventslist-smw-log": "Semantic Mediawiki log option label on [[Special:Log]]",
+ "log-description-smw": "A description for Semantic MediaWiki entries displayed in [[Special:Log]].",
+ "logentry-smw-maintenance": "This is the message for an entry added by Semantic MediaWiki to special page \"[[Special:Log]]\".",
+ "smw-datavalue-import-unknown-namespace": "This is an error message.\n\nParameter:\n* $1 holds the name of the namespace.",
+ "smw-datavalue-import-missing-namespace-uri": "This is an error message.\n\nParameter:\n* $1 holds the name of the namespace.",
+ "smw-datavalue-import-missing-type": "This is an error message show to the user during [https://semantic-mediawiki.org/wiki/Help:Import_vocabulary the import of an vocabulary]\n\nParameters:\n* $1 - name of the property to be imported\n* $2 - number of vocabulary to be imported",
+ "smw-datavalue-import-link": "This is an informatory message shown to the user during [https://semantic-mediawiki.org/wiki/Help:Import_vocabulary the import of an vocabulary]\n\nParameters:\n* $1 - name of the vocabulary",
+ "smw-datavalue-import-invalid-value": "This is an error message shown to the user during [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary the import of an vocabulary].\n\nParameter:\n* $1 holds the name of erroneous namespace identifier combination",
+ "smw-datavalue-import-invalid-format": "This is an error message shown to the user during [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary the import of an vocabulary].\n\nParameter:\n* $1 holds the erroneous string",
+ "smw-property-predefined-impo": "This informatory message describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Imported_from imported from].\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-type": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding the [https://semantic-mediawiki.org/wiki/Help:Special_property_Has_type datatype] of a property.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-sobj": "This informatory message shown in a tooltip describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Has_subobject subobjects]. Think of a subobject being an invisible wiki page which can only hold semantic annotations, i.e. assignments of values to properties.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-long-sobj": "This informatory message shown on a property page describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Has_subobject subobjects]. Think of a subobject being an invisible wiki page which can only hold semantic annotations, i.e. assignments of values to properties.",
+ "smw-property-predefined-errp": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Has_improper_value_for improper values].\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-long-errp": "This informatory message shown on a property page describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Has_improper_value_for improper values].",
+ "smw-property-predefined-pval": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Allows_value allowed values]. Only allowed values may be saved without generating an error.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-pvali": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list allowed values]. Only allowed values may be saved without generating an error.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-datavalue-property-restricted-annotation-use": "This is an error message.\n\nParameters:\n* $1 - holds the name of the property",
+ "smw-datavalue-property-restricted-declarative-use": "This is an error message. Translate declarative in a sense of defining something.\n\nParameters:\n* $1 - holds the name of the property",
+ "smw-datavalue-property-create-restriction": "This is an error message shown when am edit protected property page should be created.\n\nParameters:\n* $1 - Holds the name of property\n* $2 - Holds the name of the user right",
+ "smw-datavalue-property-invalid-character": "This is an error message.\n\nParameters:\n* $1 holds the name of the property\n* $2 holds the invalid characters withing the name of the property",
+ "smw-datavalue-property-invalid-chain": "This is an error message.\n\nParameter:\n* $1 - holds the name of the property chain",
+ "smw-datavalue-restricted-use": "This is an error/warning message. This basically means that the datavalue may not be annotated.\n\nParameters:\n\n* $1 holds the datavalue causing the error/warning.",
+ "smw-datavalue-invalid-number": "This is an error message shown to the user in case the stored value is not a number.",
+ "smw-query-condition-circular": "This is an error/warning message.\n\nParameters:\n\n* $1 holds the name of the template causing the error/warning.",
+ "smw-query-condition-empty": "This is an error message.",
+ "smw-types-list": "A header",
+ "smw-types-default": "This is an informatory message shown to the user.",
+ "smw-types-help": "This is an informatory message shown to the user.",
+ "smw-type-anu": "This is an informatory message shown to the user.",
+ "smw-type-boo": "This is an informatory message shown to the user.",
+ "smw-type-cod": "This is an informatory message shown to the user.",
+ "smw-type-geo": "This is an informatory message shown to the user.",
+ "smw-type-tel": "This is an informatory message shown to the user.",
+ "smw-type-txt": "This is an informatory message shown to the user.",
+ "smw-type-dat": "This is an informatory message shown to the user.",
+ "smw-type-ema": "This is an informatory message on special page \"Types\".\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-type-tem": "This is an informatory message on special page \"Types\".\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-type-qty": "This is an informatory message on special page \"Types\".\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-type-rec": "This is an informatory message on special page \"Types\".\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-type-extra-tem": "This is an informatory message on special page \"Types\".",
+ "smw-type-tab-properties": "This is the label of a tab on special page \"Types\".",
+ "smw-type-tab-types": "This is the label of a tab on special page \"Types\". Translae types as in datatypes.",
+ "smw-type-tab-errors": "This is the label of a tab on special page \"Types\".",
+ "smw-type-primitive": "This is the label of a tab on special page \"Types\".",
+ "smw-type-contextual": "This is the label of a tab on special page \"Types\".",
+ "smw-type-compound": "This is the label of a tab on special page \"Types\".",
+ "smw-specials-browse-helplink": "{{notranslate}}",
+ "smw-specials-bytype-helplink": "This message links to the respective help page on <semantic-mediawiki.org>.\n\nParameter:\n* Holds the string identifying the help page.\n\n{{Optional}}",
+ "smw-specials-types-helplink": "This message links to the respective help page on <semantic-mediawiki.org>.\n\n{{Optional}}",
+ "smw-special-pageproperty-helplink": "{{Notranslate}}",
+ "smw-special-pageproperty-description": "This is an informatory message documenting special page \"PageProperty\".",
+ "smw-property-predefined-errc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] shown in a tooltip.",
+ "smw-property-predefined-long-errc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] on the property's page.",
+ "smw-property-predefined-errt": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].",
+ "smw-subobject-parser-invalid-naming-scheme": "This is an error message shown to the user in case a named identifier contains a dot (foo.bar).",
+ "smw-datavalue-record-invalid-property-declaration": "This is an error message shown to the user.\n\n* $1 holds the name of the property of type record.",
+ "smw-property-predefined-mdat": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-cdat": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-newp": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-ledt": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-mime": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-media": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-askfo": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-askst": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-askdu": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-asksc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-property-predefined-askco": "This informatory message describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] holding the [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Query_status_code status code] of a query.\n\nParameter:\n\n* $1 - name of predefined property",
+ "smw-property-predefined-long-askco": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] on the property's page. Translate \"internal codified\" in a sense of \"internally used representation of a state\" the query is in.",
+ "smw-property-predefined-prec": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-attch-link": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-types-extra-geo-not-available": "This is an informatory message shown to the user.\n\n* $1 holds the name of the property.",
+ "smw-datavalue-monolingual-dataitem-missing": "This is an error message shown to the user.",
+ "smw-datavalue-monolingual-lcode-parenthesis": "{{optional}}",
+ "smw-datavalue-languagecode-missing": "This is an error message.\n\n* $1 is the erroneous property value combination (=annotation) added by the user.",
+ "smw-datavalue-languagecode-invalid": "This is an error message.\n\n* $1 is the erroneous language code added by the user.",
+ "smw-property-predefined-lcode": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.\n\n[[wikipedia:en:IETF language tag|BCP47]] is an abbreviated language code (for example, en for English, pt-BR for Brazilian Portuguese, or nan-Hant-TW for Min Nan Chinese as spoken in Taiwan using traditional Han characters).",
+ "smw-type-mlt-rec": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-types-extra-mlt-lcode": "This is an error message shown to the user.",
+ "smw-property-predefined-text": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-pdesc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-list": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-limitreport-intext-parsertime": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]].\n\n{{doc-important|Do not translate: [SMW]}}",
+ "smw-limitreport-intext-postproctime": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]].\n\n{{doc-important|Do not translate: [SMW]}}",
+ "smw-limitreport-intext-parsertime-value": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]].\n\n* $1 holds the value of the seconds elapsed.\n\n{{Identical|Second}}",
+ "smw-limitreport-intext-postproctime-value": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]].\n\n* $1 holds the value of the seconds elapsed.\n\n{{Identical|Second}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]]. Store refers to the type of database backend used by the software, i.e. a SQL-store or RDF-store.\n\n{{doc-important|Do not translate: [SMW]}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "This is an informatory message that extends the \"NewPP limit report\" generated by MediaWiki shown when viewing the raw HTML source of a generated page, e.g. [[Media:NewPP limit report swm.png|NewPP limit report swm]].\n\n* $1 holds the value of the seconds elapsed (always plural).\n\n{{doc-important|Do not translate: [SMW]}}\n{{Identical|Second}}",
+ "smw_allows_pattern": "Modifications are expected to occur on a local wiki only as this page represents a placeholder.",
+ "smw-datavalue-allows-pattern-mismatch": "This is an error message.\n\nParameters:\n* $1 holds the property value\n* $2 holds the pattern",
+ "smw-datavalue-allows-pattern-reference-unknown": "This is an error message.\n\nParameters:\n* $1 - holds the pattern string",
+ "smw-datavalue-allows-value-list-unknown": "This is an error message.\n\nParameters:\n* $1 - holds the reference string",
+ "smw-datavalue-allows-value-list-missing-marker": "This is an error message.\n\nParameter:\n* $1 - holds the name of the list",
+ "smw-datavalue-feature-not-supported": "This is an error message.\n\nParameters:\n* $1 - holds the name of the feature",
+ "smw-property-predefined-pvap": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-dtitle": "This informatory message describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-pvuc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] shown in a tooltip.",
+ "smw-property-predefined-long-pvuc": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] on the property's page.",
+ "smw-datavalue-uniqueness-constraint-error": "This is an error message.\n\nParameters:\n* $1 holds the name of the property\n* $2 holds the data value assigned to the propery\n* $3 holds the name of the page",
+ "smw-datavalue-uniqueness-constraint-isknown": "This is an error message.\n\nParameters:\n* $1 holds the name of the property\n* $2 holds the name of the page\n* $3 holds the data value assigned to the propery",
+ "smw-property-predefined-boo": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-property-predefined-num": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-property-predefined-dat": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-property-predefined-uri": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-property-predefined-qty": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-values": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value\n* $2 holds the part of the value which could not be interpreted",
+ "smw-datavalue-time-invalid-date-components-common": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-date-components-dash": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-date-components-empty": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-date-components-three": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-date-components-sequence": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid-ampm": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value\n* $2 holds the incorrect part of the annotated value",
+ "smw-datavalue-time-invalid-jd": "This is an error message:\n\nParameters:\n* $1 holds the incorrectly annotated value\n* $2 holds the incorrect output",
+ "smw-datavalue-time-invalid-prehistoric": "This is an error message. \"prehistoric\" refers to the year before which we do not accept anything but year numbers and largely discourage calendar models.\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-time-invalid": "This is an error message:\n\nParameters:\n* $1 holds the incorrectly annotated value\n* $2 holds the incorrect output",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "This is an error message:\n\nParameter:\n* $1 holds the missing part of the annotated value\n\nThink of an \"Formatter URI\" as and URI containing a placeholder which needs to be filled with a value, e.g. https://id.ndl.go.jp/auth/ndlna/$1. So if $1 is missing the URI cannot be generated like e.g. https://id.ndl.go.jp/auth/ndlna/00564222. Then the user gets this message.",
+ "smw-datavalue-external-formatter-invalid-uri": "This is an error message:\n\nParameter:\n* $1 holds the incorrectly annotated value",
+ "smw-datavalue-external-identifier-formatter-missing": "This is an error message.\n\nAn URI needs to be generated somehow using the given value. If the external formatter URI, like https://id.ndl.go.jp/auth/ndlna/$1, is missing, the software does not know how to generate it using the value (e.g, \"00564222\") which should be filled into $1 placeholder. In the end, the software should come up with https://id.ndl.go.jp/auth/ndlna/00564222.",
+ "smw-datavalue-keyword-maximum-length": "This is an error message.\n\nParameter:\n* $1 - holds the maximum length for the characters (defined integer)",
+ "smw-property-predefined-eid": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-property-predefined-peid": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property\n\nThink of an \"Formatter URI\" as and URI containing a placeholder which needs to be filled with a value, e.g. https://id.ndl.go.jp/auth/ndlna/$1. So if the external identifier is missing the URI cannot be constructed like e.g. https://id.ndl.go.jp/auth/ndlna/00564222. In this example the external identifierer is \"00564222\".",
+ "smw-property-predefined-pefu": "This informatory message describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.\n\n\nThink of an \"Formatter URI\" as and URI containing a placeholder which needs to be filled with a value, e.g. https://id.ndl.go.jp/auth/ndlna/$1. So if the formatter URI is missing the URI cannot be gernerated like e.g. https://id.ndl.go.jp/auth/ndlna/00564222. In this example the formatter URI is \"https://id.ndl.go.jp/auth/ndlna/$1\".",
+ "smw-property-predefined-long-pefu": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property] shown in a tooltip.\n\nThink of an \"Formatter URI\" as and URI containing a placeholder which needs to be filled with a value, e.g. https://id.ndl.go.jp/auth/ndlna/$1. So if $1 (=placeholder) is missing the URI cannot be generated like e.g. https://id.ndl.go.jp/auth/ndlna/00564222.",
+ "smw-type-eid": "This is an explanatory message.\n\nParameter:\n* $1 holds the name of a data type\n\nThink of an \"Formatter URI\" as and URI containing a placeholder which needs to be filled with a value, e.g. https://id.ndl.go.jp/auth/ndlna/$1. So if $1 is missing the URI cannot be generated like e.g. https://id.ndl.go.jp/auth/ndlna/00564222.",
+ "smw-property-predefined-keyw": "This informatory message describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] and [https://www.semantic-mediawiki.org/wiki/Help:Datatype datatype] holding a keyword.\n\nParameter:\n* $1 - name of predefined property",
+ "smw-type-keyw": "This is an explanatory message.\n\nParameter:\n* $1 - holds the name of the datatype",
+ "smw-datavalue-stripmarker-parse-error": "This is an error message.\n\nParameter:\n* $1 holds the invalid value.",
+ "smw-datavalue-parse-error": "This is an error message.\n\nParameter:\n* $1 holds the property value which was not interpretable.",
+ "smw-datavalue-propertylist-invalid-property-key": "This is an error message.\n\nParameters:\n* $1 holds the name of the property list.\n* $2 holds the property key which was invalid.",
+ "smw-datavalue-type-invalid-typeuri": "This is an error message.\n\nParameter:\n* $1 holds the invalid type.",
+ "smw-datavalue-wikipage-missing-fragment-context": "This is an error message.\n\nParameter:\n* $1 holds the invalid value.",
+ "smw-datavalue-wikipage-invalid-title": "This is an error message.\n\nParameter:\n* $1 holds the invalid value.",
+ "smw-datavalue-wikipage-property-invalid-title": "This is an error message.\n\nParameters:\n* $1 - holds the name of the property\n* $2 - holds the invalid datavalue",
+ "smw-datavalue-wikipage-empty": "This is an error message.\n\nParameter:\n* $1 holds the invalid value.",
+ "smw-type-ref-rec": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes datatype].\n\nParameter:\n* $1 holds the name of the datatype.",
+ "smw-datavalue-reference-outputformat": "{{optional}}",
+ "smw-datavalue-reference-invalid-fields-definition": "This is an error message.",
+ "smw-parser-invalid-json-format": "This is an error message.\n\nParameter:\n* $1 holds the error output.",
+ "smw-property-preferred-title-format": "{{optional}}",
+ "smw-property-preferred-label-language-combination-exists": "This is an error message.\n\nParameters:\n* $1 name of the property label used\n* $2 language of the property label\n* $3 name of the already exixting property label in use",
+ "smw-parse": "{{Notranslate}}",
+ "smw-clipboard-copy-link": "A label that is displayed on the info tooltip.",
+ "smw-property-userdefined-fixedtable": "This is an informatory message.",
+ "smw-data-lookup": "This is an informatory message.",
+ "smw-data-lookup-with-wait": "This is an informatory message.",
+ "smw-no-data-available": "This is an informatory message.",
+ "smw-property-req-violation-missing-fields": "This is an error message.\n\nParameters:\n* $1 - Holds the name of the property\n* $2 - Holds the name of the datatype",
+ "smw-property-req-violation-missing-formatter-uri": "This is an error message.\n\nParameter:\n* $1 - Holds the name of the property",
+ "smw-property-req-violation-predefined-type": "This is an error message.\n\nParameters:\n* $1 - Holds the name of the property\n* $2 - Holds the name of the datatype",
+ "smw-property-req-violation-import-type": "This is an error message.\n\nParameter:\n* $1 - holds the name of the imported vacabulary",
+ "smw-property-req-violation-change-propagation-locked-error": "This is an informatory message. Similar to\n\n{{Msg-mw|Smw-property-req-violation-change-propagation-locked-warning}}\n\nand\n\n{{Msg-mw|smw-property-req-violation-change-propagation-pending}}\n\nParameters:\n* $1 - holds the name of the property",
+ "smw-property-req-violation-change-propagation-locked-warning": "This is an informatory message. Similar to\n\n{{Msg-mw|Smw-property-req-violation-change-propagation-locked-error}}\n\nand\n\n{{Msg-mw|smw-property-req-violation-change-propagation-pending}}\n\nParameters:\n* $1 - holds the name of the property",
+ "smw-property-req-violation-change-propagation-pending": "This is an informatory message on a property page. Similar to\n\n{{Msg-mw|smw-property-req-violation-change-propagation-locked-error}}\n\nand\n\n{{Msg-mw|smw-property-req-violation-change-propagation-locked-warning}}\n\nParameters:\n* $1 - holds the number of jobs queued",
+ "smw-property-req-violation-missing-maps-extension": "This is an error message.",
+ "smw-property-req-violation-type": "This is an informatory message. type refers to datatype.",
+ "smw-change-propagation-protection": "This is an informatory message. Similar to:\n* {{Msg-mw|Smw-category-change-propagation-locked-error}},\n* {{Msg-mw|Smw-category-change-propagation-locked-warning}} and\n* {{Msg-mw|Smw-category-change-propagation-pending}}",
+ "smw-category-change-propagation-locked-error": "This is an informatory message. Similar to\n\n{{Msg-mw|Smw-category-change-propagation-locked-warning}}\n\nand\n\n{{Msg-mw|smw-category-change-propagation-pending}}\n\nParameters:\n* $1 - holds the name of the category",
+ "smw-category-change-propagation-locked-warning": "This is an informatory message. Similar to\n\n{{Msg-mw|smw-category-change-propagation-locked-error}}\n\nand\n\n{{Msg-mw|smw-category-change-propagation-pending}}\n\nParameters:\n* $1 - holds the name of the category",
+ "smw-category-change-propagation-pending": "This is an informatory message on a category page. Similar to\n\n{{Msg-mw|Smw-category-change-propagation-locked-error}}\n\nand\n\n{{Msg-mw|smw-category-change-propagation-locked-warning}}\n\nParameters:\n* $1 - holds the number of jobs queued",
+ "smw-category-invalid-value-assignment": "This is an error message.\n\nParameter:\n* $1 - holds the value of the erroneous entry",
+ "protect-level-smw-pageedit": "This is one of the options that may be selected when performing a page protection (<code>action=protect</code>) via the graphical interface of the wiki.",
+ "smw-create-protection": "This is an error message shown when am edit protected property page should be edited.\n\nParameters:\n* $1 - Holds the name of property\n* $2 - Holds the name of the user right",
+ "smw-create-protection-exists": "This is an error message shown when am edit protected property page should be edited.\n\nParameters:\n* $1 - Holds the name of property\n* $2 - Holds the name of the user right",
+ "smw-edit-protection": "This is an informatory message shown on every page which is edit protected via an annotation.\n\nParameter:\n* $1 - Holds the name of the user right",
+ "smw-edit-protection-disabled": "This is an informatory message.\n\nParameter:\n* $1 - Holds the name of the special property",
+ "smw-edit-protection-auto-update": "This is an informatory message shown after a page protection was performed.",
+ "smw-edit-protection-enabled": "This is an informatory message shown with a tooltip telling that the page is edit protected via the mechanism provided by Semantic MediaWiki.",
+ "smw-patternedit-protection": "This is an informatory message shown on every page which is edit protected via a software setting.",
+ "smw-property-predefined-edip": "This informatory message shown in a tooltip describes the [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property] holding [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Is_edit_protected Is edit protected].\n\nParameter:\n* $1 - name of predefined property",
+ "smw-property-predefined-long-edip": "This informatory message describes the value added to a [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].",
+ "smw-query-reference-link-label": "This is the label of a link.",
+ "smw-format-datatable-emptytable": "This is an informatory message shown by the \"datatable\" format.",
+ "smw-format-datatable-info": "This is an informatory message shown by the \"datatable\" format, e.g. \"Showing 1 to 12 of 27 entries.\"\n\n{{doc-important|Do not translate the placeholders <code>_START_</code>, <code>_END_</code> and <code>_TOTAL_</code>.}}",
+ "smw-format-datatable-infoempty": "This is an informatory message shown by the \"datatable\" format.",
+ "smw-format-datatable-infofiltered": "This is an informatory message shown by the \"datatable\" format. The placeholder holds the total number of results.\n\n{{doc-important|Do not translate the placeholder <code>_MAX_</code>.}}",
+ "smw-format-datatable-infothousands": "This message holds the thousands separator used by the \"datatables\" format.\n\n{{Optional}}",
+ "smw-format-datatable-lengthmenu": "This is an informatory message shown by the \"datatable\" format. The placeholder holds a drop-down field allowing to select the number of results to be shown, e.g. 10, 25, 50, etc.\n\n{{doc-important|Do not translate the placeholder <code>_MENU_</code>.}}",
+ "smw-format-datatable-loadingrecords": "This is a progress message shown by the \"datatable\" format.\n\n{{Identical|Loading}}",
+ "smw-format-datatable-processing": "This is a progress message shown by the \"datatable\" format.\n{{Identical|Processing}}",
+ "smw-format-datatable-search": "This is a field label shown by the \"datatable\" format. Translate as a noun.\n{{Identical|Search}}",
+ "smw-format-datatable-zerorecords": "This is an informatory message shown by the \"datatable\" format.",
+ "smw-format-datatable-first": "This is a message used as a selector. Show the ... resultset.\n\n{{Identical|First}}",
+ "smw-format-datatable-last": "This is a message used as a selector. Show the ... resultset.\n\n{{Identical|Last}}",
+ "smw-format-datatable-next": "This is a message used as a selector. Show the ... resultset.\n\n{{Identical|Next}}",
+ "smw-format-datatable-previous": "This is a message used as a selector. Show the ... resultset.\n\n{{Identical|Previous}}",
+ "smw-format-datatable-sortascending": "This is a selection message shown by the \"datatable\" format.",
+ "smw-format-datatable-sortdescending": "This is a selection message shown by the \"datatable\" format.",
+ "smw-format-datatable-toolbar-export": "This is the label of a link.\n\n{{Identical|Export}}",
+ "smw-format-list-separator": "Separator between entries in a result list. See 'sep' at https://www.semantic-mediawiki.org/wiki/Help:List_format#Parameters",
+ "smw-format-list-property-separator": "Separator between the properties of a result entry. See 'propsep' at https://www.semantic-mediawiki.org/wiki/Help:List_format#Parameters",
+ "smw-format-list-value-separator": "Separator between the values belonging to one property of a result entry. See 'valsep' at https://www.semantic-mediawiki.org/wiki/Help:List_format#Parameters",
+ "smw-format-list-field-label-separator": "Separator between a property label and the property values, e.g. the : in 'LABEL: VALUE'",
+ "smw-format-list-other-fields-open": "Separator opening the group of other values, e.g. the ( in 'MAIN VALUE (OTHER VALUES)'",
+ "smw-format-list-other-fields-close": "{{Optional}}\n\nSeparator closing the group of other values, e.g. the ) in 'MAIN VALUE (OTHER VALUES)'",
+ "smw-category-invalid-redirect-target": "This is an error message.\n\nParamters:\n* $1 - holds the name of the category",
+ "smw-parser-function-expensive-execution-limit": "This is an explanatory message.",
+ "smw-postproc-queryref": "This is an informatory message.",
+ "apihelp-smwinfo-summary": "This is an informatory message describing and API module.",
+ "apihelp-ask-summary": "This is an informatory message describing and API module.",
+ "apihelp-askargs-summary": "This is an informatory message describing and API module.",
+ "apihelp-browsebyproperty-summary": "This is an informatory message describing and API module.",
+ "apihelp-browsebysubject-summary": "This is an informatory message describing and API module.",
+ "apihelp-smwtask-summary": "This is an informatory message describing and API module.",
+ "apihelp-smwbrowse-summary": "This is an informatory message describing an API module.",
+ "apihelp-ask-parameter-api-version": "This is an informatory message for the API.",
+ "smw-api-invalid-parameters": "This is an error message emitted by an API module.\n\nParameter:\n* $1 - holds the name of the invalid parameter",
+ "smw-parser-recursion-level-exceeded": "This is an error message.\n\nParameter:\n* $1 - holds the count of recursions (integer) detected by the software",
+ "smw-property-page-list-count": "This is an informatory message on a property page.\n\nParameter:\n* $1 - holds the number of pages",
+ "smw-property-page-list-search-count": "This is an informatory message on a property page.\n\nParameters:\n* $1 - holds the number of pages\n* $2 - holds the datavalue searched for",
+ "smw-property-reserved-category": "This is the name of a reserved property name.\n\n{{Identical|Category}}",
+ "smw-category": "This is a label. singular only.\n\n{{Identical|Category}}",
+ "smw-datavalue-uri-invalid-scheme": "This is an error message.\n\nParameter:\n* $1 - holds the identifier of an Uniform Resource Identifier (URI) schema",
+ "smw-datavalue-uri-invalid-authority-path-component": "This is an error message. Translate authority in a sense of \"responsible for/responsibility\".\n\nParameters:\n* $1 - holds a broken URI\n* $2 - holds the transgressing part of the broken URI",
+ "smw-browse-property-group-title": "This is the name of a header on special page \"Browse\".",
+ "smw-browse-property-group-label": "This is the label of an entity similar to special property which is shown er on special page \"Browse\".",
+ "smw-browse-property-group-description": "This is the label of an entity similar to special property which is shown er on special page \"Browse\".",
+ "smw-property-predefined-ppgr": "This informatory message describes the [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\n* $1 holds the name of the special property.",
+ "smw-filter": "This is a label.\n{{Identical|Filter}}",
+ "smw-section-expand": "This is a tooltip.",
+ "smw-section-collapse": "This is a tooltip.",
+ "smw-ask-format-help-link": "This is an informatory message.\n\nParameter:\n* $1 - holds the name of a format",
+ "smw-help": "{{Identical|Help}}",
+ "smw-cheat-sheet": "This is the label of a link opening a tooltip.",
+ "smw-personal-jobqueue-watchlist": "This is the label of a link pointing to a help page.",
+ "smw-property-predefined-label-skey": "This is a label.\n\n{{Identical|Sortkey}}",
+ "smw-processing": "This is an informatory message.",
+ "smw-redirect-target-unresolvable": "This is an error message.\n\nParameter:\n* $1 - holds an error note.",
+ "smw-types-title": "This is the item label shown on a page in namespace \"Rule\". Do not confuse this with a datatype.\n\nParameter:\n* $1 - holds the string of the configuration defining the rule formatter, e.g. \"LINK_FORMAT_RULE\"\n\n{{Identical|Type}}",
+ "smw-schema-namespace-editcontentmodel-disallowed": "This is an error message.",
+ "smw-schema-namespace-edit-protection": "This is an informatory message.",
+ "smw-schema-error": "This is the label of a tab on a page in namespace \"smw/schema\".",
+ "smw-schema-error-schema": "This is an error message.\n\nParameter:\n* $1 - holds the erroneous specification",
+ "smw-schema-error-violation": "This is an error message.\n\nParameters:\n* $1 - holds the schema name\n* $2 - holds the error",
+ "smw-schema-error-type-missing": "This is an error message.",
+ "smw-schema-error-type-unknown": "This is an error message.\n\nParameter:\n* $1 - holds the name of the invalid type",
+ "smw-schema-title": "This is the label of a tab on a page in namespace \"smw/schema\".",
+ "smw-schema-type-help-link": "This is the link on a page in namespace \"smw/schema\".\n\nParameter:\n* $1 - holds the string of the configuration defining the schema formatter, e.g. \"LINK_FORMAT_SCHEMA\"\n\n{{Notranslate}}\n{{Optional}}",
+ "smw-schema-type": "This is the label of a tab on a page in namespace \"smw/schema\". \n\n{{Identical|Type}}",
+ "smw-schema-description-link-format-schema": "This is an informatory message shown on a type page in namespace \"smw/schema\".",
+ "smw-schema-description-search-form-schema": "This is an informatory message shown on a type page in namespace \"smw/schema\".",
+ "smw-schema-tag": "This is the label of a Link to a special property on a page in namespace \"smw/schema\".",
+ "smw-property-predefined-schema-desc": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-property-predefined-schema-def": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-property-predefined-schema-tag": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-property-predefined-long-schema-tag": "This informatory message describes the value added to a special property.",
+ "smw-property-predefined-schema-type": "This informatory message describes a [https://www.semantic-mediawiki.org/wiki/Help:Special_properties special property].\n\nParameter:\n* $1 holds the name of the special property.",
+ "smw-property-predefined-long-schema-type": "This informatory message describes the value added to a [https://semantic-mediawiki.org/wiki/Help:Special_properties special property].",
+ "smw-ask-title-keyword-type": "This is a label.",
+ "smw-ask-message-keyword-type": "This is an informatory message.\n\nParameter:\n* $1 holds the query condition",
+ "smw-remote-source-unavailable": "This is an error message.\n\nParameters:\n* $1 - holds the name of the remote target",
+ "smw-remote-source-disabled": "This is an error message.\n\nParameters:\n* $1 - holds the name of the remote target",
+ "smw-remote-source-unmatched-id": "This is an error message.\n\nParameters:\n* $1 - holds the name of the remote target",
+ "smw-remote-request-note": "This is an informatory message.\n\nParameters:\n* $1 - holds the name of the remote target",
+ "smw-remote-request-note-cached": "This is an informatory message.\n\nParameters:\n* $1 - holds the name of the remote target",
+ "smw-parameter-missing": "This is an error message emitted by an API module.\n\nParameter:\n* $1 - holds the name of the missing parameter",
+ "smw-property-tab-usage": "This is the label of a tab on a property or concept page.",
+ "smw-property-tab-redirects": "This is the label of a tab on a property page. \n\n{{Identical|Synonym}}",
+ "smw-property-tab-subproperties": "This is the label of a tab on a property page.",
+ "smw-property-tab-errors": "This is the label of a tab on a property page.",
+ "smw-property-tab-specification": "This is the label of a tab on a property page or concept page.",
+ "smw-concept-tab-list": "This is the label of a tab on a concept page. \n\n{{Identical|List}}",
+ "smw-concept-tab-errors": "This is the label of a tab on a concept page. \n\n{{Identical|Error}}",
+ "smw-ask-tab-result": "This is the label of a tab on special page \"Ask\".\n\n{{Identical|Result}}",
+ "smw-ask-tab-extra": "This is the label of a tab on special page \"Ask\".",
+ "smw-ask-tab-debug": "This is the label of a tab on special page \"Ask\".",
+ "smw-ask-tab-code": "This is the label of a tab on special page \"Ask\".",
+ "smw-helplink-concepts": "This message links to the respective help page on <semantic-mediawiki.org>.\n\n{{Optional}}",
+ "smw-install-incomplete-intro": "This is an error message.",
+ "smw-install-incomplete-populate-hash-field": "This is an error/informatory message.",
+ "smw-helplink": "This is the URL formatter for an link to the homepage to Semantic MediaWiki.\n{{Optional}} \n\nor\n\n{{Notranslate}}"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/qu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/qu.json
new file mode 100644
index 00000000..5f5976bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/qu.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "AlimanRuna"
+ ]
+ },
+ "smw_purge": "Musuqchay",
+ "browse": "Wikipi wamp'uy",
+ "smw_browselink": "Kaqninkunapi wamp'uy",
+ "smw-livepreview-loading": "Chaqnamuspa…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/rif.json b/www/wiki/extensions/SemanticMediaWiki/i18n/rif.json
new file mode 100644
index 00000000..0dc50840
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/rif.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77"
+ ]
+ },
+ "smw_browse_go": "Raḥ ɣa"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/rm.json b/www/wiki/extensions/SemanticMediaWiki/i18n/rm.json
new file mode 100644
index 00000000..bbed7f4c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/rm.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kazu89"
+ ]
+ },
+ "smw-ui-tooltip-title-legend": "Legenda",
+ "smw-livepreview-loading": "Chargiar…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ro.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ro.json
new file mode 100644
index 00000000..78ca53e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ro.json
@@ -0,0 +1,129 @@
+{
+ "@metadata": {
+ "authors": [
+ "AdiJapan",
+ "Firilacroco",
+ "KlaudiuMihaila",
+ "Minisarm",
+ "Stelistcristi",
+ "Ion"
+ ]
+ },
+ "smw-desc": "Wikiul dumneavoastră devine mai accesibil — pentru mașini ''și'' oameni ([https://www.semantic-mediawiki.org/wiki/Help:User_manual documentație on-line])",
+ "smw_viewasrdf": "Flux RDF",
+ "smw_finallistconjunct": "și",
+ "smw_factbox_head": "InformaÈ›ii despre „$1â€",
+ "smw_isspecprop": "Această proprietate este una specială pe acest wiki.",
+ "smw_concept_description": "Descrierea conceptului „$1â€",
+ "version-semantic": "Extensii semantice",
+ "smw_printername_count": "Numără rezultatele",
+ "smw_printername_csv": "Export CSV",
+ "smw_printername_dsv": "Export DSV",
+ "smw_printername_debug": "Interogare de depanare (pentru experți)",
+ "smw_printername_embedded": "Conținut de pagini încorporate",
+ "smw_printername_json": "Export JSON",
+ "smw_printername_list": "Listă",
+ "smw_printername_ol": "Enumerare",
+ "smw_printername_table": "Tabel",
+ "smw_printername_template": "Format",
+ "smw_printername_rdf": "Export RDF",
+ "smw_printername_category": "Categorie",
+ "validator-type-class-SMWParamSource": "text",
+ "smw_iq_disabled": "Interogările semantice au fost dezactivate pe acest wiki.",
+ "smw_iq_moreresults": "… mai multe rezultate",
+ "smw_parseerror": "Valoarea oferită nu este înțeleasă.",
+ "smw_notitle": "„$1†nu poate fi folosit ca nume de pagină în acest wiki.",
+ "smw_wrong_namespace": "Numai paginile din spațiul de nume \"$1\" sunt permise aici.",
+ "smw_emptystring": "Șirurile vide nu sunt acceptate.",
+ "smw_notinenum": "„$1†nu este în lista de valori posibile ($2) pentru această proprietate.",
+ "smw_noboolean": "„$1†nu este recunoscut ca valoare booleană (adevărat/fals).",
+ "smw_nofloat": "„$1†nu este număr.",
+ "smw_infinite": "Numerele mari precum „$1†nu sunt suportate.",
+ "smw_nodatetime": "Data „$1†nu a fost înțeleasă.",
+ "smw_unexpectedpart": "Partea „$1†a interogării nu a fost înțeleasă.\nRezultatele pot fi neașteptate.",
+ "smw_propvalueproblem": "Valoarea proprietății „$1†nu a fost înțeleasă.",
+ "smw_type_header": "Proprietăți de tipul „$1â€",
+ "smw_attribute_header": "Pagini care folosesc proprietatea „$1â€",
+ "smw_subproperty_header": "Subproprietăți",
+ "smw_subpropertyarticlecount": "Această proprietate are {{PLURAL:$1|următoarea subproprietate|următoarele $1 subproprietăți}}:",
+ "exportrdf": "Exportare pagini în RDF",
+ "smw_exportrdf_submit": "Export",
+ "uriresolver": "Rezolvator URI",
+ "properties": "Proprietăți",
+ "smw_properties_docu": "Următoarele proprietăți sunt folosite în wiki.",
+ "smw_property_template": "$1 de tipul $2 ($3)",
+ "smw_propertylackstype": "Nici un tip nu a fost specificat pentru această proprietate (presupunem deocamdată tipul $1).",
+ "smw_propertyhardlyused": "Această proprietate este puțin folosită în wiki!",
+ "unusedproperties": "Proprietăți neutilizate",
+ "smw_unusedproperties_docu": "Următoarele proprietăți există, deși nici o altă pagină nu le folosește.",
+ "smw_unusedproperty_template": "$1 de tipul $2",
+ "wantedproperties": "Proprietăți dorite",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|utilizare|utilizări}})",
+ "smw_purge": "Reîncărcare",
+ "types": "Tipuri",
+
+ "ask": "Căutare semantică",
+ "smw_ask_sortby": "Sortare după coloană (opțional)",
+ "smw_ask_ascorder": "Crescător",
+ "smw_ask_descorder": "Descrescător",
+ "smw_ask_submit": "Găsește rezultate",
+ "smw_ask_editquery": "Modifică interogarea",
+ "smw_add_sortcondition": "[Adaugă o condiție de sortare]",
+ "smw_ask_hidequery": "Ascunde interogarea",
+ "smw_ask_help": "Ajutor despre interogare",
+ "smw_ask_queryhead": "Interogare",
+ "smw_ask_printhead": "Date suplimentare de afișat",
+ "smw_ask_printdesc": "(adăugați câte un nume de proprietate pe fiecare rând)",
+ "smw_ask_format_as": "Formatare ca:",
+ "smw_ask_defaultformat": "implicit",
+ "smw_ask_otheroptions": "Alte opțiuni",
+ "smw-ask-otheroptions-collapsed-info": "Utilizați pictograma „plus†pentru a vedea toate opțiunile disponibile",
+ "smw_ask_show_embed": "Arată codul de încorporat",
+ "smw_ask_hide_embed": "Ascunde codul de încorporat",
+ "smw_ask_embed_instr": "Pentru a încorpora această interogare într-o pagină wiki, utilizați codul de mai jos.",
+ "smw-ask-delete": "[Șterge]",
+ "smw-ask-sorting": "Sortare",
+ "searchbyproperty": "Căutare după proprietate",
+ "smw_sbv_docu": "Caută toate paginile care au anumite proprietăți și valori.",
+ "smw_sbv_property": "Proprietate:",
+ "smw_sbv_value": "Valoare:",
+ "smw_sbv_submit": "Găsește rezultate",
+ "browse": "Răsfoire wiki",
+ "smw_browselink": "Răsfoire proprietăți",
+ "smw_browse_article": "Introduceți numele paginii de la care să porniți navigarea.",
+ "smw_browse_go": "Mergi",
+ "smw_browse_show_incoming": "afișează proprietățile care trimit aici",
+ "smw_browse_hide_incoming": "ascunde proprietățile care trimit aici",
+ "smw_browse_no_outgoing": "Această pagină nu are proprietăți.",
+ "smw_browse_no_incoming": "Nici o proprietate nu trimite la această pagină.",
+ "smw_inverse_label_default": "$1 din",
+ "smw_pp_from": "De la pagina",
+ "smw_pp_type": "Proprietate",
+ "smw_pp_submit": "Găsește rezultate",
+ "smw_result_prev": "Precedent",
+ "smw_result_next": "Următor",
+ "smw_result_results": "Rezultate",
+ "smw_result_noresults": "Ne pare rău, niciun rezultat.",
+ "smwadmin": "Funcții administrative pentru Semantic MediaWiki",
+ "smw_smwadmin_return": "ÃŽnapoi la $1",
+ "smw_smwadmin_datarefreshbutton": "Pornește actualizarea datelor",
+ "smw_smwadmin_datarefreshstop": "Oprește această actualizare",
+ "smw_smwadmin_datarefreshstopconfirm": "Da, sunt sigur.",
+ "smw_smwadmin_support": "Obținere ajutor",
+ "smw_adminlinks_datastructure": "Structura datelor",
+ "smw_adminlinks_displayingdata": "Afișarea datelor",
+ "smw-createproperty-isproperty": "Aceasta este o proprietate de tipul $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Valoarea admisă pentru această proprietate este|Valorile admise pentru această proprietate sunt}}:",
+ "smw-info-par-message": "Mesaj spre afișare.",
+ "smw-ui-tooltip-title-property": "Proprietate",
+ "smw-ui-tooltip-title-quantity": "Cantitate",
+ "smw-ui-tooltip-title-info": "Informații",
+ "smw-ui-tooltip-title-warning": "Eroare",
+ "smw-ui-tooltip-title-parameter": "Parametru",
+ "smw-ui-tooltip-title-event": "Eveniment",
+ "smw-ui-tooltip-title-note": "Notă",
+ "smw_unknowntype": "Tipul acestei proprietăți este invalid",
+ "smw_concept_header": "Pagini ale conceptului „$1â€",
+ "smw-qp-empty-data": "Imposibil de afișat datele solicitate ca urmare a unor criterii de selecție insuficiente.",
+ "smw-livepreview-loading": "Încărcare…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/roa-tara.json b/www/wiki/extensions/SemanticMediaWiki/i18n/roa-tara.json
new file mode 100644
index 00000000..161e4afe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/roa-tara.json
@@ -0,0 +1,58 @@
+{
+ "@metadata": {
+ "authors": [
+ "Joetaras"
+ ]
+ },
+ "smw_viewasrdf": "Feed RDF",
+ "smw_finallistconjunct": ", e",
+ "smw_factbox_head": "Fatte sus a \"$1\"",
+ "smw_isspecprop": "Sta probbietà jè 'na probbietà speciale jndr'à sta uicchi.",
+ "smw_concept_description": "Descrizione d'u congette \"$1\"",
+ "version-semantic": "Estenziune semandeche",
+ "smw_baduri": "Le URI da 'u module \"$1\" non ge sò permesse.",
+ "smw_printername_count": "Resultate d'u condegge",
+ "smw_printername_csv": "Esporte CSV",
+ "smw_printername_dsv": "Esporte DSV",
+ "smw_printername_debug": "Analise de l'inderrogazione (pe esperte)",
+ "smw_printername_embedded": "'Ngapsule le pàggene de condenute",
+ "smw_printername_json": "esporte JSON",
+ "smw_printername_list": "Elenghe",
+ "smw_printername_ol": "Enumerazione",
+ "smw_printername_table": "Tabbelle",
+ "smw_printername_template": "Template",
+ "smw_printername_rdf": "Esporte RDF",
+ "smw_printername_category": "Categorije",
+ "validator-type-class-SMWParamSource": "teste",
+ "smw-paramdesc-limit": "'U numere massime de resultate da turnà",
+ "smw-paramdesc-link": "Fà vedè le calore cumme collegaminde",
+ "smw-paramdesc-csv-sep": "Separatore da ausà",
+ "smw-paramdesc-dsv-separator": "Separatore da ausà",
+ "smw-paramdesc-dsv-filename": "'U nome pu file DSV",
+ "smw-paramdesc-filename": "'U nome pu file d'u resultate",
+ "smw_true_words": "vere,true,t,sine,si,yes,y",
+ "smw_false_words": "fause,false,f,none,no,n",
+ "smw_exportrdf_submit": "Esporte",
+ "properties": "Proprietà",
+ "smw_property_template": "$1 de tipe $2 ($3 {{PLURAL:$3|ause}})",
+ "smw_unusedproperty_template": "$1 de tipe $2",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|ause|ausene}})",
+ "types": "Tipe",
+ "smw_ask_ascorder": "Inghiananne",
+ "smw_ask_descorder": "Scennènne",
+ "smw_ask_submit": "Iacchie le resultate",
+ "smw_ask_editquery": "Cange l'inderrogazione",
+ "smw_add_sortcondition": "[Aggiunge 'a condizione de ordinamende]",
+ "smw_ask_hidequery": "Scunne l'inderrogazione",
+ "smw_ask_queryhead": "Inderroghe",
+ "smw_ask_defaultformat": "de base",
+ "smw_ask_otheroptions": "Otre opziune",
+ "smw-ask-delete": "[Scangelle]",
+ "smw_sbv_property": "Probbietà:",
+ "smw_sbv_value": "Valore:",
+ "browse": "Sfogghie 'a uicchi",
+ "smw_browse_go": "Véje",
+ "smw-ui-tooltip-title-property": "Probbietà",
+ "smw-livepreview-loading": "Stoche a careche…",
+ "smw-datavalue-invalid-number": "\"$1\" non ge pò essere 'nderpretate cumme 'nu numere."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ru.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ru.json
new file mode 100644
index 00000000..ee64c81d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ru.json
@@ -0,0 +1,821 @@
+{
+ "@metadata": {
+ "authors": [
+ "Eleferen",
+ "Ferrer",
+ "Innv",
+ "Kaganer",
+ "Lockal",
+ "Okras",
+ "Pastakhov",
+ "QuestPC",
+ "Van de Bugger",
+ "ÐлекÑандр Сигачёв",
+ "ì•„ë¼",
+ "Meshkov.a",
+ "NBS",
+ "Tourorist",
+ "Mariya",
+ "Ядерный Трамвай",
+ "Iniquity",
+ "Macofe",
+ "Alexandr Efremov",
+ "Mailman",
+ "Туллук",
+ "Irus",
+ "Kareyac",
+ "Zpizza",
+ "Alex Great",
+ "Nemo bis",
+ "Redredsonia",
+ "Nitch",
+ "Helpau",
+ "Putnik",
+ "LeonardoGW",
+ "Wirbel78",
+ "Eduardo Addad de Oliveira",
+ "Lugimax",
+ "Edible Melon",
+ "Staspotanin2",
+ "Gehhrr",
+ "Movses",
+ "Mouse21",
+ "Patrick Star",
+ "Happy13241",
+ "Silicium Power",
+ "Vlad5250",
+ "Evs",
+ "Shaleych",
+ "Marshmallych",
+ "Drbug",
+ "Батыр Комдошев",
+ "Infovarius",
+ "Soul Train",
+ "Alex Mashin",
+ "Nk88"
+ ]
+ },
+ "smw-desc": "Делает вашу вики более доÑтупной — Ð´Ð»Ñ Ð¼Ð°ÑˆÐ¸Ð½ ''и'' людей ([https://www.semantic-mediawiki.org/wiki/Help:User_manual Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð² Ñети])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-semantics-not-enabled": "ФункциональноÑÑ‚ÑŒ Semantic MediaWiki не подключена на Ñтой вики.",
+ "smw_viewasrdf": "RDF иÑточник",
+ "smw_finallistconjunct": " и",
+ "smw-factbox-head": "... больше о \"$1\"",
+ "smw-factbox-facts": "Факты",
+ "smw-factbox-facts-help": "Показывает выÑÐºÐ°Ð·Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¸ факты, которые были Ñозданы пользователем",
+ "smw-factbox-facts-derived": "Полученные факты",
+ "smw-factbox-facts-derived-help": "Показывает факты, которые были получены из правил, или Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ других методов раÑÑуждений",
+ "smw_isspecprop": "Это ÑвойÑтво отноÑитÑÑ Ðº чиÑлу Ñпециальных ÑвойÑтв данного вики-Ñайта.",
+ "smw-concept-cache-header": "ИÑпользование кÑша",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count concept cache] Ñодержит {{PLURAL:$1|'''one''' entity|'''$1''' entities}} ($2).",
+ "smw-concept-no-cache": "КÑш отÑутÑтвует.",
+ "smw_concept_description": "ОпиÑание концепта «$1»",
+ "smw_no_concept_namespace": "Концепты могут определÑÑ‚ÑŒÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ на Ñтраницах, принадлежащих проÑтранÑтву имён «Концепт:» («Concept:»).",
+ "smw_multiple_concepts": "Страница предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚ Ñодержать только одно определение предÑтавлениÑ.",
+ "smw_concept_cache_miss": "Концепт «$1» в наÑтоÑщий момент не может быть иÑпользовано, так как наÑтройка вики-Ñайта требует, чтобы его результат определÑлÑÑ Ð² фоновом режиме. ЕÑли данное Ñообщение не иÑчезнет через некоторое времÑ, обратитеÑÑŒ к админиÑтратору вики-Ñайта Ð´Ð»Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ концепта.",
+ "smw_noinvannot": "Обратным ÑвойÑтвам не могут быть приÑвоены значениÑ.",
+ "version-semantic": "СемантичеÑкие раÑширениÑ",
+ "smw_baduri": "Извините, но ÑÑылки из диапазона \"$1\" не доÑтупны отÑюда.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Результаты подÑчёта",
+ "smw_printername_csv": "ЭкÑпорт CSV",
+ "smw_printername_dsv": "ЭкÑпорт DSV",
+ "smw_printername_debug": "Отладка запроÑов (Ð´Ð»Ñ ÑкÑпертов)",
+ "smw_printername_embedded": "Включаемое Ñодержимое Ñтраниц",
+ "smw_printername_json": "ЭкÑпорт JSON",
+ "smw_printername_list": "СпиÑок",
+ "smw_printername_plainlist": "Обычный ÑпиÑок",
+ "smw_printername_ol": "ПеречиÑление",
+ "smw_printername_ul": "СпиÑок",
+ "smw_printername_table": "Таблица",
+ "smw_printername_broadtable": "Ð¨Ð¸Ñ€Ð¾ÐºÐ°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°",
+ "smw_printername_template": "Шаблон",
+ "smw_printername_templatefile": "Файл шаблона",
+ "smw_printername_rdf": "RDF-ÑкÑпорт",
+ "smw_printername_category": "КатегориÑ",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw-paramdesc-limit": "МакÑимальное чиÑло возвращаемых результатов",
+ "smw-paramdesc-offset": "Смещение первого результата",
+ "smw-paramdesc-headers": "Показывать заголовки/Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ ÑвойÑтв",
+ "smw-paramdesc-mainlabel": "Обозначение, даваемое названию главной Ñтраницы",
+ "smw-paramdesc-link": "Показывать Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÐºÐ°Ðº ÑÑылки",
+ "smw-paramdesc-intro": "ТекÑÑ‚ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ результатами запроÑа, еÑли они еÑÑ‚ÑŒ",
+ "smw-paramdesc-outro": "ТекÑÑ‚ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле результатов запроÑа, еÑли они еÑÑ‚ÑŒ",
+ "smw-paramdesc-default": "ТекÑÑ‚ Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ, еÑли нет результатов запроÑа",
+ "smw-paramdesc-sep": "Разделитель значений",
+ "smw-paramdesc-propsep": "Разделитель между ÑвойÑтвами в конечной запиÑи",
+ "smw-paramdesc-valuesep": "Разделитель между значениÑми переменной Ð´Ð»Ñ Ñтого ÑвойÑтва",
+ "smw-paramdesc-showsep": "Показать разделитель в верхней чаÑти файла CSV (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "ВмеÑто Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð²Ñех значений, подÑчитать и отобразить их вхождениÑ.",
+ "smw-paramdesc-distributionsort": "Сортировать раÑпределение значений по чаÑтоте поÑвлениÑ.",
+ "smw-paramdesc-distributionlimit": "Ограничение раÑÐ¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ Ð´Ð»Ñ Ð¿Ð¾Ð´Ñчёта только некоторыми значениÑми.",
+ "smw-paramdesc-aggregation": "Укажите, к чему должна отноÑитьÑÑ Ð°Ð³Ñ€ÐµÐ³Ð°Ñ†Ð¸Ñ",
+ "smw-paramdesc-template": "Ðазвание шаблона, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ которого будут выводитьÑÑ Ð´Ð°Ð½Ð½Ñ‹Ðµ",
+ "smw-paramdesc-columns": "КоличеÑтво Ñтолбцов Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° результатов поиÑка",
+ "smw-paramdesc-userparam": "Значение, передаваемое в каждый вызов шаблонов, еÑли шаблон иÑпользуетÑÑ",
+ "smw-paramdesc-class": "Дополнительный клаÑÑ CSS Ð´Ð»Ñ ÑпиÑка",
+ "smw-paramdesc-introtemplate": "Ðазвание шаблона Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ результатами запроÑа, еÑли они еÑÑ‚ÑŒ",
+ "smw-paramdesc-outrotemplate": "Ðазвание шаблона Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле результатов запроÑа, еÑли они еÑÑ‚ÑŒ",
+ "smw-paramdesc-embedformat": "HTML-тег, иÑпользуемый Ð´Ð»Ñ Ð¾Ð±Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð²",
+ "smw-paramdesc-embedonly": "Ðе отображать заголовки",
+ "smw-paramdesc-table-class": "Дополнительный клаÑÑ CSS Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†",
+ "smw-paramdesc-table-transpose": "Отобразить заголовки таблицы по вертикали, а результаты — по горизонтали",
+ "smw-paramdesc-rdfsyntax": "Какой ÑинтакÑÐ¸Ñ RDF иÑпользовать",
+ "smw-paramdesc-csv-sep": "ИÑпользовать разделитель",
+ "smw-paramdesc-csv-valuesep": "ИÑпользовать разделитель значений",
+ "smw-paramdesc-csv-merge": "Объединить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñтолбцов и колонок Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ‡Ð½Ñ‹Ð¼ идентификатором Ñубъекта (aka Ð¿ÐµÑ€Ð²Ð°Ñ ÐºÐ¾Ð»Ð¾Ð½ÐºÐ°)",
+ "smw-paramdesc-csv-bom": "Добавить BOM (Ñимвол Ð´Ð»Ñ Ñигнальной ÑущноÑти) в верху выходного файла",
+ "smw-paramdesc-dsv-separator": "ИÑпользовать разделитель",
+ "smw-paramdesc-dsv-filename": "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° DSV",
+ "smw-paramdesc-filename": "Ð˜Ð¼Ñ Ð´Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð½Ð¾Ð³Ð¾ файла",
+ "smw-smwdoc-description": "Показывает таблицу вÑех параметров, которые могут иÑпользоватьÑÑ Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ формата вывода результата вмеÑте Ñо значениÑми по умолчанию и опиÑаниÑми.",
+ "smw-smwdoc-default-no-parameter-list": "Этот результат не предоÑтавлÑет Ñпецифичные Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° параметры.",
+ "smw-smwdoc-par-format": "Результирующий формат Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ð¸ по параметрам.",
+ "smw-smwdoc-par-parameters": "Какие параметры Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒË Â«specific» — Ð´Ð»Ñ Ñ‚ÐµÑ…, которые добавлены форматом, «base» — Ð´Ð»Ñ Ñ‚ÐµÑ…, которые доÑтупных во вÑех форматах, и «all» — Ð´Ð»Ñ Ð¾Ð±Ð¾Ð¸Ñ….",
+ "smw-paramdesc-sort": "СвойÑтво, по которому Ñортировать запроÑ",
+ "smw-paramdesc-order": "ПорÑдок Ñортировки запроÑа",
+ "smw-paramdesc-searchlabel": "ТекÑÑ‚ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð¸Ñка",
+ "smw-paramdesc-named_args": "Имена аргументов, передаваемых в шаблон",
+ "smw-paramdesc-template-arguments": "ОпределÑет, как названные аргументы передаютÑÑ Ðº шаблону",
+ "smw-paramdesc-import-annotation": "Дополнительные аннотированные данные, которые будут Ñкопированы во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð°Ñ€Ñинга Ñубъекта.",
+ "smw-paramdesc-export": "Параметры ÑкÑпорта",
+ "smw-paramdesc-prettyprint": "КраÑивый вывод на печать, который отображает дополнительные отÑтупы и переводы Ñтрок",
+ "smw-paramdesc-json-unescape": "Выводить неÑкранированные Ñлеши и многобайтные Ñимволы Юникода.",
+ "smw-paramdesc-json-type": "Тип Ñериализации",
+ "smw-paramdesc-source": "Ðльтернативный иÑточник запроÑа",
+ "smw-paramdesc-jsonsyntax": "СинтакÑÐ¸Ñ JSON, который будет иÑпользоватьÑÑ",
+ "smw-printername-feed": "Лента RSS и Atom",
+ "smw-paramdesc-feedtype": "Тип канала",
+ "smw-paramdesc-feedtitle": "ТекÑÑ‚ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ ÐºÐ°Ð½Ð°Ð»Ð°",
+ "smw-paramdesc-feeddescription": "ТекÑÑ‚ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² качеÑтве опиÑание канала",
+ "smw-paramdesc-feedpagecontent": "Содержимое Ñтраницы, которое будет отображатьÑÑ Ð½Ð° канале",
+ "smw-label-feed-description": "$2-канал $1",
+ "smw_iq_disabled": "Извините, но вÑтроенные запроÑÑ‹ отключены Ð´Ð»Ñ Ñтого Ñайта.",
+ "smw_iq_moreresults": "… Ñледующие результаты",
+ "smw_parseerror": "Переданное значение не было понÑто.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": "",
+ "smw_notitle": "“$1†не может быть иÑпользован как заголовок Ñтатьи на данном Ñайте.",
+ "smw_noproperty": "«$1» не может иÑпользоватьÑÑ ÐºÐ°Ðº Ð¸Ð¼Ñ ÑвойÑтва в вики.",
+ "smw_wrong_namespace": "ЗдеÑÑŒ допуÑтимы только Ñтраницы из проÑтранÑтва имён «$1».",
+ "smw_manytypes": "Более одного типа определено Ð´Ð»Ñ ÑвойÑтва.",
+ "smw_emptystring": "ПуÑтые Ñтроки недопуÑтимы.",
+ "smw_notinenum": "«$1» не входит в ÑпиÑок ($2) [[Property:Allows value|допуÑтимых значений]] Ð´Ð»Ñ ÑвойÑтва «$3».",
+ "smw-datavalue-constraint-error-allows-value-list": "«$1» не входит в ÑпиÑок ($2) [[Property:Allows value|допуÑтимых значений]] Ð´Ð»Ñ ÑвойÑтва «$3».",
+ "smw-datavalue-constraint-error-allows-value-range": "«$1» не входит в ÑпиÑок ($2) ограничений Ð´Ð»Ñ [[Property:Allows value|допуÑтимых значений]] Ð´Ð»Ñ ÑвойÑтва «$3».",
+ "smw_noboolean": "«$1» — не булево значение (да/нет).",
+ "smw_true_words": "да,t,yes,д,иÑтина,и,true",
+ "smw_false_words": "нет,f,no,n,н,ложь,л,false",
+ "smw_nofloat": "«$1» — не чиÑло.",
+ "smw_infinite": "Столь длинные чиÑла как $1 не поддерживаютÑÑ.",
+ "smw_unitnotallowed": "«$1» не объÑвлена ​​как допуÑÑ‚Ð¸Ð¼Ð°Ñ ÐµÐ´Ð¸Ð½Ð¸Ñ†Ð° Ð¸Ð·Ð¼ÐµÑ€ÐµÐ½Ð¸Ñ Ñтого ÑвойÑтва.",
+ "smw_nounitsdeclared": "Ð”Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ ÑвойÑтва не объÑвлены единицы измерениÑ.",
+ "smw_novalues": "Ðе указаны значениÑ.",
+ "smw_nodatetime": "Дата «$1» не раÑпознана.",
+ "smw_toomanyclosing": "Ошибка: Слишком много вхождений “$1†в данном запроÑе.",
+ "smw_noclosingbrackets": "ОткрывающаÑÑÑ Ð¿Ð°Ñ€Ð° Ñкобок «<nowiki>[[</nowiki>» не была закрыта парой ÑоответÑтвующих ей закрывающих Ñкобок «]]» в данном запроÑе.",
+ "smw_misplacedsymbol": "Ошибка: ИÑпользование Ñимвола “$1†в данном меÑте лишено ÑмыÑла.",
+ "smw_unexpectedpart": "Ошибка: ЧаÑÑ‚ÑŒ “$1†запроÑа не была раÑпознана. Результаты могут отличатьÑÑ Ð¾Ñ‚ ожидаемых.",
+ "smw_emptysubquery": "Ошибка: Ð’ одном из подзапроÑов не указано правильного знака уÑловиÑ.",
+ "smw_misplacedsubquery": "Ошибка: ÐŸÐ¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸ÑпользуетÑÑ Ð² меÑте, где подзапроÑÑ‹ не разрешены.",
+ "smw_valuesubquery": "ПодзапроÑÑ‹ не поддерживаютÑÑ Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ ÑвойÑтва «$1».",
+ "smw_badqueryatom": "ЧаÑÑ‚ÑŒ запроÑа «<nowiki>[[…]]</nowiki>» не была разобрана.",
+ "smw_propvalueproblem": "Ошибка: Значение ÑвойÑтва “$1†не разобрано.",
+ "smw_noqueryfeature": "ЧаÑÑ‚ÑŒ запроÑа была опущена, так как некоторые из возможноÑтей Ñзыка запроÑов не поддерживаютÑÑ Ð½Ð° Ñтом вики-Ñайте ($1).",
+ "smw_noconjunctions": "ЧаÑÑ‚ÑŒ запроÑа была опущена, так как Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Â«Ð›Ð¾Ð³Ð¸Ñ‡ÐµÑкое И» не поддерживаетÑÑ Ð½Ð° Ñтом вики-Ñайте ($1).",
+ "smw_nodisjunctions": "Ошибка: Дизъюнкции (логичеÑкое ИЛИ) не поддерживаютÑÑ Ð´Ð°Ð½Ð½Ñ‹Ð¼ Ñайтом, поÑтому иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑŽÑ‰Ð°Ñ Ð¸Ñ… чаÑÑ‚ÑŒ запроÑа была проигнорирована ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|1=Указанное уÑловие|$2 указанных уÑловиÑ|$2 указанных уÑловий}} запроÑа не могут быть выполнены из-за Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° глубину или размер запроÑа: <code>$1</code>.",
+ "smw_notemplategiven": "Чтобы данный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÑлÑÑ, необходимо задать значение Ð´Ð»Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° «template».",
+ "smw_db_sparqlqueryproblem": "Ðе удалоÑÑŒ получить результат запроÑа к базе данных SPARQL. Эта может быть Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° или проблема в программном обеÑпечении базы данных.",
+ "smw_db_sparqlqueryincomplete": "ПоиÑк ответа на Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¾ÐºÐ°Ð·Ð°Ð»ÑÑ Ñлишком Ñложным и был прерван. Ðекоторые результаты могут быть не показаны. По возможноÑти попробуйте упроÑтить запроÑ.",
+ "smw_type_header": "СвойÑтва типа “$1â€",
+ "smw_typearticlecount": "{{PLURAL:$1|ОтображаетÑÑ|ОтображаютÑÑ}} $1 {{PLURAL:$1|ÑвойÑтво|ÑвойÑтва|ÑвойÑтв}} Ñтого типа.",
+ "smw_attribute_header": "Страницы, иÑпользующие ÑвойÑтво “$1â€",
+ "smw_attributearticlecount": "{{PLURAL:$1|ОтображаетÑÑ|ОтображаютÑÑ}} $1 {{PLURAL:$1|Ñтраница, иÑпользующаÑ|Ñтраницы, иÑпользующие|Ñтраниц, иÑпользующих}} Ñто ÑвойÑтво.",
+ "smw-propertylist-subproperty-header": "ПодÑвойÑтва",
+ "smw-propertylist-redirect-header": "Синонимы",
+ "smw-propertylist-error-header": "Страницы Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¼Ð¸ назначениÑми",
+ "smw-propertylist-count": "Показ $1 {{PLURAL:$1|1=ÑвÑзанной ÑущноÑти|ÑвÑзанных ÑущноÑтей}}.",
+ "smw-propertylist-count-with-restricted-note": "Показ $1 {{PLURAL:$1|1=ÑвÑзанной ÑущноÑти|ÑвÑзанных ÑущноÑтей}} (доÑтупно больше, но отобразить можно только «$2»).",
+ "smw-propertylist-count-more-available": "Показ $1 {{PLURAL:$1|1=ÑвÑзанной ÑущноÑти|ÑвÑзанных ÑущноÑтей}} (доÑтупно больше).",
+ "exportrdf": "ЭкÑпорт Ñтраниц в RDF",
+ "smw_exportrdf_docu": "Эта Ñтраница позволÑет ÑкÑпортировать чаÑти Ñтатьи в формате RDF. Ðаберите заголовки необходимых Ñтатей по одному на Ñтроку.",
+ "smw_exportrdf_recursive": "РекурÑивный ÑкÑпорт вÑех ÑвÑзанных Ñтраниц. Результат Ñтой операции может быть очень большим!",
+ "smw_exportrdf_backlinks": "Также ÑкÑпортировать вÑе Ñтраницы, которые ÑÑылаютÑÑ Ð½Ð° ÑкÑпортируемые Ñтраницы. Генерирует RDF Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¾Ð¹ полноценной навигации.",
+ "smw_exportrdf_lastdate": "Ðе ÑкÑпортировать Ñтраницы, которые не менÑлиÑÑŒ Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð¹ даты.",
+ "smw_exportrdf_submit": "ЭкÑпорт",
+ "uriresolver": "Преобразователь URI",
+ "properties": "СвойÑтва",
+ "smw_properties_docu": "Следующие ÑвойÑтва иÑпользуютÑÑ Ð½Ð° данном Ñайте.",
+ "smw_property_template": "$1 имеет тип $2, ($3 {{PLURAL:$3|иÑпользование|иÑпользованиÑ|иÑпользований}})",
+ "smw_propertylackspage": "Каждое ÑвойÑтво должно иметь Ñвою Ñтраницу опиÑаниÑ!",
+ "smw_propertylackstype": "Данному ÑвойÑтву не ÑопоÑтавлен тип данных (по умолчанию будет иÑпользоватьÑÑ Ñ‚Ð¸Ð¿ $1).",
+ "smw_propertyhardlyused": "Это ÑвойÑтво почти не иÑпользуетÑÑ Ð½Ð° Ñайте.",
+ "smw-property-name-invalid": "СвойÑтво $1 не может быть иÑпользовано (недопуÑтимое Ð¸Ð¼Ñ ÑвойÑтва).",
+ "smw-property-name-reserved": "«$1» входит в чиÑло зарезервированных имён и не должно быть иÑпользовано как ÑвойÑтво. Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ том, почему Ñто Ð¸Ð¼Ñ Ð·Ð°Ñ€ÐµÐ·ÐµÑ€Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¾, может ÑодержатьÑÑ Ð² [https://www.semantic-mediawiki.org/wiki/Help:Property_naming Ñтой Ñправочной Ñтранице].",
+ "smw-sp-property-searchform": "Показать ÑвойÑтва, которые Ñодержат:",
+ "smw-sp-property-searchform-inputinfo": "Ввод чувÑтвителен к региÑтру, и когда иÑпользуетÑÑ Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸, отображаютÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ те ÑвойÑтва, которые ÑоответÑтвуют уÑловию.",
+ "smw-special-property-searchform": "Показать ÑвойÑтва, которые Ñодержат:",
+ "smw-special-property-searchform-inputinfo": "Ввод чувÑтвителен к региÑтру, и когда иÑпользуетÑÑ Ð´Ð»Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ð¸, отображаютÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ те ÑвойÑтва, которые ÑоответÑтвуют уÑловию.",
+ "smw-special-property-searchform-options": "Параметры",
+ "smw-special-wantedproperties-filter-label": "Фильтр:",
+ "smw-special-wantedproperties-filter-none": "Ðичего",
+ "smw-special-wantedproperties-filter-unapproved": "неподтвержденный",
+ "smw-special-wantedproperties-filter-unapproved-desc": "ÐžÐ¿Ñ†Ð¸Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð° иÑпользована в ÑвÑзи Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ð¾Ð¼ полномочий",
+ "concepts": "Концепты",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Концепты Концепт] можно раÑÑматривать как «динамичеÑкую категорию», Ñ‚.е. как ÑовокупноÑÑ‚ÑŒ Ñтраниц, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½Ðµ Ñоздана вручную, а Ñобрана СемантичеÑкой Медиавики из опиÑÐ°Ð½Ð¸Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ запроÑа.",
+ "smw-special-concept-header": "СпиÑок концептов",
+ "smw-special-concept-count": "Ð’ ÑпиÑке {{PLURAL:$1|1=чиÑлитÑÑ Ñледующий|чиÑлÑÑ‚ÑÑ Ñледующие}} {{PLURAL:$1|$1 концепциÑ|$1 концепции|$1 концепций|1=концепциÑ}}",
+ "smw-special-concept-empty": "Концепции не найдены.",
+ "unusedproperties": "ÐеиÑпользуемые ÑвойÑтва",
+ "smw-unusedproperties-docu": "Ðа Ñтой Ñтранице перечиÑлены [https://www.semantic-mediawiki.org/wiki/Wanted_properties неиÑпользуемые ÑвойÑтва], которые определены, но при Ñтом ни одна Ñтраница не иÑпользует их. См. также:[[Special:Properties|иÑпользуемые]] и [[Special:WantedProperties|требуемые ÑвойÑтва]].",
+ "smw-unusedproperty-template": "$1 имеет тип $2",
+ "wantedproperties": "ÐеопиÑанные ÑвойÑтва",
+ "smw-wantedproperties-docu": "Ðа Ñтой Ñтранице перечиÑлены [https://www.semantic-mediawiki.org/wiki/Wanted_properties необходимые ÑвойÑтва], которые иÑпользуютÑÑ Ð½Ð° вики, но не имеют Ñтраницу Ñ Ð¸Ñ… опиÑанием. Ð”Ð»Ñ Ð´Ð¸Ñ„Ñ„ÐµÑ€ÐµÐ½Ñ†Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ подхода, Ñм. Ñлужебные Ñтраницы Ñ [[Special:Properties|заполненными]] или [[Special:UnusedProperties|неиÑпользуемыми ÑвойÑтвами]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|иÑпользование|иÑпользованиÑ|иÑпользований}})",
+ "smw-special-wantedproperties-docu": "Ðа Ñтой Ñтранице перечиÑлены [https://www.semantic-mediawiki.org/wiki/Wanted_properties необходимые ÑвойÑтва], иÑпользуемые в вики, но не имеющие Ñвоей Ñтраницы опиÑаниÑ. Ð”Ð»Ñ Ñ€Ð°Ð·Ð´ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ проÑмотре Ñм. Ñлужебные Ñтраницы Ñо ÑпиÑком [[Special:Properties|доÑтупных]] или [[Special:UnusedProperties|неиÑпользуемых]] ÑвойÑтв.",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|иÑпользование|иÑпользованиÑ|иÑпользований}})",
+ "smw_purge": "Обновить",
+ "smw-purge-failed": "Обновление не выполнено",
+ "types": "Типы",
+ "smw_types_docu": "СпиÑок [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes доÑтупных типов данных], в котором каждый [https://www.semantic-mediawiki.org/wiki/Help:Datatype тип данных] предÑтавлÑет Ñобой уникальный набор атрибутов Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ Ð·Ñ€ÐµÐ½Ð¸Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ…Ð°Ñ€Ð°ÐºÑ‚ÐµÑ€Ð¸Ñтик, которые можно задавать Ð´Ð»Ñ ÑвойÑтв.",
+ "smw-special-types-no-such-type": "\"$1\" неизвеÑтен или не был указан как дейÑтвительный тип данных.",
+ "smw-statistics": "СемантичеÑÐºÐ°Ñ ÑтатиÑтика",
+ "smw-statistics-property-instance": "{{PLURAL:$1|1=Значение|ЗначениÑ|Значений}} ÑвойÑтва (вÑего)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|1=СвойÑтво|СвойÑтва|СвойÑтв}}]] (вÑего)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|1=СвойÑтво|СвойÑтва|СвойÑтв}} (вÑего)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|СвойÑтво|СвойÑтва|СвойÑтв}}]] (Ñ Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одним значением)",
+ "smw-statistics-property-page": "{{PLURAL:$1|СвойÑтво|СвойÑтва|СвойÑтв}} (зарегиÑтрировано на Ñтранице)",
+ "smw-statistics-property-type": "{{PLURAL:$1|СвойÑтво|СвойÑтва|СвойÑтв}} (назначено Ð´Ð»Ñ Ñ‚Ð¸Ð¿Ð° данных)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|ЗапроÑ|ЗапроÑа|ЗапроÑов}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|ЗапроÑ|ЗапроÑа|ЗапроÑов}}]]",
+ "smw-statistics-query-size": "Размер запроÑа",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|1=КонцепциÑ|Концепции|Концепций}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|1=КонцепциÑ|Концепции|Концепций}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Подобъект|Подобъекта|Подобъектов}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Подобъект|Подобъекта|Подобъектов}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|1=Тип данных|Типа данных|Типов данных}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Значение ÑвойÑтва|Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвойÑтва|Значений ÑвойÑтв}} ([[Special:ProcessingErrorList|{{PLURAL:$1|Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ|неправильные аннотации|неправильных аннотаций}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Значение ÑвойÑтва|Ð—Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвойÑтва|Значений ÑвойÑтв}} ({{PLURAL:$1|Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ|неправильные аннотации|неправильных аннотаций}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|УÑтаревший объект (отмеченный Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)|УÑтаревших объекта (отмеченных Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)|УÑтаревших объектов (отмеченных Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ)}}",
+ "smw_uri_doc": "Преобразователь URI оÑущеÑтвлÑет [$1 W3C поиÑк http Ñ‚Ñгов Ñ Ð¸Ñпользованием Range-14].\nÐ”Ð°Ð½Ð½Ð°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑ‚ÑŒ упрощает поиÑк ÑемантичеÑкой информации.",
+ "ask": "СемантичеÑкий поиÑк",
+ "smw-ask-help": "Эта ÑÐµÐºÑ†Ð¸Ñ Ñодержит ÑÑылки, которые помогают объÑÑнить, как иÑпользовать ÑинтакÑÐ¸Ñ <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Selecting pages] опиÑывает выбранные Ñтраницы и уÑÐ»Ð¾Ð²Ð¸Ñ Ñборки\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Search operators] ÑпиÑок доÑтупных операторов поиÑка, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ‚Ð¾Ñ€Ñ‹ Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа диапазона и знаки подÑтановки\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Displaying information] выделÑет иÑпользование раÑпечатки уÑловий и опций форматированиÑ",
+ "smw_ask_sortby": "Сортировать по Ñтолбцу (необÑзательно)",
+ "smw_ask_ascorder": "По возраÑтанию",
+ "smw_ask_descorder": "По убыванию",
+ "smw-ask-order-rand": "Ñлучайно",
+ "smw_ask_submit": "Ðайти",
+ "smw_ask_editquery": "Редактировать запроÑ",
+ "smw_add_sortcondition": "[Добавить уÑловие Ñортировки]",
+ "smw-ask-sort-add-action": "Добавить уÑловие Ñортировки",
+ "smw_ask_hidequery": "Скрыть Ð·Ð°Ð¿Ñ€Ð¾Ñ (компактный вид)",
+ "smw_ask_help": "Помощь по ÑоÑтавлению запроÑов",
+ "smw_ask_queryhead": "УÑловие",
+ "smw_ask_printhead": "Выбор раÑпечатки",
+ "smw_ask_printdesc": "(добавлÑйте по одному названию ÑвойÑтва на Ñтроку)",
+ "smw_ask_format_as": "Форматировать как:",
+ "smw_ask_defaultformat": "по умолчанию",
+ "smw_ask_otheroptions": "Другие наÑтройки",
+ "smw-ask-otheroptions-info": "Этот раздел Ñодержит параметры, которые изменÑÑŽÑ‚ наÑтройки вывода. ОпиÑÐ°Ð½Ð¸Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð² можно проÑмотреть, Ð½Ð°Ð²ÐµÐ´Ñ Ð½Ð° них курÑор мыши.",
+ "smw-ask-otheroptions-collapsed-info": "ПожалуйÑта, иÑпользуйте значок плюÑа Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра вÑех доÑтупных параметров",
+ "smw_ask_show_embed": "Показать вÑтроенный код",
+ "smw_ask_hide_embed": "Скрыть вÑтроенный код",
+ "smw_ask_embed_instr": "Ð”Ð»Ñ Ð»Ð¸Ð½ÐµÐ¹Ð½Ð¾Ð³Ð¾ вÑÑ‚Ñ€Ð°Ð¸Ð²Ð°Ð½Ð¸Ñ Ñтого запроÑа в вики-Ñтраницу, иÑпользуйте приведённых ниже код.",
+ "smw-ask-delete": "Удалить",
+ "smw-ask-sorting": "Сортировка",
+ "smw-ask-options": "ÐаÑтройки",
+ "smw-ask-options-sort": "ÐаÑтройки Ñортировки",
+ "smw-ask-format-options": "Формат и опции",
+ "smw-ask-parameters": "Параметры",
+ "smw-ask-search": "ПоиÑк",
+ "smw-ask-debug": "Отладка",
+ "smw-ask-debug-desc": "Генерирует информацию об отладке запроÑа",
+ "smw-ask-no-cache": "Отключить кÑш",
+ "smw-ask-no-cache-desc": "Результаты без кеша запроÑа",
+ "smw-ask-result": "Результат",
+ "smw-ask-empty": "ОчиÑтить вÑе запиÑи",
+ "smw-ask-download-link-desc": "Загружать запрошенные результаты в формате $1",
+ "smw-ask-format": "Формат",
+ "smw-ask-format-selection-help": "Справка Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð³Ð¾ формата: $1.",
+ "smw-ask-condition-change-info": "УÑловие было изменено, и поиÑковой ÑиÑтеме требуетÑÑ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¸Ñ‚ÑŒ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð², ÑоответÑтвующих новым требованиÑм.",
+ "smw-ask-input-assistance": "Помощь при вводе",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Помощь по вводу] предоÑтавлÑетÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ печати, Ñортировки и уÑловиÑ. Поле уÑÐ»Ð¾Ð²Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ иметь одну из Ñледующих приÑтавок:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> Ð´Ð»Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñказок о ÑвойÑтвах (к примеру <code>[[p:Имеет ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñказок по категориÑм",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñказок по концепции",
+ "smw-ask-format-change-info": "Формат был изменен и требуетÑÑ Ñовершить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ñнова, Ð´Ð»Ñ ÑоответÑÑ‚Ð²Ð¸Ñ Ð½Ð¾Ð²Ñ‹Ñ… параметров и наÑтроек визуализации.",
+ "smw-ask-format-export-info": "Выбранный формат ÑвлÑетÑÑ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¾Ð¼ ÑкÑпорта, который не имеет визуальной репрезентации, поÑтому результаты доÑтупны только поÑле загрузки.",
+ "smw-ask-query-search-info": "Ðа Ð·Ð°Ð¿Ñ€Ð¾Ñ <code><nowiki>$1</nowiki></code> был получен ответ из {{PLURAL:$3|1=<code>$2</code> (из кеша)|<code>$2</code> (из кеша)|<code>$2</code>}} за $4 {{PLURAL:$4|Ñекунду|Ñекунды|Ñекунд}}.",
+ "searchbyproperty": "ИÑкать по ÑвойÑтву",
+ "processingerrorlist": "СпиÑок ошибок обработки",
+ "propertylabelsimilarity": "Сообщение о похожеÑти метки ÑвойÑтв",
+ "smw-processingerrorlist-intro": "CпиÑок ошибок в работе [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Этот ÑпиÑок рекомендуетÑÑ Ñ€ÐµÐ³ÑƒÐ»Ñрно проÑматривать, иÑправлÑÑ Ð½ÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ñ‹Ðµ ÑемантичеÑкие аннотации.",
+ "smw_sbv_docu": "ИÑкать вÑе Ñтраницы, которые Ñодержат указаннок ÑвойÑтво и значение.",
+ "smw_sbv_novalue": "Укажите значение или проÑмотрите вÑе Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвойÑтва $1.",
+ "smw_sbv_displayresult": "СпиÑок вÑех Ñтраниц, которые Ñодержат ÑвойÑтво $1 Ñо значением $2.",
+ "smw_sbv_displayresultfuzzy": "СпиÑок вÑех Ñтраниц, Ñодержащих ÑвойÑтво «$1» Ñо значением «$2».\nТак как количеÑтво точных результатов невелико, также показаны Ñтраницы, Ñодержащие близкие Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ ÑвойÑтва.",
+ "smw_sbv_property": "СвойÑтво:",
+ "smw_sbv_value": "Значение:",
+ "smw_sbv_submit": "Ðайти",
+ "browse": "Обзор Ñтраниц",
+ "smw_browselink": "ПроÑмотреть ÑвойÑтва",
+ "smw_browse_article": "Введите Ð¸Ð¼Ñ Ñтраницы Ð´Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° обзора.",
+ "smw_browse_go": "Перейти",
+ "smw_browse_show_incoming": "Показать входÑщие ÑвойÑтва",
+ "smw_browse_hide_incoming": "Скрыть ÑвойÑтва, ÑÑылающиеÑÑ Ñюда",
+ "smw_browse_no_outgoing": "Эта Ñтраница не Ñодержит ÑвойÑтв.",
+ "smw_browse_no_incoming": "Ðет ÑвойÑтв, ÑÑылающихÑÑ Ð½Ð° Ñту Ñтраницу.",
+ "smw-browse-from-backend": "Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð¸Ð´Ñ‘Ñ‚ получение информации от Ñерверного приложениÑ.",
+ "smw-browse-intro": "Эта Ñтраница предоÑтавлÑет детали о Ñубъекте или ÑкземплÑре ÑущноÑти, пожалуйÑта введите Ð¸Ð¼Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð°, который будет инÑпектирован.",
+ "smw-browse-invalid-subject": "Ð’Ð°Ð»Ð¸Ð´Ð°Ñ†Ð¸Ñ Ñубъекта вернула ошибку \"$1\" .",
+ "smw-browse-api-subject-serialization-invalid": "Субъект имеет неверный формат Ñериализации.",
+ "smw-browse-js-disabled": "ЕÑÑ‚ÑŒ подозрение, что JavaScript отключен или недоÑтупен, и мы рекомендуем иÑпользовать браузер, в котором он поддерживаетÑÑ. Другие варианты обÑуждаютÑÑ Ð½Ð° Ñтранице параметра конфигурации [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Показать группы",
+ "smw-browse-hide-group": "Скрыть группы",
+ "smw-noscript": "Эта Ñтраница или дейÑтвие требует работы JavaScript. ПожалуйÑта, включите JavaScript в вашем браузере, или иÑпользуйте браузер, в котором он поддерживаетÑÑ, чтобы функциональноÑÑ‚ÑŒ дейÑтвовала и предоÑтавлÑлаÑÑŒ по запроÑу. ЕÑли Вам нужна Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ°, пожалуйÑта, поÑмотрите Ñтраницу Ñправки [https://www.semantic-mediawiki.org/wiki/Help:Noscript «Noscript»]",
+ "smw_inverse_label_default": "$1 из",
+ "smw_inverse_label_property": "Метка обратного ÑвойÑтва",
+ "pageproperty": "Страница поиÑка ÑвойÑтв",
+ "smw_pp_docu": "Введите Ñтраницу и ÑвойÑтво, либо проÑто ÑвойÑтво Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð²Ñех назначенных значений.",
+ "smw_pp_from": "Со Ñтраницы:",
+ "smw_pp_type": "СвойÑтво:",
+ "smw_pp_submit": "ПоиÑк результатов",
+ "smw_result_prev": "ПредыдущаÑ",
+ "smw_result_next": "СледующаÑ",
+ "smw_result_results": "Результаты",
+ "smw_result_noresults": "Извините, но ничего не найдено.",
+ "smwadmin": "ÐдминиÑтративные и техобÑлуживающие функции",
+ "smw-admin-statistics-job-title": "СтатиÑтика заданий",
+ "smw-admin-statistics-job-docu": "СтатиÑтика заданий отображает информацию о запланированных заданиÑÑ… Semantic MediaWiki, которые еще не были выполнены. КоличеÑтво заданий может быть неточным или может Ñодержать неудачные попытки их выполнениÑ. ОбратитеÑÑŒ к [https://www.mediawiki.org/wiki/manual:Job_queue к руководÑтву] за дополнительной информацией.",
+ "smw-admin-statistics-querycache-title": "СтатиÑтика кÑша запроÑов",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] не включён на Ñтой вики, поÑтому ÑтатиÑтика недоÑтупна.",
+ "smw-admin-statistics-querycache-explain": "СтатиÑтика кÑша показывает текущие показатели кÑша нараÑтающим итогом, а также производные метрики:\n* \"misses\" — общее количеÑтво промахов кÑша, потребовавших Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ ÐºÂ ÑемантичеÑкому хранилищу (БД или RDF-хранидище),\n* \"deletes\" — общее чиÑло удалений из кÑша, вызванных обновлением Ñтраницы или обработкой завиÑимоÑти,\n* \"hits\" — количеÑтво удачных получений из кÑша, как от вÑтроенных запроÑов, так и Ñо ÑпецÑтраницы [[СлужебнаÑ:Ask]] или поÑредÑтвом API,\n* \"medianRetrievalResponseTime\" — ориентировочное медианное Ð²Ñ€ÐµÐ¼Ñ (в Ñекундах) Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÐºÑшированных и некÑшированных запроÑов к данным Ð·Ð°Â Ð²Ñ€ÐµÐ¼Ñ Ñбора ÑтатиÑтики,\n* \"noCache\" — количеÑтво запроÑов без попыток Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ ÐºÂ ÐºÑшу (запроÑÑ‹ Ñ <code>limit=0</code>, Ð¾Ð¿Ñ†Ð¸Ñ no-cache и т.п.)",
+ "smw-admin-permission-missing": "ДоÑтуп к Ñтой Ñтранице ограничен. ПожалуйÑта, обратитеÑÑŒ к [https://www.semantic-mediawiki.org/wiki/Help:Permissions Ñправочоной] Ñтранице помощи за подробной информацией.",
+ "smw-admin-setupsuccess": "СиÑтема Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð° уÑтановлена.",
+ "smw_smwadmin_return": "ВернутьÑÑ Ðº $1",
+ "smw_smwadmin_updatestarted": "Запущен новый процеÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ ÑемантичеÑких данных.\nÐ’Ñе Ñохранённые данные будут переÑтроены и воÑÑтановлены, где Ñто необходимо.\nÐ’Ñ‹ можете Ñледить за ходом Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð° Ñтой Ñлужебной Ñтранице.",
+ "smw_smwadmin_updatenotstarted": "Уже запущен один процеÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ.\nДругой не ÑоздаётÑÑ.",
+ "smw_smwadmin_updatestopped": "Ð’Ñе ÑущеÑтвующие процеÑÑÑ‹ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ñтановлены.",
+ "smw_smwadmin_updatenotstopped": "Чтобы оÑтановить запущенный процеÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ, вы должны поÑтавить отметку, подтверждающую, что вы дейÑтвительно уверены в Ñтом решении.",
+ "smw-admin-docu": "Эта ÑÐ»ÑƒÐ¶ÐµÐ±Ð½Ð°Ñ Ñтраница поможет вам в процеÑÑе уÑтановки, обновлениÑ, обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð¸ иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°ÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>.\nÐе забывайте производить резервное копирование важных данных перед выполнением админиÑтративных дейÑтвий.",
+ "smw-admin-environment": "Оболочка программного обеÑпечениÑ",
+ "smw-admin-db": "ÐаÑтройка базы данных",
+ "smw-admin-db-preparation": "Идёт уÑтановка таблицы. Это может занÑÑ‚ÑŒ некоторое времÑ, прежде чем отобразÑÑ‚ÑÑ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ‹ и Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹.",
+ "smw-admin-dbdocu": "Semantic MediaWiki требует Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… раÑширений, позволÑющих хранить ÑемантичеÑкие данные в базе данных MediaWiki.\nÐŸÑ€Ð¸Ð²ÐµÐ´Ñ‘Ð½Ð½Ð°Ñ Ð½Ð¸Ð¶Ðµ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€Ñет корректноÑÑ‚ÑŒ наÑтроек базы данных.\nВыполненные здеÑÑŒ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ влиÑÑŽÑ‚ на оÑновную чаÑÑ‚ÑŒ базы данных MediaWiki и при желании могут быть легко отменены.\nÐ¥Ð¾Ñ‚Ñ Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¼Ð¾Ð¶ÐµÑ‚ быть многократно выполнена без каких-либо поÑледÑтвий, её запуÑк необходим только один раз — во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки или Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Semantic MediaWiki.",
+ "smw-admin-permissionswarn": "Причиной Ñбоев при выполнении SQL-команд может быть отÑутÑтвие необходимых прав у пользователÑ, от имени которого вы подключаетеÑÑŒ к базе данных вики (проверьте файл LocalSettings.php).\nПопробуйте предоÑтавить Ñтому пользователю дополнительные права на Ñоздание и удаление таблиц; временно введите логин «root» в файле LocalSettings.php, или иÑпользуйте обÑлуживающий Ñкрипт <code>setupStore.php</code>, который может иÑпользовать наÑтройки админиÑтратора.",
+ "smw-admin-dbbutton": "Инициализировать или обновить таблицы",
+ "smw-admin-announce": "ОбъÑвление вашей вики",
+ "smw-admin-announce-text": "ЕÑли ваша вики общедоÑтупнаÑ, вы можете зарегиÑтрировать её в <a href=\"https://wikiapiary.com\">WikiApiary</a>.",
+ "smw-admin-deprecation-notice-title": "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾Ð± уÑтаревании",
+ "smw-admin-deprecation-notice-docu": "Следующий раздел Ñодержит уÑтаревшие или удаленные параметры, которые однако вÑе еще активны на данном вики-проекте. ПредполагаетÑÑ, что при каждом обновлении будет удалена поддержка Ñтих конфигураций.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> уÑтарел и будет удалён в $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> уберёт (или заменит) {{PLURAL:$2|Ñледующую опцию|Ñледующие опции}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> уÑтарел и будет удалён в $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> заменён на <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|опциÑ|опции|опций}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> заменён на <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> удалён в $2",
+ "smw-admin-deprecation-notice-title-notice": "ПредÑтоÑщие изменениÑ",
+ "smw-admin-deprecation-notice-title-notice-explanation": "Обнаружено, что в Ñтой вики иÑпользуютÑÑ Ñти наÑтройки, и они планируютÑÑ Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² будущем релизе.",
+ "smw-admin-deprecation-notice-title-replacement": "Заменённые или переименованные наÑтройки",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Указанный раздел Ñодержит наÑтройки, которые были переименованы или изменены другим ÑпоÑобом, поÑтому рекомендуем обновить их Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ формат.",
+ "smw-admin-deprecation-notice-title-removal": "Удалённые наÑтройки",
+ "smw-admin-deprecation-notice-title-removal-explanation": "ПеречиÑленные наÑтройки были удалены в предыдущем выпуÑке, однако они вÑÑ‘ ещё иÑпользуютÑÑ Ð² Ñтой вики.",
+ "smw-smwadmin-refresh-title": "ИÑправление и обновление данных",
+ "smw_smwadmin_datarefresh": "ПереÑтроение данных",
+ "smw_smwadmin_datarefreshdocu": "Можно воÑÑтановить вÑе данные Semantic MediaWiki, оÑнованные на текущем Ñодержимом вики.\nЭто может быть полезно Ð´Ð»Ñ Ð²Ð¾ÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñломанных данных, Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… в Ñлучае, еÑли внутренний формат изменилÑÑ Ð² ÑвÑзи Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ð¼ обновлением программного обеÑпечениÑ.\nОбновление будет выполнÑÑ‚ÑŒÑÑ Ñтраница за Ñтраницей и займёт некоторое времÑ.\nÐиже показано как идёт процеÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ, даётÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑ‚ÑŒ начать или прекратить обновление (еÑли Ñта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð½Ðµ была отключена на админиÑтратором Ñайте).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Обновление уже запущено.</strong>\nЭто нормально, что процеÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð´Ñ‘Ñ‚ доÑтаточно медленно, поÑкольку он обновлÑет данные лишь небольшими порциÑми, когда пользователь обращаетÑÑ Ðº вики.\nЧтобы закончить данное обновление быÑтрее, вы можете вызвать Ñкрипт обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ MediaWiki <code>runJobs.php</code> (иÑпользуйте наÑтройку <code>- maxjobs 1000</code>, чтобы ограничить количеÑтво обновлений в одной партии).\nОриентировочный ход Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ обновлениÑ:",
+ "smw_smwadmin_datarefreshbutton": "ВоÑÑтановление данных по раÑпиÑанию",
+ "smw_smwadmin_datarefreshstop": "ОÑтановить Ñто обновление",
+ "smw_smwadmin_datarefreshstopconfirm": "Да, Ñ ÑƒÐ²ÐµÑ€ÐµÐ½{{GENDER:$1||а}}.",
+ "smw-admin-job-scheduler-note": "БольшинÑтво дейÑтвий в Ñтом разделе выполнÑÑŽÑ‚ÑÑ ÐºÐ°Ðº заданиÑ, чтобы избежать Ñитуаций блокировки во Ð²Ñ€ÐµÐ¼Ñ Ð¸Ñ… выполнениÑ. [https://www.mediawiki.org/wiki/Manual:Job_queue Планировщик заданий] отвечает за обработку, поÑтому очень важно, чтобы Ñценарий обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ <code>runJobs.php</code> (Ñм. также параметр конфигурации <code>$wgRunJobsAsync</code>) имел ÑоответÑтвующую ÑпоÑобноÑÑ‚ÑŒ.",
+ "smw-admin-outdateddisposal-title": "Ð›Ð¸ÐºÐ²Ð¸Ð´Ð°Ñ†Ð¸Ñ ÑƒÑтаревших ÑущноÑтей",
+ "smw-admin-outdateddisposal-intro": "Ðекоторые дейÑÑ‚Ð²Ð¸Ñ (изменение типа ÑвойÑтва, удаление викиÑтраниц или иÑправление ошибочных значений) вызовут поÑвление [https://www.semantic-mediawiki.org/wiki/Outdated_entities уÑтаревших ÑущноÑтей], поÑтому рекомендуетÑÑ Ð¿ÐµÑ€Ð¸Ð¾Ð´Ð¸Ñ‡ÐµÑки убирать их, чтобы оÑвободить проÑтранÑтво в ÑвÑзанной таблице.",
+ "smw-admin-outdateddisposal-active": "Ðазначена работа по ликвидации уÑтаревших объектов.",
+ "smw-admin-outdateddisposal-button": "Запланировать ликвидацию",
+ "smw-admin-feature-disabled": "Эта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð° на Ñтой вики. ПожалуйÑта, поÑмотрите Ñправочную Ñтраницу о <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">наÑтройках</a>, или обратитеÑÑŒ к ÑиÑтемному админиÑтратору.",
+ "smw-admin-propertystatistics-title": "ПереÑтроить ÑтатиÑтику ÑвойÑтва",
+ "smw-admin-propertystatistics-intro": "ПереÑтраивает вÑÑŽ ÑтатиÑтику иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑвойÑтв, поÑле чего обновлÑет и иÑправлÑет [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count Ñчётчик иÑпользованиÑ] ÑвойÑтв.",
+ "smw-admin-propertystatistics-active": "Запланирована работа по переÑтройке ÑтатиÑтики ÑвойÑтва.",
+ "smw-admin-propertystatistics-button": "Запланировать переÑтройку ÑвойÑтва",
+ "smw-admin-fulltext-title": "ПереÑтройка полнотекÑтового поиÑка",
+ "smw-admin-fulltext-intro": "ПереÑтраивает поиÑковый Ð¸Ð½Ð´ÐµÐºÑ Ð¸Ð· таблиц ÑвойÑтв Ñ Ð²ÐºÐ»ÑŽÑ‡Ñ‘Ð½Ð½Ñ‹Ð¼ типом данных [https://www.semantic-mediawiki.org/wiki/Full-text полнотекÑтового поиÑка]. Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² правила индекÑации (изменённые шумовые Ñлова, новый Ñтемер и Ñ‚. п.) и/или заново Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð½Ð°Ñ Ð¸Ð»Ð¸ Ð¸Ð·Ð¼ÐµÐ½Ñ‘Ð½Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° требуют повторного запуÑка Ñтого заданиÑ.",
+ "smw-admin-fulltext-active": "Была запланирована работа по перепоÑтроению полнотекÑтового поиÑка.",
+ "smw-admin-fulltext-button": "Запланировать полнотекÑтовую переÑтройку",
+ "smw-admin-support": "Получение поддержки",
+ "smw-admin-supportdocu": "Различные реÑурÑÑ‹, которые помогут вам в Ñлучае проблем:",
+ "smw-admin-installfile": "ЕÑли вы иÑпытываете Ð·Ð°Ñ‚Ñ€ÑƒÐ´Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ уÑтановке, начните Ñ Ð¸Ð·ÑƒÑ‡ÐµÐ½Ð¸Ñ Ñ€ÑƒÐºÐ¾Ð²Ð¾Ð´Ñтва в <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">файле INSTALL</a> и на <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">Ñтранице уÑтановки</a>.",
+ "smw-admin-smwhomepage": "ÐŸÐ¾Ð»Ð½Ð°Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Semantic MediaWiki на <b><a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_-_%D0%B7%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Об ошибках можно Ñообщать на <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>, а на <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">ÑоответÑтвующей Ñтранице Ñправочного раздела</a> приведены некоторые рекомендации о том, как напиÑать Ñффективное Ñообщение о проблеме.",
+ "smw-admin-questions": "ЕÑли у Ð²Ð°Ñ ÐµÑÑ‚ÑŒ дополнительные вопроÑÑ‹ или предложениÑ, приÑоединÑйтеÑÑŒ к обÑуждению в <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">ÑпиÑках раÑÑылки пользователей Semantic MediaWiki</a> или в <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">чате</a>.",
+ "smw-admin-other-functions": "Другие функции",
+ "smw-admin-supplementary-section-title": "Дополнительные функции",
+ "smw-admin-supplementary-section-subtitle": "Функции Ñдра",
+ "smw-admin-supplementary-section-intro": "Эта ÑÐµÐºÑ†Ð¸Ñ Ñодержит дополнительные функции кроме цели обÑлуживаниÑ, и возможно, что некоторые функции, которые перечиÑлены в [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions документации], ограничены или недоÑтупны, и поÑтому Ñто Ð½ÐµÐ»ÑŒÐ·Ñ Ñделать в Ñтой вики.",
+ "smw-admin-supplementary-settings-title": "Параметры конфигурации",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> выводит общий ÑпиÑок доÑтупных наÑтроек Semantic MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "ÐžÐ¿ÐµÑ€Ð°Ñ‚Ð¸Ð²Ð½Ð°Ñ ÑтатиÑтика",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> отображает раÑширенную информацию.",
+ "smw-admin-supplementary-idlookup-title": "ПоиÑк и удаление ÑущноÑти",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> Ñодержит функции Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка и раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ñ… ÑущноÑтей",
+ "smw-admin-supplementary-duplookup-title": "Дублирующие объекты",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÑ‡Ð¸ÑÐ»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñей, которые клаÑÑифицируютÑÑ ÐºÐ°Ðº Ñодержащие дубликаты в таблице ÑущноÑтей",
+ "smw-admin-supplementary-duplookup-docu": "Ðа Ñтой Ñтранице перечиÑлены [https://www.semantic-mediawiki.org/wiki/Help:Entity_table ÑемантичеÑкие запиÑи], отмеченные как дубликаты. Дубликаты изредка (еÑли вообще) поÑвлÑÑŽÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ из-за прерванного Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð±Ð°Ð·Ñ‹ данных или неудавшегоÑÑ Ð¾Ñ‚ÐºÐ°Ñ‚Ð° транзакции",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "СтатиÑтика кÑша",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> показывает ÑтатиÑтику каÑательно кÑша",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> информирует о наÑтройках и ÑтатиÑтике кÑша",
+ "smw-admin-supplementary-elastic-docu": "Ðа Ñтой Ñтранице находÑÑ‚ÑÑ Ð´Ð°Ð½Ð½Ñ‹Ðµ о наÑтройках, отображениÑÑ…, ÑоÑтоÑнии и ÑтатиÑтике индекÑов, отноÑÑщаÑÑÑ ÐºÂ ÐºÐ»Ð°Ñтеру ''Elasticsearch'', подключённому к ''Semantic MediaWiki'' и его хранилищу [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Поддерживаемые функции",
+ "smw-admin-supplementary-elastic-settings-title": "ÐаÑтройки",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> иÑпользуетÑÑ ''Elasticsearch'' длÑÂ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑами ''Semantic MediaWiki''",
+ "smw-admin-supplementary-elastic-mappings-title": "СоответÑтвиÑ",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> — ÑпиÑок отображений индекÑов и полей",
+ "smw-admin-supplementary-elastic-mappings-docu": "Ðа Ñтой Ñтранице находÑÑ‚ÑÑ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñти Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹, иÑпользуемых Ñ текущими индекÑами. Отчёт об отображении Ñледует анализировать, ÑƒÑ‡Ð¸Ñ‚Ñ‹Ð²Ð°Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸ÐµÂ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ <code>index.mapping.total_fields.limit</code>, хранÑщей макÑимальное чиÑло полей в индекÑе.",
+ "smw-admin-supplementary-elastic-mappings-summary": "ОпиÑание",
+ "smw-admin-supplementary-elastic-mappings-fields": "ÐžÑ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹",
+ "smw-admin-supplementary-elastic-nodes-title": "Узлы",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> показывает ÑтатиÑтику узлов",
+ "smw-admin-supplementary-elastic-indices-title": "ИндекÑÑ‹",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> предоÑтавлÑет обзор доÑтупных индекÑов и их ÑтатиÑтику",
+ "smw-admin-supplementary-elastic-statistics-title": "СтатиÑтика",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> показывает ÑтатиÑтику ÑƒÑ€Ð¾Ð²Ð½Ñ Ð¸Ð½Ð´ÐµÐºÑа",
+ "smw-admin-supplementary-elastic-statistics-docu": "Эта Ñтраница дает предÑтавление о ÑтатиÑтике индекÑов Ð´Ð»Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡Ð½Ñ‹Ñ… операций, выполнÑемых на уровне индекÑа, Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð½Ð°Ñ ÑтатиÑтика агрегируетÑÑ Ñ Ð¿ÐµÑ€Ð²Ð¸Ñ‡Ð½Ñ‹Ð¼Ð¸ и Ñуммарными агрегациÑми. [Https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html Ñтраница Ñправки] Ñодержит подробное опиÑание ÑтатиÑтики доÑтупных индекÑов.",
+ "smw-admin-supplementary-elastic-status-replication": "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ€ÐµÐ¿Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "ПоÑледнÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð°Ñ Ñ€ÐµÐ¿Ð»Ð¸ÐºÐ°Ñ†Ð¸Ñ: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Интервал обновлениÑ: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Задержка воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚: $1 (оценка)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Журнал невыполненных работ (файл): $1 (оценка)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Ð ÐµÐ¿Ð»Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð°: $1 (перепоÑтроение в процеÑÑе)",
+ "smw-list-count": "СпиÑок Ñодержит $1 {{PLURAL:$1|запиÑÑŒ|запиÑи|запиÑей}}.",
+ "smw-list-count-from-cache": "Ð’ ÑпиÑке, полученном из кÑша в (UTC: $2), $1 {{PLURAL:$1|значение|значениÑ|значений}}.",
+ "smw-property-label-uniqueness": "Метка \"$1\" была ÑопоÑтавлена по крайней мере Ñ Ð¾Ð´Ð½Ð¸Ð¼ другим предÑтавлением ÑвойÑтв. ПожалуйÑта, проконÑультируйтеÑÑŒ Ñо [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness Ñтраницей помощи] о том, как уÑтранить Ñту проблему.",
+ "smw-property-label-similarity-title": "Сообщение о подобноÑти названий ÑвойÑтв",
+ "smw-property-label-similarity-intro": "<u>$1</u> вычиÑлÑет подобие ÑущеÑтвующих названий ÑвойÑтв",
+ "smw-property-label-similarity-threshold": "Порог:",
+ "smw-property-label-similarity-type": "Показать ID типа",
+ "smw-property-label-similarity-noresult": "Ð”Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… опций не найдено никаких результатов.",
+ "smw-property-label-similarity-docu": "Сравнивает и Ñообщает о [https://www.semantic-mediawiki.org/wiki/Property_similarity ÑинтакÑичеÑком подобии] (не ÑемантичеÑком подобии) между Ð´Ð²ÑƒÐ¼Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñми ÑвойÑтв, что может помочь отфильтровать ÑвойÑтва, имеющие ошибки в названии или ÑвлÑющиеÑÑ Ñквивалентными и репрезентуют тот же концепт (Ñм. Ñлужебную Ñтраницу [[Special:Properties|СвойÑтва]], чтобы проÑÑнить концепт и иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÑвойÑтв в отчёте). Порог можно наÑтроить, чтобы или раÑширить, или Ñузить уровень подобиÑ, который Ñледует принимать во внимание. <code>[[Property:$1|$1]]</code> иÑпользуетÑÑ Ð´Ð»Ñ Ð¸ÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÑвойÑтв из анализа.",
+ "smw-admin-operational-statistics": "Эта Ñтраница Ñодержит операционную ÑтатиÑтику, Ñобранную в или из функций, ÑвÑзанных из Semantic MediaWiki. РаÑширенный ÑпиÑок ÑтатиÑтичеÑких данных, ÑпецифичеÑких Ð´Ð»Ñ Ð²Ð¸ÐºÐ¸, можно поÑмотреть [[Special:Statistics|<b>здеÑÑŒ</b>]].",
+ "smw_adminlinks_datastructure": "Структура данных",
+ "smw_adminlinks_displayingdata": "Отображение данных",
+ "smw_adminlinks_inlinequerieshelp": "Справка по вÑтроенным запроÑам",
+ "smw-page-indicator-usage-count": "Примерное [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count \nколичеÑтво иÑпользований]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "СвойÑтво, определённое {{PLURAL:$1|учаÑтником|ÑиÑтемой}}",
+ "smw-property-indicator-last-count-update": "Примерное чиÑло иÑпользований\nПоÑледний раз обновлено: $1",
+ "smw-concept-indicator-cache-update": "Счётчик кÑша\nОбновлён в $1",
+ "smw-createproperty-isproperty": "Это ÑвойÑтво типа $1.",
+ "smw-createproperty-allowedvals": "Ð”Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ ÑвойÑтва {{PLURAL:$1|1=допуÑкаетÑÑ Ñледующее значение|допуÑкаютÑÑ Ñледующие значениÑ}}:",
+ "smw-paramdesc-category-delim": "Разделитель",
+ "smw-paramdesc-category-template": "Шаблон, Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ которого форматируютÑÑ Ñлементы",
+ "smw-paramdesc-category-userparam": "Параметр Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ шаблону",
+ "smw-info-par-message": "Сообщение Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ.",
+ "smw-info-par-icon": "Значок Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ («информациÑ» или «предупреждение»).",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Общие параметры",
+ "prefs-ask-options": "Параметры ÑемантичеÑкого поиÑка",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Semantic MediaWiki] (и ÑвÑзанные Ñ Ð½ÐµÐ¹ раÑширениÑ) предоÑтавлÑÑŽÑ‚ возможноÑти индивидуальной наÑтройки Ð´Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… функций. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации, пожалуйÑта, поÑмотрите в [https://www.semantic-mediawiki.org/wiki/Help:User_preferences раздел Ñправки].",
+ "smw-prefs-ask-options-tooltip-display": "Отображать параметр «текÑт» в виде вÑплывающей подÑказки",
+ "smw-prefs-ask-options-compact-view-basic": "Включить проÑтой Ñжатый формат",
+ "smw-prefs-help-ask-options-compact-view-basic": "ЕÑли включено, показывает Ñокращённый набор ÑÑылок на Special:Ask в Ñжатом формате",
+ "smw-prefs-general-options-time-correction": "Включить корректировку времени Ð´Ð»Ñ Ñлужебных Ñтраниц при помощи локальной наÑтройки [[Special:Preferences#mw-prefsection-rendering|временного ÑмещениÑ]]",
+ "smw-prefs-general-options-jobqueue-watchlist": "Показывать ÑпиÑок Ð½Ð°Ð±Ð»ÑŽÐ´ÐµÐ½Ð¸Ñ Ð·Ð°Â Ð¾Ñ‡ÐµÑ€ÐµÐ´ÑŒÑŽ задач на личной панели",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Включите, чтобы показывать [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist ÑпиÑок] ожидающих избранных задач, вмеÑте Ñ приблизительными размерами их очередей.",
+ "smw-prefs-general-options-disable-editpage-info": "Отключить вÑтупительный текÑÑ‚ на Ñтранице редактированиÑ",
+ "smw-prefs-general-options-disable-search-info": "Отключить информацию о поддержке ÑинтакÑиÑа на Ñтандартной Ñтранице поиÑка",
+ "smw-prefs-general-options-suggester-textinput": "Включить аÑÑиÑтента ввода Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ð¹ ÑемантичеÑких объектов",
+ "smw-prefs-help-general-options-suggester-textinput": "Включите, чтобы получать [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance автодополнение] названий ÑвойÑтв, концепций и категорий при вводе",
+ "smw-ui-tooltip-title-property": "СвойÑтво",
+ "smw-ui-tooltip-title-quantity": "Преобразование единиц",
+ "smw-ui-tooltip-title-info": "ИнформациÑ",
+ "smw-ui-tooltip-title-service": "Ð¡Ð»ÑƒÐ¶ÐµÐ±Ð½Ð°Ñ ÑÑылка",
+ "smw-ui-tooltip-title-warning": "Предупреждение",
+ "smw-ui-tooltip-title-error": "Ошибка",
+ "smw-ui-tooltip-title-parameter": "Параметр",
+ "smw-ui-tooltip-title-event": "Событие",
+ "smw-ui-tooltip-title-note": "Примечание",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw-ui-tooltip-title-reference": "Примечание",
+ "smw_unknowntype": "Ð”Ð»Ñ Ñтого ÑвойÑтва задан неизвеÑтный тип «$1»",
+ "smw-concept-cache-text": "ÐšÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ð¸Ñ Ð¸Ð¼ÐµÐµÑ‚ в общей ÑложноÑти $1 {{PLURAL:$1|Ñтраницу|Ñтраницы|Ñтраниц}} и поÑледний раз обновлÑлÑÑ $2 в $3.",
+ "smw_concept_header": "Страницы, иÑпользующие предÑтавление «$1»",
+ "smw_conceptarticlecount": "Ðиже {{PLURAL:$1|показана|показаны}} $1 {{PLURAL:$1|Ñтраница|Ñтраницы|Ñтраниц}}.",
+ "smw-qp-empty-data": "Запрошенные данные не удаётÑÑ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð·Ð¸Ñ‚ÑŒ из-за недоÑтаточноÑти некоторых критериев отбора.",
+ "right-smw-admin": "ДоÑтуп к админиÑтрированию (Semantic MediaWiki)",
+ "right-smw-patternedit": "ДоÑтуп к редактированию разрешенных Ð´Ð»Ñ Ð¾Ð±ÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ³ÑƒÐ»Ñрных выражений и шаблонов (Semantic MediaWiki)",
+ "right-smw-pageedit": "Редактирование Ñтраниц Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸ÐµÐ¹ <code>Is edit protected</code> (Semantic MediaWiki)",
+ "right-smw-ruleedit": "Редактировать Ñтраницы правил (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "защищено (только подходÑщие пользователи)",
+ "action-smw-patternedit": "Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ³ÑƒÐ»Ñрных выражений, иÑпользуемых Semantic MediaWiki",
+ "action-smw-pageedit": "редактирование Ñтраниц Ñ аннотацией <code>Is edit protected</code> (''Semantic MediaWiki'')",
+ "group-smwadministrator": "ÐдминиÑтраторы (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|админиÑтратор (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:ÐдминиÑтраторы (Semantic MediaWiki)",
+ "group-smwcurator": "Кураторы (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|куратор (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Кураторы (Semantic MediaWiki)",
+ "action-smw-admin": "доÑтуп к админиÑтративным функциÑм Semantic MediaWiki",
+ "action-smw-ruleedit": "редактировать Ñтраницы правил (Semantic MediaWiki)",
+ "smw-property-predefined-default": "«$1» — Ñто предопределённое ÑвойÑтво.",
+ "smw-property-predefined-common": "Это предварительно развернутое ÑвойÑтво (также извеÑтное как [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Ñпециальное ÑвойÑтво]) и неÑет дополнительные админиÑтративные привилегии, но может быть иÑпользовано, как любое другое [https://www.semantic-mediawiki.org/wiki/Property определенное пользователем ÑвойÑтво].",
+ "smw-property-predefined-ask": "«$1» — Ñто предопределённое ÑвойÑтво, которое предÑтавлÑет мета-данные (в форме [https://www.semantic-mediawiki.org/wiki/Subobject подобъекта]) об индивидуальных запроÑах и предоÑтавлÑетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "«$1» — Ñто предопределённое ÑвойÑтво, которое Ñобирает количеÑтво уÑловий, иÑпользуемых в запроÑе и предоÑтавлÑетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "«$1» — предварительно определенное ÑвойÑтво, которое информирует о глубине запроÑа и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askde": "ЧиÑленное значение, полученное на оÑновании вложенноÑти подзапроÑов, цепочек ÑвойÑтв и доÑтупных Ñлементов опиÑÐ°Ð½Ð¸Ñ Ð¸Â Ð¸Ñпользуемой длÑÂ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов поÑредÑтвом наÑтройки <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>",
+ "smw-property-predefined-askpa": "$1 - Ñто предопределенное ÑвойÑтво, опиÑывающее параметры влиÑющие на результат запроÑа. ПредоÑтавлÑетÑÑ Ð¾Ñ‚ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-askpa": "Это чаÑÑ‚ÑŒ набора ÑвойÑтв, который определÑет [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler query profile].",
+ "smw-sp-properties-docu": "Ðа Ñтой Ñтранице перечиÑлены доÑтупные [https://www.semantic-mediawiki.org/wiki/Property ÑвойÑтва] и количеÑтво их иÑпользований в Ñтой вики. Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð¹ ÑтатиÑтики рекомендуетÑÑ, чтоб ÑервиÑный Ñкрипт [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics ÑтатиÑтики ÑвойÑÑ‚] запуÑкалÑÑ Ð½Ð° регулÑрной оÑнове. Ð”Ð»Ñ Ð´Ð¸Ñ„Ñ„ÐµÑ€ÐµÐ½Ñ†Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ предÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñм. Ñлужебные Ñтраницы Ñо ÑпиÑком [[Special:UnusedProperties|неиÑпользованных]] или [[Special:WantedProperties|требуемых ÑвойÑтв]].",
+ "smw-sp-properties-cache-info": "ПеречиÑленные данные были получены из [https://www.semantic-mediawiki.org/wiki/Caching кÑша] и были поÑледний раз обновлены $1.",
+ "smw-sp-properties-header-label": "СпиÑок ÑвойÑтв",
+ "smw-admin-settings-docu": "Отображает ÑпиÑок вÑех наÑтроек по-умолчанию и локализованных наÑтроек, которые имеют отношение к окружению Semantic MediaWiki. За ÑведениÑми об отдельных параметров обратитеÑÑŒ к Ñтранице помощи по [https://www.semantic-mediawiki.org/wiki/Help:Configuration конфигурации].",
+ "smw-sp-admin-settings-button": "Создать ÑпиÑок наÑтроек",
+ "smw-admin-idlookup-title": "ПоиÑк",
+ "smw-admin-idlookup-docu": "Ð’ Ñтом разделе предÑтавлены техничеÑкие ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± отдельном объекте (вики-Ñтраница, подобъект, ÑвойÑтво и Ñ‚.д.) в СемантичеÑкой МедиаВики. Ввод может предÑтавлÑÑ‚ÑŒ Ñобой чиÑловой идентификатор или Ñтроковое значение Ð´Ð»Ñ ÑÐ¾Ð²Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ Ñ ÑоответÑтвующим полем Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, при Ñтом Ð»ÑŽÐ±Ð°Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÑÑылка отноÑитÑÑ Ðº СемантичеÑкой МедиаВики, а не к Ñтранице MediaWiki или номеру верÑии.",
+ "smw-admin-iddispose-title": "Удаление",
+ "smw-admin-iddispose-docu": "Обратите внимание, что Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ ÑƒÑ‚Ð¸Ð»Ð¸Ð·Ð°Ñ†Ð¸Ð¸ ÑвлÑетÑÑ Ð½ÐµÐ¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð½Ð¾Ð¹ и удалит внутренний идентификатор объекта от движка Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ подтверждении. ПожалуйÑта, выполнÑйте Ñту задачу Ñ '''оÑторожноÑтью''' и только поÑле того, как [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal documentation] были проведены конÑультации.",
+ "smw-admin-iddispose-done": "Идентификатор «$1» был удалён из хранилища.",
+ "smw-admin-iddispose-references": "ID «$1» {{PLURAL:$2|не имеет активных ÑÑылок|Ñ Ð¿Ð¾ крайней мере одной активной ÑÑылкой}}.",
+ "smw-admin-iddispose-references-multiple": "СпиÑок Ñовпадений Ñ Ð¿Ð¾ крайней мере одной активной ÑÑылкой.",
+ "smw-admin-iddispose-no-references": "ПоиÑк не Ñмог ÑопоÑтавить «$1» Ñ Ñлементом таблицы.",
+ "smw-admin-idlookup-input": "ПоиÑк:",
+ "smw-admin-objectid": "Идентификатор:",
+ "smw-admin-tab-general": "Обзор",
+ "smw-admin-tab-notices": "Ð—Ð°Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ð¿Ð¾ запрету",
+ "smw-admin-tab-maintenance": "ТехничеÑкое обÑлуживание",
+ "smw-admin-tab-supplement": "Дополнительные функции",
+ "smw-admin-tab-registry": "РееÑÑ‚Ñ€",
+ "smw-admin-maintenance-no-description": "Ðет опиÑаниÑ.",
+ "smw-livepreview-loading": "Загрузка…",
+ "smw-sp-searchbyproperty-description": "Ðа Ñтой Ñтранице предоÑтавлен проÑтой [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¿Ñ€Ð¾Ñмотра] Ð´Ð»Ñ Ð½Ð°Ñ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ ÑущноÑтей по опиÑанным ÑвойÑтвам и именованным значениÑм. Другие доÑтупные интерфейÑÑ‹ поиÑка включают в ÑÐµÐ±Ñ [[Special:PageProperty|Ñтраницу поиÑк ÑвойÑтв]] и [[Special:Ask|поÑтроитель поиÑковых запроÑов]].",
+ "smw-sp-searchbyproperty-resultlist-header": "СпиÑок результатов",
+ "smw-sp-searchbyproperty-nonvaluequery": "СпиÑок значений, которые имеют назначенное ÑвойÑтво «$1».",
+ "smw-sp-searchbyproperty-valuequery": "СпиÑок Ñтраниц, которые Ñодержат ÑвойÑтво «$1» Ñ Ð¾Ð±ÑŠÑвленным значением «$2».",
+ "smw-datavalue-number-textnotallowed": "«$1» не может быть приÑвоен заÑвленному типу чиÑло Ñо значением $2.",
+ "smw-datavalue-number-nullnotallowed": "«$1» вернулÑÑ Ñ Ñо значением «NULL», что не разрешено Ð´Ð»Ñ Ñ‡Ð¸Ñел.",
+ "smw-editpage-annotation-enabled": "Эта Ñтраница поддерживает ÑемантичеÑкие аннотации в текÑте (например <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) Ð´Ð»Ñ Ð¿Ð¾ÑÑ‚Ñ€Ð¾ÐµÐ½Ð¸Ñ Ñтруктурированного контента, в который можно делать запроÑÑ‹, обеÑпечиваетÑÑ Semantic MediaWiki. Ð”Ð»Ñ ÐºÐ¾Ð¼Ð¿Ð»ÐµÐºÑного опиÑаниÑ, как иÑпользовать аннотации или парÑерную функцию ask, пожалуйÑта, поÑетите Ñправочные Ñтраницы о [https://www.semantic-mediawiki.org/wiki/Help:Getting_started начале работы], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation in-text annotation аннотации в текÑте] и [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries Ñтрочных запроÑах].",
+ "smw-editpage-annotation-disabled": "Ðа Ñтой Ñтранице невозможны ÑемантичеÑкие аннотации в текÑте из-за ограничений проÑтранÑтва имён. Детали отноÑительно того, как разрешить проÑтранÑтво имен, находÑÑ‚ÑÑ Ð² Ñправочной Ñтранице [https://www.semantic-mediawiki.org/wiki/Help:Configuration конфигурации].",
+ "smw-editpage-property-annotation-enabled": "Это ÑвойÑтво можно раÑширить Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÑемантичеÑких аннотаций Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñ‚Ð¸Ð¿Ð° данных (например <nowiki>\"[[Has type::Page]]\"</nowiki>) или других поддерживаемых утверждений (e.g. <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). ОпиÑание, как раÑширить Ñту Ñтраницу Ñмотрите на Ñтранице Ñправки о [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration \nзаÑвлении ÑвойÑтва] или [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes ÑпиÑок доÑтупных типов данных].",
+ "smw-editpage-property-annotation-disabled": "Это ÑвойÑтво Ð½ÐµÐ»ÑŒÐ·Ñ Ñ€Ð°Ñширить аннотацией типа данных (например <nowiki>\"[[Has type::Page]]\"</nowiki>), поÑкольку она уже предварительно определена (Ñм. более подробную информацию на Ñправочной Ñтранице о [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Ñпециальные ÑвойÑтва]).",
+ "smw-editpage-concept-annotation-enabled": "Этот концепт можно раÑширить Ñ Ð¸Ñпользованием парÑерной функции #concept. ОпиÑание, как иÑпользовать #concept, Ñм. на Ñправочной Ñтранице о [https://www.semantic-mediawiki.org/wiki/Help:Concepts концептах].",
+ "smw-search-syntax-support": "Поле поиÑка поддерживает [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search ÑинтакÑÐ¸Ñ ÑемантичеÑких запроÑов] ''MediaWiki''",
+ "smw-search-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Помощник по вводу] включён, чтобы упроÑтить выбор вÑех возможных опций и категорий.",
+ "smw-search-help-intro": "Ввод <code><nowiki>[[ ... ]]</nowiki></code> укажет процеÑÑору иÑпользовать поиÑковый механизм ''Semantic MediaWiki''. Обратите внимание, что Ñочетание <code><nowiki>[[ ... ]]</nowiki></code> Ñ проÑтым текÑтовым поиÑком, наподобие <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code>, не поддерживаетÑÑ.",
+ "smw-search-help-structured": "Структурированный поиÑк:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> — [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context контекÑÑ‚ отбора]\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> — Ñ [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context контекÑтом запроÑа]",
+ "smw-search-help-ask": "По Ñтим ÑÑылкам находитÑÑ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Â Ñ‚Ð¾Ð¼, как иÑпользовать функцию парÑера <code>#ask</code>:\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Выбор Ñтраниц] — как выбирать Ñтраницы и ÑоÑтавлÑÑ‚ÑŒ уÑÐ»Ð¾Ð²Ð¸Ñ Ð¾Ñ‚Ð±Ð¾Ñ€Ð°\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators ПоиÑковые операторы] — поддерживаемые поиÑковые операторы, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð¾Ñ‚Ð±Ð¾Ñ€ по диапазонам и метаÑимволам",
+ "smw-search-input": "Ввод и поиÑк",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Ðвтодополение] в поле ввода требует одиниз Ñледующих префикÑов:\n\n*<code>p:</code> длÑ ÑвойÑтв (например, <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> длÑ категорий\n\n*<code>con:</code> длÑ концепций",
+ "smw-search-syntax": "СинтакÑиÑ",
+ "smw-search-profile": "Дополнительно",
+ "smw-search-profile-tooltip": "Функции поиÑка, отноÑÑщиеÑÑ ÐºÂ Semantic MediaWiki",
+ "smw-search-profile-sort-best": "Лучшее Ñовпадение",
+ "smw-search-profile-sort-recent": "Самые недавние",
+ "smw-search-profile-sort-title": "Ðазвание",
+ "smw-search-profile-extended-help-intro": "Специальный: ПоиÑк [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile раÑширенный профиль] предоÑтавлÑет доÑтуп к функциÑм поиÑка, Ñпецифичным Ð´Ð»Ñ Semantic MediaWiki и его поддерживаемой Ñерверной чаÑти запроÑа.",
+ "smw-search-profile-extended-help-sort": "ПорÑдок Ñортировки результатов запроÑа:",
+ "smw-search-profile-extended-help-sort-title": "*«Ðазвание» Ñ Ð¸Ñпользованием заголовка Ñтраницы (или отображаемого заголовка) в качеÑтве ÐºÑ€Ð¸Ñ‚ÐµÑ€Ð¸Ñ Ñортировки",
+ "smw-search-profile-extended-help-sort-recent": "* \"СовÑем недавние\" длÑ показа Ñтраниц в порÑдке поÑледнего Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ (от недавних к давним), при Ñтом подобъекты не будут выведены, поÑкольку длÑ них не определено ÑвойÑтво [[Property:Modification date|Дата изменениÑ]]",
+ "smw-search-profile-extended-help-search-syntax": "Поле поиÑка поддерживает ÑинÑтакÑÐ¸Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ парÑера <code>#ask</code> длÑ ÑемантичеÑкого поиÑка. Полезные Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚:",
+ "smw-search-profile-extended-help-query-link": "(О подробноÑÑ‚ÑÑ… Ñм. $1).",
+ "smw-search-profile-extended-help-find-forms": "ДоÑтупные формы",
+ "smw-search-profile-extended-section-sort": "Сортировать по",
+ "smw-search-profile-extended-section-form": "Формы",
+ "smw-search-profile-extended-section-search-syntax": "ПоиÑк входа",
+ "smw-search-profile-extended-section-namespace": "ПроÑтранÑтво имён",
+ "smw-search-profile-extended-section-query": "ЗапроÑ",
+ "smw-search-profile-link-caption-query": "Ñм.",
+ "smw-search-show": "Показать",
+ "smw-search-hide": "Скрыть",
+ "log-name-smw": "Журнал Semantic MediaWiki",
+ "log-show-hide-smw": "$1 журнал Semantic MediaWiki",
+ "logeventslist-smw-log": "Журнал Semantic MediaWiki",
+ "log-description-smw": "ДеÑтельноÑÑ‚ÑŒ отноÑительно [https://www.semantic-mediawiki.org/wiki/Help:Logging включенных типов Ñобытий], которую региÑтрирует Semantic MediaWiki и ее компоненты.",
+ "smw-datavalue-import-missing-namespace-uri": "Ðе удалоÑÑŒ найти URI проÑтранÑтва имен «$1» в [[MediaWiki:Smw import $1|импорте $1]].",
+ "smw-datavalue-import-missing-type": "Ðе было найдено определение типа Ð´Ð»Ñ Â«$1» в [[MediaWiki:Smw import $2|импорте $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Импорт $1]]",
+ "smw-datavalue-import-invalid-value": "«$1» не ÑвлÑетÑÑ Ð´ÐµÐ¹Ñтвительным форматом и должен ÑоÑтоÑÑ‚ÑŒ из «проÑтранÑтво имен»:«идентификатор» (например «foaf:name»).",
+ "smw-property-predefined-impo": "\"$1\" — Ñто предопределённое ÑвойÑтво, которое опиÑывает отношение к [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary заимÑтвованной лекÑике] и предоÑтавлÑетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "«$1» — предварительно определенное ÑвойÑтво, которое опиÑывает [[Special:Types|тип данных]] ÑвойÑтва, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "«$1» — предопределённое ÑвойÑтво, которое предÑтавлÑет поÑтроение [https://www.semantic-mediawiki.org/wiki/Help:Container контейнера], и обеÑпечиваемое [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "Это хранилище позволÑет ÑохранÑÑ‚ÑŒ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвойÑтв, как на обычной вики-Ñтранице, но в оÑобом проÑтранÑтве, привÑзанном к Ñодержащей его Ñтранице",
+ "smw-property-predefined-errp": "«$1» — Ñто предварительно определённое ÑвойÑтво Ð´Ð»Ñ Ð¾Ñ‚ÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ²ÐµÑ€Ð½Ñ‹Ñ… значений в аннотациÑÑ… и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-errp": "Обычно вызываетÑÑ Ð½ÐµÑоответÑтвием типа данных или неÑоблюдением Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ [[Property:Allows value|допуÑтимых значений]].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value «$1»] — \nпредварительно определенное ÑвойÑтво, которое может определÑÑ‚ÑŒ ÑпиÑок разрешенных значений, чтобы ограничивать назначение значений Ð´Ð»Ñ ÑвойÑтва, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list «$1»] — предопределённое ÑвойÑтво, длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑÑылки на ÑпиÑок допуÑтимых значений ÑвойÑтва. ПредоÑтавлÑетÑÑ ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]''.",
+ "smw-datavalue-property-restricted-annotation-use": "СвойÑтво «$1» имеет оÑобое назначение, и его значение не может уÑтанавливатьÑÑ Ð² ÑемантичеÑкой аннотации.",
+ "smw-datavalue-property-restricted-declarative-use": "СвойÑтво «$1» — декларативное, и должно иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ на Ñтраницах ÑвойÑтв или категорий.",
+ "smw-datavalue-property-create-restriction": "СвойÑтво «$1» ещё не ÑущеÑтвует, Ð°Â ÑƒÂ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð½ÐµÑ‚ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Â«$2» (Ñм. [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode Режим Ñ€Ð°Ð·Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ñтупа]), чтобы Ñоздавать новые ÑвойÑтва.",
+ "smw-datavalue-property-invalid-character": "«$1» Ñодержит запрещённый Ñимвол «$2» и, Ñледовательно, отмечено, как недопуÑтимое.",
+ "smw-datavalue-property-invalid-chain": "ИÑпользование цепочки ÑвойÑтв «$1» недопуÑтимо в ÑемантичеÑкой аннотации.",
+ "smw-datavalue-restricted-use": "Значение данных «$1» было отмечено Ð´Ð»Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð½Ð¾Ð³Ð¾ иÑпользованиÑ.",
+ "smw-datavalue-invalid-number": "«$1» не может быть интерпретировано как чиÑло.",
+ "smw-query-condition-circular": "Возможное цикличеÑкое уÑловие было обнаружено в «$1».",
+ "smw-query-condition-empty": "В запроÑе еÑÑ‚ÑŒ пуÑтое уÑловие.",
+ "smw-types-list": "СпиÑок типов данных",
+ "smw-types-default": "«$1» ÑвлÑетÑÑ Ð²Ñтроенным типом данных.",
+ "smw-types-help": "Дальнейшую информацию и примеры можно найти на [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 Ñтранице Ñправки].",
+ "smw-type-anu": "«$1» — Ñто вариант типа данных [[Special:Types/URL|URL]] и в оÑновном иÑпользуетÑÑ Ð´Ð»Ñ ÑкÑпортной декларации \"owl:AnnotationProperty\".",
+ "smw-type-boo": "«$1» — Ñто примитивный тип данных Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¸Ñтина/ложь.",
+ "smw-type-cod": "«$1» — Ñто вариант типа данных [[Special:Types/Text|ТекÑÑ‚]] Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² техничеÑких текÑтах произвольной длины, таких как ÑпиÑки иÑходного кода.",
+ "smw-type-geo": "«$1» — Ñто тип данных, который опиÑывает географичеÑкое положение и требует уÑтановить [https://www.semantic-mediawiki.org/wiki/Extension:Maps раÑширение «Maps»].",
+ "smw-type-tel": "«$1» — Ñто Ñпециальный тип данных Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð¼ÐµÐ¶Ð´ÑƒÐ½Ð°Ñ€Ð¾Ð´Ð½Ñ‹Ñ… телефонных номеров ÑоглаÑно RFC 3966.",
+ "smw-type-txt": "«$1» — Ñто примитивный тип данных Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ñтрок произвольной длины.",
+ "smw-type-dat": "«$1» — Ñто тип данных Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð¾Ð² времени в едином формате.",
+ "smw-type-ema": "«$1» — тип данных длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа Ñлектронной почты.",
+ "smw-type-tem": "«$1» — оÑобый чиÑленный тип данных длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ‚ÐµÐ¼Ð¿ÐµÑ€Ð°Ñ‚ÑƒÑ€Ñ‹.",
+ "smw-type-qty": "«$1» — чиÑленный тип данных Ñ единицей измерениÑ.",
+ "smw-type-rec": "«$1» — тип данных длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑƒÐ¿Ñ€Ñдоченного ÑпиÑка типизированных ÑвойÑтв.",
+ "smw-type-extra-tem": "Процедура перевода поддерживает температурные шкалы Кельвина, ЦельÑиÑ, Фаренгейта и Ранкина.",
+ "smw-type-tab-properties": "СвойÑтва",
+ "smw-type-tab-types": "Типы",
+ "smw-type-tab-errors": "Ошибки",
+ "smw-type-primitive": "СкалÑрные",
+ "smw-type-contextual": "ЗавиÑимые",
+ "smw-type-compound": "СоÑтавные",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "Ðа Ñтой Ñтранице предÑтавлен Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¿Ñ€Ð¾Ñмотра Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка вÑех значений ÑвойÑтва и заданной Ñтраницы. Другие доÑтупные интерфейÑÑ‹ поиÑка включают в ÑÐµÐ±Ñ [[Special:SearchByProperty|поиÑк ÑвойÑтв]] и [[Special:Ask|поÑтроитель поиÑковых запроÑов]].",
+ "smw-property-predefined-errc": "«$1» — Ñто предварительно определённое ÑвойÑтво, обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] и предÑтавлÑет ошибки, которые возникли в ÑвÑзи Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ñ‹Ð¼Ð¸ аннотациÑми значений или обработкой ввода.",
+ "smw-property-predefined-long-errc": "Ошибки накапливаютÑÑ Ð² [https://www.semantic-mediawiki.org/wiki/Help:Container контейнере], который также может включать ÑÑылки на ÑвойÑтво, которое и повлекло неÑоответÑтвие.",
+ "smw-property-predefined-errt": "«$1» — Ñто предварительно определенное ÑвойÑтво, Ñодержит текÑтовое опиÑание ошибки и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "Определенный пользователем подобъект Ñодержал недейÑтвительную Ñхему наименованиÑ. Точка ($1) в первых пÑти Ñимволах предназначена Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ñключительно раÑширениÑми. Ð’Ñ‹ можете уÑтановить [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier именованный идентификатор].",
+ "smw-datavalue-record-invalid-property-declaration": "ЗапиÑÑŒ Ñодержит ÑвойÑтво «$1», ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñама по Ñебе объÑвлена типом запиÑи, и Ñто не допуÑкаетÑÑ.",
+ "smw-property-predefined-mdat": "«$1» — Ñто предварительно определенное ÑвойÑтво, что ÑоответÑтвует дате поÑледнего Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð°, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-cdat": "«$1» — Ñто предварительно определенное ÑвойÑтво, что ÑоответÑтвует дате первой верÑии Ñубъекта, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-newp": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое указывает, ÑвлÑетÑÑ Ð»Ð¸ Ñубъект новым или нет, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-ledt": "«$1» — Ñто предварительно определенное ÑвойÑтво Ñодержит название Ñтраницы того пользователÑ, который Ñоздал первую верÑию, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-mime": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое опиÑывает MIME-тип загруженного файла и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-media": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое опиÑывает тип ноÑителÑ, загруженного медиафайла, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askfo": "«$1» — Ñто предварительно определенное ÑвойÑтво Ñодержит Ð¸Ð¼Ñ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð³Ð¾ формата, иÑпользованного в запроÑе, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askst": "«$1» — Ñто предварительно определенное ÑвойÑтво, опиÑывающее уÑÐ»Ð¾Ð²Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа в виде Ñтроки, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askdu": "«$1» — Ñто предварительно определенное ÑвойÑтво, Ñодержащее значение времени (в Ñекундах), которое требуетÑÑ Ð´Ð»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksc": "«$1» — предопределённое ÑвойÑтво, предоÑтавленное ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'' длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð°Ð»ÑŒÑ‚ÐµÑ€Ð½Ð°Ñ‚Ð¸Ð²Ð½Ñ‹Ñ… (Ñ‚.е. внешник) иÑточников запроÑов.",
+ "smw-property-predefined-askco": "«$1» — предопределённое ÑвойÑтво, предоÑтавленное ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'' длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑоÑтоÑÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа или его чаÑтей.",
+ "smw-property-predefined-long-askco": "ЧиÑло или чиÑла, предÑтавлÑющие код ÑоÑтоÑÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа (Ñм. [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler Профилировщик запроÑов]).",
+ "smw-property-predefined-prec": "«$1» — Ñто предварительно определенное ÑвойÑтво, опиÑывающее [https://www.semantic-mediawiki.org/wiki/Help:Display_precision точноÑÑ‚ÑŒ отображениÑ] (в деÑÑтичных цифрах) Ð´Ð»Ñ Ñ‡Ð¸Ñловых типов данных.",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Extension:Maps РаÑширение «Maps»] не было обнаружено, поÑтому ÑвойÑтво «$1» ограничено в Ñвоей работе.",
+ "smw-datavalue-monolingual-dataitem-missing": "ОтÑутÑтвует ожидаемый Ñлемент Ð´Ð»Ñ Ð¿Ð¾ÑÑ‚Ñ€Ð¾ÐµÐ½Ð¸Ñ Ð¾Ð´Ð½Ð¾Ñзычного значение.",
+ "smw-datavalue-languagecode-missing": "Ð”Ð»Ñ Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ð¸ «$1» парÑер не Ñмог определить кода Ñзыка (например «foo@en»).",
+ "smw-datavalue-languagecode-invalid": "«$1» не был раÑпознан как поддерживаемый код Ñзыка.",
+ "smw-property-predefined-lcode": "«$1» — Ñто предварительно определенное ÑвойÑтво, предÑтавлÑющее BCP47-форматированный код Ñзыка, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-type-mlt-rec": "«$1» — Ñто [https://www.semantic-mediawiki.org/wiki/Help:Container контейнерный] тип данных, который ÑвÑзывает текÑтовое значение Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ñ‹Ð¼ [[Property:Language code|кодом Ñзыка]].",
+ "smw-types-extra-mlt-lcode": "Этот тип данных {{PLURAL:$2|требует|не требует}} кода Ñзыка (то еÑÑ‚ÑŒ {{PLURAL:$2|Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±ÐµÐ· кода Ñзыка не принимаетÑÑ|Ð°Ð½Ð½Ð¾Ñ‚Ð°Ñ†Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð±ÐµÐ· кода Ñзыка принимаетÑÑ}}).",
+ "smw-property-predefined-text": "«$1» — Ñто предварительно определенное ÑвойÑтво, предÑтавлÑющее текÑÑ‚ вÑпомогательной длины, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pdesc": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое дает возможноÑÑ‚ÑŒ опиÑать ÑвойÑтво в контекÑте Ñзыка, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-list": "«$1» — Ñто предварительно определенное ÑвойÑтво Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ ÑпиÑка ÑвойÑтв, иÑпользованных Ñо ÑвойÑтвом типа [[Special:Types/Record|record]], и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] Ð’Ñ€ÐµÐ¼Ñ Ñ€Ð°Ð·Ð±Ð¾Ñ€Ð° внутритекÑтовой аннотации",
+ "smw-limitreport-intext-postproctime": "[SMW] Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾ÑÑ‚-обработки",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунды|Ñекунд}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунды|Ñекунд}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Ð’Ñ€ÐµÐ¼Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ store-базы данных (при обновлении Ñтраницы)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунды|Ñекунд}}",
+ "smw_allows_pattern": "Чтобы доÑтуп к Ñтранице был открыт Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ÑвойÑтва «[[Property:Allows pattern|Allows pattern]]», Ñта Ñтраница должна Ñодержать ÑпиÑок ÑÑылок (поÑле которого идут [https://ru.wikipedia.org/wiki/РегулÑрные_Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ñ€ÐµÐ³ÑƒÐ»Ñрные выражениÑ]). Ð”Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтой Ñтраницы необходимы права <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "РегулÑрное выражение «$2» клаÑÑифицировало значение «$1» как недопуÑтимое.",
+ "smw-datavalue-allows-pattern-reference-unknown": "СÑылки на шаблон «$1» не удалоÑÑŒ Ñравнить Ñ Ð·Ð°Ð¿Ð¸Ñью на [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "Строка «$1» не найдена в ÑпиÑке [[MediaWiki:Smw allows list $1]] .",
+ "smw-datavalue-allows-value-list-missing-marker": "СпиÑок «$1» не Ñодержит Ñтрок Ñ маркёром ÑпиÑка <code>*</code>.",
+ "smw-datavalue-feature-not-supported": "Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Â«$1» не поддерживаетÑÑ Ð¸Ð»Ð¸ была выключена в Ñтой вики.",
+ "smw-property-predefined-pvap": "«$1» — Ñто предварительно определенное ÑвойÑтво, которой можно указать [[MediaWiki:Smw allows pattern|ÑÑылка на шаблон]], чтобы применить ÑопоÑтавление [https://en.wikipedia.org/wiki/Regular_expression регулÑрных выражений], и ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¾Ð±ÐµÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-dtitle": "«$1» — Ñто предварительно определенное ÑвойÑтво, которой Ð´Ð»Ñ ÑущноÑти можно назначить отдельное название Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ, и которое обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvuc": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] Ð´Ð»Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ÑÐ²Ð¾ÐµÐ½Ð¸Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ð¹ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑкземплÑра уникальным значением (или макÑимум одним).",
+ "smw-property-predefined-long-pvuc": "УникальноÑÑ‚ÑŒ уÑтанавливаетÑÑ, когда два Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвлÑÑŽÑ‚ÑÑ Ð½ÐµÐ¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñ‹Ð¼Ð¸ в Ñвоей Ñимвольной репрезентации, и любое нарушение Ñтого Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ квалифицировано как ошибка.",
+ "smw-datavalue-uniqueness-constraint-error": "СвойÑтво «$1» позволÑет только уникальные приÑвоенные значениÑ, а «$2» уже было аннотировано в теме «$3».",
+ "smw-datavalue-uniqueness-constraint-isknown": "СвойÑтво «$1» предуÑматривает только уникальные приÑвоениÑ. Ðа Ñтранице «$2» уже еÑÑ‚ÑŒ приÑвоенное значение. «$3» нарушает требование уникальноÑти.",
+ "smw-property-predefined-boo": "«$1» — [[Special:Types/Boolean|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'' длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ»ÐµÐ²Ñ‹Ñ… значений.",
+ "smw-property-predefined-num": "«$1» — [[Special:Types/Number|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'' длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñ‡Ð¸Ñленных данных.",
+ "smw-property-predefined-dat": "«$1» — [[Special:Types/Date|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ñ‚.",
+ "smw-property-predefined-uri": "«$1» — [[Special:Types/Date|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ URI и URL.",
+ "smw-property-predefined-qty": "«$1» — [[Special:Types/Date|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÐºÐ¾Ð»Ð¸Ñ‡ÐµÑтвенных данных Ñ единицами измерениÑ.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "Сдвиг и чаÑовой поÑÑ Ð²Â Â«$1» не раÑпознан.",
+ "smw-datavalue-time-invalid-values": "Значение «$1» Ñодержит не раÑпознанную информацию «$2».",
+ "smw-datavalue-time-invalid-date-components-common": "«$1» Ñодержит некоторую не поддающуюÑÑ Ð¸Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚Ð°Ñ†Ð¸Ð¸ информации.",
+ "smw-datavalue-time-invalid-date-components-dash": "«$1» Ñодержит поÑторонний Ð´ÐµÑ„Ð¸Ñ Ð¸Ð»Ð¸Â Ð´Ñ€ÑƒÐ³Ð¾Ð¹ Ñимвол, не допуÑтимый в дате.",
+ "smw-datavalue-time-invalid-date-components-empty": "«$1» Ñодержит некоторые пуÑтые компоненты.",
+ "smw-datavalue-time-invalid-date-components-three": "«$1» Ñодержит более трёх ÑоÑтавлÑющих, требуемых длÑ раÑÐ¿Ð¾Ð·Ð½Ð°Ð½Ð¸Ñ Ð´Ð°Ñ‚Ñ‹.",
+ "smw-datavalue-time-invalid-date-components-sequence": "«$1» Ñодержит поÑледовательноÑÑ‚ÑŒ Ñимволов, не поддающуюÑÑ Ð¸Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚Ð°Ñ†Ð¸Ð¸ в ÑоответÑтвии Ñ имеющейÑÑ Ð¼Ð°Ñ‚Ñ€Ð¸Ñ†ÐµÐ¹ компонентов дат.",
+ "smw-datavalue-time-invalid-ampm": "«$1» Ñодержит подÑтроку «$2», ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½ÐµÂ Ð¼Ð¾Ð¶ÐµÑ‚ ÑодержатьÑÑ Ð²Ð¾Â Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ в двенадцатичаÑовом формате.",
+ "smw-datavalue-time-invalid-jd": "«$1» не поддаётÑÑ Ñ€Ð°Ð·Ð±Ð¾Ñ€Ñƒ в качеÑтве корректного юлианÑкого Ð´Ð½Ñ (результат разбора — «$2»)",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "Ð’ маÑке URI нет Ñимвола подÑтановки «$1».",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» — недопуÑтимый URL-адреÑ.",
+ "smw-datavalue-external-identifier-formatter-missing": "Ðе назначено ÑвойÑтво «[[Property:External formatter uri|External formatter URI]]».",
+ "smw-datavalue-keyword-maximum-length": "Ключевое Ñлово превыÑило макÑимальную длину $1 {{PLURAL:$1|Ñимвол|Ñимвола|Ñимволов}}.",
+ "smw-property-predefined-eid": "«$1» — [[Special:Types/External identifier|тип данных]] и предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', длÑÂ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½Ð¸Ñ… идентификаторов.",
+ "smw-property-predefined-peid": "«$1» — предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ идентификатора.",
+ "smw-property-predefined-pefu": "«$1» — предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', Ð´Ð»Ñ Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа внешнего реÑурÑа Ñ Ñимволом подÑтановки.",
+ "smw-property-predefined-long-pefu": "В URI должен ÑодержатьÑÑ Ñимвол подÑтановки, который будет заменён на [[Special:Types/External identifier|внешний идентификатор]] длÑÂ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾Ð¹ ÑÑылки на реÑурÑ",
+ "smw-type-eid": "«$1» — разновидноÑÑ‚ÑŒ типа «[[Special:Types/Text|ТекÑÑ‚]]», длÑ которого надо указать ÑвойÑтво «[[Property:External formatter uri|External formatter URI]]».",
+ "smw-datavalue-parse-error": "Переданное значение «$1» не было понÑто.",
+ "smw-datavalue-propertylist-invalid-property-key": "СпиÑок ÑвойÑтв «$1» Ñодержит недопуÑтимый ключ ÑвойÑтва «$2».",
+ "smw-datavalue-type-invalid-typeuri": "Тип «$1» не может быть преобразован в корректный URI.",
+ "smw-datavalue-wikipage-invalid-title": "Значение «$1» типа «Страница» Ñодержит недопуÑтимые Ñимволы или неполно и может привеÑти к неожиданным результатам при ÑемантичеÑком аннотировании или запроÑе.",
+ "smw-datavalue-wikipage-property-invalid-title": "СвойÑтво «$1» типа «Страница» Ñо значением «$2» Ñодержит недопуÑтимые Ñимволы или неполно и может привеÑти к неожиданным результатам при ÑемантичеÑком аннотировании или запроÑе.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "Тип «[[Special:Types/Reference|СÑылка]]» требует ввода ÑпиÑка ÑвойÑтв, объÑвленных Ñ помощью ÑвойÑтва «[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields Has fields]».",
+ "smw-parser-invalid-json-format": "JSON-парÑер вернул ошибку «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "«$1» Ð½ÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать в качеÑтве предпочтительной метки, поÑкольку Ñзык «$2» уже назначен метке «$3».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Скопировать ÑÑылку в буфер обмена",
+ "smw-property-userdefined-fixedtable": "СвойÑтво «$1» Ñконфигурировано как as [https://www.semantic-mediawiki.org/wiki/Fixed_properties фикÑированное], и любые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÐµÐ³Ð¾Â [https://www.semantic-mediawiki.org/wiki/Type_declaration типа] требуют или запуÑтить <code>setupStore.php</code>, или выполнить задачу «УÑтановка и обновление базы данных» на Ñлужебной Ñтранице ''[[Special:SemanticMediaWiki|SemanticMediaWiki]]''.",
+ "smw-data-lookup": "Извлечение данных…",
+ "smw-data-lookup-with-wait": "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð¾Ð±Ñ€Ð°Ð±Ð°Ñ‚Ñ‹Ð²Ð°ÐµÑ‚ÑÑ Ð¸Â Ð¼Ð¾Ð¶ÐµÑ‚ занÑÑ‚ÑŒ некоторое времÑ.",
+ "smw-no-data-available": "Ðет доÑтупных данных.",
+ "smw-property-req-violation-missing-fields": "ДлÑ ÑвойÑтва «$1» типа «$2» не задано обÑзательное ÑвойÑтво <code>Has fields</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "ДлÑ ÑвойÑтва «$1» не задано обÑзательное ÑвойÑтво <code>External formatter URI</code>.",
+ "smw-property-req-violation-predefined-type": "ДлÑ предопределённого ÑвойÑтва «$1» задан тип «$2», Ñ которым неÑовмеÑтимо значение ÑвойÑтва по умолчанию.",
+ "smw-property-req-violation-import-type": "Обнаружено объÑвление типа, которое неÑовмеÑтимо Ñ Ð¿Ñ€ÐµÐ´Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ð¼ типом импортированного ÑÐ»Ð¾Ð²Ð°Ñ€Ñ Â«$1». Как правило, нет необходимоÑти объÑвлÑÑ‚ÑŒ тип, потому что Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¸Ð·Ð²Ð»ÐµÐºÐ°ÐµÑ‚ÑÑ Ð¸Ð· Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°.",
+ "smw-property-req-violation-change-propagation-locked-error": "СвойÑтво «$1» изменено, и ÑвÑзанные Ñ ним Ñтраницы должны быть обновлены путём [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹]. Страница ÑвойÑтва закрыта от изменений пока первичное обновление не будет завершено, чтобы избежать Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸Â Ð¿Ñ€Ð¾Ñ‚Ð¸Ð²Ð¾Ñ€ÐµÑ‡Ð¸Ð²Ñ‹Ñ… наÑтроек ÑвойÑтва. Это может занÑÑ‚ÑŒ некоторое времÑ, завиÑÑщее от размера [https://www.mediawiki.org/wiki/Manual:Job_queue очереди задач] и чаÑтоты её обработки, по иÑтечении которого Ñтраница будет открыта длÑ изменений.",
+ "smw-property-req-violation-change-propagation-locked-warning": "СвойÑтво «$1» изменено, и ÑвÑзанные Ñтраницы должны быть обновены путём [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹]. Обновление может занÑÑ‚ÑŒ некоторое времÑ, завиÑÑщее от размера [https://www.mediawiki.org/wiki/Manual:Job_queue очереди заданий]. РекомендуетÑÑ Ð¾Ñ‚Ð»Ð¾Ð¶Ð¸Ñ‚ÑŒ редактирование ÑвойÑтва, чтобы избежать Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸Â Ð¿Ñ€Ð¾Ñ‚Ð¸Ð²Ð¾Ñ€ÐµÑ‡Ð¸Ð²Ñ‹Ñ… наÑтроек.",
+ "smw-property-req-violation-change-propagation-pending": "ОюидаетÑÑ [https://www.semantic-mediawiki.org/wiki/Change_propagation РаÑпроÑтранение изменений] (около $1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|задание|заданий|заданиÑ}}]). РекомендуетÑÑ Ð¾Ñ‚Ð»Ð¾Ð¶Ð¸Ñ‚ÑŒ редактирование Ñтраницы ÑвойÑтва, пока процеÑÑ Ð½ÐµÂ Ð±ÑƒÐ´ÐµÑ‚ завершён, чтобы избезать прерываний Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸Â Ð¿Ñ€Ð¾Ñ‚Ð¸Ð²Ð¾Ñ€ÐµÑ‡Ð¸Ð²Ñ‹Ñ… наÑтроек ÑвойÑтва.",
+ "smw-property-req-violation-missing-maps-extension": "РаÑширение '[https://www.semantic-mediawiki.org/wiki/Extension:Maps Maps]', необходимое длÑ Ñтого типа данных. Как ÑледÑтвие, функциональноÑÑ‚ÑŒ Ñтого ÑвойÑтва ограничена.",
+ "smw-property-req-violation-type": "ДлÑ Ñтого ÑвойÑтва указаны противоречивые типы данных, что может привеÑти к уÑтановке некорректных значений длÑ него. Следует выбрать один подходÑщий тип.",
+ "smw-change-propagation-protection": "Эта Ñтраница закрыта длÑ изменений, чтобы предотвратить Ñлучайное изменение, пока идёт [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑтранение изменений]. Это может занÑÑ‚ÑŒ некоторое времÑ, завиÑÑщее от размера и чаÑтоты обработки [https://www.mediawiki.org/wiki/Manual:Job_queue очереди заданий], прежде чем Ñтраница будет открыта длÑ изменений.",
+ "smw-category-change-propagation-locked-error": "ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Â«$1» изменена, и ÑвÑзанные Ñтраницы должны быть обновлены путём [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹]. Рпока, Ñтраница категорий закрыта длÑ изменений, пока обновление не будет завершено, чтобы избежать Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸Â Ð¿Ñ€Ð¾Ñ‚Ð¸Ð²Ð¾Ñ€ÐµÑ‡Ð¸Ð¹ в наÑтройках. Это может занÑÑ‚ÑŒ некоторое времÑ, завиÑÑщее от размера [https://www.mediawiki.org/wiki/Manual:Job_queue очереди заданий], прежде чем Ñтраница будет открыта длÑ изменений.",
+ "smw-category-change-propagation-locked-warning": "ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Â«$1» изменена, и ÑвÑзанные Ñтраницы должны быть обновлены путём [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹]. Это может занÑÑ‚ÑŒ некоторое времÑ, завиÑÑщее от размера [https://www.mediawiki.org/wiki/Manual:Job_queue очереди заданий], прежде чем Ñтраница будет открыта Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹. ПредлагаетÑÑ Ð¾Ñ‚Ð»Ð¾Ð¶Ð¸Ñ‚ÑŒ редактирование Ñтраницы, пока обновление не будет завершено, чтобы избежать Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ противоречий в наÑтройках.",
+ "smw-category-change-propagation-pending": "ОжидаетÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ðµ в результате [https://www.semantic-mediawiki.org/wiki/Change_propagation раÑпроÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ð¹] (около $1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|задание|заданий|заданиÑ}}]). РекомендуетÑÑ Ð¾Ñ‚Ð»Ð¾Ð¶Ð¸Ñ‚ÑŒ редактирование категории, пока обновление не будет завершено, чтобы избежать Ð¿Ñ€ÐµÑ€Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸Â Ð¿Ñ€Ð¾Ñ‚Ð¸Ð²Ð¾Ñ€ÐµÑ‡Ð¸Ð²Ñ‹Ñ… наÑтроек.",
+ "smw-category-invalid-value-assignment": "«$1» — не ÑвлÑетÑÑ Ð´ÐµÐ¹Ñтвительной ÑемантичеÑкой аннотацией или заданием категории.",
+ "protect-level-smw-pageedit": "Разрешить только учаÑтникам, имеющим право на редактирование Ñтраниц (Semantic MediaWiki)",
+ "smw-create-protection": "Пока дейÑтвует [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode режим ограниченного доÑтупа], Ñоздание ÑвойÑтва «$1» разрешено только пользователÑм Ñ правами «$2» (или из [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups группы]).",
+ "smw-create-protection-exists": "Пока дейÑтвует [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode режим ограниченного доÑтупа], изменение ÑвойÑтва «$1» разрешено только пользователÑм Ñ Ð¿Ñ€Ð°Ð²Ð°Ð¼Ð¸ «$2» (или из такой [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups группы]).",
+ "smw-edit-protection": "Эта Ñтраница [[Property:Is edit protected|защищена от изменений]], чтобы предотвратить нежелательное изменеие данных, и может редактироватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ пользователÑми Ñ правами «$1» или из такой [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups группы].",
+ "smw-edit-protection-disabled": "Режим защиты Ð¾Ñ‚Â Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½, Ñледовательно ÑвойÑтво «$1» не может быть иÑпользовано длÑ защиты Ñтраницы от редактированиÑ.",
+ "smw-edit-protection-auto-update": "Уровень защиты Ñтраницы изменён на оÑновании Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвойÑтва «Is edit protected».",
+ "smw-edit-protection-enabled": "Редактировать Ñтраницы (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Эта Ñтраница защищена и может редактироватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ пользователÑми Ñ правами <code>smw-patternedit</code> (Ñм. ''[https://www.semantic-mediawiki.org/wiki/Help:Permissions разрешениÑ]'').",
+ "smw-property-predefined-edip": "«$1» — предопределённое ÑвойÑтво, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]'', хранÑщее признак защиты от редактированиÑ.",
+ "smw-property-predefined-long-edip": "Ð¥Ð¾Ñ‚Ñ Ð»ÑŽÐ±Ð¾Ð¹ учаÑтник может уÑтановить Ñто ÑвойÑтво на Ñтраницу, только учаÑтники Ñ оÑобыми полномочиÑми Ñмогут изменить или удалить защиту поÑле того, как она будет уÑтановлена.",
+ "smw-query-reference-link-label": "СÑылка на запроÑ",
+ "smw-format-datatable-emptytable": "Данные отÑутÑтвуют в таблице",
+ "smw-format-datatable-info": "Отображение запиÑей от _START_ до _END_ Ñ _TOTAL_",
+ "smw-format-datatable-infoempty": "Показаны от 0 до 0 из 0 запиÑей",
+ "smw-format-datatable-infofiltered": "(отфильтровано по _MAX_ вÑего запиÑей)",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-lengthmenu": "Показать _MENU_ запиÑей",
+ "smw-format-datatable-loadingrecords": "Загрузка…",
+ "smw-format-datatable-processing": "Обработка…",
+ "smw-format-datatable-search": "ПоиÑк:",
+ "smw-format-datatable-zerorecords": "СоответÑтвующие запиÑи не найдены",
+ "smw-format-datatable-first": "ПерваÑ",
+ "smw-format-datatable-last": "ПоÑледнÑÑ",
+ "smw-format-datatable-next": "СледующаÑ",
+ "smw-format-datatable-previous": "ПредыдущаÑ",
+ "smw-format-datatable-sortascending": ":активировать Ð´Ð»Ñ Ñортировки Ñтолбца по возраÑтанию",
+ "smw-format-datatable-sortdescending": ":активировать Ð´Ð»Ñ Ñортировки Ñтолбца по убыванию",
+ "smw-format-datatable-toolbar-export": "ЭкÑпорт",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Â«$1» Ñодержит некорректное перенаправление в иное проÑтранÑтво имён.",
+ "apihelp-smwinfo-summary": "Модуль API Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о ÑтатиÑтике Semantic MediaWiki и другой метаинформации.",
+ "apihelp-ask-summary": "Модуль API Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа Semantic MediaWiki Ñ Ð¸Ñпользованием Ñзыка запроÑов.",
+ "apihelp-askargs-summary": "Модуль API Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа Semantic MediaWiki Ñ Ð¸Ñпользованием Ñзыка запроÑов в качеÑтве ÑпиÑка уÑловий, раÑпечаток и параметров.",
+ "apihelp-browsebyproperty-summary": "Модуль API Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о ÑвойÑтве или ÑпиÑке ÑвойÑтв.",
+ "apihelp-browsebysubject-summary": "Модуль API Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ о предмете.",
+ "apihelp-smwtask-summary": "Модуль API Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡, ÑвÑзанных Ñ Semantic MediaWiki.",
+ "apihelp-smwbrowse-summary": "Модуль API Ð´Ð»Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ дейÑтвий проÑмотра Ð´Ð»Ñ Ñ€Ð°Ð·Ð»Ð¸Ñ‡Ð½Ñ‹Ñ… типов объектов в Semantic MediaWiki.",
+ "smw-api-invalid-parameters": "ÐедопуÑтимые параметры, \"$1\"",
+ "smw-property-page-list-count": "$1 {{PLURAL:$1|Ñтраница|Ñтраниц|Ñтраницы}}, иÑпользующих Ñто ÑвойÑтво.",
+ "smw-property-page-list-search-count": "$1 {{PLURAL:$1|Ñтраница|Ñтраниц|Ñтраницы}}, на которых длÑ ÑвойÑтва уÑтановлено значение «$2».",
+ "smw-property-reserved-category": "КатегориÑ",
+ "smw-category": "КатегориÑ",
+ "smw-datavalue-uri-invalid-scheme": "«$1» не указан, как допуÑтимый протокол URI",
+ "smw-browse-property-group-title": "Группа ÑвойÑтв",
+ "smw-browse-property-group-label": "Метка группы ÑвойÑтв",
+ "smw-browse-property-group-description": "ОпиÑание группы ÑвойÑтв",
+ "smw-property-predefined-ppgr": "«$1» — предопределённое ÑвойÑтво, отмечающее ÑущноÑти (в оÑновном, категории), иÑпользуемые длÑ группировки ÑвойÑтв, предоÑтавлÑемое ''[https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki]''.",
+ "smw-filter": "Фильтр",
+ "smw-section-expand": "РаÑкрыть раздел",
+ "smw-section-collapse": "Свернуть раздел",
+ "smw-ask-format-help-link": "Формат [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Справка",
+ "smw-cheat-sheet": "Шпаргалка",
+ "smw-personal-jobqueue-watchlist": "СпиÑок Ð½Ð°Ð±Ð»ÑŽÐ´ÐµÐ½Ð¸Ñ (очередь заданий)",
+ "smw-property-predefined-label-skey": "Ключ Ñортировки",
+ "smw-processing": "Обработка…",
+ "smw-types-title": "Тип: $1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "Изменение контентной модели [https://www.semantic-mediawiki.org/wiki/Help:Schema Ñтраницы Ñхемы] не разрешено.",
+ "smw-schema-namespace-edit-protection": "Эта Ñтраница защищена, править её могут только учаÑтники Ñ [https://www.semantic-mediawiki.org/wiki/Help:Permissions правом] <code>smw-schemaedit</code>.",
+ "smw-schema-error": "Ошибка проверки",
+ "smw-schema-error-violation": "Ðарушение («$1», «$2»)",
+ "smw-schema-title": "Схема",
+ "smw-schema-type": "Тип",
+ "smw-property-predefined-schema-desc": "«$1» — Ñто предварительно определенное ÑвойÑтво, Ñодержащее опиÑание Ñхемы, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-schema-def": "«$1» — Ñто предварительно определенное ÑвойÑтво, которое опиÑывает Ñодержимое Ñхемы, и обеÑпечиваетÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-ask-title-keyword-type": "ПоиÑк по ключевым Ñловам",
+ "smw-remote-source-unavailable": "\nÐевозможно подключитьÑÑ Ðº удаленной цели «$1».",
+ "smw-remote-source-disabled": "ИÑточник '' '$1' '' отключил поддержку удаленного запроÑа!",
+ "smw-parameter-missing": "Параметр «$1» отÑутÑтвует.",
+ "smw-property-tab-usage": "ИÑпользование",
+ "smw-property-tab-redirects": "Синонимы",
+ "smw-property-tab-subproperties": "ПодÑвойÑтва",
+ "smw-property-tab-specification": "… ещё",
+ "smw-concept-tab-list": "СпиÑок",
+ "smw-concept-tab-errors": "Ошибки",
+ "smw-ask-tab-result": "Результат",
+ "smw-ask-tab-extra": "Дополнительно",
+ "smw-ask-tab-debug": "Отладка",
+ "smw-ask-tab-code": "Код",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/rue.json b/www/wiki/extensions/SemanticMediaWiki/i18n/rue.json
new file mode 100644
index 00000000..e5199871
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/rue.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gazeb"
+ ]
+ },
+ "smw_browse_go": "Выконати",
+ "smw_result_prev": "ПопереднÑ",
+ "smw_result_next": "Далша",
+ "smw_result_results": "Резултаты",
+ "smw_result_noresults": "Жадны реÑултаты",
+ "smw_smwadmin_return": "ÐÐ°Ð²ÐµÑ€Ð½ÑƒÑ‚Ñ Ð´Ð¾ «$1».",
+ "smw-livepreview-loading": "ÐаладовованÑ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sa.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sa.json
new file mode 100644
index 00000000..0e8364ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sa.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "NehalDaveND"
+ ]
+ },
+ "browse": "विकि दृशà¥à¤¯à¤¤à¤¾à¤®à¥",
+ "smw-livepreview-loading": "आरोपयति..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sah.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sah.json
new file mode 100644
index 00000000..77ae0d82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sah.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "HalanTul",
+ "Ðй-Куо"
+ ]
+ },
+ "smw_purge": "Саҥарт",
+ "smw-ask-search": "Бул",
+ "browse": "Биикини көрүү",
+ "smw-livepreview-loading": "КиллÑрии бара турар…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sc.json
new file mode 100644
index 00000000..913c7669
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sc.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "L2212"
+ ]
+ },
+ "smw_smwadmin_datarefreshstopconfirm": "Eja, deo so {{GENDER:$1|seguru|segura}}"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/scn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/scn.json
new file mode 100644
index 00000000..3a5e8621
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/scn.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "Gmelfi",
+ "Sarvaturi"
+ ]
+ },
+ "browse": "Esplora lu situ",
+ "smw_browselink": "Prupità",
+ "smw-livepreview-loading": "Carricamentu 'n cursu..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sco.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sco.json
new file mode 100644
index 00000000..677c97ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sco.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": [
+ "John Reid",
+ "AmaryllisGardener"
+ ]
+ },
+ "browse": "Brouse wiki",
+ "smw_smwadmin_mediazilla": "Bugs can be reported til <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-sp-admin-idlookup-title": "ObjectId luikup",
+ "smw-sp-admin-idlookup-docu": "Displeys details aneat aen internal object Id that represents indeevidual entities (wikipage, subobject etc.) in Semantic MediaWiki.",
+ "smw-sp-admin-idlookup-objectid": "The Object Id:",
+ "smw-livepreview-loading": "Laidin..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sd.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sd.json
new file mode 100644
index 00000000..d24e7a72
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sd.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Aursani",
+ "Mehtab ahmed"
+ ]
+ },
+ "browse": "وڪي جھانگيو",
+ "smw-limitreport-intext-parsertime-value": "$1 سيڪنڊ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sdc.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sdc.json
new file mode 100644
index 00000000..7ec42fc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sdc.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Carrigghendi…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sgs.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sgs.json
new file mode 100644
index 00000000..ad0eeb96
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sgs.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hugo.arg"
+ ]
+ },
+ "browse": "Poslapiu apveiza",
+ "smw-livepreview-loading": "Kraunama īr…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sh.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sh.json
new file mode 100644
index 00000000..74453aa1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sh.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "OC Ripper"
+ ]
+ },
+ "browse": "Pretražuj wiki",
+ "smw_result_prev": "Prethodna",
+ "smw-livepreview-loading": "UÄitavanje..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/shn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/shn.json
new file mode 100644
index 00000000..e5168faa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/shn.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Saimawnkham"
+ ]
+ },
+ "browse": "ပိုá€á€ºá‚‡á‚ႃ wiki"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/si.json b/www/wiki/extensions/SemanticMediaWiki/i18n/si.json
new file mode 100644
index 00000000..eacdfcf1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/si.json
@@ -0,0 +1,152 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කà·à·€à·’න්ද",
+ "Susith Chandira Gts",
+ "SusithCM"
+ ]
+ },
+ "smw_viewasrdf": "RDF පà·à·‚කය",
+ "smw_finallistconjunct": ", සහ",
+ "smw_factbox_head": "\"$1\" පිළිබඳ පරිසිද්ධි",
+ "smw_isspecprop": "මෙම වත්කම විකියෙහි තිබෙන විà·à·šà·‚ වත්කමකි.",
+ "smw_concept_description": "\"$1\" සංකල්පයේ විස්තරය",
+ "version-semantic": "අර්ථ විචà·à¶» විස්තීරණ",
+ "smw_baduri": "\"$1\" à·†à·à¶»à¶¸à¶ºà·š URIs ඉඩ නොදේ.",
+ "smw_printername_count": "ප්â€à¶»à¶­à·’එල ගණනය කරන්න",
+ "smw_printername_csv": "CSV නිර්යà·à¶­ කිරීම",
+ "smw_printername_dsv": "DSV නිර්යà·à¶­ කිරීම",
+ "smw_printername_debug": "නොදොස් කිරීම් ප්â€à¶»à·à·Šà¶±à¶º (ප්â€à¶»à·€à·“ණයන් සඳහà·)",
+ "smw_printername_embedded": "කà·à·€à¶¯à·Šà¶¯à·– ගොනුවේ අන්තර්ගතය",
+ "smw_printername_json": "JSON නිර්යà·à¶­ කිරීම",
+ "smw_printername_list": "ලà·à¶ºà·’ස්තුව",
+ "smw_printername_ol": "ප්â€à¶»à¶œà¶«à¶±à¶º",
+ "smw_printername_ul": "ලà·à¶ºà·’ස්තුගතකෙරුම",
+ "smw_printername_table": "වගුව",
+ "smw_printername_broadtable": "ග්â€à¶»à·à¶¸à·Šâ€à¶º වගුව",
+ "smw_printername_template": "à·ƒà·à¶šà·’ල්ල",
+ "smw_printername_rdf": "RDF නිර්යà·à¶­ කිරීම",
+ "smw_printername_category": "ප්â€à¶»à·€à¶»à·Šà¶œà¶º",
+ "validator-type-class-SMWParamSource": "පෙළ",
+ "smw-paramdesc-limit": "නà·à·€à¶­ ආ යුතු උපරිම ප්â€à¶»à¶­à·’එල ගණන",
+ "smw-paramdesc-offset": "පළමු ප්â€à¶»à¶­à·’එලයේ එලෙන්දුව",
+ "smw-paramdesc-headers": "à·à·“ර්ෂයන්/වත්කම් නà·à¶¸à¶ºà¶±à·Š පෙන්වන්න",
+ "smw-paramdesc-mainlabel": "ප්â€à¶»à¶°à·à¶± පිටු නà·à¶¸à¶ºà¶§ දිය යුතු ලේබලය",
+ "smw-paramdesc-link": "අගයන් සබà·à¶³à·’ ලෙස පෙන්වන්න",
+ "smw-paramdesc-sep": "අගයන් සඳහ෠විභේදකය",
+ "smw-paramdesc-embedonly": "à·à·Šâ€à¶»à·“ර්ෂනà·à¶¸ පෙන්වන්න එපà·",
+ "smw-paramdesc-csv-sep": "භà·à·€à·’ත෠කල යුතු විභේදකය",
+ "smw-paramdesc-dsv-separator": "භà·à·€à·’ත෠කල යුතු විභේදකය",
+ "smw-paramdesc-dsv-filename": "DSV ගොනුවේ නම",
+ "smw-paramdesc-order": "ප්â€à¶»à·à·Šà¶± පෙළගà·à·ƒà·Šà¶¸à·š අනුපිලිවෙල",
+ "smw-paramdesc-searchlabel": "සෙවුම දිගටම කරගෙන යà·à¶¸ සඳහ෠පà·à¶¨",
+ "smw-paramdesc-export": "නිර්යà·à¶­ විකල්පය",
+ "smw-paramdesc-source": "වෛකල්පික විමසුම් මූලà·à·à·Šâ€à¶»à¶º",
+ "smw-paramdesc-jsonsyntax": "භà·à·€à·’ත෠කල යුතු JSON à·€à·à¶šà·Šâ€à¶º වින්â€à¶ºà·à·ƒà¶º",
+ "smw-printername-feed": "RSS සහ Atom පà·à·‚කය",
+ "smw-paramdesc-feedtype": "පà·à·‚ක වර්ගය",
+ "smw-label-feed-description": "$1 $2 පà·à·‚කය",
+ "smw_iq_moreresults": "… ඉදිරි ප්â€à¶»à¶­à·’එල",
+ "smw_parseerror": "ලබ෠දුන් අගය තේරුම්ගත නොහà·à¶š.",
+ "smw_manytypes": "වත්කම සඳහ෠වර්ගයන් එකකට වඩ෠අර්ථ දක්ව෠ඇත.",
+ "smw_emptystring": "හිස් තන්තු බà·à¶»à¶œà¶±à·” නොලà·à¶¶à·š.",
+ "smw_true_words": "true,t,yes,y",
+ "smw_false_words": "false,f,no,n",
+ "smw_nofloat": "\"$1\" යනු ඉලක්කමක් නොවේ.",
+ "smw_novalues": "අගයන් කිසිවක් විà·à·šà·‚ණය කර නොමà·à¶­.",
+ "smw_nodatetime": "\"$1\" දිනය තේරුම්ගත නොහà·à¶š.",
+ "smw_emptysubquery": "සමහර උපප්â€à¶»à·à·Šà¶±à¶ºà¶±à·Š සතුව වලංගු කොන්දේසියක් නà·à¶­.",
+ "smw_propvalueproblem": "\"$1\" වත්කමෙහි අගය තේරුම්ගත නොහà·à¶š.",
+ "smw_type_header": "\"$1\" වර්ගයේ වත්කම්",
+ "smw_attribute_header": "\"$1\" වත්කම භà·à·€à·’ත෠කරන පිටු",
+ "smw_subproperty_header": "උපගුණà·à¶‚ග",
+ "exportrdf": "RDF වෙත පිටු නිර්යà·à¶­ කරන්න",
+ "smw_exportrdf_submit": "නිර්යà·à¶­ කරන්න",
+ "uriresolver": "URIපිළිවිසඳුව",
+ "properties": "ගුණ",
+ "smw_properties_docu": "පහත දà·à¶šà·Šà·€à·™à¶± වත්කම් විකිය තුල භà·à·€à·’ත෠වේ.",
+ "smw_propertylackspage": "සියලුම වත්කම් පිටුවක් මඟින් විස්තර කල යුතු වේ!",
+ "smw_propertyhardlyused": "මෙම වත්කම විකිය තුල දà·à¶©à·’ ලෙස භà·à·€à·’ත෠වේ!",
+ "unusedproperties": "අභà·à·€à·’ත ගුණà·à¶‚ග",
+ "smw_unusedproperty_template": "$2 වර්ගයෙන් $1",
+ "wantedproperties": "උවමන෠ගුණà·à¶‚ග",
+ "smw_wantedproperty_template": "$1 ({{PLURAL:$2|භà·à·€à·’තයන්}} $2)",
+ "smw_purge": "නà·à·€à¶­ ප්â€à¶»à·à¶«à·€à¶­à·Š කරන්න",
+ "types": "වර්ග",
+
+ "ask": "අර්ථ විචà·à¶» සෙවුම",
+ "smw_ask_sortby": "තීරුව අනුව පෙළගස්වන්න (වෛකල්පික)",
+ "smw_ask_ascorder": "ආරà·à·„ණ",
+ "smw_ask_descorder": "අවරà·à·„ණ",
+ "smw_ask_submit": "ප්â€à¶»à¶­à·’ඵල සොයන්න",
+ "smw_ask_editquery": "ප්â€à¶»à·à·Šà¶±à¶º සංස්කරණය කරන්න",
+ "smw_add_sortcondition": "[පෙළගà·à·ƒà·Šà·€à·“ම් කොන්දේසියක් එක් කරන්න]",
+ "smw_ask_hidequery": "ප්â€à¶»à·à·Šà¶±à¶º සඟවන්න",
+ "smw_ask_help": "විමසුම් උදව්",
+ "smw_ask_queryhead": "ප්â€à¶»à·à·Šà¶±à¶º",
+ "smw_ask_printhead": "සංදර්à·à¶±à¶º කල යුතු අමතර දත්ත",
+ "smw_ask_printdesc": "(එක් පෙළකට එක් වත්කම් නà·à¶¸à¶ºà¶šà·Š එක් කරන්න)",
+ "smw_ask_format_as": "මෙලෙස ආකෘති කරන්න:",
+ "smw_ask_defaultformat": "à·ƒà·à¶¸à·à¶±à·Šâ€à¶º",
+ "smw_ask_otheroptions": "වෙනත් විකල්පයන්",
+ "smw_ask_show_embed": "කà·à·€à·à¶¯à·Šà¶¯à·– කේතය පෙන්වන්න",
+ "smw_ask_hide_embed": "කà·à·€à·à¶¯à·Šà¶¯à·– කේතය සඟවන්න",
+ "smw-ask-delete": "[මකන්න]",
+ "smw-ask-sorting": "තේරීම",
+ "searchbyproperty": "ගුණà·à¶‚ගය අනුව සොයන්න",
+ "smw_sbv_property": "ගුණà·à¶‚ගය:",
+ "smw_sbv_value": "අගය:",
+ "smw_sbv_submit": "ප්â€à¶»à¶­à·’ඵල සොයන්න",
+ "browse": "විකිය ගවේෂණය කරන්න",
+ "smw_browselink": "ගුණ ගවේෂණය කරන්න",
+ "smw_browse_go": "යන්න",
+ "smw_browse_show_incoming": "මෙයට සබà·à¶³à·™à¶± වත්කම් පෙන්වන්න",
+ "smw_browse_hide_incoming": "මෙයට සබà·à¶³à·™à¶± වත්කම් සඟවන්න",
+ "smw_browse_no_outgoing": "මෙම පිටුව සතුව ගුණà·à¶‚ග නොමà·à¶­.",
+ "smw_browse_no_incoming": "මෙම පිටුවට කිසිදු වත්කමක් සම්බන්ධ නොවේ.",
+ "smw_inverse_label_default": "$1 න්",
+ "smw_inverse_label_property": "වත්කම් ලේබලය ප්â€à¶»à¶­à·’ලà·à¶¸ කරන්න",
+ "pageproperty": "පිටු වත්කම් සෙවුම",
+ "smw_pp_from": "පිටුව වෙතින්",
+ "smw_pp_type": "ගුණà·à¶‚ගය",
+ "smw_pp_submit": "ප්â€à¶»à¶­à·’ඵල සොයන්න",
+ "smw_result_prev": "පෙර",
+ "smw_result_next": "මීළඟ",
+ "smw_result_results": "ප්â€à¶»à¶­à·’ඵල",
+ "smw_result_noresults": "ප්â€à¶»à¶­à·’ඵල නොමà·à¶­",
+ "smw_smwadmin_setupsuccess": "ආචයන එන්ජිම à·ƒà·à¶»à·Šà¶®à¶šà·€ සකසන ලදී.",
+ "smw_smwadmin_return": "$1 ට නà·à·€à¶­ යන්න",
+ "smw_smwadmin_db": "දත්ත සංචිත ස්ථà·à¶´à¶±à¶º හ෠උසස් කිරීම",
+ "smw_smwadmin_dbbutton": "වගු ඇරඹීම හ෠යà·à·€à¶­à·Šà¶šà·à¶½à·“න කිරීම සිදු කරන්න",
+ "smw_smwadmin_announce": "ඔබේ විකිය නිවේදනය කරන්න",
+ "smw_smwadmin_datarefresh": "දත්ත අලුත්වà·à¶©à·’යà·à·€ සහ උසස් කිරීම",
+ "smw_smwadmin_datarefreshbutton": "දත්ත යà·à·€à¶­à·Šà¶šà·à¶½à·“නය අරඹන්න",
+ "smw_smwadmin_datarefreshstop": "මෙම යà·à·€à¶­à·Šà¶šà·à¶½à·“නය නවතන්න",
+ "smw_smwadmin_datarefreshstopconfirm": "ඔව්, මට විà·à·Šà·€à·à·ƒà¶ºà·’.",
+ "smw_smwadmin_support": "සහà·à¶º ලබ෠ගනිමින්",
+ "smw_adminlinks_datastructure": "දත්ත ව්â€à¶ºà·”හය",
+ "smw_adminlinks_displayingdata": "දත්ත ප්â€à¶»à¶¯à¶»à·Šà·à¶±à¶º",
+ "smw_adminlinks_inlinequerieshelp": "එක් තල ප්â€à¶»à·à·Šà¶± උපකà·à¶»",
+ "smw-createproperty-isproperty": "මෙය $1 වර්ගයේ වත්කමකි.",
+ "smw-paramdesc-category-delim": "පරිසීමකය",
+ "smw-paramdesc-category-userparam": "à·ƒà·à¶šà·’ල්ල වෙත අතහà·à¶» යà·à¶ºà·”තු පරà·à¶¸à·’තිය",
+ "smw-info-par-message": "පෙන්වීමට ඇති පණිවුඩය.",
+ "prefs-smw": "අර්ථ විචà·à¶» මà·à¶°à·Šâ€à¶ºà·€à·’කිය",
+ "prefs-ask-options": "අර්ථ විචà·à¶» සෙවුම් විකල්ප",
+ "smw-ui-tooltip-title-property": "ගුණà·à¶‚ගය",
+ "smw-ui-tooltip-title-quantity": "ප්â€à¶»à¶¸à·à¶«à¶º",
+ "smw-ui-tooltip-title-info": "තොරතුරු",
+ "smw-ui-tooltip-title-service": "සේව෠සබà·à¶³à·’",
+ "smw-ui-tooltip-title-warning": "දà·à·‚ය",
+ "smw-ui-tooltip-title-parameter": "පරà·à¶¸à·’තිය",
+ "smw-ui-tooltip-title-event": "සිදුවීම",
+ "smw-ui-tooltip-title-note": "සටහන",
+ "smw-ui-tooltip-title-legend": "ප්â€à¶»à¶¶à¶±à·Šà¶°à¶º",
+ "smw_unknowntype": "මෙම වත්කමේ වර්ගය වලංගු නොවේ",
+ "smw_concept_header": "\"$1\" සංකල්පයේ පිටු",
+ "smw-livepreview-loading": "බ෠ගà·à¶±à·™à¶¸à·’න්…",
+ "smw-sp-searchbyproperty-description": "මෙම පිටුව [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces browsing interface] නà·à¶¸à·’ක අගය හ෠හිමිකම් පà·à·„à·à¶¯à·’ලි කිරීම් සෙවුම් සදහ෠විස්තර ලබ෠දේ.\nතවත් පවතින අතුරුමුහුණත් සෙවුම් ඇතුළත් කරනව෠[[Special:PageProperty|page property search]], සහ [[Special:Ask|ask query builder]].",
+ "smw-sp-searchbyproperty-resultlist-header": "ප්â€à¶»à¶­à·’පල ලà·à¶ºà·’ස්තුව",
+ "smw-sp-searchbyproperty-nonvaluequery": "\"$1\" සදහ෠ඇති වටිනà·à¶šà¶¸à·Š ලà·à¶ºà·’ස්තුව පවරන ලදී.",
+ "smw-sp-searchbyproperty-valuequery": "\"$2\"ක අගයේ \"$1\" හිමිකම් ඇති පිටු ලà·à¶ºà·’ස්තුව විස්තර කරන ලදී."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sk.json
new file mode 100644
index 00000000..e781cc9e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sk.json
@@ -0,0 +1,183 @@
+{
+ "@metadata": {
+ "authors": [
+ "Helix84",
+ "ì•„ë¼",
+ "Kghbln"
+ ]
+ },
+ "smw-desc": "Sprístupnenie vašej wiki pre stroje ''aj'' ľudí ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentácia])",
+ "smw_viewasrdf": "RDF kanál",
+ "smw_finallistconjunct": " a",
+ "smw_factbox_head": "SkutoÄnosti o $1",
+ "smw_isspecprop": "Táto vlastnosť je špeciálna vlastnosť na tejto wiki.",
+ "smw_concept_description": "Popis pojmu „$1â€",
+ "smw_no_concept_namespace": "Pojmy je možné definovať iba na stránkach v mennom priestore Concept:",
+ "smw_multiple_concepts": "Každý pojem môže mať iba jednu definíciu pojmu.",
+ "smw_concept_cache_miss": "Pojem „$1†nemožno momentálne použiÅ¥, pretože konfigurácia wiki vyžaduje, aby sa vypoÄítal, keÄ wiki nebude pripojená. Ak problém po urÄitej dobe nezmizne, požiadajte správcu, aby tento pojem sprístupnil.",
+ "smw_noinvannot": "Hodnoty nemožno priradiť inverzným vlastnostiam.",
+ "smw_baduri": "PrepáÄte, URI z rozsahu \"$1\" na tomto mieste nie sú dostupné.",
+ "smw_csv_link": "CSV",
+ "smw_printername_count": "Výsledky poÄítania",
+ "smw_printername_csv": "export CSV",
+ "smw_printername_debug": "Ladiaca požiadavka (pre expertov)",
+ "smw_printername_embedded": "Vložiť obsah stránky",
+ "smw_printername_json": "export JSON",
+ "smw_printername_list": "Zoznam",
+ "smw_printername_ol": "Vymenovaný typ",
+ "smw_printername_ul": "Rozpoložkovanie",
+ "smw_printername_table": "Tabuľka",
+ "smw_printername_broadtable": "Široká tabuľka",
+ "smw_printername_template": "Šablóna",
+ "smw-paramdesc-limit": "Maximálny poÄet vrátených výsledov",
+ "smw-paramdesc-headers": "ZobrazovaÅ¥ hlaviÄky/názvy vlastností",
+ "smw-paramdesc-mainlabel": "OznaÄenie názvu hlavnej stránky",
+ "smw-paramdesc-link": "Zobrazovať hodnoty ako odkazy",
+ "smw-paramdesc-intro": "Text, ktorý sa má zobrazovať pred výsledkami požiadavky (nepovinnné)",
+ "smw-paramdesc-outro": "Text, ktorý sa má zobrazovať po výsledkoch požiadavky (nepovinnné)",
+ "smw-paramdesc-default": "Text, ktorý sa má zobrazovať ak požiadavka nevráti žiadne výsledky",
+ "smw-paramdesc-sep": "OddeľovaÄ hodnôt",
+ "smw-paramdesc-template": "Názov Å¡ablóny pomocou ktorej zobrazovaÅ¥ výtlaÄky",
+ "smw-paramdesc-columns": "PoÄet stĺpcov v ktorých zobrazovaÅ¥ výsledky (predvolené $1)",
+ "smw-paramdesc-embedformat": "ZnaÄka HTML na definíciu nadpisov",
+ "smw-paramdesc-embedonly": "Nezobrazovať nadpisy",
+ "smw-paramdesc-searchlabel": "Text v odkaze na výsledky",
+ "smw_iq_disabled": "PrepáÄte. Inline queries have been disabled for this wiki.",
+ "smw_iq_moreresults": "&hellip; ÄalÅ¡ie výsledky",
+ "smw_parseerror": "Zadaná hodnota nebola pochopená.",
+ "smw_notitle": "„$1“ nie je možné na tejto wiki použiť ako názov stránky.",
+ "smw_wrong_namespace": "Sú tu povolené iba stránky v mennom priestore „$1â€.",
+ "smw_manytypes": "Pre atribút bol definovaný viac ako jeden typ.",
+ "smw_emptystring": "Prázdne reťazcie nie sú akceptované.",
+ "smw_notinenum": "„$1“ nie je v zozname možných hodnôt ($2) tejto vlastnosti.",
+ "smw_noboolean": "\"$1\" nebolo rozpoznané ako platná hodnota typy boolean (áno/nie).",
+ "smw_true_words": "áno,true",
+ "smw_false_words": "nie,false",
+ "smw_nofloat": "\"$1\" nie je Äíslo s plávajúcou desatinnou Äiarkou.",
+ "smw_infinite": "Čísla také veľké ako „$1“ nie sú podporované.",
+ "smw_nodatetime": "Nevedel som interpretovať dátum \"$1\".",
+ "smw_toomanyclosing": "Zdá sa, že požiadavka obsahuje príliš mnoho výskytov „$1“.",
+ "smw_noclosingbrackets": "Niektoré použitie „<nowiki>[[</nowiki>†vo vaÅ¡ej požiadavke nebolo ukonÄené zodpovedajúcim „]]â€.",
+ "smw_misplacedsymbol": "Symbol „$1“ bol použitý na mieste, kde nemá význam.",
+ "smw_unexpectedpart": "ÄŒasÅ¥ požiadavky „$1“ nebola pochopená.\nVýsledky nemusia byÅ¥ podľa oÄakávaní.",
+ "smw_emptysubquery": "Niektorá subpožiadavka nemá platný stav.",
+ "smw_misplacedsubquery": "Niektorá subpožiadavka bola použitá na mieste, kde nie sú povolené subpožiadavky.",
+ "smw_valuesubquery": "Podpožiadavky nie sú podporované pre hodnoty vlastnosti „$1“.",
+ "smw_badqueryatom": "Niektorá ÄasÅ¥ „<nowiki>[[…]]</nowiki>“ nebola pochopená.",
+ "smw_propvalueproblem": "Hodnota vlastnosti „$1“ nebola pochopená.",
+ "smw_noqueryfeature": "Táto wiki nepodporuje istú ÄasÅ¥ požiadavky a jej ÄasÅ¥ bola ignorovaná ($1).",
+ "smw_noconjunctions": "Táto wiki nepodporuje konjunkcie v požiadavkách a ÄasÅ¥ požiadavky bola ignorovaná ($1).",
+ "smw_nodisjunctions": "Disjunkcie nie sú v požiadavkách na tejto wiki podporované a ÄasÅ¥ požiadavky bola ignorovaná ($1).",
+ "smw_querytoolarge": "Nasledovné podmienky požiadavky nebudú zohľadnené z dôvodu obmedzení tejto wiki na dĺžku alebo hĺbku požiadavky: $1.",
+ "smw_notemplategiven": "Aby tento formát požiadavky fungoval, poskytnite prosím hodnotu parametra „templateâ€.",
+ "smw_type_header": "Vlastnosti typu „$1“",
+ "smw_typearticlecount": "{{PLURAL:$1|Zobrazuje sa $1 vlastnosť|Zobrazujú sa $1 vlastnosti|Zobrazuje sa $1 vlastností}} tohto typu.",
+ "smw_attribute_header": "Stránky používajúce vlastnosť „$1“",
+ "smw_attributearticlecount": "Zobrazuje sa $1 {{PLURAL:$1|stránka|stránky|stránok}}, ktoré používajú túto vlastnosť.",
+ "smw_subproperty_header": "Podvlastnosti",
+ "smw-subpropertylist-count": "Táto vlastnosť má {{PLURAL:|nasledovnú podvlastnosť|nasledovné $1 podvlastnosti|nasledovných $1 podvlastností}}:",
+ "exportrdf": "Exportovať stránky do RDF",
+ "smw_exportrdf_docu": "Táto stránka vám umožňuje exportovaÅ¥ Äasti stránok do formátu RDF. Po zadaní názvov stránok do spodného textového poľa, jeden názov na riadok, môžete exportovaÅ¥ stránky.",
+ "smw_exportrdf_recursive": "Rekurzívne exportovať všetky súvisiace stránky. Pozor, výsledok môže byť veľmi veľký!",
+ "smw_exportrdf_backlinks": "Tieť exportovať všetky stránky, ktoré odkazujú na exportované stránky. Vytvorí prehliadateľné RDF.",
+ "smw_exportrdf_lastdate": "NeexportovaÅ¥ stránky, ktoré neboli zmenené od zadaného Äasu.",
+ "smw_exportrdf_submit": "Export",
+ "uriresolver": "PrekladaÄ URI",
+ "properties": "Vlastnosti",
+ "smw_properties_docu": "Na tejto wiki sa používajú nasledovné vlastnosti.",
+ "smw_property_template": "$1 typu $2 ($3)",
+ "smw_propertylackspage": "Všetky vlastnosti by mali mať popisnú stránku!",
+ "smw_propertylackstype": "Pre túto vlastnosť nebol uvedený žiadny typ (nateraz sa predpokladá typ $1).",
+ "smw_propertyhardlyused": "Táto vlastnosť sa na wiki takmer nepoužíva!",
+ "unusedproperties": "Nepoužité vlastnosti",
+ "smw_unusedproperties_docu": "Nasledovné vlastnosti existujú napriek tomu, že ich nevyužíva žiadna iná stránka.",
+ "smw_unusedproperty_template": "$1 typu $2",
+ "wantedproperties": "Žiadané vlastnosti",
+ "smw_wantedproperties_docu": "Nasledovné vlastnosti, ktoré existujú na tejto wiki, zatiaľ nemajú popisnú stránku.",
+ "smw_wantedproperty_template": "$1 (použité {{PLURAL:$2|raz|$2-krát}})",
+ "smw_purge": "Obnoviť",
+ "types": "Typy",
+ "smw_types_docu": "Nasleduje zoznam vÅ¡etkých údajových typov, ktoré je možné priradiÅ¥ atribútom. Každý údajový typ má stránku, kde je možné poskytnúť dodatoÄné informácie.",
+
+
+ "smw_uri_doc": "URI resolver sa stará o implementáciu [$1 W3C TAG hľadanie na httpRange-14].\nStará sa o to, aby sa ľudia nestali webstránkami.",
+ "ask": "Sémantické vyhľadávanie",
+ "smw_ask_sortby": "Zoradiť podľa stĺpca",
+ "smw_ask_ascorder": "Vzostupne",
+ "smw_ask_descorder": "Zostupne",
+ "smw_ask_submit": "Nájdi výsledky",
+ "smw_ask_editquery": "Upraviť požiadavku",
+ "smw_add_sortcondition": "[Pridať podmienku na zoradenie]",
+ "smw_ask_hidequery": "Skryť požiadavku",
+ "smw_ask_help": "Pomocník pre požiadavky",
+ "smw_ask_queryhead": "Požiadavka",
+ "smw_ask_printhead": "Ďalšie zobrazované údaje",
+ "smw_ask_printdesc": "(pridajte jednu vlastnosť na riadok)",
+ "smw_ask_format_as": "Formátovať ako:",
+ "smw_ask_defaultformat": "Å¡tandardne",
+ "smw_ask_otheroptions": "Ďalšie možnosti",
+ "smw_ask_show_embed": "Zobraziť kód embed",
+ "smw_ask_hide_embed": "Skryť kód embed",
+ "smw_ask_embed_instr": "Ak chcete vložiť túto požiadavku do wiki stránky, použite dolu uvedený kód.",
+ "searchbyproperty": "Hľadať podľa hodnoty atribútu",
+ "smw_sbv_docu": "HľadaÅ¥ na wiki Älánok, ktorý má atribút s istou hodnotou.",
+ "smw_sbv_novalue": "Nebola poskytnutá hodnota. Prosím, poskytnite ju vo formulári alebo zobrazte všetky atribúty typu $1",
+ "smw_sbv_displayresult": "Zoznam vÅ¡etkých Älánkov, ktoré majú atribút $1 $2.",
+ "smw_sbv_displayresultfuzzy": "Zoznam vÅ¡etkých stránok, ktoré majú vlastnosÅ¥ „$1†s hodnotou „$2â€.\nKeÄže výsledkov bolo len niekoľko, zobrazujú sa aj blízke hodnoty.",
+ "smw_sbv_property": "Atribút:",
+ "smw_sbv_value": "Hodnota:",
+ "smw_sbv_submit": "Hľadať výsledky",
+ "browse": "Prehliadať wiki",
+ "smw_browselink": "Prehliadať vlastnosti",
+ "smw_browse_article": "Zadajte názov stránky, od ktorej chcete zaÄaÅ¥ prehliadaÅ¥.",
+ "smw_browse_go": "Vykonať",
+ "smw_browse_show_incoming": "zobraziť vlastnosti, ktoré sem odkazujú",
+ "smw_browse_hide_incoming": "skryť vlastnosti, ktoré sem odkazujú",
+ "smw_browse_no_outgoing": "Táto stránka neobsahuje vlastnosti.",
+ "smw_browse_no_incoming": "Žiadne vlastnosti neodkazujú na túto stránku.",
+ "smw_inverse_label_default": "$1 z",
+ "smw_inverse_label_property": "OznaÄenie inverznej vlastnosti",
+ "pageproperty": "Hľadanie vlastností stránky",
+ "smw_pp_docu": "Hľadanie všetkých výplní vlastnosti na zadanej stránke.\nProsím, zadajte stránku aj vlastnosť.",
+ "smw_pp_from": "Od stránky",
+ "smw_pp_type": "Vlastnosť",
+ "smw_pp_submit": "Výsledky hľadania",
+ "smw_result_prev": "Späť",
+ "smw_result_next": "ÄŽalej",
+ "smw_result_results": "Výsledky",
+ "smw_result_noresults": "PrepáÄte, žiadne výsledky.",
+ "smwadmin": "Správcovské funkcie Semantic MediaWiki",
+ "smw_smwadmin_setupsuccess": "Úložisko bolo úspešne nastavené.",
+ "smw_smwadmin_return": "Späť na $1",
+ "smw_smwadmin_updatestarted": "ZaÄal sa nový proces aktualizácie sémantických údajov.\nVÅ¡etky uložené údaje budú znova zostavené alebo opravené tam, kde je to potrebné.\nNa tejto Å¡peciálnej stránke môžete sledovaÅ¥ priebeh aktualizácie.",
+ "smw_smwadmin_updatenotstarted": "Už beží proces aktualizácie.\nNespustí sa Äalší.",
+ "smw_smwadmin_updatestopped": "Všetky bežiace procesy aktualizácie boli zastavené.",
+ "smw_smwadmin_updatenotstopped": "Bežiaci proces aktualizácie môžete zastaviÅ¥ oznaÄením zaÅ¡krtávacieho poľa, Äím potvrdíte, že ste si naozaj istý.",
+ "smw_smwadmin_docu": "Táto Å¡peciálna stránka vám pomôže poÄas inÅ¡talácie a aktualizácie <a href=\"http://semantic-mediawiki.org\">Semantic MediaWiki</a>.\nPamätajte na zálohovanie dôležitých údajov pred vykonávaním správcovských funkcií.",
+ "smw_smwadmin_db": "Inštalácia a aktualizácia databázy",
+ "smw_smwadmin_dbdocu": "Semantic MediaWiki vyžaduje niektoré rozšírenia databázy MediaWiki, aby molo ukladaÅ¥ sémantické údaje.\nFunkcia nižšie zabezpeÄuje správne nastavenie databázy.\nZmeny vykonané v tomto kroku neovplyvňujú zvyÅ¡ok databázy MediaWiki a ak je to potrebné, je ich možno jednoducho vrátiÅ¥.\nTúto funkcia nastavenia možno vykonaÅ¥ viacnásobne bez akejkoľvek ujmy, ale je potrebná iba raz poÄas inÅ¡talácie Äi aktualizácie.",
+ "smw_smwadmin_permissionswarn": "Ak táto operácia zlyhá na chybách SQL, používateľ databázy, ktorého využíva vaÅ¡a wiki (pozrite sa do LocalSettings.php) pravdepodobne nemá dostatoÄné privilégiá.\nBuÄ udeľte tomuto používateľovi ÄalÅ¡ie privilégiá na vytváranie a mazanie tabuliek, doÄasne zadajte prihlasovacie údaje používateľa root databázy do LocalSettings.php alebo použite skript na údržbu t>setupStore.php</code>, ktorý dokáže využiÅ¥ oprávnenia z AdminSettings.php.",
+ "smw_smwadmin_dbbutton": "Inicializovať alebo aktualizovať tabuľky",
+ "smw_smwadmin_announce": "Oznámiť vašu wiki",
+ "smw_smwadmin_datarefresh": "Oprava a aktualizácia dát",
+ "smw_smwadmin_datarefreshdocu": "Je možné obnoviÅ¥ vÅ¡etky údaje Semantic MediaWiki na základe aktuálneho obsahu wiki.\nTo sa hodí na opravu poÅ¡kodených údajov alebo obnovu údajov ak sa pri aktualizácii softvéru zmenil vnútorný formát ukladania údajov.\nAktualizáciu je možné spustiÅ¥ na Å¡peciálnej stránke a nebude dokoÄená okamžite.\nTu sa zobrazuje priebeh aktualizácie a môžete tu spustiÅ¥ alebo zastaviÅ¥ aktualizácie (ak túto funkciu správca nevypol).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Aktualizácia už prebieha.</strong>\nJe normálne, že aktualizácie prebieha pomaly, pretože obnovuje údaje iba po malých kúskoch naraz, keÄ používateľ zobrazí stránku wiki.\nRýchlejÅ¡ie dokonÄenie tejto aktualizácie môžete dosiahnuÅ¥ vyvolaním údržbového skriptu MediaWiki <code>runJobs.php</code> (použite voľbu <code>--maxjobs 1000</code> na obmedzenie poÄtu aktualizácií v jednej dávke).\nOdhadovaný priebeh aktualizácie:",
+ "smw_smwadmin_datarefreshbutton": "Spustiť aktualizáciu údajov",
+ "smw_smwadmin_datarefreshstop": "Zastaviť túto aktualizáciu",
+ "smw_smwadmin_datarefreshstopconfirm": "Ãno, som si istý.",
+ "smw_smwadmin_support": "Ako získať podporu",
+ "smw_smwadmin_supportdocu": "V prípade problémov vám môžu pomôcÅ¥ rozliÄné zdroje:",
+ "smw_smwadmin_installfile": "Ak budete maÅ¥ s inÅ¡taláciou problém, zaÄnite kontrolou smerníc <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">v súbore INSTALL</a>.",
+ "smw_smwadmin_smwhomepage": "Úplná používateľská dokumentácia Semantic MediaWiki sa nachádza na <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw_smwadmin_mediazilla": "Chyby môžete oznamovať v systéme <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw_smwadmin_questions": "Ak máte ÄalÅ¡ie otázky alebo návrhy, zapojte sa do diskusie <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">na používateľskom fóre Semantic MediaWiki</a>.",
+ "smw_adminlinks_datastructure": "Štruktúra údajov",
+ "smw_adminlinks_displayingdata": "Zobrazenie údajov",
+ "smw_adminlinks_inlinequerieshelp": "Pomoc k inline požiadavkám",
+ "smw-createproperty-isproperty": "Toto je vlastnosť typu $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Povolená hodnota|Povolené hodnoty}} tejto vlastnosti {{PLURAL:$1|je|sú}}:",
+ "smw_unknowntype": "Pre atribút je definovaný nepodporovaný typ \"$1\".",
+ "smw_concept_header": "Návrh „$1â€",
+ "smw_conceptarticlecount": "Zobrazuje sa $1 {{PLURAL:$1|stránka patriaca|stránky patriace|stránok patriacich}} tomuto návrhu.",
+ "smw-livepreview-loading": "NaÄítava sa…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sl.json
new file mode 100644
index 00000000..fa3c6455
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sl.json
@@ -0,0 +1,133 @@
+{
+ "@metadata": {
+ "authors": [
+ "Dbc334",
+ "Eleassar",
+ "Lesko987"
+ ]
+ },
+ "smw-desc": "Naredi vaš wiki dostopnejši – za stroje ''in'' ljudi ([https://www.semantic-mediawiki.org/wiki/Help:User_manual spletna dokumentacija])",
+ "smw_viewasrdf": "Vir RDF",
+ "smw_finallistconjunct": "in",
+ "smw_factbox_head": "Dejstva o $1",
+ "smw_isspecprop": "Ta lastnost je posebna lastnost za ta wiki.",
+ "smw_concept_description": "Opis zasnove »$1«",
+ "version-semantic": "SemantiÄna razÅ¡iritev",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Å tevilo",
+ "smw_printername_csv": "Izvozi v CSV",
+ "smw_printername_dsv": "Izvozi v DSV",
+ "smw_printername_debug": "Debug poizvedba (za strokovnjake)",
+ "smw_printername_embedded": "Vstavljena vsebina strani",
+ "smw_printername_json": "Izvoz v JSON",
+ "smw_printername_list": "Seznam",
+ "smw_printername_ol": "Naštevanje",
+ "smw_printername_ul": "Podroben seznam",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Å iroka tabela",
+ "smw_printername_template": "Predloga",
+ "smw_printername_rdf": "Izvozi v RDF",
+ "smw_printername_category": "Kategorija",
+ "smw-paramdesc-limit": "NajveÄje Å¡tevilo rezultatov",
+ "smw-paramdesc-offset": "Index prvega rezultata",
+ "smw-paramdesc-headers": "Prikaži glavo tabele",
+ "smw-paramdesc-mainlabel": "Naslov glavne strani",
+ "smw-paramdesc-link": "Priaži vrednosti kot povezave",
+ "smw-paramdesc-intro": "Besedilo za prikaz pred rezultati poizvedbe, Äe obstajajo",
+ "smw-paramdesc-outro": "Besedilo za prikaz po rezultatih poizvedbe, Äe obstajajo",
+ "smw-paramdesc-default": "Besedilo za prikaz Äe ni rezultatov poizvedbe",
+ "smw-paramdesc-sep": "LoÄilo vrednosti",
+ "smw-paramdesc-embedonly": "Ne prikaži naslovov",
+ "smw-paramdesc-csv-sep": "Uporabljeno loÄilo",
+ "smw-paramdesc-dsv-separator": "Uporabljeno loÄilo",
+ "smw-paramdesc-dsv-filename": "Ime datoteke DSV",
+ "smw_iq_moreresults": "… nadaljnji rezultati",
+ "smw_emptystring": "Prazni nizi niso sprejemljivi.",
+ "smw_true_words": "true,t,yes,y,resniÄno,r,da,d",
+ "smw_false_words": "false,f,no,n,neresniÄno,nr,ne",
+ "smw_nofloat": "»$1« ni število.",
+ "smw_novalues": "DoloÄenih ni nobenih vrednosti.",
+ "smw_unexpectedpart": "Del poizvedbe \"$1\" ni bil razumljen.\nRezultati morda niso v pravilni.",
+ "smw_type_header": "Lastnosti tipa \"$1\"",
+ "smw_typearticlecount": "Prikazujem $1 {{PLURAL:$1|lastnost|lastnosti}} ki uporablja ta tip.",
+ "smw_attribute_header": "Strani, ki uporabljajo ljastnost \"$1\"",
+ "smw_attributearticlecount": "Prikazujem $1 {{PLURAL:$1|stran|strani}} ki uporabljajo to lastnost.",
+ "smw_subproperty_header": "Podlastnosti",
+ "smw_subpropertyarticlecount": "Ta lastnost ima {{PLURAL:$1|naslednjo $1 podlastnost|naslednji $1 podlastnosti|naslednje $1 podlastnosti|naslednjih $1 podlastnosti}}:",
+ "specialpages-group-smw_group": "SemantiÄni MediaWiki",
+ "exportrdf": "Izvoz strani v RDF",
+ "smw_exportrdf_docu": "Ta stran vam omogoÄa, da dobite podatke iz strani v formatu RDF.\nZa izvoz strani vpiÅ¡ite njihove naslove v spodnjem oknu, po en naslov na vrstico.",
+ "smw_exportrdf_recursive": "Rekurzivno izvozite vse povezane strani.\nUpoštevajte, da lahko velik rezultat!",
+ "smw_exportrdf_backlinks": "Izvozi tudi vse strani ki se sklicujejo na strani izvoza.\nUstvari RDF za brskanje.",
+ "smw_exportrdf_submit": "Izvozi",
+ "properties": "Lastnosti",
+ "smw_properties_docu": "Naslednje lastnosti se uporabljajo na tem wikiju.",
+ "smw_property_template": "$1 vrste $2 ($3 {{PLURAL:$3|uporaba|uporabi|uporabe|uporab}})",
+ "smw_propertyhardlyused": "Ta lastnost je komaj uporabljena v wikiju!",
+ "unusedproperties": "Neuporabljene lastnosti",
+ "smw_unusedproperties_docu": "Naslednje lastnosti obstajajo, Äeprav jih ne uporablja nobena druga stran.",
+ "smw_unusedproperty_template": "$1 tipa $2",
+ "wantedproperties": "Želene lastnosti",
+ "smw_wantedproperties_docu": "Naslednje lastnosti se uporabljajo v wikiju, vendar Å¡e nimajo strani, ki jih opisujejo.",
+ "smw_wantedproperty_template": "$1 ( $2 {{PLURAL:$2|uporaba|uporabi|uporabe|uporab}})",
+ "smw_purge": "Osveži",
+ "types": "Tipi",
+ "smw_types_docu": "V nadaljevanju sledi [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes seznam] vseh podatkovnih tipov, ki so lahko doloÄeni za lastnosti.",
+
+
+ "ask": "SemantiÄno iskanje",
+ "smw_ask_sortby": "Razvrsti po stolpcu (neobvezno)",
+ "smw_ask_ascorder": "NaraÅ¡ÄajoÄe",
+ "smw_ask_descorder": "PadajoÄe",
+ "smw_ask_submit": "PoiÅ¡Äi",
+ "smw_ask_editquery": "Uredi poizvedbo",
+ "smw_add_sortcondition": "[Dodaj pravilo razvrÅ¡Äanja]",
+ "smw_ask_hidequery": "Skrij poizvedbo",
+ "smw_ask_help": "PomoÄ za poizvedbo",
+ "smw_ask_queryhead": "Poizvedba",
+ "smw_ask_printhead": "Dodatni podatki za prikaz",
+ "smw_ask_printdesc": "(dodaj po eno lastnost v vrstico)",
+ "smw_ask_format_as": "Oblikuj kot:",
+ "smw_ask_defaultformat": "privzeto",
+ "smw_ask_otheroptions": "Ostale nastavitve",
+ "smw_ask_show_embed": "Prikaži besedilo za vstavljanje",
+ "smw_ask_hide_embed": "Skrij besedilo za vstavljanje",
+ "smw_ask_embed_instr": "Za vstavljanje te poizvedbe na wiki stran uporabite sledeÄe besedilo.",
+ "searchbyproperty": "Iskanje po lastnosti",
+ "smw_sbv_docu": "IÅ¡Äi vse strani, ki imajo doloÄeno lastnost in vrednost.",
+ "smw_sbv_novalue": "Vnesite pravilno vrednost za lastnost ali pa si oglejte vse vrednosti lastnosti \"$1\".",
+ "smw_sbv_displayresult": "Seznam vseh strani, ki imajo lastnost \"$1\" z vrednostjo \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Seznam vseh strani, ki imajo lastnost \"$1\" z vrednostjo \"$2\".\nKer je bilo le malo rezultatov, so prikazane strani s približnim ujemanjem.",
+ "smw_sbv_property": "Lastnost:",
+ "smw_sbv_value": "Vrednost:",
+ "smw_sbv_submit": "PoiÅ¡Äi",
+ "browse": "Raziskuj wiki",
+ "smw_browselink": "Raziskuj lastnosti",
+ "smw_browse_article": "VpiÅ¡ite ime strani, za zaÄntek raziskovanja.",
+ "smw_browse_go": "Pojdi",
+ "smw_browse_show_incoming": "prikaži lastnosti, ki se povezujejo sem",
+ "smw_browse_hide_incoming": "skrij lastnosti, ki se povezujejo sem",
+ "smw_browse_no_outgoing": "Ta stran nima lastnosti.",
+ "smw_browse_no_incoming": "Nobenie lastnosti niso povezave na to stran.",
+ "smw_inverse_label_default": "$1 od",
+ "smw_pp_from": "S strani",
+ "smw_pp_type": "Lastnost",
+ "smw_pp_submit": "PoiÅ¡Äi",
+ "smw_result_prev": "Nazaj",
+ "smw_result_next": "Naprej",
+ "smw_result_results": "Rezultati",
+ "smw_result_noresults": "Ni zadetkov.",
+ "smw_smwadmin_return": "Vrnitev na $1",
+ "smw_smwadmin_docu": "Ta posebna stran vam pomaga pri namestitvi in nadgradnji <a href=\"https://www.semantic-mediawiki.org\">SemantiÄnega MediaWiki</a>.\nPred izvajanje administrativnih dejanj ustvarite varnostno kopijo uporabnih podatkov.",
+ "smw_smwadmin_datarefresh": "Nadgraditev in popravilo podatkov",
+ "smw_smwadmin_datarefreshbutton": "ZaÄni posodabljati podatke",
+ "smw_smwadmin_datarefreshstop": "Ustavi to posodobitev",
+ "smw_smwadmin_datarefreshstopconfirm": "Da, {{GENDER:$1|prepriÄan|prepriÄana}} sem.",
+ "smw_smwadmin_smwhomepage": "Celotna uporabniška dokumentacija za Semantic MediaWiki je na razpolago na naslovu <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw_smwadmin_mediazilla": "HroÅ¡Äe lahko prijavite na <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHubu</a>.",
+ "smw_adminlinks_datastructure": "Zgradba podatkov",
+ "smw_adminlinks_displayingdata": "Prikaz podatkov",
+ "prefs-smw": "SemantiÄni MediaWiki",
+ "smw_concept_header": "Strani zasnove »$1«",
+ "smw-livepreview-loading": "Nalaganje ..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sli.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sli.json
new file mode 100644
index 00000000..4a28ed4d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sli.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Loada…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sma.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sma.json
new file mode 100644
index 00000000..fd8faad1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sma.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Leedtedh…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sq.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sq.json
new file mode 100644
index 00000000..58f18413
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sq.json
@@ -0,0 +1,23 @@
+{
+ "@metadata": {
+ "authors": [
+ "GretaDoci",
+ "Liridon",
+ "Edlira"
+ ]
+ },
+ "smw_finallistconjunct": "dhe",
+ "smw_factbox_head": "Fakte rreth \"$1\"",
+ "smw_isspecprop": "Ky tipar është një tipar i veçantë në këtë wiki.",
+ "smw_concept_description": "Përshkrim i konceptit \"$1\"",
+ "smw_no_concept_namespace": "Konceptet mund të përcaktohen vetëm në faqet në namespace Concept:",
+ "smw_multiple_concepts": "Çdo faqe koncepti mund të ketë vetëm një përkufizim të konceptit.",
+ "smw_concept_cache_miss": "Koncepti \"$1\" nuk mund të përdoret në këtë moment, pasi konfigurimi i wiki kërkon që ai të llogaritet off-line.\nNëse problemi nuk do të iki pas një farë kohe, pyesni administratorin e faqes tuaj për ta vënë në dispozicion këtë koncept.",
+ "smw_noinvannot": "Vlerat nuk mund të caktohen për tiparet e anasjelltë.",
+ "smw_baduri": "URI të formës \"$1\" nuk lejohen.",
+ "smw_printername_csv": "Eksport CSV",
+ "smw_printername_dsv": "Eksport DSV",
+ "browse": "Shfletoni wiki",
+ "smw_smwadmin_mediazilla": "Bugs mund të raportohet për të <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/çështjet\">GitHub</a>.",
+ "smw-livepreview-loading": "Duke punuar…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sr-ec.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sr-ec.json
new file mode 100644
index 00000000..cc47df30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sr-ec.json
@@ -0,0 +1,343 @@
+{
+ "@metadata": {
+ "authors": [
+ "Milicevic01",
+ "Rancher",
+ "Sasa Stefanovic",
+ "Slaven Kosanovic",
+ "Жељко Тодоровић",
+ "Михајло Ðнђелковић",
+ "Сербијана",
+ "Kghbln",
+ "Obsuser",
+ "Srdjan m",
+ "Милан ЈелиÑавчић",
+ "Acamicamacaraca",
+ "Prevodim",
+ "BadDog",
+ "Zoranzoki21"
+ ]
+ },
+ "smw-desc": "Чини вики приÑтупачнијим — за машине „и†људе ([https://www.semantic-mediawiki.org/wiki/Help:User_manual документација на мрежи])",
+ "smw-title": "Семантички Медијавики",
+ "smw-upgrade-error-why-title": "Зашто видим ову грешку?",
+ "smw-upgrade-error-how-title": "Како да иÑправим ову грешку?",
+ "smw-semantics-not-enabled": "ФункционалноÑÑ‚ Семантичког MediaWiki-ја није омогућена за овај вики.",
+ "smw_viewasrdf": "RDF фид",
+ "smw_finallistconjunct": " и",
+ "smw-factbox-head": "… више о Ñтраници „$1â€",
+ "smw-factbox-facts": "Чињенице",
+ "smw_isspecprop": "Ово ÑвојÑтво је поÑебно на овом викију.",
+ "smw-concept-cache-header": "Употреба кеша",
+ "smw-concept-no-cache": "Ðије доÑтупан кеш.",
+ "smw_concept_description": "ÐžÐ¿Ð¸Ñ ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ‚Ð° „$1â€",
+ "smw_no_concept_namespace": "Концепти једино могу да Ñе дефинишу на Ñтраницама у именÑком проÑтору Concept:",
+ "smw_multiple_concepts": "Свака Ñтраница концепта Ñме да има Ñамо једну дефиницију концепта.",
+ "smw_concept_cache_miss": "Концепт \"$1\" Ñе тренутно не може кориÑтити, пошто конфигурација викија захтева да буде компутован офлајн.\nÐко проблем не неÑтане за неко време, затражите од админиÑтратора Ñајта да учини овај концепт доÑтупним.",
+ "smw_noinvannot": "Ðа обртна ÑвојÑтва не могу да Ñе доделе вредноÑти.",
+ "version-semantic": "Семантички додаци",
+ "smw_baduri": "URI-ји за облик „$1“ ниÑу дозвољени.",
+ "smw_csv_link": "CSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "Резултат бројања",
+ "smw_printername_csv": "CSV извоз",
+ "smw_printername_dsv": "DSV извоз",
+ "smw_printername_debug": "Упит за отклањање грешака (за Ñтручњаке)",
+ "smw_printername_embedded": "Угради Ñадржај Ñтранице",
+ "smw_printername_json": "JSON извоз",
+ "smw_printername_list": "ЛиÑта",
+ "smw_printername_plainlist": "Обична лиÑта",
+ "smw_printername_ol": "Ðабрајање",
+ "smw_printername_table": "Табела",
+ "smw_printername_broadtable": "Широка табела",
+ "smw_printername_template": "Шаблон",
+ "smw_printername_templatefile": "Датотека шаблона",
+ "smw_printername_rdf": "RDF извоз",
+ "smw_printername_category": "Категорија",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw-paramdesc-limit": "Ðајвећи број резултата који ће бити враћени",
+ "smw-paramdesc-mainlabel": "Етикета која ће Ñе кориÑтити за име главне Ñтранице",
+ "smw-paramdesc-link": "Прикажи вредноÑти у облику веза",
+ "smw-paramdesc-intro": "ТекÑÑ‚ који треба да Ñе прикаже пре резултата упита (у Ñлучају потребе)",
+ "smw-paramdesc-outro": "ТекÑÑ‚ који треба да Ñе прикаже иза резултата упита (у Ñлучају потребе)",
+ "smw-paramdesc-default": "ТекÑÑ‚ који треба да Ñе прикаже у Ñлучају да упит није дао резултате",
+ "smw-paramdesc-sep": "Раздвајач између резултата",
+ "smw-paramdesc-columns": "Број колона у којима ће Ñе приказати резултати",
+ "smw-paramdesc-embedonly": "Ðе приказуј заглавља",
+ "smw-paramdesc-searchlabel": "ТекÑÑ‚ везе Ñ Ñ€ÐµÐ·ÑƒÐ»Ñ‚Ð°Ñ‚Ð¸Ð¼Ð°",
+ "smw-paramdesc-template-arguments": "Подешавање иÑпоруке именованих параметара до шаблона",
+ "smw_iq_disabled": "Семантички упити Ñу онемогућени на овом викију.",
+ "smw_iq_moreresults": "... више резултата",
+ "smw_parseerror": "Дата вредноÑÑ‚ није Ñхваћена.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "\"$1\" Ñе не може кориÑтити за име Ñтранице на овом викију.",
+ "smw_wrong_namespace": "Само Ñтранице из именÑког проÑтора \"$1\" Ñу дозвољене овде.",
+ "smw_manytypes": "Више од једног типа је дефиниÑано за ову оÑобину.",
+ "smw_emptystring": "Празни Ñтрингови ниÑу прихватљиви.",
+ "smw_notinenum": "„$1†није на лиÑти ($2) [[Property:Allows value|могућих вредноÑти]] за ÑвојÑтво „$3â€.",
+ "smw_noboolean": "\"$1\" није препознат као Болова вредноÑÑ‚ (иÑтинито/неиÑтинито).",
+ "smw_true_words": "иÑтинито, и, да, д",
+ "smw_false_words": "неиÑтинито, н, не, н",
+ "smw_nofloat": "\"$1\" није број.",
+ "smw_infinite": "Бројеви дужине као \"$1\" ниÑу подржани.",
+ "smw_novalues": "Ðема назначене вредноÑти.",
+ "smw_nodatetime": "Формат датума \"$1\" није разумљив.",
+ "smw_toomanyclosing": "Изгледа да је превише Ñлучајева типа \"$1\" у упиту.",
+ "smw_noclosingbrackets": "Ðеке углаÑте заграде \"<nowiki>[[</nowiki>\" у вашем упиту, ниÑу затворене одговарајућим \"]]\".",
+ "smw_misplacedsymbol": "Симбол \"$1\" је иÑкоришћен на меÑту где није од кориÑти.",
+ "smw_unexpectedpart": "Део упита \"$1\" није Ñхваћен.\nРезултати могу бити неочекивани.",
+ "smw_emptysubquery": "Ðеки подупити не Ñадрже ваљане уÑлове.",
+ "smw_misplacedsubquery": "Ðеки подупити Ñу коришћени на меÑту где подупити ниÑу дозвољени.",
+ "smw_valuesubquery": "За вредноÑти оÑобине \"$1\", подупити ниÑу подржани.",
+ "smw_badqueryatom": "Један део \"<nowiki>[[…]]</nowiki>\" упита није Ñхваћен.",
+ "smw_propvalueproblem": "ВредноÑÑ‚ за оÑобину \"$1\" није Ñхваћена.",
+ "smw_noqueryfeature": "Ðеки делови овог упита ниÑу подржани на овом викију, те је део упита изоÑтављен ($1).",
+ "smw_noconjunctions": "Конјукције у упитима ниÑу подржане на овом викију, те је део упита изоÑтављен ($1).",
+ "smw_nodisjunctions": "ДиÑјункције у упитима ниÑу подржане на овом викију, те је део упита изоÑтављен ($1).",
+ "smw_querytoolarge": "Ðије могуће разматрати {{PLURAL:$2|Ñледећи уÑлов упита $2|Ñледеће уÑлове упита $2}} због ограничења викија на величину и дубину упита: <code>$1</code>.",
+ "smw_notemplategiven": "Понудите вредноÑÑ‚ за параметар â€ÑˆÐ°Ð±Ð»Ð¾Ð½â€, како би овај формат упита дао резултат.",
+ "smw_type_header": "ОÑобине типа \"$1\"",
+ "smw_typearticlecount": "Приказ $1 {{PLURAL:$1|оÑобине|оÑобина}} за овај тип.",
+ "smw_attribute_header": "Странице које кориÑте оÑобину \"$1\"",
+ "smw_attributearticlecount": "Приказ $1 {{PLURAL:$1|Ñтранице|Ñтраница}} које кориÑте ову оÑобину.",
+ "smw-propertylist-subproperty-header": "ПодÑвојÑтва",
+ "smw-propertylist-redirect-header": "Синоними",
+ "specialpages-group-smw_group": "Семантички Медијавики",
+ "exportrdf": "Извоз Ñтраница у RDF",
+ "smw_exportrdf_docu": "Ова Ñтраница омогућава преузимање података Ñ Ð½ÐµÐºÐµ Ñтранице у формату RDF.\nДа биÑте извезли Ñтранице, унеÑите наÑлове у кућицу иÑпод (по један наÑлов у Ñваком реду).",
+ "smw_exportrdf_recursive": "Рекурзивно извези Ñве повезане Ñтранице.\nОбратите пажњу да резултат може бити веома обиман!",
+ "smw_exportrdf_backlinks": "Такође извези Ñве Ñтранице које вежу на већ извезене Ñтранице.\nГенерише RDF који је могуће прегледати.",
+ "smw_exportrdf_lastdate": "Ðе извози Ñтранице које ниÑу мењане од датог момента.",
+ "smw_exportrdf_submit": "Извоз",
+ "uriresolver": "URI резолвер",
+ "properties": "СвојÑтва",
+ "smw_properties_docu": "Следеће оÑобине Ñе кориÑте на викију.",
+ "smw_property_template": "$1 типа $2 ($3 {{PLURAL:$3|употреба|употребе|употреба}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Све оÑобине требају имати опиÑну Ñтраницу!",
+ "smw_propertylackstype": "Ðема дефиниÑаног типа за ову оÑобину (за Ñада уÑвајам тип $1).",
+ "smw_propertyhardlyused": "Ова оÑобина једва да Ñе кориÑти на овом викију!",
+ "smw-special-property-searchform-options": "Опције",
+ "smw-special-wantedproperties-filter-label": "Филтер:",
+ "smw-special-wantedproperties-filter-none": "Ðишта",
+ "smw-special-wantedproperties-filter-unapproved": "Ðеодобрено",
+ "concepts": "Концепти",
+ "smw-special-concept-header": "ЛиÑта концепата",
+ "unusedproperties": "ÐеиÑкоришћене оÑобине",
+ "smw-unusedproperties-docu": "Следеће оÑобине поÑтоје иако нема других Ñтраница које их кориÑте.",
+ "smw-unusedproperty-template": "$1 типа $2",
+ "wantedproperties": "Тражене оÑобине",
+ "smw-wantedproperties-docu": "Следеће оÑобине Ñе кориÑте на викију, али још увек немају Ñвоју Ñтраницу Ñа опиÑом.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|употреба|употреба}})",
+ "smw_purge": "ОÑвежи",
+ "types": "Типови",
+ "smw_types_docu": "ЛиÑта [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes доÑтупних типова података] Ñа Ñваким [https://www.semantic-mediawiki.org/wiki/Help:Datatype типом] предÑтавља јединÑтвен Ñкуп атрибута који опиÑују вредноÑÑ‚ у ÑмиÑлу карактериÑтика чување и излагање које Ñу наÑледне по додељеним ÑвојÑтвима.",
+ "smw-statistics": "Семантичка ÑтатиÑтика",
+ "smw-statistics-query-size": "Величина упита",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Концепт|Концепти}}",
+ "ask": "Семантичка претрага",
+ "smw_ask_sortby": "Поређај у колоне (опционално)",
+ "smw_ask_ascorder": "РаÑтући",
+ "smw_ask_descorder": "Опадајући",
+ "smw-ask-order-rand": "Случајно",
+ "smw_ask_submit": "Пронађи резултате",
+ "smw_ask_editquery": "Уреди упит",
+ "smw_add_sortcondition": "[Додај уÑлове Ñортирања]",
+ "smw-ask-sort-add-action": "Додај уÑлове Ñортирања",
+ "smw_ask_hidequery": "Сакриј упит (компактни приказ)",
+ "smw_ask_help": "Упит у Ñтранице помоћи",
+ "smw_ask_queryhead": "УÑлов",
+ "smw_ask_printhead": "Избор штампе",
+ "smw_ask_printdesc": "(додај једно име оÑобине по линији)",
+ "smw_ask_format_as": "Обликуј као:",
+ "smw_ask_defaultformat": "подразумевано",
+ "smw_ask_otheroptions": "Друге опције",
+ "smw_ask_show_embed": "Прикажи уграђени код",
+ "smw_ask_hide_embed": "Сакриј уградбени код",
+ "smw_ask_embed_instr": "За непоÑредно гнеждење овог упита у Ñтраницу викија, кориÑти доњи код.",
+ "smw-ask-delete": "Уклони",
+ "smw-ask-sorting": "Сортирање",
+ "smw-ask-options": "Опције",
+ "smw-ask-options-sort": "Опције Ñортирања",
+ "smw-ask-format-options": "Формат и опције",
+ "smw-ask-parameters": "Параметри",
+ "smw-ask-search": "Претрага",
+ "smw-ask-result": "Резултат",
+ "smw-ask-empty": "ОчиÑти Ñве уноÑе",
+ "smw-ask-format": "Формат",
+ "smw-ask-query-search-info": "Ðа упит <code><nowiki>$1</nowiki></code> одговорио је {{PLURAL:$3|1=<code>$2</code> (из кеша)|<code>$2</code> (из кеша)|<code>$2</code>}} за $4 {{PLURAL:$4|Ñекунд|Ñекунда|Ñекунди}}.",
+ "searchbyproperty": "Претрага по ÑвојÑтвима",
+ "smw_sbv_docu": "Претражи Ñве Ñтранице које поÑедују дату оÑобину и вредноÑÑ‚.",
+ "smw_sbv_novalue": "УнеÑите ваљану вредноÑÑ‚ за ову оÑобину, или погледајте Ñве вредноÑти оÑобине за \"$1\".",
+ "smw_sbv_displayresult": "ЛиÑта Ñвих Ñтраница које Ñадрже ÑвојÑтво „$1†Ñа вредношћу „$2â€",
+ "smw_sbv_displayresultfuzzy": "ЛиÑта Ñвих Ñтраница које Ñадрже ÑвојÑтво „$1†Ñа вредношћу „$2â€.\nПошто је пронађено Ñамо неколико резултата, такође Ñу приказане приближне вредноÑти.",
+ "smw_sbv_property": "СвојÑтво:",
+ "smw_sbv_value": "ВредноÑÑ‚:",
+ "smw_sbv_submit": "Пронађи резултате",
+ "browse": "Преглед викија",
+ "smw_browselink": "Преглед ÑвојÑтава",
+ "smw_browse_article": "УнеÑите име почетне Ñтранице за претрагу.",
+ "smw_browse_go": "Иди",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "Прикажи долазна ÑвојÑтва",
+ "smw_browse_hide_incoming": "Сакриј долазна ÑвојÑтва",
+ "smw_browse_no_outgoing": "Ова Ñтраница нема ÑвојÑтва.",
+ "smw_browse_no_incoming": "Ðема оÑобина које вежу на ову Ñтраницу.",
+ "smw-browse-show-group": "Прикажи групе",
+ "smw-browse-hide-group": "Сакриј групе",
+ "smw_inverse_label_default": "$1 од",
+ "smw_inverse_label_property": "Обрнута ознака ÑвојÑтва",
+ "pageproperty": "Страница за претрагу ÑвојÑтва",
+ "smw_pp_docu": "Претражи Ñве вредноÑти оÑобине на датој Ñтраници.\nУнеÑите обоје, и Ñтраницу, и оÑобину.",
+ "smw_pp_from": "Са Ñтранице:",
+ "smw_pp_type": "СвојÑтво:",
+ "smw_pp_submit": "Пронађени резултати",
+ "smw_result_prev": "Претходних",
+ "smw_result_next": "Следећих",
+ "smw_result_results": "Резултати",
+ "smw_result_noresults": "Ðема резултата.",
+ "smwadmin": "ÐдминиÑтративне и одржавачке могућноÑти",
+ "smw-admin-statistics-job-title": "СтатиÑтика поÑлова",
+ "smw-admin-setupsuccess": "Машина за Ñкладиштење је поÑтављена.",
+ "smw_smwadmin_return": "Ðазад на $1",
+ "smw_smwadmin_updatestarted": "Ðов Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð°Ð¶ÑƒÑ€Ð¸Ñ€Ð°ÑšÐ° за оÑвежавање Ñемантичких података је започет.\nСви Ñачувани подаци ће бити поново изграђени или поправљени по потреби.\nМожете да пратите ток ажурирања на овој поÑебној Ñтраници.",
+ "smw_smwadmin_updatenotstarted": "Већ је у току један Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð°Ð¶ÑƒÑ€Ð¸Ñ€Ð°ÑšÐ°.\nÐе покрећите други.",
+ "smw_smwadmin_updatestopped": "Сви поÑтојећи процеÑи ажурирања Ñу обуÑтављени.",
+ "smw_smwadmin_updatenotstopped": "Како биÑте обуÑтавили покренути Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð°Ð¶ÑƒÑ€Ð¸Ñ€Ð°ÑšÐ°, морате активирати контролни оквир да биÑте потврдили да Ñте Ñигурни да желите да га обуÑтавите.",
+ "smw-admin-docu": "Ова поÑебна Ñтраница вам помаже приликом инÑталирања, ажурирања, одржавања и коришћења <a href=\"https://www.semantic-mediawiki.org\">Семантичког медијавикија</a> и такође обезбеђује додатне функције админиÑтрације и задатке, као и ÑтатиÑтику.\nÐе заборавите да направите копију вредних података пре него покренете функције админиÑтрације.",
+ "smw-admin-db": "Подешавање базе података",
+ "smw-admin-dbdocu": "Семантички медијавики захтева неке додатке у бази података Медијавикија, како би Ñе могли чувати Ñемантички подаци.\nДоња функција потврђује да је ваша база података иÑправно инÑталирана.\nПромене које Ñу направљене у овом кораку немају поÑледица за оÑтали део базе података Медијавикија, а по потреби Ñе могу врло лако поништити.\nОва функција инÑталирања Ñе може покретати више пута без бојазни да ће нанети неку штету, али је неопходна Ñамо једном приликом инÑталирања или ажурирања.",
+ "smw-admin-permissionswarn": "Ðко Ñе операција заврши Ñа SQL грешкама, кориÑник базе података (погледајте вашу LocalSettings.php датотеку) вероватно нема довољно дозвола за извршавање неопходних операција.\nИли дајте неопходне дозволе том кориÑнику како би могао да прави и брише табеле у бази, привремено Ñе пријавите као корен базе података у LocalSettings.php датотеци, или кориÑтите Ñкрипту за одржавање <code>setupStore.php</code>, која може кориÑтити акредитације админиÑтратора.",
+ "smw-admin-dbbutton": "Покретање или ажурирање табела",
+ "smw-admin-announce": "Ðајавите Вашу викију",
+ "smw-admin-deprecation-notice-title": "Ðапомене о заÑтаревању",
+ "smw-admin-deprecation-notice-docu": "Следећа Ñекција Ñадржи подешавања која Ñу заÑтарела или Ñу уклоњена али Ñу детектована као активна на овој Вики. Очекује Ñе да ће Ñе неким будућим ажурирањем уклонити подрша за ова подешавања.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> је заÑтарео параметар и биће уклоњен у верзији $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> је замењен параметар Ñа параметром <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> је уклоњен параметар у верзији $2",
+ "smw-admin-deprecation-notice-title-notice": "ПредÑтојеће промене",
+ "smw-admin-deprecation-notice-title-replacement": "Замењена или преименована подешавања",
+ "smw-admin-deprecation-notice-title-removal": "Уклоњена подешавања",
+ "smw_smwadmin_datarefresh": "Поправљање података",
+ "smw_smwadmin_datarefreshdocu": "ПоÑтоји могућноÑÑ‚ за враћање Ñвих података Ñемантичког Медијавикија који Ñу заÑновани на тренутном Ñадржају викија.\nОво је кориÑно за иÑправку оштећених података или за обнову података ако је унутрашњи формат промењен као резултат доградње Ñофтвера.\nÐжурирање Ñе врши за Ñваку Ñтраницу понаоÑоб, и треба да прође неко време како би Ñе Ñве вратило у нормалу.\nИÑпод је приказано да ли је ажурирање у току, а то вам омогућава да започнете или зауÑтавите доградње (оÑим ако ту могућноÑÑ‚ није иÑкључио админиÑтратор).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Једно ажурирање је већ у току.</strong>\nÐормално је да ажурирање напредује Ñпоро, пошто Ñе Ñамо оÑвежавају мали делови података Ñваки пут када кориÑник приÑтупи викију.\nДа би Ñе ово ажурирање брже завршило, можете покренути Ñкрипту за одржавање Медијавикија <code>runJobs.php</code> (кориÑтите опцију <code>--maxjobs 1000</code> за ограничење броја ажурирања у једном пакету).\nПроцена напредовања тренутног ажурирања:",
+ "smw_smwadmin_datarefreshbutton": "Закажи поновну иградњу података",
+ "smw_smwadmin_datarefreshstop": "ЗауÑтави ово ажурирање",
+ "smw_smwadmin_datarefreshstopconfirm": "Да, {{GENDER:$1|Ñигуран|Ñигурна}} Ñам.",
+ "smw-admin-support": "Добијање подршке",
+ "smw-admin-supportdocu": "Обезбеђени Ñу разни реÑурÑи да би вам помогли у Ñлучају проблемâ:",
+ "smw-admin-installfile": "Ðко наиђете на проблем при инÑталацији, најпре прочитајте Ñмернице у <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">датотеци INSTALL</a> и <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">Ñтраници за инÑталацију</a>.",
+ "smw-admin-smwhomepage": "Потпуна кориÑничка документација за Семантички медијавики је на <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Грешке могу да Ñе пријављују на <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">пратиоцу грешака</a>. Страница „<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">Пријављивање грешака</a>†пружа неке Ñмернице о томе како да напишете ефикаÑан извештај о грешци.",
+ "smw-admin-questions": "Ðко имате даљих питања или предлога, укључите Ñе у диÑкуÑију на <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">кориÑничком мејлинг ÑпиÑку Сематичког медијавикија</a> или <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">чет Ñоби</a>.",
+ "smw-admin-other-functions": "Друге функције",
+ "smw-admin-supplementary-section-subtitle": "ОÑновне функције",
+ "smw-admin-supplementary-settings-title": "Подешавања конфигурације",
+ "smw-admin-supplementary-operational-statistics-title": "Оперативна ÑтатиÑтика",
+ "smw-admin-supplementary-operational-statistics-cache-title": "СтатиÑтика кеширања",
+ "smw-admin-supplementary-elastic-functions": "Подржане функције",
+ "smw-admin-supplementary-elastic-settings-title": "Подешавања",
+ "smw-admin-supplementary-elastic-mappings-summary": "Резиме",
+ "smw-admin-supplementary-elastic-statistics-title": "СтатиÑтика",
+ "smw_adminlinks_datastructure": "Структура података",
+ "smw_adminlinks_displayingdata": "Приказивање података",
+ "smw_adminlinks_inlinequerieshelp": "Помоћ за непоÑредне упите",
+ "smw-info-par-message": "Порука за приказ.",
+ "prefs-smw": "Семантички Медијавики",
+ "prefs-general-options": "Опште опције",
+ "smw-ui-tooltip-title-info": "Информације",
+ "smw-ui-tooltip-title-warning": "Упозорење",
+ "smw-ui-tooltip-title-error": "Грешка",
+ "smw-ui-tooltip-title-parameter": "Параметар",
+ "smw-ui-tooltip-title-event": "Догађај",
+ "smw-ui-tooltip-title-note": "Ðапомена",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw-ui-tooltip-title-reference": "Референца",
+ "smw_unknowntype": "„$1†тип овог ÑвојÑтва је неважећи.",
+ "smw_concept_header": "Странице концепта \"$1\"",
+ "smw_conceptarticlecount": "Приказ $1 {{PLURAL:$1|Ñтранице|Ñтраница}} које припадају том концепту.",
+ "right-smw-admin": "приÑтупање админиÑтраторÑким задацима (Семантички Медијавики)",
+ "action-smw-patternedit": "уређујете регуларне изразе које кориÑти Семантички Медијавики",
+ "group-smwadministrator": "ÐдминиÑтратори (Семантички Медијавики)",
+ "grouppage-smwadministrator": "{{ns:project}}:ÐдминиÑтратори (Семантички Медијавики)",
+ "smw-sp-properties-header-label": "ЛиÑта ÑвојÑтава",
+ "smw-sp-admin-settings-button": "Генериши лиÑту подешавања",
+ "smw-admin-idlookup-input": "Претрага:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Преглед",
+ "smw-admin-tab-maintenance": "Одржавање",
+ "smw-admin-tab-supplement": "ДопунÑке функције",
+ "smw-admin-tab-registry": "РегиÑтар",
+ "smw-admin-maintenance-no-description": "Без опиÑа.",
+ "smw-livepreview-loading": "Учитавам…",
+ "smw-sp-searchbyproperty-resultlist-header": "ЛиÑта резултата",
+ "smw-search-syntax": "СинтакÑа",
+ "smw-search-profile-tooltip": "Претражите функције у вези Семантичког Медијавикија",
+ "smw-search-profile-sort-title": "ÐаÑлов",
+ "smw-search-profile-extended-help-query-link": "(За више детаља $1).",
+ "smw-search-profile-extended-help-find-forms": "доÑтупни облици",
+ "smw-search-profile-extended-section-sort": "Сортирај по",
+ "smw-search-profile-extended-section-form": "ОбраÑци",
+ "smw-search-profile-extended-section-namespace": "ИменÑки проÑтор",
+ "smw-search-profile-extended-section-query": "Упит",
+ "smw-search-profile-link-caption-query": "прикажи",
+ "smw-search-show": "Прикажи",
+ "smw-search-hide": "Сакриј",
+ "log-name-smw": "Дневник Семантичког Медијавикија",
+ "log-show-hide-smw": "$1 дневник Семантичког Медијавикија",
+ "logeventslist-smw-log": "Дневник Семантичког Медијавикија",
+ "smw-type-tab-properties": "СвојÑтва",
+ "smw-type-tab-types": "Типови",
+ "smw-type-tab-errors": "Грешке",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-limitreport-intext-parsertime": "[SMW] Време рашчлањивања унутартекÑтуалних анотација",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Ñекунд|Ñекунда|Ñекунди}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|Ñекунд|Ñекунде|Ñекунди}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Ñекунд|Ñекунда|Ñекунди}}",
+ "smw-datavalue-allows-value-list-missing-marker": "Садржају лиÑте „$1†недоÑтају Ñтавке Ñа * као означивачем лиÑте.",
+ "smw-datavalue-wikipage-invalid-title": "УнеÑена вредноÑÑ‚ типа Ñтранице „$1†Ñадржи невалидне карактере или је некомплетна и Ñтога може да узрокује неочекиване резултате током претраге или процеÑа означавања.",
+ "smw-datavalue-wikipage-property-invalid-title": "СвојÑтво „$1†(као тип Ñтранице) Ñа унеÑеном вредношћу „$2†Ñадржи невалидне карактере или је некомплетно и Ñтога може да узрокује неочекиване резултате током претраге или процеÑа означавања.",
+ "smw-clipboard-copy-link": "Копирај везу на клипборд",
+ "smw-data-lookup": "Добављање података…",
+ "smw-no-data-available": "Ðема података.",
+ "smw-format-datatable-emptytable": "Ðема доÑтупних података у табели",
+ "smw-format-datatable-info": "Приказ _START_ до _END_ од _TOTAL_ Ñтавки",
+ "smw-format-datatable-infoempty": "Приказ 0 до 0 од 0 Ñтавки",
+ "smw-format-datatable-infofiltered": "(филтрирано од _MAX_ укупно Ñтавки)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Прикажи _MENU_ Ñтавки",
+ "smw-format-datatable-loadingrecords": "Учитавање...",
+ "smw-format-datatable-processing": "Обрада...",
+ "smw-format-datatable-search": "Претрага:",
+ "smw-format-datatable-zerorecords": "Ðема нађених одговарајућих података",
+ "smw-format-datatable-first": "Први",
+ "smw-format-datatable-last": "ПоÑледњи",
+ "smw-format-datatable-next": "Следећи",
+ "smw-format-datatable-previous": "Претходни",
+ "smw-format-datatable-sortascending": ": активирај за раÑтуће Ñортирање колоне",
+ "smw-format-datatable-sortdescending": ": активирај за опадајуће Ñортирање колоне",
+ "smw-format-datatable-toolbar-export": "Извоз",
+ "smw-property-reserved-category": "Категорија",
+ "smw-category": "Категорија",
+ "smw-filter": "Филтер",
+ "smw-help": "Помоћ",
+ "smw-processing": "Обрађивање…",
+ "smw-types-title": "Тип: $1",
+ "smw-schema-error": "Грешка приликом провере ваљаноÑти",
+ "smw-schema-error-violation": "Кршење („$1â€, „$2â€)",
+ "smw-schema-title": "Шема",
+ "smw-schema-type": "Тип",
+ "smw-schema-tag": "{{PLURAL:$1|Ознака|Ознаке}}",
+ "smw-property-tab-usage": "Употреба",
+ "smw-property-tab-redirects": "Синоними",
+ "smw-property-tab-subproperties": "ПодÑвојÑтва",
+ "smw-property-tab-specification": "… више",
+ "smw-concept-tab-list": "ЛиÑта",
+ "smw-concept-tab-errors": "Грешке",
+ "smw-ask-tab-extra": "Додатно",
+ "smw-ask-tab-code": "Кôд",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Помоћ:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sr-el.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sr-el.json
new file mode 100644
index 00000000..233f2665
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sr-el.json
@@ -0,0 +1,263 @@
+{
+ "@metadata": {
+ "authors": [
+ "Liangent",
+ "Michaello",
+ "Milicevic01",
+ "Rancher",
+ "Slaven Kosanovic",
+ "Sociologist",
+ "Kghbln",
+ "Acamicamacaraca",
+ "Obsuser",
+ "Zoranzoki21"
+ ]
+ },
+ "smw_viewasrdf": "RDF fid",
+ "smw_finallistconjunct": " i",
+ "smw-factbox-head": "… viÅ¡e o stranici „$1â€",
+ "smw-factbox-facts": "ÄŒinjenice",
+ "smw_isspecprop": "Ovo svojstvo je posebno na ovom vikiju.",
+ "smw-concept-cache-header": "Upotreba keša",
+ "smw-concept-no-cache": "Nije dostupan keš.",
+ "smw_concept_description": "Opis koncepta „$1â€",
+ "smw_no_concept_namespace": "Koncepti jedino mogu da se definišu na stranicama u imenskom prostoru Concept:",
+ "smw_multiple_concepts": "Svaka stranica koncepta sme da ima samo jednu definiciju koncepta.",
+ "smw_concept_cache_miss": "Koncept \"$1\" se trenutno ne može koristiti, poÅ¡to konfiguracija vikija zahteva da bude komputovan oflajn.\nAko problem ne nestane za neko vreme, zatražite od administratora sajta da uÄini ovaj koncept dostupnim.",
+ "smw_noinvannot": "Na obrtna svojstva ne mogu da se dodele vrednosti.",
+ "smw_baduri": "URI-ji za oblik „$1“ nisu dozvoljeni.",
+ "smw_csv_link": "CSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "Rezultat brojanja",
+ "smw_printername_csv": "CSV izvoz",
+ "smw_printername_dsv": "DSV izvoz",
+ "smw_printername_debug": "Upit za preÄišćavanje (samo za eksperte)",
+ "smw_printername_embedded": "Ugradi sadržaj stranice",
+ "smw_printername_json": "JSON izvoz",
+ "smw_printername_list": "Spisak",
+ "smw_printername_plainlist": "ObiÄan spisak",
+ "smw_printername_ol": "Nabrajanje",
+ "smw_printername_table": "Tabela",
+ "smw_printername_broadtable": "Å iroka tabela",
+ "smw_printername_template": "Å ablon",
+ "smw_printername_templatefile": "Datoteka Å¡ablona",
+ "smw_printername_rdf": "RDF izvoz",
+ "smw_printername_category": "Kategorija",
+ "validator-type-class-SMWParamSource": "tekst",
+ "smw-paramdesc-limit": "Najveći broj rezultata koji će biti vraćeni",
+ "smw-paramdesc-mainlabel": "Etiketa koja će se koristiti za ime glavne stranice",
+ "smw-paramdesc-link": "Prikaži vrednosti u obliku veza",
+ "smw-paramdesc-intro": "Tekst koji treba da se prikaže pre rezultata upita (u sluÄaju potrebe)",
+ "smw-paramdesc-outro": "Tekst koji treba da se prikaže iza rezultata upita (u sluÄaju potrebe)",
+ "smw-paramdesc-default": "Tekst koji treba da se prikaže u sluÄaju da upit nije dao rezultate",
+ "smw-paramdesc-sep": "RazdvajaÄ izmeÄ‘u rezultata",
+ "smw-paramdesc-columns": "Broj kolona u kojima će se prikazati rezultati",
+ "smw-paramdesc-embedonly": "Ne prikazuj zaglavlja",
+ "smw-paramdesc-searchlabel": "Tekst veze s rezultatima",
+ "smw-paramdesc-template-arguments": "Podešavanje isporuke imenovanih parametara do šablona",
+ "smw_iq_disabled": "SemantiÄki upiti su onemogućeni na ovom vikiju.",
+ "smw_iq_moreresults": "... više rezultata",
+ "smw_parseerror": "Data vrednost nije shvaćena.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "\"$1\" se ne može koristiti za ime stranice na ovom vikiju.",
+ "smw_wrong_namespace": "Samo stranice iz imenskog prostora \"$1\" su dozvoljene ovde.",
+ "smw_manytypes": "Više od jednog tipa je definisano za ovu osobinu.",
+ "smw_emptystring": "Prazni stringovi nisu prihvatljivi.",
+ "smw_notinenum": "„$1“ nije na spisku ($2) [[Property:Allows value|mogućih vrednosti]] za osobinu „$3“.",
+ "smw_noboolean": "\"$1\" nije prepoznat kao Bolova vrednost (istinito/neistinito).",
+ "smw_true_words": "istinito, i, da, d",
+ "smw_false_words": "neistinito, n, ne, n",
+ "smw_nofloat": "\"$1\" nije broj.",
+ "smw_infinite": "Brojevi dužine kao \"$1\" nisu podržani.",
+ "smw_novalues": "Nema naznaÄene vrednosti.",
+ "smw_nodatetime": "Format datuma \"$1\" nije razumljiv.",
+ "smw_toomanyclosing": "Izgleda da je previÅ¡e sluÄajeva tipa \"$1\" u upitu.",
+ "smw_noclosingbrackets": "Neke uglaste zagrade \"<nowiki>[[</nowiki>\" u vašem upitu, nisu zatvorene odgovarajućim \"]]\".",
+ "smw_misplacedsymbol": "Simbol \"$1\" je iskorišćen na mestu gde nije od koristi.",
+ "smw_unexpectedpart": "Deo upita \"$1\" nije shvaćen.\nRezultati mogu biti neoÄekivani.",
+ "smw_emptysubquery": "Neki podupiti ne sadrže valjane uslove.",
+ "smw_misplacedsubquery": "Neki podupiti su korišćeni na mestu gde podupiti nisu dozvoljeni.",
+ "smw_valuesubquery": "Za vrednosti osobine \"$1\", podupiti nisu podržani.",
+ "smw_badqueryatom": "Jedan deo \"<nowiki>[[…]]</nowiki>\" upita nije shvaćen.",
+ "smw_propvalueproblem": "Vrednost za osobinu \"$1\" nije shvaćena.",
+ "smw_noqueryfeature": "Neki delovi ovog upita nisu podržani na ovom vikiju, te je deo upita izostavljen ($1).",
+ "smw_noconjunctions": "Konjukcije u upitima nisu podržane na ovom vikiju, te je deo upita izostavljen ($1).",
+ "smw_nodisjunctions": "Disjunkcije u upitima nisu podržane na ovom vikiju, te je deo upita izostavljen ($1).",
+ "smw_querytoolarge": "Sledeće uslove upita nije moguće razmatrati zbog restrikcija vikija za dužinu i obim upita: <code>$1</code>.",
+ "smw_notemplategiven": "Ponudite vrednost za parametar â€Å¡ablonâ€, kako bi ovaj format upita dao rezultat.",
+ "smw_type_header": "Osobine tipa \"$1\"",
+ "smw_typearticlecount": "Prikaz $1 {{PLURAL:$1|osobine|osobina}} za ovaj tip.",
+ "smw_attribute_header": "Stranice koje koriste osobinu \"$1\"",
+ "smw_attributearticlecount": "Prikaz $1 {{PLURAL:$1|stranice|stranica}} koje koriste ovu osobinu.",
+ "smw-propertylist-redirect-header": "Sinonimi",
+ "specialpages-group-smw_group": "SemantiÄki Medijaviki",
+ "exportrdf": "Izvoz stranica u RDF",
+ "smw_exportrdf_docu": "Ova stranica omogućava preuzimanje podataka s neke stranice u formatu RDF.\nDa biste izvezli stranice, unesite naslove u kućicu ispod (po jedan naslov u svakom redu).",
+ "smw_exportrdf_recursive": "Rekurzivno izvezi sve povezane stranice.\nObratite pažnju da rezultat može biti veoma obiman!",
+ "smw_exportrdf_backlinks": "Takođe izvezi sve stranice koje vežu na već izvezene stranice.\nGeneriše RDF koji je moguće pregledati.",
+ "smw_exportrdf_lastdate": "Ne izvozi stranice koje nisu menjane od datog momenta.",
+ "smw_exportrdf_submit": "Izvoz",
+ "uriresolver": "URI rezolver",
+ "properties": "Osobine",
+ "smw_properties_docu": "Sledeće osobine se koriste na vikiju.",
+ "smw_property_template": "$1 tipa $2 ($3 {{PLURAL:$3|upotreba|upotrebe|upotreba}})",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Sve osobine trebaju imati opisnu stranicu!",
+ "smw_propertylackstype": "Nema definisanog tipa za ovu osobinu (za sada usvajam tip $1).",
+ "smw_propertyhardlyused": "Ova osobina jedva da se koristi na ovom vikiju!",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "concepts": "Koncepti",
+ "unusedproperties": "Neiskorišćene osobine",
+ "smw-unusedproperties-docu": "Sledeće osobine postoje iako nema drugih stranica koje ih koriste.",
+ "smw-unusedproperty-template": "$1 tipa $2",
+ "wantedproperties": "Tražene osobine",
+ "smw-wantedproperties-docu": "Sledeće osobine se koriste na vikiju, ali još uvek nemaju svoju stranicu sa opisom.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|upotreba|upotreba}})",
+ "smw_purge": "Osveži",
+ "types": "Tipovi",
+ "smw_types_docu": "Spisak [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes dostupnih vrsta podataka] sa svakom [https://www.semantic-mediawiki.org/wiki/Help:Datatype vrstom] predstavlja jedinstven skup atributa koji opisuju vrednost u smislu karakteristika Äuvanje i izlaganje koje su nasledne po dodeljenim svojstvima.",
+ "ask": "SemantiÄka pretraga",
+ "smw_ask_sortby": "Poređaj u kolone (opcionalno)",
+ "smw_ask_ascorder": "Rastući",
+ "smw_ask_descorder": "Opadajući",
+ "smw_ask_submit": "Pronađi rezultate",
+ "smw_ask_editquery": "Uredi upit",
+ "smw_add_sortcondition": "[Dodaj uslove sortiranja]",
+ "smw_ask_hidequery": "Sakrij upit (kompaktni prikaz)",
+ "smw_ask_help": "Upit u stranice pomoći",
+ "smw_ask_queryhead": "Uslov",
+ "smw_ask_printhead": "Izbor Å¡tampe",
+ "smw_ask_printdesc": "(dodaj jedno ime osobine po liniji)",
+ "smw_ask_format_as": "Oblikuj kao:",
+ "smw_ask_defaultformat": "podrazumevano",
+ "smw_ask_otheroptions": "Druge opcije",
+ "smw_ask_show_embed": "Prikaži ugrađeni kod",
+ "smw_ask_hide_embed": "Sakrij ugradbeni kod",
+ "smw_ask_embed_instr": "Za neposredno gneždenje ovog upita u stranicu vikija, koristi donji kod.",
+ "smw-ask-delete": "Ukloni",
+ "smw-ask-search": "Pretraga",
+ "smw-ask-result": "Rezultat",
+ "smw-ask-empty": "OÄisti sve unose",
+ "smw-ask-format": "Format",
+ "smw-ask-query-search-info": "Na upit <code><nowiki>$1</nowiki></code> odgovorio je {{PLURAL:$3|1=<code>$2</code> (iz keša)|<code>$2</code> (iz keša)|<code>$2</code>}} za $4 {{PLURAL:$4|sekund|sekunda|sekundi}}.",
+ "searchbyproperty": "Pretraga po osobinama",
+ "smw_sbv_docu": "Pretraži sve stranice koje poseduju datu osobinu i vrednost.",
+ "smw_sbv_novalue": "Unesite valjanu vrednost za ovu osobinu, ili pogledajte sve vrednosti osobine za \"$1\".",
+ "smw_sbv_displayresult": "Spisak svih stranica koje sadrže osobinu \"$1\" sa vrednošću \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Spisak svih stranica koje sadrže svojstvo „$1†sa vrednošću „$2â€.\nPoÅ¡to je pronaÄ‘eno samo nekoliko rezultata, takoÄ‘e su prikazane približne vrednosti.",
+ "smw_sbv_property": "Osobina:",
+ "smw_sbv_value": "Vrednost:",
+ "smw_sbv_submit": "Pronađi rezultate",
+ "browse": "Pregled vikija",
+ "smw_browselink": "Pregled svojstava",
+ "smw_browse_article": "Unesite ime poÄetne stranice za pretragu.",
+ "smw_browse_go": "Idi",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "prikaži osobine koje vežu ovde",
+ "smw_browse_hide_incoming": "sakrij osobine koje vežu ovde",
+ "smw_browse_no_outgoing": "Ova stranica nema osobina.",
+ "smw_browse_no_incoming": "Nema osobina koje vežu na ovu stranicu.",
+ "smw_inverse_label_default": "$1 od",
+ "smw_inverse_label_property": "ReciproÄna etiketa osobine",
+ "pageproperty": "Stranica za pretragu osobina",
+ "smw_pp_docu": "Pretraži sve vrednosti osobine na datoj stranici.\nUnesite oboje, i stranicu, i osobinu.",
+ "smw_pp_from": "Sa stranice:",
+ "smw_pp_type": "Svojstvo:",
+ "smw_pp_submit": "Pronađeni rezultati",
+ "smw_result_prev": "Prethodnih",
+ "smw_result_next": "Sledećih",
+ "smw_result_results": "Rezultati",
+ "smw_result_noresults": "Nema rezultata.",
+ "smwadmin": "Administrativne i održavaÄke mogućnosti",
+ "smw-admin-setupsuccess": "Mašina za skladištenje je postavljena.",
+ "smw_smwadmin_return": "Nazad na $1",
+ "smw_smwadmin_updatestarted": "Nov proces ažuriranja za osvežavanje semantiÄkih podataka je zapoÄet.\nSvi saÄuvani podaci će biti ponovo izgraÄ‘eni ili popravljeni po potrebi.\nMožete da pratite tok ažuriranja na ovoj posebnoj stranici.",
+ "smw_smwadmin_updatenotstarted": "Već je u toku jedan proces ažuriranja.\nNe pokrećite drugi.",
+ "smw_smwadmin_updatestopped": "Svi postojeći procesi ažuriranja su obustavljeni.",
+ "smw_smwadmin_updatenotstopped": "Kako biste obustavili pokrenuti proces ažuriranja, morate aktivirati kontrolni okvir da biste potvrdili da ste sigurni da želite da ga obustavite.",
+ "smw-admin-docu": "Ova posebna stranica vam pomaže prilikom instaliranja, ažuriranja, održavanja i korišćenja <a href=\"https://www.semantic-mediawiki.org\">SemantiÄkog medijavikija</a> i takoÄ‘e obezbeÄ‘uje dodatne funkcije administracije i zadatke, kao i statistiku.\nNe zaboravite da napravite kopiju vrednih podataka pre nego pokrenete funkcije administracije.",
+ "smw-admin-db": "Održavanje baze podataka",
+ "smw-admin-dbdocu": "SemantiÄki medijaviki zahteva neke dodatke u bazi podataka Medijavikija, kako bi se mogli Äuvati semantiÄki podaci.\nDonja funkcija potvrÄ‘uje da je vaÅ¡a baza podataka ispravno instalirana.\nPromene koje su napravljene u ovom koraku nemaju posledica za ostali deo baze podataka Medijavikija, a po potrebi se mogu vrlo lako poniÅ¡titi.\nOva funkcija instaliranja se može pokretati viÅ¡e puta bez bojazni da će naneti neku Å¡tetu, ali je neophodna samo jednom prilikom instaliranja ili ažuriranja.",
+ "smw-admin-permissionswarn": "Ako se operacija završi sa SQL greškama, korisnik baze podataka (pogledajte vašu LocalSettings.php datoteku) verovatno nema dovoljno dozvola za izvršavanje neophodnih operacija.\nIli dajte neophodne dozvole tom korisniku kako bi mogao da pravi i briše tabele u bazi, privremeno se prijavite kao koren baze podataka u LocalSettings.php datoteci, ili koristite skriptu za održavanje <code>setupStore.php</code>, koja može koristiti akreditacije administratora.",
+ "smw-admin-dbbutton": "Pokretanje ili ažuriranje tabela",
+ "smw-admin-announce": "Najavite Vašu vikiju",
+ "smw-admin-deprecation-notice-title": "Napomene o zastarevanju",
+ "smw-admin-deprecation-notice-docu": "Sledeća sekcija sadrži podeÅ¡avanja koja su zastarela ili su uklonjena ali su detektovana kao aktivna na ovoj Viki. OÄekuje se da će se nekim budućim ažuriranjem ukloniti podrÅ¡a za ova podeÅ¡avanja.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> je zastareo parametar i biće uklonjen u verziji $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> je zamenjen parametar sa parametrom <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> je uklonjen parametar u verziji $2",
+ "smw-admin-deprecation-notice-title-notice": "Predstojeće promene",
+ "smw-admin-deprecation-notice-title-replacement": "Zamenjena ili preimenovana podešavanja",
+ "smw-admin-deprecation-notice-title-removal": "Uklonjena podešavanja",
+ "smw_smwadmin_datarefresh": "Popravljanje podataka",
+ "smw_smwadmin_datarefreshdocu": "Postoji mogućnost za vraćanje svih podataka semantiÄkog Medijavikija koji su zasnovani na trenutnom sadržaju vikija.\nOvo je korisno za ispravku oÅ¡tećenih podataka ili za obnovu podataka ako je unutraÅ¡nji format promenjen kao rezultat dogradnje softvera.\nAžuriranje se vrÅ¡i za svaku stranicu ponaosob, i treba da proÄ‘e neko vreme kako bi se sve vratilo u normalu.\nIspod je prikazano da li je ažuriranje u toku, a to vam omogućava da zapoÄnete ili zaustavite dogradnje (osim ako tu mogućnost nije iskljuÄio administrator).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Jedno ažuriranje je već u toku.</strong>\nNormalno je da ažuriranje napreduje sporo, poÅ¡to se samo osvežavaju mali delovi podataka svaki put kada korisnik pristupi vikiju.\nDa bi se ovo ažuriranje brže zavrÅ¡ilo, možete pokrenuti skriptu za održavanje Medijavikija <code>runJobs.php</code> (koristite opciju <code>--maxjobs 1000</code> za ograniÄenje broja ažuriranja u jednom paketu).\nProcena napredovanja trenutnog ažuriranja:",
+ "smw_smwadmin_datarefreshbutton": "Zakaži ponovnu igradnju podataka",
+ "smw_smwadmin_datarefreshstop": "Zaustavi ovo ažuriranje",
+ "smw_smwadmin_datarefreshstopconfirm": "Da, {{GENDER:$1|siguran|sigurna}} sam.",
+ "smw-admin-support": "Dobijanje podrške",
+ "smw-admin-supportdocu": "ObezbeÄ‘eni su razni resursi da bi vam pomogli u sluÄaju problemâ:",
+ "smw-admin-installfile": "Ako naiÄ‘ete na problem pri instalaciji, najpre proÄitajte smernice u <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">datoteci INSTALL</a> i <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">stranici za instalaciju</a>.",
+ "smw-admin-smwhomepage": "Potpuna korisniÄka dokumentacija za SemantiÄki medijaviki je na <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Greške mogu da se prijavljuju na <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">pratiocu grešaka</a>. Stranica „<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">Prijavljivanje grešaka</a>†pruža neke smernice o tome kako da napišete efikasan izveštaj o grešci.",
+ "smw-admin-questions": "Ako imate daljih pitanja ili predloga, ukljuÄite se u diskusiju na <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">korisniÄkom mejling spisku SematiÄkog medijavikija</a> ili <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">Äet sobi</a>.",
+ "smw-admin-supplementary-elastic-mappings-summary": "Rezime",
+ "smw_adminlinks_datastructure": "Struktura podataka",
+ "smw_adminlinks_displayingdata": "Prikazivanje podataka",
+ "smw_adminlinks_inlinequerieshelp": "Pomoć za neposredne upite",
+ "prefs-smw": "SemantiÄki Medijaviki",
+ "prefs-general-options": "Opšte opcije",
+ "smw_unknowntype": "„$1†tip ovog svojstva je nevažeći.",
+ "smw_concept_header": "Stranice koncepta \"$1\"",
+ "smw_conceptarticlecount": "Prikaz $1 {{PLURAL:$1|stranice|stranica}} koje pripadaju tom konceptu.",
+ "right-smw-admin": "pristupanje administratorskim zadacima (SemantiÄki Medijaviki)",
+ "action-smw-patternedit": "ureÄ‘ujete regularne izraze koje koristi SemantiÄki Medijaviki",
+ "smw-admin-tab-registry": "Registar",
+ "smw-livepreview-loading": "UÄitavam…",
+ "smw-search-profile-extended-help-find-forms": "dostupni oblici",
+ "smw-search-profile-extended-section-namespace": "Imenski prostor",
+ "smw-search-show": "Prikaži",
+ "smw-search-hide": "Sakrij",
+ "smw-type-tab-properties": "Svojstva",
+ "smw-type-tab-types": "Tipovi",
+ "smw-type-tab-errors": "Greške",
+ "smw-limitreport-intext-parsertime": "[SMW] Vreme raÅ¡Älanjivanja unutartekstualnih anotacija",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekund|sekunda|sekundi}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekund|sekunda|sekundi}}",
+ "smw-datavalue-allows-value-list-missing-marker": "Sadržaj spiska „$1†nema stavke sa * kao oznakom spiska.",
+ "smw-datavalue-wikipage-invalid-title": "Unesena vrednost tipa stranice „$1†sadrži nevalidne karaktere ili je nekompletna i stoga može da uzrokuje neoÄekivane rezultate tokom pretrage ili procesa oznaÄavanja.",
+ "smw-datavalue-wikipage-property-invalid-title": "Svojstvo „$1†(kao tip stranice) sa unesenom vrednošću „$2†sadrži nevalidne karaktere ili je nekompletno i stoga može da uzrokuje neoÄekivane rezultate tokom pretrage ili procesa oznaÄavanja.",
+ "smw-clipboard-copy-link": "Kopiraj vezu na klipbord",
+ "smw-no-data-available": "Nema podataka.",
+ "smw-format-datatable-emptytable": "Nema dostupnih podataka u tabeli",
+ "smw-format-datatable-info": "Prikaz _START_ do _END_ od _TOTAL_ stavki",
+ "smw-format-datatable-infoempty": "Prikaz 0 do 0 od 0 stavki",
+ "smw-format-datatable-infofiltered": "(filtrirano od _MAX_ ukupno stavki)",
+ "smw-format-datatable-infothousands": ".",
+ "smw-format-datatable-lengthmenu": "Prikaži _MENU_ stavki",
+ "smw-format-datatable-loadingrecords": "UÄitavanje...",
+ "smw-format-datatable-processing": "Obrada...",
+ "smw-format-datatable-search": "Pretraga:",
+ "smw-format-datatable-zerorecords": "Nema nađenih odgovarajućih podataka",
+ "smw-format-datatable-first": "Prvi",
+ "smw-format-datatable-last": "Poslednji",
+ "smw-format-datatable-next": "Sledeći",
+ "smw-format-datatable-previous": "Prethodni",
+ "smw-format-datatable-sortascending": ": aktiviraj za rastuće sortiranje kolone",
+ "smw-format-datatable-sortdescending": ": aktiviraj za opadajuće sortiranje kolone",
+ "smw-schema-error": "Greška prilikom provere valjanosti",
+ "smw-schema-error-violation": "KrÅ¡enje („$1â€, „$2â€)",
+ "smw-schema-title": "Å ema",
+ "smw-schema-type": "Tip",
+ "smw-schema-tag": "{{PLURAL:$1|Oznaka|Oznake}}",
+ "smw-property-tab-usage": "Upotreba",
+ "smw-property-tab-redirects": "Sinonimi",
+ "smw-property-tab-subproperties": "Podsvojstva",
+ "smw-property-tab-specification": "… više",
+ "smw-concept-tab-list": "Spisak",
+ "smw-concept-tab-errors": "Greške",
+ "smw-ask-tab-extra": "Dodatno",
+ "smw-ask-tab-code": "Kôd"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/stq.json b/www/wiki/extensions/SemanticMediaWiki/i18n/stq.json
new file mode 100644
index 00000000..408da62c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/stq.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Leede …"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/su.json b/www/wiki/extensions/SemanticMediaWiki/i18n/su.json
new file mode 100644
index 00000000..85fc503e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/su.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kandar"
+ ]
+ },
+ "browse": "Sungsi wiki",
+ "smw-livepreview-loading": "Ngamuat…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sv.json
new file mode 100644
index 00000000..9217168f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sv.json
@@ -0,0 +1,555 @@
+{
+ "@metadata": {
+ "authors": [
+ "Boivie",
+ "Eliasb",
+ "Flrn",
+ "Gabbe.g",
+ "Jopparn",
+ "Lejonel",
+ "Leo Johannes",
+ "M.M.S.",
+ "MagnusA",
+ "Martinwiss",
+ "Najami",
+ "Per",
+ "Rotsee",
+ "Sannab",
+ "WikiPhoenix",
+ "ì•„ë¼",
+ "Lokal Profil",
+ "Freked",
+ "Ainali",
+ "Dan Koehl",
+ "Macofe",
+ "Rockyfelle",
+ "Nemo bis",
+ "Skalman",
+ "Josve05a",
+ "Bengtsson96",
+ "Umeaboy"
+ ]
+ },
+ "smw-desc": "Gör din wiki mer tillgänglig – för både maskiner och människor ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentation online])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error-why-title": "Varför ser jag detta fel?",
+ "smw-upgrade-error-how-title": "Hur fixar jag detta fel?",
+ "smw-semantics-not-enabled": "Semantic MediaWiki-funktionalitet är inte aktiverad på denna wiki.",
+ "smw_viewasrdf": "RDF-matning",
+ "smw_finallistconjunct": "och",
+ "smw-factbox-head": "... mer om \"$1\"",
+ "smw-factbox-facts": "Fakta",
+ "smw-factbox-facts-help": "Visar påståenden och fakta som en användare har skapat",
+ "smw-factbox-attachments": "Bilagor",
+ "smw-factbox-attachments-help": "Visa tillgängliga bilagor",
+ "smw_isspecprop": "Den här egenskapen är en specialegenskap på den här wikin.",
+ "smw-concept-cache-header": "Cacheanvändning",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count Konceptcachen] innehåller {{PLURAL:$1|'''en''' entitet|'''$1''' entiteter}} ($2).",
+ "smw-concept-no-cache": "Ingen tillgänglig cache.",
+ "smw_concept_description": "Beskrivning av konceptet â€$1â€",
+ "smw_no_concept_namespace": "Koncept kan endast defineras på sidor i namnrymden Concept:",
+ "smw_multiple_concepts": "Varje konceptsida kan endast ha en konceptdefinition.",
+ "smw_concept_cache_miss": "Konceptet \"$1\" kan inte användas för tillfället, eftersom wiki-konfigurationen kräver att det beräknas off-line.\nOm problemet inte försvinner efter någon tid, så be din administratör att göra konceptet tillgängligt.",
+ "smw_noinvannot": "Värden kan inte tilldelas inverterade egenskaper.",
+ "version-semantic": "Semantiska tillägg",
+ "smw_baduri": "Beklagar, URI:er på formen \"$1\" är inte tillåtna.",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Antal",
+ "smw_printername_csv": "CSV",
+ "smw_printername_dsv": "DSV",
+ "smw_printername_debug": "Debug (för experter)",
+ "smw_printername_embedded": "Bädda in sidinnehåll",
+ "smw_printername_json": "JSON",
+ "smw_printername_list": "Lista",
+ "smw_printername_plainlist": "Vanlig lista",
+ "smw_printername_ol": "Numrerad lista",
+ "smw_printername_ul": "Punktlista",
+ "smw_printername_table": "Tabell",
+ "smw_printername_broadtable": "Bred tabell",
+ "smw_printername_template": "Mall",
+ "smw_printername_templatefile": "Mallfil",
+ "smw_printername_rdf": "RDF",
+ "smw_printername_category": "Kategori",
+ "validator-type-class-SMWParamSource": "text",
+ "smw-paramdesc-limit": "Maximalt antal resultat att visa",
+ "smw-paramdesc-offset": "Vilket resultat i ordningen ska visas som det första?",
+ "smw-paramdesc-headers": "Visa rubriker/egenskapsnamn",
+ "smw-paramdesc-mainlabel": "Etiketten som ska ges till huvudsidans namn",
+ "smw-paramdesc-link": "Visa värden som länkar",
+ "smw-paramdesc-intro": "Text som ska visas före resultatet (om det finns något)",
+ "smw-paramdesc-outro": "Text som ska visas efter resultatet (om det finns något)",
+ "smw-paramdesc-default": "Text som ska visas om det inte finns något resultat",
+ "smw-paramdesc-sep": "Skiljetecken mellan resultaten",
+ "smw-paramdesc-showsep": "Visa skiljetecken högst upp i CSV-filen (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "Räkna och visa antalet förekomster istället för att visa alla värden.",
+ "smw-paramdesc-distributionsort": "Sortera värdena efter hur ofta de förekommer.",
+ "smw-paramdesc-distributionlimit": "Begränsa värdefördelningen till vissa värdens antal",
+ "smw-paramdesc-template": "Namnet på den mall som ska skriva ut resultatet",
+ "smw-paramdesc-columns": "Antalet kolumner som ska visa resultat",
+ "smw-paramdesc-userparam": "Ett värde som skickas till varje mall, om mallar används",
+ "smw-paramdesc-introtemplate": "Mall som ska ska visas före resultatet, om det finns något",
+ "smw-paramdesc-outrotemplate": "Mall som ska visas efter resultatet, som det finns något",
+ "smw-paramdesc-embedformat": "HTML-element för rubriker",
+ "smw-paramdesc-embedonly": "Visa inga rubriker",
+ "smw-paramdesc-table-class": "Ytterligare CSS-klass som ska användas för tabellen",
+ "smw-paramdesc-table-transpose": "Visa tabellrubriker vertikalt och resultat horisontellt",
+ "smw-paramdesc-rdfsyntax": "RDF-syntax som ska användas",
+ "smw-paramdesc-csv-sep": "Anger en kolumnseparator",
+ "smw-paramdesc-csv-valuesep": "Anger en värdeseparator",
+ "smw-paramdesc-dsv-separator": "Skiljetecken att använda",
+ "smw-paramdesc-dsv-filename": "Namn på DSV-filen",
+ "smw-paramdesc-filename": "Namn på filen",
+ "smw-smwdoc-description": "Visar en tabell med alla parametrar som kan användas för det angivna resultatformatet, samt standardvärden och beskrivningar.",
+ "smw-smwdoc-par-format": "Resultatformatet som parameter-dokumentation ska visas för.",
+ "smw-smwdoc-par-parameters": "Vilka parametrar som ska visas. Välj â€specific†för sÃ¥dana som hör till det här formatet, â€base†för sÃ¥dana som är tillgängliga för alla format, och â€all†för samtliga parametrar.",
+ "smw-paramdesc-sort": "Egenskap som resultatet ska sorteras efter",
+ "smw-paramdesc-order": "Hur ska resultaten sorteras?",
+ "smw-paramdesc-searchlabel": "Text för att visa ytterligare resultat",
+ "smw-paramdesc-named_args": "Namnet på de argument som ska ges till mallen",
+ "smw-paramdesc-export": "Export-alternativ",
+ "smw-paramdesc-prettyprint": "â€Pretty-printâ€-version med indrag och radbrytningar",
+ "smw-paramdesc-source": "Alternativ frågekälla",
+ "smw-paramdesc-jsonsyntax": "JSON-syntax som ska användas",
+ "smw-printername-feed": "RSS- och Atom-feed",
+ "smw-paramdesc-feedtype": "Feed-typ",
+ "smw-paramdesc-feedtitle": "Texten som ska användas som rubrik för feeden",
+ "smw-paramdesc-feeddescription": "Texten som ska användas som beskrivning av feeden",
+ "smw-paramdesc-feedpagecontent": "Sidinnehåll som ska visas tillsammans med feeden",
+ "smw-label-feed-description": "$1 $2-feed",
+ "smw-paramdesc-mimetype": "Mediatypen (MIMI-typen) för utmatningsfilen",
+ "smw_iq_disabled": "Beklagar. Semantiska sökning har slagits av på den här wikin.",
+ "smw_iq_moreresults": "… ytterligare resultat",
+ "smw_parseerror": "Det angivna värdet förstods inte.",
+ "smw_notitle": "\"$1\" kan inte användas som sidnamn på den här wikin.",
+ "smw_noproperty": "â€$1†kan inte användas som namn pÃ¥ en egenskap pÃ¥ den här wikin.",
+ "smw_wrong_namespace": "Endast sidor i namnrymden \"$1\" tillåts här.",
+ "smw_manytypes": "Mer än en typ definierad för egenskapen.",
+ "smw_emptystring": "Tomma stränger accepteras inte.",
+ "smw_notinenum": "\"$1\" är inte i listan ($2) över [[Property:Allows value|tillåtna värden]] för egenskapen \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-list": "\"$1\" finns inte i listan ($2) över [[Property:Allows value|tillåtna värden]] för egenskapen \"$3\".",
+ "smw-datavalue-constraint-error-allows-value-range": "\"$1\" är inte inom intervallet för \"$2\" som specificeras av begränsningen [[Property:Allows value|tillåtna värden]] för egenskapen \"$3\".",
+ "smw_noboolean": "\"$1\" är inte ett giltigt booleskt värde (sant/falskt).",
+ "smw_true_words": "sant,s,ja,j",
+ "smw_false_words": "falskt,f,nej,n",
+ "smw_nofloat": "\"$1\" är inte ett tal.",
+ "smw_infinite": "Tal så stora som \"$1\" stödjs inte.",
+ "smw_unitnotallowed": "â€$1†är ingen giltigt mÃ¥ttenhet för den här egenskapen.",
+ "smw_nounitsdeclared": "Inga måttenheter har definierats för den här egenskapen.",
+ "smw_novalues": "Inga värden angivna.",
+ "smw_nodatetime": "Datumet \"$1\" förstods inte.",
+ "smw_toomanyclosing": "\"$1\" uppträder för många gånger i efterfrågningen.",
+ "smw_noclosingbrackets": "Användningen av \"<nowiki>[[</nowiki>\" i din efterfrågning stängdes inte av \"]]\".",
+ "smw_misplacedsymbol": "Symbolen \"$1\" användes på en plats där den inte är användbar.",
+ "smw_unexpectedpart": "Delen \"$1\" av efterfrågningen förstods inte.\nVissa resultat kan bli oväntade.",
+ "smw_emptysubquery": "Någon underfråga har inget giltigt villkor.",
+ "smw_misplacedsubquery": "Någon underfråga användes på ett ställe där inga underfrågor tillåts.",
+ "smw_valuesubquery": "UnderfrÃ¥gor stöds inte för värden pÃ¥ egenskapen “$1â€.",
+ "smw_badqueryatom": "Någon del “<nowiki>[[…]]</nowiki>†av frågan förstods inte.",
+ "smw_propvalueproblem": "Värdet på egenskap “$1†förstods inte.",
+ "smw_noqueryfeature": "För någon frågefunktion saknades det stöd i denna wikin och delar av frågan hoppades över ($1).",
+ "smw_noconjunctions": "För konjunktioner i frågor saknas det stöd i denna wikin och delar av frågan hoppades över ($1).",
+ "smw_nodisjunctions": "För disjunktioner i frågor saknas det stöd i denna wikin och delar av frågan hoppades över ($1).",
+ "smw_querytoolarge": "Följande {{PLURAL:$2|frågevillkor|$2 frågevillkor}} kunde inte tas hänsyn till på grund av wikins begränsningar i frågestorlek eller -djup: <code>$1</code>.",
+ "smw_notemplategiven": "För att den här frågan ska fungera behöver du ange ett värde på parametern \"template\".",
+ "smw_db_sparqlqueryproblem": "Svaren på frågan kunde inte fås från SPARQL-databasen. Det här felet kan vara tillfälligt eller också så kan det bero på ett permanent fel med databasen.",
+ "smw_db_sparqlqueryincomplete": "Den här frågan visade sig vara för komplex, och har arbetet med att besvara den har avbrutits. En del resultat kan saknas. Försök om möjligt med en enklare fråga.",
+ "smw_type_header": "Egenskaper av typen “$1â€",
+ "smw_typearticlecount": "Visar $1 {{PLURAL:$1|egenskap|egenskaper}} som använder den här typen.",
+ "smw_attribute_header": "Sidor som använder egenskapen \"$1\"",
+ "smw_attributearticlecount": "Visar $1 {{PLURAL:$1|sida|sidor}} som använder den här egenskapen.",
+ "smw-propertylist-subproperty-header": "Underegenskaper",
+ "smw-propertylist-redirect-header": "Synonymer",
+ "smw-propertylist-count": "Visar $1 {{PLURAL:$1|relaterad entitet|relaterade entiteter}}.",
+ "smw-propertylist-count-with-restricted-note": "Visar $1 {{PLURAL:$1|relaterad entitet|relaterade entiteter}} (fler finns tillgängliga men antalet som visas begränsas till \"$2\").",
+ "smw-propertylist-count-more-available": "Visar $1 {{PLURAL:$1|relaterad entitet|relaterade entiteter}} (fler finns tillgängliga).",
+ "exportrdf": "Exportera sidor till RDF",
+ "smw_exportrdf_docu": "Den här sidan låter dig hämta data från en sida i RDF-format.\nSkriv sidtitlar i textrutan härunder för att exportera sidor (en titel per rad).",
+ "smw_exportrdf_recursive": "Exportera alla relaterade sidor rekursivt.\nObservera att resultatet kan bli stort!",
+ "smw_exportrdf_backlinks": "Exportera också alla sidor som refererar till de exporterade sidorna.\nSkapar en RDF som kan gås igenom.",
+ "smw_exportrdf_lastdate": "Exportera inte sidor som inte ändrats efter den uppgivna tidpunkten.",
+ "smw_exportrdf_submit": "Exportera",
+ "uriresolver": "URI-lösare",
+ "properties": "Egenskaper",
+ "smw_properties_docu": "Följande egenskaper används i wikin",
+ "smw_property_template": "$1 av typen $2 ($3 {{PLURAL:$3| gång| gånger}})",
+ "smw_propertylackspage": "Alla egenskaper ska beskrivas av en sida!",
+ "smw_propertylackstype": "Ingen typ specificerades för denna egenskap (antar typ $1 tills vidare).",
+ "smw_propertyhardlyused": "Denna egenskap används knappt i wikin!",
+ "smw-property-name-invalid": "Egenskapen $1 kan inte användas (ogiltigt egenskapsnamn).",
+ "smw-property-name-reserved": "\"$1\" har listats som ett reservat namn och bör inte användas som en egenskap. Följande [https://www.semantic-mediawiki.org/wiki/Help:Property_naming hjälpsida] kan innehålla information om varför detta namn reserverades.",
+ "smw-sp-property-searchform": "Visa egenskaper som innehåller:",
+ "smw-sp-property-searchform-inputinfo": "Indata är skiftlägeskänsligt och, när den används för filtrering, visas endast egenskaper som matchar villkoret.",
+ "smw-special-property-searchform": "Visa egenskaper som innehåller:",
+ "smw-special-property-searchform-inputinfo": "Indata är skiftlägeskänsligt och, när den används för filtrering, visas endast egenskaper som matchar villkoret.",
+ "smw-special-property-searchform-options": "Alternativ",
+ "smw-special-wantedproperties-filter-label": "Filter:",
+ "smw-special-wantedproperties-filter-none": "Ingen",
+ "smw-special-wantedproperties-filter-unapproved": "Inte godkänd",
+ "smw-special-wantedproperties-filter-unapproved-desc": "Filtreringsalternativ som används i anslutning med auktoriseringsläget.",
+ "concepts": "Begrepp",
+ "smw-special-concept-docu": "Ett [https://www.semantic-mediawiki.org/wiki/Help:Concepts koncept] är ett slags â€dynamisk katagoriâ€, det vill säga en samling sidor som skapas automatiskt utifrÃ¥n en semantisk sökning",
+ "smw-special-concept-header": "Lista över begrepp",
+ "smw-special-concept-count": "Följande {{PLURAL:$1|begrepp|$1 begrepp}} {{PLURAL:$1|är}} listade.",
+ "smw-special-concept-empty": "Inget begrepp hittades.",
+ "unusedproperties": "Oanvända egenskaper",
+ "smw-unusedproperties-docu": "Denna sida listar [https://www.semantic-mediawiki.org/wiki/Unused_properties oanvända egenskaper] som finns fastän ingen annan sida använder dem. For en annan vy, se specialsidorna [[Special:Properties|alla]] eller [[Special:WantedProperties|önskade egenskaper]].",
+ "smw-unusedproperty-template": "$1 av typen $2",
+ "wantedproperties": "Önskade egenskaper",
+ "smw-wantedproperties-docu": "Denna sida listar [https://www.semantic-mediawiki.org/wiki/Wanted_properties önskade egenskaper] som används i wikin men har ännu inte en sida som beskriver dem. För en annan vy, se specialsidorna [[Special:Properties|alla]] eller [[Special:UnusedProperties|oanvända egenskaper]].",
+ "smw-wantedproperty-template": "$1 (använd $2 {{PLURAL:$2|gång|gånger}})",
+ "smw-special-wantedproperties-docu": "Denna sida listar [https://www.semantic-mediawiki.org/wiki/Wanted_properties önskade egenskaper] som används i wikin men har ännu inte en sida som beskriver dem. För en annan vy, se specialsidorna [[Special:Properties|alla]] eller [[Special:UnusedProperties|oanvända egenskaper]].",
+ "smw-special-wantedproperties-template": "$1 (används $2 {{PLURAL:$2|gång|gånger}})",
+ "smw_purge": "Uppdatera",
+ "smw-purge-failed": "Uppdatering misslyckades",
+ "types": "Typer",
+ "smw_types_docu": "Lista över [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tillgängliga datatyper] där varje [https://www.semantic-mediawiki.org/wiki/Help:Datatype typ] representerar en unik uppsättning attribut för att beskriva ett värde i termer av hur det lagras och visas, som ärvs av en tilldelad egenskap.",
+ "smw-special-types-no-such-type": "Den angivna datatypen finns inte",
+ "smw-statistics": "Semantisk statistik",
+ "smw-statistics-property-instance": "Egenskaps{{PLURAL:$1|värde|värden}} (totalt)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|Egenskap|Egenskaper}}]] (totalt)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|Egenskap|Egenskaper}} (totalt)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|Egenskap|Egenskaper}}]] (används med minst ett värde)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Egenskap|Egenskaper}} (som har en egen sida)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Egenskap|Egenskaper}} (som har tilldelats en datatyp)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Fråga|Frågor}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Förfråga|Förfrågningar}}]]",
+ "smw-statistics-query-size": "Frågestorlek",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Koncept}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Koncept}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Underobjekt}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Underobjekt}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Datatyp|Datatyper}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Egenskapsvärde|Egenskapsvärden}} ([[Special:ProcessingErrorList|{{PLURAL:$1|felaktig annotering|felaktiga annoteringar}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Egenskapsvärde|Egenskapsvärden}} ({{PLURAL:$1|felaktig kommentar|felaktiga kommentarer}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|Föråldrad entitet (märkt för borttagning)|Föråldrade entiteter (märkta för borttagning)}}",
+ "smw_uri_doc": "The URI resolver implements the [$1 W3C TAG finding on httpRange-14].\nIt takes care that humans do not turn into websites.",
+ "ask": "Semantisk sökning",
+ "smw_ask_sortby": "Sortera efter kolumn (valfritt)",
+ "smw_ask_ascorder": "Stigande",
+ "smw_ask_descorder": "Fallande",
+ "smw-ask-order-rand": "Slumpartad",
+ "smw_ask_submit": "Sök",
+ "smw_ask_editquery": "Redigera fråga",
+ "smw_add_sortcondition": "[Lägg till sorteringsvillkor]",
+ "smw-ask-sort-add-action": "Lägg till sorteringsvillkor",
+ "smw_ask_hidequery": "Dölj fråga (kompakt vy)",
+ "smw_ask_help": "Frågehjälp",
+ "smw_ask_queryhead": "Villkor",
+ "smw_ask_printhead": "Urval för utskrift",
+ "smw_ask_printdesc": "(lägg till varje egenskap på en egen rad)",
+ "smw_ask_format_as": "Formatera som:",
+ "smw_ask_defaultformat": "standard",
+ "smw_ask_otheroptions": "Andra alternativ",
+ "smw-ask-otheroptions-info": "Den här delen innehÃ¥ller alternativ som pÃ¥verkar â€printout statementsâ€. Beskrivningar av parametrarna visas när du för muspekaren över dem.",
+ "smw-ask-otheroptions-collapsed-info": "Använd plus-tecknet för att visa alla tillgänliga alternativ.",
+ "smw_ask_show_embed": "Visa inbäddad kod",
+ "smw_ask_hide_embed": "Göm inbäddad kod",
+ "smw_ask_embed_instr": "Använd koden nedan för att lägga in den här frågan i en wiki-sida.",
+ "smw-ask-delete": "Ta bort",
+ "smw-ask-sorting": "Sortering",
+ "smw-ask-options": "Alternativ",
+ "smw-ask-options-sort": "Sorteringsalternativ",
+ "smw-ask-format-options": "Format och alternativ",
+ "smw-ask-parameters": "Parametrar",
+ "smw-ask-search": "Sök",
+ "smw-ask-debug": "Felsökning",
+ "smw-ask-debug-desc": "Skapar felsökningsinformation för fråga",
+ "smw-ask-no-cache": "Inaktivera frågecache",
+ "smw-ask-no-cache-desc": "Resultat utan frågecache",
+ "smw-ask-result": "Resultat",
+ "smw-ask-empty": "Rensa bort alla poster",
+ "smw-ask-download-link-desc": "Ladda ned frågeresultat i formatet $1",
+ "smw-ask-format": "Format",
+ "smw-ask-format-selection-help": "Hjälp med det valda formatet $1.",
+ "smw-ask-condition-change-info": "Villkoret förändrades och sökmotorn behöver köra frågan igen för att producera resultat som matchar de nya kraven.",
+ "smw-ask-input-assistance": "Inmatningassistans",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Inmatnings assistans] tillhandahålls för utskrifter, sortering och villkorsfält. Villkorsfältet kräver att en av följande prefix används:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> för att hämta egenskapsförslag (t.ex. <code>[[p:Har …</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> för att hämta kategoriförslag",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> för att hämta konceptsökningar",
+ "smw-ask-query-search-info": "Frågan <code><nowiki>$1</nowiki></code> besvarades av {{PLURAL:$3|1=<code>$2</code> (från cachen)|<code>$2</code> (från cachen)|<code>$2</code>}} i $4 {{PLURAL:$4|sekund|sekunder}}.",
+ "searchbyproperty": "Sökning per egenskap",
+ "processingerrorlist": "Bearbetar fellistan",
+ "smw-processingerrorlist-intro": "Följande lista tillhandahåller en översikt över bearbetningsfelen som uppstod i anslutning med [https://www.semantic-mediawiki.org/ Semantic MediaWiki]. Det är rekommenderat att övervaka denna lista då och då och korrigera annoteringar om ogiltiga värden.",
+ "smw_sbv_docu": "Sök efter alla sidor som har en given egenskap och värde.",
+ "smw_sbv_novalue": "Skriv in ett giltigt värde för egenskapen, eller visa alla egenskapsvärden för â€$1â€.",
+ "smw_sbv_displayresult": "En lista över alla sidor som har egenskapen â€$1†med värdet â€$2â€",
+ "smw_sbv_displayresultfuzzy": "En lista över alla sidor som har egenskapen â€$1†med värdet â€$2â€.\nEftersom det bara finns ett fÃ¥tal resultat, visas även snarlika värden.",
+ "smw_sbv_property": "Egenskap:",
+ "smw_sbv_value": "Värde:",
+ "smw_sbv_submit": "Sök",
+ "browse": "Bläddra igenom wikin",
+ "smw_browselink": "Bläddra genom egenskaper",
+ "smw_browse_article": "Skriv namnet på sidan du vill börja bläddra från.",
+ "smw_browse_go": "Visa",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "Visa inkommande egenskaper",
+ "smw_browse_hide_incoming": "Dölj inkommande egenskaper",
+ "smw_browse_no_outgoing": "Denna sida har inga egenskaper.",
+ "smw_browse_no_incoming": "Inga egenskaper länkar till den här sidan.",
+ "smw-browse-show-group": "Visa grupper",
+ "smw-browse-hide-group": "Dölj grupper",
+ "smw_inverse_label_default": "$1 av",
+ "smw_inverse_label_property": "Benämning på inverterad egenskap",
+ "pageproperty": "Sidegenskapssökning",
+ "smw_pp_docu": "Ange antingen en sida och egenskap, eller bara en egenskap för att hämta alla tilldelade värden.",
+ "smw_pp_from": "Från sidan:",
+ "smw_pp_type": "Egenskap:",
+ "smw_pp_submit": "Hitta resultat",
+ "smw_result_prev": "Föregående",
+ "smw_result_next": "Nästa",
+ "smw_result_results": "Resultat",
+ "smw_result_noresults": "Beklagar, inga resultat.",
+ "smwadmin": "Administrativa och underhållsorienterande funktioner",
+ "smw-admin-statistics-job-title": "Jobbstatistik",
+ "smw-admin-statistics-job-docu": "Jobbstatistiken visar information om schemalagda Semantic MediaWiki-jobb som inte ännu har utförts. Antalet jobb kan vara något felaktiga eller innehålla misslyckade försök, läs [https://www.mediawiki.org/wiki/Manual:Job_queue manualen] för mer information.",
+ "smw-admin-setupsuccess": "Lagringsmotorn har satts upp.",
+ "smw_smwadmin_return": "GÃ¥ tillbaka till $1",
+ "smw_smwadmin_updatestarted": "Nu har en ny process för att uppdatera semantiska data påbörjats.\nAlla semantiska data kommer att återuppbyggas och repareras där så behövs.\nDu kan följa processen på den här specialsidan.",
+ "smw_smwadmin_updatenotstarted": "Process för att uppdatera pågår.\nStartar ingen ny process.",
+ "smw_smwadmin_updatestopped": "Alla uppdateringsprocesser har avslutats.",
+ "smw_smwadmin_updatenotstopped": "För att avsluta uppdaterings-processen måste du aktivera kryss-rutan.",
+ "smw-admin-docu": "Denna specialsida hjälper dig under installation, uppgradering, underhåll och användning av <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> och tillhandahåller också ytterligare administrativa funktioner och uppgifter, såväl som statistik.\nKom ihåg att säkerhetskopiera värdefull data innan du kör administrativa funktioner!",
+ "smw-admin-environment": "Programvarumiljö",
+ "smw-admin-db": "Inställning av databas",
+ "smw-admin-dbdocu": "Semantic's Mediawiki kräver sin egen databas-struktur (och är oberoende från Mediawiki vilket inte påverkar resten av Mediawiki-installationen) för att kunna lagra semantic-datan.\nDet här installationsfunktionen kan exekveras flerfaldiga gånger utan att det gör någon skada, men det behövs endast en gång i installation eller uppgradering.",
+ "smw-admin-permissionswarn": "Om operationen misslyckas och visar SQL-fel så har din användare för databasen (kolla inställningarna i filen \"LocalSettings.php\") förmodligen inte tillräckliga rättigheter.\nDet finns tre sätt att åtgärda detta: Ge rättigheter till användaren att skapa och radera tabeller, ändra temporärt användaren till ''root'' i \"LocalSettings.php\", eller använd underhålls-scriptet <code>setupStore.php</code>, vilken kan använda rättigheterna för en administratör.",
+ "smw-admin-dbbutton": "Initialisera eller uppgradera tabeller",
+ "smw-admin-announce": "Meddela att din wiki finns",
+ "smw-admin-announce-text": "Om din wiki är offentligt kan du registrera den på <a href=\"https://wikiapiary.com\">WikiApiary</a>, wikin som listar upp wikis.",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> kommer ta bort eller ersätta följande {{PLURAL:$2|val}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> rekommenderas inte och kommer tas bort i $2",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|val}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> ersätts av <code>$2</code>",
+ "smw-admin-deprecation-notice-title-notice": "Kommande ändringar",
+ "smw-admin-deprecation-notice-title-replacement": "Ersatta eller omdöpta inställningar",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Följande avsnitt innehåller inställningar som har döpts om eller annars förändrats och rekommenderas att uppdatera deras namn eller format omedelbart.",
+ "smw-admin-deprecation-notice-title-removal": "Borttagna inställningar",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Inställningarna som listas har tagits bort i en tidigare utgåva men användning har upptäckts på denna wiki.",
+ "smw-smwadmin-refresh-title": "Datareparation och -uppdatering",
+ "smw_smwadmin_datarefresh": "Datauppbyggning",
+ "smw_smwadmin_datarefreshdocu": "Det är möjligt att återställa all Semantic MediaWiki-data baserat på det aktuella innehållet för din wiki.\nDetta kan vara användbart för att reparera eller fräscha upp data om det interna formatet har ändrats på grund av programuppdateringar.\nUppdateringen utförs sida för sida och blir inte klar omedelbart.\nDet följande visar om en uppdatering pågår och tillåter dig att starta eller stoppa uppdateringar (såvida inte denna finess har stängts av av administratören).",
+ "smw_smwadmin_datarefreshprogress": "<strong>En uppdatering pågår redan.</strong>\nDet är normalt att uppdateringen fortskrider långsamt, eftersom den endast uppdaterar data i små bitar åt gången varje gång en användare använder wikin.\nFör att avsluta uppdateringen snabbare, kan du köra MediaWiki-scriptet <code>runJobs.php</code> (använd valet <code>--maxjobs 1000</code> för att begränsa antalet uppdateringar per körning).\nUppskattning av hur långt uppdateringen har kommit:",
+ "smw_smwadmin_datarefreshbutton": "Schemalägg återuppbyggnad av data",
+ "smw_smwadmin_datarefreshstop": "Stoppa denna uppdatering",
+ "smw_smwadmin_datarefreshstopconfirm": "Ja, jag är {{GENDER:$1|säker}}.",
+ "smw-admin-support": "FÃ¥ support",
+ "smw-admin-supportdocu": "Diverse resurser tillhandahålls för kan hjälpa dig, om du får problem:",
+ "smw-admin-installfile": "Om du har problem med din installation, börja med att läsa instruktionerna i <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">installationsfilen</a> och <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">installationssidan</a>.",
+ "smw-admin-smwhomepage": "Den kompletta användardokumentationen till Semantic MediaWiki finns på <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Fel kan rapporteras på <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">ärendehanteraren</a>, sidan för att <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">rapportera buggar</a> tillhandahåller lite hjälp om hur man skriver en effektiv ärenderapport.",
+ "smw-admin-questions": "Om du har ytterligare frågor eller förslag, diskutera dem på <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">Semantic MediaWiki användarsändlista</a>!",
+ "smw-admin-other-functions": "Andra funktioner",
+ "smw-admin-supplementary-section-title": "Extra funktioner",
+ "smw-admin-supplementary-section-subtitle": "Kärnfunktioner",
+ "smw-admin-supplementary-section-intro": "Vissa av de listades funktionerna i detta avsnitt kan vara begränsade och är därför otillgängliga på denna wiki.",
+ "smw-admin-supplementary-settings-title": "Konfigurationsinställningar",
+ "smw-admin-supplementary-operational-statistics-title": "Driftstatistik",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> visar en utvidgad samling statistik",
+ "smw-admin-supplementary-duplookup-title": "Duplicera entiteter",
+ "smw-admin-supplementary-operational-statistics-cache-title": "Cachestatistik",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> visar cacherelaterad statistik",
+ "smw-admin-supplementary-elastic-functions": "Tillgängliga funktioner",
+ "smw-admin-supplementary-elastic-settings-title": "Inställningar",
+ "smw-admin-supplementary-elastic-mappings-summary": "Sammanfattning",
+ "smw-admin-supplementary-elastic-nodes-title": "Noder",
+ "smw-admin-supplementary-elastic-statistics-title": "Statistik",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Uppdateringsintervall: $1",
+ "smw-list-count": "Listan innehåller $1 {{PLURAL:$1|element}}.",
+ "smw-list-count-from-cache": "Listan innehåller $1 {{PLURAL:$1|element}} och hämtades från cacheminnet (UTC: $2).",
+ "smw-property-label-similarity-intro": "<u>$1</u> beräknar liknelser för befintliga egenskapsetiketter",
+ "smw-property-label-similarity-threshold": "Gränsvärde:",
+ "smw-property-label-similarity-type": "Visa typ-ID",
+ "smw-property-label-similarity-noresult": "Inga resultat hittades för de valda alternativen.",
+ "smw_adminlinks_datastructure": "Datastruktur",
+ "smw_adminlinks_displayingdata": "Datavisning",
+ "smw_adminlinks_inlinequerieshelp": "Hjälp för inbäddade frågor",
+ "smw-page-indicator-usage-count": "Uppskattat [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count användningsantal]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|Användardefinierad|Systemdefinierad}} egenskap",
+ "smw-property-indicator-last-count-update": "Uppskattat användningsantal\nSenast uppdaterad: $1",
+ "smw-createproperty-isproperty": "Det här är en egenskap av typen $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Det tillåtna värdet|De tillåtna värdena}} för den här egenskapen är:",
+ "smw-paramdesc-category-delim": "Skiljetecken",
+ "smw-paramdesc-category-template": "Mall att formatera resultatet med",
+ "smw-paramdesc-category-userparam": "Parameter till mallen",
+ "smw-info-par-message": "Meddelande att skriva ut.",
+ "smw-info-par-icon": "Ikon att visa, antingen \"info\" eller \"warning\".",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "Allmänna alternativ",
+ "prefs-ask-options": "Special:Frågealternativ",
+ "smw-prefs-intro-text": "Alternativen här nedanför är hämtade från [https://www.semantic-mediawiki.org/ Semantic MediaWiki] (och relaterade tillägg) i syfte att tillhandahålla inställningar för utvalda funktioner. På [https://www.semantic-mediawiki.org/wiki/Help:User_preferences hjälpsidan] finns en mer detaljerad beskrivning.",
+ "smw-prefs-ask-options-tooltip-display": "Visa parameterns text som inforuta",
+ "smw-prefs-general-options-disable-editpage-info": "Inaktivera introduktionstexten på redigeringssidan.",
+ "smw-ui-tooltip-title-property": "Egenskap",
+ "smw-ui-tooltip-title-quantity": "Enhetskonvertering",
+ "smw-ui-tooltip-title-info": "Information",
+ "smw-ui-tooltip-title-service": "Service-länkar",
+ "smw-ui-tooltip-title-warning": "Varning",
+ "smw-ui-tooltip-title-error": "Fel",
+ "smw-ui-tooltip-title-parameter": "Parameter",
+ "smw-ui-tooltip-title-event": "Händelse",
+ "smw-ui-tooltip-title-note": "Anteckning",
+ "smw-ui-tooltip-title-legend": "Förklaring",
+ "smw-ui-tooltip-title-reference": "Referens",
+ "smw_unknowntype": "Typen \"$1\" för denna egenskap är ogiltig",
+ "smw-concept-cache-text": "Det här konceptet har totalt {{PLURAL:$1|en sida|$1 sidor}}, och uppdaterades senast $3, $2.",
+ "smw_concept_header": "Sidor som hör till konceptet \"$1\"",
+ "smw_conceptarticlecount": "Visar {{PLURAL:$1|en sida|$1 sidor}}",
+ "smw-qp-empty-data": "Önskad data kunde inte visas på grund av att vissa urvalskriterier är otillräckliga.",
+ "right-smw-admin": "Tillgång till administrationsåtgärder (Semantisk MediaWiki)",
+ "right-smw-pageedit": "Redigeringsåtkomst för sidor med annoteringen <code>Är redigeringsskyddad</code> (Semantic MediaWiki)",
+ "restriction-level-smw-pageedit": "skyddad (endast berättigade användare)",
+ "action-smw-patternedit": "redigera reguljära uttryck som Semantic MediaWiki använder",
+ "group-smwadministrator": "Administratörer (Semantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|administratör (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:Administratörer (Semantic MediaWiki)",
+ "group-smwcurator": "Kuratorer (Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|kurator (Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Kuratorer (Semantic MediaWiki)",
+ "action-smw-admin": "komma åt Semantisk MediaWikis administrationsåtgärder",
+ "smw-property-predefined-default": "\"$1\" är en fördefinierad egenskap.",
+ "smw-property-predefined-ask": "\"$1\" är en fördefinierad egenskap som representerar metainformation (i form av ett [https://www.semantic-mediawiki.org/wiki/Subobject underobjektet]) om individuella förfrågningar.",
+ "smw-property-predefined-asksi": "$1 är en fördefinierad egenskap som samlar antalet villkor i en förfrågan.",
+ "smw-sp-properties-docu": "Denna sida visar [https://www.semantic-mediawiki.org/wiki/Property egenskaper] som är tillgängliga och när filtrerad, visas endast användardefinierade egenskaper som matchar villkoret. För en differentierad vy, se specialsidan [[Special:UnusedProperties|oanvända egenskaper]] eller [[Special:WantedProperties|önskade egenskaper]].",
+ "smw-sp-properties-cache-info": "Listad data har hämtats från [https://www.semantic-mediawiki.org/wiki/Caching cache], och uppdaterades sist $1.",
+ "smw-sp-properties-header-label": "Lista med egenskaper",
+ "smw-admin-settings-docu": "Visar en lista över alla standard- och lokaliserade inställningar som är relevanta för den semantiska MediaWiki-miljön. För detaljer om individuella inställningar, rådfråga hjälpsidan för [https://www.semantic-mediawiki.org/wiki/Help:Configuration konfiguration].",
+ "smw-sp-admin-settings-button": "Generera lista med inställningar",
+ "smw-admin-idlookup-title": "Kolla upp",
+ "smw-admin-idlookup-docu": "Visar detaljer om ett internt objekt-Id som representerar enskilda enheter (wikisida, underobjekt etc.) i Semantisk MediaWiki.",
+ "smw-admin-iddispose-title": "Förfogande",
+ "smw-admin-iddispose-references": "ID \"$1\" har {{PLURAL:$2|ingen|minst en}} aktiv referens till ID \"$1\".",
+ "smw-admin-idlookup-input": "Sök:",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "Översikt",
+ "smw-admin-tab-maintenance": "Underhåll",
+ "smw-admin-tab-registry": "Register",
+ "smw-admin-maintenance-no-description": "Ingen beskrivning.",
+ "smw-admin-maintenance-script-section-title": "Lista över tillgängliga underhållsskript",
+ "smw-admin-maintenance-script-section-intro": "Följande underhållsskript kräver en administratör och åtkomst till kommandoraden för att kunna utföra listade skript.",
+ "smw-livepreview-loading": "Laddar…",
+ "smw-sp-searchbyproperty-description": "Denna sida erbjuder ett enkelt [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces navigeringsgränssnitt] för att hitta entiteter som beskrivs av en egenskap och ett namngivet värde. Andra tillgängliga sökgränssnitt inkluderar [[Special:PageProperty|sidegenskapssök]] och [[Special:Ask|ask-förfrågningsbyggare]].",
+ "smw-sp-searchbyproperty-resultlist-header": "Lista med resultat",
+ "smw-sp-searchbyproperty-nonvaluequery": "En lista över värden som har tilldelats egenskapen \"$1\".",
+ "smw-sp-searchbyproperty-valuequery": "En lista över sidor som har egenskapen \"$1\" med värdet \"$2\" annoterat.",
+ "smw-datavalue-number-nullnotallowed": "\"$1\" returnerades med \"NULL\" som inte tillåts som en siffra.",
+ "smw-search-input": "Inmatning och sökning",
+ "smw-search-syntax": "Syntax",
+ "smw-search-profile": "Utökad",
+ "smw-search-profile-sort-best": "Bästa träff",
+ "smw-search-profile-sort-recent": "Senaste",
+ "smw-search-profile-sort-title": "Titel",
+ "smw-search-profile-extended-help-query": "Länk till: $1",
+ "smw-search-profile-extended-help-query-link": "(För mer detaljer $1).",
+ "smw-search-profile-extended-help-find-forms": "tillgängliga formulär",
+ "smw-search-profile-extended-section-sort": "Sortera efter",
+ "smw-search-profile-extended-section-form": "Formulär",
+ "smw-search-profile-extended-section-search-syntax": "Sökinmatning",
+ "smw-search-profile-extended-section-namespace": "Namnrymd",
+ "smw-search-profile-extended-section-query": "Förfrågan",
+ "smw-search-profile-link-caption-query": "se",
+ "smw-search-show": "Visa",
+ "smw-search-hide": "Dölj",
+ "log-name-smw": "Semantic MediaWiki-logg",
+ "log-show-hide-smw": "$1 Semantic MediaWiki-logg",
+ "logeventslist-smw-log": "Semantic MediaWiki-logg",
+ "log-description-smw": "Aktiviteter för [https://www.semantic-mediawiki.org/wiki/Help:Logging aktiverade händelstyper] som har rapporterats av Semantic MediaWiki och dess underkomponenter.",
+ "logentry-smw-maintenance": "Underhållsrelaterade händelser som utelämnas av Semantic MediaWiki",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1 importering]]",
+ "smw-datavalue-import-invalid-value": "\"$1\" är inte ett giltig format och förväntas vara \"namnrymd\":\"identifierare\" (d.v.s \"foaf:namn\").",
+ "smw-datavalue-import-invalid-format": "Förväntade att strängen \"$1\" skulle delas upp i fyra delar men formatet uppfattades inte.",
+ "smw-property-predefined-impo": "\"$1\" är en fördefinierade egenskap som beskriver ett förhållande till en [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary importerad ordlista] och tillhandahålls av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "\"$1\" är en fördefinierad egenskap som beskriver [[Special:Types|datatypen]] av en egenskap och tillhandahålls av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-datavalue-property-invalid-character": "\"$1\" innehåller tecknet \"$2\" som del av en egenskapsetikett och har klassificerats som ogiltig.",
+ "smw-datavalue-property-invalid-chain": "\"$1\" tillåts inte användas som en egenskapskedja under kommentarsprocessen.",
+ "smw-datavalue-restricted-use": "Datavärdet \"$1\" har märkts för begränsad användning.",
+ "smw-datavalue-invalid-number": "\"$1\" kan inte tolkas som ett nummer.",
+ "smw-types-list": "Lista över datatyper",
+ "smw-types-default": "\"$1\" är en inbyggd datatyp.",
+ "smw-type-boo": "\"$1\" är en primitiv datatyp för att beskriva ett värde som kan vara sant/falskt.",
+ "smw-type-tab-properties": "Egenskaper",
+ "smw-type-tab-types": "Typer",
+ "smw-type-tab-errors": "Fel",
+ "smw-type-primitive": "Grundläggande",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-types-extra-mlt-lcode": "Datatypen {{PLURAL:$2|kräver inte}} en språkkod (t.ex. {{PLURAL:$2|ett värde utan en språkkod accepteras inte}}).",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|sekund|sekunder}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|sekund|sekunder}}",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|sekund|sekunder}}",
+ "smw_allows_pattern": "Den här sidan är förväntad att innehålla en lista av referenser (följt av [https://en.wikipedia.org/wiki/Regular_expression regular expressions]) och att bli tillgänglig genom att använda [[Property:Allows pattern|Allows pattern]] egenskap.",
+ "smw-datavalue-time-invalid-date-components-empty": "\"$1\" innehåller några tomma komponenter.",
+ "smw-datavalue-time-invalid-date-components-three": "\"$1\" innehåller mer än tre komponenter.",
+ "smw-datavalue-time-invalid-ampm": "\"$1\" innehåller \"$2\" som timmeselement som är ogiltigt för en 12-timmarskonvention.",
+ "smw-datavalue-external-formatter-invalid-uri": " \"$1\" är en ogiltig URL.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "\"$1\" kan inte användas som en föredragen etikett eftersom språket \"$2\" är redan tilldelat till etiketten \"$3\".",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Kopiera länk till urklipp",
+ "smw-data-lookup": "Hämtar data...",
+ "smw-no-data-available": "Ingen tillgänglig data.",
+ "protect-level-smw-pageedit": "Tillåt endast användare med sidredigeringsbehörighet (Semantic MediaWiki)",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki har uppdaterat skyddsstatusen enligt egenskapen `Är redigeringsskyddad`.",
+ "smw-edit-protection-enabled": "Redigeringsskyddad (Semantic MediaWiki)",
+ "smw-patternedit-protection": "Denna sida skyddas och kan endast redigeras av användare med den lämpliga [https://www.semantic-mediawiki.org/wiki/Help:Permissions behörigheten <code>smw-patternedit</code>].",
+ "smw-property-predefined-edip": "\"$1\" är en fördefinierad egenskap tillhandahållen av [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] för att indikera om redigering är skyddad eller inte.",
+ "smw-format-datatable-emptytable": "Ingen data tillgänglig i tabellen",
+ "smw-format-datatable-info": "Visar _START_ till _END_ av _TOTAL_ poster",
+ "smw-format-datatable-infoempty": "Visar 0 till 0 av 0 poster",
+ "smw-format-datatable-infofiltered": "(filtrerat från _MAX_ totala poster)",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-lengthmenu": "Visar _MENU_ poster",
+ "smw-format-datatable-loadingrecords": "Läser in...",
+ "smw-format-datatable-processing": "Bearbetar...",
+ "smw-format-datatable-search": "Sök:",
+ "smw-format-datatable-zerorecords": "Inga matchande poster hittades",
+ "smw-format-datatable-first": "Första",
+ "smw-format-datatable-last": "Sista",
+ "smw-format-datatable-next": "Nästa",
+ "smw-format-datatable-previous": "Föregående",
+ "smw-format-datatable-sortascending": ": aktivera för att sortera kolumnen stigande",
+ "smw-format-datatable-sortdescending": ": aktivera för att sortera kolumnen fallande",
+ "smw-format-datatable-toolbar-export": "Exportera",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "Kategorin \"$1\" innehåller en ogiltigt omdirigeringsmål till en namnrymd som inte är en kategori.",
+ "smw-postproc-queryref": "Sidan markerades för att uppdateras p.g.a. behov av efterbehandling.",
+ "smw-api-invalid-parameters": "Ogiltiga parametrar, \"$1\"",
+ "smw-property-page-list-count": "Visar $1 {{PLURAL:$1|sida|sidor}} som använder den här egenskapen.",
+ "smw-property-page-list-search-count": "Visar $1 {{PLURAL:$1|sida|sidor}} som använder den här egenskapen med det matchande värdet \"$2\".",
+ "smw-property-reserved-category": "Kategori",
+ "smw-category": "Kategori",
+ "smw-browse-property-group-title": "Egenskapsgrupp",
+ "smw-browse-property-group-label": "Etikett för egenskapsgrupp",
+ "smw-browse-property-group-description": "Beskrivning för egenskapsgrupp",
+ "smw-filter": "Filter",
+ "smw-section-expand": "Expandera avsnittet",
+ "smw-section-collapse": "Stäng avsnittet",
+ "smw-ask-format-help-link": "Formatet [https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]",
+ "smw-help": "Hjälp",
+ "smw-cheat-sheet": "Lathund",
+ "smw-personal-jobqueue-watchlist": "Bevakningslista (jobbkö)",
+ "smw-property-predefined-label-skey": "Sorteringsnyckel",
+ "smw-processing": "Bearbetar...",
+ "smw-types-title": "Typ: $1",
+ "smw-schema-error": "Valideringsfel",
+ "smw-schema-title": "Schema",
+ "smw-schema-type": "Typ",
+ "smw-schema-tag": "{{PLURAL:$1|Märke|Märken}}",
+ "smw-ask-title-keyword-type": "Nyckelordssökning",
+ "smw-ask-message-keyword-type": "Denna sökning matchar villkoret <code><nowiki>$1</nowiki></code>.",
+ "smw-parameter-missing": "Parametern \"$1\" saknas.",
+ "smw-property-tab-usage": "Användning",
+ "smw-property-tab-redirects": "Synonymer",
+ "smw-property-tab-subproperties": "Underegenskaper",
+ "smw-property-tab-errors": "Felaktiga tilldelningar",
+ "smw-concept-tab-list": "Lista",
+ "smw-concept-tab-errors": "Fel",
+ "smw-ask-tab-result": "Resultat",
+ "smw-ask-tab-extra": "Extra",
+ "smw-ask-tab-debug": "Felsökning",
+ "smw-ask-tab-code": "Kod"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/sw.json b/www/wiki/extensions/SemanticMediaWiki/i18n/sw.json
new file mode 100644
index 00000000..9ba38ffb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/sw.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Kwisha"
+ ]
+ },
+ "smw-ui-tooltip-title-legend": "Simulizi",
+ "smw-livepreview-loading": "Inapakizwa..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/szl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/szl.json
new file mode 100644
index 00000000..9e449150
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/szl.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Krol111"
+ ]
+ },
+ "browse": "Przeglůndańy artiklůw",
+ "smw-livepreview-loading": "Trwo uadowańy…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ta.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ta.json
new file mode 100644
index 00000000..46260578
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ta.json
@@ -0,0 +1,59 @@
+{
+ "@metadata": {
+ "authors": [
+ "Karthi.dr",
+ "Logicwiki",
+ "Shanmugamp7",
+ "TRYPPN"
+ ]
+ },
+ "smw_finallistconjunct": ", மறà¯à®±à¯à®®à¯",
+ "smw_printername_count": "எணà¯à®£à®¿à®•à¯à®•à¯ˆ à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯",
+ "smw_printername_csv": "CSV à®à®±à¯à®±à¯à®®à®¤à®¿",
+ "smw_printername_dsv": "DSV à®à®±à¯à®±à¯à®®à®¤à®¿",
+ "smw_printername_list": "படà¯à®Ÿà®¿à®¯à®²à¯",
+ "smw_printername_table": "படà¯à®Ÿà®¿à®¯à®²à¯",
+ "smw_printername_template": "வாரà¯à®ªà¯à®ªà¯à®°à¯",
+ "smw_printername_category": "பகà¯à®ªà¯à®ªà¯",
+ "smw_exportrdf_submit": "à®à®±à¯à®±à¯à®®à®¤à®¿ செயà¯",
+ "properties": "பணà¯à®ªà¯à®•à®³à¯",
+ "smw_ask_ascorder": "à®à®±à¯à®µà®°à®¿à®šà¯ˆ",
+ "smw_ask_descorder": "இறஙà¯à®•à¯à®µà®°à®¿à®šà¯ˆ",
+ "smw_ask_submit": "à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯ˆà®¤à¯ தேடà¯",
+ "smw_ask_defaultformat": "பொதà¯à®µà®¾à®©à®¤à¯",
+ "smw_ask_otheroptions": "மறà¯à®± விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯‡à®°à¯à®µà¯à®•à®³à¯",
+ "smw-ask-delete": "[நீகà¯à®•à¯]",
+ "smw-ask-sorting": "வரிசைபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®¤à®²à¯",
+ "smw_sbv_property": "உடமை:",
+ "smw_sbv_value": "மதிபà¯à®ªà¯:",
+ "smw_sbv_submit": "à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯ˆà®¤à¯ தேடà¯",
+ "browse": "விகà¯à®•à®¿à®¯à®¿à®²à¯ உலாவà¯",
+ "smw_browselink": "உடமைகளில௠உலாவà¯",
+ "smw_browse_article": "உலாவ ஆரமà¯à®ªà®¿à®•à¯à®• விரà¯à®®à¯à®ªà¯à®®à¯ பகà¯à®•à®¤à¯à®¤à®¿à®©à¯ பெயரை உளà¯à®³à®¿à®Ÿà®µà¯à®®à¯.",
+ "smw_browse_go": "செலà¯",
+ "smw_browse_show_incoming": "இஙà¯à®•à¯ இணைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³ உடமைகளைக௠காணà¯à®ªà®¿à®•à¯à®•à®µà¯à®®à¯",
+ "smw_browse_hide_incoming": "இஙà¯à®•à¯ இணைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà¯à®³à¯à®³ உடமைகளைக௠மறைகà¯à®•à®µà¯à®®à¯",
+ "smw_browse_no_outgoing": "இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ உடமைகள௠à®à®¤à¯à®®à®¿à®²à¯à®²à¯ˆ.",
+ "smw_browse_no_incoming": "இநà¯à®¤à®ªà¯ பகà¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯ உடமைகள௠à®à®¤à¯à®®à¯ இணைகà¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ.",
+ "smw_pp_from": "பகà¯à®•à®¤à¯à®¤à®¿à®²à®¿à®°à¯à®¨à¯à®¤à¯",
+ "smw_pp_type": "உடமை",
+ "smw_pp_submit": "à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯ˆà®¤à¯ தேடà¯",
+ "smw_result_prev": "à®®à¯à®¨à¯à®¤à¯ˆà®¯",
+ "smw_result_next": "அடà¯à®¤à¯à®¤à¯",
+ "smw_result_results": "à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯",
+ "smw_result_noresults": "à®®à¯à®Ÿà®¿à®µà¯à®•à®³à¯ à®à®¤à¯à®®à®¿à®²à¯à®²à¯ˆ.",
+ "smw_smwadmin_datarefreshbutton": "தரவà¯à®•à®³à¯ˆ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®• ஆரமà¯à®ªà®¿à®•à¯à®•à®µà¯à®®à¯",
+ "smw_smwadmin_datarefreshstop": "இநà¯à®¤ பà¯à®¤à¯à®ªà¯à®ªà®¿à®¤à¯à®¤à®²à¯ˆ நிறà¯à®¤à¯à®¤à®µà¯à®®à¯",
+ "smw_smwadmin_datarefreshstopconfirm": "ஆமà¯, நான௠உறà¯à®¤à®¿à®¯à®¾à®• உளà¯à®³à¯‡à®©à¯.",
+ "smw_smwadmin_support": "ஆதரவ௠பெறà¯à®¤à®²à¯",
+ "smw-ui-tooltip-title-property": "உடமை",
+ "smw-ui-tooltip-title-quantity": "அளவà¯",
+ "smw-ui-tooltip-title-info": "தகவலà¯",
+ "smw-ui-tooltip-title-service": "சேவை இணைபà¯à®ªà¯à®•à®³à¯",
+ "smw-ui-tooltip-title-warning": "பிழை",
+ "smw-ui-tooltip-title-parameter": "அளபà¯à®°à¯",
+ "smw-ui-tooltip-title-event": "நிகழà¯à®šà¯à®šà®¿",
+ "smw-ui-tooltip-title-note": "கà¯à®±à®¿à®ªà¯à®ªà¯",
+ "smw_unknowntype": "இநà¯à®¤ உடமையின௠வகை செலà¯à®²à®¾à®¤à®¤à¯",
+ "smw-livepreview-loading": "à®à®±à¯à®±à®ªà¯à®ªà®Ÿà¯à®•à®¿à®±à®¤à¯â€¦"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tcy.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tcy.json
new file mode 100644
index 00000000..598ea8a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tcy.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "VASANTH S.N."
+ ]
+ },
+ "browse": "ವಿಕಿನೠನಾಡà³â€à²²à³†"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/te.json b/www/wiki/extensions/SemanticMediaWiki/i18n/te.json
new file mode 100644
index 00000000..2a859839
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/te.json
@@ -0,0 +1,66 @@
+{
+ "@metadata": {
+ "authors": [
+ "Veeven",
+ "à°°à°¹à±à°®à°¾à°¨à±à°¦à±à°¦à±€à°¨à±",
+ "Ravichandra"
+ ]
+ },
+ "smw_finallistconjunct": ", మరియà±",
+ "smw_factbox_head": "$1 à°—à±à°°à°¿à°‚à°šà°¿ వాసà±à°¤à°µà°¾à°²à±",
+ "smw_isspecprop": "à°ˆ లకà±à°·à°£à°‚ à°ˆ వికీలో à°ªà±à°°à°¤à±à°¯à±‡à°•à°‚",
+ "smw_concept_description": "\"$1\" భావన యొకà±à°• వివరణ",
+ "smw_printername_list": "జాబితా",
+ "smw_printername_table": "పటà±à°Ÿà°¿à°•",
+ "smw_printername_template": "మూస",
+ "smw_printername_category": "వరà±à°—à°‚",
+ "smw-paramdesc-embedonly": "శీరà±à°·à°¿à°•à°²à°¨à± చూపించకà±",
+ "smw_iq_moreresults": "… మరినà±à°¨à°¿ ఫలితాలà±",
+ "smw_parseerror": "ఇచà±à°šà°¿à°¨ విలà±à°µ à°…à°°à±à°¥à°‚ కాలేదà±.",
+ "smw_notitle": "\"$1\"ని à°ˆ వికీలో పేజీ పేరà±à°—à°¾ ఉపయోగించలేరà±.",
+ "smw_wrong_namespace": "\"$1\" అనే పేరà±à°¬à°°à°¿à°¤à±‹ ఉనà±à°¨ పేజీలనౠమాతà±à°°à°®à±‡ ఇకà±à°•à°¡ à°…à°¨à±à°®à°¤à°¿à°¸à±à°¤à°¾à°‚.",
+ "smw_emptystring": "ఖాళీ పదాలనౠఅనà±à°®à°¤à°¿à°‚à°šà°‚.",
+ "smw_true_words": "true,t,yes,y,à°…à°µà±à°¨à±,సతà±à°¯à°‚",
+ "smw_false_words": "false,f,no,n,కాదà±,అసతà±à°¯à°‚",
+ "smw_nofloat": "“$1†అనేది సంఖà±à°¯ కాదà±.",
+ "smw_nodatetime": "\"$1\" అనే తేదీ à°à°®à°¿à°Ÿà±‹ à°…à°°à±à°¥à°‚కాలేదà±.",
+ "smw_type_header": "\"$1\" రకపౠలకà±à°·à°£à°¾à°²à±",
+ "smw_typearticlecount": "à°ˆ రకానà±à°¨à°¿ వాడà±à°¤à±à°¨à±à°¨ $1 {{PLURAL:$1|లకà±à°·à°£à°¾à°¨à±à°¨à°¿|లకà±à°·à°£à°¾à°²à°¨à°¿}} చూపిసà±à°¤à±à°¨à±à°¨à°¾à°‚.",
+ "smw_attribute_header": "\"$1\" లకà±à°·à°£à°¾à°¨à±à°¨à°¿ వాడà±à°¤à±à°¨à±à°¨ పేజీలà±",
+ "smw_attributearticlecount": "à°ˆ లకà±à°·à°£à°¾à°¨à±à°¨à°¿ వాడà±à°¤à±à°¨à±à°¨ $1 {{PLURAL:$1|పేజీని|పేజీలనà±}} చూపిసà±à°¤à±à°¨à±à°¨à°¾à°‚.",
+ "smw_subproperty_header": "ఉపలకà±à°·à°£à°¾à°²à±",
+ "smw_exportrdf_submit": "à°Žà°—à±à°®à°¤à°¿à°‚à°šà±",
+ "properties": "లకà±à°·à°£à°¾à°²à±",
+ "smw_properties_docu": "à°ˆ వికీలో à°ˆ à°•à±à°°à°¿à°‚ది లకà±à°·à°£à°¾à°²à°¨à± ఉపయోగించారà±.",
+ "smw_property_template": "$2 à°°à°•à°‚ యొకà±à°• $1 ($3)",
+ "unusedproperties": "ఉపయోగించని లకà±à°·à°£à°¾à°²à±",
+ "smw_unusedproperties_docu": "à°ˆ à°•à±à°°à°¿à°‚ది లకà±à°·à°£à°¾à°²à± ఉనà±à°¨à°¾ వాటిని పేజీలలోనూ వాడలేదà±.",
+ "smw_unusedproperty_template": "$2 à°°à°•à°‚ యొకà±à°• $1",
+ "wantedproperties": "కావాలà±à°¸à°¿à°¨ లకà±à°·à°£à°¾à°²à±",
+ "smw_wantedproperties_docu": "à°ˆ à°•à±à°°à°¿à°‚ది లకà±à°·à°£à°¾à°²à°¨à± à°ˆ వికీలో ఉపయోగించారౠకానీ ఇంకా వాటి à°—à±à°°à°¿à°‚à°šà°¿ వివరించే పేజీలేమీ లేవà±.",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|వాడà±à°•|వాడà±à°•à°²à±}})",
+ "types": "రకాలà±",
+ "smw_ask_ascorder": "ఆరోహణ",
+ "smw_ask_descorder": "అవరోహణ",
+ "smw_ask_printhead": "చూపించాలà±à°¸à°¿à°¨ అదనపౠభోగటà±à°Ÿà°¾",
+ "smw_ask_defaultformat": "à°…à°ªà±à°°à°®à±‡à°¯à°‚",
+ "smw_ask_otheroptions": "ఇతర ఎంపికలà±",
+ "smw_sbv_property": "లకà±à°·à°£à°‚:",
+ "smw_sbv_value": "విలà±à°µ:",
+ "browse": "వికీనౠదరà±à°¶à°¿à°‚à°šà°‚à°¡à°¿",
+ "smw_browse_go": "వెళà±à°³à±",
+ "smw_browse_no_outgoing": "à°ˆ పేజీలో లకà±à°·à°£à°¾à°²à±‡à°®à±€ లేవà±.",
+ "smw_browse_no_incoming": "à°ˆ పేజీకి లంకె వేసà±à°¤à±à°¨à±à°¨ లకà±à°·à°£à°¾à°²à±‡à°®à±€ లేవà±.",
+ "smw_pp_from": "పేజీ à°¨à±à°‚à°¡à°¿",
+ "smw_pp_type": "లకà±à°·à°£à°‚",
+ "smw_result_prev": "à°—à°¤",
+ "smw_result_next": "తదà±à°ªà°°à°¿",
+ "smw_result_results": "ఫలితాలà±",
+ "smw_result_noresults": "ఫలితాలేమీ లేవà±.",
+ "smw_smwadmin_db": "డాటాబేసౠసà±à°¥à°¾à°ªà°¨ మరియౠనవీకరణ",
+ "smw_smwadmin_announce": "మీ వికీని à°ªà±à°°à°•à°Ÿà°¿à°‚à°šà°‚à°¡à°¿",
+ "smw_smwadmin_datarefreshbutton": "భోగటà±à°Ÿà°¾à°¨à± తాజాకరించడం మొదలà±à°ªà±†à°Ÿà±à°Ÿà±",
+ "smw_smwadmin_support": "తోడà±à°ªà°¾à°Ÿà±à°¨à°¿ పొందడం",
+ "smw-ui-tooltip-title-quantity": "à°ªà±à°°à°®à°¾à°£à°‚ మారà±à°ªà±",
+ "smw-livepreview-loading": "లోడవà±à°¤à±‹à°‚ది..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tet.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tet.json
new file mode 100644
index 00000000..6fd8787a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tet.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": [
+ "MF-Warburg"
+ ]
+ },
+ "smw_true_words": "loos,l,sin,s",
+ "smw_false_words": "sala,s,lae,la",
+ "smw_result_prev": "Molok",
+ "smw_result_next": "Oinmai"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tg-cyrl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tg-cyrl.json
new file mode 100644
index 00000000..71735b8f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tg-cyrl.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ibrahim"
+ ]
+ },
+ "smw_sbv_value": "Қимат:",
+ "smw-createproperty-isproperty": "Ин Ñк вижагӣ аз навъи $1 аÑÑ‚.",
+ "smw-livepreview-loading": "Дар ҳоли бор шудан…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tg-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tg-latn.json
new file mode 100644
index 00000000..1218cedf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tg-latn.json
@@ -0,0 +1,5 @@
+{
+ "@metadata": [],
+ "smw-createproperty-isproperty": "In jak viƶagī az nav'i $1 ast.",
+ "smw-livepreview-loading": "Dar holi bor şudan…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/th.json b/www/wiki/extensions/SemanticMediaWiki/i18n/th.json
new file mode 100644
index 00000000..07ce3ebf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/th.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Horus"
+ ]
+ },
+ "browse": "ค้นดูวิà¸à¸´",
+ "smw-livepreview-loading": "à¸à¸³à¸¥à¸±à¸‡à¸„้นหา…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tk.json
new file mode 100644
index 00000000..54fd3165
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tk.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Hanberke"
+ ]
+ },
+ "smw_browse_go": "Git",
+ "smw_result_prev": "Öňki",
+ "smw-livepreview-loading": "Ãüklenýär..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tl.json
new file mode 100644
index 00000000..129cea88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tl.json
@@ -0,0 +1,246 @@
+{
+ "@metadata": {
+ "authors": [
+ "AnakngAraw",
+ "ì•„ë¼",
+ "Emem.calist",
+ "Sky Harbor"
+ ]
+ },
+ "smw-desc": "Ginagawang mas napupuntahan ang wiki mo - para sa ([https://www.semantic-mediawiki.org/wiki/Help:User_manual dokumentasyon habang nasa Internet]) ng mga makina ''at'' mga tao",
+ "smw_viewasrdf": "Pasubo/pakaing RDF",
+ "smw_finallistconjunct": ", at",
+ "smw-factbox-facts": "Mga katunayan",
+ "smw_isspecprop": "Ang pag-aaring ito ay isang natatanging pag-aari sa wiking ito.",
+ "smw_concept_description": "Paglalarawan ng diwa/konseptong \"$1\"",
+ "smw_no_concept_namespace": "Mabibigyang kahulugan lamang ang mga diwa (konsepto) sa ibabaw ng mga pahinang nasa loob ng Diwa: espasyo ng pangalan.",
+ "smw_multiple_concepts": "Magkakaroon lamang ng isang kahulugang pangkonsepto ang bawat isang pahina ng diwa.",
+ "smw_concept_cache_miss": "Hindi magagamit sa sandaling ito ang diwang \"$1\", dahil sa kinakailangan ng pagkakaayos ng wiki na tuusin ito habang hindi nakaugnay sa internet.\nKapag hindi nawala/naalis ang suliranin makalipas ang ilang panahon, pakihiling sa iyong tagapangasiwa na gawing makukuha ang diwang ito.",
+ "smw_noinvannot": "Ang mga halaga ay hindi maitatalaga sa mga katangiang pabaligtad.",
+ "version-semantic": "Mga dugtong na semantiko",
+ "smw_baduri": "Hindi pinapahintulutan ang mga URI ng pormularyong \"$1\".",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "Bilangin ang mga kinalabasan",
+ "smw_printername_csv": "Luwas ng CSV",
+ "smw_printername_dsv": "Pagluluwas ng DSV",
+ "smw_printername_debug": "Tanggalan ng depekto ang tanong (para sa mga dalubhasa)",
+ "smw_printername_embedded": "Ibaon ang mga nilalaman ng pahina",
+ "smw_printername_json": "Luwas ng JSON",
+ "smw_printername_list": "Talaan",
+ "smw_printername_ol": "Pagtatala",
+ "smw_printername_ul": "Pag-iisa-isa",
+ "smw_printername_table": "Tabla",
+ "smw_printername_broadtable": "Malawak na tabla",
+ "smw_printername_template": "Suleras",
+ "smw_printername_rdf": "Angkat ng RDF",
+ "smw_printername_category": "Kategorya",
+ "smw-paramdesc-limit": "Ang pinakamataas na bilang ng mga resultang ibabalik",
+ "smw-paramdesc-offset": "Ang timbang na pambalanse ng unang resulta",
+ "smw-paramdesc-headers": "Ipakita ang mga pangalan ng paulo/katangiang-ari",
+ "smw-paramdesc-mainlabel": "Ang tatak na ibibigay sa pangalan ng pangunahing pahina",
+ "smw-paramdesc-link": "Ipakita ang mga halaga bilang mga kawing",
+ "smw-paramdesc-intro": "Ang tekstong ipapakita bago ang mga resulta ng pagtatanong, kung mayroon",
+ "smw-paramdesc-outro": "Ang tekstong ipapakita pagkaraan ng mga resulta ng pagtatanong, kung mayroon",
+ "smw-paramdesc-default": "Ang tekstong ipapakita kung walang mga resulta ng pagtatanong",
+ "smw-paramdesc-sep": "Ang panghiwalay ng mga halaga",
+ "smw-paramdesc-distribution": "Sa halip na ipakita ang lahat ng mga halaga, bilangin ang pagsipot ng mga ito, at ipakita ang mga ito.",
+ "smw-paramdesc-distributionsort": "Iayos ang pamumudmod ng halaga ayon sa bilang ng pagsipot.",
+ "smw-paramdesc-distributionlimit": "Hanggahan ang pamumudmod ng halaga sa bilang ng ilang mga halaga lamang.",
+ "smw-paramdesc-template": "Ang pangalan ng isang suleras kung saan ipapakita ang mga inilimbag",
+ "smw-paramdesc-columns": "Ang bilang ng mga haligi kung saan ipapakita ang mga resulta (likas na nakatakda ang $1)",
+ "smw-paramdesc-userparam": "Isang halagang pinadaraan papasok sa bawat pagtawag ng suleras, kapag ginamit ang isang suleras",
+ "smw-paramdesc-introtemplate": "Ang pangalan ng isang suleras na ipapakita bago ang mga resulta ng pagtatanong, kung mayroon",
+ "smw-paramdesc-outrotemplate": "Ang suleras na ipapakita pagkaraan ng mga resulta ng pagtatanong, kung mayroon",
+ "smw-paramdesc-embedformat": "Ang ginagamit na tatak ng HTML upang ilarawan ang mga paulo",
+ "smw-paramdesc-embedonly": "Huwag magpakita ng mga paulo",
+ "smw-paramdesc-table-class": "Isang karagdagang klase ng Mga Pilas ng Estilong Lumalagaslas na itatalaga para sa talahanayan",
+ "smw-paramdesc-rdfsyntax": "Ang gagamiting palaugnayan ng RDF",
+ "smw-paramdesc-csv-sep": "Ang panghiwalay na gagamitin",
+ "smw-paramdesc-dsv-separator": "Ang panghiwalay na gagamitin",
+ "smw-paramdesc-dsv-filename": "Ang pangalan para sa talaksan ng DSV",
+ "smw-smwdoc-description": "Nagpapakita ng isang talahanayan ng lahat ng mga parametro na magagamit para sa tinukoy na kaanyuan ng resulta na kasama ang likas na nakatakdang mga halaga at mga paglalarawan.",
+ "smw-smwdoc-par-format": "Ang kaanyuan ng resulta na pagpapakitaan ng kasulatan ng parametro.",
+ "smw-smwdoc-par-parameters": "Kung aling mga parametro ang ipapakita. \"specific\" para sa mga idinagdag ayon sa kaanyuan, \"base\" para sa mga makukuha na nasa lahat ng mga kaanyuan, at \"all\" para sa dalawang mga ito.",
+ "smw-paramdesc-sort": "Kaarian na pag-aayusan ng pag-uusisa",
+ "smw-paramdesc-order": "Pagkakasunud-sunod ng pagkakaayos ng pag-uusisa",
+ "smw-paramdesc-searchlabel": "Tekstong para sa pagpapatuloy ng paghahanap",
+ "smw-paramdesc-named_args": "Pangalanan ang mga pangangatwiran na ipinasang papunta sa suleras",
+ "smw-paramdesc-export": "Mapagpipilian ng pag-angkat",
+ "smw-paramdesc-prettyprint": "Isang kalalabasang maganda ang pagkakalimbag na nagpapakita ng karagdagang mga pag-urong at bagong mga guhit",
+ "smw-paramdesc-source": "Panghaliling pinagmulan ng pag-uusisa",
+ "smw-paramdesc-jsonsyntax": "Gagamiting palaugnayan ng JSON",
+ "smw_iq_disabled": "Hindi pinagana ang mga katanungang hinggil sa kahulugan ng salita (semantiko) para sa wiking ito.",
+ "smw_iq_moreresults": "… karagdagang mga resulta/kinalabasan",
+ "smw_parseerror": "Hindi naunawaan ang ibinigay na halaga.",
+ "smw_decseparator": ".",
+ "smw_kiloseparator": ",",
+ "smw_notitle": "Hindi magagamit sa loob ng wiking ito ang \"$1\" bilang isang pangalan ng pahina.",
+ "smw_noproperty": "Hindi magagamit sa loob ng wiking ito ang \"$1\" bilang isang pangalan ng pag-aari.",
+ "smw_wrong_namespace": "Tanging mga pahinang nasa espasyo ng pangalang \"$1\" ang pinapahintulutan dito.",
+ "smw_manytypes": "Mahigit sa isang uri ang binigyang kahulugan para sa pag-aari.",
+ "smw_emptystring": "Hindi tinatanggap ang mga bagting na walang laman.",
+ "smw_notinenum": "Ang \"$1\" ay wala sa loob ng talaan ng maaaring maging mga halaga ($2) para sa pag-aaring ito.",
+ "smw_noboolean": "Ang \"$1\" ay hindi kinikilala bilang isang halaga ng ''Boolean'' (tama/mali).",
+ "smw_true_words": "totoo,t,oo,o",
+ "smw_false_words": "mali,m,hindi,h",
+ "smw_nofloat": "Hindi isang bilang ang \"$1\".",
+ "smw_infinite": "Hindi tinatangkilik ang mga bilang na kasinlaki ng \"$1\".",
+ "smw_unitnotallowed": "Ang \"$1\" ay hindi ipinahayag bilang isang katanggap-tanggap na yunit ng pagsukat para sa pag-aaring ito.",
+ "smw_nounitsdeclared": "Walang ipinahayag na mga yunit ng sukat para sa pag-aaring ito.",
+ "smw_novalues": "Walang tinukoy na mga halaga.",
+ "smw_nodatetime": "Hindi naunawaan ang petsang \"$1\".",
+ "smw_toomanyclosing": "Tila mayroong napakaraming mga kaganapan ng \"$1\" sa loob ng katanungan.",
+ "smw_noclosingbrackets": "Ilang mga paggamit ng \"<nowiki>[[</nowiki>\" sa loob ng iyong katanungan ang hindi naisara sa pamamagitan ng isang tumutugmang \"]]\".",
+ "smw_misplacedsymbol": "Ginamit ang sagisag na \"$1\" sa loob ng isang pook na hindi naman ito magagamit (walang pakinabang).",
+ "smw_unexpectedpart": "Hindi naunawaan ang bahaging \"$1\" ng katanungan.\nMaaaring hindi inaasahan ang magiging mga kinalabasan.",
+ "smw_emptysubquery": "Walang tanggap na kalagayan (kalakaran) ang kabahaging katanungan.",
+ "smw_misplacedsubquery": "Hindi ginamit ang ilang kabahaging mga katanungan sa loob ng isang pook kung saan hindi pinapahintulutan ang mga kabahaging katanungan.",
+ "smw_valuesubquery": "Hindi tinatangkilik ang kabahaging mga katanungan para sa mga halaga ng pag-aaring \"$1\".",
+ "smw_badqueryatom": "Hindi naunawaan ang ilang bahaging \"<nowiki>[[…]]</nowiki>\" ng katanungan.",
+ "smw_propvalueproblem": "Hindi naunawaan ang halaga ng pag-aaring \"$1\".",
+ "smw_noqueryfeature": "Ilang katangiang-kasangkapan ng katanungan ay hindi tinatangkilik sa wiking ito at inilaglag na ang bahagi ng katanungan ($1).",
+ "smw_noconjunctions": "Hindi tinatangkilik sa wiking ito ang mga pangatnig at inilaglag na ang bahagi ng katanungan ($1).",
+ "smw_nodisjunctions": "Hindi tinatangkilik sa wiking ito ang pagkakakalas-kalas sa mga katanungan at inilaglag na ang bahgi ng katanungan ($1).",
+ "smw_querytoolarge": "Hindi maisasaalang-alang ang sumusunod na mga kalagayan ng katanungan dahil sa mga hangganan ng mga wiki sa sukat o lalim ng katanungan: $1.",
+ "smw_notemplategiven": "Magbigay ng isang halaga para sa parametrong \"suleras\" upang gumana/maganap ang ganitong anyo/pormat ng katanungan.",
+ "smw_db_sparqlqueryproblem": "Ang resulta ng pagsisiyasat ay hindi makamtan mula sa kalipunan ng dato ng SPARQL. Ang kamaliang ito ay maaaring pansamantala o nagpapahiwatig ng isang depekto sa loob ng sopwer ng kalipunan ng dato.",
+ "smw_db_sparqlqueryincomplete": "Ang pagsagot sa pagsisiyasat ay lumitaw na napakahirap at pinaglubay. Maaaring nawawala ang ilang mga resulta. Kung maaari, sa halip ay subukang gumamit ng isang mas payak na pagsisiyasat.",
+ "smw_type_header": "Mga pag-aari ng uring \"$1\"",
+ "smw_typearticlecount": "Nagpapakita ng $1 na {{PLURAL:$1|pag-aari|mga pag-aari}}ng ginagamitan ng ganitong uri.",
+ "smw_attribute_header": "Mga pahinang gumagamit na pag-aaring \"$1\"",
+ "smw_attributearticlecount": "Nagpapakita ng $1 {{PLURAL:$1|pahina|mga pahina}}ng ginagamitan ng ganitong ari-arian.",
+ "specialpages-group-smw_group": "Semantikong MediaWiki",
+ "exportrdf": "Iluwas/Ipadala ang mga pahina patungo sa RDF",
+ "smw_exportrdf_docu": "Ipinapahintulot ng pahinang ito na makakuha ka ng dato mula sa isang pahinang nasa anyong RDF.\nUpang makapagluwas ng mga pahina, ipasok ang mga pamagat sa loob ng kahong pangtekstong nasa ibaba, isang pamagat bawat guhit/hanay.",
+ "smw_exportrdf_recursive": "Tumatawag sa sarili na angkatin ang lahat ng kaugnay na mga pahina.\nTandaan lamang na maaaring malaki ang kinalabasan/resulta!",
+ "smw_exportrdf_backlinks": "Nagluluwas din ng lahat ng mga pahinang tumutukoy sa iniluwas na mga pahina.\nGumagawa ng matitingnan-tingnang RDF.",
+ "smw_exportrdf_lastdate": "Huwag iluwas ang mga pahinang hindi nabago mula sa isang ibinigay na sandali sa loob ng panahon/oras.",
+ "smw_exportrdf_submit": "Iluwas",
+ "uriresolver": "tagalutas ng URI",
+ "properties": "Mga pag-aari",
+ "smw_properties_docu": "Ginagamit sa wiki ang sumusunod na mga pahina ng pag-aari.",
+ "smw_property_template": "$1 ng uring $2 ($3)",
+ "smw_property_template_notype": "$1 ($2)",
+ "smw_propertylackspage": "Lahat ng mga pag-aari ay dapat na nilalarawan ng isang pahina!",
+ "smw_propertylackstype": "Walang uring tinukoy para sa pag-aaring ito (ipapalagay na nasa uring $1 muna sa ngayon).",
+ "smw_propertyhardlyused": "Madalang gamitin ang ganitong pag-aari sa loob ng wiking ito!",
+ "unusedproperties": "Mga pag-aaring hindi pa nagagamit",
+ "smw-unusedproperties-docu": "Umiiral ang sumusunod na mga pag-aari bagaman wala namang ibang pahinang gumagamit ng mga ito.",
+ "smw-unusedproperty-template": "$1 ng uring $2",
+ "wantedproperties": "Ninanais na mga ari-arian",
+ "smw-wantedproperties-docu": "Ginagamit sa loob ng wiki ang sumusunod na mga pag-aari ngunit wala pang isang pahinang naglalarawan sa kanila.",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|gamit|mga gamit}})",
+ "smw_purge": "Sariwain",
+ "types": "Mga uri",
+ "smw_types_docu": "Ang sumusunod ay isang talaan ng lahat ng mga uri ng dato na maaaring italaga sa mga pag-aari.",
+ "smw_uri_doc": "Ipinatutupad ng tagapaglutas na URI ang [$1 Paghahanap ng W3C TAG sa httpRange-14].\nPinag-iingatan nitong huwag maging mga sityo ng web (websayt) ang mga tao.",
+ "ask": "Paghahanap hinggil sa kahulugan ng mga salita (semantiko)",
+ "smw_ask_sortby": "Pagpangkat-pangkating ayon sa pahabang kahanayan (maaaring wala nito)",
+ "smw_ask_ascorder": "Tumataas",
+ "smw_ask_descorder": "Bumababa",
+ "smw_ask_submit": "Hanapin ang mga kinalabasan/resulta",
+ "smw_ask_editquery": "Baguhin ang katanungan",
+ "smw_add_sortcondition": "[Idagdag ang kalagayang pampagpapangkat-pangkat]",
+ "smw_ask_hidequery": "Itago ang katanungan",
+ "smw_ask_help": "Tulong sa pagtatanong",
+ "smw_ask_queryhead": "Katanungan",
+ "smw_ask_printhead": "Karagdagang mga datong ipapakita",
+ "smw_ask_printdesc": "(magdagdag ng isang pangalan ng ari-arian sa bawat guhit)",
+ "smw_ask_format_as": "Iayos bilang:",
+ "smw_ask_defaultformat": "likas na katakdaan",
+ "smw_ask_otheroptions": "Iba pang mga mapagpipilian",
+ "smw-ask-otheroptions-info": "Ang seksiyong ito ay naglalaman ng mga mapagpipilian na nakapagpapabago sa mga pahayag ng nakalimbag na kinalabasan. Ang mga paglalarawan ng paraemetro ay maaaring tingnan sa pamamagitan ng pag-aligid sa ibabaw ng mga ito.",
+ "smw-ask-otheroptions-collapsed-info": "Paki gamitin ang kinatawang larawan ng pagdaragdag upang makita ang lahat ng makukuhang mga mapagpipilian",
+ "smw_ask_show_embed": "Ipakita ang kodigong pambaon",
+ "smw_ask_hide_embed": "Ibaong nakakubli ang kodigo",
+ "smw_ask_embed_instr": "Upang maibaon ang katanungang ito sa loob ng guhit papasok sa isang pahina ng wiki, gamitin ang kodigong nasa ibaba.",
+ "smw-ask-delete": "[Burahin]",
+ "smw-ask-sorting": "Pagbubukud-bukod",
+ "searchbyproperty": "Maghanap ayon sa pag-aari",
+ "smw_sbv_docu": "Hanapin ang lahat ng mga pahinang may isang ibinigay na pag-aari at halaga.",
+ "smw_sbv_novalue": "Maglagay/magpasok ng isang tanggap na halaga para sa pag-aari, o tingnan ang lahat ng mga halaga ng ari-arian para sa \"$1\".",
+ "smw_sbv_displayresult": "Isang talaan ng lahat ng mga pahinang may pag-aaring \"$1\" na may halagang \"$2\"",
+ "smw_sbv_displayresultfuzzy": "Isang talaan ng lahat ng mga pahinang may pag-aaring \"$1\" na may halagang \"$2\".\nDahil mayroong mangilan-ngilang mga kinalabasan lamang, ipinakikita rin ang kalapit na mga halaga.",
+ "smw_sbv_property": "Pag-aari:",
+ "smw_sbv_value": "Halaga:",
+ "smw_sbv_submit": "Hanapin ang mga kinalabasan",
+ "browse": "Magbasa-basa sa wiki",
+ "smw_browselink": "Tumingin-tingin sa mga pag-aari",
+ "smw_browse_article": "Ipasok/ilagay ang pangalan ng pahinang pagsisimulan ng pagtingin-tingin.",
+ "smw_browse_go": "Gawin na",
+ "smw_browse_more": "…",
+ "smw_browse_show_incoming": "ipakita ang mga pag-aaring kumakawing (umuugnay) dito",
+ "smw_browse_hide_incoming": "itago ang mga pag-aaring kumakawing (umuugnay) dito",
+ "smw_browse_no_outgoing": "Walang mga pag-aari ang pahinang ito.",
+ "smw_browse_no_incoming": "Walang mga pag-aaring nakakawing (umuugnay) patungo sa pahinang ito.",
+ "smw_inverse_label_default": "$1 ng",
+ "smw_inverse_label_property": "Ibinaligtad na tatak ng pag-aari",
+ "pageproperty": "Paghahanap sa pag-aari ng pahina",
+ "smw_pp_docu": "Hanapin ang lahat ng mga pampuno ng isang pag-aari sa isang ibinigay na pahina.\nKapwa ipasok ang isang pahina at isang pag-aari.",
+ "smw_pp_from": "Mula sa pahina",
+ "smw_pp_type": "Pag-aari",
+ "smw_pp_submit": "Hanapin ang mga resulta",
+ "smw_result_prev": "Sinundan",
+ "smw_result_next": "Susunod",
+ "smw_result_results": "Mga kinalabasan (resulta)",
+ "smw_result_noresults": "Walang mga kinalabasan/resulta.",
+ "smwadmin": "Mga punsiyon para sa pangangasiwa at pagpapanatili",
+ "smw-admin-setupsuccess": "Matagumpay na naihanda ang makina ng pagtatago.",
+ "smw_smwadmin_return": "Magbalik sa $1",
+ "smw_smwadmin_updatestarted": "Sinimulan ang isang bagong proseso ng pagsasapanahon para sa pagsasariwa ng datong semantiko.\nLahat ng nakatabing mga dato ay muling bubuuin o kukumpunihin kung saan kailangan.\nMasusundan mo ang pagsulong ng pagsasapanahon sa natatanging pahinang ito.\n\nBumalik sa $1.",
+ "smw_smwadmin_updatenotstarted": "Mayroon nang isang tumatakbong proseso ng pagsasapanahon.\nHindi na lilikha ng isa pa.\n\nBumalik sa $1.",
+ "smw_smwadmin_updatestopped": "Inihinto ang lahat ng umiiral na mga proseso ng pagsasapanahon.\n\nBumalik sa $1.",
+ "smw_smwadmin_updatenotstopped": "Upang mapahinto ang tumatakbong proseso ng pagsasapanahon, dapat mong pasiglahin ang kahong lagayan ng tsek upang maipahiwatig na talagang nakatitiyak ka.\n\nBumalik sa $1.",
+ "smw-admin-docu": "Tumutulong ang natatanging pahinang ito sa iyo sa panahon ng pagluluklok at pagtataas ng uri ng <a href=\"http://semantic-mediawiki.org\">Semantikong MediaWiki</a>.\nHuwag kalimutang gumawa ng karagdagang sipi ng mahalagang dato bago isagawa ang mga tungkuling pangtagapangasiwa.",
+ "smw-admin-db": "Pagpapanatili ng kalipunan ng datos",
+ "smw-admin-dbdocu": "Nangangailangan ang Semantikong Mediawiki ng ilang mga karugtong sa kalipunan ng dato ng MediaWiki upang maitabi ang datong hinggil sa kahulugan ng mga salita.\nTinitiyak ng tungkuling nasa ibaba na tama ang pagkakahanda ng kalipunan ng dato mo.\nHindi nakakaapekto ang mga pagbabagong ginawa sa hakbang na ito sa iba pang mga bahagi ng kalipunan ng dato ng MediaWiki, at madaling/maginhawang maibabalik kung nanaisin.\nMaaaring isagawa ng maramihang ulit ang tungkulin ng paghahandang ito na hindi nakapagdurulot ng anumang pinsala, subalit isang ulit lamang itong kailangan sa pagluluklok/instalasyon o pagtataas ng uri/grado.",
+ "smw-admin-permissionswarn": "Kapag nabigo ang pagsasagawa na mayroong mga kamalian ng SQL, maaaring walang sapat na mga kapahintulutan ang tagagamit ng kalipunan ng dato na hinirang ng wiki mo (siyasatin ang iyong LocalSettings.php).\nMaaaring pagkalooban ang tagagamit na ito ng karagdagang mga kapahintulutan upang makalikha at makapagbura ng mga talahanayan, pansamantalang ipasok ang panglagda ng iyong ugat ng kalipunan ng dato sa LocalSettings.php, o gamitin ang panitik ng pagpapanatili na <code>setupStore.php</code> na makakagamit ng mga kredensiyal ng isang tagapangasiwa.",
+ "smw-admin-dbbutton": "Umpisahan o itaas ang uri (klase) ng mga tabla",
+ "smw-admin-announce": "Ipahayag ang wiki mo",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> ay napatanggal sa $2",
+ "smw_smwadmin_datarefresh": "Pagkukumpuni at pagtataas ng uri (grado/klase) ng dato",
+ "smw_smwadmin_datarefreshdocu": "Maaaring maibalik muli ang lahat ng dato ng Semantikong MediaWiki batay sa pangkasalukuyang mga nilalaman ng wiki.\nMagiging gamitin ito sa pagaayos/pagkukumpuni ng nasirang dato o upang masariwa ang dato kung nabago ang panloob na kaanyuan/pormat dahil sa ilang mga pagtataas ng uri ng sopwer.\nIsinasakatuparan ang pagsasapanahon ng pahina sa pahina at hindi agad-agarang mabubuo.\nIpinapakita ng sumusunod kung sumusulong na ang isang pagsasapanahon at magpapahintulot sa iyo upang simulan o pahintuin ang mga pagsasapanahon (maliban na lamang kung hindi pinagana/pinaandar ng tagapangasiwa ng sityo/sayt ang katangiang-kasangkapang ito).",
+ "smw_smwadmin_datarefreshprogress": "<strong>Sumusulong na ang isang pagsasapanahon.</b>\nPangkaraniwan lamang na mabagal ang pagsulong ng pagsasapanahon dahil sinasariwa lamang nito ang maliliit na tipak ng dato sa bawat panahong pumupunta sa wiki ang isang tagagamit.\nUpang mas mabilisang matapos ang pagsasapanahong ito, maaari mong tawagin ang panitik na pangpagpapanatiling <code>runJobs.php</code> (gamitin ang pagpipiliang <code>--maxjobs 1000</code> ng MediaWiki upang mabigyan ng hangganan ang bilang ng mga pagsasapanahong ginagawa sa bawat isang bungkos).\nTinatayang progreso ng pangkasalukuyang pagsasapanahon:",
+ "smw_smwadmin_datarefreshbutton": "Umpisahang isapanahon ang dato",
+ "smw_smwadmin_datarefreshstop": "Ihinto ang pagsasapanahong ito",
+ "smw_smwadmin_datarefreshstopconfirm": "Oo, nakatitiyak ako.",
+ "smw-admin-support": "Pagkuha ng suporta",
+ "smw-admin-supportdocu": "Maaaring makatulong sa iyo ang sari-saring kapakipakinabang na mga bagay-bagay kung sakaling magkaroon ng mga suliranin:",
+ "smw-admin-installfile": "Kung sakaling makaranas ka ng mga suliranin sa pagluluklok mo, magsimula sa pamamagitan ng pagsusuri ng mga patnubay na nasa loob ng <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md#smw-installation\">ILUKLOK ang talaksan</a>.",
+ "smw-admin-smwhomepage": "Ang buong kasulatang pantagagamit ng Semantikong Mediawiki ay nasa <b><a href=\"http://semantic-mediawiki.org\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Maaaring iulat ang mga depekto/sira (''bug'') sa <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">GitHub</a>.",
+ "smw-admin-questions": "Kung mayroon ka pang karagdagang mga katanungan o mga mungkahi, makilahok sa usapang nasa <a href=\"http://sourceforge.net/mailarchive/forum.php?forum_name=semediawiki-user\">Pangmadlang talakayan ng tagagamit ng MediaWiking Semantiko</a>.",
+ "smw-admin-supplementary-elastic-functions": "Mapagpipiliang mga tungkulin",
+ "smw_adminlinks_datastructure": "Kayarian ng dato",
+ "smw_adminlinks_displayingdata": "Pagpapakita ng dato",
+ "smw_adminlinks_inlinequerieshelp": "Tulong sa mga katanungang nasa guhit",
+ "smw-createproperty-isproperty": "Isa itong pag-aari ng uring $1.",
+ "smw-createproperty-allowedvals": "Ang pinahihintulutang {{PLURAL:$1|halaga para sa pag-aaring ito ay|mga halaga para sa pag-aaring ito ay}}:",
+ "smw-paramdesc-category-delim": "Ang pantakda ng hangganan",
+ "smw-paramdesc-category-template": "Isang suleras na pangbigay ng kaanyuan sa mga bagay",
+ "smw-paramdesc-category-userparam": "Isang parametro na ipapasa sa suleras",
+ "smw-info-par-message": "Mensaheng ipapakita.",
+ "smw-info-par-icon": "Kinatawang larawan na ipapakita, maaaring \"kabatiran\" o \"babala\".",
+ "prefs-smw": "Semantikong MediaWiki",
+ "prefs-ask-options": "Mga mapagpipilian ng semantikong paghahanap",
+ "smw-prefs-ask-options-tooltip-display": "Ipakita ang teksto ng parametro bilang pangkabatiran na payo sa kasangkapan",
+ "smw-prefs-ask-options-collapsed-default": "Paganahin ang kahon ng mapagpipilian na paguguhuin ayon sa likas na katakdaan",
+ "smw_unknowntype": "Hindi katanggap-tanggap ang uri ng pag-aaring ito",
+ "smw_concept_header": "Mga pahina ng diwang \"$1\"",
+ "smw_conceptarticlecount": "Nagpapakita ng $1 {{PLURAL:$1|pahina|mga pahina}}ng kasali sa ganyang diwa.",
+ "smw-livepreview-loading": "Ikinakarga...",
+ "logeventslist-smw-log": "Log ng Semantikong MediaWiki",
+ "smw-property-preferred-title-format": "Isang dolyar (dalawang dolyar)",
+ "smw-format-datatable-emptytable": "Walang 'data'ng maipakita sa hapagkainan",
+ "smw-format-datatable-infoempty": "Ipinapakita na 0 hanggang 0 na 0 -ng resulta",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-loadingrecords": "Ikinakasa...",
+ "smw-format-datatable-search": "Maghanap:",
+ "smw-format-datatable-last": "Dulo",
+ "smw-filter": "Pansala",
+ "smw-help": "Makatulong",
+ "smw-remote-source-unavailable": "Hindi mai-'connect' sa 'remote \"$1\" target'."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tly.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tly.json
new file mode 100644
index 00000000..7393e6bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tly.json
@@ -0,0 +1,12 @@
+{
+ "@metadata": {
+ "authors": [
+ "ГуÑейн"
+ ]
+ },
+ "smw_finallistconjunct": ", ијән",
+ "smw_printername_list": "Сијоһи",
+ "smw_printername_template": "Ғәлиб",
+ "smw_printername_category": "ТиÑпир",
+ "smw_purge": "Тожә кардеј"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tr.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tr.json
new file mode 100644
index 00000000..6e2abbe3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tr.json
@@ -0,0 +1,156 @@
+{
+ "@metadata": {
+ "authors": [
+ "Emperyan",
+ "Erdemaslancan",
+ "Karduelis",
+ "Vito Genovese",
+ "Incelemeelemani",
+ "HakanIST",
+ "Sayginer",
+ "Hbseren"
+ ]
+ },
+ "smw_viewasrdf": "RDF beslemesi",
+ "smw_finallistconjunct": " ve",
+ "smw_factbox_head": "$1 hakkındaki gerçekler",
+ "smw_isspecprop": "Bu özellik, bu vikideki özel bir özelliktir.",
+ "smw_concept_description": "\"$1\" kavramının tanımı",
+ "smw_no_concept_namespace": "Kavramlar yalnızca kavram sayfalarında tanımlanabilir: ad alanı",
+ "smw_multiple_concepts": "Tüm kavram sayfaları sadece birer kavram tanımına sahip olabilir.",
+ "version-semantic": "Semantik eklentileri",
+ "smw_baduri": "\"$1\" form URL'sine izin verilmiyor.",
+ "smw_printername_count": "Sonuçları say",
+ "smw_printername_csv": "CSV dışa aktarımı",
+ "smw_printername_dsv": "CSV çıktısı",
+ "smw_printername_debug": "Hata ayıklama sorgusu (uzmanlar için)",
+ "smw_printername_embedded": "Sayfa içeriklerini yerleştir",
+ "smw_printername_json": "JSON çıktısı",
+ "smw_printername_list": "Liste",
+ "smw_printername_ol": "Numaralandırma",
+ "smw_printername_ul": "Tanımlama",
+ "smw_printername_table": "Tablo",
+ "smw_printername_broadtable": "GeniÅŸ tablo",
+ "smw_printername_template": "Åžablon",
+ "smw_printername_rdf": "RDF çıktısı",
+ "smw_printername_category": "Kategori",
+ "validator-type-class-SMWParamSource": "metin",
+ "smw-paramdesc-limit": "Verilecek azami sonuç sayısı",
+ "smw-paramdesc-headers": "Başlıkları/özellik adlarını göster",
+ "smw-paramdesc-link": "Değerleri bağlantı olarak gösterir",
+ "smw-paramdesc-sep": "Değerler için ayraç",
+ "smw-paramdesc-embedonly": "Başlık gösterme",
+ "smw-paramdesc-searchlabel": "Aramanın devam etmesi için metin",
+ "smw_iq_moreresults": "... ek sonuçlar",
+ "smw_emptystring": "BoÅŸ dizgiler kabul edilmemektedir.",
+ "smw_nofloat": "\"$1\" bir sayı değil.",
+ "smw_toomanyclosing": "Sorguda çok fazla \"$1\" kullanımı mevcut.",
+ "smw_misplacedsymbol": "\"$1\" sembolü, yararlı olmadığı bir noktada kullanıldı.",
+ "smw_type_header": "\"$1\" türünün özellikleri",
+ "smw_attribute_header": "\"$1\" özelliğini kullanan sayfalar",
+ "smw_subproperty_header": "Alt özellikler",
+ "exportrdf": "Sayfaları RDF'ye aktar",
+ "smw_exportrdf_lastdate": "Süre olarak belirtilen noktadan bu yana değiştirilmemiş sayfaları dışa aktarma.",
+ "smw_exportrdf_submit": "Dışa aktar",
+ "properties": "Özellikler",
+ "smw_properties_docu": "Vikide aşağıdaki özellikler kullanılıyor.",
+ "smw_property_template": "$1 tipi $2 ($3)",
+ "smw_propertylackspage": "Tüm özellikler bir sayfa tarafından açıklanmalıdır!",
+ "smw_propertyhardlyused": "Bu özellik, viki dahilinde nadiren kullanılmaktadır!",
+ "concepts": "Kavramlar",
+ "smw-sp-concept-header": "Kavramlar listesi",
+ "smw-sp-concept-empty": "Hiçbir kavram bulunamadı.",
+ "unusedproperties": "Kullanılmayan özellikler",
+ "wantedproperties": "İstenen özellikler",
+ "smw_wantedproperty_template": "$1 ($2 {{PLURAL:$2|kullanım|kullanım}})",
+ "smw_purge": "Yenile",
+ "types": "Türler",
+
+ "smw-statistics": "Semantik istatistikleri",
+ "smw-statistics-query-size": "Sorgu boyutu",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|Kavram|Kavram}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|Kavram|Kavram}}]]",
+ "smw-statistics-subobject-count": "{{PLURAL:$1|Altnesne|Altnesne}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Veritürü|Veritürü}}]]",
+ "smw-statistics-delete-count": "Zamanı geçmiş {{PLURAL:$1|entity|entities}} (kaldırılmak üzere işaretli)",
+ "ask": "Anlamsal arama",
+ "smw_ask_sortby": "Sütuna göre sırala (isteğe bağlı)",
+ "smw_ask_ascorder": "Artan",
+ "smw_ask_descorder": "Azalan",
+ "smw_ask_submit": "Sonuçları bul",
+ "smw_ask_editquery": "Sorguyu deÄŸiÅŸtir",
+ "smw_add_sortcondition": "[Sıralama koşulu ekle]",
+ "smw_ask_hidequery": "Sorguyu gizle",
+ "smw_ask_help": "Sorgulama yardımı",
+ "smw_ask_queryhead": "Sorgu",
+ "smw_ask_printhead": "Görüntülenecek ek veriler",
+ "smw_ask_printdesc": "(satır başına bir özellik adı girin)",
+ "smw_ask_format_as": "Biçim olarak:",
+ "smw_ask_defaultformat": "varsayılan",
+ "smw_ask_otheroptions": "Diğer seçenekler",
+ "smw_ask_show_embed": "Yerleştirme kodunu göster",
+ "smw_ask_hide_embed": "YerleÅŸtirme kodunu gizle",
+ "smw-ask-delete": "[Sil]",
+ "smw-ask-sorting": "Sıralama",
+ "smw-ask-search": "Ara",
+ "searchbyproperty": "Özelliğe göre ara",
+ "smw_sbv_docu": "Belirtilen özellik ve değere sahip tüm sayfaları ara.",
+ "smw_sbv_novalue": "Özellik için geçerli bir değer girin ya da \"$1\" için tüm özellik değerlerini görün.",
+ "smw_sbv_displayresult": "\"$2\" değeri ile \"$1\" özelliğine sahip tüm sayfaların bir listesi",
+ "smw_sbv_property": "Özellik:",
+ "smw_sbv_value": "DeÄŸer:",
+ "smw_sbv_submit": "Sonuç bul",
+ "browse": "Vikiye gözat",
+ "smw_browselink": "Özelliklere gözat",
+ "smw_browse_go": "Git",
+ "smw_browse_show_incoming": "buraya bağlantı veren özellikleri göster",
+ "smw_browse_hide_incoming": "buraya bağlantı veren özellikleri gizle",
+ "smw_browse_no_outgoing": "Bu sayfa özelliğe sahip değil.",
+ "smw_browse_no_incoming": "Bu sayfaya bağlantı veren özellik yok.",
+ "smw_inverse_label_default": "$1",
+ "smw_inverse_label_property": "Ters özellik etiketi",
+ "pageproperty": "Sayfa özelliği arama",
+ "smw_pp_from": "Åžu sayfadan:",
+ "smw_pp_type": "Özellik",
+ "smw_pp_submit": "Sonuçları bul",
+ "smw_result_prev": "Önceki",
+ "smw_result_next": "sonraki",
+ "smw_result_results": "Sonuçlar",
+ "smw_result_noresults": "Sonuç yok.",
+ "smwadmin": "Anlamsal MediaWiki için hizmetlilik fonksiyonları",
+ "smw_smwadmin_setupsuccess": "Saklama motoru başarıyla ayarlandı.",
+ "smw_smwadmin_return": "$1 geri dön",
+ "smw_smwadmin_updatenotstarted": "Yürürlükte olan bir güncelleme işlemi mevcut.\nBir diğeri oluşturulmuyor.",
+ "smw_smwadmin_updatestopped": "Tüm mevcut güncelleme işlemleri durduruldu.",
+ "smw_smwadmin_announce": "Vikinizi duyurun",
+ "smw_smwadmin_datarefresh": "Veri onarımı ve yükseltmesi",
+ "smw_smwadmin_datarefreshbutton": "Veri güncellemesini başlat",
+ "smw_smwadmin_datarefreshstop": "Bu güncellemeyi durdur",
+ "smw_smwadmin_datarefreshstopconfirm": "Evet, eminim.",
+ "smw_smwadmin_support": "Destek alınıyor",
+ "smw_adminlinks_datastructure": "Veri yapısı",
+ "smw_adminlinks_displayingdata": "Veriler görüntüleniyor",
+ "smw_adminlinks_inlinequerieshelp": "Satıriçi sorgu yardımı",
+ "smw-ui-tooltip-title-property": "Özellik",
+ "smw-ui-tooltip-title-quantity": "Miktar",
+ "smw-ui-tooltip-title-info": "Bilgi",
+ "smw-ui-tooltip-title-service": "Hizmet bağlantıları",
+ "smw-ui-tooltip-title-warning": "Hata",
+ "smw-ui-tooltip-title-parameter": "Parametre",
+ "smw-ui-tooltip-title-event": "Olay",
+ "smw-ui-tooltip-title-note": "Not",
+ "smw-ui-tooltip-title-legend": "Gösterge",
+ "smw_unknowntype": "Bu özelliğin türü geçersiz",
+ "smw_concept_header": "\"$1\" kavram sayfaları",
+ "smw-sp-properties-header-label": "Özellikler listesi",
+ "smw-sp-admin-settings-title": "Yapılandırma ayarları",
+ "smw-sp-admin-settings-button": "Ayarlar listesi oluÅŸturma",
+ "smw-sp-admin-idlookup-title": "Nesne ID araştırma",
+ "smw-sp-admin-objectid": "Nesne kimliÄŸi:",
+ "smw-livepreview-loading": "Yükleniyor...",
+ "smw-sp-searchbyproperty-resultlist-header": "Sonuç listesi",
+ "log-name-smw": "Semantik MediaWiki günlüğü",
+ "smw-pa-property-predefined_pvuc": "\"$1\" önceden tanımlanmış, bir özelliğe yapılan atamaların eşsiz olmasının beklendiğini belirten bir özelliktir ve [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantik MediaWiki] tarafından sunulur.",
+ "smw-pa-property-predefined-long_pvuc": "Benzersizlik iki değerin değişmez temsilinin eşit olmaması halinde belirlenir ve bu kısıtlamanın herhangi bir ihlali hata olarak kategorize edilir.",
+ "smw-datavalue-uniqueness-constraint-error": "\"$1\" özelliği sadece benzersiz değer atamalarını kabul eder ve ''$2'' halihazırda \"$3\" konusu için atanmış durumda."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tt-cyrl.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tt-cyrl.json
new file mode 100644
index 00000000..fcf0c244
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tt-cyrl.json
@@ -0,0 +1,34 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ерней",
+ "Ильнар",
+ "Macofe"
+ ]
+ },
+ "smw_viewasrdf": "RDF чыганак",
+ "smw_finallistconjunct": "һәм",
+ "smw_factbox_head": "Фактлар: $1",
+ "smw_printername_csv": "CSV ÑкÑпортлау",
+ "smw_printername_dsv": "DSV ÑкÑпортлау",
+ "smw_printername_json": "JSON ÑкÑпортлау",
+ "smw_printername_list": "ИÑемлек",
+ "smw_printername_ol": "Санау",
+ "smw_printername_ul": "ИÑемлек",
+ "smw_printername_table": "Табын",
+ "smw_printername_broadtable": "Киң табын",
+ "smw_printername_template": "Калып",
+ "smw_printername_rdf": "RDF-ÑкÑпорт",
+ "smw_printername_category": "Төркем",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw_ask_defaultformat": "килешү буенча",
+ "smw_sbv_property": "Халәте:",
+ "smw_sbv_value": "ÐңлатмаÑÑ‹:",
+ "smw_sbv_submit": "Эзләү",
+ "browse": "Сәхифәне карау",
+ "smw_browselink": "Үзенчәлекләрен карау",
+ "group-smwadministrator": "Semantic MediaWiki идәрәчеләре",
+ "group-smwadministrator-member": "{{GENDER:$1|идәрәче (Semantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:SMW_идәрәчеләре",
+ "smw-livepreview-loading": "Йөкләү..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tt-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tt-latn.json
new file mode 100644
index 00000000..a282f54d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tt-latn.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Frhdkazan"
+ ]
+ },
+ "browse": "Wiki eçtälegen qaraw",
+ "smw-livepreview-loading": "Yökläw..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/tyv.json b/www/wiki/extensions/SemanticMediaWiki/i18n/tyv.json
new file mode 100644
index 00000000..33efe678
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/tyv.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Монгуш Салим"
+ ]
+ },
+ "browse": "Ðрыннарның ниити көрүлдези"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ug-arab.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ug-arab.json
new file mode 100644
index 00000000..20321eb3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ug-arab.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Alfredie"
+ ]
+ },
+ "smw_browse_go": "كۆچۈش",
+ "smw-livepreview-loading": "يۈكلەۋاتىدۇ…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ug-latn.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ug-latn.json
new file mode 100644
index 00000000..ae06b6c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ug-latn.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Jose77",
+ "Minh Nguyen",
+ "Vinhtantran"
+ ]
+ },
+ "smw_browse_go": "Köchüsh"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/uk.json b/www/wiki/extensions/SemanticMediaWiki/i18n/uk.json
new file mode 100644
index 00000000..08895df3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/uk.json
@@ -0,0 +1,762 @@
+{
+ "@metadata": {
+ "authors": [
+ "Andriykopanytsia",
+ "Base",
+ "Olvin",
+ "Prima klasy4na",
+ "Steve.rusyn",
+ "SteveR",
+ "ТеÑÑ‚",
+ "Юрій Булка",
+ "ì•„ë¼",
+ "Ðта",
+ "Piramidion",
+ "KHMELNYTSKYIA",
+ "Nemo bis",
+ "Ahonc",
+ "Bunyk",
+ "Eduardo Addad de Oliveira",
+ "Renamerr",
+ "Avatar6",
+ "Movses",
+ "Vlad5250"
+ ]
+ },
+ "smw-desc": "Робить вашу вікі доÑтупнішою — Ð´Ð»Ñ Ð¼Ð°ÑˆÐ¸Ð½ ''та'' людей ([https://www.semantic-mediawiki.org/wiki/Help:User_manual довідка в мережі])",
+ "smw-title": "Семантична MediaWiki",
+ "smw-upgrade-error": "Семантичну MediaWiki вÑтановлено й увімкнено, але вона не має належного [https://www.semantic-mediawiki.org/wiki/Help:Upgrade ключа оновленнÑ], Ñкий відповідав би: <code>$1</code>.",
+ "smw-upgrade-error-why-title": "Чому Ñ Ð±Ð°Ñ‡Ñƒ цю помилку?",
+ "smw-upgrade-error-why-explain": "Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ñтруктура бази даних Семантичної MediaWiki змінилаÑÑ Ñ– потребує деÑкого коригуваннÑ, щоб повноцінно функціювати. Цьому може бути декілька причин, Ñеред Ñких:\n* Додано додаткові фікÑовані влаÑтивоÑÑ‚Ñ– (необхідні додаткові Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ)\n* ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ñ–Ñтить ÑкіÑÑŒ зміни до таблиць або індекÑи, що роблÑÑ‚ÑŒ Ð¿ÐµÑ€ÐµÑ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð¾Ð²'Ñзковим перед отриманнÑм доÑтупу до даних",
+ "smw-upgrade-error-how-title": "Як мені виправити цю помилку?",
+ "smw-upgrade-error-how-explain": "ÐдмініÑтраторові (чи будь-Ñкій оÑобі з адмініÑтративними правами) треба або запуÑтити [https://www.mediawiki.org/wiki/Manual:Update.php update.php] MediaWiki, або Ñкрипт обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] Семантичної MediaWiki. Також можна звіритиÑÑ Ð· такими Ñторінками, щоб дізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ:\n* ІнÑтрукції щодо [https://www.semantic-mediawiki.org/wiki/Help:Installation вÑтановленнÑ]\n* Сторінка довідки на тему [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼]",
+ "smw-semantics-not-enabled": "Функціонал Семантичного Медіавікі не ввімкнено в цій вікі.",
+ "smw_viewasrdf": "вивід RDF",
+ "smw_finallistconjunct": " Ñ–",
+ "smw-factbox-head": "... більше про \"$1\"",
+ "smw-factbox-facts": "Факти",
+ "smw-factbox-facts-help": "Показує виÑÐ»Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ– факти, Ñкі були Ñтворені кориÑтувачем",
+ "smw-factbox-facts-derived": "Отримані факти",
+ "smw-factbox-facts-derived-help": "Показує факти, Ñкі були отримані з правил, або за допомогою інших методів розÑуджень",
+ "smw_isspecprop": "Ð¦Ñ Ð²Ð»Ð°ÑтивіÑÑ‚ÑŒ Ñ” Ñпеціальною у цій вікі",
+ "smw-concept-cache-header": "ВикориÑÑ‚Ð°Ð½Ð½Ñ ÐºÐµÑˆÑƒ",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count Кеш концептів] міÑтить {{PLURAL:$1|'''одну''' ÑуніÑÑ‚ÑŒ|'''$1''' ÑутроÑÑ‚Ñ–|'''$1''' ÑутроÑтей}} ($2).",
+ "smw-concept-no-cache": "Ðемає кешу.",
+ "smw_concept_description": "ÐžÐ¿Ð¸Ñ ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ñ–Ñ— «$1».",
+ "smw_no_concept_namespace": "Концепції можна означувати лише у проÑторі назв «КонцепціÑ:».",
+ "smw_multiple_concepts": "Кожна Ñторінка концепції може мати лише одне Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ†ÐµÐ¿Ñ†Ñ–Ñ—.",
+ "smw_concept_cache_miss": "Концепцію «$1» неможливо викориÑтати у цей момент, оÑкільки Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ†Ñ–Ñ”Ñ— вікі вимагає попередньої автономної обробки. Якщо проблема не вирішитьÑÑ Ð¿Ñ€Ð¾Ñ‚Ñгом певного чаÑу, звернітьÑÑ Ð´Ð¾ адмініÑтратора Ñайту.",
+ "smw_noinvannot": "У зворотні влаÑтивоÑÑ‚Ñ– неможливо запиÑати значеннÑ.",
+ "version-semantic": "Ð Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Semantic",
+ "smw_baduri": "ВикориÑтовувати адреÑи (URI), що мають форму «$1» не дозволÑєтьÑÑ.",
+ "smw_printername_count": "Результати підрахунку",
+ "smw_printername_csv": "ЕкÑпорт у CSV",
+ "smw_printername_dsv": "ЕкÑпорт DSV",
+ "smw_printername_debug": "Ðалагоджувальний запит (Ð´Ð»Ñ Ñ„Ð°Ñ…Ñ–Ð²Ñ†Ñ–Ð²)",
+ "smw_printername_embedded": "Включити вміÑÑ‚ Ñторінки",
+ "smw_printername_json": "ЕкÑпорт у JSON",
+ "smw_printername_list": "СпиÑок",
+ "smw_printername_plainlist": "Звичайний ÑпиÑок",
+ "smw_printername_ol": "ПерерахуваннÑ",
+ "smw_printername_ul": "СпиÑок",
+ "smw_printername_table": "ТаблицÑ",
+ "smw_printername_broadtable": "Широка таблицÑ",
+ "smw_printername_template": "Шаблон",
+ "smw_printername_templatefile": "Файл шаблону",
+ "smw_printername_rdf": "екÑпорт у RDF",
+ "smw_printername_category": "КатегоріÑ",
+ "validator-type-class-SMWParamSource": "текÑÑ‚",
+ "smw-paramdesc-limit": "МакÑимальне чиÑло результатів",
+ "smw-paramdesc-offset": "Ð—Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ результату",
+ "smw-paramdesc-headers": "Показувати заголовки (або назви влаÑтивоÑтей)",
+ "smw-paramdesc-mainlabel": "Заголовок ÑÑ‚Ð¾Ð²Ð¿Ñ†Ñ Ñ–Ð· назвою Ñторінки",
+ "smw-paramdesc-link": "Показувати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ виглÑді поÑилань",
+ "smw-paramdesc-intro": "ТекÑÑ‚, Ñкий показувати перед результатами запиту, Ñкщо такі Ñ”",
+ "smw-paramdesc-outro": "ТекÑÑ‚, Ñкий показувати піÑÐ»Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ–Ð² запиту, Ñкщо такі Ñ”",
+ "smw-paramdesc-default": "ТекÑÑ‚, Ñкий показувати у випадку відÑутноÑÑ‚Ñ– результатів запиту",
+ "smw-paramdesc-sep": "Розділювач між результатами",
+ "smw-paramdesc-propsep": "Розділювач між влаÑтивоÑÑ‚Ñми у кінцевому запиÑÑ–",
+ "smw-paramdesc-valuesep": "Розділювач між значеннÑми змінної Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ–",
+ "smw-paramdesc-showsep": "Показувати роздільник у верхній чаÑтині CSV-файлу (\"sep=<значеннÑ>\")",
+ "smw-paramdesc-distribution": "ЗаміÑÑ‚ÑŒ показу уÑÑ–Ñ… значень, підрахувати Ñ—Ñ… чаÑтоту Ñ– показати це",
+ "smw-paramdesc-distributionsort": "Ð¡Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ розподілу за чиÑлом входжень.",
+ "smw-paramdesc-distributionlimit": "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð¿Ð¾Ð´Ñ–Ð»Ñƒ значень за чиÑлом деÑких значень.",
+ "smw-paramdesc-aggregation": "Уточніть, чого має ÑтоÑуватиÑÑ Ð°Ð³Ñ€ÐµÐ³Ð°Ñ†Ñ–Ñ",
+ "smw-paramdesc-template": "Ðазва шаблону, Ñкий викориÑтовувати Ð´Ð»Ñ Ð²Ð¸Ð²Ð¾Ð´Ñƒ результатів запиту",
+ "smw-paramdesc-columns": "КількіÑÑ‚ÑŒ Ñтовпців Ð´Ð»Ñ Ð²Ð¸Ð²Ð¾Ð´Ñƒ результатів запиту",
+ "smw-paramdesc-userparam": "ЗначеннÑ, що передаєтьÑÑ Ð² кожне Ð·Ð²ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ шаблону при викориÑтанні шаблонів",
+ "smw-paramdesc-class": "Додатковий ÐºÐ»Ð°Ñ CSS Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñ ÑпиÑку",
+ "smw-paramdesc-introtemplate": "Ðазва шаблону, Ñкий показувати перед результатами запиту, Ñкщо такі Ñ”",
+ "smw-paramdesc-outrotemplate": "Ðазва шаблону, Ñкий показувати піÑÐ»Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ–Ð² запиту, Ñкщо такі Ñ”",
+ "smw-paramdesc-embedformat": "Теґ HTML, Ñкий викориÑтовувати Ð´Ð»Ñ Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÑ–Ð²",
+ "smw-paramdesc-embedonly": "Ðе показувати заголовків",
+ "smw-paramdesc-table-class": "Додатковий ÐºÐ»Ð°Ñ CSS Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–",
+ "smw-paramdesc-table-transpose": "Відобразити заголовки таблиці по вертикалі, а результати - по горизонталі",
+ "smw-paramdesc-rdfsyntax": "Який ÑинтакÑÐ¸Ñ RDF вживати",
+ "smw-paramdesc-csv-sep": "Вказує роздільник колонок",
+ "smw-paramdesc-csv-valuesep": "Вказує роздільник значень",
+ "smw-paramdesc-csv-merge": "Поєднати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ€Ñдків Ñ– Ñтовпців з ідентичним ідентифікатором Ñуб'єкта (aka перший Ñтовпчик)",
+ "smw-paramdesc-csv-bom": "Додати BOM (Ñимвол Ð´Ð»Ñ Ñигнальної ÑутноÑÑ‚Ñ–) у верху вихідного файлу",
+ "smw-paramdesc-dsv-separator": "ВикориÑтовувати роздільник",
+ "smw-paramdesc-dsv-filename": "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ DSV",
+ "smw-paramdesc-filename": "Ім'Ñ Ð²Ð¸Ñ…Ñ–Ð´Ð½Ð¾Ð³Ð¾ файлу",
+ "smw-smwdoc-description": "Показує таблицю вÑÑ–Ñ… параметрів, Ñкі можуть бути викориÑтані Ð´Ð»Ñ Ð²ÐºÐ°Ð·Ð°Ð½Ð¾Ð³Ð¾ формату результату разом із типовими значеннÑми Ñ– опиÑами.",
+ "smw-smwdoc-default-no-parameter-list": "Цей результат не надає Ñпецифичні Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ñƒ параметри.",
+ "smw-smwdoc-par-format": "Результуючий формат Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² документації.",
+ "smw-smwdoc-par-parameters": "Які параметри показувати. \"specific\" Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð½Ð¸Ñ… за форматом, \"base\" Ð´Ð»Ñ Ð´Ð¾Ñтупних в уÑÑ–Ñ… форматах, та \"all\" Ð´Ð»Ñ Ð¾Ð±Ð¾Ñ….",
+ "smw-paramdesc-sort": "ВлаÑтивіÑÑ‚ÑŒ Ð´Ð»Ñ ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ за",
+ "smw-paramdesc-order": "ПорÑдок ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ",
+ "smw-paramdesc-searchlabel": "ТекÑÑ‚ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ",
+ "smw-paramdesc-named_args": "Імена аргументів, переданих в шаблон",
+ "smw-paramdesc-template-arguments": "Задає Ñк іменовані аргументи передаютьÑÑ Ð² шаблон",
+ "smw-paramdesc-import-annotation": "Додаткові анотовані дані, що мають бути Ñкопійовані під Ñ‡Ð°Ñ Ð¿Ð°Ñ€Ñингу Ñуб'єкта",
+ "smw-paramdesc-export": "Параметри екÑпорту",
+ "smw-paramdesc-prettyprint": "Вивід докладного друку, Ñкий відображає додаткові відÑтупи та Ñимволи",
+ "smw-paramdesc-json-unescape": "У виводі Ñ” неекрановані коÑÑ– риÑки Ñ– багатобайтні Ñимволи Юнікоду",
+ "smw-paramdesc-json-type": "Тип Ñеріалізації",
+ "smw-paramdesc-source": "Ðльтернативне джерело запиту",
+ "smw-paramdesc-jsonsyntax": "JSON ÑинтакÑÐ¸Ñ Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑтаннÑ",
+ "smw-printername-feed": "Канал RSS та Atom",
+ "smw-paramdesc-feedtype": "Тип каналу",
+ "smw-paramdesc-feedtitle": "ТекÑÑ‚, Ñкий необхідно викориÑтовувати Ñк назву каналу",
+ "smw-paramdesc-feeddescription": "ТекÑÑ‚, Ñкий необхідно викориÑтовувати Ñк Ð¾Ð¿Ð¸Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ",
+ "smw-paramdesc-feedpagecontent": "ВміÑÑ‚ Ñторінки буде відображатиÑÑ Ð· каналу",
+ "smw-label-feed-description": "$1 $2 канал",
+ "smw-paramdesc-mimetype": "Тип медіафайлу (тип MIME) Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ñƒ на виході",
+ "smw_iq_disabled": "Семантичні запити було вимкнено у цій вікі.",
+ "smw_iq_moreresults": "... подальші результати",
+ "smw_parseerror": "Вказане Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ â€” незрозуміле.",
+ "smw_notitle": "«$1» неможливо викориÑтати у ÑкоÑÑ‚Ñ– назви Ñторінки у цій вікі.",
+ "smw_noproperty": "\" $1 \" не можна викориÑтовувати Ñк ім'Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– в цій вікі.",
+ "smw_wrong_namespace": "Тут дозволено лише Ñторінки в проÑторі назв «$1».",
+ "smw_manytypes": "Ð”Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– означено більше одного типу даних.",
+ "smw_emptystring": "ВикориÑтовувати пуÑÑ‚Ñ– Ñ€Ñдки не дозволÑєтьÑÑ.",
+ "smw_notinenum": "«$1» не Ñ” у ÑпиÑку ($2) [[Property:Allows value|дозволених значень]] Ð´Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$3».",
+ "smw-datavalue-constraint-error-allows-value-list": "«$1» немає в ÑпиÑку ($2) [[Property:Allows value|дозволених значень]] Ð´Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$3».",
+ "smw-datavalue-constraint-error-allows-value-range": "«$1» не перебуває в межах діапазону «$2», вказаного в обмеженні [[Property:Allows value|дозволених значень]] Ð´Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$3».",
+ "smw_noboolean": "«$1» не розпізнаєтьÑÑ Ñк булове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ (Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«Ñ‚Ð°Ðº/ні»).",
+ "smw_true_words": "true,t,yes,y,так,т",
+ "smw_false_words": "false,f,no,n,ні,н",
+ "smw_nofloat": "«$1» не Ñ” чиÑлом",
+ "smw_infinite": "Такі довгі чиÑла, Ñк «$1» не підтримуютьÑÑ.",
+ "smw_unitnotallowed": "«$1» не оголошена Ñк допуÑтима Ð¾Ð´Ð¸Ð½Ð¸Ñ†Ñ Ð²Ð¸Ð¼Ñ–Ñ€Ñƒ цієї влаÑтивоÑÑ‚Ñ–.",
+ "smw_nounitsdeclared": "Ð”Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– не оголошені одиниці вимірюваннÑ.",
+ "smw_novalues": "Ðе вказано значень.",
+ "smw_nodatetime": "Дата «$1» — незрозуміла.",
+ "smw_toomanyclosing": "«$1» зуÑтрічаєтьÑÑ Ð·Ð°Ð½Ð°Ð´Ñ‚Ð¾ багато разів.",
+ "smw_noclosingbrackets": "ДеÑкі з «<nowiki>[[</nowiki>» у запиті не було закрито відповідними «]]».",
+ "smw_misplacedsymbol": "Символ «$1» викориÑтано у невідповідному міÑці.",
+ "smw_unexpectedpart": "Фрагмент запиту «$1» — незрозумілий. Результати можуть відрізнÑтиÑÑŒ від очікуваних.",
+ "smw_emptysubquery": "ДеÑкий підзапит не міÑтить інформації про умову.",
+ "smw_misplacedsubquery": "ДеÑкий підзапит був викориÑтаний у невідповідному міÑці.",
+ "smw_valuesubquery": "Підзапити не підтримуютьÑÑ Ð´Ð»Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ влаÑтивоÑÑ‚Ñ– «$1».",
+ "smw_badqueryatom": "Певна чаÑтина «<nowiki>[[…]]</nowiki>» — незрозуміла.",
+ "smw_propvalueproblem": "Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$1» — незрозуміле.",
+ "smw_noqueryfeature": "Певна Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ не підтримуєтьÑÑ Ñƒ цій вікі, тому Ñ†Ñ Ñ‡Ð°Ñтина запиту була пропущена ($1).",
+ "smw_noconjunctions": "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð»Ð¾Ð³Ñ–Ñ‡Ð½Ð¾Ð³Ð¾ «і» не підтримуєтьÑÑ Ñƒ цій вікі, тому Ñ†Ñ Ñ‡Ð°Ñтина запиту була пропущена ($1).",
+ "smw_nodisjunctions": "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð»Ð¾Ð³Ñ–Ñ‡Ð½Ð¾Ð³Ð¾ «або» не підтримуєтьÑÑ Ñƒ цій вікі, тому Ñ†Ñ Ñ‡Ð°Ñтина запиту була пропущена ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Вказана умова запиту не може бути опрацьована|Вказані умови запиту не можуть бути опрацьовані}} через чинні у вікі Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð² розмірі або глибині запиту: <code>$1</code>.",
+ "smw_notemplategiven": "Щоб цей формат запиту Ñпрацював, потрібно вказати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð° «template».",
+ "smw_db_sparqlqueryproblem": "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ результат запиту до бази даних SPARQL. Ð¦Ñ Ð¼Ð¾Ð¶Ðµ бути тимчаÑова помилка або проблема в програмному забезпеченні бази даних.",
+ "smw_db_sparqlqueryincomplete": "Пошук відповіді на запит виÑвивÑÑ Ð·Ð°Ð½Ð°Ð´Ñ‚Ð¾ Ñкладним Ñ– був перерваний. ДеÑкі результати можуть бути не показані. По можливоÑÑ‚Ñ– Ñпробуйте ÑпроÑтити запит.",
+ "smw_type_header": "ВлаÑтивоÑÑ‚Ñ– типу даних «$1»",
+ "smw_typearticlecount": "{{PLURAL:$1|ПоказуєтьÑÑ|ПоказуютьÑÑ|ПоказуєтьÑÑ}} $1 {{PLURAL:$1|влаÑтивіÑÑ‚ÑŒ|влаÑтивоÑÑ‚Ñ–|влаÑтивоÑтей}} цього типу даних.",
+ "smw_attribute_header": "Сторінки, що викориÑтовують влаÑтивіÑÑ‚ÑŒ «$1»",
+ "smw_attributearticlecount": "{{PLURAL:$1|ПоказуєтьÑÑ|ПоказуютьÑÑ|ПоказуєтьÑÑ}} $1 {{PLURAL:$1|Ñторінка, що викориÑтовує|Ñторінки, що викориÑтовують|Ñторінок, що викориÑтовують}} цю влаÑтивіÑÑ‚ÑŒ.",
+ "smw-propertylist-subproperty-header": "ПідвлаÑтивоÑÑ‚Ñ–",
+ "smw-propertylist-redirect-header": "Синоніми",
+ "smw-propertylist-error-header": "Сторінки з невідповідними призначеннÑми",
+ "smw-propertylist-count": "Показано $1 {{PLURAL:$1|пов'Ñзану ÑутніÑÑ‚ÑŒ|пов'Ñзані ÑутноÑÑ‚Ñ–|пов'Ñзаних ÑутноÑтей}}.",
+ "smw-propertylist-count-with-restricted-note": "Показано $1 {{PLURAL:$1|пов'Ñзану ÑутніÑÑ‚ÑŒ|пов'Ñзані ÑутноÑÑ‚Ñ–|пов'Ñзаних ÑутноÑтей}} (доÑтупно більше, але кількіÑÑ‚ÑŒ обмежено до «$2»).",
+ "smw-propertylist-count-more-available": "Показано $1 {{PLURAL:$1|пов'Ñзану ÑутніÑÑ‚ÑŒ|пов'Ñзані ÑутноÑÑ‚Ñ–|пов'Ñзаних ÑутноÑтей}} (доÑтупно більше).",
+ "exportrdf": "ЕкÑпорт Ñторінок у RDF",
+ "smw_exportrdf_docu": "Ð¦Ñ Ñторінка дозволÑÑ” отримати дані із Ñторінки у форматі RDF. Введіть назви Ñторінок, по одній у Ñ€Ñдку, в текÑтовому полі, що знаходитьÑÑ Ð½Ð¸Ð¶Ñ‡Ðµ.",
+ "smw_exportrdf_recursive": "РекурÑивно екÑпортувати уÑÑ– пов’Ñзані Ñторінки. Зауважте: результат може бути об’ємним.",
+ "smw_exportrdf_backlinks": "Також екÑпортувати уÑÑ– Ñторінки, що звертаютьÑÑ Ð´Ð¾ екÑпортованих. Генерує RDF з можливіÑÑ‚ÑŽ навігації.",
+ "smw_exportrdf_lastdate": "Ðе екÑпортувати Ñторінки, що не були змінені з чаÑу заданого моменту в чаÑÑ–.",
+ "smw_exportrdf_submit": "ЕкÑпорт",
+ "uriresolver": "Оброблювач URI",
+ "properties": "ВлаÑтивоÑÑ‚Ñ–",
+ "smw_properties_docu": "ÐаÑтупні влаÑтивоÑÑ‚Ñ– викориÑтовуютьÑÑ Ñƒ цій вікі.",
+ "smw_property_template": "$1 із типом $2 ($3 {{PLURAL:$3|1=викориÑтаннÑ|викориÑтань}})",
+ "smw_propertylackspage": "Кожна влаÑтивіÑÑ‚ÑŒ повинна мати Ñторінку, що Ñ—Ñ— опиÑує.",
+ "smw_propertylackstype": "Ð”Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– не було означено типу даних (поки що викориÑтовуєтьÑÑ $1).",
+ "smw_propertyhardlyused": "Ð¦Ñ Ð²Ð»Ð°ÑтивіÑÑ‚ÑŒ викориÑтовуєтьÑÑ Ñ€ÑƒÑˆÑ–Ñ”Ð¼ вікі.",
+ "smw-property-name-invalid": "ВлаÑтивіÑÑ‚ÑŒ $1 не може викориÑтовуватиÑÑ (неприпуÑтиме ім'Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–).",
+ "smw-property-name-reserved": "«$1» входить у чиÑло зарезервованих імен Ñ– не повинне бути викориÑтане Ñк влаÑтивіÑÑ‚ÑŒ. Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ те, чому це ім'Ñ Ð·Ð°Ñ€ÐµÐ·ÐµÑ€Ð²Ð¾Ð²Ð°Ð½Ð¾, може міÑтитиÑÑ Ñƒ [https://www.semantic-mediawiki.org/wiki/Help:Property_naming довідковій Ñторінці].",
+ "smw-sp-property-searchform": "Відобразити влаÑтивоÑÑ‚Ñ–, Ñкі міÑÑ‚ÑÑ‚ÑŒ:",
+ "smw-sp-property-searchform-inputinfo": "Ð’Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ñ‡ÑƒÑ‚Ð»Ð¸Ð²Ðµ до регіÑтру Ñ– коли викориÑтовуєтьÑÑ Ð´Ð»Ñ Ñ„Ñ–Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ñ–Ñ—, відображаютьÑÑ Ð»Ð¸ÑˆÐµ Ñ‚Ñ– влаÑтивоÑÑ‚Ñ–, Ñкі відповідають умові.",
+ "smw-special-property-searchform": "Показати влаÑтивоÑÑ‚Ñ–, Ñкі міÑÑ‚ÑÑ‚ÑŒ:",
+ "smw-special-property-searchform-inputinfo": "Введені дані чутливі до регіÑтру, Ñ– Ñкщо Ñ—Ñ… викориÑтати Ð´Ð»Ñ Ñ„Ñ–Ð»ÑŒÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ, виведені будуть лише Ñ‚Ñ– влаÑтивоÑÑ‚Ñ–, Ñкі відповідають цій умові.",
+ "smw-special-property-searchform-options": "Опції",
+ "smw-special-wantedproperties-filter-label": "Фільтр:",
+ "smw-special-wantedproperties-filter-none": "Ðемає",
+ "smw-special-wantedproperties-filter-unapproved": "Ðезатверджені",
+ "smw-special-wantedproperties-filter-unapproved-desc": "ÐžÐ¿Ñ†Ñ–Ñ Ñ„Ñ–Ð»ÑŒÑ‚Ñ€Ð°, Ñка викориÑтовуєтьÑÑ Ñƒ Ñполученні з режимом авторитету.",
+ "concepts": "Концепції",
+ "smw-special-concept-docu": "Вікі [https://www.semantic-mediawiki.org/wiki/Help:Concepts concept] можна переглÑнути Ñк \"динамічну категорію\", тобто Ñк збірку Ñторінок, що не Ñтворені вручну, а опрацьовані Семантичною Медіавікі на оÑнові опиÑу заданого запиту.",
+ "smw-special-concept-header": "Перелік концепцій",
+ "smw-special-concept-count": "{{PLURAL:$1|ÐаÑтупне понÑÑ‚Ñ‚Ñ|$1 наÑтупні понÑÑ‚Ñ‚Ñ|$1 наÑтупних понÑÑ‚ÑŒ}} {{PLURAL:$1|перераховане|переховані|перераховано}}.",
+ "smw-special-concept-empty": "ПонÑÑ‚Ñ‚Ñ Ð½Ðµ знайдено.",
+ "unusedproperties": "ВлаÑтивоÑÑ‚Ñ–, що не викориÑтовуютьÑÑ",
+ "smw-unusedproperties-docu": "Ðа цій Ñторінці перераховані [https://www.semantic-mediawiki.org/wiki/Unused_properties невикориÑтані влаÑтивоÑÑ‚Ñ–], Ñкі були оголошені, хоча ніÑкі інші Ñторінки Ñ—Ñ… не викориÑтовують. Ð”Ð»Ñ Ð´Ð¸Ñ„ÐµÑ€ÐµÐ½Ñ†Ñ–Ð¹Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ виглÑду див. Ñпеціальні Ñторінки [[Special:Properties|вÑÑ–Ñ…]] або [[Special:WantedProperties|потрібних влаÑтивоÑтей]].",
+ "smw-unusedproperty-template": "$1 із типом даних $2",
+ "wantedproperties": "Потрібні влаÑтивоÑÑ‚Ñ–",
+ "smw-wantedproperties-docu": "Ðа цій Ñторінці перелічені [https://www.semantic-mediawiki.org/wiki/Wanted_properties бажані влаÑтивоÑÑ‚Ñ–], Ñкі викориÑтовуютьÑÑ Ñƒ вікі, але не мають Ñторінки опиÑу. Диференційований показ див. на Ñпеціальних Ñторінках [[Special:Properties|уÑÑ–Ñ…]] або [[Special:UnusedProperties|невикориÑтовуваних влаÑтивоÑтей]].",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|зверненнÑ|зверненнÑ|звернень}})",
+ "smw-special-wantedproperties-docu": "Ðа цій Ñторінці перелічені [https://www.semantic-mediawiki.org/wiki/Wanted_properties бажані влаÑтивоÑÑ‚Ñ–], Ñкі викориÑтовуютьÑÑ Ñƒ вікі, але не мають Ñторінки опиÑу. Диференційований показ див. на Ñпеціальних Ñторінках [[Special:Properties|уÑÑ–Ñ…]] або [[Special:UnusedProperties|невикориÑтовуваних влаÑтивоÑтей]].",
+ "smw-special-wantedproperties-template": "$1 ($2 {{PLURAL:$2|зверненнÑ|зверненнÑ|звернень}})",
+ "smw_purge": "Оновити",
+ "smw-purge-failed": "Ðе вдалоÑÑŒ оновити",
+ "types": "Типи",
+ "smw_types_docu": "СпиÑок [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes доÑтупних типів даних], де кожен [https://www.semantic-mediawiki.org/wiki/Help:Datatype тип] репрезентує унікальний набір атрибутів, що опиÑують Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· точки зору характериÑтик Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¹ відображеннÑ, похідних від приÑвоєної влаÑтивоÑÑ‚Ñ–.",
+ "smw-special-types-no-such-type": "\"$1\" невідомий або не був указаний Ñк правильний тип даних.",
+ "smw-statistics": "Семантична ÑтатиÑтика",
+ "smw-statistics-property-instance": "{{PLURAL:$1|1= ЗначеннÑ|ЗначеннÑ}} влаÑтивоÑÑ‚Ñ– (вÑього)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|ВлаÑтивіÑÑ‚ÑŒ|ВлаÑтивоÑÑ‚Ñ–|ВлаÑтивоÑтей}}]] (вÑього)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|ВлаÑтивіÑÑ‚ÑŒ|ВлаÑтивоÑÑ‚Ñ–|ВлаÑтивоÑтей}} (вÑього)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|ВлаÑтивіÑÑ‚ÑŒ|ВлаÑтивоÑÑ‚Ñ–}}]] (викориÑтовуєтьÑÑ Ñ‰Ð¾Ð½Ð°Ð¹Ð¼ÐµÐ½ÑˆÐµ з одним значеннÑм)",
+ "smw-statistics-property-page": "{{PLURAL:$1|ВлаÑтивіÑÑ‚ÑŒ|ВлаÑтивоÑÑ‚Ñ–|ВлаÑтивоÑтей}} (зареєÑтровано на Ñторінці)",
+ "smw-statistics-property-type": "{{PLURAL:$1|ВлаÑтивіÑÑ‚ÑŒ|ВлаÑтивоÑÑ‚Ñ–|ВлаÑтивоÑтей}} (приÑвоєно тип даних)",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|Запит|Запити}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|Запит|Запити}}]]",
+ "smw-statistics-query-size": "Розмір запиту",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|ПонÑÑ‚Ñ‚Ñ|ПонÑÑ‚Ñ‚Ñ|ПонÑÑ‚ÑŒ}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|ПонÑÑ‚Ñ‚Ñ|ПонÑÑ‚Ñ‚Ñ|ПонÑÑ‚ÑŒ}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|Підоб'єкт|Підоб'єкти}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|Підоб'єкт|Підоб'єкти}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|Тип даних|Типи даних|Типів даних}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–}} ([[Special:ProcessingErrorList|{{PLURAL:$1|неправильна анотаціÑ|неправильні анотації}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–}} ({{PLURAL:$1|некоректна анотаціÑ|некоректні анотації}})",
+ "smw-statistics-delete-count": "{{PLURAL:$1|ЗаÑтаріла ÑутніÑÑ‚ÑŒ (позначена до вилученнÑ)|ЗаÑтарілі ÑутноÑÑ‚Ñ– (позначені до вилученнÑ)}}",
+ "smw_uri_doc": "Оброблювач URI виконує [$1 «W3C TAG finding on httpRange-14»].\nÐ¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñпрощує пошук Ñемантичної інформації.",
+ "ask": "Семантичний пошук",
+ "smw-ask-help": "Цей розділ міÑтить поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° деÑкі Ñторінки, де поÑÑнюєтьÑÑ, Ñк викориÑтовувати ÑинтакÑÐ¸Ñ <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages ÐžÐ±Ð¸Ñ€Ð°Ð½Ð½Ñ Ñторінок] опиÑує, Ñк обирати Ñторінки Ñ– будувати умови\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Оператори пошуку] перелічує оператори пошуку, включно з операторами Ð´Ð»Ñ Ð´Ñ–Ð°Ð¿Ð°Ð·Ð¾Ð½Ñ–Ð² Ñ– знаки підÑтановки\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information Відображувана інформаціÑ] виділÑÑ” викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½ÑŒ виводу та опції форматуваннÑ",
+ "smw_ask_sortby": "Сортувати за Ñтовпцем (необов’Ñзково)",
+ "smw_ask_ascorder": "За зроÑтаннÑм",
+ "smw_ask_descorder": "За ÑпаданнÑм",
+ "smw-ask-order-rand": "Випадково",
+ "smw_ask_submit": "Знайти",
+ "smw_ask_editquery": "Редагувати запит",
+ "smw_add_sortcondition": "[Додати умову ÑортуваннÑ]",
+ "smw-ask-sort-add-action": "Додати умову ÑортуваннÑ",
+ "smw_ask_hidequery": "Приховати запит (компактний виглÑд)",
+ "smw_ask_help": "Довідка про запити",
+ "smw_ask_queryhead": "Умова",
+ "smw_ask_printhead": "Вибір роздруківки",
+ "smw_ask_printdesc": "(одна назва влаÑтивоÑÑ‚Ñ– на Ñ€Ñдок)",
+ "smw_ask_format_as": "Форматувати Ñк:",
+ "smw_ask_defaultformat": "за замовчуваннÑм",
+ "smw_ask_otheroptions": "Інші опції",
+ "smw-ask-otheroptions-info": "Цей розділ міÑтить параметри, Ñкі змінюють роздруківку заÑви. ОпиÑи параметрів можна переглÑдати, навівши на них курÑор.",
+ "smw-ask-otheroptions-collapsed-info": "Будь лаÑка, викориÑтовуйте значок \"+\", щоб переглÑнути вÑÑ– доÑтупні параметри",
+ "smw_ask_show_embed": "Показати включений код",
+ "smw_ask_hide_embed": "Приховати включений код",
+ "smw_ask_embed_instr": "Щоб вбудувати цей запит в вікіÑторінку, викориÑтовуйте поданий нижче код.",
+ "smw-ask-delete": "Вилучити",
+ "smw-ask-sorting": "СортуваннÑ",
+ "smw-ask-options": "Опції",
+ "smw-ask-options-sort": "Опції ÑортуваннÑ",
+ "smw-ask-format-options": "Формат і опції",
+ "smw-ask-parameters": "Параметри",
+ "smw-ask-search": "Пошук",
+ "smw-ask-debug": "ÐалагодженнÑ",
+ "smw-ask-debug-desc": "Генерує інформацію про запит щодо налагодженнÑ",
+ "smw-ask-no-cache": "Відімкнути кеш запиту",
+ "smw-ask-no-cache-desc": "Результати без кешу запиту",
+ "smw-ask-result": "Результат",
+ "smw-ask-empty": "ОчиÑтити вÑе",
+ "smw-ask-download-link-desc": "Завантажити запитувані результати у форматі $1",
+ "smw-ask-format": "Формат",
+ "smw-ask-format-selection-help": "Довідка Ð´Ð»Ñ Ð²Ð¸Ð±Ñ€Ð°Ð½Ð¾Ð³Ð¾ формату: $1.",
+ "smw-ask-condition-change-info": "Умову було змінено й пошукові треба запуÑтити запит повторно, щоб вивеÑти результати, Ñкі відповідають новим вимогам.",
+ "smw-ask-input-assistance": "Допомога при вводі",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Допомога з введеннÑм] надаєтьÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð»Ñ–Ð² друку, ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° умови. Поле умови повинно мати один з таких префікÑів:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑƒ підказок влаÑтивоÑÑ‚Ñ– (наприклад, <code>[[p:Має ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑƒ підказок категорії",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> Ð´Ð»Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÑƒ підказок концепції",
+ "smw-ask-format-change-info": "Формат було змінено, Ñ– запит треба виконати ще раз, щоб він відповідав новим параметрам та опціÑм візуалізації.",
+ "smw-ask-format-export-info": "Обраний формат Ñ” форматом екÑпорту, Ñкий не має візуальної репрезентації, а тому результати надаютьÑÑ Ð»Ð¸ÑˆÐµ Ñк завантаженнÑ.",
+ "smw-ask-query-search-info": "Ðа запит <code><nowiki>$1</nowiki></code> було отримано відповідь із {{PLURAL:$3|1=<code>$2</code> (з кешу)|<code>$2</code> (з кешу)|<code>$2</code>}} за $4 {{PLURAL:$4|Ñекунду|Ñекунди|Ñекунд}}.",
+ "searchbyproperty": "Шукати за влаÑтивіÑÑ‚ÑŽ",
+ "processingerrorlist": "СпиÑок помилок опрацюваннÑ",
+ "propertylabelsimilarity": "Звіт про подібніÑÑ‚ÑŒ назв влаÑтивоÑтей",
+ "smw-processingerrorlist-intro": "Ðаведений ÑпиÑок подає загальний оглÑд помилок опрацюваннÑ, що виникли у зв'Ñзку з [https://www.semantic-mediawiki.org/ Семантичною MediaWiki]. РекомендуєтьÑÑ Ñ€ÐµÐ³ÑƒÐ»Ñрно Ñтежити за цим ÑпиÑком Ñ– виправлÑти недійÑні анотації значень.",
+ "smw_sbv_docu": "Шукати вÑÑ– Ñторінки, що мають вказану влаÑтивіÑÑ‚ÑŒ Ñ– значеннÑ.",
+ "smw_sbv_novalue": "Введіть правильне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– або переглÑньте вÑÑ– Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$1».",
+ "smw_sbv_displayresult": "СпиÑок вÑÑ–Ñ… Ñторінок, що мають влаÑтивіÑÑ‚ÑŒ «$1» із значеннÑм «$2»",
+ "smw_sbv_displayresultfuzzy": "СпиÑок вÑÑ–Ñ… Ñторінок, що мають влаÑтивіÑÑ‚ÑŒ «$1» із значеннÑм «$2». ОÑкільки було лише кілька результатів, показано також Ñторінки, що міÑÑ‚ÑÑ‚ÑŒ близькі Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ–.",
+ "smw_sbv_property": "ВлаÑтивіÑÑ‚ÑŒ:",
+ "smw_sbv_value": "ЗначеннÑ:",
+ "smw_sbv_submit": "Знайти",
+ "browse": "ПереглÑд вікі",
+ "smw_browselink": "ПереглÑд влаÑтивоÑтей",
+ "smw_browse_article": "Введіть назву Ñторінки, з Ñкої почати переглÑд.",
+ "smw_browse_go": "Перейти",
+ "smw_browse_show_incoming": "Показати вхідні влаÑтивоÑÑ‚Ñ–",
+ "smw_browse_hide_incoming": "Приховати вхідні влаÑтивоÑÑ‚Ñ–",
+ "smw_browse_no_outgoing": "Ð¦Ñ Ñторінка не міÑтить влаÑтивоÑтей.",
+ "smw_browse_no_incoming": "Ðа цю Ñторінку не поÑилаєтьÑÑ Ð¶Ð¾Ð´Ð½Ð° влаÑтивіÑÑ‚ÑŒ.",
+ "smw-browse-from-backend": "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– отримуєтьÑÑ Ð· бекенду.",
+ "smw-browse-intro": "Ð¦Ñ Ñторінка надає подробиці про тему або примірник ÑутноÑÑ‚Ñ–, будь лаÑка, введіть назву об'єкта, Ñкий Ñлід перевірити.",
+ "smw-browse-invalid-subject": "Перевірка Ñуб'єкта повернулаÑÑ Ð· помилкою «$1».",
+ "smw-browse-api-subject-serialization-invalid": "Суб'єкт має некоректний формат Ñеріалізації.",
+ "smw-browse-js-disabled": "Є підозра, що JavaScript відключений або недоÑтупний, Ñ– ми рекомендуємо викориÑтовувати браузер, де він підтримуєтьÑÑ. Інші варіанти обговорюютьÑÑ Ð½Ð° Ñторінці параметра конфігурації [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>].",
+ "smw-browse-show-group": "Показати групи",
+ "smw-browse-hide-group": "Приховати групи",
+ "smw-noscript": "Ð¦Ñ Ñторінка чи Ð´Ñ–Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” роботи JavaScript. Будь лаÑка, увімкніть JavaScript у Ñвоєму браузері, або ÑкориÑтайтеÑÑ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð¼, де він підтримуєтьÑÑ, щоб функціонал діÑв Ñ– поÑлуги надавалиÑÑ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾ до запиту. Якщо Вам потрібна додаткова підтримка, будь лаÑка, переглÑньте Ñторінку довідки [https://www.semantic-mediawiki.org/wiki/Help:Noscript «Noscript»]",
+ "smw_inverse_label_default": "$1 з",
+ "smw_inverse_label_property": "Ðазва зворотної влаÑтивоÑÑ‚Ñ–",
+ "pageproperty": "Пошук влаÑтивоÑтей Ñторінки",
+ "smw_pp_docu": "Введіть або Ñторінку та влаÑтивіÑÑ‚ÑŒ, або проÑто влаÑтивіÑÑ‚ÑŒ, щоб отримати вÑÑ– задані значеннÑ.",
+ "smw_pp_from": "Зі Ñторінки:",
+ "smw_pp_type": "ВлаÑтивіÑÑ‚ÑŒ:",
+ "smw_pp_submit": "Знайти",
+ "smw_result_prev": "ПопереднÑ",
+ "smw_result_next": "ÐаÑтупна",
+ "smw_result_results": "Результати",
+ "smw_result_noresults": "Ðемає результатів.",
+ "smwadmin": "ÐдмініÑтративні та обÑлуговувальні функції",
+ "smw-admin-statistics-job-title": "СтатиÑтика завдань",
+ "smw-admin-statistics-job-docu": "СтатиÑтика завдань показує інформацію про заплановані Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾Ñ— MediaWiki, Ñкі ще не було виконано. КількіÑÑ‚ÑŒ завдань може бути дещо неточною або міÑтити невдалі Ñпроби. Будь лаÑка, переглÑньте [https://www.mediawiki.org/wiki/Manual:Job_queue поÑібник], щоб дізнатиÑÑ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð¸Ñ†Ñ–.",
+ "smw-admin-statistics-querycache-title": "Запит на ÑтатиÑтику кешу",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache QueryCache] не було увімкнено в цій вікі, а тому немає доÑтупної ÑтатиÑтики.",
+ "smw-admin-statistics-querycache-explain": "СтатиÑтика кешу повинна міÑтити тимчаÑові збірні дані, а також отримані дані, зокрема: \n* «misses» — загальна кількіÑÑ‚ÑŒ Ñпроб отримати дані з кешу з недоÑÑжними відповідÑми, що ÑпричинÑÑ” примуÑове прÑме Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ (бази даних, потрійного Ñховища тощо) \n* «deletes» — Ñк загальну кількіÑÑ‚ÑŒ операцій з Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ ÐºÐµÑˆÑƒ (або через Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÐºÐµÑˆÑƒ, або ж через залежноÑÑ‚Ñ– в запитах) \n* «hits» — міÑтить кількіÑÑ‚ÑŒ виведень кешу або із вбудованих (запити, надіÑлані зÑередини вікіÑторінки), або з невбудованих (Ñкщо увімкнено, запити надÑилаютьÑÑ Ñ‚Ð°ÐºÐ¸Ð¼Ð¸ Ñторінками, Ñк Special:Ask, чи API) джерел\n* «medianRetrievalResponseTime» — Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¾Ñ€Ñ–Ñ”Ð½Ñ‚Ð°Ñ†Ñ–Ñ— Ñереднього чаÑу відповіді (в Ñекундах) Ð´Ð»Ñ ÐºÐµÑˆÐ¾Ð²Ð°Ð½Ð¸Ñ… чи некешованих запитів на інформацію за період чаÑу, потрібний Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑу Ñ—Ñ— Ð½Ð°ÐºÐ¾Ð¿Ð¸Ñ‡ÐµÐ½Ð½Ñ \n* «noCache» позначає кількіÑÑ‚ÑŒ запитів без Ñпроб (limit=0, Ð¾Ð¿Ñ†Ñ–Ñ 'no-cache' тощо) Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ñ–Ð² з кешу",
+ "smw-admin-permission-missing": "ДоÑтуп до цієї Ñторінки було заблоковано через відÑутніÑÑ‚ÑŒ відповідних прав. Будь лаÑка, ознайомтеÑÑ Ð·Ñ– Ñторінкою довідки щодо [https://www.semantic-mediawiki.org/wiki/Help:Permissions прав], щоб отримати інформацію про необхідні налаштуваннÑ.",
+ "smw-admin-setupsuccess": "Базу даних налаштовано.",
+ "smw_smwadmin_return": "ПовернутиÑÑŒ до $1",
+ "smw_smwadmin_updatestarted": "Запущено новий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñемантичних даних.\nУÑÑ– збережені дані буде перебудовано чи відновлено за необхідноÑÑ‚Ñ–.\nЗа процеÑом Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ð½Ð° Ñтежити на цій Ñторінці.",
+ "smw_smwadmin_updatenotstarted": "ÐŸÑ€Ð¾Ñ†ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ð¶Ðµ працює.\nÐового запущено не буде.",
+ "smw_smwadmin_updatestopped": "УÑÑ– процеÑи Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ зупинено.",
+ "smw_smwadmin_updatenotstopped": "Щоб зупинити запущений Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ, Ви маєте поÑтавити позначку, аби заÑвідчити, що ви Ñправді впевнені в цьому.",
+ "smw-admin-docu": "Ð¦Ñ Ñторінка допоможе при вÑтановленні, оновленні, обÑлуговуванні та викориÑтанню <a href=\"https://www.semantic-mediawiki.org\">Семантичної MediaWiki</a>, а також забезпечує подальші адмініÑтративні функції Ñ– завданнÑ, так Ñамо Ñк Ñ– ÑтатиÑтику. Ðе забудьте зробити резервну копію вÑÑ–Ñ… важливих даних перед запуÑком адмініÑтративних функцій.",
+ "smw-admin-environment": "Оболонка програмного забезпеченнÑ",
+ "smw-admin-db": "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ð°Ð·Ð¸ даних",
+ "smw-admin-db-preparation": "Триває Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–, Ñ– це може зайнÑти трохи чаÑу, перед тим Ñк результати буде виведено, залежно від розміру та ймовірної оптимізації таблиці.",
+ "smw-admin-dbdocu": "Семантична MediaWiki вимагає Ñвоєї влаÑної Ñтруктури бази даних (Ñ– Ñ” незалежною від MediaWiki, тож не впливає на решту інÑталÑції MediaWiki) Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ñемантичних даних.\nЦю функцію Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð¾Ð¶Ð½Ð° виконувати багато разів без жодної шкоди, але наÑправді вона необхідна лише раз — під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸ оновленнÑ.",
+ "smw-admin-permissionswarn": "Якщо Ñ†Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ зупинена через помилки SQL, вірогідною причиною може бути відÑутніÑÑ‚ÑŒ необхідних прав кориÑтувача, під іменем Ñкого вікі під'єднуєтьÑÑ Ð´Ð¾ бази даних (він вказуєтьÑÑ Ñƒ файлі «LocalSettings.php»). Ð’ такому випадку вам необхідно або дозволити цьому кориÑтувачу Ñтворювати та вилучати таблиці БД, або тимчаÑово ввеÑти в файл «LocalSettings.php» ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача БД «root», або викориÑтати Ñценарій обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ <code>setupStore.php</code>, Ñкий може викориÑтовувати облікові дані адмініÑтратора.",
+ "smw-admin-dbbutton": "Ð†Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð°Ð±Ð¾ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ",
+ "smw-admin-announce": "ЗаÑвити про Вашу вікі",
+ "smw-admin-announce-text": "Якщо Ваша вікі публічна, Ви можете зареєÑтрувати Ñ—Ñ— у <a href=\"https://wikiapiary.com\">WikiApiary</a>, вікі з відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–.",
+ "smw-admin-deprecation-notice-title": "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ заÑтаріліÑÑ‚ÑŒ",
+ "smw-admin-deprecation-notice-docu": "Поданий нижче розділ міÑтить налаштуваннÑ, Ñкі вже заÑтаріли або були уÑунуті, але, Ñк було виÑвлено, вÑе ще Ñ” активними в цій вікі. ОчікуєтьÑÑ, що будь-Ñкий майбутній випуÑк припинить підтримку цих конфігурацій.",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> Ñ” заÑтарілим Ñ– буде уÑунутий у верÑÑ–Ñ— $2",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> вилучить (або замінить)\n {{PLURAL:$2|таку опцію|такі опції}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code> — заÑтарілий параметр Ñ– його буде уÑунено в $2",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> замінено на <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|опціÑ|опції|опцій}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> буде замінено <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> уÑунуто у верÑÑ–Ñ— $2",
+ "smw-admin-deprecation-notice-title-notice": "Прийдешні зміни",
+ "smw-admin-deprecation-notice-title-notice-explanation": "ВиÑвлено, що у цій вікі викориÑтовуютьÑÑ Ñ‚Ð°ÐºÑ– налаштуваннÑ, Ñ– плануєтьÑÑ, що Ñ—Ñ… буде вилучено або змінено в майбутньому випуÑку.",
+ "smw-admin-deprecation-notice-title-replacement": "Замінені або перейменовані налаштуваннÑ",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "Вказаний розділ міÑтить налаштуваннÑ, Ñкі було перейменовано чи змінено в інший ÑпоÑіб, а тому рекомендуємо негайно оновити їхні назви чи формат.",
+ "smw-admin-deprecation-notice-title-removal": "Вилучені налаштуваннÑ",
+ "smw-admin-deprecation-notice-title-removal-explanation": "Перелічені Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ вилучено в попередньому випуÑку, але виÑвлено, що вони вÑе ще викориÑтовуютьÑÑ Ð² цій вікі.",
+ "smw-smwadmin-refresh-title": "Ð’Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ– Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…",
+ "smw_smwadmin_datarefresh": "Перебудова даних",
+ "smw_smwadmin_datarefreshdocu": "Можна відновити вÑÑ– дані Semantic MediaWiki, базуючиÑÑŒ на поточному Ñтані вміÑту вікі. Це можна викориÑтати при відновленні пошкоджених даних або при оновленні даних, Ñкщо внутрішній формат змінивÑÑ Ñ‡ÐµÑ€ÐµÐ· Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð½Ð¾Ð³Ð¾ забезпеченнÑ. ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ виконувати Ñторінка за Ñторінкою, тому займе ÑкийÑÑŒ чаÑ. Ðижче показано Ð¿Ñ€Ð¾Ð³Ñ€ÐµÑ Ð¿Ñ€Ð¾Ñ†ÐµÑу оновленнÑ, даєтьÑÑ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ñ–ÑÑ‚ÑŒ запуÑтити або зупинити Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (Ñкщо Ñ†Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð½Ðµ була заблокована адмініÑтратором).",
+ "smw_smwadmin_datarefreshprogress": "<strong>ÐŸÑ€Ð¾Ñ†ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ð¶Ðµ запущено.</strong>\nÐŸÑ€Ð¾Ñ†ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ð±ÑƒÐ²Ð°Ñ”Ñ‚ÑŒÑÑ Ð¼Ð°Ð»ÐµÐ½ÑŒÐºÐ¸Ð¼Ð¸ порціÑм із кожним відкриттÑм Ñторінки кориÑтувачами, тому він проходить повільно. Щоб швидше закінчити оновленнÑ, можна ÑкориÑтатиÑÑŒ Ñценарієм обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ <code>runJobs.php</code> (викориÑтовуйте опцію <code>--maxjobs 1000</code> щоб обмежити чиÑло оновлень в одному пакеті).\nПриблизний Ñтан запущеного оновленнÑ:",
+ "smw_smwadmin_datarefreshbutton": "Почати Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…",
+ "smw_smwadmin_datarefreshstop": "Зупинити це оновленнÑ",
+ "smw_smwadmin_datarefreshstopconfirm": "Так, Ñ Ð²Ð¿ÐµÐ²Ð½ÐµÐ½{{GENDER:$1|ий|а}}.",
+ "smw-admin-job-scheduler-note": "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ (Ñ‚Ñ–, що увімкнені) у цьому розділі виконуютьÑÑ Ñ‡ÐµÑ€ÐµÐ· чергу завдань, щоб уникнути глухих кутів у ході Ñ—Ñ… виконаннÑ. [https://www.mediawiki.org/wiki/Manual:Job_queue Черга завдань] відповідальна за обробку, тому критично важливо, щоб Ñкрипт обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð°Ð² відповідну ÑпроможніÑÑ‚ÑŒ <code>runJobs.php</code> (див. також параметр конфігурації <code>$wgRunJobsAsync</code>).",
+ "smw-admin-outdateddisposal-title": "Ð›Ñ–ÐºÐ²Ñ–Ð´Ð°Ñ†Ñ–Ñ Ð·Ð°Ñтарілих ÑутноÑтей",
+ "smw-admin-outdateddisposal-intro": "ДеÑкі дії (зміна типу влаÑтивоÑÑ‚Ñ–, Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–Ñторінок чи Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¾Ð²Ð¸Ñ… значень) ÑпричинÑÑ‚ÑŒ поÑву [https://www.semantic-mediawiki.org/wiki/Outdated_entities заÑтарілих ÑутноÑтей], тому рекомендуєтьÑÑ Ñ‡Ð°Ñ Ð²Ñ–Ð´ чаÑу вилучати Ñ—Ñ…, щоб звільнÑти проÑÑ‚Ñ–Ñ€ у пов'Ñзаній таблиці. Залежно від чаÑового інтервалу, визначеного планувальником завдань, Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти деÑкий Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ´ тим, Ñк Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ виконано Ñ– завершено. Залежно від вікна чаÑу, визначеного планувальником завдань, Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти деÑкий Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ´ тим, Ñк Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ виконано й завершено.",
+ "smw-admin-outdateddisposal-active": "Було заплановано Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð· ліквідації заÑтарілих ÑутноÑтей.",
+ "smw-admin-outdateddisposal-button": "Запланувати ліквідацію",
+ "smw-admin-feature-disabled": "Цю функцію було вимкнено Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— вікі. Будь лаÑка, переглÑньте довідкову Ñторінку щодо <a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">налаштувань</a>, або зв'ÑжітьÑÑ Ð· ÑиÑтемним адмініÑтратором.",
+ "smw-admin-propertystatistics-title": "Перебудова ÑтатиÑтики влаÑтивоÑÑ‚Ñ–",
+ "smw-admin-propertystatistics-intro": "Перебудовує вÑÑŽ ÑтатиÑтику викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð»Ð°ÑтивоÑтей, піÑÐ»Ñ Ñ‡Ð¾Ð³Ð¾ оновлює Ñ– виправлÑÑ” [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count кількіÑÑ‚ÑŒ викориÑтань] влаÑтивоÑтей.",
+ "smw-admin-propertystatistics-active": "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð· перебудови ÑтатиÑтики влаÑтивоÑтей заплановано.",
+ "smw-admin-propertystatistics-button": "Запланувати перебудову ÑтатиÑтики",
+ "smw-admin-fulltext-title": "Перебудова повнотекÑтового пошуку",
+ "smw-admin-fulltext-intro": "Перебудовує пошуковий Ñ–Ð½Ð´ÐµÐºÑ Ñ–Ð· таблиць влаÑтивоÑтей з увімкненим типом даних [https://www.semantic-mediawiki.org/wiki/Full-text повнотекÑтового пошуку]. Зміни до правил індекÑÑƒÐ²Ð°Ð½Ð½Ñ (змінені шумові Ñлова, новий Ñтемер тощо) та/або заново додана або змінена Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ÑŒ запуÑку цього Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ‰Ðµ раз.",
+ "smw-admin-fulltext-active": "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð· перебудови повнотекÑтового пошуку заплановано.",
+ "smw-admin-fulltext-button": "Запланувати повнотекÑтову перебудову",
+ "smw-admin-support": "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÐºÐ¸",
+ "smw-admin-supportdocu": "Є різні реÑурÑи, Ñтворені, щоб допомогти Вам у разі Ð²Ð¸Ð½Ð¸ÐºÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼:",
+ "smw-admin-installfile": "Якщо у Ð²Ð°Ñ Ð²Ð¸Ð½Ð¸ÐºÐ»Ð¸ проблеми із вÑтановленнÑм, наÑамперед подивітьÑÑ Ð¿Ð¾Ñ€Ð°Ð´Ð¸ у <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">файлі INSTALL</a> та на <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">Ñторінці інÑталÑції</a>.",
+ "smw-admin-smwhomepage": "Повна довідка із кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾ÑŽ MediaWiki знаходитьÑÑ Ð½Ð° Ñайті <b><a href=\"https://www.semantic-mediawiki.org/wiki/%D0%A1%D0%B5%D0%BC%D0%B0%D0%BD%D1%82%D0%B8%D1%87%D0%BD%D0%B0_%D0%9C%D0%B5%D0%B4%D1%96%D0%B0%D0%B2%D1%96%D0%BA%D1%96\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилки відÑилайте через <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">багтрекер</a>; Ñторінка про <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð²Ñ–Ñ‚Ñ–Ð² про помилки</a> має деÑкі підказки щодо того, Ñк напиÑати уÑпішний звіт про помилку.",
+ "smw-admin-questions": "Якщо у Ð²Ð°Ñ Ñ” додаткові Ð·Ð°Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ‡Ð¸ побажаннÑ, приєднуйтеÑÑŒ до обговорень на <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">поштовій розÑилці Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів Семантичної MediaWiki</a>, або в <a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">чат-кімнаті</a>.",
+ "smw-admin-other-functions": "Інші функції",
+ "smw-admin-supplementary-section-title": "Додаткові функції",
+ "smw-admin-supplementary-section-subtitle": "ОÑновні функції",
+ "smw-admin-supplementary-section-intro": "У цьому розділі подано додаткові функції поза цілÑми підтримки, Ñ– може бути, що деÑкі з функцій, перелічених [https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions документації], будуть обмежені або недоÑтупні у цій вікі.",
+ "smw-admin-supplementary-settings-title": "Параметри конфігурації",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> виводить збірний ÑпиÑок доÑтупних параметрів, що викориÑтовуютьÑÑ Ñƒ Семантичній MediaWiki",
+ "smw-admin-supplementary-operational-statistics-title": "Операційна ÑтатиÑтика",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u> відображає розширений набір ÑтатиÑтичних даних",
+ "smw-admin-supplementary-idlookup-title": "Пошук Ñ– Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ ÑутноÑÑ‚Ñ–",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u> міÑтить функції Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ й Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð¾ÐºÑ€ÐµÐ¼Ð¸Ñ… ÑутноÑтей",
+ "smw-admin-supplementary-duplookup-title": "Дубльовані ÑутноÑÑ‚Ñ–",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</span> Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ»Ñ–ÐºÑƒ запиÑів, Ñкі клаÑифікуютьÑÑ Ñк такі, що міÑÑ‚ÑÑ‚ÑŒ дублікати у таблиці ÑутноÑтей",
+ "smw-admin-supplementary-duplookup-docu": "Ð¦Ñ Ñторінка перелічує запиÑи з [https://www.semantic-mediawiki.org/wiki/Help:Entity_table таблиці ÑутноÑтей], категоризовані Ñк дублікати. Дублікати запиÑів можуть з'ÑвлÑтиÑÑ Ð»Ð¸ÑˆÐµ в рідкіÑних випадках (або й узагалі ніколи), потенційно Ñпричинені передчаÑно завершеним процеÑом під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð·Ð¸ даних чи невдалої транзакції відкоту.",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "СтатиÑтика кешу",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> Показати кеш-дотичну ÑтатиÑтику",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Функції Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> показує Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° ÑтатиÑтику індекÑу",
+ "smw-admin-supplementary-elastic-docu": "Ð¦Ñ Ñторінка міÑтить інформацію про налаштуваннÑ, мапуваннÑ, здоров'Ñ, а також ÑтатиÑтику індекÑу, пов'Ñзані з клаÑтером Elasticsearch, Ñкий поєднаний із Семантичною MediaWiki та Ñ—Ñ— [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>].",
+ "smw-admin-supplementary-elastic-functions": "Підтримувані функції",
+ "smw-admin-supplementary-elastic-settings-title": "ÐалаштуваннÑ",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> викориÑтовуєтьÑÑ Elasticsearch Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑами Семантичної MediaWiki",
+ "smw-admin-supplementary-elastic-mappings-title": "МапуваннÑ",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ»Ñ–Ñ‡ÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑів Ñ– мапувань полів",
+ "smw-admin-supplementary-elastic-mappings-docu": "Ð¦Ñ Ñторінка міÑтить подробиці щодо мапувань полів, викориÑтовуваних з поточними індекÑами. ПідÑумок Ð¼Ð°Ð¿ÑƒÐ²Ð°Ð½Ð½Ñ Ñлід проÑлідковувати у зв'Ñзку з <code>index.mapping.total_fields.limit</code>, що зазначає макÑимальну кількіÑÑ‚ÑŒ полів у індекÑÑ–.",
+ "smw-admin-supplementary-elastic-mappings-summary": "ПідÑумок",
+ "smw-admin-supplementary-elastic-mappings-fields": "ÐœÐ°Ð¿ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð»Ñ–Ð²",
+ "smw-admin-supplementary-elastic-nodes-title": "Вузли",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u> показує ÑтатиÑтику вузлів",
+ "smw-admin-supplementary-elastic-indices-title": "ІндекÑи",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> надає загальний оглÑд доÑтупних індекÑів та їхню ÑтатиÑтику",
+ "smw-admin-supplementary-elastic-statistics-title": "СтатиÑтика",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> показує ÑтатиÑтику Ñ€Ñ–Ð²Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑів",
+ "smw-admin-supplementary-elastic-statistics-docu": "Ð¦Ñ Ñторінка дає загальне уÑÐ²Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ ÑтатиÑтику індекÑів Ð´Ð»Ñ Ñ€Ñ–Ð·Ð½Ð¸Ñ… операцій, що відбуваютьÑÑ Ð½Ð° рівні індекÑів; ÑтатиÑтика, що виводитьÑÑ, збираєтьÑÑ ÑˆÐ»Ñхом праймеріз та загальних агрегацій. [https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html Сторінка довідки] міÑтить детальний Ð¾Ð¿Ð¸Ñ Ð´Ð¾Ñтупної ÑтатиÑтики індекÑів.",
+ "smw-admin-supplementary-elastic-status-replication": "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð° реплікаціÑ: $1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "Інтервал оновленнÑ: $1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "Затримка завдань з відновленнÑ: $1 (приблизно)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "Затримка завдань із Ð²Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ (файл): $1 (приблизно)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "Реплікацію заблоковано: $1 (перебудова у процеÑÑ–)",
+ "smw-list-count": "СпиÑок міÑтить $1 {{PLURAL:$1|запиÑ|запиÑи|запиÑів}}.",
+ "smw-list-count-from-cache": "СпиÑок міÑтить $1 {{PLURAL:$1|запиÑ|запиÑи|запиÑів}}, Ñ– його було отримано з кешу (UTC: $2).",
+ "smw-property-label-uniqueness": "Ðазва «$1» збігаєтьÑÑ Ð¿Ñ€Ð¸Ð½Ð°Ð¹Ð¼Ð½Ñ– з одним іншим предÑтавленнÑм влаÑтивоÑÑ‚Ñ–. Будь лаÑка, проконÑультуйтеÑÑ Ð·Ñ– [https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness Ñторінкою довідки], щоб дізнатиÑÑŒ, Ñк вирішити цю проблему.",
+ "smw-property-label-similarity-title": "Звіт про подібніÑÑ‚ÑŒ назв влаÑтивоÑтей",
+ "smw-property-label-similarity-intro": "<u>$1</u> обчиÑлює подібноÑÑ‚Ñ– наÑвних назв влаÑтивоÑтей",
+ "smw-property-label-similarity-threshold": "Поріг:",
+ "smw-property-label-similarity-type": "Показувати ідентифікатор типу",
+ "smw-property-label-similarity-noresult": "Ð”Ð»Ñ Ð²Ð¸Ð±Ñ€Ð°Ð½Ð¸Ñ… опцій не знайдено жодних результатів.",
+ "smw-property-label-similarity-docu": "Порівнює Ñ– звітує про [https://www.semantic-mediawiki.org/wiki/Property_similarity ÑинтакÑичну подібніÑÑ‚ÑŒ] (не Ñемантичну подібніÑÑ‚ÑŒ) між двома назвами влаÑтивоÑтей, що може допомогти відфільтрувати влаÑтивоÑÑ‚Ñ–, Ñкі мають помилки у назві або Ñ” еквівалентними Ñ– репрезентують той же концепт (див. Ñпеціальну Ñторінку [[Special:Properties|ВлаÑтивоÑÑ‚Ñ–]], щоб проÑÑнити концепт Ñ– викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð»Ð°ÑтивоÑтей, отриманих у звіті). Поріг можна налаштувати, щоб або розширити, або звузити рівень подібноÑÑ‚Ñ–, Ñкий Ñлід брати до уваги. <code>[[Property:$1|$1]]</code> викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð²Ð¸ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑтей з аналізу.",
+ "smw-admin-operational-statistics": "Ð¦Ñ Ñторінка міÑтить операційну ÑтатиÑтику, зібрану в або з функцій, пов'Ñзаних із Семантичною MediaWiki. Розширений ÑпиÑок ÑтатиÑтичних даних, Ñпецифічних Ð´Ð»Ñ Ð²Ñ–ÐºÑ–, можна переглÑнути [[Special:Statistics|<b>тут</b>]].",
+ "smw_adminlinks_datastructure": "Структура даних",
+ "smw_adminlinks_displayingdata": "Ð’Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…",
+ "smw_adminlinks_inlinequerieshelp": "Довідка із вбудованих запитів",
+ "smw-page-indicator-usage-count": "Приблизний [https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count підрахунок викориÑтаннÑ]: {{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "ВлаÑтивіÑÑ‚ÑŒ, визначена {{PLURAL:$1|кориÑтувачем|ÑиÑтемою}}",
+ "smw-property-indicator-last-count-update": "Приблизна кількіÑÑ‚ÑŒ викориÑтань\nВоÑтаннє оновлено: $1",
+ "smw-concept-indicator-cache-update": "Кешована кількіÑÑ‚ÑŒ\nВоÑтаннє оновлено: $1",
+ "smw-createproperty-isproperty": "Це влаÑтивіÑÑ‚ÑŒ типу $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|1=Дозволене Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– Ñ”|Дозволені Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– Ñ”}}:",
+ "smw-paramdesc-category-delim": "Розділювач",
+ "smw-paramdesc-category-template": "Шаблон Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ»ÐµÐ¼ÐµÐ½Ñ‚Ñ–Ð² із",
+ "smw-paramdesc-category-userparam": "Параметр, Ñкий передаєтьÑÑ Ð² шаблон",
+ "smw-info-par-message": "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ.",
+ "smw-info-par-icon": "Значок, щоб показати \"info\" або \"warning\".",
+ "prefs-smw": "Семантична MediaWiki",
+ "prefs-general-options": "Загальні опції",
+ "prefs-ask-options": "Опції Special:Ask",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ Семантична Медіавікі] (та пов'Ñзані розширеннÑ), забезпечують можливіÑÑ‚ÑŒ індивідуальних налаштувань Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð½Ð¸Ñ… функцій. Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, будь лаÑка, переглÑньте цю [http://semantic-mediawiki.org/wiki/Help:User_preferences Ñторінку довідки].",
+ "smw-prefs-ask-options-tooltip-display": "Показувати текÑÑ‚ параметра Ñк інформаційну підказку",
+ "smw-prefs-ask-options-compact-view-basic": "Увімкнути базовий компактний режим переглÑду",
+ "smw-prefs-help-ask-options-compact-view-basic": "Якщо увімкнено, показує зменшений набір поÑилань у компактному режимі переглÑду Special:Ask.",
+ "smw-prefs-general-options-time-correction": "Увімкнути ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу Ð´Ð»Ñ Ñпеціальних Ñторінок за допомогою локального Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ [[Special:Preferences#mw-prefsection-rendering|чаÑового зміщеннÑ]]",
+ "smw-prefs-general-options-jobqueue-watchlist": "Показувати на моїй перÑональній панелі ÑпиÑок ÑпоÑÑ‚ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð° чергою завдань",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "Якщо увімкнено, показує [https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist ÑпиÑок] обраних завдань, що очікують Ñвоєї черги, разом із приблизними розмірами черги.",
+ "smw-prefs-general-options-disable-editpage-info": "Вимкнути вÑтупний текÑÑ‚ на Ñторінці редагуваннÑ",
+ "smw-prefs-general-options-disable-search-info": "Вимкнути довідкову інформацію про ÑинтакÑÐ¸Ñ Ð½Ð° Ñтандартній Ñторінці пошуку",
+ "smw-prefs-general-options-suggester-textinput": "Увімкнути підтримку вводу Ð´Ð»Ñ Ñемантичних ÑутноÑтей",
+ "smw-prefs-help-general-options-suggester-textinput": "Якщо увімкнено, дає змогу викориÑтовувати [https://www.semantic-mediawiki.org/wiki/Help:Input_assistance підтримку вводу] Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ влаÑтивоÑтей, концептів та категорій у контекÑÑ‚Ñ– введеного пошукового запиту.",
+ "smw-ui-tooltip-title-property": "ВлаÑтивіÑÑ‚ÑŒ",
+ "smw-ui-tooltip-title-quantity": "КонверÑÑ–Ñ Ð¾Ð´Ð¸Ð½Ð¸Ñ†ÑŒ",
+ "smw-ui-tooltip-title-info": "ІнформаціÑ",
+ "smw-ui-tooltip-title-service": "Службове поÑиланнÑ",
+ "smw-ui-tooltip-title-warning": "ПопередженнÑ",
+ "smw-ui-tooltip-title-error": "Помилка",
+ "smw-ui-tooltip-title-parameter": "Параметр",
+ "smw-ui-tooltip-title-event": "ПодіÑ",
+ "smw-ui-tooltip-title-note": "Примітка",
+ "smw-ui-tooltip-title-legend": "Легенда",
+ "smw-ui-tooltip-title-reference": "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° джерело",
+ "smw_unknowntype": "Тип \"$1\" Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— влаÑтивоÑÑ‚Ñ– некоректний",
+ "smw-concept-cache-text": "ПонÑÑ‚Ñ‚Ñ Ð¼Ð°Ñ” $1 {{PLURAL:$1|Ñторінку|Ñторінки|Ñторінок}}, Ñ– воÑтаннє було оновлено $3, $2.",
+ "smw_concept_header": "Сторінки, що належать до концепції «$1»",
+ "smw_conceptarticlecount": "{{PLURAL:$1|1=Показана|Показані}} нижче $1 {{PLURAL:$1|Ñторінка|Ñторінки|Ñторінок}}.",
+ "smw-qp-empty-data": "Запитані дані не можуть відображатиÑÑ Ñ‡ÐµÑ€ÐµÐ· деÑкі недоÑтатні критерії відбору.",
+ "right-smw-admin": "доÑтуп до завдань адмініÑÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (Ñемантична MediaWiki)",
+ "right-smw-patternedit": "доÑтуп до Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±ÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÐµÐ½Ð¸Ñ… регулÑрних виразів та шаблонів (Семантична MediaWiki)",
+ "right-smw-pageedit": "доÑтуп до редагувань Ñторінок з анотацією <code>Is edit protected</code> (Семантична MediaWiki)",
+ "right-smw-ruleedit": "Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñторінок правил (Семантична MediaWiki)",
+ "restriction-level-smw-pageedit": "захищено (лише прийнÑтні кориÑтувачі)",
+ "action-smw-patternedit": "Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÐ³ÑƒÐ»Ñрних виразів, що викориÑтовуютьÑÑ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾ÑŽ MediaWiki",
+ "action-smw-pageedit": "Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñторінок з анотацією <code>Is edit protected</code> (Семантична MediaWiki)",
+ "group-smwadministrator": "ÐдмініÑтратори (Семантична MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|адмініÑтратор (Семантична MediaWiki)|адмініÑтраторка (Семантична MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:ÐдмініÑтратори (Семантична MediaWiki)",
+ "group-smwcurator": "Куратори (Семантична MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|куратор (Семантична MediaWiki)|кураторка (Семантична MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:Куратори (Семантична MediaWiki)",
+ "action-smw-admin": "доÑтуп до завдань адмініÑÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñемантичної MediaWiki",
+ "action-smw-ruleedit": "редагувати Ñторінки правил (Семантична MediaWiki)",
+ "smw-property-predefined-default": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ.",
+ "smw-property-predefined-common": "Це влаÑтивіÑÑ‚ÑŒ Ñ” попередньо розгорнутою (також відома Ñк [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Ñпеціальна влаÑтивіÑÑ‚ÑŒ]) Ñ– неÑе додаткові адмініÑтративні привілеї, але може викориÑтовуватиÑÑ, Ñк будь-Ñка інша [https://www.semantic-mediawiki.org/wiki/Property визначена кориÑтувачем влаÑтивіÑÑ‚ÑŒ].",
+ "smw-property-predefined-ask": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка предÑтавлÑÑ” метаінформацію (у виглÑді [https://www.semantic-mediawiki.org/wiki/Subobject підоб'єкта]) про індивідуальні запити, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-asksi": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка збирає Ñ€Ñд умов, викориÑтаних у запиті, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-askde": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка інформує про глибину запиту, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-long-askde": "Це — чиÑлове значеннÑ, обчиÑлене на оÑнові Ð³Ð½Ñ–Ð·Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ–Ð´Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð², ланцюжків влаÑтивоÑтей, Ñ– доÑтупних елементів опиÑу із виконаннÑм запиту, що обмежуєтьÑÑ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ð¾Ð¼ конфігурації <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>.",
+ "smw-property-predefined-askpa": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що опиÑує параметри, Ñкі впливають на результат запиту, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-long-askpa": "Це чаÑтина колекції влаÑтивоÑтей, Ñкі визначають [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler профіль запиту].",
+ "smw-sp-properties-docu": "Ðа цій Ñторінці перелічено [https://www.semantic-mediawiki.org/wiki/Property влаÑтивоÑÑ‚Ñ–], Ñкі доÑтупні Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— вікі, та Ñ—Ñ… викориÑтаннÑ. Ð”Ð»Ñ Ð½Ð°Ð¹Ñвіжішої ÑтатиÑтики рекомендуєтьÑÑ, щоб Ñкрипт [https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics ÑтатиÑтики влаÑтивоÑÑ‚Ñ–] запуÑкавÑÑ Ñ€ÐµÐ³ÑƒÐ»Ñрно. Ð”Ð»Ñ Ð´Ð¸Ñ„ÐµÑ€ÐµÐ½Ñ†Ñ–Ð¹Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ переглÑду почитайте Ñпеціальну Ñторінку [[Special:UnusedProperties|невикориÑтані]] або [[Special:WantedProperties|бажані влаÑтивоÑÑ‚Ñ–]].",
+ "smw-sp-properties-cache-info": "Перераховані дані отримані з [https://www.semantic-mediawiki.org/wiki/Caching кеш], Ñ– воÑтаннє оновлені $1.",
+ "smw-sp-properties-header-label": "СпиÑок влаÑтивоÑтей",
+ "smw-admin-settings-docu": "Відображає ÑпиÑок вÑÑ–Ñ… Ñтандартних Ñ– локалізованих налаштувань, Ñкі мають ÑтоÑунок до Ñередовища Семантичної MediaWiki. За детальною інформацією про окремі параметри звернітьÑÑ Ð´Ð¾ Ñторінки довідки щодо [https://www.semantic-mediawiki.org/wiki/Help:Configuration налаштувань].",
+ "smw-sp-admin-settings-button": "Створити ÑпиÑок налаштувань",
+ "smw-admin-idlookup-title": "Пошук",
+ "smw-admin-idlookup-docu": "Цей розділ показує технічні подробиці про окрему ÑутніÑÑ‚ÑŒ (вікіÑторінку, підоб'єкт, влаÑтивіÑÑ‚ÑŒ тощо) в Семантичній MediaWiki. Введені дані можуть бути чиÑловим ідентифікатором або Ñ€Ñдковим значеннÑм, відповідно до Ð¿Ð¾Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ, проте будь-Ñке поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° ідентифікатор ÑтоÑуєтьÑÑ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾Ñ— MediaWiki, а не ідентифікатора верÑÑ–Ñ— чи Ñторінки в MediaWiki.",
+ "smw-admin-iddispose-title": "ЛіквідаціÑ",
+ "smw-admin-iddispose-docu": "Зверніть увагу, що Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð· ліквідації не обмежена Ñ– призведе до Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ‚Ð¾Ñ€Ð° внутрішнього об'єкта з Ñ€ÑƒÑˆÑ–Ñ Ð±Ð°Ð·Ð¸ даних піÑÐ»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ. Будь лаÑка, виконуйте цю дію '''обережно''' Ñ– лише піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾, Ñк ознайомитеÑÑ Ð· [https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal документацією].",
+ "smw-admin-iddispose-done": "ID «$1» вилучено з бекенду бази даних.",
+ "smw-admin-iddispose-references": "Ідентифікатор \"$1\" {{PLURAL:$2|не має активних поÑилань|має щонайменше одне активне поÑиланнÑ}}:",
+ "smw-admin-iddispose-references-multiple": "СпиÑок збігів із принаймні одним активним запиÑом поÑиланнÑ.",
+ "smw-admin-iddispose-no-references": "Пошук не зміг зіÑтавити \"$1\" з елементом таблиці.",
+ "smw-admin-idlookup-input": "Пошук:",
+ "smw-admin-objectid": "Ідентифікатор:",
+ "smw-admin-tab-general": "ОглÑд",
+ "smw-admin-tab-notices": "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ заÑтаріліÑÑ‚ÑŒ",
+ "smw-admin-tab-maintenance": "ОбÑлуговуваннÑ",
+ "smw-admin-tab-supplement": "Додаткові функції",
+ "smw-admin-tab-registry": "РеєÑÑ‚Ñ€",
+ "smw-admin-maintenance-no-description": "Ðемає опиÑу.",
+ "smw-admin-maintenance-script-section-title": "СпиÑок доÑтупних Ñкриптів обÑлуговуваннÑ",
+ "smw-admin-maintenance-script-section-intro": "Подані Ñкрипти обÑÐ»ÑƒÐ³Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°ÑŽÑ‚ÑŒ прав адмініÑтратора й доÑтупу до командного Ñ€Ñдка, щоб можна було виконувати перелічені Ñкрипти.",
+ "smw-admin-maintenance-script-description-dumprdf": "RDF-екÑпорт Ñ–Ñнуючих трійок.",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "Цей Ñкрипт викориÑтовуєтьÑÑ Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐµÑˆÐµÐ¼ концептів Ð´Ð»Ñ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾Ñ— вікі, де він може Ñтворювати, вилучити й оновлювати вибраний кеш.",
+ "smw-admin-maintenance-script-description-rebuilddata": "Відтворює вÑÑ– Ñемантичні дані в базі даних шлÑхом швидкого аналізу вÑÑ–Ñ… Ñторінок, Ñкі можуть мати Ñемантичні дані.",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "Відбудовує Ñ–Ð½Ð´ÐµÐºÑ Elasticsearch (Ð´Ð»Ñ Ñ–Ð½ÑталÑцій, Ñкі викориÑтовують <code>ElasticStore</code>) шлÑхом швидкого аналізу вÑÑ–Ñ… ÑутноÑтей, Ñкі мають Ñемантичні дані.",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "Відбудовує Ñ–Ð½Ð´ÐµÐºÑ Ð¿Ð¾Ð²Ð½Ð¾Ñ‚ÐµÐºÑтового пошуку <code>SQLStore</code> (Ð´Ð»Ñ Ñ–Ð½ÑталÑцій, у Ñких це Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾).",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "Відбудовує ÑтатиÑтику викориÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ… ÑутноÑтей влаÑтивоÑтей.",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "Вилучає дублікати ÑутноÑтей, виÑвлені у вибраних таблицÑÑ…, Ñкі не мають активних поÑилань на джерела.",
+ "smw-admin-maintenance-script-description-setupstore": "Ð’Ñтановлює бекенд Ñховища, вибраний у <code>LocalSettings.php</code>.",
+ "smw-admin-maintenance-script-description-updateentitycollation": "Оновлює поле <code>smw_sort</code> у <code>SQLStore</code> (відповідно до Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation]).",
+ "smw-admin-maintenance-script-description-populatehashfield": "Заповнює поле <code>smw_hash</code> Ð´Ð»Ñ Ñ€Ñдків, Ñкі не мають відповідного значеннÑ.",
+ "smw-livepreview-loading": "ЗавантаженнÑ…",
+ "smw-sp-searchbyproperty-description": "Ðа цій Ñторінці розміщено проÑтий [https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду] Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ ÑутноÑтей, опиÑаних влаÑтивіÑÑ‚ÑŽ й іменним значеннÑм. До інших доÑтупних пошукових інтерфейÑів належать [[Special:PageProperty|пошук влаÑтивоÑтей Ñторінки]] та [[Special:Ask|майÑтер запитів ask]].",
+ "smw-sp-searchbyproperty-resultlist-header": "СпиÑок результатів",
+ "smw-sp-searchbyproperty-nonvaluequery": "СпиÑок значень, Ñким призначена влаÑтивіÑÑ‚ÑŒ «$1».",
+ "smw-sp-searchbyproperty-valuequery": "СпиÑок Ñторінок, що мають влаÑтивіÑÑ‚ÑŒ «$1» із анотованим значеннÑм «$2».",
+ "smw-datavalue-number-textnotallowed": "«$1» не може бути приÑвоєно заÑвленому типу чиÑла типу зі значеннÑм $2.",
+ "smw-datavalue-number-nullnotallowed": "«$1» повернув Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«NULL», Ñке не дозволене в ÑкоÑÑ‚Ñ– чиÑла.",
+ "smw-editpage-annotation-enabled": "Ð¦Ñ Ñторінка підтримує Ñемантичні анотації в текÑÑ‚Ñ– (e.g. <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) Ð´Ð»Ñ Ð¿Ð¾Ð±ÑƒÐ´Ð¾Ð²Ð¸ Ñтруктурованого контенту, у Ñкий можна робити запити, що забезпечуєтьÑÑ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾ÑŽ Semantic MediaWiki. Ð”Ð»Ñ ÐºÐ¾Ð¼Ð¿Ð»ÐµÐºÑного опиÑу, Ñк викориÑтовувати анотації або парÑерну функцію ask, будь лаÑка, відвідайте Ñторінки [https://www.semantic-mediawiki.org/wiki/Help:Getting_started початку роботи], [https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation анотації в текÑÑ‚Ñ–] та довідкову Ñторінку [https://www.semantic-mediawiki.org/wiki/Help:Inline_queries Ñ€Ñдкових запитів].",
+ "smw-editpage-annotation-disabled": "Ðа цій Ñторінці не можливі Ñемантичні анотації в текÑÑ‚Ñ– через Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñтору назв. Деталі щодо того, Ñк дозволити проÑÑ‚Ñ–Ñ€ назв, можна знайти на довідковій Ñторінці [https://www.semantic-mediawiki.org/wiki/Help:Configuration конфігурації].",
+ "smw-editpage-property-annotation-enabled": "Цю влаÑтивіÑÑ‚ÑŒ можна розширити з допомогою Ñемантичних анотацій Ð´Ð»Ñ Ð²ÐºÐ°Ð·Ð°Ð½Ð½Ñ Ñ‚Ð¸Ð¿Ñƒ даних (e.g. <nowiki>\"[[Has type::Page]]\"</nowiki>) або інших підтримуваних тверджень (e.g. <nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>). ОпиÑ, Ñк розширити цю Ñторінки, див на Ñторінці довідки про [https://www.semantic-mediawiki.org/wiki/Help:Property_declaration заÑÐ²Ð»ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–] або [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes ÑпиÑок доÑтупних типів даних].",
+ "smw-editpage-property-annotation-disabled": "Цю влаÑтивіÑÑ‚ÑŒ не можна розширити анотацією типу даних (e.g. <nowiki>\"[[Has type::Page]]\"</nowiki>), оÑкільки вона уже попередньо визначена (див. детальнішу інформацію на довідковій Ñторінці про [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Ñпеціальні влаÑтивоÑÑ‚Ñ–]).",
+ "smw-editpage-concept-annotation-enabled": "Цей концепт можна розширити з викориÑтаннÑм парÑерної функції #concept. ОпиÑ, Ñк викориÑтовувати #concept, див. на довідковій Ñторінці про [https://www.semantic-mediawiki.org/wiki/Help:Concepts концепт].",
+ "smw-search-syntax-support": "Пошукові запити підтримують [https://www.semantic-mediawiki.org/wiki/Help:Semantic_search ÑинтакÑÐ¸Ñ Ñемантичних запитів] Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ результатів з викориÑтаннÑм Семантичної MediaWiki.",
+ "smw-search-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance ÐÑиÑтента вводу] також увімкнено Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ³ÑˆÐµÐ½Ð½Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ вибору доÑтупних влаÑтивоÑтей Ñ– категорій.",
+ "smw-search-help-intro": "Ð’Ð²ÐµÐ´ÐµÐ½Ð½Ñ <code><nowiki>[[ ... ]]</nowiki></code> повідомить процеÑорові вводу, що він має ÑкориÑтатиÑÑ Ð±ÐµÐºÐµÐ½Ð´Ð¾Ð¼ пошуку Семантичної MediaWiki. Слід зауважити, що Ð¿Ð¾Ñ”Ð´Ð½Ð°Ð½Ð½Ñ <code><nowiki>[[ ... ]]</nowiki></code> з неÑтруктурованим текÑтовим пошуком, таке Ñк <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code> не підтримуєтьÑÑ.",
+ "smw-search-help-structured": "Структурований пошук:\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>, <code><nowiki>[[Has number::123]]</nowiki></code> (Ñк [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context фільтрований контекÑÑ‚])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code> (із [https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context контекÑтом запиту])",
+ "smw-search-help-proximity": "Приблизний пошук (коли влаÑтивіÑÑ‚ÑŒ невідома, доÑтупно '''лише''' Ð´Ð»Ñ Ñ‚Ð¸Ñ… бекендів, Ñкі забезпечують інтеграцію повнотекÑтового пошуку):\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code> (пошук «lorem» та «ipsum» у вÑÑ–Ñ… індекÑованих документах)\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code> (пошук збігів «lorem ipsum» Ñк ціліÑної фрази)",
+ "smw-search-help-ask": "Подані поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð¿Ð¾ÑÑнÑÑ‚ÑŒ, Ñк кориÑтуватиÑÑ ÑинтакÑиÑом <code>#ask</code>.\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages Вибір Ñторінок] — опиÑує, Ñк вибирати Ñторінки й будувати умови\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators Пошукові оператори] — перелічує доÑтупні пошукові оператори, включно з тими, що викориÑтовуютьÑÑ Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ в діапазоні, а також запитами з викориÑтаннÑм пошукових шаблонів (wildcards)",
+ "smw-search-input": "Ð’Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ñ‚ÐµÐºÑту й пошук",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance Підтримка вводу] надаєтьÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð»Ñ Ð²Ð²Ð¾Ð´Ñƒ Ñ– вимагає викориÑÑ‚Ð°Ð½Ð½Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ з таких префікÑів:\n\n*<code>p:</code> Ð´Ð»Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ð¹ влаÑтивоÑтей (напр., <code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> Ð´Ð»Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ð¹ категорій\n\n*<code>con:</code> Ð´Ð»Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ð¹ концептів",
+ "smw-search-syntax": "СинтакÑиÑ",
+ "smw-search-profile": "Додатково",
+ "smw-search-profile-tooltip": "Функції пошуку у зв'Ñзку із Семантичною MediaWiki",
+ "smw-search-profile-sort-best": "Ðайкращий збіг",
+ "smw-search-profile-sort-recent": "Ðайновіші",
+ "smw-search-profile-sort-title": "Ðазва",
+ "smw-search-profile-extended-help-intro": "[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile Розширений профііль] Ñпеціальної Ñторінки Special:Search надає доÑтуп до пошукових функцій, Ñпецифчних Ð´Ð»Ñ Ð¡ÐµÐ¼Ð°Ð½Ñ‚Ð¸Ñ‡Ð½Ð¾Ñ— MediaWiki та Ñ—Ñ— підтримуваного бекенду запитів.",
+ "smw-search-profile-extended-help-sort": "Ð’Ñтановлює Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ результатів:",
+ "smw-search-profile-extended-help-sort-title": "* «Ðазва» викориÑтовуватиме назву Ñторінки (чи назву, Ñку викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ) Ñк критерій ÑортуваннÑ",
+ "smw-search-profile-extended-help-sort-recent": "* «Ðайновіші» покаже Ñпершу ÑутноÑÑ‚Ñ–, Ñкі недавно хтоÑÑŒ змінював (підоб'єктні ÑутноÑÑ‚Ñ– буде приховано, оÑкільки вони не анотуютьÑÑ Ð·Ð° допомогою [[Property:Modification date|дати редагуваннÑ]])",
+ "smw-search-profile-extended-help-sort-best": "* «Ðайкращий збіг» Ñортуватиме ÑутноÑÑ‚Ñ– за [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy відповідніÑÑ‚ÑŽ] на оÑнові оцінок, Ñкі надає бекенд",
+ "log-name-smw": "Журнал Семантичної MediaWiki",
+ "log-show-hide-smw": "$1 журнал Семантичної MediaWiki",
+ "log-description-smw": "ДіÑльніÑÑ‚ÑŒ щодо [https://www.semantic-mediawiki.org/wiki/Help:Logging увімкнених типів подій], Ñку реєÑтрує Семантична MediaWiki та Ñ—Ñ— компоненти.",
+ "logentry-smw-maintenance": "Події, пов'Ñзані з підтримкою, що відбулиÑÑ Ñƒ Семантичній MediaWiki",
+ "smw-datavalue-import-unknown-namespace": "ПроÑÑ‚Ñ–Ñ€ назв імпорту «$1» невідомий. Будь лаÑка, впевнітьÑÑ, що деталі імпорту OWL доÑтупні через [[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ URI проÑтору назв «$1» в [[MediaWiki:Smw import $1|імпорті $1]].",
+ "smw-datavalue-import-missing-type": "Ðе було знайдено Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð¸Ð¿Ñƒ Ð´Ð»Ñ Â«$1» в [[MediaWiki:Smw import $2|імпорті $2]].",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|Імпорт $1]]",
+ "smw-datavalue-import-invalid-value": "«$1» не Ñ” дійÑним форматом Ñ– має ÑкладатиÑÑ Ð· \"проÑÑ‚Ñ–Ñ€ назв\":\"ідентифікатор\" (e.g. \"foaf:name\").",
+ "smw-datavalue-import-invalid-format": "ОчікувалоÑÑ, що Ñ€Ñдок «$1» мав бути поділеним на чотири чаÑтини, але формат виÑвивÑÑ Ð½ÐµÐ·Ñ€Ð¾Ð·ÑƒÐ¼Ñ–Ð»Ð¸Ð¼.",
+ "smw-property-predefined-impo": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка опиÑує зв'Ñзок з [https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary імпортованим Ñловником], Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-type": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка опиÑує [[Special:Types|тип даних]] влаÑтивоÑÑ‚Ñ–, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-sobj": "«$1» — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка репрезентує побудову [https://www.semantic-mediawiki.org/wiki/Help:Container контейнера], що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-long-sobj": "Контейнер дозволÑÑ” акумулювати Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивіÑÑ‚ÑŒ-Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾Ð´Ñ–Ð±Ð½Ð¾ до нормальної вікіÑторінки, але в межах іншого проÑтору влаÑтивоÑÑ‚Ñ–, будучи пов'Ñзаним з вбудовуваним об'єктом.",
+ "smw-property-predefined-errp": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ що відÑлідковує помилки вводу Ð´Ð»Ñ Ð°Ð½Ð¾Ñ‚Ð°Ñ†Ñ–Ð¹ нерегулÑрних значень Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value «$1»] — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка може визначати ÑпиÑок дозволених значень, щоб обмежувати Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ Ð´Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list «$1»] — попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка може зазначати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° ÑпиÑок, Ñкий міÑтить допуÑтимі Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ значень Ð´Ð»Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ–, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-datavalue-property-create-restriction": "ВлаÑтивіÑÑ‚ÑŒ «$1» не Ñ–Ñнує, а кориÑтувач не має права «$2» (див. [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode режим авторитету]) Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‡Ð¸ Ð°Ð½Ð¾Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ із незатвердженою влаÑтивіÑÑ‚ÑŽ.",
+ "smw-datavalue-property-invalid-character": "«$1» міÑтить Ñимвол «$2» Ñк чаÑтину назви влаÑтивоÑÑ‚Ñ–, Ñ– тому клаÑифікована Ñк недійÑна.",
+ "smw-datavalue-property-invalid-chain": "ВикориÑÑ‚Ð°Ð½Ð½Ñ Â«$1» Ñк ланцюжка влаÑтивоÑтей протÑгом процеÑу анотації заборонене.",
+ "smw-datavalue-restricted-use": "Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… «$1» було позначено Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾Ð³Ð¾ викориÑтаннÑ.",
+ "smw-datavalue-invalid-number": "«$1» не може бути інтерпретовано Ñк чиÑло.",
+ "smw-query-condition-circular": "Можливу циклічну умову було виÑвлено у «$1».",
+ "smw-types-list": "СпиÑок типів даних",
+ "smw-types-default": "«$1» є вбудованим типом даних.",
+ "smw-types-help": "Подальшу інформацію Ñ– приклади можна знайти на цій [https://www.semantic-mediawiki.org/wiki/Help:Type_$1 Ñторінці довідки].",
+ "smw-type-anu": "«$1» — це варіант типу даних [[Special:Types/URL|URL]] Ñ– здебільшого викориÑтовуєтьÑÑ Ð´Ð»Ñ ÐµÐºÑпортної декларації ''owl:AnnotationProperty''.",
+ "smw-type-boo": "«$1» — це примітивний тип даних Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ–Ñтина/хиба.",
+ "smw-type-cod": "«$1» — це варіант типу даних [[Special:Types/Text|ТекÑÑ‚]] Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñƒ технічних текÑтах довільної довжини, таких Ñк ÑпиÑки вихідного коду.",
+ "smw-type-geo": "«$1» — це тип даних, Ñкий опиÑує географічне Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ñ– вимагає [https://www.semantic-mediawiki.org/wiki/Extension:Maps Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Â«ÐšÐ°Ñ€Ñ‚Ð¸Â»].",
+ "smw-type-tel": "«$1» — це Ñпеціальний тип даних Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу міжнародних телефонних номерів згідно з RFC 3966.",
+ "smw-type-txt": "«$1» — це примітивний тип даних Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу Ñ€Ñдків довільної довжини.",
+ "smw-type-dat": "«$1» — це тип даних Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ñ–Ð² чаÑу в єдиному форматі.",
+ "smw-property-predefined-errc": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] Ñ– репрезентує помилки, Ñкі виникли у зв'Ñзку з неправильними анотаціÑми значень чи обробкою вводу.",
+ "smw-property-predefined-long-errc": "Помилки накопичуютьÑÑ Ð² [https://www.semantic-mediawiki.org/wiki/Help:Container контейнері], Ñкий також може включати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° влаÑтивіÑÑ‚ÑŒ, Ñка й Ñпричинила невідповідніÑÑ‚ÑŒ.",
+ "smw-property-predefined-errt": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що міÑтить текÑтовий Ð¾Ð¿Ð¸Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¸ Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-subobject-parser-invalid-naming-scheme": "Визначений кориÑтувачем підоб'єкт міÑтив недійÑну Ñхему йменуваннÑ. Крапка ($1) у перших п'Ñти Ñимволах призначена Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ»ÑŽÑ‡Ð½Ð¾ розширеннÑми. Ви можете задати [https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier іменований ідентифікатор].",
+ "smw-datavalue-record-invalid-property-declaration": "Ð’Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñу міÑтить влаÑтивіÑÑ‚ÑŒ «$1», Ñка Ñама по Ñобі оголошена типом запиÑу, Ñ– це не допуÑкаєтьÑÑ.",
+ "smw-property-predefined-mdat": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що відповідає даті оÑтанньої зміни об'єкта, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-cdat": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що відповідає даті першої верÑÑ–Ñ— Ñуб'єкта, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-newp": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñка вказує, чи Ñ” Ñуб'єкт новим чи ні, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-ledt": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що міÑтить назву Ñторінки того кориÑтувача, Ñкий Ñтворив першу верÑÑ–ÑŽ, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-mime": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що опиÑує MIME-тип завантаженого файлу Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-media": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що опиÑує медіатип завантаженого медіафайлу, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-askfo": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що міÑтить назву кінцевого формату, викориÑтаного в запиті, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-askst": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що опиÑує умови запиту у формі Ñ€Ñдка, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-askdu": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що міÑтить Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу (в Ñекундах), Ñкий був викориÑтаний на Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-asksc": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] та ідентифікує альтернативні (напр., віддалені, федеративні) джерела запитів.",
+ "smw-property-predefined-askco": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] Ð´Ð»Ñ Ð¾Ð¿Ð¸Ñу Ñтану запиту чи його компонентів.",
+ "smw-property-predefined-long-askco": "Ðомер чи номери, призначені Ð´Ð»Ñ Ñ€ÐµÐ¿Ñ€ÐµÐ·ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ— внутрішнього кодифікованого Ñтану, Ñкий поÑÑнений на [https://www.semantic-mediawiki.org/wiki/Help:Query_profiler Ñторінці довідки].",
+ "smw-property-predefined-prec": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що опиÑує [https://www.semantic-mediawiki.org/wiki/Help:Display_precision точніÑÑ‚ÑŒ відображеннÑ] (у деÑÑткових цифрах) Ð´Ð»Ñ Ñ‡Ð¸Ñлових типів даних.",
+ "smw-types-extra-geo-not-available": "[https://www.semantic-mediawiki.org/wiki/Extension:Maps Ð Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Â«ÐšÐ°Ñ€Ñ‚Ð¸Â»] не виÑвлене, тому влаÑтивіÑÑ‚ÑŒ «$1» обмежена у Ñвоїй здатноÑÑ‚Ñ– працювати.",
+ "smw-datavalue-monolingual-dataitem-missing": "ВідÑутній очікуваний елемент Ð´Ð»Ñ Ð¿Ð¾Ð±ÑƒÐ´Ð¾Ð²Ð¸ одномовного значеннÑ.",
+ "smw-datavalue-languagecode-missing": "Ð”Ð»Ñ Ð°Ð½Ð¾Ñ‚Ð°Ñ†Ñ–Ñ— «$1» парÑер не зміг визначити коду мови (тобто «foo@en»).",
+ "smw-datavalue-languagecode-invalid": "«$1» не було розпізнано Ñк підтримуваний код мови.",
+ "smw-property-predefined-lcode": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що репрезентує BCP47-форматований код мови, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-type-mlt-rec": "«$1» — це [https://www.semantic-mediawiki.org/wiki/Help:Container контейнерний] тип даних, що пов'Ñзує текÑтове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ–Ð· конкретним [[Property:Language code|кодом мови]].",
+ "smw-types-extra-mlt-lcode": "Цей тип даних {{PLURAL:$2|вимагає|не вимагає}} коду мови (тобто {{PLURAL:$2|Ð°Ð½Ð¾Ñ‚Ð°Ñ†Ñ–Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð±ÐµÐ· коду мови не приймаєтьÑÑ|Ð°Ð½Ð¾Ñ‚Ð°Ñ†Ñ–Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð±ÐµÐ· коду мови приймаєтьÑÑ}}).",
+ "smw-property-predefined-text": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що репрезентує текÑÑ‚ допоміжної довжини, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-pdesc": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що дає змогу опиÑати влаÑтивіÑÑ‚ÑŒ у контекÑÑ‚Ñ– мови, Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-list": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ Ð´Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÑпиÑку влаÑтивоÑтей, викориÑтаних із влаÑтивіÑÑ‚ÑŽ типу [[Special:Types/Record|record]], Ñ– забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-limitreport-intext-parsertime": "[SMW] Ð§Ð°Ñ Ñ€Ð¾Ð·Ð±Ð¾Ñ€Ñƒ внутрішньотекÑтової анотації",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунди|Ñекунд}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунди|Ñекунд}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW] Ð§Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ store-бази даних (при оновленні Ñторінки)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|Ñекунда|Ñекунди|Ñекунд}}",
+ "smw_allows_pattern": "Ð¦Ñ Ñторінка повинна міÑтити ÑпиÑок поÑилань (піÑÐ»Ñ Ñкого йдуть [https://uk.wikipedia.org/wiki/РегулÑрний_вираз регулÑрні вирази]), Ñ– доÑтуп до неї має бути відкритий за допомогою влаÑтивоÑÑ‚Ñ– «[[Property:Allows pattern|Allows pattern]]». Щоб редагувати цю Ñторінку, потрібне право <code>smw-patternedit</code>.",
+ "smw-datavalue-allows-pattern-mismatch": "РегулÑрний вираз «$2» клаÑифікував Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» Ñк неприпуÑтиме.",
+ "smw-datavalue-allows-pattern-reference-unknown": "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° шаблон «$1» не вдалоÑÑ Ð¿Ð¾Ñ€Ñ–Ð²Ð½Ñти із запиÑом на [[MediaWiki:Smw allows pattern]].",
+ "smw-datavalue-allows-value-list-unknown": "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° ÑпиÑок «$1» не збігалоÑÑŒ зі Ñторінкою [[MediaWiki:Smw allows list $1]].",
+ "smw-datavalue-allows-value-list-missing-marker": "Контентові ÑпиÑку «$1» бракує елементів з маркером ÑпиÑку «*».",
+ "smw-datavalue-feature-not-supported": "Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Â«$1» не підтримуєтьÑÑ Ð°Ð±Ð¾ була вимкнена в цій вікі.",
+ "smw-property-predefined-pvap": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñкою можна вказати [[MediaWiki:Smw allows pattern|поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° шаблон]], аби заÑтоÑувати зіÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ [https://en.wikipedia.org/wiki/Regular_expression регулÑрних виразів], Ñ– Ñка забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-dtitle": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, Ñкою Ð´Ð»Ñ ÑутноÑÑ‚Ñ– можна призначити окрему назву Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ, Ñ– Ñка забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-pvuc": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] Ð´Ð»Ñ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ значень Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ примірника таким чином, щоб вони були унікальними (або щонайбільше єдиним)",
+ "smw-property-predefined-long-pvuc": "УнікальніÑÑ‚ÑŒ вÑтановлюєтьÑÑ, коли два Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ” неоднаковими у Ñвоїй Ñимвольній репрезентації, Ñ– будь-Ñке Ð¿Ð¾Ñ€ÑƒÑˆÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ кваліфіковано Ñк помилку.",
+ "smw-datavalue-uniqueness-constraint-error": "ВлаÑтивіÑÑ‚ÑŒ «$1» дозволÑÑ” лише унікальні приÑÐ²Ð¾Ñ”Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ, а ''$2'' вже було анотовано в темі «$3».",
+ "smw-datavalue-uniqueness-constraint-isknown": "ВлаÑтивіÑÑ‚ÑŒ «$1» дозволÑÑ” лише унікальні анотації значень, ''$2'' вже було закріплено за «$3».",
+ "smw-property-predefined-boo": "«$1» — [[Special:Types/Boolean|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð»Ð¾Ð³Ñ–Ñ‡Ð½Ð¸Ñ… значень.",
+ "smw-property-predefined-num": "«$1» — [[Special:Types/Number|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ñ‡Ð¸Ñлових значень.",
+ "smw-property-predefined-dat": "«$1» — [[Special:Types/Date|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ дат.",
+ "smw-property-predefined-uri": "«$1» — [[Special:Types/URL|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½ÑŒ URI/URL.",
+ "smw-property-predefined-qty": "«$1» — [[Special:Types/Quantity|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÑ–Ð»ÑŒÐºÑ–Ñних значень.",
+ "smw-datavalue-time-invalid-offset-zone-usage": "«$1» міÑтить ідентифікатори Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ‚Ð° зони, що не підтримуютьÑÑ.",
+ "smw-datavalue-time-invalid-values": "Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» міÑтить інформацію, непридатну до інтерпретації у формі «$2».",
+ "smw-datavalue-time-invalid-date-components-common": "«$1» міÑтить деÑку інформацію, непридатну до інтерпретації.",
+ "smw-datavalue-time-invalid-date-components-dash": "«$1» міÑтить невлаÑтиве тире або інші Ñимволи, непридатні до інтерпретації дати.",
+ "smw-datavalue-time-invalid-date-components-empty": "«$1» міÑтить деÑкі порожні компоненти.",
+ "smw-datavalue-time-invalid-date-components-three": "«$1» міÑтить понад три компоненти, необхідні Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚Ð°Ñ†Ñ–Ñ— дати.",
+ "smw-datavalue-time-invalid-date-components-sequence": "«$1» міÑтить поÑлідовніÑÑ‚ÑŒ, Ñку не можна інтерпретувати згідно з матрицею Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚Ñ–Ð² дати.",
+ "smw-datavalue-time-invalid-ampm": "«$1» міÑтить «$2» Ñк елемент години, непридатний Ð´Ð»Ñ 12-годинного формату чаÑу.",
+ "smw-datavalue-time-invalid-jd": "Ðе вдалоÑÑ Ñ–Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚ÑƒÐ²Ð°Ñ‚Ð¸ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð²Ð¾Ð´Ñƒ «$1» Ñк дійÑне чиÑло (день за ЮліанÑьким календарем). Було видано «$2».",
+ "smw-datavalue-time-invalid-prehistoric": "Ðе вдалоÑÑ Ñ–Ð½Ñ‚ÐµÑ€Ð¿Ñ€ÐµÑ‚ÑƒÐ²Ð°Ñ‚Ð¸ доіÑторичне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð²Ð¾Ð´Ñƒ «$1». Ðаприклад, Ñкщо вказати більше інформації, ніж роки чи календарну модель, це може вивеÑти неочікувані результати у доіÑторичному контекÑÑ‚Ñ–.",
+ "smw-datavalue-time-invalid": "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð¿Ñ–Ð·Ð½Ð°Ñ‚Ð¸ вхідне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» Ñк дійÑну дату чи компонент чаÑу. Ðа виході було отримано «$2».",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "URI форматувальника не міÑтить плейÑхолдера «$1».",
+ "smw-datavalue-external-formatter-invalid-uri": "«$1» — неправильна URL-адреÑа.",
+ "smw-datavalue-external-identifier-formatter-missing": "ВлаÑтивіÑÑ‚ÑŒ відÑÑƒÑ‚Ð½Ñ Ð² призначенні [[Property:External formatter uri|«URI зовнішнього форматувальника»]].",
+ "smw-property-predefined-eid": "«$1» — [[Special:Types/External identifier|тип]] Ñ– наперед визначена влаÑтивіÑÑ‚ÑŒ, Ñку надає [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантична MediaWiki] Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ–Ñ… ідентифікаторів.",
+ "smw-property-predefined-peid": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що зазначає зовнішній ідентифікатор Ñ– надаєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki].",
+ "smw-property-predefined-pefu": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] Ð´Ð»Ñ Ð²ÐºÐ°Ð·ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ реÑурÑу за допомогою заповнювача.",
+ "smw-property-predefined-long-pefu": "URI має міÑтити заповнювач, Ñкий буде налаштовано за допомогою Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ [[Special:Types/External identifier|зовнішнього ідентифікатора]], щоб Ñформувати валідне поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° реÑурÑ.",
+ "smw-type-eid": "«$1» — це варіант типу даних [[Special:Types/Text|ТекÑÑ‚]], що вимагає призначених влаÑтивоÑтей Ð´Ð»Ñ Ð¾Ð³Ð¾Ð»Ð¾ÑˆÐµÐ½Ð½Ñ [[Property:External formatter uri|URI зовнішнього форматувальника]].",
+ "smw-datavalue-stripmarker-parse-error": "Задане Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» міÑтить маркери [https://en.wikipedia.org/wiki/Help:Strip_markers «strip markers»], а тому його не можна парÑити належним чином.",
+ "smw-datavalue-parse-error": "Задане Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» було незрозумілим.",
+ "smw-datavalue-propertylist-invalid-property-key": "СпиÑок влаÑтивоÑтей «$1» міÑтив недійÑний ключ влаÑтивоÑÑ‚Ñ– «$2».",
+ "smw-datavalue-type-invalid-typeuri": "Тип «$1» не вдалоÑÑ Ñ‚Ñ€Ð°Ð½Ñформувати у дійÑну репрезентацію URI.",
+ "smw-datavalue-wikipage-missing-fragment-context": "Вхідне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» вікіÑторінки не можна викориÑтовувати без контекÑтної Ñторінки.",
+ "smw-datavalue-wikipage-invalid-title": "Вхідне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$1» типу Ñторінки міÑтить недійÑні Ñимволи або Ñ” неповним, а тому воно може призвеÑти до неÑподіваних результатів під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ чи процеÑу анотації.",
+ "smw-datavalue-wikipage-property-invalid-title": "ВлаÑтивіÑÑ‚ÑŒ «$1» (Ñк тип Ñторінки) із введеним значеннÑм «$2» міÑтить недійÑні Ñимволи, або ж Ñ” неповною, а тому може Ñпричинити непередбачені результати в процеÑÑ– Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ чи анотації.",
+ "smw-datavalue-wikipage-empty": "Вхідне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ñ–ÐºÑ–Ñторінки порожнє (напр., <code>[[SomeProperty::]], [[]]</code>), а тому його не можна викориÑтати Ñк назву чи Ñк чаÑтину умови запиту.",
+ "smw-type-ref-rec": "«$1» Ñ” [https://www.semantic-mediawiki.org/wiki/Container контейнерним] типом, що дозволÑÑ” запиÑувати додаткову інформацію (напр., дані про першоджерело) про вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ.",
+ "smw-datavalue-reference-outputformat": "$1: $2",
+ "smw-datavalue-reference-invalid-fields-definition": "Тип [[Special:Types/Reference|поÑиланнÑ]] передбачає, що має бути визначений ÑпиÑок влаÑтивоÑтей з викориÑтаннÑм влаÑтивоÑÑ‚Ñ– [https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields «Has fields»].",
+ "smw-parser-invalid-json-format": "JSON-парÑер вивів помилку «$1».",
+ "smw-property-preferred-title-format": "$1 ($2)",
+ "smw-property-preferred-label-language-combination-exists": "«$1» не можна викориÑтати Ñк бажану назву, оÑкільки мова «$2» вже закріплена за назвою «$3».",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "Скопіювати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð² буфер обміну",
+ "smw-property-userdefined-fixedtable": "«$1» було налаштовано Ñк [https://www.semantic-mediawiki.org/wiki/Fixed_properties фікÑовану влаÑтивіÑÑ‚ÑŒ] Ñ– будь-Ñка Ð¼Ð¾Ð´Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ñ—Ñ— [https://www.semantic-mediawiki.org/wiki/Type_declaration декларації типу] вимагає, щоб або було запущено <code>setupStore.php</code>, або завершено Ñпеціальне Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ [[Special:SemanticMediaWiki|«ІнÑталÑÑ†Ñ–Ñ Ñ‚Ð° Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð·Ð¸ даних»]].",
+ "smw-data-lookup": "ÐžÑ‚Ñ€Ð¸Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…...",
+ "smw-no-data-available": "Ðемає доÑтупних даних.",
+ "smw-property-req-violation-missing-fields": "ВлаÑтивоÑÑ‚Ñ– «$1» бракує даних декларації Ð´Ð»Ñ Ð°Ð½Ð¾Ñ‚Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ типу «$2», оÑкільки не вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ влаÑтивіÑÑ‚ÑŒ <code>Has fields</code>.",
+ "smw-property-req-violation-missing-formatter-uri": "ВлаÑтивоÑÑ‚Ñ– «$1» бракує даних декларації Ð´Ð»Ñ Ð°Ð½Ð¾Ñ‚Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ типу, оÑкільки не вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ влаÑтивіÑÑ‚ÑŒ <code>External formatter URI</code>.",
+ "smw-property-req-violation-predefined-type": "ВлаÑтивіÑÑ‚ÑŒ «$1», Ñк попередньо визначена влаÑтивіÑÑ‚ÑŒ, міÑтить декларацію типу «$2», Ñка Ñ” неÑуміÑною зі Ñтандартним типом цієї влаÑтивоÑÑ‚Ñ–.",
+ "smw-property-req-violation-import-type": "ВиÑвлено декларацію типу, неÑуміÑну з попередньо визначеним типом імпортованого Ñловника «$1». Загалом, непотрібно декларувати тип, оÑкільки Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼ÑƒÑ”Ñ‚ÑŒÑÑ Ð· Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ.",
+ "smw-property-req-violation-change-propagation-locked-error": "ВлаÑтивіÑÑ‚ÑŒ «$1» змінено й вона потребує повторного Ð¾Ñ†Ñ–Ð½ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ… ÑутноÑтей за допомогою процеÑу [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ]. Сторінку влаÑтивоÑÑ‚Ñ– буде замкнуто, доки не завершитьÑÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ñновної Ñпецифікації, щоб запобігти проміжним порушеннÑм чи Ñуперечливим ÑпецифікаціÑм. Цей Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¼Ð¾Ð¶Ðµ зайнÑти трохи чаÑу, перш ніж Ñторінку можна буде розблокувати, оÑкільки це залежить від розміру та чаÑтоти планувальника [https://www.mediawiki.org/wiki/Manual:Job_queue черги завдань].",
+ "smw-property-req-violation-change-propagation-locked-warning": "ВлаÑтивіÑÑ‚ÑŒ «$1» змінено й вона потребує повторного Ð¾Ñ†Ñ–Ð½ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ… ÑутноÑтей за допомогою процеÑу [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ]. ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти трохи чаÑу, оÑкільки це залежить від розміру та чаÑтоти планувальника [https://www.mediawiki.org/wiki/Manual:Job_queue черги завдань], тому рекомендуєтьÑÑ Ñ‚Ð°ÐºÐ¾Ð¶ відклаÑти зміни влаÑтивоÑÑ‚Ñ– на пізніше, щоб запобігти проміжним порушеннÑм чи Ñуперечливим ÑпецифікаціÑм.",
+ "smw-property-req-violation-change-propagation-pending": "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ] перебувають в очікуванні (приблизно $1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|завданнÑ|завданнÑ|завдань}}]), а тому рекомендовано почекати з будь-Ñкими змінами влаÑтивоÑÑ‚Ñ–, доки цей Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ðµ завершитьÑÑ, щоб запобігти проміжним порушеннÑм чи Ñуперечливим ÑпецифікаціÑм.",
+ "smw-category-change-propagation-locked-error": "Категорію «$1» змінено, Ñ– призначені ÑутноÑÑ‚Ñ– Ñлід повторно оцінити за допомогою процеÑу [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ]. Тим чаÑом Ñторінку категорію заблоковано, доки не завершитьÑÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¾Ñновної Ñпецифікації, щоб запобігти проміжним порушеннÑм чи Ñуперечливим ÑпецифікаціÑм. Цей Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¼Ð¾Ð¶Ðµ зайнÑти трохи чаÑу, перед тим Ñк Ñторінку можна буде розблокувати, оÑкільки це залежить від розміру та чаÑтоти планувальника [https://www.mediawiki.org/wiki/Manual:Job_queue черги завдань].",
+ "smw-category-change-propagation-locked-warning": "Категорію «$1» змінено, Ñ– призначені ÑутноÑÑ‚Ñ– необхідно повторно оцінити за допомогою процеÑу [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ]. ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ðµ зайнÑти трохи чаÑу, оÑкільки воно залежить від розміру та чаÑтоти планувальника [https://www.mediawiki.org/wiki/Manual:Job_queue черги завдань], а тому рекомендовано відклаÑти будь-Ñкі подальші зміни в категорії, щоб запобігти проміжним порушеннÑми чи Ñуперечливим ÑпецифікаціÑм.",
+ "smw-category-change-propagation-pending": "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ [https://www.semantic-mediawiki.org/wiki/Change_propagation зміни поширеннÑ] перебувають в очікуванні (приблизно $1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|завданнÑ|завданнÑ|завдань}}]), тому рекомендовано почекати з будь-Ñкими змінами в категорії, доки не завершитьÑÑ Ñ†ÐµÐ¹ процеÑ, щоб запобігти проміжним порушеннÑм чи Ñуперечливим ÑпецифікаціÑм.",
+ "protect-level-smw-pageedit": "Дозволити лише кориÑтувачам, що мають права на Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñторінок (Семантична MediaWiki)",
+ "smw-create-protection": "МожливіÑÑ‚ÑŒ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð»Ð°ÑтивоÑÑ‚Ñ– «$1» обмежене лише до кориÑтувачів з відповідним правом «$2» (або до [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups групи кориÑтувачів]), нарÑду з увімкненим [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode режимом авторитету].",
+ "smw-create-protection-exists": "МожливіÑÑ‚ÑŒ зміни влаÑтивоÑÑ‚Ñ– «$1» обмежене лише до кориÑтувачів з відповідним правом «$2» (або до [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups групи кориÑтувачів]), нарÑду з увімкненим [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode режимом авторитету].",
+ "smw-edit-protection": "Цю Ñторінку [[Property:Is edit protected|захищено]] Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ð²Ð¸Ð¿Ð°Ð´ÐºÐ¾Ð²Ñ–Ð¹ зміні даних, а тому Ñ—Ñ— можуть редагувати лише кориÑтувачі з відповідним редакторÑьким правом («$1») або Ñ‚Ñ–, що належать до відповідної [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups групи кориÑтувачів].",
+ "smw-edit-protection-disabled": "ЗахиÑÑ‚ від редагувань було вимкнено, тому «$1» не може бути викориÑтано Ð´Ð»Ñ Ð·Ð°Ñ…Ð¸Ñту Ñторінок ÑутноÑтей від неавторизованого редагуваннÑ.",
+ "smw-edit-protection-auto-update": "Семантична MediaWiki оновила ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð°Ñ…Ð¸Ñту відповідно до влаÑтивоÑÑ‚Ñ– «Is edit protected».",
+ "smw-edit-protection-enabled": "Захищено від редагувань (Семантична MediaWiki)",
+ "smw-patternedit-protection": "Ð¦Ñ Ñторінка захищена, Ñ—Ñ— можуть редагувати лише кориÑтувачі з відповідними <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions правами].",
+ "smw-property-predefined-edip": "«$1» — це попередньо визначена влаÑтивіÑÑ‚ÑŒ, що забезпечуєтьÑÑ [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Семантичною MediaWiki] Ð´Ð»Ñ Ð·Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð¾Ð³Ð¾, чи вÑтановлено захиÑÑ‚ від редагувань, чи ні.",
+ "smw-property-predefined-long-edip": "Тоді Ñк будь-Ñкий кориÑтувач може додати цю влаÑтивіÑÑ‚ÑŒ до ÑкоїÑÑŒ теми, лише кориÑтувач з оÑобливими правами може редагувати чи відкликати захиÑÑ‚ ÑутноÑÑ‚Ñ– піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾, Ñк влаÑтивіÑÑ‚ÑŒ було додано.",
+ "smw-query-reference-link-label": "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° запит",
+ "smw-format-datatable-emptytable": "Ðемає даних у таблиці",
+ "smw-format-datatable-info": "Ð’Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів від _START_ по _END_ з _TOTAL_",
+ "smw-format-datatable-infoempty": "Показані від 0 до 0 з 0 запиÑів",
+ "smw-format-datatable-infofiltered": "(відфільтровано з уÑього _MAX_ запиÑів)",
+ "smw-format-datatable-infothousands": "&nbsp;",
+ "smw-format-datatable-lengthmenu": "Показати _MENU_ запиÑів",
+ "smw-format-datatable-loadingrecords": "ЗавантаженнÑ…",
+ "smw-format-datatable-processing": "Обробка…",
+ "smw-format-datatable-search": "Пошук:",
+ "smw-format-datatable-zerorecords": "Ðічого не знайдено відповідно до критеріїв пошуку",
+ "smw-format-datatable-first": "Перший",
+ "smw-format-datatable-last": "ОÑтанній",
+ "smw-format-datatable-next": "ÐаÑтупний",
+ "smw-format-datatable-previous": "Попередній",
+ "smw-format-datatable-sortascending": ": активувати ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñтовпців за зроÑтаннÑм",
+ "smw-format-datatable-sortdescending": ": активувати ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñтовпців за ÑпаданнÑм",
+ "smw-format-datatable-toolbar-export": "ЕкÑпорт",
+ "smw-category-invalid-redirect-target": "ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ñ–Ñ Â«$1» міÑтить недійÑну ціль Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° проÑÑ‚Ñ–Ñ€ назв, Ñкий не Ñ” категорією.",
+ "smw-parser-function-expensive-execution-limit": "Ð¦Ñ Ð¿Ð°Ñ€Ñерна Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð´Ð¾ÑÑгла ліміту Ð´Ð»Ñ Ð²Ð¸Ñ‚Ñ€Ð°Ñ‚Ð½Ð¸Ñ… запуÑків (див. параметр конфігурації [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>]).",
+ "smw-postproc-queryref": "Семантична MediaWiki ініціює Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñторінки через необхідніÑÑ‚ÑŒ деÑкої поÑÑ‚-обробки запиту.",
+ "apihelp-smwinfo-summary": "Модуль API Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про ÑтатиÑтику Семантичної MediaWiki та іншої мета-інформації.",
+ "apihelp-ask-summary": "Модуль API Ð´Ð»Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² до Семантичної MediaWiki з викориÑтаннÑм мови «Ask».",
+ "apihelp-askargs-summary": "Модуль API Ð´Ð»Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² до Семантичної MediaWiki за допомогою мови «Ask» у формі ÑпиÑку умов, роздруківок та параметрів.",
+ "apihelp-browsebyproperty-summary": "Модуль API Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про влаÑтивіÑÑ‚ÑŒ або ÑпиÑок влаÑтивоÑтей.",
+ "apihelp-browsebysubject-summary": "Модуль API Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про предмет.",
+ "apihelp-smwtask-summary": "Модуль API Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð´Ð°Ð½ÑŒ, пов'Ñзаних із Семантичною MediaWiki.",
+ "apihelp-smwbrowse-summary": "Модуль API Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÐºÐ¸ дій з переглÑду та пошуку Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ñ€Ð¸Ð¼ÑƒÐ²Ð°Ð½Ð¸Ñ… ÑутноÑтей Семантичної MediaWiki.",
+ "smw-parser-recursion-level-exceeded": "Рівень рекурÑій $1 було перевищено протÑгом процеÑу обробки. Пропонуємо перевірити Ñтруктуру шаблону, або, Ñкщо Ñ” така необхідніÑÑ‚ÑŒ, підкоригувати параметр конфігурації <code>$maxRecursionDepth</code>.",
+ "smw-property-page-list-count": "Показано $1 {{PLURAL:$1|Ñторінку, Ñка викориÑтовує|Ñторінки, Ñкі викориÑтовують|Ñторінок, Ñкі викориÑтовують}} цю влаÑтивіÑÑ‚ÑŒ.",
+ "smw-property-page-list-search-count": "Показано $1 {{PLURAL:$1|Ñторінку, Ñка викориÑтовує|Ñторінки, Ñкі викориÑтовують|Ñторінок, Ñкі викориÑтовують}} цю влаÑтивіÑÑ‚ÑŒ зі збігом Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«$2».",
+ "smw-property-reserved-category": "КатегоріÑ",
+ "smw-category": "КатегоріÑ",
+ "smw-help": "Допомога",
+ "smw-ask-title-keyword-type": "Пошук за ключовим Ñловом",
+ "smw-ask-message-keyword-type": "Цей пошук відповідає умові <code><nowiki>$1</nowiki></code>.",
+ "smw-remote-source-unavailable": "Ðе вдалоÑÑ Ð¿Ñ–Ð´'єднатиÑÑŒ до віддаленої цілі «$1».",
+ "smw-remote-source-disabled": "Джерело '''$1''' вимкнуло підтримку віддалених запитів!",
+ "smw-remote-source-unmatched-id": "Джерело '''$1''' не відповідає верÑÑ–Ñ— Семантичної MediaWiki, Ñка має підтримку віддалених запитів.",
+ "smw-remote-request-note": "Результат отримано із віддаленого джерела '''$1''', Ñ– Ñ” ймовірніÑÑ‚ÑŒ, що згенерований контент міÑтить інформацію, недоÑтупну в межах поточної вікі.",
+ "smw-remote-request-note-cached": "Результат '''кешовано''' з віддаленого джерела '''$1''', Ñ– Ñ” ймовірніÑÑ‚ÑŒ, що згенерований контент міÑтить інформацію, недоÑтупну в межах поточної вікі.",
+ "smw-parameter-missing": "ВідÑутній параметр «$1».",
+ "smw-property-tab-usage": "ВикориÑтаннÑ",
+ "smw-property-tab-redirects": "Синоніми",
+ "smw-property-tab-subproperties": "ПідвлаÑтивоÑÑ‚Ñ–",
+ "smw-property-tab-errors": "Ðеналежні призначеннÑ",
+ "smw-property-tab-specification": "... більше",
+ "smw-concept-tab-list": "СпиÑок",
+ "smw-concept-tab-errors": "Помилки",
+ "smw-ask-tab-result": "Результат",
+ "smw-ask-tab-extra": "Додатково",
+ "smw-ask-tab-debug": "ÐалагодженнÑ",
+ "smw-ask-tab-code": "Код",
+ "smw-install-incomplete-intro": "Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (чи оновленнÑ) <b>Семантичної MediaWiki</b> не завершено, Ñ– треба, щоб ÑкийÑÑŒ адмініÑтратор запуÑтив подані завданнÑ, щоб запобігти невідповідноÑÑ‚Ñ– даних, перед тим, Ñк кориÑтувачі почнуть Ñтворювати чи змінювати контент Ñайту.",
+ "smw-install-incomplete-populate-hash-field": "Ð—Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ Ð¿Ð¾Ð»Ñ–Ð² <code>smw_hash</code> було пропущено під Ñ‡Ð°Ñ Ð²ÑтановленнÑ; необхідно виконати Ñкрипт [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php].",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/ur.json b/www/wiki/extensions/SemanticMediaWiki/i18n/ur.json
new file mode 100644
index 00000000..d41f8b57
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/ur.json
@@ -0,0 +1,35 @@
+{
+ "@metadata": {
+ "authors": [
+ "පසිඳු කà·à·€à·’න්ද",
+ "عرÙان ارشد",
+ "محبوب عالم"
+ ]
+ },
+ "smw_printername_list": "ÙÛرست",
+ "smw_printername_table": "ٹیبل",
+ "smw_printername_category": "زمرÛ",
+ "smw_exportrdf_submit": "برآمد",
+ "properties": "کی خصوصیات",
+ "smw_purge": "ØªØ§Ø²Û Ú©Ø±Ùˆ",
+ "types": "اقسام",
+ "smw_ask_queryhead": "استÙÛام",
+ "smw_ask_defaultformat": "Ù¾ÛÙ„Û’ سے Ø·Û’ شدÛ",
+ "smw_ask_otheroptions": "دوسرے اختیارات",
+ "smw-ask-delete": "[حذ٠کریں]",
+ "smw_sbv_property": "جائداد:",
+ "smw_sbv_value": "قیمت:",
+ "smw_sbv_submit": "نتائج تلاش کریں",
+ "browse": "ویکی میں براؤز کریں",
+ "smw_browselink": "کی خصوصیات کو براؤز کریں",
+ "smw_browse_go": "جانا",
+ "smw_pp_from": "صÙØ­Û Ø³Û’",
+ "smw_pp_type": "پراپرٹی",
+ "smw_pp_submit": "نتائج تلاش کریں",
+ "smw_result_prev": "Ù¾Ú†Ú¾Ù„Û’",
+ "smw_result_next": "اگلے",
+ "smw_result_results": "نتائج",
+ "smw_result_noresults": "کوئی نتائج.",
+ "smw_smwadmin_datarefreshstopconfirm": "جی Ûاں, میں اس بات کا یقین ÛÙˆÚº.",
+ "smw_smwadmin_support": "ÛÙˆ رÛÛŒ ÛÛ’ Ú©ÛŒ حمایت"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/uz.json b/www/wiki/extensions/SemanticMediaWiki/i18n/uz.json
new file mode 100644
index 00000000..ff01c644
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/uz.json
@@ -0,0 +1,17 @@
+{
+ "@metadata": {
+ "authors": [
+ "CoderSI",
+ "Sociologist"
+ ]
+ },
+ "specialpages-group-smw_group": "Semantic MediaWiki",
+ "smw_purge": "Yangilash",
+ "smw_browselink": "Xossalarni koʻrish",
+ "smw-info-par-message": "Tasvirlash uchun xabar",
+ "prefs-smw": "Semantik MediaWiki",
+ "prefs-ask-options": "Maʼnoviy qidiruv moslamalari",
+ "smw-prefs-ask-options-tooltip-display": "«Matn» koʻrsatkichini paydo boʻladigan yordamchi sifatida aks ettirish",
+ "smw-prefs-ask-options-collapsed-default": "Boshlang'ich holatga yig'ilgan turishi uchun tanlov maydoniga ruxsatlilikni yoqish",
+ "smw_unknowntype": "Ushbu xossaning tipi noto'g'ri"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/vec.json b/www/wiki/extensions/SemanticMediaWiki/i18n/vec.json
new file mode 100644
index 00000000..0fc00474
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/vec.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Candalua"
+ ]
+ },
+ "browse": "Nàvega la wiki",
+ "smw-livepreview-loading": "Caricamento in corso…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/vep.json b/www/wiki/extensions/SemanticMediaWiki/i18n/vep.json
new file mode 100644
index 00000000..0df5acaa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/vep.json
@@ -0,0 +1,30 @@
+{
+ "@metadata": {
+ "authors": [
+ "Игорь БродÑкий"
+ ]
+ },
+ "smw_viewasrdf": "RDF-purde",
+ "smw_finallistconjunct": " da",
+ "smw_factbox_head": "Faktad $1:n polhe",
+ "smw_printername_count": "Lugeda rezul'tatad",
+ "smw_printername_list": "Nimikirjutez",
+ "smw_printername_table": "Tablut",
+ "smw_printername_broadtable": "Leved tablut",
+ "smw_exportrdf_submit": "Eksport",
+ "properties": "IÄendad",
+
+ "ask": "Semantine ecind",
+ "smw_ask_submit": "Löuta rezul'tatad",
+ "smw_ask_editquery": "Redaktiruida küzund",
+ "smw_browse_go": "Mäne",
+ "smw_pp_from": "Lehtpolelpäi",
+ "smw_pp_type": "IÄend",
+ "smw_pp_submit": "Löuta rezul'tatad",
+ "smw_result_prev": "Edeline",
+ "smw_result_next": "Jäl'ghine",
+ "smw_result_results": "Rezul'tatad",
+ "smw_result_noresults": "Ei ole rezul'tatoid.",
+ "smwadmin": "Redaktiruida Semantic MediaWiki",
+ "smw-livepreview-loading": "Ozutase…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/vi.json b/www/wiki/extensions/SemanticMediaWiki/i18n/vi.json
new file mode 100644
index 00000000..901f88eb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/vi.json
@@ -0,0 +1,185 @@
+{
+ "@metadata": {
+ "authors": [
+ "Minh Nguyen",
+ "Vinhtantran",
+ "ì•„ë¼"
+ ]
+ },
+ "smw-desc": "Làm cho wiki của bạn khả dụng hÆ¡n – đối vá»›i máy ''cÅ©ng nhÆ°'' con ngÆ°á»i ([https://www.semantic-mediawiki.org/wiki/Help:User_manual tài liệu trá»±c tuyến])",
+ "smw-title": "MediaWiki Ngữ nghĩa",
+ "smw_viewasrdf": "Tin RDF",
+ "smw_finallistconjunct": ", và",
+ "smw_isspecprop": "Thuộc tính này là thuộc tính đặc biệt trong wiki này.",
+ "smw_concept_description": "Miêu tả khái niệm “$1â€",
+ "version-semantic": "Phần mở rộng ngữ nghĩa",
+ "smw_baduri": "Rất tiếc, không cho phép URI có dạng “$1â€.",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "Äếm kết quả",
+ "smw_printername_csv": "Xuất CSV",
+ "smw_printername_dsv": "Xuất DSV",
+ "smw_printername_debug": "Truy vấn gỡ lỗi (cho các chuyên gia)",
+ "smw_printername_embedded": "Nhúng nội dung trang",
+ "smw_printername_json": "Xuất JSON",
+ "smw_printername_list": "Danh sách",
+ "smw_printername_ol": "Liệt kê",
+ "smw_printername_table": "Bảng",
+ "smw_printername_broadtable": "Bảng rộng",
+ "smw_printername_template": "Bản mẫu",
+ "smw_printername_rdf": "Xuất RDF",
+ "smw_printername_category": "Thể loại",
+ "validator-type-class-SMWParamSource": "văn bản",
+ "smw-paramdesc-limit": "Äa số kết quả để cho ra",
+ "smw-paramdesc-mainlabel": "Nhãn cho tên trang chính",
+ "smw-paramdesc-link": "Hiển thị các giá trị là liên kết",
+ "smw-paramdesc-sep": "Dấu phân cách các giá trị",
+ "smw-paramdesc-embedonly": "Không hiển thị các tiêu Ä‘á»",
+ "smw-paramdesc-rdfsyntax": "Cú pháp RDF được sử dụng",
+ "smw-paramdesc-csv-sep": "Dấu phân cách để sử dụng",
+ "smw-paramdesc-dsv-separator": "Dấu phân cách để sử dụng",
+ "smw-paramdesc-dsv-filename": "Tên của tập tin DSV",
+ "smw-paramdesc-order": "Thứ tự xếp của truy vấn",
+ "smw-paramdesc-export": "Tùy chá»n xuất",
+ "smw-printername-feed": "Nguồn cấp RSS và Atom",
+ "smw-paramdesc-feedtype": "Kiểu nguồn cấp",
+ "smw_iq_disabled": "Rất tiếc. Chức năng truy vấn ngữ nghĩa đã bị tắt tại wiki này.",
+ "smw_iq_moreresults": "… kết quả khác",
+ "smw_parseerror": "Không hiểu giá trị cung cấp.",
+ "smw_decseparator": ",",
+ "smw_kiloseparator": ".",
+ "smw_notitle": "Không thể dùng “$1†làm tên trang trên wiki này.",
+ "smw_manytypes": "Thuá»™c tính này có nhiá»u hÆ¡n má»™t kiểu",
+ "smw_emptystring": "Không chấp nhận chuỗi trống.",
+ "smw_notinenum": "“$1†không nằm trong danh sách [[Property:Allows value|các giá trị khả dÄ©]] ($2) cho thuá»™c tính “$3â€.",
+ "smw_noboolean": "“$1†không phải là giá trị Boole (đúng/sai).",
+ "smw_true_words": "true,t,yes,y,đúng,đ,có,c",
+ "smw_false_words": "false,f,no,n,sai,s,không,k",
+ "smw_nofloat": "“$1†không phải là số.",
+ "smw_infinite": "Không há»— trợ các số lá»›n nhÆ° “$1â€.",
+ "smw_novalues": "Không định rõ giá trị.",
+ "smw_nodatetime": "Không hiểu ngày “$1â€.",
+ "smw_toomanyclosing": "DÆ°á»ng có quá nhiá»u lần xuất hiện “$1†trong câu truy vấn.",
+ "smw_noclosingbrackets": "Lần sá»­ dụng “<nowiki>[[</nowiki>†nào đó trong câu truy vấn của bạn không được đóng bằng “]]â€.",
+ "smw_misplacedsymbol": "Ký hiệu “$1†được dùng tại nơi nó không hữu ích.",
+ "smw_unexpectedpart": "Phần “$1†của câu truy vấn không hiểu được.\nKết quả có thể không như mong đợi.",
+ "smw_emptysubquery": "Má»™t truy vấn con nào đó không có Ä‘iá»u kiện hợp lệ.",
+ "smw_misplacedsubquery": "Một truy vấn con nào đó được dùng tại nơi không cho phép dùng truy vấn con.",
+ "smw_valuesubquery": "Truy vấn con không há»— trợ các giá trị của tham số “$1â€.",
+ "smw_badqueryatom": "Phần “<nowiki>[[…]]</nowiki>†nào đó của câu truy vấn không hiểu được.",
+ "smw_propvalueproblem": "Không hiểu giá trị của thuá»™c tính “$1â€.",
+ "smw_nodisjunctions": "Phân tách trong câu truy vấn không hỗ trợ trong wiki này và một phần câu truy vấn bị bỠqua ($1).",
+ "smw_querytoolarge": "{{PLURAL:$2|Äiá»u kiện|Các Ä‘iá»u kiện}} truy vấn sau đây không được xem xét do hạn chế của wiki trong Ä‘á»™ dài hoặc Ä‘á»™ sâu truy vấn: <code>$1</code>.",
+ "smw_notemplategiven": "Xin hãy cung cấp giá trị cho tham số “template†để đinh dạng truy vấn này có thể hoạt động.",
+ "smw_type_header": "Tính chất của loại “$1â€",
+ "smw_typearticlecount": "Hiển thị $1 thuộc tính sử dụng kiểu này.",
+ "smw_attribute_header": "Những trang dùng thuá»™c tính “$1â€",
+ "smw_attributearticlecount": "Hiển thị $1 trang dùng thuộc tính này.",
+ "smw-propertylist-subproperty-header": "Thuộc tính con",
+ "smw-propertylist-redirect-header": "Từ đồng nghĩa",
+ "exportrdf": "Xuất trang ra RDF",
+ "smw_exportrdf_docu": "Trang này cho phép bạn lấy dữ liệu từ trang theo dạng RDF.\nÄể xuất trang, gõ vào tá»±a Ä‘á» trong khung dÆ°á»›i đây, má»™t dòng má»™t tá»±a Ä‘á».",
+ "smw_exportrdf_recursive": "Xuất đệ quy tất cả các trang liên quan.\nChú ý rằng kết quả không thể quá lớn!",
+ "smw_exportrdf_backlinks": "Cũng xuất tất cả các trang chỉ đến trang được xuất.\nTạo ra RDF có thể duyệt được.",
+ "smw_exportrdf_lastdate": "Äừng xuất trang không thay đổi từ má»™t thá»i Ä‘iểm cho trÆ°á»›c.",
+ "smw_exportrdf_submit": "Xuất",
+ "uriresolver": "Bộ giải URI",
+ "properties": "Thuộc tính",
+ "smw_properties_docu": "Các thuộc tính được dùng trong wiki.",
+ "smw_property_template": "$1 thuộc loại $2 (sử dụng $3 lần)",
+ "smw_propertylackspage": "Tất cả các thuộc tính nên được mô tả bằng một trang!",
+ "smw_propertylackstype": "Không có loại này được chỉ định cho thuộc tính này (từ giỠgiả thiết loại $1).",
+ "smw_propertyhardlyused": "Thuộc tính này khó sử dụng trong wiki!",
+ "smw-special-property-searchform-options": "Tùy chá»n",
+ "smw-special-wantedproperties-filter-label": "Bá»™ lá»c:",
+ "smw-special-wantedproperties-filter-none": "Không có",
+ "smw-special-wantedproperties-filter-unapproved": "Chưa chấp nhận",
+ "concepts": "Khái niệm",
+ "smw-sp-concept-header": "Danh sách khái niệm",
+ "unusedproperties": "Thuộc tính không sử dụng",
+ "smw-unusedproperties-docu": "Các thuộc tính sau được tuyên bố mặc dù [https://www.semantic-mediawiki.org/wiki/Unused_properties không có trang nào] sử dụng chúng. Cũng có thể xem [[Special:Properties|danh sách đầu đủ]] hoặc [[Special:WantedProperties|các thuộc tính cần thiết]].",
+ "smw-unusedproperty-template": "$1 thuộc loại $2",
+ "wantedproperties": "Thuộc tính cần nhất",
+ "smw-wantedproperties-docu": "Các thuộc tính sau [https://www.semantic-mediawiki.org/wiki/Wanted_properties được sử dùng trong wiki] nhưng chưa có trang nào miêu tả chúng. Cũng có thể xem [[Special:Properties|danh sách đầy đủ]] hoặc [[Special:UnusedProperties|các thuộc tính không dùng]].",
+ "smw-wantedproperty-template": "$1 ($2 lần sử dụng)",
+ "smw_purge": "Làm tươi",
+ "types": "Các dạng",
+ "smw_types_docu": "Sau đây là danh sách [https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes tất cả các kiểu dữ liệu có sẵn], má»—i [https://www.semantic-mediawiki.org/wiki/Help:Datatype kiểu] ứng vá»›i tập hợp thuá»™c tính khác biệt để miêu tả giá trị theo đặc Ä‘iểm lÆ°u giữ và hiển thị di truyá»n đến má»™t thuá»™c tính được chỉ định.",
+ "smw-statistics": "Thống kê ngữ nghĩa",
+ "smw-statistics-query-size": "Kích thước truy vấn",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1}}Khái niệm",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1}}Khái niệm]]",
+ "smw_uri_doc": "Bá»™ giải URI hiện thá»±c [$1 THẺ W3C tìm thấy tại httpRange-14].\nNó lo việc con ngÆ°á»i không quay lại trang web.",
+ "ask": "Tìm kiếm ngữ nghĩa",
+ "smw_ask_sortby": "Sắp xếp theo cá»™t (tùy chá»n)",
+ "smw_ask_ascorder": "Tăng dần",
+ "smw_ask_descorder": "Giảm dần",
+ "smw-ask-order-rand": "Ngẫu nhiên",
+ "smw_ask_submit": "Kết quả tìm kiếm",
+ "smw_ask_editquery": "Sửa truy vấn",
+ "smw_add_sortcondition": "[Thêm Ä‘iá»u kiện sắp xếp]",
+ "smw-ask-sort-add-action": "Thêm Ä‘iá»u kiện sắp xếp",
+ "smw_ask_hidequery": "Ẩn truy vấn",
+ "smw_ask_help": "Trợ giúp truy vấn",
+ "smw_ask_queryhead": "Äiá»u kiện",
+ "smw_ask_printhead": "Lá»±a chá»n dữ liệu để hiển thị",
+ "smw_ask_format_as": "Äịnh dạng:",
+ "smw_ask_defaultformat": "mặc định",
+ "smw_ask_otheroptions": "Tùy chá»n khác",
+ "smw_ask_show_embed": "Hiện mã để nhúng",
+ "smw_ask_hide_embed": "Ẩn mã để nhúng",
+ "smw-ask-delete": "Xóa",
+ "smw-ask-sorting": "Sắp xếp",
+ "smw-ask-options": "Tùy chá»n",
+ "smw-ask-options-sort": "Tùy chá»n sắp xếp",
+ "smw-ask-search": "Tìm kiếm",
+ "smw-ask-debug": "Gỡ lỗi",
+ "smw-ask-result": "Kết quả",
+ "smw-ask-empty": "Trống",
+ "smw-ask-format": "Äịnh dạng",
+ "searchbyproperty": "Tìm theo thuộc tính",
+ "smw_sbv_docu": "Tìm tất cả các trang có thuộc tính và giá trị cho trước.",
+ "smw_sbv_novalue": "Xin nhập vào má»™t giá trị hợp lệ cho thuá»™c tính, hoặc xem tất cả các giá trị thuá»™c tính của “$1.â€",
+ "smw_sbv_displayresult": "Danh sách tất cả các trang có thuá»™c tính “$1†có giá trị “$2â€",
+ "smw_sbv_property": "Thuộc tính:",
+ "smw_sbv_value": "Giá trị:",
+ "smw_sbv_submit": "Kết quả tìm kiếm",
+ "browse": "Duyệt wiki",
+ "smw_browselink": "Duyệt thuộc tính",
+ "smw_browse_article": "Gõ vào tên trang để bắt đầu duyệt.",
+ "smw_browse_go": "Xem",
+ "smw_browse_show_incoming": "hiện các thuộc tính liên kết đến đây",
+ "smw_browse_hide_incoming": "ẩn các thuộc tính liên kết đến đây",
+ "smw_browse_no_outgoing": "Trang này không có thuộc tính.",
+ "smw_browse_no_incoming": "Không có thuộc tính liên kết đến trang này.",
+ "pageproperty": "Tìm kiếm thuộc tính trang",
+ "smw_pp_docu": "Tìm tất cả các lấp chỗ trống của một thuộc tính tại một trang cho trước.\nXin nhập vào cả một trang và một thuộc tính.",
+ "smw_pp_from": "Từ trang",
+ "smw_pp_type": "Thuộc tính",
+ "smw_pp_submit": "Kết quả tìm kiếm",
+ "smw_result_prev": "TrÆ°á»›c",
+ "smw_result_next": "Sau",
+ "smw_result_results": "Kết quả",
+ "smw_result_noresults": "Không tìm thấy kết quả.",
+ "smwadmin": "Chức năng quản lý",
+ "smw-admin-statistics-job-title": "Thống kê việc làm",
+ "smw_smwadmin_return": "Trở lại $1",
+ "smw-admin-announce": "Tuyên bố wiki của bạn",
+ "smw-admin-deprecation-notice-title-notice": "Thông báo",
+ "smw-admin-deprecation-notice-title-replacement": "Thay thế",
+ "smw-admin-deprecation-notice-title-removal": "Loại bá»",
+ "smw_smwadmin_datarefreshbutton": "Bắt đầu nâng cấp dữ liệu",
+ "smw_smwadmin_datarefreshstop": "Ngừng việc nâng cấp",
+ "smw_smwadmin_datarefreshstopconfirm": "{{GENDER:$1}}Có chắc",
+ "smw-admin-support": "Trợ giúp",
+ "smw-admin-smwhomepage": "Tài liệu sử dụng đầy đủ của MediaWiki Ngữ nghĩa có sẵn tại <b><a href=\"https://www.semantic-mediawiki.org/?uselang=vi\">semantic-mediawiki.org</a></b>.",
+ "smw-admin-bugsreport": "Hãy báo lá»—i tại <a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">trang quản lý vấn Ä‘á»</a>. Trang <a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">báo cáo lá»—i</a> hÆ°á»›ng dẫn soạn thảo báo cáo vấn Ä‘á» có hiệu lá»±c.",
+ "smw_adminlinks_datastructure": "Cấu trúc dữ liệu",
+ "smw_adminlinks_displayingdata": "Hiển thị dữ liệu",
+ "smw_adminlinks_inlinequerieshelp": "Trợ giúp truy vấn nội dòng",
+ "smw-createproperty-isproperty": "Nó là tính chất thuộc kiểu $1.",
+ "smw-createproperty-allowedvals": "{{PLURAL:$1|Giá trị|Các giá trị}} cho phép của thuộc tính này là:",
+ "prefs-smw": "MediaWiki Ngữ nghĩa",
+ "smw_unknowntype": "Kiểu của thuộc tính này không hợp lệ.",
+ "smw_concept_header": "Trang vá» khái niệm “$1â€",
+ "smw-livepreview-loading": "Äang tải…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/vo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/vo.json
new file mode 100644
index 00000000..bf281811
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/vo.json
@@ -0,0 +1,85 @@
+{
+ "@metadata": {
+ "authors": [
+ "Malafaya",
+ "Smeira"
+ ]
+ },
+ "smw_finallistconjunct": ", e",
+ "smw_factbox_head": "Jenöfots dö $1",
+ "smw_isspecprop": "Patöf at binon patöf patik in vük at.",
+ "smw_concept_description": "Bepenam suemoda: „$1“.",
+ "smw_no_concept_namespace": "Suemods kanons pamiedetön te su pads nemadaspada: Miedet.",
+ "smw_multiple_concepts": "Suemodapad alik dalon labön suemodimiedeti te bali.",
+ "smw_baduri": "Els URI ön fom: „$1“ no padälons.",
+ "smw_iq_moreresults": "... seks pluik",
+ "smw_parseerror": "Völad pegivöl no pesuemon.",
+ "smw_notitle": "„$1“ no dalon pagebön as padanem in vük at.",
+ "smw_wrong_namespace": "Te pades se nemaspad: „$1“ padälons ad binön is.",
+ "smw_manytypes": "Sots plu bals pemiedetons pro patöf.",
+ "smw_emptystring": "Vödems vagik no pelasumons.",
+ "smw_notinenum": "„$1“ no komon in lised völadas mögik ($2) patöfa at.",
+ "smw_true_words": "veratik,v,si,s",
+ "smw_false_words": "dobik,d,no,n",
+ "smw_nofloat": "„$1“ no binon num.",
+ "smw_infinite": "Nums so gretik äs „$1“ no kanons pagebön.",
+ "smw_nodatetime": "Dät: „$1“ no pesuemon.",
+ "smw_emptysubquery": "Donaseivid semik no labon stipi lonöföl.",
+ "smw_misplacedsubquery": "Donaseivid semik pägebon uto, kö donaseivids no padälons.",
+ "smw_badqueryatom": "Dil semik: „<nowiki>[[…]]</nowiki>“ seivida no pesuemon.",
+ "smw_propvalueproblem": "Völad patöfa: „$1“ no pesuemon.",
+ "smw_notemplategiven": "Gevolös völadi paramete: „samafomot“, dat seividafomät at ojäfidon.",
+ "smw_type_header": "Patöfs sota: „$1“",
+ "smw_typearticlecount": "{{PLURAL:$1|Patöf $1 pajonon, kel labon|Patöfs $1 pajonons, kels labons}} soti at.",
+ "smw_attribute_header": "Pads patöfi: „$1“ geböls",
+ "smw_attributearticlecount": "{{PLURAL:$1|Pad $1 pajonon, kel gebon|Pads $1 pajonons, kels gebons}} patöfi at.",
+ "smw_subproperty_header": "Donapatöfs",
+ "smw_subpropertyarticlecount": "Patöf at labon {{PLURAL:$1|donapatöfi|donapatöfis}} sököl $1.",
+ "exportrdf": "Seveigön padis at RDF",
+ "properties": "Patöfs",
+ "smw_properties_docu": "Patöfs sököl pagebons in vük.",
+ "smw_property_template": "$1 sota: $2 ($3)",
+ "smw_propertylackspage": "Patöfs valik sötons pabepenön su pad semik!",
+ "smw_propertyhardlyused": "Patöf at pagebon vemo selediko in vük!",
+ "unusedproperties": "Patöfs no pegeböls",
+ "smw_unusedproperties_docu": "Patöfs sököl dabinons, do pad votik nonik gebon onis.",
+ "smw_unusedproperty_template": "$1 sota: $2",
+ "wantedproperties": "Patöfs pavilöl",
+ "smw_wantedproperties_docu": "Patöfs sököl pagebons in vük ab no nog labons bepenamapadis oksik.",
+ "smw_wantedproperty_template": "$1 (gebs $2)",
+ "types": "Sots",
+ "smw_ask_submit": "Tuvön sekis",
+ "smw_ask_editquery": "Bevobön seividi",
+ "smw_add_sortcondition": "[Läükön leodükamastipi]",
+ "smw_ask_hidequery": "Klänedön seividi",
+ "smw_ask_queryhead": "Seivid",
+ "searchbyproperty": "Sukön ma patöf",
+ "smw_sbv_docu": "Sukön padis valik patöfi e völadi semikis labölis.",
+ "smw_sbv_novalue": "Penolös völadi lonöfol patöfa, u logolös patöfavöladis valik tefü „$1“.",
+ "smw_sbv_displayresult": "Lised padas valik labü völad: „$2“ patöfa: „$1“.",
+ "smw_sbv_displayresultfuzzy": "Lised padas valik labü völad: „$1“ patöfa: „$1“.\nBi petuvons pads te aniks, i völads nilöfik palisedons.",
+ "smw_sbv_property": "Patöf",
+ "smw_sbv_value": "Völad:",
+ "smw_sbv_submit": "Tuvön sekis",
+ "browse": "Padön vüki",
+ "smw_browselink": "Logön patöfis",
+ "smw_browse_article": "Penolös nemi pada, de kel padam oprimon.",
+ "smw_browse_go": "Ledunön",
+ "smw_browse_show_incoming": "jonön patöfis isio peyümölis",
+ "smw_browse_hide_incoming": "klänedön patöfis isio peyümölis",
+ "smw_browse_no_outgoing": "Pad at no labon patöfis.",
+ "smw_browse_no_incoming": "Patöfs nonik peyümons lü pad at.",
+ "smw_inverse_label_default": "$1 de",
+ "pageproperty": "Suk padapatöfas",
+ "smw_pp_from": "De pad",
+ "smw_pp_type": "Patöf",
+ "smw_pp_submit": "Tuvön sekis",
+ "smw_result_prev": "Büik",
+ "smw_result_next": "Sököl",
+ "smw_result_results": "Seks.",
+ "smw_result_noresults": "Seks nonik.",
+ "smw_smwadmin_return": "Geikön lü pad: $1",
+ "smw_concept_header": "Pads suemoda: „$1“.",
+ "smw_conceptarticlecount": "{{PLURAL:$1|Pajonon pad $1, kel duton|Pajonons pads $1, kels dutons}} lü suemod at.",
+ "smw-livepreview-loading": "Pabelodon…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/vro.json b/www/wiki/extensions/SemanticMediaWiki/i18n/vro.json
new file mode 100644
index 00000000..d361fd59
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/vro.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Võrok"
+ ]
+ },
+ "browse": "Viki kaeminõ",
+ "smw-livepreview-loading": "Laat…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/wa.json b/www/wiki/extensions/SemanticMediaWiki/i18n/wa.json
new file mode 100644
index 00000000..6017ba0a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/wa.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Srtxg"
+ ]
+ },
+ "smw_purge": "Rafrister",
+ "smw-ui-tooltip-title-legend": "Ledjinde",
+ "smw-livepreview-loading": "Tcherdjant..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/war.json b/www/wiki/extensions/SemanticMediaWiki/i18n/war.json
new file mode 100644
index 00000000..34a442ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/war.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "JinJian"
+ ]
+ },
+ "browse": "Browse wiki",
+ "smw-livepreview-loading": "Ginkakarga. . ."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/wo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/wo.json
new file mode 100644
index 00000000..349e3169
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/wo.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Ibou"
+ ]
+ },
+ "browse": "Wër wiki bi"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/wuu.json b/www/wiki/extensions/SemanticMediaWiki/i18n/wuu.json
new file mode 100644
index 00000000..9fed25af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/wuu.json
@@ -0,0 +1,8 @@
+{
+ "@metadata": {
+ "authors": [
+ "Poiuyt"
+ ]
+ },
+ "browse": "æµè§ˆç»´åŸº"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/xal.json b/www/wiki/extensions/SemanticMediaWiki/i18n/xal.json
new file mode 100644
index 00000000..7b919d21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/xal.json
@@ -0,0 +1,4 @@
+{
+ "@metadata": [],
+ "smw-livepreview-loading": "Белднә..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/xmf.json b/www/wiki/extensions/SemanticMediaWiki/i18n/xmf.json
new file mode 100644
index 00000000..dca5cb1f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/xmf.json
@@ -0,0 +1,10 @@
+{
+ "@metadata": {
+ "authors": [
+ "Silovan"
+ ]
+ },
+ "browse": "ვიკიშ ძირáƒáƒ¤áƒ",
+ "smw-limitreport-intext-parsertime-value": "$1 მერქáƒ",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 მერქáƒ"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/yi.json b/www/wiki/extensions/SemanticMediaWiki/i18n/yi.json
new file mode 100644
index 00000000..b25b19f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/yi.json
@@ -0,0 +1,21 @@
+{
+ "@metadata": {
+ "authors": [
+ "פוילישער",
+ "පසිඳු කà·à·€à·’න්ද"
+ ]
+ },
+ "smw_finallistconjunct": "×ון",
+ "smw_printername_list": "ליסטע",
+ "smw_printername_category": "ק×ַטעג×ָריע",
+ "smw-paramdesc-table-transpose": "ווײַזן ט×בעלע קעפלעך ווערטיק×ל ×ון רעזולט×טן ×”×ריז×נט",
+ "smw_nodatetime": "די ד×ַטע \"$1\" ××™×– נישט פֿ×רשטענדלעך.",
+ "smw_exportrdf_submit": "עקספ×רטירן",
+ "smw_sbv_value": "ווערט:",
+ "browse": "בלעטערן וויקי",
+ "smw_browse_go": "גיין",
+ "smw_result_prev": "פֿריערדיקער",
+ "smw_result_results": "רעזולט×ט",
+ "smw_smwadmin_datarefreshstopconfirm": "×™×, ×›'בין {{GENDER:$1|זיכער}}.",
+ "smw-livepreview-loading": "ל×דנדיג…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/yo.json b/www/wiki/extensions/SemanticMediaWiki/i18n/yo.json
new file mode 100644
index 00000000..a32285dc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/yo.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Demmy"
+ ]
+ },
+ "browse": "Ìtúwò wiki",
+ "smw-livepreview-loading": "Óúnbá»Ì€wá..."
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/yue.json b/www/wiki/extensions/SemanticMediaWiki/i18n/yue.json
new file mode 100644
index 00000000..fc2f0e7e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/yue.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Yueman"
+ ]
+ },
+ "browse": "ç€è¦½ç¶­åŸº",
+ "smw-livepreview-loading": "載入緊…"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hans.json b/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hans.json
new file mode 100644
index 00000000..5943e927
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hans.json
@@ -0,0 +1,804 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Gzdavidwong",
+ "Li3939108",
+ "Liangent",
+ "Linforest",
+ "Liuxinyu970226",
+ "Onecountry",
+ "PhiLiP",
+ "Richarddong",
+ "Shirayuki",
+ "Xiaomingyan",
+ "Yanmiao liu",
+ "Yfdyh000",
+ "ì•„ë¼",
+ "Hudafu",
+ "Stieizc",
+ "CXuesong",
+ "Nemo bis",
+ "NeverBehave",
+ "Eduardo Addad de Oliveira",
+ "A2093064",
+ "Deathkon",
+ "阿pp",
+ "Wxyveronica",
+ "Hanyanbo98",
+ "夢è¶è‘¬èŠ±",
+ "A Chinese Wikipedian",
+ "Phenolla",
+ "Angrydog001"
+ ]
+ },
+ "smw-desc": "让您更亲近wiki——对机器''与''人都是如此([https://www.semantic-mediawiki.org/wiki/Help:User_manual 在线文档])",
+ "smw-title": "语义MediaWiki",
+ "smw-semantics-not-enabled": "语义MediaWiki功能没有为此wikiå¯ç”¨ã€‚",
+ "smw_viewasrdf": "RDFæº",
+ "smw_finallistconjunct": ",和",
+ "smw-factbox-head": "...更多有关“$1â€",
+ "smw-factbox-facts": "事实",
+ "smw-factbox-facts-help": "显示由æŸä¸€ä½ç”¨æˆ·åˆ›å»ºçš„声明和事实",
+ "smw-factbox-facts-derived": "è¡ç”Ÿçš„事实",
+ "smw-factbox-facts-derived-help": "显示è¡ç”Ÿè‡ªè§„则的事实,或是在其他推ç†æŠ€æœ¯çš„帮助下生æˆçš„事实",
+ "smw_isspecprop": "此属性为当å‰ç»´åŸºä¸­çš„特殊属性。",
+ "smw-concept-cache-header": "缓存使用",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count 概念缓存]包å«{{PLURAL:$1|'''$1'''个实体}}($2)。",
+ "smw-concept-no-cache": "没有å¯ç”¨ç¼“存。",
+ "smw_concept_description": "关于概念“$1â€çš„æè¿°",
+ "smw_no_concept_namespace": "åªèƒ½åœ¨Concept:å字空间之中定义概念。",
+ "smw_multiple_concepts": "æ¯ä¸ªæ¦‚念页é¢åªèƒ½æœ‰ä¸€ä¸ªæ¦‚念定义。",
+ "smw_concept_cache_miss": "因为维基设置è¦æ±‚概念\"$1\"在离线时计算,因而此概念现在无法使用。如果这个问题在一段时间åŽä»æœªè§£å†³ï¼Œè¯·å‘网站管ç†å‘˜è¯·æ±‚令该概念å¯ç”¨ã€‚",
+ "smw_noinvannot": "ä¸èƒ½ä¸ºé€†å±žæ€§æŒ‡å®šå–值。",
+ "version-semantic": "语义扩展程åº",
+ "smw_baduri": "ä¸å…许\"$1\"å½¢å¼çš„URI。",
+ "smw_csv_link": "CSV",
+ "smw_dsv_link": "DSV",
+ "smw_json_link": "JSON",
+ "smw_rdf_link": "RDF",
+ "smw_printername_count": "计数结果",
+ "smw_printername_csv": "导出为CSV",
+ "smw_printername_dsv": "导出为DSV",
+ "smw_printername_debug": "调试查询(适用于专家)",
+ "smw_printername_embedded": "嵌入型页é¢å†…容",
+ "smw_printername_json": "导出为JSON",
+ "smw_printername_list": "列表",
+ "smw_printername_plainlist": "纯列表",
+ "smw_printername_ol": "有åºåˆ—表",
+ "smw_printername_ul": "æ— åºåˆ—表",
+ "smw_printername_table": "表格",
+ "smw_printername_broadtable": "宽表格",
+ "smw_printername_template": "模æ¿",
+ "smw_printername_templatefile": "模æ¿æ–‡ä»¶",
+ "smw_printername_rdf": "导出为RDF",
+ "smw_printername_category": "分类",
+ "validator-type-class-SMWParamSource": "文本",
+ "smw-paramdesc-limit": "返回结果最大数目",
+ "smw-paramdesc-offset": "第一个结果的å移é‡",
+ "smw-paramdesc-headers": "显示标头/属性å称",
+ "smw-paramdesc-mainlabel": "用于为首页赋予å称的标签",
+ "smw-paramdesc-link": "以链接形å¼æ˜¾ç¤ºå–值",
+ "smw-paramdesc-intro": "查询结果å‰è¦æ˜¾ç¤ºçš„文字(如果有任何结果的è¯ï¼‰",
+ "smw-paramdesc-outro": "查询结果åŽè¦æ˜¾ç¤ºçš„文字(如果有任何结果的è¯ï¼‰",
+ "smw-paramdesc-default": "查询结果为空时è¦æ˜¾ç¤ºçš„文字",
+ "smw-paramdesc-sep": "结果之间的分隔符",
+ "smw-paramdesc-propsep": "结果记录的属性之间的分隔符",
+ "smw-paramdesc-valuesep": "用于结果属性值之间的分隔符",
+ "smw-paramdesc-showsep": "在CSV文件顶部显示分隔符(“sep=<value>â€ï¼‰",
+ "smw-paramdesc-distribution": "计算并显示值出现的次数,而ä¸æ˜¯æ¯ä¸€ä¸ªå€¼ã€‚",
+ "smw-paramdesc-distributionsort": "按出现次数排åºå–值的分布。",
+ "smw-paramdesc-distributionlimit": "å°†å–值分布é™åˆ¶åœ¨æŸäº›å–值的计数之上。",
+ "smw-paramdesc-aggregation": "指定èšåˆåº”与什么相关",
+ "smw-paramdesc-template": "输出显示模æ¿çš„å称",
+ "smw-paramdesc-columns": "è¦æ˜¾ç¤ºç»“果的列数",
+ "smw-paramdesc-userparam": "使用模æ¿æ—¶ï¼Œå‘模æ¿è°ƒç”¨æ‰€ä¼ é€’çš„å–值",
+ "smw-paramdesc-class": "为列表设置的é¢å¤–CSSç±»",
+ "smw-paramdesc-introtemplate": "用于在查询结果å‰æ˜¾ç¤ºå†…容的模æ¿çš„å称",
+ "smw-paramdesc-outrotemplate": "用于在查询结果åŽæ˜¾ç¤ºå†…容的模æ¿çš„å称",
+ "smw-paramdesc-embedformat": "用于定义标题的HTML标签",
+ "smw-paramdesc-embedonly": "ä¸æ˜¾ç¤ºæ ‡é¢˜",
+ "smw-paramdesc-table-class": "添加的设置表的CSS类",
+ "smw-paramdesc-table-transpose": "åž‚ç›´æ–¹å‘显示表头,水平方å‘显示结果",
+ "smw-paramdesc-rdfsyntax": "所è¦ä½¿ç”¨çš„RDF语法",
+ "smw-paramdesc-csv-sep": "指定的æ åˆ†éš”符",
+ "smw-paramdesc-csv-valuesep": "指定值的分隔符",
+ "smw-paramdesc-csv-merge": "åˆå¹¶è¡Œå’Œåˆ—值与独立的对象标识符(或称首列)",
+ "smw-paramdesc-csv-bom": "在输出文件的顶端添加BOM(信å·å­—节顺åºå­—符)",
+ "smw-paramdesc-dsv-separator": "è¦ä½¿ç”¨çš„分隔符",
+ "smw-paramdesc-dsv-filename": "DSV文件的å称",
+ "smw-paramdesc-filename": "输出文件的å称",
+ "smw-smwdoc-description": "显示å¯ä»¥ç”¨äºŽæŒ‡å®šç»“果格å¼çš„所有å‚数以åŠé»˜è®¤å€¼å’Œè¯´æ˜Žçš„表格。",
+ "smw-smwdoc-default-no-parameter-list": "此结果格å¼æœªæ供格å¼ç‰¹å®šå‚数。",
+ "smw-smwdoc-par-format": "显示å‚数文档的结果格å¼ã€‚",
+ "smw-smwdoc-par-parameters": "è¦æ˜¾ç¤ºå“ªäº›å‚数。\"specific\"(专用)表示该格å¼æ‰€æ·»åŠ é‚£äº›çš„,\"base\"(基础)表示所有格å¼å½“中å¯ç”¨çš„那些,而\"all\"(全部)表示两ç§æƒ…况具有。",
+ "smw-paramdesc-sort": "排åºæŸ¥è¯¢çš„属性",
+ "smw-paramdesc-order": "查询排åºçš„顺åº",
+ "smw-paramdesc-searchlabel": "继续æœç´¢çš„文本",
+ "smw-paramdesc-named_args": "请指定传递给该模æ¿çš„å˜é‡",
+ "smw-paramdesc-template-arguments": "设置命åå‚数如何传递到模æ¿",
+ "smw-paramdesc-import-annotation": "é¢å¤–的带注释数æ®åœ¨è§£æžä¸»é¢˜æœŸé—´éƒ½ä¼šè¢«å¤åˆ¶",
+ "smw-paramdesc-export": "导出选项",
+ "smw-paramdesc-prettyprint": "显示é¢å¤–缩进和æ¢è¡Œçš„漂亮打å°å¼ï¼ˆpretty-print)输出形å¼",
+ "smw-paramdesc-json-unescape": "包å«éžè½¬ä¹‰æ–œæ å’Œå¤šå­—节Unicode字符的输出",
+ "smw-paramdesc-json-type": "åºåˆ—化类型",
+ "smw-paramdesc-source": "备选查询æ¥æº",
+ "smw-paramdesc-jsonsyntax": "所è¦ä½¿ç”¨çš„JSON语法",
+ "smw-printername-feed": "RSS和Atom订阅",
+ "smw-paramdesc-feedtype": "订阅类型",
+ "smw-paramdesc-feedtitle": "æºæ ‡é¢˜æ–‡å­—",
+ "smw-paramdesc-feeddescription": "è¦ç”¨ä½œfeed的说明的文字",
+ "smw-paramdesc-feedpagecontent": "è¦é€šè¿‡è®¢é˜…显示的页é¢å†…容",
+ "smw-label-feed-description": "$1 $2订阅",
+ "smw_iq_disabled": "此维基的语义查询已ç¦ç”¨",
+ "smw_iq_moreresults": "...更多结果",
+ "smw_parseerror": "输入的值无法ç†è§£ã€‚",
+ "smw_notitle": "\"$1\"ä¸å¾—被用åšæ­¤ç»´åŸºçš„页é¢å称。",
+ "smw_noproperty": "\"$1\"ä¸èƒ½ç”¨ä½œæ­¤ç»´åŸºä¸­çš„一个属性å称。",
+ "smw_wrong_namespace": "ä»…å…许å字空间“$1â€ä¸­çš„页é¢ã€‚",
+ "smw_manytypes": "此属性定义了一ç§ä»¥ä¸Šç±»åž‹ã€‚",
+ "smw_emptystring": "ä¸æŽ¥å—空字符串。",
+ "smw_notinenum": "“$1â€ä¸åœ¨ç”¨äºŽâ€œ$3â€å±žæ€§[[Property:Allows value|å…许值]]的列表($2)中。",
+ "smw_noboolean": "\"$1\"ä¸æ˜¯å¸ƒå°”(是éžï¼‰å€¼",
+ "smw_true_words": "true,t,yes,y,是,真,对",
+ "smw_false_words": "false,f,no,n,å¦,å‡,é”™",
+ "smw_nofloat": "\"$1\"ä¸æ˜¯æ•°å­—。",
+ "smw_infinite": "\"$1\"数值过大,无法支æŒã€‚",
+ "smw_unitnotallowed": "\"$1\"未被声明为此属性的一个有效的度é‡å•ä½ã€‚",
+ "smw_nounitsdeclared": "此属性没有声明任何度é‡å•ä½ã€‚",
+ "smw_novalues": "未设置数值。",
+ "smw_nodatetime": "无法ç†è§£æ—¥æœŸ\"$1\"。",
+ "smw_toomanyclosing": "查询中似乎出现了太多次“$1â€ã€‚",
+ "smw_noclosingbrackets": "您的查询中有未匹é…“]]â€çš„“<nowiki>[[</nowiki>â€ã€‚",
+ "smw_misplacedsymbol": "符å·â€œ$1â€çš„ä½ç½®æ— æ•ˆã€‚",
+ "smw_unexpectedpart": "查询的\"$1\"部分无法ç†è§£ã€‚\n结果å¯èƒ½ä¸Žé¢„料的ä¸åŒã€‚",
+ "smw_emptysubquery": "一些å­æŸ¥è¯¢çŠ¶æ€æ— æ•ˆã€‚",
+ "smw_misplacedsubquery": "一些å­æŸ¥è¯¢è¢«ç”¨äºŽç¦æ­¢ä½ç½®ã€‚",
+ "smw_valuesubquery": "ä¸æ”¯æŒå¯¹å±žæ€§\"$1\"值的å­æŸ¥è¯¢",
+ "smw_badqueryatom": "查询的一部分“<nowiki>[[...]]</nowiki>â€æ— æ³•ç†è§£ã€‚",
+ "smw_propvalueproblem": "属性\"$1\"的值无法ç†è§£ã€‚",
+ "smw_noqueryfeature": "此维基ä¸æ”¯æŒä¸€äº›æŸ¥è¯¢åŠŸèƒ½ï¼ŒæŸ¥è¯¢è¢«éƒ¨åˆ†åœæ­¢ï¼ˆ$1)。",
+ "smw_noconjunctions": "此维基ä¸æ”¯æŒæŸ¥è¯¢ä¸­çš„åˆå–,查询被部分åœæ­¢ï¼ˆ$1)。",
+ "smw_nodisjunctions": "此维基ä¸æ”¯æŒæŸ¥è¯¢ä¸­çš„æžå–,查询被部分åœæ­¢ï¼ˆ$1)。",
+ "smw_querytoolarge": "由于此wiki在查询大å°æˆ–深度上的é™åˆ¶ï¼Œä¸‹åˆ—{{PLURAL:$2|查询æ¡ä»¶}}无法考虑:<code>$1</code>。",
+ "smw_notemplategiven": "为使查询格å¼æ­£å¸¸å·¥ä½œï¼Œè¯·ä¸ºå‚数“模æ¿â€èµ‹å€¼ã€‚",
+ "smw_db_sparqlqueryproblem": "无法从SPARQLæ•°æ®åº“获得查询结果。这个错误å¯èƒ½æ˜¯æš‚时的,也å¯èƒ½æ˜¯æ•°æ®åº“软件的错误造æˆçš„。",
+ "smw_db_sparqlqueryincomplete": "本查询太过å¤æ‚因而被中止,å¯èƒ½ä¼šå› æ­¤ä¸¢å¤±éƒ¨åˆ†ç»“果。如果å¯èƒ½ï¼Œè¯·å°è¯•æ”¹ç”¨ä¸€ä¸ªè¾ƒç®€å•çš„查询。",
+ "smw_type_header": "类型“$1â€çš„属性",
+ "smw_typearticlecount": "使用此类型显示$1个{{PLURAL:$1|属性}}。",
+ "smw_attribute_header": "使用属性“$1â€çš„页é¢",
+ "smw_attributearticlecount": "使用此属性显示$1个{{PLURAL:$1|页é¢}}。",
+ "smw-propertylist-subproperty-header": "å­å±žæ€§",
+ "smw-propertylist-redirect-header": "åŒä¹‰è¯",
+ "smw-propertylist-error-header": "ä¸é€‚当分é…的页é¢",
+ "smw-propertylist-count": "显示$1个相关{{PLURAL:$1|实体}}。",
+ "smw-propertylist-count-with-restricted-note": "显示$1个相关{{PLURAL:$1|实体}}(有更多实体å¯ç”¨ï¼Œä½†å¯æ˜¾ç¤ºçš„实体被é™åˆ¶åœ¨â€œ$2â€ï¼‰ã€‚",
+ "smw-propertylist-count-more-available": "显示$1个相关{{PLURAL:$1|实体}}(有更多实体å¯ç”¨ï¼‰ã€‚",
+ "specialpages-group-smw_group": "语义MediaWiki",
+ "exportrdf": "导出页é¢è‡³RDF",
+ "smw_exportrdf_docu": "此页é¢å…许您从一个RDFæ ¼å¼çš„页é¢èŽ·å¾—æ•°æ®ã€‚è¦å¯¼å‡ºæ•°æ®ï¼Œè¯·åœ¨ä¸‹æ–¹çš„框中输入标题,æ¯è¡Œä¸€ä¸ªã€‚",
+ "smw_exportrdf_recursive": "递归导出所有相关页é¢ã€‚注æ„:结果文件会很大。",
+ "smw_exportrdf_backlinks": "åŒæ—¶å¯¼å‡ºä¸Žå¯¼å‡ºé¡µé¢ç›¸å…³çš„所有页é¢ã€‚生æˆå¯æµè§ˆçš„RDF。",
+ "smw_exportrdf_lastdate": "ä¸è¦å¯¼å‡ºè‡ªç»™å®šæ—¶é—´åŽæ— æ›´æ”¹çš„页é¢ã€‚",
+ "smw_exportrdf_submit": "导出",
+ "uriresolver": "URI解æžå™¨",
+ "properties": "属性",
+ "smw_properties_docu": "本维基使用以下属性。",
+ "smw_property_template": "$2类型的$1($3次使用)",
+ "smw_property_template_notype": "$1($2)",
+ "smw_propertylackspage": "所有属性应该被页é¢æè¿°ï¼",
+ "smw_propertylackstype": "此属性未指定类型(目å‰é»˜è®¤ä¸ºç±»åž‹$1)。",
+ "smw_propertyhardlyused": "此属性在本维基中ä¸å¸¸ä½¿ç”¨ï¼",
+ "smw-property-name-invalid": "属性$1ä¸èƒ½ä½¿ç”¨ï¼ˆæ— æ•ˆçš„属性å称)。",
+ "smw-property-name-reserved": "“$1â€è¢«åˆ—举为ä¿ç•™å称,且ä¸åº”用作属性。以下[https://www.semantic-mediawiki.org/wiki/Help:Property_naming 帮助页é¢]å¯èƒ½åŒ…å«æœ‰å…³ä¸ºä½•è¯¥å称是ä¿ç•™å称的信æ¯ã€‚",
+ "smw-sp-property-searchform": "显示包å«ä»¥ä¸‹å†…容的属性:",
+ "smw-sp-property-searchform-inputinfo": "该输入区分大å°å†™ï¼Œå½“用于过滤,åªæ˜¾ç¤ºé‚£äº›ä¸Žæ¡ä»¶åŒ¹é…的属性。",
+ "smw-special-property-searchform": "显示包å«ä»¥ä¸‹å†…容的属性:",
+ "smw-special-property-searchform-inputinfo": "该输入区分大å°å†™ï¼Œå½“用于过滤时åªæ˜¾ç¤ºé‚£äº›ä¸Žæ¡ä»¶åŒ¹é…的属性。",
+ "smw-special-property-searchform-options": "选项",
+ "smw-special-wantedproperties-filter-label": "过滤器:",
+ "smw-special-wantedproperties-filter-none": "æ— ",
+ "smw-special-wantedproperties-filter-unapproved": "ä¸è®¤å¯",
+ "smw-special-wantedproperties-filter-unapproved-desc": "过滤用于æƒå¨æ¨¡å¼æ—¶è¿žæŽ¥çš„选项。",
+ "concepts": "概念",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts 概念]å¯ä½œä¸ºâ€œåŠ¨æ€åˆ†ç±»â€æŸ¥çœ‹ï¼Œå°±æ˜¯ä½œä¸ºä¸æ˜¯æ‰‹åŠ¨åˆ›å»ºï¼Œè€Œæ˜¯ç”±è¯­ä¹‰MediaWiki从指定查询的说明生æˆçš„页é¢æ”¶é›†ã€‚",
+ "smw-special-concept-header": "概念列表",
+ "smw-special-concept-count": "以下{{PLURAL:$1|$1个概念}}已被列出。",
+ "smw-special-concept-empty": "没有找到概念。",
+ "unusedproperties": "未使用特性",
+ "smw-unusedproperties-docu": "此页é¢åˆ—举虽然没有被其他页é¢ä½¿ç”¨ï¼Œä½†å·²å£°æ˜Žçš„[https://www.semantic-mediawiki.org/wiki/Unused_properties 未使用属性]。è¦æŸ¥çœ‹ä¸åŒè§†å›¾ï¼Œè¯·å‚è§[[Special:Properties|完整]]或[[Special:WantedProperties|需è¦çš„属性]]特殊页é¢ã€‚",
+ "smw-unusedproperty-template": "类型为$2的属性$1",
+ "wantedproperties": "需è¦çš„属性",
+ "smw-wantedproperties-docu": "此页é¢åˆ—举在wiki中使用,但没有页é¢æè¿°çš„[https://www.semantic-mediawiki.org/wiki/Wanted_properties 需è¦çš„属性]。è¦æŸ¥çœ‹ä¸åŒè§†å›¾ï¼Œè¯·å‚è§[[Special:Properties|完整]]或[[Special:UnusedProperties|未使用属性]]特殊页é¢ã€‚",
+ "smw-wantedproperty-template": "$1($2{{PLURAL:$2|次使用}})",
+ "smw-special-wantedproperties-docu": "此页é¢åˆ—举在wiki中使用,但没有页é¢æè¿°çš„[https://www.semantic-mediawiki.org/wiki/Wanted_properties 需è¦çš„属性]。è¦æŸ¥çœ‹ä¸åŒè§†å›¾ï¼Œè¯·å‚è§[[Special:Properties|完整]]或[[Special:UnusedProperties|未使用属性]]特殊页é¢ã€‚",
+ "smw-special-wantedproperties-template": "$1($2{{PLURAL:$2|次使用}})",
+ "smw_purge": "刷新",
+ "smw-purge-failed": "刷新失败",
+ "types": "类型",
+ "smw_types_docu": "[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes å¯ç”¨æ•°æ®ç±»åž‹]的列表,æ¯ç§[https://www.semantic-mediawiki.org/wiki/Help:Datatype 类型]代表唯一的属性集,以æ述存储方é¢çš„值,并显示é—传至分é…属性的特å¾ã€‚",
+ "smw-special-types-no-such-type": "指定的数æ®ç±»åž‹ä¸å­˜åœ¨",
+ "smw-statistics": "语义统计",
+ "smw-statistics-property-instance": "属性{{PLURAL:$1|值}}(总计)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|属性}}]](总计)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|属性}}(总计)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|属性}}]](使用了至少一个值)",
+ "smw-statistics-property-page": "{{PLURAL:$1|属性}}(带页é¢æ³¨å†Œï¼‰",
+ "smw-statistics-property-type": "{{PLURAL:$1|属性}}(分é…至数æ®ç±»åž‹ï¼‰",
+ "smw-statistics-query-inline-legacy": "{{PLURAL:$1|查询}}",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|查询}}]]",
+ "smw-statistics-query-size": "查询大å°",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|概念}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|概念}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|å­å¯¹è±¡}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|å­å¯¹è±¡}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|æ•°æ®ç±»åž‹}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|属性值}}([[Special:ProcessingErrorList|{{PLURAL:$1|ä¸æ­£ç¡®æ³¨é‡Š}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|属性值}}({{PLURAL:$1|ä¸æ­£ç¡®æ³¨é‡Š}})",
+ "smw-statistics-delete-count": "过时的{{PLURAL:$1|实体}}(标记删除)",
+ "smw_uri_doc": "URI 分æžå™¨å®žçŽ°[$1 W3C httpRange-14 标记查找]。它将处ç†é‚£äº›æ²¡æœ‰ç½‘站内容的页。",
+ "ask": "语义æœç´¢",
+ "smw-ask-help": "此章节包å«ä¸€äº›å¸®åŠ©è§£é‡Šå¦‚何使用<code>#ask</code>语法的链接。\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages 选择页é¢]介ç»å¦‚何选择页é¢ï¼Œå¹¶æž„造æ¡ä»¶\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators æœç´¢æ“作]列举å¯ç”¨çš„æœç´¢æ“作,包å«èŒƒå›´æŸ¥è¯¢å’Œé€šé…符查询\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information 显示信æ¯]概述打å°è¾“出声明的用法åŠæ ¼å¼åŒ–选项",
+ "smw_ask_sortby": "按列排åºï¼ˆå¯é€‰ï¼‰",
+ "smw_ask_ascorder": "å‡åº",
+ "smw_ask_descorder": "é™åº",
+ "smw-ask-order-rand": "éšæœº",
+ "smw_ask_submit": "找到结果",
+ "smw_ask_editquery": "编辑查询",
+ "smw_add_sortcondition": "[添加排åºæ¡ä»¶]",
+ "smw-ask-sort-add-action": "添加排åºæ¡ä»¶",
+ "smw_ask_hidequery": "éšè—查询",
+ "smw_ask_help": "查询帮助",
+ "smw_ask_queryhead": "æ¡ä»¶",
+ "smw_ask_printhead": "打å°è¾“出选择",
+ "smw_ask_printdesc": "(æ¯è¡Œæ·»åŠ ä¸€ä¸ªæŸ¥è¯¢å)",
+ "smw_ask_format_as": "æ ¼å¼ï¼š",
+ "smw_ask_defaultformat": "默认",
+ "smw_ask_otheroptions": "其他选项",
+ "smw-ask-otheroptions-info": "本节包å«æ”¹å˜æ‰“å°è¾“出语å¥çš„选项。å¯é€šè¿‡åœ¨å®ƒä»¬ä¸Šé¢æ‚¬åœé¼ æ ‡æ¥æŸ¥çœ‹å‚æ•°æ述。",
+ "smw-ask-otheroptions-collapsed-info": "请使用加å·å›¾æ ‡æŸ¥çœ‹æ‰€æœ‰å¯ç”¨çš„选项",
+ "smw_ask_show_embed": "显示嵌入代ç ",
+ "smw_ask_hide_embed": "éšè—嵌入的代ç ",
+ "smw_ask_embed_instr": "使用以下代ç å°†æŸ¥è¯¢åµŒå…¥ç»´åŸºé¡µé¢ã€‚",
+ "smw-ask-delete": "移除",
+ "smw-ask-sorting": "排åº",
+ "smw-ask-options": "选项",
+ "smw-ask-options-sort": "排åºé€‰é¡¹",
+ "smw-ask-format-options": "æ ¼å¼å’Œé€‰é¡¹",
+ "smw-ask-parameters": "å‚æ•°",
+ "smw-ask-search": "æœç´¢",
+ "smw-ask-debug": "调试",
+ "smw-ask-debug-desc": "生æˆæŸ¥è¯¢è°ƒè¯•ä¿¡æ¯",
+ "smw-ask-no-cache": "关闭请求缓存",
+ "smw-ask-no-cache-desc": "没有查询缓存的结果",
+ "smw-ask-result": "结果",
+ "smw-ask-empty": "清空所有记录",
+ "smw-ask-download-link-desc": "下载查询结果为$1æ ¼å¼",
+ "smw-ask-format": "æ ¼å¼",
+ "smw-ask-format-selection-help": "有关选定格å¼çš„帮助:$1",
+ "smw-ask-condition-change-info": "æ¡ä»¶å·²æ”¹å˜ï¼Œæœç´¢å¼•æ“Žéœ€è¦é‡æ–°è¿è¡ŒæŸ¥è¯¢æ¥äº§ç”ŸåŒ¹é…æ–°è¦æ±‚的结果。",
+ "smw-ask-input-assistance": "输入帮助",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 输入帮助]为打å°è¾“出ã€æŽ’åºå’Œæ¡ä»¶å­—段æ供帮助。æ¡ä»¶å­—段需è¦ä½¿ç”¨ä»¥ä¸‹å‰ç¼€ä¹‹ä¸€ï¼š",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code>以å¯ç”¨å±žæ€§å»ºè®®ï¼ˆä¾‹å¦‚<code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code>以å¯ç”¨åˆ†ç±»å»ºè®®",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code>以å¯ç”¨æ¦‚念建议",
+ "smw-ask-format-change-info": "æ ¼å¼å·²ä¿®æ”¹ï¼Œå¹¶ä¸”需è¦å†æ¬¡æ‰§è¡ŒæŸ¥è¯¢æ¥åŒ¹é…æ–°å‚æ•°å’Œå¯è§†åŒ–选项。",
+ "smw-ask-format-export-info": "选择的格å¼æ˜¯æ²¡æœ‰å¯è§†åŒ–表现的导出格å¼ï¼Œå› æ­¤ç»“æžœåªæ供下载。",
+ "smw-ask-query-search-info": "查询<code><nowiki>$1</nowiki></code>已被{{PLURAL:$3|1=<code>$2</code>(æ¥è‡ªç¼“存)|<code>$2</code>(æ¥è‡ªç¼“存)|<code>$2</code>}}在$4{{PLURAL:$4|秒}}内回应。",
+ "searchbyproperty": "按属性æœç´¢",
+ "processingerrorlist": "处ç†é”™è¯¯åˆ—表",
+ "propertylabelsimilarity": "属性标签相似性报告",
+ "smw-processingerrorlist-intro": "以下列表æ供有关出现在连接[https://www.semantic-mediawiki.org/ 语义MediaWiki]时的处ç†é”™è¯¯çš„概览。建议定期监视此列表,并修正无效的值注释。",
+ "smw_sbv_docu": "æœç´¢æ‰€æœ‰å·²è®¾å®šå±žæ€§å’Œå€¼çš„页é¢ã€‚",
+ "smw_sbv_novalue": "为此属性输入一个有效值,或者显示所有\"$1\"的属性值。",
+ "smw_sbv_displayresult": "有值为“$2â€çš„属性“$1â€çš„所有页é¢çš„列表",
+ "smw_sbv_displayresultfuzzy": "有值为\"$2\"的属性\"$1\"的所有页é¢åˆ—表。由于精确相符的页é¢ä¸å¤šï¼ŒåŒæ—¶åˆ—出了部分有相似值的页é¢ã€‚",
+ "smw_sbv_property": "属性:",
+ "smw_sbv_value": "值:",
+ "smw_sbv_submit": "找到结果",
+ "browse": "æµè§ˆç»´åŸº",
+ "smw_browselink": "æµè§ˆå±žæ€§",
+ "smw_browse_article": "请输入开始æµè§ˆçš„页é¢å称。",
+ "smw_browse_go": "æ交",
+ "smw_browse_show_incoming": "显示链入属性",
+ "smw_browse_hide_incoming": "éšè—链入属性",
+ "smw_browse_no_outgoing": "这个页é¢æ²¡æœ‰è®¾ç½®å±žæ€§ã€‚",
+ "smw_browse_no_incoming": "没有属性连接到此页。",
+ "smw-browse-from-backend": "ä¿¡æ¯å½“å‰æ­£åœ¨ä»ŽåŽç«¯å–得。",
+ "smw-browse-intro": "此页é¢æ供有关主题或实体实例的详情,请输入主题å称以检查。",
+ "smw-browse-invalid-subject": "主题验è¯è¿”回“$1â€é”™è¯¯ã€‚",
+ "smw-browse-api-subject-serialization-invalid": "主题有无效的åºåˆ—化格å¼ã€‚",
+ "smw-browse-js-disabled": "ç›®å‰æ€€ç–‘Javascript被ç¦ç”¨æˆ–ä¸å¯ç”¨ï¼Œå¹¶ä¸”我们推è使用å—支æŒçš„æµè§ˆå™¨ã€‚其他选项已在[https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>]é…ç½®å‚数页é¢è®¨è®ºã€‚",
+ "smw-browse-show-group": "显示组",
+ "smw-browse-hide-group": "éšè—组",
+ "smw-noscript": "此页é¢æˆ–æ“作需è¦Javascriptæ‰èƒ½å·¥ä½œï¼Œè¯·åœ¨æ‚¨çš„æµè§ˆå™¨ä¸­å¯ç”¨Javascript,或使用支æŒJavascriptçš„æµè§ˆå™¨ï¼Œè¿™æ ·å°±å¯ä»¥æ供相关功能,并在请求时æ供。è¦è¿›ä¸€æ­¥äº†è§£è¯¦æƒ…,请查阅[https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript]帮助页é¢ã€‚",
+ "smw_inverse_label_default": "çš„$1",
+ "smw_inverse_label_property": "逆属性标签",
+ "pageproperty": "页é¢å±žæ€§æœç´¢",
+ "smw_pp_docu": "输入页é¢å’Œå±žæ€§ï¼Œæˆ–åªè¾“入属性æ¥æ£€ç´¢æ‰€æœ‰åˆ†é…的值。",
+ "smw_pp_from": "从页é¢ï¼š",
+ "smw_pp_type": "属性:",
+ "smw_pp_submit": "找到结果",
+ "smw_result_prev": "上一个",
+ "smw_result_next": "下一个",
+ "smw_result_results": "结果",
+ "smw_result_noresults": "没有结果。",
+ "smwadmin": "管ç†å¹¶ç»´æŠ¤å‡½æ•°",
+ "smw-admin-statistics-job-title": "工作统计",
+ "smw-admin-statistics-job-docu": "工作统计显示有关已安排但尚未执行的语义MediaWiki工作的信æ¯ã€‚工作数é‡å¯èƒ½æœ‰ç‚¹ä¸å‡†ç¡®ï¼Œæˆ–包å«å¤±è´¥å°è¯•ï¼Œè¯·æŸ¥é˜…[https://www.mediawiki.org/wiki/Manual:Job_queue 手册]以获å–进一步信æ¯ã€‚",
+ "smw-admin-statistics-querycache-title": "查询缓存统计",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache 查询缓存]未在此wikiå¯ç”¨ï¼Œå¹¶å› æ­¤æ²¡æœ‰å¯ç”¨ç»Ÿè®¡ä¿¡æ¯ã€‚",
+ "smw-admin-statistics-querycache-explain": "缓存统计是包å«ä¸´æ—¶ç´¯è®¡çš„æ•°æ®åŠå…¶è¡ç”Ÿæ•°æ®ï¼Œå…¶ä¸­ï¼š\n* “missesâ€è¡¨ç¤ºå› æ— æ³•ä»Žç¼“存中获å–æ•°æ®è€Œå¯¼è‡´çš„直接存储库(如数æ®åº“ã€å…ƒç»„存储等)访问的次数\n* “deletesâ€è¡¨ç¤ºæ€»çš„缓存清除æ“作(刷新页é¢æˆ–æŸä¸€è¯·æ±‚çš„ä¾èµ–项æ“作)数é‡\n* “hitsâ€åŒ…å«äº†ä»ŽåµŒå…¥å¼æŸ¥è¯¢ï¼ˆåŒ…å«äºŽç»´åŸºé¡µé¢å†…部的查询请求)和éžåµŒå…¥å¼æŸ¥è¯¢ï¼ˆå¦‚æžœå¯ç”¨ï¼Œåˆ™è¡¨ç¤ºä½¿ç”¨è¯¸å¦‚Special:Ask页é¢å’ŒAPI进行的查询请求)æºä¸­èŽ·å–到的缓存数é‡\n* “medianRetrievalResponseTimeâ€æ˜¯å“应时间(秒)的中ä½æ•°ã€‚æ­¤å“应时间表示在结果收集的过程中,缓存的和未缓存的结果在请求时所花费的时间\n* “noCacheâ€è¡¨ç¤ºæœªå°è¯•ä»Žç¼“存中获å–结果的请求数é‡ï¼ˆlimit=0,或å¯ç”¨äº†â€œno-cacheâ€é€‰é¡¹ç­‰ï¼‰",
+ "smw-admin-permission-missing": "由于缺少æƒé™ï¼Œå¯¹æ­¤é¡µé¢çš„访问已被阻止,请查阅[https://www.semantic-mediawiki.org/wiki/Help:Permissions æƒé™]帮助页é¢ä»¥èŽ·å–有关所需设置的详情。",
+ "smw-admin-setupsuccess": "存储引擎已建立。",
+ "smw_smwadmin_return": "返回$1",
+ "smw_smwadmin_updatestarted": "更新语义数æ®è¿‡ç¨‹å¼€å§‹ã€‚所有已存储数æ®å°†è¢«æŒ‰éœ€é‡å»ºæˆ–ä¿®å¤ã€‚您å¯ä»¥é€šè¿‡æœ¬ç‰¹æ®Šé¡µé¢è·Ÿè¸ªæ›´æ–°è¿›ç¨‹ã€‚",
+ "smw_smwadmin_updatenotstarted": "已有更新进程正在è¿è¡Œã€‚请勿新建å¦ä¸€ä¸ªã€‚",
+ "smw_smwadmin_updatestopped": "å·²åœæ­¢æ‰€æœ‰æ›´æ–°è¿›ç¨‹ã€‚",
+ "smw_smwadmin_updatenotstopped": "请勾选å¤é€‰æ¡†ï¼Œä»¥ç¡®è®¤ç¡®å®žå¸Œæœ›ç»ˆæ­¢æ­£åœ¨è¿è¡Œçš„å‡çº§è¿›ç¨‹ã€‚",
+ "smw-admin-docu": "此特殊页é¢åœ¨æ‚¨å®‰è£…ã€å‡çº§ã€ç»´æŠ¤ä¸Žä½¿ç”¨<a href=\"https://www.semantic-mediawiki.org\">语义MediaWiki</a>æ—¶æ供帮助,并进一步æ供管ç†åŠŸèƒ½å’Œä»»åŠ¡ï¼Œä»¥åŠç»Ÿè®¡ã€‚请记ä½ï¼Œåœ¨æ‰§è¡Œç®¡ç†åŠŸèƒ½å‰å¤‡ä»½è´µé‡æ•°æ®ã€‚",
+ "smw-admin-environment": "软件环境",
+ "smw-admin-db": "æ•°æ®åº“设置",
+ "smw-admin-db-preparation": "表格åˆå§‹åŒ–正在进行,并在显示结果å‰å¯èƒ½æŒç»­ä¸€æ®µæ—¶é—´ï¼Œæ­£åœ¨ç­‰å¾…大å°åŠå¯èƒ½çš„表格优化。",
+ "smw-admin-dbdocu": "语义MediaWiki需è¦å¯¹MediaWikiæ•°æ®åº“åšä¸€äº›æ‰©å±•ï¼Œä»¥å­˜å‚¨è¯­ä¹‰æ•°æ®ã€‚以下功能å¯ç¡®ä¿æ‚¨çš„æ•°æ®åº“被适当é…置。这个步骤所åšçš„修改ä¸ä¼šå½±å“MediaWikiæ•°æ®åº“的其他部分,如果需è¦ï¼Œå¯ä»¥æ–¹ä¾¿åœ°æ’¤é”€æ‰€åšçš„修改。这个设置功能å¯å¤šæ¬¡æ‰§è¡Œï¼Œä¸ä¼šé€ æˆä»»ä½•å±å®³ï¼Œä½†æ˜¯åªéœ€åœ¨å®‰è£…或å‡çº§æ—¶æ‰§è¡Œä¸€æ¬¡å³å¯ã€‚",
+ "smw-admin-permissionswarn": "如果æ“作因SQL错误而失败,å¯èƒ½æ˜¯æ‚¨wikiçš„æ•°æ®åº“用户(检查您的“LocalSettings.phpâ€æ–‡ä»¶ï¼‰æ²¡æœ‰ç›¸åº”æƒé™ã€‚您å¯ä»¥å‘该用户赋予创建和删除表格的æƒé™ï¼Œæˆ–临时将数æ®åº“根用户的登录信æ¯è¾“入至您的“LocalSettings.phpâ€æ–‡ä»¶ã€‚您也å¯ä»¥ä½¿ç”¨ç»´æŠ¤è„šæœ¬<code>setupStore.php</code>,这个脚本å¯ä»¥ä½¿ç”¨ç®¡ç†å‘˜çš„ä¿¡æ¯ã€‚",
+ "smw-admin-dbbutton": "åˆå§‹åŒ–或å‡çº§è¡¨æ ¼",
+ "smw-admin-announce": "宣布您的wiki",
+ "smw-admin-announce-text": "如果您的wiki是公开的,您å¯ä»¥åœ¨<a href=\"https://wikiapiary.com\">WikiApiary</a>注册它,这是一个追踪wiki网站的wiki。",
+ "smw-admin-deprecation-notice-title": "弃用公告",
+ "smw-admin-deprecation-notice-docu": "下é¢ä¸€èŠ‚包å«å·²å¼ƒç”¨æˆ–移除,但在此wiki上å‘现被激活的设置。今åŽä»»ä½•å‘行版本都将移除对这些é…置的支æŒã€‚",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>已弃用,并将在$2中被移除",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>将移除(或替æ¢ï¼‰ä»¥ä¸‹{{PLURAL:$2|选项}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code>已弃用,并将在$2中移除",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>已被替æ¢ä¸º<code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>{{PLURAL:$2|选项}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code>正在替æ¢ä¸º<code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>已在$2中被移除",
+ "smw-admin-deprecation-notice-title-notice": "å³å°†è¿›è¡Œçš„更改",
+ "smw-admin-deprecation-notice-title-notice-explanation": "现已检测到该wiki使用了以下设置,并计划在今åŽçš„å‘布版本中移除或更改。",
+ "smw-admin-deprecation-notice-title-replacement": "已替æ¢æˆ–é‡å‘½å的设置",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "以下包å«å·²é‡å‘½å或有å¦å¤–修改的设置,并建议立刻更新其å称或格å¼ã€‚",
+ "smw-admin-deprecation-notice-title-removal": "已移除的设置",
+ "smw-admin-deprecation-notice-title-removal-explanation": "列出的设置已在上一个å‘布版本中移除,但在该wiki上检测到使用。",
+ "smw-smwadmin-refresh-title": "æ•°æ®ä¿®å¤ä¸Žæ›´æ–°",
+ "smw_smwadmin_datarefresh": "æ•°æ®é‡å»º",
+ "smw_smwadmin_datarefreshdocu": "å¯ä»¥åŸºäºŽç»´åŸºå…ˆæœ‰å†…容对所有语义MediaWkiæ•°æ®è¿›è¡Œé‡ç½®ã€‚这个功能对修å¤æŸåçš„æ•°æ®ï¼Œæˆ–者在软件å‡çº§ï¼Œå†…部格å¼æ”¹å˜æ—¶æ›´æ–°æ•°æ®éžå¸¸æœ‰ç”¨ã€‚æ›´æ–°æ—¶é€é¡µé¢æ‰§è¡Œçš„,需è¦ä¸€æ®µæ—¶é—´ã€‚下方将显示更新是å¦æ­£åœ¨è¿›è¡Œï¼Œå¹¶å…许你开始或终止更新。(此功能å¯ä»¥è¢«ç½‘站管ç†å‘˜ç¦ç”¨ï¼‰",
+ "smw_smwadmin_datarefreshprogress": "<strong>有一个更新正在进行。</strong>更新进程å¯èƒ½ä¼šå¾ˆæ…¢ï¼Œå› ä¸ºè¯¥è¿›ç¨‹ä»…在用户访问维基时å°å—地更新数æ®ã€‚å¯ä»¥ä½¿ç”¨MediaWiki维护脚本<code>runJobs.php</code>(使用选项 <code>--maxjobs 1000</code> é™åˆ¶æ¯æ‰¹æ›´æ–°æ•°ç›®ï¼‰æ¥åŠ å¿«æ›´æ–°å®Œæˆé€Ÿåº¦ã€‚ç›®å‰æ›´æ–°çš„估计进度:",
+ "smw_smwadmin_datarefreshbutton": "计划数æ®é‡å»º",
+ "smw_smwadmin_datarefreshstop": "åœæ­¢æ›´æ–°",
+ "smw_smwadmin_datarefreshstopconfirm": "是的,我{{GENDER:$1|确认}}。",
+ "smw-admin-job-scheduler-note": "本节内的多数活动都作为作业执行,以é¿å…在其执行期间å‘生åœé¡¿çŠ¶å†µã€‚[https://www.mediawiki.org/wiki/Manual:Job_queue 作业调度器]负责加工,并且é‡è¦çš„是维护脚本<code>runJobs.php</code>(å‚è§é…ç½®å‚æ•°<code>$wgRunJobsAsync</code>)有适当能力。",
+ "smw-admin-outdateddisposal-title": "过时实体处置",
+ "smw-admin-outdateddisposal-intro": "部分活动(对属性类型的更改,wiki页é¢çš„移除,或错误值的纠正)将创建[https://www.semantic-mediawiki.org/wiki/Outdated_entities 过时实体],并建议定期移除它们以释放相关表空间。",
+ "smw-admin-outdateddisposal-active": "过时实体处置工作已列入计划。",
+ "smw-admin-outdateddisposal-button": "计划处置",
+ "smw-admin-feature-disabled": "该功能已在此wikiç¦ç”¨ã€‚请查阅<a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">设置</a>帮助页é¢æˆ–è”络系统管ç†å‘˜ã€‚",
+ "smw-admin-propertystatistics-title": "属性统计é‡å»º",
+ "smw-admin-propertystatistics-intro": "é‡å»ºæ•´ä¸ªå±žæ€§ä½¿ç”¨ç»Ÿè®¡ä»¥åŠå…¶ä¸­çš„更新,并更正属性[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count 使用计数]。",
+ "smw-admin-propertystatistics-active": "属性统计é‡å»ºå·¥ä½œå·²åˆ—入计划。",
+ "smw-admin-propertystatistics-button": "计划统计é‡å»º",
+ "smw-admin-fulltext-title": "全文æœç´¢é‡å»º",
+ "smw-admin-fulltext-intro": "从属性表中使用å¯ç”¨çš„[https://www.semantic-mediawiki.org/wiki/Full-text 全文æœç´¢]æ•°æ®ç±»åž‹é‡å»ºæœç´¢ç´¢å¼•ã€‚对索引规则的更改(改å˜çš„åœç”¨è¯ã€æ–°çš„è¯å¹²ç­‰ï¼‰å’Œ/或新添加或改å˜çš„表格需è¦é‡æ–°è¿è¡Œæ­¤å·¥ä½œã€‚",
+ "smw-admin-fulltext-active": "全文æœç´¢é‡å»ºå·¥ä½œå·²åˆ—入计划。",
+ "smw-admin-fulltext-button": "计划全文é‡å»º",
+ "smw-admin-support": "获å–支æŒ",
+ "smw-admin-supportdocu": "å·²æä¾›å„ç§èµ„æºæ¥å¸®åŠ©æ‚¨è§£å†³ä»¥ä¸‹é—®é¢˜ï¼š",
+ "smw-admin-installfile": "如果您安装时é‡åˆ°é—®é¢˜ï¼Œè¯·å…ˆæ£€æŸ¥<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">安装文件</a>åŠ<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">安装页é¢</a>中的指引。",
+ "smw-admin-smwhomepage": "语义MediaWiki的完整用户文档请查阅<b><a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_-_%E4%B8%BB%E9%A1%B5_(zh-hans)\">semantic-mediawiki.org</a></b>。",
+ "smw-admin-bugsreport": "程åºé—®é¢˜å¯åœ¨<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">问题追踪器</a>报告,<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">报告错误</a>页é¢æ供一些如何书写高效问题报告的指导。",
+ "smw-admin-questions": "如果有问题或建议,å¯åŠ å…¥è¯­ä¹‰MediaWiki<a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">用户邮件列表</a>,或是<a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">èŠå¤©å®¤</a>的讨论。",
+ "smw-admin-other-functions": "其他函数",
+ "smw-admin-supplementary-section-title": "补充函数",
+ "smw-admin-supplementary-section-subtitle": "å¯ç”¨å‡½æ•°",
+ "smw-admin-supplementary-section-intro": "此章节包å«è¶…过维护范围的其他函数,[https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions documentation]列举的部分函数å¯èƒ½å—é™åˆ¶æˆ–ä¸å¯è®¿é—®ï¼Œå¹¶å› æ­¤åœ¨æ­¤wikiä¸å¯ä½¿ç”¨ã€‚",
+ "smw-admin-supplementary-settings-title": "é…置设置",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u>输出用于语义MediaWikiçš„å¯ç”¨è®¾ç½®çš„集åˆåˆ—表",
+ "smw-admin-supplementary-operational-statistics-title": "æ“作统计",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u>显示扩充的统计集åˆ",
+ "smw-admin-supplementary-idlookup-title": "实体查找与处置",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u>包å«æŸ¥æ‰¾å¹¶å¤„置个别实体的函数",
+ "smw-admin-supplementary-duplookup-title": "é‡å¤è®°å½•",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u>列举在实体表中,包å«é‡å¤é¡¹çš„记录",
+ "smw-admin-supplementary-duplookup-docu": "此页é¢åˆ—举æ¥è‡ª[https://www.semantic-mediawiki.org/wiki/Help:Entity_table 实体表]的记录,他们被归类为é‡å¤é¡¹ã€‚é‡å¤è®°å½•ï¼ˆå¦‚有)åªåº”在罕è§åœºåˆå‡ºçŽ°ï¼Œè¿™å¯èƒ½ç”±æ•°æ®åº“更新期间的结æŸè¿‡ç¨‹ï¼Œæˆ–是未æˆåŠŸçš„回退事宜导致。",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "缓存统计",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u>显示缓存相关的统计",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u>告知有关设置和索引统计",
+ "smw-admin-supplementary-elastic-docu": "此页é¢åŒ…å«æœ‰å…³Elasticsearch集群相关的设置ã€æ˜ å°„ã€å¥åº·å’Œç´¢å¼•ç»Ÿè®¡ä¿¡æ¯ï¼Œè¯¥é›†ç¾¤è¿žæŽ¥åˆ°è¯­ä¹‰MediaWikiåŠå…¶[https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>]。",
+ "smw-admin-supplementary-elastic-functions": "支æŒçš„功能",
+ "smw-admin-supplementary-elastic-settings-title": "设置",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u>ç”±Elasticsearch使用以管ç†è¯­ä¹‰MediaWiki指数",
+ "smw-admin-supplementary-elastic-mappings-title": "映射",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u>以列举指数和字段映射",
+ "smw-admin-supplementary-elastic-mappings-docu": "此页é¢åŒ…å«ä¸Žå½“å‰ç´¢å¼•ä¸€èµ·ä½¿ç”¨çš„字段映射详细信æ¯ã€‚应结åˆ<code> index.mapping.total_fields.limit</code>监视映射摘è¦ï¼Œè¯¥ç´¢å¼•æŒ‡å®šç´¢å¼•ä¸­çš„最大字段数。",
+ "smw-admin-supplementary-elastic-mappings-summary": "摘è¦",
+ "smw-admin-supplementary-elastic-mappings-fields": "字段映射",
+ "smw-admin-supplementary-elastic-nodes-title": "节点",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u>显示节点统计",
+ "smw-admin-supplementary-elastic-indices-title": "指数",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u>æä¾›å¯ç”¨æŒ‡æ•°åŠå…¶ç»Ÿè®¡çš„概览",
+ "smw-admin-supplementary-elastic-statistics-title": "统计",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u>显示索引级统计",
+ "smw-admin-supplementary-elastic-statistics-docu": "此页é¢æ供用于ä¸åŒæ“作(å‘生在索引级)的指数统计上的洞察能力,返回的统计与åˆé€‰å†…容和总计统计èšåˆã€‚[https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html 帮助页é¢]包å«è¯¦ç»†çš„å¯ç”¨æŒ‡æ•°ç»Ÿè®¡æ述。",
+ "smw-admin-supplementary-elastic-status-replication": "å¤åˆ¶çŠ¶æ€",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "最近一次活动å¤åˆ¶ï¼š$1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "刷新间隔:$1",
+ "smw-list-count": "列表包å«$1个{{PLURAL:$1|实体}}。",
+ "smw-list-count-from-cache": "列表包å«$1个{{PLURAL:$1|实体}},并æ¢å¤è‡ªç¼“存(UTC:$2)。",
+ "smw-property-label-uniqueness": "“$1â€æ ‡ç­¾åŒ¹é…了至少一个其他属性表现。请查阅[https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness 帮助页é¢]了解如何解决该问题。",
+ "smw-property-label-similarity-title": "属性标签相似性报告",
+ "smw-property-label-similarity-intro": "<u>$1</u>å¯å¯¹çŽ°æœ‰å±žæ€§æ ‡ç­¾çš„相似性进行计算",
+ "smw-property-label-similarity-threshold": "阈值:",
+ "smw-property-label-similarity-type": "显示类型ID",
+ "smw-property-label-similarity-noresult": "找ä¸åˆ°ç¬¦åˆå·²é€‰å®šé€‰é¡¹çš„结果。",
+ "smw-property-label-similarity-docu": "在两个å¯å¸®åŠ©è¿‡æ»¤æ‹¼å†™é”™è¯¯æˆ–的属性标签,或是在æ供相åŒæ¦‚念的等价属性(å‚è§ç‰¹æ®Šé¡µé¢[[Special:Properties|属性]]以é˜æ˜ŽæŠ¥å‘Šå±žæ€§çš„概念和用法)之间,比较并报告[https://www.semantic-mediawiki.org/wiki/Property_similarity 语法相似性](ä¸æ˜¯è¯­ä¹‰ç›¸ä¼¼æ€§ï¼‰ã€‚å¯ä»¥é€šè¿‡è°ƒæ•´é˜€å€¼æ¥è°ƒæ•´ç›¸ä¼¼æ€§è·ç¦»çš„宽窄。<code>[[Property:$1|$1]]</code>被用于从分æžä¸­æŽ’除属性。",
+ "smw-admin-operational-statistics": "此页é¢åŒ…å«åœ¨è¯­ä¹‰MediaWiki相关函数中(或æ¥è‡ªå…¶ç›¸å…³å‡½æ•°ï¼‰æ”¶é›†çš„æ“作统计。wiki特定统计的扩展列表å¯[[Special:Statistics|<b>在此</b>]]找到。",
+ "smw_adminlinks_datastructure": "æ•°æ®ç»“æž„",
+ "smw_adminlinks_displayingdata": "æ•°æ®æ˜¾ç¤º",
+ "smw_adminlinks_inlinequerieshelp": "直接æ’å…¥å¼æŸ¥è¯¢å¸®åŠ©",
+ "smw-page-indicator-usage-count": "估计[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count 使用计数]:{{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|用户|系统}}定义的属性",
+ "smw-property-indicator-last-count-update": "估计使用数 上次更新于:$1",
+ "smw-concept-indicator-cache-update": "缓存数\n\n上次更新:$1",
+ "smw-createproperty-isproperty": "它是一个类型为$1的属性。",
+ "smw-createproperty-allowedvals": "此属性å…许的{{PLURAL:$1|值为}}:",
+ "smw-paramdesc-category-delim": "分隔符",
+ "smw-paramdesc-category-template": "æ ¼å¼åŒ–这些æ¡ç›®çš„模æ¿",
+ "smw-paramdesc-category-userparam": "传递给模æ¿çš„å‚æ•°",
+ "smw-info-par-message": "所è¦æ˜¾ç¤ºçš„ä¿¡æ¯ã€‚",
+ "smw-info-par-icon": "所è¦æ˜¾ç¤ºçš„图标,“infoâ€æˆ–“warningâ€ã€‚",
+ "prefs-smw": "语义MediaWiki",
+ "prefs-general-options": "常规选项",
+ "prefs-ask-options": "Special:Ask选项",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ 语义MediaWiki](åŠç›¸å…³æ‰©å±•ï¼‰ä¸ºä¸€äº›é€‰å®šå‡½æ•°æä¾›å„自的定制功能。请å‚阅[https://www.semantic-mediawiki.org/wiki/Help:User_preferences 帮助页é¢]以获å–详细说明。",
+ "smw-prefs-ask-options-tooltip-display": "显示å‚数文本为信æ¯æ示",
+ "smw-prefs-general-options-time-correction": "使用本地[[Special:Preferences#mw-prefsection-rendering|时间å移]]å‚数设置,为特殊页é¢å¯ç”¨æ—¶é—´ä¿®æ­£",
+ "smw-prefs-general-options-jobqueue-watchlist": "在我的个人工具æ ä¸­æ˜¾ç¤º[https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist 工作队列监视列表]",
+ "smw-prefs-general-options-disable-editpage-info": "ç¦ç”¨ç¼–辑页é¢ä¸Šçš„介ç»æ–‡æœ¬",
+ "smw-prefs-general-options-disable-search-info": "在标准æœç´¢é¡µé¢ä¸Šç¦ç”¨è¯­æ³•æ”¯æŒä¿¡æ¯",
+ "smw-prefs-general-options-suggester-textinput": "为语义实体建议å¯ç”¨[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 输入帮助]",
+ "smw-ui-tooltip-title-property": "属性",
+ "smw-ui-tooltip-title-quantity": "å•ä½è½¬æ¢",
+ "smw-ui-tooltip-title-info": "ä¿¡æ¯",
+ "smw-ui-tooltip-title-service": "æœåŠ¡è¿žæŽ¥",
+ "smw-ui-tooltip-title-warning": "警告",
+ "smw-ui-tooltip-title-error": "错误",
+ "smw-ui-tooltip-title-parameter": "å‚æ•°",
+ "smw-ui-tooltip-title-event": "事件",
+ "smw-ui-tooltip-title-note": "注释",
+ "smw-ui-tooltip-title-legend": "图例",
+ "smw-ui-tooltip-title-reference": "å‚考文献",
+ "smw_unknowntype": "该属性的类型无效",
+ "smw-concept-cache-text": "概念共计拥有{{PLURAL:$1|页é¢}}$1个,并于$2 $3更新。",
+ "smw_concept_header": "概念\"$1\"的页é¢",
+ "smw_conceptarticlecount": "显示属于此概念的$1个页é¢ã€‚",
+ "smw-qp-empty-data": "选择标准ä¸å……分,请求数æ®æ— æ³•æ˜¾ç¤ºã€‚",
+ "right-smw-admin": "连接管ç†å‘˜å·¥ä½œç»„(语义MediaWiki)",
+ "right-smw-patternedit": "维护å…许的正则表达å¼å’Œæ¨¡å¼çš„编辑访问æƒï¼ˆè¯­ä¹‰MediaWiki)",
+ "right-smw-pageedit": "用于带<code>Is edit protected</code>注释页é¢çš„编辑访问æƒï¼ˆè¯­ä¹‰MediaWiki)",
+ "right-smw-ruleedit": "编辑规则页é¢ï¼ˆè¯­ä¹‰MediaWiki)",
+ "restriction-level-smw-pageedit": "å·²ä¿æŠ¤ï¼ˆä»…é™æœ‰èµ„格的用户)",
+ "action-smw-patternedit": "编辑由语义MediaWiki使用的正则表达å¼",
+ "action-smw-pageedit": "编辑带注释<code>Is edit protected</code>的页é¢ï¼ˆè¯­ä¹‰MediaWiki)",
+ "group-smwadministrator": "管ç†å‘˜ï¼ˆè¯­ä¹‰MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|管ç†å‘˜ï¼ˆè¯­ä¹‰MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:管ç†å‘˜ï¼ˆè¯­ä¹‰MediaWiki)",
+ "group-smwcurator": "监护人(语义MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|监护人(语义MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:监护人(语义MediaWiki)",
+ "action-smw-admin": "连接语义MediaWiki管ç†å‘˜å·¥ä½œç»„",
+ "action-smw-ruleedit": "编辑规则页é¢ï¼ˆè¯­ä¹‰MediaWiki)",
+ "smw-property-predefined-default": "“$1â€æ˜¯é¢„定义属性。",
+ "smw-property-predefined-common": "此属性预先部署(或称[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 特殊属性]),并带有é¢å¤–的管ç†æƒé™ï¼Œä½†å¯åƒå…¶ä»–[https://www.semantic-mediawiki.org/wiki/Property 用户定义属性]一样使用。",
+ "smw-property-predefined-ask": "“$1â€æ˜¯è¡¨çŽ°æœ‰å…³ä¸ªåˆ«æŸ¥è¯¢ä¸­åª’体信æ¯ï¼ˆ[https://www.semantic-mediawiki.org/wiki/Subobject å­å¯¹è±¡]å½¢å¼ï¼‰çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-asksi": "“$1â€æ˜¯æ”¶é›†ä¸€æ¬¡æŸ¥è¯¢ä¸­ä½¿ç”¨åˆ°çš„æ¡ä»¶æ•°çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-askde": "“$1â€æ˜¯æŠ¥å‘Šæœ‰å…³æŸ¥è¯¢æ·±åº¦çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-long-askde": "它是在å­æŸ¥è¯¢åµŒå¥—ã€å±žæ€§é“¾å’Œå¯ç”¨æ述元素的基础上,通过执行å—<code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code>é…ç½®å‚æ•°é™åˆ¶çš„查询计算出的数值。",
+ "smw-property-predefined-askpa": "“$1â€æ˜¯ä¸€ä¸ªæè¿°å½±å“查询结果å‚数的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-long-askpa": "这是属性集åˆçš„一部分,它指定[https://www.semantic-mediawiki.org/wiki/Help:Query_profiler 查询详情]。",
+ "smw-sp-properties-docu": "这个页é¢åˆ—举此wiki上å¯ç”¨çš„[https://www.semantic-mediawiki.org/wiki/Property 属性]åŠå…¶ä½¿ç”¨è®¡æ•°ã€‚为确ä¿èŽ·å¾—最新的计数统计信æ¯ï¼Œæˆ‘们建议您定期è¿è¡Œ[https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics 属性统计]维护脚本。差异化的视图,请å‚阅[[Special:UnusedProperties|未使用的属性]]或[[Special:WantedProperties|缺ä¹å±žæ€§]]特殊页é¢ã€‚",
+ "smw-sp-properties-cache-info": "列出数æ®å·²ä»Ž[https://www.semantic-mediawiki.org/wiki/Caching 缓存]检索,并已更新于$1。",
+ "smw-sp-properties-header-label": "属性列表",
+ "smw-admin-settings-docu": "显示所有与语义MediaWiki环境相关的默认和本地化设置的列表。关于具体设置的详细信æ¯ï¼Œè¯·æŸ¥é˜…[https://www.semantic-mediawiki.org/wiki/Help:Configuration é…ç½®]帮助页é¢ã€‚",
+ "smw-sp-admin-settings-button": "生æˆè®¾ç½®åˆ—表",
+ "smw-admin-idlookup-title": "查找",
+ "smw-admin-idlookup-docu": "此节显示语义MediaWiki中具体实体(wiki页é¢ã€å­å¯¹è±¡ã€å±žæ€§ç­‰ï¼‰çš„技术详情。输入å…许作为ID或字符串æ¥åŒ¹é…选定字段。注æ„任何ID引用ä¸åº”错误作为MediaWiki页é¢æˆ–修订版本ID。",
+ "smw-admin-iddispose-title": "处置",
+ "smw-admin-iddispose-docu": "请注æ„,处ç†æ“作ä¸å—é™åˆ¶ï¼Œä¸€æ—¦ç¡®è®¤ï¼Œå­˜å‚¨å¼•æ“Žä¸­çš„所有数æ®å’ŒæŒ‚起的请求将全部被删除。请'''æ…Žé‡'''执行此任务,并åªåœ¨æŸ¥é˜…[https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal 文档]åŽè¿›è¡Œã€‚",
+ "smw-admin-iddispose-done": "ID“$1â€å·²ä»Žå­˜å‚¨åŽç«¯ä¸­ç§»é™¤ã€‚",
+ "smw-admin-iddispose-references": "ID“$1â€{{PLURAL:$2|没有|有至少一个}}活跃的å‚考资料:",
+ "smw-admin-iddispose-references-multiple": "有至少一个活动å‚考记录的匹é…列表。",
+ "smw-admin-iddispose-no-references": "æœç´¢æ²¡æœ‰æ‰¾åˆ°åŒ¹é…“$1â€çš„表æ¡ç›®ã€‚",
+ "smw-admin-idlookup-input": "æœç´¢ï¼š",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "概览",
+ "smw-admin-tab-notices": "弃用通告",
+ "smw-admin-tab-supplement": "补充函数",
+ "smw-admin-tab-registry": "注册",
+ "smw-admin-maintenance-no-description": "æ— æè¿°",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "é‡æ–°ç”Ÿæˆæ‰€æœ‰å±žæ€§å®žä½“的使用统计信æ¯ã€‚",
+ "smw-livepreview-loading": "正在载入...",
+ "smw-sp-searchbyproperty-description": "此页é¢æ供一个简å•çš„[https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces æµè§ˆç•Œé¢]用于å‘现一个属性和一个命å值定义的实体。其他å¯ç”¨æœç´¢ç•Œé¢åŒ…括[[Special:PageProperty|页é¢å±žæ€§æœç´¢]]å’Œ[[Special:Ask|询问查询生æˆå™¨]]。",
+ "smw-sp-searchbyproperty-resultlist-header": "结果列表",
+ "smw-sp-searchbyproperty-nonvaluequery": "分é…属性“$1â€çš„值列表。",
+ "smw-sp-searchbyproperty-valuequery": "注释有属性“$1â€å¸¦å€¼â€œ$2â€çš„页é¢åˆ—表。",
+ "smw-datavalue-number-textnotallowed": "“$1â€ä¸èƒ½åˆ†é…给公开的带值$2的数字类型。",
+ "smw-datavalue-number-nullnotallowed": "“$1â€è¿”回了“NULLâ€ï¼Œå®ƒä¸å…许作为数值。",
+ "smw-editpage-annotation-enabled": "此页é¢æ”¯æŒè¯­ä¹‰æ–‡å†…注释(例如<nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>)以构造由语义MediaWikiæ供的结构化和å¯æŸ¥è¯¢å†…容。有关如何使用释文或#ask 解æžå™¨å‡½æ•°çš„综åˆä»‹ç»ï¼Œè¯·æŸ¥é˜…[https://www.semantic-mediawiki.org/wiki/Help:Getting_started 入门]ã€[https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation 文内注释]或[https://www.semantic-mediawiki.org/wiki/Help:Inline_queries 行内查询]帮助页é¢ã€‚",
+ "smw-editpage-annotation-disabled": "由于å字空间é™åˆ¶ï¼Œæ­¤é¡µé¢åœ¨è¯­ä¹‰æ–‡å†…注释中ä¸ä¼šå¯ç”¨ã€‚有关如何å¯ç”¨å字空间的详细信æ¯å¯åœ¨[https://www.semantic-mediawiki.org/wiki/Help:é…ç½® é…ç½®]帮助页é¢æ‰¾åˆ°ã€‚",
+ "smw-editpage-property-annotation-enabled": "此属性å¯é€šè¿‡è¯­ä¹‰é‡Šæ–‡æ‰©å……以指定一个数æ®ç±»åž‹ï¼ˆä¾‹å¦‚<nowiki>\"[[Has type::Page]]\"</nowiki>)或其他支æŒçš„声明(例如<nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>)。有关如何增大此页é¢çš„说明,å‚è§[https://www.semantic-mediawiki.org/wiki/Help:Property_declaration 属性宣告]或[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes å¯ç”¨æ•°æ®ç±»åž‹åˆ—表]帮助页é¢ã€‚",
+ "smw-editpage-property-annotation-disabled": "由于此属性已ç»è¢«é¢„先定义,它ä¸èƒ½é€šè¿‡ä¸€ä¸ªæ•°æ®ç±»åž‹æ³¨é‡Šï¼ˆä¾‹å¦‚<nowiki>\"[[Has type::Page]]\"</nowiki>)延伸(å‚è§[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 特殊属性]帮助页é¢ä»¥èŽ·å–更多信æ¯ï¼‰ã€‚",
+ "smw-editpage-concept-annotation-enabled": "此概念å¯ä»¥é€šè¿‡#concept解æžå™¨å‡½æ•°æ‰©å……。有关如何使用#concept的说明,å‚è§[https://www.semantic-mediawiki.org/wiki/Help:Concepts 概念]帮助页é¢ã€‚",
+ "smw-search-syntax-support": "æœç´¢è¾“入支æŒä½¿ç”¨è¯­ä¹‰[https://www.semantic-mediawiki.org/wiki/Help:Semantic_search 查询语法]以帮助使用语义MediaWiki匹é…结果。",
+ "smw-search-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 输入助手]也已å¯ç”¨ï¼Œä»¥å‡è½»å¯ç”¨å±žæ€§å’Œåˆ†ç±»çš„预选择难度。",
+ "smw-search-help-intro": "<code><nowiki>[[ ... ]]</nowiki></code>输入会将信å·å‘é€è‡³è¾“入处ç†å™¨ï¼Œä»¥ä½¿ç”¨è¯­ä¹‰MediaWikiæœç´¢åŽç«¯ã€‚需è¦æ³¨æ„ä¸æ”¯æŒå°†<code><nowiki>[[ ... ]]</nowiki></code>与éžç»“构化文本æœç´¢ï¼ˆä¾‹å¦‚<code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code>)结åˆã€‚",
+ "smw-search-help-structured": "结构化æœç´¢ï¼š\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>ã€<code><nowiki>[[Has number::123]]</nowiki></code>(作为[https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context 已过滤环境])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code>(带[https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context 查询环境])",
+ "smw-search-help-proximity": "接近æœç´¢ï¼ˆæœªçŸ¥çš„属性,'''åª'''对这些æ供全文æœç´¢é›†æˆçš„åŽç«¯å¯ç”¨ï¼‰ï¼š\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code>(在整个文档中æœç´¢å·²ç´¢å¼•çš„“loremâ€å’Œâ€œipsumâ€ï¼‰\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code>(匹é…“lorem ipsumâ€ä¸ºè¯ç»„)",
+ "smw-search-help-ask": "以下链接将解释如何使用<code>#ask</code>语法。\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages 选择页é¢]æ述如何选择页é¢ï¼Œå¹¶æž„建æ¡ä»¶\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators æœç´¢è¿ç®—符]列举了å¯ç”¨çš„æœç´¢è¿ç®—符,包括用于范围和通é…符查询的è¿ç®—符",
+ "smw-search-input": "输入和æœç´¢",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 输入帮助]为输入字段æ供,并需è¦ä½¿ç”¨ä»¥ä¸‹å‰ç¼€ä¹‹ä¸€ï¼š\n\n*<code>p:</code>以å¯ç”¨å±žæ€§å»ºè®®ï¼ˆä¾‹å¦‚<code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code>以å¯ç”¨åˆ†ç±»å»ºè®®\n\n*<code>con:</code>以å¯ç”¨æ¦‚念建议",
+ "smw-search-syntax": "语法",
+ "smw-search-profile": "已扩充",
+ "smw-search-profile-tooltip": "æœç´¢åŠŸèƒ½ä¸Žè¯­ä¹‰MediaWiki有关",
+ "smw-search-profile-sort-best": "最佳匹é…",
+ "smw-search-profile-sort-recent": "最新",
+ "smw-search-profile-sort-title": "标题",
+ "smw-search-profile-extended-help-intro": "\n[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile 扩展é…ç½®] æ供了Semantic MediaWiki 中æ¥è‡ªäºŽSpecial:Search 页é¢çš„扩展é…置选项并包å«:",
+ "smw-search-profile-extended-help-sort": "指定结果排åºé€‰é¡¹ï¼š",
+ "smw-search-profile-extended-help-sort-best": "*\"最佳匹é…\" 将会根æ®Elasticsearchæ供的 [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy å…³è”]分数排åºç»“果。",
+ "smw-search-profile-extended-help-form": "表å•ä¾›ç”¨æˆ·ä½¿ç”¨ä»¥åŒ¹é…特定用例,其å¯ä»¥å€ŸåŠ©ä¸åŒçš„属性和值输入字段,以便用户轻æ¾å¤„ç†æœç´¢è¯·æ±‚。(è§$1)",
+ "smw-search-profile-extended-help-namespace": "一旦选择了表å•ï¼Œå°±ä¼šéšè—å称空间选择框,但å¯ä»¥å€ŸåŠ©â€œæ˜¾ç¤º/éšè—â€æŒ‰é’®ä½¿å…¶å¯è§ã€‚",
+ "smw-search-profile-extended-help-query": "å…³è”到:$1",
+ "smw-search-profile-extended-help-query-link": "(查看$1的详情)",
+ "smw-search-profile-extended-help-find-forms": "查找表格",
+ "smw-search-profile-extended-section-sort": "排åºæ–¹å¼ï¼š",
+ "smw-search-profile-extended-section-form": "表å•",
+ "smw-search-profile-extended-section-search-syntax": "æœç´¢è¾“入字段",
+ "smw-search-profile-extended-section-namespace": "å字空间",
+ "smw-search-profile-extended-section-query": "查询",
+ "smw-search-profile-link-caption-query": "请å‚阅",
+ "smw-search-show": "显示",
+ "smw-search-hide": "éšè—",
+ "log-name-smw": "语义MediaWiki日志",
+ "log-show-hide-smw": "$1语义MediaWiki日志",
+ "logeventslist-smw-log": "语义MediaWiki日志",
+ "log-description-smw": "为[https://www.semantic-mediawiki.org/wiki/Help:Logging å·²å¯ç”¨çš„活动类型]激活,它们已ç»è¢«è¯­ä¹‰MediaWikiåŠå…¶ç»„件汇报。",
+ "logentry-smw-maintenance": "由语义MediaWiki放出的维护相关日志",
+ "smw-datavalue-import-unknown-namespace": "导入å字空间“$1â€æœªçŸ¥ã€‚请确信OWL导入详细信æ¯å­˜åœ¨äºŽ[[MediaWiki:Smw import $1]]",
+ "smw-datavalue-import-missing-namespace-uri": "无法在[[MediaWiki:Smw import $1|$1的导入]]中找到“$1â€å字空间的URI。",
+ "smw-datavalue-import-missing-type": "在[[MediaWiki:Smw import $2|$2导出]]中找ä¸åˆ°ç”¨äºŽâ€œ$1â€ç±»åž‹å®šä¹‰ã€‚",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1次导入]]",
+ "smw-datavalue-import-invalid-value": "“$1â€ä¸æ˜¯ä¸€ä¸ªæœ‰æ•ˆæ ¼å¼ï¼Œå¹¶é¢„计包å«â€œå字空间â€ï¼šâ€œæ ‡è¯†ç¬¦â€ï¼ˆä¾‹å¦‚:“foaf:nameâ€ï¼‰ã€‚",
+ "smw-datavalue-import-invalid-format": "字符串“$1â€åº”被分æˆå››éƒ¨åˆ†ï¼Œä½†æ ¼å¼æœªèƒ½ç†è§£ã€‚",
+ "smw-property-predefined-impo": "“$1â€æ˜¯æ述与[https://www.semantic-mediawiki.org/wiki/Help:Import_vocabulary 导入è¯æ±‡]间关系的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-type": "“$1â€æ˜¯ç”¨æ¥æ述属性[[Special:Types|æ•°æ®ç±»åž‹]]的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-sobj": "“$1â€æ˜¯ä»£è¡¨[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]结构的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-long-sobj": "容器å…许积累与普通wiki页é¢ç±»ä¼¼ï¼Œä½†å½“链接至嵌入主题时在ä¸åŒå®žä½“空间中的属性值分é…。",
+ "smw-property-predefined-errp": "“$1â€æ˜¯ä¸€ä¸ªè¿½è¸ªä¸è§„则值释文的输入错误的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-long-errp": "在多数情况下,这是由于类型ä¸åŒ¹é…,或[[Property:Allows value|值]]é™åˆ¶å¯¼è‡´ã€‚",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value “$1â€]是一个预定义的属性,它å¯ä»¥è®¢é˜…获得å…许以为一个属性é™åˆ¶å€¼æ³¨é‡Šçš„值的列表,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list “$1â€]是一个预定义的属性,它å¯ä»¥æŒ‡å®šå‚考文献至ä¿ç•™å…许值的列表,以é™åˆ¶æŸä¸ªå±žæ€§çš„值分é…,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-datavalue-property-restricted-annotation-use": "属性“$1â€æœ‰å—é™åˆ¶åº”用区域,并且ä¸èƒ½ç”±ç”¨æˆ·ä½¿ç”¨ä¸ºé‡Šæ–‡å±žæ€§ã€‚",
+ "smw-datavalue-property-restricted-declarative-use": "属性“$1â€æ˜¯é™ˆè¿°æ€§å±žæ€§ï¼Œå¹¶åªèƒ½åœ¨å±žæ€§æˆ–分类页é¢ä½¿ç”¨ã€‚",
+ "smw-datavalue-property-create-restriction": "属性“$1â€ä¸å­˜åœ¨ï¼Œä¸”用户因缺少“$2â€æƒé™ä¸èƒ½åˆ›å»ºè¯¥å±žæ€§ï¼ˆå‚è§[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode æƒå¨æ¨¡å¼]),或将值注释在未获认å¯çš„属性中。",
+ "smw-datavalue-property-invalid-character": "“$1â€åŒ…å«ä½œä¸ºå±žæ€§æ ‡ç­¾ä¸€éƒ¨åˆ†åˆ—举的“$2â€å­—符,并因此被归类为无效。",
+ "smw-datavalue-property-invalid-chain": "ä¸å…许在注释过程中使用“$1â€ä½œä¸ºå±žæ€§é“¾ã€‚",
+ "smw-datavalue-restricted-use": "æ•°æ®å€¼â€œ$1â€è¢«æ ‡è®°ä¸ºé™åˆ¶ä½¿ç”¨ã€‚",
+ "smw-datavalue-invalid-number": "“$1â€ä¸èƒ½è¢«è§£é‡Šä¸ºä¸€ä¸ªæ•°å­—。",
+ "smw-query-condition-circular": "在“$1â€ä¸­æ£€æµ‹åˆ°å¯ç”¨çš„循环环境。",
+ "smw-query-condition-empty": "查询æ述有空æ¡ä»¶ã€‚",
+ "smw-types-list": "æ•°æ®ç±»åž‹åˆ—表",
+ "smw-types-default": "“$1â€æ˜¯ä¸€ä¸ªå†…置的数æ®ç±»åž‹ã€‚",
+ "smw-types-help": "更进一步的信æ¯å’Œä¾‹å­å¯åœ¨[https://www.semantic-mediawiki.org/wiki/Help:Type_$1 帮助页é¢]找到。",
+ "smw-type-anu": "“$1â€æ˜¯ä¸€ä¸ª[[Special:Types/URL|URL]]æ•°æ®ç±»åž‹çš„å˜ä½“,并且主è¦ç”¨äºŽâ€œowl:AnnotationPropertyâ€å¯¼å‡ºç”³æŠ¥ã€‚",
+ "smw-type-boo": "“$1â€æ˜¯ä¸€ä¸ªæ述真/å‡å€¼çš„原始数æ®ç±»åž‹ã€‚",
+ "smw-type-cod": "“$1â€æ˜¯ä¸€ä¸ª[[Special:Types/Text|Text]]æ•°æ®ç±»åž‹çš„å˜ä½“,用于任æ„长度的技术文本,例如æºä»£ç è¡¨ã€‚",
+ "smw-type-geo": "“$1â€æ˜¯ä¸€ä¸ªæ述地ç†ä½ç½®çš„æ•°æ®ç±»åž‹ï¼Œå®ƒéœ€è¦[https://www.semantic-mediawiki.org/wiki/Extension:Maps 扩展“Mapsâ€]。",
+ "smw-type-tel": "“$1â€æ˜¯ä¸€ä¸ªæ ¹æ®RFC 3966æ述国际电è¯å·ç çš„æ•°æ®ç±»åž‹ã€‚",
+ "smw-type-txt": "“$1â€æ˜¯ä¸€ä¸ªæè¿°ä»»æ„长度字符串的原始数æ®ç±»åž‹ã€‚",
+ "smw-type-dat": "“$1â€æ˜¯ä¸€ä¸ªä»£è¡¨ç»Ÿä¸€æ ¼å¼çš„时间点的数æ®ç±»åž‹ã€‚",
+ "smw-type-ema": "“$1â€æ˜¯ä»¥ç”µå­é‚®ä»¶ä¹‹æ®Šï¼Œä½“。",
+ "smw-type-rec": "“$1â€æ˜¯ä¸€ä¸ªå®¹å™¨æ•°æ®ç±»åž‹ï¼Œå®ƒä»¥å›ºå®šçš„顺åºæŒ‡å®šç±»åž‹åŒ–属性的列表。",
+ "smw-type-tab-properties": "属性",
+ "smw-type-tab-types": "æ ·å¼",
+ "smw-type-tab-errors": "错误",
+ "smw-type-primitive": "基本",
+ "smw-type-contextual": "文之",
+ "smw-type-compound": "å¤åˆç‰©",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "此页é¢æ供用于查找所有属性值和指定页é¢çš„æµè§ˆç•Œé¢ã€‚其他å¯ç”¨çš„æœç´¢ç•Œé¢åŒ…括[[Special:SearchByProperty|属性æœç´¢]]å’Œ[[Special:Ask|请求查询构建器]]。",
+ "smw-property-predefined-errc": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供的预定义属性,它代表出现在ä¸å½“值注释或输入处ç†è¿žæŽ¥ä¸­çš„错误。",
+ "smw-property-predefined-long-errc": "错误会收集于[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]中,它å¯èƒ½åŒ…å«è‡³å¯¼è‡´çŸ›ç›¾çš„属性的å‚考文献。",
+ "smw-property-predefined-errt": "“$1â€æ˜¯ä¸€ä¸ªåŒ…å«ä¸€ä¸ªé”™è¯¯çš„文字æ述的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-subobject-parser-invalid-naming-scheme": "用户定义的å­å¯¹è±¡åŒ…å«ä¸€ä¸ªæ— æ•ˆçš„命å方案。用于å‰5个字符中的点符å·ï¼ˆ$1)ä¿ç•™åšæ‰©å±•ä¹‹ç”¨ã€‚您å¯ä»¥è®¾ç½®[https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier 命å的标识符]。",
+ "smw-datavalue-record-invalid-property-declaration": "记录的定义包å«â€œ$1â€å±žæ€§ï¼Œå®ƒæœ¬èº«è¢«å£°æ˜Žä¸ºè®°å½•ç±»åž‹ï¼Œå¹¶ä¸è¢«å…许使用。",
+ "smw-property-predefined-mdat": "“$1â€æ˜¯ç¬¦åˆæŸä¸€ä¸»é¢˜æœ€è¿‘修改的日期的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-cdat": "“$1â€æ˜¯ç¬¦åˆæŸä¸€ä¸»é¢˜é¦–个修订版本的日期的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-newp": "“$1â€æ˜¯ä¸€ä¸ªè¡¨æ˜Žä¸»é¢˜æ˜¯å¦æ–°çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-ledt": "“$1â€æ˜¯åŒ…å«åˆ›å»ºæœ€è¿‘修订用户的页é¢å称的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-mime": "“$1â€æ˜¯ä¸€ä¸ªæ述上传文件的MIME类型的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-media": "“$1â€æ˜¯ä¸€ä¸ªæ述上传文件的媒体类型的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-askfo": "“$1â€æ˜¯ä¿ç•™ç”¨äºŽæŸ¥è¯¢ä¸­çš„结果格å¼å称的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-askst": "“$1â€æ˜¯ä¸€ä¸ªå°†æŸ¥è¯¢æ¡ä»¶æ述为字符串的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-askdu": "“$1â€æ˜¯åŒ…å«ä¸ºå®ŒæˆæŸ¥è¯¢çš„执行,所需è¦çš„时间值(秒)的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-asksc": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供的预定义属性,它识别替代(例如远程ã€è”åˆï¼‰æŸ¥è¯¢èµ„æºã€‚",
+ "smw-property-predefined-askco": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供的预定义属性,它æ述查询状æ€æˆ–其组æˆã€‚",
+ "smw-property-predefined-long-askco": "分é…æ•°é‡ä»£è¡¨å†…部编ç çŠ¶æ€ï¼Œå®ƒåœ¨[https://www.semantic-mediawiki.org/wiki/Help:Query_profiler 帮助页é¢]进一步解释。",
+ "smw-property-predefined-prec": "“$1â€æ˜¯ä¸€ä¸ªæ述用于数值数æ®ç±»åž‹çš„[https://www.semantic-mediawiki.org/wiki/Help:Display_precision 显示精度](å°æ•°ä½æ•°ï¼‰çš„预定义属性。",
+ "smw-types-extra-geo-not-available": "未检测到[https://www.semantic-mediawiki.org/wiki/Extension:Maps 扩展“Mapsâ€],因此“$1â€çš„æ“作能力å—é™ã€‚",
+ "smw-datavalue-monolingual-dataitem-missing": "预期用于构造å•è¯­å¤åˆ©å€¼çš„项目丢失。",
+ "smw-datavalue-monolingual-lcode-parenthesis": "($1)",
+ "smw-datavalue-languagecode-missing": "对于“$1â€æ³¨é‡Šï¼Œè§£æžå™¨æ— æ³•å†³å®šè¯­è¨€ä»£ç ï¼ˆä¹Ÿå°±æ˜¯â€œfoo@zhâ€ï¼‰ã€‚",
+ "smw-datavalue-languagecode-invalid": "“$1â€ä¸è®¤ä¸ºæ˜¯æ”¯æŒçš„语言代ç ã€‚",
+ "smw-property-predefined-lcode": "“$1â€æ˜¯ä¸€ä¸ªè¡¨è¿°BCP47æ ¼å¼è¯­è¨€ä»£ç çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-type-mlt-rec": "“$1â€æ˜¯ä¸€ä¸ªå°†æ–‡æœ¬å€¼ä¸Žç‰¹å®š[[Property:Language code|语言代ç ]]è”系起æ¥çš„[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]æ•°æ®ç±»åž‹ã€‚",
+ "smw-types-extra-mlt-lcode": "æ•°æ®ç±»åž‹{{PLURAL:$2|需è¦|ä¸éœ€è¦}}语言代ç ï¼ˆä¹Ÿå°±æ˜¯è¯´{{PLURAL:$2|ä¸æŽ¥å—ä¸å¸¦è¯­è¨€ä»£ç çš„值注释|接å—ä¸å¸¦è¯­è¨€ä»£ç çš„值注释}})。",
+ "smw-property-predefined-text": "“$1â€æ˜¯ä¸€ä¸ªè¡¨æ˜Žä»»æ„长度文本的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-pdesc": "“$1â€æ˜¯å…许æ述语言环境中的属性的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-list": "“$1â€æ˜¯å®šä¹‰ä¸Ž[[Special:Types/Record|记录]]类型属性一起使用的属性列表的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-limitreport-intext-parsertime": "[SMW]文内注释解æžå™¨æ—¶é—´",
+ "smw-limitreport-intext-postproctime": "[SMW]公布处ç†æ—¶é—´",
+ "smw-limitreport-intext-parsertime-value": "$1{{PLURAL:$1|秒}}",
+ "smw-limitreport-intext-postproctime-value": "$1{{PLURAL:$1|秒}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW]存储更新时间(在页é¢åˆ·æ–°æ—¶ï¼‰",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1{{PLURAL:$1|秒}}",
+ "smw_allows_pattern": "此页é¢é¢„计包å«å‚考文献列表(跟éš[https://zh.wikipedia.org/wiki/æ­£åˆ™è¡¨è¾¾å¼ æ­£åˆ™è¡¨è¾¾å¼]),并å¯é€šè¿‡[[Property:Allows pattern|å…许的模å¼]]属性使用。è¦ç¼–辑此页é¢ï¼Œéœ€è¦<code>smw-patternedit</code>æƒé™ã€‚",
+ "smw-datavalue-allows-pattern-mismatch": "“$1â€è¢«â€œ$2â€æ­£åˆ™è¡¨è¾¾å¼å½’类为无效。",
+ "smw-datavalue-allows-pattern-reference-unknown": "“$1â€æ¨¡å¼å‚考文献ä¸èƒ½åŒ¹é…[[MediaWiki:Smw allows pattern]]中的æ¡ç›®ã€‚",
+ "smw-datavalue-allows-value-list-unknown": "“$1â€åˆ—表引用ä¸åŒ¹é…[[MediaWiki:Smw allows list $1]]页é¢ã€‚",
+ "smw-datavalue-allows-value-list-missing-marker": "“$1â€åˆ—表内容缺少带星å·ï¼ˆ*)列表标记的项目。",
+ "smw-datavalue-feature-not-supported": "“$1â€åŠŸèƒ½æœªè¢«æ”¯æŒï¼Œæˆ–在该wikiç¦ç”¨ã€‚",
+ "smw-property-predefined-pvap": "“$1â€æ˜¯ä¸€ä¸ªåˆ—举[[MediaWiki:Smw allows pattern|模å¼å‚考文献]],以å…许[https://zh.wikipedia.org/wiki/æ­£åˆ™è¡¨è¾¾å¼ æ­£åˆ™è¡¨è¾¾å¼]匹é…的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-dtitle": "“$1â€æ˜¯ä¸€ä¸ªåˆ†é…ä¸åŒæ˜¾ç¤ºæ ‡é¢˜è‡³å®žä½“的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-pvuc": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,为æ¯ä¸ªå®žä¾‹é™åˆ¶å€¼åˆ†é…以使其唯一(或者最多一个)的预定义属性。",
+ "smw-property-predefined-long-pvuc": "当两个值的文字表示ä¸ç­‰æ—¶ç‹¬ç‰¹æ€§å°±ä¼šè¢«ç¡®å®šï¼Œå¹¶ä¸”任何这类约æŸçš„è¿ä¾‹å°†è¢«å½’类为错误。",
+ "smw-datavalue-uniqueness-constraint-error": "属性“$1â€åªå…许å•ä¸€å€¼åˆ†é…,且“$2â€å·²æ³¨é‡ŠäºŽä¸»é¢˜â€œ$3â€ä¸­ã€‚",
+ "smw-datavalue-uniqueness-constraint-isknown": "属性“$1â€åªå…许唯一值释文,“$2â€å·²è¢«åˆ†é…给“$3â€ã€‚",
+ "smw-property-predefined-boo": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表布尔值的[[Special:Types/Boolean|类型]]和预定义属性。",
+ "smw-property-predefined-num": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表数值的[[Special:Types/Number|类型]]和预定义属性。",
+ "smw-property-predefined-dat": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表日期值的[[Special:Types/Date|类型]]和预定义属性。",
+ "smw-property-predefined-uri": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表URI/URL值的[[Special:Types/URL|类型]]和预定义属性。",
+ "smw-property-predefined-qty": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表é‡å€¼çš„[[Special:Types/Quantity|类型]]和预定义属性。",
+ "smw-datavalue-time-invalid-offset-zone-usage": "“$1â€åŒ…å«ä¸è¢«æ”¯æŒçš„å移和区域标识符。",
+ "smw-datavalue-time-invalid-values": "“$1â€å€¼åŒ…å«åœ¨â€œ$2â€çš„å½¢å¼ä¸‹æ— æ³•è§£é‡Šçš„ä¿¡æ¯ã€‚",
+ "smw-datavalue-time-invalid-date-components-common": "“$1â€åŒ…å«ä¸€äº›æ— æ³•è§£é‡Šçš„ä¿¡æ¯ã€‚",
+ "smw-datavalue-time-invalid-date-components-dash": "“$1â€åŒ…å«ä¸€ä¸ªå¤–æ¥ç ´æŠ˜å·ï¼Œæˆ–其他用作时间解释时无效的字符。",
+ "smw-datavalue-time-invalid-date-components-empty": "“$1â€åŒ…å«ä¸€äº›ç©ºéƒ¨ä»¶ã€‚",
+ "smw-datavalue-time-invalid-date-components-three": "“$1â€åŒ…å«è¶…过3个日期解释所需è¦çš„部件。",
+ "smw-datavalue-time-invalid-date-components-sequence": "“$1â€åŒ…å«ä¸èƒ½è¢«è§£é‡Šä¸ºç”¨äºŽæ—¥æœŸéƒ¨ä»¶çš„å¯ç”¨åŒ¹é…矩阵åºåˆ—。",
+ "smw-datavalue-time-invalid-ampm": "“$1â€åŒ…å«â€œ$2â€ä½œä¸ºå°æ—¶å…ƒç´ ï¼Œå®ƒåœ¨12å°æ—¶åˆ¶ä¸­æ˜¯æ— æ•ˆçš„。",
+ "smw-datavalue-time-invalid-jd": "无法将“$1â€è¾“入值解释为有效的JD(儒略日)数值,已报告“$2â€ã€‚",
+ "smw-datavalue-time-invalid-prehistoric": "无法解释陈旧的“$1â€è¾“入值。例如在陈旧环境中指定了超过1年或超过一个日历模型å¯èƒ½è¿”回æ„外结果。",
+ "smw-datavalue-time-invalid": "无法将“$1â€è¾“入值解释为有效日期或时间æˆåˆ†ï¼Œå·²æŠ¥å‘Šâ€œ$2â€ã€‚",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "æ ¼å¼åŒ–程åºURI缺少“$1â€å ä½ç¬¦ã€‚",
+ "smw-datavalue-external-formatter-invalid-uri": "“$1â€æ˜¯æ— æ•ˆçš„URL。",
+ "smw-datavalue-external-identifier-formatter-missing": "属性缺少[[Property:External formatter uri|“外部格å¼åŒ–程åºURIâ€]]分é…。",
+ "smw-datavalue-keyword-maximum-length": "关键è¯è¶…出最大长度$1{{PLURAL:$1|字节}}。",
+ "smw-property-predefined-eid": "“$1â€æ˜¯ä¸€ç§ç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,代表外部标识符的[[Special:Types/External identifier|类型]]和预定义属性。",
+ "smw-property-predefined-peid": "“$1â€æ˜¯ä¸€ä¸ªæŒ‡å®šå¤–部标识符的预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-property-predefined-pefu": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,通过å ä½ç¬¦æŒ‡å®šå¤–部资æºçš„预定义属性。",
+ "smw-property-predefined-long-pefu": "URI预计包å«å°†è°ƒæ•´ä¸º[[Special:Types/External identifier|外部标识符]]值,以形æˆæœ‰æ•ˆçš„资æºå¼•ç”¨çš„å ä½ç¬¦ã€‚",
+ "smw-type-eid": "“$1â€æ˜¯ä¸€ä¸ª[[Special:Types/Text|文本]]æ•°æ®ç±»åž‹çš„å˜ä½“,它è¦æ±‚属性声明[[Property:External formatter uri|外部格å¼åŒ–程åºURI]]。",
+ "smw-property-predefined-keyw": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,正常化文本的预定义属性和[[Special:Types/Keyword|类型]],并拥有有é™å­—符长度。",
+ "smw-type-keyw": "“$1â€æ˜¯ä¸€ç§[[Special:Types/Text|文本]]æ•°æ®ç±»åž‹çš„å˜ä½“,它拥有有é™å­—符长度,并正常化其内容表现形å¼ã€‚",
+ "smw-datavalue-stripmarker-parse-error": "æ供的值“$1â€åŒ…å«[https://en.wikipedia.org/wiki/Help:Strip_markers æ¡å½¢æ ‡è®°],并因此ä¸èƒ½å……分解æžã€‚",
+ "smw-datavalue-parse-error": "æ供的值“$1â€æœªèƒ½ç†è§£ã€‚",
+ "smw-datavalue-propertylist-invalid-property-key": "属性列表“$1â€åŒ…å«æ— æ•ˆçš„属性关键字“$2â€ã€‚",
+ "smw-datavalue-type-invalid-typeuri": "“$1â€ç±»åž‹ä¸èƒ½è¢«è½¬æ¢ä¸ºæœ‰æ•ˆçš„URI代表。",
+ "smw-datavalue-wikipage-missing-fragment-context": "wiki页é¢è¾“入值“$1â€ä¸èƒ½åœ¨æ²¡æœ‰çŽ¯å¢ƒé¡µé¢çš„情况下使用。",
+ "smw-datavalue-wikipage-invalid-title": "页é¢ç±»åž‹è¾“入值“$1â€åŒ…å«æ— æ•ˆå­—符或ä¸å®Œæ•´ï¼Œå¹¶å› æ­¤åœ¨æŸ¥è¯¢æˆ–注释过程期间导致æ„外结果。",
+ "smw-datavalue-wikipage-property-invalid-title": "属性“$1â€ï¼ˆä½œä¸ºé¡µé¢ç±»åž‹ï¼‰ä¸Žè¾“入值“$2â€åŒ…å«æ— æ•ˆå­—符或ä¸å®Œæ•´ï¼Œå¹¶å› æ­¤åœ¨æŸ¥è¯¢æˆ–注释过程期间导致æ„外结果。",
+ "smw-datavalue-wikipage-empty": "wiki页é¢è¾“入值为空(例如<code>[[SomeProperty::]], [[]]</code>),并因此ä¸èƒ½ä½œä¸ºæŸ¥è¯¢æ¡ä»¶å称或其之一使用。",
+ "smw-type-ref-rec": "“$1â€æ˜¯ä¸€ä¸ªå…许记录有关值分é…çš„é¢å¤–ä¿¡æ¯ï¼ˆä¾‹å¦‚æ¥æºæ•°æ®ï¼‰çš„[https://www.semantic-mediawiki.org/wiki/Container 容器]类型。",
+ "smw-datavalue-reference-outputformat": "$1:$2",
+ "smw-datavalue-reference-invalid-fields-definition": "[[Special:Types/Reference|å‚考文献]]类型应使用[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields 有字段]属性声明属性列表。",
+ "smw-parser-invalid-json-format": "JSON解æžå™¨è¿”回了“$1â€ã€‚",
+ "smw-property-preferred-title-format": "$1($2)",
+ "smw-property-preferred-label-language-combination-exists": "“$1â€ä¸èƒ½ç”¨ä½œé¦–选标签,因为语言“$2â€å·²åˆ†é…给“$3â€æ ‡ç­¾ã€‚",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "å¤åˆ¶é“¾æŽ¥åˆ°å‰ªè´´æ¿",
+ "smw-property-userdefined-fixedtable": "“$1â€è¢«é…置为[https://www.semantic-mediawiki.org/wiki/Fixed_properties 固定属性],并且对其[https://www.semantic-mediawiki.org/wiki/Type_declaration 类型声明]的修改需è¦è¿è¡Œ<code>setupStore.php</code>或完æˆç‰¹æ®Šçš„[[Special:SemanticMediaWiki|“数æ®åº“安装与å‡çº§â€]]任务。",
+ "smw-data-lookup": "正在å–å¾—æ•°æ®...",
+ "smw-data-lookup-with-wait": "请求正在处ç†ï¼Œå¯èƒ½èŠ±è´¹ä¸€äº›æ—¶é—´ã€‚",
+ "smw-no-data-available": "没有å¯ç”¨æ•°æ®ã€‚",
+ "smw-property-req-violation-missing-fields": "由于定义<code>Has fields</code>属性失败,属性“$1â€ä¸¢å¤±ç”¨äºŽå·²æ³¨é‡Šâ€œ$2â€ç±»åž‹çš„声明详情。",
+ "smw-property-req-violation-missing-formatter-uri": "由于定义<code>External formatter URI</code>属性失败,属性“$1â€ä¸¢å¤±ç”¨äºŽå·²æ³¨é‡Šç±»åž‹çš„声明详情。",
+ "smw-property-req-violation-predefined-type": "作为预定义属性,属性“$1â€åŒ…å«â€œ$2â€ç±»åž‹å£°æ˜Žï¼Œå®ƒä¸Žæ­¤å±žæ€§é»˜è®¤ç±»åž‹ä¸å…¼å®¹ã€‚",
+ "smw-property-req-violation-import-type": "检测到与预定义导入的“$1â€è¯æ±‡ç±»åž‹ä¸å…¼å®¹çš„类型声明。一般情况下,因为信æ¯ä»Žå¯¼å…¥å®šä¹‰ä¸­æ£€ç´¢ï¼Œæ‰€ä»¥ä¸éœ€è¦å£°æ˜Žç±»åž‹ã€‚",
+ "smw-property-req-violation-change-propagation-locked-error": "属性“$1â€å·²æ”¹å˜ï¼Œå¹¶è¦æ±‚[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传播]过程é‡æ–°è¯„估分é…的实体。属性页é¢å·²é”定,直到主è¦è§„格更新完æˆï¼Œä»¥é˜²æ­¢ä¸­é€”被打断或矛盾的规格。在页é¢å¯ä»¥è§£é”å‰ï¼Œè¿‡ç¨‹å¯èƒ½æŒç»­ä¸€æ®µæ—¶é—´ï¼Œè¿™å–决于[https://www.mediawiki.org/wiki/Manual:Job_queue 工作队列]调度表的大å°å’Œé¢‘率。",
+ "smw-property-req-violation-change-propagation-locked-warning": "属性“$1â€å·²æ”¹å˜ï¼Œå¹¶è¦æ±‚[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传播]过程é‡æ–°è¯„估分é…的实体。更新å¯èƒ½æŒç»­ä¸€æ®µæ—¶é—´ï¼Œè¿™å–决于[https://www.mediawiki.org/wiki/Manual:Job_queue 工作队列]调度表的大å°å’Œé¢‘率,并建议推迟对属性的更新以防止中途被打断或矛盾的规格。",
+ "smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传输]过程正在等待(估计$1个[https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|工作}}]),建议暂缓修改属性的æ“作,直到更新过程结æŸä¸ºæ­¢ï¼Œä»¥é˜²æ­¢ä¸­é€”被打断或矛盾的规格。",
+ "smw-property-req-violation-missing-maps-extension": "语义MediaWiki无法检测[https://www.semantic-mediawiki.org/wiki/Extension:Maps “Mapsâ€]扩展,这是选定类型的必è¦æ¡ä»¶ï¼Œå¹¶å› æ­¤é™åˆ¶äº†æ­¤å±žæ€§çš„功能性。",
+ "smw-property-req-violation-type": "属性包å«çŸ›ç›¾çš„类型规格,这å¯èƒ½å¯¼è‡´æ— æ•ˆå€¼é‡Šæ–‡ï¼Œå› æ­¤æœ€å¥½æœ‰ç”¨æˆ·åˆ†é…一个适当的类型。",
+ "smw-change-propagation-protection": "此页é¢å·²é”定,以阻止在[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传播]æ›´æ–°è¿è¡Œæ—¶çš„æ„外数æ®ä¿®æ”¹ã€‚在页é¢è§£é™¤é”定å‰ï¼Œè¿‡ç¨‹å¯èƒ½èŠ±è´¹ä¸€äº›æ—¶é—´ï¼Œè¿™å–决于[https://www.mediawiki.org/wiki/Manual:Job_queue 作业队列]调度器的大å°å’Œé¢‘率。",
+ "smw-category-change-propagation-locked-error": "属性“$1â€å·²æ”¹å˜ï¼Œå¹¶è¦æ±‚[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传播]过程é‡æ–°è¯„估分é…的实体。目å‰åˆ†ç±»é¡µé¢å·²é”定,直到主è¦è§„格更新完æˆï¼Œä»¥é˜²æ­¢ä¸­é€”被打断或矛盾的规格。在页é¢å¯ä»¥è§£é”å‰ï¼Œè¿‡ç¨‹å¯èƒ½æŒç»­ä¸€æ®µæ—¶é—´ï¼Œè¿™å–决于[https://www.mediawiki.org/wiki/Manual:Job_queue 工作队列]调度表的大å°å’Œé¢‘率。",
+ "smw-category-change-propagation-locked-warning": "属性“$1â€å·²æ”¹å˜ï¼Œå¹¶è¦æ±‚[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传播]过程é‡æ–°è¯„估分é…的实体。更新å¯èƒ½æŒç»­ä¸€æ®µæ—¶é—´ï¼Œè¿™å–决于[https://www.mediawiki.org/wiki/Manual:Job_queue 工作队列]调度表的大å°å’Œé¢‘率,并建议推迟对分类的更新以防止中途被打断或矛盾的规格。",
+ "smw-category-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改传输]更新正在等待(估计$1个[https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|工作}}]),建议暂缓修改分类的æ“作,直到更新过程结æŸä¸ºæ­¢ï¼Œä»¥é˜²æ­¢ä¸­é€”被打断或矛盾的规格。",
+ "protect-level-smw-pageedit": "åªå…许带页é¢ç¼–辑æƒé™çš„用户(语义MediaWiki)",
+ "smw-create-protection": "当å¯ç”¨[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode æƒå¨æ¨¡å¼]时,仅é™æ‹¥æœ‰é€‚当的“$2â€æƒé™ï¼ˆæˆ–[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 用户组])时,æ‰å¯ä»¥åˆ›å»ºâ€œ$1â€å±žæ€§ã€‚",
+ "smw-create-protection-exists": "当å¯ç”¨[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode æƒå¨æ¨¡å¼]时,仅é™æ‹¥æœ‰é€‚当的“$2â€æƒé™ï¼ˆæˆ–[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 用户组])时,æ‰å¯ä»¥åˆ›å»ºâ€œ$1â€å±žæ€§ã€‚",
+ "smw-edit-protection": "此页é¢å—到[[Property:Is edit protected|ä¿æŠ¤]],以阻止æ„外数æ®ä¿®æ”¹ï¼Œåªå¯ä»¥ç”±æ‹¥æœ‰é€‚当编辑æƒé™ï¼ˆâ€œ$1â€ï¼‰æˆ–[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 用户组]的用户编辑。",
+ "smw-edit-protection-disabled": "编辑ä¿æŠ¤è¢«ç¦ç”¨ï¼Œå› æ­¤â€œ$1â€ä¸èƒ½ç”¨äºŽä¿æŠ¤å®žä½“页é¢ä¸å—未ç»æŽˆæƒçš„编辑。",
+ "smw-edit-protection-auto-update": "语义MediaWiki已根æ®â€œIs edit protectedâ€å±žæ€§æ›´æ–°ä¿æŠ¤çŠ¶æ€ã€‚",
+ "smw-edit-protection-enabled": "编辑被ä¿æŠ¤å†…容(语义MediaWiki)",
+ "smw-patternedit-protection": "此页é¢å—到ä¿æŠ¤ï¼Œå¹¶å› æ­¤åªæœ‰æ‹¥æœ‰é€‚当<code>smw-patternedit</code>[https://www.semantic-mediawiki.org/wiki/Help:Permissions æƒé™]的用户å¯ä»¥ç¼–辑。",
+ "smw-property-predefined-edip": "“$1â€æ˜¯ä¸€ä¸ªç”±[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供,识别编辑是å¦å—到ä¿æŠ¤çš„预定义属性。",
+ "smw-property-predefined-long-edip": "尽管任何用户都å¯ä»¥æ·»åŠ æ­¤å±žæ€§è‡³ä¸€ä¸ªä¸»é¢˜ï¼Œä½†åªæœ‰æ‹¥æœ‰ä¸“用æƒé™çš„一ä½ç”¨æˆ·å¯ä»¥åœ¨å…¶æ·»åŠ è‡³å®žä½“页é¢åŽï¼Œå¯¹å…¶ç¼–辑或解除ä¿æŠ¤ã€‚",
+ "smw-query-reference-link-label": "查询å‚考文献",
+ "smw-format-datatable-emptytable": "表中无å¯ç”¨æ•°æ®",
+ "smw-format-datatable-info": "显示_TOTAL_æ¡è®°å½•ï¼Œä»Ž_START_至_END_",
+ "smw-format-datatable-infoempty": "显示0æ¡è®°å½•ï¼Œä»Ž0至0",
+ "smw-format-datatable-infofiltered": "(从_MAX_总记录中过滤)",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "显示_MENU_记录",
+ "smw-format-datatable-loadingrecords": "正在载入...",
+ "smw-format-datatable-processing": "正在处ç†...",
+ "smw-format-datatable-search": "æœç´¢ï¼š",
+ "smw-format-datatable-zerorecords": "未找到匹é…记录",
+ "smw-format-datatable-first": "第一",
+ "smw-format-datatable-last": "最åŽ",
+ "smw-format-datatable-next": "下一个",
+ "smw-format-datatable-previous": "上一个",
+ "smw-format-datatable-sortascending": ":激活按å‡åºè¿›è¡Œåˆ—排åº",
+ "smw-format-datatable-sortdescending": ":激活按é™åºè¿›è¡Œåˆ—排åº",
+ "smw-format-datatable-toolbar-export": "导出",
+ "smw-format-list-other-fields-open": "(",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "分类“$1â€åŒ…å«æ— æ•ˆçš„链至éžåˆ†ç±»å字空间的é‡å®šå‘目标。",
+ "smw-parser-function-expensive-execution-limit": "解æžå™¨å‚数已达到高开销执行的é™åˆ¶ï¼ˆå‚è§é…ç½®å‚æ•°[https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>])。",
+ "smw-postproc-queryref": "由于一些必è¦çš„查询åŽå¤„ç†ï¼Œè¯­ä¹‰MediaWiki将开始页é¢åˆ·æ–°ã€‚",
+ "apihelp-smwinfo-summary": "检索有关语义MediaWiki统计信æ¯åŠå…¶ä»–元信æ¯çš„API模å—。",
+ "apihelp-ask-summary": "使用请求语言查询语义MediaWikiçš„API模å—。",
+ "apihelp-askargs-summary": "使用请求语言作为æ¡ä»¶ã€æ‰“å°è¾“出和å‚数的列表,æ¥æŸ¥è¯¢è¯­ä¹‰MediaWikiçš„API模å—。",
+ "apihelp-browsebyproperty-summary": "检索有关æŸä¸€å±žæ€§ï¼Œæˆ–是属性列表信æ¯çš„API模å—。",
+ "apihelp-browsebysubject-summary": "检索有关主题信æ¯çš„API模å—。",
+ "apihelp-smwtask-summary": "执行语义MediaWiki相关任务的API模å—。",
+ "apihelp-smwbrowse-summary": "支æŒæµè§ˆä¸Žæœç´¢è¯­ä¹‰MediaWiki维护实体活动的API模å—。",
+ "apihelp-ask-parameter-api-version": "输出格å¼ï¼š\n;2:åŽç«¯å…¼å®¹æ ¼å¼ï¼Œç»“果列表使用{}。\n;3:实验格å¼ï¼Œç»“果列表使用[]。",
+ "smw-api-invalid-parameters": "无效的å‚æ•°\"$1\"",
+ "smw-parser-recursion-level-exceeded": "$1递归的等级在解æžæœŸé—´æº¢å‡ºã€‚建议您验è¯æ¨¡æ¿ç»“果,或如有需è¦å¯è°ƒæ•´é…ç½®å‚æ•°<code>$maxRecursionDepth</code>。",
+ "smw-property-page-list-count": "显示$1个使用该属性的{{PLURAL:$1|页é¢}}。",
+ "smw-property-page-list-search-count": "显示$1个使用该属性,匹é…“$2â€å€¼çš„{{PLURAL:$1|页é¢}}。",
+ "smw-property-reserved-category": "分类",
+ "smw-category": "分类",
+ "smw-datavalue-uri-invalid-scheme": "“$1â€æœªåˆ—举为有效的URI方案。",
+ "smw-browse-property-group-title": "属性组",
+ "smw-browse-property-group-label": "属性组标签",
+ "smw-browse-property-group-description": "属性组æè¿°",
+ "smw-property-predefined-ppgr": "“$1â€æ˜¯ä¸€ä¸ªå®šä¹‰ç”¨ä½œå±žæ€§åˆ†ç»„示例的实体(主è¦æ˜¯åˆ†ç±»ï¼‰çš„预定义属性,并由[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 语义MediaWiki]æ供。",
+ "smw-filter": "过滤器",
+ "smw-section-expand": "展开章节",
+ "smw-section-collapse": "关闭章节",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]æ ¼å¼",
+ "smw-help": "帮助",
+ "smw-cheat-sheet": "å°æŠ„",
+ "smw-personal-jobqueue-watchlist": "监视列表(工作队列)",
+ "smw-property-predefined-label-skey": "Sortkey",
+ "smw-processing": "处ç†ä¸­...",
+ "smw-redirect-target-unresolvable": "目标无法解决,原因是“$1â€",
+ "smw-types-title": "类型:$1",
+ "smw-schema-error": "验è¯é”™è¯¯",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "类型",
+ "smw-schema-tag": "{{PLURAL:$1|标签|标签}}",
+ "smw-ask-title-keyword-type": "关键è¯æœç´¢",
+ "smw-ask-message-keyword-type": "æ­¤æœç´¢åŒ¹é…<code><nowiki>$1</nowiki></code>æ¡ä»¶ã€‚",
+ "smw-remote-source-unavailable": "无法连接远程“$1â€ç›®æ ‡ã€‚",
+ "smw-remote-source-disabled": "'''$1'''æ¥æºå·²ç¦ç”¨è¿œç¨‹è¯·æ±‚支æŒï¼",
+ "smw-remote-source-unmatched-id": "'''$1'''æ¥æºä¸åŒ¹é…语义MediaWikiçš„æŸä¸ªç‰ˆæœ¬ä»¥æ”¯æŒè¿œç¨‹è¯·æ±‚。",
+ "smw-remote-request-note": "结果已从'''$1'''远程æ¥æºæ£€ç´¢ï¼Œå¹¶ä¸”它很å¯èƒ½ç”¨äºŽç”Ÿæˆå†…容以包å«åœ¨å½“å‰wiki内ä¸å¯ç”¨çš„ä¿¡æ¯ã€‚",
+ "smw-remote-request-note-cached": "结果已从'''$1'''远程æ¥æº'''缓存''',并且它很å¯èƒ½ç”¨äºŽç”Ÿæˆå†…容以包å«åœ¨å½“å‰wiki内ä¸å¯ç”¨çš„ä¿¡æ¯ã€‚",
+ "smw-parameter-missing": "没有找到å‚æ•°\"$1\"。",
+ "smw-property-tab-usage": "使用é‡",
+ "smw-property-tab-redirects": "åŒä¹‰è¯",
+ "smw-property-tab-subproperties": "å­å±žæ€§",
+ "smw-property-tab-errors": "分é…ä¸å½“",
+ "smw-property-tab-specification": "……更多",
+ "smw-concept-tab-list": "列表",
+ "smw-concept-tab-errors": "错误",
+ "smw-ask-tab-result": "结果",
+ "smw-ask-tab-extra": "附加功能",
+ "smw-ask-tab-debug": "调试",
+ "smw-ask-tab-code": "代ç "
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hant.json b/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hant.json
new file mode 100644
index 00000000..58f6943a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/i18n/zh-hant.json
@@ -0,0 +1,863 @@
+{
+ "@metadata": {
+ "authors": [
+ "Anakmalaysia",
+ "Ch.Andrew",
+ "Frankou",
+ "Gzdavidwong",
+ "Justincheng12345",
+ "Littletung",
+ "Liuxinyu970226",
+ "Mark85296341",
+ "Oapbtommy",
+ "Sheepy",
+ "Shirayuki",
+ "Simon Shek",
+ "StephDC",
+ "Wrightbus",
+ "ì•„ë¼",
+ "Cwlin0416",
+ "LNDDYL",
+ "Reke",
+ "Wehwei",
+ "Wwycheuk",
+ "Hitaypayan",
+ "机智的å°é±¼å›",
+ "A2093064",
+ "Kly",
+ "Assoc",
+ "Laundry Machine",
+ "Fanjiayi",
+ "Hello903hello",
+ "Taiwania Justo",
+ "A Chinese Wikipedian"
+ ]
+ },
+ "smw-desc": "讓您的 Wiki 更容易使用 - ä¸è«–是å°æ–¼æ©Ÿå™¨''或''人類([https://www.semantic-mediawiki.org/wiki/Help:User_manual 線上說明文件])",
+ "smw-title": "Semantic MediaWiki",
+ "smw-upgrade-error": "Semantic MediaWiki 已安è£å®Œä¸¦ä¸”已啟用,但缺少符åˆï¼š<code>$1</code> 的相稱[https://www.semantic-mediawiki.org/wiki/Help:Upgrade æ›´æ–°éµ]。",
+ "smw-upgrade-error-why-title": "為什麼我會看到此錯誤。",
+ "smw-upgrade-error-why-explain": "Semantic MediaWiki 的內部資料庫架構已更改,並且需è¦åšå‡ºä¸€äº›èª¿æ•´ä¾†è®“功能齊全,這å¯èƒ½æœ‰åŒ…å«ä»¥ä¸‹æ•¸ç¨®åŽŸå› ï¼š\n* 已添加é¡å¤–固定屬性(需è¦é¡å¤–表格設定)\n* 更新包å«ä¸€äº›å°æ–¼è¡¨æ ¼çš„變動,或是在存å–資料之å‰ç´¢å¼•æœ‰å¿…è¦ç”¢ç”Ÿä¸­æ–·",
+ "smw-upgrade-error-how-title": "我è¦å¦‚何修正此錯誤?",
+ "smw-upgrade-error-how-explain": "管ç†å“¡ï¼ˆæˆ–是任何有管ç†æ¬Šé™çš„人)已執行 MediaWiki çš„ [https://www.mediawiki.org/wiki/Manual:Update.php update.php] 或是 Semantic MediaWiki çš„ [https://www.semantic-mediawiki.org/wiki/Help:SetupStore.php setupStore.php] 維護手稿。您å¯ä»¥æŸ¥é–±ä»¥ä¸‹é é¢ä¾†ç²å¾—進一步å”助:\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation 安è£]介紹\n* [https://www.semantic-mediawiki.org/wiki/Help:Installation/Troubleshooting ç‹€æ³è™•ç†]說明é é¢",
+ "smw-semantics-not-enabled": "Semantic MediaWiki 功能沒有為此 wiki 啟用。",
+ "smw_viewasrdf": "訂閱 RDF feed",
+ "smw_finallistconjunct": "和",
+ "smw-factbox-head": "...更多有關「$1ã€",
+ "smw-factbox-facts": "事實",
+ "smw-factbox-facts-help": "顯示由æŸä¸€ä½¿ç”¨è€…所創建的è²æ˜Žå’Œäº‹å¯¦",
+ "smw-factbox-facts-derived": "è¡ç”Ÿçš„事實",
+ "smw-factbox-facts-derived-help": "顯示è¡ç”Ÿè‡ªè¦å‰‡çš„實情,或是在其它推ç†æŠ€è¡“幫助下所生æˆçš„實情",
+ "smw_isspecprop": "此屬性為此 Wiki 的特殊屬性。",
+ "smw-concept-cache-header": "暫存使用",
+ "smw-concept-cache-count": "[https://www.semantic-mediawiki.org/wiki/Help:Concept_cache_count 概念快å–]åŒ…å« {{PLURAL:$1|'''1'''|'''$1'''}} 個實體($2)。",
+ "smw-concept-no-cache": "沒有å¯ç”¨å¿«å–。",
+ "smw_concept_description": "概念 \"$1\" çš„æè¿°",
+ "smw_no_concept_namespace": "概念åªèƒ½å®šç¾©æ–¼å‘½å空間 Concept: 中。",
+ "smw_multiple_concepts": "æ¯é æ¦‚念é é¢åªèƒ½æ“有一個概念定義。",
+ "smw_concept_cache_miss": "因為維基設定需è¦æ¦‚念 \"$1\" 在離線時計算,此概念ç¾åœ¨ç„¡æ³•ä½¿ç”¨ã€‚\n如果這個å•é¡Œåœ¨ä¸€æ®µæ™‚間後ä»æœªè§£æ±ºï¼Œè«‹å‘網站管ç†å“¡è«‹æ±‚令該概念å¯ç”¨ã€‚",
+ "smw_noinvannot": "ä¸èƒ½ç‚ºé€†å±¬æ€§æŒ‡å®šå€¼ã€‚",
+ "version-semantic": "語æ„擴充套件",
+ "smw_baduri": "ä¸å…許 \"$1\" å½¢å¼çš„ URI。",
+ "smw_dsv_link": "DSV",
+ "smw_printername_count": "計算çµæžœ",
+ "smw_printername_csv": "匯出為 CSV",
+ "smw_printername_dsv": "匯出為 DSV",
+ "smw_printername_debug": "åµéŒ¯æŸ¥è©¢ (用於專業人士)",
+ "smw_printername_embedded": "嵌入é é¢å…§å®¹",
+ "smw_printername_json": "匯出為 JSON",
+ "smw_printername_list": "清單",
+ "smw_printername_plainlist": "純清單",
+ "smw_printername_ol": "有åºæ¸…å–®",
+ "smw_printername_ul": "ç„¡åºæ¸…å–®",
+ "smw_printername_table": "表格",
+ "smw_printername_broadtable": "寬版表格",
+ "smw_printername_template": "模æ¿",
+ "smw_printername_templatefile": "模æ¿æª”案",
+ "smw_printername_rdf": "匯出為 RDF",
+ "smw_printername_category": "類別",
+ "validator-type-class-SMWParamSource": "內容",
+ "smw-paramdesc-limit": "查詢çµæžœæ•¸é‡ä¸Šé™",
+ "smw-paramdesc-offset": "第一個çµæžœçš„åå·®",
+ "smw-paramdesc-headers": "顯示標頭/屬性å稱",
+ "smw-paramdesc-mainlabel": "主è¦é é¢çš„標籤å稱",
+ "smw-paramdesc-link": "以連çµé¡¯ç¤ºæ•¸å€¼",
+ "smw-paramdesc-intro": "查詢çµæžœå‰é¡¯ç¤ºçš„文字",
+ "smw-paramdesc-outro": "查詢çµæžœå¾Œé¡¯ç¤ºçš„文字",
+ "smw-paramdesc-default": "查詢çµæžœç‚ºç©ºæ™‚顯示的文字",
+ "smw-paramdesc-sep": "çµæžœä¹‹é–“的分隔符號",
+ "smw-paramdesc-propsep": "çµæžœè¨˜éŒ„的内容之間的分隔符號",
+ "smw-paramdesc-valuesep": "用於çµæžœè£¡å±¬æ€§å€¼ä¹‹é–“的分隔符號",
+ "smw-paramdesc-showsep": "顯示分隔符號於 CSV 檔案的頂端 (\"sep=<value>\")",
+ "smw-paramdesc-distribution": "顯示出ç¾çš„次數,ä¸é¡¯ç¤ºæ‰€æœ‰æ•¸å€¼ã€‚",
+ "smw-paramdesc-distributionsort": "ä¾å‡ºç¾æ¬¡æ•¸æŽ’åºæ•¸å€¼åˆ†å¸ƒã€‚",
+ "smw-paramdesc-distributionlimit": "é™åˆ¶æ•¸å€¼åˆ†ä½ˆåªè¨ˆç®—特殊數值。",
+ "smw-paramdesc-aggregation": "指定èšé›†æ‡‰èˆ‡ä»€éº¼ç›¸é—œ",
+ "smw-paramdesc-template": "用來顯示列å°é é¢çš„模æ¿å稱",
+ "smw-paramdesc-columns": "所è¦é¡¯ç¤ºçµæžœçš„欄數",
+ "smw-paramdesc-userparam": "使用模æ¿æ™‚,傳éžçµ¦æ¨¡æ¿çš„åƒæ•¸å€¼",
+ "smw-paramdesc-class": "為清單設定的é¡å¤– CSS 類別",
+ "smw-paramdesc-introtemplate": "查詢çµæžœå‰é¡¯ç¤ºçš„模æ¿å稱",
+ "smw-paramdesc-outrotemplate": "查詢çµæžœå¾Œé¡¯ç¤ºçš„模æ¿å稱",
+ "smw-paramdesc-embedformat": "定義標題的 HTML 標籤",
+ "smw-paramdesc-embedonly": "ä¸é¡¯ç¤ºæ¨™é¡Œ",
+ "smw-paramdesc-table-class": "é¡å¤–套用到表格上的 CSS 類別",
+ "smw-paramdesc-table-transpose": "垂直顯示表格標題,水平顯示çµæžœ",
+ "smw-paramdesc-rdfsyntax": "使用 RDF 語法",
+ "smw-paramdesc-csv-sep": "指定的欄ä½åˆ†éš”符號",
+ "smw-paramdesc-csv-valuesep": "指定一個值的分隔符號",
+ "smw-paramdesc-csv-merge": "與相åŒä¸»é¡Œè­˜åˆ¥å­—(或稱首欄)來åˆä½µåˆ—和欄的值",
+ "smw-paramdesc-csv-bom": "在輸出檔案的頂端添加 BOM(訊號ä½å…ƒçµ„é †åºå­—元)",
+ "smw-paramdesc-dsv-separator": "使用的分隔符號",
+ "smw-paramdesc-dsv-filename": "DSV 檔案å稱",
+ "smw-paramdesc-filename": "輸出的檔案å稱",
+ "smw-smwdoc-description": "顯示所有åƒæ•¸çš„表,它們å¯ä»¥èˆ‡é è¨­å€¼å’Œæ述一起用於指定的çµæžœæ ¼å¼ã€‚",
+ "smw-smwdoc-default-no-parameter-list": "æ­¤çµæžœæ ¼å¼æœªæ供格å¼ç‰¹å®šåƒæ•¸ã€‚",
+ "smw-smwdoc-par-format": "顯示åƒæ•¸æ–‡ä»¶çš„çµæžœæ ¼å¼ã€‚",
+ "smw-smwdoc-par-parameters": "è¦é¡¯ç¤ºå“ªäº›åƒæ•¸ã€‚\"specific\" (專用) 表示該格å¼æ‰€æ–°å¢žé‚£äº›çš„,\"base\" (基礎) 表示所有格å¼ç•¶ä¸­å¯ç”¨çš„那些,而\"all\" (全部) 表示兩種情æ³å…·æœ‰ã€‚",
+ "smw-paramdesc-sort": "排åºæŸ¥è©¢çš„屬性",
+ "smw-paramdesc-order": "查詢排åºçš„é †åº",
+ "smw-paramdesc-searchlabel": "繼續æœå°‹æ–‡å­—",
+ "smw-paramdesc-named_args": "請指定傳éžçµ¦è©²æ¨¡æ¿çš„變數",
+ "smw-paramdesc-template-arguments": "設定命ååƒæ•¸å¦‚何傳éžåˆ°æ¨¡æ¿",
+ "smw-paramdesc-import-annotation": "é¡å¤–的帶註釋資料在解æžä¸»é¡ŒæœŸé–“都會被複製",
+ "smw-paramdesc-export": "匯出é¸é …",
+ "smw-paramdesc-prettyprint": "美化輸出顯示é¡å¤–縮排與æ›è¡Œ",
+ "smw-paramdesc-json-unescape": "包å«æœªè·³è„«æ–œç·šä»¥åŠå¤šä½å…ƒçµ„è¬åœ‹ç¢¼å­—元的輸出",
+ "smw-paramdesc-json-type": "åºåˆ—化類型",
+ "smw-paramdesc-source": "替代的查詢來æº",
+ "smw-paramdesc-jsonsyntax": "使用 JSON 語法",
+ "smw-printername-feed": "訂閱 RSS 與 Atom feed",
+ "smw-paramdesc-feedtype": "訂閱類型",
+ "smw-paramdesc-feedtitle": "訂閱來æºçš„標題文字",
+ "smw-paramdesc-feeddescription": "訂閱來æºçš„æ述文字",
+ "smw-paramdesc-feedpagecontent": "訂閱來æºé¡¯ç¤ºçš„é é¢å…§å®¹",
+ "smw-label-feed-description": "訂閱 $1 $2 feed",
+ "smw-paramdesc-mimetype": "用於輸出檔案的媒體類型(MIME 類型)",
+ "smw_iq_disabled": "æ­¤ wiki 的語義查詢已被åœç”¨",
+ "smw_iq_moreresults": "...更多çµæžœ",
+ "smw_parseerror": "輸入的值無法ç†è§£ã€‚",
+ "smw_notitle": " \"$1\" ä¸å¾—被用åšæ­¤ wiki çš„é é¢å稱。",
+ "smw_noproperty": "\"$1\"ä¸èƒ½ç”¨ä½œæ­¤ wiki 中的一個屬性å稱。",
+ "smw_wrong_namespace": "此處åªå…許命å空間 \"$1\" 中的é é¢ã€‚",
+ "smw_manytypes": "此屬性定義了一種以上類型。",
+ "smw_emptystring": "ä¸æŽ¥å—空字元串。",
+ "smw_notinenum": "「$1ã€ä¸åœ¨ç”¨æ–¼ã€Œ$3ã€å±¬æ€§[[Property:Allows value|所å…許值]]的清單($2)中。",
+ "smw-datavalue-constraint-error-allows-value-list": "「$1ã€ä¸åœ¨ç”¨æ–¼ã€Œ$3ã€å±¬æ€§[[Property:Allows value|所å…許值]]的清單($2)中。",
+ "smw-datavalue-constraint-error-allows-value-range": "「$1ã€æ²’有符åˆç”¨æ–¼é™åˆ¶å±¬æ€§ã€Œ$3ã€çš„[[Property:Allows value|å…許值]]所指定的「$2ã€ç¯„åœã€‚",
+ "smw_noboolean": "\"$1\" ä¸æ˜¯å¸ƒæž— (是éž) 值",
+ "smw_true_words": "true,t,yes,y,是,真,å°",
+ "smw_false_words": "false,f,no,n,å¦,å‡,錯",
+ "smw_nofloat": " \"$1\" ä¸æ˜¯ä¸€å€‹æ•¸å­—。",
+ "smw_infinite": " \"$1\" 數值éŽå¤§ï¼Œç„¡æ³•æ”¯æ´ã€‚",
+ "smw_unitnotallowed": "\"$1\"未被è²æ˜Žç‚ºæ­¤å±¬æ€§çš„一個有效的度é‡å–®ä½ã€‚",
+ "smw_nounitsdeclared": "此屬性沒有è²æ˜Žä»»ä½•åº¦é‡å–®ä½ã€‚",
+ "smw_novalues": "未設定數值。",
+ "smw_nodatetime": "無法ç†è§£æ—¥æœŸ \"$1\"",
+ "smw_toomanyclosing": "查詢中 \"$1\" éŽå¤šã€‚",
+ "smw_noclosingbrackets": "你的查詢中有未符åˆ\"]]\"çš„\"<nowiki>[[</nowiki>\"。",
+ "smw_misplacedsymbol": "符號 \"$1\" çš„ä½ç½®ç„¡æ•ˆã€‚",
+ "smw_unexpectedpart": "查詢的 \"$1\" 部分無法ç†è§£ã€‚\nçµæžœå¯èƒ½èˆ‡é æ–™çš„ä¸åŒã€‚",
+ "smw_emptysubquery": "部份å­æŸ¥è©¢çš„æ¢ä»¶ç„¡æ•ˆã€‚",
+ "smw_misplacedsubquery": "部份å­æŸ¥è©¢ä½¿ç”¨åœ¨ä¸å…許å­æŸ¥è©¢çš„地方。",
+ "smw_valuesubquery": "å­æŸ¥è©¢ä¸æ”¯æ´å±¬æ€§ \"$1\" 的值",
+ "smw_badqueryatom": "部份查詢 \"<nowiki>[[...]]</nowiki>\" 無法ç†è§£ã€‚",
+ "smw_propvalueproblem": "屬性 \"$1\" 的值無法ç†è§£ã€‚",
+ "smw_noqueryfeature": "æ­¤ wiki ä¸æ”¯æ´ä¸€äº›æŸ¥è©¢åŠŸèƒ½ï¼ŒæŸ¥è©¢è¢«éƒ¨åˆ†åœæ­¢ ($1) 。",
+ "smw_noconjunctions": "æ­¤ wiki ä¸æ”¯æ´æŸ¥è©¢ä¸­çš„åˆå–,查詢被部分åœæ­¢ ($1) 。",
+ "smw_nodisjunctions": "æ­¤ wiki ä¸æ”¯æ´æŸ¥è©¢ä¸­çš„æžå–,查詢被部分åœæ­¢ ($1) 。",
+ "smw_querytoolarge": "由于此 wiki 在査詢大å°æˆ–深度上的é™åˆ¶ï¼Œä¸‹åˆ—{{PLURAL:$2|查詢準則}}無法列入考慮:<code>$1</code>。",
+ "smw_notemplategiven": "為使查詢格å¼æ­£å¸¸å·¥ä½œï¼Œè«‹ç‚ºåƒæ•¸ \"模æ¿\" 賦值。",
+ "smw_db_sparqlqueryproblem": "無法從 SPARQL 資料庫ç²å¾—查詢çµæžœã€‚這個錯誤å¯èƒ½æ˜¯æš«æ™‚的,也å¯èƒ½æ˜¯è³‡æ–™åº«è»Ÿé«”的錯誤造æˆçš„。",
+ "smw_db_sparqlqueryincomplete": "本查詢太éŽè¤‡é›œå› è€Œè¢«ä¸­æ­¢ï¼Œå¯èƒ½æœƒå› æ­¤éºå¤±éƒ¨åˆ†çµæžœã€‚如果å¯èƒ½ï¼Œè«‹å˜—試改用一個較簡單的查詢。",
+ "smw_type_header": "類型 \"$1\" 的屬性",
+ "smw_typearticlecount": "使用此類型顯示 $1 {{PLURAL:$1|property|屬性}}",
+ "smw_attribute_header": "使用屬性 \"$1\" çš„é é¢",
+ "smw_attributearticlecount": "使用此屬性顯示 $1 {{PLURAL:$1|page|é é¢}}",
+ "smw-propertylist-subproperty-header": "å­å±¬æ€§",
+ "smw-propertylist-redirect-header": "åŒç¾©è©ž",
+ "smw-propertylist-error-header": "ä¸é©ç•¶åˆ†é…çš„é é¢",
+ "smw-propertylist-count": "顯示 $1 個相關{{PLURAL:$1|實體|實體}}。",
+ "smw-propertylist-count-with-restricted-note": "顯示 $1 個相關{{PLURAL:$1|實體|實體}}(有更多實體å¯ç”¨ï¼Œä½†è¢«é™åˆ¶åƒ…顯示 \"$2\" 個實體)。",
+ "smw-propertylist-count-more-available": "顯示 $1 個相關{{PLURAL:$1|實體|實體}}(有更多實體å¯ç”¨ï¼‰ã€‚",
+ "exportrdf": "輸出é é¢åˆ° RDF",
+ "smw_exportrdf_docu": "這個é é¢ç”¨æ–¼ä»¥ RDF æ ¼å¼ç²å¾—é é¢è³‡æ–™ã€‚\n在下方文字框中輸入標題以匯出é é¢ï¼Œæ¯è¡Œä¸€å€‹æ¨™é¡Œã€‚",
+ "smw_exportrdf_recursive": "éžæ­¸åŒ¯å‡ºæ‰€æœ‰ç›¸é—œé é¢ã€‚\n注æ„:çµæžœæª”案會很大。",
+ "smw_exportrdf_backlinks": "åŒæ™‚匯出與匯出é é¢ç›¸é—œçš„所有é é¢ã€‚\n產生å¯ç€è¦½çš„ RDF。",
+ "smw_exportrdf_lastdate": "ä¸è¦åŒ¯å‡ºè‡ªæŒ‡å®šæ™‚間後無變更的é é¢ã€‚",
+ "smw_exportrdf_submit": "匯出",
+ "uriresolver": "URI 解æžå™¨",
+ "properties": "屬性",
+ "smw_properties_docu": "本 wiki 使用以下屬性。",
+ "smw_property_template": "類型為 $2 的屬性 $1 (å‡ºç¾ $3 次)",
+ "smw_propertylackspage": "所有屬性å‡æ‡‰æœ‰ä¸€å€‹é é¢æè¿°ï¼",
+ "smw_propertylackstype": "此屬性未指定類型 (ç›®å‰é è¨­ç‚ºé¡žåž‹ $1)",
+ "smw_propertyhardlyused": "此屬性在本 wiki 中ä¸å¸¸ä½¿ç”¨ã€‚",
+ "smw-property-name-invalid": "無法使用屬性 $1 (無效的屬性å稱)。",
+ "smw-property-name-reserved": "「$1ã€è¢«åˆ—舉為ä¿ç•™å稱,且ä¸æ‡‰ç”¨ä½œå±¬æ€§ã€‚以下[https://www.semantic-mediawiki.org/wiki/Help:Property_naming 幫助é é¢]å¯èƒ½åŒ…å«æœ‰é—œç‚ºä½•è©²å稱是ä¿ç•™å稱的資訊。",
+ "smw-sp-property-searchform": "顯示包å«ä»¥ä¸‹å…§å®¹çš„屬性:",
+ "smw-sp-property-searchform-inputinfo": "輸入å€åˆ†å¤§å°å¯«ä¸”當用來æœå°‹æ™‚åªæœƒé¡¯ç¤ºç¬¦åˆæ¢ä»¶çš„屬性。",
+ "smw-special-property-searchform": "顯示包å«ä»¥ä¸‹å…§å®¹çš„屬性:",
+ "smw-special-property-searchform-inputinfo": "輸入å€åˆ†å¤§å°å¯«ä¸”當用來æœå°‹æ™‚åªæœƒé¡¯ç¤ºç¬¦åˆæ¢ä»¶çš„屬性。",
+ "smw-special-property-searchform-options": "é¸é …",
+ "smw-special-wantedproperties-filter-label": "篩é¸ï¼š",
+ "smw-special-wantedproperties-filter-none": "ç„¡",
+ "smw-special-wantedproperties-filter-unapproved": "未批准",
+ "smw-special-wantedproperties-filter-unapproved-desc": "篩é¸ç”¨æ–¼æ¬Šå¨æ¨¡å¼æ™‚連接的é¸é …。",
+ "concepts": "概念",
+ "smw-special-concept-docu": "[https://www.semantic-mediawiki.org/wiki/Help:Concepts 概念]å¯è¢«çœ‹åšæ˜¯ \"動態分類\",å³éžæ‰‹å‹•å»ºç«‹çš„é é¢é›†åˆï¼Œæ˜¯ç”± Semantic MediaWiki 從指定查詢中的æ述所找出。",
+ "smw-special-concept-header": "概念清單",
+ "smw-special-concept-count": "以下列出{{PLURAL:$1|1 個概念|$1 個概念}}。",
+ "smw-special-concept-empty": "查無概念。",
+ "unusedproperties": "未使用的屬性",
+ "smw-unusedproperties-docu": "æ­¤é é¢åˆ—出已è²æ˜Žï¼Œæ²’有被其它é é¢ä½¿ç”¨çš„[https://www.semantic-mediawiki.org/wiki/Unused_properties 未使用屬性]。差別內容請åƒè¦‹[[Special:Properties|全部屬性]]或[[Special:WantedProperties|所需屬性]]特殊é é¢ã€‚",
+ "smw-unusedproperty-template": "類型為 $2 的屬性 $1",
+ "wantedproperties": "需è¦çš„屬性",
+ "smw-wantedproperties-docu": "æ­¤é é¢åˆ—出在 wiki 中使用,但沒有é é¢æè¿°çš„[https://www.semantic-mediawiki.org/wiki/Wanted_properties 所需屬性]。差別內容請åƒè¦‹[[Special:Properties|全部屬性]]或[[Special:UnusedProperties|未使用屬性]]特殊é é¢ã€‚",
+ "smw-wantedproperty-template": "$1 ($2 {{PLURAL:$2|use|使用}})",
+ "smw-special-wantedproperties-docu": "æ­¤é é¢åˆ—出在 wiki 中使用,但沒有é é¢æè¿°çš„[https://www.semantic-mediawiki.org/wiki/Wanted_properties 所需屬性]。差別內容請åƒè¦‹[[Special:Properties|全部屬性]]或[[Special:UnusedProperties|未使用屬性]]特殊é é¢ã€‚",
+ "smw-special-wantedproperties-template": "$1($2 {{PLURAL:$2|次使用}})",
+ "smw_purge": "é‡æ–°æ•´ç†",
+ "smw-purge-failed": "é‡æ–°æ•´ç†å¤±æ•—",
+ "types": "é¡žåž‹",
+ "smw_types_docu": "[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes å¯ç”¨è³‡æ–™é¡žåž‹]的列表,æ¯ç¨®[https://www.semantic-mediawiki.org/wiki/Help:Datatype é¡žåž‹]代表唯一的屬性集,以æ述儲存方é¢çš„值,並顯示éºå‚³è‡³åˆ†é…内容的特徵。",
+ "smw-special-types-no-such-type": "「$1ã€ç‚ºæœªçŸ¥ï¼Œæˆ–是尚未指定æˆæœ‰æ•ˆçš„資料型態。",
+ "smw-statistics": "Semantic 語義統計",
+ "smw-statistics-property-instance": "屬性{{PLURAL:$1|值|值}} (總計)",
+ "smw-statistics-property-total": "[[Special:Properties|{{PLURAL:$1|屬性|屬性}}]] (總計)",
+ "smw-statistics-property-total-legacy": "{{PLURAL:$1|屬性|屬性}} (總計)",
+ "smw-statistics-property-used": "[[Special:Properties|{{PLURAL:$1|屬性|屬性}}]] (使用了至少一個值)",
+ "smw-statistics-property-page": "{{PLURAL:$1|Property|屬性}} (使用é é¢è¨»å†Š)",
+ "smw-statistics-property-type": "{{PLURAL:$1|Property|屬性}} (指派給資料型態)",
+ "smw-statistics-query-inline-legacy": "$1 æ¢æŸ¥è©¢",
+ "smw-statistics-query-inline": "[[Property:Has query|{{PLURAL:$1|查詢}}]]",
+ "smw-statistics-query-size": "隊列長度",
+ "smw-statistics-concept-count-legacy": "{{PLURAL:$1|概念|概念}}",
+ "smw-statistics-concept-count": "[[Special:Concepts|{{PLURAL:$1|概念}}]]",
+ "smw-statistics-subobject-count": "[[Property:Has subobject|{{PLURAL:$1|å­ç‰©ä»¶}}]]",
+ "smw-statistics-subobject-count-legacy": "{{PLURAL:$1|å­ç‰©ä»¶|å­ç‰©ä»¶}}",
+ "smw-statistics-datatype-count": "[[Special:Types|{{PLURAL:$1|資料型態|資料型態}}]]",
+ "smw-statistics-error-count": "{{PLURAL:$1|屬性值}}([[Special:ProcessingErrorList|{{PLURAL:$1|ä¸æ­£ç¢ºæ³¨é‡‹}}]])",
+ "smw-statistics-error-count-legacy": "{{PLURAL:$1|屬性值|屬性值}}({{PLURAL:$1|ä¸æ­£ç¢ºæ³¨é‡‹|ä¸æ­£ç¢ºæ³¨é‡‹}})",
+ "smw-statistics-delete-count": "éŽæ™‚çš„{{PLURAL:$1|實體|實體}}(標記以删除)",
+ "smw_uri_doc": "URI 分æžå™¨å¯¦ç¾[$1 W3C httpRange-14 標記尋找]。它將處ç†é‚£äº›æ²’有網站內容的é ã€‚",
+ "ask": "語æ„æœå°‹",
+ "smw-ask-help": "此章節包å«ä¸€äº›å¹«åŠ©è§£é‡‹å¦‚何使用 <code>#ask</code> 語法的連çµã€‚\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages é¸æ“‡é é¢]介紹如何é¸æ“‡é é¢ï¼Œä¸¦æ§‹é€ æ¢ä»¶\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators æœç´¢æ“作]列舉å¯ç”¨çš„è’ç´¢æ“作,包å«ç¯„åœæŸ»è©¢å’Œè¬ç”¨å­—元査詢\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Displaying_information 顯示資訊]概述列å°è¼¸å‡ºèªªæ˜Žçš„用法åŠæ ¼å¼é¸é …",
+ "smw_ask_sortby": "æŒ‰åˆ—æŽ’åº (é¸å¡«)",
+ "smw_ask_ascorder": "å‡å†ª",
+ "smw_ask_descorder": "é™å†ª",
+ "smw-ask-order-rand": "隨機",
+ "smw_ask_submit": "æœå°‹çµæžœ",
+ "smw_ask_editquery": "編輯查詢",
+ "smw_add_sortcondition": "[增加排åºæ¢ä»¶]",
+ "smw-ask-sort-add-action": "增加排åºæ¢ä»¶",
+ "smw_ask_hidequery": "éš±è—查詢(密集檢視)",
+ "smw_ask_help": "查詢幫助",
+ "smw_ask_queryhead": "æ¢ä»¶",
+ "smw_ask_printhead": "列å°è¼¸å‡ºé¸æ“‡",
+ "smw_ask_printdesc": " (æ¯è¡Œå¢žåŠ ä¸€å€‹æŸ¥è©¢å)",
+ "smw_ask_format_as": "æ ¼å¼ç‚ºï¼š",
+ "smw_ask_defaultformat": "é è¨­",
+ "smw_ask_otheroptions": "其他é¸é …",
+ "smw-ask-otheroptions-info": "本節包å«æ”¹è®Šåˆ—å°è¼¸å‡ºèªžå¥çš„é¸é …。å¯é€éŽåœ¨å®ƒå€‘上é¢æ‡¸åœæ»‘鼠來查看åƒæ•¸æ述。",
+ "smw-ask-otheroptions-collapsed-info": "請使用加號圖示來檢視所有å¯ç”¨çš„é¸é …",
+ "smw_ask_show_embed": "顯示嵌入代碼",
+ "smw_ask_hide_embed": "éš±è—嵌入的代碼",
+ "smw_ask_embed_instr": "使用以下代碼將查詢嵌入維基é é¢ã€‚",
+ "smw-ask-delete": "移除",
+ "smw-ask-sorting": "排åº",
+ "smw-ask-options": "é¸é …",
+ "smw-ask-options-sort": "排åºé¸é …",
+ "smw-ask-format-options": "æ ¼å¼å’Œé¸é …",
+ "smw-ask-parameters": "åƒæ•¸",
+ "smw-ask-search": "æœå°‹",
+ "smw-ask-debug": "除錯",
+ "smw-ask-debug-desc": "產生查詢除錯訊æ¯",
+ "smw-ask-no-cache": "åœç”¨æŸ¥è©¢å¿«å–",
+ "smw-ask-no-cache-desc": "沒有査詢快å–çš„çµæžœ",
+ "smw-ask-result": "çµæžœ",
+ "smw-ask-empty": "清空所有記錄",
+ "smw-ask-download-link-desc": "以 $1 æ ¼å¼ä¾†ä¸‹è¼‰æŸ¥è©¢çµæžœ",
+ "smw-ask-format": "æ ¼å¼",
+ "smw-ask-format-selection-help": "有關é¸å®šæ ¼å¼çš„幫助:$1",
+ "smw-ask-condition-change-info": "æ¢ä»¶å·²æ”¹è®Šï¼Œæœå°‹å¼•æ“Žéœ€è¦é‡æ–°é‹è¡ŒæŸ»è©¢ä¾†ç”¢ç”ŸåŒ¹é…æ–°è¦æ±‚çš„çµæžœã€‚",
+ "smw-ask-input-assistance": "輸入幫助",
+ "smw-ask-condition-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 輸入幫助]æ供於列å°è¼¸å‡ºã€æŽ’åºã€æ¢ä»¶æ¬„ä½æ–¹é¢ã€‚æ¢ä»¶æ¬„ä½éœ€è¦ä»¥ä¸‹å­—首之一:",
+ "smw-ask-condition-input-assistance-property": "<code>p:</code> 檢索屬性建議(例如:<code>[[p:Has ...</code>)",
+ "smw-ask-condition-input-assistance-category": "<code>c:</code> 來索å–分類建議",
+ "smw-ask-condition-input-assistance-concept": "<code>con:</code> 來索å–概念建議",
+ "smw-ask-format-change-info": "æ ¼å¼å·²è®Šå‹•ï¼Œä¸¦ä¸”需è¦å†æ¬¡åŸ·è¡ŒæŸ¥è©¢ä¾†æ¯”å°æ–°åƒæ•¸å’Œè¦–覺化é¸é …。",
+ "smw-ask-format-export-info": "所é¸æ ¼å¼ç‚ºä¸å…·æœ‰è¦–覺化呈ç¾çš„匯出格å¼ï¼Œå› æ­¤å…§å®¹çµæžœåƒ…能æ供下載。",
+ "smw-ask-query-search-info": "查詢 <code><nowiki>$1</nowiki></code> ç”± {{PLURAL:$3|1=<code>$2</code>(來自快å–)|<code>$2</code>(來自快å–)|<code>$2</code>}}在 $4 {{PLURAL:$4|秒|秒}}內回應。",
+ "searchbyproperty": "按屬性查詢",
+ "processingerrorlist": "處ç†éŒ¯èª¤æ¸…å–®",
+ "propertylabelsimilarity": "屬性標籤相似度報告",
+ "smw-processingerrorlist-intro": "以下清單æ供有關出ç¾åœ¨é€£æŽ¥ [https://www.semantic-mediawiki.org/ Semantic MediaWiki] 時的處ç†éŒ¯èª¤æ–¹é¢æ¦‚è¦ã€‚建議定期查看此清單,並修正無效的值註解。",
+ "smw_sbv_docu": "æœå°‹æ‰€æœ‰å·²è¨­å®šå±¬æ€§å’Œå€¼çš„é é¢ã€‚",
+ "smw_sbv_novalue": "為此屬性輸入一個有效值,或者顯示所有 \"$1\" 的屬性值。",
+ "smw_sbv_displayresult": "有值為 \"$2\" 的屬性 \"$1\" 的所有é é¢æ¸…å–®",
+ "smw_sbv_displayresultfuzzy": "有值為 \"$2\" 的屬性 \"$1\" 的所有é é¢æ¸…單。\n由於精確相符的é é¢ä¸å¤šï¼ŒåŒæ™‚列出了部分有相似值的é é¢ã€‚",
+ "smw_sbv_property": "屬性:",
+ "smw_sbv_value": "值:",
+ "smw_sbv_submit": "æœå°‹çµæžœ",
+ "browse": "ç€è¦½ wiki",
+ "smw_browselink": "ç€è¦½å±¬æ€§",
+ "smw_browse_article": "輸入ç€è¦½èµ·å§‹é é¢å稱。",
+ "smw_browse_go": "進入",
+ "smw_browse_show_incoming": "顯示連入屬性",
+ "smw_browse_hide_incoming": "éš±è—連入屬性",
+ "smw_browse_no_outgoing": "這個é é¢æ²’有設定屬性。",
+ "smw_browse_no_incoming": "沒有屬性連çµåˆ°æ­¤é ã€‚",
+ "smw-browse-from-backend": "訊æ¯ç›®å‰æ­£å¾žå¾Œç«¯å–得。",
+ "smw-browse-intro": "æ­¤é é¢æ供有關主題或實體實例的詳情,請輸入物件å稱來檢查。",
+ "smw-browse-invalid-subject": "主題驗證返回“$1â€éŒ¯èª¤ã€‚",
+ "smw-browse-api-subject-serialization-invalid": "主題有無效的åºåˆ—化格å¼ã€‚",
+ "smw-browse-js-disabled": "似乎是 Javascript 被åœç”¨æˆ–ç„¡æ³•ä½¿ç”¨ï¼Œæˆ‘å€‘å»ºè­°ä½¿ç”¨æ”¯æ´ Javascript çš„ç€è¦½å™¨ã€‚其它é¸é …有在 [https://www.semantic-mediawiki.org/wiki/Help:$smwgBrowseByApi <code>$smwgBrowseByApi</code>] 設置åƒæ•¸é é¢è£¡è¢«æåŠåˆ°ã€‚",
+ "smw-browse-show-group": "顯示群組",
+ "smw-browse-hide-group": "éš±è—群組",
+ "smw-noscript": "æ­¤é é¢æˆ–æ“ä½œéœ€è¦ Javascript æ‰èƒ½åŸ·è¡Œï¼Œè«‹åœ¨æ‚¨çš„ç€è¦½å™¨ä¸Šå•Ÿç”¨ Javascriptï¼Œæˆ–æ˜¯ä½¿ç”¨æ”¯æ´ Javascript çš„ç€è¦½å™¨ï¼Œä»¥è®“ä¾æ“šè¦æ±‚所æ供的功能å¯é‹ä½œã€‚進一步的詳細內容,請查看 [https://www.semantic-mediawiki.org/wiki/Help:Noscript noscript] 說明é é¢ã€‚",
+ "smw_inverse_label_default": "çš„ $1",
+ "smw_inverse_label_property": "逆屬性標籤",
+ "pageproperty": "é é¢å±¬æ€§æœå°‹",
+ "smw_pp_docu": "輸入é é¢å’Œå±¬æ€§ï¼Œæˆ–是僅輸入屬性來索å–所有分é…的值。",
+ "smw_pp_from": "從é é¢ï¼š",
+ "smw_pp_type": "屬性:",
+ "smw_pp_submit": "æœå°‹çµæžœ",
+ "smw_result_prev": "上一個",
+ "smw_result_next": "下一個",
+ "smw_result_results": "çµæžœ",
+ "smw_result_noresults": "沒有çµæžœã€‚",
+ "smwadmin": "管ç†ä¸¦ç¶­è­·åŠŸèƒ½",
+ "smw-admin-statistics-job-title": "任務統計",
+ "smw-admin-statistics-job-docu": "任務統計顯示出有關已安排但尚未執行的 Semantic MediaWiki 任務。任務數目å¯èƒ½æœƒæœ‰äº›ä¸ç²¾æº–或是包å«åˆ°å¤±æ•—的嘗試,請查閱[https://www.mediawiki.org/wiki/Manual:Job_queue 手冊]來ç²å¾—進一步資訊。",
+ "smw-admin-statistics-querycache-title": "查詢快å–統計",
+ "smw-admin-statistics-querycache-disabled": "[https://www.semantic-mediawiki.org/wiki/QueryCache 查詢快å–]在此 wiki 未啟用,藉此沒有å¯ç”¨çš„統計內容。",
+ "smw-admin-statistics-querycache-explain": "å¿«å–統計為å«æœ‰è‡¨æ™‚累計內容和è¡ç”Ÿè³‡æ–™ï¼ŒåŒ…括: \n* \"misses\" 是嘗試從快å–ç´¢å–資料但帶有無效回應,而必須直接åšå„²å­˜ï¼ˆè³‡æ–™åº«ã€ä¸‰é‡å„²å­˜æˆ–其它)檢索的總數\n* \"deletes\" 代表回收快å–æ“作(é€éŽæ¸…除或是ä¾é æŸ¥è©¢ï¼‰çš„總數é‡\n* \"hits\" å«æœ‰ä¾†è‡ªå…§åµŒï¼ˆä¾†è‡ªä»¥ wiki é é¢çš„查詢呼å«ï¼‰æˆ–éžå…§åµŒï¼ˆå¦‚有啟用,由åƒæ˜¯ Special:Ask é é¢æˆ–是 API 的請求) 來æºç´¢å–çš„å¿«å–數é‡\n* \"medianRetrievalResponseTime\" 是用於超éŽæ”¶é›†éŽç¨‹æ™‚間段è½çš„å¿«å–ã€éžå¿«å–檢索請求回應時間(以秒為單ä½ï¼‰ä¸­ä½æ•¸çš„å–å‘值\n* \"noCache\" 表示沒有嘗試從快å–來索å–çµæžœçš„次數(limit=0 的查詢,或是 'no-cache' 等其它é¸é …)",
+ "smw-admin-permission-missing": "出於缺少權é™ç·£æ•…,被阻止存å–æ­¤é é¢ï¼Œè«‹åƒé–±[https://www.semantic-mediawiki.org/wiki/Help:Permissions 權é™]說明é é¢ä¾†ç²å¾—更多有關所需設定的詳情。",
+ "smw-admin-setupsuccess": "儲存引擎已設定。",
+ "smw_smwadmin_return": "返回 $1",
+ "smw_smwadmin_updatestarted": "更新語æ„資料éŽç¨‹é–‹å§‹ã€‚\n所有已儲存資料將被按需é‡å»ºæˆ–修復。\nä½ å¯ä»¥é€éŽæœ¬ç‰¹æ®Šé é¢è¿½è¹¤æ›´æ–°ç¨‹åºã€‚",
+ "smw_smwadmin_updatenotstarted": "已有更新程åºæ­£åœ¨åŸ·è¡Œï¼Œè«‹å‹¿å»ºç«‹å¦ä¸€å€‹ã€‚",
+ "smw_smwadmin_updatestopped": "å·²åœæ­¢æ‰€æœ‰æ›´æ–°ç¨‹åºã€‚",
+ "smw_smwadmin_updatenotstopped": "è‹¥è¦åœæ­¢æ›´æ–°ç¨‹åºï¼Œæ‚¨å¿…須勾é¸æ ¸å–方塊來表示您確實如此è¦æ±‚。",
+ "smw-admin-docu": "這個特殊é é¢ç‚ºæ‚¨å®‰è£ã€å‡ç´šã€ç¶­è­·å’Œä½¿ç”¨ <a href=\"https://www.semantic-mediawiki.org\">Semantic MediaWiki</a> 時æ供說明,並æ供進一步的管ç†åŠŸèƒ½å’Œä»»å‹™ï¼Œä»¥åŠçµ±è¨ˆã€‚\n請記得在執行管ç†åŠŸèƒ½å‰å‚™ä»½å¥½è³‡æ–™ã€‚",
+ "smw-admin-environment": "軟體環境",
+ "smw-admin-db": "資料庫設定",
+ "smw-admin-db-preparation": "表格åˆå§‹åŒ–正在進行,在ä¾æ“šå¤§å°ä¾†é¡¯ç¤ºå‡ºçµæžœä»¥åŠå¯èƒ½é€²è¡Œçš„表格最佳化之å‰ï¼Œå¯èƒ½æœƒèŠ±è²»ä¸€æ®µæ™‚é–“ 。",
+ "smw-admin-dbdocu": "Semantic MediaWiki 需è¦æœ¬èº«æ‰€æŒæœ‰çš„資料庫架構來儲存語æ„資料(這ç¨ç«‹æ–¼ MediaWiki,ä¸æœƒå° MediaWiki 的安è£éƒ¨ä»½é€ æˆå½±éŸ¿ï¼‰\n這個設定功能å¯å¤šæ¬¡åŸ·è¡Œï¼Œä¸æœƒé€ æˆä»»ä½•å±å®³ã€‚但是åªéœ€åœ¨å®‰è£æˆ–å‡ç´šæ™‚執行一次å³å¯ã€‚",
+ "smw-admin-permissionswarn": "如果æ“作因 SQL 錯誤而失敗,å¯èƒ½æ˜¯æ‚¨ wiki 所用的資料庫使用者(å¯åœ¨æ‚¨çš„ LocalSettings.php 檔案上檢查)沒有相應權é™ã€‚您å¯ä»¥å‘該使用者賦予建立和刪除表格的權é™ï¼Œæˆ–臨時將資料庫 root 使用者的登入資訊輸入在 LocalSettings.php 檔案,或是使用維護腳本 <code>setupStore.php</code>,該腳本å¯ä»¥ä½¿ç”¨ LocalSettings.php 中的資訊。",
+ "smw-admin-dbbutton": "åˆå§‹åŒ–或å‡ç´šè¡¨æ ¼",
+ "smw-admin-announce": "發佈 wiki",
+ "smw-admin-announce-text": "若您的 wiki 是公開的,您å¯ä»¥åœ¨ <a href=\"https://wikiapiary.com\">WikiApiary</a> 註冊它,這是用來追蹤 wiki çš„ wiki。",
+ "smw-admin-deprecation-notice-title": "åœç”¨é€šçŸ¥",
+ "smw-admin-deprecation-notice-docu": "下é¢æ®µè½åŒ…å«å·²è¢«æ£„用或移除;但在此 wiki 上發ç¾æœ‰è¢«å•Ÿå‹•çš„設定。今起往後任何發行版本都將移除å°é€™äº›é…置的支æ´ã€‚",
+ "smw-admin-deprecation-notice-config-notice": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> 已棄用,並將在 $2 版本裡移除",
+ "smw-admin-deprecation-notice-config-notice-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> 將移除(或替æ›ï¼‰ä»¥ä¸‹{{PLURAL:$2|é¸é …}}:",
+ "smw-admin-deprecation-notice-config-notice-option-list": "<code>$1</code>已棄用,並將在$2中移除",
+ "smw-admin-deprecation-notice-config-replacement": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> 已被替æ›ç‚º <code>[https://www.semantic-mediawiki.org/wiki/Help:$2 $2]</code>",
+ "smw-admin-deprecation-notice-config-replacement-option": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code> {{PLURAL:$2|é¸é …}}:",
+ "smw-admin-deprecation-notice-config-replacement-option-list": "<code>$1</code> 正在替æ›ç‚º <code>$2</code>",
+ "smw-admin-deprecation-notice-config-removal": "<code>[https://www.semantic-mediawiki.org/wiki/Help:$1 $1]</code>已在 $2 版本中被移除",
+ "smw-admin-deprecation-notice-title-notice": "å³å°‡é€²è¡Œçš„更改",
+ "smw-admin-deprecation-notice-title-notice-explanation": "ç¾å·²æª¢æ¸¬åˆ°æ­¤ wiki 使用了以下設定,並計畫在今後的發佈版本中移除或更改。",
+ "smw-admin-deprecation-notice-title-replacement": "已替æ›æˆ–é‡æ–°å‘½å的設定",
+ "smw-admin-deprecation-notice-title-replacement-explanation": "以下段è½åŒ…å«å·²é‡æ–°å‘½å或或是其它變動的設定,建議立å³æ›´æ–°å…¶å稱或格å¼ã€‚",
+ "smw-admin-deprecation-notice-title-removal": "已移除的設定",
+ "smw-admin-deprecation-notice-title-removal-explanation": "列出的設定已在上一個發佈版本中移除,但在此 wiki 上被檢測到使用著。",
+ "smw-smwadmin-refresh-title": "資料修復與更新",
+ "smw_smwadmin_datarefresh": "資料é‡å»º",
+ "smw_smwadmin_datarefreshdocu": "å¯ä»¥åŸºæ–¼ç¶­åŸºå…ˆæœ‰å…§å®¹å°æ‰€æœ‰ Semantic MediaWki 資料進行é‡ç½®ã€‚\n這個功能å°ä¿®å¾©æ壞的資料,或者在軟體å‡ç´šï¼Œå…§éƒ¨æ ¼å¼æ”¹è®Šæ™‚更新資料éžå¸¸æœ‰ç”¨ã€‚\n更新時é€é é¢åŸ·è¡Œçš„,需è¦ä¸€æ®µæ™‚間。\n下方將顯示更新是å¦æ­£åœ¨é€²è¡Œï¼Œä¸¦å…許你開始或終止更新。 (此功能å¯ä»¥è¢«ç¶²ç«™ç®¡ç†å“¡åœç”¨)",
+ "smw_smwadmin_datarefreshprogress": "<strong>有一個更新正在進行。</strong>\n更新程åºå¯èƒ½æœƒå¾ˆæ…¢ï¼Œå› ç‚ºè©²ç¨‹åºåƒ…在使用者訪å•ç¶­åŸºæ™‚å°å¡Šåœ°æ›´æ–°è³‡æ–™ã€‚\nå¯ä»¥ä½¿ç”¨ MediaWiki 維護腳本<code>runJobs.php</code> (使用é¸é … <code>--maxjobs 1000</code> é™åˆ¶æ¯æ‰¹æ›´æ–°æ•¸ç›®) 來加快更新完æˆé€Ÿåº¦ã€‚\nç›®å‰æ›´æ–°çš„估計進度:",
+ "smw_smwadmin_datarefreshbutton": "安排資料é‡å»º",
+ "smw_smwadmin_datarefreshstop": "åœæ­¢æ›´æ–°",
+ "smw_smwadmin_datarefreshstopconfirm": "是的,我{{GENDER:$1|確èª}}。",
+ "smw-admin-job-scheduler-note": "在此段è½çš„任務(已啟用)會é€éŽä»»å‹™ä½‡åˆ—進行,以é¿å…在執行期間出ç¾æ­»çµç‹€æ³ã€‚[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]負責程åºï¼Œåœ¨ç·Šè¦æ™‚å¯æŽ¡ç”¨ <code>runJobs.php</code> 維護用手稿檔案(å¦åƒè¦‹è¨­ç½®åƒæ•¸ <code>$wgRunJobsAsync</code>)。",
+ "smw-admin-outdateddisposal-title": "éŽæ™‚實體處置",
+ "smw-admin-outdateddisposal-intro": "部分æ“作(å°æ–¼å±¬æ€§é¡žåž‹çš„更改ã€wiki é é¢çš„移除ã€æˆ–錯誤值的糾正)的çµæžœæœƒåœ¨[https://www.semantic-mediawiki.org/wiki/Outdated_entities éŽæ™‚實體]上,並建議定期移除它們以釋放相關表格空間。",
+ "smw-admin-outdateddisposal-active": "éŽæ™‚實體處置任務已加入排程。",
+ "smw-admin-outdateddisposal-button": "安排處置",
+ "smw-admin-feature-disabled": "該功能已在此 wiki 上ç¦ç”¨ã€‚請查閱<a href=\"https://www.semantic-mediawiki.org/wiki/Help:$smwgAdminFeatures\">設定</a>幫助é é¢æˆ–è¯çµ¡ç³»çµ±ç®¡ç†å“¡ã€‚",
+ "smw-admin-propertystatistics-title": "屬性統計é‡å»º",
+ "smw-admin-propertystatistics-intro": "é‡å»ºæ•´å€‹å±¬æ€§ä½¿ç”¨çµ±è¨ˆä»¥åŠå…¶ä¸­çš„更新,並更正[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count usage 使用計數]屬性。",
+ "smw-admin-propertystatistics-active": "屬性統計é‡å»ºä»»å‹™å·²åŠ å…¥æŽ’程。",
+ "smw-admin-propertystatistics-button": "安排統計é‡å»º",
+ "smw-admin-fulltext-title": "é‡å»ºå…¨æ–‡æœå°‹",
+ "smw-admin-fulltext-intro": "從啟用[https://www.semantic-mediawiki.org/wiki/Full-text 全文æœç´¢]資料類型的屬性表格來é‡å»ºæœå°‹ç´¢å¼•ã€‚å°ç´¢å¼•è¦å‰‡çš„更改(改變的åœç”¨è©žã€æ–°çš„詞幹等)以åŠ/或是新的已添加或已變動表格,需è¦é‡æ–°é‹è¡Œæ­¤ä»»å‹™ã€‚",
+ "smw-admin-fulltext-active": "全文æœå°‹é‡å»ºä»»å‹™å·²åŠ å…¥æŽ’程。",
+ "smw-admin-fulltext-button": "排程é‡å»ºå…¨æ–‡",
+ "smw-admin-support": "ç²å¾—å”助",
+ "smw-admin-supportdocu": "å·²æä¾›å„種資æºä¾†å¹«åŠ©æ‚¨è§£å†³ä»¥ä¸‹å•é¡Œï¼š",
+ "smw-admin-installfile": "如果您安è£æ™‚é‡åˆ°å•é¡Œï¼Œè«‹å…ˆæª¢æŸ¥<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/INSTALL.md\">安è£æ–‡ä»¶</a>åŠ<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Installation\">安è£é é¢</a>中的指引。",
+ "smw-admin-smwhomepage": "Semantic MediaWiki 的完整使用者文件請查閱 <b><a href=\"https://www.semantic-mediawiki.org\">semantic-mediawiki.org</a></b>。",
+ "smw-admin-bugsreport": "程å¼å•é¡Œå¯åœ¨<a href=\"https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues\">å•é¡Œè¿½è¹¤</a>報告,<a href=\"https://www.semantic-mediawiki.org/wiki/Help:Reporting_bugs\">報告錯誤</a>é é¢æ供一些如何書寫有效å•é¡Œå ±å‘Šçš„指導。",
+ "smw-admin-questions": "如果有å•é¡Œæˆ–建議,å¯åŠ å…¥ Semantic MediaWiki <a href=\"https://sourceforge.net/p/semediawiki/mailman/semediawiki-user/\">使用者郵件清單</a>,或是<a href=\"https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki_chatroom\">èŠå¤©å®¤</a>的討論。",
+ "smw-admin-other-functions": "其它功能",
+ "smw-admin-supplementary-section-title": "補充功能",
+ "smw-admin-supplementary-section-subtitle": "核心功能",
+ "smw-admin-supplementary-section-intro": "此段è½æ‰€æä¾›é¡å¤–功能ä¸åœ¨æ‰€ç¶­è­·ç¯„åœä¹‹å…§ï¼Œä¸¦ä¸”å¯èƒ½æ˜¯åˆ—在[https://www.semantic-mediawiki.org/wiki/Help:Special:SemanticMediaWiki/Supplementary_functions 文檔]裡的部份函å¼ç‚ºå—到é™åˆ¶æˆ–是ä¸å¯ç”¨ï¼Œå› æ­¤åœ¨æ­¤ wiki 上ä¸å¯å–用。",
+ "smw-admin-supplementary-settings-title": "設置設定",
+ "smw-admin-supplementary-settings-intro": "<u>$1</u> 輸出用於 Semantic MediaWiki çš„å¯ç”¨è¨­å®šçš„集åˆæ¸…å–®",
+ "smw-admin-supplementary-operational-statistics-title": "æ“作統計",
+ "smw-admin-supplementary-operational-statistics-intro": "<u>$1</u>顯示擴充的統計集åˆ",
+ "smw-admin-supplementary-idlookup-title": "實體查找與處置",
+ "smw-admin-supplementary-idlookup-intro": "<u>$1</u>包å«æŸ¥æ‰¾ä¸¦è™•ç½®å€‹åˆ¥å¯¦é«”的功能",
+ "smw-admin-supplementary-duplookup-title": "é‡è¤‡é …ç›®",
+ "smw-admin-supplementary-duplookup-intro": "<u>$1</u> 列出歸類在實體表中å«æœ‰é‡è¤‡å…§å®¹çš„é …",
+ "smw-admin-supplementary-duplookup-docu": "æ­¤é é¢åˆ—舉來自[https://www.semantic-mediawiki.org/wiki/Help:Entity_table 實體表]的項目,它們被歸類為é‡è¤‡é …。é‡è¤‡é …(如有)應僅å¶ç„¶æœƒåœ¨è³‡æ–™åº«æ›´æ–°æœŸé–“çš„çµæŸéŽç¨‹è£¡ç™¼ç”Ÿï¼Œæˆ–是未æˆåŠŸçš„回退事項所導致。",
+ "smw-admin-supplementary-duplookup-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Duplicate_entities",
+ "smw-admin-supplementary-operational-statistics-cache-title": "å¿«å–統計",
+ "smw-admin-supplementary-operational-statistics-cache-intro": "<u>$1</u> 顯示出快å–相關統計",
+ "smw-admin-supplementary-elastic-title": "Elasticsearch",
+ "smw-admin-supplementary-elastic-section-subtitle": "Elasticsearch 功能",
+ "smw-admin-supplementary-elastic-intro": "<u>$1</u> 顯示設定與索引統計",
+ "smw-admin-supplementary-elastic-docu": "æ­¤é é¢åŒ…å«æœ‰é—œè¨­å®šçš„資訊ã€å°æ‡‰ã€ç‹€æ…‹ï¼Œä»¥åŠç›¸é—œé€£çµåˆ° Semantic MediaWiki 以åŠå…¶ [https://www.semantic-mediawiki.org/wiki/Help:ElasticStore <code>ElasticStore</code>] 之 Elasticsearch å¢é›†çš„索引統計。",
+ "smw-admin-supplementary-elastic-functions": "支æ´åŠŸèƒ½",
+ "smw-admin-supplementary-elastic-settings-title": "設定",
+ "smw-admin-supplementary-elastic-settings-intro": "<u>$1</u> ç”± Elasticsearch æ‰€ä½¿ç”¨ä¾†ç®¡ç† Semantic MediaWiki 索引",
+ "smw-admin-supplementary-elastic-mappings-title": "å°æ‡‰",
+ "smw-admin-supplementary-elastic-mappings-intro": "<u>$1</u> 用來列出索引與欄ä½å°æ‡‰",
+ "smw-admin-supplementary-elastic-mappings-docu": "æ­¤é é¢åŒ…å«èˆ‡ç›®å‰ç´¢å¼•ä¸€åŒä½¿ç”¨çš„欄ä½å°æ‡‰è©³æƒ…。å°æ‡‰æ‘˜è¦æ‡‰èˆ‡æŒ‡å®šå‡ºåœ¨ç´¢å¼•è£¡æœ€å¤§æ¬„ä½æ•¸çš„ <code>index.mapping.total_fields.limit</code> 一åŒé€£çµç›£æ¸¬ã€‚",
+ "smw-admin-supplementary-elastic-mappings-summary": "摘è¦",
+ "smw-admin-supplementary-elastic-mappings-fields": "欄ä½å°æ‡‰",
+ "smw-admin-supplementary-elastic-nodes-title": "節點",
+ "smw-admin-supplementary-elastic-nodes-intro": "<u>$1</u>顯示節點統計",
+ "smw-admin-supplementary-elastic-indices-title": "索引",
+ "smw-admin-supplementary-elastic-indices-intro": "<u>$1</u> æä¾›å¯ç”¨ç´¢å¼•ä»¥åŠå…¶çµ±è¨ˆå…§å®¹çš„概è¦",
+ "smw-admin-supplementary-elastic-statistics-title": "統計",
+ "smw-admin-supplementary-elastic-statistics-intro": "<u>$1</u> 顯示索引層級統計",
+ "smw-admin-supplementary-elastic-statistics-docu": "æ­¤é é¢æ供發生在索引層級上ä¸åŒæ“作的索引統計方é¢æ´žå¯Ÿï¼Œæ‰€å›žå‚³çµ±è¨ˆæ˜¯ä»¥åˆç´šå…§å®¹èˆ‡ç¸½è¨ˆé›†æˆçš„èšåˆã€‚[https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-stats.html 說明é é¢]包å«å¯ç”¨ç´¢å¼•çµ±è¨ˆçš„詳情æ述。",
+ "smw-admin-supplementary-elastic-status-replication": "複製狀態",
+ "smw-admin-supplementary-elastic-status-last-active-replication": "上一次有效的複製:$1",
+ "smw-admin-supplementary-elastic-status-refresh-interval": "é‡æ•´é–“隔:$1",
+ "smw-admin-supplementary-elastic-status-recovery-job-count": "ç©å£“修復任務:$1(估計值)",
+ "smw-admin-supplementary-elastic-status-file-ingest-job-count": "ç©å£“收å–(檔案)任務:$1(估計值)",
+ "smw-admin-supplementary-elastic-status-rebuild-lock": "複製已鎖定:$1(進行中的é‡å»ºï¼‰",
+ "smw-list-count": "æ¸…å–®åŒ…å« $1 個{{PLURAL:$1|é …ç›®|é …ç›®}}。",
+ "smw-list-count-from-cache": "æ¸…å–®åŒ…å« $1 個{{PLURAL:$1|實體}},並自快å–還原(UTC:$2)。",
+ "smw-property-label-uniqueness": "「$1ã€æ¨™ç±¤ç¬¦åˆäº†è‡³å°‘一個其它屬性表示內容。請查閱[https://www.semantic-mediawiki.org/wiki/Help:Property_uniqueness 幫助é é¢]瞭解如何解决該å•é¡Œã€‚",
+ "smw-property-label-similarity-title": "屬性標籤相似度報告",
+ "smw-property-label-similarity-intro": "<u>$1</u>å¯å°ç¾æœ‰å±¬æ€§æ¨™ç±¤çš„相似性進行計算",
+ "smw-property-label-similarity-threshold": "門檻:",
+ "smw-property-label-similarity-type": "顯示類型 ID",
+ "smw-property-label-similarity-noresult": "找ä¸åˆ°ç¬¦åˆæ‰€é¸é …目的结果。",
+ "smw-property-label-similarity-docu": "比較與報告兩個屬性標籤間的[https://www.semantic-mediawiki.org/wiki/Property_similarity 語法相似性](ä¸æ˜¯èªžç¾©ç›¸ä¼¼æ€§ï¼‰ï¼Œå¯å¹«åŠ©ç¯©é¸æŽ‰æ‹¼å¯«éŒ¯èª¤æˆ–æ供相åŒæ¦‚念的等效屬性(請åƒè¦‹ç‰¹æ®Šé é¢[[Special:Properties|屬性]]來闡明報告屬性的概念和用法)。å¯é€éŽèª¿æ•´è‡¨ç•Œå€¼ä¾†å¢žåŠ æˆ–減少相似è·é›¢ã€‚<code>[[Property:$1|$1]]</code> 用於從分æžä¸­æŽ’除屬性。",
+ "smw-admin-operational-statistics": "æ­¤é é¢åŒ…å«å­˜åœ¨æ–¼æˆ–來自 Semantic MediaWiki 相關功能的所收集é‹ä½œçµ±è¨ˆã€‚Wiki 特定統計的擴充清單å¯[[Special:Statistics|<b>在此</b>]]找到。",
+ "smw_adminlinks_datastructure": "資料çµæ§‹",
+ "smw_adminlinks_displayingdata": "資料顯示",
+ "smw_adminlinks_inlinequerieshelp": "直接æ’å…¥å¼æŸ¥è©¢å¹«åŠ©",
+ "smw-page-indicator-usage-count": "已估計[https://www.semantic-mediawiki.org/wiki/Help:Property_usage_count 使用次數]:{{PLURAL:$2|'''$2'''}}",
+ "smw-property-indicator-type-info": "{{PLURAL:$1|使用者|系統}}定義的屬性",
+ "smw-property-indicator-last-count-update": "估計使用數\n上一次更新於:$1",
+ "smw-concept-indicator-cache-update": "å¿«å–數\n上一次更新:$1",
+ "smw-createproperty-isproperty": "它是一個類型為$1的屬性。",
+ "smw-createproperty-allowedvals": "å…許的{{PLURAL:$1|屬性值為|屬性值為}}:",
+ "smw-paramdesc-category-delim": "分隔符",
+ "smw-paramdesc-category-template": "æ ¼å¼åŒ–這些æ¢ç›®çš„模æ¿",
+ "smw-paramdesc-category-userparam": "傳éžæ¨¡æ¿çš„åƒæ•¸",
+ "smw-info-par-message": "顯示的訊æ¯ã€‚",
+ "smw-info-par-icon": "顯示的圖示,\"訊æ¯\"或\"警告\"。",
+ "prefs-smw": "Semantic MediaWiki",
+ "prefs-general-options": "一般é¸é …",
+ "prefs-ask-options": "Special:Ask é¸é …",
+ "smw-prefs-intro-text": "[https://www.semantic-mediawiki.org/ 語義MediaWiki] (åŠç›¸é—œæ“´å……套件) 在部分指定的函數å„自æ供自訂功能。請åƒé–±[https://www.semantic-mediawiki.org/wiki/Help:User_preferences 使用說明é é¢]以ç²å–更多資訊。",
+ "smw-prefs-ask-options-tooltip-display": "顯示åƒæ•¸æ–‡å­—作為資訊æ示",
+ "smw-prefs-ask-options-compact-view-basic": "啟用基本密集檢視",
+ "smw-prefs-help-ask-options-compact-view-basic": "若有啟用,顯示在 Special:Ask 密集檢視的連çµç°¡åŒ–集åˆã€‚",
+ "smw-prefs-general-options-time-correction": "使用本地[[Special:Preferences#mw-prefsection-rendering|時間å移]]å好設定來啟用替特殊é é¢åšæ™‚間調正。",
+ "smw-prefs-general-options-jobqueue-watchlist": "在我的個人工具列顯示任務佇列",
+ "smw-prefs-help-general-options-jobqueue-watchlist": "若有啟用,將待定所é¸ä»»å‹™[https://www.semantic-mediawiki.org/wiki/Help:Job_queue_watchlist 清單]與所估計佇列大å°ä¸€åŒé¡¯ç¤ºã€‚",
+ "smw-prefs-general-options-disable-editpage-info": "在編輯é é¢åœç”¨å‰è¨€æ–‡å­—",
+ "smw-prefs-general-options-disable-search-info": "在標準æœå°‹é é¢ä¸Šåœç”¨èªžæ³•æ”¯æ´è³‡è¨Š",
+ "smw-prefs-general-options-suggester-textinput": "啟用語æ„實體的輸入幫助",
+ "smw-prefs-help-general-options-suggester-textinput": "若有啟用,å…許使用[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 輸入幫助]來查找來自輸入上下文的屬性ã€æ¦‚念ã€ä»¥åŠåˆ†é¡žã€‚",
+ "smw-ui-tooltip-title-property": "屬性",
+ "smw-ui-tooltip-title-quantity": "å–®ä½è½‰æ›",
+ "smw-ui-tooltip-title-info": "資訊",
+ "smw-ui-tooltip-title-service": "æœå‹™é€£çµ",
+ "smw-ui-tooltip-title-warning": "警告",
+ "smw-ui-tooltip-title-error": "錯誤",
+ "smw-ui-tooltip-title-parameter": "åƒæ•¸",
+ "smw-ui-tooltip-title-event": "事件",
+ "smw-ui-tooltip-title-note": "註釋",
+ "smw-ui-tooltip-title-legend": "圖例",
+ "smw-ui-tooltip-title-reference": "åƒè€ƒæ–‡ç»",
+ "smw_unknowntype": "此屬性類型「$1ã€ç„¡æ•ˆ",
+ "smw-concept-cache-text": "此概念總共有 $1 {{PLURAL:$1|個é é¢|個é é¢}},最後更新於 $2 $3。",
+ "smw_concept_header": "概念 \"$1\" çš„é é¢",
+ "smw_conceptarticlecount": "顯示於以下 $1 {{PLURAL:$1|個é é¢|個é é¢}}。",
+ "smw-qp-empty-data": "出於一些é¸æ“‡æ¨™æº–ä¸è¶³ï¼Œè«‹æ±‚資料無法顯示。",
+ "right-smw-admin": "å­˜å–管ç†å·¥ä½œé …目(Semantic MediaWiki)",
+ "right-smw-patternedit": "編輯維護所å…許正è¦è¡¨é”å¼èˆ‡æ¨¡å¼æ–¹é¢çš„å–用(Semantic MediaWiki)",
+ "right-smw-pageedit": "編輯有關 <code>Is edit protected</code> 註釋é é¢çš„å–用(Semantic MediaWiki)",
+ "right-smw-ruleedit": "編輯è¦å‰‡é é¢ï¼ˆSemantic MediaWiki)",
+ "restriction-level-smw-pageedit": "å·²ä¿è­·ï¼ˆåƒ…é™æœ‰è³‡æ ¼çš„使用者)",
+ "action-smw-patternedit": "編輯由語義MediaWiki使用的è¦å‰‡é‹ç®—å¼",
+ "action-smw-pageedit": "編輯帶有註釋 <code>Is edit protected</code> çš„é é¢ï¼ˆSemantic MediaWiki)",
+ "group-smwadministrator": "管ç†å“¡ï¼ˆSemantic MediaWiki)",
+ "group-smwadministrator-member": "{{GENDER:$1|管ç†å“¡ï¼ˆSemantic MediaWiki)}}",
+ "grouppage-smwadministrator": "{{ns:project}}:管ç†å“¡ï¼ˆSemantic MediaWiki)",
+ "group-smwcurator": "策展人(Semantic MediaWiki)",
+ "group-smwcurator-member": "{{GENDER:$1|策展人(Semantic MediaWiki)}}",
+ "grouppage-smwcurator": "{{ns:project}}:策展人(Semantic MediaWiki)",
+ "action-smw-admin": "å­˜å– Semantic MediaWiki 的管ç†å·¥ä½œé …ç›®",
+ "action-smw-ruleedit": "編輯è¦å‰‡é é¢ï¼ˆSemantic MediaWiki)",
+ "smw-property-predefined-default": "「$1ã€æ˜¯é å…ˆå®šç¾©çš„屬性。",
+ "smw-property-predefined-common": "此屬性為é å…ˆéƒ¨ç½²ï¼ˆå³ç‚º[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 特殊屬性])且帶有é¡å¤–管ç†æ¬Šé™ï¼Œä½†å¯ä»¥åƒå…¶å®ƒ[https://www.semantic-mediawiki.org/wiki/Property 使用者定義屬性]般地使用。",
+ "smw-property-predefined-ask": "「$1ã€æ˜¯ä»£è¡¨å‡ºæœ‰é—œå„查詢的詮釋資訊(在[https://www.semantic-mediawiki.org/wiki/Subobject å­ç‰©ä»¶]çš„å½¢å¼ï¼‰ï¼Œä¸”ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-asksi": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,收集使用在查詢的æ¢ä»¶æ•¸é‡ä¹‹é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-askde": "「$1ã€æ˜¯ç”¨ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,告知有關查詢深度的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-askde": "這是基於å­æŸ¥è©¢å·¢ç‹€ã€å±¬æ€§éˆï¼Œå’Œå¯ç”¨çš„æ述元素,根據 <code>[https://www.semantic-mediawiki.org/wiki/Help:$smwgQMaxDepth $smwgQMaxDepth]</code> 設置åƒæ•¸æ‰€é™åˆ¶çš„執行查詢計算出來的數值。",
+ "smw-property-predefined-askpa": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,æ述影響查詢çµæžœçš„åƒæ•¸ä¹‹é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-askpa": "這是屬性集åˆçš„一部分,å¯æŒ‡å®š[https://www.semantic-mediawiki.org/wiki/Help:Query_profiler 查詢å好設定]。",
+ "smw-sp-properties-docu": "æ­¤é é¢åˆ—出此 wiki 上的å¯ç”¨[https://www.semantic-mediawiki.org/wiki/Property 屬性]以åŠå…¶ä½¿ç”¨æ•¸ã€‚為了能有最新的數é‡çµ±è¨ˆï¼Œå»ºè­°æ‚¨å®šæœŸé‹ä½œ[https://www.semantic-mediawiki.org/wiki/rebuildPropertyStatistics 屬性統計]維護手稿程å¼ã€‚觀點上的差異,請查看[[Special:UnusedProperties|未使用]]或[[Special:WantedProperties|有所需è¦]]的屬性特殊é é¢ã€‚",
+ "smw-sp-properties-cache-info": "所列出資料已從[https://www.semantic-mediawiki.org/wiki/Caching å¿«å–]檢索,並更新於 $1。",
+ "smw-sp-properties-header-label": "屬性清單",
+ "smw-admin-settings-docu": "顯示所有相關 Semantic MediaWiki 環境之é è¨­åŠæœ¬åœ°åŒ–設定的清單。有關個別設定詳情,請åƒé–±[https://www.semantic-mediawiki.org/wiki/Help:Configuration 設置]說明é é¢ã€‚",
+ "smw-sp-admin-settings-button": "產生設定清單",
+ "smw-admin-idlookup-title": "查找",
+ "smw-admin-idlookup-docu": "此段è½é¡¯ç¤ºå‡ºæœ‰é—œåœ¨ Semantic MediaWiki 上個別實體(wiki é é¢ã€å­ç‰©ä»¶ã€å±¬æ€§ã€å…¶å®ƒï¼‰ 的技術詳情。輸入內容å¯ä»¥æ˜¯æ•¸å­— ID 或是字串值來比å°ç›¸é—œæœå°‹æ¬„ä½ï¼Œä¸éŽä»»ä½• ID 的引用是相關 Semantic MediaWiki;而ä¸æ˜¯ MediaWiki çš„é é¢æˆ–修訂 ID。",
+ "smw-admin-iddispose-title": "æ“´æ•£",
+ "smw-admin-iddispose-docu": "請注æ„,處ç†æ“作ä¸å—é™åˆ¶ï¼Œä¸€æ—¦ç¢ºèªï¼Œå­˜å„²å¼•æ“Žä¸­çš„所有數據和掛起的請求將全部被删除。請'''æ…Žé‡'''執行此任務,並åªåœ¨æŸ¥é–±[https://www.semantic-mediawiki.org/wiki/Help:Object_ID_disposal 檔案]後進行。",
+ "smw-admin-iddispose-done": "ID“$1â€å·²å¾žå­˜å„²å¾Œç«¯ä¸­ç§»é™¤ã€‚",
+ "smw-admin-iddispose-references": "ID「$1ã€{{PLURAL:$2|沒有|至少有一個}}有效的åƒè€ƒæ–‡ç»ã€‚",
+ "smw-admin-iddispose-references-multiple": "至少具有一個有效åƒè€ƒæ–‡ç»ç´€éŒ„的符åˆæ¸…單。",
+ "smw-admin-iddispose-no-references": "æœå°‹ç„¡æ³•æ‰¾å‡ºç¬¦åˆã€Œ$1ã€çš„表格項目。",
+ "smw-admin-idlookup-input": "æœå°‹ï¼š",
+ "smw-admin-objectid": "ID:",
+ "smw-admin-tab-general": "é è¦½",
+ "smw-admin-tab-notices": "åœç”¨é€šçŸ¥",
+ "smw-admin-tab-maintenance": "維護",
+ "smw-admin-tab-supplement": "補充功能",
+ "smw-admin-tab-registry": "註冊",
+ "smw-admin-maintenance-no-description": "沒有æ述。",
+ "smw-admin-maintenance-script-section-title": "å¯ç”¨ç¶­è­·æ‰‹ç¨¿æ¸…å–®",
+ "smw-admin-maintenance-script-section-intro": "以下維護手稿需è¦ç‚ºç®¡ç†å“¡ï¼Œä¸¦ä¸”得使用命令列æ‰èƒ½åŸ·è¡Œæ‰€åˆ—出的手稿。",
+ "smw-admin-maintenance-script-description-dumprdf": "ç¾æœ‰çš„ä¸‰é‡ RDF 匯出。",
+ "smw-admin-maintenance-script-description-rebuildconceptcache": "æ­¤æ‰‹ç¨¿ç”¨æ–¼ç®¡ç† Semantic MediaWiki 的概念快å–,這å¯å‰µå»ºã€ç§»é™¤ã€æ›´æ–°æ‰€é¸å¿«å–。",
+ "smw-admin-maintenance-script-description-rebuilddata": "以循環é€éŽæ‰€æœ‰å¯èƒ½å«æœ‰èªžæ„資料的é é¢ä¾†é‡æ–°å‰µå»ºåœ¨è³‡æ–™åº«çš„語æ„資料。",
+ "smw-admin-maintenance-script-description-rebuildelasticindex": "以循環é€éŽæ‰€æœ‰å«æœ‰èªžæ„資料的實體來é‡æ–°å»ºç½® Elasticsearch 索引(用於使用 <code>ElasticStore</code> 的安è£ï¼‰ã€‚",
+ "smw-admin-maintenance-script-description-rebuildfulltextsearchtable": "é‡æ–°å»ºç½® <code>SQLStore</code> 全文æœå°‹ç´¢å¼•ï¼ˆç”¨æ–¼è¨­å®šå·²å•Ÿç”¨çš„安è£ã€‚",
+ "smw-admin-maintenance-script-description-rebuildpropertystatistics": "é‡æ–°å»ºç½®æ‰€æœ‰å±¬æ€§å¯¦é«”的使用統計內容。",
+ "smw-admin-maintenance-script-description-removeduplicateentities": "移除在所é¸ä¸”沒有有效åƒè€ƒæ–‡ç»çš„表格上找出的é‡è¤‡å¯¦é«”。",
+ "smw-admin-maintenance-script-description-setupstore": "設定在 <code>LocalSettings.php</code> 所é¸çš„儲存後端。",
+ "smw-admin-maintenance-script-description-updateentitycollation": "更新在 <code>SQLStore</code> çš„ <code>smw_sort</code> 欄ä½ï¼ˆä¾æ“š [https://www.semantic-mediawiki.org/wiki/Help:$smwgEntityCollation $smwgEntityCollation] 設定)。",
+ "smw-admin-maintenance-script-description-populatehashfield": "為缺少值的列填充 <code>smw_hash</code> 欄ä½ã€‚",
+ "smw-livepreview-loading": "讀å–中...",
+ "smw-sp-searchbyproperty-description": "æ­¤é é¢æ供用於找尋由屬性實體與命å值所æ述實體的簡易[https://www.semantic-mediawiki.org/wiki/Help:Browsing_interfaces ç€è¦½ä»‹é¢]。其它å¯ç”¨çš„æœå°‹ä»‹é¢ï¼ŒåŒ…å«æœ‰[[Special:PageProperty|é é¢å±¬æ€§æœå°‹]]以åŠ[[Special:Ask|è©¢å•æŸ¥è©¢å»ºç½®]]。",
+ "smw-sp-searchbyproperty-resultlist-header": "çµæžœæ¸…å–®",
+ "smw-sp-searchbyproperty-nonvaluequery": "已分é…屬性「$1ã€çš„值清單。",
+ "smw-sp-searchbyproperty-valuequery": "已註解屬性「$1ã€å¸¶æœ‰å€¼ã€Œ$2ã€çš„é é¢æ¸…å–®",
+ "smw-datavalue-number-textnotallowed": "「$1ã€ä¸èƒ½åˆ†é…給值為 $2 çš„è²æ˜Žæ•¸å­—類型。",
+ "smw-datavalue-number-nullnotallowed": "「$1ã€å›žå‚³äº†ã€ŒNULLã€ï¼Œé€™ä¸å…許作為數字。",
+ "smw-editpage-annotation-enabled": "æ­¤é é¢æ”¯æ´èªžç¾©æ–‡å…§è¨»é‡‹ (例如 <nowiki>\"[[Is specified as::World Heritage Site]]\"</nowiki>) 來建立由 Semantic MediaWiki æ供的çµæ§‹åŒ–ã€å¯æŸ¥è©¢çš„內容。 如何使用標示法或 #ask 分æžå™¨åŠŸèƒ½çš„詳細說明,請查看 [https://www.semantic-mediawiki.org/wiki/Help:Getting_started 入門指引]ã€[https://www.semantic-mediawiki.org/wiki/Help:In-text_annotation 文內註釋]或[https://www.semantic-mediawiki.org/wiki/Help:Inline_queries 行內查詢]說明é é¢ã€‚",
+ "smw-editpage-annotation-disabled": "由於命å空間é™åˆ¶ï¼Œæ­¤é é¢åœ¨èªžç¾©æ–‡å…§è¨»é‡‹ä¸­ä¸æœƒé–‹å•Ÿã€‚有關如何開啟命å空間的詳細資料å¯åœ¨[https://www.semantic-mediawiki.org/wiki/Help:Configuration 設定]說明é é¢æ‰¾åˆ°ã€‚",
+ "smw-editpage-property-annotation-enabled": "此屬性å¯ä»¥æ“´å……使用語æ„註解來指定資料類型(例如:<nowiki>\"[[Has type::Page]]\"</nowiki>)或支æ´çš„宣告(例如:<nowiki>\"[[Subproperty of::dc:date]]\"</nowiki>)。有關如何增加此é é¢çš„æ述,請查看[https://www.semantic-mediawiki.org/wiki/Help:Property_declaration 屬性宣告]或是[https://www.semantic-mediawiki.org/wiki/Help:List_of_datatypes å¯ç”¨è³‡æ–™é¡žåž‹æ¸…å–®]說明é é¢ã€‚",
+ "smw-editpage-property-annotation-disabled": "此屬性已被é å…ˆå®šç¾©ï¼Œå› æ­¤ä¸èƒ½ä»¥è³‡æ–™é¡žåž‹è¨»è§£ï¼ˆä¾‹å¦‚:<nowiki>\"[[Has type::Page]]\"</nowiki>)來擴充(請查看[https://www.semantic-mediawiki.org/wiki/Help:Special_properties 特殊屬性]說明é é¢ä¾†ç²å¾—更多資訊)。",
+ "smw-editpage-concept-annotation-enabled": "此概念å¯ä½¿ç”¨ #concept 解æžåŠŸèƒ½ä¾†æ“´å……。有關如何使用 #concept 的敘述內容,請åƒè¦‹ [https://www.semantic-mediawiki.org/wiki/Help:Concepts 概念]說明é é¢ã€‚",
+ "smw-search-syntax-support": "æœå°‹è¼¸å…¥æ”¯æ´èªžæ„[https://www.semantic-mediawiki.org/wiki/Help:Semantic_search 查詢語法]的使用,來å”助使用 Semantic MediaWiki 比å°çµæžœã€‚",
+ "smw-search-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 輸入幫助]也已啟用,來減少å¯ç”¨å±¬æ€§å’Œåˆ†é¡žçš„é å…ˆé¸æ“‡ã€‚",
+ "smw-search-help-intro": "<code><nowiki>[[ ... ]]</nowiki></code> 輸入會指示至輸入處ç†å™¨ä¾†ä½¿ç”¨ Semantic MediaWiki 後端æœå°‹ã€‚å¦å¤–需注æ„ä¸æ”¯æ´ <code><nowiki>[[ ... ]]</nowiki></code> çµåˆåƒæ˜¯ <code><nowiki>[[ ... ]] OR Lorem ipsum</nowiki></code> 這樣的éžçµæ§‹åŒ–文字æœå°‹ã€‚",
+ "smw-search-help-structured": "çµæ§‹åŒ–æœå°‹ï¼š\n\n*<code><nowiki>[[Category:Lorem ipsum]]</nowiki></code>ã€<code><nowiki>[[Has number::123]]</nowiki></code>(作為 [https://www.semantic-mediawiki.org/wiki/Help:Search#Filter_context 已篩é¸èªžå¢ƒ])\n\n*<code><nowiki>[[Has text::~*lorem*]]</nowiki></code>(帶有[https://www.semantic-mediawiki.org/wiki/Help:Search#Query_context 查詢語境])",
+ "smw-search-help-proximity": "接近æœå°‹ï¼ˆæœªçŸ¥çš„屬性,'''åª'''å°æ–¼æ供全文æœå°‹æ•´åˆçš„後端å¯ç”¨ï¼‰ï¼š\n\n*<code><nowiki>[[in:lorem ipsum]]</nowiki></code>(在所有文件裡æœå°‹å·²ç´¢å¼•çš„ \"lorem\" å’Œ \"ipsum\")\n\n* <code><nowiki>[[phrase:lorem ipsum]]</nowiki></code>ï¼ˆæ¯”å° \"lorem ipsum\" 為詞組)",
+ "smw-search-help-ask": "以下連çµæœƒè§£é‡‹å¦‚何使用 <code>#ask</code> 語法。\n\n* [https://www.semantic-mediawiki.org/wiki/Help:Selecting_pages é¸æ“‡é é¢]æ述來é¸æ“‡é é¢å¦‚何並建置æ¢ä»¶\n\n*[https://www.semantic-mediawiki.org/wiki/Help:Search_operators æœå°‹é‹ç®—]列出包括用於範åœå’Œè¬ç”¨æŸ¥è©¢çš„å¯ç”¨æœå°‹é‹ç®—",
+ "smw-search-input": "輸入與æœå°‹",
+ "smw-search-help-input-assistance": "[https://www.semantic-mediawiki.org/wiki/Help:Input_assistance 輸入幫助]用於輸入欄ä½æ™‚æ供,這需è¦ä½¿ç”¨ä»¥ä¸‹å­—首之一:\n\n*<code>p:</code> 用來啟用屬性建議(例如:<code><nowiki>[[p:Has ...</nowiki></code>)\n\n*<code>c:</code> 用來啟用分類建議\n\n*<code>con:</code> 用來啟用概念建議",
+ "smw-search-syntax": "語法",
+ "smw-search-profile": "æ“´å……",
+ "smw-search-profile-tooltip": "æœå°‹èˆ‡ Semantic MediaWiki é—œè¯çš„功能",
+ "smw-search-profile-sort-best": "最佳符åˆ",
+ "smw-search-profile-sort-recent": "最新",
+ "smw-search-profile-sort-title": "標題",
+ "smw-search-profile-extended-help-intro": "Special:Search [https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile æ“´å……å好設定]æä¾›å–用在特定於 Semantic MediaWiki çš„æœå°‹åŠŸèƒ½ï¼Œä¸¦ä¸”支æ´å¾Œç«¯æŸ¥è©¢ã€‚",
+ "smw-search-profile-extended-help-sort": "指定用於çµæžœé¡¯ç¤ºçš„排åºå好:",
+ "smw-search-profile-extended-help-sort-title": "* \"Title\" 使用é é¢æ¨™é¡Œï¼ˆæˆ–顯示標題)來作為排åºæ¨™æº–",
+ "smw-search-profile-extended-help-sort-recent": "* \"Most recent\" 會優先顯示出最多近期變動的實體(å­ç‰©ä»¶å¯¦é«”會抑制æˆé€™äº›å¯¦é«”,並ä¸æœƒä»¥[[Property:Modification date|修改日期]]來註解)",
+ "smw-search-profile-extended-help-sort-best": "* \"Best match\" 會基於由後端所æ供的[https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/Relevancy é—œè¯åº¦]分數來排åºå¯¦é«”",
+ "smw-search-profile-extended-help-form": "所æ供會根據所見的ä¸åŒå±¬æ€§å’Œå€¼æ¬„ä½ä¾†æ¯”å°ç‰¹å®šä½¿ç”¨æƒ…æ³çš„表單(如有ä¿æŒè‘—),會減少輸入éŽç¨‹è®“è¦æŒçºŒæœå°‹è«‹æ±‚的使用者能更容易利用。(請查看$1)",
+ "smw-search-profile-extended-help-namespace": "當表單é¸æ“‡æ™‚,命å空間é¸æ“‡æ¡†æœƒè¢«éš±è—起來,但å¯å€ŸåŠ©ã€Œé¡¯ç¤º/éš±è—ã€æŒ‰éˆ•ä¾†å°‡å…¶å¯è¦‹ã€‚",
+ "smw-search-profile-extended-help-search-syntax": "æœå°‹è¼¸å…¥æ¬„ä½æ”¯æ´ç”¨ä¾†å®šç¾© Semantic MediaWiki 指定æœå°‹èªžå¢ƒçš„ <code>#ask</code> 語法使用。有效的表é”å¼åŒ…å«ï¼š",
+ "smw-search-profile-extended-help-search-syntax-simplified-in": "* <code>in:</code> 用於查詢包å«ã€Œ...ã€çš„任何內容,這在所涉åŠçš„æœå°‹ä¸Šä¸‹æ–‡æˆ–屬性未知時很有用(例如:<code>in:(lorem && ipsum)</code> ç­‰åŒ <code><nowiki>[[~~*lorem*]] && [[~~*ipsum*]]</nowiki></code>)。",
+ "smw-search-profile-extended-help-search-syntax-simplified-phrase": "* <code>phrase:</code> 為在完全相åŒæŽ’åºè£¡æŸ¥æ‰¾ä»»ä½•å«æœ‰ã€Œ...ã€çš„é …ç›®",
+ "smw-search-profile-extended-help-search-syntax-simplified-has": "* <code>has:</code> 為符åˆä»»ä½•å…·æœ‰å±¬æ€§ \"...\" 的項目(例如:<code>has:(Foo && Bar)</code> ç­‰åŒæ–¼ <code><nowiki>[[Foo::+]] && [[Bar::+]]</nowiki></code>)",
+ "smw-search-profile-extended-help-search-syntax-simplified-not": "* <code>not:</code> 為沒有符åˆä»»ä½•åŒ…å«ã€Œ...ã€çš„é …ç›®",
+ "smw-search-profile-extended-help-search-syntax-prefix": "* é¡å¤–å¯ç”¨ä¸”已定義的自定義字首,例如åƒæ˜¯ï¼š$1",
+ "smw-search-profile-extended-help-search-syntax-reserved": "* 一些表é”å¼ç‚ºä¿ç•™çš„,例如åƒæ˜¯ï¼š<nowiki>$1</nowiki>",
+ "smw-search-profile-extended-help-search-syntax-note": "''一些所列出的æ“作,僅é©ç”¨æ–¼å•Ÿç”¨çš„全文索引或 ElasticStore。''",
+ "smw-search-profile-extended-help-query": "已使用 <code><nowiki>$1</nowiki></code> 作為查詢。",
+ "smw-search-profile-extended-help-query-link": "(更多詳情$1)。",
+ "smw-search-profile-extended-help-find-forms": "å¯ç”¨è¡¨å–®",
+ "smw-search-profile-extended-section-sort": "排åºä¾",
+ "smw-search-profile-extended-section-form": "表單",
+ "smw-search-profile-extended-section-search-syntax": "æœå°‹è¼¸å…¥",
+ "smw-search-profile-extended-section-namespace": "命å空間",
+ "smw-search-profile-extended-section-query": "查詢",
+ "smw-search-profile-link-caption-query": "è«‹åƒé–±",
+ "smw-search-show": "顯示",
+ "smw-search-hide": "éš±è—",
+ "log-name-smw": "Semantic MediaWiki 日誌",
+ "log-show-hide-smw": "$1 Semantic MediaWiki 日誌",
+ "logeventslist-smw-log": "Semantic MediaWiki 日誌",
+ "log-description-smw": "有關[https://www.semantic-mediawiki.org/wiki/Help:Logging 已啟用事件類型]çš„æ“作,該已由 Semantic MediaWiki åŠå…¶å…ƒä»¶æ‰€å›žå ±ã€‚",
+ "logentry-smw-maintenance": "由 Semantic MediaWiki 發佈出的維護相關事件",
+ "smw-datavalue-import-unknown-namespace": "匯入的命å空間 \"$1\" ä¸æ˜Žï¼Œè«‹ç¢ºèª OWL 匯入詳細資訊,å¯è‡³ [[MediaWiki:Smw import $1]] å–得。",
+ "smw-datavalue-import-missing-namespace-uri": "無法在[[MediaWiki:Smw import $1|$1 匯入]]資料中找到 \"$1\" 命å空間的 URI。",
+ "smw-datavalue-import-missing-type": "在 [[MediaWiki:Smw import $2|$2]] 個匯入裡,找ä¸åˆ°ç”¨æ–¼ã€Œ$1ã€çš„類型定義。",
+ "smw-datavalue-import-link": "[[MediaWiki:Smw import $1|$1 匯入]]",
+ "smw-datavalue-import-invalid-value": "「$1ã€ä¸æ˜¯å€‹æœ‰æ•ˆæ ¼å¼ï¼Œä¸”é æœŸè¦å«æœ‰\"命å空間\":\"識別碼\"(例如:\"foaf:name\")。",
+ "smw-datavalue-import-invalid-format": "字串「$1ã€é æœŸæ‡‰åˆ†å‰²æˆå››å€‹éƒ¨åˆ†ï¼Œä½†æ ¼å¼æœªèƒ½ç†è§£ã€‚",
+ "smw-property-predefined-impo": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來æ述與[https://semantic-mediawiki.org/wiki/Help:Import_vocabulary 已匯入詞彙]間的關係的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-type": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來æ述屬性[[Special:Types|資料類型]]çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-sobj": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來表示出[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]çµæ§‹çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-sobj": "容器å…許與普通 wiki é é¢ç›¸ä¼¼ï¼Œä½†ç•¶é€£çµè‡³åµŒå…¥ä¸»é¡Œæ™‚,是以ä¸åŒå¯¦é«”空間的累ç©å±¬æ€§-值分é…。",
+ "smw-property-predefined-errp": "「$1ã€æ˜¯ç”¨ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,用來追蹤異常值通知方é¢è¼¸å…¥éŒ¯èª¤çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-errp": "在多數情æ³ä¸‹ï¼Œé€™æ˜¯ç”±æ–¼é¡žåž‹ä¸ç¬¦åˆæˆ–是[[Property:Allows value|值]]æ–¹é¢çš„é™åˆ¶æ‰€é€ æˆã€‚",
+ "smw-property-predefined-pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] 是由 [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å¯å®šç¾©å‡ºå…許值清單,來å°å±¬æ€§åšå€¼åˆ†é…é™åˆ¶çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] 是由 [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å¯æŒ‡å®šåƒè€ƒæ–‡ç»è‡³å…許值清單,來å°å±¬æ€§åšå€¼åˆ†é…é™åˆ¶çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-datavalue-property-restricted-annotation-use": "屬性「$1ã€æœ‰é™åˆ¶æ‡‰ç”¨å€åŸŸï¼Œä¸”ä¸èƒ½ç”±ä½¿ç”¨è€…來註解屬性。",
+ "smw-datavalue-property-restricted-declarative-use": "屬性「$1ã€æ˜¯é™³è¿°å±¬æ€§ï¼Œä¸”僅能在屬性和分類é é¢ä¸Šä½¿ç”¨ã€‚",
+ "smw-datavalue-property-create-restriction": "屬性「$1ã€ä¸å­˜åœ¨ï¼Œä¸”使用者缺少建立或是以未核准屬性來註解值的「$2ã€æ¬Šé™ï¼ˆæŸ¥çœ‹[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode 權å¨æ¨¡å¼])。",
+ "smw-datavalue-property-invalid-character": "「$1ã€åŒ…å«ä½œç‚ºå±¬æ€§æ¨™ç±¤ä¸€éƒ¨ä»½çš„列舉字元「$2ã€ï¼Œå› æ­¤è¢«æ­¸é¡žç‚ºç„¡æ•ˆã€‚",
+ "smw-datavalue-property-invalid-chain": "ä¸å…許在註解éŽç¨‹ä¸­ä½¿ç”¨ã€Œ$1ã€ä½œç‚ºå±¬æ€§éˆã€‚",
+ "smw-datavalue-restricted-use": "資料值「$1ã€è¢«æ¨™è¨˜ç‚ºé™åˆ¶ä½¿ç”¨ã€‚",
+ "smw-datavalue-invalid-number": "\"$1\" 無法作為數字解讀。",
+ "smw-query-condition-circular": "æ–¼ \"$1\" åµæ¸¬åˆ°å¯èƒ½æœƒç™¼ç”Ÿå¾ªç’°çš„情æ³ã€‚",
+ "smw-query-condition-empty": "查詢æè¿°å«æœ‰ç©ºçš„æ¢ä»¶ã€‚",
+ "smw-types-list": "資料型態清單",
+ "smw-types-default": "\"$1\" 是內建的資料型態。",
+ "smw-types-help": "更多的資訊與範例å¯åœ¨æ­¤[https://www.semantic-mediawiki.org/wiki/Help:Type_$1 說明é é¢]找到。",
+ "smw-type-anu": "\"$1\" 是 [[Special:Types/URL|URL]] 資料型態的變體,大多用在 ''owl:AnnotationProperty'' 匯出宣告。",
+ "smw-type-boo": "「$1ã€æ˜¯åŸºæœ¬è³‡æ–™åž‹æ…‹ï¼Œç”¨ä¾†æ述真/å‡å€¼ã€‚",
+ "smw-type-cod": "\"$1\" 是[[Special:Types/Text|文字]]資料型態的變體,使用在ä¸å®šé•·åº¦çš„技術文字,如原始碼清單。",
+ "smw-type-geo": "「$1ã€æ˜¯è³‡æ–™åž‹æ…‹ï¼Œç”¨ä¾†æ述地ç†ä½ç½®ï¼Œéœ€è¦æ­é… [https://www.semantic-mediawiki.org/wiki/Extension:Maps \"Maps\"] 擴充功能使用。",
+ "smw-type-tel": "\"$1\" 是特殊的資料型態,用來æè¿°ä¾ RFC 3966 è¦ç¯„的國際電話號碼。",
+ "smw-type-txt": "「$1ã€æ˜¯åŸºæœ¬è³‡æ–™åž‹æ…‹ï¼Œç”¨ä¾†æè¿°ä»»æ„長度的字串。",
+ "smw-type-dat": "「$1ã€æ˜¯åŸºæœ¬è³‡æ–™åž‹æ…‹ï¼Œä½¿ç”¨çµ±ä¸€æ ¼å¼ä¾†ä»£è¡¨æ™‚間點。",
+ "smw-type-ema": "「$1ã€æ˜¯è¡¨ç¾å‡ºé›»å­éƒµä»¶çš„特殊資料類型。",
+ "smw-type-tem": "「$1ã€æ˜¯ä»£è¡¨æº«åº¦çš„特殊數字資料類型。",
+ "smw-type-qty": "「$1ã€æ˜¯ä»¥æ•¸å­—表示以åŠè¨ˆé‡å–®ä½ä¾†æ述數é‡çš„資料類型。",
+ "smw-type-rec": "「$1ã€æ˜¯æŒ‡å®šå‡ºå›ºå®šæŽ’åºé¡žåž‹åŒ–的屬性清單之容器資料類型。",
+ "smw-type-extra-tem": "轉æ›æž¶æ§‹å«æœ‰åƒæ˜¯å…‹è€³æ–‡ã€æ”æ°ã€è¯æ°ï¼Œä»¥åŠè˜­é‡‘溫標這類的支æ´å–®ä½ã€‚",
+ "smw-type-tab-properties": "屬性",
+ "smw-type-tab-types": "é¡žåž‹",
+ "smw-type-tab-errors": "錯誤",
+ "smw-type-primitive": "基本",
+ "smw-type-contextual": "上下文",
+ "smw-type-compound": "åˆæˆè©ž",
+ "smw-specials-bytype-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Type_$1",
+ "smw-specials-types-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Types",
+ "smw-special-pageproperty-helplink": "https://www.semantic-mediawiki.org/wiki/Help:Special:PageProperty",
+ "smw-special-pageproperty-description": "æ­¤é é¢æä¾›ç€è¦½ç”¨æ–¼æ‰¾å°‹å±¬æ€§å…¨éƒ¨å€¼èˆ‡æŒ‡å®šé é¢çš„介é¢ã€‚其它å¯ç”¨æœå°‹ä»‹é¢åŒ…括[[Special:SearchByProperty|屬性æœå°‹]]與[[Special:Ask|請求查詢建置器]]。",
+ "smw-property-predefined-errc": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,表示出ç¾åœ¨é€£æŽ¥åˆ°ä¸æ°ç•¶å€¼è¨»è§£æˆ–輸入處ç†æ™‚的錯誤之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-errc": "錯誤會收集於[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]裡,å¯èƒ½æœƒåŒ…å«å°Žè‡´å…§å®¹ä¸ç¬¦çš„屬性åƒè€ƒæ–‡ç»ã€‚",
+ "smw-property-predefined-errt": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å«æœ‰éŒ¯èª¤æ–¹é¢æ–‡å­—æè¿°çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-subobject-parser-invalid-naming-scheme": "使用者定義的å­ç‰©ä»¶åŒ…å«ç„¡æ•ˆçš„命å架構。使用在å‰äº”個字元的點號($1)已被ä¿ç•™ç”¨æ–¼æ“´å……æ–¹é¢ã€‚您å¯ä»¥ä¾†è¨­å®š[https://www.semantic-mediawiki.org/wiki/Help:Adding_subobjects#Named_identifier 命å的識別碼]。",
+ "smw-datavalue-record-invalid-property-declaration": "紀錄定義包å«è‘—本身已被è²æ˜Žç‚ºç´€éŒ„類型屬性「$1ã€ï¼Œå› æ­¤ä¸è¢«è¨±å¯ã€‚",
+ "smw-property-predefined-mdat": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å°æ‡‰ä¸»é¡Œæœ€å¾Œä¸€æ¬¡ä¿®æ”¹çš„日期之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-cdat": "「$1ã€æ˜¯ç”¨ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å°æ‡‰ä¸»é¡Œé¦–次修訂的日期之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-newp": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,表示出是å¦ç‚ºæ–°ä¸»é¡Œçš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-ledt": "「$1ã€æ˜¯ç”¨ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å«æœ‰ä½¿ç”¨è€…所建立出最新一次修訂的é é¢å稱之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-mime": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來æ述已上傳檔案的 MIME 類型之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-media": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來æ述已上傳檔案的多媒體類型之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-askfo": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,æŒæœ‰ä½¿ç”¨åœ¨æŸ¥è©¢è£¡çµæžœæ ¼å¼å稱的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-askst": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,以字串來æ述查詢æ¢ä»¶çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-askdu": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å«æœ‰æŸ¥è©¢åŸ·è¡Œå®Œç•¢æ‰€éœ€æ™‚間值(以秒為單ä½ï¼‰çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-asksc": "「$1ã€æ˜¯ç”¨ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,確èªæ›¿ä»£ï¼ˆä¾‹å¦‚:é ç«¯ã€è¯åˆï¼‰æŸ¥è©¢ä¾†æºçš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-askco": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,æ述查詢狀態或構æˆè¦ç´ çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-askco": "數字或分é…數字代表內部編碼狀態,在[https://www.semantic-mediawiki.org/wiki/Help:Query_profiler 說明é é¢]裡å«æœ‰é€²ä¸€æ­¥è§£é‡‹ã€‚",
+ "smw-property-predefined-prec": "「$1ã€æ˜¯ç”¨æ–¼æ•¸å­—資料類型裡[https://www.semantic-mediawiki.org/wiki/Help:Display_precision 顯示精確度](å°æ•¸ä½æ•¸ï¼‰çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-attch-link": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,收集在é é¢è£¡æ‰€æ‰¾å‡ºçš„內嵌檔案和圖片連çµä¹‹é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-types-extra-geo-not-available": "未åµæ¸¬åˆ°[https://www.semantic-mediawiki.org/wiki/Extension:Maps 擴充「Mapsã€] ,因此「$1ã€çš„é‹ä½œèƒ½åŠ›è¢«å—é™ã€‚",
+ "smw-datavalue-monolingual-dataitem-missing": "缺少用來建置單語åˆæˆè©žå€¼çš„é æœŸé …目。",
+ "smw-datavalue-languagecode-missing": "å°æ–¼ã€Œ$1ã€è¨»è§£ï¼Œè§£æžå™¨ç„¡æ³•åˆ¤æ–·èªžè¨€ä»£ç¢¼ï¼ˆä¾‹å¦‚åƒï¼š\"foo@en\")。",
+ "smw-datavalue-languagecode-invalid": "「$1ã€ä¸è¢«èªç‚ºæ˜¯æ”¯æ´çš„語言代碼。",
+ "smw-property-predefined-lcode": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,表示出 BCP47 æ ¼å¼èªžè¨€ä»£ç¢¼çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-type-mlt-rec": "「$1ã€æ˜¯ä»¥ç‰¹å®š[[Property:Language code|語言代碼]]來關è¯æ–‡å­—值的[https://www.semantic-mediawiki.org/wiki/Help:Container 容器]資料類型。",
+ "smw-types-extra-mlt-lcode": "資料類型{{PLURAL:$2|需è¦|ä¸éœ€}}語言代碼(註:{{PLURAL:$2|ä¸æŽ¥å—沒有語言代碼的值註解|å¯æŽ¥å—沒有語言代碼的值註解}})。",
+ "smw-property-predefined-text": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,表示出任æ„長度文字的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-pdesc": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å…許æ述在語境裡屬性的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-list": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來定義與[[Special:Types/Record|紀錄]]類型屬性一åŒä½¿ç”¨çš„屬性清單之é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-limitreport-intext-parsertime": "[SMW]文內註解解æžå™¨æ™‚é–“",
+ "smw-limitreport-intext-postproctime": "[SMW]發佈處ç†æ™‚é–“",
+ "smw-limitreport-intext-parsertime-value": "$1 {{PLURAL:$1|秒|秒}}",
+ "smw-limitreport-intext-postproctime-value": "$1 {{PLURAL:$1|秒|秒}}",
+ "smw-limitreport-pagepurge-storeupdatetime": "[SMW]存儲更新時間(在é é¢æ¸…除時)",
+ "smw-limitreport-pagepurge-storeupdatetime-value": "$1 {{PLURAL:$1|秒|秒}}",
+ "smw_allows_pattern": "æ­¤é é¢é æœŸåŒ…å«åƒè€ƒæ–‡ç»æ¸…單(ä¾æ“š[https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F æ­£è¦è¡¨ç¤ºå¼]),é€éŽ[[Property:Allows pattern|å…許模å¼]]åƒæ•¸å¯ç”¨ã€‚è¦ç·¨è¼¯æ­¤é é¢ï¼Œéœ€è¦ <code>smw-patternedit</code> 權é™ã€‚",
+ "smw-datavalue-allows-pattern-mismatch": "「$1ã€è¢«æ­£è¦è¡¨é”å¼ã€Œ$2ã€æ­¸é¡žç‚ºç„¡æ•ˆã€‚",
+ "smw-datavalue-allows-pattern-reference-unknown": "「$1ã€æ¨¡å¼åƒè€ƒæ–‡ç»ä¸ç¬¦åˆåœ¨ [[MediaWiki:Smw allows pattern]] 裡的項目。",
+ "smw-datavalue-allows-value-list-unknown": "「$1ã€æ¸…å–®åƒè€ƒæ–‡ç»ä¸ç¬¦åˆ[[MediaWiki:Smw allows list $1]]é é¢ã€‚",
+ "smw-datavalue-allows-value-list-missing-marker": "「$1ã€æ¸…單內容缺少帶有 * 清單標記的項目。",
+ "smw-datavalue-feature-not-supported": "「$1ã€åŠŸèƒ½åœ¨æ­¤ wiki ä¸æ”¯æ´æˆ–是被åœç”¨ã€‚",
+ "smw-property-predefined-pvap": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å¯æŒ‡å®š[[MediaWiki:Smw allows pattern|模å¼åƒç…§]]來套用到[https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F æ­£è¦è¡¨é”å¼]比å°çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-dtitle": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,å¯ç‚ºå¯¦é«”分é…ä¸åŒé¡¯ç¤ºæ¨™é¡Œçš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-pvuc": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,替å„實例é™åˆ¶å€¼åˆ†é…以é”到內容ä¸é‡è¤‡ï¼ˆæˆ–最多一個)的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-pvuc": "唯一性會在兩個值在字é¢æ¶µç¾©ä¸Šä¸ç›¸ç­‰æ™‚建立,且é•å任何é™åˆ¶æœƒè¢«æ­¸é¡žç‚ºéŒ¯èª¤ã€‚",
+ "smw-datavalue-uniqueness-constraint-error": "屬性「$1ã€åƒ…å…許唯一值分é…,而「$2ã€å·²è¨»è§£åœ¨ä¸»é¡Œã€Œ$3ã€è£¡ã€‚",
+ "smw-datavalue-uniqueness-constraint-isknown": "屬性「$1ã€åƒ…å…許唯一值註解,「$2ã€å·²åŒ…å«æ‰€åˆ†é…值。「$3ã€é•å了唯一性é™åˆ¶ã€‚",
+ "smw-property-predefined-boo": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表布林值的[[Special:Types/Boolean|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-num": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表數字值的[[Special:Types/Number|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-dat": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表日期值的[[Special:Types/Date|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-uri": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表 URI/URL 值的[[Special:Types/URL|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-qty": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表數é‡çš„[[Special:Types/Quantity|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-datavalue-time-invalid-offset-zone-usage": "「$1ã€å«æœ‰ä¸æ”¯æ´çš„å差和å€åŸŸè­˜åˆ¥ç¢¼ã€‚",
+ "smw-datavalue-time-invalid-values": "值「$1ã€åŒ…å«åœ¨ã€Œ$2ã€å½¢å¼ä¸‹ç„¡æ³•è§£é‡‹çš„資訊。",
+ "smw-datavalue-time-invalid-date-components-common": "「$1ã€åŒ…å«ä¸€äº›ç„¡æ³•è§£é‡‹çš„資訊。",
+ "smw-datavalue-time-invalid-date-components-dash": "「$1ã€å«æœ‰å¤–部連接號或是其它無效的闡明日期字元。",
+ "smw-datavalue-time-invalid-date-components-empty": "「$1ã€åŒ…å«ä¸€äº›ç©ºçš„æˆä»½ã€‚",
+ "smw-datavalue-time-invalid-date-components-three": "「$1ã€åŒ…å«è¶…éŽé—¡æ˜Žæ—¥æœŸæ‰€éœ€çš„三個è¦ç´ ã€‚",
+ "smw-datavalue-time-invalid-date-components-sequence": "「$1ã€å«æœ‰ç„¡æ³•è§£è®€ï¼Œé•å用於日期組件之å¯ç”¨ç¬¦åˆæ¨¡åž‹çš„åºåˆ—。",
+ "smw-datavalue-time-invalid-ampm": "「$1ã€åŒ…å«ä½œç‚ºå°æ™‚元素的「$2ã€ï¼Œé€™åœ¨ 12 å°æ™‚制裡是無效的。",
+ "smw-datavalue-time-invalid-jd": "無法將輸入值「$1ã€è§£è®€ç‚ºæœ‰æ•ˆçš„儒略日,以「$2ã€å…§å®¹å›žå ±ã€‚",
+ "smw-datavalue-time-invalid-prehistoric": "無法解讀éŽæ™‚的輸入值「$1ã€ã€‚例如在舊å¼ä¸Šä¸‹æ–‡è£¡æŒ‡å®šäº†è¶…éŽä¸€å¹´æˆ–是日曆模組,å¯èƒ½æœƒå›žå‚³éžé æœŸçš„çµæžœã€‚",
+ "smw-datavalue-time-invalid": "無法將輸入值「$1ã€è§£è®€ç‚ºæœ‰æ•ˆçš„日期或時間æˆä»½ï¼Œä»¥ã€Œ$2ã€å…§å®¹å›žå ±ã€‚",
+ "smw-datavalue-external-formatter-uri-missing-placeholder": "æ ¼å¼åŒ– URI 缺少「$1ã€ä½”ä½ç¬¦ã€‚",
+ "smw-datavalue-external-formatter-invalid-uri": "「$1ã€æ˜¯ç„¡æ•ˆçš„ URL。",
+ "smw-datavalue-external-identifier-formatter-missing": "屬性缺少[[Property:External formatter uri|\"外部格å¼åŒ– URI\"]] 分é…。",
+ "smw-datavalue-keyword-maximum-length": "é—œéµè©žè¶…出最大長度 $1 個{{PLURAL:$1|å­—å…ƒ|å­—å…ƒ}}。",
+ "smw-property-predefined-eid": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來代表外部識別碼的[[Special:Types/External identifier|é¡žåž‹]]åŠé å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-peid": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,指定外部識別碼的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-pefu": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,以佔ä½ç¬¦ä¾†æŒ‡å®šå¤–部資æºçš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-pefu": "URI é æœŸåŒ…å«å°‡ç”±[[Special:Types/External identifier|外部識別碼]]值所調整的佔ä½ç¬¦ï¼Œä¾†å½¢æˆæœ‰æ•ˆçš„資æºåƒç…§ã€‚",
+ "smw-type-eid": "\"$1\" 是[[Special:Types/Text|文字]]資料型態的變體,需è¦åˆ†é…屬性來è²æ˜Ž[[Property:External formatter uri|外部格å¼åŒ– URI]]。",
+ "smw-property-predefined-keyw": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å¯æ¨™æº–化文字且å«æœ‰é™åˆ¶å­—元長度的é å…ˆå®šç¾©å±¬æ€§èˆ‡[[Special:Types/Keyword|é¡žåž‹]]。",
+ "smw-type-keyw": "\"$1\" 是[[Special:Types/Text|文字]]資料型態的變體,為有é™çš„字元長度,且è¦ç¯„內容的表ç¾å½¢å¼ã€‚",
+ "smw-datavalue-stripmarker-parse-error": "æ供的值「$1ã€å«æœ‰ [https://en.wikipedia.org/wiki/Help:Strip_markers strip markers] 內容,因此ä¸èƒ½è¢«å……分解æžã€‚",
+ "smw-datavalue-parse-error": "æ供的值「$1ã€ç„¡æ³•ç†è§£ã€‚",
+ "smw-datavalue-propertylist-invalid-property-key": "屬性清單「$1ã€åŒ…å«ç„¡æ•ˆçš„屬性éµã€Œ$2ã€ã€‚",
+ "smw-datavalue-type-invalid-typeuri": "類型「$1ã€ä¸å¯è½‰æ›æˆæœ‰æ•ˆçš„ URI 表述。",
+ "smw-datavalue-wikipage-missing-fragment-context": "Wiki é é¢è¼¸å…¥å€¼ã€Œ$1ã€ä¸å¯åœ¨ä¸å¸¶è„ˆçµ¡é é¢æƒ…æ³ä¸‹ä½¿ç”¨ã€‚",
+ "smw-datavalue-wikipage-invalid-title": "é é¢é¡žåž‹è¼¸å…¥å€¼ã€Œ$1ã€åŒ…å«ç„¡æ•ˆå­—元或是內容ä¸å®Œæ•´ï¼Œå°Žè‡´åœ¨æŸ¥è©¢æˆ–註釋éŽç¨‹è£¡ç™¼ç”Ÿæ„外çµæžœã€‚",
+ "smw-datavalue-wikipage-property-invalid-title": "屬性「$1ã€ï¼ˆä½œç‚ºé é¢é¡žåž‹ï¼‰æ‰€å¸¶çš„輸入值「$2ã€åŒ…å«ç„¡æ•ˆå­—元或是內容ä¸å®Œæ•´ï¼Œå°Žè‡´åœ¨æŸ¥è©¢æˆ–註釋éŽç¨‹è£¡ç™¼ç”Ÿæ„外çµæžœã€‚",
+ "smw-datavalue-wikipage-empty": "Wiki é é¢è¼¸å…¥å€¼ç‚ºç©ºï¼ˆä¾‹å¦‚åƒï¼š<code>[[SomeProperty::]], [[]]</code>),因此ä¸èƒ½ç”¨ä½œç‚ºæŸ¥è©¢æ¢ä»¶çš„å稱或一部份。",
+ "smw-type-ref-rec": "「$1ã€æ˜¯å€‹å…許記錄有關值分é…之é¡å¤–資訊(例如:出處資料)的[https://www.semantic-mediawiki.org/wiki/Container 容器]類型。",
+ "smw-datavalue-reference-outputformat": "$1:$2",
+ "smw-datavalue-reference-invalid-fields-definition": "[[Special:Types/Reference|åƒè€ƒæ–‡ç»]]類型應為è²æ˜Žä½¿ç”¨[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Has_fields å«æœ‰æ¬„ä½]屬性的屬性清單。",
+ "smw-parser-invalid-json-format": "JSON 解æžå™¨å›žå‚³äº†ã€Œ$1ã€ã€‚",
+ "smw-property-preferred-title-format": "$1($2)",
+ "smw-property-preferred-label-language-combination-exists": "「$1ã€ä¸èƒ½ç”¨æ–¼é¦–é¸æ¨™ç±¤ï¼Œå› ç‚ºèªžè¨€ã€Œ$2ã€å·²åˆ†é…給標籤「$3ã€ã€‚",
+ "smw-parse": "$1",
+ "smw-clipboard-copy-link": "複製到剪貼簿",
+ "smw-property-userdefined-fixedtable": "「$1ã€è¢«è¨­ç½®ç‚º[https://www.semantic-mediawiki.org/wiki/Fixed_properties 固定屬性],並且任何å°å…¶[https://www.semantic-mediawiki.org/wiki/Type_declaration 類型宣告]的變更需è¦é‹ä½œ <code>setupStore.php</code> 或是完æˆç‰¹æ®Š[[Special:SemanticMediaWiki|「資料庫與安è£èˆ‡æ›´æ–°ã€]]任務。",
+ "smw-data-lookup": "ç´¢å–資料中...",
+ "smw-data-lookup-with-wait": "正在處ç†è«‹æ±‚,å¯èƒ½éœ€èŠ±è²»ä¸€äº›æ™‚間。",
+ "smw-no-data-available": "沒有å¯ç”¨è³‡æ–™ã€‚",
+ "smw-property-req-violation-missing-fields": "因定義 <code>Has fields</code> 屬性失敗,屬性「$1ã€ç¼ºå°‘用於註釋「$2ã€é¡žåž‹çš„宣告詳情。",
+ "smw-property-req-violation-missing-formatter-uri": "因定義 <code>External formatter URI</code> 屬性失敗,屬性「$1ã€ç¼ºå°‘用於註釋類型的定義詳情。",
+ "smw-property-req-violation-predefined-type": "作為é å…ˆå®šç¾©å±¬æ€§çš„「$1ã€å±¬æ€§åŒ…å«ã€Œ$2ã€é¡žåž‹å®£å‘Šï¼Œå› æ­¤èˆ‡æ­¤å±¬æ€§çš„é è¨­é¡žåž‹ä¸ç›¸å®¹ã€‚",
+ "smw-property-req-violation-import-type": "åµæ¸¬åˆ°èˆ‡æ‰€åŒ¯å…¥è©žå½™ã€Œ$1ã€é å…ˆå®šç¾©é¡žåž‹ä¸ç›¸å®¹çš„類型宣告。在一般情æ³ä¸‹ï¼Œå› ç‚ºæœ‰å¾žåŒ¯å…¥å®šç¾©è£¡æª¢ç´¢è³‡è¨Šï¼Œæ‰€ä»¥ä¸éœ€è¦å®£å‘Šé¡žåž‹ã€‚",
+ "smw-property-req-violation-change-propagation-locked-error": "屬性「$1ã€å·²èª¿æ•´ï¼Œä¸¦è¦æ±‚使用[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]éŽç¨‹ä¾†é‡æ–°è©•ä¼°åˆ†é…的實體。此期間屬性é é¢æœƒåœ¨ä¸»è¦è¦æ ¼æ›´æ–°å®Œæˆä¹‹å‰è¢«éŽ–定,以防止中途打斷或是è¦æ ¼è¡çªã€‚在é é¢å–消鎖定之å‰ï¼ŒéŽç¨‹èŠ±è²»çš„時間會ä¾æ“š[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]排程的大å°èˆ‡é »çŽ‡ã€‚",
+ "smw-property-req-violation-change-propagation-locked-warning": "屬性「$1ã€å·²èª¿æ•´ï¼Œä¸¦è¦æ±‚使用[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]éŽç¨‹ä¾†é‡æ–°è©•ä¼°åˆ†é…的實體。更新花費的時間會ä¾æ“š[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]排程的大å°èˆ‡é »çŽ‡ï¼Œä¸¦å»ºè­°å»¶å¾Œå°å±¬æ€§é€²è¡Œæ›´æ”¹ï¼Œä»¥é˜²æ­¢ä¸­é€”打斷或是è¦æ ¼è¡çªã€‚",
+ "smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]更新正在等待中(估計有 $1 個[https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|任務|任務}}]),建議在處ç†å®Œæˆå‰æš«åœå±¬æ€§æ–¹é¢çš„變動,以防止中途打斷或是è¦æ ¼è¡çªã€‚",
+ "smw-property-req-violation-missing-maps-extension": "Semantic MediaWiki 無法檢測出[https://www.semantic-mediawiki.org/wiki/Extension:Maps 「Mapsã€]擴充,該為所é¸å±¬æ€§çš„å¿…è¦æ¢ä»¶ï¼Œå› æ­¤å—é™äº†æ­¤å±¬æ€§çš„功能。",
+ "smw-property-req-violation-type": "屬性包å«è¡çªçš„é¡žåž‹è¦æ ¼ï¼Œå¯èƒ½æœƒå°Žè‡´å€¼è¨»è§£ç„¡æ•ˆï¼Œå› æ­¤æ‡‰è®“使用者分é…到一個é©ç•¶çš„類型。",
+ "smw-change-propagation-protection": "æ­¤é é¢å·²è¢«éŽ–定,以防止當[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]更新在é‹è¡Œæ™‚æ„外的資料變動。在é é¢è§£é–‹éŽ–定å‰ï¼Œæœƒä¾æ“š[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]排程的大å°èˆ‡é »çŽ‡èŠ±è²»ä¸€æ®µæ™‚間。",
+ "smw-category-change-propagation-locked-error": "分類「$1ã€å·²èª¿æ•´ï¼Œä¸¦è¦æ±‚使用[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]éŽç¨‹ä¾†é‡æ–°è©•ä¼°åˆ†é…的實體。此期間分類é é¢æœƒåœ¨ä¸»è¦è¦æ ¼æ›´æ–°å®Œæˆä¹‹å‰è¢«éŽ–定,以防止中途打斷或是è¦æ ¼è¡çªã€‚在é é¢å–消鎖定之å‰ï¼ŒéŽç¨‹èŠ±è²»çš„時間會ä¾æ“š[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]排程的大å°èˆ‡é »çŽ‡ã€‚",
+ "smw-category-change-propagation-locked-warning": "分類「$1ã€å·²èª¿æ•´ï¼Œä¸¦è¦æ±‚使用[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]éŽç¨‹ä¾†é‡æ–°è©•ä¼°åˆ†é…的實體。更新花費的時間會ä¾æ“š[https://www.mediawiki.org/wiki/Manual:Job_queue 任務佇列]排程的大å°èˆ‡é »çŽ‡ï¼Œä¸¦å»ºè­°å»¶å¾Œå°åˆ†é¡žé€²è¡Œæ›´æ”¹ï¼Œä»¥é˜²æ­¢ä¸­é€”打斷或是è¦æ ¼è¡çªã€‚",
+ "smw-category-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation 更改傳播]更新正在等待中(估計有 $1 個[https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|任務|任務}}]),建議在處ç†å®Œæˆå‰æš«åœåˆ†é¡žæ–¹é¢çš„變動,以防止中途打斷或是è¦æ ¼è¡çªã€‚",
+ "smw-category-invalid-value-assignment": "「$1ã€æœªè¢«è¦–為有效分類或值註解。",
+ "protect-level-smw-pageedit": "僅å…許俱有é é¢ç·¨è¼¯æ¬Šé™çš„使用者(Semantic MediaWiki)",
+ "smw-create-protection": "當[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode 權å¨æ¨¡å¼]啟用後,具有符åˆã€Œ$2ã€æ¬Šé™çš„使用者(或是[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 使用者群組])會在建立屬性「$1ã€æ™‚å—é™",
+ "smw-create-protection-exists": "當[https://www.semantic-mediawiki.org/wiki/Help:Authority_mode 權å¨æ¨¡å¼]啟用後,具有符åˆã€Œ$2ã€æ¬Šé™çš„使用者(或是[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 使用者群組])會在更改屬性「$1ã€æ™‚å—é™",
+ "smw-edit-protection": "æ­¤é é¢å·²è¢«[[Property:Is edit protected|ä¿è­·]]以防止æ„外的資料變動,並僅å¯ç”±æ“有編輯權é™ï¼ˆ\"$1\")的使用者或[https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups 使用者群組]來編輯。",
+ "smw-edit-protection-disabled": "編輯ä¿è­·å·²è¢«åœç”¨ï¼Œå› æ­¤ã€Œ$1ã€ä¸èƒ½ç”¨æ–¼ä¿è­·å¯¦é«”é é¢ä¾†å…於未經授權的編輯。",
+ "smw-edit-protection-auto-update": "Semantic MediaWiki 已根據「Is edit protectedã€å±¬æ€§ä¾†æ›´æ–°ä¿è­·ç‹€æ…‹ã€‚",
+ "smw-edit-protection-enabled": "編輯已ä¿è­·å…§å®¹ï¼ˆSemantic MediaWiki)",
+ "smw-patternedit-protection": "æ­¤é é¢å·²è¢«ä¿è­·ï¼Œä¸¦åƒ…能由具有é©ç•¶ <code>smw-patternedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions 權é™]的使用者編輯。",
+ "smw-property-predefined-edip": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,指示編輯是å¦å—到ä¿è­·çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-edip": "儘管任何使用者都能添加此屬性至主題,但僅有專用權é™çš„使用者å¯ä»¥å°å·²è¢«æ·»åŠ çš„實體åšå‡ºç·¨è¼¯æˆ–解除ä¿è­·ã€‚",
+ "smw-query-reference-link-label": "查詢åƒè€ƒæ–‡ç»",
+ "smw-format-datatable-emptytable": "表格裡沒有å¯ç”¨è³‡æ–™",
+ "smw-format-datatable-info": "顯示 _TOTAL_ 個項目裡從 _START_ 到 _END_ 的內容",
+ "smw-format-datatable-infoempty": "顯示 0 個項目從 0 到 0 的內容",
+ "smw-format-datatable-infofiltered": "(從 _MAX_ total 項目篩é¸ï¼‰",
+ "smw-format-datatable-infothousands": ",",
+ "smw-format-datatable-lengthmenu": "顯示 _MENU_ 項目",
+ "smw-format-datatable-loadingrecords": "載入中...",
+ "smw-format-datatable-processing": "處ç†ä¸­...",
+ "smw-format-datatable-search": "æœå°‹ï¼š",
+ "smw-format-datatable-zerorecords": "找ä¸åˆ°ç¬¦åˆçš„紀錄",
+ "smw-format-datatable-first": "第一",
+ "smw-format-datatable-last": "最後",
+ "smw-format-datatable-next": "下一個",
+ "smw-format-datatable-previous": "上一個",
+ "smw-format-datatable-sortascending": ":啟用å‡å†ªæŽ’åºæ¬„ä½",
+ "smw-format-datatable-sortdescending": ":啟用é™å†ªæŽ’åºæ¬„ä½",
+ "smw-format-datatable-toolbar-export": "匯出",
+ "smw-format-list-other-fields-open": " (",
+ "smw-format-list-other-fields-close": ")",
+ "smw-category-invalid-redirect-target": "分類「$1ã€åŒ…å«æŒ‡å‘到éžåˆ†é¡žå‘½å空間的無效é‡æ–°å°Žå‘目標。",
+ "smw-parser-function-expensive-execution-limit": "解æžåŠŸèƒ½å·²é”到耗é‡åŸ·è¡Œçš„é™åˆ¶ï¼ˆè«‹åƒé–± [https://www.semantic-mediawiki.org/wiki/Help:$smwgQExpensiveExecutionLimit <code>$smwgQExpensiveExecutionLimit</code>] 設置åƒæ•¸ï¼‰ã€‚",
+ "smw-postproc-queryref": "Semantic MediaWiki 會在一些必è¦æŸ¥è©¢å¾ŒæœŸè™•ç†æ¢ä»¶ä¸‹ï¼Œé‡æ–°æ•´ç†é é¢ã€‚",
+ "apihelp-smwinfo-summary": "檢索有關 Semantic MediaWiki 統計資訊與其它詮釋資訊的 API 模組。",
+ "apihelp-ask-summary": "使用請求語言來查詢 Semantic MediaWiki 的 API 模組。",
+ "apihelp-askargs-summary": "使用請求語言作為æ¢ä»¶ã€è¼¸å‡ºå…§å®¹ã€åƒæ•¸çš„清單,來查詢 Semantic MediaWiki çš„ API 模組。",
+ "apihelp-browsebyproperty-summary": "檢索有關屬性或是屬性清單資訊的 API 模組。",
+ "apihelp-browsebysubject-summary": "檢索有關主題資訊的 API 模組。",
+ "apihelp-smwtask-summary": "執行 Semantic MediaWiki 相關任務的 API 模組。",
+ "apihelp-smwbrowse-summary": "支æ´åœ¨ Semantic MediaWiki ç€è¦½ä¸åŒå¯¦é«”類型行動的 API 模組。",
+ "apihelp-ask-parameter-api-version": "輸出格å¼ï¼š\n;2:用於çµæžœæ¸…單,使用 {} 的後端相容格å¼ã€‚\n;3:使用 [] 來作為çµæžœæ¸…單的實驗格å¼ã€‚",
+ "smw-api-invalid-parameters": "無效åƒæ•¸ã€Œ$1ã€",
+ "smw-parser-recursion-level-exceeded": "$1 個éžè¿´çš„層次在解æžæœŸé–“溢出。建議您驗證模æ¿çµæ§‹ï¼Œæˆ–是如有需è¦å¯è¨­ç½®åƒæ•¸ <code>$maxRecursionDepth</code>。",
+ "smw-property-page-list-count": "顯示使用到此屬性的 $1 個{{PLURAL:$1|é é¢|é é¢}}。",
+ "smw-property-page-list-search-count": "顯示使用到符åˆå€¼ã€Œ$2ã€ä¹‹å±¬æ€§çš„ $1 個{{PLURAL:$1|é é¢|é é¢}}。",
+ "smw-property-reserved-category": "分類",
+ "smw-category": "分類",
+ "smw-datavalue-uri-invalid-scheme": "「$1ã€æœªè¢«åˆ—入在有效的 URI 架構。",
+ "smw-datavalue-uri-invalid-authority-path-component": "「$1ã€è¢«ç¢ºèªå«æœ‰ç„¡æ•ˆçš„權å¨ã€Œ$2ã€æˆ–路徑組æˆã€‚",
+ "smw-browse-property-group-title": "屬性群組",
+ "smw-browse-property-group-label": "屬性群組標籤",
+ "smw-browse-property-group-description": "屬性群組æè¿°",
+ "smw-property-predefined-ppgr": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,å¯è­˜åˆ¥ç”¨ä¾†æ›¿å±¬æ€§åˆ†çµ„實例之實體(主è¦æ˜¯åˆ†é¡žï¼‰çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-filter": "篩é¸",
+ "smw-section-expand": "展開章節",
+ "smw-section-collapse": "摺疊章節",
+ "smw-ask-format-help-link": "[https://www.semantic-mediawiki.org/wiki/Help:$1_format $1]æ ¼å¼",
+ "smw-help": "說明",
+ "smw-cheat-sheet": "æ示",
+ "smw-personal-jobqueue-watchlist": "監視清單(任務佇列)",
+ "smw-property-predefined-label-skey": "排åºéµ",
+ "smw-processing": "處ç†ä¸­...",
+ "smw-redirect-target-unresolvable": "目標無法解決,出於原因「$1ã€",
+ "smw-types-title": "類型:$1",
+ "smw-schema-namespace-editcontentmodel-disallowed": "ä¸å…許更改[https://www.semantic-mediawiki.org/wiki/Help:Schema 架構é é¢]的內容模組。",
+ "smw-schema-namespace-edit-protection": "æ­¤é é¢å·²è¢«ä¿è­·ï¼Œä¸¦åƒ…能由具有é©ç•¶ <code>smw-schemaedit</code> [https://www.semantic-mediawiki.org/wiki/Help:Permissions 權é™]的使用者編輯。",
+ "smw-schema-error": "驗證錯誤",
+ "smw-schema-error-schema": "用於目å‰æž¶æ§‹çš„è¦æ ¼èˆ‡é©—證「$1ã€ç™¼ç¾ä»¥ä¸‹ä¸ç›¸å®¹æˆ–ä¸åˆè¦å‰‡ï¼š",
+ "smw-schema-error-violation": "ä¸åˆè¦å‰‡ï¼ˆ\"$1\",\"$2\")",
+ "smw-schema-error-type-missing": "內容缺少å¯ç”¨æ–¼è­˜åˆ¥ä¸”用在[https://www.semantic-mediawiki.org/wiki/Help:Schema 架構命å空間]çš„é¡žåž‹",
+ "smw-schema-error-type-unknown": "類型「$1ã€æœªè¨»å†Šï¼Œå› æ­¤ä¸èƒ½ç”¨æ–¼[https://www.semantic-mediawiki.org/wiki/Help:Schema 架構命å空間]內容裡。",
+ "smw-schema-title": "架構",
+ "smw-schema-type-help-link": "https://www.semantic-mediawiki.org/wiki/Help:Schema/Type/$1",
+ "smw-schema-type": "é¡žåž‹",
+ "smw-schema-description-link-format-schema": "此架構類型é æœŸä¾†å®šç¾©å‡ºèˆ‡åˆ†é…給屬性的[[Property:Formatter schema|æ ¼å¼åŒ–架構]]相關,用於創建上下文有關連çµçš„字元。",
+ "smw-schema-description-search-form-schema": "此架構類型é æœŸç”¨ä¾†å®šç¾©ç”¨æ–¼[https://www.semantic-mediawiki.org/wiki/Help:SMWSearch æ“´å……æœå°‹]å好設定的輸入表單與字元,其中包å«å¦‚何產生輸入欄ä½ã€å®šç¾©é è¨­å‘½å空間ã€æˆ–是宣告用於æœå°‹è«‹æ±‚之字首表é”的說明。",
+ "smw-schema-tag": "{{PLURAL:$1|標籤|標籤}}",
+ "smw-property-predefined-schema-desc": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,儲存架構æè¿°çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-schema-def": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,儲存架構內容的é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-schema-tag": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] æ供,識別綱è¦æ”¶é›†çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-schema-tag": "識別相似內容或特徵之綱è¦çš„標籤。",
+ "smw-property-predefined-schema-type": "「$1ã€æ˜¯ç”± [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki] 所æ供,用來æè¿°å¯å€åˆ†æž¶æ§‹é¡žåž‹çš„é å…ˆå®šç¾©å±¬æ€§ã€‚",
+ "smw-property-predefined-long-schema-type": "å„[https://www.semantic-mediawiki.org/wiki/Help:Schema/Type é¡žåž‹]é æœŸé€éŽ[https://www.semantic-mediawiki.org/wiki/Help:Schema#validation 驗證模組]來æ供本身的語法元素闡明ã€é™åˆ¶ã€è¡¨é”。",
+ "smw-ask-title-keyword-type": "é—œéµå­—æœå°‹",
+ "smw-ask-message-keyword-type": "æ­¤æœå°‹ç¬¦åˆ <code><nowiki>$1</nowiki></code> æ¢ä»¶ã€‚",
+ "smw-remote-source-unavailable": "無法連接至é ç«¯ã€Œ$1ã€ç›®æ¨™ã€‚",
+ "smw-remote-source-disabled": "來æº'''$1'''å·²åœç”¨é ç«¯è«‹æ±‚支æ´ï¼",
+ "smw-remote-source-unmatched-id": "來æºã€Œ'''$1'''ã€ä¸ç¬¦åˆå¯æ”¯æ´é ç«¯è«‹æ±‚çš„ Semantic MediaWiki 版本。",
+ "smw-remote-request-note": "çµæžœå·²å¾žé ç«¯ä¾†æºã€Œ'''$1'''ã€æª¢ç´¢ï¼Œä¸¦ä¸”å¯èƒ½ç”¨æ–¼ç”¢ç”ŸåŒ…å«ç›®å‰ wiki çš„ä¸å¯ç”¨è³‡è¨Šå…§å®¹ã€‚",
+ "smw-remote-request-note-cached": "çµæžœå·²å¾žé ç«¯ä¾†æºã€Œ'''$1'''ã€çµ¦'''å¿«å–''',並且å¯èƒ½ç”¨æ–¼ç”¢ç”ŸåŒ…å«ç›®å‰ wiki çš„ä¸å¯ç”¨è³‡è¨Šå…§å®¹ã€‚",
+ "smw-parameter-missing": "éºå¤±åƒæ•¸ã€Œ$1ã€ã€‚",
+ "smw-property-tab-usage": "使用é‡",
+ "smw-property-tab-redirects": "åŒç¾©è©ž",
+ "smw-property-tab-subproperties": "å­å±¬æ€§",
+ "smw-property-tab-errors": "ä¸é©ç•¶åˆ†é…",
+ "smw-property-tab-specification": "... 更多",
+ "smw-concept-tab-list": "清單",
+ "smw-concept-tab-errors": "錯誤",
+ "smw-ask-tab-result": "çµæžœ",
+ "smw-ask-tab-extra": "é¡å¤–",
+ "smw-ask-tab-debug": "除錯",
+ "smw-ask-tab-code": "代碼",
+ "smw-install-incomplete-intro": "<b>Semantic MediaWiki</b> 的安è£ï¼ˆæˆ–更新)尚未çµæŸï¼Œåœ¨ä½¿ç”¨è€…繼續創建或更動內容之å‰ï¼Œç®¡ç†å“¡éœ€åŸ·è¡Œä»¥ä¸‹ä»»å‹™ä¾†é˜²æ­¢è³‡æ–™ä¸ä¸€è‡´ã€‚",
+ "smw-install-incomplete-populate-hash-field": "在設定éŽç¨‹ä¸­è£¡æœ‰ç•¥éŽå¡«å…… <code>smw_hash</code> 欄ä½ï¼Œéœ€è¦ [https://www.semantic-mediawiki.org/wiki/Help:populateHashField.php populateHashField.php] 手稿來執行。",
+ "smw-helplink": "https://www.semantic-mediawiki.org/wiki/Help:$1"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php b/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php
new file mode 100644
index 00000000..3fda57a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/ContentParser.php
@@ -0,0 +1,251 @@
+<?php
+
+namespace SMW;
+
+use Parser;
+use ParserOptions;
+use Revision;
+use Title;
+use User;
+
+/**
+ * Fetches the ParserOutput either by parsing an invoked text component,
+ * re-parsing a text revision, or accessing the ContentHandler to generate a
+ * ParserOutput object
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ContentParser {
+
+ /** @var Title */
+ protected $title;
+
+ /** @var Parser */
+ protected $parser = null;
+
+ /** @var ParserOutput */
+ protected $parserOutput = null;
+
+ /** @var Revision */
+ protected $revision = null;
+
+ /** @var array */
+ protected $errors = [];
+
+ /**
+ * @var boolean
+ */
+ private $enabledToUseContentHandler = true;
+
+ /**
+ * @var boolean
+ */
+ private $skipInTextAnnotationParser = false;
+
+ /**
+ * @note Injecting new Parser() alone will not yield an expected result and
+ * doing new Parser( $GLOBALS['wgParserConf'] brings no benefits therefore
+ * we stick to the GLOBAL as fallback if no parser is injected.
+ *
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param Parser|null $parser
+ */
+ public function __construct( Title $title, Parser $parser = null ) {
+ $this->title = $title;
+ $this->parser = $parser;
+
+ if ( $this->parser === null ) {
+ $this->parser = $GLOBALS['wgParser'];
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return Parser $parser
+ */
+ public function setParser( Parser $parser ) {
+ $this->parser = $parser;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return ContentParser
+ */
+ public function setRevision( Revision $revision = null ) {
+ $this->revision = $revision;
+ return $this;
+ }
+
+ /**
+ * @bug 62856 and #212
+ *
+ * @since 2.0
+ */
+ public function forceToUseParser() {
+ $this->enabledToUseContentHandler = false;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserOutput|null
+ */
+ public function getOutput() {
+ return $this->parserOutput;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ public function skipInTextAnnotationParser() {
+ return $this->skipInTextAnnotationParser = true;
+ }
+
+ /**
+ * Generates or fetches the ParserOutput object from an appropriate source
+ *
+ * @since 1.9
+ *
+ * @param string|null $text
+ *
+ * @return ContentParser
+ */
+ public function parse( $text = null ) {
+
+ if ( $text !== null ) {
+ return $this->parseText( $text );
+ }
+
+ if ( $this->hasContentHandler() && $this->enabledToUseContentHandler ) {
+ return $this->fetchFromContent();
+ }
+
+ return $this->fetchFromParser();
+ }
+
+ protected function parseText( $text ) {
+
+ $this->parserOutput = $this->parser->parse(
+ $text,
+ $this->getTitle(),
+ $this->makeParserOptions()
+ );
+
+ return $this;
+ }
+
+ /**
+ * @note Revision ID must be passed to the parser output to
+ * get revision variables correct
+ *
+ * @note If no content is available create an empty object
+ */
+ protected function fetchFromContent() {
+
+ if ( $this->getRevision() === null ) {
+ return $this->msgForNullRevision();
+ }
+
+ $content = $this->getRevision()->getContent( Revision::RAW );
+
+ if ( !$content ) {
+ $content = $this->getRevision()->getContentHandler()->makeEmptyContent();
+ }
+
+ $this->parserOutput = $content->getParserOutput(
+ $this->getTitle(),
+ $this->getRevision()->getId(),
+ null,
+ true
+ );
+
+ return $this;
+ }
+
+ protected function fetchFromParser() {
+
+ if ( $this->getRevision() === null ) {
+ return $this->msgForNullRevision();
+ }
+
+ $this->parserOutput = $this->parser->parse(
+ $this->getRevision()->getText(),
+ $this->getTitle(),
+ $this->makeParserOptions(),
+ true,
+ true,
+ $this->getRevision()->getID()
+ );
+
+ return $this;
+ }
+
+ protected function msgForNullRevision( $fname = __METHOD__ ) {
+ $this->errors = [ $fname . " No revision available for {$this->getTitle()->getPrefixedDBkey()}" ];
+ return $this;
+ }
+
+ protected function makeParserOptions() {
+
+ $user = null;
+
+ if ( $this->getRevision() !== null ) {
+ $user = User::newFromId( $this->getRevision()->getUser() );
+ }
+
+ $parserOptions = new ParserOptions( $user );
+
+ // Use the InterfaceMessage marker to skip InTextAnnotationParser
+ // processing
+ $parserOptions->setInterfaceMessage( $this->skipInTextAnnotationParser );
+
+ return $parserOptions;
+ }
+
+ protected function getRevision() {
+
+ if ( $this->revision instanceof Revision ) {
+ return $this->revision;
+ }
+
+ // Revision::READ_NORMAL is not specified in MW 1.19
+ if ( defined( 'Revision::READ_NORMAL' ) ) {
+ $this->revision = Revision::newFromTitle( $this->getTitle(), false, Revision::READ_NORMAL );
+ } else {
+ $this->revision = Revision::newFromTitle( $this->getTitle() );
+ }
+
+ \Hooks::run( 'SMW::Parser::ChangeRevision', [ $this->getTitle(), &$this->revision ] );
+
+ return $this->revision;
+ }
+
+ protected function hasContentHandler() {
+ return defined( 'CONTENT_MODEL_WIKITEXT' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php b/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php
new file mode 100644
index 00000000..317a6df5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/ExternalHooks.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * Static class for hooks handled by the Semantic MediaWiki extension.
+ *
+ * @since 1.7
+ *
+ * @file SemanticMediaWiki.hooks.php
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+final class SMWExternalHooks {
+
+ /**
+ * TODO
+ *
+ * @since 1.7
+ *
+ * @return boolean
+ */
+ public static function onPageSchemasRegistration() {
+ // @codeCoverageIgnoreStart
+ $GLOBALS['wgPageSchemasHandlerClasses'][] = 'SMWPageSchemas';
+
+ return true;
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Adds links to Admin Links page.
+ *
+ * @since 1.7
+ *
+ * @param ALTree $admin_links_tree
+ *
+ * @return boolean
+ */
+ public static function addToAdminLinks( ALTree $admin_links_tree ) {
+ // @codeCoverageIgnoreStart
+ $data_structure_section = new ALSection( wfMessage( 'smw_adminlinks_datastructure' )->text() );
+
+ $smw_row = new ALRow( 'smw' );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Categories' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Properties' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'UnusedProperties' ) );
+
+ $data_structure_section->addRow( $smw_row );
+ $smw_admin_row = new ALRow( 'smw_admin' );
+ $smw_admin_row->addItem( ALItem::newFromSpecialPage( 'SMWAdmin' ) );
+
+ $data_structure_section->addRow( $smw_admin_row );
+ $smw_docu_row = new ALRow( 'smw_docu' );
+ $smw_name = wfMessage( 'specialpages-group-smw_group' )->text();
+ $smw_docu_label = wfMessage( 'adminlinks_documentation', $smw_name )->text();
+ $smw_docu_row->addItem( AlItem::newFromExternalLink( 'http://semantic-mediawiki.org/wiki/Help:User_manual', $smw_docu_label ) );
+
+ $data_structure_section->addRow( $smw_docu_row );
+ $admin_links_tree->addSection( $data_structure_section, wfMessage( 'adminlinks_browsesearch' )->text() );
+ $smw_row = new ALRow( 'smw' );
+ $displaying_data_section = new ALSection( wfMessage( 'smw_adminlinks_displayingdata' )->text() );
+ $smw_row->addItem( AlItem::newFromExternalLink(
+ 'http://semantic-mediawiki.org/wiki/Help:Inline_queries',
+ wfMessage( 'smw_adminlinks_inlinequerieshelp' )->text()
+ ) );
+
+ $displaying_data_section->addRow( $smw_row );
+ $admin_links_tree->addSection( $displaying_data_section, wfMessage( 'adminlinks_browsesearch' )->text() );
+ $browse_search_section = $admin_links_tree->getSection( wfMessage( 'adminlinks_browsesearch' )->text() );
+
+ $smw_row = new ALRow( 'smw' );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Browse' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'Ask' ) );
+ $smw_row->addItem( ALItem::newFromSpecialPage( 'SearchByProperty' ) );
+ $browse_search_section->addRow( $smw_row );
+
+ return true;
+ // @codeCoverageIgnoreEnd
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php b/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php
new file mode 100644
index 00000000..6b659346
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Highlighter.php
@@ -0,0 +1,370 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMWOutputs;
+
+/**
+ * Highlighter utility function for Semantic MediaWiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Highlighter {
+
+ /**
+ * Highlighter ID for no types
+ */
+ const TYPE_NOTYPE = 0;
+
+ /**
+ * Highlighter ID for properties
+ */
+ const TYPE_PROPERTY = 1;
+
+ /**
+ * Highlighter ID for text
+ */
+ const TYPE_TEXT = 2;
+
+ /**
+ * Highlighter ID for quantities
+ */
+ const TYPE_QUANTITY = 3;
+
+ /**
+ * Highlighter ID for warnings
+ */
+ const TYPE_WARNING = 4;
+
+ /**
+ * Highlighter ID for error
+ */
+ const TYPE_ERROR = 5;
+
+ /**
+ * Highlighter ID for information
+ */
+ const TYPE_INFO = 6;
+
+ /**
+ * Highlighter ID for help
+ */
+ const TYPE_HELP = 7;
+
+ /**
+ * Highlighter ID for notes
+ */
+ const TYPE_NOTE = 8;
+
+ /**
+ * Highlighter ID for service links
+ */
+ const TYPE_SERVICE = 9;
+
+ /**
+ * Highlighter ID for reference links
+ */
+ const TYPE_REFERENCE = 10;
+
+ /**
+ * @var array $options
+ */
+ private $options;
+
+ /**
+ * @var int $type
+ */
+ private $type;
+
+ /**
+ * @var string|null
+ */
+ private $language = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param int $type
+ * @param string|null $language
+ */
+ public function __construct( $type, $language = null ) {
+ $this->type = $type;
+ $this->language = $language;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string|int $type
+ * @param string|null $language
+ *
+ * @return Highlighter
+ */
+ public static function factory( $type, $language = null ) {
+ if ( $type === '' || !is_int( $type ) ) {
+ $type = self::getTypeId( $type );
+ }
+
+ return new Highlighter( $type, $language );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ * @param string|null $type
+ *
+ * @return booelan
+ */
+ public static function hasHighlighterClass( $text, $type = null ) {
+
+ if ( strpos( $text, 'smw-highlighter' ) === false ) {
+ return false;
+ }
+
+ if ( $type !== null ) {
+ return strpos( $text, 'data-type="' . self::getTypeId( $type ) . '"' ) !== false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public static function decode( $text ) {
+ // #2347, '[' is handled by the MediaWiki parser/sanitizer itself
+ return str_replace(
+ [ '&amp;', '&lt;', '&gt;', '&#160;', '<nowiki>', '</nowiki>' ],
+ [ '&', '<', '>', ' ', '', '' ],
+ $text
+ );
+ }
+
+ /**
+ * Returns html output
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHtml() {
+ SMWOutputs::requireResource( 'ext.smw.tooltips' );
+ return $this->getContainer();
+ }
+
+ /**
+ * Set content
+ *
+ * An external class that invokes content via setContent has to ensure
+ * that all data are appropriately escaped.
+ *
+ * @since 1.9
+ *
+ * @param array $content
+ *
+ * @return array
+ */
+ public function setContent( array $content ) {
+ /**
+ * @var $content
+ * $content['caption'] = a text or null
+ * $content['context'] = a text or null
+ */
+ return $this->options = array_merge( $this->getTypeConfiguration( $this->type ), $content );
+ }
+
+ /**
+ * Returns type id
+ *
+ * @since 1.9
+ *
+ * @param string $type
+ *
+ * @return integer
+ */
+ public static function getTypeId( $type ) {
+ // TODO: why do we have a htmlspecialchars here?!
+ switch ( strtolower ( htmlspecialchars ( $type ) ) ) {
+ case 'property':
+ return self::TYPE_PROPERTY;
+ case 'text':
+ return self::TYPE_TEXT;
+ case 'quantity':
+ return self::TYPE_QUANTITY;
+ case 'warning':
+ return self::TYPE_WARNING;
+ case 'error':
+ return self::TYPE_ERROR;
+ case 'info':
+ return self::TYPE_INFO;
+ case 'help':
+ return self::TYPE_HELP;
+ case 'note':
+ return self::TYPE_NOTE;
+ case 'service':
+ return self::TYPE_SERVICE;
+ case 'reference':
+ return self::TYPE_REFERENCE;
+ default:
+ return self::TYPE_NOTYPE;
+ }
+ }
+
+ /**
+ * Builds Html container
+ *
+ * Content that is being invoked has to be escaped
+ * @see Highlighter::setContent
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ private function getContainer() {
+
+ $captionclass = $this->options['captionclass'];
+
+ // 2.4+ can display context for user-defined properties, here we ensure
+ // to keep the style otherwise it displays italic which is by convention
+ // reserved for predefined properties
+ if ( $this->type === self::TYPE_PROPERTY && isset( $this->options['userDefined'] ) ) {
+ $captionclass = $this->options['userDefined'] ? 'smwtext' : $captionclass;
+ }
+
+ $language = is_string( $this->language ) ? $this->language : Message::USER_LANGUAGE;
+ $style = [];
+
+ if ( isset( $this->options['style'] ) ) {
+ $style = [ 'style' => $this->options['style'] ];
+ }
+
+ // #1875
+ // title attribute contains stripped content to allow for a display in
+ // no-js environments, the tooltip will remove the element once it is
+ // loaded
+ $title = $this->title( $this->options['content'], $language );
+
+ $html = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-highlighter',
+ 'data-type' => $this->options['type'],
+ 'data-content' => isset( $this->options['data-content'] ) ? $this->options['data-content'] : null,
+ 'data-state' => $this->options['state'],
+ 'data-title' => Message::get( $this->options['title'], Message::TEXT, $language ),
+ 'title' => $title
+ ] + $style,
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => $captionclass
+ ],
+ $this->options['caption']
+ ) . Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smwttcontent'
+ ],
+ htmlspecialchars_decode( $this->options['content'] )
+ )
+ );
+
+ return $html;
+ }
+
+ /**
+ * Returns initial configuration settings
+ *
+ * @note You could create a class per entity type but does this
+ * really make sense just to get some configuration parameters?
+ *
+ * @since 1.9
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ private function getTypeConfiguration( $type ) {
+ $settings = [];
+ $settings['type'] = $type;
+ $settings['caption'] = '';
+ $settings['content'] = '';
+
+ switch ( $type ) {
+ case self::TYPE_PROPERTY:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-property';
+ $settings['captionclass'] = 'smwbuiltin';
+ break;
+ case self::TYPE_TEXT:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_QUANTITY:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-quantity';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_NOTE:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-note';
+ $settings['captionclass'] = 'smwtticon note';
+ break;
+ case self::TYPE_WARNING:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-warning';
+ $settings['captionclass'] = 'smwtticon warning';
+ break;
+ case self::TYPE_ERROR:
+ $settings['state'] = 'inline';
+ $settings['title'] = 'smw-ui-tooltip-title-error';
+ $settings['captionclass'] = 'smwtticon error';
+ break;
+ case self::TYPE_SERVICE:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-service';
+ $settings['captionclass'] = 'smwtticon service';
+ break;
+ case self::TYPE_REFERENCE:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-reference';
+ $settings['captionclass'] = 'smwtext';
+ break;
+ case self::TYPE_HELP:
+ case self::TYPE_INFO:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwtticon info';
+ break;
+ case self::TYPE_NOTYPE:
+ default:
+ $settings['state'] = 'persistent';
+ $settings['title'] = 'smw-ui-tooltip-title-info';
+ $settings['captionclass'] = 'smwbuiltin';
+ };
+
+ return $settings;
+ }
+
+ private function title( $content, $language ) {
+
+ // Pre-process the content when used as title to avoid breaking elements
+ // (URLs etc.)
+ if ( strpos( $content, '[' ) !== false || strpos( $content, '//' ) !== false ) {
+ $content = Message::get( [ 'smw-parse', $content ], Message::PARSE, $language );
+ }
+
+ return strip_tags( htmlspecialchars_decode( str_replace( [ "[", '&#160;' ], [ "&#91;", ' ' ], $content ) ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php b/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php
new file mode 100644
index 00000000..3c83dd78
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/IdeAliases.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Indicate class aliases in a way PHPStorm and Eclipse understand.
+ * This is purely an IDE helper file, and is not loaded by the extension.
+ *
+ * @since 1.9
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+throw new Exception( 'Not an actual source file' );
+
+class SMWDataItemException extends SMW\Exception\DataItemException {
+}
+
+abstract class SMWStore extends SMW\Store {
+}
+
+class SMWSemanticData extends SMW\SemanticData {
+}
+
+class SMWDIWikiPage extends SMW\DIWikiPage {
+}
+
+class SMWDIConcept extends SMW\DIConcept {
+}
+
+class SMWDIProperty extends SMW\DIProperty {
+}
+
+class SMWDISerializer extends SMW\Serializers\QueryResultSerializer {
+}
+
+class SMWUpdateJob extends SMW\UpdateJob {
+}
+
+class SMWRefreshJob extends SMW\RefreshJob {
+}
+
+abstract class SMWResultPrinter extends SMW\ResultPrinter {
+}
+
+class SMWCategoryResultPrinter extends SMW\Query\ResultPrinters\CategoryResultPrinter {
+}
+
+class SMWDSVResultPrinter extends SMW\DsvResultPrinter {
+}
+
+class SMWEmbeddedResultPrinter extends SMW\EmbeddedResultPrinter {
+}
+
+class SMWRDFResultPrinter extends SMW\RdfResultPrinter {
+}
+
+class SMWListResultPrinter extends SMW\Query\ResultPrinters\ListResultPrinter {
+}
+
+interface SMWIResultPrinter extends SMW\QueryResultPrinter {
+}
+
+class SMWSparqlDatabase4Store extends SMW\SPARQLStore\FourstoreHttpDatabaseConnector {
+}
+
+class SMWSparqlDatabaseVirtuoso extends SMW\SPARQLStore\VirtuosoHttpDatabaseConnector {
+}
+
+class SMWSparqlStore extends SMW\SPARQLStore\SPARQLStore {
+}
+
+class SMWSparqlDatabase extends SMW\SPARQLStore\GenericHttpDatabaseConnector {
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php b/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php
new file mode 100644
index 00000000..3481405e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/QueryPrinterFactory.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use SMW\Query\Exception\ResultFormatNotFoundException;
+
+/**
+ * Factory for "result formats", ie classes implementing QueryResultPrinter.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5 (since 1.9, renamed in 2.5)
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+final class QueryPrinterFactory {
+
+ /**
+ * Returns the global instance of the factory.
+ *
+ * @since 2.5
+ *
+ * @return QueryPrinterFactory
+ */
+ public static function singleton() {
+ static $instance = null;
+
+ if ( $instance === null ) {
+ $instance = self::newFromGlobalState();
+ }
+
+ return $instance;
+ }
+
+ private static function newFromGlobalState() {
+ $instance = new self();
+
+ foreach ( $GLOBALS['smwgResultFormats'] as $formatName => $printerClass ) {
+ $instance->registerFormat( $formatName, $printerClass );
+ }
+
+ foreach ( $GLOBALS['smwgResultAliases'] as $formatName => $aliases ) {
+ $instance->registerAliases( $formatName, $aliases );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Format registry. Format names pointing to their associated QueryResultPrinter implementing classes.
+ *
+ * @var string[]
+ */
+ private $formats = [];
+
+ /**
+ * Form alias registry. Aliases pointing to their canonical format name.
+ *
+ * @var string[]
+ */
+ private $aliases = [];
+
+ /**
+ * Registers a format.
+ * If there is a format already with the provided name,
+ * it will be overridden with the newly provided data.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ * @param string $class
+ *
+ * @throws InvalidArgumentException
+ */
+ public function registerFormat( $formatName, $class ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ if ( !is_string( $class ) ) {
+ throw new InvalidArgumentException( 'Format class names can only be of type string' );
+ }
+
+ $this->formats[$formatName] = $class;
+ }
+
+ /**
+ * Registers the provided format aliases.
+ * If an aliases is already registered, it will
+ * be overridden with the newly provided data.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ * @param array $aliases
+ *
+ * @throws InvalidArgumentException
+ */
+ public function registerAliases( $formatName, array $aliases ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ foreach ( $aliases as $alias ) {
+ if ( !is_string( $alias ) ) {
+ throw new InvalidArgumentException( 'Format aliases can only be of type string' );
+ }
+
+ $this->aliases[$alias] = $formatName;
+ }
+ }
+
+ /**
+ * Returns the canonical format names.
+ *
+ * @since 2.5
+ *
+ * @return string[]
+ */
+ public function getFormats() {
+ return array_keys( $this->formats );
+ }
+
+ /**
+ * Returns if there is a format or format alias with the provided name.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return boolean
+ */
+ public function hasFormat( $formatName ) {
+ $formatName = $this->getCanonicalName( $formatName );
+ return array_key_exists( $formatName, $this->formats );
+ }
+
+ /**
+ * Returns a new instance of the handling result printer for the provided format.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName
+ *
+ * @return QueryResultPrinter
+ * @throws ResultFormatNotFoundException
+ */
+ public function getPrinter( $formatName ) {
+ $class = $this->getPrinterClass( $formatName );
+ return new $class( $formatName );
+ }
+
+ /**
+ * Returns the QueryResultPrinter implementing class that is the printer for the provided format.
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return string
+ * @throws ResultFormatNotFoundException
+ */
+ private function getPrinterClass( $formatName ) {
+ $formatName = $this->getCanonicalName( $formatName );
+
+ if ( !array_key_exists( $formatName, $this->formats ) ) {
+ throw new ResultFormatNotFoundException( 'Unknown format name "' . $formatName . '" has no associated printer class' );
+ }
+
+ return $this->formats[$formatName];
+ }
+
+ /**
+ * Resolves format aliases into their associated canonical format name.
+ *
+ * @since 2.5
+ *
+ * @param string $formatName Format name or alias
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function getCanonicalName( $formatName ) {
+
+ if ( !is_string( $formatName ) ) {
+ throw new InvalidArgumentException( 'Format names can only be of type string' );
+ }
+
+ if ( array_key_exists( $formatName, $this->aliases ) ) {
+ $formatName = $this->aliases[$formatName];
+ }
+
+ return $formatName;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php b/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php
new file mode 100644
index 00000000..1a171fec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/RecurringEvents.php
@@ -0,0 +1,374 @@
+<?php
+
+namespace SMW;
+
+use SMWDITime;
+
+/**
+ * TODO This class needs some real refactoring!
+ *
+ * @private This class should not be instantiated directly.
+ *
+ * This class determines recurring events based on invoked parameters
+ *
+ * @see https://www.semantic-mediawiki.org/wiki/Help:Recurring_events
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Yaron Koren
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class RecurringEvents {
+
+ /**
+ * Defines the property used
+ */
+ private $property = null;
+
+ /**
+ * Defines the dates
+ */
+ private $dates = [];
+
+ /**
+ * Defines remaining / unused parameters
+ */
+ private $parameters = [];
+
+ /**
+ * Defines errors
+ */
+ private $errors = [];
+
+ /**
+ * @var integer
+ */
+ private $defaultNumRecurringEvents = 25;
+
+ /**
+ * @var integer
+ */
+ private $maxNumRecurringEvents = 25;
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $defaultNumRecurringEvents
+ */
+ public function setDefaultNumRecurringEvents( $defaultNumRecurringEvents ) {
+ $this->defaultNumRecurringEvents = $defaultNumRecurringEvents;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $maxNumRecurringEvents
+ */
+ public function setMaxNumRecurringEvents( $maxNumRecurringEvents ) {
+ $this->maxNumRecurringEvents = $maxNumRecurringEvents;
+ }
+
+ /**
+ * Returns property used
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * Returns dates
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getDates() {
+ return $this->dates;
+ }
+
+ /**
+ * Returns unused parameters
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns errors
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Set error
+ *
+ * @since 1.9
+ *
+ * @return mixed
+ */
+ private function setError( $error ) {
+ $this->errors = array_merge( $error, $this->errors );
+ }
+
+ /**
+ * Returns the "Julian day" value from an object of type
+ * SMWTimeValue.
+ */
+ public function getJulianDay( $dateDataValue ) {
+ if ( is_null( $dateDataValue ) ) {
+ return null;
+ }
+ $dateDataItem = $dateDataValue->getDataItem();
+ // This might have returned an 'SMWDIError' object.
+ if ( $dateDataItem instanceof SMWDITime ) {
+ return $dateDataItem->getJD();
+ }
+ return null;
+ }
+
+ /**
+ * Parse parameters and set internal properties
+ *
+ * @since 1.9
+ *
+ * @param array $parameters
+ */
+ public function parse( array $parameters ) {
+ // Initialize variables.
+ $all_date_strings = [];
+ $start_date = $end_date = $unit = $period = $week_num = null;
+ $included_dates = [];
+ $excluded_dates = [];
+ $excluded_dates_jd = [];
+
+ // Parse parameters and assign values
+ foreach ( $parameters as $name => $values ) {
+
+ foreach ( $values as $value ) {
+ switch( $name ) {
+ case 'property':
+ $this->property = $value;
+ break;
+ case 'start':
+ $start_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $value );
+ break;
+ case 'end':
+ $end_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $value );
+ break;
+ case 'limit':
+ // Override default limit with query specific limit
+ $this->defaultNumRecurringEvents = (int)$value;
+ break;
+ case 'unit':
+ $unit = $value;
+ break;
+ case 'period':
+ $period = (int)$value;
+ break;
+ case 'week number':
+ $week_num = (int)$value;
+ break;
+ case 'include':
+ // This is for compatibility only otherwise we break
+ // to much at once. Instead of writing include=...;...
+ // it should be include=...;...|+sep=; because the
+ // ParameterParser class is conditioned to split those
+ // parameter accordingly
+ if ( strpos( $value, ';' ) ){
+ $included_dates = explode( ';', $value );
+ } else {
+ $included_dates[] = $value;
+ }
+ break;
+ case 'exclude':
+ // Some as above
+ if ( strpos( $value, ';' ) ){
+ $excluded_dates = explode( ';', $value );
+ } else {
+ $excluded_dates[] = $value;
+ }
+ break;
+ default:
+ $this->parameters[$name][] = $value;
+ }
+ }
+ }
+
+ if ( $start_date === null ) {
+ $this->errors[] = Message::get( 'smw-events-start-date-missing' );
+ return;
+ } elseif ( !( $start_date->getDataItem() instanceof SMWDITime ) ) {
+ $this->setError( $start_date->getErrors() );
+ return;
+ }
+
+ // Check property
+ if ( is_null( $this->property ) ) {
+ $this->errors[] = Message::get( 'smw-events-property-missing' );
+ return;
+ }
+
+ // Exclude dates
+ foreach ( $excluded_dates as $date_str ) {
+ $excluded_dates_jd[] = $this->getJulianDay(
+ DataValueFactory::getInstance()->newTypeIDValue( '_dat', $date_str )
+ );
+ }
+
+ // If the period is null, or outside of normal bounds, set it to 1.
+ if ( is_null( $period ) || $period < 1 || $period > 500 ) {
+ $period = 1;
+ }
+
+ // Handle 'week number', but only if it's of unit 'month'.
+ if ( $unit == 'month' && ! is_null( $week_num ) ) {
+ $unit = 'dayofweekinmonth';
+
+ if ( $week_num < -4 || $week_num > 5 || $week_num == 0 ) {
+ $week_num = null;
+ }
+ }
+
+ if ( $unit == 'dayofweekinmonth' && is_null( $week_num ) ) {
+ $week_num = ceil( $start_date->getDay() / 7 );
+ }
+
+ // Get the Julian day value for both the start and end date.
+ $end_date_jd = $this->getJulianDay( $end_date );
+
+ $cur_date = $start_date;
+ $cur_date_jd = $this->getJulianDay( $cur_date );
+ $i = 0;
+
+ do {
+ $i++;
+ $exclude_date = ( in_array( $cur_date_jd, $excluded_dates_jd ) );
+
+ if ( !$exclude_date ) {
+ $all_date_strings[] = $cur_date->getLongWikiText();
+ }
+
+ // Now get the next date.
+ // Handling is different depending on whether it's
+ // month/year or week/day since the latter is a set
+ // number of days while the former isn't.
+ if ( $unit === 'year' || $unit == 'month' ) {
+ $cur_year = $cur_date->getYear();
+ $cur_month = $cur_date->getMonth();
+ $cur_day = $start_date->getDay();
+ $cur_time = $cur_date->getTimeString();
+
+ if ( $unit == 'year' ) {
+ $cur_year += $period;
+ $display_month = $cur_month;
+ } else { // $unit === 'month'
+ $cur_month += $period;
+ $cur_year += (int)( ( $cur_month - 1 ) / 12 );
+ $cur_month %= 12;
+ $display_month = ( $cur_month == 0 ) ? 12 : $cur_month;
+ }
+
+ // If the date is greater than 28 for February, and it is not
+ // a leap year, change it to be a fixed 28 otherwise set it to
+ // 29 (for a leap year date)
+ if ( $cur_month == 2 && $cur_day > 28 ) {
+ $cur_day = !date( 'L', strtotime( "$cur_year-1-1" ) ) ? 28 : 29;
+ } elseif ( $cur_day > 30 ) {
+ // Check whether 31 is a valid day of a month
+ $cur_day = ( $display_month - 1 ) % 7 % 2 ? 30 : 31;
+ }
+
+ $date_str = "$cur_year-$display_month-$cur_day $cur_time";
+ $cur_date = DataValueFactory::getInstance()->newTypeIDValue( '_dat', $date_str );
+ $all_date_strings = array_merge( $all_date_strings, $included_dates);
+ if ( $cur_date->isValid() ) {
+ $cur_date_jd = $cur_date->getDataItem()->getJD();
+ }
+ } elseif ( $unit == 'dayofweekinmonth' ) {
+ // e.g., "3rd Monday of every month"
+ $prev_month = $cur_date->getMonth();
+ $prev_year = $cur_date->getYear();
+
+ $new_month = ( $prev_month + $period ) % 12;
+ if ( $new_month == 0 ) {
+ $new_month = 12;
+ }
+
+ $new_year = $prev_year + floor( ( $prev_month + $period - 1 ) / 12 );
+ $cur_date_jd += ( 28 * $period ) - 7;
+
+ // We're sometime before the actual date now -
+ // keep incrementing by a week, until we get there.
+ do {
+ $cur_date_jd += 7;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ $right_month = ( $cur_date->getMonth() == $new_month );
+
+ if ( $week_num < 0 ) {
+ $next_week_jd = $cur_date_jd;
+
+ do {
+ $next_week_jd += 7;
+ $next_week_date = $this->getJulianDayTimeValue( $next_week_jd );
+ $right_week = ( $next_week_date->getMonth() != $new_month ) || ( $next_week_date->getYear() != $new_year );
+ } while ( !$right_week );
+
+ $cur_date_jd = $next_week_jd + ( 7 * $week_num );
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ } else {
+ $cur_week_num = ceil( $cur_date->getDay() / 7 );
+ $right_week = ( $cur_week_num == $week_num );
+
+ if ( $week_num == 5 && ( $cur_date->getMonth() % 12 == ( $new_month + 1 ) % 12 ) ) {
+ $cur_date_jd -= 7;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ $right_month = $right_week = true;
+ }
+ }
+ } while ( !$right_month || !$right_week);
+ } else { // $unit == 'day' or 'week'
+ // Assume 'day' if it's none of the above.
+ $cur_date_jd += ( $unit === 'week' ) ? 7 * $period : $period;
+ $cur_date = $this->getJulianDayTimeValue( $cur_date_jd );
+ }
+
+ // should we stop?
+ if ( is_null( $end_date ) ) {
+ $reached_end_date = $i > $this->defaultNumRecurringEvents;
+ } else {
+ $reached_end_date = ( $cur_date_jd > $end_date_jd ) || ( $i > $this->maxNumRecurringEvents );
+ }
+ } while ( !$reached_end_date );
+
+ // Handle the 'include' dates as well.
+ $all_date_strings = array_filter( array_merge( $all_date_strings, $included_dates ) );
+
+ // Set dates
+ $this->dates = str_replace( ' 00:00:00', '', $all_date_strings );
+ }
+
+ /**
+ * Helper function - creates an object of type SMWTimeValue based
+ * on a "Julian day" integer
+ */
+ private function getJulianDayTimeValue( $jd ) {
+ $timeDataItem = SMWDITime::newFromJD( $jd, SMWDITime::CM_GREGORIAN, SMWDITime::PREC_YMDT );
+ return DataValueFactory::getInstance()->newDataValueByItem( $timeDataItem );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php
new file mode 100644
index 00000000..18a37b30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Infolink.php
@@ -0,0 +1,666 @@
+<?php
+
+use SMW\Site;
+
+/**
+ * This class mainly is a container to store URLs for the factbox in a
+ * clean way. The class provides methods for creating source code for
+ * realising them in wiki or html contexts.
+ *
+ * @license GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class SMWInfolink {
+
+ const LINK_UPPER_LENGTH_RESTRICTION = 2000;
+
+ /**
+ * The actual link target.
+ *
+ * @var string
+ */
+ protected $mTarget;
+
+ /**
+ * The label for the link.
+ *
+ * @var string
+ */
+ protected $mCaption;
+
+ /**
+ * CSS class of a span to embedd the link into,
+ * or false if no extra style is required.
+ *
+ * @var mixed
+ */
+ protected $mStyle;
+
+ /**
+ * @var array
+ */
+ private $linkAttributes = [];
+
+ /**
+ * Indicates whether $target is a page name (true) or URL (false).
+ *
+ * @var boolean
+ */
+ protected $mInternal;
+
+ /**
+ * Array of parameters, format $name => $value, if any.
+ *
+ * @var array
+ */
+ protected $mParams;
+
+ /**
+ * @var boolean
+ */
+ private $isRestricted = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCompactLink = false;
+
+ /**
+ * Create a new link to some internal page or to some external URL.
+ *
+ * @param boolean $internal Indicates whether $target is a page name (true) or URL (false).
+ * @param string $caption The label for the link.
+ * @param string $target The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ */
+ public function __construct( $internal, $caption, $target, $style = false, array $params = [] ) {
+ $this->mInternal = $internal;
+ $this->mCaption = $caption;
+ $this->mTarget = $target;
+ $this->mStyle = $style;
+ $this->mParams = $params;
+ $this->setCompactLink( $GLOBALS['smwgCompactLinkSupport'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isRestricted
+ */
+ public function isRestricted( $isRestricted ) {
+ $this->isRestricted = (bool)$isRestricted;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isCompactLink
+ */
+ public function setCompactLink( $isCompactLink = true ) {
+ $this->isCompactLink = (bool)$isCompactLink;
+ }
+
+ /**
+ * Create a new link to an internal page $target.
+ * All parameters are mere strings as used by wiki users.
+ *
+ * @param string $caption The label for the link.
+ * @param string $target The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ *
+ * @return SMWInfolink
+ */
+ public static function newInternalLink( $caption, $target, $style = false, array $params = [] ) {
+ return new SMWInfolink( true, $caption, $target, $style, $params );
+ }
+
+ /**
+ * Create a new link to an external location $url.
+ *
+ * @param string $caption The label for the link.
+ * @param string $url The actual link target.
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ * @param array $params Array of parameters, format $name => $value, if any.
+ *
+ * @return SMWInfolink
+ */
+ public static function newExternalLink( $caption, $url, $style = false, array $params = [] ) {
+ return new SMWInfolink( false, $caption, $url, $style, $params );
+ }
+
+ /**
+ * Static function to construct links to property searches.
+ *
+ * @param string $caption The label for the link.
+ * @param string $propertyName
+ * @param string $propertyValue
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newPropertySearchLink( $caption, $propertyName, $propertyValue, $style = 'smwsearch' ) {
+ global $wgContLang;
+
+ $infolink = new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':SearchByProperty',
+ $style,
+ [ ':' . $propertyName, $propertyValue ] // `:` is marking that the link was auto-generated
+ );
+
+ // Link that reaches a length restriction will most likely cause a
+ // "HTTP 414 "Request URI too long ..." therefore prevent a link creation
+ if ( mb_strlen( $propertyName . $propertyValue ) > self::LINK_UPPER_LENGTH_RESTRICTION ) {
+ $infolink->isRestricted( true );
+ }
+
+ return $infolink;
+ }
+
+ /**
+ * Static function to construct links to inverse property searches.
+ *
+ * @param string $caption The label for the link.
+ * @param string $subject
+ * @param string $propertyName
+ * @param mixed $style CSS class of a span to embed the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newInversePropertySearchLink( $caption, $subject, $propertyname, $style = false ) {
+ global $wgContLang;
+ return new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':PageProperty',
+ $style,
+ [ $subject . '::' . $propertyname ]
+ );
+ }
+
+ /**
+ * Static function to construct links to the browsing special.
+ *
+ * @param string $caption The label for the link.
+ * @param string $titleText
+ * @param mixed $style CSS class of a span to embedd the link into, or false if no extra style is required.
+ *
+ * @return SMWInfolink
+ */
+ public static function newBrowsingLink( $caption, $titleText, $style = 'smwbrowse' ) {
+ global $wgContLang;
+ return new SMWInfolink(
+ true,
+ $caption,
+ $wgContLang->getNsText( NS_SPECIAL ) . ':Browse',
+ $style,
+ [ ':' . $titleText ]
+ );
+ }
+
+ /**
+ * Set (or add) parameter values for an existing link.
+ *
+ * @param mixed $value
+ * @param mixed $key
+ */
+ public function setParameter( $value, $key = false ) {
+ if ( $key === false ) {
+ $this->mParams[] = $value;
+ } else {
+ $this->mParams[$key] = $value;
+ }
+ }
+
+ /**
+ * Get the value of some named parameter, or null if no parameter of
+ * that name exists.
+ */
+ public function getParameter( $key ) {
+ if ( array_key_exists( $key, $this->mParams ) ) {
+ return $this->mParams[$key];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Change the link text.
+ */
+ public function setCaption( $caption ) {
+ $this->mCaption = $caption;
+ }
+
+ /**
+ * Change the link's CSS class.
+ */
+ public function setStyle( $style ) {
+ $this->mStyle = $style;
+ }
+
+ /**
+ * Modify link attributes
+ *
+ * @since 3.0
+ *
+ * @param array $linkAttributes
+ */
+ public function setLinkAttributes( array $linkAttributes ) {
+ $this->linkAttributes = $linkAttributes;
+ }
+
+ /**
+ * Returns a suitable text string for displaying this link in HTML or wiki, depending
+ * on whether $outputformat is SMW_OUTPUT_WIKI or SMW_OUTPUT_HTML.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output). Some default linker will be created
+ * if needed and not provided.
+ */
+ public function getText( $outputformat, $linker = null ) {
+
+ if ( $this->isRestricted ) {
+ return '';
+ }
+
+ if ( $this->mStyle !== false ) {
+ SMWOutputs::requireResource( 'ext.smw.style' );
+ $start = "<span class=\"$this->mStyle\">";
+ $end = '</span>';
+ } else {
+ $start = '';
+ $end = '';
+ }
+
+ if ( $this->mInternal ) {
+ if ( count( $this->mParams ) > 0 ) {
+
+ $query = self::encodeParameters( $this->mParams );
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query );
+ }
+
+ $titletext = $this->mTarget . '/' . $query;
+ } else {
+ $titletext = $this->mTarget;
+ }
+
+ $title = Title::newFromText( $titletext );
+
+ if ( $title !== null ) {
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+ $link = "[[$titletext|$this->mCaption]]";
+ } elseif ( $outputformat == SMW_OUTPUT_RAW ) {
+ return $this->getURL();
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+ $link = $this->getLinker( $linker )->link( $title, $this->mCaption, $this->linkAttributes );
+ }
+ } else {
+ // Title creation failed, maybe illegal symbols or too long; make
+ // a direct URL link (only possible if offending target parts belong
+ // to some parameter that can be separated from title text, e.g.
+ // as in Special:Bla/il<leg>al -> Special:Bla&p=il&lt;leg&gt;al)
+ $title = Title::newFromText( $this->mTarget );
+
+ // Just give up due to the title being bad, normally this would
+ // indicate a software bug
+ if ( $title === null ) {
+ return '';
+ }
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query, false );
+ }
+
+ $link = '[' . $title->getFullURL( $query ) . " $this->mCaption]";
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+
+ if ( $this->isCompactLink ) {
+ $query = self::encodeCompactLink( $query, true );
+ } else {
+ // #511, requires an array
+ $query = wfCgiToArray( $query );
+ }
+
+ $link = $this->getLinker( $linker )->link(
+ $title,
+ $this->mCaption,
+ $this->linkAttributes,
+ $query
+ );
+ }
+ }
+ } else {
+ $target = $this->getURL();
+
+ if ( $outputformat == SMW_OUTPUT_WIKI ) {
+ $link = "[$target $this->mCaption]";
+ } else { // SMW_OUTPUT_HTML, SMW_OUTPUT_FILE
+ $link = '<a href="' . htmlspecialchars( $target ) . "\">$this->mCaption</a>";
+ }
+ }
+
+ return $start . $link . $end;
+ }
+
+ /**
+ * Return hyperlink for this infolink in HTML format.
+ *
+ * @return string
+ */
+ public function getHTML( $linker = null ) {
+ return $this->getText( SMW_OUTPUT_HTML, $linker );
+ }
+
+ /**
+ * Return hyperlink for this infolink in wiki format.
+ *
+ * @return string
+ */
+ public function getWikiText( $linker = null ) {
+ return $this->getText( SMW_OUTPUT_WIKI, $linker );
+ }
+
+ /**
+ * Return a fully qualified URL that points to the link target (whether internal or not).
+ * This function might be used when the URL is needed outside normal links, e.g. in the HTML
+ * header or in some metadata file. For making normal links, getText() should be used.
+ *
+ * @return string
+ */
+ public function getURL() {
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $this->isCompactLink && $query !== '' ) {
+ $query = self::encodeCompactLink( $query, true );
+ }
+
+ if ( !$this->mInternal ) {
+ return $this->buildTarget( $query );
+ }
+
+ $title = Title::newFromText( $this->mTarget );
+
+ if ( $title !== null ) {
+ return $title->getFullURL( $query );
+ }
+
+ // the title was bad, normally this would indicate a software bug
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getLocalURL() {
+
+ $query = self::encodeParameters( $this->mParams, $this->isCompactLink );
+
+ if ( $this->isCompactLink && $query !== '' ) {
+ $query = self::encodeCompactLink( $query, true );
+ }
+
+ if ( !$this->mInternal ) {
+ return $this->buildTarget( $query );
+ }
+
+ $title = Title::newFromText( $this->mTarget );
+
+ if ( $title !== null ) {
+ return $title->getLocalURL( $query );
+ }
+
+ // the title was bad, normally this would indicate a software bug
+ return '';
+ }
+
+ /**
+ * Return a Linker object, using the parameter $linker if not null, and creatng a new one
+ * otherwise. $linker is usually a user skin object, while the fallback linker object is
+ * not customised to user settings.
+ *
+ * @return Linker
+ */
+ protected function getLinker( &$linker = null ) {
+ if ( is_null( $linker ) ) {
+ $linker = new Linker;
+ }
+ return $linker;
+ }
+
+ /**
+ * Encode an array of parameters, formatted as $name => $value, to a parameter
+ * string that can be used for linking. If $forTitle is true (default), then the
+ * parameters are encoded for use in a MediaWiki page title (useful for making
+ * internal links to parameterised special pages), otherwise the parameters are
+ * encoded HTTP GET style. The parameter name "x" is used to collect parameters
+ * that do not have any string keys in GET, and hence "x" should never be used
+ * as a parameter name.
+ *
+ * The function SMWInfolink::decodeParameters() can be used to undo this encoding.
+ * It is strongly recommended to not create any code that depends on the concrete
+ * way of how parameters are encoded within this function, and to always use the
+ * respective encoding/decoding methods instead.
+ *
+ * @param array $params
+ * @param boolean $forTitle
+ */
+ static public function encodeParameters( array $params, $forTitle = true ) {
+ $result = '';
+
+ if ( $forTitle ) {
+ foreach ( $params as $name => $value ) {
+ if ( is_string( $name ) && ( $name !== '' ) ) {
+ $value = $name . '=' . $value;
+ }
+ // Escape certain problematic values. Use SMW-escape
+ // (like URLencode but - instead of % to prevent double encoding by later MW actions)
+ //
+ /// : SMW's parameter separator, must not occur within params
+ // - : used in SMW-encoding strings, needs escaping too
+ // [ ] < > &lt; &gt; '' |: problematic in MW titles
+ // & : sometimes problematic in MW titles ([[&amp;]] is OK, [[&test]] is OK, [[&test;]] is not OK)
+ // (Note: '&' in strings obtained during parsing already has &entities; replaced by
+ // UTF8 anyway)
+ // ' ': are equivalent with '_' in MW titles, but are not equivalent in certain parameter values
+ // "\n": real breaks not possible in [[...]]
+ // "#": has special meaning in URLs, triggers additional MW escapes (using . for %)
+ // '%': must be escaped to prevent any impact of double decoding when replacing -
+ // by % before urldecode
+ // '?': if not escaped, strange effects were observed on some sites (printout and other
+ // parameters ignored without obvious cause); SMW-escaping is always save to do -- it just
+ // make URLs less readable
+ //
+ $value = str_replace(
+ [ '-', '#', "\n", ' ', '/', '[', ']', '<', '>', '&lt;', '&gt;', '&amp;', '\'\'', '|', '&', '%', '?', '$', "\\", ";", "_" ],
+ [ '-2D', '-23', '-0A', '-20', '-2F', '-5B', '-5D', '-3C', '-3E', '-3C', '-3E', '-26', '-27-27', '-7C', '-26', '-25', '-3F', '-24', '-5C', "-3B", "-5F" ],
+ $value
+ );
+
+ if ( $result !== '' ) {
+ $result .= '/';
+ }
+
+ $result .= $value;
+ }
+ } else { // Note: this requires to have HTTP compatible parameter names (ASCII)
+ $q = []; // collect unlabelled query parameters here
+
+ foreach ( $params as $name => $value ) {
+ if ( is_string( $name ) && ( $name !== '' ) ) {
+ $value = rawurlencode( $name ) . '=' . rawurlencode( $value );
+
+ if ( $result !== '' ) {
+ $result .= '&';
+ }
+
+ $result .= $value;
+ } else {
+ $q[] = $value;
+ }
+ }
+ if ( count( $q ) > 0 ) { // prepend encoding for unlabelled parameters
+ if ( $result !== '' ) {
+ $result = '&' . $result;
+ }
+ $result = 'x=' . rawurlencode( self::encodeParameters( $q, true ) ) . $result;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Obtain an array of parameters from the parameters given to some HTTP service.
+ * In particular, this function performs all necessary decoding as may be needed, e.g.,
+ * to recover the proper parameter strings after encoding for use in wiki title names
+ * as done by SMWInfolink::encodeParameters().
+ *
+ * If $allparams is set to true, it is assumed that further data should be obtained
+ * from the global $wgRequest, and all given parameters are read.
+ *
+ * $titleparam is the string extracted by MediaWiki from special page calls of the
+ * form Special:Something/titleparam
+ * Note: it is assumed that the given $titleparam is already urldecoded, as is normal
+ * when getting such parameters from MediaWiki. SMW-escaped parameters largely prevent
+ * double decoding effects (i.e. there are no new "%" after one pass of urldecoding)
+ *
+ * The function SMWInfolink::encodeParameters() can be used to create a suitable
+ * encoding. It is strongly recommended to not create any code that depends on the
+ * concrete way of how parameters are encoded within this function, and to always use
+ * the respective encoding/decoding methods instead.
+ *
+ * @param string $titleParam
+ * @param boolean $allParams
+ */
+ static public function decodeParameters( $titleParam = '', $allParams = false ) {
+ global $wgRequest;
+
+ $result = [];
+
+ if ( $allParams ) {
+ $result = $wgRequest->getValues();
+
+ if ( array_key_exists( 'x', $result ) ) { // Considered to be part of the title param.
+ if ( $titleParam !== '' ) {
+ $titleParam .= '/';
+ }
+ $titleParam .= $result['x'];
+ unset( $result['x'] );
+ }
+ }
+
+ if ( is_array( $titleParam ) ) {
+ return $titleParam;
+ } elseif ( $titleParam !== '' ) {
+ // unescape $p; escaping scheme: all parameters rawurlencoded, "-" and "/" urlencoded, all "%" replaced by "-", parameters then joined with /
+ $ps = explode( '/', $titleParam ); // params separated by / here (compatible with wiki link syntax)
+
+ foreach ( $ps as $p ) {
+ if ( $p !== '' ) {
+ $result[] = rawurldecode( str_replace( '-', '%', $p ) );
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return string|array
+ */
+ public static function encodeCompactLink( $value, $compound = false ) {
+
+ // Expect to gain on larger strings and set an identifier to
+ // distinguish between compressed and non compressed
+ if ( mb_strlen( $value ) > 150 ) {
+ $value = 'c:' . gzdeflate( $value, 9 );
+ }
+
+ // https://en.wikipedia.org/wiki/Base64#URL_applications
+ // The MW parser swallows `__` and transforms it into a simple `_`
+ // hence we need to encode it once more
+ $value = rtrim( str_replace( '__', '.', strtr( base64_encode( $value ), '+/', '-_' ) ), '=' );
+
+ if ( $compound ) {
+ return [ 'cl' => $value ];
+ }
+
+ return "cl:$value";
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function decodeCompactLink( $value ) {
+
+ if ( !is_string( $value ) || mb_substr( $value, 0, 3 ) !== 'cl:' ) {
+ return $value;
+ }
+
+ $value = mb_substr( $value, 3 );
+
+ $value = base64_decode(
+ str_pad( strtr( str_replace( '.', '__', $value ), '-_', '+/' ), strlen( $value ) % 4, '=', STR_PAD_RIGHT )
+ );
+
+ // Compressed?
+ if ( mb_substr( $value, 0, 2 ) === 'c:' ) {
+ $val = @gzinflate( mb_substr( $value, 2 ) );
+
+ // Guessing that MediaWiki swallowed the last `_`
+ if ( $val === false ) {
+ $val = @gzinflate( mb_substr( $value , 2 ) . '?' );
+ }
+
+ $value = $val;
+ }
+
+ // Normalize if nceessary for those that are "encoded for use in a
+ // MediaWiki page title"
+ if ( mb_substr( $value, 0, 2 ) === 'x=' ) {
+ $value = str_replace( [ 'x=', '=-&' , '&', '%2F' ], [ '' , '=-2D&', '/', '/' ], $value );
+ }
+
+ return $value;
+ }
+
+ private function buildTarget( $query ) {
+
+ $target = $this->mTarget;
+
+ if ( count( $this->mParams ) > 0 ) {
+ if ( strpos( Site::wikiurl(), '?' ) === false ) {
+ $target = $this->mTarget . '?' . $query;
+ } else {
+ $target = $this->mTarget . '&' . $query;
+ }
+ }
+
+ return $target;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php
new file mode 100644
index 00000000..2d2c1e4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_Outputs.php
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * This class attempts to provide safe yet simple means for managing data that is relevant
+ * for the final HTML output of MediaWiki. In particular, this concerns additions to the HTML
+ * header in the form of scripts of stylesheets.
+ *
+ * The problem is that many components in SMW create hypertext that should eventually be displayed.
+ * The normal way of accessing such text are functions of the form getText() which return a
+ * (hypertext) string. Such a string, however, might refer to styles or scripts that are also
+ * needed. It is not possible to directly add those scripts to the MediaWiki output, since the form
+ * of this output depends on the context in which the function is called. Many functions might be
+ * called both during parsing and directly in special pages that do not use the usual parsing and
+ * caching mechanisms.
+ *
+ * Ideally, all functions that generate hypertext with dependencies would also include parameters to
+ * record required scripts. Since this would require major API changes, the current solution is to have
+ * a "temporal" global storage for the required items, managed in this class. It is not safe to use
+ * such a global store across hooks -- you never know what happens in between! Hence, every function
+ * that creates SMW outputs that may require head items must afterwards clear the temporal store by
+ * writing its contents to the according output.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+class SMWOutputs {
+
+ /**
+ * Protected member for temporarily storing header items.
+ * Format $id => $headItem where $id is used only to avoid duplicate
+ * items in the time before they are forwarded to the output.
+ */
+ protected static $headItems = [];
+
+ /**
+ * Protected member for temporarily storing additional Javascript
+ * snippets. Format $id => $scriptText where $id is used only to
+ * avoid duplicate scripts in the time before they are forwarded
+ * to the output.
+ */
+ protected static $scripts = [];
+
+ /**
+ * Protected member for temporarily storing resource modules.
+ *
+ * @var array
+ */
+ protected static $resourceModules = [];
+
+ /**
+ * Protected member for temporarily storing resource modules.
+ *
+ * @var array
+ */
+ protected static $resourceStyles = [];
+
+ /**
+ * Adds a resource module to the parser output.
+ *
+ * @since 1.5.3
+ *
+ * @param string $moduleName
+ */
+ public static function requireResource( $moduleName ) {
+ self::$resourceModules[$moduleName] = $moduleName;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $stylesName
+ */
+ public static function requireStyle( $stylesName ) {
+ self::$resourceStyles[$stylesName] = $stylesName;
+ }
+
+ /**
+ * Require the presence of header scripts, provided as strings with
+ * enclosing script tags. Note that the same could be achieved with
+ * requireHeadItems, but scripts use a special method "addScript" in
+ * MediaWiki OutputPage, hence we distinguish them.
+ *
+ * The id is used to avoid that the requirement for one script is
+ * recorded multiple times in SMWOutputs.
+ *
+ * @param string $id
+ * @param string $item
+ */
+ public static function requireScript( $id, $script ) {
+ self::$scripts[$id] = $script;
+ }
+
+ /**
+ * Adds head items that are not Resource Loader modules. Should only
+ * be used for custom head items such as RSS fedd links.
+ *
+ * The id is used to avoid that the requirement for one script is
+ * recorded multiple times in SMWOutputs.
+ *
+ * Support for calling this with the old constants SMW_HEADER_STYLE
+ * and SMW_HEADER_TOOLTIP will vanish in SMW 1.7 at the latest.
+ *
+ * @param mixed $id
+ * @param string $item
+ */
+ public static function requireHeadItem( $id, $item = '' ) {
+ if ( is_numeric( $id ) ) {
+ switch ( $id ) {
+ case SMW_HEADER_TOOLTIP:
+ self::requireResource( 'ext.smw.tooltips' );
+ break;
+ case SMW_HEADER_STYLE:
+ self::requireStyle( 'ext.smw.style' );
+ break;
+ }
+ } else {
+ self::$headItems[$id] = $item;
+ }
+ }
+
+ /**
+ * This function takes output requirements as can be found in a given ParserOutput
+ * object and puts them back in to the internal temporal requirement list from
+ * which they can be committed to some other output. It is needed when code that
+ * would normally call SMWOutputs::requireHeadItem() has need to use a full
+ * independent parser call (Parser::parse()) that produces its own parseroutput.
+ * If omitted, all output items potentially committed to this parseroutput during
+ * parsing will not be passed on to higher levels.
+ *
+ * Note that this is not required if the $parseroutput is further processed by
+ * MediaWiki, but there are cases where the output is discarded and only its text
+ * is used.
+ *
+ * @param ParserOutput $parserOutput
+ */
+ static public function requireFromParserOutput( ParserOutput $parserOutput ) {
+ // Note: we do not attempt to recover which head items where scripts here.
+
+ $parserOutputHeadItems = $parserOutput->getHeadItems();
+
+ self::$headItems = array_merge( (array)self::$headItems, $parserOutputHeadItems );
+
+ /// TODO Is the following needed?
+ if ( isset( $parserOutput->mModules ) ) {
+ foreach ( $parserOutput->mModules as $module ) {
+ self::$resourceModules[$module] = $module;
+ }
+ }
+ }
+
+ /**
+ * Actually commit the collected requirements to a given parser that is about to parse
+ * what will later be the HTML output. This makes sure that HTML output based on the
+ * parser results contains all required output items.
+ *
+ * If the parser creates output for a normal wiki page, then the committed items will
+ * also become part of the page cache so that they will correctly be added to all page
+ * outputs built from this cache later on.
+ *
+ * @param Parser $parser
+ */
+ static public function commitToParser( Parser $parser ) {
+ /// TODO find out and document when this b/c code can go away
+ if ( method_exists( $parser, 'getOutput' ) ) {
+ $po = $parser->getOutput();
+ } else {
+ $po = $parser->mOutput;
+ }
+
+ if ( isset( $po ) ) {
+ self::commitToParserOutput( $po );
+ }
+ }
+
+ /**
+ * Similar to SMWOutputs::commitToParser() but acting on a ParserOutput object.
+ *
+ * @param ParserOutput $parserOutput
+ */
+ static public function commitToParserOutput( ParserOutput $parserOutput ) {
+
+ foreach ( self::$scripts as $key => $script ) {
+ $parserOutput->addHeadItem( $script . "\n", $key );
+ }
+
+ foreach ( self::$headItems as $key => $item ) {
+ $parserOutput->addHeadItem( "\t\t" . $item . "\n", $key );
+ }
+
+ $parserOutput->addModuleStyles( array_values( self::$resourceStyles ) );
+ $parserOutput->addModules( array_values( self::$resourceModules ) );
+
+ self::$resourceStyles = [];
+ self::$resourceModules = [];
+ self::$headItems = [];
+ }
+
+ /**
+ * Actually commit the collected requirements to a given OutputPage object that
+ * will later generate the HTML output. This makes sure that HTML output contains
+ * all required output items. Note that there is no parser caching at this level of
+ * processing. In particular, data should not be committed to $wgOut in methods
+ * that run during page parsing, since these would not run next time when the page
+ * is produced from parser cache.
+ *
+ * @param OutputPage $output
+ */
+ static public function commitToOutputPage( OutputPage $output ) {
+ foreach ( self::$scripts as $script ) {
+ $output->addScript( $script );
+ }
+ foreach ( self::$headItems as $key => $item ) {
+ $output->addHeadItem( $key, "\t\t" . $item . "\n" );
+ }
+
+ $output->addModuleStyles( array_values( self::$resourceStyles ) );
+ $output->addModules( array_values( self::$resourceModules ) );
+
+ self::$resourceStyles = [];
+ self::$resourceModules = [];
+ self::$headItems = [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php
new file mode 100644
index 00000000..9e40d1fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageLister.php
@@ -0,0 +1,355 @@
+<?php
+
+use SMW\Query\PrintRequest;
+
+/**
+ * Helper class to generate HTML lists of wiki pages, with support for paged
+ * navigation using the from/until and limit settings as in MediaWiki's
+ * CategoryPage.
+ *
+ * The class attempts to allow as much code as possible to be shared among
+ * different places where similar lists are used.
+ *
+ * Some code adapted from CategoryPage.php
+ *
+ * @ingroup SMW
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SMWPageLister {
+
+ protected $mDiWikiPages;
+ protected $mDiProperty;
+ protected $mLimit;
+ protected $mFrom;
+ protected $mUntil;
+
+ /**
+ * Constructor
+ *
+ * @param $diWikiPages array of SMWDIWikiPage
+ * @param $diProperty mixed SMWDIProperty that the wikipages are values of, or null
+ * @param $limit integer maximal amount of items to display
+ * @param $from string if the results were selected starting from this string
+ * @param $until string if the results were selected reaching until this string
+ */
+ public function __construct( $diWikiPages, $diProperty, $limit, $from = '', $until = '' ) {
+ $this->mDiWikiPages = $diWikiPages;
+ $this->mDiProperty = $diProperty;
+ $this->mLimit = $limit;
+ $this->mFrom = $from;
+ $this->mUntil = $until;
+ }
+
+ /**
+ * Generates the prev/next link part to the HTML code of the top and
+ * bottom section of the page. Whether and how these links appear
+ * depends on specified boundaries, limit, and results. The title is
+ * required to create a link to the right page. The query array gives
+ * optional further parameters to append to all navigation links.
+ *
+ * @param $title Title
+ * @param $query array that associates parameter names to parameter values
+ * @return string
+ */
+ public function getNavigationLinks( Title $title, $query = [] ) {
+ global $wgLang;
+
+ $limitText = $wgLang->formatNum( $this->mLimit );
+
+ $resultCount = count( $this->mDiWikiPages );
+ $beyondLimit = ( $resultCount > $this->mLimit );
+
+ if ( !is_null( $this->mUntil ) && $this->mUntil !== '' ) {
+ if ( $beyondLimit ) {
+ $first = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $this->mDiWikiPages[1] );
+ } else {
+ $first = '';
+ }
+
+ $last = $this->mUntil;
+ } elseif ( $beyondLimit || ( !is_null( $this->mFrom ) && $this->mFrom !== '' ) ) {
+ $first = $this->mFrom;
+
+ if ( $beyondLimit ) {
+ $last = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $this->mDiWikiPages[$resultCount - 1] );
+ } else {
+ $last = '';
+ }
+ } else {
+ return '';
+ }
+
+ $prevLink = wfMessage( 'prevn', $limitText )->escaped();
+ if ( $first !== '' ) {
+ $prevLink = $this->makeSelfLink( $title, $prevLink, $query + [ 'until' => $first ] );
+ }
+
+ $nextLink = wfMessage( 'nextn', $limitText )->escaped();
+ if ( $last !== '' ) {
+ $nextLink = $this->makeSelfLink( $title, $nextLink, $query + [ 'from' => $last ] );
+ }
+
+ return "($prevLink) ($nextLink)";
+ }
+
+ /**
+ * Format an HTML link with the given text and parameters.
+ *
+ * @return string
+ */
+ protected function makeSelfLink( Title $title, $linkText, array $parameters ) {
+ return smwfGetLinker()->link( $title, $linkText, [], $parameters );
+ }
+
+ /**
+ * Make SMWRequestOptions suitable for obtaining a list of results for
+ * the given limit, and from or until string. One more result than the
+ * limit will be created, and the results may have to be reversed in
+ * order if ascending is set to false in the resulting object.
+ *
+ * @param $limit integer
+ * @param $from string can be empty if no from condition is desired
+ * @param $until string can be empty if no until condition is desired
+ * @return SMWRequestOptions
+ */
+ public static function getRequestOptions( $limit, $from, $until ) {
+ $options = new SMWRequestOptions();
+ $options->limit = $limit + 1;
+ $options->sort = true;
+
+ if ( $from !== '' ) {
+ $options->boundary = $from;
+ $options->ascending = true;
+ $options->include_boundary = true;
+ } elseif ( $until !== '' ) {
+ $options->boundary = $until;
+ $options->ascending = false;
+ $options->include_boundary = false;
+ }
+
+ return $options;
+ }
+
+ /**
+ * Make SMWQuery suitable for obtaining a list of results based on the
+ * given description, limit, and from or until string. One more result
+ * than the limit will be created, and the results may have to be
+ * reversed in order if $until is nonempty.
+ *
+ * @param $description SMWDescription main query description
+ * @param $limit integer
+ * @param $from string can be empty if no from condition is desired
+ * @param $until string can be empty if no until condition is desired
+ * @return SMWQuery
+ */
+ public static function getQuery( SMWDescription $description, $limit, $from, $until ) {
+ if ( $from !== '' ) {
+ $diWikiPage = new SMWDIWikiPage( $from, NS_MAIN, '' ); // make a dummy wiki page as boundary
+ $fromDescription = new SMWValueDescription( $diWikiPage, null, SMW_CMP_GEQ );
+ $queryDescription = new SMWConjunction( [ $description, $fromDescription ] );
+ $order = 'ASC';
+ } elseif ( $until !== '' ) {
+ $diWikiPage = new SMWDIWikiPage( $until, NS_MAIN, '' ); // make a dummy wiki page as boundary
+ $untilDescription = new SMWValueDescription( $diWikiPage, null, SMW_CMP_LESS ); // do not include boundary in this case
+ $queryDescription = new SMWConjunction( [ $description, $untilDescription ] );
+ $order = 'DESC';
+ } else {
+ $queryDescription = $description;
+ $order = 'ASC';
+ }
+
+ $queryDescription->addPrintRequest( new PrintRequest( PrintRequest::PRINT_THIS, '' ) );
+
+ $query = new SMWQuery( $queryDescription );
+ $query->sortkeys[''] = $order;
+ $query->setLimit( $limit + 1 );
+
+ return $query;
+ }
+
+ /**
+ * Format a list of data items chunked by letter, either as a
+ * bullet list or a columnar format, depending on the length.
+ *
+ * @param $cutoff integer, use columns for more results than that
+ * @return string
+ */
+ public function formatList( $cutoff = 6 ) {
+ $end = count( $this->mDiWikiPages );
+ $start = 0;
+ if ( $end > $this->mLimit ) {
+ if ( $this->mFrom !== '' ) {
+ $end -= 1;
+ } else {
+ $start += 1;
+ }
+ }
+
+ if ( count ( $this->mDiWikiPages ) > $cutoff ) {
+ return self::getColumnList( $start, $end, $this->mDiWikiPages, $this->mDiProperty );
+ } elseif ( count( $this->mDiWikiPages ) > 0 ) {
+ return self::getShortList( $start, $end, $this->mDiWikiPages, $this->mDiProperty );
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Format a list of SMWDIWikiPage objects chunked by letter in a three-column
+ * list, ordered vertically.
+ *
+ * @param $start integer
+ * @param $end integer
+ * @param $diWikiPages array of SMWDIWikiPage
+ * @param $diProperty SMWDIProperty that the wikipages are values of, or null
+ *
+ * @return string
+ */
+ public static function getColumnList( $start, $end, $diWikiPages, $diProperty, $moreCallback = null ) {
+ global $wgContLang;
+
+ if ( $diWikiPages instanceof \Iterator ) {
+ $diWikiPages = iterator_to_array( $diWikiPages );
+ }
+
+ // Divide list into three equal chunks.
+ $chunk = (int) ( ( $end - $start + 1 ) / 3 );
+
+ // Get and display header.
+ $r = '<table width="100%"><tr valign="top">';
+
+ $prevStartChar = 'none';
+
+ // Loop through the chunks.
+ for ( $startChunk = $start, $endChunk = $chunk, $chunkIndex = 0;
+ $chunkIndex < 3;
+ ++$chunkIndex, $startChunk = $endChunk, $endChunk += $chunk + 1 ) {
+ $r .= "<td width='33%'>\n";
+ $atColumnTop = true;
+
+ // output all diWikiPages
+ for ( $index = $startChunk; $index < $endChunk && $index < $end; ++$index ) {
+
+ if ( !isset( $diWikiPages[$index] ) ) {
+ continue;
+ }
+
+ $dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$index], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $dataValue->getWikiValue() );
+
+ // check for change of starting letter or beginning of chunk
+ $sortkey = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $diWikiPages[$index] );
+ $startChar = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
+
+ if ( ( $index == $startChunk ) ||
+ ( $startChar != $prevStartChar ) ) {
+ if ( $atColumnTop ) {
+ $atColumnTop = false;
+ } else {
+ $r .= "</ul>\n";
+ }
+
+ if ( $startChar == $prevStartChar ) {
+ $cont_msg = ' ' . wfMessage( 'listingcontinuesabbrev' )->escaped();
+ } else {
+ $cont_msg = '';
+ }
+
+ $r .= "<h3>" . htmlspecialchars( $startChar ) . $cont_msg . "</h3>\n<ul>";
+
+ $prevStartChar = $startChar;
+ }
+
+ $r .= "<li>" . $dataValue->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . "</li>\n";
+ }
+
+ if ( $index == $end && $moreCallback !== null ) {
+ $r .= "<li>" . call_user_func( $moreCallback ) . "</li>\n";
+ }
+
+ if ( !$atColumnTop ) {
+ $r .= "</ul>\n";
+ }
+
+ $r .= "</td>\n";
+ }
+
+ $r .= '</tr></table>';
+
+ return $r;
+ }
+
+ /**
+ * Format a list of diWikiPages chunked by letter in a bullet list.
+ *
+ * @param $start integer
+ * @param $end integer
+ * @param $diWikiPages array of SMWDataItem
+ * @param $diProperty SMWDIProperty that the wikipages are values of, or null
+ *
+ * @return string
+ */
+ public static function getShortList( $start, $end, $diWikiPages, $diProperty, $moreCallback = null ) {
+
+ if ( $diWikiPages instanceof \Iterator ) {
+ $diWikiPages = iterator_to_array( $diWikiPages );
+ }
+
+ $startDv = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$start], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $startDv->getWikiValue() );
+
+ // For a redirect, disable the DisplayTitle to show the original (aka source) page
+ if ( $diProperty !== null && $diProperty->getKey() == '_REDI' ) {
+ $startDv->setOption( 'smwgDVFeatures', ( $startDv->getOption( 'smwgDVFeatures' ) & ~SMW_DV_WPV_DTITLE ) );
+ }
+
+ $startChar = self::getFirstChar( $diWikiPages[$start] );
+
+ $r = '<h3>' . htmlspecialchars( $startChar ) . "</h3>\n" .
+ '<ul><li>' . $startDv->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</li>';
+
+ $prevStartChar = $startChar;
+ for ( $index = $start + 1; $index < $end; $index++ ) {
+ $dataValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diWikiPages[$index], $diProperty );
+ $searchlink = \SMWInfolink::newBrowsingLink( '+', $dataValue->getWikiValue() );
+
+ // For a redirect, disable the DisplayTitle to show the original (aka source) page
+ if ( $diProperty !== null && $diProperty->getKey() == '_REDI' ) {
+ $dataValue->setOption( 'smwgDVFeatures', ( $dataValue->getOption( 'smwgDVFeatures' ) & ~SMW_DV_WPV_DTITLE ) );
+ }
+
+ $startChar = self::getFirstChar( $diWikiPages[$index] );
+
+ if ( $startChar != $prevStartChar ) {
+ $r .= "</ul><h3>" . htmlspecialchars( $startChar ) . "</h3>\n<ul>";
+ $prevStartChar = $startChar;
+ }
+
+ $r .= '<li>' . $dataValue->getLongHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</li>';
+ }
+
+ if ( $moreCallback !== null ) {
+ $r .= '<li>' . call_user_func( $moreCallback ) . '</li>';
+ }
+
+ $r .= '</ul>';
+
+ return $r;
+ }
+
+ private static function getFirstChar( $dataItem ) {
+ global $wgContLang;
+
+ $sortkey = \SMW\StoreFactory::getStore()->getWikiPageSortKey( $dataItem );
+
+ if ( $sortkey === '' ) {
+ $sortkey = $dataItem->getDBKey();
+ }
+
+ return $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php
new file mode 100644
index 00000000..8143ace2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SMW_PageSchemas.php
@@ -0,0 +1,379 @@
+<?php
+
+/**
+ * Functions for handling Semantic MediaWiki data within the Page Schemas
+ * extension.
+ *
+ * @author Ankit Garg
+ * @author Yaron Koren
+ * @ingroup SMW
+ */
+
+class SMWPageSchemas extends PSExtensionHandler {
+
+ public static function getDisplayColor() {
+ return '#DEF';
+ }
+
+ public static function getTemplateDisplayString() {
+ return 'Connecting property';
+ }
+
+ public static function getFieldDisplayString() {
+ return 'Semantic property';
+ }
+
+ /**
+ * Returns the display info for the "connecting property" (if any)
+ * of the #subobject call (if any) in this template.
+ */
+ public static function getTemplateDisplayValues( $templateXML ) {
+ foreach ( $templateXML->children() as $tag => $child ) {
+ if ( $tag == "semanticmediawiki_ConnectingProperty" ) {
+ $propName = $child->attributes()->name;
+ $values = [];
+ return [ $propName, $values ];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the display info for the property (if any is defined)
+ * for a single field in the Page Schemas XML.
+ */
+ public static function getFieldDisplayValues( $fieldXML ) {
+ foreach ( $fieldXML->children() as $tag => $child ) {
+ if ( $tag == "semanticmediawiki_Property" ) {
+ $propName = $child->attributes()->name;
+ $values = [];
+ foreach ( $child->children() as $prop => $value ) {
+ $values[$prop] = (string)$value;
+ }
+ return [ $propName, $values ];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the set of SMW property data from the entire page schema.
+ */
+ static function getAllPropertyData( $pageSchemaObj ) {
+ $propertyDataArray = [];
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $psTemplateFields = $psTemplate->getFields();
+ foreach ( $psTemplateFields as $psTemplateField ) {
+ $prop_array = $psTemplateField->getObject('semanticmediawiki_Property');
+ if ( empty( $prop_array ) ) {
+ continue;
+ }
+ // If property name is blank, set it to the
+ // field name.
+ if ( !array_key_exists( 'name', $prop_array ) || empty( $prop_array['name'] ) ) {
+ $prop_array['name'] = $psTemplateField->getName();
+ }
+ $propertyDataArray[] = $prop_array;
+ }
+ }
+ return $propertyDataArray;
+ }
+
+ /**
+ * Constructs XML for the "connecting property", based on what was
+ * submitted in the 'edit schema' form.
+ */
+ public static function createTemplateXMLFromForm() {
+ global $wgRequest;
+
+ $xmlPerTemplate = [];
+ foreach ( $wgRequest->getValues() as $var => $val ) {
+ if ( substr( $var, 0, 24 ) == 'smw_connecting_property_' ) {
+ $templateNum = substr( $var, 24 );
+ $xml = '<semanticmediawiki_ConnectingProperty name="' . $val . '" />';
+ $xmlPerTemplate[$templateNum] = $xml;
+ }
+ }
+ return $xmlPerTemplate;
+ }
+
+ static function getConnectingPropertyName( $psTemplate ) {
+ // TODO - there should be a more direct way to get
+ // this data.
+ $smwConnectingPropertyArray = $psTemplate->getObject( 'semanticmediawiki_ConnectingProperty' );
+ return PageSchemas::getValueFromObject( $smwConnectingPropertyArray, 'name' );
+ }
+
+ /**
+ * Sets the list of property pages defined by the passed-in
+ * Page Schemas object.
+ */
+ public static function getPagesToGenerate( $pageSchemaObj ) {
+ $pagesToGenerate = [];
+
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $smwConnectingPropertyName = self::getConnectingPropertyName( $psTemplate );
+ if ( is_null( $smwConnectingPropertyName ) ) {
+ continue;
+ }
+ $pagesToGenerate[] = Title::makeTitleSafe( SMW_NS_PROPERTY, $smwConnectingPropertyName );
+ }
+
+ $propertyDataArray = self::getAllPropertyData( $pageSchemaObj );
+ foreach ( $propertyDataArray as $propertyData ) {
+ $title = Title::makeTitleSafe( SMW_NS_PROPERTY, $propertyData['name'] );
+ $pagesToGenerate[] = $title;
+ }
+ return $pagesToGenerate;
+ }
+
+ /**
+ * Constructs XML for the SMW property, based on what was submitted
+ * in the 'edit schema' form.
+ */
+ public static function createFieldXMLFromForm() {
+ global $wgRequest;
+
+ $fieldNum = -1;
+ $xmlPerField = [];
+ foreach ( $wgRequest->getValues() as $var => $val ) {
+ if ( substr( $var, 0, 18 ) == 'smw_property_name_' ) {
+ $fieldNum = substr( $var, 18 );
+ $xml = '<semanticmediawiki_Property name="' . $val . '" >';
+ } elseif ( substr( $var, 0, 18 ) == 'smw_property_type_'){
+ $xml .= '<Type>' . $val . '</Type>';
+ } elseif ( substr( $var, 0, 16 ) == 'smw_linked_form_') {
+ if ( $val !== '' ) {
+ $xml .= '<LinkedForm>' . $val . '</LinkedForm>';
+ }
+ } elseif ( substr( $var, 0, 11 ) == 'smw_values_') {
+ if ( $val !== '' ) {
+ // replace the comma substitution character that has no chance of
+ // being included in the values list - namely, the ASCII beep
+ $listSeparator = ',';
+ $allowed_values_str = str_replace( "\\$listSeparator", "\a", $val );
+ $allowed_values_array = explode( $listSeparator, $allowed_values_str );
+ foreach ( $allowed_values_array as $value ) {
+ // replace beep back with comma, trim
+ $value = str_replace( "\a", $listSeparator, trim( $value ) );
+ $xml .= '<AllowedValue>' . $value . '</AllowedValue>';
+ }
+ }
+ $xml .= '</semanticmediawiki_Property>';
+ $xmlPerField[$fieldNum] = $xml;
+ }
+ }
+ return $xmlPerField;
+ }
+
+ /**
+ * Returns the HTML necessary for getting information about the
+ * "connecting property" within the Page Schemas 'editschema' page.
+ */
+ public static function getTemplateEditingHTML( $psTemplate) {
+ // Only display this if the Semantic Internal Objects extension
+ // isn't displaying something similar.
+ if ( class_exists( 'SIOPageSchemas' ) ) {
+ return null;
+ }
+
+ $prop_array = [];
+ $hasExistingValues = false;
+ if ( !is_null( $psTemplate ) ) {
+ $prop_array = $psTemplate->getObject( 'semanticmediawiki_ConnectingProperty' );
+ if ( !is_null( $prop_array ) ) {
+ $hasExistingValues = true;
+ }
+ }
+ $text = '<p>' . 'Name of property to connect this template\'s fields to the rest of the page:' . ' ' . '(should only be used if this template can have multiple instances)' . ' ';
+ $propName = PageSchemas::getValueFromObject( $prop_array, 'name' );
+ $text .= Html::input( 'smw_connecting_property_num', $propName, [ 'size' => 15 ] ) . "\n";
+
+ return [ $text, $hasExistingValues ];
+ }
+
+ /**
+ * Returns the HTML necessary for getting information about a regular
+ * semantic property within the Page Schemas 'editschema' page.
+ */
+ public static function getFieldEditingHTML( $psTemplateField ) {
+ global $smwgContLang;
+
+ $prop_array = [];
+ $hasExistingValues = false;
+ if ( !is_null( $psTemplateField ) ) {
+ $prop_array = $psTemplateField->getObject('semanticmediawiki_Property');
+ if ( !is_null( $prop_array ) ) {
+ $hasExistingValues = true;
+ }
+ }
+ $html_text = '<p>' . wfMessage( 'ps-optional-name' )->text() . ' ';
+ $propName = PageSchemas::getValueFromObject( $prop_array, 'name' );
+ $html_text .= Html::input( 'smw_property_name_num', $propName, [ 'size' => 15 ] ) . "\n";
+ $propType = PageSchemas::getValueFromObject( $prop_array, 'Type' );
+ $select_body = "";
+ $datatype_labels = $smwgContLang->getDatatypeLabels();
+ foreach ( $datatype_labels as $label ) {
+ $optionAttrs = [];
+ if ( $label == $propType) {
+ $optionAttrs['selected'] = 'selected';
+ }
+ $select_body .= "\t" . Xml::element( 'option', $optionAttrs, $label ) . "\n";
+ }
+ $propertyDropdownAttrs = [
+ 'id' => 'property_dropdown',
+ 'name' => 'smw_property_type_num',
+ 'value' => $propType
+ ];
+ $html_text .= "Type: " . Xml::tags( 'select', $propertyDropdownAttrs, $select_body ) . "</p>\n";
+
+ // This can't be last, because of the hacky way the XML is
+ // ocnstructed from this form's output.
+ if ( defined( 'SF_VERSION' ) ) {
+ $html_text .= '<p>' . wfMessage( 'sf_createproperty_linktoform' )->text() . ' ';
+ $linkedForm = PageSchemas::getValueFromObject( $prop_array, 'LinkedForm' );
+ $html_text .= Html::input( 'smw_linked_form_num', $linkedForm, [ 'size' => 15 ] ) . "\n";
+ $html_text .= "(for Page properties only)</p>\n";
+ }
+
+ $html_text .= '<p>If you want this property to only be allowed to have certain values, enter the list of allowed values, separated by commas (if a value contains a comma, replace it with "\,"):</p>';
+ $allowedValsInputAttrs = [
+ 'size' => 80
+ ];
+ $allowedValues = PageSchemas::getValueFromObject( $prop_array, 'allowed_values' );
+ if ( is_null( $allowedValues ) ) {
+ $allowed_val_string = '';
+ } else {
+ $allowed_val_string = implode( ', ', $allowedValues );
+ }
+ $html_text .= '<p>' . Html::input( 'smw_values_num', $allowed_val_string, 'text', $allowedValsInputAttrs ) . "</p>\n";
+
+ return [ $html_text, $hasExistingValues ];
+ }
+
+ /**
+ * Creates the property page for each property specified in the
+ * passed-in Page Schemas XML object.
+ */
+ public static function generatePages( $pageSchemaObj, $selectedPages ) {
+ global $smwgContLang, $wgUser;
+
+ $datatypeLabels = $smwgContLang->getDatatypeLabels();
+ $pageTypeLabel = $datatypeLabels['_wpg'];
+
+ $jobs = [];
+ $jobParams = [];
+ $jobParams['user_id'] = $wgUser->getId();
+
+ // First, create jobs for all "connecting properties".
+ $psTemplates = $pageSchemaObj->getTemplates();
+ foreach ( $psTemplates as $psTemplate ) {
+ $smwConnectingPropertyName = self::getConnectingPropertyName( $psTemplate );
+ if ( is_null( $smwConnectingPropertyName ) ) {
+ continue;
+ }
+ $propTitle = Title::makeTitleSafe( SMW_NS_PROPERTY, $smwConnectingPropertyName );
+ if ( !in_array( $propTitle, $selectedPages ) ) {
+ continue;
+ }
+
+ $jobParams['page_text'] = self::createPropertyText( $pageTypeLabel, null, null );
+ $jobs[] = new PSCreatePageJob( $propTitle, $jobParams );
+ }
+
+ // Second, create jobs for all regular properties.
+ $propertyDataArray = self::getAllPropertyData( $pageSchemaObj );
+ foreach ( $propertyDataArray as $propertyData ) {
+ $propTitle = Title::makeTitleSafe( SMW_NS_PROPERTY, $propertyData['name'] );
+ if ( !in_array( $propTitle, $selectedPages ) ) {
+ continue;
+ }
+ $propertyType = array_key_exists( 'Type', $propertyData ) ? $propertyData['Type'] : null;
+ $propertyAllowedValues = array_key_exists( 'allowed_values', $propertyData ) ? $propertyData['allowed_values'] : null;
+ $propertyLinkedForm = array_key_exists( 'LinkedForm', $propertyData ) ? $propertyData['LinkedForm'] : null;
+ $jobParams['page_text'] = self::createPropertyText( $propertyType, $propertyAllowedValues, $propertyLinkedForm );
+ $jobs[] = new PSCreatePageJob( $propTitle, $jobParams );
+ }
+ if ( class_exists( 'JobQueueGroup' ) ) {
+ JobQueueGroup::singleton()->push( $jobs );
+ } else {
+ // MW <= 1.20
+ Job::batchInsert( $jobs );
+ }
+ }
+
+ /**
+ * Creates the text for a property page.
+ */
+ static public function createPropertyText( $propertyType, $allowedValues, $linkedForm = null ) {
+ /**
+ * @var SMWLanguage $smwgContLang
+ */
+ global $smwgContLang, $wgContLang;
+
+ $propLabels = $smwgContLang->getPropertyLabels();
+ $hasTypeLabel = $propLabels['_TYPE'];
+ $typeTag = "[[$hasTypeLabel::$propertyType]]";
+ $text = wfMessage( 'smw-createproperty-isproperty', $typeTag )->inContentLanguage()->text();
+
+ if ( $linkedForm !== '' && defined( 'SF_VERSION' ) ) {
+ global $sfgContLang;
+ $sfPropLabels = $sfgContLang->getPropertyLabels();
+ $defaultFormTag = "[[{$sfPropLabels[SF_SP_HAS_DEFAULT_FORM]}::$linkedForm]]";
+ $text .= ' ' . wfMessage( 'sf_property_linkstoform', $defaultFormTag )->inContentLanguage()->text();
+ }
+
+ if ( $allowedValues != null) {
+ $text .= "\n\n" . wfMessage( 'smw-createproperty-allowedvals', $wgContLang->formatNum( count( $allowedValues ) ) )->inContentLanguage()->text();
+
+ foreach ( $allowedValues as $value ) {
+ $prop_labels = $smwgContLang->getPropertyLabels();
+ $text .= "\n* [[" . $prop_labels['_PVAL'] . "::$value]]";
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Returns either the "connecting property", or a field property, based
+ * on the XML passed from the Page Schemas extension.
+ */
+ public static function createPageSchemasObject( $tagName, $xml ) {
+ if ( $tagName == "semanticmediawiki_ConnectingProperty" ) {
+ foreach ( $xml->children() as $tag => $child ) {
+ if ( $tag == $tagName ) {
+ $smw_array = [];
+ $propName = $child->attributes()->name;
+ $smw_array['name'] = (string)$propName;
+ foreach ( $child->children() as $prop => $value ) {
+ $smw_array[$prop] = (string)$value;
+ }
+ return $smw_array;
+ }
+ }
+ } elseif ( $tagName == "semanticmediawiki_Property" ) {
+ foreach ( $xml->children() as $tag => $child ) {
+ if ( $tag == $tagName ) {
+ $smw_array = [];
+ $propName = $child->attributes()->name;
+ $smw_array['name'] = (string)$propName;
+ $allowed_values = [];
+ $count = 0;
+ foreach ( $child->children() as $prop => $value ) {
+ if ( $prop == "AllowedValue" ) {
+ $allowed_values[$count++] = $value;
+ } else {
+ $smw_array[$prop] = (string)$value;
+ }
+ }
+ $smw_array['allowed_values'] = $allowed_values;
+ return $smw_array;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php b/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php
new file mode 100644
index 00000000..e54ec20e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/SemanticData.php
@@ -0,0 +1,782 @@
+<?php
+
+namespace SMW;
+
+use MWException;
+use SMW\DataModel\SubSemanticData;
+use SMW\Exception\SemanticDataImportException;
+use SMWContainerSemanticData;
+use SMWDataItem;
+use SMWDataValue;
+use SMWDIContainer;
+
+/**
+ * Class for representing chunks of semantic data for one given
+ * subject. This consists of property-value pairs, grouped by property,
+ * and possibly by SMWSemanticData objects about subobjects.
+ *
+ * Data about subobjects can be added in two ways: by directly adding it
+ * using addSubSemanticData() or by adding a property value of type
+ * SMWDIContainer.
+ *
+ * By its very design, the container is unable to hold inverse properties.
+ * For one thing, it would not be possible to identify them with mere keys.
+ * Since SMW cannot annotate pages with inverses, this is not a limitation.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SemanticData {
+
+ /**
+ * Returns the last modified timestamp the data were stored to the Store or
+ * have been fetched from cache.
+ */
+ const OPT_LAST_MODIFIED = 'opt.last.modified';
+
+ /**
+ * Identifies that a data block was created by a user.
+ */
+ const PROC_USER = 'proc.user';
+
+ /**
+ * Identifies that a data block was initiated by a delete request.
+ */
+ const PROC_DELETE = 'proc.delete';
+
+ /**
+ * Cache for the localized version of the namespace prefix "Property:".
+ *
+ * @var string
+ */
+ static protected $mPropertyPrefix = '';
+
+ /**
+ * States whether this is a stub object. Stubbing might happen on
+ * serialisation to save DB space.
+ *
+ * @todo Check why this is public and document this here. Or fix it.
+ *
+ * @var boolean
+ */
+ public $stubObject;
+
+ /**
+ * Array mapping property keys (string) to arrays of SMWDataItem
+ * objects.
+ *
+ * @var SMWDataItem[]
+ */
+ protected $mPropVals = [];
+
+ /**
+ * Array mapping property keys (string) to DIProperty objects.
+ *
+ * @var DIProperty[]
+ */
+ protected $mProperties = [];
+
+ /**
+ * States whether the container holds any normal properties.
+ *
+ * @var boolean
+ */
+ protected $mHasVisibleProps = false;
+
+ /**
+ * States whether the container holds any displayable predefined
+ * $mProperties (as opposed to predefined properties without a display
+ * label). For some settings we need this to decide if a Factbox is
+ * displayed.
+ *
+ * @var boolean
+ */
+ protected $mHasVisibleSpecs = false;
+
+ /**
+ * States whether repeated values should be avoided. Not needing
+ * duplicate elimination (e.g. when loading from store) can save some
+ * time, especially in subclasses like SMWSqlStubSemanticData, where
+ * the first access to a data item is more costy.
+ *
+ * @note This setting is merely for optimization. The SMW data model
+ * never cares about the multiplicity of identical data assignments.
+ *
+ * @var boolean
+ */
+ protected $mNoDuplicates;
+
+ /**
+ * DIWikiPage object that is the subject of this container.
+ * Subjects can never be null (and this is ensured in all methods setting
+ * them in this class).
+ *
+ * @var DIWikiPage
+ */
+ protected $mSubject;
+
+ /**
+ * Semantic data associated to subobjects of the subject of this
+ * SMWSemanticData.
+ * These key-value pairs of subObjectName (string) =>SMWSemanticData.
+ *
+ * @since 1.8
+ * @var SubSemanticData
+ */
+ protected $subSemanticData;
+
+ /**
+ * Internal flag that indicates if this semantic data will accept
+ * subdata. Semantic data objects that are subdata already do not allow
+ * (second level) subdata to be added. This ensures that all data is
+ * collected on the top level, and in particular that there is only one
+ * way to represent the same data with subdata. This is also useful for
+ * diff computation.
+ */
+ protected $subDataAllowed = true;
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * Cache the hash to ensure a minimal impact in case of repeated usage. Any
+ * removal or insert action will reset the hash to null to ensure it is
+ * recreated in corresponds to changed nature of the data.
+ *
+ * @var string|null
+ */
+ private $hash = null;
+
+ /**
+ * @var Options
+ */
+ protected $options;
+
+ /**
+ * @var array
+ */
+ protected $extensionData = [];
+
+ /**
+ * This is kept public to keep track of the depth during a recursive processing
+ * when accessed through the SubSemanticData instance.
+ *
+ * @var integer
+ */
+ public $subContainerDepthCounter = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param DIWikiPage $subject to which this data refers
+ * @param boolean $noDuplicates stating if duplicate data should be avoided
+ */
+ public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
+ $this->clear();
+ $this->mSubject = $subject;
+ $this->mNoDuplicates = $noDuplicates;
+ $this->subSemanticData = new SubSemanticData( $subject, $noDuplicates );
+ }
+
+ /**
+ * This object is added to the parser output of MediaWiki, but it is
+ * not useful to have all its data as part of the parser cache since
+ * the data is already stored in more accessible format in SMW. Hence
+ * this implementation of __sleep() makes sure only the subject is
+ * serialised, yielding a minimal stub data container after
+ * unserialisation. This is a little safer than serialising nothing:
+ * if, for any reason, SMW should ever access an unserialised parser
+ * output, then the Semdata container will at least look as if properly
+ * initialised (though empty).
+ *
+ * @return array
+ */
+ public function __sleep() {
+ return [ 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mHasVisibleProps', 'mHasVisibleSpecs', 'options', 'extensionData' ];
+ }
+
+ /**
+ * Return subject to which the stored semantic annotations refer to.
+ *
+ * @return DIWikiPage subject
+ */
+ public function getSubject() {
+ return $this->mSubject;
+ }
+
+ /**
+ * Get the array of all properties that have stored values.
+ *
+ * @return array of DIProperty objects
+ */
+ public function getProperties() {
+ ksort( $this->mProperties, SORT_STRING );
+ return $this->mProperties;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasProperty( DIProperty $property ) {
+ return isset( $this->mProperties[$property->getKey()] ) || array_key_exists( $property->getKey(), $this->mProperties );
+ }
+
+ /**
+ * Get the array of all stored values for some property.
+ *
+ * @param DIProperty $property
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( DIProperty $property ) {
+ if ( $property->isInverse() ) { // we never have any data for inverses
+ return [];
+ }
+
+ if ( array_key_exists( $property->getKey(), $this->mPropVals ) ) {
+ return array_values( $this->mPropVals[$property->getKey()] );
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setExtensionData( $key, $value ) {
+ $this->extensionData[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed|null
+ */
+ public function getExtensionData( $key ) {
+
+ if ( !isset( $this->extensionData[$key] ) ) {
+ return null;
+ }
+
+ return $this->extensionData[$key];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ if ( $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * Returns collected errors occurred during processing
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Adds an error array
+ *
+ * @since 1.9
+ *
+ * @return array|string
+ */
+ public function addError( $error ) {
+ $this->errors = array_merge( $this->errors, (array)$error );
+ }
+
+ /**
+ * Generate a hash value to simplify the comparison of this data
+ * container with other containers. Subdata is taken into account.
+ *
+ * The hash uses PHP's md5 implementation, which is among the fastest
+ * hash algorithms that PHP offers.
+ *
+ * @note This function may be used to obtain keys for SemanticData
+ * objects or to do simple equality tests. Equal hashes with very
+ * high probability indicate equal data.
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ if ( $this->hash !== null ) {
+ return $this->hash;
+ }
+
+ return $this->hash = Hash::createFromSemanticData( $this );
+ }
+
+ /**
+ * @see SubSemanticData::getSubSemanticData
+ *
+ * @since 1.8
+ *
+ * @return ContainerSemanticData[]
+ */
+ public function getSubSemanticData() {
+
+ // Remove the check in 3.0
+ $subSemanticData = $this->subSemanticData;
+
+ // Avoids an issue where the serialized array from a previous usage is
+ // returned from a __wakeup, where now a SubSemanticData (#2177) is expected.
+ if ( !$subSemanticData instanceof SubSemanticData ) {
+ $this->subSemanticData = new SubSemanticData( $this->mSubject, $this->mNoDuplicates );
+ $this->subSemanticData->copyDataFrom( $subSemanticData );
+ }
+
+ return $this->subSemanticData->getSubSemanticData();
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function clearSubSemanticData() {
+
+ if ( $this->subContainerDepthCounter > 0 ) {
+ $this->subContainerDepthCounter--;
+ }
+
+ if ( $this->subSemanticData !== null ) {
+ $this->subSemanticData->clear();
+ }
+ }
+
+ /**
+ * Return true if there are any visible properties.
+ *
+ * @note While called "visible" this check actually refers to the
+ * function DIProperty::isShown(). The name is kept for
+ * compatibility.
+ *
+ * @return boolean
+ */
+ public function hasVisibleProperties() {
+ return $this->mHasVisibleProps;
+ }
+
+ /**
+ * Return true if there are any special properties that can
+ * be displayed.
+ *
+ * @note While called "visible" this check actually refers to the
+ * function DIProperty::isShown(). The name is kept for
+ * compatibility.
+ *
+ * @return boolean
+ */
+ public function hasVisibleSpecialProperties() {
+ return $this->mHasVisibleSpecs;
+ }
+
+ /**
+ * Store a value for a property identified by its SMWDataItem object.
+ *
+ * @note There is no check whether the type of the given data item
+ * agrees with the type of the property. Since property types can
+ * change, all parts of SMW are prepared to handle mismatched data item
+ * types anyway.
+ *
+ * @param $property DIProperty
+ * @param $dataItem SMWDataItem
+ */
+ public function addPropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
+
+ $this->hash = null;
+
+ if( $dataItem instanceof SMWDIContainer ) {
+ $this->addSubSemanticData( $dataItem->getSemanticData() );
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if( $property->getKey() === DIProperty::TYPE_MODIFICATION_DATE ) {
+ $this->setOption( self::OPT_LAST_MODIFIED, $dataItem->getMwTimestamp() );
+ }
+
+ if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
+ return;
+ }
+
+ if ( !array_key_exists( $property->getKey(), $this->mPropVals ) ) {
+ $this->mPropVals[$property->getKey()] = [];
+ $this->mProperties[$property->getKey()] = $property;
+ }
+
+ if ( $this->mNoDuplicates ) {
+ $this->mPropVals[$property->getKey()][$dataItem->getHash()] = $dataItem;
+ } else {
+ $this->mPropVals[$property->getKey()][] = $dataItem;
+ }
+
+ if ( !$property->isUserDefined() ) {
+ if ( $property->isShown() ) {
+ $this->mHasVisibleSpecs = true;
+ $this->mHasVisibleProps = true;
+ }
+ } else {
+ $this->mHasVisibleProps = true;
+ }
+
+ // Account for things like DISPLAYTITLE or DEFAULTSORT which are only set
+ // after #subobject has been processed therefore keep them in-memory
+ // for a post process
+ if ( $this->mSubject->getSubobjectName() === '' && $property->getKey() === DIProperty::TYPE_SORTKEY ) {
+ foreach ( $this->getSubSemanticData() as $subSemanticData ) {
+ $subSemanticData->setExtensionData( 'sort.extension', $dataItem->getString() );
+ }
+ }
+ }
+
+ /**
+ * Store a value for a given property identified by its text label
+ * (without namespace prefix).
+ *
+ * @param $propertyName string
+ * @param $dataItem SMWDataItem
+ */
+ public function addPropertyValue( $propertyName, SMWDataItem $dataItem ) {
+ $propertyKey = smwfNormalTitleDBKey( $propertyName );
+
+ if ( array_key_exists( $propertyKey, $this->mProperties ) ) {
+ $property = $this->mProperties[$propertyKey];
+ } else {
+ if ( self::$mPropertyPrefix === '' ) {
+ global $wgContLang;
+ self::$mPropertyPrefix = $wgContLang->getNsText( SMW_NS_PROPERTY ) . ':';
+ } // explicitly use prefix to cope with things like [[Property:User:Stupid::somevalue]]
+
+ $propertyDV = DataValueFactory::getInstance()->newPropertyValueByLabel( self::$mPropertyPrefix . $propertyName );
+
+ if ( !$propertyDV->isValid() ) { // error, maybe illegal title text
+ return;
+ }
+
+ $property = $propertyDV->getDataItem();
+ }
+
+ $this->addPropertyObjectValue( $property, $dataItem );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param SMWDataValue $dataValue
+ */
+ public function addDataValue( SMWDataValue $dataValue ) {
+
+ if ( !$dataValue->getProperty() instanceof DIProperty || !$dataValue->isValid() ) {
+
+ $processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $this->getSubject()
+ );
+
+ $processingErrorMsgHandler->addToSemanticData(
+ $this,
+ $processingErrorMsgHandler->newErrorContainerFromDataValue( $dataValue )
+ );
+
+ return $this->addError( $dataValue->getErrors() );
+ }
+
+ $this->addPropertyObjectValue(
+ $dataValue->getProperty(),
+ $dataValue->getDataItem()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Subobject $subobject
+ */
+ public function addSubobject( Subobject $subobject ) {
+ $this->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+ }
+
+ /**
+ * Remove a value for a property identified by its SMWDataItem object.
+ * This method removes a property-value specified by the property and
+ * dataitem. If there are no more property-values for this property it
+ * also removes the property from the mProperties.
+ *
+ * @note There is no check whether the type of the given data item
+ * agrees with the type of the property. Since property types can
+ * change, all parts of SMW are prepared to handle mismatched data item
+ * types anyway.
+ *
+ * @param $property DIProperty
+ * @param $dataItem SMWDataItem
+ *
+ * @since 1.8
+ */
+ public function removePropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
+
+ $this->hash = null;
+
+ //delete associated subSemanticData
+ if( $dataItem instanceof SMWDIContainer ) {
+ $this->removeSubSemanticData( $dataItem->getSemanticData() );
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
+ return;
+ }
+
+ if ( !array_key_exists( $property->getKey(), $this->mPropVals ) || !array_key_exists( $property->getKey(), $this->mProperties ) ) {
+ return;
+ }
+
+ if ( $this->mNoDuplicates ) {
+ //this didn't get checked for my tests, but should work
+ unset( $this->mPropVals[$property->getKey()][$dataItem->getHash()] );
+ } else {
+ foreach( $this->mPropVals[$property->getKey()] as $index => $di ) {
+ if( $di->equals( $dataItem ) ) {
+ unset( $this->mPropVals[$property->getKey()][$index] );
+ }
+ }
+ $this->mPropVals[$property->getKey()] = array_values( $this->mPropVals[$property->getKey()] );
+ }
+
+ if ( $this->mPropVals[$property->getKey()] === [] ) {
+ unset( $this->mProperties[$property->getKey()] );
+ unset( $this->mPropVals[$property->getKey()] );
+ }
+ }
+
+ /**
+ * Removes a property and all the values associated with this property.
+ *
+ * @since 2.5
+ *
+ * @param $property DIProperty
+ */
+ public function removeProperty( DIProperty $property ) {
+
+ $this->hash = null;
+ $key = $property->getKey();
+
+ // Inverse properties cannot be used for an annotation
+ if ( $property->isInverse() ) {
+ return;
+ }
+
+ if ( !isset( $this->mProperties[$key] ) || !isset( $this->mPropVals[$key] ) ) {
+ return;
+ }
+
+ // Find and remove associated assignments (e.g. _ASK as subobject
+ // contains _ASKSI ...)
+ foreach ( $this->mPropVals[$key] as $dataItem ) {
+
+ if ( !$dataItem instanceof DIWikiPage || $dataItem->getSubobjectName() === '' ) {
+ continue;
+ }
+
+ if ( ( $subSemanticData = $this->findSubSemanticData( $dataItem->getSubobjectName() ) ) !== null ) {
+ $this->removeSubSemanticData( $subSemanticData );
+ }
+ }
+
+ unset( $this->mPropVals[$key] );
+ unset( $this->mProperties[$key] );
+ }
+
+ /**
+ * Delete all data other than the subject.
+ */
+ public function clear() {
+ $this->mPropVals = [];
+ $this->mProperties = [];
+ $this->mHasVisibleProps = false;
+ $this->mHasVisibleSpecs = false;
+ $this->stubObject = false;
+ $this->clearSubSemanticData();
+ $this->hash = null;
+ $this->options = null;
+ }
+
+ /**
+ * Return true if this SemanticData is empty.
+ * This is the case when the subject has neither property values nor
+ * data for subobjects.
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isEmpty() {
+ return $this->getProperties() === [] && $this->getSubSemanticData() === [];
+ }
+
+ /**
+ * Add all data from the given SMWSemanticData.
+ * Only works if the imported SMWSemanticData has the same subject as
+ * this SMWSemanticData; an exception is thrown otherwise.
+ *
+ * @since 1.7
+ *
+ * @param SemanticData $semanticData object to copy from
+ *
+ * @throws SemanticDataImportException
+ */
+ public function importDataFrom( SemanticData $semanticData ) {
+
+ if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
+ throw new SemanticDataImportException( "SemanticData can only represent data about one subject. Importing data for another subject is not possible." );
+ }
+
+ $this->hash = null;
+
+ // Shortcut when copying into empty objects that don't ask for
+ // more duplicate elimination:
+ if ( count( $this->mProperties ) == 0 &&
+ ( $semanticData->mNoDuplicates >= $this->mNoDuplicates ) ) {
+ $this->mProperties = $semanticData->getProperties();
+ $this->mPropVals = [];
+
+ foreach ( $this->mProperties as $property ) {
+ $this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
+ }
+
+ $this->mHasVisibleProps = $semanticData->hasVisibleProperties();
+ $this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
+ } else {
+ foreach ( $semanticData->getProperties() as $property ) {
+ $values = $semanticData->getPropertyValues( $property );
+
+ foreach ( $values as $dataItem ) {
+ $this->addPropertyObjectValue( $property, $dataItem);
+ }
+ }
+ }
+
+ foreach( $semanticData->getSubSemanticData() as $semData ) {
+ $this->addSubSemanticData( $semData );
+ }
+ }
+
+ /**
+ * Removes data from the given SMWSemanticData.
+ * If the subject of the data that is to be removed is not equal to the
+ * subject of this SMWSemanticData, it will just be ignored (nothing to
+ * remove). Likewise, removing data that is not present does not change
+ * anything.
+ *
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ */
+ public function removeDataFrom( SemanticData $semanticData ) {
+ if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
+ return;
+ }
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $this->removeProperty( $property );
+ }
+
+ foreach( $semanticData->getSubSemanticData() as $semData ) {
+ $this->removeSubSemanticData( $semData );
+ }
+ }
+
+ /**
+ * @see SubSemanticData::hasSubSemanticData
+ * @since 1.9
+ *
+ * @param string $subobjectName|null
+ *
+ * @return boolean
+ */
+ public function hasSubSemanticData( $subobjectName = null ) {
+ return $this->subSemanticData->hasSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * @see SubSemanticData::findSubSemanticData
+ * @since 1.9
+ *
+ * @param string $subobjectName
+ *
+ * @return SMWContainerSemanticData|null
+ */
+ public function findSubSemanticData( $subobjectName ) {
+ return $this->subSemanticData->findSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * @see SubSemanticData::addSubSemanticData
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ * @throws SubSemanticDataException
+ */
+ public function addSubSemanticData( SemanticData $semanticData ) {
+ $this->hash = null;
+ $this->subSemanticData->addSubSemanticData( $semanticData );
+ }
+
+ /**
+ * @see SubSemanticData::removeSubSemanticData
+ * @since 1.8
+ *
+ * @param SemanticData $semanticData
+ */
+ public function removeSubSemanticData( SemanticData $semanticData ) {
+ $this->hash = null;
+ $this->subSemanticData->removeSubSemanticData( $semanticData );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php b/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php
new file mode 100644
index 00000000..64d0b3d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Settings.php
@@ -0,0 +1,613 @@
+<?php
+
+namespace SMW;
+
+use SMW\Exception\SettingNotFoundException;
+
+/**
+ * Encapsulate Semantic MediaWiki settings to access values through a
+ * specified interface
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Settings extends Options {
+
+ /**
+ * @var Settings
+ */
+ private static $instance = null;
+
+ /**
+ * @var array
+ */
+ private $iterate = [];
+
+ /**
+ * Assemble individual SMW related settings into one accessible array for
+ * easy instantiation since we don't have unique way of accessing only
+ * SMW related settings ( e.g. $smwgSettings['...']) we need this method
+ * as short cut to invoke only smwg* related settings
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromGlobals();
+ * $settings->get( 'smwgDefaultStore' );
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @return Settings
+ */
+ public static function newFromGlobals() {
+
+ $configuration = [
+ 'smwgIP' => $GLOBALS['smwgIP'],
+ 'smwgExtraneousLanguageFileDir' => $GLOBALS['smwgExtraneousLanguageFileDir'],
+ 'smwgServicesFileDir' => $GLOBALS['smwgServicesFileDir'],
+ 'smwgResourceLoaderDefFiles' => $GLOBALS['smwgResourceLoaderDefFiles'],
+ 'smwgMaintenanceDir' => $GLOBALS['smwgMaintenanceDir'],
+ 'smwgConfigFileDir' => $GLOBALS['smwgConfigFileDir'],
+ 'smwgImportFileDirs' => $GLOBALS['smwgImportFileDirs'],
+ 'smwgImportReqVersion' => $GLOBALS['smwgImportReqVersion'],
+ 'smwgSemanticsEnabled' => $GLOBALS['smwgSemanticsEnabled'],
+ 'smwgUpgradeKey' => $GLOBALS['smwgUpgradeKey'],
+ 'smwgJobQueueWatchlist' => $GLOBALS['smwgJobQueueWatchlist'],
+ 'smwgEnabledCompatibilityMode' => $GLOBALS['smwgEnabledCompatibilityMode'],
+ 'smwgDefaultStore' => $GLOBALS['smwgDefaultStore'],
+ 'smwgDefaultLoggerRole' => $GLOBALS['smwgDefaultLoggerRole'],
+ 'smwgLocalConnectionConf' => $GLOBALS['smwgLocalConnectionConf'],
+ 'smwgSparqlRepositoryConnector' => $GLOBALS['smwgSparqlRepositoryConnector'],
+ 'smwgSparqlCustomConnector' => $GLOBALS['smwgSparqlCustomConnector'],
+ 'smwgSparqlEndpoint' => $GLOBALS['smwgSparqlEndpoint'],
+ 'smwgSparqlDefaultGraph' => $GLOBALS['smwgSparqlDefaultGraph'],
+ 'smwgSparqlRepositoryConnectorForcedHttpVersion' => $GLOBALS['smwgSparqlRepositoryConnectorForcedHttpVersion'],
+ 'smwgSparqlReplicationPropertyExemptionList' => $GLOBALS['smwgSparqlReplicationPropertyExemptionList'],
+ 'smwgSparqlQFeatures' => $GLOBALS['smwgSparqlQFeatures'],
+ 'smwgNamespaceIndex' => $GLOBALS['smwgNamespaceIndex'],
+ 'smwgFactboxFeatures' => $GLOBALS['smwgFactboxFeatures'],
+ 'smwgShowFactbox' => $GLOBALS['smwgShowFactbox'],
+ 'smwgShowFactboxEdit' => $GLOBALS['smwgShowFactboxEdit'],
+ 'smwgCompactLinkSupport' => $GLOBALS['smwgCompactLinkSupport'],
+ 'smwgDefaultNumRecurringEvents' => $GLOBALS['smwgDefaultNumRecurringEvents'],
+ 'smwgMaxNumRecurringEvents' => $GLOBALS['smwgMaxNumRecurringEvents'],
+ 'smwgSearchByPropertyFuzzy' => $GLOBALS['smwgSearchByPropertyFuzzy'],
+ 'smwgPagingLimit' => $GLOBALS['smwgPagingLimit'],
+ 'smwgPropertyListLimit' => $GLOBALS['smwgPropertyListLimit'],
+ 'smwgQEnabled' => $GLOBALS['smwgQEnabled'],
+ 'smwgQMaxLimit' => $GLOBALS['smwgQMaxLimit'],
+ 'smwgIgnoreQueryErrors' => $GLOBALS['smwgIgnoreQueryErrors'],
+ 'smwgQSubcategoryDepth' => $GLOBALS['smwgQSubcategoryDepth'],
+ 'smwgQSubpropertyDepth' => $GLOBALS['smwgQSubpropertyDepth'],
+ 'smwgQEqualitySupport' => $GLOBALS['smwgQEqualitySupport'],
+ 'smwgQDefaultNamespaces' => $GLOBALS['smwgQDefaultNamespaces'],
+ 'smwgQComparators' => $GLOBALS['smwgQComparators'],
+ 'smwgQFilterDuplicates' => $GLOBALS['smwgQFilterDuplicates'],
+ 'smwStrictComparators' => $GLOBALS['smwStrictComparators'],
+ 'smwgQStrictComparators' => $GLOBALS['smwgQStrictComparators'],
+ 'smwgQMaxSize' => $GLOBALS['smwgQMaxSize'],
+ 'smwgQMaxDepth' => $GLOBALS['smwgQMaxDepth'],
+ 'smwgQFeatures' => $GLOBALS['smwgQFeatures'],
+ 'smwgQDefaultLimit' => $GLOBALS['smwgQDefaultLimit'],
+ 'smwgQUpperbound' => $GLOBALS['smwgQUpperbound'],
+ 'smwgQMaxInlineLimit' => $GLOBALS['smwgQMaxInlineLimit'],
+ 'smwgQPrintoutLimit' => $GLOBALS['smwgQPrintoutLimit'],
+ 'smwgQDefaultLinking' => $GLOBALS['smwgQDefaultLinking'],
+ 'smwgQConceptCaching' => $GLOBALS['smwgQConceptCaching'],
+ 'smwgQConceptMaxSize' => $GLOBALS['smwgQConceptMaxSize'],
+ 'smwgQConceptMaxDepth' => $GLOBALS['smwgQConceptMaxDepth'],
+ 'smwgQConceptFeatures' => $GLOBALS['smwgQConceptFeatures'],
+ 'smwgQConceptCacheLifetime' => $GLOBALS['smwgQConceptCacheLifetime'],
+ 'smwgQExpensiveThreshold' => $GLOBALS['smwgQExpensiveThreshold'],
+ 'smwgQExpensiveExecutionLimit' => $GLOBALS['smwgQExpensiveExecutionLimit'],
+ 'smwgRemoteReqFeatures' => $GLOBALS['smwgRemoteReqFeatures'],
+ 'smwgQuerySources' => $GLOBALS['smwgQuerySources'],
+ 'smwgQTemporaryTablesAutoCommitMode' => $GLOBALS['smwgQTemporaryTablesAutoCommitMode'],
+ 'smwgQSortFeatures' => $GLOBALS['smwgQSortFeatures'],
+ 'smwgResultFormats' => $GLOBALS['smwgResultFormats'],
+ 'smwgResultFormatsFeatures' => $GLOBALS['smwgResultFormatsFeatures'],
+ 'smwgResultAliases' => $GLOBALS['smwgResultAliases'],
+ 'smwgPDefaultType' => $GLOBALS['smwgPDefaultType'],
+ 'smwgAllowRecursiveExport' => $GLOBALS['smwgAllowRecursiveExport'],
+ 'smwgExportBacklinks' => $GLOBALS['smwgExportBacklinks'],
+ 'smwgExportResourcesAsIri' => $GLOBALS['smwgExportResourcesAsIri'],
+ 'smwgExportBCNonCanonicalFormUse' => $GLOBALS['smwgExportBCNonCanonicalFormUse'],
+ 'smwgExportBCAuxiliaryUse' => $GLOBALS['smwgExportBCAuxiliaryUse'],
+ 'smwgMaxNonExpNumber' => $GLOBALS['smwgMaxNonExpNumber'],
+ 'smwgEnableUpdateJobs' => $GLOBALS['smwgEnableUpdateJobs'],
+ 'smwgNamespacesWithSemanticLinks' => $GLOBALS['smwgNamespacesWithSemanticLinks'],
+ 'smwgPageSpecialProperties' => $GLOBALS['smwgPageSpecialProperties'],
+ 'smwgChangePropagationWatchlist' => $GLOBALS['smwgChangePropagationWatchlist'],
+ 'smwgDataTypePropertyExemptionList' => $GLOBALS['smwgDataTypePropertyExemptionList'],
+ 'smwgDefaultOutputFormatters' => $GLOBALS['smwgDefaultOutputFormatters'],
+ 'smwgTranslate' => $GLOBALS['smwgTranslate'],
+ 'smwgAutoRefreshSubject' => $GLOBALS['smwgAutoRefreshSubject'],
+ 'smwgAdminFeatures' => $GLOBALS['smwgAdminFeatures'],
+ 'smwgAutoRefreshOnPurge' => $GLOBALS['smwgAutoRefreshOnPurge'],
+ 'smwgAutoRefreshOnPageMove' => $GLOBALS['smwgAutoRefreshOnPageMove'],
+ 'smwgContLang' => isset( $GLOBALS['smwgContLang'] ) ? $GLOBALS['smwgContLang'] : '',
+ 'smwgMaxPropertyValues' => $GLOBALS['smwgMaxPropertyValues'],
+ 'smwgNamespace' => $GLOBALS['smwgNamespace'],
+ 'smwgMasterStore' => isset( $GLOBALS['smwgMasterStore'] ) ? $GLOBALS['smwgMasterStore'] : '',
+ 'smwgIQRunningNumber' => isset( $GLOBALS['smwgIQRunningNumber'] ) ? $GLOBALS['smwgIQRunningNumber'] : 0,
+ 'smwgCacheUsage' => $GLOBALS['smwgCacheUsage'],
+ 'smwgMainCacheType' => $GLOBALS['smwgMainCacheType'],
+ 'smwgEntityLookupCacheType' => $GLOBALS['smwgEntityLookupCacheType'],
+ 'smwgEntityLookupCacheLifetime' => $GLOBALS['smwgEntityLookupCacheLifetime'],
+ 'smwgEntityLookupFeatures' => $GLOBALS['smwgEntityLookupFeatures'],
+ 'smwgFixedProperties' => $GLOBALS['smwgFixedProperties'],
+ 'smwgPropertyLowUsageThreshold' => $GLOBALS['smwgPropertyLowUsageThreshold'],
+ 'smwgPropertyZeroCountDisplay' => $GLOBALS['smwgPropertyZeroCountDisplay'],
+ 'smwgQueryProfiler' => $GLOBALS['smwgQueryProfiler'],
+ 'smwgEnabledSpecialPage' => $GLOBALS['smwgEnabledSpecialPage'],
+ 'smwgFallbackSearchType' => $GLOBALS['smwgFallbackSearchType'],
+ 'smwgEnabledEditPageHelp' => $GLOBALS['smwgEnabledEditPageHelp'],
+ 'smwgEnabledDeferredUpdate' => $GLOBALS['smwgEnabledDeferredUpdate'],
+ 'smwgEnabledQueryDependencyLinksStore' => $GLOBALS['smwgEnabledQueryDependencyLinksStore'],
+ 'smwgQueryDependencyPropertyExemptionList' => $GLOBALS['smwgQueryDependencyPropertyExemptionList'],
+ 'smwgQueryDependencyAffiliatePropertyDetectionList' => $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionList'],
+ 'smwgParserFeatures' => $GLOBALS['smwgParserFeatures'],
+ 'smwgDVFeatures' => $GLOBALS['smwgDVFeatures'],
+ 'smwgEnabledFulltextSearch' => $GLOBALS['smwgEnabledFulltextSearch'],
+ 'smwgFulltextDeferredUpdate' => $GLOBALS['smwgFulltextDeferredUpdate'],
+ 'smwgFulltextSearchTableOptions' => $GLOBALS['smwgFulltextSearchTableOptions'],
+ 'smwgFulltextSearchPropertyExemptionList' => $GLOBALS['smwgFulltextSearchPropertyExemptionList'],
+ 'smwgFulltextSearchMinTokenSize' => $GLOBALS['smwgFulltextSearchMinTokenSize'],
+ 'smwgFulltextLanguageDetection' => $GLOBALS['smwgFulltextLanguageDetection'],
+ 'smwgFulltextSearchIndexableDataTypes' => $GLOBALS['smwgFulltextSearchIndexableDataTypes'],
+ 'smwgQueryResultCacheType' => $GLOBALS['smwgQueryResultCacheType'],
+ 'smwgQueryResultCacheLifetime' => $GLOBALS['smwgQueryResultCacheLifetime'],
+ 'smwgQueryResultNonEmbeddedCacheLifetime' => $GLOBALS['smwgQueryResultNonEmbeddedCacheLifetime'],
+ 'smwgQueryResultCacheRefreshOnPurge' => $GLOBALS['smwgQueryResultCacheRefreshOnPurge'],
+ 'smwgEditProtectionRight' => $GLOBALS['smwgEditProtectionRight'],
+ 'smwgCreateProtectionRight' => $GLOBALS['smwgCreateProtectionRight'],
+ 'smwgSimilarityLookupExemptionProperty' => $GLOBALS['smwgSimilarityLookupExemptionProperty'],
+ 'smwgPropertyInvalidCharacterList' => $GLOBALS['smwgPropertyInvalidCharacterList'],
+ 'smwgPropertyReservedNameList' => $GLOBALS['smwgPropertyReservedNameList'],
+ 'smwgEntityCollation' => $GLOBALS['smwgEntityCollation'],
+ 'smwgExperimentalFeatures' => $GLOBALS['smwgExperimentalFeatures'],
+ 'smwgFieldTypeFeatures' => $GLOBALS['smwgFieldTypeFeatures'],
+ 'smwgChangePropagationProtection' => $GLOBALS['smwgChangePropagationProtection'],
+ 'smwgUseComparableContentHash' => $GLOBALS['smwgUseComparableContentHash'],
+ 'smwgBrowseFeatures' => $GLOBALS['smwgBrowseFeatures'],
+ 'smwgCategoryFeatures' => $GLOBALS['smwgCategoryFeatures'],
+ 'smwgURITypeSchemeList' => $GLOBALS['smwgURITypeSchemeList'],
+ 'smwgSchemaTypes' => $GLOBALS['smwgSchemaTypes'],
+ 'smwgElasticsearchConfig' => $GLOBALS['smwgElasticsearchConfig'],
+ 'smwgElasticsearchProfile' => $GLOBALS['smwgElasticsearchProfile'],
+ 'smwgElasticsearchEndpoints' => $GLOBALS['smwgElasticsearchEndpoints'],
+ 'smwgPostEditUpdate' => $GLOBALS['smwgPostEditUpdate'],
+ 'smwgSpecialAskFormSubmitMethod' => $GLOBALS['smwgSpecialAskFormSubmitMethod'],
+ 'smwgSupportSectionTag' => $GLOBALS['smwgSupportSectionTag'],
+ ];
+
+ self::initLegacyMapping( $configuration );
+
+ \Hooks::run( 'SMW::Config::BeforeCompletion', [ &$configuration ] );
+
+ if ( self::$instance === null ) {
+ self::$instance = self::newFromArray( $configuration );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Factory method for immediate instantiation of a settings object for a
+ * given array
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromArray( array( 'Foo' => 'Bar' ) );
+ * $settings->get( 'Foo' );
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @return Settings
+ */
+ public static function newFromArray( array $settings ) {
+ return new self( $settings );
+ }
+
+ /**
+ * Returns settings for a given key (nested settings are supported)
+ *
+ * @par Example:
+ * @code
+ * $settings = Settings::newFromArray( array(
+ * 'Foo' => 'Bar'
+ * 'Parent' => array(
+ * 'Child' => array( 'Lisa', 'Lula', array( 'Lila' ) )
+ * )
+ * );
+ *
+ * $settings->get( 'Child' ) will return array( 'Lisa', 'Lula', array( 'Lila' ) )
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param string $key
+ *
+ * @return mixed
+ * @throws SettingNotFoundException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return parent::get( $key );
+ }
+
+ // If the key wasn't matched it could be because of a nested array
+ // hence iterate and verify otherwise throw an exception
+ return $this->doIterate( $key, $this->toArray() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function safeGet( $key, $default = false ) {
+
+ try {
+ $r = $this->get( $key );
+ } catch ( SettingNotFoundException $e ) {
+ return $default;
+ }
+
+ return $r;
+ }
+
+ /**
+ * @since 1.9
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * Iterates over a nested array to find an element
+ */
+ private function doIterate( $key, $options ) {
+
+ if ( isset( $this->iterate[$key] ) ) {
+ return $this->iterate[$key];
+ }
+
+ $iterator = new \RecursiveIteratorIterator(
+ new \RecursiveArrayIterator( $options ),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
+
+ foreach( $iterator as $it => $value ) {
+ if ( $key === $it ) {
+ return $this->iterate[$key] = $value;
+ }
+ }
+
+ throw new SettingNotFoundException( "'{$key}' is not a valid settings key" );
+ }
+
+ private static function initLegacyMapping( &$configuration ) {
+
+ if ( isset( $GLOBALS['smwgAdminRefreshStore'] ) && $GLOBALS['smwgAdminRefreshStore'] === false ) {
+ $configuration['smwgAdminFeatures'] = $configuration['smwgAdminFeatures'] & ~SMW_ADM_REFRESH;
+ }
+
+ // smwgParserFeatures
+ if ( isset( $GLOBALS['smwgEnabledInTextAnnotationParserStrictMode'] ) && $GLOBALS['smwgEnabledInTextAnnotationParserStrictMode'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_STRICT;
+ }
+
+ if ( isset( $GLOBALS['smwgInlineErrors'] ) && $GLOBALS['smwgInlineErrors'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_INL_ERROR;
+ }
+
+ if ( isset( $GLOBALS['smwgShowHiddenCategories'] ) && $GLOBALS['smwgShowHiddenCategories'] === false ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] & ~SMW_PARSER_HID_CATS;
+ }
+
+ // smwgFactboxFeatures
+ if ( isset( $GLOBALS['smwgFactboxUseCache'] ) && $GLOBALS['smwgFactboxUseCache'] === false ) {
+ $configuration['smwgFactboxFeatures'] = $configuration['smwgFactboxFeatures'] & ~SMW_FACTBOX_CACHE;
+ }
+
+ if ( isset( $GLOBALS['smwgFactboxCacheRefreshOnPurge'] ) && $GLOBALS['smwgFactboxCacheRefreshOnPurge'] === false ) {
+ $configuration['smwgFactboxFeatures'] = $configuration['smwgFactboxFeatures'] & ~SMW_FACTBOX_PURGE_REFRESH;
+ }
+
+ // smwgLinksInValues
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === SMW_LINV_PCRE ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === SMW_LINV_OBFU ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ if ( isset( $GLOBALS['smwgLinksInValues'] ) && $GLOBALS['smwgLinksInValues'] === true ) {
+ $configuration['smwgParserFeatures'] = $configuration['smwgParserFeatures'] | SMW_PARSER_LINV;
+ }
+
+ // smwgCategoryFeatures
+ if ( isset( $GLOBALS['smwgUseCategoryRedirect'] ) && $GLOBALS['smwgUseCategoryRedirect'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_REDIRECT;
+ }
+
+ if ( isset( $GLOBALS['smwgCategoriesAsInstances'] ) && $GLOBALS['smwgCategoriesAsInstances'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_INSTANCE;
+ }
+
+ if ( isset( $GLOBALS['smwgUseCategoryHierarchy'] ) && $GLOBALS['smwgUseCategoryHierarchy'] === false ) {
+ $configuration['smwgCategoryFeatures'] = $configuration['smwgCategoryFeatures'] & ~SMW_CAT_HIERARCHY;
+ }
+
+ if ( isset( $GLOBALS['smwgQueryDependencyPropertyExemptionlist'] ) ) {
+ $configuration['smwgQueryDependencyPropertyExemptionList'] = $GLOBALS['smwgQueryDependencyPropertyExemptionlist'];
+ }
+
+ if ( isset( $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionlist'] ) ) {
+ $configuration['smwgQueryDependencyAffiliatePropertyDetectionList'] = $GLOBALS['smwgQueryDependencyAffiliatePropertyDetectionlist'];
+ }
+
+ // smwgPropertyListLimit
+ if ( isset( $GLOBALS['smwgSubPropertyListLimit'] ) ) {
+ $configuration['smwgPropertyListLimit']['subproperty'] = $GLOBALS['smwgSubPropertyListLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgRedirectPropertyListLimit'] ) ) {
+ $configuration['smwgPropertyListLimit']['redirect'] = $GLOBALS['smwgRedirectPropertyListLimit'];
+ }
+
+ // smwgCacheUsage
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgStatisticsCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.statistics'] = $GLOBALS['smwgCacheUsage']['smwgStatisticsCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgStatisticsCache'] ) && $GLOBALS['smwgCacheUsage']['smwgStatisticsCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.statistics'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.properties'] = $GLOBALS['smwgCacheUsage']['smwgPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.properties'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.unusedproperties'] = $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgUnusedPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.unusedproperties'] = false;
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCacheExpiry'] ) ) {
+ $configuration['smwgCacheUsage']['special.wantedproperties'] = $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCacheExpiry'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCache'] ) && $GLOBALS['smwgCacheUsage']['smwgWantedPropertiesCache'] === false ) {
+ $configuration['smwgCacheUsage']['special.wantedproperties'] = false;
+ }
+
+ // smwgQueryProfiler
+ if ( isset( $GLOBALS['smwgQueryProfiler']['smwgQueryDurationEnabled'] ) && $GLOBALS['smwgQueryProfiler']['smwgQueryDurationEnabled'] === true ) {
+ $configuration['smwgQueryProfiler'] = $configuration['smwgQueryProfiler'] | SMW_QPRFL_DUR;
+ }
+
+ if ( isset( $GLOBALS['smwgQueryProfiler']['smwgQueryParametersEnabled'] ) && $GLOBALS['smwgQueryProfiler']['smwgQueryParametersEnabled'] === true ) {
+ $configuration['smwgQueryProfiler'] = $configuration['smwgQueryProfiler'] | SMW_QPRFL_PARAMS;
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDatabaseConnector'] ) ) {
+ $configuration['smwgSparqlRepositoryConnector'] = $GLOBALS['smwgSparqlDatabaseConnector'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDatabase'] ) ) {
+ $configuration['smwgSparqlCustomConnector'] = $GLOBALS['smwgSparqlDatabase'];
+ }
+
+ if ( isset( $GLOBALS['smwgDeclarationProperties'] ) ) {
+ $configuration['smwgChangePropagationWatchlist'] = $GLOBALS['smwgDeclarationProperties'];
+ }
+
+ // smwgBrowseFeatures
+ if ( isset( $GLOBALS['smwgToolboxBrowseLink'] ) && $GLOBALS['smwgToolboxBrowseLink'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_TLINK;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseShowInverse'] ) && $GLOBALS['smwgBrowseShowInverse'] === true ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] | SMW_BROWSE_SHOW_INVERSE;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseShowAll'] ) && $GLOBALS['smwgBrowseShowAll'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_SHOW_INCOMING;
+ }
+
+ if ( isset( $GLOBALS['smwgBrowseByApi'] ) && $GLOBALS['smwgBrowseByApi'] === false ) {
+ $configuration['smwgBrowseFeatures'] = $configuration['smwgBrowseFeatures'] & ~SMW_BROWSE_USE_API;
+ }
+
+ // smwgQSortFeatures
+ if ( isset( $GLOBALS['smwgQSortingSupport'] ) && $GLOBALS['smwgQSortingSupport'] === false ) {
+ $configuration['smwgQSortFeatures'] = $configuration['smwgQSortFeatures'] & ~SMW_QSORT;
+ }
+
+ if ( isset( $GLOBALS['smwgQRandSortingSupport'] ) && $GLOBALS['smwgQRandSortingSupport'] === false ) {
+ $configuration['smwgQSortFeatures'] = $configuration['smwgQSortFeatures'] & ~SMW_QSORT_RANDOM;
+ }
+
+ if ( isset( $GLOBALS['smwgImportFileDir'] ) ) {
+ $configuration['smwgImportFileDirs'] = (array)$GLOBALS['smwgImportFileDir'];
+ }
+
+ // smwgValueLookupFeatures
+ if ( isset( $GLOBALS['smwgValueLookupCacheType'] ) ) {
+ $configuration['smwgEntityLookupCacheType'] = $GLOBALS['smwgValueLookupCacheType'];
+ }
+
+ if ( isset( $GLOBALS['smwgValueLookupCacheLifetime'] ) ) {
+ $configuration['smwgEntityLookupCacheLifetime'] = $GLOBALS['smwgValueLookupCacheLifetime'];
+ }
+
+ if ( isset( $GLOBALS['smwgValueLookupFeatures'] ) ) {
+ $configuration['smwgEntityLookupFeatures'] = $GLOBALS['smwgValueLookupFeatures'];
+ }
+
+ // smwgPagingLimit
+ if ( isset( $GLOBALS['smwgTypePagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['type'] = $GLOBALS['smwgTypePagingLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgConceptPagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['concept'] = $GLOBALS['smwgConceptPagingLimit'];
+ }
+
+ if ( isset( $GLOBALS['smwgPropertyPagingLimit'] ) ) {
+ $configuration['smwgPagingLimit']['property'] = $GLOBALS['smwgPropertyPagingLimit'];
+ }
+
+ // smwgSparqlEndpoint
+ if ( isset( $GLOBALS['smwgSparqlQueryEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['query'] = $GLOBALS['smwgSparqlQueryEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlUpdateEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['update'] = $GLOBALS['smwgSparqlUpdateEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgSparqlDataEndpoint'] ) ) {
+ $configuration['smwgSparqlEndpoint']['data'] = $GLOBALS['smwgSparqlDataEndpoint'];
+ }
+
+ if ( isset( $GLOBALS['smwgCacheType'] ) ) {
+ $configuration['smwgMainCacheType'] = $GLOBALS['smwgCacheType'];
+ }
+
+ $jobQueueWatchlist = [];
+
+ // FIXME Remove with 3.1
+ foreach ( $GLOBALS['smwgJobQueueWatchlist'] as $job ) {
+ if ( strpos( $job, 'SMW\\' ) !== false ) {
+ $jobQueueWatchlist[$job] = \SMW\MediaWiki\JobQueue::mapLegacyType( $job );
+ }
+ }
+
+ // Deprecated mapping used in DeprecationNoticeTaskHandler to detect and
+ // output notices
+ $GLOBALS['smwgDeprecationNotices']['smw'] = [
+ 'notice' => [
+ 'smwgAdminRefreshStore' => '3.1.0',
+ 'smwgQueryDependencyPropertyExemptionlist' => '3.1.0',
+ 'smwgQueryDependencyAffiliatePropertyDetectionlist' => '3.1.0',
+ 'smwgSubPropertyListLimit' => '3.1.0',
+ 'smwgRedirectPropertyListLimit' => '3.1.0',
+ 'smwgSparqlDatabaseConnector' => '3.1.0',
+ 'smwgSparqlDatabase' => '3.1.0',
+ 'smwgDeclarationProperties' => '3.1.0',
+ 'smwgToolboxBrowseLink' => '3.1.0',
+ 'smwgBrowseShowInverse' => '3.1.0',
+ 'smwgBrowseShowAll' => '3.1.0',
+ 'smwgBrowseByApi' => '3.1.0',
+ 'smwgEnabledInTextAnnotationParserStrictMode' => '3.1.0',
+ 'smwgInlineErrors' => '3.1.0',
+ 'smwgShowHiddenCategories' => '3.1.0',
+ 'smwgUseCategoryRedirect' => '3.1.0',
+ 'smwgCategoriesAsInstances' => '3.1.0',
+ 'smwgUseCategoryHierarchy' => '3.1.0',
+ 'smwgQSortingSupport' => '3.1.0',
+ 'smwgQRandSortingSupport' => '3.1.0',
+ 'smwgLinksInValues' => '3.1.0',
+ 'smwgTypePagingLimit' => '3.1.0',
+ 'smwgConceptPagingLimit' => '3.1.0',
+ 'smwgPropertyPagingLimit' => '3.1.0',
+ 'smwgSparqlQueryEndpoint' => '3.1.0',
+ 'smwgSparqlUpdateEndpoint' => '3.1.0',
+ 'smwgSparqlDataEndpoint' => '3.1.0',
+ 'smwgCacheType' => '3.1.0',
+ 'smwgFactboxUseCache' => '3.1.0',
+ 'smwgFactboxCacheRefreshOnPurge' => '3.1.0',
+ 'options' => [
+ 'smwgCacheUsage' => [
+ 'smwgStatisticsCache' => '3.1.0',
+ 'smwgStatisticsCacheExpiry' => '3.1.0',
+ 'smwgPropertiesCache' => '3.1.0',
+ 'smwgPropertiesCacheExpiry' => '3.1.0',
+ 'smwgUnusedPropertiesCache' => '3.1.0',
+ 'smwgUnusedPropertiesCacheExpiry' => '3.1.0',
+ 'smwgWantedPropertiesCache' => '3.1.0',
+ 'smwgWantedPropertiesCacheExpiry' => '3.1.0',
+ ],
+ 'smwgQueryProfiler' => [
+ 'smwgQueryDurationEnabled' => '3.1.0',
+ 'smwgQueryParametersEnabled' => '3.1.0'
+ ]
+ ]
+ ],
+ 'replacement' => [
+ 'smwgAdminRefreshStore' => 'smwgAdminFeatures',
+ 'smwgQueryDependencyPropertyExemptionlist' => 'smwgQueryDependencyPropertyExemptionList',
+ 'smwgQueryDependencyAffiliatePropertyDetectionlist' => 'smwgQueryDependencyAffiliatePropertyDetectionList',
+ 'smwgSubPropertyListLimit' => 'smwgPropertyListLimit',
+ 'smwgRedirectPropertyListLimit' => 'smwgPropertyListLimit',
+ 'smwgSparqlDatabaseConnector' => 'smwgSparqlRepositoryConnector',
+ 'smwgSparqlDatabase' => 'smwgSparqlCustomConnector',
+ 'smwgDeclarationProperties' => 'smwgChangePropagationWatchlist',
+ 'smwgToolboxBrowseLink' => 'smwgBrowseFeatures',
+ 'smwgBrowseShowInverse' => 'smwgBrowseFeatures',
+ 'smwgBrowseShowAll' => 'smwgBrowseFeatures',
+ 'smwgBrowseByApi' => 'smwgBrowseFeatures',
+ 'smwgEnabledInTextAnnotationParserStrictMode' => 'smwgParserFeatures',
+ 'smwgInlineErrors' => 'smwgParserFeatures',
+ 'smwgShowHiddenCategories' => 'smwgParserFeatures',
+ 'smwgLinksInValues' => 'smwgParserFeatures',
+ 'smwgUseCategoryRedirect' => 'smwgCategoryFeatures',
+ 'smwgCategoriesAsInstances' => 'smwgCategoryFeatures',
+ 'smwgUseCategoryHierarchy' => 'smwgCategoryFeatures',
+ 'smwgQSortingSupport' => 'smwgQSortFeatures',
+ 'smwgQRandSortingSupport' => 'smwgQSortFeatures',
+ 'smwgImportFileDir' => 'smwgImportFileDirs',
+ 'smwgValueLookupCacheType' => 'smwgEntityLookupCacheType',
+ 'smwgValueLookupCacheLifetime' => 'smwgEntityLookupCacheLifetime',
+ 'smwgValueLookupFeatures' => 'smwgEntityLookupFeatures',
+ 'smwgTypePagingLimit' => 'smwgPagingLimit',
+ 'smwgConceptPagingLimit' => 'smwgPagingLimit',
+ 'smwgPropertyPagingLimit' => 'smwgPagingLimit',
+ 'smwgSparqlQueryEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgSparqlUpdateEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgSparqlDataEndpoint' => 'smwgSparqlEndpoint',
+ 'smwgCacheType' => 'smwgMainCacheType',
+ 'smwgFactboxUseCache' => 'smwgFactboxFeatures',
+ 'smwgFactboxCacheRefreshOnPurge' => 'smwgFactboxFeatures',
+ 'options' => [
+ 'smwgCacheUsage' => [
+ 'smwgStatisticsCacheExpiry' => 'special.statistics',
+ 'smwgPropertiesCacheExpiry' => 'special.properties',
+ 'smwgUnusedPropertiesCacheExpiry' => 'special.unusedproperties',
+ 'smwgWantedPropertiesCacheExpiry' => 'special.wantedproperties',
+ ],
+ 'smwgQueryProfiler' => [
+ 'smwgQueryDurationEnabled' => 'SMW_QPRFL_DUR',
+ 'smwgQueryParametersEnabled' => 'SMW_QPRFL_PARAMS'
+ ]
+ ] + ( $jobQueueWatchlist !== [] ? [ 'smwgJobQueueWatchlist' => $jobQueueWatchlist ] : [] )
+ ],
+ 'removal' => [
+ 'smwgOnDeleteAction' => '2.4.0',
+ 'smwgAutocompleteInSpecialAsk' => '3.0.0',
+ 'smwgSparqlDatabaseMaster' => '3.0.0',
+ 'smwgHistoricTypeNamespace' => '3.0.0',
+ 'smwgEnabledHttpDeferredJobRequest' => '3.0.0'
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php b/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php
new file mode 100644
index 00000000..33d215a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Setup.php
@@ -0,0 +1,441 @@
+<?php
+
+namespace SMW;
+
+use Hooks;
+use SMW\Connection\ConnectionManager;
+use SMW\MediaWiki\Hooks\HookRegistry;
+use SMW\SQLStore\Installer;
+
+/**
+ * Extension setup and registration
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+final class Setup {
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory;
+
+ /**
+ * @since 1.9
+ *
+ * @param ApplicationFactory $applicationFactory
+ */
+ public function __construct( ApplicationFactory $applicationFactory ) {
+ $this->applicationFactory = $applicationFactory;
+ }
+
+ /**
+ * Runs at the earliest possible event to initialize functions or hooks that
+ * are otherwise too late for the hook system to be recognized.
+ *
+ * @since 3.0
+ */
+ public static function initExtension( &$vars ) {
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Localisation#Localising_namespaces_and_special_page_aliases
+ */
+ $vars['wgMessagesDirs']['SemanticMediaWiki'] = $vars['smwgIP'] . 'i18n';
+ $vars['wgExtensionMessagesFiles']['SemanticMediaWikiAlias'] = $vars['smwgIP'] . 'i18n/extra/SemanticMediaWiki.alias.php';
+ $vars['wgExtensionMessagesFiles']['SemanticMediaWikiMagic'] = $vars['smwgIP'] . 'i18n/extra/SemanticMediaWiki.magic.php';
+
+ HookRegistry::initExtension( $vars );
+ }
+
+ /**
+ * @see HookRegistry::initExtension
+ */
+ public static function getAPIModules() {
+
+ if ( !ApplicationFactory::getInstance()->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return [];
+ }
+
+ return [
+ 'smwinfo' => '\SMW\MediaWiki\Api\Info',
+ 'smwtask' => '\SMW\MediaWiki\Api\Task',
+ 'smwbrowse' => '\SMW\MediaWiki\Api\Browse',
+ 'ask' => '\SMW\MediaWiki\Api\Ask',
+ 'askargs' => '\SMW\MediaWiki\Api\AskArgs',
+ 'browsebysubject' => '\SMW\MediaWiki\Api\BrowseBySubject',
+ 'browsebyproperty' => '\SMW\MediaWiki\Api\BrowseByProperty'
+ ];
+ }
+
+ /**
+ * @see HookRegistry::initExtension
+ */
+ public static function initSpecialPageList( array &$specialPages ) {
+
+ if ( !ApplicationFactory::getInstance()->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ $specials = [
+ 'Ask' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialAsk'
+ ],
+ 'Browse' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialBrowse'
+ ],
+ 'PageProperty' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialPageProperty'
+ ],
+ 'SearchByProperty' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialSearchByProperty'
+ ],
+ 'ProcessingErrorList' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialProcessingErrorList'
+ ],
+ 'PropertyLabelSimilarity' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialPropertyLabelSimilarity'
+ ],
+ 'SMWAdmin' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialAdmin'
+ ],
+ 'Concepts' => [
+ 'page' => 'SMW\SpecialConcepts'
+ ],
+ 'ExportRDF' => [
+ 'page' => 'SMWSpecialOWLExport'
+ ],
+ 'Types' => [
+ 'page' => 'SMWSpecialTypes'
+ ],
+ 'URIResolver' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialURIResolver'
+ ],
+ 'Properties' => [
+ 'page' => 'SMW\SpecialProperties'
+ ],
+ 'UnusedProperties' => [
+ 'page' => 'SMW\SpecialUnusedProperties'
+ ],
+ 'WantedProperties' => [
+ 'page' => 'SMW\SpecialWantedProperties'
+ ],
+ 'DeferredRequestDispatcher' => [
+ 'page' => 'SMW\MediaWiki\Specials\SpecialDeferredRequestDispatcher'
+ ],
+ ];
+
+ // Register data
+ foreach ( $specials as $special => $page ) {
+ $specialPages[$special] = $page['page'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function isEnabled() {
+ return defined( 'SMW_VERSION' ) && $GLOBALS['smwgSemanticsEnabled'];
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function isValid( $isCli = false ) {
+ return Installer::isGoodSchema( $isCli );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$vars
+ */
+ public function loadSchema( &$vars ) {
+ Installer::loadSchema( $vars );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array &$vars
+ * @param string $directory
+ */
+ public function init( &$vars, $directory ) {
+
+ $this->initMessageCallbackHandler();
+
+ if ( $this->isValid() === false ) {
+ smwfAbort(
+ Message::get( [ 'smw-upgrade-error', $vars['smwgUpgradeKey'] ], Message::PARSE ) .
+ '<h3>' . Message::get( 'smw-upgrade-error-why-title' ) . '</h3>' .
+ Message::get( 'smw-upgrade-error-why-explain', Message::PARSE ) .
+ '<h3>' . Message::get( 'smw-upgrade-error-how-title' ) . '</h3>' .
+ Message::get( 'smw-upgrade-error-how-explain', Message::PARSE )
+ );
+ }
+
+ $this->addDefaultConfigurations( $vars );
+
+ if ( CompatibilityMode::extensionNotEnabled() ) {
+ CompatibilityMode::disableSemantics();
+ }
+
+ $this->initConnectionProviders( );
+
+ $this->registerJobClasses( $vars );
+ $this->registerPermissions( $vars );
+
+ $this->registerParamDefinitions( $vars );
+ $this->registerFooterIcon( $vars, $directory );
+ $this->registerHooks( $vars, $directory );
+
+ Hooks::run( 'SMW::Setup::AfterInitializationComplete', [ &$vars ] );
+ }
+
+ private function addDefaultConfigurations( &$vars ) {
+
+ $vars['wgLogTypes'][] = 'smw';
+ $vars['wgFilterLogTypes']['smw'] = true;
+
+ $vars['smwgMasterStore'] = null;
+ $vars['smwgIQRunningNumber'] = 0;
+
+ if ( !isset( $vars['smwgNamespace'] ) ) {
+ $vars['smwgNamespace'] = parse_url( $vars['wgServer'], PHP_URL_HOST );
+ }
+
+ foreach ( $vars['smwgResourceLoaderDefFiles'] as $key => $file ) {
+ if ( is_readable( $file ) ) {
+ $vars['wgResourceModules'] = array_merge( $vars['wgResourceModules'], include ( $file ) );
+ }
+ }
+ }
+
+ private function initConnectionProviders() {
+
+ $mwCollaboratorFactory = $this->applicationFactory->newMwCollaboratorFactory();
+ $connectionManager = $this->applicationFactory->getConnectionManager();
+
+ $connectionManager->registerConnectionProvider(
+ DB_MASTER,
+ $mwCollaboratorFactory->newLoadBalancerConnectionProvider( DB_MASTER )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ DB_SLAVE,
+ $mwCollaboratorFactory->newLoadBalancerConnectionProvider( DB_SLAVE )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ 'mw.db',
+ $mwCollaboratorFactory->newConnectionProvider( 'mw.db' )
+ );
+
+ // Connection can be used to redirect queries to another DB cluster
+ $connectionManager->registerConnectionProvider(
+ 'mw.db.queryengine',
+ $mwCollaboratorFactory->newConnectionProvider( 'mw.db.queryengine' )
+ );
+
+ $connectionManager->registerConnectionProvider(
+ 'elastic',
+ $this->applicationFactory->singleton( 'ElasticFactory' )->newConnectionProvider()
+ );
+ }
+
+ private function initMessageCallbackHandler() {
+
+ Message::registerCallbackHandler( Message::TEXT, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ return call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language )->text();
+ } );
+
+ Message::registerCallbackHandler( Message::ESCAPED, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ return call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language )->escaped();
+ } );
+
+ Message::registerCallbackHandler( Message::PARSE, function( $arguments, $language ) {
+
+ if ( $language === Message::CONTENT_LANGUAGE ) {
+ $language = Localizer::getInstance()->getContentLanguage();
+ }
+
+ if ( $language === Message::USER_LANGUAGE ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ }
+
+ $message = call_user_func_array( 'wfMessage', $arguments )->inLanguage( $language );
+
+ // 1.27+
+ // [GlobalTitleFail] MessageCache::parse called by ...
+ // Message::parseText/MessageCache::parse with no title set.
+ //
+ // Message::setInterfaceMessageFlag "... used to restore the flag
+ // after setting a language"
+ return $message->setInterfaceMessageFlag( true )->title( $GLOBALS['wgTitle'] )->parse();
+ } );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgJobClasses
+ */
+ private function registerJobClasses( &$vars ) {
+
+ $jobClasses = [
+
+ 'smw.update' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'smw.refresh' => 'SMW\MediaWiki\Jobs\RefreshJob',
+ 'smw.updateDispatcher' => 'SMW\MediaWiki\Jobs\UpdateDispatcherJob',
+ 'smw.parserCachePurge' => 'SMW\MediaWiki\Jobs\ParserCachePurgeJob',
+ 'smw.fulltextSearchTableUpdate' => 'SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob',
+ 'smw.entityIdDisposer' => 'SMW\MediaWiki\Jobs\EntityIdDisposerJob',
+ 'smw.propertyStatisticsRebuild' => 'SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob',
+ 'smw.fulltextSearchTableRebuild' => 'SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob',
+ 'smw.changePropagationDispatch' => 'SMW\MediaWiki\Jobs\ChangePropagationDispatchJob',
+ 'smw.changePropagationUpdate' => 'SMW\MediaWiki\Jobs\ChangePropagationUpdateJob',
+ 'smw.changePropagationClassUpdate' => 'SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob',
+ 'smw.elasticIndexerRecovery' => 'SMW\Elastic\Indexer\IndexerRecoveryJob',
+ 'smw.elasticFileIngest' => 'SMW\Elastic\Indexer\FileIngestJob',
+
+ // Legacy 3.0-
+ 'SMW\UpdateJob' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'SMW\RefreshJob' => 'SMW\MediaWiki\Jobs\RefreshJob',
+ 'SMW\UpdateDispatcherJob' => 'SMW\MediaWiki\Jobs\UpdateDispatcherJob',
+ 'SMW\ParserCachePurgeJob' => 'SMW\MediaWiki\Jobs\ParserCachePurgeJob',
+ 'SMW\FulltextSearchTableUpdateJob' => 'SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob',
+ 'SMW\EntityIdDisposerJob' => 'SMW\MediaWiki\Jobs\EntityIdDisposerJob',
+ 'SMW\PropertyStatisticsRebuildJob' => 'SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob',
+ 'SMW\FulltextSearchTableRebuildJob' => 'SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob',
+ 'SMW\ChangePropagationDispatchJob' => 'SMW\MediaWiki\Jobs\ChangePropagationDispatchJob',
+ 'SMW\ChangePropagationUpdateJob' => 'SMW\MediaWiki\Jobs\ChangePropagationUpdateJob',
+ 'SMW\ChangePropagationClassUpdateJob' => 'SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob',
+
+ // Legacy 2.0-
+ 'SMWUpdateJob' => 'SMW\MediaWiki\Jobs\UpdateJob',
+ 'SMWRefreshJob' => 'SMW\MediaWiki\Jobs\RefreshJob'
+ ];
+
+ foreach ( $jobClasses as $job => $class ) {
+ $vars['wgJobClasses'][$job] = $class;
+ }
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgAvailableRights
+ * @see https://www.mediawiki.org/wiki/Manual:$wgGroupPermissions
+ */
+ private function registerPermissions( &$vars ) {
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ $rights = [
+ 'smw-admin' => [
+ 'sysop',
+ 'smwadministrator'
+ ],
+ 'smw-patternedit' => [
+ 'smwcurator'
+ ],
+ 'smw-schemaedit' => [
+ 'smwcurator'
+ ],
+ 'smw-pageedit' => [
+ 'smwcurator'
+ ],
+ // 'smw-watchlist' => [
+ // 'smwcurator'
+ // ],
+ ];
+
+ foreach ( $rights as $right => $roles ) {
+
+ // Rights
+ $vars['wgAvailableRights'][] = $right;
+
+ // User group rights
+ foreach ( $roles as $role ) {
+ if ( !isset( $vars['wgGroupPermissions'][$role][$right] ) ) {
+ $vars['wgGroupPermissions'][$role][$right] = true;
+ }
+ }
+ }
+
+ // Add an additional protection level restricting edit/move/etc
+ if ( ( $editProtectionRight = $this->applicationFactory->getSettings()->get( 'smwgEditProtectionRight' ) ) !== false ) {
+ $vars['wgRestrictionLevels'][] = $editProtectionRight;
+ }
+ }
+
+ private function registerParamDefinitions( &$vars ) {
+ $vars['wgParamDefinitions']['smwformat'] = [
+ 'definition'=> 'SMWParamFormat',
+ ];
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgFooterIcons
+ */
+ private function registerFooterIcon( &$vars, $path ) {
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ if( isset( $vars['wgFooterIcons']['poweredby']['semanticmediawiki'] ) ) {
+ return;
+ }
+
+ $src = '';
+
+ if ( is_file( $path . '/res/DataURI.php' ) && ( $dataURI = include $path . '/res/DataURI.php' ) !== [] ) {
+ $src = $dataURI['footer'];
+ }
+
+ $vars['wgFooterIcons']['poweredby']['semanticmediawiki'] = [
+ 'src' => $src,
+ 'url' => 'https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki',
+ 'alt' => 'Powered by Semantic MediaWiki'
+ ];
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgHooks
+ *
+ * @note $wgHooks contains a list of hooks which specifies for every event an
+ * array of functions to be called.
+ */
+ private function registerHooks( &$vars, $directory ) {
+
+ $hookRegistry = new HookRegistry( $vars, $directory );
+ $hookRegistry->register();
+
+ if ( !$this->applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ // Old-style registration
+ $vars['wgHooks']['AdminLinks'][] = 'SMWExternalHooks::addToAdminLinks';
+ $vars['wgHooks']['PageSchemasRegisterHandlers'][] = 'SMWExternalHooks::onPageSchemasRegistration';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php b/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php
new file mode 100644
index 00000000..f1d129a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/Subobject.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use SMW\Exception\SubSemanticDataException;
+use SMWContainerSemanticData;
+use SMWDataValue;
+use SMWDIContainer;
+use Title;
+
+/**
+ * @see http://www.semantic-mediawiki.org/wiki/Help:Subobject
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Subobject {
+
+ /**
+ * @var Title
+ */
+ protected $title;
+
+ /**
+ * @var SMWContainerSemanticData
+ */
+ protected $semanticData;
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ */
+ public function __construct( Title $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * Returns the Title object
+ *
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->getSemanticData()->getSubject();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getSubobjectId() {
+ return $this->getSemanticData()->getSubject()->getSubobjectName();
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array|string $error
+ */
+ public function addError( $error ) {
+
+ if ( is_string( $error ) ) {
+ $error = [ md5( $error ) => $error ];
+ }
+
+ // Preserve the keys, avoid using array_merge to avert a possible
+ // Fatal error: Allowed memory size of ... bytes exhausted ... Subobject.php on line 89
+ $this->errors += $error;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $identifier
+ *
+ * @return self
+ * @throws InvalidArgumentException
+ */
+ public function setEmptyContainerForId( $identifier ) {
+
+ if ( $identifier === '' ) {
+ throw new InvalidArgumentException( 'Expected a valid (non-empty) indentifier' );
+ }
+
+ $subWikiPage = new DIWikiPage(
+ $this->title->getDBkey(),
+ $this->title->getNamespace(),
+ $this->title->getInterwiki(),
+ $identifier
+ );
+
+ $this->semanticData = new SMWContainerSemanticData( $subWikiPage );
+
+ return $this;
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ public function setSemanticData( $identifier ) {
+ $this->setEmptyContainerForId( $identifier );
+ }
+
+ /**
+ * Returns semantic data container for a subobject
+ *
+ * @since 1.9
+ *
+ * @return SMWContainerSemanticData
+ */
+ public function getSemanticData() {
+
+ if ( !( $this->semanticData instanceof SMWContainerSemanticData ) ) {
+ throw new SubSemanticDataException( 'The semantic data container is not initialized' );
+ }
+
+ return $this->semanticData;
+ }
+
+ /**
+ * Returns the property data item for the subobject
+ *
+ * @since 1.9
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return new DIProperty( DIProperty::TYPE_SUBOBJECT );
+ }
+
+ /**
+ * Returns the container data item for the subobject
+ *
+ * @since 1.9
+ *
+ * @return SMWDIContainer
+ */
+ public function getContainer() {
+ return new SMWDIContainer( $this->getSemanticData() );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param DataValue $dataValue
+ *
+ * @throws SubSemanticDataException
+ */
+ public function addDataValue( SMWDataValue $dataValue ) {
+
+ if ( !( $this->semanticData instanceof SMWContainerSemanticData ) ) {
+ throw new SubSemanticDataException( 'The semantic data container is not initialized' );
+ }
+
+ $this->semanticData->addDataValue( $dataValue );
+ $this->addError( $this->semanticData->getErrors() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php
new file mode 100644
index 00000000..ea6cb70c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/DIConcept.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW;
+
+use SMWDataItem;
+
+/**
+ * This class implements Concept data items.
+ *
+ * @note These special data items for storing concept declaration data in SMW
+ * should vanish at some point since Container values could encode this data
+ * just as well.
+ *
+ * @since 1.6
+ *
+ * @ingroup SMWDataItems
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class DIConcept extends \SMWDataItem {
+
+ /**
+ * Query string for this concept. Possibly long.
+ * @var string
+ */
+ protected $m_concept;
+ /**
+ * Documentation for this concept. Possibly long.
+ * @var string
+ */
+ protected $m_docu;
+ /**
+ * Flags of query features.
+ * @var integer
+ */
+ protected $m_features;
+ /**
+ * Size of the query.
+ * @var integer
+ */
+ protected $m_size;
+ /**
+ * Depth of the query.
+ * @var integer
+ */
+ protected $m_depth;
+
+ /**
+ * Status
+ * @var integer
+ */
+ protected $cacheStatus;
+
+ /**
+ * Date
+ * @var integer
+ */
+ protected $cacheDate;
+
+ /**
+ * Count
+ * @var integer
+ */
+ protected $cacheCount;
+
+ /**
+ * @param string $concept the concept query string
+ * @param string $docu user documentation
+ * @param integer $queryefeatures flags about query features
+ * @param integer $size concept query size
+ * @param integer $depth concept query depth
+ */
+ public function __construct( $concept, $docu, $queryfeatures, $size, $depth ) {
+ $this->m_concept = $concept;
+ $this->m_docu = $docu;
+ $this->m_features = $queryfeatures;
+ $this->m_size = $size;
+ $this->m_depth = $depth;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_CONCEPT;
+ }
+
+ public function getConceptQuery() {
+ return $this->m_concept;
+ }
+
+ public function getDocumentation() {
+ return $this->m_docu;
+ }
+
+ public function getQueryFeatures() {
+ return $this->m_features;
+ }
+
+ public function getSize() {
+ return $this->m_size;
+ }
+
+ public function getDepth() {
+ return $this->m_depth;
+ }
+
+ public function getSortKey() {
+ return $this->m_docu;
+ }
+
+ public function getSerialization() {
+ return serialize( $this );
+ }
+
+ /**
+ * Sets cache status
+ *
+ * @since 1.9
+ *
+ * @param string
+ */
+ public function setCacheStatus( $status ) {
+ $this->cacheStatus = $status;
+ }
+
+ /**
+ * Sets cache date
+ *
+ * @since 1.9
+ *
+ * @param string
+ */
+ public function setCacheDate( $date ) {
+ $this->cacheDate = $date;
+ }
+
+ /**
+ * Sets cache count
+ *
+ * @since 1.9
+ *
+ * @param int
+ */
+ public function setCacheCount( $count ) {
+ $this->cacheCount = $count;
+ }
+
+ /**
+ * Returns cache status
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheStatus() {
+ return $this->cacheStatus;
+ }
+
+ /**
+ * Returns cache date
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheDate() {
+ return $this->cacheDate;
+ }
+
+ /**
+ * Returns cache count
+ *
+ * @since 1.9
+ *
+ * @return int
+ */
+ public function getCacheCount() {
+ return $this->cacheCount;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return DIConcept
+ */
+ public static function doUnserialize( $serialization ) {
+ $result = unserialize( $serialization );
+ if ( $result === false ) {
+ throw new DataItemException( "Unserialization failed." );
+ }
+ return $result;
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_CONCEPT ) {
+ return false;
+ }
+ return $di->getSerialization() === $this->getSerialization();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php
new file mode 100644
index 00000000..fee4d0ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Blob.php
@@ -0,0 +1,77 @@
+<?php
+
+use Onoi\Tesa\Normalizer;
+
+/**
+ * @ingroup SMWDataItems
+ */
+
+/**
+ * This class implements blob (long string) data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIBlob extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var string
+ */
+ protected $m_string;
+
+ public function __construct( $string ) {
+ $this->m_string = trim( $string );
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_BLOB;
+ }
+
+ public function getString() {
+ return $this->m_string;
+ }
+
+ public static function normalize( $text ) {
+ return Normalizer::convertDoubleWidth(
+ Normalizer::applyTransliteration(
+ Normalizer::toLowercase( $text )
+ )
+ );
+ }
+
+ public function getSortKey() {
+ return $this->m_string;
+ }
+
+ /**
+ * @see SMWDataItem::getSortKeyDataItem()
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ return $this;
+ }
+
+ public function getSerialization() {
+ return $this->m_string;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIBlob
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDIBlob( $serialization );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( !( $di instanceof SMWDIBlob ) ) {
+ return false;
+ }
+
+ return $di->getString() === $this->m_string;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php
new file mode 100644
index 00000000..8bddf9fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Bool.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements Boolean data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIBoolean extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var bool
+ */
+ protected $m_boolean;
+
+ public function __construct( $boolean ) {
+ if ( !is_bool( $boolean ) ) {
+ throw new DataItemException( "Initialization value '$boolean' is not a boolean." );
+ }
+
+ $this->m_boolean = ( $boolean == true );
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_BOOLEAN;
+ }
+
+ public function getBoolean() {
+ return $this->m_boolean;
+ }
+
+ public function getSerialization() {
+ return $this->m_boolean ? 't' : 'f';
+ }
+
+ public function getSortKey() {
+ return $this->m_boolean ? 1 : 0;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIBoolean
+ */
+ public static function doUnserialize( $serialization ) {
+ if ( $serialization == 't' ) {
+ return new SMWDIBoolean( true );
+ } elseif ( $serialization == 'f' ) {
+ return new SMWDIBoolean( false );
+ } else {
+ throw new DataItemException( "Boolean data item unserialised from illegal value '$serialization'" );
+ }
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_BOOLEAN ) {
+ return false;
+ }
+ return $di->getBoolean() === $this->m_boolean;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php
new file mode 100644
index 00000000..f7f230e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Container.php
@@ -0,0 +1,141 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\DIProperty;
+use SMW\Exception\DataItemException;
+use SMWDIBlob as DIBlob;
+
+/**
+ * This class implements container data items that can store SMWSemanticData
+ * objects. Containers are not dataitems in the proper sense: they do not
+ * represent a single, opaque value that can be assigned to a property. Rather,
+ * a container represents a "subobject" with a number of property-value
+ * assignments. When a container is stored, these individual data assignments
+ * are stored -- the data managed by SMW never contains any "container", just
+ * individual property assignments for the subobject. Likewise, when a container
+ * is used in search, it is interpreted as a patterns of possible property
+ * assignments, and this pattern is searched for.
+ *
+ * The data encapsulated in a container data item is essentially an
+ * SMWSemanticData object of class SMWContainerSemanticData. This class allows
+ * the subject to be kept anonymous if not known (if no context page is
+ * available for finding a suitable subobject name). See the repsective
+ * documentation for details.
+ *
+ * Being a mere placeholder/template for other data, an SMWDIContainer is not
+ * immutable as the other basic data items. New property-value pairs can always
+ * be added to the internal SMWContainerSemanticData.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIContainer extends SMWDataItem {
+
+ /**
+ * Internal value.
+ *
+ * @var SMWSemanticData
+ */
+ protected $m_semanticData;
+
+ /**
+ * Constructor. The given SMWContainerSemanticData object will be owned
+ * by the constructed object afterwards, and in particular will not
+ * allow further changes.
+ *
+ * @param $semanticData SMWContainerSemanticData
+ */
+ public function __construct( SMWContainerSemanticData $semanticData ) {
+ $this->m_semanticData = $semanticData;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_CONTAINER;
+ }
+
+ public function getSemanticData() {
+ return $this->m_semanticData;
+ }
+
+ public function getSortKey() {
+ return '';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $sortKey
+ */
+ public function setSortKey( $sortKey ) {
+ $this->m_semanticData->addPropertyObjectValue(
+ new DIProperty( '_SKEY' ),
+ new DIBlob( $this->m_semanticData->getSubject()->getSortKey() . '#' . $sortKey )
+ );
+ }
+
+ public function getSerialization() {
+ return serialize( $this->m_semanticData );
+ }
+
+ /**
+ * Get a hash string for this data item.
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ $hash = $this->getValueHash( $this->m_semanticData );
+ sort( $hash );
+
+ return md5( implode( '#', $hash ) );
+
+ // We want a value hash, not an entity hash!!
+ // return $this->m_semanticData->getHash();
+ }
+
+ private function getValueHash( $semanticData ) {
+
+ $hash = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $hash[] = $property->getKey();
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $di ) {
+ $hash[] = $di->getHash();
+ }
+ }
+
+ foreach ( $semanticData->getSubSemanticData() as $data ) {
+ $hash[] = $this->getValueHash( $data );
+ }
+
+ return $hash;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ *
+ * @return SMWDIContainer
+ */
+ public static function doUnserialize( $serialization ) {
+ /// TODO May issue an E_NOTICE when problems occur; catch this
+ $data = unserialize( $serialization );
+ if ( !( $data instanceof SMWContainerSemanticData ) ) {
+ throw new DataItemException( "Could not unserialize SMWDIContainer from the given string." );
+ }
+ return new SMWDIContainer( $data );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_CONTAINER ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php
new file mode 100644
index 00000000..099b306a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Error.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+/**
+ * This class implements error list data items. These data items are used to
+ * pass around lists of error messages within the application. They are not
+ * meant to be stored or exported, but they can be useful to a user.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIError extends SMWDataItem {
+
+ /**
+ * List of error messages. Should always be safe for HTML.
+ * @var array of strings
+ */
+ protected $m_errors;
+
+ /**
+ * @var string
+ */
+ private $userValue;
+
+ public function __construct( $errors, $userValue = '' ) {
+ $this->m_errors = $errors;
+ $this->userValue = $userValue;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_ERROR;
+ }
+
+ public function getErrors() {
+ return $this->m_errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getUserValue() {
+ return $this->userValue;
+ }
+
+ public function getSortKey() {
+ return 'error';
+ }
+
+ public function getString() {
+ return $this->getSerialization();
+ }
+
+ public function getSerialization() {
+ return serialize( $this->m_errors );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @todo Be more careful with unserialization. It can create E_NOTICEs.
+ * @return SMWDIError
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDIError( unserialize( $serialization ) );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_ERROR ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php
new file mode 100644
index 00000000..208366e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_GeoCoord.php
@@ -0,0 +1,185 @@
+<?php
+
+use SMW\Exception\DataItemException;
+
+/**
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWDIGeoCoord extends SMWDataItem {
+
+ /**
+ * @var float
+ */
+ private $latitude;
+
+ /**
+ * @var float
+ */
+ private $longitude;
+
+ /**
+ * @var float|null
+ */
+ private $altitude = null;
+
+ /**
+ * Takes a latitude and longitude, and optionally an altitude. These can be provided in 2 forms:
+ * * An associative array with lat, lon and alt keys
+ * * Lat, lon and alt arguments
+ *
+ * The second way to provide the arguments, as well as the altitude argument, where introduced in SMW 1.7.
+ */
+ public function __construct() {
+ $args = func_get_args();
+
+ $count = count( $args );
+
+ if ( $count === 1 && is_array( $args[0] ) ) {
+ if ( array_key_exists( 'lat', $args[0] ) && array_key_exists( 'lon', $args[0] ) ) {
+ $this->setLatitude( $args[0]['lat'] );
+ $this->setLongitude( $args[0]['lon'] );
+
+ if ( array_key_exists( 'alt', $args[0] ) ) {
+ $this->altitude = (float)$args[0]['alt'];
+ }
+ }
+ else {
+ throw new DataItemException( 'Invalid coordinate data passed to the SMWDIGeoCoord constructor' );
+ }
+ }
+ elseif ( $count === 2 || $count === 3 ) {
+ $this->setLatitude( $args[0] );
+ $this->setLongitude( $args[1] );
+
+ if ( $count === 3 ) {
+ $this->altitude = (float)$args[2];
+ }
+ }
+ else {
+ throw new DataItemException( 'Invalid coordinate data passed to the SMWDIGeoCoord constructor' );
+ }
+ }
+
+ private function setLatitude( $latitude ) {
+ if ( is_int( $latitude ) ) {
+ $latitude = (float)$latitude;
+ }
+
+ if ( !is_float( $latitude ) ) {
+ throw new DataItemException( '$latitude should be a float' );
+ }
+
+ $this->latitude = $latitude;
+ }
+
+ private function setLongitude( $longitude ) {
+ if ( is_int( $longitude ) ) {
+ $longitude = (float)$longitude;
+ }
+
+ if ( !is_float( $longitude ) ) {
+ throw new DataItemException( '$longitude should be a float' );
+ }
+
+ $this->longitude = $longitude;
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getDIType()
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_GEO;
+ }
+
+ /**
+ * Returns the coordinate set as an array with lat and long (and alt) keys
+ * pointing to float values.
+ *
+ * @return array
+ */
+ public function getCoordinateSet() {
+ $coords = [ 'lat' => $this->latitude, 'lon' => $this->longitude ];
+
+ if ( !is_null( $this->altitude ) ) {
+ $coords['alt'] = $this->altitude;
+ }
+
+ return $coords;
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getSortKey()
+ */
+ public function getSortKey() {
+ return $this->latitude . ',' . $this->longitude . ( $this->altitude !== null ? ','. $this->altitude : '' );
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWDataItem::getSerialization()
+ */
+ public function getSerialization() {
+ return implode( ',', $this->getCoordinateSet() );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @note PHP can convert any string to some number, so we do not do
+ * validation here (because this would require less efficient parsing).
+ *
+ * @param string $serialization
+ *
+ * @return self
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( ',', $serialization );
+ $count = count( $parts );
+
+ if ( $count !== 2 && $count !== 3 ) {
+ throw new DataItemException( 'Unserialization of coordinates failed' );
+ }
+
+ $coords = [ 'lat' => (float)$parts[0], 'lon' => (float)$parts[1] ];
+
+ if ( $count === 3 ) {
+ $coords['alt'] = (float)$parts[2];
+ }
+
+ return new self( $coords );
+ }
+
+ /**
+ * @return float
+ */
+ public function getLatitude() {
+ return $this->latitude;
+ }
+
+ /**
+ * @return float
+ */
+ public function getLongitude() {
+ return $this->longitude;
+ }
+
+ /**
+ * Returns the altitude if set, null otherwise.
+ *
+ * @return float|null
+ */
+ public function getAltitude() {
+ return $this->altitude;
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_GEO ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php
new file mode 100644
index 00000000..7105f31c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Number.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @ingroup SMWDataItems
+ */
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements number data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDINumber extends SMWDataItem {
+
+ /**
+ * Internal value.
+ * @var float|int
+ */
+ protected $m_number;
+
+ public function __construct( $number ) {
+ if ( !is_numeric( $number ) ) {
+ throw new DataItemException( "Initialization value '$number' is not a number." );
+ }
+ $this->m_number = $number;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_NUMBER;
+ }
+
+ public function getNumber() {
+ return $this->m_number;
+ }
+
+ public function getSortKey() {
+ return $this->m_number;
+ }
+
+ /**
+ * @see SMWDataItem::getSortKeyDataItem()
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ return $this;
+ }
+
+ public function getSerialization() {
+ return strval( $this->m_number );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @note PHP can convert any string to some number, so we do not do
+ * validation here (because this would require less efficient parsing).
+ * @return SMWDINumber
+ */
+ public static function doUnserialize( $serialization ) {
+ return new SMWDINumber( floatval( $serialization ) );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_NUMBER ) {
+ return false;
+ }
+
+ return $di->getNumber() === $this->m_number;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php
new file mode 100644
index 00000000..06e076df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Property.php
@@ -0,0 +1,509 @@
+<?php
+
+namespace SMW;
+
+use RuntimeException;
+use SMW\Exception\DataTypeLookupException;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\Exception\PropertyLabelNotResolvedException;
+use SMWDataItem;
+use SMWDIUri;
+
+/**
+ * This class implements Property item
+ *
+ * @note PropertyRegistry class manages global registrations of predefined
+ * (built-in) properties, and maintains an association of property IDs, localized
+ * labels, and aliases.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class DIProperty extends SMWDataItem {
+
+ /**
+ * @see PropertyRegistry::registerPredefinedProperties
+ */
+ const TYPE_SUBOBJECT = '_SOBJ';
+ const TYPE_ERROR = '_ERRP';
+ const TYPE_CATEGORY = '_INST';
+ const TYPE_SUBCATEGORY = '_SUBC';
+ const TYPE_SORTKEY = '_SKEY';
+ const TYPE_MODIFICATION_DATE = '_MDAT';
+ const TYPE_CREATION_DATE = '_CDAT';
+ const TYPE_LAST_EDITOR = '_LEDT';
+ const TYPE_NEW_PAGE = '_NEWP';
+ const TYPE_HAS_TYPE = '_TYPE';
+ const TYPE_CONVERSION = '_CONV';
+ const TYPE_ASKQUERY = '_ASK';
+ const TYPE_MEDIA = '_MEDIA';
+ const TYPE_MIME = '_MIME';
+ const TYPE_DISPLAYTITLE = '_DTITLE';
+
+ /**
+ * Change propagation
+ */
+ const TYPE_CHANGE_PROP = '_CHGPRO';
+
+ /**
+ * Either an internal SMW property key (starting with "_") or the DB
+ * key of a property page in the wiki.
+ * @var string
+ */
+ private $m_key;
+
+ /**
+ * Whether to take the inverse of this property or not.
+ * @var boolean
+ */
+ private $m_inverse;
+
+ /**
+ * @var string
+ */
+ private $propertyValueType;
+
+ /**
+ * Interwiki prefix for when a property represents a non-local entity
+ *
+ * @var string
+ */
+ private $interwiki = '';
+
+ /**
+ * Initialise a property. This constructor checks that keys of
+ * predefined properties do really exist (in the current configuration
+ * of the wiki). No check is performed to see if a user label is in
+ * fact the label or alias of a predefined property. If this should be
+ * done, the function self::newFromUserLabel() can be used.
+ *
+ * @param $key string key for the property (internal SMW key or wikipage DB key)
+ * @param $inverse boolean states if the inverse of the property is constructed
+ */
+ public function __construct( $key, $inverse = false ) {
+
+ if ( ( $key === '' ) || ( $key{0} == '-' ) ) {
+ throw new PropertyLabelNotResolvedException( "Illegal property key \"$key\"." );
+ }
+
+ if ( $key{0} == '_' ) {
+ if ( !PropertyRegistry::getInstance()->isRegistered( $key ) ) {
+ throw new PredefinedPropertyLabelMismatchException( "There is no predefined property with \"$key\"." );
+ }
+ }
+
+ $this->m_key = $key;
+ $this->m_inverse = $inverse;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_PROPERTY;
+ }
+
+ /**
+ * @return string
+ */
+ public function getKey() {
+ return $this->m_key;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isInverse() {
+ return $this->m_inverse;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSortKey() {
+ return $this->m_key;
+ }
+
+ /**
+ * Specifies whether values of this property should be shown in the
+ * Factbox. A property may wish to prevent this if either
+ * (1) its information is really dull, e.g. being a mere copy of
+ * information that is obvious from other things that are shown, or
+ * (2) the property is set in a hook after parsing, so that it is not
+ * reliably available when Factboxes are displayed. If a property is
+ * internal so it should never be observed by users, then it is better
+ * to just not associate any translated label with it, so it never
+ * appears anywhere.
+ *
+ * Examples of properties that are not shown include Modification date
+ * (not available in time), and Has improper value for (errors are
+ * shown directly on the page anyway).
+ *
+ * @return boolean
+ */
+ public function isShown() {
+
+ if ( $this->isUserDefined() ) {
+ return true;
+ }
+
+ return PropertyRegistry::getInstance()->isVisible( $this->m_key );
+ }
+
+ /**
+ * Return true if this is a usual wiki property that is defined by a
+ * wiki page, and not a property that is pre-defined in the wiki.
+ *
+ * @return boolean
+ */
+ public function isUserDefined() {
+ return $this->m_key{0} != '_';
+ }
+
+ /**
+ * Whether a user can freely use this property for value annotation or
+ * not.
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isUserAnnotable() {
+
+ // A user defined property is generally assumed to be unrestricted for
+ // usage
+ if ( $this->isUserDefined() ) {
+ return true;
+ }
+
+ return PropertyRegistry::getInstance()->isAnnotable( $this->m_key );
+ }
+
+ /**
+ * Find a user-readable label for this property, or return '' if it is
+ * a predefined property that has no label. For inverse properties, the
+ * label starts with a "-".
+ *
+ * @return string
+ */
+ public function getLabel() {
+ $prefix = $this->m_inverse ? '-' : '';
+
+ if ( $this->isUserDefined() ) {
+ return $prefix . str_replace( '_', ' ', $this->m_key );
+ }
+
+ return $prefix . PropertyRegistry::getInstance()->findPropertyLabelById( $this->m_key );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCanonicalLabel() {
+ $prefix = $this->m_inverse ? '-' : '';
+
+ if ( $this->isUserDefined() ) {
+ return $prefix . str_replace( '_', ' ', $this->m_key );
+ }
+
+ return $prefix . PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
+ }
+
+ /**
+ * Borrowing the skos:prefLabel definition where a preferred label is expected
+ * to have only one label per given language (skos:altLabel can have many
+ * alternative labels)
+ *
+ * An empty string signals that no preferred label is available in the current
+ * user language.
+ *
+ * @since 2.5
+ *
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getPreferredLabel( $languageCode = '' ) {
+
+ $label = PropertyRegistry::getInstance()->findPreferredPropertyLabelFromIdByLanguageCode(
+ $this->m_key,
+ $languageCode
+ );
+
+ if ( $label !== '' ) {
+ return ( $this->m_inverse ? '-' : '' ) . $label;
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $interwiki
+ */
+ public function setInterwiki( $interwiki ) {
+ $this->interwiki = $interwiki;
+ }
+
+ /**
+ * Get an object of type DIWikiPage that represents the page which
+ * relates to this property, or null if no such page exists. The latter
+ * can happen for special properties without user-readable label.
+ *
+ * It is possible to construct subobjects of the property's wikipage by
+ * providing an optional subobject name.
+ *
+ * @param string $subobjectName
+ * @return DIWikiPage|null
+ */
+ public function getDiWikiPage( $subobjectName = '' ) {
+
+ $dbkey = $this->m_key;
+
+ if ( !$this->isUserDefined() ) {
+ $dbkey = $this->getLabel();
+ }
+
+ return $this->newDIWikiPage( $dbkey, $subobjectName );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $subobjectName
+ *
+ * @return DIWikiPage|null
+ */
+ public function getCanonicalDiWikiPage( $subobjectName = '' ) {
+
+ if ( $this->isUserDefined() ) {
+ $dbkey = $this->m_key;
+ } elseif ( $this->m_key === $this->findPropertyTypeID() ) {
+ // If _dat as property [[Date::...]] refers directly to its _dat type
+ // then use the en-label as canonical representation
+ $dbkey = PropertyRegistry::getInstance()->findPropertyLabelFromIdByLanguageCode( $this->m_key, 'en' );
+ } else {
+ $dbkey = PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key );
+ }
+
+ return $this->newDIWikiPage( $dbkey, $subobjectName );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DIProperty
+ */
+ public function getRedirectTarget() {
+
+ if ( $this->m_inverse ) {
+ return $this;
+ }
+
+ return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $this );
+ }
+
+ /**
+ * @deprecated since 3.0, use DIProperty::setPropertyValueType
+ */
+ public function setPropertyTypeId( $valueType ) {
+ return $this->setPropertyValueType( $valueType );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $valueType
+ *
+ * @return self
+ * @throws DataTypeLookupException
+ * @throws RuntimeException
+ */
+ public function setPropertyValueType( $valueType ) {
+
+ if ( !DataTypeRegistry::getInstance()->isRegistered( $valueType ) ) {
+ throw new DataTypeLookupException( "{$valueType} is an unknown type id" );
+ }
+
+ if ( $this->isUserDefined() && $this->propertyValueType === null ) {
+ $this->propertyValueType = $valueType;
+ return $this;
+ }
+
+ if ( !$this->isUserDefined() && $valueType === PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key ) ) {
+ $this->propertyValueType = $valueType;
+ return $this;
+ }
+
+ throw new RuntimeException( 'DataType cannot be altered for a predefined property' );
+ }
+
+ /**
+ * @deprecated since 3.0, use DIProperty::findPropertyValueType
+ */
+ public function findPropertyTypeId() {
+ return $this->findPropertyValueType();
+ }
+
+ /**
+ * Find the property's type ID, either by looking up its predefined ID
+ * (if any) or by retrieving the relevant information from the store.
+ * If no type is stored for a user defined property, the global default
+ * type will be used.
+ *
+ * @since 3.0
+ *
+ * @return string type ID
+ */
+ public function findPropertyValueType() {
+
+ if ( isset( $this->propertyValueType ) ) {
+ return $this->propertyValueType;
+ }
+
+ if ( !$this->isUserDefined() ) {
+ return $this->propertyValueType = PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key );
+ }
+
+ $diWikiPage = new DIWikiPage( $this->getKey(), SMW_NS_PROPERTY, $this->interwiki );
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $typearray = $applicationFactory->getPropertySpecificationLookup()->getSpecification(
+ $this,
+ new self( '_TYPE' )
+ );
+
+ if ( is_array( $typearray ) && count( $typearray ) >= 1 ) { // some types given, pick one (hopefully unique)
+ $typeDataItem = reset( $typearray );
+
+ if ( $typeDataItem instanceof SMWDIUri ) {
+ $this->propertyValueType = $typeDataItem->getFragment();
+ } else {
+ // This is important. If a page has an invalid assignment to "has type", no
+ // value will be stored, so the elseif case below occurs. But if the value
+ // is retrieved within the same run, then the error value for "has type" is
+ // cached and thus this case occurs. This is why it is important to tolerate
+ // this case -- it is not necessarily a DB error.
+ $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' );
+ }
+ } else { // no type given
+ $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' );
+ }
+
+ return $this->propertyValueType;
+ }
+
+ /**
+ * @see DataItem::getSerialization
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ return ( $this->m_inverse ? '-' : '' ) . $this->m_key;
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ *
+ * @param string $serialization
+ *
+ * @return DIProperty
+ */
+ public static function doUnserialize( $serialization ) {
+ $inverse = false;
+
+ if ( $serialization{0} == '-' ) {
+ $serialization = substr( $serialization, 1 );
+ $inverse = true;
+ }
+
+ return new self( $serialization, $inverse );
+ }
+
+ /**
+ * @param SMWDataItem $di
+ *
+ * @return boolean
+ */
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_PROPERTY ) {
+ return false;
+ }
+
+ return $di->getKey() === $this->m_key;
+ }
+
+ /**
+ * Construct a property from a user-supplied label. The main difference
+ * to the normal constructor of DIProperty is that it is checked
+ * whether the label refers to a known predefined property.
+ * Note that this function only gives access to the registry data that
+ * DIProperty stores, but does not do further parsing of user input.
+ *
+ * To process wiki input, SMWPropertyValue should be used.
+ *
+ * @param $label string label for the property
+ * @param $inverse boolean states if the inverse of the property is constructed
+ *
+ * @return DIProperty object
+ */
+ public static function newFromUserLabel( $label, $inverse = false, $languageCode = false ) {
+
+ if ( $label !== '' && $label{0} == '-' ) {
+ $label = substr( $label, 1 );
+ $inverse = true;
+ }
+
+ // Special handling for when the user value contains a @LCODE marker
+ if ( ( $annotatedLanguageCode = Localizer::getAnnotatedLanguageCodeFrom( $label ) ) !== false ) {
+ $languageCode = $annotatedLanguageCode;
+ }
+
+ $id = false;
+ $label = str_replace( '_', ' ', $label );
+
+ if ( $languageCode ) {
+ $id = PropertyRegistry::getInstance()->findPropertyIdFromLabelByLanguageCode(
+ $label,
+ $languageCode
+ );
+ }
+
+ if ( $id !== false ) {
+ return new self( $id, $inverse );
+ }
+
+ $id = PropertyRegistry::getInstance()->findPropertyIdByLabel(
+ $label
+ );
+
+ if ( $id === false ) {
+ return new self( str_replace( ' ', '_', $label ), $inverse );
+ }
+
+ return new self( $id, $inverse );
+ }
+
+ private function newDIWikiPage( $dbkey, $subobjectName ) {
+
+ // If an inverse marker is present just omit the marker so a normal
+ // property page link can be produced independent of its directionality
+ if ( $dbkey !== '' && $dbkey{0} == '-' ) {
+ $dbkey = substr( $dbkey, 1 );
+ }
+
+ try {
+ return new DIWikiPage( str_replace( ' ', '_', $dbkey ), SMW_NS_PROPERTY, $this->interwiki, $subobjectName );
+ } catch ( DataItemException $e ) {
+ return null;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php
new file mode 100644
index 00000000..00a473d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_Time.php
@@ -0,0 +1,609 @@
+<?php
+
+use SMW\DataValues\Time\CalendarModel;
+use SMW\DataValues\Time\JulianDay;
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements time data items.
+ * Such data items represent a unique point in time, given in either Julian or
+ * Gregorian notation (possibly proleptic), and a precision setting that states
+ * which of the components year, month, day, time were specified expicitly.
+ * Even when not specified, the data item always assumes default values for the
+ * missing parts, so the item really captures one point in time, no intervals.
+ * Times are always assumed to be in UTC.
+ *
+ * "Y0K issue": Neither the Gregorian nor the Julian calendar assume a year 0,
+ * i.e. the year 1 BC(E) was followed by 1 AD/CE. See
+ * http://en.wikipedia.org/wiki/Year_zero
+ * This implementation adheres to this convention and disallows year 0. The
+ * stored year numbers use positive numbers for CE and negative numbers for
+ * BCE. This is not just relevant for the question of how many years have
+ * (exactly) passed since a given date, but also for the location of leap
+ * years.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDITime extends SMWDataItem implements CalendarModel {
+
+ const PREC_Y = SMW_PREC_Y;
+ const PREC_YM = SMW_PREC_YM;
+ const PREC_YMD = SMW_PREC_YMD;
+ const PREC_YMDT = SMW_PREC_YMDT;
+
+ /**
+ * The year before which we do not accept anything but year numbers and
+ * largely discourage calendar models.
+ */
+ const PREHISTORY = -10000;
+
+ /**
+ * Maximal number of days in a given month.
+ * @var array
+ */
+ protected static $m_daysofmonths = [ 1 => 31, 2 => 29, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 ];
+
+ /**
+ * Precision SMWDITime::PREC_Y, SMWDITime::PREC_YM,
+ * SMWDITime::PREC_YMD, or SMWDITime::PREC_YMDT.
+ * @var integer
+ */
+ protected $m_precision;
+ /**
+ * Calendar model: SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN.
+ * @var integer
+ */
+ protected $m_model;
+ /**
+ * Number of year, possibly negative.
+ * @var integer
+ */
+ protected $m_year;
+ /**
+ * Number of month.
+ * @var integer
+ */
+ protected $m_month;
+ /**
+ * Number of day.
+ * @var integer
+ */
+ protected $m_day;
+ /**
+ * Hours of the day.
+ * @var integer
+ */
+ protected $m_hours;
+ /**
+ * Minutes of the hour.
+ * @var integer
+ */
+ protected $m_minutes;
+ /**
+ * Seconds of the minute.
+ * @var integer
+ */
+ protected $m_seconds;
+
+ /**
+ * @var integer
+ */
+ protected $timezone;
+
+ /**
+ * @var integer|null
+ */
+ protected $era = null;
+
+ /**
+ * @var integer
+ */
+ protected $julianDay = null;
+
+ /**
+ * Create a time data item. All time components other than the year can
+ * be false to indicate that they are not specified. This will affect
+ * the internal precision setting. The missing values are initialised
+ * to minimal values (0 or 1) for internal calculations.
+ *
+ * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @param $year integer number of the year (possibly negative)
+ * @param $month mixed integer number or false
+ * @param $day mixed integer number or false
+ * @param $hour mixed integer number or false
+ * @param $minute mixed integer number or false
+ * @param $second mixed integer number or false
+ * @param integer|false $timezone
+ *
+ * @todo Implement more validation here.
+ */
+ public function __construct( $calendarmodel, $year, $month = false, $day = false,
+ $hour = false, $minute = false, $second = false, $timezone = false ) {
+
+ if ( ( $calendarmodel != self::CM_GREGORIAN ) && ( $calendarmodel != self::CM_JULIAN ) ) {
+ throw new DataItemException( "Unsupported calendar model constant \"$calendarmodel\"." );
+ }
+
+ if ( $year == 0 ) {
+ throw new DataItemException( "There is no year 0 in Gregorian and Julian calendars." );
+ }
+
+ $this->m_model = $calendarmodel;
+ $this->m_year = intval( $year );
+ $this->m_month = $month != false ? intval( $month ) : 1;
+ $this->m_day = $day != false ? intval( $day ) : 1;
+ $this->m_hours = $hour !== false ? intval( $hour ) : 0;
+ $this->m_minutes = $minute !== false ? intval( $minute ) : 0;
+ $this->m_seconds = $second !== false ? floatval( $second ) : 0;
+
+ $this->timezone = $timezone !== false ? $timezone : 0;
+ $year = strval( $year );
+ $this->era = $year{0} === '+' ? 1 : ( $year{0} === '-' ? -1 : 0 );
+
+ if ( $this->isOutOfBoundsBySome() ) {
+ throw new DataItemException( "Part of the date is out of bounds." );
+ }
+
+ if ( $this->isOutOfBoundsByDayNumberOfMonth() ) {
+ throw new DataItemException( "Month {$this->m_month} in year {$this->m_year} did not have {$this->m_day} days in this calendar model." );
+ }
+
+ $this->setPrecisionLevelBy( $month, $day, $hour );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getDIType() {
+ return SMWDataItem::TYPE_TIME;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getCalendarModel() {
+ return $this->m_model;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getTimezone() {
+ return $this->timezone;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getPrecision() {
+ return $this->m_precision;
+ }
+
+ /**
+ * Indicates whether a user explicitly used an era marker even for a positive
+ * year.
+ *
+ * - [-1] indicates BC(E)
+ * - [0]/null indicates no era marker
+ * - [1] indicates AD/CE was used
+ *
+ * @since 2.4
+ *
+ * @return integer
+ */
+ public function getEra() {
+ return $this->era;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getYear() {
+ return $this->m_year;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getMonth() {
+ return $this->m_month;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getDay() {
+ return $this->m_day;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getHour() {
+ return $this->m_hours;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getMinute() {
+ return $this->m_minutes;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getSecond() {
+ return $this->m_seconds;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCalendarModelLiteral() {
+
+ $literal = [
+ self::CM_GREGORIAN => '',
+ self::CM_JULIAN => 'JL'
+ ];
+
+ return $literal[$this->m_model];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DateTime $dateTime
+ *
+ * @return SMWDITime|false
+ */
+ public static function newFromDateTime( DateTime $dateTime ) {
+
+ $calendarModel = self::CM_JULIAN;
+
+ $year = $dateTime->format( 'Y' );
+ $month = $dateTime->format( 'm' );
+ $day = $dateTime->format( 'd' );
+
+ if ( ( $year > 1582 ) ||
+ ( ( $year == 1582 ) && ( $month > 10 ) ) ||
+ ( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
+ $calendarModel = self::CM_GREGORIAN;
+ }
+
+ return self::doUnserialize( $calendarModel . '/' . $dateTime->format( 'Y/m/d/H/i/s.u' ) );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DateTime
+ */
+ public function asDateTime() {
+
+ $year = str_pad( $this->m_year, 4, '0', STR_PAD_LEFT );
+
+ // Avoid "Failed to parse time string (-900-02-02 00:00:00) at
+ // position 7 (-): Double timezone specification"
+ if ( $this->m_year < 0 ) {
+ $year = '-' . str_pad( $this->m_year * -1, 4, '0', STR_PAD_LEFT );
+ }
+
+ // Avoid "Failed to parse time string (1300-11-02 12:03:25.888499949) at
+ // at position 11 (1): The timezone could not ..."
+ $seconds = number_format( str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ), 7, '.', '' );
+
+ $time = $year . '-' .
+ str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ) . '-' .
+ str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ) . ' ' .
+ str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ) . ':' .
+ str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ) . ':' .
+ $seconds;
+
+ return new DateTime( $time );
+ }
+
+ /**
+ * Creates and returns a new instance of SMWDITime from a MW timestamp.
+ *
+ * @since 1.8
+ *
+ * @param string $timestamp must be in format
+ *
+ * @return SMWDITime|false
+ */
+ public static function newFromTimestamp( $timestamp ) {
+ $timestamp = wfTimestamp( TS_MW, (string)$timestamp );
+
+ if ( $timestamp === false ) {
+ return false;
+ }
+
+ return new self(
+ self::CM_GREGORIAN,
+ substr( $timestamp, 0, 4 ),
+ substr( $timestamp, 4, 2 ),
+ substr( $timestamp, 6, 2 ),
+ substr( $timestamp, 8, 2 ),
+ substr( $timestamp, 10, 2 ),
+ substr( $timestamp, 12, 2 )
+ );
+ }
+
+ /**
+ * Returns a MW timestamp representation of the value.
+ *
+ * @since 1.6.2
+ *
+ * @param $outputtype
+ *
+ * @return mixed
+ */
+ public function getMwTimestamp( $outputtype = TS_UNIX ) {
+ return wfTimestamp(
+ $outputtype,
+ implode( '', [
+ str_pad( $this->m_year, 4, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_month, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_day, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_hours, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_minutes, 2, '0', STR_PAD_LEFT ),
+ str_pad( $this->m_seconds, 2, '0', STR_PAD_LEFT ),
+ ] )
+ );
+ }
+
+ /**
+ * Get the data in the specified calendar model. This might require
+ * conversion.
+ * @note Conversion can be unreliable for very large absolute year
+ * numbers when the internal calculations hit floating point accuracy.
+ * Callers might want to avoid this (calendar models make little sense
+ * in such cases anyway).
+ * @param $calendarmodel integer one of SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return SMWDITime
+ */
+ public function getForCalendarModel( $calendarmodel ) {
+ if ( $calendarmodel == $this->m_model ) {
+ return $this;
+ } else {
+ return self::newFromJD( $this->getJD(), $calendarmodel, $this->m_precision );
+ }
+ }
+
+ /**
+ * Return a number that helps comparing time data items. For
+ * dates in the Julian Day era (roughly from 4713 BCE onwards), we use
+ * the Julian Day number. For earlier dates, the (negative) year number
+ * with a fraction for the date is used (times are ignored). This
+ * avoids calculation errors that would occur for very ancient dates
+ * if the JD number was used there.
+ * @return double sortkey
+ */
+ public function getSortKey() {
+ $jd = ( $this->m_year >= -4713 ) ? $jd = $this->getJD() : -1;
+ if ( $jd > 0 ) {
+ return $jd;
+ } else {
+ return $this->m_year - 1 + ( $this->m_month - 1 ) / 12 + ( $this->m_day - 1 ) / 12 / 31;
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return double
+ */
+ public function getJD() {
+
+ if ( $this->julianDay !== null ) {
+ return $this->julianDay;
+ }
+
+ $this->julianDay = JulianDay::getJD(
+ $this->getCalendarModel(),
+ $this->getYear(),
+ $this->getMonth(),
+ $this->getDay(),
+ $this->getHour(),
+ $this->getMinute(),
+ $this->getSecond()
+ );
+
+ return $this->julianDay;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ $result = strval( $this->m_model ) . '/' . ( $this->era > 0 ? '+' : '' ) . strval( $this->m_year );
+
+ if ( $this->m_precision >= self::PREC_YM ) {
+ $result .= '/' . strval( $this->m_month );
+ }
+
+ if ( $this->m_precision >= self::PREC_YMD ) {
+ $result .= '/' . strval( $this->m_day );
+ }
+
+ if ( $this->m_precision >= self::PREC_YMDT ) {
+ $result .= '/' . strval( $this->m_hours ) . '/' . strval( $this->m_minutes ) . '/' . strval( $this->m_seconds ) . '/' . strval( $this->timezone );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create a data item from the provided serialization string.
+ *
+ * @return SMWDITime
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( '/', $serialization, 8 );
+ $values = [];
+
+ if ( count( $parts ) <= 1 ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
+ }
+
+ for ( $i = 0; $i < 8; $i += 1 ) {
+
+ $values[$i] = false;
+
+ // Can contain something like '1/1970/1/12/11/43/0/Asia/Tokyo'
+ if ( $i == 7 && isset( $parts[$i] ) ) {
+ $values[$i] = strval( $parts[$i] );
+ continue;
+ }
+
+ if ( $i < count( $parts ) ) {
+
+ if ( $parts[$i] !== '' && !is_numeric( $parts[$i] ) ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid datetime specification." );
+ }
+
+ // 6 == seconds, we want to keep microseconds
+ $values[$i] = $i == 6 ? floatval( $parts[$i] ) : intval( $parts[$i] );
+
+ // Find out whether the input contained an explicit AD/CE era marker
+ if ( $i == 1 ) {
+ $values[$i] = ( $parts[1]{0} === '+' ? '+' : '' ) . $values[$i];
+ }
+ }
+ }
+
+ return new self( $values[0], $values[1], $values[2], $values[3], $values[4], $values[5], $values[6], $values[7] );
+ }
+
+ /**
+ * Create a new time dataItem from a specified Julian Day number,
+ * calendar model, presicion.
+ *
+ * @param double $jdValue
+ * @param integer|null $calendarmodel
+ * @param integer|null $precision
+ *
+ * @return DITime object
+ */
+ public static function newFromJD( $jdValue, $calendarModel = null, $precision = null, $timezone = false ) {
+
+ $hour = $minute = $second = false;
+ $year = $month = $day = false;
+ $jdValue = JulianDay::format( $jdValue );
+
+ if ( $precision === null ) {
+ $precision = strpos( strval( $jdValue ), '.5' ) !== false ? self::PREC_YMD : self::PREC_YMDT;
+ }
+
+ list( $calendarModel, $year, $month, $day ) = JulianDay::JD2Date( $jdValue, $calendarModel );
+
+ if ( $precision <= self::PREC_YM ) {
+ $day = false;
+ if ( $precision === self::PREC_Y ) {
+ $month = false;
+ }
+ }
+
+ if ( $precision === self::PREC_YMDT ) {
+ list( $hour, $minute, $second ) = JulianDay::JD2Time( $jdValue );
+ }
+
+ return new self( $calendarModel, $year, $month, $day, $hour, $minute, $second, $timezone );
+ }
+
+ /**
+ * Find out whether the given year number is a leap year.
+ * This calculation assumes that neither calendar has a year 0.
+ * @param $year integer year number
+ * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return boolean
+ */
+ static public function isLeapYear( $year, $calendarmodel ) {
+ $astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
+ if ( $calendarmodel == self::CM_JULIAN ) {
+ return ( $astroyear % 4 ) == 0;
+ } else {
+ return ( ( $astroyear % 400 ) == 0 ) ||
+ ( ( ( $astroyear % 4 ) == 0 ) && ( ( $astroyear % 100 ) != 0 ) );
+ }
+ }
+
+ /**
+ * Find out how many days the given month had in the given year
+ * based on the specified calendar model.
+ * This calculation assumes that neither calendar has a year 0.
+ * @param $month integer month number
+ * @param $year integer year number
+ * @param $calendarmodel integer either SMWDITime::CM_GREGORIAN or SMWDITime::CM_JULIAN
+ * @return boolean
+ */
+ static public function getDayNumberForMonth( $month, $year, $calendarmodel ) {
+ if ( $month !== 2 ) {
+ return self::$m_daysofmonths[$month];
+ } elseif ( self::isLeapYear( $year, $calendarmodel ) ) {
+ return 29;
+ } else {
+ return 28;
+ }
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_TIME ) {
+ return false;
+ }
+
+ return $di->getSortKey() === $this->getSortKey();
+ }
+
+ private function isOutOfBoundsBySome() {
+ return ( $this->m_hours < 0 ) || ( $this->m_hours > 23 ) ||
+ ( $this->m_minutes < 0 ) || ( $this->m_minutes > 59 ) ||
+ ( $this->m_seconds < 0 ) || ( $this->m_seconds > 59 ) ||
+ ( $this->m_month < 1 ) || ( $this->m_month > 12 );
+ }
+
+ private function isOutOfBoundsByDayNumberOfMonth() {
+ return $this->m_day > self::getDayNumberForMonth( $this->m_month, $this->m_year, $this->m_model );
+ }
+
+ private function setPrecisionLevelBy( $month, $day, $hour ) {
+ if ( $month === false ) {
+ $this->m_precision = self::PREC_Y;
+ } elseif ( $day === false ) {
+ $this->m_precision = self::PREC_YM;
+ } elseif ( $hour === false ) {
+ $this->m_precision = self::PREC_YMD;
+ } else {
+ $this->m_precision = self::PREC_YMDT;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php
new file mode 100644
index 00000000..d1aaa67d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_URI.php
@@ -0,0 +1,165 @@
+<?php
+
+use SMW\Exception\DataItemException;
+
+/**
+ * This class implements URI data items.
+ *
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataItems
+ */
+class SMWDIUri extends SMWDataItem {
+
+ /**
+ * URI scheme such as "html" or "mailto".
+ * @var string
+ */
+ protected $m_scheme;
+ /**
+ * "Hierpart" of the URI (usually some authority and path).
+ * @var string
+ */
+ protected $m_hierpart;
+ /**
+ * Query part of the URI.
+ * @var string
+ */
+ protected $m_query;
+ /**
+ * Fragment part of the URI.
+ * @var string
+ */
+ protected $m_fragment;
+
+ /**
+ * Initialise a URI by providing its scheme (e.g. "html"), 'hierpart'
+ * following "scheme:" (e.g. "//username@example.org/path), query (e.g.
+ * "q=Search+term", and fragment (e.g. "section-one"). The complete URI
+ * with these examples would be
+ * http://username@example.org/path?q=Search+term#section-one
+ * @param $scheme string for the scheme
+ * @param $hierpart string for the "hierpart"
+ * @param $query string for the query
+ * @param $fragment string for the fragment
+ *
+ * @todo Implement more validation here.
+ */
+ public function __construct( $scheme, $hierpart, $query, $fragment, $strict = true ) {
+ if ( $strict && ( ( $scheme === '' ) || ( preg_match( '/[^a-zA-Z]/u', $scheme ) ) ) ) {
+ throw new DataItemException( "Illegal URI scheme \"$scheme\"." );
+ }
+ if ( $strict && $hierpart === '' ) {
+ throw new DataItemException( "Illegal URI hierpart \"$hierpart\"." );
+ }
+ $this->m_scheme = $scheme;
+ $this->m_hierpart = $hierpart;
+ $this->m_query = $query;
+ $this->m_fragment = $fragment;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_URI;
+ }
+
+ /// @todo This should be changed to the spelling getUri().
+ public function getURI() {
+ $schemesWithDoubleslesh = [
+ 'http', 'https', 'ftp'
+ ];
+
+ $uri = $this->m_scheme . ':'
+ . ( in_array( $this->m_scheme, $schemesWithDoubleslesh ) ? '//' : '' )
+ . $this->m_hierpart
+ . ( $this->m_query ? '?' . $this->m_query : '' )
+ . ( $this->m_fragment ? '#' . $this->m_fragment : '' );
+
+ // #1878
+ // https://tools.ietf.org/html/rfc3986
+ // Normalize spaces to use `_` instead of %20 and so ensure
+ // that http://example.org/Foo bar === http://example.org/Foo_bar === http://example.org/Foo%20bar
+ return str_replace( [ ' ', '%20'], '_', $uri );
+ }
+
+ public function getScheme() {
+ return $this->m_scheme;
+ }
+
+ public function getHierpart() {
+ return $this->m_hierpart;
+ }
+
+ public function getQuery() {
+ return $this->m_query;
+ }
+
+ public function getFragment() {
+ return $this->m_fragment;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSortKey() {
+ return urldecode( $this->getURI() );
+ }
+
+ public function getSerialization() {
+ return $this->getURI();
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type
+ * ID.
+ * @return SMWDIUri
+ */
+ public static function doUnserialize( $serialization ) {
+
+ // try to split "schema:rest"
+ $parts = explode( ':', $serialization, 2 );
+ $strict = true;
+
+ if ( $serialization !== null && count( $parts ) <= 1 ) {
+ throw new DataItemException( "Unserialization failed: the string \"$serialization\" is no valid URI." );
+ }
+
+ if ( $serialization === null ) {
+ $parts = [ '', 'NO_VALUE' ];
+ $strict = false;
+ }
+
+ $scheme = $parts[0];
+
+ // try to split "hier-part?queryfrag"
+ $parts = explode( '?', $parts[1], 2 );
+
+ if ( count( $parts ) == 2 ) {
+ $hierpart = $parts[0];
+ // try to split "query#frag"
+ $parts = explode( '#', $parts[1], 2 );
+ $query = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ } else {
+ $query = '';
+ // try to split "hier-part#frag"
+ $parts = explode( '#', $parts[0], 2 );
+ $hierpart = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ }
+
+ $hierpart = ltrim( $hierpart, '/' );
+
+ return new SMWDIUri( $scheme, $hierpart, $query, $fragment, $strict );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_URI ) {
+ return false;
+ }
+
+ return $di->getURI() === $this->getURI();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php
new file mode 100644
index 00000000..863cbef1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DI_WikiPage.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace SMW;
+
+use SMW\Exception\DataItemDeserializationException;
+use SMW\Exception\DataItemException;
+use SMWDataItem;
+use Title;
+
+/**
+ * This class implements wiki page data items.
+ *
+ * @since 1.6
+ * @ingroup SMWDataItems
+ *
+ * @author Markus Krötzsch
+ */
+class DIWikiPage extends SMWDataItem {
+
+ /**
+ * MediaWiki DB key string
+ * @var string
+ */
+ protected $m_dbkey;
+
+ /**
+ * MediaWiki namespace integer.
+ * @var integer
+ */
+ protected $m_namespace;
+
+ /**
+ * MediaWiki interwiki prefix.
+ * @var string
+ */
+ protected $m_interwiki;
+
+ /**
+ * Name for subobjects of pages, or empty string if the given object is
+ * the page itself (not a subobject).
+ * @var string
+ */
+ protected $m_subobjectname;
+
+ /**
+ * @var string
+ */
+ private $sortkey = null;
+
+ /**
+ * @var string
+ */
+ private $contextReference = null;
+
+ /**
+ * @var string
+ */
+ private $pageLanguage = null;
+
+ /**
+ * @var integer
+ */
+ private $id = 0;
+
+ /**
+ * Constructor. We do not bother with too much detailed validation here,
+ * regarding the known namespaces, canonicity of the dbkey (namespace
+ * exrtacted?), validity of interwiki prefix (known?), and general use
+ * of allowed characters (may depend on MW configuration). All of this
+ * would be more work than it is worth, since callers will usually be
+ * careful and since errors here do not have major consequences.
+ *
+ * @param string $dbkey
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobjectname
+ */
+ public function __construct( $dbkey, $namespace, $interwiki = '', $subobjectname = '' ) {
+ // Check if the provided value holds an integer
+ // (it can be of type string or float as well, as long as the value is an int)
+ if ( !ctype_digit( ltrim( (string)$namespace, '-' ) ) ) {
+ throw new DataItemException( "Given namespace '$namespace' is not an integer." );
+ }
+
+ // Check for a potential fragment such as Foo#Bar, Bar#_49c8ab
+ if ( strpos( $dbkey, '#' ) !== false ) {
+ list( $dbkey, $subobjectname ) = explode( '#', $dbkey );
+ }
+
+ $this->m_dbkey = str_replace( ' ', '_', $dbkey );
+ $this->m_namespace = (int)$namespace; // really make this an integer
+ $this->m_interwiki = $interwiki;
+ $this->m_subobjectname = $subobjectname;
+ }
+
+ public function getDIType() {
+ return SMWDataItem::TYPE_WIKIPAGE;
+ }
+
+ public function getDBkey() {
+ return $this->m_dbkey;
+ }
+
+ public function getNamespace() {
+ return $this->m_namespace;
+ }
+
+ public function getInterwiki() {
+ return $this->m_interwiki;
+ }
+
+ public function getSubobjectName() {
+ return $this->m_subobjectname;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $sortkey
+ */
+ public function setSortKey( $sortkey ) {
+ $this->sortkey = str_replace( '_', ' ', $sortkey );
+ }
+
+ /**
+ * Get the sortkey of the wiki page data item. Note that this is not
+ * the sortkey that might have been set for the corresponding wiki
+ * page. To obtain the latter, query for the values of the property
+ * "new SMW\DIProperty( '_SKEY' )".
+ */
+ public function getSortKey() {
+
+ if ( $this->sortkey === null || $this->sortkey === '' ) {
+ $this->sortkey = str_replace( '_', ' ', $this->m_dbkey );
+ }
+
+ return $this->sortkey;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $contextReference
+ */
+ public function setContextReference( $contextReference ) {
+ $this->contextReference = $contextReference;
+ }
+
+ /**
+ * Returns a reference for the processing context (parser etc.).
+ *
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getContextReference() {
+ return $this->contextReference;
+ }
+
+ /**
+ * Returns the page content language
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getPageLanguage() {
+
+ if ( $this->pageLanguage === null ) {
+ $this->pageLanguage = false;
+
+ if ( ( $title = $this->getTitle() ) !== null ) {
+ $this->pageLanguage = $title->getPageLanguage()->getCode();
+ }
+ }
+
+ return $this->pageLanguage;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $id
+ */
+ public function setId( $id ) {
+ $this->id = (int)$id;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Create a MediaWiki Title object for this DIWikiPage. The result
+ * can be null if an error occurred.
+ *
+ * @return Title|null
+ */
+ public function getTitle() {
+ return Title::makeTitleSafe(
+ $this->m_namespace,
+ $this->m_dbkey,
+ $this->m_subobjectname,
+ $this->m_interwiki
+ );
+ }
+
+ /**
+ * Returns the base part (without a fragment) of a wikipage representation.
+ *
+ * @since 2.4
+ *
+ * @return DIWikiPage
+ */
+ public function asBase() {
+ return new self (
+ $this->m_dbkey,
+ $this->m_namespace,
+ $this->m_interwiki
+ );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getSerialization() {
+ $segments = [
+ $this->m_dbkey,
+ $this->m_namespace,
+ $this->m_interwiki
+ ];
+
+ $segments[] = $this->m_subobjectname;
+
+ return implode( '#', $segments );
+ }
+
+ /**
+ * Create a data item from the provided serialization string and type ID.
+ *
+ * @param string $serialization
+ *
+ * @return DIWikiPage
+ * @throws DataItemDeserializationException
+ */
+ public static function doUnserialize( $serialization ) {
+ $parts = explode( '#', $serialization, 4 );
+
+ if ( count( $parts ) == 3 ) {
+ return new self( $parts[0], intval( $parts[1] ), $parts[2] );
+ } elseif ( count( $parts ) == 4 ) {
+ return new self( $parts[0], intval( $parts[1] ), $parts[2], $parts[3] );
+ } else {
+ throw new DataItemDeserializationException( "Unserialization failed: the string \"$serialization\" was not understood." );
+ }
+ }
+
+ /**
+ * Create a data item from a MediaWiki Title.
+ *
+ * @param Title $title
+ * @return DIWikiPage
+ */
+ public static function newFromTitle( Title $title ) {
+ return new self(
+ $title->getDBkey(),
+ $title->getNamespace(),
+ $title->getInterwiki(),
+ str_replace( ' ', '_', $title->getFragment() )
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $text
+ * @param integer namespace
+ *
+ * @return DIWikiPage
+ */
+ public static function newFromText( $text, $namespace = NS_MAIN ) {
+ return new self( $text, $namespace );
+ }
+
+ public function equals( SMWDataItem $di ) {
+ if ( $di->getDIType() !== SMWDataItem::TYPE_WIKIPAGE ) {
+ return false;
+ }
+
+ return $di->getSerialization() === $this->getSerialization();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php
new file mode 100644
index 00000000..96137956
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/dataitems/SMW_DataItem.php
@@ -0,0 +1,236 @@
+<?php
+
+use SMW\Options;
+
+/**
+ * This group contains all parts of SMW that relate to the processing of dataitems
+ * of various types.
+ *
+ * @defgroup SMWDataItems SMWDataItems
+ * @ingroup SMW
+ */
+
+/**
+ * Objects of this type represent all that is known about a certain piece of
+ * data that could act as the value of some property. Data items only represent
+ * the stored data, and are thus at the core of SMW's data model. Data items
+ * are always immutable, i.e. they must not be changed after creation (and this
+ * is mostly enforced by the API with some minor exceptions).
+ *
+ * The set of available data items is fixed and cannot be extended. These are
+ * the kinds of information that SMW can process. Their concrete use and
+ * handling might depend on the context in which they are used. In particular,
+ * property values may be influences by settings made for their property. This
+ * aspect, however, is not part of the data item API.
+ *
+ * @since 1.6
+ *
+ * @ingroup SMWDataItems
+ */
+abstract class SMWDataItem {
+
+ /// Data item ID that can be used to indicate that no data item class is appropriate
+ const TYPE_NOTYPE = 0;
+ /// Data item ID for SMWDINumber
+ const TYPE_NUMBER = 1;
+ /// Data item ID for SMWDIBlob
+ const TYPE_BLOB = 2;
+ /// Data item ID for SMWDIBoolean
+ const TYPE_BOOLEAN = 4;
+ /// Data item ID for SMWDIUri
+ const TYPE_URI = 5;
+ /// Data item ID for SMWDITimePoint
+ const TYPE_TIME = 6;
+ /// Data item ID for SMWDIGeoCoord
+ const TYPE_GEO = 7;
+ /// Data item ID for SMWDIContainer
+ const TYPE_CONTAINER = 8;
+ /// Data item ID for SMWDIWikiPage
+ const TYPE_WIKIPAGE = 9;
+ /// Data item ID for SMWDIConcept
+ const TYPE_CONCEPT = 10;
+ /// Data item ID for SMWDIProperty
+ const TYPE_PROPERTY = 11;
+ /// Data item ID for SMWDIError
+ const TYPE_ERROR = 12;
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * Convenience method that returns a constant that defines the concrete
+ * class that implements this data item. Used to switch when processing
+ * data items.
+ * @return integer that specifies the basic type of data item
+ */
+ abstract public function getDIType();
+
+ /**
+ * Return a value that can be used for sorting data of this type.
+ * If the data is of a numerical type, the sorting must be done in
+ * numerical order. If the data is a string, the data must be sorted
+ * alphabetically.
+ *
+ * @note Every data item returns a sort key, even if there is no
+ * natural linear order for the type. SMW must order listed data
+ * in some way in any case. If there is a natural order (e.g. for
+ * Booleans where false < true), then the sortkey must agree with
+ * this order (e.g. for Booleans where false maps to 0, and true
+ * maps to 1).
+ *
+ * @note Wiki pages are a special case in SMW. They are ordered by a
+ * sortkey that is assigned to them as a property value. When pages are
+ * sorted, this data should be used if possible.
+ *
+ * @return float|string
+ */
+ abstract public function getSortKey();
+
+ /**
+ * Method to compare two SMWDataItems
+ * This should result true only if they are of the same DI type
+ * and have the same internal value
+ *
+ * @since 1.8
+ *
+ * @param SMWDataItem $di
+ * @return boolean
+ */
+ abstract public function equals( SMWDataItem $di );
+
+ /**
+ * Create a data item that represents the sortkey, i.e. either an
+ * SMWDIBlob or an SMWDINumber. For efficiency, these subclasses
+ * overwrite this method to return themselves.
+ *
+ * @return SMWDataItem
+ */
+ public function getSortKeyDataItem() {
+ $sortKey = $this->getSortKey();
+
+ if ( is_numeric( $sortKey ) ) {
+ return new SMWDINumber( $sortKey );
+ }
+
+ return new SMWDIBlob( $sortKey );
+ }
+
+ /**
+ * Get a UTF-8 encoded string serialization of this data item.
+ * The serialisation should be concise and need not be pretty, but it
+ * must allow unserialization. Each subclass of SMWDataItem implements
+ * a static method doUnserialize() for this purpose.
+ * @return string
+ */
+ abstract public function getSerialization();
+
+ /**
+ * Get a hash string for this data item. Might be overwritten in
+ * subclasses to obtain shorter or more efficient hashes.
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->getSerialization();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getSerialization();
+ }
+
+ /**
+ * Create a data item of the given dataitem ID based on the the
+ * provided serialization string and (optional) typeid.
+ *
+ * @param integer $diType dataitem ID
+ * @param string $serialization
+ *
+ * @return SMWDataItem
+ */
+ public static function newFromSerialization( $diType, $serialization ) {
+ $diClass = self::getDataItemClassNameForId( $diType );
+ return call_user_func( [ $diClass, 'doUnserialize' ], $serialization );
+ }
+
+ /**
+ * Gets the class name of the data item that has the provided type id.
+ *
+ * @param integer $diType Element of the SMWDataItem::TYPE_ enum
+ *
+ * @throws InvalidArgumentException
+ *
+ * @return string
+ */
+ public static function getDataItemClassNameForId( $diType ) {
+ switch ( $diType ) {
+ case self::TYPE_NUMBER:
+ return SMWDINumber::class;
+ case self::TYPE_BLOB:
+ return SMWDIBlob::class;
+ case self::TYPE_BOOLEAN:
+ return SMWDIBoolean::class;
+ case self::TYPE_URI:
+ return SMWDIUri::class;
+ case self::TYPE_TIME:
+ return SMWDITime::class;
+ case self::TYPE_GEO:
+ return SMWDIGeoCoord::class;
+ case self::TYPE_CONTAINER:
+ return SMWDIContainer::class;
+ case self::TYPE_WIKIPAGE:
+ return SMWDIWikiPage::class;
+ case self::TYPE_CONCEPT:
+ return SMWDIConcept::class;
+ case self::TYPE_PROPERTY:
+ return SMWDIProperty::class;
+ case self::TYPE_ERROR:
+ return SMWDIError::class;
+ case self::TYPE_NOTYPE: default:
+ throw new InvalidArgumentException( "The value \"$diType\" is not a valid dataitem ID." );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string|null $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ if ( $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php
new file mode 100644
index 00000000..01128751
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Concept.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue is used as a container for concept descriptions as used
+ * on Concept pages with the #concept parserfunction. It has a somewhat
+ * non-standard interface as compared to other datavalues, but this is not
+ * an issue.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWConceptValue extends SMWDataValue {
+
+ protected function parseUserValue( $value ) {
+ throw new Exception( 'Concepts cannot be initialized from user-provided strings. This should not happen.' );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataItem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_CONCEPT ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = $dataItem->getConceptQuery(); // probably useless
+
+ return true;
+ }
+
+ protected function clear() {
+ $this->m_dataitem = new \SMW\DIConcept( '', '', 0, -1, -1, $this->m_typeid );
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return $this->m_caption;
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return $this->getShortWikiText( $linker ); // should be save (based on xsdvalue)
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ } else {
+ return $this->m_caption;
+ }
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ } else {
+ return $this->m_caption; // should be save (based on xsdvalue)
+ }
+ }
+
+ public function getWikiValue() {
+ /// This should not be used for anything. This class does not support wiki values.
+ return str_replace( [ '&lt;', '&gt;', '&amp;' ], [ '<', '>', '&' ], $this->m_dataitem->getConceptQuery() );
+ }
+
+ /// Return the concept's defining text (in SMW query syntax)
+ public function getConceptText() {
+ return $this->m_dataitem->getConceptQuery();
+ }
+
+ /// Return the optional concept documentation.
+ public function getDocu() {
+ return $this->m_dataitem->getDocumentation();
+ }
+
+ /// Return the concept's size (a metric used to estimate computation complexity).
+ public function getSize() {
+ return $this->m_dataitem->getSize();
+ }
+
+ /// Return the concept's depth (a metric used to estimate computation complexity).
+ public function getDepth() {
+ return $this->m_dataitem->getDepth();
+ }
+
+ /// Return the concept's query feature bit field (a metric used to estimate computation complexity).
+ public function getQueryFeatures() {
+ return $this->m_dataitem->getQueryFeatures();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php
new file mode 100644
index 00000000..1ad6a3ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Error.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements error datavalues, a kind of pseudo data value that
+ * is used in places where a data value is expected but no more meaningful
+ * value could be created. It is always invalid and never gets stored or
+ * exported, but it can help to transport an error message.
+ *
+ * @note SMWDataValue will return a data item of type SMWDIError for invalid
+ * data values. Hence this is the DI type of this DV, even if not mentioned in
+ * this file.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWErrorValue extends SMWDataValue {
+
+ public function __construct( $typeid, $errormsg = '', $uservalue = '', $caption = false ) {
+ parent::__construct( $typeid );
+ $this->m_caption = ( $caption !== false ) ? $caption : $uservalue;
+ if ( $errormsg !== '' ) {
+ $this->addErrorMsg( $errormsg );
+ }
+ }
+
+ protected function parseUserValue( $value ) {
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+ $this->addErrorMsg( [ 'smw-datavalue-parse-error', $value ] );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_ERROR ) {
+ $this->addError( $dataItem->getErrors() );
+ $this->m_caption = $this->getErrorText();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return $this->m_caption;
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return htmlspecialchars( $this->getShortWikiText( $linker ) );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->getErrorText();
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->getErrorText();
+ }
+
+ public function getWikiValue() {
+
+ if ( $this->m_dataitem !== null ) {
+ return $this->m_dataitem->getString();
+ }
+
+ return $this->getErrorText();
+ }
+
+ public function isValid() {
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php
new file mode 100644
index 00000000..cfa75cfb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Number.php
@@ -0,0 +1,602 @@
+<?php
+
+use SMW\DataValues\Number\IntlNumberFormatter;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Localizer;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements numerical datavalues, and supports optional
+ * unit conversions. It parses and manages unit strings, since even plain
+ * numbers may have (not further specified) units that are stored. However,
+ * only subclasses implement full unit conversion by extending the methods
+ * convertToMainUnit() and makeConversionValues().
+ *
+ * Units work as follows: a unit is a string, but many such strings might
+ * refer to the same unit of measurement. There is always one string, that
+ * canonically represents the unit, and we will call this version of writing
+ * the unit the /unit id/. IDs for units are needed for tasks like duplicate
+ * avoidance. If no conversion information is given, any unit is its own ID.
+ * In any case, units are /normalised/, i.e. given a more standardised meaning
+ * before being processed. All units, IDs or otherwise, should be suitable for
+ * printout in wikitext, and main IDs should moreover be suitable for printout
+ * in HTML.
+ *
+ * Subclasses that support unit conversion may interpret the output format set
+ * via setOutputFormat() to allow a unit to be selected for display. Note that
+ * this setting does not affect the internal representation of the value
+ * though. So choosing a specific output format will change the behavior of
+ * output functions like getLongWikiText(), but not of functions that access
+ * the value itself, such as getUnit() or getDBKeys().
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ *
+ * @todo Wiki-HTML-conversion for unit strings must be revisited, as the current
+ * solution might be unsafe.
+ */
+class SMWNumberValue extends SMWDataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_num';
+
+ /**
+ * Internal state to ensure no precision limitation is applied to an output
+ */
+ const NO_DISP_PRECISION_LIMIT = 'num.no.displayprecision.limit';
+
+ /**
+ * Separator related constants
+ */
+ const DECIMAL_SEPARATOR = 'decimal.separator';
+ const THOUSANDS_SEPARATOR = 'thousands.separator';
+
+ /**
+ * Array with entries unit=>value, mapping a normalized unit to the
+ * converted value. Used for conversion tooltips.
+ * @var array
+ */
+ protected $m_unitvalues;
+
+ /**
+ * Whether the unit is preferred as prefix or not
+ *
+ * @var array
+ */
+ protected $prefixalUnitPreference = [];
+
+ /**
+ * Canonical identifier for the unit that the user gave as input. Used
+ * to avoid printing this in conversion tooltips again. If the
+ * outputformat was set to show another unit, then the values of
+ * $m_caption and $m_unitin will be updated as if the formatted string
+ * had been the original user input, i.e. the two values reflect what
+ * is currently printed.
+ * @var string
+ */
+ protected $m_unitin;
+
+ /**
+ * @var integer|null
+ */
+ protected $precision = null;
+
+ /**
+ * @var IntlNumberFormatter
+ */
+ private $intlNumberFormatter;
+
+ /**
+ * @var ValueFormatter
+ */
+ private $valueFormatter;
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( $typeid );
+ $this->intlNumberFormatter = IntlNumberFormatter::getInstance();
+ $this->intlNumberFormatter->reset();
+ }
+
+ /**
+ * Parse a string of the form "number unit" where unit is optional. The
+ * results are stored in the $number and $unit parameters. Returns an
+ * error code.
+ * @param $value string to parse
+ * @param $number call-by-ref parameter that will be set to the numerical value
+ * @param $unit call-by-ref parameter that will be set to the "unit" string (after the number)
+ * @return integer 0 (no errors), 1 (no number found at all), 2 (number
+ * too large for this platform)
+ */
+ public function parseNumberValue( $value, &$number, &$unit, &$asPrefix = false ) {
+
+ $intlNumberFormatter = $this->getNumberFormatter();
+
+ // Parse to find $number and (possibly) $unit
+ $kiloseparator = $intlNumberFormatter->getSeparatorByLanguage(
+ self::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE
+ );
+
+ $decseparator = $intlNumberFormatter->getSeparatorByLanguage(
+ self::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE
+ );
+
+ // #753
+ $regex = '/([-+]?\s*(?:' .
+ // Either numbers like 10,000.99 that start with a digit
+ '\d+(?:\\' . $kiloseparator . '\d\d\d)*(?:\\' . $decseparator . '\d+)?' .
+ // or numbers like .001 that start with the decimal separator
+ '|\\' . $decseparator . '\d+' .
+ ')\s*(?:[eE][-+]?\d+)?)/u';
+
+ // #1718 Whether to preserve spaces in unit labels or not (e.g. sq mi, sqmi)
+ $space = $this->isEnabledFeature( SMW_DV_NUMV_USPACE ) ? ' ' : '';
+
+ $parts = preg_split(
+ $regex,
+ trim( str_replace( [ '&nbsp;', '&#160;', '&thinsp;', ' ' ], $space, $value ) ),
+ 2,
+ PREG_SPLIT_DELIM_CAPTURE
+ );
+
+ if ( count( $parts ) >= 2 ) {
+ $numstring = str_replace( $kiloseparator, '', preg_replace( '/\s*/u', '', $parts[1] ) ); // simplify
+ if ( $decseparator != '.' ) {
+ $numstring = str_replace( $decseparator, '.', $numstring );
+ }
+ list( $number ) = sscanf( $numstring, "%f" );
+ if ( count( $parts ) >= 3 ) {
+ $asPrefix = $parts[0] !== '';
+ $unit = $this->normalizeUnit( $parts[0] !== '' ? $parts[0] : $parts[2] );
+ }
+ }
+
+ if ( ( count( $parts ) == 1 ) || ( $numstring === '' ) ) { // no number found
+ return 1;
+ } elseif ( is_infinite( $number ) ) { // number is too large for this platform
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+ // Set caption
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ if ( $value !== '' && $value{0} === ':' ) {
+ $this->addErrorMsg( [ 'smw-datavalue-invalid-number', $value ] );
+ return;
+ }
+
+ $this->m_unitin = false;
+ $this->m_unitvalues = false;
+ $number = $unit = '';
+ $error = $this->parseNumberValue( $value, $number, $unit );
+
+ if ( $error == 1 ) { // no number found
+ $this->addErrorMsg( [ 'smw_nofloat', $value ] );
+ } elseif ( $error == 2 ) { // number is too large for this platform
+ $this->addErrorMsg( [ 'smw_infinite', $value ] );
+ } elseif ( $this->getTypeID() === '_num' && $unit !== '' ) {
+ $this->addErrorMsg( [ 'smw-datavalue-number-textnotallowed', $unit, $number ] );
+ } elseif ( $number === null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-number-nullnotallowed', $value ] ); // #1628
+ } elseif ( $this->convertToMainUnit( $number, $unit ) === false ) { // so far so good: now convert unit and check if it is allowed
+ $this->addErrorMsg( [ 'smw_unitnotallowed', $unit ] );
+ } // note that convertToMainUnit() also sets m_dataitem if valid
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_NUMBER ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = false;
+ $this->m_unitin = false;
+ $this->makeUserValue();
+ $this->m_unitvalues = false;
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::setOutputFormat
+ *
+ * @param $string $formatstring
+ */
+ public function setOutputFormat( $formatstring ) {
+
+ if ( $formatstring == $this->m_outformat ) {
+ return null;
+ }
+
+ // #1591
+ $this->findPreferredLanguageFrom( $formatstring );
+
+ // #1335
+ $this->m_outformat = $this->findPrecisionFrom( $formatstring );
+
+ if ( $this->isValid() ) { // update caption/unitin for this format
+ $this->m_caption = false;
+ $this->m_unitin = false;
+ $this->makeUserValue();
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return float
+ */
+ public function getNumber() {
+
+ if ( !$this->isValid() ) {
+ return 999999999999999;
+ }
+
+ return $this->m_dataitem->getNumber();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return float
+ */
+ public function getLocalizedFormattedNumber( $value ) {
+ return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision() );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return float
+ */
+ public function getNormalizedFormattedNumber( $value ) {
+ return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision(), IntlNumberFormatter::VALUE_FORMAT );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @return string
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ *
+ * @return string
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ *
+ * @return string
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ *
+ * @return string
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ *
+ * @return string
+ */
+ public function getWikiValue() {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->valueFormatter->setDataValue( $this );
+
+ return $this->valueFormatter->format( DataValueFormatter::VALUE );
+ }
+
+ /**
+ * @see DataVelue::getInfolinks
+ *
+ * @return array
+ */
+ public function getInfolinks() {
+
+ // When generating an infoLink, use the normalized value without any
+ // precision limitation
+ $this->setOption( self::NO_DISP_PRECISION_LIMIT, true );
+ $this->setOption( self::OPT_CONTENT_LANGUAGE, Message::CONTENT_LANGUAGE );
+
+ $infoLinks = parent::getInfolinks();
+
+ $this->setOption( self::NO_DISP_PRECISION_LIMIT, false );
+
+ return $infoLinks;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCanonicalMainUnit() {
+ return $this->m_unitin;
+ }
+
+ /**
+ * Returns array of converted unit-value-pairs that can be
+ * printed.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getConvertedUnitValues() {
+ $this->makeConversionValues();
+ return $this->m_unitvalues;
+ }
+
+ /**
+ * Return the unit in which the returned value is to be interpreted.
+ * This string is a plain UTF-8 string without wiki or html markup.
+ * The returned value is a canonical ID for the main unit.
+ * Returns the empty string if no unit is given for the value.
+ * Overwritten by subclasses that support units.
+ */
+ public function getUnit() {
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $unit
+ *
+ * @return boolean
+ */
+ public function hasPrefixalUnitPreference( $unit ) {
+ return isset( $this->prefixalUnitPreference[$unit] ) && $this->prefixalUnitPreference[$unit];
+ }
+
+ /**
+ * Create links to mapping services based on a wiki-editable message.
+ * The parameters available to the message are:
+ * $1: string of numerical value in English punctuation
+ * $2: string of integer version of value, in English punctuation
+ *
+ * @return array
+ */
+ protected function getServiceLinkParams() {
+ if ( $this->isValid() ) {
+ return [ strval( $this->m_dataitem->getNumber() ), strval( round( $this->m_dataitem->getNumber() ) ) ];
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * Transform a (typically unit-) string into a normalised form,
+ * so that, e.g., "km²" and "km<sup>2</sup>" do not need to be
+ * distinguished.
+ */
+ public function normalizeUnit( $unit ) {
+ $unit = str_replace( [ '[[', ']]' ], '', trim( $unit ) ); // allow simple links to be used inside annotations
+ $unit = str_replace( [ '²', '<sup>2</sup>' ], '&sup2;', $unit );
+ $unit = str_replace( [ '³', '<sup>3</sup>' ], '&sup3;', $unit );
+ return smwfXMLContentEncode( $unit );
+ }
+
+ /**
+ * Compute the value based on the given input number and unit string.
+ * If the unit is not supported, return false, otherwise return true.
+ * This is called when parsing user input, where the given unit value
+ * has already been normalized.
+ *
+ * This class does not support any (non-empty) units, but subclasses
+ * may overwrite this behavior.
+ * @param $number float value obtained by parsing user input
+ * @param $unit string after the numericla user input
+ * @return boolean specifying if the unit string is allowed
+ */
+ protected function convertToMainUnit( $number, $unit ) {
+ $this->m_dataitem = new SMWDINumber( $number );
+ $this->m_unitin = '';
+ return ( $unit === '' );
+ }
+
+ /**
+ * This method creates an array of unit-value-pairs that should be
+ * printed. Units are the keys and should be canonical unit IDs.
+ * The result is stored in $this->m_unitvalues. Again, any class that
+ * requires effort for doing this should first check whether the array
+ * is already set (i.e. not false) before doing any work.
+ * Note that the values should be plain numbers. Output formatting is done
+ * later when needed. Also, it should be checked if the value is valid
+ * before trying to calculate with its contents.
+ * This method also must call or implement convertToMainUnit().
+ *
+ * Overwritten by subclasses that support units.
+ */
+ protected function makeConversionValues() {
+ $this->m_unitvalues = [ '' => $this->m_dataitem->getNumber() ];
+ }
+
+ /**
+ * This method is used when no user input was given to find the best
+ * values for m_unitin and m_caption. After conversion,
+ * these fields will look as if they were generated from user input,
+ * and convertToMainUnit() will have been called (if not, it would be
+ * blocked by the presence of m_unitin).
+ *
+ * Overwritten by subclasses that support units.
+ */
+ protected function makeUserValue() {
+ $this->m_caption = '';
+
+ $number = $this->m_dataitem->getNumber();
+
+ // -u is the format for displaying the unit only
+ if ( $this->m_outformat == '-u' ) {
+ $this->m_caption = '';
+ } elseif ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ) {
+ $this->m_caption = $this->getLocalizedFormattedNumber( $number );
+ } else {
+ $this->m_caption = $this->getNormalizedFormattedNumber( $number );
+ }
+
+ // no unit ever, so nothing to do about this
+ $this->m_unitin = '';
+ }
+
+ /**
+ * Return an array of major unit strings (ids only recommended) supported by
+ * this datavalue.
+ *
+ * Overwritten by subclasses that support units.
+ */
+ public function getUnitList() {
+ return [ '' ];
+ }
+
+ protected function getPreferredDisplayPrecision() {
+
+ // Don't restrict the value with a display precision
+ if ( $this->getProperty() === null || $this->getOption( self::NO_DISP_PRECISION_LIMIT ) ) {
+ return false;
+ }
+
+ if ( $this->precision === null ) {
+ $this->precision = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getDisplayPrecision(
+ $this->getProperty()
+ );
+ }
+
+ return $this->precision;
+ }
+
+ private function findPrecisionFrom( $formatstring ) {
+
+ if ( strpos( $formatstring, '-' ) === false ) {
+ return $formatstring;
+ }
+
+ $parts = explode( '-', $formatstring );
+
+ // Find precision from annotated -p<number of digits> formatstring which
+ // has priority over a possible _PREC value
+ foreach ( $parts as $key => $value ) {
+ if ( strpos( $value, 'p' ) !== false && is_numeric( substr( $value, 1 ) ) ) {
+ $this->precision = strval( substr( $value, 1 ) );
+ unset( $parts[$key] );
+ }
+ }
+
+ // Rebuild formatstring without a possible p element to ensure other
+ // options can be used in combination such as -n-p2 etc.
+ return implode( '-', $parts );
+ }
+
+ private function getNumberFormatter() {
+
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::USER_LANGUAGE,
+ $this->getOption( self::OPT_USER_LANGUAGE )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ self::THOUSANDS_SEPARATOR,
+ $this->getOption( self::THOUSANDS_SEPARATOR )
+ );
+
+ $this->intlNumberFormatter->setOption(
+ self::DECIMAL_SEPARATOR,
+ $this->getOption( self::DECIMAL_SEPARATOR )
+ );
+
+ return $this->intlNumberFormatter;
+ }
+
+ private function findPreferredLanguageFrom( &$formatstring ) {
+ // Localized preferred user language
+ if ( strpos( $formatstring, 'LOCL' ) !== false && ( $languageCode = Localizer::getLanguageCodeFrom( $formatstring ) ) !== false ) {
+ $this->intlNumberFormatter->setOption(
+ IntlNumberFormatter::PREFERRED_LANGUAGE,
+ $languageCode
+ );
+ }
+
+ // Remove any remaining
+ $formatstring = str_replace( [ '#LOCL', 'LOCL' ], '', $formatstring );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php
new file mode 100644
index 00000000..7c255aaa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_PropertyList.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements special processing suitable for defining the list
+ * of properties that is required for SMWRecordValue objects. The input is a
+ * plain semicolon-separated list of property names, optionally with the
+ * namespace prefix.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWPropertyListValue extends SMWDataValue {
+ /**
+ * List of properte data items that are stored.
+ * @var array of SMWDIProperty
+ */
+ protected $m_diProperties;
+
+ protected function parseUserValue( $value ) {
+ global $wgContLang;
+
+ $this->m_diProperties = [];
+ $stringValue = '';
+ $valueList = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
+ foreach ( $valueList as $propertyName ) {
+ $propertyNameParts = explode( ':', $propertyName, 2 );
+ if ( count( $propertyNameParts ) > 1 ) {
+ $namespace = smwfNormalTitleText( $propertyNameParts[0] );
+ $propertyName = $propertyNameParts[1];
+ $propertyNamespace = $wgContLang->getNsText( SMW_NS_PROPERTY );
+ if ( $namespace != $propertyNamespace ) {
+ $this->addErrorMsg( [ 'smw_wrong_namespace', $propertyNamespace ] );
+ }
+ }
+
+ $propertyName = smwfNormalTitleText( $propertyName );
+
+ try {
+ $diProperty = SMW\DIProperty::newFromUserLabel( $propertyName );
+ } catch ( SMWDataItemException $e ) {
+ $diProperty = new SMW\DIProperty( 'Error' );
+ $this->addErrorMsg( [ 'smw_noproperty', $propertyName ] );
+ }
+
+ $this->m_diProperties[] = $diProperty;
+ $stringValue .= ( $stringValue ? ';' : '' ) . $diProperty->getKey();
+ }
+
+ $this->m_dataitem = new SMWDIBlob( $stringValue );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ *
+ * @param $dataitem SMWDataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( !$dataItem instanceof SMWDIBlob ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_diProperties = [];
+
+ foreach ( explode( ';', $dataItem->getString() ) as $propertyKey ) {
+ $property = null;
+
+ try {
+ $property = new SMW\DIProperty( $propertyKey );
+ } catch ( SMWDataItemException $e ) {
+ $property = new SMW\DIProperty( 'Error' );
+ $this->addErrorMsg( [ 'smw-datavalue-propertylist-invalid-property-key', $dataItem->getString(), $propertyKey ] );
+ }
+
+ if ( $property instanceof SMWDIProperty ) {
+ // Find a possible redirect
+ $this->m_diProperties[] = $property->getRedirectTarget();
+ }
+ }
+
+ $this->m_caption = false;
+
+ return true;
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ return ( $this->m_caption !== false ) ? $this->m_caption : $this->makeOutputText( 2, $linked );
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ return ( $this->m_caption !== false ) ? $this->m_caption : $this->makeOutputText( 3, $linker );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->makeOutputText( 2, $linked );
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->makeOutputText( 3, $linker );
+ }
+
+ public function getWikiValue() {
+ return $this->makeOutputText( 4 );
+ }
+
+ public function getPropertyDataItems() {
+ return $this->m_diProperties;
+ }
+
+////// Internal helper functions
+
+ protected function makeOutputText( $type, $linker = null ) {
+ if ( !$this->isValid() ) {
+ return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText();
+ }
+ $result = '';
+ $sep = ( $type == 4 ) ? '; ' : ', ';
+ foreach ( $this->m_diProperties as $diProperty ) {
+ if ( $result !== '' ) {
+ $result .= $sep;
+ }
+ $propertyValue = \SMW\DataValueFactory::getInstance()->newDataValueByItem( $diProperty, null );
+ $result .= $this->makeValueOutputText( $type, $propertyValue, $linker );
+ }
+ return $result;
+ }
+
+ protected function makeValueOutputText( $type, $propertyValue, $linker ) {
+ switch ( $type ) {
+ case 0:
+ return $propertyValue->getShortWikiText( $linker );
+ case 1:
+ return $propertyValue->getShortHTMLText( $linker );
+ case 2:
+ return $propertyValue->getLongWikiText( $linker );
+ case 3:
+ return $propertyValue->getLongHTMLText( $linker );
+ case 4:
+ return $propertyValue->getWikiValue();
+ }
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php
new file mode 100644
index 00000000..75e1dc57
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Quantity.php
@@ -0,0 +1,218 @@
+<?php
+
+use SMW\DataValues\Number\UnitConverter;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements unit support custom units, for which users have
+ * provided linear conversion factors within the wiki. Those user settings
+ * are retrieved from a property page associated with this object.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWQuantityValue extends SMWNumberValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_qty';
+
+ /**
+ * Array with format (canonical unit ID string) => (conversion factor)
+ * @var float[]|bool
+ */
+ protected $m_unitfactors = false;
+
+ /**
+ * Array with format (normalised unit string) => (canonical unit ID string)
+ * @var string[]|bool
+ */
+ protected $m_unitids = false;
+
+ /**
+ * Ordered array of (normalized) units that should be displayed in tooltips, etc.
+ * @var string[]|bool
+ */
+ protected $m_displayunits = false;
+
+ /**
+ * Main unit in canonical form (recognised by the conversion factor 1)
+ * @var string|bool
+ */
+ protected $m_mainunit = false;
+
+ protected function convertToMainUnit( $number, $unit ) {
+ $this->initConversionData();
+
+ if ( array_key_exists( $unit, $this->m_unitids ) ) {
+ $this->m_unitin = $this->m_unitids[$unit];
+ assert( $this->m_unitfactors[$this->m_unitin] != 0 /* Should be filtered by initConversionData() */ );
+ $this->m_dataitem = new SMWDINumber( $number / $this->m_unitfactors[$this->m_unitin], $this->m_typeid );
+ return true;
+ } else { // unsupported unit
+ return false;
+ }
+ }
+
+ protected function makeConversionValues() {
+ if ( $this->m_unitvalues !== false ) {
+ return; // do this only once
+ }
+
+ $this->m_unitvalues = [];
+
+ if ( !$this->isValid() ) {
+ return;
+ }
+
+ $this->initDisplayData();
+
+ if ( count( $this->m_displayunits ) == 0 ) { // no display units, just show all
+ foreach ( $this->m_unitfactors as $unit => $factor ) {
+ if ( $unit !== '' ) { // filter out the empty fallback unit that is always there
+ $this->m_unitvalues[$unit] = $this->m_dataitem->getNumber() * $factor;
+ }
+ }
+ } else {
+ foreach ( $this->m_displayunits as $unit ) {
+ /// NOTE We keep non-ID units unless the input unit is used, so display units can be used to pick
+ /// the preferred form of a unit. Doing this requires us to recompute the conversion values whenever
+ /// the m_unitin changes.
+ $unitkey = ( $this->m_unitids[$unit] == $this->m_unitin ) ? $this->m_unitids[$unit] : $unit;
+ $this->m_unitvalues[$unitkey] = $this->m_dataitem->getNumber() * $this->m_unitfactors[$this->m_unitids[$unit]];
+ }
+ }
+ }
+
+ protected function makeUserValue() {
+
+ // The normalised string of a known unit to use for printouts
+ $printunit = false;
+ $unitfactor = 1;
+
+ // Check if a known unit is given as outputformat:
+ if ( ( $this->m_outformat ) && ( $this->m_outformat != '-' ) &&
+ ( $this->m_outformat != '-n' ) && ( $this->m_outformat != '-u' ) ) { // first try given output unit
+ $wantedunit = $this->normalizeUnit( $this->m_outformat );
+ if ( array_key_exists( $wantedunit, $this->m_unitids ) ) {
+ $printunit = $wantedunit;
+ }
+ }
+
+ // Alternatively, try to use the main display unit as a default:
+ if ( $printunit === false ) {
+ $this->initDisplayData();
+ if ( count( $this->m_displayunits ) > 0 ) {
+ $printunit = reset( $this->m_displayunits );
+ }
+ }
+ // Finally, fall back to main unit:
+ if ( $printunit === false ) {
+ $printunit = $this->getUnit();
+ }
+
+ $asPrefix = isset( $this->prefixalUnitPreference[$printunit] ) && $this->prefixalUnitPreference[$printunit];
+ $this->m_unitin = isset( $this->m_unitids[$printunit] ) ? $this->m_unitids[$printunit] : 0;
+
+ // This array depends on m_unitin if displayunits were used, better
+ // invalidate it here
+ $this->m_unitvalues = false;
+ $this->m_caption = '';
+
+ if ( isset( $this->m_unitfactors[$this->m_unitin] ) ) {
+ $unitfactor = $this->m_unitfactors[$this->m_unitin];
+ }
+
+ $value = $this->m_dataitem->getNumber() * $unitfactor;
+
+ // -u is the format for displaying the unit only
+ if ( $this->m_outformat != '-u' ) {
+ $this->m_caption .= ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ? $this->getLocalizedFormattedNumber( $value ) : $this->getNormalizedFormattedNumber( $value ) );
+ }
+
+ // -n is the format for displaying the number only
+ if ( ( $printunit !== '' ) && ( $this->m_outformat != '-n' ) ) {
+
+ $sep = '';
+
+ if ( $this->m_outformat != '-u' ) {
+ $sep = ( $this->m_outformat != '-' ? '&#160;' : ' ' );
+ }
+
+ $this->m_caption = $asPrefix ? $printunit . $sep . $this->m_caption : $this->m_caption . $sep . $printunit;
+ }
+ }
+
+ public function getUnitList() {
+ $this->initConversionData();
+ return array_keys( $this->m_unitfactors );
+ }
+
+ public function getUnit() {
+ $this->initConversionData();
+ return $this->m_mainunit;
+ }
+
+/// The remaining functions are relatively "private" but are kept protected since
+/// subclasses might exploit this to, e.g., "fake" conversion factors instead of
+/// getting them from the database. A cheap way of making built-in types.
+
+ /**
+ * This method initializes $m_unitfactors, $m_unitids, and $m_mainunit.
+ */
+ protected function initConversionData() {
+
+ if ( $this->m_unitids !== false ) {
+ return;
+ }
+
+ $unitConverter = new UnitConverter( $this );
+ $unitConverter->initConversionData( $this->m_property );
+
+ if ( $unitConverter->getErrors() !== [] ) {
+ foreach ( $unitConverter->getErrors() as $error ) {
+ $this->addErrorMsg(
+ $error,
+ Message::TEXT,
+ Message::USER_LANGUAGE
+ );
+ }
+ }
+
+ $this->m_unitids = $unitConverter->getUnitIds();
+ $this->m_unitfactors = $unitConverter->getUnitFactors();
+ $this->m_mainunit = $unitConverter->getMainUnit();
+ $this->prefixalUnitPreference = $unitConverter->getPrefixalUnitPreference();
+ }
+
+ /**
+ * This method initializes $m_displayunits.
+ */
+ protected function initDisplayData() {
+ if ( $this->m_displayunits !== false ) {
+ return; // do the below only once
+ }
+ $this->initConversionData(); // needed to normalise unit strings
+ $this->m_displayunits = [];
+
+ if ( is_null( $this->m_property ) || is_null( $this->m_property->getDIWikiPage() ) ) {
+ return;
+ }
+
+ $units = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getDisplayUnits(
+ $this->getProperty()
+ );
+
+ foreach ( $units as $unit ) {
+ $unit = $this->normalizeUnit( $unit );
+ if ( array_key_exists( $unit, $this->m_unitids ) ) {
+ $this->m_displayunits[] = $unit; // do not avoid duplicates, users can handle this
+ } // note: we ignore unsuppported units -- no way to display them
+ }
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php
new file mode 100644
index 00000000..f4bab7e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Record.php
@@ -0,0 +1,300 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\AbstractMultiValue;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDataItem as DataItem;
+use SMWDIContainer as DIContainer;
+
+/**
+ * SMWDataValue implements the handling of small sets of property-value pairs.
+ * The declaration of Records in SMW uses the order of values to encode the
+ * property that should be used, so the user only needs to enter a list of
+ * values. Internally, however, the property-value assignments are not stored
+ * with a particular order; they will only be ordered for display, following
+ * the declaration. This is why it is not supported to have Records using the
+ * same property for more than one value.
+ *
+ * The class uses DIContainer objects to return its inner state. See the
+ * documentation for DIContainer for details on how this "pseudo" data
+ * encapsulated many property assignments. Such data is stored internally
+ * like a page with various property-value assignments. Indeed, record values
+ * can be created from DIWikiPage objects (the missing information will
+ * be fetched from the store).
+ *
+ * @todo Enforce limitation of maximal number of values.
+ * @todo Enforce uniqueness of properties in declaration.
+ * @todo Complete internationalisation.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWRecordValue extends AbstractMultiValue {
+
+ /// cache for properties for the fields of this data value
+ protected $m_diProperties = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( '_rec' );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return DIProperty[]|null
+ */
+ public function getProperties() {
+ return $this->m_diProperties;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $value
+ *
+ * @return array
+ */
+ public function getValuesFromString( $value ) {
+ // #664 / T17732
+ $value = str_replace( "\;", "-3B", $value );
+
+ // Bug 21926 / T23926
+ // Values that use html entities are encoded with a semicolon
+ $value = htmlspecialchars_decode( $value, ENT_QUOTES );
+ $values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
+
+ return str_replace( "-3B", ";", $values );
+ }
+
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ return;
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData( $value );
+ $sortKeys = [];
+
+ $values = $this->getValuesFromString( $value );
+ $valueIndex = 0; // index in value array
+ $propertyIndex = 0; // index in property list
+ $empty = true;
+
+ foreach ( $this->getPropertyDataItems() as $diProperty ) {
+
+ if ( !array_key_exists( $valueIndex, $values ) || $this->getErrors() !== [] ) {
+ break; // stop if there are no values left
+ }
+
+ // generating the DVs:
+ if ( ( $values[$valueIndex] === '' ) || ( $values[$valueIndex] == '?' ) ) { // explicit omission
+ $valueIndex++;
+ } else {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $diProperty,
+ $values[$valueIndex],
+ false,
+ $containerSemanticData->getSubject()
+ );
+
+ if ( $dataValue->isValid() ) { // valid DV: keep
+ $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() );
+ $sortKeys[] = $dataValue->getDataItem()->getSortKey();
+
+ $valueIndex++;
+ $empty = false;
+ } elseif ( ( count( $values ) - $valueIndex ) == ( count( $this->m_diProperties ) - $propertyIndex ) ) {
+ $containerSemanticData->addPropertyObjectValue( $diProperty, $dataValue->getDataItem() );
+ $this->addError( $dataValue->getErrors() );
+ ++$valueIndex;
+ }
+ }
+ ++$propertyIndex;
+ }
+
+ if ( $empty && $this->getErrors() === [] ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ }
+
+ // Remember the data to extend the sortkey
+ $containerSemanticData->setExtensionData( 'sort.data', implode( ';', $sortKeys ) );
+
+ $this->m_dataitem = new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem DataItem
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+ if ( $dataItem->getDIType() == DataItem::TYPE_CONTAINER ) {
+ $this->m_dataitem = $dataItem;
+ return true;
+ } elseif ( $dataItem->getDIType() == DataItem::TYPE_WIKIPAGE ) {
+ $semanticData = new ContainerSemanticData( $dataItem );
+ $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
+ $this->m_dataitem = new DIContainer( $semanticData );
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getShortWikiText( $linked = null ) {
+ if ( $this->m_caption !== false ) {
+ return $this->m_caption;
+ }
+ return $this->makeOutputText( 0, $linked );
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+ if ( $this->m_caption !== false ) {
+ return $this->m_caption;
+ }
+ return $this->makeOutputText( 1, $linker );
+ }
+
+ public function getLongWikiText( $linked = null ) {
+ return $this->makeOutputText( 2, $linked );
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+ return $this->makeOutputText( 3, $linker );
+ }
+
+ public function getWikiValue() {
+ return $this->makeOutputText( 4 );
+ }
+
+ /**
+ * Make sure that the content is reset in this case.
+ * @todo This is not a full reset yet (the case that property is changed after a value
+ * was set does not occur in the normal flow of things, hence this has low priority).
+ */
+ public function setProperty( DIProperty $property ) {
+ parent::setProperty( $property );
+ $this->m_diProperties = null;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIProperty[] $properties
+ */
+ public function setFieldProperties( array $properties ) {
+ foreach ( $properties as $property ) {
+ if ( $property instanceof DIProperty ) {
+ $this->m_diProperties[] = $property;
+ }
+ }
+ }
+
+ /**
+ * @since 1.6
+ *
+ * {@inheritDoc}
+ */
+ public function getDataItems() {
+ return parent::getDataItems();
+ }
+
+ /**
+ * Return the array (list) of properties that the individual entries of
+ * this datatype consist of.
+ *
+ * @since 1.6
+ *
+ * @todo I18N for error message.
+ *
+ * @return array of DIProperty
+ */
+ public function getPropertyDataItems() {
+
+ if ( $this->m_diProperties !== null ) {
+ return $this->m_diProperties;
+ }
+
+ $this->m_diProperties = $this->getFieldProperties( $this->m_property );
+
+ if ( $this->m_diProperties === [] ) { // TODO internalionalize
+ $this->addError( 'The list of properties to be used for the data fields has not been specified properly.' );
+ }
+
+ return $this->m_diProperties;
+ }
+
+ protected function makeOutputText( $type = 0, $linker = null ) {
+ if ( !$this->isValid() ) {
+ return ( ( $type == 0 ) || ( $type == 1 ) ) ? '' : $this->getErrorText();
+ }
+
+ $result = '';
+ $i = 0;
+ foreach ( $this->getPropertyDataItems() as $propertyDataItem ) {
+ if ( $i == 1 ) {
+ $result .= ( $type == 4 ) ? '; ' : ' (';
+ } elseif ( $i > 1 ) {
+ $result .= ( $type == 4 ) ? '; ' : ', ';
+ }
+ ++$i;
+ $propertyValues = $this->m_dataitem->getSemanticData()->getPropertyValues( $propertyDataItem ); // combining this with next line violates PHP strict standards
+ $dataItem = reset( $propertyValues );
+ if ( $dataItem !== false ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $propertyDataItem );
+ $result .= $this->makeValueOutputText( $type, $dataValue, $linker );
+ } else {
+ $result .= '?';
+ }
+ }
+ if ( ( $i > 1 ) && ( $type != 4 ) ) {
+ $result .= ')';
+ }
+
+ return $result;
+ }
+
+ protected function makeValueOutputText( $type, SMWDataValue $dataValue, $linker ) {
+ switch ( $type ) {
+ case 0:
+ return $dataValue->getShortWikiText( $linker );
+ case 1:
+ return $dataValue->getShortHTMLText( $linker );
+ case 2:
+ return $dataValue->getShortWikiText( $linker );
+ case 3:
+ return $dataValue->getShortHTMLText( $linker );
+ case 4:
+ return str_replace( ";", "\;", $dataValue->getWikiValue() );
+ }
+ }
+
+ private function newContainerSemanticData( $value ) {
+
+ if ( $this->m_contextPage === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ $containerSemanticData->skipAnonymousCheck();
+ } else {
+ $subobjectName = '_' . hash( 'md4', $value, false ); // md4 is probably fastest of PHP's hashes
+
+ $subject = new DIWikiPage(
+ $this->m_contextPage->getDBkey(),
+ $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getInterwiki(),
+ $subobjectName
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return $containerSemanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php
new file mode 100644
index 00000000..9a6962cd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_Time.php
@@ -0,0 +1,699 @@
+<?php
+
+use SMW\DataValues\Time\Components;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Localizer;
+use SMWDITime as DITime;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue captures values of dates and times, in many formats,
+ * throughout history and pre-history. The implementation can handle dates
+ * across history with full precision for storing, and substantial precision
+ * for sorting and querying. The range of supported past dates should encompass
+ * the Beginning of Time according to most of today's theories. The range of
+ * supported future dates is limited more strictly, but it does also allow
+ * year numbers in the order of 10^9.
+ *
+ * The implementation notices and stores whether parts of a date/time have been
+ * omitted (as in "2008" or "May 2007"). For all exporting and sorting
+ * purposes, incomplete dates are completed with defaults (usually using the
+ * earliest possible time, i.e. interpreting "2008" as "Jan 1 2008 00:00:00").
+ * The information on what was unspecified is kept internally for improving
+ * behavior e.g. for outputs (defaults are not printed when querying for a
+ * value). This largely uses the precision handling of DITime.
+ *
+ *
+ * Date formats
+ *
+ * Dates can be given in many formats, using numbers, month names, and
+ * abbreviated month names. The preferred interpretation of ambiguous dates
+ * ("1 2 2008" or even "1 2 3 BC") is controlled by the language file, as is
+ * the local naming of months. English month names are always supported.
+ *
+ * Dates can be given in Gregorian or Julian calendar, set by the token "Jl"
+ * or "Gr" in the input. If neither is set, a default is chosen: inputs after
+ * October 15, 1582 (the time when the Gregorian calendar was first inaugurated
+ * in some parts of the world) are considered Gr, earlier inputs are considered
+ * Jl. In addition to Jl and Gr, we support "OS" (Old Style English dates that
+ * refer to the use of Julian calendar with a displaced change of year on March
+ * 24), JD (direct numerical input in Julian Day notation), and MJD (direct
+ * numerical input in Modified Julian Day notation as used in aviation and
+ * space flight).
+ *
+ * The class does not support the input of negative year numbers but uses the
+ * markers "BC"/"BCE" and "AD"/"CE" instead. There is no year 0 in Gregorian or
+ * Julian calendars, but the class graciously considers this input to mean year
+ * 1 BC(E).
+ *
+ * For prehisoric dates before 9999 BC(E) only year numbers are allowed
+ * (nothing else makes much sense). At this time, the years of Julian and
+ * Gregorian calendar still overlap significantly, so the transition to a
+ * purely solar annotation of prehistoric years is smooth. Technically, the
+ * class will consider prehistoric dates as Gregorian but very ancient times
+ * may be interpreted as desired (probably with reference to a physical notion
+ * of time that is not dependent on revolutions of earth around the sun).
+ *
+ *
+ * Time formats
+ *
+ * Times can be in formats like "23:12:45" and "12:30" possibly with additional
+ * modifiers "am" or "pm". Timezones are supported: the class knows many
+ * international timezone monikers (e.g. CET or GMT) and also allows time
+ * offsets directly after a time (e.g. "10:30-3:30" or "14:45:23+2"). Such
+ * offsets always refer to UTC. Timezones are only used on input and are not
+ * stored as part of the value.
+ *
+ * Time offsets take leap years into account, e.g. the date
+ * "Feb 28 2004 23:00+2:00" is equivalent to "29 February 2004 01:00:00", while
+ * "Feb 28 1900 23:00+2:00" is equivalent to "1 March 1900 01:00:00".
+ *
+ * Military time format is supported. This consists of 4 or 6 numeric digits
+ * followed by a one-letter timezone code (e.g. 1240Z is equivalent to 12:40
+ * UTC).
+ *
+ *
+ * I18N
+ *
+ * Currently, neither keywords like "BCE", "Jl", or "pm", nor timezone monikers
+ * are internationalized. Timezone monikers may not require this, other than
+ * possibly for Cyrillic (added when needed). Month names are fully
+ * internationalized, but English names and abbreviations will also work in all
+ * languages. The class also supports ordinal day-of-month annotations like
+ * "st" and "rd", again only for English.
+ *
+ * I18N includes the preferred order of dates, e.g. to interpret "5 6 2010".
+ *
+ * @todo Theparsing process can encounter many kinds of well-defined problems
+ * but uses only one error message. More detailed reporting should be done.
+ * @todo Try to reuse more of MediaWiki's records, e.g. to obtain month names
+ * or to format dates. The problem is that MW is based on SIO timestamps that
+ * don't extend to very ancient or future dates, and that MW uses PHP functions
+ * that are bound to UNIX time.
+ *
+ * @author Markus Krötzsch
+ * @author Fabian Howahl
+ * @author Terry A. Hurlbut
+ * @ingroup SMWDataValues
+ */
+class SMWTimeValue extends SMWDataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_dat';
+
+ protected $m_dataitem_greg = null;
+ protected $m_dataitem_jul = null;
+
+ protected $m_wikivalue; // a suitable wiki input value
+
+ /**
+ * The following are constant (array-valued constants are not supported, hence
+ * the declaration as private static variable):
+ *
+ * @var array
+ */
+ protected static $m_formats = [
+ SMW_Y => [ 'y' ],
+ SMW_YM => [ 'y', 'm' ],
+ SMW_MY => [ 'm', 'y' ],
+ SMW_YDM => [ 'y', 'd', 'm' ],
+ SMW_YMD => [ 'y', 'm', 'd' ],
+ SMW_DMY => [ 'd', 'm', 'y' ],
+ SMW_MDY => [ 'm', 'd', 'y' ]
+ ];
+
+ /**
+ * Moment of switchover to Gregorian calendar.
+ */
+ const J1582 = 2299160.5;
+
+ /**
+ * Offset of Julian Days for Modified JD inputs.
+ */
+ const MJD_EPOCH = 2400000.5;
+
+ /**
+ * The year before which we do not accept anything but year numbers and
+ * largely discourage calendar models.
+ */
+ const PREHISTORY = -10000;
+
+ /**
+ * @see DataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+
+ $value = Localizer::convertDoubleWidth( $value );
+
+ $this->m_wikivalue = $value;
+ $this->m_dataitem = null;
+
+ // Store the caption now
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ $timeValueParser = $this->dataValueServiceFactory->getValueParser(
+ $this
+ );
+
+ $timeValueParser->clearErrors();
+
+ // Parsing is bound to the content language otherwise any change of a user
+ // preferred user language would negate the parsing results
+ $timeValueParser->setLanguageCode(
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ if ( $this->isYear( $value ) ) {
+ try {
+ $this->m_dataitem = new DITime( $this->getCalendarModel( null, $value, null, null ), $value );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid', $value, $e->getMessage() ] );
+ }
+ } elseif ( $this->isTimestamp( $value ) ) {
+ $this->m_dataitem = DITime::newFromTimestamp( $value );
+ } elseif ( ( $components = $timeValueParser->parse( $value ) ) ) {
+
+ $calendarmodel = $components->get( 'calendarmodel' );
+
+ if ( ( $calendarmodel == 'JD' ) || ( $calendarmodel == 'MJD' ) ) {
+ $this->setDateFromJD( $components );
+ } else {
+ $this->setDateFromParsedValues( $components );
+ }
+ }
+
+ foreach ( $timeValueParser->getErrors() as $err ) {
+ $this->addErrorMsg( $err );
+ }
+
+ // Make sure that m_dataitem is set in any case
+ if ( $this->m_dataitem === null ) {
+ $this->m_dataitem = new DITime( DITime::CM_GREGORIAN, 32202 );
+ }
+ }
+
+ /**
+ * Validate and interpret the date components as retrieved when parsing
+ * a user input. The method takes care of guessing how a list of values
+ * such as "10 12 13" is to be interpreted using the current language
+ * settings. The result is stored in the call-by-ref parameter
+ * $date that uses keys 'y', 'm', 'd' and contains the respective
+ * numbers as values, or false if not specified. If errors occur, error
+ * messages are added to the objects list of errors, and false is
+ * returned. Otherwise, true is returned.
+ *
+ * @param $datecomponents array of strings that might belong to the specification of a date
+ * @param $date array set to result
+ *
+ * @return boolean stating if successful
+ */
+ protected function interpretDateComponents( $datecomponents, &$date ) {
+
+ // The following code segment creates a bit vector to encode
+ // which role each digit of the entered date can take (day,
+ // year, month). The vector starts with 1 and contains three
+ // bits per date component, set ot true whenever this component
+ // could be a month, a day, or a year (this is the order).
+ // Examples:
+ // 100 component could only be a month
+ // 010 component could only be a day
+ // 001 component could only be a year
+ // 011 component could be a day or a year but no month etc.
+ // For three components, we thus get a 10 digit bit vector.
+ $datevector = 1;
+ $propercomponents = [];
+ $justfounddash = true; // avoid two dashes in a row, or dashes at the end
+ $error = false;
+ $numvalue = 0;
+ foreach ( $datecomponents as $component ) {
+ if ( $component == "-" ) {
+ if ( $justfounddash ) {
+ $error = true;
+ break;
+ }
+ $justfounddash = true;
+ } else {
+ $justfounddash = false;
+ $datevector = ( $datevector << 3 ) | $this->checkDateComponent( $component, $numvalue );
+ $propercomponents[] = $numvalue;
+ }
+ }
+
+ if ( ( $error ) || ( $justfounddash ) || ( count( $propercomponents ) == 0 ) || ( count( $propercomponents ) > 3 ) ) {
+
+ $msgKey = 'smw-datavalue-time-invalid-date-components';
+
+ if ( $justfounddash ) {
+ $msgKey .= '-dash';
+ } elseif ( count( $propercomponents ) == 0 ) {
+ $msgKey .= '-empty';
+ } elseif ( count( $propercomponents ) > 3 ) {
+ $msgKey .= '-three';
+ } else{
+ $msgKey .= '-common';
+ }
+
+ $this->addErrorMsg( [ $msgKey, $this->m_wikivalue ] );
+ return false;
+ }
+
+ // Now use the bitvector to find the preferred interpretation of the date components:
+ $dateformats = Localizer::getInstance()->getLang( $this->getOption( self::OPT_CONTENT_LANGUAGE ) )->getDateFormats();
+ $date = [ 'y' => false, 'm' => false, 'd' => false ];
+
+ foreach ( $dateformats[count( $propercomponents ) - 1] as $formatvector ) {
+ if ( !( ~$datevector & $formatvector ) ) { // check if $formatvector => $datevector ("the input supports the format")
+ $i = 0;
+ foreach ( self::$m_formats[$formatvector] as $fieldname ) {
+ $date[$fieldname] = $propercomponents[$i];
+ $i += 1;
+ }
+ break;
+ }
+ }
+
+ if ( $date['y'] === false ) { // no band matches the entered date
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-date-components-sequence', $this->m_wikivalue ] );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Initialise data from an anticipated JD value.
+ */
+ private function setDateFromJD( $components ) {
+
+ $datecomponents = $components->get( 'datecomponents' );
+ $calendarmodel = $components->get( 'calendarmodel' );
+ $era = $components->get( 'era' );
+ $hours = $components->get( 'hours' );
+
+ if ( ( $era === false ) && ( $hours === false ) && ( $components->get( 'timeoffset' ) == 0 ) ) {
+ try {
+ $jd = floatval( isset( $datecomponents[1] ) ? $datecomponents[0] . '.' . $datecomponents[1] : $datecomponents[0] );
+ if ( $calendarmodel == 'MJD' ) {
+ $jd += self::MJD_EPOCH;
+ }
+ $this->m_dataitem = DITime::newFromJD( $jd, DITime::CM_GREGORIAN, DITime::PREC_YMDT, $components->get( 'timezone' ) );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, $e->getMessage() ] );
+ }
+ } else {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, "NO_EXCEPTION" ] );
+ }
+ }
+
+ /**
+ * Initialise data from the provided intermediate results after
+ * parsing, assuming that a conventional date notation is used.
+ * If errors occur, error messages are added to the objects list of
+ * errors, and false is returned. Otherwise, true is returned.
+ *
+ * @param $datecomponents array of strings that might belong to the specification of a date
+ * @param $calendarmodesl string if model was set in input, otherwise false
+ * @param $era string '+' or '-' if provided, otherwise false
+ * @param $hours integer value between 0 and 24
+ * @param $minutes integer value between 0 and 59
+ * @param $seconds integer value between 0 and 59, or false if not given
+ * @param $timeoffset double value for time offset (e.g. 3.5), or false if not given
+ *
+ * @return boolean stating if successful
+ */
+ protected function setDateFromParsedValues( $components ) {
+
+ $datecomponents = $components->get( 'datecomponents' );
+ $calendarmodel = $components->get( 'calendarmodel' );
+ $era = $components->get( 'era' );
+ $hours = $components->get( 'hours' );
+ $minutes = $components->get( 'minutes' );
+ $seconds = $components->get( 'seconds' );
+ $microseconds = $components->get( 'microseconds' );
+ $timeoffset = $components->get( 'timeoffset' );
+ $timezone = $components->get( 'timezone' );
+
+ $date = false;
+
+ if ( !$this->interpretDateComponents( $datecomponents, $date ) ) {
+ return false;
+ }
+
+ // Handle BC: the year is negative.
+ if ( ( $era == '-' ) && ( $date['y'] > 0 ) ) { // see class documentation on BC, "year 0", and ISO conformance ...
+ $date['y'] = -( $date['y'] );
+ }
+
+ // Keep information about the era
+ if ( ( $era == '+' ) && ( $date['y'] > 0 ) ) {
+ $date['y'] = $era . $date['y'];
+ }
+
+ // Old Style is a special case of Julian calendar model where the change of the year was 25 March:
+ if ( ( $calendarmodel == 'OS' ) &&
+ ( ( $date['m'] < 3 ) || ( ( $date['m'] == 3 ) && ( $date['d'] < 25 ) ) ) ) {
+ $date['y']++;
+ }
+
+ $calmod = $this->getCalendarModel( $calendarmodel, $date['y'], $date['m'], $date['d'] );
+
+ try {
+ $this->m_dataitem = new DITime( $calmod, $date['y'], $date['m'], $date['d'], $hours, $minutes, $seconds . '.' . $microseconds, $timezone );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid', $this->m_wikivalue, $e->getMessage() ] );
+ return false;
+ }
+
+ // Having more than years or specifying a calendar model does
+ // not make sense for prehistoric dates, and our calendar
+ // conversion would not be reliable if JD numbers get too huge:
+ if ( ( $date['y'] <= self::PREHISTORY ) &&
+ ( ( $this->m_dataitem->getPrecision() > DITime::PREC_Y ) || ( $calendarmodel !== false ) ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-prehistoric', $this->m_wikivalue ] );
+ return false;
+ }
+
+ if ( $timeoffset != 0 ) {
+ $newjd = $this->m_dataitem->getJD() - $timeoffset / 24;
+ try {
+ $this->m_dataitem = DITime::newFromJD( $newjd, $calmod, $this->m_dataitem->getPrecision(), $timezone );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw-datavalue-time-invalid-jd', $this->m_wikivalue, $e->getMessage() ] );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check which roles a string component might play in a date, and
+ * set the call-by-ref parameter to the proper numerical
+ * representation. The component string has already been normalized to
+ * be either a plain number, a month name, or a plain number with "d"
+ * pre-pended. The result is a bit vector to indicate the possible
+ * interpretations.
+ *
+ * @param $component string
+ * @param $numvalue integer representing the components value
+ *
+ * @return integer that encodes a three-digit bit vector
+ */
+ protected static function checkDateComponent( $component, &$numvalue ) {
+
+ if ( $component === '' ) { // should not happen
+ $numvalue = 0;
+ return 0;
+ } elseif ( is_numeric( $component ) ) {
+ $numvalue = intval( $component );
+ if ( ( $numvalue >= 1 ) && ( $numvalue <= 12 ) ) {
+ return SMW_DAY_MONTH_YEAR; // can be a month, day or year
+ } elseif ( ( $numvalue >= 1 ) && ( $numvalue <= 31 ) ) {
+ return SMW_DAY_YEAR; // can be day or year
+ } else { // number can just be a year
+ return SMW_YEAR;
+ }
+ } elseif ( $component { 0 } == 'd' ) { // already marked as day
+ if ( is_numeric( substr( $component, 1 ) ) ) {
+ $numvalue = intval( substr( $component, 1 ) );
+ return ( ( $numvalue >= 1 ) && ( $numvalue <= 31 ) ) ? SMW_DAY : 0;
+ } else {
+ return 0;
+ }
+ }
+
+ $monthnum = array_search( $component, Components::$monthsShort );
+
+ if ( $monthnum !== false ) {
+ $numvalue = $monthnum + 1;
+ return SMW_MONTH;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Determine the calendar model under which an input should be
+ * interpreted based on the given input data.
+ *
+ * @param $presetmodel mixed string related to a user input calendar model (OS, Jl, Gr) or false
+ * @param $year integer of the given year (adjusted for BC(E), i.e. possibly negative)
+ * @param $month mixed integer of the month or false
+ * @param $day mixed integer of the day or false
+ *
+ * @return integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ */
+ protected function getCalendarModel( $presetmodel, $year, $month, $day ) {
+
+ // Old Style is a notational convention of Julian dates only
+ if ( $presetmodel == 'OS' ) {
+ $presetmodel = 'Jl';
+ }
+
+ if ( $presetmodel === 'Gr' || $presetmodel === 'GR' ) {
+ return DITime::CM_GREGORIAN;
+ } elseif ( $presetmodel === 'Jl' || $presetmodel === 'JL' ) {
+ return DITime::CM_JULIAN;
+ }
+
+ if ( ( $year > 1582 ) ||
+ ( ( $year == 1582 ) && ( $month > 10 ) ) ||
+ ( ( $year == 1582 ) && ( $month == 10 ) && ( $day > 4 ) ) ) {
+ return DITime::CM_GREGORIAN;
+ } elseif ( $year > self::PREHISTORY ) {
+ return DITime::CM_JULIAN;
+ }
+
+ // Proleptic Julian years at some point deviate from the count of complete
+ // revolutions of the earth around the sun hence assume that earlier
+ // date years are Gregorian (where this effect is very weak). This is
+ // mostly for internal use since we will not allow users to specify
+ // calendar models at this scale
+ return DITime::CM_GREGORIAN;
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem
+ *
+ * {@inheritDoc}
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_TIME ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = false;
+ $this->m_wikivalue = false;
+
+ return true;
+ }
+
+ /**
+ * @see SMWDataValue::getShortWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getShortHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getLongWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see SMWDataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @todo The preferred caption may not be suitable as a wiki value (i.e. not parsable).
+ * @see SMWDataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getWikiValue() {
+ return $this->m_wikivalue ? $this->m_wikivalue : strip_tags( $this->getLongWikiText() );
+ }
+
+ /**
+ * @see SMWDataValue::isNumeric
+ *
+ * {@inheritDoc}
+ */
+ public function isNumeric() {
+ return true;
+ }
+
+ /**
+ * Return the year number in the given calendar model, or false if
+ * this number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates). As everywhere in this class,
+ * there is no year 0.
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ *
+ * @return mixed typically a number but possibly false
+ */
+ public function getYear( $calendarmodel = DITime::CM_GREGORIAN ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return $dataItem->getYear();
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the month number in the given calendar model, or false if
+ * this number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates).
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ * @param $default value to return if month is not set at our level of precision
+ *
+ * @return mixed typically a number but possibly anything given as $default
+ */
+ public function getMonth( $calendarmodel = DITime::CM_GREGORIAN, $default = 1 ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return ( $dataItem->getPrecision() >= DITime::PREC_YM ) ? $dataItem->getMonth() : $default;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the day number in the given calendar model, or false if this
+ * number is not available (typically when attempting to get
+ * prehistoric Julian calendar dates).
+ *
+ * @param $calendarmodel integer either DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ * @param $default value to return if day is not set at our level of precision
+ *
+ * @return mixed typically a number but possibly anything given as $default
+ */
+ public function getDay( $calendarmodel = DITime::CM_GREGORIAN, $default = 1 ) {
+
+ $dataItem = $this->getDataItemForCalendarModel(
+ $calendarmodel
+ );
+
+ if ( $dataItem instanceof DITime ) {
+ return ( $dataItem->getPrecision() >= DITime::PREC_YMD ) ? $dataItem->getDay() : $default;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see TimeValueFormatter::getTimeStringFromDataItem
+ *
+ * @return
+ */
+ public function getTimeString( $default = '00:00:00' ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getTimeString( $default );
+ }
+
+ /**
+ * @deprecated This method is now called getISO8601Date(). It will vanish before SMW 1.7.
+ */
+ public function getXMLSchemaDate( $mindefault = true ) {
+ return $this->getISO8601Date( $mindefault );
+ }
+
+ /**
+ * @see TimeValueFormatter::getISO8601DateFromDataItem
+ *
+ * @param $mindefault boolean determining whether values below the
+ * precision of our input should be completed with minimal or maximal
+ * conceivable values
+ *
+ * @return string
+ */
+ public function getISO8601Date( $mindefault = true ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getISO8601Date( $mindefault );
+ }
+
+ /**
+ * @see TimeValueFormatter::getMediaWikiDateFromDataItem
+ *
+ * @return string
+ */
+ public function getMediaWikiDate() {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->getMediaWikiDate();
+ }
+
+ /**
+ * Get the current data in the specified calendar model. Conversion is
+ * not done for prehistoric dates (where it might lead to precision
+ * errors and produce results that are not meaningful). In this case,
+ * null might be returned if no data in the specified format is
+ * available.
+ *
+ * @param $calendarmodel integer one of DITime::CM_GREGORIAN or DITime::CM_JULIAN
+ *
+ * @return DITime
+ */
+ public function getDataItemForCalendarModel( $calendarmodel ) {
+ if ( $this->m_dataitem->getYear() <= self::PREHISTORY ) {
+ return ( $this->m_dataitem->getCalendarModel() == $calendarmodel ) ? $this->m_dataitem : null;
+ } elseif ( $calendarmodel == DITime::CM_GREGORIAN ) {
+ if ( is_null( $this->m_dataitem_greg ) ) {
+ $this->m_dataitem_greg = $this->m_dataitem->getForCalendarModel( DITime::CM_GREGORIAN );
+ }
+ return $this->m_dataitem_greg;
+ } else {
+ if ( is_null( $this->m_dataitem_jul ) ) {
+ $this->m_dataitem_jul = $this->m_dataitem->getForCalendarModel( DITime::CM_JULIAN );
+ }
+ return $this->m_dataitem_jul;
+ }
+ }
+
+ private function isYear( $value ) {
+ return strpos( $value, ' ' ) === false && is_numeric( strval( $value ) ) && ( strval( $value ) < 0 || strlen( $value ) < 6 );
+ }
+
+ private function isTimestamp( $value ) {
+ // 1200-11-02T12:03:25 or 20120320055913
+ // avoid things like 2458119.500000 (JD)
+ return ( ( strlen( $value ) > 4 && substr( $value, 10, 1 ) === 'T' ) || ( strlen( $value ) == 14 && strpos( $value, '.' ) === false ) ) && wfTimestamp( TS_MW, $value ) !== false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php
new file mode 100644
index 00000000..94724268
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_URI.php
@@ -0,0 +1,379 @@
+<?php
+
+use SMW\Encoder;
+use SMW\Message;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+define( 'SMW_URI_MODE_EMAIL', 1 );
+define( 'SMW_URI_MODE_URI', 3 );
+define( 'SMW_URI_MODE_ANNOURI', 4 );
+define( 'SMW_URI_MODE_TEL', 5 );
+
+/**
+ * This datavalue implements URL/URI/ANNURI/PHONE/EMAIL datavalues suitable for
+ * defining the respective types of properties.
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ * @bug Correctly create safe HTML and Wiki text.
+ */
+class SMWURIValue extends SMWDataValue {
+
+ /**
+ * Raw value without encoding
+ */
+ const VALUE_RAW = 'uri.value.raw';
+
+ /**
+ * The value as returned by getWikitext() and getLongText().
+ * @var string
+ */
+ protected $m_wikitext;
+ /**
+ * One of the basic modes of operation for this class (emails, URL,
+ * telephone number URI, ...).
+ * @var integer
+ */
+ private $m_mode;
+
+ /**
+ * @var boolean
+ */
+ private $showUrlContextInRawFormat = true;
+
+ /**
+ * @var array
+ */
+ private $schemeList = [];
+
+ public function __construct( $typeid ) {
+ parent::__construct( $typeid );
+ switch ( $typeid ) {
+ case '_ema':
+ $this->m_mode = SMW_URI_MODE_EMAIL;
+ break;
+ case '_anu':
+ $this->m_mode = SMW_URI_MODE_ANNOURI;
+ break;
+ case '_tel':
+ $this->m_mode = SMW_URI_MODE_TEL;
+ break;
+ case '__spu':
+ case '_uri':
+ case '_url':
+ default:
+ $this->m_mode = SMW_URI_MODE_URI;
+ break;
+ }
+
+ $this->schemeList = array_flip( $GLOBALS['smwgURITypeSchemeList'] );
+ }
+
+ protected function parseUserValue( $value ) {
+ $value = trim( $value );
+ $this->m_wikitext = $value;
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $this->m_wikitext;
+ }
+
+ $scheme = $hierpart = $query = $fragment = '';
+ if ( $value === '' ) { // do not accept empty strings
+ $this->addErrorMsg( [ 'smw_emptystring' ] );
+ return;
+ }
+
+ switch ( $this->m_mode ) {
+ case SMW_URI_MODE_URI:
+ case SMW_URI_MODE_ANNOURI:
+
+ // Whether the url value was externally encoded or not
+ if ( strpos( $value, "%" ) === false ) {
+ $this->showUrlContextInRawFormat = false;
+ }
+
+ // If somehow the slash was encoded bring into one format
+ $value = str_replace( "%2F", "/", $value );
+
+ $parts = explode( ':', $value, 2 ); // try to split "schema:rest"
+ if ( count( $parts ) == 1 ) { // possibly add "http" as default
+ $value = 'http://' . $value;
+ $parts[1] = $parts[0];
+ $parts[0] = 'http';
+ }
+ // check against blacklist
+ $uri_blacklist = explode( "\n", Message::get( 'smw_uri_blacklist', Message::TEXT, Message::CONTENT_LANGUAGE ) );
+ foreach ( $uri_blacklist as $uri ) {
+ $uri = trim( $uri );
+ if ( $uri !== '' && $uri == mb_substr( $value, 0, mb_strlen( $uri ) ) ) { // disallowed URI!
+ $this->addErrorMsg( [ 'smw_baduri', $value ] );
+ return;
+ }
+ }
+ // decompose general URI components
+ $scheme = $parts[0];
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && !isset( $this->schemeList[$scheme] ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-uri-invalid-scheme', $scheme ] );
+ return;
+ }
+
+ $parts = explode( '?', $parts[1], 2 ); // try to split "hier-part?queryfrag"
+ if ( count( $parts ) == 2 ) {
+ $hierpart = $parts[0];
+ $parts = explode( '#', $parts[1], 2 ); // try to split "query#frag"
+ $query = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ } else {
+ $query = '';
+ $parts = explode( '#', $parts[0], 2 ); // try to split "hier-part#frag"
+ $hierpart = $parts[0];
+ $fragment = ( count( $parts ) == 2 ) ? $parts[1] : '';
+ }
+ // We do not validate the URI characters (the data item will do this) but we do some escaping:
+ // encode most characters, but leave special symbols as given by user:
+ $hierpart = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $hierpart ) );
+ $query = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $query ) );
+ $fragment = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $fragment ) );
+ /// NOTE: we do not support raw [ (%5D) and ] (%5E), although they are needed for ldap:// (but rarely in a wiki)
+ /// NOTE: "+" gets encoded, as it is interpreted as space by most browsers when part of a URL;
+ /// this prevents tel: from working directly, but we have a datatype for this anyway.
+
+ if ( substr( $hierpart, 0, 2 ) === '//' ) {
+ $hierpart = substr( $hierpart, 2 );
+ }
+
+ // #3540
+ if ( $hierpart !== '' && $hierpart[0] === '/' ) {
+ return $this->addErrorMsg( [ 'smw-datavalue-uri-invalid-authority-path-component', $value, $hierpart ] );
+ }
+
+ break;
+ case SMW_URI_MODE_TEL:
+ $scheme = 'tel';
+
+ if ( substr( $value, 0, 4 ) === 'tel:' ) { // accept optional "tel"
+ $value = substr( $value, 4 );
+ $this->m_wikitext = $value;
+ }
+
+ $hierpart = preg_replace( '/(?<=[0-9]) (?=[0-9])/', '\1-\2', $value );
+ $hierpart = str_replace( ' ', '', $hierpart );
+ if ( substr( $hierpart, 0, 2 ) == '00' ) {
+ $hierpart = '+' . substr( $hierpart, 2 );
+ }
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && ( ( strlen( preg_replace( '/[^0-9]/', '', $hierpart ) ) < 6 ) ||
+ ( preg_match( '<[-+./][-./]>', $hierpart ) ) ||
+ ( !self::isValidTelURI( 'tel:' . $hierpart ) ) ) ) { /// TODO: introduce error-message for "bad" phone number
+ $this->addErrorMsg( [ 'smw_baduri', $this->m_wikitext ] );
+ return;
+ }
+ break;
+ case SMW_URI_MODE_EMAIL:
+ $scheme = 'mailto';
+ if ( strpos( $value, 'mailto:' ) === 0 ) { // accept optional "mailto"
+ $value = substr( $value, 7 );
+ $this->m_wikitext = $value;
+ }
+
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && !Sanitizer::validateEmail( $value ) ) {
+ /// TODO: introduce error-message for "bad" email
+ $this->addErrorMsg( [ 'smw_baduri', $value ] );
+ return;
+ }
+ $hierpart = str_replace( [ '%3A', '%2F', '%23', '%40', '%3F', '%3D', '%26', '%25' ], [ ':', '/', '#', '@', '?', '=', '&', '%' ], rawurlencode( $value ) );
+ }
+
+ // Now create the URI data item:
+ try {
+ $this->m_dataitem = new SMWDIUri( $scheme, $hierpart, $query, $fragment, !$this->getOption( self::OPT_QUERY_CONTEXT ) );
+ } catch ( SMWDataItemException $e ) {
+ $this->addErrorMsg( [ 'smw_baduri', $this->m_wikitext ] );
+ }
+ }
+
+ /**
+ * Returns true if the argument is a valid RFC 3966 phone number.
+ * Only global phone numbers are supported, and no full validation
+ * of parameters (appended via ;param=value) is performed.
+ */
+ protected static function isValidTelURI( $s ) {
+ $tel_uri_regex = '<^tel:\+[0-9./-]*[0-9][0-9./-]*(;[0-9a-zA-Z-]+=(%[0-9a-zA-Z][0-9a-zA-Z]|[0-9a-zA-Z._~:/?#[\]@!$&\'()*+,;=-])*)*$>';
+ return (bool) preg_match( $tel_uri_regex, $s );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_URI ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ if ( $this->m_mode == SMW_URI_MODE_EMAIL ) {
+ $this->m_wikitext = substr( $dataItem->getURI(), 7 );
+ } elseif ( $this->m_mode == SMW_URI_MODE_TEL ) {
+ $this->m_wikitext = substr( $dataItem->getURI(), 4 );
+ } else {
+ $this->m_wikitext = $dataItem->getURI();
+ }
+
+ $this->m_caption = $this->m_wikitext;
+ $this->showUrlContextInRawFormat = false;
+
+ return true;
+ }
+
+ public function getShortWikiText( $linked = null ) {
+
+ list( $url, $caption ) = $this->decodeUriContext( $this->m_caption, $linked );
+
+ if ( is_null( $linked ) || ( $linked === false ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_caption === '' ) ) {
+ return $caption;
+ } elseif ( $this->m_outformat == 'nowiki' ) {
+ return $this->makeNonlinkedWikiText( $caption );
+ } else {
+ return '[' . $url . ' ' . $caption . ']';
+ }
+ }
+
+ public function getShortHTMLText( $linker = null ) {
+
+ list( $url, $caption ) = $this->decodeUriContext( $this->m_caption, $linker );
+
+ if ( is_null( $linker ) || ( !$this->isValid() ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_outformat == 'nowiki' ) ||
+ ( $this->m_caption === '' ) || $linker === false ) {
+ return $caption;
+ } else {
+ return $linker->makeExternalLink( $url, $caption );
+ }
+ }
+
+ public function getLongWikiText( $linked = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ list( $url, $wikitext ) = $this->decodeUriContext( $this->m_wikitext, $linked );
+
+ if ( is_null( $linked ) || ( $linked === false ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || $linked === false ) {
+ return $wikitext;
+ } elseif ( $this->m_outformat == 'nowiki' ) {
+ return $this->makeNonlinkedWikiText( $wikitext );
+ } else {
+ return '[' . $url . ' ' . $wikitext . ']';
+ }
+ }
+
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ list( $url, $wikitext ) = $this->decodeUriContext( $this->m_wikitext, $linker );
+
+ if ( is_null( $linker ) || ( !$this->isValid() ) || ( $url === '' ) ||
+ ( $this->m_outformat == '-' ) || ( $this->m_outformat == 'nowiki' ) || $linker === false ) {
+ return $wikitext;
+ } else {
+ return $linker->makeExternalLink( $url, $wikitext );
+ }
+ }
+
+ public function getWikiValue() {
+
+ if ( $this->getOption( self::VALUE_RAW ) ) {
+ return rawurldecode( $this->m_wikitext );
+ }
+
+ return $this->m_wikitext;
+ }
+
+ public function getURI() {
+ return $this->getUriDataitem()->getURI();
+ }
+
+ protected function getServiceLinkParams() {
+ // Create links to mapping services based on a wiki-editable message. The parameters
+ // available to the message are:
+ // $1: urlencoded version of URI/URL value (includes mailto: for emails)
+ return [ rawurlencode( $this->getUriDataitem()->getURI() ) ];
+ }
+
+ /**
+ * Get a URL for hyperlinking this URI, or the empty string if this URI
+ * is not hyperlinked in MediaWiki.
+ * @return string
+ */
+ public function getURL() {
+ global $wgUrlProtocols;
+
+ foreach ( $wgUrlProtocols as $prot ) {
+ if ( ( $prot == $this->getUriDataitem()->getScheme() . ':' ) || ( $prot == $this->getUriDataitem()->getScheme() . '://' ) ) {
+ return $this->getUriDataitem()->getURI();
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Helper function to get the current dataitem, or some dummy URI
+ * dataitem if the dataitem was not set. This makes it easier to
+ * write code that avoids errors even if the data was not
+ * initialized properly.
+ * @return SMWDIUri
+ */
+ protected function getUriDataitem() {
+ if ( isset( $this->m_dataitem ) ) {
+ return $this->m_dataitem;
+ } else { // note: use "noprotocol" to avoid accidental use in an MW link, see getURL()
+ return new SMWDIUri( 'noprotocol', 'x', '', '', $this->m_typeid );
+ }
+ }
+
+ /**
+ * Helper function that changes a URL string in such a way that it
+ * can be used in wikitext without being turned into a hyperlink,
+ * while still displaying the same characters. The use of
+ * &lt;nowiki&gt; is avoided, since the resulting strings may be
+ * inserted during parsing, after this has been stripped.
+ *
+ * @since 1.8
+ */
+ protected function makeNonlinkedWikiText( $url ) {
+ return str_replace( ':', '&#58;', $url );
+ }
+
+ private function decodeUriContext( $context, $linker ) {
+
+ // Prior to decoding turn any `-` into an internal representation to avoid
+ // potential breakage
+ if ( !$this->showUrlContextInRawFormat ) {
+ $context = Encoder::decode( str_replace( '-', '-2D', $context ) );
+ }
+
+ if ( $this->m_mode !== SMW_URI_MODE_EMAIL && $linker !== null ) {
+ $context = str_replace( '_', ' ', $context );
+ }
+
+ // Allow the display without `_` so that URIs can be split
+ // during the outout by the browser without breaking the URL itself
+ // as it contains the `_` for spaces
+ return [ $this->getURL(), $context ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php
new file mode 100644
index 00000000..c1c1579e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DV_WikiPage.php
@@ -0,0 +1,751 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\Utils\Image;
+
+/**
+ * @ingroup SMWDataValues
+ */
+
+/**
+ * This datavalue implements special processing suitable for defining
+ * wikipages as values of properties.
+ *
+ * The class can support general wiki pages, or pages of a fixed
+ * namespace, Whether a namespace is fixed is decided based on the
+ * type ID when the object is constructed.
+ *
+ * The short display simulates the behavior of the MediaWiki "pipe trick"
+ * but always includes fragments. This can be overwritten by setting a
+ * caption, which is also done by default when generating a value from user
+ * input. The long display always includes all relevant information. Only if a
+ * fixed namespace is used for the datatype, the namespace prefix is omitted.
+ * This behavior has changed in SMW 1.7: up to this time, short displays have
+ * always included the namespace and long displays used the pipe trick, leading
+ * to a paradoxical confusion of "long" and "short".
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @ingroup SMWDataValues
+ */
+class SMWWikiPageValue extends SMWDataValue {
+
+ /**
+ * Whether text transformation should be suppressed or not.
+ */
+ const NO_TEXT_TRANSFORMATION = 'no.text.transformation';
+
+ /**
+ * Whether to use the short form or not.
+ */
+ const SHORT_FORM = 'short.form';
+
+ /**
+ * Fragment text for user-specified title. Not stored, but kept for
+ * printout on page.
+ * @var string
+ */
+ protected $m_fragment = '';
+
+ /**
+ * Full titletext with prefixes, including interwiki prefix.
+ * Set to empty string if not computed yet.
+ * @var string
+ */
+ protected $m_prefixedtext = '';
+
+ /**
+ * Cache for the related MW page ID.
+ * Set to -1 if not computed yet.
+ * @var integer
+ */
+ protected $m_id = -1;
+
+ /**
+ * Cache for the related MW title object.
+ * Set to null if not computed yet.
+ * @var Title
+ */
+ protected $m_title = null;
+
+ /**
+ * If this has a value other than NS_MAIN, the datavalue will only
+ * accept pages in this namespace. This field is initialized when
+ * creating the object (based on the type id or base on the preference
+ * of some subclass); it is not usually changed afterwards.
+ * @var integer
+ */
+ protected $m_fixNamespace = NS_MAIN;
+
+ /**
+ * @var array
+ */
+ protected $linkAttributes = [];
+
+ /**
+ * @var array
+ */
+ protected $queryParameters = [];
+
+ public function __construct( $typeid ) {
+ parent::__construct( $typeid );
+ switch ( $typeid ) {
+ case '_wpp' : case '__sup':
+ $this->m_fixNamespace = SMW_NS_PROPERTY;
+ break;
+ case '_wpc' : case '__suc': case '__sin':
+ $this->m_fixNamespace = NS_CATEGORY;
+ break;
+ case '_wpf' : case '__spf':
+ $this->m_fixNamespace = SF_NS_FORM;
+ break;
+ case '_wps' :
+ $this->m_fixNamespace = SMW_NS_SCHEMA;
+ break;
+ default: // case '_wpg':
+ $this->m_fixNamespace = NS_MAIN;
+ }
+ }
+
+ protected function parseUserValue( $value ) {
+ global $wgContLang;
+
+ // support inputs like " [[Test]] ";
+ // note that this only works when SMW_PARSER_LINV is set
+ $value = ltrim( rtrim( $value, ' ]' ), ' [' );
+
+ // #1066, Manipulate the output only for when the value has no caption
+ // assigned and only if a single :Foo is being present, ::Foo is not permitted
+ if ( $this->m_caption === false && isset( $value[2] ) && $value[0] === ':' && $value[1] !== ':' ) {
+ $value = substr( $value, 1 );
+ }
+
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ if ( $value === '' && !$this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-empty' ], Message::ESCAPED );
+ return;
+ }
+
+ // #1701 If the DV is part of a Description and an approximate search
+ // (e.g. ~foo* / ~Foo*) then use the value as-is and avoid being
+ // transformed by the Title object
+ // If the vaue contains a valid NS then use the Title to create a correct
+ // instance to distinguish [[~Foo*]] from [[Help:~Foo*]]
+ if ( $this->getOption( self::OPT_QUERY_COMP_CONTEXT ) || $this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+
+ $title = Title::newFromText( $value, $this->m_fixNamespace );
+
+ // T:P0427 If the user value says `ab c*` then make sure to use this one
+ // instead of the transformed DBKey which would be `Ab c*`
+ if ( $title !== null && $title->getNamespace() === NS_MAIN && $this->getOption( 'isCapitalLinks' ) === false ) {
+ return $this->m_dataitem = new SMWDIWikiPage( $value, NS_MAIN );
+ // If we know that it is a wikipage in a query context and the wiki
+ // requires `isCapitalLinks` then use the standard transformation so
+ // they appear as standard links even though the user input was `abc`.
+ // T:P0902 (`[[Help:]]`)
+ } elseif ( $title !== null ) {
+ return $this->m_dataitem = SMWDIWikiPage::newFromTitle( $title );
+ } elseif ( !Localizer::getInstance()->getNamespaceIndexByName( substr( $value, 0, -1 ) ) ) {
+ return $this->m_dataitem = new SMWDIWikiPage( $value, NS_MAIN );
+ }
+ }
+
+ if ( $value[0] == '#' ) {
+ if ( is_null( $this->m_contextPage ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-missing-fragment-context', $value ] );
+ return;
+ } else {
+ $this->m_title = Title::makeTitle( $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getDBkey(), substr( $value, 1 ),
+ $this->m_contextPage->getInterwiki() );
+ }
+ } else {
+ $this->m_title = Title::newFromText( $value, $this->m_fixNamespace );
+ }
+
+ /// TODO: Escape the text so users can see punctuation problems (bug 11666).
+ if ( $this->m_title === null && $this->getProperty() !== null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-property-invalid-title', $this->getProperty()->getLabel(), $value ] );
+ } elseif ( $this->m_title === null ) {
+ $this->addErrorMsg( [ 'smw-datavalue-wikipage-invalid-title', $value ] );
+ } elseif ( ( $this->m_fixNamespace != NS_MAIN ) &&
+ ( $this->m_fixNamespace != $this->m_title->getNamespace() ) ) {
+ $this->addErrorMsg( [ 'smw_wrong_namespace', $wgContLang->getNsText( $this->m_fixNamespace ) ] );
+ } else {
+ $this->m_fragment = str_replace( ' ', '_', $this->m_title->getFragment() );
+ $this->m_prefixedtext = '';
+ $this->m_id = -1; // unset id
+ $this->m_dataitem = SMWDIWikiPage::newFromTitle( $this->m_title, $this->m_typeid );
+ }
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ protected function loadDataItem( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_CONTAINER ) {
+ // might throw an exception, we just pass it through
+ $dataItem = $dataItem->getSemanticData()->getSubject();
+ }
+
+ if ( $dataItem->getDIType() !== SMWDataItem::TYPE_WIKIPAGE ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_id = -1;
+ $this->m_title = null;
+ $this->m_fragment = $dataItem->getSubobjectName();
+ $this->m_prefixedtext = '';
+ $this->m_caption = false; // this class can handle this
+ $this->linkAttributes = [];
+
+ if ( ( $this->m_fixNamespace != NS_MAIN ) &&
+ ( $this->m_fixNamespace != $dataItem->getNamespace() ) ) {
+ $this->addErrorMsg(
+ [
+ 'smw_wrong_namespace',
+ Localizer::getInstance()->getNamespaceTextById( $this->m_fixNamespace )
+ ]
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $linkAttributes
+ */
+ public function setLinkAttributes( array $linkAttributes ) {
+ $this->linkAttributes = $linkAttributes;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $queryParameters
+ */
+ public function setQueryParameters( array $queryParameters ) {
+ $this->queryParameters = $queryParameters;
+ }
+
+ /**
+ * Display the value on a wiki page. This is used to display the value
+ * in the place where it was annotated on a wiki page. The desired
+ * behavior is that the display in this case looks as if no property
+ * annotation had been given, i.e. an annotation [[property::page|foo]]
+ * should display like [[page|foo]] in MediaWiki. But this should lead
+ * to a link, not to a category assignment. This means that:
+ *
+ * (1) If Image: is used (instead of Media:) then let MediaWiki embed
+ * the image.
+ *
+ * (2) If Category: is used, treat it as a page and link to it (do not
+ * categorize the page)
+ *
+ * (3) Preserve everything given after "|" for display (caption, image
+ * parameters, ...)
+ *
+ * (4) Use the (default) caption for display. When the value comes from
+ * user input, this includes the full value that one would also see in
+ * MediaWiki.
+ *
+ * @param $linked mixed generate links if not null or false
+ * @return string
+ */
+ public function getShortWikiText( $linked = null ) {
+
+ if ( is_null( $linked ) || $linked === false ||
+ $this->m_outformat == '-' || !$this->isValid() ||
+ $this->m_caption === '' ) {
+ return $this->m_caption !== false ? $this->m_caption : $this->getWikiValue();
+ }
+
+ if ( Image::isImage( $this->m_dataitem ) && $this->m_dataitem->getInterwiki() === '' ) {
+ $linkEscape = '';
+ $options = $this->m_outformat === false ? 'frameless|border|text-top|' : str_replace( ';', '|', \Sanitizer::removeHTMLtags( $this->m_outformat ) );
+ $defaultCaption = '|' . $this->getShortCaptionText() . '|' . $options;
+ } else {
+ $linkEscape = ':';
+ $defaultCaption = '|' . $this->getShortCaptionText();
+ }
+
+ if ( $this->m_caption === false ) {
+ $link = '[[' . $linkEscape . $this->getWikiLinkTarget() . $defaultCaption . ']]';
+ } else {
+ $link = '[[' . $linkEscape . $this->getWikiLinkTarget() . '|' . $this->m_caption . ']]';
+ }
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ if ( $this->linkAttributes !== [] ) {
+ $link = \Html::rawElement(
+ 'span',
+ $this->linkAttributes,
+ $link
+ );
+ }
+
+ return $link;
+ }
+
+ /**
+ * Display the value as in getShortWikiText() but create HTML.
+ * The only difference is that images are not embedded.
+ *
+ * @param Linker $linker mixed the Linker object to use or null if no linking is desired
+ * @return string
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ // init the Title object, may reveal hitherto unnoticed errors:
+ if ( !is_null( $linker ) && $linker !== false &&
+ $this->m_caption !== '' && $this->m_outformat != '-' ) {
+ $this->getTitle();
+ }
+
+ if ( is_null( $linker ) || $linker === false || !$this->isValid() ||
+ $this->m_outformat == '-' || $this->m_caption === '' ) {
+
+ $caption = $this->m_caption === false ? $this->getWikiValue() : $this->m_caption;
+ return \Sanitizer::removeHTMLtags( $caption );
+ }
+
+ $caption = $this->m_caption === false ? $this->getShortCaptionText() : $this->m_caption;
+ $caption = \Sanitizer::removeHTMLtags( $caption );
+
+ if ( $this->getNamespace() == NS_MEDIA ) { // this extra case *is* needed
+ return $linker->makeMediaLinkObj( $this->getTitle(), $caption );
+ }
+
+ return $linker->link(
+ $this->getTitle(),
+ $caption,
+ $this->linkAttributes,
+ $this->queryParameters
+ );
+ }
+
+ /**
+ * Display the "long" value on a wiki page. This behaves largely like
+ * getShortWikiText() but does not use the caption. Instead, it always
+ * takes the long display form (wiki value).
+ *
+ * @param $linked mixed if true the result will be linked
+ * @return string
+ */
+ public function getLongWikiText( $linked = null ) {
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ if ( is_null( $linked ) || $linked === false || $this->m_outformat == '-' ) {
+ return $this->getWikiValue();
+ } elseif ( Image::isImage( $this->m_dataitem ) && $this->m_dataitem->getInterwiki() === '' ) {
+ // Embed images and other files
+ // Note that the embedded file links to the image, hence needs no additional link text.
+ // There should not be a linebreak after an impage, just like there is no linebreak after
+ // other values (whether formatted or not).
+ return '[[' . $this->getWikiLinkTarget() . '|' .
+ $this->getLongCaptionText() . '|frameless|border|text-top]]';
+ }
+
+ $link = '[[:' . $this->getWikiLinkTarget() . '|' . $this->getLongCaptionText() . ']]';
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ if ( $this->linkAttributes !== [] ) {
+ $link = \Html::rawElement(
+ 'span',
+ $this->linkAttributes,
+ $link
+ );
+ }
+
+ return $link;
+ }
+
+ /**
+ * Display the "long" value in HTML. This behaves largely like
+ * getLongWikiText() but does not embed images.
+ *
+ * @param $linker mixed if a Linker is given, the result will be linked
+ * @return string
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->m_fragment !== '' ) {
+ $this->linkAttributes['class'] = 'smw-subobject-entity';
+ }
+
+ // init the Title object, may reveal hitherto unnoticed errors:
+ if ( !is_null( $linker ) && ( $this->m_outformat != '-' ) ) {
+ $this->getTitle();
+ }
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ if ( $linker === null || $linker === false || $this->m_outformat == '-' ) {
+ return \Sanitizer::removeHTMLtags( $this->getWikiValue() );
+ } elseif ( $this->getNamespace() == NS_MEDIA ) { // this extra case is really needed
+ return $linker->makeMediaLinkObj(
+ $this->getTitle(),
+ \Sanitizer::removeHTMLtags( $this->getLongCaptionText() )
+ );
+ }
+
+ // all others use default linking, no embedding of images here
+ return $linker->link(
+ $this->getTitle(),
+ \Sanitizer::removeHTMLtags( $this->getLongCaptionText() ),
+ $this->linkAttributes,
+ $this->queryParameters
+ );
+ }
+
+ /**
+ * Return a string that could be used in an in-page property assignment
+ * for setting this value. This does not include initial ":" for
+ * escaping things like Category: links since the property value does
+ * not include such escapes either. Fragment information is included.
+ * Namespaces are omitted if a fixed namespace is used, since they are
+ * not needed in this case when making a property assignment.
+ *
+ * @return string
+ */
+ public function getWikiValue() {
+
+ if ( $this->getOption( self::SHORT_FORM, false ) ) {
+ $text = $this->getText();
+ } elseif ( $this->getTypeID() === '_wpp' || $this->m_fixNamespace == NS_MAIN ) {
+ $text = $this->getPrefixedText();
+ } else {
+ $text = $this->getText();
+ }
+
+ return $text . ( $this->m_fragment !== '' ? "#{$this->m_fragment}" : '' );
+ }
+
+ public function getHash() {
+ return $this->isValid() ? $this->getPrefixedText() : implode( "\t", $this->getErrors() );
+ }
+
+ /**
+ * Create links to mapping services based on a wiki-editable message.
+ * The parameters available to the message are:
+ * $1: urlencoded article name (no namespace)
+ *
+ * @return array
+ */
+ protected function getServiceLinkParams() {
+ if ( $this->isValid() ) {
+ return [ rawurlencode( str_replace( '_', ' ', $this->m_dataitem->getDBkey() ) ) ];
+ } else {
+ return [];
+ }
+ }
+
+///// special interface for wiki page values
+
+ /**
+ * Return according Title object or null if no valid value was set.
+ * null can be returned even if this object returns true for isValid(),
+ * since the latter function does not check whether MediaWiki can really
+ * make a Title out of the given data.
+ * However, isValid() will return false *after* this function failed in
+ * trying to create a title.
+ *
+ * @return Title
+ */
+ public function getTitle() {
+
+ if ( $this->m_title !== null ) {
+ return $this->m_title;
+ }
+
+ if ( $this->isValid() ) {
+
+ if ( ( $title = $this->m_dataitem->getTitle() ) !== null ) {
+ return $this->m_title = $title;
+ }
+
+ // #3278, Special handling of `>` in the user namespace, MW (1.31+)
+ // added a prefix to users that originate from imported content
+ if (
+ $this->m_dataitem->getNamespace() === NS_USER &&
+ strpos( $this->m_dataitem->getDBkey(), '>' ) !== false ) {
+
+ $this->setOption( self::OPT_DISABLE_INFOLINKS, true );
+
+ $this->m_title = Title::newFromText(
+ $this->m_dataitem->getDBkey()
+ );
+
+ return $this->m_title;
+ }
+ }
+
+ $errArg = $this->m_caption;
+
+ if ( $this->isValid() ) {
+ $ns = Localizer::getInstance()->getNamespaceTextById(
+ $this->m_dataitem->getNamespace()
+ );
+
+ $errArg = "$ns:" . $this->m_dataitem->getDBkey();
+ }
+
+ // Should not normally happen, but anyway ...
+ $this->addErrorMsg( [ 'smw_notitle', $errArg ] );
+ }
+
+ /**
+ * Get MediaWiki's ID for this value or 0 if not available.
+ *
+ * @return integer
+ */
+ public function getArticleID() {
+ if ( $this->m_id === false ) {
+ $this->m_id = !is_null( $this->getTitle() ) ? $this->m_title->getArticleID() : 0;
+ }
+
+ return $this->m_id;
+ }
+
+ /**
+ * Get namespace constant for this value.
+ *
+ * @return integer
+ */
+ public function getNamespace() {
+ return $this->m_dataitem->getNamespace();
+ }
+
+ /**
+ * Get DBKey for this value. Subclasses that allow for values that do not
+ * correspond to wiki pages may choose a DB key that is not a legal title
+ * DB key but rather another suitable internal ID. Thus it is not suitable
+ * to use this method in places where only MediaWiki Title keys are allowed.
+ *
+ * @return string
+ */
+ public function getDBkey() {
+ return $this->m_dataitem->getDBkey();
+ }
+
+ /**
+ * Get text label for this value, just like Title::getText().
+ *
+ * @return string
+ */
+ public function getText() {
+
+ if ( $this->getOption( self::NO_TEXT_TRANSFORMATION, false ) ) {
+ return $this->m_dataitem->getDBkey();
+ }
+
+ return str_replace( '_', ' ', $this->m_dataitem->getDBkey() );
+ }
+
+ /**
+ * Get the prefixed text for this value, including a localized namespace
+ * prefix.
+ *
+ * @return string
+ */
+ public function getPrefixedText() {
+
+ if ( $this->m_prefixedtext !== '' ) {
+ return $this->m_prefixedtext;
+ }
+
+ // In case something went wrong (invalid NS etc.), hint the ID to aid the
+ // investigation
+ if ( $this->m_dataitem->getId() > 0 ) {
+ $this->m_prefixedtext = 'NO_VALID_VALUE (ID: ' . $this->m_dataitem->getId() . ')';
+ } else {
+ $this->m_prefixedtext = 'NO_VALID_VALUE';
+ }
+
+ if ( $this->isValid() ) {
+ $nstext = Localizer::getInstance()->getNamespaceTextById( $this->m_dataitem->getNamespace() );
+ $this->m_prefixedtext =
+ ( $this->m_dataitem->getInterwiki() !== '' ? $this->m_dataitem->getInterwiki() . ':' : '' ) .
+ ( $nstext !== '' ? "$nstext:" : '' ) . $this->getText();
+ }
+
+ return $this->m_prefixedtext;
+ }
+
+ /**
+ * Get interwiki prefix or empty string.
+ *
+ * @return string
+ */
+ public function getInterwiki() {
+ return $this->m_dataitem->getInterwiki();
+ }
+
+ /**
+ * DataValue::getPreferredCaption
+ *
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getPreferredCaption() {
+
+ if ( ( $preferredCaption = parent::getPreferredCaption() ) !== '' && $preferredCaption !== false ) {
+ return $preferredCaption;
+ }
+
+ $preferredCaption = $this->getDisplayTitle();
+
+ if ( $preferredCaption === '' && $this->getOption( 'prefixed.preferred.caption' ) ) {
+ $preferredCaption = $this->getPrefixedText();
+ } elseif ( $preferredCaption === '' ) {
+ $preferredCaption = $this->getText();
+ }
+
+ return $preferredCaption;
+ }
+
+ /**
+ * Get a short caption used to label this value. In particular, this
+ * omits namespace and interwiki prefixes (similar to the MediaWiki
+ * "pipe trick"). Fragments are included unless they start with an
+ * underscore (used for generated fragment names that are not helpful
+ * for users and that might change easily).
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getShortCaptionText() {
+ if ( $this->m_fragment !== '' && $this->m_fragment[0] != '_' ) {
+ $fragmentText = '#' . str_replace( '_', ' ', $this->m_fragment );
+ } else {
+ $fragmentText = '';
+ }
+
+ if ( $this->m_caption && $this->m_caption !== '' ) {
+ return $this->m_caption;
+ }
+
+ $displayTitle = $this->getDisplayTitle();
+
+ if ( $displayTitle === '' ) {
+ $displayTitle = $this->getText();
+ }
+
+ return $displayTitle . $fragmentText;
+ }
+
+ /**
+ * Get a long caption used to label this value. In particular, this
+ * includes namespace and interwiki prefixes, while fragments are only
+ * included if they do not start with an underscore (used for generated
+ * fragment names that are not helpful for users and that might change
+ * easily).
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getLongCaptionText() {
+ if ( $this->m_fragment !== '' && $this->m_fragment[0] != '_' ) {
+ $fragmentText = '#' . str_replace( '_', ' ', $this->m_fragment );
+ } else {
+ $fragmentText = '';
+ }
+
+ if ( $this->m_caption && $this->m_caption !== '' ) {
+ return $this->m_caption;
+ }
+
+ $displayTitle = $this->getDisplayTitle();
+
+ if ( $displayTitle === '' ) {
+ $displayTitle = $this->m_fixNamespace == NS_MAIN ? $this->getPrefixedText() : $this->getText();
+ }
+
+ return $displayTitle . $fragmentText;
+ }
+
+ /**
+ * Compute a text that can be used in wiki text to link to this
+ * datavalue. Processing includes some escaping and adding the
+ * fragment.
+ *
+ * @since 1.7
+ * @return string
+ */
+ protected function getWikiLinkTarget() {
+ return str_replace( "'", '&#x0027;', $this->getPrefixedText() ) .
+ ( $this->m_fragment !== '' ? "#{$this->m_fragment}" : '' );
+ }
+
+ /**
+ * Find the sortkey for this object.
+ *
+ * @deprecated Use SMWStore::getWikiPageSortKey(). Will vanish before SMW 1.7
+ *
+ * @return string sortkey
+ */
+ public function getSortKey() {
+ return ApplicationFactory::getInstance()->getStore()->getWikiPageSortKey( $this->m_dataitem );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getDisplayTitle() {
+
+ if ( $this->m_dataitem === null || !$this->isEnabledFeature( SMW_DV_WPV_DTITLE ) ) {
+ return '';
+ }
+
+ return $this->findDisplayTitleFor( $this->m_dataitem );
+ }
+
+ private function findDisplayTitleFor( $subject ) {
+
+ $displayTitle = '';
+
+ $dataItems = ApplicationFactory::getInstance()->getCachedPropertyValuesPrefetcher()->getPropertyValues(
+ $subject,
+ new DIProperty( '_DTITLE' )
+ );
+
+ if ( $dataItems !== null && $dataItems !== [] ) {
+ $displayTitle = end( $dataItems )->getString();
+ } elseif ( $subject->getSubobjectName() !== '' ) {
+ // Check whether the base subject has a DISPLAYTITLE
+ return $this->findDisplayTitleFor( $subject->asBase() );
+ }
+
+ return $displayTitle;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php
new file mode 100644
index 00000000..a4becb46
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/datavalues/SMW_DataValue.php
@@ -0,0 +1,897 @@
+<?php
+
+/**
+ * This group contains all parts of SMW that relate to the processing of datavalues
+ * of various types.
+ *
+ * @defgroup SMWDataValues SMWDataValues
+ * @ingroup SMW
+ */
+
+use SMW\DataValues\InfoLinksProvider;
+use SMW\Deserializers\DVDescriptionDeserializerRegistry;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\Options;
+use SMW\Query\QueryComparator;
+use SMW\Services\DataValueServiceFactory;
+use SMW\Utils\CharArmor;
+
+/**
+ * Objects of this type represent all that is known about a certain user-provided
+ * data value, especially its various representations as strings, tooltips,
+ * numbers, etc. Objects can be created as "empty" containers of a certain type,
+ * but are then usually filled with data to present one particular data value.
+ *
+ * Data values have two chief representation forms: the user-facing syntax and the
+ * internal representation. In user syntax, every value is (necessarily) a single
+ * string, however complex the value is. For example, a string such as "Help:editing"
+ * may represent a wiki page called "Editing" in the namespace for "Help". The
+ * internal representation may be any numerical array of strings and numbers. In the
+ * example, it might be array("Editing",12), where 12 is the number used for identifying
+ * the namespace "Help:". Of course, the internal representation could also use a single
+ * string value, such as in array("Help:Editing"), but this might be less useful for
+ * certain operations (e.g. filterng by namespace). Moreover, all values that are
+ * restored from the database are given in the internal format, so it wise to choose a
+ * format that allows for very fast and easy processing without unnecessary parsing.
+ *
+ * The main functions of data value objects are:
+ * - setUserValue() which triggers parseUserValue() to process a user-level string.
+ *
+ * In addition, there are a number of get-functions that provide useful output versions
+ * for displaying and serializing the value.
+ *
+ * @ingroup SMWDataValues
+ *
+ * @author Markus Krötzsch
+ */
+abstract class SMWDataValue {
+
+ /**
+ * Contains the user language a user operates in.
+ */
+ const OPT_USER_LANGUAGE = 'user.language';
+
+ /**
+ * Contains either the global "site" content language or a specified page
+ * content language invoked by the context page.
+ */
+ const OPT_CONTENT_LANGUAGE = 'content.language';
+
+ /**
+ * Describes a state where a DataValue is part of a query condition and may
+ * (or not) require a different treatment.
+ */
+ const OPT_QUERY_CONTEXT = 'query.context';
+
+ /**
+ * Describes a state where a DataValue is part of a query condition and
+ * contains a comparator.
+ */
+ const OPT_QUERY_COMP_CONTEXT = 'query.comparator.context';
+
+ /**
+ * Option to disable related infolinks
+ */
+ const OPT_DISABLE_INFOLINKS = 'disable.infolinks';
+
+ /**
+ * Option to disable service links
+ */
+ const OPT_DISABLE_SERVICELINKS = 'disable.servicelinks';
+
+ /**
+ * Option to use compact infolinks
+ */
+ const OPT_COMPACT_INFOLINKS = 'compact.infolinks';
+
+ /**
+ * Associated data item. This is the reference to the immutable object
+ * that represents the current data content. All other data stored here
+ * is only about presentation and parsing, but is not relevant to the
+ * actual data that is represented (and stored later on).
+ *
+ * This variable must always be set to some data item, even if there
+ * have been errors in initialising the data.
+ * @var SMWDataItem
+ */
+ protected $m_dataitem;
+
+ /**
+ * The property for which this value is constructed or null if none
+ * given. Property pages are used to make settings that affect parsing
+ * and display, hence it is sometimes needed to know them.
+ *
+ * @var DIProperty
+ */
+ protected $m_property = null;
+
+ /**
+ * Wiki page in the context of which the value is to be interpreted, or
+ * null if not given (or not on a page). This information is used to
+ * parse user values such as "#subsection" which only make sense when
+ * used on a certain page.
+ *
+ * @var SMWDIWikiPage
+ */
+ protected $m_contextPage = null;
+
+ /**
+ * The text label to be used for output or false if none given.
+ * @var string
+ */
+ protected $m_caption;
+
+ /**
+ * The type id for this value object.
+ * @var string
+ */
+ protected $m_typeid;
+
+ /**
+ * Output formatting string, false when not set.
+ * @see setOutputFormat()
+ * @var mixed
+ */
+ protected $m_outformat = false;
+
+ /**
+ * Array of error text messages. Private to allow us to track error insertion
+ * (PHP's count() is too slow when called often) by using $mHasErrors.
+ * @var array
+ */
+ private $mErrors = [];
+
+ /**
+ * Boolean indicating if there where any errors.
+ * Should be modified accordingly when modifying $mErrors.
+ * @var boolean
+ */
+ private $mHasErrors = false;
+
+ /**
+ * @var false|array
+ */
+ protected $restrictionError = false;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var InfoLinksProvider
+ */
+ private $infoLinksProvider = null;
+
+ /**
+ * @var string
+ */
+ private $userValue = '';
+
+ /**
+ * @var DataValueServiceFactory
+ */
+ protected $dataValueServiceFactory;
+
+ /**
+ * Constructor.
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid ) {
+ $this->m_typeid = $typeid;
+ }
+
+ /**
+ * Return a short string that unambiguously specify the type of this
+ * value. This value will globally be used to identify the type of a
+ * value (in spite of the class it actually belongs to, which can still
+ * implement various types).
+ */
+ public function getTypeID() {
+ return $this->m_typeid;
+ }
+
+ /**
+ * Set the user value (and compute other representations if possible).
+ * The given value is a string as supplied by some user. An alternative
+ * label for printout might also be specified.
+ *
+ * @param string $value
+ * @param mixed $caption
+ */
+ public function setUserValue( $value, $caption = false ) {
+
+ $this->m_dataitem = null;
+ $this->mErrors = []; // clear errors
+ $this->mHasErrors = false;
+ $this->m_caption = is_string( $caption ) ? trim( $caption ) : false;
+ $this->userValue = $value;
+
+ // #2435
+ $value = CharArmor::removeControlChars(
+ CharArmor::removeSpecialChars( $value )
+ );
+
+ // Process may set a caption if not set yet, depending on datavalue
+ $this->parseUserValue( $value );
+
+ // The following checks for Strip markers generated by MediaWiki to handle special content,
+ // from parser and extension tags e.g. <pre>,<nowiki>,<math>,<source>.
+ // See https://en.wikipedia.org/wiki/Help:Strip_markers
+ // In general, we are not prepared to handle such content properly, and we
+ // also have no means of obtaining the user input at this point. Hence the assignment
+ // just fails, even if parseUserValue() above might not have noticed this issue.
+ // Note: \x07 was used in MediaWiki 1.11.0, \x7f is used now (backwards compatibility, b/c)
+ if ( is_string( $value ) && ( ( strpos( $value, "\x7f" ) !== false ) || ( strpos( $value, "\x07" ) !== false ) ) ) {
+ $this->addErrorMsg( [ 'smw-datavalue-stripmarker-parse-error', $value ] );
+ }
+
+ if ( $this->isValid() && !$this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+ $this->checkAllowedValues();
+ }
+ }
+
+ /**
+ * Set the actual data contained in this object. The method returns
+ * true if this was successful (requiring the type of the dataitem
+ * to match the data value). If false is returned, the data value is
+ * left unchanged (the data item was rejected).
+ *
+ * @note Even if this function returns true, the data value object
+ * might become invalid if the content of the data item caused errors
+ * in spite of it being of the right basic type. False is only returned
+ * if the data item is fundamentally incompatible with the data value.
+ *
+ * @param $dataitem SMWDataItem
+ * @return boolean
+ */
+ public function setDataItem( SMWDataItem $dataItem ) {
+ $this->m_dataitem = null;
+ $this->mErrors = [];
+ $this->mHasErrors = $this->m_caption = false;
+ return $this->loadDataItem( $dataItem );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValueServiceFactory $dataValueServiceFactory
+ */
+ public function setDataValueServiceFactory( DataValueServiceFactory $dataValueServiceFactory ) {
+ $this->dataValueServiceFactory = $dataValueServiceFactory;
+ }
+
+ /**
+ * Specify the property to which this value refers. Property pages are
+ * used to make settings that affect parsing and display, hence it is
+ * sometimes needed to know them.
+ *
+ * @since 1.6
+ *
+ * @param DIProperty $property
+ */
+ public function setProperty( DIProperty $property ) {
+ $this->m_property = $property;
+ }
+
+ /**
+ * Returns the property to which this value refers.
+ *
+ * @since 1.8
+ *
+ * @return DIProperty|null
+ */
+ public function getProperty() {
+ return $this->m_property;
+ }
+
+ /**
+ * Specify the wiki page to which this value refers. This information is
+ * used to parse user values such as "#subsection" which only make sense
+ * when used on a certain page.
+ *
+ * @since 1.7
+ *
+ * @param SMWDIWikiPage|null $contextPage
+ */
+ public function setContextPage( SMWDIWikiPage $contextPage = null ) {
+ $this->m_contextPage = $contextPage;
+
+ $this->setOption(
+ self::OPT_CONTENT_LANGUAGE,
+ Localizer::getInstance()->getPreferredContentLanguage( $contextPage )->getCode()
+ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DIWikiPage|null
+ */
+ public function getContextPage() {
+ return $this->m_contextPage;
+ }
+
+ /**
+ * Change the caption (the text used for displaying this datavalue). The given
+ * value must be a string.
+ *
+ * @param string $caption
+ */
+ public function setCaption( $caption ) {
+ $this->m_caption = $caption;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $caption
+ */
+ public function getCaption() {
+ return $this->m_caption;
+ }
+
+ /**
+ * Returns a preferred caption and may deviate from the standard caption as
+ * a subclass is permitted to override this method and provide a more
+ * contextualized display representation (language or value context etc.).
+ *
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getPreferredCaption() {
+ return $this->m_caption;
+ }
+
+ /**
+ * Define a particular output format. Output formats are user-supplied strings
+ * that the datavalue may (or may not) use to customise its return value. For
+ * example, quantities with units of measurement may interpret the string as
+ * a desired output unit. In other cases, the output format might be built-in
+ * and subject to internationalisation (which the datavalue has to implement).
+ * In any case, an empty string resets the output format to the default.
+ *
+ * There is one predefined output format that all datavalues should respect: the
+ * format '-' indicates "plain" output that is most useful for further processing
+ * the value in a template. It should not use any wiki markup or beautification,
+ * and it should also avoid localization to the current language. When users
+ * explicitly specify an empty format string in a query, it is normalized to "-"
+ * to avoid confusion. Note that empty format strings are not interpreted in
+ * this way when directly passed to this function.
+ *
+ * @param string $formatString
+ */
+ public function setOutputFormat( $formatString ) {
+ $this->m_outformat = $formatString; // just store it, subclasses may or may not use this
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getOutputFormat() {
+ return $this->m_outformat;
+ }
+
+ /**
+ * Add a new error string or array of such strings to the error list.
+ *
+ * @note Errors should not be escaped here in any way, in contradiction to what
+ * the docs used to say here in 1.5 and before. Escaping should happen at the output.
+ *
+ * @param mixed $error A single string, or array of strings.
+ */
+ public function addError( $error ) {
+ if ( is_array( $error ) ) {
+ $this->mErrors = array_merge( $this->mErrors, $error );
+ $this->mHasErrors = $this->mHasErrors || ( count( $error ) > 0 );
+ } else {
+ $this->mErrors[] = $error;
+ $this->mHasErrors = true;
+ }
+ }
+
+ /**
+ * Messages are not resolved until the output and instead will be kept with the
+ * message and argument keys (e.g. `[2,"smw_baduri","~*0123*"]`). This allows to
+ * switch the a representation without requiring language context by the object
+ * that reports an error.
+ *
+ * @since 2.4
+ *
+ * @param $parameters
+ * @param integer|null $type
+ * @param integer|null $language
+ */
+ public function addErrorMsg( $parameters, $type = null ) {
+ $this->mErrors[Message::getHash( $parameters, $type )] = Message::encode( $parameters, $type );
+ $this->mHasErrors = true;
+ }
+
+ /**
+ * Return a string that displays all error messages as a tooltip, or
+ * an empty string if no errors happened.
+ *
+ * @return string
+ */
+ public function getErrorText() {
+ return smwfEncodeMessages( $this->mErrors );
+ }
+
+ /**
+ * Return an array of error messages, or an empty array
+ * if no errors occurred.
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->mErrors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array|false
+ */
+ public function getRestrictionError() {
+ return $this->restrictionError;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function clearErrors() {
+ $this->mErrors = [];
+ $this->mHasErrors = false;
+ }
+
+///// Query support /////
+
+ /**
+ * FIXME 3.0, allow NULL as value
+ *
+ * @see DataValueDescriptionDeserializer::deserialize
+ *
+ * @note Descriptions of values need to know their property to be able to
+ * create a parsable wikitext version of a query condition again. Thus it
+ * might be necessary to call setProperty() before using this method.
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function getQueryDescription( $value ) {
+
+ $descriptionDeserializer = DVDescriptionDeserializerRegistry::getInstance()->getDescriptionDeserializerBy( $this );
+ $description = $descriptionDeserializer->deserialize( $value );
+
+ foreach ( $descriptionDeserializer->getErrors() as $error ) {
+ $this->addError( $error );
+ }
+
+ return $description;
+ }
+
+ /**
+ * @deprecated since 2.3
+ *
+ * @see DescriptionDeserializer::prepareValue
+ *
+ * This method should no longer be used for direct public access, instead a
+ * DataValue is expected to register a DescriptionDeserializer with
+ * DVDescriptionDeserializerRegistry.
+ */
+ static public function prepareValue( &$value, &$comparator ) {
+ $comparator = QueryComparator::getInstance()->extractComparatorFromString( $value );
+ }
+
+///// Get methods /////
+
+ /**
+ * Get the actual data contained in this object or null if the data is
+ * not defined (due to errors or due to not being set at all).
+ * @note Most implementations ensure that a data item is always set,
+ * even if errors occurred, to avoid additional checks for not
+ * accessing null. Hence, one must not assume that a non-null return
+ * value here implies that isValid() returns true.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataItem|SMWDIError
+ */
+ public function getDataItem() {
+
+ if ( $this->isValid() ) {
+ return $this->m_dataitem;
+ }
+
+ return new SMWDIError( $this->mErrors, $this->userValue );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getDataItem()->getSerialization();
+ }
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in wiki text.
+ *
+ * The parameter $linked controls linking of values such as titles and should
+ * be non-NULL and non-false if this is desired.
+ */
+ abstract public function getShortWikiText( $linked = null );
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in HTML text.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (or NULL for no linking).
+ */
+ abstract public function getShortHTMLText( $linker = null );
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message
+ * The result always is a wiki-source string.
+ *
+ * The parameter $linked controls linking of values such as titles and should
+ * be non-NULL and non-false if this is desired.
+ */
+ abstract public function getLongWikiText( $linked = null );
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message
+ * The result always is an HTML string.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (or NULL for no linking).
+ */
+ abstract public function getLongHTMLText( $linker = null );
+
+ /**
+ * Return the plain wiki version of the value, or
+ * FALSE if no such version is available. The returned
+ * string suffices to reobtain the same DataValue
+ * when passing it as an input string to setUserValue().
+ */
+ abstract public function getWikiValue();
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in the specified format.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output), or NULL for no linking.
+ */
+ public function getShortText( $outputformat, $linker = null ) {
+ switch ( $outputformat ) {
+ case SMW_OUTPUT_WIKI:
+ return $this->getShortWikiText( $linker );
+ case SMW_OUTPUT_HTML:
+ case SMW_OUTPUT_FILE:
+ default:
+ return $this->getShortHTMLText( $linker );
+ }
+ }
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message.
+ * The output is in the specified format.
+ *
+ * The parameter $linker controls linking of values such as titles and should
+ * be some Linker object (for HTML output), or NULL for no linking.
+ */
+ public function getLongText( $outputformat, $linker = null ) {
+ switch ( $outputformat ) {
+ case SMW_OUTPUT_WIKI:
+ return $this->getLongWikiText( $linker );
+ case SMW_OUTPUT_HTML:
+ case SMW_OUTPUT_FILE:
+ default:
+ return $this->getLongHTMLText( $linker );
+ }
+ }
+
+ /**
+ * Return text serialisation of info links. Ensures more uniform layout
+ * throughout wiki (Factbox, Property pages, ...).
+ *
+ * @param integer $outputformat Element of the SMW_OUTPUT_ enum
+ * @param $linker
+ *
+ * @return string
+ */
+ public function getInfolinkText( $outputformat, $linker = null ) {
+
+ if ( $this->getOption( self::OPT_DISABLE_INFOLINKS ) === true ) {
+ return '';
+ }
+
+ if ( $this->infoLinksProvider === null ) {
+ $this->infoLinksProvider = $this->dataValueServiceFactory->newInfoLinksProvider( $this );
+ }
+
+ if ( $this->getOption( self::OPT_DISABLE_SERVICELINKS ) === true ) {
+ $this->infoLinksProvider->disableServiceLinks();
+ }
+
+ $this->infoLinksProvider->setCompactLink(
+ $this->getOption( self::OPT_COMPACT_INFOLINKS, false )
+ );
+
+ return $this->infoLinksProvider->getInfolinkText( $outputformat, $linker );
+ }
+
+ /**
+ * Return an array of SMWLink objects that provide additional resources
+ * for the given value. Captions can contain some HTML markup which is
+ * admissible for wiki text, but no more. Result might have no entries
+ * but is always an array.
+ */
+ public function getInfolinks() {
+
+ if ( $this->infoLinksProvider === null ) {
+ $this->infoLinksProvider = $this->dataValueServiceFactory->newInfoLinksProvider( $this );
+ }
+
+ $this->infoLinksProvider->setServiceLinkParameters(
+ $this->getServiceLinkParams()
+ );
+
+ return $this->infoLinksProvider->createInfoLinks();
+ }
+
+ /**
+ * Return a string that identifies the value of the object, and that can
+ * be used to compare different value objects.
+ * Possibly overwritten by subclasses (e.g. to ensure that returned
+ * value is normalized first)
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->isValid() ? $this->m_dataitem->getHash() : implode( "\t", $this->mErrors );
+ }
+
+ /**
+ * Convenience method that checks if the value that is used to sort
+ * data of this type is numeric. This only works if the value is set.
+ *
+ * @return boolean
+ */
+ public function isNumeric() {
+ if ( isset( $this->m_dataitem ) ) {
+ return is_numeric( $this->m_dataitem->getSortKey() );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return true if a value was defined and understood by the given type,
+ * and false if parsing errors occurred or no value was given.
+ *
+ * @return boolean
+ */
+ public function isValid() {
+ return !$this->mHasErrors && isset( $this->m_dataitem );
+ }
+
+ /**
+ * Whether a datavalue can be used or not (can be made more restrictive then
+ * isValid).
+ *
+ * @note Validity defines a processable state without any technical restrictions
+ * while usability is determined by its accessibility to a context
+ * (permission, convention etc.)
+ *
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canUse() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isRestricted() {
+ return false;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $name
+ * @param array $parameters
+ *
+ * @return mixed
+ * @throws RuntimeException
+ */
+ public function getExtraneousFunctionFor( $name, array $parameters = [] ) {
+ return $this->dataValueServiceFactory->newExtraneousFunctionByName( $name, $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $data
+ */
+ public function setExtensionData( $key, $data ) {
+ $this->extenstionData[$key] = $data;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getExtensionData( $key ) {
+
+ if ( isset( $this->extenstionData[$key] ) ) {
+ return $this->extenstionData[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Options|null $options
+ */
+ public function copyOptions( Options $options = null ) {
+
+ if ( $options === null ) {
+ return;
+ }
+
+ foreach ( $options->getOptions() as $key => $value ) {
+ $this->setOption( $key, $value );
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string $key
+ * @param mxied $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return mixed|false
+ */
+ public function getOption( $key, $default = false ) {
+
+ if ( $this->options !== null && $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function hasFeature( $feature ) {
+
+ if ( $this->options !== null ) {
+ return $this->options->isFlagSet( 'smwgDVFeatures', (int)$feature );
+ }
+
+ return false;
+ }
+
+ /**
+ * @deprecated since 3.0, use DataValue::hasFeature
+ * @since 2.4
+ */
+ public function isEnabledFeature( $feature ) {
+ return $this->hasFeature( $feature );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return Options
+ */
+ protected function getOptions() {
+ return $this->options;
+ }
+
+ /**
+ * Initialise the datavalue from the given value string.
+ * The format of this strings might be any acceptable user input
+ * and especially includes the output of getWikiValue().
+ *
+ * @param string $value
+ */
+ abstract protected function parseUserValue( $value );
+
+ /**
+ * Set the actual data contained in this object. The method returns
+ * true if this was successful (requiring the type of the dataitem
+ * to match the data value). If false is returned, the data value is
+ * left unchanged (the data item was rejected).
+ *
+ * @note Even if this function returns true, the data value object
+ * might become invalid if the content of the data item caused errors
+ * in spite of it being of the right basic type. False is only returned
+ * if the data item is fundamentally incompatible with the data value.
+ *
+ * @since 1.6
+ *
+ * @param SMWDataItem $dataItem
+ *
+ * @return boolean
+ */
+ abstract protected function loadDataItem( SMWDataItem $dataItem );
+
+ /**
+ * Overwritten by callers to supply an array of parameters that can be used for
+ * creating servicelinks. The number and content of values in the parameter array
+ * may vary, depending on the concrete datatype.
+ */
+ protected function getServiceLinkParams() {
+ return false;
+ }
+
+ /**
+ * Check if property is range restricted and, if so, whether the current value is allowed.
+ * Creates an error if the value is illegal.
+ */
+ protected function checkAllowedValues() {
+
+ if ( $this->dataValueServiceFactory === null ) {
+ return;
+ }
+
+ $this->dataValueServiceFactory->getConstraintValueValidator()->validate( $this );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php
new file mode 100644
index 00000000..e4c81210
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exp_Data.php
@@ -0,0 +1,328 @@
+<?php
+
+use SMW\Exporter\Element;
+
+/**
+ * SMWExpData is a class representing semantic data that is ready for easy
+ * serialisation in OWL or RDF.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMW
+ */
+
+
+
+/**
+ * SMWExpData is a data container for export-ready semantic content. It is
+ * organised as a tree-shaped data structure with one root subject and zero
+ * or more children connected with labelled edges to the root. Children are
+ * again SMWExpData objects, and edges are annotated with SMWExpNsElements
+ * specifying properties.
+ * @note We do not allow property element without namespace abbreviation
+ * here. Property aabbreviations are mandatory for some serialisations.
+ *
+ * @ingroup SMW
+ */
+class SMWExpData implements Element {
+
+ /**
+ * @var DataItem|null
+ */
+ private $dataItem;
+
+ /**
+ * The subject of the data that we store.
+ * @var SMWExpResource
+ */
+ protected $m_subject;
+
+ /**
+ * Array mapping property URIs to arrays their values, given as
+ * SMWExpElement objects.
+ * @var array of array of SMWElement
+ */
+ protected $m_children = [];
+
+ /**
+ * Array mapping property URIs to arrays their SMWExpResource
+ * @var array of SMWExpResource
+ */
+ protected $m_edges = [];
+
+ /**
+ * @var string|null
+ */
+ private $hash = null;
+
+ /**
+ * Constructor. $subject is the SMWExpResource for the
+ * subject about which this SMWExpData is.
+ */
+ public function __construct( SMWExpResource $subject ) {
+ $this->dataItem = $subject->getDataItem();
+ $this->m_subject = $subject;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DataItem|null
+ */
+ public function getDataItem() {
+ return $this->dataItem;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ if ( $this->hash !== null ) {
+ return $this->hash;
+ }
+
+ $hashes = [];
+ $hashes[] = $this->m_subject->getHash();
+
+ foreach ( $this->getProperties() as $property ) {
+
+ $hashes[] = $property->getHash();
+
+ foreach ( $this->getValues( $property ) as $child ) {
+ $hashes[] = $child->getHash();
+ }
+ }
+
+ sort( $hashes );
+
+ $this->hash = md5( implode( '#', $hashes ) );
+ unset( $hashes );
+
+ return $this->hash;
+ }
+
+ /**
+ * Turn an array of SMWExpElements into an RDF collection.
+ *
+ * @param $elements array of SMWExpElement
+ * @return SMWExpData
+ */
+ public static function makeCollection( array $elements ) {
+
+ if ( count( $elements ) == 0 ) {
+ return new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'nil' ) );
+ }
+
+ $result = new SMWExpData( new SMWExpResource( '' ) ); // bnode
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
+ new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'List' ) )
+ );
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'first' ),
+ array_shift( $elements )
+ );
+
+ $result->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'rest' ),
+ self::makeCollection( $elements )
+ );
+
+ return $result;
+ }
+
+ /**
+ * Return subject to which the stored semantic annotation refer to.
+ *
+ * @return SMWExpResource
+ */
+ public function getSubject() {
+ return $this->m_subject;
+ }
+
+ /**
+ * Store a value for a property identified by its title object. No
+ * duplicate elimination as this is usually done in SMWSemanticData
+ * already (which is typically used to generate this object).
+ *
+ * @param SMWExpNsResource $property
+ * @param Element $child
+ */
+ public function addPropertyObjectValue( SMWExpNsResource $property, Element $child ) {
+
+ $this->hash = null;
+
+ if ( !array_key_exists( $property->getUri(), $this->m_edges ) ) {
+ $this->m_children[$property->getUri()] = [];
+ $this->m_edges[$property->getUri()] = $property;
+ }
+
+ $this->m_children[$property->getUri()][] = $child;
+ }
+
+ /**
+ * Return the list of SMWExpResource objects for all properties for
+ * which some values have been given.
+ *
+ * @return array of SMWExpResource
+ */
+ public function getProperties() {
+ return $this->m_edges;
+ }
+
+ /**
+ * Return the list of SMWExpElement values associated to some property
+ * (element).
+ *
+ * @return array of SMWExpElement
+ */
+ public function getValues( SMWExpResource $property ) {
+
+ if ( array_key_exists( $property->getUri(), $this->m_children ) ) {
+ return $this->m_children[$property->getUri()];
+ }
+
+ return [];
+ }
+
+ /**
+ * Return the list of SMWExpData values associated to some property that is
+ * specified by a standard namespace id and local name.
+ *
+ * @param $namespaceId string idetifying a known special namespace (e.g. "rdf")
+ * @param $localName string of local name (e.g. "type")
+ * @return array of SMWExpData
+ */
+ public function getSpecialValues( $namespaceId, $localName ) {
+ $pe = SMWExporter::getInstance()->getSpecialNsResource( $namespaceId, $localName );
+ return $this->getValues( $pe );
+ }
+
+ /**
+ * This function finds the main type (class) element of the subject
+ * based on the current property assignments. It returns this type
+ * element (SMWExpElement) and removes the according type assignement
+ * from the data. If no type is assigned, the element for rdf:Resource
+ * is returned.
+ *
+ * @note Under all normal conditions, the result will be an
+ * SMWExpResource.
+ *
+ * @return SMWExpElement
+ */
+ public function extractMainType() {
+ $pe = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' );
+ if ( array_key_exists( $pe->getUri(), $this->m_children ) ) {
+ $result = array_shift( $this->m_children[$pe->getUri()] );
+ if ( count( $this->m_children[$pe->getUri()] ) == 0 ) {
+ unset( $this->m_edges[$pe->getUri()] );
+ unset( $this->m_children[$pe->getUri()] );
+ }
+ return ( $result instanceof SMWExpData ) ? $result->getSubject() : $result;
+ } else {
+ return SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'Resource' );
+ }
+ }
+
+ /**
+ * Check if this element encodes an RDF list, and if yes return an
+ * array of SMWExpElements corresponding to the collection elements in
+ * the specified order. Otherwise return false.
+ * The method only returns lists that can be encoded using
+ * parseType="Collection" in RDF/XML, i.e. only lists of non-literal
+ * resources.
+ *
+ * @return mixed array of SMWExpElement (but not SMWExpLiteral) or false
+ */
+ public function getCollection() {
+ $rdftypeUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' )->getUri();
+ $rdffirstUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'first' )->getUri();
+ $rdfrestUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'rest' )->getUri();
+ $rdfnilUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'nil' )->getUri();
+ // first check if we are basically an RDF List:
+ if ( ( $this->m_subject->isBlankNode() ) &&
+ ( count( $this->m_children ) == 3 ) &&
+ ( array_key_exists( $rdftypeUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdftypeUri] ) == 1 ) &&
+ ( array_key_exists( $rdffirstUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdffirstUri] ) == 1 ) &&
+ !( end( $this->m_children[$rdffirstUri] ) instanceof SMWExpLiteral ) &&
+ // (parseType collection in RDF not possible with literals :-/)
+ ( array_key_exists( $rdfrestUri, $this->m_children ) ) &&
+ ( count( $this->m_children[$rdfrestUri] ) == 1 ) ) {
+ $typedata = end( $this->m_children[$rdftypeUri] );
+ $rdflistUri = SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'List' )->getUri();
+ if ( $typedata->getSubject()->getUri() == $rdflistUri ) {
+ $first = end( $this->m_children[$rdffirstUri] );
+ $rest = end( $this->m_children[$rdfrestUri] );
+ if ( $rest instanceof SMWExpData ) {
+ $restlist = $rest->getCollection();
+ if ( $restlist === false ) {
+ return false;
+ } else {
+ array_unshift( $restlist, $first );
+ return $restlist;
+ }
+ } elseif ( ( $rest instanceof SMWExpResource ) &&
+ ( $rest->getUri() == $rdfnilUri ) ) {
+ return [ $first ];
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } elseif ( ( count( $this->m_children ) == 0 ) && ( $this->m_subject->getUri() == $rdfnilUri ) ) {
+ return [];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return an array of ternary arrays (subject predicate object) of
+ * SMWExpElements that represents the flattened version of this data.
+ *
+ * @return array of array of SMWExpElement
+ */
+ public function getTripleList( Element $subject = null ) {
+ global $smwgBnodeCount;
+ if ( !isset( $smwgBnodeCount ) ) {
+ $smwgBnodeCount = 0;
+ }
+
+ if ( $subject == null ) {
+ $subject = $this->m_subject;
+ }
+
+ $result = [];
+
+ foreach ( $this->m_edges as $key => $edge ) {
+ foreach ( $this->m_children[$key] as $childElement ) {
+ if ( $childElement instanceof SMWExpData ) {
+ $childSubject = $childElement->getSubject();
+ } else {
+ $childSubject = $childElement;
+ }
+
+ if ( ( $childSubject instanceof SMWExpResource ) &&
+ ( $childSubject->isBlankNode() ) ) { // bnode, rename ID to avoid unifying bnodes of different contexts
+ // TODO: should we really rename bnodes of the form "_id" here?
+ $childSubject = new SMWExpResource( '_' . $smwgBnodeCount++, $childSubject->getDataItem() );
+ }
+
+ $result[] = [ $subject, $edge, $childSubject ];
+ if ( $childElement instanceof SMWExpData ) { // recursively add child's triples
+ $result = array_merge( $result, $childElement->getTripleList( $childSubject ) );
+ }
+ }
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php
new file mode 100644
index 00000000..3e40c077
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_ExportController.php
@@ -0,0 +1,768 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\Escaper;
+use SMW\Query\PrintRequest;
+use SMW\SemanticData;
+use SMW\Site;
+
+/**
+ * File holding the SMWExportController class that provides basic functions for
+ * exporting pages to RDF and OWL.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for controlling the export of SMW page data, supporting high-level
+ * features such as recursive export and backlink inclusion. The class controls
+ * export independent of the serialisation syntax that is used.
+ *
+ * @ingroup SMW
+ */
+class SMWExportController {
+ const MAX_CACHE_SIZE = 5000; // do not let cache arrays get larger than this
+ const CACHE_BACKJUMP = 500; // kill this many cached entries if limit is reached,
+ // avoids too much array copying; <= MAX_CACHE_SIZE!
+ /**
+ * The object used for serialisation.
+ * @var SMWSerializer
+ */
+ protected $serializer;
+ /**
+ * An array that keeps track of the elements for which we still need to
+ * write auxiliary definitions/declarations.
+ */
+ protected $element_queue;
+ /**
+ * An array that keeps track of the recursion depth with which each object
+ * has been serialised.
+ */
+ protected $element_done;
+ /**
+ * Boolean to indicate whether all objects that are exported in full (with
+ * all data) should also lead to the inclusion of all "inlinks" that they
+ * receive from other objects. If yes, these other objects are also
+ * serialised with at least the relevant inlinking properties included.
+ * Adding such dependencies counts as "recursive serialisation" and whether
+ * or not inlinking objects are included in full depends on the setting for
+ * recursion depth. Setting this to true enables "browsable RDF".
+ */
+ protected $add_backlinks;
+ /**
+ * Controls how long to wait until flushing content to output. Flushing
+ * early may reduce the memory footprint of serialization functions.
+ * Flushing later has some advantages for export formats like RDF/XML where
+ * global namespace declarations are only possible by modifying the header,
+ * so that only local declarations are possible after the first flush.
+ */
+ protected $delay_flush;
+ /**
+ * File handle for a potential output file to write to, or null if printing
+ * to standard output.
+ */
+ protected $outputfile;
+
+ /**
+ * @var DeepRedirectTargetResolver
+ */
+ private $deepRedirectTargetResolver = null;
+
+ /**
+ * Constructor.
+ * @param SMWSerializer $serializer defining the object used for syntactic
+ * serialization.
+ * @param boolean $enable_backlinks defining if backlinks are included,
+ * see $add_backlinks for details.
+ */
+ public function __construct( SMWSerializer $serializer, $enable_backlinks = false ) {
+ $this->serializer = $serializer;
+ $this->outputfile = null;
+ $this->add_backlinks = $enable_backlinks;
+ }
+
+ /**
+ * Enable or disable inclusion of backlinks into the output.
+ * @param boolean $enable
+ */
+ public function enableBacklinks( $enable ) {
+ $this->add_backlinks = $enable;
+ }
+
+ /**
+ * Initialize all internal structures to begin with some serialization.
+ * Returns true if initialization was successful (this means that the
+ * optional output file is writable).
+ * @param string $outfilename URL of the file that output should be written
+ * to, or empty string for writing to the standard output.
+ *
+ * @return boolean
+ */
+ protected function prepareSerialization( $outfilename = '' ) {
+ $this->serializer->clear();
+ $this->element_queue = [];
+ $this->element_done = [];
+ if ( $outfilename !== '' ) {
+ $this->outputfile = fopen( $outfilename, 'w' );
+ if ( !$this->outputfile ) { // TODO Rather throw an exception here.
+ print "\nCannot open \"$outfilename\" for writing.\n";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Serialize data associated to a specific page. This method works on the
+ * level of pages, i.e. it serialises parts of SMW content and implements
+ * features like recursive export or backlinks that are available for this
+ * type of data.
+ *
+ * The recursion depth means the following. Depth of 1 or above means
+ * the object is serialised with all property values, and referenced
+ * objects are serialised with depth reduced by 1. Depth 0 means that only
+ * minimal declarations are serialised, so no dependencies are added. A
+ * depth of -1 encodes "infinite" depth, i.e. a complete recursive
+ * serialisation without limit.
+ *
+ * @param SMWDIWikiPage $diWikiPage specifying the page to be exported
+ * @param integer $recursiondepth specifying the depth of recursion
+ */
+ protected function serializePage( SMWDIWikiPage $diWikiPage, $recursiondepth = 1 ) {
+
+ if ( $this->isPageDone( $diWikiPage, $recursiondepth ) ) {
+ return; // do not export twice
+ }
+
+ $this->markPageAsDone( $diWikiPage, $recursiondepth );
+ $semData = $this->getSemanticData( $diWikiPage, ( $recursiondepth == 0 ) );
+
+ // Don't try to serialize an empty page that cause an incomplete exp-data set
+ // (e.g. _REDI as no property page hence DBKey is empty)
+ if ( $semData === null || $diWikiPage->getDBKey() === '' ) {
+ return null;
+ }
+
+ $expData = SMWExporter::getInstance()->makeExportData( $semData );
+ $this->serializer->serializeExpData( $expData, $recursiondepth );
+
+ foreach( $semData->getSubSemanticData() as $subSemanticData ) {
+
+ // Mark SubSemanticData subjects as well to ensure that backlinks to
+ // the same subject do not create duplicate XML export entities
+ $this->markPageAsDone(
+ $subSemanticData->getSubject(),
+ $recursiondepth
+ );
+
+ $expData = SMWExporter::getInstance()->makeExportData(
+ $subSemanticData
+ );
+
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ // let other extensions add additional RDF data for this page
+ $expDataList = [];
+
+ \Hooks::run(
+ 'SMW::Exporter::Controller::AddExpData',
+ [
+ $diWikiPage,
+ &$expDataList,
+ ( $recursiondepth != 0 ),
+ $this->add_backlinks
+ ]
+ );
+
+ foreach ( $expDataList as $data ) {
+
+ if ( !$data instanceof SMWExpData ) {
+ continue;
+ }
+
+ $this->serializer->serializeExpData( $data );
+ }
+
+ if ( $recursiondepth != 0 ) {
+ $subrecdepth = $recursiondepth > 0 ? ( $recursiondepth - 1 ) :
+ ( $recursiondepth == 0 ? 0 : -1 );
+
+ foreach ( $expData->getProperties() as $property ) {
+ if ( $property->getDataItem() instanceof SMWWikiPageValue ) {
+ $this->queuePage( $property->getDataItem(), 0 ); // no real recursion along properties
+ }
+ $wikipagevalues = false;
+ foreach ( $expData->getValues( $property ) as $valueExpElement ) {
+ $valueResource = $valueExpElement instanceof SMWExpData ? $valueExpElement->getSubject() : $valueExpElement;
+ if ( !$wikipagevalues && ( $valueResource->getDataItem() instanceof SMWWikiPageValue ) ) {
+ $wikipagevalues = true;
+ } elseif ( !$wikipagevalues ) {
+ break;
+ }
+ $this->queuePage( $valueResource->getDataItem(), $subrecdepth );
+ }
+ }
+
+ // Add backlinks:
+ // Note: Backlinks are different from recursive serialisations, since
+ // stub declarations (recdepth==0) still need to have the property that
+ // links back to the object. So objects that would be exported with
+ // recdepth 0 cannot be put into the main queue but must be done right
+ // away. They also might be required many times, if they link back to
+ // many different objects in many ways (we cannot consider them "Done"
+ // if they were serialised at recdepth 0 only).
+ if ( $this->add_backlinks ) {
+ $inprops = \SMW\StoreFactory::getStore()->getInProperties( $diWikiPage );
+
+ foreach ( $inprops as $inprop ) {
+ $propWikiPage = $inprop->getCanonicalDiWikiPage();
+
+ if ( !is_null( $propWikiPage ) ) {
+ $this->queuePage( $propWikiPage, 0 ); // no real recursion along properties
+ }
+
+ $inSubs = \SMW\StoreFactory::getStore()->getPropertySubjects( $inprop, $diWikiPage );
+
+ foreach ( $inSubs as $inSub ) {
+ if ( !$this->isPageDone( $inSub, $subrecdepth ) ) {
+ $semdata = $this->getSemanticData( $inSub, true );
+
+ if ( !$semdata instanceof SMWSemanticData ) {
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $inprop, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData, $subrecdepth );
+ }
+ }
+ }
+
+ if ( NS_CATEGORY === $diWikiPage->getNamespace() ) { // also print elements of categories
+ $options = new SMWRequestOptions();
+ $options->limit = 100; // Categories can be large, always use limit
+ $instances = \SMW\StoreFactory::getStore()->getPropertySubjects( new SMW\DIProperty( '_INST' ), $diWikiPage, $options );
+ $pinst = new SMW\DIProperty( '_INST' );
+
+ foreach ( $instances as $instance ) {
+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
+ $semdata = $this->getSemanticData( $instance, true );
+
+ if ( !$semdata instanceof SMWSemanticData ) {
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData, $subrecdepth );
+ }
+ }
+ } elseif ( SMW_NS_CONCEPT === $diWikiPage->getNamespace() ) { // print concept members (slightly different code)
+ $desc = new SMWConceptDescription( $diWikiPage );
+ $desc->addPrintRequest( new PrintRequest( PrintRequest::PRINT_THIS, '' ) );
+ $query = new SMWQuery( $desc );
+ $query->setLimit( 100 );
+
+ $res = \SMW\StoreFactory::getStore()->getQueryResult( $query );
+ $resarray = $res->getNext();
+ $pinst = new SMW\DIProperty( '_INST' );
+
+ while ( $resarray !== false ) {
+ $instance = end( $resarray )->getNextDataItem();
+
+ if ( !$instance instanceof \SMWDataItem ) {
+ $resarray = $res->getNext();
+ continue;
+ }
+
+ if ( !array_key_exists( $instance->getHash(), $this->element_done ) ) {
+ $semdata = $this->getSemanticData( $instance, true );
+
+ if ( !$semdata instanceof \SMW\SemanticData ) {
+ $resarray = $res->getNext();
+ continue;
+ }
+
+ $semdata->addPropertyObjectValue( $pinst, $diWikiPage );
+ $expData = SMWExporter::getInstance()->makeExportData( $semdata );
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ $resarray = $res->getNext();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a given SMWDIWikiPage to the export queue if needed.
+ */
+ protected function queuePage( SMWDIWikiPage $diWikiPage, $recursiondepth ) {
+ if ( !$this->isPageDone( $diWikiPage, $recursiondepth ) ) {
+ $diWikiPage->recdepth = $recursiondepth; // add a field
+ $this->element_queue[$diWikiPage->getHash()] = $diWikiPage;
+ }
+ }
+
+ /**
+ * Mark an article as done while making sure that the cache used for this
+ * stays reasonably small. Input is given as an SMWDIWikiPage object.
+ */
+ protected function markPageAsDone( SMWDIWikiPage $di, $recdepth ) {
+ $this->markHashAsDone( $di->getHash(), $recdepth );
+ }
+
+ /**
+ * Mark a task as done while making sure that the cache used for this
+ * stays reasonably small.
+ */
+ protected function markHashAsDone( $hash, $recdepth ) {
+ if ( count( $this->element_done ) >= self::MAX_CACHE_SIZE ) {
+ $this->element_done = array_slice( $this->element_done,
+ self::CACHE_BACKJUMP,
+ self::MAX_CACHE_SIZE - self::CACHE_BACKJUMP,
+ true );
+ }
+ if ( !$this->isHashDone( $hash, $recdepth ) ) {
+ $this->element_done[$hash] = $recdepth; // mark title as done, with given recursion
+ }
+ unset( $this->element_queue[$hash] ); // make sure it is not in the queue
+ }
+
+ /**
+ * Check if the given object has already been serialised at sufficient
+ * recursion depth.
+ * @param SMWDIWikiPage $st specifying the object to check
+ *
+ * @return boolean
+ */
+ protected function isPageDone( SMWDIWikiPage $di, $recdepth ) {
+ return $this->isHashDone( $di->getHash(), $recdepth );
+ }
+
+ /**
+ * Check if the given task has already been completed at sufficient
+ * recursion depth.
+ */
+ protected function isHashDone( $hash, $recdepth ) {
+ return ( ( array_key_exists( $hash, $this->element_done ) ) &&
+ ( ( $this->element_done[$hash] == -1 ) ||
+ ( ( $recdepth != -1 ) && ( $this->element_done[$hash] >= $recdepth ) ) ) );
+ }
+
+ /**
+ * Retrieve a copy of the semantic data for a wiki page, possibly filtering
+ * it so that only essential properties are included (in some cases, we only
+ * want to export stub information about a page).
+ * We make a copy of the object since we may want to add more data later on
+ * and we do not want to modify the store's result which may be used for
+ * caching purposes elsewhere.
+ */
+ protected function getSemanticData( SMWDIWikiPage $diWikiPage, $core_props_only ) {
+
+ // Issue 619
+ // Resolve the redirect target and return a container with information
+ // about the redirect
+ if ( $diWikiPage->getTitle() !== null && $diWikiPage->getTitle()->isRedirect() ) {
+
+ try {
+ $redirectTarget = $this->getDeepRedirectTargetResolver()->findRedirectTargetFor( $diWikiPage->getTitle() );
+ } catch ( \Exception $e ) {
+ $redirectTarget = null;
+ }
+
+ // Couldn't resolve the redirect which is most likely caused by a
+ // circular redirect therefore we give up
+ if ( $redirectTarget === null ) {
+ return null;
+ }
+
+ $semData = new SemanticData( $diWikiPage );
+
+ $semData->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ DIWikiPage::newFromTitle( $redirectTarget )
+ );
+
+ return $semData;
+ }
+
+ $semdata = \SMW\StoreFactory::getStore()->getSemanticData( $diWikiPage, $core_props_only ? [ '__spu', '__typ', '__imp' ] : false ); // advise store to retrieve only core things
+ if ( $core_props_only ) { // be sure to filter all non-relevant things that may still be present in the retrieved
+ $result = new SMWSemanticData( $diWikiPage );
+ foreach ( [ '_URI', '_TYPE', '_IMPO' ] as $propid ) {
+ $prop = new SMW\DIProperty( $propid );
+ $values = $semdata->getPropertyValues( $prop );
+ foreach ( $values as $dv ) {
+ $result->addPropertyObjectValue( $prop, $dv );
+ }
+ }
+ } else {
+ $result = clone $semdata;
+ }
+
+
+ return $result;
+ }
+
+ /**
+ * Send to the output what has been serialized so far. The flush might
+ * be deferred until later unless $force is true.
+ */
+ protected function flush( $force = false ) {
+ if ( !$force && ( $this->delay_flush > 0 ) ) {
+ $this->delay_flush -= 1;
+ } elseif ( !is_null( $this->outputfile ) ) {
+ fwrite( $this->outputfile, $this->serializer->flushContent() );
+ } else {
+ ob_start();
+ print $this->serializer->flushContent();
+ // Ship data in small chunks (even though browsers often do not display anything
+ // before the file is complete -- this might be due to syntax highlighting features
+ // for app/xml). You may want to sleep(1) here for debugging this.
+ ob_flush();
+ flush();
+ ob_get_clean();
+ }
+ }
+
+ /**
+ * This function prints all selected pages, specified as an array of page
+ * names (strings with namespace identifiers).
+ *
+ * @param array $pages list of page names to export
+ * @param integer $recursion determines how pages are exported recursively:
+ * "0" means that referenced resources are only declared briefly, "1" means
+ * that all referenced resources are also exported recursively (propbably
+ * retrieving the whole wiki).
+ * @param string $revisiondate filter page list by including only pages
+ * that have been changed since this date; format "YmdHis"
+ *
+ * @todo Consider dropping the $revisiondate filtering and all associated
+ * functionality. Is anybody using this?
+ */
+ public function printPages( $pages, $recursion = 1, $revisiondate = false ) {
+
+ $linkCache = LinkCache::singleton();
+ $this->prepareSerialization();
+ $this->delay_flush = 10; // flush only after (fully) printing 11 objects
+
+ // transform pages into queued short titles
+ foreach ( $pages as $page ) {
+ $title = Title::newFromText( $page );
+ if ( null === $title ) {
+ continue; // invalid title name given
+ }
+ if ( $revisiondate !== '' ) { // filter page list by revision date
+ $rev = Revision::getTimeStampFromID( $title, $title->getLatestRevID() );
+ if ( $rev < $revisiondate ) {
+ continue;
+ }
+ }
+
+ $diPage = SMWDIWikiPage::newFromTitle( $title );
+ $this->queuePage( $diPage, ( $recursion==1 ? -1 : 1 ) );
+ }
+
+ $this->serializer->startSerialization();
+
+ if ( count( $pages ) == 1 ) { // ensure that ontologies that are retrieved as linked data are not confused with their subject!
+ $ontologyuri = SMWExporter::getInstance()->expandURI( '&export;' ) . '/' . Escaper::encodeUri( end( $pages ) );
+ } else { // use empty URI, i.e. "location" as URI otherwise
+ $ontologyuri = '';
+ }
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( $ontologyuri ) );
+
+ while ( count( $this->element_queue ) > 0 ) {
+ $diPage = reset( $this->element_queue );
+ $this->serializePage( $diPage, $diPage->recdepth );
+ $this->flush();
+ $linkCache->clear(); // avoid potential memory leak
+ }
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+ /**
+ * Exports semantic data for all pages within the wiki and for all elements
+ * that are referred to a file resource
+ *
+ * @since 2.0
+ *
+ * @param string $outfile the output file URI, or false if printing to stdout
+ * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
+ * @param integer $delay number of microseconds for which to sleep during
+ * export to reduce server load in long-running operations
+ * @param integer $delayeach number of pages to process between two sleeps
+ */
+ public function printAllToFile( $outfile, $ns_restriction = false, $delay, $delayeach ) {
+
+ if ( !$this->prepareSerialization( $outfile ) ) {
+ return;
+ }
+
+ $this->printAll( $ns_restriction, $delay, $delayeach );
+ }
+
+ /**
+ * Exports semantic data for all pages within the wiki and for all elements
+ * that are referred to the stdout
+ *
+ * @since 2.0
+ *
+ * @param mixed $ns_restriction namespace restriction, see fitsNsRestriction()
+ * @param integer $delay number of microseconds for which to sleep during
+ * export to reduce server load in long-running operations
+ * @param integer $delayeach number of pages to process between two sleeps
+ */
+ public function printAllToOutput( $ns_restriction = false, $delay, $delayeach ) {
+ $this->prepareSerialization();
+ $this->printAll( $ns_restriction, $delay, $delayeach );
+ }
+
+ /**
+ * @since 2.0 made protected; use printAllToFile or printAllToOutput
+ */
+ protected function printAll( $ns_restriction = false, $delay, $delayeach ) {
+ $linkCache = LinkCache::singleton();
+ $db = wfGetDB( DB_SLAVE );
+
+ $this->delay_flush = 10;
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ $end = $db->selectField( 'page', 'max(page_id)', false, __METHOD__ );
+ $a_count = 0; // DEBUG
+ $d_count = 0; // DEBUG
+ $delaycount = $delayeach;
+
+ for ( $id = 1; $id <= $end; $id += 1 ) {
+ $title = Title::newFromID( $id );
+ if ( is_null( $title ) || !\SMW\NamespaceExaminer::getInstance()->isSemanticEnabled( $title->getNamespace() ) ) {
+ continue;
+ }
+ if ( !self::fitsNsRestriction( $ns_restriction, $title->getNamespace() ) ) {
+ continue;
+ }
+ $a_count += 1; // DEBUG
+
+ $diPage = SMWDIWikiPage::newFromTitle( $title );
+ $this->queuePage( $diPage, 1 );
+
+ while ( count( $this->element_queue ) > 0 ) {
+ $diPage = reset( $this->element_queue );
+ $this->serializePage( $diPage, $diPage->recdepth );
+ // resolve dependencies that will otherwise not be printed
+ foreach ( $this->element_queue as $key => $diaux ) {
+ if ( !\SMW\NamespaceExaminer::getInstance()->isSemanticEnabled( $diaux->getNamespace() ) ||
+ !self::fitsNsRestriction( $ns_restriction, $diaux->getNamespace() ) ) {
+ // Note: we do not need to check the cache to guess if an element was already
+ // printed. If so, it would not be included in the queue in the first place.
+ $d_count += 1; // DEBUG
+ } else { // don't carry values that you do not want to export (yet)
+ unset( $this->element_queue[$key] );
+ }
+ }
+ // sleep each $delaycount for $delay µs to be nice to the server
+ if ( ( $delaycount-- < 0 ) && ( $delayeach != 0 ) ) {
+ usleep( $delay );
+ $delaycount = $delayeach;
+ }
+ }
+
+ $this->flush();
+ $linkCache->clear();
+ }
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+ }
+
+ /**
+ * Print basic definitions a list of pages ordered by their page id.
+ * Offset and limit refer to the count of existing pages, not to the
+ * page id.
+ * @param integer $offset the number of the first (existing) page to
+ * serialize a declaration for
+ * @param integer $limit the number of pages to serialize
+ */
+ public function printPageList( $offset = 0, $limit = 30 ) {
+ global $smwgNamespacesWithSemanticLinks;
+
+ $db = wfGetDB( DB_SLAVE );
+ $this->prepareSerialization();
+ $this->delay_flush = 35; // don't do intermediate flushes with default parameters
+ $linkCache = LinkCache::singleton();
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ $query = '';
+ foreach ( $smwgNamespacesWithSemanticLinks as $ns => $enabled ) {
+ if ( $enabled ) {
+ if ( $query !== '' ) {
+ $query .= ' OR ';
+ }
+ $query .= 'page_namespace = ' . $db->addQuotes( $ns );
+ }
+ }
+ $res = $db->select( $db->tableName( 'page' ),
+ 'page_id,page_title,page_namespace', $query,
+ 'SMW::RDF::PrintPageList', [ 'ORDER BY' => 'page_id ASC', 'OFFSET' => $offset, 'LIMIT' => $limit ] );
+ $foundpages = false;
+
+ foreach ( $res as $row ) {
+ $foundpages = true;
+ try {
+ $diPage = new SMWDIWikiPage( $row->page_title, $row->page_namespace, '' );
+ $this->serializePage( $diPage, 0 );
+ $this->flush();
+ $linkCache->clear();
+ } catch ( SMWDataItemException $e ) {
+ // strange data, who knows, not our DB table, keep calm and carry on
+ }
+ }
+
+ if ( $foundpages ) { // add link to next result page
+ if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=' ) . ( $offset + $limit );
+ } else {
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=' ) . ( $offset + $limit );
+ }
+
+ $expData = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $this->serializer->serializeExpData( $expData );
+ }
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+
+ /**
+ * Print basic information about this site.
+ */
+ public function printWikiInfo() {
+
+ $this->prepareSerialization();
+ $this->delay_flush = 35; // don't do intermediate flushes with default parameters
+
+ // assemble export data:
+ $expData = new SMWExpData( new SMWExpResource( '&wiki;#wiki' ) );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
+ new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'Wikisite' ) )
+ );
+
+ // basic wiki information
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'label' ),
+ new SMWExpLiteral( Site::name() )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'siteName' ),
+ new SMWExpLiteral( Site::name(), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'pagePrefix' ),
+ new SMWExpLiteral( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'smwVersion' ),
+ new SMWExpLiteral( SMW_VERSION, 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'langCode' ),
+ new SMWExpLiteral( Site::languageCode(), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+
+ $mainpage = Title::newMainPage();
+
+ if ( !is_null( $mainpage ) ) {
+ $ed = new SMWExpData( new SMWExpResource( $mainpage->getFullURL() ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'swivt', 'mainPage' ), $ed );
+ }
+
+ // statistical information
+ foreach ( Site::stats() as $key => $value ) {
+ $expData->addPropertyObjectValue(
+ SMWExporter::getInstance()->getSpecialNsResource( 'swivt', $key ),
+ new SMWExpLiteral( (string)$value, 'http://www.w3.org/2001/XMLSchema#int' )
+ );
+ }
+
+ $this->serializer->startSerialization();
+ $this->serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+ $this->serializer->serializeExpData( $expData );
+
+ // link to list of existing pages:
+ if ( strpos( SMWExporter::getInstance()->expandURI( '&wikiurl;' ), '?' ) === false ) { // check whether we have title as a first parameter or in URL
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;?offset=0' );
+ } else {
+ $nexturl = SMWExporter::getInstance()->expandURI( '&export;&amp;offset=0' );
+ }
+ $expData = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $ed = new SMWExpData( SMWExporter::getInstance()->getSpecialNsResource( 'owl', 'Thing' ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new SMWExpData( new SMWExpResource( $nexturl ) );
+ $expData->addPropertyObjectValue( SMWExporter::getInstance()->getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $this->serializer->serializeExpData( $expData );
+
+ $this->serializer->finishSerialization();
+ $this->flush( true );
+
+ }
+
+ /**
+ * This function checks whether some article fits into a given namespace
+ * restriction. Restrictions are encoded as follows: a non-negative number
+ * requires the namespace to be identical to the given number; "-1"
+ * requires the namespace to be different from Category, Property, and
+ * Type; "false" means "no restriction".
+ *
+ * @param $res mixed encoding the restriction as described above
+ * @param $ns integer the namespace constant to be checked
+ *
+ * @return boolean
+ */
+ static public function fitsNsRestriction( $res, $ns ) {
+ if ( $res === false ) {
+ return true;
+ }
+ if ( is_array( $res ) ) {
+ return in_array( $ns, $res );
+ }
+ if ( $res >= 0 ) {
+ return ( $res == $ns );
+ }
+ return ( ( $res != NS_CATEGORY ) && ( $res != SMW_NS_PROPERTY ) );
+ }
+
+ private function getDeepRedirectTargetResolver() {
+
+ if ( $this->deepRedirectTargetResolver === null ) {
+ $this->deepRedirectTargetResolver = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newDeepRedirectTargetResolver();
+ }
+
+ return $this->deepRedirectTargetResolver;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php
new file mode 100644
index 00000000..9f97d748
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Exporter.php
@@ -0,0 +1,675 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Site;
+use SMW\Exporter\DataItemMatchFinder;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ElementFactory;
+use SMW\Exporter\Escaper;
+use SMW\Exporter\ExpResourceMapper;
+use SMW\Exporter\ResourceBuilders\DispatchingResourceBuilder;
+use SMW\Localizer;
+use SMW\NamespaceUriFinder;
+
+/**
+ * SMWExporter is a class for converting internal page-based data (SMWSemanticData) into
+ * a format for easy serialisation in OWL or RDF.
+ *
+ * @author Markus Krötzsch
+ * @ingroup SMW
+ */
+class SMWExporter {
+
+ const POOLCACHE_ID = 'exporter.shared';
+
+ /**
+ * @var SMWExporter
+ */
+ private static $instance = null;
+
+ /**
+ * @var ExpResourceMapper
+ */
+ private static $expResourceMapper = null;
+
+ /**
+ * @var ElementFactory
+ */
+ private static $elementFactory = null;
+
+ /**
+ * @var DataItemMatchFinder
+ */
+ private static $dataItemMatchFinder = null;
+
+ /**
+ * @var DispatchingResourceBuilder
+ */
+ private static $dispatchingResourceBuilder = null;
+
+ static protected $m_exporturl = false;
+ static protected $m_ent_wiki = false;
+ static protected $m_ent_property = false;
+ static protected $m_ent_category = false;
+ static protected $m_ent_wikiurl = false;
+
+ /**
+ * @since 2.0
+ *
+ * @return SMWExporter
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+
+ self::$instance = new self();
+ self::$instance->initBaseURIs();
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $poolCache = $applicationFactory->getInMemoryPoolCache();
+ $poolCache->resetPoolCacheById( self::POOLCACHE_ID );
+
+ // FIXME with 3.x
+ // There is no better way of getting around the static use without BC
+ self::$elementFactory = new ElementFactory();
+
+ self::$dispatchingResourceBuilder = new DispatchingResourceBuilder();
+
+ self::$expResourceMapper = new ExpResourceMapper(
+ $applicationFactory->getStore()
+ );
+
+ self::$expResourceMapper->reset();
+
+ self::$expResourceMapper->setBCAuxiliaryUse(
+ $applicationFactory->getSettings()->get( 'smwgExportBCAuxiliaryUse' )
+ );
+
+ self::$dataItemMatchFinder = new DataItemMatchFinder(
+ $applicationFactory->getStore(),
+ self::$m_ent_wiki
+ );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static function clear() {
+ self::$instance = null;
+ self::$m_exporturl = false;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function resetCacheBy( SMWDIWikiPage $diWikiPage ) {
+ self::$expResourceMapper->invalidateCache( $diWikiPage );
+ }
+
+ /**
+ * Make sure that necessary base URIs are initialised properly.
+ */
+ static public function initBaseURIs() {
+ if ( self::$m_exporturl !== false ) {
+ return;
+ }
+ global $wgContLang;
+
+ global $smwgNamespace; // complete namespace for URIs (with protocol, usually http://)
+
+ $resolver = Title::makeTitle( NS_SPECIAL, 'URIResolver' );
+
+ if ( '' == $smwgNamespace ) {
+ $smwgNamespace = $resolver->getFullURL() . '/';
+ } elseif ( $smwgNamespace[0] == '.' ) {
+ $smwgNamespace = "http://" . substr( $smwgNamespace, 1 ) . $resolver->getLocalURL() . '/';
+ }
+
+ // The article name must be the last part of wiki URLs for proper OWL/RDF export:
+ self::$m_ent_wikiurl = Site::wikiurl();
+ self::$m_ent_wiki = $smwgNamespace;
+
+ $property = $GLOBALS['smwgExportBCNonCanonicalFormUse'] ? urlencode( str_replace( ' ', '_', $wgContLang->getNsText( SMW_NS_PROPERTY ) ) ) : 'Property';
+ $category = $GLOBALS['smwgExportBCNonCanonicalFormUse'] ? urlencode( str_replace( ' ', '_', $wgContLang->getNsText( NS_CATEGORY ) ) ) : 'Category';
+
+ self::$m_ent_property = self::$m_ent_wiki . Escaper::encodeUri( $property . ':' );
+ self::$m_ent_category = self::$m_ent_wiki . Escaper::encodeUri( $category . ':' );
+
+ $title = Title::makeTitle( NS_SPECIAL, 'ExportRDF' );
+ self::$m_exporturl = self::$m_ent_wikiurl . $title->getPrefixedURL();
+
+ // Canonical form, the title object always contains a wgContLang reference
+ // therefore replace it
+ if ( !$GLOBALS['smwgExportBCNonCanonicalFormUse'] ) {
+ $localizer = Localizer::getInstance();
+
+ self::$m_ent_property = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_property );
+ self::$m_ent_category = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_category );
+ self::$m_ent_wiki = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_ent_wiki );
+ self::$m_exporturl = $localizer->getCanonicalizedUrlByNamespace( NS_SPECIAL, self::$m_exporturl );
+ }
+ }
+
+ /**
+ * Create exportable data from a given semantic data record.
+ *
+ * @param $semdata SMWSemanticData
+ * @return SMWExpData
+ */
+ static public function makeExportData( SMWSemanticData $semdata ) {
+ self::initBaseURIs();
+
+ $subject = $semdata->getSubject();
+
+ // Make sure to use the canonical form, a localized representation
+ // should not carry a reference to a subject (e.g invoked as incoming
+ // property caused by a different user language)
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY && $subject->getSubobjectName() === '' ) {
+ $subject = DIProperty::newFromUserLabel( $subject->getDBKey() )->getCanonicalDiWikiPage();
+ }
+
+ // #1690 Couldn't match a CanonicalDiWikiPage which is most likely caused
+ // by an outdated pre-defined property therefore use the original subject
+ if ( $subject->getDBKey() === '' ) {
+ $subject = $semdata->getSubject();
+ }
+
+ // #649 Alwways make sure to have a least one valid sortkey
+ if ( !$semdata->getPropertyValues( new DIProperty( '_SKEY' ) ) && $subject->getSortKey() !== '' ) {
+
+ // @see SMWSQLStore3Writers::getSortKey
+ if ( $semdata->getExtensionData( 'sort.extension' ) !== null ) {
+ $sortkey = $semdata->getExtensionData( 'sort.extension' );
+ } else {
+ $sortkey = $subject->getSortKey();
+ }
+
+ // Extend the subobject sortkey in case no @sortkey was given for an
+ // entity
+ if ( $subject->getSubobjectName() !== '' ) {
+
+ // Add sort data from some dedicated containers (of a record or
+ // reference type etc.) otherwise use the sobj name as extension
+ // to distinguish each entity
+ if ( $semdata->getExtensionData( 'sort.data' ) !== null ) {
+ $sortkey .= '#' . $semdata->getExtensionData( 'sort.data' );
+ } else {
+ $sortkey .= '#' . $subject->getSubobjectName();
+ }
+ }
+
+ // #649 Be consistent about how sortkeys are stored therefore always
+ // normalize even for usages like {{DEFAULTSORT: Foo_bar }}
+ $sortkey = str_replace( '_', ' ', $sortkey );
+
+ $semdata->addPropertyObjectValue(
+ new DIProperty( '_SKEY' ),
+ new SMWDIBlob( $sortkey )
+ );
+ }
+
+ $result = self::makeExportDataForSubject( $subject );
+
+ foreach ( $semdata->getProperties() as $property ) {
+ self::addPropertyValues( $property, $semdata->getPropertyValues( $property ), $result, $subject );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Make an SMWExpData object for the given page, and include the basic
+ * properties about this subject that are not directly represented by
+ * SMW property values. The optional parameter $typevalueforproperty
+ * can be used to pass a particular SMWTypesValue object that is used
+ * for determining the OWL type for property pages.
+ *
+ * @todo Take into account whether the wiki page belongs to a builtin property, and ensure URI alignment/type declaration in this case.
+ *
+ * @param $diWikiPage SMWDIWikiPage
+ * @param $addStubData boolean to indicate if additional data should be added to make a stub entry for this page
+ * @return SMWExpData
+ */
+ static public function makeExportDataForSubject( SMWDIWikiPage $diWikiPage, $addStubData = false ) {
+ global $wgContLang;
+
+ $wikiPageExpElement = self::getDataItemExpElement( $diWikiPage );
+ $result = new SMWExpData( $wikiPageExpElement );
+
+ if ( $diWikiPage->getSubobjectName() !== '' ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'rdf', 'type' ),
+ self::getSpecialNsResource( 'swivt', 'Subject' )
+ );
+
+ $masterPage = new SMWDIWikiPage(
+ $diWikiPage->getDBkey(),
+ $diWikiPage->getNamespace(),
+ $diWikiPage->getInterwiki()
+ );
+
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'masterPage' ),
+ self::getDataItemExpElement( $masterPage )
+ );
+
+ // #649
+ // Subobjects contain there individual sortkey's therefore
+ // no need to add them twice
+
+ // #520
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'wikiNamespace' ),
+ new ExpLiteral( strval( $diWikiPage->getNamespace() ), 'http://www.w3.org/2001/XMLSchema#integer' )
+ );
+
+ } else {
+
+ // #1410 (use the display title as label when available)
+ $displayTitle = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage )->getDisplayTitle();
+
+ $pageTitle = str_replace( '_', ' ', $diWikiPage->getDBkey() );
+ if ( $diWikiPage->getNamespace() !== 0 ) {
+ $prefixedSubjectTitle = $wgContLang->getNsText( $diWikiPage->getNamespace()) . ":" . $pageTitle;
+ } else {
+ $prefixedSubjectTitle = $pageTitle;
+ }
+
+ $prefixedSubjectUrl = Escaper::encodeUri( str_replace( ' ', '_', $prefixedSubjectTitle ) );
+
+ switch ( $diWikiPage->getNamespace() ) {
+ case NS_CATEGORY: case SMW_NS_CONCEPT:
+ $maintype_pe = self::getSpecialNsResource( 'owl', 'Class' );
+ $label = $pageTitle;
+ break;
+ case SMW_NS_PROPERTY:
+ $property = new DIProperty( $diWikiPage->getDBKey() );
+ $maintype_pe = self::getSpecialNsResource( 'owl', self::getOWLPropertyType( $property->findPropertyTypeID() ) );
+ $label = $pageTitle;
+ break;
+ default:
+ $label = $prefixedSubjectTitle;
+ $maintype_pe = self::getSpecialNsResource( 'swivt', 'Subject' );
+ }
+
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), $maintype_pe );
+
+ if ( !$wikiPageExpElement->isBlankNode() ) {
+ $ed = new ExpLiteral( $displayTitle !== '' ? $displayTitle : $label );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'label' ), $ed );
+ $ed = new SMWExpResource( self::$m_exporturl . '/' . $prefixedSubjectUrl );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'rdfs', 'isDefinedBy' ), $ed );
+ $ed = new SMWExpResource( self::getNamespaceUri( 'wikiurl' ) . $prefixedSubjectUrl );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'page' ), $ed );
+ $ed = new ExpLiteral( strval( $diWikiPage->getNamespace() ), 'http://www.w3.org/2001/XMLSchema#integer' );
+ $result->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'wikiNamespace' ), $ed );
+
+ if ( $addStubData ) {
+ // Add a default sort key; for pages that exist in the wiki,
+ // this is set during parsing
+ $property = new DIProperty( '_SKEY' );
+ $resourceBuilder = self::$dispatchingResourceBuilder->findResourceBuilder( $property );
+ $resourceBuilder->addResourceValue( $result, $property, $diWikiPage );
+ }
+
+ if ( $diWikiPage->getPageLanguage() ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'wikiPageContentLanguage' ),
+ new ExpLiteral( strval( $diWikiPage->getPageLanguage() ), 'http://www.w3.org/2001/XMLSchema#string' )
+ );
+ }
+
+ if ( $diWikiPage->getNamespace() === NS_FILE ) {
+
+ $title = Title::makeTitle( $diWikiPage->getNamespace(), $diWikiPage->getDBkey() );
+ $file = wfFindFile( $title );
+
+ if ( $file !== false ) {
+ $result->addPropertyObjectValue(
+ self::getSpecialNsResource( 'swivt', 'file' ),
+ new SMWExpResource( $file->getFullURL() )
+ );
+ }
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Extend a given SMWExpData element by adding export data for the
+ * specified property data itme. This method is called when
+ * constructing export data structures from SMWSemanticData objects.
+ *
+ * @param $property SMWDIProperty
+ * @param $dataItems array of SMWDataItem objects for the given property
+ * @param $data SMWExpData to add the data to
+ */
+ static public function addPropertyValues( SMWDIProperty $property, array $dataItems, SMWExpData &$expData ) {
+
+ $resourceBuilder = self::$dispatchingResourceBuilder->findResourceBuilder( $property );
+
+ if ( $property->isUserDefined() ) {
+ foreach ( $dataItems as $dataItem ) {
+ $resourceBuilder->addResourceValue( $expData, $property, $dataItem );
+ }
+ } else { // pre-defined property, only exported if known
+ $diSubject = $expData->getSubject()->getDataItem();
+ // subject wikipage required for disambiguating special properties:
+ if ( is_null( $diSubject ) ||
+ $diSubject->getDIType() != SMWDataItem::TYPE_WIKIPAGE ) {
+ return;
+ }
+
+ $pe = self::getSpecialPropertyResource( $property->getKey(), $diSubject->getNamespace() );
+ if ( is_null( $pe ) ) {
+ return; // unknown special property, not exported
+ }
+ // have helper property ready before entering the for loop, even if not needed:
+ $peHelper = self::getResourceElementForProperty( $property, true );
+
+ $filterNamespace = ( $property->getKey() == '_REDI' || $property->getKey() == '_URI' );
+
+ foreach ( $dataItems as $dataItem ) {
+ // Basic namespace filtering to ensure that types match for redirects etc.
+ /// TODO: currently no full check for avoiding OWL DL illegal redirects is done (OWL property type ignored)
+ if ( $filterNamespace && !( $dataItem instanceof SMWDIUri ) &&
+ ( !( $dataItem instanceof SMWDIWikiPage ) ) ) {
+ continue;
+ }
+
+ $resourceBuilder->addResourceValue( $expData, $property, $dataItem );
+ }
+ }
+ }
+
+ /**
+ * @see ExpResourceMapper::mapPropertyToResourceElement
+ */
+ static public function getResourceElementForProperty( SMWDIProperty $diProperty, $helperProperty = false, $seekImportVocabulary = true ) {
+ return self::$expResourceMapper->mapPropertyToResourceElement( $diProperty, $helperProperty, $seekImportVocabulary );
+ }
+
+ /**
+ * @see ExpResourceMapper::mapWikiPageToResourceElement
+ */
+ static public function getResourceElementForWikiPage( SMWDIWikiPage $diWikiPage, $markForAuxiliaryUsage = false ) {
+ return self::$expResourceMapper->mapWikiPageToResourceElement( $diWikiPage, $markForAuxiliaryUsage );
+ }
+
+ /**
+ * Try to find an SMWDataItem that the given ExpElement might
+ * represent. Returns null if this attempt failed.
+ *
+ * @param ExpElement $expElement
+ * @return SMWDataItem or null
+ */
+ public function findDataItemForExpElement( ExpElement $expElement ) {
+ return self::$dataItemMatchFinder->matchExpElement( $expElement );
+ }
+
+ /**
+ * Determine what kind of OWL property some SMW property should be exported as.
+ * The input is an SMWTypesValue object, a typeid string, or empty (use default)
+ * @todo An improved mechanism for selecting property types here is needed.
+ */
+ static public function getOWLPropertyType( $type = '' ) {
+ if ( $type instanceof SMWDIWikiPage ) {
+ $type = DataTypeRegistry::getInstance()->findTypeByLabel( str_replace( '_', ' ', $type->getDBkey() ) );
+ } elseif ( $type == false ) {
+ $type = '';
+ }
+
+ switch ( $type ) {
+ case '_anu':
+ return 'AnnotationProperty';
+ case '': case '_wpg': case '_wpp': case '_wpc': case '_wpf':
+ case '_uri': case '_ema': case '_tel': case '_rec': case '__typ':
+ case '__red': case '__spf': case '__spu':
+ return 'ObjectProperty';
+ default:
+ return 'DatatypeProperty';
+ }
+ }
+
+ /**
+ * Get an ExpNsResource for a special property of SMW, or null if
+ * no resource is assigned to the given property key. The optional
+ * namespace is used to select the proper resource for properties that
+ * must take the type of the annotated object into account for some
+ * reason.
+ *
+ * @param $propertyKey string the Id of the special property
+ * @param $forNamespace integer the namespace of the page which has a value for this property
+ * @return ExpNsResource|null
+ */
+ static public function getSpecialPropertyResource( $propertyKey, $forNamespace = NS_MAIN ) {
+ switch ( $propertyKey ) {
+ case '_INST':
+ return self::getSpecialNsResource( 'rdf', 'type' );
+ case '_SUBC':
+ return self::getSpecialNsResource( 'rdfs', 'subClassOf' );
+ case '_CONC':
+ return self::getSpecialNsResource( 'owl', 'equivalentClass' );
+ case '_URI':
+ if ( $forNamespace == NS_CATEGORY || $forNamespace == SMW_NS_CONCEPT ) {
+ return self::getSpecialNsResource( 'owl', 'equivalentClass' );
+ } elseif ( $forNamespace == SMW_NS_PROPERTY ) {
+ return self::getSpecialNsResource( 'owl', 'equivalentProperty' );
+ } else {
+ return self::getSpecialNsResource( 'owl', 'sameAs' );
+ }
+ case '_REDI':
+ return self::getSpecialNsResource( 'swivt', 'redirectsTo' );
+ case '_SUBP':
+ if ( $forNamespace == SMW_NS_PROPERTY ) {
+ return self::getSpecialNsResource( 'rdfs', 'subPropertyOf' );
+ } else {
+ return null;
+ }
+ case '_MDAT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageModificationDate' );
+ case '_CDAT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageCreationDate' );
+ case '_LEDT':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageLastEditor' );
+ case '_NEWP':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageIsNew' );
+ case '_SKEY':
+ return self::getSpecialNsResource( 'swivt', 'wikiPageSortKey' );
+ case '_TYPE':
+ return self::getSpecialNsResource( 'swivt', 'type' );
+ case '_IMPO':
+ return self::getSpecialNsResource( 'swivt', 'specialImportedFrom' );
+ default:
+ return self::getSpecialNsResource( 'swivt', 'specialProperty' . $propertyKey );
+ }
+ }
+
+
+ /**
+ * Create an ExpNsResource for some special element that belongs to
+ * a known vocabulary. An exception is generated when given parameters
+ * that do not fit any known vocabulary.
+ *
+ * @param $namespaceId string (e.g. "rdf")
+ * @param $localName string (e.g. "type")
+ * @return ExpNsResource
+ */
+ static public function getSpecialNsResource( $namespaceId, $localName ) {
+ $namespace = self::getNamespaceUri( $namespaceId );
+ if ( $namespace !== '' ) {
+ return new ExpNsResource( $localName, $namespace, $namespaceId );
+ } else {
+ throw new InvalidArgumentException( "The vocabulary '$namespaceId' is not a known special vocabulary." );
+ }
+ }
+
+ /**
+ * This function expands standard XML entities used in some generated
+ * URIs. Given a string with such entities, it returns a string with
+ * all entities properly replaced.
+ *
+ * @note The function SMWExporter::getInstance()->getNamespaceUri() is often more
+ * suitable. This XML-specific method might become obsolete.
+ *
+ * @param $uri string of the URI to be expanded
+ * @return string of the expanded URI
+ */
+ static public function expandURI( $uri ) {
+ self::initBaseURIs();
+ $uri = str_replace( [ '&wiki;', '&wikiurl;', '&property;', '&category;', '&owl;', '&rdf;', '&rdfs;', '&swivt;', '&export;' ],
+ [ self::$m_ent_wiki, self::$m_ent_wikiurl, self::$m_ent_property, self::$m_ent_category, 'http://www.w3.org/2002/07/owl#', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'http://www.w3.org/2000/01/rdf-schema#', 'http://semantic-mediawiki.org/swivt/1.0#',
+ self::$m_exporturl ],
+ $uri );
+ return $uri;
+ }
+
+ /**
+ * @return string
+ */
+ public function decodeURI( $uri ) {
+ return Escaper::decodeUri( $uri );
+ }
+
+ /**
+ * Get the URI of a standard namespace prefix used in SMW, or the empty
+ * string if the prefix is not known.
+ *
+ * @param $shortName string id (prefix) of the namespace
+ * @return string of the expanded URI
+ */
+ static public function getNamespaceUri( $shortName ) {
+ self::initBaseURIs();
+
+ if ( ( $uri = NamespaceUriFinder::getUri( $shortName ) ) !== false ) {
+ return $uri;
+ }
+
+ switch ( $shortName ) {
+ case 'wiki':
+ return self::$m_ent_wiki;
+ case 'wikiurl':
+ return self::$m_ent_wikiurl;
+ case 'property':
+ return self::$m_ent_property;
+ case 'category':
+ return self::$m_ent_category;
+ case 'export':
+ return self::$m_exporturl;
+ case 'owl':
+ return 'http://www.w3.org/2002/07/owl#';
+ case 'rdf':
+ return 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+ case 'rdfs':
+ return 'http://www.w3.org/2000/01/rdf-schema#';
+ case 'swivt':
+ return 'http://semantic-mediawiki.org/swivt/1.0#';
+ case 'xsd':
+ return 'http://www.w3.org/2001/XMLSchema#';
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Create an SMWExpData container that encodes the ontology header for an
+ * SMW exported OWL file.
+ *
+ * @param string $ontologyuri specifying the URI of the ontology, possibly
+ * empty
+ *
+ * @return SMWExpData
+ */
+ static public function getOntologyExpData( $ontologyuri ) {
+ $data = new SMWExpData( new SMWExpResource( $ontologyuri ) );
+ $ed = self::getSpecialNsResource( 'owl', 'Ontology' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'rdf', 'type' ), $ed );
+ $ed = new ExpLiteral( date( DATE_W3C ), 'http://www.w3.org/2001/XMLSchema#dateTime' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'swivt', 'creationDate' ), $ed );
+ $ed = new SMWExpResource( 'http://semantic-mediawiki.org/swivt/1.0' );
+ $data->addPropertyObjectValue( self::getSpecialNsResource( 'owl', 'imports' ), $ed );
+ return $data;
+ }
+
+ /**
+ * @see ElementFactory::mapDataItemToElement
+ */
+ static public function getDataItemExpElement( SMWDataItem $dataItem ) {
+ return self::$elementFactory->newFromDataItem( $dataItem );
+ }
+
+ /**
+ * Create an SWMExpElement that encodes auxiliary data for representing
+ * values of the specified dataitem object in a simplified fashion.
+ * This is done for types of dataitems that are not supported very well
+ * in current systems, or that do not match a standard datatype in RDF.
+ * For example, time points (DITime) are encoded as numbers. The number
+ * can replace the actual time for all query and ordering purposes (the
+ * order in either case is linear and maps to the real number line).
+ * Only data retrieval should better use the real values to avoid that
+ * rounding errors lead to unfaithful recovery of data. Note that the
+ * helper values do not maintain any association with their original
+ * values -- they are a fully redundant alternative representation, not
+ * an additional piece of information for the main values. Even if
+ * decoding is difficult, they must be in one-to-one correspondence to
+ * the original value.
+ *
+ * For dataitems that do not have such a simplification, the method
+ * returns null.
+ *
+ * @note If a helper element is used, then it must be the same as
+ * getDataItemHelperExpElement( $dataItem->getSortKeyDataItem() ).
+ * Query conditions like ">" use sortkeys for values, and helper
+ * elements are always preferred in query answering.
+ *
+ * @param $dataItem SMWDataItem
+ * @return ExpElement|null
+ */
+ static public function getDataItemHelperExpElement( SMWDataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_TIME ) {
+ return new ExpLiteral( (string)$dataItem->getSortKey(), 'http://www.w3.org/2001/XMLSchema#double', '', $dataItem );
+ }
+
+ if ( $dataItem->getDIType() == SMWDataItem::TYPE_GEO ) {
+ return new ExpLiteral( (string)$dataItem->getSortKey(), 'http://www.w3.org/2001/XMLSchema#string', '', $dataItem );
+ }
+
+ return null;
+ }
+
+ /**
+ * Check whether the values of a given type of dataitem have helper
+ * values in the sense of SMWExporter::getInstance()->getDataItemHelperExpElement().
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ static public function hasHelperExpElement( DIProperty $property ) {
+ return ( $property->findPropertyTypeID() === '_dat' || $property->findPropertyTypeID() === '_geo' ) || ( !$property->isUserDefined() && !self::hasSpecialPropertyResource( $property ) );
+ }
+
+ static protected function hasSpecialPropertyResource( DIProperty $property ) {
+ return $property->getKey() === '_SKEY' ||
+ $property->getKey() === '_INST' ||
+ $property->getKey() === '_MDAT' ||
+ $property->getKey() === '_SUBC' ||
+ $property->getKey() === '_SUBP' ||
+ $property->getKey() === '_TYPE' ||
+ $property->getKey() === '_IMPO' ||
+ $property->getKey() === '_URI';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php
new file mode 100644
index 00000000..775d1b42
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer.php
@@ -0,0 +1,333 @@
+<?php
+
+/**
+ * File holding the SMWSerializer class that provides basic functions for
+ * serialising data in OWL and RDF syntaxes.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+define( 'SMW_SERIALIZER_DECL_CLASS', 1 );
+define( 'SMW_SERIALIZER_DECL_OPROP', 2 );
+define( 'SMW_SERIALIZER_DECL_APROP', 4 );
+
+/**
+ * Abstract class for serializing exported data (encoded as SMWExpData object)
+ * in a concrete syntactic format such as Turtle or RDF/XML. The serializer
+ * adds object serialisations to an internal string that can be retrieved for
+ * pushing it to an output. This abstract class does not define this string as
+ * implementations may want to use their own scheme (e.g. using two buffers as
+ * in the case of SMWRDFXMLSerializer). The function flushContent() returns the
+ * string serialized so far so as to enable incremental serialization.
+ *
+ * RDF and OWL have two types of dependencies that are managed by this class:
+ * namespaces (and similar abbreviation schemes) and element declarations.
+ * The former need to be defined before being used, while the latter can occur
+ * at some later point in the serialization. Declarations are relevant to the
+ * OWL data model, being one of Class, DatatypeProperty, and ObjectProperty
+ * (only the latter two are mutually exclusive). This class determines the
+ * required declaration from the context in which an element is used.
+ *
+ * @ingroup SMW
+ */
+abstract class SMWSerializer {
+ /**
+ * The current working string is obtained by concatenating the strings
+ * $pre_ns_buffer and $post_ns_buffer. The split between the two is such
+ * that one can append additional namespace declarations to $pre_ns_buffer
+ * so that they affect all current elements. The buffers are flushed during
+ * output in order to achieve "streaming" RDF export for larger files.
+ * @var string
+ */
+ protected $pre_ns_buffer;
+ /**
+ * See documentation for $pre_ns_buffer.
+ * @var string
+ */
+ protected $post_ns_buffer;
+ /**
+ * Array for recording required declarations; format:
+ * resourcename => decl-flag, where decl-flag is a sum of flags
+ * SMW_SERIALIZER_DECL_CLASS, SMW_SERIALIZER_DECL_OPROP,
+ * SMW_SERIALIZER_DECL_APROP.
+ * @var array of integer
+ */
+ protected $decl_todo;
+ /**
+ * Array for recording previous declarations; format:
+ * resourcename => decl-flag, where decl-flag is a sum of flags
+ * SMW_SERIALIZER_DECL_CLASS, SMW_SERIALIZER_DECL_OPROP,
+ * SMW_SERIALIZER_DECL_APROP.
+ * @var array of integer
+ */
+ protected $decl_done;
+ /**
+ * Array of additional namespaces (abbreviation => URI), flushed on
+ * closing the current namespace tag. Since we export in a streamed
+ * way, it is not always possible to embed additional namespaces into
+ * a syntactic block (e.g. an RDF/XML tag) which might have been sent to
+ * the client already. But we wait with printing the current block so that
+ * extra namespaces from this array can still be printed (note that one
+ * never know which extra namespaces you encounter during export).
+ * @var array of string
+ */
+ protected $extra_namespaces;
+ /**
+ * Array of namespaces that have been declared globally already. Contains
+ * entries of format 'namespace abbreviation' => true, assuming that the
+ * same abbreviation always refers to the same URI.
+ * @var array of string
+ */
+ protected $global_namespaces;
+
+ /**
+ * Constructor.
+ */
+ public function __construct() {
+ $this->clear();
+ }
+
+ /**
+ * Clear internal states to start a new serialization.
+ */
+ public function clear() {
+ $this->pre_ns_buffer = '';
+ $this->post_ns_buffer = '';
+ $this->decl_todo = [];
+ $this->decl_done = [];
+ $this->global_namespaces = [];
+ $this->extra_namespaces = [];
+ }
+
+ /**
+ * Start a new serialization, resetting all internal data and serializing
+ * necessary header elements.
+ */
+ public function startSerialization() {
+ $this->clear();
+ $this->serializeHeader();
+ }
+
+ /**
+ * Complete the serialization so that calling flushContent() will return
+ * the final part of the output, leading to a complete serialization with
+ * all necessary declarations. No further serialization functions must be
+ * called after this.
+ */
+ public function finishSerialization() {
+ $this->serializeDeclarations();
+ $this->serializeFooter();
+ }
+
+ /**
+ * Serialize the header (i.e. write it to the internal buffer). May
+ * include standard syntax to start output but also declare some common
+ * namespaces globally.
+ */
+ abstract protected function serializeHeader();
+
+ /**
+ * Serialise the footer (i.e. write it to the internal buffer).
+ */
+ abstract protected function serializeFooter();
+
+ /**
+ * Serialize any declarations that have been found to be missing while
+ * serializing other elements.
+ */
+ public function serializeDeclarations() {
+ foreach ( $this->decl_todo as $name => $flag ) {
+ $types = [];
+ if ( $flag & SMW_SERIALIZER_DECL_CLASS ) {
+ $types[] = 'owl:Class';
+ }
+ if ( $flag & SMW_SERIALIZER_DECL_OPROP ) {
+ $types[] = 'owl:ObjectProperty';
+ }
+ if ( $flag & SMW_SERIALIZER_DECL_APROP ) {
+ $types[] = 'owl:DatatypeProperty';
+ }
+ foreach ( $types as $typename ) {
+ $this->serializeDeclaration( $name, $typename );
+ }
+ $curdone = array_key_exists( $name, $this->decl_done ) ? $this->decl_done[$name] : 0;
+ $this->decl_done[$name] = $curdone | $flag;
+ }
+ $this->decl_todo = []; // reset all
+ }
+
+ /**
+ * Serialize a single declaration for the given $uri (expanded) and type
+ * (given as a QName).
+ * @param $uri string URI of the thing to declare
+ * @param $typename string one of owl:Class, owl:ObjectProperty, and
+ * owl:datatypeProperty
+ */
+ abstract public function serializeDeclaration( $uri, $typename );
+
+ /**
+ * Serialise the given SMWExpData object. The method must not assume that
+ * the exported data refers to wiki pages or other SMW data, and it must
+ * ensure that all required auxiliary declarations for obtaining proper OWL
+ * are included in any case (this can be done using requireDeclaration()).
+ *
+ * @param $data SMWExpData containing the data to be serialised.
+ */
+ abstract public function serializeExpData( SMWExpData $data );
+
+ /**
+ * Get the string that has been serialized so far. This function also
+ * resets the internal buffers for serilized strings and namespaces
+ * (what is flushed is gone).
+ */
+ public function flushContent() {
+ if ( ( $this->pre_ns_buffer === '' ) && ( $this->post_ns_buffer === '' ) ) {
+ return '';
+ }
+ $this->serializeNamespaces();
+ $result = $this->pre_ns_buffer . $this->post_ns_buffer;
+ $this->pre_ns_buffer = '';
+ $this->post_ns_buffer = '';
+ return $result;
+ }
+
+ /**
+ * Include collected namespace information into the serialization.
+ */
+ protected function serializeNamespaces() {
+ foreach ( $this->extra_namespaces as $nsshort => $nsuri ) {
+ $this->serializeNamespace( $nsshort, $nsuri );
+ }
+ $this->extra_namespaces = [];
+ }
+
+ /**
+ * Serialize a single namespace.
+ * Namespaces that were serialized in such a way that they remain
+ * available for all following output should be added to
+ * $global_namespaces.
+ * @param $shortname string abbreviation/prefix to declare
+ * @param $uri string URI prefix that the namespace encodes
+ */
+ abstract protected function serializeNamespace( $shortname, $uri );
+
+ /**
+ * Require an additional namespace to be declared in the serialization.
+ * The function checks whether the required namespace is available globally
+ * and add it to the list of required namespaces otherwise.
+ */
+ protected function requireNamespace( $nsshort, $nsuri ) {
+ if ( !array_key_exists( $nsshort, $this->global_namespaces ) ) {
+ $this->extra_namespaces[$nsshort] = $nsuri;
+ }
+ }
+
+ /**
+ * State that a certain declaration is needed. The method checks if the
+ * declaration is already available, and records a todo otherwise.
+ */
+ protected function requireDeclaration( SMWExpResource $resource, $decltype ) {
+ // Do not declare predefined OWL language constructs:
+ if ( $resource instanceof SMWExpNsResource ) {
+ $nsId = $resource->getNamespaceId();
+ if ( ( $nsId == 'owl' ) || ( $nsId == 'rdf' ) || ( $nsId == 'rdfs' ) ) {
+ return;
+ }
+ }
+ // Do not declare blank nodes:
+ if ( $resource->isBlankNode() ) {
+ return;
+ }
+
+ $name = $resource->getUri();
+ if ( array_key_exists( $name, $this->decl_done ) && ( $this->decl_done[$name] & $decltype ) ) {
+ return;
+ }
+ if ( !array_key_exists( $name, $this->decl_todo ) ) {
+ $this->decl_todo[$name] = $decltype;
+ } else {
+ $this->decl_todo[$name] = $this->decl_todo[$name] | $decltype;
+ }
+ }
+
+ /**
+ * Update the declaration "todo" and "done" lists for the case that the
+ * given data has been serialized with the type information it provides.
+ *
+ * @param $expData specifying the type data upon which declarations are based
+ */
+ protected function recordDeclarationTypes( SMWExpData $expData ) {
+ foreach ( $expData->getSpecialValues( 'rdf', 'type') as $typeresource ) {
+ if ( $typeresource instanceof SMWExpNsResource ) {
+ switch ( $typeresource->getQName() ) {
+ case 'owl:Class': $typeflag = SMW_SERIALIZER_DECL_CLASS;
+ break;
+ case 'owl:ObjectProperty': $typeflag = SMW_SERIALIZER_DECL_OPROP;
+ break;
+ case 'owl:DatatypeProperty': $typeflag = SMW_SERIALIZER_DECL_APROP;
+ break;
+ default: $typeflag = 0;
+ }
+ if ( $typeflag != 0 ) {
+ $this->declarationDone( $expData->getSubject(), $typeflag );
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the declaration "todo" and "done" lists to reflect the fact that
+ * the given element has been declared to has the given type.
+ *
+ * @param $element SMWExpResource specifying the element to update
+ * @param $typeflag integer specifying the type (e.g. SMW_SERIALIZER_DECL_CLASS)
+ */
+ protected function declarationDone( SMWExpResource $element, $typeflag ) {
+ $name = $element->getUri();
+ $curdone = array_key_exists( $name, $this->decl_done ) ? $this->decl_done[$name] : 0;
+ $this->decl_done[$name] = $curdone | $typeflag;
+ if ( array_key_exists( $name, $this->decl_todo ) ) {
+ $this->decl_todo[$name] = $this->decl_todo[$name] & ( ~$typeflag );
+ if ( $this->decl_todo[$name] == 0 ) {
+ unset( $this->decl_todo[$name] );
+ }
+ }
+ }
+
+ /**
+ * Check if the given property is one of the special properties of the OWL
+ * language that require their values to be classes or RDF lists of
+ * classes. In these cases, it is necessary to declare this in the exported
+ * data.
+ *
+ * @note The list of properties checked here is not complete for the OWL
+ * language but covers what is used in SMW.
+ * @note OWL 2 allows URIs to refer to both classes and individual elements
+ * in different contexts. We only need declarations for classes that are
+ * used as such, hence it is enough to check the property. Moreover, we do
+ * not use OWL Datatypes in SMW, so rdf:type, rdfs:domain, etc. always
+ * refer to classes.
+ *
+ * @param SMWExpNsResource $property
+ *
+ * @return boolean
+ */
+ protected function isOWLClassTypeProperty( SMWExpNsResource $property ) {
+ $locname = $property->getLocalName();
+ if ( $property->getNamespaceID() == 'rdf' ) {
+ return ( $locname == 'type' );
+ } elseif ( $property->getNamespaceID() == 'owl' ) {
+ return ( $locname == 'intersectionOf' ) || ( $locname == 'unionOf' ) ||
+ ( $locname == 'equivalentClass' ) ||
+ ( $locname == 'complementOf' ) || ( $locname == 'someValuesFrom' ) ||
+ ( $locname == 'allValuesFrom' ) || ( $locname == 'onClass' );
+ } elseif ( $property->getNamespaceID() == 'rdfs' ) {
+ return ( $locname == 'subClassOf' ) || ( $locname == 'range' ) || ( $locname == 'domain' );
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php
new file mode 100644
index 00000000..4848dcf0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_RDFXML.php
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * File holding the SMWRDFXMLSerializer class that provides basic functions for
+ * serialising OWL data in RDF/XML syntax.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for serializing exported data (encoded as SMWExpData object) in
+ * RDF/XML.
+ *
+ * @ingroup SMW
+ */
+class SMWRDFXMLSerializer extends SMWSerializer {
+ /**
+ * True if the $pre_ns_buffer contains the beginning of a namespace
+ * declaration block to which further declarations for the current
+ * context can be appended.
+ */
+ protected $namespace_block_started;
+ /**
+ * True if the namespaces that are added at the current serialization stage
+ * become global, i.e. remain available for all later contexts. This is the
+ * case in RDF/XML only as long as the header has not been streamed to the
+ * client (reflected herein by calling flushContent()). Later, namespaces
+ * can only be added locally to individual elements, thus requiring them to
+ * be re-added multiple times if used in many elements.
+ */
+ protected $namespaces_are_global;
+
+ public function clear() {
+ parent::clear();
+ $this->namespaces_are_global = false;
+ $this->namespace_block_started = false;
+ }
+
+ protected function serializeHeader() {
+ $this->namespaces_are_global = true;
+ $this->namespace_block_started = true;
+ $this->pre_ns_buffer =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" .
+ "<!DOCTYPE rdf:RDF[\n" .
+ "\t<!ENTITY rdf " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdf;' ) ) . ">\n" .
+ "\t<!ENTITY rdfs " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdfs;' ) ) . ">\n" .
+ "\t<!ENTITY owl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&owl;' ) ) . ">\n" .
+ "\t<!ENTITY swivt " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&swivt;' ) ) . ">\n" .
+ // A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
+ // In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
+ "\t<!ENTITY wiki " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wiki;' ) ) . ">\n" .
+ "\t<!ENTITY category " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&category;' ) ) . ">\n" .
+ "\t<!ENTITY property " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&property;' ) ) . ">\n" .
+ "\t<!ENTITY wikiurl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wikiurl;' ) ) . ">\n" .
+ "]>\n\n" .
+ "<rdf:RDF\n" .
+ "\txmlns:rdf=\"&rdf;\"\n" .
+ "\txmlns:rdfs=\"&rdfs;\"\n" .
+ "\txmlns:owl =\"&owl;\"\n" .
+ "\txmlns:swivt=\"&swivt;\"\n" .
+ "\txmlns:wiki=\"&wiki;\"\n" .
+ "\txmlns:category=\"&category;\"\n" .
+ "\txmlns:property=\"&property;\"";
+ $this->global_namespaces = [ 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true ];
+ $this->post_ns_buffer .= ">\n\n";
+ }
+
+ protected function serializeFooter() {
+ $this->post_ns_buffer .= "\t<!-- Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/ -->\n";
+ $this->post_ns_buffer .= '</rdf:RDF>';
+ }
+
+ public function serializeDeclaration( $uri, $typename ) {
+ $this->post_ns_buffer .= "\t<$typename rdf:about=\"$uri\" />\n";
+ }
+
+ public function serializeExpData( SMWExpData $expData ) {
+ $this->serializeNestedExpData( $expData, '' );
+ $this->serializeNamespaces();
+ if ( !$this->namespaces_are_global ) {
+ $this->pre_ns_buffer .= $this->post_ns_buffer;
+ $this->post_ns_buffer = '';
+ $this->namespace_block_started = false;
+ }
+ }
+
+ public function flushContent() {
+ $result = parent::flushContent();
+ $this->namespaces_are_global = false; // must not be done before calling the parent method (which may declare namespaces)
+ $this->namespace_block_started = false;
+ return $result;
+ }
+
+ protected function serializeNamespace( $shortname, $uri ) {
+ if ( $this->namespaces_are_global ) {
+ $this->global_namespaces[$shortname] = true;
+ $this->pre_ns_buffer .= "\n\t";
+ } else {
+ $this->pre_ns_buffer .= ' ';
+ }
+ $this->pre_ns_buffer .= "xmlns:$shortname=\"$uri\"";
+ }
+
+ /**
+ * Serialize the given SMWExpData object, possibly recursively with
+ * increased indentation.
+ *
+ * @param $expData SMWExpData containing the data to be serialised.
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeNestedExpData( SMWExpData $expData, $indent ) {
+ $this->recordDeclarationTypes( $expData );
+
+ $type = $expData->extractMainType()->getQName();
+ if ( !$this->namespace_block_started ) { // start new ns block
+ $this->pre_ns_buffer .= "\t$indent<$type";
+ $this->namespace_block_started = true;
+ } else { // continue running block
+ $this->post_ns_buffer .= "\t$indent<$type";
+ }
+
+ if ( ( $expData->getSubject() instanceof SMWExpResource ) &&
+ !$expData->getSubject()->isBlankNode() ) {
+ $this->post_ns_buffer .= ' rdf:about="' . $expData->getSubject()->getUri() . '"';
+ } // else: blank node, no "rdf:about"
+
+ if ( count( $expData->getProperties() ) == 0 ) { // nothing else to export
+ $this->post_ns_buffer .= " />\n";
+ } else { // process data
+ $this->post_ns_buffer .= ">\n";
+
+ foreach ( $expData->getProperties() as $property ) {
+ $prop_decl_queued = false;
+ $isClassTypeProp = $this->isOWLClassTypeProperty( $property );
+
+ foreach ( $expData->getValues( $property ) as $valueElement ) {
+ $this->requireNamespace( $property->getNamespaceID(), $property->getNamespace() );
+
+ if ( $valueElement instanceof SMWExpLiteral ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_APROP;
+ $this->serializeExpLiteral( $property, $valueElement, "\t\t$indent" );
+ } elseif ( $valueElement instanceof SMWExpResource ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $this->serializeExpResource( $property, $valueElement, "\t\t$indent", $isClassTypeProp );
+ } elseif ( $valueElement instanceof SMWExpData ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+
+ $collection = $valueElement->getCollection();
+ if ( $collection !== false ) { // RDF-style collection (list)
+ $this->serializeExpCollection( $property, $collection, "\t\t$indent", $isClassTypeProp );
+ } elseif ( count( $valueElement->getProperties() ) > 0 ) { // resource with data
+ $this->post_ns_buffer .= "\t\t$indent<" . $property->getQName() . ">\n";
+ $this->serializeNestedExpData( $valueElement, "\t\t$indent" );
+ $this->post_ns_buffer .= "\t\t$indent</" . $property->getQName() . ">\n";
+ } else { // resource without data
+ $this->serializeExpResource( $property, $valueElement->getSubject(), "\t\t$indent", $isClassTypeProp );
+ }
+ } // else: no other types of export elements
+
+ if ( !$prop_decl_queued ) {
+ $this->requireDeclaration( $property, $prop_decl_type );
+ $prop_decl_queued = true;
+ }
+ }
+ }
+ $this->post_ns_buffer .= "\t$indent</" . $type . ">\n";
+ }
+ }
+
+ /**
+ * Add to the output a serialization of a property assignment where an
+ * SMWExpLiteral is the object. It is assumed that a suitable subject
+ * block has already been openend.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expLiteral SMWExpLiteral the data value to use
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeExpLiteral( SMWExpNsResource $expResourceProperty, SMWExpLiteral $expLiteral, $indent ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
+
+ // https://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-languages
+ // "... to indicate that the included content is in the given language.
+ // Typed literals which includes XML literals are not affected by this
+ // attribute. The most specific in-scope language present (if any) is
+ // applied to property element string literal ..."
+ if ( $expLiteral->getDatatype() !== '' && $expLiteral->getLang() !== '' ) {
+ $this->post_ns_buffer .= ' xml:lang="' . $expLiteral->getLang() . '"';
+ } elseif ( $expLiteral->getDatatype() !== '' ) {
+ $this->post_ns_buffer .= ' rdf:datatype="' . $expLiteral->getDatatype() . '"';
+ }
+
+ $this->post_ns_buffer .= '>' . $this->makeAttributeValueString( $expLiteral->getLexicalForm() );
+ $this->post_ns_buffer .= '</' . $expResourceProperty->getQName() . ">\n";
+ }
+
+ /**
+ * Add to the output a serialization of a property assignment where an
+ * SMWExpResource is the object. It is assumed that a suitable subject
+ * block has already been openend.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expResource SMWExpResource the data value to use
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ * @param $isClassTypeProp boolean whether the resource must be declared as a class
+ */
+ protected function serializeExpResource( SMWExpNsResource $expResourceProperty, SMWExpResource $expResource, $indent, $isClassTypeProp ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
+ if ( !$expResource->isBlankNode() ) {
+ if ( ( $expResource instanceof SMWExpNsResource ) && ( $expResource->getNamespaceID() == 'wiki' ) ) {
+ // very common case, reduce bandwidth
+ $this->post_ns_buffer .= ' rdf:resource="&wiki;' . $expResource->getLocalName() . '"';
+ } else {
+ $uriValue = $this->makeAttributeValueString( $expResource->getUri() );
+ $this->post_ns_buffer .= ' rdf:resource="' . $uriValue . '"';
+ }
+ }
+ $this->post_ns_buffer .= "/>\n";
+ if ( $isClassTypeProp ) {
+ $this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+
+ /**
+ * Add a serialization of the given SMWExpResource to the output,
+ * assuming that an opening property tag is alerady there.
+ *
+ * @param $expResourceProperty SMWExpNsResource the property to use
+ * @param $expResource array of (SMWExpResource or SMWExpData)
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ * @param $isClassTypeProp boolean whether the resource must be declared as a class
+ *
+ * @bug The $isClassTypeProp parameter is not properly taken into account.
+ * @bug Individual resources are not serialised properly.
+ */
+ protected function serializeExpCollection( SMWExpNsResource $expResourceProperty, array $collection, $indent, $isClassTypeProp ) {
+ $this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName() . " rdf:parseType=\"Collection\">\n";
+ foreach ( $collection as $expElement ) {
+ if ( $expElement instanceof SMWExpData ) {
+ $this->serializeNestedExpData( $expElement, $indent );
+ } else {
+ // FIXME: the below is not the right thing to do here
+ //$this->serializeExpResource( $expResourceProperty, $expElement, $indent );
+ }
+ if ( $isClassTypeProp ) {
+ // FIXME: $expResource is undefined
+ //$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+ $this->post_ns_buffer .= "$indent</" . $expResourceProperty->getQName() . ">\n";
+ }
+
+ /**
+ * Escape a string in the special form that is required for values in
+ * DTD entity declarations in XML. Namely, this require the percent sign
+ * to be replaced.
+ *
+ * @param $string string to be escaped
+ * @return string
+ */
+ protected function makeValueEntityString( $string ) {
+ return "'" . str_replace( '%', '&#37;', $string ) . "'";
+ }
+
+ /**
+ * Escape a string as required for using it in XML attribute values.
+ *
+ * @param $string string to be escaped
+ * @return string
+ */
+ protected function makeAttributeValueString( $string ) {
+ return str_replace( [ '&', '>', '<' ], [ '&amp;', '&gt;', '&lt;' ], $string );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php
new file mode 100644
index 00000000..d7a6ff2a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/export/SMW_Serializer_Turtle.php
@@ -0,0 +1,294 @@
+<?php
+
+use SMW\InMemoryPoolCache;
+
+/**
+ * File holding the SMWTurtleSerializer class that provides basic functions for
+ * serialising OWL data in Turtle syntax.
+ *
+ * @ingroup SMW
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class for serializing exported data (encoded as SMWExpData object) in
+ * Turtle syntax.
+ *
+ * @ingroup SMW
+ */
+class SMWTurtleSerializer extends SMWSerializer {
+ /**
+ * Array of non-trivial sub-SMWExpData elements that cannot be nested while
+ * serializing some SMWExpData. The elements of the array are serialized
+ * later during the same serialization step (so this is not like another
+ * queue for declarations or the like; it just unfolds an SMWExpData
+ * object).
+ *
+ * @var array of SMWExpData
+ */
+ protected $subexpdata;
+
+ /**
+ * If true, do not serialize namespace declarations and record them in
+ * $sparql_namespaces instead for later retrieval.
+ * @var boolean
+ */
+ protected $sparqlmode;
+
+ /**
+ * Array of retrieved namespaces (abbreviation => URI) for later use.
+ * @var array of string
+ */
+ protected $sparql_namespaces;
+
+ public function __construct( $sparqlMode = false ) {
+ parent::__construct();
+ $this->sparqlmode = $sparqlMode;
+ }
+
+ public function clear() {
+ parent::clear();
+ $this->sparql_namespaces = [];
+ }
+
+ /**
+ * @since 2.3
+ */
+ public static function reset() {
+ InMemoryPoolCache::getInstance()->resetPoolCacheById( 'turtle.serializer' );
+ }
+
+ /**
+ * Get an array of namespace prefixes used in SPARQL mode.
+ * Namespaces are not serialized among triples in SPARQL mode but are
+ * collected separately. This method returns the prefixes and empties
+ * the collected list afterwards.
+ *
+ * @return array shortName => namespace URI
+ */
+ public function flushSparqlPrefixes() {
+ $result = $this->sparql_namespaces;
+ $this->sparql_namespaces = [];
+ return $result;
+ }
+
+ protected function serializeHeader() {
+ if ( $this->sparqlmode ) {
+ $this->pre_ns_buffer = '';
+ $this->sparql_namespaces = [
+ "rdf" => SMWExporter::getInstance()->expandURI( '&rdf;' ),
+ "rdfs" => SMWExporter::getInstance()->expandURI( '&rdfs;' ),
+ "owl" => SMWExporter::getInstance()->expandURI( '&owl;' ),
+ "swivt" => SMWExporter::getInstance()->expandURI( '&swivt;' ),
+ "wiki" => SMWExporter::getInstance()->expandURI( '&wiki;' ),
+ "category" => SMWExporter::getInstance()->expandURI( '&category;' ),
+ "property" => SMWExporter::getInstance()->expandURI( '&property;' ),
+ "xsd" => "http://www.w3.org/2001/XMLSchema#" ,
+ "wikiurl" => SMWExporter::getInstance()->expandURI( '&wikiurl;' )
+ ];
+ } else {
+ $this->pre_ns_buffer =
+ "@prefix rdf: <" . SMWExporter::getInstance()->expandURI( '&rdf;' ) . "> .\n" .
+ "@prefix rdfs: <" . SMWExporter::getInstance()->expandURI( '&rdfs;' ) . "> .\n" .
+ "@prefix owl: <" . SMWExporter::getInstance()->expandURI( '&owl;' ) . "> .\n" .
+ "@prefix swivt: <" . SMWExporter::getInstance()->expandURI( '&swivt;' ) . "> .\n" .
+ // A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
+ // In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
+ "@prefix wiki: <" . SMWExporter::getInstance()->expandURI( '&wiki;' ) . "> .\n" .
+ "@prefix category: <" . SMWExporter::getInstance()->expandURI( '&category;' ) . "> .\n" .
+ "@prefix property: <" . SMWExporter::getInstance()->expandURI( '&property;' ) . "> .\n" .
+ "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n" . // note that this XSD URI is hardcoded below (its unlikely to change, of course)
+ "@prefix wikiurl: <" . SMWExporter::getInstance()->expandURI( '&wikiurl;' ) . "> .\n";
+ }
+ $this->global_namespaces = [ 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true ];
+ $this->post_ns_buffer = "\n";
+ }
+
+ protected function serializeFooter() {
+ if ( !$this->sparqlmode ) {
+ $this->post_ns_buffer .= "\n# Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/\n";
+ }
+ }
+
+ public function serializeDeclaration( $uri, $typename ) {
+ $this->post_ns_buffer .= "<" . SMWExporter::getInstance()->expandURI( $uri ) . "> rdf:type $typename .\n";
+ }
+
+ public function serializeExpData( SMWExpData $expData ) {
+
+ $this->subExpData = [ $expData ];
+
+ while ( count( $this->subExpData ) > 0 ) {
+ $this->serializeNestedExpData( array_pop( $this->subExpData ), '' );
+ }
+
+ $this->serializeNamespaces();
+ }
+
+ protected function serializeNamespace( $shortname, $uri ) {
+ $this->global_namespaces[$shortname] = true;
+ if ( $this->sparqlmode ) {
+ $this->sparql_namespaces[$shortname] = $uri;
+ } else {
+ $this->pre_ns_buffer .= "@prefix $shortname: <$uri> .\n";
+ }
+ }
+
+ /**
+ * Serialize the given SMWExpData object, possibly recursively with
+ * increased indentation.
+ *
+ * @param $data SMWExpData containing the data to be serialised.
+ * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
+ */
+ protected function serializeNestedExpData( SMWExpData $data, $indent ) {
+ if ( count( $data->getProperties() ) == 0 ) {
+ return; // nothing to export
+ }
+
+ // Avoid posting turtle property declarations already known for the
+ // subject more than once
+ if ( $data->getSubject()->getDataItem() !== null && $data->getSubject()->getDataItem()->getNamespace() === SMW_NS_PROPERTY ) {
+
+ $hash = $data->getHash();
+ $poolCache = InMemoryPoolCache::getInstance()->getPoolCacheById( 'turtle.serializer' );
+
+ if ( $poolCache->contains( $hash ) && $poolCache->fetch( $hash ) ) {
+ return;
+ }
+
+ $poolCache->save( $hash, true );
+ }
+
+ $this->recordDeclarationTypes( $data );
+
+ $bnode = false;
+ $this->post_ns_buffer .= $indent;
+ if ( !$data->getSubject()->isBlankNode() ) {
+ $this->serializeExpResource( $data->getSubject() );
+ } else { // blank node
+ $bnode = true;
+ $this->post_ns_buffer .= "[";
+ }
+
+ if ( ( $indent !== '' ) && ( !$bnode ) ) { // called to generate a nested descripion; but Turtle cannot nest non-bnode descriptions, do this later
+ $this->subexpdata[] = $data;
+ return;
+ } elseif ( !$bnode ) {
+ $this->post_ns_buffer .= "\n ";
+ }
+
+ $firstproperty = true;
+ foreach ( $data->getProperties() as $property ) {
+ $this->post_ns_buffer .= $firstproperty ? "\t" : " ;\n $indent\t";
+ $firstproperty = false;
+ $prop_decl_queued = false;
+ $class_type_prop = $this->isOWLClassTypeProperty( $property );
+ $this->serializeExpResource( $property );
+ $firstvalue = true;
+
+ foreach ( $data->getValues( $property ) as $value ) {
+ $this->post_ns_buffer .= $firstvalue ? ' ' : ' , ';
+ $firstvalue = false;
+
+ if ( $value instanceof SMWExpLiteral ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_APROP;
+ $this->serializeExpLiteral( $value );
+ } elseif ( $value instanceof SMWExpResource ) {
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $this->serializeExpResource( $value );
+ } elseif ( $value instanceof SMWExpData ) { // resource (maybe blank node), could have subdescriptions
+ $prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
+ $collection = $value->getCollection();
+ if ( $collection !== false ) { // RDF-style collection (list)
+ $this->post_ns_buffer .= "( ";
+ foreach ( $collection as $subvalue ) {
+ $this->serializeNestedExpData( $subvalue, $indent . "\t\t" );
+ if ( $class_type_prop ) {
+ $this->requireDeclaration( $subvalue->getSubject(), SMW_SERIALIZER_DECL_CLASS );
+ }
+ }
+ $this->post_ns_buffer .= " )";
+ } else {
+ if ( $class_type_prop ) {
+ $this->requireDeclaration( $value->getSubject(), SMW_SERIALIZER_DECL_CLASS );
+ }
+ if ( count( $value->getProperties() ) > 0 ) { // resource with data: serialise
+ $this->post_ns_buffer .= "\n";
+ $this->serializeNestedExpData( $value, $indent . "\t\t" );
+ } else { // resource without data: may need to be queued
+ $this->serializeExpResource( $value->getSubject() );
+ }
+ }
+ }
+
+ if ( !$prop_decl_queued ) {
+ $this->requireDeclaration( $property, $prop_decl_type );
+ $prop_decl_queued = true;
+ }
+ }
+ }
+ $this->post_ns_buffer .= ( $bnode ? " ]" : " ." ) . ( $indent === '' ? "\n\n" : '' );
+ }
+
+ protected function serializeExpLiteral( SMWExpLiteral $element ) {
+ $this->post_ns_buffer .= self::getTurtleNameForExpElement( $element );
+ }
+
+ protected function serializeExpResource( SMWExpResource $element ) {
+ if ( $element instanceof SMWExpNsResource ) {
+ $this->requireNamespace( $element->getNamespaceID(), $element->getNamespace() );
+ }
+ $this->post_ns_buffer .= self::getTurtleNameForExpElement( $element );
+ }
+
+ /**
+ * Get the Turtle serialization string for the given SMWExpElement. The
+ * method just computes a name, and does not serialize triples, so the
+ * parameter must be an SMWExpResource or SMWExpLiteral, no SMWExpData.
+ *
+ * @param $expElement SMWExpElement being SMWExpLiteral or SMWExpResource
+ * @return string
+ */
+ public static function getTurtleNameForExpElement( SMWExpElement $expElement ) {
+ if ( $expElement instanceof SMWExpResource ) {
+ if ( $expElement->isBlankNode() ) {
+ return '[]';
+ } elseif ( ( $expElement instanceof SMWExpNsResource ) && ( $expElement->hasAllowedLocalName() ) ) {
+ return $expElement->getQName();
+ } else {
+ return '<' . str_replace( '>', '\>', SMWExporter::getInstance()->expandURI( $expElement->getUri() ) ) . '>';
+ }
+ } elseif ( $expElement instanceof SMWExpLiteral ) {
+ $dataType = $expElement->getDatatype();
+ $lexicalForm = self::getCorrectLexicalForm( $expElement );
+
+ if ( ( $dataType !== '' ) && ( $dataType != 'http://www.w3.org/2001/XMLSchema#string' ) ) {
+ $count = 0;
+ $newdt = str_replace( 'http://www.w3.org/2001/XMLSchema#', 'xsd:', $dataType, $count );
+ return ( $count == 1 ) ? "$lexicalForm^^$newdt" : "$lexicalForm^^<$dataType>";
+ } else {
+ return $lexicalForm;
+ }
+ } else {
+ throw new InvalidArgumentException( 'The method can only serialize atomic elements of type SMWExpResource or SMWExpLiteral.' );
+ }
+ }
+
+ private static function getCorrectLexicalForm( $expElement ) {
+
+ $lexicalForm = str_replace( [ '\\', "\n", '"' ], [ '\\\\', "\\n", '\"' ], $expElement->getLexicalForm() );
+
+ if ( $expElement->getLang() !== '' && ( $expElement->getDatatype() === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString' ) ) {
+ $lexicalForm = '"' . $lexicalForm . '@' . $expElement->getLang() . '"';
+ } elseif ( $expElement->getLang() !== '' ) {
+ $lexicalForm = '"' . $lexicalForm . '"'. '@' . $expElement->getLang();
+ } else {
+ $lexicalForm = '"' . $lexicalForm . '"';
+ }
+
+ return $lexicalForm;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php b/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php
new file mode 100644
index 00000000..642ee9da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/formatters/MessageFormatter.php
@@ -0,0 +1,295 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use Language;
+
+/**
+ * Class implementing message output formatting
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * This class is implementing message output formatting to avoid having
+ * classes to invoke a language object that is not a direct dependency (which
+ * means that context relevant information is mostly missing from the invoking
+ * class) therefore it is more appropriate to collect Message objects from the
+ * source and initiate an output formatting only when necessary and requested.
+ *
+ * @ingroup Formatter
+ */
+class MessageFormatter {
+
+ /** @var array */
+ protected $messages = [];
+
+ /** @var string */
+ protected $type = 'warning';
+
+ /** @var string */
+ protected $separator = ' <!--br-->';
+
+ /** @var boolean */
+ protected $escape = true;
+
+ /**
+ * @since 1.9
+ *
+ * @param Language $language
+ */
+ public function __construct( Language $language ) {
+ $this->language = $language;
+ }
+
+ /**
+ * Convenience factory method to invoke a message array together with
+ * a language object
+ *
+ * @par Example:
+ * @code
+ * MessageFormatter::newFromArray( $language, array( 'Foo' ) )->getHtml();
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param Language $language
+ * @param array|null $messages
+ *
+ * @return MessageFormatter
+ */
+ public static function newFromArray( Language $language, array $messages = [] ) {
+ $instance = new self( $language );
+ return $instance->addFromArray( $messages );
+ }
+
+ /**
+ * Creates a Message object from a key and adds it to an internal array
+ *
+ * @since 1.9
+ *
+ * @param string $key message key
+ *
+ * @return MessageFormatter
+ */
+ public function addFromKey( $key /*...*/ ) {
+ $params = func_get_args();
+ array_shift( $params );
+ $this->addFromArray( [ new \Message( $key, $params ) ] );
+ return $this;
+ }
+
+ /**
+ * Adds an arbitrary array of messages which can either contain text
+ * or/and Message objects
+ *
+ * @par Example:
+ * @code
+ * $msgFormatter = new MessageFormatter( $language );
+ * $msgFormatter->addFromArray( array( 'Foo', new Message( 'Bar' ) ) )->getHtml()
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ *
+ * @return MessageFormatter
+ */
+ public function addFromArray( array $messages ) {
+
+ $messages = ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $messages );
+
+ foreach ( $messages as $message ) {
+ if ( is_string( $message ) ) {
+ $this->messages[md5( $message )] = $message;
+ } else{
+ $this->messages[] = $message;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns unformatted invoked messages
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getMessages() {
+ return $this->messages;
+ }
+
+ /**
+ * Used in connection with the html output to invoke a specific display
+ * type
+ *
+ * @see Highlighter::getTypeId
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function setType( $type ) {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * Enables/disables escaping for the output representation
+ *
+ * @note Escaping is generally enabled but in cases of special pages or
+ * with messages already being escaped this option can be circumvent by
+ * invoking escape( false )
+ *
+ * @since 1.9
+ *
+ * @param boolean $escape
+ *
+ * @return MessageFormatter
+ */
+ public function escape( $escape ) {
+ $this->escape = (bool)$escape;
+ return $this;
+ }
+
+ /**
+ * Clears the internal message array
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function clear() {
+ $this->messages = [];
+ return $this;
+ }
+
+ /**
+ * Returns if the internal message array does contain messages
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function exists() {
+ return $this->messages !== [];
+ }
+
+ /**
+ * Overrides invoked language object
+ *
+ * @since 1.9
+ *
+ * @param Language $language
+ *
+ * @return MessageFormatter
+ */
+ public function setLanguage( Language $language ) {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * Formatting and normalization of an array
+ *
+ * @note The array is being recursively resolved in order to ensure that
+ * the returning representation is a 1-n array where duplicate entries
+ * have been eliminated already while Message objects being transformed
+ * into a simple text representation using the invoked language
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ *
+ * @return array
+ */
+ protected function doFormat( array $messages ) {
+ $newArray = [];
+
+ foreach ( $messages as $msg ) {
+
+ if ( $msg instanceof \Message ) {
+ $text = $msg->inLanguage( $this->language )->text();
+ $newArray[md5( $text )] = $text;
+ } elseif ( (array)$msg === $msg ) {
+ foreach ( $this->doFormat( $msg ) as $m ) {
+ $newArray[md5( $m )] = $m;
+ }
+ } elseif ( (string)$msg === $msg ) {
+ $newArray[md5( $msg )] = $msg;
+ }
+ }
+
+ return $newArray;
+ }
+
+ /**
+ * Converts the message array into a string representation
+ *
+ * @since 1.9
+ *
+ * @param boolean $escape
+ * @param boolean $html
+ *
+ * @return string
+ */
+ protected function getString( $html = true ) {
+
+ if ( $this->escape ) {
+ $messages = array_map( 'htmlspecialchars', array_values( $this->doFormat( $this->messages ) ) );
+ } else {
+ $messages = array_values( $this->doFormat( $this->messages ) );
+ }
+
+ if ( count( $messages ) == 1 ) {
+ $messageString = $messages[0];
+ } else {
+ foreach ( $messages as &$message ) {
+ $message = $html ? Html::rawElement( 'li', [], $message ) : $message;
+ }
+
+ $messageString = implode( $this->separator, $messages );
+ $messageString = $html ? Html::rawElement( 'ul', [], $messageString ) : $messageString;
+ }
+
+ return $messageString;
+ }
+
+ /**
+ * Returns html representation of the formatted messages
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ if ( $this->exists() ) {
+
+ $highlighter = Highlighter::factory( $this->type );
+ $highlighter->setContent( [ 'content' => $this->getString( true ) ] );
+
+ return $highlighter->getHtml();
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns plain text representation of the formatted messages
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getPlain() {
+ return $this->exists() ? $this->getString( false ) : '';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php b/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php
new file mode 100644
index 00000000..935fd4e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/params/SMW_ParamFormat.php
@@ -0,0 +1,171 @@
+<?php
+
+use ParamProcessor\Definition\StringParam;
+use ParamProcessor\IParam;
+use SMW\Query\PrintRequest;
+
+/**
+ * Definition for the format parameter.
+ *
+ * @since 1.6.2
+ * @deprecated since 1.9
+ *
+ * @ingroup SMW
+ * @ingroup ParamDefinition
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWParamFormat extends StringParam {
+
+ /**
+ * List of the queries print requests, used to determine the format
+ * when it's not provided. Set with setPrintRequests before passing
+ * to Validator.
+ *
+ * @since 1.6.2
+ *
+ * @var PrintRequest[]
+ */
+ protected $printRequests = [];
+
+ protected $showMode = false;
+
+ /**
+ * Takes a format name, which can be an alias and returns a format name
+ * which will be valid for sure. Aliases are resolved. If the given
+ * format name is invalid, the predefined default format will be returned.
+ *
+ * @since 1.6.2
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ protected function getValidFormatName( $value ) {
+ global $smwgResultFormats;
+
+ $value = strtolower( trim( $value ) );
+
+ if ( !array_key_exists( $value, $smwgResultFormats ) ) {
+ $isAlias = self::resolveFormatAliases( $value );
+
+ if ( !$isAlias ) {
+ $value = $this->getDefaultFormat();
+ self::resolveFormatAliases( $value );
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Turns format aliases into main formats.
+ *
+ * @since 1.6.2
+ *
+ * @param string $format
+ *
+ * @return boolean Indicates if the passed format was an alias, and thus was changed.
+ */
+ public static function resolveFormatAliases( &$format ) {
+ global $smwgResultAliases;
+
+ $isAlias = false;
+
+ foreach ( $smwgResultAliases as $mainFormat => $aliases ) {
+ if ( in_array( $format, $aliases ) ) {
+ $format = $mainFormat;
+ $isAlias = true;
+ break;
+ }
+ }
+
+ return $isAlias;
+ }
+
+ /**
+ * Determines and returns the default format, based on the queries print
+ * requests, if provided.
+ *
+ * @since 1.6.2
+ *
+ * @return string Array key in $smwgResultFormats
+ */
+ protected function getDefaultFormat() {
+
+ if ( empty( $this->printRequests ) ) {
+ return 'table';
+ }
+
+ $format = false;
+
+ /**
+ * This hook allows extensions to override SMWs implementation of default result
+ * format handling.
+ *
+ * @since 1.5.2
+ */
+ \Hooks::run( 'SMWResultFormat', [ &$format, $this->printRequests, [] ] );
+
+ if ( $format !== false ) {
+ return $format;
+ }
+
+ // If no default was set by an extension, use a table, plainlist or list, depending on showMode and column count.
+ if ( count( $this->printRequests ) > 1 ) {
+ return 'table';
+ }
+
+ return 'plainlist';
+ }
+
+ /**
+ * Sets the print requests of the query, used for determining
+ * the default format if none is provided.
+ *
+ * @since 1.6.2
+ *
+ * @param PrintRequest[] $printRequests
+ */
+ public function setPrintRequests( array $printRequests ) {
+ $this->printRequests = $printRequests;
+ }
+
+ /**
+ *
+ * @since 3.0
+ *
+ * @param bool $showMode
+ */
+ public function setShowMode( $showMode ) {
+ $this->showMode = $showMode;
+ }
+
+ /**
+ * Formats the parameter value to it's final result.
+ *
+ * @since 1.8
+ *
+ * @param mixed $value
+ * @param IParam $param
+ * @param IParamDefinition[] $definitions
+ * @param IParam[] $params
+ *
+ * @return mixed
+ */
+ protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
+ $value = parent::formatValue( $value, $param, $definitions, $params );
+
+ // Make sure the format value is valid.
+ $value = self::getValidFormatName( $value );
+
+ // Add the formats parameters to the parameter list.
+ $queryPrinter = SMWQueryProcessor::getResultPrinter( $value );
+
+ $definitions = $queryPrinter->getParamDefinitions( $definitions );
+
+ return $value;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php
new file mode 100644
index 00000000..eadf97b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_Query.php
@@ -0,0 +1,552 @@
+<?php
+
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\Query\Language\Description;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryContext;
+use SMW\Query\QueryStringifier;
+use SMW\Query\QueryToken;
+
+/**
+ * This file contains the class for representing queries in SMW, each
+ * consisting of a query description and possible query parameters.
+ * @ingroup SMWQuery
+ * @author Markus Krötzsch
+ */
+
+/**
+ * This group contains all parts of SMW that relate to processing semantic queries.
+ * SMW components that relate to plain storage access (for querying or otherwise)
+ * have their own group.
+ * @defgroup SMWQuery SMWQuery
+ * @ingroup SMW
+ */
+
+/**
+ * Representation of queries in SMW, each consisting of a query
+ * description and various parameters. Some settings might also lead to
+ * changes in the query description.
+ *
+ * Most additional query parameters (limit, sort, ascending, ...) are
+ * interpreted as in SMWRequestOptions (though the latter contains some
+ * additional settings).
+ * @ingroup SMWQuery
+ */
+class SMWQuery implements QueryContext {
+
+ const ID_PREFIX = '_QUERY';
+
+ /**
+ * The time the QueryEngine required to answer a query condition
+ */
+ const PROC_QUERY_TIME = 'proc.query.time';
+
+ /**
+ * The time a ResultPrinter required to build the final result including all
+ * PrintRequests
+ */
+ const PROC_PRINT_TIME = 'proc.print.time';
+
+ /**
+ * The processing context in which the query is being executed
+ */
+ const PROC_CONTEXT = 'proc.context';
+
+ /**
+ * Status code information
+ */
+ const PROC_STATUS_CODE = 'proc.status.code';
+
+ /**
+ * The processing parameters
+ */
+ const OPT_PARAMETERS = 'proc.parameters';
+
+ /**
+ * Suppress a possible cache request
+ */
+ const NO_CACHE = 'no.cache';
+
+ /**
+ * Indicates no dependency trace
+ */
+ const NO_DEPENDENCY_TRACE = 'no.dependency.trace';
+
+ /**
+ * Sort by score if the query engine supports it.
+ */
+ const SCORE_SORT = 'score.sort';
+
+ public $sort = false;
+ public $sortkeys = []; // format: "Property key" => "ASC" / "DESC" (note: order of entries also matters)
+ public $querymode = self::MODE_INSTANCES;
+
+ private $limit;
+ private $offset = 0;
+ private $description;
+ private $errors = []; // keep any errors that occurred so far
+ private $queryString = false; // string (inline query) version (if fixed and known)
+ private $isInline; // query used inline? (required for finding right default parameters)
+ private $isUsedInConcept; // query used in concept? (required for finding right default parameters)
+
+ /**
+ * @var PrintRequest[]
+ */
+ private $m_extraprintouts = []; // SMWPrintoutRequest objects supplied outside querystring
+ private $m_mainlabel = ''; // Since 1.6
+
+ /**
+ * @var DIWikiPage|null
+ */
+ private $contextPage;
+
+ /**
+ * Describes a non-local (remote) query source
+ *
+ * @var string|null
+ */
+ private $querySource = null;
+
+ /**
+ * @var QueryToken|null
+ */
+ private $queryToken;
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 1.6
+ *
+ * @param Description $description
+ * @param integer|boolean $context
+ */
+ public function __construct( Description $description = null, $context = false ) {
+ $inline = false;
+ $concept = false;
+
+ // stating whether this query runs in an inline context; used to
+ // determine proper default parameters (e.g. the default limit)
+ if ( $context === self::INLINE_QUERY || $context === self::DEFERRED_QUERY ) {
+ $inline = true;
+ }
+
+ // stating whether this query belongs to a concept; used to determine
+ // proper default parameters (concepts usually have less restrictions)
+ if ( $context === self::CONCEPT_DESC ) {
+ $concept = true;
+ }
+
+ $this->limit = $inline ? $GLOBALS['smwgQMaxInlineLimit'] : $GLOBALS['smwgQMaxLimit'];
+ $this->isInline = $inline;
+ $this->isUsedInConcept = $concept;
+ $this->description = $description;
+ $this->applyRestrictions();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function isEmbedded() {
+ return $this->isInline;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer
+ */
+ public function setQueryMode( $queryMode ) {
+ // FIXME 3.0; $this->querymode is a public property
+ // declare it private and rename it to $this->queryMode
+ $this->querymode = $queryMode;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer
+ */
+ public function getQueryMode() {
+ return $this->querymode;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIWikiPage|null $contextPage
+ */
+ public function setContextPage( DIWikiPage $contextPage = null ) {
+ $this->contextPage = $contextPage;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return DIWikiPage|null
+ */
+ public function getContextPage() {
+ return $this->contextPage;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string
+ */
+ public function setQuerySource( $querySource ) {
+ $this->querySource = $querySource;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getQuerySource() {
+ return $this->querySource;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryToken|null $queryToken
+ */
+ public function setQueryToken( QueryToken $queryToken = null ) {
+ $this->queryToken = $queryToken;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return QueryToken|null
+ */
+ public function getQueryToken() {
+ return $this->queryToken;
+ }
+
+ /**
+ * Sets the mainlabel.
+ *
+ * @since 1.6.
+ *
+ * @param string $mainlabel
+ */
+ public function setMainLabel( $mainlabel ) {
+ $this->m_mainlabel = $mainlabel;
+ }
+
+ /**
+ * Gets the mainlabel.
+ *
+ * @since 1.6.
+ *
+ * @return string
+ */
+ public function getMainLabel() {
+ return $this->m_mainlabel;
+ }
+
+ public function setDescription( SMWDescription $description ) {
+ $this->description = $description;
+ $this->queryString = false;
+
+ foreach ( $this->m_extraprintouts as $printout ) {
+ $this->description->addPrintRequest( $printout );
+ }
+ $this->applyRestrictions();
+ }
+
+ public function getDescription() {
+ return $this->description;
+ }
+
+ public function setExtraPrintouts( $extraprintouts ) {
+ $this->m_extraprintouts = $extraprintouts;
+
+ if ( !is_null( $this->description ) ) {
+ foreach ( $extraprintouts as $printout ) {
+ $this->description->addPrintRequest( $printout );
+ }
+ }
+ }
+
+ /**
+ * @return PrintRequest[]
+ */
+ public function getExtraPrintouts() {
+ return $this->m_extraprintouts;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clearErrors() {
+ $this->errors = [];
+ }
+
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ public function addErrors( $errors ) {
+ $this->errors = array_merge( $this->errors, $errors );
+ }
+
+ public function setQueryString( $querystring ) {
+ $this->queryString = $querystring;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key ) {
+ return isset( $this->options[$key] ) ? $this->options[$key] : false;
+ }
+
+ /**
+ * @since 1.7
+ *
+ * @param boolean $fresh
+ *
+ * @return string
+ */
+ public function getQueryString( $fresh = false ) {
+
+ // Mostly relevant on requesting a further results link to
+ // ensure that localized values are transformed into a canonical
+ // representation
+ if ( $fresh && $this->description !== null ) {
+ return $this->description->getQueryString();
+ }
+
+ if ( $this->queryString !== false ) {
+ return $this->queryString;
+ } elseif ( !is_null( $this->description ) ) {
+ return $this->description->getQueryString();
+ } else {
+ return '';
+ }
+ }
+
+ public function getOffset() {
+ return $this->offset;
+ }
+
+ /**
+ * Set an offset for the returned query results. No offset beyond the maximal query
+ * limit will be set, and the current query limit might be reduced in order to ensure
+ * that no results beyond the maximal limit are returned.
+ * The function returns the chosen offset.
+ * @todo The function should be extended to take into account whether or not we
+ * are in inline mode (not critical, since offsets are usually not applicable inline).
+ */
+ public function setOffset( $offset ) {
+ global $smwgQMaxLimit;
+ $this->offset = min( $smwgQMaxLimit, $offset ); // select integer between 0 and maximal limit;
+ $this->limit = min( $smwgQMaxLimit - $this->offset, $this->limit ); // note that limit might become 0 here
+ return $this->offset;
+ }
+
+ public function getLimit() {
+ return $this->limit;
+ }
+
+ /**
+ * Set a limit for number of query results. The set limit might be restricted by the
+ * current offset so as to ensure that the number of the last considered result does not
+ * exceed the maximum amount of supported results.
+ * The function returns the chosen limit.
+ * @note It makes sense to have limit==0, e.g. to only show a link to the search special
+ */
+ public function setLimit( $limit, $restrictinline = true ) {
+ global $smwgQMaxLimit, $smwgQMaxInlineLimit;
+ $maxlimit = ( $this->isInline && $restrictinline ) ? $smwgQMaxInlineLimit : $smwgQMaxLimit;
+ $this->limit = min( $smwgQMaxLimit - $this->offset, $limit, $maxlimit );
+ return $this->limit;
+ }
+
+ /**
+ * @note Sets an unbound limit that is independent from GLOBAL settings
+ *
+ * @since 2.0
+ *
+ * @param integer $limit
+ */
+ public function setUnboundLimit( $limit ) {
+ $this->limit = (int)$limit;
+ }
+
+ /**
+ * @note format: "Property key" => "ASC" / "DESC" (note: order of entries also matters)
+ *
+ * @since 2.2
+ *
+ * @param array $sortKeys
+ */
+ public function setSortKeys( array $sortKeys ) {
+ $this->sortkeys = $sortKeys;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSortKeys() {
+ return $this->sortkeys;
+ }
+
+ /**
+ * Apply structural restrictions to the current description.
+ */
+ public function applyRestrictions() {
+ global $smwgQMaxSize, $smwgQMaxDepth, $smwgQConceptMaxSize, $smwgQConceptMaxDepth;
+
+ if ( !is_null( $this->description ) ) {
+ if ( $this->isUsedInConcept ) {
+ $maxsize = $smwgQConceptMaxSize;
+ $maxdepth = $smwgQConceptMaxDepth;
+ } else {
+ $maxsize = $smwgQMaxSize;
+ $maxdepth = $smwgQMaxDepth;
+ }
+
+ $log = [];
+ $this->description = $this->description->prune( $maxsize, $maxdepth, $log );
+
+ if ( count( $log ) > 0 ) {
+ $this->errors[] = Message::encode( [
+ 'smw_querytoolarge',
+ str_replace( '[', '&#91;', implode( ', ', $log ) ),
+ count( $log )
+ ] );
+ }
+ }
+ }
+
+ /**
+ * Returns serialized query details
+ *
+ * The output is following the askargs api module convention
+ *
+ * conditions The query conditions (requirements for a subject to be included)
+ * printouts The query printouts (which properties to show per subject)
+ * parameters The query parameters (non-condition and non-printout arguments)
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function toArray() {
+ $serialized = [];
+
+ $serialized['conditions'] = $this->getQueryString();
+
+ // This can be extended but for the current use cases that is
+ // sufficient since most printer related parameters have to be sourced
+ // in the result printer class
+ $serialized['parameters'] = [
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ 'sortkeys' => $this->sortkeys,
+ 'mainlabel' => $this->m_mainlabel,
+ 'querymode' => $this->querymode
+ ];
+
+ // @2.4 Keep the queryID stable with previous versions unless
+ // a query source is selected. The "same" query executed on different
+ // remote systems requires a different queryID
+ if ( $this->querySource !== null && $this->querySource !== '' ) {
+ $serialized['parameters']['source'] = $this->querySource;
+ }
+
+ foreach ( $this->getExtraPrintouts() as $printout ) {
+ if ( ( $serialisation = $printout->getSerialisation() ) !== '' ) {
+ $serialized['printouts'][] = $serialisation;
+ }
+ }
+
+ return $serialized;
+ }
+
+ /**
+ * @note Before 2.5, toArray was used to generate the content, as of 2.5
+ * only parameters that influence the result of an query is included.
+ *
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ // Only use elements that directly influence the result list
+ $serialized = [];
+
+ // Don't use the QueryString, use the canonized fingerprint to ensure that
+ // [[Foo::123]][[Bar::abc]] returns the same ID as [[Bar::abc]][[Foo::123]]
+ // given that limit, offset, and sort/order are the same
+ if ( $this->description !== null ) {
+ $serialized['fingerprint'] = $this->description->getFingerprint();
+ } else {
+ $serialized['conditions'] = $this->getQueryString();
+ }
+
+ $serialized['parameters'] = [
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ 'sortkeys' => $this->sortkeys,
+
+ // COUNT, DEBUG ...
+ 'querymode' => $this->querymode
+ ];
+
+ // Make to sure to distinguish queries and results from a foreign repository
+ if ( $this->querySource !== null && $this->querySource !== '' ) {
+ $serialized['parameters']['source'] = $this->querySource;
+ }
+
+ // Printouts are avoided as part of the hash as they not influence the
+ // list of entities and are only resolved after the query result has
+ // been retrieved
+ return md5( json_encode( $serialized ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function toString() {
+ return QueryStringifier::toString( $this );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getQueryId() {
+ return self::ID_PREFIX . $this->getHash();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php
new file mode 100644
index 00000000..ed851db5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/query/SMW_QueryProcessor.php
@@ -0,0 +1,530 @@
+<?php
+
+use ParamProcessor\Options;
+use ParamProcessor\Param;
+use ParamProcessor\ParamDefinition;
+use ParamProcessor\Processor;
+use SMW\ApplicationFactory;
+use SMW\Message;
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\Query\Deferred;
+use SMW\Query\PrintRequest;
+use SMW\Query\Processor\ParamListProcessor;
+use SMW\Query\Processor\DefaultParamDefinition;
+use SMW\Query\QueryContext;
+use SMW\Query\Exception\ResultFormatNotFoundException;
+
+/**
+ * This file contains a static class for accessing functions to generate and execute
+ * semantic queries and to serialise their results.
+ *
+ * @ingroup SMWQuery
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Static class for accessing functions to generate and execute semantic queries
+ * and to serialise their results.
+ * @ingroup SMWQuery
+ */
+class SMWQueryProcessor implements QueryContext {
+
+ /**
+ * @var RecursiveTextProcessor
+ */
+ private static $recursiveTextProcessor;
+
+ /**
+ * @since 3.0
+ *
+ * @param RecursiveTextProcessor|null $recursiveTextProcessor
+ */
+ public static function setRecursiveTextProcessor( RecursiveTextProcessor $recursiveTextProcessor = null ) {
+ self::$recursiveTextProcessor = $recursiveTextProcessor;
+ }
+
+ /**
+ * Takes an array of unprocessed parameters, processes them using
+ * Validator, and returns them.
+ *
+ * Both input and output arrays are
+ * param name (string) => param value (mixed)
+ *
+ * @since 1.6.2
+ * The return value changed in SMW 1.8 from an array with result values
+ * to an array with Param objects.
+ *
+ * @param array $params
+ * @param array $printRequests
+ * @param boolean $unknownInvalid
+ *
+ * @return Param[]
+ */
+ public static function getProcessedParams( array $params, array $printRequests = [], $unknownInvalid = true, $context = null, $showMode = false ) {
+ $validator = self::getValidatorForParams( $params, $printRequests, $unknownInvalid, $context, $showMode );
+ $validator->processParameters();
+ $parameters = $validator->getParameters();
+
+ // Negates some weird behaviour of ParamDefinition::setDefault used in
+ // an individual printer.
+ // Applying $smwgQMaxLimit, $smwgQMaxInlineLimit will happen at a later
+ // stage.
+ if ( isset( $params['limit'] ) && isset( $parameters['limit'] ) ) {
+ $parameters['limit']->setValue( (int)$params['limit'] );
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Parse a query string given in SMW's query language to create
+ * an SMWQuery. Parameters are given as key-value-pairs in the
+ * given array. The parameter $context defines in what context the
+ * query is used, which affects ceretain general settings.
+ * An object of type SMWQuery is returned.
+ *
+ * The format string is used to specify the output format if already
+ * known. Otherwise it will be determined from the parameters when
+ * needed. This parameter is just for optimisation in a common case.
+ *
+ * @param string $queryString
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param $context
+ * @param string $format
+ * @param array $extraPrintouts
+ *
+ * @return SMWQuery
+ */
+ static public function createQuery( $queryString, array $params, $context = self::INLINE_QUERY, $format = '', array $extraPrintouts = [], $contextPage = null ) {
+
+ if ( $format === '' || is_null( $format ) ) {
+ $format = $params['format']->getValue();
+ }
+
+ $defaultSort = 'ASC';
+
+ if ( $format == 'count' ) {
+ $queryMode = SMWQuery::MODE_COUNT;
+ } elseif ( $format == 'debug' ) {
+ $queryMode = SMWQuery::MODE_DEBUG;
+ } else {
+ $printer = self::getResultPrinter( $format, $context );
+ $queryMode = $printer->getQueryMode( $context );
+ $defaultSort = $printer->getDefaultSort();
+ }
+
+ // set mode, limit, and offset:
+ $offset = 0;
+ $limit = $GLOBALS['smwgQDefaultLimit'];
+
+ if ( ( array_key_exists( 'offset', $params ) ) && ( is_int( $params['offset']->getValue() + 0 ) ) ) {
+ $offset = $params['offset']->getValue();
+ }
+
+ if ( ( array_key_exists( 'limit', $params ) ) && ( is_int( trim( $params['limit']->getValue() ) + 0 ) ) ) {
+ $limit = $params['limit']->getValue();
+
+ // limit < 0: always show further results link only
+ if ( ( trim( $params['limit']->getValue() ) + 0 ) < 0 ) {
+ $queryMode = SMWQuery::MODE_NONE;
+ }
+ }
+
+ // largest possible limit for "count", even inline
+ if ( $queryMode == SMWQuery::MODE_COUNT ) {
+ $offset = 0;
+ $limit = $GLOBALS['smwgQMaxLimit'];
+ }
+
+ $queryCreator = ApplicationFactory::getInstance()->singleton( 'QueryCreator' );
+
+ $params = [
+ 'extraPrintouts' => $extraPrintouts,
+ 'queryMode' => $queryMode,
+ 'context' => $context,
+ 'contextPage' => $contextPage,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'source' => $params['source']->getValue(),
+ 'mainLabel' => $params['mainlabel']->getValue(),
+ 'sort' => $params['sort']->getValue(),
+ 'order' => $params['order']->getValue(),
+ 'defaultSort' => $defaultSort
+ ];
+
+ return $queryCreator->create( $queryString, $params );
+ }
+
+ /**
+ * Add the subject print request, unless mainlabel is set to "-".
+ *
+ * @since 1.7
+ *
+ * @param array $printRequests
+ * @param array $rawParams
+ */
+ public static function addThisPrintout( array &$printRequests, array $rawParams ) {
+
+ if ( $printRequests === null ) {
+ return;
+ }
+
+ // If THIS is already registered, bail-out!
+ foreach ( $printRequests as $printRequest ) {
+ if ( $printRequest->isMode( PrintRequest::PRINT_THIS ) ) {
+ return;
+ }
+ }
+
+ $hasMainlabel = array_key_exists( 'mainlabel', $rawParams );
+
+ if ( !$hasMainlabel || trim( $rawParams['mainlabel'] ) !== '-' ) {
+ $printRequest = new PrintRequest(
+ PrintRequest::PRINT_THIS,
+ $hasMainlabel ? $rawParams['mainlabel'] : ''
+ );
+
+ // Signal to any post-processing that THIS was added outside of
+ // the normal processing chain
+ $printRequest->isDisconnected( true );
+
+ array_unshift( $printRequests, $printRequest );
+ }
+ }
+
+ /**
+ * Preprocess a query as given by an array of parameters as is typically
+ * produced by the #ask parser function. The parsing results in a querystring,
+ * an array of additional parameters, and an array of additional SMWPrintRequest
+ * objects, which are filled into call-by-ref parameters.
+ * $showMode is true if the input should be treated as if given by #show
+ *
+ * @param array $rawParams
+ * @param string $querystring
+ * @param array $params
+ * @param array $printouts array of SMWPrintRequest
+ * @param boolean $showMode
+ * @deprecated Will vanish after SMW 1.8 is released.
+ * Use getComponentsFromFunctionParams which has a cleaner interface.
+ */
+ static public function processFunctionParams( array $rawParams, &$querystring, &$params, &$printouts, $showMode = false ) {
+ list( $querystring, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+ }
+
+
+ /**
+ * Preprocess a query as given by an array of parameters as is
+ * typically produced by the #ask parser function or by Special:Ask.
+ * The parsing results in a querystring, an array of additional
+ * parameters, and an array of additional SMWPrintRequest objects,
+ * which are returned in an array with three components. If
+ * $showMode is true then the input will be processed as if for #show.
+ * This uses a slightly different way to get the query, and different
+ * default labels (empty) for additional print requests.
+ *
+ * @param array $rawParams
+ * @param boolean $showMode
+ * @return array( string, array( string => string ), array( SMWPrintRequest ) )
+ */
+ static public function getComponentsFromFunctionParams( array $rawParams, $showMode ) {
+
+ $paramListProcessor = ApplicationFactory::getInstance()->singleton( 'ParamListProcessor' );
+
+ return $paramListProcessor->format(
+ $paramListProcessor->preprocess( $rawParams, $showMode ),
+ ParamListProcessor::FORMAT_LEGACY
+ );
+ }
+
+ /**
+ * Process and answer a query as given by an array of parameters as is
+ * typically produced by the #ask parser function. The parameter
+ * $context defines in what context the query is used, which affects
+ * certain general settings.
+ *
+ * The main task of this function is to preprocess the raw parameters
+ * to obtain actual parameters, printout requests, and the query string
+ * for further processing.
+ *
+ * @since 1.8
+ * @param array $rawParams user-provided list of unparsed parameters
+ * @param integer $outputMode SMW_OUTPUT_WIKI, SMW_OUTPUT_HTML, ...
+ * @param integer $context INLINE_QUERY, SPECIAL_PAGE, CONCEPT_DESC
+ * @param boolean $showMode process like #show parser function?
+ * @return array( SMWQuery, array of IParam )
+ */
+ static public function getQueryAndParamsFromFunctionParams( array $rawParams, $outputMode, $context, $showMode, $contextPage = null ) {
+ list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+
+ if ( !$showMode ) {
+ self::addThisPrintout( $printouts, $params );
+ }
+
+ $params = self::getProcessedParams( $params, $printouts, true, $context, $showMode );
+
+ $query = self::createQuery( $queryString, $params, $context, '', $printouts, $contextPage );
+
+ // For convenience keep parameters and options to be available for immediate
+ // processing
+ if ( $context === self::DEFERRED_QUERY ) {
+ $query->setOption( Deferred::QUERY_PARAMETERS, implode( '|', $rawParams ) );
+ $query->setOption( Deferred::SHOW_MODE, $showMode );
+ $query->setOption( Deferred::CONTROL_ELEMENT, isset( $params['@control'] ) ? $params['@control']->getValue() : '' );
+ }
+
+ return [ $query, $params ];
+ }
+
+ /**
+ * Process and answer a query as given by an array of parameters as is
+ * typically produced by the #ask parser function. The result is formatted
+ * according to the specified $outputformat. The parameter $context defines
+ * in what context the query is used, which affects ceretain general settings.
+ *
+ * The main task of this function is to preprocess the raw parameters to
+ * obtain actual parameters, printout requests, and the query string for
+ * further processing.
+ *
+ * @note Consider using getQueryAndParamsFromFunctionParams() and
+ * getResultFromQuery() instead.
+ * @deprecated Will vanish after release of SMW 1.8.
+ * See SMW_Ask.php for example code on how to get query results from
+ * #ask function parameters.
+ */
+ static public function getResultFromFunctionParams( array $rawParams, $outputMode, $context = self::INLINE_QUERY, $showMode = false ) {
+ list( $queryString, $params, $printouts ) = self::getComponentsFromFunctionParams( $rawParams, $showMode );
+
+ if ( !$showMode ) {
+ self::addThisPrintout( $printouts, $params );
+ }
+
+ $params = self::getProcessedParams( $params, $printouts );
+
+ return self::getResultFromQueryString( $queryString, $params, $printouts, SMW_OUTPUT_WIKI, $context );
+ }
+
+ /**
+ * Process a query string in SMW's query language and return a formatted
+ * result set as specified by $outputmode. A parameter array of key-value-pairs
+ * constrains the query and determines the serialisation mode for results. The
+ * parameter $context defines in what context the query is used, which affects
+ * certain general settings. Finally, $extraprintouts supplies additional
+ * printout requests for the query results.
+ *
+ * @param string $queryString
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param $extraPrintouts
+ * @param $outputMode
+ * @param $context
+ *
+ * @return string
+ * @deprecated Will vanish after release of SMW 1.8.
+ * See SMW_Ask.php for example code on how to get query results from
+ * #ask function parameters.
+ */
+ static public function getResultFromQueryString( $queryString, array $params, $extraPrintouts, $outputMode, $context = self::INLINE_QUERY ) {
+
+ $query = self::createQuery( $queryString, $params, $context, '', $extraPrintouts );
+ $result = self::getResultFromQuery( $query, $params, $outputMode, $context );
+
+
+ return $result;
+ }
+
+ /**
+ * Create a fully formatted result string from a query and its
+ * parameters. The method takes care of processing various types of
+ * query result. Most cases are handled by printers, but counting and
+ * debugging uses special code.
+ *
+ * @param SMWQuery $query
+ * @param array $params These need to be the result of a list fed to getProcessedParams
+ * @param integer $outputMode
+ * @param integer $context
+ * @since before 1.7, but public only since 1.8
+ *
+ * @return string
+ */
+ public static function getResultFromQuery( SMWQuery $query, array $params, $outputMode, $context ) {
+
+ $printer = self::getResultPrinter(
+ $params['format']->getValue(),
+ $context
+ );
+
+ if ( $printer->isDeferrable() && $context === self::DEFERRED_QUERY && $query->getLimit() > 0 ) {
+
+ // Halt processing that is not `DEFERRED_DATA` as it is expected the
+ // process is picked-up by the `deferred.js` loader that will
+ // initiate an API request to finalize the query after MW has build
+ // the page.
+ if ( $printer->isDeferrable() !== $printer::DEFERRED_DATA ) {
+ return Deferred::buildHTML( $query );
+ }
+
+ // `DEFERRED_DATA` is interpret as "execute the query with limit=0" (i.e.
+ // no query execution) but allow the printer to setup the HTML so that
+ // the data can be loaded after MW has finished the page build including
+ // the pre-rendered query HTML representation. This mode deferrers the
+ // actual query execution and data load to after the page build.
+ //
+ // Each printer that uses this mode has to handle the required parameters
+ // and data load accordingly.
+ $query->querymode = SMWQuery::MODE_INSTANCES;
+ $query->setOption( 'deferred.limit', $query->getLimit() );
+ $query->setLimit( 0 );
+ }
+
+ $res = self::getStoreFromParams( $params )->getQueryResult( $query );
+ $start = microtime( true );
+
+ if ( $res instanceof SMWQueryResult && $query->getOption( 'calc.result_hash' ) ) {
+ $query->setOption( 'result_hash', $res->getHash( 'quick' ) );
+ }
+
+ if ( ( $query->querymode == SMWQuery::MODE_INSTANCES ) ||
+ ( $query->querymode == SMWQuery::MODE_NONE ) ) {
+
+ $result = $printer->getResult( $res, $params, $outputMode );
+
+ $query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
+ return $result;
+ } else { // result for counting or debugging is just a string or number
+
+ if ( $res instanceof SMWQueryResult ) {
+ $res = $res->getCountValue();
+ }
+
+ if ( is_numeric( $res ) ) {
+ $res = strval( $res );
+ }
+
+ if ( is_string( $res ) ) {
+ $result = str_replace( '_', ' ', $params['intro']->getValue() )
+ . $res
+ . str_replace( '_', ' ', $params['outro']->getValue() )
+ . smwfEncodeMessages( $query->getErrors() );
+ } else {
+ // When no valid result was obtained, $res will be a SMWQueryResult.
+ $result = smwfEncodeMessages( $query->getErrors() );
+ }
+
+ $query->setOption( SMWQuery::PROC_PRINT_TIME, microtime( true ) - $start );
+
+ return $result;
+ }
+ }
+
+ private static function getStoreFromParams( array $params ) {
+ return ApplicationFactory::getInstance()->getQuerySourceFactory()->get( $params['source']->getValue() );
+ }
+
+ /**
+ * Find suitable SMWResultPrinter for the given format. The context in
+ * which the query is to be used determines some basic settings of the
+ * returned printer object. Possible contexts are
+ * SMWQueryProcessor::SPECIAL_PAGE, SMWQueryProcessor::INLINE_QUERY,
+ * SMWQueryProcessor::CONCEPT_DESC.
+ *
+ * @param string $format
+ * @param $context
+ *
+ * @return SMWResultPrinter
+ * @throws MissingResultFormatException
+ */
+ static public function getResultPrinter( $format, $context = self::SPECIAL_PAGE ) {
+ global $smwgResultFormats;
+
+ SMWParamFormat::resolveFormatAliases( $format );
+
+ if ( !array_key_exists( $format, $smwgResultFormats ) ) {
+ throw new ResultFormatNotFoundException( "There is no result format for '$format'." );
+ }
+
+ $formatClass = $smwgResultFormats[$format];
+
+ $printer = new $formatClass( $format, ( $context != self::SPECIAL_PAGE ) );
+
+ if ( self::$recursiveTextProcessor === null ) {
+ self::$recursiveTextProcessor = new RecursiveTextProcessor();
+ }
+
+ $printer->setRecursiveTextProcessor(
+ self::$recursiveTextProcessor
+ );
+
+ return $printer;
+ }
+
+ /**
+ * Produces a list of default allowed parameters for a result printer. Most
+ * query printers should override this function.
+ *
+ * @since 1.6.2, return element type changed in 1.8
+ *
+ * @param integer|null $context
+ * @param ResultPrinter|null $resultPrinter
+ *
+ * @return IParamDefinition[]
+ */
+ public static function getParameters( $context = null, $resultPrinter = null ) {
+ return DefaultParamDefinition::getParamDefinitions( $context, $resultPrinter );
+ }
+
+ /**
+ * Returns the definitions of all parameters supported by the specified format.
+ *
+ * @since 1.8
+ *
+ * @param string $format
+ *
+ * @return ParamDefinition[]
+ */
+ public static function getFormatParameters( $format ) {
+ SMWParamFormat::resolveFormatAliases( $format );
+
+ if ( !array_key_exists( $format, $GLOBALS['smwgResultFormats'] ) ) {
+ return [];
+ }
+
+ $resultPrinter = self::getResultPrinter( $format );
+
+ if ( $resultPrinter instanceof \SMW\Query\ResultPrinters\NullResultPrinter ) {
+ return [];
+ }
+
+ return ParamDefinition::getCleanDefinitions(
+ $resultPrinter->getParamDefinitions( self::getParameters( null, $resultPrinter ) )
+ );
+ }
+
+ /**
+ * Takes an array of unprocessed parameters,
+ * and sets them on a new Validator object,
+ * which is returned and ready to process the parameters.
+ *
+ * @since 1.8
+ *
+ * @param array $params
+ * @param array $printRequests
+ * @param boolean $unknownInvalid
+ *
+ * @return Processor
+ */
+ private static function getValidatorForParams( array $params, array $printRequests = [], $unknownInvalid = true, $context = null, $showMode = false ) {
+ $paramDefinitions = self::getParameters( $context );
+
+ $paramDefinitions['format']->setPrintRequests( $printRequests );
+ $paramDefinitions['format']->setShowMode( $showMode );
+
+ $processorOptions = new Options();
+ $processorOptions->setUnknownInvalid( $unknownInvalid );
+
+ $validator = Processor::newFromOptions( $processorOptions );
+
+ $validator->setParameters( $params, $paramDefinitions, false );
+
+ return $validator;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php
new file mode 100644
index 00000000..737fce97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/PropertiesQueryPage.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\Exception\PropertyNotFoundException;
+use SMWDIError;
+use SMWTypesValue;
+
+/**
+ * Query class that provides content for the Special:Properties page
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class PropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @return string
+ */
+ function getPageHeader() {
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-sp-properties-docu' ],
+ $this->msg( 'smw-sp-properties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo() ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return 'Properties';
+ }
+
+ /**
+ * Format a result in the list of results as a string. We expect the
+ * result to be an array with one object of type SMWDIProperty
+ * (normally) or maybe SMWDIError (if something went wrong), followed
+ * by a number (how often the property is used).
+ *
+ * @param Skin $skin provided by MediaWiki, not needed here
+ * @param mixed $result
+ * @return String
+ * @throws PropertyNotFoundException if the result was not of a supported type
+ */
+ function formatResult( $skin, $result ) {
+
+ list ( $dataItem, $useCount ) = $result;
+
+ if ( $dataItem instanceof DIProperty ) {
+ return $this->formatPropertyItem( $dataItem, $useCount );
+ } elseif ( $dataItem instanceof SMWDIError ) {
+ return $this->getMessageFormatter()->clear()
+ ->setType( 'warning' )
+ ->addFromArray( [ $dataItem->getErrors(), 'ID: ' . ( isset( $dataItem->id ) ? $dataItem->id : 'N/A' ) ] )
+ ->getHtml();
+ }
+
+ throw new PropertyNotFoundException( 'PropertiesQueryPage expects results that are properties or errors.' );
+ }
+
+ /**
+ * Produce a formatted string representation for showing a property and
+ * its usage count in the list of used properties.
+ *
+ * @since 1.8
+ *
+ * @param DIProperty $property
+ * @param integer $useCount
+ * @return string
+ */
+ protected function formatPropertyItem( DIProperty $property, $useCount ) {
+
+ // Clear formatter before invoking messages
+ $this->getMessageFormatter()->clear();
+
+ $diWikiPage = $property->getDiWikiPage();
+ $title = $diWikiPage !== null ? $diWikiPage->getTitle() : null;
+
+ if ( $useCount == 0 && !$this->settings->get( 'smwgPropertyZeroCountDisplay' ) ) {
+ return '';
+ }
+
+ if ( $property->isUserDefined() ) {
+
+ if ( $title === null ) {
+ // Show even messed up property names.
+ $typestring = '';
+ $proplink = $property->getLabel();
+ $this->getMessageFormatter()
+ ->addFromArray( [ 'ID: ' . ( isset( $property->id ) ? $property->id : 'N/A' ) ] )
+ ->addFromKey( 'smw_notitle', $proplink );
+ } else {
+ list( $typestring, $proplink ) = $this->getUserDefinedPropertyInfo( $title, $property, $useCount );
+ }
+
+ $infoLink = '';
+
+ // Add a link to SearchByProperty to hopefully identify the
+ // "hidden" reference
+ if ( $useCount < 1 ) {
+ $infoLink = '&#160;' . \SMWInfolink::newPropertySearchLink( '+', $property->getLabel(), '' )->getHTML( $this->getLinker() );
+ }
+
+ $proplink .= $infoLink;
+
+ } else {
+ list( $typestring, $proplink ) = $this->getPredefinedPropertyInfo( $property );
+ }
+
+ if ( $typestring === '' ) { // Built-ins have no type
+
+ // @todo Should use numParams for $useCount?
+ return $this->msg( 'smw_property_template_notype' )
+ ->rawParams( $proplink )->numParams( $useCount )->text() . ' ' .
+ $this->getMessageFormatter()
+ ->setType( 'warning' )
+ ->escape( false )->getHtml();
+
+ } else {
+
+ // @todo Should use numParams for $useCount?
+ return $this->msg( 'smw_property_template' )
+ ->rawParams( $proplink, $typestring )->numParams( $useCount )->escaped() . ' ' .
+ $this->getMessageFormatter()
+ ->setType( 'warning' )
+ ->escape( false )->getHtml();
+
+ }
+ }
+
+ /**
+ * Returns information related to user-defined properties
+ *
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param DIProperty $property
+ * @param integer $useCount
+ *
+ * @return array
+ */
+ private function getUserDefinedPropertyInfo( $title, $property, $useCount ) {
+
+ if ( $useCount <= $this->settings->get( 'smwgPropertyLowUsageThreshold' ) ) {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertyhardlyused' );
+ }
+
+ // User defined types default to Page
+ $typestring = SMWTypesValue::newFromTypeId( $this->settings->get( 'smwgPDefaultType' ) )->getLongHTMLText( $this->getLinker() );
+
+ $label = htmlspecialchars( $property->getLabel() );
+ $linkAttributes = [];
+
+ if ( isset( $property->id ) ) {
+ $linkAttributes['title'] = 'ID: ' . $property->id;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $property );
+ $dataValue->setLinkAttributes( $linkAttributes );
+
+ $proplink = $dataValue->getFormattedLabel(
+ DataValueFormatter::HTML_SHORT,
+ $this->getLinker()
+ );
+
+ if ( !$title->exists() ) {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackspage' );
+ }
+
+ $typeProperty = new DIProperty( '_TYPE' );
+ $types = $this->store->getPropertyValues( $property->getDiWikiPage(), $typeProperty );
+
+ if ( is_array( $types ) && count( $types ) >= 1 ) {
+ $typeDataValue = DataValueFactory::getInstance()->newDataValueByItem( current( $types ), $typeProperty );
+ $typestring = $typeDataValue->getLongHTMLText( $this->getLinker() );
+ } else {
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackstype', $typestring );
+ }
+
+ return [ $typestring, $proplink ];
+ }
+
+ /**
+ * Returns information related to predefined properties
+ *
+ * @since 1.9
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ private function getPredefinedPropertyInfo( DIProperty $property ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $property, null );
+
+ $dataValue->setLinkAttributes( [
+ 'title' => 'ID: ' . ( isset( $property->id ) ? $property->id : 'N/A' ) . ' (' . $property->getKey() . ')'
+ ] );
+
+ $label = $dataValue->getFormattedLabel(
+ DataValueFormatter::HTML_SHORT,
+ $this->getLinker()
+ );
+
+ return [
+ SMWTypesValue::newFromTypeId( $property->findPropertyTypeID() )->getLongHTMLText( $this->getLinker() ),
+ $label
+ ];
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of array( SMWDIProperty|SMWDIError, integer )
+ */
+ function getResults( $requestOptions ) {
+ $this->listLookup = $this->store->getPropertiesSpecial( $requestOptions );
+ return $this->listLookup->fetchList();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php
new file mode 100644
index 00000000..79cecae8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/QueryPage.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMWRequestOptions;
+use SMWStringCondition;
+use Xml;
+
+/**
+ * An abstract query page base class that supports array-based
+ * data retrieval instead of the SQL-based access used by MW.
+ *
+ *
+ * @license GNU GPL v2+
+ * @since ??
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Abstract base class for SMW's variant of the MW QueryPage.
+ * Subclasses must implement getResults() and formatResult(), as
+ * well as some other standard functions of QueryPage.
+ *
+ * @ingroup SMW
+ * @ingroup QueryPage
+ */
+abstract class QueryPage extends \QueryPage {
+
+ /** @var MessageFormatter */
+ protected $msgFormatter;
+
+ /** @var Linker */
+ protected $linker = null;
+
+ /** @var array */
+ protected $selectOptions = [];
+
+ /** @var array */
+ protected $useSerchForm = false;
+
+ /**
+ * Implemented by subclasses to provide concrete functions.
+ */
+ abstract function getResults( $requestoptions );
+
+ /**
+ * Clear the cache and save new results
+ * @todo Implement caching for SMW query pages
+ */
+ function recache( $limit, $ignoreErrors = true ) {
+ /// TODO
+ }
+
+ function isExpensive() {
+ return false; // Disables caching for now
+ }
+
+ function isSyndicated() {
+ return false; // TODO: why not?
+ }
+
+ /**
+ * @see QueryPage::linkParameters
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function linkParameters() {
+
+ $parameters = [];
+ $property = $this->getRequest()->getVal( 'property' );
+
+ if ( $property !== null && $property !== '' ) {
+ $parameters['property'] = $property;
+ }
+
+ $filter = $this->getRequest()->getVal( 'filter' );
+
+ if ( $filter !== null && $filter !== '' ) {
+ $parameters['filter'] = $filter;
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Returns a MessageFormatter object
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ public function getMessageFormatter() {
+
+ if ( !isset( $this->msgFormatter ) ) {
+ $this->msgFormatter = new MessageFormatter( $this->getLanguage() );
+ }
+
+ return $this->msgFormatter;
+ }
+
+ /**
+ * Returns a Linker object
+ *
+ * @since 1.9
+ *
+ * @return Linker
+ */
+ public function getLinker() {
+
+ if ( $this->linker === null ) {
+ $this->linker = smwfGetLinker();
+ }
+
+ return $this->linker;
+ }
+
+ /**
+ * Generates a search form
+ *
+ * @since 1.9
+ *
+ * @param string $property
+ *
+ * @return string
+ */
+ public function getSearchForm( $property = '', $cacheDate = '', $propertySearch = true, $filter = '' ) {
+
+ $this->useSerchForm = true;
+ $this->getOutput()->addModules( 'ext.smw.autocomplete.property' );
+
+ // No need to verify $this->selectOptions because its values are set
+ // during doQuery() which is processed before this form is generated
+ $resultCount = wfShowingResults( $this->selectOptions['offset'], $this->selectOptions['count'] );
+
+ $selection = $this->getLanguage()->viewPrevNext(
+ $this->getContext()->getTitle(),
+ $this->selectOptions['offset'],
+ $this->selectOptions['limit'],
+ $this->linkParameters(),
+ $this->selectOptions['end']
+ );
+
+ if ( $cacheDate !== '' ) {
+ $cacheDate = Xml::tags( 'p', [], $cacheDate );
+ }
+
+ if ( $propertySearch ) {
+ $propertySearch = Xml::tags( 'hr', [ 'style' => 'margin-bottom:10px;' ], '' ) .
+ Xml::inputLabel( $this->msg( 'smw-special-property-searchform' )->text(), 'property', 'smw-property-input', 20, $property ) . ' ' .
+ Xml::submitButton( $this->msg( 'allpagessubmit' )->text() );
+ }
+
+ if ( $filter !== '' ) {
+ $filter = Xml::tags( 'hr', [ 'style' => 'margin-bottom:10px;' ], '' ) . $filter;
+ }
+
+ return Xml::tags( 'form', [
+ 'method' => 'get',
+ 'action' => htmlspecialchars( $GLOBALS['wgScript'] ),
+ 'class' => 'plainlinks'
+ ], Html::hidden( 'title', $this->getContext()->getTitle()->getPrefixedText() ) .
+ Xml::fieldset( $this->msg( 'smw-special-property-searchform-options' )->text(),
+ Xml::tags( 'p', [], $resultCount ) .
+ Xml::tags( 'p', [], $selection ) .
+ $cacheDate .
+ $propertySearch .
+ $filter
+ )
+ );
+ }
+
+ /**
+ * This is the actual workhorse. It does everything needed to make a
+ * real, honest-to-gosh query page.
+ * Alas, we need to overwrite the whole beast since we do not assume
+ * an SQL-based storage backend.
+ *
+ * @param $offset database query offset
+ * @param $limit database query limit
+ * @param $property database string query
+ */
+ function doQuery( $offset = false, $limit = false, $property = false ) {
+ $out = $this->getOutput();
+ $sk = $this->getSkin();
+
+ $options = new SMWRequestOptions();
+ $options->limit = $limit;
+ $options->offset = $offset;
+ $options->sort = true;
+
+ if ( $property ) {
+ $options->addStringCondition( $property, SMWStringCondition::STRCOND_MID );
+ }
+
+ if ( ( $filter = $this->getRequest()->getVal( 'filter' ) ) === 'unapprove' ) {
+ $options->addExtraCondition( [ 'filter.unapprove' => true ] );
+ }
+
+ $res = $this->getResults( $options );
+ $num = count( $res );
+
+ // often disable 'next' link when we reach the end
+ $atend = $num < $limit;
+
+ $this->selectOptions = [
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'end' => $atend,
+ 'count' => $num
+ ];
+
+ $out->addHTML( $this->getPageHeader() );
+
+ // if list is empty, show it
+ if ( $num == 0 ) {
+ $out->addHTML( '<p>' . $this->msg( 'specialpage-empty' )->escaped() . '</p>' );
+ return;
+ }
+
+ if ( $num > 0 ) {
+ $s = [];
+ if ( ! $this->listoutput ) {
+ $s[] = $this->openList( $offset );
+ }
+
+ foreach ( $res as $r ) {
+ $format = $this->formatResult( $sk, $r );
+ if ( $format ) {
+ $s[] = $this->listoutput ? $format : "<li>{$format}</li>\n";
+ }
+ }
+
+ if ( ! $this->listoutput ) {
+ $s[] = $this->closeList();
+ }
+ $str = $this->listoutput ? $this->getLanguage()->listToText( $s ) : implode( '', $s );
+ $out->addHTML( $str );
+ }
+
+ if ( !$this->useSerchForm ) {
+ $out->addHTML( "<p>{$sl}</p>\n" );
+ }
+
+ return $num;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php
new file mode 100644
index 00000000..878acf98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/UnusedPropertiesQueryPage.php
@@ -0,0 +1,186 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\Exception\PropertyNotFoundException;
+use SMWDIError;
+use SMWTypesValue;
+
+/**
+ * Query page that provides content to Special:UnusedProperties
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class UnusedPropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return "UnusedProperties";
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isExpensive() {
+ return false; // Disables caching for now
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isSyndicated() {
+ return false; // TODO: why not?
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getPageHeader() {
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-unusedproperties-docu' ],
+ $this->msg( 'smw-unusedproperties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo() ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * Format a result in the list of results as a string. We expect the
+ * result to be an object of type SMWDIProperty (normally) or maybe
+ * SMWDIError (if something went wrong).
+ *
+ * @param Skin $skin provided by MediaWiki, not needed here
+ * @param mixed $result
+ *
+ * @return String
+ * @throws InvalidResultException if the result was not of a supported type
+ */
+ function formatResult( $skin, $result ) {
+
+ if ( $result instanceof DIProperty ) {
+ return $this->formatPropertyItem( $result );
+ } elseif ( $result instanceof SMWDIError ) {
+ return $this->getMessageFormatter()->clear()
+ ->setType( 'warning' )
+ ->addFromArray( [ $result->getErrors() ] )
+ ->getHtml();
+ }
+
+ throw new PropertyNotFoundException( 'UnusedPropertiesQueryPage expects results that are properties or errors.' );
+ }
+
+ /**
+ * Produce a formatted string representation for showing a property in
+ * the list of unused properties.
+ *
+ * @since 1.8
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ protected function formatPropertyItem( DIProperty $property ) {
+
+ // Clear formatter before invoking messages and
+ // avoid having previous data to be present
+ $this->getMessageFormatter()->clear();
+
+ if ( $property->isUserDefined() ) {
+
+ $title = $property->getDiWikiPage()->getTitle();
+
+ if ( !$title instanceof \Title ) {
+ return '';
+ }
+
+ $propertyLink = $this->getLinker()->link(
+ $title,
+ $property->getLabel()
+ );
+
+ $types = $this->store->getPropertyValues( $property->getDiWikiPage(), new DIProperty( '_TYPE' ) );
+
+ if ( is_array( $types ) && count( $types ) >= 1 ) {
+ $typeDataValue = DataValueFactory::getInstance()->newDataValueByItem( current( $types ), new DIProperty( '_TYPE' ) );
+ } else {
+ $typeDataValue = SMWTypesValue::newFromTypeId( '_wpg' );
+ $this->getMessageFormatter()->addFromKey( 'smw_propertylackstype', $typeDataValue->getLongHTMLText() );
+ }
+
+ } else {
+ $typeDataValue = SMWTypesValue::newFromTypeId( $property->findPropertyTypeID() );
+ $propertyLink = DataValueFactory::getInstance()->newDataValueByItem( $property, null )->getShortHtmlText( $this->getLinker() );
+ }
+
+ return $this->msg( 'smw-unusedproperty-template', $propertyLink, $typeDataValue->getLongHTMLText( $this->getLinker() ) )->text() . ' ' .
+ $this->getMessageFormatter()->getHtml();
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of SMWDIProperty|SMWDIError
+ */
+ function getResults( $requestOptions ) {
+ $this->listLookup = $this->store->getUnusedPropertiesSpecial( $requestOptions );
+ return $this->listLookup->fetchList();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php
new file mode 100644
index 00000000..0daf7608
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/querypages/WantedPropertiesQueryPage.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace SMW;
+
+use Html;
+
+/**
+ * Query class that provides content for the Special:WantedProperties page
+ *
+ * @ingroup QueryPage
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class WantedPropertiesQueryPage extends QueryPage {
+
+ /** @var Store */
+ protected $store;
+
+ /** @var Settings */
+ protected $settings;
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ public function setTitle( $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getName() {
+ return "WantedProperties";
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isExpensive() {
+ return false; /// disables caching for now
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return boolean
+ */
+ function isSyndicated() {
+ return false; ///TODO: why not?
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * Returns available cache information (takes into account user preferences)
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getCacheInfo() {
+
+ if ( $this->listLookup->isFromCache() ) {
+ return $this->msg( 'smw-sp-properties-cache-info', $this->getLanguage()->userTimeAndDate( $this->listLookup->getTimestamp(), $this->getUser() ) )->parse();
+ }
+
+ return '';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @return string
+ */
+ function getPageHeader() {
+
+ $filer = $this->getRequest()->getVal( 'filter', '' );
+
+ if ( $filer !== 'unapprove' ) {
+ $label = $this->msg( 'smw-special-wantedproperties-filter-unapproved' )->text();
+ $title = $this->msg( 'smw-special-wantedproperties-filter-unapproved-desc' )->text();
+ } else {
+ $label = $this->msg( 'smw-special-wantedproperties-filter-none' )->text();
+ $title = '';
+ }
+
+ $filter = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-special-filter'
+ ],
+ $this->msg( 'smw-special-wantedproperties-filter-label' )->text() .
+ '&nbsp;' .
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-special-filter-button',
+ 'title' => $title
+ ],
+ Html::element(
+ 'a',
+ [
+ 'href' => $this->title->getLocalURL( [ 'filter' => $filer !== '' ? '' : 'unapprove' ] ),
+ 'rel' => 'nofollow'
+ ],
+ $label
+ )
+ )
+ );
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-wantedproperties-docu plainlinks' ],
+ $this->msg( 'smw-special-wantedproperties-docu' )->parse()
+ ) . $this->getSearchForm( $this->getRequest()->getVal( 'property' ), $this->getCacheInfo(), false, $filter ) .
+ Html::element(
+ 'h2',
+ [],
+ $this->msg( 'smw-sp-properties-header-label' )->text()
+ );
+ }
+
+ /**
+ * @param $skin
+ * @param array $result First item is SMWDIProperty, second item is int
+ *
+ * @return string
+ */
+ function formatResult( $skin, $result ) {
+ // Only display user-defined properties because it can happen that
+ // custom predefined (fixed) properties are mixed within the result
+ // (did not use their own fixedProperty table and therefore were
+ // selected as well e.g _SF_PDF etc.)
+ if ( !$result[0] instanceof DIProperty || !$result[0]->isUserDefined() ) {
+ return '';
+ }
+
+ $title = $result[0]->getDiWikiPage()->getTitle();
+
+ if ( !$title instanceof \Title ) {
+ return '';
+ }
+
+ $proplink = $this->getLinker()->link(
+ $title,
+ htmlspecialchars( $result[0]->getLabel() ),
+ [],
+ [ 'action' => 'view' ]
+ );
+
+ return $this->msg( 'smw-special-wantedproperties-template' )
+ ->rawParams( $proplink )
+ ->params( $result[1] )
+ ->escaped();
+ }
+
+ /**
+ * Get the list of results.
+ *
+ * @param SMWRequestOptions $requestOptions
+ * @return array of SMWDIProperty|SMWDIError
+ */
+ function getResults( $requestoptions ) {
+ $this->listLookup = $this->store->getWantedPropertiesSpecial( $requestoptions );
+ return $this->listLookup->fetchList();
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php
new file mode 100644
index 00000000..7439d2f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/AggregatablePrinter.php
@@ -0,0 +1,280 @@
+<?php
+
+namespace SMW;
+
+use SMWDataItem;
+use SMWQueryResult;
+
+/**
+ * Abstract class that supports the aggregation and distributive calculation
+ * of numerical data.
+ *
+ * @since 1.9
+ *
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+/**
+ * Abstract class that supports the aggregation and distributive calculation
+ * of numerical data. Supports the distribution parameter, and related
+ * parameters that allows the user to choose between regular behavior or
+ * generating a distribution of values.
+ *
+ * For example, this result set: foo bar baz foo bar bar ohi
+ * Will be turned into
+ * * bar (3)
+ * * foo (2)
+ * * baz (1)
+ * * ohi (1)
+ *
+ * @ingroup QueryPrinter
+ */
+abstract class AggregatablePrinter extends ResultPrinter {
+
+ /**
+ * Create the formats output given the result data and return it.
+ *
+ * @since 1.7
+ *
+ * @param array $data label => value
+ */
+ protected abstract function getFormatOutput( array $data );
+
+ /**
+ * Method gets called right before the result is returned
+ * in case there are values to display. It is meant for
+ * adding resources such as JS and CSS only needed for this
+ * format when it has actual values.
+ *
+ * @since 1.7
+ */
+ protected function addResources() {
+ }
+
+ /**
+ * (non-PHPdoc)
+ * @see SMWResultPrinter::getResultText()
+ */
+ protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
+ $data = $this->getResults( $queryResult, $outputMode );
+
+ if ( $data === [] ) {
+ $queryResult->addErrors( [
+ $this->msg( 'smw-qp-empty-data' )->inContentLanguage()->text()
+ ] );
+ return '';
+ } else {
+ $this->applyDistributionParams( $data );
+ $this->addResources();
+ return $this->getFormatOutput( $data );
+ }
+ }
+
+ /**
+ * Apply the distribution specific parameters.
+ *
+ * @since 1.7
+ *
+ * @param array $data
+ */
+ protected function applyDistributionParams( array &$data ) {
+ if ( $this->params['distributionsort'] == 'asc' ) {
+ asort( $data, SORT_NUMERIC );
+ }
+ elseif ( $this->params['distributionsort'] == 'desc' ) {
+ arsort( $data, SORT_NUMERIC );
+ }
+
+ if ( $this->params['distributionlimit'] !== false ) {
+ $data = array_slice( $data, 0, $this->params['distributionlimit'], true );
+ }
+ }
+
+ /**
+ * Gets and processes the results so they can be fed directly to the
+ * getFormatOutput method. They are returned as an array with the keys
+ * being the labels and the values being their corresponding (numeric) values.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $result
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getResults( SMWQueryResult $result, $outputMode ) {
+ if ( $this->params['distribution'] ) {
+ return $this->getDistributionResults( $result, $outputMode );
+ }
+ else {
+ return $this->getNumericResults( $result, $outputMode );
+ }
+ }
+
+ /**
+ * Counts all the occurrences of all values in the query result,
+ * and returns an array with as key the value and as value the count.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $result
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getDistributionResults( SMWQueryResult $result, $outputMode ) {
+ $values = [];
+
+ while ( /* array of SMWResultArray */ $row = $result->getNext() ) { // Objects (pages)
+ for ( $i = 0, $n = count( $row ); $i < $n; $i++ ) { // SMWResultArray for a sinlge property
+ while ( ( /* SMWDataValue */ $dataValue = $row[$i]->getNextDataValue() ) !== false ) { // Data values
+
+ // Get the HTML for the tag content. Pages are linked, other stuff is just plaintext.
+ if ( $dataValue->getTypeID() == '_wpg' ) {
+ $value = $dataValue->getTitle()->getText();
+ }
+ else {
+ $value = $dataValue->getShortText( $outputMode, $this->getLinker( false ) );
+ }
+
+ if ( !array_key_exists( $value, $values ) ) {
+ $values[$value] = 0;
+ }
+
+ $values[$value]++;
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns an array with the numerical data in the query result.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode
+ *
+ * @return array label => value
+ */
+ protected function getNumericResults( SMWQueryResult $res, $outputMode ) {
+ $values = [];
+
+ // print all result rows
+ while ( $subject = $res->getNext() ) {
+ $dataValue = $subject[0]->getNextDataValue();
+
+ if ( $dataValue !== false ) {
+ $name = $dataValue->getShortWikiText();
+
+ foreach ( $subject as $field ) {
+
+ // Use the aggregation parameter to determine the source of
+ // the number composition
+ if ( $this->params['aggregation'] === 'property' ) {
+ $name = $field->getPrintRequest()->getLabel();
+ }
+
+ // Aggregated array key depends on the mainlabel which is
+ // either the subject or a printout property
+ if ( $this->params['mainlabel'] === '-' ) {
+
+ // In case of '-', getNextDataValue() already shifted the
+ // array forward which means the first column
+ // ( $subject[0] == $field ) contains a possible value
+ // and has to be collected as well
+ if ( ( $subject[0] == $field ) && $dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_NUMBER ) {
+ $value = $dataValue->getDataItem( )->getNumber();
+ $values[$name] = isset( $values[$name] ) ? $values[$name] + $value : $value;
+ }
+ }
+
+ while ( ( /* SMWDataItem */ $dataItem = $field->getNextDataItem() ) !== false ) {
+ $this->addNumbersForDataItem( $dataItem, $values, $name );
+ }
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Adds all numbers contained in a dataitem to the list.
+ *
+ * @since 1.7
+ *
+ * @param SMWDataItem $dataItem
+ * @param array $values
+ * @param string $name
+ */
+ protected function addNumbersForDataItem( SMWDataItem $dataItem, array &$values, $name ) {
+ switch ( $dataItem->getDIType() ) {
+ case SMWDataItem::TYPE_NUMBER:
+ // Collect and aggregate values for the same array key
+ $value = $dataItem->getNumber();
+ if ( !isset( $values[$name] ) ) {
+ $values[$name] = 0;
+ }
+ $values[$name] += $value;
+ break;
+ case SMWDataItem::TYPE_CONTAINER:
+ foreach ( $dataItem->getDataItems() as $di ) {
+ $this->addNumbersForDataItem( $di, $values, $name );
+ }
+ break;
+ default:
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions['distribution'] = [
+ 'name' => 'distribution',
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-distribution',
+ ];
+
+ $definitions['distributionsort'] = [
+ 'name' => 'distributionsort',
+ 'type' => 'string',
+ 'default' => 'none',
+ 'message' => 'smw-paramdesc-distributionsort',
+ 'values' => [ 'asc', 'desc', 'none' ],
+ ];
+
+ $definitions['distributionlimit'] = [
+ 'name' => 'distributionlimit',
+ 'type' => 'integer',
+ 'default' => false,
+ 'manipulatedefault' => false,
+ 'message' => 'smw-paramdesc-distributionlimit',
+ 'lowerbound' => 1,
+ ];
+
+ $definitions['aggregation'] = [
+ 'message' => 'smw-paramdesc-aggregation',
+ 'default' => 'subject',
+ 'values' => [ 'property', 'subject' ],
+ ];
+
+ return $definitions;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php
new file mode 100644
index 00000000..f472132e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/DsvResultPrinter.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace SMW;
+
+use Sanitizer;
+use SMWQueryResult;
+
+/**
+ * Result printer to print results in UNIX-style DSV (deliminter separated value) format.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DsvResultPrinter extends FileExportPrinter {
+
+ protected $separator = ':';
+ protected $fileName = 'result.dsv';
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.6
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+
+ // Do not allow backspaces as delimiter, as they'll break stuff.
+ if ( trim( $params['separator'] ) != '\\' ) {
+ $this->separator = trim( $params['separator'] );
+ }
+
+ $this->fileName = str_replace( ' ', '_', $params['filename'] );
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return 'text/dsv';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return $this->fileName;
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_dsv' )->text();
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $outputMode == SMW_OUTPUT_FILE ) { // Make the DSV file.
+ return $this->getResultFileContents( $res );
+ }
+ else { // Create a link pointing to the DSV file.
+ return $this->getLinkToFile( $res, $outputMode );
+ }
+ }
+
+ /**
+ * Returns the query result in DSV.
+ *
+ * @since 1.6
+ *
+ * @param SMWQueryResult $res
+ *
+ * @return string
+ */
+ protected function getResultFileContents( SMWQueryResult $queryResult ) {
+ $lines = [];
+
+ if ( $this->mShowHeaders ) {
+ $headerItems = [];
+
+ foreach ( $queryResult->getPrintRequests() as $printRequest ) {
+ $headerItems[] = $printRequest->getLabel();
+ }
+
+ $lines[] = $this->getDSVLine( $headerItems );
+ }
+
+ // Loop over the result objects (pages).
+ while ( $row = $queryResult->getNext() ) {
+ $rowItems = [];
+
+ /**
+ * Loop over their fields (properties).
+ * @var SMWResultArray $field
+ */
+ foreach ( $row as $field ) {
+ $itemSegments = [];
+
+ // Loop over all values for the property.
+ while ( ( $object = $field->getNextDataValue() ) !== false ) {
+ $itemSegments[] = Sanitizer::decodeCharReferences( $object->getWikiValue() );
+ }
+
+ // Join all values into a single string, separating them with comma's.
+ $rowItems[] = implode( ',', $itemSegments );
+ }
+
+ $lines[] = $this->getDSVLine( $rowItems );
+ }
+
+ return implode( "\n", $lines );
+ }
+
+ /**
+ * Returns a single DSV line.
+ *
+ * @since 1.6
+ *
+ * @param array $fields
+ *
+ * @return string
+ */
+ protected function getDSVLine( array $fields ) {
+ return implode( $this->separator, array_map( [ $this, 'encodeDSV' ], $fields ) );
+ }
+
+ /**
+ * Encodes a single DSV.
+ *
+ * @since 1.6
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ protected function encodeDSV( $value ) {
+ // TODO
+ // \nnn or \onnn or \0nnn for the character with octal value nnn
+ // \xnn for the character with hexadecimal value nn
+ // \dnnn for the character with decimal value nnn
+ // \unnnn for a hexadecimal Unicode literal.
+ return str_replace(
+ [ '\n', '\r', '\t', '\b', '\f', '\\', $this->separator ],
+ [ "\n", "\r", "\t", "\b", "\f", '\\\\', "\\$this->separator" ],
+ $value
+ );
+ }
+
+ /**
+ * Returns html for a link to a query that returns the DSV file.
+ *
+ * @since 1.6
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode
+ *
+ * @return string
+ */
+ protected function getLinkToFile( SMWQueryResult $res, $outputMode ) {
+ // yes, our code can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = ( $outputMode == SMW_OUTPUT_HTML );
+ return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( wfMessage( 'smw_dsv_link' )->text() );
+
+ $params['limit']->setDefault( 100 );
+
+ $params[] = [
+ 'name' => 'separator',
+ 'message' => 'smw-paramdesc-dsv-separator',
+ 'default' => $this->separator,
+ 'aliases' => 'sep',
+ ];
+
+ $params[] = [
+ 'name' => 'filename',
+ 'message' => 'smw-paramdesc-dsv-filename',
+ 'default' => $this->fileName,
+ ];
+
+ return $params;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php
new file mode 100644
index 00000000..d067a255
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/EmbeddedResultPrinter.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW;
+
+use SMWQueryResult;
+use Title;
+
+/**
+ * Printer for embedded data.
+ *
+ * Embeds in the page output the contents of the pages in the query result set.
+ * Printouts are ignored: it only matters which pages were returned by the query.
+ * The optional "titlestyle" formatting parameter can be used to apply a format to
+ * the headings for the page titles. If "titlestyle" is not specified, a <h1> tag is
+ * used.
+ *
+ * @license GNU GPL v2+
+ * @since 1.7
+ *
+ * @author Fernando Correia
+ * @author Markus Krötzsch
+ */
+class EmbeddedResultPrinter extends ResultPrinter {
+
+ protected $m_showhead;
+ protected $m_embedformat;
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.7
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+
+ $this->m_showhead = !$params['embedonly'];
+ $this->m_embedformat = $params['embedformat'];
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_embedded' )->text();
+ }
+
+ /**
+ * @see ResultPrinter::isDeferrable
+ *
+ * {@inheritDoc}
+ */
+ public function isDeferrable() {
+ return true;
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+
+ // Ensure that there is an annotation block in place before starting the
+ // parse and transclution process. Unfortunately we are unable to block
+ // the inclusion of categories which are attached to a MediaWiki
+ // object we have no immediate access or control.
+ $this->transcludeAnnotation = false;
+
+ global $wgParser;
+ // No page should embed itself, find out who we are:
+ if ( $wgParser->getTitle() instanceof Title ) {
+ $title = $wgParser->getTitle()->getPrefixedText();
+ } else { // this is likely to be in vain -- this case is typical if we run on special pages
+ global $wgTitle;
+ $title = $wgTitle->getPrefixedText();
+ }
+
+ // print header
+ $result = '';
+ $footer = '';
+ $embstart = '';
+ $embend = '';
+ $headstart = '';
+ $headend = '';
+ $this->hasTemplates = true;
+
+ switch ( $this->m_embedformat ) {
+ case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
+ $headstart = '<' . $this->m_embedformat . '>';
+ $headend = '</' . $this->m_embedformat . ">\n";
+ break;
+ case 'ul': case 'ol':
+ $result .= '<' . $this->m_embedformat . '>';
+ $footer = '</' . $this->m_embedformat . '>';
+ $embstart = '<li>';
+ $headend = "<br />\n";
+ $embend = "</li>\n";
+ break;
+ }
+
+ // Print all result rows:
+ foreach ( $res->getResults() as $diWikiPage ) {
+ if ( $diWikiPage instanceof DIWikiPage ) { // ensure that we deal with title-likes
+ $dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null );
+ $result .= $embstart;
+
+ if ( $this->m_showhead ) {
+ $result .= $headstart . $dvWikiPage->getLongWikiText( $this->mLinker ) . $headend;
+ }
+
+ if ( $dvWikiPage->getLongWikiText() != $title ) {
+ if ( $diWikiPage->getNamespace() == NS_MAIN ) {
+ $result .= '{{:' . $diWikiPage->getDBkey() . '}}';
+ } else {
+ $result .= '{{' . $dvWikiPage->getLongWikiText() . '}}';
+ }
+ } else { // block recursion
+ $result .= '<b>' . $dvWikiPage->getLongWikiText() . '</b>';
+ }
+
+ $result .= $embend;
+ }
+ }
+
+ // show link to more results
+ if ( $this->linkFurtherResults( $res ) ) {
+ $result .= $embstart
+ . $this->getFurtherResultsLink( $res, $outputMode )->getText( SMW_OUTPUT_WIKI, $this->mLinker )
+ . $embend;
+ }
+
+ $result .= $footer;
+
+ return $result;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions[] = [
+ 'name' => 'embedformat',
+ 'message' => 'smw-paramdesc-embedformat',
+ 'default' => 'h1',
+ 'values' => [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul' ],
+ ];
+
+ $definitions[] = [
+ 'name' => 'embedonly',
+ 'type' => 'boolean',
+ 'message' => 'smw-paramdesc-embedonly',
+ 'default' => false,
+ ];
+
+
+ return $definitions;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php
new file mode 100644
index 00000000..990e11d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/JsonResultPrinter.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace SMW;
+
+use FormatJson;
+use SMWQueryResult;
+
+/**
+ * Print links to JSON files representing query results.
+ *
+ * @see http://www.semantic-mediawiki.org/wiki/Help:JSON_format
+ *
+ * @since 1.5.3
+ *
+ *
+ * @license GNU GPL v2 or later
+ * @author mwjames
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Fabian Howahl
+ */
+
+/**
+ * Print links to JSON files representing query results.
+ *
+ * @ingroup QueryPrinter
+ */
+class JsonResultPrinter extends FileExportPrinter {
+
+ /**
+ * Returns human readable label for this printer
+ * @codeCoverageIgnore
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->msg( 'smw_printername_json' )->text();
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return 'application/json';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+
+ if ( $this->getSearchLabel( SMW_OUTPUT_WIKI ) !== '' ) {
+ return str_replace( ' ', '_', $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) . '.json';
+ }
+
+ return 'result.json';
+ }
+
+ /**
+ * Returns a filename that is to be sent to the caller
+ *
+ * @param SMWQueryResult $res
+ * @param $outputMode integer
+ *
+ * @return string
+ */
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+
+ if ( $outputMode == SMW_OUTPUT_FILE ) {
+
+ // No results, just bailout
+ if ( $res->getCount() == 0 ){
+ return $this->params['default'] !== '' ? $this->params['default'] : '';
+ }
+
+ $flags = $this->params['prettyprint'] ? JSON_PRETTY_PRINT : 0;
+ $flags = $flags | ( $this->params['unescape'] ? JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES : 0 );
+
+ // Serialize queryResult
+ if ( isset( $this->params['type'] ) && $this->params['type'] === 'simple' ) {
+ $result = $this->serializeAsSimpleList( $res );
+ } else {
+ $result = array_merge(
+ $res->serializeToArray(),
+ [ 'rows' => $res->getCount() ]
+ );
+ }
+
+ $result = json_encode(
+ $result,
+ $flags
+ );
+
+ } else {
+ // Create a link that points to the JSON file
+ $result = $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+
+ // Code can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = $outputMode == SMW_OUTPUT_HTML;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ * @codeCoverageIgnore
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( $this->msg( 'smw_json_link' )->inContentLanguage()->text() );
+
+ $params['limit']->setDefault( 100 );
+
+ $params['type'] = [
+ 'values' => [ 'simple', 'full' ],
+ 'default' => 'full',
+ 'message' => 'smw-paramdesc-json-type',
+ ];
+
+ $params['prettyprint'] = [
+ 'type' => 'boolean',
+ 'default' => '',
+ 'message' => 'smw-paramdesc-prettyprint',
+ ];
+
+ $params['unescape'] = [
+ 'type' => 'boolean',
+ 'default' => '',
+ 'message' => 'smw-paramdesc-json-unescape',
+ ];
+
+ return $params;
+ }
+
+ private function serializeAsSimpleList( $res ) {
+
+ $result = [];
+
+ while ( $row = $res->getNext() ) {
+ $item = [];
+ $subject = '';
+
+ foreach ( $row as /* SMWResultArray */ $field ) {
+ $label = $field->getPrintRequest()->getLabel();
+
+ if ( $label === '' ) {
+ continue;
+ }
+
+ $values = [];
+ $subject = $field->getResultSubject()->getHash();
+
+ while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
+ $values[] = $dataValue->getWikiValue();
+ }
+
+ $item[$label] = $values;
+ }
+
+ if ( $this->params['mainlabel'] === '-' || $subject === '' ) {
+ $result[] = $item;
+ } else {
+ $result[$subject] = $item;
+ }
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php
new file mode 100644
index 00000000..1e975162
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RawResultPrinter.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW;
+
+use FormatJson;
+use Html;
+use SMWOutputs;
+use SMWQueryResult;
+
+/**
+ * Base class for result printers that use the serialized results
+ *
+ * @since 1.9
+ *
+ * @license GNU GPL v2 or later
+ * @author mwjames
+ */
+abstract class RawResultPrinter extends ResultPrinter {
+
+ /**
+ * Returns html output.
+ *
+ * @since 1.9
+ */
+ abstract protected function getHtml( array $data );
+
+ /**
+ * Convenience method to register resources
+ *
+ * @since 1.9
+ *
+ * @param string $resource
+ */
+ protected function addResources( $resource ) {
+ SMWOutputs::requireResource( $resource );
+ }
+
+ /**
+ * Convenience method to create a unique id
+ *
+ * @since 1.9
+ */
+ protected function getId( ) {
+ return 'smw-' . uniqid();
+ }
+
+ /**
+ * Convenience method generating a visual placeholder before any
+ * JS is registered to indicate that resources (JavaScript, CSS)
+ * are being loaded and once ready ensure to set
+ * ( '.smw-spinner' ).hide()
+ *
+ * @since 1.9
+ */
+ protected function createLoadingHtmlPlaceholder() {
+ $this->addResources( 'ext.smw.style' );
+
+ return Html::rawElement(
+ 'div',
+ [ 'class' => 'smw-spinner left mw-small-spinner' ],
+ Html::element(
+ 'p',
+ [ 'class' => 'text' ],
+ $this->msg( 'smw-livepreview-loading' )->inContentLanguage()->text()
+ )
+ );
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ protected function loading() {
+ return $this->createLoadingHtmlPlaceholder();
+ }
+
+ /**
+ * Convenience method to encode output data
+ *
+ * @since 1.9
+ *
+ * @param string $id
+ * @param array $data
+ */
+ protected function encodeToJsonForId( $id, $data ) {
+ SMWOutputs::requireHeadItem(
+ $id,
+ $this->getSkin()->makeVariablesScript( [ $id => FormatJson::encode( $data ) ]
+ ) );
+
+ return $this;
+ }
+
+ /**
+ * @deprecated since 2.0
+ */
+ protected function encode( $id, $data ) {
+ return $this->encodeToJsonForId( $id, $data );
+ }
+
+ /**
+ * Returns serialised content
+ *
+ * @see SMWResultPrinter::getResultText()
+ *
+ * @param SMWQueryResult $queryResult
+ * @param $outputMode
+ *
+ * @return string
+ */
+ protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
+
+ // Add parameters that are only known to the specific printer
+ $ask = $queryResult->getQuery()->toArray();
+ foreach ( $this->params as $key => $value ) {
+ if ( is_string( $value ) || is_integer( $value ) || is_bool( $value ) ) {
+ $ask['parameters'][$key] = $value;
+ }
+ }
+
+ // Combine all data into one object
+ $data = [
+ 'query' => [
+ 'result' => $queryResult->toArray(),
+ 'ask' => $ask
+ ]
+ ];
+
+ return $this->getHtml( $data );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php
new file mode 100644
index 00000000..24006dbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/queryprinters/RdfResultPrinter.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW;
+
+use SMW\Query\PrintRequest;
+use SMWExporter;
+use SMWQueryResult;
+use SMWRDFXMLSerializer;
+use SMWTurtleSerializer;
+
+/**
+ * Printer class for generating RDF output
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class RdfResultPrinter extends FileExportPrinter {
+
+ /**
+ * The syntax to be used for export. May be 'rdfxml' or 'turtle'.
+ */
+ protected $syntax;
+
+ /**
+ * @see SMWResultPrinter::handleParameters
+ *
+ * @since 1.7
+ *
+ * @param array $params
+ * @param $outputmode
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+ $this->syntax = $params['syntax'];
+ }
+
+ /**
+ * @see SMWIExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( SMWQueryResult $queryResult ) {
+ return $this->syntax == 'turtle' ? 'application/x-turtle' : 'application/xml';
+ }
+
+ /**
+ * @see SMWIExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return $this->syntax == 'turtle' ? 'result.ttl' : 'result.rdf';
+ }
+
+ public function getName() {
+ return wfMessage( 'smw_printername_rdf' )->text();
+ }
+
+ protected function getResultText( SMWQueryResult $res, $outputMode ) {
+ if ( $outputMode == SMW_OUTPUT_FILE ) { // make RDF file
+ $serializer = $this->syntax == 'turtle' ? new SMWTurtleSerializer() : new SMWRDFXMLSerializer();
+ $serializer->startSerialization();
+ $serializer->serializeExpData( SMWExporter::getInstance()->getOntologyExpData( '' ) );
+
+ while ( $row = $res->getNext() ) {
+ $subjectDi = reset( $row )->getResultSubject();
+ $data = SMWExporter::getInstance()->makeExportDataForSubject( $subjectDi );
+
+ foreach ( $row as $resultarray ) {
+ $printreq = $resultarray->getPrintRequest();
+ $property = null;
+
+ switch ( $printreq->getMode() ) {
+ case PrintRequest::PRINT_PROP:
+ $property = $printreq->getData()->getDataItem();
+ break;
+ case PrintRequest::PRINT_CATS:
+ $property = new DIProperty( '_TYPE' );
+ break;
+ case PrintRequest::PRINT_CCAT:
+ // not serialised right now
+ break;
+ case PrintRequest::PRINT_THIS:
+ // ignored here (object is always included in export)
+ break;
+ }
+
+ if ( !is_null( $property ) ) {
+ SMWExporter::getInstance()->addPropertyValues( $property, $resultarray->getContent(), $data, $subjectDi );
+ }
+ }
+ $serializer->serializeExpData( $data );
+ }
+
+ $serializer->finishSerialization();
+
+ return $serializer->flushContent();
+ } else { // just make link to feed
+ $this->isHTML = ( $outputMode == SMW_OUTPUT_HTML ); // yes, our code can be viewed as HTML if requested, no more parsing needed
+
+ return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
+ }
+ }
+
+ /**
+ * @see SMWResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions['limit']->setDefault( 100 );
+
+ $definitions['searchlabel']->setDefault( wfMessage( 'smw_rdf_link' )->inContentLanguage()->text() );
+
+ $definitions[] = [
+ 'name' => 'syntax',
+ 'message' => 'smw-paramdesc-rdfsyntax',
+ 'values' => [ 'rdfxml', 'turtle' ],
+ 'default' => 'rdfxml',
+ ];
+
+ return $definitions;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php
new file mode 100644
index 00000000..06884ffd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialOWLExport.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * This special page (Special:ExportRDF) for MediaWiki implements an OWL-export of semantic data,
+ * gathered both from the annotations in articles, and from metadata already
+ * present in the database.
+ *
+ * @ingroup SMWSpecialPage
+ * @ingroup SpecialPage
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class SMWSpecialOWLExport extends SpecialPage {
+
+ /// Export controller object to be used for serializing data
+ protected $export_controller;
+
+ public function __construct() {
+ parent::__construct( 'ExportRDF' );
+ }
+
+ public function execute( $page ) {
+ $this->setHeaders();
+ global $wgOut, $wgRequest;
+
+ $wgOut->setPageTitle( wfMessage( 'exportrdf' )->text() );
+
+ // see if we can find something to export:
+ $page = is_null( $page ) ? $wgRequest->getVal( 'page' ) : rawurldecode( $page );
+ $pages = false;
+
+ if ( !is_null( $page ) || $wgRequest->getCheck( 'page' ) ) {
+ $page = is_null( $page ) ? $wgRequest->getCheck( 'text' ) : $page;
+
+ if ( $page !== '' ) {
+ $pages = [ $page ];
+ }
+ }
+
+ if ( $pages === false && $wgRequest->getCheck( 'pages' ) ) {
+ $pageBlob = $wgRequest->getText( 'pages' );
+
+ if ( $pageBlob !== '' ) {
+ $pages = explode( "\n", $wgRequest->getText( 'pages' ) );
+ }
+ }
+
+ if ( $pages !== false ) {
+ $this->exportPages( $pages );
+ return;
+ } else {
+ $offset = $wgRequest->getVal( 'offset' );
+
+ if ( isset( $offset ) ) {
+ $this->startRDFExport();
+ $this->export_controller->printPageList( $offset );
+ return;
+ } else {
+ $stats = $wgRequest->getVal( 'stats' );
+
+ if ( isset( $stats ) ) {
+ $this->startRDFExport();
+ $this->export_controller->printWikiInfo();
+ return;
+ }
+ }
+ }
+
+ // Nothing exported yet; show user interface:
+ $this->showForm();
+ }
+
+ /**
+ * Create the HTML user interface for this special page.
+ */
+ protected function showForm() {
+ global $wgOut, $wgUser, $smwgAllowRecursiveExport, $smwgExportBacklinks, $smwgExportAll;
+
+ $html = '<form name="tripleSearch" action="" method="POST">' . "\n" .
+ '<p>' . wfMessage( 'smw_exportrdf_docu' )->text() . "</p>\n" .
+ '<input type="hidden" name="postform" value="1"/>' . "\n" .
+ '<textarea name="pages" cols="40" rows="10"></textarea><br />' . "\n";
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgAllowRecursiveExport ) {
+ $html .= '<input type="checkbox" name="recursive" value="1" id="rec">&#160;<label for="rec">' . wfMessage( 'smw_exportrdf_recursive' )->text() . '</label></input><br />' . "\n";
+ }
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgExportBacklinks ) {
+ $html .= '<input type="checkbox" name="backlinks" value="1" default="true" id="bl">&#160;<label for="bl">' . wfMessage( 'smw_exportrdf_backlinks' )->text() . '</label></input><br />' . "\n";
+ }
+
+ if ( $wgUser->isAllowed( 'delete' ) || $smwgExportAll ) {
+ $html .= '<br />';
+ $html .= '<input type="text" name="date" value="' . date( DATE_W3C, mktime( 0, 0, 0, 1, 1, 2000 ) ) . '" id="date">&#160;<label for="ea">' . wfMessage( 'smw_exportrdf_lastdate' )->text() . '</label></input><br />' . "\n";
+ }
+
+ $html .= '<br /><input type="submit" value="' . wfMessage( 'smw_exportrdf_submit' )->text() . "\"/>\n</form>";
+
+ $wgOut->addHTML( $html );
+ }
+
+ /**
+ * Prepare $wgOut for printing non-HTML data.
+ */
+ protected function startRDFExport() {
+ global $wgOut, $wgRequest;
+
+ $syntax = $wgRequest->getText( 'syntax' );
+
+ if ( $syntax === '' ) {
+ $syntax = $wgRequest->getVal( 'syntax' );
+ }
+
+ $wgOut->disable();
+ ob_start();
+
+ if ( $syntax == 'turtle' ) {
+ $mimetype = 'application/x-turtle'; // may change to 'text/turtle' at some time, watch Turtle development
+ $serializer = new SMWTurtleSerializer();
+ } else { // rdfxml as default
+ // Only use rdf+xml mimetype if explicitly requested (browsers do
+ // not support it by default).
+ // We do not add this parameter to RDF links within the export
+ // though; it is only meant to help some tools to see that HTML
+ // included resources are RDF (from there on they should be fine).
+ $mimetype = ( $wgRequest->getVal( 'xmlmime' ) == 'rdf' ) ? 'application/rdf+xml' : 'application/xml';
+ $serializer = new SMWRDFXMLSerializer();
+ }
+
+ header( "Content-type: $mimetype; charset=UTF-8" );
+
+ $this->export_controller = new SMWExportController( $serializer );
+ }
+
+ /**
+ * Export the given pages to RDF.
+ * @param array $pages containing the string names of pages to be exported
+ */
+ protected function exportPages( $pages ) {
+ global $wgRequest, $smwgExportBacklinks, $wgUser, $smwgAllowRecursiveExport;
+
+ // Effect: assume "no" from missing parameters generated by checkboxes.
+ $postform = $wgRequest->getText( 'postform' ) == 1;
+
+ $recursive = 0; // default, no recursion
+ $rec = $wgRequest->getText( 'recursive' );
+
+ if ( $rec === '' ) {
+ $rec = $wgRequest->getVal( 'recursive' );
+ }
+
+ if ( ( $rec == '1' ) && ( $smwgAllowRecursiveExport || $wgUser->isAllowed( 'delete' ) ) ) {
+ $recursive = 1; // users may be allowed to switch it on
+ }
+
+ $backlinks = $smwgExportBacklinks; // default
+ $bl = $wgRequest->getText( 'backlinks' );
+
+ if ( $bl === '' ) {
+ // TODO: wtf? this does not make a lot of sense...
+ $bl = $wgRequest->getVal( 'backlinks' );
+ }
+
+ if ( ( $bl == '1' ) && ( $wgUser->isAllowed( 'delete' ) ) ) {
+ $backlinks = true; // admins can always switch on backlinks
+ } elseif ( ( $bl == '0' ) || ( '' == $bl && $postform ) ) {
+ $backlinks = false; // everybody can explicitly switch off backlinks
+ }
+
+ $date = $wgRequest->getText( 'date' );
+ if ( $date === '' ) {
+ $date = $wgRequest->getVal( 'date' );
+ }
+
+ if ( $date !== '' ) {
+ $timeint = strtotime( $date );
+ $stamp = date( "YmdHis", $timeint );
+ $date = $stamp;
+ }
+
+ // If it is a redirect then we don't want to generate triples other than
+ // the redirect target information
+ if ( isset( $pages[0] ) && ( $title = Title::newFromText( $pages[0] ) ) !== null && $title->isRedirect() ) {
+ $backlinks = false;
+ }
+
+ $this->startRDFExport();
+ $this->export_controller->enableBacklinks( $backlinks );
+ $this->export_controller->printPages( $pages, $recursive, $date );
+ }
+
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php
new file mode 100644
index 00000000..1fe7ad8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SMW_SpecialTypes.php
@@ -0,0 +1,387 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\Utils\HtmlTabs;
+use SMW\Page\ListPager;
+use SMW\Utils\HtmlColumns;
+use SMWDataItem as DataItem;
+use SMW\MediaWiki\Collator;
+use SMW\TypesRegistry;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMWInfolink as Infolink;
+use SMW\DataValues\TypesValue;
+use SMWErrorValue as ErrorValue;
+use SMW\Page\ListBuilder;
+
+/**
+ * This special page for MediaWiki provides information about available types
+ * and those related properties.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class SMWSpecialTypes extends SpecialPage {
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function __construct() {
+ parent::__construct( 'Types' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+ $out = $this->getOutput();
+
+ $out->addModuleStyles( 'ext.smw.page.styles' );
+ $out->addModules( 'smw.property.page' );
+
+ $params = Infolink::decodeParameters( $param, false );
+ $typeLabel = reset( $params );
+
+ if ( $typeLabel == false ) {
+ $out->setPageTitle( wfMessage( 'types' )->text() );
+ $html = $this->getTypesList();
+ } else {
+ $typeLabel = str_replace( '%', '-', $typeLabel );
+ $typeName = str_replace( '_', ' ', $typeLabel );
+ $out->setPageTitle( wfMessage( 'smw-types-title', $typeName )->text() );
+ $out->prependHTML( $this->getBreadcrumbLink() );
+ $html = $this->getPropertiesByType( $typeLabel );
+ }
+
+ $out->addHTML( $html );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataValue $dataValue
+ * @param Link $linker
+ *
+ * @return string
+ */
+ public function formatItem( $dataValue, $linker ) {
+
+ // Outdated property? Predefined property definition no longer exists?
+ if ( $dataValue->getDataItem()->getInterwiki() === SMW_SQL3_SMWIW_OUTDATED ) {
+ $dataItem = $dataValue->getDataItem();
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIWikiPage( $dataItem->getDBKey(), SMW_NS_PROPERTY ),
+ null
+ );
+
+ $dataValue->setOption( $dataValue::NO_TEXT_TRANSFORMATION, true );
+ $dataValue->setOption( $dataValue::SHORT_FORM, true );
+
+ return $dataValue->getWikiValue();
+ }
+
+ $searchlink = Infolink::newBrowsingLink(
+ '+', $dataValue->getWikiValue()
+ );
+
+ return $dataValue->getLongHTMLText( $linker ) . '&#160;' . $searchlink->getHTML( $linker );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'pages';
+ }
+
+ private function getTypesList() {
+
+ $this->addHelpLink( wfMessage( 'smw-specials-types-helplink' )->escaped(), true );
+
+ $typeLabels = DataTypeRegistry::getInstance()->getKnownTypeLabels();
+ $linker = smwfGetLinker();
+ asort( $typeLabels, SORT_STRING );
+
+ $primitive = TypesRegistry::getTypesByGroup( 'primitive' );
+ $compound = TypesRegistry::getTypesByGroup( 'compound' );
+
+ $pr_text = wfMessage( 'smw-type-primitive' )->text();
+ $cx_text = wfMessage( 'smw-type-contextual' )->text();
+ $cp_text = wfMessage( 'smw-type-compound' )->text();
+
+ $contents = [
+ $pr_text => [],
+ $cx_text => [],
+ $cp_text => []
+ ];
+
+ foreach ( $typeLabels as $typeId => $label ) {
+ $typeValue = TypesValue::newFromTypeId( $typeId );
+ $msgKey = 'smw-type' . str_replace( '_', '-', strtolower( $typeId ) );
+
+ $text = $typeValue->getLongHTMLText( $linker );
+
+ if ( wfMessage( $msgKey )->exists() ) {
+ $msg = wfMessage( $msgKey, '' )->parse();
+ $text .= Html::rawElement(
+ 'span' ,
+ [
+ 'class' => 'plainlinks',
+ 'style' => 'font-size:85%'
+ ],
+
+ // Remove the first two chars which are a localized
+ // diacritical, quotation mark
+ str_replace( mb_substr( $msg, 0, 2 ), '', $msg )
+ );
+ }
+
+ if ( isset( $primitive[$typeId] ) ) {
+ $contents[$pr_text][] = $typeValue->getLongHTMLText( $linker );
+ } elseif ( isset( $compound[$typeId] ) ) {
+ $contents[$cp_text][] = $text;
+ } else {
+ $contents[$cx_text][] = $text;
+ }
+ }
+
+ $htmlColumns = new HtmlColumns();
+ $htmlColumns->setColumnClass( 'smw-column-responsive' );
+
+ $htmlColumns->setContinueAbbrev(
+ wfMessage( 'listingcontinuesabbrev' )->text()
+ );
+
+ $htmlColumns->setColumns( 2 );
+ $htmlColumns->addContents( $contents, HtmlColumns::INDX_CONTENT );
+ $html = $htmlColumns->getHtml();
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'types' );
+
+ $htmlTabs->tab( 'smw-type-list', $this->msg( 'smw-type-tab-types' ) );
+ $htmlTabs->content( 'smw-type-list', "<div class='smw-page-navigation'>$html</div>" );
+
+ $html = $htmlTabs->buildHTML(
+ [
+ 'id' => 'smw-types',
+ 'class' => 'smw-types'
+ ]
+ );
+
+ return Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks smw-types-intro'
+ ],
+ wfMessage( 'smw_types_docu' )->parse()
+ ) . $html;
+ }
+
+ private function getPropertiesByType( $typeLabel ) {
+
+ $typeValue = DataValueFactory::getInstance()->newTypeIDValue(
+ TypesValue::TYPE_ID,
+ $typeLabel
+ );
+
+ if ( !$typeValue->isValid() ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'plainlinks smw-type-unknown'
+ ],
+ $this->msg( 'smw-special-types-no-such-type', $typeLabel )->escaped()
+ );
+ }
+
+ $this->addHelpLink( wfMessage( 'smw-specials-bytype-helplink', $typeLabel )->escaped(), true );
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $pagingLimit = $applicationFactory->getSettings()->dotGet( 'smwgPagingLimit.type' );
+
+ // not too useful, but we comply to this request
+ if ( $pagingLimit <= 0 ) {
+ return '';
+ }
+
+ $limit = $this->getRequest()->getVal( 'limit', $pagingLimit );
+ $offset = $this->getRequest()->getVal( 'offset', 0 );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->setLimit( $limit + 1 );
+ $requestOptions->setOffset( $offset );
+
+ $dataItems = $applicationFactory->getStore()->getPropertySubjects(
+ new DIProperty( '_TYPE' ),
+ $typeValue->getDataItem(),
+ $requestOptions
+ );
+
+ if ( $dataItems instanceof \Iterator ) {
+ $dataItems = iterator_to_array( $dataItems );
+ }
+
+ if ( !$requestOptions->ascending ) {
+ $dataItems = array_reverse( $dataItems );
+ }
+
+ $typeId = $typeValue->getDataItem()->getFragment();
+
+ $dataValue = DataValueFactory::getInstance()->newTypeIDValue(
+ $typeId
+ );
+
+ $label = htmlspecialchars( $typeValue->getWikiValue() );
+ $typeKey = 'smw-type' . str_replace( '_', '-', strtolower( $typeId ) );
+ $msgKey = wfMessage( $typeKey )->exists() ? $typeKey : 'smw-types-default';
+
+ $result = \Html::rawElement(
+ 'div',
+ [
+ 'class' => 'plainlinks smw-types-intro '. $typeKey
+ ],
+ wfMessage( $msgKey, str_replace( '_', ' ', $label ) )->parse() .
+ $this->find_extras( $dataValue, $typeId, $label )
+ );
+
+ $count = count( $dataItems );
+
+ if ( $count == 0 ) {
+ return $result;
+ }
+
+ $html = Html::rawElement(
+ 'div' ,
+ [
+ 'class' => 'smw-page-navigation'
+ ],
+ ListPager::pagination(
+ $this->getTitleFor( 'Types', $typeLabel ),
+ $limit,
+ $offset,
+ $count,
+ [ '_target' => '#smw-list' ]
+ ) . Html::rawElement(
+ 'div' ,
+ [
+ 'class' => 'smw-page-nav-note'
+ ],
+ wfMessage( 'smw_typearticlecount' )->numParams( min( $limit, $count ) )->text()
+ )
+ );
+
+ $listBuilder = new ListBuilder(
+ ApplicationFactory::getInstance()->getStore()
+ );
+
+ $listBuilder->setItemFormatter( [ $this, 'formatItem' ] );
+
+ $html .= $listBuilder->getColumnList(
+ $dataItems
+ );
+
+ $errors = $this->find_errors( $dataValue, $typeId, $label );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'type' );
+
+ $htmlTabs->setActiveTab(
+ $errors !== null ? 'smw-type-errors' : 'smw-type-list'
+ );
+
+ $htmlTabs->tab( 'smw-type-list', $this->msg( 'smw-type-tab-properties' ) );
+ $htmlTabs->content( 'smw-type-list', "<div>$html</div>" );
+
+ $htmlTabs->tab(
+ 'smw-type-errors',
+ $this->msg( 'smw-type-tab-errors' ),
+ [
+ 'hide' => $errors === null,
+ 'class' => 'smw-tab-warning'
+ ]
+ );
+
+ $htmlTabs->content( 'smw-type-errors', "<div>$errors</div>" );
+
+ return $result . $htmlTabs->buildHTML(
+ [
+ 'id' => 'smw-list',
+ 'class' => 'smw-types'
+ ]
+ );
+ }
+
+ private function find_errors( $dataValue, $typeId, $label ) {
+
+ $errors = [];
+
+ if ( $typeId === '_geo' && $dataValue instanceof ErrorValue ) {
+ $errors[] = Html::rawElement(
+ 'li',
+ [],
+ wfMessage( 'smw-types-extra-geo-not-available', $label )->parse()
+ );
+ }
+
+ if ( $errors !== [] ) {
+ return Html::rawElement(
+ 'ul',
+ [
+ 'class' => 'smw-page-content plainlinks'
+ ],
+ implode( '', $errors)
+ );
+ }
+ }
+
+ private function find_extras( $dataValue, $typeId, $label ) {
+
+ $html = '';
+
+ if ( $typeId === '_mlt_rec' ) {
+ $option = $dataValue->hasFeature( SMW_DV_MLTV_LCODE ) ? 1 : 2;
+ $html = '&nbsp;' . wfMessage( 'smw-types-extra-mlt-lcode', $label, $option )->parse();
+ }
+
+ $extra = 'smw-type-extra' . str_replace( '_', '-', $typeId );
+
+ if ( wfMessage( $extra )->exists() ) {
+ $html = '&nbsp;' . wfMessage( $extra )->parse();
+ }
+
+ return $html;
+ }
+
+ private function getBreadcrumbLink() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-breadcrumb-link'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-breadcrumb-arrow-right'
+ ]
+ ) .
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => SpecialPage::getTitleFor( 'Types')->getFullURL()
+ ],
+ $this->msg( 'types' )->escaped()
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php
new file mode 100644
index 00000000..9e5bea44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialConcepts.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\Page\ListPager;
+use SMW\Page\ListBuilder;
+use SMW\SQLStore\SQLStore;
+use SMW\Utils\HtmlTabs;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Collator;
+
+/**
+ * Special page that lists available concepts
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SpecialConcepts extends \SpecialPage {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @see SpecialPage::__construct
+ */
+ public function __construct() {
+ parent::__construct( 'Concepts' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+
+ $this->setHeaders();
+ $out = $this->getOutput();
+ $out->addModuleStyles( 'ext.smw.page.styles' );
+
+ $limit = $this->getRequest()->getVal( 'limit', 50 );
+ $offset = $this->getRequest()->getVal( 'offset', 0 );
+
+ $this->store = ApplicationFactory::getInstance()->getStore();
+
+ $diWikiPages = $this->fetchFromTable( $limit, $offset );
+ $html = $this->getHtml( $diWikiPages, $limit, $offset );
+
+ $this->addHelpLink( wfMessage( 'smw-helplink-concepts' )->escaped(), true );
+
+ $out->setPageTitle( $this->msg( 'concepts' )->text() );
+ $out->addHTML( $html );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return DIWikiPage[]
+ */
+ public function fetchFromTable( $limit, $offset ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $results = [];
+
+ $fields = [
+ 'smw_id',
+ 'smw_title'
+ ];
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_CONCEPT,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_proptable_hash IS NOT NULL',
+ 'concept_features > 0'
+ ];
+
+ $options = [
+ 'LIMIT' => $limit + 1,
+ 'OFFSET' => $offset,
+ ];
+
+ $res = $connection->select(
+ [
+ $connection->tableName( SQLStore::ID_TABLE ),
+ $connection->tableName( SQLStore::CONCEPT_TABLE )
+ ],
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options,
+ [
+ $connection->tableName( SQLStore::ID_TABLE ) => [ 'INNER JOIN', [ 'smw_id=s_id' ] ]
+ ]
+ );
+
+ foreach ( $res as $row ) {
+ $results[] = new DIWikiPage( $row->smw_title, SMW_NS_CONCEPT );
+ }
+
+ return $results;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param DIWikiPage[] $dataItems
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return string
+ */
+ public function getHtml( $dataItems, $limit, $offset ) {
+
+ if ( $this->store === null ) {
+ $this->store = ApplicationFactory::getInstance()->getStore();
+ }
+
+ $count = count( $dataItems );
+ $resultNumber = min( $limit, $count );
+
+ if ( $resultNumber == 0 ) {
+ $key = 'smw-special-concept-empty';
+ } else {
+ $key = 'smw-special-concept-count';
+ }
+
+ $listBuilder = new ListBuilder(
+ $this->store,
+ Collator::singleton()
+ );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'concept' );
+
+ $html = Html::rawElement(
+ 'div',
+ [ 'id' => 'mw-pages'],
+ Html::rawElement(
+ 'div',
+ [ 'class' => 'smw-page-navigation' ],
+ ListPager::pagination( $this->getPageTitle(), $limit, $offset, $count )
+ ) . Html::element(
+ 'div',
+ [ 'class' => $key, 'style' => 'margin-top:10px;margin-bottom:10px;' ],
+ $this->msg( $key, $resultNumber )->parse()
+ ) . $listBuilder->getColumnList( $dataItems )
+ );
+
+ $htmlTabs->tab( 'smw-concept-list', $this->msg( 'smw-concept-tab-list' ) );
+ $htmlTabs->content( 'smw-concept-list', $html );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-concept clearfix' ]
+ );
+
+ return Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-special-concept-docu plainlinks' ],
+ $this->msg( 'smw-special-concept-docu' )->parse()
+ ) . $html;
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'pages';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php
new file mode 100644
index 00000000..5bc84de0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialPage.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Semantic MediaWiki SpecialPage base class
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Semantic MediaWiki SpecialPage base class
+ *
+ * @ingroup SpecialPage
+ * @codeCoverageIgnore
+ */
+class SpecialPage extends \SpecialPage {
+
+ /** @var Store */
+ protected $store = null;
+
+ /** @var Settings */
+ protected $settings = null;
+
+ /**
+ * @see SpecialPage::__construct
+ *
+ * @since 1.9
+ *
+ * @param $name
+ * @param $restriction
+ */
+ public function __construct( $name = '', $restriction = '' ) {
+ parent::__construct( $name, $restriction );
+ $this->store = StoreFactory::getStore();
+ }
+
+ /**
+ * Sets store instance
+ *
+ * @since 1.9
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ return $this;
+ }
+
+ /**
+ * Returns store object
+ *
+ * @since 1.9
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * Sets Settings object
+ *
+ * @since 1.9
+ *
+ * @param Settings $settings
+ */
+ public function setSettings( Settings $settings ) {
+ $this->settings = $settings;
+ return $this;
+ }
+
+ /**
+ * Returns Settings object
+ *
+ * @since 1.9
+ *
+ * @return Store
+ */
+ public function getSettings() {
+
+ if ( $this->settings === null ) {
+ $this->settings = Settings::newFromGlobals();
+ }
+
+ return $this->settings;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php
new file mode 100644
index 00000000..8a4ab4c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialProperties.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:Properties) for MediaWiki shows all
+ * used properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page for MediaWiki shows all used properties.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'Properties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+ $out = $this->getOutput();
+
+ $out->setPageTitle( $this->msg( 'properties' )->text() );
+
+ $page = new PropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit, $this->getRequest()->getVal( 'property' ) );
+
+ // Ensure locally collected output data is pushed to the output!
+ SMWOutputs::commitToOutputPage( $out );
+
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'pages';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php
new file mode 100644
index 00000000..23987442
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialUnusedProperties.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:UnusedProperties) for MediaWiki shows all
+ * unused properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page (Special:UnusedProperties) for MediaWiki shows all unused
+ * properties.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialUnusedProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'UnusedProperties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+
+ $out = $this->getOutput();
+
+ $out->setPageTitle( $this->msg( 'unusedproperties' )->text() );
+
+ $page = new UnusedPropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit, $this->getRequest()->getVal( 'property' ) );
+
+ // Ensure locally collected output data is pushed to the output!
+ SMWOutputs::commitToOutputPage( $out );
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php
new file mode 100644
index 00000000..79a9f681
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/specials/SpecialWantedProperties.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW;
+
+use SMWOutputs;
+
+/**
+ * Special page (Special:WantedProperties) for MediaWiki shows all
+ * wanted properties
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+
+/**
+ * This special page (Special:WantedProperties) for MediaWiki shows all wanted
+ * properties (used but not having a page).
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialWantedProperties extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'WantedProperties' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $param ) {
+ $this->setHeaders();
+
+ $out = $this->getOutput();
+
+ $out->addModuleStyles( [
+ 'ext.smw.special.style'
+ ] );
+
+ $out->setPageTitle( $this->msg( 'wantedproperties' )->text() );
+
+ $page = new WantedPropertiesQueryPage( $this->getStore(), $this->getSettings() );
+ $page->setContext( $this->getContext() );
+ $page->setTitle( $this->getPageTitle() );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+ $page->doQuery( $offset, $limit );
+
+ // Ensure locally collected output data is pushed to the output!
+ // ?? still needed !!
+ SMWOutputs::commitToOutputPage( $out );
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php
new file mode 100644
index 00000000..17f760d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_QueryResult.php
@@ -0,0 +1,462 @@
+<?php
+
+use SMW\Query\Excerpts;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryLinker;
+use SMW\Query\Result\ResolverJournal;
+use SMW\Query\ScoreSet;
+use SMW\SerializerFactory;
+
+/**
+ * Objects of this class encapsulate the result of a query in SMW. They
+ * provide access to the query result and printed data, and to some
+ * relevant query parameters that were used.
+ *
+ * Standard access is provided through the iterator function getNext(),
+ * which returns an array ("table row") of SMWResultArray objects ("table cells").
+ * It is also possible to access the set of result pages directly using
+ * getResults(). This is useful for printers that disregard printouts and
+ * only are interested in the actual list of pages.
+ *
+ *
+ * @ingroup SMWQuery
+ *
+ * @licence GNU GPL v2 or later
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWQueryResult {
+
+ /**
+ * Array of SMWDIWikiPage objects that are the basis for this result
+ * @var SMWDIWikiPage[]
+ */
+ protected $mResults;
+
+ /**
+ * Array of SMWPrintRequest objects, indexed by their natural hash keys
+ *
+ * @var PrintRequest[]
+ */
+ protected $mPrintRequests;
+
+ /**
+ * Are there more results than the ones given?
+ * @var boolean
+ */
+ protected $mFurtherResults;
+
+ /**
+ * The query object for which this is a result, must be set on create and is the source of
+ * data needed to create further result links.
+ * @var SMWQuery
+ */
+ protected $mQuery;
+
+ /**
+ * The SMWStore object used to retrieve further data on demand.
+ * @var SMWStore
+ */
+ protected $mStore;
+
+ /**
+ * Holds a value that belongs to a count query result
+ * @var integer|null
+ */
+ private $countValue;
+
+ /**
+ * Indicates whether results have been retrieved from cache or not
+ *
+ * @var boolean
+ */
+ private $isFromCache = false;
+
+ /**
+ * @var ResolverJournal
+ */
+ private $resolverJournal;
+
+ /**
+ * @var integer
+ */
+ private $serializer_version = 2;
+
+ /**
+ * @var ScoreSet
+ */
+ private $scoreSet;
+
+ /**
+ * @var Excerpts
+ */
+ private $excerpts;
+
+ /**
+ * Initialise the object with an array of SMWPrintRequest objects, which
+ * define the structure of the result "table" (one for each column).
+ *
+ * TODO: Update documentation
+ *
+ * @param PrintRequest[] $printRequests
+ * @param SMWQuery $query
+ * @param SMWDIWikiPage[] $results
+ * @param SMWStore $store
+ * @param boolean $furtherRes
+ */
+ public function __construct( array $printRequests, SMWQuery $query, array $results, SMWStore $store, $furtherRes = false ) {
+ $this->mResults = $results;
+ reset( $this->mResults );
+ $this->mPrintRequests = $printRequests;
+ $this->mFurtherResults = $furtherRes;
+ $this->mQuery = $query;
+ $this->mStore = $store;
+ $this->resolverJournal = new ResolverJournal();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ResolverJournal $resolverJournal
+ */
+ public function setResolverJournal( ResolverJournal $ResolverJournal ) {
+ $this->resolverJournal = $ResolverJournal;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return ResolverJournal
+ */
+ public function getResolverJournal() {
+ return $this->resolverJournal;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param boolean $isFromCache
+ */
+ public function setFromCache( $isFromCache ) {
+ $this->isFromCache = (bool)$isFromCache;
+ }
+
+ /**
+ * Only available by some stores that support the computation of scores.
+ *
+ * @since 3.0
+ *
+ * @param ScoreSet $scoreSet
+ */
+ public function setScoreSet( ScoreSet $scoreSet ) {
+ $this->scoreSet = $scoreSet;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ScoreSet|null
+ */
+ public function getScoreSet() {
+ return $this->scoreSet;
+ }
+
+ /**
+ * Only available by some stores that support the retrieval of excerpts.
+ *
+ * @since 3.0
+ *
+ * @param Excerpts $excerpts
+ */
+ public function setExcerpts( Excerpts $excerpts ) {
+ $this->excerpts = $excerpts;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Excerpts|null
+ */
+ public function getExcerpts() {
+ return $this->excerpts;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return $this->isFromCache;
+ }
+
+ /**
+ * Get the SMWStore object that this result is based on.
+ *
+ * @return SMWStore
+ */
+ public function getStore() {
+ return $this->mStore;
+ }
+
+ /**
+ * Return the next result row as an array of SMWResultArray objects, and
+ * advance the internal pointer.
+ *
+ * @return SMWResultArray[]|false
+ */
+ public function getNext() {
+ $page = current( $this->mResults );
+ next( $this->mResults );
+
+ if ( $page === false ) {
+ return false;
+ }
+
+ $row = [];
+
+ foreach ( $this->mPrintRequests as $p ) {
+ $resultArray = new SMWResultArray( $page, $p, $this->mStore );
+ $resultArray->setResolverJournal( $this->resolverJournal );
+ $resultArray->setQueryToken( $this->mQuery->getQueryToken() );
+ $row[] = $resultArray;
+ }
+
+ return $row;
+ }
+
+ /**
+ * Return number of available results.
+ *
+ * @return integer
+ */
+ public function getCount() {
+ return count( $this->mResults );
+ }
+
+ /**
+ * Return an array of SMWDIWikiPage objects that make up the
+ * results stored in this object.
+ *
+ * @return SMWDIWikiPage[]
+ */
+ public function getResults() {
+ return $this->mResults;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function reset() {
+ return reset( $this->mResults );
+ }
+
+ /**
+ * Returns the query object of the current result set
+ *
+ * @since 1.8
+ *
+ * @return SMWQuery
+ */
+ public function getQuery() {
+ return $this->mQuery;
+ }
+
+ /**
+ * Return the number of columns of result values that each row
+ * in this result set contains.
+ *
+ * @return integer
+ */
+ public function getColumnCount() {
+ return count( $this->mPrintRequests );
+ }
+
+ /**
+ * Return array of print requests (needed for printout since they contain
+ * property labels).
+ *
+ * @return PrintRequest[]
+ */
+ public function getPrintRequests() {
+ return $this->mPrintRequests;
+ }
+
+ /**
+ * Returns the query string defining the conditions for the entities to be
+ * returned.
+ *
+ * @return string
+ */
+ public function getQueryString() {
+ return $this->mQuery->getQueryString();
+ }
+
+ /**
+ * Would there be more query results that were not shown due to a limit?
+ *
+ * @return boolean
+ */
+ public function hasFurtherResults() {
+ return $this->mFurtherResults;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param integer $countValue
+ */
+ public function setCountValue( $countValue ) {
+ $this->countValue = (int)$countValue;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return integer|null
+ */
+ public function getCountValue() {
+ return $this->countValue;
+ }
+
+ /**
+ * Return error array, possibly empty.
+ *
+ * @return array
+ */
+ public function getErrors() {
+ // Just use query errors, as no errors generated in this class at the moment.
+ return $this->mQuery->getErrors();
+ }
+
+ /**
+ * Adds an array of erros.
+ *
+ * @param array $errors
+ */
+ public function addErrors( array $errors ) {
+ $this->mQuery->addErrors( $errors );
+ }
+
+ /**
+ * Create an SMWInfolink object representing a link to further query results.
+ * This link can then be serialised or extended by further params first.
+ * The optional $caption can be used to set the caption of the link (though this
+ * can also be changed afterwards with SMWInfolink::setCaption()). If empty, the
+ * message 'smw_iq_moreresults' is used as a caption.
+ *
+ * @param string|false $caption
+ *
+ * @return SMWInfolink
+ */
+ public function getQueryLink( $caption = false ) {
+
+ $link = QueryLinker::get( $this->mQuery );
+
+ $link->setCaption( $caption );
+ $link->setParameter( $this->mQuery->getOffset() + count( $this->mResults ), 'offset' );
+
+ return $link;
+ }
+
+ /**
+ * @deprecated since 2.5, use QueryResult::getQueryLink
+ *
+ * Returns an SMWInfolink object with the QueryResults print requests as parameters.
+ *
+ * @since 1.8
+ *
+ * @return SMWInfolink
+ */
+ public function getLink() {
+ return $this->getQueryLink();
+ }
+
+ /**
+ * @private
+ *
+ * @since 3.0
+ */
+ public function setSerializerVersion( $version ) {
+ $this->serializer_version = $version;
+ }
+
+ /**
+ * @see DISerializer::getSerializedQueryResult
+ * @since 1.7
+ * @return array
+ */
+ public function serializeToArray() {
+
+ $serializerFactory = new SerializerFactory();
+ $serializer = $serializerFactory->newQueryResultSerializer();
+ $serializer->version( $this->serializer_version );
+
+ $serialized = $serializer->serialize( $this );
+ reset( $this->mResults );
+
+ return $serialized;
+ }
+
+ /**
+ * Returns a serialized SMWQueryResult object with additional meta data
+ *
+ * This methods extends the serializeToArray() for additional meta
+ * that are useful when handling data via the api
+ *
+ * @note should be used instead of SMWQueryResult::serializeToArray()
+ * as this method contains additional informaion
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function toArray() {
+
+ $time = microtime( true );
+
+ // @note micro optimization: We call getSerializedQueryResult()
+ // only once and create the hash here instead of calling getHash()
+ // to avoid getSerializedQueryResult() being called again
+ // @note count + offset equals total therefore we deploy both values
+ $serializeArray = $this->serializeToArray();
+
+ return array_merge( $serializeArray, [
+ 'meta'=> [
+ 'hash' => md5( json_encode( $serializeArray ) ),
+ 'count' => $this->getCount(),
+ 'offset' => $this->mQuery->getOffset(),
+ 'source' => $this->mQuery->getQuerySource(),
+ 'time' => number_format( ( microtime( true ) - $time ), 6, '.', '' )
+ ]
+ ]
+ );
+ }
+
+ /**
+ * Returns result hash value
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHash( $type = null ) {
+
+ // Just iterate over available subjects to create a "quick" hash given
+ // that resolving the entire object tree is costly due to recursive
+ // processing of all data items including its printouts
+ if ( $type === 'quick' ) {
+ $hash = [];
+
+ foreach ( $this->mResults as $dataItem ) {
+ $hash[] = $dataItem->getHash();
+ }
+
+ reset( $this->mResults );
+ return 'q:' . md5( json_encode( $hash ) );
+ }
+
+ return md5( json_encode( $this->serializeToArray() ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php
new file mode 100644
index 00000000..438edf5a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SMW_ResultArray.php
@@ -0,0 +1,282 @@
+<?php
+
+use SMW\DataValueFactory;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryToken;
+use SMW\Query\Result\ResolverJournal;
+use SMW\Query\Result\ResultFieldMatchFinder;
+use SMWDataItem as DataItem;
+
+/**
+ * Container for the contents of a single result field of a query result,
+ * i.e. basically an array of SMWDataItems with some additional parameters.
+ * The content of the array is fetched on demand only.
+ *
+ * @ingroup SMWQuery
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWResultArray {
+
+ /**
+ * @var PrintRequest
+ */
+ private $mPrintRequest;
+
+ /**
+ * @var SMWDIWikiPage
+ */
+ private $mResult;
+
+ /**
+ * @var SMWStore
+ */
+ private $mStore;
+
+ /**
+ * @var SMWDataItem[]|false
+ */
+ private $mContent;
+
+ /**
+ * @var ResolverJournal
+ */
+ private $resolverJournal;
+
+ /**
+ * @var ResultFieldMatchFinder
+ */
+ private $resultFieldMatchFinder;
+
+ /**
+ * @var QueryToken
+ */
+ private $queryToken;
+
+ /**
+ * Constructor.
+ *
+ * @param SMWDIWikiPage $resultPage
+ * @param PrintRequest $printRequest
+ * @param SMWStore $store
+ */
+ public function __construct( SMWDIWikiPage $resultPage, PrintRequest $printRequest, SMWStore $store ) {
+ $this->mResult = $resultPage;
+ $this->mPrintRequest = $printRequest;
+ $this->mStore = $store;
+ $this->mContent = false;
+
+ // FIXME 3.0; Inject the object
+ $this->resultFieldMatchFinder = new ResultFieldMatchFinder( $store, $printRequest );
+ }
+
+ /**
+ * Get the SMWStore object that this result is based on.
+ *
+ * @return SMWStore
+ */
+ public function getStore() {
+ return $this->mStore;
+ }
+
+ /**
+ * Returns the SMWDIWikiPage object to which this SMWResultArray refers.
+ * If you only care for those objects, consider using SMWQueryResult::getResults()
+ * directly.
+ *
+ * @return SMWDIWikiPage
+ */
+ public function getResultSubject() {
+ return $this->mResult;
+ }
+
+ /**
+ * Temporary track what entities are used while being instantiated, so an external
+ * service can have access to the list without requiring to resolve the objects
+ * independently.
+ *
+ * @since 2.4
+ *
+ * @param ResolverJournal $resolverJournal
+ */
+ public function setResolverJournal( ResolverJournal $resolverJournal ) {
+ $this->resolverJournal = $resolverJournal;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryToken|null $queryToken
+ */
+ public function setQueryToken( QueryToken $queryToken = null ) {
+ $this->queryToken = $queryToken;
+ }
+
+ /**
+ * Returns an array of SMWDataItem objects that contain the results of
+ * the given print request for the given result object.
+ *
+ * @return SMWDataItem[]|false
+ */
+ public function getContent() {
+ $this->loadContent();
+ return $this->mContent;
+ }
+
+ /**
+ * Return a PrintRequest object describing what is contained in this
+ * result set.
+ *
+ * @return PrintRequest
+ */
+ public function getPrintRequest() {
+ return $this->mPrintRequest;
+ }
+
+ /**
+ * Return the next SMWDataItem object or false if no further object exists.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataItem|false
+ */
+ public function getNextDataItem() {
+ $this->loadContent();
+ $result = current( $this->mContent );
+
+ if ( $this->resolverJournal !== null && $result instanceof DataItem ) {
+ $this->resolverJournal->recordItem( $result );
+ }
+
+ next( $this->mContent );
+ return $result;
+ }
+
+ /**
+ * Set the internal pointer of the array of SMWDataItem objects to its first
+ * element. Return the first SMWDataItem object or false if the array is
+ * empty.
+ *
+ * @since 1.7.1
+ *
+ * @return SMWDataItem|false
+ */
+ public function reset() {
+ $this->loadContent();
+ return reset( $this->mContent );
+ }
+
+ /**
+ * Return an SMWDataValue object for the next SMWDataItem object or
+ * false if no further object exists.
+ *
+ * @since 1.6
+ *
+ * @return SMWDataValue|false
+ */
+ public function getNextDataValue() {
+ $dataItem = $this->getNextDataItem();
+
+ if ( $dataItem === false ) {
+ return false;
+ }
+
+ if ( $this->mPrintRequest->getMode() == PrintRequest::PRINT_PROP &&
+ strpos( $this->mPrintRequest->getTypeID(), '_rec' ) !== false &&
+ $this->mPrintRequest->getParameter( 'index' ) !== false ) {
+
+ $recordValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $this->mPrintRequest->getData()->getDataItem()
+ );
+
+ $diProperty = $recordValue->getPropertyDataItemByIndex(
+ $this->mPrintRequest->getParameter( 'index' )
+ );
+ } elseif ( $this->mPrintRequest->isMode( PrintRequest::PRINT_PROP ) ) {
+ $diProperty = $this->mPrintRequest->getData()->getDataItem();
+ } elseif ( $this->mPrintRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ $diProperty = $this->mPrintRequest->getData()->getLastPropertyChainValue()->getDataItem();
+ } else {
+ $diProperty = null;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $diProperty
+ );
+
+ $dataValue->setContextPage(
+ $this->mResult
+ );
+
+ if ( $this->mPrintRequest->getOutputFormat() ) {
+ $dataValue->setOutputFormat( $this->mPrintRequest->getOutputFormat() );
+ }
+
+ if ( $this->resolverJournal !== null && $dataItem instanceof DataItem ) {
+ $this->resolverJournal->recordItem( $dataItem );
+ $this->resolverJournal->recordProperty( $diProperty );
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * Return the main text representation of the next SMWDataItem object
+ * in the specified format, or false if no further object exists.
+ *
+ * The parameter $linker controls linking of title values and should
+ * be some Linker object (or NULL for no linking).
+ *
+ * @param integer $outputMode
+ * @param mixed $linker
+ *
+ * @return string|false
+ */
+ public function getNextText( $outputMode, $linker = null ) {
+ $dataValue = $this->getNextDataValue();
+ if ( $dataValue !== false ) { // Print data values.
+ return $dataValue->getShortText( $outputMode, $linker );
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Load results of the given print request and result subject. This is only
+ * done when needed.
+ */
+ protected function loadContent() {
+
+ if ( $this->mContent !== false ) {
+ return;
+ }
+
+ $this->resultFieldMatchFinder->setQueryToken(
+ $this->queryToken
+ );
+
+ $this->mContent = $this->resultFieldMatchFinder->findAndMatch(
+ $this->mResult
+ );
+
+ return reset( $this->mContent );
+ }
+
+ /**
+ * Make a request option object based on the given parameters, and
+ * return NULL if no such object is required. The parameter defines
+ * if the limit should be taken into account, which is not always desired
+ * (especially if results are to be cached for future use).
+ *
+ * @param boolean $useLimit
+ *
+ * @return SMWRequestOptions|null
+ */
+ protected function getRequestOptions( $useLimit = true ) {
+ return $this->resultFieldMatchFinder->getRequestOptions( $useLimit );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php
new file mode 100644
index 00000000..cfc21271
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3.php
@@ -0,0 +1,653 @@
+<?php
+
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableInfoFetcher;
+use SMW\SQLStore\RequestOptionsProc;
+use SMW\SQLStore\SQLStoreFactory;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\SQLStore\TableDefinition;
+
+/**
+ * SQL-based implementation of SMW's storage abstraction layer.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ *
+ * @ingroup SMWStore
+ */
+
+// The use of the following constants is explained in SMWSQLStore3::setup():
+define( 'SMW_SQL3_SMWIW_OUTDATED', ':smw' ); // virtual "interwiki prefix" for old-style special SMW objects (no longer used)
+define( 'SMW_SQL3_SMWREDIIW', ':smw-redi' ); // virtual "interwiki prefix" for SMW objects that are redirected
+define( 'SMW_SQL3_SMWBORDERIW', ':smw-border' ); // virtual "interwiki prefix" separating very important pre-defined properties from the rest
+define( 'SMW_SQL3_SMWINTDEFIW', ':smw-intprop' ); // virtual "interwiki prefix" marking internal (invisible) predefined properties
+define( 'SMW_SQL3_SMWDELETEIW', ':smw-delete' ); // virtual "interwiki prefix" marking a deleted subject, see #1100
+
+/**
+ * Storage access class for using the standard MediaWiki SQL database for
+ * keeping semantic data.
+ *
+ * @note Regarding the use of interwiki links in the store, there is currently
+ * no support for storing semantic data about interwiki objects, and hence
+ * queries that involve interwiki objects really make sense only for them
+ * occurring in object positions. Most methods still use the given input
+ * interwiki text as a simple way to filter out results that may be found if an
+ * interwiki object is given but a local object of the same name exists. It is
+ * currently not planned to support things like interwiki reuse of properties.
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3 extends SMWStore {
+
+ /**
+ * Specifies the border limit (upper bound) for pre-defined properties used
+ * in the ID_TABLE
+ *
+ * When changing the upper bound, please make sure to copy the current upper
+ * bound as legcy to the TableIntegrityExaminer::checkPredefinedPropertyUpperbound
+ */
+ const FIXED_PROPERTY_ID_UPPERBOUND = 500;
+
+ /**
+ * Name of the table to store the concept cache in.
+ *
+ * @note This should never change. If it is changed, the concept caches
+ * will appear empty until they are recomputed.
+ */
+ const CONCEPT_CACHE_TABLE = 'smw_concept_cache';
+ const CONCEPT_TABLE = 'smw_fpt_conc';
+
+ /**
+ * Name of the table to store the concept cache in.
+ *
+ * @note This should never change, but if it does then its contents can
+ * simply be rebuilt by running the setup.
+ */
+ const PROPERTY_STATISTICS_TABLE = 'smw_prop_stats';
+
+ /**
+ * Name of the table that manages the query dependency links
+ */
+ const QUERY_LINKS_TABLE = 'smw_query_links';
+
+ /**
+ * Name of the table that manages the fulltext index
+ */
+ const FT_SEARCH_TABLE = 'smw_ft_search';
+
+ /**
+ * Name of the table that manages the Store IDs
+ */
+ const ID_TABLE = 'smw_object_ids';
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var PropertyTableInfoFetcher|null
+ */
+ private $propertyTableInfoFetcher = null;
+
+ /**
+ * @var PropertyTableIdReferenceFinder
+ */
+ private $propertyTableIdReferenceFinder;
+
+ /**
+ * @var DataItemHandlerDispatcher
+ */
+ private $dataItemHandlerDispatcher;
+
+ /**
+ * @var EntityLookup
+ */
+ private $entityLookup;
+
+ /**
+ * @var ServicesContainer
+ */
+ protected $servicesContainer;
+
+ /**
+ * Object to access the SMW IDs table.
+ *
+ * @since 1.8
+ * @var SMWSql3SmwIds
+ */
+ public $smwIds;
+
+ /**
+ * The reader object used by this store. Initialized by getReader()
+ * Always access using getReader()
+ *
+ * @since 1.8
+ * @var SMWSQLStore3Readers
+ */
+ protected $reader = false;
+
+ /**
+ * The writer object used by this store. Initialized by getWriter(),
+ * which is the only way in which it should be accessed.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3Writers
+ */
+ protected $writer = false;
+
+ /**
+ * @since 1.8
+ */
+ public function __construct() {
+ $this->factory = new SQLStoreFactory( $this, $this->messageReporter );
+ $this->smwIds = $this->factory->newEntityTable();
+ }
+
+ /**
+ * Get an object of the dataitem handler from the dataitem provided.
+ *
+ * @since 1.8
+ * @param integer $diType
+ *
+ * @return SMWDIHandler
+ * @throws RuntimeException if no handler exists for the given type
+ */
+ public function getDataItemHandlerForDIType( $diType ) {
+
+ if ( $this->dataItemHandlerDispatcher === null ) {
+ $this->dataItemHandlerDispatcher = $this->factory->newDataItemHandlerDispatcher( $this );
+ }
+
+ return $this->dataItemHandlerDispatcher->getHandlerByType( $diType );
+ }
+
+///// Reading methods /////
+
+ public function getReader() {
+ if( $this->reader == false ) {
+ $this->reader = new SMWSQLStore3Readers( $this, $this->factory );//Initialize if not done already
+ }
+
+ return $this->reader;
+ }
+
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+ return $this->getEntityLookup()->getSemanticData( $subject, $filter );
+ }
+
+ /**
+ * @param mixed $subject
+ * @param DIProperty $property
+ * @param null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( $subject, DIProperty $property, $requestOptions = null ) {
+ return $this->getEntityLookup()->getPropertyValues( $subject, $property, $requestOptions );
+ }
+
+ public function getProperties( DIWikiPage $subject, $requestOptions = null ) {
+ return $this->getEntityLookup()->getProperties( $subject, $requestOptions );
+ }
+
+ public function getPropertySubjects( DIProperty $property, $dataItem, $requestOptions = null ) {
+ return $this->getEntityLookup()->getPropertySubjects( $property, $dataItem, $requestOptions );
+ }
+
+ public function getAllPropertySubjects( DIProperty $property, $requestoptions = null ) {
+ return $this->getEntityLookup()->getAllPropertySubjects( $property, $requestoptions );
+ }
+
+ public function getInProperties( SMWDataItem $value, $requestoptions = null ) {
+ return $this->getEntityLookup()->getInProperties( $value, $requestoptions );
+ }
+
+
+///// Writing methods /////
+
+
+ public function getWriter() {
+ if( $this->writer == false ) {
+ $this->writer = new SMWSQLStore3Writers( $this, $this->factory );
+ }
+
+ return $this->writer;
+ }
+
+ public function deleteSubject( Title $title ) {
+
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $this->getEntityLookup()->invalidateCache(
+ $subject
+ );
+
+ $this->getWriter()->deleteSubject( $title );
+
+ $this->doDeferredCachedListLookupUpdate(
+ $subject
+ );
+ }
+
+ protected function doDataUpdate( SemanticData $semanticData ) {
+
+ $this->getEntityLookup()->invalidateCache(
+ $semanticData->getSubject()
+ );
+
+ $this->getWriter()->doDataUpdate( $semanticData );
+
+ $this->doDeferredCachedListLookupUpdate(
+ $semanticData->getSubject()
+ );
+ }
+
+ public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) {
+
+ $this->getEntityLookup()->invalidateCache(
+ DIWikiPage::newFromTitle( $oldtitle )
+ );
+
+ $this->getEntityLookup()->invalidateCache(
+ DIWikiPage::newFromTitle( $newtitle )
+ );
+
+ $this->getWriter()->changeTitle( $oldtitle, $newtitle, $pageid, $redirid );
+
+ $this->doDeferredCachedListLookupUpdate(
+ DIWikiPage::newFromTitle( $oldtitle )
+ );
+ }
+
+ private function doDeferredCachedListLookupUpdate( DIWikiPage $subject ) {
+
+ if ( $subject->getNamespace() !== SMW_NS_PROPERTY ) {
+ return null;
+ }
+
+ $deferredCallableUpdate = $this->factory->newDeferredCallableCachedListLookupUpdate();
+ $deferredCallableUpdate->setOrigin( __METHOD__ );
+ $deferredCallableUpdate->waitOnTransactionIdle();
+ $deferredCallableUpdate->pushUpdate();
+ }
+
+///// Query answering /////
+
+ /**
+ * @note Move hooks to the base class in 3.*
+ *
+ * @see SMWStore::fetchQueryResult
+ *
+ * @since 1.8
+ * @param SMWQuery $query
+ * @return SMWQueryResult|string|integer depends on $query->querymode
+ */
+ public function getQueryResult( SMWQuery $query ) {
+
+ $result = null;
+ $start = microtime( true );
+
+ if ( \Hooks::run( 'SMW::Store::BeforeQueryResultLookupComplete', [ $this, $query, &$result, $this->factory->newSlaveQueryEngine() ] ) ) {
+ $result = $this->fetchQueryResult( $query );
+ }
+
+ \Hooks::run( 'SMW::SQLStore::AfterQueryResultLookupComplete', [ $this, &$result ] );
+ \Hooks::run( 'SMW::Store::AfterQueryResultLookupComplete', [ $this, &$result ] );
+
+ $query->setOption( SMWQuery::PROC_QUERY_TIME, microtime( true ) - $start );
+
+ return $result;
+ }
+
+ protected function fetchQueryResult( SMWQuery $query ) {
+ return $this->factory->newSlaveQueryEngine()->getQueryResult( $query );
+ }
+
+///// Special page functions /////
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newPropertyUsageCachedListLookup( $requestOptions );
+ }
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getUnusedPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newUnusedPropertyCachedListLookup( $requestOptions );
+ }
+
+ /**
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function getWantedPropertiesSpecial( $requestOptions = null ) {
+ return $this->factory->newUndeclaredPropertyCachedListLookup( $requestOptions );
+ }
+
+ public function getStatistics() {
+ return $this->factory->newUsageStatisticsCachedListLookup()->fetchList();
+ }
+
+
+///// Setup store /////
+
+ /**
+ * @see Store::service
+ *
+ * {@inheritDoc}
+ */
+ public function service( $service, ...$args ) {
+
+ if ( $this->servicesContainer === null ) {
+ $this->servicesContainer = $this->newServicesContainer();
+ }
+
+ return $this->servicesContainer->get( $service, ...$args );
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function setup( $verbose = true ) {
+
+ $installer = $this->factory->newInstaller();
+ $installer->setMessageReporter( $this->messageReporter );
+
+ return $installer->install( $verbose );
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function drop( $verbose = true ) {
+
+ $installer = $this->factory->newInstaller();
+ $installer->setMessageReporter( $this->messageReporter );
+
+ return $installer->uninstall( $verbose );
+ }
+
+ public function refreshData( &$id, $count, $namespaces = false, $usejobs = true ) {
+
+ $entityRebuildDispatcher = $this->factory->newEntityRebuildDispatcher();
+
+ $entityRebuildDispatcher->setDispatchRangeLimit( $count );
+ $entityRebuildDispatcher->setRestrictionToNamespaces( $namespaces );
+
+ $entityRebuildDispatcher->setOptions(
+ [
+ 'use-job' => $usejobs
+ ]
+ );
+
+ return $entityRebuildDispatcher;
+ }
+
+
+///// Concept caching /////
+
+ /**
+ * Refresh the concept cache for the given concept.
+ *
+ * @since 1.8
+ * @param Title $concept
+ * @return array of error strings (empty if no errors occurred)
+ */
+ public function refreshConceptCache( Title $concept ) {
+ return $this->factory->newMasterConceptCache()->refreshConceptCache( $concept );
+ }
+
+ /**
+ * Delete the concept cache for the given concept.
+ *
+ * @since 1.8
+ * @param Title $concept
+ */
+ public function deleteConceptCache( $concept ) {
+ $this->factory->newMasterConceptCache()->deleteConceptCache( $concept );
+ }
+
+ /**
+ * Return status of the concept cache for the given concept as an array
+ * with key 'status' ('empty': not cached, 'full': cached, 'no': not
+ * cacheable). If status is not 'no', the array also contains keys 'size'
+ * (query size), 'depth' (query depth), 'features' (query features). If
+ * status is 'full', the array also contains keys 'date' (timestamp of
+ * cache), 'count' (number of results in cache).
+ *
+ * @since 1.8
+ * @param Title|SMWWikiPageValue $concept
+ *
+ * @return DIConcept|null
+ */
+ public function getConceptCacheStatus( $concept ) {
+ return $this->factory->newSlaveConceptCache()->getStatus( $concept );
+ }
+
+
+///// Helper methods, mostly protected /////
+
+ /**
+ * @see RequestOptionsProc::getSQLOptions
+ *
+ * @since 1.8
+ *
+ * @param SMWRequestOptions|null $requestOptions
+ * @param string $valuecol
+ *
+ * @return array
+ */
+ public function getSQLOptions( SMWRequestOptions $requestOptions = null, $valueCol = '' ) {
+ return RequestOptionsProc::getSQLOptions( $requestOptions, $valueCol );
+ }
+
+ /**
+ * @see RequestOptionsProc::getSQLConditions
+ *
+ * @since 1.8
+ *
+ * @param SMWRequestOptions|null $requestOptions
+ * @param string $valueCol name of SQL column to which conditions apply
+ * @param string $labelCol name of SQL column to which string conditions apply, if any
+ * @param boolean $addAnd indicate whether the string should begin with " AND " if non-empty
+ *
+ * @return string
+ */
+ public function getSQLConditions( SMWRequestOptions $requestOptions = null, $valueCol = '', $labelCol = '', $addAnd = true ) {
+ return RequestOptionsProc::getSQLConditions( $this, $requestOptions, $valueCol, $labelCol, $addAnd );
+ }
+
+ /**
+ * @see RequestOptionsProc::applyRequestOptions
+ *
+ * @since 1.8
+ *
+ * @param array $data array of SMWDataItem objects
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function applyRequestOptions( array $data, SMWRequestOptions $requestOptions = null ) {
+ return RequestOptionsProc::applyRequestOptions( $this, $data, $requestOptions );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForDataTypeTypeId
+ *
+ * @param string $typeid
+ *
+ * @return string
+ */
+ public function findTypeTableId( $typeid ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForDataTypeTypeId( $typeid );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForDataItemTypeId
+ *
+ * @param integer $dataItemId
+ *
+ * @return string
+ */
+ public function findDiTypeTableId( $dataItemId ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForDataItemTypeId( $dataItemId );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::findTableIdForProperty
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public function findPropertyTableID( DIProperty $property ) {
+ return $this->getPropertyTableInfoFetcher()->findTableIdForProperty( $property );
+ }
+
+ /**
+ * PropertyTableInfoFetcher::getPropertyTableDefinitions
+ *
+ * @return TableDefinition[]
+ */
+ public function getPropertyTables() {
+ return $this->getPropertyTableInfoFetcher()->getPropertyTableDefinitions();
+ }
+
+ /**
+ * Returns SMW Id object
+ *
+ * @since 1.9
+ *
+ * @return SMWSql3SmwIds
+ */
+ public function getObjectIds() {
+ return $this->smwIds;
+ }
+
+ /**
+ * Returns the statics table
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getStatisticsTable() {
+ return self::PROPERTY_STATISTICS_TABLE;
+ }
+
+ /**
+ * Resets internal objects
+ *
+ * @since 1.9.1.1
+ */
+ public function clear() {
+ parent::clear();
+ $this->factory->newSemanticDataLookup()->clear();
+ $this->propertyTableInfoFetcher = null;
+ $this->servicesContainer = null;
+ $this->getObjectIds()->initCache();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getInfo( $type = null ) {
+
+ if ( $type === 'store' ) {
+ return 'SMWSQLStore';
+ }
+
+ $connection = $this->getConnection( 'mw.db' );
+
+ if ( $type === 'db' ) {
+ return $connection->getInfo();
+ }
+
+ return [
+ 'SMWSQLStore' => $connection->getInfo()
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ *
+ * @return Database
+ */
+ public function getConnection( $type = 'mw.db' ) {
+ return parent::getConnection( $type );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return PropertyTableInfoFetcher
+ */
+ public function getPropertyTableInfoFetcher() {
+
+ if ( $this->propertyTableInfoFetcher === null ) {
+ $this->propertyTableInfoFetcher = $this->factory->newPropertyTableInfoFetcher();
+ }
+
+ return $this->propertyTableInfoFetcher;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return PropertyTableIdReferenceFinder
+ */
+ public function getPropertyTableIdReferenceFinder() {
+
+ if ( $this->propertyTableIdReferenceFinder === null ) {
+ $this->propertyTableIdReferenceFinder = $this->factory->newPropertyTableIdReferenceFinder();
+ }
+
+ return $this->propertyTableIdReferenceFinder;
+ }
+
+ /**
+ * @return ServicesContainer
+ */
+ protected function newServicesContainer() {
+ return $this->factory->newServicesContainer();
+ }
+
+ /**
+ * @return EntityLookup
+ */
+ private function getEntityLookup() {
+
+ if ( $this->entityLookup === null ) {
+ $this->entityLookup = $this->factory->newEntityLookup();
+ }
+
+ return $this->entityLookup;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
new file mode 100644
index 00000000..f2968edb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
@@ -0,0 +1,501 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\Enum;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableDefinition;
+
+/**
+ * Class to provide all basic read methods for SMWSQLStore3.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3Readers {
+
+ /**
+ * The store used by this store reader
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var TraversalPropertyLookup
+ */
+ private $traversalPropertyLookup;
+
+ /**
+ * @var PropertySubjectsLookup
+ */
+ private $propertySubjectsLookup;
+
+ /**
+ * @var PropertiesLookup
+ */
+ private $propertiesLookup;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ public function __construct( SMWSQLStore3 $parentStore, $factory ) {
+ $this->store = $parentStore;
+ $this->factory = $factory;
+ $this->traversalPropertyLookup = $this->factory->newTraversalPropertyLookup();
+ $this->propertySubjectsLookup = $this->factory->newPropertySubjectsLookup();
+ $this->propertiesLookup = $this->factory->newPropertiesLookup();
+ $this->semanticDataLookup = $this->factory->newSemanticDataLookup();
+ }
+
+ /**
+ * @see SMWStore::getSemanticData()
+ * @since 1.8
+ *
+ * @param DIWikiPage $subject
+ * @param string[]|bool $filter
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+
+ // *** Find out if this subject exists ***//
+ $sortKey = '';
+
+ $sid = $this->store->smwIds->getSMWPageIDandSort(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ $sortKey,
+ true,
+ true
+ );
+
+ // Ensures that a cached item to contain an expected sortKey when
+ // for example the ID was just created and the sortKey from the DB
+ // is empty otherwise the DB wins over the invoked sortKey
+ if ( !$sortKey ) {
+ $sortKey = $subject->getSortKey();
+ }
+
+ $subject->setSortKey( $sortKey );
+
+ if ( $sid == 0 ) {
+ // We consider redirects for getting $sid,
+ // so $sid == 0 also means "no redirects".
+ return new SMWSemanticData( $subject );
+ }
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+ $opts = null;
+ $semanticData = null;
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $semanticData = $this->semanticDataLookup->newStubSemanticData( $subject );
+ }
+
+ foreach ( $this->store->getPropertyTables() as $tid => $proptable ) {
+ if ( !array_key_exists( $proptable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $opts = $filter;
+ } elseif ( $filter !== false ) {
+ $relevant = false;
+ foreach ( $filter as $typeId ) {
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $typeId );
+ $relevant = $relevant || ( $proptable->getDiType() == $diType );
+ if ( $relevant ) {
+ break;
+ }
+ }
+ if ( !$relevant ) {
+ continue;
+ }
+ }
+
+ $data = $this->semanticDataLookup->getSemanticDataFromTable( $sid, $subject, $proptable, $opts );
+
+ if ( $semanticData !== null ) {
+ $semanticData->importDataFrom( $data );
+ }
+ }
+
+ if ( $semanticData === null ) {
+ // Note: the sortkey is always set but belongs to no property table,
+ // hence no entry in $this->store->m_sdstate[$sid] is made.
+ $this->semanticDataLookup->lockCache();
+ $this->semanticDataLookup->initLookupCache( $sid, $subject );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataById(
+ $sid
+ );
+
+ $this->semanticDataLookup->unlockCache();
+ }
+
+ // Avoid adding a sortkey for an already extended stub
+ if ( !$semanticData->hasProperty( new DIProperty( '_SKEY' ) ) ) {
+ $semanticData->addPropertyStubValue( '_SKEY', [ '', $sortKey ] );
+ }
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getProperties()
+ );
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getPropertyValues( new DIProperty( '_INST' ) )
+ );
+
+ return $semanticData;
+ }
+
+ /**
+ * @see SMWStore::getPropertyValues
+ *
+ * @todo Retrieving all sortkeys (values for _SKEY with $subject null)
+ * is not supported. An empty array will be given.
+ *
+ * @since 1.8
+ *
+ * @param $subject mixed DIWikiPage or null
+ * @param $property SMWDIProperty
+ * @param $requestOptions SMWRequestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( $subject, SMWDIProperty $property, $requestOptions = null ) {
+
+ if ( $property->isInverse() ) { // inverses are working differently
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertySubjects( $noninverse, $subject, $requestOptions );
+ } elseif ( !is_null( $subject ) ) { // subject given, use semantic data cache
+ $sid = $this->store->smwIds->getSMWPageID( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), true );
+ if ( $sid == 0 ) {
+ $result = [];
+ } elseif ( $property->getKey() == '_SKEY' ) {
+ $this->store->smwIds->getSMWPageIDandSort( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), $sortKey, true );
+ $sortKeyDi = new SMWDIBlob( $sortKey );
+ $result = $this->store->applyRequestOptions( [ $sortKeyDi ], $requestOptions );
+ } else {
+ $propTableId = $this->store->findPropertyTableID( $property );
+ $proptables = $this->store->getPropertyTables();
+
+ if ( !isset( $proptables[$propTableId] ) ) {
+ return [];
+ }
+
+ $propertyTableDef = $proptables[$propTableId];
+
+ $opts = $this->semanticDataLookup->newRequestOptions(
+ $propertyTableDef,
+ $property,
+ $requestOptions
+ );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataFromTable(
+ $sid,
+ $subject,
+ $propertyTableDef,
+ $opts
+ );
+
+ $pv = $semanticData->getPropertyValues( $property );
+ $this->store->smwIds->warmUpCache( $pv );
+
+ $result = $this->store->applyRequestOptions(
+ $pv,
+ $requestOptions
+ );
+ }
+ } else { // no subject given, get all values for the given property
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $data = $this->semanticDataLookup->fetchSemanticData(
+ $pid,
+ $property,
+ $proptables[$tableid],
+ $requestOptions
+ );
+
+ $result = [];
+ $propertyTypeId = $property->findPropertyTypeID();
+ $propertyDiId = DataTypeRegistry::getInstance()->getDataItemId( $propertyTypeId );
+
+ foreach ( $data as $dbkeys ) {
+ try {
+ $diHandler = $this->store->getDataItemHandlerForDIType( $propertyDiId );
+ $result[] = $diHandler->dataItemFromDBKeys( $dbkeys );
+ } catch ( SMWDataItemException $e ) {
+ // maybe type assignment changed since data was stored;
+ // don't worry, but we can only drop the data here
+ }
+ }
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * @see SMWStore::getPropertySubjects
+ *
+ * @todo This method cannot retrieve subjects for sortkeys, i.e., for
+ * property _SKEY. Only empty arrays will be returned there.
+ *
+ * @param SMWDIProperty $property
+ * @param SMWDataItem|null $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getPropertySubjects( SMWDIProperty $property, SMWDataItem $dataItem = null, SMWRequestOptions $requestOptions = null ) {
+
+ // inverses are working differently
+ if ( $property->isInverse() ) {
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertyValues( $dataItem, $noninverse, $requestOptions );
+ return $result;
+ }
+
+ $type = DataTypeRegistry::getInstance()->getDataItemByType(
+ $property->findPropertyTypeID()
+ );
+
+ // #1222, Filter those where types don't match (e.g property = _txt
+ // and value = _wpg)
+ if ( $dataItem !== null && $type !== $dataItem->getDIType() ) {
+ return [];
+ }
+
+ // First build $select, $from, and $where for the DB query
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $proptable = $proptables[$tableid];
+
+ $result = $this->propertySubjectsLookup->fetchFromTable(
+ $pid,
+ $proptable,
+ $dataItem,
+ $requestOptions
+ );
+
+ // Keep the result as iterator which is normally advised when the result
+ // size is expected to be larger than 1000 or results are retrieved through
+ // a job which may process them in batches.
+ if ( $requestOptions !== null && $requestOptions->getOption( Enum::SUSPEND_CACHE_WARMUP ) ) {
+ return $result;
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+
+ /**
+ * Helper function to compute from and where strings for a DB query so that
+ * only rows of the given value object match. The parameter $tableindex
+ * counts that tables used in the query to avoid duplicate table names. The
+ * parameter $proptable provides the SMWSQLStore3Table object that is
+ * queried.
+ *
+ * @todo Maybe do something about redirects. The old code was
+ * $oid = $this->store->smwIds->getSMWPageID($value->getDBkey(),$value->getNamespace(),$value->getInterwiki(),false);
+ *
+ * @note This method cannot handle DIContainer objects with sortkey
+ * properties correctly. This should never occur, but it would be good
+ * to fail in a more controlled way if it ever does.
+ *
+ * @param string $from
+ * @param string $where
+ * @param TableDefinition $propTable
+ * @param SMWDataItem $value
+ * @param integer $tableIndex
+ */
+ private function prepareValueQuery( &$from, &$where, TableDefinition $propTable, $value, $tableIndex = 1 ) {
+ $db = $this->store->getConnection();
+
+ if ( $value instanceof SMWDIContainer ) { // recursive handling of containers
+ $keys = array_keys( $propTable->getFields( $this->store ) );
+ $joinfield = "t$tableIndex." . reset( $keys ); // this must be a type 'p' object
+ $proptables = $this->store->getPropertyTables();
+ $semanticData = $value->getSemanticData();
+
+ foreach ( $semanticData->getProperties() as $subproperty ) {
+ $tableid = $this->store->findPropertyTableID( $subproperty );
+ $subproptable = $proptables[$tableid];
+
+ foreach ( $semanticData->getPropertyValues( $subproperty ) as $subvalue ) {
+ $tableIndex++;
+
+ if ( $subproptable->usesIdSubject() ) { // simply add property table to check values
+ $from .= " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON t$tableIndex.s_id=$joinfield";
+ } else { // exotic case with table that uses subject title+namespace in container object (should never happen in SMW core)
+ $from .= " INNER JOIN " . $db->tableName( SMWSql3SmwIds::TABLE_NAME ) . " AS ids$tableIndex ON ids$tableIndex.smw_id=$joinfield" .
+ " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON " .
+ "t$tableIndex.s_title=ids$tableIndex.smw_title AND t$tableIndex.s_namespace=ids$tableIndex.smw_namespace";
+ }
+
+ if ( !$subproptable->isFixedPropertyTable() ) { // the ID we get should be !=0, so no point in filtering the converse
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.p_id=" . $db->addQuotes( $this->store->smwIds->getSMWPropertyID( $subproperty ) );
+ }
+
+ $this->prepareValueQuery( $from, $where, $subproptable, $subvalue, $tableIndex );
+ }
+ }
+ } elseif ( !is_null( $value ) ) { // add conditions for given value
+ $diHandler = $this->store->getDataItemHandlerForDIType( $value->getDIType() );
+ foreach ( $diHandler->getWhereConds( $value ) as $fieldname => $value ) {
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.$fieldname=" . $db->addQuotes( $value );
+ }
+ }
+ }
+
+ /**
+ * @see SMWStore::getAllPropertySubjects
+ *
+ * @param SMWDIProperty $property
+ * @param SMWRequestOptions $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getAllPropertySubjects( SMWDIProperty $property, SMWRequestOptions $requestOptions = null ) {
+ return $this->getPropertySubjects( $property, null, $requestOptions );
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @param DIWikiPage $subject
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getProperties( DIWikiPage $subject, SMWRequestOptions $requestOptions = null ) {
+
+ $sid = $this->store->smwIds->getSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName()
+ );
+
+ // no id, no page, no properties
+ if ( $sid == 0 ) {
+ return [];
+ }
+
+ $subject->setId( $sid );
+ $result = [];
+
+ // Potentially need to get more results, since options apply to union
+ $lookupOptions = $this->propertiesLookup->newRequestOptions(
+ $requestOptions
+ );
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+
+ foreach ( $this->store->getPropertyTables() as $tid => $propertyTable ) {
+
+ if ( !array_key_exists( $propertyTable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ $res = $this->propertiesLookup->fetchFromTable(
+ $subject,
+ $propertyTable,
+ $lookupOptions
+ );
+
+ foreach ( $res as $row ) {
+ $result[] = new DIProperty(
+ isset( $row->smw_title ) ? $row->smw_title : $row
+ );
+ }
+ }
+
+ // apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * Implementation of SMWStore::getInProperties(). This function is meant to
+ * be used for finding properties that link to wiki pages.
+ *
+ * @since 1.8
+ * @see SMWStore::getInProperties
+ *
+ * @param SMWDataItem $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return DIProperty[]
+ */
+ public function getInProperties( SMWDataItem $value, SMWRequestOptions $requestOptions = null ) {
+
+ $result = [];
+ $diType = $value->getDIType();
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+
+ if ( $diType != $proptable->getDiType() ) {
+ continue;
+ }
+
+ $res = $this->traversalPropertyLookup->fetchFromTable(
+ $proptable,
+ $value,
+ $requestOptions
+ );
+
+ foreach ( $res as $row ) {
+ try {
+ $result[] = new SMW\DIProperty( $row->smw_title );
+ } catch (SMWDataItemException $e) {
+ // has been observed to happen (empty property title); cause unclear; ignore this data
+ }
+ }
+ }
+
+ // Apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php
new file mode 100644
index 00000000..bc8ef57e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Writers.php
@@ -0,0 +1,825 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\ChangePropListener;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\Deferred\ChangeTitleUpdate;
+use SMW\SemanticData;
+use SMW\Parameters;
+use SMW\SQLStore\PropertyStatisticsTable;
+use SMW\SQLStore\PropertyTableRowDiffer;
+
+/**
+ * Class Handling all the write and update methods for SMWSQLStore3.
+ *
+ * @note Writing may also require some reading operations. Operations that are
+ * only needed in helper methods of this class should be implemented here, not
+ * in SMWSQLStore3Readers.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3Writers {
+
+ /**
+ * The store used by this store writer.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ protected $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var PropertyTableRowDiffer
+ */
+ private $propertyTableRowDiffer;
+
+ /**
+ * @var PropertyTableUpdater
+ */
+ private $propertyTableUpdater;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ /**
+ * @var IdChanger
+ */
+ private $idChanger;
+
+ /**
+ * @since 1.8
+ *
+ * @param SMWSQLStore3 $parentStore
+ * @param SQLStoreFactory $factory
+ */
+ public function __construct( SMWSQLStore3 $parentStore, $factory ) {
+ $this->store = $parentStore;
+ $this->factory = $factory;
+ $this->propertyTableRowDiffer = $this->factory->newPropertyTableRowDiffer();
+ $this->propertyTableUpdater = $this->factory->newPropertyTableUpdater();
+ $this->semanticDataLookup = $this->factory->newSemanticDataLookup();
+ $this->idChanger = $this->factory->newIdChanger();
+ }
+
+ /**
+ * @see SMWStore::deleteSubject
+ *
+ * @since 1.8
+ * @param Title $title
+ */
+ public function deleteSubject( Title $title ) {
+
+ // @deprecated since 2.1, use 'SMW::SQLStore::BeforeDeleteSubjectComplete'
+ \Hooks::run( 'SMWSQLStore3::deleteSubjectBefore', [ $this->store, $title ] );
+
+ \Hooks::run( 'SMW::SQLStore::BeforeDeleteSubjectComplete', [ $this->store, $title ] );
+
+ // Fetch all possible matches (including any duplicates created by
+ // incomplete rollback or DB deadlock)
+ $idList = $this->store->getObjectIds()->findAllEntitiesThatMatch(
+ $title->getDBkey(),
+ $title->getNamespace()
+ );
+
+ $extensionList = array_flip( $idList );
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $emptySemanticData = new SemanticData( $subject );
+ $emptySemanticData->setOption( SemanticData::PROC_DELETE, true );
+
+ $subobjectListFinder = $this->factory->newSubobjectListFinder();
+
+ foreach ( $idList as $id ) {
+ $this->doDelete( $id, $subject, $subobjectListFinder, $extensionList );
+ $this->doDataUpdate( $emptySemanticData );
+
+ if ( $this->store->service( 'PropertyTableIdReferenceFinder' )->hasResidualPropertyTableReference( $id ) === false ) {
+ // Mark subject/subobjects with a special IW, the final removal is being
+ // triggered by the `EntityRebuildDispatcher`
+ $this->store->getObjectIds()->updateInterwikiField(
+ $id,
+ $subject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ } else {
+ // Convert the subject into a simple object instance
+ $this->store->getObjectIds()->setPropertyTableHashes(
+ $id,
+ null
+ );
+ }
+ }
+
+ $extensionList = array_keys( $extensionList );
+
+ $this->store->extensionData['delete.list'] = $extensionList;
+
+ // @deprecated since 2.1, use 'SMW::SQLStore::AfterDeleteSubjectComplete'
+ \Hooks::run( 'SMWSQLStore3::deleteSubjectAfter', [ $this->store, $title ] );
+
+ \Hooks::run( 'SMW::SQLStore::AfterDeleteSubjectComplete', [ $this->store, $title ] );
+ }
+
+ private function doDelete( $id, $subject, $subobjectListFinder, &$extensionList ) {
+
+ $this->semanticDataLookup->invalidateCache( $id );
+
+ if ( $subject->getNamespace() === SMW_NS_CONCEPT ) { // make sure to clear caches
+ $db = $this->store->getConnection();
+
+ $db->delete(
+ SMWSQLStore3::CONCEPT_TABLE,
+ [ 's_id' => $id ],
+ 'SMW::deleteSubject::Conc'
+ );
+
+ $db->delete(
+ SMWSQLStore3::CONCEPT_CACHE_TABLE,
+ [ 'o_id' => $id ],
+ 'SMW::deleteSubject::Conccache'
+ );
+ }
+
+ $subject->setId( $id );
+
+ foreach( $subobjectListFinder->find( $subject ) as $subobject ) {
+ $extensionList[$subobject->getId()] = true;
+
+ $this->store->getObjectIds()->updateInterwikiField(
+ $subobject->getId(),
+ $subobject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ /**
+ * @see SMWStore::doDataUpdate
+ *
+ * @since 1.8
+ * @param SMWSemanticData $data
+ */
+ public function doDataUpdate( SMWSemanticData $semanticData ) {
+ \Hooks::run( 'SMWSQLStore3::updateDataBefore', [ $this->store, $semanticData ] );
+
+ $subject = $semanticData->getSubject();
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $subobjectListFinder = $this->factory->newSubobjectListFinder();
+
+ $changeOp = $this->factory->newChangeOp(
+ $subject
+ );
+
+ $this->propertyTableRowDiffer->setChangeOp(
+ $changeOp
+ );
+
+ $changePropListener = $this->factory->newChangePropListener();
+ $hierarchyLookup = $this->factory->newHierarchyLookup();
+
+ // #2698
+ $hierarchyLookup->addListenersTo(
+ $changePropListener
+ );
+
+ $changePropListener->loadListeners(
+ $this->store
+ );
+
+ // Update data about our main subject
+ $this->doFlatDataUpdate( $semanticData );
+ $sid = $subject->getId();
+
+ // Update data about our subobjects
+ $subSemanticData = $semanticData->getSubSemanticData();
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ foreach( $subSemanticData as $subobjectData ) {
+ $this->doFlatDataUpdate( $subobjectData );
+ }
+
+ $deleteList = [];
+
+ // Mark subobjects without reference to be deleted
+ foreach( $subobjectListFinder->find( $subject ) as $subobject ) {
+ if( !$semanticData->hasSubSemanticData( $subobject->getSubobjectName() ) ) {
+
+ $this->doFlatDataUpdate( new SemanticData( $subobject ) );
+ $deleteList[] = $subobject->getId();
+
+ $this->store->getObjectIds()->updateInterwikiField(
+ $subobject->getId(),
+ $subobject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ if ( ( $rev_id = $semanticData->getExtensionData( 'revision_id' ) ) !== null ) {
+ $this->store->getObjectIds()->updateRevField( $sid, $rev_id );
+ }
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ // Store the diff in cache so any post processing has a chance to find
+ // what entities and values were changed
+ $changeDiff = $changeOp->newChangeDiff();
+ $changeDiff->save( ApplicationFactory::getInstance()->getCache() );
+
+ $changePropListener->callListeners();
+
+ $this->store->extensionData['delete.list'] = $deleteList;
+ $this->store->extensionData['change.diff'] = $changeDiff;
+
+ // Deprecated since 2.3, use SMW::SQLStore::AfterDataUpdateComplete
+ \Hooks::run( 'SMWSQLStore3::updateDataAfter', [ $this->store, $semanticData ] );
+
+ \Hooks::run( 'SMW::SQLStore::AfterDataUpdateComplete', [
+ $this->store,
+ $semanticData,
+ $changeOp
+ ] );
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ /**
+ * Update the store to contain the given data, without taking any
+ * subobject data into account.
+ *
+ * @since 1.8
+ * @param SMWSemanticData $data
+ */
+ protected function doFlatDataUpdate( SMWSemanticData $data ) {
+ $subject = $data->getSubject();
+
+ // Take care of redirects
+ $redirects = $data->getPropertyValues( new SMW\DIProperty( '_REDI' ) );
+
+ if ( count( $redirects ) > 0 ) {
+ $redirect = end( $redirects ); // at most one redirect per page
+ $this->updateRedirects(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $redirect->getDBkey(),
+ $redirect->getNameSpace()
+ );
+ // Stop here:
+ // * no support for annotations on redirect pages
+ // * updateRedirects takes care of deleting any previous data
+ return;
+ } else {
+ $this->updateRedirects(
+ $subject->getDBkey(),
+ $subject->getNamespace()
+ );
+ }
+
+ // Find an approriate sortkey, the field is influenced by various
+ // elements incl. DEFAULTSORT and can be altered without modifying
+ // any other annotation.
+ $sortKey = $this->makeSortKey( $subject, $data );
+
+ // Always fetch an ID which is either recalled from cache or is created.
+ // Doing so ensures that the sortkey and namespace data are updated
+ // to both the DB and the cache.
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ true,
+ $sortKey,
+ true
+ );
+
+ $subject->setSortKey( $sortKey );
+ $subject->setId( $sid );
+
+ // Find any potential duplicate entries for the current subject and
+ // if matched, mark them as to be deleted
+ $idList = $this->store->getObjectIds()->findAllEntitiesThatMatch(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName()
+ );
+
+ foreach ( $idList as $id ) {
+ if ( $id != $sid ) {
+ $this->store->getObjectIds()->updateInterwikiField(
+ $id,
+ $subject,
+ SMW_SQL3_SMWDELETEIW
+ );
+ }
+ }
+
+ // Take care of all remaining property table data
+ list( $insertRows, $deleteRows, $newHashes ) = $this->propertyTableRowDiffer->computeTableRowDiff(
+ $sid,
+ $data
+ );
+
+ $params = new Parameters(
+ [
+ 'insert_rows' => $insertRows,
+ 'delete_rows' => $deleteRows,
+ 'new_hashes' => $newHashes
+ ]
+ );
+
+ $this->propertyTableUpdater->update( $sid, $params );
+
+ if ( $redirects === [] && $subject->getSubobjectName() === '' ) {
+
+ $dataItemFromId = $this->store->getObjectIds()->getDataItemById( $sid );
+
+ // If for some reason the internal redirect marker is still set but no
+ // redirect annotations are known then do update the interwiki field
+ if ( $dataItemFromId !== null && $dataItemFromId->getInterwiki() === SMW_SQL3_SMWREDIIW ) {
+ $this->store->getObjectIds()->updateInterwikiField( $sid, $subject );
+ }
+ }
+
+ // Update caches (may be important if jobs are directly following this call)
+ $this->semanticDataLookup->setLookupCache( $sid, $data );
+ }
+
+ private function makeSortKey( $subject, $data ) {
+
+ // Don't mind the delete process
+ if ( $data->getOption( SemanticData::PROC_DELETE ) ) {
+ return '';
+ }
+
+ $property = new DIProperty( '_SKEY' );
+
+ // Take care of the sortkey
+ $pv = $data->getPropertyValues( $property );
+ $dataItem = end( $pv );
+
+ if ( $dataItem instanceof SMWDIBlob ) {
+ $sortkey = $dataItem->getString();
+ } elseif ( $data->getExtensionData( 'sort.extension' ) !== null ) {
+ $sortkey = $data->getExtensionData( 'sort.extension' );
+ } else {
+ $sortkey = $subject->getSortKey();
+ }
+
+ // Extend the subobject sortkey in case no @sortkey was given for an
+ // entity
+ if ( $subject->getSubobjectName() !== '' && !$dataItem instanceof SMWDIBlob ) {
+
+ // Add sort data from some dedicated containers (of a record or
+ // reference type etc.) otherwise use the sobj name as extension
+ // to distinguish each entity
+ if ( $data->getExtensionData( 'sort.data' ) !== null ) {
+ $sortkey .= '#' . $data->getExtensionData( 'sort.data' );
+ } else {
+ $sortkey .= '#' . $subject->getSubobjectName();
+ }
+ }
+
+ // #649 Be consistent about how sortkeys are stored therefore always
+ // normalize even for usages like {{DEFAULTSORT: Foo_bar }}
+ $sortkey = str_replace( '_', ' ', $sortkey );
+
+ return $sortkey;
+ }
+
+ /**
+ * Implementation of SMWStore::changeTitle(). In contrast to
+ * updateRedirects(), this function does not simply write a redirect
+ * from the old page to the new one, but also deletes all data that may
+ * already be stored for the new title (normally the new title should
+ * belong to an empty page that has no data but at least it could have a
+ * redirect to the old page), and moves all data that exists for the old
+ * title to the new location. Thus, the function executes three steps:
+ * delete data at newtitle, move data from oldtitle to newtitle, and set
+ * redirect from oldtitle to newtitle. In some cases, the goal can be
+ * achieved more efficiently, e.g. if the new title does not occur in SMW
+ * yet: then we can just change the ID records for the titles instead of
+ * changing all data tables
+ *
+ * Note that the implementation ignores the MediaWiki IDs since this
+ * store has its own ID management. Also, the function requires that both
+ * titles are local, i.e. have empty interwiki prefix.
+ *
+ * @todo Currently the sortkey is not moved with the remaining data. It is
+ * not possible to move it reliably in all cases: we cannot distinguish an
+ * unset sortkey from one that was set to the name of oldtitle. Maybe use
+ * update jobs right away?
+ *
+ * @since 1.8
+ *
+ * @param Title $oldTitle
+ * @param Title $newTitle
+ * @param integer $pageId
+ * @param integer $redirectId
+ */
+ public function changeTitle( Title $oldTitle, Title $newTitle, $pageId, $redirectId = 0 ) {
+ global $smwgQEqualitySupport;
+
+ \Hooks::run(
+ 'SMW::SQLStore::BeforeChangeTitleComplete',
+ [ $this->store, $oldTitle, $newTitle, $pageId, $redirectId ]
+ );
+
+ $db = $this->store->getConnection();
+
+ // get IDs but do not resolve redirects:
+ $sid = $this->store->getObjectIds()->getSMWPageID(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ $tid = $this->store->getObjectIds()->getSMWPageID(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ // Easy case: target not used anywhere yet, just hijack its title for our current id
+ if ( ( $tid == 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) {
+ // This condition may not hold even if $newtitle is
+ // currently unused/non-existing since we keep old IDs.
+ // If equality support is off, then this simple move
+ // does too much; fall back to general case below.
+ if ( $sid != 0 ) { // change id entry to refer to the new title
+ // Note that this also changes the reference for internal objects (subobjects)
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [
+ 'smw_title' => $newTitle->getDBkey(),
+ 'smw_namespace' => $newTitle->getNamespace(),
+ 'smw_iw' => ''
+ ],
+ [
+ 'smw_title' => $oldTitle->getDBkey(),
+ 'smw_namespace' => $oldTitle->getNamespace(),
+ 'smw_iw' => ''
+ ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->moveSubobjects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ '',
+ '',
+ 0,
+ ''
+ );
+
+ // We do not know the new sortkey, so just clear the cache:
+ $this->store->getObjectIds()->deleteCache(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ ''
+ );
+
+ } else { // make new (target) id for use in redirect table
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ ''
+ );
+ } // at this point, $sid is the id of the target page (according to the IDs table)
+
+ // make redirect id for oldtitle:
+ $this->store->getObjectIds()->makeSMWPageID(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ SMW_SQL3_SMWREDIIW,
+ ''
+ );
+
+ $this->store->getObjectIds()->addRedirect(
+ $sid,
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace()
+ );
+
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore();
+
+ $propertyStatisticsStore->addToUsageCount(
+ $this->store->getObjectIds()->getSMWPropertyID( new SMW\DIProperty( '_REDI' ) ),
+ 1
+ );
+
+ /// NOTE: there is the (bad) case that the moved page is a redirect. As chains of
+ /// redirects are not supported by MW or SMW, the above is maximally correct in this case too.
+ /// NOTE: this temporarily leaves existing redirects to oldtitle point to newtitle as well, which
+ /// will be lost after the next update. Since double redirects are an error anyway, this is not
+ /// a bad behavior: everything will continue to work until the existing redirects are updated,
+ /// which will hopefully be done to fix the double redirect.
+ } else { // General move method: should always be correct
+ // (equality support respected when updating redirects)
+
+ // Delete any existing data (including redirects) from old title
+ $emptyNewSemanticData = new SMWSemanticData( SMWDIWikiPage::newFromTitle( $oldTitle ) );
+ $this->doDataUpdate( $emptyNewSemanticData );
+
+ // Move all data of old title to new position:
+ if ( $sid != 0 ) {
+ $this->idChanger->change(
+ $sid,
+ $tid,
+ $oldTitle->getNamespace(),
+ $newTitle->getNamespace(),
+ true,
+ false
+ );
+ }
+
+ // Associate internal objects (subobjects) with the new title:
+ $table = $db->tableName( SMWSql3SmwIds::TABLE_NAME );
+
+ $values = [
+ 'smw_title' => $newTitle->getDBkey(),
+ 'smw_namespace' => $newTitle->getNamespace(),
+ 'smw_iw' => ''
+ ];
+
+ $sql = "UPDATE $table SET " . $db->makeList( $values, LIST_SET ) .
+ ' WHERE smw_title = ' . $db->addQuotes( $oldTitle->getDBkey() ) . ' AND ' .
+ 'smw_namespace = ' . $db->addQuotes( $oldTitle->getNamespace() ) . ' AND ' .
+ 'smw_iw = ' . $db->addQuotes( '' ) . ' AND ' .
+ 'smw_subobject != ' . $db->addQuotes( '' ); // The "!=" is why we cannot use MW array syntax here
+
+ $db->query( $sql, __METHOD__ );
+
+ $this->store->getObjectIds()->moveSubobjects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ // $redirid == 0 means that the oldTitle was not supposed to be a redirect
+ // (oldTitle is delete from the db) but instead of deleting all
+ // references we will still copy data from old to new during updateRedirects()
+ // and clear the semantic data container for the oldTitle instance
+ // to ensure that no ghost references exists for an deleted oldTitle
+ // @see Title::moveTo(), createRedirect
+ if ( $redirectId == 0 ) {
+
+ // Delete any existing data (including redirects) from old title
+ $this->updateRedirects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace()
+ );
+
+ } else {
+
+ // Write a redirect from old title to new one:
+ // (this also updates references in other tables as needed.)
+ // TODO: may not be optimal for the standard case that newtitle
+ // existed and redirected to oldtitle (PERFORMANCE)
+ $this->updateRedirects(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace()
+ );
+
+ }
+
+ }
+
+ if ( $redirectId == 0 ) {
+ $oldTitle = null;
+ }
+
+ ChangeTitleUpdate::addUpdate( $oldTitle, $newTitle );
+ }
+
+ /**
+ * Helper method to write information about some redirect. Various updates
+ * can be necessary if redirects are resolved as identities in SMW. The
+ * title and namespace of the affected page and of its updated redirect
+ * target are given. The target can be empty ('') to delete any redirect.
+ * Returns the canonical ID that is now to be used for the subject.
+ *
+ * This method does not change the ids of the affected pages, and thus it
+ * is not concerned with updates of the data that is currently stored for
+ * the subject. Normally, a subject that is a redirect will not have other
+ * data, but this method does not depend on this.
+ *
+ * @note Please make sure you fully understand this code before making any
+ * changes here. Keeping the redirect structure consistent is important,
+ * and errors in this code can go unnoticed for quite some time.
+ *
+ * @note This method merely handles the addition or deletion of a redirect
+ * statement in the wiki. It does not assume that any page contents has
+ * been changed (e.g. moved). See changeTitle() for additional handling in
+ * this case.
+ *
+ * @todo Clean up this code.
+ *
+ * @since 1.8
+ * @param string $subject_t
+ * @param integer $subject_ns
+ * @param string $curtarget_t
+ * @param integer $curtarget_ns
+ * @return integer the new canonical ID of the subject
+ */
+ protected function updateRedirects( $subject_t, $subject_ns, $curtarget_t = '', $curtarget_ns = -1 ) {
+ global $smwgQEqualitySupport;
+
+ $count = 0; //track count changes for redi property
+ $db = $this->store->getConnection();
+
+ // *** First get id of subject, old redirect target, and current (new) redirect target ***//
+
+ $sid_sort = '';
+
+ // find real id of subject, if any
+ $sid = $this->store->getObjectIds()->getSMWPageIDandSort(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ $sid_sort,
+ false
+ );
+
+ /// NOTE: $sid can be 0 here; this is useful to know since it means that fewer table updates are needed
+ $new_tid = $curtarget_t ? ( $this->store->getObjectIds()->makeSMWPageID( $curtarget_t, $curtarget_ns, '', '', false ) ) : 0; // real id of new target, if given
+
+ $old_tid = $this->store->getObjectIds()->findRedirect(
+ $subject_t,
+ $subject_ns
+ );
+
+ /// NOTE: $old_tid and $new_tid both (intentionally) ignore further redirects: no redirect chains
+
+ if ( $old_tid == $new_tid ) { // no change, all happy
+ return ( $new_tid == 0 ) ? $sid : $new_tid;
+ } // note that this means $old_tid != $new_tid in all cases below
+
+ // *** Make relevant changes in property tables (don't write the new redirect yet) ***//
+ $jobs = [];
+
+ if ( ( $old_tid == 0 ) && ( $sid != 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) { // new redirect
+ // $smwgQEqualitySupport requires us to change all tables' page references from $sid to $new_tid.
+ // Since references must not be 0, we don't have to do this is $sid == 0.
+ $this->idChanger->change(
+ $sid,
+ $new_tid,
+ $subject_ns,
+ $curtarget_ns,
+ false,
+ true
+ );
+
+ } elseif ( $old_tid != 0 ) { // existing redirect is changed or deleted
+
+ $count--;
+
+ $this->store->getObjectIds()->updateRedirect(
+ $old_tid,
+ $subject_t,
+ $subject_ns
+ );
+ }
+
+ // *** Finally, write the new redirect data ***//
+
+ if ( $new_tid != 0 ) { // record a new redirect
+ // Redirecting done right:
+ // (1) make a new ID with iw SMW_SQL3_SMWREDIIW or
+ // change iw field of current ID in this way,
+ // (2) write smw_fpt_redi table,
+ // (3) update canonical cache.
+ // This order must be obeyed unless you really understand what you are doing!
+
+ if ( ( $old_tid == 0 ) && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) {
+ // mark subject as redirect (if it was no redirect before)
+ if ( $sid == 0 ) { // every redirect page must have an ID
+ $sid = $this->store->getObjectIds()->makeSMWPageID(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ false
+ );
+ } else {
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [ 'smw_iw' => SMW_SQL3_SMWREDIIW ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ 0,
+ ''
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ $sid,
+ $sid_sort
+ );
+ }
+ }
+
+ $this->store->getObjectIds()->addRedirect(
+ $new_tid,
+ $subject_t,
+ $subject_ns
+ );
+
+ $count++;
+
+ } else { // delete old redirect
+ // This case implies $old_tid != 0 (or we would have new_tid == old_tid above).
+ // Therefore $subject had a redirect, and it must also have an ID.
+ // This shows that $sid != 0 here.
+ if ( $smwgQEqualitySupport != SMW_EQ_NONE ) { // mark subject as non-redirect
+
+ $db->update(
+ SMWSql3SmwIds::TABLE_NAME,
+ [ 'smw_iw' => '' ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ SMW_SQL3_SMWREDIIW,
+ '',
+ 0,
+ ''
+ );
+
+ $this->store->getObjectIds()->setCache(
+ $subject_t,
+ $subject_ns,
+ '',
+ '',
+ $sid,
+ $sid_sort
+ );
+ }
+ }
+
+ // *** Flush some caches to be safe, though they are not essential in runs with redirect updates ***//
+ $this->semanticDataLookup->invalidateCache( $sid );
+ $this->semanticDataLookup->invalidateCache( $new_tid );
+ $this->semanticDataLookup->invalidateCache( $old_tid );
+
+ // *** Update reference count for _REDI property ***//
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore();
+
+ $propertyStatisticsStore->addToUsageCount(
+ $this->store->getObjectIds()->getSMWPropertyID( new SMW\DIProperty( '_REDI' ) ),
+ $count
+ );
+
+ return ( $new_tid == 0 ) ? $sid : $new_tid;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php
new file mode 100644
index 00000000..b8c402b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_Sql3SmwIds.php
@@ -0,0 +1,1296 @@
+<?php
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Collator;
+use SMW\PropertyRegistry;
+use SMW\RequestOptions;
+use SMW\SQLStore\EntityStore\IdCacheManager;
+use SMW\SQLStore\IdToDataItemMatchFinder;
+use SMW\SQLStore\RedirectStore;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\SQLStoreFactory;
+use SMW\SQLStore\TableFieldUpdater;
+use SMWDataItem as DataItem;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\Connection\Sequence;
+use SMW\TypesRegistry;
+
+/**
+ * @ingroup SMWStore
+ * @since 1.8
+ * @author Markus Krötzsch
+ */
+
+/**
+ * Class to access the SMW IDs table in SQLStore3.
+ * Provides transparent in-memory caching facilities.
+ *
+ * Documentation for the SMW IDs table: This table is a dictionary that
+ * assigns integer IDs to pages, properties, and other objects used by SMW.
+ * All tables that refer to such objects store these IDs instead. If the ID
+ * information is lost (e.g., table gets deleted), then the data stored in SMW
+ * is no longer meaningful: all tables need to be dropped, recreated, and
+ * refreshed to get back to a working database.
+ *
+ * The table has a column for storing interwiki prefixes, used to refer to
+ * pages on external sites (like in MediaWiki). This column is also used to
+ * mark some special objects in the table, using "interwiki prefixes" that
+ * cannot occur in MediaWiki:
+ *
+ * - Rows with iw SMW_SQL3_SMWREDIIW are similar to normal entries for
+ * (internal) wiki pages, but the iw indicates that the page is a redirect, the
+ * (target of which should be sought using the smw_fpt_redi table.
+ *
+ * - The (unique) row with iw SMW_SQL3_SMWBORDERIW just marks the border
+ * between predefined ids (rows that are reserved for hardcoded ids built into
+ * SMW) and normal entries. It is no object, but makes sure that SQL's auto
+ * increment counter is high enough to not add any objects before that marked
+ * "border".
+ *
+ * @note Do not call the constructor of SMWDIWikiPage using data from the SMW
+ * IDs table; use SMWDIHandlerWikiPage::dataItemFromDBKeys() instead. The table
+ * does not always contain data as required wiki pages. Especially predefined
+ * properties are represented by language-independent keys rather than proper
+ * titles. SMWDIHandlerWikiPage takes care of this.
+ *
+ * @since 1.8
+ *
+ * @ingroup SMWStore
+ */
+class SMWSql3SmwIds {
+
+ /**
+ * Specifies the border limit for pre-defined properties declared
+ * in SMWSql3SmwIds::special_ids
+ */
+ const FXD_PROP_BORDER_ID = SMWSQLStore3::FIXED_PROPERTY_ID_UPPERBOUND;
+
+ /**
+ * Name of the table to store IDs in.
+ *
+ * @note This should never change. Existing wikis will have to drop and
+ * rebuild their SMW tables completely to recover from any change here.
+ */
+ const TABLE_NAME = SMWSQLStore3::ID_TABLE;
+
+ const MAX_CACHE_SIZE = 1000;
+ const POOLCACHE_ID = 'smw.sqlstore';
+
+ /**
+ * Parent SMWSQLStore3.
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ public $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var IdToDataItemMatchFinder
+ */
+ private $idMatchFinder;
+
+ /**
+ * @var RedirectStore
+ */
+ private $redirectStore;
+
+ /**
+ * @var TableFieldUpdater
+ */
+ private $tableFieldUpdater;
+
+ /**
+ * @var array
+ */
+ public static $special_ids = [];
+
+ /**
+ * @var IdCacheManager
+ */
+ private $idCacheManager;
+
+ /**
+ * @var IdEntityFinder
+ */
+ private $idEntityFinder;
+
+ /**
+ * @var IdChanger
+ */
+ private $idChanger;
+
+ /**
+ * @var UniquenessLookup
+ */
+ private $uniquenessLookup;
+
+ /**
+ * @since 1.8
+ * @param SMWSQLStore3 $store
+ */
+ public function __construct( SMWSQLStore3 $store, SQLStoreFactory $factory ) {
+ $this->store = $store;
+ $this->factory = $factory;
+ $this->initCache();
+
+ $this->idEntityFinder = $this->factory->newIdEntityFinder(
+ $this->idCacheManager
+ );
+
+ $this->redirectStore = $this->factory->newRedirectStore();
+ $this->uniquenessLookup = $this->factory->newUniquenessLookup();
+
+ $this->tableFieldUpdater = $this->factory->newTableFieldUpdater();
+ $this->idChanger = $this->factory->newIdChanger();
+
+ self::$special_ids = TypesRegistry::getFixedPropertyIdList();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return boolean
+ */
+ public function isRedirect( DIWikiPage $subject ) {
+ return $this->redirectStore->isRedirect( $subject->getDBKey(), $subject->getNamespace() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ public function isUnique( DataItem $dataItem ) {
+ return $this->uniquenessLookup->isUnique( $dataItem );
+ }
+
+ /**
+ * @see RedirectStore::findRedirect
+ *
+ * @since 2.1
+ *
+ * @param string $title DB key
+ * @param integer $namespace
+ *
+ * @return integer
+ */
+ public function findRedirect( $title, $namespace ) {
+ return $this->redirectStore->findRedirect( $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::addRedirect
+ *
+ * @since 2.1
+ *
+ * @param integer $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function addRedirect( $id, $title, $namespace ) {
+ $this->redirectStore->addRedirect( $id, $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::updateRedirect
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function updateRedirect( $id, $title, $namespace ) {
+ $this->redirectStore->updateRedirect( $id, $title, $namespace );
+ }
+
+ /**
+ * @see RedirectStore::deleteRedirect
+ *
+ * @since 2.1
+ *
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function deleteRedirect( $title, $namespace ) {
+ $this->redirectStore->deleteRedirect( $title, $namespace );
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given title,
+ * namespace, interwiki, and subobject. If $canonical is set to true,
+ * redirects are taken into account to find the canonical alias ID for
+ * the given page. If no such ID exists, 0 is returned. The Call-By-Ref
+ * parameter $sortkey is set to the current sortkey, or to '' if no ID
+ * exists.
+ *
+ * If $fetchhashes is true, the property table hash blob will be
+ * retrieved in passing if the opportunity arises, and cached
+ * internally. This will speed up a subsequent call to
+ * getPropertyTableHashes() for this id. This should only be done
+ * if such a call is intended, both to safe the previous cache and
+ * to avoid extra work (even if only a little) to fill it.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes = false ) {
+ $id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
+ if ( $id != 0 ) {
+ return (int)$id;
+ } else {
+ return (int)$this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $sortkey, $canonical, $fetchHashes );
+ }
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given normalized title,
+ * namespace, interwiki, and subobjectName. Predefined IDs are not
+ * taken into account (however, they would still be found correctly by
+ * an avoidable database read if they are stored correctly in the
+ * database; this should always be the case). In all other aspects, the
+ * method works just like getSMWPageIDandSort().
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ protected function getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes ) {
+ global $smwgQEqualitySupport;
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // Integration test "query-04-02-subproperty-dc-import-marc21.json"
+ // showed a deterministic failure (due to a wrong cache id during querying
+ // for redirects) hence we force to read directly from the RedirectStore
+ // for objects marked as redirect
+ if ( $iw === SMW_SQL3_SMWREDIIW && $canonical &&
+ $smwgQEqualitySupport !== SMW_EQ_NONE && $subobjectName === '' ) {
+ $id = $this->findRedirect( $title, $namespace );
+ } else {
+ $id = $this->idCacheManager->getId( [ $title, (int)$namespace, $iw, $subobjectName ] );
+ }
+
+ if ( $id !== false && $id != 0 ) { // cache hit
+ $sortkey = $this->idCacheManager->getSort( [ $title, (int)$namespace, $iw, $subobjectName ] );
+ } elseif ( $iw == SMW_SQL3_SMWREDIIW && $canonical &&
+ $smwgQEqualitySupport != SMW_EQ_NONE && $subobjectName === '' ) {
+ $id = $this->findRedirect( $title, $namespace );
+ if ( $id != 0 ) {
+
+ if ( $fetchHashes ) {
+ $select = [ 'smw_sortkey', 'smw_sort', 'smw_proptable_hash' ];
+ } else {
+ $select = [ 'smw_sortkey', 'smw_sort' ];
+ }
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ $select,
+ [ 'smw_id' => $id ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ // Make sure that smw_sort is being re-computed in case it is null
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash );
+ }
+ } else { // inconsistent DB; just recover somehow
+ $sortkey = str_replace( '_', ' ', $title );
+ }
+ } else {
+ $sortkey = '';
+ }
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+ } else {
+
+ if ( $fetchHashes ) {
+ $select = [ 'smw_id', 'smw_sortkey', 'smw_sort', 'smw_proptable_hash' ];
+ } else {
+ $select = [ 'smw_id', 'smw_sortkey', 'smw_sort' ];
+ }
+
+ // #2001
+ // In cases where title components are excessively long (beyond the
+ // field limit) it has been observed that at least on MySQL/MariaDB no
+ // appropriate matches are found even though a row with a truncated
+ // representation exists in the table.
+ //
+ // `postgres` has no field limit and a divergent behaviour has not
+ // been observed
+ if ( $subobjectName !== '' && !$db->isType( 'postgres' ) ) {
+ $subobjectName = mb_substr( $subobjectName, 0, 255 );
+ }
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ $select,
+ [
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName
+ ],
+ __METHOD__
+ );
+
+ //$this->selectrow_sort_debug++;
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+ // Make sure that smw_sort is being re-computed in case it is null
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash);
+ }
+ } else {
+ $id = 0;
+ $sortkey = '';
+ }
+
+ $this->setCache(
+ $title,
+ $namespace,
+ $iw,
+ $subobjectName,
+ $id,
+ $sortkey
+ );
+ }
+
+ if ( $id == 0 && $subobjectName === '' && $iw === '' ) { // could be a redirect; check
+ $id = $this->getSMWPageIDandSort(
+ $title,
+ $namespace,
+ SMW_SQL3_SMWREDIIW,
+ $subobjectName,
+ $sortkey,
+ $canonical,
+ $fetchHashes
+ );
+ }
+
+ return $id;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function findDuplicates() {
+ return $this->uniquenessLookup->findDuplicates();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string|null $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ *
+ * @param array
+ */
+ public function findAllEntitiesThatMatch( $title, $namespace, $iw = null, $subobjectName = '' ) {
+
+ $matches = [];
+ $query = [];
+
+ $query['fields'] = ['smw_id'];
+
+ $query['conditions'] = [
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName
+ ];
+
+ // Null means select all (incl. those marked delete, redi etc.)
+ if ( $iw === null ) {
+ unset( $query['conditions']['smw_iw'] );
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $rows = $connection->select(
+ $connection->tableName( self::TABLE_NAME ),
+ $query['fields'],
+ $query['conditions'],
+ __METHOD__
+ );
+
+ if ( $rows === false ) {
+ return $matches;
+ }
+
+ foreach ( $rows as $row ) {
+ $matches[] = (int)$row->smw_id;
+ }
+
+ return $matches;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ *
+ * @param boolean
+ */
+ public function exists( DIWikiPage $subject ) {
+ return $this->getId( $subject ) > 0;
+ }
+
+ /**
+ * @note SMWSql3SmwIds::getSMWPageID has some issues with the cache as it returned
+ * 0 even though an object was matchable, using this method is safer then trying
+ * to encipher getSMWPageID related methods.
+ *
+ * It uses the PoolCache which means Lru is in place to avoid memory leakage.
+ *
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ *
+ * @param integer
+ */
+ public function getId( DIWikiPage $subject ) {
+
+ // Try to match a predefined property
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY && $subject->getInterWiki() === '' ) {
+ $property = DIProperty::newFromUserLabel( $subject->getDBKey() );
+ $key = $property->getKey();
+
+ // Has a fixed ID?
+ if ( isset( self::$special_ids[$key] ) && $subject->getSubobjectName() === '' ) {
+ return self::$special_ids[$key];
+ }
+
+ // Switch title for fixed properties without a fixed ID (e.g. _MIME is the smw_title)
+ if ( !$property->isUserDefined() ) {
+ $subject = new DIWikiPage(
+ $key,
+ SMW_NS_PROPERTY,
+ $subject->getInterWiki(),
+ $subject->getSubobjectName()
+ );
+ }
+ }
+
+ if ( ( $id = $this->idCacheManager->getId( $subject ) ) !== false ) {
+ return $id;
+ }
+
+ $id = 0;
+
+ $row = $this->store->getConnection( 'mw.db' )->selectRow(
+ self::TABLE_NAME,
+ [ 'smw_id' ],
+ [
+ 'smw_title' => $subject->getDBKey(),
+ 'smw_namespace' => $subject->getNamespace(),
+ 'smw_iw' => $subject->getInterWiki(),
+ 'smw_subobject' => $subject->getSubobjectName()
+ ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+
+ // Legacy
+ $this->setCache(
+ $subject->getDBKey(),
+ $subject->getNamespace(),
+ $subject->getInterWiki(),
+ $subject->getSubobjectName(),
+ $id,
+ $subject->getSortKey()
+ );
+ }
+
+ return $id;
+ }
+
+ /**
+ * Convenience method for calling getSMWPageIDandSort without
+ * specifying a sortkey (if not asked for).
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function getSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $fetchHashes = false ) {
+ $sort = '';
+ return $this->getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, $sort, $canonical, $fetchHashes );
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given title, namespace,
+ * interwiki, and subobjectName. If $canonical is set to true,
+ * redirects are taken into account to find the canonical alias ID for
+ * the given page. If no such ID exists, a new ID is created and
+ * returned. In any case, the current sortkey is set to the given one
+ * unless $sortkey is empty.
+ *
+ * @note Using this with $canonical==false can make sense, especially when
+ * the title is a redirect target (we do not want chains of redirects).
+ * But it is of no relevance if the title does not have an id yet.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ public function makeSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $sortkey = '', $fetchHashes = false ) {
+ $id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
+ if ( $id != 0 ) {
+ return (int)$id;
+ } else {
+ return (int)$this->makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes );
+ }
+ }
+
+ /**
+ * Find the numeric ID used for the page of the given normalized title,
+ * namespace, interwiki, and subobjectName. Predefined IDs are not
+ * taken into account (however, they would still be found correctly by
+ * an avoidable database read if they are stored correctly in the
+ * database; this should always be the case). In all other aspects, the
+ * method works just like makeSMWPageID(). Especially, if no ID exists,
+ * a new ID is created and returned.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName name of subobject
+ * @param boolean $canonical should redirects be resolved?
+ * @param string $sortkey call-by-ref will be set to sortkey
+ * @param boolean $fetchHashes should the property hashes be obtained and cached?
+ * @return integer SMW id or 0 if there is none
+ */
+ protected function makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes ) {
+
+ $oldsort = '';
+ $id = $this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $oldsort, $canonical, $fetchHashes );
+ $db = $this->store->getConnection( 'mw.db' );
+ $collator = Collator::singleton();
+
+ // Safeguard to ensure that no duplicate IDs are created
+ if ( $id == 0 ) {
+ $id = $this->getId( new DIWikiPage( $title, $namespace, $iw, $subobjectName ) );
+ }
+
+ $db->beginAtomicTransaction( __METHOD__ );
+
+ if ( $id == 0 ) {
+ $sortkey = $sortkey ? $sortkey : ( str_replace( '_', ' ', $title ) );
+
+ // Bug 42659
+ $sequenceValue = $db->nextSequenceValue(
+ Sequence::makeSequence( SQLStore::ID_TABLE, 'smw_id' )
+ );
+
+ // #2089 (MySQL 5.7 complained with "Data too long for column")
+ $sortkey = mb_substr( $sortkey, 0, 254 );
+
+ $db->insert(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $sequenceValue,
+ 'smw_title' => $title,
+ 'smw_namespace' => $namespace,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => $subobjectName,
+ 'smw_sortkey' => $sortkey,
+ 'smw_sort' => $collator->getSortKey( $sortkey ),
+ 'smw_hash' => $this->computeSha1( [ $title, (int)$namespace, $iw, $subobjectName ] )
+ ],
+ __METHOD__
+ );
+
+ $id = (int)$db->insertId();
+
+ // Properties also need to be in the property statistics table
+ if( $namespace === SMW_NS_PROPERTY ) {
+
+ $propertyStatisticsStore = $this->factory->newPropertyStatisticsStore(
+ $db
+ );
+
+ $propertyStatisticsStore->insertUsageCount( $id, 0 );
+ }
+
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+
+ if ( $fetchHashes ) {
+ $this->setPropertyTableHashesCache( $id, null );
+ }
+
+ } elseif ( $sortkey !== '' && ( $sortkey != $oldsort || !$collator->isIdentical( $oldsort, $sortkey ) ) ) {
+ $this->tableFieldUpdater->updateSortField( $id, $sortkey );
+ $this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
+ }
+
+ $db->endAtomicTransaction( __METHOD__ );
+
+ return $id;
+ }
+
+ /**
+ * Properties have a mechanisms for being predefined (i.e. in PHP instead
+ * of in wiki). Special "interwiki" prefixes separate the ids of such
+ * predefined properties from the ids for the current pages (which may,
+ * e.g., be moved, while the predefined object is not movable).
+ *
+ * @todo This documentation is out of date. Right now, the special
+ * interwiki is used only for special properties without a label, i.e.,
+ * which cannot be shown to a user. This allows us to filter such cases
+ * from all queries that retrieve lists of properties. It should be
+ * checked that this is really the only use that this has throughout
+ * the code.
+ *
+ * @since 1.8
+ * @param SMWDIProperty $property
+ * @return string
+ */
+ public function getPropertyInterwiki( SMWDIProperty $property ) {
+ return ( $property->getLabel() !== '' ) ? '' : SMW_SQL3_SMWINTDEFIW;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $sid
+ * @param DIWikiPage $subject
+ * @param integer|string|null $interwiki
+ */
+ public function updateInterwikiField( $sid, DIWikiPage $subject, $interwiki = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( $interwiki === null ) {
+ $interwiki = $subject->getInterWiki();
+ }
+
+ $hash = [
+ $subject->getDBKey(),
+ (int)$subject->getNamespace(),
+ $interwiki,
+ $subject->getSubobjectName()
+ ];
+
+ $connection->update(
+ self::TABLE_NAME,
+ [
+ 'smw_iw' => $interwiki,
+ 'smw_hash' => $this->computeSha1( $hash )
+ ],
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->setCache(
+ $subject->getDBKey(),
+ $subject->getNamespace(),
+ $subject->getInterWiki(),
+ $subject->getSubobjectName(),
+ $sid,
+ $subject->getSortKey()
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $title
+ * @param integer $namespace
+ * @param string $iw
+ */
+ public function findAssociatedRev( $title, $namespace, $iw = '' ) {
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ self::TABLE_NAME,
+ 'smw_rev',
+ [
+ "smw_title =" . $connection->addQuotes( $title ),
+ "smw_namespace =" . $connection->addQuotes( $namespace ),
+ "smw_iw =" . $connection->addQuotes( $iw ),
+ "smw_subobject =''"
+ ],
+ __METHOD__
+ );
+
+ return $row === false ? 0 : $row->smw_rev;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $sid
+ * @param integer $sid
+ */
+ public function updateRevField( $sid, $rev_id ) {
+ $this->tableFieldUpdater->updateRevField( $sid, $rev_id );
+ }
+
+ /**
+ * Fetch the ID for an SMWDIProperty object. This method achieves the
+ * same as getSMWPageID(), but avoids additional normalization steps
+ * that have already been performed when creating an SMWDIProperty
+ * object.
+ *
+ * @note There is no distinction between properties and inverse
+ * properties here. A property and its inverse have the same ID in SMW.
+ *
+ * @param SMWDIProperty $property
+ * @return integer
+ */
+ public function getSMWPropertyID( SMWDIProperty $property ) {
+ if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
+ return self::$special_ids[$property->getKey()];
+ } else {
+ $sortkey = '';
+ return $this->getDatabaseIdAndSort( $property->getKey(), SMW_NS_PROPERTY, $this->getPropertyInterwiki( $property ), '', $sortkey, true, false );
+ }
+ }
+
+ /**
+ * Fetch and possibly create the ID for an SMWDIProperty object. The
+ * method achieves the same as getSMWPageID() but avoids additional
+ * normalization steps that have already been performed when creating
+ * an SMWDIProperty object.
+ *
+ * @see getSMWPropertyID
+ * @param SMWDIProperty $property
+ * @return integer
+ */
+ public function makeSMWPropertyID( SMWDIProperty $property ) {
+ if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
+ return (int)self::$special_ids[$property->getKey()];
+ } else {
+ return (int)$this->makeDatabaseId(
+ $property->getKey(),
+ SMW_NS_PROPERTY,
+ $this->getPropertyInterwiki( $property ),
+ '',
+ true,
+ $property->getLabel(),
+ false
+ );
+ }
+ }
+
+ /**
+ * Normalize the information for an SMW object (page etc.) and return
+ * the predefined ID if any. All parameters are call-by-reference and
+ * will be changed to perform any kind of built-in normalization that
+ * SMW requires. This mainly applies to predefined properties that
+ * should always use their property key as a title, have fixed
+ * sortkeys, etc. Some very special properties also have fixed IDs that
+ * do not require any DB lookups. In such cases, the method returns
+ * this ID; otherwise it returns 0.
+ *
+ * @note This function could be extended to account for further kinds
+ * of normalization and predefined ID. However, both getSMWPropertyID
+ * and makeSMWPropertyID must then also be adjusted to do the same.
+ *
+ * @since 1.8
+ * @param string $title DB key
+ * @param integer $namespace namespace
+ * @param string $iw interwiki prefix
+ * @param string $subobjectName
+ * @param string $sortkey
+ * @return integer predefined id or 0 if none
+ */
+ protected function getPredefinedData( &$title, &$namespace, &$iw, &$subobjectName, &$sortkey ) {
+ if ( $namespace == SMW_NS_PROPERTY &&
+ ( $iw === '' || $iw == SMW_SQL3_SMWINTDEFIW ) && $title != '' ) {
+
+ // Check if this is a predefined property:
+ if ( $title{0} != '_' ) {
+ // This normalization also applies to
+ // subobjects of predefined properties.
+ $newTitle = PropertyRegistry::getInstance()->findPropertyIdByLabel( str_replace( '_', ' ', $title ) );
+ if ( $newTitle ) {
+ $title = $newTitle;
+ $sortkey = PropertyRegistry::getInstance()->findPropertyLabelById( $title );
+ if ( $sortkey === '' ) {
+ $iw = SMW_SQL3_SMWINTDEFIW;
+ }
+ }
+ }
+
+ // Check if this is a property with a fixed SMW ID:
+ if ( $subobjectName === '' && array_key_exists( $title, self::$special_ids ) ) {
+ return self::$special_ids[$title];
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Change an internal id to another value. If no target value is given, the
+ * value is changed to become the last id entry (based on the automatic id
+ * increment of the database). Whatever currently occupies this id will be
+ * moved consistently in all relevant tables. Whatever currently occupies
+ * the target id will be ignored (it should be ensured that nothing is
+ * moved to an id that is still in use somewhere).
+ *
+ * @since 1.8
+ * @param integer $curid
+ * @param integer $targetid
+ */
+ public function moveSMWPageID( $curid, $targetid = 0 ) {
+ $db = $this->store->getConnection();
+
+ $row = $db->selectRow(
+ self::TABLE_NAME,
+ '*',
+ [ 'smw_id' => $curid ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ return; // no id at current position, ignore
+ }
+
+ $db->beginAtomicTransaction( __METHOD__ );
+
+ if ( $targetid == 0 ) { // append new id
+
+ // Bug 42659
+ $sequenceValue = $db->nextSequenceValue(
+ Sequence::makeSequence( SQLStore::ID_TABLE, 'smw_id' )
+ );
+
+ $db->insert(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $sequenceValue,
+ 'smw_title' => $row->smw_title,
+ 'smw_namespace' => $row->smw_namespace,
+ 'smw_iw' => $row->smw_iw,
+ 'smw_subobject' => $row->smw_subobject,
+ 'smw_sortkey' => $row->smw_sortkey,
+ 'smw_sort' => $row->smw_sort
+ ],
+ __METHOD__
+ );
+
+ $targetid = $db->insertId();
+ } else { // change to given id
+ $db->insert(
+ self::TABLE_NAME,
+ [ 'smw_id' => $targetid,
+ 'smw_title' => $row->smw_title,
+ 'smw_namespace' => $row->smw_namespace,
+ 'smw_iw' => $row->smw_iw,
+ 'smw_subobject' => $row->smw_subobject,
+ 'smw_sortkey' => $row->smw_sortkey,
+ 'smw_sort' => $row->smw_sort
+ ],
+ __METHOD__
+ );
+ }
+
+ $db->delete(
+ self::TABLE_NAME,
+ [
+ 'smw_id' => $curid
+ ],
+ __METHOD__
+ );
+
+ $this->setCache(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject,
+ $targetid,
+ $row->smw_sortkey
+ );
+
+ $this->idChanger->change(
+ $curid,
+ $targetid,
+ $row->smw_namespace,
+ $row->smw_namespace
+ );
+
+ $db->endAtomicTransaction( __METHOD__ );
+
+ if ( ( $title = \Title::newFromText( $row->smw_title, $row->smw_namespace ) ) !== null ) {
+ $updateJob = new UpdateJob(
+ $title
+ );
+
+ $updateJob->insert();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|array $args
+ *
+ * @return string
+ */
+ public function computeSha1( $args = '' ) {
+ return IdCacheManager::computeSha1( $args );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $list
+ */
+ public function warmUpCache( $list = [] ) {
+
+ $hashList = [];
+
+ if ( $list instanceof \SMWQueryResult ) {
+ $list = $list->getResults();
+ }
+
+ if ( !$list instanceof \Iterator && !is_array( $list ) ) {
+ return;
+ }
+
+ foreach ( $list as $item ) {
+
+ $hash = null;
+
+ if ( $item instanceof DIWikiPage ) {
+ $hash = [
+ $item->getDBKey(),
+ (int)$item->getNamespace(),
+ $item->getInterwiki(),
+ $item->getSubobjectName()
+ ];
+ }
+
+ if ( $item instanceof DIProperty ) {
+
+ // Avoid _SKEY as it is not used during an entity lookup to
+ // match an ID
+ if ( $item->getKey() === '_SKEY' ) {
+ continue;
+ }
+
+ $hash = [ $item->getKey(), SMW_NS_PROPERTY, '', '' ];
+ }
+
+ if ( $hash === null ) {
+ continue;
+ }
+
+ $hash = IdCacheManager::computeSha1( $hash );
+
+ if ( !$this->idCacheManager->hasCache( $hash ) ) {
+ $hashList[] = $hash;
+ }
+ }
+
+ if ( $hashList === [] ) {
+ return;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $rows = $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey',
+ 'smw_sort'
+ ],
+ [
+ 'smw_hash' => $hashList
+ ],
+ __METHOD__
+ );
+
+ foreach ( $rows as $row ) {
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+
+ $this->idCacheManager->setCache(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject,
+ $row->smw_id,
+ $sortkey
+ );
+ }
+ }
+
+ /**
+ * Add or modify a cache entry. The key consists of the
+ * parameters $title, $namespace, $interwiki, and $subobject. The
+ * cached data is $id and $sortkey.
+ *
+ * @since 1.8
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ * @param integer $id
+ * @param string $sortkey
+ */
+ public function setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey ) {
+ $this->idCacheManager->setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $id
+ *
+ * @return DIWikiPage|null
+ */
+ public function getDataItemById( $id ) {
+ return $this->idEntityFinder->getDataItemById( $id );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $id
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return string[]
+ */
+ public function getDataItemsFromList( array $idlist, RequestOptions $requestOptions = null ) {
+ return $this->idEntityFinder->getDataItemsFromList( $idlist, $requestOptions );
+ }
+
+ /**
+ * @deprecated since 3.0, use SMWSql3SmwIds::getDataItemsFromList
+ */
+ public function getDataItemPoolHashListFor( array $idlist, RequestOptions $requestOptions = null ) {
+ return $this->idEntityFinder->getDataItemsFromList( $idlist, $requestOptions );
+ }
+
+ /**
+ * Remove any cache entry for the given data. The key consists of the
+ * parameters $title, $namespace, $interwiki, and $subobject. The
+ * cached data is $id and $sortkey.
+ *
+ * @since 1.8
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ */
+ public function deleteCache( $title, $namespace, $interwiki, $subobject ) {
+ $this->idCacheManager->deleteCache( $title, $namespace, $interwiki, $subobject );
+ }
+
+ /**
+ * Move all cached information about subobjects.
+ *
+ * @todo This method is neither efficient nor very convincing
+ * architecturally; it should be redesigned.
+ *
+ * @since 1.8
+ * @param string $oldtitle
+ * @param integer $oldnamespace
+ * @param string $newtitle
+ * @param integer $newnamespace
+ */
+ public function moveSubobjects( $oldtitle, $oldnamespace, $newtitle, $newnamespace ) {
+ // Currently we have no way to change title and namespace across all entries.
+ // Best we can do is clear the cache to avoid wrong hits:
+ if ( $oldnamespace != SMW_NS_PROPERTY || $newnamespace != SMW_NS_PROPERTY ) {
+ $this->idCacheManager->deleteCache( $oldtitle, $oldnamespace, '', '' );
+ $this->idCacheManager->deleteCache( $newtitle, $newnamespace, '', '' );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function initCache() {
+
+ // Tests indicate that it is more memory efficient to have two
+ // arrays (IDs and sortkeys) than to have one array that stores both
+ // values in some data structure (other than a single string).
+ $this->idCacheManager = $this->factory->newIdCacheManager(
+ self::POOLCACHE_ID,
+ [
+ 'entity.id' => self::MAX_CACHE_SIZE,
+ 'entity.sort' => self::MAX_CACHE_SIZE,
+ 'entity.lookup' => 2000,
+ 'table.hash' => self::MAX_CACHE_SIZE,
+ ]
+ );
+ }
+
+ /**
+ * Return an array of hashes with table names as keys. These
+ * hashes are used to compare new data with old data for each
+ * property-value table when updating data
+ *
+ * @since 1.8
+ *
+ * @param integer $subjectId ID of the page as stored in the SMW IDs table
+ *
+ * @return array
+ */
+ public function getPropertyTableHashes( $sid ) {
+
+ if ( $sid == 0 ) {
+ return [];
+ }
+
+ $hash = null;
+ $cache = $this->idCacheManager->get( 'table.hash' );
+
+ if ( ( $hash = $cache->fetch( $sid ) ) !== false ) {
+ return $hash;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ self::TABLE_NAME,
+ [ 'smw_proptable_hash' ],
+ 'smw_id=' . $sid,
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $hash = $row->smw_proptable_hash;
+ }
+
+ if ( $hash !== null && $hash !== false && $connection->isType( 'postgres' ) ) {
+ $hash = pg_unescape_bytea( $hash );
+ }
+
+ $hash = $hash === null || $hash === false ? [] : unserialize( $hash );
+ $cache->save( $sid, $hash );
+
+ return $hash;
+ }
+
+ /**
+ * Update the proptable_hash for a given page.
+ *
+ * @since 1.8
+ * @param integer $sid ID of the page as stored in SMW IDs table
+ * @param string[] of hash values with table names as keys
+ */
+ public function setPropertyTableHashes( $sid, $hash = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $update = [];
+
+ if ( $hash === null ) {
+ $update = [ 'smw_proptable_hash' => $hash, 'smw_rev' => null ];
+ } elseif ( is_array( $hash ) ) {
+ $update = [ 'smw_proptable_hash' => serialize( $hash ) ];
+ } else {
+ throw new RuntimeException( "Expected a null or an array as value!");
+ }
+
+ $connection->update(
+ self::TABLE_NAME,
+ $update,
+ [ 'smw_id' => $sid ],
+ __METHOD__
+ );
+
+ $this->setPropertyTableHashesCache( $sid, $hash );
+
+ if ( $hash === null ) {
+ $this->idCacheManager->deleteCacheById( $sid );
+ }
+ }
+
+ /**
+ * Temporarily cache a property tablehash that has been retrieved for
+ * the given SMW ID.
+ *
+ * @since 1.8
+ * @param $id integer
+ * @param $propertyTableHash string
+ */
+ /**
+ * Temporarily cache a property tablehash that has been retrieved for
+ * the given SMW ID.
+ *
+ * @since 1.8
+ * @param $id integer
+ * @param $propertyTableHash string
+ */
+ protected function setPropertyTableHashesCache( $sid, $hash ) {
+
+ // never cache 0
+ if ( $sid == 0 ) {
+ return;
+ }
+
+ if ( $hash === null ) {
+ $hash = [];
+ } elseif ( is_string( $hash ) ) {
+ $hash = unserialize( $hash );
+ }
+
+ $cache = $this->idCacheManager->get( 'table.hash' );
+ $cache->save( $sid, $hash );
+ }
+
+ /**
+ * Returns store Id table name
+ *
+ * @return string
+ */
+ public function getIdTable() {
+ return self::TABLE_NAME;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/README.md b/www/wiki/extensions/SemanticMediaWiki/maintenance/README.md
new file mode 100644
index 00000000..37f675da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/README.md
@@ -0,0 +1,106 @@
+## Semantic MediaWiki maintenance scripts
+
+Scripts can be run with a command line PHP call if your MediaWiki is
+properly configured to run maintenance scripts.
+
+If you keep SMW in the standard directory `./extensions/SemanticMediaWiki`
+below your MediaWiki installation, then you can run these scripts from
+almost anywhere.
+
+Otherwise, it is required to set the environment variable `MW_INSTALL_PATH`
+to the root of your MediaWiki installation first. This is also required if
+you use a symbolic link from `./extensions/SemanticMediaWiki` to the actual
+installation directory of Semantic MediaWiki. Setting environment variables
+is different for different operating systems and shells, but can normally be
+done from the command line right before the php call. On Bash (Linux), e.g.
+one can use the following call to execute "setupStore.php" with a different
+MediaWiki location:
+
+ export MW_INSTALL_PATH="/path/to/mediawiki" && php setupStore.php
+
+In some setups that use a lot of shared code for many wikis, it might be
+required to specify the location of "LocalSettings.php" explicitly, too:
+
+```
+export MW_INSTALL_PATH="/path/to/mediawiki" && php setupStore.php --conf=/path/to/mediawiki/LocalSettings.php
+```
+
+### dumpRDF.php
+
+Complete RDF export of existing triples.
+
+Usage:
+- php dumpRDF.php
+- [--categories|--classes|--concepts|--conf|--d|--dbpass|--dbuser|--e|--file|--globals|--help|--individuals|--memory-limit|--page|--profiler|--properties|--quiet|--server|--types|--wiki]
+
+### populateHashField.php
+
+Populate the `smw_hash` field for all entities that have a missing entry.
+
+Usage:
+- php populateHashField.php
+- [--conf|--dbpass|--dbuser|--globals|--help|--memory-limit|--profiler|--quiet|--server|--wiki]
+
+### rebuildConceptCache.php
+
+Manages concept caches in Semantic MediaWiki.
+
+Usage:
+- php rebuildConceptCache.php
+- [--concept|--conf|--create|--dbpass|--dbuser|--debug|--delete|--e|--globals|--hard|--help|--memory-limit|--old|--profiler|--quiet|--report-runtime|--s|--server|--status|--update|--verbose|--wiki|--with-maintenance-log]
+
+### rebuildData.php
+
+Recreates all the semantic data in the database
+
+Usage:
+- php rebuildData.php
+- [--b|--categories|--conf|--d|--dbpass|--dbuser|--debug|--dispose-outdated|--e|--exception-log|--f|--force-update|--globals|--help|--ignore-exceptions|--memory-limit|--n|--no-cache|--p|--page|--profiler|--property-statistics|--query|--quiet|--redirects|--report-poolcache|--report-runtime|--revision-mode|--s|--server|--shallow-update|--skip-properties|--startidfile|--v|--wiki|--with-maintenance-log]
+
+### rebuildElasticIndex.php
+
+Rebuilds the Elasticsearch index.
+
+Usage:
+- php rebuildElasticIndex.php
+- [--conf|--dbpass|--dbuser|--debug|--delete-all|--e|--force-refresh|--globals|--help|--memory-limit|--page|--profiler|--quiet|--report-runtime|--run-fileindex|--s|--server|--skip-fileindex|--update-settings|--wiki]
+
+### rebuildFulltextSearchTable.php
+
+Rebuilds the fulltext search index.
+
+Usage:
+- php rebuildFulltextSearchTable.php
+- [--conf|--dbpass|--dbuser|--globals|--help|--memory-limit|--optimize|--profiler|--quick|--quiet|--report-runtime|--server|--v|--wiki|--with-maintenance-log]
+
+### rebuildPropertyStatistics.php
+
+Rebuilds the property usage statistics
+
+Usage:
+- php rebuildPropertyStatistics.php
+- [--conf|--dbpass|--dbuser|--globals|--help|--memory-limit|--profiler|--quiet|--server|--wiki|--with-maintenance-log]
+
+### removeDuplicateEntities.php
+
+Removes duplicate entities.
+
+Usage:
+- php removeDuplicateEntities.php
+- [--conf|--dbpass|--dbuser|--globals|--help|--memory-limit|--profiler|--quiet|--s|--server|--wiki]
+
+### setupStore.php
+
+Sets up the storage backend.
+
+Usage:
+- php setupStore.php
+- [--backend|--conf|--dbpass|--dbuser|--delete|--globals|--help|--memory-limit|--nochecks|--profiler|--quiet|--server|--skip-import|--skip-optimize|--wiki]
+
+### updateEntityCollation.php
+
+Updates the `smw_sort` field.
+
+Usage:
+- php updateEntityCollation.php
+- [--conf|--dbpass|--dbuser|--globals|--help|--memory-limit|--profiler|--quiet|--s|--server|--wiki]
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/dumpRDF.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/dumpRDF.php
new file mode 100644
index 00000000..5a411057
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/dumpRDF.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMWExportController as ExportController;
+use SMWRDFXMLSerializer as RDFXMLSerializer;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * Usage:
+ * php dumpRDF.php [options...]
+ *
+ * --file (-o) <file> Export everything to given output file, stdout is used if omitted;
+ * file output is generally better and strongly recommended for large wikis
+ * --categories Export only categories
+ * --concepts Export only concepts
+ * --classes Export only concepts and categories
+ * --properties Export only properties
+ * --types Export only types
+ * --individuals Export only pages that are no categories, properties, or types
+ * --page <pagelist> Export only pages included in the <pagelist> with | being used as a separator.
+ * Example: --page "Page 1|Page 2", -e, -file, -d are ignored if --page is given.
+ * -d <delay> Slows down the export in order to stress the server less,
+ * sleeping for <delay> milliseconds every now and then
+ * -e <each> After how many exported entities should the process take a nap?
+ * --server=<server> The protocol and server name to as base URLs, e.g.
+ * https://en.wikipedia.org. This is sometimes necessary because
+ * server name detection may fail in command line scripts.
+ *
+ * @ingroup SMWMaintenance
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class DumpRdf extends \Maintenance {
+
+ private $delay = 0;
+ private $delayeach = 0;
+
+ /**
+ * @var boolean|array
+ */
+ private $restrictNamespaceTo = false;
+
+ /**
+ * @var array
+ */
+ private $pages = [];
+
+ /**
+ * @since 2.0
+ */
+ public function __construct() {
+ parent::__construct();
+
+ $this->addDescription( "\n" ."Complete RDF export of existing triples. \n" );
+ $this->addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 2.0
+ */
+ protected function addDefaultParams() {
+
+ parent::addDefaultParams();
+
+ $this->addOption( 'd', '<delay> Wait for this many milliseconds after processing, useful for limiting server load.', false, true );
+ $this->addOption( 'e', '<each> after how many exported entities should the process take a nap.', false, true );
+ $this->addOption( 'file', '<file> output file.', false, true, 'o' );
+
+ $this->addOption( 'categories', 'Export only categories', false );
+ $this->addOption( 'concepts', 'Export only concepts', false );
+ $this->addOption( 'classes', 'Export only classes', false );
+ $this->addOption( 'properties', 'Export only properties', false );
+ $this->addOption( 'types', 'Export only types', false );
+ $this->addOption( 'individuals', 'Export only individuals', false );
+
+ $this->addOption( 'page', 'Export only pages included in the <pagelist> with | being used as a separator. ' .
+ 'Example: --page "Page 1|Page 2", -e, -file, -d are ignored if --page is given.', false, true );
+
+ $this->addOption( 'server', '<server> The protocol and server name to as base URLs, e.g. http://en.wikipedia.org. ' .
+ 'This is sometimes necessary because server name detection may fail in command line scripts.', false, true );
+
+ $this->addOption( 'quiet', 'Do not give any output', false, false, 'q' );
+ }
+
+ /**
+ * @see Maintenance::execute
+ *
+ * @since 2.0
+ */
+ public function execute() {
+
+ if ( !defined( 'SMW_VERSION' ) ) {
+ $this->output( "You need to have SMW enabled in order to use this maintenance script!\n\n" );
+ exit;
+ }
+
+ $this->reportMessage( "\nWriting OWL/RDF dump to " . $this->getOption( 'file' ) . " ...\n" );
+ $this->setParameters()->exportRdfToOutputChannel();
+
+ return true;
+ }
+
+ /**
+ * @see Maintenance::reportMessage
+ *
+ * @since 2.0
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+ private function setParameters() {
+
+ if ( $this->hasOption( 'd' ) ) {
+ $this->delay = intval( $this->getOption( 'd' ) ) * 1000;
+ }
+
+ $this->delayeach = ( $this->delay === 0 ) ? 0 : 1;
+
+ if ( $this->hasOption( 'e' ) ) {
+ $this->delayeach = intval( $this->getOption( 'e' ) );
+ }
+
+ if ( $this->hasOption( 'categories' ) ) {
+ $this->restrictNamespaceTo = NS_CATEGORY;
+ } elseif ( $this->hasOption( 'concepts' ) ) {
+ $this->restrictNamespaceTo = SMW_NS_CONCEPT;
+ } elseif ( $this->hasOption( 'classes' ) ) {
+ $this->restrictNamespaceTo = [ NS_CATEGORY, SMW_NS_CONCEPT ];
+ } elseif ( $this->hasOption( 'properties' ) ) {
+ $this->restrictNamespaceTo = SMW_NS_PROPERTY;
+ } elseif ( $this->hasOption( 'individuals' ) ) {
+ $this->restrictNamespaceTo = - 1;
+ }
+
+ if ( $this->hasOption( 'page' ) ) {
+ $this->pages = explode( '|', $this->getOption( 'page' ) );
+ }
+
+ if ( $this->hasOption( 'server' ) ) {
+ $GLOBALS['wgServer'] = $this->getOption( 'server' );
+ }
+
+ return $this;
+ }
+
+ private function exportRdfToOutputChannel() {
+
+ $exportController = new ExportController( new RDFXMLSerializer() );
+
+ if ( $this->pages !== [] ) {
+ return $exportController->printPages(
+ $this->pages
+ );
+ }
+
+ if ( $this->hasOption( 'file' ) ) {
+ return $exportController->printAllToFile(
+ $this->getOption( 'file' ),
+ $this->restrictNamespaceTo,
+ $this->delay,
+ $this->delayeach
+ );
+ }
+
+ $exportController->printAllToOutput(
+ $this->restrictNamespaceTo,
+ $this->delay,
+ $this->delayeach
+ );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\DumpRdf';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/populateHashField.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/populateHashField.php
new file mode 100644
index 00000000..e19d0e69
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/populateHashField.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\MessageReporter\MessageReporter;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\Installer;
+use SMW\Setup;
+use SMW\Store;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv('MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.1
+ *
+ * @author mwjames
+ */
+class PopulateHashField extends \Maintenance {
+
+ /**
+ * Threshold as the when the `populateHashField.php` should be used by an
+ * administrator instead.
+ *
+ * This postpones the execution to after `setupStore.php`/`update.php` in
+ * order to help minimize the time required for the initial setup/upgrade.
+ */
+ const COUNT_SCRIPT_EXECUTION_THRESHOLD = 200000;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @since 3.1
+ */
+ public function __construct() {
+ $this->mDescription = "Populate the 'smw_hash' field for all entities that have a missing entry.";
+ parent::__construct();
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param Store $store
+ */
+ public function setComplete( $incomplete ) {
+
+ $this->reportMessage(
+ " ... writing the status to the setup information file ... \n"
+ );
+
+ Installer::setUpgradeFile(
+ $GLOBALS,
+ [
+ Installer::POPULATE_HASH_FIELD_COMPLETE => $incomplete
+ ]
+ );
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+
+ if ( $this->messageReporter !== null ) {
+ return $this->messageReporter->reportMessage( $message );
+ }
+
+ $this->output( $message );
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have Semantic MediaWiki enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ $this->store = ApplicationFactory::getInstance()->getStore(
+ 'SMW\SQLStore\SQLStore'
+ );
+
+ $this->reportMessage( "\nChecking 'smw_hash' field consistency ...\n" );
+ $this->populate();
+
+ $this->reportMessage( " ... done.\n" );
+
+ return true;
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @return Iterator
+ */
+ public function fetchRows() {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ return $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject'
+ ],
+ [
+ 'smw_hash' => null,
+ 'smw_iw != ' . $connection->addQuotes( SMW_SQL3_SMWDELETEIW )
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param Iterator $rows
+ */
+ public function populate( \Iterator $rows = null ) {
+
+ if ( $rows === null ) {
+ $rows = $this->fetchRows();
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $idTable = $this->store->getObjectIds();
+
+ $count = 0;
+ $i = 0;
+
+ if ( $rows !== null ) {
+ $count = $rows->numRows();
+ }
+
+ if ( $count == 0 ) {
+ $this->reportMessage( " ... all rows populated ...\n" );
+ } else {
+ $this->reportMessage( " ... missing $count rows ...\n" );
+
+ foreach ( $rows as $row ) {
+
+ $hash = $idTable->computeSha1(
+ [
+ $row->smw_title,
+ (int)$row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject
+ ]
+ );
+
+ $this->reportMessage(
+ $this->progress( $row->smw_id, $i++, $count )
+ );
+
+ $connection->update(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_hash' => $hash
+ ],
+ [
+ 'smw_id' => $row->smw_id
+ ],
+ __METHOD__
+ );
+ }
+ }
+
+ $this->reportMessage( "\n" );
+ $this->setComplete( true );
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ */
+ protected function addDefaultParams() {
+ parent::addDefaultParams();
+ }
+
+ private function progress( $id, $i, $count ) {
+ return "\r". sprintf( "%-35s%s", " ... updating document no.", sprintf( "%s (%1.0f%%)", $id, round( ( $i / $count ) * 100 ) ) );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\PopulateHashField';
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildConceptCache.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildConceptCache.php
new file mode 100644
index 00000000..86a77df5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildConceptCache.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * Manage concept caches
+ *
+ * This script is used to manage concept caches for Semantic MediaWiki. Concepts
+ * are semantic queries stored on Concept: pages. The elements of concepts can be
+ * computed online, or they can come from a pre-computed cache. The wiki may even
+ * be configured to display certain concepts only if they are available cached.
+ *
+ * This script can create, delete and update these caches, or merely show their
+ * status.
+ *
+ * Usage: php rebuildConceptCache.php <action> [<select concepts>] [<options>]
+ *
+ * Actions:
+ * --help Show this message.
+ * --status Show the cache status of the selected concepts.
+ * --create Rebuild caches for the selected concepts.
+ * --delete Remove all caches for the selected concepts.
+ *
+ * If no further options are given, all concepts in the wiki are processed.
+ *
+ * Select concepts:
+ * --concept 'Concept name' Process only this one concept.
+ * --hard Process only concepts that are not allowed to be computed
+ * online according to the current wiki settings.
+ * --update Process only concepts that already have some cache, i.e. do
+ * not create any new caches. For the opposite (only concepts
+ * without caches), use --old with a very high number.
+ * --old <min> Process only concepts with caches older than <min> minutes
+ * or with no caches at all.
+ * -s <startid> Process only concepts with page id of at least <startid>
+ * -e <endid> Process only concepts with page id of at most <endid>
+ *
+ * Selection options can be combined to process only concepts that meet all the
+ * requirements at once. If --concept is given, then -s and -e are ignored.
+ *
+ * Options:
+ * --quiet Do not give any output.
+ * --verbose Give additional output. No effect if --quiet is given.
+ *
+ * Use option --help for usage details.
+ *
+ * Note: if SMW is not installed in its standard path under ./extensions
+ * then the MW_INSTALL_PATH environment variable must be set.
+ * See README in the maintenance directory.
+ *
+ * @ingroup Maintenance
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class RebuildConceptCache extends \Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+
+ $this->addDescription( "\n" .
+ "This script is used to manage concept caches for Semantic MediaWiki. Concepts \n" .
+ "are semantic queries stored on Concept: pages. The elements of concepts can be \n" .
+ "computed online, or they can come from a pre-computed cache. The wiki may even \n" .
+ "be configured to display certain concepts only if they are available cached. \n" .
+ "\n" . "This script can create, delete and update these caches, or merely show their \n".
+ "status. "
+ );
+
+ $this->addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ */
+ protected function addDefaultParams() {
+
+ parent::addDefaultParams();
+
+ // Actions
+ $this->addOption( 'status', 'Show the cache status of the selected concepts' );
+ $this->addOption( 'create', 'Rebuild caches for the selected concepts.' );
+ $this->addOption( 'delete', 'Remove all caches for the selected concepts.' );
+
+ // Options
+ $this->addOption( 'concept', '"Concept name" Process only this one concept.', false, true );
+ $this->addOption( 'hard', 'Process only concepts that are not allowed to be computed online according to the current wiki settings.' );
+
+ $this->addOption( 'update', 'Process only concepts that already have some cache, i.e. do not create any new caches. ' .
+ 'For the opposite (only concepts without caches), use --old with a very high number.' );
+
+ $this->addOption( 'old', '<min> Process only concepts with caches older than <min> minutes or with no caches at all.', false, true );
+ $this->addOption( 's', '<startid> Process only concepts with page id of at least <startid>', false, true );
+ $this->addOption( 'e', '<endid> Process only concepts with page id of at most <endid>', false, true );
+
+ $this->addOption( 'with-maintenance-log', 'Add log entry to `Special:Log` about the maintenance run.', false );
+ $this->addOption( 'report-runtime', 'Report execution time and memory usage', false );
+ $this->addOption( 'debug', 'Sets global variables to support debug ouput while running the script', false );
+ $this->addOption( 'quiet', 'Do not give any output', false );
+ $this->addOption( 'verbose', 'Give additional output. No effect if --quiet is given.', false );
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper();
+ $maintenanceHelper->initRuntimeValues();
+
+ if ( $this->hasOption( 'debug' ) ) {
+ $maintenanceHelper->setGlobalToValue( 'wgShowExceptionDetails', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowSQLErrors', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowDBErrorBacktrace', true );
+ }
+
+ $conceptCacheRebuilder = $maintenanceFactory->newConceptCacheRebuilder(
+ $applicationFactory->getStore(),
+ [ $this, 'reportMessage' ]
+ );
+
+ $conceptCacheRebuilder->setParameters( $this->mOptions );
+
+ $result = $this->checkForRebuildState(
+ $conceptCacheRebuilder->rebuild()
+ );
+
+ if ( $result && $this->hasOption( 'report-runtime' ) ) {
+ $this->reportMessage( "\n" . "Runtime report ..." . "\n" );
+ $this->reportMessage( $maintenanceHelper->getFormattedRuntimeValues( ' ...' ) . "\n" );
+ }
+
+ if ( $this->hasOption( 'with-maintenance-log' ) ) {
+ $maintenanceLogger = $maintenanceFactory->newMaintenanceLogger( 'RebuildConceptCacheLogger' );
+ $maintenanceLogger->log( $maintenanceHelper->getFormattedRuntimeValues() );
+ }
+
+ $maintenanceHelper->reset();
+
+ return $result;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+ private function checkForRebuildState( $rebuildResult ) {
+
+ if ( !$rebuildResult ) {
+ $this->reportMessage( $this->mDescription . "\n\n" . 'Use option --help for usage details.' . "\n" );
+ return false;
+ }
+
+ return true;
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RebuildConceptCache';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildData.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildData.php
new file mode 100644
index 00000000..e73931de
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildData.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\StoreFactory;
+use SMW\Store;
+use SMW\Setup;
+use SMW\Options;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * Recreates all the semantic data in the database, by cycling through all
+ * the pages that might have semantic data, and calling functions that
+ * re-save semantic data for each one.
+ *
+ * Note: if SMW is not installed in its standard path under ./extensions
+ * then the MW_INSTALL_PATH environment variable must be set.
+ * See README in the maintenance directory.
+ *
+ * Usage:
+ * php rebuildData.php [options...]
+ *
+ * -d <delay> Wait for this many milliseconds after processing an article, useful for limiting server load.
+ * -s <startid> Start refreshing at given article ID, useful for partial refreshing
+ * -e <endid> Stop refreshing at given article ID, useful for partial refreshing
+ * -n <numids> Stop refreshing after processing a given number of IDs, useful for partial refreshing
+ * --startidfile <startidfile> Read <startid> from a file instead of the arguments and write the next id
+ * to the file when finished. Useful for continual partial refreshing from cron.
+ * -b <backend> Execute the operation for the storage backend of the given name
+ * (default is to use the current backend)
+ * -v Be verbose about the progress.
+ * -c Will refresh only category pages (and other explicitly named namespaces)
+ * -p Will refresh only property pages (and other explicitly named namespaces)
+ * --page=<pagelist> will refresh only the pages of the given names, with | used as a separator.
+ * Example: --page="Page 1|Page 2" refreshes Page 1 and Page 2
+ * Options -s, -e, -n, --startidfile, -c, -p, -t are ignored if --page is given.
+ * --query=<query> Will refresh only pages returned by a given query.
+ * Example: --query='[[Category:SomeCategory]]'
+ * -f Fully delete all content instead of just refreshing relevant entries. This will also
+ * rebuild the whole storage structure. May leave the wiki temporarily incomplete.
+ * --server=<server> The protocol and server name to as base URLs, e.g.
+ * http://en.wikipedia.org. This is sometimes necessary because
+ * server name detection may fail in command line scripts.
+ *
+ * @author Yaron Koren
+ * @author Markus Krötzsch
+ */
+class RebuildData extends \Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+
+ $this->addDescription( "\n" .
+ "Recreates all the semantic data in the database, by cycling through all \n" .
+ "the pages that might have semantic data, and calling functions that \n" .
+ "re-save semantic data for each one. \n"
+ );
+
+ $this->addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ */
+ protected function addDefaultParams() {
+
+ parent::addDefaultParams();
+
+ $this->addOption( 'd', '<delay> Wait for this many milliseconds after processing an article, useful for limiting server load.', false, true );
+ $this->addOption( 's', '<startid> Start refreshing at given article ID, useful for partial refreshing.', false, true );
+ $this->addOption( 'e', '<endid> Stop refreshing at given article ID, useful for partial refreshing.', false, true );
+ $this->addOption( 'n', '<numids> Stop refreshing after processing a given number of IDs, useful for partial refreshing.', false, true );
+
+ $this->addOption( 'startidfile', '<startidfile> Read <startid> from a file instead of the arguments and write the next id to the file when finished. ' .
+ 'Useful for continual partial refreshing from cron.', false, true );
+
+ $this->addOption( 'b', '<backend> Execute the operation for the storage backend of the given name (default is to use the current backend).', false, true );
+
+ $this->addOption( 'f', 'Fully delete all content instead of just refreshing relevant entries. This will also rebuild the whole storage structure. ' .
+ 'May leave the wiki temporarily incomplete.', false );
+
+ $this->addOption( 'v', 'Be verbose about the progress', false );
+ $this->addOption( 'p', 'Only refresh property pages (and other explicitly named namespaces)', false );
+ $this->addOption( 'categories', 'Only refresh category pages (and other explicitly named namespaces)', false, false, 'c' );
+ $this->addOption( 'redirects', 'Only refresh redirect pages', false );
+ $this->addOption( 'dispose-outdated', 'Only Remove outdated marked entities (including pending references).', false );
+
+ $this->addOption( 'skip-properties', 'Skip the default properties rebuild (only recommended when successive build steps are used)', false );
+ $this->addOption( 'shallow-update', 'Skip processing of entities that compare to the last known revision date', false );
+ $this->addOption( 'property-statistics', 'Execute `rebuildPropertyStatistics` after the `rebuildData` run has finished.', false );
+
+ $this->addOption( 'force-update', 'Force an update even when an associated revision is known', false );
+ $this->addOption( 'revision-mode', 'Skip entities where its associated revision matches the latests referenced revision of an associated page', false );
+
+ $this->addOption( 'ignore-exceptions', 'Ignore exceptions and log exception to a file', false );
+ $this->addOption( 'exception-log', 'Exception log file location (e.g. /tmp/logs/)', false, true );
+ $this->addOption( 'with-maintenance-log', 'Add log entry to `Special:Log` about the maintenance run.', false );
+
+ $this->addOption( 'page', '<pagelist> Will refresh only the pages of the given names, with | used as a separator. ' .
+ 'Example: --page "Page 1|Page 2" refreshes Page 1 and Page 2 Options -s, -e, -n, ' .
+ '--startidfile, -c, -p, -t are ignored if --page is given.', false, true );
+
+ $this->addOption( 'server', '<server> The protocol and server name to as base URLs, e.g. http://en.wikipedia.org. ' .
+ 'This is sometimes necessary because server name detection may fail in command line scripts.', false, true );
+
+ $this->addOption( 'query', "<query> Will refresh only pages returned by a given query. Example: --query='[[Category:SomeCategory]]'", false, true );
+
+ $this->addOption( 'report-runtime', 'Report execution time and memory usage', false );
+ $this->addOption( 'report-poolcache', 'Report internal poolcache memory usage', false );
+ $this->addOption( 'no-cache', 'Sets the `wgMainCacheType` to none while running the script', false );
+ $this->addOption( 'debug', 'Sets global variables to support debug ouput while running the script', false );
+ $this->addOption( 'quiet', 'Do not give any output', false );
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $maintenanceFactory = ApplicationFactory::getInstance()->newMaintenanceFactory();
+
+ $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper();
+ $maintenanceHelper->initRuntimeValues();
+
+ if ( $this->hasOption( 'no-cache' ) ) {
+ $maintenanceHelper->setGlobalToValue( 'wgMainCacheType', CACHE_NONE );
+ $maintenanceHelper->setGlobalToValue( 'smwgEntityLookupCacheType', CACHE_NONE );
+ $maintenanceHelper->setGlobalToValue( 'smwgQueryResultCacheType', CACHE_NONE );
+ }
+
+ if ( $this->hasOption( 'debug' ) ) {
+ $maintenanceHelper->setGlobalToValue( 'wgShowExceptionDetails', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowSQLErrors', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowDBErrorBacktrace', true );
+ } else {
+ $maintenanceHelper->setGlobalToValue( 'wgDebugLogFile', '' );
+ $maintenanceHelper->setGlobalToValue( 'wgDebugLogGroups', [] );
+ }
+
+ $store = StoreFactory::getStore( $this->hasOption( 'b' ) ? $this->getOption( 'b' ) : null );
+ $store->setOption( Store::OPT_CREATE_UPDATE_JOB, false );
+
+ $dataRebuilder = $maintenanceFactory->newDataRebuilder(
+ $store,
+ [ $this, 'reportMessage' ]
+ );
+
+ $dataRebuilder->setOptions(
+ new Options( $this->mOptions )
+ );
+
+ $result = $this->checkForRebuildState(
+ $dataRebuilder->rebuild()
+ );
+
+ if ( $result && $this->hasOption( 'property-statistics' ) ) {
+ $rebuildPropertyStatistics = $maintenanceFactory->newRebuildPropertyStatistics();
+ $rebuildPropertyStatistics->execute();
+ }
+
+ if ( $result && $this->hasOption( 'report-runtime' ) ) {
+ $this->reportMessage( "\n" . "Runtime report ..." . "\n" );
+ $this->reportMessage( $maintenanceHelper->getFormattedRuntimeValues( ' ...' ) . "\n" );
+ }
+
+ if ( $this->hasOption( 'with-maintenance-log' ) ) {
+ $maintenanceLogger = $maintenanceFactory->newMaintenanceLogger( 'RebuildDataLogger' );
+ $runtimeValues = $maintenanceHelper->getRuntimeValues();
+
+ $log = [
+ 'Memory used: ' . $runtimeValues['memory-used'],
+ 'Time used: ' . $runtimeValues['humanreadable-time'],
+ 'Rebuild count: ' . $dataRebuilder->getRebuildCount(),
+ 'Exception count: ' . $dataRebuilder->getExceptionCount()
+ ];
+
+ $maintenanceLogger->log( implode( ', ', $log ) );
+ }
+
+ $maintenanceHelper->reset();
+
+ if ( $this->hasOption( 'report-poolcache' ) ) {
+ $this->reportMessage( "\n" . ApplicationFactory::getInstance()->getInMemoryPoolCache()->getStats( \SMW\Utils\StatsFormatter::FORMAT_JSON ) . "\n" );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+ private function checkForRebuildState( $rebuildResult ) {
+
+ if ( !$rebuildResult ) {
+ $this->reportMessage( $this->mDescription . "\n\n" . 'Use option --help for usage details.' . "\n" );
+ return false;
+ }
+
+ return true;
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RebuildData';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildElasticIndex.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildElasticIndex.php
new file mode 100644
index 00000000..fe48d734
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildElasticIndex.php
@@ -0,0 +1,366 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\SQLStore\SQLStore;
+use SMW\Elastic\ElasticFactory;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv('MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RebuildElasticIndex extends \Maintenance {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Rebuilder
+ */
+ private $rebuilder;
+
+ /**
+ * @var JobQueue
+ */
+ private $jobQueue;
+
+ /**
+ * @see Maintenance::__construct
+ *
+ * @since 3.0
+ */
+ public function __construct() {
+ $this->mDescription = 'Rebuild the Elasticsearch index from property tables (content is not explicitly parsed!)';
+ $this->addOption( 's', 'Start with a selected document no.', false, true );
+ $this->addOption( 'e', 'End with a selected document no. (requires a start ID)', false, true );
+ $this->addOption( 'page', 'Set of pages (Foo|Bar|...)', false, true );
+ $this->addOption( 'update-settings', 'Update settings and mappings for listed indices', false, false );
+ $this->addOption( 'force-refresh', 'Forces a refresh of listed indices', false, false );
+ $this->addOption( 'delete-all', 'Delete listed indices without rebuilding the data', false, false );
+ $this->addOption( 'skip-fileindex', 'Skipping any file ingest actions', false, false );
+ $this->addOption( 'run-fileindex', 'Only run file ingest actions', false, false );
+
+ $this->addOption( 'debug', 'Sets global variables to support debug ouput while running the script', false );
+ $this->addOption( 'report-runtime', 'Report execution time and memory usage', false );
+
+ parent::__construct();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->output( "You need to have SMW enabled in order to use this maintenance script!\n\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ // If available, set a callback to listen to a possible user termination
+ // and try to recover the index settings.
+ if ( function_exists( 'pcntl_signal_dispatch' ) ) {
+ pcntl_signal( SIGTERM, [ $this, 'handleTermSignal' ], false );
+ pcntl_signal_dispatch();
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper();
+ $maintenanceHelper->initRuntimeValues();
+
+ if ( $this->hasOption( 'debug' ) ) {
+ $maintenanceHelper->setGlobalToValue( 'wgShowExceptionDetails', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowSQLErrors', true );
+ $maintenanceHelper->setGlobalToValue( 'wgShowDBErrorBacktrace', true );
+ } else {
+ $maintenanceHelper->setGlobalToValue( 'wgDebugLogFile', '' );
+ $maintenanceHelper->setGlobalToValue( 'wgDebugLogGroups', [] );
+ }
+
+ $this->jobQueue = $applicationFactory->getJobQueue();
+ $this->store = $applicationFactory->getStore( 'SMW\SQLStore\SQLStore' );
+ $elasticFactory = $applicationFactory->create( 'ElasticFactory' );
+
+ $this->rebuilder = $elasticFactory->newRebuilder(
+ $this->store
+ );
+
+ $this->rebuilder->setMessageReporter(
+ $maintenanceFactory->newMessageReporter( [ $this, 'reportMessage' ] )
+ );
+
+ if ( !$this->rebuilder->ping() ) {
+ return $this->reportMessage(
+ "\n" . 'Elasticsearch endpoint(s) are not available!' . "\n"
+ );
+ }
+
+ $this->reportMessage(
+ "\nThe script rebuilds the index from available property tables. Any\n" .
+ "change of the index rules (e.g. altered stopwords, new stemmer etc.)\n" .
+ "or a newly added (or altered) table requires to run this script again\n" .
+ "to ensure that the index complies with the rules set forth by the SQL\n" .
+ "back-end or the Elasticsearch field mapping.\n"
+ );
+
+ if ( $this->otherActivities() ) {
+ return true;
+ }
+
+ $this->showAbort();
+
+ $this->reportMessage(
+ "\nIf for some reason the rebuild process is aborted, please make sure\n" .
+ "to run `--update-settings` so that default settings can be recovered\n".
+ "and set to a normal working mode.\n"
+ );
+
+ $this->rebuild();
+
+ if ( $this->hasOption( 'report-runtime' ) ) {
+ $this->reportMessage( "\n" . $maintenanceHelper->getFormattedRuntimeValues() . "\n" );
+ }
+
+ $maintenanceHelper->reset();
+
+ return true;
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 3.0
+ */
+ protected function addDefaultParams() {
+ parent::addDefaultParams();
+ }
+
+ protected function handleTermSignal( $signal ) {
+
+ $this->reportMessage( "\n" . ' ... rebuild was terminated, start recovery process ...' );
+ $this->rebuilder->setDefaults();
+ $this->rebuilder->refresh();
+ $this->reportMessage( "\n" . ' ... done.' . "\n" );
+
+ pcntl_signal( SIGTERM, SIG_DFL );
+ exit( 1 );
+ }
+
+ private function otherActivities() {
+
+ if ( $this->hasOption( 'update-settings' ) ) {
+ $this->reportMessage(
+ "\n" . 'Settings and mappings ...'
+ );
+
+ $message = $this->rebuilder->setDefaults() ? ' ... done.' : ' ... failed (due to missing index).';
+ $this->reportMessage( "\n$message\n" );
+
+ return true;
+ }
+
+ if ( $this->hasOption( 'force-refresh' ) ) {
+ $this->reportMessage(
+ "\n" . 'Forcing refresh of known indices ...'
+ );
+
+ $message = $this->rebuilder->refresh() ? ' ... done.' : ' ... failed (due to missing index).';
+ $this->reportMessage( "\n$message\n" );
+
+ return true;
+ }
+
+ if ( $this->hasOption( 'delete-all' ) ) {
+ $this->reportMessage(
+ "\n" . 'Deleting all indices ...'
+ );
+
+ $this->rebuilder->deleteAndSetupIndices();
+ $this->reportMessage( "\n ... done.\n" );
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private function showAbort() {
+
+ $showAbort = !$this->hasOption( 'quick' ) && !$this->hasOption( 's' ) && !$this->hasOption( 'page' ) && !$this->hasOption( 'run-fileindex' );
+
+ if ( !$showAbort ) {
+ return;
+ }
+
+ $this->reportMessage(
+ "\nThe rebuild will use a rollover approach which means that while the\n" .
+ "new index is created, the old index is still available and allows\n" .
+ "queries to work even though the rebuild is ongoing. Once completed,\n" .
+ "a \"rollover\" will switch the indices at which point the old indices\n" .
+ "are being removed.\n"
+ );
+
+ $this->reportMessage(
+ "\nIt should be noted that the replication is paused for the duration\n" .
+ "of the rebuild to allow changes to pages and annotations to be\n" .
+ "processed after the re-index has been completed therefore running\n".
+ "the job scheduler is obligatory.\n"
+ );
+
+ $this->reportMessage( "\n" . 'Abort the rebuild with control-c in the next five seconds ... ' );
+ swfCountDown( 5 );
+ }
+
+ private function rebuild() {
+
+ $this->reportMessage( "\nRebuilding indices ..." );
+ $isSelective = $this->hasOption( 's' ) || $this->hasOption( 'page' );
+
+ if ( !$this->hasOption( 's' ) && !$this->hasOption( 'page' ) && !$this->hasOption( 'run-fileindex' ) ) {
+ $this->reportMessage( "\n" . ' ... creating required indices and aliases ...' );
+ $this->rebuilder->createIndices();
+ }
+
+ $this->rebuilder->prepare();
+
+ list( $res, $last ) = $this->rebuilder->select(
+ $this->store,
+ $this->select_conditions()
+ );
+
+ if ( $isSelective ) {
+ $last = $res->numRows();
+ }
+
+ if ( $res->numRows() > 0 ) {
+ $this->reportMessage( "\n" );
+ } else {
+ $this->reportMessage( "\n" . ' ... no documents to process ...' );
+ }
+
+ $this->rebuilder->set( 'skip-fileindex', $this->getOption( 'skip-fileindex' ) );
+ $i = 0;
+
+ foreach ( $res as $row ) {
+ $i++;
+ $this->rebuild_row( $i, $row, $last, $isSelective );
+ }
+
+ $this->rebuilder->setDefaults();
+ $this->rebuilder->refresh();
+
+ $this->reportMessage( "\n" . ' ... done.' . "\n" );
+
+ if ( ( $count = $this->jobQueue->getQueueSize( 'smw.elasticIndexerRecovery' ) ) > 0 ) {
+ $this->reportMessage( "\n" . "Job queue ..." );
+ $this->reportMessage( "\n" . " ... smw.elasticIndexerRecovery has $count unprocessed jobs ..." );
+ $this->reportMessage( "\n" . ' ... done.' . "\n" );
+ }
+ }
+
+ private function rebuild_row( $i, $row, $last, $isSelective ) {
+
+ $i = $isSelective ? $i : $row->smw_id;
+ $key = $isSelective ? '(count)' : 'no.';
+
+ $this->reportMessage(
+ "\r". sprintf( "%-50s%s", " ... updating document $key", sprintf( "%4.0f%% (%s/%s)", ( $i / $last ) * 100, $i, $last ) )
+ );
+
+ if ( $row->smw_iw === SMW_SQL3_SMWDELETEIW || $row->smw_iw === SMW_SQL3_SMWREDIIW ) {
+ return $this->rebuilder->delete( $row->smw_id );
+ }
+
+ $dataItem = $this->store->getObjectIds()->getDataItemById(
+ $row->smw_id
+ );
+
+ if ( $dataItem === null ) {
+ return;
+ }
+
+ $this->rebuilder->rebuild(
+ $row->smw_id,
+ $this->store->getSemanticData( $dataItem )
+ );
+ }
+
+ private function select_conditions() {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $conditions = [];
+ $conditions[] = "smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED );
+
+ if ( $this->hasOption( 's' ) ) {
+ $conditions[] = 'smw_id >= ' . $connection->addQuotes( $this->getOption( 's' ) );
+
+ if ( $this->hasOption( 'e' ) ) {
+ $conditions[] = 'smw_id <= ' . $connection->addQuotes( $this->getOption( 'e' ) );
+ }
+ }
+
+ if ( $this->hasOption( 'run-fileindex' ) ) {
+ $conditions[] = 'smw_namespace=' . $connection->addQuotes( NS_FILE );
+ }
+
+ if ( $this->hasOption( 'page' ) ) {
+ $pages = explode( '|', $this->getOption( 'page' ) );
+
+ foreach ( $pages as $page ) {
+ $title = \Title::newFromText( $page );
+
+ if ( $title === null ) {
+ continue;
+ }
+
+ $op = '=';
+ $text = $title->getDBKey();
+
+ // Match something like --page="Lorem*"
+ if ( strpos( $title->getDBKey(), '*' ) !== false ) {
+ $op = ' LIKE ';
+ $text = str_replace( '*', '%', $text );
+ }
+
+ $cond = [
+ "smw_title$op" . $connection->addQuotes( $text ),
+ 'smw_namespace=' . $connection->addQuotes( $title->getNamespace() )
+ ];
+
+ $conditions[] = implode( ' AND ', $cond );
+ }
+ }
+
+ return $conditions;
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RebuildElasticIndex';
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildFulltextSearchTable.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildFulltextSearchTable.php
new file mode 100644
index 00000000..c600d257
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildFulltextSearchTable.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+use SMW\ApplicationFactory;
+use SMWDataItem as DataItem;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RebuildFulltextSearchTable extends \Maintenance {
+
+ public function __construct() {
+ $this->mDescription = 'Rebuild the fulltext search index (only works with SQLStore)';
+ $this->addOption( 'report-runtime', 'Report execution time and memory usage', false );
+ $this->addOption( 'with-maintenance-log', 'Add log entry to `Special:Log` about the maintenance run.', false );
+ $this->addOption( 'optimize', 'Run possible table optimization (support depends on the SQL back-end) ', false );
+ $this->addOption( 'v', 'Show additional (verbose) information about the progress', false );
+ $this->addOption( 'quick', 'Suppress abort operation', false );
+
+ parent::__construct();
+ }
+
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 2.5
+ */
+ protected function addDefaultParams() {
+
+ parent::addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ $fulltextSearchTableFactory = new FulltextSearchTableFactory();
+
+ // Only the SQLStore is supported
+ $searchTableRebuilder = $fulltextSearchTableFactory->newSearchTableRebuilder(
+ $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' )
+ );
+
+ $textSanitizer = $fulltextSearchTableFactory->newTextSanitizer();
+
+ $searchTableRebuilder->reportVerbose(
+ $this->hasOption( 'v' )
+ );
+
+ $searchTableRebuilder->requestOptimization(
+ $this->hasOption( 'optimize' )
+ );
+
+ $this->reportMessage(
+ "\nThe script rebuilds the search index from property tables that\n" .
+ "support a fulltext search. Any change of the index rules (altered\n".
+ "stopwords, new stemmer etc.) and/or a newly added or altered table\n".
+ "requires to run this script again to ensure that the index complies\n".
+ "with the rules set forth by the SQL back-end or Sanitizer.\n"
+ );
+
+ $this->reportConfiguration(
+ $searchTableRebuilder,
+ $textSanitizer
+ );
+
+ if ( !$this->hasOption( 'quick' ) ) {
+ $this->reportMessage( "\n" . 'Abort the rebuild with control-c in the next five seconds ... ' );
+ swfCountDown( 5 );
+ }
+
+ $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper();
+ $maintenanceHelper->initRuntimeValues();
+
+ // Need to instantiate an extra object here since we cannot make this class itself
+ // into a MessageReporter since the maintenance script does not load the interface in time.
+ $reporter = MessageReporterFactory::getInstance()->newObservableMessageReporter();
+ $reporter->registerReporterCallback( [ $this, 'reportMessage' ] );
+
+ $searchTableRebuilder->setMessageReporter( $reporter );
+ $result = $searchTableRebuilder->rebuild();
+
+ if ( $result && $this->hasOption( 'report-runtime' ) ) {
+ $this->reportMessage( "\n" . "Runtime report ..." . "\n" );
+ $this->reportMessage( $maintenanceHelper->getFormattedRuntimeValues( ' ...' ) . "\n" );
+ }
+
+ if ( $this->hasOption( 'with-maintenance-log' ) ) {
+ $maintenanceLogger = $maintenanceFactory->newMaintenanceLogger( 'RebuildFulltextSearchTableLogger' );
+ $maintenanceLogger->log( $maintenanceHelper->getFormattedRuntimeValues() );
+ }
+
+ $maintenanceHelper->reset();
+ return $result;
+ }
+
+ private function reportConfiguration( $searchTableRebuilder, $textSanitizer ) {
+
+ $this->reportMessage( "\nConfiguration ..." );
+
+ foreach ( $textSanitizer->getVersions() as $key => $value ) {
+ $this->reportMessage( "\n" . sprintf( "%-36s%s", " ... {$key}", $value ) );
+ }
+
+ $searchTable = $searchTableRebuilder->getSearchTable();
+ $indexableDataTypes = [];
+
+ $dataTypes = [
+ DataItem::TYPE_BLOB => 'BLOB',
+ DataItem::TYPE_URI => 'URI',
+ DataItem::TYPE_WIKIPAGE => 'WIKIPAGE'
+ ];
+
+ foreach ( $dataTypes as $key => $value ) {
+ if ( $searchTable->isValidByType( $key ) ) {
+ $indexableDataTypes[] = $value;
+ }
+ }
+
+ $this->reportMessage( "\n" . sprintf( "%-36s%s", " ... DataTypes (indexable)", implode( ', ', $indexableDataTypes ) ) );
+ $this->reportMessage( "\n\nExempted properties (not indexable) ..." );
+
+ $exemptionList = '';
+
+ foreach ( $searchTable->getPropertyExemptionList() as $prop ) {
+ $exemptionList .= ( $exemptionList === '' ? '' : ', ' ) . $prop;
+
+ if ( strlen( $exemptionList ) > 50 ) {
+ $this->reportMessage( "\n ... " . $exemptionList );
+ $exemptionList = '';
+ }
+ }
+
+ $this->reportMessage( "\n ... " . $exemptionList . "\n" );
+ }
+
+ /**
+ * @see Maintenance::reportMessage
+ *
+ * @since 2.5
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RebuildFulltextSearchTable';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildPropertyStatistics.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildPropertyStatistics.php
new file mode 100644
index 00000000..ad64f6cd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/rebuildPropertyStatistics.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * Maintenance script for rebuilding the property usage statistics.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class RebuildPropertyStatistics extends \Maintenance {
+
+ public function __construct() {
+ $this->mDescription = 'Rebuild the property usage statistics (only works with SQLStore3 for now)';
+ $this->addOption( 'with-maintenance-log', 'Add log entry to `Special:Log` about the maintenance run.', false );
+
+ parent::__construct();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 1.9
+ */
+ protected function addDefaultParams() {
+ parent::addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->output( "You need to have SMW enabled in order to use this maintenance script!\n\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper();
+ $maintenanceHelper->initRuntimeValues();
+
+ $statisticsRebuilder = $maintenanceFactory->newPropertyStatisticsRebuilder(
+ $applicationFactory->getStore( 'SMW\SQLStore\SQLStore' ),
+ [ $this, 'reportMessage' ]
+ );
+
+ $statisticsRebuilder->rebuild();
+
+ if ( $this->hasOption( 'with-maintenance-log' ) ) {
+ $maintenanceLogger = $maintenanceFactory->newMaintenanceLogger( 'RebuildPropertyStatisticsLogger' );
+ $maintenanceLogger->log( $maintenanceHelper->getFormattedRuntimeValues() );
+ }
+ }
+
+ /**
+ * @see Maintenance::reportMessage
+ *
+ * @since 1.9
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RebuildPropertyStatistics';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/removeDuplicateEntities.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/removeDuplicateEntities.php
new file mode 100644
index 00000000..a058f167
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/removeDuplicateEntities.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv(
+'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RemoveDuplicateEntities extends \Maintenance {
+
+ /**
+ * @since 3.0
+ */
+ public function __construct() {
+ $this->mDescription = 'Remove duplicate entities without active references.';
+ $this->addOption( 's', 'ID starting point', false, true );
+
+ parent::__construct();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 3.0
+ */
+ protected function addDefaultParams() {
+ parent::addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid() ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $this->reportMessage(
+ "\nThe script will only dispose of those duplicate entities that have no active\n" .
+ "references. The log section 'untouched' contains IDs that have not been\n" .
+ "removed and the user is asked to verify the content and manually remove\n".
+ "those listed entities.\n\n"
+ );
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ $duplicateEntitiesDisposer = $maintenanceFactory->newDuplicateEntitiesDisposer(
+ $applicationFactory->getStore( 'SMW\SQLStore\SQLStore' ),
+ [ $this, 'reportMessage' ]
+ );
+
+ $duplicateEntityRecords = $duplicateEntitiesDisposer->findDuplicates();
+ $duplicateEntitiesDisposer->verifyAndDispose( $duplicateEntityRecords );
+
+ return true;
+ }
+
+ /**
+ * @see Maintenance::reportMessage
+ *
+ * @since 1.9
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\RemoveDuplicateEntities';
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/setupStore.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/setupStore.php
new file mode 100644
index 00000000..59d6fe98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/setupStore.php
@@ -0,0 +1,265 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\Store;
+use SMW\StoreFactory;
+use Onoi\MessageReporter\MessageReporterFactory;
+use Onoi\MessageReporter\MessageReporter;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\Installer;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv( 'MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * Sets up the storage backend currently selected in LocalSettings.php
+ * (or the default MySQL store if no other store was selected). This
+ * is equivalent to clicking the respective button on the special page
+ * Special:SMWAdmin. However, the latter may timeout if the setup involves
+ * migrating a lot of existing data.
+ *
+ * Note: If SMW is not installed in its standard path under ./extensions
+ * then the MW_INSTALL_PATH environment variable must be set.
+ * See README in the maintenance directory.
+ *
+ * Usage:
+ * php setupStore.php [options...]
+ *
+ * -password Password for user account
+ * NOTE: specifying user credentials in a command line call will usually store them
+ * within the shell history file. For security, provide credentials in Adminssetings.php
+ * instead and ensure that your text editor does not create world-readable backup copies
+ * when modifying this file.
+ *
+ * --delete Delete all SMW data, uninstall the selected storage backend. This is useful
+ * when moving to a new storage engine, and in the rare case of unsinstalling
+ * SMW. Deleted data can be recreated using this script (setup) followed by the
+ * use of the rebuildhData.php script which may take some time.
+ *
+ * --backend The backend to use, e.g. SMWSQLStore3.
+ *
+ * --skip-optimize Skips the table optimization process.
+ *
+ * --skip-import Skips the import process.
+ *
+ * --nochecks When specified, no prompts are provided. Deletion will thus happen
+ * without the need to provide any confirmation.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SetupStore extends \Maintenance {
+
+ /**
+ * Name of the store class configured in LocalSettings.php. Stored to
+ * be able to tell if the selected store is the currecnt default or not.
+ *
+ * @var string
+ */
+ protected $originalStore;
+
+ /**
+ * @var MessageReporter
+ */
+ protected $messageReporter;
+
+ /**
+ * @since 2.0
+ */
+ public function __construct() {
+ parent::__construct();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 2.0
+ */
+ protected function addDefaultParams() {
+ parent::addDefaultParams();
+
+ $this->mDescription = 'Sets up the SMW storage backend currently selected in LocalSettings.php.';
+
+ $this->addOption( 'backend', 'Execute the operation for the storage backend of the given name.', false, true, 'b' );
+
+ $this->addOption( 'delete', 'Delete all SMW data, uninstall the selected storage backend.' );
+ $this->addOption( 'skip-optimize', 'Skipping the table optimization process (not recommended).', false );
+ $this->addOption( 'skip-import', 'Skipping the import process.', false );
+
+ $this->addOption(
+ 'nochecks',
+ 'Run the script without providing prompts. Deletion will thus happen without the need to provide any confirmation.'
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @see Maintenance::getDbType
+ *
+ * @since 3.0
+ */
+ public function getDbType() {
+ return \Maintenance::DB_ADMIN;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function getConnection() {
+ return $this->getDB( DB_MASTER );
+ }
+
+ /**
+ * @see Maintenance::execute
+ *
+ * @since 2.0
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ StoreFactory::clear();
+
+ $this->loadGlobalFunctions();
+ $store = $this->getStore();
+
+ $connectionManager = ApplicationFactory::getInstance()->getConnectionManager();
+
+ // #2963 Use the Maintenance DB connection instead and the DB_ADMIN request
+ // to allow to use the admin user/pass, if set
+ $connectionManager->registerCallbackConnection( DB_MASTER, [ $this, 'getConnection' ] );
+
+ $store->setConnectionManager(
+ $connectionManager
+ );
+
+ $store->setMessageReporter(
+ $this->getMessageReporter()
+ );
+
+ $store->setOption( Installer::OPT_TABLE_OPTIMIZE, !$this->hasOption( 'skip-optimize' ) );
+ $store->setOption( Installer::OPT_IMPORT, !$this->hasOption( 'skip-import' ) );
+ $store->setOption( Installer::OPT_SUPPLEMENT_JOBS, true );
+
+ if ( $this->hasOption( 'delete' ) ) {
+ $this->dropStore( $store );
+ } else {
+ $store->setup();
+ }
+
+ // Avoid holding a reference
+ StoreFactory::clear();
+ }
+
+ protected function getMessageReporter() {
+
+ $messageReporterFactory = MessageReporterFactory::getInstance();
+
+ if ( $this->messageReporter === null && $this->getOption( 'quiet' ) ) {
+ $this->messageReporter = $messageReporterFactory->newNullMessageReporter();
+ } elseif( $this->messageReporter === null ) {
+ $this->messageReporter = $messageReporterFactory->newObservableMessageReporter();
+ $this->messageReporter->registerReporterCallback( [ $this, 'reportMessage' ] );
+ }
+
+ return $this->messageReporter;
+ }
+
+ protected function loadGlobalFunctions() {
+ global $smwgIP;
+
+ if ( !isset( $smwgIP ) ) {
+ $smwgIP = dirname( __FILE__ ) . '/../';
+ }
+
+ require_once ( $smwgIP . 'src/GlobalFunctions.php' );
+ }
+
+ protected function getStore() {
+ global $smwgDefaultStore;
+
+ $storeClass = $this->getOption( 'backend', $smwgDefaultStore );
+ $this->originalStore = $smwgDefaultStore;
+
+ if ( class_exists( $storeClass ) ) {
+ $smwgDefaultStore = $storeClass;
+ } else {
+ $this->error( "\nError: There is no backend class \"$storeClass\". Aborting.", 1 );
+ }
+
+ return StoreFactory::getStore( $storeClass );
+ }
+
+ protected function dropStore( Store $store ) {
+ $storeName = get_class( $store );
+
+ $verification = $this->promptDeletionVerification( $storeName );
+
+ if ( !$verification ) {
+ return;
+ }
+
+ $store->drop( !$this->isQuiet() );
+
+ // be sure to have some buffer, otherwise some PHPs complain
+ while ( ob_get_level() > 0 ) {
+ ob_end_flush();
+ }
+
+ $this->output( "\nYou can recreate them with this script followed by the use\n");
+ $this->output( "of the rebuildData.php script to rebuild their contents.\n");
+ }
+
+ /**
+ * @param string $storeName
+ *
+ * @return boolean
+ */
+ protected function promptDeletionVerification( $storeName ) {
+ $this->output( "You are about to delete all data stored in the SMW backend $storeName.\n" );
+
+ if ( $storeName === $this->originalStore ) {
+ $this->output( "This backend is CURRENTLY IN USE. Deleting it is likely to BREAK YOUR WIKI.\n" );
+ } else {
+ $this->output( "This backend is not currently in use. Deleting it should not cause any problems.\n" );
+ }
+ $this->output( "To undo this operation later on, a complete refresh of the data will be needed.\n" );
+
+ if ( !$this->hasOption( 'nochecks' ) ) {
+ print ( "If you are sure you want to proceed, type DELETE.\n" );
+
+ if ( $this->readconsole() !== 'DELETE' ) {
+ print ( "Aborting.\n\n" );
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\SetupStore';
+require_once ( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/maintenance/updateEntityCollation.php b/www/wiki/extensions/SemanticMediaWiki/maintenance/updateEntityCollation.php
new file mode 100644
index 00000000..2328cf91
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/maintenance/updateEntityCollation.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\TableFieldUpdater;
+use SMW\DIWikiPage;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\Setup;
+
+$basePath = getenv( 'MW_INSTALL_PATH' ) !== false ? getenv('MW_INSTALL_PATH' ) : __DIR__ . '/../../..';
+
+require_once $basePath . '/maintenance/Maintenance.php';
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UpdateEntityCollation extends \Maintenance {
+
+ public function __construct() {
+ $this->mDescription = 'Update the smw_sort field (relying on the $smwgEntityCollation setting)';
+ $this->addOption( 's', 'ID starting point', false, true );
+
+ parent::__construct();
+ }
+
+ /**
+ * @see Maintenance::addDefaultParams
+ *
+ * @since 3.0
+ */
+ protected function addDefaultParams() {
+
+ parent::addDefaultParams();
+ }
+
+ /**
+ * @see Maintenance::execute
+ */
+ public function execute() {
+
+ if ( !Setup::isEnabled() ) {
+ $this->reportMessage( "\nYou need to have SMW enabled in order to run the maintenance script!\n" );
+ exit;
+ }
+
+ if ( !Setup::isValid( true ) ) {
+ $this->reportMessage( "\nYou need to run `update.php` or `setupStore.php` first before continuing\nwith any maintenance tasks!\n" );
+ exit;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $store = $applicationFactory->getStore( 'SMW\SQLStore\SQLStore' );
+
+ $connection = $store->getConnection( 'mw.db' );
+ $tableFieldUpdater = new TableFieldUpdater( $store );
+
+ $condition = " smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ) . " AND smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWDELETEIW );
+ $i = 1;
+
+ if ( $this->hasOption( 's' ) ) {
+ $i = $this->getOption( 's' );
+ $condition .= ' AND smw_id > ' . $connection->addQuotes( $this->getOption( 's' ) );
+ }
+
+ $res = $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_sortkey'
+ ],
+ $condition,
+ __METHOD__
+ );
+
+ $expected = $res->numRows() + $i;
+
+ if ( $applicationFactory->getSettings()->get( 'smwgEntityCollation' ) !== $GLOBALS['wgCategoryCollation'] ) {
+ $this->reportMessage(
+ "\n" . 'The setting of $smwgEntityCollation and $wgCategoryCollation are different' . "\n" .
+ 'and may result in an inconsitent sorting display for entities.' . "\n"
+ );
+
+ $this->reportMessage( "\n" . '$smwgEntityCollation: ' . $applicationFactory->getSettings()->get( 'smwgEntityCollation' ) );
+ $this->reportMessage( "\n" . '$wgCategoryCollation: ' . $GLOBALS['wgCategoryCollation'] . "\n" );
+ }
+
+ $this->reportMessage(
+ "\nPerforming the update ..."
+ );
+
+ $this->reportMessage( "\n ... selecting $expected rows ..." );
+ $this->reportMessage( "\n" );
+
+ $this->doUpdate( $store, $tableFieldUpdater, $res, $i, $expected );
+ $this->reportMessage( "\n" );
+ }
+
+ private function doUpdate( $store, $tableFieldUpdater, $res, $i, $expected ) {
+ $property = new DIProperty( '_SKEY' );
+
+ foreach ( $res as $row ) {
+
+ if ( $row->smw_title === '' ) {
+ continue;
+ }
+
+ $i++;
+
+ $dataItem = $store->getObjectIds()->getDataItemById( $row->smw_id );
+ $pv = $store->getPropertyValues( $dataItem, $property );
+
+ $search = $this->getSortKey( $row, $pv );
+
+ if ( $search === '' && $row->smw_title !== '' ) {
+ $search = str_replace( '_', ' ', $row->smw_title );
+ }
+
+ $this->reportMessage(
+ "\r". sprintf( "%-35s%s", " ... updating document no.", sprintf( "%4.0f%% (%s/%s)", ( $i / $expected ) * 100, $i, $expected ) )
+ );
+
+ $tableFieldUpdater->updateSortField( $row->smw_id, $search );
+ }
+ }
+
+ private function getSortKey( $row, $pv ) {
+
+ if ( $pv !== [] ) {
+ return end( $pv )->getString();
+ }
+
+ if ( $row->smw_title{0} !== '_' ) {
+ return $row->smw_sortkey;
+ }
+
+ try {
+ $property = new DIProperty( $row->smw_title );
+ } catch ( PredefinedPropertyLabelMismatchException $e ) {
+ return $row->smw_sortkey;
+ }
+
+ return $property->getCanonicalLabel();
+ }
+
+ /**
+ * @see Maintenance::reportMessage
+ *
+ * @since 1.9
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->output( $message );
+ }
+
+}
+
+$maintClass = 'SMW\Maintenance\UpdateEntityCollation';
+require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/www/wiki/extensions/SemanticMediaWiki/phpcs.xml b/www/wiki/extensions/SemanticMediaWiki/phpcs.xml
new file mode 100644
index 00000000..960d4782
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/phpcs.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<!--
+ - https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml
+ - https://github.com/squizlabs/PHP_CodeSniffer/tree/master/CodeSniffer/Standards
+-->
+<ruleset name="MediaWiki">
+ <rule ref="Generic.Classes" />
+ <rule ref="Generic.CodeAnalysis" />
+ <rule ref="Generic.ControlStructures" />
+
+ <rule ref="Generic.Files.ByteOrderMark" />
+ <rule ref="Generic.Files.EndFileNewline" />
+ <rule ref="Generic.Files.LineEndings" />
+ <rule ref="Generic.Files.LineLength">
+ <properties>
+ <property name="lineLimit" value="180" />
+ <property name="absoluteLineLimit" value="180" />
+ </properties>
+ </rule>
+ <rule ref="Generic.Files.OneClassPerFile" />
+
+ <rule ref="Generic.Formatting.DisallowMultipleStatements" />
+
+ <rule ref="Generic.Functions.CallTimePassByReference" />
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
+ <rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie" />
+
+ <rule ref="Generic.Metrics.NestingLevel">
+ <properties>
+ <property name="nestingLevel" value="3" />
+ <property name="absoluteNestingLevel" value="3" />
+ </properties>
+ </rule>
+
+ <rule ref="Generic.Metrics.CyclomaticComplexity">
+ <properties>
+ <property name="complexity" value="10" />
+ <property name="absoluteComplexity" value="10" />
+ </properties>
+ </rule>
+
+ <rule ref="Generic.NamingConventions" />
+ <!-- TODO: create variation of this sniff that allows underscores in test methods -->
+ <rule ref="Generic.NamingConventions.CamelCapsFunctionName.ScopeNotCamelCaps">
+ <severity>0</severity>
+ </rule>
+
+ <rule ref="Generic.PHP.CharacterBeforePHPOpeningTag" />
+ <rule ref="Generic.PHP.DeprecatedFunctions" />
+ <rule ref="Generic.PHP.DisallowShortOpenTag" />
+ <rule ref="Generic.PHP.ForbiddenFunctions" />
+ <rule ref="Generic.PHP.LowerCaseConstant" />
+ <rule ref="Generic.PHP.LowerCaseKeyword" />
+ <rule ref="Generic.PHP.NoSilencedErrors" />
+ <rule ref="Generic.PHP.SAPIUsage" />
+
+ <rule ref="Generic.WhiteSpace.DisallowSpaceIndent" />
+
+ <rule ref="PSR1" />
+ <!-- TODO: create variation of this sniff that allows underscores in test methods -->
+ <rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
+ <severity>0</severity>
+ </rule>
+
+ <rule ref="PSR2.Classes.PropertyDeclaration" />
+ <rule ref="PSR2.ControlStructures.ElseIfDeclaration" />
+ <rule ref="PSR2.Namespaces" />
+
+ <rule ref="Squiz.Arrays.ArrayBracketSpacing" />
+ <rule ref="Squiz.CSS.SemicolonSpacing" />
+ <rule ref="Squiz.Classes.DuplicateProperty" />
+ <rule ref="Squiz.Classes.SelfMemberReference" />
+ <rule ref="Squiz.Classes.ValidClassName" />
+ <rule ref="Squiz.Functions.FunctionDuplicateArgument" />
+ <rule ref="Squiz.Functions.GlobalFunction" />
+ <rule ref="Squiz.Scope" />
+ <rule ref="Squiz.WhiteSpace.CastSpacing" />
+ <rule ref="Squiz.WhiteSpace.LanguageConstructSpacing" />
+ <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing" />
+
+ <!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/348 -->
+ <!--<rule ref="Squiz.WhiteSpace.OperatorSpacing" />-->
+
+ <rule ref="Squiz.WhiteSpace.ScopeClosingBrace" />
+ <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
+ <rule ref="Squiz.WhiteSpace.SemicolonSpacing" />
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace" />
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
+ <severity>0</severity>
+ </rule>
+
+ <rule ref="Zend.Files.ClosingTag" />
+</ruleset>
diff --git a/www/wiki/extensions/SemanticMediaWiki/phpmd.xml b/www/wiki/extensions/SemanticMediaWiki/phpmd.xml
new file mode 100644
index 00000000..1bc93f2c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/phpmd.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<ruleset xmlns="http://pmd.sf.net/ruleset/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
+ xsi:noNamespaceSchemaLocation=" http://pmd.sf.net/ruleset_xml_schema.xsd">
+
+ <rule ref="rulesets/codesize.xml">
+ <exclude name="TooManyMethods" />
+ <exclude name="ExcessiveClassComplexity" />
+ </rule>
+ <rule ref="rulesets/codesize.xml/TooManyMethods">
+ <properties>
+ <property name="maxmethods" value="20" />
+ </properties>
+ </rule>
+
+ <!-- todo: exclude the camel case method rule for the tests -->
+ <rule ref="rulesets/controversial.xml" />
+
+ <rule ref="rulesets/design.xml" />
+
+ <rule ref="rulesets/naming.xml">
+ <exclude name="ShortVariable" />
+ </rule>
+
+ <rule ref="rulesets/unusedcode.xml">
+ <exclude name="UnusedLocalVariable" />
+ </rule>
+</ruleset> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/phpunit.xml.dist b/www/wiki/extensions/SemanticMediaWiki/phpunit.xml.dist
new file mode 100644
index 00000000..6a1fd256
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/phpunit.xml.dist
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ bootstrap="tests/bootstrap.php"
+ cacheTokens="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ stopOnError="false"
+ stopOnFailure="false"
+ stopOnIncomplete="false"
+ stopOnSkipped="false"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ verbose="true">
+ <listeners>
+ <listener file="tests/phpunit/ExecutionTimeTestListener.php" class="SMW\Tests\ExecutionTimeTestListener">
+ <arguments>
+ <boolean>true</boolean>
+ <integer>10</integer>
+ </arguments>
+ </listener>
+ </listeners>
+ <testsuites>
+ <testsuite name="semantic-mediawiki-unit">
+ <directory>tests/phpunit/includes</directory>
+ <directory>tests/phpunit/Unit</directory>
+ </testsuite>
+ <testsuite name="semantic-mediawiki-integration">
+ <directory>tests/phpunit/Integration</directory>
+ <exclude>tests/phpunit/Integration/MediaWiki/Import</exclude>
+ <exclude>tests/phpunit/Integration/System</exclude>
+ </testsuite>
+ <testsuite name="semantic-mediawiki-import">
+ <directory>tests/phpunit/Integration/MediaWiki/Import</directory>
+ </testsuite>
+ <testsuite name="semantic-mediawiki-system">
+ <directory>tests/phpunit/Integration/System</directory>
+ </testsuite>
+ <testsuite name="semantic-mediawiki-benchmark">
+ <directory>tests/phpunit/Benchmark</directory>
+ </testsuite>
+ </testsuites>
+ <groups>
+ <exclude>
+ <group>semantic-mediawiki-benchmark</group>
+ </exclude>
+ </groups>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">src</directory>
+ <directory suffix=".php">includes</directory>
+ <directory suffix=".php">maintenance</directory>
+ </whitelist>
+ </filter>
+ <php>
+ <var name="smwgSemanticsEnabled" value="true"/>
+ <var name="wgUseFileCache" value="false"/>
+ <var name="smwgEntityCollation" value="identity"/>
+ <var name="smwgFieldTypeFeatures" value="false"/>
+ <var name="smwgCreateProtectionRight" value="false"/>
+ <var name="smwgSparqlDefaultGraph" value="http://example.org/phpunit-testrun"/>
+ <var name="smwgSparqlQFeatures" value="false"/>
+ <var name="smwgElasticsearchProfile" value="false"/>
+ <var name="smwgEntityLookupCacheType" value="hash"/>
+ <var name="smwgEnabledDeferredUpdate" value="false"/>
+ <var name="smwgImportReqVersion" value="false"/>
+ <var name="smwgEnabledFulltextSearch" value="false"/>
+ <var name="smwgEnabledQueryDependencyLinksStore" value="true"/>
+ <var name="smwgQueryResultCacheType" value="hash"/>
+ <var name="benchmarkPageCopyCount" value="1000"/>
+ <var name="benchmarkPageEditRepetitionCount" value="50"/>
+ </php>
+</phpunit>
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/DataURI.php b/www/wiki/extensions/SemanticMediaWiki/res/DataURI.php
new file mode 100644
index 00000000..4c21c224
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/DataURI.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * Collection of data URIs
+ *
+ * @license GNU GPL v2 or later
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+return [
+ 'footer' => "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAYAAABjyArgAAAPf0lEQVR4Ae1aBXBUS9Ol9PcfhyS4u7u7Q0hwd3d3l7yH80KSD4d4gLjL4h7cJe7urufrU3l3a1OLLHwuXTWVK7Nz557pOX26byr8T7MpEyt13/mkx8yz6QPmX/yLt35zL6TX6H8opXJvk3/qRkyJbYVK3XY8exGUhu9ZKYCCwmIkJKYjJycff4qlZhag1Sxv1DB0Qe3xbhix8TaGb7iFnksDeO2v2sZsuYPp+x6Wu9ZrmQqc09D1t9B0mtdPj01sK/SceS4d37GiEsDE9Db6zXZFqwku6DbZFhv2uKKgoAi6WmlJkbRiKLb6t2fgJDot8APt0btkJKfnw/VutHqC9Se5/8gLsf8P3W80xQPmzkHwuB9T7vr1Z/EIj8vGi8+pyM4rQvt5vj8FMD25ArftN4EpBfaeuIW2sx6i+9KP6LzoDRpMUkFvrAuWbLoGXawoOxlZb5xQlB4HxTZavCwHML13g/kLvpCM7Qr/J3F8QTyX1meFCm73onHBKwTG2++C1m2xP248T8Cvtu8xbsc93HudhMAPKTjnEYK6E9xBe/A2GX6BcTAUL737KpGLCEvfMI4P1dN4pGUVIDY574sA8xrnGJeSRy/G29B07Ln0Bp0X+qGouBTt5/r+eQCml/ac5oBOC9+g/7pY9F4VhuYz78HA2BONDW3w4WM08J0Vyo8W4KyrI/u1I0qLCr8IcHBMFsHFLzbvMX7nPSSk5qGmLCIBOe8ZgqXHnuJ9eAYB5Mti76W3yMotQt+V1wk0waP3o7C4hPQD2swDD9FypjdBxhMB3/l2FIpLSrkgoNEzv+bBfJbD9QguAGkEW06/wge5xvl5Poj583lwQkIamhraoYV4cLdlQei4+B3qTfSHnqEbaoy+BgfHR1AsPiETxy1uYvEOL6GUABQVFYNWkpeFosQP8jcTIE1oA8yXlnHLtnD/VdeRmVPELcyXwSHbD+JFnigUropOzIXRtrtcAITGZrM/rt6IJHjosSQA8399TI8DjePwvq1/ONwFRN6fY/IIXRf5c905DuxVEV8E+IqMOWjNDcQk5+Kg9Ts0nuKJvIJi8fpCOsCfD+D8/EJ0nWRJj0Wd8T6oM8FHtpgbB0CdEZfRZfQpLNzkCNPTAZi8/KosRCBaznoGAyNPjJpzCbm5BWpPVkwT4NazfXD/TZIWz3E70usueoWqAyKPz3oE07Ppldh+7jWvo4sAxuuvgtPgL9e5MByT13m/oyziGbdgvJT7pB5yMXcAKcXxViSOOnwo9+xTTp/5e9x5mcgdQ3B5nTsEQdFZfP6fD2Da7kNe0O9rhpqGLjK4kwDsjMbj7DBv0XH8csQeK1adQFfD42g67Dw6LQoROgkSbwyA3qir8FO9htq0Af6HaeRtBj5lUX8aYG5rR5dAnLW8A1/VG3G8Upic9EWtwRfQYshJNBtzHmOnHsCZ7csRZTUD0WeHIub8SLw8ORZLFmxGv7keaDPzFuqO9wRX+qiZv04Acztzy9Yydvt7AlYCugcl259HRTh5PMegOfbQN3SGnrE79Ec5oN/kC2gy2AyrdzgiOT4er+33I9piAMJ/qYOYYwaIP1kLib/pI+E3A4Qdrg+37QOxZNFGNJ5gh75TLyI+PvWbAHMRGKAUI8f+rUElpSjHVC408vGfBLCP6i3aTHREw6k30XzWfTSccp28KwC4otnI8wi89wxJqsMIO9QSSScEVHM9pF+sgSyr6tKqgS31XE2kmOvj7cEm2Lt4Fg6aXAZKi7X4tzg7CdvMysT9tL0PQHv8PpkSjPz3NwX3gOU7nLz6SX0+ZO1NmDp+ltjj/vMA8/2nrHJCk2m30G35Z/RdF4PuK4LQZPpd6Bt5gJ2XrjBFxPFOiD9ugNQzNZBtUw0F16qgyLky2AodKyPfoSoIeMYZPXw60g4HNmyhhKCOAI2JRl7YXWR5T4L7yZWoZ3wFIzfdBi09uxDdl5TP5KgGDlq9w6Td99XXqAKO2H9A7+UqHHP4iJ3nX8scXbHgUCBOXvtEzazuS6287/JbsN+A1WUeyPv8PemIfEpAKeV4b/HRJyAWD2VHsQ+vz/79ee3m+qi5mJmfmfNnzks3gHMk0rcbb4dWcwLRT7Tu8G3ZGLA+Ts4fw2CcRFDZxv0NDyLmUG0kWegJuFVR6FQFJe6VUepRCaXuZa3EtRIIeublaog31cfZtaNxx8cP9GJaSWEe0u+YIEcWJ9GyAfrNMgUpgpGd9jkqE1QAnBwTj5y8YrjciQLFPr2I16/djAQtMS0fvE5jxI8X2VYi6DAQccxVkilS1lFVUF6FiM4mOAsPB4KWkVMIyjwakxhKRGUeigZmMkG5RhsmaTPHpQan8TlULToBzMDWfrw1Gk+7je704DXR6L4sSM5viXeUbY2+I/cINRgg/UJ1gihgVgTcpXmoGwhysUsl5NlXQcbZmnh1qD327TgClBbSf0HLDfJDqvN4uB9fhsbGNupAQslE83oYC16joKdNFi+hhiX4mgCvOPEUA8UrFUC4MOxH47bmAlECUuLxPq3DfF8FYPYl4EwkCLYmXWlShCbA6gTF2i8MvFdrnJvuHDx2oa1wrgfIvU1nkoNvQF/O6b3sPNtovgQ0fWRaVkehc2WCSVDLN8+K4tVlXpx+vjriTtTHhJm78Pp1MNLTsgDQmQtRmp+GLRZPRfY5qyfJRIKZGbcogbn1IgE038dx9GI2LoQaYGZw3LY0eh7HsJGEgsbMi1qXyQe9+2NEphbApAb+hgtXUFiiC8CkCmVxfzzIWTs8RH0je9Q0coOekbusrprU0WLMRbis7Y9EUwFYOLbIuRKpQQtgxYsJcObF6ogya43Fszej1yQrdJlojUOm/pSB5VTEEuE9grlDuDRftjJpgcL+xNWPoDFFZT8ladAV4NOSWHC8ZtO9WIdQrn8TYMoxZReR49vO8SkHMKUkjRzN+YzadPvHZNqpczfRxthSbjqXSagRkiL3Pwwrs7OIONQG8UIRGZdqoEACWombNsCkjBLXysi7UhUZ52vi+a+90X/RHUk+wtBkyl16LE6cvl4OYKadTItpLKIwq1ICFNNhpbzJShs5UFeAz7oHg8ZUV6EIFo6+BbByrhgDoSbAvG/lG0ZuB41p8w8nGomJ6Tj+BxUOnvDD3OWXUH+kJW4H3EXQ/haI+tUAKadrINeuKpUDvZWeTGB5LKBXpJqgZEPiKT247RiF7gseCMChaDz1vgDkjmU73LQSDdLExF33SQ1a5cQpex6QUwmuulbLFycF8Hc87idg8x5rDTznDiC/UvY1nOxBNcLrVBtoIcqAx6QL/oY137Fb76ifyUrcVKEKpu6az+M4Sh+OMX3/Q7ThfP+UVJnbeeAMSyze5AC7jaPxcU8DJJw0QNp5AdmeSqIyikU5FLsxuFUWaqhM+YZUkWmxJxvj9MY5aDnZCfUnqqD/O+Us3uL0j5gq/+XqwQeO+aD+ADO067UOa40nwHxuP0Qdr4UU0cIMeDnizfRoSrcM4d3UP+gh5kgtxF4ygsVvokz6HkLN0dfKKmfCwxGRyX81gJl2/w3GYz8dPbi4BCOmm6PhoLNUFaymQW+MI3oN2QrPdR2QdMoAiRaSvQmoSebitSf0EX20IaLODEP2e0+hGXc0lSxwxjIr7D/mi4eBwVqpsgh2rcjN7Untmptf/CMAUAdT7/KYdV5yJGXaV/uzBk3+JS0o1TLa0HU31RU92nrzFyxZUuUwNjAw8zKL/Vpj2gaEM3ZQ8XwbYKrWBUINLabflDrwR/RcEYz2858L90m2JfzWYagJAnd2QtjBOog91gSRh5tILaI1Ih1W4v09Fd69C0VnUQ9rdzlB27QBZsRXeNFJ5BXtTwCY4zP95ot+tT/TchozMnod9TBNCbS3XyaqZSNBffYpldndNwHeb/mWZU6O922AU9Nz0GjoOXRY9AoDN8Rh2OZUSUAiJH2+w0IQB8OalUeQ5GeCJNUhJNy2wLadF9B7iiXqSZmy/tCzaNT3KKKiknUCmEZgldQZ2gAz2GkFQgNjVyYADHiaAFNlMDgp3skgSVmleU39LAuXIEwQNaMYMzRmdvTupx9TtIKdBsDKvHiPc2BRiMec17cB3rLfCvXGXEW7ec8F2Eh+LqInS2S/qS64T1hmi6KsJECSh+37rqHR1BvouPAV2s59JhP0oyzDsq26eTC1Kr0lLC6bjdtMDTBfll4REZ9DyaauyXK7si8tSWQcTQGYBXka6xszJOIzrVY8NOBpvLquwJT7XVgGP0uBxq8ZtHVmLyBGj9RKODQBpgrhwvKzFftx19Cov78CMLdroQSn1TAYZiWUoELzmQ/Qek6gSK3bskreApwrB0OHkWboMfkyxi11QMsRF4RCXshCxKD36ig0n3GfaTaajLFCTGzKdwHmp56YpFzQWHThsQLw3F8eg0YP5LZWPhWRF2km1u/54lygLwLMdJsgE1BFEzeZ6sl+THv5O+psvA5JZ1/NBWPK/VWASSX8DemNi60zwBevSD2052q0Hn6CmR1TaKbNzPDU4LItWu+APUe8MUU8uc7As2g5+yF6LAtCl6VBXBhyNZMVPAr8/F2ACcCaU885YW7ncgDbqSLUBZbIhBx6NwsxuCleQ+P21eBgLYAJLDNC1jsIJK35DK9y6THtsN0HTS5W6h9fBZhBksbveuyjE8CcpNGiQ6jYdiazLtQdY88fqrM7pfWdYYPo3z0zLS0brYwu02NRb1KA6N4AOS6jkdZGlkxedAKYiQA/i/O6JsBKDYAvpjkHKgUaRT+Dz9c8mGk4FQBfmoGLxiRB+ccXfs2msaSpEWRZwfsmwJw7F53zZJKjE8DFIs1ajNiENkOX8nMRzC/eEf1qhRpjnMo03ugrMFxgg1dvIqFpmw9ItKbe1fwgONoR6/e6g6YDwMrvtABmpsZtSCBYJyDvUn4xSPEzPcEjN3+Ng3ms/J8EpRtNVcbDbKyscWeoz0lXNOVT0Tc4mHPgvwJQYdBBtAFuaWSaQq9VLDMrD9W6LJbgZArF+K9S563uwuz8TXj7vyLwWoCVyEO2mXig68TLqD/KEp3GX8bGfa5KcUfL+D8LnDhTZHqo4gFKYyGdHKucszTJAhAL8IM1Umd+gicXsjjDz/vbzr5SF885LtNkfgJi0Z3gMCgRGIVW2KhCSE+a/wHE/8dQnsGmWXjngvNYSbFZ3+A5x1xj+pzHVCqo3OtgUoWKXbd6serPQjI5x/PWWzQdvB7mVv48/+H2OigF959F4NWnpK/2YcFEKVP+MzdiW+E/a/dt/n/tlpnxRGmVum/z5d9/t59vxJTY/hEVgbJsm2iLkwAAAABJRU5ErkJggg==",
+]; \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/Resources.php b/www/wiki/extensions/SemanticMediaWiki/res/Resources.php
new file mode 100644
index 00000000..4bab89cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/Resources.php
@@ -0,0 +1,664 @@
+<?php
+
+/**
+ * Collection of resource module definitions
+ *
+ * @license GNU GPL v2 or later
+ * @since 1.8
+ *
+ * @author mwjames
+ */
+
+// #1466 (Make sure to work on both Win and Ux)
+$pathParts = explode( '/', str_replace( DIRECTORY_SEPARATOR, '/', __DIR__ ) );
+
+$moduleTemplate = [
+ 'localBasePath' => __DIR__,
+ 'remoteExtPath' => implode( '/', array_slice( $pathParts, -2 ) ),
+ 'group' => 'ext.smw'
+];
+
+return [
+ // SMW core class
+ 'ext.smw' => $moduleTemplate + [
+ 'scripts' => 'smw/ext.smw.js',
+ 'dependencies' => 'ext.jquery.async',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Resource is loaded at the top otherwise the stylesheet will only
+ // become active after all content is loaded with icons appearing with a
+ // delay due to missing stylesheet definitions at the time of the display
+ 'ext.smw.style' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/ext.smw.css',
+ 'smw/ext.smw.skin.css',
+ 'smw/ext.smw.dropdown.css',
+ 'smw/ext.smw.table.css',
+ 'smw/ext.smw.tabs.css',
+ 'smw/ext.smw.factbox.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'smw.ui' => $moduleTemplate + [
+ 'scripts' => 'smw/smw.ui.js',
+ 'dependencies' => [ 'ext.smw', 'jquery.selectmenu' ],
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'smw.ui.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'jquery/jquery.selectmenu.css',
+ 'smw/smw.selectmenu.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'ext.smw.special.style' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/special/ext.smw.special.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // https://github.com/TerryZ/SelectMenu
+ 'jquery.selectmenu' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.selectmenu.js',
+ 'dependencies' => [
+ 'jquery.selectmenu.styles'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'jquery.selectmenu.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'jquery/jquery.selectmenu.css',
+ 'smw/smw.selectmenu.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Load the module explicitly, otherwise mobile will complain with
+ // "Uncaught Error: Unknown dependency: jquery.async"
+ 'ext.jquery.async' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.async.js',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Avoid "Warning: Use of the json module is deprecated since MediaWiki 1.29"
+ // jStorage was added in MW 1.20
+ 'ext.jquery.jStorage' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.jstorage.js',
+ 'dependencies' => version_compare( $GLOBALS['wgVersion'], '1.29', '<' ) ? 'json' : [],
+ ],
+
+ // md5 hash key generator
+ 'ext.jquery.md5' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.md5.js'
+ ],
+
+ // dataItem representation
+ 'ext.smw.dataItem' => $moduleTemplate + [
+ 'scripts' => [
+ 'smw/data/ext.smw.dataItem.wikiPage.js',
+ 'smw/data/ext.smw.dataItem.uri.js',
+ 'smw/data/ext.smw.dataItem.time.js',
+ 'smw/data/ext.smw.dataItem.property.js',
+ 'smw/data/ext.smw.dataItem.unknown.js',
+ 'smw/data/ext.smw.dataItem.number.js',
+ 'smw/data/ext.smw.dataItem.text.js',
+ 'smw/data/ext.smw.dataItem.geo.js',
+ ],
+ 'dependencies' => [
+ 'ext.smw',
+ 'mediawiki.Title',
+ 'mediawiki.Uri'
+ ]
+ ],
+
+ // dataValue representation
+ 'ext.smw.dataValue' => $moduleTemplate + [
+ 'scripts' => [
+ 'smw/data/ext.smw.dataValue.quantity.js',
+ ],
+ 'dependencies' => 'ext.smw.dataItem'
+ ],
+
+ // dataItem representation
+ 'ext.smw.data' => $moduleTemplate + [
+ 'scripts' => 'smw/data/ext.smw.data.js',
+ 'dependencies' => [
+ 'ext.smw.dataItem',
+ 'ext.smw.dataValue'
+ ]
+ ],
+
+ // Query
+ 'ext.smw.query' => $moduleTemplate + [
+ 'scripts' => 'smw/query/ext.smw.query.js',
+ 'dependencies' => [
+ 'ext.smw',
+ 'mediawiki.util'
+ ],
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // API
+ 'ext.smw.api' => $moduleTemplate + [
+ 'scripts' => 'smw/api/ext.smw.api.js',
+ 'dependencies' => [
+ 'mediawiki.util',
+ 'ext.smw.data',
+ 'ext.smw.query',
+ 'ext.jquery.jStorage',
+ 'ext.jquery.md5'
+ ]
+ ],
+
+ // https://github.com/devbridge/jQuery-Autocomplete
+ 'ext.jquery.autocomplete' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.autocomplete.js',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Tooltip qtip2 resources
+ 'ext.jquery.qtip.styles' => $moduleTemplate + [
+ 'styles' => 'jquery/jquery.qtip.css',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Tooltip qtip2 resources
+ 'ext.jquery.qtip' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.qtip.js',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Tooltip
+ 'ext.smw.tooltip.styles' => $moduleTemplate + [
+ 'styles' => [
+ // Style dependencies don't work
+ // therefore make sure to load it
+ // together
+ 'jquery/jquery.qtip.css',
+ 'smw/util/ext.smw.util.tooltip.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Tooltip
+ 'ext.smw.tooltip' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.util.tooltip.js',
+ 'dependencies' => [
+ 'ext.smw.tooltip.styles',
+ 'ext.smw',
+ 'ext.jquery.qtip'
+ ],
+ 'messages' => [
+ 'smw-ui-tooltip-title-property',
+ 'smw-ui-tooltip-title-quantity',
+ 'smw-ui-tooltip-title-info',
+ 'smw-ui-tooltip-title-service',
+ 'smw-ui-tooltip-title-warning',
+ 'smw-ui-tooltip-title-parameter',
+ 'smw-ui-tooltip-title-event',
+ ],
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'ext.smw.tooltips' => $moduleTemplate + [
+ 'dependencies' => [
+ 'ext.smw.style',
+ 'ext.smw.tooltip'
+ ],
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Autocomplete resources
+ 'ext.smw.autocomplete' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.util.autocomplete.js',
+ 'dependencies' => 'jquery.ui.autocomplete',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Purge resources
+ 'ext.smw.purge' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.util.purge.js',
+ 'messages' => [
+ 'smw-purge-failed'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // VTabs
+ 'ext.smw.vtabs.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/util/ext.smw.vertical.tabs.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // VTabs
+ 'ext.smw.vtabs' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/util/ext.smw.vertical.tabs.css'
+ ],
+ 'scripts' => [
+ 'smw/util/ext.smw.vertical.tabs.js'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Modal
+ 'ext.smw.modal.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/util/ext.smw.modal.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Modal
+ 'ext.smw.modal' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/util/ext.smw.modal.css'
+ ],
+ 'scripts' => [
+ 'smw/util/ext.smw.modal.js'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Special:Search
+ 'smw.special.search.styles' => $moduleTemplate + [
+ 'styles' => 'smw/special.search/search.css',
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'smw.special.search' => $moduleTemplate + [
+ 'scripts' => [
+ 'smw/special.search/search.namespace.js',
+ 'smw/special.search/search.input.js',
+ 'smw/special.search/search.form.js'
+ ],
+ 'styles' => 'smw/special.search/search.css',
+ 'position' => 'top',
+ 'dependencies' => [
+ 'ext.smw',
+ 'smw.ui'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ],
+ 'messages' => [
+ 'smw-search-hide',
+ 'smw-search-show',
+ ],
+ ],
+
+ // Postproc resources
+ 'ext.smw.postproc' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.util.postproc.js',
+ 'position' => 'top',
+ 'messages' => [
+ 'smw-postproc-queryref'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // https://github.com/ichord/Caret.js
+ 'ext.jquery.caret' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.caret.js',
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // https://github.com/ichord/At.js
+ 'ext.jquery.atwho' => $moduleTemplate + [
+ 'scripts' => 'jquery/jquery.atwho.js',
+ 'styles' => 'jquery/jquery.atwho.css',
+ 'position' => 'top',
+ 'dependencies' => [
+ 'ext.jquery.caret'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.suggester' => $moduleTemplate + [
+ 'scripts' => 'smw/suggester/ext.smw.suggester.js',
+ 'position' => 'top',
+ 'dependencies' => [
+ 'ext.smw',
+ 'ext.jquery.atwho'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.suggester.textInput' => $moduleTemplate + [
+ 'scripts' => 'smw/suggester/ext.smw.suggester.textInput.js',
+ 'position' => 'top',
+ 'dependencies' => [
+ 'ext.smw',
+ 'ext.smw.suggester'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.autocomplete.page' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.util.autocomplete.page.js',
+ 'dependencies' => [
+ 'mediawiki.util',
+ 'ext.jquery.autocomplete'
+ ],
+ 'position' => 'bottom',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'ext.smw.autocomplete.property' => $moduleTemplate + [
+ 'scripts' => [
+ 'smw/util/ext.smw.util.autocomplete.property.js',
+ 'smw/util/ext.smw.util.autocomplete.propertyvalue.js',
+ 'smw/util/ext.smw.util.autocomplete.propertysubject.js'
+ ],
+ 'dependencies' => [
+ 'mediawiki.util',
+ 'ext.jquery.autocomplete'
+ ],
+ 'position' => 'bottom',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Special:Ask
+ 'ext.smw.ask.styles' => $moduleTemplate + [
+ 'styles' => 'smw/special/ext.smw.special.ask.css',
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Special:Ask
+ 'ext.smw.ask' => $moduleTemplate + [
+ 'scripts' => 'smw/special/ext.smw.special.ask.js',
+ 'dependencies' => [
+ 'ext.smw.tooltip',
+ 'ext.smw.style',
+ 'ext.smw.ask.styles',
+ 'ext.smw.suggester'
+ ],
+ 'messages' => [
+ 'smw-ask-delete',
+ 'smw-ask-format-selection-help',
+ 'smw-ask-condition-change-info',
+ 'smw-ask-format-change-info',
+ 'smw-ask-format-export-info',
+ 'smw-ask-format-help-link',
+ 'smw-section-expand',
+ 'smw-section-collapse'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Table styles
+ 'ext.smw.table.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/ext.smw.table.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Facts and browse
+ 'ext.smw.browse.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/ext.smw.table.css',
+ 'smw/special/ext.smw.special.browse.css',
+ 'smw/special/ext.smw.special.browse.skin.css',
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.browse' => $moduleTemplate + [
+ 'scripts' => 'smw/special/ext.smw.special.browse.js',
+ 'dependencies' => [
+ 'mediawiki.api',
+ 'ext.smw.style'
+ ],
+ 'position' => 'top',
+ 'messages' => [
+ 'smw-browse-api-subject-serialization-invalid'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.browse.autocomplete' => $moduleTemplate + [
+ 'dependencies' => [
+ 'ext.smw.browse',
+ 'ext.smw.autocomplete.page'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Special:Admin/SemanticMediaWiki
+ 'ext.smw.admin' => $moduleTemplate + [
+ 'scripts' => 'smw/special/ext.smw.special.admin.js',
+ 'dependencies' => [
+ 'mediawiki.api'
+ ],
+ 'messages' => [
+ 'smw-no-data-available',
+ 'smw-list-count',
+ 'smw-list-count-from-cache'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Personal resource
+ 'ext.smw.personal' => $moduleTemplate + [
+ 'scripts' => 'smw/util/ext.smw.personal.js',
+ 'dependencies' => [
+ 'ext.smw.tooltip',
+ 'mediawiki.api'
+ ],
+ 'messages' => [
+ 'smw-personal-jobqueue-watchlist'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // TableResultPrinter resource
+ 'smw.tableprinter.datatable' => $moduleTemplate + [
+ 'scripts' => [
+ 'smw/printer/ext.smw.tableprinter.js'
+ ],
+ 'styles' => [
+ 'smw/printer/ext.smw.tableprinter.css',
+ 'smw/printer/ext.smw.tableprinter.skin.css'
+ ],
+ 'dependencies' => [
+ 'onoi.dataTables',
+ 'ext.smw.query'
+ ],
+ 'position' => 'top',
+ 'messages' => [
+ "smw-format-datatable-emptytable",
+ "smw-format-datatable-info",
+ "smw-format-datatable-infoempty",
+ "smw-format-datatable-infofiltered",
+ "smw-format-datatable-infothousands",
+ "smw-format-datatable-lengthmenu",
+ "smw-format-datatable-loadingrecords",
+ "smw-format-datatable-processing",
+ "smw-format-datatable-search",
+ "smw-format-datatable-zerorecords",
+ "smw-format-datatable-first",
+ "smw-format-datatable-last",
+ "smw-format-datatable-next",
+ "smw-format-datatable-previous",
+ "smw-format-datatable-sortascending",
+ "smw-format-datatable-sortdescending",
+ "smw-format-datatable-toolbar-export"
+ ],
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ 'smw.tableprinter.datatable.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/printer/ext.smw.tableprinter.css',
+ 'smw/printer/ext.smw.tableprinter.skin.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [ 'mobile', 'desktop' ]
+ ],
+
+ // Deferred
+ 'ext.smw.deferred.styles' => $moduleTemplate + [
+ 'position' => 'top',
+ 'styles' => [
+ 'smw/deferred/ext.smw.deferred.css',
+ 'smw/deferred/ext.smw.deferred.skin.css'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'ext.smw.deferred' => $moduleTemplate + [
+ 'position' => 'top',
+ 'styles' => [
+ 'smw/deferred/ext.smw.deferred.css',
+ 'smw/deferred/ext.smw.deferred.skin.css'
+ ],
+ 'scripts' => [ 'smw/deferred/ext.smw.deferred.js' ],
+ 'dependencies' => [
+ 'mediawiki.api',
+ 'mediawiki.api.parse',
+ 'onoi.rangeslider'
+ ],
+ 'messages' => [
+ 'smw_result_noresults'
+ ],
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ // Page styles
+ 'ext.smw.page.styles' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/ext.smw.page.css',
+ 'smw/ext.smw.table.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+ 'smw.property.page' => $moduleTemplate + [
+ 'position' => 'top',
+ 'scripts' => [ 'smw/util/smw.property.page.js' ],
+ 'dependencies' => [
+ 'mediawiki.api',
+ 'mediawiki.api.parse',
+ 'ext.smw.tooltip',
+ ],
+ 'messages' => [
+ 'smw_result_noresults'
+ ],
+ ],
+
+ // Schema content styles
+ 'smw.content.schema' => $moduleTemplate + [
+ 'styles' => [
+ 'smw/smw.schema.css',
+ 'smw/ext.smw.table.css'
+ ],
+ 'position' => 'top',
+ 'targets' => [
+ 'mobile',
+ 'desktop'
+ ]
+ ],
+
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/docs/jsduck.external.js b/www/wiki/extensions/SemanticMediaWiki/res/docs/jsduck.external.js
new file mode 100644
index 00000000..6d87218a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/docs/jsduck.external.js
@@ -0,0 +1,37 @@
+/**
+ * @class jQuery
+ * @source <http://api.jquery.com/>
+ */
+
+/**
+ * @method ajax
+ * @source <http://api.jquery.com/jQuery.ajax/>
+ * @return {jqXHR}
+ */
+
+/**
+ * @class jQuery.Event
+ * @source <http://api.jquery.com/Types/#Event>
+ */
+
+/**
+ * @class jQuery.Promise
+ * @source <http://api.jquery.com/Types/#Promise>
+ */
+
+/**
+ * @class jQuery.Deferred
+ * @mixins jQuery.Promise
+ * @source <http://api.jquery.com/jQuery.Deferred/>
+ */
+
+/**
+ * @class jQuery.jqXHR
+ * @source <http://api.jquery.com/Types/#jqXHR>
+ * @alternateClassName jqXHR
+ */
+
+/**
+ * @class QUnit
+ * @source <http://api.qunitjs.com/>
+ */
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.async.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.async.js
new file mode 100644
index 00000000..2161f6b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.async.js
@@ -0,0 +1,69 @@
+/*
+ * jQuery Asynchronous Plugin 1.0
+ *
+ * Copyright (c) 2008 Vincent Robert (genezys.net)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ */
+(function($){
+
+// opts.delay : (default 10) delay between async call in ms
+// opts.bulk : (default 500) delay during which the loop can continue synchronously without yielding the CPU
+// opts.test : (default true) function to test in the while test part
+// opts.loop : (default empty) function to call in the while loop part
+// opts.end : (default empty) function to call at the end of the while loop
+$.whileAsync = function(opts) {
+ var delay = Math.abs(opts.delay) || 10,
+ bulk = isNaN(opts.bulk) ? 500 : Math.abs(opts.bulk),
+ test = opts.test || function(){ return true; },
+ loop = opts.loop || function(){},
+ end = opts.end || function(){};
+
+ (function(){
+
+ var t = false,
+ begin = new Date();
+
+ while( t = test() ) {
+ loop();
+ if( bulk === 0 || (new Date() - begin) > bulk ) {
+ break;
+ }
+ }
+ if( t ) {
+ setTimeout(arguments.callee, delay);
+ }
+ else {
+ end();
+ }
+
+ })();
+};
+
+// opts.delay : (default 10) delay between async call in ms
+// opts.bulk : (default 500) delay during which the loop can continue synchronously without yielding the CPU
+// opts.loop : (default empty) function to call in the each loop part, signature: function(index, value) this = value
+// opts.end : (default empty) function to call at the end of the each loop
+$.eachAsync = function(array, opts) {
+ var i = 0,
+ l = array.length,
+ loop = opts.loop || function(){};
+
+ $.whileAsync(
+ $.extend(opts, {
+ test: function() { return i < l; },
+ loop: function() {
+ var val = array[i];
+ return loop.call(val, i++, val);
+ }
+ })
+ );
+};
+
+$.fn.eachAsync = function(opts) {
+ $.eachAsync(this, opts);
+ return this;
+}
+
+})(jQuery); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.css b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.css
new file mode 100644
index 00000000..f5538dcd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.css
@@ -0,0 +1,74 @@
+.atwho-view {
+ position:absolute;
+ top: 0;
+ left: 0;
+ display: none;
+ margin-top: 18px;
+ background: white;
+ color: rgb(111, 128, 146);
+ border: 1px solid #DDD;
+ border-radius: 3px;
+ box-shadow: 0 0 5px rgba(0,0,0,0.1);
+ min-width: 120px;
+ z-index: 11110 !important;
+}
+
+.atwho-view .atwho-header {
+ padding: 5px;
+ margin: 5px;
+ cursor: pointer;
+ border-bottom: solid 1px #eaeff1;
+ color: #6f8092;
+ font-size: 11px;
+ /* font-weight: bold; */
+}
+
+.atwho-view .atwho-header .small {
+ color: #6f8092;
+ float: right;
+ padding-top: 2px;
+ margin-right: -5px;
+ font-size: 12px;
+ font-weight: normal;
+}
+
+.atwho-view .atwho-header:hover {
+ cursor: default;
+}
+
+.atwho-view .cur {
+ background: #F0F0F0;
+ color: rgb(111, 128, 146);
+}
+.atwho-view .cur small {
+ color: white;
+}
+.atwho-view strong {
+ color: #3399FF;
+ font-weight: normal;
+}
+.atwho-view .cur strong {
+ color: #3399FF;
+ /* font:bold; */
+}
+.atwho-view ul {
+ /* width: 100px; */
+ list-style:none;
+ padding:0;
+ margin:auto;
+ max-height: 200px;
+ overflow-y: auto;
+}
+.atwho-view ul li {
+ display: block;
+ padding: 5px 10px;
+ border-bottom: 1px solid #DDD;
+ cursor: pointer;
+ /* border-top: 1px solid #C8C8C8; */
+ margin-bottom:0;
+}
+.atwho-view small {
+ font-size: smaller;
+ color: #777;
+ font-weight: normal;
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.js
new file mode 100644
index 00000000..5456c97b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.atwho.js
@@ -0,0 +1,1212 @@
+/**
+ * at.js - 1.5.4
+ * Copyright (c) 2017 chord.luo <chord.luo@gmail.com>;
+ * Homepage: http://ichord.github.com/At.js
+ * License: MIT
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module unless amdModuleId is set
+ define(["jquery"], function (a0) {
+ return (factory(a0));
+ });
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory(require("jquery"));
+ } else {
+ factory(jQuery);
+ }
+}(this, function ($) {
+var DEFAULT_CALLBACKS, KEY_CODE;
+
+KEY_CODE = {
+ ESC: 27,
+ TAB: 9,
+ ENTER: 13,
+ CTRL: 17,
+ A: 65,
+ P: 80,
+ N: 78,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ BACKSPACE: 8,
+ SPACE: 32
+};
+
+DEFAULT_CALLBACKS = {
+ beforeSave: function(data) {
+ return Controller.arrayToDefaultHash(data);
+ },
+ matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
+ var _a, _y, match, regexp, space;
+ flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ if (should_startWithSpace) {
+ flag = '(?:^|\\s)' + flag;
+ }
+ _a = decodeURI("%C3%80");
+ _y = decodeURI("%C3%BF");
+ space = acceptSpaceBar ? "\ " : "";
+ regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');
+ match = regexp.exec(subtext);
+ if (match) {
+ return match[2] || match[1];
+ } else {
+ return null;
+ }
+ },
+ filter: function(query, data, searchKey) {
+ var _results, i, item, len;
+ _results = [];
+ for (i = 0, len = data.length; i < len; i++) {
+ item = data[i];
+ if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
+ _results.push(item);
+ }
+ }
+ return _results;
+ },
+ remoteFilter: null,
+ sorter: function(query, items, searchKey) {
+ var _results, i, item, len;
+ if (!query) {
+ return items;
+ }
+ _results = [];
+ for (i = 0, len = items.length; i < len; i++) {
+ item = items[i];
+ item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());
+ if (item.atwho_order > -1) {
+ _results.push(item);
+ }
+ }
+ return _results.sort(function(a, b) {
+ return a.atwho_order - b.atwho_order;
+ });
+ },
+ tplEval: function(tpl, map) {
+ var error, error1, template;
+ template = tpl;
+ try {
+ if (typeof tpl !== 'string') {
+ template = tpl(map);
+ }
+ return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {
+ return map[key];
+ });
+ } catch (error1) {
+ error = error1;
+ return "";
+ }
+ },
+ highlighter: function(li, query) {
+ var regexp;
+ if (!query) {
+ return li;
+ }
+ regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig');
+ return li.replace(regexp, function(str, $1, $2, $3) {
+ return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
+ });
+ },
+ beforeInsert: function(value, $li, e) {
+ return value;
+ },
+ beforeReposition: function(offset) {
+ return offset;
+ },
+ afterMatchFailed: function(at, el) {}
+};
+
+var App;
+
+App = (function() {
+ function App(inputor) {
+ this.currentFlag = null;
+ this.controllers = {};
+ this.aliasMaps = {};
+ this.$inputor = $(inputor);
+ this.setupRootElement();
+ this.listen();
+ }
+
+ App.prototype.createContainer = function(doc) {
+ var ref;
+ if ((ref = this.$el) != null) {
+ ref.remove();
+ }
+ return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
+ };
+
+ App.prototype.setupRootElement = function(iframe, asRoot) {
+ var error, error1;
+ if (asRoot == null) {
+ asRoot = false;
+ }
+ if (iframe) {
+ this.window = iframe.contentWindow;
+ this.document = iframe.contentDocument || this.window.document;
+ this.iframe = iframe;
+ } else {
+ this.document = this.$inputor[0].ownerDocument;
+ this.window = this.document.defaultView || this.document.parentWindow;
+ try {
+ this.iframe = this.window.frameElement;
+ } catch (error1) {
+ error = error1;
+ this.iframe = null;
+ if ($.fn.atwho.debug) {
+ throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);
+ }
+ }
+ }
+ return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
+ };
+
+ App.prototype.controller = function(at) {
+ var c, current, currentFlag, ref;
+ if (this.aliasMaps[at]) {
+ current = this.controllers[this.aliasMaps[at]];
+ } else {
+ ref = this.controllers;
+ for (currentFlag in ref) {
+ c = ref[currentFlag];
+ if (currentFlag === at) {
+ current = c;
+ break;
+ }
+ }
+ }
+ if (current) {
+ return current;
+ } else {
+ return this.controllers[this.currentFlag];
+ }
+ };
+
+ App.prototype.setContextFor = function(at) {
+ this.currentFlag = at;
+ return this;
+ };
+
+ App.prototype.reg = function(flag, setting) {
+ var base, controller;
+ controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
+ if (setting.alias) {
+ this.aliasMaps[setting.alias] = flag;
+ }
+ controller.init(setting);
+ return this;
+ };
+
+ App.prototype.listen = function() {
+ return this.$inputor.on('compositionstart', (function(_this) {
+ return function(e) {
+ var ref;
+ if ((ref = _this.controller()) != null) {
+ ref.view.hide();
+ }
+ _this.isComposing = true;
+ return null;
+ };
+ })(this)).on('compositionend', (function(_this) {
+ return function(e) {
+ _this.isComposing = false;
+ setTimeout(function(e) {
+ return _this.dispatch(e);
+ });
+ return null;
+ };
+ })(this)).on('keyup.atwhoInner', (function(_this) {
+ return function(e) {
+ return _this.onKeyup(e);
+ };
+ })(this)).on('keydown.atwhoInner', (function(_this) {
+ return function(e) {
+ return _this.onKeydown(e);
+ };
+ })(this)).on('blur.atwhoInner', (function(_this) {
+ return function(e) {
+ var c;
+ if (c = _this.controller()) {
+ c.expectedQueryCBId = null;
+ return c.view.hide(e, c.getOpt("displayTimeout"));
+ }
+ };
+ })(this)).on('click.atwhoInner', (function(_this) {
+ return function(e) {
+ return _this.dispatch(e);
+ };
+ })(this)).on('scroll.atwhoInner', (function(_this) {
+ return function() {
+ var lastScrollTop;
+ lastScrollTop = _this.$inputor.scrollTop();
+ return function(e) {
+ var currentScrollTop, ref;
+ currentScrollTop = e.target.scrollTop;
+ if (lastScrollTop !== currentScrollTop) {
+ if ((ref = _this.controller()) != null) {
+ ref.view.hide(e);
+ }
+ }
+ lastScrollTop = currentScrollTop;
+ return true;
+ };
+ };
+ })(this)());
+ };
+
+ App.prototype.shutdown = function() {
+ var _, c, ref;
+ ref = this.controllers;
+ for (_ in ref) {
+ c = ref[_];
+ c.destroy();
+ delete this.controllers[_];
+ }
+ this.$inputor.off('.atwhoInner');
+ return this.$el.remove();
+ };
+
+ App.prototype.dispatch = function(e) {
+ var _, c, ref, results;
+ ref = this.controllers;
+ results = [];
+ for (_ in ref) {
+ c = ref[_];
+ results.push(c.lookUp(e));
+ }
+ return results;
+ };
+
+ App.prototype.onKeyup = function(e) {
+ var ref;
+ switch (e.keyCode) {
+ case KEY_CODE.ESC:
+ e.preventDefault();
+ if ((ref = this.controller()) != null) {
+ ref.view.hide();
+ }
+ break;
+ case KEY_CODE.DOWN:
+ case KEY_CODE.UP:
+ case KEY_CODE.CTRL:
+ case KEY_CODE.ENTER:
+ $.noop();
+ break;
+ case KEY_CODE.P:
+ case KEY_CODE.N:
+ if (!e.ctrlKey) {
+ this.dispatch(e);
+ }
+ break;
+ default:
+ this.dispatch(e);
+ }
+ };
+
+ App.prototype.onKeydown = function(e) {
+ var ref, view;
+ view = (ref = this.controller()) != null ? ref.view : void 0;
+ if (!(view && view.visible())) {
+ return;
+ }
+ switch (e.keyCode) {
+ case KEY_CODE.ESC:
+ e.preventDefault();
+ view.hide(e);
+ break;
+ case KEY_CODE.UP:
+ e.preventDefault();
+ view.prev();
+ break;
+ case KEY_CODE.DOWN:
+ e.preventDefault();
+ view.next();
+ break;
+ case KEY_CODE.P:
+ if (!e.ctrlKey) {
+ return;
+ }
+ e.preventDefault();
+ view.prev();
+ break;
+ case KEY_CODE.N:
+ if (!e.ctrlKey) {
+ return;
+ }
+ e.preventDefault();
+ view.next();
+ break;
+ case KEY_CODE.TAB:
+ case KEY_CODE.ENTER:
+ case KEY_CODE.SPACE:
+ if (!view.visible()) {
+ return;
+ }
+ if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {
+ return;
+ }
+ if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {
+ return;
+ }
+ if (view.highlighted()) {
+ e.preventDefault();
+ view.choose(e);
+ } else {
+ view.hide(e);
+ }
+ break;
+ default:
+ $.noop();
+ }
+ };
+
+ return App;
+
+})();
+
+var Controller,
+ slice = [].slice;
+
+Controller = (function() {
+ Controller.prototype.uid = function() {
+ return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
+ };
+
+ function Controller(app, at1) {
+ this.app = app;
+ this.at = at1;
+ this.$inputor = this.app.$inputor;
+ this.id = this.$inputor[0].id || this.uid();
+ this.expectedQueryCBId = null;
+ this.setting = null;
+ this.query = null;
+ this.pos = 0;
+ this.range = null;
+ if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {
+ this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
+ }
+ this.model = new Model(this);
+ this.view = new View(this);
+ }
+
+ Controller.prototype.init = function(setting) {
+ this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
+ this.view.init();
+ return this.model.reload(this.setting.data);
+ };
+
+ Controller.prototype.destroy = function() {
+ this.trigger('beforeDestroy');
+ this.model.destroy();
+ this.view.destroy();
+ return this.$el.remove();
+ };
+
+ Controller.prototype.callDefault = function() {
+ var args, error, error1, funcName;
+ funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
+ try {
+ return DEFAULT_CALLBACKS[funcName].apply(this, args);
+ } catch (error1) {
+ error = error1;
+ return $.error(error + " Or maybe At.js doesn't have function " + funcName);
+ }
+ };
+
+ Controller.prototype.trigger = function(name, data) {
+ var alias, eventName;
+ if (data == null) {
+ data = [];
+ }
+ data.push(this);
+ alias = this.getOpt('alias');
+ eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";
+ return this.$inputor.trigger(eventName, data);
+ };
+
+ Controller.prototype.callbacks = function(funcName) {
+ return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
+ };
+
+ Controller.prototype.getOpt = function(at, default_value) {
+ var e, error1;
+ try {
+ return this.setting[at];
+ } catch (error1) {
+ e = error1;
+ return null;
+ }
+ };
+
+ Controller.prototype.insertContentFor = function($li) {
+ var data, tpl;
+ tpl = this.getOpt('insertTpl');
+ data = $.extend({}, $li.data('item-data'), {
+ 'atwho-at': this.at
+ });
+ return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
+ };
+
+ Controller.prototype.renderView = function(data) {
+ var searchKey;
+ searchKey = this.getOpt("searchKey");
+ data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);
+ return this.view.render(data.slice(0, this.getOpt('limit')));
+ };
+
+ Controller.arrayToDefaultHash = function(data) {
+ var i, item, len, results;
+ if (!$.isArray(data)) {
+ return data;
+ }
+ results = [];
+ for (i = 0, len = data.length; i < len; i++) {
+ item = data[i];
+ if ($.isPlainObject(item)) {
+ results.push(item);
+ } else {
+ results.push({
+ name: item
+ });
+ }
+ }
+ return results;
+ };
+
+ Controller.prototype.lookUp = function(e) {
+ var query, wait;
+ if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
+ return;
+ }
+ if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
+ return;
+ }
+ query = this.catchQuery(e);
+ if (!query) {
+ this.expectedQueryCBId = null;
+ return query;
+ }
+ this.app.setContextFor(this.at);
+ if (wait = this.getOpt('delay')) {
+ this._delayLookUp(query, wait);
+ } else {
+ this._lookUp(query);
+ }
+ return query;
+ };
+
+ Controller.prototype._delayLookUp = function(query, wait) {
+ var now, remaining;
+ now = Date.now ? Date.now() : new Date().getTime();
+ this.previousCallTime || (this.previousCallTime = now);
+ remaining = wait - (now - this.previousCallTime);
+ if ((0 < remaining && remaining < wait)) {
+ this.previousCallTime = now;
+ this._stopDelayedCall();
+ return this.delayedCallTimeout = setTimeout((function(_this) {
+ return function() {
+ _this.previousCallTime = 0;
+ _this.delayedCallTimeout = null;
+ return _this._lookUp(query);
+ };
+ })(this), wait);
+ } else {
+ this._stopDelayedCall();
+ if (this.previousCallTime !== now) {
+ this.previousCallTime = 0;
+ }
+ return this._lookUp(query);
+ }
+ };
+
+ Controller.prototype._stopDelayedCall = function() {
+ if (this.delayedCallTimeout) {
+ clearTimeout(this.delayedCallTimeout);
+ return this.delayedCallTimeout = null;
+ }
+ };
+
+ Controller.prototype._generateQueryCBId = function() {
+ return {};
+ };
+
+ Controller.prototype._lookUp = function(query) {
+ var _callback;
+ _callback = function(queryCBId, data) {
+ if (queryCBId !== this.expectedQueryCBId) {
+ return;
+ }
+ if (data && data.length > 0) {
+ return this.renderView(this.constructor.arrayToDefaultHash(data));
+ } else {
+ return this.view.hide();
+ }
+ };
+ this.expectedQueryCBId = this._generateQueryCBId();
+ return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
+ };
+
+ return Controller;
+
+})();
+
+var TextareaController,
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ hasProp = {}.hasOwnProperty;
+
+TextareaController = (function(superClass) {
+ extend(TextareaController, superClass);
+
+ function TextareaController() {
+ return TextareaController.__super__.constructor.apply(this, arguments);
+ }
+
+ TextareaController.prototype.catchQuery = function() {
+ var caretPos, content, end, isString, query, start, subtext;
+ content = this.$inputor.val();
+ caretPos = this.$inputor.caret('pos', {
+ iframe: this.app.iframe
+ });
+ subtext = content.slice(0, caretPos);
+ query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
+ isString = typeof query === 'string';
+ if (isString && query.length < this.getOpt('minLen', 0)) {
+ return;
+ }
+ if (isString && query.length <= this.getOpt('maxLen', 20)) {
+ start = caretPos - query.length;
+ end = start + query.length;
+ this.pos = start;
+ query = {
+ 'text': query,
+ 'headPos': start,
+ 'endPos': end
+ };
+ this.trigger("matched", [this.at, query.text]);
+ } else {
+ query = null;
+ this.view.hide();
+ }
+ return this.query = query;
+ };
+
+ TextareaController.prototype.rect = function() {
+ var c, iframeOffset, scaleBottom;
+ if (!(c = this.$inputor.caret('offset', this.pos - 1, {
+ iframe: this.app.iframe
+ }))) {
+ return;
+ }
+ if (this.app.iframe && !this.app.iframeAsRoot) {
+ iframeOffset = $(this.app.iframe).offset();
+ c.left += iframeOffset.left;
+ c.top += iframeOffset.top;
+ }
+ scaleBottom = this.app.document.selection ? 0 : 2;
+ return {
+ left: c.left,
+ top: c.top,
+ bottom: c.top + c.height + scaleBottom
+ };
+ };
+
+ TextareaController.prototype.insert = function(content, $li) {
+ var $inputor, source, startStr, suffix, text;
+ $inputor = this.$inputor;
+ source = $inputor.val();
+ startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
+ suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
+ content += suffix;
+ text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));
+ $inputor.val(text);
+ $inputor.caret('pos', startStr.length + content.length, {
+ iframe: this.app.iframe
+ });
+ if (!$inputor.is(':focus')) {
+ $inputor.focus();
+ }
+ return $inputor.change();
+ };
+
+ return TextareaController;
+
+})(Controller);
+
+var EditableController,
+ extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ hasProp = {}.hasOwnProperty;
+
+EditableController = (function(superClass) {
+ extend(EditableController, superClass);
+
+ function EditableController() {
+ return EditableController.__super__.constructor.apply(this, arguments);
+ }
+
+ EditableController.prototype._getRange = function() {
+ var sel;
+ sel = this.app.window.getSelection();
+ if (sel.rangeCount > 0) {
+ return sel.getRangeAt(0);
+ }
+ };
+
+ EditableController.prototype._setRange = function(position, node, range) {
+ if (range == null) {
+ range = this._getRange();
+ }
+ if (!(range && node)) {
+ return;
+ }
+ node = $(node)[0];
+ if (position === 'after') {
+ range.setEndAfter(node);
+ range.setStartAfter(node);
+ } else {
+ range.setEndBefore(node);
+ range.setStartBefore(node);
+ }
+ range.collapse(false);
+ return this._clearRange(range);
+ };
+
+ EditableController.prototype._clearRange = function(range) {
+ var sel;
+ if (range == null) {
+ range = this._getRange();
+ }
+ sel = this.app.window.getSelection();
+ if (this.ctrl_a_pressed == null) {
+ sel.removeAllRanges();
+ return sel.addRange(range);
+ }
+ };
+
+ EditableController.prototype._movingEvent = function(e) {
+ var ref;
+ return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
+ };
+
+ EditableController.prototype._unwrap = function(node) {
+ var next;
+ node = $(node).unwrap().get(0);
+ if ((next = node.nextSibling) && next.nodeValue) {
+ node.nodeValue += next.nodeValue;
+ $(next).remove();
+ }
+ return node;
+ };
+
+ EditableController.prototype.catchQuery = function(e) {
+ var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
+ if (!(range = this._getRange())) {
+ return;
+ }
+ if (!range.collapsed) {
+ return;
+ }
+ if (e.which === KEY_CODE.ENTER) {
+ ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();
+ if ($query.is(':empty')) {
+ $query.remove();
+ }
+ ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();
+ this._clearRange();
+ return;
+ }
+ if (/firefox/i.test(navigator.userAgent)) {
+ if ($(range.startContainer).is(this.$inputor)) {
+ this._clearRange();
+ return;
+ }
+ if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {
+ _range = range.cloneRange();
+ _range.setStart(range.startContainer, offset);
+ if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
+ inserted = $(range.startContainer).contents().get(offset);
+ this._setRange('after', $(inserted).contents().last());
+ }
+ } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {
+ $inserted = $(range.startContainer.previousSibling);
+ if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
+ this._setRange('after', $inserted.contents().last());
+ }
+ }
+ }
+ $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
+ if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
+ $query.remove();
+ }
+ if (!this._movingEvent(e)) {
+ $query.removeClass('atwho-inserted');
+ }
+ if ($query.length > 0) {
+ switch (e.which) {
+ case KEY_CODE.LEFT:
+ this._setRange('before', $query.get(0), range);
+ $query.removeClass('atwho-query');
+ return;
+ case KEY_CODE.RIGHT:
+ this._setRange('after', $query.get(0).nextSibling, range);
+ $query.removeClass('atwho-query');
+ return;
+ }
+ }
+ if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
+ $query.empty().html(query_content).attr('data-atwho-at-query', null);
+ this._setRange('after', $query.get(0), range);
+ }
+ _range = range.cloneRange();
+ _range.setStart(range.startContainer, 0);
+ matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
+ isString = typeof matched === 'string';
+ if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
+ range.setStart(range.startContainer, index);
+ $query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
+ range.surroundContents($query.get(0));
+ lastNode = $query.contents().last().get(0);
+ if (lastNode) {
+ if (/firefox/i.test(navigator.userAgent)) {
+ range.setStart(lastNode, lastNode.length);
+ range.setEnd(lastNode, lastNode.length);
+ this._clearRange(range);
+ } else {
+ this._setRange('after', lastNode, range);
+ }
+ }
+ }
+ if (isString && matched.length < this.getOpt('minLen', 0)) {
+ return;
+ }
+ if (isString && matched.length <= this.getOpt('maxLen', 20)) {
+ query = {
+ text: matched,
+ el: $query
+ };
+ this.trigger("matched", [this.at, query.text]);
+ return this.query = query;
+ } else {
+ this.view.hide();
+ this.query = {
+ el: $query
+ };
+ if ($query.text().indexOf(this.at) >= 0) {
+ if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
+ $query.removeClass('atwho-query');
+ } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {
+ this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));
+ }
+ }
+ return null;
+ }
+ };
+
+ EditableController.prototype.rect = function() {
+ var $iframe, iframeOffset, rect;
+ rect = this.query.el.offset();
+ if (!(rect && this.query.el[0].getClientRects().length)) {
+ return;
+ }
+ if (this.app.iframe && !this.app.iframeAsRoot) {
+ iframeOffset = ($iframe = $(this.app.iframe)).offset();
+ rect.left += iframeOffset.left - this.$inputor.scrollLeft();
+ rect.top += iframeOffset.top - this.$inputor.scrollTop();
+ }
+ rect.bottom = rect.top + this.query.el.height();
+ return rect;
+ };
+
+ EditableController.prototype.insert = function(content, $li) {
+ var data, overrides, range, suffix, suffixNode;
+ if (!this.$inputor.is(':focus')) {
+ this.$inputor.focus();
+ }
+ overrides = this.getOpt('functionOverrides');
+ if (overrides.insert) {
+ return overrides.insert.call(this, content, $li);
+ }
+ suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
+ data = $li.data('item-data');
+ this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false");
+ if (range = this._getRange()) {
+ if (this.query.el.length) {
+ range.setEndAfter(this.query.el[0]);
+ }
+ range.collapse(false);
+ range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix));
+ this._setRange('after', suffixNode, range);
+ }
+ if (!this.$inputor.is(':focus')) {
+ this.$inputor.focus();
+ }
+ return this.$inputor.change();
+ };
+
+ return EditableController;
+
+})(Controller);
+
+var Model;
+
+Model = (function() {
+ function Model(context) {
+ this.context = context;
+ this.at = this.context.at;
+ this.storage = this.context.$inputor;
+ }
+
+ Model.prototype.destroy = function() {
+ return this.storage.data(this.at, null);
+ };
+
+ Model.prototype.saved = function() {
+ return this.fetch() > 0;
+ };
+
+ Model.prototype.query = function(query, callback) {
+ var _remoteFilter, data, searchKey;
+ data = this.fetch();
+ searchKey = this.context.getOpt("searchKey");
+ data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];
+ _remoteFilter = this.context.callbacks('remoteFilter');
+ if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
+ return callback(data);
+ } else {
+ return _remoteFilter.call(this.context, query, callback);
+ }
+ };
+
+ Model.prototype.fetch = function() {
+ return this.storage.data(this.at) || [];
+ };
+
+ Model.prototype.save = function(data) {
+ return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
+ };
+
+ Model.prototype.load = function(data) {
+ if (!(this.saved() || !data)) {
+ return this._load(data);
+ }
+ };
+
+ Model.prototype.reload = function(data) {
+ return this._load(data);
+ };
+
+ Model.prototype._load = function(data) {
+ if (typeof data === "string") {
+ return $.ajax(data, {
+ dataType: "json"
+ }).done((function(_this) {
+ return function(data) {
+ return _this.save(data);
+ };
+ })(this));
+ } else {
+ return this.save(data);
+ }
+ };
+
+ return Model;
+
+})();
+
+var View;
+
+View = (function() {
+ function View(context) {
+ this.context = context;
+ this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
+ this.$elUl = this.$el.children();
+ this.timeoutID = null;
+ this.context.$el.append(this.$el);
+ this.bindEvent();
+ }
+
+ View.prototype.init = function() {
+ var header_tpl, id;
+ id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
+ header_tpl = this.context.getOpt("headerTpl");
+ if (header_tpl && this.$el.children().length === 1) {
+ this.$el.prepend(header_tpl);
+ }
+ return this.$el.attr({
+ 'id': "at-view-" + id
+ });
+ };
+
+ View.prototype.destroy = function() {
+ return this.$el.remove();
+ };
+
+ View.prototype.bindEvent = function() {
+ var $menu, lastCoordX, lastCoordY;
+ $menu = this.$el.find('ul');
+ lastCoordX = 0;
+ lastCoordY = 0;
+ return $menu.on('mousemove.atwho-view', 'li', (function(_this) {
+ return function(e) {
+ var $cur;
+ if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
+ return;
+ }
+ lastCoordX = e.clientX;
+ lastCoordY = e.clientY;
+ $cur = $(e.currentTarget);
+ if ($cur.hasClass('cur')) {
+ return;
+ }
+ $menu.find('.cur').removeClass('cur');
+ return $cur.addClass('cur');
+ };
+ })(this)).on('click.atwho-view', 'li', (function(_this) {
+ return function(e) {
+ $menu.find('.cur').removeClass('cur');
+ $(e.currentTarget).addClass('cur');
+ _this.choose(e);
+ return e.preventDefault();
+ };
+ })(this));
+ };
+
+ View.prototype.visible = function() {
+ return $.expr.pseudos.visible(this.$el[0]);
+ };
+
+ View.prototype.highlighted = function() {
+ return this.$el.find(".cur").length > 0;
+ };
+
+ View.prototype.choose = function(e) {
+ var $li, content;
+ if (($li = this.$el.find(".cur")).length) {
+ content = this.context.insertContentFor($li);
+ this.context._stopDelayedCall();
+ this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);
+ this.context.trigger("inserted", [$li, e]);
+ this.hide(e);
+ }
+ if (this.context.getOpt("hideWithoutSuffix")) {
+ return this.stopShowing = true;
+ }
+ };
+
+ View.prototype.reposition = function(rect) {
+ var _window, offset, overflowOffset, ref;
+ _window = this.context.app.iframeAsRoot ? this.context.app.window : window;
+ if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
+ rect.bottom = rect.top - this.$el.height();
+ }
+ if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
+ rect.left = overflowOffset;
+ }
+ offset = {
+ left: rect.left,
+ top: rect.bottom
+ };
+ if ((ref = this.context.callbacks("beforeReposition")) != null) {
+ ref.call(this.context, offset);
+ }
+ this.$el.offset(offset);
+ return this.context.trigger("reposition", [offset]);
+ };
+
+ View.prototype.next = function() {
+ var cur, next, nextEl, offset;
+ cur = this.$el.find('.cur').removeClass('cur');
+ next = cur.next();
+ if (!next.length) {
+ next = this.$el.find('li:first');
+ }
+ next.addClass('cur');
+ nextEl = next[0];
+ offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
+ return this.scrollTop(Math.max(0, offset - this.$el.height()));
+ };
+
+ View.prototype.prev = function() {
+ var cur, offset, prev, prevEl;
+ cur = this.$el.find('.cur').removeClass('cur');
+ prev = cur.prev();
+ if (!prev.length) {
+ prev = this.$el.find('li:last');
+ }
+ prev.addClass('cur');
+ prevEl = prev[0];
+ offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
+ return this.scrollTop(Math.max(0, offset - this.$el.height()));
+ };
+
+ View.prototype.scrollTop = function(scrollTop) {
+ var scrollDuration;
+ scrollDuration = this.context.getOpt('scrollDuration');
+ if (scrollDuration) {
+ return this.$elUl.animate({
+ scrollTop: scrollTop
+ }, scrollDuration);
+ } else {
+ return this.$elUl.scrollTop(scrollTop);
+ }
+ };
+
+ View.prototype.show = function() {
+ var rect;
+ if (this.stopShowing) {
+ this.stopShowing = false;
+ return;
+ }
+ if (!this.visible()) {
+ this.$el.show();
+ this.$el.scrollTop(0);
+ this.context.trigger('shown');
+ }
+ if (rect = this.context.rect()) {
+ return this.reposition(rect);
+ }
+ };
+
+ View.prototype.hide = function(e, time) {
+ var callback;
+ if (!this.visible()) {
+ return;
+ }
+ if (isNaN(time)) {
+ this.$el.hide();
+ return this.context.trigger('hidden', [e]);
+ } else {
+ callback = (function(_this) {
+ return function() {
+ return _this.hide();
+ };
+ })(this);
+ clearTimeout(this.timeoutID);
+ return this.timeoutID = setTimeout(callback, time);
+ }
+ };
+
+ View.prototype.render = function(list) {
+ var $li, $ul, i, item, len, li, tpl;
+ if (!($.isArray(list) && list.length > 0)) {
+ this.hide();
+ return;
+ }
+ this.$el.find('ul').empty();
+ $ul = this.$el.find('ul');
+ tpl = this.context.getOpt('displayTpl');
+ for (i = 0, len = list.length; i < len; i++) {
+ item = list[i];
+ item = $.extend({}, item, {
+ 'atwho-at': this.context.at
+ });
+ li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");
+ $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
+ $li.data("item-data", item);
+ $ul.append($li);
+ }
+ this.show();
+ if (this.context.getOpt('highlightFirst')) {
+ return $ul.find("li:first").addClass("cur");
+ }
+ };
+
+ return View;
+
+})();
+
+var Api;
+
+Api = {
+ load: function(at, data) {
+ var c;
+ if (c = this.controller(at)) {
+ return c.model.load(data);
+ }
+ },
+ isSelecting: function() {
+ var ref;
+ return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);
+ },
+ hide: function() {
+ var ref;
+ return (ref = this.controller()) != null ? ref.view.hide() : void 0;
+ },
+ reposition: function() {
+ var c;
+ if (c = this.controller()) {
+ return c.view.reposition(c.rect());
+ }
+ },
+ setIframe: function(iframe, asRoot) {
+ this.setupRootElement(iframe, asRoot);
+ return null;
+ },
+ run: function() {
+ return this.dispatch();
+ },
+ destroy: function() {
+ this.shutdown();
+ return this.$inputor.data('atwho', null);
+ }
+};
+
+$.fn.atwho = function(method) {
+ var _args, result;
+ _args = arguments;
+ result = null;
+ this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() {
+ var $this, app;
+ if (!(app = ($this = $(this)).data("atwho"))) {
+ $this.data('atwho', (app = new App(this)));
+ }
+ if (typeof method === 'object' || !method) {
+ return app.reg(method.at, method);
+ } else if (Api[method] && app) {
+ return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
+ } else {
+ return $.error("Method " + method + " does not exist on jQuery.atwho");
+ }
+ });
+ if (result != null) {
+ return result;
+ } else {
+ return this;
+ }
+};
+
+$.fn.atwho["default"] = {
+ at: void 0,
+ alias: void 0,
+ data: null,
+ displayTpl: "<li>${name}</li>",
+ insertTpl: "${atwho-at}${name}",
+ headerTpl: null,
+ callbacks: DEFAULT_CALLBACKS,
+ functionOverrides: {},
+ searchKey: "name",
+ suffix: void 0,
+ hideWithoutSuffix: false,
+ startWithSpace: true,
+ acceptSpaceBar: false,
+ highlightFirst: true,
+ limit: 5,
+ maxLen: 20,
+ minLen: 0,
+ displayTimeout: 300,
+ delay: null,
+ spaceSelectsMatch: false,
+ tabSelectsMatch: true,
+ editableAtwhoQueryAttrs: {},
+ scrollDuration: 150,
+ suspendOnComposing: true,
+ lookUpOnClick: true
+};
+
+$.fn.atwho.debug = false;
+
+}));
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.autocomplete.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.autocomplete.js
new file mode 100644
index 00000000..467a8280
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.autocomplete.js
@@ -0,0 +1,998 @@
+/**
+* Ajax Autocomplete for jQuery, version 1.4.7
+* (c) 2017 Tomas Kirda
+*
+* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
+* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
+*/
+
+/*jslint browser: true, white: true, single: true, this: true, multivar: true */
+/*global define, window, document, jQuery, exports, require */
+
+// Expose plugin as an AMD module if AMD loader is present:
+(function (factory) {
+ "use strict";
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof exports === 'object' && typeof require === 'function') {
+ // Browserify
+ factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ var
+ utils = (function () {
+ return {
+ escapeRegExChars: function (value) {
+ return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
+ },
+ createNode: function (containerClass) {
+ var div = document.createElement('div');
+ div.className = containerClass;
+ div.style.position = 'absolute';
+ div.style.display = 'none';
+ return div;
+ }
+ };
+ }()),
+
+ keys = {
+ ESC: 27,
+ TAB: 9,
+ RETURN: 13,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40
+ },
+
+ noop = $.noop;
+
+ function Autocomplete(el, options) {
+ var that = this;
+
+ // Shared variables:
+ that.element = el;
+ that.el = $(el);
+ that.suggestions = [];
+ that.badQueries = [];
+ that.selectedIndex = -1;
+ that.currentValue = that.element.value;
+ that.timeoutId = null;
+ that.cachedResponse = {};
+ that.onChangeTimeout = null;
+ that.onChange = null;
+ that.isLocal = false;
+ that.suggestionsContainer = null;
+ that.noSuggestionsContainer = null;
+ that.options = $.extend({}, Autocomplete.defaults, options);
+ that.classes = {
+ selected: 'autocomplete-selected',
+ suggestion: 'autocomplete-suggestion'
+ };
+ that.hint = null;
+ that.hintValue = '';
+ that.selection = null;
+
+ // Initialize and set options:
+ that.initialize();
+ that.setOptions(options);
+ }
+
+ Autocomplete.utils = utils;
+
+ $.Autocomplete = Autocomplete;
+
+ Autocomplete.defaults = {
+ ajaxSettings: {},
+ autoSelectFirst: false,
+ appendTo: 'body',
+ serviceUrl: null,
+ lookup: null,
+ onSelect: null,
+ width: 'auto',
+ minChars: 1,
+ maxHeight: 300,
+ deferRequestBy: 0,
+ params: {},
+ formatResult: _formatResult,
+ formatGroup: _formatGroup,
+ delimiter: null,
+ zIndex: 9999,
+ type: 'GET',
+ noCache: false,
+ onSearchStart: noop,
+ onSearchComplete: noop,
+ onSearchError: noop,
+ preserveInput: false,
+ containerClass: 'autocomplete-suggestions',
+ tabDisabled: false,
+ dataType: 'text',
+ currentRequest: null,
+ triggerSelectOnValidInput: true,
+ preventBadQueries: true,
+ lookupFilter: _lookupFilter,
+ paramName: 'query',
+ transformResult: _transformResult,
+ showNoSuggestionNotice: false,
+ noSuggestionNotice: 'No results',
+ orientation: 'bottom',
+ forceFixPosition: false
+ };
+
+ function _lookupFilter(suggestion, originalQuery, queryLowerCase) {
+ return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
+ };
+
+ function _transformResult(response) {
+ return typeof response === 'string' ? $.parseJSON(response) : response;
+ };
+
+ function _formatResult(suggestion, currentValue) {
+ // Do not replace anything if the current value is empty
+ if (!currentValue) {
+ return suggestion.value;
+ }
+
+ var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
+
+ return suggestion.value
+ .replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>')
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/&lt;(\/?strong)&gt;/g, '<$1>');
+ };
+
+ function _formatGroup(suggestion, category) {
+ return '<div class="autocomplete-group">' + category + '</div>';
+ };
+
+ Autocomplete.prototype = {
+
+ initialize: function () {
+ var that = this,
+ suggestionSelector = '.' + that.classes.suggestion,
+ selected = that.classes.selected,
+ options = that.options,
+ container;
+
+ // Remove autocomplete attribute to prevent native suggestions:
+ that.element.setAttribute('autocomplete', 'off');
+
+ // html() deals with many types: htmlString or Element or Array or jQuery
+ that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
+ .html(this.options.noSuggestionNotice).get(0);
+
+ that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
+
+ container = $(that.suggestionsContainer);
+
+ container.appendTo(options.appendTo || 'body');
+
+ // Only set width if it was provided:
+ if (options.width !== 'auto') {
+ container.css('width', options.width);
+ }
+
+ // Listen for mouse over event on suggestions list:
+ container.on('mouseover.autocomplete', suggestionSelector, function () {
+ that.activate($(this).data('index'));
+ });
+
+ // Deselect active element when mouse leaves suggestions container:
+ container.on('mouseout.autocomplete', function () {
+ that.selectedIndex = -1;
+ container.children('.' + selected).removeClass(selected);
+ });
+
+ // Listen for click event on suggestions list:
+ container.on('click.autocomplete', suggestionSelector, function () {
+ that.select($(this).data('index'));
+ });
+
+ container.on('click.autocomplete', function () {
+ clearTimeout(that.blurTimeoutId);
+ })
+
+ that.fixPositionCapture = function () {
+ if (that.visible) {
+ that.fixPosition();
+ }
+ };
+
+ $(window).on('resize.autocomplete', that.fixPositionCapture);
+
+ that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
+ that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
+ that.el.on('blur.autocomplete', function () { that.onBlur(); });
+ that.el.on('focus.autocomplete', function () { that.onFocus(); });
+ that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
+ that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); });
+ },
+
+ onFocus: function () {
+ var that = this;
+
+ that.fixPosition();
+
+ if (that.el.val().length >= that.options.minChars) {
+ that.onValueChange();
+ }
+ },
+
+ onBlur: function () {
+ var that = this;
+
+ // If user clicked on a suggestion, hide() will
+ // be canceled, otherwise close suggestions
+ that.blurTimeoutId = setTimeout(function () {
+ that.hide();
+ }, 200);
+ },
+
+ abortAjax: function () {
+ var that = this;
+ if (that.currentRequest) {
+ that.currentRequest.abort();
+ that.currentRequest = null;
+ }
+ },
+
+ setOptions: function (suppliedOptions) {
+ var that = this,
+ options = $.extend({}, that.options, suppliedOptions);
+
+ that.isLocal = Array.isArray(options.lookup);
+
+ if (that.isLocal) {
+ options.lookup = that.verifySuggestionsFormat(options.lookup);
+ }
+
+ options.orientation = that.validateOrientation(options.orientation, 'bottom');
+
+ // Adjust height, width and z-index:
+ $(that.suggestionsContainer).css({
+ 'max-height': options.maxHeight + 'px',
+ 'width': options.width + 'px',
+ 'z-index': options.zIndex
+ });
+
+ this.options = options;
+ },
+
+
+ clearCache: function () {
+ this.cachedResponse = {};
+ this.badQueries = [];
+ },
+
+ clear: function () {
+ this.clearCache();
+ this.currentValue = '';
+ this.suggestions = [];
+ },
+
+ disable: function () {
+ var that = this;
+ that.disabled = true;
+ clearTimeout(that.onChangeTimeout);
+ that.abortAjax();
+ },
+
+ enable: function () {
+ this.disabled = false;
+ },
+
+ fixPosition: function () {
+ // Use only when container has already its content
+
+ var that = this,
+ $container = $(that.suggestionsContainer),
+ containerParent = $container.parent().get(0);
+ // Fix position automatically when appended to body.
+ // In other cases force parameter must be given.
+ if (containerParent !== document.body && !that.options.forceFixPosition) {
+ return;
+ }
+
+ // Choose orientation
+ var orientation = that.options.orientation,
+ containerHeight = $container.outerHeight(),
+ height = that.el.outerHeight(),
+ offset = that.el.offset(),
+ styles = { 'top': offset.top, 'left': offset.left };
+
+ if (orientation === 'auto') {
+ var viewPortHeight = $(window).height(),
+ scrollTop = $(window).scrollTop(),
+ topOverflow = -scrollTop + offset.top - containerHeight,
+ bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
+
+ orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom';
+ }
+
+ if (orientation === 'top') {
+ styles.top += -containerHeight;
+ } else {
+ styles.top += height;
+ }
+
+ // If container is not positioned to body,
+ // correct its position using offset parent offset
+ if(containerParent !== document.body) {
+ var opacity = $container.css('opacity'),
+ parentOffsetDiff;
+
+ if (!that.visible){
+ $container.css('opacity', 0).show();
+ }
+
+ parentOffsetDiff = $container.offsetParent().offset();
+ styles.top -= parentOffsetDiff.top;
+ styles.top += containerParent.scrollTop;
+ styles.left -= parentOffsetDiff.left;
+
+ if (!that.visible){
+ $container.css('opacity', opacity).hide();
+ }
+ }
+
+ if (that.options.width === 'auto') {
+ styles.width = that.el.outerWidth() - 1 + 'px';
+ }
+
+ $container.css(styles);
+ },
+
+ isCursorAtEnd: function () {
+ var that = this,
+ valLength = that.el.val().length,
+ selectionStart = that.element.selectionStart,
+ range;
+
+ if (typeof selectionStart === 'number') {
+ return selectionStart === valLength;
+ }
+ if (document.selection) {
+ range = document.selection.createRange();
+ range.moveStart('character', -valLength);
+ return valLength === range.text.length;
+ }
+ return true;
+ },
+
+ onKeyPress: function (e) {
+ var that = this;
+
+ // If suggestions are hidden and user presses arrow down, display suggestions:
+ if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
+ that.suggest();
+ return;
+ }
+
+ if (that.disabled || !that.visible) {
+ return;
+ }
+
+ switch (e.which) {
+ case keys.ESC:
+ that.el.val(that.currentValue);
+ that.hide();
+ break;
+ case keys.RIGHT:
+ if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
+ that.selectHint();
+ break;
+ }
+ return;
+ case keys.TAB:
+ if (that.hint && that.options.onHint) {
+ that.selectHint();
+ return;
+ }
+ if (that.selectedIndex === -1) {
+ that.hide();
+ return;
+ }
+ that.select(that.selectedIndex);
+ if (that.options.tabDisabled === false) {
+ return;
+ }
+ break;
+ case keys.RETURN:
+ if (that.selectedIndex === -1) {
+ that.hide();
+ return;
+ }
+ that.select(that.selectedIndex);
+ break;
+ case keys.UP:
+ that.moveUp();
+ break;
+ case keys.DOWN:
+ that.moveDown();
+ break;
+ default:
+ return;
+ }
+
+ // Cancel event if function did not return:
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ },
+
+ onKeyUp: function (e) {
+ var that = this;
+
+ if (that.disabled) {
+ return;
+ }
+
+ switch (e.which) {
+ case keys.UP:
+ case keys.DOWN:
+ return;
+ }
+
+ clearTimeout(that.onChangeTimeout);
+
+ if (that.currentValue !== that.el.val()) {
+ that.findBestHint();
+ if (that.options.deferRequestBy > 0) {
+ // Defer lookup in case when value changes very quickly:
+ that.onChangeTimeout = setTimeout(function () {
+ that.onValueChange();
+ }, that.options.deferRequestBy);
+ } else {
+ that.onValueChange();
+ }
+ }
+ },
+
+ onValueChange: function () {
+ if (this.ignoreValueChange) {
+ this.ignoreValueChange = false;
+ return;
+ }
+
+ var that = this,
+ options = that.options,
+ value = that.el.val(),
+ query = that.getQuery(value);
+
+ if (that.selection && that.currentValue !== query) {
+ that.selection = null;
+ (options.onInvalidateSelection || $.noop).call(that.element);
+ }
+
+ clearTimeout(that.onChangeTimeout);
+ that.currentValue = value;
+ that.selectedIndex = -1;
+
+ // Check existing suggestion for the match before proceeding:
+ if (options.triggerSelectOnValidInput && that.isExactMatch(query)) {
+ that.select(0);
+ return;
+ }
+
+ if (query.length < options.minChars) {
+ that.hide();
+ } else {
+ that.getSuggestions(query);
+ }
+ },
+
+ isExactMatch: function (query) {
+ var suggestions = this.suggestions;
+
+ return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase());
+ },
+
+ getQuery: function (value) {
+ var delimiter = this.options.delimiter,
+ parts;
+
+ if (!delimiter) {
+ return value;
+ }
+ parts = value.split(delimiter);
+ return $.trim(parts[parts.length - 1]);
+ },
+
+ getSuggestionsLocal: function (query) {
+ var that = this,
+ options = that.options,
+ queryLowerCase = query.toLowerCase(),
+ filter = options.lookupFilter,
+ limit = parseInt(options.lookupLimit, 10),
+ data;
+
+ data = {
+ suggestions: $.grep(options.lookup, function (suggestion) {
+ return filter(suggestion, query, queryLowerCase);
+ })
+ };
+
+ if (limit && data.suggestions.length > limit) {
+ data.suggestions = data.suggestions.slice(0, limit);
+ }
+
+ return data;
+ },
+
+ getSuggestions: function (q) {
+ var response,
+ that = this,
+ options = that.options,
+ serviceUrl = options.serviceUrl,
+ params,
+ cacheKey,
+ ajaxSettings;
+
+ options.params[options.paramName] = q;
+
+ if (options.onSearchStart.call(that.element, options.params) === false) {
+ return;
+ }
+
+ params = options.ignoreParams ? null : options.params;
+
+ if ($.isFunction(options.lookup)){
+ options.lookup(q, function (data) {
+ that.suggestions = data.suggestions;
+ that.suggest();
+ options.onSearchComplete.call(that.element, q, data.suggestions);
+ });
+ return;
+ }
+
+ if (that.isLocal) {
+ response = that.getSuggestionsLocal(q);
+ } else {
+ if ($.isFunction(serviceUrl)) {
+ serviceUrl = serviceUrl.call(that.element, q);
+ }
+ cacheKey = serviceUrl + '?' + $.param(params || {});
+ response = that.cachedResponse[cacheKey];
+ }
+
+ if (response && Array.isArray(response.suggestions)) {
+ that.suggestions = response.suggestions;
+ that.suggest();
+ options.onSearchComplete.call(that.element, q, response.suggestions);
+ } else if (!that.isBadQuery(q)) {
+ that.abortAjax();
+
+ ajaxSettings = {
+ url: serviceUrl,
+ data: params,
+ type: options.type,
+ dataType: options.dataType
+ };
+
+ $.extend(ajaxSettings, options.ajaxSettings);
+
+ that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
+ var result;
+ that.currentRequest = null;
+ result = options.transformResult(data, q);
+ that.processResponse(result, q, cacheKey);
+ options.onSearchComplete.call(that.element, q, result.suggestions);
+ }).fail(function (jqXHR, textStatus, errorThrown) {
+ options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
+ });
+ } else {
+ options.onSearchComplete.call(that.element, q, []);
+ }
+ },
+
+ isBadQuery: function (q) {
+ if (!this.options.preventBadQueries){
+ return false;
+ }
+
+ var badQueries = this.badQueries,
+ i = badQueries.length;
+
+ while (i--) {
+ if (q.indexOf(badQueries[i]) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ hide: function () {
+ var that = this,
+ container = $(that.suggestionsContainer);
+
+ if ($.isFunction(that.options.onHide) && that.visible) {
+ that.options.onHide.call(that.element, container);
+ }
+
+ that.visible = false;
+ that.selectedIndex = -1;
+ clearTimeout(that.onChangeTimeout);
+ $(that.suggestionsContainer).hide();
+ that.signalHint(null);
+ },
+
+ suggest: function () {
+ if (!this.suggestions.length) {
+ if (this.options.showNoSuggestionNotice) {
+ this.noSuggestions();
+ } else {
+ this.hide();
+ }
+ return;
+ }
+
+ var that = this,
+ options = that.options,
+ groupBy = options.groupBy,
+ formatResult = options.formatResult,
+ value = that.getQuery(that.currentValue),
+ className = that.classes.suggestion,
+ classSelected = that.classes.selected,
+ container = $(that.suggestionsContainer),
+ noSuggestionsContainer = $(that.noSuggestionsContainer),
+ beforeRender = options.beforeRender,
+ html = '',
+ category,
+ formatGroup = function (suggestion, index) {
+ var currentCategory = suggestion.data[groupBy];
+
+ if (category === currentCategory){
+ return '';
+ }
+
+ category = currentCategory;
+
+ return options.formatGroup(suggestion, category);
+ };
+
+ if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
+ that.select(0);
+ return;
+ }
+
+ // Build suggestions inner HTML:
+ $.each(that.suggestions, function (i, suggestion) {
+ if (groupBy){
+ html += formatGroup(suggestion, value, i);
+ }
+
+ html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, i) + '</div>';
+ });
+
+ this.adjustContainerWidth();
+
+ noSuggestionsContainer.detach();
+ container.html(html);
+
+ if ($.isFunction(beforeRender)) {
+ beforeRender.call(that.element, container, that.suggestions);
+ }
+
+ that.fixPosition();
+ container.show();
+
+ // Select first value by default:
+ if (options.autoSelectFirst) {
+ that.selectedIndex = 0;
+ container.scrollTop(0);
+ container.children('.' + className).first().addClass(classSelected);
+ }
+
+ that.visible = true;
+ that.findBestHint();
+ },
+
+ noSuggestions: function() {
+ var that = this,
+ beforeRender = that.options.beforeRender,
+ container = $(that.suggestionsContainer),
+ noSuggestionsContainer = $(that.noSuggestionsContainer);
+
+ this.adjustContainerWidth();
+
+ // Some explicit steps. Be careful here as it easy to get
+ // noSuggestionsContainer removed from DOM if not detached properly.
+ noSuggestionsContainer.detach();
+
+ // clean suggestions if any
+ container.empty();
+ container.append(noSuggestionsContainer);
+
+ if ($.isFunction(beforeRender)) {
+ beforeRender.call(that.element, container, that.suggestions);
+ }
+
+ that.fixPosition();
+
+ container.show();
+ that.visible = true;
+ },
+
+ adjustContainerWidth: function() {
+ var that = this,
+ options = that.options,
+ width,
+ container = $(that.suggestionsContainer);
+
+ // If width is auto, adjust width before displaying suggestions,
+ // because if instance was created before input had width, it will be zero.
+ // Also it adjusts if input width has changed.
+ if (options.width === 'auto') {
+ width = that.el.outerWidth();
+ container.css('width', width > 0 ? width : 300);
+ } else if(options.width === 'flex') {
+ // Trust the source! Unset the width property so it will be the max length
+ // the containing elements.
+ container.css('width', '');
+ }
+ },
+
+ findBestHint: function () {
+ var that = this,
+ value = that.el.val().toLowerCase(),
+ bestMatch = null;
+
+ if (!value) {
+ return;
+ }
+
+ $.each(that.suggestions, function (i, suggestion) {
+ var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
+ if (foundMatch) {
+ bestMatch = suggestion;
+ }
+ return !foundMatch;
+ });
+
+ that.signalHint(bestMatch);
+ },
+
+ signalHint: function (suggestion) {
+ var hintValue = '',
+ that = this;
+ if (suggestion) {
+ hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
+ }
+ if (that.hintValue !== hintValue) {
+ that.hintValue = hintValue;
+ that.hint = suggestion;
+ (this.options.onHint || $.noop)(hintValue);
+ }
+ },
+
+ verifySuggestionsFormat: function (suggestions) {
+ // If suggestions is string array, convert them to supported format:
+ if (suggestions.length && typeof suggestions[0] === 'string') {
+ return $.map(suggestions, function (value) {
+ return { value: value, data: null };
+ });
+ }
+
+ return suggestions;
+ },
+
+ validateOrientation: function(orientation, fallback) {
+ orientation = $.trim(orientation || '').toLowerCase();
+
+ if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
+ orientation = fallback;
+ }
+
+ return orientation;
+ },
+
+ processResponse: function (result, originalQuery, cacheKey) {
+ var that = this,
+ options = that.options;
+
+ result.suggestions = that.verifySuggestionsFormat(result.suggestions);
+
+ // Cache results if cache is not disabled:
+ if (!options.noCache) {
+ that.cachedResponse[cacheKey] = result;
+ if (options.preventBadQueries && !result.suggestions.length) {
+ that.badQueries.push(originalQuery);
+ }
+ }
+
+ // Return if originalQuery is not matching current query:
+ if (originalQuery !== that.getQuery(that.currentValue)) {
+ return;
+ }
+
+ that.suggestions = result.suggestions;
+ that.suggest();
+ },
+
+ activate: function (index) {
+ var that = this,
+ activeItem,
+ selected = that.classes.selected,
+ container = $(that.suggestionsContainer),
+ children = container.find('.' + that.classes.suggestion);
+
+ container.find('.' + selected).removeClass(selected);
+
+ that.selectedIndex = index;
+
+ if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
+ activeItem = children.get(that.selectedIndex);
+ $(activeItem).addClass(selected);
+ return activeItem;
+ }
+
+ return null;
+ },
+
+ selectHint: function () {
+ var that = this,
+ i = $.inArray(that.hint, that.suggestions);
+
+ that.select(i);
+ },
+
+ select: function (i) {
+ var that = this;
+ that.hide();
+ that.onSelect(i);
+ },
+
+ moveUp: function () {
+ var that = this;
+
+ if (that.selectedIndex === -1) {
+ return;
+ }
+
+ if (that.selectedIndex === 0) {
+ $(that.suggestionsContainer).children('.' + that.classes.suggestion).first().removeClass(that.classes.selected);
+ that.selectedIndex = -1;
+ that.ignoreValueChange = false;
+ that.el.val(that.currentValue);
+ that.findBestHint();
+ return;
+ }
+
+ that.adjustScroll(that.selectedIndex - 1);
+ },
+
+ moveDown: function () {
+ var that = this;
+
+ if (that.selectedIndex === (that.suggestions.length - 1)) {
+ return;
+ }
+
+ that.adjustScroll(that.selectedIndex + 1);
+ },
+
+ adjustScroll: function (index) {
+ var that = this,
+ activeItem = that.activate(index);
+
+ if (!activeItem) {
+ return;
+ }
+
+ var offsetTop,
+ upperBound,
+ lowerBound,
+ heightDelta = $(activeItem).outerHeight();
+
+ offsetTop = activeItem.offsetTop;
+ upperBound = $(that.suggestionsContainer).scrollTop();
+ lowerBound = upperBound + that.options.maxHeight - heightDelta;
+
+ if (offsetTop < upperBound) {
+ $(that.suggestionsContainer).scrollTop(offsetTop);
+ } else if (offsetTop > lowerBound) {
+ $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
+ }
+
+ if (!that.options.preserveInput) {
+ // During onBlur event, browser will trigger "change" event,
+ // because value has changed, to avoid side effect ignore,
+ // that event, so that correct suggestion can be selected
+ // when clicking on suggestion with a mouse
+ that.ignoreValueChange = true;
+ that.el.val(that.getValue(that.suggestions[index].value));
+ }
+
+ that.signalHint(null);
+ },
+
+ onSelect: function (index) {
+ var that = this,
+ onSelectCallback = that.options.onSelect,
+ suggestion = that.suggestions[index];
+
+ that.currentValue = that.getValue(suggestion.value);
+
+ if (that.currentValue !== that.el.val() && !that.options.preserveInput) {
+ that.el.val(that.currentValue);
+ }
+
+ that.signalHint(null);
+ that.suggestions = [];
+ that.selection = suggestion;
+
+ if ($.isFunction(onSelectCallback)) {
+ onSelectCallback.call(that.element, suggestion);
+ }
+ },
+
+ getValue: function (value) {
+ var that = this,
+ delimiter = that.options.delimiter,
+ currentValue,
+ parts;
+
+ if (!delimiter) {
+ return value;
+ }
+
+ currentValue = that.currentValue;
+ parts = currentValue.split(delimiter);
+
+ if (parts.length === 1) {
+ return value;
+ }
+
+ return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
+ },
+
+ dispose: function () {
+ var that = this;
+ that.el.off('.autocomplete').removeData('autocomplete');
+ $(window).off('resize.autocomplete', that.fixPositionCapture);
+ $(that.suggestionsContainer).remove();
+ }
+ };
+
+ // Create chainable jQuery plugin:
+ $.fn.devbridgeAutocomplete = function (options, args) {
+ var dataKey = 'autocomplete';
+ // If function invoked without argument return
+ // instance of the first matched element:
+ if (!arguments.length) {
+ return this.first().data(dataKey);
+ }
+
+ return this.each(function () {
+ var inputElement = $(this),
+ instance = inputElement.data(dataKey);
+
+ if (typeof options === 'string') {
+ if (instance && typeof instance[options] === 'function') {
+ instance[options](args);
+ }
+ } else {
+ // If instance already exists, destroy it:
+ if (instance && instance.dispose) {
+ instance.dispose();
+ }
+ instance = new Autocomplete(this, options);
+ inputElement.data(dataKey, instance);
+ }
+ });
+ };
+
+ // Don't overwrite if it already exists
+ if (!$.fn.autocomplete) {
+ $.fn.autocomplete = $.fn.devbridgeAutocomplete;
+ }
+}));
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.caret.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.caret.js
new file mode 100644
index 00000000..5d08d724
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.caret.js
@@ -0,0 +1,436 @@
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(["jquery"], function ($) {
+ return (root.returnExportsGlobal = factory($));
+ });
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory(require("jquery"));
+ } else {
+ factory(jQuery);
+ }
+}(this, function ($) {
+
+/*
+ Implement Github like autocomplete mentions
+ http://ichord.github.com/At.js
+
+ Copyright (c) 2013 chord.luo@gmail.com
+ Licensed under the MIT license.
+*/
+
+/*
+本æ’件æ“作 textarea 或者 input 内的æ’入符
+åªå®žçŽ°äº†èŽ·å¾—æ’入符在文本框中的ä½ç½®ï¼Œæˆ‘设置
+æ’入符的ä½ç½®.
+*/
+
+"use strict";
+var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
+
+pluginName = 'caret';
+
+EditableCaret = (function() {
+ function EditableCaret($inputor) {
+ this.$inputor = $inputor;
+ this.domInputor = this.$inputor[0];
+ }
+
+ EditableCaret.prototype.setPos = function(pos) {
+ var fn, found, offset, sel;
+ if (sel = oWindow.getSelection()) {
+ offset = 0;
+ found = false;
+ (fn = function(pos, parent) {
+ var node, range, _i, _len, _ref, _results;
+ _ref = parent.childNodes;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ node = _ref[_i];
+ if (found) {
+ break;
+ }
+ if (node.nodeType === 3) {
+ if (offset + node.length >= pos) {
+ found = true;
+ range = oDocument.createRange();
+ range.setStart(node, pos - offset);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ break;
+ } else {
+ _results.push(offset += node.length);
+ }
+ } else {
+ _results.push(fn(pos, node));
+ }
+ }
+ return _results;
+ })(pos, this.domInputor);
+ }
+ return this.domInputor;
+ };
+
+ EditableCaret.prototype.getIEPosition = function() {
+ return this.getPosition();
+ };
+
+ EditableCaret.prototype.getPosition = function() {
+ var inputor_offset, offset;
+ offset = this.getOffset();
+ inputor_offset = this.$inputor.offset();
+ offset.left -= inputor_offset.left;
+ offset.top -= inputor_offset.top;
+ return offset;
+ };
+
+ EditableCaret.prototype.getOldIEPos = function() {
+ var preCaretTextRange, textRange;
+ textRange = oDocument.selection.createRange();
+ preCaretTextRange = oDocument.body.createTextRange();
+ preCaretTextRange.moveToElementText(this.domInputor);
+ preCaretTextRange.setEndPoint("EndToEnd", textRange);
+ return preCaretTextRange.text.length;
+ };
+
+ EditableCaret.prototype.getPos = function() {
+ var clonedRange, pos, range;
+ if (range = this.range()) {
+ clonedRange = range.cloneRange();
+ clonedRange.selectNodeContents(this.domInputor);
+ clonedRange.setEnd(range.endContainer, range.endOffset);
+ pos = clonedRange.toString().length;
+ clonedRange.detach();
+ return pos;
+ } else if (oDocument.selection) {
+ return this.getOldIEPos();
+ }
+ };
+
+ EditableCaret.prototype.getOldIEOffset = function() {
+ var range, rect;
+ range = oDocument.selection.createRange().duplicate();
+ range.moveStart("character", -1);
+ rect = range.getBoundingClientRect();
+ return {
+ height: rect.bottom - rect.top,
+ left: rect.left,
+ top: rect.top
+ };
+ };
+
+ EditableCaret.prototype.getOffset = function(pos) {
+ var clonedRange, offset, range, rect, shadowCaret;
+ if (oWindow.getSelection && (range = this.range())) {
+ if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
+ clonedRange = range.cloneRange();
+ clonedRange.setStart(range.endContainer, range.endOffset - 1);
+ clonedRange.setEnd(range.endContainer, range.endOffset);
+ rect = clonedRange.getBoundingClientRect();
+ offset = {
+ height: rect.height,
+ left: rect.left + rect.width,
+ top: rect.top
+ };
+ clonedRange.detach();
+ }
+ if (!offset || (offset != null ? offset.height : void 0) === 0) {
+ clonedRange = range.cloneRange();
+ shadowCaret = $(oDocument.createTextNode("|"));
+ clonedRange.insertNode(shadowCaret[0]);
+ clonedRange.selectNode(shadowCaret[0]);
+ rect = clonedRange.getBoundingClientRect();
+ offset = {
+ height: rect.height,
+ left: rect.left,
+ top: rect.top
+ };
+ shadowCaret.remove();
+ clonedRange.detach();
+ }
+ } else if (oDocument.selection) {
+ offset = this.getOldIEOffset();
+ }
+ if (offset) {
+ offset.top += $(oWindow).scrollTop();
+ offset.left += $(oWindow).scrollLeft();
+ }
+ return offset;
+ };
+
+ EditableCaret.prototype.range = function() {
+ var sel;
+ if (!oWindow.getSelection) {
+ return;
+ }
+ sel = oWindow.getSelection();
+ if (sel.rangeCount > 0) {
+ return sel.getRangeAt(0);
+ } else {
+ return null;
+ }
+ };
+
+ return EditableCaret;
+
+})();
+
+InputCaret = (function() {
+ function InputCaret($inputor) {
+ this.$inputor = $inputor;
+ this.domInputor = this.$inputor[0];
+ }
+
+ InputCaret.prototype.getIEPos = function() {
+ var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
+ inputor = this.domInputor;
+ range = oDocument.selection.createRange();
+ pos = 0;
+ if (range && range.parentElement() === inputor) {
+ normalizedValue = inputor.value.replace(/\r\n/g, "\n");
+ len = normalizedValue.length;
+ textInputRange = inputor.createTextRange();
+ textInputRange.moveToBookmark(range.getBookmark());
+ endRange = inputor.createTextRange();
+ endRange.collapse(false);
+ if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
+ pos = len;
+ } else {
+ pos = -textInputRange.moveStart("character", -len);
+ }
+ }
+ return pos;
+ };
+
+ InputCaret.prototype.getPos = function() {
+ if (oDocument.selection) {
+ return this.getIEPos();
+ } else {
+ return this.domInputor.selectionStart;
+ }
+ };
+
+ InputCaret.prototype.setPos = function(pos) {
+ var inputor, range;
+ inputor = this.domInputor;
+ if (oDocument.selection) {
+ range = inputor.createTextRange();
+ range.move("character", pos);
+ range.select();
+ } else if (inputor.setSelectionRange) {
+ inputor.setSelectionRange(pos, pos);
+ }
+ return inputor;
+ };
+
+ InputCaret.prototype.getIEOffset = function(pos) {
+ var h, textRange, x, y;
+ textRange = this.domInputor.createTextRange();
+ pos || (pos = this.getPos());
+ textRange.move('character', pos);
+ x = textRange.boundingLeft;
+ y = textRange.boundingTop;
+ h = textRange.boundingHeight;
+ return {
+ left: x,
+ top: y,
+ height: h
+ };
+ };
+
+ InputCaret.prototype.getOffset = function(pos) {
+ var $inputor, offset, position;
+ $inputor = this.$inputor;
+ if (oDocument.selection) {
+ offset = this.getIEOffset(pos);
+ offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
+ offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
+ return offset;
+ } else {
+ offset = $inputor.offset();
+ position = this.getPosition(pos);
+ return offset = {
+ left: offset.left + position.left - $inputor.scrollLeft(),
+ top: offset.top + position.top - $inputor.scrollTop(),
+ height: position.height
+ };
+ }
+ };
+
+ InputCaret.prototype.getPosition = function(pos) {
+ var $inputor, at_rect, end_range, format, html, mirror, start_range;
+ $inputor = this.$inputor;
+ format = function(value) {
+ value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
+ if (/firefox/i.test(navigator.userAgent)) {
+ value = value.replace(/\s/g, '&nbsp;');
+ }
+ return value;
+ };
+ if (pos === void 0) {
+ pos = this.getPos();
+ }
+ start_range = $inputor.val().slice(0, pos);
+ end_range = $inputor.val().slice(pos);
+ html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
+ html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
+ html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
+ mirror = new Mirror($inputor);
+ return at_rect = mirror.create(html).rect();
+ };
+
+ InputCaret.prototype.getIEPosition = function(pos) {
+ var h, inputorOffset, offset, x, y;
+ offset = this.getIEOffset(pos);
+ inputorOffset = this.$inputor.offset();
+ x = offset.left - inputorOffset.left;
+ y = offset.top - inputorOffset.top;
+ h = offset.height;
+ return {
+ left: x,
+ top: y,
+ height: h
+ };
+ };
+
+ return InputCaret;
+
+})();
+
+Mirror = (function() {
+ Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];
+
+ function Mirror($inputor) {
+ this.$inputor = $inputor;
+ }
+
+ Mirror.prototype.mirrorCss = function() {
+ var css,
+ _this = this;
+ css = {
+ position: 'absolute',
+ left: -9999,
+ top: 0,
+ zIndex: -20000
+ };
+ if (this.$inputor.prop('tagName') === 'TEXTAREA') {
+ this.css_attr.push('width');
+ }
+ $.each(this.css_attr, function(i, p) {
+ return css[p] = _this.$inputor.css(p);
+ });
+ return css;
+ };
+
+ Mirror.prototype.create = function(html) {
+ this.$mirror = $('<div></div>');
+ this.$mirror.css(this.mirrorCss());
+ this.$mirror.html(html);
+ this.$inputor.after(this.$mirror);
+ return this;
+ };
+
+ Mirror.prototype.rect = function() {
+ var $flag, pos, rect;
+ $flag = this.$mirror.find("#caret");
+ pos = $flag.position();
+ rect = {
+ left: pos.left,
+ top: pos.top,
+ height: $flag.height()
+ };
+ this.$mirror.remove();
+ return rect;
+ };
+
+ return Mirror;
+
+})();
+
+Utils = {
+ contentEditable: function($inputor) {
+ return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
+ }
+};
+
+methods = {
+ pos: function(pos) {
+ if (pos || pos === 0) {
+ return this.setPos(pos);
+ } else {
+ return this.getPos();
+ }
+ },
+ position: function(pos) {
+ if (oDocument.selection) {
+ return this.getIEPosition(pos);
+ } else {
+ return this.getPosition(pos);
+ }
+ },
+ offset: function(pos) {
+ var offset;
+ offset = this.getOffset(pos);
+ return offset;
+ }
+};
+
+oDocument = null;
+
+oWindow = null;
+
+oFrame = null;
+
+setContextBy = function(settings) {
+ var iframe;
+ if (iframe = settings != null ? settings.iframe : void 0) {
+ oFrame = iframe;
+ oWindow = iframe.contentWindow;
+ return oDocument = iframe.contentDocument || oWindow.document;
+ } else {
+ oFrame = void 0;
+ oWindow = window;
+ return oDocument = document;
+ }
+};
+
+discoveryIframeOf = function($dom) {
+ var error;
+ oDocument = $dom[0].ownerDocument;
+ oWindow = oDocument.defaultView || oDocument.parentWindow;
+ try {
+ return oFrame = oWindow.frameElement;
+ } catch (_error) {
+ error = _error;
+ }
+};
+
+$.fn.caret = function(method, value, settings) {
+ var caret;
+ if (methods[method]) {
+ if ($.isPlainObject(value)) {
+ setContextBy(value);
+ value = void 0;
+ } else {
+ setContextBy(settings);
+ }
+ caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
+ return methods[method].apply(caret, [value]);
+ } else {
+ return $.error("Method " + method + " does not exist on jQuery.caret");
+ }
+};
+
+$.fn.caret.EditableCaret = EditableCaret;
+
+$.fn.caret.InputCaret = InputCaret;
+
+$.fn.caret.Utils = Utils;
+
+$.fn.caret.apis = methods;
+
+
+})); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.jstorage.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.jstorage.js
new file mode 100644
index 00000000..45e19ac6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.jstorage.js
@@ -0,0 +1,996 @@
+/*
+ * ----------------------------- JSTORAGE -------------------------------------
+ * Simple local storage wrapper to save data on the browser side, supporting
+ * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
+ *
+ * Author: Andris Reinman, andris.reinman@gmail.com
+ * Project homepage: www.jstorage.info
+ *
+ * Licensed under Unlicense:
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
+/* global ActiveXObject: false */
+/* jshint browser: true */
+
+(function() {
+ 'use strict';
+
+ var
+ /* jStorage version */
+ JSTORAGE_VERSION = '0.4.12',
+
+ /* detect a dollar object or create one if not found */
+ $ = window.jQuery || window.$ || (window.$ = {}),
+
+ /* check for a JSON handling support */
+ JSON = {
+ parse: window.JSON && (window.JSON.parse || window.JSON.decode) ||
+ String.prototype.evalJSON && function(str) {
+ return String(str).evalJSON();
+ } ||
+ $.parseJSON ||
+ $.evalJSON,
+ stringify: Object.toJSON ||
+ window.JSON && (window.JSON.stringify || window.JSON.encode) ||
+ $.toJSON
+ };
+
+ // Break if no JSON support was found
+ if (typeof JSON.parse !== 'function' || typeof JSON.stringify !== 'function') {
+ throw new Error('No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page');
+ }
+
+ var
+ /* This is the object, that holds the cached values */
+ _storage = {
+ __jstorage_meta: {
+ CRC32: {}
+ }
+ },
+
+ /* Actual browser storage (localStorage or globalStorage['domain']) */
+ _storage_service = {
+ jStorage: '{}'
+ },
+
+ /* DOM element for older IE versions, holds userData behavior */
+ _storage_elm = null,
+
+ /* How much space does the storage take */
+ _storage_size = 0,
+
+ /* which backend is currently used */
+ _backend = false,
+
+ /* onchange observers */
+ _observers = {},
+
+ /* timeout to wait after onchange event */
+ _observer_timeout = false,
+
+ /* last update time */
+ _observer_update = 0,
+
+ /* pubsub observers */
+ _pubsub_observers = {},
+
+ /* skip published items older than current timestamp */
+ _pubsub_last = +new Date(),
+
+ /* Next check for TTL */
+ _ttl_timeout,
+
+ /**
+ * XML encoding and decoding as XML nodes can't be JSON'ized
+ * XML nodes are encoded and decoded if the node is the value to be saved
+ * but not if it's as a property of another object
+ * Eg. -
+ * $.jStorage.set('key', xmlNode); // IS OK
+ * $.jStorage.set('key', {xml: xmlNode}); // NOT OK
+ */
+ _XMLService = {
+
+ /**
+ * Validates a XML node to be XML
+ * based on jQuery.isXML function
+ */
+ isXML: function(elm) {
+ var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
+ return documentElement ? documentElement.nodeName !== 'HTML' : false;
+ },
+
+ /**
+ * Encodes a XML node to string
+ * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
+ */
+ encode: function(xmlNode) {
+ if (!this.isXML(xmlNode)) {
+ return false;
+ }
+ try { // Mozilla, Webkit, Opera
+ return new XMLSerializer().serializeToString(xmlNode);
+ } catch (E1) {
+ try { // IE
+ return xmlNode.xml;
+ } catch (E2) {}
+ }
+ return false;
+ },
+
+ /**
+ * Decodes a XML node from string
+ * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
+ */
+ decode: function(xmlString) {
+ var dom_parser = ('DOMParser' in window && (new DOMParser()).parseFromString) ||
+ (window.ActiveXObject && function(_xmlString) {
+ var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
+ xml_doc.async = 'false';
+ xml_doc.loadXML(_xmlString);
+ return xml_doc;
+ }),
+ resultXML;
+ if (!dom_parser) {
+ return false;
+ }
+ resultXML = dom_parser.call('DOMParser' in window && (new DOMParser()) || window, xmlString, 'text/xml');
+ return this.isXML(resultXML) ? resultXML : false;
+ }
+ };
+
+
+ ////////////////////////// PRIVATE METHODS ////////////////////////
+
+ /**
+ * Initialization function. Detects if the browser supports DOM Storage
+ * or userData behavior and behaves accordingly.
+ */
+ function _init() {
+ /* Check if browser supports localStorage */
+ var localStorageReallyWorks = false;
+ if ('localStorage' in window) {
+ try {
+ window.localStorage.setItem('_tmptest', 'tmpval');
+ localStorageReallyWorks = true;
+ window.localStorage.removeItem('_tmptest');
+ } catch (BogusQuotaExceededErrorOnIos5) {
+ // Thanks be to iOS5 Private Browsing mode which throws
+ // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
+ }
+ }
+
+ if (localStorageReallyWorks) {
+ try {
+ if (window.localStorage) {
+ _storage_service = window.localStorage;
+ _backend = 'localStorage';
+ _observer_update = _storage_service.jStorage_update;
+ }
+ } catch (E3) { /* Firefox fails when touching localStorage and cookies are disabled */ }
+ }
+ /* Check if browser supports globalStorage */
+ else if ('globalStorage' in window) {
+ try {
+ if (window.globalStorage) {
+ if (window.location.hostname == 'localhost') {
+ _storage_service = window.globalStorage['localhost.localdomain'];
+ } else {
+ _storage_service = window.globalStorage[window.location.hostname];
+ }
+ _backend = 'globalStorage';
+ _observer_update = _storage_service.jStorage_update;
+ }
+ } catch (E4) { /* Firefox fails when touching localStorage and cookies are disabled */ }
+ }
+ /* Check if browser supports userData behavior */
+ else {
+ _storage_elm = document.createElement('link');
+ if (_storage_elm.addBehavior) {
+
+ /* Use a DOM element to act as userData storage */
+ _storage_elm.style.behavior = 'url(#default#userData)';
+
+ /* userData element needs to be inserted into the DOM! */
+ document.getElementsByTagName('head')[0].appendChild(_storage_elm);
+
+ try {
+ _storage_elm.load('jStorage');
+ } catch (E) {
+ // try to reset cache
+ _storage_elm.setAttribute('jStorage', '{}');
+ _storage_elm.save('jStorage');
+ _storage_elm.load('jStorage');
+ }
+
+ var data = '{}';
+ try {
+ data = _storage_elm.getAttribute('jStorage');
+ } catch (E5) {}
+
+ try {
+ _observer_update = _storage_elm.getAttribute('jStorage_update');
+ } catch (E6) {}
+
+ _storage_service.jStorage = data;
+ _backend = 'userDataBehavior';
+ } else {
+ _storage_elm = null;
+ return;
+ }
+ }
+
+ // Load data from storage
+ _load_storage();
+
+ // remove dead keys
+ _handleTTL();
+
+ // start listening for changes
+ _setupObserver();
+
+ // initialize publish-subscribe service
+ _handlePubSub();
+
+ // handle cached navigation
+ if ('addEventListener' in window) {
+ window.addEventListener('pageshow', function(event) {
+ if (event.persisted) {
+ _storageObserver();
+ }
+ }, false);
+ }
+ }
+
+ /**
+ * Reload data from storage when needed
+ */
+ function _reloadData() {
+ var data = '{}';
+
+ if (_backend == 'userDataBehavior') {
+ _storage_elm.load('jStorage');
+
+ try {
+ data = _storage_elm.getAttribute('jStorage');
+ } catch (E5) {}
+
+ try {
+ _observer_update = _storage_elm.getAttribute('jStorage_update');
+ } catch (E6) {}
+
+ _storage_service.jStorage = data;
+ }
+
+ _load_storage();
+
+ // remove dead keys
+ _handleTTL();
+
+ _handlePubSub();
+ }
+
+ /**
+ * Sets up a storage change observer
+ */
+ function _setupObserver() {
+ if (_backend == 'localStorage' || _backend == 'globalStorage') {
+ if ('addEventListener' in window) {
+ window.addEventListener('storage', _storageObserver, false);
+ } else {
+ document.attachEvent('onstorage', _storageObserver);
+ }
+ } else if (_backend == 'userDataBehavior') {
+ setInterval(_storageObserver, 1000);
+ }
+ }
+
+ /**
+ * Fired on any kind of data change, needs to check if anything has
+ * really been changed
+ */
+ function _storageObserver() {
+ var updateTime;
+ // cumulate change notifications with timeout
+ clearTimeout(_observer_timeout);
+ _observer_timeout = setTimeout(function() {
+
+ if (_backend == 'localStorage' || _backend == 'globalStorage') {
+ updateTime = _storage_service.jStorage_update;
+ } else if (_backend == 'userDataBehavior') {
+ _storage_elm.load('jStorage');
+ try {
+ updateTime = _storage_elm.getAttribute('jStorage_update');
+ } catch (E5) {}
+ }
+
+ if (updateTime && updateTime != _observer_update) {
+ _observer_update = updateTime;
+ _checkUpdatedKeys();
+ }
+
+ }, 25);
+ }
+
+ /**
+ * Reloads the data and checks if any keys are changed
+ */
+ function _checkUpdatedKeys() {
+ var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
+ newCrc32List;
+
+ _reloadData();
+ newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));
+
+ var key,
+ updated = [],
+ removed = [];
+
+ for (key in oldCrc32List) {
+ if (oldCrc32List.hasOwnProperty(key)) {
+ if (!newCrc32List[key]) {
+ removed.push(key);
+ continue;
+ }
+ if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == '2.') {
+ updated.push(key);
+ }
+ }
+ }
+
+ for (key in newCrc32List) {
+ if (newCrc32List.hasOwnProperty(key)) {
+ if (!oldCrc32List[key]) {
+ updated.push(key);
+ }
+ }
+ }
+
+ _fireObservers(updated, 'updated');
+ _fireObservers(removed, 'deleted');
+ }
+
+ /**
+ * Fires observers for updated keys
+ *
+ * @param {Array|String} keys Array of key names or a key
+ * @param {String} action What happened with the value (updated, deleted, flushed)
+ */
+ function _fireObservers(keys, action) {
+ keys = [].concat(keys || []);
+
+ var i, j, len, jlen;
+
+ if (action == 'flushed') {
+ keys = [];
+ for (var key in _observers) {
+ if (_observers.hasOwnProperty(key)) {
+ keys.push(key);
+ }
+ }
+ action = 'deleted';
+ }
+ for (i = 0, len = keys.length; i < len; i++) {
+ if (_observers[keys[i]]) {
+ for (j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
+ _observers[keys[i]][j](keys[i], action);
+ }
+ }
+ if (_observers['*']) {
+ for (j = 0, jlen = _observers['*'].length; j < jlen; j++) {
+ _observers['*'][j](keys[i], action);
+ }
+ }
+ }
+ }
+
+ /**
+ * Publishes key change to listeners
+ */
+ function _publishChange() {
+ var updateTime = (+new Date()).toString();
+
+ if (_backend == 'localStorage' || _backend == 'globalStorage') {
+ try {
+ _storage_service.jStorage_update = updateTime;
+ } catch (E8) {
+ // safari private mode has been enabled after the jStorage initialization
+ _backend = false;
+ }
+ } else if (_backend == 'userDataBehavior') {
+ _storage_elm.setAttribute('jStorage_update', updateTime);
+ _storage_elm.save('jStorage');
+ }
+
+ _storageObserver();
+ }
+
+ /**
+ * Loads the data from the storage based on the supported mechanism
+ */
+ function _load_storage() {
+ /* if jStorage string is retrieved, then decode it */
+ if (_storage_service.jStorage) {
+ try {
+ _storage = JSON.parse(String(_storage_service.jStorage));
+ } catch (E6) {
+ _storage_service.jStorage = '{}';
+ }
+ } else {
+ _storage_service.jStorage = '{}';
+ }
+ _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
+
+ if (!_storage.__jstorage_meta) {
+ _storage.__jstorage_meta = {};
+ }
+ if (!_storage.__jstorage_meta.CRC32) {
+ _storage.__jstorage_meta.CRC32 = {};
+ }
+ }
+
+ /**
+ * This functions provides the 'save' mechanism to store the jStorage object
+ */
+ function _save() {
+ _dropOldEvents(); // remove expired events
+ try {
+ _storage_service.jStorage = JSON.stringify(_storage);
+ // If userData is used as the storage engine, additional
+ if (_storage_elm) {
+ _storage_elm.setAttribute('jStorage', _storage_service.jStorage);
+ _storage_elm.save('jStorage');
+ }
+ _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
+ } catch (E7) { /* probably cache is full, nothing is saved this way*/ }
+ }
+
+ /**
+ * Function checks if a key is set and is string or numberic
+ *
+ * @param {String} key Key name
+ */
+ function _checkKey(key) {
+ if (typeof key != 'string' && typeof key != 'number') {
+ throw new TypeError('Key name must be string or numeric');
+ }
+ if (key == '__jstorage_meta') {
+ throw new TypeError('Reserved key name');
+ }
+ return true;
+ }
+
+ /**
+ * Removes expired keys
+ */
+ function _handleTTL() {
+ var curtime, i, TTL, CRC32, nextExpire = Infinity,
+ changed = false,
+ deleted = [];
+
+ clearTimeout(_ttl_timeout);
+
+ if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != 'object') {
+ // nothing to do here
+ return;
+ }
+
+ curtime = +new Date();
+ TTL = _storage.__jstorage_meta.TTL;
+
+ CRC32 = _storage.__jstorage_meta.CRC32;
+ for (i in TTL) {
+ if (TTL.hasOwnProperty(i)) {
+ if (TTL[i] <= curtime) {
+ delete TTL[i];
+ delete CRC32[i];
+ delete _storage[i];
+ changed = true;
+ deleted.push(i);
+ } else if (TTL[i] < nextExpire) {
+ nextExpire = TTL[i];
+ }
+ }
+ }
+
+ // set next check
+ if (nextExpire != Infinity) {
+ _ttl_timeout = setTimeout(_handleTTL, Math.min(nextExpire - curtime, 0x7FFFFFFF));
+ }
+
+ // save changes
+ if (changed) {
+ _save();
+ _publishChange();
+ _fireObservers(deleted, 'deleted');
+ }
+ }
+
+ /**
+ * Checks if there's any events on hold to be fired to listeners
+ */
+ function _handlePubSub() {
+ var i, len;
+ if (!_storage.__jstorage_meta.PubSub) {
+ return;
+ }
+ var pubelm,
+ _pubsubCurrent = _pubsub_last,
+ needFired = [];
+
+ for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
+ pubelm = _storage.__jstorage_meta.PubSub[i];
+ if (pubelm[0] > _pubsub_last) {
+ _pubsubCurrent = pubelm[0];
+ needFired.unshift(pubelm);
+ }
+ }
+
+ for (i = needFired.length - 1; i >= 0; i--) {
+ _fireSubscribers(needFired[i][1], needFired[i][2]);
+ }
+
+ _pubsub_last = _pubsubCurrent;
+ }
+
+ /**
+ * Fires all subscriber listeners for a pubsub channel
+ *
+ * @param {String} channel Channel name
+ * @param {Mixed} payload Payload data to deliver
+ */
+ function _fireSubscribers(channel, payload) {
+ if (_pubsub_observers[channel]) {
+ for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
+ // send immutable data that can't be modified by listeners
+ try {
+ _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
+ } catch (E) {}
+ }
+ }
+ }
+
+ /**
+ * Remove old events from the publish stream (at least 2sec old)
+ */
+ function _dropOldEvents() {
+ if (!_storage.__jstorage_meta.PubSub) {
+ return;
+ }
+
+ var retire = +new Date() - 2000;
+
+ for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
+ if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
+ // deleteCount is needed for IE6
+ _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
+ break;
+ }
+ }
+
+ if (!_storage.__jstorage_meta.PubSub.length) {
+ delete _storage.__jstorage_meta.PubSub;
+ }
+
+ }
+
+ /**
+ * Publish payload to a channel
+ *
+ * @param {String} channel Channel name
+ * @param {Mixed} payload Payload to send to the subscribers
+ */
+ function _publish(channel, payload) {
+ if (!_storage.__jstorage_meta) {
+ _storage.__jstorage_meta = {};
+ }
+ if (!_storage.__jstorage_meta.PubSub) {
+ _storage.__jstorage_meta.PubSub = [];
+ }
+
+ _storage.__jstorage_meta.PubSub.unshift([+new Date(), channel, payload]);
+
+ _save();
+ _publishChange();
+ }
+
+
+ /**
+ * JS Implementation of MurmurHash2
+ *
+ * SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
+ *
+ * @author <a href='mailto:gary.court@gmail.com'>Gary Court</a>
+ * @see http://github.com/garycourt/murmurhash-js
+ * @author <a href='mailto:aappleby@gmail.com'>Austin Appleby</a>
+ * @see http://sites.google.com/site/murmurhash/
+ *
+ * @param {string} str ASCII only
+ * @param {number} seed Positive integer only
+ * @return {number} 32-bit positive integer hash
+ */
+
+ function murmurhash2_32_gc(str, seed) {
+ var
+ l = str.length,
+ h = seed ^ l,
+ i = 0,
+ k;
+
+ while (l >= 4) {
+ k =
+ ((str.charCodeAt(i) & 0xff)) |
+ ((str.charCodeAt(++i) & 0xff) << 8) |
+ ((str.charCodeAt(++i) & 0xff) << 16) |
+ ((str.charCodeAt(++i) & 0xff) << 24);
+
+ k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
+ k ^= k >>> 24;
+ k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
+
+ h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
+
+ l -= 4;
+ ++i;
+ }
+
+ switch (l) {
+ case 3:
+ h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
+ /* falls through */
+ case 2:
+ h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
+ /* falls through */
+ case 1:
+ h ^= (str.charCodeAt(i) & 0xff);
+ h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
+ }
+
+ h ^= h >>> 13;
+ h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
+ h ^= h >>> 15;
+
+ return h >>> 0;
+ }
+
+ ////////////////////////// PUBLIC INTERFACE /////////////////////////
+
+ $.jStorage = {
+ /* Version number */
+ version: JSTORAGE_VERSION,
+
+ /**
+ * Sets a key's value.
+ *
+ * @param {String} key Key to set. If this value is not set or not
+ * a string an exception is raised.
+ * @param {Mixed} value Value to set. This can be any value that is JSON
+ * compatible (Numbers, Strings, Objects etc.).
+ * @param {Object} [options] - possible options to use
+ * @param {Number} [options.TTL] - optional TTL value, in milliseconds
+ * @return {Mixed} the used value
+ */
+ set: function(key, value, options) {
+ _checkKey(key);
+
+ options = options || {};
+
+ // undefined values are deleted automatically
+ if (typeof value == 'undefined') {
+ this.deleteKey(key);
+ return value;
+ }
+
+ if (_XMLService.isXML(value)) {
+ value = {
+ _is_xml: true,
+ xml: _XMLService.encode(value)
+ };
+ } else if (typeof value == 'function') {
+ return undefined; // functions can't be saved!
+ } else if (value && typeof value == 'object') {
+ // clone the object before saving to _storage tree
+ value = JSON.parse(JSON.stringify(value));
+ }
+
+ _storage[key] = value;
+
+ _storage.__jstorage_meta.CRC32[key] = '2.' + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);
+
+ this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
+
+ _fireObservers(key, 'updated');
+ return value;
+ },
+
+ /**
+ * Looks up a key in cache
+ *
+ * @param {String} key - Key to look up.
+ * @param {mixed} def - Default value to return, if key didn't exist.
+ * @return {Mixed} the key value, default value or null
+ */
+ get: function(key, def) {
+ _checkKey(key);
+ if (key in _storage) {
+ if (_storage[key] && typeof _storage[key] == 'object' && _storage[key]._is_xml) {
+ return _XMLService.decode(_storage[key].xml);
+ } else {
+ return _storage[key];
+ }
+ }
+ return typeof(def) == 'undefined' ? null : def;
+ },
+
+ /**
+ * Deletes a key from cache.
+ *
+ * @param {String} key - Key to delete.
+ * @return {Boolean} true if key existed or false if it didn't
+ */
+ deleteKey: function(key) {
+ _checkKey(key);
+ if (key in _storage) {
+ delete _storage[key];
+ // remove from TTL list
+ if (typeof _storage.__jstorage_meta.TTL == 'object' &&
+ key in _storage.__jstorage_meta.TTL) {
+ delete _storage.__jstorage_meta.TTL[key];
+ }
+
+ delete _storage.__jstorage_meta.CRC32[key];
+
+ _save();
+ _publishChange();
+ _fireObservers(key, 'deleted');
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Sets a TTL for a key, or remove it if ttl value is 0 or below
+ *
+ * @param {String} key - key to set the TTL for
+ * @param {Number} ttl - TTL timeout in milliseconds
+ * @return {Boolean} true if key existed or false if it didn't
+ */
+ setTTL: function(key, ttl) {
+ var curtime = +new Date();
+ _checkKey(key);
+ ttl = Number(ttl) || 0;
+ if (key in _storage) {
+
+ if (!_storage.__jstorage_meta.TTL) {
+ _storage.__jstorage_meta.TTL = {};
+ }
+
+ // Set TTL value for the key
+ if (ttl > 0) {
+ _storage.__jstorage_meta.TTL[key] = curtime + ttl;
+ } else {
+ delete _storage.__jstorage_meta.TTL[key];
+ }
+
+ _save();
+
+ _handleTTL();
+
+ _publishChange();
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
+ *
+ * @param {String} key Key to check
+ * @return {Number} Remaining TTL in milliseconds
+ */
+ getTTL: function(key) {
+ var curtime = +new Date(),
+ ttl;
+ _checkKey(key);
+ if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
+ ttl = _storage.__jstorage_meta.TTL[key] - curtime;
+ return ttl || 0;
+ }
+ return 0;
+ },
+
+ /**
+ * Deletes everything in cache.
+ *
+ * @return {Boolean} Always true
+ */
+ flush: function() {
+ _storage = {
+ __jstorage_meta: {
+ CRC32: {}
+ }
+ };
+ _save();
+ _publishChange();
+ _fireObservers(null, 'flushed');
+ return true;
+ },
+
+ /**
+ * Returns a read-only copy of _storage
+ *
+ * @return {Object} Read-only copy of _storage
+ */
+ storageObj: function() {
+ function F() {}
+ F.prototype = _storage;
+ return new F();
+ },
+
+ /**
+ * Returns an index of all used keys as an array
+ * ['key1', 'key2',..'keyN']
+ *
+ * @return {Array} Used keys
+ */
+ index: function() {
+ var index = [],
+ i;
+ for (i in _storage) {
+ if (_storage.hasOwnProperty(i) && i != '__jstorage_meta') {
+ index.push(i);
+ }
+ }
+ return index;
+ },
+
+ /**
+ * How much space in bytes does the storage take?
+ *
+ * @return {Number} Storage size in chars (not the same as in bytes,
+ * since some chars may take several bytes)
+ */
+ storageSize: function() {
+ return _storage_size;
+ },
+
+ /**
+ * Which backend is currently in use?
+ *
+ * @return {String} Backend name
+ */
+ currentBackend: function() {
+ return _backend;
+ },
+
+ /**
+ * Test if storage is available
+ *
+ * @return {Boolean} True if storage can be used
+ */
+ storageAvailable: function() {
+ return !!_backend;
+ },
+
+ /**
+ * Register change listeners
+ *
+ * @param {String} key Key name
+ * @param {Function} callback Function to run when the key changes
+ */
+ listenKeyChange: function(key, callback) {
+ _checkKey(key);
+ if (!_observers[key]) {
+ _observers[key] = [];
+ }
+ _observers[key].push(callback);
+ },
+
+ /**
+ * Remove change listeners
+ *
+ * @param {String} key Key name to unregister listeners against
+ * @param {Function} [callback] If set, unregister the callback, if not - unregister all
+ */
+ stopListening: function(key, callback) {
+ _checkKey(key);
+
+ if (!_observers[key]) {
+ return;
+ }
+
+ if (!callback) {
+ delete _observers[key];
+ return;
+ }
+
+ for (var i = _observers[key].length - 1; i >= 0; i--) {
+ if (_observers[key][i] == callback) {
+ _observers[key].splice(i, 1);
+ }
+ }
+ },
+
+ /**
+ * Subscribe to a Publish/Subscribe event stream
+ *
+ * @param {String} channel Channel name
+ * @param {Function} callback Function to run when the something is published to the channel
+ */
+ subscribe: function(channel, callback) {
+ channel = (channel || '').toString();
+ if (!channel) {
+ throw new TypeError('Channel not defined');
+ }
+ if (!_pubsub_observers[channel]) {
+ _pubsub_observers[channel] = [];
+ }
+ _pubsub_observers[channel].push(callback);
+ },
+
+ /**
+ * Publish data to an event stream
+ *
+ * @param {String} channel Channel name
+ * @param {Mixed} payload Payload to deliver
+ */
+ publish: function(channel, payload) {
+ channel = (channel || '').toString();
+ if (!channel) {
+ throw new TypeError('Channel not defined');
+ }
+
+ _publish(channel, payload);
+ },
+
+ /**
+ * Reloads the data from browser storage
+ */
+ reInit: function() {
+ _reloadData();
+ },
+
+ /**
+ * Removes reference from global objects and saves it as jStorage
+ *
+ * @param {Boolean} option if needed to save object as simple 'jStorage' in windows context
+ */
+ noConflict: function(saveInGlobal) {
+ delete window.$.jStorage;
+
+ if (saveInGlobal) {
+ window.jStorage = this;
+ }
+
+ return this;
+ }
+ };
+
+ // Initialize jStorage
+ _init();
+
+})();
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.md5.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.md5.js
new file mode 100644
index 00000000..da60ff56
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.md5.js
@@ -0,0 +1,275 @@
+/*
+ * JavaScript MD5 1.1.0
+ * https://github.com/blueimp/JavaScript-MD5
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*jslint bitwise: true */
+/*global unescape, define */
+
+(function ($) {
+ 'use strict';
+
+ /*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+ function safe_add(x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF),
+ msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ }
+
+ /*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+ function bit_rol(num, cnt) {
+ return (num << cnt) | (num >>> (32 - cnt));
+ }
+
+ /*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+ function md5_cmn(q, a, b, x, s, t) {
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
+ }
+ function md5_ff(a, b, c, d, x, s, t) {
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+ }
+ function md5_gg(a, b, c, d, x, s, t) {
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+ }
+ function md5_hh(a, b, c, d, x, s, t) {
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+ }
+ function md5_ii(a, b, c, d, x, s, t) {
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+ }
+
+ /*
+ * Calculate the MD5 of an array of little-endian words, and a bit length.
+ */
+ function binl_md5(x, len) {
+ /* append padding */
+ x[len >> 5] |= 0x80 << (len % 32);
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ var i, olda, oldb, oldc, oldd,
+ a = 1732584193,
+ b = -271733879,
+ c = -1732584194,
+ d = 271733878;
+
+ for (i = 0; i < x.length; i += 16) {
+ olda = a;
+ oldb = b;
+ oldc = c;
+ oldd = d;
+
+ a = md5_ff(a, b, c, d, x[i], 7, -680876936);
+ d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+ c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+ b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+ a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+ d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+ c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+ b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+ a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+ d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+ c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+ b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+ a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+ d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+ c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+ b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+ a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+ d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+ c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+ b = md5_gg(b, c, d, a, x[i], 20, -373897302);
+ a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+ d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+ c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+ b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+ a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+ d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+ c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+ b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+ a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+ d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+ c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+ b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+ a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+ d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+ c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+ b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+ a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+ d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+ c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+ b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+ a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+ d = md5_hh(d, a, b, c, x[i], 11, -358537222);
+ c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+ b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+ a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+ d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+ c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+ b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+ a = md5_ii(a, b, c, d, x[i], 6, -198630844);
+ d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+ c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+ b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+ a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+ d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+ c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+ b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+ a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+ d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+ c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+ b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+ a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+ d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+ c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+ b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ }
+ return [a, b, c, d];
+ }
+
+ /*
+ * Convert an array of little-endian words to a string
+ */
+ function binl2rstr(input) {
+ var i,
+ output = '';
+ for (i = 0; i < input.length * 32; i += 8) {
+ output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
+ }
+ return output;
+ }
+
+ /*
+ * Convert a raw string to an array of little-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+ function rstr2binl(input) {
+ var i,
+ output = [];
+ output[(input.length >> 2) - 1] = undefined;
+ for (i = 0; i < output.length; i += 1) {
+ output[i] = 0;
+ }
+ for (i = 0; i < input.length * 8; i += 8) {
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
+ }
+ return output;
+ }
+
+ /*
+ * Calculate the MD5 of a raw string
+ */
+ function rstr_md5(s) {
+ return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
+ }
+
+ /*
+ * Calculate the HMAC-MD5, of a key and some data (raw strings)
+ */
+ function rstr_hmac_md5(key, data) {
+ var i,
+ bkey = rstr2binl(key),
+ ipad = [],
+ opad = [],
+ hash;
+ ipad[15] = opad[15] = undefined;
+ if (bkey.length > 16) {
+ bkey = binl_md5(bkey, key.length * 8);
+ }
+ for (i = 0; i < 16; i += 1) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+ hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
+ return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
+ }
+
+ /*
+ * Convert a raw string to a hex string
+ */
+ function rstr2hex(input) {
+ var hex_tab = '0123456789abcdef',
+ output = '',
+ x,
+ i;
+ for (i = 0; i < input.length; i += 1) {
+ x = input.charCodeAt(i);
+ output += hex_tab.charAt((x >>> 4) & 0x0F) +
+ hex_tab.charAt(x & 0x0F);
+ }
+ return output;
+ }
+
+ /*
+ * Encode a string as utf-8
+ */
+ function str2rstr_utf8(input) {
+ return unescape(encodeURIComponent(input));
+ }
+
+ /*
+ * Take string arguments and return either raw or hex encoded strings
+ */
+ function raw_md5(s) {
+ return rstr_md5(str2rstr_utf8(s));
+ }
+ function hex_md5(s) {
+ return rstr2hex(raw_md5(s));
+ }
+ function raw_hmac_md5(k, d) {
+ return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d));
+ }
+ function hex_hmac_md5(k, d) {
+ return rstr2hex(raw_hmac_md5(k, d));
+ }
+
+ function md5(string, key, raw) {
+ if (!key) {
+ if (!raw) {
+ return hex_md5(string);
+ }
+ return raw_md5(string);
+ }
+ if (!raw) {
+ return hex_hmac_md5(key, string);
+ }
+ return raw_hmac_md5(key, string);
+ }
+
+ if (typeof define === 'function' && define.amd) {
+ define(function () {
+ return md5;
+ });
+ } else {
+ $.md5 = md5;
+ }
+}(this));
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.css b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.css
new file mode 100644
index 00000000..c22f3727
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.css
@@ -0,0 +1,485 @@
+/*
+ * qTip2 - Pretty powerful tooltips - v3.0.3
+ * http://qtip2.com
+ *
+ * Copyright (c) 2017
+ * Released under the MIT licenses
+ * http://jquery.org/license
+ *
+ * Date: Tue Aug 29 2017 04:38 EDT-0400
+ * Plugins: tips viewport svg
+ * Styles: core css3
+ */
+.qtip{
+ position: absolute;
+ left: -28000px;
+ top: -28000px;
+ display: none;
+
+ max-width: 280px;
+ min-width: 50px;
+
+ font-size: 10.5px;
+ line-height: 12px;
+
+ direction: ltr;
+
+ box-shadow: none;
+ padding: 0;
+}
+
+ .qtip-content{
+ position: relative;
+ padding: 5px 9px;
+ overflow: hidden;
+
+ text-align: left;
+ word-wrap: break-word;
+ }
+
+ .qtip-titlebar{
+ position: relative;
+ padding: 5px 35px 5px 10px;
+ overflow: hidden;
+
+ border-width: 0 0 1px;
+ font-weight: bold;
+ }
+
+ .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
+
+ /* Default close button class */
+ .qtip-close{
+ position: absolute;
+ right: -9px; top: -9px;
+ z-index: 11; /* Overlap .qtip-tip */
+
+ cursor: pointer;
+ outline: medium none;
+
+ border: 1px solid transparent;
+ }
+
+ .qtip-titlebar .qtip-close{
+ right: 4px; top: 50%;
+ margin-top: -9px;
+ }
+
+ * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
+
+ .qtip-titlebar .ui-icon,
+ .qtip-icon .ui-icon{
+ display: block;
+ text-indent: -1000em;
+ direction: ltr;
+ }
+
+ .qtip-icon, .qtip-icon .ui-icon{
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ text-decoration: none;
+ }
+
+ .qtip-icon .ui-icon{
+ width: 18px;
+ height: 14px;
+
+ line-height: 14px;
+ text-align: center;
+ text-indent: 0;
+ font: normal bold 10px/13px Tahoma,sans-serif;
+
+ color: inherit;
+ background: transparent none no-repeat -100em -100em;
+ }
+
+/* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
+.qtip-focus{}
+
+/* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
+.qtip-hover{}
+
+/* Default tooltip style */
+.qtip-default{
+ border: 1px solid #F1D031;
+
+ background-color: #FFFFA3;
+ color: #555;
+}
+
+ .qtip-default .qtip-titlebar{
+ background-color: #FFEF93;
+ }
+
+ .qtip-default .qtip-icon{
+ border-color: #CCC;
+ background: #F1F1F1;
+ color: #777;
+ }
+
+ .qtip-default .qtip-titlebar .qtip-close{
+ border-color: #AAA;
+ color: #111;
+ }
+
+
+.qtip-shadow{
+ -webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+ box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
+}
+
+/* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
+.qtip-rounded,
+.qtip-tipsy,
+.qtip-bootstrap{
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.qtip-rounded .qtip-titlebar{
+ -moz-border-radius: 4px 4px 0 0;
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+/* Youtube tooltip style */
+.qtip-youtube{
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+
+ -webkit-box-shadow: 0 0 3px #333;
+ -moz-box-shadow: 0 0 3px #333;
+ box-shadow: 0 0 3px #333;
+
+ color: white;
+ border: 0 solid transparent;
+
+ background: #4A4A4A;
+ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
+ background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -moz-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -ms-linear-gradient(top,#4A4A4A 0,black 100%);
+ background-image: -o-linear-gradient(top,#4A4A4A 0,black 100%);
+}
+
+ .qtip-youtube .qtip-titlebar{
+ background-color: #4A4A4A;
+ background-color: rgba(0,0,0,0);
+ }
+
+ .qtip-youtube .qtip-content{
+ padding: .75em;
+ font: 12px arial,sans-serif;
+
+ filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);
+ -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);";
+ }
+
+ .qtip-youtube .qtip-icon{
+ border-color: #222;
+ }
+
+ .qtip-youtube .qtip-titlebar .ui-state-hover{
+ border-color: #303030;
+ }
+
+
+/* jQuery TOOLS Tooltip style */
+.qtip-jtools{
+ background: #232323;
+ background: rgba(0, 0, 0, 0.7);
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323));
+ background-image: -moz-linear-gradient(top, #717171, #232323);
+ background-image: -webkit-linear-gradient(top, #717171, #232323);
+ background-image: -ms-linear-gradient(top, #717171, #232323);
+ background-image: -o-linear-gradient(top, #717171, #232323);
+
+ border: 2px solid #ddd;
+ border: 2px solid rgba(241,241,241,1);
+
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+
+ -webkit-box-shadow: 0 0 12px #333;
+ -moz-box-shadow: 0 0 12px #333;
+ box-shadow: 0 0 12px #333;
+}
+
+ /* IE Specific */
+ .qtip-jtools .qtip-titlebar{
+ background-color: transparent;
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";
+ }
+ .qtip-jtools .qtip-content{
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";
+ }
+
+ .qtip-jtools .qtip-titlebar,
+ .qtip-jtools .qtip-content{
+ background: transparent;
+ color: white;
+ border: 0 dashed transparent;
+ }
+
+ .qtip-jtools .qtip-icon{
+ border-color: #555;
+ }
+
+ .qtip-jtools .qtip-titlebar .ui-state-hover{
+ border-color: #333;
+ }
+
+
+/* Cluetip style */
+.qtip-cluetip{
+ -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+ -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+ box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
+
+ background-color: #D9D9C2;
+ color: #111;
+ border: 0 dashed transparent;
+}
+
+ .qtip-cluetip .qtip-titlebar{
+ background-color: #87876A;
+ color: white;
+ border: 0 dashed transparent;
+ }
+
+ .qtip-cluetip .qtip-icon{
+ border-color: #808064;
+ }
+
+ .qtip-cluetip .qtip-titlebar .ui-state-hover{
+ border-color: #696952;
+ color: #696952;
+ }
+
+
+/* Tipsy style */
+.qtip-tipsy{
+ background: black;
+ background: rgba(0, 0, 0, .87);
+
+ color: white;
+ border: 0 solid transparent;
+
+ font-size: 11px;
+ font-family: 'Lucida Grande', sans-serif;
+ font-weight: bold;
+ line-height: 16px;
+ text-shadow: 0 1px black;
+}
+
+ .qtip-tipsy .qtip-titlebar{
+ padding: 6px 35px 0 10px;
+ background-color: transparent;
+ }
+
+ .qtip-tipsy .qtip-content{
+ padding: 6px 10px;
+ }
+
+ .qtip-tipsy .qtip-icon{
+ border-color: #222;
+ text-shadow: none;
+ }
+
+ .qtip-tipsy .qtip-titlebar .ui-state-hover{
+ border-color: #303030;
+ }
+
+
+/* Tipped style */
+.qtip-tipped{
+ border: 3px solid #959FA9;
+
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+
+ background-color: #F9F9F9;
+ color: #454545;
+
+ font-weight: normal;
+ font-family: serif;
+}
+
+ .qtip-tipped .qtip-titlebar{
+ border-bottom-width: 0;
+
+ color: white;
+ background: #3A79B8;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D));
+ background-image: -webkit-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -ms-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -o-linear-gradient(top, #3A79B8, #2E629D);
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";
+ }
+
+ .qtip-tipped .qtip-icon{
+ border: 2px solid #285589;
+ background: #285589;
+ }
+
+ .qtip-tipped .qtip-icon .ui-icon{
+ background-color: #FBFBFB;
+ color: #555;
+ }
+
+
+/**
+ * Twitter Bootstrap style.
+ *
+ * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11.
+ * Does not work with IE 7.
+ */
+.qtip-bootstrap{
+ /** Taken from Bootstrap body */
+ font-size: 14px;
+ line-height: 20px;
+ color: #333333;
+
+ /** Taken from Bootstrap .popover */
+ padding: 1px;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+ .qtip-bootstrap .qtip-titlebar{
+ /** Taken from Bootstrap .popover-title */
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+ }
+
+ .qtip-bootstrap .qtip-titlebar .qtip-close{
+ /**
+ * Overrides qTip2:
+ * .qtip-titlebar .qtip-close{
+ * [...]
+ * right: 4px;
+ * top: 50%;
+ * [...]
+ * border-style: solid;
+ * }
+ */
+ right: 11px;
+ top: 45%;
+ border-style: none;
+ }
+
+ .qtip-bootstrap .qtip-content{
+ /** Taken from Bootstrap .popover-content */
+ padding: 9px 14px;
+ }
+
+ .qtip-bootstrap .qtip-icon{
+ /**
+ * Overrides qTip2:
+ * .qtip-default .qtip-icon {
+ * border-color: #CCC;
+ * background: #F1F1F1;
+ * color: #777;
+ * }
+ */
+ background: transparent;
+ }
+
+ .qtip-bootstrap .qtip-icon .ui-icon{
+ /**
+ * Overrides qTip2:
+ * .qtip-icon .ui-icon{
+ * width: 18px;
+ * height: 14px;
+ * }
+ */
+ width: auto;
+ height: auto;
+
+ /* Taken from Bootstrap .close */
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 18px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+ }
+
+ .qtip-bootstrap .qtip-icon .ui-icon:hover{
+ /* Taken from Bootstrap .close:hover */
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+ }
+
+
+/* IE9 fix - removes all filters */
+.qtip:not(.ie9haxors) div.qtip-content,
+.qtip:not(.ie9haxors) div.qtip-titlebar{
+ filter: none;
+ -ms-filter: none;
+}
+
+
+.qtip .qtip-tip{
+ margin: 0 auto;
+ overflow: hidden;
+ z-index: 10;
+
+}
+
+ /* Opera bug #357 - Incorrect tip position
+ https://github.com/Craga89/qTip2/issues/367 */
+ x:-o-prefocus, .qtip .qtip-tip{
+ visibility: hidden;
+ }
+
+ .qtip .qtip-tip,
+ .qtip .qtip-tip .qtip-vml,
+ .qtip .qtip-tip canvas{
+ position: absolute;
+
+ color: #123456;
+ background: transparent;
+ border: 0 dashed transparent;
+ }
+
+ .qtip .qtip-tip canvas{ top: 0; left: 0; }
+
+ .qtip .qtip-tip .qtip-vml{
+ behavior: url(#default#VML);
+ display: inline-block;
+ visibility: visible;
+ }
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.js
new file mode 100644
index 00000000..91c3efed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.qtip.js
@@ -0,0 +1,2967 @@
+/*
+ * qTip2 - Pretty powerful tooltips - v3.0.3
+ * http://qtip2.com
+ *
+ * Copyright (c) 2017
+ * Released under the MIT licenses
+ * http://jquery.org/license
+ *
+ * Date: Tue Aug 29 2017 04:38 EDT-0400
+ * Plugins: tips viewport svg
+ * Styles: core css3
+ */
+/*global window: false, jQuery: false, console: false, define: false */
+
+/* Cache window, document, undefined */
+(function( window, document, undefined ) {
+
+// Uses AMD or browser globals to create a jQuery plugin.
+(function( factory ) {
+ "use strict";
+ if(typeof define === 'function' && define.amd) {
+ define(['jquery'], factory);
+ }
+ else if(jQuery && !jQuery.fn.qtip) {
+ factory(jQuery);
+ }
+}
+(function($) {
+ "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
+;// Munge the primitives - Paul Irish tip
+var TRUE = true,
+FALSE = false,
+NULL = null,
+
+// Common variables
+X = 'x', Y = 'y',
+WIDTH = 'width',
+HEIGHT = 'height',
+
+// Positioning sides
+TOP = 'top',
+LEFT = 'left',
+BOTTOM = 'bottom',
+RIGHT = 'right',
+CENTER = 'center',
+
+// Position adjustment types
+FLIP = 'flip',
+FLIPINVERT = 'flipinvert',
+SHIFT = 'shift',
+
+// Shortcut vars
+QTIP, PROTOTYPE, CORNER, CHECKS,
+PLUGINS = {},
+NAMESPACE = 'qtip',
+ATTR_HAS = 'data-hasqtip',
+ATTR_ID = 'data-qtip-id',
+WIDGET = ['ui-widget', 'ui-tooltip'],
+SELECTOR = '.'+NAMESPACE,
+INACTIVE_EVENTS = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '),
+
+CLASS_FIXED = NAMESPACE+'-fixed',
+CLASS_DEFAULT = NAMESPACE + '-default',
+CLASS_FOCUS = NAMESPACE + '-focus',
+CLASS_HOVER = NAMESPACE + '-hover',
+CLASS_DISABLED = NAMESPACE+'-disabled',
+
+replaceSuffix = '_replacedByqTip',
+oldtitle = 'oldtitle',
+trackingBound,
+
+// Browser detection
+BROWSER = {
+ /*
+ * IE version detection
+ *
+ * Adapted from: http://ajaxian.com/archives/attack-of-the-ie-conditional-comment
+ * Credit to James Padolsey for the original implemntation!
+ */
+ ie: (function() {
+ /* eslint-disable no-empty */
+ var v, i;
+ for (
+ v = 4, i = document.createElement('div');
+ (i.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->') && i.getElementsByTagName('i')[0];
+ v+=1
+ ) {}
+ return v > 4 ? v : NaN;
+ /* eslint-enable no-empty */
+ })(),
+
+ /*
+ * iOS version detection
+ */
+ iOS: parseFloat(
+ ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
+ .replace('undefined', '3_2').replace('_', '.').replace('_', '')
+ ) || FALSE
+};
+;function QTip(target, options, id, attr) {
+ // Elements and ID
+ this.id = id;
+ this.target = target;
+ this.tooltip = NULL;
+ this.elements = { target: target };
+
+ // Internal constructs
+ this._id = NAMESPACE + '-' + id;
+ this.timers = { img: {} };
+ this.options = options;
+ this.plugins = {};
+
+ // Cache object
+ this.cache = {
+ event: {},
+ target: $(),
+ disabled: FALSE,
+ attr: attr,
+ onTooltip: FALSE,
+ lastClass: ''
+ };
+
+ // Set the initial flags
+ this.rendered = this.destroyed = this.disabled = this.waiting =
+ this.hiddenDuringWait = this.positioning = this.triggering = FALSE;
+}
+PROTOTYPE = QTip.prototype;
+
+PROTOTYPE._when = function(deferreds) {
+ return $.when.apply($, deferreds);
+};
+
+PROTOTYPE.render = function(show) {
+ if(this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit
+
+ var self = this,
+ options = this.options,
+ cache = this.cache,
+ elements = this.elements,
+ text = options.content.text,
+ title = options.content.title,
+ button = options.content.button,
+ posOptions = options.position,
+ deferreds = [];
+
+ // Add ARIA attributes to target
+ $.attr(this.target[0], 'aria-describedby', this._id);
+
+ // Create public position object that tracks current position corners
+ cache.posClass = this._createPosClass(
+ (this.position = { my: posOptions.my, at: posOptions.at }).my
+ );
+
+ // Create tooltip element
+ this.tooltip = elements.tooltip = $('<div/>', {
+ 'id': this._id,
+ 'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass ].join(' '),
+ 'width': options.style.width || '',
+ 'height': options.style.height || '',
+ 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
+
+ /* ARIA specific attributes */
+ 'role': 'alert',
+ 'aria-live': 'polite',
+ 'aria-atomic': FALSE,
+ 'aria-describedby': this._id + '-content',
+ 'aria-hidden': TRUE
+ })
+ .toggleClass(CLASS_DISABLED, this.disabled)
+ .attr(ATTR_ID, this.id)
+ .data(NAMESPACE, this)
+ .appendTo(posOptions.container)
+ .append(
+ // Create content element
+ elements.content = $('<div />', {
+ 'class': NAMESPACE + '-content',
+ 'id': this._id + '-content',
+ 'aria-atomic': TRUE
+ })
+ );
+
+ // Set rendered flag and prevent redundant reposition calls for now
+ this.rendered = -1;
+ this.positioning = TRUE;
+
+ // Create title...
+ if(title) {
+ this._createTitle();
+
+ // Update title only if its not a callback (called in toggle if so)
+ if(!$.isFunction(title)) {
+ deferreds.push( this._updateTitle(title, FALSE) );
+ }
+ }
+
+ // Create button
+ if(button) { this._createButton(); }
+
+ // Set proper rendered flag and update content if not a callback function (called in toggle)
+ if(!$.isFunction(text)) {
+ deferreds.push( this._updateContent(text, FALSE) );
+ }
+ this.rendered = TRUE;
+
+ // Setup widget classes
+ this._setWidget();
+
+ // Initialize 'render' plugins
+ $.each(PLUGINS, function(name) {
+ var instance;
+ if(this.initialize === 'render' && (instance = this(self))) {
+ self.plugins[name] = instance;
+ }
+ });
+
+ // Unassign initial events and assign proper events
+ this._unassignEvents();
+ this._assignEvents();
+
+ // When deferreds have completed
+ this._when(deferreds).then(function() {
+ // tooltiprender event
+ self._trigger('render');
+
+ // Reset flags
+ self.positioning = FALSE;
+
+ // Show tooltip if not hidden during wait period
+ if(!self.hiddenDuringWait && (options.show.ready || show)) {
+ self.toggle(TRUE, cache.event, FALSE);
+ }
+ self.hiddenDuringWait = FALSE;
+ });
+
+ // Expose API
+ QTIP.api[this.id] = this;
+
+ return this;
+};
+
+PROTOTYPE.destroy = function(immediate) {
+ // Set flag the signify destroy is taking place to plugins
+ // and ensure it only gets destroyed once!
+ if(this.destroyed) { return this.target; }
+
+ function process() {
+ if(this.destroyed) { return; }
+ this.destroyed = TRUE;
+
+ var target = this.target,
+ title = target.attr(oldtitle),
+ timer;
+
+ // Destroy tooltip if rendered
+ if(this.rendered) {
+ this.tooltip.stop(1,0).find('*').remove().end().remove();
+ }
+
+ // Destroy all plugins
+ $.each(this.plugins, function() {
+ this.destroy && this.destroy();
+ });
+
+ // Clear timers
+ for (timer in this.timers) {
+ if (this.timers.hasOwnProperty(timer)) {
+ clearTimeout(this.timers[timer]);
+ }
+ }
+
+ // Remove api object and ARIA attributes
+ target.removeData(NAMESPACE)
+ .removeAttr(ATTR_ID)
+ .removeAttr(ATTR_HAS)
+ .removeAttr('aria-describedby');
+
+ // Reset old title attribute if removed
+ if(this.options.suppress && title) {
+ target.attr('title', title).removeAttr(oldtitle);
+ }
+
+ // Remove qTip events associated with this API
+ this._unassignEvents();
+
+ // Remove ID from used id objects, and delete object references
+ // for better garbage collection and leak protection
+ this.options = this.elements = this.cache = this.timers =
+ this.plugins = this.mouse = NULL;
+
+ // Delete epoxsed API object
+ delete QTIP.api[this.id];
+ }
+
+ // If an immediate destroy is needed
+ if((immediate !== TRUE || this.triggering === 'hide') && this.rendered) {
+ this.tooltip.one('tooltiphidden', $.proxy(process, this));
+ !this.triggering && this.hide();
+ }
+
+ // If we're not in the process of hiding... process
+ else { process.call(this); }
+
+ return this.target;
+};
+;function invalidOpt(a) {
+ return a === NULL || $.type(a) !== 'object';
+}
+
+function invalidContent(c) {
+ return !($.isFunction(c) ||
+ c && c.attr ||
+ c.length ||
+ $.type(c) === 'object' && (c.jquery || c.then));
+}
+
+// Option object sanitizer
+function sanitizeOptions(opts) {
+ var content, text, ajax, once;
+
+ if(invalidOpt(opts)) { return FALSE; }
+
+ if(invalidOpt(opts.metadata)) {
+ opts.metadata = { type: opts.metadata };
+ }
+
+ if('content' in opts) {
+ content = opts.content;
+
+ if(invalidOpt(content) || content.jquery || content.done) {
+ text = invalidContent(content) ? FALSE : content;
+ content = opts.content = {
+ text: text
+ };
+ }
+ else { text = content.text; }
+
+ // DEPRECATED - Old content.ajax plugin functionality
+ // Converts it into the proper Deferred syntax
+ if('ajax' in content) {
+ ajax = content.ajax;
+ once = ajax && ajax.once !== FALSE;
+ delete content.ajax;
+
+ content.text = function(event, api) {
+ var loading = text || $(this).attr(api.options.content.attr) || 'Loading...',
+
+ deferred = $.ajax(
+ $.extend({}, ajax, { context: api })
+ )
+ .then(ajax.success, NULL, ajax.error)
+ .then(function(newContent) {
+ if(newContent && once) { api.set('content.text', newContent); }
+ return newContent;
+ },
+ function(xhr, status, error) {
+ if(api.destroyed || xhr.status === 0) { return; }
+ api.set('content.text', status + ': ' + error);
+ });
+
+ return !once ? (api.set('content.text', loading), deferred) : loading;
+ };
+ }
+
+ if('title' in content) {
+ if($.isPlainObject(content.title)) {
+ content.button = content.title.button;
+ content.title = content.title.text;
+ }
+
+ if(invalidContent(content.title || FALSE)) {
+ content.title = FALSE;
+ }
+ }
+ }
+
+ if('position' in opts && invalidOpt(opts.position)) {
+ opts.position = { my: opts.position, at: opts.position };
+ }
+
+ if('show' in opts && invalidOpt(opts.show)) {
+ opts.show = opts.show.jquery ? { target: opts.show } :
+ opts.show === TRUE ? { ready: TRUE } : { event: opts.show };
+ }
+
+ if('hide' in opts && invalidOpt(opts.hide)) {
+ opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide };
+ }
+
+ if('style' in opts && invalidOpt(opts.style)) {
+ opts.style = { classes: opts.style };
+ }
+
+ // Sanitize plugin options
+ $.each(PLUGINS, function() {
+ this.sanitize && this.sanitize(opts);
+ });
+
+ return opts;
+}
+
+// Setup builtin .set() option checks
+CHECKS = PROTOTYPE.checks = {
+ builtin: {
+ // Core checks
+ '^id$': function(obj, o, v, prev) {
+ var id = v === TRUE ? QTIP.nextid : v,
+ newId = NAMESPACE + '-' + id;
+
+ if(id !== FALSE && id.length > 0 && !$('#'+newId).length) {
+ this._id = newId;
+
+ if(this.rendered) {
+ this.tooltip[0].id = this._id;
+ this.elements.content[0].id = this._id + '-content';
+ this.elements.title[0].id = this._id + '-title';
+ }
+ }
+ else { obj[o] = prev; }
+ },
+ '^prerender': function(obj, o, v) {
+ v && !this.rendered && this.render(this.options.show.ready);
+ },
+
+ // Content checks
+ '^content.text$': function(obj, o, v) {
+ this._updateContent(v);
+ },
+ '^content.attr$': function(obj, o, v, prev) {
+ if(this.options.content.text === this.target.attr(prev)) {
+ this._updateContent( this.target.attr(v) );
+ }
+ },
+ '^content.title$': function(obj, o, v) {
+ // Remove title if content is null
+ if(!v) { return this._removeTitle(); }
+
+ // If title isn't already created, create it now and update
+ v && !this.elements.title && this._createTitle();
+ this._updateTitle(v);
+ },
+ '^content.button$': function(obj, o, v) {
+ this._updateButton(v);
+ },
+ '^content.title.(text|button)$': function(obj, o, v) {
+ this.set('content.'+o, v); // Backwards title.text/button compat
+ },
+
+ // Position checks
+ '^position.(my|at)$': function(obj, o, v){
+ if('string' === typeof v) {
+ this.position[o] = obj[o] = new CORNER(v, o === 'at');
+ }
+ },
+ '^position.container$': function(obj, o, v){
+ this.rendered && this.tooltip.appendTo(v);
+ },
+
+ // Show checks
+ '^show.ready$': function(obj, o, v) {
+ v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE));
+ },
+
+ // Style checks
+ '^style.classes$': function(obj, o, v, p) {
+ this.rendered && this.tooltip.removeClass(p).addClass(v);
+ },
+ '^style.(width|height)': function(obj, o, v) {
+ this.rendered && this.tooltip.css(o, v);
+ },
+ '^style.widget|content.title': function() {
+ this.rendered && this._setWidget();
+ },
+ '^style.def': function(obj, o, v) {
+ this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v);
+ },
+
+ // Events check
+ '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) {
+ this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v);
+ },
+
+ // Properties which require event reassignment
+ '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() {
+ if(!this.rendered) { return; }
+
+ // Set tracking flag
+ var posOptions = this.options.position;
+ this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);
+
+ // Reassign events
+ this._unassignEvents();
+ this._assignEvents();
+ }
+ }
+};
+
+// Dot notation converter
+function convertNotation(options, notation) {
+ var i = 0, obj, option = options,
+
+ // Split notation into array
+ levels = notation.split('.');
+
+ // Loop through
+ while(option = option[ levels[i++] ]) {
+ if(i < levels.length) { obj = option; }
+ }
+
+ return [obj || options, levels.pop()];
+}
+
+PROTOTYPE.get = function(notation) {
+ if(this.destroyed) { return this; }
+
+ var o = convertNotation(this.options, notation.toLowerCase()),
+ result = o[0][ o[1] ];
+
+ return result.precedance ? result.string() : result;
+};
+
+function setCallback(notation, args) {
+ var category, rule, match;
+
+ for(category in this.checks) {
+ if (!this.checks.hasOwnProperty(category)) { continue; }
+
+ for(rule in this.checks[category]) {
+ if (!this.checks[category].hasOwnProperty(rule)) { continue; }
+
+ if(match = (new RegExp(rule, 'i')).exec(notation)) {
+ args.push(match);
+
+ if(category === 'builtin' || this.plugins[category]) {
+ this.checks[category][rule].apply(
+ this.plugins[category] || this, args
+ );
+ }
+ }
+ }
+ }
+}
+
+var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,
+ rrender = /^prerender|show\.ready/i;
+
+PROTOTYPE.set = function(option, value) {
+ if(this.destroyed) { return this; }
+
+ var rendered = this.rendered,
+ reposition = FALSE,
+ options = this.options,
+ name;
+
+ // Convert singular option/value pair into object form
+ if('string' === typeof option) {
+ name = option; option = {}; option[name] = value;
+ }
+ else { option = $.extend({}, option); }
+
+ // Set all of the defined options to their new values
+ $.each(option, function(notation, val) {
+ if(rendered && rrender.test(notation)) {
+ delete option[notation]; return;
+ }
+
+ // Set new obj value
+ var obj = convertNotation(options, notation.toLowerCase()), previous;
+ previous = obj[0][ obj[1] ];
+ obj[0][ obj[1] ] = val && val.nodeType ? $(val) : val;
+
+ // Also check if we need to reposition
+ reposition = rmove.test(notation) || reposition;
+
+ // Set the new params for the callback
+ option[notation] = [obj[0], obj[1], val, previous];
+ });
+
+ // Re-sanitize options
+ sanitizeOptions(options);
+
+ /*
+ * Execute any valid callbacks for the set options
+ * Also set positioning flag so we don't get loads of redundant repositioning calls.
+ */
+ this.positioning = TRUE;
+ $.each(option, $.proxy(setCallback, this));
+ this.positioning = FALSE;
+
+ // Update position if needed
+ if(this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) {
+ this.reposition( options.position.target === 'mouse' ? NULL : this.cache.event );
+ }
+
+ return this;
+};
+;PROTOTYPE._update = function(content, element) {
+ var self = this,
+ cache = this.cache;
+
+ // Make sure tooltip is rendered and content is defined. If not return
+ if(!this.rendered || !content) { return FALSE; }
+
+ // Use function to parse content
+ if($.isFunction(content)) {
+ content = content.call(this.elements.target, cache.event, this) || '';
+ }
+
+ // Handle deferred content
+ if($.isFunction(content.then)) {
+ cache.waiting = TRUE;
+ return content.then(function(c) {
+ cache.waiting = FALSE;
+ return self._update(c, element);
+ }, NULL, function(e) {
+ return self._update(e, element);
+ });
+ }
+
+ // If content is null... return false
+ if(content === FALSE || !content && content !== '') { return FALSE; }
+
+ // Append new content if its a DOM array and show it if hidden
+ if(content.jquery && content.length > 0) {
+ element.empty().append(
+ content.css({ display: 'block', visibility: 'visible' })
+ );
+ }
+
+ // Content is a regular string, insert the new content
+ else { element.html(content); }
+
+ // Wait for content to be loaded, and reposition
+ return this._waitForContent(element).then(function(images) {
+ if(self.rendered && self.tooltip[0].offsetWidth > 0) {
+ self.reposition(cache.event, !images.length);
+ }
+ });
+};
+
+PROTOTYPE._waitForContent = function(element) {
+ var cache = this.cache;
+
+ // Set flag
+ cache.waiting = TRUE;
+
+ // If imagesLoaded is included, ensure images have loaded and return promise
+ return ( $.fn.imagesLoaded ? element.imagesLoaded() : new $.Deferred().resolve([]) )
+ .done(function() { cache.waiting = FALSE; })
+ .promise();
+};
+
+PROTOTYPE._updateContent = function(content, reposition) {
+ this._update(content, this.elements.content, reposition);
+};
+
+PROTOTYPE._updateTitle = function(content, reposition) {
+ if(this._update(content, this.elements.title, reposition) === FALSE) {
+ this._removeTitle(FALSE);
+ }
+};
+
+PROTOTYPE._createTitle = function()
+{
+ var elements = this.elements,
+ id = this._id+'-title';
+
+ // Destroy previous title element, if present
+ if(elements.titlebar) { this._removeTitle(); }
+
+ // Create title bar and title elements
+ elements.titlebar = $('<div />', {
+ 'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '')
+ })
+ .append(
+ elements.title = $('<div />', {
+ 'id': id,
+ 'class': NAMESPACE + '-title',
+ 'aria-atomic': TRUE
+ })
+ )
+ .insertBefore(elements.content)
+
+ // Button-specific events
+ .on('mousedown keydown mouseup keyup mouseout', '.qtip-close', function(event) {
+ $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
+ })
+ .on('mouseover mouseout', '.qtip-close', function(event){
+ $(this).toggleClass('ui-state-hover', event.type === 'mouseover');
+ });
+
+ // Create button if enabled
+ if(this.options.content.button) { this._createButton(); }
+};
+
+PROTOTYPE._removeTitle = function(reposition)
+{
+ var elements = this.elements;
+
+ if(elements.title) {
+ elements.titlebar.remove();
+ elements.titlebar = elements.title = elements.button = NULL;
+
+ // Reposition if enabled
+ if(reposition !== FALSE) { this.reposition(); }
+ }
+};
+;PROTOTYPE._createPosClass = function(my) {
+ return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev();
+};
+
+PROTOTYPE.reposition = function(event, effect) {
+ if(!this.rendered || this.positioning || this.destroyed) { return this; }
+
+ // Set positioning flag
+ this.positioning = TRUE;
+
+ var cache = this.cache,
+ tooltip = this.tooltip,
+ posOptions = this.options.position,
+ target = posOptions.target,
+ my = posOptions.my,
+ at = posOptions.at,
+ viewport = posOptions.viewport,
+ container = posOptions.container,
+ adjust = posOptions.adjust,
+ method = adjust.method.split(' '),
+ tooltipWidth = tooltip.outerWidth(FALSE),
+ tooltipHeight = tooltip.outerHeight(FALSE),
+ targetWidth = 0,
+ targetHeight = 0,
+ type = tooltip.css('position'),
+ position = { left: 0, top: 0 },
+ visible = tooltip[0].offsetWidth > 0,
+ isScroll = event && event.type === 'scroll',
+ win = $(window),
+ doc = container[0].ownerDocument,
+ mouse = this.mouse,
+ pluginCalculations, offset, adjusted, newClass;
+
+ // Check if absolute position was passed
+ if($.isArray(target) && target.length === 2) {
+ // Force left top and set position
+ at = { x: LEFT, y: TOP };
+ position = { left: target[0], top: target[1] };
+ }
+
+ // Check if mouse was the target
+ else if(target === 'mouse') {
+ // Force left top to allow flipping
+ at = { x: LEFT, y: TOP };
+
+ // Use the mouse origin that caused the show event, if distance hiding is enabled
+ if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) {
+ event = cache.origin;
+ }
+
+ // Use cached event for resize/scroll events
+ else if(!event || event && (event.type === 'resize' || event.type === 'scroll')) {
+ event = cache.event;
+ }
+
+ // Otherwise, use the cached mouse coordinates if available
+ else if(mouse && mouse.pageX) {
+ event = mouse;
+ }
+
+ // Calculate body and container offset and take them into account below
+ if(type !== 'static') { position = container.offset(); }
+ if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) {
+ offset = $(document.body).offset();
+ }
+
+ // Use event coordinates for position
+ position = {
+ left: event.pageX - position.left + (offset && offset.left || 0),
+ top: event.pageY - position.top + (offset && offset.top || 0)
+ };
+
+ // Scroll events are a pain, some browsers
+ if(adjust.mouse && isScroll && mouse) {
+ position.left -= (mouse.scrollX || 0) - win.scrollLeft();
+ position.top -= (mouse.scrollY || 0) - win.scrollTop();
+ }
+ }
+
+ // Target wasn't mouse or absolute...
+ else {
+ // Check if event targetting is being used
+ if(target === 'event') {
+ if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
+ cache.target = $(event.target);
+ }
+ else if(!event.target) {
+ cache.target = this.elements.target;
+ }
+ }
+ else if(target !== 'event'){
+ cache.target = $(target.jquery ? target : this.elements.target);
+ }
+ target = cache.target;
+
+ // Parse the target into a jQuery object and make sure there's an element present
+ target = $(target).eq(0);
+ if(target.length === 0) { return this; }
+
+ // Check if window or document is the target
+ else if(target[0] === document || target[0] === window) {
+ targetWidth = BROWSER.iOS ? window.innerWidth : target.width();
+ targetHeight = BROWSER.iOS ? window.innerHeight : target.height();
+
+ if(target[0] === window) {
+ position = {
+ top: (viewport || target).scrollTop(),
+ left: (viewport || target).scrollLeft()
+ };
+ }
+ }
+
+ // Check if the target is an <AREA> element
+ else if(PLUGINS.imagemap && target.is('area')) {
+ pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE);
+ }
+
+ // Check if the target is an SVG element
+ else if(PLUGINS.svg && target && target[0].ownerSVGElement) {
+ pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE);
+ }
+
+ // Otherwise use regular jQuery methods
+ else {
+ targetWidth = target.outerWidth(FALSE);
+ targetHeight = target.outerHeight(FALSE);
+ position = target.offset();
+ }
+
+ // Parse returned plugin values into proper variables
+ if(pluginCalculations) {
+ targetWidth = pluginCalculations.width;
+ targetHeight = pluginCalculations.height;
+ offset = pluginCalculations.offset;
+ position = pluginCalculations.position;
+ }
+
+ // Adjust position to take into account offset parents
+ position = this.reposition.offset(target, position, container);
+
+ // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2)
+ if(BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1 ||
+ BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33 ||
+ !BROWSER.iOS && type === 'fixed'
+ ){
+ position.left -= win.scrollLeft();
+ position.top -= win.scrollTop();
+ }
+
+ // Adjust position relative to target
+ if(!pluginCalculations || pluginCalculations && pluginCalculations.adjustable !== FALSE) {
+ position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0;
+ position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0;
+ }
+ }
+
+ // Adjust position relative to tooltip
+ position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0);
+ position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0);
+
+ // Use viewport adjustment plugin if enabled
+ if(PLUGINS.viewport) {
+ adjusted = position.adjusted = PLUGINS.viewport(
+ this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight
+ );
+
+ // Apply offsets supplied by positioning plugin (if used)
+ if(offset && adjusted.left) { position.left += offset.left; }
+ if(offset && adjusted.top) { position.top += offset.top; }
+
+ // Apply any new 'my' position
+ if(adjusted.my) { this.position.my = adjusted.my; }
+ }
+
+ // Viewport adjustment is disabled, set values to zero
+ else { position.adjusted = { left: 0, top: 0 }; }
+
+ // Set tooltip position class if it's changed
+ if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) {
+ cache.posClass = newClass;
+ tooltip.removeClass(cache.posClass).addClass(newClass);
+ }
+
+ // tooltipmove event
+ if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; }
+ delete position.adjusted;
+
+ // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
+ if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
+ tooltip.css(position);
+ }
+
+ // Use custom function if provided
+ else if($.isFunction(posOptions.effect)) {
+ posOptions.effect.call(tooltip, this, $.extend({}, position));
+ tooltip.queue(function(next) {
+ // Reset attributes to avoid cross-browser rendering bugs
+ $(this).css({ opacity: '', height: '' });
+ if(BROWSER.ie) { this.style.removeAttribute('filter'); }
+
+ next();
+ });
+ }
+
+ // Set positioning flag
+ this.positioning = FALSE;
+
+ return this;
+};
+
+// Custom (more correct for qTip!) offset calculator
+PROTOTYPE.reposition.offset = function(elem, pos, container) {
+ if(!container[0]) { return pos; }
+
+ var ownerDocument = $(elem[0].ownerDocument),
+ quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat',
+ parent = container[0],
+ scrolled, position, parentOffset, overflow;
+
+ function scroll(e, i) {
+ pos.left += i * e.scrollLeft();
+ pos.top += i * e.scrollTop();
+ }
+
+ // Compensate for non-static containers offset
+ do {
+ if((position = $.css(parent, 'position')) !== 'static') {
+ if(position === 'fixed') {
+ parentOffset = parent.getBoundingClientRect();
+ scroll(ownerDocument, -1);
+ }
+ else {
+ parentOffset = $(parent).position();
+ parentOffset.left += parseFloat($.css(parent, 'borderLeftWidth')) || 0;
+ parentOffset.top += parseFloat($.css(parent, 'borderTopWidth')) || 0;
+ }
+
+ pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0);
+ pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0);
+
+ // If this is the first parent element with an overflow of "scroll" or "auto", store it
+ if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); }
+ }
+ }
+ while(parent = parent.offsetParent);
+
+ // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode)
+ if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) {
+ scroll(scrolled, 1);
+ }
+
+ return pos;
+};
+
+// Corner class
+var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) {
+ corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase();
+ this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
+ this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
+ this.forceY = !!forceY;
+
+ var f = corner.charAt(0);
+ this.precedance = f === 't' || f === 'b' ? Y : X;
+}).prototype;
+
+C.invert = function(z, center) {
+ this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z];
+};
+
+C.string = function(join) {
+ var x = this.x, y = this.y;
+
+ var result = x !== y ?
+ x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ?
+ [y,x] :
+ [x,y] :
+ [x];
+
+ return join !== false ? result.join(' ') : result;
+};
+
+C.abbrev = function() {
+ var result = this.string(false);
+ return result[0].charAt(0) + (result[1] && result[1].charAt(0) || '');
+};
+
+C.clone = function() {
+ return new CORNER( this.string(), this.forceY );
+};
+
+;
+PROTOTYPE.toggle = function(state, event) {
+ var cache = this.cache,
+ options = this.options,
+ tooltip = this.tooltip;
+
+ // Try to prevent flickering when tooltip overlaps show element
+ if(event) {
+ if((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) &&
+ options.show.target.add(event.target).length === options.show.target.length &&
+ tooltip.has(event.relatedTarget).length) {
+ return this;
+ }
+
+ // Cache event
+ cache.event = $.event.fix(event);
+ }
+
+ // If we're currently waiting and we've just hidden... stop it
+ this.waiting && !state && (this.hiddenDuringWait = TRUE);
+
+ // Render the tooltip if showing and it isn't already
+ if(!this.rendered) { return state ? this.render(1) : this; }
+ else if(this.destroyed || this.disabled) { return this; }
+
+ var type = state ? 'show' : 'hide',
+ opts = this.options[type],
+ posOptions = this.options.position,
+ contentOptions = this.options.content,
+ width = this.tooltip.css('width'),
+ visible = this.tooltip.is(':visible'),
+ animate = state || opts.target.length === 1,
+ sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
+ identicalState, allow, after;
+
+ // Detect state if valid one isn't provided
+ if((typeof state).search('boolean|number')) { state = !visible; }
+
+ // Check if the tooltip is in an identical state to the new would-be state
+ identicalState = !tooltip.is(':animated') && visible === state && sameTarget;
+
+ // Fire tooltip(show/hide) event and check if destroyed
+ allow = !identicalState ? !!this._trigger(type, [90]) : NULL;
+
+ // Check to make sure the tooltip wasn't destroyed in the callback
+ if(this.destroyed) { return this; }
+
+ // If the user didn't stop the method prematurely and we're showing the tooltip, focus it
+ if(allow !== FALSE && state) { this.focus(event); }
+
+ // If the state hasn't changed or the user stopped it, return early
+ if(!allow || identicalState) { return this; }
+
+ // Set ARIA hidden attribute
+ $.attr(tooltip[0], 'aria-hidden', !!!state);
+
+ // Execute state specific properties
+ if(state) {
+ // Store show origin coordinates
+ this.mouse && (cache.origin = $.event.fix(this.mouse));
+
+ // Update tooltip content & title if it's a dynamic function
+ if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
+ if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }
+
+ // Cache mousemove events for positioning purposes (if not already tracking)
+ if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
+ $(document).on('mousemove.'+NAMESPACE, this._storeMouse);
+ trackingBound = TRUE;
+ }
+
+ // Update the tooltip position (set width first to prevent viewport/max-width issues)
+ if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
+ this.reposition(event, arguments[2]);
+ if(!width) { tooltip.css('width', ''); }
+
+ // Hide other tooltips if tooltip is solo
+ if(!!opts.solo) {
+ (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
+ .not(tooltip).not(opts.target).qtip('hide', new $.Event('tooltipsolo'));
+ }
+ }
+ else {
+ // Clear show timer if we're hiding
+ clearTimeout(this.timers.show);
+
+ // Remove cached origin on hide
+ delete cache.origin;
+
+ // Remove mouse tracking event if not needed (all tracking qTips are hidden)
+ if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
+ $(document).off('mousemove.'+NAMESPACE);
+ trackingBound = FALSE;
+ }
+
+ // Blur the tooltip
+ this.blur(event);
+ }
+
+ // Define post-animation, state specific properties
+ after = $.proxy(function() {
+ if(state) {
+ // Prevent antialias from disappearing in IE by removing filter
+ if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }
+
+ // Remove overflow setting to prevent tip bugs
+ tooltip.css('overflow', '');
+
+ // Autofocus elements if enabled
+ if('string' === typeof opts.autofocus) {
+ $(this.options.show.autofocus, tooltip).focus();
+ }
+
+ // If set, hide tooltip when inactive for delay period
+ this.options.show.target.trigger('qtip-'+this.id+'-inactive');
+ }
+ else {
+ // Reset CSS states
+ tooltip.css({
+ display: '',
+ visibility: '',
+ opacity: '',
+ left: '',
+ top: ''
+ });
+ }
+
+ // tooltipvisible/tooltiphidden events
+ this._trigger(state ? 'visible' : 'hidden');
+ }, this);
+
+ // If no effect type is supplied, use a simple toggle
+ if(opts.effect === FALSE || animate === FALSE) {
+ tooltip[ type ]();
+ after();
+ }
+
+ // Use custom function if provided
+ else if($.isFunction(opts.effect)) {
+ tooltip.stop(1, 1);
+ opts.effect.call(tooltip, this);
+ tooltip.queue('fx', function(n) {
+ after(); n();
+ });
+ }
+
+ // Use basic fade function by default
+ else { tooltip.fadeTo(90, state ? 1 : 0, after); }
+
+ // If inactive hide method is set, active it
+ if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); }
+
+ return this;
+};
+
+PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); };
+
+PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); };
+;PROTOTYPE.focus = function(event) {
+ if(!this.rendered || this.destroyed) { return this; }
+
+ var qtips = $(SELECTOR),
+ tooltip = this.tooltip,
+ curIndex = parseInt(tooltip[0].style.zIndex, 10),
+ newIndex = QTIP.zindex + qtips.length;
+
+ // Only update the z-index if it has changed and tooltip is not already focused
+ if(!tooltip.hasClass(CLASS_FOCUS)) {
+ // tooltipfocus event
+ if(this._trigger('focus', [newIndex], event)) {
+ // Only update z-index's if they've changed
+ if(curIndex !== newIndex) {
+ // Reduce our z-index's and keep them properly ordered
+ qtips.each(function() {
+ if(this.style.zIndex > curIndex) {
+ this.style.zIndex = this.style.zIndex - 1;
+ }
+ });
+
+ // Fire blur event for focused tooltip
+ qtips.filter('.' + CLASS_FOCUS).qtip('blur', event);
+ }
+
+ // Set the new z-index
+ tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
+ }
+ }
+
+ return this;
+};
+
+PROTOTYPE.blur = function(event) {
+ if(!this.rendered || this.destroyed) { return this; }
+
+ // Set focused status to FALSE
+ this.tooltip.removeClass(CLASS_FOCUS);
+
+ // tooltipblur event
+ this._trigger('blur', [ this.tooltip.css('zIndex') ], event);
+
+ return this;
+};
+;PROTOTYPE.disable = function(state) {
+ if(this.destroyed) { return this; }
+
+ // If 'toggle' is passed, toggle the current state
+ if(state === 'toggle') {
+ state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled);
+ }
+
+ // Disable if no state passed
+ else if('boolean' !== typeof state) {
+ state = TRUE;
+ }
+
+ if(this.rendered) {
+ this.tooltip.toggleClass(CLASS_DISABLED, state)
+ .attr('aria-disabled', state);
+ }
+
+ this.disabled = !!state;
+
+ return this;
+};
+
+PROTOTYPE.enable = function() { return this.disable(FALSE); };
+;PROTOTYPE._createButton = function()
+{
+ var self = this,
+ elements = this.elements,
+ tooltip = elements.tooltip,
+ button = this.options.content.button,
+ isString = typeof button === 'string',
+ close = isString ? button : 'Close tooltip';
+
+ if(elements.button) { elements.button.remove(); }
+
+ // Use custom button if one was supplied by user, else use default
+ if(button.jquery) {
+ elements.button = button;
+ }
+ else {
+ elements.button = $('<a />', {
+ 'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE+'-icon'),
+ 'title': close,
+ 'aria-label': close
+ })
+ .prepend(
+ $('<span />', {
+ 'class': 'ui-icon ui-icon-close',
+ 'html': '&times;'
+ })
+ );
+ }
+
+ // Create button and setup attributes
+ elements.button.appendTo(elements.titlebar || tooltip)
+ .attr('role', 'button')
+ .click(function(event) {
+ if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); }
+ return FALSE;
+ });
+};
+
+PROTOTYPE._updateButton = function(button)
+{
+ // Make sure tooltip is rendered and if not, return
+ if(!this.rendered) { return FALSE; }
+
+ var elem = this.elements.button;
+ if(button) { this._createButton(); }
+ else { elem.remove(); }
+};
+;// Widget class creator
+function createWidgetClass(cls) {
+ return WIDGET.concat('').join(cls ? '-'+cls+' ' : ' ');
+}
+
+// Widget class setter method
+PROTOTYPE._setWidget = function()
+{
+ var on = this.options.style.widget,
+ elements = this.elements,
+ tooltip = elements.tooltip,
+ disabled = tooltip.hasClass(CLASS_DISABLED);
+
+ tooltip.removeClass(CLASS_DISABLED);
+ CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled';
+ tooltip.toggleClass(CLASS_DISABLED, disabled);
+
+ tooltip.toggleClass('ui-helper-reset '+createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on);
+
+ if(elements.content) {
+ elements.content.toggleClass( createWidgetClass('content'), on);
+ }
+ if(elements.titlebar) {
+ elements.titlebar.toggleClass( createWidgetClass('header'), on);
+ }
+ if(elements.button) {
+ elements.button.toggleClass(NAMESPACE+'-icon', !on);
+ }
+};
+;function delay(callback, duration) {
+ // If tooltip has displayed, start hide timer
+ if(duration > 0) {
+ return setTimeout(
+ $.proxy(callback, this), duration
+ );
+ }
+ else{ callback.call(this); }
+}
+
+function showMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED)) { return; }
+
+ // Clear hide timers
+ clearTimeout(this.timers.show);
+ clearTimeout(this.timers.hide);
+
+ // Start show timer
+ this.timers.show = delay.call(this,
+ function() { this.toggle(TRUE, event); },
+ this.options.show.delay
+ );
+}
+
+function hideMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; }
+
+ // Check if new target was actually the tooltip element
+ var relatedTarget = $(event.relatedTarget),
+ ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0],
+ ontoTarget = relatedTarget[0] === this.options.show.target[0];
+
+ // Clear timers and stop animation queue
+ clearTimeout(this.timers.show);
+ clearTimeout(this.timers.hide);
+
+ // Prevent hiding if tooltip is fixed and event target is the tooltip.
+ // Or if mouse positioning is enabled and cursor momentarily overlaps
+ if(this !== relatedTarget[0] &&
+ (this.options.position.target === 'mouse' && ontoTooltip) ||
+ this.options.hide.fixed && (
+ (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget))
+ )
+ {
+ /* eslint-disable no-empty */
+ try {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ } catch(e) {}
+ /* eslint-enable no-empty */
+
+ return;
+ }
+
+ // If tooltip has displayed, start hide timer
+ this.timers.hide = delay.call(this,
+ function() { this.toggle(FALSE, event); },
+ this.options.hide.delay,
+ this
+ );
+}
+
+function inactiveMethod(event) {
+ if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; }
+
+ // Clear timer
+ clearTimeout(this.timers.inactive);
+
+ this.timers.inactive = delay.call(this,
+ function(){ this.hide(event); },
+ this.options.hide.inactive
+ );
+}
+
+function repositionMethod(event) {
+ if(this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); }
+}
+
+// Store mouse coordinates
+PROTOTYPE._storeMouse = function(event) {
+ (this.mouse = $.event.fix(event)).type = 'mousemove';
+ return this;
+};
+
+// Bind events
+PROTOTYPE._bind = function(targets, events, method, suffix, context) {
+ if(!targets || !method || !events.length) { return; }
+ var ns = '.' + this._id + (suffix ? '-'+suffix : '');
+ $(targets).on(
+ (events.split ? events : events.join(ns + ' ')) + ns,
+ $.proxy(method, context || this)
+ );
+ return this;
+};
+PROTOTYPE._unbind = function(targets, suffix) {
+ targets && $(targets).off('.' + this._id + (suffix ? '-'+suffix : ''));
+ return this;
+};
+
+// Global delegation helper
+function delegate(selector, events, method) {
+ $(document.body).on(
+ (events.split ? events : events.join('.'+NAMESPACE + ' ')) + '.'+NAMESPACE,
+ selector,
+ function() {
+ var api = QTIP.api[ $.attr(this, ATTR_ID) ];
+ api && !api.disabled && method.apply(api, arguments);
+ }
+ );
+}
+// Event trigger
+PROTOTYPE._trigger = function(type, args, event) {
+ var callback = new $.Event('tooltip'+type);
+ callback.originalEvent = event && $.extend({}, event) || this.cache.event || NULL;
+
+ this.triggering = type;
+ this.tooltip.trigger(callback, [this].concat(args || []));
+ this.triggering = FALSE;
+
+ return !callback.isDefaultPrevented();
+};
+
+PROTOTYPE._bindEvents = function(showEvents, hideEvents, showTargets, hideTargets, showCallback, hideCallback) {
+ // Get tasrgets that lye within both
+ var similarTargets = showTargets.filter( hideTargets ).add( hideTargets.filter(showTargets) ),
+ toggleEvents = [];
+
+ // If hide and show targets are the same...
+ if(similarTargets.length) {
+
+ // Filter identical show/hide events
+ $.each(hideEvents, function(i, type) {
+ var showIndex = $.inArray(type, showEvents);
+
+ // Both events are identical, remove from both hide and show events
+ // and append to toggleEvents
+ showIndex > -1 && toggleEvents.push( showEvents.splice( showIndex, 1 )[0] );
+ });
+
+ // Toggle events are special case of identical show/hide events, which happen in sequence
+ if(toggleEvents.length) {
+ // Bind toggle events to the similar targets
+ this._bind(similarTargets, toggleEvents, function(event) {
+ var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false;
+ (state ? hideCallback : showCallback).call(this, event);
+ });
+
+ // Remove the similar targets from the regular show/hide bindings
+ showTargets = showTargets.not(similarTargets);
+ hideTargets = hideTargets.not(similarTargets);
+ }
+ }
+
+ // Apply show/hide/toggle events
+ this._bind(showTargets, showEvents, showCallback);
+ this._bind(hideTargets, hideEvents, hideCallback);
+};
+
+PROTOTYPE._assignInitialEvents = function(event) {
+ var options = this.options,
+ showTarget = options.show.target,
+ hideTarget = options.hide.target,
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
+
+ // Catch remove/removeqtip events on target element to destroy redundant tooltips
+ this._bind(this.elements.target, ['remove', 'removeqtip'], function() {
+ this.destroy(true);
+ }, 'destroy');
+
+ /*
+ * Make sure hoverIntent functions properly by using mouseleave as a hide event if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
+ if(/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) {
+ hideEvents.push('mouseleave');
+ }
+
+ /*
+ * Also make sure initial mouse targetting works correctly by caching mousemove coords
+ * on show targets before the tooltip has rendered. Also set onTarget when triggered to
+ * keep mouse tracking working.
+ */
+ this._bind(showTarget, 'mousemove', function(moveEvent) {
+ this._storeMouse(moveEvent);
+ this.cache.onTarget = TRUE;
+ });
+
+ // Define hoverIntent function
+ function hoverIntent(hoverEvent) {
+ // Only continue if tooltip isn't disabled
+ if(this.disabled || this.destroyed) { return FALSE; }
+
+ // Cache the event data
+ this.cache.event = hoverEvent && $.event.fix(hoverEvent);
+ this.cache.target = hoverEvent && $(hoverEvent.target);
+
+ // Start the event sequence
+ clearTimeout(this.timers.show);
+ this.timers.show = delay.call(this,
+ function() { this.render(typeof hoverEvent === 'object' || options.show.ready); },
+ options.prerender ? 0 : options.show.delay
+ );
+ }
+
+ // Filter and bind events
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function() {
+ if(!this.timers) { return FALSE; }
+ clearTimeout(this.timers.show);
+ });
+
+ // Prerendering is enabled, create tooltip now
+ if(options.show.ready || options.prerender) { hoverIntent.call(this, event); }
+};
+
+// Event assignment method
+PROTOTYPE._assignEvents = function() {
+ var self = this,
+ options = this.options,
+ posOptions = options.position,
+
+ tooltip = this.tooltip,
+ showTarget = options.show.target,
+ hideTarget = options.hide.target,
+ containerTarget = posOptions.container,
+ viewportTarget = posOptions.viewport,
+ documentTarget = $(document),
+ windowTarget = $(window),
+
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
+
+
+ // Assign passed event callbacks
+ $.each(options.events, function(name, callback) {
+ self._bind(tooltip, name === 'toggle' ? ['tooltipshow','tooltiphide'] : ['tooltip'+name], callback, null, tooltip);
+ });
+
+ // Hide tooltips when leaving current window/frame (but not select/option elements)
+ if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') {
+ this._bind(documentTarget, ['mouseout', 'blur'], function(event) {
+ if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) {
+ this.hide(event);
+ }
+ });
+ }
+
+ // Enable hide.fixed by adding appropriate class
+ if(options.hide.fixed) {
+ hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) );
+ }
+
+ /*
+ * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
+ else if(/mouse(over|enter)/i.test(options.show.event)) {
+ this._bind(hideTarget, 'mouseleave', function() {
+ clearTimeout(this.timers.show);
+ });
+ }
+
+ // Hide tooltip on document mousedown if unfocus events are enabled
+ if(('' + options.hide.event).indexOf('unfocus') > -1) {
+ this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) {
+ var elem = $(event.target),
+ enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0,
+ isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0;
+
+ if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor &&
+ !this.target.has(elem[0]).length && enabled
+ ) {
+ this.hide(event);
+ }
+ });
+ }
+
+ // Check if the tooltip hides when inactive
+ if('number' === typeof options.hide.inactive) {
+ // Bind inactive method to show target(s) as a custom event
+ this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod, 'inactive');
+
+ // Define events which reset the 'inactive' event handler
+ this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod);
+ }
+
+ // Filter and bind events
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod);
+
+ // Mouse movement bindings
+ this._bind(showTarget.add(tooltip), 'mousemove', function(event) {
+ // Check if the tooltip hides when mouse is moved a certain distance
+ if('number' === typeof options.hide.distance) {
+ var origin = this.cache.origin || {},
+ limit = this.options.hide.distance,
+ abs = Math.abs;
+
+ // Check if the movement has gone beyond the limit, and hide it if so
+ if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
+ this.hide(event);
+ }
+ }
+
+ // Cache mousemove coords on show targets
+ this._storeMouse(event);
+ });
+
+ // Mouse positioning events
+ if(posOptions.target === 'mouse') {
+ // If mouse adjustment is on...
+ if(posOptions.adjust.mouse) {
+ // Apply a mouseleave event so we don't get problems with overlapping
+ if(options.hide.event) {
+ // Track if we're on the target or not
+ this._bind(showTarget, ['mouseenter', 'mouseleave'], function(event) {
+ if(!this.cache) {return FALSE; }
+ this.cache.onTarget = event.type === 'mouseenter';
+ });
+ }
+
+ // Update tooltip position on mousemove
+ this._bind(documentTarget, 'mousemove', function(event) {
+ // Update the tooltip position only if the tooltip is visible and adjustment is enabled
+ if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) {
+ this.reposition(event);
+ }
+ });
+ }
+ }
+
+ // Adjust positions of the tooltip on window resize if enabled
+ if(posOptions.adjust.resize || viewportTarget.length) {
+ this._bind( $.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod );
+ }
+
+ // Adjust tooltip position on scroll of the window or viewport element if present
+ if(posOptions.adjust.scroll) {
+ this._bind( windowTarget.add(posOptions.container), 'scroll', repositionMethod );
+ }
+};
+
+// Un-assignment method
+PROTOTYPE._unassignEvents = function() {
+ var options = this.options,
+ showTargets = options.show.target,
+ hideTargets = options.hide.target,
+ targets = $.grep([
+ this.elements.target[0],
+ this.rendered && this.tooltip[0],
+ options.position.container[0],
+ options.position.viewport[0],
+ options.position.container.closest('html')[0], // unfocus
+ window,
+ document
+ ], function(i) {
+ return typeof i === 'object';
+ });
+
+ // Add show and hide targets if they're valid
+ if(showTargets && showTargets.toArray) {
+ targets = targets.concat(showTargets.toArray());
+ }
+ if(hideTargets && hideTargets.toArray) {
+ targets = targets.concat(hideTargets.toArray());
+ }
+
+ // Unbind the events
+ this._unbind(targets)
+ ._unbind(targets, 'destroy')
+ ._unbind(targets, 'inactive');
+};
+
+// Apply common event handlers using delegate (avoids excessive .on calls!)
+$(function() {
+ delegate(SELECTOR, ['mouseenter', 'mouseleave'], function(event) {
+ var state = event.type === 'mouseenter',
+ tooltip = $(event.currentTarget),
+ target = $(event.relatedTarget || event.target),
+ options = this.options;
+
+ // On mouseenter...
+ if(state) {
+ // Focus the tooltip on mouseenter (z-index stacking)
+ this.focus(event);
+
+ // Clear hide timer on tooltip hover to prevent it from closing
+ tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide);
+ }
+
+ // On mouseleave...
+ else {
+ // When mouse tracking is enabled, hide when we leave the tooltip and not onto the show target (if a hide event is set)
+ if(options.position.target === 'mouse' && options.position.adjust.mouse &&
+ options.hide.event && options.show.target && !target.closest(options.show.target[0]).length) {
+ this.hide(event);
+ }
+ }
+
+ // Add hover class
+ tooltip.toggleClass(CLASS_HOVER, state);
+ });
+
+ // Define events which reset the 'inactive' event handler
+ delegate('['+ATTR_ID+']', INACTIVE_EVENTS, inactiveMethod);
+});
+;// Initialization method
+function init(elem, id, opts) {
+ var obj, posOptions, attr, config, title,
+
+ // Setup element references
+ docBody = $(document.body),
+
+ // Use document body instead of document element if needed
+ newTarget = elem[0] === document ? docBody : elem,
+
+ // Grab metadata from element if plugin is present
+ metadata = elem.metadata ? elem.metadata(opts.metadata) : NULL,
+
+ // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise
+ metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL,
+
+ // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method,
+ html5 = elem.data(opts.metadata.name || 'qtipopts');
+
+ // If we don't get an object returned attempt to parse it manualyl without parseJSON
+ /* eslint-disable no-empty */
+ try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; }
+ catch(e) {}
+ /* eslint-enable no-empty */
+
+ // Merge in and sanitize metadata
+ config = $.extend(TRUE, {}, QTIP.defaults, opts,
+ typeof html5 === 'object' ? sanitizeOptions(html5) : NULL,
+ sanitizeOptions(metadata5 || metadata));
+
+ // Re-grab our positioning options now we've merged our metadata and set id to passed value
+ posOptions = config.position;
+ config.id = id;
+
+ // Setup missing content if none is detected
+ if('boolean' === typeof config.content.text) {
+ attr = elem.attr(config.content.attr);
+
+ // Grab from supplied attribute if available
+ if(config.content.attr !== FALSE && attr) { config.content.text = attr; }
+
+ // No valid content was found, abort render
+ else { return FALSE; }
+ }
+
+ // Setup target options
+ if(!posOptions.container.length) { posOptions.container = docBody; }
+ if(posOptions.target === FALSE) { posOptions.target = newTarget; }
+ if(config.show.target === FALSE) { config.show.target = newTarget; }
+ if(config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); }
+ if(config.hide.target === FALSE) { config.hide.target = newTarget; }
+ if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; }
+
+ // Ensure we only use a single container
+ posOptions.container = posOptions.container.eq(0);
+
+ // Convert position corner values into x and y strings
+ posOptions.at = new CORNER(posOptions.at, TRUE);
+ posOptions.my = new CORNER(posOptions.my);
+
+ // Destroy previous tooltip if overwrite is enabled, or skip element if not
+ if(elem.data(NAMESPACE)) {
+ if(config.overwrite) {
+ elem.qtip('destroy', true);
+ }
+ else if(config.overwrite === FALSE) {
+ return FALSE;
+ }
+ }
+
+ // Add has-qtip attribute
+ elem.attr(ATTR_HAS, id);
+
+ // Remove title attribute and store it if present
+ if(config.suppress && (title = elem.attr('title'))) {
+ // Final attr call fixes event delegatiom and IE default tooltip showing problem
+ elem.removeAttr('title').attr(oldtitle, title).attr('title', '');
+ }
+
+ // Initialize the tooltip and add API reference
+ obj = new QTip(elem, config, id, !!attr);
+ elem.data(NAMESPACE, obj);
+
+ return obj;
+}
+
+// jQuery $.fn extension method
+QTIP = $.fn.qtip = function(options, notation, newValue)
+{
+ var command = ('' + options).toLowerCase(), // Parse command
+ returned = NULL,
+ args = $.makeArray(arguments).slice(1),
+ event = args[args.length - 1],
+ opts = this[0] ? $.data(this[0], NAMESPACE) : NULL;
+
+ // Check for API request
+ if(!arguments.length && opts || command === 'api') {
+ return opts;
+ }
+
+ // Execute API command if present
+ else if('string' === typeof options) {
+ this.each(function() {
+ var api = $.data(this, NAMESPACE);
+ if(!api) { return TRUE; }
+
+ // Cache the event if possible
+ if(event && event.timeStamp) { api.cache.event = event; }
+
+ // Check for specific API commands
+ if(notation && (command === 'option' || command === 'options')) {
+ if(newValue !== undefined || $.isPlainObject(notation)) {
+ api.set(notation, newValue);
+ }
+ else {
+ returned = api.get(notation);
+ return FALSE;
+ }
+ }
+
+ // Execute API command
+ else if(api[command]) {
+ api[command].apply(api, args);
+ }
+ });
+
+ return returned !== NULL ? returned : this;
+ }
+
+ // No API commands. validate provided options and setup qTips
+ else if('object' === typeof options || !arguments.length) {
+ // Sanitize options first
+ opts = sanitizeOptions($.extend(TRUE, {}, options));
+
+ return this.each(function(i) {
+ var api, id;
+
+ // Find next available ID, or use custom ID if provided
+ id = $.isArray(opts.id) ? opts.id[i] : opts.id;
+ id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id;
+
+ // Initialize the qTip and re-grab newly sanitized options
+ api = init($(this), id, opts);
+ if(api === FALSE) { return TRUE; }
+ else { QTIP.api[id] = api; }
+
+ // Initialize plugins
+ $.each(PLUGINS, function() {
+ if(this.initialize === 'initialize') { this(api); }
+ });
+
+ // Assign initial pre-render events
+ api._assignInitialEvents(event);
+ });
+ }
+};
+
+// Expose class
+$.qtip = QTip;
+
+// Populated in render method
+QTIP.api = {};
+;$.each({
+ /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
+ attr: function(attr, val) {
+ if(this.length) {
+ var self = this[0],
+ title = 'title',
+ api = $.data(self, 'qtip');
+
+ if(attr === title && api && api.options && 'object' === typeof api && 'object' === typeof api.options && api.options.suppress) {
+ if(arguments.length < 2) {
+ return $.attr(self, oldtitle);
+ }
+
+ // If qTip is rendered and title was originally used as content, update it
+ if(api && api.options.content.attr === title && api.cache.attr) {
+ api.set('content.text', val);
+ }
+
+ // Use the regular attr method to set, then cache the result
+ return this.attr(oldtitle, val);
+ }
+ }
+
+ return $.fn['attr'+replaceSuffix].apply(this, arguments);
+ },
+
+ /* Allow clone to correctly retrieve cached title attributes */
+ clone: function(keepData) {
+ // Clone our element using the real clone method
+ var elems = $.fn['clone'+replaceSuffix].apply(this, arguments);
+
+ // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false
+ if(!keepData) {
+ elems.filter('['+oldtitle+']').attr('title', function() {
+ return $.attr(this, oldtitle);
+ })
+ .removeAttr(oldtitle);
+ }
+
+ return elems;
+ }
+}, function(name, func) {
+ if(!func || $.fn[name+replaceSuffix]) { return TRUE; }
+
+ var old = $.fn[name+replaceSuffix] = $.fn[name];
+ $.fn[name] = function() {
+ return func.apply(this, arguments) || old.apply(this, arguments);
+ };
+});
+
+/* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar).
+ * This snippet is taken directly from jQuery UI source code found here:
+ * http://code.jquery.com/ui/jquery-ui-git.js
+ */
+if(!$.ui) {
+ $['cleanData'+replaceSuffix] = $.cleanData;
+ $.cleanData = function( elems ) {
+ for(var i = 0, elem; (elem = $( elems[i] )).length; i++) {
+ if(elem.attr(ATTR_HAS)) {
+ /* eslint-disable no-empty */
+ try { elem.triggerHandler('removeqtip'); }
+ catch( e ) {}
+ /* eslint-enable no-empty */
+ }
+ }
+ $['cleanData'+replaceSuffix].apply(this, arguments);
+ };
+}
+;// qTip version
+QTIP.version = '3.0.3';
+
+// Base ID for all qTips
+QTIP.nextid = 0;
+
+// Inactive events array
+QTIP.inactiveEvents = INACTIVE_EVENTS;
+
+// Base z-index for all qTips
+QTIP.zindex = 15000;
+
+// Define configuration defaults
+QTIP.defaults = {
+ prerender: FALSE,
+ id: FALSE,
+ overwrite: TRUE,
+ suppress: TRUE,
+ content: {
+ text: TRUE,
+ attr: 'title',
+ title: FALSE,
+ button: FALSE
+ },
+ position: {
+ my: 'top left',
+ at: 'bottom right',
+ target: FALSE,
+ container: FALSE,
+ viewport: FALSE,
+ adjust: {
+ x: 0, y: 0,
+ mouse: TRUE,
+ scroll: TRUE,
+ resize: TRUE,
+ method: 'flipinvert flipinvert'
+ },
+ effect: function(api, pos) {
+ $(this).animate(pos, {
+ duration: 200,
+ queue: FALSE
+ });
+ }
+ },
+ show: {
+ target: FALSE,
+ event: 'mouseenter',
+ effect: TRUE,
+ delay: 90,
+ solo: FALSE,
+ ready: FALSE,
+ autofocus: FALSE
+ },
+ hide: {
+ target: FALSE,
+ event: 'mouseleave',
+ effect: TRUE,
+ delay: 0,
+ fixed: FALSE,
+ inactive: FALSE,
+ leave: 'window',
+ distance: FALSE
+ },
+ style: {
+ classes: '',
+ widget: FALSE,
+ width: FALSE,
+ height: FALSE,
+ def: TRUE
+ },
+ events: {
+ render: NULL,
+ move: NULL,
+ show: NULL,
+ hide: NULL,
+ toggle: NULL,
+ visible: NULL,
+ hidden: NULL,
+ focus: NULL,
+ blur: NULL
+ }
+};
+;var TIP,
+createVML,
+SCALE,
+PIXEL_RATIO,
+BACKING_STORE_RATIO,
+
+// Common CSS strings
+MARGIN = 'margin',
+BORDER = 'border',
+COLOR = 'color',
+BG_COLOR = 'background-color',
+TRANSPARENT = 'transparent',
+IMPORTANT = ' !important',
+
+// Check if the browser supports <canvas/> elements
+HASCANVAS = !!document.createElement('canvas').getContext,
+
+// Invalid colour values used in parseColours()
+INVALID = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i;
+
+// Camel-case method, taken from jQuery source
+// http://code.jquery.com/jquery-1.8.0.js
+function camel(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
+
+/*
+ * Modified from Modernizr's testPropsAll()
+ * http://modernizr.com/downloads/modernizr-latest.js
+ */
+var cssProps = {}, cssPrefixes = ['Webkit', 'O', 'Moz', 'ms'];
+function vendorCss(elem, prop) {
+ var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
+ props = (prop + ' ' + cssPrefixes.join(ucProp + ' ') + ucProp).split(' '),
+ cur, val, i = 0;
+
+ // If the property has already been mapped...
+ if(cssProps[prop]) { return elem.css(cssProps[prop]); }
+
+ while(cur = props[i++]) {
+ if((val = elem.css(cur)) !== undefined) {
+ cssProps[prop] = cur;
+ return val;
+ }
+ }
+}
+
+// Parse a given elements CSS property into an int
+function intCss(elem, prop) {
+ return Math.ceil(parseFloat(vendorCss(elem, prop)));
+}
+
+
+// VML creation (for IE only)
+if(!HASCANVAS) {
+ createVML = function(tag, props, style) {
+ return '<qtipvml:'+tag+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(props||'')+
+ ' style="behavior: url(#default#VML); '+(style||'')+ '" />';
+ };
+}
+
+// Canvas only definitions
+else {
+ PIXEL_RATIO = window.devicePixelRatio || 1;
+ BACKING_STORE_RATIO = (function() {
+ var context = document.createElement('canvas').getContext('2d');
+ return context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio ||
+ context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || 1;
+ })();
+ SCALE = PIXEL_RATIO / BACKING_STORE_RATIO;
+}
+
+
+function Tip(qtip, options) {
+ this._ns = 'tip';
+ this.options = options;
+ this.offset = options.offset;
+ this.size = [ options.width, options.height ];
+
+ // Initialize
+ this.qtip = qtip;
+ this.init(qtip);
+}
+
+$.extend(Tip.prototype, {
+ init: function(qtip) {
+ var context, tip;
+
+ // Create tip element and prepend to the tooltip
+ tip = this.element = qtip.elements.tip = $('<div />', { 'class': NAMESPACE+'-tip' }).prependTo(qtip.tooltip);
+
+ // Create tip drawing element(s)
+ if(HASCANVAS) {
+ // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
+ context = $('<canvas />').appendTo(this.element)[0].getContext('2d');
+
+ // Setup constant parameters
+ context.lineJoin = 'miter';
+ context.miterLimit = 100000;
+ context.save();
+ }
+ else {
+ context = createVML('shape', 'coordorigin="0,0"', 'position:absolute;');
+ this.element.html(context + context);
+
+ // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML
+ qtip._bind( $('*', tip).add(tip), ['click', 'mousedown'], function(event) { event.stopPropagation(); }, this._ns);
+ }
+
+ // Bind update events
+ qtip._bind(qtip.tooltip, 'tooltipmove', this.reposition, this._ns, this);
+
+ // Create it
+ this.create();
+ },
+
+ _swapDimensions: function() {
+ this.size[0] = this.options.height;
+ this.size[1] = this.options.width;
+ },
+ _resetDimensions: function() {
+ this.size[0] = this.options.width;
+ this.size[1] = this.options.height;
+ },
+
+ _useTitle: function(corner) {
+ var titlebar = this.qtip.elements.titlebar;
+ return titlebar && (
+ corner.y === TOP || corner.y === CENTER && this.element.position().top + this.size[1] / 2 + this.options.offset < titlebar.outerHeight(TRUE)
+ );
+ },
+
+ _parseCorner: function(corner) {
+ var my = this.qtip.options.position.my;
+
+ // Detect corner and mimic properties
+ if(corner === FALSE || my === FALSE) {
+ corner = FALSE;
+ }
+ else if(corner === TRUE) {
+ corner = new CORNER( my.string() );
+ }
+ else if(!corner.string) {
+ corner = new CORNER(corner);
+ corner.fixed = TRUE;
+ }
+
+ return corner;
+ },
+
+ _parseWidth: function(corner, side, use) {
+ var elements = this.qtip.elements,
+ prop = BORDER + camel(side) + 'Width';
+
+ return (use ? intCss(use, prop) :
+ intCss(elements.content, prop) ||
+ intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) ||
+ intCss(elements.tooltip, prop)
+ ) || 0;
+ },
+
+ _parseRadius: function(corner) {
+ var elements = this.qtip.elements,
+ prop = BORDER + camel(corner.y) + camel(corner.x) + 'Radius';
+
+ return BROWSER.ie < 9 ? 0 :
+ intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) ||
+ intCss(elements.tooltip, prop) || 0;
+ },
+
+ _invalidColour: function(elem, prop, compare) {
+ var val = elem.css(prop);
+ return !val || compare && val === elem.css(compare) || INVALID.test(val) ? FALSE : val;
+ },
+
+ _parseColours: function(corner) {
+ var elements = this.qtip.elements,
+ tip = this.element.css('cssText', ''),
+ borderSide = BORDER + camel(corner[ corner.precedance ]) + camel(COLOR),
+ colorElem = this._useTitle(corner) && elements.titlebar || elements.content,
+ css = this._invalidColour, color = [];
+
+ // Attempt to detect the background colour from various elements, left-to-right precedance
+ color[0] = css(tip, BG_COLOR) || css(colorElem, BG_COLOR) || css(elements.content, BG_COLOR) ||
+ css(elements.tooltip, BG_COLOR) || tip.css(BG_COLOR);
+
+ // Attempt to detect the correct border side colour from various elements, left-to-right precedance
+ color[1] = css(tip, borderSide, COLOR) || css(colorElem, borderSide, COLOR) ||
+ css(elements.content, borderSide, COLOR) || css(elements.tooltip, borderSide, COLOR) || elements.tooltip.css(borderSide);
+
+ // Reset background and border colours
+ $('*', tip).add(tip).css('cssText', BG_COLOR+':'+TRANSPARENT+IMPORTANT+';'+BORDER+':0'+IMPORTANT+';');
+
+ return color;
+ },
+
+ _calculateSize: function(corner) {
+ var y = corner.precedance === Y,
+ width = this.options.width,
+ height = this.options.height,
+ isCenter = corner.abbrev() === 'c',
+ base = (y ? width: height) * (isCenter ? 0.5 : 1),
+ pow = Math.pow,
+ round = Math.round,
+ bigHyp, ratio, result,
+
+ smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
+ hyp = [
+ this.border / base * smallHyp,
+ this.border / height * smallHyp
+ ];
+
+ hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(this.border, 2) );
+ hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(this.border, 2) );
+
+ bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
+ ratio = bigHyp / smallHyp;
+
+ result = [ round(ratio * width), round(ratio * height) ];
+ return y ? result : result.reverse();
+ },
+
+ // Tip coordinates calculator
+ _calculateTip: function(corner, size, scale) {
+ scale = scale || 1;
+ size = size || this.size;
+
+ var width = size[0] * scale,
+ height = size[1] * scale,
+ width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
+
+ // Define tip coordinates in terms of height and width values
+ tips = {
+ br: [0,0, width,height, width,0],
+ bl: [0,0, width,0, 0,height],
+ tr: [0,height, width,0, width,height],
+ tl: [0,0, 0,height, width,height],
+ tc: [0,height, width2,0, width,height],
+ bc: [0,0, width,0, width2,height],
+ rc: [0,0, width,height2, 0,height],
+ lc: [width,0, width,height, 0,height2]
+ };
+
+ // Set common side shapes
+ tips.lt = tips.br; tips.rt = tips.bl;
+ tips.lb = tips.tr; tips.rb = tips.tl;
+
+ return tips[ corner.abbrev() ];
+ },
+
+ // Tip coordinates drawer (canvas)
+ _drawCoords: function(context, coords) {
+ context.beginPath();
+ context.moveTo(coords[0], coords[1]);
+ context.lineTo(coords[2], coords[3]);
+ context.lineTo(coords[4], coords[5]);
+ context.closePath();
+ },
+
+ create: function() {
+ // Determine tip corner
+ var c = this.corner = (HASCANVAS || BROWSER.ie) && this._parseCorner(this.options.corner);
+
+ // If we have a tip corner...
+ this.enabled = !!this.corner && this.corner.abbrev() !== 'c';
+ if(this.enabled) {
+ // Cache it
+ this.qtip.cache.corner = c.clone();
+
+ // Create it
+ this.update();
+ }
+
+ // Toggle tip element
+ this.element.toggle(this.enabled);
+
+ return this.corner;
+ },
+
+ update: function(corner, position) {
+ if(!this.enabled) { return this; }
+
+ var elements = this.qtip.elements,
+ tip = this.element,
+ inner = tip.children(),
+ options = this.options,
+ curSize = this.size,
+ mimic = options.mimic,
+ round = Math.round,
+ color, precedance, context,
+ coords, bigCoords, translate, newSize, border;
+
+ // Re-determine tip if not already set
+ if(!corner) { corner = this.qtip.cache.corner || this.corner; }
+
+ // Use corner property if we detect an invalid mimic value
+ if(mimic === FALSE) { mimic = corner; }
+
+ // Otherwise inherit mimic properties from the corner object as necessary
+ else {
+ mimic = new CORNER(mimic);
+ mimic.precedance = corner.precedance;
+
+ if(mimic.x === 'inherit') { mimic.x = corner.x; }
+ else if(mimic.y === 'inherit') { mimic.y = corner.y; }
+ else if(mimic.x === mimic.y) {
+ mimic[ corner.precedance ] = corner[ corner.precedance ];
+ }
+ }
+ precedance = mimic.precedance;
+
+ // Ensure the tip width.height are relative to the tip position
+ if(corner.precedance === X) { this._swapDimensions(); }
+ else { this._resetDimensions(); }
+
+ // Update our colours
+ color = this.color = this._parseColours(corner);
+
+ // Detect border width, taking into account colours
+ if(color[1] !== TRANSPARENT) {
+ // Grab border width
+ border = this.border = this._parseWidth(corner, corner[corner.precedance]);
+
+ // If border width isn't zero, use border color as fill if it's not invalid (1.0 style tips)
+ if(options.border && border < 1 && !INVALID.test(color[1])) { color[0] = color[1]; }
+
+ // Set border width (use detected border width if options.border is true)
+ this.border = border = options.border !== TRUE ? options.border : border;
+ }
+
+ // Border colour was invalid, set border to zero
+ else { this.border = border = 0; }
+
+ // Determine tip size
+ newSize = this.size = this._calculateSize(corner);
+ tip.css({
+ width: newSize[0],
+ height: newSize[1],
+ lineHeight: newSize[1]+'px'
+ });
+
+ // Calculate tip translation
+ if(corner.precedance === Y) {
+ translate = [
+ round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize[0] - curSize[0] - border : (newSize[0] - curSize[0]) / 2),
+ round(mimic.y === TOP ? newSize[1] - curSize[1] : 0)
+ ];
+ }
+ else {
+ translate = [
+ round(mimic.x === LEFT ? newSize[0] - curSize[0] : 0),
+ round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize[1] - curSize[1] - border : (newSize[1] - curSize[1]) / 2)
+ ];
+ }
+
+ // Canvas drawing implementation
+ if(HASCANVAS) {
+ // Grab canvas context and clear/save it
+ context = inner[0].getContext('2d');
+ context.restore(); context.save();
+ context.clearRect(0,0,6000,6000);
+
+ // Calculate coordinates
+ coords = this._calculateTip(mimic, curSize, SCALE);
+ bigCoords = this._calculateTip(mimic, this.size, SCALE);
+
+ // Set the canvas size using calculated size
+ inner.attr(WIDTH, newSize[0] * SCALE).attr(HEIGHT, newSize[1] * SCALE);
+ inner.css(WIDTH, newSize[0]).css(HEIGHT, newSize[1]);
+
+ // Draw the outer-stroke tip
+ this._drawCoords(context, bigCoords);
+ context.fillStyle = color[1];
+ context.fill();
+
+ // Draw the actual tip
+ context.translate(translate[0] * SCALE, translate[1] * SCALE);
+ this._drawCoords(context, coords);
+ context.fillStyle = color[0];
+ context.fill();
+ }
+
+ // VML (IE Proprietary implementation)
+ else {
+ // Calculate coordinates
+ coords = this._calculateTip(mimic);
+
+ // Setup coordinates string
+ coords = 'm' + coords[0] + ',' + coords[1] + ' l' + coords[2] +
+ ',' + coords[3] + ' ' + coords[4] + ',' + coords[5] + ' xe';
+
+ // Setup VML-specific offset for pixel-perfection
+ translate[2] = border && /^(r|b)/i.test(corner.string()) ?
+ BROWSER.ie === 8 ? 2 : 1 : 0;
+
+ // Set initial CSS
+ inner.css({
+ coordsize: newSize[0]+border + ' ' + newSize[1]+border,
+ antialias: ''+(mimic.string().indexOf(CENTER) > -1),
+ left: translate[0] - translate[2] * Number(precedance === X),
+ top: translate[1] - translate[2] * Number(precedance === Y),
+ width: newSize[0] + border,
+ height: newSize[1] + border
+ })
+ .each(function(i) {
+ var $this = $(this);
+
+ // Set shape specific attributes
+ $this[ $this.prop ? 'prop' : 'attr' ]({
+ coordsize: newSize[0]+border + ' ' + newSize[1]+border,
+ path: coords,
+ fillcolor: color[0],
+ filled: !!i,
+ stroked: !i
+ })
+ .toggle(!!(border || i));
+
+ // Check if border is enabled and add stroke element
+ !i && $this.html( createVML(
+ 'stroke', 'weight="'+border*2+'px" color="'+color[1]+'" miterlimit="1000" joinstyle="miter"'
+ ) );
+ });
+ }
+
+ // Opera bug #357 - Incorrect tip position
+ // https://github.com/Craga89/qTip2/issues/367
+ window.opera && setTimeout(function() {
+ elements.tip.css({
+ display: 'inline-block',
+ visibility: 'visible'
+ });
+ }, 1);
+
+ // Position if needed
+ if(position !== FALSE) { this.calculate(corner, newSize); }
+ },
+
+ calculate: function(corner, size) {
+ if(!this.enabled) { return FALSE; }
+
+ var self = this,
+ elements = this.qtip.elements,
+ tip = this.element,
+ userOffset = this.options.offset,
+ position = {},
+ precedance, corners;
+
+ // Inherit corner if not provided
+ corner = corner || this.corner;
+ precedance = corner.precedance;
+
+ // Determine which tip dimension to use for adjustment
+ size = size || this._calculateSize(corner);
+
+ // Setup corners and offset array
+ corners = [ corner.x, corner.y ];
+ if(precedance === X) { corners.reverse(); }
+
+ // Calculate tip position
+ $.each(corners, function(i, side) {
+ var b, bc, br;
+
+ if(side === CENTER) {
+ b = precedance === Y ? LEFT : TOP;
+ position[ b ] = '50%';
+ position[MARGIN+'-' + b] = -Math.round(size[ precedance === Y ? 0 : 1 ] / 2) + userOffset;
+ }
+ else {
+ b = self._parseWidth(corner, side, elements.tooltip);
+ bc = self._parseWidth(corner, side, elements.content);
+ br = self._parseRadius(corner);
+
+ position[ side ] = Math.max(-self.border, i ? bc : userOffset + (br > b ? br : -b));
+ }
+ });
+
+ // Adjust for tip size
+ position[ corner[precedance] ] -= size[ precedance === X ? 0 : 1 ];
+
+ // Set and return new position
+ tip.css({ margin: '', top: '', bottom: '', left: '', right: '' }).css(position);
+ return position;
+ },
+
+ reposition: function(event, api, pos) {
+ if(!this.enabled) { return; }
+
+ var cache = api.cache,
+ newCorner = this.corner.clone(),
+ adjust = pos.adjusted,
+ method = api.options.position.adjust.method.split(' '),
+ horizontal = method[0],
+ vertical = method[1] || method[0],
+ shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
+ offset, css = {}, props;
+
+ function shiftflip(direction, precedance, popposite, side, opposite) {
+ // Horizontal - Shift or flip method
+ if(direction === SHIFT && newCorner.precedance === precedance && adjust[side] && newCorner[popposite] !== CENTER) {
+ newCorner.precedance = newCorner.precedance === X ? Y : X;
+ }
+ else if(direction !== SHIFT && adjust[side]){
+ newCorner[precedance] = newCorner[precedance] === CENTER ?
+ adjust[side] > 0 ? side : opposite :
+ newCorner[precedance] === side ? opposite : side;
+ }
+ }
+
+ function shiftonly(xy, side, opposite) {
+ if(newCorner[xy] === CENTER) {
+ css[MARGIN+'-'+side] = shift[xy] = offset[MARGIN+'-'+side] - adjust[side];
+ }
+ else {
+ props = offset[opposite] !== undefined ?
+ [ adjust[side], -offset[side] ] : [ -adjust[side], offset[side] ];
+
+ if( (shift[xy] = Math.max(props[0], props[1])) > props[0] ) {
+ pos[side] -= adjust[side];
+ shift[side] = FALSE;
+ }
+
+ css[ offset[opposite] !== undefined ? opposite : side ] = shift[xy];
+ }
+ }
+
+ // If our tip position isn't fixed e.g. doesn't adjust with viewport...
+ if(this.corner.fixed !== TRUE) {
+ // Perform shift/flip adjustments
+ shiftflip(horizontal, X, Y, LEFT, RIGHT);
+ shiftflip(vertical, Y, X, TOP, BOTTOM);
+
+ // Update and redraw the tip if needed (check cached details of last drawn tip)
+ if(newCorner.string() !== cache.corner.string() || cache.cornerTop !== adjust.top || cache.cornerLeft !== adjust.left) {
+ this.update(newCorner, FALSE);
+ }
+ }
+
+ // Setup tip offset properties
+ offset = this.calculate(newCorner);
+
+ // Readjust offset object to make it left/top
+ if(offset.right !== undefined) { offset.left = -offset.right; }
+ if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
+ offset.user = this.offset;
+
+ // Perform shift adjustments
+ shift.left = horizontal === SHIFT && !!adjust.left;
+ if(shift.left) {
+ shiftonly(X, LEFT, RIGHT);
+ }
+ shift.top = vertical === SHIFT && !!adjust.top;
+ if(shift.top) {
+ shiftonly(Y, TOP, BOTTOM);
+ }
+
+ /*
+ * If the tip is adjusted in both dimensions, or in a
+ * direction that would cause it to be anywhere but the
+ * outer border, hide it!
+ */
+ this.element.css(css).toggle(
+ !(shift.x && shift.y || newCorner.x === CENTER && shift.y || newCorner.y === CENTER && shift.x)
+ );
+
+ // Adjust position to accomodate tip dimensions
+ pos.left -= offset.left.charAt ? offset.user :
+ horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left + this.border : 0;
+ pos.top -= offset.top.charAt ? offset.user :
+ vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top + this.border : 0;
+
+ // Cache details
+ cache.cornerLeft = adjust.left; cache.cornerTop = adjust.top;
+ cache.corner = newCorner.clone();
+ },
+
+ destroy: function() {
+ // Unbind events
+ this.qtip._unbind(this.qtip.tooltip, this._ns);
+
+ // Remove the tip element(s)
+ if(this.qtip.elements.tip) {
+ this.qtip.elements.tip.find('*')
+ .remove().end().remove();
+ }
+ }
+});
+
+TIP = PLUGINS.tip = function(api) {
+ return new Tip(api, api.options.style.tip);
+};
+
+// Initialize tip on render
+TIP.initialize = 'render';
+
+// Setup plugin sanitization options
+TIP.sanitize = function(options) {
+ if(options.style && 'tip' in options.style) {
+ var opts = options.style.tip;
+ if(typeof opts !== 'object') { opts = options.style.tip = { corner: opts }; }
+ if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
+ }
+};
+
+// Add new option checks for the plugin
+CHECKS.tip = {
+ '^position.my|style.tip.(corner|mimic|border)$': function() {
+ // Make sure a tip can be drawn
+ this.create();
+
+ // Reposition the tooltip
+ this.qtip.reposition();
+ },
+ '^style.tip.(height|width)$': function(obj) {
+ // Re-set dimensions and redraw the tip
+ this.size = [ obj.width, obj.height ];
+ this.update();
+
+ // Reposition the tooltip
+ this.qtip.reposition();
+ },
+ '^content.title|style.(classes|widget)$': function() {
+ this.update();
+ }
+};
+
+// Extend original qTip defaults
+$.extend(TRUE, QTIP.defaults, {
+ style: {
+ tip: {
+ corner: TRUE,
+ mimic: FALSE,
+ width: 6,
+ height: 6,
+ border: TRUE,
+ offset: 0
+ }
+ }
+});
+;PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
+{
+ var target = posOptions.target,
+ tooltip = api.elements.tooltip,
+ my = posOptions.my,
+ at = posOptions.at,
+ adjust = posOptions.adjust,
+ method = adjust.method.split(' '),
+ methodX = method[0],
+ methodY = method[1] || method[0],
+ viewport = posOptions.viewport,
+ container = posOptions.container,
+ adjusted = { left: 0, top: 0 },
+ fixed, newMy, containerOffset, containerStatic,
+ viewportWidth, viewportHeight, viewportScroll, viewportOffset;
+
+ // If viewport is not a jQuery element, or it's the window/document, or no adjustment method is used... return
+ if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') {
+ return adjusted;
+ }
+
+ // Cach container details
+ containerOffset = container.offset() || adjusted;
+ containerStatic = container.css('position') === 'static';
+
+ // Cache our viewport details
+ fixed = tooltip.css('position') === 'fixed';
+ viewportWidth = viewport[0] === window ? viewport.width() : viewport.outerWidth(FALSE);
+ viewportHeight = viewport[0] === window ? viewport.height() : viewport.outerHeight(FALSE);
+ viewportScroll = { left: fixed ? 0 : viewport.scrollLeft(), top: fixed ? 0 : viewport.scrollTop() };
+ viewportOffset = viewport[0] !== window && viewport.offset() || adjusted;
+
+ // Generic calculation method
+ function calculate(side, otherSide, type, adjustment, side1, side2, lengthName, targetLength, elemLength) {
+ var initialPos = position[side1],
+ mySide = my[side],
+ atSide = at[side],
+ isShift = type === SHIFT,
+ myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2,
+ atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2,
+ sideOffset = viewportScroll[side1] + viewportOffset[side1] - (containerStatic ? 0 : containerOffset[side1]),
+ overflow1 = sideOffset - initialPos,
+ overflow2 = initialPos + elemLength - (lengthName === WIDTH ? viewportWidth : viewportHeight) - sideOffset,
+ offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0);
+
+ // shift
+ if(isShift) {
+ offset = (mySide === side1 ? 1 : -1) * myLength;
+
+ // Adjust position but keep it within viewport dimensions
+ position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0;
+ position[side1] = Math.max(
+ -containerOffset[side1] + viewportOffset[side1],
+ initialPos - offset,
+ Math.min(
+ Math.max(
+ -containerOffset[side1] + viewportOffset[side1] + (lengthName === WIDTH ? viewportWidth : viewportHeight),
+ initialPos + offset
+ ),
+ position[side1],
+
+ // Make sure we don't adjust complete off the element when using 'center'
+ mySide === 'center' ? initialPos - myLength : 1E9
+ )
+ );
+
+ }
+
+ // flip/flipinvert
+ else {
+ // Update adjustment amount depending on if using flipinvert or flip
+ adjustment *= type === FLIPINVERT ? 2 : 0;
+
+ // Check for overflow on the left/top
+ if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) {
+ position[side1] -= offset + adjustment;
+ newMy.invert(side, side1);
+ }
+
+ // Check for overflow on the bottom/right
+ else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) {
+ position[side1] -= (mySide === CENTER ? -offset : offset) + adjustment;
+ newMy.invert(side, side2);
+ }
+
+ // Make sure we haven't made things worse with the adjustment and reset if so
+ if(position[side1] < viewportScroll[side1] && -position[side1] > overflow2) {
+ position[side1] = initialPos; newMy = my.clone();
+ }
+ }
+
+ return position[side1] - initialPos;
+ }
+
+ // Set newMy if using flip or flipinvert methods
+ if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); }
+
+ // Adjust position based onviewport and adjustment options
+ adjusted = {
+ left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0,
+ top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0,
+ my: newMy
+ };
+
+ return adjusted;
+};
+;PLUGINS.polys = {
+ // POLY area coordinate calculator
+ // Special thanks to Ed Cradock for helping out with this.
+ // Uses a binary search algorithm to find suitable coordinates.
+ polygon: function(baseCoords, corner) {
+ var result = {
+ width: 0, height: 0,
+ position: {
+ top: 1e10, right: 0,
+ bottom: 0, left: 1e10
+ },
+ adjustable: FALSE
+ },
+ i = 0, next,
+ coords = [],
+ compareX = 1, compareY = 1,
+ realX = 0, realY = 0,
+ newWidth, newHeight;
+
+ // First pass, sanitize coords and determine outer edges
+ i = baseCoords.length;
+ while(i--) {
+ next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
+
+ if(next[0] > result.position.right){ result.position.right = next[0]; }
+ if(next[0] < result.position.left){ result.position.left = next[0]; }
+ if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
+ if(next[1] < result.position.top){ result.position.top = next[1]; }
+
+ coords.push(next);
+ }
+
+ // Calculate height and width from outer edges
+ newWidth = result.width = Math.abs(result.position.right - result.position.left);
+ newHeight = result.height = Math.abs(result.position.bottom - result.position.top);
+
+ // If it's the center corner...
+ if(corner.abbrev() === 'c') {
+ result.position = {
+ left: result.position.left + result.width / 2,
+ top: result.position.top + result.height / 2
+ };
+ }
+ else {
+ // Second pass, use a binary search algorithm to locate most suitable coordinate
+ while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
+ {
+ newWidth = Math.floor(newWidth / 2);
+ newHeight = Math.floor(newHeight / 2);
+
+ if(corner.x === LEFT){ compareX = newWidth; }
+ else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
+ else{ compareX += Math.floor(newWidth / 2); }
+
+ if(corner.y === TOP){ compareY = newHeight; }
+ else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
+ else{ compareY += Math.floor(newHeight / 2); }
+
+ i = coords.length;
+ while(i--)
+ {
+ if(coords.length < 2){ break; }
+
+ realX = coords[i][0] - result.position.left;
+ realY = coords[i][1] - result.position.top;
+
+ if(
+ corner.x === LEFT && realX >= compareX ||
+ corner.x === RIGHT && realX <= compareX ||
+ corner.x === CENTER && (realX < compareX || realX > result.width - compareX) ||
+ corner.y === TOP && realY >= compareY ||
+ corner.y === BOTTOM && realY <= compareY ||
+ corner.y === CENTER && (realY < compareY || realY > result.height - compareY)) {
+ coords.splice(i, 1);
+ }
+ }
+ }
+ result.position = { left: coords[0][0], top: coords[0][1] };
+ }
+
+ return result;
+ },
+
+ rect: function(ax, ay, bx, by) {
+ return {
+ width: Math.abs(bx - ax),
+ height: Math.abs(by - ay),
+ position: {
+ left: Math.min(ax, bx),
+ top: Math.min(ay, by)
+ }
+ };
+ },
+
+ _angles: {
+ tc: 3 / 2, tr: 7 / 4, tl: 5 / 4,
+ bc: 1 / 2, br: 1 / 4, bl: 3 / 4,
+ rc: 2, lc: 1, c: 0
+ },
+ ellipse: function(cx, cy, rx, ry, corner) {
+ var c = PLUGINS.polys._angles[ corner.abbrev() ],
+ rxc = c === 0 ? 0 : rx * Math.cos( c * Math.PI ),
+ rys = ry * Math.sin( c * Math.PI );
+
+ return {
+ width: rx * 2 - Math.abs(rxc),
+ height: ry * 2 - Math.abs(rys),
+ position: {
+ left: cx + rxc,
+ top: cy + rys
+ },
+ adjustable: FALSE
+ };
+ },
+ circle: function(cx, cy, r, corner) {
+ return PLUGINS.polys.ellipse(cx, cy, r, r, corner);
+ }
+};
+;PLUGINS.svg = function(api, svg, corner)
+{
+ var elem = svg[0],
+ root = $(elem.ownerSVGElement),
+ ownerDocument = elem.ownerDocument,
+ strokeWidth2 = (parseInt(svg.css('stroke-width'), 10) || 0) / 2,
+ frameOffset, mtx, transformed,
+ len, next, i, points,
+ result, position;
+
+ // Ascend the parentNode chain until we find an element with getBBox()
+ while(!elem.getBBox) { elem = elem.parentNode; }
+ if(!elem.getBBox || !elem.parentNode) { return FALSE; }
+
+ // Determine which shape calculation to use
+ switch(elem.nodeName) {
+ case 'ellipse':
+ case 'circle':
+ result = PLUGINS.polys.ellipse(
+ elem.cx.baseVal.value,
+ elem.cy.baseVal.value,
+ (elem.rx || elem.r).baseVal.value + strokeWidth2,
+ (elem.ry || elem.r).baseVal.value + strokeWidth2,
+ corner
+ );
+ break;
+
+ case 'line':
+ case 'polygon':
+ case 'polyline':
+ // Determine points object (line has none, so mimic using array)
+ points = elem.points || [
+ { x: elem.x1.baseVal.value, y: elem.y1.baseVal.value },
+ { x: elem.x2.baseVal.value, y: elem.y2.baseVal.value }
+ ];
+
+ for(result = [], i = -1, len = points.numberOfItems || points.length; ++i < len;) {
+ next = points.getItem ? points.getItem(i) : points[i];
+ result.push.apply(result, [next.x, next.y]);
+ }
+
+ result = PLUGINS.polys.polygon(result, corner);
+ break;
+
+ // Unknown shape or rectangle? Use bounding box
+ default:
+ result = elem.getBBox();
+ result = {
+ width: result.width,
+ height: result.height,
+ position: {
+ left: result.x,
+ top: result.y
+ }
+ };
+ break;
+ }
+
+ // Shortcut assignments
+ position = result.position;
+ root = root[0];
+
+ // Convert position into a pixel value
+ if(root.createSVGPoint) {
+ mtx = elem.getScreenCTM();
+ points = root.createSVGPoint();
+
+ points.x = position.left;
+ points.y = position.top;
+ transformed = points.matrixTransform( mtx );
+ position.left = transformed.x;
+ position.top = transformed.y;
+ }
+
+ // Check the element is not in a child document, and if so, adjust for frame elements offset
+ if(ownerDocument !== document && api.position.target !== 'mouse') {
+ frameOffset = $((ownerDocument.defaultView || ownerDocument.parentWindow).frameElement).offset();
+ if(frameOffset) {
+ position.left += frameOffset.left;
+ position.top += frameOffset.top;
+ }
+ }
+
+ // Adjust by scroll offset of owner document
+ ownerDocument = $(ownerDocument);
+ position.left += ownerDocument.scrollLeft();
+ position.top += ownerDocument.scrollTop();
+
+ return result;
+};
+;}));
+}( window, document ));
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.css b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.css
new file mode 100644
index 00000000..978bf7c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.css
@@ -0,0 +1,854 @@
+@charset "utf-8";
+
+/**
+ * Target item style, when menu opened
+ */
+.sm_target_clicked {
+ background-color: #e9ecef;
+ background-image: none;
+ border-color: rgba(27,31,35,0.35);
+ box-shadow: inset 0 0.15em 0.3em rgba(27,31,35,0.15);
+}
+
+.sm_caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 10px;
+ border-left: 4px solid;
+ border-top: 4px solid transparent;
+ border-bottom: 4px solid transparent;
+ /*float: right;*/
+ margin-top: 3px;
+ /*vertical-align: middle;*/
+ content: "";
+ position: absolute;
+ top: 7px;
+ right: 10px;
+}
+
+/* Sub menus */
+.sm_sub_menu {
+ display: none;
+}
+.sm_sub_header {
+ margin-top: 2px !important;
+}
+.sm_sub_header:hover {
+ background-color: transparent !important;
+ color: black !important;
+ cursor: default !important;
+}
+.sm_sub_header p {
+ padding: 0;
+ margin: 0;
+ font-weight: 600;
+ line-height: 26px;
+ font-size: 16px;
+}
+.sm_sub_header button {
+ float: left;
+ font-weight: 700;
+ line-height: 1;
+ color: #000;
+ filter: alpha(opacity=20);
+
+ -webkit-appearance: none;
+ padding: 0;
+ cursor: pointer;
+ background: 0 0;
+ border: 0;
+
+ -webkit-transition: all .5s cubic-bezier(.175,.885,.32,1);
+ transition: all .5s cubic-bezier(.175,.885,.32,1);
+ height: 26px;
+ width: 30px;
+ text-shadow: none;
+ outline: none !important;
+ margin: 0;
+ opacity: 0.5;
+ font-size: 14px !important;
+}
+.sm_sub_header button:hover {
+ opacity: 1;
+}
+.sm_sub_header button i {
+ font-size: 16px;
+}
+
+
+/**
+ * Container
+ */
+.sm_container {
+ border: 1px solid #D6D7D7;
+ margin: 0;
+ padding: 0;
+ display: inline-block;
+ position: absolute;
+ top:0;
+ left:0;
+ vertical-align: middle;
+ min-width: 300px;
+ box-sizing: border-box;
+
+ background-color: #F5F5F5;
+ border-radius: 2px;
+ box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+ -webkit-box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+ /*display: none;*/
+ z-index: 100;
+}
+.sm_container.sm_regular {
+ width: auto;
+ min-width: 150px;
+}
+.sm_container.sm_embed {
+ position: relative;
+}
+
+
+.sm_header {
+ /*border-bottom: 1px solid #E6E7E7;*/
+}
+.sm_header > h3 {
+ padding: 0;
+ margin: 8px 80px 8px 10px;
+ color: #24292e;
+ font-size: 16px;
+ white-space: nowrap;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ font-weight: 500;
+ line-height: 1.1;
+}
+.sm_header button{
+ position: absolute;
+ -webkit-appearance: none;
+ padding: 0;
+ cursor: pointer;
+ background: 0 0;
+ border: 0;
+ outline: none;
+ line-height: 100%;
+ color: #999999;
+ top: 3px;
+ font-size: 20px;
+}
+.sm_header button.sm_close_button {
+ right: 10px;
+ font-size: 25px;
+}
+.sm_header button.sm_removeall_button {
+ right: 32px;
+}
+.sm_header button.sm_selectall_button {
+ right: 54px;
+}
+.sm_header button:hover {
+ color: black;
+}
+.sm_input_area {
+ padding: 10px;
+}
+.sm_input {
+ display: block;
+ background-color: white;
+ margin: 0 !important;
+ width: 100%;
+
+ font-size: 14px;
+ line-height: 20px;
+ min-height: 20px;
+ padding: 4px 6px;
+ vertical-align: middle;
+ box-sizing: border-box;
+
+ outline: none !important;
+ height: 30px;
+
+ border-radius: 2px;
+ border: 1px solid #dddddd;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+
+ /*
+ -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
+ -moz-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
+ */
+ /*transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;*/
+ /*transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;*/
+ transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
+}
+.sm_input:focus {
+ border: 1px solid #bbbbbb;
+ /* åŒæ—¶è®¾ç½®äº†å†…部和外部的阴影 */
+ /*
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 1px 8px rgba(90,90,90,0.35);
+ -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 1px 8px rgba(90,90,90,0.35);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 1px 8px rgba(90,90,90,0.35);
+ */
+ box-shadow: 0 0 0 3px rgba(150,150,150, 0.2);
+}
+
+
+/**
+ * "Get all" button
+ */
+div.sm_button {
+ display: inline-block;
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ cursor: pointer;
+ text-align: center;
+ box-sizing: border-box;
+
+ border: 0;
+ width: 24px;
+ height: 100%;
+ padding: 0;
+ vertical-align: middle;
+ line-height: 100%;
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.sm_container_open > .sm_button {
+ border-bottom-right-radius: 0 !important;
+}
+
+div.sm_clear_btn {
+ position: absolute;
+ top: 0;
+ right: 25px;
+ display: block;
+ width: auto;
+ height: 100%;
+ cursor: pointer;
+ font-size: 20px;
+ color: #666666;
+ margin: 0;
+ padding: 4px 0 0 0;
+ box-sizing: border-box;
+ line-height: 1;
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
+}
+div.sm_clear_btn:hover {
+ color: black;
+ font-weight: bold;
+}
+
+/**
+ * Results
+ */
+.sm_result_area {
+ /* background-color: transparent; */
+ background-color: white;
+ /*border: 1px solid #D6D7D7;*/
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ width: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ clear: both;
+}
+.sm_result_area:before,
+.sm_result_area:after{
+ display: table;
+ content: " ";
+}
+.sm_result_area.sm_list_mode{
+ border-top: 1px solid #E6E7E7;
+}
+div.sm_result_area.shadowDown{
+ box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+ -webkit-box-shadow: 0 3px 12px rgba(0,0,0,0.2);
+}
+div.sm_result_area.shadowUp{
+ box-shadow: 0 -1px 12px rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 -1px 12px rgba(0,0,0,0.2);
+ -webkit-box-shadow: 0 -1px 12px rgba(0,0,0,0.2);
+}
+.sm_result_tabs {
+ padding: 0 10px;
+ border-bottom: 1px solid #E6E7E7;
+}
+.sm_result_tabs ul {
+ position: relative;
+ bottom: -1px;
+ padding: 0;
+ margin: 0;
+}
+.sm_result_tabs ul li {
+ display: inline-block;
+}
+.sm_result_tabs ul li a {
+ display: inline-block;
+ padding: 7px 10px 3px;
+ font-size: 14px;
+ color: #6a737d;
+ text-decoration: none;
+ cursor: pointer;
+ line-height: 100%;
+ background: transparent;
+ border: 1px solid transparent;
+ border-radius: 3px 3px 0 0;
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
+}
+.sm_result_tabs ul li a.active {
+ color: #24292e;
+ background-color: #fff;
+ border-color: #dfe2e5;
+ border-bottom-color: #fff;
+ font-weight: bold;
+}
+/* menu arrow */
+.sm_arrow,
+.sm_arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+.sm_arrow {
+ border-width: 11px;
+}
+.sm_arrow:after {
+ content: "";
+ border-width: 10px;
+}
+
+.sm_arrow_bottom > .sm_arrow {
+ top: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999;
+ border-bottom-color: rgba(0,0,0,.25);
+}
+.sm_arrow_bottom > .sm_arrow:after {
+ content: " ";
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #fff;
+}
+.sm_arrow_bottom > .sm_arrow.sm_have_title:after {
+ border-bottom-color: #f6f6f6;
+}
+.sm_arrow_top > .sm_arrow {
+ bottom: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999;
+ border-top-color: rgba(0,0,0,.25);
+}
+.sm_arrow_top > .sm_arrow:after {
+ content: " ";
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #fff;
+}
+/* menu arrow */
+
+
+.sm_results {
+ background-color: white;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+/* regular menu item style */
+
+.sm_regular .sm_results {
+ padding: 5px 0;
+}
+.sm_regular .sm_results > li {
+ padding: 0;
+}
+.sm_regular .sm_results > li:hover{
+ background-color: #53A4EA;
+ color: white;
+}
+.sm_regular .sm_results > li > a {
+ width: 100%;
+ display: block;
+ padding: 7px 10px;
+}
+.sm_regular .sm_results > li:hover a {
+ color: white;
+}
+.sm_regular .sm_results > li.sm_disabled,
+.sm_regular .sm_results > li.sm_disabled:hover,
+.sm_regular .sm_results > li.sm_disabled a{
+ background-color: white;
+ color: #CCCCCC;
+ cursor: default;
+}
+.sm_regular .sm_results > li.sm_header,
+.sm_regular .sm_results > li.sm_header:hover,
+.sm_regular .sm_results > li.sm_header a {
+ background-color: white;
+ color: black;
+ font-size: 16px;
+ font-weight: 600;
+ cursor: default;
+}
+
+/* regular menu item style */
+.sm_results > li {
+ height: auto;
+ line-height: 1;
+ margin: 0;
+ overflow: hidden;
+ padding: 3px 10px;
+ position: relative;
+ text-align: left;
+ white-space: nowrap;
+ font-size: 14px;
+ color: black;
+ cursor : pointer;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+.sm_results > li > a {
+ color: black;
+ text-decoration: none;
+}
+
+.sm_results > li > div.sm_selected_icon {
+ width: 16px;
+ margin-right: 10px;
+ float: left;
+}
+.sm_results > li > div.sm_selected_icon > i {
+ font-size: 16px;
+ font-weight: bold;
+ line-height: 1.5;
+ visibility: hidden;
+}
+.sm_results > li > div.sm_item_text {
+ font-size: 14px;
+ display: block;
+ line-height: 1.7;
+}
+
+.sm_results > li.sm_message_box {
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ box-sizing: content-box;
+ font-size: 14px;
+ cursor: default;
+}
+.sm_results > li.sm_message_box i {
+ font-size: 18px;
+}
+.sm_results > li.sm_divider {
+ margin: 8px 0;
+ padding: 0;
+ cursor: default;
+ overflow: hidden;
+ border-bottom: 1px solid #E5E5E5;
+}
+
+ul.sm_results > li.sm_over {
+ background-color: #53A4EA !important;
+ color: #fff !important;
+ cursor: pointer;
+}
+
+ul.sm_results > li.sm_selected {
+ font-weight: 600;
+}
+ul.sm_results > li.sm_selected > div.sm_selected_icon > i {
+ visibility: visible;
+}
+
+.sm_control_box{
+ padding: 0;
+ height: 27px;
+ background-color: white;
+ border-top: 1px solid #E6E7E7;
+}
+.sm_control_box.sm_two_btn button{
+ width: 50%;
+}
+.sm_control_box button{
+ height: 26px;
+ line-height: 20px;
+ width: 33.3%;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ font-size: 13px !important;
+ padding: 1px 10px;
+ border: 0;
+ background-color: #F5F5F5;
+ color: #666666;
+ outline: none;
+}
+.sm_control_box button:hover{
+ background-color: #EEEEEE;
+ color: black;
+}
+
+/**
+ * 多选模å¼ç›¸å…³æ ·å¼
+ */
+div.sm_container_combo{
+ border: 1px solid #CCCCCC;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-sizing: border-box;
+}
+/* 多选模å¼çš„ç¦ç”¨çŠ¶æ€æ ·å¼ */
+div.sm_container_combo.sm_disabled,
+div.sm_container_combo.sm_disabled ul.sm_element_box,
+div.sm_container_combo.sm_disabled .sm_input{
+ background-color: #eeeeee;
+ cursor: not-allowed !important;
+}
+div.sm_container_combo.sm_container_open{
+ border-radius: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ border: 1px solid #75B9F0;
+}
+input.sm_combo_input{
+ border: 0;
+ box-shadow: none;
+ background-color: transparent;
+ max-width: 100%;
+ padding: 0;
+ height: 30px !important;
+ line-height: 30px !important;
+ min-height: 30px !important;
+}
+input.sm_combo_input:focus {
+ box-shadow: none;
+}
+input.sm_combo_input[readonly],
+input.sm_combo_input[disabled]{
+ background-color: white;
+}
+ul.sm_element_box{
+ margin: 0;
+ position: relative;
+ overflow: hidden;
+ clear: both;
+ cursor: text;
+ margin-right: 24px;
+ list-style: none;
+ height: auto;
+ padding: 4px 0 0 5px;
+ font-size: 14px;
+ min-height: 24px;
+}
+ul.sm_element_box > li{
+ list-style: none;
+ padding: 0 5px;
+/* margin-left: 5px;
+ margin-top: 3px; */
+ /* margin-bottom: 2px; */
+ float: left;
+ height: 19px;
+ line-height: 19px;
+ position: relative;
+ margin: 0;
+ box-sizing: content-box;
+}
+ul.sm_element_box li.full_width {
+ width: 100%;
+}
+ul.sm_element_box li.full_width input{
+ width: 100% !important;
+}
+ul.sm_element_box li.selected_tag {
+ border: 1px solid #AAAAAA;
+ border-radius: 3px;
+ background-color: #EFEFEF;
+ cursor: pointer;
+ max-width: 100%;
+ box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+ margin-bottom: 2px;
+ margin-right: 5px;
+}
+ul.sm_element_box li.input_box {
+ padding: 0;
+ /* margin: 0; */
+ margin-top: 0;
+ height: 21px;
+ min-height: 21px;
+}
+ul.sm_element_box li.input_box input {
+ height: 21px !important;
+ line-height: 21px !important;
+ min-height: 21px !important;
+}
+ul.sm_element_box li.selected_tag span.tag_close {
+ cursor: pointer;
+ margin-left: 5px;
+ font-size: 15px;
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
+}
+
+
+/**
+ * 加深颜色表示éžé€‰ä¸­
+ */
+.sm_results_off {
+ /*background: rgba(255, 255, 255, 0.8);*/
+}
+
+.sm_input_off {
+ background: #eee;
+ color: #333333;
+}
+
+.sm_hide {
+ display: none;
+}
+
+/**
+ * Navi
+ */
+.sm_navi {
+ background: #eee;
+ border-bottom: 1px solid #79b;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ margin: 0;
+ padding: 4px;
+ text-align: right;
+}
+
+.sm_navi > p > a:link,
+.sm_navi > p > a:visited,
+.sm_navi > p > a:hover,
+.sm_navi > p > a:active {
+ color: blue;
+ font-weight: normal;
+ margin: 0 4px;
+ text-decoration: underline;
+}
+
+.sm_navi > p {
+ color: black;
+ font-size: 15px;
+ margin: 0;
+ padding-top: 4px;
+ text-align: center;
+}
+
+.sm_navi > p > a >.current {
+ color: #00c;
+ font-size: 16px;
+ font-weight: bold;
+}
+
+.sm_navi > p >.page_end {
+ color: gray;
+ font-weight: normal;
+ margin: 0 4px;
+}
+
+.navi_page,
+.navi_first,
+.navi_prev,
+.navi_next,
+.navi_last {
+ margin: auto 4px !important;
+}
+
+.sm_navi > .info {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+/**
+ * Select only
+ */
+.sm_container > .sm_select_ng {
+ background: #fcc;
+}
+
+
+
+
+
+
+
+/*输入框设置了input-block-levelæ ·å¼æ—¶çš„特殊情况修å¤*/
+div.sm_container input.sm_input.input-block-level{
+ box-sizing:border-box;
+ height: 30px;
+ line-height: 30px;
+ min-height: 30px;
+ width: 100%;
+}
+
+div.sm_container_open .sm_input::-ms-clear{display:none;}
+/* 移除微软æµè§ˆå™¨ï¼Œåœ¨è¾“入框输入文本åŽï¼Œä¼šå‡ºçŽ°X的问题,but is look like not working */
+input::-ms-clear{display:none;}/*éšè—文本框å‰å­*/
+input::-ms-reveal{display:none;}/*éšè—密ç æ¡†å°çœ¼ç›*/
+
+div.sm_button span.caret {
+ position: absolute;
+ top: 50%;
+ right: 12px;
+ margin-top: -1px;
+ vertical-align: middle;
+}
+div.sm_navi > p {
+ font-size: 12px;
+}
+
+
+
+
+/**
+ * pagination bar
+ */
+div.sm_result_area div.pagination {
+ margin: 0;
+ padding: 0;
+ height: 26px;
+ line-height: 26px;
+ width: 100%;
+ /* border-top: 1px solid #DDDDDD; */
+}
+div.sm_result_area div.pagination ul {
+ width: 100%;
+}
+div.sm_result_area div.pagination li{
+ text-align: center;
+}
+div.sm_result_area div.pagination>ul>li>a {
+ margin: 0;
+ border-radius: 0;
+ padding: 0;
+ box-shadow: none;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ background-color: white;
+
+ border: 0;
+ box-sizing: content-box;
+ color: #666666;
+ font-size: 20px;
+ height: 26px;
+ line-height: 26px;
+}
+
+div.sm_result_area div.pagination li.csFirstPage a,
+div.sm_result_area div.pagination li.csPreviousPage a,
+div.sm_result_area div.pagination li.csNextPage a,
+div.sm_result_area div.pagination li.csLastPage a {
+ width: 30px;
+}
+div.sm_result_area div.pagination li.csFirstPage a{
+ border-left: 0;
+}
+div.sm_result_area div.pagination li.csLastPage a{
+ border-right: 0;
+}
+
+div.sm_result_area div.pagination>ul>li>a:hover {
+ color: #000000; background-color: #E8E8E8;
+}
+
+div.sm_result_area div.pagination>ul>li.disabled>a{
+ color: #DDDDDD;
+ cursor: default;
+}
+div.sm_result_area div.pagination>ul>li.disabled>a:hover {
+ color: #DDDDDD;background-color: white;
+}
+
+div.sm_result_area div.pagination>ul>li.pageInfoBox>a {
+ width: 178px;
+ text-align: center;
+ /* padding-left: 4px;
+ padding-right: 4px; */
+ color: #666666;
+ font-size: 14px;
+}
+div.sm_result_area div.pagination>ul>li.pageInfoBox>a:hover {
+ background-color: inherit;
+ color: #666666;
+ cursor: default;
+}
+
+/* icons */
+@font-face {font-family: "iconfont";
+ src: url('data:image/eot;base64,ZAsAALwKAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAApsUkVgAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzJW7kk6AAABfAAAAFZjbWFwbyWiuQAAAfAAAAHeZ2x5ZsG49g0AAAPgAAAECGhlYWQPk2N3AAAA4AAAADZoaGVhB94DiAAAALwAAAAkaG10eBvpAAAAAAHUAAAAHGxvY2EDfgQyAAAD0AAAABBtYXhwARYAYgAAARgAAAAgbmFtZT5U/n0AAAfoAAACbXBvc3RVALKVAAAKWAAAAGIAAQAAA4D/gABcBAAAAAAABAAAAQAAAAAAAAAAAAAAAAAAAAcAAQAAAAEAAFYkxaZfDzz1AAsEAAAAAADWOg+9AAAAANY6D70AAP+/BAADQQAAAAgAAgAAAAAAAAABAAAABwBWAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQP9AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABAAHjnSwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAPpAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAUAAAADAAAALAAAAAQAAAGGAAEAAAAAAIAAAwABAAAALAADAAoAAAGGAAQAVAAAAA4ACAACAAYAeOYn5mPnIucu50v//wAAAHjmJ+Zj5yLnLudL//8AAAAAAAAAAAAAAAAAAQAOAA4ADgAOAA4ADgAAAAEABgACAAMABAAFAAABBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAABYAAAAAAAAAAYAAAB4AAAAeAAAAAEAAOYnAADmJwAAAAYAAOZjAADmYwAAAAIAAOciAADnIgAAAAMAAOcuAADnLgAAAAQAAOdLAADnSwAAAAUAAAAAAAAAdgDSAS4BXAHaAgQABQAA/+EDvAMYABMAKAAxAEQAUAAAAQYrASIOAh0BISc0LgIrARUhBRUXFA4DJyMnIQcjIi4DPQEXIgYUFjI2NCYXBgcGDwEOAR4BMyEyNicuAicBNTQ+AjsBMhYdAQEZGxpTEiUcEgOQAQoYJx6F/koCogEVHyMcDz4t/kksPxQyIBMIdwwSEhkSEowIBgUFCAICBA8OAW0XFgkFCQoG/qQFDxoVvB8pAh8BDBknGkxZDSAbEmGING4dJRcJAQGAgAETGyAOpz8RGhERGhF8GhYTEhkHEA0IGBoNIyQUAXfkCxgTDB0m4wAAAAAFAAD/3wPDAx8AEQAeACsALAA1AAAFISIuATY3AT4BMhYXAR4BDgEBBgcBBhYXIT4BJwEmAyImNRE0NjIWFREUBgcjPgEyFhQGIiYDUf1eIjQbAxABVBEvNS8RAVUQAxs0/o0SEf6sEBQiAqIiFBD+qxASDhISHBISDjABGygbGygbIRouOh4CYR4hIR79nx46LhoDAAEd/Z4dIgEBIh0CYh3+IhIOASAOEhIO/uAOEnAUGxspGxsAAwAA/98DoAMgABcAJwA3AAABJiIPAScuAQYUHwEWHwEWMj8BNjcBNiYTIS4BJxE+ATchHgEXEQ4BASIGFREUFjMhMjY1ETQmIwLXChkK6mcKGRQJfwMEAwYMBgQDBAEACgFg/YApNgEBNikCgCk2AQE2/VcOEhIOAoAOEhIOAfcJCexqCQETGgqCAgIDAgIDAQMBBAka/fMBNikCgCk2AQE2Kf2AKTYC/xIO/YAOEhIOAoAOEgAAAAEAAAAAA4MCXwAXAAABJgYHAScmIgYUFwUWHwEWMj8BNjcBNjQDeAoaCf5V6QoaEwoBAQMEAwYMBgQDBAHBCQJWCQEK/jrmCRQZCv0DAQMCAwICAwHfChkAAAAABQAA/78DwQNBAAwAGQAvADkAVQAAJSImNRE0NjIWFREUBiMiJjURNDYyFhURFAYBIzUuASchDgEHFSMiBhQWMyEyNjQmJTQ2MyEyFh0BIQEhLgEnETQ2MhYVERQWMyEyNjURPgEyFhURDgECYA4SEhwSEs4OEhIcEhIB8qABNij+vyk2AaAOEhIOA0AOEhL9khIOAUENEv6AAaD+QCk2ARMbEhMNAcAOEgESGxIBNoASDgFgDhISDv6gDhISDgFgDhISDv6gDhICIEApNgEBNilAEhwSEhwSQA4SEg5A/SABNikB4A4SEg7+IA4SEg4B3w4SEg7+ISk2AAAAAQAA/90C6QMoABIAAAEWFwEWDgEnASY3ATYXHgEHAQYBewcDAVASCioT/oohIQFwEhkWDQ3+qgMBgwUD/rERMA4PAXUiIQFxEwYHKBP+qgMAAAAAAAASAN4AAQAAAAAAAAAVAAAAAQAAAAAAAQAIABUAAQAAAAAAAgAHAB0AAQAAAAAAAwAIACQAAQAAAAAABAAIACwAAQAAAAAABQALADQAAQAAAAAABgAIAD8AAQAAAAAACgArAEcAAQAAAAAACwATAHIAAwABBAkAAAAqAIUAAwABBAkAAQAQAK8AAwABBAkAAgAOAL8AAwABBAkAAwAQAM0AAwABBAkABAAQAN0AAwABBAkABQAWAO0AAwABBAkABgAQAQMAAwABBAkACgBWARMAAwABBAkACwAmAWkKQ3JlYXRlZCBieSBpY29uZm9udAppY29uZm9udFJlZ3VsYXJpY29uZm9udGljb25mb250VmVyc2lvbiAxLjBpY29uZm9udEdlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC5odHRwOi8vZm9udGVsbG8uY29tAAoAQwByAGUAYQB0AGUAZAAgAGIAeQAgAGkAYwBvAG4AZgBvAG4AdAAKAGkAYwBvAG4AZgBvAG4AdABSAGUAZwB1AGwAYQByAGkAYwBvAG4AZgBvAG4AdABpAGMAbwBuAGYAbwBuAHQAVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG4AZgBvAG4AdABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAQIBAwEEAQUBBgEHAQgAAXgEd2FybhFmYW5neGluZ3h1YW56aG9uZwZ4dWFuemUHc2hhbmNodQZpLWxlZnQAAAAA'); /* IE9*/
+ src: url('data:image/eot;base64,ZAsAALwKAAABAAIAAAAAAAIABQMAAAAAAAABAJABAAAAAExQAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAApsUkVgAAAAAAAAAAAAAAAAAAAAAAABAAaQBjAG8AbgBmAG8AbgB0AAAADgBSAGUAZwB1AGwAYQByAAAAFgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAAQAGkAYwBvAG4AZgBvAG4AdAAAAAAAAAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzJW7kk6AAABfAAAAFZjbWFwbyWiuQAAAfAAAAHeZ2x5ZsG49g0AAAPgAAAECGhlYWQPk2N3AAAA4AAAADZoaGVhB94DiAAAALwAAAAkaG10eBvpAAAAAAHUAAAAHGxvY2EDfgQyAAAD0AAAABBtYXhwARYAYgAAARgAAAAgbmFtZT5U/n0AAAfoAAACbXBvc3RVALKVAAAKWAAAAGIAAQAAA4D/gABcBAAAAAAABAAAAQAAAAAAAAAAAAAAAAAAAAcAAQAAAAEAAFYkxaZfDzz1AAsEAAAAAADWOg+9AAAAANY6D70AAP+/BAADQQAAAAgAAgAAAAAAAAABAAAABwBWAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQP9AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABAAHjnSwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAPpAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAUAAAADAAAALAAAAAQAAAGGAAEAAAAAAIAAAwABAAAALAADAAoAAAGGAAQAVAAAAA4ACAACAAYAeOYn5mPnIucu50v//wAAAHjmJ+Zj5yLnLudL//8AAAAAAAAAAAAAAAAAAQAOAA4ADgAOAA4ADgAAAAEABgACAAMABAAFAAABBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAABYAAAAAAAAAAYAAAB4AAAAeAAAAAEAAOYnAADmJwAAAAYAAOZjAADmYwAAAAIAAOciAADnIgAAAAMAAOcuAADnLgAAAAQAAOdLAADnSwAAAAUAAAAAAAAAdgDSAS4BXAHaAgQABQAA/+EDvAMYABMAKAAxAEQAUAAAAQYrASIOAh0BISc0LgIrARUhBRUXFA4DJyMnIQcjIi4DPQEXIgYUFjI2NCYXBgcGDwEOAR4BMyEyNicuAicBNTQ+AjsBMhYdAQEZGxpTEiUcEgOQAQoYJx6F/koCogEVHyMcDz4t/kksPxQyIBMIdwwSEhkSEowIBgUFCAICBA8OAW0XFgkFCQoG/qQFDxoVvB8pAh8BDBknGkxZDSAbEmGING4dJRcJAQGAgAETGyAOpz8RGhERGhF8GhYTEhkHEA0IGBoNIyQUAXfkCxgTDB0m4wAAAAAFAAD/3wPDAx8AEQAeACsALAA1AAAFISIuATY3AT4BMhYXAR4BDgEBBgcBBhYXIT4BJwEmAyImNRE0NjIWFREUBgcjPgEyFhQGIiYDUf1eIjQbAxABVBEvNS8RAVUQAxs0/o0SEf6sEBQiAqIiFBD+qxASDhISHBISDjABGygbGygbIRouOh4CYR4hIR79nx46LhoDAAEd/Z4dIgEBIh0CYh3+IhIOASAOEhIO/uAOEnAUGxspGxsAAwAA/98DoAMgABcAJwA3AAABJiIPAScuAQYUHwEWHwEWMj8BNjcBNiYTIS4BJxE+ATchHgEXEQ4BASIGFREUFjMhMjY1ETQmIwLXChkK6mcKGRQJfwMEAwYMBgQDBAEACgFg/YApNgEBNikCgCk2AQE2/VcOEhIOAoAOEhIOAfcJCexqCQETGgqCAgIDAgIDAQMBBAka/fMBNikCgCk2AQE2Kf2AKTYC/xIO/YAOEhIOAoAOEgAAAAEAAAAAA4MCXwAXAAABJgYHAScmIgYUFwUWHwEWMj8BNjcBNjQDeAoaCf5V6QoaEwoBAQMEAwYMBgQDBAHBCQJWCQEK/jrmCRQZCv0DAQMCAwICAwHfChkAAAAABQAA/78DwQNBAAwAGQAvADkAVQAAJSImNRE0NjIWFREUBiMiJjURNDYyFhURFAYBIzUuASchDgEHFSMiBhQWMyEyNjQmJTQ2MyEyFh0BIQEhLgEnETQ2MhYVERQWMyEyNjURPgEyFhURDgECYA4SEhwSEs4OEhIcEhIB8qABNij+vyk2AaAOEhIOA0AOEhL9khIOAUENEv6AAaD+QCk2ARMbEhMNAcAOEgESGxIBNoASDgFgDhISDv6gDhISDgFgDhISDv6gDhICIEApNgEBNilAEhwSEhwSQA4SEg5A/SABNikB4A4SEg7+IA4SEg4B3w4SEg7+ISk2AAAAAQAA/90C6QMoABIAAAEWFwEWDgEnASY3ATYXHgEHAQYBewcDAVASCioT/oohIQFwEhkWDQ3+qgMBgwUD/rERMA4PAXUiIQFxEwYHKBP+qgMAAAAAAAASAN4AAQAAAAAAAAAVAAAAAQAAAAAAAQAIABUAAQAAAAAAAgAHAB0AAQAAAAAAAwAIACQAAQAAAAAABAAIACwAAQAAAAAABQALADQAAQAAAAAABgAIAD8AAQAAAAAACgArAEcAAQAAAAAACwATAHIAAwABBAkAAAAqAIUAAwABBAkAAQAQAK8AAwABBAkAAgAOAL8AAwABBAkAAwAQAM0AAwABBAkABAAQAN0AAwABBAkABQAWAO0AAwABBAkABgAQAQMAAwABBAkACgBWARMAAwABBAkACwAmAWkKQ3JlYXRlZCBieSBpY29uZm9udAppY29uZm9udFJlZ3VsYXJpY29uZm9udGljb25mb250VmVyc2lvbiAxLjBpY29uZm9udEdlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC5odHRwOi8vZm9udGVsbG8uY29tAAoAQwByAGUAYQB0AGUAZAAgAGIAeQAgAGkAYwBvAG4AZgBvAG4AdAAKAGkAYwBvAG4AZgBvAG4AdABSAGUAZwB1AGwAYQByAGkAYwBvAG4AZgBvAG4AdABpAGMAbwBuAGYAbwBuAHQAVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG4AZgBvAG4AdABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAQIBAwEEAQUBBgEHAQgAAXgEd2FybhFmYW5neGluZ3h1YW56aG9uZwZ4dWFuemUHc2hhbmNodQZpLWxlZnQAAAAA') format('embedded-opentype'), /* IE6-IE8 */
+ url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAdEAAsAAAAACrwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kk6Y21hcAAAAYAAAACGAAAB3m8lorlnbHlmAAACCAAAAxYAAAQIwbj2DWhlYWQAAAUgAAAALwAAADYPk2N3aGhlYQAABVAAAAAcAAAAJAfeA4hobXR4AAAFbAAAABMAAAAcG+kAAGxvY2EAAAWAAAAAEAAAABADfgQybWF4cAAABZAAAAAfAAAAIAEWAGJuYW1lAAAFsAAAAUUAAAJtPlT+fXBvc3QAAAb4AAAATAAAAGJVALKVeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/ss4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDz3Zm7438AQw9zA0AAUZgTJAQAqoAy9eJzFkTEKwzAMRZ/qxJRSeogsWUoukrVD91wiY6ce1TpG+hUHQnqBfPMM/4MlIwEtkMRTNGBfjNBHqa154rbmDS/5B1cuZObSl8k7H3xcFuX/fpfpzX7CZ1VIqqfeljlNdl7ro+7r/d5cTGTe0BdLX4m8TBXND+8qsT0fKrFBHyu0P19hH+AAAHicTVLBi9xUHH7fezOZTZzJTF4yySbZyUwSN5np1tk6ZrK44uwUvCgeKu22eNGtF0GwtIdSQcHBIogoiH+ALkUQ1Iv3Qi3evXqSrShCiyc9eErWX2ZadsOP917e+/3e+37f97E6Y8e/i7tinZlsyJ5lL7ELjEHZQqTzHsI0G/MtdMN617F0kcZp2IijsXgRTqRY9iTPEkdpKG3oCPBcOMnTMU8xzWb8BUzsHuD63kW5uSHFF9DW0+Dj8hV+B91+vNGePVO+fHbPmgzMtVtNKV0pP1tT6vU1zmttHe86tlpXNaX8pt72unf7I95H0029V19vDXx59ZPsWm/TUYHFAqY/0L/dMzyD4gPPNqXb6LTW1r1W/LSFW38+tW42e8kfjL6q1yPxs+gzgwVsi51lU9oMozHyHcwIsENd6IDSgGI74QwpEhElUyPLJ3bXsJRGXGVZSpSI14o3oswXHVwytqfbBvY7ws/Kz6VR/tCxIn4nsjrl9x2pS7khpX4O/tCnCL3xbsCvBmEYFF8Hu2NPMPSKr3oREPX4W70ykjoGVKSXD3R53fL9ke8zscR9KAbMYSnbIX2SqI10DMXqw6aY7FUd5IkZjpEaM+yEARyDOokUgm1XwlATScx/1Vzt0duaa6kfippQmkpN1MA0HBSLUQ7kI76aiysVBr6oRvynqn+/o8L0tI84FxQQqKle8e9J/qiq58dSLxZPColvVKSL2/xNwk2YidY0Ids49VOYM/Ge5qnl/kPNMzXgBNV9lV9WoZW7f6mWqxX06OrtI819ouU9cV+cZ03msm32PNtnbPO0WPHpH8RToibU0ejGlXMrSrJkM8tpQT4NsWTucfZjwiqtu8QiP1ip+Mtqwj+HyIflPWr8sGpWzGksviSizrdkucBhOacj05dmCz/pEtKXyBd0fLDUdVlzsuaD+YrBeXX3hqwu0+fFgHbwYJm0tAOOlutwlK94Pf6NPxRDRiyDXGvrlVWJTScAeRfvNwQuSO2MWX4ahrguXbvVKr8TuF0X5Y/GOb2Nm1GIG6bSGJq0T1f+Dy+JqQMAAHicY2BkYGAA4jCVVUnx/DZfGbhZGEDgmhX/XgT9fz8LA7MjkMvBwAQSBQABFAj/AHicY2BkYGBu+N/AEMPCAAJAkpEBFbADAEcNAnB4nGNhYGBgfsnAwMKAiQEWswEFAAAAAAAAdgDSAS4BXAHaAgR4nGNgZGBgYGcIY2BlAAEmIOYCQgaG/2A+AwAR4AF5AHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgJ2RiZGZkYWRlZGNkZ2Rg4GxgqU8sShPMC0xL70iE4hLE/OqMvLz0tnArFT24ozEvOSMUrZM3ZzUtBIGBgDMKhGR') format('woff'),
+ url('data:image/ttf;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzJW7kk6AAABfAAAAFZjbWFwbyWiuQAAAfAAAAHeZ2x5ZsG49g0AAAPgAAAECGhlYWQPk2N3AAAA4AAAADZoaGVhB94DiAAAALwAAAAkaG10eBvpAAAAAAHUAAAAHGxvY2EDfgQyAAAD0AAAABBtYXhwARYAYgAAARgAAAAgbmFtZT5U/n0AAAfoAAACbXBvc3RVALKVAAAKWAAAAGIAAQAAA4D/gABcBAAAAAAABAAAAQAAAAAAAAAAAAAAAAAAAAcAAQAAAAEAAFYkqmJfDzz1AAsEAAAAAADWOg+9AAAAANY6D70AAP+/BAADQQAAAAgAAgAAAAAAAAABAAAABwBWAAUAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQP9AZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABAAHjnSwOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAPpAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAUAAAADAAAALAAAAAQAAAGGAAEAAAAAAIAAAwABAAAALAADAAoAAAGGAAQAVAAAAA4ACAACAAYAeOYn5mPnIucu50v//wAAAHjmJ+Zj5yLnLudL//8AAAAAAAAAAAAAAAAAAQAOAA4ADgAOAA4ADgAAAAEABgACAAMABAAFAAABBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAABYAAAAAAAAAAYAAAB4AAAAeAAAAAEAAOYnAADmJwAAAAYAAOZjAADmYwAAAAIAAOciAADnIgAAAAMAAOcuAADnLgAAAAQAAOdLAADnSwAAAAUAAAAAAAAAdgDSAS4BXAHaAgQABQAA/+EDvAMYABMAKAAxAEQAUAAAAQYrASIOAh0BISc0LgIrARUhBRUXFA4DJyMnIQcjIi4DPQEXIgYUFjI2NCYXBgcGDwEOAR4BMyEyNicuAicBNTQ+AjsBMhYdAQEZGxpTEiUcEgOQAQoYJx6F/koCogEVHyMcDz4t/kksPxQyIBMIdwwSEhkSEowIBgUFCAICBA8OAW0XFgkFCQoG/qQFDxoVvB8pAh8BDBknGkxZDSAbEmGING4dJRcJAQGAgAETGyAOpz8RGhERGhF8GhYTEhkHEA0IGBoNIyQUAXfkCxgTDB0m4wAAAAAFAAD/3wPDAx8AEQAeACsALAA1AAAFISIuATY3AT4BMhYXAR4BDgEBBgcBBhYXIT4BJwEmAyImNRE0NjIWFREUBgcjPgEyFhQGIiYDUf1eIjQbAxABVBEvNS8RAVUQAxs0/o0SEf6sEBQiAqIiFBD+qxASDhISHBISDjABGygbGygbIRouOh4CYR4hIR79nx46LhoDAAEd/Z4dIgEBIh0CYh3+IhIOASAOEhIO/uAOEnAUGxspGxsAAwAA/98DoAMgABcAJwA3AAABJiIPAScuAQYUHwEWHwEWMj8BNjcBNiYTIS4BJxE+ATchHgEXEQ4BASIGFREUFjMhMjY1ETQmIwLXChkK6mcKGRQJfwMEAwYMBgQDBAEACgFg/YApNgEBNikCgCk2AQE2/VcOEhIOAoAOEhIOAfcJCexqCQETGgqCAgIDAgIDAQMBBAka/fMBNikCgCk2AQE2Kf2AKTYC/xIO/YAOEhIOAoAOEgAAAAEAAAAAA4MCXwAXAAABJgYHAScmIgYUFwUWHwEWMj8BNjcBNjQDeAoaCf5V6QoaEwoBAQMEAwYMBgQDBAHBCQJWCQEK/jrmCRQZCv0DAQMCAwICAwHfChkAAAAABQAA/78DwQNBAAwAGQAvADkAVQAAJSImNRE0NjIWFREUBiMiJjURNDYyFhURFAYBIzUuASchDgEHFSMiBhQWMyEyNjQmJTQ2MyEyFh0BIQEhLgEnETQ2MhYVERQWMyEyNjURPgEyFhURDgECYA4SEhwSEs4OEhIcEhIB8qABNij+vyk2AaAOEhIOA0AOEhL9khIOAUENEv6AAaD+QCk2ARMbEhMNAcAOEgESGxIBNoASDgFgDhISDv6gDhISDgFgDhISDv6gDhICIEApNgEBNilAEhwSEhwSQA4SEg5A/SABNikB4A4SEg7+IA4SEg4B3w4SEg7+ISk2AAAAAQAA/90C6QMoABIAAAEWFwEWDgEnASY3ATYXHgEHAQYBewcDAVASCioT/oohIQFwEhkWDQ3+qgMBgwUD/rERMA4PAXUiIQFxEwYHKBP+qgMAAAAAAAASAN4AAQAAAAAAAAAVAAAAAQAAAAAAAQAIABUAAQAAAAAAAgAHAB0AAQAAAAAAAwAIACQAAQAAAAAABAAIACwAAQAAAAAABQALADQAAQAAAAAABgAIAD8AAQAAAAAACgArAEcAAQAAAAAACwATAHIAAwABBAkAAAAqAIUAAwABBAkAAQAQAK8AAwABBAkAAgAOAL8AAwABBAkAAwAQAM0AAwABBAkABAAQAN0AAwABBAkABQAWAO0AAwABBAkABgAQAQMAAwABBAkACgBWARMAAwABBAkACwAmAWkKQ3JlYXRlZCBieSBpY29uZm9udAppY29uZm9udFJlZ3VsYXJpY29uZm9udGljb25mb250VmVyc2lvbiAxLjBpY29uZm9udEdlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC5odHRwOi8vZm9udGVsbG8uY29tAAoAQwByAGUAYQB0AGUAZAAgAGIAeQAgAGkAYwBvAG4AZgBvAG4AdAAKAGkAYwBvAG4AZgBvAG4AdABSAGUAZwB1AGwAYQByAGkAYwBvAG4AZgBvAG4AdABpAGMAbwBuAGYAbwBuAHQAVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG4AZgBvAG4AdABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAQIBAwEEAQUBBgEHAQgAAXgEd2FybhFmYW5neGluZ3h1YW56aG9uZwZ4dWFuemUHc2hhbmNodQZpLWxlZnQAAAAA') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('data:image/svg;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiID4KPCEtLQoyMDEzLTktMzA6IENyZWF0ZWQuCi0tPgo8c3ZnPgo8bWV0YWRhdGE+CkNyZWF0ZWQgYnkgaWNvbmZvbnQKPC9tZXRhZGF0YT4KPGRlZnM+Cgo8Zm9udCBpZD0iaWNvbmZvbnQiIGhvcml6LWFkdi14PSIxMDI0IiA+CiAgPGZvbnQtZmFjZQogICAgZm9udC1mYW1pbHk9Imljb25mb250IgogICAgZm9udC13ZWlnaHQ9IjUwMCIKICAgIGZvbnQtc3RyZXRjaD0ibm9ybWFsIgogICAgdW5pdHMtcGVyLWVtPSIxMDI0IgogICAgYXNjZW50PSI4OTYiCiAgICBkZXNjZW50PSItMTI4IgogIC8+CiAgICA8bWlzc2luZy1nbHlwaCAvPgogICAgCiAgICA8Z2x5cGggZ2x5cGgtbmFtZT0ieCIgdW5pY29kZT0ieCIgaG9yaXotYWR2LXg9IjEwMDEiCmQ9Ik0yODEgNTQzcS0yNyAtMSAtNTMgLTFoLTgzcS0xOCAwIC0zNi41IC02dC0zMi41IC0xOC41dC0yMyAtMzJ0LTkgLTQ1LjV2LTc2aDkxMnY0MXEwIDE2IC0wLjUgMzB0LTAuNSAxOHEwIDEzIC01IDI5dC0xNyAyOS41dC0zMS41IDIyLjV0LTQ5LjUgOWgtMTMzdi05N2gtNDM4djk3ek05NTUgMzEwdi01MnEwIC0yMyAwLjUgLTUydDAuNSAtNTh0LTEwLjUgLTQ3LjV0LTI2IC0zMHQtMzMgLTE2dC0zMS41IC00LjVxLTE0IC0xIC0yOS41IC0wLjUKdC0yOS41IDAuNWgtMzJsLTQ1IDEyOGgtNDM5bC00NCAtMTI4aC0yOWgtMzRxLTIwIDAgLTQ1IDFxLTI1IDAgLTQxIDkuNXQtMjUuNSAyM3QtMTMuNSAyOS41dC00IDMwdjE2N2g5MTF6TTE2MyAyNDdxLTEyIDAgLTIxIC04LjV0LTkgLTIxLjV0OSAtMjEuNXQyMSAtOC41cTEzIDAgMjIgOC41dDkgMjEuNXQtOSAyMS41dC0yMiA4LjV6TTMxNiAxMjNxLTggLTI2IC0xNCAtNDhxLTUgLTE5IC0xMC41IC0zN3QtNy41IC0yNXQtMyAtMTV0MSAtMTQuNQp0OS41IC0xMC41dDIxLjUgLTRoMzdoNjdoODFoODBoNjRoMzZxMjMgMCAzNCAxMnQyIDM4cS01IDEzIC05LjUgMzAuNXQtOS41IDM0LjVxLTUgMTkgLTExIDM5aC0zNjh6TTMzNiA0OTh2MjI4cTAgMTEgMi41IDIzdDEwIDIxLjV0MjAuNSAxNS41dDM0IDZoMTg4cTMxIDAgNTEuNSAtMTQuNXQyMC41IC01Mi41di0yMjdoLTMyN3oiIC8+CiAgICAKCiAgICAKICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJ3YXJuIiB1bmljb2RlPSImIzU4OTc5OyIgZD0iTTg0OS4xMi0zMi43MDQgMTc0Ljg4LTMyLjcwNGMtNDUuMjE2IDAtODEuNTM2IDE3LjcyOC05OS42OCA0OC42NC0xOC4xNDQgMzAuOTEyLTE1LjkzNiA3MS4yOTYgNi4wOCAxMTAuNzUyTDQyMS40NzIgNzM2LjM1MmMyMi4xNDQgMzkuNzQ0IDU1LjA3MiA2Mi41MjggOTAuMzA0IDYyLjUyOHM2OC4xMjgtMjIuNzUyIDkwLjMzNi02Mi40NjRsMzQwLjU0NC02MDkuNzkyYzIyLjAxNi0zOS40NTYgMjQuMjg4LTc5LjgwOCA2LjExMi0xMTAuNzJDOTMwLjY1Ni0xNS4wMDggODk0LjMwNC0zMi43MDQgODQ5LjEyLTMyLjcwNHpNNTExLjgwOCA3MzQuODhjLTExLjIgMC0yNC4wMzItMTEuMTA0LTM0LjQzMi0yOS42OTZMMTM3LjE4NCA5NS40NTZjLTEwLjY1Ni0xOS4xMzYtMTMuMTUyLTM2LjMyLTYuNzg0LTQ3LjE2OCA2LjM2OC0xMC44MTYgMjIuNTkyLTE3LjAyNCA0NC40OC0xNy4wMjRsNjc0LjI0IDBjMjEuOTIgMCAzOC4xMTIgNi4xNzYgNDQuNDggMTcuMDI0IDYuMzM2IDEwLjgxNiAzLjg3MiAyOC02LjgxNiA0Ny4xMzZMNTQ2LjI0IDcwNS4xODRDNTM1Ljg3MiA3MjMuNzc2IDUyMi45NzYgNzM0Ljg4IDUxMS44MDggNzM0Ljg4ek01MTIgMjU2Yy0xNy42NjQgMC0zMiAxNC4zMDQtMzIgMzJsMCAyODhjMCAxNy42NjQgMTQuMzM2IDMyIDMyIDMyczMyLTE0LjMzNiAzMi0zMmwwLTI4OEM1NDQgMjcwLjMwNCA1MjkuNjY0IDI1NiA1MTIgMjU2ek01MTIgMTQzLjg3Mm0tNDggMGExLjUgMS41IDAgMSAwIDk2IDAgMS41IDEuNSAwIDEgMC05NiAwWiIgIGhvcml6LWFkdi14PSIxMDI0IiAvPgoKICAgIAogICAgPGdseXBoIGdseXBoLW5hbWU9ImZhbmd4aW5neHVhbnpob25nIiB1bmljb2RlPSImIzU5MTcwOyIgZD0iTTcyNi45NzY2OTcgNTAyLjgxNTg1OGMtMTIuNTQzNjkgMTIuNDQ3MzU5LTMyLjgzMTcxNiAxMi4zMjAwNjUtNDUuMjQ4MTEyLTAuMjU2MzFMNDQ4LjQ0NzI1MiAyNjYuNzUxMjQzbC0xMDMuMjYzNTQgMTA2LjExMjE4OWMtMTIuMzUyNzQ4IDEyLjcwMzY2OS0zMi42MDgwOSAxMi45MjcyOTUtNDUuMjQ4MTEyIDAuNjM5OTE0LTEyLjY3MjcwNS0xMi4zMjAwNjUtMTIuOTU5OTc4LTMyLjYwODA5LTAuNjM5OTE0LTQ1LjI0ODExMmwxMjYuMDE2NjExLTEyOS41MDM0NTRjMC4wNjM2NDctMC4wOTYzMzEgMC4xOTI2NjItMC4wOTYzMzEgMC4yNTYzMS0wLjE5MjY2MiAwLjA2MzY0Ny0wLjA2MzY0NyAwLjA5NjMzMS0wLjE5MjY2MiAwLjE1OTk3OC0wLjI1NjMxIDIuMDE2MDczLTEuOTgzMzg5IDQuNTEyMDgyLTMuMTk5NTcgNi44ODA3OTYtNC41NDQ3NjUgMS4yNDcxNDQtMC42NzI1OTggMi4yMzk2OTktMS43OTI0NDcgMy41MTk1MjctMi4zMDMzNDYgMy44NzIxNjgtMS41OTk3ODUgOC4wMDA2NDUtMi4zOTk2NzcgMTIuMDk2NDM5LTIuMzk5Njc3IDQuMDY0ODMgMCA4LjEyNzk0IDAuNzk5ODkyIDExLjk2NzQyNCAyLjMzNjAzIDEuMjQ3MTQ0IDAuNTEyNjE5IDIuMjA4NzM1IDEuNTM2MTM4IDMuMzkyMjMyIDIuMTc2MDUyIDIuMzk5Njc3IDEuMzQzNDc1IDQuODk1Njg2IDIuNTI4NjkyIDYuOTQ0NDQzIDQuNTQ0NzY1IDAuMDYzNjQ3IDAuMDYzNjQ3IDAuMDk2MzMxIDAuMTkyNjYyIDAuMTkyNjYyIDAuMjU2MzEgMC4wNjM2NDcgMC4wOTYzMzEgMC4xNTk5NzggMC4xMjcyOTUgMC4yNTYzMSAwLjE5MjY2MmwyNTYuMjIzNjI2IDI1OS4wMDg2MjhDNzM5LjY0NzY4MiA0NzAuMTExNDM3IDczOS41MjAzODcgNDkwLjM2ODQ5OSA3MjYuOTc2Njk3IDUwMi44MTU4NTh6TTgzMi0zMi4wMDA4NmwtNjQwIDBjLTUyLjkyODggMC05Ni4wMDA4NiA0My4wNzIwNi05Ni4wMDA4NiA5NS45OTkxNGwwIDY0MGMwIDUyLjkyODggNDMuMDcyMDYgOTYuMDAwODYgOTYuMDAwODYgOTYuMDAwODZsNjQwIDBjNTIuOTI3MDggMCA5NS45OTkxNC00My4wNzIwNiA5NS45OTkxNC05Ni4wMDA4NmwwLTY0MEM5MjguMDAwODYgMTEuMDcxMiA4ODQuOTI4OC0zMi4wMDA4NiA4MzItMzIuMDAwODZ6TTE5MiA3MzUuOTk5MTRjLTE3LjYzMjAzOSAwLTMyLjAwMDg2LTE0LjM2ODgyMS0zMi4wMDA4Ni0zMi4wMDA4NmwwLTY0MGMwLTE3LjY2NDcyMiAxNC4zNjg4MjEtMzEuOTk5MTQgMzIuMDAwODYtMzEuOTk5MTRsNjQwIDBjMTcuNjY0NzIyIDAgMzEuOTk5MTQgMTQuMzM2MTM4IDMxLjk5OTE0IDMxLjk5OTE0bDAgNjQwYzAgMTcuNjMyMDM5LTE0LjMzNjEzOCAzMi4wMDA4Ni0zMS45OTkxNCAzMi4wMDA4NkwxOTIgNzM1Ljk5OTE0eiIgIGhvcml6LWFkdi14PSIxMDI0IiAvPgoKICAgIAogICAgPGdseXBoIGdseXBoLW5hbWU9Inh1YW56ZSIgdW5pY29kZT0iJiM1OTE4MjsiIGQ9Ik04ODcuOTA0NzQ0IDU5Ny43OTE0OGMtMTIuODYzNjQ3IDEyLjA2Mzc1NS0zMy4xNTE2NzMgMTEuNDg3NDg4LTQ1LjIxNTQyOC0xLjQwODg0M0w0MTUuOTM1NDkzIDE0Mi4wMTYxODEgMTgyLjgxNTg1OCAzNzEuNzEyNjE5Yy0xMi42MDczMzggMTIuNDE2Mzk2LTMyLjg2NDQgMTIuMjg3MzgxLTQ1LjI4MDc5Ni0wLjMxOTk1Ny0xMi40MTYzOTYtMTIuNTc2Mzc0LTEyLjI1NjQxNy0zMi44NjQ0IDAuMzUyNjQxLTQ1LjI0ODExMmwyNTYuNDc5OTM1LTI1Mi42NzE0MTVjMC4wOTYzMzEtMC4wOTYzMzEgMC4yMjM2MjYtMC4xMjcyOTUgMC4zMTk5NTctMC4yMjM2MjZzMC4xMjcyOTUtMC4yMjM2MjYgMC4yMjM2MjYtMC4zMTk5NTdjMi4wMTYwNzMtMS45MTk3NDIgNC40NDg0MzQtMy4wMDg2MjggNi43ODQ0NjQtNC4yODg0NTYgMS4xNTI1MzMtMC42NzI1OTggMi4xNDMzNjgtMS42NjM0MzIgMy4zNTk1NDgtMi4xNDMzNjggMy43NzU4MzctMS40NzI0OSA3Ljc3NTI5OS0yLjIzOTY5OSAxMS43NDM3OTgtMi4yMzk2OTkgNC4xOTIxMjUgMCA4LjM4NDI0OSAwLjgzMjU3NiAxMi4yODczODEgMi40OTYwMDkgMS4zMTI1MTIgMC41NDM1ODMgMi4zMzYwMyAxLjY2MzQzMiAzLjU1MjIxMSAyLjM2ODcxNCAyLjM5OTY3NyAxLjQwODg0MyA0Ljg5NTY4NiAyLjU5MjM0IDYuOTQ0NDQzIDQuNjcyMDYgMC4wOTYzMzEgMC4wOTYzMzEgMC4xMjcyOTUgMC4yNTYzMSAwLjIyMzYyNiAwLjM1MjY0MSAwLjA2MzY0NyAwLjA5NjMzMSAwLjE5MjY2MiAwLjEyNzI5NSAwLjI4NzI3MyAwLjIyMzYyNkw4ODkuMjc3NDYzIDU1Mi41Nzk0OTJDOTAxLjQzOTI2OSA1NjUuNDA4NzM1IDkwMC43NjgzOTEgNTg1LjY2NDA3NyA4ODcuOTA0NzQ0IDU5Ny43OTE0OHoiICBob3Jpei1hZHYteD0iMTAyNCIgLz4KCiAgICAKICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJzaGFuY2h1IiB1bmljb2RlPSImIzU5MjExOyIgZD0iTTYwNy44OTc4NjcgMTI3Ljk1Njk5NmMtMTcuNzE3NDUzIDAtMzEuOTk0NjI1IDE0LjI3NzE3MS0zMS45OTQ2MjUgMzEuOTk0NjI1TDU3NS45MDMyNDIgNTEyLjA2NDUwNWMwIDE3LjcxNzQ1MyAxNC4yNzcxNzEgMzEuOTk0NjI1IDMxLjk5NDYyNSAzMS45OTQ2MjVzMzEuOTk0NjI1LTE0LjI3NzE3MSAzMS45OTQ2MjUtMzEuOTk0NjI1bDAtMzUxLjk0MDg3QzYzOS44OTI0OTEgMTQyLjQwNjE4MiA2MjUuNjE1MzIgMTI3Ljk1Njk5NiA2MDcuODk3ODY3IDEyNy45NTY5OTZ6TTQxNS45MzAxMTkgMTI3Ljk1Njk5NmMtMTcuNzE3NDUzIDAtMzEuOTk0NjI1IDE0LjI3NzE3MS0zMS45OTQ2MjUgMzEuOTk0NjI1TDM4My45MzU0OTUgNTEyLjA2NDUwNWMwIDE3LjcxNzQ1MyAxNC4yNzcxNzEgMzEuOTk0NjI1IDMxLjk5NDYyNSAzMS45OTQ2MjUgMTcuNzE3NDUzIDAgMzEuOTk0NjI1LTE0LjI3NzE3MSAzMS45OTQ2MjUtMzEuOTk0NjI1bDAtMzUxLjk0MDg3QzQ0Ny45MjQ3NDQgMTQyLjQwNjE4MiA0MzMuNjQ3NTczIDEyNy45NTY5OTYgNDE1LjkzMDExOSAxMjcuOTU2OTk2ek05MjguMDE2MTI2IDY3Mi4wMzc2MjhsLTE1OS45NzMxMjMgMEw3NjguMDQzMDA0IDczNi4wMjY4NzdjMCA1Mi45ODAzNDYtNDIuNjU5NDk5IDk1Ljk4Mzg3NC05NS4yOTU4MTcgOTUuOTgzODc0TDM1MS45NDA4NyA4MzIuMDEwNzUxYy01Mi45ODAzNDYgMC05NS45ODM4NzQtNDMuMDAzNTI4LTk1Ljk4Mzg3NC05NS45ODM4NzRsMC02My45ODkyNDktMTU5Ljk3MzEyMyAwYy0xNy43MTc0NTMgMC0zMS45OTQ2MjUtMTQuMjc3MTcxLTMxLjk5NDYyNS0zMS45OTQ2MjVzMTQuMjc3MTcxLTMxLjk5NDYyNSAzMS45OTQ2MjUtMzEuOTk0NjI1bDgzMi4wMzIyNTMgMGMxNy43MTc0NTMgMCAzMS45OTQ2MjUgMTQuMjc3MTcxIDMxLjk5NDYyNSAzMS45OTQ2MjVTOTQ1LjczMzU4IDY3Mi4wMzc2MjggOTI4LjAxNjEyNiA2NzIuMDM3NjI4ek0zMTkuOTQ2MjQ2IDczNi4wMjY4NzdjMCAxNy41NDU0MzkgMTQuNDQ5MTg1IDMxLjk5NDYyNSAzMS45OTQ2MjUgMzEuOTk0NjI1bDMyMC44MDYzMTYgMGMxNy41NDU0MzkgMCAzMS4zMDY1NjgtMTQuMTA1MTU3IDMxLjMwNjU2OC0zMS45OTQ2MjVsMC02My45ODkyNDlMMzE5Ljk0NjI0NiA2NzIuMDM3NjI4IDMxOS45NDYyNDYgNzM2LjAyNjg3NyAzMTkuOTQ2MjQ2IDczNi4wMjY4Nzd6TTczNi4wNDgzNzktNjQuMDEwNzUxIDI4OC4xMjM2MzUtNjQuMDEwNzUxYy01Mi45ODAzNDYgMC05NS45ODM4NzQgNDMuMDAzNTI4LTk1Ljk4Mzg3NCA5NS45ODM4NzRMMTkyLjEzOTc2MSA1MTIuNDA4NTM0YzAgMTcuNzE3NDUzIDE0LjI3NzE3MSAzMS45OTQ2MjUgMzEuOTk0NjI1IDMxLjk5NDYyNXMzMS45OTQ2MjUtMTQuMjc3MTcxIDMxLjk5NDYyNS0zMS45OTQ2MjVsMC00ODAuNDM1NDExYzAtMTcuNzE3NDUzIDE0LjQ0OTE4NS0zMS45OTQ2MjUgMzEuOTk0NjI1LTMxLjk5NDYyNWw0NDguMDk2NzU4IDBjMTcuNzE3NDUzIDAgMzEuOTk0NjI1IDE0LjI3NzE3MSAzMS45OTQ2MjUgMzEuOTk0NjI1TDc2OC4yMTUwMTggNTExLjIwNDQzNWMwIDE3LjcxNzQ1MyAxNC4yNzcxNzEgMzEuOTk0NjI1IDMxLjk5NDYyNSAzMS45OTQ2MjVzMzEuOTk0NjI1LTE0LjI3NzE3MSAzMS45OTQ2MjUtMzEuOTk0NjI1bDAtNDc5LjIzMTMxMkM4MzIuMDMyMjUzLTIwLjgzNTIwOSA3ODkuMDI4NzI1LTY0LjAxMDc1MSA3MzYuMDQ4Mzc5LTY0LjAxMDc1MXoiICBob3Jpei1hZHYteD0iMTAyNCIgLz4KCiAgICAKICAgIDxnbHlwaCBnbHlwaC1uYW1lPSJpLWxlZnQiIHVuaWNvZGU9IiYjNTg5MTk7IiBkPSJNMzc5LjEwNCAzODYuNjU2YzQuNjQ3LTMuNDY1IDcuNzA3LTUuMjExIDEwLjEwMi03LjYxNCAxMTEuODgyLTExMS43NzcgMjIzLjY4LTIyMy42NDIgMzM1LjU4NC0zMzUuMzk1IDExLjQ5OC0xMS40ODIgMTcuNjAxLTI0LjQzMiAxNC4wNjAtNDAuODkzLTYuNTQ5LTMwLjQ0NC00Mi43MjQtNDMuMDU2LTY2LjcwMi0yMy4yMDctMi42MzggMi4xNzktNS4wMDYgNC42OTEtNy40MyA3LjExNi0xMjIuMTQ3IDEyMi4xMzEtMjQ0LjI5MSAyNDQuMjY4LTM2Ni40MzQgMzY2LjQxMS0yMi4yMTggMjIuMjE4LTIyLjIzNiA0NC45NDQtMC4wMzMgNjcuMTQzIDEyMi43NDkgMTIyLjc1NSAyNDUuNTQ4IDI0NS40NTEgMzY4LjE4NSAzNjguMzE2IDEyLjE2NiAxMi4xOTEgMjYuMDYyIDE3LjU4OSA0Mi43MzQgMTMuMzI3IDI4Ljk1Ny03LjM5NSA0MC4yNDctNDIuNTExIDIxLjQ2Mi02NS44MjctMi4zMjMtMi44ODEtNS4wNDYtNS40NTctNy42NjktOC4wODMtMTExLjIxNC0xMTEuMjI4LTIyMi40MzUtMjIyLjQ1OS0zMzMuNzA2LTMzMy42MzQtMi40MDctMi40MDQtNS40NjktNC4xNjYtMTAuMTQ4LTcuNjZ6IiAgaG9yaXotYWR2LXg9IjEwMjQiIC8+CgogICAgCgoKICA8L2ZvbnQ+CjwvZGVmcz48L3N2Zz4K') format('svg'); /* iOS 4.1- */
+}
+
+.iconfont {
+ font-family:"iconfont" !important;
+ font-size:14px;
+ font-style:normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.icon-warn:before { content: "\e663"; }
+.icon-selectall:before { content: "\e722"; }
+.icon-selected:before { content: "\e72e"; }
+.icon-removeall:before { content: "\e74b"; }
+.icon-back:before { content: "\e627"; }
+
+/* css animate */
+.vivify {
+ -webkit-animation-duration: .1s;
+ -webkit-animation-fill-mode: both;
+ animation-duration: .1s;
+ animation-fill-mode: both; }
+.fadeInRight {
+ -webkit-animation-name: fadeInRight;
+ animation-name: fadeInRight;
+ -webkit-animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
+ animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); }
+
+@keyframes fadeInRight {
+ 0% {
+ -webkit-transform: translate3d(100px, 0, 0);
+ transform: translate3d(100px, 0, 0);
+ opacity: 0; }
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ opacity: 1; } }
+.fadeInLeft {
+ -webkit-animation-name: fadeInLeft;
+ animation-name: fadeInLeft;
+ -webkit-animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
+ animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955); }
+
+@keyframes fadeInLeft {
+ 0% {
+ -webkit-transform: translate3d(-100px, 0, 0);
+ transform: translate3d(-100px, 0, 0);
+ opacity: 0; }
+ 100% {
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ opacity: 1; } } \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.js b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.js
new file mode 100644
index 00000000..8c052221
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/jquery/jquery.selectmenu.js
@@ -0,0 +1,1597 @@
+/**
+ * @summary SelectMenu
+ * @desc Simple, easily and diversity menu solution
+ * @file selectmenu.js
+ * @version 2.1
+ * @author TerryZeng
+ * @contact https://terryz.github.io/
+ * @license MIT License
+ *
+ * depend on:
+ * jQuery1.x
+ *
+ */
+;(function($){
+ "use strict";
+ /**
+ * Version: v3.6.1
+ * The MIT License: Copyright (c) 2010-2017 LiosK.
+ * Link: https://github.com/LiosK/UUID.js
+ */
+ var UUID;
+ UUID=function(f){function a(){}a.generate=function(){var b=a._getRandomInt,c=a._hexAligner;return c(b(32),8)+"-"+c(b(16),4)+"-"+c(16384|b(12),4)+"-"+c(32768|b(14),4)+"-"+c(b(48),12)};a._getRandomInt=function(b){if(0>b||53<b)return NaN;var c=0|1073741824*Math.random();return 30<b?c+1073741824*(0|Math.random()*(1<<b-30)):c>>>30-b};a._hexAligner=function(b,c){for(var a=b.toString(16),d=c-a.length,e="0";0<d;d>>>=1,e+=e)d&1&&(a=e+a);return a};a.overwrittenUUID=f;"undefined"!==typeof module&&module&&module.exports&&
+ (module.exports=a);return a}(UUID);
+ /**
+ * Default options
+ */
+ var defaults = {
+ /**
+ * Menu data source
+ * @type array | function
+ * @example
+ * array:[{a:1,b:2,c:3},{...}]
+ * function: function(){ return [{...}];}
+ * return data format is same to array
+ */
+ data: undefined,
+ /**
+ * Show quick search input element, work on advance menu mode
+ * @type boolean
+ * @default true
+ */
+ search : true,
+ /**
+ * Title bar text, set false to close title bar
+ * @type string | boolean
+ * @default 'SelectMenu'
+ */
+ title : 'SelectMenu',
+ /**
+ * Regular menu mode
+ * @type boolean
+ * @default false
+ */
+ regular : false,
+ /**
+ * Mouse right click to show menu
+ * @type boolean
+ * @default false
+ */
+ rightClick : false,
+ /**
+ * Menu show arrow, look like a bubble
+ * @type boolean
+ * @default false
+ */
+ arrow : false,
+ /**
+ * Alignment direction
+ * @type string
+ * @enum
+ * 'left'
+ * 'center'
+ * 'right'
+ * @default 'left'
+ */
+ position : 'left',
+ /**
+ * Embedded to page
+ * @type boolean
+ * @default false
+ */
+ embed : false,
+ /**
+ * Language ('cn', 'ja', 'en', 'es', 'pt-br')
+ * @type string
+ * @default 'cn'
+ */
+ lang: 'cn',
+ /**
+ * Multiple select mode(tags)
+ * @type boolean
+ * @default false
+ */
+ multiple: false,
+ /**
+ * Menu result list size, the number mean is visible menu item amount
+ * @type number
+ * @default 10
+ */
+ listSize : 10,
+ /**
+ * Maximum selected item limit in multiple select mode, set 0 to unlimited
+ * @type number
+ * @default 0 (unlimited)
+ */
+ maxSelectLimit: 0,
+ /**
+ * Close result list after menu item selected, work on multiple select mode
+ * @type boolean
+ * @default true
+ */
+ selectToCloseList: false,
+ /**
+ * Set a key to selected menu item when plugin init complete, work on multiple select mode
+ * the key will match to keyField
+ * @type string
+ */
+ initSelected: undefined,
+ /**
+ * Key field to return data
+ * @type string
+ * @default 'id'
+ */
+ keyField: 'id',
+ /**
+ * Show content field
+ * @type string
+ * @default 'name'
+ */
+ showField: 'name',
+ /**
+ * Data field in search, not set default used showField
+ * @type string
+ */
+ searchField : undefined,
+ /**
+ * Filter type ('AND' or 'OR')
+ * @type string default: 'AND'
+ */
+ andOr: 'AND',
+ /**
+ * Sort order, not set default used showField
+ * @type array
+ * @example
+ * orderBy : ['id desc']//order by id desc
+ */
+ orderBy: undefined,
+ /**
+ * Max item size
+ * @type number
+ */
+ pageSize: 100,
+ /**
+ * Menu item result format
+ * @type function
+ * @param data {object} menu item data
+ * @return string
+ */
+ formatItem : undefined,
+ /**
+ * -----------------------------------------Event--------------------------------------------
+ */
+ /**
+ * Menu item select callback
+ * @type function
+ * @param data {array[Object]} selected items data
+ */
+ eSelect : undefined,
+ /**
+ * Multiple group data type tab switch callback
+ * @type function
+ * @param index {number}
+ */
+ eTabSwitch : undefined,
+ /**
+ * Menu hide callback
+ * @type function
+ * @param data {array[Object]} selected items data
+ */
+ eHidden : undefined
+ };
+
+
+ /**
+ * @constructor
+ * @param {Object} input - menu caller
+ * @param {Object} option - menu init option
+ */
+ var SelectMenu = function(input, option) {
+ this.target = input;
+ this.setOption(option);
+ if(this.option.embed && !$(input).is('div')){
+ console.warn('SelectMenu embed mode need a "div" container element!');
+ return;
+ }
+
+ this.setLanguage();
+ this.setCssClass();
+ this.setProp();
+
+ if(option.regular) this.setRegularMenu();
+ else this.setElem();
+
+ if(!option.rightClick) this.populate();
+
+ this.eInput();
+ if(!option.embed) this.eWhole();
+ this.atLast();
+ };
+ /**
+ * Plugin version number
+ */
+ SelectMenu.version = '2.1';
+ /**
+ * Plugin object cache key
+ */
+ SelectMenu.dataKey = 'selectMenuObject';
+ /**
+ * Data source type
+ * List type
+ */
+ SelectMenu.dataTypeList = 'SelectMenuList';
+ /**
+ * Group type
+ */
+ SelectMenu.dataTypeGroup = 'SelectMenuGroup';
+ /**
+ * Regular menu type
+ */
+ SelectMenu.dataTypeMenu = 'SelectMenuMenu';
+ /**
+ * Initial plugin option
+ * @param {Object} option
+ */
+ SelectMenu.prototype.setOption = function(option) {
+ //if not set, default used showField set field
+ option.searchField = option.searchField || option.showField;
+
+ if(option.regular && option.title === defaults.title) option.title = false;
+ //Close arrow in embed and mouse right click mode
+ if(option.embed || option.rightClick) option.arrow = false;
+
+ option.andOr = option.andOr.toUpperCase();
+ if(option.andOr!=='AND' && option.andOr!=='OR') option.andOr = 'AND';
+
+ option.orderBy = (option.orderBy === undefined) ? option.showField : option.orderBy;
+
+ //Multiple field sort
+ //Example: [ ['id', 'ASC'], ['name', 'DESC'] ]
+ option.orderBy = this.setOrderbyOption(option.orderBy, option.showField);
+
+ if($.type(option.data) === 'string'){
+ option.autoSelectFirst = false;
+ }
+ if($.type(option.listSize) !== 'number' || option.listSize < 0) option.listSize = 12;
+
+ this.option = option;
+ };
+
+ /**
+ * Initial order
+ * @param {Array} arg_order
+ * @param {string} arg_field
+ * @return {Array}
+ */
+ SelectMenu.prototype.setOrderbyOption = function(arg_order, arg_field) {
+ var arr = [],orders = [];
+ if (typeof arg_order == 'object') {
+ for (var i = 0; i < arg_order.length; i++) {
+ orders = $.trim(arg_order[i]).split(' ');
+ arr[i] = (orders.length == 2) ? orders: [orders[0], 'ASC'];
+ }
+ } else {
+ orders = $.trim(arg_order).split(' ');
+ arr[0] = (orders.length == 2) ? orders: (orders[0].match(/^(ASC|DESC)$/i)) ? [arg_field, orders[0]] : [orders[0], 'ASC'];
+ }
+ return arr;
+ };
+
+ /**
+ * i18n
+ */
+ SelectMenu.prototype.setLanguage = function() {
+ var message;
+ switch (this.option.lang) {
+ // 中文
+ case 'cn':
+ message = {
+ select_all_btn: '选择所有 (或当å‰é¡µç­¾) 项目',
+ remove_all_btn: '清除所有选中的项目',
+ close_btn: '关闭èœå• (Escé”®)',
+ loading: '读å–中...',
+ select_ng: '请注æ„:请从列表中选择.',
+ select_ok: 'OK : å·²ç»é€‰æ‹©.',
+ not_found: '无查询结果',
+ ajax_error: '连接到æœåŠ¡å™¨æ—¶å‘生错误ï¼',
+ max_selected: '最多åªèƒ½é€‰æ‹© max_selected_limit 个项目'
+ };
+ break;
+ // English
+ case 'en':
+ message = {
+ select_all_btn: 'Select All (Tabs) items',
+ remove_all_btn: 'Clear all selected items',
+ close_btn: 'Close Menu (Esc key)',
+ loading: 'loading...',
+ select_ng: 'Attention : Please choose from among the list.',
+ select_ok: 'OK : Correctly selected.',
+ not_found: 'not found',
+ ajax_error: 'An error occurred while connecting to server.',
+ max_selected: 'You can only select up to max_selected_limit items'
+ };
+ break;
+ // Japanese
+ case 'ja':
+ message = {
+ select_all_btn: 'ã™ã¹ã¦ã® (ã¾ãŸã¯ç¾åœ¨ã®ã‚¿ãƒ–) 項目をé¸æŠž',
+ remove_all_btn: 'é¸æŠžã—ãŸã™ã¹ã¦ã®é …目をクリアã™ã‚‹',
+ close_btn: 'é–‰ã˜ã‚‹ (Tabキー)',
+ loading: '読ã¿è¾¼ã¿ä¸­...',
+ select_ng: 'æ³¨æ„ : リストã®ä¸­ã‹ã‚‰é¸æŠžã—ã¦ãã ã•ã„',
+ select_ok: 'OK : æ­£ã—ãé¸æŠžã•ã‚Œã¾ã—ãŸã€‚',
+ not_found: '(0 件)',
+ ajax_error: 'サーãƒã¨ã®é€šä¿¡ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚',
+ max_selected: '最多㧠max_selected_limit ã®ãƒ—ロジェクトをé¸ã¶ã“ã¨ã—ã‹ã§ãã¾ã›ã‚“'
+ };
+ break;
+ // German
+ case 'de':
+ message = {
+ select_all_btn: 'Wählen Sie alle (oder aktuellen Registerkarten) aus',
+ remove_all_btn: 'Alle ausgewählten Elemente löschen',
+ close_btn: 'Schließen (Tab)',
+ loading: 'lade...',
+ select_ng: 'Achtung: Bitte wählen Sie aus der Liste aus.',
+ select_ok: 'OK : Richtig ausgewählt.',
+ not_found: 'nicht gefunden',
+ ajax_error: 'Bei der Verbindung zum Server ist ein Fehler aufgetreten.',
+ max_selected: 'Sie können nur bis zu max_selected_limit Elemente auswählen'
+ };
+ break;
+ // Spanish
+ case 'es':
+ message = {
+ select_all_btn: 'Seleccionar todos los elementos (o la pestaña actual)',
+ remove_all_btn: 'Borrar todos los elementos seleccionados',
+ close_btn: 'Cerrar (tecla TAB)',
+ loading: 'Cargando...',
+ select_ng: 'Atencion: Elija una opcion de la lista.',
+ select_ok: 'OK: Correctamente seleccionado.',
+ not_found: 'no encuentre',
+ ajax_error: 'Un error ocurrió mientras conectando al servidor.',
+ max_selected: 'Solo puedes seleccionar hasta max_selected_limit elementos'
+ };
+ break;
+ // Brazilian Portuguese
+ case 'pt-br':
+ message = {
+ select_all_btn: 'Selecione todos os itens (ou guia atual)',
+ remove_all_btn: 'Limpe todos os itens selecionados',
+ close_btn: 'Fechar (tecla TAB)',
+ loading: 'Carregando...',
+ select_ng: 'Atenção: Escolha uma opção da lista.',
+ select_ok: 'OK: Selecionado Corretamente.',
+ not_found: 'não encontrado',
+ ajax_error: 'Um erro aconteceu enquanto conectando a servidor.',
+ max_selected: 'Você só pode selecionar até max_selected_limit itens'
+ };
+ break;
+ }
+ this.message = message;
+ };
+
+ /**
+ * CSS classname set
+ */
+ SelectMenu.prototype.setCssClass = function() {
+ var css_class = {
+ target_clicked : 'sm_target_clicked',
+ container: 'sm_container',
+ container_open: 'sm_container_open',
+ container_embed: 'sm_embed',
+ header: 'sm_header',
+ re_area: 'sm_result_area',
+ re_tabs: 'sm_result_tabs',
+ re_list: 'sm_list_mode',
+ control_box: 'sm_control_box',
+ two_btn: 'sm_two_btn',
+ element_box: 'sm_element_box',
+ results: 'sm_results',
+ re_off: 'sm_results_off',
+ select: 'sm_over',
+ selected_icon: 'sm_selected_icon',
+ item_text: 'sm_item_text',
+ select_ok: 'sm_select_ok',
+ select_ng: 'sm_select_ng',
+ selected: 'sm_selected',
+ input_off: 'sm_input_off',
+ message_box: 'sm_message_box',
+
+ btn_close: 'sm_close_button',
+ btn_selectall: 'sm_selectall_button',
+ btn_removeall: 'sm_removeall_button',
+ btn_on: 'sm_btn_on',
+ btn_out: 'sm_btn_out',
+ btn_back: 'sm_sub_back',
+ input: 'sm_input',
+ input_area: 'sm_input_area',
+ clear_btn: 'sm_clear_btn',
+
+ menu_root: 'sm_menu_root',
+ menu_divider: 'sm_divider',
+ menu_regular: 'sm_regular',
+ menu_arrow: 'sm_arrow',
+ menu_arrow_have_title : 'sm_have_title',
+ menu_disabled: 'sm_disabled',
+ menu_header: 'sm_header',
+ menu_caret: 'sm_caret',
+ menu_sub_menu: 'sm_sub_menu',
+ menu_sub_item: 'sm_sub_item',
+ menu_sub_header: 'sm_sub_header',
+
+
+ direction_top : 'sm_arrow_top',
+ direction_bottom : 'sm_arrow_bottom'
+ };
+ this.css_class = css_class;
+ this.template = {
+ msg :{
+ maxSelectLimit: 'max_selected_limit'
+ }
+ };
+ };
+
+ /**
+ * Internal variable initial
+ */
+ SelectMenu.prototype.setProp = function() {
+ this.prop = {
+ //selected menu item keys
+ values : [],
+ data : undefined,
+ //multiple group data current data index
+ data_index : 0,
+ key_select: false,
+ prev_value: '',
+ selected_text : '',
+ last_input_time: undefined,
+ //menu data type
+ data_type : SelectMenu.dataTypeList,
+ //id prefix
+ menu_tab_id_prefix : 'selectmenu_tab_',
+ menu_code_prefix: 'selectmenu_',
+ //mouse x point
+ x : undefined,
+ //mouse y point
+ y : undefined
+ };
+ };
+
+ /**
+ * Data source type check
+ */
+ SelectMenu.prototype.checkDataType = function(d){
+ var self = this,p = this.option;
+ if(d && $.isArray(d) && d.length){
+ if(p.regular) return SelectMenu.dataTypeMenu;
+ else{
+ var row = d[0];
+ if(row.hasOwnProperty('title') && row.hasOwnProperty('list') && $.isArray(row.list)){
+ return SelectMenu.dataTypeGroup;
+ }else return SelectMenu.dataTypeList;
+ }
+ }else return null;
+ };
+
+ /**
+ * Menu structure build
+ */
+ SelectMenu.prototype.setElem = function() {
+ var self = this, p = this.option, css = this.css_class;
+ // 1. build dom element
+ var elem = {};
+
+ elem.container = p.embed ? $(self.target).addClass(css.container_embed) : $('<div>');
+ elem.container.addClass(css.container).addClass(css.direction_bottom);
+ if(p.title){
+ elem.header = $('<div>').addClass(css.header);
+ elem.header.append('<h3>' + p.title + '</h3>');
+ if(p.multiple){
+ elem.selectAllButton = $('<button type="button"><i class="iconfont icon-selectall"></i></button>')
+ .attr('title',this.message.select_all_btn)
+ .addClass(css.btn_selectall);
+ elem.removeAllButton = $('<button type="button"><i class="iconfont icon-removeall"></i></button>')
+ .attr('title',this.message.remove_all_btn)
+ .addClass(css.btn_removeall);
+ elem.header.append(elem.selectAllButton);
+ elem.header.append(elem.removeAllButton);
+ }
+
+ if(!p.embed){
+ elem.closeButton = $('<button type="button">×</button>')
+ .attr('title',self.message.close_btn)
+ .addClass(css.btn_close);
+ elem.header.append(elem.closeButton);
+ }
+ }
+
+ elem.inputArea = $('<div>').addClass(css.input_area);
+ elem.input = $('<input type="text" autocomplete="off">').addClass(css.input);
+
+ //Result list
+ elem.resultArea = $('<div>').addClass(css.re_area);
+ elem.resultTabs = $('<div>').addClass(css.re_tabs);
+ elem.results = $('<ul>').addClass(css.results);
+ elem.selectedIcon = $('<i class="iconfont icon-selected">');
+
+ // 2. DOM element put
+ if(p.arrow){
+ elem.arrow = $('<div>').addClass(css.menu_arrow);
+ if(p.title) elem.arrow.addClass(css.menu_arrow_have_title);
+ elem.container.append(elem.arrow);
+ }
+ if(p.title) elem.container.append(elem.header);
+ if(p.search){
+ elem.container.append(elem.inputArea);
+ elem.inputArea.append(elem.input);
+ }
+ elem.container.append(elem.resultTabs).append(elem.resultArea);
+ elem.resultArea.append(elem.results);
+
+ if(!p.embed) $(document.body).append(elem.container);
+
+ this.elem = elem;
+ };
+
+ /**
+ * Initial regular menu frame
+ */
+ SelectMenu.prototype.setRegularMenu = function(){
+ var p = this.option, self = this, css = this.css_class;
+ var elem = {};
+ elem.container = p.embed ? $(self.target).addClass(css.container_embed) : $('<div>');
+ elem.container.addClass(css.container)
+ .addClass(css.direction_bottom)
+ .addClass(css.menu_regular);
+ if(p.title){
+ elem.header = $('<div>').addClass(css.header);
+ elem.header.append('<h3>' + p.title + '</h3>');
+ if(!p.embed)
+ elem.closeButton = $('<button type="button">×</button>')
+ .attr('title',self.message.close_btn)
+ .addClass(css.btn_close);
+ }
+
+ elem.resultArea = $('<div>').addClass(css.re_area);
+ elem.results = $('<ul>').addClass(css.results).addClass(css.menu_root);
+
+ if(p.arrow){
+ elem.arrow = $('<div>').addClass(css.menu_arrow);
+ if(p.title) elem.arrow.addClass(css.menu_arrow_have_title);
+ elem.container.append(elem.arrow);
+ }
+
+ if(p.title){
+ elem.container.append(elem.header);
+ if(!p.embed) elem.header.append(elem.closeButton);
+ }
+ elem.container.append(elem.resultArea);
+ elem.resultArea.append(elem.results);
+
+ if(!p.embed) $(document.body).append(elem.container);
+ this.elem = elem;
+ };
+
+ /**
+ * Regular menu item render
+ */
+ SelectMenu.prototype.regularMenuInit = function(){
+ var d = this.prop.data, p = this.option, self = this, css = this.css_class, el = self.elem;
+ var showMenu = function(){
+ if(!p.embed){
+ this.calcResultsSize(this);
+ el.container.addClass(css.container_open);
+ }
+ };
+ if(el.results.find('li').length && !$.isFunction(p.data)){
+ showMenu.call(self);
+ return;
+ }
+ if(d && $.isArray(d) && d.length){
+ var buildMenu = function(menudata, ul){
+ if(ul.hasClass(css.menu_root)) ul.empty().hide();
+
+ $.each(menudata,function(i,row){
+ if(!row.content ||
+ (!row.header &&
+ !row.url &&
+ !row.callback &&
+ !row.menus &&
+ row.content !== css.menu_divider))
+ return true;
+ var li = $('<li>');
+ if(row.content === css.menu_divider){
+ li.addClass(css.menu_divider);
+ }else{
+ var a = $('<a>').html(row.content).attr('href',
+ (row.url && !row.disabled)?row.url:'javascript:void(0);');
+ if(row.callback && $.isFunction(row.callback) && !row.url){
+ a.on('click.selectMenu',function(e){
+ e.stopPropagation();
+ if(row.disabled) return;
+ row.callback();
+ self.hideResults(self);
+ });
+ }
+ //build sub menus
+ if(row.menus && $.isArray(row.menus) && row.menus.length){
+ var itemCode = self.prop.menu_code_prefix + UUID.generate();
+ a.attr({
+ 'href': 'javascript:void(0);',
+ 'item_code': itemCode
+ }).append($('<span>').addClass(css.menu_caret)).addClass(css.menu_sub_item);
+ var subMenu = $('<ul>').attr('id', itemCode).addClass(css.results).addClass(css.menu_sub_menu);
+
+ //build sub menu header bar
+ var backBtn = $('<button type="button">').addClass(css.btn_back).append('<i class="iconfont icon-back"></i>');
+ var header = $('<li>').append(backBtn).append($('<p>').text(row.content)).addClass(css.menu_sub_header);
+
+ subMenu.append(header).append($('<li>').addClass(css.menu_divider));
+ el.resultArea.append(subMenu);
+ buildMenu(row.menus, subMenu);
+ }
+ li.prepend(a);
+ if(row.disabled) li.addClass(css.menu_disabled);
+ if(row.header) li.addClass(css.menu_header);
+ }
+ ul.append(li);
+ });
+ if(!ul.hasClass(css.menu_sub_menu)) ul.show();
+ };
+
+ el.resultArea.find('ul.'+css.results+':not(.'+css.menu_root+')').remove();
+ buildMenu(d, el.results);
+
+ //sub menus event bind
+ el.resultArea.find('a.'+css.menu_sub_item).off('click.SelectMenu').on('click.SelectMenu', function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ var $this = $(this),
+ $menu = $this.closest('ul.'+css.results),
+ $subMenu = $('#'+$this.attr('item_code'));
+ if($subMenu.length){
+ $menu.hide();
+ /*
+ $subMenu.css({ marginLeft: 60 }).show().animate({
+ marginLeft: 0
+ },100);
+ */
+ $subMenu.addClass('vivify fadeInRight').show();
+ }
+ });
+ //back button
+ el.resultArea.find('button.'+css.btn_back).off('click.SelectMenu').on('click.SelectMenu', function(e){
+ var $btn = $(this),
+ $menu = $btn.closest('ul'),
+ $parentMenu = $('a[item_code="'+$menu.attr('id')+'"]').closest('ul');
+ $menu.hide();
+ $parentMenu.addClass('vivify fadeInLeft').show();
+ });
+
+ showMenu.call(self);
+ }
+ };
+
+ /**
+ * Show menu
+ * @param self
+ */
+ SelectMenu.prototype.showMenu = function(self){
+ self.populate();
+ if($(self.target).is('button'))
+ $(self.target).addClass(self.css_class.target_clicked);
+ };
+
+ /**
+ * Set menu item to selected
+ * @param self
+ * @param list - datasource
+ */
+ SelectMenu.prototype.setInitSelected = function(self, list){
+ var p = self.option;
+ if($.type(p.initSelected) !== 'undefined' &&
+ !p.regular && list && $.isArray(list) && list.length){
+ var str = String(p.initSelected),arr = str.split(',');
+ var matchItem = function(dataList){
+ $.each(dataList, function(i,row){
+ var id = String(row[p.keyField]);
+ if(id && $.inArray(id,arr) !== -1) self.prop.values.push(row);
+ });
+ };
+ if(self.prop.data_type === SelectMenu.dataTypeList){
+ matchItem(list);
+ }else if(self.prop.data_type === SelectMenu.dataTypeGroup){
+ $.each(list, function(i,group){
+ group && group.list && group.list.length && matchItem(group.list);
+ });
+ }
+ p.initSelected = undefined;
+ }
+ };
+
+ /**
+ * Menu frame event handle
+ */
+ SelectMenu.prototype.eInput = function() {
+ var self = this,p = this.option,el = self.elem;
+ if(!p.regular && p.search){
+ el.input.keyup(function(e) {
+ self.processKey(self, e);
+ }).keydown(function(e){
+ self.processControl(self, e);
+ });
+ }
+ if(p.title){
+ if(!p.embed){
+ el.closeButton.click(function(e){
+ self.hideResults(self);
+ });
+ }
+ if(!p.regular){
+ if(p.multiple){
+ el.selectAllButton.click(function(e){
+ e.stopPropagation();
+ self.selectAllLine(self);
+ });
+ el.removeAllButton.click(function(e){
+ e.stopPropagation();
+ self.clearAll(self);
+ });
+ }
+ }
+ }
+ if(!p.regular && self.prop.data_type === SelectMenu.dataTypeGroup){
+ el.resultTabs.off('click.selectMenu').on('click.selectMenu', 'a', function(e){
+ e.stopPropagation();
+ if(!$(this).hasClass('active')){
+ var li = $(this).closest('li');
+ li.siblings().children('a').removeClass('active');
+ $(this).addClass('active');
+ self.prop.data_index = parseInt($(this).attr('data_index'));
+ self.populate();
+ if(p.eTabSwitch && $.isFunction(p.eTabSwitch)){
+ var currentGroup = $.extend({}, self.prop.data[self.prop.data_index]);
+ //cut the list item
+ delete currentGroup.list;
+ p.eTabSwitch.call(this, self.prop.data_index, currentGroup);
+ }
+ }
+ });
+ }
+ if(p.rightClick){
+ $(self.target).on('contextmenu',function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ e.cancelBubble = true;
+ e.returnValue = false;
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft,
+ scrollY = document.documentElement.scrollTop || document.body.scrollTop;
+ self.prop.x = e.pageX || e.clientX + scrollX;
+ self.prop.y = e.pageY || e.clientY + scrollY;
+ if(!self.isVisible(self)) self.populate();
+ else self.calcResultsSize(self);
+ return false;
+ }).mouseup(function(e){
+ if(e.button != 2) self.hideResults(self);
+ });
+ self.hideResults(self);
+ }
+ };
+
+ /**
+ * Out of menu event bind
+ */
+ SelectMenu.prototype.eWhole = function() {
+ var self = this, css = this.css_class;
+ $(document).off('mouseup.selectMenu').on('mouseup.selectMenu',function(e) {
+ var srcEl = e.target || e.srcElement,
+ sm = $(srcEl).closest('div.' + css.container);
+ //out of menu area click, when menu is opened , hide it
+ $('div.' + css.container + '.' + css.container_open).each(function(){
+ var d = $(this).data(SelectMenu.dataKey);
+ if(this == sm[0] || d.target == srcEl || $(srcEl).closest(d.target).length) return;
+ d.hideResults(d);
+ });
+ });
+ };
+
+ /**
+ * Menu item event bind
+ */
+ SelectMenu.prototype.eResultList = function() {
+ var self = this,p = this.option,el = self.elem;
+ self.elem.results.children('li').mouseenter(function() {
+ if (self.prop.key_select) {
+ self.prop.key_select = false;
+ return;
+ }
+ if(!$(this).hasClass('sm_message_box')) $(this).addClass(self.css_class.select);
+ }).mouseleave(function(){
+ $(this).removeClass(self.css_class.select);
+ }).click(function(e) {
+ if (self.prop.key_select) {
+ self.prop.key_select = false;
+ return;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+
+ self.selectCurrentLine(self, false);
+ });
+ };
+
+ /**
+ * Reposition result list when list beyond the visible area
+ */
+ SelectMenu.prototype.eScroll = function(){
+ var self = this, css = this.css_class;
+ $(window).on('scroll.SelectMenu',function(e){
+ $('div.' + css.container + '.' + css.container_open).each(function(){
+ var d = $(this).data(SelectMenu.dataKey),
+ offset = d.elem.container.offset(),
+ screenScrollTop = $(window).scrollTop(),
+ docHeight = $(document).height(),//the document full height
+ viewHeight = $(window).height(),//browser visible area height
+ menuHeight = d.elem.container.outerHeight(),
+ menuBottom = offset.top + menuHeight,
+ hasOverflow = docHeight > viewHeight,
+ down = d.elem.container.hasClass(css.direction_bottom);
+ if(hasOverflow){
+ if(down){//show down
+ if(menuBottom > (viewHeight + screenScrollTop)) d.calcResultsSize(d);
+ }else{//show up
+ if(offset.top < screenScrollTop) d.calcResultsSize(d);
+ }
+ }
+ });
+ });
+ };
+
+ /**
+ * Closing work
+ * @param {Object} self
+ */
+ SelectMenu.prototype.atLast = function(self){
+ if(!self) self = this;
+ var p = self.option;
+ if(p.search && !p.regular && !p.embed && !p.rightClick) self.elem.input.focus();
+ self.elem.container.data(SelectMenu.dataKey,self);
+ if($(self.target).is('button,.btn') && !p.embed && !p.rightClick)
+ $(self.target).addClass(self.css_class.target_clicked);
+ }
+
+ /**
+ * Ajax request fail
+ * @param {Object} self
+ * @param {string} errorThrown
+ */
+ SelectMenu.prototype.ajaxErrorNotify = function(self, errorThrown) {
+ self.showMessage(self.message.ajax_error);
+ };
+
+ /**
+ * Show some message
+ * @param {Object} self
+ * @param msg {string}
+ */
+ SelectMenu.prototype.showMessage = function(self,msg){
+ if(!msg) return;
+ var msgLi = '<li class="sm_message_box"><i class="iconfont icon-warn"></i> '+msg+'</li>';
+ self.elem.results.empty().append(msgLi);
+ self.calcResultsSize(self);
+ self.elem.container.addClass(self.css_class.container_open);
+ self.elem.control.hide();
+ };
+
+ /**
+ * Check input to search
+ * @param {Object} self
+ */
+ SelectMenu.prototype.checkValue = function(self) {
+ var now_value = self.elem.input.val();
+ if (now_value != self.prop.prev_value) {
+ self.prop.prev_value = now_value;
+ self.suggest(self);
+ }
+ };
+
+ /**
+ * Input element event handle( regular letter )
+ * @param {Object} self
+ * @param {Object} e - event
+ */
+ SelectMenu.prototype.processKey = function(self, e){
+ if($.inArray(e.keyCode, [38, 40, 27, 9, 13]) === -1){
+ //if(e.keyCode != 16) self.setCssFocusedInput(self); // except Shift(16)
+ if($.type(self.option.data) === 'string'){
+ self.prop.last_input_time = e.timeStamp;
+ setTimeout(function(){
+ if((e.timeStamp - self.prop.last_input_time) === 0)
+ self.checkValue(self);
+ },self.option.inputDelay * 1000);
+ }else{
+ self.checkValue(self);
+ }
+ }
+ }
+
+ /**
+ * Input element event handle( control key )
+ * @param {Object} self
+ * @param {Object} e - event
+ */
+ SelectMenu.prototype.processControl = function(self, e) {
+ if (($.inArray(e.keyCode, [38, 40, 27, 9]) > -1 && self.elem.container.is(':visible')) ||
+ ($.inArray(e.keyCode, [13, 9]) > -1 && self.getCurrentLine(self))) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.cancelBubble = true;
+ e.returnValue = false;
+ switch (e.keyCode) {
+ case 38:// up
+ self.prop.key_select = true;
+ self.prevLine(self);
+ break;
+ case 40:// down
+ if (self.elem.results.children('li').length) {
+ self.prop.key_select = true;
+ self.nextLine(self);
+ } else self.suggest(self);
+ break;
+ case 9: // tab
+ self.selectCurrentLine(self, true);
+ //self.hideResults(self);
+ break;
+ case 13:// return
+ self.selectCurrentLine(self, true);
+ break;
+ case 27:// escape
+ self.hideResults(self);
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Populate menu data
+ */
+ SelectMenu.prototype.populate = function() {
+ var self = this, p = this.option;
+ if(!p.regular) self.elem.input.val('');
+ /**
+ * 1.Process data source
+ */
+ if(p.data){
+ if($.type(p.data) === 'array'){
+ self.prop.data = p.data;
+ }else if($.type(p.data) === 'function'){
+ self.prop.data = p.data();
+ }
+ }
+ //Check data type
+ if($.type(self.prop.data) === 'array')
+ this.prop.data_type = this.checkDataType(self.prop.data);
+ /**
+ * 2.Set menu init selected
+ */
+ if($.type(p.data) !== 'string') self.setInitSelected(self, self.prop.data);
+ /**
+ * 3.Show data
+ */
+ if(p.regular) self.regularMenuInit();
+ else self.suggest(self);
+
+ //scrolling listen
+ if(!p.embed) self.eScroll();
+ };
+
+ /**
+ * Search suggest
+ * @param {Object} self
+ */
+ SelectMenu.prototype.suggest = function(self) {
+ var q_word, p = self.option,
+ val = $.trim(self.elem.input.val());
+ if(p.multiple) q_word = val;
+ else{
+ if(val && val === self.prop.selected_text) q_word = '';
+ else q_word = val;
+ }
+ q_word = q_word.split(/[\s ]+/);
+ self.setLoading(self);
+ if ($.type(p.data) === 'array' || $.type(p.data) === 'function') self.search(self, q_word);
+ };
+
+ /**
+ * Loading
+ * @param {Object} self
+ */
+ SelectMenu.prototype.setLoading = function(self) {
+ if (self.elem.results.html() === '') {
+ //self.calcResultsSize(self);
+ if(!self.option.embed) self.elem.container.addClass(self.css_class.container_open);
+ }
+ };
+
+ /**
+ * Search / load menu data
+ * @param {Object} self
+ * @param {Array} q_word - query keywords
+ */
+ SelectMenu.prototype.search = function(self, q_word) {
+ var p = self.option, innerData = self.prop.data,
+ matched = [], esc_q = [], sorted = [], json = {}, i = 0, arr_reg = [];
+ do {
+ //'/\W/g'正则代表全部ä¸æ˜¯å­—æ¯ï¼Œæ•°å­—,下划线,汉字的字符
+ //å°†éžæ³•å­—符进行转义
+ esc_q[i] = q_word[i].replace(/\W/g, '\\$&').toString();
+ arr_reg[i] = new RegExp(esc_q[i], 'gi');
+ i++;
+ } while ( i < q_word.length );
+ var d = [];
+ if(self.prop.data_index > (innerData.length-1) || self.prop.data_index < 0) self.prop.data_index = 0;
+ if(self.prop.data_type === SelectMenu.dataTypeGroup){
+ d = innerData[self.prop.data_index].list;
+ }else d = innerData;
+ // SELECT * FROM data WHERE field LIKE q_word;
+ for (i = 0; i < d.length; i++) {
+ var flag = false;
+ var row = d[i];
+ for (var j = 0; j < arr_reg.length; j++) {
+ var itemText = row[p.searchField];
+ if(p.formatItem && $.isFunction(p.formatItem))
+ itemText = p.formatItem(row);
+ if (itemText.match(arr_reg[j])) {
+ flag = true;
+ if (p.andOr == 'OR') break;
+ } else {
+ flag = false;
+ if (p.andOr == 'AND') break;
+ }
+ }
+ if (flag) matched.push(row);
+ }
+
+ // (CASE WHEN ...) then ã order some field
+ var reg1 = new RegExp('^' + esc_q[0] + '$', 'gi'),
+ reg2 = new RegExp('^' + esc_q[0], 'gi'),
+ matched1 = [], matched2 = [], matched3 = [];
+ for (i = 0; i < matched.length; i++) {
+ var orderField = p.orderBy[0][0],
+ orderValue = String(matched[i][orderField]);
+ if (orderValue.match(reg1)) {
+ matched1.push(matched[i]);
+ } else if (orderValue.match(reg2)) {
+ matched2.push(matched[i]);
+ } else {
+ matched3.push(matched[i]);
+ }
+ }
+
+ if (p.orderBy[0][1].match(/^asc$/i)) {
+ matched1 = self.sortAsc(self, matched1);
+ matched2 = self.sortAsc(self, matched2);
+ matched3 = self.sortAsc(self, matched3);
+ } else {
+ matched1 = self.sortDesc(self, matched1);
+ matched2 = self.sortDesc(self, matched2);
+ matched3 = self.sortDesc(self, matched3);
+ }
+ sorted = sorted.concat(matched1).concat(matched2).concat(matched3);
+
+ /*
+ if (sorted.length === undefined || sorted.length === 0 ) {
+ self.notFoundSearch(self);
+ return;
+ }
+ */
+ //json.cnt_whole = sorted.length;
+
+ //Cache original row data
+ json.originalResult = [];
+ if(json.keyField === undefined) json.keyField = [];
+ if(json.candidate === undefined) json.candidate = [];
+
+ $.each(sorted, function(i,row){
+ if(row === undefined || $.type(row) !== 'object') return true;
+ json.originalResult.push(row);
+ if(row.hasOwnProperty(p.keyField) && row.hasOwnProperty(p.showField)){
+ json.keyField.push(row[p.keyField]);
+ json.candidate.push(row[p.showField]);
+ }
+ });
+
+ //json.cnt_page = json.candidate.length;
+ self.prepareResults(self, json, q_word);
+ };
+
+ /**
+ * Sort ascending
+ * @param {Object} self
+ * @param {Array} arr
+ */
+ SelectMenu.prototype.sortAsc = function(self, arr) {
+ arr.sort(function(a, b) {
+ var valA = a[self.option.orderBy[0][0]],
+ valB = b[self.option.orderBy[0][0]];
+ return $.type(valA) === 'number' ? valA - valB : String(valA).localeCompare(String(valB));
+ });
+ return arr;
+ };
+
+ /**
+ * Sort descending
+ * @param {Object} self
+ * @param {Array} arr
+ */
+ SelectMenu.prototype.sortDesc = function(self, arr) {
+ arr.sort(function(a, b) {
+ var valA = a[self.option.orderBy[0][0]],
+ valB = b[self.option.orderBy[0][0]];
+ return $.type(valA) === 'number' ? valB - valA : String(valB).localeCompare(String(valA));
+ });
+ return arr;
+ };
+
+ /**
+ * No result handle
+ * @param {Object} self
+ */
+ SelectMenu.prototype.notFoundSearch = function(self) {
+ self.elem.results.empty();
+ self.calcResultsSize(self);
+ self.elem.container.addClass(self.css_class.container_open);
+ self.setCssFocusedInput(self);
+ };
+
+ /**
+ * Prepare data to render menu item
+ * @param {Object} self
+ * @param {Object} json
+ * @param {Array} q_word - query keywords
+ */
+ SelectMenu.prototype.prepareResults = function(self, json, q_word) {
+ if (!json.keyField) json.keyField = false;
+
+ if (self.option.selectOnly &&
+ json.candidate.length === 1 &&
+ json.candidate[0] == q_word[0]) {
+ self.elem.hidden.val(json.keyField[0]);
+ this.setButtonAttrDefault();
+ }
+ var is_query = false;
+ if (q_word && q_word.length && q_word[0]) is_query = true;
+ //self.setInitSelected(self,json.originalResult);
+ self.displayResults(self, json, is_query);
+ };
+
+ /**
+ * Render menu item
+ * @param {Object} self
+ * @param {Object} json
+ * @param {boolean} is_query
+ */
+ SelectMenu.prototype.displayResults = function(self, json, is_query) {
+ var p = self.option, el = self.elem, css = self.css_class;
+ el.results.hide().empty();
+
+ // build tabs
+ if(self.prop.data_type === SelectMenu.dataTypeGroup) {
+ var ul = $('<ul>');
+ $.each(self.prop.data,function(i,row){
+ var a = $('<a href="javascript:void(0);">').html(row.title).attr({
+ 'tab_id' : self.prop.menu_tab_id_prefix + (i+1),
+ 'data_index' : i
+ });
+ if(i === self.prop.data_index) a.addClass('active');
+ var li = $('<li>').append(a);
+ ul.append(li);
+ });
+ el.resultTabs.empty().append(ul);
+ }else{
+ el.resultTabs.hide();
+ if(p.title || p.search) el.resultArea.addClass(this.css_class.re_list);
+ }
+
+ if(p.multiple && $.type(p.maxSelectLimit) === 'number' && p.maxSelectLimit){
+ var selectedSize = self.prop.results.length;
+ if(selectedSize && selectedSize >= p.maxSelectLimit){
+ var msg = self.message.max_selected;
+ self.showMessage(self, msg.replace(self.template.msg.maxSelectLimit, p.maxSelectLimit));
+ return;
+ }
+ }
+
+ if(json.candidate.length){
+ var arr_candidate = json.candidate, arr_primary_key = json.keyField;
+ for (var i = 0; i < arr_candidate.length; i++) {
+ var itemText = '', custom = false, row = json.originalResult[i];
+ if(p.formatItem && $.isFunction(p.formatItem)){
+ try {
+ itemText = p.formatItem(row);
+ custom = true;
+ } catch (e) {
+ console.error('formatItem 内容格å¼åŒ–函数内容设置ä¸æ­£ç¡®ï¼');
+ itemText = arr_candidate[i];
+ }
+ }else itemText = arr_candidate[i];
+ var icon = $('<div>').html('<i class="iconfont icon-selected">').addClass(css.selected_icon),
+ text = $('<div>').html(itemText).addClass(css.item_text),
+ li = $('<li>').append(icon).append(text).attr('pkey' , arr_primary_key[i]);
+ if(!custom) li.attr('title',itemText);
+
+ //set selected item to highlight
+ if ($.inArray(row,self.prop.values) !== -1) {
+ li.addClass(css.selected);
+ }
+ //cache item data
+ li.data('dataObj',row);
+ el.results.append(li);
+ }
+ }else{
+ var li = '<li class="sm_message_box"><i class="iconfont icon-warn"></i> ' + self.message.not_found + '</li>';
+ el.results.append(li);
+ }
+ el.results.show();
+
+ self.calcResultsSize(self);
+ if(!p.embed) el.container.addClass(css.container_open);
+
+ //menu item event bind
+ self.eResultList();
+ //auto highlight first item in search, in have result and set autoSelectFirst to true situation
+ //if (is_query && json.candidate.length > 0 && p.autoSelectFirst) self.nextLine(self);
+ self.atLast(self);
+ };
+
+ /**
+ * Calculate menu position and size
+ * @param {Object} self
+ */
+ SelectMenu.prototype.calcResultsSize = function(self) {
+ var p = self.option, el = self.elem, css = self.css_class,
+ hasScroll = function(){
+ return $(document).height() > $(window).height();
+ };
+ var setListHeight = function(){
+ if(!p.regular){
+ //result list height
+ var itemHeight = el.results.find('li:first').outerHeight(),
+ listHeight = itemHeight * p.listSize;
+ el.results.css({
+ 'max-height':listHeight
+ });
+ }
+ };
+ var scrollFlag = hasScroll();
+ var rePosition = function(){
+ var menuHeight = el.container.outerHeight(),
+ screenScrollTop = $(window).scrollTop(),
+ viewHeight = $(window).height();
+ if(p.rightClick){
+ var top = self.prop.y;
+ if((self.prop.y + menuHeight) > (screenScrollTop + viewHeight))
+ top = self.prop.y - menuHeight;
+ return {top : top, left : self.prop.x};
+ }
+ var boxoffset = $(self.target).offset(),
+ t = boxoffset.top,
+ menuWidth = el.container.outerWidth(),
+ targetWidth = Math.round($(self.target)[0].getBoundingClientRect().width),
+ dist = 5;
+ t += $(self.target).outerHeight() + dist;
+ if(p.arrow && !p.embed) t += el.arrow.outerHeight(true);
+
+ if((t + menuHeight) > (screenScrollTop + viewHeight)){
+ t = boxoffset.top - dist - menuHeight;
+ if(p.arrow && !p.embed) t -= el.arrow.outerHeight(true);
+ el.container.removeClass(css.direction_bottom).addClass(css.direction_top);
+ }else{
+ if(el.container.hasClass(css.direction_top))
+ el.container.removeClass(css.direction_top).addClass(css.direction_bottom);
+ }
+
+ var l = boxoffset.left;
+ switch (p.position){
+ case 'right':
+ l = l + targetWidth - menuWidth;
+ if(p.arrow) el.arrow.css('left',menuWidth - (targetWidth / 2));
+ break;
+ case 'center':
+ l = l + (targetWidth / 2) - (menuWidth / 2);
+ break;
+ case 'left':
+ default:
+ if(p.arrow) el.arrow.css('left',targetWidth / 2);
+ break;
+ }
+ return {top : t,left : l};
+ }
+ if(el.container.is(':visible')){
+ setListHeight();
+ if(!p.embed) el.container.offset(rePosition());
+ }else{
+ el.container.show(1,function(){
+ setListHeight();
+ $(this).offset(rePosition());
+ });
+ }
+ if(scrollFlag !== hasScroll()) el.container.offset(rePosition());
+ };
+
+ /**
+ *
+ */
+ SelectMenu.prototype.subMenuPosition = function(parent, menu){
+ var pOffset = $(parent).offset();
+ var t = pOffset.top,l = pOffset.left + $(parent).outerWidth() + 5;
+ };
+
+ /**
+ * Hide menu
+ * @param {Object} self
+ */
+ SelectMenu.prototype.hideResults = function(self) {
+ var p = self.option;
+ if (p.autoFillResult) {
+ //self.selectCurrentLine(self, true);
+ }
+
+ if(!p.regular) self.elem.results.empty();
+ if(!p.embed){
+ self.elem.container.removeClass(self.css_class.container_open).hide();
+ if($(self.target).is('button,.btn')) $(self.target).removeClass(self.css_class.target_clicked);
+ }
+ self.elem.resultArea.find('ul.'+self.css_class.results).not('.'+self.css_class.menu_root).hide();
+ //remove animate class
+ self.elem.results.removeClass('vivify').removeClass('fadeInLeft').show();
+ $(window).off('scroll.SelectMenu');
+ if(!p.regular && p.eHidden && $.isFunction(p.eHidden)) p.eHidden.call(self, self.prop.values.concat());
+ };
+ /**
+ * do something after select/unSelect action
+ * @param {Object} self
+ */
+ SelectMenu.prototype.afterAction = function(self){
+ //$(self.elem.input).change();
+ if(self.option.multiple){
+ if(self.option.selectToCloseList){
+ self.hideResults(self);
+ self.elem.input.blur();
+ }else{
+ //self.suggest(self);
+ self.elem.input.focus();
+ }
+ }else{
+ self.hideResults(self);
+ self.elem.input.blur();
+ }
+ };
+
+ /**
+ * Get current menu item
+ * @param {Object} self
+ */
+ SelectMenu.prototype.getCurrentLine = function(self) {
+ if (self.elem.container.is(':hidden')) return false;
+ var obj = self.elem.results.find('li.' + self.css_class.select);
+ if (obj.length) return obj;
+ else return false;
+ };
+
+ /**
+ * Get selected menu item
+ * @param self
+ * @returns {*}
+ */
+ SelectMenu.prototype.getSelectedLine = function(self) {
+ if (self.elem.container.is(':hidden')) return false;
+ var obj = self.elem.results.find('li.' + self.css_class.selected);
+ if (obj.length) return obj;
+ else return false;
+ };
+
+ /**
+ * Selected menu item and trigger select callback
+ * @param {Object} self
+ * @param {boolean} is_enter_key
+ */
+ SelectMenu.prototype.selectCurrentLine = function(self, is_enter_key) {
+ var current = self.getCurrentLine(self), p = self.option;
+ if (current) {
+ var rowData = current.data('dataObj'),
+ id = String(rowData[p.keyField]);
+ if($.inArray(rowData,self.prop.values) === -1){
+ if(!p.multiple) self.prop.values.splice(0,self.prop.values.length);
+ self.prop.values.push(rowData);
+ if(!p.multiple)
+ self.elem.results.find('li.' + self.css_class.selected).removeClass(self.css_class.selected);
+ current.addClass(self.css_class.selected);
+ } else{
+ self.prop.values.splice($.inArray(rowData,self.prop.values),1);
+ current.removeClass(self.css_class.selected);
+ }
+
+ //trigger callback
+ if(p.eSelect && $.isFunction(p.eSelect)){
+ if(p.multiple){
+ p.eSelect.call(self, self.prop.values.concat());
+ }else p.eSelect.call(self, [rowData]);
+ }
+
+ self.prop.prev_value = self.elem.input.val();
+ self.prop.selected_text = self.elem.input.val();
+ }
+ if(!p.embed) self.afterAction(self);
+ };
+
+ /**
+ * Select all menu item
+ * @param {Object} self
+ */
+ SelectMenu.prototype.selectAllLine = function(self){
+ self.elem.results.find('li').each(function(i,row){
+ var d = $(row).data('dataObj');
+ if($.inArray(d,self.prop.values) === -1) self.prop.values.push(d);
+ $(this).addClass(self.css_class.selected);
+ //limited max select items
+ /*
+ if($.type(self.option.maxSelectLimit) === 'number' &&
+ self.option.maxSelectLimit > 0 &&
+ self.option.maxSelectLimit === $('li.selected_tag',self.elem.element_box).length){
+ return false;
+ }
+ */
+ });
+ if(self.option.eSelect && $.isFunction(self.option.eSelect))
+ self.option.eSelect.call(self, self.prop.values.concat());
+ self.afterAction(self);
+ };
+ /**
+ * Clear all selected menu items
+ * @param {Object} self
+ */
+ SelectMenu.prototype.clearAll = function(self){
+ var p = self.option, el = self.elem;
+ el.input.val('');
+ el.results.find('li').each(function(i,row){
+ $(this).removeClass(self.css_class.selected);
+ });
+ self.prop.values.splice(0,self.prop.values.length);
+ self.afterAction(self);
+ if (p.eSelect && $.isFunction(p.eSelect)) p.eSelect.call(self, []);
+ };
+
+ /**
+ * Select next menu item
+ * @param {Object} self
+ */
+ SelectMenu.prototype.nextLine = function(self) {
+ var obj = self.getCurrentLine(self), el = self.elem, idx;
+ if (!obj) idx = -1;
+ else {
+ idx = el.results.children('li').index(obj);
+ obj.removeClass(self.css_class.select);
+ }
+ idx++;
+ var size = el.results.find('li').length;
+ if(idx === size) idx = size - 1;
+ if (idx < size) {
+ var next = el.results.children('li').eq(idx);
+ next.addClass(self.css_class.select);
+
+ var itemHeight = el.results.find('li:first').outerHeight(),
+ curTop = next.position().top,
+ curScrollTop = el.resultArea.scrollTop(),
+ listHeight = el.resultArea.outerHeight(),
+ dist = curTop + itemHeight - listHeight;
+ if((curTop + itemHeight) > listHeight)
+ el.resultArea.scrollTop(curScrollTop + dist);
+ }
+ };
+
+ /**
+ * Select previous menu item
+ * @param {Object} self
+ */
+ SelectMenu.prototype.prevLine = function(self) {
+ var el = self.elem, idx, obj = self.getCurrentLine(self);
+ if (!obj) idx = el.results.children('li').length;
+ else {
+ idx = el.results.children('li').index(obj);
+ obj.removeClass(self.css_class.select);
+ }
+ idx--;
+ if(idx < 0) idx = 0;
+ if (idx > -1) {
+ var prev = el.results.children('li').eq(idx),
+ itemHeight = el.results.find('li:first').outerHeight(),
+ curTop = prev.position().top,
+ curScrollTop = el.resultArea.scrollTop(),
+ listHeight = el.resultArea.outerHeight();
+ prev.addClass(self.css_class.select);
+ if((curTop > (curScrollTop + listHeight)) || (curTop < curScrollTop))
+ el.resultArea.scrollTop(curScrollTop - (0 - curTop));
+ }
+ };
+
+ /**
+ * Check menu visible
+ * @param self
+ */
+ SelectMenu.prototype.isVisible = function(self){
+ return self.elem.container.hasClass(self.css_class.container_open);
+ }
+
+
+ /**
+ * Init plugin entrance
+ * @global
+ * @memberof jQuery
+ * @param option {Object} init parameters
+ */
+ function Plugin(option) {
+ return this.each(function(){
+ var $this = $(this),
+ data = $this.data(SelectMenu.dataKey),
+ params = $.extend({}, defaults, $this.data(), data && data.option ,typeof option === 'object' && option);
+ if(!data) $this.data(SelectMenu.dataKey,(data = new SelectMenu(this,params)));
+ else{
+ if(data.isVisible(data)) data.hideResults(data);
+ else data.showMenu(data);
+ }
+ });
+ }
+
+ /**
+ * Hide menu
+ */
+ function HideMenu(){
+ return this.each(function(){
+ var $this = $(this),
+ data = $this.data(SelectMenu.dataKey);
+ if(data) data.hideResults(data);
+ });
+ }
+
+ /**
+ * Clear all menu selected item
+ */
+ function ClearSelected(){
+ return this.each(function(){
+ var $this = $(this),
+ data = $this.data(SelectMenu.dataKey);
+ if(data) data.clearAll(data);
+ });
+ }
+
+ /**
+ * Get selected item data
+ */
+ function GetSelected(){
+ var results = new Array();
+ this.each(function(){
+ var $this =$(this),
+ data = $this.data(SelectMenu.dataKey);
+ if(data) results = data.prop.values.concat();
+ });
+ return results;
+ }
+
+ var old = $.fn.selectMenu;
+
+ $.fn.selectMenu = Plugin;
+ $.fn.selectMenu.Constructor = SelectMenu;
+ $.fn.selectMenuHide = HideMenu;
+ $.fn.selectMenuClear = ClearSelected;
+ $.fn.selectMenuValues = GetSelected;
+
+ // SelectMenu no conflict
+ // =================
+ $.fn.selectMenu.noConflict = function () {
+ $.fn.selectMenu = old;
+ return this;
+ };
+})(window.jQuery); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/api/ext.smw.api.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/api/ext.smw.api.js
new file mode 100644
index 00000000..e87dd505
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/api/ext.smw.api.js
@@ -0,0 +1,163 @@
+/**
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @file
+ * @ignore
+ *
+ * @since 1.9
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /*global md5 */
+
+ /**
+ * Constructor to create an object to interact with the Semantic
+ * MediaWiki Api
+ *
+ * @since 1.9
+ *
+ * @class
+ * @alias smw.api
+ * @constructor
+ */
+ smw.Api = function() {};
+
+ /* Public methods */
+
+ smw.Api.prototype = {
+
+ /**
+ * Convenience method to parse and map a JSON string
+ *
+ * Emulates partly $.parseJSON (jquery.js)
+ * (see https://web.archive.org/web/20170101125747/http://www.json.org/js.html)
+ * @since 1.9
+ *
+ * @param {string} data
+ *
+ * @return {object|null}
+ */
+ parse: function( data ) {
+
+ // Use smw.Api JSON custom parser to resolve raw data and add
+ // type hinting
+ var smwData = new smw.Data();
+
+ if ( !data || typeof data !== 'string' ) {
+ return null;
+ }
+
+ // Remove leading/trailing whitespace
+ data = $.trim(data);
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return JSON.parse( data, function ( key, value ) { return smwData.factory( key, value ); } );
+ }
+
+ // If the above fails, use jquery to do the rest
+ return $.parseJSON( data );
+ },
+
+ /**
+ * Returns results from the SMWApi
+ *
+ * On the topic of converters (see http://bugs.jquery.com/ticket/9095)
+ *
+ * Example:
+ * var smwApi = new smw.Api();
+ * smwApi.fetch( query )
+ * .done( function ( data ) { } )
+ * .fail( function ( error ) { } );
+ *
+ * @since 1.9
+ *
+ * @param {string} queryString
+ * @param {boolean|number} useCache
+ *
+ * @return {jQuery.Promise}
+ */
+ fetch: function( queryString, useCache ){
+ var self = this,
+ apiDeferred = $.Deferred();
+
+ if ( !queryString || typeof queryString !== 'string' ) {
+ throw new Error( 'Invalid query string: ' + queryString );
+ }
+
+ // Look for a cache object otherwise do an Ajax call
+ if ( useCache ) {
+
+ // Use a hash key to compare queries and use it as identifier for
+ // stored resultObjects, each change in the queryString will result
+ // in another hash key which will ensure only objects are stored
+ // with this key can be reused
+ var hash = md5( queryString );
+
+ var resultObject = $.jStorage.get( hash );
+ if ( resultObject !== null ) {
+ var results = self.parse( resultObject );
+ results.isCached = true;
+ apiDeferred.resolve( results );
+ return apiDeferred.promise();
+ }
+ }
+
+ return $.ajax( {
+ url: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ data: {
+ 'action': 'ask',
+ 'format': 'json',
+ 'query' : queryString
+ },
+ converters: { 'text json': function ( data ) {
+ // Store only the string as we want to return a typed object
+ // If useCache is not a number use 15 min as default ttl
+ if ( useCache ){
+ $.jStorage.set( hash, data, {
+ TTL: $.type( useCache ) === 'number' ? useCache : 900000
+ } );
+ }
+
+ var results;
+
+ try {
+ results = self.parse( data );
+ } catch ( e ) {
+ console.log( e );
+ throw e;
+ }
+
+ results.isCached = false;
+ return results;
+ } }
+ } );
+ }
+ };
+
+ //Alias
+ smw.api = smw.Api;
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.data.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.data.js
new file mode 100644
index 00000000..75644105
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.data.js
@@ -0,0 +1,183 @@
+/**
+ * This file is part of the Semantic MediaWiki JavaScript DataItem module
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @file
+ * @ignore
+ *
+ * @since 1.9
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Constructor to create an object to interact with data objects and Api
+ *
+ * @since 1.9
+ *
+ * @class
+ * @alias smw.data
+ * @constructor
+ */
+ smw.Data = function() {};
+
+ /* Public methods */
+
+ smw.Data.prototype = {
+
+ /**
+ * List of properties used
+ *
+ * @property
+ * @static
+ */
+ properties: null,
+
+ /**
+ * Factory methods that maps an JSON.parse key/value to an dataItem object
+ * This function is normally only called during smw.Api.parse/fetch()
+ *
+ * Structure will be similar to
+ *
+ * Subject (if exists is of type smw.dataItem.wikiPage otherwise a simple object)
+ * |--> property -> smw.dataItem.property
+ * |--> smw.dataItem.wikiPage
+ * |--> ...
+ * |--> property -> smw.dataItem.property
+ * |--> smw.dataItem.uri
+ * |--> ...
+ * |--> property -> smw.dataItem.property
+ * |--> smw.dataItem.time
+ * |--> ...
+ *
+ * @since 1.9
+ *
+ * @param {string} key
+ * @param {mixed} value
+ *
+ * @return {object}
+ */
+ factory: function( key, value ) {
+ var self = this;
+
+ // Map printrequests in order to be used as key accessible reference object
+ // which enables type hinting for all items that exists within in this list
+ if ( key === 'printrequests' && value !== undefined ){
+ var list = {};
+ $.map( value, function( key, index ) {
+ list[key.label] = { typeid: key.typeid, position: index };
+ } );
+ self.properties = list;
+ }
+
+ // Map the entire result object, for objects that have a subject as
+ // full fledged head item and rebuild the entire object to ensure
+ // that wikiPage is invoked at the top as well
+ if ( key === 'results' ){
+ var nResults = {};
+
+ $.each( value, function( subjectName, subject ) {
+ if( subject.hasOwnProperty( 'fulltext' ) ){
+ var nSubject = new smw.dataItem.wikiPage( subject.fulltext, subject.fullurl, subject.namespace, subject.exists, subject.displaytitle );
+ nSubject.printouts = subject.printouts;
+ nResults[subjectName] = nSubject;
+ } else {
+ // Headless entry without a subject
+ nResults = value;
+ }
+ } );
+
+ return nResults;
+ }
+
+ // Map individual properties according to its type
+ if ( typeof value === 'object' && self.properties !== null ){
+ if ( key !== '' && value.length > 0 && self.properties.hasOwnProperty( key ) ){
+ var property = new smw.dataItem.property( key ),
+ typeid = self.properties[key].typeid,
+ factoredValue = [];
+
+ // Assignment of individual classes
+ switch ( typeid ) {
+ case '_wpg':
+ $.map( value, function( w ) {
+ factoredValue.push( new smw.dataItem.wikiPage( w.fulltext, w.fullurl, w.namespace, w.exists, w.displaytitle ) );
+ } );
+ break;
+ case '_uri':
+ $.map( value, function( u ) {
+ factoredValue.push( new smw.dataItem.uri( u ) );
+ } );
+ break;
+ case '_dat':
+ $.map( value, function( t ) {
+ // API 2.4+
+ if ( t.hasOwnProperty( 'raw' ) ) {
+ var time = new smw.dataItem.time( t.timestamp, t.raw );
+ } else {
+ var time = new smw.dataItem.time( t, undefined );
+ }
+
+ factoredValue.push( time );
+ } );
+ break;
+ case '_num':
+ $.map( value, function( n ) {
+ factoredValue.push( new smw.dataItem.number( n ) );
+ } );
+ break;
+ case '_qty':
+ $.map( value, function( q ) {
+ factoredValue.push( new smw.dataValue.quantity( q.value, q.unit ) );
+ } );
+ break;
+ case '_str':
+ case '_txt':
+ $.map( value, function( s ) {
+ factoredValue.push( new smw.dataItem.text( s, typeid ) );
+ } );
+ break;
+ case '_geo':
+ $.map( value, function( g ) {
+ factoredValue.push( new smw.dataItem.geo( g, typeid ) );
+ } );
+ break;
+ default:
+ // Register all non identifiable types as unknown
+ $.map( value, function( v ) {
+ factoredValue.push( new smw.dataItem.unknown( v, typeid ) );
+ } );
+ }
+
+ return $.extend( property, factoredValue );
+ }
+ }
+
+ // Return all other values
+ return value;
+ }
+ };
+
+ // Alias
+ smw.data = smw.Data;
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.geo.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.geo.js
new file mode 100644
index 00000000..e10b4c89
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.geo.js
@@ -0,0 +1,88 @@
+/**
+ * SMW Text DataItem JavaScript representation
+ *
+ * @see SMW\DIGeoCoord
+ *
+ * Implementation of dataitems that are geographic coordinates.
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author Peter Grassberger < petertheone@gmail.com >
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class for the smw.dataItem constructor
+ *
+ * @class
+ * @abstract
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Number constructor
+ *
+ * @param {string}
+ * @param {string}
+ * @return {this}
+ */
+ var geo = function ( geo, type ) {
+ this.geo = geo !== {} ? geo : null;
+
+ // If the type is not specified we assume it has to be '_geo'
+ this.type = type !== '' && type !== undefined ? type : '_geo';
+
+ return this;
+ };
+
+ /**
+ * Class constructor
+ *
+ * @class
+ * @constructor
+ * @extends smw.dataItem
+ */
+ smw.dataItem.geo = function( geo, type ) {
+ if ( $.type( geo ) === 'object' ) {
+ this.constructor( geo, type );
+ } else {
+ throw new Error( 'smw.dataItem.geo: invoked text must be a string but is of type ' + $.type( geo ) );
+ }
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: geo,
+
+ /**
+ * Returns type
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return this.type;
+ },
+
+ /**
+ * Returns a plain geo representation
+ *
+ * @return {string}
+ */
+ getGeo: function() {
+ return this.geo;
+ }
+ };
+
+ // Alias
+ fn.getValue = fn.getGeo;
+ fn.getString = fn.getGeo;
+
+ // Assign methods
+ smw.dataItem.geo.prototype = fn;
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.number.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.number.js
new file mode 100644
index 00000000..484e6877
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.number.js
@@ -0,0 +1,100 @@
+/**
+ * SMW Number DataItem JavaScript representation
+ *
+ * @see SMW\DINumber
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class for the smw.dataItem constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @abstract
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Number constructor
+ *
+ * @since 1.9
+ *
+ * @param {number}
+ * @return {this}
+ */
+ var number = function ( number ) {
+ this.number = number !== '' ? number : null;
+
+ return this;
+ };
+
+ /**
+ * Class constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @constructor
+ * @extends smw.dataItem
+ */
+ smw.dataItem.number = function( value ) {
+ if ( $.type( value ) === 'number' ) {
+ this.constructor( value );
+ } else {
+ throw new Error( 'smw.dataItem.number: invoked value must be a number but is of type ' + $.type( value ) );
+ }
+ };
+
+ /* Public methods */
+
+ smw.dataItem.number.prototype = {
+
+ constructor: number,
+
+ /**
+ * Returns type
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_num';
+ },
+
+ /**
+ * Returns a number together with the number constructor functions
+ *
+ * @since 1.9
+ *
+ * @return {number}
+ */
+ getNumber: function() {
+ return Number( this.number );
+ },
+
+ /**
+ * Returns a plain value representation
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getValue: function() {
+ return this.number;
+ }
+ };
+
+ // Alias
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.property.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.property.js
new file mode 100644
index 00000000..7aa67387
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.property.js
@@ -0,0 +1,117 @@
+/**
+ * SMW Property DataItem JavaScript representation
+ *
+ * @see SMW\DITime
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ var html = mw.html;
+
+ /**
+ * Inheritance class
+ *
+ * @type Object
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Property constructor
+ *
+ * @since 1.9
+ *
+ * @param {string}
+ *
+ * @return {this}
+ */
+ var property = function ( property ) {
+ this.property = property !== '' && property !== undefined ? property : null;
+ return this;
+ };
+
+ /**
+ * Constructor
+ *
+ * @var Object
+ */
+ smw.dataItem.property = function( property ) {
+ if ( $.type( property ) === 'string' ) {
+ this.constructor( property );
+ } else {
+ throw new Error( 'smw.dataItem.property: invoked property must be a string but is of type ' + $.type( property ) );
+ }
+ };
+
+ /* Public methods */
+
+ smw.dataItem.property.prototype = {
+
+ constructor: property,
+ namespace: 102, // SMW_NS_PROPERTY needs a better way to fill this value
+
+ /**
+ * Returns type
+ *
+ * @see SMW\DIProperty::getDIType()
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_IDONTKNOW'; // what is the type here
+ },
+
+ /**
+ * Returns a label
+ *
+ * @see SMW\DIProperty::getLabel()
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getLabel: function() {
+ return this.property;
+ },
+
+ /**
+ * Returns wikiPage representation
+ *
+ * @see SMW\DIProperty::getDiWikiPage()
+ *
+ * @since 1.9
+ *
+ * @return {smw.dataItem.wikiPage}
+ */
+ getDiWikiPage: function() {
+ return null; //new smw.dataItem.wikiPage( this.property );
+ },
+
+ /**
+ * Returns html representation
+ *
+ * @since 1.9
+ *
+ * @param {boolean}
+ * @return {string}
+ */
+ getHtml: function( linker ) {
+ if( linker ){
+ return html.element( 'a', { href: mw.util.wikiGetlink( 'Property:' + this.property ) }, this.property );
+ }
+ return this.property;
+ }
+ };
+
+ // Alias
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.text.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.text.js
new file mode 100644
index 00000000..51c92eb4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.text.js
@@ -0,0 +1,104 @@
+/**
+ * SMW Text DataItem JavaScript representation
+ *
+ * @see SMW\DIString, SMW\DIBlob
+ *
+ * A string is a text representation only limited by its length.
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class for the smw.dataItem constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @abstract
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Number constructor
+ *
+ * @since 1.9
+ *
+ * @param {string}
+ * @param {string}
+ * @return {this}
+ */
+ var text = function ( text, type ) {
+ this.text = text !== '' ? text : null;
+
+ // If the type is not specified we assume it has to be '_txt'
+ this.type = type !== '' && type !== undefined ? type : '_txt';
+
+ return this;
+ };
+
+ /**
+ * Class constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @constructor
+ * @extends smw.dataItem
+ */
+ smw.dataItem.text = function( text, type ) {
+ if ( $.type( text ) === 'string' ) {
+ this.constructor( text, type );
+ } else {
+ throw new Error( 'smw.dataItem.text: invoked text must be a string but is of type ' + $.type( text ) );
+ }
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: text,
+
+ /**
+ * Returns type
+ *
+ * Flexible in what to return as type as it could be either '_str' or
+ * '_txt' but the methods are the same therefore there is no need for
+ * an extra dataItem representing a string
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return this.type;
+ },
+
+ /**
+ * Returns a plain text representation
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getText: function() {
+ return this.text;
+ }
+ };
+
+ // Alias
+ fn.getValue = fn.getText;
+ fn.getString = fn.getText;
+
+ // Assign methods
+ smw.dataItem.text.prototype = fn;
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.time.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.time.js
new file mode 100644
index 00000000..d018a902
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.time.js
@@ -0,0 +1,246 @@
+/**
+ * SMW Time DataItem JavaScript representation
+ *
+ * @see SMW\DITime
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class
+ *
+ * @type Object
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Date constructor
+ *
+ * @since 1.9
+ *
+ * @param {string|number}
+ * @param {string|number}
+ *
+ * @return {this}
+ */
+ var time = function ( timestamp, raw ) {
+ var FLAG_YEAR = 1;
+ var FLAG_MONTH = 2;
+ var FLAG_DAY = 4;
+ var FLAG_TIME = 8;
+
+ this.timestamp = timestamp !== '' && timestamp !== undefined ? timestamp : null;
+ this.raw = raw !== '' && raw !== undefined ? raw : null;
+ this.precision = 0;
+
+ // Returns a normalized timestamp as JS date object
+ if ( typeof this.timestamp === 'number' ) {
+ this.date = new Date( this.timestamp * 1000 );
+ }
+
+ if ( typeof this.timestamp === 'string' ) {
+ if ( this.timestamp.match(/^\d+(\.\d+)?$/) ) {
+ this.date = new Date( parseFloat( this.timestamp ) * 1000 );
+ }
+ }
+
+ // SMW serialization format with:
+ if ( this.raw !== null ) {
+ var date = this.raw.split( '/' );
+
+ // [0] contains the calendar model
+ this.calendarModel = date[0];
+ this.precision = FLAG_YEAR;
+
+ if ( typeof date[2] !== 'undefined' ) {
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
+ // Note: January is 0, February is 1, and so on
+ date[2] = date[2] - 1;
+ this.precision = this.precision | FLAG_MONTH;
+ } else {
+ date[2] = 0;
+ }
+
+ if ( typeof date[3] !== 'undefined' ) {
+ this.precision = this.precision | FLAG_DAY;
+ } else {
+ date[3] = 0;
+ }
+
+ if ( typeof date[4] !== 'undefined' ) {
+ this.precision = this.precision | FLAG_TIME;
+ } else {
+ date[4] = 0;
+ }
+
+ if ( typeof date[5] === 'undefined' ) {
+ date[5] = 0;
+ }
+
+ if ( typeof date[6] === 'undefined' ) {
+ date[6] = 0;
+ }
+
+ // Date is called as a constructor with more than one argument, the
+ // specifed arguments represent local time. If UTC is desired, use
+ // new Date(Date.UTC(...))
+ this.date = new Date( Date.UTC( date[1], date[2], date[3], date[4], date[5], date[6] ) );
+ };
+
+ return this;
+ };
+
+ /**
+ * Map wgMonthNames and create an indexed array
+ *
+ */
+ var monthNames = [];
+ $.map ( mw.config.get( 'wgMonthNames' ), function( index ) {
+ if( index !== '' ) {
+ monthNames.push( index );
+ }
+ } );
+
+ /**
+ * Constructor
+ *
+ * @type object
+ */
+ smw.dataItem.time = function( timestamp, raw ) {
+ if ( $.type( timestamp ) === 'string' || 'number' ) {
+ this.constructor( timestamp, raw );
+ } else {
+ throw new Error( 'smw.dataItem.time: timestamp must be a string or number' );
+ }
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: time,
+
+ /**
+ * Returns type
+ *
+ * @see SMW\DITime::getDIType()
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_dat';
+ },
+
+ /**
+ * Returns a MW timestamp representation of the value
+ *
+ * @see SMW\DITime::getMwTimestamp()
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getMwTimestamp: function() {
+ return this.timestamp;
+ },
+
+ /**
+ * Returns Date object
+ *
+ * @since 1.9
+ *
+ * @return {Date}
+ */
+ getDate: function() {
+ return this.date;
+ },
+
+ /**
+ * Returns an ISO string
+ *
+ * @see SMWTimeValue::getISO8601Date()
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getISO8601Date: function() {
+ return this.date.toISOString();
+ },
+
+ /**
+ * Returns a formatted time (HH:MM:SS)
+ *
+ * In case of no time '00:00:00' is returned
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getTimeString: function() {
+ var d = this.date;
+ if ( d.getUTCHours() + d.getUTCMinutes() + d.getUTCSeconds() === 0 ){
+ return '00:00:00';
+ }
+ return ( d.getUTCHours() < 10 ? '0' + d.getUTCHours() : d.getUTCHours() ) +
+ ':' + ( d.getUTCMinutes() < 10 ? '0' + d.getUTCMinutes() : d.getUTCMinutes() ) +
+ ':' + ( d.getUTCSeconds() < 10 ? '0' + d.getUTCSeconds() : d.getUTCSeconds() );
+ },
+
+ /**
+ * Returns MediaWiki's date and time formatting
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getMediaWikiDate: function() {
+
+ var FLAG_YEAR = 1;
+ var FLAG_MONTH = 2;
+ var FLAG_DAY = 4;
+ var FLAG_TIME = 8;
+
+ var CM_GREGORIAN = 1;
+ var CM_JULIAN = 2;
+
+ // Fallback
+ if ( this.date === undefined ) {
+ return '';
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
+ // Use the precision from the raw date
+ if ( this.precision > 0 ) {
+ var calendarModel = this.calendarModel !== undefined && this.calendarModel == CM_JULIAN ? ' <sup>JL</sup>' : '';
+
+ return '' +
+ ( ( this.precision & FLAG_DAY ) ? ( this.date.getUTCDate() ) + ' ' + ( monthNames[(this.date.getUTCMonth())] ) + ' ' : '' ) +
+ ( ( this.precision & FLAG_YEAR ) ? ( this.date.getUTCFullYear() ) + ' ' : '' ) +
+ ( ( this.precision & FLAG_TIME ) && this.getTimeString() !== '00:00:00' ? ( this.getTimeString() ) : '' ) + calendarModel;
+ };
+
+ return this.date.getUTCDate() + ' ' +
+ monthNames[this.date.getUTCMonth()] + ' ' +
+ this.date.getUTCFullYear() +
+ ( this.getTimeString() !== '00:00:00' ? ' ' + this.getTimeString() : '' );
+ }
+ };
+
+ // Alias
+ fn.getValue = fn.getMwTimestamp;
+
+ // Assign methods
+ smw.dataItem.time.prototype = fn;
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.unknown.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.unknown.js
new file mode 100644
index 00000000..86ac21fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.unknown.js
@@ -0,0 +1,91 @@
+/**
+ * SMW Unlisted/Unknown DataItem JavaScript representation
+ *
+ * A representation for types we do not know or are unlisted
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class for the smw.dataItem constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @abstract
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Unknown constructor
+ *
+ * @since 1.9
+ *
+ * @param {string}
+ * @param {mixed}
+ * @return {this}
+ */
+ var unknown = function ( value, type ) {
+ this.value = value !== '' ? value : null;
+ this.type = type !== '' ? type : null;
+
+ return this;
+ };
+
+ /**
+ * Class constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @constructor
+ * @extends smw.dataItem
+ */
+ smw.dataItem.unknown = function( value, type ) {
+ this.constructor( value, type );
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: unknown,
+
+ /**
+ * Returns type
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return this.type;
+ },
+
+ /**
+ * Returns value
+ *
+ * @since 1.9
+ *
+ * @return {mixed}
+ */
+ getValue: function() {
+ return this.value;
+ }
+ };
+
+ // Alias
+ fn.getHtml = fn.getValue;
+
+ // Assign methods
+ smw.dataItem.unknown.prototype = fn;
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.uri.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.uri.js
new file mode 100644
index 00000000..c6448902
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.uri.js
@@ -0,0 +1,106 @@
+/**
+ * SMW Uri DataItem JavaScript representation
+ *
+ * @see SMW\DIUri
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ var html = mw.html;
+
+ /**
+ * Inheritance class
+ *
+ * @type object
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Uri constructor
+ *
+ * @since 1.9
+ *
+ * @param {string}
+ *
+ * @return {this}
+ */
+ var uri = function ( fullurl ) {
+ this.fullurl = fullurl !== '' && fullurl !== undefined ? fullurl : null;
+
+ // Get mw.Uri inheritance
+ if ( this.fullurl !== null ) {
+ this.uri = new mw.Uri( this.fullurl );
+ }
+
+ return this;
+ };
+
+ /**
+ * Constructor
+ *
+ * @var object
+ */
+ smw.dataItem.uri = function( fullurl ) {
+ if ( $.type( fullurl ) === 'string' ) {
+ this.constructor( fullurl );
+ } else {
+ throw new Error( 'smw.dataItem.uri: invoked fullurl must be a string but is of type ' + $.type( fullurl ) );
+ }
+ };
+
+ /* Public methods */
+
+ smw.dataItem.uri.prototype = {
+
+ constructor: uri,
+
+ /**
+ * Returns type
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_uri';
+ },
+
+ /**
+ * Returns uri
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getUri: function() {
+ return this.fullurl;
+ },
+
+ /**
+ * Returns html representation
+ *
+ * @since 1.9
+ *
+ * @param {boolean}
+ *
+ * @return {string}
+ */
+ getHtml: function( linker ) {
+ if ( linker && this.fullurl !== null ){
+ return html.element( 'a', { 'href': this.fullurl }, this.fullurl );
+ }
+ return this.fullurl;
+ }
+ };
+
+ // Alias
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.wikiPage.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.wikiPage.js
new file mode 100644
index 00000000..3a2e6546
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataItem.wikiPage.js
@@ -0,0 +1,211 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Helper method
+ * @ignore
+ */
+ var html = mw.html;
+
+ /**
+ * Inheritance class for the smw.dataItem constructor
+ *
+ * @since 1.9
+ *
+ * @class smw.dataItem
+ * @abstract
+ */
+ smw.dataItem = smw.dataItem || {};
+
+ /**
+ * Initializes the constructor
+ *
+ * @param {string} fulltext
+ * @param {string} fullurl
+ * @param {number} ns
+ * @param {boolean} exists
+ *
+ * @return {smw.dataItem.wikiPage} this
+ */
+ var wikiPage = function ( fulltext, fullurl, ns, exists, displaytitle ) {
+ this.fulltext = fulltext !== ''&& fulltext !== undefined ? fulltext : null;
+ this.fullurl = fullurl !== '' && fullurl !== undefined ? fullurl : null;
+ this.ns = ns !== undefined ? ns : 0;
+ this.exists = exists !== undefined ? exists : true;
+ this.displaytitle = displaytitle !== '' && displaytitle !== undefined ? displaytitle : null;
+
+ // Get mw.Title inheritance
+ if ( this.fulltext !== null ){
+ this.title = new mw.Title( this.fulltext );
+ }
+
+ return this;
+ };
+
+ /**
+ * A class that includes methods to create a wikiPage dataItem representation
+ * in JavaScript that resembles the SMW\DIWikiPage object in PHP
+ *
+ * @since 1.9
+ *
+ * @class
+ * @constructor
+ */
+ smw.dataItem.wikiPage = function( fulltext, fullurl, ns, exists, displaytitle ) {
+ if ( $.type( fulltext ) === 'string' && $.type( fullurl ) === 'string' ) {
+ this.constructor( fulltext, fullurl, ns, exists, displaytitle );
+ } else {
+ throw new Error( 'smw.dataItem.wikiPage: fulltext, fullurl must be a string' );
+ }
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: wikiPage,
+
+ /**
+ * Returns type
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_wpg';
+ },
+
+ /**
+ * Returns wikiPage text title as full name like "File:Foo bar.jpg"
+ * due to fact that the name is serialized in fulltext
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getFullText: function() {
+ return this.fulltext;
+ },
+
+ /**
+ * Returns main part of the title without any fragment
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getText: function() {
+ return this.fulltext && this.fulltext.split( '#' )[0];
+ },
+
+ /**
+ * Returns wikiPage uri
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getUri: function() {
+ return this.fullurl;
+ },
+
+ /**
+ * Returns mw.Title object
+ *
+ * @since 1.9
+ *
+ * @return {mw.Title}
+ */
+ getTitle: function() {
+ return this.title;
+ },
+
+ /**
+ * Returns if the wikiPage is a known entity or not
+ *
+ * @since 1.9
+ *
+ * @return {boolean}
+ */
+ isKnown: function(){
+ return this.exists;
+ },
+
+ /**
+ * Returns namespace id
+ *
+ * @since 1.9
+ *
+ * @return {number}
+ */
+ getNamespaceId: function() {
+ return this.ns;
+ },
+
+ /**
+ * Returns html representation
+ *
+ * @since 1.9
+ *
+ * @param {boolean}
+ *
+ * @return {string}
+ */
+ getHtml: function( linker ) {
+ var displaytitle = this.displaytitle;
+
+ if ( displaytitle === null ) {
+ displaytitle = this.getText();
+ }
+
+ if ( linker && this.fullurl !== null ){
+ var attributes = this.exists ? { 'href': this.fullurl } : { 'href': this.fullurl, 'class': 'new' };
+ return html.element( 'a', attributes , displaytitle );
+ }
+
+ return displaytitle;
+ }
+ };
+
+ // Alias
+ fn.exists = fn.isKnown;
+ fn.getPrefixedText = fn.getFullText;
+ fn.getName = fn.getFullText;
+ fn.getValue = fn.getFullText;
+
+ // Assign methods
+ smw.dataItem.wikiPage.prototype = fn;
+
+ // For additional methods use
+ // $.extend( smw.dataItem.wikiPage.prototype, { method: function (){ ... } } );
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataValue.quantity.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataValue.quantity.js
new file mode 100644
index 00000000..36ca8034
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/data/ext.smw.dataValue.quantity.js
@@ -0,0 +1,116 @@
+/**
+ * SMW Quantity DataValue JavaScript representation
+ *
+ * @see SMW\DINumber
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Inheritance class for the smw.dataValue constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @abstract
+ */
+ smw.dataValue = smw.dataValue || {};
+
+ /**
+ * Number constructor
+ *
+ * @since 1.9
+ *
+ * @param {number}
+ * @param {string}
+ * @param {number}
+ * @return {this}
+ */
+ var quantity = function ( value, unit, accuracy ) {
+ this.value = value !== '' ? value : null;
+ this.unit = unit !== '' ? unit : null;
+ this.accuracy = accuracy !== '' ? accuracy : null;
+
+ return this;
+ };
+
+ /**
+ * Class constructor
+ *
+ * @since 1.9
+ *
+ * @class
+ * @constructor
+ * @extends smw.dataValue
+ */
+ smw.dataValue.quantity = function( value, unit, accuracy ) {
+ if ( $.type( value ) === 'number' ) {
+ this.constructor( value, unit, accuracy );
+ } else {
+ throw new Error( 'smw.dataValue.quantity: invoked value must be a number but is of type ' + $.type( value ) );
+ }
+ };
+
+ /* Public methods */
+
+ smw.dataValue.quantity.prototype = {
+
+ constructor: quantity,
+
+ /**
+ * Returns type
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getDIType: function() {
+ return '_qty';
+ },
+
+ /**
+ * Returns value
+ *
+ * @since 1.9
+ *
+ * @return {number}
+ */
+ getValue: function() {
+ return this.value;
+ },
+
+ /**
+ * Returns unit
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ getUnit: function() {
+ return this.unit;
+ },
+
+ /**
+ * Returns accuracy
+ *
+ * @since 1.9
+ *
+ * @return {number}
+ */
+ getAccuracy: function() {
+ return this.accuracy;
+ }
+
+ };
+
+ // Alias
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.css
new file mode 100644
index 00000000..fc8f7987
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.css
@@ -0,0 +1,61 @@
+
+.smw-deferred-query-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.smw-deferred-query .loading-image {
+ text-align: center;
+}
+
+.smw-deferred-query .overlay {
+ filter:alpha(opacity=50);
+ -moz-opacity:0.5;
+ -khtml-opacity: 0.5;
+ opacity: 0.5;
+ z-index: 10000;
+ cursor: wait;
+}
+
+.smw-deferred-query #controls {
+ margin-bottom:10px;
+}
+
+.smw-deferred-query .ui-slider-horizontal .ui-state-default {
+ margin-left: -1.5em;
+}
+
+.smw-deferred-query .ui-slider .ui-slider-handle {
+ width:3em;
+ left:-1.5em;
+ text-decoration:none;
+ text-align:center;
+}
+
+.smw-deferred-query .irs-from, .smw-deferred-query .irs-to, .smw-deferred-query .irs-single {
+ background: rgba(0,0,0,0.3);
+}
+
+.smw-deferred-query .irs-from:after, .smw-deferred-query .irs-to:after, .smw-deferred-query .irs-single:after {
+ border-top-color: rgba(0,0,0,0.3);
+}
+
+.smw-deferred-query .irs {
+ margin-bottom: 10px;
+}
+
+.is-disabled {
+ opacity: .5;
+ position: relative;
+ pointer-events: none;
+}
+
+.is-disabled::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ content: ' ';
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.js
new file mode 100644
index 00000000..fc907587
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.js
@@ -0,0 +1,242 @@
+/**
+ * Responsible for executing a deferred request to the MediaWiki back-end to
+ * retrieve the representation for a #ask query.
+ */
+
+/*global jQuery, mediaWiki, smw */
+/*jslint white: true */
+
+( function( $, mw, onoi ) {
+
+ 'use strict';
+
+ /**
+ * @since 3.0
+ * @constructor
+ *
+ * @param container {Object}
+ * @param api {Object}
+ */
+ var Query = function ( container, api ) {
+
+ this.VERSION = "3.0";
+
+ this.container = container;
+ this.mwApi = api;
+
+ this.title = mw.config.get( 'wgPageName' );
+ this.data = container.data( 'query' );
+ this.query = this.data.query;
+
+ this.cmd = this.data.cmd;
+ this.control = container.find( '#deferred-control' ).data( 'control' );
+
+ this.limit = this.data.limit;
+ this.offset = this.data.offset;
+
+ this.rangeLimit = this.limit;
+ this.rangeOffset = this.offset;
+ this.init = true;
+
+ this.max = this.data.max;
+ this.step = 5;
+ this.postfix = '';
+
+ // Ensure to have a limit, offset parameter for queries that use
+ // the default setting
+ if ( this.query.indexOf( "|limit=" ) == -1 ) {
+ this.query = this.query + '|limit=' + this.limit;
+ }
+
+ if ( this.query.indexOf( "|offset=" ) == -1 ) {
+ this.query = this.query + '|offset=' + this.offset;
+ }
+
+ if ( this.query.indexOf( "|default=" ) == -1 ) {
+ this.query = this.query + '|default=' + mw.msg( 'smw_result_noresults' );
+ }
+ };
+
+ /**
+ * Request and parse a #ask/#show query using the MediaWiki API back-end
+ *
+ * @since 3.0
+ */
+ Query.prototype.doApiRequest = function() {
+
+ var self = this,
+ noTrace = '';
+
+ // Replace limit, offset with altered values
+ var query = self.query.replace(
+ 'limit=' + self.limit,
+ 'limit=' + self.rangeLimit
+ ).replace(
+ 'offset=' + self.offset,
+ 'offset=' + self.rangeOffset
+ );
+
+ // In case the query was altered from its original request, signal
+ // to the QueryDependencyLinksStore to disable any tracking
+ if ( self.query !== query ) {
+ noTrace = '|@notrace';
+ };
+
+ // API notes "modules: Gives the ResourceLoader modules used on the page.
+ // Either jsconfigvars or encodedjsconfigvars must be requested jointly
+ // with modules. 1.24+"
+ self.mwApi.post( {
+ action: "parse",
+ title: self.title,
+ contentmodel: 'wikitext',
+ prop: 'text|modules|jsconfigvars',
+ text: '{{#' + self.cmd + ':' + query + noTrace + '}}'
+ } ).done( function( data ) {
+
+ if ( self.init === true ) {
+ self.initControls();
+ self.replaceOutput( '' );
+ };
+
+ // Remove any comments retrieved from the API parse
+ var text = data.parse.text['*'].replace(/<!--[\S\s]*?-->/gm, '' );
+
+ // Remove any remaining placeholder loading classes
+ if ( self.control !== '' ) {
+ text = text.replace( 'smw-loading-image-dots', '' );
+ }
+
+ // Remove any <p> element to avoid line breakages
+ if ( self.cmd === 'show' ) {
+ text = text.replace( /(?:^<p[^>]*>)|(?:<\/p>$)/img, '' );
+ }
+
+ self.replaceOutput( text, '', data.parse.modules );
+
+ } ).fail ( function( code, failure ) {
+ var error = code + ': ' + failure.textStatus;
+
+ if ( failure.hasOwnProperty( 'exception' ) && failure.hasOwnProperty( 'xhr' ) ) {
+ error = failure.xhr.responseText;
+ } else if ( failure.hasOwnProperty( 'error' ) && failure.error.hasOwnProperty( 'info' ) ) {
+ error = failure.error.info;
+ }
+
+ self.container.find( '#deferred-control' ).replaceWith( "<div id='deferred-control'></div>" );
+ self.container.find( '.irs' ).hide();
+ self.replaceOutput( error, "smw-callout smw-callout-error" );
+ } );
+ };
+
+ /**
+ * Replace output with generated content
+ *
+ * @since 3.0
+ *
+ * @param text {String}
+ * @param oClass {String}
+ * @param modules {Array}
+ *
+ * @return {this}
+ */
+ Query.prototype.replaceOutput = function( text, oClass, modules ) {
+
+ var self = this,
+ element = this.cmd === 'ask' ? 'div' : 'span';
+
+ oClass = oClass !== undefined ? "class='" + oClass + "'" : '';
+
+ self.container.find( '#deferred-output' ).replaceWith(
+ "<" + element + " id='deferred-output'" + oClass + ">" + text + "</" + element + ">"
+ );
+
+ self.reload( modules );
+ };
+
+ /**
+ * Reload module objects that rely on JavaScript to be executed after a
+ * fresh parse.
+ *
+ * @since 3.0
+ *
+ * @param modules {Array}
+ */
+ Query.prototype.reload = function( modules ) {
+
+ var self = this;
+
+ // Trigger an event to re-apply JS instances initialization on new
+ // content
+ if ( modules !== undefined ) {
+ mw.loader.using( modules ).done( function () {
+ mw.hook( 'smw.deferred.query' ).fire( self.container );
+ } );
+ } else {
+ mw.hook( 'smw.deferred.query' ).fire( self.container );
+ }
+
+ var table = self.container.find( '#deferred-output table' );
+
+ // MW's table sorter isn't listed as page module therefore make an exception
+ // and reload it manually
+ if ( table.length > 0 && table.hasClass( 'sortable' ) ) {
+ mw.loader.using( 'jquery.tablesorter' ).done( function () {
+ table.tablesorter();
+ mw.hook( 'smw.deferred.query.tablesorter' ).fire( table );
+ } );
+ }
+ };
+
+ /**
+ * Executes and manages the initialization of control elements
+ *
+ * @since 3.0
+ */
+ Query.prototype.initControls = function() {
+
+ var self = this;
+ var loading = '<div class="smw-flex-center smw-absolute"><span class="smw-overlay-spinner medium flex" alt="Loading..."></span></div>';
+
+ if ( self.init === true && self.control === 'slider' ) {
+ self.container.find( '#deferred-control' ).ionRangeSlider( {
+ type: "double",
+ min: 0,
+ max: self.max,
+ step: self.step,
+ from: self.offset,
+ to: self.limit + self.offset,
+ force_edges: true,
+ postfix: self.postfix,
+ min_interval: 1,
+ grid: true,
+ grid_num: 2,
+ onChange: function ( data ) {
+ self.container.find( '#deferred-output' ).addClass( 'is-disabled' ).append( loading );
+ },
+ onFinish: function ( data ) {
+ self.rangeOffset = data.from - self.offset;
+ self.rangeLimit = data.to - data.from;
+ self.doApiRequest();
+ }
+ } );
+ };
+
+ // Once called turn off the init flag
+ self.init = self.init === true ? false : self.init;
+ };
+
+ /**
+ * @since 3.0
+ */
+ $( document ).ready( function() {
+ $( '.smw-deferred-query' ).each( function() {
+ var q = new Query(
+ $( this ),
+ new mw.Api()
+ );
+
+ q.doApiRequest();
+ } );
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.skin.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.skin.css
new file mode 100644
index 00000000..c97af628
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/deferred/ext.smw.deferred.skin.css
@@ -0,0 +1,3 @@
+/**
+ * .skin-vector specific styles
+ */
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.css
new file mode 100644
index 00000000..7dc6fdc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.css
@@ -0,0 +1,1053 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.8
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
+ * @author mwjames
+ */
+
+.float-right {
+ float: right;
+ margin-left: 10px;
+ clear: right;
+}
+
+.float-left {
+ float: left;
+ margin-right: 10px;
+ clear: left;
+}
+
+.clear-both {
+ clear: both;
+}
+
+.smw-placeholder {
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+}
+
+.client-nojs .smw-placeholder::after {
+ content: "JavaScript is disabled or could not be detected!";
+ top: 80%;
+ text-align: center;
+ color:red;
+}
+
+.smw-personal-jobqueue-watchlist:hover, .smw-personal-jobqueue-watchlist:focus, .smw-personal-jobqueue-watchlist:active {
+ text-decoration: none;
+ color: #0645ad;
+}
+
+.smw-personal-table {
+ font-size: 90%;
+}
+
+/* highlighting for builtin elements */
+span.smwbuiltin,
+span.smwttactiveinline span.smwbuiltin {
+ font-style: italic;
+}
+
+.item-count {
+ display: inline-block;
+ padding: 2px 5px;
+ font-size: 12px;
+ font-weight: 600;
+ line-height: 1;
+ background-color: rgba(27,31,35,0.04);
+ border-radius: 4px;
+ color: #bbb;
+ margin-left: 10px;
+}
+
+.item-count.active {
+ background-color: rgba(27,31,35,0.08);
+ color: #444d56;
+}
+
+/* make divs look like <pre> */
+.smwpre {
+ font-family: monospace;
+ padding: 1em;
+ border: 1px dashed #2f6fab;
+ color: black;
+ background-color: #f9f9f9;
+ line-height: 1.1em;
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+ white-space: pre-wrap;
+ white-space: -moz-pre-wrap;
+ white-space: -pre-wrap;
+ white-space: -o-pre-wrap;
+ word-wrap: break-word;
+ word-break: break-word;
+ margin-right: 5px;
+}
+
+.smwpre-no-margin {
+ margin: 0px;
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+}
+
+.smw-debug-box {
+ border: 5px dotted #ffcc00;
+ background: #FFF0BD;
+ padding: 15px
+ margin-bottom: 10px;
+}
+
+.smw-debug-box-header {
+ font-family: sans-serif;
+ padding-bottom: 15px;
+ padding-top: 10px;
+ font-weight: bolder;
+ font-size: 16px;
+ border-bottom: 1px dotted #ffcc00;
+ margin-bottom: 15px;
+}
+
+/* terminate page contents when inserting stuff below a page, typically used in <br> */
+#smwfootbr {
+ clear: both;
+}
+
+/* hide keys for sorting table entries */
+span.smwsortkey {
+ display: none;
+}
+
+/* buttons for sort-arrows */
+a.sortheader:hover {
+ text-decoration: none;
+}
+
+/* "semantic" span classes for Timeline */
+div.smwtimeline {
+ border: 1px solid #aaa;
+ background-color: #f9f9f9;
+ /*text-align: center;*/
+ /* After hours of debugging and frustration I now can safely say: IE sucks. (mak)
+ You can support Semantic MediaWiki development by not using Internet Explorer. Thanks.
+ (IE centers the Timeline *elements*, which messes up the whole layout) */
+}
+
+span.smwtlevent,
+span.smwtlband,
+span.smwtlsize,
+span.smwtlposition {
+ display: none;
+ speak: none;
+}
+
+span.smwtlcomment {
+ font-style: italic;
+ padding: 5px;
+}
+.smwtable-striped tbody > tr:nth-child(even) {
+ background-color: #f5f5f5;
+}
+
+.smwtable-striped tbody > tr:nth-child(odd) {
+ background-color: #fff;
+}
+
+.smwtable-striped tbody > tr:hover {
+ background-color: #eee;
+}
+
+div.smwhr hr {
+ background-color: #ddd;
+ color: #ddd;
+}
+
+/* warning messages */
+span.smwwarning {
+ color: #888;
+ font-style: italic;
+ font-size: 90%;
+}
+
+.smw-edit-protection {
+ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABE9JREFUeNqcll1olmUcxu93e/apm83lu0a5tjk2UUGR/KpMxMTqQA0MPcgDKewgFMr8OBGEVBQ9MDBBomBSFAVCEXYSszoIP1DexWyp62Wu5YqNbW7te3vXdT1c/3HvYfOd3vBje97n4/rf/8871tfX52awMsBSsAY8BxaBACTBDXAVXAdpPxZLI1gM3gFvgTLwr0Tug1EQBxW61wu+AWfA7ccR3AcOgyFwHnwNGsHYFM9S8DXwLlgIPgMHwIOZCBaCL8HL4EPwkax3sVjMZWRkhH9tjY+Pu7GxSTa8CU7I0C2g4WGCFKsD82TxLbuRmZnphoeHXUdHh+M7FMnKynJFRUUhXKlUyh5/AnwB1oKXQGI6wR/AcrAMtNmugiBwbW1trqmpyfX390/OJuy4uLjY1dTUuPz8fDc6Ouon2vdKMrq5Myq4S75nJl6xj9HqZDLpWlpaJgSiiyIUq66udvF43Bctkpfq5OoJwbngL3AKHDEXDgwMuIaGBtfd3R3uUms92AZKwe/gc/CHubOystJVVFT4cV0vwXXgFzN3DxhQsEM3Dg0Nufr6+qjYae/lPPA2+A3ssGSi2+kNGqx1GVwCx83P2ao1ZuOg7a61tdX19PT4YttVKofAEvAqN6S414IFFOS7zc3NbnBw0Hf/MfACY2kdhO75yu7SHZ2dnb6VTsV/D5z0fmMGvSejt/reobFe+TAn/gabKbhCGZm0pGBcmY2RBJkD7k5R9El1nYKJ1IcQDfYWA3yNuwyUtn9aB6EI3cma89z5vrrJiMXZWzkKzSuK50V+o7293ZWXl7ucnByrT97bFqgR3/M7B13idxPFbZ7+PzhNK1zF5KEg32Vp0GgKarH/zg1kXcp3R15eXvRjW9U9xtNMlKTfECI5EGpQ8A542r8T6Y1OibEgjSBdchEcpZdoeMRLT4HuQPNsrV4IP8jY8SVvPasxlG7FfU9Fkm4xQxdocD4D5oMWChUWFkat26UsTLfD+5YHDEt2drbf0FeCC7ZDzq3XVfx+dtqiq6pnsMNP2X0oyG/QaHlqmZrEt4FaWq1S/ywsGuMOCwoKXG9vrwX+Y/B8GrGUJn4owibueWm/yiJhzZtJw3Gwlx+nCMUSiURYIpFsm3bZMC4rKwvHlZJvqeYhh/F3/ng6oAbLurxDEfbDxsbGsIh5HYnrJCHGis9UVVWFgrzG7yzCm6AVbIrOw0wlEPviavCfPw+7urrCYh4ZGZmUwZz6hAVeWlrqSkpKwp3pmU/ATlBjzSU68enaX3U62wz+sfSmMAWJ1ZlNFiaIuV1u5MU5sBtsBD/63cFf7OgvapcM8k6Li2Vebm5uON2Z9oS7MyGJrdM5leNsgy82laDT5F+jk1uthFmHcYpavAy5bpbmI2fjT6BdpVD3qAdh+v4D8AbDpXPpLY0zbudJHZA4kGdLgNPk58c9efuHITvyLVILo3e6QJPiflml9dD1vwADAEsW2XutLd7kAAAAAElFTkSuQmCC');
+ background-size: 28px 28px;
+ background-repeat: no-repeat;
+ height: 28px;
+ width: 28px;
+}
+
+/* Search, browse, RDF icons/ FIXME: this was only used for Factbox docu, should be removed from code */
+span.smwsearchicon {
+ padding-right: 16px;
+ margin-right: 2px;
+ color: #888;
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' height='18' width='18' viewBox='0 0 22 22'%3E%3Cpath d='m132.77 118.03l-27.945-27.945c6.735-9.722 10.1-20.559 10.1-32.508 0-7.767-1.508-15.195-4.523-22.283-3.01-7.089-7.088-13.199-12.221-18.332-5.133-5.133-11.242-9.207-18.33-12.221-7.09-3.01-14.518-4.522-22.285-4.522-7.767 0-15.195 1.507-22.283 4.522-7.089 3.01-13.199 7.088-18.332 12.221-5.133 5.133-9.207 11.244-12.221 18.332-3.01 7.089-4.522 14.516-4.522 22.283 0 7.767 1.507 15.193 4.522 22.283 3.01 7.088 7.088 13.197 12.221 18.33 5.133 5.134 11.244 9.207 18.332 12.222 7.089 3.02 14.516 4.522 22.283 4.522 11.951 0 22.787-3.369 32.509-10.1l27.945 27.863c1.955 2.064 4.397 3.096 7.332 3.096 2.824 0 5.27-1.032 7.332-3.096 2.064-2.063 3.096-4.508 3.096-7.332.0001-2.877-1-5.322-3.01-7.331m-49.41-34.668c-7.143 7.143-15.738 10.714-25.787 10.714-10.05 0-18.643-3.572-25.786-10.714-7.143-7.143-10.714-15.737-10.714-25.786 0-10.05 3.572-18.644 10.714-25.786 7.142-7.143 15.738-10.714 25.786-10.714 10.05 0 18.643 3.572 25.787 10.714 7.143 7.142 10.715 15.738 10.715 25.786 0 10.05-3.573 18.643-10.715 25.786' transform='matrix%28.11417.00745-.00745.11417 3.93 2.548%29' fill='%23888888'/%3E%3C/svg%3E") center right no-repeat;
+}
+
+#bodyContent span.smwsearch a {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' height='15' width='15' viewBox='0 0 22 22'%3E%3Cpath d='m132.77 118.03l-27.945-27.945c6.735-9.722 10.1-20.559 10.1-32.508 0-7.767-1.508-15.195-4.523-22.283-3.01-7.089-7.088-13.199-12.221-18.332-5.133-5.133-11.242-9.207-18.33-12.221-7.09-3.01-14.518-4.522-22.285-4.522-7.767 0-15.195 1.507-22.283 4.522-7.089 3.01-13.199 7.088-18.332 12.221-5.133 5.133-9.207 11.244-12.221 18.332-3.01 7.089-4.522 14.516-4.522 22.283 0 7.767 1.507 15.193 4.522 22.283 3.01 7.088 7.088 13.197 12.221 18.33 5.133 5.134 11.244 9.207 18.332 12.222 7.089 3.02 14.516 4.522 22.283 4.522 11.951 0 22.787-3.369 32.509-10.1l27.945 27.863c1.955 2.064 4.397 3.096 7.332 3.096 2.824 0 5.27-1.032 7.332-3.096 2.064-2.063 3.096-4.508 3.096-7.332.0001-2.877-1-5.322-3.01-7.331m-49.41-34.668c-7.143 7.143-15.738 10.714-25.787 10.714-10.05 0-18.643-3.572-25.786-10.714-7.143-7.143-10.714-15.737-10.714-25.786 0-10.05 3.572-18.644 10.714-25.786 7.142-7.143 15.738-10.714 25.786-10.714 10.05 0 18.643 3.572 25.787 10.714 7.143 7.142 10.715 15.738 10.715 25.786 0 10.05-3.573 18.643-10.715 25.786' transform='matrix%28.11417.00745-.00745.11417 3.93 2.548%29' fill='%23ccc'/%3E%3C/svg%3E") no-repeat right center;
+ padding-right: 18px;
+ color: #ccc;
+ text-decoration: none;
+ margin-right: 4px;
+}
+
+#bodyContent span.smwsearch a:hover {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' height='18' width='18' viewBox='0 0 22 22'%3E%3Cpath d='m132.77 118.03l-27.945-27.945c6.735-9.722 10.1-20.559 10.1-32.508 0-7.767-1.508-15.195-4.523-22.283-3.01-7.089-7.088-13.199-12.221-18.332-5.133-5.133-11.242-9.207-18.33-12.221-7.09-3.01-14.518-4.522-22.285-4.522-7.767 0-15.195 1.507-22.283 4.522-7.089 3.01-13.199 7.088-18.332 12.221-5.133 5.133-9.207 11.244-12.221 18.332-3.01 7.089-4.522 14.516-4.522 22.283 0 7.767 1.507 15.193 4.522 22.283 3.01 7.088 7.088 13.197 12.221 18.33 5.133 5.134 11.244 9.207 18.332 12.222 7.089 3.02 14.516 4.522 22.283 4.522 11.951 0 22.787-3.369 32.509-10.1l27.945 27.863c1.955 2.064 4.397 3.096 7.332 3.096 2.824 0 5.27-1.032 7.332-3.096 2.064-2.063 3.096-4.508 3.096-7.332.0001-2.877-1-5.322-3.01-7.331m-49.41-34.668c-7.143 7.143-15.738 10.714-25.787 10.714-10.05 0-18.643-3.572-25.786-10.714-7.143-7.143-10.714-15.737-10.714-25.786 0-10.05 3.572-18.644 10.714-25.786 7.142-7.143 15.738-10.714 25.786-10.714 10.05 0 18.643 3.572 25.787 10.714 7.143 7.142 10.715 15.738 10.715 25.786 0 10.05-3.573 18.643-10.715 25.786' transform='matrix%28.11417.00745-.00745.11417 3.93 2.548%29' fill='%23888888'/%3E%3C/svg%3E") no-repeat right center;
+ padding-right: 20px;
+ color: #888;
+ text-decoration: none;
+ margin-right: 2px;
+}
+
+#bodyContent span.smwbrowse a {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='15' width='15' viewBox='0 0 22 22'%3E%3Cg transform='matrix%28.02146 0 0 .02146 1 1%29' fill='%23ccc'%3E%3Cpath d='m466.07 161.53c-205.6 0-382.8 121.2-464.2 296.1-2.5 5.3-2.5 11.5 0 16.9 81.4 174.9 258.6 296.1 464.2 296.1 205.6 0 382.8-121.2 464.2-296.1 2.5-5.3 2.5-11.5 0-16.9-81.4-174.9-258.6-296.1-464.2-296.1m0 514.7c-116.1 0-210.1-94.1-210.1-210.1 0-116.1 94.1-210.1 210.1-210.1 116.1 0 210.1 94.1 210.1 210.1 0 116-94.1 210.1-210.1 210.1'/%3E%3Ccircle cx='466.08' cy='466.02' r='134.5'/%3E%3C/g%3E%3C/svg%3E") no-repeat right center;
+ padding-right: 18px;
+ color: #ccc;
+ text-decoration: none;
+ margin-right: 4px;
+}
+
+#bodyContent span.smwbrowse a:hover {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='18' width='18' viewBox='0 0 22 22'%3E%3Cg transform='matrix%28.02146 0 0 .02146 1 1%29' fill='%23888888'%3E%3Cpath d='m466.07 161.53c-205.6 0-382.8 121.2-464.2 296.1-2.5 5.3-2.5 11.5 0 16.9 81.4 174.9 258.6 296.1 464.2 296.1 205.6 0 382.8-121.2 464.2-296.1 2.5-5.3 2.5-11.5 0-16.9-81.4-174.9-258.6-296.1-464.2-296.1m0 514.7c-116.1 0-210.1-94.1-210.1-210.1 0-116.1 94.1-210.1 210.1-210.1 116.1 0 210.1 94.1 210.1 210.1 0 116-94.1 210.1-210.1 210.1'/%3E%3Ccircle cx='466.08' cy='466.02' r='134.5'/%3E%3C/g%3E%3C/svg%3E") no-repeat right center;
+ padding-right: 20px;
+ color: #888;
+ text-decoration: none;
+ margin-right: 2px;
+}
+
+.concept-documenation {
+ border-top: 1px dotted #aaa;
+}
+
+/* @since 1.9; Spinner */
+.smw-spinner .text {
+ padding-left: 2.1em;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+/* @since 1.9; Spinner for left side in-text */
+.smw-spinner.left.mw-small-spinner {
+ background-position: left;
+ vertical-align: middle;
+ display: inline-block;
+ padding: 0px !important;
+}
+
+/* @since 1.9; Sppinner for image center */
+.smw-spinner.center.mw-small-spinner {
+ vertical-align: middle;
+ display: inline-block;
+ padding: 0px !important;
+}
+
+/* @since 1.9.1 */
+table.smw-ask-query .smw-ask-query-condition {
+ width: 100%;
+}
+
+table.smw-ask-query .smw-ask-query-printout {
+ width: 100%;
+}
+
+hr.smw-form-horizontalrule {
+ margin-bottom: 10px;
+ background-color: #ddd;
+ }
+
+.smw-horizontalrule {
+ margin-bottom: 10px;
+ border-bottom: 1px solid #ddd;
+ }
+
+.smw-form-select,
+.smw-form-input {
+ padding: 1px 1px;
+}
+
+.smw-form-checkbox {
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+label.smw-form-checkbox {
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.smw-editpage-help {
+ background-color: #f0f0f0;
+ border: 1px solid silver;
+ /* border-top: none; */
+ padding: 0.5em 1em 0.5em 1em;
+ margin-bottom: 1em;
+}
+
+.smw-column-header {
+ font-weight: bold;
+ font-size: 1.17em;
+ line-height: 1.6;
+ margin-top: 0.3em;
+ margin-bottom: 0;
+ padding-bottom: 0;
+ color: black;
+ overflow: hidden;
+ padding-top: .5em;
+}
+
+.smw-column-header:first-child {
+ margin-top: 0em;
+ padding-top: 0em;
+}
+
+.smw-column {
+ float: left;
+ word-wrap: break-word;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+.smw-column-responsive {
+ -webkit-columns: 3 20em;
+ -moz-columns: 3 20em;
+ columns: 3 20em;
+ -webkit-column-gap: 1em;
+ -moz-column-gap: 1em;
+ column-gap: 1em;
+}
+
+.smw-column-twofold-responsive {
+ -webkit-columns: 2 20em;
+ -moz-columns: 2 20em;
+ columns: 2 20em;
+ -webkit-column-gap: 1em;
+ -moz-column-gap: 1em;
+ column-gap: 1em;
+}
+
+.smw-column[dir="rtl"] {
+ float: right;
+}
+
+.smw-column-responsive[dir="rtl"] {
+ float: right;
+}
+
+.smw-columnlist-container .smw-column-responsive {
+ margin-bottom: -2em;
+}
+
+.smw-note {
+ padding: 8px 15px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+}
+
+/* Thanks to the bootstrap css*/
+.smw-callout {
+ padding: 10px;
+ margin: 0.5em 0;
+ border: 1px solid #eee;
+ border-left-width: 5px;
+ border-radius: 3px;
+}
+
+.smw-callout p {
+ margin: 5px 0 5px 0;
+}
+
+.smw-callout-info {
+ border: 1px solid #d9edf7;
+ background-color: #d9edf7;
+ border-left-width: 5px;
+ border-left-color: #1b809e;
+}
+
+.smw-callout-info .title {
+ color: #1b809e;
+}
+
+.smw-callout-info-light {
+ border: 1px solid #f9f9f9;
+ background-color: #f9f9f9;
+ border-left-width: 5px;
+ border-left-color: #ddd;
+}
+
+.smw-callout-info-light .title {
+ color: #1b809e;
+}
+
+.smw-callout-warning {
+ border: 1px solid #fcf8e3;
+ background-color: #fcf8e3;
+ border-left-width: 5px;
+ border-left-color: #aa6708;
+}
+
+.smw-callout-warning .title {
+ color: #aa6708;
+}
+
+.smw-callout-success {
+ border: 1px solid #dff0d8;
+ background-color: #dff0d8;
+ border-left-width: 5px;
+ border-left-color: #3c763d;
+}
+
+.smw-callout-success .title {
+ color: #3c763d;
+}
+
+.smw-callout-error {
+ border: 1px solid #f2dede;
+ background-color: #f2dede;
+ border-left-width: 5px;
+ border-left-color: #ce4844;
+}
+
+.smw-callout .title {
+ margin-top: 0;
+ margin-bottom: 5px;
+ font-size: 16px;
+ font-family: inherit;
+ font-weight: 500;
+}
+
+.smw-svg-icon {
+ display: inline-flex;
+ align-self: center;
+ position: relative;
+ height: 1em;
+ width: 1em;
+}
+.smw-svg-icon svg {
+ height: 1em;
+ width: 1em;
+}
+.smw-svg-icon.svg-baseline svg {
+ bottom: -0.125em;
+ position: absolute;
+}
+
+.smw-icon-info {
+ background:
+ url("data:image/svg+xml,%3C%3Fxml version=%221.0%22 encoding=%22UTF-8%22%3F%3E%0D%0A%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22%3E%0D%0A%09%3Cpath d=%22M11.5 17a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11zm0-12a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13zm.5 5v4h1v1h-3v-1h1v-3h-1v-1zm-1-2h1v1h-1z%22/%3E%0D%0A%3C/svg%3E%0D%0A") no-repeat left center;
+ padding: 0 0 5px 25px;
+}
+
+.smw-icon-pen {
+ background-image: linear-gradient(transparent,transparent),url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2215%22 viewBox=%220 0 20 20%22%3E%3Ctitle%3Ehighlight%3C/title%3E%3Cpath d=%22M18.73 5.86l-3.59-3.59a1 1 0 0 0-1.41 0l-10 10a1 1 0 0 0 0 1.41L4 14l-3 4h5l1-1 .29.29a1 1 0 0 0 1.41 0l10-10a1 1 0 0 0 .03-1.43zM7 15l-2-2 9-9 2 2z%22/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ padding: 0 0 0 25px;
+ background-position: center;
+}
+
+.smw-icon-bookmark {
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 20 20'%3E%3Ctitle%3E bookmark outlined %3C/title%3E%3Cpath d='M15 1H5a2 2 0 0 0-2 2v16l7-5 7 5V3a2 2 0 0 0-2-2zm0 14.25l-5-3.5-5 3.5V3h10z'/%3E%3C/svg%3E%0A");
+ background-repeat: no-repeat;
+ padding: 0 0 0 25px;
+ background-position: center;
+}
+
+.smw-icon-compact {
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='23' height='12'%3E%3Cpath stroke-width='3' stroke='%23000' d='M1,3.5h21M1,9.5h9m3,0h9'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ padding: 0 0 0 15px;
+ background-position: center;
+}
+
+.smwb-group {
+ font-style: normal;
+ font-weight: normal;
+ font-size: 100%;
+}
+
+/* Handling Long Words and URLs (Forcing Breaks, Hyphenation, Ellipsis */
+.smwb-value,
+.smwprops,
+.smwpropname,
+.smwb-factbox .smwtype_wpg,
+.smwtype_uri,
+.smwb-ivalue,
+.smwb-title {
+ word-break: break-word;
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+/* Only apply when less then 350*/
+@media (max-width: 400px) {
+ .smwb-propvalue,
+ .smwb-ipropvalue {
+ word-break: break-word;
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+ }
+}
+
+/* Distinguish subobjects from other titles */
+
+.smw-subobject-entity {
+ font-style: italic;
+}
+
+/* jquery-autocomplete
+.autocomplete-suggestions {
+ border: 1px solid #999;
+ background: #fff;
+ overflow: auto;
+}
+
+.autocomplete-suggestion {
+ padding: 2px 5px;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 0.8em;
+}
+
+.autocomplete-selected {
+ background: #f0f0f0;
+}
+
+.autocomplete-suggestions strong {
+ font-weight: normal;
+ color: #39f;
+}
+
+.autocomplete-group {
+ padding: 2px 5px;
+}
+
+.autocomplete-group strong {
+ display: block;
+ border-bottom: 1px solid #000;
+}
+*/
+
+.autocomplete-suggestions {
+ background: white;
+ border: 1px solid rgb( 221, 221, 221 );
+ border-radius: 3px;
+ box-shadow: rgba( 0, 0, 0, 0.1 ) 0px 0px 5px;
+ min-width: 120px;
+ color: rgb( 111, 128, 146 );
+ float: right;
+ padding-top: 2px;
+ margin-right: -15px;
+ font-size: 16px;
+ font-weight: normal;
+ z-index: 11110 !important;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.autocomplete-arrow.mw-ui-input {
+ background-position-y: 4px;
+}
+
+[dir='rtl'] .autocomplete-suggestions {
+ margin-right: 0px;
+}
+
+.autocomplete-suggestion {
+ display: block;
+ padding: 5px 10px;
+ border-bottom: 1px solid #ddd;
+ cursor: pointer;
+}
+
+.autocomplete-selected {
+ background: #f0f0f0;
+}
+
+.autocomplete-suggestions strong {
+ font-weight: normal;
+ color: #3399ff;
+}
+
+.autocomplete-group {
+ padding: 2px 5px;
+}
+
+.autocomplete-group strong {
+ display: block;
+ border-bottom: 1px solid #000;
+}
+
+.autocomplete-loading {
+ background: url('data:image/gif;base64,R0lGODlhDgAOAPQAAP///wMDAwQEBCwsLDo6Ojs7Ozw8PExMTE5OTlBQUGRkZGdnZ3BwcHV1dYODg4SEhJCQkJiYmLa2try8vL6+vr+/v8DAwMHBwcLCwsPDw8XFxcbGxsjIyAAAAAAAAAAAACH5BAkKAAAAIf4gUmVzaXplZCB3aXRoIGV6Z2lmLmNvbSBHSUYgbWFrZXIAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAADgAOAAAFSSAgjsNojokoiJV1AkQBBMBGnYhoMCLHYiLEofGaVEyKxesFWQIkkkzk4XBaTZmLqTJZUo4ADFgz6gJcJopWAqiATRYwO3zNvEIAIfkECQoAAAAsAAAAAA4ADgCE////HR0dHh4eIiIiKioqNTU1Pj4+QUFBS0tLUFBQUlJSYWFhfX19jo6OkpKSlZWVnZ2dn5+fqamptra2vLy8vb29vr6+v7+/wMDAwcHBwsLCw8PDxcXFAAAAAAAAAAAABUkgII7HaI6LKIgDcQKKAgSFEZyOmDCigQAXjejRkLwqFxMk8npRmoDJZLORQq+mTcZ0qTQtSYAmzBk9AZiTZTsBhk2YcFuM3bxCACH5BAkKAAAALAAAAAAOAA4AhP///yMjIyUlJTQ0NDY2Njs7Oz4+PkxMTFJSUlRUVFtbW2BgYGZmZpKSkpSUlJWVlaCgoKqqqra2tre3t7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8XFxcbGxsrKygAAAAVMICCOymiOkEiIhXECTwMMB7KamOhEYsIAgoLoUvG8AgITp/NanCgv0USy2Ugk0exrkzERo5aLSCMGcEZQQM5k6WIvZVwZC9BoAZtXCAAh+QQJCgAAACwAAAAADgAOAIT///8DAwMEBAQsLCw6Ojo7Ozs8PDxMTExOTk5QUFBkZGRnZ2dwcHB1dXWDg4OEhISQkJCYmJi2tra8vLy9vb2+vr6/v7/AwMDCwsLDw8PExMTFxcXGxsbHx8fKysoAAAAFSiAgjtFojpr4iAhyApYFOJBynJdoeeLSAISEjsI7GQqmzebFOE1eooBgkJFIoNgXp2OyUKAVGQAjXoqegJypkrvGXhfxdZwFZF4hACH5BAkKAAAALAAAAAAOAA4Ahf///x0dHR4eHiEhISQkJDc3N0BAQEFBQUpKSk9PT1JSUmFhYXt7e4+Pj5GRkZKSkpmZmZqamp2dnaCgoKSkpKmpqa2trba2try8vL29vb6+vr+/v8DAwMLCwsPDw8TExMXFxcrKysvLywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZWQIBwaBkahx9hROh4HAGbDQBCkTSOHOFGJJxUAIqFNhN6JhRGEOjJOGKewoDg4Llc4IAC3mgwGDcZTwgBA0IdUgBrQoEABEcaWXdRTxyIdwAdewAeT0EAIfkECQoAAAAsAAAAAA4ADgCF////JCQkJSUlNDQ0NjY2Ozs7Pj4+TExMUlJSVFRUW1tbYmJiampqkZGRk5OTnp6epKSkqKioqqqqrKyssLCwtbW1tra2t7e3vLy8vb29vr6+v7+/wMDAwsLCw8PDxMTExcXFysrKzc3NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlhAgHDoGRqHH6FFGJkcAZsNwIKhQI4c4UYkrGgAjYc2E3o6GkYQ6Ck5Yp7CAUHhsVTggAPemEAYNxlPDAQFQh1SAGsACwFCBkcaWRcAAgJPHIhLAIV7RUdBACH5BAkKAAAALAAAAAAOAA4Ahf///wMDAwQEBCwsLDo6Ojs7Ozw8PExMTE5OTlBQUGRkZGdnZ3BwcHV1dYODg4SEhJCQkJiYmLa2try8vL29vb6+vr+/v8DAwMLCwsPDw8TExMXFxcbGxsfHx8jIyMrKysvLy83NzQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZWQIBwmBkahxqhRGi5HAEWC0Ay2VCOTmhI2AFBk1DK5xk1bjbPsXHyFDoekYxk2Ya0jwuF0cI+Ng4IQhhSAB5CDAZCgUYVHAABAAUETxeEAkIJd0IDT0EAIfkECQoAAAAsAAAAAA4ADgCF////HR0dHh4eIiIiKioqNjY2Ojo6QEBAQUFBSkpKUFBQUVFRUlJSYWFhfX19j4+PkZGRkpKSnZ2dn5+fqamptra2vLy8vb29v7+/wMDAwsLCw8PDxMTExcXFx8fHysrKy8vLzc3NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlZAgHC4GRqHHGFFiMkcARgMoGLpXI5OaEjoAUGT0MvnGTV2Os+t0fJUVjabyrLNbhsnEiPmeqQ8IEIaUgkGQg4KQhFHAQcFAQALDE8EA0ICQg12QghPQQAh+QQJCgAAACwAAAAADgAOAIX///8jIyMlJSU0NDQ2NjY3Nzc7Ozs+Pj5MTExSUlJUVFRWVlZgYGBqamqRkZGTk5OVlZWenp6qqqq2tra3t7e8vLy9vb2/v7/AwMDDw8PExMTFxcXGxsbHx8fKysrLy8vNzc0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGWUCAcJgZGoca4UR4wRwBlwtgUtlYjk4oSNj5QJNQi+cZNW42z62x8lROMpkJpQ1gGxlPDnooCDw/FlIABgIADQpCEg9CWUMECQgDABAOTwcGQgVCEXRCC09BACH5BAkKAAAALAAAAAAOAA4Ahf///wICAgQEBCIiIiwsLDo6Ojs7Ozw8PExMTE5OTlBQUGRkZGdnZ3BwcHV1dYODg4SEhJCQkJiYmLa2try8vL29vb+/v8DAwMPDw8TExMXFxcbGxsfHx8jIyMrKysvLy83NzQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZWQIBwiBkahxnhRGi5HAEWC2BC0VSOTihIyPFAk9CK9xg1ejrPrXHwVE4wBEGgDaAcG8+Nxmg4PD8VUgAKBQAODEIegllDCAsRD1CCRgkJQhBCYHQST0EAIfkECQoAAAAsAAAAAA4ADgCF////AgICBAQEHBwcICAgISEhJCQkLCwsQEBAQUFBT09PUlJSVlZWYWFhenp6jo6OkpKSlZWVnZ2dn5+fqamptra2vLy8vb29v7+/wMDAw8PDxMTExcXFxsbGx8fHy8vLzc3NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlVAgHCoGRqHG2FFiMkcARgMoGLhXI4GJkjo+UCTgMKA8YwaE4jn1kh4KiuagyDgBliOjmeHY1wonh8XUgANCwAUE0Igg05GDxIWS2ZHEBFKQmB1RUdBACH5BAkKAAAALAAAAAAOAA4Ahf///yMjIyUlJTQ0NDY2Njs7Oz4+PkxMTFJSUlRUVFtbW2BgYGpqapKSkpOTk5SUlJ6enqqqqra2tre3t7y8vL29vb+/v8DAwMPDw8TExMXFxcbGxsfHx8jIyMrKysvLy83NzQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZZQIBwiBkahwWhRGi5HAECAWBC0VSOBmFgIeR8AJaMsEBgPC0WIyLxBB0PT6UEoyAM4gDKMfLcaIwNDk8fFWkAEA8AHh1CIIZORhUaFEtoTxeGSwBieABFR0EAOw==') no-repeat left center;
+ padding: 5px 0 5px 35px;
+ vertical-align: middle;
+ background-position: right 10px center;
+}
+
+/* https://codepen.io/vkjgr/pen/VYMeXp */
+.autocomplete-arrow-light {
+ background-image: linear-gradient(45deg, transparent 50%, gray 50%), linear-gradient(135deg, gray 50%, transparent 50%), linear-gradient(to right, #ffffff, #ffff);
+ background-position: calc(100% - 17px) calc(1em + 2px), calc(100% - 12px) calc(1em + 2px), calc(100% - 2.5em) 0.5em;
+ background-size: 5px 5px, 5px 5px, 1px 1.5em;
+ background-repeat: no-repeat;
+}
+
+.mw-ui-input.autocomplete-loading {
+ vertical-align: unset;
+ padding: 0.57142857em 0.57142857em 0.5em;
+}
+
+.autocomplete-arrow {
+/*background: transparent; */
+ background-image: url("data:image/svg+xml,%3Csvg fill='%23888888' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position-x: calc(100% - 6px);
+ background-position-y: 5px;
+}
+
+.autocomplete-arrow.mw-ui-input {
+ background-position-y: 4px;
+}
+
+[dir='rtl'] .autocomplete-arrow {
+ background-position-x:6px;
+}
+
+.skin-vector input#smw-property-input.autocomplete-suggestions {
+ height: 1.15em;
+ padding: 2px 2px;
+}
+
+.skin-chameleon input#smw-property-input.autocomplete-suggestions {
+ height: 30px;
+ padding: 2px 2px;
+}
+
+.skin-foreground input#smw-property-input.autocomplete-suggestions {
+ height: 32px;
+ padding: 2px 0px 0px 8px;
+}
+
+.skin-chameleon .autocomplete-suggestion,
+.skin-foreground .autocomplete-suggestion {
+ padding: 2px 5px;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 0.9em;
+}
+
+.smw-breadcrumb-link {
+ color: #7d7d7d;
+ font-size: 84%;
+ line-height: 1.2em;
+ margin: 0.4em 0 0.6em 0;
+ width: auto;
+}
+
+.smw-breadcrumb-arrow-right {
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 5px solid #aaa;
+ display: inline-block;
+ position: relative;
+ margin-left: 0px;
+ margin-right: 4px;
+}
+
+.smw-ask-action-btn,
+.smw-action-btn {
+ display: inline-block;
+ padding: 3px 6px;
+ margin-bottom: 0;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.smw-action-btn-min200 {
+ min-width: 200px;
+}
+
+.smw-ask-action-btn-lgrey,
+a.smw-ask-action-btn-lgrey:visited,
+.smw-action-btn-lgrey,
+.smw-action-btn-lgrey:visited {
+ color: #222;
+ background-color: #eee;
+ border-color: #ddd;
+ text-decoration:none;
+}
+
+a.smw-ask-action-btn-lgrey:hover,
+.smw-action-btn-lgrey:hover {
+ color: #222;
+ background-color: #ddd;
+ border-color: #ddd;
+ text-decoration:none;
+}
+
+.smw-ask-action-btn-lblue,
+a.smw-ask-action-btn-lblue:visited {
+ color: #fff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+ text-decoration: none;
+}
+
+a.smw-ask-action-btn-lblue:hover {
+ color: #fff;
+ background-color: #31b0d5;
+ border-color: #269abc;
+ text-decoration: none;
+}
+
+.smw-ask-action-btn-dblue,
+a.smw-ask-action-btn-dblue:visited {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+ text-decoration: none;
+}
+
+.smw-ask-action-btn-dblue:hover,
+.smw-ask-action-btn-dblue:focus,
+a.smw-ask-action-btn-dblue:hover,
+a.smw-ask-action-btn-dblue:focus {
+ color: #fff;
+ background-image:none;
+ background-color: #286090;
+ border-color: #204d74;
+ text-decoration: none;
+}
+
+.smw-concept-page-indicator a.external.text,
+.smw-page-indicator a.external.text {
+ background-image: none;
+ padding-right: 0;
+}
+
+.smw-page-indicator {
+ display: inline-block;
+ font-weight: normal;
+ line-height: 1.25;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ /* background-color: #e6e6e6; */
+ border-color: #ddd;
+}
+
+.smw-protection-indicator {
+ display: inline-block;
+ /* font-weight: normal; */
+ /* line-height: 1.25; */
+ /* text-align: center; */
+ /* white-space: nowrap; */
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.15rem 0.25rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ border-color: transparent;
+}
+
+.smw-protection-indicator.with-border {
+ border-color: #ddd;
+}
+
+.smw-page-indicator.usage-count.moderate {
+ background-color: #f2d29b;
+ border: 1px solid #f2d29b;
+ color: #aa4c2c;
+}
+
+.smw-page-indicator.usage-count.high {
+ background-color: #ff887f;
+ border: 1px solid #ffB79e;
+ color: #800000;
+}
+
+.smw-page-indicator-rdflink {
+ line-height: 24px;
+ margin-right: 10px;
+}
+
+.smw-page-indicator-rdflink a {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' overflow='visible' height='18' width='18' viewBox='0 0 94.332 101.883'%3E%3Cg shape-rendering='geometricPrecision' text-rendering='geometricPrecision' image-rendering='optimizeQuality'%3E%3Cpath d='M84.45,66.836c-0.636-0.337-1.284-0.624-1.936-0.879l0.466-0.038c0,0-4.151-1.838-4.514-15.18 c-0.359-13.344,3.957-15.62,3.957-15.62l-0.62,0.027c3.261-1.673,6.066-4.316,7.917-7.804c4.823-9.072,1.372-20.341-7.702-25.165 C72.94-2.641,61.674,0.802,56.854,9.883c-1.982,3.725-2.545,7.817-1.919,11.683l-0.212-0.326c0,0,1.093,4.842-10.258,11.888 c-11.349,7.05-16.469,3.54-16.469,3.54l0.326,0.48c-0.325-0.201-0.636-0.406-0.975-0.583C18.269,31.741,7,35.188,2.178,44.266 c-4.82,9.077-1.372,20.341,7.703,25.167c6.766,3.591,14.744,2.59,20.365-1.914l-0.122,0.236c0,0,4.132-3.399,16.04,2.994 c9.4,5.044,10.796,9.988,10.975,11.846c-0.246,6.893,3.347,13.654,9.847,17.107c9.075,4.825,20.344,1.375,25.164-7.701 C96.974,82.926,93.528,71.656,84.45,66.836z M63.466,69.282c-1.504,0.532-5.801,1.121-14.847-3.73 c-9.797-5.26-11.251-9.654-11.464-10.973c0.139-1.6,0.05-3.197-0.223-4.755l0.06,0.09c0,0-0.798-4.274,10.412-11.235 c10.033-6.228,14.594-4.989,15.443-4.664c0.546,0.371,1.112,0.717,1.706,1.033c1.129,0.6,2.293,1.07,3.472,1.418 c1.38,1.314,3.92,5.045,4.184,14.854c0.27,9.883-2.634,13.694-4.217,15.042C66.362,67.1,64.836,68.085,63.466,69.282z' fill='%230C479C'/%3E%3Cg%3E%3Cpath d='M62.239,8.1c-5.415,5.923-5.529,14.636-0.312,19.566c-2.579-2.483-2.523-7.651,0.083-12.597 c0.335-0.443,1.306-1.49,2.725-1.014c0.143,0.049,0.237,0.062,0.292,0.053c0.321,0.069,0.65,0.11,0.99,0.095 c2.155-0.098,3.822-1.921,3.725-4.077c-0.044-0.967-0.445-1.823-1.065-2.48c5.002-3.277,10.742-3.652,13.094-1.504l0.09,0.006 C76.488,1.242,67.705,2.119,62.239,8.1z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M7.632,62.845c-0.046-0.047-0.093-0.102-0.141-0.148c0.03,0.031,0.059,0.069,0.095,0.102L7.632,62.845z' fill='%23FFFFFF'/%3E%3Cpath d='M7.805,43.13c-5.416,5.924-5.529,14.635-0.313,19.566c-2.578-2.484-2.523-7.652,0.083-12.598 c0.336-0.444,1.308-1.49,2.727-1.014c0.141,0.049,0.236,0.061,0.292,0.054c0.321,0.069,0.651,0.11,0.99,0.095 c2.156-0.099,3.822-1.922,3.725-4.076c-0.045-0.967-0.445-1.824-1.063-2.48c4.999-3.276,10.74-3.654,13.092-1.505l0.089,0.008 C22.054,36.271,13.269,37.147,7.805,43.13z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M65.256,92.504c-0.047-0.048-0.094-0.102-0.141-0.148c0.029,0.031,0.059,0.069,0.094,0.101L65.256,92.504z' fill='%23FFFFFF'/%3E%3Cpath d='M65.428,72.786c-5.416,5.926-5.529,14.639-0.313,19.569c-2.58-2.483-2.523-7.653,0.082-12.597 c0.336-0.445,1.307-1.49,2.727-1.014c0.143,0.047,0.235,0.061,0.292,0.053c0.32,0.069,0.651,0.11,0.99,0.096 c2.154-0.1,3.82-1.924,3.723-4.08c-0.044-0.966-0.445-1.822-1.063-2.479c5-3.275,10.739-3.652,13.093-1.504l0.088,0.007 C79.677,65.93,70.891,66.807,65.428,72.786z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A") center left no-repeat;
+ padding-left: 28px;
+ display: inline-block;
+ height: 24px;
+ line-height: 24px;
+}
+
+.smw-reference-indicator {
+ vertical-align: super;
+ margin-left: 2px;
+ font-size: 10px;
+}
+
+.smw-reference-indicator::before {
+ content: '';
+}
+
+.smw-reference-indicator::after {
+ content: '≡';
+}
+
+.smw-admin-circle-orange {
+ width: 14px;
+ height: 14px;
+ background: orange;
+ position: absolute;
+ display: block;
+ border-radius: 50%;
+ display: inline-block;
+ margin-right: 20px;
+ margin-top: 3px;
+}
+
+.is-disabled {
+ opacity: .5;
+ position: relative;
+ pointer-events: none;
+}
+
+.is-disabled::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ content: ' ';
+}
+
+.smw-break-word {
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ -ms-hyphens: auto;
+ -moz-hyphens: auto;
+ -webkit-hyphens: auto;
+ hyphens: auto;
+}
+
+.smw-break-word a {
+ -ms-word-break: break-all;
+ word-break: break-all;
+ word-break: break-word;
+}
+
+.smw-flex-center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.smw-absolute {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+/* http://stackoverflow.com/questions/6091253/overlay-with-spinner */
+
+.smw-margin-small {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.smw-margin-extra {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 100px;
+}
+
+.smw-loading-image-dots {
+ background: url('data:image/gif;base64,R0lGODlhIgAUAMQRAOjp6dTW19ze3+Xm54iKjevs7IKEh/f396Olp5ianPz8/Judn9fY2YGDhp+ho4CChdHT1P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxRThFNDZFQ0RFQjJFMjExQUZCNUREQjU1MEFCRTI5OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNkVFNURCOUIyREYxMUUyQTBEQUNFNUIzREVGNjg0MyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNkVFNURCOEIyREYxMUUyQTBEQUNFNUIzREVGNjg0MyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoyMDhFNDZFQ0RFQjJFMjExQUZCNUREQjU1MEFCRTI5OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxRThFNDZFQ0RFQjJFMjExQUZCNUREQjU1MEFCRTI5OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAkyABEALAAAAAAiABQAAAVeYCSOZGmeaKqubOu+cCzPdM0iBIGYgCAUPB8Q5XgYHw4SAMKEAJRNJ8pwfBhIgagAq0U1qg1ukyFmkk+L6oI0iD5H7ebbdEgYEweSYhAIDEp7fX82hIWGh4iJiosoIQAh+QQJMgARACwAAAAAIgAUAAAFZGAkjmRpnmiqrmzrvnAsz3TNFoIAmAhBICZArpACQI6Q3cjxaD4cJCNSaRIgIQGSwfkwkAJXAYpxzY4a3Mb3ykBJjwPSgrsgDa5U0yAQGChIBwlNCQckCnt9NoqLjI2Oj5CRKSEAIfkEBTIAEQAsAAAAACIAFAAABWNgJI5kaZ5oqq5s675wLM90zRaCAJi4biIEAiIFgBghu1HxmBQ5HtCHAyU4QgKk6hE7MkQfBhTDyhWNt6TGt4FaGgckNwQ+WnwXqUEgMFCU9Hx+IwcJUAkHNomKi4yNjo+QKCEAOw==') no-repeat left center;
+ padding: 5px 0 5px 35px;
+ vertical-align: middle;
+}
+
+.smw-overlay-spinner.small {
+ height: 20px;
+ width: 20px;
+}
+.smw-overlay-spinner.medium {
+ height: 40px;
+ width: 40px;
+}
+
+.smw-overlay-spinner.large {
+ height: 60px;
+ width: 60px;
+}
+
+.smw-overlay-spinner.flex {
+ left: unset;
+ top: unset;
+}
+
+.smw-overlay-spinner {
+ position: absolute;
+ left: 47%;
+ top: 30%;
+ height: 60px;
+ width: 60px;
+ margin: 0px auto;
+ -webkit-animation: rotation 1.0s infinite linear;
+ -moz-animation: rotation 1.0s infinite linear;
+ -o-animation: rotation 1.0s infinite linear;
+ animation: rotation 1.0s infinite linear;
+ border-left: 6px solid rgba( 0, 174, 239, .15 );
+ border-right: 6px solid rgba( 0, 174, 239, .15 );
+ border-bottom: 6px solid rgba( 0, 174, 239, .15 );
+ border-top: 6px solid rgba( 0, 174, 239, .8 );
+ border-radius: 100%;
+}
+
+@-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(359deg);}
+}
+
+@-moz-keyframes rotation {
+ from {-moz-transform: rotate(0deg);}
+ to {-moz-transform: rotate(359deg);}
+}
+
+@-o-keyframes rotation {
+ from {-o-transform: rotate(0deg);}
+ to {-o-transform: rotate(359deg);}
+}
+
+@keyframes rotation {
+ from {transform: rotate(0deg);}
+ to {transform: rotate(359deg);}
+}
+
+/* MobileFrontend overrides some desktop rules */
+.content table.broadtable,
+.smw-ask-result table.broadtable {
+ width: 100% !important;
+}
+
+/**
+ * Responsive settings (#see smw.table.css)
+ */
+@media screen and (max-width: 800px) {
+
+}
+
+/**
+ * JSON
+ */
+.mw-json .value, .mw-json-single-value {
+ word-break: break-word;
+}
+
+/**
+ * User preference
+ */
+fieldset#mw-prefsection-smw-general-options,
+fieldset#mw-prefsection-smw-ask-options,
+fieldset#mw-prefsection-smw-srf {
+ border: 0px solid #2a4b8d;
+ border-top: 1px solid #ddd;
+ margin-top: -5px;
+ margin-bottom: 1px;
+ padding-bottom: 5px;
+ box-shadow: 0;
+}
+
+fieldset#mw-prefsection-smw-general-options .htmlform-tip,
+fieldset#mw-prefsection-smw-ask-options .htmlform-tip,
+fieldset#mw-prefsection-smw-srf .htmlform-tip{
+ padding-left: 25px !important;
+}
+
+#mw-prefsection-smw-general-options legend,
+#mw-prefsection-smw-ask-options legend {
+ font-weight: 100;
+}
+
+#mw-prefsection-smw-srf legend::before {
+ content: 'â–¼ ';
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.dropdown.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.dropdown.css
new file mode 100644
index 00000000..d4c08903
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.dropdown.css
@@ -0,0 +1,153 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ *
+ * https://codepen.io/mbdavid/pen/xGLaBJ
+ */
+
+.smw-dropdown {
+ position: relative;
+ display: inline-block;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 14px;
+}
+
+.smw-dropdown > a,
+.smw-dropdown > button {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ background-color: white;
+ border: 1px solid #ccc;
+ padding: 6px 20px 6px 10px;
+ border-radius: 4px;
+ display: inline-block;
+ color: black;
+ text-decoration: none;
+}
+
+.smw-dropdown > a:before,
+.smw-dropdown > button:before {
+ position: absolute;
+ right: 12px;
+ top: 12px;
+ content: '';
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid black;
+}
+
+.smw-dropdown input[type=checkbox] {
+ position: absolute;
+ display: block;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ margin: 0px;
+ opacity: 0;
+}
+
+.smw-dropdown input[type=checkbox]:checked {
+ position: fixed;
+ z-index: +0;
+ top: 0px; left: 0px;
+ right: 0px; bottom: 0px;
+}
+
+.smw-dropdown .smw-dropdown-menu {
+ position: absolute;
+ top: 31px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ left: 0px;
+ list-style: none;
+ padding: 4px 0px !important;
+ display: none;
+ background-color: white;
+ box-shadow: 0 3px 6px rgba( 0, 0, 0, .175 );
+ margin: 0 0 0 0 !important;
+}
+
+.smw-dropdown input[type=checkbox]:checked + .smw-dropdown-menu {
+ display: block;
+}
+
+.smw-dropdown .smw-dropdown-menu li {
+ display: block;
+ padding: 4px 20px;
+ white-space: nowrap;
+ min-width: 100px;
+ margin-bottom: 0;
+}
+
+.smw-dropdown .smw-dropdown-menu li:hover {
+ background-color: #f5f5f5;
+ cursor: pointer;
+}
+
+.smw-dropdown .smw-dropdown-menu li a {
+ text-decoration: none;
+ display: block;
+ color: black
+}
+
+.smw-dropdown .smw-dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ font-size: 1px;
+ padding: 0;
+}
+
+.smw-dropdown-menu::before {
+ top: -16px;
+ right: 9px;
+ left: auto;
+ border: 8px solid transparent;
+ border-bottom-color: rgba( 27, 31, 35, 0.15 );
+}
+
+.smw-dropdown-menu::before {
+ top: -16px;
+ right: 7px;
+ left: auto;
+ border: 8px solid transparent;
+ border-bottom-color: rgba( 27, 31, 35, 0.15 );
+ position: absolute;
+ display: inline-block;
+ content: '';
+}
+
+.smw-dropdown-menu::after {
+ position: absolute;
+ display: inline-block;
+ content: '';
+ top: -14px;
+ right: 8px;
+ left: auto;
+ border: 7px solid transparent;
+ border-bottom-color: #fff;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.factbox.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.factbox.css
new file mode 100644
index 00000000..28ae3e3d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.factbox.css
@@ -0,0 +1,214 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Factbox related CSS rules
+ *
+ * @file
+ * @ingroup semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+.smwfact {
+ clear: both;
+ background-color: #f9f9f9;
+ padding: 5px;
+ margin-top: 1em;
+ border: 1px solid #aaa;
+ font-size: 95%;
+ min-height: 23px; /* required by SMW_FACTBOX_SHOWN */
+}
+
+.smwfact td,
+.smwfact tr,
+.smwfact table {
+ background-color: #f9f9f9;
+}
+
+.smwfact .smwfactboxhead {
+ font-size: 110%;
+ font-weight: bold;
+ float: left;
+ margin-bottom: 2px;
+ max-width: 90%;
+}
+
+.smwfact .smwfacttable {
+ border-top: 1px dotted #aaa;
+ width: 100%;
+ clear: both;
+}
+
+.smwfact .smw-table-cell {
+ border:none;
+}
+
+.smw-table-row .smwpropname,
+.smw-table-row .smwpropname,
+.smw-table-row .smwspecname {
+ text-align: right;
+ vertical-align: top;
+ padding-right: 1em;
+ width: 33%;
+}
+
+.smwfact .smw-table-row:nth-child(odd) {
+ background-color: #f9f9f9;
+}
+
+.smwfact .smw-table-row:nth-child(even) {
+ background-color: #fff;
+}
+
+.smwfact .smwpropname {
+ width: 33%;
+}
+
+.smwfact .smwprops,
+.smwfact .smwspecs {
+ width: 100%;
+}
+
+.swmfactboxheadbrowse a {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='18' width='18' viewBox='0 0 22 22'%3E%3Cg transform='matrix%28.02146 0 0 .02146 1 1%29' fill='%23ccc'%3E%3Cpath d='m466.07 161.53c-205.6 0-382.8 121.2-464.2 296.1-2.5 5.3-2.5 11.5 0 16.9 81.4 174.9 258.6 296.1 464.2 296.1 205.6 0 382.8-121.2 464.2-296.1 2.5-5.3 2.5-11.5 0-16.9-81.4-174.9-258.6-296.1-464.2-296.1m0 514.7c-116.1 0-210.1-94.1-210.1-210.1 0-116.1 94.1-210.1 210.1-210.1 116.1 0 210.1 94.1 210.1 210.1 0 116-94.1 210.1-210.1 210.1'/%3E%3Ccircle cx='466.08' cy='466.02' r='134.5'/%3E%3C/g%3E%3C/svg%3E")
+ no-repeat
+ right center;
+ padding-right: 20px;
+ text-decoration: none;
+ margin-right: 4px;
+}
+
+.swmfactboxheadbrowse a:hover {
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='20' width='20' viewBox='0 0 22 22'%3E%3Cg transform='matrix%28.02146 0 0 .02146 1 1%29' fill='%23888888'%3E%3Cpath d='m466.07 161.53c-205.6 0-382.8 121.2-464.2 296.1-2.5 5.3-2.5 11.5 0 16.9 81.4 174.9 258.6 296.1 464.2 296.1 205.6 0 382.8-121.2 464.2-296.1 2.5-5.3 2.5-11.5 0-16.9-81.4-174.9-258.6-296.1-464.2-296.1m0 514.7c-116.1 0-210.1-94.1-210.1-210.1 0-116.1 94.1-210.1 210.1-210.1 116.1 0 210.1 94.1 210.1 210.1 0 116-94.1 210.1-210.1 210.1'/%3E%3Ccircle cx='466.08' cy='466.02' r='134.5'/%3E%3C/g%3E%3C/svg%3E")
+ no-repeat
+ right center;
+ padding-right: 22px;
+ text-decoration: none;
+ margin-right: 2px;
+}
+
+.rdflink {
+ float: right;
+ margin-top:2px;
+}
+
+.rdflink a {
+ padding-top: 2px;
+ padding-right: 15px;
+ color: #888;
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' overflow='visible' height='10' width='10' viewBox='0 0 94.332 101.883'%3E%3Cg shape-rendering='geometricPrecision' text-rendering='geometricPrecision' image-rendering='optimizeQuality'%3E%3Cpath d='M84.45,66.836c-0.636-0.337-1.284-0.624-1.936-0.879l0.466-0.038c0,0-4.151-1.838-4.514-15.18 c-0.359-13.344,3.957-15.62,3.957-15.62l-0.62,0.027c3.261-1.673,6.066-4.316,7.917-7.804c4.823-9.072,1.372-20.341-7.702-25.165 C72.94-2.641,61.674,0.802,56.854,9.883c-1.982,3.725-2.545,7.817-1.919,11.683l-0.212-0.326c0,0,1.093,4.842-10.258,11.888 c-11.349,7.05-16.469,3.54-16.469,3.54l0.326,0.48c-0.325-0.201-0.636-0.406-0.975-0.583C18.269,31.741,7,35.188,2.178,44.266 c-4.82,9.077-1.372,20.341,7.703,25.167c6.766,3.591,14.744,2.59,20.365-1.914l-0.122,0.236c0,0,4.132-3.399,16.04,2.994 c9.4,5.044,10.796,9.988,10.975,11.846c-0.246,6.893,3.347,13.654,9.847,17.107c9.075,4.825,20.344,1.375,25.164-7.701 C96.974,82.926,93.528,71.656,84.45,66.836z M63.466,69.282c-1.504,0.532-5.801,1.121-14.847-3.73 c-9.797-5.26-11.251-9.654-11.464-10.973c0.139-1.6,0.05-3.197-0.223-4.755l0.06,0.09c0,0-0.798-4.274,10.412-11.235 c10.033-6.228,14.594-4.989,15.443-4.664c0.546,0.371,1.112,0.717,1.706,1.033c1.129,0.6,2.293,1.07,3.472,1.418 c1.38,1.314,3.92,5.045,4.184,14.854c0.27,9.883-2.634,13.694-4.217,15.042C66.362,67.1,64.836,68.085,63.466,69.282z' fill='%230C479C'/%3E%3Cg%3E%3Cpath d='M62.239,8.1c-5.415,5.923-5.529,14.636-0.312,19.566c-2.579-2.483-2.523-7.651,0.083-12.597 c0.335-0.443,1.306-1.49,2.725-1.014c0.143,0.049,0.237,0.062,0.292,0.053c0.321,0.069,0.65,0.11,0.99,0.095 c2.155-0.098,3.822-1.921,3.725-4.077c-0.044-0.967-0.445-1.823-1.065-2.48c5.002-3.277,10.742-3.652,13.094-1.504l0.09,0.006 C76.488,1.242,67.705,2.119,62.239,8.1z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M7.632,62.845c-0.046-0.047-0.093-0.102-0.141-0.148c0.03,0.031,0.059,0.069,0.095,0.102L7.632,62.845z' fill='%23FFFFFF'/%3E%3Cpath d='M7.805,43.13c-5.416,5.924-5.529,14.635-0.313,19.566c-2.578-2.484-2.523-7.652,0.083-12.598 c0.336-0.444,1.308-1.49,2.727-1.014c0.141,0.049,0.236,0.061,0.292,0.054c0.321,0.069,0.651,0.11,0.99,0.095 c2.156-0.099,3.822-1.922,3.725-4.076c-0.045-0.967-0.445-1.824-1.063-2.48c4.999-3.276,10.74-3.654,13.092-1.505l0.089,0.008 C22.054,36.271,13.269,37.147,7.805,43.13z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M65.256,92.504c-0.047-0.048-0.094-0.102-0.141-0.148c0.029,0.031,0.059,0.069,0.094,0.101L65.256,92.504z' fill='%23FFFFFF'/%3E%3Cpath d='M65.428,72.786c-5.416,5.926-5.529,14.639-0.313,19.569c-2.58-2.483-2.523-7.653,0.082-12.597 c0.336-0.445,1.307-1.49,2.727-1.014c0.143,0.047,0.235,0.061,0.292,0.053c0.32,0.069,0.651,0.11,0.99,0.096 c2.154-0.1,3.82-1.924,3.723-4.08c-0.044-0.966-0.445-1.822-1.063-2.479c5-3.275,10.739-3.652,13.093-1.504l0.088,0.007 C79.677,65.93,70.891,66.807,65.428,72.786z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A")
+ center right no-repeat;
+}
+
+.rdflink a:hover {
+ padding-top: 2px;
+ padding-right: 15px;
+ text-decoration: none;
+ color: #0000ff;
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1' overflow='visible' height='12' width='12' viewBox='0 0 94.332 101.883'%3E%3Cg shape-rendering='geometricPrecision' text-rendering='geometricPrecision' image-rendering='optimizeQuality'%3E%3Cpath d='M84.45,66.836c-0.636-0.337-1.284-0.624-1.936-0.879l0.466-0.038c0,0-4.151-1.838-4.514-15.18 c-0.359-13.344,3.957-15.62,3.957-15.62l-0.62,0.027c3.261-1.673,6.066-4.316,7.917-7.804c4.823-9.072,1.372-20.341-7.702-25.165 C72.94-2.641,61.674,0.802,56.854,9.883c-1.982,3.725-2.545,7.817-1.919,11.683l-0.212-0.326c0,0,1.093,4.842-10.258,11.888 c-11.349,7.05-16.469,3.54-16.469,3.54l0.326,0.48c-0.325-0.201-0.636-0.406-0.975-0.583C18.269,31.741,7,35.188,2.178,44.266 c-4.82,9.077-1.372,20.341,7.703,25.167c6.766,3.591,14.744,2.59,20.365-1.914l-0.122,0.236c0,0,4.132-3.399,16.04,2.994 c9.4,5.044,10.796,9.988,10.975,11.846c-0.246,6.893,3.347,13.654,9.847,17.107c9.075,4.825,20.344,1.375,25.164-7.701 C96.974,82.926,93.528,71.656,84.45,66.836z M63.466,69.282c-1.504,0.532-5.801,1.121-14.847-3.73 c-9.797-5.26-11.251-9.654-11.464-10.973c0.139-1.6,0.05-3.197-0.223-4.755l0.06,0.09c0,0-0.798-4.274,10.412-11.235 c10.033-6.228,14.594-4.989,15.443-4.664c0.546,0.371,1.112,0.717,1.706,1.033c1.129,0.6,2.293,1.07,3.472,1.418 c1.38,1.314,3.92,5.045,4.184,14.854c0.27,9.883-2.634,13.694-4.217,15.042C66.362,67.1,64.836,68.085,63.466,69.282z' fill='%230C479C'/%3E%3Cg%3E%3Cpath d='M62.239,8.1c-5.415,5.923-5.529,14.636-0.312,19.566c-2.579-2.483-2.523-7.651,0.083-12.597 c0.335-0.443,1.306-1.49,2.725-1.014c0.143,0.049,0.237,0.062,0.292,0.053c0.321,0.069,0.65,0.11,0.99,0.095 c2.155-0.098,3.822-1.921,3.725-4.077c-0.044-0.967-0.445-1.823-1.065-2.48c5.002-3.277,10.742-3.652,13.094-1.504l0.09,0.006 C76.488,1.242,67.705,2.119,62.239,8.1z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M7.632,62.845c-0.046-0.047-0.093-0.102-0.141-0.148c0.03,0.031,0.059,0.069,0.095,0.102L7.632,62.845z' fill='%23FFFFFF'/%3E%3Cpath d='M7.805,43.13c-5.416,5.924-5.529,14.635-0.313,19.566c-2.578-2.484-2.523-7.652,0.083-12.598 c0.336-0.444,1.308-1.49,2.727-1.014c0.141,0.049,0.236,0.061,0.292,0.054c0.321,0.069,0.651,0.11,0.99,0.095 c2.156-0.099,3.822-1.922,3.725-4.076c-0.045-0.967-0.445-1.824-1.063-2.48c4.999-3.276,10.74-3.654,13.092-1.505l0.089,0.008 C22.054,36.271,13.269,37.147,7.805,43.13z' fill='%23FFFFFF'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M65.256,92.504c-0.047-0.048-0.094-0.102-0.141-0.148c0.029,0.031,0.059,0.069,0.094,0.101L65.256,92.504z' fill='%23FFFFFF'/%3E%3Cpath d='M65.428,72.786c-5.416,5.926-5.529,14.639-0.313,19.569c-2.58-2.483-2.523-7.653,0.082-12.597 c0.336-0.445,1.307-1.49,2.727-1.014c0.143,0.047,0.235,0.061,0.292,0.053c0.32,0.069,0.651,0.11,0.99,0.096 c2.154-0.1,3.82-1.924,3.723-4.08c-0.044-0.966-0.445-1.822-1.063-2.479c5-3.275,10.739-3.652,13.093-1.504l0.088,0.007 C79.677,65.93,70.891,66.807,65.428,72.786z' fill='%23FFFFFF'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A")
+ center right no-repeat;
+}
+
+.smwrdflink {
+ float: right;
+ font-size: small;
+ padding: 2px 0 0 0;
+}
+
+.smwprops, .smwpropname {
+ word-break: break-word;
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+/**
+ * Tabbed factbox
+ */
+.smw-factbox #tab-facts-rendered:checked ~ #tab-content-facts-rendered,
+.smw-factbox #tab-facts-derived:checked ~ #tab-content-facts-derived {
+ display: block;
+}
+
+.smw-factbox section {
+ border: 1px solid #aaa;
+}
+
+.smw-factbox label.nav-label {
+ padding: 3px 25px 1px 25px;
+}
+
+.smw-factbox input.nav-tab:checked + label.nav-label {
+ border: 1px solid #aaa;
+ border-top: 2px solid orange;
+ border-bottom: 1px solid #fff;
+}
+
+.smw-factbox .smwfact {
+ border:none;
+ background-color: #fff;
+ padding: 0 0 0 0;
+ margin-top: 10px;
+}
+
+.smw-factbox .smwrdflink {
+ padding: 1px 5px 1px 5px;
+ margin-bottom: 8px;
+}
+
+.smw-factbox .smwfactboxhead {
+ padding: 1px 5px 1px 25px;
+ margin-bottom: 8px;
+ font-weight: normal;
+ width: 75%;
+}
+
+.smw-factbox .smwpropname, .smw-factbox .smwprops, .smw-factbox .smwspecname, .smw-factbox .smwspecs {
+ padding: 0.4em;
+}
+
+/**
+ * Responsive settings (#see smw.table.css)
+ */
+@media screen and (max-width: 800px) {
+ .smw-factbox .smwfactboxhead {
+ padding: 1px 5px 1px 5px;
+ }
+
+ .smw-factbox label {
+ padding: 3px 5px 1px 5px;
+ }
+
+ .smwfact .smwpropname, .smwfact .smwspecname {
+ width: auto;
+ font-weight: bold;
+ }
+}
+
+@media screen and (max-width: 600px) {
+ .smw-factbox .smwfactboxhead {
+ padding: 1px 5px 1px 5px;
+ width: 50%;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.js
new file mode 100644
index 00000000..773d557a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.js
@@ -0,0 +1,375 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.8
+ *
+ * @file
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw <jeroendedauw at gmail dot com>
+ * @author mwjames
+ */
+var smw = ( function ( $, undefined ) {
+ 'use strict';
+
+ /*global console:true message:true */
+
+ /**
+ *
+ * Declares methods and properties that are available through the smw namespace
+ *
+ * @since 1.8
+ * @class smw
+ * @alternateClassName semanticMediaWiki
+ * @singleton
+ */
+ return {
+
+ /* Public Members */
+
+ /**
+ * Merges two objects
+ *
+ * @since 1.8
+ *
+ * @return {object}
+ */
+ merge: function( obj1, obj2 ) {
+ // ECMAScript 2015 (ES6) Standard Method
+ // return Object.assign(obj1, obj2);
+
+ // ES5 and Earlier
+ return jQuery.extend( obj1, obj2 );
+ },
+
+ /**
+ * Outputs a debug log
+ *
+ * @since 1.8
+ *
+ * @return {string}
+ */
+ log: function( message ) {
+ if ( typeof mediaWiki === 'undefined' ) {
+ if ( typeof console !== 'undefined' ) {
+ console.log( 'SMW: ', message );
+ }
+ } else {
+ return mediaWiki.log.call( mediaWiki.log, 'SMW: ', message );
+ }
+ },
+
+ /**
+ * @since 3.0
+ */
+ load: function load( callback ) {
+ if ( document.readyState == 'complete' ) {
+ callback();
+ } else {
+ window.addEventListener( 'load', callback );
+ }
+ },
+
+ /**
+ * Outputs a message
+ *
+ * @since 1.8
+ *
+ * @return {string}
+ */
+ msg: function() {
+ if ( typeof mediaWiki === 'undefined' ) {
+ message = window.wgSMWMessages[arguments[0]];
+
+ for ( var i = arguments.length - 1; i > 0; i-- ) {
+ message = message.replace( '$' + i, arguments[i] );
+ }
+ return message;
+ } else {
+ return mediaWiki.msg.apply( mediaWiki.msg, arguments );
+ }
+ },
+
+ /**
+ * Returns current debug status
+ *
+ * @since 1.9
+ *
+ * @return {boolean}
+ */
+ debug: function() {
+ return mediaWiki.config.get( 'debug' );
+ },
+
+ /**
+ * Returns Semantic MediaWiki version
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ version: function() {
+ return mediaWiki.config.get( 'smw-config' ).version;
+ },
+
+ /**
+ * Declares methods to access utility functions
+ *
+ * @since 1.9
+ *
+ * @static
+ * @class smw.util
+ * @alias smw.Util
+ */
+ util: {
+
+ /**
+ * Strip some illegal chars: control chars, colon, less than, greater than,
+ * brackets, braces, pipe, whitespace and normal spaces. This still leaves some insanity
+ * intact, like unicode bidi chars, but it's a good start..
+ *
+ * Borrowed from mw.Title
+ *
+ * @ignore
+ * @param {string} s
+ * @return {string}
+ */
+ clean: function ( s ) {
+ if ( s !== undefined ) {
+ return s.trim().replace( /[\x00-\x1f\x23\x3c\x3e\x5b\x5d\x7b\x7c\x7d\x7f\s]+/g, '_' );
+ }
+ },
+
+ /**
+ * Capitalizes the first letter of a string
+ *
+ * @ignore
+ * @param {string} s
+ * @return {string}
+ */
+ ucFirst: function( s ) {
+ return s.charAt(0).toUpperCase() + s.slice(1);
+ },
+
+ /**
+ * Declares methods to access information about namespace settings
+
+ * Example to find localised name:
+ * smw.util.namespace.getName( 'property' );
+ * smw.util.namespace.getName( 'file' );
+ *
+ * @since 1.9
+ *
+ * @static
+ * @class smw.util.namespace
+ */
+ namespace: {
+
+ /**
+ * Returns list of available namespaces
+ *
+ * @since 1.9
+ *
+ * @return {Object}
+ */
+ getList: function() {
+ return smw.settings.get( 'namespace' );
+ },
+
+ /**
+ * Returns namespace Id
+ *
+ * @since 1.9
+ *
+ * @param {string} key
+ *
+ * @return {number}
+ */
+ getId: function( key ) {
+ if( typeof key === 'string' ) {
+ return this.getList()[ smw.util.ucFirst( smw.util.clean( key ) ) ];
+ }
+ return undefined;
+ },
+
+ /**
+ * Returns formatted localized name for a selected namespace
+ *
+ * @since 1.9
+ *
+ * @param {string} key
+ *
+ * @return {string}
+ */
+ getName: function( key ) {
+ if( typeof key === 'string' ) {
+ var id = this.getId( key );
+ return id && mediaWiki.config.get( 'wgFormattedNamespaces' )[id.toString()];
+ }
+ return undefined;
+ }
+ }
+ },
+
+ /**
+ * Declares methods to improve browser responsiveness by loading
+ * invoked methods asynchronously using the jQuery.eachAsync plug-in
+ *
+ * Example:
+ * var fn = function( options ) {};
+ * smw.async.load( $( this ), fn, {} );
+ *
+ * @since 1.9
+ *
+ * @singleton
+ * @class smw.async
+ */
+ async: {
+
+ /**
+ * Returns if eachAsync is available for asynchronous loading
+ *
+ * @return {boolean}
+ */
+ isEnabled: function() {
+ return $.isFunction( $.fn.eachAsync );
+ },
+
+ /**
+ * Negotiates and executes asynchronous loading
+ *
+ * @since 1.9
+ *
+ * @param {object} context
+ * @param {function} method
+ * @param {object|string} args
+ *
+ * @return {boolean}
+ * @throws {Error} Missing callback
+ */
+ load: function( context, method ) {
+ if ( typeof method !== 'function' ) {
+ throw new Error( 'Invoked parameter was not a function' );
+ }
+
+ // Filter arguments that are attached to the caller
+ var args = Array.prototype.slice.call( arguments, 2 );
+
+ if ( this.isEnabled() ) {
+ context.eachAsync( {
+ delay: 100,
+ bulk: 0,
+ loop: function() {
+ method.apply( $( this ), args );
+ }
+ } );
+ } else {
+ context.each( function() {
+ method.apply( $( this ), args );
+ } );
+ }
+ }
+ },
+
+ /**
+ * Declares methods to access information about available formats
+ *
+ * @since 1.9
+ *
+ * @class smw.formats
+ * @alias smw.Formats
+ */
+ formats: {
+
+ /**
+ * Returns list of available formats
+ *
+ * @since 1.9
+ * @extends smw.formats
+ *
+ * @return {Object}
+ */
+ getList: function() {
+ return mediaWiki.config.get( 'smw-config' ).formats;
+ },
+
+ /**
+ * Returns localized name for a select format
+ *
+ * @since 1.9
+ *
+ * @param {string} format
+ *
+ * @return {string}
+ */
+ getName: function( format ) {
+ if( typeof format === 'string' ){
+ return this.getList()[ smw.util.clean( format ).toLowerCase() ];
+ }
+ return undefined;
+ }
+ },
+
+ /**
+ * Declares methods to access information about invoked settings (see also
+ * SMWHooks::onResourceLoaderGetConfigVars)
+ *
+ * @since 1.9
+ *
+ * @class smw.settings
+ * @singleton
+ */
+ settings: {
+
+ /**
+ * Returns list of available settings
+ *
+ * @since 1.9
+ *
+ * @return {Object}
+ */
+ getList: function() {
+ return mediaWiki.config.get( 'smw-config' ).settings;
+ },
+
+ /**
+ * Returns a specific settings value (see SMW\Settings::get)
+ *
+ * @since 1.9
+ *
+ * @param {string} key to be selected
+ *
+ * @return {mixed}
+ */
+ get: function( key ) {
+ if( typeof key === 'string' ) {
+ return this.getList()[key];
+ }
+ return undefined;
+ }
+ }
+ };
+
+} )( jQuery );
+
+// Assign namespace
+window.smw = window.semanticMediaWiki = smw; \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.page.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.page.css
new file mode 100644
index 00000000..3fbdcc0f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.page.css
@@ -0,0 +1,420 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/* Property page */
+.smw-property-page-results .smwpropname {
+ width: 50%;
+}
+
+.smw-property-page-results .smwprops {
+ vertical-align: top;
+ width: 50%;
+}
+
+@media (min-width: 600px) {
+ .smw-property-page-results .smwpropname {
+ width: 33%;
+ }
+
+ .smw-property-page-results .smwprops {
+ width: 75%;
+ }
+}
+
+.smw-property-page-content-line {
+ border-bottom: 1px solid #ddd;
+ line-height: 0.6;
+}
+
+.smw-property-page-results {
+ margin-top: 0.9em;
+}
+
+.smw-property-page-results table {
+ border: none;
+}
+
+.smw-property-page-results .header-row {
+ /* background-color: #f5f5f5; */
+}
+
+.smw-property-page-results .smwpropname {
+ text-align: left;
+ vertical-align: top;
+ padding-right: 1em;
+ width: 33%;
+ border: 0px;
+}
+
+.smw-property-page-results .smwprops {
+ vertical-align: top;
+ text-align: left;
+ /* width: 75%; */
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ border: 0px;
+}
+
+.smw-property-page-results .value-row:hover {
+ background-color: #f5f5f5;
+}
+
+.smw-property-page-results .header-row > .header-title {
+ border-bottom: 1px solid #ddd;
+}
+
+.smw-property-page-results .value-row > .smw-table-cell {
+ padding-bottom: 0.2em;
+ padding-top: 0.2em;
+}
+
+.smw-property-page-results .header-row + .value-row .smw-table-cell {
+ padding-top: 0.3em;
+}
+
+.smw-page-navigation, .smw-page-content {
+ margin-top: 1.2em;
+}
+
+.smw-page-nav-note, .smw-page-nav-container {
+ margin-top: 0.5em;
+}
+
+.smw-page-nav-container::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
+.smw-page-nav-left {
+ float: left;
+ margin-right: 20px;
+}
+
+.smw-page-nav-right {
+ float: right;
+}
+
+.smw-ui-pagination .page-link {
+ display: inline-block;
+ padding: .30em .55em;
+ margin-left: -1px;
+ line-height: 1.25;
+ color: #007bff;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ padding-top: 0.35em;
+}
+
+.smw-ui-pagination .page-link:first-child {
+ margin-left: 0;
+ border-top-left-radius: .2em;
+ border-bottom-left-radius: .2em;
+ }
+
+.smw-ui-pagination .page-link:last-child {
+ border-top-right-radius: .2em;
+ border-bottom-right-radius: .2em;
+}
+
+.smw-ui-pagination .page-link:hover {
+ color: #0056b3;
+ text-decoration: none;
+ background-color: #e9ecef;
+ border-color: #dee2e6;
+}
+
+.smw-ui-pagination .page-link.link-disabled {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: auto;
+ background-color: #fff;
+ border-color: #dee2e6;
+}
+
+.smw-ui-pagination .page-link.link-active {
+ z-index: 1;
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.smw-property-page-results .header-title {
+ font-weight: bold;
+ font-size: 1.17em;
+ line-height: 1.6;
+ margin-top: 0.0em;
+ color: #333;
+ overflow: hidden;
+ padding: 2px 3px;
+ background-color: #fff;
+ border: none;
+ /* border-right: 1px solid #ddd;
+ padding: 5px 20px;
+ background-color: #f9f9f9; */
+}
+
+.smw-property-page-results tr.value-row:nth-child(even) {
+ /* background-color: #f5f5f5; */
+}
+
+.list-pager-value-filter {
+ text-align: right;
+}
+
+.list-pager-value-filter input {
+ font-weight: normal;
+ line-height: 1.25;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5em 1em;
+ border-radius: 0.25em;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25em 0.5em;
+ border-radius: 0.2em;
+ color: #292b2c;
+ border-color: #ddd;
+ display: inline-block;
+ vertical-align: baseline;
+ margin-left: 0.5em;
+
+ position: relative;
+ /* display: block; */
+ /* padding: 0.5em 0.75em; */
+ /* margin-left: -1px; */
+ line-height: 1.25;
+ /* color: #0275d8; */
+ background-color: #fff;
+ border: 1px solid #ddd;
+}
+
+.smw-ui-input-filter {
+ display: inline-block;
+ padding: 0 .25em;
+ /* margin-left: 10px; */
+ line-height: 1.25;
+ color: #007bff;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-top-left-radius: .2em;
+ border-bottom-left-radius: .2em;
+ border-top-right-radius: .2em;
+ border-bottom-right-radius: .2em;
+}
+
+.smw-ui-input-filter label {
+ margin-left: 0.25em;
+ margin-bottom: 0px;
+ font-weight: normal;
+}
+
+.smw-ui-input-filter input {
+ border :0px solid #ddd;
+ padding: .42em .55em .35em .55em;
+ border-top-left-radius: .0em;
+ border-bottom-left-radius: .0em;
+ outline: none;
+ border-left: 0.1em solid #ddd;
+ /* border-left: 0; */
+ margin-left: 0.45em;
+ font-weight: normal;
+ color: #333333;
+}
+
+.smw-property-page-results .value-row:nth-child(odd) {
+ /* background-color: #f9f9f9; */
+}
+
+.smw-property-page-results .smwpropname, .smw-property-page-results .smwprops {
+ /* border-top: 1px solid #ddd; */
+ padding: 20px 20px;
+}
+
+.smw-property-page-results .header-title {
+ border-bottom: 1px solid #ddd;
+}
+
+.clearfix::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
+.smw-property-page-results.legacy .header-title {
+ text-align: right;
+ border-bottom: 0px solid #ddd;
+}
+
+.smw-property-page-results.legacy .smwpropname {
+ text-align: right;
+ vertical-align: top;
+ padding-right: 1em;
+}
+
+.smw-property-page-results.legacy .smwpropname {
+ padding: 0px 20px;
+ padding-top: 0.3em;
+}
+
+.smw-property-page-results.legacy .smwprops {
+ padding: 0px 0px;
+ padding-top: 0.3em;
+}
+
+.smw-property-page-info {
+ float:right;
+ padding-top: 8px;
+}
+
+.smw-property-specification {
+ display: none;
+}
+
+.usage-count, .item-count {
+ display: inline-block;
+ padding: 2px 5px;
+ font-size: 12px;
+ font-weight: 600;
+ line-height: 1;
+ background-color: rgba(27,31,35,0.04);
+ border-radius: 4px;
+ color: #bbb;
+ margin-left: 10px;
+}
+
+#tab-smw-property-value:checked + label.nav-label .usage-count,
+#tab-smw-concept-list:checked + label.nav-label .usage-count,
+#tab-smw-property-subp:checked + label.nav-label .item-count,
+#tab-smw-property-errp:checked + label.nav-label .item-count,
+#tab-smw-property-redi:checked + label.nav-label .item-count,
+#tab-smw-property-spec:checked + label.nav-label .item-count {
+ background-color: rgba(27,31,35,0.08);
+ color: #444d56;
+}
+
+#tab-smw-property-value:checked + label.nav-label .usage-count.moderate,
+#tab-smw-concept-list:checked + label.nav-label .usage-count.moderate {
+ background-color: rgba(43,157,239,0.18);
+ color: #444d56;
+}
+
+#tab-smw-property-value:checked + label.nav-label .usage-count.high,
+#tab-smw-concept-list:checked + label.nav-label .usage-count.high {
+ background-color: rgba(186,0,0,0.16);
+ color: #444d56;
+}
+
+/**
+ * Tabs
+ */
+.smw-tabs.smw-property, .smw-tabs.smw-property p {
+ margin-top: 15px;
+}
+
+.smw-tabs label.nav-label.smw-tab-right {
+ float: right;
+ margin: -1px 0 0 0;
+}
+
+.smw-property #tab-smw-property-value:checked ~ #tab-content-smw-property-value,
+.smw-property #tab-smw-property-subp:checked ~ #tab-content-smw-property-subp,
+.smw-property #tab-smw-property-errp:checked ~ #tab-content-smw-property-errp,
+.smw-property #tab-smw-property-redi:checked ~ #tab-content-smw-property-redi,
+.smw-property #tab-smw-property-spec:checked ~ #tab-content-smw-property-spec,
+.smw-types #tab-smw-type-errors:checked ~ #tab-content-smw-type-errors,
+.smw-types #tab-smw-type-list:checked ~ #tab-content-smw-type-list {
+ display: block;
+}
+
+.smw-concept #tab-smw-concept-list:checked ~ #tab-content-smw-concept-list,
+.smw-concept #tab-smw-concept-errors:checked ~ #tab-content-smw-concept-errors {
+ display: block;
+}
+
+.smw-property input.nav-tab:checked + label.nav-label.smw-tab-warning,
+.smw-concept input.nav-tab:checked + label.nav-label.smw-tab-warning,
+.smw-types input.nav-tab:checked + label.nav-label.smw-tab-warning {
+ border-top: 2px solid rgb(204,0,0);
+}
+
+.smw-property input.nav-tab:checked + label.nav-label.smw-tab-spec,
+.smw-concept input.nav-tab:checked + label.nav-label.smw-tab-spec {
+ border-top: 2px solid orange;
+}
+
+/**
+ * Responsive settings (@see ext.smw.table)
+ */
+@media screen and (max-width: 800px) {
+
+ .smw-page-nav-right {
+ float: left;
+ }
+
+ .smw-tabs label.nav-label.smw-tab-right {
+ float: unset;
+ margin: 0 0 -1px;
+ }
+
+ .smw-page-nav-left {
+ margin-bottom: 8px;
+ }
+
+ .smw-property-page-results .header-title, .smw-property-page-results .header-row > .header-title {
+ border-bottom: none;
+ }
+
+ .smw-property-page-results .header-title:first-child, .smw-property-page-results .header-row > .header-title:first-child {
+ border-bottom: 1px solid #ddd;
+ }
+
+ .smw-property-page-results .smwpropname {
+ font-weight: bold;
+ width: 100%;
+ }
+
+ .smw-property-page-results .smwprops {
+ width: 100%;
+ }
+
+ .smw-property-page-results > .smw-table-row .smwpropname {
+ text-align: left;
+ vertical-align: top;
+ padding-right: 1em;
+ width: 100%;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.skin.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.skin.css
new file mode 100644
index 00000000..bf3d26cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.skin.css
@@ -0,0 +1,79 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * .skin-vector specific styles
+ */
+
+.skin-vector input#smw-property-input.autocomplete-suggestions {
+ height: 1.15em;
+ padding: 2px 2px;
+}
+
+.skin-chameleon .smw-page-indicator {
+ padding: 0.35rem 0.85rem;
+ line-height: 1.55;
+}
+
+/**
+ * .skin-chameleon specific styles
+ */
+.skin-chameleon input#smw-property-input.autocomplete-suggestions {
+ height: 30px;
+ padding: 2px 2px;
+}
+
+.skin-chameleon .autocomplete-suggestion {
+ padding: 2px 5px;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 0.9em;
+}
+
+.skin-chameleon .smwfact td {
+ padding: 5px;
+}
+
+.skin-chameleon .smw-modal-content {
+ line-height: 1.8;
+}
+
+/**
+ * .skin-foreground specific styles
+ */
+.skin-foreground input#smw-property-input.autocomplete-suggestions {
+ height: 32px;
+ padding: 2px 0px 0px 8px;
+}
+
+.skin-foreground .autocomplete-suggestion {
+ padding: 2px 5px;
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 0.9em;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.table.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.table.css
new file mode 100644
index 00000000..fd7068a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.table.css
@@ -0,0 +1,78 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+.smw-table {
+ display: table;
+ width: 100%;
+}
+
+.smw-table-row {
+ display: table-row;
+}
+
+.smw-table-header {
+ background-color: #eee;
+ display: table-header-group;
+}
+
+.smw-table-cell, .smw-table-head {
+ border: 1px solid #999;
+ display: table-cell;
+ padding: 3px 10px;
+}
+
+.smw-table-header {
+ background-color: #eee;
+ display: table-header-group;
+ font-weight: bold;
+}
+
+.smw-table-footer {
+ background-color: #eee;
+ display: table-footer-group;
+ font-weight: bold;
+}
+
+.smw-table-body {
+ display: table-row-group;
+}
+
+/**
+ * Responsive settings
+ */
+@media screen and (max-width: 800px) {
+ .smw-table-cell {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ }
+
+ .smw-table-row, .smw-table-header {
+ flex: 1 1 150px;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.tabs.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.tabs.css
new file mode 100644
index 00000000..2ef9e68e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/ext.smw.tabs.css
@@ -0,0 +1,85 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @see https://codepen.io/oknoblich/pen/tfjFl
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+.smw-tabs {
+ margin-top: 10px;
+ clear: both;
+}
+
+.smw-tabs section {
+ display: none;
+ padding: 0 0 0 0;
+ border-top: 1px solid #ddd;
+}
+
+.smw-tabs input.nav-tab {
+ display: none;
+}
+
+.smw-tabs label.nav-label {
+ display: inline-block;
+ margin: 0 0 -1px;
+ padding: 5px 25px;
+ font-weight: normal;
+ text-align: center;
+ color: #bbb;
+ border: 1px solid transparent;
+}
+
+.smw-tabs label.nav-label:before {
+ font-weight: normal;
+ margin-right: 10px;
+}
+
+.smw-tabs label.nav-label[for*='1']:before { content: ''; }
+.smw-tabs label.nav-label[for*='2']:before { content: ''; }
+.smw-tabs label.nav-label[for*='3']:before { content: ''; }
+.smw-tabs label.nav-label[for*='4']:before { content: ''; }
+
+.smw-tabs label.nav-label:hover {
+ color: #888;
+ cursor: pointer;
+}
+
+.smw-tabs input.nav-tab:checked + label.nav-label {
+ color: #24292e;
+ border: 1px solid #ddd;
+ border-top: 2px solid #337ab7;
+ border-bottom: 1px solid #fff;
+}
+
+/**
+ * Requires to be adjusted by each set that uses the rules
+ * to assign a tab to a content section.
+ */
+#tab1:checked ~ #content1,
+#tab2:checked ~ #content2,
+#tab3:checked ~ #content3,
+#tab4:checked ~ #content4 {
+ display: block;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.css
new file mode 100644
index 00000000..e945422f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.css
@@ -0,0 +1,291 @@
+/**
+ * Special rules for Semantic MediaWiki
+ *
+ * @note Default dataTable CSS contains !important attribute for the
+ * button, remove them in order to override with the following rules.
+ */
+
+.smw-datatable {
+ margin-bottom: 5px;
+ margin-top: 5px;
+}
+
+.smw-datatable.smw-placeholder {
+ padding:0px;
+}
+
+.smw-datatable table.dataTable {
+ margin-bottom: 10px;
+ width: 100% !important;
+}
+
+.smw-datatable table.dataTable.no-footer {
+ border-bottom: 1px solid #aaaaaa;
+}
+
+.smw-datatable table.dataTable tfoot td {
+ padding: 10px 18px 10px 10px;
+ border-bottom: 1px solid #aaaaaa;
+ border-top: 1px solid #aaaaaa;
+}
+
+.smw-datatable table.dataTable.compact tfoot th,
+.smw-datatable table.dataTable.compact tfoot td {
+ padding: 4px;
+ border-bottom: 1px solid #aaaaaa;
+ border-top: 1px solid #aaaaaa;
+}
+
+.smw-datatable table.dataTable thead th, .smw-datatable table.dataTable thead td,
+.smw-datatable table.data.smw-datatable table.compact thead th,
+.smw-datatable table.data.smw-datatable table.compact thead td,
+.smw-datatable table.data.smw-datatable table.no-footer {
+ border-bottom: 1px solid #aaaaaa;
+}
+
+.smw-datatable table.dataTable thead th,
+.smw-datatable table.dataTable thead td,
+.smw-datatable table.data.smw-datatable table.compact thead th,
+.smw-datatable table.data.smw-datatable table.compact thead td {
+ border-top: 1px solid #aaaaaa;
+}
+
+.smw-datatable .dataTables_wrapper {
+ position: relative;
+ clear: both;
+ zoom: 1;
+ margin-top: 10px;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_paginate {
+ float: right;
+ padding-top: 0.45em;
+ margin-top: 0;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.current,
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
+ display: inline-block;
+ background: #eee;
+ font-weight: normal;
+ line-height: 1.25;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ /* background-color: #e6e6e6; */
+ border-color: #ddd;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button {
+ display: inline-block;
+ font-weight: normal;
+ line-height: 1.25;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ /* background-color: #e6e6e6; */
+ border-color: #ddd;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
+ background-color: #e6e6e6;
+ background: #007bff;
+ color: #aaa;
+ display: inline-block;
+ font-weight: normal;
+ line-height: 1.25;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #fff;
+ border-color: #007bff;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
+.smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
+ cursor: default;
+ color: #666;
+ border: 1px solid transparent;
+ background: transparent;
+ box-shadow: none;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_filter input {
+/* display: inline-block; */
+ font-weight: normal;
+ line-height: 1.25;
+ /* text-align: center; */
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ /* background-color: #e6e6e6; */
+ border-color: #ddd;
+ display: inline-block;
+ vertical-align: baseline;
+}
+
+.smw-datatable .dataTables_wrapper select {
+/* display: inline-block; */
+ font-weight: normal;
+ line-height: 1.25;
+ /* text-align: center; */
+ white-space: nowrap;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ border: 1px solid transparent;
+ padding: 0.5rem 1rem;
+ /* font-size: 1rem; */
+ border-radius: 0.25rem;
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ padding: 0.25rem 0.5rem;
+ /* font-size: 0.875rem; */
+ border-radius: 0.2rem;
+ color: #292b2c;
+ /* background-color: #e6e6e6; */
+ border-color: #ddd;
+}
+
+.smw-datatable .dataTables_wrapper select:focus,
+.smw-datatable .dataTables_wrapper select:active,
+.smw-datatable .dataTables_wrapper .dataTables_filter input:focus,
+.smw-datatable .dataTables_wrapper .dataTables_filter input:active {
+ border:1px solid #007bff;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_info {
+ clear: both;
+ float: left;
+ padding-top: 0.45em;
+}
+
+.smw-datatable .dataTables_wrapper .dataTables_filter {
+ margin-bottom: 10px;
+}
+
+.smw-datatable-toolbar {
+ display: inline-block;
+ margin-left: 10px;
+ padding: 0px;
+}
+
+.smw-datatable-toolbar .smw-dropdown {
+ top: -1px;
+}
+
+.smw-datatable-toolbar .smw-dropdown button {
+ padding: 4px 25px 5px 15px;
+ border: 1px solid #ddd;
+ border-radius: 0.2rem;
+}
+
+@-moz-document url-prefix() {
+ .smw-datatable-toolbar .smw-dropdown button {
+ padding: 3px 25px 4px 15px;
+ }
+}
+
+.smw-datatable-toolbar .smw-dropdown .smw-dropdown-menu {
+ right: 0px;
+ left: initial;
+}
+
+.smw-datatable-toolbar .smw-dropdown .smw-dropdown-menu li {
+ min-width: 60px;
+ text-align: right;
+}
+
+.smw-datatable-toolbar .smw-dropdown .divider {
+ margin: 4px 0;
+}
+
+.smw-datatable-sep {
+ border-left: 1px solid #ddd;
+ margin-right: 0.4em;
+ padding: 0.2em;
+}
+
+.smw-datatable-text {
+ margin-right: 10px;
+}
+
+.smw-datatable-button {
+ position: relative;
+ padding: 5px 12px;
+ color: #586069;
+ border: 1px solid #e1e4e8;
+ border-radius: 3px;
+ font-size: 0.975em;
+ margin-right: 5px;
+}
+
+.smw-datatable-button:hover {
+ background-color: #f6f8fa;
+}
+
+.smw-datatable-button a {
+ text-decoration: none;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js
new file mode 100644
index 00000000..610ae56a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.js
@@ -0,0 +1,335 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/* global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ var dataTable = {
+
+ /**
+ * Adds the initial sort/order from the #ask request that is available as
+ * `data-column-sort` attribute with something like:
+ *
+ * {
+ * "list":["","Foo","Bar"]
+ * "sort":["Foo"],
+ * "order":["asc"]
+ * }
+ *
+ * on
+ *
+ * {{#ask: ...
+ * |?Foo
+ * |?Bar
+ * |sort=Foo
+ * |order=asc
+ * ...
+ * }}
+ *
+ * @since 3.0
+ *
+ * @private
+ * @static
+ *
+ * @param {Object} context
+ */
+ initColumnSort: function ( context ) {
+
+ var column = context.data( 'column-sort' );
+ var order = [];
+
+ // SMW allows descending and ascending but those are unknown to DataTables
+ var orderMap = {
+ 'descending' : 'desc',
+ 'ascending': 'asc',
+ 'asc': 'asc',
+ 'desc': 'desc'
+ };
+
+ // In case of a transposed table, don't try to match a column or its order
+ if ( column === undefined || !column.hasOwnProperty( 'sort' ) || column.sort.length === 0 || context.attr( 'data-transpose' ) ) {
+ return;
+ };
+
+ // https://datatables.net/reference/api/order()
+ // [1, 'asc'], [2, 'desc']
+ $.map( column.sort, function( val, i ) {
+ if ( val === '' ) {
+ return;
+ };
+ order.push( [
+ $.inArray( val, column.list ), // Find matchable index from the list
+ column.order[i] === undefined ? 'asc' : orderMap[column.order[i]]
+ ] );
+ } );
+
+ if ( order.length > 0 ) {
+ context.data( 'order', order );
+ };
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @private
+ * @static
+ *
+ * @param {Object} context
+ */
+ addHeader: function ( context ) {
+
+ // Copy the thead to a position the DataTable plug-in can transform
+ // and display
+ if ( context.find( 'thead' ).length === 0 ) {
+ var head = context.find( 'tbody tr' );
+ context.prepend( '<thead>' + head.html() + '</thead>' );
+ head.eq(0).remove();
+
+ // In case of a transposed, turn any td into a th
+ context.find( 'thead td' ).wrapInner( '<th />' ).contents().unwrap();
+ }
+
+ // Ensure that any link in the header stops the propagation of the
+ // click sorting event
+ context.find( 'thead tr a' ).on( 'click.sorting', function ( event ) {
+ event.stopPropagation();
+ } );
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @private
+ * @static
+ *
+ * @param {Object} context
+ */
+ addFooter: function ( context ) {
+
+ // As a transposed table, move the footer column to the bottom
+ // and remove any footer-cell from the table matrix to
+ // ensure a proper formatted table
+ if ( context.data( 'transpose' ) === 1 && context.find( 'tbody .sortbottom' ).length === 1 ) {
+ var footer = context.find( 'tbody .sortbottom' );
+ context.append( '<tfoot><tr><td colspan=' + footer.index() + '>' + footer.html() + '</td></tr></tfoot>' );
+ footer.eq(0).remove();
+
+ // Remove remaining footer cells to avoid an uneven table
+ context.find( 'tbody .footer-cell' ).each( function() {
+ $( this ).remove();
+ } );
+ };
+
+ // Copy the tbody to a position the DataTable plug-in can transform
+ // and display
+ if ( context.find( 'tbody .smwfooter' ).length == 1 ) {
+ var footer = context.find( 'tbody .smwfooter' );
+ context.append( '<tfoot>' + footer.html() + '</tfoot>' );
+ footer.eq(0).remove();
+ }
+
+ context.find( '.sortbottom' ).addClass( 'plainlinks' );
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @param {Object} context
+ */
+ addToolbarExportLinks: function ( context ) {
+
+ var toolbar = context.parent().find( '.smw-datatable-toolbar' ),
+ query = context.data( 'query' ),
+ exportFormats = {
+ 'JSON': {
+ 'format': 'json',
+ 'searchlabel': 'JSON',
+ 'type': 'simple',
+ 'prettyprint': true,
+ 'unescape': true
+ },
+ 'CSV': {
+ 'format': 'csv',
+ 'searchlabel': 'CSV'
+ },
+ 'RSS': {
+ 'format': 'rss',
+ 'searchlabel': 'RSS'
+ },
+ 'RDF': {
+ 'format': 'rdf',
+ 'searchlabel': 'RDF'
+ }
+ }
+
+ if ( !query instanceof Object || query === undefined ) {
+ return;
+ }
+
+ var items = '';
+
+ Object.keys( exportFormats ).forEach( function( key ) {
+
+ // https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript
+ var conf = exportFormats[key],
+ parameters = $.extend( {}, query.parameters );
+
+ // Modify the default query with that of the configuration
+ Object.keys( conf ).forEach(function( k ) {
+ parameters[k] = conf[k];
+ } );
+
+ var q = new smw.query(
+ query.printouts,
+ parameters,
+ query.conditions
+ );
+
+ q.setLinkAttributes( {
+ 'title': key
+ } );
+
+ if ( key === 'RDF' ) {
+ items += '<li class="divider"></li>';
+ };
+
+ items += '<li class="action">' + q.getLink( key ) + '</li>';
+ } );
+
+ toolbar.append(
+ '<span class="smw-dropdown">' +
+ '<button>' + mw.msg( 'smw-format-datatable-toolbar-export' ) + '</button>' +
+ '<label><input type="checkbox">' +
+ '<ul class="smw-dropdown-menu">' +
+ items +
+ '</ul></label></span>'
+ );
+
+ toolbar.find( '.action a' ).on( 'click', function( e ) {
+ toolbar.find( '.smw-dropdown input' ).prop( 'checked', false );
+ } );
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @param {Object} context
+ */
+ attach: function ( context ) {
+
+ var self = this;
+ context.show();
+
+ // Remove any class that may interfere due to some external JS or CSS
+ context.removeClass( 'jquery-tablesorter' );
+ context.removeClass( 'sortable' );
+ context.removeClass( 'is-disabled' );
+ context.removeClass( 'wikitable' );
+
+ // DataTables default display class
+ context.addClass( 'display' );
+
+ mw.loader.using( 'onoi.dataTables' ).done( function () {
+
+ self.initColumnSort( context );
+
+ // MediaWiki table output is missing some standard formatting hence
+ // add a footer and header
+ self.addFooter( context );
+ self.addHeader( context );
+
+ // https://datatables.net/manual/tech-notes/3
+ // Ensure the object initialization only happens once
+ if ( $.fn.dataTable.isDataTable( context ) ) {
+ return;
+ }
+
+ var table = context.DataTable( {
+ dom: 'l<"smw-datatable-toolbar float-right">frtip',
+ searchHighlight: true,
+ language: {
+ "sProcessing": mw.msg( 'smw-format-datatable-processing' ),
+ "sLengthMenu": mw.msg( 'smw-format-datatable-lengthmenu' ),
+ "sZeroRecords": mw.msg( 'smw-format-datatable-zerorecords' ),
+ "sEmptyTable": mw.msg( 'smw-format-datatable-emptytable' ),
+ "sInfo": mw.msg( 'smw-format-datatable-info' ),
+ "sInfoEmpty": mw.msg( 'smw-format-datatable-infoempty' ),
+ "sInfoFiltered": mw.msg( 'smw-format-datatable-infofiltered' ),
+ "sSearch": mw.msg( 'smw-format-datatable-search' ),
+ "sInfoThousands": mw.msg( 'smw-format-datatable-infothousands' ),
+ "sLoadingRecords": mw.msg( 'smw-format-datatable-loadingrecords' ),
+ "oPaginate": {
+ "sFirst": mw.msg( 'smw-format-datatable-first' ),
+ "sLast": mw.msg( 'smw-format-datatable-last' ),
+ "sNext": mw.msg( 'smw-format-datatable-next' ),
+ "sPrevious": mw.msg( 'smw-format-datatable-previous' )
+ },
+ "oAria": {
+ "sSortAscending": mw.msg( 'smw-format-datatable-sortascending' ),
+ "sSortDescending": mw.msg( 'smw-format-datatable-sortdescending' )
+ }
+ }
+ } );
+
+ // Remove accented characters from the search input
+ context.parent().find( 'input' ).on( "keyup", function () {
+ table.search(
+ $.fn.DataTable.ext.type.search.string( $.trim( this.value ) )
+ ).draw();
+ } );
+
+ self.addToolbarExportLinks( context );
+
+ // Fire to instantiate the tooltip after the DT has been generated
+ mw.hook( 'smw.tooltip' ).fire( context );
+ } );
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @private
+ * @static
+ *
+ * @param {Object} context
+ */
+ init: function ( context ) {
+
+ context.removeClass( 'is-disabled' );
+ context.removeClass( 'smw-flex-center' );
+
+ context.css( "background-color", "transparent" );
+ context.css( "height", "" );
+ context.find( '.smw-overlay-spinner' ).hide();
+
+ context.find( '.datatable' ).css( "opacity", "1" );
+ context.removeClass( 'smw-extra-margin' );
+
+ context.find( '.smw-datatable' ).removeClass( 'smw-extra-margin' );
+ this.attach( context.find( '.datatable' ) );
+ }
+ };
+
+ $( document ).ready( function() {
+
+ $( '.smw-datatable' ).each( function() {
+ dataTable.init( $( this ) );
+ } );
+
+ // Listen to the smw.deferred.query event
+ mw.hook( 'smw.deferred.query' ).add( function( context ) {
+ if ( context.find( '.smw-datatable .datatable' ).length ) {
+ dataTable.init( context.find( '.smw-datatable' ) );
+ } else {
+ dataTable.init( context );
+ }
+ } );
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.skin.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.skin.css
new file mode 100644
index 00000000..63df7a83
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/printer/ext.smw.tableprinter.skin.css
@@ -0,0 +1,41 @@
+/**
+ * .skin-chameleon specific styles
+ */
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button:hover,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.current,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
+ padding: 0.55rem 0.85rem;
+ line-height: 1.0;
+ font-size: 14px;
+}
+
+.skin-chameleon .smw-datatable .dataTables_wrapper select,
+.skin-chameleon .smw-datatable .dataTables_wrapper select:focus,
+.skin-chameleon .smw-datatable .dataTables_wrapper select:active {
+ /*padding: 0.15rem 0.5rem;*/
+}
+
+.skin-chameleon .smw-datatable .dataTables_wrapper .dataTables_filter input {
+ line-height: 1.35;
+}
+
+.skin-chameleon .dataTables_wrapper label {
+ font-weight: normal;
+}
+
+.skin-chameleon table th {
+ text-align: center;
+}
+
+.skin-chameleon .smw-datatable-toolbar .smw-dropdown button {
+ padding: 2px 25px 2px 15px;
+ border-radius: 0.2rem;
+}
+
+.skin-chameleon .smw-datatable .smw-datatable-sep {
+ padding: 0 0.2em 0.2em 0;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/query/ext.smw.query.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/query/ext.smw.query.js
new file mode 100644
index 00000000..57fec534
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/query/ext.smw.query.js
@@ -0,0 +1,198 @@
+/**
+ * This file is part of the Semantic MediaWiki JavaScript Query module
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @file
+ * @ignore
+ *
+ * @since 1.9
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Private object and methods
+ * @ignore
+ */
+ var html = mw.html;
+
+ /**
+ * Query constructor
+ *
+ * @since 1.9
+ *
+ * @param {object} printouts
+ * @param {object} parameters
+ * @param {object|string} conditions
+ *
+ * @return {this}
+ */
+ var query = function ( printouts, parameters, conditions ) {
+
+ this.printouts = [];
+ this.conditions = [];
+
+ this.parameters = {};
+ this.linkAttributes = {};
+
+ if ( printouts !== '' && printouts !== undefined ) {
+ this.printouts = printouts;
+ };
+
+ if ( parameters !== '' && parameters !== undefined ) {
+ this.parameters = parameters;
+ };
+
+ if ( conditions !== '' && conditions !== undefined ) {
+ this.conditions = conditions;
+ };
+
+ return this;
+ };
+
+ /**
+ * Constructor to create an object to interact with the Query
+ *
+ * @since 1.9
+ *
+ * @class
+ * @alias smw.Query
+ * @constructor
+ */
+ smw.Query = function( printouts, parameters, conditions ) {
+
+ // You need to have some conditions otherwise jump the right light
+ // because a query one can survive without printouts or parameters
+ if ( conditions !== '' || $.type( this.conditions ) === 'array' ) {
+ this.constructor( printouts, parameters, conditions );
+ } else {
+ throw new Error( 'smw.dataItem.query: conditions are empty' );
+ }
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ constructor: query,
+
+ /**
+ * Returns query limit parameter (see SMW\Query::getLimit())
+ *
+ * @since 1.9
+ *
+ * @return {number}
+ */
+ getLimit: function() {
+ return this.parameters.limit;
+ },
+
+ /**
+ * @since 3.0
+ *
+ * @param {Object} linkAttributes
+ */
+ setLinkAttributes: function( linkAttributes ) {
+ this.linkAttributes = linkAttributes;
+ },
+
+ /**
+ * Returns query link (see SMW\QueryResult::getLink())
+ *
+ * Caption text is set either by using parameters.searchlabel or by
+ * .getLink( 'myCaption' )
+ *
+ * @since 1.9
+ *
+ * @param {string}
+ * @return {string}
+ */
+ getLink: function( caption ) {
+ var c = caption ? caption : this.parameters.searchlabel !== undefined ? this.parameters.searchlabel : '...' ;
+
+ var args = {
+ title: 'Special:Ask',
+ q: $.type( this.conditions ) === 'string' ? this.conditions : this.conditions.join( '' ),
+ po: this.printouts.join( '\n' ),
+ p: this.parameters
+ };
+
+ var attr = {
+ 'class': 'query-link',
+ 'href' : mw.config.get( 'wgScript' ) + '?' + $.param( args )
+ } ;
+
+ $.extend( attr, this.linkAttributes );
+
+ return html.element( 'a', attr , c );
+ },
+
+ /**
+ * Returns query string (see SMW\Query::getQueryString())
+ *
+ * @since 1.9
+ *
+ * @return {string}
+ */
+ toString: function() {
+
+ var printouts = '';
+ if ( $.type( this.printouts ) === 'array' ){
+ $.each( this.printouts, function( key, value ) {
+ printouts += '|' + value;
+ } );
+ } else {
+ // @see ext.smw.query.test why we are failing here and not earlier
+ throw new Error( 'smw.Api.query: printouts is not an array, it is a + ' + $.type( this.printouts ) );
+ }
+
+ var parameters = '';
+ if ( $.type( this.parameters ) === 'object' ){
+ $.each( this.parameters, function( key, value ) {
+ parameters += '|' + key + '=' + value;
+ } );
+ } else {
+ // @see ext.smw.query.test why we are failing here and not earlier
+ throw new Error( 'smw.Api.query: parameters is not an object, it is a + ' + $.type( this.parameters ) );
+ }
+
+ var conditions = '';
+ if ( $.type( this.conditions ) === 'array' || $.type( this.conditions ) === 'object' ){
+ $.each( this.conditions, function( key, value ) {
+ conditions += value;
+ } );
+ } else if ( $.type( this.conditions ) === 'string' ) {
+ conditions += this.conditions;
+ }
+
+ return conditions + printouts + parameters;
+ }
+ };
+
+ // Alias
+ fn.getQueryString = fn.toString;
+
+ // Assign methods
+ smw.Query.prototype = fn;
+ smw.query = smw.Query;
+
+} )( jQuery, mediaWiki, semanticMediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.schema.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.schema.css
new file mode 100644
index 00000000..21571b95
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.schema.css
@@ -0,0 +1,134 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Schema content related CSS
+ *
+ * @file
+ * @ingroup semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+.content-highlight {
+ font-size: 1.2em;
+ margin: 0;
+ border-top: 1px solid #eaecf0;
+ border-left: 1px solid #eaecf0;
+ border-right: 1px solid #eaecf0;
+ border-bottom: 1px solid #eaecf0;
+}
+
+.content-no-highlight-o {
+ margin: 0;
+ padding: 5px;
+ background-color: #f8f9fa;
+ border-top: 1px solid #eaecf0;
+ border-left: 1px solid #eaecf0;
+ border-right: 1px solid #eaecf0;
+ border-bottom: 1px solid #eaecf0;
+}
+
+.unknown-type .content-highlight {
+ border-top: 1px solid #eaecf0;
+}
+
+.schema-tags {
+ border-bottom: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+ border-right: 1px solid #ddd;
+ background-color: #f8f9fa;
+ /* background-color: #f8f9fa;*/
+ padding: 5px;
+ /*margin-top: 1em;*/
+ clear: both;
+}
+
+.schema-tags:first-child {
+ border: 1px solid #ddd;
+ background-color: #f8f9fa;
+ padding: 5px;
+ margin-top: 1em;
+ clear: both;
+}
+.schema-tags ul {
+ display: inline;
+ margin: 0 !important;
+ padding: 0;
+ list-style: none;
+ list-style-type: none;
+ list-style-image: none;
+ }
+
+.schema-tags li:first-child {
+ padding-left: 0.25em;
+ border-left: 0;
+}
+
+.schema-tags li {
+ display: inline-block;
+ line-height: 1.25em;
+ border-left: 1px solid #a2a9b1;
+ margin: 0.125em 0;
+ padding: 0 0.5em;
+ zoom: 1;
+}
+
+.smw-schema-description {
+ color:#888;
+}
+
+.schema-error {
+ color: #c00;
+}
+
+.smw-schema-validation-error {
+ padding-left:0px;
+ padding-top:5px;
+}
+
+/**
+ * Tabs
+ */
+.smw-schema #tab-schema-error:checked ~ #tab-content-schema-error,
+.smw-schema #tab-schema-summary:checked ~ #tab-content-schema-summary {
+ display: block;
+}
+
+.smw-schema section {
+ border-top: 1px solid #ddd;
+ border-left: 0px solid #ddd;
+ border-right: 0px solid #ddd;
+}
+
+.smw-schema label.nav-label {
+ padding: 8px 20px;
+}
+
+.smw-schema input.nav-tab:checked + label.nav-label {
+ border: 1px solid #ddd;
+ border-top: 2px solid #337ab7;
+ border-bottom: 1px solid #fff;
+}
+
+.smw-schema input.nav-tab:checked + label.nav-label.error-label {
+ border-top: 2px solid #cc0000;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.selectmenu.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.selectmenu.css
new file mode 100644
index 00000000..e8d2d7b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.selectmenu.css
@@ -0,0 +1,119 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://www.semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+.smw-selectmenu-button {
+ display: inline-block;
+ padding: 0;
+ font-size: inherit;
+ color: #0366d6;
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-color: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ position: relative;
+ display: inline-block;
+ padding-top: 13px;
+ padding-bottom: 13px;
+ font-weight: 400;
+ padding-right: 5px;
+ padding-left: 2px;
+ color: #586069;
+ position: relative;
+ display: inline-block;
+ padding-top: 5px;
+ padding-bottom: 2px;
+ font-weight: 400;
+ box-shadow:unset;
+}
+
+.smw-selectmenu-button:after {
+ position: unset !important;
+ display: inline-block !important;
+ width: 0 !important;
+ height: 0 !important;
+ vertical-align: -2px;
+ content: "";
+ border: 4px solid;
+ border-right-color: transparent;
+ border-bottom-color: transparent;
+ border-left-color: transparent;
+ margin-left:5px;
+}
+
+.smw-selectmenu-button:focus {outline:0;}
+
+.smw-selectmenu-label {
+ padding-right: 5px;
+ padding-left: 5px;
+ color: #4a4a4a;
+ /* position: relative; */
+ /* display: inline-block; */
+ padding-top: 2px;
+ /*padding-bottom: 2px;*/
+ font-weight: 600;
+ box-shadow: unset;
+}
+
+.smw-selectmenu-label:first-child {
+ padding-left: 0px;
+}
+
+.sm_container {
+ border-radius: 3px;
+}
+
+.sm_result_area, .sm_results {
+ border-radius: 0px 0px 3px 3px;
+}
+
+ul.sm_results > li.sm_over:last-child {
+ border-radius: 0px 0px 3px 3px;
+}
+
+/**
+ * Responsive settings
+ */
+@media screen and (max-width: 800px) {
+ .smw-table-cell {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ }
+
+ .smw-table-row, .smw-table-header {
+ flex: 1 1 150px;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.ui.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.ui.js
new file mode 100644
index 00000000..eaed7fa2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/smw.ui.js
@@ -0,0 +1,59 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * @since 3.0
+ * @class
+ */
+ smw.ui = smw.ui || {};
+
+ /**
+ * Class constructor
+ *
+ * @since 3.0
+ *
+ * @class
+ * @constructor
+ */
+ smw.ui = function() {
+ 'use strict';
+ };
+
+ /* Public methods */
+
+ smw.ui.prototype = {
+
+ /**
+ * @since 3.0
+ */
+ selectMenu: function( context, opts ) {
+
+ var that = context;
+ var val = that.prop( 'value' );
+ var data = that.data( 'list' );
+
+ that.selectMenu(
+ smw.merge(
+ {
+ showField : 'desc',
+ keyField : 'id',
+ arrow : true,
+ selectToCloseList: true,
+ initSelected : val,
+ search: false,
+ title: that.prop( 'title' ),
+ data : data,
+ },
+ opts
+ )
+ );
+ }
+ }
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.css
new file mode 100644
index 00000000..dbea75b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.css
@@ -0,0 +1,327 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+.smw-search-results-prepend {
+ clear: both;
+ padding: 0.65em 0.75em 0.60em 0.75em;
+ background-color: #ddeefc;
+ margin: -1px 0 0;
+ border: 1px solid rgba( 94, 157, 200, 0.25 );
+ border-radius: 2px 2px 2px 2px;
+ margin-bottom: 1em;
+}
+
+/* https://stackoverflow.com/questions/2812770/add-centered-text-to-the-middle-of-a-hr-like-line */
+.smw-text-strike {
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ padding: 15px 0 15px 0;
+ clear: both;
+}
+
+.smw-text-strike > span {
+ position: relative;
+ display: inline-block;
+ font-size: 95%;
+ margin-left: 20px;
+}
+
+.smw-text-strike > span:before,
+.smw-text-strike > span:after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ width: 9999px;
+ height: 1px;
+ background: #aaa;
+}
+
+.smw-text-strike > span:before {
+ right: 100%;
+ margin-right: 10px;
+}
+
+.smw-text-strike > span:after {
+ left: 100%;
+ margin-left: 10px;
+}
+
+.smw-fields {
+ display: flex;
+ /* justify-content: space-between; */
+ /* padding-left: 10px; */
+ /* margin: 0 -10px; */
+ margin: 0 -10px;
+ /* flex-direction: column; */
+ margin-right: 6px;
+ flex-direction: row;
+ flex-wrap: wrap;
+ flex-flow: row wrap;
+ align-content: flex-end;
+}
+
+.smw-fields .smw-input-field {
+ margin: 0 10px;
+ margin-top: 4px;
+ flex: 1 0 40%; /* make max 2 columns*/
+}
+
+.smw-input-field label {
+ margin-left: 8px;
+}
+
+.smw-input {
+ padding:8px;
+ display:block;
+ border:none;
+ border-bottom:1px solid #eaecf0;
+ width:100%
+}
+
+.smw-input-group {
+ display: flex;
+ flex: 1 0 100%;
+ flex-direction: row;
+ flex-wrap: wrap;
+ flex-flow: row wrap;
+ align-content: flex-end;
+}
+
+
+.smw-select-field {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg fill='%23888888' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position-x: calc(100% - 6px);
+ background-position-y: 5px;
+
+ padding:7px 25px 7px 10px;
+ margin-top: 4px;
+ margin-left: 4px;
+ border:none;
+ border-bottom:1px solid #eaecf0;
+ flex: 1 0 35px;
+ display: inline-block;
+ padding: 7px;
+ /* margin: 0 10px; */
+ margin-top: 4px;
+ /* width: 100%; */
+ margin-left: 10px;
+ margin-right: -5px;
+}
+
+.smw-button-field {
+ vertical-align: middle;
+ text-align: center;
+ margin-top: 10px;
+ margin-left: 10px;
+ color: #888888;
+ /* flex: 1 0 35px; */
+ display: inline-block;
+}
+
+.smw-form-description {
+ margin-left: 14px;
+ margin-bottom: 5px;
+ width: 100%;
+}
+
+.smw-form-link-form {
+ margin-left: 0.25em;
+}
+
+.smw-form-link-query {
+ display: inline-block;
+ line-height: 1.25em;
+ border-right: 1px solid #a2a9b1;
+ margin: 0.125em 0;
+ padding: 0 0.5em;
+ zoom: 1;
+}
+
+.smw-input-tooltip {
+ width: calc(100% - 40px);
+ display: inline-block;
+}
+
+.smw-search-options {
+ display: flex;
+ width: 100%;
+}
+
+.smw-search-options div:last-child {
+ margin-left: auto;
+}
+
+.smw-search-options div:first-child {
+ margin-left: unset;
+}
+
+.smw-search-sort {
+ min-width: 120px;
+}
+
+#smw-search-togglensview {
+ float: right;
+}
+
+.ns-list-off {
+ display:none;
+}
+
+.ns-list-on {
+ display:block;
+}
+
+#smw-togglensview {
+ margin-left: 1em;
+ text-align: center;
+ text-decoration: none;
+ display: inline-block;
+ opacity: 0.6;
+}
+
+#smw-togglensview:hover {
+ opacity: 1
+}
+
+#smw-togglensview:focus{
+ outline: none;
+}
+
+#smw-searchoptions {
+ margin: 0;
+ padding: 0.5em 0.75em 0.75em 0.75em;
+ background-color: #f8f9fa;
+ border: 1px solid #c8ccd1;
+ border-top-width: 0;
+}
+
+#smw-searchoptions .divider {
+ clear: both;
+ border-bottom: 1px solid #eaecf0;
+ padding-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+#smw-search-synatx-list, #smw-search-sort-list {
+ /* font-size: smaller; */
+}
+
+#smw-search-synatx-list code {
+ background-color: #5e9dc82e;
+ border: 1px solid #5e9dc84a;
+}
+
+#mw-searchoptions h4 {
+ margin-top: 2px !important;
+}
+
+#smw-search-togglensview, #mw-search-togglebox {
+ margin-top: 2px !important;
+}
+
+.searchresult {
+ text-align: justify;
+}
+
+.search-types .current a:after {
+ color: #586069;
+ position: unset !important;
+ display: inline-block !important;
+ width: 0 !important;
+ height: 0 !important;
+ vertical-align: -2px;
+ content: "";
+ border: 4px solid;
+ border-right-color: transparent;
+ border-bottom-color: transparent;
+ border-left-color: transparent;
+ margin-left: 5px;
+}
+
+.smw-search-options .smw-modal-link {
+ position: absolute;
+ right: 10px;
+}
+
+.smw-search-options .smw-icon-info {
+ padding: 0 0 8px 22px !important;
+}
+
+/**
+ * Responsive settings (#see smw.table.css)
+ */
+@media screen and (max-width: 800px) {
+ .smw-fields .smw-input-field, .smw-select-field {
+ flex: 1 1 auto;
+ }
+
+ #smw-search-sort label {
+ display: none;
+ }
+
+ .smw-input-group {
+ /* display:block;*/
+ }
+}
+
+/**
+ * .skin-chameleon specific styles
+ */
+.skin-chameleon .smw-search-help-proximity {
+ margin-top: 10px;
+}
+
+.skin-chameleon .smw-select-field {
+ margin-right: -3px;
+}
+
+.skin-chameleon .smw-fields .smw-input-field {
+ margin: 5px -3px 0 10px;
+}
+
+.skin-chameleon #mw-searchoptions h4 {
+ margin-top: 6px !important;
+ margin-bottom: 2px !important;
+}
+
+.skin-chameleon #smw-search-togglensview, .skin-chameleon #mw-search-togglebox {
+ margin-top: 0px !important;
+}
+
+.skin-chameleon .smw-search-options .smw-modal-link {
+ position: absolute;
+ right: 35px;
+}
+
+.skin-chameleon .smw-search-options .smw-icon-info {
+ padding: 0 0 15px 24px !important;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.form.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.form.js
new file mode 100644
index 00000000..9a525d7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.form.js
@@ -0,0 +1,267 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Support extended form in Special:Search
+ *
+ * @since 3.0
+ */
+ var form = function() {
+
+ var namespaces = [];
+ var ui = new smw.ui();
+
+ // Empty value, inject a hidden non visible char to allow to trigger
+ // the search without an input text
+ if ( $( '#searchText > input' ).val() === '' ) {
+ $('#search').append('<input type="hidden" name="search" value=" " id="search-hidden" />' );
+ };
+
+ // If a users enters some data, remove the hidden field when it is not
+ // empty.
+ $( document ).on( "change keyup", "#searchText > input", function() {
+ if ( $( '#searchText > input' ).val() !== '' ) {
+ $( "#search-hidden" ).remove();
+ } else {
+ $('#search').append('<input type="hidden" name="search" value=" " id="search-hidden" />' );
+ }
+ } );
+
+ $( document ).on( "click", "#smw-search-sort", function(){
+
+ var that = $( this );
+ var opts = {
+ eSelect: function( data ) {
+ if( data && data.length ) {
+ that.text( data[0].name );
+ that.prop( 'value', data[0].id );
+ that.parent().find( 'input[name^=sort]' ).prop( 'value', data[0].id );
+ that.trigger( 'change' );
+ };
+ }
+ }
+
+ ui.selectMenu( that, opts );
+ } );
+
+ $( document ).on( "click", "#smw-search-forms", function(){
+
+ var that = $( this );
+ var opts = {
+ label: 'Form',
+ search: true,
+ orderBy: 'name',
+ eSelect: function( data ) {
+ if( data && data.length ) {
+ that.text( data[0].name );
+ that.prop( 'value', data[0].id );
+ that.parent().find( 'input[name^=smw-form]' ).prop( 'value', data[0].id );
+ that.trigger( 'change' );
+ };
+ }
+ }
+
+ ui.selectMenu( that, opts );
+ } );
+
+ $( document ).ready( function() {
+
+ /**
+ * Open form ...
+ */
+ $( '#smw-form-open > .smw-input-group' ).each( function() {
+ var context = $( this );
+
+ if ( $( "#smw-search-forms select" ).val() === 'open' ) {
+ context.find( '.smw-input-field' ).show();
+ context.find( '.smw-select-field' ).show();
+ context.find( '.smw-button-field' ).show();
+ };
+
+ if ( context.find( '.smw-property-input' ).val() === '' ) {
+ context.find( '.smw-propertyvalue-input' ).addClass( 'is-disabled' );
+ } else {
+ context.find( '.smw-propertyvalue-input' ).removeClass( 'is-disabled' );
+ };
+
+ if ( context.find( '.smw-propertyvalue-input' ).val() === '' ) {
+ context.find( '.smw-propertyvalue-input' ).addClass( 'is-disabled' );
+ }
+ } )
+
+ /**
+ * Act on changes to the form select
+ */
+ $( this ).on( "change", ".smw-select-field", function( event ) {
+ var context = $( this ).closest( '.smw-input-group' );
+ var length = $( '#smw-form-open .smw-input-group' ) ? $( '#smw-form-open .smw-input-group' ).length : 0;
+
+ // Property input empty, make the value field appear to be disabled
+ if ( context.find( '.smw-property-input' ).val() === '' ) {
+ context.find( '.smw-propertyvalue-input' ).val( '' );
+ context.find( '.smw-propertyvalue-input' ).addClass( 'is-disabled' );
+ }
+
+ // Act on the `del` request but only for as long as one group remains
+ if ( length > 1 && context.find( '.smw-select-field' ).val() === 'del' ) {
+ context.remove();
+ }
+
+ // Disable `del` when there are not enough groups available
+ if ( length <= 2 ) {
+ $( "#smw-form-open > .smw-input-group > .smw-select-field option[value='del']" ).attr( "disabled", "disabled" );
+ }
+ } );
+
+ /**
+ * Listing to the property select complete event on a field that uses
+ * the autocomplete.
+ */
+ $( "#smw-form-open" ).on( "smw.autocomplete.property.select.complete", function( event, opts ) {
+
+ var context = $( this );
+ var input = opts.context.closest( '.smw-input-group' ).find( '.smw-propertyvalue-input' );
+
+ // A property was "really" selected! enable the value field
+ if ( opts.suggestion && opts.suggestion.value !== '' ) {
+ input.removeClass( 'is-disabled' );
+ input.data( 'property', opts.suggestion.value );
+
+ // Trigger event to initialize the instance
+ context.trigger( 'smw.autocomplete.propertyvalue', { context: opts.context.closest( '.smw-input-group' ) } );
+ }
+ } );
+
+ /**
+ * Listing to the last property value complete event on a field that
+ * uses the autocomplete and decide whether to clone and create an
+ * empty group.
+ */
+ $( "#smw-form-open" ).on( "smw.autocomplete.propertyvalue.select.complete", function( event, opts ) {
+ var context = opts.context.closest( '.smw-input-group' );
+ var last = $( ".smw-input-group" ).last();
+
+ // Only clone when the autocomplete contains a term and is the last
+ // property input value is not empty to avoid cloning empty lines from
+ // a repeated confirmation of the same property value autocompletion
+ // request
+ var hasValues = last.find( '.smw-property-input' ).val() !== '' && last.last().find( '.smw-propertyvalue-input' ).val();
+
+ if ( opts.suggestion && opts.suggestion.value !== '' && hasValues ) {
+
+ // Clone the entire line for a new input
+ var clone = context.clone();
+ clone.find( "input:text" ).val( "" ).end().appendTo( '#smw-form-open' );
+
+ // We clone which means we have +1 available
+ $( "#smw-form-open > .smw-input-group > .smw-select-field option[value='del']" ).prop( "disabled", false );
+ clone.find( '.smw-propertyvalue-input' ).addClass( 'is-disabled' );
+ context.trigger( 'smw.autocomplete.property', { context: clone } );
+ }
+ } );
+
+ var initialForm = $( '#smw-searchoptions' ).find( 'input[name^=smw-form]' ).prop( 'value' );
+
+ /**
+ * Listing to the form select field change
+ */
+ $( this ).on( "change", "#smw-search-forms", function( event ) {
+
+ var that = $( this );
+ var type = $( '#smw-searchoptions' ).find( 'input[name^=smw-form]' ).prop( 'value' );
+ var nsList = $( "#smw-search-forms" ).data( 'nslist' );
+
+ if ( type !== '' ) {
+ $( '#smw-searchoptions .divider' ).show();
+
+ // Handle the hide/show of the NS section
+ if ( $( '#ns-list' ).css( 'display' ) === 'block' && initialForm === '' ) {
+ $( '#ns-list' ).css( 'display', 'none' );
+ $( '#smw-search-togglensview input' ).prop( 'value', 'Show' );
+ $( 'input[name=ns-list]' ).attr( 'value', 1 );
+ initialForm = $( "#smw-search-forms select" ).val();
+ } else {
+ $( '#smw-search-togglensview input' ).prop(
+ 'value',
+ ( $( '#ns-list' ).css( 'display' ) !== 'none' ? 'Hide': 'Show' )
+ );
+ }
+ } else {
+ $( '#smw-searchoptions .divider' ).hide();
+ }
+
+ // On any change, hide all fields by default and only make fields
+ // visible that belong the selected form.
+ $( '.smw-input-field' ).hide();
+ $( '.smw-select-field' ).hide();
+ $( '.smw-button-field' ).hide();
+ $( '.smw-form-description' ).hide();
+
+ // Important! The browser will complain about require fields that
+ // have been made invisible.
+ $( '.smw-input-field' ).find( 'input' ).prop( 'required', false );
+
+ if ( type === 'open' ) {
+ var group = $( '#smw-form-open > .smw-input-group' );
+
+ group.find( '.smw-input-field' ).show();
+ group.find( '.smw-select-field' ).show();
+ group.find( '.smw-button-field' ).show();
+ };
+
+ // Restore the NS settings for the once we have forcibly set
+ namespaces.map( function( v, ns ) {
+
+ if ( v === true ) {
+ $( '#search input[id^=mw-search-ns' + ns + ']' ).prop( 'checked', false );
+ };
+
+ namespaces[ns] = false
+ } );
+
+ if ( type !== '' ) {
+
+ var fields = $( '#smw-form-' + type + ' > .smw-input-field' )
+
+ fields.show();
+ $( '#smw-form-' + type + ' .smw-form-description' ).show();
+
+ fields.each( function() {
+ var required = $( this ).find( 'input' ).data( 'required' );
+
+ if ( required ) {
+ $( this ).find( 'input' ).prop( "required", true );
+ };
+ } );
+
+ // Do we have some namespaces that we want to enforce via the
+ // form definition?
+ if ( nsList.hasOwnProperty( type ) ) {
+ nsList[type].map( function( ns ) {
+ var checkbox = $( '#search input[id^=mw-search-ns' + ns + ']' );
+
+ // Already checked by a user? Do nothing!
+ if ( checkbox.prop( 'checked' ) === false ) {
+ namespaces[ns] = true;
+ checkbox.prop( 'checked', true );
+ };
+ } );
+ };
+ }
+ } );
+ } );
+ };
+
+ // Only load when it is Special:Search and the search type supports
+ // https://www.semantic-mediawiki.org/wiki/Help:SMWSearch
+ if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == 'Search' && mw.config.get( 'wgSearchType' ) == 'SMWSearch' ) {
+ smw.load( form );
+ };
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.input.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.input.js
new file mode 100644
index 00000000..cdc56b8c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.input.js
@@ -0,0 +1,52 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Support text input on Special:Search
+ *
+ * @since 3.0
+ */
+ var search = function() {
+
+ var context = $( '#searchText > input' ),
+ isHidden = false;
+
+ if ( context.length ) {
+
+ // Disable the standard autocompleter as no meaningfull help can be
+ // expected on a [[ ... ]] input
+ context.on( 'keyup keypres mouseenter', function( e ) {
+
+ // MW 1.27 - MW 1.31
+ var highlighter = context.parent().find( '.oo-ui-widget' );
+
+ // MW 1.32+
+ if ( highlighter.length == 0 ) {
+ highlighter = $( '.oo-ui-defaultOverlay > .oo-ui-widget' );
+ };
+
+ // Disable (hide) the MW's search input highlighter
+ if ( context.val().search( /\[|\[\[|in:|not:|has:|phrase:|::/gi ) > -1 ) {
+ highlighter.hide();
+ isHidden = true;
+ } else if( isHidden ) {
+ isHidden = false;
+ highlighter.show();
+ };
+ } );
+ }
+ };
+
+ // Only load when it is Special:Search and the search type supports
+ // https://www.semantic-mediawiki.org/wiki/Help:SMWSearch
+ if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == 'Search' && mw.config.get( 'wgSearchType' ) == 'SMWSearch' ) {
+ smw.load( search );
+ };
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.namespace.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.namespace.js
new file mode 100644
index 00000000..21659b0e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special.search/search.namespace.js
@@ -0,0 +1,73 @@
+/*!
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Namespace form in Special:Search
+ *
+ * @since 3.0
+ */
+ var namespace = function() {
+
+ /**
+ * Copied from mediawiki.special.search.js in order to have the NS
+ * button to work without #powersearch
+ */
+ var $checkboxes = $( '#search input[id^=mw-search-ns]' );
+
+ // JS loaded enable all fields
+ $( ".is-disabled" ).removeClass( 'is-disabled' );
+
+ $( document ).on( "click", "#mw-search-toggleall", function() {
+ $checkboxes.prop( 'checked', true );
+ } );
+
+ $( document ).on( "click", "#mw-search-togglenone", function(){
+ $checkboxes.prop( 'checked', false );
+ } );
+
+ // When saving settings, use the proper request method (POST instead of GET).
+ $( this ).on( "change", "#mw-search-powersearch-remember", function() {
+ this.form.method = this.checked ? 'post' : 'get';
+ } ).trigger( 'change' );
+
+ var nsList = $( '#mw-search-ns' ).css( 'display' ) !== 'none' ? mw.msg( 'smw-search-hide' ) : mw.msg( 'smw-search-show' );
+
+ /**
+ * Append hide/show button to the NS section
+ */
+ $( '#smw-search-togglensview' ).append(
+ $( '<input>' ).attr( 'type', 'button' )
+ .attr( 'id', 'smw-togglensview' )
+ .prop( 'value', nsList )
+ .click( function ( event ) {
+
+ // We carry the hidden `ns-list` on a submit so the status
+ // of the prevsious acion is retained to either show or hide
+ // the section
+ if ( $( '#mw-search-ns' ).css( 'display' ) !== 'none' ) {
+ $( 'input[name=ns-list]' ).attr( 'value', 1 );
+ event.target.value = mw.msg( 'smw-search-show' );
+ $( '#mw-search-ns' ).css( 'display', 'none' );
+ } else {
+ event.target.value = mw.msg( 'smw-search-hide' );
+ $( 'input[name=ns-list]' ).attr( 'value', 0 );
+ $( '#mw-search-ns' ).css( 'display', 'block' );
+ }
+ } )
+ )
+
+ };
+
+ // Only load when it is Special:Search and the search type supports
+ // https://www.semantic-mediawiki.org/wiki/Help:SMWSearch
+ if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == 'Search' && mw.config.get( 'wgSearchType' ) == 'SMWSearch' ) {
+ smw.load( namespace );
+ };
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.admin.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.admin.js
new file mode 100644
index 00000000..248c692a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.admin.js
@@ -0,0 +1,207 @@
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ /**
+ * @since 2.5
+ * @constructor
+ *
+ * @param {Object} mwApi
+ * @param {Object} util
+ *
+ * @return {this}
+ */
+ var admin = function ( mwApi ) {
+
+ this.VERSION = "2.5";
+ this.api = mwApi;
+
+ return this;
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {Object} context
+ */
+ admin.prototype.setContext = function( context ) {
+
+ this.context = context;
+ this.config = this.context.data( 'config' );
+
+ this.contentClass = '';
+ this.errorClass = '';
+
+ if ( this.config && this.config.hasOwnProperty( 'contentClass' ) ) {
+ this.contentClass = this.config.contentClass;
+ };
+
+ if ( this.config && this.config.hasOwnProperty( 'errorClass' ) ) {
+ this.errorClass = this.config.errorClass;
+ };
+
+ return this;
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ */
+ admin.prototype.doApiRequest = function( parameters ) {
+
+ var self = this,
+ content = mw.msg( 'smw-no-data-available' );
+
+ self.api.postWithToken( 'csrf', parameters ).done( function( data ) {
+
+ if ( data.hasOwnProperty( 'info' ) ) {
+ content = data.info.jobcount.length === 0 ? content : '<pre>' + JSON.stringify( data.info.jobcount, null, 2 ) + '</pre>';
+ } else if ( data.hasOwnProperty( 'task' ) ) {
+
+ if ( data.task.hasOwnProperty( 'isFromCache' ) ) {
+ var time = new Date( data.task.time * 1000 );
+ content = '<p>' + mw.msg( 'smw-list-count-from-cache', data.task.count, time.toUTCString() ) + '</p>';
+ } else {
+ content = '<p>' + mw.msg( 'smw-list-count', data.task.count ) + '</p>';
+ }
+
+ if ( data.task.list.length === 0 ) {
+ content = mw.msg( 'smw-no-data-available' );
+ } else {
+ content = content + '<pre>' + JSON.stringify( data.task.list, null, 2 ) + '</pre>';
+ }
+ }
+
+ self.replace( content );
+ } ).fail ( function( xhr, status, error ) {
+
+ var text = 'The API encountered an unknown error';
+
+ if ( status.hasOwnProperty( 'xhr' ) ) {
+ var xhr = status.xhr;
+
+ if ( xhr.hasOwnProperty( 'responseText' ) ) {
+ text = xhr.responseText.replace(/\<br \/\>/g," " );
+ } else if ( xhr.hasOwnProperty( 'statusText' ) ) {
+ text = 'The API returned with: ' + xhr.statusText.replace(/\<br \/\>/g," " );
+ };
+ }
+
+ if ( status.hasOwnProperty( 'error' ) ) {
+ text = status.error.code + ': ' + status.error.info + status.error['*'];
+ }
+
+ self.reportError( text );
+ } );
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {string} error
+ */
+ admin.prototype.reportError = function( error ) {
+
+ if ( this.contentClass === '' || this.errorClass === '' ) {
+ return;
+ };
+
+ this.context.find( '.' + this.contentClass ).hide();
+ this.context.find( '.' + this.errorClass ).append( error ).addClass( 'smw-callout smw-callout-error' );
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {string} content
+ */
+ admin.prototype.replace = function( content ) {
+
+ if ( this.contentClass === '' ) {
+ return;
+ };
+
+ this.context.css( 'opacity', 1 );
+ this.context.find( '.' + this.contentClass ).replaceWith( '<div class="' + this.contentClass + '">' + content + '</div>' );
+ };
+
+ var instance = new admin(
+ new mw.Api()
+ );
+
+ $( document ).ready( function() {
+
+ /**
+ * Find job count via the API
+ */
+ $( '.smw-admin-statistics-job' ).each( function() {
+
+ var parameters = {
+ action: 'smwinfo',
+ info: 'jobcount'
+ };
+
+ instance.setContext( $( this ) ).doApiRequest( parameters );
+ } );
+
+ /**
+ * Run selected jobs via the API
+ */
+ $( '.smw-admin-api-job-task' ).on( 'click', function( event ) {
+
+ var params = $.extend(
+ { 'waitOnCommandLine': true },
+ $( this ).data( 'parameters' )
+ );
+
+ var parameters = {
+ action: 'smwtask',
+ task: 'job',
+ params: JSON.stringify( {
+ 'subject': $( this ).data( 'subject' ),
+ 'job': $( this ).data( 'job' ),
+ 'parameters': params
+ } )
+ };
+
+ instance.setContext( $( this ) ).doApiRequest( parameters );
+ } );
+
+ /**
+ * Reload the page to ensure that the page on the first visit is not
+ * blocked by the request.
+ *
+ * @see https://stackoverflow.com/questions/5997450/append-to-url-and-refresh-page
+ */
+ $( '.smw-admin-db-preparation' ).each( function() {
+ window.location.search += '&prep=done';
+ } );
+
+ /**
+ * Find duplicate entities via the API
+ */
+ $( '.smw-admin-supplementary-duplookup' ).each( function() {
+
+ var parameters = {
+ action: 'smwtask',
+ task: 'duplookup',
+ params: []
+ };
+
+ instance.setContext( $( this ) ).doApiRequest( parameters );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.css
new file mode 100644
index 00000000..77997297
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.css
@@ -0,0 +1,661 @@
+/**
+ * This file is part of the Semantic MediaWiki Special:Ask module
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.8
+ *
+ * @file
+ * @ignore
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+.float-right {
+ float: right;
+}
+
+.float-left {
+ float: left;
+}
+
+.clear-both {
+ clear: both;
+}
+
+/* MobileFrontend sets those to 0 therefore declare them explicitly */
+div#ask fieldset {
+ margin: 0 0 0.5em 0;
+ padding: 0 1em 1em;
+}
+
+div#ask legend {
+ padding: 0.5em;
+ font-size: 95%;
+ font-weight:normal;
+ text-transform:none;
+}
+
+div#query legend {
+ font-weight:bold;
+}
+
+.smw-ask-toplinks {
+ padding: 0px 5px 5px 0px;
+ padding: 8px 15px;
+ margin-bottom: 10px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 3px;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+}
+
+.smw-ask-toplinks-edit {
+ padding: 11px 0px;
+ margin-right: 15px;
+ border-right: 3px solid #fff;
+}
+
+.smw-ask-actions-nav {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.smw-ask-actions-nav, .smw-ask-button-submit {
+ margin-top: 15px;
+}
+
+.smw-ask-condition, .smw-ask-printhead {
+ width: 49.5%;
+ border: 0;
+}
+
+.smw-ask-query .smw-table-row .smw-table-cell.smw-ask-condition,
+.smw-ask-query .smw-table-row .smw-table-cell.smw-ask-printhead {
+ border: 0;
+}
+
+.smw-ask-condition fieldset, .smw-ask-printhead fieldset {
+ border: 1px solid #a2a9b1;
+ border-radius:1px;
+ margin-top:5px;
+}
+
+.smw-ask-condition fieldset legend, .smw-ask-printhead fieldset legend{
+ font-weight: 600;
+}
+
+.smw-ask-result-navigation a, .smw-ask-result-navigation b, .smw-ask-downloadlinks a, .smw-ask-nav-prev, .smw-ask-nav-next, .smw-ask-actions-nav a {
+ vertical-align: middle;
+}
+
+.smw-ask-horizontalrule {
+ margin-bottom: 10px;
+ margin-top:10px;
+}
+
+.smw-ask-format-selection-help {
+ padding: 5px 5px 0 5px;
+}
+
+.smw-ask-cond-info {
+ margin-top: 15px;
+ margin-bottom: 10px;
+}
+
+.smw-ask-format-list select {
+ vertical-align: middle;
+ margin-top: 5px;
+}
+
+.smw-ask-query {
+ margin-bottom: 5px;
+}
+
+.smw-ask-query .smw-table-header {
+ background-color: transparent;
+ text-align: center;
+ padding-bottom: 5px;
+}
+
+.smw-ask-query .smw-table-header .smw-table-cell {
+ border: 0px;
+ padding: 0px 0px 5px 0px;
+}
+
+.smw-ask-query .smw-table-row .smw-table-cell {
+ border: 1px solid #a2a9b1;
+ padding: 0px;
+}
+
+.smw-ask-query textarea {
+ border: 0px;
+ resize: none;
+ padding: 2px 0 0 2px;
+ outline: none;
+ line-height: normal !important;
+ box-shadow: none !important;
+ margin:0 !important;
+}
+
+.smw-ask-query .condition {
+ width: 49.5%;
+}
+
+.smw-ask-options-row-odd {
+ background: #eee;
+}
+
+.smw-ask-options-row-odd td {
+ padding: 5px;
+ vertical-align: top;
+}
+
+.smw-ask-options-row-even {
+ background: #fff;
+}
+
+.smw-ask-options-row-even td {
+ padding: 5px;
+ vertical-align: top;
+}
+
+.smw-ask-button {
+ position: relative;
+ padding: 6px 14px;
+ color: #586069;
+ border: 1px solid #e1e4e8;
+ border-radius: 3px;
+ font-size: 0.975em;
+ margin-right: 5px;
+}
+
+.smw-ask-button-submit input {
+display: inline-block;
+ padding: .30em .55em;
+ margin-left: -1px;
+ margin-right: 10px;
+ line-height: 1.25;
+ color: #007bff;
+ background-color: #fff;
+ border: 1px solid #f9f9f9;
+ padding-top: 0.35em;
+ z-index: 1;
+ color: #fff;
+ background-color: #eee;
+ border-color: #f9f9f9;
+ margin-left: 0;
+ border-top-right-radius: .2em;
+ border-bottom-right-radius: .2em;
+ margin-left: 0;
+ border-top-left-radius: .2em;
+ border-bottom-left-radius: .2em;
+ font-weight: normal;
+ text-shadow: none !important;
+ outline: none;
+ cursor: pointer;
+ font-size: 1.1em;
+ color: #72777d;
+ width: 100%;
+}
+
+.smw-ask-button-submit input:hover {
+ background-color: #ddd;
+ border-color: #ddd;
+}
+
+.smw-ask-button-right {
+ position: relative;
+ padding: 6px 14px;
+ color: #586069;
+ border: 1px solid #e1e4e8;
+ border-radius: 3px;
+ float: right;
+}
+
+.smw-ask-button:hover {
+ background-color: #f6f8fa;
+}
+
+.smw-ask-button input {
+ background: transparent !important;
+ border: none !important;
+ font-size: 1.1em;
+ color: #fff !important;
+ text-shadow: none !important;
+ outline: none;
+ cursor: pointer;
+}
+
+.smw-ask-button-right {
+ padding: 4px 14px;
+ float: right;
+ margin-left: 5px;
+ margin-right: 0px;
+ margin-top: -4px;
+}
+
+.smw-ask-button-lblue {
+ color: #fff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
+ text-decoration: none;
+}
+
+.smw-ask-button-lblue:hover {
+ background-image: none;
+ background-color: #31b0d5;
+ border-color: #269abc;
+}
+
+.smw-ask-button-dblue {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+ text-decoration: none;
+}
+
+.smw-ask-button-dblue:hover {
+ background-image: none;
+ background-color: #286090;
+ border-color: #204d74;
+}
+
+.smw-ask-button-lgrey {
+ background-image: none;
+ background-color: #eee;
+ border-color: #ddd;
+}
+
+.smw-ask-button-lgrey:hover {
+ background-image: none;
+ background-color: #ddd;
+ border-color: #ddd;
+}
+
+.smw-ask-button a, .smw-ask-button a:hover, .smw-ask-button a:focus, .smw-ask-button a:active, .smw-ask-button a:visited {
+ font-weight: 300;
+ line-height: 20px;
+ text-decoration: none;
+ color: #586069;
+}
+
+.smw-ask-button-lblue a, .smw-ask-button-lblue a:hover, .smw-ask-button-lblue a:focus, .smw-ask-button-lblue a:active, .smw-ask-button-lblue a:visited {
+ font-weight: 300;
+ line-height: 20px;
+ text-decoration: none;
+ color: #fff;
+}
+
+.smw-ask-button-dblue a, .smw-ask-button-dblue a:hover, .smw-ask-button-dblue a:focus, .smw-ask-button-dblue a:active, .smw-ask-button-dblue a:visited {
+ font-weight: 300;
+ line-height: 20px;
+ text-decoration: none;
+ color: #fff;
+}
+
+#inlinequeryembed {
+ margin-top: 15px;
+}
+
+#inlinequeryembedinstruct {
+ margin-bottom: 10px;
+}
+
+.smw-ask-search fieldset, .smw-ask-format fieldset, .smw-ask-options fieldset {
+ border: 1px solid #a2a9b1;
+ border-radius: 1px;
+}
+
+.smw-tabs.smw-ask-search-edit {
+ margin-top: 20px;
+}
+
+.smw-tabs.smw-ask-search-edit.empty-result {
+ margin-top: 0px;
+}
+
+.smw-ask-search-edit, .smw-ask-search-compact {
+ margin-bottom: 20px;
+}
+
+.smw-ask-search-edit.empty-result {
+ margin-bottom: 0px;
+}
+
+/* https://stackoverflow.com/questions/2812770/add-centered-text-to-the-middle-of-a-hr-like-line */
+.strike {
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ padding: 15px 0 15px 0;
+}
+
+.strike > span {
+ position: relative;
+ display: inline-block;
+ font-size: 95%;
+ margin-left: 20px;
+}
+
+.strike > span:before,
+.strike > span:after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ width: 9999px;
+ height: 1px;
+ background: #aaa;
+}
+
+.strike > span:before {
+ right: 100%;
+ margin-right: 10px;
+}
+
+.strike > span:after {
+ left: 100%;
+ margin-left: 10px;
+}
+
+.smw-ask-format-help-link {
+ margin-top: 10px;
+ width: auto;
+ word-break: break-word;
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+}
+
+.smw-ask-sort-input input, .smw-propertysubject-input {
+ padding: 4px;
+ border: 1px solid #aaa;
+ margin-right: 10px;
+ margin-top: 5px;
+}
+
+.options-list .smw-property-input {
+}
+
+.smw-ask-sort-input .autocomplete-arrow, .smw-table-cell .autocomplete-arrow {
+ background-position-y: 0px;
+}
+
+.options-list .smw-table-cell .autocomplete-arrow {
+}
+
+.smw-ask-sort-input .autocomplete-loading {
+ vertical-align: unset;
+}
+
+.smw-table-cell .autocomplete-loading {
+ vertical-align: unset;
+ padding: 4px;
+}
+
+.options-list .smw-table-cell .autocomplete-loading {
+}
+
+.smw-table-cell .mw-ui-input.autocomplete-loading {
+ vertical-align: unset;
+ padding: 0.625em 0.625em 0.546875em;
+}
+
+.smw-ask-sort-input .smw-ask-sort-delete {
+ padding: 3px;
+ margin-right: 10px;
+ margin-bottom: 5px;
+}
+
+.smw-ask-sort-input select {
+ padding: 3px;
+ margin-right: 10px;
+ margin-bottom: 5px;
+ margin-top: 5px;
+}
+
+.smw-ask-sort-add a::before, .smw-ask-sort-delete a::before {
+ content:"[";
+}
+
+.smw-ask-sort-add a::after, .smw-ask-sort-delete a::after {
+ content:"]";
+}
+
+.smw-ask-sort-delete, .smw-ask-sort-add {
+ cursor: pointer;
+}
+
+.smw-ask-downloadlinks {
+ margin-left: 10px;
+}
+
+/**
+ * Toggle
+ */
+
+.options-toggle-action {
+ min-width: 15px;
+ display: inline-block;
+ text-align: center;
+}
+
+.options-toggle-action label {
+ cursor: pointer;
+}
+
+.options-parameter-list {
+ position: relative;
+}
+
+#options-toggle {
+ display: none;
+}
+
+#options-list .options-parameter-list {
+ min-height: 100px;
+ overflow: hidden;
+ max-height: 0;
+ padding: 0;
+ margin: 0 auto;
+ -webkit-transition: all 0.3s ease;
+}
+
+#options-toggle:checked + #options-list .options-parameter-list {
+ max-height: none;
+}
+
+#options-toggle:checked + #options-list .options-message {
+ display:none;
+}
+
+#options-toggle:not(:checked) + #options-list .options-parameter-list:after {
+ content: "";
+ position: absolute;
+ z-index: 1;
+ bottom: 0;
+ left: 0;
+ pointer-events: none;
+ background-image: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255, 1) 90%);
+ width: 100%;
+ height: 4em;
+}
+
+.options-parameter-list-plain {
+ min-height: 100px;
+ overflow: hidden;
+ max-height: 0;
+ padding: 0;
+ margin: 0 auto;
+ -webkit-transition: all 0.3s ease;
+}
+
+.options-parameter-list-plain:after {
+ content: "";
+ position: absolute;
+ z-index: 1;
+ bottom: 0;
+ left: 0;
+ pointer-events: none;
+ background-image: none;
+ width: 100%;
+ height: 4em;
+}
+
+.slowfade textarea::placeholder { color:#777777; transition: color 1s; font-weight:300; font-style: italic; }
+.slowfade textarea:focus::placeholder { color:transparent; }
+
+.hide-mode {
+ display: none;
+}
+
+.export-links ul{
+ display: inline;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ list-style-type: none;
+ list-style-image: none;
+}
+
+.export-links li {
+ display: inline-block;
+ line-height: 1em;
+ border-left: 1px solid #a2a9b1;
+ margin: 0.125em 0;
+ padding: 0 0.5em;
+ zoom: 1;
+}
+
+
+/**
+ * Tabs
+*/
+#tab-smw-askt-search:checked ~ #tab-content-smw-askt-search,
+#tab-smw-askt-result:checked ~ #tab-content-smw-askt-result,
+#tab-smw-askt-code:checked ~ #tab-content-smw-askt-code,
+#tab-smw-askt-clipboard:checked ~ #tab-content-smw-askt-clipboard,
+#tab-smw-askt-debug:checked ~ #tab-content-smw-askt-debug,
+#tab-smw-askt-extra:checked ~ #tab-content-smw-askt-extra,
+#tab-smw-askt-edit:checked ~ #tab-content-smw-askt-edit,
+#tab-smw-askt-compact:checked ~ #tab-content-smw-askt-compact,
+#tab-smw-askt-options:checked ~ #tab-content-smw-askt-options {
+ display: block;
+}
+
+.smw-tabs input.nav-tab:checked + label.nav-label.result-cache {
+ border-top: 2px solid orange;
+}
+
+.smw-tabs input.nav-tab:checked + label.nav-label.clipboard-bookmark {
+ border:0;
+}
+
+.smw-tabs input.nav-tab:checked + label.nav-label.edit-action {
+ border: 0;
+}
+
+.smw-tabs label.nav-label.edit-action,
+.smw-tabs label.nav-label.compact-action,
+.smw-tabs label.nav-label.clipboard-bookmark.smw-tab-right {
+ padding: 5px 0px;
+}
+
+.clipboard.smw-icon-bookmark {
+ padding: 0 0 0 60px;
+}
+
+.smw-tabs input.nav-tab:checked + label.nav-label.compact-action {
+ border:0;
+}
+
+/**
+ * Responsive settings (#see smw.table.css)
+ */
+@media screen and (max-width: 800px) {
+ .smw-table-row .smw-table-cell {
+ width: auto !important;
+ margin: 0.5em 0;
+ }
+
+ .smw-table-cell .parameter-number-input, .smw-table-cell .parameter-string-input, .smw-ask-sort-input, .strike, .slowfade fieldset {
+ width:100% !important;
+ }
+
+ .smw-ask-button-right {
+ clear:both;
+ float:none;
+ margin: 0 5px 0 0;
+ }
+
+ .smw-ask-toplinks-edit {
+ border-right: 0;
+ }
+
+ .smw-ask-downloadlinks.float-right {
+ clear:both;
+ float:unset;
+ }
+
+ .smw-ask-button {
+ display: flex;
+ margin-bottom: 10px;
+ }
+
+ .smw-ask-button-submit {
+ display: flex;
+ margin-bottom: 10px;
+ }
+
+ .smw-ask-actions-nav {
+ flex-direction: column;
+ }
+
+ .smw-ask-downloadlinks {
+ margin-top: 10px;
+ margin-left: 0px;
+ }
+
+ .smw-ask-button input, .smw-ask-button-right input, .smw-ask-button a, .smw-ask-button-right a, #embed_show a, #embed_hide a {
+ width: 100%;
+ text-align: center;
+ }
+
+ .smw-ask-format-selector {
+ display: inline-block;
+ margin-bottom: 0;
+ }
+}
+
+.skin-chameleon .autocomplete-arrow {
+ background-position-y: 1px;
+ padding: 2px;
+}
+
+.skin-chameleon .autocomplete-loading {
+ padding: 2px;
+}
+
+.skin-chameleon .smw-ask-sort-input select, .skin-chameleon .smw-ask-options-list select {
+ padding: 0.25em;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.js
new file mode 100644
index 00000000..e30f8029
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.ask.js
@@ -0,0 +1,372 @@
+/**
+ * This file is part of the Semantic MediaWiki Special:Ask module
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @file
+ * @ignore
+ *
+ * @since 1.9
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author: Jeroen De Dauw <jeroendedauw at gmail dot com>
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * @since 3.0
+ * @constructor
+ *
+ * @return {this}
+ */
+ var change = function ( name ) {
+
+ this.name = name;
+ this.messages = {};
+
+ this.html = mw.html;
+ this.hideList = '#ask-embed, #inlinequeryembed, #ask-showhide, #ask-debug, #ask-clipboard, #ask-navinfo, #ask-cache, #result, #result-error, #ask-pagination, #ask-export-links, #tab-label-smw-askt-compact, #tab-label-smw-askt-code, #tab-label-smw-askt-debug, #tab-label-smw-askt-extra, #tab-label-smw-askt-result, #tab-label-smw-askt-clipboard, #search';
+
+ return this;
+ };
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ * @param {Sting} message
+ */
+ change.prototype.add = function( key, message, type ) {
+ this.messages[key] = [ message, type ];
+ }
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ */
+ change.prototype.delete = function( key ) {
+ delete this.messages[key];
+ }
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ */
+ change.prototype.informAbout = function( key ) {
+
+ var informAbout = '';
+
+ // Anything to inform about?
+ if ( this.messages.hasOwnProperty( key ) ) {
+ informAbout = key;
+ } else {
+ for( var prop in this.messages ) {
+ if ( prop !== key && this.messages.hasOwnProperty( prop ) ) {
+ informAbout = prop
+ }
+ }
+ }
+
+ if ( informAbout !== '' ) {
+ this.show( informAbout );
+ } else {
+ this.hide();
+ }
+ }
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ */
+ change.prototype.show = function( key ) {
+
+ var msg = this.messages[key];
+
+ var html = this.html.element(
+ 'div',
+ {
+ id: 'status-format-change',
+ class: 'smw-callout smw-callout-' + msg[1]
+ },
+ mw.msg( msg[0], this.name )
+ );
+
+ $( this.hideList ).hide();
+
+ $( '#status-format-change' ).remove();
+ $( '#ask-status' ).append( html );
+ }
+
+ /**
+ * @since 3.0
+ * @method
+ */
+ change.prototype.hide = function() {
+
+ $( this.hideList ).show();
+ $( '#status-format-change' ).remove();
+
+ $( '#inlinequeryembed, #embed_hide' ).hide();
+ $( '#embed_show' ).show();
+ }
+
+ /**
+ * Support and helper methods
+ * @ignore
+ */
+ var tooltip = new smw.util.tooltip();
+
+ var _init = {
+
+ // Tooltip
+ tooltip: function(){
+ $( '.smw-ask-info' ).each( function(){
+ tooltip.show( {
+ context: $( this ),
+ content: $( this ).data( 'info' ),
+ title : mw.msg( 'smw-ui-tooltip-title-parameter' ),
+ button : false
+ } );
+ } );
+
+ $( '#options-list' ).trigger( 'smw.autocomplete.propertysubject', { context: $( '#options-list' ) } );
+ $( '#options-list' ).trigger( 'smw.autocomplete.property', { context: $( '#options-list' ) } );
+ },
+
+ // Format help link
+ formatHelp: function( options ){
+ $( '.smw-ask-format-help-link' ).replaceWith(
+ '<li class="smw-ask-format-help-link">'+ mw.msg( 'smw-ask-format-selection-help', addFormatHelpLink( options ) ) + '</li>'
+ );
+ }
+ };
+
+ /**
+ * Add format help link
+ *
+ * We do not try to be smart here but using a pragmatic approach to generate
+ * the URL by assuming Help:<format> format
+ *
+ * @return object
+ */
+ function addFormatHelpLink ( options ){
+ var h = mw.html,
+ link = h.element( 'a', {
+ href: 'https://semantic-mediawiki.org/wiki/Help:' + options.format + ' format',
+ title: options.name
+ }, options.name
+ );
+ return link;
+ }
+
+ /**
+ * Multiple sorting
+ */
+ var num_elements = $( '#sorting_main > div' ).length;
+
+ function addSortInstance(starter_div_id, main_div_id) {
+ num_elements++;
+
+ var starter_div = $( '#' + starter_div_id),
+ main_div = $( '#' + main_div_id),
+ new_div = starter_div.clone();
+
+ new_div.attr( {
+ 'class': 'smw-ask-sort-input multipleTemplate',
+ 'id': 'sort_div_' + num_elements
+ } );
+
+ new_div.css( 'display', 'block' );
+
+ //Create 'delete' link
+ var button = $( '<a>').attr( {
+ 'class': 'smw-ask-sort-delete-action',
+ 'data-target': 'sort_div_' + num_elements
+ } ).text( mw.msg( 'smw-ask-delete' ) );
+
+ // Register event on the added instance
+ button.click( function( event ) {
+ removeInstance( $( this ).data( 'target' ) );
+ } );
+
+ new_div.append(
+ $( '<span class="smw-ask-sort-delete">' ).html( button )
+ );
+
+ // Trigger an event to ensure that the input field has an autocomplete
+ // instance attached
+ main_div.trigger( 'SMW::Property::Autocomplete' , {
+ 'context': new_div
+ } );
+
+ // Add the new instance
+ main_div.append( new_div );
+ }
+
+ function removeInstance(div_id) {
+ $( '#' + div_id ).remove();
+ }
+
+ /**
+ * Implementation of an Special:Ask instance
+ * @since 1.8
+ * @ignore
+ */
+ $( document ).ready( function() {
+
+ var condition = $( '#ask-query-condition' ),
+ condVal = '',
+ isEmpty = '';
+
+ if ( condition.length ) {
+
+ condVal = condition.val().trim(),
+ isEmpty = condVal === '';
+
+ var entitySuggester = smw.Factory.newEntitySuggester(
+ condition
+ );
+
+ // Register autocomplete default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+ };
+
+ // Field input is kept disabled until JS is fully loaded to signal
+ // "ready for input"
+ $( '#ask, #result' ).removeClass( 'is-disabled' );
+
+ // Get initial format and language settings
+ var selected = $( '#formatSelector option:selected' ),
+ options = {
+ selector : '#formatSelector',
+ format : selected.val(),
+ name : selected.text(),
+ isExport: selected.data( 'isexport' ) == 1
+ };
+
+ var chg = new change( options['name'] );
+
+ // Init
+ _init.tooltip();
+ _init.formatHelp( options );
+
+ $( '.smw-ask-sort-delete-action' ).click( function() {
+ removeInstance( $( this).data( 'target' ) );
+ } );
+
+ $( '.smw-ask-sort-add-action' ).click( function() {
+ addSortInstance( 'sorting_starter', 'sorting_main' );
+ } );
+
+ // Options toggle icon
+ $( '.options-toggle-action label' ).click( function() {
+ if ( $( '#options-toggle' ).prop( 'checked' ) ) {
+ $( this).html( '+' );
+ $( this).attr( 'title', mw.msg( 'smw-section-expand' ) );
+ } else {
+ $( this).html( '-' );
+ $( this).attr( 'title', mw.msg( 'smw-section-collapse' ) );
+ }
+ } );
+
+ // Submit the form via CTRL + q
+ $( "form" ).keypress( function ( event ) {
+ if ( event.ctrlKey && event.keyCode == 17 ) {
+ $( '#search input[type=submit]' ).click();
+ event.preventDefault();
+ return false;
+ } else {
+ return true;
+ };
+ } );
+
+ // Changed condition
+ $( '#ask-query-condition' ).change( function( event, $opts ) {
+
+ var $this = $( this );
+
+ if ( isEmpty === false && $this.val().trim() !== condVal ) {
+ chg.add( 'condition', 'smw-ask-condition-change-info', 'warning' );
+ } else {
+ chg.delete( 'condition' );
+ }
+
+ chg.informAbout( 'condition' );
+ } );
+
+ // Change format parameter form via ajax
+ $( '#formatSelector' ).change( function( event, $opts ) {
+
+ var $this = $( this ),
+ isExport = $this.find( 'option:selected' ).data( 'isexport' ) == 1;
+
+ // Opaque options list for as long as the list is being generated
+ // via an ajax request
+ $( '#options-list' ).addClass( 'is-disabled' );
+
+ if ( isExport ) {
+ chg.add( 'format', 'smw-ask-format-export-info', 'info' );
+ } else if ( isEmpty === false && options['format'] !== $this.val() ) {
+ chg.add( 'format', 'smw-ask-format-change-info', 'warning' );
+ } else {
+ chg.delete( 'format' )
+ }
+
+ chg.informAbout( 'format' );
+
+ $.ajax( {
+ // Evil hack to get more evil Spcial:Ask stuff to work with less evil JS.
+ 'url': $this.data( 'url' ).replace( 'this.value', $this.val() ),
+ 'context': document.body,
+ 'success': function( data ) {
+ $( '#options-list' ).html(
+ '<div class="options-parameter-list">' + data + '</div>'
+ );
+
+ // Remove disable state that was set at the beginning of the
+ // onChange event
+ $( '#options-list' ).removeClass( 'is-disabled' );
+
+ // Reinitialize functions after each ajax request
+ _init.tooltip();
+
+ // Update format created by the ajax instance
+ _init.formatHelp( $.extend( {}, options, {
+ format: $this.val(),
+ name: $this.find( 'option:selected' ).text()
+ } ) );
+ }
+ } );
+ } );
+ } );
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.css
new file mode 100644
index 00000000..c585cd92
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.css
@@ -0,0 +1,259 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+.smwb-container .smw-overlay-spinner {
+ left: 45%;
+ top: 40%;
+}
+
+.smwb-datasheet, .smwb-content {
+ margin-right: 0.5em;
+}
+
+.smwb-factbox {
+ border-left: 0.5em solid #DDDDDD;
+ width: 100%
+}
+
+.smwb-factbox .smwb-propvalue.smwb-group {
+ width: 100%;
+}
+
+.smwb-prophead a {
+ overflow-wrap: break-word;
+ word-break: break-word;
+ word-wrap: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+.smwb-factbox .smwb-cell {
+ border: 0px solid #999999;
+ border-top: 2px solid #fff;
+/* border-bottom: 2px solid #fff; */
+}
+
+.smwb-cell-empty {
+ border-left: 0.5em solid #DDDDDD;
+}
+
+.smwb-prophead {
+ background-color: #ddd;
+}
+
+.smwb-title {
+ font-size: 200%;
+ background-color: #DDDDDD;
+ line-height: 1.5;
+ font-weight: normal;
+}
+
+.smwb-title td {
+ padding-left: 5px;
+ border-bottom: 2px solid white;
+}
+
+.smwb-propvalue {
+ background-color: #EEEEEE;
+}
+
+.smwb-propvalue .smwb-prophead {
+ text-align: right;
+ vertical-align: top;
+ font-weight: bold;
+ font-size: 120%;
+ background-color: #DDDDDD;
+ padding: 0.2em 0.6em;
+}
+
+.smwb-propvalue .smwb-propval {
+ padding: 0.4em 0.6em;
+ background-color: #EEEEEE;
+ vertical-align: top;
+}
+
+.smwb-center, .smwb-actions {
+ background-color: #DDDDDD;
+ padding: 0px;
+}
+
+.smwb-actions {
+ line-height: 2.0em;
+}
+
+.smwb-title a, .smwb-center a:first-child, .smwb-actions a:first-child {
+ padding-left: 8px;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+span.smwb-value {
+}
+
+.smwb-bottom {
+ border-bottom: 2px solid #fff;
+}
+
+/**
+ * Inverse factbox
+ * @ignore
+ */
+.smwb-ifactbox {
+ border-right: 0.5em solid #DDDDDD;
+ width: 100%
+}
+
+.smwb-ifactbox .smwb-cell {
+ border: 0px solid #999999;
+ border-top: 2px solid #fff;
+/* border-bottom: 2px solid #fff; */
+}
+
+.smwb-ititle {
+ font-size: 200%;
+ background-color: #DDDDDD;
+ line-height: 1.5;
+}
+
+.smwb-ititle td {
+ padding-left: 5px;
+ border-bottom: 2px solid white;
+}
+
+.smwb-ipropvalue {
+ background-color: #EEEEEE;
+ text-align: right;
+}
+
+.smwb-ipropvalue .smwb-prophead {
+ text-align: left;
+ font-weight: bold;
+ font-size: 120%;
+ background-color: #DDDDDD;
+ padding: 0.2em 0.6em;
+}
+
+.smwb-ipropvalue .smwb-propval {
+ background-color: #EEEEEE;
+ padding-right: 1em;
+ padding: 0.4em 0.6em;
+}
+
+.smwb-factbox .group-link:before {
+ content:"â–¾ ";
+}
+
+.smwb-ifactbox .group-link:after {
+ content:" â–¾";
+}
+
+.smwb-input {
+ display: flex;
+}
+
+.smwb-input .input-field {
+ flex: 1; margin: 0 0 0 0;
+}
+
+.smwb-input .button-field {
+ flex: 0; margin: 0 0 0 1rem;
+}
+
+.smwb-input .button-field input {
+ padding: 6px;
+}
+
+.smwb-input .mw-ui-input:focus {
+ box-shadow: inset 0 0 0 1px #ddd;
+ border-color: #ddd;
+ outline: 0;
+}
+
+.smwb-input input {
+ padding: 0.52em 1em;
+}
+
+.smwb-factbox .smwb-propvalue .smwb-prophead, .smwb-ifactbox .smwb-ipropvalue .smwb-prophead, .smwb-ifactbox .smwb-propvalue .smwb-prophead {
+ width: 30%;
+}
+
+.mw-ui-input.autocomplete-loading {
+ vertical-align: unset;
+ padding: 0.52em 1em !important;
+}
+
+.smwb-action-separator {
+ display: inline-block;
+ line-height: 1.025em;
+ border-left: 1px solid #a2a9b1;
+ margin: 0.2em 0.2em 0.2em 0.45em;
+ zoom: 1;
+}
+
+/**
+ * Light theme
+ */
+.smwb-theme-light .smwb-factbox {
+ border-left: 0.5em solid #f0f0f0;
+}
+
+.smwb-theme-light .smwb-ifactbox {
+ border-right: 0.5em solid #f0f0f0;
+}
+
+.smwb-theme-light .smwb-title, .smwb-theme-light .smwb-center, .smwb-theme-light .smwb-actions, .smwb-theme-light .smwb-prophead {
+ background-color: #f0f0f0;
+}
+
+.smwb-theme-light .smwb-propval {
+ background-color: #f7f7f7;
+}
+
+/**
+ * Responsive settings (#see smw.table.css)
+ */
+@media screen and (max-width: 800px) {
+ .smwb-factbox .smwb-propvalue .smwb-prophead, .smwb-ifactbox .smwb-ipropvalue .smwb-prophead, .smwb-ifactbox .smwb-propvalue .smwb-prophead {
+ width: auto;
+ }
+
+ .smwb-ifactbox .smwb-ipropvalue .smwb-prophead, .smwb-ifactbox .smwb-ipropvalue .smwb-propval, .smwb-ifactbox .smwb-propvalue .smwb-prophead {
+ justify-content: flex-end;
+ }
+
+ .smwb-group .smwb-propval, .smwb-group .smwb-theme-light .smwb-propval {
+ background-color: #d9e8f3;
+ }
+
+ .smwb-emptysheet .smwb-prophead {
+ border-top: none;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.js
new file mode 100644
index 00000000..88a91318
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.js
@@ -0,0 +1,224 @@
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw, smw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ /**
+ * @since 2.5.0
+ * @constructor
+ *
+ * @param {Object} mwApi
+ * @param {Object} util
+ *
+ * @return {this}
+ */
+ var browse = function ( mwApi ) {
+
+ this.VERSION = "3.0.0";
+ this.api = mwApi;
+
+ return this;
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {Object} context
+ */
+ browse.prototype.setContext = function( context ) {
+ this.context = context;
+ this.options = context.data( 'options' );
+
+ return this;
+ }
+
+ /**
+ * @since 2.5
+ * @method
+ */
+ browse.prototype.requestHTML = function() {
+
+ var self = this,
+ subject = self.context.data( 'subject' );
+
+ // Expect a serialization format (see DIWikiPage::getHash)
+ if ( subject.indexOf( "#" ) == -1 ) {
+ return this.context.find( '.smwb-status' )
+ .append(
+ mw.msg( 'smw-browse-api-subject-serialization-invalid' )
+ )
+ .addClass( 'smw-callout smw-callout-error' );
+ }
+
+ subject = subject.split( "#" );
+
+ self.api.post( {
+ action: "smwbrowse",
+ browse: "subject",
+ params: JSON.stringify( {
+ subject: subject[0],
+ ns: subject[1],
+ iw: subject[2],
+ subobject: subject[3],
+ options: self.options,
+ type: 'html'
+ } )
+ } ).done( function( data ) {
+ self.context.find( '.smwb-emptysheet' ).replaceWith( data.query );
+ self.triggerEvents();
+ } ).fail ( function( xhr, status, error ) {
+ self.reportError( xhr, status, error );
+ } );
+ }
+
+ /**
+ * @since 3.0
+ * @method
+ */
+ browse.prototype.doUpdate = function( opts ) {
+
+ var self = this,
+ subject = self.context.data( 'subject' );
+
+ subject = subject.split( "#" );
+
+ self.context.addClass( 'is-disabled' );
+ self.context.append( '<span id="smw-wait" class="smw-overlay-spinner large inline"></span>' );
+
+ self.api.post( {
+ action: "smwbrowse",
+ browse: "subject",
+ params: JSON.stringify( {
+ subject: subject[0],
+ ns: subject[1],
+ iw: subject[2],
+ subobject: subject[3],
+ options: $.extend( self.options, opts ),
+ type: 'html'
+ } )
+ } ).done( function( data ) {
+ self.context.removeClass( 'is-disabled' );
+ self.context.find( '#smw-wait' ).remove();
+
+ self.context.find( '.smwb-form' ).remove();
+ self.context.find( '.smwb-modules' ).remove();
+ self.context.find( '.smwb-datasheet' ).replaceWith( data.query );
+
+ self.triggerEvents();
+ } ).fail ( function( xhr, status, error ) {
+ self.reportError( xhr, status, error );
+ } );
+ }
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {object} xhr
+ * @param {object} status
+ * @param {object} error
+ */
+ browse.prototype.reportError = function( xhr, status, error ) {
+
+ var text = 'The API encountered an unknown error';
+
+ if ( status.hasOwnProperty( 'xhr' ) ) {
+ var xhr = status.xhr;
+
+ if ( xhr.hasOwnProperty( 'statusText' ) ) {
+ text = 'The API returned with: ' + xhr.statusText.replace(/\<br \/\>/g," " );
+ };
+
+ if ( xhr.hasOwnProperty( 'responseText' ) ) {
+ text = xhr.responseText.replace(/\<br \/\>/g," " ).replace(/#/g, "<br\>#" );
+ };
+ }
+
+ if ( status.hasOwnProperty( 'error' ) ) {
+ text = '<b>' + status.error.code + '</b><br\>' + status.error.info.replace(/#/g, "<br\>#" );
+ }
+
+ this.context.find( '.smwb-status' ).append( text ).addClass( 'smw-callout smw-callout-error' );
+ }
+
+ /**
+ * @since 2.5
+ * @method
+ */
+ browse.prototype.triggerEvents = function() {
+
+ var self = this;
+ var form = self.context.find( '.smwb-form' );
+
+ form.trigger( 'smw.page.autocomplete' , {
+ 'context': form
+ } );
+
+ mw.loader.load(
+ self.context.find( '.smwb-modules' ).data( 'modules' )
+ );
+
+ // Re-apply JS-component instances on new content
+ // Trigger an event
+ mw.hook( 'smw.browse.apiparsecomplete' ).fire( self.context );
+
+ $( document ).trigger( 'SMW::Browse::ApiParseComplete' , {
+ 'context': self.context
+ } );
+ }
+
+ var instance = new browse(
+ new mw.Api()
+ );
+
+ $( document ).ready( function() {
+
+ /**
+ * Group related actions
+ */
+ $( document ).on( 'click', '.smw-browse-hide-group', function( event ) {
+ instance.doUpdate( { "group": "hide" } );
+ event.preventDefault();
+ } );
+
+ $( document ).on( 'click', '.smw-browse-show-group', function( event ) {
+ instance.doUpdate( { "group": "show" } );
+ event.preventDefault();
+ } );
+
+ /**
+ * Incoming, outgoing related actions
+ */
+ $( document ).on( 'click', '.smw_browse_hide_incoming', function( event ) {
+ instance.doUpdate( { "dir": "out" } );
+ event.preventDefault();
+ } );
+
+ $( document ).on( 'click', '.smw_browse_show_incoming', function( event ) {
+ instance.doUpdate( { "dir": "both" } );
+ event.preventDefault();
+ } );
+
+ $( '.smwb-container' ).each( function() {
+ instance.setContext( $( this ) ).requestHTML();
+ } );
+
+ var form = $( this ).find( '.smwb-form' );
+
+ mw.loader.using( [ 'ext.smw.browse', 'ext.smw.browse.autocomplete' ] ).done( function () {
+ form.trigger( 'smw.page.autocomplete' , {
+ 'context': form
+ } );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.skin.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.skin.css
new file mode 100644
index 00000000..c5b2f790
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.browse.skin.css
@@ -0,0 +1,11 @@
+/**
+ * .skin-chameleon specific styles
+ */
+.skin-chameleon .smwb-datasheet, .skin-chameleon .smwb-content {
+ margin-right: 0em;
+}
+
+.skin-chameleon .smwb-container .smw-overlay-spinner {
+ left: 50%;
+ top: 45%;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.css
new file mode 100644
index 00000000..fe3be12b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/special/ext.smw.special.css
@@ -0,0 +1,111 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Special:Admin
+ */
+.smw-admin-deprecation-notice-docu-section {
+ padding: 0.8rem;
+ background-color: #fcf8e3;
+ border-color: #faf2cc;
+ color: #8a6d3b;
+}
+
+.smw-admin-hr {
+ margin-top:20px;
+ background-color: #ddd;
+}
+
+.smw-admin-deprecation-notice-section-explanation {
+ font-size:12px;
+ font-style: italic;
+}
+
+.smw-special-filter{
+ padding: 5px 0px 0px 0px;
+}
+
+.smw-special-filter-button {
+ position: relative;
+ padding: 6px 14px;
+ color: #586069;
+ border: 1px solid #e1e4e8;
+ border-radius: 3px;
+}
+
+.smw-special-filter-button:hover {
+ background-color: #f6f8fa;
+}
+
+.smw-special-filter-button a, .smw-special-filter-button a:hover, .smw-special-filter-button a:focus, .smw-special-filter-button a:active, .smw-special-filter-button a:visited {
+ font-weight: 300;
+ line-height: 20px;
+ text-decoration: none;
+ color: #586069;
+}
+
+/**
+ * Tabbed admin
+ */
+.smw-admin #tab-general:checked ~ #tab-content-general,
+.smw-admin #tab-supplement:checked ~ #tab-content-supplement,
+.smw-admin #tab-rebuild:checked ~ #tab-content-rebuild,
+.smw-admin #tab-registry:checked ~ #tab-content-registry,
+.smw-admin #tab-notices:checked ~ #tab-content-notices {
+ display: block;
+}
+
+.smw-admin section {
+ border: 0px solid #aaa;
+ padding: 5px 0 0 0 !important;
+}
+
+.smw-tabs.smw-admin label.nav-label {
+ padding: 8px 20px;
+}
+
+.smw-admin input.nav-tab:checked + label.nav-label {
+ border: 1px solid #aaa;
+ border-top: 2px solid orange;
+ border-bottom: 1px solid #fff;
+}
+
+.smw-admin input.nav-tab:checked + label.nav-label.smw-tab-warning {
+ border-top: 2px solid rgb(204, 0, 0);
+}
+
+/**
+ * Responsive settings
+ */
+@media screen and (max-width: 800px) {
+ .smw-special-pageproperty-input {
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/README.md b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/README.md
new file mode 100644
index 00000000..ef74349b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/README.md
@@ -0,0 +1,66 @@
+
+## Register an input context
+
+Adding a input context can be done fairly easy using something like:
+
+<pre>
+// Ensures that the module is loaded before trying to access an
+// instance.
+mw.loader.using( [ 'ext.smw.suggester' ], function() {
+
+ var context = $( '#someElement > input' );
+
+ if ( context.length ) {
+
+ var entitySuggester = smw.Factory.newEntitySuggester(
+ context
+ );
+
+ // Register default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+ };
+} );
+</pre>
+
+## Register an additional token
+
+It may be desired to define additional tokens that active a suggestion request.
+
+<pre>
+mw.loader.using( [ 'ext.smw.suggester' ], function() {
+
+ var context = $( '#someElement > input' );
+
+ if ( context.length ) {
+
+ var entitySuggester = smw.Factory.newEntitySuggester(
+ context
+ );
+
+ // Register default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+
+ entitySuggester.registerTokenDefinition(
+ 'property',
+ {
+ token: '?p:',
+ beforeInsert: function( token, value ) {
+ return value.replace( 'p:', '' );
+ }
+ }
+ );
+ };
+} );
+</pre> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.js
new file mode 100644
index 00000000..fe64e63b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.js
@@ -0,0 +1,259 @@
+/*!
+ * This file is part of the Semantic MediaWiki
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+/* global jQuery, mediaWiki, semanticMediaWiki */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * @since 3.0
+ * @class
+ */
+ smw.suggester = smw.suggester || {};
+
+ /**
+ * Class constructor
+ *
+ * @since 3.0
+ *
+ * @class
+ * @constructor
+ */
+ smw.suggester = function ( context ) {
+
+ var localizedName = mw.config.get( 'smw-config' ).namespaces.localizedName;
+ this.context = context;
+
+ this.category = localizedName[mw.config.get( 'wgNamespaceIds' ).category];
+ this.concept = localizedName[mw.config.get( 'wgNamespaceIds' ).concept];
+
+ this.currentRequest = null;
+ this.tempCache = [];
+
+ return this;
+ };
+
+ /* Public methods */
+
+ var fn = {
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @return {Object}
+ */
+ getDefaultTokenDefinitions: function() {
+
+ var that = this;
+
+ return {
+ property: {
+ token: '[[p:',
+ beforeInsert: function( token, value ) {
+ return value.replace( 'p:', '' ) + '::';
+ }
+ },
+ category: {
+ token: '[[c:',
+ beforeInsert: function( token, value ) {
+ return '[[' + that.category + ':' + value.replace( token, '' ) + ']]';
+ }
+ },
+ concept: {
+ token: '[[con:',
+ beforeInsert: function( token, value ) {
+ return '[[' + that.concept + ':' + value.replace( token, '' ) + ']]';
+ }
+ }
+ };
+ },
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ * @param {Sting} cacheKey
+ * @param {Sting} term
+ * @param {Object} callback
+ *
+ * @return {Object}
+ */
+ search: function( key, cacheKey, term, limit, callback ) {
+
+ var that = this;
+
+ var data = {
+ 'action': 'smwbrowse',
+ 'format': 'json',
+ 'browse': key,
+ 'params': JSON.stringify( {
+ "search": term,
+ "limit": limit
+ } )
+ };
+
+ // Abort any active request handle
+ if ( that.currentRequest !== null ) {
+ that.currentRequest.abort();
+ }
+
+ that.currentRequest = $.ajax( {
+ url: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ data: data,
+ 'success': function( response ) {
+
+ that.tempCache[cacheKey] = $.map( response.query, function( item, key ) {
+ return { id: item.label, name: item.label };
+ } );
+
+ that.currentRequest = null;
+ that.context.removeClass( 'is-disabled' );
+
+ return callback( that.tempCache[cacheKey] );
+ }
+ } );
+
+ return callback( [] );
+ },
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Array} list
+ */
+ registerDefaultTokenList: function( list ) {
+
+ var that = this,
+ defaultTokenDefinitions = that.getDefaultTokenDefinitions();
+
+ list.forEach( function( key ) {
+ if ( defaultTokenDefinitions.hasOwnProperty( key ) ) {
+ that.registerTokenDefinition( key, defaultTokenDefinitions[key] );
+ }
+ } );
+ },
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {String} key
+ * @param {Object} definition
+ */
+ registerTokenDefinition: function( key, definition ) {
+ this.register( key, definition.token, definition.beforeInsert );
+ },
+
+ /**
+ * @since 3.0
+ * @method
+ *
+ * @param {Sting} key
+ * @param {Sting} token
+ * @param {Object} beforeInsert
+ */
+ register: function( key, token, beforeInsert ) {
+
+ var that = this,
+ limit = 20;
+
+ // Default atwho options
+ var options = {
+ at: token,
+ spaceSelectsMatch: false,
+ startWithSpace: false,
+ lookUpOnClick: false,
+ acceptSpaceBar: true,
+ hideWithoutSuffix: false,
+ displayTimeout: 300,
+ suffix: '',
+ limit: limit,
+ callbacks: {}
+ };
+
+ // https://github.com/ichord/At.js/wiki/How-to-use-remoteFilter
+ options.callbacks.remoteFilter = function ( term, callback ) {
+
+ var cacheKey = token + term;
+
+ if ( term == null || term.length < 2 ) {
+ return callback( [] );
+ }
+
+ that.context.addClass( 'is-disabled' );
+
+ if( typeof that.tempCache[cacheKey] == "object" ) {
+ that.context.removeClass( 'is-disabled' );
+ return callback( that.tempCache[cacheKey] );
+ }
+
+ return that.search( key, cacheKey, term, limit, callback );
+ };
+
+ /**
+ * Sort data
+ *
+ * @param query [String] matched string
+ * @param items [Array] data that was refactored
+ * @param searchKey [String] at char to search
+ *
+ * @return [Array] sorted data
+ */
+ options.callbacks.sorter = function ( query, items, searchKey ) {
+ // https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value-in-javascript
+ return items.sort( function( a, b ) {
+ return ( a.id > b.id ) ? 1 : ( ( b.id > a.id ) ? -1 : 0 );
+ } );
+ };
+
+ // https://github.com/ichord/At.js/wiki/Callbacks
+ options.callbacks.beforeInsert = function ( value, $li ) {
+ return beforeInsert( token, value );
+ };
+
+ // https://github.com/ichord/At.js
+ this.context.atwho( options );
+ }
+ };
+
+ /**
+ * Factory
+ * @since 3.0
+ */
+ var Factory = {
+ newEntitySuggester: function( $context ) {
+ return new smw.suggester( $context );
+ }
+ };
+
+ smw.suggester.prototype = fn;
+
+ smw.Factory = smw.Factory || {};
+ smw.Factory = $.extend( smw.Factory, Factory );
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.textInput.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.textInput.js
new file mode 100644
index 00000000..74b0c9c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/suggester/ext.smw.suggester.textInput.js
@@ -0,0 +1,206 @@
+/*!
+ * This file is part of the Semantic MediaWiki
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Only when the registered marker are used will the search be activated
+ * to avoid arbitrary matches for non-semantic content.
+ */
+ mw.loader.using( [ 'ext.smw.suggester' ], function() {
+
+ /**
+ * Support text input on Special:Search
+ *
+ * @since 3.0
+ */
+ var search = function() {
+
+ var context = $( '#searchText > input' ),
+ isHidden = false;
+
+ if ( context.length ) {
+
+ // This features is only enabled for SMWSearch hence when the input
+ // field contains [[ ... ]] we assume a search via the QueryEngine
+ // therefore disable the standard highlighlter as no meaningfull
+ // input help will be shown
+ context.on( 'keyup keypres mouseenter', function( e ) {
+
+ // MW 1.27 - MW 1.31
+ var highlighter = context.parent().find( '.oo-ui-widget' );
+
+ // MW 1.32+
+ if ( highlighter.length == 0 ) {
+ highlighter = $( '.oo-ui-defaultOverlay > .oo-ui-widget' );
+ };
+
+ // Disable (hide) the MW's search input highlighter
+ if ( context.val().search( /\[|\[\[|in:|not:|has:|phrase:|::/gi ) > -1 ) {
+ highlighter.hide();
+ isHidden = true;
+ } else if( isHidden ) {
+ isHidden = false;
+ highlighter.show();
+ };
+ } );
+
+ var entitySuggester = smw.Factory.newEntitySuggester(
+ context
+ );
+
+ // Register default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+
+ entitySuggester.registerTokenDefinition(
+ 'property',
+ {
+ token: 'has:?',
+ beforeInsert: function( token, value ) {
+ return value.replace( '?', '' );
+ }
+ }
+ );
+ };
+ };
+
+ /**
+ * Support text input on Special:Upload
+ *
+ * @since 3.0
+ */
+ var upload = function() {
+
+ var context = $( '#wpUploadDescription' );
+
+ if ( context.length ) {
+
+ var entitySuggester= smw.Factory.newEntitySuggester(
+ context
+ );
+
+ // Register autocomplete default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+ };
+ };
+
+ /**
+ * Support text input on the wikiEditor textbox
+ *
+ * @since 3.0
+ */
+ var wikiEditor = function() {
+
+ var wpTextbox1 = $( '#wpTextbox1' );
+
+ // Attach to the textarea
+ if ( wpTextbox1.length ) {
+
+ var entitySuggester = smw.Factory.newEntitySuggester(
+ wpTextbox1
+ );
+
+ // Register default tokens
+ entitySuggester.registerDefaultTokenList(
+ [
+ 'property',
+ 'concept',
+ 'category'
+ ]
+ );
+
+ // Register additional definition since the editing can involve
+ // different patterns
+
+ // Used in combination with printouts as in:
+ //
+ // {{#ask: ..
+ // |?p: ...
+ // }}
+ entitySuggester.registerTokenDefinition(
+ 'property',
+ {
+ token: '?p:',
+ beforeInsert: function( token, value ) {
+ return value.replace( 'p:', '' );
+ }
+ }
+ );
+
+ // Used in combination with #set and #subobject as in:
+ //
+ // {{#set:
+ // |p: ...
+ // }}
+ entitySuggester.registerTokenDefinition(
+ 'property',
+ {
+ token: '|p:',
+ beforeInsert: function( token, value ) {
+ return value.replace( 'p:', '' ) + '=';
+ }
+ }
+ );
+ };
+ };
+
+ function load( callback ) {
+ if ( document.readyState == 'complete' ) {
+ callback();
+ } else {
+ window.addEventListener( 'load', callback );
+ }
+ }
+
+ // Only load when it is Special:Search and the search type supports
+ // https://www.semantic-mediawiki.org/wiki/Help:SMWSearch
+ if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == 'Search' && mw.config.get( 'wgSearchType' ) == 'SMWSearch' ) {
+ load( search );
+ };
+
+ if ( mw.config.get( 'wgCanonicalSpecialPageName' ) == 'Upload' ) {
+ load( upload );
+ };
+
+ var wgAction = mw.config.get( 'wgAction' );
+
+ if ( ( wgAction == 'edit' || wgAction == 'submit' ) && mw.config.get( 'wgPageContentModel' ) == 'wikitext' ) {
+ load( wikiEditor );
+ };
+ } );
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.css
new file mode 100644
index 00000000..f6e38c8d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.css
@@ -0,0 +1,133 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @see https://www.w3schools.com/howto/howto_css_modals.asp
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+.smw-modal {
+ display: none; /* Hidden by default */
+ position: absolute; /* Stay in place */
+ z-index: 1; /* Sit on top */
+ left: 0;
+ top: -1px;
+ width: 100%; /* Full width */
+ height: 100%; /* Full height */
+ overflow: auto; /* Enable scroll if needed */
+ background-color: rgb(0,0,0); /* Fallback color */
+ background-color: rgba(0,0,0,0.23); /* Black w*/
+}
+
+.smw-modal-title {
+ color: #333333;
+ vertical-align: -0.25em;
+ font-size: 1.2em;
+}
+
+/* smw-modal Content */
+.smw-modal-content {
+ background-color: #fefefe;
+ margin: auto;
+ padding: 20px;
+ border: 1px solid #888;
+ width: 80%;
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
+ -webkit-animation-name: animatetop;
+ -webkit-animation-duration: 0.4s;
+ animation-name: animatetop;
+ animation-duration: 0.4s
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(27,31,35,0.25);
+ border-radius: 3px;
+ box-shadow: 0 0 18px rgba(27,31,35,0.4);
+ margin-top:50px;
+}
+
+/* Add Animation */
+@-webkit-keyframes animatetop {
+ from {top:-300px; opacity:0}
+ to {top:0; opacity:1}
+}
+
+@keyframes animatetop {
+ from {top:-300px; opacity:0}
+ to {top:0; opacity:1}
+}
+
+/* The Close Button */
+.smw-modal-close {
+ float: right;
+ font-size: 20px;
+ cursor: pointer;
+ background-color: transparent;
+ border: 0;
+ opacity: 0.25;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.smw-modal-close:hover,
+.smw-modal-close:focus {
+ color: #000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 1;
+}
+
+.smw-modal-header {
+ padding: 15px;
+ margin: -20px -20px 15px;
+ font-weight: normal;
+ border-bottom: 1px solid #e1e4e8;
+ background-color: #f6f8fa;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+
+.smw-modal-header h2 {
+ display: block;
+ font-size: 1.5em;
+ margin-bottom: 0.2em;
+ -webkit-margin-before: 0.83em;
+ -webkit-margin-after: 0.83em;
+ -webkit-margin-start: 0px;
+ -webkit-margin-end: 0px;
+ font-weight: bold;
+ border-bottom: 0px solid #a2a9b1;
+ color: #fff;
+}
+
+.smw-modal-body {
+}
+
+.smw-modal-footer {
+ display: none;
+ padding: 2px 16px;
+ background-color: #5cb85c;
+ color: white;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.js
new file mode 100644
index 00000000..017504f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.modal.js
@@ -0,0 +1,63 @@
+/**
+ * @see https://www.w3schools.com/howto/howto_css_modals.asp
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ $( document ).ready( function() {
+
+ $( '.smw-modal-link' ).removeClass( 'is-disabled' );
+
+ // Iterate over available nav links onClick
+ $( '.smw-modal-link' ).on( "click", function( event ) {
+
+ var that = this;
+
+ // Avoid visual disruption on the parameter list (uses a fading field
+ // display with the help of an ::after element) and override the element
+ // for the time the modal window is displayed
+ var parameterList = $( '.options-parameter-list' );
+ parameterList.removeClass( 'options-parameter-list' );
+ parameterList.addClass( 'options-parameter-list-plain' );
+
+ // Find the content ID
+ var id = $( this ).data( 'id' );
+
+ // Get the modal
+ var modal = document.getElementById( id );
+
+ // When the user clicks the button, open the modal
+ modal.style.display = "block";
+
+ $( '#' + id + ' .smw-modal-close' ).on( "click", function( event ) {
+ modal.style.display = "none";
+ parameterList.removeClass( 'options-parameter-list-plain' );
+ parameterList.addClass( 'options-parameter-list' );
+ } );
+
+ // When the user clicks anywhere outside of the modal, close it
+ window.onclick = function( e ) {
+
+ var isClickInside = that.contains( e.target );
+
+ if ( e.target == modal || ( isClickInside === false && modal.contains( e.target ) === false ) ) {
+ modal.style.display = "none";
+ parameterList.removeClass( 'options-parameter-list-plain' );
+ parameterList.addClass( 'options-parameter-list' );
+ }
+ }
+
+ event.preventDefault();
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.personal.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.personal.js
new file mode 100644
index 00000000..d5e6f955
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.personal.js
@@ -0,0 +1,44 @@
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ $( document ).ready( function() {
+
+ $( '.smw-personal-jobqueue-watchlist' ).removeClass( 'is-disabled' );
+
+ // Iterate over available nav links onClick
+ $( '.smw-personal-jobqueue-watchlist' ).each( function() {
+
+ var watchlist = mw.config.get( 'smwgJobQueueWatchlist' );
+ var text = '';
+
+ for ( var prop in watchlist ) {
+ if ( watchlist.hasOwnProperty( prop ) ) {
+ text = text + '<tr><td>' + prop + '</td><td>&nbsp;</td><td><span class="item-count active">' + watchlist[prop] + '</span></td></tr>';
+ }
+ }
+
+ if ( text !== '' ) {
+ text = '<table class="smw-personal-table"><tbody>' + text + '</tbody></table>';
+
+ var tooltip = smw.Factory.newTooltip();
+ tooltip.show ( {
+ context: $( this ),
+ title: mw.msg( 'smw-personal-jobqueue-watchlist' ),
+ type: 'inline',
+ content: text
+ } );
+ };
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.js
new file mode 100644
index 00000000..a3f20c73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.js
@@ -0,0 +1,157 @@
+/**
+ * JavaScript for SMW autocomplete functionality
+ *
+ * @see https://www.mediawiki.org/wiki/Extension:Semantic_MediaWiki
+ *
+ * @since 1.8
+ * @release 0.1
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+/*global mediaWiki:true*/
+( function( $, mw ) {
+ 'use strict';
+
+ /**
+ * Default options
+ *
+ */
+ var defaults = {
+ limit: 10,
+ separator: null,
+ search: 'property',
+ namespace: mw.config.get( 'wgNamespaceIds' ).property
+ };
+
+ /**
+ * Handle autocomplete function for various instances
+ *
+ * @var options
+ *
+ * @since: 1.8
+ */
+ $.fn.smwAutocomplete = function( settings ){
+
+ // Merge defaults and options
+ var options = $.extend( {}, defaults, settings );
+
+ // Specify regular expression
+ var regex = new RegExp( options.separator , 'mi' );
+
+ // Helper functions
+ function split( val ) {
+ return val.split( regex );
+ }
+
+ function extractLast( term ) {
+ return split( term ).pop();
+ }
+
+ function escapeQuestion(term){
+ if ( term.substring(0, 1) === "?" ) {
+ return term.substring(1);
+ } else {
+ return term;
+ }
+ }
+
+ // MW 1.24 changed ui versions
+ var version = $.ui ? parseFloat( $.ui.version.substring( 2 ) ) : 1;
+
+ if ( version >= 9 ) {
+ // Extending jQuery functions for custom highligting
+ $.ui.autocomplete( 'instance' )._renderItem = function( ul, item ) {
+ var term_without_q = escapeQuestion(extractLast( this.term ));
+ var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term_without_q.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi");
+ var loc = item.label.search(re),
+ t = '';
+ if (loc >= 0) {
+ t = item.label.substr(0, loc) + '<strong>' + item.label.substr(loc, term_without_q.length) + '</strong>' + item.label.substr(loc + term_without_q.length);
+ } else {
+ t = item.label;
+ }
+ return $( "<li>" ).append( "<a>" + t + "</a>" ).appendTo( ul );
+ };
+
+ } else{
+ // Extending jQuery functions for custom highligting
+ $.ui.autocomplete.prototype._renderItem = function( ul, item ) {
+ var term_without_q = escapeQuestion(extractLast( this.term ));
+ var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term_without_q.replace("/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi", "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi");
+ var loc = item.label.search(re),
+ t = '';
+ if (loc >= 0) {
+ t = item.label.substr(0, loc) + '<strong>' + item.label.substr(loc, term_without_q.length) + '</strong>' + item.label.substr(loc + term_without_q.length);
+ } else {
+ t = item.label;
+ }
+ $( "<li></li>" )
+ .data( "item.autocomplete", item )
+ .append( " <a>" + t + "</a>" )
+ .appendTo( ul );
+ };
+ }
+
+ // Extending jquery functions for custom autocomplete matching
+ $.extend( $.ui.autocomplete, {
+ filter: function(array, term) {
+ var matcher = new RegExp( "\\\b" + $.ui.autocomplete.escapeRegex(term), "i" );
+ return $.grep( array, function(value) {
+ return matcher.test( value.label || value.value || value );
+ });
+ }
+ } );
+
+ // Autocomplete core
+ this.autocomplete( {
+ minLength: 2,
+ source: function(request, response) {
+ $.getJSON(
+ mw.config.get( 'wgScriptPath' ) + '/api.php',
+ {
+ 'action': 'opensearch',
+ 'format': 'json',
+ 'limit': options.limit,
+ 'namespace': options.namespace ,
+ 'search': extractLast( request.term )
+ },
+ function( data ){
+
+ if ( data.error === undefined ) {
+ //remove the word 'Property:' from returned data
+ if ( options.search === 'property' ){
+ for( var i=0; i < data[1].length; i++ ) {
+ data[1][i]= data[1][i].substr( data[1][i].indexOf( ':' ) + 1 );
+ }
+ }
+ response( data[1] );
+ } else {
+ response ( false );
+ }
+ }
+ );
+ },
+ focus: function() {
+ // prevent value inserted on focus
+ return false;
+ },
+ select: function( event, ui ) {
+ var terms = this.value;
+ terms = split( terms );
+ // remove the current input
+ terms.pop();
+ // add the selected item
+ terms.push( ui.item.value );
+ // add placeholder to get the comma-and-space at the end
+ terms.push("");
+ this.value = terms.join( options.separator !== null ? options.separator : '' );
+ return false;
+ }
+ } );
+ };
+
+} )( jQuery, mediaWiki ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.page.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.page.js
new file mode 100644
index 00000000..efcdc0eb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.page.js
@@ -0,0 +1,107 @@
+/**
+ * JavaScript for property autocomplete function
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+
+( function( $, mw ) {
+ 'use strict';
+
+ var autocomplete = function( context ) {
+
+ var limit = 20;
+
+ var indicator = context.hasClass( 'autocomplete-arrow' );
+ context.removeClass( 'is-disabled' );
+
+ // https://github.com/devbridge/jQuery-Autocomplete
+ context.autocomplete( {
+ serviceUrl: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ minChars: 3,
+ maxHeight: 150,
+ paramName: 'search',
+ delimiter: "\n",
+ noCache: false,
+ triggerSelectOnValidInput: false,
+ params: {
+ 'action': 'smwbrowse',
+ 'format': 'json',
+ 'browse': 'page',
+ 'params': {
+ search: '',
+ limit: limit,
+ fullText: true
+ }
+ },
+ onSearchStart: function( query ) {
+
+ // Avoid a search request on options or invalid characters
+ if ( query.search.indexOf( '#' ) > 0 || query.search.indexOf( '|' ) > 0 ) {
+ return false;
+ };
+
+ context.removeClass( 'autocomplete-arrow' );
+ context.addClass( 'is-disabled' );
+
+ if ( indicator ) {
+ context.addClass( 'autocomplete-loading' );
+ };
+
+ query.params = JSON.stringify( {
+ search: query.search.replace( "?", '' ),
+ limit: limit,
+ fullText: true
+ } );
+
+ // Avoids {"warnings":{"main":{"*":"Unrecognized parameter: search."}
+ delete query.search;
+ },
+ onSearchComplete: function( query ) {
+ context.removeClass( 'is-disabled' );
+
+ if ( indicator ) {
+ context.removeClass( 'autocomplete-loading' );
+ context.addClass( 'autocomplete-arrow' );
+ };
+ },
+ transformResult: function( response ) {
+
+ if ( !response.hasOwnProperty( 'query' ) ) {
+ return { suggestions: [] };
+ };
+
+ return {
+ suggestions: $.map( response.query, function( val, key ) {
+ return { value: val.fullText, data: key };
+ } )
+ };
+ }
+ } );
+
+ // https://github.com/devbridge/jQuery-Autocomplete/issues/498
+ context.off( 'focus.autocomplete' );
+ }
+
+ mw.hook( 'smw.page.autocomplete' ).add( function( context ) {
+ context.find( '.smw-page-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+ } );
+
+ $ ( document ).on( 'smw.page.autocomplete', function( event, opts ) {
+ opts.context.find( '.smw-page-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+ } );
+
+ $( document ).ready( function() {
+ $( '#smw-page-input, .smw-page-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.property.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.property.js
new file mode 100644
index 00000000..7551a01b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.property.js
@@ -0,0 +1,127 @@
+/**
+ * JavaScript for property autocomplete function
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+
+( function( $, mw ) {
+ 'use strict';
+
+ var autocomplete = function( context ) {
+
+ var limit = 20;
+ var currentValue = context.val();
+
+ var indicator = context.hasClass( 'autocomplete-arrow' );
+ context.removeClass( 'is-disabled' );
+
+ // https://github.com/devbridge/jQuery-Autocomplete
+ context.autocomplete( {
+ serviceUrl: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ minChars: 3,
+ maxHeight: 150,
+ paramName: 'search',
+ delimiter: "\n",
+ noCache: false,
+ triggerSelectOnValidInput: false,
+ params: {
+ 'action': 'smwbrowse',
+ 'format': 'json',
+ 'browse': 'property',
+ 'params': {
+ "search": '',
+ "limit": limit
+ }
+ },
+ onSearchStart: function( query ) {
+
+ // Avoid a search request on options or invalid characters
+ if (
+ query.search.indexOf( '#' ) > -1 ||
+ query.search.indexOf( '|' ) > -1 ) {
+ return false;
+ };
+
+ // Avoid a request for when the search term on the current
+ // selected value are the same
+ if ( currentValue !== '' && currentValue === query.search ) {
+ return false;
+ };
+
+ context.removeClass( 'autocomplete-arrow' );
+ context.addClass( 'is-disabled' );
+
+ if ( indicator ) {
+ context.addClass( 'autocomplete-loading' );
+ };
+
+ query.params = JSON.stringify( {
+ 'search': query.search.replace( "?", '' ),
+ 'limit': limit
+ } );
+
+ // Avoids {"warnings":{"main":{"*":"Unrecognized parameter: search."}
+ // from the API request
+ delete query.search;
+ },
+ onSelect: function( suggestion ) {
+
+ if ( suggestion ) {
+ currentValue = suggestion.value;
+ };
+
+ context.trigger( 'smw.autocomplete.property.select.complete', {
+ suggestion: suggestion,
+ context: context
+ } );
+ },
+ onSearchComplete: function( query ) {
+ context.removeClass( 'is-disabled' );
+
+ if ( indicator ) {
+ context.removeClass( 'autocomplete-loading' );
+ context.addClass( 'autocomplete-arrow' );
+ };
+ },
+ transformResult: function( response ) {
+
+ if ( !response.hasOwnProperty( 'query' ) ) {
+ return { suggestions: [] };
+ };
+
+ return {
+ suggestions: $.map( response.query, function( dataItem, key ) {
+ return { value: dataItem.label, data: key };
+ } )
+ };
+ }
+ } );
+
+ // https://github.com/devbridge/jQuery-Autocomplete/issues/498
+ context.off( 'focus.autocomplete' );
+ }
+
+ // Listen to an event (see Special:Ask)
+ $ ( document ).on( 'SMW::Property::Autocomplete', function( event, opts ) {
+ autocomplete( opts.context.find( '.smw-property-input' ) );
+ } );
+
+ // Listen to any event that requires a value autocomplete
+ // The trigger needs to set { context: ... } so we isolate the processing
+ // to a specific instance
+ $ ( document ).on( 'smw.autocomplete.property', function( event, opts ) {
+ autocomplete( opts.context.find( '.smw-property-input' ) );
+ } );
+
+ $( document ).ready( function() {
+ $( '#smw-property-input, .smw-property-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertysubject.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertysubject.js
new file mode 100644
index 00000000..eabd134e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertysubject.js
@@ -0,0 +1,128 @@
+/**
+ * JavaScript for property value autocomplete function
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw ) {
+ 'use strict';
+
+ var autocomplete = function( context ) {
+
+ // Keep the list small to minimize straining the DB
+ var limit = 10;
+ var currentValue = context.val();
+
+ // There is no reason for the field to be enabled as long as their is no
+ // property
+ if ( context.data( 'property' ) === '' || context.data( 'property' ) === undefined ) {
+ return context.addClass( 'is-disabled' );;
+ };
+
+ context.removeClass( 'is-disabled' );
+
+ var params = {
+ 'action': 'smwbrowse',
+ 'format': 'json',
+ 'browse': 'psubject',
+ 'params': {
+ "search": '',
+ 'property': context.data( 'property' ),
+ 'value': context.data( 'value' ),
+ 'title-prefix': context.data( 'title-prefix' ),
+ "limit": limit
+ }
+ };
+
+ // https://github.com/devbridge/jQuery-Autocomplete
+ context.autocomplete( {
+ serviceUrl: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ minChars: 0,
+ maxHeight: 150,
+ paramName: 'search',
+ delimiter: "\n",
+ noCache: false,
+ triggerSelectOnValidInput: false,
+ params: params,
+ onSearchStart: function( query ) {
+
+ // Avoid a search request on options or invalid characters
+ if (
+ query.search.indexOf( '#' ) > -1 ||
+ query.search.indexOf( '|' ) > -1 ) {
+ return false;
+ };
+
+ // Avoid a request for when the search term on the current
+ // selected value are the same
+ if ( currentValue !== '' && currentValue === query.search ) {
+ return false;
+ };
+
+ context.removeClass( 'autocomplete-arrow' );
+ context.addClass( 'is-disabled' );
+ context.addClass( 'autocomplete-loading' );
+
+ query.params = JSON.stringify( {
+ 'search': query.search.replace( "?", '' ),
+ 'property': context.data( 'property' ),
+ 'value': context.data( 'value' ),
+ 'title-prefix': context.data( 'title-prefix' ),
+ 'limit': limit
+ } );
+
+ // Avoids {"warnings":{"main":{"*":"Unrecognized parameter: search."}
+ // from the API request
+ delete query.search;
+ },
+ onSelect: function( suggestion ) {
+
+ if ( suggestion ) {
+ currentValue = suggestion.value;
+ };
+
+ context.trigger( 'smw.autocomplete.propertysubject.select.complete', {
+ suggestion: suggestion,
+ context : context
+ } );
+ },
+ onSearchComplete: function( query ) {
+ context.removeClass( 'is-disabled' );
+ context.removeClass( 'autocomplete-loading' );
+ context.addClass( 'autocomplete-arrow' );
+ },
+ transformResult: function( response ) {
+
+ if ( !response.hasOwnProperty( 'query' ) ) {
+ return { suggestions: [] };
+ };
+
+ return {
+ suggestions: $.map( response.query, function( key ) {
+ return { value: key, data: key };
+ } )
+ };
+ }
+ } );
+
+ // https://github.com/devbridge/jQuery-Autocomplete/issues/498
+ // context.off( 'focus.autocomplete' );
+ }
+
+ // Listen to any event that requires a value autocomplete
+ // The trigger needs to set { context: ... } so we isolate the processing
+ // to a specific instance
+ $ ( document ).on( 'smw.autocomplete.propertysubject', function( event, opts ) {
+ autocomplete( opts.context.find( '.smw-propertysubject-input' ) );
+ } );
+
+ $( document ).ready( function() {
+ $( '.smw-propertysubject-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertyvalue.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertyvalue.js
new file mode 100644
index 00000000..631f84cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.autocomplete.propertyvalue.js
@@ -0,0 +1,134 @@
+/**
+ * JavaScript for property value autocomplete function
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+( function( $, mw ) {
+ 'use strict';
+
+ var autocomplete = function( context ) {
+
+ // Keep the list small to minimize straining the DB
+ var limit = 10;
+ var currentValue = context.val();
+
+ // There is no reason for the field to be enabled as long as their is no
+ // property
+ if ( context.data( 'property' ) === '' || context.data( 'property' ) === undefined ) {
+ return context.addClass( 'is-disabled' );;
+ };
+
+ context.removeClass( 'is-disabled' );
+
+ var params = {
+ 'action': 'smwbrowse',
+ 'format': 'json',
+ 'browse': 'pvalue',
+ 'params': {
+ "search": '',
+ 'property': context.data( 'property' ),
+ "limit": limit
+ }
+ };
+
+ // https://github.com/devbridge/jQuery-Autocomplete
+ context.autocomplete( {
+ serviceUrl: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ minChars: 0,
+ maxHeight: 150,
+ paramName: 'search',
+ delimiter: "\n",
+ noCache: false,
+ triggerSelectOnValidInput: false,
+ params: params,
+ onSearchStart: function( query ) {
+
+ // Avoid a search request on options or invalid characters
+ if (
+ query.search.indexOf( '#' ) > -1 ||
+ query.search.indexOf( '|' ) > -1 ||
+ query.search.indexOf( '*' ) > -1 ||
+ query.search.indexOf( '+' ) > -1 ||
+ query.search.indexOf( '!' ) > -1 ||
+ query.search.indexOf( '>' ) > -1 ||
+ query.search.indexOf( '<' ) > -1 ) {
+ return false;
+ };
+
+ // Avoid a request for when the search term on the current
+ // selected value are the same
+ if ( currentValue !== '' && currentValue === query.search ) {
+ return false;
+ };
+
+ context.removeClass( 'autocomplete-arrow' );
+ context.addClass( 'is-disabled' );
+ context.addClass( 'autocomplete-loading' );
+
+ query.params = JSON.stringify( {
+ 'search': query.search.replace( "?", '' ),
+ 'property': context.data( 'property' ),
+ 'limit': limit
+ } );
+
+ // Avoids {"warnings":{"main":{"*":"Unrecognized parameter: search."}
+ // from the API request
+ delete query.search;
+ },
+ onSelect: function( suggestion ) {
+
+ if ( suggestion ) {
+ currentValue = suggestion.value;
+ };
+
+ context.trigger( 'smw.autocomplete.propertyvalue.select.complete', {
+ suggestion: suggestion,
+ context : context
+ } );
+ },
+ onSearchComplete: function( query ) {
+ context.removeClass( 'is-disabled' );
+ context.removeClass( 'autocomplete-loading' );
+ context.addClass( 'autocomplete-arrow' );
+ },
+ transformResult: function( response ) {
+
+ if ( !response.hasOwnProperty( 'query' ) ) {
+ return { suggestions: [] };
+ };
+
+ return {
+ suggestions: $.map( response.query, function( key ) {
+
+ if ( key === null ) {
+ return [];
+ };
+
+ return { value: key, data: key };
+ } )
+ };
+ }
+ } );
+
+ // https://github.com/devbridge/jQuery-Autocomplete/issues/498
+ // context.off( 'focus.autocomplete' );
+ }
+
+ // Listen to any event that requires a value autocomplete
+ // The trigger needs to set { context: ... } so we isolate the processing
+ // to a specific instance
+ $ ( document ).on( 'smw.autocomplete.propertyvalue', function( event, opts ) {
+ autocomplete( opts.context.find( '.smw-propertyvalue-input' ) );
+ } );
+
+ $( document ).ready( function() {
+ $( '.smw-propertyvalue-input' ).each( function() {
+ autocomplete( $( this ) );
+ } );
+ } );
+
+} )( jQuery, mediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.postproc.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.postproc.js
new file mode 100644
index 00000000..3fed71b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.postproc.js
@@ -0,0 +1,97 @@
+/*!
+ * This file is part of the Semantic MediaWiki Reload module
+ * @see https://www.semantic-mediawiki.org/wiki/Help:Purge
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, smw */
+/*jslint white: true */
+
+( function( $, mw ) {
+
+ 'use strict';
+
+ /**
+ * @since 3.0
+ */
+ mw.loader.using( [ 'mediawiki.api', 'mediawiki.notify' ] ).then( function () {
+
+ $( '.smw-postproc' ).each( function() {
+
+ var api = new mw.Api();
+ var ref = $( this ).data( 'ref' );
+ var query = $( this ).data( 'query' );
+
+ if ( ref !== undefined && ref !== '' ) {
+ mw.notify( mw.msg( 'smw-postproc-queryref' ), { type: 'info', autoHide: false } );
+
+ var params = {
+ 'subject': $( this ).data( 'subject' ),
+ 'origin': 'api-postproc',
+ 'ref' : ref,
+ 'cache-key': $( this ).data( 'cache-key' )
+ };
+
+ var postArgs = {
+ 'action': 'smwtask',
+ 'task': 'update',
+ 'params': JSON.stringify( params )
+ };
+
+ api.postWithToken( 'csrf', postArgs ).then( function ( data ) {
+ location.reload( true );
+ } );
+ } else if ( query !== undefined ) {
+
+ var params = {
+ 'subject': $( this ).data( 'subject' ),
+ 'origin': 'api-postproc',
+ 'query' : query,
+ 'cache-key': $( this ).data( 'cache-key' )
+ };
+
+ var postArgs = {
+ 'action': 'smwtask',
+ 'task': 'check-query',
+ 'params': JSON.stringify( params )
+ };
+
+ api.postWithToken( 'csrf', postArgs ).then( function ( data ) {
+ if ( data.task.hasOwnProperty( 'reload' ) ) {
+ mw.notify( mw.msg( 'smw-postproc-queryref' ), { type: 'info', autoHide: false } );
+ location.reload( true );
+ };
+ } );
+ }
+
+ var jobs = $( this ).data( 'jobs' );
+
+ if ( jobs !== '' ) {
+
+ var params = {
+ 'subject': $( this ).data( 'subject' ),
+ 'origin': 'api-postproc',
+ 'jobs' : jobs
+ };
+
+ var postArgs = {
+ 'action': 'smwtask',
+ 'task': 'run-joblist',
+ 'params': JSON.stringify( params )
+ };
+
+ api.postWithToken( 'csrf', postArgs );
+ };
+
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.purge.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.purge.js
new file mode 100644
index 00000000..52701fd0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.purge.js
@@ -0,0 +1,42 @@
+/*!
+ * This file is part of the Semantic MediaWiki Purge module
+ * @see https://www.semantic-mediawiki.org/wiki/Help:Purge
+ *
+ * @since 2.5
+ * @revision 0.0.1
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author samwilson, mwjames
+ */
+
+/*global jQuery, mediaWiki, smw */
+/*jslint white: true */
+
+( function( $, mw ) {
+
+ 'use strict';
+
+ mw.loader.using( [ 'mediawiki.api', 'mediawiki.notify' ] ).then( function () {
+
+ // JS is loaded, now remove the "soft" disabled functionality
+ $( "#ca-purge" ).removeClass( 'is-disabled' );
+
+ // Observed on the chameleon skin
+ $( "#ca-purge a" ).removeClass( 'is-disabled' );
+
+ $( "#ca-purge a" ).on( 'click', function ( e ) {
+ var postArgs = { action: 'purge', titles: mw.config.get( 'wgPageName' ) };
+ new mw.Api().post( postArgs ).then( function () {
+ location.reload();
+ }, function () {
+ mw.notify( mw.msg( 'smw-purge-failed' ), { type: 'error' } );
+ } );
+ e.preventDefault();
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.css
new file mode 100644
index 00000000..a9eefd25
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.css
@@ -0,0 +1,124 @@
+/*!
+ * This file is part of the Semantic MediaWiki Tooltip/Highlighter module
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.8
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/* Tooltips, style for content of the bubble */
+div.smwtt {
+ color: #000000;
+}
+
+/* Tooltips, show persistent tooltips for non-JavaScript clients */
+span.smwttpersist span.smwttcontent {
+ color: #888888;
+ font-style: italic;
+ font-size: 90%;
+}
+
+/* Tooltips, hide inline tooltips for non-JavaScript clients */
+span.smwttinline span.smwttcontent {
+ display: none;
+ speak: none;
+}
+
+/* Tooltips, style for image anchor for persistent tooltips */
+span.smwtticon {
+ display: none;
+}
+
+/* Tooltips, colored anchors? */
+span.smwttactivepersist {
+ cursor: help;
+ color: #0000C8;
+}
+
+/* Tooltips, colored anchors */
+span.smwttactiveinline {
+ color: #BB7700;
+ text-decoration: none;
+}
+
+/* Tooltips, images for tooltip icons */
+img.smwttimg {
+ padding-right: 5px;
+ padding-left: 4px;
+}
+
+/* New tooltip content is always hidden */
+.smwttcontent {
+ display:none;
+}
+
+/* New tooltip icon defaults */
+.smwtticon {
+ padding: 10px 10px 0 0;
+ white-space:nowrap;
+ margin-left: 2px;
+ margin-right: 2px;
+ vertical-align: -0.05em;
+}
+
+/* New tooltip, Individual assigned icons ( inline-block is important because the icon <span> is empty) */
+.smwtticon.info {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjtJREFUeNrEl90rZHEYx88Z1Ma42JCX2dj2QlmlRUoujLjaKLlwsVIuJNn9ExSbi0GScoHEBW64keLGxW4zXCGUFSUvkYzduVjbmiWG2e/DQwcz5/zO2D2/pz7NnDPnPN/v/N4ftdR9qQhGDCgAxSAXvALx/NspOASrYB4sgguRpNECzySBelALsg2erebPTTAGhsEPvRdsBgkbwQpoFxDXRhZw8btNkRhIAJNggJs60nCAPjAFEkUNpIFZUKWX2ZmkKl+c0Te0vI0yMlLJOR1GBuifT4N8vWypL9QHomSm4Y1Rbyp5nDsxnAH6PsIP6kb+S/XJvUy7KtIlNHtGtbpaAzRYykWyLP8MPrl3dB4UHRfvwafHBpLBZ9EMXoi1bVwpvwO31x5fUBnavTYzOFtBinYdaAo3SsMFiXp8gUhnB421j6CFWiAW1CnWB2nGUQsUgddm36bpp42erWtlxmuqG9JJm1qgVJEXZTaeGrLinS3U6mRhOMiAXaKBeDKgSjRwsxD5Jer7yYBXooFjMrAm0cAaGXBLNOAmA3PUFBLEv9OWQgZ+gXEJBibAyd123GvxbPCz5v15YA90WWigG+w8PhF1ggULxJdAR6gj2TmoAfv/UfwAfABn4SqjXVABZkCG7j7qCUQiXnHX9Hp1wTrl/8fdscA5v4lWRjt8UHE9c3b4OQfl2jZbG/4BzaAQDAKfCWEqSPu5mm7mXCFDNVGe0zG6hAohkMPXMfzbJa9stK94wFe+Noy/AgwAS/d56gvLz80AAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.smwtticon.question {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuxJREFUeNq8l01oE0EUx2fqFqp20VKLxmoRxaIVikZE8WClBUFaEA9CFcGDiERPPdRLoYqH+IUXDyoWTyLUi4gK4kFJ8FSlEXJQ0abFKEStoJKqxaab/l+YLdvdnd2ZtfTBj2Rn533szJs3M7w9NcUUpRrsAHvANrAGmOLdBPgMXoMX4CX4p2LUUOjTAI6Do2BLSN9D4vctuANug29BClUhBk+CDLig4Nwpm0FS6CaiBFAP7oObYqijSiO4Dh6AFaoBrAZPwUE2f3JA2GwMywH68kcgLrPUbHIWX87ZifVzY8/8KLNhMPjJkqnGhe194LvdyB2rgCw+BJ0yC+S0e21w2hQmy+z8G4u9L5ZlXZ6ALmC5pyAR5LynOdw5SayGs8utiyq/EtkPTrtzYCU4J9Noa+CsK1alPOEmJra7iQd1OQtWOQNIyLKUxM/5wKjFOtKlConMtGfISceUVxnKtVN2AEvAsaCviddxj3NnspFzmnfPdCwOHAXyuZQC2A3WyXrVGl4j6XFvgk2UyrpLs4l80yC1h2U1DXOYdLqmqVhiQSvBlg5DbCz/JX7LczBvqahuNfyqk45cwZJz5wh9eUBBmlOqKezaqM77W7zOqSKeyU7rrFjGozinZUb1we28V935bCH6HSUAd6GhYdd1Tr4pgIKuFm1I7lI7MGZF+Y4vFEBWVytW422j4Y8gWQogpatluooT1YqIkqLteBn+vLM3hwWUr2ATjcAvqhts4eUe+GmXr2u6q4Eq37M2owKdFXSzX/ic3Y7HqKjp1ADnkYyeVQ4rDrkKcu4T0SUwpKK90fS2ba9TrmevwEW/U/EkOAI+hln4UPS2Dastwzw4DP7KjuWj4sAYGMTjglU5lDifFTafvLCdczZyyd1wA7gLds5Txg+Jq92I6s0oJw4qyah7hSPbk8LWiO7d8A/oA7vALTCu4ZgupDfEbbpP2PIVrnE9p0q5l07poFU8V4t3U6Ky0b6SBs/Fc6jMCDAA9GbPN7lxrCwAAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.smwtticon.service {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuxJREFUeNq8l01oE0EUx2fqFqp20VKLxmoRxaIVikZE8WClBUFaEA9CFcGDiERPPdRLoYqH+IUXDyoWTyLUi4gK4kFJ8FSlEXJQ0abFKEStoJKqxaab/l+YLdvdnd2ZtfTBj2Rn533szJs3M7w9NcUUpRrsAHvANrAGmOLdBPgMXoMX4CX4p2LUUOjTAI6Do2BLSN9D4vctuANug29BClUhBk+CDLig4Nwpm0FS6CaiBFAP7oObYqijSiO4Dh6AFaoBrAZPwUE2f3JA2GwMywH68kcgLrPUbHIWX87ZifVzY8/8KLNhMPjJkqnGhe194LvdyB2rgCw+BJ0yC+S0e21w2hQmy+z8G4u9L5ZlXZ6ALmC5pyAR5LynOdw5SayGs8utiyq/EtkPTrtzYCU4J9Noa+CsK1alPOEmJra7iQd1OQtWOQNIyLKUxM/5wKjFOtKlConMtGfISceUVxnKtVN2AEvAsaCviddxj3NnspFzmnfPdCwOHAXyuZQC2A3WyXrVGl4j6XFvgk2UyrpLs4l80yC1h2U1DXOYdLqmqVhiQSvBlg5DbCz/JX7LczBvqahuNfyqk45cwZJz5wh9eUBBmlOqKezaqM77W7zOqSKeyU7rrFjGozinZUb1we28V935bCH6HSUAd6GhYdd1Tr4pgIKuFm1I7lI7MGZF+Y4vFEBWVytW422j4Y8gWQogpatluooT1YqIkqLteBn+vLM3hwWUr2ATjcAvqhts4eUe+GmXr2u6q4Eq37M2owKdFXSzX/ic3Y7HqKjp1ADnkYyeVQ4rDrkKcu4T0SUwpKK90fS2ba9TrmevwEW/U/EkOAI+hln4UPS2Dastwzw4DP7KjuWj4sAYGMTjglU5lDifFTafvLCdczZyyd1wA7gLds5Txg+Jq92I6s0oJw4qyah7hSPbk8LWiO7d8A/oA7vALTCu4ZgupDfEbbpP2PIVrnE9p0q5l07poFU8V4t3U6Ky0b6SBs/Fc6jMCDAA9GbPN7lxrCwAAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.smwtticon.warning {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAf9JREFUeNrEl78vQ1EUx29fRSNpBKmkHSyIkjJJNISBiKVrQyIsHYiEQfwNrBImg9FQDAYxVGPQBgMTQiMsliaNSqSTaOp79Dxtqb77ine/ySf98e49329f3r0915bbErJqAaNgCLSBLmDna1lwA+5BDETBo0xRm0SAbrAIgqBeMuwL2AWr4KrSQK3CtQawBs5ByIS54LEhnrsJms0G8IMzsAAcono5OEiMa0oFoIH7wCv+Tl6u6TcKoJu7DEuOHAoxmSvQPmM0w1UuRHEAD9iTMq9eLvbwlAuwAtzi/+Vmr5IAw2BaWKdp9vwMsFS0qVghO3t+BOigR0pYL/LsoAABUKcgAHkGKMCgUKdBCuBTGMCnWbT0flySGv+VqlJWE4qlWbz+v+0HFCCpMECSAlwrDHBNAeIKA8SpJ+zEm0tQY7H5G+ihO5AAxwp+PXkmKADaGbFsfgtBhx5M57sh6o6crWYrkGdO3weOQER6am0jdvHt/Ksepm/DjHmEPUs6onmQkpre1Fsw1yV/B57Y61tLdgempEqkL4R4fS79LvMgG2COvcp2xXRrZg1LkHl8vBAiGc1/NhbV3pE5mk2A9UonGpNK8SEnLHsyCvMh9OAPzA+4Vtjs2TDB7dokOKnCGGtTjHGNxG9Ox7oGuGA/oN3T+eV6BtyCU36WpEK/CzAAVzJnvJUoedQAAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.smwtticon.error {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAg5JREFUeNrEl0soRGEUx8/M4JrSsKBYWClDLCxINJ5hYycLeZRmI8VCkpUlW2VhIRaShTwXMhlCZjwWdhSTlLJRKAmlvP5HZzQYd+687j31q6n7zfn/Zu797vd9psvqJNJY2aAOVIAckA8scu0NnIIL4AGb4EpLU5MGgULQB5qBTaPsA1gAo+BEbaBZ5VoaGANHwBlGOMlYp3x3CmSEK1AKDkEvUCjyUkTEIz01CfDAVWCn2JVdepaGEvCHp1PsKz2YRKBAFliJU3igxIpk/REYAZkU/8qUrB8CNaCD9KsOyfwW6A94qehRFsn8EsgFtaR/cWYuCzQCqwECnNnIAg4yrhwsUGCgQEGC6tQzW8jWOkBKUVX4rd9e6ck1Tc87C6pTMkGW0uArStsgpXYORfzzkovryKRY6Wl95l9Ns2qDkvqo/2OWCLUc/zv/H5fHowp/v7+lx9VJ1fcB34JrkBrs6vP2PJkSFTwDlZE9A+5ZejneUxt1zTuiRXxoMmgWLPEt8Bo4Db0s4AKvBoRzposFfGDXAAHO9LHABxg2QIAzP/zvgS3g1jHcLZk/dkQ94EaH8DvJ+rMlOwftOgh0S1bQXTH/NV1xDOfe86HOBROgJca340Z6Tmg9Gc3JIXQtBuFr0msu3LOhT7ZrrWA/guAN0CA9fNGcjv1VLg3LQB5I+b14gjNwIM+SJulPAQYATzVrW8qkGSQAAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.smwtticon.note {
+ display:inline-block;
+ /* @embed */ background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuxJREFUeNq8l01oE0EUx2fqFqp20VKLxmoRxaIVikZE8WClBUFaEA9CFcGDiERPPdRLoYqH+IUXDyoWTyLUi4gK4kFJ8FSlEXJQ0abFKEStoJKqxaab/l+YLdvdnd2ZtfTBj2Rn533szJs3M7w9NcUUpRrsAHvANrAGmOLdBPgMXoMX4CX4p2LUUOjTAI6Do2BLSN9D4vctuANug29BClUhBk+CDLig4Nwpm0FS6CaiBFAP7oObYqijSiO4Dh6AFaoBrAZPwUE2f3JA2GwMywH68kcgLrPUbHIWX87ZifVzY8/8KLNhMPjJkqnGhe194LvdyB2rgCw+BJ0yC+S0e21w2hQmy+z8G4u9L5ZlXZ6ALmC5pyAR5LynOdw5SayGs8utiyq/EtkPTrtzYCU4J9Noa+CsK1alPOEmJra7iQd1OQtWOQNIyLKUxM/5wKjFOtKlConMtGfISceUVxnKtVN2AEvAsaCviddxj3NnspFzmnfPdCwOHAXyuZQC2A3WyXrVGl4j6XFvgk2UyrpLs4l80yC1h2U1DXOYdLqmqVhiQSvBlg5DbCz/JX7LczBvqahuNfyqk45cwZJz5wh9eUBBmlOqKezaqM77W7zOqSKeyU7rrFjGozinZUb1we28V935bCH6HSUAd6GhYdd1Tr4pgIKuFm1I7lI7MGZF+Y4vFEBWVytW422j4Y8gWQogpatluooT1YqIkqLteBn+vLM3hwWUr2ATjcAvqhts4eUe+GmXr2u6q4Eq37M2owKdFXSzX/ic3Y7HqKjp1ADnkYyeVQ4rDrkKcu4T0SUwpKK90fS2ba9TrmevwEW/U/EkOAI+hln4UPS2Dastwzw4DP7KjuWj4sAYGMTjglU5lDifFTafvLCdczZyyd1wA7gLds5Txg+Jq92I6s0oJw4qyah7hSPbk8LWiO7d8A/oA7vALTCu4ZgupDfEbbpP2PIVrnE9p0q5l07poFU8V4t3U6Ky0b6SBs/Fc6jMCDAA9GbPN7lxrCwAAAAASUVORK5CYII=') no-repeat left bottom;
+ background-size: 10px 10px;
+}
+
+.qtip-bootstrap {
+ border-radius: 3px !important;
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.js
new file mode 100644
index 00000000..0d9dad9a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.util.tooltip.js
@@ -0,0 +1,314 @@
+/*!
+ * This file is part of the Semantic MediaWiki Tooltip/Highlighter module
+ * @see https://semantic-mediawiki.org/wiki/Help:Tooltip
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.8
+ * @revision 0.3.4
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function( $, mw, smw ) {
+ 'use strict';
+
+ /**
+ * Support variable
+ * @ignore
+ */
+ var h = mw.html;
+
+ /**
+ * Inheritance class for the smw.util constructor
+ *
+ * @since 1.9
+ * @class
+ */
+ smw.util = smw.util || {};
+
+ /**
+ * Class constructor
+ *
+ * @since 1.8
+ *
+ * @class
+ * @constructor
+ */
+ smw.util.tooltip = function( settings ) {
+ $.extend( this, this.defaults, settings );
+ };
+
+ /* Public methods */
+
+ smw.util.tooltip.prototype = {
+
+ /**
+ * Default options
+ *
+ * viewport => $(window) keeps the tooltip on-screen at all times
+ * 'top center' + 'bottom center' => Position the tooltip above the link
+ * solo => true shows only one tooltip at a time
+ *
+ * @since 1.8
+ *
+ * @property
+ */
+ defaults: {
+ qtip: {
+ position: {
+ viewport: $( window ),
+ at: 'top center',
+ my: 'bottom center'
+ },
+ show: {
+ solo: true
+ },
+ content: {
+ title: {
+ button: false
+ }
+ },
+ style: {
+ classes: 'qtip-shadow qtip-bootstrap'
+ }
+ },
+ classes: {
+ targetClass: 'smwtticon',
+ contentClass: 'smwttcontent',
+ contextClass: 'smwttpersist'
+ }
+ },
+
+ /**
+ * Get title message
+ *
+ * @since 1.8
+ *
+ * @param {string} key
+ *
+ * @return string
+ */
+ getTitleMsg: function( key ){
+ switch( key ){
+ case 'quantity': return 'smw-ui-tooltip-title-quantity';
+ case 'property': return 'smw-ui-tooltip-title-property';
+ case 'service' : return 'smw-ui-tooltip-title-service';
+ case 'warning' : return 'smw-ui-tooltip-title-warning';
+ default: return 'smw-ui-tooltip-title-info';
+ }
+ },
+
+ /**
+ * Initializes the qtip2 instance
+ *
+ * Example:
+ * tooltip = new smw.util.tooltip();
+ * tooltip.show ( {
+ * title: ...,
+ * type: ...,
+ * content: ...,
+ * button: ...,
+ * event: ...
+ * } );
+ *
+ * @since 1.8
+ *
+ * @param {Object} options
+ */
+ show: function( options ) {
+ var self = this;
+
+ // Check context
+ if ( options.context === undefined ){
+ return $.error( 'smw.util.tooltip.show() is missing a context object' );
+ }
+
+ return options.context.each( function() {
+ $( this ).qtip( $.extend( {}, self.defaults.qtip, {
+ hide: options.button ? 'unfocus' : undefined,
+ show: { event: options.event, solo: true },
+ content: {
+ text: options.content,
+ title: {
+ text: options.title,
+ button: options.button
+ }
+ }
+ } ) );
+ } );
+ },
+
+ /**
+ * The add method is a convenience method allowing to create a tooltip element
+ * with immediate instantiation
+ *
+ * @since 1.8
+ *
+ * @param {Object} options
+ */
+ add: function( options ) {
+ var self = this;
+
+ // Defaults
+ var option = $.extend( true, self.defaults.classes, options );
+
+ // Check context
+ if ( option.context === undefined ){
+ return $.error( 'smw.util.tooltip.add() is missing a context object' );
+ }
+
+ // Build a html element
+ function buildHtml( options ){
+ return h.element( 'span', { 'class' : options.contextClass, 'data-type': options.type },
+ new h.Raw(
+ h.element( 'span', { 'class' : options.targetClass }, null ) +
+ h.element( 'span', { 'class' : options.contentClass }, new h.Raw( options.content ) ) )
+ );
+ }
+
+ // Assign context
+ var $this = option.context;
+
+ // Append element
+ $this.prepend( buildHtml( option ) );
+
+ // Ensure that the right context is used as hoover/click element
+ // The class [] selector is not the fastest but the safest otherwise if
+ // spaces are used in the class definition it will break the selection
+ self.show.call( this,
+ $.extend( true, options, {
+ context: $this.find( "[class='" + option.targetClass + "']" ),
+ content: $this.find( "[class='" + option.contentClass + "']" )
+ } )
+ );
+ },
+
+ /**
+ * @since 2.4
+ *
+ * @param {Object} container
+ */
+ render: function( container ) {
+ var self = this;
+
+ container.each( function() {
+
+ // Get configuration
+ var $this = $( this ),
+ eventPrefs = mw.user.options.get( 'smw-prefs-tooltip-option-click' ) ? 'click' : undefined,
+ state = $this.data( 'state' ),
+ title = $this.data( 'title' ),
+ content = $this.data( 'content' ),
+ type = $this.data( 'type' );
+
+ // Assign sub-class
+ // Inline mostly used for special properties and quantity conversions
+ // Persistent extends interactions for service links, info, and error messages
+ $this.addClass( state === 'inline' ? 'smwttinline' : 'smwttpersist' );
+
+ // Remove title content which is supposed to be used when nojs is enabled
+ // and the "real" tooltip cannot show the ccontent
+ $this.removeAttr( "title" );
+ $this.removeClass( "is-disabled" );
+
+ // Call instance
+ self.show( {
+ context: $this,
+ content: content !== undefined ? content : $this.find( '.smwttcontent' ),
+ title : title !== undefined ? title : mw.msg( self.getTitleMsg( type ) ),
+ event : eventPrefs,
+ button : type === 'warning' || state === 'inline' ? false /* false = no close button */ : true
+ } );
+ } );
+ }
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ *
+ * @param {Object} context
+ */
+ smw.util.tooltip.prototype.initFromContext = function( context ) {
+ this.render( context.find( '.smw-highlighter' ) );
+ };
+
+ /**
+ * @since 2.5
+ * @method
+ */
+ smw.util.tooltip.prototype.registerEventListeners = function() {
+
+ var self = this;
+
+ // Listen to the Special:Browse event
+ mw.hook( 'smw.browse.apiparsecomplete' ).add( function( context ) {
+ self.initFromContext( context );
+ } );
+
+ // Listen to the Special:Browse event
+ mw.hook( 'smw.tooltip' ).add( function( context ) {
+ self.initFromContext( context );
+ } );
+
+ // Listen to the smw.deferred.query event
+ mw.hook( 'smw.deferred.query' ).add( function( context ) {
+ self.initFromContext( context );
+ } );
+
+ // SemanticForms/PageForms instance trigger
+ mw.hook( 'sf.addTemplateInstance' ).add( function( context ) {
+ self.initFromContext( context );
+ } );
+
+ mw.hook( 'pf.addTemplateInstance' ).add( function( context ) {
+ self.initFromContext( context );
+ } );
+
+ return self;
+ };
+
+ /**
+ * Factory
+ * @since 2.5
+ */
+ var Factory = {
+ newTooltip: function() {
+ return new smw.util.tooltip();
+ }
+ }
+
+ // Register addEventListeners early on
+ var instance = Factory.newTooltip().registerEventListeners();
+
+ /**
+ * Implementation of a tooltip instance
+ * @since 1.8
+ * @ignore
+ */
+ $( document ).ready( function() {
+ instance.initFromContext( $( this ) );
+ } );
+
+ smw.Factory = smw.Factory || {};
+ smw.Factory = $.extend( smw.Factory, Factory );
+
+} )( jQuery, mediaWiki, semanticMediaWiki );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.css b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.css
new file mode 100644
index 00000000..928943a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.css
@@ -0,0 +1,160 @@
+/*!
+ * This file is part of the Semantic MediaWiki Extension
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @see https://www.w3schools.com/howto/howto_js_vertical_tabs.asp
+ *
+ * @since 3.0
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/* Style the tab */
+div.smw-vtab-nav {
+ margin-bottom: 10px;
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+div.smw-vtab-nav.nav-right {
+ float: right;
+ margin-left: 10px;
+}
+
+div.smw-vtab-nav.nav-left {
+ float: left;
+ margin-right: 20px;
+}
+
+@media ( min-width: 300px ) {
+ div.smw-vtab-nav {
+ width: 25%;
+ word-wrap: break-word;
+ }
+}
+
+@media ( min-width: 600px ) {
+ div.smw-vtab-nav {
+ width: 250px;
+ }
+}
+
+/* Style the buttons inside the tab */
+div.smw-vtab-nav button {
+ display: block;
+ color: black;
+ padding: 12px 16px;
+ width: 100%;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ transition: 0.3s;
+ font-size: 1.2em;
+ background-color: #fff;
+ color: #428bca;
+ /* font-family: "Nimbus Sans L", "Liberation Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; */
+}
+
+div.smw-vtab-nav button a {
+ color: #428bca;
+ text-decoration: none;
+}
+
+div.smw-vtab-nav.nav-right button {
+ border-left: 1px solid #ddd;
+ text-align: left;
+}
+
+div.smw-vtab-nav.nav-left button {
+ border-right: 1px solid #ddd;
+ text-align: right;
+}
+
+/* Change background color of buttons on hover */
+div.smw-vtab-nav button:hover {
+ background-color: #eee;
+ border-radius: 0 4px 4px 0;
+ border-color: #ddd #ddd #ddd #ddd;
+}
+
+div.smw-vtab-nav.nav-right button:hover {
+ border-left: 1px solid #ddd;
+ border-color: #ddd #ddd #ddd #ddd;
+}
+
+div.smw-vtab-nav.nav-left button:hover {
+ border-right: 1px solid #ddd;
+ border-color: #ddd #ddd #ddd #ddd;
+}
+
+/* Create an active/current "tab button" class */
+div.smw-vtab-nav button.active {
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-radius: 10px;
+ font-style: normal;
+ color: black;
+}
+
+div.smw-vtab-nav.nav-right button.active {
+ border-radius: 0 4px 4px 0;
+}
+
+div.smw-vtab-nav.nav-left button.active {
+ border-radius: 4px 0 0 4px;
+}
+
+div.smw-vtab-nav button.active a {
+ color: black;
+ text-decoration: none;
+ font-weight: bold;
+}
+
+div.smw-vtab-nav.nav-right button.active, div.smw-vtab-nav.nav-right button.active a {
+ border-left: 0px;
+ border-color: #ddd #ddd #ddd #ddd;
+}
+
+div.smw-vtab-nav.nav-left button.active, div.smw-vtab-nav.nav-left button.active a {
+ border-right: 0px;
+ border-color: #ddd #ddd #ddd #ddd;
+}
+
+/* Style the tab content */
+.smw-vtab-content {
+ padding: 0px 0px;
+ border: 1px solid #ccc;
+ border: none;
+ min-height: 200px;
+}
+
+div.smw-vtab-nav .smw-vtab-warning a {
+ color: #ffc876;
+}
+
+div.smw-vtab-nav .smw-vtab-warning.active a {
+ color: #eb8c00;
+}
+
+div.smw-vtab-nav .smw-vtab-warning a::before, div.smw-vtab-nav .smw-vtab-warning.active a::before {
+ content: "âš  ";
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.js
new file mode 100644
index 00000000..25b9cf0d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/ext.smw.vertical.tabs.js
@@ -0,0 +1,100 @@
+/**
+ * @see https://www.w3schools.com/howto/howto_js_vertical_tabs.asp
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ $( document ).ready( function() {
+
+ // Re-enable (i.e. make fully visible) the nav menu after JS has been loaded
+ $( '.smw-vtab-nav' ).each( function() {
+ $( this ).css( 'opacity', '1' ).css( 'pointer-events', 'all' );
+ } );
+
+ // https://stackoverflow.com/questions/1634748/how-can-i-delete-a-query-string-parameter-in-javascript
+ var removeURIKeyParam = function ( uri, key ) {
+ return uri.replace( new RegExp('([\?&])' + key + '=[^&;]+[&;]?'), '');
+ }
+
+ var setLocation = function ( id, target ) {
+
+ var i, tabcontent, tablinks;
+
+ // Get all elements with class="smw-vtab-content" and hide them
+ tabcontent = document.getElementsByClassName( "smw-vtab-content" );
+
+ for ( i = 0; i < tabcontent.length; i++ ) {
+ if ( tabcontent[i] ) {
+ tabcontent[i].style.display = "none";
+ };
+ }
+
+ // Get all elements with class="smw-vtab-link" and remove the class "active"
+ tablinks = document.getElementsByClassName( "smw-vtab-link" );
+
+ for ( i = 0; i < tablinks.length; i++ ) {
+ if ( tablinks[i] ) {
+ tablinks[i].className = tablinks[i].className.replace(" active", "" );
+ };
+ }
+
+ // Show the current tab, and add an "active" class to the link that opened the tab
+ document.getElementById( id ).style.display = 'inline';
+ if ( target ) {
+ target.className += " active";
+ };
+ }
+
+ // A request was initiated with a href hash
+ var id = window.location.hash;
+
+ // @see HtmlVTabs::link
+ if ( id !== '' && id.indexOf( 'tab-' ) > 0 ) {
+ setLocation(
+ id.replace( '#', '' ),
+ document.getElementById( 'vtab-item-' + id.replace( '#', '' ) )
+ );
+ };
+
+ // Iterate over available nav links onClick
+ $( '.smw-vtab-link' ).on( "click", function( event ) {
+
+ // Remove any &tab=... query parameter to avoid a contradictory history
+ // when used in combination with #href hash
+ if( window.location.search.indexOf( '&tab' ) > 0 ) {
+ // https://developer.mozilla.org/en-US/docs/Web/API/History_API
+ if( window.history != undefined && window.history.pushState != undefined ) {
+ window.history.pushState(
+ {},
+ '',
+ window.location.pathname + removeURIKeyParam( window.location.search, 'tab' )
+ );
+ }
+ }
+
+ // Set the href in case the click went on the button element and not
+ // directly on the a element
+ window.location.href = '#' + $( this ).data( 'id' );
+
+ // Scroll the page to the top left to a void some jumping
+ window.scrollTo( 0, 0 );
+
+ setLocation(
+ $( this ).data( 'id' ),
+ event.currentTarget
+ );
+
+ event.preventDefault();
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/res/smw/util/smw.property.page.js b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/smw.property.page.js
new file mode 100644
index 00000000..766711f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/res/smw/util/smw.property.page.js
@@ -0,0 +1,50 @@
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+
+/*global jQuery, mediaWiki, mw */
+( function ( $, mw ) {
+
+ 'use strict';
+
+ $( document ).ready( function() {
+
+ $( '.smw-property-page-info' ).removeClass( 'is-disabled' );
+
+ $( '.smw-property-page-info' ).each( function() {
+
+ var context = $( this );
+
+ var params = {
+ 'search': $( this ).data( 'label' ),
+ 'limit' : 1,
+ 'strict': true,
+ 'usageCount': true
+ };
+
+ var postArgs = {
+ 'action': 'smwbrowse',
+ 'browse': 'property',
+ 'params': JSON.stringify( params )
+ };
+
+ new mw.Api().post( postArgs ).then( function ( data ) {
+ var text = '<table class="smw-personal-table"><tbody>' + data.query[context.data( 'key' )].usageCount + '</tbody></table>';
+
+ smw.Factory.newTooltip().show ( {
+ context: context,
+ title: mw.msg( 'smw-personal-jobqueue-watchlist' ),
+ type: 'persitent',
+ content: text
+ } );
+ }, function () {
+ // Do nothing
+ } );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Aliases.php b/www/wiki/extensions/SemanticMediaWiki/src/Aliases.php
new file mode 100644
index 00000000..6cdbce1e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Aliases.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * SemanticMediaWiki compatibility aliases for classes that got moved into the SMW namespace
+ */
+
+// 3.0
+class_alias( \SMW\MediaWiki\Deferred\CallableUpdate::class, 'SMW\DeferredCallableUpdate' );
+class_alias( \SMW\Parser\InTextAnnotationParser::class, 'SMW\InTextAnnotationParser' );
+class_alias( \SMW\Encoder::class, 'SMW\UrlEncoder' );
+class_alias( \SMW\Query\ResultPrinter::class, 'SMW\QueryResultPrinter' );
+class_alias( \SMW\Query\ResultPrinter::class, 'SMWIResultPrinter' );
+class_alias( \SMW\Query\ExportPrinter::class, 'SMW\ExportPrinter' );
+class_alias( \SMW\Query\ResultPrinters\ResultPrinter::class, 'SMW\ResultPrinter' );
+class_alias( \SMW\Query\ResultPrinters\ResultPrinter::class, 'SMWResultPrinter' );
+class_alias( \SMW\Query\ResultPrinters\FileExportPrinter::class, 'SMW\FileExportPrinter' );
+class_alias( \SMW\Query\ResultPrinters\ListResultPrinter::class, 'SMW\ListResultPrinter' );
+class_alias( \SMW\Query\Parser::class, 'SMWQueryParser' );
+class_alias( \SMW\SQLStore\ChangeOp\ChangeOp::class, 'SMW\SQLStore\CompositePropertyTableDiffIterator' );
+class_alias( \SMW\Connection\ConnectionProvider::class, 'SMW\DBConnectionProvider' );
+class_alias( \SMW\DataValues\TypesValue::class, 'SMWTypesValue' );
+class_alias( \SMW\DataValues\PropertyValue::class, 'SMWPropertyValue' );
+class_alias( \SMW\DataValues\StringValue::class, 'SMWStringValue' );
+class_alias( \SMW\MediaWiki\Connection\Database::class, '\SMW\MediaWiki\Database' );
+class_alias( \SMWDIBlob::class, 'SMWDIString' );
+
+// 1.9.
+class_alias( \SMW\Store::class, 'SMWStore' );
+class_alias( \SMW\MediaWiki\Jobs\UpdateJob::class, 'SMWUpdateJob' );
+class_alias( \SMW\MediaWiki\Jobs\RefreshJob::class, 'SMWRefreshJob' );
+class_alias( \SMW\SemanticData::class, 'SMWSemanticData' );
+class_alias( \SMW\DIWikiPage::class, 'SMWDIWikiPage' );
+class_alias( \SMW\DIProperty::class, 'SMWDIProperty' );
+class_alias( \SMW\Serializers\QueryResultSerializer::class, 'SMWDISerializer' );
+class_alias( \SMW\DataValueFactory::class, 'SMWDataValueFactory' );
+class_alias( \SMW\Exception\DataItemException::class, 'SMWDataItemException' );
+class_alias( \SMW\SQLStore\PropertyTableDefinition::class, 'SMWSQLStore3Table' );
+class_alias( \SMW\DIConcept::class, 'SMWDIConcept' );
+class_alias( \SMW\Query\ResultPrinters\TableResultPrinter::class, 'SMWTableResultPrinter' );
+
+// 2.0
+class_alias( \SMW\Query\ResultPrinters\FileExportPrinter::class, 'SMWExportPrinter' );
+class_alias( \SMW\AggregatablePrinter::class, 'SMWAggregatablePrinter' );
+class_alias( \SMW\Query\ResultPrinters\CategoryResultPrinter::class, 'SMWCategoryResultPrinter' );
+class_alias( \SMW\DsvResultPrinter::class, 'SMWDSVResultPrinter' );
+class_alias( \SMW\EmbeddedResultPrinter::class, 'SMWEmbeddedResultPrinter' );
+class_alias( \SMW\RdfResultPrinter::class, 'SMWRDFResultPrinter' );
+class_alias( \SMW\ListResultPrinter::class, 'SMWListResultPrinter' );
+class_alias( \SMW\RawResultPrinter::class, 'SMW\ApiResultPrinter' );
+
+// 2.0
+class_alias( \SMW\SPARQLStore\SPARQLStore::class, 'SMWSparqlStore' );
+class_alias( \SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector::class, 'SMWSparqlDatabase4Store' );
+class_alias( \SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector::class, 'SMWSparqlDatabaseVirtuoso' );
+class_alias( \SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector::class, 'SMWSparqlDatabase' );
+
+// 2.1
+class_alias( \SMWSQLStore3::class, 'SMW\SQLStore\SQLStore' );
+class_alias( \SMW\Query\Language\Description::class, 'SMWDescription' );
+class_alias( \SMW\Query\Language\ThingDescription::class, 'SMWThingDescription' );
+class_alias( \SMW\Query\Language\ClassDescription::class, 'SMWClassDescription' );
+class_alias( \SMW\Query\Language\ConceptDescription::class, 'SMWConceptDescription' );
+class_alias( \SMW\Query\Language\NamespaceDescription::class, 'SMWNamespaceDescription' );
+class_alias( \SMW\Query\Language\ValueDescription::class, 'SMWValueDescription' );
+class_alias( \SMW\Query\Language\Conjunction::class, 'SMWConjunction' );
+class_alias( \SMW\Query\Language\Disjunction::class, 'SMWDisjunction' );
+class_alias( \SMW\Query\Language\SomeProperty::class, 'SMWSomeProperty' );
+class_alias( \SMW\Query\PrintRequest::class, 'SMWPrintRequest' );
+class_alias( \SMW\MediaWiki\Search\Search::class, 'SMWSearch' );
+
+// 2.2
+// Some weird SF dependency needs to be removed as quick as possible
+class_alias( \SMW\SQLStore\Lookup\ListLookup::class, 'SMW\SQLStore\PropertiesCollector' );
+class_alias( \SMW\SQLStore\Lookup\ListLookup::class, 'SMW\SQLStore\UnusedPropertiesCollector' );
+
+class_alias( \SMW\Exporter\Element\ExpElement::class, 'SMWExpElement' );
+class_alias( \SMW\Exporter\Element\ExpResource::class, 'SMWExpResource' );
+class_alias( \SMW\Exporter\Element\ExpNsResource::class, 'SMWExpNsResource' );
+class_alias( \SMW\Exporter\Element\ExpLiteral::class, 'SMWExpLiteral' );
+class_alias( \SMW\DataValues\ImportValue::class, 'SMWImportValue' );
+class_alias( \SMW\SQLStore\QueryEngine\QueryEngine::class, 'SMWSQLStore3QueryEngine' );
+
+// 2.3
+class_alias( \SMW\ParserParameterProcessor::class, 'SMW\ParserParameterFormatter' );
+class_alias( \SMW\ParameterProcessorFactory::class, 'SMW\ParameterFormatterFactory' );
+
+// 2.4
+class_alias( \SMW\RequestOptions::class, 'SMWRequestOptions' );
+class_alias( \SMW\StringCondition::class, 'SMWStringCondition' );
+class_alias( \SMW\HashBuilder::class, 'SMW\Hash' );
+class_alias( \SMW\DataValues\BooleanValue::class, 'SMWBoolValue' );
+
+// 2.5
+class_alias( \SMW\QueryPrinterFactory::class, 'SMW\FormatFactory' );
+class_alias( \SMW\ParserFunctions\SubobjectParserFunction::class, 'SMW\SubobjectParserFunction' );
+class_alias( \SMW\ParserFunctions\RecurringEventsParserFunction::class, 'SMW\RecurringEventsParserFunction' );
+class_alias( \SMW\SQLStore\PropertyTableDefinition::class, 'SMW\SQLStore\TableDefinition' );
+class_alias( \SMW\DataModel\ContainerSemanticData::class, 'SMWContainerSemanticData' );
+
+// 3.0 (late alias definition)
+class_alias( \SMW\Elastic\ElasticStore::class, 'SMWElasticStore' );
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ApplicationFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/ApplicationFactory.php
new file mode 100644
index 00000000..17d34942
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ApplicationFactory.php
@@ -0,0 +1,591 @@
+<?php
+
+namespace SMW;
+
+use Closure;
+use Onoi\CallbackContainer\CallbackContainerFactory;
+use Onoi\CallbackContainer\ContainerBuilder;
+use Parser;
+use ParserOutput;
+use SMW\Maintenance\MaintenanceFactory;
+use SMW\MediaWiki\Jobs\JobFactory;
+use SMW\MediaWiki\MwCollaboratorFactory;
+use SMW\MediaWiki\PageCreator;
+use SMW\MediaWiki\TitleFactory;
+use SMW\Query\ProfileAnnotator\QueryProfileAnnotatorFactory;
+use SMW\Services\SharedServicesContainer;
+use SMWQueryParser as QueryParser;
+use Title;
+
+/**
+ * Application instances access for internal and external use
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ApplicationFactory {
+
+ /**
+ * @var ApplicationFactory
+ */
+ private static $instance = null;
+
+ /**
+ * @var ContainerBuilder
+ */
+ private $containerBuilder;
+
+ /**
+ * @var string
+ */
+ private $servicesFileDir = '';
+
+ /**
+ * @since 2.0
+ *
+ * @param ContainerBuilder|null $containerBuilder
+ * @param string $servicesFileDir
+ */
+ public function __construct( ContainerBuilder $containerBuilder = null, $servicesFileDir = '' ) {
+ $this->containerBuilder = $containerBuilder;
+ $this->servicesFileDir = $servicesFileDir;
+ }
+
+ /**
+ * This method returns the global instance of the application factory.
+ *
+ * Reliance on global state is needed at entry points into SMW such as
+ * hook handlers, special pages and jobs, since there we tend to not
+ * have control over the object lifecycle. Pragmatically we might also
+ * want to use this when refactoring legacy code that already has the
+ * global state dependency. For new code very special justification is
+ * required to rely on global state.
+ *
+ * @since 2.0
+ *
+ * @return self
+ */
+ public static function getInstance() {
+
+ if ( self::$instance !== null ) {
+ return self::$instance;
+ }
+
+ $servicesFileDir = $GLOBALS['smwgServicesFileDir'];
+
+ $containerBuilder = self::newContainerBuilder(
+ new CallbackContainerFactory(),
+ $servicesFileDir
+ );
+
+ return self::$instance = new self( $containerBuilder, $servicesFileDir );
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static function clear() {
+
+ if ( self::$instance !== null ) {
+ self::$instance->getSettings()->clear();
+ }
+
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $objectName
+ * @param callable|array $objectSignature
+ */
+ public function registerObject( $objectName, $objectSignature ) {
+ $this->containerBuilder->registerObject( $objectName, $objectSignature );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $file
+ */
+ public function registerFromFile( $file ) {
+ $this->containerBuilder->registerFromFile( $file );
+ }
+
+ /**
+ * @private
+ *
+ * @note Services called via this function are for internal use only and
+ * not to be relied upon for external access.
+ *
+ *
+ * @param string $service
+ *
+ * @return mixed
+ */
+ public function singleton( ...$service ) {
+ return $this->containerBuilder->singleton( ...$service );
+ }
+
+ /**
+ * @private
+ *
+ * @note Services called via this function are for internal use only and
+ * not to be relied upon for external access.
+ *
+ * @since 2.5
+ *
+ * @param string $service
+ *
+ * @return mixed
+ */
+ public function create( ...$service ) {
+ return $this->containerBuilder->create( ...$service );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return SerializerFactory
+ */
+ public function newSerializerFactory() {
+ return new SerializerFactory();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return JobFactory
+ */
+ public function newJobFactory() {
+ return $this->containerBuilder->create( 'JobFactory' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return ParserFunctionFactory
+ */
+ public function newParserFunctionFactory() {
+ return new ParserFunctionFactory();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return MaintenanceFactory
+ */
+ public function newMaintenanceFactory() {
+ return new MaintenanceFactory();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return CacheFactory
+ */
+ public function newCacheFactory() {
+ return $this->containerBuilder->create( 'CacheFactory', $this->getSettings()->get( 'smwgMainCacheType' ) );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return CacheFactory
+ */
+ public function getCacheFactory() {
+ return $this->containerBuilder->singleton( 'CacheFactory', $this->getSettings()->get( 'smwgMainCacheType' ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|null $source
+ *
+ * @return QuerySourceFactory
+ */
+ public function getQuerySourceFactory( $source = null ) {
+ return $this->containerBuilder->singleton( 'QuerySourceFactory' );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return Store
+ */
+ public function getStore( $store = null ) {
+ return $this->containerBuilder->singleton( 'Store', $store );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return Settings
+ */
+ public function getSettings() {
+ return $this->containerBuilder->singleton( 'Settings' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ConnectionManager
+ */
+ public function getConnectionManager() {
+ return $this->containerBuilder->singleton( 'ConnectionManager' );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return TitleFactory
+ */
+ public function newTitleFactory() {
+ return $this->containerBuilder->create( 'TitleFactory', $this->newPageCreator() );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return PageCreator
+ */
+ public function newPageCreator() {
+ return $this->containerBuilder->create( 'PageCreator' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PageUpdater
+ */
+ public function newPageUpdater() {
+
+ $pageUpdater = $this->containerBuilder->create(
+ 'PageUpdater',
+ $this->getStore()->getConnection( 'mw.db' ),
+ $this->newDeferredTransactionalCallableUpdate()
+ );
+
+ $pageUpdater->setLogger(
+ $this->getMediaWikiLogger()
+ );
+
+ // https://phabricator.wikimedia.org/T154427
+ // It is unclear what changed in MW 1.29 but it has been observed that
+ // executing a HTMLCacheUpdate from within an transaction can lead to a
+ // "ErrorException ... 1 buffered job ... HTMLCacheUpdateJob never
+ // inserted" hence disable the update functionality
+ $pageUpdater->isHtmlCacheUpdate(
+ false
+ );
+
+ return $pageUpdater;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return IteratorFactory
+ */
+ public function getIteratorFactory() {
+ return $this->containerBuilder->singleton( 'IteratorFactory' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataValueFactory
+ */
+ public function getDataValueFactory() {
+ return DataValueFactory::getInstance();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return Cache
+ */
+ public function getCache( $cacheType = null ) {
+ return $this->containerBuilder->singleton( 'Cache', $cacheType );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return InTextAnnotationParser
+ */
+ public function newInTextAnnotationParser( ParserData $parserData ) {
+
+ $mwCollaboratorFactory = $this->newMwCollaboratorFactory();
+
+ $linksProcessor = $this->containerBuilder->create( 'LinksProcessor' );
+ $settings = $this->getSettings();
+
+ $linksProcessor->isStrictMode(
+ $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_STRICT )
+ );
+
+ $inTextAnnotationParser = new InTextAnnotationParser(
+ $parserData,
+ $linksProcessor,
+ $mwCollaboratorFactory->newMagicWordsFinder(),
+ $mwCollaboratorFactory->newRedirectTargetFinder()
+ );
+
+ $inTextAnnotationParser->isLinksInValues(
+ $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_LINV )
+ );
+
+ $inTextAnnotationParser->showErrors(
+ $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_INL_ERROR )
+ );
+
+ return $inTextAnnotationParser;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return ParserData
+ */
+ public function newParserData( Title $title, ParserOutput $parserOutput ) {
+ return $this->containerBuilder->create( 'ParserData', $title, $parserOutput );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return ContentParser
+ */
+ public function newContentParser( Title $title ) {
+ return $this->containerBuilder->create( 'ContentParser', $title );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return DataUpdater
+ */
+ public function newDataUpdater( SemanticData $semanticData ) {
+
+ $dataUpdater = new DataUpdater(
+ $this->getStore(),
+ $semanticData
+ );
+
+ $dataUpdater->isCommandLineMode(
+ $GLOBALS['wgCommandLineMode']
+ );
+
+ return $dataUpdater;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MwCollaboratorFactory
+ */
+ public function newMwCollaboratorFactory() {
+ return new MwCollaboratorFactory( $this );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return NamespaceExaminer
+ */
+ public function getNamespaceExaminer() {
+ return $this->containerBuilder->create( 'NamespaceExaminer' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return PropertySpecificationLookup
+ */
+ public function getPropertySpecificationLookup() {
+ return $this->containerBuilder->singleton( 'PropertySpecificationLookup' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return HierarchyLookup
+ */
+ public function newHierarchyLookup() {
+ return $this->containerBuilder->create( 'HierarchyLookup' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyLabelFinder
+ */
+ public function getPropertyLabelFinder() {
+ return $this->containerBuilder->singleton( 'PropertyLabelFinder' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return CachedPropertyValuesPrefetcher
+ */
+ public function getCachedPropertyValuesPrefetcher() {
+ return $this->containerBuilder->singleton( 'CachedPropertyValuesPrefetcher' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return MediaWikiNsContentReader
+ */
+ public function getMediaWikiNsContentReader() {
+ return $this->containerBuilder->singleton( 'MediaWikiNsContentReader' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return InMemoryPoolCache
+ */
+ public function getInMemoryPoolCache() {
+ return $this->containerBuilder->singleton( 'InMemoryPoolCache' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return \createBalancer
+ */
+ public function getLoadBalancer() {
+ return $this->containerBuilder->singleton( 'DBLoadBalancer' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param callable $callback
+ *
+ * @return DeferredCallableUpdate
+ */
+ public function newDeferredCallableUpdate( callable $callback = null ) {
+
+ $deferredCallableUpdate = $this->containerBuilder->create(
+ 'DeferredCallableUpdate',
+ $callback
+ );
+
+ $deferredCallableUpdate->isDeferrableUpdate(
+ $this->getSettings()->get( 'smwgEnabledDeferredUpdate' )
+ );
+
+ $deferredCallableUpdate->setLogger(
+ $this->getMediaWikiLogger()
+ );
+
+ $deferredCallableUpdate->isCommandLineMode(
+ $GLOBALS['wgCommandLineMode']
+ );
+
+ return $deferredCallableUpdate;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $callback
+ *
+ * @return DeferredTransactionalUpdate
+ */
+ public function newDeferredTransactionalCallableUpdate( callable $callback = null ) {
+
+ $deferredTransactionalUpdate = $this->containerBuilder->create(
+ 'DeferredTransactionalCallableUpdate',
+ $callback,
+ $this->getStore()->getConnection( 'mw.db' )
+ );
+
+ $deferredTransactionalUpdate->isDeferrableUpdate(
+ $this->getSettings()->get( 'smwgEnabledDeferredUpdate' )
+ );
+
+ $deferredTransactionalUpdate->setLogger(
+ $this->getMediaWikiLogger()
+ );
+
+ $deferredTransactionalUpdate->isCommandLineMode(
+ $GLOBALS['wgCommandLineMode']
+ );
+
+ return $deferredTransactionalUpdate;
+ }
+
+ /**
+ * @deprecated since 2.5, use QueryFactory::newQueryParser
+ * @since 2.1
+ *
+ * @return QueryParser
+ */
+ public function newQueryParser( $queryFeatures = false ) {
+ return $this->getQueryFactory()->newQueryParser( $queryFeatures );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataItemFactory
+ */
+ public function getDataItemFactory() {
+ return $this->containerBuilder->singleton( 'DataItemFactory' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return QueryFactory
+ */
+ public function getQueryFactory() {
+ return $this->containerBuilder->singleton( 'QueryFactory' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return LoggerInterface
+ */
+ public function getMediaWikiLogger( $channel = 'smw' ) {
+ return $this->containerBuilder->singleton( 'MediaWikiLogger', $channel, $GLOBALS['smwgDefaultLoggerRole'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return JobQueue
+ */
+ public function getJobQueue() {
+ return $this->containerBuilder->singleton( 'JobQueue' );
+ }
+
+ private static function newContainerBuilder( CallbackContainerFactory $callbackContainerFactory, $servicesFileDir ) {
+
+ $containerBuilder = $callbackContainerFactory->newCallbackContainerBuilder();
+
+ $containerBuilder->registerCallbackContainer( new SharedServicesContainer() );
+ $containerBuilder->registerFromFile( $servicesFileDir . '/' . 'MediaWikiServices.php' );
+ $containerBuilder->registerFromFile( $servicesFileDir . '/' . 'ImporterServices.php' );
+
+ // $containerBuilder = $callbackContainerFactory->newLoggableContainerBuilder(
+ // $containerBuilder,
+ // $callbackContainerFactory->newBacktraceSniffer( 10 ),
+ // $callbackContainerFactory->newCallFuncMemorySniffer()
+ // );
+ // $containerBuilder->setLogger( $containerBuilder->singleton( 'MediaWikiLogger' ) );
+
+ return $containerBuilder;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/CacheFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/CacheFactory.php
new file mode 100644
index 00000000..4725bef7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/CacheFactory.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW;
+
+use ObjectCache;
+use Onoi\Cache\CacheFactory as OnoiCacheFactory;
+use RuntimeException;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CacheFactory {
+
+ /**
+ * @var string|integer
+ */
+ private $mainCacheType;
+
+ /**
+ * @since 2.2
+ *
+ * @param string|integer|null $mainCacheType
+ */
+ public function __construct( $mainCacheType = null ) {
+ $this->mainCacheType = $mainCacheType;
+
+ if ( $this->mainCacheType === null ) {
+ $this->mainCacheType = $GLOBALS['smwgMainCacheType'];
+ }
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string|integer
+ */
+ public function getMainCacheType() {
+ return $this->mainCacheType;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public static function getCachePrefix() {
+ return $GLOBALS['wgCachePrefix'] === false ? wfWikiID() : $GLOBALS['wgCachePrefix'];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Title|integer|string $key
+ *
+ * @return string
+ */
+ public static function getPurgeCacheKey( $key ) {
+
+ if ( $key instanceof Title ) {
+ $key = $key->getArticleID();
+ }
+
+ return self::getCachePrefix() . ':smw:arc:' . md5( $key );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $cacheOptions
+ *
+ * @return stdClass
+ * @throws RuntimeException
+ */
+ public function newCacheOptions( array $cacheOptions ) {
+
+ if ( !isset( $cacheOptions['useCache'] ) || !isset( $cacheOptions['ttl'] ) ) {
+ throw new RuntimeException( "Cache options is missing a useCache/ttl parameter" );
+ }
+
+ return (object)$cacheOptions;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param integer $cacheSize
+ *
+ * @return Cache
+ */
+ public function newFixedInMemoryCache( $cacheSize = 500 ) {
+ return OnoiCacheFactory::getInstance()->newFixedInMemoryLruCache( $cacheSize );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return Cache
+ */
+ public function newNullCache() {
+ return OnoiCacheFactory::getInstance()->newNullCache();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param integer|string $mediaWikiCacheType
+ *
+ * @return Cache
+ */
+ public function newMediaWikiCompositeCache( $mediaWikiCacheType = null ) {
+
+ $compositeCache = OnoiCacheFactory::getInstance()->newCompositeCache( [
+ $this->newFixedInMemoryCache( 500 ),
+ $this->newMediaWikiCache( $mediaWikiCacheType )
+ ] );
+
+ return $compositeCache;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|string $mediaWikiCacheType
+ *
+ * @return Cache
+ */
+ public function newMediaWikiCache( $mediaWikiCacheType = null ) {
+
+ $mediaWikiCache = ObjectCache::getInstance(
+ ( $mediaWikiCacheType === null ? $this->getMainCacheType() : $mediaWikiCacheType )
+ );
+
+ return OnoiCacheFactory::getInstance()->newMediaWikiCache( $mediaWikiCache );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|null $cacheType
+ *
+ * @return Cache
+ */
+ public function newCacheByType( $cacheType = null ) {
+
+ if ( $cacheType === CACHE_NONE || $cacheType === null ) {
+ return $this->newNullCache();
+ }
+
+ return $this->newMediaWikiCache( $cacheType );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $namespace
+ * @param string|integer|null $cacheType
+ * @param integer $cacheLifetime
+ *
+ * @return BlobStore
+ */
+ public function newBlobStore( $namespace, $cacheType = null, $cacheLifetime = 0 ) {
+ return ApplicationFactory::getInstance()->create( 'BlobStore', $namespace, $cacheType, $cacheLifetime );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/CachedPropertyValuesPrefetcher.php b/www/wiki/extensions/SemanticMediaWiki/src/CachedPropertyValuesPrefetcher.php
new file mode 100644
index 00000000..873a887f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/CachedPropertyValuesPrefetcher.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace SMW;
+
+use Onoi\BlobStore\BlobStore;
+use SMWQuery as Query;
+
+/**
+ * This class should be accessed via ApplicationFactory::getCachedPropertyValuesPrefetcher
+ * to ensure a singleton instance.
+ *
+ * The purpose of this class is to give fragmented access to frequent (hence
+ * cacheable) property values to ensure that the store is only used for when a
+ * match can not be found and so freeing up the capacities that can equally be
+ * served from a persistent cache instance.
+ *
+ * It is expected that as soon as the "on.before.semanticdata.update.complete"
+ * event has been emitted that matchable cache entries are purged for the
+ * subject in question.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CachedPropertyValuesPrefetcher {
+
+ /**
+ * @var string
+ */
+ const VERSION = '0.4.1';
+
+ /**
+ * Namespace occupied by the BlobStore
+ */
+ const CACHE_NAMESPACE = 'smw:pvp:store';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var BlobStore
+ */
+ private $blobStore;
+
+ /**
+ * @var boolean
+ */
+ private $disableCache = false;
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param BlobStore $blobStore
+ */
+ public function __construct( Store $store, BlobStore $blobStore ) {
+ $this->store = $store;
+ $this->blobStore = $blobStore;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function resetCacheBy( DIWikiPage $subject ) {
+ $this->blobStore->delete( $this->getRootHashFrom( $subject ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $disableCache
+ */
+ public function disableCache( $disableCache ) {
+ $this->disableCache = (bool)$disableCache;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return array
+ */
+ public function getPropertyValues( DIWikiPage $subject, DIProperty $property, RequestOptions $requestOptions = null ) {
+
+ // Items are collected as part of the subject hash so that any request is
+ // stored with that entity identifier allowing it to be evicted entirely
+ // when the subject is changed.
+ //
+ // The key on the other hand represent an individual request identifier
+ // that is stored as part of the overall cache item but making distinct
+ // requests possible, yet is fetched as part of the overall subject to
+ // minimize cache fragmentation and a better eviction strategy.
+ $key = $property->getKey() .
+ ':' . $subject->getSubobjectName() .
+ ':' . ( $requestOptions !== null ? md5( $requestOptions->getHash() ) : null );
+
+ $container = $this->blobStore->read(
+ $this->getRootHashFrom( $subject )
+ );
+
+ if ( $this->disableCache === false && $container->has( $key ) ) {
+ return $container->get( $key );
+ }
+
+ $dataItems = $this->store->getPropertyValues(
+ $subject,
+ $property,
+ $requestOptions
+ );
+
+ $container->set( $key, $dataItems );
+
+ $this->blobStore->save(
+ $container
+ );
+
+ return $dataItems;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Query $query
+ *
+ * @return array
+ */
+ public function queryPropertyValuesFor( Query $query ) {
+ return $this->store->getQueryResult( $query )->getResults();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return BlobStore
+ */
+ public function getBlobStore() {
+ return $this->blobStore;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return string
+ */
+ public function getRootHashFrom( DIWikiPage $subject ) {
+ return md5( $subject->asBase()->getHash() . self::VERSION );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $hash
+ *
+ * @return string
+ */
+ public function createHashFromString( $hash ) {
+ return md5( $hash . self::VERSION );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ChangePropListener.php b/www/wiki/extensions/SemanticMediaWiki/src/ChangePropListener.php
new file mode 100644
index 00000000..b41a0ddc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ChangePropListener.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW;
+
+use Closure;
+use SMW\Exception\PropertyLabelNotResolvedException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropListener {
+
+ /**
+ * @var []
+ */
+ private static $listenerCallbacks = [];
+
+ /**
+ * @var []
+ */
+ private static $deferrableCallbacks = [];
+
+ /**
+ * @since 3.0
+ */
+ public static function clearListeners() {
+ self::$listenerCallbacks = [];
+ self::$deferrableCallbacks = [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param Closure $callback
+ */
+ public function addListenerCallback( $key, Closure $callback ) {
+
+ if ( $key === '' ) {
+ return;
+ }
+
+ if ( !isset( self::$listenerCallbacks[$key] ) ) {
+ self::$listenerCallbacks[$key] = [];
+ }
+
+ self::$listenerCallbacks[$key][] = $callback;
+ }
+
+ /**
+ * Finalize event inception points by matching the key to a property
+ * equivalent representation.
+ *
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function loadListeners( Store $store ) {
+
+ foreach ( self::$listenerCallbacks as $key => $value ) {
+
+ try {
+ $property = DIProperty::newFromUserLabel( $key );
+ } catch ( PropertyLabelNotResolvedException $e ) {
+ continue;
+ }
+
+ $pid = $store->getObjectIds()->getSMWPropertyID(
+ $property
+ );
+
+ self::$listenerCallbacks[$pid] = $value;
+ unset( self::$listenerCallbacks[$key] );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $pid
+ * @param array $record
+ */
+ public static function record( $pid, array $record ) {
+
+ if ( !isset( self::$listenerCallbacks[$pid] ) ) {
+ return;
+ }
+
+ if ( !isset( self::$deferrableCallbacks[$pid] ) ) {
+ self::$deferrableCallbacks[$pid] = [];
+ }
+
+ // Copy callbacks to the deferred list to isolate the execution
+ // from the event point
+ foreach ( self::$listenerCallbacks[$pid] as $callback ) {
+ self::$deferrableCallbacks[$pid][] = [ $callback, $record ];
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function callListeners() {
+
+ if ( self::$deferrableCallbacks === [] ) {
+ return;
+ }
+
+ $deferrableCallbacks = self::$deferrableCallbacks;
+
+ $callback = function() use( $deferrableCallbacks ) {
+ foreach ( $deferrableCallbacks as $pid => $records ) {
+ foreach ( $records as $rec ) {
+ call_user_func_array( $rec[0], [ $rec[1] ] );
+ }
+ }
+ };
+
+ $deferredTransactionalUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate(
+ $callback
+ );
+
+ $deferredTransactionalUpdate->setOrigin(
+ [
+ 'ChangePropListener::callListeners'
+ ]
+ );
+
+ $deferredTransactionalUpdate->commitWithTransactionTicket();
+ $deferredTransactionalUpdate->pushUpdate();
+
+ self::$deferrableCallbacks = [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/CompatibilityMode.php b/www/wiki/extensions/SemanticMediaWiki/src/CompatibilityMode.php
new file mode 100644
index 00000000..fb214f49
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/CompatibilityMode.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Internal benchmarks (XDebug) have shown that some extensions may affect the
+ * performance to a greater degree than expected and can impose a performance
+ * penalty to the overall system (templates, queries etc.).
+ *
+ * If a user is willing to incur those potential disadvantages by setting the
+ * `CompatibilityMode`, s(he) is to understand the latent possibility of those
+ * disadvantages.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CompatibilityMode {
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public static function extensionNotEnabled() {
+
+ if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+ return !$GLOBALS['smwgSemanticsEnabled'];
+ }
+
+ $GLOBALS['smwgSemanticsEnabled'] = true;
+ ApplicationFactory::getInstance()->getSettings()->set( 'smwgSemanticsEnabled', true );
+
+ return false;
+ }
+
+ /**
+ * Allows to run `update.php` with a bare-bone setup in cases where enabledSemantics
+ * has not yet been enabled.
+ *
+ * @since 2.4
+ */
+ public static function enableTemporaryCliUpdateMode() {
+ $GLOBALS['smwgSemanticsEnabled'] = true;
+ ApplicationFactory::getInstance()->getSettings()->set( 'smwgSemanticsEnabled', true );
+ ApplicationFactory::getInstance()->getSettings()->set( 'smwgPageSpecialProperties', [ '_MDAT' ] );
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function disableSemantics() {
+
+ $disabledSettings = [
+ 'smwgSemanticsEnabled' => false,
+ 'smwgNamespacesWithSemanticLinks' => [],
+ 'smwgQEnabled' => false,
+ 'smwgAutoRefreshOnPurge' => false,
+ 'smwgAutoRefreshOnPageMove' => false,
+ 'smwgFactboxCacheRefreshOnPurge' => false,
+ 'smwgAdminFeatures' => false,
+ 'smwgPageSpecialProperties' => [],
+ 'smwgEnableUpdateJobs' => false,
+ 'smwgEnabledEditPageHelp' => false,
+ 'smwgParserFeatures' => SMW_PARSER_NONE,
+ ];
+
+ foreach ( $disabledSettings as $key => $value) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ $GLOBALS[$key] = $value;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Connection/CallbackConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Connection/CallbackConnectionProvider.php
new file mode 100644
index 00000000..84d6a7fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Connection/CallbackConnectionProvider.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace SMW\Connection;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CallbackConnectionProvider implements ConnectionProvider {
+
+ /**
+ * @var callable
+ */
+ private $callback;
+
+ /**
+ * @var mixed
+ */
+ private $connection;
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $callback
+ */
+ public function __construct( callable $callback ) {
+ $this->callback = $callback;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return mixed
+ */
+ public function getConnection() {
+
+ if ( $this->connection === null ) {
+ $this->connection = call_user_func( $this->callback );
+ }
+
+ return $this->connection;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function releaseConnection() {
+ $this->connection = null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionManager.php b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionManager.php
new file mode 100644
index 00000000..4e16ffee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionManager.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Connection;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConnectionManager {
+
+ /**
+ * By design this variable is static to ensure that ConnectionProvider
+ * instances are only initialized once per request.
+ *
+ * @var array
+ */
+ private static $connectionProviders = [];
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $id
+ *
+ * @return mixed
+ * @throws RuntimeException
+ */
+ public function getConnection( $id = null ) {
+ return $this->findConnectionProvider( strtolower( $id ) )->getConnection();
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function releaseConnections() {
+ foreach ( self::$connectionProviders as $connectionProvider ) {
+ $connectionProvider->releaseConnection();
+ }
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $id
+ * @param ConnectionProvider $connectionProvider
+ */
+ public function registerConnectionProvider( $id, ConnectionProvider $connectionProvider ) {
+ self::$connectionProviders[strtolower( $id )] = $connectionProvider;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param callable $callback
+ */
+ public function registerCallbackConnection( $id, callable $callback ) {
+ self::$connectionProviders[strtolower( $id )] = new CallbackConnectionProvider( $callback );
+ }
+
+ private function findConnectionProvider( $id ) {
+
+ if ( isset( self::$connectionProviders[$id] ) ) {
+ return self::$connectionProviders[$id];
+ }
+
+ throw new RuntimeException( "{$id} is missing a registered connection provider" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProvider.php
new file mode 100644
index 00000000..ff305428
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProvider.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace SMW\Connection;
+
+/**
+ * Interface for database connection providers.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface ConnectionProvider {
+
+ /**
+ * Returns the database connection.
+ * Initialization of this connection is done if it was not already initialized.
+ *
+ * @since 1.9
+ */
+ public function getConnection();
+
+ /**
+ * Releases the connection if doing so makes any sense resource wise.
+ *
+ * @since 1.9
+ */
+ public function releaseConnection();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProviderRef.php b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProviderRef.php
new file mode 100644
index 00000000..471f3a35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Connection/ConnectionProviderRef.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Connection;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConnectionProviderRef {
+
+ /**
+ * @var array
+ */
+ private $connectionProviders = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $connectionProviders
+ */
+ public function __construct( array $connectionProviders ) {
+ $this->connectionProviders = $connectionProviders;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function hasConnection( $key ) {
+ return isset( $this->connectionProviders[$key] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return ConnectionProvider
+ * @throws RuntimeException
+ */
+ public function getConnection( $key ) {
+
+ if ( isset( $this->connectionProviders[$key] ) && $this->connectionProviders[$key] instanceof ConnectionProvider ) {
+ return $this->connectionProviders[$key]->getConnection();
+ }
+
+ throw new RuntimeException( "$key is unknown" );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function releaseConnection() {
+ foreach ( $this->connectionProviders as $connectionProvider ) {
+ $connectionProvider->releaseConnection();
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataItemFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/DataItemFactory.php
new file mode 100644
index 00000000..3ca95457
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataItemFactory.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW;
+
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDIContainer as DIContainer;
+use SMWDIError as DIError;
+use SMWDINumber as DINumber;
+use SMWDIUri as DIUri;
+use SMWDITime as DITime;
+use Title;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DataItemFactory {
+
+ /**
+ * @since 2.4
+ *
+ * @param string $error
+ *
+ * @return DIError
+ */
+ public function newDIError( $error ) {
+ return new DIError( $error );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ * @param boolean $inverse
+ *
+ * @return DIProperty
+ */
+ public function newDIProperty( $key, $inverse = false ) {
+ return new DIProperty( str_replace( ' ', '_', $key ), $inverse );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|Title $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobjectName
+ *
+ * @return DIWikiPage
+ */
+ public function newDIWikiPage( $title, $namespace = NS_MAIN, $interwiki = '', $subobjectName = '' ) {
+
+ if ( $title instanceof Title ) {
+ return DIWikiPage::newFromTitle( $title );
+ }
+
+ return new DIWikiPage( $title, $namespace, $interwiki, $subobjectName );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param ContainerSemanticData $containerSemanticData
+ *
+ * @return DIContainer
+ */
+ public function newDIContainer( ContainerSemanticData $containerSemanticData ) {
+ return new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return ContainerSemanticData
+ */
+ public function newContainerSemanticData( DIWikiPage $subject ) {
+ return new ContainerSemanticData( $subject );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $number
+ *
+ * @return DINumber
+ */
+ public function newDINumber( $number ) {
+ return new DINumber( $number );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $text
+ *
+ * @return DIBlob
+ */
+ public function newDIBlob( $text ) {
+ return new DIBlob( $text );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param boolean $boolean
+ *
+ * @return DIBoolean
+ */
+ public function newDIBoolean( $boolean ) {
+ return new DIBoolean( $boolean );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $concept
+ * @param string $docu
+ * @param integer $queryfeatures
+ * @param integer $size
+ * @param integer $depth
+ *
+ * @return DIConcept
+ */
+ public function newDIConcept( $concept, $docu = '', $queryfeatures = 0, $size = 0, $depth = 0 ) {
+ return new DIConcept( $concept, $docu, $queryfeatures, $size, $depth );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $scheme
+ * @param string $hierpart
+ * @param string $query
+ * @param string $fragment
+ *
+ * @return DIUri
+ */
+ public function newDIUri( $scheme, $hierpart, $query = '', $fragment = '' ) {
+ return new DIUri( $scheme, $hierpart, $query, $fragment );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $calendarmodel
+ * @param integer $year
+ * @param integer|false $month
+ * @param integer|false $day
+ * @param integer|false $hour
+ * @param integer|false $minute
+ * @param integer|false $second
+ * @param integer|false $timezone
+ *
+ * @return DITime
+ */
+ public function newDITime( $calendarmodel, $year, $month = false, $day = false, $hour = false, $minute = false, $second = false, $timezone = false ) {
+ return new DITime( $calendarmodel, $year, $month, $day, $hour, $minute, $second, $timezone );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataModel/ContainerSemanticData.php b/www/wiki/extensions/SemanticMediaWiki/src/DataModel/ContainerSemanticData.php
new file mode 100644
index 00000000..38288940
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataModel/ContainerSemanticData.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW\DataModel;
+
+use SMW\DIWikiPage;
+use SMW\Exception\DataItemException;
+use SMW\SemanticData;
+
+/**
+ * Subclass of SemanticData that is used to store the data in SMWDIContainer
+ * objects. It is special since the subject that the stored property-value pairs
+ * refer may or may not be specified explicitly. This can be tested with
+ * hasAnonymousSubject(). When trying to access the subject in anonymous state,
+ * an Exception will be thrown.
+ *
+ * Anonymous container data items are used when no
+ * page context is available, e.g. when specifying such a value in a search form
+ * where the parent page is not known.
+ *
+ * Besides this change, the subclass mainly is needed to restore the disabled
+ * serialization of SemanticData.
+ *
+ * See also the documentation of SMWDIContainer.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ContainerSemanticData extends SemanticData {
+
+ /**
+ * @var boolean
+ */
+ private $skipAnonymousCheck = false;
+
+ /**
+ * Construct a data container that refers to an anonymous subject. See
+ * the documentation of the class for details.
+ *
+ * @since 1.7
+ *
+ * @param boolean $noDuplicates stating if duplicate data should be avoided
+ */
+ public static function makeAnonymousContainer( $noDuplicates = true, $skipAnonymousCheck = false ) {
+
+ $containerSemanticData = new ContainerSemanticData(
+ new DIWikiPage( 'SMWInternalObject', NS_SPECIAL, '', 'int' ),
+ $noDuplicates
+ );
+
+ if ( $skipAnonymousCheck ) {
+ $containerSemanticData->skipAnonymousCheck();
+ }
+
+ return $containerSemanticData;
+ }
+
+ /**
+ * Restore complete serialization which is disabled in SemanticData.
+ */
+ public function __sleep() {
+ return [
+ 'mSubject',
+ 'mProperties',
+ 'mPropVals',
+ 'mHasVisibleProps',
+ 'mHasVisibleSpecs',
+ 'mNoDuplicates',
+ 'skipAnonymousCheck',
+ 'subSemanticData',
+ 'options',
+ 'extensionData'
+ ];
+ }
+
+ /**
+ * Skip the check as it is required for some "search pattern match" activity
+ * to temporarily to access the container without raising an exception.
+ *
+ * @since 2.4
+ */
+ public function skipAnonymousCheck() {
+ $this->skipAnonymousCheck = true;
+ }
+
+ /**
+ * Check if the subject of this container is an anonymous object.
+ * See the documenation of the class for details.
+ *
+ * @return boolean
+ */
+ public function hasAnonymousSubject() {
+
+ if ( $this->mSubject->getNamespace() == NS_SPECIAL &&
+ $this->mSubject->getDBkey() == 'SMWInternalObject' &&
+ $this->mSubject->getInterwiki() === '' &&
+ $this->mSubject->getSubobjectName() === 'int' ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return subject to which the stored semantic annotation refer to, or
+ * throw an exception if the subject is anonymous (if the data has not
+ * been contextualized with setMasterPage() yet).
+ *
+ * @return DIWikiPage subject
+ * @throws DataItemException
+ */
+ public function getSubject() {
+
+ $error = "This container has been classified as anonymous and by trying to access" .
+ " its subject (that has not been given any) an exception is raised to inform about" .
+ " the incorrect usage. An anonymous container can only be used for a search pattern match.";
+
+ if ( !$this->skipAnonymousCheck && $this->hasAnonymousSubject() ) {
+ throw new DataItemException( $error );
+ }
+
+ return $this->mSubject;
+ }
+
+ /**
+ * Change the object to become an exact copy of the given
+ * SemanticData object. This is used to make other types of
+ * SemanticData into an SMWContainerSemanticData. To copy objects of
+ * the same type, PHP clone() should be used.
+ *
+ * @since 1.7
+ *
+ * @param SemanticData|null $semanticData
+ */
+ public function copyDataFrom( SemanticData $semanticData = null ) {
+
+ if ( $semanticData === null ) {
+ return;
+ }
+
+ $this->mSubject = $semanticData->getSubject();
+ $this->mProperties = $semanticData->getProperties();
+ $this->mPropVals = [];
+
+ foreach ( $this->mProperties as $property ) {
+ $this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
+ }
+
+ $this->mHasVisibleProps = $semanticData->hasVisibleProperties();
+ $this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
+ $this->mNoDuplicates = $semanticData->mNoDuplicates;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataModel/SubSemanticData.php b/www/wiki/extensions/SemanticMediaWiki/src/DataModel/SubSemanticData.php
new file mode 100644
index 00000000..89fa42a3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataModel/SubSemanticData.php
@@ -0,0 +1,287 @@
+<?php
+
+namespace SMW\DataModel;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exception\SubSemanticDataException;
+use SMW\SemanticData;
+
+/**
+ * @private
+ *
+ * Internal handling of the SubSemanticData container and its subsequent
+ * add and remove operations.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class SubSemanticData {
+
+ /**
+ * States whether repeated values should be avoided. Not needing
+ * duplicate elimination (e.g. when loading from store) can save some
+ * time, especially in subclasses like SMWSqlStubSemanticData, where
+ * the first access to a data item is more costy.
+ *
+ * @note This setting is merely for optimization. The SMW data model
+ * never cares about the multiplicity of identical data assignments.
+ *
+ * @var boolean
+ */
+ private $noDuplicates;
+
+ /**
+ * DIWikiPage object that is the subject of this container.
+ * Subjects can never be null (and this is ensured in all methods setting
+ * them in this class).
+ *
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * Semantic data associated to subobjects of the subject of this
+ * SMWSemanticData.
+ * These key-value pairs of subObjectName (string) =>SMWSemanticData.
+ *
+ * @since 2.5
+ * @var SemanticData[]
+ */
+ private $subSemanticData = [];
+
+ /**
+ * Internal flag that indicates if this semantic data will accept
+ * subdata. Semantic data objects that are subdata already do not allow
+ * (second level) subdata to be added. This ensures that all data is
+ * collected on the top level, and in particular that there is only one
+ * way to represent the same data with subdata. This is also useful for
+ * diff computation.
+ *
+ * @var boolean
+ */
+ private $subDataAllowed = true;
+
+ /**
+ * Maximum depth for an recursive sub data assignment
+ *
+ * @var integer
+ */
+ private $subContainerMaxDepth = 3;
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param boolean $noDuplicates stating if duplicate data should be avoided
+ */
+ public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
+ $this->clear();
+ $this->subject = $subject;
+ $this->noDuplicates = $noDuplicates;
+ }
+
+ /**
+ * This object is added to the parser output of MediaWiki, but it is
+ * not useful to have all its data as part of the parser cache since
+ * the data is already stored in more accessible format in SMW. Hence
+ * this implementation of __sleep() makes sure only the subject is
+ * serialised, yielding a minimal stub data container after
+ * unserialisation. This is a little safer than serialising nothing:
+ * if, for any reason, SMW should ever access an unserialised parser
+ * output, then the Semdata container will at least look as if properly
+ * initialised (though empty).
+ *
+ * @return array
+ */
+ public function __sleep() {
+ return [ 'subject', 'subSemanticData' ];
+ }
+
+ /**
+ * Return subject to which the stored semantic annotations refer to.
+ *
+ * @return DIWikiPage subject
+ */
+ public function getSubject() {
+ return $this->subject;
+ }
+
+ /**
+ * This is used as contingency where the serialized SementicData still
+ * has an array object reference.
+ *
+ * @since 2.5
+ *
+ * @return ContainerSemanticData[]
+ */
+ public function copyDataFrom( array $subSemanticData ) {
+ $this->subSemanticData = $subSemanticData;
+ }
+
+ /**
+ * Return the array of subSemanticData objects in form of
+ * subobjectName => ContainerSemanticData
+ *
+ * @since 2.5
+ *
+ * @return ContainerSemanticData[]
+ */
+ public function getSubSemanticData() {
+ return $this->subSemanticData;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function clear() {
+ $this->subSemanticData = [];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $subobjectName|null
+ *
+ * @return boolean
+ */
+ public function hasSubSemanticData( $subobjectName = null ) {
+
+ if ( $this->subSemanticData === [] || $subobjectName === '' ) {
+ return false;
+ }
+
+ return $subobjectName !== null ? isset( $this->subSemanticData[$subobjectName] ) : true;
+ }
+
+ /**
+ * Find a particular subobject container using its name as identifier
+ *
+ * @since 2.5
+ *
+ * @param string $subobjectName
+ *
+ * @return ContainerSemanticData|null
+ */
+ public function findSubSemanticData( $subobjectName ) {
+
+ if ( $this->hasSubSemanticData( $subobjectName ) && isset( $this->subSemanticData[$subobjectName] ) ) {
+ return $this->subSemanticData[$subobjectName];
+ }
+
+ return null;
+ }
+
+ /**
+ * Add data about subobjects
+ *
+ * Will only work if the data that is added is about a subobject of
+ * this SMWSemanticData's subject. Otherwise an exception is thrown.
+ * The SMWSemanticData object that is given will belong to this object
+ * after the operation; it should not be modified further by the caller.
+ *
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ *
+ * @throws SubSemanticDataException if not adding data about a subobject of this data
+ */
+ public function addSubSemanticData( SemanticData $semanticData ) {
+
+ if ( $semanticData->subContainerDepthCounter > $this->subContainerMaxDepth ) {
+ throw new SubSemanticDataException( "Cannot add further subdata with the depth of {$semanticData->subContainerDepthCounter}. You are trying to add data beyond the max depth of {$this->subContainerMaxDepth} to an SemanticData object." );
+ }
+
+ $subobjectName = $semanticData->getSubject()->getSubobjectName();
+
+ if ( $subobjectName == '' ) {
+ throw new SubSemanticDataException( "Cannot add data that is not about a subobject." );
+ }
+
+ if ( $semanticData->getSubject()->getDBkey() !== $this->getSubject()->getDBkey() ) {
+ throw new SubSemanticDataException( "Data for a subobject of {$semanticData->getSubject()->getDBkey()} cannot be added to {$this->getSubject()->getDBkey()}." );
+ }
+
+ $this->appendSubSemanticData( $semanticData, $subobjectName );
+ }
+
+ /**
+ * Remove data about a subobject
+ *
+ * If the removed data is not about a subobject of this object,
+ * it will silently be ignored (nothing to remove). Likewise,
+ * removing data that is not present does not change anything.
+ *
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ */
+ public function removeSubSemanticData( SemanticData $semanticData ) {
+
+ if ( $semanticData->getSubject()->getDBkey() !== $this->getSubject()->getDBkey() ) {
+ return;
+ }
+
+ $subobjectName = $semanticData->getSubject()->getSubobjectName();
+
+ if ( $this->hasSubSemanticData( $subobjectName ) ) {
+ $this->subSemanticData[$subobjectName]->removeDataFrom( $semanticData );
+
+ if ( $this->subSemanticData[$subobjectName]->isEmpty() ) {
+ unset( $this->subSemanticData[$subobjectName] );
+ }
+ }
+ }
+
+ /**
+ * Remove property and all values associated with this property.
+ *
+ * @since 2.5
+ *
+ * @param $property DIProperty
+ */
+ public function removeProperty( DIProperty $property ) {
+
+ // Inverse properties cannot be used for an annotation
+ if ( $property->isInverse() ) {
+ return;
+ }
+
+ foreach ( $this->subSemanticData as $containerSemanticData ) {
+ $containerSemanticData->removeProperty( $property );
+ }
+ }
+
+ private function appendSubSemanticData( $semanticData, $subobjectName ) {
+
+ if ( $this->hasSubSemanticData( $subobjectName ) ) {
+ $this->subSemanticData[$subobjectName]->importDataFrom( $semanticData );
+
+ foreach ( $semanticData->getSubSemanticData() as $containerSemanticData ) {
+ $this->addSubSemanticData( $containerSemanticData );
+ }
+
+ return;
+ }
+
+ $semanticData->subContainerDepthCounter++;
+
+ foreach ( $semanticData->getSubSemanticData() as $containerSemanticData ) {
+
+ // Skip container that are known to be registered (avoids recursive statement extension)
+ if ( $this->hasSubSemanticData( $containerSemanticData->getSubject()->getSubobjectName() ) ) {
+ continue;
+ }
+
+ $this->addSubSemanticData( $containerSemanticData );
+ }
+
+ $semanticData->clearSubSemanticData();
+ $this->subSemanticData[$subobjectName] = $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataTypeRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/DataTypeRegistry.php
new file mode 100644
index 00000000..92e43ea2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataTypeRegistry.php
@@ -0,0 +1,616 @@
+<?php
+
+namespace SMW;
+
+use SMW\DataValues\TypeList;
+use SMW\Lang\Lang;
+use SMWDataItem as DataItem;
+
+/**
+ * DataTypes registry class
+ *
+ * Registry class that manages datatypes, and provides various methods to access
+ * the information
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class DataTypeRegistry {
+
+ /**
+ * @var DataTypeRegistry
+ */
+ protected static $instance = null;
+
+ /**
+ * @var Lang
+ */
+ private $lang;
+
+ /**
+ * Array of type labels indexed by type ids. Used for datatype resolution.
+ *
+ * @var string[]
+ */
+ private $typeLabels = [];
+
+ /**
+ * Array of ids indexed by type aliases. Used for datatype resolution.
+ *
+ * @var string[]
+ */
+ private $typeAliases = [];
+
+ /**
+ * @var string[]
+ */
+ private $canonicalLabels = [];
+
+ /**
+ * Array of class names for creating new SMWDataValue, indexed by type
+ * id.
+ *
+ * @var string[]
+ */
+ private $typeClasses;
+
+ /**
+ * Array of data item classes, indexed by type id.
+ *
+ * @var integer[]
+ */
+ private $typeDataItemIds;
+
+ /**
+ * @var string[]
+ */
+ private $subDataTypes = [];
+
+ /**
+ * @var []
+ */
+ private $browsableTypes = [];
+
+ /**
+ * Lookup map that allows finding a datatype id given a label or alias.
+ * All labels and aliases (ie array keys) are stored lower case.
+ *
+ * @var string[]
+ */
+ private $typeByLabelOrAliasLookup = [];
+
+ /**
+ * Array of default types to use for making datavalues for dataitems.
+ *
+ * @var string[]
+ */
+ private $defaultDataItemTypeMap = [
+ DataItem::TYPE_BLOB => '_txt', // Text type
+ DataItem::TYPE_URI => '_uri', // URL/URI type
+ DataItem::TYPE_WIKIPAGE => '_wpg', // Page type
+ DataItem::TYPE_NUMBER => '_num', // Number type
+ DataItem::TYPE_TIME => '_dat', // Time type
+ DataItem::TYPE_BOOLEAN => '_boo', // Boolean type
+ DataItem::TYPE_CONTAINER => '_rec', // Value list type (replacing former nary properties)
+ DataItem::TYPE_GEO => '_geo', // Geographical coordinates
+ DataItem::TYPE_CONCEPT => '__con', // Special concept page type
+ DataItem::TYPE_PROPERTY => '__pro', // Property type
+
+ // If either of the following two occurs, we want to see a PHP error:
+ //DataItem::TYPE_NOTYPE => '',
+ //DataItem::TYPE_ERROR => '',
+ ];
+
+ /**
+ * @var Closure[]
+ */
+ private $extraneousFunctions = [];
+
+ /**
+ * @var []
+ */
+ private $extenstionData = [];
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * Returns a DataTypeRegistry instance
+ *
+ * @since 1.9
+ *
+ * @return DataTypeRegistry
+ */
+ public static function getInstance() {
+
+ if ( self::$instance !== null ) {
+ return self::$instance;
+ }
+
+ $lang = Localizer::getInstance()->getLang();
+
+ self::$instance = new self(
+ $lang
+ );
+
+ self::$instance->initDatatypes(
+ TypesRegistry::getDataTypeList()
+ );
+
+ self::$instance->setOption(
+ 'smwgDVFeatures',
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgDVFeatures' )
+ );
+
+ return self::$instance;
+ }
+
+ /**
+ * Resets the DataTypeRegistry instance
+ *
+ * @since 1.9
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 1.9.0.2
+ *
+ * @param Lang $lang
+ */
+ public function __construct( Lang $lang ) {
+ $this->lang = $lang;
+ $this->registerLabels();
+ }
+
+ /**
+ * @deprecated since 2.5, use DataTypeRegistry::getDataItemByType
+ */
+ public function getDataItemId( $typeId ) {
+ return $this->getDataItemByType( $typeId );
+ }
+
+ /**
+ * Get the preferred data item ID for a given type. The ID defines the
+ * appropriate data item class for processing data of this type. See
+ * DataItem for possible values.
+ *
+ * @note SMWDIContainer is a pseudo dataitem type that is used only in
+ * data input methods, but not for storing data. Types that work with
+ * SMWDIContainer use SMWDIWikiPage as their DI type. (Since SMW 1.8)
+ *
+ * @param $typeId string id string for the given type
+ * @return integer data item ID
+ */
+ public function getDataItemByType( $typeId ) {
+
+ if ( isset( $this->typeDataItemIds[$typeId] ) ) {
+ return $this->typeDataItemIds[$typeId];
+ }
+
+ return DataItem::TYPE_NOTYPE;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string
+ *
+ * @return boolean
+ */
+ public function isRegistered( $typeId ) {
+ return isset( $this->typeDataItemIds[$typeId] );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeId
+ *
+ * @return boolean
+ */
+ public function isSubDataType( $typeId ) {
+ return isset( $this->subDataTypes[$typeId] ) && $this->subDataTypes[$typeId];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $typeId
+ *
+ * @return boolean
+ */
+ public function isBrowsableType( $typeId ) {
+ return isset( $this->browsableTypes[$typeId] ) && $this->browsableTypes[$typeId];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $srcType
+ * @param string $tagType
+ *
+ * @return boolean
+ */
+ public function isEqualByType( $srcType, $tagType ) {
+ return $this->getDataItemByType( $srcType ) === $this->getDataItemByType( $tagType );
+ }
+
+ /**
+ * A function for registering/overwriting datatypes for SMW. Should be
+ * called from within the hook 'smwInitDatatypes'.
+ *
+ * @param $id string type ID for which this datatype is registered
+ * @param $className string name of the according subclass of SMWDataValue
+ * @param $dataItemId integer ID of the data item class that this data value uses, see DataItem
+ * @param $label mixed string label or false for types that cannot be accessed by users
+ * @param boolean $isSubDataType
+ * @param boolean $isBrowsableType
+ */
+ public function registerDataType( $id, $className, $dataItemId, $label = false, $isSubDataType = false, $isBrowsableType = false ) {
+ $this->typeClasses[$id] = $className;
+ $this->typeDataItemIds[$id] = $dataItemId;
+ $this->subDataTypes[$id] = $isSubDataType;
+ $this->browsableTypes[$id] = $isBrowsableType;
+
+ if ( $label !== false ) {
+ $this->registerTypeLabel( $id, $label );
+ }
+ }
+
+ private function registerTypeLabel( $typeId, $typeLabel ) {
+ $this->typeLabels[$typeId] = $typeLabel;
+ $this->addTextToIdLookupMap( $typeId, $typeLabel );
+ }
+
+ private function addTextToIdLookupMap( $dataTypeId, $text ) {
+ $this->typeByLabelOrAliasLookup[mb_strtolower($text)] = $dataTypeId;
+ }
+
+ /**
+ * Add a new alias label to an existing datatype id. Note that every ID
+ * should have a primary label, either provided by SMW or registered with
+ * registerDataType(). This function should be called from within the hook
+ * 'smwInitDatatypes'.
+ *
+ * @param string $typeId
+ * @param string $typeAlias
+ */
+ public function registerDataTypeAlias( $typeId, $typeAlias ) {
+ $this->typeAliases[$typeAlias] = $typeId;
+ $this->addTextToIdLookupMap( $typeId, $typeAlias );
+ }
+
+ /**
+ * @deprecated since 3.0, use DataTypeRegistry::findTypeByLabel
+ */
+ public function findTypeId( $label ) {
+ return $this->findTypeByLabel( $label );
+ }
+
+ /**
+ * Look up the ID that identifies the datatype of the given label
+ * internally. This id is used for all internal operations. If the
+ * label does not belong to a known type, the empty string is returned.
+ *
+ * @since 3.0
+ *
+ * @param string $label
+ *
+ * @return string
+ */
+ public function findTypeByLabel( $label ) {
+
+ $label = mb_strtolower( $label );
+
+ if ( isset( $this->typeByLabelOrAliasLookup[$label] ) ) {
+ return $this->typeByLabelOrAliasLookup[$label];
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $label
+ * @param string|false $languageCode
+ *
+ * @return string
+ */
+ public function findTypeByLabelAndLanguage( $label, $languageCode = false ) {
+
+ if ( !$languageCode ) {
+ return $this->findTypeByLabel( $label );
+ }
+
+ $lang = $this->lang->fetch(
+ $languageCode
+ );
+
+ return $lang->findDatatypeByLabel( $label );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getFieldType( $type ) {
+
+ if ( isset( $this->typeDataItemIds[$type] ) ) {
+ return $this->defaultDataItemTypeMap[ $this->typeDataItemIds[$type]];
+ }
+
+ return '_wpg';
+ }
+
+ /**
+ * Get the translated user label for a given internal ID. If the ID does
+ * not have a label associated with it in the current language, the
+ * empty string is returned. This is the case both for internal type ids
+ * and for invalid (unknown) type ids, so this method cannot be used to
+ * distinguish the two.
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function findTypeLabel( $id ) {
+
+ if ( isset( $this->typeLabels[$id] ) ) {
+ return $this->typeLabels[$id];
+ }
+
+ // internal type without translation to user space;
+ // might also happen for historic types after an upgrade --
+ // alas, we have no idea what the former label would have been
+ return '';
+ }
+
+ /**
+ * Returns a label for a typeId that is independent from the user/content
+ * language
+ *
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function findCanonicalLabelById( $id ) {
+
+ if ( isset( $this->canonicalLabels[$id] ) ) {
+ return $this->canonicalLabels[$id];
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getCanonicalDatatypeLabels() {
+ return $this->canonicalLabels;
+ }
+
+ /**
+ * Return an array of all labels that a user might specify as the type of
+ * a property, and that are internal (i.e. not user defined). No labels are
+ * returned for internal types without user labels (e.g. the special types
+ * for some special properties), and for user defined types.
+ *
+ * @return array
+ */
+ public function getKnownTypeLabels() {
+ return $this->typeLabels;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getKnownTypeAliases() {
+ return $this->typeAliases;
+ }
+
+ /**
+ * @deprecated since 2.5, use DataTypeRegistry::getDefaultDataItemByType
+ */
+ public function getDefaultDataItemTypeId( $diType ) {
+ return $this->getDefaultDataItemByType( $diType );
+ }
+
+ /**
+ * Returns a default DataItem for a matchable type ID
+ *
+ * @since 2.5
+ *
+ * @param string $diType
+ *
+ * @return string|null
+ */
+ public function getDefaultDataItemByType( $typeId ) {
+
+ if ( isset( $this->defaultDataItemTypeMap[$typeId] ) ) {
+ return $this->defaultDataItemTypeMap[$typeId];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a class based on a typeId
+ *
+ * @since 1.9
+ *
+ * @param string $typeId
+ *
+ * @return string|null
+ */
+ public function getDataTypeClassById( $typeId ) {
+
+ if ( $this->hasDataTypeClassById( $typeId ) ) {
+ return $this->typeClasses[$typeId];
+ }
+
+ return null;
+ }
+
+ /**
+ * Whether a datatype class is registered for a particular typeId
+ *
+ * @since 1.9
+ *
+ * @param string $typeId
+ *
+ * @return boolean
+ */
+ public function hasDataTypeClassById( $typeId ) {
+ return isset( $this->typeClasses[$typeId] ) && class_exists( $this->typeClasses[$typeId] );
+ }
+
+ /**
+ * Gather all available datatypes and label<=>id<=>datatype
+ * associations. This method is called before most methods of this
+ * factory.
+ */
+ protected function initDatatypes( array $typeList ) {
+
+ foreach ( $typeList as $id => $definition ) {
+
+ if ( isset( $definition[0] ) ) {
+ $this->typeClasses[$id] = $definition[0];
+ }
+
+ $this->typeDataItemIds[$id] = $definition[1];
+ $this->subDataTypes[$id] = $definition[2];
+ $this->browsableTypes[$id] = $definition[3];
+ }
+
+ // Deprecated since 1.9
+ \Hooks::run( 'smwInitDatatypes' );
+
+ // Since 1.9
+ \Hooks::run( 'SMW::DataType::initTypes', [ $this ] );
+ }
+
+ /**
+ * @deprecated since 3.0, use DataTypeRegistry::setExtensionData
+ * Inject services and objects that are planned to be used during the invocation of
+ * a DataValue
+ *
+ * @since 2.3
+ *
+ * @param string $name
+ * @param \Closure $callback
+ */
+ public function registerExtraneousFunction( $name, \Closure $callback ) {
+ $this->extraneousFunctions[$name] = $callback;
+ }
+
+ /**
+ * @deprecated since 3.0, use DataTypeRegistry::getExtensionData
+ * @since 2.3
+ *
+ * @return Closure[]
+ */
+ public function getExtraneousFunctions() {
+ return $this->extraneousFunctions;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Options
+ */
+ public function getOptions() {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+ $this->getOptions()->set( $key, $value );
+ }
+
+ /**
+ * This function allows for registered types to add additional data or functions
+ * required by an individual DataValue of that type.
+ *
+ * Register the data:
+ * $dataTypeRegistry = DataTypeRegistry::getInstance();
+ *
+ * $dataTypeRegistry->registerDataType( '__foo', ... );
+ * $dataTypeRegistry->setExtensionData( '__foo', [ 'ext.function' => ... ] );
+ * ...
+ *
+ * Access the data:
+ * $dataValueFactory = DataValueFactory::getInstance();
+ *
+ * $dataValue = $dataValueFactory->newDataValueByType( '__foo' );
+ * $dataValue->getExtensionData( 'ext.function' )
+ * ...
+ *
+ * @since 3.0
+ *
+ * @param string $id
+ * @param array $data
+ */
+ public function setExtensionData( $id, array $data = [] ) {
+ if ( $this->isRegistered( $id ) ) {
+ $this->extenstionData[$id] = $data;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ *
+ * @return []
+ */
+ public function getExtensionData( $id ) {
+
+ if ( isset( $this->extenstionData[$id] ) ) {
+ return $this->extenstionData[$id];
+ }
+
+ return [];
+ }
+
+ private function registerLabels() {
+
+ foreach ( $this->lang->getDatatypeLabels() as $typeId => $typeLabel ) {
+ $this->registerTypeLabel( $typeId, $typeLabel );
+ }
+
+ foreach ( $this->lang->getDatatypeAliases() as $typeAlias => $typeId ) {
+ $this->registerDataTypeAlias( $typeId, $typeAlias );
+ }
+
+ foreach ( $this->lang->getCanonicalDatatypeLabels() as $label => $id ) {
+ $this->canonicalLabels[$id] = $label;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/DataUpdater.php
new file mode 100644
index 00000000..b9aef739
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataUpdater.php
@@ -0,0 +1,430 @@
+<?php
+
+namespace SMW;
+
+use Title;
+use User;
+use WikiPage;
+
+/**
+ * This function takes care of storing the collected semantic data and
+ * clearing out any outdated entries for the processed page. It assumes
+ * that parsing has happened and that all relevant information are
+ * contained and provided for.
+ *
+ * Optionally, this function also takes care of triggering indirect updates
+ * that might be needed for an overall database consistency. If the saved page
+ * describes a property or data type, the method checks whether the property
+ * type, the data type, the allowed values, or the conversion factors have
+ * changed.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DataUpdater {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData;
+
+ /**
+ * @var TransactionalCallableUpdate
+ */
+ private $transactionalCallableUpdate;
+
+ /**
+ * @var boolean|null
+ */
+ private $canCreateUpdateJob = null;
+
+ /**
+ * @var boolean
+ */
+ private $processSemantics = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @var boolean|string
+ */
+ private $isChangeProp = false;
+
+ /**
+ * @var boolean
+ */
+ private $isDeferrableUpdate = false;
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param SemanticData $semanticData
+ */
+ public function __construct( Store $store, SemanticData $semanticData ) {
+ $this->store = $store;
+ $this->semanticData = $semanticData;
+ $this->transactionalCallableUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode.
+ *
+ * @since 3.0
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isChangeProp
+ */
+ public function isChangeProp( $isChangeProp ) {
+ $this->isChangeProp = (bool)$isChangeProp;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isChangeProp
+ */
+ public function isDeferrableUpdate( $isDeferrableUpdate ) {
+ $this->isDeferrableUpdate = (bool)$isDeferrableUpdate;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->semanticData->getSubject();
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param boolean $canCreateUpdateJob
+ */
+ public function canCreateUpdateJob( $canCreateUpdateJob ) {
+ $this->canCreateUpdateJob = (bool)$canCreateUpdateJob;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function doUpdate() {
+
+ if ( !$this->canPerformUpdate() ) {
+ return false;
+ }
+
+ DeferredCallableUpdate::releasePendingUpdates();
+
+ if ( $this->isDeferrableUpdate === false || $this->isCommandLineMode ) {
+ return $this->performUpdate();
+ }
+
+ $this->transactionalCallableUpdate->setCallback( function() {
+ $this->performUpdate();
+ } );
+
+ $this->transactionalCallableUpdate->setOrigin(
+ [
+ __METHOD__,
+ $this->origin,
+ $this->getSubject()->getHash()
+ ]
+ );
+
+ $this->transactionalCallableUpdate->isDeferrableUpdate(
+ $this->isDeferrableUpdate
+ );
+
+ $this->transactionalCallableUpdate->commitWithTransactionTicket();
+ $this->transactionalCallableUpdate->pushUpdate();
+
+ return true;
+ }
+
+ private function canPerformUpdate() {
+
+ $title = $this->getSubject()->getTitle();
+
+ // Protect against null and namespace -1 see Bug 50153
+ if ( $title === null || $title->isSpecialPage() ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @note Make sure to have a valid revision (null means delete etc.) and
+ * check if semantic data should be processed and displayed for a page in
+ * the given namespace
+ */
+ private function performUpdate() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ if ( $this->canCreateUpdateJob === null ) {
+ $this->canCreateUpdateJob( $applicationFactory->getSettings()->get( 'smwgEnableUpdateJobs' ) );
+ }
+
+ $title = $this->getSubject()->getTitle();
+ $wikiPage = $applicationFactory->newPageCreator()->createPage( $title );
+
+ $revision = $wikiPage->getRevision();
+ $user = $revision !== null ? User::newFromId( $revision->getUser() ) : null;
+
+ $this->addAnnotations( $title, $wikiPage, $revision, $user );
+
+ // In case of a restricted update, only the protection update is required
+ // hence the process bails-out early to avoid unnecessary DB connections
+ // or updates
+ if ( $this->checkUpdateEditProtection( $wikiPage, $user ) === true ) {
+ return true;
+ }
+
+ $this->checkChangePropagation();
+ $this->updateData();
+
+ if ( $this->semanticData->getOption( Enum::PURGE_ASSOC_PARSERCACHE ) === true ) {
+ $jobQueue = $applicationFactory->getJobQueue();
+ $jobQueue->runFromQueue( [ 'SMW\ParserCachePurgeJob' => 2 ] );
+ }
+
+ return true;
+ }
+
+ private function addAnnotations( Title $title, WikiPage $wikiPage, $revision, $user ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ if ( $revision !== null ) {
+ $this->processSemantics = $applicationFactory->getNamespaceExaminer()->isSemanticEnabled( $title->getNamespace() );
+ }
+
+ if ( !$this->processSemantics ) {
+ return $this->semanticData = new SemanticData( $this->getSubject() );
+ }
+
+ $pageInfoProvider = $applicationFactory->newMwCollaboratorFactory()->newPageInfoProvider(
+ $wikiPage,
+ $revision,
+ $user
+ );
+
+ $this->semanticData->setExtensionData( 'revision_id', $revision->getId() );
+
+ $propertyAnnotatorFactory = $applicationFactory->singleton( 'PropertyAnnotatorFactory' );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newNullPropertyAnnotator(
+ $this->semanticData
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newPredefinedPropertyAnnotator(
+ $propertyAnnotator,
+ $pageInfoProvider
+ );
+
+ // Standard text hooks are not run through a JSON content object therefore
+ // we attach possible annotations at this point
+ if ( $title->getNamespace() === SMW_NS_SCHEMA ) {
+
+ $schemaFactory = $applicationFactory->singleton( 'SchemaFactory' );
+
+ try {
+ $schema = $schemaFactory->newSchema(
+ $title->getDBKey(),
+ $pageInfoProvider->getNativeData()
+ );
+ } catch ( \Exception $e ) {
+ $schema = null;
+ }
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newSchemaPropertyAnnotator(
+ $propertyAnnotator,
+ $schema
+ );
+ }
+
+ $propertyAnnotator->addAnnotation();
+
+ \Hooks::run(
+ 'SMW::DataUpdater::ContentProcessor',
+ [
+ $this->semanticData,
+ $wikiPage->getContent()
+ ]
+ );
+ }
+
+ private function checkUpdateEditProtection( $wikiPage, $user ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $editProtectionUpdater = $applicationFactory->create( 'EditProtectionUpdater',
+ $wikiPage,
+ $user
+ );
+
+ $editProtectionUpdater->doUpdateFrom( $this->semanticData );
+
+ return $editProtectionUpdater->isRestrictedUpdate();
+ }
+
+ /**
+ * @note Comparison must happen *before* the storage update;
+ * even finding uses of a property fails after its type changed.
+ */
+ private function checkChangePropagation() {
+
+ // canCreateUpdateJob: if it is not enabled there's not much to do here
+ // isChangeProp: means the update is part of the ChangePropagationDispatchJob
+ // therefore skip
+ if ( !$this->canCreateUpdateJob || $this->isChangeProp ) {
+ return;
+ }
+
+ $namespace = $this->semanticData->getSubject()->getNamespace();
+
+ if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) {
+ return;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $propertyChangePropagationNotifier = new PropertyChangePropagationNotifier(
+ $this->store,
+ $applicationFactory->newSerializerFactory()
+ );
+
+ $propertyChangePropagationNotifier->setPropertyList(
+ $applicationFactory->getSettings()->get( 'smwgChangePropagationWatchlist' )
+ );
+
+ $propertyChangePropagationNotifier->isCommandLineMode(
+ $this->isCommandLineMode
+ );
+
+ $propertyChangePropagationNotifier->checkAndNotify(
+ $this->semanticData
+ );
+ }
+
+ private function updateData() {
+
+ $this->store->setOption(
+ Store::OPT_CREATE_UPDATE_JOB,
+ $this->canCreateUpdateJob
+ );
+
+ $semanticData = $this->checkOnRequiredRedirectUpdate(
+ $this->semanticData
+ );
+
+ $subject = $semanticData->getSubject();
+
+ if ( $this->processSemantics ) {
+ $this->store->updateData( $semanticData );
+ } elseif ( $this->store->getObjectIds()->exists( $subject ) ) {
+ // Only clear the data where it is know that "exists" is true otherwise
+ // an empty entity is created and later being removed by the
+ // "PropertyTableOutdatedReferenceDisposer" since it is an entity that is
+ // empty == has no reference
+ $this->store->clearData( $subject );
+ }
+
+ return true;
+ }
+
+ private function checkOnRequiredRedirectUpdate( SemanticData $semanticData ) {
+
+ // Check only during online-mode so that when a user operates Special:MovePage
+ // or #redirect the same process is applied
+ if ( !$this->canCreateUpdateJob ) {
+ return $semanticData;
+ }
+
+ $redirects = $semanticData->getPropertyValues(
+ new DIProperty( '_REDI' )
+ );
+
+ if ( $redirects !== [] && !$semanticData->getSubject()->equals( end( $redirects ) ) ) {
+ return $this->doUpdateUnknownRedirectTarget( $semanticData, end( $redirects ) );
+ }
+
+ return $semanticData;
+ }
+
+ private function doUpdateUnknownRedirectTarget( SemanticData $semanticData, DIWikiPage $target ) {
+
+ // Only keep the reference to safeguard that even in case of a text keeping
+ // its annotations there are removed from the Store. A redirect is not
+ // expected to contain any other annotation other than that of the redirect
+ // target
+ $subject = $semanticData->getSubject();
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ $target
+ );
+
+ // Force a manual changeTitle before the general update otherwise
+ // #redirect can cause an inconsistent data container as observed in #895
+ $source = $subject->getTitle();
+ $target = $target->getTitle();
+
+ $this->store->changeTitle(
+ $source,
+ $target,
+ $source->getArticleID(),
+ $target->getArticleID()
+ );
+
+ $dispatchContext = EventHandler::getInstance()->newDispatchContext();
+ $dispatchContext->set( 'title', $subject->getTitle() );
+
+ EventHandler::getInstance()->getEventDispatcher()->dispatch(
+ 'factbox.cache.delete',
+ $dispatchContext
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValueFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValueFactory.php
new file mode 100644
index 00000000..b8ed09e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValueFactory.php
@@ -0,0 +1,403 @@
+<?php
+
+namespace SMW;
+
+use SMW\DataValues\PropertyValue;
+use SMW\Services\DataValueServiceFactory;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIError;
+use SMWErrorValue as ErrorValue;
+
+/**
+ * Factory class for creating SMWDataValue objects for supplied types or
+ * properties and data values.
+ *
+ * The class has the main entry point newTypeIdValue(), which creates a new
+ * datavalue object, possibly with preset user values, captions and
+ * property names. To create suitable datavalues for a given property, the
+ * method newDataValueByProperty() can be used.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class DataValueFactory {
+
+ /**
+ * @var DataTypeRegistry
+ */
+ private static $instance = null;
+
+ /**
+ * @var DataTypeRegistry
+ */
+ private $dataTypeRegistry = null;
+
+ /**
+ * @var DataValueServiceFactory
+ */
+ private $dataValueServiceFactory;
+
+ /**
+ * @var array
+ */
+ private $defaultOutputFormatters;
+
+ /**
+ * @since 1.9
+ *
+ * @param DataTypeRegistry $dataTypeRegistry
+ * @param DataValueServiceFactory $dataValueServiceFactory
+ */
+ protected function __construct( DataTypeRegistry $dataTypeRegistry, DataValueServiceFactory $dataValueServiceFactory ) {
+ $this->dataTypeRegistry = $dataTypeRegistry;
+ $this->dataValueServiceFactory = $dataValueServiceFactory;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DataValueFactory
+ */
+ public static function getInstance() {
+
+ if ( self::$instance !== null ) {
+ return self::$instance;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $dataValueServiceFactory = $applicationFactory->create( 'DataValueServiceFactory' );
+ $dataTypeRegistry = DataTypeRegistry::getInstance();
+
+ $dataValueServiceFactory->importExtraneousFunctions(
+ $dataTypeRegistry->getExtraneousFunctions()
+ );
+
+ self::$instance = new self(
+ $dataTypeRegistry,
+ $dataValueServiceFactory
+ );
+
+ self::$instance->setDefaultOutputFormatters(
+ $applicationFactory->getSettings()->get( 'smwgDefaultOutputFormatters' )
+ );
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function clear() {
+ $this->dataTypeRegistry->clear();
+ self::$instance = null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $defaultOutputFormatters
+ */
+ public function setDefaultOutputFormatters( array $defaultOutputFormatters ) {
+
+ $this->defaultOutputFormatters = [];
+
+ foreach ( $defaultOutputFormatters as $type => $formatter ) {
+
+ $type = str_replace( ' ' , '_', $type );
+
+ if ( $type{0} !== '_' && ( $dType = $this->dataTypeRegistry->findTypeByLabel( $type ) ) !== '' ) {
+ $type = $dType;
+ }
+
+ $this->defaultOutputFormatters[$type] = $formatter;
+ }
+ }
+
+ /**
+ * Create a value from a type id. If no $value is given, an empty
+ * container is created, the value of which can be set later on.
+ *
+ * @param $typeId string id string for the given type
+ * @param $valueString mixed user value string, or false if unknown
+ * @param $caption mixed user-defined caption, or false if none given
+ * @param $property SMWDIProperty property object for which this value is made, or null
+ * @param $contextPage SMWDIWikiPage that provides a context for parsing the value string, or null
+ *
+ * @return DataValue
+ */
+ public function newDataValueByType( $typeId, $valueString = false, $caption = false, DIProperty $property = null, $contextPage = null ) {
+
+ if ( !$this->dataTypeRegistry->hasDataTypeClassById( $typeId ) ) {
+ return new ErrorValue(
+ $typeId,
+ [ 'smw_unknowntype', $typeId ],
+ $valueString,
+ $caption
+ );
+ }
+
+ $dataValue = $this->dataValueServiceFactory->newDataValueByType(
+ $typeId,
+ $this->dataTypeRegistry->getDataTypeClassById( $typeId )
+ );
+
+ $dataValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $dataValue->copyOptions(
+ $this->dataTypeRegistry->getOptions()
+ );
+
+ foreach ( $this->dataTypeRegistry->getExtensionData( $typeId ) as $key => $value ) {
+
+ if ( !is_string( $key ) ) {
+ continue;
+ }
+
+ $dataValue->setExtensionData( $key, $value );
+ }
+
+ $localizer = Localizer::getInstance();
+
+ $dataValue->setOption(
+ DataValue::OPT_USER_LANGUAGE,
+ $localizer->getUserLanguage()->getCode()
+ );
+
+ $dataValue->setOption(
+ DataValue::OPT_CONTENT_LANGUAGE,
+ $localizer->getContentLanguage()->getCode()
+ );
+
+ $dataValue->setOption(
+ DataValue::OPT_COMPACT_INFOLINKS,
+ $GLOBALS['smwgCompactLinkSupport']
+ );
+
+ if ( isset( $this->defaultOutputFormatters[$typeId] ) ) {
+ $dataValue->setOutputFormat( $this->defaultOutputFormatters[$typeId] );
+ }
+
+ if ( $property !== null ) {
+ $dataValue->setProperty( $property );
+
+ if ( isset( $this->defaultOutputFormatters[$property->getKey()] ) ) {
+ $dataValue->setOutputFormat( $this->defaultOutputFormatters[$property->getKey()] );
+ }
+ }
+
+ if ( !is_null( $contextPage ) ) {
+ $dataValue->setContextPage( $contextPage );
+ }
+
+ if ( $valueString !== false ) {
+ $dataValue->setUserValue( $valueString, $caption );
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * Create a value for a data item.
+ *
+ * @param $dataItem DataItem
+ * @param $property mixed null or SMWDIProperty property object for which this value is made
+ * @param $caption mixed user-defined caption, or false if none given
+ *
+ * @return DataValue
+ */
+ public function newDataValueByItem( DataItem $dataItem, DIProperty $property = null, $caption = false ) {
+
+ if ( $property !== null ) {
+ $typeId = $property->findPropertyTypeID();
+ } else {
+ $typeId = $this->dataTypeRegistry->getDefaultDataItemByType( $dataItem->getDiType() );
+ }
+
+ $dataValue = $this->newDataValueByType( $typeId, false, $caption, $property );
+ $dataValue->setDataItem( $dataItem );
+
+ if ( $caption !== false ) {
+ $dataValue->setCaption( $caption );
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * Create a value for the given property, provided as an SMWDIProperty
+ * object. If no value is given, an empty container is created, the
+ * value of which can be set later on.
+ *
+ * @param $property SMWDIProperty property object for which this value is made
+ * @param $valueString mixed user value string, or false if unknown
+ * @param $caption mixed user-defined caption, or false if none given
+ * @param $contextPage SMWDIWikiPage that provides a context for parsing the value string, or null
+ *
+ * @return DataValue
+ */
+ public function newDataValueByProperty( DIProperty $property, $valueString = false, $caption = false, $contextPage = null ) {
+
+ $typeId = $property->isInverse() ? '_wpg' : $property->findPropertyTypeID();
+
+ return $this->newDataValueByType( $typeId, $valueString, $caption, $property, $contextPage );
+ }
+
+ /**
+ * This factory method returns a data value object from a given property,
+ * value string. It is intended to be used on user input to allow to
+ * turn a property and value string into a data value object.
+ *
+ * @since 1.9
+ *
+ * @param string $propertyName property string
+ * @param string $valueString user value string
+ * @param mixed $caption user-defined caption
+ * @param SMWDIWikiPage|null $contextPage context for parsing the value string
+ *
+ * @return DataValue
+ */
+ public function newDataValueByText( $propertyName, $valueString, $caption = false, DIWikiPage $contextPage = null ) {
+
+ $propertyDV = $this->newPropertyValueByLabel( $propertyName, $caption, $contextPage );
+
+ if ( !$propertyDV->isValid() ) {
+ return $propertyDV;
+ }
+
+ if ( $propertyDV->isRestricted() ) {
+ $dataValue = new ErrorValue(
+ $propertyDV->getPropertyTypeID(),
+ $propertyDV->getRestrictionError(),
+ $valueString,
+ $caption
+ );
+
+ if ( $propertyDV->getDataItem() instanceof DIProperty ) {
+ $dataValue->setProperty( $propertyDV->getDataItem() );
+ }
+
+ return $dataValue;
+ }
+
+ $propertyDI = $propertyDV->getDataItem();
+
+ if ( $propertyDI instanceof SMWDIError ) {
+ return $propertyDV;
+ }
+
+ if ( $propertyDI instanceof DIProperty && !$propertyDI->isInverse() ) {
+ $dataValue = $this->newDataValueByProperty(
+ $propertyDI,
+ $valueString,
+ $caption,
+ $contextPage
+ );
+
+ $dataValue->setProperty( $propertyDV->getDataItem() );
+
+ } elseif ( $propertyDI instanceof DIProperty && $propertyDI->isInverse() ) {
+ $dataValue = new ErrorValue( $propertyDV->getPropertyTypeID(),
+ [ 'smw_noinvannot' ],
+ $valueString,
+ $caption
+ );
+
+ $dataValue->setProperty( $propertyDV->getDataItem() );
+ } else {
+ $dataValue = new ErrorValue(
+ $propertyDV->getPropertyTypeID(),
+ [ 'smw-property-name-invalid', $propertyName ],
+ $valueString,
+ $caption
+ );
+
+ $dataValue->setProperty( $propertyDV->getDataItem() );
+ }
+
+ if ( $dataValue->isValid() && !$dataValue->canUse() ) {
+ $dataValue = new ErrorValue(
+ $propertyDV->getPropertyTypeID(),
+ [ 'smw-datavalue-restricted-use', implode( ',', $dataValue->getErrors() ) ],
+ $valueString,
+ $caption
+ );
+
+ $dataValue->setProperty( $propertyDV->getDataItem() );
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $propertyLabel
+ * @param string|false $caption
+ * @param DIWikiPage|null $contextPage
+ *
+ * @return DataValue
+ */
+ public function newPropertyValueByLabel( $propertyLabel, $caption = false, DIWikiPage $contextPage = null ) {
+ return $this->newDataValueByType( PropertyValue::TYPE_ID, $propertyLabel, $caption, null, $contextPage );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $typeid
+ * @param string|array $errormsg
+ * @param string $uservalue
+ * @param string $caption
+ *
+ * @return ErrorValue
+ */
+ public function newErrorValue( $typeid, $errormsg = '', $uservalue = '', $caption = false ) {
+ return new ErrorValue( $typeid, $errormsg, $uservalue, $caption );
+ }
+
+/// Deprecated methods
+
+ /**
+ * @deprecated since 2.4, use DataValueFactory::newDataValueByItem
+ *
+ * @return DataValue
+ */
+ public static function newDataItemValue( DataItem $dataItem, DIProperty $property = null, $caption = false ) {
+ return self::getInstance()->newDataValueByItem( $dataItem, $property, $caption );
+ }
+
+ /**
+ * @deprecated since 2.4, use DataValueFactory::newDataValueByProperty
+ *
+ * @return DataValue
+ */
+ public static function newPropertyObjectValue( DIProperty $property, $valueString = false, $caption = false, $contextPage = null ) {
+ return self::getInstance()->newDataValueByProperty( $property, $valueString, $caption, $contextPage );
+ }
+
+ /**
+ * @deprecated since 2.4, use DataValueFactory::newDataValueByType
+ *
+ * @return DataValue
+ */
+ public static function newTypeIdValue( $typeId, $valueString = false, $caption = false, DIProperty $property = null, $contextPage = null ) {
+ return self::getInstance()->newDataValueByType( $typeId, $valueString, $caption, $property, $contextPage );
+ }
+
+ /**
+ * @deprecated since 2.4, use DataTypeRegistry::newDataValueByText
+ *
+ * @return DataValue
+ */
+ public function newPropertyValue( $propertyName, $valueString, $caption = false, DIWikiPage $contextPage = null ) {
+ return $this->newDataValueByText( $propertyName, $valueString, $caption, $contextPage );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AbstractMultiValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AbstractMultiValue.php
new file mode 100644
index 00000000..007aedb7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AbstractMultiValue.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMWDataValue as DataValue;
+use SMWPropertyListValue as PropertyListValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+abstract class AbstractMultiValue extends DataValue {
+
+ /**
+ * @since 2.5
+ *
+ * @param string $userValue
+ *
+ * @return array
+ */
+ abstract public function getValuesFromString( $userValue );
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty[] $properties
+ *
+ * @return DIProperty[]|null
+ */
+ abstract public function setFieldProperties( array $properties );
+
+ /**
+ * @since 2.5
+ *
+ * @return DIProperty[]|null
+ */
+ abstract public function getProperties();
+
+ /**
+ * Return the array (list) of properties that the individual entries of
+ * this datatype consist of.
+ *
+ * @since 2.5
+ *
+ * @return DIProperty[]|null
+ */
+ abstract public function getPropertyDataItems();
+
+ /**
+ * Create a list (array with numeric keys) containing the datavalue
+ * objects that this SMWRecordValue object holds. Values that are not
+ * present are set to null. Note that the first index in the array is
+ * 0, not 1.
+ *
+ * @since 2.5
+ *
+ * @return DataItem[]|null
+ */
+ public function getDataItems() {
+
+ if ( !$this->isValid() ) {
+ return [];
+ }
+
+ $dataItems = [];
+ $index = 0;
+
+ foreach ( $this->getPropertyDataItems() as $diProperty ) {
+ $values = $this->getDataItem()->getSemanticData()->getPropertyValues( $diProperty );
+ $dataItems[$index] = count( $values ) > 0 ? reset( $values ) : null;
+ $index++;
+ }
+
+ return $dataItems;
+ }
+
+ /**
+ * @note called by SMWResultArray::loadContent for matching an index as denoted
+ * in |?Foo=Bar|+index=1 OR |?Foo=Bar|+index=Bar
+ *
+ * @see https://www.semantic-mediawiki.org/wiki/Help:Type_Record#Semantic_search
+ *
+ * @since 2.5
+ *
+ * @param string|integer $index
+ *
+ * @return DataItem[]|null
+ */
+ public function getDataItemByIndex( $index ) {
+
+ if ( is_numeric( $index ) ) {
+ $pos = $index - 1;
+ $dataItems = $this->getDataItems();
+ return isset( $dataItems[$pos] ) ? $dataItems[$pos] : null;
+ }
+
+ if ( ( $property = $this->getPropertyDataItemByIndex( $index ) ) !== null ) {
+ $values = $this->getDataItem()->getSemanticData()->getPropertyValues( $property );
+ return reset( $values );
+ }
+
+ return null;
+ }
+
+ /**
+ * @note called by SMWResultArray::getNextDataValue to match an index
+ * that has been denoted using |?Foo=Bar|+index=1 OR |?Foo=Bar|+index=Bar
+ *
+ * @since 2.5
+ *
+ * @param string|integer $index
+ *
+ * @return DIProperty|null
+ */
+ public function getPropertyDataItemByIndex( $index ) {
+
+ $properties = $this->getPropertyDataItems();
+
+ if ( is_numeric( $index ) ) {
+ $pos = $index - 1;
+ return isset( $properties[$pos] ) ? $properties[$pos] : null;
+ }
+
+ foreach ( $properties as $property ) {
+ if ( $property->getLabel() === $index ) {
+ return $property;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the array (list) of properties that the individual entries of
+ * this datatype consist of.
+ *
+ * @since 2.5
+ *
+ * @param DIProperty|null $property
+ *
+ * @return DIProperty[]|[]
+ */
+ protected function getFieldProperties( DIProperty $property = null ) {
+
+ if ( $property === null || $property->getDiWikiPage() === null ) {
+ return [];
+ }
+
+ $dataItem = ApplicationFactory::getInstance()->getPropertySpecificationLookup()->getFieldListBy( $property );
+
+ if ( !$dataItem ) {
+ return [];
+ }
+
+ $propertyListValue = new PropertyListValue( '__pls' );
+ $propertyListValue->setDataItem( $dataItem );
+
+ if ( !$propertyListValue->isValid() ) {
+ return [];
+ }
+
+ return $propertyListValue->getPropertyDataItems();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsListValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsListValue.php
new file mode 100644
index 00000000..9388dc7e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsListValue.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Localizer;
+
+/**
+ * To support value list via the NS_MEDIAWIKI namespace
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class AllowsListValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__pvali';
+
+ /**
+ * Fixed Mediawiki NS prefix
+ */
+ const LIST_PREFIX = 'Smw_allows_list_';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ }
+
+ $allowsListValueParser = $this->dataValueServiceFactory->getValueParser( $this );
+
+ $allowsListValueParser->parse( $value );
+
+ if ( $allowsListValueParser->getErrors() !== [] ) {
+ foreach ( $allowsListValueParser->getErrors() as $error ) {
+ $this->addErrorMsg( $error );
+ }
+ } else {
+ parent::parseUserValue( $value );
+ }
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @param string $value
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $id = $this->getDataItem()->getString();
+
+ return '[['. Localizer::getInstance()->getNamespaceTextById( NS_MEDIAWIKI ) . ':' . self::LIST_PREFIX . $id . '|' . $id .']]';
+ }
+
+ /**
+ * @see DataValue::getLongHtmlText
+ *
+ * @param string $value
+ */
+ public function getLongHtmlText( $linker = null ) {
+ return $this->getShortHtmlText( $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHtmlText
+ *
+ * @param string $value
+ */
+ public function getShortHtmlText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $id = $this->getDataItem()->getString();
+ $title = \Title::newFromText( self::LIST_PREFIX . $id, NS_MEDIAWIKI );
+
+ return \Html::rawElement(
+ 'a',
+ [
+ 'href' => $title->getLocalUrl(),
+ 'target' => '_blank'
+ ],
+ $id
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsPatternValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsPatternValue.php
new file mode 100644
index 00000000..e1fb0054
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsPatternValue.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Localizer;
+use SMW\Message;
+
+/**
+ * To support regular expressions in connection with the `Allows pattern`
+ * property.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsPatternValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__pvap';
+
+ /**
+ * Fixed Mediawiki page
+ */
+ const REFERENCE_PAGE_ID = 'Smw_allows_pattern';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ }
+
+ if ( ( $this->getOption( 'smwgDVFeatures' ) & SMW_DV_PVAP ) == 0 && $value !== '' ) {
+ $this->addErrorMsg( [ 'smw-datavalue-feature-not-supported', 'Allows pattern (SMW_DV_PVAP)' ] );
+ }
+
+ $allowsPatternValueParser = $this->dataValueServiceFactory->getValueParser( $this );
+
+ $content = $allowsPatternValueParser->parse(
+ $value
+ );
+
+ if ( !$content ) {
+ $this->addErrorMsg( [ 'smw-datavalue-allows-pattern-reference-unknown', $value ], Message::ESCAPED );
+ }
+
+ parent::parseUserValue( $value );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @param string $value
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $id = $this->getDataItem()->getString();
+
+ return '[['. Localizer::getInstance()->getNamespaceTextById( NS_MEDIAWIKI ) . ':' . self::REFERENCE_PAGE_ID . '|' . $id .']]';
+ }
+
+ /**
+ * @see DataValue::getLongHtmlText
+ *
+ * @param string $value
+ */
+ public function getLongHtmlText( $linker = null ) {
+ return $this->getShortHtmlText( $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHtmlText
+ *
+ * @param string $value
+ */
+ public function getShortHtmlText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $id = $this->getDataItem()->getString();
+ $title = \Title::newFromText( self::REFERENCE_PAGE_ID, NS_MEDIAWIKI );
+
+ return \Html::rawElement(
+ 'a',
+ [
+ 'href' => $title->getLocalUrl(),
+ 'target' => '_blank'
+ ],
+ $id
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsValue.php
new file mode 100644
index 00000000..db4e184e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/AllowsValue.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace SMW\DataValues;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__pval';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/BooleanValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/BooleanValue.php
new file mode 100644
index 00000000..96813b07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/BooleanValue.php
@@ -0,0 +1,256 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Localizer;
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIBoolean as DIBoolean;
+
+/**
+ * This datavalue implements the handling of Boolean datavalues.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class BooleanValue extends DataValue {
+
+ /**
+ * The text to write for "true" if a custom output format was set.
+ * @var string
+ */
+ protected $trueCaption;
+
+ /**
+ * The text to write for "false" if a custom output format was set.
+ * @var string
+ */
+ protected $falseCaption;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( $typeid );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ */
+ protected function parseUserValue( $value ) {
+
+ $value = trim( $value );
+
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ $this->m_dataitem = new DIBoolean(
+ $this->doParseBoolValue( $value )
+ );
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== DataItem::TYPE_BOOLEAN ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $this->m_caption = $this->getStandardCaption( true );
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::setOutputFormat
+ */
+ public function setOutputFormat( $formatstring ) {
+
+ if ( $formatstring == $this->m_outformat ) {
+ return;
+ }
+
+ unset( $this->trueCaption );
+ unset( $this->falseCaption );
+
+ if ( $formatstring === '' ) { // no format
+ // (unsetting the captions is exactly the right thing here)
+ } elseif ( strtolower( $formatstring ) == '-' ) { // "plain" format
+ $this->trueCaption = 'true';
+ $this->falseCaption = 'false';
+ } elseif ( strtolower( $formatstring ) == 'num' ) { // "numeric" format
+ $this->trueCaption = 1;
+ $this->falseCaption = 0;
+ } elseif ( strtolower( $formatstring ) == 'tick' ) { // "tick" format
+ $this->trueCaption = '✓';
+ $this->falseCaption = '✕';
+ } elseif ( strtolower( $formatstring ) == 'x' ) { // X format
+ $this->trueCaption = '<span style="font-family: sans-serif; ">X</span>';
+ $this->falseCaption = '&nbsp;';
+ } else { // format "truelabel, falselabel" (hopefully)
+ $captions = explode( ',', $formatstring, 2 );
+ if ( count( $captions ) == 2 ) { // note: escaping needed to be safe; MW-sanitising would be an alternative
+ $this->trueCaption = \Sanitizer::removeHTMLtags( trim( $captions[0] ) );
+ $this->falseCaption = \Sanitizer::removeHTMLtags( trim( $captions[1] ) );
+ } // else: no format that is recognised, ignore
+ }
+
+ // Localized version
+ if ( strpos( $formatstring, 'LOCL' ) !== false ) {
+ $this->setLocalizedCaptions( $formatstring );
+ }
+
+ $this->m_caption = $this->getStandardCaption( true );
+ $this->m_outformat = $formatstring;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->m_caption;
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->m_caption;
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->isValid() ? $this->getStandardCaption( true ) : $this->getErrorText();
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->isValid() ? $this->getStandardCaption( true ) : $this->getErrorText();
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->getFirstBooleanCaptionFrom(
+ $this->isValid() && $this->m_dataitem->getBoolean() ? 'smw_true_words' : 'smw_false_words',
+ Message::CONTENT_LANGUAGE
+ );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return boolean
+ */
+ public function getBoolean() {
+ return !$this->isValid() ? false : $this->m_dataitem->getBoolean();
+ }
+
+ /**
+ * Get text for displaying the value of this property, or false if not
+ * valid.
+ * @param $useformat bool, true if the output format should be used, false if the returned text should be parsable
+ * @return string
+ */
+ protected function getStandardCaption( $useformat ) {
+
+ if ( !$this->isValid() ) {
+ return false;
+ }
+
+ if ( $useformat && ( isset( $this->trueCaption ) ) ) {
+ return $this->m_dataitem->getBoolean() ? $this->trueCaption : $this->falseCaption;
+ }
+
+ return $this->getFirstBooleanCaptionFrom(
+ $this->m_dataitem->getBoolean() ? 'smw_true_words' : 'smw_false_words',
+ $this->getOption( 'content.language' )
+ );
+ }
+
+ private function doParseBoolValue( $value ) {
+
+ // Use either the global or page related content language
+ $contentLanguage = $this->getOption( 'content.language' );
+
+ $lcv = mb_strtolower( $value );
+ $boolvalue = false;
+
+ if ( $lcv === '1' ) {
+ $boolvalue = true;
+ } elseif ( $lcv === '0' ) {
+ $boolvalue = false;
+ } elseif ( in_array( $lcv, $this->getBooleanWordsFrom( 'smw_true_words', $contentLanguage, 'true' ), true ) ) {
+ $boolvalue = true;
+ } elseif ( in_array( $lcv, $this->getBooleanWordsFrom( 'smw_false_words', $contentLanguage, 'false' ), true ) ) {
+ $boolvalue = false;
+ } else {
+ $this->addErrorMsg(
+ [ 'smw_noboolean', $value ],
+ Message::TEXT,
+ Message::USER_LANGUAGE
+ );
+ }
+
+ return $boolvalue;
+ }
+
+ private function setLocalizedCaptions( &$formatstring ) {
+
+ if ( !( $languageCode = Localizer::getLanguageCodeFrom( $formatstring ) ) ) {
+ $languageCode = $this->getOption( 'user.language' );
+ }
+
+ $this->trueCaption = $this->getFirstBooleanCaptionFrom(
+ 'smw_true_words',
+ $languageCode
+ );
+
+ $this->falseCaption = $this->getFirstBooleanCaptionFrom(
+ 'smw_false_words',
+ $languageCode
+ );
+ }
+
+ private function getFirstBooleanCaptionFrom( $msgKey, $languageCode = null ) {
+
+ $vals = $this->getBooleanWordsFrom(
+ $msgKey,
+ $languageCode
+ );
+
+ return reset( $vals );
+ }
+
+ private function getBooleanWordsFrom( $msgKey, $languageCode = null, $canonicalForm = null ) {
+
+ $vals = explode(
+ ',',
+ Message::get( $msgKey, Message::TEXT, $languageCode )
+ );
+
+ if ( $canonicalForm !== null ) {
+ $vals[] = $canonicalForm;
+ }
+
+ return $vals;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ErrorMsgTextValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ErrorMsgTextValue.php
new file mode 100644
index 00000000..4372f144
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ErrorMsgTextValue.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Handling of a language dependent error message encoded by Message::encode.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorMsgTextValue extends DataValue {
+
+ /**
+ * @see DataValue::__construct
+ */
+ public function __construct( $typeId = '' ) {
+ parent::__construct( '__errt' );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ }
+
+ $this->m_dataitem = new DIBlob( $value );
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * @param SMWDataItem $dataitem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ return false;
+ }
+
+ $this->m_caption = false;
+ $this->m_dataitem = $dataItem;
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->constructErrorText( null );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->constructErrorText( $linker );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->constructErrorText( $linker );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->constructErrorText( $linker );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->constructErrorText();
+ }
+
+ private function constructErrorText( $linker = null ) {
+
+ if ( !$this->isValid() || $this->getDataItem() === [] ) {
+ return '';
+ }
+
+ $string = $this->getDataItem()->getString();
+ $type = $linker !== null ? Message::PARSE : Message::TEXT;
+
+ if ( ( $message = Message::decode( $string, $type, $this->getOption( self::OPT_USER_LANGUAGE ) ) ) !== false ) {
+ return $message;
+ }
+
+ return $string;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalFormatterUriValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalFormatterUriValue.php
new file mode 100644
index 00000000..6d401256
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalFormatterUriValue.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMWURIValue as UriValue;
+
+/**
+ * https://www.ietf.org/rfc/rfc3986.txt describes:
+ *
+ * " ... Uniform Resource Identifier (URI) is a compact sequence of characters
+ * that identifies an abstract or physical resource." with "... Uniform Resource
+ * Locator" (URL) refers to the subset of URIs that provide a means of locating
+ * the resource by describing its primary access mechanism ..."
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalFormatterUriValue extends UriValue {
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( '__peru' );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ return;
+ }
+
+ if ( filter_var( $value, FILTER_VALIDATE_URL ) === false && preg_match( '/((mailto\:|(news|urn|tel|(ht|f)tp(s?))\:\/\/){1}\S+)/u', $value ) === false ) {
+ $this->addErrorMsg( [ 'smw-datavalue-external-formatter-invalid-uri', $value ] );
+ return;
+ }
+
+ if ( strpos( $value, '$1' ) === false ) {
+ $this->addErrorMsg( 'smw-datavalue-external-formatter-uri-missing-placeholder' );
+ return;
+ }
+
+ parent::parseUserValue( $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public function getUriWithPlaceholderSubstitution( $value ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ // Avoid already encoded values like `W%D6LLEKLA01` to be
+ // encoded twice
+ $value = $this->encode( rawurldecode( $value ) );
+
+ // %241 == encoded $1
+ return str_replace( [ '%241', '$1' ], [ '$1', $value ], $this->getDataItem()->getUri() );
+ }
+
+ // http://php.net/manual/en/function.urlencode.php#97969
+ private function encode( $string ) {
+ return str_replace(
+ [ '%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%2B', '%24', '%2C', '%2F', '%3F', '%25', '%23', '%5B', '%5D' ],
+ [ '!', '*', "'", "(", ")", ";", ":", "@", "&", "=", "+", "$", ",", "/", "?", "%", "#", "[", "]" ],
+ urlencode( $string )
+ );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalIdentifierValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalIdentifierValue.php
new file mode 100644
index 00000000..fab1a42f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ExternalIdentifierValue.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DIProperty;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalIdentifierValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_eid';
+
+ /**
+ * @var string|null
+ */
+ private $uri = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+ parent::parseUserValue( $value );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @param string $value
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ if ( !$this->m_caption ) {
+ $this->m_caption = $this->m_dataitem->getString();
+ }
+
+ if ( $linker === null ) {
+ return $this->m_caption;
+ }
+
+ $uri = $this->makeUri(
+ $this->m_dataitem->getString()
+ );
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ if ( $this->getOutputFormat() == 'nowiki' ) {
+ $url = $this->makeNonlinkedWikiText( $uri );
+ } else {
+ $url = '['. $uri . ' '. $this->m_caption . ']';
+ }
+
+ return \Html::rawElement(
+ 'span',
+ [
+ 'class' => 'plainlinks smw-eid'
+ ],
+ $url
+ );
+ }
+
+ /**
+ * @see StringValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ if ( !$this->m_caption ) {
+ $this->m_caption = $this->m_dataitem->getString();
+ }
+
+ if ( $linker === null ) {
+ return $this->m_caption;
+ }
+
+ $uri = $this->makeUri(
+ $this->m_dataitem->getString()
+ );
+
+ if ( !$this->isValid() ) {
+ return $this->m_caption;
+ }
+
+ return \Html::rawElement(
+ 'a',
+ [
+ 'href' => $uri,
+ 'target' => '_blank'
+ ],
+ $this->m_caption
+ );
+ }
+
+ /**
+ * @see StringValue::getLongWikiText
+ */
+ public function getLongWikiText( $linked = null ) {
+ return $this->getShortWikiText( $linked );
+ }
+
+ /**
+ * @see StringValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->getShortHTMLText( $linker );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataItem
+ */
+ public function getUri() {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $dataValue = $this->dataValueServiceFactory->getDataValueFactory()->newDataValueByType(
+ '_uri',
+ $this->makeUri( $this->m_dataitem->getString() )
+ );
+
+ return $dataValue->getDataItem();
+ }
+
+ private function makeUri( $value ) {
+
+ if ( $this->uri !== null ) {
+ return $this->uri;
+ }
+
+ $dataItem = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getExternalFormatterUri(
+ $this->getProperty()
+ );
+
+ if ( $dataItem === null ) {
+ $this->addErrorMsg( 'smw-datavalue-external-identifier-formatter-missing' );
+ return;
+ }
+
+ $dataValue = $this->dataValueServiceFactory->getDataValueFactory()->newDataValueByItem(
+ $dataItem,
+ new DIProperty( '_PEFU' )
+ );
+
+ if ( $dataValue->getErrors() !== [] ) {
+ $this->addError( $dataValue->getErrors() );
+ return;
+ }
+
+ return $this->uri = $dataValue->getUriWithPlaceholderSubstitution( $value );
+ }
+
+ private function makeNonlinkedWikiText( $url ) {
+ return str_replace( ':', '&#58;', $url );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ImportValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ImportValue.php
new file mode 100644
index 00000000..9a9e1bb4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ImportValue.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+
+/**
+ * This datavalue implements datavalues used by special property '_IMPO' used
+ * for assigning imported vocabulary to some page of the wiki. It looks up a
+ * MediaWiki message to find out whether a user-supplied vocabulary name can be
+ * imported in the wiki, and whether its declaration is correct (to the extent
+ * that this can be checked).
+ *
+ * @author Fabian Howahl
+ * @author Markus Krötzsch
+ */
+class ImportValue extends DataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__imp';
+
+ /**
+ * Fixed Mediawiki import prefix
+ */
+ const IMPORT_PREFIX = 'Smw_import_';
+
+ /**
+ * Type string assigned by the import declaration
+ *
+ * @var string
+ */
+ private $termType = '';
+
+ /**
+ * String provided by user which is used to look up data on Mediawiki:*-Page
+ *
+ * @var string
+ */
+ private $qname = '';
+
+ /**
+ * URI of namespace (without local name)
+ *
+ * @var string
+ */
+ private $uri = '';
+
+ /**
+ * Namespace id (e.g. "foaf")
+ *
+ * @var string
+ */
+ private $namespace = '';
+
+ /**
+ * Local name (e.g. "knows")
+ *
+ * @var string
+ */
+ private $term = '';
+
+ /**
+ * Wiki name of the vocab (e.g. "Friend of a Friend"), might contain wiki markup
+ *
+ * @var string
+ */
+ private $declarativeName = '';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = self::TYPE_ID ) {
+ parent::__construct( $typeid );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+ $this->qname = $value;
+
+ $importValueParser = $this->dataValueServiceFactory->getValueParser(
+ $this
+ );
+
+ list( $this->namespace, $this->term, $this->uri, $this->declarativeName, $this->termType ) = $importValueParser->parse(
+ $value
+ );
+
+ if ( $importValueParser->getErrors() !== [] ) {
+
+ foreach ( $importValueParser->getErrors() as $message ) {
+ $this->addErrorMsg( $message );
+ }
+
+ $this->m_dataitem = new DIBlob( 'ERROR' );
+ return;
+ }
+
+ // Encoded string for DB storage
+ $this->m_dataitem = new DIBlob(
+ $this->namespace . ' ' .
+ $this->term . ' ' .
+ $this->uri . ' ' .
+ $this->termType
+ );
+
+ // check whether caption is set, otherwise assign link statement to caption
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $this->createCaption( $this->namespace, $this->qname, $this->uri, $this->declarativeName );
+ }
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem
+ *
+ * @param DataItem $dataitem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ return false;
+ }
+
+ $this->m_dataitem = $dataItem;
+ $parts = explode( ' ', $dataItem->getString(), 4 );
+
+ if ( count( $parts ) != 4 ) {
+ $this->addErrorMsg( [ 'smw-datavalue-import-invalid-format', $dataItem->getString() ] );
+ } else {
+ $this->namespace = $parts[0];
+ $this->term = $parts[1];
+ $this->uri = $parts[2];
+ $this->termType = $parts[3];
+ $this->qname = $this->namespace . ':' . $this->term;
+ $this->declarativeName = '';
+ $this->m_caption = $this->createCaption( $this->namespace, $this->qname, $this->uri, $this->declarativeName );
+ }
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linked = null ) {
+ return $this->m_caption;
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return htmlspecialchars( $this->qname );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ */
+ public function getLongWikiText( $linked = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ return "[[MediaWiki:" . self::IMPORT_PREFIX . $this->namespace . "|" . $this->qname . "]]";
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return $this->getErrorText();
+ }
+
+ return htmlspecialchars( $this->qname );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->qname;
+ }
+
+ public function getNS() {
+ return $this->uri;
+ }
+
+ public function getNSID() {
+ return $this->namespace;
+ }
+
+ public function getLocalName() {
+ return $this->term;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getTermType() {
+ return $this->termType;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getImportReference() {
+ return $this->namespace . ':' . $this->term . '|' . $this->uri;
+ }
+
+ private function createCaption( $namespace, $qname, $uri, $declarativeName ) {
+ return "[[MediaWiki:" . self::IMPORT_PREFIX . $namespace . "|" . $qname . "]] " . Message::get( [ 'parentheses', "[$uri $namespace] | " . $declarativeName ], Message::PARSE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/InfoLinksProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/InfoLinksProvider.php
new file mode 100644
index 00000000..a03f6b2c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/InfoLinksProvider.php
@@ -0,0 +1,284 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\Message;
+use SMW\Parser\InTextAnnotationParser;
+use SMW\PropertySpecificationLookup;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+use SMWInfolink as Infolink;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class InfoLinksProvider {
+
+ /**
+ * @var DataValue
+ */
+ private $dataValue;
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var Infolink[]
+ */
+ protected $infoLinks = [];
+
+ /**
+ * Used to control the addition of the standard search link.
+ * @var boolean
+ */
+ private $hasSearchLink;
+
+ /**
+ * Used to control service link creation.
+ * @var boolean
+ */
+ private $hasServiceLinks;
+
+ /**
+ * @var boolean
+ */
+ private $enabledServiceLinks = true;
+
+ /**
+ * @var boolean
+ */
+ private $compactLink = false;
+
+ /**
+ * @var boolean|array
+ */
+ private $serviceLinkParameters = false;
+
+ /**
+ * @var []
+ */
+ private $browseLinks = [ '__sob' ];
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( DataValue $dataValue, PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->dataValue = $dataValue;
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function init() {
+ $this->infoLinks = [];
+ $this->hasSearchLink = false;
+ $this->hasServiceLinks = false;
+ $this->enabledServiceLinks = true;
+ $this->serviceLinkParameters = false;
+ $this->compactLink = false;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function disableServiceLinks() {
+ $this->enabledServiceLinks = false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $compactLink
+ */
+ public function setCompactLink( $compactLink ) {
+ $this->compactLink = (bool)$compactLink;
+ }
+
+ /**
+ * Adds a single SMWInfolink object to the infoLinks array.
+ *
+ * @since 2.4
+ *
+ * @param Infolink $link
+ */
+ public function addInfolink( Infolink $infoLink ) {
+ $this->infoLinks[] = $infoLink;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array|false $serviceLinkParameters
+ */
+ public function setServiceLinkParameters( $serviceLinkParameters ) {
+ $this->serviceLinkParameters = $serviceLinkParameters;
+ }
+
+ /**
+ * Return an array of SMWLink objects that provide additional resources
+ * for the given value. Captions can contain some HTML markup which is
+ * admissible for wiki text, but no more. Result might have no entries
+ * but is always an array.
+ *
+ * @since 2.4
+ */
+ public function createInfoLinks() {
+
+ if ( $this->infoLinks !== [] ) {
+ return $this->infoLinks;
+ }
+
+ if ( !$this->dataValue->isValid() ) {
+ return [];
+ }
+
+ // Avoid any localization when generating the value
+ $this->dataValue->setOutputFormat( '' );
+
+ $value = $this->dataValue->getWikiValue();
+ $property = $this->dataValue->getProperty();
+
+ // InTextAnnotationParser will detect :: therefore avoid link
+ // breakage by encoding the string
+ if ( strpos( $value, '::' ) !== false && !InTextAnnotationParser::hasMarker( $value ) ) {
+ $value = str_replace( ':', '-3A', $value );
+ }
+
+ if ( in_array( $this->dataValue->getTypeID(), $this->browseLinks ) ) {
+ $infoLink = Infolink::newBrowsingLink( '+', $this->dataValue->getLongWikiText() );
+ $infoLink->setCompactLink( $this->compactLink );
+ } elseif ( $property !== null ) {
+ $infoLink = Infolink::newPropertySearchLink( '+', $property->getLabel(), $value );
+ $infoLink->setCompactLink( $this->compactLink );
+ }
+
+ $this->infoLinks[] = $infoLink;
+ $this->hasSearchLink = $this->infoLinks !== [];
+
+ // add further service links
+ if ( !$this->hasServiceLinks && $this->enabledServiceLinks ) {
+ $this->addServiceLinks();
+ }
+
+ return $this->infoLinks;
+ }
+
+ /**
+ * Return text serialisation of info links. Ensures more uniform layout
+ * throughout wiki (Factbox, Property pages, ...).
+ *
+ * @param integer $outputformat Element of the SMW_OUTPUT_ enum
+ * @param Linker|null $linker
+ *
+ * @return string
+ */
+ public function getInfolinkText( $outputformat, $linker = null ) {
+
+ $result = '';
+ $first = true;
+ $extralinks = [];
+
+ foreach ( $this->dataValue->getInfolinks() as $link ) {
+
+ if ( $outputformat === SMW_OUTPUT_WIKI ) {
+ $text = $link->getWikiText();
+ } else {
+ $text = $link->getHTML( $linker );
+ }
+
+ // the comment is needed to prevent MediaWiki from linking
+ // URL-strings together with the nbsps!
+ if ( $first ) {
+ $result .= ( $outputformat === SMW_OUTPUT_WIKI ? '<!-- --> ' : '&#160;&#160;' ) . $text;
+ $first = false;
+ } else {
+ $extralinks[] = $text;
+ }
+ }
+
+ if ( $extralinks !== [] ) {
+ $result .= smwfEncodeMessages( $extralinks, 'service', '', false );
+ }
+
+ // #1453 SMW::on/off will break any potential link therefore just don't even try
+ return !InTextAnnotationParser::hasMarker( $result ) ? $result : '';
+ }
+
+ /**
+ * Servicelinks are special kinds of infolinks that are created from
+ * current parameters and in-wiki specification of URL templates. This
+ * method adds the current property's servicelinks found in the
+ * messages. The number and content of the parameters is depending on
+ * the datatype, and the service link message is usually crafted with a
+ * particular datatype in mind.
+ */
+ public function addServiceLinks() {
+
+ if ( $this->hasServiceLinks ) {
+ return;
+ }
+
+ $dataItem = null;
+
+ if ( $this->dataValue->getProperty() !== null ) {
+ $dataItem = $this->dataValue->getProperty()->getDiWikiPage();
+ }
+
+ // No property known, or not associated with a page!
+ if ( $dataItem === null ) {
+ return;
+ }
+
+ $args = $this->serviceLinkParameters;
+
+ if ( $args === false ) {
+ return; // no services supported
+ }
+
+ // add a 0 element as placeholder
+ array_unshift( $args, '' );
+
+ $servicelinks = $this->propertySpecificationLookup->getSpecification(
+ $dataItem,
+ new DIProperty( '_SERV' )
+ );
+
+ foreach ( $servicelinks as $servicelink ) {
+ $this->makeLink( $servicelink, $args );
+ }
+
+ $this->hasServiceLinks = true;
+ }
+
+ private function makeLink( $dataItem, $args ) {
+
+ if ( !( $dataItem instanceof DIBlob ) ) {
+ return;
+ }
+
+ // messages distinguish ' ' from '_'
+ $args[0] = 'smw_service_' . str_replace( ' ', '_', $dataItem->getString() );
+ $text = Message::get( $args, Message::TEXT, Message::CONTENT_LANGUAGE );
+ $links = preg_split( "/[\n][\s]?/u", $text );
+
+ foreach ( $links as $link ) {
+ $linkdat = explode( '|', $link, 2 );
+
+ if ( count( $linkdat ) == 2 ) {
+ $this->addInfolink( Infolink::newExternalLink( $linkdat[0], trim( $linkdat[1] ) ) );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/KeywordValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/KeywordValue.php
new file mode 100644
index 00000000..b14e7d7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/KeywordValue.php
@@ -0,0 +1,287 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWInfolink as Infolink;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class KeywordValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_keyw';
+
+ /**
+ * @var string|null
+ */
+ private $uri = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $value ) {
+
+ // For the normal blob field setup multi-byte requires more space and
+ // since we use the o_hash field to store the normalized content and
+ // as match field, ensure to have enough space to actually store
+ // a mb keyword
+ $maxLength = mb_detect_encoding( $value, 'ASCII', true ) ? 150 : 85;
+
+ if ( mb_strlen( $value, 'utf8' ) > $maxLength ) {
+ $this->addErrorMsg( [ 'smw-datavalue-keyword-maximum-length', $maxLength ] );
+ return;
+ }
+
+ if ( $this->getOption( self::OPT_QUERY_COMP_CONTEXT ) || $this->getOption( self::OPT_QUERY_CONTEXT ) ) {
+ $value = DIBlob::normalize( $value );
+ }
+
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ parent::parseUserValue( $value );
+
+ $this->m_dataitem->setOption( 'is.keyword', true );
+ }
+
+ /**
+ * @see DataValue::getDataItem
+ */
+ public function getDataItem() {
+
+ if ( $this->isValid() && $this->getOption( 'is.search' ) ) {
+ return new DIBlob( DIBlob::normalize( $this->m_dataitem->getString() ) );
+ }
+
+ return parent::getDataItem();
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @param string $value
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ if ( !$this->m_caption ) {
+ $this->m_caption = $this->m_dataitem->getString();
+ }
+
+ if ( $linker === null ) {
+ return $this->m_caption;
+ }
+
+ $uri = $this->makeUri(
+ $this->m_dataitem->getString(),
+ SMW_OUTPUT_WIKI,
+ $linker
+ );
+
+ if ( $uri === '' ) {
+ return $this->m_caption;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * @see StringValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ if ( !$this->m_caption ) {
+ $this->m_caption = $this->m_dataitem->getString();
+ }
+
+ if ( $linker === null ) {
+ return $this->m_caption;
+ }
+
+ $uri = $this->makeUri(
+ $this->m_dataitem->getString(),
+ SMW_OUTPUT_HTML,
+ $linker
+ );
+
+ if ( $uri === '' ) {
+ return $this->m_caption;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * @see StringValue::getLongWikiText
+ */
+ public function getLongWikiText( $linked = null ) {
+ return $this->getShortWikiText( $linked );
+ }
+
+ /**
+ * @see StringValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->getShortHTMLText( $linker );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataItem
+ */
+ public function getUri() {
+
+ if ( !$this->isValid() ) {
+ return '';
+ }
+
+ $uri = $this->makeUri(
+ $this->m_dataitem->getString(),
+ SMW_OUTPUT_RAW
+ );
+
+ if ( $uri === '' ) {
+ return '';
+ }
+
+ $dataValue = $this->dataValueServiceFactory->getDataValueFactory()->newDataValueByType(
+ '_uri',
+ $uri
+ );
+
+ return $dataValue->getDataItem();
+ }
+
+ private function makeUri( $value, $outputformat, $linker = null ) {
+
+ if ( $this->uri !== null ) {
+ return $this->uri;
+ }
+
+ $propertySpecificationLookup = $this->dataValueServiceFactory->getPropertySpecificationLookup();
+
+ // Formatter schema?
+ $dataItems = $propertySpecificationLookup->getSpecification(
+ $this->getProperty(),
+ new DIProperty( '_FORMAT_SCHEMA' )
+ );
+
+ if ( $dataItems === [] ) {
+ return '';
+ }
+
+ $dataItem = end( $dataItems );
+
+ $dataItems = $propertySpecificationLookup->getSpecification(
+ $dataItem,
+ new DIProperty( '_SCHEMA_DEF' )
+ );
+
+ if ( $dataItems === [] ) {
+ return '';
+ }
+
+ $dataItem = end( $dataItems );
+
+ $link = $this->getFormatLink( $dataItem, $value );
+
+ if ( $link === '' ) {
+ return '';
+ }
+
+ $this->uri = $link->getText( $outputformat );
+
+ $this->uri = Localizer::getInstance()->getCanonicalizedUrlByNamespace(
+ NS_SPECIAL,
+ $this->uri
+ );
+
+ return $this->uri;
+ }
+
+ private function getFormatLink( $dataItem, $value ) {
+
+ $infolink = '';
+
+ $data = json_decode(
+ $dataItem->getString(),
+ true
+ );
+
+ // Schema enforced
+ if ( $data['type'] !== 'LINK_FORMAT_SCHEMA' ) {
+ return '';
+ }
+
+ if ( !isset( $data['rule']['link_to'] ) ) {
+ return '';
+ }
+
+ $link_to = $data['rule']['link_to'];
+ $label = $this->getProperty()->getLabel();
+
+ if ( $link_to === 'SPECIAL_ASK' ) {
+ $infolink = Infolink::newInternalLink( $this->m_caption, ':Special:Ask', false, [] );
+ $infolink->setParameter( "[[$label::$value]]", false );
+ $infolink->setCompactLink( $this->getOption( KeywordValue::OPT_COMPACT_INFOLINKS, false ) );
+
+ foreach ( $data['rule']['parameters'] as $key => $value ) {
+
+ if ( $key === 'title' || $key === 'msg' ) {
+ $key = "b$key";
+ }
+
+ if ( $key === 'printouts' ) {
+ foreach ( $value as $v ) {
+ $infolink->setParameter( "?$v" );
+ }
+ } else {
+ $infolink->setParameter( $value, $key );
+ }
+ }
+
+ } elseif ( $link_to === 'SPECIAL_SEARCH_BY_PROPERTY' ) {
+ $infolink = Infolink::newInternalLink( $this->m_caption, ':Special:SearchByProperty', false, [] );
+ $infolink->setCompactLink( $this->getOption( KeywordValue::OPT_COMPACT_INFOLINKS, false ) );
+ $infolink->setParameter( ":$label" );
+ $infolink->setParameter( $value );
+ }
+
+ return $infolink;
+ }
+
+ private function makeNonlinkedWikiText( $url ) {
+ return str_replace( ':', '&#58;', $url );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/LanguageCodeValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/LanguageCodeValue.php
new file mode 100644
index 00000000..f6bef8d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/LanguageCodeValue.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\Localizer;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Handles a string value to adhere the BCP47 normative content declaration for
+ * a language code tag
+ *
+ * @see https://en.wikipedia.org/wiki/IETF_language_tag
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class LanguageCodeValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__lcode';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $userValue ) {
+
+ $languageCode = Localizer::asBCP47FormattedLanguageCode( $userValue );
+
+ if ( $languageCode === '' ) {
+ $this->addErrorMsg( [
+ 'smw-datavalue-languagecode-missing',
+ $this->m_property !== null ? $this->m_property->getLabel() : 'UNKNOWN'
+ ] );
+ return;
+ }
+
+ // Checks whether the language tag is valid in MediaWiki for when
+ // it is not executed in a query context
+ if ( !$this->getOption( self::OPT_QUERY_CONTEXT ) && !Localizer::isKnownLanguageTag( $languageCode ) ) {
+ $this->addErrorMsg( [
+ 'smw-datavalue-languagecode-invalid',
+ $languageCode
+ ] );
+ return;
+ }
+
+ $this->m_dataitem = new DIBlob( $languageCode );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/MonolingualTextValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/MonolingualTextValue.php
new file mode 100644
index 00000000..2233ef7d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/MonolingualTextValue.php
@@ -0,0 +1,363 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+use SMWDIContainer as DIContainer;
+
+/**
+ * MonolingualTextValue requires two components, a language code and a
+ * text.
+ *
+ * A text `foo@en` is expected to be invoked with a BCP47 language
+ * code tag and a language dependent text component.
+ *
+ * Internally, the value is stored as container object that represents
+ * the language code and text as separate entities in order to be queried
+ * individually.
+ *
+ * External output representation depends on the context (wiki, html)
+ * whether the language code is omitted or not.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValue extends AbstractMultiValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_mlt_rec';
+
+ /**
+ * @var DIProperty[]|null
+ */
+ private static $properties = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @see AbstractMultiValue::setFieldProperties
+ *
+ * @param DIProperty[] $properties
+ */
+ public function setFieldProperties( array $properties ) {
+ // Keep the interface while the properties for this type
+ // are fixed.
+ }
+
+ /**
+ * @see AbstractMultiValue::getProperties
+ *
+ * @param DIProperty[] $properties
+ */
+ public function getProperties() {
+ self::$properties;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param $userValue
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getTextWithLanguageTag( $text, $languageCode ) {
+ return $text . '@' . Localizer::asBCP47FormattedLanguageCode( $languageCode );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ * @note called by DataValue::setUserValue
+ *
+ * @param string $userValue
+ */
+ protected function parseUserValue( $userValue ) {
+
+ list( $text, $languageCode ) = $this->getValuesFromString( $userValue );
+
+ $languageCodeValue = $this->newLanguageCodeValue( $languageCode );
+
+ if (
+ ( $languageCode !== '' && $languageCodeValue->getErrors() !== [] ) ||
+ ( $languageCode === '' && $this->isEnabledFeature( SMW_DV_MLTV_LCODE ) ) ) {
+ $this->addError( $languageCodeValue->getErrors() );
+ return;
+ }
+
+ $dataValues = [];
+
+ foreach ( $this->getPropertyDataItems() as $property ) {
+
+ if (
+ ( $languageCode === '' && $property->getKey() === '_LCODE' ) ||
+ ( $text === '' && $property->getKey() === '_TEXT' ) ) {
+ continue;
+ }
+
+ $value = $text;
+
+ if ( $property->getKey() === '_LCODE' ) {
+ $value = $languageCode;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ $value,
+ false,
+ $this->m_contextPage
+ );
+
+ $this->addError( $dataValue->getErrors() );
+
+ $dataValues[] = $dataValue;
+ }
+
+ // Generate a hash from the normalized representation so that foo@en being
+ // the same as foo@EN independent of a user input
+ $containerSemanticData = $this->newContainerSemanticData( $text . '@' . $languageCode );
+
+ foreach ( $dataValues as $dataValue ) {
+ $containerSemanticData->addDataValue( $dataValue );
+ }
+
+ // Remember the data to extend the sortkey
+ $containerSemanticData->setExtensionData( 'sort.data', implode( ';', [ $text, $languageCode ] ) );
+
+ $this->m_dataitem = new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @note called by MonolingualTextValueDescriptionDeserializer::deserialize
+ * and MonolingualTextValue::parseUserValue
+ *
+ * No explicit check is made on the validity of a language code and is
+ * expected to be done before calling this method.
+ *
+ * @since 2.4
+ *
+ * @param string $userValue
+ *
+ * @return array
+ */
+ public function getValuesFromString( $userValue ) {
+ return $this->dataValueServiceFactory->getValueParser( $this )->parse( $userValue );
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_CONTAINER ) {
+ $this->m_dataitem = $dataItem;
+ return true;
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $semanticData = new ContainerSemanticData( $dataItem );
+ $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
+ $this->m_dataitem = new DIContainer( $semanticData );
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::VALUE );
+ }
+
+ /**
+ * @since 2.4
+ * @note called by AbstractRecordValue::getPropertyDataItems
+ *
+ * @return DIProperty[]
+ */
+ public function getPropertyDataItems() {
+
+ if ( self::$properties !== null && self::$properties !== [] ) {
+ return self::$properties;
+ }
+
+ foreach ( [ '_TEXT', '_LCODE' ] as $id ) {
+ self::$properties[] = new DIProperty( $id );
+ }
+
+ return self::$properties;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function getDataItems() {
+ return parent::getDataItems();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $languageCode
+ *
+ * @return DataValue|null
+ */
+ public function getTextValueByLanguage( $languageCode ) {
+
+ if ( ( $list = $this->toArray() ) === [] ) {
+ return null;
+ }
+
+ if ( $list['_LCODE'] !== Localizer::asBCP47FormattedLanguageCode( $languageCode ) ) {
+ return null;
+ }
+
+ if ( $list['_TEXT'] === '' ) {
+ return null;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( $list['_TEXT'] ),
+ new DIProperty( '_TEXT' )
+ );
+
+ return $dataValue;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function toArray() {
+
+ if ( !$this->isValid() || $this->getDataItem() === [] ) {
+ return [];
+ }
+
+ $semanticData = $this->getDataItem()->getSemanticData();
+
+ $list = [
+ '_TEXT' => '',
+ '_LCODE' => ''
+ ];
+
+ $dataItems = $semanticData->getPropertyValues( new DIProperty( '_TEXT' ) );
+ $dataItem = reset( $dataItems );
+
+ if ( $dataItem !== false ) {
+ $list['_TEXT'] = $dataItem->getString();
+ }
+
+ $dataItems = $semanticData->getPropertyValues( new DIProperty( '_LCODE' ) );
+ $dataItem = reset( $dataItems );
+
+ if ( $dataItem !== false ) {
+ $list['_LCODE'] = $dataItem->getString();
+ }
+
+ return $list;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function toString() {
+
+ if ( !$this->isValid() || $this->getDataItem() === [] ) {
+ return '';
+ }
+
+ $list = $this->toArray();
+
+ return $list['_TEXT'] . '@' . $list['_LCODE'];
+ }
+
+ private function newContainerSemanticData( $value ) {
+
+ if ( $this->m_contextPage === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ $containerSemanticData->skipAnonymousCheck();
+ } else {
+ $subobjectName = '_ML' . md5( $value );
+
+ $subject = new DIWikiPage(
+ $this->m_contextPage->getDBkey(),
+ $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getInterwiki(),
+ $subobjectName
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return $containerSemanticData;
+ }
+
+ private function newLanguageCodeValue( $languageCode ) {
+
+ $languageCodeValue = new LanguageCodeValue();
+
+ if ( $this->m_property !== null ) {
+ $languageCodeValue->setProperty( $this->m_property );
+ }
+
+ $languageCodeValue->setUserValue( $languageCode );
+
+ return $languageCodeValue;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/IntlNumberFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/IntlNumberFormatter.php
new file mode 100644
index 00000000..a3bb90e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/IntlNumberFormatter.php
@@ -0,0 +1,384 @@
+<?php
+
+namespace SMW\DataValues\Number;
+
+use InvalidArgumentException;
+use SMW\Message;
+use SMW\Options;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class IntlNumberFormatter {
+
+ /**
+ * Localization related constants
+ */
+ const CONTENT_LANGUAGE = Message::CONTENT_LANGUAGE;
+ const USER_LANGUAGE = Message::USER_LANGUAGE;
+ const PREFERRED_LANGUAGE = 'preferred.language';
+
+ /**
+ * Separator related constants
+ */
+ const DECIMAL_SEPARATOR = NumberValue::DECIMAL_SEPARATOR;
+ const THOUSANDS_SEPARATOR = NumberValue::THOUSANDS_SEPARATOR;
+
+ /**
+ * Format related constants
+ */
+ const DEFAULT_FORMAT = 'default.format';
+ const VALUE_FORMAT = 'value.format';
+
+ /**
+ * @var IntlNumberFormatter
+ */
+ private static $instance = null;
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * @var integer
+ */
+ private $maxNonExpNumber = null;
+
+ /**
+ * @var integer
+ */
+ private $defaultPrecision = 3;
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $maxNonExpNumber
+ */
+ public function __construct( $maxNonExpNumber ) {
+ $this->maxNonExpNumber = $maxNonExpNumber;
+ $this->options = new Options();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return IntlNumberFormatter
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self(
+ $GLOBALS['smwgMaxNonExpNumber']
+ );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function reset() {
+ $this->options->set( self::DECIMAL_SEPARATOR, false );
+ $this->options->set( self::THOUSANDS_SEPARATOR, false );
+ $this->options->set( self::USER_LANGUAGE, false );
+ $this->options->set( self::CONTENT_LANGUAGE, false );
+ $this->options->set( self::PREFERRED_LANGUAGE, false );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $type
+ * @param string|integer $locale
+ *
+ * @return string
+ */
+ public function getSeparatorByLanguage( $type, $locale = '' ) {
+
+ $language = $locale === self::USER_LANGUAGE ? $this->getUserLanguage() : $this->getContentLanguage();
+
+ if ( $type === self::DECIMAL_SEPARATOR ) {
+ return $this->getPreferredLocalizedSeparator( self::DECIMAL_SEPARATOR, 'smw_decseparator', $language );
+ }
+
+ if ( $type === self::THOUSANDS_SEPARATOR ) {
+ return $this->getPreferredLocalizedSeparator( self::THOUSANDS_SEPARATOR, 'smw_kiloseparator', $language );
+ }
+
+ throw new InvalidArgumentException( $type . " is unknown" );
+ }
+
+ /**
+ * This method formats a float number value according to the given language and
+ * precision settings, with some intelligence to produce readable output. Used
+ * to format a number that was not hand-formatted by a user.
+ *
+ * @param mixed $value input number
+ * @param integer|false $precision optional positive integer, controls how many digits after
+ * the decimal point are shown
+ * @param string|integer $format
+ *
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function format( $value, $precision = false, $format = '' ) {
+
+ if ( $format === self::VALUE_FORMAT ) {
+ return $this->getValueFormattedNumberWithPrecision( $value, $precision );
+ }
+
+ if ( $precision !== false || $format === self::DEFAULT_FORMAT ) {
+ return $this->getDefaultFormattedNumberWithPrecision( $value, $precision );
+ }
+
+ return $this->doFormatByHeuristicRuleWith( $value, $precision );
+ }
+
+ /**
+ * This method formats a float number value according to the given language and
+ * precision settings, with some intelligence to produce readable output. Used
+ * to format a number that was not hand-formatted by a user.
+ *
+ * @param mixed $value input number
+ * @param integer|false $precision optional positive integer, controls how many digits after
+ * the decimal point are shown
+ *
+ * @since 2.1
+ *
+ * @return string
+ */
+ private function doFormatByHeuristicRuleWith( $value, $precision = false ) {
+
+ // BC configuration to keep default behaviour
+ $precision = $this->defaultPrecision;
+
+ $decseparator = $this->getSeparatorByLanguage(
+ self::DECIMAL_SEPARATOR,
+ self::USER_LANGUAGE
+ );
+
+ // If number is a trillion or more, then switch to scientific
+ // notation. If number is less than 0.0000001 (i.e. twice precision),
+ // then switch to scientific notation. Otherwise print number
+ // using number_format. This may lead to 1.200, so then use trim to
+ // remove trailing zeroes.
+ $doScientific = false;
+
+ // @todo: Don't do all this magic for integers, since the formatting does not fit there
+ // correctly. E.g. one would have integers formatted as 1234e6, not as 1.234e9, right?
+ // The "$value!=0" is relevant: we want to scientify numbers that are close to 0, but never 0!
+ if ( ( $precision > 0 ) && ( $value != 0 ) ) {
+ $absValue = abs( $value );
+ if ( $absValue >= $this->maxNonExpNumber ) {
+ $doScientific = true;
+ } elseif ( $absValue < pow( 10, - $precision ) ) {
+ $doScientific = true;
+ } elseif ( $absValue < 1 ) {
+ if ( $absValue < pow( 10, - $precision ) ) {
+ $doScientific = true;
+ } else {
+ // Increase decimal places for small numbers, e.g. .00123 should be 5 places.
+ for ( $i = 0.1; $absValue <= $i; $i *= 0.1 ) {
+ $precision++;
+ }
+ }
+ }
+ }
+
+ if ( $doScientific ) {
+ // Should we use decimal places here?
+ $value = sprintf( "%1.6e", $value );
+ // Make it more readable by removing trailing zeroes from n.n00e7.
+ $value = preg_replace( '/(\\.\\d+?)0*e/u', '${1}e', $value, 1 );
+ // NOTE: do not use the optional $count parameter with preg_replace. We need to
+ // remain compatible with PHP 4.something.
+ if ( $decseparator !== '.' ) {
+ $value = str_replace( '.', $decseparator, $value );
+ }
+ } else {
+ $value = $this->doFormatWithPrecision(
+ $value,
+ $precision,
+ $decseparator,
+ $this->getSeparatorByLanguage( self::THOUSANDS_SEPARATOR, self::USER_LANGUAGE )
+ );
+
+ // Make it more readable by removing ending .000 from nnn.000
+ // Assumes substr is faster than a regular expression replacement.
+ $end = $decseparator . str_repeat( '0', $precision );
+ $lenEnd = strlen( $end );
+
+ if ( substr( $value, - $lenEnd ) === $end ) {
+ $value = substr( $value, 0, - $lenEnd );
+ } else {
+ $decseparator = preg_quote( $decseparator, '/' );
+ // If above replacement occurred, no need to do the next one.
+ // Make it more readable by removing trailing zeroes from nn.n00.
+ $value = preg_replace( "/($decseparator\\d+?)0*$/u", '$1', $value, 1 );
+ }
+ }
+
+ return $value;
+ }
+
+ private function getValueFormattedNumberWithPrecision( $value, $precision = false ) {
+
+ // The decimal are in ISO format (.), the separator as plain representation
+ // may collide with the content language (FR) therefore use the content language
+ // to match the decimal separator
+ if ( $this->isScientific( $value ) ) {
+ return $this->doFormatExponentialNotation( $value );
+ }
+
+ if ( $precision === false || $precision === null ) {
+ $precision = $this->getPrecisionFrom( $value );
+ }
+
+ return $this->doFormatWithPrecision(
+ $value,
+ $precision,
+ $this->getSeparatorByLanguage( self::DECIMAL_SEPARATOR, self::CONTENT_LANGUAGE ),
+ ''
+ );
+ }
+
+ private function getDefaultFormattedNumberWithPrecision( $value, $precision = false ) {
+
+ if ( $precision === false ) {
+ return $this->isDecimal( $value ) ? $this->applyDefaultPrecision( $value ) : floatval( $value );
+ }
+
+ return $this->doFormatWithPrecision(
+ $value,
+ $precision,
+ $this->getSeparatorByLanguage( self::DECIMAL_SEPARATOR, self::USER_LANGUAGE ),
+ $this->getSeparatorByLanguage( self::THOUSANDS_SEPARATOR, self::USER_LANGUAGE )
+ );
+ }
+
+ private function isDecimal( $value ) {
+ return floor( $value ) !== $value;
+ }
+
+ private function isScientific( $value ) {
+ return strpos( $value, 'E' ) !== false || strpos( $value, 'e' ) !== false;
+ }
+
+ private function applyDefaultPrecision( $value ) {
+ return round( $value, $this->defaultPrecision );
+ }
+
+ private function getPrecisionFrom( $value ) {
+ return strlen( strrchr( $value, "." ) ) - 1;
+ }
+
+ private function doFormatExponentialNotation( $value ) {
+ return str_replace(
+ [ '.', 'E' ],
+ [ $this->getSeparatorByLanguage( self::DECIMAL_SEPARATOR, self::CONTENT_LANGUAGE ), 'e' ],
+ $value
+ );
+ }
+
+ private function doFormatWithPrecision( $value, $precision = false, $decimal, $thousand ) {
+
+ $replacement = 0;
+
+ // Don't try to be more precise than the actual value (e.g avoid turning
+ // 72.769482308 into 72.76948230799999350892904)
+ if ( ( $actualPrecision = $this->getPrecisionFrom( $value ) ) < $precision && $actualPrecision > 0 && !$this->isScientific( $value ) ) {
+ $replacement = $precision - $actualPrecision;
+ $precision = $actualPrecision;
+ }
+
+ $value = (float)$value;
+ $isNegative = $value < 0;
+
+ // Format to some level of precision; number_format does rounding and
+ // locale formatting, x and y are used temporarily since number_format
+ // supports only single characters for either
+ $value = number_format( $value, $precision, 'x', 'y' );
+
+ // Due to https://bugs.php.net/bug.php?id=76824
+ if ( $isNegative && $value >= 0 ) {
+ $value = "-$value";
+ }
+
+ $value = str_replace(
+ [ 'x', 'y' ],
+ [
+ $decimal,
+ $thousand
+ ],
+ $value
+ );
+
+ if ( $replacement > 0 ) {
+ $value .= str_repeat( '0', $replacement );
+ }
+
+ return $value;
+ }
+
+ private function getUserLanguage() {
+
+ $language = Message::USER_LANGUAGE;
+
+ // The preferred language is set when the output formatter contained
+ // something like LOCL@es
+
+ if ( $this->options->has( self::PREFERRED_LANGUAGE ) && $this->options->get( self::PREFERRED_LANGUAGE ) ) {
+ $language = $this->options->get( self::PREFERRED_LANGUAGE );
+ } elseif ( $this->options->has( self::USER_LANGUAGE ) && $this->options->get( self::USER_LANGUAGE ) ) {
+ $language = $this->options->get( self::USER_LANGUAGE );
+ }
+
+ return $language;
+ }
+
+ private function getContentLanguage() {
+
+ $language = Message::CONTENT_LANGUAGE;
+
+ if ( $this->options->has( self::CONTENT_LANGUAGE ) && $this->options->get( self::CONTENT_LANGUAGE ) ) {
+ $language = $this->options->get( self::CONTENT_LANGUAGE );
+ }
+
+ return $language;
+ }
+
+ private function getPreferredLocalizedSeparator( $custom, $standard, $language ) {
+
+ if ( $this->options->has( $custom ) && ( $separator = $this->options->get( $custom ) ) !== false ) {
+ return $separator;
+ }
+
+ return Message::get( $standard, Message::TEXT, $language );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/UnitConverter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/UnitConverter.php
new file mode 100644
index 00000000..066c8937
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Number/UnitConverter.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace SMW\DataValues\Number;
+
+use SMW\ApplicationFactory;
+use SMW\CachedPropertyValuesPrefetcher;
+use SMW\DIProperty;
+use SMWDIBlob as DIBlob;
+use SMWNumberValue as NumberValue;
+
+/**
+ * Returns conversion data from a cache instance to enable a responsive query
+ * feedback and eliminate possible repeated DB requests.
+ *
+ * The cache is evicted as soon as the property that contains "Corresponds to"
+ * is altered.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UnitConverter {
+
+ /**
+ * @var NumberValue
+ */
+ private $numberValue;
+
+ /**
+ * @var CachedPropertyValuesPrefetcher
+ */
+ private $cachedPropertyValuesPrefetcher;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $unitIds = [];
+
+ /**
+ * @var array
+ */
+ private $unitFactors = [];
+
+ /**
+ * @var false|string
+ */
+ private $mainUnit = false;
+
+ /**
+ * @var array
+ */
+ protected $prefixalUnitPreference = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param NumberValue $numberValue
+ * @param CachedPropertyValuesPrefetcher|null $cachedPropertyValuesPrefetcher
+ */
+ public function __construct( NumberValue $numberValue, CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher = null ) {
+ $this->numberValue = $numberValue;
+ $this->cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher;
+
+ if ( $this->cachedPropertyValuesPrefetcher === null ) {
+ $this->cachedPropertyValuesPrefetcher = ApplicationFactory::getInstance()->getCachedPropertyValuesPrefetcher();
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getUnitIds() {
+ return $this->unitIds;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getUnitFactors() {
+ return $this->unitFactors;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getMainUnit() {
+ return $this->mainUnit;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getPrefixalUnitPreference() {
+ return $this->prefixalUnitPreference;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ */
+ public function fetchConversionData( DIProperty $property ) {
+
+ $this->unitIds = [];
+ $this->unitFactors = [];
+ $this->mainUnit = false;
+ $this->prefixalUnitPreference = [];
+ $this->errors = [];
+
+ $factors = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getDiWikiPage(),
+ new DIProperty( '_CONV' )
+ );
+
+ $this->numberValue->setContextPage( $property->getDiWikiPage() );
+
+ if ( $factors === null || $factors === [] ) { // no custom type
+ return $this->errors[] = 'smw_nounitsdeclared';
+ }
+
+ $number = '';
+ $unit = '';
+ $asPrefix = false;
+
+ foreach ( $factors as $di ) {
+
+ // ignore corrupted data and bogus inputs
+ if ( !( $di instanceof DIBlob ) ||
+ ( $this->numberValue->parseNumberValue( $di->getString(), $number, $unit, $asPrefix ) != 0 ) ||
+ ( $number == 0 ) ) {
+ continue;
+ }
+
+ $this->matchUnitAliases(
+ $number,
+ $asPrefix,
+ preg_split( '/\s*,\s*/u', $unit )
+ );
+ }
+
+ // No unit with factor 1? Make empty string the main unit.
+ if ( $this->mainUnit === false ) {
+ $this->mainUnit = '';
+ }
+
+ // Always add an extra empty unit; not as a synonym for the main unit
+ // but as a new unit with ID '' so if users do not give any unit, the
+ // conversion tooltip will still display the main unit for clarity
+ // (the empty unit is never displayed; we filter it when making
+ // conversion values)
+ $this->unitFactors = [ '' => 1 ] + $this->unitFactors;
+ $this->unitIds[''] = '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty|null $property
+ */
+ public function initConversionData( DIProperty $property = null ) {
+
+ if ( $property === null || ( $propertyDiWikiPage = $property->getDiWikiPage() ) === null ) {
+ return;
+ }
+
+ $blobStore = $this->cachedPropertyValuesPrefetcher->getBlobStore();
+
+ // Ensure that when the property page is altered the cache gets
+ // evicted
+ $hash = $this->cachedPropertyValuesPrefetcher->getRootHashFrom(
+ $propertyDiWikiPage
+ );
+
+ $container = $blobStore->read(
+ $hash
+ );
+
+ $key = '--conv';
+
+ if ( $container->has( $key ) ) {
+ $data = $container->get( $key );
+ $this->unitIds = $data['ids'];
+ $this->unitFactors = $data['factors'];
+ $this->mainUnit = $data['main'];
+ $this->prefixalUnitPreference = $data['prefix'];
+ return;
+ }
+
+ $this->fetchConversionData( $property );
+
+ if ( $this->errors !== [] ) {
+ return;
+ }
+
+ $data = [
+ 'ids' => $this->unitIds,
+ 'factors' => $this->unitFactors,
+ 'main' => $this->mainUnit,
+ 'prefix' => $this->prefixalUnitPreference
+ ];
+
+ $container->set( $key, $data );
+
+ $blobStore->save(
+ $container
+ );
+ }
+
+ private function matchUnitAliases( $number, $asPrefix, array $unitAliases ) {
+ $first = true;
+
+ foreach ( $unitAliases as $unit ) {
+ $unit = $this->numberValue->normalizeUnit( $unit );
+
+ // Legacy match the preserve some behaviour where spaces where normalized
+ // no matter what
+ $normalizedUnit = $this->numberValue->normalizeUnit(
+ str_replace( [ '&nbsp;', '&#160;', '&thinsp;', ' ' ], '', $unit )
+ );
+
+ if ( $first ) {
+ $unitid = $unit;
+ if ( $number == 1 ) { // add main unit to front of array (displayed first)
+ $this->mainUnit = $unit;
+ $this->unitFactors = [ $unit => 1 ] + $this->unitFactors;
+ } else { // non-main units are not ordered (can be modified via display units)
+ $this->unitFactors[$unit] = $number;
+ }
+ $first = false;
+ }
+ // add all known units to m_unitids to simplify checking for them
+ $this->unitIds[$unit] = $unitid;
+ $this->unitIds[$normalizedUnit] = $unitid;
+ $this->prefixalUnitPreference[$unit] = $asPrefix;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyChainValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyChainValue.php
new file mode 100644
index 00000000..7c6054b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyChainValue.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DataValueFactory;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyChainValue extends StringValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__pchn';
+
+ /**
+ * @var PropertyValue[]
+ */
+ private $propertyValues = [];
+
+ /**
+ * @var PropertyValue
+ */
+ private $lastPropertyChainValue;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public static function isChained( $value ) {
+ return strpos( $value, '.' ) !== false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyValue
+ */
+ public function getLastPropertyChainValue() {
+ return $this->lastPropertyChainValue;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyValue[]
+ */
+ public function getPropertyChainValues() {
+ return $this->propertyValues;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function setCaption( $caption ) {
+ $this->m_caption = $caption;
+
+ if ( $this->lastPropertyChainValue !== null ) {
+ $this->lastPropertyChainValue->setCaption( $caption );
+ }
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( $this->lastPropertyChainValue !== null ) {
+ return $this->lastPropertyChainValue->getShortWikiText( $linker ) . $this->doHintPropertyChainMembers();
+ }
+
+ return '';
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( $this->lastPropertyChainValue !== null ) {
+ return $this->lastPropertyChainValue->getLongWikiText( $linker ) . $this->doHintPropertyChainMembers();
+ }
+
+ return '';
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->lastPropertyChainValue !== null ) {
+ return $this->lastPropertyChainValue->getShortHTMLText( $linker ) . $this->doHintPropertyChainMembers();
+ }
+
+ return '';
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->lastPropertyChainValue !== null ) {
+ return $this->lastPropertyChainValue->getLongHTMLText( $linker ) . $this->doHintPropertyChainMembers();
+ }
+
+ return '';
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ */
+ public function getWikiValue() {
+ return $this->lastPropertyChainValue !== null ? $this->lastPropertyChainValue->getWikiValue() : '';
+ }
+
+ /**
+ * @see PropertyValue::isVisible
+ */
+ public function isVisible() {
+ return $this->isValid() && ( $this->lastPropertyChainValue->getDataItem()->isUserDefined() || $this->lastPropertyChainValue->getDataItem()->getLabel() !== '' );
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ *
+ * @param $dataitem SMWDataItem
+ *
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ return false;
+ }
+
+ $this->m_caption = false;
+ $this->m_dataitem = $dataItem;
+
+ $this->initPropertyChain( $dataItem->getString() );
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ * @note called by DataValue::setUserValue
+ *
+ * @param string $userValue
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ }
+
+ if ( !$this->isChained( $value ) ) {
+ $this->addErrorMsg( 'smw-datavalue-propertychain-missing-chain-indicator' );
+ }
+
+ $this->initPropertyChain( $value );
+
+ $this->m_dataitem = new DIBlob( $value );
+ }
+
+ private function initPropertyChain( $value ) {
+
+ $chain = explode( '.', $value );
+
+ // Get the last which represents the final output
+ // Foo.Bar.Foobar.Baz
+ $last = array_pop( $chain );
+
+ $this->lastPropertyChainValue = DataValueFactory::getInstance()->newPropertyValueByLabel( $last );
+
+ if ( !$this->lastPropertyChainValue->isValid() ) {
+ return $this->addError( $this->lastPropertyChainValue->getErrors() );
+ }
+
+ $this->lastPropertyChainValue->copyOptions( $this->getOptions() );
+
+ // Generate a forward list from the remaining property labels
+ // Foo.Bar.Foobar
+ foreach ( $chain as $value ) {
+ $propertyValue = DataValueFactory::getInstance()->newPropertyValueByLabel( $value );
+
+ if ( !$propertyValue->isValid() ) {
+ continue;
+ }
+
+ $propertyValue->copyOptions( $this->getOptions() );
+
+ $this->propertyValues[] = $propertyValue;
+ }
+ }
+
+ private function doHintPropertyChainMembers() {
+ return '&nbsp;' . \Html::rawElement( 'span', [ 'title' => $this->m_dataitem ], 'â ‰' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyValue.php
new file mode 100644
index 00000000..d88286d9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/PropertyValue.php
@@ -0,0 +1,501 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\DIProperty;
+use SMW\Exception\DataItemException;
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+
+/**
+ * Objects of this class represent properties in SMW.
+ *
+ * This class represents both normal (user-defined) properties and predefined
+ * ("special") properties. Predefined properties may still have a standard label
+ * (and associated wiki article) and they will behave just like user-defined
+ * properties in most cases (e.g. when asking for a printout text, a link to the
+ * according page is produced).
+ *
+ * It is possible that predefined properties have no visible label at all, if they
+ * are used only internally and never specified by or shown to the user. Those
+ * will use their internal ID as DB key, and empty texts for most printouts. All
+ * other properties use their canonical DB key (even if they are predefined and
+ * have an id).
+ *
+ * Functions are provided to check whether a property is visible or
+ * user-defined, and to get the internal ID, if any.
+ *
+ * @note This datavalue is used only for representing properties and, possibly
+ * objects/values, but never for subjects (pages as such). Hence it does not
+ * provide a complete Title-like interface, or support for things like sortkey.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class PropertyValue extends DataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__pro';
+
+ /**
+ * Avoid the display of a tooltip
+ */
+ const OPT_NO_HIGHLIGHT = 'no.highlight';
+
+ /**
+ * Use linker with the highlighter
+ */
+ const OPT_HIGHLIGHT_LINKER = 'highlight.linker';
+
+ /**
+ * Avoid the display of a preferred label marker
+ */
+ const OPT_NO_PREF_LHNT = 'no.preflabel.marker';
+
+ /**
+ * Special formatting of the label/preferred label
+ */
+ const FORMAT_LABEL = 'format.label';
+
+ /**
+ * Label to be used for matching a DB search
+ */
+ const SEARCH_LABEL = 'search.label';
+
+ /**
+ * Cache for wiki page value object associated to this property, or
+ * null if no such page exists. Use getWikiPageValue() to get the data.
+ * @var SMWWikiPageValue
+ */
+ protected $m_wikipage = null;
+
+ /**
+ * @var array
+ */
+ protected $linkAttributes = [];
+
+ /**
+ * @var string
+ */
+ private $preferredLabel = '';
+
+ /**
+ * @var DIProperty
+ */
+ private $inceptiveProperty;
+
+ /**
+ * @var ValueFormatter
+ */
+ private $valueFormatter;
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid = self::TYPE_ID ) {
+ parent::__construct( $typeid );
+ }
+
+ /**
+ * @deprecated since 3.0
+ */
+ static private function makeUserProperty( $propertyLabel ) {
+ return DataValueFactory::getInstance()->newPropertyValueByLabel( $propertyLabel );
+ }
+
+ /**
+ * @removed since 3.0
+ */
+ static private function makeProperty( $propertyid ) {
+ $diProperty = new DIProperty( $propertyid );
+ $dvProperty = new SMWPropertyValue( self::TYPE_ID );
+ $dvProperty->setDataItem( $diProperty );
+ return $dvProperty;
+ }
+
+ /**
+ * We use the internal wikipage object to store some of this objects data.
+ * Clone it to make sure that data can be modified independently from the
+ * original object's content.
+ */
+ public function __clone() {
+ if ( !is_null( $this->m_wikipage ) ) {
+ $this->m_wikipage = clone $this->m_wikipage;
+ }
+ }
+
+ /**
+ * @note If the inceptive property and the property referenced in dataItem
+ * are not equal then the dataItem represents the end target to which the
+ * inceptive property has been redirected.
+ *
+ * @since 2.4
+ *
+ * @return DIProperty
+ */
+ public function getInceptiveProperty() {
+ return $this->inceptiveProperty;
+ }
+
+ /**
+ * Extended parsing function to first check whether value refers to pre-defined
+ * property, resolve aliases, and set internal property id accordingly.
+ * @todo Accept/enforce property namespace.
+ */
+ protected function parseUserValue( $value ) {
+ $this->m_wikipage = null;
+
+ $propertyValueParser = $this->dataValueServiceFactory->getValueParser(
+ $this
+ );
+
+ $propertyValueParser->isQueryContext(
+ $this->getOption( self::OPT_QUERY_CONTEXT )
+ );
+
+ $reqCapitalizedFirstChar = $this->getContextPage() !== null && $this->getContextPage()->getNamespace() === SMW_NS_PROPERTY;
+
+ $propertyValueParser->reqCapitalizedFirstChar(
+ $reqCapitalizedFirstChar
+ );
+
+ list( $propertyName, $capitalizedName, $inverse ) = $propertyValueParser->parse( $value );
+
+ foreach ( $propertyValueParser->getErrors() as $error ) {
+ return $this->addErrorMsg( $error, Message::PARSE );
+ }
+
+ try {
+ $this->m_dataitem = $this->createDataItemFrom(
+ $reqCapitalizedFirstChar,
+ $propertyName,
+ $capitalizedName,
+ $inverse
+ );
+ } catch ( DataItemException $e ) { // happens, e.g., when trying to sort queries by property "-"
+ $this->addErrorMsg( [ 'smw_noproperty', $value ] );
+ $this->m_dataitem = new DIProperty( 'ERROR', false ); // just to have something
+ }
+
+ // @see the SMW_DV_PROV_DTITLE explanation
+ if ( $this->isEnabledFeature( SMW_DV_PROV_DTITLE ) ) {
+ $dataItem = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getPropertyFromDisplayTitle(
+ $value
+ );
+
+ $this->m_dataitem = $dataItem ? $dataItem : $this->m_dataitem;
+ }
+
+ // Copy the original DI to ensure we can compare it against a possible redirect
+ $this->inceptiveProperty = $this->m_dataitem;
+
+ if ( $this->isEnabledFeature( SMW_DV_PROV_REDI ) ) {
+ $this->m_dataitem = $this->m_dataitem->getRedirectTarget();
+ }
+
+ // If no external caption has been invoked then fetch a preferred label
+ if ( $this->m_caption === false || $this->m_caption === '' ) {
+ $this->preferredLabel = $this->m_dataitem->getPreferredLabel( $this->getOption( self::OPT_USER_LANGUAGE ) );
+ }
+
+ // Use the preferred label as first choice for a caption, if available
+ if ( $this->preferredLabel !== '' ) {
+ $this->m_caption = $this->preferredLabel;
+ } elseif ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+ }
+
+ /**
+ * @see SMWDataValue::loadDataItem()
+ * @param $dataitem DataItem
+ * @return boolean
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() !== DataItem::TYPE_PROPERTY ) {
+ return false;
+ }
+
+ $this->inceptiveProperty = $dataItem;
+ $this->m_dataitem = $dataItem;
+ $this->preferredLabel = $this->m_dataitem->getPreferredLabel();
+
+ unset( $this->m_wikipage );
+ $this->m_caption = false;
+ $this->linkAttributes = [];
+
+ if ( $this->preferredLabel !== '' ) {
+ $this->m_caption = $this->preferredLabel;
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getPreferredLabel() {
+ return $this->preferredLabel;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $linkAttributes
+ */
+ public function setLinkAttributes( array $linkAttributes ) {
+ $this->linkAttributes = $linkAttributes;
+
+ if ( $this->getWikiPageValue() instanceof SMWDataValue ) {
+ $this->m_wikipage->setLinkAttributes( $linkAttributes );
+ }
+ }
+
+ public function setCaption( $caption ) {
+ parent::setCaption( $caption );
+ if ( $this->getWikiPageValue() instanceof SMWDataValue ) { // pass caption to embedded datavalue (used for printout)
+ $this->m_wikipage->setCaption( $caption );
+ }
+ }
+
+ public function setOutputFormat( $formatstring ) {
+
+ if ( $formatstring === false || $formatstring === '' ) {
+ return;
+ }
+
+ $this->m_outformat = $formatstring;
+
+ if ( $this->getWikiPageValue() instanceof SMWDataValue ) {
+ $this->m_wikipage->setOutputFormat( $formatstring );
+ }
+ }
+
+ public function setInverse( $isinverse ) {
+ return $this->m_dataitem = new DIProperty( $this->m_dataitem->getKey(), ( $isinverse == true ) );
+ }
+
+ /**
+ * Return a wiki page value that can be used for displaying this
+ * property, or null if no such wiki page exists (for predefined
+ * properties without any label).
+ * @return SMWWikiPageValue or null
+ */
+ public function getWikiPageValue() {
+
+ if ( isset( $this->m_wikipage ) ) {
+ return $this->m_wikipage;
+ }
+
+ $diWikiPage = $this->m_dataitem->getCanonicalDiWikiPage();
+
+ if ( $diWikiPage !== null ) {
+ $this->m_wikipage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null, $this->m_caption );
+ $this->m_wikipage->setOutputFormat( $this->m_outformat );
+ $this->m_wikipage->setLinkAttributes( $this->linkAttributes );
+ $this->m_wikipage->copyOptions( $this->getOptions() );
+ $this->addError( $this->m_wikipage->getErrors() );
+ } else { // should rarely happen ($value is only changed if the input $value really was a label for a predefined prop)
+ $this->m_wikipage = null;
+ }
+
+ return $this->m_wikipage;
+ }
+
+ /**
+ * Return TRUE if this is a property that can be displayed, and not a pre-defined
+ * property that is used only internally and does not even have a user-readable name.
+ * @note Every user defined property is necessarily visible.
+ */
+ public function isVisible() {
+ return $this->isValid() && ( $this->m_dataitem->isUserDefined() || $this->m_dataitem->getCanonicalLabel() !== '' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isRestricted() {
+
+ if ( !$this->isValid() ) {
+ return true;
+ }
+
+ $propertyRestrictionExaminer = $this->dataValueServiceFactory->getPropertyRestrictionExaminer();
+
+ $propertyRestrictionExaminer->isQueryContext(
+ $this->getOption( self::OPT_QUERY_CONTEXT )
+ );
+
+ $propertyRestrictionExaminer->checkRestriction(
+ $this->getDataItem(),
+ $this->getContextPage()
+ );
+
+ if ( !$propertyRestrictionExaminer->hasRestriction() ) {
+ return false;
+ }
+
+ $this->restrictionError = $propertyRestrictionExaminer->getError();
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * @return string
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::WIKI_SHORT, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ *
+ * @return string
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::HTML_SHORT, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ *
+ * @return string
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::WIKI_LONG, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ *
+ * @return string
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::HTML_LONG, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ *
+ * @return string
+ */
+ public function getWikiValue() {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::VALUE ] );
+ }
+
+ /**
+ * Outputs a formatted property label that takes into account preferred/
+ * canonical label characteristics
+ *
+ * @param integer|string $format
+ * @param Linker|null $linker
+ *
+ * @return string
+ */
+ public function getFormattedLabel( $format = DataValueFormatter::VALUE, $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ $this->setOption(
+ self::FORMAT_LABEL,
+ $format
+ );
+
+ return $this->valueFormatter->format( $this, [ self::FORMAT_LABEL, $linker ] );
+ }
+
+ /**
+ * Outputs a label that corresponds to the display and sort characteristics (
+ * e.g. display title etc.) and can be used to initiate a match and search
+ * process.
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getSearchLabel() {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ self::SEARCH_LABEL ] );
+ }
+
+ /**
+ * Convenience method to find the type id of this property. Most callers
+ * should rather use DIProperty::findPropertyTypeId() directly. Note
+ * that this is not the same as getTypeID(), which returns the id of
+ * this property datavalue.
+ *
+ * @return string
+ */
+ public function getPropertyTypeID() {
+
+ if ( !$this->isValid() ) {
+ return '__err';
+ }
+
+ return $this->m_dataitem->findPropertyTypeId();
+ }
+
+ private function createDataItemFrom( $reqCapitalizedFirstChar, $propertyName, $capitalizedName, $inverse ) {
+
+ $contentLanguage = $this->getOption( self::OPT_CONTENT_LANGUAGE );
+
+ // Probe on capitalizedFirstChar because we only want predefined
+ // properties (e.g. Has type vs. has type etc.) to adhere the rule while
+ // custom (user) defined properties can appear in any form
+ if ( $reqCapitalizedFirstChar ) {
+ $dataItem = DIProperty::newFromUserLabel( $capitalizedName, $inverse, $contentLanguage );
+ $propertyName = $dataItem->isUserDefined() ? $propertyName : $capitalizedName;
+ }
+
+ return DIProperty::newFromUserLabel( $propertyName, $inverse, $contentLanguage );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ReferenceValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ReferenceValue.php
new file mode 100644
index 00000000..cbcb4208
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ReferenceValue.php
@@ -0,0 +1,294 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\ApplicationFactory;
+use SMW\DataModel\ContainerSemanticData;
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMWDIContainer as DIContainer;
+use SMWDITime as DITime;
+
+/**
+ * ReferenceValue allows to define additional DV to describe the state of a
+ * SourceValue in terms of provenance or referential evidence. ReferenceValue is
+ * stored as separate entity to the original subject in order to encapsulate the
+ * SourceValue from the remaining annotations with reference to a subject.
+ *
+ * Defining which fields are required can vary and therefore is left to the user
+ * to specify such requirements using the `'Has fields' property.
+ *
+ * For example, declaring `[[Has fields::SomeValue;Date;SomeUrl;...]]` on a
+ * `SomeProperty` property page is to define:
+ *
+ * - a property called `SomeValue` with its own specification
+ * - a date property with the Date type
+ * - a property called `SomeUrl` with its own specification
+ * - ... any other property the users expects to require when making a value
+ * annotation of this type
+ *
+ * An annotation like `[[SomeProperty::Foo;12-12-1212;http://example.org]]` is
+ * expected to be a concatenated string and to be separated by ';' to indicate
+ * the next value string and will corespondent to the index of the `Has fields`
+ * declaration.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReferenceValue extends AbstractMultiValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_ref_rec';
+
+ /**
+ * @var DIProperty[]|null
+ */
+ private $properties = null;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function setFieldProperties( array $properties ) {
+ foreach ( $properties as $property ) {
+ if ( $property instanceof DIProperty ) {
+ $this->properties[] = $property;
+ }
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getProperties() {
+ return $this->properties;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getValuesFromString( $value ) {
+ // #664 / T17732
+ $value = str_replace( "\;", "-3B", $value );
+
+ // Bug 21926 / T23926
+ // Values that use html entities are encoded with a semicolon
+ $value = htmlspecialchars_decode( $value, ENT_QUOTES );
+ $values = preg_split( '/[\s]*;[\s]*/u', trim( $value ) );
+
+ return str_replace( "-3B", ";", $values );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getShortWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getShortHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_SHORT, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getLongWikiText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::WIKI_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getLongHTMLText( $linker = null ) {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::HTML_LONG, $linker );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getWikiValue() {
+ return $this->dataValueServiceFactory->getValueFormatter( $this )->format( DataValueFormatter::VALUE );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getPropertyDataItems() {
+
+ if ( $this->properties === null ) {
+ $this->properties = $this->getFieldProperties( $this->getProperty() );
+
+ if ( count( $this->properties ) == 0 ) {
+ $this->addErrorMsg( [ 'smw-datavalue-reference-invalid-fields-definition' ], Message::PARSE );
+ }
+ }
+
+ return $this->properties;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getDataItems() {
+ return parent::getDataItems();
+ }
+
+ /**
+ * @note called by DataValue::setUserValue
+ * @see DataValue::parseUserValue
+ *
+ * {@inheritDoc}
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ return;
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData( $value );
+ $sortKeys = [];
+
+ $values = $this->getValuesFromString( $value );
+ $index = 0; // index in value array
+
+ $propertyIndex = 0; // index in property list
+ $empty = true;
+
+ foreach ( $this->getPropertyDataItems() as $property ) {
+
+ if ( !array_key_exists( $index, $values ) || $this->getErrors() !== [] ) {
+ break; // stop if there are no values left
+ }
+
+ // generating the DVs:
+ if ( ( $values[$index] === '' ) || ( $values[$index] == '?' ) ) { // explicit omission
+ $index++;
+ } else {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ $values[$index],
+ false,
+ $containerSemanticData->getSubject()
+ );
+
+ if ( $dataValue->isValid() ) { // valid DV: keep
+ $dataItem = $dataValue->getDataItem();
+
+ $containerSemanticData->addPropertyObjectValue(
+ $property,
+ $dataItem
+ );
+
+ // Chronological order determined first
+ if ( $dataItem instanceof DITime ) {
+ array_unshift( $sortKeys, $dataItem->getSortKey() );
+ } else {
+ $sortKeys[] = $dataItem->getSortKey();
+ }
+
+ $index++;
+ $empty = false;
+ } elseif ( $index == 0 || ( count( $values ) - $index ) == ( count( $this->properties ) - $propertyIndex ) ) {
+ $containerSemanticData->addPropertyObjectValue( $property, $dataValue->getDataItem() );
+ $this->addError( $dataValue->getErrors() );
+ ++$index;
+ }
+ }
+
+ ++$propertyIndex;
+ }
+
+ if ( $empty && $this->getErrors() === [] ) {
+ $this->addErrorMsg( [ 'smw_novalues' ] );
+ }
+
+ // Remember the data to extend the sortkey
+ $containerSemanticData->setExtensionData( 'sort.data', implode( ';', $sortKeys ) );
+
+ $this->m_dataitem = new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_CONTAINER ) {
+ $this->m_dataitem = $dataItem;
+ return true;
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $semanticData = new ContainerSemanticData( $dataItem );
+ $semanticData->copyDataFrom( ApplicationFactory::getInstance()->getStore()->getSemanticData( $dataItem ) );
+ $this->m_dataitem = new DIContainer( $semanticData );
+ return true;
+ }
+
+ return false;
+ }
+
+ private function newContainerSemanticData( $value ) {
+
+ if ( $this->m_contextPage === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ $containerSemanticData->skipAnonymousCheck();
+ } else {
+ $subobjectName = '_REF' . md5( $value );
+
+ $subject = new DIWikiPage(
+ $this->m_contextPage->getDBkey(),
+ $this->m_contextPage->getNamespace(),
+ $this->m_contextPage->getInterwiki(),
+ $subobjectName
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return $containerSemanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/StringValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/StringValue.php
new file mode 100644
index 00000000..cb68f84c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/StringValue.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Implements a string/text based datavalue suitable for defining text properties.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ */
+class StringValue extends DataValue {
+
+ /**
+ * DV text identifier
+ */
+ const TYPE_ID = '_txt';
+
+ /**
+ * DV identifier
+ */
+ const TYPE_LEGACY_ID = '_str';
+
+ /**
+ * DV code identifier
+ */
+ const TYPE_COD_ID = '_cod';
+
+ /**
+ * @var ValueFormatter
+ */
+ private $valueFormatter;
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::WIKI_SHORT, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::HTML_SHORT, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::WIKI_LONG, $linker ] );
+ }
+
+ /**
+ * @todo Rather parse input to obtain properly formatted HTML.
+ * @see DataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::HTML_LONG, $linker ] );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ *
+ * {@inheritDoc}
+ */
+ public function getWikiValue() {
+
+ if ( $this->valueFormatter === null ) {
+ $this->valueFormatter = $this->dataValueServiceFactory->getValueFormatter( $this );
+ }
+
+ return $this->valueFormatter->format( $this, [ DataValueFormatter::VALUE, null ] );
+ }
+
+ /**
+ * @see DataValue::getInfolinks
+ *
+ * {@inheritDoc}
+ */
+ public function getInfolinks() {
+
+ if ( $this->m_typeid != '_cod' ) {
+ return parent::getInfolinks();
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getLength() {
+
+ if ( !$this->isValid() ) {
+ return 0;
+ }
+
+ return mb_strlen( $this->m_dataitem->getString() );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * {@inheritDoc}
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $value === '' ) {
+ $this->addErrorMsg( 'smw_emptystring' );
+ }
+
+ $this->m_dataitem = new DIBlob( $value );
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * {@inheritDoc}
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ return false;
+ }
+
+ $this->m_caption = false;
+ $this->m_dataitem = $dataItem;
+
+ return true;
+ }
+
+ /**
+ * @see DataValue::getServiceLinkParams
+ *
+ * {@inheritDoc}
+ */
+ protected function getServiceLinkParams() {
+
+ if ( !$this->isValid() ) {
+ return false;
+ }
+
+ // Create links to mapping services based on a wiki-editable message. The parameters
+ // available to the message are:
+ // $1: urlencoded string
+ return [ rawurlencode( $this->m_dataitem->getString() ) ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TelephoneUriValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TelephoneUriValue.php
new file mode 100644
index 00000000..24be076d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TelephoneUriValue.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMWURIValue as UriValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TelephoneUriValue extends UriValue {
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( '_tel' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TemperatureValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TemperatureValue.php
new file mode 100644
index 00000000..37048df8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TemperatureValue.php
@@ -0,0 +1,221 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMWDINumber as DINumber;
+use SMWNumberValue as NumberValue;
+
+/**
+ * This datavalue implements unit support for measuring temperatures. This is
+ * mostly an example implementation of how to realise custom unit types easily.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class TemperatureValue extends NumberValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '_tem';
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * NumberValue::convertToMainUnit
+ */
+ protected function convertToMainUnit( $number, $unit ) {
+
+ $this->m_unitin = $this->getUnitID( $unit );
+
+ if ( ( $value = $this->convertToKelvin( $number, $this->m_unitin ) ) === false ) {
+ return false;
+ }
+
+ $this->m_dataitem = new DINumber( $value );
+
+ return true;
+ }
+
+ /**
+ * NumberValue::makeConversionValues
+ */
+ protected function makeConversionValues() {
+
+ if ( $this->m_unitvalues !== false ) {
+ return; // do this only once
+ }
+
+ $this->m_unitvalues = [];
+
+ if ( !$this->isValid() ) {
+ return $this->m_unitvalues;
+ }
+
+ $displayUnit = $this->getPreferredDisplayUnit();
+ $number = $this->m_dataitem->getNumber();
+
+ $unitvalues = [
+ 'K' => $number,
+ '°C' => $number - 273.15,
+ '°F' => ( $number - 273.15 ) * 1.8 + 32,
+ '°R' => ( $number ) * 1.8
+ ];
+
+ if ( isset( $unitvalues[$displayUnit] ) ) {
+ $this->m_unitvalues[$displayUnit] = $unitvalues[$displayUnit];
+ }
+
+ $this->m_unitvalues += $unitvalues;
+ }
+
+ /**
+ * NumberValue::makeUserValue
+ */
+ protected function makeUserValue() {
+
+ if ( ( $this->m_outformat ) && ( $this->m_outformat != '-' ) &&
+ ( $this->m_outformat != '-n' ) && ( $this->m_outformat != '-u' ) ) { // first try given output unit
+ $printUnit = $this->normalizeUnit( $this->m_outformat );
+ $this->m_unitin = $this->getUnitID( $printUnit );
+ } else {
+ $this->m_unitin = $this->getPreferredDisplayUnit();
+ $printUnit = $this->m_unitin;
+ }
+
+ $value =$this->convertToUnit(
+ $this->m_dataitem->getNumber(),
+ $this->m_unitin
+ );
+
+ // -u is the format for displaying the unit only
+ if ( $this->m_outformat == '-u' ) {
+ $this->m_caption = '';
+ } elseif ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ) {
+ $this->m_caption = $this->getLocalizedFormattedNumber( $value );
+ $this->m_caption .= '&#160;';
+ } else {
+ $this->m_caption = $this->getNormalizedFormattedNumber( $value );
+ $this->m_caption .= ' ';
+ }
+
+ // -n is the format for displaying the number only
+ if ( $this->m_outformat == '-n' ) {
+ $printUnit = '';
+ }
+
+ $this->m_caption .= $printUnit;
+ }
+
+ /**
+ * Helper method to find the main representation of a certain unit.
+ */
+ protected function getUnitID( $unit ) {
+ /// TODO possibly localise some of those strings
+ switch ( $unit ) {
+ case '':
+ case 'K':
+ case 'Kelvin':
+ case 'kelvin':
+ case 'kelvins':
+ return 'K';
+ // There's a dedicated Unicode character (℃, U+2103) for degrees C.
+ // Your font may or may not display it; do not be alarmed.
+ case '°C':
+ case '℃':
+ case 'Celsius':
+ case 'centigrade':
+ return '°C';
+ case '°F':
+ case 'Fahrenheit':
+ return '°F';
+ case '°R':
+ case 'Rankine':
+ return '°R';
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * NumberValue::getUnitList
+ */
+ public function getUnitList() {
+ return [ 'K', '°C', '°F', '°R' ];
+ }
+
+ /**
+ * NumberValue::getUnit
+ */
+ public function getUnit() {
+ return 'K';
+ }
+
+ private function getPreferredDisplayUnit() {
+
+ $unit = $this->getUnit();
+
+ if ( $this->getProperty() === null ) {
+ return $unit;
+ }
+
+ $units = $this->dataValueServiceFactory->getPropertySpecificationLookup()->getDisplayUnits(
+ $this->getProperty()
+ );
+
+ if ( $units !== null && $units !== [] ) {
+ $unit = $this->getUnitID( end( $units ) );
+ }
+
+ return $this->getUnitID( $unit );
+ }
+
+ private function convertToKelvin( $number, $unit ) {
+
+ switch ( $unit ) {
+ case 'K':
+ return $number;
+ break;
+ case '°C':
+ return $number + 273.15;
+ break;
+ case '°F':
+ return ( $number - 32 ) / 1.8 + 273.15;
+ break;
+ case '°R':
+ return ( $number ) / 1.8;
+ }
+
+ return false; // unsupported unit
+ }
+
+ private function convertToUnit( $number, $unit ) {
+
+ switch ( $unit ) {
+ case 'K':
+ return $number;
+ break;
+ case '°C':
+ return $number - 273.15;
+ break;
+ case '°F':
+ return ( $number - 273.15 ) * 1.8 + 32;
+ break;
+ case '°R':
+ return ( $number ) * 1.8;
+ break;
+ // default: unit not supported
+ }
+
+ return 0;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/CalendarModel.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/CalendarModel.php
new file mode 100644
index 00000000..8ac24544
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/CalendarModel.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace SMW\DataValues\Time;
+
+/**
+ * It is assumed that the changeover from the Julian calendar to the Gregorian
+ * calendar occurred in October of 1582.
+ *
+ * For dates on or before 4 October 1582, the Julian calendar is used; for dates
+ * on or after 15 October 1582, the Gregorian calendar is used.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface CalendarModel {
+
+ /**
+ * Gregorian calendar
+ */
+ const CM_GREGORIAN = 1;
+
+ /**
+ * Julian calendar
+ */
+ const CM_JULIAN = 2;
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Components.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Components.php
new file mode 100644
index 00000000..f1aea129
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Components.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\DataValues\Time;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Components {
+
+ /**
+ * @var array
+ */
+ public static $months = [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December'
+ ];
+
+ /**
+ * @var array
+ */
+ public static $monthsShort = [
+ 'Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'May',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec'
+ ];
+
+ /**
+ * @var []
+ */
+ private $components = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $components
+ */
+ public function __construct( array $components = [] ) {
+ $this->components = $components;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function get( $key ) {
+
+ if ( isset( $this->components[$key] ) ) {
+ return $this->components[$key];
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/IntlTimeFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/IntlTimeFormatter.php
new file mode 100644
index 00000000..888d6711
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/IntlTimeFormatter.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace SMW\DataValues\Time;
+
+use Language;
+use SMW\Localizer;
+use SMWDITime as DITime;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class IntlTimeFormatter {
+
+ const LOCL_DEFAULT = 0;
+ const LOCL_TIMEZONE = 0x2;
+ const LOCL_TIMEOFFSET = 0x4;
+
+ /**
+ * @var DITime
+ */
+ private $dataItem;
+
+ /**
+ * @var Language
+ */
+ private $language;
+
+ /**
+ * @var boolean
+ */
+ private $hasLocalTimeCorrection = false;
+
+ /**
+ * @since 2.4
+ *
+ * @param DITime $dataItem
+ * @param Language|null $language
+ */
+ public function __construct( DITime $dataItem, Language $language = null ) {
+ $this->dataItem = $dataItem;
+ $this->language = $language;
+
+ if ( $this->language === null ) {
+ $this->language = Localizer::getInstance()->getContentLanguage();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasLocalTimeCorrection() {
+ return $this->hasLocalTimeCorrection;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $formatFlag
+ *
+ * @return string|boolean
+ */
+ public function getLocalizedFormat( $formatFlag = self::LOCL_DEFAULT ) {
+
+ $dateTime = $this->dataItem->asDateTime();
+ $timezone = '';
+
+ $this->hasLocalTimeCorrection = false;
+
+ if ( !$dateTime ) {
+ return false;
+ }
+
+ $localizer = Localizer::getInstance();
+
+ if ( ( self::LOCL_TIMEOFFSET & $formatFlag ) != 0 ) {
+ $dateTime = $localizer->getLocalTime( $dateTime );
+ $this->hasLocalTimeCorrection = isset( $dateTime->hasLocalTimeCorrection ) ? $dateTime->hasLocalTimeCorrection : false;
+ }
+
+ if ( ( self::LOCL_TIMEZONE & $formatFlag ) != 0 ) {
+ $timezone = $this->dataItem->getTimezone();
+ $dateTime = Timezone::getModifiedTime( $dateTime, $timezone );
+ }
+
+ $lang = $localizer->getLang(
+ $this->language
+ );
+
+ $precision = $this->dataItem->getPrecision();
+
+ // Look for the Z precision which indicates the position of the TZ
+ if ( $precision === SMW_PREC_YMDT && $timezone !== '' ) {
+ $precision = SMW_PREC_YMDTZ;
+ }
+
+ $preferredDateFormatByPrecision = $lang->getPreferredDateFormatByPrecision(
+ $precision
+ );
+
+ // Mark the position since we cannot use DateTime::setTimezone in case
+ // it is a military zone
+ $preferredDateFormatByPrecision = str_replace( 'T', '**', $preferredDateFormatByPrecision );
+
+ $dateString = $this->formatWithLocalizedTextReplacement(
+ $dateTime,
+ $preferredDateFormatByPrecision
+ );
+
+ return str_replace( '**', $timezone, $dateString );
+ }
+
+ /**
+ * Permitted formatting options are specified by http://php.net/manual/en/function.date.php
+ *
+ * @since 2.4
+ *
+ * @param string $format
+ *
+ * @return string|boolean
+ */
+ public function format( $format ) {
+
+ $dateTime = $this->dataItem->asDateTime();
+
+ if ( !$dateTime ) {
+ return false;
+ }
+
+ $output = $this->formatWithLocalizedTextReplacement(
+ $dateTime,
+ $format
+ );
+
+ return $output;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $format
+ *
+ * @return boolean
+ */
+ public function containsValidDateFormatRule( $format ) {
+
+ foreach ( str_split( $format ) as $value ) {
+ if ( in_array( $value, [ 'd', 'D', 'j', 'l', 'N', 'w', 'W', 'F', 'M', 'm', 'n', 't', 'L', 'o', 'Y', 'y', "c", 'r' ] ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * DateTime generally outputs English textual representation
+ *
+ * - D A textual representation of a day, three letters
+ * - l (lowercase 'L'), A full textual representation of the day of the week
+ * - F A full textual representation of a month, such as January or March
+ * - M A short textual representation of a month, three letters
+ * - a Lowercase Ante meridiem and Post meridiem am or pm
+ * - A Uppercase Ante meridiem and Post meridiem
+ */
+ private function formatWithLocalizedTextReplacement( $dateTime, $format ) {
+
+ $output = $dateTime->format( $format );
+
+ // (n) DateTime => 1 through 12
+ $monthNumber = $dateTime->format( 'n' );
+
+ // (N) DateTime => 1 (for Monday) through 7 (for Sunday)
+ // (w) DateTime => 0 (for Sunday) through 6 (for Saturday)
+ // MW => 1 (for Sunday) through 7 (for Saturday)
+ $dayNumber = $dateTime->format( 'w' ) + 1;
+
+ if ( strpos( $format, 'F' ) !== false ) {
+ $output = str_replace(
+ $dateTime->format( 'F' ),
+ $this->language->getMonthName( $monthNumber ),
+ $output
+ );
+ }
+
+ if ( strpos( $format, 'M' ) !== false ) {
+ $output = str_replace(
+ $dateTime->format( 'M' ),
+ $this->language->getMonthAbbreviation( $monthNumber ),
+ $output
+ );
+ }
+
+ if ( strpos( $format, 'l' ) !== false ) {
+ $output = str_replace(
+ $dateTime->format( 'l' ),
+ $this->language->getWeekdayName( $dayNumber ),
+ $output
+ );
+ }
+
+ if ( strpos( $format, 'D' ) !== false ) {
+ $output = str_replace(
+ $dateTime->format( 'D' ),
+ $this->language->getWeekdayAbbreviation( $dayNumber ),
+ $output
+ );
+ }
+
+ return $output;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/JulianDay.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/JulianDay.php
new file mode 100644
index 00000000..150b206d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/JulianDay.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW\DataValues\Time;
+
+use RuntimeException;
+
+/**
+ * Julian dates (abbreviated JD) are a continuous count of days and fractions
+ * since noon Universal Time on January 1, 4713 BCE (on the Julian calendar).
+ *
+ * It is assumed that the changeover from the Julian calendar to the Gregorian
+ * calendar occurred in October of 1582.
+ *
+ * For dates on or before 4 October 1582, the Julian calendar is used; for dates
+ * on or after 15 October 1582, the Gregorian calendar is used.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author Markus Krötzsch
+ */
+class JulianDay implements CalendarModel {
+
+ /**
+ * Moment of switchover to Gregorian calendar.
+ */
+ const J1582 = 2299160.5;
+
+ /**
+ * Offset of Julian Days for Modified JD inputs.
+ */
+ const MJD = 2400000.5;
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $calendarmodel
+ * @param integer $year
+ * @param integer $month
+ * @param integer $day
+ * @param integer $hour
+ * @param integer $minute
+ * @param integer $second
+ *
+ * @return float
+ */
+ public static function getJD( $calendarModel = self::CM_GREGORIAN, $year, $month, $day, $hour, $minute, $second ) {
+ return self::format( self::date2JD( $calendarModel, $year, $month, $day ) + self::time2JDoffset( $hour, $minute, $second ) );
+ }
+
+ /**
+ * Return a formatted value
+ *
+ * @note April 25, 2017 20:00-4:00 is expected to be 2457869.5 and not
+ * 2457869.4999999665 hence apply the same formatting on all values to avoid
+ * some unexpected behaviour as observed in #2454
+ *
+ * @since 3.0
+ *
+ * @param $value
+ *
+ * @return float
+ */
+ public static function format( $value ) {
+ // Keep microseconds to a certain degree distinguishable
+ return floatval( number_format( $value, 7, '.', '' ) );
+ }
+
+ /**
+ * The MJD has a starting point of midnight on November 17, 1858 and is
+ * computed by MJD = JD - 2400000.5
+ *
+ * @since 2.4
+ *
+ * @param float jdValue
+ *
+ * @return float
+ */
+ public static function getModifiedJulianDate( $jdValue ) {
+ return $jdValue - self::MJD;
+ }
+
+ /**
+ * Compute the Julian Day number from a given date in the specified
+ * calendar model. This calculation assumes that neither calendar
+ * has a year 0.
+ *
+ * @param $year integer representing the year
+ * @param $month integer representing the month
+ * @param $day integer representing the day
+ * @param $calendarmodel integer either CM_GREGORIAN or CM_JULIAN
+ *
+ * @return float Julian Day number
+ * @throws RuntimeException
+ */
+ protected static function date2JD( $calendarmodel, $year, $month, $day ) {
+ $astroyear = ( $year < 1 ) ? ( $year + 1 ) : $year;
+
+ if ( $calendarmodel === self::CM_GREGORIAN ) {
+ $a = intval( ( 14 - $month ) / 12 );
+ $y = $astroyear + 4800 - $a;
+ $m = $month + 12 * $a - 3;
+ return $day + floor( ( 153 * $m + 2 ) / 5 ) + 365 * $y + floor( $y / 4 ) - floor( $y / 100 ) + floor( $y / 400 ) - 32045.5;
+ } elseif ( $calendarmodel === self::CM_JULIAN ) {
+ $y2 = ( $month <= 2 ) ? ( $astroyear - 1 ) : $astroyear;
+ $m2 = ( $month <= 2 ) ? ( $month + 12 ) : $month;
+ return floor( ( 365.25 * ( $y2 + 4716 ) ) ) + floor( ( 30.6001 * ( $m2 + 1 ) ) ) + $day - 1524.5;
+ }
+
+ throw new RuntimeException( "Unsupported calendar model ($calendarmodel)" );
+ }
+
+ /**
+ * Compute the offset for the Julian Day number from a given time.
+ * This computation is the same for all calendar models.
+ *
+ * @param $hours integer representing the hour
+ * @param $minutes integer representing the minutes
+ * @param $seconds integer representing the seconds
+ *
+ * @return float offset for a Julian Day number to get this time
+ */
+ protected static function time2JDoffset( $hours, $minutes, $seconds ) {
+ return ( $hours / 24 ) + ( $minutes / ( 60 * 24 ) ) + ( $seconds / ( 3600 * 24 ) );
+ }
+
+ /**
+ * Convert a Julian Day number to a date in the given calendar model.
+ * This calculation assumes that neither calendar has a year 0.
+ * @note The algorithm may fail for some cases, in particular since the
+ * conversion to Gregorian needs positive JD. If this happens, wrong
+ * values will be returned. Avoid date conversions before 10000 BCE.
+ *
+ * @param $jdValue float number of Julian Days
+ * @param $calendarModel integer either CM_GREGORIAN or CM_JULIAN
+ *
+ * @return array( calendarModel, yearnumber, monthnumber, daynumber )
+ * @throws RuntimeException
+ */
+ public static function JD2Date( $jdValue, $calendarModel = null ) {
+
+ if ( $calendarModel === null ) { // 1582/10/15
+ $calendarModel = $jdValue < self::J1582 ? self::CM_JULIAN : self::CM_GREGORIAN;
+ }
+
+ if ( $calendarModel === self::CM_GREGORIAN ) {
+ $jdValue += 2921940; // add the days of 8000 years (this algorithm only works for positive JD)
+ $j = floor( $jdValue + 0.5 ) + 32044;
+ $g = floor( $j / 146097 );
+ $dg = $j % 146097;
+ $c = floor( ( ( floor( $dg / 36524 ) + 1 ) * 3 ) / 4 );
+ $dc = $dg - $c * 36524;
+ $b = floor( $dc / 1461 );
+ $db = $dc % 1461;
+ $a = floor( ( ( floor( $db / 365 ) + 1 ) * 3 ) / 4 );
+ $da = $db - ( $a * 365 );
+ $y = $g * 400 + $c * 100 + $b * 4 + $a;
+ $m = floor( ( $da * 5 + 308 ) / 153 ) - 2;
+ $d = $da - floor( ( ( $m + 4 ) * 153 ) / 5 ) + 122;
+
+ $year = $y - 4800 + floor( ( $m + 2 ) / 12 ) - 8000;
+ $month = ( ( $m + 2 ) % 12 + 1 );
+ $day = $d + 1;
+ } elseif ( $calendarModel === self::CM_JULIAN ) {
+ $b = floor( $jdValue + 0.5 ) + 1524;
+ $c = floor( ( $b - 122.1 ) / 365.25 );
+ $d = floor( 365.25 * $c );
+ $e = floor( ( $b - $d ) / 30.6001 );
+
+ $month = floor( ( $e < 14 ) ? ( $e - 1 ) : ( $e - 13 ) );
+ $year = floor( ( $month > 2 ) ? ( $c - 4716 ) : ( $c - 4715 ) );
+ $day = ( $b - $d - floor( 30.6001 * $e ) );
+ } else {
+ throw new RuntimeException( "Unsupported calendar model ($calendarModel)" );
+ }
+
+ $year = ( $year < 1 ) ? ( $year - 1 ) : $year; // correct "year 0" to -1 (= 1 BC(E))
+
+ return [ $calendarModel, $year, $month, $day ];
+ }
+
+ /**
+ * Extract the time from a Julian Day number and return it as a string.
+ * This conversion is the same for all calendar models.
+ *
+ * @param $jdvalue float number of Julian Days
+ * @return array( hours, minutes, seconds )
+ */
+ public static function JD2Time( $jdvalue ) {
+ $wjd = $jdvalue + 0.5;
+ $fraction = $wjd - floor( $wjd );
+ $time = round( $fraction * 3600 * 24 );
+ $hours = floor( $time / 3600 );
+ $time = $time - $hours * 3600;
+ $minutes = floor( $time / 60 );
+ $seconds = floor( $time - $minutes * 60 );
+ return [ $hours, $minutes, $seconds ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Timezone.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Timezone.php
new file mode 100644
index 00000000..a9e4dcd4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/Time/Timezone.php
@@ -0,0 +1,388 @@
+<?php
+
+namespace SMW\DataValues\Time;
+
+use DateInterval;
+use DateTime;
+use DateTimeZone;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Timezone {
+
+ /**
+ * A new TZ is expected to be added at the end of the list without changing
+ * existing ID's (those are used as internal serialization identifier).
+ *
+ * The associated offsets are in hours or fractions of hours.
+ *
+ * 'FOO' => array( ID, OffsetInSeconds, isMilitary )
+ *
+ * @var array
+ */
+ private static $shortList = [
+ "UTC" => [ 0, 0, false ],
+ "Z" => [ 1, 0, true ],
+ "A" => [ 2, 3600, true ],
+ "ACDT" => [ 3, 37800, false ],
+ "ACST" => [ 4, 34200, false ],
+ "ADT" => [ 5, -10800, false ],
+ "AEDT" => [ 6, 39600, false ],
+ "AEST" => [ 7, 36000, false ],
+ "AKDT" => [ 8, -28800, false ],
+ "AKST" => [ 9, -32400, false ],
+ "AST" => [ 10, -14400, false ],
+ "AWDT" => [ 11, 32400, false ],
+ "AWST" => [ 12, 28800, false ],
+ "B" => [ 13, 7200, true ],
+ "BST" => [ 14, 3600, false ],
+ "C" => [ 15, 10800, true ],
+ "CDT" => [ 16, -18000, false ],
+ "CEDT" => [ 17, 7200, false ],
+ "CEST" => [ 18, 7200, false ],
+ "CET" => [ 19, 3600, false ],
+ "CST" => [ 20, -21600, false ],
+ "CXT" => [ 21, 25200, false ],
+ "D" => [ 22, 14400, true ],
+ "E" => [ 23, 18000, true ],
+ "EDT" => [ 24, -14400, false ],
+ "EEDT" => [ 25, 10800, false ],
+ "EEST" => [ 26, 10800, false ],
+ "EET" => [ 27, 7200, false ],
+ "EST" => [ 28, -18000, false ],
+ "F" => [ 29, 21600, true ],
+ "G" => [ 30, 25200, true ],
+ "GMT" => [ 31, 0, false ],
+ "H" => [ 32, 28800, true ],
+ "HAA" => [ 33, -10800, false ],
+ "HAC" => [ 34, -18000, false ],
+ "HADT" => [ 35, -32400, false ],
+ "HAE" => [ 36, -14400, false ],
+ "HAP" => [ 37, -25200, false ],
+ "HAR" => [ 38, -21600, false ],
+ "HAST" => [ 39, -36000, false ],
+ "HAT" => [ 40, -9000, false ],
+ "HAY" => [ 41, -28800, false ],
+ "HNA" => [ 42, -14400, false ],
+ "HNC" => [ 43, -21600, false ],
+ "HNE" => [ 44, -18000, false ],
+ "HNP" => [ 45, -28800, false ],
+ "HNR" => [ 46, -25200, false ],
+ "HNT" => [ 47, -12600, false ],
+ "HNY" => [ 48, -32400, false ],
+ "I" => [ 49, 32400, true ],
+ "IST" => [ 50, 3600, false ],
+ "K" => [ 51, 36000, true ],
+ "L" => [ 52, 39600, true ],
+ "M" => [ 53, 43200, true ],
+ "MDT" => [ 54, -21600, false ],
+ "MESZ" => [ 55, 7200, false ],
+ "MEZ" => [ 56, 3600, false ],
+ "MSD" => [ 57, 14400, false ],
+ "MSK" => [ 58, 10800, false ],
+ "MST" => [ 59, -25200, false ],
+ "N" => [ 60, -3600, true ],
+ "NDT" => [ 61, -9000, false ],
+ "NFT" => [ 62, 41400, false ],
+ "NST" => [ 63, -12600, false ],
+ "O" => [ 64, -7200, true ],
+ "P" => [ 65, -10800, true ],
+ "PDT" => [ 66, -25200, false ],
+ "PST" => [ 67, -28800, false ],
+ "Q" => [ 68, -14400, true ],
+ "R" => [ 69, -18000, true ],
+ "S" => [ 70, -21600, true ],
+ "T" => [ 71, -25200, true ],
+ "U" => [ 72, -28800, true ],
+ "V" => [ 73, -32400, true ],
+ "W" => [ 74, -36000, true ],
+ "WDT" => [ 75, 32400, false ],
+ "WEDT" => [ 76, 3600, false ],
+ "WEST" => [ 77, 3600, false ],
+ "WET" => [ 78, 0, false ],
+ "WST" => [ 79, 28800, false ],
+ "X" => [ 80, -39600, true ],
+ "Y" => [ 81, -43200, true ],
+ ];
+
+ /**
+ * Generated from the DateTimeZone::listAbbreviations and contains "Area/Location",
+ * e.g. "America/New_York".
+ *
+ * Citing https://en.wikipedia.org/wiki/Tz_database which describes that " ...
+ * The underscore character is used in place of spaces. Hyphens are used
+ * where they appear in the name of a location ... names have a maximum
+ * length of 14 characters ..."
+ *
+ * @var array
+ */
+ private static $dateTimeZoneList = [];
+
+ /**
+ * @var array
+ */
+ private static $offsetCache = [];
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public static function listShortAbbreviations() {
+ return array_keys( self::$shortList );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $identifer
+ *
+ * @return boolean
+ */
+ public static function isValid( $identifer ) {
+
+ $identifer = str_replace( ' ', '_', $identifer );
+
+ if ( isset( self::$shortList[strtoupper( $identifer )] ) ) {
+ return true;
+ }
+
+ $dateTimeZoneList = self::getDateTimeZoneList();
+
+ if ( isset( $dateTimeZoneList[$identifer] ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $abbreviation
+ *
+ * @return boolean
+ */
+ public static function isMilitary( $abbreviation ) {
+
+ $abbreviation = strtoupper( $abbreviation );
+
+ if ( isset( self::$shortList[$abbreviation] ) ) {
+ return self::$shortList[$abbreviation][2];
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $identifer
+ *
+ * @return false|integer
+ */
+ public static function getIdByAbbreviation( $identifer ) {
+
+ if ( isset( self::$shortList[strtoupper( $identifer )] ) ) {
+ return self::$shortList[strtoupper( $identifer )][0];
+ }
+
+ $identifer = str_replace( ' ', '_', $identifer );
+ $dateTimeZoneList = self::getDateTimeZoneList();
+
+ if ( isset( $dateTimeZoneList[$identifer] ) ) {
+ return $dateTimeZoneList[$identifer];
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $identifer
+ *
+ * @return false|string
+ */
+ public static function getTimezoneLiteralById( $identifer ) {
+
+ foreach ( self::$shortList as $abbreviation => $value ) {
+ if ( is_numeric( $identifer ) && $value[0] == $identifer ) {
+ return $abbreviation;
+ }
+ }
+
+ $dateTimeZoneList = self::getDateTimeZoneList();
+
+ if ( ( $abbreviation = array_search( $identifer, $dateTimeZoneList ) ) !== false ) {
+ return $abbreviation;
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $abbreviation
+ *
+ * @return false|string
+ */
+ public static function getOffsetByAbbreviation( $abbreviation ) {
+
+ if ( isset( self::$shortList[strtoupper( $abbreviation )] ) ) {
+ return self::$shortList[strtoupper( $abbreviation )][1];
+ }
+
+ $abbreviation = str_replace( ' ', '_', $abbreviation );
+
+ if ( isset( self::$offsetCache[$abbreviation] ) ) {
+ return self::$offsetCache[$abbreviation];
+ }
+
+ $offset = false;
+
+ try {
+ $dateTimeZone = new DateTimeZone( $abbreviation );
+ $offset = $dateTimeZone->getOffset( new DateTime() );
+ } catch( \Exception $e ) {
+ //
+ }
+
+ return self::$offsetCache[$abbreviation] = $offset;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $abbreviation
+ *
+ * @return string
+ */
+ public static function getNameByAbbreviation( $abbreviation ) {
+
+ $abbreviation = strtoupper( $abbreviation );
+
+ if ( isset( self::$shortList[$abbreviation] ) ) {
+ $name = timezone_name_from_abbr( $abbreviation );
+ }
+
+ // If the abbrevation couldn't be matched use the offset instead
+ if ( !$name ) {
+ $name = timezone_name_from_abbr(
+ "",
+ self::getOffsetByAbbreviation( $abbreviation ) * 3600,
+ 0
+ );
+ }
+
+ return $name;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $abbreviation
+ *
+ * @return DateInterval
+ */
+ public static function newDateIntervalWithOffsetFrom( $abbreviation ) {
+
+ $minutes = 0;
+ $hour = 0;
+
+ // Here we don't care for +/-, the caller of the function
+ // has to care for it
+ $offsetInSeconds = abs( self::getOffsetByAbbreviation( $abbreviation ) );
+
+ return new DateInterval( "PT{$offsetInSeconds}S" );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $abbreviation
+ *
+ * @return false|DateTimeZone
+ */
+ public static function newDateTimeZone( $abbreviation ) {
+
+ try {
+ $dateTimeZone = new DateTimeZone( $abbreviation );
+ } catch( \Exception $e ) {
+ if ( ( $name = self::getNameByAbbreviation( $abbreviation ) ) !== false ) {
+ return new DateTimeZone( $name );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Generated from the DateTimeZone::listAbbreviations
+ *
+ * @since 2.5
+ *
+ * @return array
+ */
+ public static function getDateTimeZoneList() {
+
+ if ( self::$dateTimeZoneList !== [] ) {
+ return self::$dateTimeZoneList;
+ }
+
+ $list = DateTimeZone::listIdentifiers();
+
+ foreach ( $list as $identifier ) {
+ self::$dateTimeZoneList[$identifier] = $identifier;
+ }
+
+ return self::$dateTimeZoneList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DateTime $dateTime
+ * @param string|integer &$tz
+ *
+ * @return DateTime
+ */
+ public static function getModifiedTime( DateTime $dateTime, &$tz = 0 ) {
+
+ if ( ( $timezoneLiteral = self::getTimezoneLiteralById( $tz ) ) === false ) {
+ $tz = $timezoneLiteral;
+ return $dateTime;
+ }
+
+ $dateTimeZone = null;
+
+ if ( !self::isMilitary( $timezoneLiteral ) && self::getOffsetByAbbreviation( $timezoneLiteral ) != 0 ) {
+ $dateTimeZone = self::newDateTimeZone( $timezoneLiteral );
+ }
+
+ // DI is stored in UTC time therefore find and add the offset
+ if ( !$dateTimeZone instanceof DateTimeZone ) {
+ $dateInterval = self::newDateIntervalWithOffsetFrom( $timezoneLiteral );
+
+ if ( self::getOffsetByAbbreviation( $timezoneLiteral ) > 0 ) {
+ $dateTime->add( $dateInterval );
+ } else {
+ $dateTime->sub( $dateInterval );
+ }
+ } else {
+ $dateTime->setTimezone( $dateTimeZone );
+ }
+
+ $tz = $timezoneLiteral;
+
+ return $dateTime;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TypesValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TypesValue.php
new file mode 100644
index 00000000..97763490
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/TypesValue.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\DataValues;
+
+use SMW\DataTypeRegistry;
+use SMW\Localizer;
+use SMWDataItem as DataItem;
+use SMWDataItemException as DataItemException;
+use SMWDataValue as DataValue;
+use SMWDIUri as DIUri;
+use SpecialPageFactory;
+use Title;
+
+/**
+ * This datavalue implements special processing suitable for defining types of
+ * properties. Types behave largely like values of type SMWWikiPageValue
+ * with three main differences. First, they actively check if a value is an
+ * alias for another type, modifying the internal representation accordingly.
+ * Second, they have a modified display for emphasizing if some type is defined
+ * in SMW (built-in). Third, they use type ids for storing data (DB keys)
+ * instead of using page titles.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class TypesValue extends DataValue {
+
+ /**
+ * DV identifier
+ */
+ const TYPE_ID = '__typ';
+
+ /**
+ * @var string
+ */
+ private $typeLabel;
+
+ /**
+ * @var string
+ */
+ private $givenLabel;
+
+ /**
+ * @var string
+ */
+ private $m_typeId;
+
+ /**
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( self::TYPE_ID );
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @param string $typeId
+ *
+ * @return TypesValue
+ */
+ public static function newFromTypeId( $typeId ) {
+ $result = new TypesValue( self::TYPE_ID );
+
+ try {
+ $dataItem = self::getTypeUriFromTypeId( $typeId );
+ } catch ( DataItemException $e ) {
+ $dataItem = self::getTypeUriFromTypeId( 'notype' );
+ }
+
+ $result->setDataItem( $dataItem );
+
+ return $result;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @param string $typeId
+ *
+ * @return DIUri
+ */
+ public static function getTypeUriFromTypeId( $typeId ) {
+ return new DIUri( 'http', 'semantic-mediawiki.org/swivt/1.0', '', $typeId );
+ }
+
+ /**
+ * @see DataValue::getShortWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortWikiText( $linker = null ) {
+
+ if ( !$linker || $this->m_outformat === '-' || $this->m_caption === '' ) {
+ return $this->m_caption;
+ }
+
+ $titleText = $this->makeSpecialPageTitleText();
+
+ $contentLanguage = Localizer::getInstance()->getLanguage(
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ $namespace = $contentLanguage->getNsText(
+ NS_SPECIAL
+ );
+
+ return "[[$namespace:$titleText|{$this->m_caption}]]";
+ }
+
+ /**
+ * @see DataValue::getShortHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getShortHTMLText( $linker = null ) {
+
+ if ( !$linker || $this->m_outformat === '-' || $this->m_caption === '' ) {
+ return htmlspecialchars( $this->m_caption );
+ }
+
+ $title = Title::makeTitle(
+ NS_SPECIAL,
+ $this->makeSpecialPageTitleText()
+ );
+
+ return $linker->link( $title, htmlspecialchars( $this->m_caption ) );
+ }
+
+ /**
+ * @see DataValue::getLongWikiText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongWikiText( $linker = null ) {
+
+ if ( !$linker || $this->typeLabel === '' ) {
+ return $this->typeLabel;
+ }
+
+ $titleText = $this->makeSpecialPageTitleText();
+
+ $contentLanguage = Localizer::getInstance()->getLanguage(
+ $this->getOption( self::OPT_CONTENT_LANGUAGE )
+ );
+
+ $namespace = $contentLanguage->getNsText(
+ NS_SPECIAL
+ );
+
+ return "[[$namespace:$titleText|{$this->typeLabel}]]";
+ }
+
+ /**
+ * @see DataValue::getLongHTMLText
+ *
+ * {@inheritDoc}
+ */
+ public function getLongHTMLText( $linker = null ) {
+
+ if ( !$linker || $this->typeLabel === '' ) {
+ return htmlspecialchars( $this->typeLabel );
+ }
+
+ $title = Title::makeTitle(
+ NS_SPECIAL,
+ $this->makeSpecialPageTitleText()
+ );
+
+ return $linker->link( $title, htmlspecialchars( $this->typeLabel ) );
+ }
+
+ /**
+ * @see DataValue::getWikiValue
+ *
+ * {@inheritDoc}
+ */
+ public function getWikiValue() {
+ return $this->typeLabel;
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * {@inheritDoc}
+ */
+ protected function parseUserValue( $value ) {
+
+ if ( $this->m_caption === false ) {
+ $this->m_caption = $value;
+ }
+
+ $valueParts = explode( ':', $value, 2 );
+ $contentLanguage = $this->getOption( self::OPT_CONTENT_LANGUAGE );
+
+ if ( $value !== '' && $value{0} === '_' ) {
+ $this->m_typeId = $value;
+ } else {
+ $this->givenLabel = smwfNormalTitleText( $value );
+ $this->m_typeId = DataTypeRegistry::getInstance()->findTypeByLabelAndLanguage( $this->givenLabel, $contentLanguage );
+ }
+
+ if ( $this->m_typeId === '' ) {
+ $this->addErrorMsg( [ 'smw_unknowntype', $this->givenLabel ] );
+ $this->typeLabel = $this->givenLabel;
+ } else {
+ $this->typeLabel = DataTypeRegistry::getInstance()->findTypeLabel( $this->m_typeId );
+ }
+
+ try {
+ $this->m_dataitem = self::getTypeUriFromTypeId( $this->m_typeId );
+ } catch ( DataItemException $e ) {
+ $this->m_dataitem = self::getTypeUriFromTypeId( 'notype' );
+ $this->addErrorMsg( [ 'smw-datavalue-type-invalid-typeuri', $this->m_typeId ] );
+ }
+ }
+
+ /**
+ * @see DataValue::loadDataItem
+ *
+ * {@inheritDoc}
+ */
+ protected function loadDataItem( DataItem $dataItem ) {
+
+ if ( ( $dataItem instanceof DIUri ) && ( $dataItem->getScheme() == 'http' ) &&
+ ( $dataItem->getHierpart() == 'semantic-mediawiki.org/swivt/1.0' ) &&
+ ( $dataItem->getQuery() === '' ) ) {
+
+ $this->m_typeId = $dataItem->getFragment();
+ $this->typeLabel = DataTypeRegistry::getInstance()->findTypeLabel( $this->m_typeId );
+ $this->m_caption = $this->givenLabel = $this->typeLabel;
+ $this->m_dataitem = $dataItem;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function makeSpecialPageTitleText() {
+ return SpecialPageFactory::getLocalNameFor( 'Types', $this->typeLabel );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/UniquenessConstraintValue.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/UniquenessConstraintValue.php
new file mode 100644
index 00000000..5a6545e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/UniquenessConstraintValue.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace SMW\DataValues;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintValue extends BooleanValue {
+
+ /**
+ * @since 2.4
+ *
+ * @param string $typeid
+ */
+ public function __construct( $typeid = '' ) {
+ parent::__construct( '__pvuc' );
+ }
+
+ /**
+ * @see DataValue::parseUserValue
+ *
+ * @param string $value
+ */
+ protected function parseUserValue( $userValue ) {
+
+ if ( !$this->isEnabledFeature( SMW_DV_PVUC ) ) {
+ $this->addErrorMsg(
+ [
+ 'smw-datavalue-feature-not-supported',
+ 'SMW_DV_PVUC'
+ ]
+ );
+ }
+
+ parent::parseUserValue( $userValue );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/CodeStringValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/CodeStringValueFormatter.php
new file mode 100644
index 00000000..d6f3a069
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/CodeStringValueFormatter.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use SMWDataValue as DataValue;
+use SMWOutputs as Outputs;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CodeStringValueFormatter extends StringValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue->getTypeID() === '_cod';
+ }
+
+ /**
+ * @see StringValueFormatter::doFormat
+ */
+ protected function doFormat( $dataValue, $type, $linker ) {
+
+ $abbreviate = $type === self::WIKI_LONG || $type === self::HTML_LONG;
+ $text = $dataValue->getDataItem()->getString();
+
+ // Escape and wrap values of type Code. The result is escaped to be
+ // HTML-safe (it will also work in wiki context). The result will
+ // contain mark-up that must not be escaped again.
+
+ Outputs::requireResource( 'ext.smw.style' );
+
+ if ( $this->isJson( $text ) ) {
+ $result = self::asJson( $text );
+ } else {
+ // This disables all active wiki and HTML markup:
+ $result = str_replace(
+ [ '<code>', '</code>', '<nowiki>', '</nowiki>', '<', '>', ' ', '[', '{', '=', "'", ':', "\n", '&#x005B;' ],
+ [ '', '', '', '', '&lt;', '&gt;', '&#160;', '&#91;', '&#x007B;', '&#x003D;', '&#x0027;', '&#58;', "<br />", '&#91;' ],
+ $text
+ );
+ }
+
+ if ( $abbreviate ) {
+ $result = "<div style=\"min-height:5em; overflow:auto;\">$result</div>";
+ }
+
+ return "<div class=\"smwpre\">$result</div>";
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function asJson( $string, $flag = 0 ) {
+
+ if ( $flag > 0 ) {
+ return json_encode( json_decode( $string ), $flag );
+ }
+
+ return json_encode( json_decode( $string ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ private function isJson( $string ) {
+
+ // Don't bother
+ if ( substr( $string, 0, 1 ) !== '{' ) {
+ return false;
+ }
+
+ json_decode( $string );
+
+ return ( json_last_error() == JSON_ERROR_NONE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DataValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DataValueFormatter.php
new file mode 100644
index 00000000..8ffdf555
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DataValueFormatter.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use SMW\Options;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+abstract class DataValueFormatter implements ValueFormatter {
+
+ /**
+ * Return the plain wiki version of the value, or FALSE if no such version
+ * is available. The returned string suffices to reobtain the same DataValue
+ * when passing it as an input string to DataValue::setUserValue.
+ */
+ const VALUE = 0;
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in wiki text.
+ */
+ const WIKI_SHORT = 1;
+
+ /**
+ * Returns a short textual representation for this data value. If the value
+ * was initialised from a user supplied string, then this original string
+ * should be reflected in this short version (i.e. no normalisation should
+ * normally happen). There might, however, be additional parts such as code
+ * for generating tooltips. The output is in HTML text.
+ */
+ const HTML_SHORT = 2;
+
+ /**
+ * Return the long textual description of the value, as printed for example
+ * in the factbox. If errors occurred, return the error message. The result
+ * is always a wiki-source string.
+ */
+ const WIKI_LONG = 3;
+
+ /**
+ * Return the long textual description of the value, as printed for
+ * example in the factbox. If errors occurred, return the error message
+ * The result always is an HTML string.
+ */
+ const HTML_LONG = 4;
+
+ /**
+ * @var DataValue
+ */
+ protected $dataValue;
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue|null $dataValue
+ */
+ public function __construct( DataValue $dataValue = null ) {
+ $this->dataValue = $dataValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ *
+ * @return boolean
+ */
+ abstract public function isFormatterFor( DataValue $dataValue );
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ */
+ public function setDataValue( DataValue $dataValue ) {
+ $this->dataValue = $dataValue;
+ $this->options = null;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->dataValue->getErrors();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return mixed|false
+ */
+ public function getOption( $key ) {
+
+ if ( $this->options !== null && $this->options->has( $key ) ) {
+ return $this->options->get( $key );
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DispatchingDataValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DispatchingDataValueFormatter.php
new file mode 100644
index 00000000..c8056a98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/DispatchingDataValueFormatter.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMWDataValue as DataValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DispatchingDataValueFormatter {
+
+ /**
+ * @var DataValueFormatter[]
+ */
+ private $dataValueFormatters = [];
+
+ /**
+ * @var DataValueFormatter[]
+ */
+ private $defaultDataValueFormatters = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValueFormatter $dataValueFormatter
+ */
+ public function addDataValueFormatter( DataValueFormatter $dataValueFormatter ) {
+ $this->dataValueFormatters[] = $dataValueFormatter;
+ }
+
+ /**
+ * DataValueFormatters registered with this method are validated after
+ * DispatchingDataValueFormatter::getDataValueFormatterFor was not able to
+ * match any Formatter. This to ensure that a distinct FooStringValueFormatter
+ * is tried before the default StringValueFormatter.
+ *
+ * @since 2.4
+ *
+ * @param DataValueFormatter $dataValueFormatter
+ */
+ public function addDefaultDataValueFormatter( DataValueFormatter $dataValueFormatter ) {
+ $this->defaultDataValueFormatters[] = $dataValueFormatter;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ *
+ * @return DataValueFormatter
+ * @throws RuntimeException
+ */
+ public function getDataValueFormatterFor( DataValue $dataValue ) {
+
+ foreach ( $this->dataValueFormatters as $dataValueFormatter ) {
+ if ( $dataValueFormatter->isFormatterFor( $dataValue ) ) {
+ $dataValueFormatter->setDataValue( $dataValue );
+ return $dataValueFormatter;
+ }
+ }
+
+ foreach ( $this->defaultDataValueFormatters as $dataValueFormatter ) {
+ if ( $dataValueFormatter->isFormatterFor( $dataValue ) ) {
+ $dataValueFormatter->setDataValue( $dataValue );
+ return $dataValueFormatter;
+ }
+ }
+
+ throw new RuntimeException( "The dispatcher could not match a DataValueFormatter for " . get_class( $dataValue ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/MonolingualTextValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/MonolingualTextValueFormatter.php
new file mode 100644
index 00000000..71702889
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/MonolingualTextValueFormatter.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DIProperty;
+use SMW\Message;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof MonolingualTextValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function format( $type, $linker = null ) {
+
+ if ( !$this->dataValue instanceof MonolingualTextValue ) {
+ throw new RuntimeException( "The formatter is missing a valid MonolingualTextValue object" );
+ }
+
+ if (
+ $this->dataValue->getCaption() !== false &&
+ ( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) ) {
+ return $this->dataValue->getCaption();
+ }
+
+ return $this->getOutputText( $type, $linker );
+ }
+
+ protected function getOutputText( $type, $linker = null ) {
+
+ if ( !$this->dataValue->isValid() ) {
+ return ( ( $type == self::WIKI_SHORT ) || ( $type == self::HTML_SHORT ) ) ? '' : $this->dataValue->getErrorText();
+ }
+
+ // For the inverse case, return the subject that contains the reference
+ // for Foo annotated with [[Bar::abc@en]] -> [[-Bar::Foo]]
+ if ( $this->dataValue->getProperty() !== null && $this->dataValue->getProperty()->isInverse() ) {
+
+ $dataItems = $this->dataValue->getDataItem()->getSemanticData()->getPropertyValues(
+ new DIProperty( $this->dataValue->getProperty()->getKey() )
+ );
+
+ $dataItem = reset( $dataItems );
+
+ if ( !$dataItem ) {
+ return '';
+ }
+
+ return $dataItem->getDBKey();
+ }
+
+ return $this->doFormatFinalOutputFor( $type, $linker );
+ }
+
+ private function doFormatFinalOutputFor( $type, $linker ) {
+
+ $text = '';
+ $languagecode = '';
+
+ foreach ( $this->dataValue->getPropertyDataItems() as $property ) {
+
+ // If we wanted to omit the language code display for some outputs then
+ // this is the point to make it happen
+ if ( ( $type == self::HTML_LONG || $type == self::WIKI_SHORT ) && $property->getKey() === '_LCODE' ) {
+ //continue;
+ }
+
+ $dataItems = $this->dataValue->getDataItem()->getSemanticData()->getPropertyValues(
+ $property
+ );
+
+ // Should not happen but just in case
+ if ( !$dataItems === [] ) {
+ $this->dataValue->addErrorMsg( 'smw-datavalue-monolingual-dataitem-missing' );
+ continue;
+ }
+
+ $dataItem = reset( $dataItems );
+
+ if ( $dataItem === false ) {
+ continue;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $result = $this->findValueOutputFor(
+ $type,
+ $dataValue,
+ $linker
+ );
+
+ if ( $property->getKey() === '_LCODE' && $type !== self::VALUE ) {
+ $languagecode = ' ' . Message::get( [ 'smw-datavalue-monolingual-lcode-parenthesis', $result ] );
+ } elseif ( $property->getKey() === '_LCODE' && $type === self::VALUE ) {
+ $languagecode = '@' . $result;
+ } else {
+ $text = $result;
+ }
+ }
+
+ return $text . $languagecode;
+ }
+
+ private function findValueOutputFor( $type, $dataValue, $linker ) {
+ switch ( $type ) {
+ case self::VALUE:
+ return $dataValue->getWikiValue();
+ case self::WIKI_SHORT:
+ return $dataValue->getShortWikiText( $linker );
+ case self::HTML_SHORT:
+ return $dataValue->getShortHTMLText( $linker );
+ case self::WIKI_LONG:
+ return $dataValue->getShortWikiText( $linker );
+ case self::HTML_LONG:
+ return $dataValue->getShortHTMLText( $linker );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NoValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NoValueFormatter.php
new file mode 100644
index 00000000..f154a4c0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NoValueFormatter.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NoValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof DataValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function format( $type, $linker = null ) {
+
+ if ( !$this->dataValue instanceof DataValue ) {
+ throw new RuntimeException( "The formatter is missing a valid DataValue object" );
+ }
+
+ return $this->dataValue->getDataItem()->getSerialization();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NumberValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NumberValueFormatter.php
new file mode 100644
index 00000000..a9a50f2b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/NumberValueFormatter.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\Highlighter;
+use SMWDataValue as DataValue;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NumberValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof NumberValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function format( $type, $linker = null ) {
+
+ if ( !$this->dataValue instanceof NumberValue ) {
+ throw new RuntimeException( "The formatter is missing a valid NumberValue object" );
+ }
+
+ if ( $type === self::VALUE ) {
+ return $this->valueFormat();
+ }
+
+ if ( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) {
+ return $this->shortFormat( $linker );
+ }
+
+ if ( $type === self::WIKI_LONG || $type === self::HTML_LONG ) {
+ return $this->longFormat( $linker );
+ }
+
+ return 'UNKNOWN';
+ }
+
+ private function valueFormat() {
+
+ if ( !$this->dataValue->isValid() ) {
+ return 'error';
+ }
+
+ $unit = $this->dataValue->getUnit();
+
+ $number = $this->dataValue->getNormalizedFormattedNumber(
+ $this->dataValue->getNumber()
+ );
+
+ if ( $unit === '' ) {
+ return $number;
+ }
+
+ return $this->dataValue->hasPrefixalUnitPreference( $unit ) ? $unit . ' ' . $number : $number . ' ' . $unit;
+ }
+
+ private function shortFormat( $linker = null ) {
+
+ $outformat = $this->dataValue->getOutputFormat();
+
+ if ( $linker === null || ( $linker === false ) || ( $outformat == '-' ) || ( $outformat == '-u' ) || ( $outformat == '-n' ) || !$this->dataValue->isValid() ) {
+ return $this->dataValue->getCaption();
+ }
+
+ $convertedUnitValues = $this->dataValue->getConvertedUnitValues();
+ $tooltip = '';
+
+ $i = 0;
+
+ foreach ( $convertedUnitValues as $unit => $value ) {
+ if ( $unit != $this->dataValue->getCanonicalMainUnit() ) {
+ $number = $this->dataValue->getLocalizedFormattedNumber( $value );
+ if ( $unit !== '' ) {
+ $tooltip .= $this->dataValue->hasPrefixalUnitPreference( $unit ) ? $unit . '&#160;' . $number : $number . '&#160;' . $unit;
+ } else{
+ $tooltip .= $number;
+ }
+ $tooltip .= ' <br />';
+ $i++;
+ if ( $i >= 5 ) { // limit number of printouts in tooltip
+ break;
+ }
+ }
+ }
+
+ if ( $tooltip === '' ) {
+ return $this->dataValue->getCaption();
+ }
+
+ $highlighter = Highlighter::factory(
+ Highlighter::TYPE_QUANTITY,
+ $this->dataValue->getOption( DataValue::OPT_USER_LANGUAGE )
+ );
+
+ $highlighter->setContent(
+ [
+ 'caption' => $this->dataValue->getCaption(),
+ 'content' => $tooltip
+ ]
+ );
+
+ return $highlighter->getHtml();
+ }
+
+ private function longFormat( $linker = null ) {
+
+ if ( !$this->dataValue->isValid() ) {
+ return $this->dataValue->getErrorText();
+ }
+
+ $outformat = $this->dataValue->getOutputFormat();
+ $convertedUnitValues = $this->dataValue->getConvertedUnitValues();
+
+ $result = '';
+ $i = 0;
+
+ foreach ( $convertedUnitValues as $unit => $value ) {
+
+ if ( $i == 1 ) {
+ $result .= ' (';
+ } elseif ( $i > 1 ) {
+ $result .= ', ';
+ }
+
+ $number = ( $outformat != '-' ? $this->dataValue->getLocalizedFormattedNumber( $value ) : $value );
+
+ if ( $unit !== '' ) {
+ $result .= $this->dataValue->hasPrefixalUnitPreference( $unit ) ? $unit . '&#160;' . $number : $number . '&#160;' . $unit;
+ } else {
+ $result .= $number;
+ }
+
+ $i++;
+
+ if ( $outformat == '-' ) { // no further conversions for plain output format
+ break;
+ }
+ }
+
+ if ( $i > 1 ) {
+ $result .= ')';
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/PropertyValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/PropertyValueFormatter.php
new file mode 100644
index 00000000..81407fb6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/PropertyValueFormatter.php
@@ -0,0 +1,337 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\Highlighter;
+use SMW\Localizer;
+use SMW\Message;
+use SMWDataValue as DataValue;
+use SMW\DataValues\PropertyValue;
+use SMW\PropertySpecificationLookup;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueFormatter extends DataValueFormatter {
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof PropertyValue;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function format( $dataValue, $options = null ) {
+
+ if ( !is_array( $options ) ) {
+ throw new RuntimeException( "Option is not an array!" );
+ }
+
+ if ( !$dataValue instanceof PropertyValue ) {
+ throw new RuntimeException( "The formatter is missing a valid PropertyValue object" );
+ }
+
+ $this->dataValue = $dataValue;
+
+ $type = $options[0];
+ $linker = isset( $options[1] ) ? $options[1] : null;
+
+ if ( !$this->dataValue->isVisible() ) {
+ return '';
+ }
+
+ if ( $type === self::VALUE ) {
+ return $this->getWikiValue();
+ }
+
+ if ( $type === PropertyValue::FORMAT_LABEL ) {
+ return $this->getFormattedLabel( $linker );
+ }
+
+ if ( $type === PropertyValue::SEARCH_LABEL ) {
+ return $this->getSearchLabel();
+ }
+
+ $wikiPageValue = $this->prepareWikiPageValue( $linker );
+ $text = '';
+
+ if ( $wikiPageValue === null ) {
+ return '';
+ }
+
+ if ( $type === self::WIKI_SHORT ) {
+ $text = $this->doHighlightText(
+ $wikiPageValue->getShortWikiText( $linker ),
+ $this->dataValue->getOption( PropertyValue::OPT_HIGHLIGHT_LINKER ) ? $linker : null
+ );
+ }
+
+ if ( $type === self::HTML_SHORT ) {
+ $text = $this->doHighlightText( $wikiPageValue->getShortHTMLText( $linker ), $linker );
+ }
+
+ if ( $type === self::WIKI_LONG ) {
+ $text = $this->doHighlightText( $wikiPageValue->getLongWikiText( $linker ) );
+ }
+
+ if ( $type === self::HTML_LONG ) {
+ $text = $this->doHighlightText( $wikiPageValue->getLongHTMLText( $linker ), $linker );
+ }
+
+ return $text . $this->hintPreferredLabelUse();
+ }
+
+ /**
+ * Formatting rule set:
+ * - preferred goes before translation
+ * - displayTitle goes before translation
+ * - translation goes before "normal" label
+ */
+ private function getFormattedLabel( $linker = null ) {
+
+ $property = $this->dataValue->getDataItem();
+ $output = '';
+ $displayTitle = '';
+
+ $preferredLabel = $property->getPreferredLabel(
+ $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE )
+ );
+
+ $label = $preferredLabel;
+
+ if ( $preferredLabel === '' && ( $label = $this->findTranslatedPropertyLabel( $property ) ) === '' ) {
+ $label = $property->getLabel();
+ }
+
+ if ( $this->dataValue->getWikiPageValue() !== null ) {
+ $displayTitle = $this->dataValue->getWikiPageValue()->getDisplayTitle();
+ }
+
+ $canonicalLabel = $property->getCanonicalLabel();
+
+ // Display title goes before a translated label (but not preferred)
+ if ( $preferredLabel === '' && $displayTitle !== '' ) {
+ $label = $displayTitle;
+ // $canonicalLabel = $displayTitle;
+ }
+
+ // Internal format only used by PropertyValue
+ $format = $this->dataValue->getOption( PropertyValue::FORMAT_LABEL );
+ $this->dataValue->setCaption( $label );
+
+ if ( $format === self::VALUE ) {
+ $output = $this->dataValue->getWikiValue();
+ }
+
+ if ( $format === self::WIKI_LONG && $linker !== null ) {
+ $output = $this->dataValue->getLongWikiText( $linker );
+ } elseif ( $format === self::WIKI_LONG && $preferredLabel === '' && $displayTitle !== '' ) {
+ $output = $displayTitle;
+ } elseif ( $format === self::WIKI_LONG ) {
+ // Avoid Title::getPrefixedText as it transforms the text to have a
+ // leading capital letter in some configurations
+ $output = Localizer::getInstance()->createTextWithNamespacePrefix( SMW_NS_PROPERTY, $label );
+ }
+
+ if ( $format === self::HTML_SHORT && $linker !== null ) {
+ $output = $this->dataValue->getShortHTMLText( $linker );
+ }
+
+ // Output both according to the formatting rule set forth by
+ if ( $canonicalLabel !== $label ) {
+ $canonicalLabel = \Html::rawElement(
+ 'span', [ 'style' => 'font-size:small;' ], '(' . $canonicalLabel . ')' );
+ $output = $output . '&nbsp;'. $canonicalLabel;
+ }
+
+ return $output;
+ }
+
+ private function getWikiValue() {
+
+ $property = $this->dataValue->getDataItem();
+ $languageCode = $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE );
+
+ if ( ( $preferredLabel = $property->getPreferredLabel( $languageCode ) ) !== '' ) {
+ return $preferredLabel;
+ }
+
+ if ( $this->dataValue->getWikiPageValue() !== null && $this->dataValue->getWikiPageValue()->getDisplayTitle() !== '' ) {
+ return $this->dataValue->getWikiPageValue()->getDisplayTitle();
+ }
+
+ if ( ( $translatedPropertyLabel = $this->findTranslatedPropertyLabel( $property ) ) !== '' ) {
+ return $translatedPropertyLabel;
+ }
+
+ return $this->dataValue->getDataItem()->getLabel();
+ }
+
+ /**
+ * The display title modifies the search/sort characteristics (#1534),
+ * (foo:Bar vs. Foo:Bar vs. FOO:bar) therefore select a possible DisplayTitle
+ * before any other label preference.
+ */
+ private function getSearchLabel() {
+
+ $wikiPageValue = $this->dataValue->getWikiPageValue();
+
+ if ( $wikiPageValue !== null && ( $displayTitle = $wikiPageValue->getDisplayTitle() ) !== '' ) {
+ return $displayTitle;
+ }
+
+ return $this->dataValue->getDataItem()->getLabel();
+ }
+
+ private function prepareWikiPageValue( $linker = null ) {
+
+ $wikiPageValue = $this->dataValue->getWikiPageValue();
+
+ if ( $wikiPageValue === null ) {
+ return null;
+ }
+
+ $property = $this->dataValue->getDataItem();
+ $caption = $this->dataValue->getCaption();
+
+ if ( $caption !== false && $caption !== '' ) {
+ $wikiPageValue->setCaption( $caption );
+ } elseif ( ( $preferredLabel = $this->dataValue->getPreferredLabel() ) !== '' ) {
+ $wikiPageValue->setCaption( $preferredLabel );
+ } elseif ( ( $translatedPropertyLabel = $this->findTranslatedPropertyLabel( $property ) ) !== '' ) {
+ $wikiPageValue->setCaption( $translatedPropertyLabel );
+ } else {
+ $wikiPageValue->setCaption( $property->getLabel() );
+ }
+
+ return $wikiPageValue;
+ }
+
+ private function doHighlightText( $text, $linker = null ) {
+
+ $content = '';
+
+ if ( !$this->canHighlight( $content, $linker ) ) {
+ return $text;
+ }
+
+ $highlighter = Highlighter::factory(
+ Highlighter::TYPE_PROPERTY,
+ $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE )
+ );
+
+ $highlighter->setContent(
+ [
+ 'userDefined' => $this->dataValue->getDataItem()->isUserDefined(),
+ 'caption' => $text,
+ 'content' => $content !== '' ? $content : Message::get( 'smw_isspecprop' )
+ ]
+ );
+
+ return $highlighter->getHtml();
+ }
+
+ private function canHighlight( &$propertyDescription, $linker ) {
+
+ if ( $this->dataValue->getOption( PropertyValue::OPT_NO_HIGHLIGHT ) === true ) {
+ return false;
+ }
+
+ $dataItem = $this->dataValue->getDataItem();
+
+ $propertyDescription = $this->propertySpecificationLookup->getPropertyDescriptionByLanguageCode(
+ $dataItem,
+ $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE ),
+ $linker
+ );
+
+ return !$dataItem->isUserDefined() || $propertyDescription !== '';
+ }
+
+ private function hintPreferredLabelUse() {
+
+ if ( !$this->dataValue->isEnabledFeature( SMW_DV_PROV_LHNT ) ||
+ $this->dataValue->getOption( PropertyValue::OPT_NO_PREF_LHNT ) ) {
+ return '';
+ }
+
+ $property = $this->dataValue->getDataItem();
+
+ $preferredLabel = $property->getPreferredLabel(
+ $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE )
+ );
+
+ // When comparing with a caption set from the "outside", normalize
+ // the string to avoid a false negative in case of a non-breaking space
+ $caption = str_replace(
+ [ "&#160;", "&nbsp;", html_entity_decode( '&#160;', ENT_NOQUOTES, 'UTF-8' ) ],
+ " ",
+ $this->dataValue->getCaption()
+ );
+
+ if ( $preferredLabel === '' || $caption !== $preferredLabel ) {
+ return '';
+ }
+
+ $label = $property->getLabel();
+
+ if ( $preferredLabel === $label ) {
+ return '';
+ }
+
+ return '&nbsp;' . \Html::rawElement(
+ 'span',
+ [
+ 'title' => $property->getCanonicalLabel()
+ ],
+ '<sup>áµ–</sup>'
+ );
+ }
+
+ private function findTranslatedPropertyLabel( $property ) {
+
+ // User-defined properties don't have any translatable label (this is
+ // what the preferred label is for)
+ if ( $property->isUserDefined() ) {
+ return '';
+ }
+
+ $prefix = '';
+
+ if ( $property->isInverse() ) {
+ $prefix = '-';
+ }
+
+ return $prefix . ApplicationFactory::getInstance()->getPropertyLabelFinder()->findPropertyLabelFromIdByLanguageCode(
+ $property->getKey(),
+ $this->dataValue->getOption( PropertyValue::OPT_USER_LANGUAGE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ReferenceValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ReferenceValueFormatter.php
new file mode 100644
index 00000000..927f5579
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ReferenceValueFormatter.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DataValues\ExternalIdentifierValue;
+use SMW\DataValues\ReferenceValue;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMWDataValue as DataValue;
+use SMWDITime as DITime;
+use SMWDIUri as DIUri;
+use SMWPropertyValue as PropertyValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReferenceValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof ReferenceValue;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function format( $type, $linker = null ) {
+
+ if ( !$this->dataValue instanceof ReferenceValue ) {
+ throw new RuntimeException( "The formatter is missing a valid ReferenceValue object" );
+ }
+
+ if ( $this->dataValue->getCaption() !== false &&
+ ( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) ) {
+ return $this->dataValue->getCaption();
+ }
+
+ return $this->getOutputText( $type, $linker );
+ }
+
+ protected function getOutputText( $type, $linker = null ) {
+
+ if ( !$this->dataValue->isValid() ) {
+ return ( ( $type == self::WIKI_SHORT ) || ( $type == self::HTML_SHORT ) ) ? '' : $this->dataValue->getErrorText();
+ }
+
+ return $this->createOutput( $type, $linker );
+ }
+
+ private function createOutput( $type, $linker ) {
+
+ $results = $this->getListOfFormattedPropertyDataItems(
+ $type,
+ $linker,
+ $this->dataValue->getPropertyDataItems()
+ );
+
+ if ( $type == self::VALUE || $linker === null ) {
+ return implode( ';', $results );
+ }
+
+ $result = array_shift( $results );
+ $class = 'smw-reference-otiose';
+
+ // "smw-highlighter smwttinline" signals to invoke the tooltip
+ if ( count( $results ) > 0 ) {
+ $class = 'smw-reference smw-reference-indicator smw-highlighter smwttinline';
+ }
+
+ // Add an extra "title" attribute to support nojs environments by allowing
+ // it to display references even without JS, it will be removed when JS is available
+ // to show the "normal" tooltip
+ $result .= \Html::rawElement(
+ 'span',
+ [
+ 'class' => $class,
+ 'data-title' => Message::get( 'smw-ui-tooltip-title-reference', Message::TEXT, Message::USER_LANGUAGE ),
+ 'data-content' => '<ul><li>' . implode( '</li><li>', $results ) . '</li></ul>',
+ 'title' => strip_tags( implode( ', ', $results ) )
+ ]
+ );
+
+ return $result;
+ }
+
+ private function getListOfFormattedPropertyDataItems( $type, $linker, $propertyDataItems ) {
+
+ $results = [];
+
+ foreach ( $propertyDataItems as $propertyDataItem ) {
+
+ $propertyValues = $this->dataValue->getDataItem()->getSemanticData()->getPropertyValues( $propertyDataItem );
+ $dataItem = reset( $propertyValues );
+
+ // By definition the first element in the list is the VALUE other
+ // members are referencing to
+ $isValue = $results === [];
+ $dataValue = null;
+
+ if ( $dataItem !== false ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $propertyDataItem );
+ $output = $this->findValueOutputFor( $isValue, $type, $dataValue, $linker );
+ } else {
+ $output = '?';
+ }
+
+ // Return a plain value in case no linker object is available
+ if ( $dataValue !== null && $linker === null ) {
+ return [ $dataValue->getWikiValue() ];
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $propertyDataItem
+ );
+
+ // Tooltip in tooltip isn't expected to work therefore avoid them
+ // when generating property labels in a reference output
+ $dataValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ if ( !$isValue && $type !== self::VALUE ) {
+ $output = Message::get(
+ [
+ 'smw-datavalue-reference-outputformat',
+ $dataValue->getShortHTMLText( smwfGetLinker() ),
+ $output
+ ],
+ Message::TEXT
+ );
+ }
+
+ $results[] = $output;
+ }
+
+ return $results;
+ }
+
+ private function findValueOutputFor( $isValue, $type, $dataValue, $linker ) {
+
+ $dataItem = $dataValue->getDataItem();
+
+ // Turn Uri/Page links into a href representation when not used as value
+ if ( !$isValue &&
+ ( $dataItem instanceof DIUri || $dataItem instanceof DIWikiPage ) &&
+ $type !== self::VALUE || $dataValue->getTypeID() === ExternalIdentifierValue::TYPE_ID ) {
+ return $dataValue->getShortHTMLText( smwfGetLinker() );
+ }
+
+ // Dates and times are to be displayed in a localized format
+ if ( !$isValue && $dataItem instanceof DITime && $type !== self::VALUE ) {
+ $dataValue->setOutputFormat( 'LOCL' );
+ }
+
+ switch ( $type ) {
+ case self::VALUE:
+ return $dataValue->getWikiValue();
+ case self::WIKI_SHORT:
+ return $dataValue->getShortWikiText( $linker );
+ case self::HTML_SHORT:
+ return $dataValue->getShortHTMLText( $linker );
+ case self::WIKI_LONG:
+ return $dataValue->getLongWikiText( $linker );
+ case self::HTML_LONG:
+ return $dataValue->getLongHTMLText( $linker );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/StringValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/StringValueFormatter.php
new file mode 100644
index 00000000..d55ad620
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/StringValueFormatter.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\DataValues\StringValue;
+use SMW\Highlighter;
+use SMW\Utils\Normalizer;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class StringValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof StringValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function format( $dataValue, $options = null ) {
+
+ if ( !is_array( $options ) ) {
+ throw new RuntimeException( "Option is not an array!" );
+ }
+
+ // Normally we would do `list( $type, $linker ) = $options;` BUT due to
+ // PHP 7.0 ... "The order that the assignment operations are performed in has changed."
+
+ $type = $options[0];
+ $linker = isset( $options[1] ) ? $options[1] : null;
+
+ if ( !$dataValue instanceof StringValue ) {
+ throw new RuntimeException( "The formatter is missing a valid StringValue object" );
+ }
+
+ if ( $type === self::VALUE ) {
+ return $dataValue->isValid() ? $dataValue->getDataItem()->getString() : 'error';
+ }
+
+ if ( $dataValue->getCaption() !== false && $type === self::WIKI_SHORT ) {
+ return $dataValue->getCaption();
+ }
+
+ if ( $dataValue->getCaption() !== false && $type === self::HTML_SHORT ) {
+ return smwfXMLContentEncode( $dataValue->getCaption() );
+ }
+
+ if ( !$dataValue->isValid() ) {
+ return $dataValue->getDataItem()->getUserValue();
+ }
+
+ return $this->doFormat( $dataValue, $type, $linker );
+ }
+
+ protected function doFormat( $dataValue, $type, $linker ) {
+
+ $text = $dataValue->getDataItem()->getString();
+ $length = mb_strlen( $text );
+
+ // Make a possibly shortened printout string for displaying the value.
+ // The result is only escaped to be HTML-safe if this is requested
+ // explicitly. The result will contain mark-up that must not be escaped
+ // again.
+ $abbreviate = $type === self::WIKI_LONG || $type === self::HTML_LONG;
+ $requestedLength = intval( $dataValue->getOutputFormat() );
+
+ // Appease the MW parser to correctly apply formatting on the
+ // first indent
+ if ( $text !== '' && ( $text{0} === '*' || $text{0} === '#' || $text{0} === ':' ) ) {
+ $text = "\n" . $text . "\n";
+ }
+
+ if ( $requestedLength > 0 && $requestedLength < $length ) {
+ // Reduces the length and finish it with a whole word
+ return Normalizer::reduceLengthTo( $text, $requestedLength ) . ' …';
+ }
+
+ if ( $type === self::HTML_SHORT || $type === self::HTML_LONG ) {
+ $text = smwfXMLContentEncode( $text );
+ }
+
+ if ( $abbreviate && $length > 255 ) {
+ $text = $this->getAbbreviatedText( $text, $length, $linker );
+ }
+
+ return $text;
+ }
+
+ private function getAbbreviatedText( $text, $length, $linker ) {
+
+ if ( $linker === false || $linker === null ) {
+ $ellipsis = ' <span class="smwwarning">…</span> ';
+ } else {
+ $highlighter = Highlighter::factory( Highlighter::TYPE_TEXT );
+ $highlighter->setContent( [
+ 'caption' => ' … ',
+ 'content' => $text
+ ] );
+
+ $ellipsis = $highlighter->getHtml();
+ }
+
+ $startOff = 42;
+ $endOff = 42;
+
+ // Avoid breaking a link (i.e. [[ ... ]])
+ if ( ( $pos = stripos ( $text, '[[' ) ) && $pos < 42 ) {
+ $startOff = stripos ( $text, ']]' ) + 2;
+ }
+
+ if ( ( $pos = strrpos ( $text, ']]' ) ) && $pos > $length - $endOff ) {
+ $endOff = $length - strrpos( $text, '[[' );
+ }
+
+ return mb_substr( $text, 0, $startOff ) . $ellipsis . mb_substr( $text, $length - $endOff );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/TimeValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/TimeValueFormatter.php
new file mode 100644
index 00000000..40440e91
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/TimeValueFormatter.php
@@ -0,0 +1,402 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+use RuntimeException;
+use SMW\DataValues\Time\IntlTimeFormatter;
+use SMW\Localizer;
+use SMWDataValue as DataValue;
+use SMWDITime as DITime;
+use SMWTimeValue as TimeValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ * @author Fabian Howahl
+ * @author Terry A. Hurlbut
+ */
+class TimeValueFormatter extends DataValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isFormatterFor( DataValue $dataValue ) {
+ return $dataValue instanceof TimeValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function format( $type, $linker = null ) {
+
+ if ( !$this->dataValue instanceof TimeValue ) {
+ throw new RuntimeException( "The formatter is missing a valid TimeValue object" );
+ }
+
+ if (
+ $this->dataValue->isValid() &&
+ ( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) ) {
+ return ( $this->dataValue->getCaption() !== false ) ? $this->dataValue->getCaption() : $this->getPreferredCaption();
+ }
+
+ if (
+ $this->dataValue->isValid() &&
+ ( $type === self::WIKI_LONG || $type === self::HTML_LONG ) ) {
+ return $this->getPreferredCaption();
+ }
+
+ // #1074
+ return $this->dataValue->getCaption() !== false ? $this->dataValue->getCaption() : '';
+ }
+
+ /**
+ * @private
+ *
+ * Compute a string representation that largely follows the ISO8601 standard
+ * of representing dates. Large year numbers may have more than 4 digits,
+ * which is not strictly conforming to the standard. The date includes year,
+ * month, and day regardless of the input precision, but will only include
+ * time when specified.
+ *
+ * Conforming to the 2000 version of ISO8601, year 1 BC(E) is represented
+ * as "0000", year 2 BC(E) as "-0001" and so on.
+ *
+ * @since 2.4
+ *
+ * @param DITime $dataItem
+ * @param boolean $mindefault determining whether values below the
+ * precision of our input should be completed with minimal or maximal
+ * conceivable values
+ *
+ * @return string
+ */
+ public function getISO8601Date( $mindefault = true ) {
+
+ $dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
+ $precision = $dataItem->getPrecision();
+
+ $result = $dataItem->getYear() > 0 ? '' : '-';
+ $result .= str_pad( $dataItem->getYear(), 4, "0", STR_PAD_LEFT );
+
+ $monthnum = $precision >= DITime::PREC_YM ? $dataItem->getMonth() : ( $mindefault ? 1 : 12 );
+ $result .= '-' . str_pad( $monthnum, 2, "0", STR_PAD_LEFT );
+
+ $day = $dataItem->getDay();
+
+ if ( !$mindefault && $precision < DITime::PREC_YMD ) {
+ $day = DITime::getDayNumberForMonth( $monthnum, $dataItem->getYear(), DITime::CM_GREGORIAN );
+ }
+
+ $result .= '-' . str_pad( $day, 2, "0", STR_PAD_LEFT );
+
+ if ( $precision === DITime::PREC_YMDT ) {
+ $result .= 'T' . $this->getTimeString( ( $mindefault ? '00:00:00' : '23:59:59' ) );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @private
+ *
+ * Use MediaWiki's date and time formatting. It can't handle all inputs
+ * properly, but has superior i18n support.
+ *
+ * @since 2.4
+ *
+ * @param DITime $dataItem
+ *
+ * @return string
+ */
+ public function getMediaWikiDate() {
+
+ $dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
+ $precision = $dataItem->getPrecision();
+
+ $language = Localizer::getInstance()->getLanguage(
+ $this->dataValue->getOption( DataValue::OPT_USER_LANGUAGE )
+ );
+
+ $year = $dataItem->getYear();
+
+ if ( $year < 0 || $year > 9999 ) {
+ $year = '0000';
+ }
+
+ $year = str_pad( $year, 4, "0", STR_PAD_LEFT );
+
+ if ( $precision <= DITime::PREC_Y ) {
+ return $language->formatNum( $year, true );
+ }
+
+ $month = str_pad( $dataItem->getMonth(), 2, "0", STR_PAD_LEFT );
+ $day = str_pad( $dataItem->getDay(), 2, "0", STR_PAD_LEFT );
+
+ // date/timeanddate options:
+ // 1. Whether to adjust the time output according to the user
+ // configured offset ($timecorrection)
+ // 2. format, if it's false output the default one (default true)
+ // 3. timecorrection, the time offset as returned from
+ // Special:Preferences
+
+ if ( $precision <= DITime::PREC_YMD ) {
+ return $language->date( "$year$month$day" . '000000', false, true, false );
+ }
+
+ $time = str_replace( ':', '', $this->getTimeString() );
+
+ return $language->timeanddate( "$year$month$day$time", false, true, false );
+ }
+
+ /**
+ * @private
+ *
+ * @todo Internationalize the CE and BCE strings.
+ *
+ * Compute a suitable string to display the given date item.
+ *
+ * @note MediaWiki's date functions are not applicable for the range of
+ * historic dates we support.
+ *
+ * @since 2.4
+ *
+ * @param DITime $dataitem
+ *
+ * @return string
+ */
+ public function getCaptionFromDataItem( DITime $dataItem ) {
+
+ // If the language code is empty then the content language code is used
+ $lang = Localizer::getInstance()->getLang(
+ Localizer::getInstance()->getContentLanguage()
+ );
+
+ // https://en.wikipedia.org/wiki/Anno_Domini
+ // "...placing the "AD" abbreviation before the year number ... BC is
+ // placed after the year number (for example: AD 2016, but 68 BC)..."
+ // Chicago Manual of Style 2010, pp. 476–7; Goldstein 2007, p. 6.
+
+ if ( $dataItem->getYear() > 0 ) {
+ $cestring = $dataItem->getEra() > 0 ? 'AD' : '';
+ $result = ( $cestring ? ( $cestring . ' ' ) : '' ) . number_format( $dataItem->getYear(), 0, '.', '' );
+ } else {
+ $bcestring = 'BC';
+ $result = number_format( -( $dataItem->getYear() ), 0, '.', '' ) . ( $bcestring ? ( ' ' . $bcestring ) : '' );
+ }
+
+ if ( $dataItem->getPrecision() >= DITime::PREC_YM ) {
+ $result = $lang->getMonthLabel( $dataItem->getMonth() ) . " " . $result;
+ }
+
+ if ( $dataItem->getPrecision() >= DITime::PREC_YMD ) {
+ $result = $dataItem->getDay() . " " . $result;
+ }
+
+ if ( $dataItem->getPrecision() >= DITime::PREC_YMDT ) {
+ $result .= " " . $this->getTimeString();
+ }
+
+ $result .= $this->hintCalendarModel( $dataItem );
+
+ return $result;
+ }
+
+ /**
+ * @private
+ *
+ * Return the time as a string. The time string has the format HH:MM:SS,
+ * without any timezone information (see class documentation for details
+ * on current timezone handling).
+ * The parameter $default optionally specifies the value returned
+ * if the date is valid but has no explicitly specified time. It can
+ * also be set to false to detect this situation.
+ *
+ * @since 2.4
+ *
+ * @param string $default
+ *
+ * @return string
+ */
+ public function getTimeString( $default = '00:00:00' ) {
+
+ $dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
+
+ if ( $dataItem->getPrecision() < DITime::PREC_YMDT ) {
+ return $default;
+ }
+
+ return sprintf( "%02d", $dataItem->getHour() ) . ':' .
+ sprintf( "%02d", $dataItem->getMinute() ) . ':' .
+ sprintf( "%02d", $dataItem->getSecond() );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DITime|null $dataItem
+ *
+ * @return string
+ */
+ public function getCaptionFromFreeFormat( DITime $dataItem = null ) {
+
+ $language = Localizer::getInstance()->getLanguage(
+ $this->dataValue->getOption( DataValue::OPT_USER_LANGUAGE )
+ );
+
+ // Prehistory dates are not supported when using this output format
+ // Only match options encapsulated by [ ... ]
+ if (
+ $dataItem !== null &&
+ $dataItem->getYear() > DITime::PREHISTORY &&
+ preg_match("/\[([^\]]*)\]/", $this->dataValue->getOutputFormat(), $matches ) ) {
+ $intlTimeFormatter = new IntlTimeFormatter( $dataItem, $language );
+
+ if ( ( $caption = $intlTimeFormatter->format( $matches[1] ) ) !== false ) {
+
+ if ( $intlTimeFormatter->containsValidDateFormatRule( $matches[1] ) ) {
+ $caption .= $this->hintCalendarModel( $dataItem );
+ }
+
+ return $caption;
+ }
+ }
+
+ return $this->getISO8601Date();
+ }
+
+ /**
+ * @private
+ *
+ * @since 2.4
+ *
+ * @param DITime|null $dataItem
+ *
+ * @return string
+ */
+ public function getLocalizedFormat( DITime $dataItem = null ) {
+
+ if ( $dataItem === null ) {
+ return '';
+ }
+
+ if ( $dataItem->getYear() < DITime::PREHISTORY ) {
+ return $this->getISO8601Date();
+ }
+
+ $outputFormat = $this->dataValue->getOutputFormat();
+ $formatFlag = IntlTimeFormatter::LOCL_DEFAULT;
+ $hasTimeCorrection = false;
+
+ if ( strpos( $outputFormat, 'TO' ) !== false ) {
+ $formatFlag = IntlTimeFormatter::LOCL_TIMEOFFSET;
+ $outputFormat = str_replace( '#TO', '', $outputFormat );
+ }
+
+ if ( strpos( $outputFormat, 'TZ' ) !== false ) {
+ $formatFlag = $formatFlag | IntlTimeFormatter::LOCL_TIMEZONE;
+ $outputFormat = str_replace( '#TZ', '', $outputFormat );
+ }
+
+ if ( ( $language = Localizer::getInstance()->getLanguageCodeFrom( $outputFormat ) ) === false ) {
+ $language = $this->dataValue->getOption( DataValue::OPT_USER_LANGUAGE );
+ }
+
+ $language = Localizer::getInstance()->getLanguage( $language );
+
+ $intlTimeFormatter = new IntlTimeFormatter(
+ $dataItem,
+ $language
+ );
+
+ // Avoid an exception on "DateTime::__construct(): Failed to parse time
+ // string (2147483647-01-01 00:00:0.0000000) at position 17 (0): Double
+ // time specification" for an annotation like [[Date::Jan 10000000000]]
+ try {
+ $localizedFormat = $intlTimeFormatter->getLocalizedFormat( $formatFlag ) .
+ $this->hintTimeCorrection( $intlTimeFormatter->hasLocalTimeCorrection() ) .
+ $this->hintCalendarModel( $dataItem );
+ } catch ( \Exception $e ) {
+ $localizedFormat = $this->getISO8601Date();
+ }
+
+ return $localizedFormat;
+ }
+
+ /**
+ * Compute a suitable string to display this date, taking into account the
+ * output format and the preferable calendar models for the data.
+ *
+ * @note MediaWiki's date functions are not applicable for the range
+ * of historic dates we support.
+ *
+ * @return string
+ */
+ protected function getPreferredCaption() {
+
+ $dataItem = $this->dataValue->getDataItem();
+ $format = strtoupper( $this->dataValue->getOutputFormat() );
+
+ if ( $format == 'ISO' || $this->dataValue->getOutputFormat() == '-' ) {
+ return $this->getISO8601Date();
+ } elseif ( $format == 'MEDIAWIKI' ) {
+ return $this->getMediaWikiDate();
+ } elseif ( $format == 'SORTKEY' ) {
+ return $dataItem->getSortKey();
+ } elseif ( $format == 'JD' ) {
+ return $dataItem->getJD();
+ }
+
+ // Does the formatting require calendar conversion?
+ $model = $dataItem->getCalendarModel();
+
+ if (
+ ( strpos( $format, 'JL' ) !== false ) ||
+ ( $dataItem->getJD() < TimeValue::J1582 && strpos( $format, 'GR' ) === false ) ) {
+ $model = DITime::CM_JULIAN;
+ } elseif ( strpos( $format, 'GR' ) !== false ) {
+ $model = DITime::CM_GREGORIAN;
+ }
+
+ if ( strpos( $format, '-F[' ) !== false ) {
+ return $this->getCaptionFromFreeFormat( $this->dataValue->getDataItemForCalendarModel( $model ) );
+ } elseif ( strpos( $format, 'LOCL' ) !== false ) {
+ return $this->getLocalizedFormat( $this->dataValue->getDataItemForCalendarModel( $model ) );
+ } elseif ( $dataItem->getYear() > TimeValue::PREHISTORY && $dataItem->getPrecision() >= DITime::PREC_YM ) {
+ // Do not convert between Gregorian and Julian if only
+ // year is given (years largely overlap in history, but
+ // assuming 1 Jan as the default date, the year number
+ // would change in conversion).
+ // Also do not convert calendars in prehistory: not
+ // meaningful (getDataItemForCalendarModel may return null).
+ return $this->getCaptionFromDataItem( $this->dataValue->getDataItemForCalendarModel( $model ) );
+ }
+
+ return $this->getCaptionFromDataItem( $dataItem );
+ }
+
+ private function hintTimeCorrection( $hasTimeCorrection ) {
+
+ if ( $hasTimeCorrection ) {
+ return '&nbsp;' . \Html::rawElement( 'sup', [ 'title' => 'ISO: ' . $this->getISO8601Date() ], 'á´¸' );
+ }
+
+ return '';
+ }
+
+ private function hintCalendarModel( $dataItem ) {
+
+ if ( $this->dataValue->isEnabledFeature( SMW_DV_TIMEV_CM ) && $dataItem->getCalendarModel() !== DITime::CM_GREGORIAN ) {
+ return ' ' . \Html::rawElement( 'sup', [], $dataItem->getCalendarModelLiteral() );
+ }
+
+ return '';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ValueFormatter.php
new file mode 100644
index 00000000..921570be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueFormatters/ValueFormatter.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\DataValues\ValueFormatters;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+interface ValueFormatter {
+
+ /**
+ * @since 2.4
+ *
+ * @param mixed $type
+ * @param mixed|null $linker
+ *
+ * @return mixed
+ * @throws RuntimeException
+ */
+ public function format( $type, $linker = null );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsListValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsListValueParser.php
new file mode 100644
index 00000000..b476a7b2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsListValueParser.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\DataValues\AllowsListValue;
+use SMW\MediaWiki\MediaWikiNsContentReader;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class AllowsListValueParser implements ValueParser {
+
+ /**
+ * @var MediaWikiNsContentReader
+ */
+ private $mediaWikiNsContentReader;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private static $contents = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param MediaWikiNsContentReader $mediaWikiNsContentReader
+ */
+ public function __construct( MediaWikiNsContentReader $mediaWikiNsContentReader ) {
+ $this->mediaWikiNsContentReader = $mediaWikiNsContentReader;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {
+ self::$contents = [];
+ $this->errors = [];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $userValue
+ *
+ * @return string|false
+ */
+ public function parse( $userValue ) {
+
+ $this->errors = [];
+
+ if ( isset( self::$contents[$userValue] ) ) {
+ return self::$contents[$userValue];
+ }
+
+ self::$contents[$userValue] = $this->parse_contents(
+ $userValue,
+ $this->mediaWikiNsContentReader->read( AllowsListValue::LIST_PREFIX . $userValue )
+ );
+
+ return self::$contents[$userValue];
+ }
+
+ private function parse_contents( $userValue, $contents ) {
+
+ if ( $contents === '' ) {
+ return $this->errors[] = [ 'smw-datavalue-allows-value-list-unknown', $userValue ];
+ }
+
+ if ( $contents{0} === '{' && ( $list = json_decode( $contents, true ) ) && is_array( $list ) ) {
+ return $list;
+ }
+
+ return $this->parse_string( $userValue, $contents );
+ }
+
+ private function parse_string( $userValue, $contents ) {
+
+ $parts = array_map( 'trim', preg_split( "([\n][\s]?)", $contents ) );
+ $list = [];
+
+ foreach ( $parts as $part ) {
+
+ // Only recognize those with a * Foo
+ if ( strpos( $part, '*' ) === false ) {
+ continue;
+ }
+
+ // Remove * from the content, other processes may use the hierarchy
+ // indicator something else
+ $part = trim( str_replace( '*', '', $part ) );
+
+ // Allow something like * Foo|Bar
+ if ( strpos( $part, '|' ) !== false ) {
+ list( $reference, $val ) = explode( '|', $part, 2 );
+ $list[$reference] = $val;
+ } else {
+ $list[$part] = $part;
+ }
+ }
+
+ if ( $list === [] ) {
+ $this->errors[] = [ 'smw-datavalue-allows-value-list-missing-marker', $userValue ];
+ }
+
+ return $list;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsPatternValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsPatternValueParser.php
new file mode 100644
index 00000000..f8a29c6e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/AllowsPatternValueParser.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\DataValues\AllowsPatternValue;
+use SMW\MediaWiki\MediaWikiNsContentReader;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsPatternValueParser implements ValueParser {
+
+ /**
+ * @var MediaWikiNsContentReader
+ */
+ private $mediaWikiNsContentReader;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param MediaWikiNsContentReader $mediaWikiNsContentReader
+ */
+ public function __construct( MediaWikiNsContentReader $mediaWikiNsContentReader ) {
+ $this->mediaWikiNsContentReader = $mediaWikiNsContentReader;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $userValue
+ *
+ * @return string|false
+ */
+ public function parse( $userValue ) {
+
+ $this->errors = [];
+
+ $contentList = $this->doParseContent(
+ $this->mediaWikiNsContentReader->read( AllowsPatternValue::REFERENCE_PAGE_ID )
+ );
+
+ if ( !isset( $contentList[$userValue] ) ) {
+ return false;
+ }
+
+ return $contentList[$userValue];
+ }
+
+ private function doParseContent( $contents ) {
+
+ $list = [];
+
+ if ( $contents === '' ) {
+ return null;
+ }
+
+ $parts = array_map( 'trim', preg_split( "([\n][\s]?)", $contents ) );
+
+ // Get definition from first line
+ array_shift( $parts );
+
+ foreach ( $parts as $part ) {
+
+ if ( strpos( $part, '|' ) === false ) {
+ continue;
+ }
+
+ list( $reference, $regex ) = explode( '|', $part, 2 );
+ $list[trim( $reference )] = $regex;
+ }
+
+ return $list;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ImportValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ImportValueParser.php
new file mode 100644
index 00000000..4674433f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ImportValueParser.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\DataValues\ImportValue;
+use SMW\MediaWiki\MediaWikiNsContentReader;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ImportValueParser implements ValueParser {
+
+ /**
+ * @var MediaWikiNsContentReader
+ */
+ private $mediaWikiNsContentReader;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @since 2.2
+ *
+ * @param MediaWikiNsContentReader $mediaWikiNsContentReader
+ */
+ public function __construct( MediaWikiNsContentReader $mediaWikiNsContentReader ) {
+ $this->mediaWikiNsContentReader = $mediaWikiNsContentReader;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array|null
+ */
+ public function parse( $value ) {
+
+ list( $namespace, $section, $controlledVocabulary ) = $this->splitByNamespaceSection(
+ $value
+ );
+
+ if ( $this->errors !== [] ) {
+ return null;
+ }
+
+ list( $uri, $name, $typelist ) = $this->doParse(
+ $controlledVocabulary
+ );
+
+ $type = $this->checkForValidType(
+ $namespace,
+ $section,
+ $uri,
+ $typelist
+ );
+
+ if ( $this->errors !== [] ) {
+ return null;
+ }
+
+ return [
+ $namespace,
+ $section,
+ $uri,
+ $name,
+ $type
+ ];
+ }
+
+ /**
+ * @return array|null
+ */
+ private function splitByNamespaceSection( $value ) {
+
+ if ( strpos( $value, ':' ) === false ) {
+
+ $this->errors[] = [
+ 'smw-datavalue-import-invalid-value',
+ $value
+ ];
+
+ return null;
+ }
+
+ list( $namespace, $section ) = explode( ':', $value, 2 );
+
+ /*
+ * A controlled vocabulary is a list of terms, with terms being unambiguous,
+ * and non-redundant. Vocabulary definitions adhere only a limited set of
+ * rules/constraints (e.g. Type/Label)
+ */
+ $controlledVocabulary = $this->mediaWikiNsContentReader->read(
+ ImportValue::IMPORT_PREFIX . $namespace
+ );
+
+ // Check that elements exists for the namespace
+ if ( $controlledVocabulary === '' ) {
+
+ $this->errors[] = [
+ 'smw-datavalue-import-unknown-namespace',
+ $namespace
+ ];
+
+ return null;
+ }
+
+ return [ $namespace, $section, $controlledVocabulary ];
+ }
+
+ /**
+ * @return array|null
+ */
+ private function checkForValidType( $namespace, $section, $uri, $typelist ) {
+
+ if ( $uri === '' ) {
+
+ $this->errors[] = [
+ 'smw-datavalue-import-missing-namespace-uri',
+ $namespace
+ ];
+
+ return null;
+ }
+
+ if ( !isset( $typelist[$section] ) ) {
+
+ $this->errors[] = [
+ 'smw-datavalue-import-missing-type',
+ $section,
+ $namespace
+ ];
+
+ return null;
+ }
+
+ return $typelist[$section];
+ }
+
+ /**
+ * @return array|null
+ */
+ private function doParse( $controlledVocabulary ) {
+
+ $list = [];
+ $importDefintions = array_map( 'trim', preg_split( "([\n][\s]?)", $controlledVocabulary ) );
+
+ // Get definition from first line
+ $fristLine = array_shift( $importDefintions );
+
+ if ( strpos( $fristLine, '|' ) === false ) {
+ return;
+ }
+
+ list( $uri, $name ) = explode( '|', $fristLine, 2 );
+
+ foreach ( $importDefintions as $importDefintion ) {
+
+ if ( strpos( $importDefintion, '|' ) === false ) {
+ continue;
+ }
+
+ list( $secname, $typestring ) = explode( '|', $importDefintion, 2 );
+ $list[trim( $secname )] = $typestring;
+ }
+
+ return [ $uri, $name, $list ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/MonolingualTextValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/MonolingualTextValueParser.php
new file mode 100644
index 00000000..fdf1e5ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/MonolingualTextValueParser.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\Localizer;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueParser implements ValueParser {
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|array $userValue
+ *
+ * @return array
+ */
+ public function parse( $userValue ) {
+
+ // Allow things like [ "en" => "Foo ..." ] when retrieved from a JSON string
+ if ( is_array( $userValue ) ) {
+ foreach ( $userValue as $key => $value ) {
+ $languageCode = is_string( $key ) ? $key : '';
+ $text = is_string( $value ) ? $value : '';
+ }
+ } else {
+ $text = $userValue;
+ $languageCode = mb_substr( strrchr( $userValue, "@" ), 1 );
+
+ // Remove the language code and marker from the text
+ if ( $languageCode !== '' ) {
+ $text = substr_replace( $userValue, '', ( mb_strlen( $languageCode ) + 1 ) * -1 );
+ }
+ }
+
+ return [ $text, Localizer::asBCP47FormattedLanguageCode( $languageCode ) ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/PropertyValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/PropertyValueParser.php
new file mode 100644
index 00000000..884aac62
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/PropertyValueParser.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\Localizer;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueParser implements ValueParser {
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $invalidCharacterList = [];
+
+ /**
+ * @var boolean
+ */
+ private $isCapitalLinks = true;
+
+ /**
+ * @var boolean
+ */
+ private $reqCapitalizedFirstChar = false;
+
+ /**
+ * @var boolean
+ */
+ private $isQueryContext = false;
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $invalidCharacterList
+ */
+ public function setInvalidCharacterList( array $invalidCharacterList ) {
+ $this->invalidCharacterList = $invalidCharacterList;
+ }
+
+ /**
+ * Corresponds to the $wgCapitalLinks setting
+ *
+ * @since 3.0
+ *
+ * @param boolean $isCapitalLinks
+ */
+ public function isCapitalLinks( $isCapitalLinks ) {
+ $this->isCapitalLinks = (bool)$isCapitalLinks;
+ }
+
+ /**
+ * Whether upper case for the first character is required or not in case of
+ * $wgCapitalLinks = false.
+ *
+ * @since 2.5
+ *
+ * @param boolean $reqCapitalizedFirstChar
+ */
+ public function reqCapitalizedFirstChar( $reqCapitalizedFirstChar ) {
+ $this->reqCapitalizedFirstChar = (bool)$reqCapitalizedFirstChar;
+ }
+
+ /**
+ * Whether or not the parsing is executed within a query context which may
+ * allow exceptions on the validation of invalid characters.
+ *
+ * @since 2.5
+ *
+ * @param boolean $isQueryContext
+ */
+ public function isQueryContext( $isQueryContext ) {
+ $this->isQueryContext = (bool)$isQueryContext;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $userValue
+ *
+ * @return array
+ */
+ public function parse( $userValue ) {
+
+ $this->errors = [];
+
+ // #1727 <Foo> or <Foo-<Bar> are not permitted but
+ // Foo-<Bar will be converted to Foo-
+ $userValue = strip_tags(
+ htmlspecialchars_decode( $userValue )
+ );
+
+ if ( !$this->hasValidCharacters( $userValue ) ) {
+ return [ null, null, null ];
+ }
+
+ return $this->getNormalizedValueFrom( $userValue );
+ }
+
+ private function hasValidCharacters( $value ) {
+
+ if ( trim( $value ) === '' ) {
+ $this->errors[] = [ 'smw_emptystring' ];
+ return false;
+ }
+
+ $invalidCharacter = '';
+
+ foreach ( $this->invalidCharacterList as $character ) {
+ if ( strpos( $value, $character ) !== false ) {
+ $invalidCharacter = $character;
+ break;
+ }
+ }
+
+ // #1567, Only allowed in connection with a query context (e.g sort=#)
+ if ( $invalidCharacter === '' && strpos( $value, '#' ) !== false && !$this->isQueryContext ) {
+ $invalidCharacter = '#';
+ }
+
+ if ( $invalidCharacter !== '' ) {
+
+ // Replace selected control chars otherwise the error display becomes
+ // unreadable
+ $invalidCharacter = str_replace(
+ [ "\r", "\n", ],
+ [ "CR", "LF" ],
+ $invalidCharacter
+ );
+
+ $this->errors[] = [ 'smw-datavalue-property-invalid-character', $value, $invalidCharacter ];
+ return false;
+ }
+
+ // #676, only on a query context allow Foo.Bar
+ if ( $invalidCharacter === '' && !$this->isQueryContext && strpos( $value, '.' ) !== false ) {
+ $this->errors[] = [ 'smw-datavalue-property-invalid-chain', $value ];
+ return false;
+ }
+
+ return true;
+ }
+
+ private function getNormalizedValueFrom( $value ) {
+
+ $inverse = false;
+ $capitalizedName = '';
+
+ // slightly normalise label
+ $propertyName = $this->doNormalize(
+ ltrim( rtrim( $value, ' ]' ), ' [' ),
+ $this->isCapitalLinks
+ );
+
+ if ( $this->reqCapitalizedFirstChar ) {
+ $capitalizedName = $this->doNormalize( $propertyName, true );
+ }
+
+ // property refers to an inverse
+ if ( ( $propertyName !== '' ) && ( $propertyName { 0 } == '-' ) ) {
+ $propertyName = $this->doNormalize( (string)substr( $value, 1 ), $this->isCapitalLinks );
+ /// NOTE The cast is necessary at least in PHP 5.3.3 to get string '' instead of boolean false.
+ /// NOTE It is necessary to normalize again here, since normalization may uppercase the first letter.
+ $inverse = true;
+ }
+
+ return [ $propertyName, $capitalizedName, $inverse ];
+ }
+
+ private function doNormalize( $text, $isCapitalLinks ) {
+
+ $text = trim( $text );
+
+ if ( $isCapitalLinks ) {
+ $text = Localizer::getInstance()->getContentLanguage()->ucfirst( $text );
+ }
+
+ return str_replace( '_', ' ', $text );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/TimeValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/TimeValueParser.php
new file mode 100644
index 00000000..91ce12cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/TimeValueParser.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+use SMW\DataValues\Time\Components;
+use SMW\DataValues\Time\Timezone;
+use SMW\Localizer;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ * @author Fabian Howahl
+ * @author Terry A. Hurlbut
+ * @author mwjames
+ */
+class TimeValueParser implements ValueParser {
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var string
+ */
+ private $userValue = '';
+
+ /**
+ * @var array
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $languageCode
+ */
+ public function setLanguageCode( $languageCode ) {
+ $this->languageCode = $languageCode;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clearErrors() {
+ $this->errors = [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $userValue
+ *
+ * @return string|false
+ */
+ public function parse( $userValue ) {
+
+ $this->errors = [];
+ $this->userValue = $userValue;
+
+ $datecomponents = [];
+ $calendarmodel = $era = $hours = $minutes = $seconds = $microseconds = $timeoffset = $timezone = false;
+
+ $status = $this->parseDateString(
+ $userValue,
+ $datecomponents,
+ $calendarmodel,
+ $era,
+ $hours,
+ $minutes,
+ $seconds,
+ $microseconds,
+ $timeoffset,
+ $timezone
+ );
+
+ // Default to JD input if a single number was given as the date
+ if ( ( $calendarmodel === false ) && ( $era === false ) && ( count( $datecomponents ) == 1 || count( $datecomponents ) == 2 ) && ( intval( end( $datecomponents ) ) >= 100000 ) ) {
+ $calendarmodel = 'JD';
+ }
+
+ $components = new Components(
+ [
+ 'value' => $userValue,
+ 'datecomponents' => $datecomponents,
+ 'calendarmodel' => $calendarmodel,
+ 'era' => $era,
+ 'hours' => $hours,
+ 'minutes' => $minutes,
+ 'seconds' => $seconds,
+ 'microseconds' => $microseconds,
+ 'timeoffset' => $timeoffset,
+ 'timezone' => $timezone
+ ]
+ );
+
+ return $status ? $components : false;
+ }
+
+ /**
+ * Parse the given string to check if it a date/time value.
+ * The function sets the provided call-by-ref values to the respective
+ * values. If errors are encountered, they are added to the objects
+ * error list and false is returned. Otherwise, true is returned.
+ *
+ * @todo This method in principle allows date parsing to be internationalized
+ * further.
+ *
+ * @param $string string input time representation, e.g. "12 May 2007 13:45:23-3:30"
+ * @param $datecomponents array of strings that might belong to the specification of a date
+ * @param $calendarmodesl string if model was set in input, otherwise false
+ * @param $era string '+' or '-' if provided, otherwise false
+ * @param $hours integer set to a value between 0 and 24
+ * @param $minutes integer set to a value between 0 and 59
+ * @param $seconds integer set to a value between 0 and 59, or false if not given
+ * @param $timeoffset double set to a value for time offset (e.g. 3.5), or false if not given
+ *
+ * @return boolean stating if the parsing succeeded
+ */
+ private function parseDateString( $string, &$datecomponents, &$calendarmodel, &$era, &$hours, &$minutes, &$seconds, &$microseconds, &$timeoffset, &$timezone ) {
+
+ $calendarmodel = $timezoneoffset = $era = $ampm = false;
+ $hours = $minutes = $seconds = $microseconds = $timeoffset = $timezone = false;
+
+ // Fetch possible "America/Argentina/Mendoza"
+ $timzoneIdentifier = substr( $string, strrpos( $string, ' ' ) + 1 );
+
+ if ( Timezone::isValid( $timzoneIdentifier ) ) {
+ $string = str_replace( $timzoneIdentifier, '', $string );
+ $timezoneoffset = Timezone::getOffsetByAbbreviation( $timzoneIdentifier ) / 3600;
+ $timezone = Timezone::getIdByAbbreviation( $timzoneIdentifier );
+ }
+
+ // Preprocessing for supporting different date separation characters;
+ // * this does not allow localized time notations such as "10.34 pm"
+ // * this creates problems with keywords that contain "." such as "p.m."
+ // * yet "." is an essential date separation character in languages such as German
+ $parsevalue = str_replace( [ '/', '.', '&nbsp;', ',', '年', '月', '日', '時', '分' ], [ '-', ' ', ' ', ' ', ' ', ' ', ' ', ':', ' ' ], $string );
+
+ $matches = preg_split( "/([T]?[0-2]?[0-9]:[\:0-9]+[+\-]?[0-2]?[0-9\:]+|[\p{L}]+|[0-9]+|[ ])/u", $parsevalue, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
+ $datecomponents = [];
+ $unclearparts = [];
+
+ // Used for looking back; numbers are days/months/years by default but
+ // may be re-interpreted if certain further symbols are found
+ $matchisnumber = false;
+
+ // Used for ensuring that date parts are in one block
+ $matchisdate = false;
+
+ foreach ( $matches as $match ) {
+ $prevmatchwasnumber = $matchisnumber;
+ $prevmatchwasdate = $matchisdate;
+ $matchisnumber = $matchisdate = false;
+
+ if ( $match == ' ' ) {
+ $matchisdate = $prevmatchwasdate; // spaces in dates do not end the date
+ } elseif ( $match == '-' ) { // can only occur separately between date components
+ $datecomponents[] = $match; // we check later if this makes sense
+ $matchisdate = true;
+ } elseif ( is_numeric( $match ) &&
+ ( $prevmatchwasdate || count( $datecomponents ) == 0 ) ) {
+ $datecomponents[] = $match;
+ $matchisnumber = true;
+ $matchisdate = true;
+ } elseif ( $era === false && in_array( $match, [ 'AD', 'CE' ] ) ) {
+ $era = '+';
+ } elseif ( $era === false && in_array( $match, [ 'BC', 'BCE' ] ) ) {
+ $era = '-';
+ } elseif ( $calendarmodel === false && in_array( $match, [ 'Gr', 'GR' , 'He', 'Jl', 'JL', 'MJD', 'JD', 'OS' ] ) ) {
+ $calendarmodel = $match;
+ } elseif ( $ampm === false && ( strtolower( $match ) === 'am' || strtolower( $match ) === 'pm' ) ) {
+ $ampm = strtolower( $match );
+ } elseif ( $hours === false && self::parseTimeString( $match, $hours, $minutes, $seconds, $timeoffset ) ) {
+ // nothing to do
+ } elseif ( $hours !== false && $timezoneoffset === false && Timezone::isValid( $match ) ) {
+ // only accept timezone if time has already been set
+ $timezoneoffset = Timezone::getOffsetByAbbreviation( $match ) / 3600;
+ $timezone = Timezone::getIdByAbbreviation( $match );
+ } elseif ( $prevmatchwasnumber && $hours === false && $timezoneoffset === false &&
+ Timezone::isMilitary( $match ) &&
+ self::parseMilTimeString( end( $datecomponents ), $hours, $minutes, $seconds ) ) {
+ // military timezone notation is found after a number -> re-interpret the number as military time
+ array_pop( $datecomponents );
+ $timezoneoffset = Timezone::getOffsetByAbbreviation( $match ) / 3600;
+ $timezone = Timezone::getIdByAbbreviation( $match );
+ } elseif ( ( $prevmatchwasdate || count( $datecomponents ) == 0 ) &&
+ $this->parseMonthString( $match, $monthname ) ) {
+ $datecomponents[] = $monthname;
+ $matchisdate = true;
+ } elseif ( $prevmatchwasnumber && $prevmatchwasdate && in_array( $match, [ 'st', 'nd', 'rd', 'th' ] ) ) {
+ $datecomponents[] = 'd' . strval( array_pop( $datecomponents ) ); // must be a day; add standard marker
+ $matchisdate = true;
+ } elseif ( is_string( $match ) ) {
+ $microseconds = $match;
+ } else {
+ $unclearparts[] = $match;
+ }
+ }
+
+ // $this->debug( $datecomponents, $calendarmodel, $era, $hours, $minutes, $seconds, $microseconds, $timeoffset, $timezone );
+
+ // Abort if we found unclear or over-specific information:
+ if ( count( $unclearparts ) != 0 ) {
+ $this->errors[] = [ 'smw-datavalue-time-invalid-values', $this->userValue, implode( ', ', $unclearparts ) ];
+ return false;
+ }
+
+ if ( ( $timezoneoffset !== false && $timeoffset !== false ) ) {
+ $this->errors[] = [ 'smw-datavalue-time-invalid-offset-zone-usage', $this->userValue ];
+ return false;
+ }
+
+ if ( ( $timezoneoffset !== false && $timeoffset !== false ) ) {
+ $this->errors[] = [ 'smw-datavalue-time-invalid-offset-zone-usage', $this->userValue ];
+ return false;
+ }
+
+ $timeoffset = $timeoffset + $timezoneoffset;
+
+ // Check if the a.m. and p.m. information is meaningful
+ // Note: the == 0 check subsumes $hours===false
+ if ( $ampm !== false && ( $hours > 12 || $hours == 0 ) ) {
+ $this->errors[] = [ 'smw-datavalue-time-invalid-ampm', $this->userValue, $hours ];
+ return false;
+ } elseif ( $ampm == 'am' && $hours == 12 ) {
+ $hours = 0;
+ } elseif ( $ampm == 'pm' && $hours < 12 ) {
+ $hours += 12;
+ }
+
+ return true;
+ }
+
+ /**
+ * Parse the given string to check if it encodes an international time.
+ * If successful, the function sets the provided call-by-ref values to
+ * the respective numbers and returns true. Otherwise, it returns
+ * false and does not set any values.
+ *
+ * @param $string string input time representation, e.g. "13:45:23-3:30"
+ * @param $hours integer between 0 and 24
+ * @param $minutes integer between 0 and 59
+ * @param $seconds integer between 0 and 59, or false if not given
+ * @param $timeoffset double for time offset (e.g. 3.5), or false if not given
+ *
+ * @return boolean stating if the parsing succeeded
+ */
+ private static function parseTimeString( $string, &$hours, &$minutes, &$seconds, &$timeoffset ) {
+
+ if ( !preg_match( "/^[T]?([0-2]?[0-9]):([0-5][0-9])(:[0-5][0-9])?(([+\-][0-2]?[0-9])(:(30|00))?)?$/u", $string, $match ) ) {
+ return false;
+ } else {
+ $nhours = intval( $match[1] );
+ $nminutes = $match[2] ? intval( $match[2] ) : false;
+
+ if ( ( count( $match ) > 3 ) && ( $match[3] !== '' ) ) {
+ $nseconds = intval( substr( $match[3], 1 ) );
+ } else {
+ $nseconds = false;
+ }
+
+ if ( ( $nhours < 25 ) && ( ( $nhours < 24 ) || ( $nminutes + $nseconds == 0 ) ) ) {
+ $hours = $nhours;
+ $minutes = $nminutes;
+ $seconds = $nseconds;
+ if ( ( count( $match ) > 5 ) && ( $match[5] !== '' ) ) {
+ $timeoffset = intval( $match[5] );
+ if ( ( count( $match ) > 7 ) && ( $match[7] == '30' ) ) {
+ $timeoffset += 0.5;
+ }
+ } else {
+ $timeoffset = false;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse the given string to check if it encodes a "military time".
+ * If successful, the function sets the provided call-by-ref values to
+ * the respective numbers and returns true. Otherwise, it returns
+ * false and does not set any values.
+ *
+ * @param $string string input time representation, e.g. "134523"
+ * @param $hours integer between 0 and 24
+ * @param $minutes integer between 0 and 59
+ * @param $seconds integer between 0 and 59, or false if not given
+ *
+ * @return boolean stating if the parsing succeeded
+ */
+ private static function parseMilTimeString( $string, &$hours, &$minutes, &$seconds ) {
+
+ if ( !preg_match( "/^([0-2][0-9])([0-5][0-9])([0-5][0-9])?$/u", $string, $match ) ) {
+ return false;
+ } else {
+ $nhours = intval( $match[1] );
+ $nminutes = $match[2] ? intval( $match[2] ) : false;
+ $nseconds = ( ( count( $match ) > 3 ) && $match[3] ) ? intval( $match[3] ) : false;
+
+ if ( ( $nhours < 25 ) && ( ( $nhours < 24 ) || ( $nminutes + $nseconds == 0 ) ) ) {
+ $hours = $nhours;
+ $minutes = $nminutes;
+ $seconds = $nseconds;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse the given string to check if it refers to the string name ot
+ * abbreviation of a month name. If yes, it is replaced by a normalized
+ * month name (placed in the call-by-ref parameter) and true is
+ * returned. Otherwise, false is returned and $monthname is not changed.
+ *
+ * @param $string string month name or abbreviation to parse
+ * @param $monthname string with standard 3-letter English month abbreviation
+ *
+ * @return boolean stating whether a month was found
+ */
+ private function parseMonthString( $string, &$monthname ) {
+
+ // takes precedence over English month names!
+ $monthnum = Localizer::getInstance()->getLang( $this->languageCode )->findMonthNumberByLabel( $string );
+
+ if ( $monthnum !== false ) {
+ $monthnum -= 1;
+ } else {
+ $monthnum = array_search( $string, Components::$months ); // check English names
+ }
+
+ if ( $monthnum !== false ) {
+ $monthname = Components::$monthsShort[$monthnum];
+ return true;
+ } elseif ( array_search( $string, Components::$monthsShort ) !== false ) {
+ $monthname = $string;
+ return true;
+ }
+
+ return false;
+ }
+
+ private function debug( $datecomponents, $calendarmodel, $era, $hours, $minutes, $seconds, $microseconds, $timeoffset, $timezone ) {
+ //print "\n\n Results \n\n";
+ //debug_zval_dump( $datecomponents );
+ //print "\ncalendarmodel: $calendarmodel \ntimezoneoffset: $timezoneoffset \nera: $era \nampm: $ampm \nh: $hours \nm: $minutes \ns:$seconds \ntimeoffset: $timeoffset \n";
+ //debug_zval_dump( $unclearparts );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ValueParser.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ValueParser.php
new file mode 100644
index 00000000..439b7ddf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueParsers/ValueParser.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace SMW\DataValues\ValueParsers;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface ValueParser {
+
+ /**
+ * @since 2.2
+ *
+ * @param mixed $value
+ *
+ * @return array|null
+ */
+ public function parse( $value );
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getErrors();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/AllowsListConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/AllowsListConstraintValueValidator.php
new file mode 100644
index 00000000..80bde017
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/AllowsListConstraintValueValidator.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+use SMW\ApplicationFactory;
+use SMW\DataValues\ValueParsers\AllowsListValueParser;
+use SMW\PropertySpecificationLookup;
+use SMW\Message;
+use SMWDataValue as DataValue;
+use SMWNumberValue as NumberValue;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsListConstraintValueValidator implements ConstraintValueValidator {
+
+ /**
+ * @var AllowsListValueParser
+ */
+ private $allowsListValueParser;
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var boolean
+ */
+ private $hasConstraintViolation = false;
+
+ /**
+ * @var string
+ */
+ private $errorMsg = '';
+
+ /**
+ * @since 2.4
+ *
+ * @param AllowsListValueParser $allowsListValueParser
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( AllowsListValueParser $allowsListValueParser, PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->allowsListValueParser = $allowsListValueParser;
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function hasConstraintViolation() {
+ return $this->hasConstraintViolation;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function validate( $dataValue ) {
+
+ $this->hasConstraintViolation = false;
+ $this->errorMsg = 'smw-datavalue-constraint-error-allows-value-list';
+
+ if ( !$dataValue instanceof DataValue || $dataValue->getProperty() === null ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $property = $dataValue->getProperty();
+
+ $allowedValues = $this->propertySpecificationLookup->getAllowedValues(
+ $property
+ );
+
+ $allowedListValues = $this->propertySpecificationLookup->getAllowedListValues(
+ $property
+ );
+
+ if ( $allowedValues === [] && $allowedListValues === [] ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $allowedValueList = [];
+
+ $isAllowed = $this->checkConstraintViolation(
+ $dataValue,
+ $allowedValues,
+ $allowedValueList
+ );
+
+
+ if ( !$isAllowed ) {
+ foreach ( $allowedListValues as $dataItem ) {
+ $list = $this->allowsListValueParser->parse( $dataItem->getString() );
+
+ // Combine different lists into one
+ if ( is_array( $list ) ) {
+ $allowedValues = array_merge( $allowedValues, $list );
+ }
+ }
+
+ // On assignments like [Foo => Foo] (* Foo) or [Foo => Bar] (* Foo|Bar)
+ // use the key as comparison entity
+ $allowedValues = array_keys( $allowedValues );
+ } else {
+ return;
+ }
+
+ $isAllowed = $this->checkConstraintViolation(
+ $dataValue,
+ $allowedValues,
+ $allowedValueList
+ );
+
+ if ( $isAllowed === true ) {
+ return;
+ }
+
+ $count = count( $allowedValueList );
+
+ // Only the first 10 values otherwise the list may become too long
+ $allowedValueList = implode( ', ', array_slice(
+ array_keys( $allowedValueList ), 0 , 10 )
+ );
+
+ $allowedValueList = str_replace( [ '>', '<' ], [ '%3C', '%3E' ], $allowedValueList );
+
+ $dataValue->addErrorMsg(
+ [
+ $this->errorMsg,
+ $dataValue->getWikiValue(),
+ $allowedValueList . ( $count > 10 ? ', ...' : '' ),
+ $property->getLabel()
+ ],
+ Message::PARSE
+ );
+
+ $this->hasConstraintViolation = true;
+ }
+
+ private function checkConstraintViolation( $dataValue, $allowedValues, &$allowedValueList ) {
+
+ if ( !is_array( $allowedValues ) ) {
+ return true;
+ }
+
+ $hash = $dataValue->getDataItem()->getHash();
+ $value = $dataValue->getWikiValue();
+
+ $testDataValue = ApplicationFactory::getInstance()->getDataValueFactory()->newTypeIDValue(
+ $dataValue->getTypeID()
+ );
+
+ $isAllowed = false;
+
+ // Track a range related constraint which can be used as single
+ // `[[Allows value::>0]]` assignment or as conjunctive compound as in
+ // `[[Allows value::>0]] [[Allows value::<100]]` and can appear in any
+ // order
+ $range = null;
+
+ foreach ( $allowedValues as $allowedValue ) {
+
+ if ( is_string( $allowedValue ) ) {
+ $allowedValue = new DIBlob( $allowedValue );
+ }
+
+ if ( !$allowedValue instanceof DIBlob ) {
+ continue;
+ }
+
+ if ( $testDataValue instanceof NumberValue ) {
+
+ // Check [[Allows value::>0]]
+ if ( $this->check_range( '>', $value, $allowedValue, $range, $isAllowed, $allowedValueList ) ) {
+ continue;
+ }
+
+ // Check [[Allows value::<100]]
+ if ( $this->check_range( '<', $value, $allowedValue, $range, $isAllowed, $allowedValueList ) ) {
+ continue;
+ }
+
+ // Check [[Allows value::1...100]]
+ if ( $this->check_bounds( $value, $allowedValue, $isAllowed, $allowedValueList ) ) {
+ break;
+ }
+ }
+
+ // For a time based range one could use the JD date and simply apply
+ // a >, < comparison on something like `[[Allows value::>1970]]
+ // [[Allows value::<31.12.2100]]`
+
+ // String range based constraints seems to make not much sense for
+ // something like `[[Allows value::>abc]] [[Allows value::<def]]`
+
+ $testDataValue->setUserValue( $allowedValue->getString() );
+
+ if ( $hash === $testDataValue->getDataItem()->getHash() ) {
+ $isAllowed = true;
+ break;
+ } else {
+ // Filter dups
+ $allowedValueList[$allowedValue->getString()] = true;
+ }
+ }
+
+ return $isAllowed;
+ }
+
+ private function check_range( $exp, $value, $allowedValue, &$range, &$isAllowed, &$allowedValueList ) {
+
+ $v = $allowedValue->getString();
+
+ // If a previous range comparison failed then bail-out!
+ if ( $v{0} === $exp && ( $range === null || $range ) ) {
+ $v = intval( trim( substr( $v, 1 ) ) );
+
+ if ( $exp === '>' && $value > $v ) {
+ $isAllowed = true;
+ } elseif ( $exp === '<' && $value < $v ) {
+ $isAllowed = true;
+ } else {
+ $isAllowed = false;
+ $range = false;
+ }
+
+ if ( $range === false ) {
+ $allowedValueList[$allowedValue->getString()] = true;
+ }
+
+ return true;
+ }
+
+ $this->errorMsg = 'smw-datavalue-constraint-error-allows-value-range';
+
+ return false;
+ }
+
+ private function check_bounds( $value, $allowedValue, &$isAllowed, &$allowedValueList ) {
+
+ $v = $allowedValue->getString();
+
+ if ( strpos( $v, '...' ) === false ) {
+ return false;
+ }
+
+ list( $lower, $upper ) = explode( '...', $v );
+
+ if ( $value >= intval( $lower ) && $value <= intval( $upper ) ) {
+ return $isAllowed = true;
+ } else {
+ $allowedValueList[$allowedValue->getString()] = true;
+ }
+
+ $this->errorMsg = 'smw-datavalue-constraint-error-allows-value-range';
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/CompoundConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/CompoundConstraintValueValidator.php
new file mode 100644
index 00000000..ec412a02
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/CompoundConstraintValueValidator.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+use RuntimeException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CompoundConstraintValueValidator implements ConstraintValueValidator {
+
+ /**
+ * @var boolean
+ */
+ private $hasConstraintViolation = false;
+
+ /**
+ * @var array
+ */
+ private $constraintValueValidators = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param ConstraintValueValidator $constraintValueValidator
+ */
+ public function registerConstraintValueValidator( ConstraintValueValidator $constraintValueValidator ) {
+ $this->constraintValueValidators[] = $constraintValueValidator;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function hasConstraintViolation() {
+ return $this->hasConstraintViolation;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function validate( $dataValue ) {
+
+ $this->hasConstraintViolation = false;
+
+ if ( $this->constraintValueValidators === [] ) {
+ throw new RuntimeException( "Missing a registered ConstraintValueValidator" );
+ }
+
+ // Any constraint violation by a ConstraintValueValidator registered will
+ // force an immediate halt without checking any other possible constraint
+ foreach ( $this->constraintValueValidators as $constraintValueValidator ) {
+ $constraintValueValidator->validate( $dataValue );
+
+ if ( $constraintValueValidator->hasConstraintViolation() ) {
+ $this->hasConstraintViolation = true;
+ break;
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/ConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/ConstraintValueValidator.php
new file mode 100644
index 00000000..bddb7901
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/ConstraintValueValidator.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+interface ConstraintValueValidator {
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ */
+ public function validate( $dataValue );
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function hasConstraintViolation();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PatternConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PatternConstraintValueValidator.php
new file mode 100644
index 00000000..d96e2656
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PatternConstraintValueValidator.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+use SMW\ApplicationFactory;
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+use SMWDataValue as DataValue;
+
+/**
+ * To support regular expressions in connection with the `Allows pattern`
+ * property.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PatternConstraintValueValidator implements ConstraintValueValidator {
+
+ /**
+ * @var AllowsPatternContentParser
+ */
+ private $allowsPatternValueParser;
+
+ /**
+ * @var boolean
+ */
+ private $hasConstraintViolation = false;
+
+ /**
+ * @since 2.4
+ *
+ * @param AllowsPatternValueParser $allowsPatternValueParser
+ */
+ public function __construct( AllowsPatternValueParser $allowsPatternValueParser ) {
+ $this->allowsPatternValueParser = $allowsPatternValueParser;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function hasConstraintViolation() {
+ return $this->hasConstraintViolation;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function validate( $dataValue ) {
+
+ $this->hasConstraintViolation = false;
+
+ if (
+ !$dataValue instanceof DataValue ||
+ $dataValue->getProperty() === null ||
+ !$dataValue->isEnabledFeature( SMW_DV_PVAP ) ) {
+ return $this->hasConstraintViolation;
+ }
+
+ if ( ( $reference = ApplicationFactory::getInstance()->getPropertySpecificationLookup()->getAllowedPatternBy( $dataValue->getProperty() ) ) === '' ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $content = $this->allowsPatternValueParser->parse(
+ $reference
+ );
+
+ if ( !$content ) {
+ return $this->hasConstraintViolation;
+ }
+
+ // Prevent a possible remote code execution vulnerability in connection
+ // with PCRE
+ $pattern = str_replace( [ '/e' ], [ '' ], trim( $content ) );
+
+ $this->doPregMatch(
+ $pattern,
+ $dataValue,
+ $reference
+ );
+ }
+
+ private function doPregMatch( $pattern, $dataValue, $reference ) {
+
+ // Convert escaping as in /\d{4}
+ $pattern = str_replace( "/\\", "\\", $pattern );
+
+ // Add a mandatory backslash
+ if ( $pattern !== '' && $pattern{0} !== '/' ) {
+ $pattern = '/' . $pattern;
+ }
+
+ if ( substr( $pattern, -1 ) !== '/' ) {
+ $pattern = $pattern . '/';
+ }
+
+ // @to suppress any errors caused by an invalid regex, the user should
+ // test the expression before making it available
+ if ( !@preg_match( $pattern, $dataValue->getDataItem()->getSortKey() ) ) {
+ $dataValue->addErrorMsg(
+ [
+ 'smw-datavalue-allows-pattern-mismatch',
+ $dataValue->getWikiValue(),
+ $reference
+ ]
+ );
+
+ $this->hasConstraintViolation = true;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PropertySpecificationConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PropertySpecificationConstraintValueValidator.php
new file mode 100644
index 00000000..b58903d9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/PropertySpecificationConstraintValueValidator.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+use SMWDataValue as DataValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationConstraintValueValidator implements ConstraintValueValidator {
+
+ /**
+ * @var boolean
+ */
+ private $hasConstraintViolation = false;
+
+ /**
+ * @var array
+ */
+ private static $inMemoryLabelToLanguageTracer = [];
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function hasConstraintViolation() {
+ return $this->hasConstraintViolation;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function validate( $dataValue ) {
+
+ $this->hasConstraintViolation = false;
+
+ if (
+ !$dataValue instanceof DataValue ||
+ $dataValue->getProperty() === null ||
+ $dataValue->getContextPage() === null ||
+ $dataValue->getContextPage()->getNamespace() !== SMW_NS_PROPERTY ) {
+ return $this->hasConstraintViolation;
+ }
+
+ if ( $dataValue->getProperty()->getKey() === '_PPLB' ) {
+ return $this->doValidateCodifiedPreferredPropertyLabelConstraints( $dataValue );
+ }
+ }
+
+ private function doValidateCodifiedPreferredPropertyLabelConstraints( $dataValue ) {
+
+ // Annotated but not enabled
+ if ( !$dataValue->isEnabledFeature( SMW_DV_PPLB ) ) {
+ return $dataValue->addErrorMsg(
+ [
+ 'smw-datavalue-feature-not-supported',
+ 'SMW_DV_PPLB'
+ ]
+ );
+ }
+
+ $value = $dataValue->toArray();
+ $dbKey = $dataValue->getContextPage()->getDBKey();
+
+ // Language has been already assigned!
+ if ( ( $isKnownBy = $this->isKnownByLabelAndLanguage( $value, $dbKey ) ) !== false ) {
+ $dataValue->addErrorMsg(
+ [
+ 'smw-property-preferred-label-language-combination-exists',
+ $value['_TEXT'],
+ $value['_LCODE'],
+ $isKnownBy
+ ]
+ );
+ }
+ }
+
+ private function isKnownByLabelAndLanguage( $value, $dbkey ) {
+
+ $lang = isset( $value['_LCODE'] ) ? $value['_LCODE'] : false;
+
+ if ( !isset( self::$inMemoryLabelToLanguageTracer[$dbkey] ) ) {
+ self::$inMemoryLabelToLanguageTracer[$dbkey] = [];
+ }
+
+ if ( $lang && !isset( self::$inMemoryLabelToLanguageTracer[$dbkey][$lang] ) ) {
+ self::$inMemoryLabelToLanguageTracer[$dbkey][$lang] = $value['_TEXT'];
+ }
+
+ if ( $lang && self::$inMemoryLabelToLanguageTracer[$dbkey][$lang] !== $value['_TEXT'] ) {
+ return self::$inMemoryLabelToLanguageTracer[$dbkey][$lang];
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/UniquenessConstraintValueValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/UniquenessConstraintValueValidator.php
new file mode 100644
index 00000000..65ace8e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/DataValues/ValueValidators/UniquenessConstraintValueValidator.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace SMW\DataValues\ValueValidators;
+
+use SMW\PropertySpecificationLookup;
+use SMW\DIWikiPage;
+use SMWDataValue as DataValue;
+use SMW\RequestOptions;
+use SMW\Store;
+
+/**
+ * @private
+ *
+ * Only allow values that are unique where uniqueness is establised for the first (
+ * in terms of time which also entails that after a full rebuild the first value
+ * found is being categorised as established value) value assigned to a property
+ * (that requires this trait) and any value that compares to an establised
+ * value with the same literal representation is being identified as violating the
+ * uniqueness constraint.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintValueValidator implements ConstraintValueValidator {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var boolean
+ */
+ private $hasConstraintViolation = false;
+
+ /**
+ * Tracks annotations for the current context to verify that a subject only
+ * contains unique assignments.
+ *
+ * @var array
+ */
+ private static $annotations = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( Store $store, PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->store = $store;
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function hasConstraintViolation() {
+ return $this->hasConstraintViolation;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function clear() {
+ self::$annotations = [];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function validate( $dataValue ) {
+
+ $this->hasConstraintViolation = false;
+
+ if ( !$this->canValidate( $dataValue ) ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $property = $dataValue->getProperty();
+
+ if ( !$this->propertySpecificationLookup->hasUniquenessConstraint( $property ) ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $contextPage = $dataValue->getContextPage();
+
+ // Exclude the current page from the result match to check whether another
+ // page matches the condition and if so then the value can no longer be
+ // assigned and is not unique
+ $requestOptions = new RequestOptions();
+
+ $requestOptions->addExtraCondition( function( $store, $query, $alias ) use( $contextPage ) {
+ return $query->neq( "$alias.s_id", $store->getObjectIds()->getId( $contextPage ) );
+ }
+ );
+
+ $requestOptions->setLimit( 2 );
+ $count = 0;
+
+ if ( !$this->hasAnnotation( $dataValue ) ) {
+ $entityValueUniquenessConstraintChecker = $this->store->service( 'EntityValueUniquenessConstraintChecker' );
+
+ $res = $entityValueUniquenessConstraintChecker->checkConstraint(
+ $property,
+ $dataValue->getDataItem(),
+ $requestOptions
+ );
+
+ $count = count( $res );
+ }
+
+ // Check whether the current page has any other annotation for the
+ // same property
+ if ( $count < 1 && $this->isRegistered( $dataValue ) ) {
+ $dataValue->addErrorMsg(
+ [
+ 'smw-datavalue-uniqueness-constraint-isknown',
+ $property->getLabel(),
+ $contextPage->getTitle()->getPrefixedText(),
+ $dataValue->getWikiValue()
+ ]
+ );
+
+ $this->hasConstraintViolation = true;
+ }
+
+ // Has the page different values for the same property?
+ if ( $count < 1 ) {
+ return $this->hasConstraintViolation;
+ }
+
+ $this->hasConstraintViolation = true;
+
+ foreach ( $res as $dataItem ) {
+ $val = $dataValue->isValid() ? $dataValue->getWikiValue() : '...';
+ $text = '';
+
+ if ( $dataItem !== null && ( $title = $dataItem->getTitle() ) !== null ) {
+ $text = $title->getPrefixedText();
+ }
+
+ $dataValue->addErrorMsg(
+ [
+ 'smw-datavalue-uniqueness-constraint-error',
+ $property->getLabel(),
+ $val,
+ $text
+ ]
+ );
+ }
+
+ return $this->hasConstraintViolation;
+ }
+
+ private function canValidate( $dataValue ) {
+
+ if ( !$dataValue->isEnabledFeature( SMW_DV_PVUC ) || !$dataValue instanceof DataValue ) {
+ return false;
+ }
+
+ return $dataValue->getContextPage() !== null && $dataValue->getProperty() !== null;
+ }
+
+ private function isRegistered( $dataValue ) {
+
+ $contextPage = $dataValue->getContextPage();
+ $dataItem = $dataValue->getDataItem();
+ $property = $dataValue->getProperty();
+
+ $valueHash = md5( $property->getKey() . $dataItem->getHash() );
+ $key = $property->getKey();
+ $hash = $contextPage->getHash();
+
+ if ( isset( self::$annotations[$hash][$key] ) && self::$annotations[$hash][$key] !== $valueHash ) {
+ return true;
+ } else {
+ self::$annotations[$hash][$key] = $valueHash;
+ }
+
+ return false;
+ }
+
+ private function hasAnnotation( $dataValue ) {
+
+ $key = $dataValue->getProperty()->getKey();
+ $hash = $dataValue->getContextPage()->getHash();
+
+ return isset( self::$annotations[$hash][$key] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Defines.php b/www/wiki/extensions/SemanticMediaWiki/src/Defines.php
new file mode 100644
index 00000000..7a93dcab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Defines.php
@@ -0,0 +1,300 @@
+<?php
+/**
+ * Constants relevant to Semantic MediaWiki
+ *
+ */
+
+/**
+ * @ingroup Constants
+ * @ingroup SMW
+ */
+
+/**@{
+ * SMW\ResultPrinter related constants that define
+ * how/if headers should be displayed
+ */
+define( 'SMW_HEADERS_SHOW', 2 );
+define( 'SMW_HEADERS_PLAIN', 1 );
+define( 'SMW_HEADERS_HIDE', 0 ); // Used to be "false" hence use "0" to support extensions that still assume this.
+/**@}*/
+
+/**@{
+ * Constants for denoting output modes in many functions: HTML or Wiki?
+ * "File" is for printing results into stand-alone files (e.g. building RSS)
+ * and should be treated like HTML when building single strings. Only query
+ * printers tend to have special handling for that.
+ */
+define( 'SMW_OUTPUT_HTML', 1 );
+define( 'SMW_OUTPUT_WIKI', 2 );
+define( 'SMW_OUTPUT_FILE', 3 );
+define( 'SMW_OUTPUT_RAW', 4 );
+/**@}*/
+
+/**@{
+ * Constants for displaying the factbox
+ */
+define( 'SMW_FACTBOX_HIDDEN', 1 );
+define( 'SMW_FACTBOX_SPECIAL', 2 );
+define( 'SMW_FACTBOX_NONEMPTY', 3 );
+define( 'SMW_FACTBOX_SHOWN', 5 );
+
+define( 'SMW_FACTBOX_CACHE', 16 );
+define( 'SMW_FACTBOX_PURGE_REFRESH', 32 );
+define( 'SMW_FACTBOX_DISPLAY_SUBOBJECT', 64 );
+
+/**@}*/
+
+/**@{
+ * Constants for regulating equality reasoning
+ */
+define( 'SMW_EQ_NONE', 0 );
+define( 'SMW_EQ_SOME', 1 );
+define( 'SMW_EQ_FULL', 2 );
+/**@}*/
+
+/**@{
+ * Flags to classify available query descriptions,
+ * used to enable/disable certain features
+ */
+define( 'SMW_PROPERTY_QUERY', 1 ); // [[some property::...]]
+define( 'SMW_CATEGORY_QUERY', 2 ); // [[Category:...]]
+define( 'SMW_CONCEPT_QUERY', 4 ); // [[Concept:...]]
+define( 'SMW_NAMESPACE_QUERY', 8 ); // [[User:+]] etc.
+define( 'SMW_CONJUNCTION_QUERY', 16 ); // any conjunctions
+define( 'SMW_DISJUNCTION_QUERY', 32 ); // any disjunctions (OR, ||)
+define( 'SMW_ANY_QUERY', 0xFFFFFFFF ); // subsumes all other options
+/**@}*/
+
+/**@{
+ * Constants for defining which concepts to show only if cached
+ */
+define( 'CONCEPT_CACHE_ALL', 4 ); // show concept elements anywhere only if cached
+define( 'CONCEPT_CACHE_HARD', 1 ); // show without cache if concept is not harder than permitted inline queries
+define( 'CONCEPT_CACHE_NONE', 0 ); // show all concepts even without any cache
+/**@}*/
+
+/**@{
+ * Constants for identifying javascripts as used in SMWOutputs
+ */
+/// @deprecated Use module 'ext.smw.tooltips', see SMW_Ouptuts.php. Vanishes in SMW 1.7 at the latest.
+define( 'SMW_HEADER_TOOLTIP', 2 );
+/// @deprecated Module removed. Vanishes in SMW 1.7 at the latest.
+define( 'SMW_HEADER_SORTTABLE', 3 );
+/// @deprecated Use module 'ext.smw.style', see SMW_Ouptuts.php. Vanishes in SMW 1.7 at the latest.
+define( 'SMW_HEADER_STYLE', 4 );
+/**@}*/
+
+/**@{
+ * Comparators for datavalues
+ */
+define( 'SMW_CMP_EQ', 1 ); // Matches only datavalues that are equal to the given value.
+define( 'SMW_CMP_LEQ', 2 ); // Matches only datavalues that are less or equal than the given value.
+define( 'SMW_CMP_GEQ', 3 ); // Matches only datavalues that are greater or equal to the given value.
+define( 'SMW_CMP_NEQ', 4 ); // Matches only datavalues that are unequal to the given value.
+define( 'SMW_CMP_LIKE', 5 ); // Matches only datavalues that are LIKE the given value.
+define( 'SMW_CMP_NLKE', 6 ); // Matches only datavalues that are not LIKE the given value.
+define( 'SMW_CMP_LESS', 7 ); // Matches only datavalues that are less than the given value.
+define( 'SMW_CMP_GRTR', 8 ); // Matches only datavalues that are greater than the given value.
+define( 'SMW_CMP_PRIM_LIKE', 20 ); // Native LIKE matches (in disregards of an existing full-text index)
+define( 'SMW_CMP_PRIM_NLKE', 21 ); // Native NLIKE matches (in disregards of an existing full-text index)
+define( 'SMW_CMP_IN', 22 ); // Short-cut for ~* ... *
+define( 'SMW_CMP_PHRASE', 23 ); // Short-cut for a phrase match ~" ... " mostly for a full-text context
+define( 'SMW_CMP_NOT', 24 ); // Short-cut for ~! ... * ostly for a full-text context
+/**@}*/
+
+/**@{
+ * Constants for date formats (using binary encoding of nine bits:
+ * 3 positions x 3 interpretations)
+ */
+define( 'SMW_MDY', 785 ); // Month-Day-Year
+define( 'SMW_DMY', 673 ); // Day-Month-Year
+define( 'SMW_YMD', 610 ); // Year-Month-Day
+define( 'SMW_YDM', 596 ); // Year-Day-Month
+define( 'SMW_MY', 97 ); // Month-Year
+define( 'SMW_YM', 76 ); // Year-Month
+define( 'SMW_Y', 9 ); // Year
+define( 'SMW_YEAR', 1 ); // an entered digit can be a year
+define( 'SMW_DAY', 2 ); // an entered digit can be a year
+define( 'SMW_MONTH', 4 ); // an entered digit can be a month
+define( 'SMW_DAY_MONTH_YEAR', 7 ); // an entered digit can be a day, month or year
+define( 'SMW_DAY_YEAR', 3 ); // an entered digit can be either a month or a year
+/**@}*/
+
+/**@{
+ * Constants for date/time precision
+ */
+define( 'SMW_PREC_Y', 0 );
+define( 'SMW_PREC_YM', 1 );
+define( 'SMW_PREC_YMD', 2 );
+define( 'SMW_PREC_YMDT', 3 );
+define( 'SMW_PREC_YMDTZ', 4 ); // with time zone
+/**@}*/
+
+/**@{
+ * Constants for SPARQL supported features (mostly SPARQL 1.1) because we are unable
+ * to verify against the REST API whether a feature is supported or not
+ */
+define( 'SMW_SPARQL_QF_NONE', 0 ); // does not support any features
+define( 'SMW_SPARQL_QF_REDI', 2 ); // support for inverse property paths to find redirects
+define( 'SMW_SPARQL_QF_SUBP', 4 ); // support for rdfs:subPropertyOf*
+define( 'SMW_SPARQL_QF_SUBC', 8 ); // support for rdfs:subClassOf*
+define( 'SMW_SPARQL_QF_COLLATION', 16 ); // support for use of $smwgEntityCollation
+define( 'SMW_SPARQL_QF_NOCASE', 32 ); // support case insensitive pattern matches
+/**@}*/
+
+/**@{
+ * Constants for ValueLookupStore
+ */
+define( 'SMW_VL_SD', 1 ); // enables ValueLookupStore::getSemanticData
+define( 'SMW_VL_PL', 2 ); // enables ValueLookupStore::getProperties
+define( 'SMW_VL_PV', 4 ); // enables ValueLookupStore::getPropertyValues
+define( 'SMW_VL_PS', 8 ); // enables ValueLookupStore::getPropertySubject
+/**@}*/
+
+/**@{
+ * Deprecated since 3.0, remove options after complete removal in 3.1
+ */
+define( 'SMW_HTTP_DEFERRED_ASYNC', true );
+define( 'SMW_HTTP_DEFERRED_SYNC_JOB', 4 );
+define( 'SMW_HTTP_DEFERRED_LAZY_JOB', 8 );
+/**@}*/
+
+/**@{
+ * Constants DV features
+ */
+define( 'SMW_DV_NONE', 0 );
+define( 'SMW_DV_PROV_REDI', 2 ); // PropertyValue to follow a property redirect target
+define( 'SMW_DV_MLTV_LCODE', 4 ); // MonolingualTextValue requires language code
+define( 'SMW_DV_NUMV_USPACE', 8 ); // Preserve spaces in unit labels
+define( 'SMW_DV_PVAP', 16 ); // Allows pattern
+define( 'SMW_DV_WPV_DTITLE', 32 ); // WikiPageValue to use an explicit display title
+define( 'SMW_DV_PROV_DTITLE', 64 ); // PropertyValue allow to find a property using the display title
+define( 'SMW_DV_PVUC', 128 ); // Declares a uniqueness constraint
+define( 'SMW_DV_TIMEV_CM', 256 ); // TimeValue to indicate calendar model
+define( 'SMW_DV_PPLB', 512 ); // Preferred property label
+define( 'SMW_DV_PROV_LHNT', 1024 ); // PropertyValue to output a hint in case of a preferred label usage
+/**@}*/
+
+/**@{
+ * Constants for Fulltext types
+ */
+define( 'SMW_FT_NONE', 0 );
+define( 'SMW_FT_BLOB', 2 ); // DataItem::TYPE_BLOB
+define( 'SMW_FT_URI', 4 ); // DataItem::TYPE_URI
+define( 'SMW_FT_WIKIPAGE', 8 ); // DataItem::TYPE_WIKIPAGE
+/**@}*/
+
+/**@{
+ * Constants for admin features
+ */
+define( 'SMW_ADM_NONE', 0 );
+define( 'SMW_ADM_REFRESH', 2 ); // RefreshStore
+define( 'SMW_ADM_DISPOSAL', 4 ); // IDDisposal
+define( 'SMW_ADM_SETUP', 8 ); // SetupStore
+define( 'SMW_ADM_PSTATS', 16 ); // Property statistics update
+define( 'SMW_ADM_FULLT', 32 ); // Fulltext update
+/**@}*/
+
+/**@{
+ * Constants for ResultPrinter
+ */
+define( 'SMW_RF_NONE', 0 );
+define( 'SMW_RF_TEMPLATE_OUTSEP', 2 ); // #2022 Enable 2.5 behaviour for template handling
+/**@}*/
+
+/**@{
+ * Constants for $smwgExperimentalFeatures
+ */
+/**@}*/
+
+/**@{
+ * Constants for $smwgFieldTypeFeatures
+ */
+define( 'SMW_FIELDT_NONE', 0 );
+define( 'SMW_FIELDT_CHAR_NOCASE', 2 ); // Using FieldType::TYPE_CHAR_NOCASE
+define( 'SMW_FIELDT_CHAR_LONG', 4 ); // Using FieldType::TYPE_CHAR_LONG
+/**@}*/
+
+/**@{
+ * Constants for $smwgQueryProfiler
+ */
+define( 'SMW_QPRFL_NONE', 0 );
+define( 'SMW_QPRFL_PARAMS', 2 ); // Support for Query parameters
+define( 'SMW_QPRFL_DUR', 4 ); // Support for Query duration
+/**@}*/
+
+/**@{
+ * Constants for $smwgBrowseFeatures
+ */
+define( 'SMW_BROWSE_NONE', 0 );
+define( 'SMW_BROWSE_TLINK', 2 ); // Support for the toolbox link
+define( 'SMW_BROWSE_SHOW_INVERSE', 4 ); // Support inverse direction
+define( 'SMW_BROWSE_SHOW_INCOMING', 8 ); // Support for incoming links
+define( 'SMW_BROWSE_SHOW_GROUP', 16 ); // Support for grouping properties
+define( 'SMW_BROWSE_SHOW_SORTKEY', 32 ); // Support for the sortkey display
+define( 'SMW_BROWSE_USE_API', 64 ); // Support for using the API as request backend
+/**@}*/
+
+/**@{
+ * Constants for $smwgParserFeatures
+ */
+define( 'SMW_PARSER_NONE', 0 );
+define( 'SMW_PARSER_STRICT', 2 ); // Support for strict mode
+define( 'SMW_PARSER_UNSTRIP', 4 ); // Support for using the StripMarkerDecoder
+define( 'SMW_PARSER_INL_ERROR', 8 ); // Support for display of inline errors
+define( 'SMW_PARSER_HID_CATS', 16 ); // Support for parsing hidden categories
+define( 'SMW_PARSER_LINV', 32 ); // Support for links in value
+define( 'SMW_PARSER_LINKS_IN_VALUES', 32 ); // Support for links in value
+/**@}*/
+
+/**@{
+ * Constants for LinksInValue features
+ */
+define( 'SMW_LINV_PCRE', 2 ); // Using the PCRE approach
+define( 'SMW_LINV_OBFU', 4 ); // Using the Obfuscator approach
+/**@}*/
+
+/**@{
+ * Constants for $smwgCategoryFeatures
+ */
+define( 'SMW_CAT_NONE', 0 );
+define( 'SMW_CAT_REDIRECT', 2 ); // Support resolving category redirects
+define( 'SMW_CAT_INSTANCE', 4 ); // Support using a category as instantiatable object
+define( 'SMW_CAT_HIERARCHY', 8 ); // Support for category hierarchies
+/**@}*/
+
+/**@{
+ * Constants for $smwgQSortFeatures
+ */
+define( 'SMW_QSORT_NONE', 0 );
+define( 'SMW_QSORT', 2 ); // General sort support
+define( 'SMW_QSORT_RANDOM', 4 ); // Random sort support
+define( 'SMW_QSORT_UNCONDITIONAL', 8 ); // Unconditional sort support
+/**@}*/
+
+/**@{
+ * Constants for $smwgRemoteReqFeatures
+ */
+define( 'SMW_REMOTE_REQ_SEND_RESPONSE', 2 ); // Remote responses are enabled
+define( 'SMW_REMOTE_REQ_SHOW_NOTE', 4 ); // Shows a note
+/**@}*/
+
+/**@{
+ * Constants for Schema groups
+ */
+define( 'SMW_SCHEMA_GROUP_FORMAT', 'schema.group.format' );
+define( 'SMW_SCHEMA_GROUP_SEARCH_FORM', 'schema.group.search.form' );
+
+/**@{
+ * Constants for Special:Ask submit method
+ */
+define( 'SMW_SASK_SUBMIT_GET', 'get' );
+define( 'SMW_SASK_SUBMIT_GET_REDIRECT', 'get.redirect' );
+define( 'SMW_SASK_SUBMIT_POST', 'post' );
+/**@}*/
+
+/**@{
+ * Constants for content types
+ */
+define( 'CONTENT_MODEL_SMW_SCHEMA', 'smw/schema' );
+/**@}*/
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DescriptionDeserializer.php
new file mode 100644
index 00000000..4230784c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DescriptionDeserializer.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use Deserializers\DispatchableDeserializer;
+use SMW\ApplicationFactory;
+use SMW\DataItemFactory;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\QueryComparator;
+use SMWDataValue as DataValue;
+
+/**
+ * @private
+ *
+ * Create an Description object based on a value string that was entered
+ * in a query. Turning inputs that a user enters in place of a value within
+ * a query string into query conditions is often a standard procedure. The
+ * processing must take comparators like "<" into account, but otherwise
+ * the normal parsing function can be used. However, there can be datatypes
+ * where processing is more complicated, e.g. if the input string contains
+ * more than one value, each of which may have comparators, as in
+ * SMWRecordValue. In this case, it makes sense to overwrite this method.
+ * Another reason to do this is to add new forms of comparators or new ways
+ * of entering query conditions.
+ *
+ * The resulting Description may or may not make use of the datavalue
+ * object that this function was called on, so it must be ensured that this
+ * value is not used elsewhere when calling this method. The function can
+ * return ThingDescription to not impose any condition, e.g. if parsing
+ * failed. Error messages of this DataValue object are propagated.
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+abstract class DescriptionDeserializer implements DispatchableDeserializer {
+
+ /**
+ * @var DescriptionFactory
+ */
+ protected $descriptionFactory;
+
+ /**
+ * @var DataItemFactory
+ */
+ protected $dataItemFactory;
+
+ /**
+ * @var array
+ */
+ protected $errors = [];
+
+ /**
+ * @var DataValue
+ */
+ protected $dataValue;
+
+ /**
+ * @since 2.5
+ *
+ * @param DescriptionFactory|null $descriptionFactory
+ * @param DataItemFactory|null $dataItemFactory
+ */
+ public function __construct( DescriptionFactory $descriptionFactory = null, DescriptionFactory $dataItemFactory = null ) {
+ $this->descriptionFactory = $descriptionFactory;
+ $this->dataItemFactory = $dataItemFactory;
+
+ if ( $this->descriptionFactory === null ) {
+ $this->descriptionFactory = ApplicationFactory::getInstance()->getQueryFactory()->newDescriptionFactory();
+ }
+
+ if ( $this->dataItemFactory === null ) {
+ $this->dataItemFactory = ApplicationFactory::getInstance()->getDataItemFactory();
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DataValue $dataValue
+ */
+ public function setDataValue( DataValue $dataValue ) {
+ $this->dataValue = $dataValue;
+ $this->errors = [];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $error
+ */
+ public function addError( $error ) {
+
+ if ( is_array( $error ) ) {
+ return $this->errors = array_merge( $this->errors, $error );
+ }
+
+ $this->errors[] = $error;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Helper function for DescriptionDeserializer::deserialize that prepares a
+ * single value string, possibly extracting comparators. $value is changed
+ * to consist only of the remaining effective value string (without the
+ * comparator).
+ *
+ * @param string $value
+ * @param string|integer $comparator
+ */
+ protected function prepareValue( &$value, &$comparator ) {
+ $comparator = QueryComparator::getInstance()->extractComparatorFromString( $value );
+
+ // [[in:lorem ipsum]] / [[Has text::in:lorem ipsum]] to be turned into a
+ // proximity match where lorem AND ipsum needs to be present in the
+ // indexed match field.
+ //
+ // For those query engines that support those search patterns!
+ if ( $comparator === SMW_CMP_IN ) {
+ $comparator = SMW_CMP_LIKE;
+
+ // Looking for something like [[in:phrase:foo]]
+ if ( strpos( $value, 'phrase:' ) !== false ) {
+ $value = str_replace( 'phrase:', '', $value );
+ $value = '"' . $value . '"';
+ }
+
+ // `in:...` is for the "busy" user to avoid adding wildcards now and
+ // then to the value string
+ $value = "*$value*";
+
+ // No property and the assumption is [[in:...]] with the expected use
+ // of the wide proximity as indicated by an additional `~`
+ if ( $this->dataValue->getProperty() === null ) {
+ $value = "~$value";
+ }
+ }
+
+ // [[not:foo bar]]
+ // For those query engines that support those text search patterns!
+ if ( $comparator === SMW_CMP_NOT ) {
+ $comparator = SMW_CMP_NLKE;
+
+ $value = str_replace( '!', '', $value );
+
+ // Opposed to `in:` which includes *, `not:` is intended to match
+ // only the exact entered term. It can be extended using *
+ // if necessary (e.g. [[Has text::not:foo*]]).
+
+ // Use as phrase to signal an exact term match for a wide proximity
+ // search
+ if ( $this->dataValue->getProperty() === null ) {
+ $value = "~\"$value\"";
+ }
+ }
+
+ // [[phrase:lorem ipsum]] to be turned into a promixity phrase_match
+ // where the entire string (incl. its order) are to be matched.
+ //
+ // For those query engines that support those search patterns!
+ if ( $comparator === SMW_CMP_PHRASE ) {
+ $comparator = SMW_CMP_LIKE;
+ $value = '"' . $value . '"';
+
+ if ( $this->dataValue->getProperty() === null ) {
+ $value = "~$value";
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializer.php
new file mode 100644
index 00000000..5d803064
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializer.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use RuntimeException;
+use SMWDataValue as DataValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class DispatchingDescriptionDeserializer {
+
+ /**
+ * @var DescriptionDeserializer[]
+ */
+ private $descriptionDeserializers = [];
+
+ /**
+ * @var DescriptionDeserializer
+ */
+ private $defaultDescriptionDeserializer = null;
+
+ /**
+ * @since 2.3
+ *
+ * @param DescriptionDeserializer $descriptionDeserializer
+ */
+ public function addDescriptionDeserializer( DescriptionDeserializer $descriptionDeserializer ) {
+ $this->descriptionDeserializers[] = $descriptionDeserializer;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DescriptionDeserializer $defaultDescriptionDeserializer
+ */
+ public function addDefaultDescriptionDeserializer( DescriptionDeserializer $defaultDescriptionDeserializer ) {
+ $this->defaultDescriptionDeserializer = $defaultDescriptionDeserializer;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DataValue $dataValue
+ *
+ * @return DescriptionDeserializer
+ * @throws RuntimeException
+ */
+ public function getDescriptionDeserializerBy( DataValue $dataValue ) {
+
+ foreach ( $this->descriptionDeserializers as $descriptionDeserializer ) {
+ if ( $descriptionDeserializer->isDeserializerFor( $dataValue ) ) {
+ $descriptionDeserializer->setDataValue( $dataValue );
+ return $descriptionDeserializer;
+ }
+ }
+
+ if ( $this->defaultDescriptionDeserializer !== null && $this->defaultDescriptionDeserializer->isDeserializerFor( $dataValue ) ) {
+ $this->defaultDescriptionDeserializer->setDataValue( $dataValue );
+ return $this->defaultDescriptionDeserializer;
+ }
+
+ throw new RuntimeException( "Missing registered DescriptionDeserializer." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializer.php
new file mode 100644
index 00000000..066953fd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializer.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use InvalidArgumentException;
+use SMW\DataValueFactory;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueDescriptionDeserializer extends DescriptionDeserializer {
+
+ /**
+ * @since 2.4
+ *
+ * {@inheritDoc}
+ */
+ public function isDeserializerFor( $serialization ) {
+ return $serialization instanceof MonolingualTextValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function deserialize( $value ) {
+
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( 'Value needs to be a string' );
+ }
+
+ if ( $value === '' ) {
+ $this->addError( wfMessage( 'smw_novalues' )->text() );
+ return new ThingDescription();
+ }
+
+ $subdescriptions = [];
+ list( $text, $languageCode ) = $this->dataValue->getValuesFromString( $value );
+
+ foreach ( $this->dataValue->getPropertyDataItems() as $property ) {
+
+ // If the DVFeature doesn't require a language code to be present then
+ // allow to skip it as conjunctive condition when it is empty
+ if (
+ ( $languageCode === '' ) &&
+ ( $property->getKey() === '_LCODE' ) &&
+ ( !$this->dataValue->isEnabledFeature( SMW_DV_MLTV_LCODE ) ) ) {
+ continue;
+ }
+
+ $value = $property->getKey() === '_LCODE' ? $languageCode : $text;
+ $comparator = SMW_CMP_EQ;
+
+ $this->prepareValue( $value, $comparator );
+
+ // Directly use the DI instead of going through the DVFactory to
+ // avoid having ~zh-* being validated when building a DV
+ // If one of the values is empty use, ? so queries can be arbitrary
+ // in respect of the query condition
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( $value === '' ? '?' : $value ),
+ $property,
+ false,
+ $this->dataValue->getContextPage()
+ );
+
+ if ( !$dataValue->isValid() ) {
+ $this->addError( $dataValue->getErrors() );
+ continue;
+ }
+
+ $subdescriptions[] = $this->newSubdescription( $dataValue, $comparator );
+ }
+
+ return $this->getFinalDescriptionFor( $subdescriptions );
+ }
+
+ private function getFinalDescriptionFor( $subdescriptions ) {
+
+ $count = count( $subdescriptions );
+
+ if ( $count == 0 ) {
+ return new ThingDescription();
+ }
+
+ if ( $count == 1 ) {
+ return reset( $subdescriptions );
+ }
+
+ return new Conjunction( $subdescriptions );
+ }
+
+ private function newSubdescription( $dataValue, $comparator ) {
+
+ $description = new ValueDescription(
+ $dataValue->getDataItem(),
+ $dataValue->getProperty(),
+ $comparator
+ );
+
+ if ( $dataValue->getWikiValue() === '+' || $dataValue->getWikiValue() === '?' ) {
+ $description = new ThingDescription();
+ }
+
+ return new SomeProperty(
+ $dataValue->getProperty(),
+ $description
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializer.php
new file mode 100644
index 00000000..b3e34810
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializer.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use SMWDINumber as DINumber;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NumberValueDescriptionDeserializer extends DescriptionDeserializer {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isDeserializerFor( $serialization ) {
+ return $serialization instanceof NumberValue;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return Description
+ */
+ public function deserialize( $value ) {
+
+ $comparator = SMW_CMP_EQ;
+ $this->prepareValue( $value, $comparator );
+
+ if( $comparator !== SMW_CMP_LIKE && $comparator !== SMW_CMP_PRIM_LIKE ) {
+
+ $this->dataValue->setUserValue( $value );
+
+ if ( $this->dataValue->isValid() ) {
+ return $this->descriptionFactory->newValueDescription(
+ $this->dataValue->getDataItem(),
+ $this->dataValue->getProperty(),
+ $comparator
+ );
+ } else {
+ return $this->descriptionFactory->newThingDescription();
+ }
+ }
+
+ // Remove things that belong to SMW_CMP_LIKE
+ $value = str_replace( [ '~', '*', '!' ], '', $value );
+
+ $this->dataValue->setUserValue( $value );
+
+ if ( !$this->dataValue->isValid() ) {
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ $dataItem = $this->dataValue->getDataItem();
+ $property = $this->dataValue->getProperty();
+
+ if ( $this->getErrors() !== [] ) {
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ // in:/~ signals a range request for a number context
+ if ( $dataItem->getNumber() >= 0 ) {
+ // `[[Has number::in:99]]` -> `[[Has number:: [[≥0]] [[≤99]] ]]`)
+ $description = $this->descriptionFactory->newConjunction(
+ [
+ $this->descriptionFactory->newValueDescription( new DINumber( 0 ), $property, SMW_CMP_GEQ ),
+ $this->descriptionFactory->newValueDescription( $dataItem, $property, SMW_CMP_LEQ )
+ ]
+ );
+ } else {
+ // `[[Has number::in:-100]]` -> `[[Has number:: [[≥-100]] [[≤0]] ]]`
+ $description = $this->descriptionFactory->newConjunction(
+ [
+ $this->descriptionFactory->newValueDescription( $dataItem, $property, SMW_CMP_GEQ ),
+ $this->descriptionFactory->newValueDescription( new DINumber( 0 ), $property,SMW_CMP_LEQ )
+ ]
+ );
+ }
+
+ return $description;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializer.php
new file mode 100644
index 00000000..3d08be24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializer.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use InvalidArgumentException;
+use SMW\DataValueFactory;
+use SMW\DataValues\ReferenceValue;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMWRecordValue as RecordValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class RecordValueDescriptionDeserializer extends DescriptionDeserializer {
+
+ /**
+ * @since 2.3
+ *
+ * {@inheritDoc}
+ */
+ public function isDeserializerFor( $serialization ) {
+ return $serialization instanceof RecordValue || $serialization instanceof ReferenceValue;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function deserialize( $value ) {
+
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( 'value needs to be a string' );
+ }
+
+ if ( $value === '' ) {
+ $this->addError( wfMessage( 'smw_novalues' )->text() );
+ return new ThingDescription();
+ }
+
+ $subdescriptions = [];
+ $values = $this->dataValue->getValuesFromString( $value );
+
+ $valueIndex = 0; // index in value array
+ $propertyIndex = 0; // index in property list
+
+ foreach ( $this->dataValue->getPropertyDataItems() as $diProperty ) {
+
+ // stop if there are no values left
+ if ( !is_array( $values ) || !array_key_exists( $valueIndex, $values ) ) {
+ break;
+ }
+
+ $description = $this->getDescriptionForProperty(
+ $diProperty,
+ $values,
+ $valueIndex,
+ $propertyIndex
+ );
+
+ if ( $description !== null ) {
+ $subdescriptions[] = $description;
+ }
+
+ ++$propertyIndex;
+ }
+
+ if ( $subdescriptions === [] ) {
+ $this->addError( wfMessage( 'smw_novalues' )->text() );
+ }
+
+ return $this->getDescriptionFor( $subdescriptions );
+ }
+
+ private function getDescriptionFor( $subdescriptions ) {
+ switch ( count( $subdescriptions ) ) {
+ case 0:
+ return new ThingDescription();
+ case 1:
+ return reset( $subdescriptions );
+ default:
+ return new Conjunction( $subdescriptions );
+ }
+ }
+
+ private function getDescriptionForProperty( $diProperty, $values, &$valueIndex, $propertyIndex ) {
+
+ $values[$valueIndex] = str_replace( "-3B", ";", $values[$valueIndex] );
+ $beforePrepareValue = $values[$valueIndex];
+
+ $description = null;
+ $comparator = SMW_CMP_EQ;
+
+ $this->prepareValue( $values[$valueIndex], $comparator );
+
+ // generating the DVs:
+ if ( ( $values[$valueIndex] === '' ) || ( $values[$valueIndex] == '?' ) ) { // explicit omission
+ $valueIndex++;
+ return $description;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $diProperty,
+ $values[$valueIndex],
+ false,
+ $this->dataValue->getContextPage()
+ );
+
+ if ( $dataValue->isValid() ) { // valid DV: keep
+ $description = new SomeProperty(
+ $diProperty,
+ $dataValue->getQueryDescription( $beforePrepareValue )
+ );
+ $valueIndex++;
+ } elseif ( ( count( $values ) - $valueIndex ) == ( count( $this->dataValue->getProperties() ) - $propertyIndex ) ) {
+ $this->addError( $dataValue->getErrors() );
+ ++$valueIndex;
+ }
+
+ return $description;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializer.php
new file mode 100644
index 00000000..596245b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializer.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use InvalidArgumentException;
+use SMW\DIWikiPage;
+use SMWDataValue as DataValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SomeValueDescriptionDeserializer extends DescriptionDeserializer {
+
+ /**
+ * @since 2.3
+ *
+ * {@inheritDoc}
+ */
+ public function isDeserializerFor( $serialization ) {
+ return $serialization instanceof DataValue;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function deserialize( $value ) {
+
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( 'Value needs to be a string' );
+ }
+
+ // https://www.w3.org/TR/html4/charset.html
+ // Internally encode something like [[Help:>Foo*]] since &lt; and &gt;
+ // would throw off the Title validator; apply only in combination with
+ // a NS such as [[Help:>...]]
+ $value = str_replace( [ ':<', ':>' ], [ ':-3C', ':-3E' ], $value );
+
+ $comparator = SMW_CMP_EQ;
+ $this->prepareValue( $value, $comparator );
+
+ $this->dataValue->setOption(
+ DataValue::OPT_QUERY_COMP_CONTEXT,
+ ( $comparator !== SMW_CMP_EQ && $comparator !== SMW_CMP_NEQ )
+ );
+
+ $this->dataValue->setUserValue( $value );
+
+ if ( !$this->dataValue->isValid() ) {
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ $dataItem = $this->dataValue->getDataItem();
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ $this->dataValue->getProperty(),
+ $comparator
+ );
+
+ // Ensure [[>Help:Foo]] === [[Help:>Foo]] / [[Help:~Foo*]] === [[~Help:Foo*]]
+ if ( $dataItem instanceof DIWikiPage && $dataItem->getNamespace() !== NS_MAIN ) {
+ $description = $this->findApproriateDescription( $comparator, $dataItem, $description );
+ }
+
+ return $description;
+ }
+
+ private function findApproriateDescription( $comparator, $dataItem, $description ) {
+
+ $value = $dataItem->getDBKey();
+
+ // Normalize a possible earlier encoded string part in order for the
+ // QueryComparator::extractComparatorFromString to work its magic
+ if ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+ $value = str_replace( [ '-3C', '-3E' ], [ '<', '>' ], $value );
+ $this->prepareValue( $value, $comparator );
+ }
+
+ // No approximate, use the normal ValueDescription
+ if ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+ return $description;
+ }
+
+ // The NS has been stripped, use a normal value clause in the MAIN namespace
+ $valueDescription = $this->descriptionFactory->newValueDescription(
+ $this->dataItemFactory->newDIWikiPage( $value, NS_MAIN ),
+ null,
+ $comparator
+ );
+
+ // #1652
+ // Use [[Help:~Foo*]] as conjunctive description since the comparator
+ // is only applied on the sortkey that contains the DBKey part
+ $description = $this->descriptionFactory->newConjunction( [
+ $this->descriptionFactory->newNamespaceDescription( $dataItem->getNamespace() ),
+ $valueDescription
+ ] );
+
+ return $description;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializer.php
new file mode 100644
index 00000000..dd4e50ba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializer.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\Deserializers\DVDescriptionDeserializer;
+
+use DateInterval;
+use InvalidArgumentException;
+use SMWDITime as DITime;
+use SMWTimeValue as TimeValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class TimeValueDescriptionDeserializer extends DescriptionDeserializer {
+
+ /**
+ * @since 2.3
+ *
+ * {@inheritDoc}
+ */
+ public function isDeserializerFor( $serialization ) {
+ return $serialization instanceof TimeValue;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $value
+ *
+ * @return Description
+ * @throws InvalidArgumentException
+ */
+ public function deserialize( $value ) {
+
+ if ( !is_string( $value ) ) {
+ throw new InvalidArgumentException( 'The value needs to be a string' );
+ }
+
+ $comparator = SMW_CMP_EQ;
+ $this->prepareValue( $value, $comparator );
+
+ if( $comparator !== SMW_CMP_LIKE && $comparator !== SMW_CMP_NLKE ) {
+
+ $this->dataValue->setUserValue( $value );
+
+ if ( $this->dataValue->isValid() ) {
+ return $this->descriptionFactory->newValueDescription( $this->dataValue->getDataItem(), $this->dataValue->getProperty(), $comparator );
+ } else {
+ return $this->descriptionFactory->newThingDescription();
+ }
+ }
+
+ // #1178 to support queries like [[Has date::~ Dec 2001]]
+ $this->dataValue->setOption( TimeValue::OPT_QUERY_COMP_CONTEXT, true );
+ $this->dataValue->setUserValue( $value );
+
+ if ( !$this->dataValue->isValid() ) {
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ $dataItem = $this->dataValue->getDataItem();
+ $property = $this->dataValue->getProperty();
+
+ $upperLimitDataItem = $this->getUpperLimit( $dataItem );
+
+ if ( $this->getErrors() !== [] ) {
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ if( $comparator === SMW_CMP_LIKE ) {
+ $description = $this->descriptionFactory->newConjunction( [
+ $this->descriptionFactory->newValueDescription( $dataItem, $property, SMW_CMP_GEQ ),
+ $this->descriptionFactory->newValueDescription( $upperLimitDataItem, $property, SMW_CMP_LESS )
+ ] );
+ }
+
+ if( $comparator === SMW_CMP_NLKE ) {
+ $description = $this->descriptionFactory->newDisjunction( [
+ $this->descriptionFactory->newValueDescription( $dataItem, $property, SMW_CMP_LESS ),
+ $this->descriptionFactory->newValueDescription( $upperLimitDataItem, $property, SMW_CMP_GEQ )
+ ] );
+ }
+
+ return $description;
+ }
+
+ private function getUpperLimit( $dataItem ) {
+
+ $prec = $dataItem->getPrecision();
+ $dateTime = $dataItem->asDateTime();
+
+ if ( $dateTime === false ) {
+ return $this->addError( 'Cannot compute interval for ' . $dataItem->getSerialization() );
+ }
+
+ if ( $prec === DITime::PREC_Y ) {
+ $dateTime->add( new DateInterval( 'P1Y' ) );
+ } elseif( $prec === DITime::PREC_YM ) {
+ $dateTime->add( new DateInterval( 'P1M' ) );
+ } elseif( $prec === DITime::PREC_YMD ) {
+ $dateTime->add( new DateInterval( 'P1D' ) );
+ } elseif( $prec === DITime::PREC_YMDT ) {
+
+ if ( $dataItem->getSecond() > 0 ) {
+ $dateTime->add( new DateInterval( 'PT1S' ) );
+ } elseif( $dataItem->getMinute() > 0 ) {
+ $dateTime->add( new DateInterval( 'PT1M' ) );
+ } elseif( $dataItem->getHour() > 0 ) {
+ $dateTime->add( new DateInterval( 'PT1H' ) );
+ } else {
+ $dateTime->add( new DateInterval( 'PT24H' ) );
+ }
+ }
+
+ return DITime::doUnserialize( $dataItem->getCalendarModel() . '/' . $dateTime->format( 'Y/m/d/H/i/s' ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializerRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializerRegistry.php
new file mode 100644
index 00000000..8927334b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/DVDescriptionDeserializerRegistry.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace SMW\Deserializers;
+
+use SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\DispatchingDescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\MonolingualTextValueDescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\NumberValueDescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\RecordValueDescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\SomeValueDescriptionDeserializer;
+use SMW\Deserializers\DVDescriptionDeserializer\TimeValueDescriptionDeserializer;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class DVDescriptionDeserializerRegistry {
+
+ /**
+ * @var DVDescriptionDeserializerRegistry
+ */
+ private static $instance = null;
+
+ /**
+ * @var DispatchingDescriptionDeserializer
+ */
+ private $dispatchingDescriptionDeserializer = null;
+
+ /**
+ * @since 2.3
+ *
+ * @param DispatchingDescriptionDeserializer|null $dispatchingDescriptionDeserializer
+ */
+ public function __construct( DispatchingDescriptionDeserializer $dispatchingDescriptionDeserializer = null ) {
+ $this->dispatchingDescriptionDeserializer = $dispatchingDescriptionDeserializer;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return self
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @note This allows extensions to inject their own DescriptionDeserializer
+ * without further violating SRP of the DataType or DataValue.
+ *
+ * @since 2.3
+ *
+ * @param DescriptionDeserializer $descriptionDeserializer
+ */
+ public function registerDescriptionDeserializer( DescriptionDeserializer $descriptionDeserializer ) {
+
+ if ( $this->dispatchingDescriptionDeserializer === null ) {
+ $this->dispatchingDescriptionDeserializer = $this->newDispatchingDescriptionDeserializer();
+ }
+
+ $this->dispatchingDescriptionDeserializer->addDescriptionDeserializer( $descriptionDeserializer );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DataValue $dataValue
+ *
+ * @return DescriptionDeserializer
+ */
+ public function getDescriptionDeserializerBy( DataValue $dataValue ) {
+
+ if ( $this->dispatchingDescriptionDeserializer === null ) {
+ $this->dispatchingDescriptionDeserializer = $this->newDispatchingDescriptionDeserializer();
+ }
+
+ return $this->dispatchingDescriptionDeserializer->getDescriptionDeserializerBy( $dataValue );
+ }
+
+ private function newDispatchingDescriptionDeserializer() {
+
+ $dispatchingDescriptionDeserializer = new DispatchingDescriptionDeserializer();
+ $dispatchingDescriptionDeserializer->addDescriptionDeserializer( new TimeValueDescriptionDeserializer() );
+ $dispatchingDescriptionDeserializer->addDescriptionDeserializer( new NumberValueDescriptionDeserializer() );
+ $dispatchingDescriptionDeserializer->addDescriptionDeserializer( new RecordValueDescriptionDeserializer() );
+ $dispatchingDescriptionDeserializer->addDescriptionDeserializer( new MonolingualTextValueDescriptionDeserializer() );
+
+ $dispatchingDescriptionDeserializer->addDefaultDescriptionDeserializer( new SomeValueDescriptionDeserializer() );
+
+ return $dispatchingDescriptionDeserializer;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/ExpDataDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/ExpDataDeserializer.php
new file mode 100644
index 00000000..bb5906db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/ExpDataDeserializer.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Deserializers;
+
+use Deserializers\Deserializer;
+use OutOfBoundsException;
+use SMW\Exporter\Element\ExpElement;
+use SMWExpData as ExpData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpDataDeserializer implements Deserializer {
+
+ /**
+ * @see Deserializers::deserialize
+ *
+ * @since 2.2
+ *
+ * @return ExpData
+ * @throws OutOfBoundsException
+ */
+ public function deserialize( $serialization ) {
+
+ $expData = null;
+
+ if ( isset( $serialization['version'] ) && $serialization['version'] !== 0.1 ) {
+ throw new OutOfBoundsException( 'Serializer/Deserializer version does not match, please update your data' );
+ }
+
+ if ( isset( $serialization['subject'] ) ) {
+ $expData = $this->newExpData( $serialization['subject'] );
+ }
+
+ if ( !$expData instanceof ExpData ) {
+ throw new OutOfBoundsException( 'ExpData could not be created probably due to an invalid subject' );
+ }
+
+ $this->doDeserialize( $serialization, $expData );
+
+ return $expData;
+ }
+
+ private function newExpData( $subject ) {
+ return new ExpData( ExpElement::newFromSerialization( $subject ) );
+ }
+
+ private function doDeserialize( $serialization, $expData ) {
+
+ foreach ( $serialization['data'] as $data ) {
+
+ $property = ExpElement::newFromSerialization( $data['property'] );
+
+ foreach ( $data['children'] as $child ) {
+ $expData->addPropertyObjectValue(
+ $property,
+ $this->doDeserializeChild( $child )
+ );
+ }
+ }
+ }
+
+ private function doDeserializeChild( $serialization ) {
+
+ if ( !isset( $serialization['subject'] ) ) {
+ return ExpElement::newFromSerialization( $serialization );
+ }
+
+ $element = $this->newExpData( $serialization['subject'] );
+ $this->doDeserialize( $serialization, $element );
+
+ return $element;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/SemanticDataDeserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/SemanticDataDeserializer.php
new file mode 100644
index 00000000..2e95894a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Deserializers/SemanticDataDeserializer.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace SMW\Deserializers;
+
+use Deserializers\Deserializer;
+use OutOfBoundsException;
+use RuntimeException;
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMWContainerSemanticData;
+use SMWDataItem as DataItem;
+use SMWDIContainer as DIContainer;
+use SMWErrorValue as ErrorValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataDeserializer implements Deserializer {
+
+ /**
+ * @var array
+ */
+ private $dataItemTypeIdCache = [];
+
+ /**
+ * @see Deserializers::deserialize
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ * @throws OutOfBoundsException
+ * @throws RuntimeException
+ */
+ public function deserialize( $data ) {
+
+ $semanticData = null;
+
+ if ( isset( $data['version'] ) && $data['version'] !== 0.1 && $data['version'] !== 2 ) {
+ throw new OutOfBoundsException( 'Serializer/Unserializer version does not match, please update your data' );
+ }
+
+ if ( isset( $data['subject'] ) ) {
+ $semanticData = new SemanticData( DIWikiPage::doUnserialize( $data['subject'] ) );
+ }
+
+ if ( !$semanticData instanceof SemanticData ) {
+ throw new RuntimeException( 'SemanticData could not be created probably due to a missing subject' );
+ }
+
+ $this->doDeserialize( $data, $semanticData );
+
+ return $semanticData;
+ }
+
+ /**
+ * @return null
+ */
+ private function doDeserialize( $data, &$semanticData ) {
+
+ $property = null;
+
+ if ( !isset( $data['data'] ) ) {
+ return;
+ }
+
+ foreach ( $data['data'] as $values ) {
+
+ if ( is_array( $values ) ) {
+
+ foreach ( $values as $key => $value ) {
+
+ /**
+ * @var DIProperty $property
+ */
+ if ( $key === 'property' ) {
+ $property = DIProperty::doUnserialize( $value );
+ }
+
+ /**
+ * @var DataItem
+ */
+ if ( $key === 'dataitem' ) {
+ foreach ( $value as $val ) {
+ $this->doDeserializeDataItem( $property, $data, $val, $semanticData );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return DataItem
+ */
+ private function doDeserializeDataItem( $property, $data, $value, $semanticData ) {
+
+ $dataItem = null;
+
+ if ( !is_array( $value ) ) {
+ return;
+ }
+
+ $type = $this->getDataItemId( $property );
+
+ // Verify that the current property type definition and the type of the
+ // property during serialization do match, throw an error value to avoid any
+ // exception during unserialization caused by the DataItem object due to a
+ // mismatch of type definitions
+
+ if ( $type === $value['type'] ) {
+ $dataItem = DataItem::newFromSerialization( $value['type'], $value['item'] );
+ } else {
+ $dataItem = $property->getDiWikiPage();
+ $property = new DIProperty( DIProperty::TYPE_ERROR );
+
+ $semanticData->addError( [
+ new ErrorValue( $type, 'type mismatch', $property->getLabel() )
+ ] );
+
+ }
+
+ // Check whether the current dataItem has a subobject reference
+ if ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE && $dataItem->getSubobjectName() !== '' ) {
+
+ $dataItem = $this->doDeserializeSubSemanticData(
+ $data,
+ $value['item'],
+ new SMWContainerSemanticData( $dataItem )
+ );
+
+ }
+
+ // Ensure that errors are collected from a subobject level as well and
+ // made available at the top
+ if ( $dataItem instanceof DIContainer ) {
+ $semanticData->addError( $dataItem->getSemanticData()->getErrors() );
+ }
+
+ if ( $property !== null && $dataItem !== null ) {
+ $semanticData->addPropertyObjectValue( $property, $dataItem );
+ }
+
+ }
+
+ /**
+ * Resolves properties and dataitems assigned to a subobject recursively
+ *
+ * @note The serializer has to make sure to provide a complete data set
+ * otherwise the subobject is neglected (of course one could set an error
+ * value to the DIContainer but as of now that seems unnecessary)
+ *
+ * @return DIContainer|null
+ */
+ private function doDeserializeSubSemanticData( $data, $id, $semanticData ) {
+
+ if ( !isset( $data['sobj'] ) ) {
+ return new DIContainer( $semanticData );;
+ }
+
+ foreach ( $data['sobj'] as $subobject ) {
+ if ( isset( $subobject['subject'] ) && $subobject['subject'] === $id && isset( $subobject['data'] ) ) {
+ $this->doDeserialize( $subobject, $semanticData );
+ }
+ }
+
+ return new DIContainer( $semanticData );
+ }
+
+ /**
+ * Returns DataItemId for a property
+ *
+ * @note findPropertyTypeID is calling the Store to find the
+ * typeId reference this is costly but at the moment there is no other
+ * way to determine the typeId
+ *
+ * This check is to ensure that during unserialization the correct item
+ * in terms of its definition is being sought otherwise inconsistencies
+ * can occur due to type changes of a property between the time of
+ * the serialization and the deserialization (e.g for when the
+ * serialization object is stored in cache, DB etc.)
+ *
+ * @return integer
+ */
+ private function getDataItemId( DIProperty $property ) {
+
+ if ( !isset( $this->dataItemTypeIdCache[$property->getKey()] ) ) {
+ $this->dataItemTypeIdCache[$property->getKey()] = DataTypeRegistry::getInstance()->getDataItemId( $property->findPropertyTypeID() );
+ }
+
+ return $this->dataItemTypeIdCache[$property->getKey()];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/ElasticClientTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/ElasticClientTaskHandler.php
new file mode 100644
index 00000000..01f08ba5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/ElasticClientTaskHandler.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use Html;
+use SMW\MediaWiki\Specials\Admin\OutputFormatter;
+use SMW\MediaWiki\Specials\Admin\TaskHandler;
+use SMW\Message;
+use SMW\ApplicationFactory;
+use WebRequest;
+use SMW\Elastic\Indexer\ReplicationStatus;
+use SMW\Elastic\Connection\Client as ElasticClient;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ElasticClientTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var array
+ */
+ private $taskHandlers = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param OutputFormatter $outputFormatter
+ * @param array $taskHandlers
+ */
+ public function __construct( OutputFormatter $outputFormatter, array $taskHandlers = [] ) {
+ $this->outputFormatter = $outputFormatter;
+ $this->taskHandlers = $taskHandlers;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+
+ // Root
+ $actions = [
+ 'elastic'
+ ];
+
+ foreach ( $this->taskHandlers as $taskHandler ) {
+ $actions[] = $taskHandler->getTask();
+ }
+
+ return in_array( $task, $actions );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+
+ if ( !$connection->ping() ) {
+ return '';
+ }
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-elastic-title' ),
+ [ 'action' => 'elastic' ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+ $action = $webRequest->getText( 'action' );
+
+ if ( !$connection->ping() ) {
+ return $this->outputNoNodesAvailable();
+ } elseif ( $action === 'elastic' ) {
+ $this->outputHead();
+ } else {
+ foreach ( $this->taskHandlers as $taskHandler ) {
+ if ( $taskHandler->isTaskFor( $action ) ) {
+
+ $taskHandler->setStore(
+ $this->getStore()
+ );
+
+ return $taskHandler->handleRequest( $webRequest );
+ }
+ }
+ }
+
+ $this->outputInfo();
+ }
+
+ private function outputNoNodesAvailable() {
+
+ $this->outputHead();
+
+ $html = Html::element(
+ 'div',
+ [ 'class' => 'smw-callout smw-callout-error' ],
+ 'Elasticsearch has no active nodes available.'
+ );
+
+ $this->outputFormatter->addHTML( $html );
+ }
+
+ private function outputHead() {
+
+ $this->outputFormatter->setPageTitle( 'Elasticsearch' );
+ $this->outputFormatter->addHelpLink( 'https://www.semantic-mediawiki.org/wiki/Help:ElasticStore' );
+
+ $this->outputFormatter->addParentLink(
+ [ 'tab' => 'supplement' ]
+ );
+
+ $html = Html::rawElement(
+ 'p',
+ [ 'class' => 'plainlinks' ],
+ $this->msg( [ 'smw-admin-supplementary-elastic-docu' ], Message::PARSE )
+ );
+
+ $this->outputFormatter->addHTML( $html );
+ }
+
+ private function outputInfo() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+ $html = '';
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $connection->info() )
+ );
+
+ $replicationStatus = new ReplicationStatus(
+ $connection
+ );
+
+ $jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+
+ $html .= Html::element(
+ 'li',
+ [],
+ $this->msg( [ 'smw-admin-supplementary-elastic-status-last-active-replication', $replicationStatus->get( 'last_update' ) ] )
+ );
+
+ $html .= Html::rawElement(
+ 'li', [ 'class' => 'plainlinks' ],
+ $this->msg( [ 'smw-admin-supplementary-elastic-status-recovery-job-count', $jobQueue->getQueueSize( 'smw.elasticIndexerRecovery') ], Message::PARSE )
+ );
+
+ if ( $connection->getConfig()->dotGet( 'indexer.experimental.file.ingest', false ) ) {
+ $html .= Html::rawElement(
+ 'li',
+ [ 'class' => 'plainlinks' ],
+ $this->msg( [ 'smw-admin-supplementary-elastic-status-file-ingest-job-count', $jobQueue->getQueueSize( 'smw.elasticFileIngest') ], Message::PARSE )
+ );
+ }
+
+ if ( $connection->hasLock( ElasticClient::TYPE_DATA ) ) {
+ $html .= Html::rawElement(
+ 'li',
+ [ 'class' => 'plainlinks' ],
+ $this->msg( [ 'smw-admin-supplementary-elastic-status-rebuild-lock', '✓' ], Message::TEXT )
+ );
+ }
+
+ $html .= Html::element(
+ 'li',
+ [],
+ $this->msg( [ 'smw-admin-supplementary-elastic-status-refresh-interval', $replicationStatus->get( 'refresh_interval' ) ] )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h3', [], $this->msg(
+ 'smw-admin-supplementary-elastic-status-replication' )
+ ) . Html::rawElement( 'ul', [], $html )
+ );
+
+ $list = '';
+
+ foreach ( $this->taskHandlers as $taskHandler ) {
+ $list .= $taskHandler->getHtml();
+ }
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h3', [], $this->msg( 'smw-admin-supplementary-elastic-functions' ) )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement( 'ul', [], $list )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/IndicesInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/IndicesInfoProvider.php
new file mode 100644
index 00000000..aeef88cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/IndicesInfoProvider.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use Html;
+use SMW\Message;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IndicesInfoProvider extends InfoProviderHandler {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSupplementTask() {
+ return 'indices';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-elastic-indices-title' ),
+ [ 'action' => $this->getTask() ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-indices-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle( 'Elasticsearch indices' );
+
+ $this->outputFormatter->addParentLink(
+ [ 'action' => $this->getParentTask() ],
+ 'smw-admin-supplementary-elastic-title'
+ );
+
+ $this->outputInfo();
+ }
+
+ private function outputInfo() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+
+ $html = Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-statistics-docu',
+ ],
+ Message::PARSE
+ )
+ );
+
+ $this->outputFormatter->addHtml( $html );
+
+ $this->outputFormatter->addHtml( '<h2>Indices</h2>' );
+
+ $indices = $connection->cat( 'indices' );
+ ksort( $indices );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $indices )
+ );
+
+ $this->outputFormatter->addHtml( '<h2>Statistics</h2>' );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $connection->stats( 'indices' ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/InfoProviderHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/InfoProviderHandler.php
new file mode 100644
index 00000000..0be22606
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/InfoProviderHandler.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use SMW\MediaWiki\Specials\Admin\OutputFormatter;
+use SMW\MediaWiki\Specials\Admin\TaskHandler;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+abstract class InfoProviderHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ protected $outputFormatter;
+
+ /**
+ * @since 3.0
+ *
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( OutputFormatter $outputFormatter ) {
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === $this->getTask();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getParentTask() {
+ return 'elastic';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getTask() {
+ return $this->getParentTask() . '/' . $this->getSupplementTask();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ abstract public function getSupplementTask();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/MappingsInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/MappingsInfoProvider.php
new file mode 100644
index 00000000..e09e4738
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/MappingsInfoProvider.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use Html;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use WebRequest;
+use SMW\Utils\HtmlTabs;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class MappingsInfoProvider extends InfoProviderHandler {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSupplementTask() {
+ return 'mappings';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-elastic-mappings-title' ),
+ [ 'action' => $this->getTask() ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-mappings-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle( 'Elasticsearch mappings' );
+
+ $this->outputFormatter->addParentLink(
+ [ 'action' => $this->getParentTask() ],
+ 'smw-admin-supplementary-elastic-title'
+ );
+
+ $this->outputInfo();
+ }
+
+ private function outputInfo() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+
+ $mappings = [
+ $connection->getMapping(
+ [
+ 'index' => $connection->getIndexNameByType( ElasticClient::TYPE_DATA )
+ ]
+ ),
+ $connection->getMapping(
+ [
+ 'index' => $connection->getIndexNameByType( ElasticClient::TYPE_LOOKUP )
+ ]
+ )
+ ];
+
+ $this->outputFormatter->addHtml(
+ Html::rawElement( 'p', [], $this->msg( 'smw-admin-supplementary-elastic-mappings-docu' ) )
+ );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'es-mapping' );
+ $htmlTabs->setActiveTab( 'summary' );
+
+ $htmlTabs->tab( 'summary', $this->msg( 'smw-admin-supplementary-elastic-mappings-summary' ) );
+
+ $htmlTabs->content(
+ 'summary',
+ '<pre>' . $this->outputFormatter->encodeAsJson( $this->buildSummary( $mappings ) ) . '</pre>'
+ );
+
+ $htmlTabs->tab( 'fields', $this->msg( 'smw-admin-supplementary-elastic-mappings-fields' ) );
+
+ $htmlTabs->content(
+ 'fields',
+ '<pre>' . $this->outputFormatter->encodeAsJson( $mappings ) . '</pre>'
+ );
+
+ $html = $htmlTabs->buildHTML( [ 'class' => 'es-mapping' ] );
+
+ $this->outputFormatter->addHtml(
+ $html
+ );
+
+ $this->outputFormatter->addInlineStyle(
+ '.es-mapping #tab-summary:checked ~ #tab-content-summary,' .
+ '.es-mapping #tab-fields:checked ~ #tab-content-fields {' .
+ 'display: block;}'
+ );
+ }
+
+ private function buildSummary( $mappings ) {
+
+ $count = [
+ ElasticClient::TYPE_DATA => [
+ 'fields' => [
+ 'property_fields' => 0,
+ 'nested_fields' => 0
+ ],
+ 'total' => 0
+ ],
+ ElasticClient::TYPE_LOOKUP => [
+ 'fields' => [
+ 'property_fields' => 0,
+ 'nested_fields' => 0
+ ],
+ 'total' => 0
+ ]
+ ];
+
+ foreach ( $mappings as $inx ) {
+ foreach ( $inx as $key => $value ) {
+
+ if ( isset( $value['mappings'][ElasticClient::TYPE_DATA] ) ) {
+ foreach ( $value['mappings'][ElasticClient::TYPE_DATA]['properties'] as $k => $val ) {
+ foreach ( $val as $p => $v ) {
+ if ( $p === 'properties' ) {
+ foreach ( $v as $field => $mappings ) {
+ if ( is_string( $field ) ) {
+ $count[ElasticClient::TYPE_DATA]['fields']['property_fields']++;
+ }
+
+ if ( isset( $mappings['fields'] ) ) {
+ $count[ElasticClient::TYPE_DATA]['fields']['nested_fields'] += count( $mappings['fields'] );
+ }
+ }
+ } elseif ( $p === 'type' ) {
+ $count[ElasticClient::TYPE_DATA]['fields']['property_fields']++;
+ } elseif ( $p === 'fields' ) {
+ $count[ElasticClient::TYPE_DATA]['fields']['nested_fields'] += count( $v );
+ }
+ }
+ }
+
+ $count[ElasticClient::TYPE_DATA]['total'] = $count[ElasticClient::TYPE_DATA]['fields']['property_fields'] +
+ $count[ElasticClient::TYPE_DATA]['fields']['nested_fields'];
+ }
+
+ if ( isset( $value['mappings'][ElasticClient::TYPE_LOOKUP] ) ) {
+ foreach ( $value['mappings'][ElasticClient::TYPE_LOOKUP]['properties'] as $k => $val ) {
+ foreach ( $val as $p => $v ) {
+
+ if ( $p === 'properties' ) {
+ foreach ( $v as $field => $mappings ) {
+ if ( is_string( $field ) ) {
+ $count[ElasticClient::TYPE_LOOKUP]['fields']['property_fields']++;
+ }
+
+ if ( isset( $mappings['fields'] ) ) {
+ $count[ElasticClient::TYPE_LOOKUP]['fields']['nested_fields'] += count( $mappings['fields'] );
+ }
+ }
+ } elseif ( $p === 'type' ) {
+ $count[ElasticClient::TYPE_LOOKUP]['fields']['property_fields']++;
+ }
+ }
+ }
+
+ $count[ElasticClient::TYPE_LOOKUP]['total'] = $count[ElasticClient::TYPE_LOOKUP]['fields']['property_fields'] +
+ $count[ElasticClient::TYPE_LOOKUP]['fields']['nested_fields'];
+ }
+ }
+ }
+
+ return $count;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/NodesInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/NodesInfoProvider.php
new file mode 100644
index 00000000..ac3f6007
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/NodesInfoProvider.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use Html;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NodesInfoProvider extends InfoProviderHandler {
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getSupplementTask() {
+ return 'nodes';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-elastic-nodes-title' ),
+ [ 'action' => $this->getTask() ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-nodes-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle( 'Elasticsearch nodes' );
+
+ $this->outputFormatter->addParentLink(
+ [ 'action' => $this->getParentTask() ],
+ 'smw-admin-supplementary-elastic-title'
+ );
+
+ $this->outputInfo();
+ }
+
+ private function outputInfo() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+
+ $nodes = $connection->stats( 'nodes' );
+ ksort( $nodes );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $nodes )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/SettingsInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/SettingsInfoProvider.php
new file mode 100644
index 00000000..6a7cd76e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Admin/SettingsInfoProvider.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace SMW\Elastic\Admin;
+
+use Html;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SettingsInfoProvider extends InfoProviderHandler {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSupplementTask() {
+ return 'settings';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-elastic-settings-title' ),
+ [ 'action' => $this->getTask() ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-elastic-settings-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle( 'Elasticsearch settings' );
+
+ $this->outputFormatter->addParentLink(
+ [ 'action' => $this->getParentTask() ],
+ 'smw-admin-supplementary-elastic-title'
+ );
+
+ $this->outputInfo();
+ }
+
+ private function outputInfo() {
+
+ $connection = $this->getStore()->getConnection( 'elastic' );
+
+ $settings = [
+ $connection->getSettings(
+ [
+ 'index' => $connection->getIndexNameByType( ElasticClient::TYPE_DATA )
+ ]
+ ),
+ $connection->getSettings(
+ [
+ 'index' => $connection->getIndexNameByType( ElasticClient::TYPE_LOOKUP )
+ ]
+ )
+ ];
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $settings )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Config.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Config.php
new file mode 100644
index 00000000..7f426c76
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Config.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Elastic;
+
+use SMW\Options;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Config extends Options {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $data
+ */
+ public function loadFromJSON( $data ) {
+
+ if ( $data === false ) {
+ return;
+ }
+
+ $data = json_decode( $data, true );
+ $merge = true;
+
+ if ( ( $error = json_last_error() ) !== JSON_ERROR_NONE ) {
+ throw new RuntimeException( 'JSON returned with a "' . json_last_error_msg() . '"' );
+ }
+
+ foreach ( $data as $key => $value ) {
+
+ if ( $merge && isset( $this->options[$key] ) && is_array( $value ) && is_array( $this->options[$key] ) ) {
+ $value = array_merge( $this->options[$key], $value );
+ }
+
+ $this->options[$key] = $value;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ *
+ * @return string|false
+ * @throws RuntimeException
+ */
+ public function readFile( $file ) {
+
+ if ( $file === false ) {
+ return false;
+ }
+
+ $file = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, realpath( $file ) );
+
+ if ( is_readable( $file ) ) {
+ return file_get_contents( $file );
+ }
+
+ throw new RuntimeException( "$file is inaccessible!" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/Client.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/Client.php
new file mode 100644
index 00000000..a252c2cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/Client.php
@@ -0,0 +1,889 @@
+<?php
+
+namespace SMW\Elastic\Connection;
+
+use Elasticsearch\Client as ElasticClient;
+use Elasticsearch\Common\Exceptions\NoNodesAvailableException;
+use Exception;
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use Psr\Log\LoggerAwareTrait;
+use Psr\Log\NullLogger;
+use SMW\Elastic\Exception\InvalidJSONException;
+use SMW\Elastic\Exception\ReplicationException;
+use SMW\Options;
+
+/**
+ * Reduced interface to the Elasticsearch client class.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Client {
+
+ use LoggerAwareTrait;
+
+ /**
+ * Identifies the cache namespace
+ */
+ const CACHE_NAMESPACE = 'smw:elastic';
+
+ const CACHE_CHECK_TTL = 3600;
+
+ /**
+ * @see https://www.elastic.co/blog/index-vs-type
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/master/removal-of-types.html
+ *
+ * " ... Indices created in Elasticsearch 6.0.0 or later may only contain a
+ * single mapping type ..."
+ */
+ const TYPE_DATA = 'data';
+
+ /**
+ * Index, type to temporary store index lookups during the execution
+ * of subqueries.
+ */
+ const TYPE_LOOKUP = 'lookup';
+
+ /**
+ * @var Client
+ */
+ private $client;
+
+ /**
+ * @var boolean
+ */
+ private static $ping;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var boolean
+ */
+ private $inTest = false;
+
+ /**
+ * @var boolean
+ */
+ private static $hasIndex = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $client
+ * @param Cache|null $cache
+ * @param Options|null $options
+ */
+ public function __construct( ElasticClient $client, Cache $cache = null, Options $options = null ) {
+ $this->client = $client;
+ $this->cache = $cache;
+ $this->options = $options;
+ $this->inTest = defined( 'MW_PHPUNIT_TEST' );
+
+ if ( $this->cache === null ) {
+ $this->cache = new NullCache();
+ }
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->logger = new NullLogger();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Options
+ */
+ public function getConfig() {
+ return $this->options;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {
+ self::$ping = null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getIndexNameByType( $type ) {
+ return $this->getIndexName( $type );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getIndexName( $type ) {
+ static $indices = [];
+
+ if ( !isset( $indices[$type] ) ) {
+ $indices[$type] = "smw-$type-" . wfWikiID();
+ }
+
+ return $indices[$type];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getIndexDefByType( $type ) {
+ static $indexDef = [];
+
+ if ( isset( $indexDef[$type] ) ) {
+ return $indexDef[$type];
+ }
+
+ $indexDef[$type] = file_get_contents( $this->options->dotGet( "index_def.$type" ) );
+
+ // Modify settings on-the-fly
+ if ( $this->options->dotGet( "settings.$type", [] ) !== [] ) {
+ $definition = json_decode( $indexDef[$type], true );
+
+ if ( ( $error = json_last_error() ) !== JSON_ERROR_NONE ) {
+ throw new InvalidJSONException( $error, $this->options->dotGet( "index_def.$type" ) );
+ }
+
+ $definition['settings'] = $this->options->dotGet( "settings.$type" ) + $definition['settings'];
+ $indexDef[$type] = json_encode( $definition );
+ }
+
+ return $indexDef[$type];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getIndexDefFileModificationTimeByType( $type ) {
+
+ static $filemtime = [];
+
+ if ( !isset( $filemtime[$type] ) ) {
+ $filemtime[$type] = filemtime( $this->options->dotGet( "index_def.$type" ) );
+ }
+
+ return $filemtime[$type];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getVersion() {
+
+ $info = $this->info();
+
+ if ( $this->options->safeGet( 'elastic.enabled' ) && isset( $info['version']['number'] ) ) {
+ return $info['version']['number'];
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getSoftwareInfo() {
+ return [
+ 'component' => "[https://www.elastic.co/products/elasticsearch Elasticsearch]",
+ 'version' => $this->getVersion()
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array
+ */
+ public function info() {
+
+ if ( !$this->ping() ) {
+ return [];
+ }
+
+ try {
+ $info = $this->client->info( [] );
+ } catch( NoNodesAvailableException $e ) {
+ $info = [];
+ }
+
+ return $info;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array
+ */
+ public function stats( $type = 'indices', $params = [] ) {
+
+ $indices = [
+ $this->getIndexNameByType( self::TYPE_DATA ),
+ $this->getIndexNameByType( self::TYPE_LOOKUP )
+ ];
+
+ switch ( $type ) {
+ case 'indices':
+ $res = $this->client->indices()->stats( [ 'index' => $indices ] + $params );
+ break;
+ case 'nodes':
+ $res = $this->client->nodes()->stats( $params );
+ break;
+ default:
+ $res = [];
+ }
+
+ if ( $type === 'indices' && isset( $res['indices'] ) ) {
+ unset( $res['_all'] );
+ ksort( $res['indices'] );
+ }
+
+ if ( $type === 'nodes' && isset( $res['nodes'] ) ) {
+ foreach ( $res['nodes'] as $key => &$value ) {
+ // Remove privacy info
+ unset( $value['transport_address'] );
+ unset( $value['host'] );
+ unset( $value['ip'] );
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array
+ */
+ public function cat( $type, $params = [] ) {
+
+ $res = [];
+
+ if ( $type === 'indices' ) {
+ $indices = $this->client->cat()->indices( $params );
+
+ foreach ( $indices as $key => $value ) {
+ $res[$value['index']] = $indices[$key];
+ unset( $res[$value['index']]['index'] );
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Indices
+ */
+ public function indices() {
+ return $this->client->indices();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Ingest
+ */
+ public function ingest() {
+ return $this->client->ingest();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @param boolean
+ */
+ public function hasIndex( $type ) {
+
+ if ( isset( self::$hasIndex[$type] ) && self::$hasIndex[$type] ) {
+ return true;
+ }
+
+ $index = $this->getIndexNameByType( $type );
+
+ $ret = $this->client->indices()->exists(
+ [
+ 'index' => $index
+ ]
+ );
+
+ return self::$hasIndex[$type] = $ret;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function createIndex( $type ) {
+
+ $index = $this->getIndexNameByType( $type );
+ $indices = $this->client->indices();
+
+ $version = 'v1';
+
+ if ( $indices->exists( [ 'index' => "$index-$version" ] ) ) {
+ $version = 'v2';
+
+ if ( $indices->exists( [ 'index' => "$index-$version" ] ) ) {
+ $indices->delete( [ 'index' => "$index-$version" ] );
+ }
+ }
+
+ $params = [
+ 'index' => "$index-$version",
+ 'body' => $this->getIndexDefByType( $type )
+ ];
+
+ $response = $indices->create( $params );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'index' => $index,
+ 'reponse' => json_encode( $response )
+ ];
+
+ $this->logger->info( 'Created index {index} with: {reponse}', $context );
+
+ return $version;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function deleteIndex( $type ) {
+
+ $index = $this->getIndexNameByType( $type );
+
+ $params = [
+ 'index' => $index,
+ ];
+
+ try {
+ $response = $this->client->indices()->delete( $params );
+ } catch ( Exception $e ) {
+ $response = $e->getMessage();
+ }
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ $index,
+ // A modified file causes a new cache key!
+ $this->getIndexDefFileModificationTimeByType( $type )
+ ]
+ );
+
+ $this->cache->delete( $key );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'index' => $index,
+ 'reponse' => json_encode( $response )
+ ];
+
+ $this->logger->info( 'Deleted index {index} with: {reponse}', $context );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function putSettings( array $params ) {
+ $this->client->indices()->putSettings( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function putMapping( array $params ) {
+ $this->client->indices()->putMapping( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function getMapping( array $params ) {
+ return $this->client->indices()->getMapping( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function getSettings( array $params ) {
+ return $this->client->indices()->getSettings( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function refresh( array $params ) {
+ $this->client->indices()->refresh( [ 'index' => $params['index'] ] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function validate( array $params ) {
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $results = [];
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'index' => $params['index']
+ ];
+
+ unset( $params['body']['sort'] );
+ unset( $params['body']['_source'] );
+ unset( $params['body']['profile'] );
+ unset( $params['body']['from'] );
+ unset( $params['body']['size'] );
+
+ try {
+ $results = $this->client->indices()->validateQuery( $params );
+ } catch ( Exception $e ) {
+ $context['exception'] = $e->getMessage();
+ $this->logger->info( 'Failed the validate with: {exception}', $context );
+ }
+
+ return $results;
+ }
+
+ /**
+ * @see Client::ping
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function ping() {
+
+ if ( self::$ping !== null ) {
+ return self::$ping;
+ }
+
+ if ( $this->options->dotGet( 'connection.quick_ping' ) ) {
+ return self::$ping = $this->quick_ping();
+ }
+
+ return self::$ping = $this->client->ping( [] );
+ }
+
+ /**
+ * Check is faster than the standard Client::ping
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function quick_ping( $timeout = 2 ) {
+
+ $hosts = $this->options->get( 'endpoints' );
+
+ foreach ( $hosts as $host ) {
+
+ if ( is_string( $host ) ) {
+ $host = parse_url( $host );
+ }
+
+ $fsock = @fsockopen( $host['host'], $host['port'], $errno, $errstr, $timeout );
+
+ if ( $fsock ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see Client::exists
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return boolean
+ */
+ public function exists( array $params ) {
+ return $this->client->exists( $params );
+ }
+
+ /**
+ * @see Client::get
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function get( array $params ) {
+ return $this->client->get( $params );
+ }
+
+ /**
+ * @see Client::delete
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function delete( array $params ) {
+ return $this->client->delete( $params );
+ }
+
+ /**
+ * @see Client::update
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function update( array $params ) {
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'index' => $params['index'],
+ 'id' => $params['id'],
+ 'response' => ''
+ ];
+
+ try {
+ $context['response'] = $this->client->update( $params );
+ } catch( Exception $e ) {
+ $context['exception'] = $e->getMessage();
+ $this->logger->info( 'Updated failed for document {id} with: {exception}, DOC: {doc}', $context );
+ }
+
+ return json_encode( $context['response'] );
+ }
+
+ /**
+ * @see Client::index
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function index( array $params ) {
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'index' => $params['index'],
+ 'id' => $params['id'],
+ 'response' => ''
+ ];
+
+ try {
+ $context['response'] = $this->client->index( $params );
+ } catch( Exception $e ) {
+ $context['exception'] = $e->getMessage();
+ $this->logger->info( 'Index failed for document {id} with: {exception}', $context );
+ }
+
+ return json_encode( $context['response'] );
+ }
+
+ /**
+ * @see Client::index
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function bulk( array $params ) {
+
+ if ( $params === [] ) {
+ return;
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'response' => ''
+ ];
+
+ if ( $this->inTest ) {
+ $params = $params + [ 'refresh' => true ];
+ }
+
+ try {
+ $response = $this->client->bulk( $params );
+
+ // No errors, just log the head otherwise show the entire
+ // response
+ if ( $response['errors'] === false ) {
+ unset( $response['items'] );
+ } else {
+
+ $throw = $this->options->dotGet(
+ 'replication.throw.exception.on.illegal.argument.error'
+ );
+
+ foreach ( $response['items'] as $value ) {
+
+ if ( !isset( $value['index'] ) ) {
+ continue;
+ }
+
+ if ( $throw && $value['index']['error']['type'] === 'illegal_argument_exception' ) {
+ throw new ReplicationException( $value['index']['error']['reason'] );
+ }
+ }
+ }
+
+ $context['response'] = $response;
+ } catch( ReplicationException $e ) {
+ throw new ReplicationException( $e->getMessage() );
+ } catch( Exception $e ) {
+ $this->logger->info( 'Bulk update failed with' . $e->getMessage(), $context );
+ }
+
+ return json_encode( $context['response'] );
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html
+ * @see Client::count
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function count( array $params ) {
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $results = [];
+ $time = -microtime( true );
+
+ // https://discuss.elastic.co/t/es-5-2-refresh-interval-doesnt-work-if-set-to-0/79248/2
+ // Make sure the replication/index lag doesn't hinder the search
+ if ( $this->inTest ) {
+ $this->client->indices()->refresh( [ 'index' => $params['index'] ] );
+ }
+
+ // ... "_source", "from", "profile", "query", "size", "sort" are not valid parameters.
+ unset( $params['body']['sort'] );
+ unset( $params['body']['_source'] );
+ unset( $params['body']['profile'] );
+ unset( $params['body']['from'] );
+ unset( $params['body']['size'] );
+
+ try {
+ $results = $this->client->count( $params );
+ } catch ( Exception $e ) {
+ $context['exception'] = $e->getMessage();
+ $this->logger->info( 'Failed the count with: {exception}', $context );
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'index' => $params['index'],
+ 'query' => json_encode( $params ),
+ 'procTime' => microtime( true ) + $time
+ ];
+
+ $this->logger->info( 'COUNT: {query}, queryTime: {procTime}', $context );
+
+ return $results;
+ }
+
+ /**
+ * @see Client::search
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function search( array $params ) {
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $results = [];
+ $errors = [];
+
+ $time = -microtime( true );
+
+ // https://discuss.elastic.co/t/es-5-2-refresh-interval-doesnt-work-if-set-to-0/79248/2
+ // Make sure the replication/index lag doesn't hinder the search
+ if ( $this->inTest ) {
+ $this->client->indices()->refresh( [ 'index' => $params['index'] ] );
+ }
+
+ try {
+ $results = $this->client->search( $params );
+ } catch ( NoNodesAvailableException $e ) {
+ $errors[] = 'Elasticsearch endpoint returned with "' . $e->getMessage() . '" .';
+ } catch ( Exception $e ) {
+ $context['exception'] = $e->getMessage();
+ $this->logger->info( 'Failed the search with: {exception}', $context );
+ }
+
+ $this->logger->info(
+ [
+ 'Search',
+ '{query}, queryTime: {procTime}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'index' => $params['index'],
+ 'query' => $params,
+ 'procTime' => microtime( true ) + $time
+ ]
+ );
+
+ return [ $results, $errors ];
+ }
+
+ /**
+ * @see Client::explain
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return mixed
+ */
+ public function explain( array $params ) {
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ // https://discuss.elastic.co/t/es-5-2-refresh-interval-doesnt-work-if-set-to-0/79248/2
+ // Make sure the replication/index lag doesn't hinder the search
+ if ( $this->inTest ) {
+ $this->client->indices()->refresh( [ 'index' => $params['index'] ] );
+ }
+
+ return $this->client->explain( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param string $version
+ */
+ public function setLock( $type, $version ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [ 'lock', $type ]
+ );
+
+ $this->cache->save( $key, $version );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function hasLock( $type ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [ 'lock', $type ]
+ );
+
+ return $this->cache->fetch( $key ) !== false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return mixed
+ */
+ public function getLock( $type ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [ 'lock', $type ]
+ );
+
+ return $this->cache->fetch( $key );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function releaseLock( $type ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [ 'lock', $type ]
+ );
+
+ $this->cache->delete( $key );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/ConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/ConnectionProvider.php
new file mode 100644
index 00000000..532f34a7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/ConnectionProvider.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\Elastic\Connection;
+
+use Elasticsearch\ClientBuilder;
+use SMW\Elastic\Exception\ClientBuilderNotFoundException;
+use SMW\ApplicationFactory;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+use SMW\Options;
+use Psr\Log\LoggerAwareTrait;
+use Onoi\Cache\Cache;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConnectionProvider implements IConnectionProvider {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var ElasticClient
+ */
+ private $connection;
+
+ /**
+ * @since 3.0
+ *
+ * @param Options $options
+ * @param Cache $cache
+ */
+ public function __construct( Options $options, Cache $cache ) {
+ $this->options = $options;
+ $this->cache = $cache;
+ }
+
+ /**
+ * @see ConnectionProvider::getConnection
+ *
+ * @since 3.0
+ *
+ * @return Connection
+ */
+ public function getConnection() {
+
+ if ( $this->connection !== null ) {
+ return $this->connection;
+ }
+
+ $params = [
+ 'hosts' => $this->options->get( 'endpoints' ),
+ 'retries' => $this->options->dotGet( 'connection.retries', 1 ),
+
+ 'client' => [
+
+ // controls the request timeout
+ 'timeout' => $this->options->dotGet( 'connection.timeout', 30 ),
+
+ // controls the original connection timeout duration
+ 'connect_timeout' => $this->options->dotGet( 'connection.connect_timeout', 30 )
+ ]
+
+ // Use `singleHandler` if you know you will never need async capabilities,
+ // since it will save a small amount of overhead by reducing indirection
+ // 'handler' => ClientBuilder::singleHandler()
+ ];
+
+ if ( $this->hasClientBuilder() ) {
+ $this->connection = new Client(
+ ClientBuilder::fromConfig( $params, true ),
+ $this->cache,
+ $this->options
+ );
+ } else {
+ $this->connection = new DummyClient();
+ }
+
+ $this->connection->setLogger(
+ $this->logger
+ );
+
+ $this->logger->info(
+ [
+ 'Connection',
+ '{provider} : {hosts}'
+ ],
+ [
+ 'role' => 'developer',
+ 'provider' => 'elastic',
+ 'hosts' => $params['hosts']
+ ]
+ );
+
+ return $this->connection;
+ }
+
+ /**
+ * @see ConnectionProvider::releaseConnection
+ *
+ * @since 3.0
+ */
+ public function releaseConnection() {
+ $this->connection = null;
+ }
+
+ private function hasClientBuilder() {
+
+ if ( $this->options->dotGet( 'is.elasticstore', false ) === false ) {
+ return false;
+ }
+
+ // Fail hard because someone selected the ElasticStore but forgot to install
+ // the elastic interface!
+ if ( !class_exists( '\Elasticsearch\ClientBuilder' ) ) {
+ throw new ClientBuilderNotFoundException();
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/DummyClient.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/DummyClient.php
new file mode 100644
index 00000000..2fc7d797
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Connection/DummyClient.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace SMW\Elastic\Connection;
+
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use Psr\Log\NullLogger;
+use RuntimeException;
+use SMW\Options;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DummyClient extends Client {
+
+ /**
+ * @var Client
+ */
+ private $client;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $client
+ * @param Cache|null $cache
+ * @param Options|null $options
+ */
+ public function __construct( $client = null, Cache $cache = null, Options $options = null ) {
+ $this->client = $client;
+ $this->cache = $cache;
+ $this->options = $options;
+
+ if ( $this->cache === null ) {
+ $this->cache = new NullCache();
+ }
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->logger = new NullLogger();
+ }
+
+ /**
+ * @see Client::getConfig
+ */
+ public function getConfig() {
+ return $this->options;
+ }
+
+ /**
+ * @see Client::getIndexName
+ */
+ public function getIndexName( $type ) {
+ return '';
+ }
+
+ /**
+ * @see Client::getIndexDefByType
+ */
+ public function getIndexDefByType( $type ) {
+ return '';
+ }
+
+ /**
+ * @see Client::getIndexDefFileModificationTimeByType
+ */
+ public function getIndexDefFileModificationTimeByType( $type ) {
+ return 0;
+ }
+
+ /**
+ * @see Client::getSoftwareInfo
+ */
+ public function getSoftwareInfo() {
+ return [
+ 'component' => "[https://www.elastic.co/products/elasticsearch Elasticsearch]",
+ 'version' => null
+ ];
+ }
+
+ /**
+ * @see Client::info
+ */
+ public function info() {
+ return [];
+ }
+
+ /**
+ * @see Client::stats
+ */
+ public function stats( $type = 'indices', $params = [] ) {
+ return [];
+ }
+
+ /**
+ * @see Client::cat
+ */
+ public function cat( $type, $params = [] ) {
+ return [];
+ }
+
+ /**
+ * @see Client::hasIndex
+ */
+ public function hasIndex( $type, $useCache = true ) {
+ return true;
+ }
+
+ /**
+ * @see Client::createIndex
+ */
+ public function createIndex( $type ) {}
+
+ /**
+ * @see Client::deleteIndex
+ */
+ public function deleteIndex( $type ) {}
+
+ /**
+ * @see Client::putSettings
+ */
+ public function putSettings( array $params ) {}
+
+ /**
+ * @see Client::putMapping
+ */
+ public function putMapping( array $params ) {}
+
+ /**
+ * @see Client::getMapping
+ */
+ public function getMapping( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::getSettings
+ */
+ public function getSettings( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::refresh
+ */
+ public function refresh( array $params ) {}
+
+ /**
+ * @see Client::validate
+ */
+ public function validate( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::ping
+ */
+ public function ping() {
+ return false;
+ }
+
+ /**
+ * @see Client::quick_ping
+ */
+ public function quick_ping( $timeout = 2 ) {
+ return false;
+ }
+
+ /**
+ * @see Client::exists
+ */
+ public function exists( array $params ) {
+ return false;
+ }
+
+ /**
+ * @see Client::get
+ */
+ public function get( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::delete
+ */
+ public function delete( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::update
+ */
+ public function update( array $params ) {}
+
+ /**
+ * @see Client::index
+ */
+ public function index( array $params ) {}
+
+ /**
+ * @see Client::bulk
+ */
+ public function bulk( array $params ) {}
+
+ /**
+ * @see Client::count
+ */
+ public function count( array $params ) {
+ return 0;
+ }
+
+ /**
+ * @see Client::search
+ */
+ public function search( array $params ) {
+ return [ [], [] ];
+ }
+
+ /**
+ * @see Client::explain
+ */
+ public function explain( array $params ) {
+ return [];
+ }
+
+ /**
+ * @see Client::setLock
+ */
+ public function setLock( $type, $version ) {}
+
+ /**
+ * @see Client::hasLock
+ */
+ public function hasLock( $type ) {
+ return false;
+ }
+
+ /**
+ * @see Client::getLock
+ */
+ public function getLock( $type ) {
+ return false;
+ }
+
+ /**
+ * @see Client::getLock
+ */
+ public function releaseLock( $type ) {}
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticFactory.php
new file mode 100644
index 00000000..d5a79c38
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticFactory.php
@@ -0,0 +1,426 @@
+<?php
+
+namespace SMW\Elastic;
+
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\NullMessageReporter;
+use Psr\Log\LoggerInterface;
+use SMW\ApplicationFactory;
+use SMW\Elastic\Admin\ElasticClientTaskHandler;
+use SMW\Elastic\Admin\IndicesInfoProvider;
+use SMW\Elastic\Admin\MappingsInfoProvider;
+use SMW\Elastic\Admin\NodesInfoProvider;
+use SMW\Elastic\Admin\SettingsInfoProvider;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\Connection\DummyClient;
+use SMW\Elastic\Indexer\Indexer;
+use SMW\Elastic\Indexer\FileIndexer;
+use SMW\Elastic\Indexer\Rollover;
+use SMW\Elastic\Indexer\Rebuilder;
+use SMW\Elastic\Indexer\Bulk;
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Elastic\QueryEngine\QueryEngine;
+use SMW\Elastic\QueryEngine\TermsLookup\CachingTermsLookup;
+use SMW\Elastic\QueryEngine\TermsLookup\TermsLookup;
+use SMW\Options;
+use SMW\SQLStore\PropertyTableRowMapper;
+use SMW\Store;
+use SMW\Elastic\Connection\ConnectionProvider;
+use SMW\Services\ServicesContainer;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\SomeValueInterpreter;
+use SMW\Elastic\Lookup\ProximityPropertyValueLookup;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ElasticFactory {
+
+ /**
+ * @var Indexer
+ */
+ private $indexer;
+
+ /**
+ * @since 3.0
+ *
+ * @return Config
+ */
+ public function newConfig() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $config = new Config(
+ $settings->get( 'smwgElasticsearchConfig' )
+ );
+
+ $isElasticstore = strpos( $settings->get( 'smwgDefaultStore' ), 'Elastic' ) !== false;
+
+ $config->set(
+ 'elastic.enabled',
+ $isElasticstore
+ );
+
+ $config->set(
+ 'is.elasticstore',
+ $isElasticstore
+ );
+
+ $config->set(
+ 'endpoints',
+ $settings->get( 'smwgElasticsearchEndpoints' )
+ );
+
+ $config->loadFromJSON(
+ $config->readFile( $settings->get( 'smwgElasticsearchProfile' ) )
+ );
+
+ return $config;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ConnectionProvider
+ */
+ public function newConnectionProvider() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $connectionProvider = new ConnectionProvider(
+ $this->newConfig(),
+ $applicationFactory->getCache()
+ );
+
+ $connectionProvider->setLogger(
+ $applicationFactory->getMediaWikiLogger( 'smw-elastic' )
+ );
+
+ return $connectionProvider;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ *
+ * @return ProximityPropertyValueLookup
+ */
+ public function newProximityPropertyValueLookup( Store $store ) {
+ return new ProximityPropertyValueLookup( $store );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param MessageReporter|null $messageReporter
+ *
+ * @return Indexer
+ */
+ public function newIndexer( Store $store, MessageReporter $messageReporter = null ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ // Construction is postponed to the point where it is needed
+ $servicesContainer = new ServicesContainer(
+ [
+ 'Rollover' => [
+ '_service' => [ $this, 'newRollover' ],
+ '_type' => Rollover::class
+ ],
+ 'FileIndexer' => [ $this, 'newFileIndexer' ],
+ 'Bulk' => [ $this, 'newBulk' ],
+ ]
+ );
+
+ $indexer = new Indexer(
+ $store,
+ $servicesContainer
+ );
+
+ if ( $messageReporter === null ) {
+ $messageReporter = new NullMessageReporter();
+ }
+
+ $indexer->setLogger(
+ $applicationFactory->getMediaWikiLogger( 'smw-elastic' )
+ );
+
+ $indexer->setMessageReporter(
+ $messageReporter
+ );
+
+ return $indexer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $connection
+ *
+ * @return Rollover
+ */
+ public function newRollover( ElasticClient $connection ) {
+ return new Rollover( $connection );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $connection
+ *
+ * @return Bulk
+ */
+ public function newBulk( ElasticClient $connection ) {
+ return new Bulk( $connection );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Indexer $indexer
+ *
+ * @return FileIndexer
+ */
+ public function newFileIndexer( Indexer $indexer ) {
+ return new FileIndexer( $indexer );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ *
+ * @return QueryEngine
+ */
+ public function newQueryEngine( Store $store ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $options = $this->newConfig();
+
+ $queryOptions = new Options(
+ $options->safeGet( 'query', [] )
+ );
+
+ $termsLookup = new CachingTermsLookup(
+ new TermsLookup( $store, $queryOptions ),
+ $applicationFactory->getCache()
+ );
+
+ $servicesContainer = new ServicesContainer(
+ [
+ 'ConceptDescriptionInterpreter' => [ $this, 'newConceptDescriptionInterpreter' ],
+ 'SomePropertyInterpreter' => [ $this, 'newSomePropertyInterpreter' ],
+ 'ClassDescriptionInterpreter' => [ $this, 'newClassDescriptionInterpreter' ],
+ 'NamespaceDescriptionInterpreter' => [ $this, 'newNamespaceDescriptionInterpreter' ],
+ 'ValueDescriptionInterpreter' => [ $this, 'newValueDescriptionInterpreter' ],
+ 'ConjunctionInterpreter' => [ $this, 'newConjunctionInterpreter' ],
+ 'DisjunctionInterpreter' => [ $this, 'newDisjunctionInterpreter' ],
+ 'SomeValueInterpreter' => [ $this, 'newSomeValueInterpreter' ]
+ ]
+ );
+
+ $conditionBuilder = new ConditionBuilder(
+ $store,
+ $termsLookup,
+ $applicationFactory->newHierarchyLookup(),
+ $servicesContainer
+ );
+
+ $conditionBuilder->setOptions( $queryOptions );
+
+ $queryEngine = new QueryEngine(
+ $store,
+ $conditionBuilder,
+ $options
+ );
+
+ $queryEngine->setLogger(
+ $applicationFactory->getMediaWikiLogger( 'smw-elastic' )
+ );
+
+ return $queryEngine;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ *
+ * @return Rebuilder
+ */
+ public function newRebuilder( Store $store ) {
+
+ $connection = $store->getConnection( 'elastic' );
+
+ $rebuilder = new Rebuilder(
+ $connection,
+ $this->newIndexer( $store ),
+ new PropertyTableRowMapper( $store ),
+ $this->newRollover( $connection )
+ );
+
+ return $rebuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ *
+ * @return ElasticClientTaskHandler
+ */
+ public function newInfoTaskHandler( Store $store, $outputFormatter ) {
+
+ $taskHandlers = [
+ new SettingsInfoProvider( $outputFormatter ),
+ new MappingsInfoProvider( $outputFormatter ),
+ new IndicesInfoProvider( $outputFormatter ),
+ new NodesInfoProvider( $outputFormatter )
+ ];
+
+ return new ElasticClientTaskHandler( $outputFormatter, $taskHandlers );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return ConceptDescriptionInterpreter
+ */
+ public function newConceptDescriptionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new ConceptDescriptionInterpreter(
+ $containerBuilder,
+ ApplicationFactory::getInstance()->newQueryParser()
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return SomePropertyInterpreter
+ */
+ public function newSomePropertyInterpreter( ConditionBuilder $containerBuilder ) {
+ return new SomePropertyInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return ClassDescriptionInterpreter
+ */
+ public function newClassDescriptionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new ClassDescriptionInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return NamespaceDescriptionInterpreter
+ */
+ public function newNamespaceDescriptionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new NamespaceDescriptionInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return ValueDescriptionInterpreter
+ */
+ public function newValueDescriptionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new ValueDescriptionInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return SomeValueInterpreter
+ */
+ public function newSomeValueInterpreter( ConditionBuilder $containerBuilder ) {
+ return new SomeValueInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return ConjunctionInterpreter
+ */
+ public function newConjunctionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new ConjunctionInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $containerBuilder
+ *
+ * @return DisjunctionInterpreter
+ */
+ public function newDisjunctionInterpreter( ConditionBuilder $containerBuilder ) {
+ return new DisjunctionInterpreter( $containerBuilder );
+ }
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::SQLStore::EntityReferenceCleanUpComplete
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function onEntityReferenceCleanUpComplete( Store $store, $id, $subject, $isRedirect ) {
+
+ if ( !$store instanceof ElasticStore || $store->getConnection( 'elastic' ) instanceof DummyClient ) {
+ return true;
+ }
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->newIndexer( $store );
+ }
+
+ $this->indexer->setOrigin( __METHOD__ );
+ $this->indexer->delete( [ $id ] );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::Admin::TaskHandlerFactory
+ * @since 3.0
+ */
+ public function onTaskHandlerFactory( &$taskHandlers, Store $store, $outputFormatter, $user ) {
+
+ if ( !$store instanceof ElasticStore ) {
+ return true;
+ }
+
+ $taskHandlers[] = $this->newInfoTaskHandler(
+ $store,
+ $outputFormatter
+ );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticStore.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticStore.php
new file mode 100644
index 00000000..9de6fa6b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/ElasticStore.php
@@ -0,0 +1,353 @@
+<?php
+
+namespace SMW\Elastic;
+
+use Hooks;
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\SQLStore;
+use SMWQuery as Query;
+use Title;
+
+/**
+ * @private
+ *
+ * The `ElasticStore` is the interface to an `Elasticsearch` cluster both in
+ * regards for replicating data to a cluster as well as retrieving search results
+ * from it.
+ *
+ * `Elasticsearch` is expected:
+ * - to be used as search (aka query) engine with all other data management tasks
+ * to be carried out using the default `SQLStore`.
+ * - to inherit most of the `SQLStore` methods
+ *
+ * @see https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/Elastic/README.md
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ElasticStore extends SQLStore {
+
+ /**
+ * @var ElasticFactory
+ */
+ private $elasticFactory;
+
+ /**
+ * @var Indexer
+ */
+ private $indexer;
+
+ /**
+ * @var QueryEngine
+ */
+ private $queryEngine;
+
+ /**
+ * @since 3.0
+ */
+ public function __construct() {
+ parent::__construct();
+ $this->elasticFactory = new ElasticFactory();
+ }
+
+ /**
+ * @see Store::service
+ *
+ * {@inheritDoc}
+ */
+ public function service( $service, ...$args ) {
+
+ if ( $this->servicesContainer === null ) {
+ $this->servicesContainer = parent::newServicesContainer();
+
+ // Replace an existing (or add) SQLStore service with a ES specific
+ // optimized service
+
+ // $this->servicesContainer->add( 'ProximityPropertyValueLookup', function() {
+ // return $this->elasticFactory->newProximityPropertyValueLookup( $this );
+ // } );
+ }
+
+ return $this->servicesContainer->get( $service, ...$args );
+ }
+
+ /**
+ * @see SQLStore::deleteSubject
+ * @since 3.0
+ *
+ * @param Title $title
+ */
+ public function deleteSubject( Title $title ) {
+ parent::deleteSubject( $title );
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->elasticFactory->newIndexer( $this, $this->messageReporter );
+ }
+
+ $this->indexer->setOrigin( 'ElasticStore::DeleteSubject' );
+ $idList = [];
+
+ if ( isset( $this->extensionData['delete.list'] ) ) {
+ $idList = $this->extensionData['delete.list'];
+ }
+
+ $this->indexer->delete( $idList, $title->getNamespace() === SMW_NS_CONCEPT );
+
+ unset( $this->extensionData['delete.list'] );
+ }
+
+ /**
+ * @see SQLStore::changeTitle
+ * @since 3.0
+ *
+ * @param Title $oldtitle
+ * @param Title $newtitle
+ * @param integer $pageid
+ * @param integer $redirid
+ */
+ public function changeTitle( Title $oldTitle, Title $newTitle, $pageId, $redirectId = 0 ) {
+ parent::changeTitle( $oldTitle, $newTitle, $pageId, $redirectId );
+
+ $id = $this->getObjectIds()->getSMWPageID(
+ $oldTitle->getDBkey(),
+ $oldTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->elasticFactory->newIndexer( $this, $this->messageReporter );
+ }
+
+ $this->indexer->setOrigin( 'ElasticStore::ChangeTitle' );
+ $idList = [ $id ];
+
+ if ( isset( $this->extensionData['delete.list'] ) ) {
+ $idList = array_merge( $idList, $this->extensionData['delete.list'] );
+ }
+
+ $this->indexer->delete( $idList );
+
+ // Use case [[Foo]] redirects to #REDIRECT [[Bar]] with Bar not yet being
+ // materialized and with the update not having created any reference,
+ // fulfill T:Q0604 by allowing to create a minimized document body
+ if ( $newTitle->exists() === false ) {
+ $id = $this->getObjectIds()->getSMWPageID(
+ $newTitle->getDBkey(),
+ $newTitle->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ $dataItem = DIWikiPage::newFromTitle( $newTitle );
+ $dataItem->setId( $id );
+
+ $this->indexer->create( $dataItem );
+ }
+
+ unset( $this->extensionData['delete.list'] );
+ }
+
+ /**
+ * @see SQLStore::fetchQueryResult
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return QueryResult
+ */
+ public function getQueryResult( Query $query ) {
+
+ $result = null;
+ $time = -microtime( true );
+
+ $connection = $this->getConnection( 'elastic' );
+
+ if ( $this->queryEngine === null ) {
+ $this->queryEngine = $this->elasticFactory->newQueryEngine( $this );
+ }
+
+ if ( $connection->getConfig()->dotGet( 'query.fallback.no.connection' ) && !$connection->ping() ) {
+ return parent::getQueryResult( $query );
+ }
+
+ $params = [
+ $this,
+ $query,
+ &$result,
+ $this->queryEngine
+ ];
+
+ if ( Hooks::run( 'SMW::Store::BeforeQueryResultLookupComplete', $params ) ) {
+ $result = $this->queryEngine->getQueryResult( $query );
+ }
+
+ $params = [
+ $this,
+ &$result
+ ];
+
+ Hooks::run( 'SMW::ElasticStore::AfterQueryResultLookupComplete', $params );
+ Hooks::run( 'SMW::Store::AfterQueryResultLookupComplete', $params );
+
+ $query->setOption( Query::PROC_QUERY_TIME, microtime( true ) + $time );
+
+ return $result;
+ }
+
+ /**
+ * @see SQLStore::doDataUpdate
+ * @since 3.0
+ *
+ * @param SemanticData $semanticData
+ */
+ protected function doDataUpdate( SemanticData $semanticData ) {
+ parent::doDataUpdate( $semanticData );
+
+ $time = -microtime( true );
+ $config = $this->getConnection( 'elastic' )->getConfig();
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->elasticFactory->newIndexer( $this, $this->messageReporter );
+ }
+
+ $this->indexer->setOrigin( 'ElasticStore::DoDataUpdate' );
+ $subject = $semanticData->getSubject();
+
+ if ( isset( $this->extensionData['delete.list'] ) ) {
+ $this->indexer->delete( $this->extensionData['delete.list'] );
+ }
+
+ if ( !isset( $this->extensionData['change.diff'] ) ) {
+ throw new RuntimeException( "Unable to replicate, missing a `change.diff` object!" );
+ }
+
+ $text = '';
+
+ if ( $config->dotGet( 'indexer.raw.text', false ) && ( $revID = $semanticData->getExtensionData( 'revision_id' ) ) !== null ) {
+ $text = $this->indexer->fetchNativeData( $revID );
+ }
+
+ $this->indexer->safeReplicate(
+ $this->extensionData['change.diff'],
+ $text
+ );
+
+ unset( $this->extensionData['delete.list'] );
+ unset( $this->extensionData['change.diff'] );
+
+ $this->logger->info(
+ [
+ 'ElasticStore',
+ 'Data update completed',
+ 'procTime in sec: {procTime}',
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'procTime' => microtime( true ) + $time,
+ ]
+ );
+
+ if ( $subject->getNamespace() === NS_FILE && $config->dotGet( 'indexer.experimental.file.ingest', false ) && $semanticData->getOption( 'is.fileupload' ) ) {
+ $this->indexer->getFileIndexer()->planIngestJob( $subject->getTitle() );
+ }
+ }
+
+ /**
+ * @see SQLStore::setup
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function setup( $verbose = true ) {
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->elasticFactory->newIndexer( $this, $this->messageReporter );
+ }
+
+ $this->indexer->setup();
+
+ if ( $verbose ) {
+ $this->messageReporter->reportMessage( "\n" );
+ $this->messageReporter->reportMessage( 'Selected query engine: "SMWElasticStore"' );
+ $this->messageReporter->reportMessage( "\n" );
+ $this->messageReporter->reportMessage( "\nSetting up indices ...\n" );
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ parent::setup( $verbose );
+ }
+
+ /**
+ * @see SQLStore::drop
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function drop( $verbose = true ) {
+
+ if ( $this->indexer === null ) {
+ $this->indexer = $this->elasticFactory->newIndexer( $this, $this->messageReporter );
+ }
+
+ $this->indexer->drop();
+
+ if ( $verbose ) {
+ $this->messageReporter->reportMessage( "\n" );
+ $this->messageReporter->reportMessage( 'Selected query engine: "SMWElasticStore"' );
+ $this->messageReporter->reportMessage( "\n" );
+ $this->messageReporter->reportMessage( "\nDropping indices ...\n" );
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ parent::drop( $verbose );
+ }
+
+ /**
+ * @see SQLStore::clear
+ * @since 3.0
+ */
+ public function clear() {
+ parent::clear();
+ $this->indexer = null;
+ $this->queryEngine = null;
+ }
+
+ /**
+ * @see Store::getInfo
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getInfo( $type = null ) {
+
+ if ( $type === 'store' ) {
+ return 'SMWElasticStore';
+ }
+
+ $database = $this->getConnection( 'mw.db' );
+ $client = $this->getConnection( 'elastic' );
+
+ if ( $type === 'db' ) {
+ return $database->getInfo();
+ }
+
+ if ( $type === 'es' ) {
+ return $client->getVersion();
+ }
+
+ return [
+ 'SMWElasticStore' => $database->getInfo() + [ 'es' => $client->getVersion() ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ClientBuilderNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ClientBuilderNotFoundException.php
new file mode 100644
index 00000000..4c1d65da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ClientBuilderNotFoundException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace SMW\Elastic\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ClientBuilderNotFoundException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ */
+ public function __construct() {
+ parent::__construct( "The \Elasticsearch\ClientBuilder class is missing, please see https://www.semantic-mediawiki.org/wiki/Help:ElasticStore/ClientBuilder!" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/InvalidJSONException.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/InvalidJSONException.php
new file mode 100644
index 00000000..3b2db2f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/InvalidJSONException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Elastic\Exception;
+
+use RuntimeException;
+use SMW\Utils\ErrorCodeFormatter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class InvalidJSONException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( $error, $content = '' ) {
+ parent::__construct( ErrorCodeFormatter::getMessageFromJsonErrorCode( $error ) . " caused by: $content" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/NoConnectionException.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/NoConnectionException.php
new file mode 100644
index 00000000..a34e34ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/NoConnectionException.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Elastic\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NoConnectionException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ */
+ public function __construct() {
+ parent::__construct(
+ "Could not establish a connection to Elasticsearch using " . json_encode( $GLOBALS['smwgElasticsearchEndpoints'] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ReplicationException.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ReplicationException.php
new file mode 100644
index 00000000..1b6e3731
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Exception/ReplicationException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Elastic\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ReplicationException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/AttachmentAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/AttachmentAnnotator.php
new file mode 100644
index 00000000..63fb7684
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/AttachmentAnnotator.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\DataItemFactory;
+use SMW\DIProperty;
+use SMW\PropertyAnnotator;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+use SMWDITime as DITime;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class AttachmentAnnotator implements PropertyAnnotator {
+
+ /**
+ * @var ContainerSemanticData
+ */
+ private $containerSemanticData;
+
+ /**
+ * @var []
+ */
+ private $doc = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ContainerSemanticData $containerSemanticData
+ * @param array $doc
+ */
+ public function __construct( ContainerSemanticData $containerSemanticData, array $doc ) {
+ $this->containerSemanticData = $containerSemanticData;
+ $this->doc = $doc;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return new DIProperty( '_FILE_ATTCH' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DIContainer
+ */
+ public function getContainer() {
+ return new DIContainer( $this->containerSemanticData );
+ }
+
+ /**
+ * @see PropertyAnnotator::getSemanticData
+ * @since 3.0
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData() {
+ return $this->containerSemanticData;
+ }
+
+ /**
+ * @see PropertyAnnotator::addAnnotation
+ * @since 3.0
+ *
+ * @return PropertyAnnotator
+ */
+ public function addAnnotation() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ // @see https://www.elastic.co/guide/en/elasticsearch/plugins/master/using-ingest-attachment.html
+ if ( isset( $this->doc['_source']['attachment']['date'] ) ) {
+ if ( ( $dataItem = DITime::newFromTimestamp( $this->doc['_source']['attachment']['date'] ) ) instanceof DITime ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_DATE' ),
+ $dataItem
+ );
+ }
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['content_type'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_TYPE' ),
+ $dataItemFactory->newDIBlob( $this->doc['_source']['attachment']['content_type'] )
+ );
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['author'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_AUTHOR' ),
+ $dataItemFactory->newDIBlob( $this->doc['_source']['attachment']['author'] )
+ );
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['title'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_TITLE' ),
+ $dataItemFactory->newDIBlob( $this->doc['_source']['attachment']['title'] )
+ );
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['language'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_LANG' ),
+ $dataItemFactory->newDIBlob( $this->doc['_source']['attachment']['language'] )
+ );
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['content_length'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_LEN' ),
+ $dataItemFactory->newDINumber( intval( $this->doc['_source']['attachment']['content_length'] ) )
+ );
+ }
+
+ if ( isset( $this->doc['_source']['attachment']['keywords'] ) ) {
+ $this->containerSemanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_CONT_KEYW' ),
+ $dataItemFactory->newDINumber( intval( $this->doc['_source']['attachment']['keywords'] ) )
+ );
+ }
+
+ return $this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Bulk.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Bulk.php
new file mode 100644
index 00000000..2392d1a3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Bulk.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\Elastic\Connection\Client as ElasticClient;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Bulk {
+
+ /**
+ * @var ElasticClient
+ */
+ private $connection;
+
+ /**
+ * @var array
+ */
+ private $bulk = [];
+
+ /**
+ * @var array
+ */
+ private $head = [];
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( ElasticClient $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {
+ $this->bulk = [];
+ $this->head = [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function head( array $params ) {
+ $this->head = $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ */
+ public function delete( array $params ) {
+ $this->bulk['body'][] = [ 'delete' => $params + $this->head ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ * @param array $source
+ */
+ public function index( array $params, array $source ) {
+ $this->bulk['body'][] = [ 'index' => $params + $this->head ];
+ $this->bulk['body'][] = $source;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ * @param array $source
+ */
+ public function upsert( array $params, array $doc ) {
+ $this->bulk['body'][] = [ 'update' => $params + $this->head ];
+ $this->bulk['body'][] = [ 'doc' => $doc, "doc_as_upsert" => true ];
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function execute() {
+
+ $response = $this->connection->bulk(
+ $this->bulk
+ );
+
+ $this->bulk = [];
+
+ return $response;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function toJson( $flags = 0 ) {
+ return json_encode( $this->bulk, $flags );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIndexer.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIndexer.php
new file mode 100644
index 00000000..e9408dfc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIndexer.php
@@ -0,0 +1,479 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use File;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMW\Store;
+use SMWContainerSemanticData as ContainerSemanticData;
+use Title;
+
+/**
+ * Experimental file indexer that uses the ES ingest pipeline to ingest and retrieve
+ * data from an attachment and make file content searchable outside of a normal
+ * wiki content.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileIndexer {
+
+ use MessageReporterAwareTrait;
+ use LoggerAwareTrait;
+
+ /**
+ * @var Indexer
+ */
+ private $indexer;
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @var boolean
+ */
+ private $sha1Check = true;
+
+ /**
+ * @since 3.0
+ *
+ * @param Indexer $indexer
+ */
+ public function __construct( Indexer $indexer ) {
+ $this->indexer = $indexer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function noSha1Check() {
+ $this->sha1Check = false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param File|null $file
+ */
+ public function planIngestJob( Title $title ) {
+
+ $fileIngestJob = new FileIngestJob(
+ $title
+ );
+
+ $fileIngestJob->lazyPush();
+ }
+
+ /**
+ * The ES ingest pipeline only does create (not update) index content which
+ * means any other content is deleted after the ingest process has finished
+ * therefore:
+ *
+ * - Read the document before, and retrieve any annotations that exists for
+ * that entity
+ * - Let ES ingest the file content and attach the earlier retrieved
+ * annotations
+ * - SMW doesn't know anything about the file attachment details ES has gather
+ * from the file hence update the SQLStore (!important not the ElasticStore)
+ * with the data
+ * - After the SQLStore update make sure that those attachment details (which
+ * are represented as subobject) are added to ES manually (means not through
+ * the standard Store::updateData to avoid an update circle) otherwise there
+ * will be invisible the any SMW user
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage $dataItem
+ * @param File|null $file
+ */
+ public function index( DIWikiPage $dataItem, File $file = null ) {
+
+ if ( $dataItem->getId() == 0 ) {
+ $dataItem->setId( $this->indexer->getId( $dataItem ) );
+ }
+
+ if ( $dataItem->getId() == 0 || $dataItem->getNamespace() !== NS_FILE || $dataItem->getSubobjectName() !== '' ) {
+ return;
+ }
+
+ $time = -microtime( true );
+
+ $params = [
+ 'id' => 'attachment',
+ 'body' => [
+ 'description' => 'Extract attachment information',
+ 'processors' => [
+ [
+ 'attachment' => [
+ 'field' => 'file_content',
+ 'indexed_chars' => -1
+ ]
+ ],
+ [
+ 'remove' => [
+ "field" => "file_content"
+ ]
+ ]
+ ]
+ ],
+ ];
+
+ $connection = $this->indexer->getConnection();
+ $connection->ingest()->putPipeline( $params );
+
+ if ( $file === null ) {
+ $file = wfFindFile( $dataItem->getTitle() );
+ }
+
+ if ( $file === false || $file === null ) {
+ return;
+ }
+
+ $url = $file->getFullURL();
+ $id = $dataItem->getId();
+
+ $sha1 = $file->getSha1();
+ $ingest = true;
+
+ $index = $this->indexer->getIndexName( ElasticClient::TYPE_DATA );
+ $doc = [ '_source' => [] ];
+
+ $params = [
+ 'index' => $index,
+ 'type' => ElasticClient::TYPE_DATA,
+ 'id' => $id,
+ ];
+
+ // Do we have any existing data? The ingest pipeline will override the
+ // entire document, so rescue any data before starting the ingest.
+ if ( $connection->exists( $params ) ) {
+ $doc = $connection->get( $params + [ '_source_include' => [ 'file_sha1', 'subject', 'text_raw', 'text_copy', 'P*' ] ] );
+ }
+
+ // Is the sha1 the same? Don't do anything since the content is expected
+ // to be the same!
+ if ( $this->sha1Check && isset( $doc['_source']['file_sha1'] ) && $doc['_source']['file_sha1'] === $sha1 ) {
+ $ingest = false;
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'origin' => $this->origin,
+ 'subject' => $dataItem->getHash()
+ ];
+
+ if ( $ingest === false ) {
+
+ $msg = [
+ 'File indexer',
+ 'Skipping the ingest process',
+ 'Found identical file_sha1 ({subject})'
+ ];
+
+ return $this->logger->info( $msg, $context );
+ }
+
+ $contents = '';
+
+ // Avoid a "failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found"
+ $file_headers = @get_headers( $url );
+
+ if ( $file_headers !== false && $file_headers[0] !== 'HTTP/1.1 404 Not Found' && $file_headers[0] !== 'HTTP/1.0 404 Not Found' ) {
+ $contents = file_get_contents( $url );
+ } else {
+ $this->logger->info( [ 'File indexer', "HTTP/1.1 404 Not Found for $url" ], $context );
+ }
+
+ $params += [
+ 'pipeline' => 'attachment',
+ 'body' => [
+ 'file_content' => base64_encode( $contents ),
+ 'file_path' => $url,
+ 'file_sha1' => $sha1,
+ ] + $doc['_source']
+ ];
+
+ $context['response'] = $connection->index( $params );
+ $context['procTime'] = microtime( true ) + $time;
+
+ $msg = [
+ 'File indexer',
+ 'Ingest process completed ({subject})',
+ 'procTime (in sec): {procTime}',
+ 'Response: {response}'
+ ];
+
+ $this->logger->info( $msg, $context );
+
+ // Don't use the ElasticStore otherwise we index the added fields once more
+ // and hereby remove the content from the attachment! and start a circle
+ // since the annotation update can only happen after the information is
+ // retrieved from ES.
+ $this->addAnnotation(
+ ApplicationFactory::getInstance()->getStore( '\SMW\SQLStore\SQLStore' ),
+ $dataItem
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param DIWikiPage $dataItem
+ */
+ public function addAnnotation( Store $store, DIWikiPage $dataItem ) {
+
+ $time = -microtime( true );
+
+ if ( $dataItem->getId() == 0 ) {
+ $dataItem->setId( $this->indexer->getId( $dataItem ) );
+ }
+
+ if ( $dataItem->getId() == 0 ) {
+ throw new RuntimException( "Missing ID: " . $dataItem );
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'origin' => $this->origin,
+ 'subject' => $dataItem->getHash()
+ ];
+
+ $semanticData = $store->getSemanticData( $dataItem );
+ $connection = $this->indexer->getConnection();
+
+ $index = $this->indexer->getIndexName( ElasticClient::TYPE_DATA );
+ $doc = [ '_source' => [] ];
+
+ $params = [
+ 'index' => $index,
+ 'type' => ElasticClient::TYPE_DATA,
+ 'id' => $dataItem->getId(),
+ ];
+
+ if ( !$connection->exists( $params ) ) {
+
+ $msg = [
+ 'File indexer',
+ 'Abort annotation update',
+ 'Missing {id} document!'
+ ];
+
+ return $this->logger->info( $msg, $context + [ 'id' => $dataItem->getId() ] );
+ }
+
+ $params = $params + [
+ '_source_include' => [
+ 'file_sha1',
+ 'attachment.date',
+ 'attachment.content_type',
+ 'attachment.author',
+ 'attachment.language',
+ 'attachment.title',
+ 'attachment.content_length'
+ ]
+ ];
+
+ $doc = $connection->get( $params );
+
+ if ( !isset( $doc['_source']['file_sha1'] ) ) {
+
+ $msg = [
+ 'File indexer',
+ 'No annotation update',
+ 'Missing file_sha1!'
+ ];
+
+ return $this->logger->info( $msg, $context );
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData(
+ $dataItem,
+ $doc
+ );
+
+ $attachmentAnnotator = new AttachmentAnnotator(
+ $containerSemanticData,
+ $doc
+ );
+
+ $attachmentAnnotator->addAnnotation();
+ $property = $attachmentAnnotator->getProperty();
+
+ // Remove any existing `_FILE_ATTCH` in case it was a reupload with a different
+ // content sha1
+ $semanticData->removeProperty( $property );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ $attachmentAnnotator->getContainer()
+ );
+
+ $callableUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate( function() use( $store, $semanticData, $attachmentAnnotator ) {
+ // Update the SQLStore with the annotated information which will NOT
+ // trigger another ES index update BUT ...
+ $store->updateData( $semanticData );
+
+ // ... we need to replicate the container data (subobject) in order to
+ // make them usable via query engine therefore ...
+ $this->indexAttachmentInfo( $attachmentAnnotator );
+ } );
+
+ $callableUpdate->setOrigin( __METHOD__ );
+ $callableUpdate->waitOnTransactionIdle();
+ $callableUpdate->pushUpdate();
+
+ $context['procTime'] = microtime( true ) + $time;
+
+ $msg = [
+ 'File indexer',
+ 'Attachment annotation update completed ({subject})',
+ 'procTime (in sec): {procTime}'
+ ];
+
+ $this->logger->info( $msg, $context );
+ }
+
+ /**
+ * Meta assignments from a file ingest need to be republished in a SMW conform
+ * manner so that property path `[[File attachment.Content title::..]]` work
+ * as expected.
+ *
+ * @since 3.0
+ *
+ * @param AttachmentAnnotator $attachmentAnnotator
+ */
+ public function indexAttachmentInfo( AttachmentAnnotator $attachmentAnnotator ) {
+
+ $data = [];
+ $time = -microtime( true );
+
+ $semanticData = $attachmentAnnotator->getSemanticData();
+ $subject = $semanticData->getSubject();
+
+ // Find base document ID
+ $baseDocId = $this->indexer->getId( $subject->asBase() );
+
+ if ( $baseDocId == 0 ) {
+ throw new RuntimeException( "Missing ID: " . $subject );
+ }
+
+ $subject->setId( $this->indexer->getId( $subject ) );
+
+ if ( $subject->getId() == 0 ) {
+ throw new RuntimeException( "Missing ID: " . $subject );
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'origin' => $this->origin,
+ 'subject' => $subject->getHash()
+ ];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ $pid = $this->indexer->getId(
+ $property->getCanonicalDiWikiPage()
+ );
+
+ $pid = FieldMapper::getPID( $pid );
+ $data[$pid] = [];
+ $field = FieldMapper::getField( $property );
+
+ $data[$pid][$field] = [];
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $dataItem ) {
+ $data[$pid][$field][] = $dataItem->getSortKey();
+ }
+ }
+
+ $this->indexer->create( $subject, $data );
+
+ // Attach the subobject to the base subject
+ $response = $this->upsertDoc(
+ $baseDocId,
+ $subject,
+ $attachmentAnnotator->getProperty()
+ );
+
+ $context['time'] = microtime( true ) + $time;
+ $context['response'] = $response;
+
+ $msg = [
+ 'File indexer',
+ 'Pushed attachment information to ES ({subject})',
+ 'procTime (in sec): {procTime}',
+ 'Response: {response}'
+ ];
+
+ $this->logger->info( $msg, $context );
+ }
+
+ private function upsertDoc( $baseDocId, $subject, $property ) {
+
+ $params = [
+ '_index' => $this->indexer->getIndexName( ElasticClient::TYPE_DATA ),
+ '_type' => ElasticClient::TYPE_DATA
+ ];
+
+ $bulk = $this->indexer->newBulk( $params );
+ $data = [];
+
+ $pid = $this->indexer->getId(
+ $property->getCanonicalDiWikiPage()
+ );
+
+ $pid = FieldMapper::getPID( $pid );
+ $data[$pid] = [];
+
+ // It is the ID field we want not any type related field!
+ $field = 'wpgID';
+
+ $data[$pid][$field] = [];
+ $data[$pid][$field][] = $subject->getId();
+
+ // Upsert of the base document to link subject -> subobject otherwise
+ // a property path like `File attachment.Content length`) is not going
+ // to work
+ $bulk->upsert( [ '_id' => $baseDocId ], $data );
+
+ return $bulk->execute();
+ }
+
+ private function newContainerSemanticData( $dataItem, $doc ) {
+
+ $subobjectName = '_FILE' . md5( $doc['_source']['file_sha1'] );
+
+ $subject = new DIWikiPage(
+ $dataItem->getDBkey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $subobjectName
+ );
+
+ return new ContainerSemanticData( $subject );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIngestJob.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIngestJob.php
new file mode 100644
index 00000000..dfa43298
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/FileIngestJob.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Job;
+use SMW\Elastic\ElasticFactory;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+use SMW\DIWikiPage;
+use Title;
+
+/**
+ * @license GNU GPL v2
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileIngestJob extends Job {
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.elasticFileIngest', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 3.0
+ */
+ public function run() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $store = $applicationFactory->getStore();
+
+ $connection = $store->getConnection( 'elastic' );
+
+ // Make sure a node is available
+ if ( $connection->hasLock( ElasticClient::TYPE_DATA ) || !$connection->ping() ) {
+
+ if ( $connection->hasLock( ElasticClient::TYPE_DATA ) ) {
+ $this->params['retryCount'] = 0;
+ }
+
+ return $this->requeueRetry( $connection->getConfig() );
+ }
+
+ $elasticFactory = new ElasticFactory();
+
+ $indexer = $elasticFactory->newIndexer(
+ $store
+ );
+
+ $fileIndexer = $indexer->getFileIndexer();
+
+ $fileIndexer->setOrigin( __METHOD__ );
+
+ $fileIndexer->setLogger(
+ $applicationFactory->getMediaWikiLogger( 'smw-elastic' )
+ );
+
+ $file = wfFindFile( $this->getTitle() );
+
+ // File isn't available yet (or uploaded), try again!
+ if ( $file === false ) {
+ return $this->requeueRetry( $connection->getConfig() );
+ }
+
+ // It has been observed that when this job is run, the job runner can
+ // return with "Fatal error: Allowed memory size of ..." which in most
+ // cases happen when large files are involved therefore temporary lift
+ // the limitation!
+ $memory_limit = ini_get( 'memory_limit' );
+
+ if ( wfShorthandToInteger( $memory_limit ) < wfShorthandToInteger( '1024M' ) ) {
+ ini_set( 'memory_limit', '1024M' );
+ }
+
+ $fileIndexer->index(
+ DIWikiPage::newFromTitle( $this->getTitle() ),
+ $file
+ );
+
+ ini_set( 'memory_limit', $memory_limit );
+
+ return true;
+ }
+
+ private function requeueRetry( $config ) {
+
+ // Give up!
+ if ( $this->getParameter( 'retryCount' ) >= $config->dotGet( 'indexer.job.file.ingest.retries' ) ) {
+ return true;
+ }
+
+ if ( !isset( $this->params['retryCount'] ) ) {
+ $this->params['retryCount'] = 1;
+ } else {
+ $this->params['retryCount']++;
+ }
+
+ if ( !isset( $this->params['createdAt'] ) ) {
+ $this->params['createdAt'] = time();
+ }
+
+ $job = new self( $this->title, $this->params );
+ $job->setDelay( 60 * 10 );
+
+ $job->insert();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Indexer.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Indexer.php
new file mode 100644
index 00000000..98e177a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Indexer.php
@@ -0,0 +1,777 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Psr\Log\LoggerAwareTrait;
+use SMW\Services\ServicesContainer;
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\Store;
+use SMW\Utils\CharArmor;
+use SMWDIBlob as DIBlob;
+use Title;
+use Revision;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Indexer {
+
+ use MessageReporterAwareTrait;
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var ServicesContainer
+ */
+ private $servicesContainer;
+
+ /**
+ * @var FileIndexer
+ */
+ private $fileIndexer;
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @var boolean
+ */
+ private $isRebuild = false;
+
+ /**
+ * @var []
+ */
+ private $versions = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param ServicesContainer $servicesContainer
+ */
+ public function __construct( Store $store, ServicesContainer $servicesContainer ) {
+ $this->store = $store;
+ $this->servicesContainer = $servicesContainer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param [] $versions
+ */
+ public function setVersions( array $versions ) {
+ $this->versions = $versions;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return FileIndexer
+ */
+ public function getFileIndexer() {
+
+ if ( $this->fileIndexer === null ) {
+ $this->fileIndexer = $this->servicesContainer->get( 'FileIndexer', $this );
+ }
+
+ $this->fileIndexer->setLogger(
+ $this->logger
+ );
+
+ return $this->fileIndexer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getId( DIWikiPage $dataItem ) {
+ return $this->store->getObjectIds()->getId( $dataItem );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isAccessible() {
+ return $this->isSafe();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isRebuild
+ */
+ public function isRebuild( $isRebuild = true ) {
+ $this->isRebuild = $isRebuild;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Client
+ */
+ public function getConnection() {
+ return $this->store->getConnection( 'elastic' );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function setup() {
+
+ $rollover = $this->servicesContainer->get(
+ 'Rollover',
+ $this->store->getConnection( 'elastic' )
+ );
+
+ $rollover->update(
+ ElasticClient::TYPE_DATA
+ );
+
+ $rollover->update(
+ ElasticClient::TYPE_LOOKUP
+ );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function drop() {
+
+ $rollover = $this->servicesContainer->get(
+ 'Rollover',
+ $this->store->getConnection( 'elastic' )
+ );
+
+ $rollover->delete(
+ ElasticClient::TYPE_DATA
+ );
+
+ $rollover->delete(
+ ElasticClient::TYPE_LOOKUP
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function getIndexName( $type ) {
+
+ $index = $this->store->getConnection( 'elastic' )->getIndexNameByType(
+ $type
+ );
+
+ // If the rebuilder has set a specific version, use it to avoid writing to
+ // the alias of the index when running a rebuild.
+ if ( isset( $this->versions[$type] ) ) {
+ $index = "$index-" . $this->versions[$type];
+ }
+
+ return $index;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return Bulk
+ */
+ public function newBulk( array $params ) {
+
+ $bulk = $this->servicesContainer->get(
+ 'Bulk',
+ $this->store->getConnection( 'elastic' )
+ );
+
+ $bulk->head( $params );
+
+ return $bulk;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $idList
+ */
+ public function delete( array $idList, $isConcept = false ) {
+
+ if ( $idList === [] ) {
+ return;
+ }
+
+ $title = Title::newFromText( $this->origin . ':' . md5( json_encode( $idList ) ) );
+
+ $params = [
+ 'delete' => $idList
+ ];
+
+ if ( $this->isSafe( $title, $params ) === false ) {
+ return $this->planRecoveryJob( $title, $params );
+ }
+
+ $index = $this->getIndexName(
+ ElasticClient::TYPE_DATA
+ );
+
+ $params = [
+ '_index' => $index,
+ '_type' => ElasticClient::TYPE_DATA
+ ];
+
+ $bulk = $this->newBulk( $params );
+ $time = -microtime( true );
+
+ foreach ( $idList as $id ) {
+
+ $bulk->delete( [ '_id' => $id ] );
+
+ if ( $isConcept ) {
+ $bulk->delete(
+ [
+ '_index' => $this->getIndexName( ElasticClient::TYPE_LOOKUP ),
+ '_type' => ElasticClient::TYPE_LOOKUP,
+ '_id' => md5( $id )
+ ]
+ );
+ }
+ }
+
+ $response = $bulk->execute();
+
+ $this->logger->info(
+ [
+ 'Indexer',
+ 'Deleted list',
+ 'procTime (in sec): {procTime}',
+ 'Response: {response}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->origin,
+ 'procTime' => $time + microtime( true ),
+ 'response' => $response
+ ]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $dataItem
+ * @param array $data
+ */
+ public function create( DIWikiPage $dataItem, array $data = [] ) {
+
+ $title = $dataItem->getTitle();
+
+ $params = [
+ 'create' => $dataItem->getHash()
+ ];
+
+ if ( $this->isSafe() === false ) {
+ return $this->planRecoveryJob( $title, $params );
+ }
+
+ if ( $dataItem->getId() == 0 ) {
+ throw new RuntimeException( "Missing ID: " . $dataItem );
+ }
+
+ $connection = $this->store->getConnection( 'elastic' );
+
+ $params = [
+ 'index' => $this->getIndexName( ElasticClient::TYPE_DATA ),
+ 'type' => ElasticClient::TYPE_DATA,
+ 'id' => $dataItem->getId()
+ ];
+
+ $data['subject'] = [
+ 'title' => str_replace( '_', ' ', $dataItem->getDBKey() ),
+ 'subobject' => $dataItem->getSubobjectName(),
+ 'namespace' => $dataItem->getNamespace(),
+ 'interwiki' => $dataItem->getInterwiki(),
+ 'sortkey' => mb_convert_encoding( $dataItem->getSortKey(), 'UTF-8', 'UTF-8' )
+ ];
+
+ $response = $connection->index( $params + [ 'body' => $data ] );
+
+ $this->logger->info(
+ [
+ 'Indexer',
+ 'Create ({subject}, {id})',
+ 'Response: {response}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->origin,
+ 'subject' => $dataItem->getHash(),
+ 'id' => $dataItem->getId(),
+ 'response' => $response
+ ]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ChangeDiff $changeDiff
+ * @param string $text
+ */
+ public function safeReplicate( ChangeDiff $changeDiff, $text = '' ) {
+
+ $subject = $changeDiff->getSubject();
+
+ $params = [
+ 'index' => $subject->getHash()
+ ];
+
+ if ( $this->isSafe() === false ) {
+ return $this->planRecoveryJob( $subject->getTitle(), $params ) ;
+ }
+
+ $this->index( $changeDiff, $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|Title|integer $id
+ *
+ * @return string
+ */
+ public function fetchNativeData( $id ) {
+
+ if ( $id instanceof DIWikiPage ) {
+ $id = $id->getTitle();
+ }
+
+ if ( $id instanceof Title ) {
+ $id = $id->getLatestRevID( \Title::GAID_FOR_UPDATE );
+ }
+
+ if ( $id == 0 ) {
+ return '';
+ };
+
+ $revision = Revision::newFromId( $id );
+
+ if ( $revision == null ) {
+ return '';
+ };
+
+ $content = $revision->getContent( Revision::RAW );
+
+ return $content->getNativeData();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ChangeDiff $changeDiff
+ * @param string $text
+ */
+ public function index( ChangeDiff $changeDiff, $text = '' ) {
+
+ $time = -microtime( true );
+ $subject = $changeDiff->getSubject();
+
+ $params = [
+ '_index' => $this->getIndexName( ElasticClient::TYPE_DATA ),
+ '_type' => ElasticClient::TYPE_DATA
+ ];
+
+ $bulk = $this->newBulk( $params );
+
+ $this->map_data( $bulk, $changeDiff );
+ $this->map_text( $bulk, $subject, $text );
+
+ $response = $bulk->execute();
+
+ // We always index (not upsert) since we want to have a complete state of
+ // an entity (and ES would delete and insert any document) so trying
+ // to filter and diff the data update has no real merit besides that it
+ // would require us to read each ID in the update from ES and wire the data
+ // back and forth which has shown to be ineffective especially when a
+ // subject has many subobjects.
+ //
+ // The disadvantage is that we loose any auxiliary data that were attached
+ // while not being part of the on-wiki information such as attachment
+ // information from a file ingest.
+ //
+ // In order to reapply those information we could read them in the same
+ // transaction before the actual update but since we expect the
+ // `attachment.content` to contain a large chunk of text, we push that
+ // into the job-queue so that the background process can take of it.
+ //
+ // Of course, this will cause a delay for the file content being searchable
+ // but that should be acceptable to avoid blocking any online transaction.
+ if ( !$this->isRebuild && $subject->getNamespace() === NS_FILE ) {
+ $this->getFileIndexer()->planIngestJob( $subject->getTitle() );
+ }
+
+ $this->logger->info(
+ [
+ 'Indexer',
+ 'Data index completed ({subject})',
+ 'procTime (in sec): {procTime}',
+ 'Response: {response}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->origin,
+ 'subject' => $subject->getHash(),
+ 'procTime' => $time + microtime( true ),
+ 'response' => $response
+ ]
+ );
+ }
+
+ /**
+ * Remove anything that resembles [[:...|foo]] to avoid distracting the indexer
+ * with internal links annotation that are not relevant.
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function removeLinks( $text ) {
+
+ // {{DEFAULTSORT: ... }}
+ $text = preg_replace( "/\\{\\{([^|]+?)\\}\\}/", "", $text );
+ $text = preg_replace( '/\\[\\[[\s\S]+?::/', '[[', $text );
+
+ // [[:foo|bar]]
+ $text = preg_replace( '/\\[\\[:[^|]+?\\|/', '[[', $text );
+ $text = preg_replace( "/\\{\\{([^|]+\\|)(.*?)\\}\\}/", "\\2", $text );
+ $text = preg_replace( "/\\[\\[([^|]+?)\\]\\]/", "\\1", $text );
+
+ // [[Has foo::Bar]]
+ // $text = \SMW\Parser\LinksEncoder::removeAnnotation( $text );
+
+ return $text;
+ }
+
+ private function isSafe() {
+
+ $connection = $this->store->getConnection( 'elastic' );
+
+ // Make sure a node is available and is not locked by the rebuilder
+ if ( !$connection->hasLock( ElasticClient::TYPE_DATA ) && $connection->ping() ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function planRecoveryJob( $title, array $params ) {
+
+ $indexerRecoveryJob = new IndexerRecoveryJob(
+ $title,
+ $params
+ );
+
+ $indexerRecoveryJob->insert();
+
+ $this->logger->info(
+ [
+ 'Indexer',
+ 'Insert IndexerRecoveryJob: {subject}',
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'origin' => $this->origin,
+ 'subject' => $title->getPrefixedDBKey()
+ ]
+ );
+ }
+
+ private function map_text( $bulk, $subject, $text ) {
+
+ if ( $text === '' ) {
+ return;
+ }
+
+ $id = $subject->getId();
+
+ if ( $id == 0 ) {
+ $id = $this->store->getObjectIds()->getSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ true
+ );
+ }
+
+ $bulk->upsert(
+ [
+ '_index' => $this->getIndexName( ElasticClient::TYPE_DATA ),
+ '_type' => ElasticClient::TYPE_DATA,
+ '_id' => $id
+ ],
+ [
+ 'text_raw' => $this->removeLinks( $text )
+ ]
+ );
+ }
+
+ private function map_data( $bulk, $changeDiff ) {
+
+ $dbType = $this->store->getInfo( 'db' );
+ $unescape_bytea = isset( $dbType['postgres'] );
+
+ $inserts = [];
+ $inverted = [];
+
+ // In the event that a _SOBJ (or hereafter any inherited object)
+ // is deleted, remove the reference directly from the index since
+ // the object is embedded and is therefore handled outside of the
+ // normal wikiPage delete action
+ foreach ( $changeDiff->getTableChangeOps() as $tableChangeOp ) {
+ foreach ( $tableChangeOp->getFieldChangeOps( ChangeOp::OP_DELETE ) as $fieldChangeOp ) {
+
+ if ( !$fieldChangeOp->has( 'o_id' ) ) {
+ continue;
+ }
+
+ $bulk->delete( [ '_id' => $fieldChangeOp->get( 'o_id' ) ] );
+ }
+ }
+
+ $propertyList = $changeDiff->getPropertyList( 'id' );
+
+ foreach ( $changeDiff->getDataOps() as $tableChangeOp ) {
+ foreach ( $tableChangeOp->getFieldChangeOps() as $fieldChangeOp ) {
+
+ if ( !$fieldChangeOp->has( 's_id' ) ) {
+ continue;
+ }
+
+ $this->mapRows( $fieldChangeOp, $propertyList, $inserts, $inverted, $unescape_bytea );
+ }
+ }
+
+ foreach ( $inverted as $id => $update ) {
+ $bulk->upsert( [ '_id' => $id ], $update );
+ }
+
+ foreach ( $inserts as $id => $value ) {
+ $bulk->index( [ '_id' => $id ], $value );
+ }
+ }
+
+ private function mapRows( $fieldChangeOp, $propertyList, &$insertRows, &$invertedRows, $unescape_bytea ) {
+
+ // The structure to be expected in ES:
+ //
+ // "subject": {
+ // "title": "Foaf:knows",
+ // "subobject": "",
+ // "namespace": 102,
+ // "interwiki": "",
+ // "sortkey": "Foaf:knows"
+ // },
+ // "P:8": {
+ // "txtField": [
+ // "foaf knows http://xmlns.com/foaf/0.1/ Type:Page"
+ // ]
+ // },
+ // "P:29": {
+ // "datField": [
+ // 2458150.6958333
+ // ]
+ // },
+ // "P:1": {
+ // "uriField": [
+ // "http://semantic-mediawiki.org/swivt/1.0#_wpg"
+ // ]
+ // }
+
+ // - datField (time value) is a numeric field (JD number) to allow using
+ // ranges on dates with values being representable from January 1, 4713 BC
+ // (proleptic Julian calendar)
+
+ $sid = $fieldChangeOp->get( 's_id' );
+
+ if ( !isset( $insertRows[$sid] ) ) {
+ $insertRows[$sid] = [];
+ }
+
+ if ( !isset( $insertRows[$sid]['subject'] ) ) {
+ $dataItem = $this->store->getObjectIds()->getDataItemById( $sid );
+ $sort = $dataItem->getSortKey();
+
+ // Use collated sort field if available
+ if ( $dataItem->getOption( 'sort', '' ) !== '' ) {
+ $sort = $dataItem->getOption( 'sort' );
+ }
+
+ // Avoid issue with the Ealstic serializer
+ $sort = CharArmor::removeSpecialChars(
+ CharArmor::removeControlChars( $sort )
+ );
+
+ $insertRows[$sid]['subject'] = [
+ 'title' => str_replace( '_', ' ', $dataItem->getDBKey() ),
+ 'subobject' => $dataItem->getSubobjectName(),
+ 'namespace' => $dataItem->getNamespace(),
+ 'interwiki' => $dataItem->getInterwiki(),
+ 'sortkey' => $sort
+ ];
+ }
+
+ // Avoid issues where the p_id is unknown as in case of an empty
+ // concept (red linked) as reference
+ if ( !$fieldChangeOp->has( 'p_id' ) ) {
+ return;
+ }
+
+ $ins = $fieldChangeOp->getChangeOp();
+ $pid = $fieldChangeOp->get( 'p_id' );
+
+ $prop = isset( $propertyList[$pid] ) ? $propertyList[$pid] : [];
+
+ $pid = 'P:' . $pid;
+ unset( $ins['s_id'] );
+
+ $val = 'n/a';
+ $type = 'wpgField';
+
+ if ( $fieldChangeOp->has( 'o_blob' ) && $fieldChangeOp->has( 'o_hash' ) ) {
+ $type = 'txtField';
+ $val = $ins['o_blob'] === null ? $ins['o_hash'] : $ins['o_blob'];
+
+ // Postgres requires special handling of blobs otherwise escaped
+ // text elements are used as index input
+ // Tests: P9010, Q0704, Q1206, and Q0103
+ if ( $unescape_bytea && $ins['o_blob'] !== null ) {
+ $val = pg_unescape_bytea( $val );
+ }
+
+ // #3020, 3035
+ if ( isset( $prop['_type'] ) && $prop['_type'] === '_keyw' ) {
+ $val = DIBlob::normalize( $ins['o_hash'] );
+ }
+
+ // Remove control chars and avoid Elasticsearch to throw a
+ // "SmartSerializer.php: Failed to JSON encode: 5" since JSON requires
+ // valid UTF-8
+ $val = $this->removeLinks( mb_convert_encoding( $val, 'UTF-8', 'UTF-8' ) );
+ } elseif ( $fieldChangeOp->has( 'o_serialized' ) && $fieldChangeOp->has( 'o_blob' ) ) {
+ $type = 'uriField';
+ $val = $ins['o_blob'] === null ? $ins['o_serialized'] : $ins['o_blob'];
+
+ if ( $unescape_bytea && $ins['o_blob'] !== null ) {
+ $val = pg_unescape_bytea( $val );
+ }
+
+ } elseif ( $fieldChangeOp->has( 'o_serialized' ) && $fieldChangeOp->has( 'o_sortkey' ) ) {
+ $type = strpos( $ins['o_serialized'], '/' ) !== false ? 'datField' : 'numField';
+ $val = (float)$ins['o_sortkey'];
+ } elseif ( $fieldChangeOp->has( 'o_value' ) ) {
+ $type = 'booField';
+ // Avoid a "Current token (VALUE_NUMBER_INT) not of boolean type ..."
+ $val = $ins['o_value'] ? true : false;
+ } elseif ( $fieldChangeOp->has( 'o_lat' ) ) {
+ // https://www.elastic.co/guide/en/elasticsearch/reference/6.1/geo-point.html
+ // Geo-point expressed as an array with the format: [ lon, lat ]
+ // Geo-point expressed as a string with the format: "lat,lon".
+ $type = 'geoField';
+ $val = $ins['o_serialized'];
+ } elseif ( $fieldChangeOp->has( 'o_id' ) ) {
+ $type = 'wpgField';
+ $dataItem = $this->store->getObjectIds()->getDataItemById( $ins['o_id'] );
+
+ $val = $dataItem->getSortKey();
+ $val = mb_convert_encoding( $val, 'UTF-8', 'UTF-8' );
+
+ if ( !isset( $insertRows[$sid][$pid][$type] ) ) {
+ $insertRows[$sid][$pid][$type] = [];
+ }
+
+ $insertRows[$sid][$pid][$type] = array_merge( $insertRows[$sid][$pid][$type], [ $val ] );
+ $type = 'wpgID';
+ $val = (int)$ins['o_id'];
+
+ // Create a minimal body for an inverted relation
+ //
+ // When a query `[[-Has mother::Michael]]` inquiries that relationship
+ // on the fact of `Michael` -> `[[Has mother::Carol]] with `Carol`
+ // being redlinked (not exists as page) the query can match the object
+ if ( !isset( $invertedRows[$val] ) ) {
+
+ // Ensure we have something to sort on
+ // See also Q0105#8
+ $subject = [
+ 'title' => str_replace( '_', ' ', $dataItem->getDBKey() ),
+ 'subobject' => $dataItem->getSubobjectName(),
+ 'namespace' => $dataItem->getNamespace(),
+ 'interwiki' => $dataItem->getInterwiki(),
+ 'sortkey' => mb_convert_encoding( $dataItem->getSortKey(), 'UTF-8', 'UTF-8' )
+ ];
+
+ $invertedRows[$val] = [ 'subject' => $subject ];
+ }
+
+ // A null, [] (an empty array), and [null] are all equivalent, they
+ // simply don't exists in an inverted index
+ }
+
+ if ( !isset( $insertRows[$sid][$pid][$type] ) ) {
+ $insertRows[$sid][$pid][$type] = [];
+ }
+
+ $insertRows[$sid][$pid][$type] = array_merge(
+ $insertRows[$sid][$pid][$type],
+ [ $val ]
+ );
+
+ // Replicate dates in the serialized raw_format to give aggregations a chance
+ // to filter dates by term
+ if ( $type === 'datField' && isset( $ins['o_serialized'] ) ) {
+
+ if ( !isset( $insertRows[$sid][$pid]["dat_raw"] ) ) {
+ $insertRows[$sid][$pid]["dat_raw"] = [];
+ }
+
+ $insertRows[$sid][$pid]["dat_raw"][] = $ins['o_serialized'];
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/IndexerRecoveryJob.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/IndexerRecoveryJob.php
new file mode 100644
index 00000000..7304fc1d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/IndexerRecoveryJob.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Job;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\ElasticFactory;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IndexerRecoveryJob extends Job {
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.elasticIndexerRecovery', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 3.0
+ */
+ public function allowRetries() {
+ return false;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 3.0
+ */
+ public function run() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+
+ $connection = $store->getConnection( 'elastic' );
+
+ // Make sure a node is available
+ if ( $connection->hasLock( ElasticClient::TYPE_DATA ) || !$connection->ping() ) {
+
+ if ( $connection->hasLock( ElasticClient::TYPE_DATA ) ) {
+ $this->params['retryCount'] = 0;
+ }
+
+ return $this->requeueRetry( $connection->getConfig() );
+ }
+
+ $elasticFactory = $applicationFactory->singleton( 'ElasticFactory' );
+
+ $this->indexer = $elasticFactory->newIndexer(
+ $store
+ );
+
+ $this->indexer->setOrigin( __METHOD__ );
+
+ $this->indexer->setLogger(
+ $applicationFactory->getMediaWikiLogger( 'smw-elastic' )
+ );
+
+ if ( $this->hasParameter( 'delete' ) ) {
+ $this->delete( $this->getParameter( 'delete' ) );
+ }
+
+ if ( $this->hasParameter( 'create' ) ) {
+ $this->create( $this->getParameter( 'create' ) );
+ }
+
+ if ( $this->hasParameter( 'index' ) ) {
+ $this->index(
+ $connection,
+ $applicationFactory->getCache(),
+ $this->getParameter( 'index' )
+ );
+ }
+
+ return true;
+ }
+
+ private function requeueRetry( $config ) {
+
+ // Give up!
+ if ( $this->getParameter( 'retryCount' ) >= $config->dotGet( 'indexer.job.recovery.retries' ) ) {
+ return true;
+ }
+
+ if ( !isset( $this->params['retryCount'] ) ) {
+ $this->params['retryCount'] = 1;
+ } else {
+ $this->params['retryCount']++;
+ }
+
+ if ( !isset( $this->params['createdAt'] ) ) {
+ $this->params['createdAt'] = time();
+ }
+
+ $job = new self( $this->title, $this->params );
+ $job->setDelay( 60 * 10 );
+
+ $job->insert();
+ }
+
+ private function delete( array $idList ) {
+ $this->indexer->delete( $idList );
+ }
+
+ private function create( $hash ) {
+ $this->indexer->create( DIWikiPage::doUnserialize( $hash ) );
+ }
+
+ private function index( $connection, $cache, $hash ) {
+
+ $subject = DIWikiPage::doUnserialize( $hash );
+ $text = '';
+
+ $changeDiff = ChangeDiff::fetch(
+ $cache,
+ $subject
+ );
+
+ if ( $connection->getConfig()->dotGet( 'indexer.raw.text', false ) ) {
+ $text = $this->indexer->fetchNativeData( $subject );
+ }
+
+ if ( $changeDiff !== false ) {
+ $this->indexer->index( $changeDiff, $text );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rebuilder.php
new file mode 100644
index 00000000..272f685c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rebuilder.php
@@ -0,0 +1,421 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use Exception;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableRowMapper;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Rebuilder {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * @var ElasticClient
+ */
+ private $client;
+
+ /**
+ * @var Indexer
+ */
+ private $indexer;
+
+ /**
+ * @var PropertyTableRowMapper
+ */
+ private $propertyTableRowMapper;
+
+ /**
+ * @var Rollover
+ */
+ private $rollover;
+
+ /**
+ * @var FileIndexer
+ */
+ private $fileIndexer;
+
+ /**
+ * @var array
+ */
+ private $settings = [];
+
+ /**
+ * @var array
+ */
+ private $versions = [];
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $client
+ * @param Indexer $indexer
+ * @param PropertyTableRowMapper $propertyTableRowMapper
+ * @param Rollover $rollover
+ */
+ public function __construct( ElasticClient $client, Indexer $indexer, PropertyTableRowMapper $propertyTableRowMapper, Rollover $rollover ) {
+ $this->client = $client;
+ $this->indexer = $indexer;
+ $this->propertyTableRowMapper = $propertyTableRowMapper;
+ $this->rollover = $rollover;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function ping() {
+ return $this->client->ping();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param array $conditions
+ *
+ * @return array
+ */
+ public function select( Store $store, array $conditions ) {
+
+ $connection = $store->getConnection( 'mw.db' );
+
+ $res = $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_iw'
+ ],
+ $conditions,
+ __METHOD__,
+ [ 'ORDER BY' => 'smw_id' ]
+ );
+
+ $last = $connection->selectField(
+ SQLStore::ID_TABLE,
+ 'MAX(smw_id)',
+ '',
+ __METHOD__
+ );
+
+ return [ $res, $last ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function rollover() {
+
+ if ( $this->versions === [] ) {
+ return false;
+ }
+
+ $this->rollover_version(
+ ElasticClient::TYPE_DATA,
+ $this->versions[ElasticClient::TYPE_DATA]
+ );
+
+ $this->rollover_version(
+ ElasticClient::TYPE_LOOKUP,
+ $this->versions[ElasticClient::TYPE_LOOKUP]
+ );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function prepare() {
+ $this->prepare_index( ElasticClient::TYPE_DATA );
+ $this->prepare_index( ElasticClient::TYPE_LOOKUP );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function deleteAndSetupIndices() {
+
+ $this->messageReporter->reportMessage( "\n ... deleting indices and aliases ..." );
+ $this->indexer->drop();
+
+ $this->messageReporter->reportMessage( "\n ... setting up indices and aliases ..." );
+ $this->indexer->setup();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function createIndices() {
+ $this->create_index( ElasticClient::TYPE_DATA );
+ $this->create_index( ElasticClient::TYPE_LOOKUP );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function setDefaults() {
+
+ if ( !$this->client->hasIndex( ElasticClient::TYPE_DATA ) ) {
+ return false;
+ }
+
+ $this->messageReporter->reportMessage( "\n" . ' ... updating settings and mappings ...' );
+
+ $this->set_default( ElasticClient::TYPE_DATA );
+ $this->set_default( ElasticClient::TYPE_LOOKUP );
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ */
+ public function delete( $id ) {
+
+ $index = $this->client->getIndexName( ElasticClient::TYPE_DATA );
+
+ if ( isset( $this->versions[ElasticClient::TYPE_DATA] ) ) {
+ $index = $index . '-' . $this->versions[ElasticClient::TYPE_DATA];
+ }
+
+ $params = [
+ 'index' => $index,
+ 'type' => ElasticClient::TYPE_DATA,
+ 'id' => $id
+ ];
+
+ try {
+ $this->client->delete( $params );
+ } catch ( Exception $e ) {
+ // Do nothing
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param SemanticData $semanticData
+ */
+ public function rebuild( $id, SemanticData $semanticData ) {
+
+ if ( $this->fileIndexer === null ) {
+ $skip = false;
+
+ if ( isset( $this->options['skip-fileindex'] ) ) {
+ $skip = (bool)$this->options['skip-fileindex'];
+ }
+
+ if ( !$skip && $this->client->getConfig()->dotGet( 'indexer.experimental.file.ingest', false ) ) {
+ $this->fileIndexer = $this->indexer->getFileIndexer();
+ } else {
+ $this->fileIndexer = false;
+ }
+ }
+
+ $changeOp = $this->propertyTableRowMapper->newChangeOp(
+ $id,
+ $semanticData
+ );
+
+ $dataItem = $semanticData->getSubject();
+ $dataItem->setId( $id );
+
+ $this->indexer->setVersions( $this->versions );
+ $this->indexer->isRebuild();
+
+ $this->indexer->index(
+ $changeOp->newChangeDiff(),
+ $this->raw_text( $dataItem )
+ );
+
+ if ( $this->fileIndexer && $dataItem->getNamespace() === NS_FILE ) {
+ $this->fileIndexer->noSha1Check();
+ $this->fileIndexer->index( $dataItem, null );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function refresh() {
+
+ if ( !$this->client->hasIndex( ElasticClient::TYPE_DATA ) ) {
+ return false;
+ }
+
+ $this->messageReporter->reportMessage( "\n" . ' ... refreshing indices ...' );
+
+ $this->refresh_index( ElasticClient::TYPE_DATA );
+ $this->refresh_index( ElasticClient::TYPE_LOOKUP );
+
+ return true;
+ }
+
+ private function raw_text( $dataItem ) {
+
+ if ( !$this->client->getConfig()->dotGet( 'indexer.raw.text', false ) || $dataItem->getSubobjectName() !== '' ) {
+ return '';
+ }
+
+ if ( ( $title = $dataItem->getTitle() ) !== null ) {
+ return $this->indexer->fetchNativeData( $title );
+ }
+
+ return '';
+ }
+
+ private function prepare_index( $type ) {
+
+ $index = $this->client->getIndexName( $type );
+
+ if ( isset( $this->versions[$type] ) ) {
+ $index = "$index-" . $this->versions[$type];
+ }
+
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
+ $params = [
+ 'index' => $index,
+ 'body' => [
+ 'settings' => [
+ 'number_of_replicas' => 0,
+ 'refresh_interval' => -1
+ ]
+ ]
+ ];
+
+ $this->client->putSettings( $params );
+ }
+
+ private function refresh_index( $type ) {
+ $this->client->refresh( [ 'index' => $this->client->getIndexName( $type ) ] );
+ }
+
+ private function set_default( $type ) {
+
+ $indices = $this->client->indices();
+
+ $index = $this->client->getIndexName(
+ $type
+ );
+
+ $this->messageReporter->reportMessage( "\n ... '$type' index ... " );
+
+ if ( $this->client->hasLock( $type ) ) {
+ $this->rollover_version( $type, $this->client->getLock( $type ) );
+ }
+
+ $this->messageReporter->reportMessage( "\n ... closing" );
+
+ // Certain changes ( ... to define new analyzers ...) requires to close
+ // and reopen an index
+ $indices->close( [ 'index' => $index ] );
+
+ $indexDef = $this->client->getIndexDefByType(
+ $type
+ );
+
+ $indexDef = json_decode( $indexDef, true );
+
+ // Cannot be altered by a simple settings update and requires a complete
+ // rebuild
+ unset( $indexDef['settings']['number_of_shards'] );
+
+ $params = [
+ 'index' => $index,
+ 'body' => [
+ 'settings' => $indexDef['settings']
+ ]
+ ];
+
+ $this->client->putSettings( $params );
+
+ $params = [
+ 'index' => $index,
+ 'type' => $type,
+ 'body' => $indexDef['mappings']
+ ];
+
+ $this->client->putMapping( $params );
+
+ $this->messageReporter->reportMessage( ", reopening the index ... " );
+ $indices->open( [ 'index' => $index ] );
+
+
+ $this->client->releaseLock( $type );
+ }
+
+ private function create_index( $type ) {
+
+ // If for some reason a recent rebuild didn't finish, use
+ // the locked version as master
+ if ( ( $version = $this->client->getLock( $type ) ) === false ) {
+ $version = $this->client->createIndex( $type );
+ }
+
+ if ( !$this->client->hasIndex( $type ) ) {
+ $version = $this->client->createIndex( $type );
+ }
+
+ $index = $this->client->getIndexName( $type );
+ $indices = $this->client->indices();
+
+ // No Alias available, create one before the rollover
+ if ( !$indices->exists( [ 'index' => "$index" ] ) ) {
+ $actions = [
+ [ 'add' => [ 'index' => "$index-$version", 'alias' => $index ] ]
+ ];
+
+ $params['body'] = [ 'actions' => $actions ];
+
+ $indices->updateAliases( $params );
+ }
+
+ $this->versions[$type] = $version;
+ $this->client->setLock( $type, $version );
+ }
+
+ private function rollover_version( $type, $version ) {
+
+ $old = $this->rollover->rollover(
+ $type,
+ $version
+ );
+
+ $this->messageReporter->reportMessage(
+ "\n" . sprintf( " ... switching index version from %s to %s (rollover) ...", $old, $version )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/ReplicationStatus.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/ReplicationStatus.php
new file mode 100644
index 00000000..3e59c6e4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/ReplicationStatus.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMWDITime as DITime;
+use SMW\DIProperty;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ReplicationStatus {
+
+ /**
+ * @var ElasticClient
+ */
+ private $elasticClient;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $elasticClient
+ */
+ public function __construct( ElasticClient $connection ) {
+ $this->connection = $connection;
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function get( $key ) {
+
+ if ( !is_callable( [ $this, $key ] ) ) {
+ throw new RuntimeException( "`$key` as accessor is unknown!" );
+ }
+
+ return $this->{$key}();
+ }
+
+ /**
+ * @since 3.0
+ */
+ private function refresh_interval() {
+
+ $refresh_interval = null;
+
+ $settings = $this->connection->getSettings(
+ [
+ 'index' => $this->connection->getIndexName( ElasticClient::TYPE_DATA )
+ ]
+ );
+
+ foreach ( $settings as $key => $value ) {
+ if ( isset( $value['settings']['index']['refresh_interval'] ) ) {
+ $refresh_interval = $value['settings']['index']['refresh_interval'];
+ }
+ }
+
+ return $refresh_interval;
+ }
+
+ /**
+ * @since 3.0
+ */
+ private function last_update() {
+
+ $pid = $this->fieldMapper->getPID( \SMWSql3SmwIds::$special_ids['_MDAT'] );
+ $field = $this->fieldMapper->getField( new DIProperty( '_MDAT' ) );
+
+ $params = $this->fieldMapper->exists( "$pid.$field" );
+
+ $body = [
+ '_source' => [ "$pid.$field", "subject" ],
+ 'size' => 1,
+ 'query' => $params,
+ 'sort' => [ "$pid.$field" => [ 'order' => 'desc' ] ]
+ ];
+
+ $params = [
+ 'index' => $this->connection->getIndexName( ElasticClient::TYPE_DATA ),
+ 'type' => ElasticClient::TYPE_DATA,
+ 'body' => $body
+ ];
+
+ list( $res, $errors ) = $this->connection->search( $params );
+ $time = null;
+
+ foreach ( $res as $result ) {
+
+ if ( !isset( $result['hits'] ) ) {
+ continue;
+ }
+
+ foreach ( $result['hits'] as $key => $value ) {
+ foreach ( $value as $key => $v ) {
+ if ( $key === '_source' ) {
+ $time = DITime::newFromJD( end( $v[$pid][$field] ) );
+ }
+ }
+ }
+ }
+
+ if ( $time !== null ) {
+ $time = $time->asDateTime()->format( 'Y-m-d H:i:s' );
+ } else {
+ $time = '0000-00-00 00:00:00';
+ }
+
+ return $time;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rollover.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rollover.php
new file mode 100644
index 00000000..dcd10da8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Indexer/Rollover.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Elastic\Indexer;
+
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\Exception\NoConnectionException;
+
+/**
+ * The index uses V1/V2 to switch between versions during a rebuild allowing the
+ * index to be available while a reindex is on going and after the process has
+ * been indices will be switched (aka rollover) without down time. The index uses
+ * aliases to hide the "real" identity of the current active index.
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-rollover-index.html
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Rollover {
+
+ /**
+ * @var ElasticClient
+ */
+ private $connection;
+
+ /**
+ * @since 3.0
+ *
+ * @param ElasticClient $connection
+ */
+ public function __construct( ElasticClient $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param string $version
+ *
+ * @return string
+ */
+ public function rollover( $type, $version ) {
+
+ $index = $this->connection->getIndexNameByType( $type );
+ $indices = $this->connection->indices();
+
+ $params = [];
+ $actions = [];
+
+ $old = $version === 'v2' ? 'v1' : 'v2';
+ $check = false;
+
+ if ( $indices->exists( [ 'index' => "$index-$old" ] ) ) {
+ $actions = [
+ [ 'remove' => [ 'index' => "$index-$old", 'alias' => $index ] ],
+ [ 'add' => [ 'index' => "$index-$version", 'alias' => $index ] ]
+ ];
+
+ $check = true;
+ } else {
+ // No old index
+ $old = $version;
+
+ $actions = [
+ [ 'add' => [ 'index' => "$index-$version", 'alias' => $index ] ]
+ ];
+ }
+
+ $params['body'] = [ 'actions' => $actions ];
+
+ $indices->updateAliases( $params );
+
+ if ( $check && $indices->exists( [ 'index' => "$index-$old" ] ) ) {
+ $indices->delete( [ "index" => "$index-$old" ] );
+ }
+
+ $this->connection->releaseLock( $type );
+
+ return $old;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @throws NoConnectionException
+ */
+ public function update( $type ) {
+
+ // Fail hard since we expect to create an index but are unable to do so!
+ if ( !$this->connection->ping() ) {
+ throw new NoConnectionException();
+ }
+
+ $indices = $this->connection->indices();
+
+ $index = $this->connection->getIndexName(
+ $type
+ );
+
+ // Shouldn't happen but just in case where the root index is
+ // used as index but not an alias
+ if ( $indices->exists( [ 'index' => "$index" ] ) && !$indices->existsAlias( [ 'name' => "$index" ] ) ) {
+ $indices->delete( [ 'index' => "$index" ] );
+ }
+
+ // Check v1/v2 and if both exists (which shouldn't happen but most likely
+ // caused by an unfinshed rebuilder run) then use v1 as master
+ if ( $indices->exists( [ 'index' => "$index-v1" ] ) ) {
+
+ // Just in case
+ if ( $indices->exists( [ 'index' => "$index-v2" ] ) ) {
+ $indices->delete( [ 'index' => "$index-v2" ] );
+ }
+
+ $actions[] = [ 'add' => [ 'index' => "$index-v1", 'alias' => $index ] ];
+ } elseif ( $indices->exists( [ 'index' => "$index-v2" ] ) ) {
+ $actions[] = [ 'add' => [ 'index' => "$index-v2", 'alias' => $index ] ];
+ } else {
+ $version = $this->connection->createIndex( $type );
+
+ $actions = [
+ [ 'add' => [ 'index' => "$index-$version", 'alias' => $index ] ]
+ ];
+ }
+
+ $params['body'] = [ 'actions' => $actions ];
+ $indices->updateAliases( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @throws NoConnectionException
+ */
+ public function delete( $type ) {
+
+ // Fail hard since we expect to delete an index but are unable to do so!
+ if ( !$this->connection->ping() ) {
+ throw new NoConnectionException();
+ }
+
+ $indices = $this->connection->indices();
+
+ $index = $this->connection->getIndexName(
+ $type
+ );
+
+ if ( $indices->exists( [ 'index' => "$index-v1" ] ) ) {
+ $indices->delete( [ 'index' => "$index-v1" ] );
+ }
+
+ if ( $indices->exists( [ 'index' => "$index-v2" ] ) ) {
+ $indices->delete( [ 'index' => "$index-v2" ] );
+ }
+
+ if ( $indices->exists( [ 'index' => "$index" ] ) && !$indices->existsAlias( [ 'name' => "$index" ] ) ) {
+ $indices->delete( [ 'index' => "$index" ] );
+ }
+
+ $this->connection->releaseLock( $type );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Lookup/ProximityPropertyValueLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Lookup/ProximityPropertyValueLookup.php
new file mode 100644
index 00000000..ac5005ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/Lookup/ProximityPropertyValueLookup.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace SMW\Elastic\Lookup;
+
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMWDITime as DITime;
+use SMWDataItem as DataItem;
+use SMW\DIProperty;
+use SMW\Store;
+use SMW\RequestOptions;
+use RuntimeException;
+
+/**
+ * Experimental implementation to showcase how a Elasticsearch specific implementation
+ * for a property value lookup can be used and override the default SQL service.
+ *
+ * The class is targeted to be used for API (e.g. autocomplete etc.) intensive
+ * services.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ProximityPropertyValueLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param string $value
+ * @param RequestOptions $opts
+ *
+ * @return array
+ */
+ public function lookup( DIProperty $property, $value = '', RequestOptions $opts ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+ $continueOffset = 0;
+
+ $pid = $this->fieldMapper->getPID(
+ $this->store->getObjectIds()->getSMWPropertyID( $property )
+ );
+
+ $diType = DataTypeRegistry::getInstance()->getDataItemByType(
+ $property->findPropertyTypeID()
+ );
+
+ $field = $this->fieldMapper->getField( $property );
+
+ if ( $value === '' ) {
+ // Just create a list of available values where the property exists
+ $params = $this->fieldMapper->exists( "$pid.$field" );
+
+ // Increase the range of the initial match since a property field
+ // stores are all sorts of values, this is to make sure that the
+ // aggregation has enough objects available to build a selection
+ // list that satisfies the RequestOptions::getLimit
+ $limit = 500;
+ } elseif( $diType === DataItem::TYPE_TIME ) {
+ $limit = 500;
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ $value
+ );
+
+ $params = $this->fieldMapper->bool(
+ 'must',
+ $this->fieldMapper->range( "$pid.$field", $dataValue->getDataItem()->getJD(), SMW_CMP_GEQ )
+ );
+ } elseif( $diType === DataItem::TYPE_NUMBER ) {
+ $limit = 500;
+
+ if ( strpos( $value, '*' ) === false ) {
+ $value = "*$value*";
+ }
+
+ $params = $this->fieldMapper->bool(
+ 'must',
+ $this->fieldMapper->wildcard( "$pid.$field.keyword", $value )
+ );
+ } else {
+ $limit = 500;
+
+ if ( strpos( $value, '*' ) === false ) {
+ $value = "$value*";
+ }
+
+ $params = $this->fieldMapper->bool(
+ 'must',
+ $this->fieldMapper->match_phrase( "$pid.$field", $value )
+ );
+ }
+
+ $body = [
+ '_source' => [ "$pid.$field" ],
+ 'from' => $opts->getOffset(),
+ 'size' => $limit,
+ 'query' => $params
+ ];
+
+ $limit = $opts->getLimit() + 1;
+
+ // Aggregation is used to filter a specific value aspect from a property
+ // field contents
+ if ( $value !== '' ) {
+ // Setting size to 0 which avoids executing the fetch query of the search
+ // hereby making the request more efficient.
+ $body['size'] = 0;
+
+ $body += $this->aggs_filter( $diType, $pid, $field, $limit, $property, trim( $value, '*' ) );
+ }
+
+ if ( $opts->sort ) {
+ $body += [ 'sort' => [ "$pid.$field" => [ 'order' => $opts->sort ] ] ];
+ }
+
+ $params = [
+ 'index' => $connection->getIndexName( ElasticClient::TYPE_DATA ),
+ 'type' => ElasticClient::TYPE_DATA,
+ 'body' => $body
+ ];
+
+ list( $res, $errors ) = $connection->search( $params );
+
+ if ( isset( $res['aggregations'] ) ) {
+ list( $list, $i ) = $this->match_aggregations( $res['aggregations'], $diType, $limit );
+ } elseif ( isset( $res['hits'] ) ) {
+ list( $list, $i ) = $this->match_hits( $res['hits'], $pid, $field, $limit );
+ } else {
+ $list = [];
+ $i = 0;
+ }
+
+ if ( $list !== [] ) {
+ $list = array_values( $list );
+
+ if ( $diType === DataItem::TYPE_TIME ) {
+ foreach ( $list as $key => $value ) {
+
+ if ( strpos( $value, '/' ) !== false ) {
+ $dataItem = DITime::doUnserialize( $value );
+ } else {
+ $dataItem = DITime::newFromJD( $value );
+ }
+
+ $list[$key] = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $property )->getWikiValue();
+ }
+ }
+ }
+
+ return $list;
+ }
+
+ private function aggs_filter( $diType, $pid, $field, $limit, $property, $value ) {
+
+ // A field on ES to a property can can all different kind of values and
+ // the request is only interested in those values that match a certain
+ // prefix or affix hence use `include` to only return aggregated values
+ // that contain the search term or value
+
+ if ( $diType === DataItem::TYPE_TIME ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ $value
+ );
+
+ return [
+ 'aggs' => [
+ 'value_terms' => [
+ 'terms' => [
+ 'field' => "$pid.dat_raw",
+ 'size' => $limit,
+ "order" => [ "_key" => "asc" ],
+ 'include' => $dataValue->getDataItem()->getSerialization() . ".*"
+ ]
+ ]
+ ]
+ ];
+ }
+
+ if ( $diType === DataItem::TYPE_NUMBER ) {
+ return [
+ 'aggs' => [
+ 'value_terms' => [
+ 'terms' => [
+ 'field' => "$pid.$field.keyword",
+ 'size' => $limit,
+ "order" => [ "_key" => "asc" ],
+ 'include' => ".*" . $value . ".*"
+ ]
+ ]
+ ]
+ ];
+ }
+
+ return [
+ 'aggs' => [
+ 'value_terms' => [
+ 'terms' => [
+ 'field' => "$pid.$field.keyword",
+ 'size' => $limit,
+ 'include' =>
+ ".*" . $value . ".*|" .
+ ".*" . ucfirst( $value ) . ".*|" .
+ ".*" . mb_strtoupper( $value ) . ".*"
+ ]
+ ]
+ ]
+ ];
+ }
+
+ private function match_aggregations( $res, $diType, $limit ) {
+
+ $isNumeric = $diType === DataItem::TYPE_NUMBER;
+ $list = [];
+ $i = 0;
+
+ foreach ( $res as $aggs ) {
+ foreach ( $aggs as $val ) {
+
+ if ( !is_array( $val ) ) {
+ continue;
+ }
+
+ foreach ( $val as $v ) {
+
+ if ( $i >= $limit ) {
+ break;
+ }
+
+ if ( isset( $v['key'] ) ) {
+ $val = (string)$v['key'];
+
+ // Aggregation happens on keyword field, numerics are of type
+ // double hence is coerced as 5 -> 5.0
+ if ( $isNumeric && substr( $val, -2 ) === '.0' ) {
+ $val = substr( $val, 0, -2 );
+ }
+
+ $list[] = $val;
+ $i++;
+ }
+ }
+ }
+ }
+
+ return [ $list, $i ];
+ }
+
+ private function match_hits( $res, $pid, $field, $limit ) {
+
+ $list = [];
+ $i = 0;
+
+ foreach ( $res as $key => $value ) {
+
+ if ( $key !== 'hits' ) {
+ continue;
+ }
+
+ foreach ( $value as $v ) {
+
+ if ( !isset( $v['_source'][$pid][$field] ) ) {
+ continue;
+ }
+
+ foreach ( $v['_source'][$pid][$field] as $match ) {
+
+ if ( $i >= $limit ) {
+ break;
+ }
+
+ // Filter duplicates
+ $hash = md5( $match );
+
+ if ( isset( $list[$hash] ) ) {
+ continue;
+ }
+
+ $list[$hash] = (string)$match;
+ $i++;
+ }
+ }
+ }
+
+ return [ $list, $i ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Aggregations.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Aggregations.php
new file mode 100644
index 00000000..68beb5d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Aggregations.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Aggregations {
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @var array
+ */
+ private $subAggregations = [];
+
+ /**
+ * @var boolean
+ */
+ private $plain = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Aggregations|array $parameters
+ */
+ public function __construct( $parameters = [] ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Aggregations $aggregations
+ */
+ public function addSubAggregations( Aggregations $aggregations ) {
+ $this->subAggregations[] = $aggregations;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function plain() {
+ $this->plain = true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function toArray() {
+
+ $params = $this->params( $this->parameters );
+
+ foreach ( $this->subAggregations as $subAggregation ) {
+ foreach ( $params as $key => $value) {
+ $params[$key] += $subAggregation->toArray();
+ }
+ }
+
+ if ( $params === [] || $this->plain ) {
+ return $params;
+ }
+
+ return [ 'aggregations' => $params ];
+ }
+
+ private function params( &$params ) {
+
+ $aggregation = $params;
+
+ if ( $aggregation instanceof Aggregations ) {
+ $aggregation->plain();
+ $params = $aggregation->toArray();
+ }
+
+ if ( !is_array( $params ) ) {
+ return $params;
+ }
+
+ $p = [];
+
+ foreach ( $params as $k => $aggregation ) {
+ if ( $aggregation instanceof Aggregations ) {
+ $aggregation = $this->params( $aggregation );
+ }
+
+ if ( is_string( $k ) ) {
+ $p[$k] = $aggregation;
+ } else {
+ $p = array_merge( $p, $aggregation );
+ }
+ }
+
+ return $p;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+ return json_encode( $this->toArray() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Condition.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Condition.php
new file mode 100644
index 00000000..a04acd11
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Condition.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Condition {
+
+ const TYPE_MUST = 'must';
+ const TYPE_SHOULD = 'should';
+ const TYPE_MUST_NOT = 'must_not';
+ const TYPE_FILTER = 'filter';
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @var array
+ */
+ private $logs = [];
+
+ /**
+ * @var string
+ */
+ private $type = 'must';
+
+ /**
+ * @since 3.0
+ *
+ * @param Condition|array $parameters
+ */
+ public function __construct( $parameters = [] ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function type( $type ) {
+ $this->type = $type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $log
+ */
+ public function log( $log ) {
+ $this->logs[] = $log;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getLogs() {
+ return $this->logs;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function toArray() {
+
+ $params = $this->params( $this->parameters, $this->logs );
+
+ if ( $this->type === '' || $this->type === null || $params === [] ) {
+ return $params;
+ }
+
+ return [ 'bool' => [ $this->type => $params ] ];
+ }
+
+ private function params( $params, &$logs ) {
+
+ $condition = $params;
+
+ if ( $condition instanceof Condition ) {
+ $params = $condition->toArray();
+
+ if ( ( $rlogs = $condition->getLogs() ) !== [] ) {
+ $logs[] = $rlogs;
+ }
+ }
+
+ if ( !is_array( $params ) ) {
+ return $params;
+ }
+
+ foreach ( $params as $k => $condition ) {
+ $params[$k] = $this->params( $condition, $logs );
+ }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+ return json_encode( $this->toArray(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php
new file mode 100644
index 00000000..09370a24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php
@@ -0,0 +1,464 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\HierarchyLookup;
+use SMW\Services\ServicesContainer;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Store;
+use SMWDataItem as DataItem;
+
+/**
+ * Build an internal representation for a SPARQL condition from individual query
+ * descriptions.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConditionBuilder {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var TermsLookup
+ */
+ private $termsLookup;
+
+ /**
+ * @var HierarchyLookup
+ */
+ private $hierarchyLookup;
+
+ /**
+ * @var ServicesContainer
+ */
+ private $servicesContainer;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @var ConceptDescriptionInterpreter
+ */
+ private $conceptDescriptionInterpreter;
+
+ /**
+ * @var ClassDescriptionInterpreter
+ */
+ private $classDescriptionInterpreter;
+
+ /**
+ * @var ValueDescriptionInterpreter
+ */
+ private $valueDescriptionInterpreter;
+
+ /**
+ * @var SomePropertyInterpreter
+ */
+ private $somePropertyInterpreter;
+
+ /**
+ * @var ConjunctionInterpreter
+ */
+ private $conjunctionInterpreter;
+
+ /**
+ * @var DisjunctionInterpreter
+ */
+ private $disjunctionInterpreter;
+
+ /**
+ * @var NamespaceDescriptionInterpreter
+ */
+ private $namespaceDescriptionInterpreter;
+
+ /**
+ * @var SomeValueInterpreter
+ */
+ private $someValueInterpreter;
+
+ /**
+ * @var array
+ */
+ private $sortFields = [];
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $queryInfo = [];
+
+ /**
+ * @var array
+ */
+ private $descriptionLog = [];
+
+ /**
+ * @var boolean
+ */
+ protected $isConstantScore = true;
+
+ /**
+ * @var boolean
+ */
+ private $initServices = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param TermsLookup $termsLookup
+ * @param HierarchyLookup $hierarchyLookup
+ * @param ServicesContainer $servicesContainer
+ */
+ public function __construct( Store $store, TermsLookup $termsLookup, HierarchyLookup $hierarchyLookup, ServicesContainer $servicesContainer ) {
+ $this->store = $store;
+ $this->termsLookup = $termsLookup;
+ $this->hierarchyLookup = $hierarchyLookup;
+ $this->servicesContainer = $servicesContainer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Options $options
+ */
+ public function setOptions( Options $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = false ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $sortFields
+ */
+ public function setSortFields( array $sortFields ) {
+ $this->sortFields = $sortFields;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TermsLookup
+ */
+ public function getTermsLookup() {
+ return $this->termsLookup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return FieldMapper
+ */
+ public function getFieldMapper() {
+
+ if ( $this->fieldMapper === null ) {
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ return $this->fieldMapper;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param []
+ */
+ public function getQueryInfo() {
+ return $this->queryInfo;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $queryInfo
+ */
+ public function addQueryInfo( array $queryInfo ) {
+ $this->queryInfo[] = $queryInfo;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param []
+ */
+ public function getDescriptionLog() {
+ return $this->descriptionLog;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $error
+ */
+ public function addError( array $error ) {
+ $this->errors[] = $error;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getID( $dataItem ) {
+
+ if ( $dataItem instanceof DIProperty ) {
+ return (int)$this->store->getObjectIds()->getSMWPropertyID(
+ $dataItem
+ );
+ }
+
+ if ( $dataItem instanceof DIWikiPage ) {
+ return (int)$this->store->getObjectIds()->getSMWPageID(
+ $dataItem->getDBKey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterWiki(),
+ $dataItem->getSubobjectName()
+ );
+ }
+
+ return 0;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Condition|array $params
+ *
+ * @return Condition
+ */
+ public function newCondition( $params ) {
+ return new Condition( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Description $description
+ * @param boolean $isConstantScore
+ *
+ * @return array
+ */
+ public function makeFromDescription( Description $description, $isConstantScore = true ) {
+
+ $this->errors = [];
+ $this->queryInfo = [];
+
+ $this->descriptionLog = [];
+ $this->termsLookup->clear();
+
+ if ( $this->fieldMapper === null ) {
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ $this->fieldMapper->isCompatMode(
+ $this->options->safeGet( 'compat.mode', true )
+ );
+
+ // Some notes on the difference between term, match, and query
+ // string:
+ //
+ // - match, term or range queries: look for a particular value in a
+ // particular field
+ // - bool: wrap other leaf or compound queries and are used to combine
+ // multiple queries
+ // - constant_score: simply returns a constant score equal to the query
+ // boost for every document in the filter
+
+ $condition = $this->interpretDescription( $description );
+
+ if ( $condition instanceof Condition ) {
+ $query = $condition->toArray();
+ $this->descriptionLog = $condition->getLogs();
+ } else {
+ $query = $condition;
+ }
+
+ if ( $this->options->safeGet( 'sort.property.must.exists' ) && $this->sortFields !== [] ) {
+ $params = [];
+
+ foreach ( $this->sortFields as $field ) {
+ $params[] = $this->fieldMapper->exists( "$field" );
+ }
+
+ $query = $this->fieldMapper->bool( 'must', [ $query, $params ] );
+ }
+
+ // If we know we don't need any score we turn this into a `constant_score`
+ // query
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html
+ if ( $isConstantScore ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ return $query;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataItem|null $dataItem
+ * @param integer $hierarchyDepth
+ *
+ * @return array
+ */
+ public function findHierarchyMembers( DataItem $dataItem = null, $hierarchyDepth ) {
+
+ $ids = [];
+
+ if ( $dataItem !== null && ( $members = $this->hierarchyLookup->getConsecutiveHierarchyList( $dataItem ) ) !== [] ) {
+
+ if ( $hierarchyDepth !== null ) {
+ $members = $hierarchyDepth == 0 ? [] : array_slice( $members, 0, $hierarchyDepth );
+ }
+
+ foreach ( $members as $member ) {
+ $ids[] = $this->getID( $member );
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Description $description
+ *
+ * @return array
+ */
+ public function interpretDescription( Description $description, $isConjunction = false ) {
+
+ $params = [];
+
+ if ( $this->initServices === false ) {
+ $this->initServices();
+ }
+
+ if ( $description instanceof SomeProperty ) {
+ $params = $this->somePropertyInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ConceptDescription ) {
+ $params = $this->conceptDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ClassDescription ) {
+ $params = $this->classDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof NamespaceDescription ) {
+ $params = $this->namespaceDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ValueDescription ) {
+ $params = $this->valueDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof Conjunction ) {
+ $params = $this->conjunctionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof Disjunction ) {
+ $params = $this->disjunctionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ValueDescription $description
+ * @param array &$options
+ *
+ * @return Condition
+ */
+ public function interpretSomeValue( ValueDescription $description, array &$options ) {
+
+ if ( $this->initServices === false ) {
+ $this->initServices();
+ }
+
+ return $this->someValueInterpreter->interpretDescription( $description, $options );
+ }
+
+ private function initServices() {
+
+ $this->somePropertyInterpreter = $this->servicesContainer->get( 'SomePropertyInterpreter', $this );
+ $this->conceptDescriptionInterpreter = $this->servicesContainer->get( 'ConceptDescriptionInterpreter', $this );
+ $this->classDescriptionInterpreter = $this->servicesContainer->get( 'ClassDescriptionInterpreter', $this );
+ $this->namespaceDescriptionInterpreter = $this->servicesContainer->get( 'NamespaceDescriptionInterpreter', $this );
+ $this->valueDescriptionInterpreter = $this->servicesContainer->get( 'ValueDescriptionInterpreter', $this );
+ $this->conjunctionInterpreter = $this->servicesContainer->get( 'ConjunctionInterpreter', $this );
+ $this->disjunctionInterpreter = $this->servicesContainer->get( 'DisjunctionInterpreter', $this );
+ $this->someValueInterpreter = $this->servicesContainer->get( 'SomeValueInterpreter', $this );
+
+ $this->initServices = true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
new file mode 100644
index 00000000..68c49fa9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Elastic\QueryEngine\Condition;
+use SMW\Query\Language\ClassDescription;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ClassDescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ClassDescription $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( ClassDescription $description, $isConjunction = false ) {
+
+ $pid = 'P:' . $this->conditionBuilder->getID( new DIProperty( '_INST' ) );
+ $field = 'wpgID';
+
+ $dataItems = $description->getCategories();
+ $hierarchyDepth = $description->getHierarchyDepth();
+
+ $should = false;
+ $params = [];
+
+ // More than one member per list means OR
+ if ( count( $dataItems ) > 1 ) {
+ $should = true;
+ }
+
+ $fieldMapper = $this->conditionBuilder->getFieldMapper();
+
+ foreach ( $dataItems as $dataItem ) {
+ $value = $this->conditionBuilder->getID( $dataItem );
+
+ $p = $fieldMapper->term( "$pid.$field", $value );
+ $hierarchy = [];
+
+ $ids = $this->conditionBuilder->findHierarchyMembers( $dataItem, $hierarchyDepth );
+
+ if ( $ids !== [] ) {
+ $hierarchy[] = $fieldMapper->terms( "$pid.$field", $ids );
+ }
+
+ // Hierarchies cannot be build as part of the normal index process
+ // therefore use the consecutive list to build a chain of disjunctive
+ // (should === OR) queries to match members of the list
+ if ( $hierarchy !== [] ) {
+ $params[] = $fieldMapper->bool( Condition::TYPE_SHOULD, array_merge( [ $p ], $hierarchy ) );
+ } else {
+ $params[] = $p;
+ }
+ }
+
+ // This feature is NOT supported by the SQLStore!!
+ // Encapsulate condition for something like `[[Category:!CatTest1]] ...`
+ if ( isset( $description->isNegation ) && $description->isNegation ) {
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( Condition::TYPE_MUST_NOT );
+ } else {
+ // ??!! If the description contains more than one category then it is
+ // interpret as OR (same as the SQLStore) and only in the case of an AND
+ // it is represented as Conjunction
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( ( $should ? Condition::TYPE_SHOULD : Condition::TYPE_FILTER ) );
+ }
+
+ $condition->log( [ 'ClassDescription' => $description->getQueryString() ] );
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
new file mode 100644
index 00000000..76b05b36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Parser as QueryParser;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\DIProperty;
+use SMW\Options;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var QueryParser
+ */
+ private $queryParser;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ * @param QueryParser $queryParser
+ */
+ public function __construct( ConditionBuilder $conditionBuilder, QueryParser $queryParser ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->queryParser = $queryParser;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ConceptDescription $description
+ *
+ * @return Condition|[]
+ */
+ public function interpretDescription( ConceptDescription $description, $isConjunction = false ) {
+
+ $concept = $description->getConcept();
+
+ $value = $this->conditionBuilder->getStore()->getPropertyValues(
+ $concept,
+ new DIProperty( '_CONC' )
+ );
+
+ if ( $value === null || $value === [] ) {
+ return [];
+ }
+
+ $value = end( $value );
+
+ $description = $this->queryParser->getQueryDescription(
+ $value->getConceptQuery()
+ );
+
+ if ( $this->hasCircularConceptDescription( $description, $concept ) ) {
+ return [];
+ }
+
+ $params = $this->conditionBuilder->interpretDescription(
+ $description,
+ $isConjunction
+ );
+
+ $params = $this->terms_lookup( $description, $concept, $params );
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( '' );
+
+ $condition->log( [ 'ConceptDescription' => $description->getQueryString() ] );
+
+ return $condition;
+ }
+
+ private function terms_lookup( $description, $concept, $params ) {
+
+ $concept->setId(
+ $this->conditionBuilder->getID( $concept )
+ );
+
+ $termsLookup = $this->conditionBuilder->getTermsLookup();
+
+ $parameters = $termsLookup->newParameters(
+ [
+ 'id' => $concept->getId(),
+ 'hash' => $concept->getHash(),
+ 'query.string' => $description->getQueryString(),
+ 'fingerprint' => $description->getFingerprint(),
+ 'params' => $params,
+ ]
+ );
+
+ // Using the terms lookup to prefetch IDs from the lookup index
+ if ( $this->conditionBuilder->getOption( 'concept.terms.lookup' ) ) {
+ $params = $termsLookup->lookup( 'concept', $parameters );
+ }
+
+ if ( $parameters->has( 'query.info' ) ) {
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+ }
+
+ return $params;
+ }
+
+ private function hasCircularConceptDescription( $description, $concept ) {
+
+ if ( $description instanceof ConceptDescription ) {
+ if ( $description->getConcept()->equals( $concept ) ) {
+ $this->conditionBuilder->addError( [ 'smw-query-condition-circular', $description->getQueryString() ] );
+ return true;
+ }
+ }
+
+ if ( $description instanceof Conjunction || $description instanceof Disjunction ) {
+ foreach ( $description->getDescriptions() as $desc ) {
+ if ( $this->hasCircularConceptDescription( $desc, $concept ) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php
new file mode 100644
index 00000000..27742e32
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Elastic\QueryEngine\Condition;
+use SMW\Query\Language\Conjunction;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConjunctionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Conjunction $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( Conjunction $description ) {
+
+ $params = [];
+
+ foreach ( $description->getDescriptions() as $desc ) {
+ if ( ( $cond = $this->conditionBuilder->interpretDescription( $desc, true ) ) instanceof Condition ) {
+ $params[] = $cond;
+ }
+ }
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( Condition::TYPE_MUST );
+ $condition->log( [ 'Conjunction' => $description->getQueryString() ] );
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php
new file mode 100644
index 00000000..9cc87e07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Query\Language\Disjunction;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DisjunctionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Disjunction $description
+ *
+ * @return Condition|[]
+ */
+ public function interpretDescription( Disjunction $description, $isConjunction = false ) {
+
+ $params = [];
+ $notConditionFields = [];
+
+ foreach ( $description->getDescriptions() as $desc ) {
+
+ // Mark each as being part of a disjunction in order to to decide
+ // whether a subquery should fail as part of a conjunction or not
+ // when it relates to a disjunctive description
+ // [[Foo.bar::123]] AND [[Foobar::123]] (fails) vs.
+ // [[Foo.bar::123]] OR [[Foobar::123]]
+ $desc->isPartOfDisjunction = true;
+
+ if ( ( $param = $this->conditionBuilder->interpretDescription( $desc, true ) ) !== [] ) {
+
+ // @see SomePropertyInterpreter
+ // Collect a possible negation condition in case `must_not.property.exists`
+ // is set (which is the SMW default mode) to allow wrapping an
+ // additional condition around an OR when the existence of the
+ // queried property is required
+ if ( isset( $desc->notConditionField ) ) {
+ $notConditionFields[] = $desc->notConditionField;
+ }
+
+ $params[] = $param;
+ }
+ }
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( 'should' );
+
+ $condition->log( [ 'Disjunction' => $description->getQueryString() ] );
+
+ $notConditionFields = array_keys( array_flip( $notConditionFields ) );
+
+ if ( $notConditionFields === [] ) {
+ return $condition;
+ }
+
+ $existsConditions = [];
+ $fieldMapper = $this->conditionBuilder->getFieldMapper();
+
+ // Extra condition that satisfies !/OR condition (see T:Q0905#5 and
+ // T:Q1106#4)
+ //
+ // Use case: `[[Category:E-Q1106]]<q>[[Has restricted status record::!~cl*]]
+ // OR [[Has restricted status record::!~*in*]]</q>` and `[[Category:Q0905]]
+ // [[!Example/Q0905/1]] <q>[[Has page::123]] OR [[Has page::!ABCD]]</q>`
+ foreach ( $notConditionFields as $field ) {
+ $existsConditions[] = $fieldMapper->exists( $field );
+ }
+
+ // We wrap the intermediary `should` clause in an extra `must` to ensure
+ // those properties are exists for the returned documents.
+ $condition = $this->conditionBuilder->newCondition( [ $condition, $existsConditions ] );
+ $condition->type( 'must' );
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
new file mode 100644
index 00000000..480d2680
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Query\Language\NamespaceDescription;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param NamespaceDescription $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( NamespaceDescription $description, $isConjunction = false ) {
+
+ $params = [];
+ $fieldMapper = $this->conditionBuilder->getFieldMapper();
+
+ $namespace = $description->getNamespace();
+ $params = $fieldMapper->term( 'subject.namespace', $namespace );
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( '' );
+
+ if ( !$isConjunction ) {
+ $condition->type( 'filter' );
+ }
+
+ $condition->log( [ 'NamespaceDescription' => $description->getQueryString() ] );
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
new file mode 100644
index 00000000..85d0f646
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
@@ -0,0 +1,464 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use Maps\Semantic\ValueDescriptions\AreaDescription;
+use SMW\DataTypeRegistry;
+use SMW\DIWikiPage;
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Elastic\QueryEngine\Condition;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMW\Elastic\QueryEngine\QueryBuilder;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\NamespaceDescription;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDIGeoCoord as DIGeoCoord;
+use SMWDInumber as DINumber;
+use SMWDITime as DITime;
+use SMWDIUri as DIUri;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SomePropertyInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @var TermsLookup
+ */
+ private $termsLookup;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param SomeProperty $description
+ *
+ * @return array
+ */
+ public function interpretDescription( SomeProperty $description, $isConjunction = false, $isChain = false ) {
+
+ // Query types
+ //
+ // - term: query matches a single term as it is, the value is not
+ // analyzed
+ // - match_phrase: query will analyze the input, all the terms must
+ // appear in the field, they must have the same order as the input
+ // value
+
+ // Bool types
+ //
+ // - must: query must appear in matching documents and will contribute
+ // to the score
+ // - filter: query must appear in matching documents, the score
+ // of the query will be ignored
+ // - should: query should appear in the matching document
+
+ $this->fieldMapper = $this->conditionBuilder->getFieldMapper();
+ $this->termsLookup = $this->conditionBuilder->getTermsLookup();
+
+ $property = $description->getProperty();
+ $pid = $this->fieldMapper->getPID( $this->conditionBuilder->getID( $property ) );
+
+ $hierarchy = $this->conditionBuilder->findHierarchyMembers(
+ $property,
+ $description->getHierarchyDepth()
+ );
+
+ $desc = $description->getDescription();
+
+ // Copy the context
+ if ( isset( $description->isPartOfDisjunction ) ) {
+ $desc->isPartOfDisjunction = true;
+ }
+
+ $field = 'wpgID';
+ $opType = Condition::TYPE_MUST;
+
+ $field = $this->fieldMapper->getField( $property, 'Field' );
+ $params = [];
+
+ // [[Foo::Bar]]
+ if ( $desc instanceof ValueDescription ) {
+ $params = $this->interpretValueDescription( $desc, $property, $pid, $field, $opType );
+ }
+
+ // [[Foo::+]]
+ if ( $desc instanceof ThingDescription ) {
+ $params = $this->interpretThingDescription( $desc, $property, $pid, $field, $opType );
+ }
+
+ if ( $params !== [] ) {
+ $params = $this->fieldMapper->hierarchy( $params, $pid, $hierarchy );
+ }
+
+ if ( $desc instanceof ClassDescription ) {
+ $params = $this->interpretClassDescription( $desc, $property, $pid, $field );
+ }
+
+ if ( $desc instanceof NamespaceDescription ) {
+ $params = $this->interpretNamespaceDescription( $desc, $property, $pid, $field );
+ }
+
+ // [[-Person:: <q>[[Person.-Has friend.Person::Andy Mars]] [[Age::>>32]]</q> ]]
+ if ( $desc instanceof Conjunction ) {
+ $params = $this->interpretConjunction( $desc, $property, $pid, $field );
+ }
+
+ // Use case: `[[Has page-2:: <q>[[Has page-1::Value 1||Value 2]]
+ // [[Has text-1::Value 1||Value 2]]</q> || <q> [[Has page-2::Value 1||Value 2]]</q> ]]`
+ if ( $desc instanceof Disjunction ) {
+ $params = $this->interpretDisjunction( $desc, $property, $pid, $field, $opType );
+ }
+
+ if ( !$params instanceof Condition ) {
+ $condition = $this->conditionBuilder->newCondition( $params );
+ } else {
+ $condition = $params;
+ }
+
+ $condition->type( $opType );
+ $condition->log( [ 'SomeProperty' => $description->getQueryString() ] );
+
+ // [[Foo.Bar::Foobar]], [[Foo.Bar::<q>[[Foo::Bar]] OR [[Fobar::Foo]]</q>]]
+ if ( $desc instanceof SomeProperty ) {
+ $condition = $this->interpretChain( $desc, $property, $pid, $field );
+ }
+
+ if ( $condition === [] ) {
+ return [];
+ }
+
+ // Build an extra condition to restore strictness by making sure
+ // the property exist on those matched entities
+ // `[[Has text::!~foo*]]` becomes `[[Has text::!~foo*]] [[Has text::+]`
+ if ( $opType === Condition::TYPE_MUST_NOT && !$desc instanceof ThingDescription ) {
+
+ // Use case: `[[Category:Q0905]] [[!Example/Q0905/1]] <q>[[Has page::123]]
+ // OR [[Has page::!ABCD]]</q>`
+ $params = [ $this->fieldMapper->exists( "$pid.$field" ), $condition ];
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( '' );
+
+ if ( $this->conditionBuilder->getOption( 'must_not.property.exists' ) ) {
+ $description->notConditionField = "$pid.$field";
+ }
+
+ // Use case: `[[Has telephone number::!~*123*]]`
+ if ( !$isConjunction ) {
+ $condition->type( 'must' );
+ }
+ }
+
+ if ( $isChain === false ) {
+ return $condition;
+ }
+
+ if ( !isset( $description->sourceChainMemberField ) ) {
+ throw new RuntimeException( "Missing `sourceChainMemberField`" );
+ }
+
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'terms_filter.field' => $description->sourceChainMemberField,
+ 'query.string' => $description->getQueryString(),
+ 'property.key' => $property->getKey(),
+ 'params' => $condition->toArray()
+ ]
+ );
+
+ $params = $this->termsLookup->lookup( 'chain', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+
+ // Let it fail for a conjunction when the subquery returns empty!
+ if ( $params === [] && !isset( $desc->isPartOfDisjunction ) ) {
+ // Fail with a non existing condition to avoid a " ...
+ // query malformed, must start with start_object ..."
+ $params = $this->fieldMapper->exists( "empty.lookup_query" );
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->log( [ 'SomeProperty' => [ 'Chain' => $description->getQueryString() ] ] );
+
+ return $condition;
+ }
+
+ private function interpretDisjunction( $description, $property, $pid, $field, &$opType ) {
+
+ $p = [];
+ $opType = Condition::TYPE_SHOULD;
+
+ foreach ( $description->getDescriptions() as $desc ) {
+
+ $d = new SomeProperty(
+ $property,
+ $desc
+ );
+
+ $d->sourceChainMemberField = "$pid.wpgID";
+ $t = $this->conditionBuilder->interpretDescription( $d, true, true );
+
+ if ( $t !== [] ) {
+ $p[] = $t->toArray();
+ }
+ }
+
+ if ( $p === [] ) {
+ return [];
+ }
+
+ //$this->fieldMapper->bool( 'should', $p );
+ $condition = $this->conditionBuilder->newCondition( $p );
+
+ return $condition;
+ }
+
+ private function interpretClassDescription( $description, $property, $pid, $field ) {
+
+ $queryString = $description->getQueryString();
+ $condition = $this->conditionBuilder->interpretDescription( $description );
+
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $queryString,
+ 'field' => "$pid.wpgID",
+ 'params' => $condition
+ ]
+ );
+
+ $params = $this->termsLookup->lookup( 'predef', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( Condition::TYPE_MUST );
+ $condition->log( [ 'SomeProperty' => [ 'ClassDescription' => $queryString ] ] );
+
+ return $condition;
+ }
+
+ private function interpretNamespaceDescription( $description, $property, $pid, $field ) {
+
+ $queryString = $description->getQueryString();
+ $condition = $this->conditionBuilder->interpretDescription( $description );
+
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $queryString,
+ 'field' => "$pid.wpgID",
+ 'params' => $condition
+ ]
+ );
+
+ $params = $this->termsLookup->lookup( 'predef', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+
+ if ( $params === [] ) {
+ return [];
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( Condition::TYPE_MUST );
+ $condition->log( [ 'SomeProperty' => [ 'NamespaceDescription' => $queryString ] ] );
+
+ return $condition;
+ }
+
+ private function interpretConjunction( $description, $property, $pid, $field ) {
+
+ $p = [];
+ $logs = [];
+ $queryString = $description->getQueryString();
+ $logs[] = $queryString;
+ $opType = Condition::TYPE_MUST;
+
+ foreach ( $description->getDescriptions() as $desc ) {
+ $params = $this->conditionBuilder->interpretDescription( $desc, true );
+
+ if ( $params !== [] ) {
+ $p[] = $params->toArray();
+ $logs = array_merge( $logs, $params->getLogs() );
+ }
+ }
+
+ if ( $p !== [] ) {
+ // We match IDs using the term lookup which is either a resource or
+ // a document field (on a txtField etc.)
+ $f = strpos( $field, 'wpg' ) !== false ? "$pid.wpgID" : "_id";
+
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $queryString,
+ 'field' => $f,
+ 'params' => $p
+ ]
+ );
+
+ $p = $this->termsLookup->lookup( 'predef', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+ }
+
+ // Inverse matches are always resource (aka wpgID) related
+ if ( $property->isInverse() ) {
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $desc->getQueryString(),
+ 'property.key' => $property->getKey(),
+ 'field' => "$pid.wpgID",
+ 'params' => $this->fieldMapper->field_filter( "$pid.wpgID", $p )
+ ]
+ );
+
+ $p = $this->termsLookup->lookup( 'inverse', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+ }
+
+ if ( $p === [] ) {
+ return [];
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $p );
+ $condition->type( '' );
+
+ $condition->log( [ 'SomeProperty' => [ 'Conjunction' => $logs ] ] );
+
+ return $condition;
+ }
+
+ private function interpretChain( $desc, $property, $pid, $field ) {
+
+ $desc->sourceChainMemberField = "$pid.wpgID";
+ $p = [];
+
+ // Use case: `[[Category:Sample-1]][[Has page-1.Has page-2:: <q>
+ // [[Has text-1::Value 1]] OR <q>[[Has text-2::Value 2]]
+ // [[Has page-2::Value 2]]</q></q> ]]`
+ if ( $desc->getDescription() instanceof Disjunction ) {
+
+ foreach ( $desc->getDescription()->getDescriptions() as $d ) {
+ $d = new SomeProperty(
+ $desc->getProperty(),
+ $d
+ );
+ $d->setMembership( $desc->getFingerprint() );
+ $d->sourceChainMemberField = "$pid.wpgID";
+
+ if ( isset( $desc->isPartOfDisjunction ) ) {
+ $d->isPartOfDisjunction = true;
+ }
+
+ $t = $this->interpretDescription( $d, true, true );
+
+ if ( $t !== [] ) {
+ $p[] = $t->toArray();
+ }
+ }
+
+ $p = $this->fieldMapper->bool( 'should', $p );
+ } else {
+ $p = $this->interpretDescription( $desc, true, true );
+ }
+
+ if ( $property->isInverse() ) {
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $desc->getQueryString(),
+ 'property.key' => $property->getKey(),
+ 'field' => "$pid.wpgID",
+ 'params' => $this->fieldMapper->field_filter( "$pid.wpgID", $p->toArray() )
+ ]
+ );
+
+ $p = $this->termsLookup->lookup( 'inverse', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $p );
+ $condition->type( '' );
+
+ return $condition;
+ }
+
+ private function interpretThingDescription( $desc, $property, $pid, $field, &$opType ) {
+
+ $isResourceType = false;
+
+ if ( DataTypeRegistry::getInstance()->getDataItemByType( $property->findPropertyValueType() ) === DataItem::TYPE_WIKIPAGE ) {
+ $field = 'wpgID';
+ $isResourceType = true;
+ }
+
+ // [[Has subobject::!+]] is only supported with the ElasticStore
+ $opType = isset( $desc->isNegation ) ? Condition::TYPE_MUST_NOT : Condition::TYPE_FILTER;
+ $params = $this->fieldMapper->exists( "$pid.$field" );
+
+ // Only allow to match wpg types (aka resources) to be used as
+ // invertible query element, this matches the SQLStore behaviour
+ if ( $property->isInverse() && $isResourceType ) {
+ $parameters = $this->termsLookup->newParameters(
+ [
+ 'query.string' => $desc->getQueryString(),
+ 'property.key' => $property->getKey(),
+ 'field' => "$pid.$field",
+ 'params' => ''
+ ]
+ );
+
+ $params = $this->termsLookup->lookup( 'inverse', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get( 'query.info' ) );
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( '' );
+
+ return $condition;
+ }
+
+ private function interpretValueDescription( $desc, $property, $pid, &$field, &$type ) {
+
+ $options = [
+ 'type' => $type,
+ 'field' => $field,
+ 'pid' => $pid,
+ 'property' => $property
+ ];
+
+ $condition = $this->conditionBuilder->interpretSomeValue( $desc, $options );
+
+ $field = $options['field'];
+ $type = $options['type'];
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreter.php
new file mode 100644
index 00000000..54c42fd3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreter.php
@@ -0,0 +1,538 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use Maps\Semantic\ValueDescriptions\AreaDescription;
+use SMW\DataTypeRegistry;
+use SMW\DIWikiPage;
+use SMW\DIProperty;
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Elastic\QueryEngine\Condition;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMW\Query\Language\ValueDescription;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDIGeoCoord as DIGeoCoord;
+use SMWDInumber as DINumber;
+use SMWDITime as DITime;
+use SMWDIUri as DIUri;
+use SMW\Utils\CharExaminer;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SomeValueInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ValueDescription $description
+ * @param array &$options
+ *
+ * @return Condition
+ * @throws RuntimeException
+ */
+ public function interpretDescription( ValueDescription $description, array &$options ) {
+
+ if ( !isset( $options['property'] ) || !$options['property'] instanceof DIProperty ) {
+ throw new RuntimeException( "Missing a property" );
+ }
+
+ if ( !isset( $options['pid'] ) ) {
+ throw new RuntimeException( "Missing a pid" );
+ }
+
+ $this->fieldMapper = $this->conditionBuilder->getFieldMapper();
+
+ $dataItem = $description->getDataItem();
+ $comparator = $description->getComparator();
+
+ // Normalize comparator (we don't distinguish them in Elastic)
+ if ( $comparator === SMW_CMP_PRIM_LIKE ) {
+ $comparator = SMW_CMP_LIKE;
+ }
+
+ if ( $comparator === SMW_CMP_PRIM_NLKE ) {
+ $comparator = SMW_CMP_NLKE;
+ }
+
+ if ( $comparator === SMW_CMP_NLKE || $comparator === SMW_CMP_NEQ ) {
+ $options['type'] = Condition::TYPE_MUST_NOT;
+ }
+
+ $options['comparator'] = $comparator;
+
+ if ( $dataItem instanceof DIWikiPage ) {
+ $params = $this->page( $dataItem, $options );
+ } elseif ( $dataItem instanceof DIBlob ) {
+ $params = $this->blob( $dataItem, $options );
+ } elseif ( $dataItem instanceof DIUri ) {
+ $params = $this->uri( $dataItem, $options );
+ } elseif ( $dataItem instanceof DIGeoCoord ) {
+
+ if ( $description instanceof AreaDescription ) {
+ $options['bounding_box'] = $description->getBoundingBox();
+ }
+
+ $params = $this->geo( $dataItem, $options );
+ } elseif ( $dataItem instanceof DITime ) {
+ $params = $this->plain( $dataItem->getJD(), $options );
+ } elseif ( $dataItem instanceof DIBoolean ) {
+ $params = $this->plain( $dataItem->getBoolean(), $options );
+ } elseif ( $dataItem instanceof DINumber ) {
+ $params = $this->plain( $dataItem->getNumber(), $options );
+ } else {
+ $params = $this->plain( $dataItem->getSerialization(), $options );
+ }
+
+ if ( $options['property']->isInverse() ) {
+ $options['query.string'] = $description->getQueryString();
+ $params = $this->inverse_property( $params, $options );
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+ $condition->type( $options['type'] );
+
+ return $condition;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $dataItem
+ *
+ * @return array
+ */
+ public function page( DIWikiPage $dataItem, array &$options ) {
+
+ $comparator = $options['comparator'];
+ $pid = $options['pid'];
+ $field = $options['field'];
+ $type = $options['type'];
+
+ $isSubDataType = DataTypeRegistry::getInstance()->isSubDataType(
+ $options['property']->findPropertyValueType()
+ );
+
+ if ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+ $field = 'wpgID';
+ $value = $this->conditionBuilder->getID( $dataItem );
+ } else {
+ $value = $dataItem->getSortKey();
+ }
+
+ if ( $this->isRange( $comparator ) ) {
+ $match = $this->fieldMapper->range( "$pid.$field", $value, $comparator );
+ } elseif ( $isSubDataType && $dataItem->getDBKey() === '' && $comparator === SMW_CMP_NEQ ) {
+ // [[Has subobject::!]] select those that are not a subobject
+ $match = $this->fieldMapper->term( "subject.subobject.keyword", '' );
+ $type = Condition::TYPE_FILTER;
+ } elseif ( $comparator === SMW_CMP_LIKE ) {
+
+ // Avoid *...* on CJK related terms so that something like
+ // [[Has text::in:åå¤å±‹]] returns a better match accuracy given that
+ // the standard analyzer splits CJK terms into single characters
+ if ( $this->conditionBuilder->getOption( 'cjk.best.effort.proximity.match', false ) && CharExaminer::isCJK( $value ) ) {
+
+ if ( $value{0} === '*' ) {
+ $value = substr( $value, 1 );
+ }
+
+ if ( substr( $value , -1 ) === '*' ) {
+ $value = substr( $value, 0, -1 );
+ }
+
+ // Use a phrase match to keep the char boundaries and avoid
+ // matching single chars
+ $value = "\"$value\"";
+ }
+
+ // Q1203
+ // [[phrase:fox jump*]] (aka ~"fox jump*") + wildcard; use match with
+ // a `multi_match` and type `phrase_prefix`
+ $isPhrase = strpos( $value, '"' ) !== false;
+ $hasWildcard = strpos( $value, '*' ) !== false;
+
+ // Match a page title, the issue is accuracy vs. proximity
+
+ // Boolean operators (+/-) are allowed? Use the query_string
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_boolean_operators
+ if ( $this->conditionBuilder->getOption( 'query_string.boolean.operators' ) && ( strpos( $value, '+' ) !== false || strpos( $value, '-' ) !== false ) ) {
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ } elseif ( ( $hasWildcard && $value{0} === '*' ) && $this->conditionBuilder->getOption( 'cjk.best.effort.proximity.match', false ) && CharExaminer::isCJK( $value ) ) {
+
+ // Avoid *...* on CJK related terms so that something like
+ // [[Has page::in:åå¤å±‹]] returns a better match accuracy given that
+ // the standard analyzer splits CJK terms into single characters
+ if ( $value{0} === '*' ) {
+ $value = mb_substr( $value, 1 );
+ }
+
+ if ( mb_substr( $value , -1 ) === '*' ) {
+ $value = mb_substr( $value, 0, -1 );
+ }
+
+ // Use a phrase match to keep the char boundaries and avoid
+ // matching single chars
+ $match = $this->fieldMapper->match( "$pid.$field", "\"$value\"" );
+
+ } elseif ( ( $hasWildcard && $value{0} === '*' ) || ( strpos( $value, '~?' ) !== false && $value{0} === '?' ) ) {
+ // ES notes "... In order to prevent extremely slow wildcard queries,
+ // a wildcard term should not start with one of the wildcards
+ // * or ? ..." therefore use `query_string` instead of a
+ // `wildcard` term search
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-wildcard-query.html
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ } elseif ( $hasWildcard && !$isPhrase ) {
+
+ // T:Q0910, Wildcard?
+ // - Use the term search `wildcard` with text not being
+ // analyzed which means that things like [[Has page::~Foo bar/Bar/*]]
+ // are matched strictly without manipulating the query string.
+ // - `lowercase` field with a normalizer to achieve case
+ // insensitivity
+ if ( $this->conditionBuilder->getOption( 'page.field.case.insensitive.proximity.match', true ) ) {
+ $field = "$field.lowercase";
+ } else {
+ $field = "$field.keyword";
+ }
+
+ $match = $this->fieldMapper->wildcard( "$pid.$field", $value );
+ $type = $type === Condition::TYPE_MUST ? Condition::TYPE_FILTER : $type;
+ } elseif ( $isPhrase ) {
+ $match = $this->fieldMapper->match( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ }
+ } elseif ( $comparator === SMW_CMP_NLKE ) {
+
+ // T:Q0905, Interpreting the meaning of `!~elastic*, +sear*` which is
+ // to match non with the term `elastic*` but those that match `sear*`
+ // with the consequence that this is turned from a `must_not` to a `must`
+ if ( $this->conditionBuilder->getOption( 'query_string.boolean.operators' ) && ( strpos( $value, '+' ) !== false ) ) {
+ $type = Condition::TYPE_MUST;
+ $value = "-$value";
+ }
+
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ } elseif ( $comparator === SMW_CMP_EQ ) {
+ $type = Condition::TYPE_FILTER;
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } elseif ( $comparator === SMW_CMP_NEQ ) {
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ }
+
+ $options['field'] = $field;
+ $options['value'] = $value;
+ $options['type'] = $type;
+
+ return $match;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIBlob $dataItem
+ * @param array $options
+ *
+ * @return array
+ */
+ public function blob( DIBlob $dataItem, array &$options ) {
+
+ $comparator = $options['comparator'];
+ $pid = $options['pid'];
+ $field = $options['field'];
+ $type = $options['type'];
+
+ $value = $dataItem->getSerialization();
+
+ if ( $this->isRange( $comparator ) ) {
+ // Use a not_analyzed field
+ $match = $this->fieldMapper->range( "$pid.$field.keyword", $value, $comparator );
+ } elseif ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+
+ // #3020
+ // Use a term query where possible to allow ES to create a bitset and
+ // cache the lookup if possible
+ if ( $options['property']->findPropertyValueType() === '_keyw' ) {
+ $match = $this->fieldMapper->term( "$pid.$field.keyword", "$value" );
+ $type = $type === Condition::TYPE_MUST ? Condition::TYPE_FILTER : $type;
+ } elseif ( $this->conditionBuilder->getOption( 'text.field.case.insensitive.eq.match' ) ) {
+ // [[Has text::Template one]] == [[Has text::template one]]
+ $match = $this->fieldMapper->match_phrase( "$pid.$field", "$value" );
+ } else {
+ $match = $this->fieldMapper->term( "$pid.$field.keyword", "$value" );
+ $type = $type === Condition::TYPE_MUST ? Condition::TYPE_FILTER : $type;
+ }
+ } elseif ( $comparator === SMW_CMP_LIKE ) {
+
+ // Q1203
+ // [[phrase:fox jump*]] (aka ~"fox jump*")
+
+ // T:Q0102 Choose a `P:xxx.*` over a specific `P:xxx.txtField` field
+ // to enforce a `DisjunctionMaxQuery` as in
+ // `"(P:8316.txtField:*\\{* | P:8316.txtField.keyword:*\\{*)",`
+ $fields = [ "$pid.$field", "$pid.$field.keyword" ];
+
+ if ( $this->fieldMapper->isPhrase( $value ) ) {
+ $match = $this->fieldMapper->match( $fields, $value );
+ } else {
+ $match = $this->fieldMapper->query_string( $fields, $value );
+ }
+ } elseif ( $comparator === SMW_CMP_NLKE ) {
+
+ // T:Q0904, Interpreting the meaning of `!~elastic*, +sear*` which is
+ // to match non with the term `elastic*` but those that match `sear*`
+ // with the consequence that this is turned from a `must_not` to a `must`
+ if ( $this->conditionBuilder->getOption( 'query_string.boolean.operators' ) && ( strpos( $value, '+' ) !== false ) ) {
+ $type = Condition::TYPE_MUST;
+ $value = "-$value";
+ }
+
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ }
+
+ $options['field'] = $field;
+ $options['value'] = $value;
+ $options['type'] = $type;
+
+ return $match;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIUri $dataItem
+ * @param array $options
+ *
+ * @return array
+ */
+ public function uri( DIUri $dataItem, array &$options ) {
+
+ $comparator = $options['comparator'];
+ $pid = $options['pid'];
+ $field = $options['field'];
+ $type = $options['type'];
+
+ $value = str_replace( [ '%2A' ], [ '*' ], rawurldecode( $dataItem->getUri() ) );
+
+ if ( $this->isRange( $comparator ) ) {
+ $match = $this->fieldMapper->range( "$pid.$field", $value, $comparator );
+ } elseif ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+
+ if ( $this->conditionBuilder->getOption( 'uri.field.case.insensitive' ) ) {
+ // As EQ, use the match_phrase to ensure that each part of the
+ // string is part of the match.
+ // T:Q0908
+ $match = $this->fieldMapper->match_phrase( "$pid.$field.lowercase", "$value" );
+ } else {
+ // Use the keyword field (not analyzed) so that the search
+ // matches the exact term
+ // T:P0419 (`http://example.org/FoO` !== `http://example.org/Foo`)
+ $match = $this->fieldMapper->term( "$pid.$field.keyword", "$value" );
+ }
+ } elseif ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) {
+
+ $value = str_replace( [ 'http://', 'https://', '=' ], [ '', '', '' ], $value );
+
+ if ( strpos( $value, 'tel:' ) !== false || strpos( $value, 'mailto:' ) !== false ) {
+ $value = str_replace( [ 'tel:', 'mailto:' ], [ '', '' ], $value );
+ $field = "$field.keyword";
+ } elseif ( $this->conditionBuilder->getOption( 'uri.field.case.insensitive' ) ) {
+ $field = "$field.lowercase";
+ }
+
+ $match = $this->fieldMapper->query_string( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ }
+
+ $options['field'] = $field;
+ $options['value'] = $value;
+
+ return $match;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIGeoCoord $dataItem
+ * @param array $options
+ *
+ * @return array
+ */
+ public function geo( DIGeoCoord $dataItem, array &$options ) {
+
+ $comparator = $options['comparator'];
+ $pid = $options['pid'];
+ $field = $options['field'];
+ $type = $options['type'];
+
+ $value = $dataItem->getSerialization();
+ $options['value'] = $value;
+
+ if ( $this->isRange( $comparator ) ) {
+ $match = $this->fieldMapper->range( "$pid.$field", $value, $comparator );
+ } elseif ( isset( $options['bounding_box'] ) ) {
+
+ // Due to "QueryShardException: Geo fields do not support exact
+ // searching, use dedicated geo queries instead" on EQ search,
+ // the geo_point is indexed as extra field geoField.point to make
+ // use of the `bounding_box` feature in ES while the standard EQ
+ // search uses the geoField string representation
+ $boundingBox = $options['bounding_box'];
+
+ $match = $this->fieldMapper->geo_bounding_box(
+ "$pid.$field.point",
+ $boundingBox['north'],
+ $boundingBox['west'],
+ $boundingBox['south'],
+ $boundingBox['east']
+ );
+ } elseif ( $comparator === SMW_CMP_LIKE ) {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ } elseif ( $comparator === SMW_CMP_EQ ) {
+ $options['type'] = Condition::TYPE_FILTER;
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } elseif ( $comparator === SMW_CMP_NEQ ) {
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ }
+
+ return $match;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $value
+ * @param array $options
+ *
+ * @return array
+ */
+ public function plain( $value, array &$options ) {
+
+ $comparator = $options['comparator'];
+ $pid = $options['pid'];
+ $field = $options['field'];
+
+ if ( $this->isRange( $comparator ) ) {
+ $match = $this->fieldMapper->range( "$pid.$field", $value, $comparator );
+ } elseif ( $comparator === SMW_CMP_LIKE ) {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ } elseif ( $comparator === SMW_CMP_EQ ) {
+ $options['type'] = Condition::TYPE_FILTER;
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } elseif ( $comparator === SMW_CMP_NEQ ) {
+ $match = $this->fieldMapper->term( "$pid.$field", $value );
+ } else {
+ $match = $this->fieldMapper->match( "$pid.$field", $value, 'and' );
+ }
+
+ return $match;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param $params
+ * @param $options
+ *
+ * @return array
+ */
+ public function inverse_property( $params, $options ) {
+
+ $termsLookup = $this->conditionBuilder->getTermsLookup();
+ $comparator = $options['comparator'];
+
+ $pid = $options['pid'];
+ $property = $options['property'];
+
+ // A simple inverse is enough to fetch the inverse match for a resource
+ // [[-Has query::F0103/PageContainsAskWithTemplateUsage]]
+ if ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+
+ $parameters = $termsLookup->newParameters(
+ [
+ 'query.string' => $options['query.string'],
+ 'property.key' => $property->getKey(),
+ 'field' => "$pid.wpgID",
+ 'params' => $options['value']
+ ]
+ );
+
+ $params = $termsLookup->lookup( 'inverse', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get('query.info' ) );
+ } else {
+
+ $field = $options['field'];
+
+ // First we need to find entities that fulfill the condition
+ // `~*Test*` to allow to match the `-Has subobject` part from
+ // [[-Has subobject::~*Test*]]
+
+ // Either use the resource or the document field
+ $f = strpos( $field, 'wpg' ) !== false ? "$pid.wpgID" : "_id";
+
+ $parameters = $termsLookup->newParameters(
+ [
+ 'query.string' => $options['query.string'],
+ 'field' => $f,
+ 'params' => $params
+ ]
+ );
+
+ $p = $termsLookup->lookup( 'predef', $parameters );
+
+ $this->conditionBuilder->addQueryInfo( $parameters->get('query.info' ) );
+
+ $p = $this->fieldMapper->field_filter( $f, $p );
+
+ $parameters->set( 'property.key', $property->getKey() );
+ $parameters->set( 'params', $p );
+
+ $params = $termsLookup->lookup( 'inverse', $parameters );
+ $this->conditionBuilder->addQueryInfo( $parameters->get('query.info' ) );
+ }
+
+ return $params;
+ }
+
+ private function isRange( $comparator ) {
+ return $comparator === SMW_CMP_GRTR || $comparator === SMW_CMP_GEQ || $comparator === SMW_CMP_LESS || $comparator === SMW_CMP_LEQ;
+ }
+
+ private function isNot( $comparator ) {
+ return $comparator === SMW_CMP_NLKE || $comparator === SMW_CMP_NEQ;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
new file mode 100644
index 00000000..2e817208
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIWikiPage;
+use SMW\Elastic\QueryEngine\ConditionBuilder;
+use SMW\Query\Language\ValueDescription;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDInumber as DINumber;
+use SMWDITime as DITime;
+use SMW\Utils\CharExaminer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ValueDescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @since 3.0
+ *
+ * @param ConditionBuilder $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder ) {
+ $this->conditionBuilder = $conditionBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ValueDescription $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( ValueDescription $description, $isConjunction = false ) {
+
+ $dataItem = $description->getDataItem();
+ $comparator = $description->getComparator();
+
+ $property = $description->getProperty();
+ $this->fieldMapper = $this->conditionBuilder->getFieldMapper();
+
+ $params = [];
+ $pid = false;
+ $filter = false;
+
+ if ( $property === null ) {
+ $field = "subject.sortkey";
+ } else {
+ $pid = 'P:' . $this->conditionBuilder->getID( $property );
+
+ if ( $property->isInverse() ) {
+ // Want to know if this case happens and if so we need to handle
+ // it somewhow ...
+ throw new RuntimeException( "ValueDescription with an inverted property! PID: $pid, " . $description->getQueryString() );
+ } else {
+ $field = $this->fieldMapper->getField( $property, 'Field' );
+ }
+
+ $field = "$pid.$field";
+ }
+
+ //$description->getHierarchyDepth(); ??
+ $hierarchyDepth = null;
+
+ $hierarchy = $this->conditionBuilder->findHierarchyMembers(
+ $property,
+ $hierarchyDepth
+ );
+
+ if ( $dataItem instanceof DIWikiPage && $comparator === SMW_CMP_EQ && $property === null ) {
+ // We want an exact match!
+ $field = '_id';
+ $value = $this->conditionBuilder->getID( $dataItem );
+ } elseif ( $dataItem instanceof DIWikiPage && $comparator === SMW_CMP_NEQ && $property === null ) {
+ // We want an exact match!
+ $field = '_id';
+ $value = $this->conditionBuilder->getID( $dataItem );
+ } elseif ( $dataItem instanceof DIWikiPage && $comparator === SMW_CMP_EQ ) {
+ $field = "$pid.wpgID";
+ $value = $this->conditionBuilder->getID( $dataItem );
+ } elseif ( $dataItem instanceof DIWikiPage && $comparator === SMW_CMP_NEQ ) {
+ $field = "$pid.wpgID";
+ $value = $this->conditionBuilder->getID( $dataItem );
+ } elseif ( $dataItem instanceof DIWikiPage ) {
+ $value = $dataItem->getSortKey();
+ } elseif ( $dataItem instanceof DITime ) {
+ $field = "$field.keyword";
+ $value = $dataItem->getJD();
+ } elseif ( $dataItem instanceof DIBoolean ) {
+ $value = $dataItem->getBoolean();
+ } elseif ( $dataItem instanceof DINumber ) {
+ $value = $dataItem->getNumber();
+ } else {
+ $value = $dataItem->getSerialization();
+ }
+
+ if ( $dataItem instanceof DIWikiPage && $this->isRange( $comparator ) ) {
+ $params = $this->fieldMapper->range( "$field.keyword", $value, $comparator );
+ } elseif ( $dataItem instanceof DIBlob && $comparator === SMW_CMP_EQ ) {
+ $params = $this->fieldMapper->match( "$field", "\"$value\"" );
+ } elseif ( $comparator === SMW_CMP_EQ || $comparator === SMW_CMP_NEQ ) {
+ $params = $this->fieldMapper->terms( "$field", $value );
+ } elseif ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) {
+ $params = $this->proximity_bool( $field, $value );
+ } elseif ( $this->isRange( $comparator ) ) {
+ $params = $this->fieldMapper->range( $field, $value, $comparator );
+ } else {
+ $params = $this->fieldMapper->match( $field, $value );
+ }
+
+ if ( $params !== [] && $pid ) {
+ $params = $this->fieldMapper->hierarchy( $params, $pid, $hierarchy );
+ }
+
+ $condition = $this->conditionBuilder->newCondition( $params );
+
+ if ( $this->isNot( $comparator ) && $isConjunction ) {
+ $condition->type( 'must_not' );
+ }
+
+ if ( !$isConjunction ) {
+ $condition->type( ( $this->isNot( $comparator ) ? 'must_not' : ( $filter ? 'filter' : 'must' ) ) );
+ }
+
+ $condition->log( [ 'ValueDescription' => $description->getQueryString() ] );
+
+ return $condition;
+ }
+
+ private function isRange( $comparator ) {
+ return $comparator === SMW_CMP_GRTR || $comparator === SMW_CMP_GEQ || $comparator === SMW_CMP_LESS || $comparator === SMW_CMP_LEQ;
+ }
+
+ private function isNot( $comparator ) {
+ return $comparator === SMW_CMP_NLKE || $comparator === SMW_CMP_NEQ;
+ }
+
+ private function proximity_bool( $field, $value ) {
+
+ $params = [];
+ $hasWildcard = strpos( $value, '*' ) !== false;
+
+ // Q1203
+ // [[phrase:fox jump*]] (aka ~"fox jump*") + wildcard; use match with
+ // a `multi_match` and type `phrase_prefix`
+ $isPhrase = strpos( $value, '"' ) !== false;
+ $isWide = false;
+
+ // Wide proximity uses ~~ as identifier as in [[~~ ... ]] or
+ // [[in:fox jumps]]
+ if ( $value{0} === '~' ) {
+ $isWide = true;
+
+ // Remove the ~ to avoid a `QueryShardException[Failed to parse query ...`
+ $value = substr( $value, 1 );
+
+ if ( !$hasWildcard && $this->conditionBuilder->getOption( 'wide.proximity.as.match_phrase', true ) ) {
+ $value = trim( $value, '"' );
+ $value = "\"$value\"";
+ }
+
+ $field = $this->conditionBuilder->getOption( 'wide.proximity.fields', [ 'text_copy' ] );
+ }
+
+ // Wide or simple proximity? + wildcard?
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html#operator-min
+ if ( $hasWildcard && $isWide && !$isPhrase ) {
+
+ // cjk.best.effort.proximity.match
+ if ( $this->isCJK( $value ) ) {
+ // Increase match accuracy by relying on a `phrase` to define char
+ // boundaries
+ $params = $this->fieldMapper->match( $field, "\"$value\"" );
+ } else {
+ $params = $this->fieldMapper->query_string( $field, $value, [ 'minimum_should_match' => 1 ] );
+ }
+
+ } elseif ( $hasWildcard && !$isWide && !$isPhrase ) {
+ // [[~Foo/Bar/*]] (simple proximity) is only used on subject.sortkey
+ // which is why we want to use a `not_analyzed` field to exactly
+ // match the content before the *.
+ // `lowercase` uses a normalizer to achieve case insensitivity
+ if ( $this->conditionBuilder->getOption( 'page.field.case.insensitive.proximity.match', true ) ) {
+ $field = "$field.lowercase";
+ } else {
+ $field = "$field.keyword";
+ }
+
+ $params = $this->fieldMapper->wildcard( $field, $value );
+ $filter = true;
+ } else {
+ $params = $this->fieldMapper->match( $field, $value );
+ }
+
+ return $params;
+ }
+
+ /**
+ * Fields that use a standard analyzer will split CJK terms into single chars
+ * and any enclosing like *...* makes a term not applicable to the same
+ * treatment which prevents a split and hereby causing the search match to be
+ * worse off hence remove `*` in case of CJK usage.
+ */
+ private function isCJK( &$text ) {
+
+ // Only use the examiner on the standard index_def since ICU provides
+ // better CJK and may handle `*` more sufficiently
+ if ( !$this->conditionBuilder->getOption( 'cjk.best.effort.proximity.match', false ) ) {
+ return false;
+ }
+
+ if ( !CharExaminer::isCJK( $text ) ) {
+ return false;
+ }
+
+ if ( $text{0} === '*' ) {
+ $text = mb_substr( $text, 1 );
+ }
+
+ if ( mb_substr( $text , -1 ) === '*' ) {
+ $text = mb_substr( $text, 0, -1 );
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Excerpts.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Excerpts.php
new file mode 100644
index 00000000..70c7ba87
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/Excerpts.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use SMW\DIWikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Excerpts extends \SMW\Query\Excerpts {
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|string $hash
+ *
+ * @return string|integer|false
+ */
+ public function getExcerpt( $hash ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ foreach ( $this->excerpts as $map ) {
+ if ( $map[0] === $hash ) {
+ return $this->format( $map[1] );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasHighlight() {
+ return $this->noHighlight ? false : true;
+ }
+
+ private function format( $v ) {
+
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html
+ // By default, highlighted text is wrapped in <em> and </em> tags
+
+ $text = '';
+
+ if ( is_array( $v ) ) {
+ foreach ( $v as $key => $value ) {
+ $text .= implode( ' ', $value ) ;
+ }
+ } else {
+ $text = $v;
+ }
+
+ if ( $this->stripTags ) {
+ $text = str_replace(
+ [ '<em>', '</em>' ],
+ [ '&lt;em&gt;', '&lt;/em&gt;' ],
+ $text
+ );
+
+ // Remove tags to avoid any output disruption
+ $text = strip_tags( $text );
+
+ $text = str_replace(
+ [ '&lt;em&gt;', '&lt;/em&gt;' ],
+ [ '<em>', '</em>' ],
+ $text
+ );
+ }
+
+ if ( $this->noHighlight ) {
+ $text = str_replace( [ '<em>', '</em>', "\n" ], [ '', '', ' ' ], $text );
+ }
+
+ return $text;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/FieldMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/FieldMapper.php
new file mode 100644
index 00000000..ae3eafb5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/FieldMapper.php
@@ -0,0 +1,676 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FieldMapper {
+
+ const TYPE_MUST = 'must';
+ const TYPE_SHOULD = 'should';
+ const TYPE_MUST_NOT = 'must_not';
+ const TYPE_FILTER = 'filter';
+
+ /**
+ * @var boolean
+ */
+ private $isCompatMode = true;
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isCompatMode
+ */
+ public function isCompatMode( $isCompatMode ) {
+ $this->isCompatMode = $isCompatMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ *
+ * @return string
+ */
+ public static function getPID( $id ) {
+ return "P:$id";
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public static function getFieldType( DIProperty $property ) {
+ return str_replace( [ '_' ], [ '' ], DataTypeRegistry::getInstance()->getFieldType( $property->findPropertyValueType() ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param string $affix
+ *
+ * @return string
+ */
+ public static function getField( DIProperty $property, $affix = 'Field' ) {
+ return self::getFieldType( $property ) . $affix;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public static function isPhrase( $value = '' ) {
+ return $value{0} === '"' && substr( $value, -1 ) === '"';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public static function hasWildcard( $value = '' ) {
+ return strpos( $value, '*' ) !== false && strpos( $value, '\*' ) === false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public function containsReservedChar( $value ) {
+
+ $reservedChars = [
+ '+', '-', '=', '&&', '||', '>', '<', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', '*', '?', ':', '\\', '//'
+ ];
+
+ foreach ( $reservedChars as $char ) {
+ if ( strpos( $value, $char ) !== false ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see https://stackoverflow.com/questions/9796470/random-order-pagination-elasticsearch
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function function_score_random( $query, $boost = 5 ) {
+ return [
+ 'function_score' => [
+ 'query' => $query,
+ "boost" => $boost,
+ "random_score" => new \stdClass(),
+ "boost_mode"=> "multiply"
+ ]
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $results
+ * @param array $params
+ *
+ * @return []
+ */
+ public function field_filter( $field, $params ) {
+
+ $idList = [];
+
+ foreach ( $params as $key => $value ) {
+
+ if ( $key === $field ) {
+ return $value;
+ }
+
+ if ( !is_array( $value ) ) {
+ return [];
+ }
+
+ return $this->field_filter( $field, $value );
+ }
+
+ return $idList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $params
+ *
+ * @return array
+ */
+ public function bool( $type, $params ) {
+ return [ 'bool' => [ $type => $params ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-constant-score-query.html
+ * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/filter-caching.html
+ *
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $params
+ *
+ * @return array
+ */
+ public function constant_score( $params ) {
+ return [ 'constant_score' => [ 'filter' => $params ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-filter-context.html
+ *
+ * "... filter context, a query clause ... is a simple Yes or No — no scores
+ * are calculated. Filter context is mostly used for filtering structured
+ * data ...", " ... used filters will be cached automatically ..."
+ *
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $params
+ *
+ * @return array
+ */
+ public function filter( $params ) {
+ return [ 'filter' => $params ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-geo-distance-query.html
+ *
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $params
+ *
+ * @return array
+ */
+ public function geo_distance( $field, $coordinates, $distance ) {
+ return [ 'geo_distance' => [ 'distance' => $distance, $field => $coordinates ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-geo-bounding-box-query.html
+ *
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $params
+ *
+ * @return array
+ */
+ public function geo_bounding_box( $field, $top, $left, $bottom, $right ) {
+ return [ 'geo_bounding_box' => [ $field => [ 'top' => $top , 'left' => $left, 'bottom' => $bottom, 'right' => $right ] ] ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function range( $field, $value, $comp = '' ) {
+
+ $comparators = [
+ SMW_CMP_LESS => 'lt',
+ SMW_CMP_GRTR => 'gt',
+ SMW_CMP_LEQ => 'lte',
+ SMW_CMP_GEQ => 'gte'
+ ];
+
+ return [
+ [ 'range' => [ "$field" => [ $comparators[$comp] => $value ] ] ]
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function match( $field, $value, $operator = 'or' ) {
+
+ if ( is_array( $field ) ) {
+ return $this->multi_match( $field, $value );
+ }
+
+ // Is it a phrase match as in "Foo bar"?
+ if ( $value !=='' && $value{0} === '"' && substr( $value, -1 ) === '"' ) {
+ return $this->match_phrase( $field, trim( $value, '"' ) );
+ }
+
+ if ( $operator !== 'or' ) {
+ return [
+ [ 'match' => [ "$field" => [ 'query' => $value, 'operator' => $operator ] ] ]
+ ];
+ }
+
+ return [
+ [ 'match' => [ $field => $value ] ]
+ ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html
+ *
+ * - `best_fields` Finds documents which match any field, but uses the _score
+ * from the best field
+ * - `most_fields` Finds documents which match any field and combines the
+ * _score from each field
+ * - `cross_fields` Treats fields with the same analyzer as though they were
+ * one big field and looks for each word in any field
+ * - `phrase` Runs a match_phrase query on each field and combines the _score
+ * from each field
+ * - `phrase_prefix` Runs a match_phrase_prefix query on each field and
+ * combines the _score from each field
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param array $params
+ *
+ * @return string
+ */
+ public function multi_match( $fields, $value, array $params = [] ) {
+
+ //return $this->multi_match( $field, trim( $value, '"' ) , [ "type" => "phrase" ] );
+
+ if ( strpos( $value, '"' ) !== false ) {
+ $value = trim( $value, '"' );
+ $params = [ "type" => "phrase" ];
+
+ if ( strpos( $value, '*' ) !== false ) {
+ $value = trim( $value, '*' );
+ $params = [ "type" => "phrase_prefix" ];
+ }
+ }
+
+ return [
+ [ 'multi_match' => [ 'fields' => $fields, 'query' => $value ] + $params ]
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ * @param array $params
+ *
+ * @return string
+ */
+ public function match_phrase( $field, $value, array $params = [] ) {
+
+ if ( strpos( $value, '*' ) !== false ) {
+ return [
+ 'match_phrase_prefix' => [ "$field" => trim( $value, '*' ) ]
+ ];
+ }
+
+ if ( $params !== [] ) {
+ return [
+ [ 'match_phrase' => [ "$field" => [ 'query' => $value ] + $params ] ]
+ ];
+ }
+
+ return [
+ [ 'match_phrase' => [ "$field" => $value ] ]
+ ];
+ }
+
+ /**
+ * In compat mode we try to guess and normalize the query string and hereby
+ * attempt to make the search execution to match closely the SMW SQL behaviour
+ * which comes at a cost that certain ES specific constructs ((), {} etc.)
+ * cannot be used when the `compat.mode` is enabled.
+ *
+ * @since 3.0
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function query_string_compat( $value, array $params = [] ) {
+
+ $wildcard = '';
+ // $params = [];
+
+ // Reserved characters are: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
+ // Failed the search with: {"error":{"root_cause":[{"type":"query_shard_exception","reason":"Failed to parse query [*{*]
+ // Use case: TQ0102
+ if ( $this->containsReservedChar( $value ) ) {
+ $value = str_replace(
+ [ '\\', '{', '}', '(', ')', '[', ']', '^', '=', '|', '/' , ':' ],
+ [ "\\\\", "\{", "\}", "\(", "\)", "\[", "\]", "\^", "\=", "\|", "\/", "\:" ],
+ $value
+ );
+ }
+
+ // Intended phrase or a single " char?
+ // Use case: TQ0102#13
+ if ( strpos( $value, '"' ) !== false && substr_count( $value, '"' ) < 2 ) {
+ $value = str_replace( '"' , '\"', $value );
+ } elseif ( substr_count( $value, '"' ) == 2 && strpos( $value, '~' ) !== false ) {
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#_fuzziness
+ // [[Has page::phrase:some text~2]] as "some text"~2
+ list( $value, $fuzziness ) = explode( '~', $value );
+ $value = "$value\"~" . str_replace( '"', '', $fuzziness );
+ }
+
+ // In this section we add modifiers to closely emulate the "known" SMW
+ // query behaviour of matching a string by SQL in terms of %foo% ...
+
+ // Uses Boolean parameters? avoid further guessing on the behalf of the
+ // query operation as in `[[Has text::~+MariaDB -database]]`
+ if ( strpos( $value, '+' ) !== false || strpos( $value, '-' ) !== false || strpos( $value, '"' ) !== false ) {
+ // Use case: `[[Has text::~sear*, -elas*]]`
+ // The user added those parameters by themselves
+ // Avoid comma separated strings
+ $value = str_replace( ',' , '', $value );
+ } elseif ( strpos( $value, ' ' ) !== false ) {
+ // Use case: `[[Has text::~some tex*]]
+ // Intention is to search for `some` AND `tex*`
+ $value = str_replace( [ ' ', '/*' ], [ ' +', '/ *' ], $value );
+ } elseif ( strpos( $value, '/' ) !== false ) {
+ // Use case: [[~Example/0608/*]]
+ // Somehow using the input as-is returns all sorts of matches mostly
+ // due to `/` being reserved hence split the string and create a
+ // conjunction using ES boolean expression `+` (AND) as in `Example`
+ // AND `0608*`
+ // T:Q0908 `http://example.org/some_title_with_a_value` becomes
+ // `example.org +some +title +with +a +value`
+ $value = str_replace( [ '\/', '/', ' +*', '_' ], [ '/', ' +', '*', ' +' ], $value );
+ } else {
+
+ // `_` in MediaWiki represents a space therefore replace it with an
+ // `+` (AND)
+ $value = str_replace( [ '_' ], [ ' ' ], $value );
+
+ // Use case: `[[Has text::~foo*]]`, `[[Has text::~foo]]`
+ // - add Boolean + which translates into "must be present"
+ if ( $value{0} !== '*' ) {
+ $value = "+$value";
+ }
+
+ // Use case: `[[Has text::~foo bar*]]`
+ if ( strpos( $value, ' ' ) !== false && substr( $value, -1 ) === '*' ) {
+ // $value = substr( $value, 0, -1 );
+ $wildcard = '*';
+ $params[ 'analyze_wildcard'] = true;
+ }
+
+ // Use case: `[[Has text::~foo bar*]]
+ // ... ( and ) signifies precedence
+ // ... " wraps a number of tokens to signify a phrase for searching
+ if ( strpos( $value, ' ' ) !== false && strpos( $value, '"' ) === false ) {
+ // $value = "\"($value)\"$wildcard";
+ }
+ }
+
+ // Force all terms to be required by ...
+ // $params[ 'default_operator'] = 'AND';
+
+ // Disable fuzzy transpositions (ab → ba)
+ // $params[ 'fuzzy_transpositions'] = false;
+
+ return [ $value, $params ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
+ * @since 3.0
+ *
+ * @param string|array $fields
+ * @param mixed $value
+ * @param array $params
+ *
+ * @return string
+ */
+ public function query_string( $fields, $value, array $params = [] ) {
+
+ if ( $this->isCompatMode ) {
+ list( $value, $params ) = $this->query_string_compat( $value, $params );
+ }
+
+ if ( !is_array( $fields ) ) {
+ $fields = [ $fields ];
+ }
+
+ return [
+ 'query_string' => [ 'fields' => $fields, 'query' => $value ] + $params
+ ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html
+ * @since 3.0
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function ids( $value ) {
+ return [ 'ids' => [ "values" => $value ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-term-query.html
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function term( $field, $value ) {
+ return [ 'term' => [ "$field" => $value ] ];
+ }
+
+ /**
+ * Filters documents that have fields that match any of the provided terms
+ * (not analyzed).
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-term-query.html
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function terms( $field, $value ) {
+
+ if ( !is_array( $value ) ) {
+ $value = [ $value ];
+ }
+
+ return [ 'terms' => [ "$field" => $value ] ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function wildcard( $field, $value ) {
+ return [ 'wildcard' => [ "$field" => $value ] ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ *
+ * @return string
+ */
+ public function exists( $field ) {
+ return [ 'exists' => [ "field" => "$field" ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-aggregations.html
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function aggs( $name, $params ) {
+ return [ 'aggregations' => [ "$name" => $params ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-aggregations-bucket-terms-aggregation.html
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function aggs_terms( $key, $field, $params = [] ) {
+ return [ $key => [ 'terms' => [ "field" => $field ] + $params ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-aggregations-bucket-significantterms-aggregation.html
+ *
+ * Aggregation based on terms that have undergone a significant change in
+ * popularity measured between a foreground and background set.
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public function aggs_significant_terms( $key, $field, $params = [] ) {
+ return [ $key => [ 'significant_terms' => [ "field" => $field ] + $params ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-aggregations-bucket-histogram-aggregation.html
+ *
+ * A multi-bucket values source based aggregation that can be applied on
+ * numeric values extracted from the documents. It dynamically builds fixed
+ * size (a.k.a. interval) buckets over the values.
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public static function aggs_histogram( $key, $field, $interval ) {
+ return [ $key => [ 'histogram' => [ "field" => $field, 'interval' => $interval ] ] ];
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-aggregations-bucket-datehistogram-aggregation.html
+ *
+ * A multi-bucket aggregation similar to the histogram except it can only be
+ * applied on date values.
+ *
+ * @since 3.0
+ *
+ * @param string $field
+ * @param mixed $value
+ *
+ * @return string
+ */
+ public static function aggs_date_histogram( $key, $field, $interval ) {
+ return [ $key => [ 'date_histogram' => [ "field" => $field, 'interval' => $interval ] ] ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Condition|array $params
+ * @param string $replacement
+ * @param array $hierarchy
+ *
+ * @return string
+ */
+ public function hierarchy( $params, $replacement, $hierarchy = [] ) {
+
+ if ( $hierarchy === [] ) {
+ return $params;
+ }
+
+ $str = is_array( $params ) ? json_encode( $params ) : (string)$params;
+
+ // P:, or iP:
+ list( $prefix, $id ) = explode( ':', $replacement );
+
+ $params = [];
+ $params[] = json_decode( $str, true );
+
+ foreach ( $hierarchy as $key ) {
+ // Quick and dirty to avoid iterating over an array and find a
+ // possible replacement without knowing the specific structure of
+ // an array
+ //
+ // Adding . to make it less likely that we replace a user value that
+ // appears as `P:42`
+ $params[] = json_decode( str_replace( "$replacement.", "$prefix:$key.", $str ), true );
+ }
+
+ $condition = new Condition( $params );
+
+ // Hierarchy as simple list of disjunctive (should) conditions where any
+ // of the condition is allowed to return a result. For example, a hierarchy
+ // defined as `Foo <- Foo1 <- Foo2` would be represented in terms of
+ // `[[Foo::bar]] OR [[Foo1::bar]] OR [[Foo2::bar]]`
+ $condition->type( 'should' );
+
+ return new Condition( $condition );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/QueryEngine.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/QueryEngine.php
new file mode 100644
index 00000000..8cda7893
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/QueryEngine.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\ApplicationFactory;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Options;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\ScoreSet;
+use SMW\QueryEngine as IQueryEngine;
+use SMW\Store;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryEngine implements IQueryEngine {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var QueryFactory
+ */
+ private $queryFactory;
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @var SortBuilder
+ */
+ private $sortBuilder;
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $queryInfo = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param ConditionBuilder $conditionBuilder
+ * @param Options|null $options
+ */
+ public function __construct( Store $store, ConditionBuilder $conditionBuilder, Options $options = null ) {
+ $this->store = $store;
+ $this->options = $options;
+
+ if ( $options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->queryFactory = ApplicationFactory::getInstance()->getQueryFactory();
+ $this->fieldMapper = new FieldMapper();
+
+ $this->conditionBuilder = $conditionBuilder;
+ $this->sortBuilder = new SortBuilder( $store );
+
+ $this->sortBuilder->setScoreField(
+ $this->options->dotGet( 'query.score.sortfield' )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param []
+ */
+ public function getQueryInfo() {
+ return $this->queryInfo;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return QueryResult
+ */
+ public function getQueryResult( Query $query ) {
+
+// if ( ( !$this->engineOptions->get( 'smwgIgnoreQueryErrors' ) || $query->getDescription() instanceof ThingDescription ) &&
+
+ if ( ( $query->getDescription() instanceof ThingDescription ) &&
+ $query->querymode != Query::MODE_DEBUG &&
+ count( $query->getErrors() ) > 0 ) {
+ return $this->queryFactory->newQueryResult( $this->store, $query, [], false );
+ // NOTE: we check this here to prevent unnecessary work, but we check
+ // it after query processing below again in case more errors occurred.
+ } elseif ( $query->querymode == Query::MODE_NONE || $query->getLimit() < 1 ) {
+ return $this->queryFactory->newQueryResult( $this->store, $query, [], true );
+ }
+
+ $this->errors = [];
+ $body = [];
+
+ $this->queryInfo = [
+ 'smw' => [],
+ 'elastic' => [],
+ 'info' => []
+ ];
+
+ list( $sort, $sortFields, $isRandom, $isConstantScore ) = $this->sortBuilder->makeSortField(
+ $query
+ );
+
+ $description = $query->getDescription();
+
+ $this->conditionBuilder->setSortFields(
+ $sortFields
+ );
+
+ $params = $this->conditionBuilder->makeFromDescription(
+ $description,
+ $isConstantScore
+ );
+
+ $this->errors = $this->conditionBuilder->getErrors();
+ $this->queryInfo['elastic'] = $this->conditionBuilder->getQueryInfo();
+
+ if ( $isRandom ) {
+ $params = $this->fieldMapper->function_score_random( $params );
+ }
+
+ $body = [
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-request-source-filtering.html
+ // We only want the ID, no need for the entire document body
+ '_source' => false,
+ 'from' => $query->getOffset(),
+ 'size' => $query->getLimit() + 1, // Look ahead +1,
+ 'query' => $params
+ ];
+
+ if ( !$isRandom && $sort !== [] ) {
+ $body['sort'] = [ $sort ];
+ }
+
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#_track_scores
+ if ( $this->sortBuilder->isScoreSort() && $query->querymode !== Query::MODE_COUNT ) {
+ $body['track_scores'] = true;
+ }
+
+ if ( $this->options->dotGet( 'query.profiling' ) ) {
+ $body['profile'] = true;
+ }
+
+ // If at all only consider the retrieval for Special:Search queries
+ if ( $query->getOption( 'highlight.fragment' ) !== false && $query->querymode !== Query::MODE_COUNT ) {
+ $this->addHighlight( $body );
+ }
+
+ $query->addErrors( $this->errors );
+
+ $connection = $this->store->getConnection( 'elastic' );
+
+ $index = $connection->getIndexNameByType(
+ ElasticClient::TYPE_DATA
+ );
+
+ $params = [
+ 'index' => $index,
+ 'type' => ElasticClient::TYPE_DATA,
+ 'body' => $body
+ ];
+
+ $this->queryInfo['elastic'][] = $params;
+
+ $this->queryInfo['smw'] = [
+ 'query' => $query->getQueryString(),
+ 'sort' => $query->getSortKeys(),
+ 'metrics' => [
+ 'query size' => $description->getSize(),
+ 'query depth' => $description->getDepth()
+ ]
+ ];
+
+ switch ( $query->querymode ) {
+ case Query::MODE_DEBUG:
+ $result = $this->newDebugQueryResult( $params );
+ break;
+ case Query::MODE_COUNT:
+ $result = $this->newCountQueryResult( $query, $params );
+ break;
+ default:
+ $result = $this->newInstanceQueryResult( $query, $params );
+ break;
+ }
+
+ return $result;
+ }
+
+ private function newDebugQueryResult( $params ) {
+
+ $params['explain'] = $this->options->dotGet( 'query.debug.explain', false );
+
+ $connection = $this->store->getConnection( 'elastic' );
+ $this->queryInfo['elastic'][] = $connection->validate( $params );
+
+ if ( ( $log = $this->conditionBuilder->getDescriptionLog() ) !== [] ) {
+ $this->queryInfo['smw']['description_log'] = $log;
+ }
+
+ if ( isset( $this->queryInfo['info'] ) && $this->queryInfo['info'] === [] ) {
+ unset( $this->queryInfo['info'] );
+ }
+
+ $info = str_replace(
+ [ '[', '<', '>', '\"', '\n' ],
+ [ '&#91;', '&lt;', '&gt;', '&quot;', '' ],
+ json_encode( $this->queryInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
+ );
+
+ $html = \Html::rawElement(
+ 'pre',
+ [
+ 'class' => 'smwpre smwpre-no-margin smw-debug-box'
+ ],
+ \Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-debug-box-header'
+ ],
+ '<big>ElasticStore debug output</big>'
+ ) . $info
+ );
+
+ return $html;
+ }
+
+ private function newCountQueryResult( $query, $params ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+ $result = $connection->count( $params );
+
+ $queryResult = $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ [],
+ false
+ );
+
+ $count = isset( $result['count'] ) ? $result['count'] : 0;
+ $queryResult->setCountValue( $count );
+
+ $this->queryInfo['info'] = $result;
+
+ return $queryResult;
+ }
+
+ private function newInstanceQueryResult( $query, array $params ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+ $scoreSet = new ScoreSet();
+ $excerpts = new Excerpts();
+
+ list( $res, $errors ) = $connection->search( $params );
+
+ $searchResult = new SearchResult( $res );
+
+ $results = $searchResult->getResults(
+ $query->getLimit()
+ );
+
+ $query->addErrors( $errors );
+
+ if ( $query->getOption( 'native_result' ) ) {
+ $query->native_result = json_encode( $res, JSON_PRETTY_PRINT |JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
+ }
+
+ $scores = $searchResult->get( 'scores' );
+ $excerptList = $searchResult->get( 'excerpts' );
+
+ // Use a bulk load via ` ... WHERE IN ...` instead of single requests
+ $dataItems = $this->store->getObjectIds()->getDataItemsFromList(
+ $results
+ );
+
+ // `... WHERE IN ...` doesn't guarantee to return the same order
+ $listPos = array_flip( $results );
+ $results = [];
+
+ // Relocate to the original position that returned from Elastic
+ foreach ( $dataItems as $dataItem ) {
+
+ // In case of an update lag (Elasticsearch is near real time where
+ // some shards may not yet have seen an update) make sure to hide any
+ // outdated entities we retrieve from the SQL as ID master back-end
+ if ( $dataItem->getInterwiki() === SMW_SQL3_SMWDELETEIW ) {
+ continue;
+ }
+
+ $id = $dataItem->getId();
+ $results[$listPos[$id]] = $dataItem;
+
+ if ( isset( $scores[$id] ) ) {
+ $scoreSet->addScore( $dataItem->getHash(), $scores[$id], $listPos[$id] );
+ }
+
+ if ( isset( $excerptList[$id] ) ) {
+ $excerpts->addExcerpt( $dataItem, $excerptList[$id] );
+ }
+ }
+
+ ksort( $results );
+
+ $queryResult = $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ $results,
+ $searchResult->get( 'continue' )
+ );
+
+ $queryResult->setScoreSet( $scoreSet );
+ $queryResult->setExcerpts( $excerpts );
+
+ return $queryResult;
+ }
+
+ private function addHighlight( &$body ) {
+
+ if ( ( $type = $this->options->dotGet( 'query.highlight.fragment.type', false ) ) === false ) {
+ return;
+ }
+
+ // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html
+ if ( !in_array( $type, [ 'plain', 'unified', 'fvh' ] ) ) {
+ return;
+ }
+
+ $body['highlight'] = [
+ 'number_of_fragments' => $this->options->dotGet( 'query.highlight.fragment.number', 1 ),
+ 'fragment_size' => $this->options->dotGet( 'query.highlight.fragment.size', 150 ),
+ 'fields' => [
+ 'attachment.content' => [ "type" => $type ],
+ 'text_raw' => [ "type" => $type ],
+ 'P*.txtField' => [ "type" => $type ]
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SearchResult.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SearchResult.php
new file mode 100644
index 00000000..a93142fa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SearchResult.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SearchResult {
+
+ /**
+ * @var []
+ */
+ private $raw = [];
+
+ /**
+ * @var []
+ */
+ private $errors = [];
+
+ /**
+ * @var []|null
+ */
+ private $results;
+
+ /**
+ * @var string
+ */
+ private $filterField = '_id';
+
+ /**
+ * @var []
+ */
+ private $container = [
+ 'info' => [],
+ 'scores' => [],
+ 'excerpts' => [],
+ 'count' => 0,
+ 'continue' => false
+ ];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $raw
+ */
+ public function __construct( array $raw = [] ) {
+ $this->raw = $raw;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $errors
+ */
+ public function setErrors( array $errors ) {
+ $this->errors = $errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $filterField
+ */
+ public function setFilterField( $filterField ) {
+ $this->filterField = $filterField;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer|null $cutoff
+ *
+ * @return array
+ */
+ public function getResults( $cutoff = null ) {
+
+ if ( $this->results === null ) {
+ $this->filter_results( $this->raw, $cutoff );
+ }
+
+ return $this->results;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function get( $key ) {
+
+ if ( isset( $this->container[$key] ) ) {
+ return $this->container[$key];
+ }
+
+ throw new InvalidArgumentException( "`$key` is an unkown key, or is not registered." );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $cutoff
+ *
+ * @return []
+ */
+ public function filter_results( array $results, $cutoff = null ) {
+
+ $this->results = [];
+
+ $this->container = [
+ 'info' => [],
+ 'scores' => [],
+ 'excerpts' => [],
+ 'count' => 0,
+ 'continue' => false
+ ];
+
+ if ( $results === [] ) {
+ return [];
+ }
+
+ $info = $results;
+ $res = $this->filter_field( $results, $cutoff );
+
+ unset( $info['hits'] );
+ unset( $info['_shards'] );
+
+ $this->results = array_keys( $res );
+ $info['max_score'] = $results['hits']['max_score'];
+ $info['total'] = count( $res );
+
+ $this->container['info'] = $info;
+ $this->container['count'] = $info['total'];
+
+ return $this->results;
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/client/php-api/6.0/_search_operations.html
+ */
+ private function filter_field( $results, $cutoff ) {
+
+ $res = [];
+ $continue = false;
+
+ $scores = [];
+ $excerpts = [];
+ $i = 0;
+
+ $field = $this->filterField;
+ $pid = null;
+
+ if ( strpos( $field, '.' ) !== false ) {
+ list( $pid, $field ) = explode( '.', $field );
+ }
+
+ foreach ( $results as $key => $value ) {
+
+ if ( !isset( $value['hits'] ) ) {
+ continue;
+ }
+
+ foreach ( $value['hits'] as $k => $v ) {
+
+ if ( $cutoff !== null && $i >= $cutoff ) {
+ $continue = true;
+ break;
+ }
+
+ $ids = [];
+
+ if ( $pid !== null && isset( $v['_source'][$pid][$field] ) ) {
+ $ids = $v['_source'][$pid][$field];
+ } elseif ( isset( $v['_source'][$field] ) ) {
+ $ids = $v['_source'][$field];
+ } elseif ( isset( $v[$field] ) ) {
+ $ids = $v[$field];
+ }
+
+ $ids = (array)$ids;
+
+ foreach ( $ids as $id ) {
+ $res[$id] = true;
+
+ if ( isset( $v['_score'] ) ) {
+ $scores[$id] = $v['_score'];
+ }
+
+ if ( isset( $v['highlight'] ) ) {
+ $excerpts[$id] = $v['highlight'];
+ }
+
+ $i++;
+ }
+ }
+ }
+
+ $this->container['scores'] = $scores;
+ $this->container['count'] = 0;
+ $this->container['excerpts'] = $excerpts;
+ $this->container['continue'] = $continue;
+
+ return $res;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SortBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SortBuilder.php
new file mode 100644
index 00000000..6d01796e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/SortBuilder.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\Store;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortBuilder {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @var string
+ */
+ private $scoreField;
+
+ /**
+ * @var boolean
+ */
+ private $isScoreSort = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $scoreField
+ */
+ public function setScoreField( $scoreField ) {
+ $this->scoreField = $scoreField;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isScoreSort() {
+ return $this->isScoreSort;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return array
+ */
+ public function makeSortField( Query $query ) {
+
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#_memory_considerations
+ // "... the relevant sorted field values are loaded into memory. This means
+ // that per shard, there should be enough memory ... string based types,
+ // the field sorted on should not be analyzed / tokenized ... numeric
+ // types it is recommended to explicitly set the type to narrower types"
+
+ $this->isScoreSort = $query->getOption( Query::SCORE_SORT );
+
+ if ( $query->getOption( Query::SCORE_SORT ) ) {
+ return [ [ '_score' => [ 'order' => $query->getOption( Query::SCORE_SORT ) ] ], [], false, false];
+ }
+
+ return $this->getFields( $query->getSortKeys() );
+ }
+
+ private function getFields( array $sortKeys ) {
+
+ $isRandom = false;
+ $isConstantScore = true;
+ $sort = [];
+ $sortFields = [];
+ $sortKeysCount = count( $sortKeys );
+
+ foreach ( $sortKeys as $key => $order ) {
+ $order = strtolower( $order );
+ $isRandom = strpos( $order, 'rand' ) !== false;
+
+ if ( strtolower( $key ) === $this->scoreField ) {
+ $key = '_score';
+ $this->isScoreSort = true;
+ $isConstantScore = false;
+ }
+
+ if ( $key === '' || $key === '#' ) {
+ $this->addDefaultField( $sort, $order, $sortKeysCount );
+ } else {
+ $this->addField( $sort, $sortFields, $key, $order );
+ }
+ }
+
+ return [ $sort, $sortFields, $isRandom, $isConstantScore ];
+ }
+
+ private function addDefaultField( &$sort, $order, $sortKeysCount ) {
+ $sort['subject.sortkey.sort'] = [ 'order' => $order ];
+
+ // Add title as extra criteria in case an entity uses the same sortkey
+ // to clarify its relative position, @see T:P0416#8
+ // Only add the title as determining factor when no other sort parameter
+ // is available
+ if ( $sortKeysCount == 1 ) {
+ $sort['subject.title.sort'] = [ 'order' => $order ];
+ }
+ }
+
+ private function addField( &$sort, &$sortFields, $key, $order ) {
+
+ $dataTypeRegistry = DataTypeRegistry::getInstance();
+ $chain = false;
+
+ // Chain?
+ if ( strpos( $key, '.' ) !== false ) {
+ $list = explode( '.', $key );
+ $last = current( $list );
+ } else {
+ $list = [ $key ];
+ }
+
+ foreach ( $list as $key ) {
+
+ if ( $key === '_score' ) {
+ $field = '_score';
+ } else {
+ $property = DIProperty::newFromUserLabel( $key );
+
+ $field = $this->fieldMapper->getField( $property, 'Field' );
+
+ $pid = $this->fieldMapper->getPID(
+ $this->store->getObjectIds()->getSMWPropertyID( $property )
+ );
+
+ // Only record the last key to be used as possible existential
+ // enforcement
+ if ( $chain === false ) {
+ $sortFields[] = "$pid.$field";
+ }
+
+ // Use special sort field on mapped fields which is not analyzed
+ if ( $this->sort_field( $field ) ) {
+ $field = "$field.sort";
+ }
+
+ $field = "$pid.$field";
+ }
+
+ if ( !isset( $sort[$field] ) ) {
+ $sort[$field] = [ 'order' => $order ];
+ }
+
+ $chain = true;
+ }
+ }
+
+ private function sort_field( $field ) {
+ return strpos( $field, 'txt' ) !== false || strpos( $field, 'wpgField' ) !== false || strpos( $field, 'uriField' ) !== false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup.php
new file mode 100644
index 00000000..ab7177c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use SMW\Elastic\QueryEngine\TermsLookup\Parameters;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+interface TermsLookup {
+
+ /**
+ * @since 3.0
+ *
+ * @return Parameters
+ */
+ public function newParameters( array $parameters = [] );
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function lookup( $key, Parameters $parameters );
+
+ /**
+ * @since 3.0
+ */
+ public function clear();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/CachingTermsLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/CachingTermsLookup.php
new file mode 100644
index 00000000..e43354e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/CachingTermsLookup.php
@@ -0,0 +1,393 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\TermsLookup;
+
+use Onoi\Cache\Cache;
+use RuntimeException;
+use SMW\Elastic\QueryEngine\Condition;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingTermsLookup extends TermsLookup {
+
+ /**
+ * Identifies the cache namespace
+ */
+ const CACHE_NAMESPACE = 'smw:elastic:lookup';
+
+ /**
+ * @var TermsLookup
+ */
+ private $termsLookup;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var []
+ */
+ private $quick_cache = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param TermsLookup $termsLookup
+ * @param Cache $cache
+ */
+ public function __construct( TermsLookup $termsLookup, Cache $cache ) {
+ $this->termsLookup = $termsLookup;
+ $this->cache = $cache;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {
+ $this->quick_cache = [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function makeCacheKey() {
+ return smwfCacheKey( self::CACHE_NAMESPACE, func_get_args() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param $type
+ * @param Parameters $parameters
+ *
+ * @return array
+ * @throws RuntimeException
+ */
+ public function lookup( $type, Parameters $parameters ) {
+
+ if ( $type === 'concept' ) {
+ return $this->concept_lookup( $parameters );
+ }
+
+ if ( $type === 'chain' ) {
+ return $this->chain_lookup( $parameters );
+ }
+
+ if ( $type === 'predef' ) {
+ return $this->predef_lookup( $parameters );
+ }
+
+ if ( $type === 'inverse' ) {
+ return $this->inverse_lookup( $parameters );
+ }
+
+ throw new RuntimeException( "$type is unknown!" );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function concept_lookup( Parameters $parameters ) {
+
+ // @see Indexer::delete
+ $parameters->set( 'id', md5( $parameters->get( 'id' ) ) );
+ $id = $parameters->get( 'id' );
+ $parameters->set( 'count', 0 );
+
+ $threshold = $this->termsLookup->getOption(
+ 'concept.terms.lookup.result.size.index.write.threshold',
+ 100
+ );
+
+ $parameters->set( 'threshold', $threshold );
+
+ $key = $this->makeCacheKey(
+ $id,
+ $threshold,
+ $parameters->get( 'fingerprint' )
+ );
+
+ if ( isset( $this->quick_cache[$key] ) ) {
+ $parameters->set( 'query.info', $this->quick_cache[$key]['info'] );
+ return $this->quick_cache[$key]['params'];
+ }
+
+ if ( ( $count = $this->cache->fetch( $key ) ) !== false ) {
+
+ $info = [
+ 'cached_concept_lookup' => $parameters->get( 'query.string' ),
+ 'count' => $count,
+ 'isFromCache' => ['id' => $id ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ $params = $this->termsLookup->terms_filter(
+ '_id',
+ $this->termsLookup->path_filter( $id )
+ );
+
+ $this->quick_cache[$key] = [
+ 'params' => $params,
+ 'info' => $parameters->get( 'query.info' )
+ ];
+
+ return $params;
+ }
+
+ $ttl = $this->termsLookup->getOption(
+ 'concept.terms.lookup.cache.lifetime',
+ 60
+ );
+
+ $params = $this->termsLookup->concept_index_lookup(
+ $parameters
+ );
+
+ $count = $parameters->get( 'count' );
+
+ if ( $count >= $threshold ) {
+ $this->cache->save( $key, $count, $ttl );
+ }
+
+ $this->quick_cache[$key] = [
+ 'params' => $params,
+ 'info' => $parameters->get( 'query.info' )
+ ];
+
+ if ( isset( $params['type'] ) && isset( $params['id'] ) ) {
+ $params = $this->termsLookup->terms_filter(
+ '_id',
+ $this->termsLookup->path_filter( $params['id'] )
+ );
+ }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function chain_lookup( Parameters $parameters ) {
+
+ $params = $parameters->get( 'params' );
+
+ if ( $params instanceof Condition ) {
+ $id = 'chain:' . md5( $params->__toString() );
+ } else {
+ $id = 'chain:' . md5( json_encode( $params ) );
+ }
+
+ $parameters->set( 'id', $id );
+ $parameters->set( 'count', 0 );
+
+ $threshold = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.result.size.index.write.threshold',
+ 100
+ );
+
+ $parameters->set( 'threshold', $threshold );
+
+ $key = $this->makeCacheKey(
+ $id,
+ $threshold
+ );
+
+ if ( ( $count = $this->cache->fetch( $key ) ) !== false ) {
+
+ $info = [
+ 'cached_chain_lookup' => [
+ $parameters->get( 'property.key' ),
+ $parameters->get( 'query.string' )
+ ],
+ 'count' => $count,
+ 'isFromCache' => [ 'id' => $id ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ $params = $this->termsLookup->terms_filter(
+ $parameters->get( 'terms_filter.field' ),
+ $this->termsLookup->path_filter( $id )
+ );
+
+ return $params;
+ }
+
+ $ttl = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.cache.lifetime',
+ 60
+ );
+
+ $params = $this->termsLookup->chain_index_lookup(
+ $parameters
+ );
+
+ $count = $parameters->get( 'count' );
+
+ if ( $count >= $threshold ) {
+ $this->cache->save( $key, $count, $ttl );
+ }
+
+ return $params;
+ }
+
+ /**
+ * `[[Has monolingual text:: <q>[[Text::two]] [[Language code::fr]]</q> ]] [[Has number::123]]`
+ *
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function predef_lookup( Parameters $parameters ) {
+
+ $params = $parameters->get( 'params' );
+
+ if ( $params instanceof Condition ) {
+ $id = 'pre:' . md5( $params->__toString() );
+ } else {
+ $id = 'pre:' . md5( json_encode( $params ) );
+ }
+
+ $parameters->set( 'id', $id );
+ $parameters->set( 'count', 0 );
+
+ $threshold = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.result.size.index.write.threshold',
+ 100
+ );
+
+ $parameters->set( 'threshold', $threshold );
+
+ $key = $this->makeCacheKey(
+ $id,
+ $threshold
+ );
+
+ if ( ( $count = $this->cache->fetch( $key ) ) !== false ) {
+
+ $info = [
+ 'cached_predefined_lookup' => $parameters->get( 'query.string' ),
+ 'count' => $count,
+ 'isFromCache' => [ 'id' => $id ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ $params = $this->termsLookup->terms_filter(
+ $parameters->get( 'field' ),
+ $this->termsLookup->path_filter( $id )
+ );
+
+ return $params;
+ }
+
+ $ttl = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.cache.lifetime',
+ 60
+ );
+
+ $params = $this->termsLookup->predef_index_lookup(
+ $parameters
+ );
+
+ $count = $parameters->get( 'count' );
+
+ if ( $count >= $threshold ) {
+ $this->cache->save( $key, $count, $ttl );
+ }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function inverse_lookup( Parameters $parameters ) {
+
+ $params = $parameters->get( 'params' );
+
+ if ( $params instanceof Condition ) {
+ $id = 'inv:' . md5( $parameters->get( 'field' ) . $params->__toString() );
+ } else {
+ $id = 'inv:' . md5( json_encode( [ $parameters->get( 'field' ), $params ] ) );
+ }
+
+ $parameters->set( 'id', $id );
+ $parameters->set( 'count', 0 );
+
+ $threshold = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.result.size.index.write.threshold',
+ 100
+ );
+
+ $parameters->set( 'terms_filter.field', '_id' );
+ $parameters->set( 'threshold', $threshold );
+
+ $key = $this->makeCacheKey(
+ $id,
+ $threshold
+ );
+
+ if ( ( $count = $this->cache->fetch( $key ) ) !== false ) {
+
+ $info = [
+ 'cached_inverse_lookup' => [
+ $parameters->get( 'property.key' ),
+ $parameters->get( 'query.string' )
+ ],
+ 'count' => $count,
+ 'isFromCache' => [ 'id' => $id ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ // Return the _id field
+ $params = $this->termsLookup->terms_filter(
+ $parameters->get( 'terms_filter.field' ),
+ $this->termsLookup->path_filter( $id )
+ );
+
+ return $params;
+ }
+
+ $ttl = $this->termsLookup->getOption(
+ 'subquery.terms.lookup.cache.lifetime',
+ 60
+ );
+
+ $params = $this->termsLookup->inverse_index_lookup(
+ $parameters
+ );
+
+ $count = $parameters->get( 'count' );
+
+ if ( $count >= $threshold ) {
+ $this->cache->save( $key, $count, $ttl );
+ }
+
+ return $params;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/Parameters.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/Parameters.php
new file mode 100644
index 00000000..189dfb6a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/Parameters.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\TermsLookup;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Parameters {
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( array $parameters = [] ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+ $this->parameters[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param array $value
+ */
+ public function merge( $key, array $value ) {
+
+ if ( !isset( $this->parameters[$key] ) ) {
+ $this->parameters[$key] = [];
+ }
+
+ $this->parameters[$key] += $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function has( $key ) {
+ return isset( $this->parameters[$key] ) || array_key_exists( $key, $this->parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return $this->parameters[$key];
+ }
+
+ throw new InvalidArgumentException( "$key is an unregistered key." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/TermsLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/TermsLookup.php
new file mode 100644
index 00000000..92f656ac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/TermsLookup/TermsLookup.php
@@ -0,0 +1,415 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine\TermsLookup;
+
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\Elastic\Connection\Client as ElasticClient;
+use SMW\Elastic\QueryEngine\Condition;
+use SMW\Elastic\QueryEngine\TermsLookup as ITermsLookup;
+use SMW\Elastic\QueryEngine\FieldMapper;
+use SMW\Elastic\QueryEngine\SearchResult;
+use SMW\Options;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TermsLookup implements ITermsLookup {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param Options $options
+ */
+ public function __construct( Store $store, Options $options = null ) {
+ $this->store = $store;
+ $this->options = $options;
+
+ if ( $options === null ) {
+ $this->options = new Options();
+ }
+
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {}
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return Parameters
+ */
+ public function newParameters( array $parameters = [] ) {
+ return new Parameters( $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = false ) {
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param $type
+ * @param Parameters $parameters
+ *
+ * @return array
+ * @throws RuntimeException
+ */
+ public function lookup( $type, Parameters $parameters ) {
+
+ if ( $type === 'concept' ) {
+ return $this->concept_index_lookup( $parameters );
+ }
+
+ if ( $type === 'chain' ) {
+ return $this->chain_index_lookup( $parameters );
+ }
+
+ if ( $type === 'predef' ) {
+ return $this->predef_index_lookup( $parameters );
+ }
+
+ if ( $type === 'inverse' ) {
+ return $this->inverse_index_lookup( $parameters );
+ }
+
+ throw new RuntimeException( "$type is unknown!" );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function concept_index_lookup( Parameters $parameters ) {
+
+ $params = $parameters->get( 'params' );
+ $query = $params instanceof Condition ? $params->toArray() : $params;
+
+ if ( $this->options->safeGet( 'subquery.constant.score', true ) ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ $parameters->set( 'search.body', [ "_source" => false, 'query' => $query ] );
+ $parameters->set( 'result_filter.field', '_id' );
+
+ $info = [
+ 'concept_lookup_query' => [
+ $parameters->get( 'hash' ),
+ $parameters->get( 'query.string' )
+ ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ $results = $this->query_result( $parameters );
+
+ // Already in the `terms_filter` structure?
+ if ( isset( $results['type'] ) && isset( $results['id'] ) ) {
+ return $results;
+ }
+
+ return $this->ids_filter( $results );
+ }
+
+ /**
+ * Chainable queries (or better subqueries) aren't natively supported in ES.
+ *
+ * This creates its own query and executes it as independent transaction to
+ * return a list of matchable `_id` to can be fed to the source query.
+ *
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function chain_index_lookup( Parameters $parameters ) {
+
+ $id = $parameters->get( 'id' );
+
+ $query = $this->fieldMapper->bool( 'must', $parameters->get( 'params' ) );
+
+ if ( $this->options->safeGet( 'subquery.constant.score', true ) ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ $parameters->set( 'search.body', [ "_source" => false, 'query' => $query ] );
+ $parameters->set( 'result_filter.field', '_id' );
+
+ $info = [
+ 'chain_lookup_query' => [
+ $parameters->get( 'property.key' ),
+ $parameters->get( 'query.string' )
+ ]
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ return $this->terms_filter( $parameters->get( 'terms_filter.field' ), $this->query_result( $parameters ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function predef_index_lookup( Parameters $parameters ) {
+
+ $id = $parameters->get( 'id' );
+ $params = $parameters->get( 'params' );
+
+ if ( $params instanceof Condition ) {
+ $query = $params->toArray();
+ } else {
+ $query = $this->fieldMapper->bool( 'must', $params );
+ }
+
+ if ( $this->options->safeGet( 'subquery.constant.score', true ) ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ $parameters->set( 'search.body', [ "_source" => false, 'query' => $query ] );
+ $parameters->set( 'result_filter.field', '_id' );
+
+ $info = [
+ 'predef_lookup_query' => $parameters->get( 'query.string' )
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ return $this->terms_filter( $parameters->get( 'field' ), $this->query_result( $parameters ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parameters $parameters
+ *
+ * @return array
+ */
+ public function inverse_index_lookup( Parameters $parameters ) {
+
+ $id = $parameters->get( 'id' );
+ $params = $parameters->get( 'params' );
+
+ $info = [
+ 'inverse_lookup_query' => [
+ $parameters->get( 'property.key' ),
+ $parameters->get( 'query.string' )
+ ]
+ ];
+
+ $parameters->set( 'query.info', $info + [ 'empty' ] );
+
+ if ( !is_string( $params ) && ( $params === [] || $params == 0 ) ) {
+ return [];
+ }
+
+ $field = $parameters->get( 'field' );
+
+ if ( $params === '' ) {
+ $query = $this->fieldMapper->bool( 'must', $this->fieldMapper->exists( "$field" ) );
+ // [[-Has subobject::+]] vs. [[-Has number::+]]
+ $field = strpos( $field, 'wpg' ) !== false ? $field : "_id";
+ } else {
+ $query = $this->fieldMapper->bool( 'must', $this->fieldMapper->terms( '_id', $params ) );
+ }
+
+ if ( $this->options->safeGet( 'subquery.constant.score', true ) ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ $parameters->set( 'search.body', [ "_source" => [ $field ], 'query' => $query ] );
+ $parameters->set( 'result_filter.field', $field );
+ $parameters->set( 'query.info', $info );
+
+ return $this->terms_filter( '_id', $this->query_result( $parameters ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ * @param array $params
+ *
+ * @return array
+ */
+ public function terms_filter( $field, $params ) {
+
+ if ( $params === [] ) {
+ // Fail with a non existing condition to avoid a " ...
+ // query malformed, must start with start_object ..."
+ return $this->fieldMapper->exists( "empty.lookup_query" );
+ }
+
+ $params = $this->fieldMapper->terms(
+ $field,
+ $params
+ );
+
+ // if ( $this->options->safeGet( 'subquery.constant.score', true ) ) {
+ // $params = $this->fieldMapper->constant_score( $params );
+ // }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function ids_filter( $params ) {
+
+ if ( $params === [] ) {
+ // Fail with a non existing condition to avoid a " ...
+ // query malformed, must start with start_object ..."
+ return $this->fieldMapper->exists( "empty.lookup_query" );
+ }
+
+ $params = $this->fieldMapper->ids(
+ $params
+ );
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ *
+ * @return array
+ */
+ public function path_filter( $id ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+
+ $params = [
+ 'index' => $connection->getIndexName( ElasticClient::TYPE_LOOKUP ),
+ 'type' => ElasticClient::TYPE_LOOKUP,
+ 'id' => $id
+ ];
+
+ // Define path for the terms filter
+ return $params + [ 'path' => 'id' ];
+ }
+
+ private function query_result( Parameters $parameters ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+ $info = $parameters->get( 'query.info' );
+
+ $params = [
+ 'index' => $connection->getIndexName( ElasticClient::TYPE_DATA ),
+ 'type' => ElasticClient::TYPE_DATA,
+ 'body' => $parameters->get( 'search.body' ),
+ 'size' => $this->options->safeGet( 'subquery.size', 100 )
+ ];
+
+ $info = $info + [
+ 'query' => $params,
+ 'search_info' => [ 'search_info' => [ 'total' => 0 ] ],
+ 'isFromCache' => false
+ ];
+
+ $parameters->set( 'query.info', $info );
+
+ if ( $parameters->get( 'params' ) === [] ) {
+ return [];
+ }
+
+ list( $res, $errors ) = $connection->search(
+ $params
+ );
+
+ $searchResult = new SearchResult( $res );
+ $searchResult->setFilterField( $parameters->get( 'result_filter.field' ) );
+ $searchResult->setErrors( $errors );
+
+ $results = $searchResult->getResults();
+ $count = $searchResult->get( 'count' );
+
+ if ( $count >= $parameters->get( 'threshold' ) ) {
+ $results = $this->terms_index( $parameters->get( 'id' ), $results );
+ }
+
+ $info['search_info'] = $searchResult->get( 'info' );
+
+ $parameters->set( 'query.info', $info );
+ $parameters->set( 'count', $count );
+
+ return $results;
+ }
+
+ /**
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-terms-query.html
+ */
+ private function terms_index( $id, $results ) {
+
+ $connection = $this->store->getConnection( 'elastic' );
+
+ $params = [
+ 'index' => $connection->getIndexName( ElasticClient::TYPE_LOOKUP ),
+ 'type' => ElasticClient::TYPE_LOOKUP,
+ 'id' => $id
+ ];
+
+ // https://www.elastic.co/blog/terms-filter-lookup
+ // From the documentation "... the terms filter will be fetched from a
+ // field in a document with the specified id in the specified type and
+ // index. Internally a get request is executed to fetch the values from
+ // the specified path. At the moment for this feature to work the _source
+ // needs to be stored ..."
+ $connection->index( $params + [ 'body' => [ 'id' => $results ] ] );
+
+ // Refresh to ensure results are available for the upcoming search
+ $connection->refresh( $params );
+
+ // Define path for the terms filter
+ return $params + [ 'path' => 'id' ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/README.md b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/README.md
new file mode 100644
index 00000000..ab5cf87a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/README.md
@@ -0,0 +1,519 @@
+# ElasticStore
+
+[Requirements](#requirements) | [Features](#features) | [Usage](#usage) | [Settings](#settings) | [Technical notes](#technical-notes) | [FAQ](#faq)
+
+The `ElasticStore` provides a framework to replicate Semantic MediaWiki related data to an Elasticsearch cluster and enable its `QueryEngine` to send `#ask` requests and retrieve information from Elasticsearch (aka ES) instead of the default `SQLStore`.
+
+The objective is to:
+
+- improve structured and allow unstructured content searches
+- extend and improve full-text query support (including sorting of results by [relevancy][es:relevance])
+- provide means for a scalability strategy by relying on the ES infrastructure
+
+## Requirements
+
+- Elasticsearch: Recommended 6.1+, Tested with 5.6.6
+- Semantic MediaWiki: 3.0+
+- [`elasticsearch/elasticsearch`][packagist:es] (PHP ^7.0 `~6.0` or PHP ^5.6.6 `~5.3`)
+
+We rely on the [elasticsearch php-api][es:php-api] to communicate with Elasticsearch and are therefore independent from any other vendor or MediaWiki extension that may use ES as search backend (e.g. `CirrusSearch`).
+
+It is recommended to use:
+
+- ES 6+ due to improvements to its [sparse field][es:6] handling
+- ES hardware with "... machine with 64 GB of RAM is the ideal sweet spot, but 32 GB and 16 GB machines are also common ..." as noted in the [elasticsearch guide][es:hardware]
+
+### Why Elasticsearch?
+
+- It it is relatively easy to install and run an ES instance (also on not recommended hardware).
+- ES allows to scale its cluster horizontally without requiring changes to Semantic MediaWiki or its query engine.
+- It is more likely that a user in a MediaWiki environment can provided access to an ES instance than to a `SPARQL` triple store (or a SORL/Lucence backend).
+
+## Features
+
+- Handle property type changes without the need to rebuild the entire index itself after it is ensured that all `ChangePropagation` jobs have been processed
+- Inverse queries are supported (e.g. `[[-Foo::Bar]]`)
+- Property chains and paths queries are supported (e.g. `[[Foo.Bar::Foobar]]`)
+- Category and property hierarchies are supported
+
+ES is not expected to be used as data store and therefore it is not assumed that ES returns any `_source` fields or any other data object (exception is the highlighting) besides those document IDs that match a query condition.
+
+The `ElasticStore` provides a customized serialization format to transform and transfer data, an interpreter (see [domain language][es:dsl]) allows `#ask` queries to be answered by an ES instance.
+
+## Usage
+
+The objective is to use Elasticsearch as drop-in replacement for the existing `SQLStore` based query answering but before it can provide this functionality, some settings and user actions are required:
+
+- Set `$GLOBALS['smwgDefaultStore'] = 'SMWElasticStore';`
+- Set `$GLOBALS['smwgElasticsearchEndpoints'] = [ ... ];`
+- Run `php setupStore.php` or `php update.php`
+- Rebuild the index using `php rebuildElasticIndex.php`
+
+For ES specific settings, please consult the [elasticsearch][es:conf] manual.
+
+### Indexing, updates, and refresh intervals
+
+Updates to an ES index happens instantaneously during a page action to guarantee that queries can use the latest available data set.
+
+This [page][es:create:index] decribes the index creation process where Semantic MediaWiki provides two index types:
+
+- the `data` index that hosts all user-facing queryable data (structured and unstructured content) and
+- the `lookup` index to store queries used for concept, property path, and inverse match computations
+
+#### Indexing
+
+The `rebuildElasticIndex.php` script is provided as method to replicate existing data from the `SQLStore` (fetches information directly from the property tables) to the ES backend instead of reparsing all content using the MW parser. The script operates in a [rollover mode][es:alias-zero] which is if there is already an existing index, a new index with a different version is created, leaving the current active index untouched and allowing queries to continue to operate while the index process is ongoing. Once completed, the new index switches places with the old index and is removed from the ES cluster at this point.
+
+It should be noted that __active replication__ is paused for the duration of the rebuild in order for changes to be processed after the re-index has been completed. It is __obligatory__ to run the job scheduler after the completion of the task to process any outstanding jobs.
+
+#### Safe replication
+
+The `ElasticStore` by default is set to a safe replication mode which entails that if during a page storage __no__ connection could be established to an ES cluster, a `smw.elasticIndexerRecovery` job is planned for changes that were not replicated. These jobs should be executed on a regular basis to ensure that data are kept in sync with the backend.
+
+The `job.recovery.retries` setting is set to a maximum of retry attempts in case the job itself cannot establish a connection after which the job is canceled even though it could __not__ recover.
+
+#### Refresh interval
+
+The [`refresh_interval`][es:indexing:speed] dictates how often Elasticsearch creates new [segments][stack:segments] and it set to `1s` as default. During the rebuild process the setting is changed to `-1` as recommended by the [documentation][es:indexing:speed]. If for some reason (aborted rebuild, exception etc.) the `refresh_interval` remained at `-1` then changes to an index will not be visible until a refresh has been commanded and to fix the situation it is suggested to run:
+
+- `php rebuildElasticIndex.php --update-settings`
+- `php rebuildElasticIndex.php --force-refresh`
+
+### Querying and searching
+
+`#ask` queries are system agnostic meaning that queries that worked with the `SQLStore` (or `SPARQLStore`) are expected to work equally with `ElasticStore` and not requiring any modifications to a query or its syntax.
+
+The `ElasticStore` has set its query execution to a `compat.mode` where queries are expected to return the same results as the `SQLStore`. In some instances ES could provide a different result set especially in connection with boolean query operators but the `compat.mode` warrants consistency among results retrieved from the `ElasticStore` in comparison to the `SQLStore` especially when running the same set of integration tests against each store.
+
+#### Filter and query context
+
+Most searches with a discrete value in Semantic MediaWiki will be classified as [structured search][es:structured:search] that operates with a [filter context][es:filter:context] while full-text or proximity searches use a [query context][es:query:context] that attributes to a relevancy score. A filter context will always yield a `1` relevancy score as it is translated into on a boolean operation which either matches or neglects a result as part of a set.
+
+* `[[Has page::Foo]]` (filter context) to match entities with value `Foo` for the property `Has page`
+* `[[Has page::~*Foo*]]` (query context) to match entities with any value that contains `Foo` (and `FOO`,`foo` etc. ) for the `Has page` property
+
+To improve the handling of proximity searches the following expression can be used.
+
+Expression | Interpret as | Description | Note
+------------ | ------------- | ------------- | -------------
+`in: ...` | `~~* ... *` or `~* ... *` | Find anything that contains `...` | The `in:` expression can also be combined with a property and depending on the type context is interpret differently.
+`phrase: ...` | `~~" ... "` or `~" ... "` | Find anything that contains `...` in the exact same order | The `phrase:` expression is only relevant for literal components such as text or page titles as well as unstructured text.
+`not: ...` | `!~~...` or `!~...` | Do not match any entity that matches `...` | The `not:` expression is intended to only match the exact entered term. It can be extended using `*` if necessary (e.g. `[[Has text::not:foo*]]`)
+
+A wide proximity is expressed with `~~` and the intent to search where a specific property is unknown (in case of ES it can expand the search radius to fields that have not been annotated or processed by Semantic MediaWiki prior a query request, see `indexer.raw.text` and `experimental.file.ingest`)
+
+Type | #ask | Interpret as
+------------ | ------------ | -------------
+\- | `[[in:some foo]]` | `[[~~*some foo*]]`
+Text | `[[Has text::in:some foo]]` | `[[Has text::~*some foo*]]`
+Page | `[[Has page::in:foo]]` | `[[Has text::~*foo*]]`
+Number | `[[Has number::in:99]]` | `[[Has number:: [[≥0]] [[≤99]] ]]`
+&nbsp; | `[[Has number::in:-100]]` | `[[Has number:: [[≥-100]] [[≤0]] ]]`
+Time | `[[Has date::in:2000]]` | `[[Has date:: <q>[[≥2000]] [[<<1 January 2001 00:00:00]]</q> ]]`
+
+#### Relevancy and scores
+
+[Relevancy][es:relevance] sorting is a topic of its own (and is only provided by ES and the `ElasticStore`). In order to sort results by a score, the `#ask` query needs to signal that a different context is required during the query execution. The `es.score` sortkey (see `score.sortfield` and is used as convention key) signals to the `QueryEngine` that for a non-filtered context score tracking is to be enabled.
+
+Only query constructs that use a non-filtered context (`~/!~/in/phrase/not`) provide meaningful scores that are expressive enough for sorting results otherwise results will not be distinguishable and not contribute to a meaningful overall sorting experience.
+
+<pre>
+// Find entities that contains "some text" in the property `Has text` and sort
+// by its score returned from each matched document
+
+{{#ask: [[Has text::in:some text]]
+ |sort=es.score
+ |order=desc
+}}
+</pre>
+
+#### Property chains, paths, and subqueries
+
+ES doesn't support [subqueries][es:subqueries] or [joins][es:joins] natively but the `ElasticStore` facilitates the [terms lookup][es:terms-lookup] to execute path or chain of properties and hereby builds an iterative process allowing to create a set of results that match a path condition (e.g. `Foo.bar.foobar`) with each element holding a restricted list of results from the previous execution to traverse the property path.
+
+The introduced process allows to match the `SQLStore` behaviour in terms of path queries where the `QueryEngine` is splitting each path and computes a list of elements. To avoid issues with a possible output of a vast list of matches, Semantic MediaWiki will "park" those results in the `lookup` index with the `subquery.terms.lookup.index.write.threshold` setting (default is 100) directing as to when the results are move into a separate `lookup` index.
+
+#### Hierarchies
+
+Property and category hierarchies are supported by relying on a conjunctive boolean expression for hierarchy members that are computed outside of the ES framework (the ES [parent join][es:parent-join] type is not used for this).
+
+#### Unstructured text
+
+Two experimental settings allow to handle unstructured text (content that does not provide any explicit property value annotations) using a separate field in ES.
+
+##### Raw text
+
+The `indexer.raw.text` setting enables to replicate the entire raw text of a page together with existing annotations so that unprocessed text can be searched in tandem with structured queries.
+
+##### File content
+
+This requires the ES [ingest-attachment plugin][es:ingest] and the ``indexer.experimental.file.ingest` setting.
+
+The [ingest][es:ingest] process provides a method to retrieve content from files and make them available to ES and Semantic MediaWiki without requiring the actual content to be stored within a wiki page.
+
+In case where the ingestions and extraction was successful, a `File attachment` annotation will appear on the specific `File` entity and depending on the extraction quality of ES and Tika additional annotations will be added such as:
+
+- `Content type`,
+- `Content author`,
+- `Content length`,
+- `Content language`,
+- `Content title`,
+- `Content date`, and
+- `Content keyword`
+
+Due to size and memory consumption by ES/Tika, file content ingestions exclusively happens in background using the `smw.elasticFileIngest` job. Only after the job has been executed successfully, aforementioned annotations and file content will be accessible during a query request.
+
+An "unstructured search" (i.e. searching without a property assignment) requires the wide proximity expression which conveniently are available as shortcut using `in:`, `phrase:`, or `not:`.
+
+#### Query debugging
+
+`format=debug` will output a detailed description of the `#ask` and ES DSL used for a query answering making it possible to analyze and retrieve explanations from ES about a query request.
+
+### Special:Search integration
+
+In case [SMWSearch][smw:search] was enabled, it is possible to retrieve [highlighted][es:highlighting] text snippets for matched entities from ES given that `special_search.highlight.fragment.type` is set to one of the excepted types (`plain`, `unified`, and `fvh`). Type `plain` can be used without any specific requirements, for the other types please consult the ES documentation.
+
+## Settings
+
+Accessing an ES cluster from within Semantic MediaWiki requires some settings and customization and includes:
+
+- [`$smwgElasticsearchEndpoints`](https://www.semantic-mediawiki.org/wiki/Help:$smwgElasticsearchEndpoints)
+- [`$smwgElasticsearchConfig`](https://www.semantic-mediawiki.org/wiki/Help:$smwgElasticsearchConfig)
+- [`$smwgElasticsearchProfile`](https://www.semantic-mediawiki.org/wiki/Help:$smwgElasticsearchProfile)
+
+### Endpoints
+
+`smwgElasticsearchEndpoints` is a __required__ setting and contains a list of available endpoints to create a connection with an ES cluster.
+
+<pre>
+$GLOBALS['smwgElasticsearchEndpoints'] = [
+ [ 'host' => '192.168.1.126', 'port' => 9200, 'scheme' => 'http' ],
+ 'localhost:9200'
+];
+</pre>
+
+Please consult the [reference material][es:conf:hosts] for details about the correct notation form.
+
+### Config
+
+`$smwgElasticsearchConfig` is a compound setting that collects various settings related to connection, index, and query details.
+
+<pre>
+$GLOBALS['smwgElasticsearchConfig'] = [
+
+ // Points to index and mapping definition files
+ 'index_def' => [ ... ],
+
+ // Defines connection details for ES endpoints
+ 'connection' => [ ... ],
+
+ // Holds replication details
+ 'indexer' => [ ... ],
+
+ // Used to modify ES specific settings
+ 'settings' => [ ... ],
+
+ // Section to optimize the query execution
+ 'query' => [ ... ]
+];
+</pre>
+
+A detailed list of settings and their explanations are available in the `DefaultSettings.php`. Please make sure that after changing any setting, `php rebuildElasticIndex.php --update-settings` is executed.
+
+When modifying a particular setting, use an appropriate key to change the value of a parameter otherwise it is possible that the entire configuration is replaced.
+
+<pre>
+// Uses a specific key and therefore replaces only the specific parameter
+$GLOBALS['smwgElasticsearchConfig']['query']['uri.field.case.insensitive'] = true;
+
+// !!Override!! the entire configuration
+$GLOBALS['smwgElasticsearchConfig'] = [
+ 'query' => [
+ 'uri.field.case.insensitive' => true
+ ]
+];
+</pre>
+
+#### Shards and replicas
+
+A default shards and replica configuration is applied to with:
+
+- The `data` index has two primary shards and two replicas
+- The `lookup` index has one primary shard and no replica with the documentation noting that "... consider using an index with a single shard ... lookup terms filter will prefer to execute the get request on a local node if possible ..."
+
+If it is required to change the numbers of [shards][es:shards] and replicas then use the `$smwgElasticsearchConfig` setting.
+
+<pre>
+$GLOBALS['smwgElasticsearchConfig']['settings']['data'] = [
+ 'number_of_shards' => 3,
+ 'number_of_replicas' => 3
+]
+</pre>
+
+ES comes with a precondition that any change to the `number_of_shards` requires to rebuild the entire index, so changes to that setting should be made carefully and in advance.
+
+Read-heavy wikis might want to add (without the need re-index the data) replica shards at the time ES performance is in decline. As noted, [replica shards][es:replica-shards] should be put on an extra hardware.
+
+#### Index mappings
+
+By default `index_def` points to the index definition and the `data` index is assigned the `smw-data-standard.json` to define its settings and mappings that influence how ES analyzes and index documents including fields that are identified to contain text and string elements. Those text fields use the [standard analyzer][es:standard:analyzer] and should work for most applications.
+
+The index name will be composed of a prefix such as `smw-data` (or `smw-lookup`), the wikiID, and a version indicator (used by the rollover) so that a single ES cluster can host different indices from different Semantic MediaWiki instances without interfering with each other.
+
+#### Text, languages, and analyzers
+
+For certain languages the `icu` analyzer (or any other language specific configuration) may provide better results therefore `index_def` provides a possibility to change the assignments and hereby allows custom settings such as different language [analyzer][es:lang:analyzer] to be used and increase the likelihood of better matching precision for text elements.
+
+`smw-data-icu.json` is provided as example on how to alter those settings. It should be noted that query results on text fields may differ compared to when one would use the standard analyzer and users are expected to evaluate whether those settings are more favorable or not to a query answering.
+
+Besides the different index mappings, it is recommended for a non-latin language environments to add the [analysis-icu plugin][es:icu:tokenizer] and select `smw-data-icu.json` as index definition (see also the [unicode normalization][es:unicode:normalization] guide) to make use of better unicode normalization and [case folding][es:unicode:case:folding].
+
+Please note that any change to the index or its analyzer settings __requires__ to rebuild the entire index.
+
+### Profile
+
+`$smwgElasticsearchProfile` is provided to simplify the maintenance of configuration parameters by linking to a JSON file that hosts and hereby alters individual settings.
+
+<pre>
+{
+ "indexer": {
+ "raw.text": true
+ },
+ "query": {
+ "uri.field.case.insensitive": true
+ }
+}
+</pre>
+
+The profile is loaded last and will override any default or individual settings made in `$smwgElasticsearchConfig`.
+
+## Technical notes
+
+Classes and objects related to the Elasticsearch interface and implementation are placed under the `SMW\Elastic` namespace.
+
+<pre>
+SMW\Elastic
+┃ ┠┠Admin # Classes used to extend `Special:SemanticMediaWiki`
+┃ ┠┠Exception
+┃ ┠┠Connection # Responsible for building a connection to ES
+┃ ┠┠Indexer # Contains all necessary classes for updating the ES index
+┃ ┕┠QueryEngine # Hosts the query builder and `#ask` language interpreter classes
+┃
+â” â” ElasticFactory
+┕┠ElasticStore
+</pre>
+
+### Field mapping and serialization
+
+<pre>
+{
+ "_index": "smw-data-mw-30-00-elastic-v1",
+ "_type": "data",
+ "_id": "334032",
+ "_version": 2,
+ "_source": {
+ "subject": {
+ "title": "ABC/20180716/k10011534941000",
+ "subobject": "_f21687e8bab0ebee627f71654ddd4bc4",
+ "namespace": 0,
+ "interwiki": "",
+ "sortkey": "foo ..."
+ },
+ "P:100": {
+ "txtField": [
+ "Foo bar ..."
+ ]
+ },
+ "P:4": {
+ "wpgField": [
+ "foobar"
+ ],
+ "wpgID": [
+ 334125
+ ]
+ }
+ }
+}
+</pre>
+
+It should remembered that besides specific available types in ES, text fields are generally divided into analyzed and not_analyzed fields.
+
+Semantic MediaWiki is [mapping][es:mapping] its internal structure using [`dynamic_templates`][es:dynamic:templates] to define expected data types, their attributes, and possibly add extra index fields (see [multi-fields][es:multi-fields]) to make use of certain query constructs.
+
+The naming convention follows a very pragmatic naming scheme, `P:<ID>.<type>Field` with each new field (aka property) being mapped dynamically to a corresponding field type.
+
+- `P:<ID>` identifies the property with a number which is the same as the internal ID in the `SQLStore` (`smw_id`)
+- `<type>Field` declares a typed field (e.g. `txtField` which is important in case the type changes from `wpg` to `txt` and vice versa) and holds the actual indexable data.
+- Dates are indexed using the julian day number (JDN) to allow for historic dates being applicable
+
+The `SemanticData` object is always serialized in its entirety to avoid the interface to keep delta information. Furthermore, ES itself creates always a new index document for each update therefore keeping deltas wouldn't make much difference for the update process. A complete object has the advantage to use the [bulk][es:bulk] updater making the update faster and more resilient while avoiding document comparison during an update process.
+
+To allow for exact matches as well as full-text searches on the same field most mapped fields will have at least two or three additional [multi-field][es:multi-fields] elements to store text as `not_analyzed` (or keyword) and as sortable entity.
+
+* The `text_copy` mapping (see [copy-to][es:copy-to]) is used to enable wide proximity searches on textual annotated elements. For example, `[[in:foo bar]]` (eq. `[[~~foo bar]]`) translates into "Find all entities that have `foo bar` in one of its assigned `_uri`, `_txt`, or `_wpg` properties. The `text_copy` field is a compound field for all strings to be searched when a specific property is unknown.
+* The `text_raw` (requires `indexer.raw.text` to be set `true`) contains unstructured and unprocessed raw text from an article so that it can be used in combination with the proximity operators `[[in:lorem ipsum]]` and `[[phrase:lorem ipsum]]`.
+* `attachment.{...}` will be added by the ingest processor
+
+### ES DSL mapping
+
+For example, the ES DSL for a `[[in:lorem ipsum]]` query (find all entities that contains `lorem ipsum`) on structured and unstructured fields will look similar to:
+
+<pre>
+"bool": {
+ "must": {
+ "query_string": {
+ "fields": [
+ "subject.title^8",
+ "text_copy^5",
+ "text_raw",
+ "attachment.title^3",
+ "attachment.content"
+ ],
+ "query": "*lorem ipsum*",
+ "minimum_should_match": 1
+ }
+ }
+}
+</pre>
+
+The term `lorem ipsum` will be queried in different fields with different boost factors to highlight preferences when a term is among a title or only part of a text field.
+
+A request for a structured term (assigned to a property e.g. `[[Has text::lorem ipsum]]`) will generate a different ES DSL query.
+
+<pre>
+"bool": {
+ "filter": {
+ "term": {
+ "P:100.txtField.keyword": "lorem ipsum"
+ }
+ }
+}
+</pre>
+
+While `P:100.txtField` contains the text component that is assigned to `Has text` and by default is an analyzed field, the `keyword` field is selected to execute the query on a not analyzed content to match the exact term. Exact term matching means that the matching process distinguishes between `lorem ipsum` and `Lorem ipsum`.
+
+
+On the contrary, a proximity request (e.g. `[[Has text::~lorem ipsum*]]`) has different requirements including case folding, lower, and upper case matching and therefore includes the analyzed field with an ES DSL output that is comparable to:
+
+<pre>
+"bool": {
+ "must": {
+ "query_string": {
+ "fields": [
+ "P:100.txtField",
+ "P:100.txtField.keyword"
+ ],
+ "query": "lorem +ipsum*"
+ }
+ }
+}
+</pre>
+
+### Monitoring
+
+To make it easier for administrators to monitor the interface between Semantic MediaWiki and ES, several service links are provided for a convenient access to selected information.
+
+The main access point is defined with `Special:SemanticMediaWiki/elastic` but only users with the `smw-admin` right (which is required for the `Special:SemanticMediaWiki` page) can access the information and only when an ES cluster is available.
+
+### Logging
+
+The enable connector specific logging, please use the `smw-elastic` identifier in your LocalSettings.
+
+<pre>
+$wgDebugLogGroups = [
+ 'smw-elastic' => ".../logs/smw-elastic-{$wgDBname}.log",
+];
+</pre>
+
+## FAQ
+
+> Why not combine the `SQLStore` and ES search where ES only handles the text search?
+
+The need to support ordering of results requires that the sorting needs to happen over the entire set of results that match a condition. It is not possible to split a search between two systems while retaining consistency for the offset (from where result starts and end) pointer.
+
+> Why not use ES as a replacement?
+
+Because at this point of implementation ES is used search engine and not a storage backend therefore the data storage and management remains part of the `SQLStore`. The `SQLStore` is responsible for creating IDs, storing data objects, and provide answers to requests that doesn't involve the `QueryEngine`.
+
+> Limit of total fields [3000] in index [...] has been exceeded
+
+If the rebuilder or ES returns with a similar message then the preconfigured limit needs to be changed which is most likely caused by an excessive use of property declarations. The user should question such usage patterns and analyze why so many properties are used and whether or not some can
+be merged or properties are in fact misused as fact statements.
+
+The limit is set to prevent [mapping explosion][es:map:explosion] but can be readjusted using the [index.mapping.total_fields.limit][es:mapping] (maximum number of fields in an index) setting.
+
+<pre>
+$GLOBALS['smwgElasticsearchConfig']['settings']['data'] = [
+ 'index.mapping.total_fields.limit' => 6000
+];
+</pre>
+
+After changing those settings, ensure to run `php rebuildElasticIndex.php --update-settings`.
+
+> Your version of PHP / json-ext does not support the constant 'JSON_PRESERVE_ZERO_FRACTION', which is important for proper type mapping in Elasticsearch. Please upgrade your PHP or json-ext.
+
+[elasticsearch-php#534](https://github.com/elastic/elasticsearch-php/issues/534) has some details about the issue. Please check the [version matrix][es:version:matrix] to see which version is compatible with your PHP environment.
+
+> "Connection.php: {"error":{"root_cause":[{"type":"parse_exception","reason":"No processor type exists with name [attachment]","header":{"processor_type":"attachment"}}] ..."
+
+The file indexer (`experimental.file.ingest`) was enabled but the required ES [ingest-plugin][es:ingest] was not installed.
+
+> I use CirrusSearch, can I search SMW (or its data) via CirrusSearch?
+
+No, because first of all SMW doesn't rely on CirrusSearch at all and even if a user has CirrusSearch installed both extensions have different requirements and different indices and are not designed to share content with each other.
+
+> Can I use `Special:Search` together with SMW and CirrusSearch?
+
+Yes, by adding `$wgSearchType = 'SMWSearch';` one can use the `#ask` syntax (e.g. `[[Has date::>1970]]`) and execute structured or unstructured searches. Using the [extended profile](https://www.semantic-mediawiki.org/wiki/Help:SMWSearch/Extended_profile) or `#ask` constructs a search input that will retrieved results via Semantic MediaWiki (and hereby ES).
+
+### Glossary
+
+- `Document` is called in ES a content container to holds indexable content and is equivalent to an entity (subject) in Semantic MediaWiki
+- `Index` holds all documents within a collection of types and contains inverted indices to search across everything within those documents at once
+- `Node` is a running instance of Elasticsearch
+- `Cluster` is a group of nodes
+
+### Other recommendations
+
+- Analysis ICU ( tokenizer and token filters from the Unicode ICU library), see `bin/elasticsearch-plugin install analysis-icu`
+- A [curated list](https://github.com/dzharii/awesome-elasticsearch) of useful resources about elasticsearch including articles, videos, blogs, tips and tricks, use cases
+- [Elasticsearch: The Definitive Guide](http://shop.oreilly.com/product/0636920028505.do) by Clinton Gormley and Zachary Tonge should provide insights in how to run and use Elasticsearch
+- [10 Elasticsearch metrics to watch][oreilly:es-metrics-to-watch] describes key metrics to keep Elasticsearch running smoothly
+
+[es:conf]: https://www.elastic.co/guide/en/elasticsearch/reference/6.1/system-config.html
+[es:conf:hosts]: https://www.elastic.co/guide/en/elasticsearch/client/php-api/6.0/_configuration.html#_extended_host_configuration
+[es:php-api]: https://www.elastic.co/guide/en/elasticsearch/client/php-api/6.0/_installation_2.html
+[es:joins]: https://github.com/elastic/elasticsearch/issues/6769
+[es:subqueries]: https://discuss.elastic.co/t/question-about-subqueries/20767/2
+[es:terms-lookup]: https://www.elastic.co/blog/terms-filter-lookup
+[es:dsl]: https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl.html
+[es:mapping]: https://www.elastic.co/guide/en/elasticsearch/reference/6.1/mapping.html
+[es:multi-fields]: https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html
+[es:map:explosion]: https://www.elastic.co/blog/found-crash-elasticsearch#mapping-explosion
+[es:indexing:speed]: https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
+[es:create:index]: https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
+[es:dynamic:templates]: https://www.elastic.co/guide/en/elasticsearch/reference/6.1/dynamic-templates.html
+[es:version:matrix]: https://www.elastic.co/guide/en/elasticsearch/client/php-api/6.0/_installation_2.html#_version_matrix
+[es:hardware]: https://www.elastic.co/guide/en/elasticsearch/guide/2.x/hardware.html#_memory
+[es:standard:analyzer]: https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html
+[es:lang:analyzer]: https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html
+[es:icu:tokenizer]: https://www.elastic.co/guide/en/elasticsearch/plugins/6.1/analysis-icu-tokenizer.html
+[es:unicode:normalization]: https://www.elastic.co/guide/en/elasticsearch/guide/current/unicode-normalization.html
+[es:unicode:case:folding]: https://www.elastic.co/guide/en/elasticsearch/guide/current/case-folding.html
+[es:shards]: https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html#getting-started-shards-and-replicas
+[es:alias-zero]: https://www.elastic.co/guide/en/elasticsearch/guide/master/index-aliases.html
+[es:bulk]: https://www.elastic.co/guide/en/elasticsearch/reference/6.2/docs-bulk.html
+[es:structured:search]: https://www.elastic.co/guide/en/elasticsearch/guide/current/structured-search.html
+[es:filter:context]: https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-filter-context.html
+[es:query:context]: https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-filter-context.html
+[es:relevance]: https://www.elastic.co/guide/en/elasticsearch/guide/master/relevance-intro.html
+[es:copy-to]: https://www.elastic.co/guide/en/elasticsearch/reference/master/copy-to.html
+[oreilly:es-metrics-to-watch]: https://www.oreilly.com/ideas/10-elasticsearch-metrics-to-watch
+[stack:segments]: https://stackoverflow.com/questions/15426441/understanding-segments-in-elasticsearch
+[es:6]: https://www.elastic.co/blog/minimize-index-storage-size-elasticsearch-6-0
+[packagist:es]:https://packagist.org/packages/elasticsearch/elasticsearch
+[es:ingest]:https://www.elastic.co/guide/en/elasticsearch/plugins/master/ingest-attachment.html
+[es:parent-join]: https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html
+[es:replica-shards]:https://www.elastic.co/guide/en/elasticsearch/guide/current/replica-shards.html
+[es:highlighting]: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html
+[smw:search]: https://www.semantic-mediawiki.org/wiki/Help:SMWSearch
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Encoder.php b/www/wiki/extensions/SemanticMediaWiki/src/Encoder.php
new file mode 100644
index 00000000..6abbd1f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Encoder.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class Encoder {
+
+ /**
+ * @see SMWInfolink::encodeParameters
+ *
+ * Escape certain problematic values. Use SMW-escape
+ * (like URLencode but - instead of % to prevent double encoding by later MW actions)
+ * : SMW's parameter separator, must not occur within params
+ * // - : used in SMW-encoding strings, needs escaping too
+ * [ ] < > &lt; &gt; '' |: problematic in MW titles
+ * & : sometimes problematic in MW titles ([[&amp;]] is OK, [[&test]] is OK, [[&test;]] is not OK)
+ * (Note: '&' in strings obtained during parsing already has &entities; replaced by
+ * UTF8 anyway)
+ * ' ': are equivalent with '_' in MW titles, but are not equivalent in certain parameter values
+ * "\n": real breaks not possible in [[...]]
+ * "#": has special meaning in URLs, triggers additional MW escapes (using . for %)
+ * '%': must be escaped to prevent any impact of double decoding when replacing -
+ * by % before urldecode
+ * '?': if not escaped, strange effects were observed on some sites (printout and other
+ * parameters ignored without obvious cause); SMW-escaping is always save to do -- it just
+ * make URLs less readable
+ *
+ * @since 2.2
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function escape( $string ) {
+
+ $value = str_replace(
+ [ '-', '#', "\n", ' ', '/', '[', ']', '<', '>', '&lt;', '&gt;', '&amp;', '\'\'', '|', '&', '%', '?', '$', "\\", ";", '_' ],
+ [ '-2D', '-23', '-0A', '-20', '-2F', '-5B', '-5D', '-3C', '-3E', '-3C', '-3E', '-26', '-27-27', '-7C', '-26', '-25', '-3F', '-24', '-5C', "-3B", '-5F' ],
+ $string
+ );
+
+ return $value;
+ }
+
+ /**
+ * Reverse of self::escape
+ *
+ * @since 2.5
+ *
+ * @param $string
+ *
+ * @return string
+ */
+ public static function unescape( $string ) {
+
+ $value = str_replace(
+ [ '-20', '-23', '-0A', '-2F', '-5B', '-5D', '-3C', '-3E', '-3C', '-3E', '-26', '-27-27', '-7C', '-26', '-25', '-3F', '-24', '-5C', "-3B", "-3A", '-5F', '-2D' ],
+ [ ' ', '#', "\n", '/', '[', ']', '<', '>', '&lt;', '&gt;', '&', '\'\'', '|', '&', '%', '?', '$', "\\", ";", ":", "_", '-' ],
+ $string
+ );
+
+ return $value;
+ }
+
+ /**
+ * @see SMWInfolink::encodeParameters
+ *
+ * @since 2.2
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function encode( $string ) {
+ return rawurlencode( $string );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function decode( $string ) {
+
+ // Apply decoding for SMW's own url encoding strategy (see SMWInfolink)
+ $string = str_replace( '%', '-', rawurldecode( str_replace( '-', '%', $string ) ) );
+
+ $string = str_replace( [ '-2D', '-3A' ], [ '-', ':' ], $string );
+
+ // Sanitize remaining string content
+ $string = trim( htmlspecialchars( $string, ENT_NOQUOTES ) );
+ $string = str_replace( '&nbsp;', ' ', str_replace( [ '&#160;', '&amp;' ], [ ' ', '&' ], $string ) );
+
+ return $string;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/EntityLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/EntityLookup.php
new file mode 100644
index 00000000..c7531813
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/EntityLookup.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW;
+
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface EntityLookup {
+
+ /**
+ * Retrieve all data stored about the given subject and return it as a
+ * SemanticData container. There are no options: it just returns all
+ * available data as shown in the page's Factbox.
+ * $filter is an array of strings that are datatype IDs. If given, the
+ * function will avoid any work that is not necessary if only
+ * properties of these types are of interest.
+ *
+ * @note There is no guarantee that the store does not retrieve more
+ * data than requested when a filter is used. Filtering just ensures
+ * that only necessary requests are made, i.e. it improves performance.
+ *
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param RequestOptions|string[]|bool $filter
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false );
+
+ /**
+ * Get an array of all properties for which the given subject has some
+ * value. The result is an array of DIProperty objects.
+ *
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DataItem[]|[]
+ */
+ public function getProperties( DIWikiPage $subject, RequestOptions $requestOptions = null );
+
+ /**
+ * Get an array of all property values stored for the given subject and
+ * property. The result is an array of DataItem objects.
+ *
+ * If called with $subject == null, all values for the given property
+ * are returned.
+ *
+ * @since 2.5
+ *
+ * @param DIWikiPage|null $subject
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DataItem[]|[]|Iterator
+ */
+ public function getPropertyValues( DIWikiPage $subject = null, DIProperty $property, RequestOptions $requestOptions = null );
+
+ /**
+ * Get an array of all subjects that have the given value for the given
+ * property. The result is an array of DIWikiPage objects. If null
+ * is given as a value, all subjects having that property are returned.
+ *
+ * @since 2.5
+ *
+ * @param DIWikiPage|null $subject
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DIWikiPage[]|[]|Iterator
+ */
+ public function getPropertySubjects( DIProperty $property, DataItem $dataItem = null, RequestOptions $requestOptions = null );
+
+ /**
+ * Get an array of all subjects that have some value for the given
+ * property. The result is an array of DIWikiPage objects.
+ *
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DIWikiPage[]|Iterator
+ */
+ public function getAllPropertySubjects( DIProperty $property, RequestOptions $requestOptions = null );
+
+ /**
+ * Get an array of all properties for which there is some subject that
+ * relates to the given value. The result is an array of DIWikiPage
+ * objects.
+ *
+ * @note In some stores, this function might be implemented partially
+ * so that only values of type Page (_wpg) are supported.
+ *
+ * @since 2.5
+ *
+ * @param DataItem $object
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DataItem[]|[]
+ */
+ public function getInProperties( DataItem $object, RequestOptions $requestOptions = null );
+
+ /**
+ * @since 3.0
+ */
+ public function invalidateCache();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Enum.php b/www/wiki/extensions/SemanticMediaWiki/src/Enum.php
new file mode 100644
index 00000000..6a4bdaea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Enum.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Enum {
+
+ /**
+ * Option that allows to suspend the page purge
+ */
+ const OPT_SUSPEND_PURGE = 'smw.opt.suspend.purge';
+
+ /**
+ * Indicates to purge an associated parser cache
+ */
+ const PURGE_ASSOC_PARSERCACHE = 'smw.purge.assoc.parsercache';
+
+ /**
+ * Indicates whether to proceed with the cache warming or not
+ */
+ const SUSPEND_CACHE_WARMUP = 'smw.suspend.cache.warmup';
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/EventHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/EventHandler.php
new file mode 100644
index 00000000..b0bdfa55
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/EventHandler.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace SMW;
+
+use Onoi\EventDispatcher\EventDispatcher;
+use Onoi\EventDispatcher\EventDispatcherFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EventHandler {
+
+ /**
+ * @var EventHandler
+ */
+ private static $instance = null;
+
+ /**
+ * @var EventDispatcher
+ */
+ private $eventDispatcher = null;
+
+ /**
+ * @since 2.2
+ *
+ * @param EventDispatcher $eventDispatcher
+ */
+ public function __construct( EventDispatcher $eventDispatcher ) {
+ $this->eventDispatcher = $eventDispatcher;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return self
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self( self::newEventDispatcher() );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return EventDispatcher
+ */
+ public function getEventDispatcher() {
+ return $this->eventDispatcher;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DispatchContext
+ */
+ public function newDispatchContext() {
+ return EventDispatcherFactory::getInstance()->newDispatchContext();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $event
+ * @param Closure $callback
+ */
+ public function addCallbackListener( $event, \Closure $callback ) {
+
+ $listener = EventDispatcherFactory::getInstance()->newGenericCallbackEventListener();
+ $listener->registerCallback( $callback );
+
+ $this->getEventDispatcher()->addListener(
+ $event,
+ $listener
+ );
+ }
+
+ private static function newEventDispatcher() {
+
+ $eventListenerRegistry = new EventListenerRegistry(
+ EventDispatcherFactory::getInstance()->newGenericEventListenerCollection()
+ );
+
+ $eventDispatcher = EventDispatcherFactory::getInstance()->newGenericEventDispatcher();
+ $eventDispatcher->addListenerCollection( $eventListenerRegistry );
+
+ return $eventDispatcher;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/EventListenerRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/EventListenerRegistry.php
new file mode 100644
index 00000000..28e4a2c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/EventListenerRegistry.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW;
+
+use Onoi\EventDispatcher\EventListenerCollection;
+use SMW\Query\QueryComparator;
+use SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal;
+use SMWExporter as Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EventListenerRegistry implements EventListenerCollection {
+
+ /**
+ * @var EventListenerCollection
+ */
+ private $eventListenerCollection = null;
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * @since 2.2
+ *
+ * @param EventListenerCollection $eventListenerCollection
+ */
+ public function __construct( EventListenerCollection $eventListenerCollection ) {
+ $this->eventListenerCollection = $eventListenerCollection;
+ }
+
+ /**
+ * @see EventListenerCollection::getCollection
+ *
+ * @since 2.2
+ */
+ public function getCollection() {
+ return $this->addListenersToCollection()->getCollection();
+ }
+
+ private function addListenersToCollection() {
+
+ $this->logger = ApplicationFactory::getInstance()->getMediaWikiLogger();
+
+ /**
+ * Emitted during UpdateJob, ArticlePurge
+ */
+ $this->eventListenerCollection->registerCallback(
+ 'factbox.cache.delete', function( $dispatchContext ) {
+
+ if ( $dispatchContext->has( 'subject' ) ) {
+ $title = $dispatchContext->get( 'subject' )->getTitle();
+ } else {
+ $title = $dispatchContext->get( 'title' );
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $applicationFactory->getCache()->delete(
+ \SMW\Factbox\CachedFactbox::makeCacheKey( $title )
+ );
+ }
+ );
+
+ $this->eventListenerCollection->registerCallback(
+ 'exporter.reset', function() {
+ Exporter::getInstance()->clear();
+ }
+ );
+
+ $this->eventListenerCollection->registerCallback(
+ 'query.comparator.reset', function() {
+ QueryComparator::getInstance()->clear();
+ }
+ );
+
+ /**
+ * Emitted during UpdateJob
+ */
+ $this->eventListenerCollection->registerCallback(
+ 'cached.propertyvalues.prefetcher.reset', function( $dispatchContext ) {
+
+ if ( $dispatchContext->has( 'title' ) ) {
+ $subject = DIWikiPage::newFromTitle( $dispatchContext->get( 'title' ) );
+ } else{
+ $subject = $dispatchContext->get( 'subject' );
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $logContext = [
+ 'role' => 'developer',
+ 'event' => 'cached.propertyvalues.prefetcher.reset',
+ 'origin' => $subject
+ ];
+
+ $this->logger->info( '[Event] {event}: {origin}', $logContext );
+
+ $applicationFactory->singleton( 'CachedPropertyValuesPrefetcher' )->resetCacheBy(
+ $subject
+ );
+
+ $dispatchContext->set( 'propagationstop', true );
+ }
+ );
+
+ /**
+ * Emitted during NewRevisionFromEditComplete, ArticleDelete, TitleMoveComplete,
+ * PropertyTableIdReferenceDisposer, ArticlePurge
+ */
+ $this->eventListenerCollection->registerCallback(
+ 'cached.prefetcher.reset', function( $dispatchContext ) {
+
+ if ( $dispatchContext->has( 'title' ) ) {
+ $subject = DIWikiPage::newFromTitle( $dispatchContext->get( 'title' ) );
+ } else{
+ $subject = $dispatchContext->get( 'subject' );
+ }
+
+ $context = $dispatchContext->has( 'context' ) ? $dispatchContext->get( 'context' ) : '';
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $logContext = [
+ 'role' => 'developer',
+ 'event' => 'cached.prefetcher.reset',
+ 'origin' => $subject
+ ];
+
+ $this->logger->info( '[Event] {event}: {origin}', $logContext );
+
+ $applicationFactory->singleton( 'CachedPropertyValuesPrefetcher' )->resetCacheBy(
+ $subject
+ );
+
+ $applicationFactory->singleton( 'CachedQueryResultPrefetcher' )->resetCacheBy(
+ $subject,
+ $context
+ );
+
+ if ( $dispatchContext->has( 'ask' ) ) {
+ $applicationFactory->singleton( 'CachedQueryResultPrefetcher' )->resetCacheBy(
+ $dispatchContext->get( 'ask' ),
+ $context
+ );
+ }
+
+ $dispatchContext->set( 'propagationstop', true );
+ }
+ );
+
+ $this->registerStateChangeEvents();
+
+ return $this->eventListenerCollection;
+ }
+
+ private function registerStateChangeEvents() {
+
+ /**
+ * Emitted during ArticleDelete
+ */
+ $this->eventListenerCollection->registerCallback(
+ 'cached.update.marker.delete', function( $dispatchContext ) {
+
+ $cache = ApplicationFactory::getInstance()->getCache();
+
+ if ( $dispatchContext->has( 'subject' ) ) {
+ $cache->delete(
+ DependencyLinksUpdateJournal::makeKey(
+ $dispatchContext->get( 'subject' )
+ )
+ );
+
+ $cache->delete(
+ smwfCacheKey(
+ ParserData::CACHE_NAMESPACE,
+ $dispatchContext->get( 'subject' )->getHash()
+ )
+ );
+ }
+ }
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemDeserializationException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemDeserializationException.php
new file mode 100644
index 00000000..e05cafb1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemDeserializationException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace SMW\Exception;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemDeserializationException extends DataItemException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemException.php
new file mode 100644
index 00000000..46b67ff4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataItemException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DataItemException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataTypeLookupException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataTypeLookupException.php
new file mode 100644
index 00000000..4311de88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/DataTypeLookupException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataTypeLookupException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotFoundException.php
new file mode 100644
index 00000000..7e1816b7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotReadableException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotReadableException.php
new file mode 100644
index 00000000..83e2ad68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotReadableException.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileNotReadableException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ */
+ public function __construct( $file ) {
+ parent::__construct( "$file is not readable." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotWritableException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotWritableException.php
new file mode 100644
index 00000000..a9ccb44f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/FileNotWritableException.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileNotWritableException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ */
+ public function __construct( $file ) {
+ parent::__construct( "$file is not writable." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/ParameterNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/ParameterNotFoundException.php
new file mode 100644
index 00000000..502275b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/ParameterNotFoundException.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\Exception;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParameterNotFoundException extends InvalidArgumentException {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ */
+ public function __construct( $name ) {
+ $this->name = $name;
+ parent::__construct( " $name is missing as argument!" );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/PredefinedPropertyLabelMismatchException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PredefinedPropertyLabelMismatchException.php
new file mode 100644
index 00000000..907a5ff7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PredefinedPropertyLabelMismatchException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace SMW\Exception;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyLabelMismatchException extends PropertyLabelNotResolvedException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyLabelNotResolvedException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyLabelNotResolvedException.php
new file mode 100644
index 00000000..8e7aa20e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyLabelNotResolvedException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace SMW\Exception;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyLabelNotResolvedException extends DataItemException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyNotFoundException.php
new file mode 100644
index 00000000..e1ebe694
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/PropertyNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/RedirectTargetUnresolvableException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/RedirectTargetUnresolvableException.php
new file mode 100644
index 00000000..f9ee1978
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/RedirectTargetUnresolvableException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RedirectTargetUnresolvableException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/SemanticDataImportException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SemanticDataImportException.php
new file mode 100644
index 00000000..61579eb8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SemanticDataImportException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SemanticDataImportException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/SettingNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SettingNotFoundException.php
new file mode 100644
index 00000000..9b852d03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SettingNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SettingNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/StoreNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/StoreNotFoundException.php
new file mode 100644
index 00000000..c3bd2409
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/StoreNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class StoreNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exception/SubSemanticDataException.php b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SubSemanticDataException.php
new file mode 100644
index 00000000..5f2dadd4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exception/SubSemanticDataException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SubSemanticDataException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ConceptMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ConceptMapper.php
new file mode 100644
index 00000000..20e43dde
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ConceptMapper.php
@@ -0,0 +1,306 @@
+<?php
+
+namespace SMW\Exporter;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\Exporter\Element\ExpResource;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+use SMWExporter as Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ConceptMapper {
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.4
+ *
+ * @param Exporter|null $exporter
+ */
+ public function __construct( Exporter $exporter = null ) {
+ $this->exporter = $exporter;
+
+ if ( $this->exporter === null ) {
+ $this->exporter = Exporter::getInstance();
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ public function isMapperFor( DataItem $dataItem ) {
+ return $dataItem instanceof DIConcept;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIConcept $concept
+ *
+ * @return ExpData|null
+ */
+ public function getElementFor( DIConcept $concept ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $concept
+ );
+
+ if ( !$dataValue->isValid() ) {
+ return null;
+ }
+
+ $description = ApplicationFactory::getInstance()->newQueryParser()->getQueryDescription(
+ $dataValue->getWikiValue()
+ );
+
+ $exact = true;
+ $owlDescription = $this->getExpDataFromDescription( $description, $exact );
+
+ if ( $owlDescription === false ) {
+ $result = new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Thing' )
+ );
+
+ return $result;
+ }
+
+ if ( $exact ) {
+ return $owlDescription;
+ }
+
+ $result = new ExpData(
+ new ExpResource( '' )
+ );
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdf', 'type' ),
+ new ExpData( $this->exporter->getSpecialNsResource( 'owl', 'Class' ) )
+ );
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdfs', 'subClassOf' ),
+ $owlDescription
+ );
+
+ return $result;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Description $description
+ *
+ * @param string &$exact
+ *
+ * @return Element|false
+ */
+ public function getExpDataFromDescription( Description $description, &$exact ) {
+
+ if ( ( $description instanceof Conjunction ) || ( $description instanceof Disjunction ) ) {
+ $result = $this->doMapConjunctionDisjunction( $description, $exact );
+ } elseif ( $description instanceof ClassDescription ) {
+ $result = $this->doMapClassDescription( $description, $exact );
+ } elseif ( $description instanceof ConceptDescription ) {
+ $result = $this->doMapConceptDescription( $description, $exact );
+ } elseif ( $description instanceof SomeProperty ) {
+ $result = $this->doMapSomeProperty( $description, $exact );
+ } elseif ( $description instanceof ValueDescription ) {
+ $result = $this->doMapValueDescription( $description, $exact );
+ } elseif ( $description instanceof ThingDescription ) {
+ $result = false;
+ } else {
+ $result = false;
+ $exact = false;
+ }
+
+ return $result;
+ }
+
+ private function doMapValueDescription( ValueDescription $description, &$exact ) {
+
+ if ( $description->getComparator() === SMW_CMP_EQ ) {
+ $result = $this->exporter->getDataItemExpElement( $description->getDataItem() );
+ } else {
+ // OWL cannot represent <= and >= ...
+ $exact = false;
+ $result = false;
+ }
+
+ return $result;
+ }
+
+ private function doMapConceptDescription( ConceptDescription $description, &$exact ) {
+
+ $result = new ExpData(
+ $this->exporter->getResourceElementForWikiPage( $description->getConcept() )
+ );
+
+ return $result;
+ }
+
+ private function doMapSomeProperty( SomeProperty $description, &$exact ) {
+
+ $result = new ExpData(
+ new ExpResource( '' )
+ );
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdf', 'type' ),
+ new ExpData( $this->exporter->getSpecialNsResource( 'owl', 'Restriction' ) )
+ );
+
+ $property = $description->getProperty();
+
+ if ( $property->isInverse() ) {
+ $property = new DIProperty( $property->getKey() );
+ }
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'owl', 'onProperty' ),
+ new ExpData(
+ $this->exporter->getResourceElementForProperty( $property )
+ )
+ );
+
+ $subdata = $this->getExpDataFromDescription(
+ $description->getDescription(),
+ $exact
+ );
+
+ if ( ( $description->getDescription() instanceof ValueDescription ) &&
+ ( $description->getDescription()->getComparator() === SMW_CMP_EQ ) ) {
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'owl', 'hasValue' ),
+ $subdata
+ );
+ } else {
+ if ( $subdata === false ) {
+
+ $owltype = $this->exporter->getOWLPropertyType(
+ $description->getProperty()->findPropertyTypeID()
+ );
+
+ if ( $owltype == 'ObjectProperty' ) {
+ $subdata = new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Thing' )
+ );
+ } elseif ( $owltype == 'DatatypeProperty' ) {
+ $subdata = new ExpData(
+ $this->exporter->getSpecialNsResource( 'rdfs', 'Literal' )
+ );
+ } else { // no restrictions at all with annotation properties ...
+ return new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Thing' )
+ );
+ }
+ }
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'owl', 'someValuesFrom' ),
+ $subdata
+ );
+ }
+
+ return $result;
+ }
+
+ private function doMapClassDescription( ClassDescription $description, &$exact ) {
+
+ if ( count( $description->getCategories() ) == 1 ) { // single category
+ $categories = $description->getCategories();
+ $result = new ExpData(
+ $this->exporter->getResourceElementForWikiPage( end( $categories ) )
+ );
+ } else { // disjunction of categories
+
+ $result = new ExpData(
+ new ExpResource( '' )
+ );
+
+ $elements = [];
+
+ foreach ( $description->getCategories() as $cat ) {
+ $elements[] = new ExpData(
+ $this->exporter->getResourceElementForWikiPage( $cat )
+ );
+ }
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'owl', 'unionOf' ),
+ ExpData::makeCollection( $elements )
+ );
+ }
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdf', 'type' ),
+ new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Class' )
+ )
+ );
+
+ return $result;
+ }
+
+ private function doMapConjunctionDisjunction( Description $description, &$exact ) {
+
+ $result = new ExpData(
+ new ExpResource( '' )
+ );
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdf', 'type' ),
+ new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Class' )
+ )
+ );
+
+ $elements = [];
+
+ foreach ( $description->getDescriptions() as $subdesc ) {
+ $element = $this->getExpDataFromDescription( $subdesc, $exact );
+
+ if ( $element === false ) {
+ $element = new ExpData(
+ $this->exporter->getSpecialNsResource( 'owl', 'Thing' )
+ );
+ }
+
+ $elements[] = $element;
+ }
+
+ $prop = $description instanceof Conjunction ? 'intersectionOf' : 'unionOf';
+
+ $result->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'owl', $prop ),
+ ExpData::makeCollection( $elements )
+ );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/DataItemMatchFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/DataItemMatchFinder.php
new file mode 100644
index 00000000..24cd2450
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/DataItemMatchFinder.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace SMW\Exporter;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpResource;
+use SMW\Localizer;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DataItemMatchFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var string
+ */
+ private $wikiNamespace;
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param string $wikiNamespace
+ */
+ public function __construct( Store $store, $wikiNamespace = '' ) {
+ $this->store = $store;
+ $this->wikiNamespace = $wikiNamespace;
+ }
+
+ /**
+ * Try to map an ExpElement to a representative DataItem which may return null
+ * if the attempt fails.
+ *
+ * @since 2.4
+ *
+ * @param ExpElement $expElement
+ *
+ * @return DataItem|null
+ */
+ public function matchExpElement( ExpElement $expElement ) {
+
+ $dataItem = null;
+
+ if ( !$expElement instanceof ExpResource ) {
+ return $dataItem;
+ }
+
+ $uri = $expElement->getUri();
+
+ if ( strpos( $uri, $this->wikiNamespace ) !== false ) {
+ $dataItem = $this->matchToWikiNamespaceUri( $uri );
+ } else {
+ // Not in wikiNamespace therefore most likely an imported URI
+ $dataItem = $this->matchToUnknownWikiNamespaceUri( $uri );
+ }
+
+ return $dataItem;
+ }
+
+ private function matchToWikiNamespaceUri( $uri ) {
+
+ $dataItem = null;
+ $localName = substr( $uri, strlen( $this->wikiNamespace ) );
+
+ $dbKey = rawurldecode( Escaper::decodeUri( $localName ) );
+ $parts = explode( '#', $dbKey, 2 );
+
+ if ( count( $parts ) == 2 ) {
+ $dbKey = $parts[0];
+ $subobjectname = $parts[1];
+ } else {
+ $subobjectname = '';
+ }
+
+ $parts = explode( ':', $dbKey, 2 );
+
+ // No extra NS
+ if ( count( $parts ) == 1 ) {
+ return new DIWikiPage( $dbKey, NS_MAIN, '', $subobjectname );
+ }
+
+ $namespaceId = $this->matchToNamespaceName( $parts[0] );
+
+ if ( $namespaceId != -1 && $namespaceId !== false ) {
+ $dataItem = new DIWikiPage( $parts[1], $namespaceId, '', $subobjectname );
+ } else {
+ $title = Title::newFromDBkey( $dbKey );
+
+ if ( $title !== null ) {
+ $dataItem = new DIWikiPage( $title->getDBkey(), $title->getNamespace(), $title->getInterwiki(), $subobjectname );
+ }
+ }
+
+ return $dataItem;
+ }
+
+ private function matchToNamespaceName( $name ) {
+ // try the by far most common cases directly before using Title
+ $namespaceName = str_replace( '_', ' ', $name );
+
+ if ( ( $namespaceId = Localizer::getInstance()->getNamespaceIndexByName( $name ) ) !== false ) {
+ return $namespaceId;
+ }
+
+ foreach ( [ SMW_NS_PROPERTY, NS_CATEGORY, NS_USER, NS_HELP ] as $nsId ) {
+ if ( $namespaceName == Localizer::getInstance()->getNamespaceTextById( $nsId ) ) {
+ $namespaceId = $nsId;
+ break;
+ }
+ }
+
+ return $namespaceId;
+ }
+
+ private function matchToUnknownWikiNamespaceUri( $uri ) {
+
+ $dataItem = null;
+
+ // Sesame: Not a valid (absolute) URI: _node1abjt1k9bx17
+ if ( filter_var( $uri, FILTER_VALIDATE_URL ) === false ) {
+ return $dataItem;
+ }
+
+ $respositoryResult = $this->store->getConnection( 'sparql' )->select(
+ '?v1 ?v2',
+ "<$uri> rdfs:label ?v1 . <$uri> swivt:wikiNamespace ?v2",
+ [ 'LIMIT' => 1 ]
+ );
+
+ $expElements = $respositoryResult->current();
+
+ if ( $expElements !== false ) {
+
+ // ?v1
+ if ( isset( $expElements[0] ) ) {
+ $dbKey = $expElements[0]->getLexicalForm();
+ } else {
+ $dbKey = 'UNKNOWN';
+ }
+
+ // ?v2
+ if ( isset( $expElements[1] ) ) {
+ $namespace = strval( $expElements[1]->getLexicalForm() );
+ } else {
+ $namespace = NS_MAIN;
+ }
+
+ $dataItem = new DIWikiPage(
+ $this->getFittingDBKey( $dbKey, $namespace ),
+ $namespace
+ );
+ }
+
+ return $dataItem;
+ }
+
+ private function getFittingDBKey( $dbKey, $namespace ) {
+
+ // https://www.mediawiki.org/wiki/Manual:$wgCapitalLinks
+ // https://www.mediawiki.org/wiki/Manual:$wgCapitalLinkOverrides
+ if ( $GLOBALS['wgCapitalLinks'] || ( isset( $GLOBALS['wgCapitalLinkOverrides'][$namespace] ) && $GLOBALS['wgCapitalLinkOverrides'][$namespace] ) ) {
+ return mb_strtoupper( mb_substr( $dbKey, 0, 1 ) ) . mb_substr( $dbKey, 1 );
+ }
+
+ return $dbKey;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element.php
new file mode 100644
index 00000000..9443f5c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace SMW\Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface Element {
+
+ /**
+ * A single resource (individual) for export, as defined by a URI.
+ */
+ const TYPE_RESOURCE = 0;
+
+ /**
+ * A single resource (individual) for export, defined by a URI for which there
+ * also is a namespace abbreviation.
+ */
+ const TYPE_NSRESOURCE = 1;
+
+ /**
+ * A single datatype literal for export. Defined by a literal value and a
+ * datatype URI.
+ */
+ const TYPE_LITERAL = 2;
+
+ /**
+ * A dataItem an export element is associated with
+ *
+ * @since 2.2
+ *
+ * @return DataItem|null
+ */
+ public function getDataItem();
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpElement.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpElement.php
new file mode 100644
index 00000000..1e37f10d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpElement.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Exporter\Element;
+
+use RuntimeException;
+use SMW\Exporter\Element;
+use SMWDataItem as DataItem;
+
+/**
+ * ExpElement is a class for representing single elements that appear in
+ * exported data, such as individual resources, data literals, or blank nodes.
+ *
+ * A single element for export, e.g. a data literal, instance name, or blank
+ * node. This abstract base class declares the basic common functionality of
+ * export elements (which is not much, really).
+ * @note This class should not be instantiated directly.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+abstract class ExpElement implements Element {
+
+ /**
+ * The DataItem that this export element is associated with, if
+ * any. Might be unset if not given yet.
+ *
+ * @var DataItem|null
+ */
+ protected $dataItem;
+
+ /**
+ * @since 1.6
+ *
+ * @param DataItem|null $dataItem
+ */
+ public function __construct( DataItem $dataItem = null ) {
+ $this->dataItem = $dataItem;
+ }
+
+ /**
+ * Get a DataItem object that represents the contents of this export
+ * element in SMW, or null if no such data item could be found.
+ *
+ * @return DataItem|null
+ */
+ public function getDataItem() {
+ return $this->dataItem;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return md5( json_encode( $this->getSerialization() ) );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSerialization() {
+
+ $dataItem = null;
+
+ if ( $this->getDataItem() !== null ) {
+ $dataItem = [
+ 'type' => $this->getDataItem()->getDIType(),
+ 'item' => $this->getDataItem()->getSerialization()
+ ];
+ }
+
+ return [
+ 'dataitem' => $dataItem
+ ];
+ }
+
+ /**
+ * @see ExpElement::newFromSerialization
+ */
+ protected static function deserialize( $serialization ) {
+
+ $dataItem = null;
+
+ if ( !array_key_exists( 'dataitem', $serialization ) ) {
+ throw new RuntimeException( "The serialization format is missing a dataitem element" );
+ }
+
+ // If it is null, isset will ignore it
+ if ( isset( $serialization['dataitem'] ) ) {
+ $dataItem = DataItem::newFromSerialization(
+ $serialization['dataitem']['type'],
+ $serialization['dataitem']['item']
+ );
+ }
+
+ return $dataItem;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $serialization
+ *
+ * @return ExpElement
+ */
+ public static function newFromSerialization( array $serialization ) {
+
+ if ( !isset( $serialization['type'] ) ) {
+ throw new RuntimeException( "The serialization format is missing a type element" );
+ }
+
+ switch ( $serialization['type'] ) {
+ case Element::TYPE_RESOURCE:
+ $elementClass = ExpResource::class;
+ break;
+ case Element::TYPE_NSRESOURCE:
+ $elementClass = ExpNsResource::class;
+ break;
+ case Element::TYPE_LITERAL:
+ $elementClass = ExpLiteral::class;
+ break;
+ default:
+ throw new RuntimeException( "Unknown type" );
+ }
+
+ $serialization['dataitem'] = self::deserialize( $serialization );
+
+ return $elementClass::deserialize( $serialization );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpLiteral.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpLiteral.php
new file mode 100644
index 00000000..f800c482
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpLiteral.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace SMW\Exporter\Element;
+
+use InvalidArgumentException;
+use RuntimeException;
+use SMWDataItem as DataItem;
+
+/**
+ * A single datatype literal for export. Defined by a literal value and a
+ * datatype URI.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ExpLiteral extends ExpElement {
+
+ /**
+ * Lexical form of the literal.
+ * @var string
+ */
+ private $lexicalForm;
+
+ /**
+ * Datatype URI for the literal.
+ * @var string
+ */
+ private $datatype;
+
+ /**
+ * @var string
+ */
+ private $lang = '';
+
+ /**
+ * @note The given lexical form should be the plain string for
+ * representing the literal without datatype or language information.
+ * It must not use any escaping or abbreviation mechanisms.
+ *
+ * @param string $lexicalForm lexical form
+ * @param string $datatype Data type URI or empty for untyped literals
+ * @param string $lang
+ * @param DataItem|null $dataItem
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $lexicalForm, $datatype = '', $lang = '', DataItem $dataItem = null ) {
+
+ if ( !is_string( $lexicalForm ) ) {
+ throw new InvalidArgumentException( '$lexicalForm needs to be a string' );
+ }
+
+ if ( !is_string( $datatype ) ) {
+ throw new InvalidArgumentException( '$datatype needs to be a string' );
+ }
+
+ if ( !is_string( $lang ) ) {
+ throw new InvalidArgumentException( '$lang needs to be a string and $datatype has to be of langString type' );
+ }
+
+ parent::__construct( $dataItem );
+
+ $this->lexicalForm = $lexicalForm;
+ $this->datatype = $datatype;
+
+ // 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'
+ // can also be used instead of the simple Foo@lang-tag convention
+
+ // https://www.w3.org/TR/2004/REC-rdf-concepts-20040210/#dfn-language-identifier
+ // "...Plain literals have a lexical form and optionally a language tag as
+ // defined by [RFC-3066], normalized to lowercase..."
+ // https://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal
+ // "...Lexical representations of language tags may be converted to
+ // lower case. The value space of language tags is always in lower case..."
+ $this->lang = strtolower( $lang );
+ }
+
+ /**
+ * Returns a language tag with the language tag must be well-formed according
+ * to BCP47
+ *
+ * @return string
+ */
+ public function getLang() {
+ return $this->lang;
+ }
+
+ /**
+ * Return the URI of the datatype used, or the empty string if untyped.
+ *
+ * @return string
+ */
+ public function getDatatype() {
+ return $this->datatype;
+ }
+
+ /**
+ * Return the lexical form of the literal. The result does not use
+ * any escapings and might still need to be escaped in some contexts.
+ * The lexical form is not validated or canonicalized.
+ *
+ * @return string
+ */
+ public function getLexicalForm() {
+ return $this->lexicalForm;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSerialization() {
+
+ $serialization = [
+ 'type' => self::TYPE_LITERAL,
+ 'lexical' => $this->lexicalForm,
+ 'datatype' => $this->datatype,
+ 'lang' => $this->lang
+ ];
+
+ return $serialization + parent::getSerialization();
+ }
+
+ /**
+ * @see ExpElement::newFromSerialization
+ */
+ protected static function deserialize( $serialization ) {
+
+ if ( !isset( $serialization['lexical'] ) || !isset( $serialization['datatype'] ) || !isset( $serialization['lang'] ) ) {
+ throw new RuntimeException( "Invalid format caused by a missing lexical/datatype element" );
+ }
+
+ return new self(
+ $serialization['lexical'],
+ $serialization['datatype'],
+ $serialization['lang'],
+ $serialization['dataitem']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpNsResource.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpNsResource.php
new file mode 100644
index 00000000..18e7f174
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpNsResource.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Exporter\Element;
+
+use InvalidArgumentException;
+use RuntimeException;
+use SMWDataItem as DataItem;
+
+/**
+ * A single resource (individual) for export, defined by a URI for which there
+ * also is a namespace abbreviation.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ExpNsResource extends ExpResource {
+
+ /**
+ * Local part of the abbreviated URI
+ * @var string
+ */
+ private $localName;
+
+ /**
+ * Namespace URI prefix of the abbreviated URI
+ * @var string
+ */
+ private $namespace;
+
+ /**
+ * Namespace abbreviation of the abbreviated URI
+ * @var string
+ */
+ private $namespaceId;
+
+ /**
+ * @note The given URI must not contain serialization-specific
+ * abbreviations or escapings, such as XML entities.
+ *
+ * @param string $localName Local part of the abbreviated URI
+ * @param string $namespace Namespace URI prefix of the abbreviated URI
+ * @param string $namespaceId Namespace abbreviation of the abbreviated URI
+ * @param DataItem|null $dataItem
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $localName, $namespace, $namespaceId, DataItem $dataItem = null ) {
+
+ if ( !is_string( $localName ) ) {
+ throw new InvalidArgumentException( '$localName needs to be a string' );
+ }
+
+ if ( !is_string( $namespace ) ) {
+ throw new InvalidArgumentException( '$namespace needs to be a string' );
+ }
+
+ if ( !is_string( $namespaceId ) ) {
+ throw new InvalidArgumentException( '$namespaceId needs to be a string' );
+ }
+
+ parent::__construct( $namespace . $localName, $dataItem );
+
+ $this->localName = $localName;
+ $this->namespace = $namespace;
+ $this->namespaceId = $namespaceId;
+ }
+
+ /**
+ * Return a qualified name for the element.
+ *
+ * @return string
+ */
+ public function getQName() {
+ return $this->namespaceId . ':' . $this->localName;
+ }
+
+ /**
+ * Get the namespace identifier used (the part before :).
+ *
+ * @return string
+ */
+ public function getNamespaceId() {
+ return $this->namespaceId;
+ }
+
+ /**
+ * Get the namespace URI that is used in the abbreviation.
+ *
+ * @return string
+ */
+ public function getNamespace() {
+ return $this->namespace;
+ }
+
+ /**
+ * Get the local name (the part after :).
+ *
+ * @return string
+ */
+ public function getLocalName() {
+ return $this->localName;
+ }
+
+ /**
+ * Check if the local name is qualifies as a local name in XML and
+ * Turtle. The function returns true if this is surely the case, and
+ * false if it may not be the case. However, we do not check the whole
+ * range of allowed Unicode entities for performance reasons.
+ *
+ * @return boolean
+ */
+ public function hasAllowedLocalName() {
+ return preg_match( '/^[A-Za-z_][-A-Za-z_0-9]*$/u', $this->localName );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSerialization() {
+
+ // Use '|' as divider as it is unlikely that symbol appears within a uri
+ $serialization = [
+ 'type' => self::TYPE_NSRESOURCE,
+ 'uri' => $this->localName . '|' . $this->namespace . '|' . $this->namespaceId
+ ];
+
+ return $serialization + parent::getSerialization();
+ }
+
+ /**
+ * @see ExpElement::newFromSerialization
+ */
+ protected static function deserialize( $serialization ) {
+
+ if ( !isset( $serialization['uri'] ) ) {
+ throw new RuntimeException( "Invalid serialization format, missing a uri element" );
+ }
+
+ if ( substr_count( $serialization['uri'], '|') < 2 ) {
+ throw new RuntimeException( "Invalid uri format, expected two '|' dividers" );
+ }
+
+ list( $localName, $namespace, $namespaceId ) = explode( '|', $serialization['uri'], 3 );
+
+ return new self(
+ $localName,
+ $namespace,
+ $namespaceId,
+ $serialization['dataitem']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpResource.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpResource.php
new file mode 100644
index 00000000..f991b7ea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Element/ExpResource.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\Exporter\Element;
+
+use InvalidArgumentException;
+use RuntimeException;
+use SMWDataItem as DataItem;
+
+/**
+ * A single resource (individual) for export, as defined by a URI.
+ * This class can also be used to represent blank nodes: It is assumed that all
+ * objects of class ExpElement or any of its subclasses represent a blank
+ * node if their name is empty or of the form "_id" where "id" is any
+ * identifier string. IDs are local to the current context, such as a list of
+ * triples or an SMWExpData container.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ExpResource extends ExpElement {
+
+ /**
+ * @var string
+ */
+ private $uri;
+
+ /**
+ * @var boolean
+ */
+ public $isImported = false;
+
+ /**
+ * @note The given URI must not contain serialization-specific
+ * abbreviations or escapings, such as XML entities.
+ *
+ * @param string $uri The full URI
+ * @param DataItem|null $dataItem
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $uri, DataItem $dataItem = null ) {
+
+ if ( !is_string( $uri ) ) {
+ throw new InvalidArgumentException( '$uri needs to be a string' );
+ }
+
+ parent::__construct( $dataItem );
+
+ // https://www.w3.org/2011/rdf-wg/wiki/IRIs/RDFConceptsProposal
+ // "... characters “<â€, “>â€, “{â€, “}â€, “|â€, “\â€, “^â€, “`â€, ‘“’ (double quote),
+ // and “ †(space) were allowed ... are not allowed in IRIs, Data
+ // containing these characters in %-encoded form is fine ..."
+ $this->uri = str_replace( [ '"' ], [ '%22' ], $uri );
+ }
+
+ /**
+ * Return true if this resource represents a blank node.
+ *
+ * @return boolean
+ */
+ public function isBlankNode() {
+ return $this->uri === '' || $this->uri{0} == '_';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isImported() {
+ return $this->isImported;
+ }
+
+ /**
+ * Get the URI of this resource. The result is a UTF-8 encoded URI (or
+ * IRI) without any escaping.
+ *
+ * @return string
+ */
+ public function getUri() {
+ return $this->uri;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getSerialization() {
+
+ $serialization = [
+ 'type' => self::TYPE_RESOURCE,
+ 'uri' => $this->getUri()
+ ];
+
+ return $serialization + parent::getSerialization();
+ }
+
+ /**
+ * @see ExpElement::newFromSerialization
+ */
+ protected static function deserialize( $serialization ) {
+
+ if ( !isset( $serialization['uri'] ) ) {
+ throw new RuntimeException( "Invalid serialization format" );
+ }
+
+ return new self(
+ $serialization['uri'],
+ $serialization['dataitem']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ElementFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ElementFactory.php
new file mode 100644
index 00000000..bfb45077
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ElementFactory.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace SMW\Exporter;
+
+use RuntimeException;
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpResource;
+use SMWDataItem as DataItem;
+use SMWDITime as DITime;
+use SMWExporter as Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class ElementFactory {
+
+ /**
+ * @var array
+ */
+ private $dataItemMapper = [];
+
+ /**
+ * @var array
+ */
+ private $dataItemToElementMapper = [];
+
+ /**
+ * @since 2.2
+ *
+ * @param integer $type
+ * @param Closure $dataItemEncoder
+ */
+ public function registerDataItemMapper( $type, \Closure $dataItemEncoder ) {
+ $this->dataItemMapper[$type] = $dataItemEncoder;
+ }
+
+ /**
+ * Create an Element that encodes the data for the given dataitem object.
+ * This method is meant to be used when exporting a dataitem as a subject
+ * or object.
+ *
+ * @param DataItem $dataItem
+ *
+ * @return Element|null
+ * @throws RuntimeException
+ */
+ public function newFromDataItem( DataItem $dataItem ) {
+
+ if ( $this->dataItemMapper === [] ) {
+ $this->initDataItemMap();
+ }
+
+ if ( $this->dataItemToElementMapper === [] ) {
+ $this->initDataItemToElementMapper();
+ }
+
+ $element = $this->findElementByDataItem( $dataItem );
+
+ if ( $element instanceof Element || $element === null ) {
+ return $element;
+ }
+
+ throw new RuntimeException( 'Encoder did not return a valid element' );
+ }
+
+ private function findElementByDataItem( $dataItem ) {
+
+ foreach ( $this->dataItemToElementMapper as $dataItemToElementMapper ) {
+ if ( $dataItemToElementMapper->isMapperFor( $dataItem ) ) {
+ return $dataItemToElementMapper->getElementFor( $dataItem );
+ }
+ }
+
+ foreach ( $this->dataItemMapper as $type => $callback ) {
+ if ( $type === $dataItem->getDIType() ) {
+ return $callback( $dataItem );
+ }
+ }
+
+ return null;
+ }
+
+ private function initDataItemToElementMapper() {
+ $this->dataItemToElementMapper[] = new ConceptMapper();
+ }
+
+ private function initDataItemMap() {
+
+ $lang = '';
+ $xsdValueMapper = new XsdValueMapper();
+
+ $this->registerDataItemMapper( DataItem::TYPE_NUMBER, function( $dataItem ) use ( $lang, $xsdValueMapper ) {
+
+ $xsdValueMapper->map( $dataItem );
+
+ return new ExpLiteral(
+ $xsdValueMapper->getXsdValue(),
+ $xsdValueMapper->getXsdType(),
+ $lang,
+ $dataItem
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_BLOB, function( $dataItem ) use ( $lang, $xsdValueMapper ) {
+
+ $xsdValueMapper->map( $dataItem );
+
+ return new ExpLiteral(
+ $xsdValueMapper->getXsdValue(),
+ $xsdValueMapper->getXsdType(),
+ $lang,
+ $dataItem
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_BOOLEAN, function( $dataItem ) use ( $lang, $xsdValueMapper ) {
+
+ $xsdValueMapper->map( $dataItem );
+
+ return new ExpLiteral(
+ $xsdValueMapper->getXsdValue(),
+ $xsdValueMapper->getXsdType(),
+ $lang,
+ $dataItem
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_URI, function( $dataItem ) {
+ return new ExpResource(
+ $dataItem->getURI(),
+ $dataItem
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_TIME, function( $dataItem ) use ( $lang, $xsdValueMapper ) {
+
+ $gregorianTime = $dataItem->getForCalendarModel( DITime::CM_GREGORIAN );
+ $xsdValueMapper->map( $gregorianTime );
+
+ return new ExpLiteral(
+ $xsdValueMapper->getXsdValue(),
+ $xsdValueMapper->getXsdType(),
+ $lang,
+ $gregorianTime
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_CONTAINER, function( $dataItem ) {
+ return Exporter::getInstance()->makeExportData(
+ $dataItem->getSemanticData()
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_WIKIPAGE, function( $dataItem ) {
+ return Exporter::getInstance()->getResourceElementForWikiPage(
+ $dataItem
+ );
+ } );
+
+ $this->registerDataItemMapper( DataItem::TYPE_PROPERTY, function( $dataItem ) {
+ return Exporter::getInstance()->getResourceElementForProperty(
+ $dataItem
+ );
+ } );
+
+ // Not implemented
+ $this->registerDataItemMapper( DataItem::TYPE_GEO, function( $dataItem ) {
+ return null;
+ } );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Escaper.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Escaper.php
new file mode 100644
index 00000000..ffbe97f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/Escaper.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\Exporter;
+
+use SMW\DIWikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ */
+class Escaper {
+
+ /**
+ * @since 2.2
+ *
+ * @param DIWikiPage $diWikiPage
+ *
+ * @return string
+ */
+ static public function encodePage( DIWikiPage $diWikiPage ) {
+
+ $localName = '';
+
+ if ( $diWikiPage->getInterwiki() !== '' ) {
+ $localName = $diWikiPage->getInterwiki() . ':';
+ }
+
+ if ( $diWikiPage->getNamespace() === SMW_NS_PROPERTY ) {
+ $localName .= 'Property' . ':' . $diWikiPage->getDBkey();
+ } elseif ( $diWikiPage->getNamespace() === NS_CATEGORY ) {
+ $localName .= 'Category' . ':' . $diWikiPage->getDBkey();
+ } elseif ( $diWikiPage->getNamespace() !== NS_MAIN ) {
+ $localName .= str_replace( ' ', '_', $GLOBALS['wgContLang']->getNSText( $diWikiPage->getNamespace() ) ) . ':' . $diWikiPage->getDBkey();
+ } else {
+ $localName .= $diWikiPage->getDBkey();
+ }
+
+ return self::encodeUri( $localName );
+ }
+
+ /**
+ * @param string
+ *
+ * @return string
+ */
+ static public function armorChars( $string ) {
+ return str_replace( [ '/' ], [ '-2F' ], $string );
+ }
+
+ /**
+ * This function escapes symbols that might be problematic in XML in a uniform
+ * and injective way.
+ *
+ * @param string
+ *
+ * @return string
+ */
+ static public function encodeUri( $uri ) {
+
+ $uri = $GLOBALS['smwgExportResourcesAsIri'] ? $uri : wfUrlencode( $uri );
+
+ $uri = str_replace(
+ [ '-', ' ' ],
+ [ '-2D', '_' ],
+ $uri
+ );
+
+ $uri = str_replace(
+ [ '*', ',' , ';', '<', '>', '(', ')', '[', ']', '{', '}', '\\', '$', '^', ':', '"', '#', '&', "'", '+', '!', '%' ],
+ [ '-2A', '-2C', '-3B', '-3C', '-3E', '-28', '-29', '-5B', '-5D', '-7B', '-7D', '-5C', '-24', '-5E', '-3A', '-22', '-23', '-26', '-27', '-2B', '-21', '-' ],
+ $uri
+ );
+
+ return $uri;
+ }
+
+ /**
+ * This function unescapes URIs generated with Escaper::decodeUri.
+ *
+ * @param string
+ *
+ * @return string
+ */
+ static public function decodeUri( $uri ) {
+
+ $uri = str_replace(
+ [ '-2A', '-2C', '-3B', '-3C', '-3E', '-28', '-29', '-5B', '-5D', '-7B', '-7D', '-5C', '-24', '-5E', '-3A', '-22', '-23', '-26', '-27', '-2B', '-21', '-25', '-' ],
+ [ '*', ',' , ';', '<', '>', '(', ')', '[', ']', '{', '}', '\\', '$', '^', ':', '"', '#', '&', "'", '+', '!', '%', '%' ],
+ $uri
+ );
+
+ $uri = str_replace( '%2D', '-', $uri );
+
+ return $uri;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ExpResourceMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ExpResourceMapper.php
new file mode 100644
index 00000000..954b048a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ExpResourceMapper.php
@@ -0,0 +1,295 @@
+<?php
+
+namespace SMW\Exporter;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\Element\ExpResource;
+use SMW\InMemoryPoolCache;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWExporter as Exporter;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class ExpResourceMapper {
+
+ /**
+ * Identifies auxiliary data (helper values)
+ */
+ const AUX_MARKER = 'aux';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @var InMemoryPoolCache
+ */
+ private $inMemoryPoolCache;
+
+ /**
+ * @note Legacy setting expected to vanish with 3.0
+ *
+ * @var boolean
+ */
+ private $bcAuxiliaryUse = true;
+
+ /**
+ * @var boolean
+ */
+ private $seekImportVocabulary = true;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->inMemoryPoolCache = InMemoryPoolCache::getInstance();
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function reset() {
+ $this->inMemoryPoolCache->resetPoolCacheById( 'exporter.expresource.mapper' );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param boolean $bcAuxiliaryUse
+ */
+ public function setBCAuxiliaryUse( $bcAuxiliaryUse ) {
+ $this->bcAuxiliaryUse = (bool)$bcAuxiliaryUse;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DIWikiPage $subject
+ */
+ public function invalidateCache( DIWikiPage $subject ) {
+
+ $hash = $subject->getHash();
+
+ $poolCache = $this->inMemoryPoolCache->getPoolCacheById(
+ 'exporter.expresource.mapper'
+ );
+
+ foreach ( [ $hash, $hash . self::AUX_MARKER . $this->seekImportVocabulary ] as $key ) {
+ $poolCache->delete( $key );
+ }
+ }
+
+ /**
+ * Create an ExpElement for some internal resource, given by an
+ * DIProperty object.
+ *
+ * This code is only applied to user-defined properties, since the
+ * code for special properties in
+ * Exporter::getSpecialPropertyResource may require information
+ * about the namespace in which some special property is used.
+ *
+ * @note $useAuxiliaryModifier is to determine whether an auxiliary
+ * property resource is to store a helper value
+ * (see Exporter::getDataItemHelperExpElement) should be generated
+ *
+ * @param DIProperty $property
+ * @param boolean $useAuxiliaryModifier
+ * @param boolean $seekImportVocabulary
+ *
+ * @return ExpResource
+ * @throws RuntimeException
+ */
+ public function mapPropertyToResourceElement( DIProperty $property, $useAuxiliaryModifier = false, $seekImportVocabulary = true ) {
+
+ // We want the a canonical representation to ensure that resources
+ // are language independent
+ $this->seekImportVocabulary = $seekImportVocabulary;
+ $diWikiPage = $property->getCanonicalDiWikiPage();
+
+ if ( $diWikiPage === null ) {
+ throw new RuntimeException( 'Only non-inverse, user-defined properties are permitted.' );
+ }
+
+ // No need for any aux properties besides those listed here
+ if ( !$this->bcAuxiliaryUse && $property->findPropertyTypeID() !== '_dat' && $property->findPropertyTypeID() !== '_geo' ) {
+ $useAuxiliaryModifier = false;
+ }
+
+ $expResource = $this->mapWikiPageToResourceElement( $diWikiPage, $useAuxiliaryModifier );
+ $this->seekImportVocabulary = true;
+
+ return $expResource;
+ }
+
+ /**
+ * Create an ExpElement for some internal resource, given by an
+ * DIWikiPage object. This is the one place in the code where URIs
+ * of wiki pages and user-defined properties are determined. A modifier
+ * can be given to make variants of a URI, typically done for
+ * auxiliary properties. In this case, the URI is modiied by appending
+ * "-23$modifier" where "-23" is the URI encoding of "#" (a symbol not
+ * occurring in MW titles).
+ *
+ * @param DIWikiPage $diWikiPage
+ * @param boolean $useAuxiliaryModifier
+ *
+ * @return ExpResource
+ */
+ public function mapWikiPageToResourceElement( DIWikiPage $diWikiPage, $useAuxiliaryModifier = false ) {
+
+ $modifier = $useAuxiliaryModifier ? self::AUX_MARKER : '';
+
+ $hash = $diWikiPage->getHash() . $modifier . $this->seekImportVocabulary;
+
+ $poolCache = $this->inMemoryPoolCache->getPoolCacheById( 'exporter.expresource.mapper' );
+
+ if ( $poolCache->contains( $hash ) ) {
+ return $poolCache->fetch( $hash );
+ }
+
+ if ( $diWikiPage->getSubobjectName() !== '' ) {
+ $modifier = $diWikiPage->getSubobjectName();
+ }
+
+ $resource = $this->newExpNsResource(
+ $diWikiPage,
+ $modifier
+ );
+
+ $poolCache->save(
+ $hash,
+ $resource
+ );
+
+ return $resource;
+ }
+
+ private function newExpNsResource( $diWikiPage, $modifier ) {
+
+ $importDataItem = $this->findImportDataItem( $diWikiPage, $modifier );
+
+ if ( $this->seekImportVocabulary && $importDataItem instanceof DataItem ) {
+ list( $localName, $namespace, $namespaceId ) = $this->defineElementsForImportDataItem( $importDataItem );
+ } else {
+ list( $localName, $namespace, $namespaceId ) = $this->defineElementsForDiWikiPage( $diWikiPage, $modifier );
+ }
+
+ $resource = new ExpNsResource(
+ $localName,
+ $namespace,
+ $namespaceId,
+ $diWikiPage
+ );
+
+ $resource->isImported = $importDataItem instanceof DataItem;
+ $dbKey = $diWikiPage->getDBkey();
+
+ if ( $diWikiPage->getNamespace() === SMW_NS_PROPERTY && $dbKey !== '' && $dbKey{0} !== '-' ) {
+ $resource->isUserDefined = DIProperty::newFromUserLabel( $diWikiPage->getDBkey() )->isUserDefined();
+ }
+
+ return $resource;
+ }
+
+ private function defineElementsForImportDataItem( DataItem $dataItem ) {
+
+ $importValue = $this->dataValueFactory->newDataValueByItem(
+ $dataItem,
+ new DIProperty( '_IMPO' )
+ );
+
+ return [
+ $importValue->getLocalName(),
+ $importValue->getNS(),
+ $importValue->getNSID()
+ ];
+ }
+
+ private function defineElementsForDiWikiPage( DIWikiPage $diWikiPage, $modifier ) {
+
+ $localName = '';
+ $hasFixedNamespace = false;
+
+ if ( $diWikiPage->getNamespace() === NS_CATEGORY ) {
+ $namespace = Exporter::getInstance()->getNamespaceUri( 'category' );
+ $namespaceId = 'category';
+ $localName = Escaper::encodeUri( $diWikiPage->getDBkey() );
+ $hasFixedNamespace = true;
+ }
+
+ if ( $diWikiPage->getNamespace() === SMW_NS_PROPERTY ) {
+ $namespace = Exporter::getInstance()->getNamespaceUri( 'property' );
+ $namespaceId = 'property';
+ $localName = Escaper::encodeUri( $diWikiPage->getDBkey() );
+ $hasFixedNamespace = true;
+ }
+
+ if ( ( $localName === '' ) ||
+ ( in_array( $localName{0}, [ '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ) ) ||
+ ( $hasFixedNamespace && strpos( $localName, '/' ) !== false )
+ ) {
+ $namespace = Exporter::getInstance()->getNamespaceUri( 'wiki' );
+ $namespaceId = 'wiki';
+ $localName = Escaper::encodePage( $diWikiPage );
+ }
+
+ if ( $hasFixedNamespace && strpos( $localName, '/' ) !== false ) {
+ $namespace = Exporter::getInstance()->getNamespaceUri( 'wiki' );
+ $namespaceId = 'wiki';
+ $localName = Escaper::armorChars( Escaper::encodePage( $diWikiPage ) );
+ }
+
+ // "-23$modifier" where "-23" is the URI encoding of "#" (a symbol not
+ // occurring in MW titles).
+ if ( $modifier !== '' ) {
+ $localName .= '-23' . Escaper::encodeUri( $modifier );
+ }
+
+ return [
+ $localName,
+ $namespace,
+ $namespaceId
+ ];
+ }
+
+ private function findImportDataItem( DIWikiPage $diWikiPage, $modifier ) {
+
+ $importDataItems = null;
+
+ // Only try to find an import vocab for a matchable entity
+ if ( $this->seekImportVocabulary && $diWikiPage->getNamespace() === NS_CATEGORY || $diWikiPage->getNamespace() === SMW_NS_PROPERTY ) {
+ $importDataItems = $this->store->getPropertyValues(
+ $diWikiPage,
+ new DIProperty( '_IMPO' )
+ );
+ }
+
+ if ( $importDataItems !== null && $importDataItems !== [] ) {
+ $importDataItems = current( $importDataItems );
+ }
+
+ return $importDataItems;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilder.php
new file mode 100644
index 00000000..5ab06fec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilder.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace SMW\Exporter;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface ResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function isResourceBuilderFor( DIProperty $property );
+
+ /**
+ * @since 2.5
+ *
+ * @param ExpData $expData
+ * @param DIProperty $property
+ * @param DataItem $dataItem
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..c39743fd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilder.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class AuxiliaryPropertyValueResourceBuilder extends PredefinedPropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return !$property->isUserDefined() && $this->requiresAuxiliary( $property->getKey() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $expElement = $this->exporter->getDataItemExpElement(
+ $dataItem
+ );
+
+ if ( $expElement === null ) {
+ return;
+ }
+
+ if ( $property->getKey() === $property->findPropertyTypeID() ) {
+ // Ensures that Boolean remains Boolean and not localized canonical
+ // representation such as "Booléen" when the content languageis not
+ // English
+ $expNsResource = $this->getResourceElementForProperty(
+ new DIProperty( $property->getCanonicalDiWikiPage()->getDBKey() )
+ );
+ } else {
+ $expNsResource = $this->getResourceElementHelperForProperty( $property );
+ }
+
+ $expData->addPropertyObjectValue(
+ $expNsResource,
+ $expElement
+ );
+
+ $this->addResourceHelperValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+ }
+
+ protected function requiresAuxiliary( $key ) {
+ return !in_array( $key, [ '_SKEY', '_INST', '_MDAT', '_CDAT', '_SUBC', '_SUBP', '_TYPE', '_IMPO', '_URI' ] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..722cd834
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilder.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ConceptPropertyValueResourceBuilder extends PredefinedPropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_CONC';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $expElement = $this->exporter->getDataItemExpElement(
+ $dataItem
+ );
+
+ if ( $expData->getSubject()->getUri() === '' || $expElement === null ) {
+ return;
+ }
+
+ foreach ( $expElement->getProperties() as $subp ) {
+ if ( $subp->getUri() != $this->exporter->getSpecialNsResource( 'rdf', 'type' )->getUri() ) {
+ foreach ( $expElement->getValues( $subp ) as $subval ) {
+ $expData->addPropertyObjectValue( $subp, $subval );
+ }
+ }
+ }
+
+ $this->addResourceHelperValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/DispatchingResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/DispatchingResourceBuilder.php
new file mode 100644
index 00000000..ba4e11f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/DispatchingResourceBuilder.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMW\Exporter\ResourceBuilder;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DispatchingResourceBuilder implements ResourceBuilder {
+
+ /**
+ * @var ResourceBuilder[]
+ */
+ private $resourceBuilders = [];
+
+ /**
+ * @var ResourceBuilder
+ */
+ private $defaultResourceBuilder = null;
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+
+ if ( $this->resourceBuilders === [] ) {
+ $this->initResourceBuilders();
+ }
+
+ foreach ( $this->resourceBuilders as $resourceBuilder ) {
+ if ( $resourceBuilder->isResourceBuilderFor( $property ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+ return $this->findResourceBuilder( $property )->addResourceValue( $expData, $property, $dataItem );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return ResourceBuilder $resourceBuilder
+ */
+ public function findResourceBuilder( DIProperty $property ) {
+
+ if ( $this->resourceBuilders === [] ) {
+ $this->initResourceBuilders();
+ }
+
+ foreach ( $this->resourceBuilders as $resourceBuilder ) {
+ if ( $resourceBuilder->isResourceBuilderFor( $property ) ) {
+ return $resourceBuilder;
+ }
+ }
+
+ return $this->defaultResourceBuilder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ResourceBuilder $resourceBuilder
+ */
+ public function addResourceBuilder( ResourceBuilder $resourceBuilder ) {
+ $this->resourceBuilders[] = $resourceBuilder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ResourceBuilder $defaultResourceBuilder
+ */
+ public function addDefaultResourceBuilder( ResourceBuilder $defaultResourceBuilder ) {
+ $this->defaultResourceBuilder = $defaultResourceBuilder;
+ }
+
+ private function initResourceBuilders() {
+
+ $this->addResourceBuilder( new UniquenessConstraintPropertyValueResourceBuilder() );
+
+ $sortPropertyValueResourceBuilder = new SortPropertyValueResourceBuilder();
+
+ $sortPropertyValueResourceBuilder->enabledCollationField(
+ ( (int)$GLOBALS['smwgSparqlQFeatures'] & SMW_SPARQL_QF_COLLATION ) != 0
+ );
+
+ $this->addResourceBuilder( $sortPropertyValueResourceBuilder );
+
+ $this->addResourceBuilder( new PropertyDescriptionValueResourceBuilder() );
+ $this->addResourceBuilder( new PreferredPropertyLabelResourceBuilder() );
+
+ $this->addResourceBuilder( new ExternalIdentifierPropertyValueResourceBuilder() );
+ $this->addResourceBuilder( new KeywordPropertyValueResourceBuilder() );
+
+ $this->addResourceBuilder( new MonolingualTextPropertyValueResourceBuilder() );
+ $this->addResourceBuilder( new ConceptPropertyValueResourceBuilder() );
+
+ $this->addResourceBuilder( new ImportFromPropertyValueResourceBuilder() );
+ $this->addResourceBuilder( new RedirectPropertyValueResourceBuilder() );
+
+ $this->addResourceBuilder( new AuxiliaryPropertyValueResourceBuilder() );
+ $this->addResourceBuilder( new PredefinedPropertyValueResourceBuilder() );
+
+ $this->addDefaultResourceBuilder( new PropertyValueResourceBuilder() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..a27f766f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilder.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWDIUri as DIUri;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalIdentifierPropertyValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->findPropertyTypeID() === '_eid';
+ }
+
+ /**
+ * Instead of representing an external identifier as "owl:sameAs", the weaker
+ * declarative axiom "skos:exactMatch" has been chosen to avoid potential
+ * issues with undesirable entailments.
+ *
+ * "skos:exactMatch" has been defined as "... indicating a high degree of
+ * confidence that the concepts can be used interchangeably across a wide
+ * range of information retrieval applications ..."
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $uri = $dataValue->getUri();
+
+ if ( $uri instanceof DIUri ) {
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'skos', 'exactMatch' ),
+ $this->exporter->getDataItemExpElement( $uri )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..bacc7c81
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilder.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWExpData as ExpData;
+use SMWImportValue as ImportValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImportFromPropertyValueResourceBuilder extends PredefinedPropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_IMPO';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $diSubject = $expData->getSubject()->getDataItem();
+
+ if ( $diSubject === null ) {
+ return;
+ }
+
+ $expNsResource = $this->exporter->getSpecialPropertyResource(
+ $property->getKey(),
+ $diSubject->getNamespace()
+ );
+
+
+ if ( $expNsResource === null ) {
+ return;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ if ( !$dataValue instanceof ImportValue ) {
+ return;
+ }
+
+ $expData->addPropertyObjectValue(
+ $expNsResource,
+ $this->exporter->getDataItemExpElement( new DIBlob( $dataValue->getImportReference() ) )
+ );
+
+ $this->addResourceHelperValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..7855be2b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilder.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIUri as DIUri;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class KeywordPropertyValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->findPropertyTypeID() === '_keyw';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $dataItem = new DIBlob(
+ DIBlob::normalize( $dataItem->getString() )
+ );
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $uri = $dataValue->getUri();
+
+ /**
+ * @see https://www.w3.org/2009/08/skos-reference/skos.rdf
+ *
+ * "skos:relatedMatch" has been defined as "... used to state an associative
+ * mapping link between two conceptual resources in different concept
+ * schemes ..."
+ */
+ if ( $uri instanceof DIUri ) {
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'skos', 'relatedMatch' ),
+ $this->exporter->getDataItemExpElement( $uri )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..7ab22b51
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilder.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MonolingualTextPropertyValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->findPropertyTypeID() === '_mlt_rec';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $expResourceElement = $this->exporter->getResourceElementForWikiPage(
+ $property->getCanonicalDiWikiPage(),
+ true
+ );
+
+ // Avoid that an imported vocabulary is pointing to an internal resource.
+ //
+ // For example: <Has_alternative_label> imported from <skos:altLabel>
+ // with "Monolingual text" type is expected to produce:
+ //
+ // - <property:Has_alternative_label rdf:resource="http://example.org/id/Foo_MLa9c103f4379a94bfab97819dacd3c182"/>
+ // - <skos:altLabel xml:lang="en">Foo</skos:altLabel>
+ if ( $expResourceElement->isImported() ) {
+ $seekImportVocabulary = false;
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getResourceElementForProperty( $property, false, $seekImportVocabulary ),
+ $this->exporter->getDataItemExpElement( $dataItem )
+ );
+ } else {
+ parent::addResourceValue( $expData, $property, $dataItem );
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $list = $dataValue->toArray();
+
+ if ( !isset( $list['_TEXT'] ) || !isset( $list['_LCODE'] ) ) {
+ return;
+ }
+
+ $expData->addPropertyObjectValue(
+ $expResourceElement,
+ new ExpLiteral(
+ (string)$list['_TEXT'],
+ 'http://www.w3.org/2001/XMLSchema#string',
+ (string)$list['_LCODE'],
+ $dataItem
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..6683de03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilder.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return !$property->isUserDefined();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $diSubject = $expData->getSubject()->getDataItem();
+
+ if ( $diSubject === null ) {
+ return;
+ }
+
+ $expNsResource = $this->exporter->getSpecialPropertyResource(
+ $property->getKey(),
+ $diSubject->getNamespace()
+ );
+
+ $expElement = $this->exporter->getDataItemExpElement(
+ $dataItem
+ );
+
+ if ( $expElement === null || $expNsResource === null ) {
+ return;
+ }
+
+ $expData->addPropertyObjectValue(
+ $expNsResource,
+ $expElement
+ );
+
+ $this->addResourceHelperValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilder.php
new file mode 100644
index 00000000..83c534bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilder.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PreferredPropertyLabelResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_PPLB';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $list = $dataValue->toArray();
+
+ if ( !isset( $list['_TEXT'] ) || !isset( $list['_LCODE'] ) ) {
+ return;
+ }
+
+ // https://www.w3.org/TR/2009/NOTE-skos-primer-20090818/#secpref
+ //
+ // "skos:prefLabel ... implies that a resource can only have one such
+ // label per language tag ... it is recommended that no two concepts in
+ // the same KOS be given the same preferred lexical label for any given
+ // language tag ..."
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'skos', 'prefLabel' ),
+ new ExpLiteral(
+ (string)$list['_TEXT'],
+ 'http://www.w3.org/2001/XMLSchema#string',
+ (string)$list['_LCODE'],
+ $dataItem
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilder.php
new file mode 100644
index 00000000..a99c77b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilder.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyDescriptionValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_PDESC';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $list = $dataValue->toArray();
+
+ if ( !isset( $list['_TEXT'] ) || !isset( $list['_LCODE'] ) ) {
+ return;
+ }
+
+ // Ussing `skos:scopeNote` instead of `skos:definition` since we can not
+ // ensure that the description given by a user is complete.
+ //
+ // "skos:scopeNote supplies some, possibly partial, information about the
+ // intended meaning of a concept ..."
+ //
+ // "skos:definition supplies a complete explanation of the intended
+ // meaning of a concept."
+ //
+ // According to https://www.w3.org/TR/2009/NOTE-skos-primer-20090818/#secdocumentation
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'skos', 'scopeNote' ),
+ new ExpLiteral(
+ (string)$list['_TEXT'],
+ 'http://www.w3.org/2001/XMLSchema#string',
+ (string)$list['_LCODE'],
+ $dataItem
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyValueResourceBuilder.php
new file mode 100644
index 00000000..d8eb3dc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/PropertyValueResourceBuilder.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\Exporter\ResourceBuilder;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+use SMWExporter as Exporter;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueResourceBuilder implements ResourceBuilder {
+
+ /**
+ * @var Exporter
+ */
+ protected $exporter;
+
+ /**
+ * @var InMemoryPoolCache
+ */
+ private $inMemoryPoolCache;
+
+ /**
+ * @since 2.5
+ *
+ * @param Exporter|null $exporter
+ */
+ public function __construct( Exporter $exporter = null ) {
+ $this->exporter = $exporter;
+
+ if ( $this->exporter === null ) {
+ $this->exporter = Exporter::getInstance();
+ }
+
+ $this->inMemoryPoolCache = ApplicationFactory::getInstance()->getInMemoryPoolCache()->getPoolCacheById(
+ Exporter::POOLCACHE_ID
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $expElement = $this->exporter->getDataItemExpElement(
+ $dataItem
+ );
+
+ if ( $expElement !== null ) {
+ $expData->addPropertyObjectValue(
+ $this->getResourceElementForProperty( $property ),
+ $expElement
+ );
+ }
+
+ $this->addResourceHelperValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+ }
+
+ protected function addResourceHelperValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ $expElementHelper = $this->exporter->getDataItemHelperExpElement(
+ $dataItem
+ );
+
+ if ( $expElementHelper !== null ) {
+ $expData->addPropertyObjectValue(
+ $this->getResourceElementHelperForProperty( $property ),
+ $expElementHelper
+ );
+ }
+ }
+
+ protected function getResourceElementForProperty( $property ) {
+
+ $key = 'resource:builder:' . $property->getKey();
+
+ if ( ( $resourceElement = $this->inMemoryPoolCache->fetch( $key ) ) !== false ) {
+ return $resourceElement;
+ }
+
+ $resourceElement = $this->exporter->getResourceElementForProperty( $property );
+
+ $this->inMemoryPoolCache->save(
+ $key,
+ $resourceElement
+ );
+
+ return $resourceElement;
+ }
+
+ protected function getResourceElementHelperForProperty( $property ) {
+
+ $key = 'resource:builder:aux:' . $property->getKey();
+
+ if ( ( $resourceElement = $this->inMemoryPoolCache->fetch( $key ) ) !== false ) {
+ return $resourceElement;
+ }
+
+ $resourceElement = $this->exporter->getResourceElementForProperty( $property, true );
+
+ $this->inMemoryPoolCache->save(
+ $key,
+ $resourceElement
+ );
+
+ return $resourceElement;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..a489beea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilder.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RedirectPropertyValueResourceBuilder extends PredefinedPropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_REDI';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ $expElement = $this->exporter->getDataItemExpElement(
+ $dataItem
+ );
+
+ if ( $expElement === null ) {
+ return;
+ }
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialPropertyResource( '_URI' ),
+ $expElement
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/SortPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/SortPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..3a91d0fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/SortPropertyValueResourceBuilder.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Collator;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWExpData as ExpData;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortPropertyValueResourceBuilder extends PredefinedPropertyValueResourceBuilder {
+
+ /**
+ * @var boolean
+ */
+ private $enabledCollationField = false;
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_SKEY';
+ }
+
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $enabledCollationField
+ */
+ public function enabledCollationField( $enabledCollationField ) {
+ $this->enabledCollationField = (bool)$enabledCollationField;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ $dataItem = new DIBlob( $dataItem->getSortKey() );
+ }
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ if ( $this->enabledCollationField === false ) {
+ return;
+ }
+
+ $sort = Collator::singleton()->armor(
+ Collator::singleton()->getSortKey( $dataItem->getSortKey() )
+ );
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'swivt', 'sort' ),
+ new ExpLiteral(
+ $sort,
+ 'http://www.w3.org/2001/XMLSchema#string'
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilder.php
new file mode 100644
index 00000000..40d4f55a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilder.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\Exporter\ResourceBuilders;
+
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+use SMWExpData as ExpData;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintPropertyValueResourceBuilder extends PropertyValueResourceBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isResourceBuilderFor( DIProperty $property ) {
+ return $property->getKey() === '_PVUC';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function addResourceValue( ExpData $expData, DIProperty $property, DataItem $dataItem ) {
+
+ parent::addResourceValue( $expData, $property, $dataItem );
+
+ // https://www.w3.org/TR/2004/REC-owl-ref-20040210/#FunctionalProperty-def
+ //
+ // "A functional property is a property that can have only one (unique)
+ // value y for each instance x ..."
+
+ $expData->addPropertyObjectValue(
+ $this->exporter->getSpecialNsResource( 'rdf', 'type' ),
+ $this->exporter->getSpecialNsResource( 'owl', 'FunctionalProperty' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Exporter/XsdValueMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/XsdValueMapper.php
new file mode 100644
index 00000000..80eb5795
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Exporter/XsdValueMapper.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Exporter;
+
+use RuntimeException;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDINumber as DINumber;
+use SMWDITime as DITime;
+
+/**
+ * This class only maps primitive types (string, boolean, integers ) mostly to
+ * be encoded as literal and all other dataitems are handled separately.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class XsdValueMapper {
+
+ /**
+ * @var string
+ */
+ private $xsdValue = '';
+
+ /**
+ * @var string
+ */
+ private $xsdType = '';
+
+ /**
+ * @since 2.2
+ *
+ * @param DataItem $dataItem
+ *
+ * @throws RuntimeException
+ */
+ public function map( DataItem $dataItem ) {
+
+ if ( $dataItem instanceof DIBoolean ) {
+ $this->parseToBooleanValue( $dataItem );
+ } elseif ( $dataItem instanceof DINumber ) {
+ $this->parseToDoubleValue( $dataItem );
+ } elseif ( $dataItem instanceof DIBlob ) {
+ $this->parseToStringValue( $dataItem );
+ } elseif ( $dataItem instanceof DITime && $dataItem->getCalendarModel() === DITime::CM_GREGORIAN ) {
+ $this->parseToTimeValueForGregorianCalendarModel( $dataItem );
+ } else {
+ throw new RuntimeException( "Cannot match the dataItem of type " . $dataItem->getDIType() );
+ }
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getXsdValue() {
+ return $this->xsdValue;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getXsdType() {
+ return $this->xsdType;
+ }
+
+ private function parseToStringValue( DIBlob $dataItem ) {
+ $this->xsdValue = smwfHTMLtoUTF8( $dataItem->getString() );
+ $this->xsdType = 'http://www.w3.org/2001/XMLSchema#string';
+ }
+
+ private function parseToDoubleValue( DINumber $dataItem ) {
+ $this->xsdValue = strval( $dataItem->getNumber() );
+ $this->xsdType = 'http://www.w3.org/2001/XMLSchema#double';
+ }
+
+ private function parseToBooleanValue( DIBoolean $dataItem ) {
+ $this->xsdValue = $dataItem->getBoolean() ? 'true' : 'false';
+ $this->xsdType = 'http://www.w3.org/2001/XMLSchema#boolean';
+ }
+
+ private function parseToTimeValueForGregorianCalendarModel( DITime $dataItem ) {
+
+ if ( $dataItem->getYear() > 0 ) {
+ $xsdvalue = str_pad( $dataItem->getYear(), 4, "0", STR_PAD_LEFT );
+ } else {
+ $xsdvalue = '-' . str_pad( 1 - $dataItem->getYear(), 4, "0", STR_PAD_LEFT );
+ }
+
+ $xsdtype = 'http://www.w3.org/2001/XMLSchema#gYear';
+
+ if ( $dataItem->getPrecision() >= DITime::PREC_YM ) {
+ $xsdtype = 'http://www.w3.org/2001/XMLSchema#gYearMonth';
+ $xsdvalue .= '-' . str_pad( $dataItem->getMonth(), 2, "0", STR_PAD_LEFT );
+ if ( $dataItem->getPrecision() >= DITime::PREC_YMD ) {
+ $xsdtype = 'http://www.w3.org/2001/XMLSchema#date';
+ $xsdvalue .= '-' . str_pad( $dataItem->getDay(), 2, "0", STR_PAD_LEFT );
+ if ( $dataItem->getPrecision() == DITime::PREC_YMDT ) {
+ $xsdtype = 'http://www.w3.org/2001/XMLSchema#dateTime';
+ $xsdvalue .= 'T' .
+ sprintf( "%02d", $dataItem->getHour() ) . ':' .
+ sprintf( "%02d", $dataItem->getMinute()) . ':' .
+ sprintf( "%02d", $dataItem->getSecond() );
+ }
+
+ // https://www.w3.org/TR/2005/NOTE-timezone-20051013/
+ // "Time zone identification in the date and time types relies
+ // entirely on time zone offset from UTC."
+ // Zone offset Z indicates UTC
+ $xsdvalue .= 'Z';
+ }
+ }
+
+ $this->xsdValue = $xsdvalue;
+ $this->xsdType = $xsdtype;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Factbox/CachedFactbox.php b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/CachedFactbox.php
new file mode 100644
index 00000000..11c5de72
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/CachedFactbox.php
@@ -0,0 +1,321 @@
+<?php
+
+namespace SMW\Factbox;
+
+use Onoi\Cache\Cache;
+use OutputPage;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\Parser\InTextAnnotationParser;
+use Title;
+use Language;
+
+/**
+ * Factbox output caching
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class CachedFactbox {
+
+ const CACHE_NAMESPACE = 'smw:fc';
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var boolean
+ */
+ private $isCached = false;
+
+ /**
+ * @var boolean
+ */
+ private $isEnabled = true;
+
+ /**
+ * @var integer
+ */
+ private $featureSet = 0;
+
+ /**
+ * @var integer
+ */
+ private $expiryInSeconds = 0;
+
+ /**
+ * @var integer
+ */
+ private $timestamp;
+
+ /**
+ * @since 1.9
+ *
+ * @param Cache $cache
+ */
+ public function __construct( Cache $cache ) {
+ $this->cache = $cache;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title|integer $id
+ *
+ * @return string
+ */
+ public static function makeCacheKey( $id ) {
+
+ if ( $id instanceof Title ) {
+ $id = $id->getArticleID();
+ }
+
+ return smwfCacheKey( self::CACHE_NAMESPACE, $id );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function isCached() {
+ return $this->isCached;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $featureSet
+ */
+ public function setFeatureSet( $featureSet ) {
+ $this->featureSet = $featureSet;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function setExpiryInSeconds( $expiryInSeconds ) {
+ $this->expiryInSeconds = $expiryInSeconds;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isEnabled( $isEnabled ) {
+ $this->isEnabled = $isEnabled;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return $this->timestamp;
+ }
+
+ /**
+ * Prepare and update the OutputPage property
+ *
+ * Factbox content is either retrieved from a CacheStore or re-parsed from
+ * the Factbox object
+ *
+ * Altered content is tracked using the revision Id, getLatestRevID() only
+ * changes after a content modification has occurred.
+ *
+ * @since 1.9
+ *
+ * @param OutputPage &$outputPage
+ * @param Language $language
+ * @param ParserOutput $parserOutput
+ */
+ public function prepareFactboxContent( OutputPage &$outputPage, Language $language, ParserOutput $parserOutput ) {
+
+ $content = '';
+ $title = $outputPage->getTitle();
+
+ $rev_id = $this->findRevId( $title, $outputPage->getContext() );
+ $lang = $language->getCode();
+
+ $key = self::makeCacheKey( $title );
+
+ if ( $this->cache->contains( $key ) ) {
+ $content = $this->retrieveFromCache( $key );
+ }
+
+ if ( $this->hasCachedContent( $rev_id, $lang, $content, $outputPage->getContext() ) ) {
+ return $outputPage->mSMWFactboxText = $content['text'];
+ }
+
+ $text = $this->rebuild(
+ $title,
+ $parserOutput,
+ $outputPage->getContext()
+ );
+
+ $this->addContentToCache(
+ $key,
+ $text,
+ $rev_id,
+ $lang,
+ $this->featureSet
+ );
+
+ $outputPage->mSMWFactboxText = $text;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $key
+ * @param string $text
+ * @param integer|null $revisionId
+ */
+ public function addContentToCache( $key, $text, $revisionId = null, $lang = 'en', $fset = null ) {
+ $this->saveToCache(
+ $key,
+ [
+ 'revId' => $revisionId,
+ 'lang' => $lang,
+ 'fset' => $fset,
+ 'text' => $text
+ ]
+ );
+ }
+
+ /**
+ * Returns parsed Factbox content from either the OutputPage property
+ * or from the Cache
+ *
+ * @since 1.9
+ *
+ * @param OutputPage $outputPage
+ *
+ * @return string
+ */
+ public function retrieveContent( OutputPage $outputPage ) {
+
+ $text = '';
+ $title = $outputPage->getTitle();
+
+ if ( $title instanceof Title && ( $title->isSpecialPage() || !$title->exists() ) ) {
+ return $text;
+ }
+
+ if ( isset( $outputPage->mSMWFactboxText ) ) {
+ $text = $outputPage->mSMWFactboxText;
+ } elseif ( $title instanceof Title ) {
+
+ $content = $this->retrieveFromCache(
+ self::makeCacheKey( $title )
+ );
+
+ $text = isset( $content['text'] ) ? $content['text'] : '';
+ }
+
+ return $text;
+ }
+
+ /**
+ * Return a revisionId either from the WebRequest object (display an old
+ * revision or permalink etc.) or from the title object
+ */
+ private function findRevId( Title $title, $requestContext ) {
+
+ if ( $requestContext->getRequest()->getCheck( 'oldid' ) ) {
+ return (int)$requestContext->getRequest()->getVal( 'oldid' );
+ }
+
+ return $title->getLatestRevID();
+ }
+
+ /**
+ * Processing and reparsing of the Factbox content
+ */
+ private function rebuild( Title $title, ParserOutput $parserOutput, $requestContext ) {
+
+ $text = null;
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $factbox = $applicationFactory->singleton( 'FactboxFactory' )->newFactbox(
+ $title,
+ $parserOutput
+ );
+
+ $factbox->setPreviewFlag(
+ $requestContext->getRequest()->getCheck( 'wpPreview' )
+ );
+
+ if ( $factbox->doBuild()->isVisible() ) {
+
+ $contentParser = $applicationFactory->newContentParser( $title );
+ $contentParser->parse( $factbox->getContent() );
+
+ $text = InTextAnnotationParser::removeAnnotation(
+ $contentParser->getOutput()->getText()
+ );
+
+ $text = $factbox->tabs( $text );
+ }
+
+ return $text;
+ }
+
+ private function hasCachedContent( $revId, $lang, $content, $requestContext ) {
+
+ if ( $requestContext->getRequest()->getVal( 'action' ) === 'edit' ) {
+ return $this->isCached = false;
+ }
+
+ if ( $revId !== 0 && isset( $content['revId'] ) && ( $content['revId'] === $revId ) && $content['text'] !== null ) {
+
+ if (
+ ( isset( $content['lang'] ) && $content['lang'] === $lang ) &&
+ ( isset( $content['fset'] ) && $content['fset'] === $this->featureSet ) ) {
+ return $this->isCached = true;
+ }
+ }
+
+ return $this->isCached = false;
+ }
+
+ private function retrieveFromCache( $key ) {
+
+ if ( !$this->cache->contains( $key ) || !$this->isEnabled ) {
+ return [];
+ }
+
+ $data = $this->cache->fetch( $key );
+
+ $this->isCached = true;
+ $this->timestamp = $data['time'];
+
+ return unserialize( $data['content'] );
+ }
+
+ /**
+ * Cached content is serialized in an associative array following:
+ * { 'revId' => $revisionId, 'text' => (...) }
+ */
+ private function saveToCache( $key, array $content ) {
+
+ $this->timestamp = wfTimestamp( TS_UNIX );
+ $this->isCached = false;
+
+ $data = [
+ 'time' => $this->timestamp,
+ 'content' => serialize( $content )
+ ];
+
+ $this->cache->save( $key, $data, $this->expiryInSeconds );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Factbox/Factbox.php b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/Factbox.php
new file mode 100644
index 00000000..feae3999
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/Factbox.php
@@ -0,0 +1,461 @@
+<?php
+
+namespace SMW\Factbox;
+
+use Html;
+use Sanitizer;
+use Title;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\ParserData;
+use SMW\Profiler;
+use SMW\SemanticData;
+use SMW\Store;
+use SMW\Utils\HtmlDivTable;
+use SMW\Utils\HtmlTabs;
+use SMWInfolink;
+use SMWSemanticData;
+
+/**
+ * Class handling the "Factbox" content rendering
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Factbox {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @var integer
+ */
+ private $featureSet = 0;
+
+ /**
+ * @var boolean
+ */
+ protected $isVisible = false;
+
+ /**
+ * @var string
+ */
+ protected $content = null;
+
+ /**
+ * @var boolean
+ */
+ private $previewFlag = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param ParserData $parserData
+ */
+ public function __construct( Store $store, ParserData $parserData ) {
+ $this->store = $store;
+ $this->parserData = $parserData;
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $featureSet
+ */
+ public function setFeatureSet( $featureSet ) {
+ $this->featureSet = $featureSet;
+ }
+
+ /**
+ * @note contains information about wpPreview
+ *
+ * @since 2.1
+ *
+ * @param boolean $previewFlag
+ */
+ public function setPreviewFlag( $previewFlag ) {
+ $this->previewFlag = $previewFlag;
+ }
+
+ /**
+ * Builds content suitable for rendering a Factbox and
+ * updating the ParserOutput accordingly
+ *
+ * @since 1.9
+ *
+ * @return Factbox
+ */
+ public function doBuild() {
+
+ $this->content = $this->fetchContent( $this->getMagicWords() );
+
+ if ( $this->content !== '' ) {
+ $this->parserData->getOutput()->addModules( $this->getModules() );
+ $this->parserData->pushSemanticDataToParserOutput();
+ $this->isVisible = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns Title object
+ *
+ * @since 1.9
+ *
+ * @return string|null
+ */
+ public function getTitle() {
+ return $this->parserData->getTitle();
+ }
+
+ /**
+ * Returns content
+ *
+ * @since 1.9
+ *
+ * @return string|null
+ */
+ public function getContent() {
+ return $this->content;
+ }
+
+ /**
+ * Returns if content is visible
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function isVisible() {
+ return $this->isVisible;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $rendered
+ * @param string $derived
+ *
+ * @return string
+ */
+ public static function tabs( $rendered, $derived = '' ) {
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setActiveTab( 'facts-rendered' );
+ $htmlTabs->tab(
+ 'facts-rendered',
+ Message::get( 'smw-factbox-facts' , Message::TEXT, Message::USER_LANGUAGE ),
+ [
+ 'title' => Message::get( 'smw-factbox-facts-help' , Message::TEXT, Message::USER_LANGUAGE )
+ ]
+ );
+
+ $htmlTabs->content( 'facts-rendered', $rendered );
+
+ $htmlTabs->tab(
+ 'facts-derived',
+ Message::get( 'smw-factbox-derived' , Message::TEXT, Message::USER_LANGUAGE ),
+ [
+ 'hide' => $derived === '' ? true : false
+ ]
+ );
+
+ $htmlTabs->content( 'facts-derived', $derived );
+
+ return $htmlTabs->buildHTML(
+ [
+ 'class' => 'smw-factbox'
+ ]
+ );
+ }
+
+ /**
+ * Returns magic words attached to the ParserOutput object
+ *
+ * @since 1.9
+ *
+ * @return string|null
+ */
+ protected function getMagicWords() {
+
+ $settings = $this->applicationFactory->getSettings();
+ $parserOutput = $this->parserData->getOutput();
+
+ // Prior MW 1.21 mSMWMagicWords is used (see SMW\ParserTextProcessor)
+ if ( method_exists( $parserOutput, 'getExtensionData' ) ) {
+ $smwMagicWords = $parserOutput->getExtensionData( 'smwmagicwords' );
+ $mws = $smwMagicWords === null ? [] : $smwMagicWords;
+ } else {
+ // @codeCoverageIgnoreStart
+ $mws = isset( $parserOutput->mSMWMagicWords ) ? $parserOutput->mSMWMagicWords : [];
+ // @codeCoverageIgnoreEnd
+ }
+
+ if ( in_array( 'SMW_SHOWFACTBOX', $mws ) ) {
+ $showfactbox = SMW_FACTBOX_NONEMPTY;
+ } elseif ( in_array( 'SMW_NOFACTBOX', $mws ) ) {
+ $showfactbox = SMW_FACTBOX_HIDDEN;
+ } elseif ( $this->previewFlag ) {
+ $showfactbox = $settings->get( 'smwgShowFactboxEdit' );
+ } else {
+ $showfactbox = $settings->get( 'smwgShowFactbox' );
+ }
+
+ return $showfactbox;
+ }
+
+ /**
+ * Returns required resource modules
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ protected function getModules() {
+ return [
+ 'ext.smw.style',
+ 'ext.smw.table.styles'
+ ];
+ }
+
+ /**
+ * Returns content found for a given ParserOutput object and if the required
+ * custom data was not available then semantic data are retrieved from
+ * the store for a given subject.
+ *
+ * The method checks whether the given setting of $showfactbox requires
+ * displaying the given data at all.
+ *
+ * @since 1.9
+ *
+ * @return integer $showFactbox
+ *
+ * @return string|null
+ */
+ protected function fetchContent( $showFactbox = SMW_FACTBOX_NONEMPTY ) {
+
+ if ( $showFactbox === SMW_FACTBOX_HIDDEN ) {
+ return '';
+ }
+
+ $semanticData = $this->parserData->getSemanticData();
+
+ if ( $semanticData === null || $semanticData->stubObject || $this->isEmpty( $semanticData ) ) {
+ $semanticData = $this->store->getSemanticData( $this->parserData->getSubject() );
+ }
+
+ if ( $showFactbox === SMW_FACTBOX_SPECIAL && !$semanticData->hasVisibleSpecialProperties() ) {
+ // show only if there are special properties
+ return '';
+ } elseif ( $showFactbox === SMW_FACTBOX_NONEMPTY && !$semanticData->hasVisibleProperties() ) {
+ // show only if non-empty
+ return '';
+ }
+
+ return $this->createTable( $semanticData );
+ }
+
+ /**
+ * Returns a formatted factbox table
+ *
+ * @since 1.9
+ *
+ * @param SMWSemanticData $semanticData
+ *
+ * @return string|null
+ */
+ protected function createTable( SemanticData $semanticData ) {
+
+ $html = '';
+
+ // Hook deprecated with SMW 1.9 and will vanish with SMW 1.11
+ \Hooks::run( 'smwShowFactbox', [ &$html, $semanticData ] );
+
+ // Hook since 1.9
+ if ( \Hooks::run( 'SMW::Factbox::BeforeContentGeneration', [ &$html, $semanticData ] ) ) {
+
+ $header = $this->createHeader( $semanticData->getSubject() );
+ $rows = $this->createRows( $semanticData );
+
+ $html .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwfact',
+ 'style' => 'display:block;'
+ ],
+ $header . HtmlDivTable::table(
+ $rows,
+ [
+ 'class' => 'smwfacttable'
+ ]
+ )
+ );
+ }
+
+ return $html;
+ }
+
+ private function createHeader( DIWikiPage $subject ) {
+
+ $dataValue = $this->dataValueFactory->newDataValueByItem( $subject, null );
+
+ $browselink = SMWInfolink::newBrowsingLink(
+ $dataValue->getPreferredCaption(),
+ $dataValue->getWikiValue(),
+ ''
+ );
+
+ $header = Html::rawElement(
+ 'div',
+ [ 'class' => 'smwfactboxhead' ],
+ Message::get( [ 'smw-factbox-head', $browselink->getWikiText() ], Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ $rdflink = SMWInfolink::newInternalLink(
+ Message::get( 'smw_viewasrdf', Message::TEXT, Message::USER_LANGUAGE ),
+ Localizer::getInstance()->getNamespaceTextById( NS_SPECIAL ) . ':ExportRDF/' . $dataValue->getWikiValue(),
+ 'rdflink'
+ );
+
+ $header .= Html::rawElement(
+ 'div',
+ [ 'class' => 'smwrdflink' ],
+ $rdflink->getWikiText()
+ );
+
+ return $header;
+ }
+
+ private function createRows( SemanticData $semanticData ) {
+
+ $rows = '';
+ $attributes = [];
+
+ $comma = Message::get(
+ 'comma-separator',
+ Message::ESCAPED,
+ Message::USER_LANGUAGE
+ );
+
+ $and = Message::get(
+ 'and',
+ Message::ESCAPED,
+ Message::USER_LANGUAGE
+ );
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ if ( $property->getKey() === '_SOBJ' && !$this->hasFeature( SMW_FACTBOX_DISPLAY_SUBOBJECT ) ) {
+ continue;
+ }
+
+ $propertyDv = $this->dataValueFactory->newDataValueByItem( $property, null );
+ $row = '';
+
+ if ( !$property->isShown() ) {
+ // showing this is not desired, hide
+ continue;
+ } elseif ( $property->isUserDefined() ) {
+ $propertyDv->setCaption( $propertyDv->getWikiValue() );
+ $attributes['property'] = [ 'class' => 'smwpropname' ];
+ $attributes['values'] = [ 'class' => 'smwprops' ];
+ } elseif ( $propertyDv->isVisible() ) {
+ // Predefined property
+ $attributes['property'] = [ 'class' => 'smwspecname' ];
+ $attributes['values'] = [ 'class' => 'smwspecs' ];
+ } else {
+ // predefined, internal property
+ // @codeCoverageIgnoreStart
+ continue;
+ // @codeCoverageIgnoreEnd
+ }
+
+ $list = [];
+ $html = '';
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $dataItem ) {
+
+ $dataValue = $this->dataValueFactory->newDataValueByItem( $dataItem, $property );
+
+ $outputFormat = $dataValue->getOutputFormat();
+ $dataValue->setOutputFormat( $outputFormat ? $outputFormat : 'LOCL' );
+
+ $dataValue->setOption( $dataValue::OPT_DISABLE_SERVICELINKS, true );
+
+ if ( $dataValue->isValid() ) {
+ $list[] = $dataValue->getLongWikiText( true ) . $dataValue->getInfolinkText( SMW_OUTPUT_WIKI );
+ }
+ }
+
+ if ( $list !== [] ) {
+ $last = array_pop( $list );
+
+ if ( $list === [] ) {
+ $html = $last;
+ } else {
+ $html = implode( $comma, $list ) . '&nbsp;' . $and . '&nbsp;' . $last;
+ }
+ }
+
+ $row .= HtmlDivTable::cell(
+ $propertyDv->getShortWikiText( true ),
+ $attributes['property']
+ );
+
+ $row .= HtmlDivTable::cell(
+ $html,
+ $attributes['values']
+ );
+
+ $rows .= HtmlDivTable::row(
+ $row
+ );
+ }
+
+ return $rows;
+ }
+
+ private function isEmpty( SemanticData $semanticData ) {
+
+ // MW's internal Parser does iterate the ParserOutput object several times
+ // which can leave a '_SKEY' property while in fact the container is empty.
+ $semanticData->removeProperty(
+ new DIProperty( '_SKEY' )
+ );
+
+ return $semanticData->isEmpty();
+ }
+
+
+ private function hasFeature( $feature ) {
+ return ( (int)$this->featureSet & $feature ) != 0;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Factbox/FactboxFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/FactboxFactory.php
new file mode 100644
index 00000000..239d037a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Factbox/FactboxFactory.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Factbox;
+
+use IContextSource;
+use OutputPage;
+use SMW\ApplicationFactory;
+use Title;
+use ParserOutput;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FactboxFactory {
+
+ /**
+ * @since 2.0
+ *
+ * @return CachedFactbox
+ */
+ public function newCachedFactbox() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $cachedFactbox = new CachedFactbox(
+ $applicationFactory->getCache(
+ $settings->get( 'smwgMainCacheType' )
+ )
+ );
+
+ // Month = 30 * 24 * 3600
+ $cachedFactbox->setExpiryInSeconds( 2592000 );
+
+ $cachedFactbox->isEnabled(
+ $settings->isFlagSet( 'smwgFactboxFeatures', SMW_FACTBOX_CACHE )
+ );
+
+ $cachedFactbox->setFeatureSet(
+ $settings->get( 'smwgFactboxFeatures' )
+ );
+
+ return $cachedFactbox;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ * @param ParserOutput $parserOutput
+ *
+ * @return Factbox
+ */
+ public function newFactbox( Title $title, ParserOutput $parserOutput ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $factbox = new Factbox(
+ $applicationFactory->getStore(),
+ $applicationFactory->newParserData( $title, $parserOutput )
+ );
+
+ $factbox->setFeatureSet(
+ $applicationFactory->getSettings()->get( 'smwgFactboxFeatures' )
+ );
+
+ return $factbox;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/GlobalFunctions.php b/www/wiki/extensions/SemanticMediaWiki/src/GlobalFunctions.php
new file mode 100644
index 00000000..7fa3a9e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/GlobalFunctions.php
@@ -0,0 +1,282 @@
+<?php
+
+use SMW\CompatibilityMode;
+use SMW\DataValues\Number\IntlNumberFormatter;
+use SMW\Highlighter;
+use SMW\NamespaceManager;
+use SMW\ProcessingErrorMsgHandler;
+
+/**
+ * Global functions specified and used by Semantic MediaWiki. In general, it is
+ * tried to fit functions in suitable classes as static methods if they clearly
+ * belong to some particular sub-function of SMW. Most functions here are used
+ * in diverse contexts so that they do not have fonud a place in any such class
+ * yet.
+ * @ingroup SMW
+ */
+
+/**
+ * Takes a title text and turns it safely into its DBKey. This function
+ * reimplements most of the title normalization as done in Title.php in order
+ * to achieve conversion with less overhead. The official code could be called
+ * here if more advanced normalization is needed.
+ *
+ * @param string $text
+ */
+function smwfNormalTitleDBKey( $text ) {
+ global $wgCapitalLinks;
+
+ $text = trim( $text );
+
+ if ( $wgCapitalLinks ) {
+ $text = ucfirst( $text );
+ }
+
+ return str_replace( ' ', '_', $text );
+}
+
+/**
+ * Takes a text and turns it into a normalised version. This function
+ * reimplements the title normalization as done in Title.php in order to
+ * achieve conversion with less overhead. The official code could be called
+ * here if more advanced normalization is needed.
+ *
+ * @param string $text
+ */
+function smwfNormalTitleText( $text ) {
+ global $wgCapitalLinks, $wgContLang;
+
+ $text = trim( $text );
+
+ if ( $wgCapitalLinks ) {
+ $text = $wgContLang->ucfirst( $text );
+ }
+
+ return str_replace( '_', ' ', $text );
+}
+
+/**
+ * Escapes text in a way that allows it to be used as XML content (e.g. as a
+ * string value for some property).
+ *
+ * @param string $text
+ */
+function smwfXMLContentEncode( $text ) {
+ return str_replace( [ '&', '<', '>' ], [ '&amp;', '&lt;', '&gt;' ], Sanitizer::decodeCharReferences( $text ) );
+}
+
+/**
+ * Decodes character references and inserts Unicode characters instead, using
+ * the MediaWiki Sanitizer.
+ *
+ * @param string $text
+ */
+function smwfHTMLtoUTF8( $text ) {
+ return Sanitizer::decodeCharReferences( $text );
+}
+
+/**
+ * @deprecated since 2.1, use NumberFormatter instead
+ */
+function smwfNumberFormat( $value, $decplaces = 3 ) {
+ return IntlNumberFormatter::getInstance()->getLocalizedFormattedNumber( $value, $decplaces );
+}
+
+/**
+ * @since 3.0
+ *
+ * @param string $text
+ */
+function smwfAbort( $text ) {
+
+ if ( PHP_SAPI === 'cli' && PHP_SAPI === 'phpdbg' ) {
+ die( $text );
+ }
+
+ $html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
+ $html .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" dir=\"ltr\">\n";
+ $html .= "<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />";
+ $html .= "<title>Error</title></head><body><h2>Error</h2><hr style='border: 0; height: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); border-bottom: 1px solid rgba(255, 255, 255, 0.3);'>";
+ $html .= "<p>{$text}</p></body></html>";
+
+ die( $html );
+}
+
+/**
+ * Formats an array of message strings so that it appears as a tooltip.
+ * $icon should be one of: 'warning' (default), 'info'.
+ *
+ * @param array $messages
+ * @param string $icon Acts like an enum. Callers must ensure safety, since this value is used directly in the output.
+ * @param string $seperator
+ * @param boolean $escape Should the messages be escaped or not (ie when they already are)
+ *
+ * @return string
+ */
+function smwfEncodeMessages( array $messages, $type = 'warning', $seperator = ' <!--br-->', $escape = true ) {
+
+ $messages = ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $messages );
+
+ if ( $messages === [] ) {
+ return '';
+ }
+
+ if ( $escape ) {
+ $messages = array_map( 'htmlspecialchars', $messages );
+ }
+
+ if ( count( $messages ) == 1 ) {
+ $content = $messages[0];
+ } else {
+ foreach ( $messages as &$message ) {
+ $message = '<li>' . $message . '</li>';
+ }
+
+ $content = '<ul>' . implode( $seperator, $messages ) . '</ul>';
+ }
+
+ // Stop when a previous processing produced an error and it is expected to be
+ // added to a new tooltip (e.g {{#info {{#show ...}} }} ) instance
+ if ( Highlighter::hasHighlighterClass( $content, 'warning' ) ) {
+ return $content;
+ }
+
+ $highlighter = Highlighter::factory( $type );
+
+ $highlighter->setContent( [
+ 'caption' => null,
+ 'content' => Highlighter::decode( $content )
+ ] );
+
+ return $highlighter->getHtml();
+}
+
+/**
+ * Returns an instance for the storage back-end
+ *
+ * @return SMWStore
+ */
+function &smwfGetStore() {
+ $store = \SMW\StoreFactory::getStore();
+ return $store;
+}
+
+/**
+ * @since 3.0
+ *
+ * @param string $namespace
+ * @param string $key
+ *
+ * @return string
+ */
+function smwfCacheKey( $namespace, $key ) {
+
+ $cachePrefix = $GLOBALS['wgCachePrefix'] === false ? wfWikiID() : $GLOBALS['wgCachePrefix'];
+
+ if ( $namespace{0} !== ':' ) {
+ $namespace = ':' . $namespace;
+ }
+
+ if ( is_array( $key ) ) {
+ $key = json_encode( $key );
+ }
+
+ return $cachePrefix . $namespace . ':' . md5( $key );
+}
+
+/**
+ * Compatibility helper for using Linker methods.
+ * MW 1.16 has a Linker with non-static methods,
+ * where in MW 1.19 they are static, and a DummyLinker
+ * class is introduced, which can be instantiated for
+ * compat reasons. As of MW 1.28, DummyLinker is being
+ * deprecated, so always use Linker.
+ *
+ * @since 1.6
+ *
+ * @return Linker
+ */
+function smwfGetLinker() {
+ static $linker = false;
+
+ if ( $linker === false ) {
+ $linker = new Linker();
+ }
+
+ return $linker;
+}
+
+/**
+ * @private
+ *
+ * Copied from wfCountDown as it became deprecated in 1.31
+ *
+ * @since 3.0
+ */
+function swfCountDown( $seconds ) {
+ for ( $i = $seconds; $i >= 0; $i-- ) {
+ if ( $i != $seconds ) {
+ echo str_repeat( "\x08", strlen( $i + 1 ) );
+ }
+ echo $i;
+ flush();
+ if ( $i ) {
+ sleep( 1 );
+ }
+ }
+ echo "\n";
+}
+
+/**
+ * Function to switch on Semantic MediaWiki. This function must be called in
+ * LocalSettings.php after including SMW_Settings.php. It is used to ensure
+ * that required parameters for SMW are really provided explicitly. For
+ * readability, this is the only global function that does not adhere to the
+ * naming conventions.
+ *
+ * This function also sets up all autoloading, such that all SMW classes are
+ * available as early on. Moreover, jobs and special pages are registered.
+ *
+ * @param mixed $namespace
+ * @param boolean $complete
+ *
+ * @return true
+ *
+ * @codeCoverageIgnore
+ */
+function enableSemantics( $namespace = null, $complete = false ) {
+ global $smwgNamespace;
+
+ // #1732 + #2813
+ wfLoadExtension( 'SemanticMediaWiki', dirname( __DIR__ ) . '/extension.json' );
+
+ // Apparently this is required (1.28+) as the earliest possible execution
+ // point in order for settings that refer to the SMW_NS_PROPERTY namespace
+ // to be available in LocalSettings
+ NamespaceManager::initCustomNamespace( $GLOBALS );
+
+ if ( !$complete && ( $smwgNamespace !== '' ) ) {
+ // The dot tells that the domain is not complete. It will be completed
+ // in the Export since we do not want to create a title object here when
+ // it is not needed in many cases.
+ $smwgNamespace = '.' . $namespace;
+ } else {
+ $smwgNamespace = $namespace;
+ }
+
+ $GLOBALS['smwgSemanticsEnabled'] = true;
+
+ return true;
+}
+
+/**
+ * To disable Semantic MediaWiki's operational functionality
+ *
+ * @note This function can be used to temporary disable SMW but it is paramount
+ * that after SMW is re-enabled to run `rebuildData.php` in order for data to
+ * represent a state that mirrors the actual environment (deleted, moved pages
+ * are not tracked when disabled).
+ */
+function disableSemantics() {
+ CompatibilityMode::disableSemantics();
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/HashBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/HashBuilder.php
new file mode 100644
index 00000000..7d5bb100
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/HashBuilder.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace SMW;
+
+use Title;
+
+/**
+ * Utility class to create unified hash keys for a variety of objects
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HashBuilder {
+
+ /**
+ * @since 2.4
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return string
+ */
+ public static function createFromSemanticData( SemanticData $semanticData ) {
+
+ $hash = [];
+ $hash[] = $semanticData->getSubject()->getSerialization();
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $hash[] = $property->getKey();
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $di ) {
+ $hash[] = $di->getSerialization();
+ }
+ }
+
+ foreach ( $semanticData->getSubSemanticData() as $data ) {
+ $hash[] = $data->getHash();
+ }
+
+ sort( $hash );
+
+ return md5( implode( '#', $hash ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string|array $hashableContent
+ * @param string $prefix
+ *
+ * @return string
+ */
+ public static function createFromContent( $hashableContent, $prefix = '' ) {
+
+ if ( is_string( $hashableContent ) ) {
+ $hashableContent = [ $hashableContent ];
+ }
+
+ return $prefix . md5( json_encode( $hashableContent ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $hashableContent
+ * @param string $prefix
+ *
+ * @return string
+ */
+ public static function createFromArray( array $hashableContent, $prefix = '' ) {
+ return $prefix . md5( json_encode( $hashableContent ) );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public static function createFromSegments( /* args */ ) {
+ return implode( '#', func_get_args() );
+ }
+
+ /**
+ * @deprecated since 2.4, use Hash::createFromSegments
+ * @since 2.1
+ *
+ * @param string $title
+ * @param string $namespace
+ * @param string $interwiki
+ * @param string $fragment
+ *
+ * @return string
+ */
+ public static function createHashIdFromSegments( $title, $namespace, $interwiki = '', $fragment = '' ) {
+ return self::createFromSegments( $title, $namespace, $interwiki, $fragment );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ *
+ * @return string
+ */
+ public static function getHashIdForTitle( Title $title ) {
+ return self::createFromSegments(
+ $title->getDBKey(),
+ $title->getNamespace(),
+ $title->getInterwiki(),
+ $title->getFragment()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $dataItem
+ *
+ * @return string
+ */
+ public static function getHashIdForDiWikiPage( DIWikiPage $dataItem ) {
+ return self::createFromSegments(
+ $dataItem->getDBKey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $dataItem->getSubobjectName()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $hash
+ *
+ * @return Title|null
+ */
+ public static function newTitleFromHash( $hash ) {
+ list( $title, $namespace, $interwiki, $fragement ) = explode( '#', $hash, 4 );
+ return Title::makeTitle( $namespace, $title, $fragement, $interwiki );
+ }
+
+ /**
+ * @note This method does not make additional checks therefore it is assumed
+ * that the input hash is derived or generated from HashBuilder::getSegmentedHashId
+ *
+ * @since 2.1
+ *
+ * @param string
+ *
+ * @return DIWikiPage|null
+ */
+ public static function newDiWikiPageFromHash( $hash ) {
+
+ list( $title, $namespace, $interwiki, $subobjectName ) = explode( '#', $hash, 4 );
+
+ // A leading underscore is an internal SMW convention to describe predefined
+ // properties and as such need to be transformed into a valid representation
+ if ( $title{0} === '_' ) {
+ $title = str_replace( ' ', '_', PropertyRegistry::getInstance()->findPropertyLabelById( $title ) );
+ }
+
+ return new DIWikiPage( $title, $namespace, $interwiki, $subobjectName );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/HierarchyLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/HierarchyLookup.php
new file mode 100644
index 00000000..584fb6f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/HierarchyLookup.php
@@ -0,0 +1,355 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use Onoi\Cache\Cache;
+use Psr\Log\LoggerAwareTrait;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class HierarchyLookup {
+
+ use LoggerAwareTrait;
+
+ /**
+ * Persistent cache namespace
+ */
+ const CACHE_NAMESPACE = 'smw:hierarchy';
+
+ /**
+ * Consecutive hierarchy types
+ */
+ const TYPE_PROPERTY = 'type.property';
+ const TYPE_CATEGORY = 'type.category';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Cache|null
+ */
+ private $cache;
+
+ /**
+ * @var []
+ */
+ private $inMemoryCache = [];
+
+ /**
+ * @var integer
+ */
+ private $cacheTTL;
+
+ /**
+ * Use 0 to disable the hierarchy lookup
+ *
+ * @var integer
+ */
+ private $subcategoryDepth = 10;
+
+ /**
+ * Use 0 to disable the hierarchy lookup
+ *
+ * @var integer
+ */
+ private $subpropertyDepth = 10;
+
+ /**
+ * @since 2.3
+ *
+ * @param Store $store
+ * @param Cache $cache
+ */
+ public function __construct( Store $store, Cache $cache ) {
+ $this->store = $store;
+ $this->cache = $cache;
+
+ $this->cacheTTL = 60 * 60 * 24 * 7;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ChangePropListener $changePropListener
+ */
+ public function addListenersTo( ChangePropListener $changePropListener ) {
+
+ // @see HierarchyLookup::getConsecutiveHierarchyList
+ //
+ // Remove the global hierarchy cache in the event that some entity was
+ // annotated (or removed) with the `Subproperty of`/ `Subcategory of`
+ // property, and while this purges the entire cache we ensure that the
+ // hierarchy lookup is always correct without loosing too much sleep
+ // over a more fine-grained caching strategy.
+
+ $callback = function( $context ) {
+ $this->cache->delete(
+ smwfCacheKey( self::CACHE_NAMESPACE, [ self::TYPE_PROPERTY, $this->subpropertyDepth ] )
+ );
+ };
+
+ $changePropListener->addListenerCallback( '_SUBP', $callback );
+
+ $callback = function( $context ) {
+ $this->cache->delete(
+ smwfCacheKey( self::CACHE_NAMESPACE, [ self::TYPE_CATEGORY, $this->subcategoryDepth ] )
+ );
+ };
+
+ $changePropListener->addListenerCallback( '_SUBC', $callback );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $subcategoryDepth
+ */
+ public function setSubcategoryDepth( $subcategoryDepth ) {
+ $this->subcategoryDepth = (int)$subcategoryDepth;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $subpropertyDepth
+ */
+ public function setSubpropertyDepth( $subpropertyDepth ) {
+ $this->subpropertyDepth = (int)$subpropertyDepth;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasSubproperty( DIProperty $property ) {
+
+ if ( $this->subpropertyDepth < 1 ) {
+ return false;
+ }
+
+ $result = $this->getConsecutiveHierarchyList(
+ $property
+ );
+
+ return $result !== [];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIWikiPage $category
+ *
+ * @return boolean
+ */
+ public function hasSubcategory( DIWikiPage $category ) {
+
+ if ( $this->subcategoryDepth < 1 ) {
+ return false;
+ }
+
+ $result = $this->getConsecutiveHierarchyList(
+ $category
+ );
+
+ return $result !== [];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIProperty $property
+ *
+ * @return DIWikiPage[]|[]
+ */
+ public function findSubpropertyList( DIProperty $property ) {
+
+ if ( $this->subpropertyDepth < 1 ) {
+ return false;
+ }
+
+ return $this->lookup( '_SUBP', $property->getKey(), $property->getDiWikiPage(), new RequestOptions() );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DIWikiPage $category
+ *
+ * @return DIWikiPage[]|[]
+ */
+ public function findSubcategoryList( DIWikiPage $category ) {
+
+ if ( $this->subcategoryDepth < 1 ) {
+ return [];
+ }
+
+ return $this->lookup( '_SUBC', $category->getDBKey(), $category, new RequestOptions() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty|DIWikiPage $id
+ *
+ * @return DIProperty[]|DIWikiPage[]|[]
+ */
+ public function getConsecutiveHierarchyList( $id ) {
+
+ $hierarchyType = null;
+
+ if ( $id instanceof DIProperty ) {
+ $hierarchyType = self::TYPE_PROPERTY;
+ } elseif ( $id instanceof DIWikiPage && $id->getNamespace() === NS_CATEGORY ) {
+ $hierarchyType = self::TYPE_CATEGORY;
+ }
+
+ if ( $hierarchyType === null ) {
+ throw new InvalidArgumentException( 'No matchable hierarchy type, expected a property or category entity.' );
+ }
+
+ // Store elements of the hierarchy tree in one large cache slot
+ // since we are unable to detect if or when a leaf is removed from within
+ // a cached tree unless one stores child and parent in a secondary cache.
+ //
+ // On the assumption that hierarchy data are less frequently changed, using
+ // a "global" cache should be sufficient to avoid constant DB lookups.
+ //
+ // Invalidation of the cache will occur on each _SUBP/_SUBC change event (see
+ // ChangePropListener).
+ $cacheKey = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ $hierarchyType,
+ ( $hierarchyType === self::TYPE_PROPERTY ? $this->subpropertyDepth : $this->subcategoryDepth )
+ ]
+ );
+
+ $hierarchyCache = $this->cache->fetch( $cacheKey );
+ $reqCacheUpdate = false;
+
+ if ( $hierarchyCache === false ) {
+ $hierarchyCache = [];
+ }
+
+ $hierarchyMembers = [];
+ $key = $hierarchyType === self::TYPE_PROPERTY ? $id->getKey() : $id->getDBKey();
+
+ if ( !isset( $hierarchyCache[$key] ) ) {
+ $hierarchyCache[$key] = [];
+
+ if ( $hierarchyType === self::TYPE_PROPERTY ) {
+ $this->findSubproperties( $hierarchyMembers, $id, 1 );
+ } else {
+ $this->findSubcategories( $hierarchyMembers, $id, 1 );
+ }
+
+ $hierarchyList[$key] = $hierarchyMembers;
+
+ // Store only the key to keep the cache size low
+ foreach ( $hierarchyList[$key] as $k ) {
+ if ( $hierarchyType === self::TYPE_PROPERTY ) {
+ $hierarchyCache[$key][] = $k->getKey();
+ } else {
+ $hierarchyCache[$key][] = $k->getDBKey();
+ }
+ }
+
+ $reqCacheUpdate = true;
+ } else {
+ $hierarchyList[$key] = [];
+
+ foreach ( $hierarchyCache[$key] as $k ) {
+ if ( $hierarchyType === self::TYPE_PROPERTY ) {
+ $hierarchyList[$key][] = new DIProperty( $k );
+ } else {
+ $hierarchyList[$key][] = new DIWikiPage( $k, NS_CATEGORY );
+ }
+ }
+ }
+
+ if ( $reqCacheUpdate ) {
+ $this->cache->save( $cacheKey, $hierarchyCache, $this->cacheTTL );
+ }
+
+ return $hierarchyList[$key];
+ }
+
+ private function findSubproperties( &$hierarchyMembers, DIProperty $property, $depth ) {
+
+ if ( $depth++ > $this->subpropertyDepth ) {
+ return;
+ }
+
+ $propertyList = $this->findSubpropertyList(
+ $property
+ );
+
+ if ( $propertyList === null || $propertyList === [] ) {
+ return;
+ }
+
+ foreach ( $propertyList as $property ) {
+ $property = DIProperty::newFromUserLabel(
+ $property->getDBKey()
+ );
+
+ $hierarchyMembers[] = $property;
+ $this->findSubproperties( $hierarchyMembers, $property, $depth );
+ }
+ }
+
+ private function findSubcategories( &$hierarchyMembers, DIWikiPage $category, $depth ) {
+
+ if ( $depth++ > $this->subcategoryDepth ) {
+ return;
+ }
+
+ $categoryList = $this->findSubcategoryList(
+ $category
+ );
+
+ foreach ( $categoryList as $category ) {
+ $hierarchyMembers[] = $category;
+ $this->findSubcategories( $hierarchyMembers, $category, $depth );
+ }
+ }
+
+ private function lookup( $id, $key, DIWikiPage $subject, $requestOptions ) {
+
+ $key = $id . '#' . $key . '#' . md5( $requestOptions->getHash() );
+
+ if ( isset( $this->inMemoryCache[$key] ) ) {
+ return $this->inMemoryCache[$key];
+ }
+
+ $res = $this->store->getPropertySubjects(
+ new DIProperty( $id ),
+ $subject,
+ $requestOptions
+ );
+
+ $this->inMemoryCache[$key] = $res;
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'id' => $id,
+ 'origin' => $subject
+ ];
+
+ $this->logger->info( "[HierarchyLookup] Lookup: {id}, {origin}", $context );
+
+ return $res;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreator.php
new file mode 100644
index 00000000..5c910bd4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreator.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace SMW\Importer;
+
+use Onoi\MessageReporter\MessageReporterAware;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+interface ContentCreator extends MessageReporterAware {
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ *
+ * @return boolean
+ */
+ public function canCreateContentsFor( ImportContents $importContents );
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ */
+ public function create( ImportContents $importContents );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/DispatchingContentCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/DispatchingContentCreator.php
new file mode 100644
index 00000000..867c44c7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/DispatchingContentCreator.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Importer\ContentCreators;
+
+use Onoi\MessageReporter\MessageReporter;
+use RuntimeException;
+use SMW\Importer\ContentCreator;
+use SMW\Importer\ImportContents;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DispatchingContentCreator implements ContentCreator {
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var ContentCreator[]
+ */
+ private $contentCreators = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ContentCreator[]
+ */
+ public function __construct( array $contentCreators ) {
+ $this->contentCreators = $contentCreators;
+ }
+
+ /**
+ * @see MessageReporterAware::setMessageReporter
+ *
+ * @since 3.0
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ */
+ public function canCreateContentsFor( ImportContents $importContents ) {
+
+ foreach ( $this->contentCreators as $contentCreator ) {
+ if ( $contentCreator->canCreateContentsFor( $importContents ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ * @throws RuntimeException
+ */
+ public function create( ImportContents $importContents ) {
+
+ foreach ( $this->contentCreators as $contentCreator ) {
+ if ( $contentCreator->canCreateContentsFor( $importContents ) ) {
+ $contentCreator->setMessageReporter( $this->messageReporter );
+ return $contentCreator->create( $importContents );
+ }
+ }
+
+ throw new RuntimeException( "No dispatchable ContentsCreator is assigned to type " . $importContents->getContentType() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/TextContentCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/TextContentCreator.php
new file mode 100644
index 00000000..81d04045
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/TextContentCreator.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SMW\Importer\ContentCreators;
+
+use ContentHandler;
+use Onoi\MessageReporter\MessageReporter;
+use SMW\Importer\ContentCreator;
+use SMW\Importer\ImportContents;
+use SMW\MediaWiki\Database;
+use SMW\MediaWiki\PageCreator;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextContentCreator implements ContentCreator {
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @since 2.5
+ *
+ * @param PageCreator $pageCreator
+ * @param Database $connection
+ */
+ public function __construct( PageCreator $pageCreator, Database $connection ) {
+ $this->pageCreator = $pageCreator;
+ $this->connection = $connection;
+ }
+
+ /**
+ * @see MessageReporterAware::setMessageReporter
+ *
+ * @since 2.5
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ImportContents $importContents
+ */
+ public function canCreateContentsFor( ImportContents $importContents ) {
+ return $importContents->getContentType() === ImportContents::CONTENT_TEXT;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ImportContents $importContents
+ */
+ public function create( ImportContents $importContents ) {
+
+ if ( !class_exists( 'ContentHandler' ) ) {
+ return $this->messageReporter->reportMessage( "\nContentHandler doesn't exist therefore importing is not possible.\n" );
+ }
+
+ $indent = ' ...';
+ $name = $importContents->getName();
+
+ if ( $name === '' ) {
+ return $this->messageReporter->reportMessage( "$indent no valid page name, abort import." );
+ }
+
+ $title = Title::newFromText(
+ $name,
+ $importContents->getNamespace()
+ );
+
+ if ( $title === null ) {
+ return $this->messageReporter->reportMessage( "$indent $name returned with a null title, abort import." );
+ }
+
+ $prefixedText = $title->getPrefixedText();
+
+ if ( $title->exists() && !$importContents->getOption( 'canReplace' ) && !$importContents->getOption( 'replaceable' ) ) {
+ return $this->messageReporter->reportMessage( "$indent skipping $prefixedText, already exists ...\n" );
+ } elseif( $title->exists() ) {
+ $this->messageReporter->reportMessage( "$indent replacing $prefixedText contents ...\n" );
+ } else {
+ $this->messageReporter->reportMessage( "$indent creating $prefixedText contents ...\n" );
+ }
+
+ // Avoid a possible "Notice: WikiPage::doEditContent: Transaction already
+ // in progress (from DatabaseUpdater::doUpdates), performing implicit
+ // commit ..."
+ $this->connection->onTransactionIdle( function() use ( $title, $importContents ) {
+ $this->doCreateContent( $title, $importContents );
+ } );
+ }
+
+ private function doCreateContent( $title, $importContents ) {
+
+ $page = $this->pageCreator->createPage( $title );
+
+ $content = ContentHandler::makeContent(
+ $this->fetchContents( $importContents ),
+ $title
+ );
+
+ $page->doEditContent(
+ $content,
+ $importContents->getDescription(),
+ EDIT_FORCE_BOT
+ );
+
+ $title->invalidateCache();
+ }
+
+ private function fetchContents( $importContents ) {
+
+ if ( $importContents->getContentsFile() === '' ) {
+ return $importContents->getContents();
+ }
+
+ $contents = file_get_contents( $importContents->getContentsFile() );
+
+ // http://php.net/manual/en/function.file-get-contents.php
+ return mb_convert_encoding(
+ $contents,
+ 'UTF-8',
+ mb_detect_encoding(
+ $contents,
+ 'UTF-8, ISO-8859-1, ISO-8859-2',
+ true
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/XmlContentCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/XmlContentCreator.php
new file mode 100644
index 00000000..5785acc5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentCreators/XmlContentCreator.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace SMW\Importer\ContentCreators;
+
+use Onoi\MessageReporter\MessageReporter;
+use SMW\Importer\ContentCreator;
+use SMW\Importer\ImportContents;
+use SMW\Services\ImporterServiceFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class XmlContentCreator implements ContentCreator {
+
+ /**
+ * @var ImportContentsIterator
+ */
+ private $importerServiceFactory;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @since 3.0
+ *
+ * @param ImporterServiceFactory $importerServiceFactory
+ */
+ public function __construct( ImporterServiceFactory $importerServiceFactory ) {
+ $this->importerServiceFactory = $importerServiceFactory;
+ }
+
+ /**
+ * @see MessageReporterAware::setMessageReporter
+ *
+ * @since 3.0
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ */
+ public function canCreateContentsFor( ImportContents $importContents ) {
+ return $importContents->getContentType() === ImportContents::CONTENT_XML;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportContents $importContents
+ */
+ public function create( ImportContents $importContents ) {
+
+ $indent = ' ...';
+
+ if ( $importContents->getOption( 'skip' ) === true || $importContents->getContentsFile() === '' ) {
+ return $this->messageReporter->reportMessage( "\n " . $importContents->getDescription() . " was skipped.\n" );
+ }
+
+ $importSource = $this->importerServiceFactory->newImportStreamSource(
+ @fopen( $importContents->getContentsFile(), 'rt' )
+ );
+
+ $importer = $this->importerServiceFactory->newWikiImporter(
+ $importSource
+ );
+
+ $importer->setDebug( false );
+ $importer->setPageOutCallback( [ $this, 'reportPage' ] );
+
+ if ( $importContents->getDescription() !== '' ) {
+ $this->messageReporter->reportMessage( "\n " . $importContents->getDescription() . "\n" );
+ }
+
+ try {
+ $importer->doImport();
+ } catch ( \Exception $e ) {
+ $this->messageReporter->reportMessage( "Failed with " . $e->getMessage() );
+ }
+
+ $this->messageReporter->reportMessage( "$indent done.\n" );
+ }
+
+ /**
+ * @see WikiImporter::handlePage
+ *
+ * @param Title $title
+ * @param ForeignTitle $foreignTitle
+ * @param int $revisionCount
+ * @param int $successCount
+ * @param array $pageInfo
+ */
+ public function reportPage( $title, $foreignTitle, $revisionCount, $successCount, $pageInfo ) {
+
+ $indent = ' ...';
+
+ // Invalid or non-importable title
+ if ( $title === null ) {
+ return;
+ }
+
+ $title->invalidateCache();
+
+ if ( $successCount > 0 ) {
+ $this->messageReporter->reportMessage( "$indent importing " . $title->getPrefixedText() . "\n" );
+ } else {
+ $this->messageReporter->reportMessage( "$indent skipping " . $title->getPrefixedText() . ", no new revision\n" );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentIterator.php
new file mode 100644
index 00000000..c0efb3c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentIterator.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace SMW\Importer;
+
+use IteratorAggregate;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface ContentIterator extends IteratorAggregate {
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getDescription();
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentModeller.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentModeller.php
new file mode 100644
index 00000000..4f667a7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ContentModeller.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Importer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentModeller {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $fileDir
+ * @param array $fileContents
+ *
+ * @return ImportContents[]|[]
+ */
+ public function makeContentList( $fileDir, array $fileContents ) {
+
+ $contents = [];
+
+ if ( !isset( $fileContents['import'] ) ) {
+ return $contents;
+ }
+
+ foreach ( $fileContents['import'] as $value ) {
+
+ $importContents = new ImportContents();
+
+ if ( isset( $value['namespace'] ) ) {
+ $importContents->setNamespace(
+ defined( $value['namespace'] ) ? constant( $value['namespace'] ) : 0
+ );
+ }
+
+ if ( isset( $value['page'] ) ) {
+ $importContents->setName( $value['page'] );
+ }
+
+ if ( isset( $value['description'] ) ) {
+ $importContents->setDescription( $value['description'] );
+ } elseif ( isset( $fileContents['description'] ) ) {
+ $importContents->setDescription( $fileContents['description'] );
+ } else {
+ $importContents->setDescription( 'No description' );
+ }
+
+ if ( isset( $fileContents['meta']['version'] ) ) {
+ $importContents->setVersion( $fileContents['meta']['version'] );
+ } else {
+ $importContents->setVersion( 0 );
+ }
+
+ if ( isset( $value['contents']['type'] ) && $value['contents']['type'] === 'xml' ) {
+ $contents[] = $this->newImportContents( $importContents, $fileDir, $value );
+ } else {
+ $contents[] = $this->newImportContents( $importContents, $fileDir, $value );
+ }
+ }
+
+ return $contents;
+ }
+
+ private function newImportContents( $importContents, $fileDir, $value ) {
+
+ $importContents->setContentType( ImportContents::CONTENT_TEXT );
+
+ if ( !isset( $value['contents'] ) || $value['contents'] === '' ) {
+ $importContents->addError( 'Missing, or has empty contents section' );
+ } else {
+ $this->setContents( $importContents, $fileDir, $value['contents'] );
+ }
+
+ if ( isset( $value['options'] ) ) {
+ $importContents->setOptions( $value['options'] );
+ }
+
+ return $importContents;
+ }
+
+ private function setContents( $importContents, $fileDir, $contents ) {
+
+ if ( !is_array( $contents ) || !isset( $contents['importFrom'] ) ) {
+ return $importContents->setContents( $contents );
+ }
+
+ $file = $this->normalizeFile( $fileDir, $contents['importFrom'] );
+
+ if ( !is_readable( $file ) ) {
+ return $importContents->addError( "File: " . $file . " wasn't accessible" );
+ }
+
+ $extension = pathinfo( $file, PATHINFO_EXTENSION );
+
+ if ( isset( $contents['type'] ) && $contents['type'] === 'xml' && $extension !== 'xml' ) {
+ return $importContents->addError( "XML: " . $file . " is not recognized as xml file extension" );
+ }
+
+ if ( $extension === 'xml' ) {
+ $importContents->setContentType( ImportContents::CONTENT_XML );
+ }
+
+ $importContents->setContentsFile( $file );
+ }
+
+ private function normalizeFile( $fileDir, $file ) {
+ return str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $fileDir . ( $file{0} === '/' ? '' : '/' ) . $file );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/ImportContents.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ImportContents.php
new file mode 100644
index 00000000..bf905e0f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/ImportContents.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace SMW\Importer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImportContents {
+
+ const CONTENT_TEXT = 'content.text';
+ const CONTENT_XML = 'content.xml';
+
+ /**
+ * @var string
+ */
+ private $version = '';
+
+ /**
+ * @var string
+ */
+ private $description = '';
+
+ /**
+ * @var string
+ */
+ private $name = '';
+
+ /**
+ * @var integer
+ */
+ private $namespace = 0;
+
+ /**
+ * @var string
+ */
+ private $contents = '';
+
+ /**
+ * @var string
+ */
+ private $contentsFile = '';
+
+ /**
+ * @var string
+ */
+ private $contentType = self::CONTENT_TEXT;
+
+ /**
+ * @var string
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param string $version
+ */
+ public function setVersion( $version ) {
+ $this->version = intval( $version );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return $this->version;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $description
+ */
+ public function setDescription( $description ) {
+ $this->description = $description;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $contentType
+ */
+ public function setContentType( $contentType ) {
+ $this->contentType = $contentType;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getContentType() {
+ return $this->contentType;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $name
+ */
+ public function setName( $name ) {
+ $this->name = $name;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $namespace
+ */
+ public function setNamespace( $namespace ) {
+ $this->namespace = $namespace;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getNamespace() {
+ return $this->namespace;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $contentsFile
+ */
+ public function setContentsFile( $contentsFile ) {
+ $this->contentsFile = $contentsFile;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getContentsFile() {
+ return $this->contentsFile;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $contents
+ */
+ public function setContents( $contents ) {
+ $this->contents = $contents;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getContents() {
+ return $this->contents;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $error
+ */
+ public function addError( $error ) {
+ $this->errors[] = $error;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $options
+ */
+ public function setOptions( $options ) {
+ $this->options = (array)$options;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getOptions() {
+ return $this->options;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key ) {
+ return isset( $this->options[$key] ) ? $this->options[$key] : false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/Importer.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/Importer.php
new file mode 100644
index 00000000..a799232c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/Importer.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Importer;
+
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterAware;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Importer implements MessageReporterAware {
+
+ /**
+ * @var ContentIterator
+ */
+ private $contentIterator;
+
+ /**
+ * @var ContentCreator
+ */
+ private $contentCreator;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var boolean
+ */
+ private $isEnabled = true;
+
+ /**
+ * @var integer|boolean
+ */
+ private $reqVersion = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param ContentIterator $contentIterator
+ * @param ContentCreator $contentCreator
+ */
+ public function __construct( ContentIterator $contentIterator, ContentCreator $contentCreator ) {
+ $this->contentIterator = $contentIterator;
+ $this->contentCreator = $contentCreator;
+ }
+
+ /**
+ * @see MessageReporterAware::setMessageReporter
+ *
+ * @since 2.5
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isEnabled
+ */
+ public function isEnabled( $isEnabled ) {
+ $this->isEnabled = $isEnabled;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|boolean $reqVersion
+ */
+ public function setReqVersion( $reqVersion ) {
+ $this->reqVersion = $reqVersion;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function doImport() {
+
+ if ( $this->isEnabled === false ) {
+ return $this->messageReporter->reportMessage( "\nSkipping the import process.\n" );
+ }
+
+ if ( $this->reqVersion === false ) {
+ return $this->messageReporter->reportMessage( "\nImport support not enabled, processing completed.\n" );
+ }
+
+ $import = false;
+
+ foreach ( $this->contentIterator as $key => $importContents ) {
+ $this->messageReporter->reportMessage( "\nImport of $key ...\n" );
+
+ foreach ( $importContents as $impContents ) {
+
+ if ( $impContents->getVersion() !== $this->reqVersion ) {
+ $this->messageReporter->reportMessage( " ... version mismatch, abort import for $key\n" );
+ break;
+ }
+
+ $this->doImportContents( $impContents );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ $import = true;
+ }
+
+ if ( $this->contentIterator->getErrors() !== [] ) {
+ $this->messageReporter->reportMessage(
+ "\n" . 'Import failed on "' . implode( ", ", $this->contentIterator->getErrors() ) . '"'
+ );
+ }
+
+ if ( $import ) {
+ $this->messageReporter->reportMessage( "\nImport processing completed.\n" );
+ }
+ }
+
+ private function doImportContents( ImportContents $importContents ) {
+
+ $indent = ' ...';
+
+ if ( $importContents->getErrors() === [] ) {
+ $this->contentCreator->setMessageReporter( $this->messageReporter );
+ $this->contentCreator->create( $importContents );
+ }
+
+ foreach ( $importContents->getErrors() as $error ) {
+ $this->messageReporter->reportMessage( "$indent " . $error . " ...\n" );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonContentIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonContentIterator.php
new file mode 100644
index 00000000..0fee90aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonContentIterator.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Importer;
+
+use ArrayIterator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonContentIterator implements ContentIterator {
+
+ /**
+ * @var JsonImportContentsFileDirReader
+ */
+ private $jsonImportContentsFileDirReader;
+
+ /**
+ * @var string
+ */
+ private $description = '';
+
+ /**
+ * @since 2.5
+ *
+ * @param JsonImportContentsFileDirReader $jsonImportContentsFileDirReader
+ */
+ public function __construct( JsonImportContentsFileDirReader $jsonImportContentsFileDirReader ) {
+ $this->jsonImportContentsFileDirReader = $jsonImportContentsFileDirReader;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $description
+ */
+ public function setDescription( $description ) {
+ $this->description = $description;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->jsonImportContentsFileDirReader->getErrors();
+ }
+
+ /**
+ * @see IteratorAggregate::getIterator
+ *
+ * @since 2.5
+ *
+ * @return Iterator
+ */
+ public function getIterator() {
+ return new ArrayIterator( $this->jsonImportContentsFileDirReader->getContentList() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonImportContentsFileDirReader.php b/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonImportContentsFileDirReader.php
new file mode 100644
index 00000000..b0161969
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/JsonImportContentsFileDirReader.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Importer;
+
+use RuntimeException;
+use SMW\Utils\ErrorCodeFormatter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonImportContentsFileDirReader {
+
+ /**
+ * @var ContentModeller
+ */
+ private $contentModeller;
+
+ /**
+ * @var array
+ */
+ private static $contents = [];
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var []
+ */
+ private $importFileDirs = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param ContentModeller $contentModeller
+ * @param array $importFileDirs
+ */
+ public function __construct( ContentModeller $contentModeller, $importFileDirs = [] ) {
+ $this->contentModeller = $contentModeller;
+ $this->importFileDirs = $importFileDirs;
+
+ if ( $this->importFileDirs === [] ) {
+ $this->importFileDirs = $GLOBALS['smwgImportFileDirss'];
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ImportContents[]
+ */
+ public function getContentList() {
+
+ $contents = [];
+
+ foreach ( $this->importFileDirs as $importFileDir ) {
+
+ try{
+ $files = $this->getFilesFromLocation( $this->normalize( $importFileDir ), 'json' );
+ } catch( RuntimeException $e ) {
+ $this->errors[] = $importFileDir . ' is not accessible.';
+ $files = [];
+ }
+
+ foreach ( $files as $file => $path ) {
+
+ $contentList = $this->contentModeller->makeContentList(
+ $importFileDir,
+ $this->readJSONFile( $path )
+ );
+
+ if ( $contentList === [] ) {
+ continue;
+ }
+
+ $contents[$file] = $contentList;
+ }
+ }
+
+ return $contents;
+ }
+
+ private function readJSONFile( $file ) {
+
+ $contents = json_decode(
+ file_get_contents( $file ),
+ true
+ );
+
+ if ( $contents !== null && json_last_error() === JSON_ERROR_NONE ) {
+ return $contents;
+ }
+
+ throw new RuntimeException( ErrorCodeFormatter::getMessageFromJsonErrorCode( json_last_error() ) );
+ }
+
+ private function normalize( $importFileDir ) {
+
+ if ( $importFileDir === '' ) {
+ return '';
+ }
+
+ $path = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $importFileDir );
+
+ if ( is_readable( $path ) ) {
+ return $path;
+ }
+
+ throw new RuntimeException( "Expected an accessible {$path} path" );
+ }
+
+ private function getFilesFromLocation( $path, $extension ) {
+
+ if ( $path === '' ) {
+ return [];
+ }
+
+ $files = [];
+
+ $directoryIterator = new \RecursiveDirectoryIterator( $path );
+
+ foreach ( new \RecursiveIteratorIterator( $directoryIterator ) as $fileInfo ) {
+ if ( strtolower( substr( $fileInfo->getFilename(), -( strlen( $extension ) + 1 ) ) ) === ( '.' . $extension ) ) {
+ $files[$fileInfo->getFilename()] = $fileInfo->getPathname();
+ }
+ }
+
+ return $files;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Importer/README.md b/www/wiki/extensions/SemanticMediaWiki/src/Importer/README.md
new file mode 100644
index 00000000..55db3c03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Importer/README.md
@@ -0,0 +1,151 @@
+The objective of the `Importer` is to provide a simple mechanism for deploying data structures and support information in a loose yet structured form during the installation (setup) process.
+
+## Import definitions
+
+[`$smwgImportFileDirs`](https://www.semantic-mediawiki.org/wiki/Help:$smwgImportFileDirs) defines import directories from where content can be imported.
+
+Import definitions are defined using a `JSON` format which provides the structural means and is considered easily extendable by end-users.
+
+The import files are sorted and therefore sequentially processed based on the file name. In case where content relies on other content an appropriate naming convention should be followed to ensure required definitions are imported in the expected order.
+
+### Default definitions
+
+Preselected import content is defined in the "default.json" file and includes:
+
+* "Smw import skos"
+* "Smw import owl"
+* "Smw import foaf"
+* "Foaf:knows"
+* "Foaf:name" and
+* "Foaf:homepage"
+
+It should be noted that `default.json` is __not__ expected to be the __authority source__ of content for a wiki and is the reason why the option `canReplace` is set `false` so that pre-existing content with the same name and namespace is not replaced.
+
+### Custom definitions
+
+It is possible to define one or more custom import definitions using [`$smwgImportFileDirs`](https://www.semantic-mediawiki.org/wiki/Help:$smwgImportFileDirs) with a custom location (directory) from where import definitions can be loaded.
+
+<pre>
+$GLOBALS['smwgImportFileDirs']['movie-actor-vocab'] = __DIR__ . '/import/movie-actor';
+</pre>
+
+<pre>
+$GLOBALS['smwgImportFileDirs']['custom-vocab'] = __DIR__ . '/custom';
+</pre>
+
+### Fields
+
+`JSON` schema and fields:
+
+- `description` short description about the purpose of the import (used in the auto summary)
+- `page` the name of a page without a namespace prefix
+- `namespace` literal constant of the namespace of the content (e.g. `NS_MAIN`, `SMW_NS_PROPERTY` ... )
+- `contents` it contains either the raw text or a parameter
+ - `importFrom` link to a file from where the raw text (contains a relative path to the `$smwgImportFileDirs`)
+- `options`
+ - `canReplace` to indicate whether content is being allowed to be replaced during
+ an import or not
+
+The [`$smwgImportReqVersion`](https://www.semantic-mediawiki.org/wiki/Help:$smwgImportReqVersion) stipulates
+the required version for an import and only definitions that match that version are permitted to be imported.
+
+### Examples
+
+#### XML import
+
+It is possible to use MediaWiki's XML format as import source when linked from the
+`importFrom` field (any non MediaWiki XML format will be ignored).
+
+The location for the mentioned `custom.xml` is relative to the selected `$smwgImportFileDirs` directory.
+
+<pre>
+{
+ "description": "Custom import",
+ "import": [
+ {
+ "description" : "Import of custom.xml that contains ...",
+ "contents": {
+ "importFrom": "/xml/custom.xml"
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+}
+</pre>
+
+<pre>
+{
+ "description": "Template import",
+ "import": [
+ {
+ "description" : "Template to ...",
+ "page": "Template_1",
+ "namespace": "NS_TEMPLATE",
+ "contents": "<includeonly>{{{1}}}, {{{2}}}</includeonly>",
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "description" : "Template with ...",
+ "page": "Template_2",
+ "namespace": "NS_TEMPLATE",
+ "contents": {
+ "importFrom": "/templates/template-1.tmpl"
+ },
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+}
+</pre>
+
+## Import process
+
+During the setup process, the `Installer` will automatically run and inform
+about the process which will output something similar to:
+
+<pre>
+Import of default.json ...
+ ... replacing MediaWiki:Smw import foaf contents ...
+ ... skipping Property:Foaf:knows, already exists ...
+
+Import processing completed.
+</pre>
+
+If not otherwise specified, content (a.k.a. pages) that pre-exists are going to be skipped by default.
+
+## Technical notes
+
+<pre>
+SMW\Importer
+│ └─ ContentCreators
+│ ├─ DispatchingContentCreator
+│ ├─ XmlContentCreator
+│ └─ TextContentCreator
+│
+├─ ImporterServiceFactory # access to import services
+├─ ContentIterator
+├─ ContentCreator
+├─ JsonContentIterator
+├─ JsonImportContentsFileDirReader
+└─ ContentModeller
+</pre>
+
+- `SMW::SQLStore::Installer::AfterCreateTablesComplete` provides the hook and is the event to execute the import during the setup
+- `ImporterServiceFactory` access to import services
+- `Importer` is responsible for importing contents provided by a `ContentIterator`
+- `ContentIterator` an interface to provide access to individual `ImportContents` instances
+- `JsonContentIterator` implements the `ContentIterator` interface
+- `JsonImportContentsFileDirReader` provides contents of all recursively fetched files from a location (e.g[`$smwgImportFileDirs`](https://www.semantic-mediawiki.org/wiki/Help:$smwgImportFileDirs) setting ) that meets the requirements
+- `ContentModeller` interprets the `JSON` definition and returns a set of `ImportContents` instances
+- `ContentCreator` an interface to specify different creation methods (e.g. text, XML etc.)
+- `DispatchingContentCreator` dispatches to the actual content creation instance based on `ImportContents::getContentType`
+- `XmlContentCreator` support the creation of MediaWiki XML specific content
+- `TextContentCreator` support for raw wikitext
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/InMemoryPoolCache.php b/www/wiki/extensions/SemanticMediaWiki/src/InMemoryPoolCache.php
new file mode 100644
index 00000000..a53b7b98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/InMemoryPoolCache.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace SMW;
+
+use SMW\Utils\StatsFormatter;
+
+/**
+ * A multipurpose non-persistent static pool cache to keep selected items for
+ * the duration of a request cacheable.
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class InMemoryPoolCache {
+
+ /**
+ * Stats as plain string
+ */
+ const FORMAT_PLAIN = StatsFormatter::FORMAT_PLAIN;
+
+ /**
+ * Stats as JSON output
+ */
+ const FORMAT_JSON = StatsFormatter::FORMAT_JSON;
+
+ /**
+ * Stats as HTML list output
+ */
+ const FORMAT_HTML = StatsFormatter::FORMAT_HTML;
+
+ /**
+ * @var InMemoryPoolCache
+ */
+ private static $instance = null;
+
+ /**
+ * @var CacheFactory
+ */
+ private $cacheFactory = null;
+
+ /**
+ * @var array
+ */
+ private $poolCacheList = [];
+
+ /**
+ * @since 2.3
+ *
+ * @param CacheFactory $cacheFactory
+ */
+ public function __construct( CacheFactory $cacheFactory ) {
+ $this->cacheFactory = $cacheFactory;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return InMemoryPoolCache
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self( ApplicationFactory::getInstance()->newCacheFactory() );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $poolCacheName
+ */
+ public function resetPoolCacheById( $poolCacheName = '' ) {
+ foreach ( $this->poolCacheList as $key => $value ) {
+ if ( $key === $poolCacheName || $poolCacheName === '' ) {
+ unset( $this->poolCacheList[$key] );
+ }
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|null $format
+ *
+ * @return string|array
+ */
+ public function getStats( $format = null ) {
+ return StatsFormatter::format( $this->computeStats(), $format );
+ }
+
+ /**
+ * @deprecated since 2.5, use InMemoryPoolCache::getPoolCacheById
+ * @since 2.3
+ *
+ * @param string $poolCacheName
+ * @param integer $cacheSize
+ *
+ * @return Cache
+ */
+ public function getPoolCacheFor( $poolCacheName, $cacheSize = 500 ) {
+ return $this->getPoolCacheById( $poolCacheName, $cacheSize );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $poolCacheId
+ * @param integer $cacheSize
+ *
+ * @return Cache
+ */
+ public function getPoolCacheById( $poolCacheId, $cacheSize = 500 ) {
+
+ if ( !isset( $this->poolCacheList[$poolCacheId] ) ) {
+ $this->poolCacheList[$poolCacheId] = $this->cacheFactory->newFixedInMemoryCache( $cacheSize );
+ }
+
+ return $this->poolCacheList[$poolCacheId];
+ }
+
+ private function computeStats() {
+
+ ksort( $this->poolCacheList );
+ $stats = [];
+
+ foreach ( $this->poolCacheList as $key => $value ) {
+ $stats[$key] = [];
+
+ $hits = 0;
+ $misses = 0;
+
+ foreach ( $value->getStats() as $k => $v ) {
+ $stats[$key][$k] = $v;
+
+ if ( $k === 'hits' ) {
+ $hits = $v;
+ }
+
+ if ( $k === 'inserts' ) {
+ $misses = $v;
+ }
+
+ if ( $k === 'misses' && $v > 0 ) {
+ $misses = $v;
+ }
+ }
+
+ $hitRatio = $hits > 0 ? round( $hits / ( $hits + $misses ), 4 ) : 0;
+
+ $stats[$key]['hit ratio'] = $hitRatio;
+ $stats[$key]['miss ratio'] = $hitRatio > 0 ? round( 1 - $hitRatio, 4 ) : 0;
+ }
+
+ return $stats;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/IteratorFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/IteratorFactory.php
new file mode 100644
index 00000000..6331352e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/IteratorFactory.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW;
+
+use SMW\Iterators\AppendIterator;
+use SMW\Iterators\ChunkedIterator;
+use SMW\Iterators\CsvFileIterator;
+use SMW\Iterators\MappingIterator;
+use SMW\Iterators\ResultIterator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class IteratorFactory {
+
+ /**
+ * @since 2.5
+ *
+ * @param ResultWrapper|Iterator|array $res
+ *
+ * @return ResultIterator
+ */
+ public function newResultIterator( $res ) {
+ return new ResultIterator( $res );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Iterator/array $iterable
+ * @param callable $callback
+ *
+ * @return MappingIterator
+ */
+ public function newMappingIterator( $iterable, callable $callback ) {
+ return new MappingIterator( $iterable, $callback );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Iterator/array $$iterable
+ * @param integer $chunkSize
+ *
+ * @return ChunkedIterator
+ */
+ public function newChunkedIterator( $iterable, $chunkSize = 500 ) {
+ return new ChunkedIterator( $iterable, $chunkSize );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return AppendIterator
+ */
+ public function newAppendIterator() {
+ return new AppendIterator();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ * @param boolean $parseHeader
+ * @param string $delimiter
+ * @param integer $length
+ *
+ * @return CsvFileIterator
+ */
+ public function newCsvFileIterator( $file, $parseHeader = false, $delimiter = "\t", $length = 8000 ) {
+ return new CsvFileIterator( $file, $parseHeader, $delimiter, $length );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Iterators/AppendIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/AppendIterator.php
new file mode 100644
index 00000000..1fcf6cda
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/AppendIterator.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Iterators;
+
+use ArrayIterator;
+use Countable;
+use Iterator;
+use RuntimeException;
+use Traversable;
+
+/**
+ * @see Guzzle::AppendIterator
+ * @see https://bugs.php.net/bug.php?id=49104
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ */
+class AppendIterator extends \AppendIterator implements Countable {
+
+ /**
+ * @var integer
+ */
+ private $count = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param Traversable|array $iterator
+ */
+ public function add( $iterable ) {
+
+ if ( is_array( $iterable ) ) {
+ $iterable = new ArrayIterator( $iterable );
+ }
+
+ if ( !$iterable instanceof Traversable ) {
+ throw new RuntimeException( "AppendIterator expected an Traversable" );
+ }
+
+ $this->append( $iterable );
+ }
+
+ /**
+ * @see Countable::count
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function count() {
+ return $this->count;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function append( Iterator $iterable ) {
+
+ if ( $iterable instanceof Countable ) {
+ $this->count += $iterable->count();
+ }
+
+ $this->getArrayIterator()->append( $iterable );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ChunkedIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ChunkedIterator.php
new file mode 100644
index 00000000..1edc5748
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ChunkedIterator.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Iterators;
+
+use ArrayIterator;
+use InvalidArgumentException;
+use IteratorIterator;
+use RuntimeException;
+use Traversable;
+
+/**
+ * @see Guzzle::ChunkedIterator
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ */
+class ChunkedIterator extends IteratorIterator {
+
+ /**
+ * @var integer
+ */
+ private $chunkSize = 0;
+
+ /**
+ * @var array
+ */
+ private $chunk;
+
+ /**
+ * @since 3.0
+ *
+ * @param Traversable|array $iterator
+ * @param integer $chunkSize
+ */
+ public function __construct( $iterable, $chunkSize = 500 ) {
+
+ $chunkSize = (int)$chunkSize;
+
+ if ( is_array( $iterable ) ) {
+ $iterable = new ArrayIterator( $iterable );
+ }
+
+ if ( !$iterable instanceof Traversable ) {
+ throw new RuntimeException( "ChunkedIterator expected an Traversable" );
+ }
+
+ if ( $chunkSize < 0 ) {
+ throw new InvalidArgumentException( "$chunkSize is lower than 0" );
+ }
+
+ parent::__construct( $iterable );
+ $this->chunkSize = $chunkSize;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function rewind() {
+ parent::rewind();
+ $this->next();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function next() {
+ $this->chunk = [];
+
+ for ( $i = 0; $i < $this->chunkSize && parent::valid(); $i++ ) {
+ $this->chunk[] = parent::current();
+ parent::next();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function current() {
+ return $this->chunk;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function valid() {
+ return (bool) $this->chunk;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Iterators/CsvFileIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/CsvFileIterator.php
new file mode 100644
index 00000000..fb8581a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/CsvFileIterator.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace SMW\Iterators;
+
+use Exception;
+use Iterator;
+use SMW\Exception\FileNotFoundException;
+
+/**
+ * @see http://php.net/manual/en/function.fgetcsv.php
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ */
+class CsvFileIterator implements Iterator {
+
+ /**
+ * @var Resource
+ */
+ private $handle;
+
+ /**
+ * @var boolean
+ */
+ private $parseHeader;
+
+ /**
+ * @var []
+ */
+ private $header = [];
+
+ /**
+ * @var string
+ */
+ private $delimiter;
+
+ /**
+ * @var integer
+ */
+ private $length;
+
+ /**
+ * @var int
+ */
+ private $key = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ * @param boolean $parseHeader
+ * @param string $delimiter
+ * @param integer $length
+ */
+ public function __construct( $file, $parseHeader = false, $delimiter = ",", $length = 8000 ) {
+
+ try {
+ $this->handle = fopen( $file, "r" );
+ } catch ( Exception $e ) {
+ throw new FileNotFoundException( 'File "'. $file . '" is not accessible.' );
+ }
+
+ $this->parseHeader = $parseHeader;
+ $this->delimiter = $delimiter;
+ $this->length = $length;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function __destruct() {
+ if( is_resource( $this->handle ) ) {
+ fclose( $this->handle );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getHeader() {
+ return $this->header;
+ }
+
+ /**
+ * Resets the file handle
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function rewind() {
+ $this->key = 0;
+ rewind( $this->handle );
+ }
+
+ /**
+ * Returns the current CSV row as a 2 dimensional array
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function current() {
+
+ // First iteration to match the header
+ if ( $this->parseHeader && $this->key == 0 ) {
+ $this->header = fgetcsv( $this->handle, $this->length, $this->delimiter );
+ }
+
+ $currentElement = fgetcsv( $this->handle, $this->length, $this->delimiter );
+ $this->key++;
+
+ return $currentElement;
+ }
+
+ /**
+ * Returns the current row number.
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function key() {
+ return $this->key;
+ }
+
+ /**
+ * Checks if the end of file is reached.
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function next() {
+ return !feof( $this->handle );
+ }
+
+ /**
+ * Checks if the next row is a valid row.
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function valid() {
+
+ if ( $this->next() ) {
+ return true;
+ }
+
+ fclose( $this->handle );
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Iterators/MappingIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/MappingIterator.php
new file mode 100644
index 00000000..0aa39888
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/MappingIterator.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Iterators;
+
+use ArrayIterator;
+use Countable;
+use Iterator;
+use IteratorIterator;
+use RuntimeException;
+
+/**
+ * This iterator is expected to be called in combination with another iterator
+ * (or traversable/array) in order to apply a mapping on the returned current element
+ * during an iterative (foreach etc.) process.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MappingIterator extends IteratorIterator implements Countable {
+
+ /**
+ * @var callable
+ */
+ private $callback;
+
+ /**
+ * @var integer
+ */
+ private $count = 1;
+
+ /**
+ * @since 2.5
+ *
+ * @param Iterator|array $iterable
+ * @param callable $callback
+ */
+ public function __construct( $iterable, callable $callback ) {
+
+ if ( is_array( $iterable ) ) {
+ $iterable = new ArrayIterator( $iterable );
+ }
+
+ if ( !$iterable instanceof Iterator ) {
+ throw new RuntimeException( "MappingIterator expected an Iterator" );
+ }
+
+ if ( $iterable instanceof Countable ) {
+ $this->count = $iterable->count();
+ }
+
+ parent::__construct( $iterable );
+ $this->callback = $callback;
+ }
+
+ /**
+ * @see Countable::count
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function count() {
+ return $this->count;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function current() {
+ return call_user_func( $this->callback, parent::current() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ResultIterator.php b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ResultIterator.php
new file mode 100644
index 00000000..31a6b9e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Iterators/ResultIterator.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Iterators;
+
+use ArrayIterator;
+use Countable;
+use Iterator;
+use ResultWrapper;
+use RuntimeException;
+use SeekableIterator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ResultIterator implements Iterator, Countable, SeekableIterator {
+
+ /**
+ * @var ResultWrapper
+ */
+ public $res;
+
+ /**
+ * @var integer
+ */
+ public $position;
+
+ /**
+ * @var mixed
+ */
+ public $current;
+
+ /**
+ * @var boolean
+ */
+ public $numRows = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param Iterator|array $res
+ */
+ public function __construct( $res ) {
+
+ if ( !$res instanceof Iterator && !is_array( $res ) ) {
+ throw new RuntimeException( "Expected an Iterator or array!" );
+ }
+
+ // @see MediaWiki's ResultWrapper
+ if ( $res instanceof Iterator && method_exists( $res , 'numRows' ) ) {
+ $this->numRows = true;
+ }
+
+ if ( is_array( $res ) ) {
+ $res = new ArrayIterator( $res );
+ }
+
+ $this->res = $res;
+ $this->position = 0;
+ $this->setCurrent( $this->res->current() );
+ }
+
+ /**
+ * @see Countable::count
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function count() {
+ return $this->numRows ? $this->res->numRows() : $this->res->count();
+ }
+
+ /**
+ * @see SeekableIterator::seek
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function seek( $position ) {
+ $this->res->seek( $position );
+ $this->setCurrent( $this->res->current() );
+ $this->position = $position;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function current() {
+ return $this->current;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function key() {
+ return $this->position;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function next() {
+ $row = $this->res->next();
+ $this->setCurrent( $row );
+ $this->position++;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function rewind() {
+ $this->res->rewind();
+ $this->position = 0;
+ $this->setCurrent( $this->res->current() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function valid() {
+ return $this->current !== false;
+ }
+
+ protected function setCurrent( $row ) {
+ if ( $row === false || $row === null ) {
+ $this->current = false;
+ } else {
+ $this->current = $row;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Lang/FallbackFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/Lang/FallbackFinder.php
new file mode 100644
index 00000000..4931ebb7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Lang/FallbackFinder.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\Lang;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FallbackFinder {
+
+ /**
+ * @var JsonContentsFileReader
+ */
+ private $jsonContentsFileReader;
+
+ /**
+ * @var string
+ */
+ private $canonicalFallbackLanguageCode = 'en';
+
+ /**
+ * @var array
+ */
+ private $fallbackLanguages = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param JsonContentsFileReader $jsonContentsFileReader
+ */
+ public function __construct( JsonContentsFileReader $jsonContentsFileReader ) {
+ $this->jsonContentsFileReader = $jsonContentsFileReader;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function emptyByLanguageCode( $languageCode ) {
+ unset( $this->fallbackLanguages[strtolower( trim( $languageCode ) )] );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getCanonicalFallbackLanguageCode() {
+ return $this->canonicalFallbackLanguageCode;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getFallbackLanguageBy( $languageCode = '' ) {
+
+ $languageCode = strtolower( trim( $languageCode ) );
+
+ if ( isset( $this->fallbackLanguages[$languageCode] ) ) {
+ return $this->fallbackLanguages[$languageCode];
+ }
+
+ $index = 'fallback_language';
+
+ // Unknown, use the default
+ if ( $languageCode === '' ) {
+ return $this->canonicalFallbackLanguageCode;
+ }
+
+ try {
+ $contents = $this->jsonContentsFileReader->readByLanguageCode( $languageCode );
+ } catch ( RuntimeException $e ) {
+ $this->fallbackLanguages[$languageCode] = $this->canonicalFallbackLanguageCode;
+ }
+
+ // Get customized fallbackLanguage
+ if ( isset( $contents[$index] ) ) {
+ $this->fallbackLanguages[$languageCode] = $contents[$index];
+ }
+
+ // The ultimate defense line, fallback was not set, or is false or empty
+ // which means use the canonicalFallbackLanguageCode
+ if (
+ !isset( $contents[$index] ) ||
+ $this->fallbackLanguages[$languageCode] === false ||
+ $this->fallbackLanguages[$languageCode] === '' ) {
+ $this->fallbackLanguages[$languageCode] = $this->canonicalFallbackLanguageCode;
+ }
+
+ return $this->fallbackLanguages[$languageCode];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Lang/JsonContentsFileReader.php b/www/wiki/extensions/SemanticMediaWiki/src/Lang/JsonContentsFileReader.php
new file mode 100644
index 00000000..18755f60
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Lang/JsonContentsFileReader.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\Lang;
+
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use RuntimeException;
+use SMW\Utils\ErrorCodeFormatter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonContentsFileReader {
+
+ /**
+ * @var array
+ */
+ private static $contents = [];
+
+ /**
+ * @var string
+ */
+ private $languageFileDir = '';
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var boolean
+ */
+ private $skipCache = false;
+
+ /**
+ * @var integer
+ */
+ private $ttl = 604800; // 7 * 24 * 3600
+
+ /**
+ * @since 2.5
+ *
+ * @param Cache|null $cache
+ * @param string $languageFileDir
+ */
+ public function __construct( Cache $cache = null, $languageFileDir = '' ) {
+ $this->cache = $cache;
+ $this->languageFileDir = $languageFileDir;
+
+ if ( $this->cache === null ) {
+ $this->cache = new NullCache();
+ }
+
+ if ( $this->languageFileDir === '' ) {
+ $this->languageFileDir = $GLOBALS['smwgExtraneousLanguageFileDir'];
+ }
+ }
+
+ /**
+ * @since 2.5
+ */
+ public static function clear() {
+ self::$contents = [];
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function skipCache() {
+ $this->skipCache = true;
+ }
+
+ /**
+ * @since 1.2.0
+ *
+ * @return integer
+ */
+ public function getFileModificationTime( $languageCode ) {
+ return filemtime( $this->getLanguageFile( $languageCode ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ * @param boolean $readFromFile
+ *
+ * @return boolean
+ */
+ public function canReadByLanguageCode( $languageCode ) {
+
+ $canReadByLanguageCode = '';
+
+ try {
+ $canReadByLanguageCode = $this->getLanguageFile( $languageCode );
+ } catch ( \Exception $e ) {
+ $canReadByLanguageCode = '';
+ }
+
+ return $canReadByLanguageCode !== '';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ * @param array $contents
+ */
+ public function writeByLanguageCode( $languageCode, $contents ) {
+
+ $languageCode = strtolower( trim( $languageCode ) );
+
+ file_put_contents(
+ $this->getLanguageFile( $languageCode ),
+ json_encode( $contents, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ * @param boolean $readFromFile
+ *
+ * @return array
+ * @throws RuntimeException
+ */
+ public function readByLanguageCode( $languageCode, $readFromFile = false ) {
+
+ $languageCode = strtolower( trim( $languageCode ) );
+
+ if ( !$readFromFile && isset( self::$contents[$languageCode] ) ) {
+ return self::$contents[$languageCode];
+ }
+
+ $cacheKey = smwfCacheKey(
+ 'smw:lang',
+ [
+ $languageCode,
+ $this->getFileModificationTime( $languageCode ),
+ $this->ttl
+ ]
+ );
+
+ if ( !$readFromFile && !$this->skipCache && !isset( self::$contents[$languageCode] ) && $this->cache->contains( $cacheKey ) ) {
+ self::$contents[$languageCode] = $this->cache->fetch( $cacheKey );
+ }
+
+ if ( $readFromFile || !isset( self::$contents[$languageCode] ) ) {
+ self::$contents[$languageCode] = $this->readJSONFile( $languageCode, $cacheKey );
+ }
+
+ return self::$contents[$languageCode];
+ }
+
+ protected function readJSONFile( $languageCode, $cacheKey ) {
+
+ $contents = json_decode(
+ file_get_contents( $this->getLanguageFile( $languageCode ) ),
+ true
+ );
+
+ if ( $contents !== null && json_last_error() === JSON_ERROR_NONE ) {
+ $this->cache->save( $cacheKey, $contents, $this->ttl );
+ return $contents;
+ }
+
+ throw new RuntimeException( ErrorCodeFormatter::getMessageFromJsonErrorCode( json_last_error() ) );
+ }
+
+ private function getLanguageFile( $languageCode ) {
+
+ $file = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $this->languageFileDir . '/' . $languageCode . '.json' );
+
+ if ( is_readable( $file ) ) {
+ return $file;
+ }
+
+ throw new RuntimeException( "Expected a {$file} file" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Lang/Lang.php b/www/wiki/extensions/SemanticMediaWiki/src/Lang/Lang.php
new file mode 100644
index 00000000..c5ebb4ef
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Lang/Lang.php
@@ -0,0 +1,600 @@
+<?php
+
+namespace SMW\Lang;
+
+/**
+ * This class provides "extraneous" language functions independent from MediaWiki
+ * to handle certain language options in a way required by Semantic MediaWiki and
+ * its registration system.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class Lang {
+
+ /**
+ * @var Lang
+ */
+ private static $instance = null;
+
+ /**
+ * @var LanguageContents
+ */
+ private $languageContents;
+
+ /**
+ * @var string
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @var string
+ */
+ private $canonicalFallbackLanguageCode = 'en';
+
+ /**
+ * @var array
+ */
+ private $propertyIdByLabelMap = [];
+
+ /**
+ * @var array
+ */
+ private $dateFormatsMap = [];
+
+ /**
+ * @var array
+ */
+ private $monthMap = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param LanguageContents $languageContents
+ */
+ public function __construct( LanguageContents $languageContents ) {
+ $this->languageContents = $languageContents;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Lang
+ */
+ public static function getInstance() {
+
+ if ( self::$instance !== null ) {
+ return self::$instance;
+ }
+
+ // $cache = ApplicationFactory::getInstance()->getCache()
+
+ $jsonContentsFileReader = new JsonContentsFileReader();
+
+ self::$instance = new self(
+ new LanguageContents(
+ $jsonContentsFileReader,
+ new FallbackFinder( $jsonContentsFileReader )
+ )
+ );
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getCode() {
+ return $this->languageCode;
+ }
+
+ /**
+ * @deprecated since 3.0, use Lang::fetch
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function fetchByLanguageCode( $languageCode ) {
+ return $this->fetch( $languageCode );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function fetch( $languageCode ) {
+
+ $this->languageCode = strtolower( trim( $languageCode ) );
+
+ if ( !$this->languageContents->isLoaded( $this->languageCode ) ) {
+ $this->languageContents->load( $this->languageCode );
+ }
+
+ $this->canonicalFallbackLanguageCode = $this->languageContents->getCanonicalFallbackLanguageCode();
+
+ return $this;
+ }
+
+ /**
+ * Function that returns an array of namespace identifiers.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getNamespaces() {
+
+ $namespaces = $this->languageContents->get(
+ 'namespace.labels',
+ $this->languageCode
+ );
+
+ $namespaces += $this->languageContents->get(
+ 'namespace.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ foreach ( $namespaces as $key => $value ) {
+ unset( $namespaces[$key] );
+
+ if ( defined( $key ) ) {
+ $namespaces[constant($key)] = $value;
+ }
+ }
+
+ return $namespaces;
+ }
+
+ /**
+ * Function that returns an array of namespace aliases, if any
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getNamespaceAliases() {
+
+ $namespaceAliases = $this->languageContents->get(
+ 'namespace.aliases',
+ $this->languageCode
+ );
+
+ $namespaceAliases += $this->languageContents->get(
+ 'namespace.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ foreach ( $namespaceAliases as $alias => $namespace ) {
+ if ( defined( $namespace ) ) {
+ $namespaceAliases[$alias] = constant( $namespace );
+ }
+ }
+
+ return $namespaceAliases;
+ }
+
+ /**
+ * Return all labels that are available as names for built-in datatypes. Those
+ * are the types that users can access via [[has type::...]] (more built-in
+ * types may exist for internal purposes but the user won't need to
+ * know this). The returned array is indexed by (internal) type ids.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getDatatypeLabels() {
+
+ $datatypeLabels = $this->languageContents->get(
+ 'datatype.labels',
+ $this->languageCode
+ );
+
+ $datatypeLabels += $this->languageContents->get(
+ 'datatype.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ return $datatypeLabels;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $label
+ *
+ * @return string
+ */
+ public function findDatatypeByLabel( $label ) {
+
+ $label = mb_strtolower( $label );
+
+ $datatypeLabels = $this->getDatatypeLabels();
+ $datatypeLabels = array_flip( $datatypeLabels );
+ $datatypeLabels += $this->getDatatypeAliases();
+
+ foreach ( $datatypeLabels as $key => $id ) {
+ if ( mb_strtolower( $key ) === $label ) {
+ return $id;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getCanonicalDatatypeLabels() {
+
+ $datatypeLabels = $this->languageContents->get(
+ 'datatype.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ $canonicalPropertyLabels = array_flip( $datatypeLabels );
+
+ return $canonicalPropertyLabels;
+ }
+
+ /**
+ * Return an array that maps aliases to internal type ids. All ids used here
+ * should also have a primary label defined in m_DatatypeLabels.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getDatatypeAliases() {
+
+ $datatypeAliases = $this->languageContents->get(
+ 'datatype.aliases',
+ $this->languageCode
+ );
+
+ $datatypeAliases += $this->languageContents->get(
+ 'datatype.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ return $datatypeAliases;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getCanonicalPropertyLabels() {
+
+ $canonicalPropertyLabels = $this->languageContents->get(
+ 'property.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ $canonicalPropertyLabels = array_flip( $canonicalPropertyLabels );
+
+ $canonicalPropertyLabels += $this->languageContents->get(
+ 'property.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ $canonicalPropertyLabels += $this->languageContents->get(
+ 'datatype.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ return $canonicalPropertyLabels;
+ }
+
+ /**
+ * Function that returns the labels for predefined properties.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getPropertyLabels() {
+
+ $propertyLabels = $this->languageContents->get(
+ 'property.labels',
+ $this->languageCode
+ );
+
+ $propertyLabels += $this->languageContents->get(
+ 'property.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ return $propertyLabels;
+ }
+
+ /**
+ * Aliases for predefined properties, if any.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getCanonicalPropertyAliases() {
+
+ $canonicalPropertyAliases = $this->languageContents->get(
+ 'property.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ // Add standard property lables from the canonical language as
+ // aliases
+ $propertyLabels = $this->languageContents->get(
+ 'property.labels',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ $canonicalPropertyAliases += array_flip( $propertyLabels );
+
+ return $canonicalPropertyAliases;
+ }
+
+ /**
+ * Aliases for predefined properties, if any.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getPropertyAliases() {
+
+ $propertyAliases = $this->languageContents->get(
+ 'property.aliases',
+ $this->languageCode
+ );
+
+ $propertyLabels = $this->languageContents->get(
+ 'property.labels',
+ $this->languageCode
+ );
+
+ $propertyAliases += array_flip( $propertyLabels );
+
+ return $propertyAliases;
+ }
+
+ /**
+ * @deprecated use getPropertyIdByLabel
+ */
+ protected function getPropertyId( $propertyLabel ) {
+
+ $list += $this->languageContents->get(
+ 'property.aliases',
+ $this->languageCode
+ );
+
+ $list += $this->languageContents->get(
+ 'property.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ return $list;
+ }
+
+ /**
+ * Function receives property name (for example, `Modificatino date') and
+ * returns a property id (for example, `_MDAT'). Property name may be
+ * localized one. If property name is not recognized, a null value returned.
+ *
+ * @since 2.4
+ *
+ * @return string|null
+ */
+ public function getPropertyIdByLabel( $label ) {
+
+ $this->initPropertyIdByLabelMap( $this->languageCode );
+
+ if ( isset( $this->propertyIdByLabelMap[$this->languageCode]['label'][$label] ) ) {
+ return $this->propertyIdByLabelMap[$this->languageCode]['label'][$label];
+ };
+
+ if ( isset( $this->propertyIdByLabelMap[$this->languageCode]['alias'][$label] ) ) {
+ return $this->propertyIdByLabelMap[$this->languageCode]['alias'][$label];
+ };
+
+ return null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getPropertyLabelList() {
+
+ $this->initPropertyIdByLabelMap( $this->languageCode );
+
+ if ( isset( $this->propertyIdByLabelMap[$this->languageCode] ) ) {
+ return $this->propertyIdByLabelMap[$this->languageCode];
+ }
+
+ return [];
+ }
+
+ /**
+ * Function that returns the preferred date formats
+ *
+ * Preferred interpretations for dates with 1, 2, and 3 components. There
+ * is an array for each case, and the constants define the obvious order
+ * (e.g. SMW_YDM means "first Year, then Day, then Month). Unlisted
+ * combinations will not be accepted at all.
+ *
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getDateFormats() {
+
+ $languageCode = $this->languageCode;
+
+ if ( !isset( $this->dateFormatsMap[$languageCode] ) || $this->dateFormatsMap[$languageCode] === [] ) {
+ $this->dateFormatsMap[$languageCode] = $this->getDateFormatsByLanguageCode( $languageCode );
+ }
+
+ return $this->dateFormatsMap[$languageCode];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer|null $precision
+ *
+ * @return string
+ */
+ public function getPreferredDateFormatByPrecision( $precision = null ) {
+
+ $dateOutputFormats = $this->languageContents->get(
+ 'date.precision',
+ $this->languageCode
+ );
+
+ foreach ( $dateOutputFormats as $key => $format ) {
+ if ( @constant( $key ) === $precision ) {
+ return $format;
+ }
+ }
+
+ // Fallback
+ return 'd F Y H:i:s';
+ }
+
+ /**
+ * @deprecated use findMonthNumberByLabel
+ */
+ public function findMonth( $label ) {
+ return $this->findMonthNumberByLabel( $label );
+ }
+
+ /**
+ * Function looks up a month and returns the corresponding number.
+ *
+ * @since 2.4
+ *
+ * @param string $label
+ *
+ * @return false|integer
+ */
+ public function findMonthNumberByLabel( $label ) {
+
+ $languageCode = $this->languageCode;
+
+ if ( !isset( $this->months[$languageCode] ) || $this->months[$languageCode] === [] ) {
+ $this->months[$languageCode] = $this->languageContents->get( 'date.months', $languageCode );
+ }
+
+ foreach ( $this->months[$languageCode] as $key => $value ) {
+ if ( strcasecmp( $value[0], $label ) == 0 || strcasecmp( $value[1], $label ) == 0 ) {
+ return $key + 1; // array starts with 0
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @deprecated use getMonthLabelByNumber
+ */
+ public function getMonthLabel( $number ) {
+ return $this->getMonthLabelByNumber( $number );
+ }
+
+ /**
+ * Return the name of the month with the given number.
+ *
+ * @since 2.4
+ *
+ * @param integer $number
+ *
+ * @return array
+ */
+ public function getMonthLabelByNumber( $number ) {
+
+ $languageCode = $this->languageCode;
+ $number = (int)( $number - 1 ); // array starts with 0
+
+ if ( !isset( $this->months[$languageCode] ) || $this->months[$languageCode] === [] ) {
+ $this->months[$languageCode] = $this->languageContents->get( 'date.months', $languageCode );
+ }
+
+ if ( ( ( $number >= 0 ) && ( $number <= 11 ) ) && isset( $this->months[$languageCode][$number]) ) {
+ return $this->months[$languageCode][$number][0]; // Long name
+ }
+
+ return '';
+ }
+
+ private function getDateFormatsByLanguageCode( $languageCode ) {
+
+ $dateformats = [];
+
+ foreach ( $this->languageContents->get( 'date.format', $languageCode ) as $row ) {
+ $internalNumberFormat = [];
+
+ foreach ( $row as $value ) {
+ $internalNumberFormat[] = constant( $value );
+ }
+
+ $dateformats[] = $internalNumberFormat;
+ }
+
+ return $dateformats;
+ }
+
+ private function initPropertyIdByLabelMap( $languageCode ) {
+
+ if ( isset( $this->propertyIdByLabelMap[$languageCode] ) && $this->propertyIdByLabelMap[$languageCode] !== [] ) {
+ return;
+ }
+
+ $this->propertyIdByLabelMap[$languageCode] = [];
+
+ $propertyLabels = $this->languageContents->get(
+ 'property.labels',
+ $languageCode
+ );
+
+ $propertyLabels += $this->languageContents->get(
+ 'datatype.labels',
+ $languageCode
+ );
+
+ foreach ( $propertyLabels as $id => $label ) {
+ $this->propertyIdByLabelMap[$languageCode]['label'][$label] = $id;
+ }
+
+ $propertyAliases = $this->languageContents->get(
+ 'property.aliases',
+ $languageCode
+ );
+
+ $propertyAliases += $this->languageContents->get(
+ 'property.aliases',
+ $this->canonicalFallbackLanguageCode
+ );
+
+ foreach ( $propertyAliases as $label => $id ) {
+ $this->propertyIdByLabelMap[$languageCode]['alias'][$label] = $id;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Lang/LanguageContents.php b/www/wiki/extensions/SemanticMediaWiki/src/Lang/LanguageContents.php
new file mode 100644
index 00000000..be3d4b3d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Lang/LanguageContents.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace SMW\Lang;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LanguageContents {
+
+ /**
+ * @var JsonContentsFileReader
+ */
+ private $jsonContentsFileReader;
+
+ /**
+ * @var FallbackFinder
+ */
+ private $fallbackFinder;
+
+ /**
+ * @var array
+ */
+ private $contents = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param JsonContentsFileReader $jsonContentsFileReader
+ * @param FallbackFinder $fallbackFinder
+ */
+ public function __construct( JsonContentsFileReader $jsonContentsFileReader, FallbackFinder $fallbackFinder ) {
+ $this->jsonContentsFileReader = $jsonContentsFileReader;
+ $this->fallbackFinder = $fallbackFinder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getCanonicalFallbackLanguageCode() {
+ return $this->fallbackFinder->getCanonicalFallbackLanguageCode();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ *
+ * @return boolean
+ */
+ public function isLoaded( $languageCode ) {
+ return isset( $this->contents[$languageCode] ) || array_key_exists( $languageCode, $this->contents );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $languageCode
+ *
+ * @return boolean
+ */
+ public function load( $languageCode ) {
+
+ if ( !$this->isLoaded( $languageCode ) && !$this->jsonContentsFileReader->canReadByLanguageCode( $languageCode ) ) {
+ $languageCode = $this->fallbackFinder->getFallbackLanguageBy( $languageCode );
+ }
+
+ if ( !$this->isLoaded( $languageCode ) ) {
+ $this->contents[$languageCode] = $this->jsonContentsFileReader->readByLanguageCode( $languageCode );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string $languageCode
+ *
+ * @return array|string|false
+ */
+ public function get( $id, $languageCode ) {
+ return $this->matchLanguage( $languageCode, $id );
+ }
+
+ private function matchLanguage( $languageCode, $id ) {
+
+ $canonicalFallbackLanguageCode = $this->fallbackFinder->getCanonicalFallbackLanguageCode();
+
+ if ( !isset( $this->contents[$languageCode] ) || $this->contents[$languageCode] === [] ) {
+ // In case a language has no matching file
+ try {
+ $this->contents[$languageCode] = $this->jsonContentsFileReader->readByLanguageCode( $languageCode );
+ } catch ( RuntimeException $e ) {
+ $this->contents[$languageCode] = [];
+ $languageCode = $canonicalFallbackLanguageCode;
+ }
+ }
+
+ $depth = 1;
+
+ // There is certainly a better (meaning generic) way to do this yet with
+ // only a limited depth, doing a recursive traversal will not yield an
+ // advantage
+ if ( strpos( $id, '.' ) !== false ) {
+ $keys = explode( '.', $id );
+ $depth = count( $keys );
+ }
+
+ if ( $depth == 1 && isset( $this->contents[$languageCode][$id] ) && $this->contents[$languageCode][$id] !== [] ) {
+ return $this->contents[$languageCode][$id];
+ }
+
+ if ( $depth == 2 && isset( $this->contents[$languageCode][$keys[0]][$keys[1]] ) && $this->contents[$languageCode][$keys[0]][$keys[1]] !== [] ) {
+ return $this->contents[$languageCode][$keys[0]][$keys[1]];
+ }
+
+ if ( $depth == 3 && isset( $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]] ) && $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]] !== [] ) {
+ return $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]];
+ }
+
+ if ( $languageCode !== $canonicalFallbackLanguageCode ) {
+ return $this->matchLanguage( $this->fallbackFinder->getFallbackLanguageBy( $languageCode ), $id );
+ }
+
+ return $this->matchCanonicalLanguage( $canonicalFallbackLanguageCode, $id );
+ }
+
+ private function matchCanonicalLanguage( $languageCode, $id ) {
+
+ $depth = 1;
+
+ if ( strpos( $id, '.' ) !== false ) {
+ $keys = explode( '.', $id );
+ $depth = count( $keys );
+ }
+
+ // Last resort before throwing the towel, make sure we really have
+ // something when the default FallbackLanguageCode is used
+ if ( $depth == 1 && !isset( $this->contents[$languageCode][$id] ) ) {
+ $this->contents[$languageCode] = $this->jsonContentsFileReader->readByLanguageCode( $languageCode, true );
+ }
+
+ if ( $depth == 1 && isset( $this->contents[$languageCode][$id] ) ) {
+ return $this->contents[$languageCode][$id];
+ }
+
+ if ( $depth == 2 && !isset( $this->contents[$languageCode][$keys[0]][$keys[1]] ) ) {
+ $this->contents[$languageCode] = $this->jsonContentsFileReader->readByLanguageCode( $languageCode, true );
+ }
+
+ if ( $depth == 2 && isset( $this->contents[$languageCode][$keys[0]][$keys[1]] ) ) {
+ return $this->contents[$languageCode][$keys[0]][$keys[1]];
+ }
+
+ if ( $depth == 3 && !isset( $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]] ) ) {
+ $this->contents[$languageCode] = $this->jsonContentsFileReader->readByLanguageCode( $languageCode, true );
+ }
+
+ if ( $depth == 3 && isset( $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]] ) ) {
+ return $this->contents[$languageCode][$keys[0]][$keys[1]][$keys[2]];
+ }
+
+ throw new RuntimeException( "Unknown or invalid `{$id}` id for `{$languageCode}`" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Lang/README.md b/www/wiki/extensions/SemanticMediaWiki/src/Lang/README.md
new file mode 100644
index 00000000..2a2c3cd4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Lang/README.md
@@ -0,0 +1,103 @@
+It provides "extraneous" language functions independent of MediaWiki that ate required by Semantic MediaWiki and its registration system.
+
+## JSON format
+
+The location of the content files is determined by the [`$smwgExtraneousLanguageFileDir`](https://www.semantic-mediawiki.org/wiki/Help:$smwgExtraneousLanguageFileDir) setting.
+
+### Field definitions
+
+- `fallback_language`defines a fallback language tag
+- `datatype`
+ - `labels` datatype labels
+ - `aliases` datatype aliases
+- `property`
+ - `labels` predefined property labels
+ - `aliases` predefined property aliases
+- `namespace`
+ - `labels` namespace names
+ - `aliases` namespace aliases
+- `date`
+ - `format` to a define a rule set of how to resolve preferred date formats for dates with 1, 2, and 3 components. It is defined as an array where the constants define the order of the interpretation.
+ - `SMW_MDY` Month-Day-Year
+ - `SMW_DMY` Day-Month-Year
+ - `SMW_YMD` Year-Month-Day
+ - `SMW_YDM` Year-Day-Month
+ - `SMW_MY` Month-Year
+ - `SMW_YM` Year-Month
+ - `SMW_Y` Year
+ - `SMW_YEAR` an entered digit can be a year
+ - `SMW_DAY` an entered digit can be a day
+ - `SMW_MONTH` an entered digit can be a month
+ - `SMW_DAY_MONTH_YEAR` an entered digit can be a day, month or year
+ - `SMW_DAY_YEAR` an entered digit can be either a day or a year
+ - `precision` used to define the rules of formatting for a specific precision:
+ - `SMW_PREC_Y` Year
+ - `SMW_PREC_YMD` Year, Month, and Day
+ - `SMW_PREC_YMDT` Year, Month, Day, and Time
+ - `SMW_PREC_YMDTZ` Year, Month, Day, Time and Timezone
+ - `months` twelve strings naming the months and short strings briefly naming the month
+ - `days` follows ISO-8601 numeric representation, starting with Monday together with the corresponding short name
+- `@...` fields leading with `@` are identified as comment fields
+
+### Example
+
+<pre>
+{
+ "fallback_language": false,
+ "datatype": {
+ "labels":{
+ "_wpg": "Page"
+ },
+ "aliases":{
+ "Page": "_wpg"
+ }
+ },
+ "property": {
+ "labels":{
+ "_TYPE": "Has type"
+ },
+ "aliases": {
+ "Has type": "_TYPE"
+ }
+ },
+ "namespaces": {
+ "labels":{
+ "SMW_NS_PROPERTY": "Property"
+ },
+ "aliases": {
+ "Property": "SMW_NS_PROPERTY"
+ }
+ },
+ "date":{
+ "precision": {
+ "SMW_PREC_YMDTZ": "H:i:s T, j F Y"
+ },
+ "format": [
+ [
+ "SMW_Y"
+ ]
+ ],
+ "months": [
+ [
+ "January",
+ "Jan"
+ ]
+ ]
+ "days":[
+ [
+ "Monday",
+ "Mon"
+ ]
+ ]
+ }
+}
+</pre>
+
+## Technical notes
+
+<pre>
+SMW\Lang
+├─ Lang # interface to the language functions
+├─ JsonContentsFileReader # access the contents of a `JSON` file
+└─ FallbackFinder # resolving a fallback language
+</pre>
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Localizer.php b/www/wiki/extensions/SemanticMediaWiki/src/Localizer.php
new file mode 100644
index 00000000..09be273b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Localizer.php
@@ -0,0 +1,394 @@
+<?php
+
+namespace SMW;
+
+use DateTime;
+use Language;
+use SMW\Lang\Lang;
+use SMW\MediaWiki\LocalTime;
+use Title;
+use User;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class Localizer {
+
+ /**
+ * @var Localizer
+ */
+ private static $instance = null;
+
+ /**
+ * @var Language
+ */
+ private $contentLanguage = null;
+
+ /**
+ * @since 2.1
+ *
+ * @param Language $contentLanguage
+ */
+ public function __construct( Language $contentLanguage ) {
+ $this->contentLanguage = $contentLanguage;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return Localizer
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self( $GLOBALS['wgContLang'] );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return Language
+ */
+ public function getContentLanguage() {
+ return $this->contentLanguage;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Language
+ */
+ public function getUserLanguage() {
+ return $GLOBALS['wgLang'];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param User|null $user
+ *
+ * @return boolean
+ */
+ public function hasLocalTimeOffsetPreference( $user = null ) {
+
+ if ( !$user instanceof User ) {
+ $user = $GLOBALS['wgUser'];
+ }
+
+ return $user->getOption( 'smw-prefs-general-options-time-correction' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DateTime $dateTime
+ * @param User|null $user
+ *
+ * @return DateTime
+ */
+ public function getLocalTime( DateTime $dateTime, $user = null ) {
+
+ if ( !$user instanceof User ) {
+ $user = $GLOBALS['wgUser'];
+ }
+
+ LocalTime::setLocalTimeOffset(
+ $GLOBALS['wgLocalTZoffset']
+ );
+
+ return LocalTime::getLocalizedTime( $dateTime, $user );
+ }
+
+ /**
+ * @note
+ *
+ * 1. If the page content language is available use it as preferred language
+ * (as it is clear that the page content was intended to be in a specific
+ * language)
+ * 2. If no page content language was assigned use the global content
+ * language
+ *
+ * General rules:
+ * - Special pages are in the user language
+ * - Display of values (DV) should use the user language if available otherwise
+ * use the content language as fallback
+ * - Storage of values (DI) should always use the content language
+ *
+ * Notes:
+ * - The page content language is the language in which the content of a page is
+ * written in wikitext
+ *
+ * @since 2.4
+ *
+ * @param DIWikiPage|Title|null $title
+ *
+ * @return Language
+ */
+ public function getPreferredContentLanguage( $title = null ) {
+
+ $language = '';
+
+ if ( $title instanceof DIWikiPage ) {
+ $title = $title->getTitle();
+ }
+
+ // If the page language is different from the global content language
+ // then we assume that an explicit language object was given otherwise
+ // the Title is using the content language as fallback
+ if ( $title instanceof Title ) {
+
+ // Avoid "MWUnknownContentModelException ... " when content model
+ // is not registered
+ try {
+ $language = $title->getPageLanguage();
+ } catch ( \Exception $e ) {
+
+ }
+ }
+
+ return $language instanceof Language ? $language : $this->getContentLanguage();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $languageCode
+ *
+ * @return Language
+ */
+ public function getLanguage( $languageCode = '' ) {
+
+ if ( $languageCode === '' || !$languageCode || $languageCode === null ) {
+ return $this->getContentLanguage();
+ }
+
+ return Language::factory( $languageCode );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Language|string $languageCode
+ *
+ * @return Lang
+ */
+ public function getLang( $language = '' ) {
+
+ $languageCode = $language;
+
+ if ( $language instanceof Language ) {
+ $languageCode = $language->getCode();
+ }
+
+ if ( $languageCode === '' || !$languageCode || $languageCode === null ) {
+ $languageCode = $this->getContentLanguage()->getCode();
+ }
+
+ return Lang::getInstance()->fetch( $languageCode );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $index
+ *
+ * @return string
+ */
+ public function getNamespaceTextById( $index ) {
+ return str_replace( '_', ' ', $this->contentLanguage->getNsText( $index ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $index
+ *
+ * @return string
+ */
+ public function getCanonicalNamespaceTextById( $index ) {
+
+ $canonicalNames = NamespaceManager::getCanonicalNames();
+
+ if ( isset( $canonicalNames[$index] ) ) {
+ return $canonicalNames[$index];
+ }
+
+ return \MWNamespace::getCanonicalName( $index );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $namespaceName
+ *
+ * @return integer|boolean
+ */
+ public function getNamespaceIndexByName( $namespaceName ) {
+ return $this->contentLanguage->getNsIndex( str_replace( ' ', '_', $namespaceName ) );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $languageCode
+ *
+ * @return boolean
+ */
+ public static function isKnownLanguageTag( $languageCode ) {
+
+ $languageCode = mb_strtolower( $languageCode );
+
+ // FIXME 1.19 doesn't know Language::isKnownLanguageTag
+ if ( !method_exists( '\Language', 'isKnownLanguageTag' ) ) {
+ return Language::isValidBuiltInCode( $languageCode );
+ }
+
+ return Language::isKnownLanguageTag( $languageCode );
+ }
+
+ /**
+ * @see IETF language tag / BCP 47 standards
+ *
+ * @since 2.4
+ *
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public static function asBCP47FormattedLanguageCode( $languageCode ) {
+ if ( !is_callable( [ '\LanguageCode', 'bcp47' ] ) ) {
+ // Backwards compatibility: remove once MW 1.30 is no
+ // longer supported (#3179)
+ return wfBCP47( $languageCode );
+ }
+ return \LanguageCode::bcp47( $languageCode );
+ }
+
+ /**
+ * @deprecated 2.5, use Localizer::getAnnotatedLanguageCodeFrom instead
+ * @since 2.4
+ *
+ * @param string &$value
+ *
+ * @return string|false
+ */
+ public static function getLanguageCodeFrom( &$value ) {
+ return self::getAnnotatedLanguageCodeFrom( $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $index
+ * @param string $text
+ *
+ * @return string
+ */
+ public function createTextWithNamespacePrefix( $index, $text ) {
+ return $this->getNamespaceTextById( $index ) . ':' . $text;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $ns
+ * @param string $url
+ *
+ * @return string
+ */
+ public function getCanonicalizedUrlByNamespace( $index, $url ) {
+
+ $namespace = $this->getNamespaceTextById( $index );
+
+ if ( strpos( $url, 'title=' ) !== false ) {
+ return str_replace(
+ [
+ 'title=' . wfUrlencode( $namespace ) . ':',
+ 'title=' . $namespace . ':'
+ ],
+ 'title=' . $this->getCanonicalNamespaceTextById( $index ) .':',
+ $url
+ );
+ }
+
+ return str_replace(
+ [
+ wfUrlencode( '/' . $namespace .':' ),
+ '/' . $namespace .':'
+ ],
+ '/' . $this->getCanonicalNamespaceTextById( $index ) . ':',
+ $url
+ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string &$value
+ *
+ * @return string|false
+ */
+ public static function getAnnotatedLanguageCodeFrom( &$value ) {
+
+ if ( strpos( $value, '@' ) === false ) {
+ return false;
+ }
+
+ if ( ( $langCode = mb_substr( strrchr( $value, "@" ), 1 ) ) !== '' ) {
+ $value = str_replace( '_', ' ', substr_replace( $value, '', ( mb_strlen( $langCode ) + 1 ) * -1 ) );
+ }
+
+ // Do we want to check here whether isKnownLanguageTag or not?
+ if ( $langCode !== '' && ctype_alpha( str_replace( [ '-' ], '', $langCode ) ) ) {
+ return $langCode;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see Language::convertDoubleWidth
+ *
+ * Convert double-width roman characters to single-width.
+ * range: ff00-ff5f ~= 0020-007f
+ *
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function convertDoubleWidth( $string ) {
+ static $full = null;
+ static $half = null;
+
+ if ( $full === null ) {
+ $fullWidth = "ï¼ï¼‘23456789ABCDEFGHIJKLMNOPQRSTUVWXYZï½ï½‚cdefghijklï½ï½Žï½ï½ï½‘rstuvwxyz";
+ $halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ // http://php.net/manual/en/function.str-split.php, mb_str_split
+ $length = mb_strlen( $fullWidth, "UTF-8" );
+ $full = [];
+
+ for ( $i = 0; $i < $length; $i += 1 ) {
+ $full[] = mb_substr( $fullWidth, $i, 1, "UTF-8" );
+ }
+
+ $half = str_split( $halfWidth );
+ }
+
+ return str_replace( $full, $half, trim( $string ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ConceptCacheRebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ConceptCacheRebuilder.php
new file mode 100644
index 00000000..c8752e0d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ConceptCacheRebuilder.php
@@ -0,0 +1,273 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\DIConcept;
+use SMW\MediaWiki\TitleLookup;
+use SMW\Settings;
+use SMW\Store;
+use Title;
+
+/**
+ * Is part of the `rebuildConceptCache.php` maintenance script to rebuild
+ * cache entries for selected concepts
+ *
+ * @note This is an internal class and should not be used outside of smw-core
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class ConceptCacheRebuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Settings
+ */
+ private $settings;
+
+ /**
+ * @var MessageReporter
+ */
+ private $reporter;
+
+ private $concept = null;
+ private $action = null;
+ private $options = [];
+ private $startId = 0;
+ private $endId = 0;
+ private $lines = 0;
+ private $verbose = false;
+
+ /**
+ * @since 1.9.2
+ *
+ * @param Store $store
+ * @param Settings $settings
+ */
+ public function __construct( Store $store, Settings $settings ) {
+ $this->store = $store;
+ $this->settings = $settings;
+ $this->reporter = MessageReporterFactory::getInstance()->newNullMessageReporter();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param MessageReporter $reporter
+ */
+ public function setMessageReporter( MessageReporter $reporter ) {
+ $this->reporter = $reporter;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param array $parameters
+ */
+ public function setParameters( array $parameters ) {
+
+ $options = [ 'hard', 'update', 'old', 'quiet', 'status', 'verbose' ];
+
+ foreach ( $options as $option ) {
+ if ( isset( $parameters[$option] ) ) {
+ $this->options[$option] = $parameters[$option];
+ }
+ }
+
+ if ( isset( $parameters['concept'] ) ) {
+ $this->concept = $parameters['concept'];
+ }
+
+ if ( isset( $parameters['s'] ) ) {
+ $this->startId = intval( $parameters['s'] );
+ }
+
+ if ( isset( $parameters['e'] ) ) {
+ $this->endId = intval( $parameters['e'] );
+ }
+
+ $actions = [ 'status', 'create', 'delete' ];
+
+ foreach ( $actions as $action ) {
+ if ( isset( $parameters[$action] ) && $this->action === null ) {
+ $this->action = $action;
+ }
+ }
+
+ $this->verbose = array_key_exists( 'verbose', $parameters );
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return boolean
+ */
+ public function rebuild() {
+
+ switch ( $this->action ) {
+ case 'status':
+ $this->reportMessage( "\nDisplaying concept cache status information. Use CTRL-C to abort.\n\n" );
+ break;
+ case 'create':
+ $this->reportMessage( "\nCreating/updating concept caches. Use CTRL-C to abort.\n\n" );
+ break;
+ case 'delete':
+ $delay = 5;
+ $this->reportMessage( "\nAbort with CTRL-C in the next $delay seconds ... " );
+
+ if ( !$this->hasOption( 'quiet' ) ) {
+ swfCountDown( $delay );
+ }
+
+ $this->reportMessage( "\nDeleting concept caches.\n\n" );
+ break;
+ default:
+ return false;
+ }
+
+ if ( $this->hasOption( 'hard' ) ) {
+
+ $settings = ' smwgQMaxDepth: ' . $this->settings->get( 'smwgQMaxDepth' );
+ $settings .= ' smwgQMaxSize: ' . $this->settings->get( 'smwgQMaxSize' );
+ $settings .= ' smwgQFeatures: ' . $this->settings->get( 'smwgQFeatures' );
+
+ $this->reportMessage( "Option 'hard' is parameterized by{$settings}\n\n" );
+ }
+
+ $concepts = $this->getConcepts();
+
+ foreach ( $concepts as $concept ) {
+ $this->workOnConcept( $concept );
+ }
+
+ if ( $concepts === [] ) {
+ $this->reportMessage( "No concept available.\n" );
+ } else {
+ $this->reportMessage( "\nDone.\n" );
+ }
+
+ return true;
+ }
+
+ private function workOnConcept( Title $title ) {
+
+ $concept = $this->store->getConceptCacheStatus( $title );
+
+ if ( $this->skipConcept( $title, $concept ) ) {
+ return $this->lines += $this->verbose ? 1 : 0;
+ }
+
+ $result = $this->performAction( $title, $concept );
+
+ if ( $result ) {
+ $this->reportMessage( ' ' . implode( $result, "\n " ) . "\n" );
+ }
+
+ return $this->lines += 1;
+ }
+
+ private function skipConcept( $title, $concept = null ) {
+
+ $skip = false;
+
+ if ( $concept === null ) {
+ $skip = 'page not cacheable (no concept description, maybe a redirect)';
+ } elseif ( ( $this->hasOption( 'update' ) ) && ( $concept->getCacheStatus() !== 'full' ) ) {
+ $skip = 'page not cached yet';
+ } elseif ( ( $this->hasOption( 'old' ) ) && ( $concept->getCacheStatus() === 'full' ) &&
+ ( $concept->getCacheDate() > ( strtotime( 'now' ) - intval( $this->options['old'] ) * 60 ) ) ) {
+ $skip = 'cache is not old yet';
+ } elseif ( ( $this->hasOption( 'hard' ) ) && ( $this->settings->get( 'smwgQMaxSize' ) >= $concept->getSize() ) &&
+ ( $this->settings->get( 'smwgQMaxDepth' ) >= $concept->getDepth() &&
+ ( ( ~( ~( $concept->getQueryFeatures() + 0 ) | $this->settings->get( 'smwgQFeatures' ) ) ) == 0 ) ) ) {
+ $skip = 'concept is not "hard" according to wiki settings';
+ }
+
+ if ( $skip ) {
+ $line = $this->lines !== false ? "($this->lines) " : '';
+ $this->reportMessage( $line . 'Skipping concept "' . $title->getPrefixedText() . "\": $skip\n", $this->verbose );
+ }
+
+ return $skip;
+ }
+
+ private function performAction( Title $title, DIConcept $concept ) {
+ $this->reportMessage( "($this->lines) " );
+
+ if ( $this->action === 'create' ) {
+ $this->reportMessage( 'Creating cache for "' . $title->getPrefixedText() . "\" ...\n" );
+ return $this->store->refreshConceptCache( $title );
+ }
+
+ if ( $this->action === 'delete' ) {
+ $this->reportMessage( 'Deleting cache for "' . $title->getPrefixedText() . "\" ...\n" );
+ return $this->store->deleteConceptCache( $title );
+ }
+
+ $this->reportMessage( 'Status for "' . $title->getPrefixedText() . '": ' );
+
+ if ( $concept->getCacheStatus() === 'full' ) {
+ $this->reportMessage( 'Cache created at ' .
+ $this->getCacheDateInfo( $concept->getCacheDate() ) .
+ "{$concept->getCacheCount()} elements in cache\n"
+ );
+ }
+ else {
+ $this->reportMessage( "Not cached.\n" );
+ }
+ }
+
+ private function getConcepts() {
+
+ if ( $this->concept !== null ) {
+ return [ $this->createConcept() ];
+ }
+
+ return $this->createMultipleConcepts();
+ }
+
+ private function createConcept() {
+ return Title::newFromText( $this->concept, SMW_NS_CONCEPT );
+ }
+
+ private function createMultipleConcepts() {
+
+ $titleLookup = new TitleLookup( $this->store->getConnection( 'mw.db' ) );
+ $titleLookup->setNamespace( SMW_NS_CONCEPT );
+
+ if ( $this->endId == 0 && $this->startId == 0 ) {
+ return $titleLookup->selectAll();
+ }
+
+ $endId = $titleLookup->getMaxId();
+
+ if ( $this->endId > 0 ) {
+ $endId = min( $this->endId, $endId );
+ }
+
+ return $titleLookup->selectByIdRange( $this->startId, $endId );
+ }
+
+ private function hasOption( $key ) {
+ return isset( $this->options[$key] );
+ }
+
+ private function reportMessage( $message, $output = true ) {
+ if ( $output ) {
+ $this->reporter->reportMessage( $message );
+ }
+ }
+
+ private function getCacheDateInfo( $date ) {
+ return date( 'Y-m-d H:i:s', $date ) . ' (' . floor( ( strtotime( 'now' ) - $date ) / 60 ) . ' minutes old), ';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DataRebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DataRebuilder.php
new file mode 100644
index 00000000..46fc5688
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DataRebuilder.php
@@ -0,0 +1,532 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Exception;
+use LinkCache;
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\TitleFactory;
+use SMW\Options;
+use SMW\Store;
+use Title;
+
+/**
+ * Is part of the `rebuildData.php` maintenance script to rebuild existing data
+ * for the store
+ *
+ * @note This is an internal class and should not be used outside of smw-core
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class DataRebuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var TitleFactory
+ */
+ private $titleFactory;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var MessageReporter
+ */
+ private $reporter;
+
+ /**
+ * @var DistinctEntityDataRebuilder
+ */
+ private $distinctEntityDataRebuilder;
+
+ /**
+ * @var ExceptionFileLogger
+ */
+ private $exceptionFileLogger;
+
+ /**
+ * @var integer
+ */
+ private $rebuildCount = 0;
+
+ /**
+ * @var integer
+ */
+ private $exceptionCount = 0;
+
+ private $delay = false;
+ private $canWriteToIdFile = false;
+ private $start = 1;
+ private $end = false;
+
+ /**
+ * @var int[]
+ */
+ private $filters = [];
+ private $verbose = false;
+ private $startIdFile = false;
+
+ /**
+ * @since 1.9.2
+ *
+ * @param Store $store
+ * @param TitleFactory $titleFactory
+ */
+ public function __construct( Store $store, TitleFactory $titleFactory ) {
+ $this->store = $store;
+ $this->titleFactory = $titleFactory;
+ $this->reporter = MessageReporterFactory::getInstance()->newNullMessageReporter();
+ $this->distinctEntityDataRebuilder = new DistinctEntityDataRebuilder( $store, $titleFactory );
+ $this->exceptionFileLogger = new ExceptionFileLogger( 'rebuilddata' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param MessageReporter $reporter
+ */
+ public function setMessageReporter( MessageReporter $reporter ) {
+ $this->reporter = $reporter;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param Options $options
+ */
+ public function setOptions( Options $options ) {
+ $this->options = $options;
+
+ if ( $options->has( 'server' ) ) {
+ $GLOBALS['wgServer'] = $options->get( 'server' );
+ }
+
+ if ( $options->has( 'd' ) ) {
+ $this->delay = intval( $options->get( 'd' ) ) * 1000; // convert milliseconds to microseconds
+ }
+
+ if ( $options->has( 's' ) ) {
+ $this->start = max( 1, intval( $options->get( 's' ) ) );
+ } elseif ( $options->has( 'startidfile' ) ) {
+
+ $this->canWriteToIdFile = $this->is_writable( $options->get( 'startidfile' ) );
+ $this->startIdFile = $options->get( 'startidfile' );
+
+ if ( is_readable( $options->get( 'startidfile' ) ) ) {
+ $this->start = max( 1, intval( file_get_contents( $options->get( 'startidfile' ) ) ) );
+ }
+ }
+
+ // Note: this might reasonably be larger than the page count
+ if ( $options->has( 'e' ) ) {
+ $this->end = intval( $options->get( 'e' ) );
+ } elseif ( $options->has( 'n' ) ) {
+ $this->end = $this->start + intval( $options->get( 'n' ) );
+ }
+
+ $this->verbose = $options->has( 'v' );
+ $this->exceptionFileLogger->setOptions( $options );
+
+ $this->setFiltersFromOptions( $options );
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return boolean
+ */
+ public function rebuild() {
+
+ $this->reportMessage(
+ "\nLong-running scripts may cause memory leaks, if a deteriorating\n" .
+ "rebuild process is detected (after many pages, typically more\n".
+ "than 10000), please abort with CTRL-C and resume this script\n" .
+ "at the last processed ID using the parameter -s. Continue this\n" .
+ "until all pages have been refreshed.\n"
+ );
+
+ $this->reportMessage(
+ "\nThe progress displayed is an estimation and is self-adjusting \n" .
+ "during the maintenance process.\n"
+ );
+
+ $storeName = get_class( $this->store );
+
+ if ( strpos( $storeName, "\\") !== false ) {
+ $storeName = explode("\\", $storeName );
+ $storeName = end( $storeName );
+ }
+
+ $this->reportMessage( "\nRunning for storage: " . $storeName . "\n\n" );
+
+ if ( $this->options->has( 'f' ) ) {
+ $this->performFullDelete();
+ }
+
+ if ( $this->options->has( 'page' ) || $this->options->has( 'query' ) || $this->hasFilters() || $this->options->has( 'redirects' ) ) {
+ return $this->rebuild_selection();
+ }
+
+ return $this->rebuild_all();
+ }
+
+ private function hasFilters() {
+ return $this->filters !== [];
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return int
+ */
+ public function getRebuildCount() {
+ return $this->rebuildCount;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return int
+ */
+ public function getExceptionCount() {
+ return $this->exceptionCount;
+ }
+
+ private function rebuild_selection() {
+
+ $this->distinctEntityDataRebuilder->setOptions(
+ $this->options
+ );
+
+ $this->distinctEntityDataRebuilder->setMessageReporter(
+ $this->reporter
+ );
+
+ $this->distinctEntityDataRebuilder->setExceptionFileLogger(
+ $this->exceptionFileLogger
+ );
+
+ $this->distinctEntityDataRebuilder->doRebuild();
+
+ $this->rebuildCount = $this->distinctEntityDataRebuilder->getRebuildCount();
+
+ if ( $this->options->has( 'ignore-exceptions' ) && $this->exceptionFileLogger->getExceptionCount() > 0 ) {
+ $count = $this->exceptionFileLogger->getExceptionCount();
+ $this->exceptionFileLogger->doWrite();
+
+ $path_parts = pathinfo(
+ str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $this->exceptionFileLogger->getExceptionFile() )
+ );
+
+ $this->reportMessage( "\nException log ..." );
+ $this->reportMessage( "\n ... counted $count exceptions" );
+ $this->reportMessage( "\n ... written to ... " . $path_parts['basename'] );
+ $this->reportMessage( "\n ... done.\n" );
+
+ $this->exceptionCount += $count;
+ }
+
+ return true;
+ }
+
+ private function rebuild_all() {
+
+ $this->entityRebuildDispatcher = $this->store->refreshData(
+ $this->start,
+ 1
+ );
+
+ $this->entityRebuildDispatcher->setDispatchRangeLimit( 1 );
+
+ $this->entityRebuildDispatcher->setOptions(
+ [
+ 'shallow-update' => $this->options->safeGet( 'shallow-update', false ),
+ 'force-update' => $this->options->safeGet( 'force-update', false ),
+ 'revision-mode' => $this->options->safeGet( 'revision-mode', false ),
+ 'use-job' => false
+ ]
+ );
+
+ // By default we expect the disposal action to take place whenever the
+ // script is run
+ $this->dispose_outdated();
+
+ // Only expected the disposal action?
+ if ( $this->options->has( 'dispose-outdated' ) ) {
+ return true;
+ }
+
+ $this->reportMessage( "\n" );
+
+ if ( !$this->options->has( 'skip-properties' ) ) {
+ $this->options->set( 'p', true );
+ $this->rebuild_selection();
+ $this->reportMessage( "\n" );
+ }
+
+ $this->store->clear();
+
+ if ( $this->start > 1 && $this->end === false ) {
+ $this->end = $this->entityRebuildDispatcher->getMaxId();
+ }
+
+ $total = $this->end && $this->end - $this->start > 0 ? $this->end - $this->start : $this->entityRebuildDispatcher->getMaxId();
+ $id = $this->start;
+
+ $this->reportMessage(
+ "Rebuilding semantic data ..."
+ );
+
+ $this->reportMessage(
+ "\n ... selecting $this->start to " .
+ ( $this->end ? "$this->end" : $this->entityRebuildDispatcher->getMaxId() ) . " IDs ...\n"
+ );
+
+ $this->rebuildCount = 0;
+ $progress = 0;
+ $estimatedProgress = 0;
+ $skipped_update = 0;
+
+ while ( ( ( !$this->end ) || ( $id <= $this->end ) ) && ( $id > 0 ) ) {
+
+ $current_id = $id;
+
+ // Changes the ID to next target!
+ $this->do_update( $id );
+
+ if ( $this->rebuildCount % 60 === 0 ) {
+ $estimatedProgress = $this->entityRebuildDispatcher->getEstimatedProgress();
+ }
+
+ $progress = round( ( $this->end - $this->start > 0 ? $this->rebuildCount / $total : $estimatedProgress ) * 100 );
+
+ foreach ( $this->entityRebuildDispatcher->getDispatchedEntities() as $value ) {
+
+ if ( isset( $value['skipped'] ) ) {
+ $skipped_update++;
+ continue;
+ }
+
+ $text = $this->getHumanReadableTextFrom( $current_id, $value );
+
+ $this->reportMessage(
+ sprintf( "%-16s%s\n", " ... updating", sprintf( "%-10s%s", $text[0], $text[1] ) ),
+ $this->options->has( 'v' )
+ );
+ }
+
+ if ( !$this->options->has( 'v' ) && $id > 0 ) {
+ $this->reportMessage(
+ "\r". sprintf( "%-50s%s", " ... updating document no.", sprintf( "%s (%1.0f%%)", $current_id, min( 100, $progress ) ) )
+ );
+ }
+ }
+
+ if ( !$this->options->has( 'v' ) ) {
+ $this->reportMessage(
+ "\r". sprintf( "%-50s%s", " ... updating document no.", sprintf( "%s (%1.0f%%)", $current_id, 100 ) )
+ );
+ }
+
+ $this->write_to_file( $id );
+
+ $this->reportMessage( "\n ... $this->rebuildCount IDs checked or refreshed ..." );
+ $this->reportMessage( "\n ... $skipped_update IDs skipped ..." );
+ $this->reportMessage( "\n ... done.\n" );
+
+ if ( $this->options->has( 'ignore-exceptions' ) && $this->exceptionFileLogger->getExceptionCount() > 0 ) {
+ $this->exceptionCount += $this->exceptionFileLogger->getExceptionCount();
+ $this->exceptionFileLogger->doWrite();
+
+ $path_parts = pathinfo(
+ str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $this->exceptionFileLogger->getExceptionFile() )
+ );
+
+ $this->reportMessage( "\nException log ..." );
+ $this->reportMessage( "\n ... counted $this->exceptionCount exceptions" );
+ $this->reportMessage( "\n ... written to ... " . $path_parts['basename'] );
+ $this->reportMessage( "\n ... done.\n" );
+ }
+
+ return true;
+ }
+
+ private function do_update( &$id ) {
+
+ if ( !$this->options->has( 'ignore-exceptions' ) ) {
+ $this->entityRebuildDispatcher->rebuild( $id );
+ } else {
+
+ try {
+ $this->entityRebuildDispatcher->rebuild( $id );
+ } catch ( Exception $e ) {
+ $this->exceptionFileLogger->recordException( $id, $e );
+ }
+ }
+
+ if ( $this->delay !== false ) {
+ usleep( $this->delay );
+ }
+
+ if ( $this->rebuildCount % 100 === 0 ) { // every 100 pages only
+ LinkCache::singleton()->clear(); // avoid memory leaks
+ }
+
+ $this->rebuildCount++;
+ }
+
+ private function getHumanReadableTextFrom( $id, array $entities ) {
+
+ if ( !$this->options->has( 'v' ) ) {
+ return [ '', ''];
+ }
+
+ // Indicates whether this is a MW page (*) or SMW's object table
+ $text = $id . ( isset( $entities['t'] ) ? '*' : ' ' );
+
+ $entity = end( $entities );
+
+ if ( $entity instanceof \Title ) {
+ return [ $text, '[' . $entity->getPrefixedDBKey() .']' ];
+ }
+
+ if ( $entity instanceof DIWikiPage ) {
+ return [ $text, '[' . $entity->getHash() .']' ];
+ }
+
+ return [ $text, '[' . ( is_string( $entity ) && $entity !== '' ? $entity : 'N/A' ) . ']' ];
+ }
+
+ private function performFullDelete() {
+
+ $this->reportMessage(
+ "Deleting all stored data completely and rebuilding it again later!\n\n" .
+ "Semantic data in the wiki might be incomplete for some time while\n".
+ "this operation runs.\n\n" .
+ "NOTE: It is usually necessary to run this script ONE MORE TIME\n".
+ "after this operation, given that some properties and types are not\n" .
+ "yet stored with the first run.\n\n"
+ );
+
+ if ( $this->options->has( 's' ) || $this->options->has( 'e' ) ) {
+ $this->reportMessage(
+ "WARNING: -s or -e are used, so some pages will not be refreshed at all!\n" .
+ "Data for those pages will only be available again when they have been\n" .
+ "refreshed as well!\n\n"
+ );
+ }
+
+ $obLevel = ob_get_level();
+
+ $this->reportMessage( 'Abort with control-c in the next five seconds ... ' );
+ swfCountDown( 6 );
+
+ $this->reportMessage( "\nDeleting all data ..." );
+
+ $this->reportMessage( "\n ... dropping tables ..." );
+ $this->store->drop( $this->verbose );
+
+ $this->reportMessage( "\n ... creating tables ..." );
+ $this->store->setupStore( $this->verbose );
+
+ $this->reportMessage( "\n ... done.\n" );
+
+ // Be sure to have some buffer, otherwise some PHPs complain
+ while ( ob_get_level() > $obLevel ) {
+ ob_end_flush();
+ }
+
+ $this->reportMessage( "\nAll storage structures have been deleted and recreated.\n\n" );
+
+ return true;
+ }
+
+ private function dispose_outdated() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $entityIdDisposerJob = $applicationFactory->newJobFactory()->newEntityIdDisposerJob(
+ Title::newFromText( __METHOD__ )
+ );
+
+ $outdatedEntitiesResultIterator = $entityIdDisposerJob->newOutdatedEntitiesResultIterator();
+ $matchesCount = $outdatedEntitiesResultIterator->count();
+ $counter = 0;
+
+ $this->reportMessage( "Removing outdated entities ..." );
+
+ if ( $matchesCount > 0 ) {
+ $this->reportMessage( "\n" );
+
+ $chunkedIterator = $applicationFactory->getIteratorFactory()->newChunkedIterator(
+ $outdatedEntitiesResultIterator,
+ 200
+ );
+
+ foreach ( $chunkedIterator as $chunk ) {
+ foreach ( $chunk as $row ) {
+ $counter++;
+ $msg = sprintf( "%s (%1.0f%%)", $row->smw_id, round( $counter / $matchesCount * 100 ) );
+
+ $this->reportMessage(
+ "\r". sprintf( "%-50s%s", " ... cleaning up document no.", $msg )
+ );
+
+ $entityIdDisposerJob->dispose( $row );
+ }
+ }
+
+ $this->reportMessage( "\n ... {$matchesCount} IDs removed ..." );
+ }
+
+ $this->reportMessage( "\n ... done.\n" );
+ }
+
+ private function is_writable( $startIdFile ) {
+
+ if ( !is_writable( file_exists( $startIdFile ) ? $startIdFile : dirname( $startIdFile ) ) ) {
+ die( "Cannot use a startidfile that we can't write to.\n" );
+ }
+
+ return true;
+ }
+
+ private function write_to_file( $id ) {
+ if ( $this->canWriteToIdFile ) {
+ file_put_contents( $this->startIdFile, "$id" );
+ }
+ }
+
+ /**
+ * @param array $options
+ */
+ private function setFiltersFromOptions( Options $options ) {
+ $this->filters = [];
+
+ if ( $options->has( 'categories' ) ) {
+ $this->filters[] = NS_CATEGORY;
+ }
+
+ if ( $options->has( 'p' ) ) {
+ $this->filters[] = SMW_NS_PROPERTY;
+ }
+ }
+
+ private function reportMessage( $message, $output = true ) {
+ if ( $output ) {
+ $this->reporter->reportMessage( $message );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DistinctEntityDataRebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DistinctEntityDataRebuilder.php
new file mode 100644
index 00000000..a7c64c84
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DistinctEntityDataRebuilder.php
@@ -0,0 +1,294 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Exception;
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\TitleFactory;
+use SMW\MediaWiki\TitleLookup;
+use SMW\ApplicationFactory;
+use SMW\Options;
+use SMW\Store;
+use SMWQueryProcessor;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DistinctEntityDataRebuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var TitleFactory
+ */
+ private $titleFactory;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var MessageReporter
+ */
+ private $reporter;
+
+ /**
+ * @var ExceptionFileLogger
+ */
+ private $exceptionFileLogger;
+
+ /**
+ * @var array
+ */
+ private $filters = [];
+
+ /**
+ * @var integer
+ */
+ private $rebuildCount = 0;
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param TitleFactory $titleFactory
+ */
+ public function __construct( Store $store, TitleFactory $titleFactory ) {
+ $this->store = $store;
+ $this->titleFactory = $titleFactory;
+ $this->reporter = MessageReporterFactory::getInstance()->newNullMessageReporter();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param MessageReporter $reporter
+ */
+ public function setOptions( Options $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param MessageReporter $reporter
+ */
+ public function setMessageReporter( MessageReporter $reporter ) {
+ $this->reporter = $reporter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ExceptionFileLogger $exceptionFileLogger
+ */
+ public function setExceptionFileLogger( ExceptionFileLogger $exceptionFileLogger ) {
+ $this->exceptionFileLogger = $exceptionFileLogger;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return int
+ */
+ public function getRebuildCount() {
+ return $this->rebuildCount;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function doRebuild() {
+
+ $type = ( $this->options->has( 'redirects' ) ? 'redirect' : '' ) .
+ ( $this->options->has( 'categories' ) ? 'category' : '' ) .
+ ( $this->options->has( 'query' ) ? 'query (' . $this->options->get( 'query' ) .')' : '' ) .
+ ( $this->options->has( 'p' ) ? 'property' : '' );
+
+ $pages = [];
+ $this->findFilters();
+
+ if ( $this->options->has( 'page' ) ) {
+ $pages = explode( '|', $this->options->get( 'page' ) );
+ }
+
+ $pages = $this->normalize(
+ [
+ $this->getPagesFromQuery(),
+ $pages,
+ $this->getPagesFromFilters(),
+ $this->getRedirectPages()
+ ]
+ );
+
+ $total = count( $pages );
+ $this->reportMessage( "Rebuilding $type pages ...\n" );
+ $this->reportMessage( " ... selecting $total pages ...\n" );
+
+ $jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+
+ foreach ( $pages as $key => $page ) {
+
+ $this->rebuildCount++;
+ $progress = round( ( $this->rebuildCount / $total ) * 100 );
+
+ if ( !$this->options->has( 'v' ) ) {
+ $this->reportMessage(
+ "\r". sprintf( "%-50s%s", " ... updating document no.", sprintf( "%s (%1.0f%%)", $this->rebuildCount, $progress ) )
+ );
+ } else {
+ $this->reportMessage(
+ sprintf( "%-16s%s\n", " ... ($this->rebuildCount/$total $progress)", "Page " . $key ),
+ $this->options->has( 'v' )
+ );
+ }
+
+ $this->doUpdate( $jobFactory, $page );
+ }
+
+ $this->reportMessage( ( $this->options->has( 'v' ) ? "" : "\n" ) . " ... done.\n" );
+
+ return true;
+ }
+
+ private function doUpdate( $jobFactory, $page ) {
+
+ $updatejob = $jobFactory->newUpdateJob(
+ $page,
+ [
+ UpdateJob::FORCED_UPDATE => true,
+ 'shallowUpdate' => $this->options->has( 'shallow-update' )
+ ]
+ );
+
+ if ( !$this->options->has( 'ignore-exceptions' ) ) {
+ return $updatejob->run();
+ }
+
+ try {
+ $updatejob->run();
+ } catch ( Exception $e ) {
+ $this->exceptionFileLogger->recordException( $page->getPrefixedDBkey(), $e );
+ }
+ }
+
+ private function findFilters() {
+ $this->filters = [];
+
+ if ( $this->options->has( 'categories' ) ) {
+ $this->filters[] = NS_CATEGORY;
+ }
+
+ if ( $this->options->has( 'p' ) ) {
+ $this->filters[] = SMW_NS_PROPERTY;
+ }
+ }
+
+ private function hasFilters() {
+ return $this->filters !== [];
+ }
+
+ private function getPagesFromQuery() {
+
+ if ( !$this->options->has( 'query' ) ) {
+ return [];
+ }
+
+ $queryString = $this->options->get( 'query' );
+
+ // get number of pages and fix query limit
+ $query = SMWQueryProcessor::createQuery(
+ $queryString,
+ SMWQueryProcessor::getProcessedParams( [ 'format' => 'count' ] )
+ );
+
+ $result = $this->store->getQueryResult( $query );
+
+ // get pages and add them to the pages explicitly listed in the 'page' parameter
+ $query = SMWQueryProcessor::createQuery(
+ $queryString,
+ SMWQueryProcessor::getProcessedParams( [] )
+ );
+
+ $query->setUnboundLimit( $result instanceof \SMWQueryResult ? $result->getCountValue() : $result );
+
+ return $this->store->getQueryResult( $query )->getResults();
+ }
+
+ private function getPagesFromFilters() {
+
+ $pages = [];
+
+ if ( !$this->hasFilters() ) {
+ return $pages;
+ }
+
+ $titleLookup = new TitleLookup( $this->store->getConnection( 'mw.db' ) );
+
+ foreach ( $this->filters as $namespace ) {
+ $pages = array_merge( $pages, $titleLookup->setNamespace( $namespace )->selectAll() );
+ }
+
+ return $pages;
+ }
+
+ private function getRedirectPages() {
+
+ if ( !$this->options->has( 'redirects' ) ) {
+ return [];
+ }
+
+ $titleLookup = new TitleLookup(
+ $this->store->getConnection( 'mw.db' )
+ );
+
+ return $titleLookup->getRedirectPages();
+ }
+
+ private function normalize( $list ) {
+
+ $titleCache = [];
+ $p = [];
+
+ foreach ( $list as $pages ) {
+ foreach ( $pages as $key => $page ) {
+
+ if ( $page instanceof DIWikiPage ) {
+ $page = $page->getTitle();
+ }
+
+ if ( !$page instanceof Title ) {
+ $page = $this->titleFactory->newFromText( $page );
+ }
+
+ $id = $page->getPrefixedDBkey();
+
+ if ( !isset( $p[$id] ) ) {
+ $p[$id] = $page;
+ }
+ }
+ }
+
+ return $p;
+ }
+
+ private function reportMessage( $message, $output = true ) {
+ if ( $output ) {
+ $this->reporter->reportMessage( $message );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DuplicateEntitiesDisposer.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DuplicateEntitiesDisposer.php
new file mode 100644
index 00000000..33afcb1e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/DuplicateEntitiesDisposer.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\Cache\Cache;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use SMW\SQLStore\PropertyTableIdReferenceDisposer;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DuplicateEntitiesDisposer {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Store
+ */
+ private $cache;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param Cache|null $cache
+ */
+ public function __construct( Store $store, Cache $cache = null ) {
+ $this->store = $store;
+ $this->cache = $cache;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function findDuplicates() {
+ return $this->store->getObjectIds()->findDuplicates();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Iterator|array $duplicates
+ */
+ public function verifyAndDispose( $duplicates ) {
+
+ if ( !$this->is_iterable( $duplicates ) ) {
+ return;
+ }
+
+ $count = count( $duplicates );
+ $this->messageReporter->reportMessage( "Found: $count duplicates\n" );
+
+ if ( $count > 0 ) {
+ $this->doDispose( $duplicates );
+ }
+
+ if ( $this->cache !== null ) {
+ $this->cache->delete( \SMW\MediaWiki\Api\Task::makeCacheKey( 'duplookup' ) );
+ }
+ }
+
+ private function doDispose( $duplicates ) {
+
+ $propertyTableIdReferenceDisposer = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $propertyTableIdReferenceDisposer->setRedirectRemoval( true );
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $log = [
+ 'disposed' => [],
+ 'untouched' => []
+ ];
+
+ $i = 0;
+ foreach ( $duplicates as $duplicate ) {
+ unset( $duplicate['count'] );
+
+ if ( ( $i ) % 60 === 0 ) {
+ $this->messageReporter->reportMessage( "\n" );
+ }
+
+ $this->messageReporter->reportMessage( '.' );
+
+ $res = $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ ],
+ [
+ 'smw_title'=> $duplicate['smw_title'],
+ 'smw_namespace'=> $duplicate['smw_namespace'],
+ 'smw_iw'=> $duplicate['smw_iw'],
+ 'smw_subobject'=> $duplicate['smw_subobject']
+ ],
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ if ( $propertyTableIdReferenceDisposer->isDisposable( $row->smw_id ) ) {
+ $propertyTableIdReferenceDisposer->cleanUpTableEntriesById( $row->smw_id );
+ $log['disposed'][$row->smw_id] = $duplicate;
+ } else {
+ $log['untouched'][$row->smw_id] = $duplicate;
+ }
+ }
+
+ $i++;
+ }
+
+ $this->messageReporter->reportMessage(
+ "\n\nLog\n\n" . json_encode( $log, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . "\n"
+ );
+ }
+
+ /**
+ * Polyfill for PHP 7.0-
+ *
+ * @see http://php.net/manual/en/function.is-iterable.php
+ *
+ * @since 3.0
+ */
+ private function is_iterable( $obj ) {
+ return is_array( $obj ) || ( is_object( $obj ) && ( $obj instanceof \Traversable ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ExceptionFileLogger.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ExceptionFileLogger.php
new file mode 100644
index 00000000..4530e49f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/ExceptionFileLogger.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Exception;
+use SMW\Options;
+use SMW\Utils\File;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ExceptionFileLogger {
+
+ /**
+ * @var string
+ */
+ private $namespace;
+
+ /**
+ * @var File
+ */
+ private $file;
+
+ /**
+ * @var string
+ */
+ private $exceptionFile;
+
+ /**
+ * @var integer
+ */
+ private $exceptionCount = 0;
+
+ /**
+ * @var array
+ */
+ private $exceptionLogMessages = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param string $namespace
+ * @param File|null $file
+ */
+ public function __construct( $namespace = 'smw', File $file = null ) {
+ $this->namespace = $namespace;
+ $this->file = $file;
+
+ if ( $this->file === null ) {
+ $this->file = new File();
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Options $options
+ */
+ public function setOptions( Options $options ) {
+
+ $dateTimeUtc = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
+ $this->exceptionFile = __DIR__ . "../../../";
+
+ if ( $options->has( 'exception-log' ) ) {
+ $this->exceptionFile = $options->get( 'exception-log' );
+ }
+
+ $this->exceptionFile .= $this->namespace . "-exceptions-" . $dateTimeUtc->format( 'Y-m-d' ) . ".log";
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getExceptionFile() {
+ return realpath( $this->exceptionFile );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return integer
+ */
+ public function getExceptionCount() {
+ return $this->exceptionCount;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ * @param Exception $exception
+ */
+ public function recordException( $id, Exception $exception ) {
+ $this->exceptionCount++;
+
+ $this->exceptionLogMessages[$id] = [
+ 'msg' => $exception->getMessage(),
+ 'trace' => $exception->getTraceAsString()
+ ];
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function doWrite() {
+
+ foreach ( $this->exceptionLogMessages as $id => $exception ) {
+ $this->put( $id, $exception );
+ }
+
+ $this->exceptionLogMessages = [];
+ $this->exceptionCount = 0;
+ }
+
+ private function put( $id, $exception ) {
+
+ $text = "\n======== EXCEPTION ======\n" .
+ "$id | " . $exception['msg'] . "\n\n" .
+ $exception['trace'] . "\n" .
+ "======== END ======" ."\n";
+
+ $this->file->write( $this->exceptionFile, $text, FILE_APPEND );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceFactory.php
new file mode 100644
index 00000000..3cf4f2c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceFactory.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\ManualEntryLogger;
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MaintenanceFactory {
+
+ /**
+ * @since 2.2
+ *
+ * @return MaintenanceHelper
+ */
+ public function newMaintenanceHelper() {
+ return new MaintenanceHelper();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param Callable|null $reporterCallback
+ *
+ * @return DataRebuilder
+ */
+ public function newDataRebuilder( Store $store, $reporterCallback = null ) {
+
+ $messageReporter = $this->newMessageReporter( $reporterCallback );
+
+ $dataRebuilder = new DataRebuilder(
+ $store,
+ ApplicationFactory::getInstance()->newTitleFactory()
+ );
+
+ $dataRebuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ return $dataRebuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param Callable|null $reporterCallback
+ *
+ * @return ConceptCacheRebuilder
+ */
+ public function newConceptCacheRebuilder( Store $store, $reporterCallback = null ) {
+
+ $conceptCacheRebuilder = new ConceptCacheRebuilder(
+ $store,
+ ApplicationFactory::getInstance()->getSettings()
+ );
+
+ $conceptCacheRebuilder->setMessageReporter(
+ $this->newMessageReporter( $reporterCallback )
+ );
+
+ return $conceptCacheRebuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param Callable|null $reporterCallback
+ *
+ * @return PropertyStatisticsRebuilder
+ */
+ public function newPropertyStatisticsRebuilder( Store $store, $reporterCallback = null ) {
+
+ $propertyStatisticsStore = new PropertyStatisticsStore(
+ $store->getConnection( 'mw.db' )
+ );
+
+ $propertyStatisticsRebuilder = new PropertyStatisticsRebuilder(
+ $store,
+ $propertyStatisticsStore
+ );
+
+ $propertyStatisticsRebuilder->setMessageReporter(
+ $this->newMessageReporter( $reporterCallback )
+ );
+
+ return $propertyStatisticsRebuilder;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return RebuildPropertyStatistics
+ */
+ public function newRebuildPropertyStatistics() {
+ return new RebuildPropertyStatistics();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DuplicateEntitiesDisposer
+ */
+ public function newDuplicateEntitiesDisposer( Store $store, $reporterCallback = null ) {
+
+ $duplicateEntitiesDisposer = new DuplicateEntitiesDisposer(
+ $store,
+ ApplicationFactory::getInstance()->getCache()
+ );
+
+ $duplicateEntitiesDisposer->setMessageReporter(
+ $this->newMessageReporter( $reporterCallback )
+ );
+
+ return $duplicateEntitiesDisposer;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $performer
+ *
+ * @return MaintenanceLogger
+ */
+ public function newMaintenanceLogger( $performer ) {
+
+ $maintenanceLogger = new MaintenanceLogger( $performer, new ManualEntryLogger() );
+ $maintenanceLogger->setMaxNameChars( $GLOBALS['wgMaxNameChars'] );
+
+ return $maintenanceLogger;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return MessageReporter
+ */
+ public function newMessageReporter( $reporterCallback = null ) {
+
+ $messageReporter = MessageReporterFactory::getInstance()->newObservableMessageReporter();
+ $messageReporter->registerReporterCallback( $reporterCallback );
+
+ return $messageReporter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceHelper.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceHelper.php
new file mode 100644
index 00000000..1b2de4a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceHelper.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use SMW\ApplicationFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MaintenanceHelper {
+
+ /**
+ * @var array
+ */
+ private $globals = [];
+
+ /**
+ * @var array
+ */
+ private $runtime = [
+ 'start' => 0,
+ 'memory' => 0
+ ];
+
+ /**
+ * @since 2.2
+ */
+ public function initRuntimeValues() {
+ $this->runtime['start'] = microtime( true );
+ $this->runtime['memory'] = memory_get_peak_usage( false );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getRuntimeValues() {
+
+ $memory = memory_get_peak_usage( false );
+ $time = microtime( true ) - $this->runtime['start'];
+
+ $hTime = round( $time, 2 ) . ' sec';
+ $hTime .= ( $time > 60 ? ' (' . round( $time / 60, 2 ) . ' min)' : '' );
+
+ return [
+ 'time' => $time,
+ 'humanreadable-time' => $hTime,
+ 'memory-before' => $this->runtime['memory'],
+ 'memory-after' => $memory,
+ 'memory-used' => $memory - $this->runtime['memory']
+ ];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getFormattedRuntimeValues( $indent = '' ) {
+
+ $runtimeValues = $this->getRuntimeValues();
+
+ return "$indent Memory used: " . $runtimeValues['memory-used'] . "\n" .
+ "$indent Time: " . $runtimeValues['humanreadable-time'];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setGlobalToValue( $key, $value ) {
+
+ if ( !isset( $GLOBALS[$key] ) ) {
+ return;
+ }
+
+ $this->globals[$key] = $GLOBALS[$key];
+ $GLOBALS[$key] = $value;
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function reset() {
+
+ foreach ( $this->globals as $key => $value ) {
+ $GLOBALS[$key] = $value;
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ $this->runtime['start'] = 0;
+ $this->runtime['memory'] = 0;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceLogger.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceLogger.php
new file mode 100644
index 00000000..7255fb45
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/MaintenanceLogger.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use RuntimeException;
+use SMW\MediaWiki\ManualEntryLogger;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MaintenanceLogger {
+
+ /**
+ * @var string
+ */
+ private $performer = '';
+
+ /**
+ * @var ManualEntryLogger
+ */
+ private $manualEntryLogger;
+
+ /**
+ * @var integer
+ */
+ private $maxNameChars = 255;
+
+ /**
+ * @since 2.4
+ *
+ * @param string $performer
+ * @param ManualEntryLogger $manualEntryLogger
+ */
+ public function __construct( $performer, ManualEntryLogger $manualEntryLogger ) {
+ $this->performer = $performer;
+ $this->manualEntryLogger = $manualEntryLogger;
+ $this->manualEntryLogger->registerLoggableEventType( 'maintenance' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $maxNameChars
+ */
+ public function setMaxNameChars( $maxNameChars ) {
+ $this->maxNameChars = $maxNameChars;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $message
+ * @param string $target
+ */
+ public function log( $message, $target = '' ) {
+
+ if ( $target === '' ) {
+ $target = $this->performer;
+ }
+
+ // #1983
+ if ( $this->maxNameChars < strlen( $target ) ) {
+ throw new RuntimeException( 'wgMaxNameChars requires at least ' . strlen( $target ) );
+ }
+
+ $this->manualEntryLogger->log( 'maintenance', $this->performer, $target, $message );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/PropertyStatisticsRebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/PropertyStatisticsRebuilder.php
new file mode 100644
index 00000000..9ee99fa1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Maintenance/PropertyStatisticsRebuilder.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\Maintenance;
+
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\Store;
+
+/**
+ * Simple class for rebuilding property usage statistics.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Nischay Nahata
+ */
+class PropertyStatisticsRebuilder {
+
+ /**
+ * @var Store
+ */
+ private $store = null;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ $this->messageReporter = MessageReporterFactory::getInstance()->newNullMessageReporter();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function rebuild() {
+ $this->reportMessage( "\nRebulding property statistics (this may take a while) ..." );
+ $table = $this->propertyStatisticsStore->getStatisticsTable();
+
+ $this->reportMessage( "\n ... deleting `$table` content ..." );
+ $this->propertyStatisticsStore->deleteAll();
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $res = $connection->select(
+ \SMWSql3SmwIds::TABLE_NAME,
+ [ 'smw_id', 'smw_title' ],
+ [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_subobject' => ''
+ ],
+ __METHOD__
+ );
+
+ $propCount = $res->numRows();
+ $this->reportMessage( "\n ... selecting $propCount properties ...\n" );
+
+ $i = 0;
+
+ foreach ( $res as $row ) {
+
+ $i++;
+
+ $this->reportMessage(
+ "\r". sprintf( "%-47s%s", " ... updating", sprintf( "%4.0f%% (%s/%s)", round( ( $i / $propCount ) * 100 ), $i, $propCount ) )
+ );
+
+ $this->propertyStatisticsStore->insertUsageCount(
+ (int)$row->smw_id,
+ $this->getCountFormRow( $row )
+ );
+ }
+
+ $connection->freeResult( $res );
+
+ $this->reportMessage( "\n ... done.\n" );
+ }
+
+ private function getCountFormRow( $row ) {
+
+ $usageCount = 0;
+ $nullCount = 0;
+
+ foreach ( $this->store->getPropertyTables() as $propertyTable ) {
+
+ if ( $propertyTable->isFixedPropertyTable() && $propertyTable->getFixedProperty() !== $row->smw_title ) {
+ // This table cannot store values for this property
+ continue;
+ }
+
+ list( $uCount, $nCount ) = $this->getPropertyTableRowCount(
+ $propertyTable,
+ $row->smw_id
+ );
+
+ $usageCount += $uCount;
+ $nullCount += $nCount;
+ }
+
+ return [ $usageCount, $nullCount ];
+ }
+
+ private function getPropertyTableRowCount( $propertyTable, $pid ) {
+
+ $condition = [];
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $condition = [ 'p_id' => $pid ];
+ }
+
+ $tableFields = $this->store->getDataItemHandlerForDIType( $propertyTable->getDiType() )->getTableFields();
+ $tableName = $propertyTable->getName();
+
+ $usageCount = 0;
+ $nullCount = 0;
+
+ // Select all (incl. NULL since for example blob table can have a null
+ // for when only the hash field is used, substract NULL in a second step)
+ $row = $connection->selectRow(
+ $tableName,
+ 'Count(*) as count',
+ $condition,
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $usageCount = $row->count;
+ }
+
+ // Select only those that match NULL for all fields
+ foreach ( $tableFields as $field => $type ) {
+ $condition[] = "$field IS NULL";
+ }
+
+ $nRow = $connection->selectRow(
+ $tableName,
+ 'Count(*) as count',
+ $condition,
+ __METHOD__
+ );
+
+ if ( $nRow !== false ) {
+ $nullCount = $nRow->count;
+ }
+
+ if ( $usageCount > 0 ) {
+ $usageCount = $usageCount - $nullCount;
+ }
+
+ return [ $usageCount, $nullCount ];
+ }
+
+ private function progress( $propCount, $i ) {
+
+ if ( $i % 60 === 0 ) {
+ if ( $i < 1 ) {
+ return "\n";
+ }
+
+ return ' ' . round( ( $i / $propCount ) * 100 ) . ' %' . "\n";
+ }
+
+ return '.';
+ }
+
+ protected function reportMessage( $message ) {
+ $this->messageReporter->reportMessage( $message );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiQueryResultFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiQueryResultFormatter.php
new file mode 100644
index 00000000..684499a3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiQueryResultFormatter.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use InvalidArgumentException;
+use SMW\ProcessingErrorMsgHandler;
+use SMWQueryResult;
+
+/**
+ * This class handles the Api related query result formatting
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ApiQueryResultFormatter {
+
+ /**
+ * @var Integer|boolean
+ */
+ protected $continueOffset = false;
+
+ /**
+ * @var String
+ */
+ protected $type;
+
+ /**
+ * @var Boolean
+ */
+ protected $isRawMode = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param SMWQueryResult $queryResult
+ */
+ public function __construct( SMWQueryResult $queryResult ) {
+ $this->queryResult = $queryResult;
+ }
+
+ /**
+ * Sets whether the formatter requested raw data and is used in connection
+ * with ApiQueryResultFormatter::setIndexedTagName
+ *
+ * @see ApiResult::getIsRawMode
+ *
+ * @since 1.9
+ *
+ * @param boolean $isRawMode
+ */
+ public function setIsRawMode( $isRawMode ) {
+ $this->isRawMode = $isRawMode;
+ }
+
+ /**
+ * Returns an offset used for continuation support
+ *
+ * @since 1.9
+ *
+ * @return integer
+ */
+ public function getContinueOffset() {
+ return $this->continueOffset;
+ }
+
+ /**
+ * Returns the result type
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Returns formatted result
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getResult() {
+ return $this->result;
+ }
+
+ /**
+ * Result formatting
+ *
+ * @since 1.9
+ */
+ public function doFormat() {
+
+ if ( $this->queryResult->getErrors() !== [] ) {
+ $this->result = $this->formatErrors(
+ ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $this->queryResult->getErrors() )
+ );
+ } else {
+ $this->result = $this->formatResults( $this->queryResult->toArray() );
+
+ if ( $this->queryResult->hasFurtherResults() ) {
+ $this->continueOffset = $this->result['meta']['count'] + $this->result['meta']['offset'];
+ }
+ }
+ }
+
+ /**
+ * Formatting a result array to support JSON/XML standards
+ *
+ * @since 1.9
+ *
+ * @param array $queryResult
+ *
+ * @return array
+ */
+ protected function formatResults( array $queryResult ) {
+
+ $this->type = 'query';
+ $results = [];
+
+ if ( !$this->isRawMode ) {
+ return $queryResult;
+ }
+
+ foreach ( $queryResult['results'] as $subjectName => $subject ) {
+ $serialized = [];
+
+ foreach ( $subject as $key => $value ) {
+
+ if ( $key === 'printouts' ) {
+ $printouts = [];
+
+ foreach ( $subject['printouts'] as $property => $values ) {
+
+ if ( (array)$values === $values ) {
+ $this->setIndexedTagName( $values, 'value' );
+ $printouts[] = array_merge( [ 'label' => $property ], $values );
+ }
+
+ }
+
+ $serialized['printouts'] = $printouts;
+ $this->setIndexedTagName( $serialized['printouts'], 'property' );
+
+ } else {
+ $serialized[$key] = $value;
+ }
+ }
+
+ $results[] = $serialized;
+ }
+
+ if ( $results !== [] ) {
+ $queryResult['results'] = $results;
+ $this->setIndexedTagName( $queryResult['results'], 'subject' );
+ }
+
+ $this->setIndexedTagName( $queryResult['printrequests'], 'printrequest' );
+ $this->setIndexedTagName( $queryResult['meta'], 'meta' );
+
+ return $queryResult;
+ }
+
+ /**
+ * Formatting an error array in order to support JSON/XML
+ *
+ * @since 1.9
+ *
+ * @param array $errors
+ *
+ * @return array
+ */
+ protected function formatErrors( array $errors ) {
+
+ $this->type = 'error';
+ $result['query'] = $errors;
+
+ $this->setIndexedTagName( $result['query'], 'info' );
+
+ return $result;
+ }
+
+ /**
+ * Add '_element' to an array
+ *
+ * @note Copied from ApiResult::setIndexedTagName to avoid having a
+ * constructor injection in order to be able to access this method
+ *
+ * @see ApiResult::setIndexedTagName
+ *
+ * @since 1.9
+ *
+ * @param array &$arr
+ * @param string $tag
+ */
+ public function setIndexedTagName( &$arr, $tag = null ) {
+
+ if ( !$this->isRawMode ) {
+ return;
+ }
+
+ if ( $arr === null || $tag === null || !is_array( $arr ) || is_array( $tag ) ) {
+ throw new InvalidArgumentException( "{$tag} was incompatible with the requirements" );
+ }
+
+ $arr['_element'] = $tag;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiRequestParameterFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiRequestParameterFormatter.php
new file mode 100644
index 00000000..7dd1165a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/ApiRequestParameterFormatter.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use SMW\DataValueFactory;
+use SMW\Options;
+use SMW\Query\PrintRequest;
+
+/**
+ * This class handles Api related request parameter formatting
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+final class ApiRequestParameterFormatter {
+
+ /**
+ * @var array
+ */
+ protected $requestParameters = [];
+
+ /**
+ * @var ObjectDictionary
+ */
+ protected $results = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param array $requestParameters
+ */
+ public function __construct( array $requestParameters ) {
+ $this->requestParameters = $requestParameters;
+ }
+
+ /**
+ * Return formatted request parameters for the AskApi
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getAskApiParameters() {
+
+ if ( $this->results === null ) {
+ $this->results = isset( $this->requestParameters['query'] ) ? preg_split( "/(?<=[^\|])\|(?=[^\|])/", $this->requestParameters['query'] ) : [];
+ }
+
+ return $this->results;
+ }
+
+ /**
+ * Return formatted request parameters AskArgsApi
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getAskArgsApiParameter( $key ) {
+
+ if ( $this->results === null ) {
+ $this->results = $this->formatAskArgs();
+ }
+
+ return $this->results->get( $key );
+ }
+
+ /**
+ * Return formatted request parameters
+ *
+ * @since 1.9
+ *
+ * @return ObjectDictionary
+ */
+ protected function formatAskArgs() {
+
+ $result = new Options();
+
+ // Set defaults
+ $result->set( 'conditions', [] );
+ $result->set( 'printouts', [] );
+ $result->set( 'parameters', [] );
+
+ if ( isset( $this->requestParameters['parameters'] ) && is_array( $this->requestParameters['parameters'] ) ) {
+ $result->set( 'parameters', $this->formatParameters() );
+ }
+
+ if ( isset( $this->requestParameters['conditions'] ) && is_array( $this->requestParameters['conditions'] ) ) {
+ $result->set( 'conditions', implode( ' ', array_map( 'self::formatConditions', $this->requestParameters['conditions'] ) ) );
+ }
+
+ if ( isset( $this->requestParameters['printouts'] ) && is_array( $this->requestParameters['printouts'] ) ) {
+ $result->set( 'printouts', array_map( 'self::formatPrintouts', $this->requestParameters['printouts'] ) );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Format parameters
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function formatParameters() {
+
+ $parameters = [];
+
+ foreach ( $this->requestParameters['parameters'] as $param ) {
+ $parts = explode( '=', $param, 2 );
+
+ if ( count( $parts ) == 2 ) {
+ $parameters[$parts[0]] = $parts[1];
+ }
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Format conditions
+ *
+ * @since 1.9
+ *
+ * @param string $condition
+ *
+ * @return string
+ */
+ protected function formatConditions( $condition ) {
+ return "[[$condition]]";
+ }
+
+ /**
+ * Format printout and returns a SMWPrintRequest object
+ *
+ * @since 1.9
+ *
+ * @param string $printout
+ *
+ * @return PrintRequest
+ */
+ protected function formatPrintouts( $printout ) {
+ return new PrintRequest(
+ PrintRequest::PRINT_PROP,
+ $printout,
+ DataValueFactory::getInstance()->newPropertyValueByLabel( $printout )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Ask.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Ask.php
new file mode 100644
index 00000000..b67d64d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Ask.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMWQueryProcessor;
+
+/**
+ * API module to query SMW by providing a query in the ask language.
+ *
+ * @ingroup Api
+ *
+ * @license GNU GPL v2+
+ * @since 1.6.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class Ask extends Query {
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ $parameterFormatter = new ApiRequestParameterFormatter( $this->extractRequestParams() );
+ $outputFormat = 'json';
+
+ list( $queryString, $parameters, $printouts ) = SMWQueryProcessor::getComponentsFromFunctionParams( $parameterFormatter->getAskApiParameters(), false );
+
+ $queryResult = $this->getQueryResult( $this->getQuery(
+ $queryString,
+ $printouts,
+ $parameters
+ ) );
+
+ if ( $this->getMain()->getPrinter() instanceof \ApiFormatXml ) {
+ $outputFormat = 'xml';
+ }
+
+ if ( isset( $params['api_version'] ) ) {
+ $queryResult->setSerializerVersion( $params['api_version'] );
+ }
+
+ $this->addQueryResult( $queryResult, $outputFormat );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'query' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true,
+ ],
+ 'api_version' => [
+ ApiBase::PARAM_TYPE => [ 2, 3 ],
+ ApiBase::PARAM_DFLT => 2,
+ ApiBase::PARAM_HELP_MSG => 'apihelp-ask-parameter-api-version',
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'query' => 'The query string in ask-language'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module to query SMW by providing a query in the ask language.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=ask&query=[[Modification%20date::%2B]]|%3FModification%20date|sort%3DModification%20date|order%3Ddesc',
+ 'api.php?action=ask&query=[[Modification%20date::%2B]]|limit%3D5|offset%3D1'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . '-' . SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/AskArgs.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/AskArgs.php
new file mode 100644
index 00000000..e716694d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/AskArgs.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+
+/**
+ * API module to query SMW by providing a query specified as
+ * a list of conditions, printouts and parameters.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class AskArgs extends Query {
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ $parameterFormatter = new ApiRequestParameterFormatter( $this->extractRequestParams() );
+ $outputFormat = 'json';
+
+ $queryResult = $this->getQueryResult( $this->getQuery(
+ $parameterFormatter->getAskArgsApiParameter( 'conditions' ),
+ $parameterFormatter->getAskArgsApiParameter( 'printouts' ),
+ $parameterFormatter->getAskArgsApiParameter( 'parameters' )
+ ) );
+
+ if ( $this->getMain()->getPrinter() instanceof \ApiFormatXml ) {
+ $outputFormat = 'xml';
+ }
+
+ if ( isset( $params['api_version'] ) ) {
+ $queryResult->setSerializerVersion( $params['api_version'] );
+ }
+
+ $this->addQueryResult( $queryResult, $outputFormat );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'conditions' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_REQUIRED => true,
+ ],
+ 'printouts' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_ISMULTI => true,
+ ],
+ 'parameters' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_ISMULTI => true,
+ ],
+ 'api_version' => [
+ ApiBase::PARAM_TYPE => [ 2, 3 ],
+ ApiBase::PARAM_DFLT => 2,
+ ApiBase::PARAM_HELP_MSG => 'apihelp-ask-parameter-api-version',
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'conditions' => 'The query conditions, i.e. the requirements for a subject to be included',
+ 'printouts' => 'The query printouts, i.e. the properties to show per subject',
+ 'parameters' => 'The query parameters, i.e. all non-condition and non-printout arguments',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module to query SMW by providing a query specified as a list of conditions, printouts and parameters.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=askargs&conditions=Modification%20date::%2B&printouts=Modification%20date&parameters=|sort%3DModification%20date|order%3Ddesc',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . '-' . SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse.php
new file mode 100644
index 00000000..68d36817
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse.php
@@ -0,0 +1,396 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMW\ApplicationFactory;
+use SMW\Exception\RedirectTargetUnresolvableException;
+use SMW\Exception\ParameterNotFoundException;
+use SMW\MediaWiki\Api\Browse\ArticleAugmentor;
+use SMW\MediaWiki\Api\Browse\ArticleLookup;
+use SMW\MediaWiki\Api\Browse\SubjectLookup;
+use SMW\MediaWiki\Api\Browse\CachingLookup;
+use SMW\MediaWiki\Api\Browse\ListAugmentor;
+use SMW\MediaWiki\Api\Browse\ListLookup;
+use SMW\MediaWiki\Api\Browse\PValueLookup;
+use SMW\MediaWiki\Api\Browse\PSubjectLookup;
+
+/**
+ * Module to support selected browse activties including:
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Browse extends ApiBase {
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ $parameters = json_decode( $params['params'], true );
+ $res = [];
+
+ if ( json_last_error() !== JSON_ERROR_NONE || !is_array( $parameters ) ) {
+
+ // 1.29+
+ if ( method_exists($this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-api-invalid-parameters', 'JSON: '. json_last_error_msg() ] );
+ } else {
+ $this->dieUsage( 'JSON: '. json_last_error_msg(), 'smw-api-invalid-parameters' );
+ }
+ }
+
+ if ( $params['browse'] === 'category' ) {
+ $res = $this->callListLookup( NS_CATEGORY, $parameters );
+ }
+
+ if ( $params['browse'] === 'property' ) {
+ $res = $this->callListLookup( SMW_NS_PROPERTY, $parameters );
+ }
+
+ if ( $params['browse'] === 'concept' ) {
+ $res = $this->callListLookup( SMW_NS_CONCEPT, $parameters );
+ }
+
+ if ( $params['browse'] === 'pvalue' ) {
+ $res = $this->callPValueLookup( $parameters );
+ }
+
+ if ( $params['browse'] === 'psubject' ) {
+ $res = $this->callPSubjectLookup( $parameters );
+ }
+
+ if ( $params['browse'] === 'subject' ) {
+ $res = $this->callSubjectLookup( $parameters );
+ }
+
+ if ( $params['browse'] === 'page' ) {
+ $res = $this->callPageLookup( $parameters );
+ }
+
+ $result = $this->getResult();
+
+ foreach ( $res as $key => $value ) {
+
+ if ( $key === 'query' && is_array( $value ) ) {
+
+ // For those items that start with _xyz as in _MDAT
+ // https://www.mediawiki.org/wiki/API:JSON_version_2
+ // " ... can indicate that a property beginning with an underscore ..."
+ foreach ( $value as $k => $v ) {
+ if ( $k{0} === '_' ) {
+ $result->addPreserveKeysList( 'query', $k );
+ }
+ }
+ }
+
+ $result->addValue( null, $key, $value );
+ }
+ }
+
+ private function callListLookup( $ns, $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = CachingLookup::CACHE_TTL;
+
+ if ( isset( $cacheUsage['api.browse'] ) ) {
+ $cacheTTL = $cacheUsage['api.browse'];
+ }
+
+ $store = $applicationFactory->getStore();
+
+ // We explicitly want the SQLStore here to avoid
+ // "Call to undefined method SMW\SPARQLStore\SPARQLStore::getSQLOptions() ..."
+ // since we don't use those methods anywher else other than the SQLStore
+ if ( !is_a( $store, '\SMW\SQLStore\SQLStore') ) {
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+ }
+
+ $listLookup = new ListLookup(
+ $store,
+ new ListAugmentor( $store )
+ );
+
+ $cachingLookup = new CachingLookup(
+ $applicationFactory->getCache(),
+ $listLookup
+ );
+
+ $cachingLookup->setCacheTTL(
+ $cacheTTL
+ );
+
+ $parameters['ns'] = $ns;
+
+ return $cachingLookup->lookup(
+ $parameters
+ );
+ }
+
+ private function callPValueLookup( $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = CachingLookup::CACHE_TTL;
+
+ if ( isset( $cacheUsage['api.browse.pvalue'] ) ) {
+ $cacheTTL = $cacheUsage['api.browse.pvalue'];
+ }
+
+ $store = $applicationFactory->getStore();
+
+ // We explicitly want the SQLStore here to avoid
+ // "Call to undefined method SMW\SPARQLStore\SPARQLStore::getSQLOptions() ..."
+ // since we don't use those methods anywher else other than the SQLStore
+ if ( !is_a( $store, '\SMW\SQLStore\SQLStore') ) {
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+ }
+
+ $listLookup = new PValueLookup(
+ $store
+ );
+
+ $cachingLookup = new CachingLookup(
+ $applicationFactory->getCache(),
+ $listLookup
+ );
+
+ $cachingLookup->setCacheTTL(
+ $cacheTTL
+ );
+
+ return $cachingLookup->lookup(
+ $parameters
+ );
+ }
+
+ private function callPSubjectLookup( $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = CachingLookup::CACHE_TTL;
+
+ if ( isset( $cacheUsage['api.browse.psubject'] ) ) {
+ $cacheTTL = $cacheUsage['api.browse.psubject'];
+ }
+
+ $store = $applicationFactory->getStore();
+
+ // We explicitly want the SQLStore here to avoid
+ // "Call to undefined method SMW\SPARQLStore\SPARQLStore::getSQLOptions() ..."
+ // since we don't use those methods anywher else other than the SQLStore
+ if ( !is_a( $store, '\SMW\SQLStore\SQLStore') ) {
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+ }
+
+ $listLookup = new PSubjectLookup(
+ $store
+ );
+
+ $cachingLookup = new CachingLookup(
+ $applicationFactory->getCache(),
+ $listLookup
+ );
+
+ $cachingLookup->setCacheTTL(
+ $cacheTTL
+ );
+
+ return $cachingLookup->lookup(
+ $parameters
+ );
+ }
+
+ private function callPageLookup( $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = CachingLookup::CACHE_TTL;
+
+ if ( isset( $cacheUsage['api.browse'] ) ) {
+ $cacheTTL = $cacheUsage['api.browse'];
+ }
+
+ $connection = $applicationFactory->getStore()->getConnection( 'mw.db' );
+
+ $articleLookup = new ArticleLookup(
+ $connection,
+ new ArticleAugmentor(
+ $applicationFactory->create( 'TitleFactory' )
+ )
+ );
+
+ $cachingLookup = new CachingLookup(
+ $applicationFactory->getCache(),
+ $articleLookup
+ );
+
+ $cachingLookup->setCacheTTL(
+ $cacheTTL
+ );
+
+ return $cachingLookup->lookup(
+ $parameters
+ );
+ }
+
+ private function callSubjectLookup( $parameters ) {
+
+ $subjectLookup = new SubjectLookup(
+ ApplicationFactory::getInstance()->getStore()
+ );
+
+ try {
+ $res = $subjectLookup->lookup( $parameters );
+ } catch ( RedirectTargetUnresolvableException $e ) {
+ // 1.29+
+ if ( method_exists( $this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-redirect-target-unresolvable', $e->getMessage() ] );
+ } else {
+ $this->dieUsage( $e->getMessage(), 'redirect-target-unresolvable' );
+ }
+ } catch ( ParameterNotFoundException $e ) {
+ // 1.29+
+ if ( method_exists( $this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-parameter-missing', $e->getName() ] );
+ } else {
+ $this->dieUsage( $e->getName(), 'smw-parameter-missing' );
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'browse' => [
+ ApiBase::PARAM_REQUIRED => true,
+ ApiBase::PARAM_TYPE => [
+
+ // List, browse of properties
+ 'property',
+
+ // List, browse of categories
+ 'category',
+
+ // List, browse of concepts
+ 'concept',
+
+ // List, browse of articles, pages (mediawiki)
+ 'page',
+
+ // Equivalent to Store::getPropertyValues
+ 'pvalue',
+
+ // Equivalent to Store::getPropertySubjects
+ 'psubject',
+
+ // Equivalent to Special:Browse
+ 'subject',
+ ]
+ ],
+ 'params' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => true,
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'browse' => 'Specifies the type of browse activity',
+ 'params' => 'JSON encoded parameters containing required and optional fields and depend on the selected browse type'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module to support browse activties for different entity types in Semantic MediaWiki.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=smwbrowse&browse=property&params={ "limit": 10, "offset": 0, "search": "Date" }',
+ 'api.php?action=smwbrowse&browse=property&params={ "limit": 10, "offset": 0, "search": "Date", "description": true }',
+ 'api.php?action=smwbrowse&browse=property&params={ "limit": 10, "offset": 0, "search": "Date", "description": true, "prefLabel": true }',
+ 'api.php?action=smwbrowse&browse=property&params={ "limit": 10, "offset": 0, "search": "Date", "description": true, "prefLabel": true, "usageCount": true }',
+ 'api.php?action=smwbrowse&browse=pvalue&params={ "limit": 10, "offset": 0, "property" : "Foo", "search": "Bar" }',
+ 'api.php?action=smwbrowse&browse=psubject&params={ "limit": 10, "offset": 0, "property" : "Foo", "value" : "Bar", "search": "foo" }',
+ 'api.php?action=smwbrowse&browse=category&params={ "limit": 10, "offset": 0, "search": "" }',
+ 'api.php?action=smwbrowse&browse=category&params={ "limit": 10, "offset": 0, "search": "Date" }',
+ 'api.php?action=smwbrowse&browse=concept&params={ "limit": 10, "offset": 0, "search": "" }',
+ 'api.php?action=smwbrowse&browse=concept&params={ "limit": 10, "offset": 0, "search": "Date" }',
+ 'api.php?action=smwbrowse&browse=page&params={ "limit": 10, "offset": 0, "search": "Main" }',
+ 'api.php?action=smwbrowse&browse=page&params={ "limit": 10, "offset": 0, "search": "Main", "fullText": true, "fullURL": true }',
+ 'api.php?action=smwbrowse&browse=subject&params={ "subject": "Main page", "ns" :0, "iw": "", "subobject": "" }',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . ':' . SMW_VERSION;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getHelpUrls() {
+ return 'https://www.semantic-mediawiki.org/wiki/Help:API';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleAugmentor.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleAugmentor.php
new file mode 100644
index 00000000..aa58bdeb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleAugmentor.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\TitleFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleAugmentor {
+
+ /**
+ * @var TitleFactory
+ */
+ private $titleFactory;
+
+ /**
+ * @since 3.0
+ *
+ * @param TitleFactory $titleFactory
+ */
+ public function __construct( TitleFactory $titleFactory ) {
+ $this->titleFactory = $titleFactory;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$res
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function augment( array &$res, array $parameters ) {
+
+ if ( !isset( $res['query'] ) && $res['query'] === [] ) {
+ return;
+ }
+
+ if ( isset( $parameters['fullText' ] ) || isset( $parameters['fullURL' ] ) ) {
+
+ foreach ( $res['query'] as $key => &$value ) {
+
+ $title = $this->titleFactory->newFromID( $value['id'] );
+
+ if ( isset( $parameters['fullText' ] ) ) {
+ $value['fullText'] = $title->getFullText();
+ }
+
+ if ( isset( $parameters['fullURL' ] ) ) {
+ $value['fullURL'] = $title->getFullURL();
+ }
+ }
+ }
+
+ // Remove the internal ID, no external consumer should rely on it
+ foreach ( $res['query'] as $key => &$value ) {
+ unset( $value['id'] );
+ }
+
+ return $res;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleLookup.php
new file mode 100644
index 00000000..1a8893e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ArticleLookup.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\Localizer;
+use SMW\MediaWiki\Database;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleLookup extends Lookup {
+
+ const VERSION = 1;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var ArticleAugmentor
+ */
+ private $articleAugmentor;
+
+ /**
+ * @since 3.0
+ *
+ * @param Database $connection
+ * @param ArticleAugmentor $articleAugmentor
+ */
+ public function __construct( Database $connection, ArticleAugmentor $articleAugmentor ) {
+ $this->connection = $connection;
+ $this->articleAugmentor = $articleAugmentor;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ public function getVersion() {
+ return 'ArticleLookup:' . self::VERSION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ $limit = 50;
+ $offset = 0;
+ $namespace = null;
+
+ if ( isset( $parameters['limit'] ) ) {
+ $limit = (int)$parameters['limit'];
+ }
+
+ if ( isset( $parameters['offset'] ) ) {
+ $offset = (int)$parameters['offset'];
+ }
+
+ if ( isset( $parameters['namespace'] ) ) {
+ $namespace = $parameters['namespace'];
+ }
+
+ if ( isset( $parameters['search'] ) ) {
+ list( $list, $continueOffset ) = $this->search( $limit, $offset, $parameters['search'], $namespace );
+ }
+
+ // Changing this output format requires to set a new version
+ $res = [
+ 'query' => $list,
+ 'query-continue-offset' => $continueOffset,
+ 'version' => self::VERSION,
+ 'meta' => [
+ 'type' => 'article',
+ 'limit' => $limit,
+ 'count' => count( $list )
+ ]
+ ];
+
+ $this->articleAugmentor->augment(
+ $res,
+ $parameters
+ );
+
+ return $res;
+ }
+
+ private function search( $limit, $offset, $search, $namespace = null ) {
+
+ $search = $this->getSearchTerm( $search, $namespace );
+
+ $escapeChar = '`';
+ $list = [];
+
+ $search = str_replace(
+ [ ' ', $escapeChar, '%', '_' ],
+ [ '_', "{$escapeChar}{$escapeChar}", "{$escapeChar}%", "{$escapeChar}_" ],
+ $search
+ );
+
+ $limit = $limit + 1;
+ $conditions = '';
+
+ $fields = [
+ 'page_id',
+ 'page_namespace',
+ 'page_title'
+ ];
+
+ $options = [
+ 'LIMIT' => $limit,
+ 'OFFSET' => $offset,
+ 'ORDER BY' => "page_title,page_namespace"
+ ];
+
+ $conds = [
+ '%' . $search . '%',
+ '%' . ucfirst( $search ) . '%',
+ '%' . strtoupper( $search ) . '%',
+ '%' . strtolower( $search ) . '%'
+ ];
+
+ foreach ( $conds as $s ) {
+ $conditions .= ( $conditions !== '' ? ' OR ' : '' ) . "page_title LIKE ";
+ $conditions .= $this->connection->addQuotes( $s );
+ $conditions .= ' ESCAPE ' . $this->connection->addQuotes( $escapeChar );
+ }
+
+ if ( $namespace !== null ) {
+ $conditions = 'page_namespace=' . $this->connection->addQuotes( $namespace ) . ' AND ('. $conditions. ')';
+ }
+
+ $res = $this->connection->select(
+ [ 'page'],
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ $count = 0;
+ $continueOffset = 0;
+
+ foreach ( $res as $row ) {
+
+ $key = $row->page_title;
+ $count++;
+
+ if ( $count > ( $limit - 1 ) ) {
+ $continueOffset = $offset + $limit;
+ break;
+ }
+
+ $label = str_replace( '_', ' ', $row->page_title );
+
+ $list[$key.'#'.$row->page_namespace] = [
+ // Only keep the ID as internal field which is
+ // removed by the Augmentor
+ 'id' => $row->page_id,
+ 'label' => $label,
+ 'key' => $key,
+ 'ns' => $row->page_namespace
+ ];
+ }
+
+ return [ $list, $continueOffset ];
+ }
+
+ private function getSearchTerm( $search, &$namespace = null ) {
+
+ if ( strpos( $search, ':' ) !== false ) {
+ list( $ns, $term ) = explode( ':', $search );
+
+ if ( ( $namespace = Localizer::getInstance()->getNamespaceIndexByName( $ns ) ) !== false ) {
+ $search = $term;
+ } else {
+ $namespace = null;
+ }
+ }
+
+ return $search;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/CachingLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/CachingLookup.php
new file mode 100644
index 00000000..923ef6b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/CachingLookup.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use Onoi\Cache\Cache;
+use SMW\Utils\Timer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingLookup {
+
+ const CACHE_NAMESPACE = 'smw:api:browse';
+ const CACHE_TTL = 3600;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Lookup
+ */
+ private $lookup;
+
+ /**
+ * @var integer|boolean
+ */
+ private $cacheTTL;
+
+ /**
+ * @since 3.0
+ *
+ * @param Cache $cache
+ * @param Lookup $lookup
+ */
+ public function __construct( Cache $cache, Lookup $lookup ) {
+ $this->cache = $cache;
+ $this->lookup = $lookup;
+ $this->cacheTTL = self::CACHE_TTL;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer|boolean $cacheTTL
+ */
+ public function setCacheTTL( $cacheTTL ) {
+ $this->cacheTTL = $cacheTTL;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ Timer::start( __METHOD__ );
+
+ $hash = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ $parameters,
+ $this->lookup->getVersion()
+ ]
+ );
+
+ if ( $this->cacheTTL !== false && ( $res = $this->cache->fetch( $hash ) ) !== false ) {
+ $res['meta']['isFromCache'] = true;
+ $res['meta']['queryTime'] = Timer::getElapsedTime( __METHOD__, 5 );
+ return $res;
+ }
+
+ $res = $this->lookup->lookup(
+ $parameters
+ );
+
+ if ( $this->cacheTTL !== false ) {
+ $this->cache->save( $hash, $res, $this->cacheTTL );
+ }
+
+ $res['meta']['isFromCache'] = false;
+ $res['meta']['queryTime'] = Timer::getElapsedTime( __METHOD__, 5 );
+
+ return $res;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListAugmentor.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListAugmentor.php
new file mode 100644
index 00000000..e02aa118
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListAugmentor.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListAugmentor {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$res
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function augment( array &$res, array $parameters ) {
+
+ if ( !isset( $res['query'] ) && $res['query'] === [] ) {
+ return;
+ }
+
+ $type = null;
+ $lang = 'en';
+
+ if ( isset( $res['meta']['type'] ) ) {
+ $type = $res['meta']['type'];
+ }
+
+ if ( isset( $parameters['lang'] ) ) {
+ $lang = $parameters['lang'];
+ }
+
+ if ( is_string( $lang ) ) {
+ $lang = [ $lang ];
+ }
+
+ if ( $type === 'property' && isset( $parameters['description' ] ) ) {
+ $this->addPropertyDescription( $res, $lang );
+ }
+
+ if ( $type === 'property' && isset( $parameters['prefLabel' ] ) ) {
+ $this->addPreferredPropertyLabel( $res, $lang );
+ }
+
+ if ( $type === 'property' && isset( $parameters['usageCount' ] ) ) {
+ $this->addUsageCount( $res );
+ }
+
+ // Remove the internal ID, no external consumer should rely on it
+ foreach ( $res['query'] as $key => &$value ) {
+ unset( $value['id'] );
+ }
+
+ return $res;
+ }
+
+ private function addUsageCount( &$res ) {
+
+ $list = $res['query'];
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ foreach ( $list as $key => $value ) {
+
+ $row = $db->selectRow(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [ 'usage_count' ],
+ [
+ 'p_id' => $value['id']
+ ],
+ __METHOD__
+ );
+
+ $list[$key] = $value + [
+ 'usageCount' => $row->usage_count
+ ];
+ }
+
+ $res['query'] = $list;
+ }
+
+ private function addPreferredPropertyLabel( &$res, array $languageCodes ) {
+
+ $list = $res['query'];
+
+ foreach ( $list as $key => $value ) {
+ $property = new DIProperty( $key );
+ $prefLabel = [];
+
+ foreach ( $languageCodes as $code ) {
+ $prefLabel[$code] = $property->getPreferredLabel( $code );
+ }
+
+ $list[$key] = $value + [
+ 'prefLabel' => $prefLabel
+ ];
+ }
+
+ $res['query'] = $list;
+ }
+
+ private function addPropertyDescription( &$res, array $languageCodes ) {
+
+ $list = $res['query'];
+ $propertySpecificationLookup = ApplicationFactory::getInstance()->getPropertySpecificationLookup();
+
+ foreach ( $list as $key => $value ) {
+ $property = new DIProperty( $key );
+ $description = [];
+
+ foreach ( $languageCodes as $code ) {
+ $description[$code] = $propertySpecificationLookup->getPropertyDescriptionByLanguageCode( $property, $code );
+ }
+
+ $list[$key] = $value + [
+ 'description' => $description
+ ];
+ }
+
+ $res['query'] = $list;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListLookup.php
new file mode 100644
index 00000000..690035c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/ListLookup.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use Exception;
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMW\StringCondition;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListLookup extends Lookup {
+
+ const VERSION = 1;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var ListAugmentor
+ */
+ private $listAugmentor;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store, ListAugmentor $listAugmentor ) {
+ $this->store = $store;
+ $this->listAugmentor = $listAugmentor;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ public function getVersion() {
+ return 'ListLookup:' . self::VERSION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ $requestOptions = $this->newRequestOptions(
+ $parameters
+ );
+
+ $limit = $requestOptions->getLimit();
+ $list = [];
+ $continueOffset = 0;
+
+ // Increase by one to look ahead
+ $requestOptions->setLimit( $limit + 1 );
+ $ns = isset( $parameters['ns'] ) ? $parameters['ns'] : '';
+
+ switch ( $ns ) {
+ case NS_CATEGORY:
+ $type = 'category';
+ break;
+ case SMW_NS_PROPERTY:
+ $type = 'property';
+ break;
+ case SMW_NS_CONCEPT:
+ $type = 'concept';
+ break;
+ default:
+ $type = 'unlisted';
+ break;
+ }
+
+ if ( isset( $parameters['search'] ) ) {
+ list( $res, $continueOffset ) = $this->fetchFromTable( $ns, $requestOptions, $parameters );
+ }
+
+ // Changing this output format requires to set a new version
+ $res = [
+ 'query' => $res,
+ 'query-continue-offset' => $continueOffset,
+ 'version' => self::VERSION,
+ 'meta' => [
+ 'type' => $type,
+ 'limit' => $limit,
+ 'count' => count( $res )
+ ]
+ ];
+
+ $this->listAugmentor->augment(
+ $res,
+ $parameters
+ );
+
+ return $res;
+ }
+
+ private function newRequestOptions( $parameters ) {
+
+ $limit = 50;
+ $offset = 0;
+ $search = '';
+
+ if ( isset( $parameters['limit'] ) ) {
+ $limit = (int)$parameters['limit'];
+ }
+
+ if ( isset( $parameters['offset'] ) ) {
+ $offset = (int)$parameters['offset'];
+ }
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->setLimit( $limit );
+ $requestOptions->setOffset( $offset );
+
+ if ( isset( $parameters['search'] ) && isset( $parameters['strict'] ) ) {
+ $search = $parameters['search'];
+
+ if ( $search !== '' && $search{0} !== '_' ) {
+ $search = str_replace( "_", " ", $search );
+ }
+
+ $requestOptions->addStringCondition(
+ $search,
+ StringCondition::COND_EQ
+ );
+
+ } elseif ( isset( $parameters['search'] ) ) {
+ $search = $parameters['search'];
+
+ if ( $search !== '' && $search{0} !== '_' ) {
+ $search = str_replace( "_", " ", $search );
+ }
+
+ $requestOptions->addStringCondition(
+ $search,
+ StringCondition::STRCOND_MID
+ );
+
+ // Disjunctive condition to allow for auto searches to match foaf OR Foaf
+ $requestOptions->addStringCondition(
+ ucfirst( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ // Allow something like FOO to match the search string `foo`
+ $requestOptions->addStringCondition(
+ strtoupper( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ $requestOptions->addStringCondition(
+ strtolower( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+ }
+
+ return $requestOptions;
+ }
+
+ private function fetchFromTable( $ns, $requestOptions, $parameters ) {
+
+ $limit = $requestOptions->getLimit() - 1;
+ $list = [];
+ $options = [];
+
+ $fields = [
+ 'smw_id',
+ 'smw_title'
+ ];
+
+ // the query needs to do the filtering of internal properties, else LIMIT is wrong
+ if ( isset( $parameters['sort'] ) ) {
+ $options = $this->store->getSQLOptions( $requestOptions, 'smw_sort' );
+ $fields[] = 'smw_sort';
+ }
+
+ $conditions = [
+ 'smw_namespace' => $ns,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ if ( ( $cond = $this->store->getSQLConditions( $requestOptions, '', 'smw_sortkey', false ) ) !== '' ) {
+ $conditions[] = $cond;
+ $fields[] = 'smw_sortkey';
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $res = $connection->select(
+ $connection->tableName( SQLStore::ID_TABLE ),
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ $count = 0;
+ $continueOffset = 0;
+
+ foreach ( $res as $row ) {
+
+ $key = $row->smw_title;
+ $count++;
+
+ if ( $count > $limit ) {
+ $continueOffset = $requestOptions->getOffset() + $limit;
+ break;
+ }
+
+ if ( $ns === SMW_NS_PROPERTY ) {
+ try {
+ $label = DIProperty::newFromUserLabel( $row->smw_title )->getLabel();
+ } catch( Exception $e ) {
+ continue;
+ }
+
+ } else {
+ $label = str_replace( '_', ' ', $row->smw_title );
+ }
+
+ $list[$key] = [
+ // Only keep the ID as internal field which is
+ // removed by the Augmentor
+ 'id' => $row->smw_id,
+ 'label' => $label,
+ 'key' => $key
+ ];
+ }
+
+ return [ $list, $continueOffset ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/Lookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/Lookup.php
new file mode 100644
index 00000000..c54e413b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/Lookup.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+abstract class Lookup {
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ abstract public function getVersion();
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ abstract public function lookup( array $parameters );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PSubjectLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PSubjectLookup.php
new file mode 100644
index 00000000..2828755a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PSubjectLookup.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\DIProperty;
+use Exception;
+use SMW\Store;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMW\StringCondition;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PSubjectLookup extends Lookup {
+
+ const VERSION = 1;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ public function getVersion() {
+ return __METHOD__ . self::VERSION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ $limit = 20;
+ $offset = 0;
+
+ if ( isset( $parameters['limit'] ) ) {
+ $limit = (int)$parameters['limit'];
+ }
+
+ if ( isset( $parameters['offset'] ) ) {
+ $offset = (int)$parameters['offset'];
+ }
+
+ $list = [];
+ $continueOffset = 0;
+ $property = null;
+ $value = null;
+
+ if ( isset( $parameters['property'] ) ) {
+ $property = $parameters['property'];
+
+ // Get the last which represents the final output
+ // Foo.Bar.Foobar.Baz
+ if ( strpos( $property, '.' ) !== false ) {
+ $chain = explode( '.', $property );
+ $property = array_pop( $chain );
+ }
+ }
+
+ if ( isset( $parameters['value'] ) ) {
+ $value = $parameters['value'];
+ }
+
+ if ( $property === '' || $property === null ) {
+ return [];
+ }
+
+ list( $list, $continueOffset ) = $this->findPropertySubjects(
+ $property,
+ $value,
+ $limit,
+ $offset,
+ $parameters
+ );
+
+ // Changing this output format requires to set a new version
+ $res = [
+ 'query' => $list,
+ 'query-continue-offset' => $continueOffset,
+ 'version' => self::VERSION,
+ 'meta' => [
+ 'type' => 'psubject',
+ 'limit' => $limit,
+ 'count' => count( $list )
+ ]
+ ];
+
+ return $res;
+ }
+
+ private function findPropertySubjects( $property, $value, $limit, $offset, $parameters ) {
+
+ $list = [];
+ $dataItem = null;
+
+ $property = DIProperty::newFromUserLabel( $property );
+
+ if ( $value !== '' && $value !== null ) {
+ $dataItem = DataValueFactory::getInstance()->newDataValueByProperty( $property, $value )->getDataItem();
+ }
+
+ $continueOffset = 0;
+ $count = 0;
+ $requestOptions = $this->newRequestOptions( $parameters );
+
+ $res = $this->store->getPropertySubjects(
+ $property,
+ $dataItem,
+ $requestOptions
+ );
+
+ foreach ( $res as $dataItem ) {
+
+ if ( !$dataItem instanceof DIWikiPage ) {
+ continue;
+ }
+
+ if ( isset( $parameters['title-prefix'] ) && (bool)$parameters['title-prefix'] === false ) {
+ $list[] = $dataItem->getTitle()->getText();
+ } else {
+ $list[] = $dataItem->getTitle()->getPrefixedText();
+ }
+ }
+
+ if ( $this->is_iterable( $res ) ) {
+ $count = count( $res );
+ }
+
+ if ( $count > $limit ) {
+ $continueOffset = $offset + $count;
+ array_pop( $list );
+ }
+
+ return [ $list, $continueOffset ];
+ }
+
+ private function newRequestOptions( $parameters ) {
+
+ $limit = 20;
+ $offset = 0;
+ $search = '';
+
+ if ( isset( $parameters['limit'] ) ) {
+ $limit = (int)$parameters['limit'];
+ }
+
+ if ( isset( $parameters['offset'] ) ) {
+ $offset = (int)$parameters['offset'];
+ }
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->setLimit( $limit + 1 );
+ $requestOptions->setOffset( $offset );
+
+ if ( isset( $parameters['search'] ) && $parameters['search'] !== '' ) {
+ $search = $parameters['search'];
+
+ if ( $search !== '' && $search{0} !== '_' ) {
+ $search = str_replace( "_", " ", $search );
+ }
+
+ $requestOptions->addStringCondition(
+ $search,
+ StringCondition::STRCOND_MID
+ );
+
+ // Disjunctive condition to allow for auto searches to match foaf OR Foaf
+ $requestOptions->addStringCondition(
+ ucfirst( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ // Allow something like FOO to match the search string `foo`
+ $requestOptions->addStringCondition(
+ strtoupper( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ $requestOptions->addStringCondition(
+ strtolower( $search ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+ }
+
+ return $requestOptions;
+ }
+
+ private function is_iterable( $obj ) {
+ return is_array( $obj ) || ( is_object( $obj ) && ( $obj instanceof \Traversable ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PValueLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PValueLookup.php
new file mode 100644
index 00000000..bc7eca51
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/PValueLookup.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\RequestOptions;
+use SMW\DIProperty;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWDITime as DIime;
+use SMW\SQLStore\Lookup\ProximityPropertyValueLookup;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PValueLookup extends Lookup {
+
+ const VERSION = 1;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ public function getVersion() {
+ return __METHOD__ . self::VERSION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ $limit = 20;
+ $offset = 0;
+
+ if ( isset( $parameters['limit'] ) ) {
+ $limit = (int)$parameters['limit'];
+ }
+
+ if ( isset( $parameters['offset'] ) ) {
+ $offset = (int)$parameters['offset'];
+ }
+
+ $res = [];
+ $continueOffset = 0;
+ $property = null;
+ $sort = false;
+ $count = 0;
+
+ if ( isset( $parameters['property'] ) ) {
+ $property = $parameters['property'];
+
+ // Get the last which represents the final output
+ // Foo.Bar.Foobar.Baz
+ if ( strpos( $property, '.' ) !== false ) {
+ $chain = explode( '.', $property );
+ $property = array_pop( $chain );
+ }
+ }
+
+ if ( $property === '' || $property === null ) {
+ return [];
+ }
+
+ // Generally we don't want to sort results to avoid having the DB to use
+ // temporary tables/filesort when the value pool is very large
+ if ( isset( $parameters['sort'] ) ) {
+ $sort = in_array( strtolower( $parameters['sort'] ), [ 'asc', 'desc' ] ) ? $parameters['sort'] : 'asc';
+ }
+
+ if ( isset( $parameters['search'] ) ) {
+
+ $opts = new RequestOptions();
+ $opts->limit = $limit;
+ $opts->offset = $offset;
+ $opts->sort = $sort;
+
+ $property = DIProperty::newFromUserLabel(
+ $property
+ );
+
+ $proximityPropertyValueLookup = $this->store->service(
+ 'ProximityPropertyValueLookup'
+ );
+
+ $res = $proximityPropertyValueLookup->lookup(
+ $property,
+ $parameters['search'],
+ $opts
+ );
+
+ if ( $this->is_iterable( $res ) ) {
+ $count = count( $res );
+ }
+
+ if ( $count > $limit ) {
+ $continueOffset = $offset + $count;
+ array_pop( $res );
+ }
+ }
+
+ // Changing this output format requires to set a new version
+ $res = [
+ 'query' => $res,
+ 'query-continue-offset' => $continueOffset,
+ 'version' => self::VERSION,
+ 'meta' => [
+ 'type' => 'pvalue',
+ 'limit' => $limit,
+ 'count' => $count
+ ]
+ ];
+
+ return $res;
+ }
+
+ private function is_iterable( $obj ) {
+ return is_array( $obj ) || ( is_object( $obj ) && ( $obj instanceof \Traversable ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/SubjectLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/SubjectLookup.php
new file mode 100644
index 00000000..c995619e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Browse/SubjectLookup.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\MediaWiki\Api\Browse;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Specials\Browse\HtmlBuilder;
+use SMW\Store;
+use SMW\Exception\RedirectTargetUnresolvableException;
+use SMW\Exception\ParameterNotFoundException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SubjectLookup extends Lookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|integer
+ */
+ public function getVersion() {
+ return 'SubjectLookup:' . self::VERSION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+ public function lookup( array $parameters ) {
+
+ if ( !isset( $parameters['subject'] ) ) {
+ throw new ParameterNotFoundException( 'subject' );
+ }
+
+ if ( !isset( $parameters['ns'] ) ) {
+ throw new ParameterNotFoundException( 'ns' );
+ }
+
+ if ( !isset( $parameters['iw'] ) ) {
+ $parameters['iw'] = '';
+ }
+
+ if ( !isset( $parameters['subobject'] ) ) {
+ $parameters['subobject'] = '';
+ }
+
+ if ( isset( $parameters['type'] ) && $parameters['type'] === 'html' ) {
+ $data = $this->buildHTML( $parameters );
+ } else {
+ $data = $this->doSerialize( $parameters );
+ }
+
+ // Changing this output format requires to set a new version
+ $res = [
+ 'query' => $data,
+ 'meta' => [
+ 'type' => 'subject'
+ ]
+ ];
+
+ return $res;
+ }
+
+ private function buildHTML( $params ) {
+
+ if ( !isset( $params['options'] ) ) {
+ throw new ParameterNotFoundException( 'options' );
+ }
+
+ $subject = new DIWikiPage(
+ $params['subject'],
+ $params['ns'],
+ $params['iw'],
+ $params['subobject']
+ );
+
+ $htmlBuilder = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $htmlBuilder->setOptions(
+ $params['options']
+ );
+
+ return $htmlBuilder->buildHTML();
+ }
+
+ private function doSerialize( $params ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $subobject = isset( $params['subobject'] ) ? $params['subobject'] : '';
+
+ $title = $applicationFactory->newTitleFactory()->newFromText(
+ $params['subject'],
+ $params['ns']
+ );
+
+ $deepRedirectTargetResolver = $applicationFactory->newMwCollaboratorFactory()->newDeepRedirectTargetResolver();
+
+ try {
+ $title = $deepRedirectTargetResolver->findRedirectTargetFor( $title );
+ } catch ( \Exception $e ) {
+ throw new RedirectTargetUnresolvableException( $e->getMessage() );
+ }
+
+ $dataItem = new DIWikiPage(
+ $title->getDBkey(),
+ $title->getNamespace(),
+ $title->getInterwiki(),
+ $subobject
+ );
+
+ $semanticData = $applicationFactory->getStore()->getSemanticData(
+ $dataItem
+ );
+
+ $semanticDataSerializer = $applicationFactory->newSerializerFactory()->newSemanticDataSerializer();
+
+ return $semanticDataSerializer->serialize( $semanticData );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseByProperty.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseByProperty.php
new file mode 100644
index 00000000..23c87ed6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseByProperty.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMW\ApplicationFactory;
+use SMW\Localizer;
+use SMW\NamespaceUriFinder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class BrowseByProperty extends ApiBase {
+
+ /**
+ * #2696
+ * @deprecated since 3.0, use the smwbrowse API module
+ */
+ public function isDeprecated() {
+ return true;
+ }
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $propertyListByApiRequest = new PropertyListByApiRequest(
+ $applicationFactory->getStore(),
+ $applicationFactory->getPropertySpecificationLookup()
+ );
+
+ $propertyListByApiRequest->setLimit(
+ $params['limit']
+ );
+
+ $propertyListByApiRequest->setListOnly(
+ $params['listonly']
+ );
+
+ if ( ( $lang = $params['lang'] ) === null ) {
+ $lang = Localizer::getInstance()->getUserLanguage()->getCode();
+ }
+
+ $propertyListByApiRequest->setLanguageCode(
+ $lang
+ );
+
+ $propertyListByApiRequest->findPropertyListBy(
+ $params['property']
+ );
+
+ foreach ( $propertyListByApiRequest->getNamespaces() as $ns ) {
+
+ $uri = NamespaceUriFinder::getUri( $ns );
+
+ if ( !$uri ) {
+ continue;
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'xmlns:' . $ns,
+ $uri
+ );
+ }
+
+ $data = $propertyListByApiRequest->getPropertyList();
+
+ // I'm without words for this utter nonsense introduced here
+ // because property keys can have a underscore _MDAT or for that matter
+ // any other data field can
+ // https://www.mediawiki.org/wiki/API:JSON_version_2
+ // " ... can indicate that a property beginning with an underscore is not metadata using"
+ if ( method_exists( $this->getResult(), 'setPreserveKeysList') ) {
+ $this->getResult()->setPreserveKeysList(
+ $data,
+ array_keys( $data )
+ );
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'query',
+ $data
+ );
+
+ $this->getResult()->addValue(
+ null,
+ 'version',
+ 2
+ );
+
+ $this->getResult()->addValue(
+ null,
+ 'query-continue-offset',
+ $propertyListByApiRequest->getContinueOffset()
+ );
+
+ $this->getResult()->addValue(
+ null,
+ 'meta',
+ $propertyListByApiRequest->getMeta()
+ );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'property' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'limit' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => 50,
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'lang' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'listonly' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_DFLT => false,
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_REQUIRED => false,
+ ]
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'property' => 'To match a specific property',
+ 'limit' => 'To specify the size of the list request',
+ 'lang' => 'To specify a specific language used for some attributes (description etc.)',
+ 'listonly' => 'To specify that only a property list is returned without further details'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module to query a property list or an individual property.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ public function getExamples() {
+ return [
+ 'api.php?action=browsebyproperty&property=Modification_date',
+ 'api.php?action=browsebyproperty&limit=50',
+ 'api.php?action=browsebyproperty&limit=5&listonly=true',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . '-' . SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseBySubject.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseBySubject.php
new file mode 100644
index 00000000..1cc4568e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/BrowseBySubject.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Specials\Browse\HtmlBuilder;
+
+/**
+ * Browse a subject api module
+ *
+ * @note To browse a particular subobject use the 'subobject' parameter because
+ * MW's WebRequest (responsible for handling request data sent by a browser) will
+ * eliminate any fragments (marked by "#") therefore using something like
+ * '"Lorem_ipsum#Foo' is not going to work but '&subject=Lorem_ipsum&subobject=Foo'
+ * will return results for the selected subobject
+ *
+ * @ingroup Api
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BrowseBySubject extends ApiBase {
+
+ /**
+ * @deprecated since 3.0, use the smwbrowse API module
+ */
+ public function isDeprecated() {
+ return true;
+ }
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ if ( isset( $params['type'] ) && $params['type'] === 'html' ) {
+ $data = $this->buildHTML( $params );
+ } else {
+ $data = $this->doSerialize( $params );
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'query',
+ $data
+ );
+ }
+
+ protected function buildHTML( $params ) {
+
+ $subject = new DIWikiPage(
+ $params['subject'],
+ $params['ns'],
+ $params['iw'],
+ $params['subobject']
+ );
+
+ $htmlBuilder = new HtmlBuilder(
+ ApplicationFactory::getInstance()->getStore(),
+ $subject
+ );
+
+ $htmlBuilder->setOptions(
+ (array)$params['options']
+ );
+
+ return $htmlBuilder->buildHTML();
+ }
+
+ protected function doSerialize( $params ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $title = $applicationFactory->newTitleFactory()->newFromText(
+ $params['subject'],
+ $params['ns']
+ );
+
+ $deepRedirectTargetResolver = $applicationFactory->newMwCollaboratorFactory()->newDeepRedirectTargetResolver();
+
+ try {
+ $title = $deepRedirectTargetResolver->findRedirectTargetFor( $title );
+ } catch ( \Exception $e ) {
+
+ // 1.29+
+ if ( method_exists( $this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-redirect-target-unresolvable', $e->getMessage() ] );
+ } else {
+ $this->dieUsage( $e->getMessage(), 'redirect-target-unresolvable' );
+ }
+ }
+
+ $dataItem = new DIWikiPage(
+ $title->getDBkey(),
+ $title->getNamespace(),
+ $title->getInterwiki(),
+ $params['subobject']
+ );
+
+ $semanticData = $applicationFactory->getStore()->getSemanticData(
+ $dataItem
+ );
+
+ $semanticDataSerializer = $applicationFactory->newSerializerFactory()->newSemanticDataSerializer();
+
+ return $this->doFormat( $semanticDataSerializer->serialize( $semanticData ) );
+ }
+
+ protected function doFormat( $serialized ) {
+
+ $this->addIndexTags( $serialized );
+
+ if ( isset( $serialized['sobj'] ) ) {
+
+ $this->getResult()->setIndexedTagName( $serialized['sobj'], 'subobject' );
+
+ foreach ( $serialized['sobj'] as $key => &$value ) {
+ $this->addIndexTags( $value );
+ }
+ }
+
+ return $serialized;
+ }
+
+ protected function addIndexTags( &$serialized ) {
+
+ if ( isset( $serialized['data'] ) && is_array( $serialized['data'] ) ) {
+
+ $this->getResult()->setIndexedTagName( $serialized['data'], 'property' );
+
+ foreach ( $serialized['data'] as $key => $value ) {
+ if ( isset( $serialized['data'][$key]['dataitem'] ) && is_array( $serialized['data'][$key]['dataitem'] ) ) {
+ $this->getResult()->setIndexedTagName( $serialized['data'][$key]['dataitem'], 'value' );
+ }
+ }
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'subject' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_REQUIRED => true,
+ ],
+ 'ns' => [
+ ApiBase::PARAM_TYPE => 'integer',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => 0,
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'iw' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'subobject' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'type' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ 'options' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_ISMULTI => false,
+ ApiBase::PARAM_DFLT => '',
+ ApiBase::PARAM_REQUIRED => false,
+ ]
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'subject' => 'The subject to be queried',
+ 'subobject' => 'A particular subobject id for the related subject'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module to query a subject.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=browsebysubject&subject=Main_Page',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . '-' . SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Info.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Info.php
new file mode 100644
index 00000000..5f6e9ac9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Info.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMW\ApplicationFactory;
+use SMW\Site;
+
+/**
+ * API module to obtain info about the SMW install, primarily targeted at
+ * usage by the SMW registry.
+ *
+ * @ingroup Api
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class Info extends ApiBase {
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+ $requestedInfo = $params['info'];
+
+ $map = [];
+ $semanticStats = [];
+
+ if ( in_array( 'propcount', $requestedInfo )
+ || in_array( 'jobcount', $requestedInfo )
+ || in_array( 'errorcount', $requestedInfo )
+ || in_array( 'deletecount', $requestedInfo )
+ || in_array( 'totalpropcount', $requestedInfo )
+ || in_array( 'usedpropcount', $requestedInfo )
+ || in_array( 'proppagecount', $requestedInfo )
+ || in_array( 'querycount', $requestedInfo )
+ || in_array( 'querysize', $requestedInfo )
+ || in_array( 'formatcount', $requestedInfo )
+ || in_array( 'conceptcount', $requestedInfo )
+ || in_array( 'subobjectcount', $requestedInfo )
+ || in_array( 'declaredpropcount', $requestedInfo ) ) {
+
+ $semanticStats = ApplicationFactory::getInstance()->getStore()->getStatistics();
+
+ $map = [
+ 'propcount' => 'PROPUSES',
+ 'errorcount' => 'ERRORUSES',
+ 'deletecount' => 'DELETECOUNT',
+ 'usedpropcount' => 'USEDPROPS',
+ 'totalpropcount' => 'TOTALPROPS',
+ 'declaredpropcount' => 'DECLPROPS',
+ 'proppagecount' => 'OWNPAGE',
+ 'querycount' => 'QUERY',
+ 'querysize' => 'QUERYSIZE',
+ 'conceptcount' => 'CONCEPTS',
+ 'subobjectcount' => 'SUBOBJECTS',
+ ];
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'info',
+ $this->doMapResultInfoFrom( $map, $requestedInfo, $semanticStats )
+ );
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'info' => [
+ ApiBase::PARAM_DFLT => 'propcount|usedpropcount|declaredpropcount',
+ ApiBase::PARAM_ISMULTI => true,
+ ApiBase::PARAM_TYPE => [
+ 'propcount',
+ 'errorcount',
+ 'deletecount',
+ 'usedpropcount',
+ 'totalpropcount',
+ 'declaredpropcount',
+ 'proppagecount',
+ 'querycount',
+ 'querysize',
+ 'formatcount',
+ 'conceptcount',
+ 'subobjectcount',
+ 'jobcount'
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'info' => 'The info to provide.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'API module get info about this SMW install.'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=smwinfo&info=proppagecount|propcount',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+
+ private function doMapResultInfoFrom( $map, $requestedInfo, $semanticStats ) {
+
+ $resultInfo = [];
+
+ foreach ( $map as $apiName => $smwName ) {
+ if ( in_array( $apiName, $requestedInfo ) ) {
+ $resultInfo[$apiName] = $semanticStats[$smwName];
+ }
+ }
+
+ if ( in_array( 'formatcount', $requestedInfo ) ) {
+ $resultInfo['formatcount'] = [];
+
+ foreach ( $semanticStats['QUERYFORMATS'] as $name => $count ) {
+ $resultInfo['formatcount'][$name] = $count;
+ }
+ }
+
+ if ( in_array( 'jobcount', $requestedInfo ) ) {
+ $resultInfo['jobcount'] = [];
+ $jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+
+ foreach ( Site::getJobClasses( 'SMW' ) as $type => $class ) {
+ $size = $jobQueue->getQueueSize( $type );
+
+ if ( $size > 0 ) {
+ $resultInfo['jobcount'][$type] = $size;
+ }
+ }
+ }
+
+ return $resultInfo;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/PropertyListByApiRequest.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/PropertyListByApiRequest.php
new file mode 100644
index 00000000..d2eb662d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/PropertyListByApiRequest.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\PropertySpecificationLookup;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMW\StringCondition;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyListByApiRequest {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var RequestOptions
+ */
+ private $requestOptions = null;
+
+ /**
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var array
+ */
+ private $namespaces = [];
+
+ /**
+ * @var array
+ */
+ private $meta = [];
+
+ /**
+ * @var integer
+ */
+ private $limit = 50;
+
+ /**
+ * @var array
+ */
+ private $continueOffset = 1;
+
+ /**
+ * @var string
+ */
+ private $languageCode = '';
+
+ /**
+ * @var boolean
+ */
+ private $listOnly = false;
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( Store $store, PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->store = $store;
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $limit
+ */
+ public function setLimit( $limit ) {
+ $this->limit = (int)$limit;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $listOnly
+ */
+ public function setListOnly( $listOnly ) {
+ $this->listOnly = (bool)$listOnly;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $languageCode
+ */
+ public function setLanguageCode( $languageCode ) {
+ $this->languageCode = (string)$languageCode;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array
+ */
+ public function getPropertyList() {
+ return $this->propertyList;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array
+ */
+ public function getNamespaces() {
+ return $this->namespaces;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array
+ */
+ public function getMeta() {
+ return $this->meta;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array
+ */
+ public function getContinueOffset() {
+ return $this->continueOffset;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $property
+ *
+ * @return boolean
+ */
+ public function findPropertyListBy( $property = '' ) {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->limit = $this->limit;
+
+ $isFromCache = false;
+
+ //
+ $this->matchPropertiesToPreferredLabelBy( $property );
+
+ // Increase by one to look ahead
+ $requestOptions->limit++;
+
+ $requestOptions = $this->doModifyRequestOptionsWith(
+ $property,
+ $requestOptions
+ );
+
+ $propertyListLookup = $this->store->getPropertiesSpecial( $requestOptions );
+
+ // Restore original limit
+ $requestOptions->limit--;
+
+ foreach ( $propertyListLookup->fetchList() as $value ) {
+
+ if ( $this->continueOffset > $requestOptions->limit ) {
+ break;
+ }
+
+ $this->addPropertyToList( $value );
+ $this->continueOffset++;
+ }
+
+ $this->continueOffset = $this->continueOffset > $requestOptions->limit ? $requestOptions->limit : 0;
+ $this->namespaces = array_keys( $this->namespaces );
+
+ $this->meta = [
+ 'limit' => $requestOptions->limit,
+ 'count' => count( $this->propertyList ),
+ 'isCached' => $propertyListLookup->isFromCache()
+ ];
+
+ return true;
+ }
+
+ private function doModifyRequestOptionsWith( $property, $requestOptions ) {
+
+ if ( $property === '' ) {
+ return $requestOptions;
+ }
+
+ if ( $property{0} !== '_' ) {
+ $property = str_replace( "_", " ", $property );
+ }
+
+ // Try to match something like _MDAT to find a label and
+ // make the request a success
+ try {
+ $property = DIProperty::newFromUserLabel( $property )->getLabel();
+ } catch ( \Exception $e ) {
+ $property = '';
+ }
+
+ $requestOptions->addStringCondition(
+ $property,
+ StringCondition::STRCOND_MID
+ );
+
+ // Disjunctive condition to allow for auto searches to match foaf OR Foaf
+ $requestOptions->addStringCondition(
+ ucfirst( $property ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ // Allow something like FOO to match the search string `foo`
+ $requestOptions->addStringCondition(
+ strtoupper( $property ),
+ StringCondition::STRCOND_MID,
+ true
+ );
+
+ return $requestOptions;
+ }
+
+ private function addPropertyToList( array $value ) {
+
+ if ( $value === [] || !$value[0] instanceof DIProperty ) {
+ return;
+ }
+
+ $property = $value[0];
+ $key = $property->getKey();
+
+ if ( strpos( $key, ':' ) !== false ) {
+ $this->namespaces[substr( $key, 0, strpos( $key, ':' ) )] = true;
+ }
+
+ $this->propertyList[$key] = [
+ 'label' => $property->getLabel(),
+ 'key' => $property->getKey()
+ ];
+
+ if ( $this->listOnly ) {
+ return;
+ }
+
+ $this->propertyList[$key]['isUserDefined'] = $property->isUserDefined();
+ $this->propertyList[$key]['usageCount'] = $value[1];
+ $this->propertyList[$key]['description'] = $this->findPropertyDescriptionBy( $property );
+ }
+
+ private function findPropertyDescriptionBy( DIProperty $property ) {
+
+ $description = $this->propertySpecificationLookup->getPropertyDescriptionByLanguageCode(
+ $property,
+ $this->languageCode
+ );
+
+ if ( $description === '' || $description === null ) {
+ return $description;
+ }
+
+ return [
+ $this->languageCode => $description
+ ];
+ }
+
+ private function matchPropertiesToPreferredLabelBy( $label ) {
+
+ $propertyLabelFinder = ApplicationFactory::getInstance()->getPropertyLabelFinder();
+
+ // Use the proximity search on a text field
+ $label = '~*' . $label . '*';
+
+ $results = $propertyLabelFinder->findPropertyListFromLabelByLanguageCode(
+ $label,
+ $this->languageCode
+ );
+
+ foreach ( $results as $result ) {
+ $this->addPropertyToList( [ $result, 0 ] );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Query.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Query.php
new file mode 100644
index 00000000..990cd0ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Query.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use SMW\ApplicationFactory;
+use SMWQuery;
+use SMWQueryProcessor;
+use SMWQueryResult;
+
+/**
+ * Base for API modules that query SMW
+ *
+ * @ingroup Api
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+abstract class Query extends ApiBase {
+
+ /**
+ * Returns a query object for the provided query string and list of printouts.
+ *
+ * @since 1.6.2
+ *
+ * @param string $queryString
+ * @param array $printouts
+ * @param array $parameters
+ *
+ * @return SMWQuery
+ */
+ protected function getQuery( $queryString, array $printouts, array $parameters = [] ) {
+
+ SMWQueryProcessor::addThisPrintout( $printouts, $parameters );
+
+ $query = SMWQueryProcessor::createQuery(
+ $queryString,
+ SMWQueryProcessor::getProcessedParams( $parameters, $printouts ),
+ SMWQueryProcessor::SPECIAL_PAGE,
+ '',
+ $printouts
+ );
+
+ $query->setOption( SMWQuery::PROC_CONTEXT, 'API' );
+
+ return $query;
+ }
+
+ /**
+ * Run the actual query and return the result.
+ *
+ * @since 1.6.2
+ *
+ * @param SMWQuery $query
+ *
+ * @return SMWQueryResult
+ */
+ protected function getQueryResult( SMWQuery $query ) {
+ return ApplicationFactory::getInstance()->getStore()->getQueryResult( $query );
+ }
+
+ /**
+ * Add the query result to the API output.
+ *
+ * @since 1.6.2
+ *
+ * @param SMWQueryResult $queryResult
+ */
+ protected function addQueryResult( SMWQueryResult $queryResult, $outputFormat = 'json' ) {
+
+ $result = $this->getResult();
+
+ $resultFormatter = new ApiQueryResultFormatter( $queryResult );
+ $resultFormatter->setIsRawMode( ( strpos( strtolower( $outputFormat ), 'xml' ) !== false ) );
+ $resultFormatter->doFormat();
+
+ if ( $resultFormatter->getContinueOffset() ) {
+ // $result->disableSizeCheck();
+ $result->addValue( null, 'query-continue-offset', $resultFormatter->getContinueOffset() );
+ // $result->enableSizeCheck();
+ }
+
+ $result->addValue(
+ null,
+ $resultFormatter->getType(),
+ $resultFormatter->getResult()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php
new file mode 100644
index 00000000..5943d005
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php
@@ -0,0 +1,431 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use Iterator;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\Enum;
+use SMWQueryProcessor as QueryProcessor;
+use SMWQuery as Query;
+
+/**
+ * Module to support various tasks initiate using the API interface
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Task extends ApiBase {
+
+ const CACHE_NAMESPACE = 'smw:api:task';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ public static function makeCacheKey( $key ) {
+ return smwfCacheKey( self::CACHE_NAMESPACE, [ $key ] );
+ }
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ $parameters = json_decode(
+ $params['params'],
+ true
+ );
+
+ $results = [];
+
+ if ( $params['task'] === 'update' ) {
+ $results = $this->callUpdateTask( $parameters );
+ }
+
+ if ( $params['task'] === 'check-query' ) {
+ $results = $this->callCheckQueryTask( $parameters );
+ }
+
+ if ( $params['task'] === 'duplookup' ) {
+ $results = $this->callDupLookupTask( $parameters );
+ }
+
+ if ( $params['task'] === 'job' ) {
+ $results = $this->callGenericJobTask( $parameters );
+ }
+
+ if ( $params['task'] === 'run-joblist' ) {
+ $results = $this->callJobListTask( $parameters );
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'task',
+ $results
+ );
+ }
+
+ private function callDupLookupTask( $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $cache = $applicationFactory->getCache();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = 3600;
+
+ if ( isset( $cacheUsage['api.task'] ) ) {
+ $cacheTTL = $cacheUsage['api.task'];
+ }
+
+ $key = self::makeCacheKey( 'duplookup' );
+
+ // Guard against repeated API calls (or fuzzing)
+ if ( ( $result = $cache->fetch( $key ) ) !== false && $cacheTTL !== false ) {
+ return $result + ['isFromCache' => true ];
+ }
+
+ $rows = $applicationFactory->getStore()->getObjectIds()->findDuplicates();
+
+ // Avoid "Exception caught: Serialization of 'Closure' is not allowedException ..."
+ if ( $rows instanceof Iterator ) {
+ $rows = iterator_to_array( $rows );
+ }
+
+ $result = [
+ 'list' => $rows,
+ 'count' => count( $rows ),
+ 'time' => time()
+ ];
+
+ $cache->save( $key, $result, $cacheTTL );
+
+ return $result;
+ }
+
+ private function callCheckQueryTask( $parameters ) {
+
+ if ( $parameters['subject'] === '' || $parameters['query'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $store = ApplicationFactory::getInstance()->getStore();
+
+ $subject = DIWikiPage::doUnserialize(
+ $parameters['subject']
+ );
+
+ foreach ( $parameters['query'] as $hash => $raw_query ) {
+
+ // @see PostProcHandler::addQuery
+ list( $query_hash, $result_hash ) = explode( '#', $hash );
+
+ // Doesn't influence the fingerprint (aka query cache) so just
+ // ignored it
+ $printouts = [];
+ $parameters = $raw_query['parameters'];
+
+ if ( isset( $parameters['sortkeys'] ) ) {
+ $order = [];
+ $sort = [];
+
+ foreach ( $parameters['sortkeys'] as $key => $order_by ) {
+ $order[] = strtolower( $order_by );
+ $sort[] = $key;
+ }
+
+ $parameters['sort'] = implode( ',', $sort );
+ $parameters['order'] = implode( ',', $order );
+ }
+
+ QueryProcessor::addThisPrintout( $printouts, $parameters );
+
+ $query = QueryProcessor::createQuery(
+ $raw_query['conditions'],
+ QueryProcessor::getProcessedParams( $parameters, $printouts ),
+ QueryProcessor::INLINE_QUERY,
+ '',
+ $printouts
+ );
+
+ $query->setLimit(
+ $parameters['limit']
+ );
+
+ $query->setOffset(
+ $parameters['offset']
+ );
+
+ $query->setQueryMode(
+ $parameters['querymode']
+ );
+
+ $query->setContextPage(
+ $subject
+ );
+
+ $query->setOption( Query::PROC_CONTEXT, 'task.api' );
+
+ $res = $store->getQueryResult(
+ $query
+ );
+
+ // If the result_hash from before the post-edit and the result_hash
+ // after the post-edit check are not the same then it means that the
+ // list of entities changed hence send a `reload` command to the
+ // API promise.
+ if ( $result_hash !== $res->getHash( 'quick' ) ) {
+ return [ 'done' => true, 'reload' => true ];
+ }
+ }
+
+ return [ 'done' => true ];
+ }
+
+ private function callGenericJobTask( $params ) {
+
+ $this->checkParameters( $params );
+
+ if ( $params['subject'] === '' ) {
+ return ['done' => false ];
+ }
+
+ $title = DIWikiPage::doUnserialize( $params['subject'] )->getTitle();
+
+ if ( $title === null ) {
+ return ['done' => false ];
+ }
+
+ if ( !isset( $params['job'] ) ) {
+ return ['done' => false ];
+ }
+
+ $parameters = [];
+
+ if ( isset( $params['parameters'] ) ) {
+ $parameters = $params['parameters'];
+ }
+
+ $jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+
+ $job = $jobFactory->newByType(
+ $params['job'],
+ $title,
+ $parameters
+ );
+
+ $job->insert();
+ }
+
+ private function callUpdateTask( $parameters ) {
+
+ $this->checkParameters( $parameters );
+
+ if ( !isset( $parameters['subject'] ) || $parameters['subject'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $subject = DIWikiPage::doUnserialize( $parameters['subject'] );
+ $title = $subject->getTitle();
+ $log = [];
+
+ if ( $title === null ) {
+ return ['done' => false ];
+ }
+
+ // Each single update is required to allow for a cascading computation
+ // where one query follows another to ensure that results are updated
+ // according to the value dependency of the referenced annotations that
+ // rely on a computed (#ask) value
+ if ( !isset( $parameters['ref'] ) ) {
+ $parameters['ref'] = [ $subject->getHash() ];
+ }
+
+ $jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+ $isPost = isset( $parameters['post'] ) ? $parameters['post'] : false;
+ $origin = [];
+
+ if ( isset( $parameters['origin'] ) ) {
+ $origin = [ 'origin' => $parameters['origin'] ];
+ }
+
+ foreach ( $parameters['ref'] as $ref ) {
+ $updateJob = $jobFactory->newUpdateJob(
+ $title,
+ [
+ UpdateJob::FORCED_UPDATE => true,
+ Enum::OPT_SUSPEND_PURGE => false,
+ 'ref' => $ref
+ ] + $origin
+ );
+
+ if ( $isPost ) {
+ $updateJob->insert();
+ } else {
+ $updateJob->run();
+ }
+ }
+
+ return [ 'done' => true, 'log' => $log ];
+ }
+
+ private function callJobListTask( $parameters ) {
+
+ $this->checkParameters( $parameters );
+
+ if ( !isset( $parameters['subject'] ) || $parameters['subject'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $subject = DIWikiPage::doUnserialize( $parameters['subject'] );
+ $title = $subject->getTitle();
+
+ if ( $title === null ) {
+ return [ 'done' => false ];
+ }
+
+ $jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+ $jobList = [];
+
+ if ( isset( $parameters['jobs'] ) ) {
+ $jobList = $parameters['jobs'];
+ }
+
+ $log = $jobQueue->runFromQueue(
+ $jobList
+ );
+
+ return [ 'done' => true, 'log' => $log ];
+ }
+
+ private function checkParameters( $parameters ) {
+ if ( json_last_error() !== JSON_ERROR_NONE || !is_array( $parameters ) ) {
+
+ // 1.29+
+ if ( method_exists( $this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-api-invalid-parameters' ] );
+ } else {
+ $this->dieUsageMsg( 'smw-api-invalid-parameters' );
+ }
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'task' => [
+ ApiBase::PARAM_REQUIRED => true,
+ ApiBase::PARAM_TYPE => [
+
+ // Run update using the updateJob
+ 'update',
+
+ // Run a query check
+ 'check-query',
+
+ // Duplicate lookup support
+ 'duplookup',
+
+ // Insert/run a job
+ 'job',
+
+ // Run jobs from a list directly without the job scheduler
+ 'run-joblist'
+ ]
+ ],
+ 'params' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'task' => 'Defines the task type',
+ 'params' => 'JSON encoded parameters that matches the selected type requirement'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'Semantic MediaWiki API module to invoke and execute tasks (for internal use only)'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::needsToken
+ */
+ public function needsToken() {
+ return 'csrf';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::mustBePosted
+ */
+ public function mustBePosted() {
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::isWriteMode
+ */
+ public function isWriteMode() {
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=smwtask&task=update&params={ "subject": "Foo" }',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . ':' . SMW_VERSION;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Collator.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Collator.php
new file mode 100644
index 00000000..5bf10d51
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Collator.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Collation;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Collator {
+
+ /**
+ * @var Collator
+ */
+ private static $instance = [];
+
+ /**
+ * @var Collation
+ */
+ private $collation;
+
+ /**
+ * @var string
+ */
+ private $collationName;
+
+ /**
+ * @private
+ *
+ * @since 3.0
+ *
+ * @param Collation $collation
+ * @param string $collationName
+ */
+ public function __construct( Collation $collation, $collationName = '' ) {
+ $this->collation = $collation;
+ $this->collationName = $collationName;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param srtring $collationName
+ *
+ * @return Collator
+ */
+ public static function singleton( $collationName = '' ) {
+
+ $collationName = $collationName === '' ? $GLOBALS['smwgEntityCollation'] : $collationName;
+
+ if ( !isset( self::$instance[$collationName] ) ) {
+ self::$instance[$collationName] = new self( Collation::factory( $collationName ), $collationName );
+ }
+
+ return self::$instance[$collationName];
+ }
+
+ /**
+ * For any uca-* generated sortkey armor any invalid or unrecognized UTF-8
+ * characters to prevent an invalid XML/UTF output.
+ *
+ * Characters that cannot be expressed are replaced by ? which is surely
+ * inaccurate in comparison to the original uca-* sortkey but it allows to
+ * replicate a near surrogate string to a back-end that requires XML
+ * compliance (triple store).
+ *
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function armor( $text, $source = '' ) {
+
+ if ( strpos( $this->collationName, 'uca' ) === false ) {
+ return $text;
+ }
+
+ // $text = mb_convert_encoding( $text, 'UTF-8' );
+
+ // https://magp.ie/2011/01/06/remove-non-utf8-characters-from-string-with-php/
+ // Remove all none utf-8 symbols
+ $text = str_replace( '�', '', htmlspecialchars( $text, ENT_SUBSTITUTE, 'UTF-8' ) );
+
+ // remove non-breaking spaces and other non-standard spaces
+ $text = preg_replace( '~\s+~u', '?', $text );
+
+ // replace controls symbols with "?"
+ $text = preg_replace( '~\p{C}+~u', '?', $text );
+
+ return $text;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function getSortKey( $text ) {
+ return $this->collation->getSortKey( $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function getFirstLetter( $text ) {
+
+ // Add check otherwise the Collation instance returns with a
+ // "Uninitialized string offset: 0"
+ if ( $text === '' ) {
+ return '';
+ }
+
+ return $this->collation->getFirstLetter( $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $old
+ * @param string $new
+ *
+ * @return boolean
+ */
+ public function isIdentical( $old, $new ) {
+ return $this->collation->getSortKey( $old ) === $this->collation->getSortKey( $new );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/ConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/ConnectionProvider.php
new file mode 100644
index 00000000..390cb07d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/ConnectionProvider.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+use SMW\Connection\ConnectionProviderRef;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConnectionProvider implements IConnectionProvider {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var string
+ */
+ private $provider;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var array
+ */
+ private $localConnectionConf = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $provider
+ */
+ public function __construct( $provider = null ) {
+ $this->provider = $provider;
+ }
+
+ /**
+ * @see #2532
+ *
+ * @param array $localConnectionConf
+ *
+ * @since 3.0
+ */
+ public function setLocalConnectionConf( array $localConnectionConf ) {
+ $this->localConnectionConf = $localConnectionConf;
+ }
+
+ /**
+ * @see IConnectionProvider::getConnection
+ *
+ * @since 2.1
+ *
+ * @return Database
+ */
+ public function getConnection() {
+
+ if ( $this->connection !== null ) {
+ return $this->connection;
+ }
+
+ // Default configuration
+ $conf = [
+ 'read' => DB_SLAVE,
+ 'write' => DB_MASTER
+ ];
+
+ if ( isset( $this->localConnectionConf[$this->provider] ) ) {
+ $conf = $this->localConnectionConf[$this->provider];
+ }
+
+ return $this->connection = $this->createConnection( $conf );
+ }
+
+ /**
+ * @see IConnectionProvider::releaseConnection
+ *
+ * @since 2.1
+ */
+ public function releaseConnection() {
+
+ if ( $this->connection !== null ) {
+ $this->connection->releaseConnection();
+ }
+
+ $this->connection = null;
+ }
+
+ private function createConnection( $conf ) {
+
+ if ( isset( $conf['callback'] ) && is_callable( $conf['callback'] ) ) {
+ return call_user_func( $conf['callback'] );
+ }
+
+ if ( !isset( $conf['read'] ) || !isset( $conf['write'] ) ) {
+ throw new RuntimeException( "The configuration is incomplete (requires a `read` and `write` identifier)." );
+ }
+
+ $connectionProviders = [];
+
+ $connectionProviders['read'] = new LoadBalancerConnectionProvider(
+ $conf['read']
+ );
+
+ if ( $conf['read'] === $conf['write'] ) {
+ $connectionProviders['write'] = $connectionProviders['read'];
+ } else {
+ $connectionProviders['write'] = new LoadBalancerConnectionProvider(
+ $conf['write']
+ );
+ }
+
+ $transactionProfiler = new TransactionProfiler(
+ \Profiler::instance()->getTransactionProfiler()
+ );
+
+ $transactionProfiler->silenceTransactionProfiler();
+
+ $connection = new Database(
+ new ConnectionProviderRef( $connectionProviders )
+ );
+
+ $connection->setTransactionProfiler(
+ $transactionProfiler
+ );
+
+ // Only required because of SQlite
+ $connection->setDBPrefix( $GLOBALS['wgDBprefix'] );
+
+ $this->logger->info(
+ [
+ 'Connection',
+ '{provider}: {conf}',
+ ],
+ [
+ 'role' => 'developer',
+ 'provider' => $this->provider,
+ 'conf' => [
+ 'read' => $conf['read'] === DB_SLAVE ? 'DB_SLAVE' : 'DB_MASTER',
+ 'write' => $conf['write'] === DB_SLAVE ? 'DB_SLAVE' : 'DB_MASTER',
+ ]
+ ]
+ );
+
+ return $connection;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Database.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Database.php
new file mode 100644
index 00000000..244e0723
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Database.php
@@ -0,0 +1,907 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+use DBError;
+use Exception;
+use ResultWrapper;
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\Connection\ConnectionProviderRef;
+use UnexpectedValueException;
+
+/**
+ * This adapter class covers MW DB specific operations. Changes to the
+ * interface are likely therefore this class should not be used other than by
+ * SMW itself.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class Database {
+
+ /**
+ * Identifies a request to be executed using an auto commit state
+ *
+ * @note (#1605 "... creating temporary tables in a transaction is not
+ * replication-safe and causes errors in MySQL 5.6. ...")
+ */
+ const AUTO_COMMIT = 'auto.commit';
+
+ /**
+ * @see IDatabase::TRIGGER_ROLLBACK
+ */
+ const TRIGGER_ROLLBACK = 3;
+
+ /**
+ * @var ConnectionProviderRef
+ */
+ private $connectionProviderRef;
+
+ /**
+ * @var ILBFactory
+ */
+ private $loadBalancerFactory;
+
+ /**
+ * @var Database
+ */
+ private $readConnection;
+
+ /**
+ * @var Database
+ */
+ private $writeConnection;
+
+ /**
+ * @var string
+ */
+ private $dbPrefix = '';
+
+ /**
+ * @var TransactionProfiler
+ */
+ private $transactionProfiler;
+
+ /**
+ * @var boolean
+ */
+ private $initConnection = false;
+
+ /**
+ * @var boolean
+ */
+ private $autoCommit = false;
+
+ /**
+ * @var integer
+ */
+ private $insertId = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param ConnectionProviderRef $connectionProviderRef
+ * @param ILBFactory|null $loadBalancerFactory
+ */
+ public function __construct( ConnectionProviderRef $connectionProviderRef, $loadBalancerFactory = null ) {
+ $this->connectionProviderRef = $connectionProviderRef;
+ $this->loadBalancerFactory = $loadBalancerFactory;
+
+ if ( $this->loadBalancerFactory === null ) {
+ $this->loadBalancerFactory = ApplicationFactory::getInstance()->create( 'DBLoadBalancerFactory' );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param TransactionProfiler $transactionProfiler
+ */
+ public function setTransactionProfiler( TransactionProfiler $transactionProfiler ) {
+ $this->transactionProfiler = $transactionProfiler;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function releaseConnection() {
+ $this->connectionProviderRef->releaseConnection();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function ping() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Query
+ */
+ public function newQuery() {
+ return new Query( $this );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function isType( $type ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->getType() === $type;
+ }
+
+ /**
+ * @see DatabaseBase::getServerInfo
+ *
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getInfo() {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return [ $this->getType() => $this->readConnection->getServerInfo() ];
+ }
+
+ /**
+ * @see DatabaseBase::getType
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getType() {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->getType();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $dbPrefix
+ */
+ public function setDBPrefix( $dbPrefix ) {
+ $this->dbPrefix = $dbPrefix;
+ }
+
+ /**
+ * @see DatabaseBase::tableName
+ *
+ * @since 1.9
+ *
+ * @param string $tableName
+ *
+ * @return string
+ */
+ public function tableName( $tableName ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( $this->getType() === 'sqlite' ) {
+ return $this->dbPrefix . $tableName;
+ }
+
+ return $this->readConnection->tableName( $tableName );
+ }
+
+ /**
+ * @see DatabaseBase::timestamp
+ *
+ * @since 3.0
+ *
+ * @param integer $ts
+ *
+ * @return string
+ */
+ public function timestamp( $ts = 0 ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->timestamp( $ts );
+ }
+
+ /**
+ * @see DatabaseBase::tablePrefix
+ *
+ * @since 3.0
+ *
+ * @param string $prefix
+ *
+ * @return string
+ */
+ public function tablePrefix( $prefix = null ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->tablePrefix( $prefix );
+ }
+
+ /**
+ * @see DatabaseBase::addQuotes
+ *
+ * @since 1.9
+ *
+ * @param string $tableName
+ *
+ * @return string
+ */
+ public function addQuotes( $value ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->addQuotes( $value );
+ }
+
+ /**
+ * @see DatabaseBase::fetchObject
+ *
+ * @since 1.9
+ *
+ * @param ResultWrapper $res
+ *
+ * @return string
+ */
+ public function fetchObject( $res ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->fetchObject( $res );
+ }
+
+ /**
+ * @see DatabaseBase::numRows
+ *
+ * @since 1.9
+ *
+ * @param mixed $results
+ *
+ * @return integer
+ */
+ public function numRows( $results ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->numRows( $results );
+ }
+
+ /**
+ * @see DatabaseBase::freeResult
+ *
+ * @since 1.9
+ *
+ * @param ResultWrapper $res
+ */
+ public function freeResult( $res ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $this->readConnection->freeResult( $res );
+ }
+
+ /**
+ * @see DatabaseBase::select
+ *
+ * @since 1.9
+ *
+ * @param string $tableName
+ * @param $fields
+ * @param $conditions
+ * @param array $options
+ * @param array $joinConditions
+ *
+ * @return ResultWrapper
+ * @throws UnexpectedValueException
+ */
+ public function select( $tableName, $fields, $conditions = '', $fname, array $options = [], $joinConditions = [] ) {
+
+ $tablePrefix = null;
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( $this->getType() === 'sqlite' ) {
+
+ // MW's SQLite implementation adds an auto prefix to the tableName but
+ // not to the conditions and since ::tableName will handle prefixing
+ // consistently ensure that the select doesn't add an extra prefix
+ $tablePrefix = $this->readConnection->tablePrefix( '' );
+
+ if ( isset( $options['ORDER BY'] ) ) {
+ $options['ORDER BY'] = str_replace( 'RAND', 'RANDOM', $options['ORDER BY'] );
+ }
+ }
+
+ try {
+ $results = $this->readConnection->select(
+ $tableName,
+ $fields,
+ $conditions,
+ $fname,
+ $options,
+ $joinConditions
+ );
+ } catch ( DBError $e ) {
+ throw new RuntimeException ( $e->getMessage() . "\n" . $e->getTraceAsString() );
+ }
+
+ if ( $tablePrefix !== null ) {
+ $this->readConnection->tablePrefix( $tablePrefix );
+ }
+
+ if ( $results instanceof ResultWrapper ) {
+ return $results;
+ }
+
+ throw new UnexpectedValueException (
+ 'Expected a ResultWrapper for ' . "\n" .
+ $tableName . "\n" .
+ $fields . "\n" .
+ $conditions
+ );
+ }
+
+ /**
+ * @see DatabaseBase::query
+ *
+ * @since 1.9
+ *
+ * @param Query|string $sql
+ * @param string $fname
+ * @param boolean $ignoreException
+ *
+ * @return ResultWrapper
+ * @throws RuntimeException
+ */
+ public function query( $sql, $fname = __METHOD__, $ignoreException = false ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( $sql instanceof Query ) {
+ $sql = $sql->build();
+ }
+
+ if ( !$this->isType( 'postgres' ) ) {
+ $sql = str_replace( '@INT', '', $sql );
+ }
+
+ if ( $this->isType( 'postgres' ) ) {
+ $sql = str_replace( '@INT', '::integer', $sql );
+ $sql = str_replace( 'IGNORE', '', $sql );
+ $sql = str_replace( 'DROP TEMPORARY TABLE', 'DROP TABLE IF EXISTS', $sql );
+ $sql = str_replace( 'RAND()', ( strpos( $sql, 'DISTINCT' ) !== false ? '' : 'RANDOM()' ), $sql );
+ }
+
+ if ( $this->isType( 'sqlite' ) ) {
+ $sql = str_replace( 'IGNORE', '', $sql );
+ $sql = str_replace( 'TEMPORARY', 'TEMP', $sql );
+ $sql = str_replace( 'ENGINE=MEMORY', '', $sql );
+ $sql = str_replace( 'DROP TEMP', 'DROP', $sql );
+ $sql = str_replace( 'TRUNCATE TABLE', 'DELETE FROM', $sql );
+ $sql = str_replace( 'RAND', 'RANDOM', $sql );
+ }
+
+ // https://github.com/wikimedia/mediawiki/blob/42d5e6f43a00eb8bedc3532876125f74e3188343/includes/deferred/AutoCommitUpdate.php
+ // https://github.com/wikimedia/mediawiki/blob/f7dad57c64db3eb1296894c2d3ae97b9f7f27c4c/includes/installer/DatabaseInstaller.php#L157
+ if ( $this->autoCommit ) {
+ $autoTrx = $this->writeConnection->getFlag( DBO_TRX );
+ $this->writeConnection->clearFlag( DBO_TRX );
+
+ if ( $autoTrx && $this->writeConnection->trxLevel() ) {
+ $this->writeConnection->commit( __METHOD__ );
+ }
+ }
+
+ try {
+ $exception = null;
+ $results = $this->writeConnection->query(
+ $sql,
+ $fname,
+ $ignoreException
+ );
+ } catch ( Exception $exception ) {
+ }
+
+ if ( $this->autoCommit && $autoTrx ) {
+ $this->writeConnection->setFlag( DBO_TRX );
+ }
+
+ // State is only valid for a single transaction
+ $this->autoCommit = false;
+
+ if ( $exception ) {
+ throw $exception;
+ }
+
+ return $results;
+ }
+
+ /**
+ * @see DatabaseBase::selectRow
+ *
+ * @since 1.9
+ */
+ public function selectRow( $table, $vars, $conds, $fname = __METHOD__, $options = [], $joinConditions = [] ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->selectRow(
+ $table,
+ $vars,
+ $conds,
+ $fname,
+ $options,
+ $joinConditions
+ );
+ }
+
+ /**
+ * @see DatabaseBase::affectedRows
+ *
+ * @since 1.9
+ *
+ * @return int
+ */
+ function affectedRows() {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->affectedRows();
+ }
+
+ /**
+ * @note Method was made protected in 1.28, hence the need
+ * for the DatabaseHelper that copies the functionality.
+ *
+ * @see DatabaseBase::makeSelectOptions
+ *
+ * @since 1.9
+ *
+ * @param array $options
+ *
+ * @return array
+ */
+ public function makeSelectOptions( $options ) {
+ return OptionsBuilder::makeSelectOptions( $this, $options );
+ }
+
+ /**
+ * @see DatabaseBase::nextSequenceValue
+ *
+ * @since 1.9
+ *
+ * @param string $seqName
+ *
+ * @return int|null
+ */
+ public function nextSequenceValue( $seqName ) {
+ $this->insertId = null;
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( !$this->isType( 'postgres' ) ) {
+ return null;
+ }
+
+ // #3101, #2903
+ // MW 1.31+
+ // https://github.com/wikimedia/mediawiki/commit/0a9c55bfd39e22828f2d152ab71789cef3b0897c#diff-278465351b7c14bbcadac82036080e9f
+ $safeseq = str_replace( "'", "''", $seqName );
+ $res = $this->writeConnection->query( "SELECT nextval('$safeseq')" );
+ $row = $this->readConnection->fetchRow( $res );
+
+ return $this->insertId = is_null( $row[0] ) ? null : (int)$row[0];
+ }
+
+ /**
+ * @see DatabaseBase::insertId
+ *
+ * @since 1.9
+ *
+ * @return int
+ */
+ function insertId() {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( $this->insertId !== null ) {
+ return $this->insertId;
+ }
+
+ return (int)$this->writeConnection->insertId();
+ }
+
+ /**
+ * @see DatabaseBase::clearFlag
+ *
+ * @since 2.4
+ */
+ function clearFlag( $flag ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $this->writeConnection->clearFlag( $flag );
+ }
+
+ /**
+ * @see DatabaseBase::getFlag
+ *
+ * @since 2.4
+ */
+ function getFlag( $flag ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->writeConnection->getFlag( $flag );
+ }
+
+ /**
+ * @see DatabaseBase::setFlag
+ *
+ * @since 2.4
+ */
+ function setFlag( $flag ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( $flag === self::AUTO_COMMIT ) {
+ return $this->autoCommit = true;
+ }
+
+ $this->writeConnection->setFlag( $flag );
+ }
+
+ /**
+ * @see DatabaseBase::insert
+ *
+ * @since 1.9
+ */
+ public function insert( $table, $rows, $fname = __METHOD__, $options = [] ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $oldSilenced = $this->transactionProfiler->setSilenced(
+ true
+ );
+
+ $res = $this->writeConnection->insert( $table, $rows, $fname, $options );
+
+ $this->transactionProfiler->setSilenced(
+ $oldSilenced
+ );
+
+ return $res;
+ }
+
+ /**
+ * @see DatabaseBase::update
+ *
+ * @since 1.9
+ */
+ function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $oldSilenced = $this->transactionProfiler->setSilenced(
+ true
+ );
+
+ $res = $this->writeConnection->update( $table, $values, $conds, $fname, $options );
+
+ $this->transactionProfiler->setSilenced(
+ $oldSilenced
+ );
+
+ return $res;
+ }
+
+ /**
+ * @see DatabaseBase::delete
+ *
+ * @since 1.9
+ */
+ public function delete( $table, $conds, $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $oldSilenced = $this->transactionProfiler->setSilenced(
+ true
+ );
+
+ $res = $this->writeConnection->delete( $table, $conds, $fname );
+
+ $this->transactionProfiler->setSilenced(
+ $oldSilenced
+ );
+
+ return $res;
+ }
+
+ /**
+ * @see DatabaseBase::replace
+ *
+ * @since 2.5
+ */
+ public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ $oldSilenced = $this->transactionProfiler->setSilenced(
+ true
+ );
+
+ $res = $this->writeConnection->replace( $table, $uniqueIndexes, $rows, $fname );
+
+ $this->transactionProfiler->setSilenced(
+ $oldSilenced
+ );
+
+ return $res;
+ }
+
+ /**
+ * @see DatabaseBase::makeList
+ *
+ * @since 1.9
+ */
+ public function makeList( $data, $mode ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->writeConnection->makeList( $data, $mode );
+ }
+
+ /**
+ * @see DatabaseBase::tableExists
+ *
+ * @since 1.9
+ *
+ * @param string $table
+ * @param string $fname
+ *
+ * @return bool
+ */
+ public function tableExists( $table, $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->tableExists( $table, $fname );
+ }
+
+ /**
+ * @see DatabaseBase::selectField
+ *
+ * @since 1.9.2
+ */
+ public function selectField( $table, $fieldName, $conditions = '', $fname = __METHOD__, $options = [] ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->selectField( $table, $fieldName, $conditions, $fname, $options );
+ }
+
+ /**
+ * @see DatabaseBase::estimateRowCount
+ *
+ * @since 2.1
+ */
+ public function estimateRowCount( $table, $vars = '*', $conditions = '', $fname = __METHOD__, $options = [] ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ return $this->readConnection->estimateRowCount(
+ $table,
+ $vars,
+ $conditions,
+ $fname,
+ $options
+ );
+ }
+
+ /**
+ * @note Only supported with 1.28+
+ * @since 3.0
+ *
+ * @param string $fname Caller name (e.g. __METHOD__)
+ *
+ * @return mixed A value to pass to commitAndWaitForReplication
+ */
+ public function getEmptyTransactionTicket( $fname = __METHOD__ ) {
+
+ $ticket = null;
+
+ if ( !method_exists( $this->loadBalancerFactory, 'getEmptyTransactionTicket' ) ) {
+ return $ticket;
+ }
+
+ // @see LBFactory::getEmptyTransactionTicket
+ // We don't try very hard at this point and will continue without a ticket
+ // if the check fails and hereby avoid a "... does not have outer scope" error
+ if ( !$this->loadBalancerFactory->hasMasterChanges() ) {
+ $ticket = $this->loadBalancerFactory->getEmptyTransactionTicket( $fname );
+ }
+
+ return $ticket;
+ }
+
+ /**
+ * Convenience method for safely running commitMasterChanges/waitForReplication
+ * where it will allow to commit and wait for whena TransactionTicket is
+ * available.
+ *
+ * @note Only supported with 1.28+
+ *
+ * @since 3.0
+ *
+ * @param string $fname Caller name (e.g. __METHOD__)
+ * @param mixed $ticket Result of Database::getEmptyTransactionTicket
+ * @param array $opts Options to waitForReplication
+ */
+ public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
+
+ if ( !is_int( $ticket ) || !method_exists( $this->loadBalancerFactory, 'commitAndWaitForReplication' ) ) {
+ return;
+ }
+
+ return $this->loadBalancerFactory->commitAndWaitForReplication( $fname, $ticket, $opts );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $fname
+ */
+ public function beginAtomicTransaction( $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ // MW 1.23
+ if ( !method_exists( $this->writeConnection, 'startAtomic' ) ) {
+ return null;
+ }
+
+ $this->writeConnection->startAtomic( $fname );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $fname
+ */
+ public function endAtomicTransaction( $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ // MW 1.23
+ if ( !method_exists( $this->writeConnection, 'endAtomic' ) ) {
+ return null;
+ }
+
+ $this->writeConnection->endAtomic( $fname );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $callback
+ */
+ public function onTransactionResolution( callable $callback, $fname = __METHOD__ ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ if ( method_exists( $this->writeConnection, 'onTransactionResolution' ) && $this->writeConnection->trxLevel() ) {
+ $this->writeConnection->onTransactionResolution( $callback, $fname );
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param callable $callback
+ */
+ public function onTransactionIdle( $callback ) {
+
+ if ( $this->initConnection === false ) {
+ $this->initConnection();
+ }
+
+ // FIXME For 1.19 it is an unknown method hence execute without idle
+ if ( !method_exists( $this->writeConnection, 'onTransactionIdle' ) ) {
+ return call_user_func( $callback );
+ }
+
+ $this->writeConnection->onTransactionIdle( $callback );
+ }
+
+ private function initConnection() {
+
+ if ( $this->readConnection === null ) {
+ $this->readConnection = $this->connectionProviderRef->getConnection( 'read' );
+ }
+
+ if ( $this->writeConnection === null && $this->connectionProviderRef->hasConnection( 'write' ) ) {
+ $this->writeConnection = $this->connectionProviderRef->getConnection( 'write' );
+ }
+
+ $this->initConnection = true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/LoadBalancerConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/LoadBalancerConnectionProvider.php
new file mode 100644
index 00000000..a309a75c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/LoadBalancerConnectionProvider.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+use DatabaseBase;
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class LoadBalancerConnectionProvider implements IConnectionProvider {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var DatabaseBase|null
+ */
+ protected $connection = null;
+
+ /**
+ * @var int|null
+ */
+ protected $id = null;
+
+ /**
+ * @var string|array
+ */
+ protected $groups;
+
+ /**
+ * @var string|boolean $wiki
+ */
+ protected $wiki;
+
+ /**
+ * @since 1.9
+ *
+ * @param int $id
+ * @param string|array $groups
+ * @param string|boolean $wiki
+ */
+ public function __construct( $id, $groups = [], $wiki = false ) {
+ $this->id = $id;
+ $this->groups = $groups;
+ $this->wiki = $wiki;
+ }
+
+ /**
+ * @see IConnectionProvider::getConnection
+ *
+ * @since 1.9
+ *
+ * @return DatabaseBase
+ * @throws RuntimeException
+ */
+ public function getConnection() {
+
+ if ( $this->connection === null ) {
+ $this->connection = wfGetLB( $this->wiki )->getConnection( $this->id, $this->groups, $this->wiki );
+ }
+
+ if ( $this->connection instanceof DatabaseBase ) {
+ return $this->connection;
+ }
+
+ throw new RuntimeException( 'Expected a DatabaseBase instance' );
+ }
+
+ /**
+ * @see IConnectionProvider::releaseConnection
+ *
+ * @since 1.9
+ */
+ public function releaseConnection() {
+ if ( $this->wiki !== false && $this->connection !== null ) {
+ wfGetLB( $this->wiki )->reuseConnection( $this->connection );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/OptionsBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/OptionsBuilder.php
new file mode 100644
index 00000000..97d64e7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/OptionsBuilder.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+/**
+ * https://phabricator.wikimedia.org/T147550
+ *
+ * The contract of the Database interface has changed in MW 1.28 and introduced
+ * incompatibilities which this class tries to bypass.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OptionsBuilder {
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function toString( array $options ) {
+
+ $string = '';
+
+ if ( isset( $options['GROUP BY'] ) ) {
+ $string .= ' GROUP BY ' . ( is_array( $options['GROUP BY'] ) ? implode( ',', $options['GROUP BY'] ) : $options['GROUP BY'] );
+ }
+
+ $string .= self::makeOrderBy( $options );
+
+ if ( isset( $options['LIMIT'] ) ) {
+ $string .= ' LIMIT ' . $options['LIMIT'];
+ }
+
+ if ( isset( $options['OFFSET'] ) ) {
+ $string .= ' OFFSET ' . $options['OFFSET'];
+ }
+
+ return $string;
+ }
+
+ /**
+ * @see Database::makeSelectOptions
+ */
+ public static function makeSelectOptions( Database $connection, $options ) {
+ $preLimitTail = $postLimitTail = '';
+ $startOpts = '';
+
+ $noKeyOptions = [];
+
+ foreach ( $options as $key => $option ) {
+ if ( is_numeric( $key ) ) {
+ $noKeyOptions[$option] = true;
+ }
+ }
+
+ $preLimitTail .= self::makeGroupByWithHaving( $connection, $options );
+
+ $preLimitTail .= self::makeOrderBy( $options );
+
+ // if (isset($options['LIMIT'])) {
+ // $tailOpts .= $this->limitResult('', $options['LIMIT'],
+ // isset($options['OFFSET']) ? $options['OFFSET']
+ // : false);
+ // }
+
+ if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
+ $postLimitTail .= ' FOR UPDATE';
+ }
+
+ if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) {
+ $postLimitTail .= ' LOCK IN SHARE MODE';
+ }
+
+ if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) {
+ $startOpts .= 'DISTINCT';
+ }
+
+ # Various MySQL extensions
+ if ( isset( $noKeyOptions['STRAIGHT_JOIN'] ) ) {
+ $startOpts .= ' /*! STRAIGHT_JOIN */';
+ }
+
+ if ( isset( $noKeyOptions['HIGH_PRIORITY'] ) ) {
+ $startOpts .= ' HIGH_PRIORITY';
+ }
+
+ if ( isset( $noKeyOptions['SQL_BIG_RESULT'] ) ) {
+ $startOpts .= ' SQL_BIG_RESULT';
+ }
+
+ if ( isset( $noKeyOptions['SQL_BUFFER_RESULT'] ) ) {
+ $startOpts .= ' SQL_BUFFER_RESULT';
+ }
+
+ if ( isset( $noKeyOptions['SQL_SMALL_RESULT'] ) ) {
+ $startOpts .= ' SQL_SMALL_RESULT';
+ }
+
+ if ( isset( $noKeyOptions['SQL_CALC_FOUND_ROWS'] ) ) {
+ $startOpts .= ' SQL_CALC_FOUND_ROWS';
+ }
+
+ if ( isset( $noKeyOptions['SQL_CACHE'] ) ) {
+ $startOpts .= ' SQL_CACHE';
+ }
+
+ if ( isset( $noKeyOptions['SQL_NO_CACHE'] ) ) {
+ $startOpts .= ' SQL_NO_CACHE';
+ }
+
+ $useIndex = '';
+ $ignoreIndex = '';
+
+ return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ];
+ }
+
+ protected static function makeGroupByWithHaving( $connection, $options ) {
+ $sql = '';
+
+ if ( isset( $options['GROUP BY'] ) ) {
+ $gb = is_array( $options['GROUP BY'] )
+ ? implode( ',', $options['GROUP BY'] )
+ : $options['GROUP BY'];
+ $sql .= ' GROUP BY ' . $gb;
+ }
+
+ if ( isset( $options['HAVING'] ) ) {
+ $having = is_array( $options['HAVING'] )
+ ? $connection->makeList( $options['HAVING'], self::LIST_AND )
+ : $options['HAVING'];
+ $sql .= ' HAVING ' . $having;
+ }
+
+ return $sql;
+ }
+
+ protected static function makeOrderBy( $options ) {
+ if ( isset( $options['ORDER BY'] ) ) {
+ $ob = is_array( $options['ORDER BY'] )
+ ? implode( ',', $options['ORDER BY'] )
+ : $options['ORDER BY'];
+
+ return ' ORDER BY ' . $ob;
+ }
+
+ return '';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Query.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Query.php
new file mode 100644
index 00000000..7b55d457
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Query.php
@@ -0,0 +1,411 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * @private
+ *
+ * Convenience class with methods to generate a SQL query statement where value
+ * quotes and name transformations are done automatically.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Query {
+
+ const TYPE_SELECT = 'SELECT';
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var string
+ */
+ protected $type = '';
+
+ /**
+ * @var []
+ */
+ protected $table = '';
+
+ /**
+ * @var []
+ */
+ protected $fields = [];
+
+ /**
+ * @var []
+ */
+ protected $conditions = [];
+
+ /**
+ * @var []
+ */
+ protected $options = [];
+
+ /**
+ * @var []
+ */
+ private $joins = [];
+
+ /**
+ * @var string
+ */
+ public $alias = '';
+
+ /**
+ * @var integer
+ */
+ public $index = 0;
+
+ /**
+ * @var boolean
+ */
+ public $autoCommit = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Database $connection
+ */
+ public function __construct( Database $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @throws InvalidArgumentException
+ */
+ public function type( $type ) {
+
+ $type = strtoupper( $type );
+
+ if ( !in_array( $type, [ self::TYPE_SELECT ] ) ) {
+ throw new InvalidArgumentException( "$type was not recognized as valid type!" );
+ }
+
+ $this->type = $type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $fields
+ */
+ public function fields( array $fields ) {
+ $this->fields = $fields;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ */
+ public function field( ...$field ) {
+ $this->fields[] = $field;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasField( $field = '' ) {
+
+ if ( (string)$field === '' ) {
+ return $this->fields !== [];
+ }
+
+ return strpos( json_encode( $this->fields ), $field ) !== false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasCondition() {
+ return $this->conditions !== [];
+ }
+
+ /**
+ * Register the main table in form of ( 'foo' ) or as ( 'foo', 't1' ).
+ *
+ * @since 3.0
+ *
+ * @param string $table
+ */
+ public function table( ...$table ) {
+ $this->table = $this->connection->tableName( $table[0] ) . ( isset( $table[1] ) ? " AS " . $table[1] : '' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string ...$join
+ */
+ public function join( ...$join ) {
+
+ if ( strpos( $join[0], 'JOIN' ) === false ) {
+ throw new InvalidArgumentException( "A join type is missing!" );
+ }
+
+ // ->join( 'INNNER JOIN', [ Table_Foo => ... ] )
+ if ( is_array( $join[1] ) ) {
+ $joins = [];
+
+ foreach ( $join[1] as $table => $value ) {
+
+ if ( is_string( $table ) ) {
+ $value = $value{0} . $value{1} === 'ON' ? "$value" : "AS $value";
+ $value = $this->connection->tableName( $table ) . " $value";
+ }
+
+ $joins[] = $value;
+ }
+
+ $join[1] = implode( ' ', $joins );
+ }
+
+ $this->joins[] = $join;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $k
+ * @param string $v
+ *
+ * @return string
+ */
+ public function like( $k, $v ) {
+ return "$k LIKE " . $this->connection->addQuotes( $v );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $k
+ * @param string $v
+ *
+ * @return string
+ */
+ public function eq( $k, $v ) {
+ return "$k=" . $this->connection->addQuotes( $v );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $k
+ * @param string $v
+ *
+ * @return string
+ */
+ public function neq( $k, $v ) {
+ return "$k!=" . $this->connection->addQuotes( $v );
+ }
+
+ /**
+ * Supposed to be called `and` but this works only on PHP 7.1+.
+ *
+ * @since 3.0
+ *
+ * @param string $condition
+ *
+ * @return array
+ */
+ public function asAnd( $condition ) {
+ return [ 'AND' => $condition ];
+ }
+
+ /**
+ * Supposed to be called `or` but this works only on PHP 7.1+.
+ *
+ * @since 3.0
+ *
+ * @param string $condition
+ *
+ * @return array
+ */
+ public function asOr( $condition ) {
+ return [ 'OR' => $condition ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|array $condition
+ */
+ public function condition( $condition ) {
+
+ if ( is_string( $condition ) ) {
+ $condition = [ $condition ];
+ }
+
+ $this->conditions[] = $condition;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $options
+ */
+ public function options( array $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+
+ $params = [
+ 'tables' => $this->table,
+ 'fields' => $this->fields,
+ 'conditions' => $this->conditions,
+ 'joins' => $this->joins,
+ 'options' => $this->options,
+ 'alias' => $this->alias,
+ 'index' => $this->index,
+ 'autocommit' => $this->autoCommit
+ ];
+
+ return json_encode( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public function build() {
+
+ $statement = $this->sql();
+
+ $this->type = '';
+ $this->table = '';
+ $this->conditions = [];
+ $this->options = [];
+ $this->joins = [];
+ $this->fields = [];
+ $this->alias = '';
+ $this->index = 0;
+
+ return $statement;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $fname
+ *
+ * @return iterable
+ */
+ public function execute( $fname ) {
+ return $this->connection->query( $this, $fname );
+ }
+
+ private function sql() {
+
+ $i = 0;
+ $sql = "";
+ $fields = [];
+
+ if ( $this->type === '' ) {
+ throw new RuntimeException( "Missing a type" );
+ } else {
+ $sql = "$this->type ";
+ }
+
+ if ( isset( $this->options['DISTINCT'] ) ) {
+ if ( is_bool( $this->options['DISTINCT'] ) ) {
+ $sql .= 'DISTINCT ';
+ } else {
+ $sql .= 'DISTINCT ' . $this->options['DISTINCT'] . ' ';
+ }
+ }
+
+ foreach ( $this->fields as $field ) {
+ $fields[] = is_array( $field ) ? implode( ' AS ', $field ) : $field;
+ }
+
+ if ( $fields === [] ) {
+ throw new RuntimeException( "Missing a field" );
+ }
+
+ $sql .= implode( ', ', $fields );
+ $sql .= ' FROM ';
+ $sql .= $this->table;
+
+ foreach ( $this->joins as $join ) {
+ $sql .= ' ' . implode( ' ', $join );
+ }
+
+ $conditions = [];
+
+ foreach ( $this->conditions as $condition ) {
+
+ foreach ( $condition as $exp => $cond ) {
+ if ( $i > 0 && is_int( $exp ) ) {
+ $exp = 'AND';
+ }
+
+ if ( is_array( $cond ) ) {
+ $cond = implode( " $exp ", $cond );
+ }
+
+ if ( $cond !== '' ) {
+
+ if ( $i > 0 && $exp === 'OR' ) {
+ $conditions = [ '(' . implode( ' ', $conditions ) . " OR ($cond))" ];
+ } else {
+ $conditions[] = $i == 0 ? "($cond)" : "$exp ($cond)";
+ }
+ }
+ }
+
+ $i++;
+ }
+
+ if ( $conditions !== [] ) {
+ $sql .= ' WHERE ' . implode( ' ', $conditions );
+ }
+
+ if ( isset( $this->options['GROUP BY'] ) ) {
+ $sql .= " GROUP BY " . $this->options['GROUP BY'];
+
+ if ( isset( $this->options['HAVING'] ) ) {
+ $sql .= " HAVING " . $this->options['HAVING'];
+ }
+ }
+
+ if ( isset( $this->options['ORDER BY'] ) ) {
+ $sql .= " ORDER BY " . $this->options['ORDER BY'];
+ }
+
+ if ( isset( $this->options['LIMIT'] ) ) {
+ $sql .= " LIMIT " . $this->options['LIMIT'];
+ }
+
+ if ( isset( $this->options['OFFSET'] ) ) {
+ $sql .= " OFFSET " . $this->options['OFFSET'];
+ }
+
+ return $sql;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Sequence.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Sequence.php
new file mode 100644
index 00000000..e2b225ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/Sequence.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+use SMW\SQLStore\SQLStore;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Sequence {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var string
+ */
+ private $tablePrefix;
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( $connection ) {
+
+ if ( !$connection instanceof Database && !$connection instanceof \DatabaseBase ) {
+ throw new RuntimeException( "Invalid connection instance!" );
+ }
+
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function tablePrefix( $tablePrefix = '' ) {
+ $this->tablePrefix = $tablePrefix;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $tableName
+ * @param string $field
+ *
+ * @return string
+ */
+ public static function makeSequence( $table, $field ) {
+ return "{$table}_{$field}_seq";
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $tableName
+ * @param string $field
+ *
+ * @return integer
+ */
+ public function restart( $table, $field ) {
+
+ if ( $this->connection->getType() !== 'postgres' ) {
+ return;
+ }
+
+ if ( $this->tablePrefix !== null ) {
+ $this->connection->tablePrefix( $this->tablePrefix );
+ }
+
+ $seq_num = $this->connection->selectField( $table, "max({$field})", [], __METHOD__ );
+ $seq_num += 1;
+
+ $sequence = self::makeSequence( $table, $field );
+
+ $this->connection->onTransactionIdle( function() use( $sequence, $seq_num ) {
+ $this->connection->query( "ALTER SEQUENCE {$sequence} RESTART WITH {$seq_num}", __METHOD__ );
+ } );
+
+ return $seq_num;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/TransactionProfiler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/TransactionProfiler.php
new file mode 100644
index 00000000..b5e45508
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Connection/TransactionProfiler.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\MediaWiki\Connection;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TransactionProfiler {
+
+ /**
+ * @var TransactionProfiler
+ */
+ private $transactionProfiler;
+
+ /**
+ * @var boolean
+ */
+ private $silenceTransactionProfiler = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param TransactionProfiler|null $transactionProfiler
+ */
+ public function __construct( $transactionProfiler = null ) {
+
+ // MW 1.28+
+ if ( method_exists( $transactionProfiler, 'setSilenced' ) ) {
+ $this->transactionProfiler = $transactionProfiler;
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function silenceTransactionProfiler() {
+ $this->silenceTransactionProfiler = true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $state
+ */
+ public function setSilenced( $state ) {
+
+ if ( $this->transactionProfiler === null || $this->silenceTransactionProfiler === false ) {
+ return;
+ }
+
+ // @see https://gerrit.wikimedia.org/r/c/mediawiki/core/+/462130/3/includes/objectcache/SqlBagOStuff.php#836
+ return $this->transactionProfiler->setSilenced( $state );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/DeepRedirectTargetResolver.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/DeepRedirectTargetResolver.php
new file mode 100644
index 00000000..b5b7e3e4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/DeepRedirectTargetResolver.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use RuntimeException;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DeepRedirectTargetResolver {
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator = null;
+
+ /**
+ * Track titles to prevent circular references caused by double redirects
+ * on the same title
+ *
+ * @var array
+ */
+ private $recursiveResolverTracker = [];
+
+ /**
+ * @since 2.1
+ *
+ * @param PageCreator $pageCreator
+ */
+ public function __construct( PageCreator $pageCreator ) {
+ $this->pageCreator = $pageCreator;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ *
+ * @return Title|null
+ * @throws RuntimeException
+ */
+ public function findRedirectTargetFor( Title $title ) {
+ return $this->doResolveRedirectTarget( $title );
+ }
+
+ protected function isValidRedirectTarget( $title ) {
+ return $title instanceof Title && $title->isValidRedirectTarget();
+ }
+
+ protected function isRedirect( $title ) {
+ return $title instanceof Title && $title->isRedirect();
+ }
+
+ private function doResolveRedirectTarget( Title $title ) {
+
+ $this->addToResolverTracker( $title );
+
+ if ( $this->isCircularByKnownRedirectTarget( $title ) ) {
+ throw new RuntimeException( "Circular redirect for {$title->getPrefixedDBkey()} detected." );
+ }
+
+ if ( $this->isRedirect( $title ) ) {
+ $title = $this->pageCreator->createPage( $title )->getRedirectTarget();
+
+ if ( $title instanceof Title ) {
+ $title = $this->doResolveRedirectTarget( $title );
+ }
+ }
+
+ if ( $this->isValidRedirectTarget( $title ) ) {
+ return $title;
+ }
+
+ throw new RuntimeException( "Redirect target is unresolvable" );
+ }
+
+ private function addToResolverTracker( $title ) {
+
+ if ( !isset( $this->recursiveResolverTracker[$title->getPrefixedDBkey()] ) ) {
+ $this->recursiveResolverTracker[$title->getPrefixedDBkey()] = 0;
+ }
+
+ return $this->recursiveResolverTracker[$title->getPrefixedDBkey()]++;
+ }
+
+ private function isCircularByKnownRedirectTarget( $title ) {
+ return isset( $this->recursiveResolverTracker[$title->getPrefixedDBkey()] ) && $this->recursiveResolverTracker[$title->getPrefixedDBkey()] > 1;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/CallableUpdate.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/CallableUpdate.php
new file mode 100644
index 00000000..f63406ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/CallableUpdate.php
@@ -0,0 +1,339 @@
+<?php
+
+namespace SMW\MediaWiki\Deferred;
+
+use Closure;
+use DeferrableUpdate;
+use DeferredUpdates;
+use Psr\Log\LoggerAwareTrait;
+use SMW\MediaWiki\Database;
+
+/**
+ * @see MWCallableUpdate
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ */
+class CallableUpdate implements DeferrableUpdate {
+
+ use LoggerAwareTrait;
+
+ /**
+ * Updates that should run before flushing output buffer
+ */
+ const STAGE_PRESEND = 'pre';
+
+ /**
+ * Updates that should run after flushing output buffer
+ */
+ const STAGE_POSTSEND = 'post';
+
+ /**
+ * @var Closure|callable
+ */
+ protected $callback;
+
+ /**
+ * @var boolean
+ */
+ protected $isDeferrableUpdate = true;
+
+ /**
+ * @var boolean
+ */
+ protected $isCommandLineMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isPending = false;
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @var array
+ */
+ private static $pendingUpdates = [];
+
+ /**
+ * @var string|null
+ */
+ private $fingerprint = null;
+
+ /**
+ * @var array
+ */
+ private static $queueList = [];
+
+ /**
+ * @var string
+ */
+ private $stage;
+
+ /**
+ * @since 2.4
+ *
+ * @param callable $callback|null
+ * @param Database|null $connection
+ */
+ public function __construct( callable $callback = null ) {
+
+ if ( $callback === null ) {
+ $callback = [ $this, 'emptyCallback' ];
+ }
+
+ $this->callback = $callback;
+ $this->stage = self::STAGE_POSTSEND;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode.
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function asPresend() {
+ $this->stage = self::STAGE_PRESEND;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getStage() {
+ return $this->stage;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $callback
+ */
+ public function setCallback( callable $callback ) {
+ $this->callback = $callback;
+ }
+
+ /**
+ * @deprecated since 3.0, use DeferredCallableUpdate::isDeferrableUpdate
+ * @since 2.4
+ */
+ public function enabledDeferredUpdate( $enabledDeferredUpdate = true ) {
+ $this->isDeferrableUpdate( $enabledDeferredUpdate );
+ }
+
+ /**
+ * @note Unit/Integration tests in MW 1.26- showed ambiguous behaviour when
+ * run in deferred mode because not all MW operations were supporting late
+ * execution.
+ *
+ * @since 3.0
+ */
+ public function isDeferrableUpdate( $isDeferrableUpdate ) {
+ $this->isDeferrableUpdate = (bool)$isDeferrableUpdate;
+ }
+
+ /**
+ * @note If wgCommandLineMode = true (e.g. MW is in CLI mode) then
+ * DeferredUpdates::addUpdate pushes updates directly into execution mode
+ * which may not be desirable for all update processes therefore hold on to it
+ * by using an internal waitableUpdate list and release them at convenience.
+ *
+ * @since 2.4
+ *
+ * @param booloan $isPending
+ */
+ public function markAsPending( $isPending = false ) {
+ $this->isPending = (bool)$isPending;
+ }
+
+ /**
+ * @note Set a fingerprint allowing it to track and detect duplicate update
+ * requests while being unprocessed.
+ *
+ * @since 2.5
+ *
+ * @param string|null $queue
+ */
+ public function setFingerprint( $fingerprint = null ) {
+ $this->fingerprint = md5( $fingerprint );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $queue
+ */
+ public function getFingerprint() {
+ return $this->fingerprint;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @see DeferrableCallback::getOrigin
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getOrigin() {
+
+ if ( is_string( $this->origin ) ) {
+ $this->origin = [ $this->origin ];
+ }
+
+ return json_encode( $this->origin );
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function releasePendingUpdates() {
+ foreach ( self::$pendingUpdates as $update ) {
+ DeferredUpdates::addUpdate( $update );
+ }
+
+ self::$pendingUpdates = [];
+ }
+
+ /**
+ * @see DeferrableUpdate::doUpdate
+ *
+ * @since 2.4
+ */
+ public function doUpdate() {
+ call_user_func( $this->callback );
+ unset( self::$queueList[$this->fingerprint] );
+
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Update completed: {origin} (fingerprint:{fingerprint})'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin(),
+ 'fingerprint' => $this->fingerprint
+ ]
+ );
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function pushUpdate() {
+
+ if ( $this->fingerprint !== null && isset( self::$queueList[$this->fingerprint] ) ) {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Push: {origin} (fingerprint: {fingerprint} is already listed, skip)'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin(),
+ 'fingerprint' => $this->fingerprint
+ ]
+ );
+ return;
+ }
+
+ self::$queueList[$this->fingerprint] = true;
+
+ if ( $this->isPending && $this->isDeferrableUpdate ) {
+
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Push: {origin} (as pending DeferredCallableUpdate)'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin(),
+ 'fingerprint' => $this->fingerprint
+ ]
+ );
+
+ return self::$pendingUpdates[] = $this;
+ }
+
+ if ( !$this->isCommandLineMode && $this->isDeferrableUpdate ) {
+ return $this->addUpdate( $this );
+ }
+
+ $this->doUpdate();
+ }
+
+ protected function addUpdate( $update ) {
+
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Added: {ctx}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'ctx' => json_encode(
+ $this->getLoggableContext(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
+ )
+ ]
+ );
+ $stage = null;
+
+ if ( $update->getStage() === self::STAGE_POSTSEND && defined( 'DeferredUpdates::POSTSEND' ) ) {
+ $stage = DeferredUpdates::POSTSEND;
+ }
+
+ if ( $update->getStage() === self::STAGE_PRESEND && defined( 'DeferredUpdates::PRESEND' ) ) {
+ $stage = DeferredUpdates::PRESEND;
+ }
+
+ DeferredUpdates::addUpdate( $update, $stage );
+ }
+
+ protected function getLoggableContext() {
+ return [
+ 'origin' => $this->origin,
+ 'fingerprint' => $this->fingerprint,
+ 'stage' => $this->stage
+ ];
+ }
+
+ protected function emptyCallback() {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Empty callback!'
+ ],
+ [
+ 'role' => 'developer',
+ 'method' => __METHOD__
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/ChangeTitleUpdate.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/ChangeTitleUpdate.php
new file mode 100644
index 00000000..73603655
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/ChangeTitleUpdate.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\MediaWiki\Deferred;
+
+use DeferrableUpdate;
+use DeferredUpdates;
+use Title;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\Site;
+use SMW\Enum;
+
+/**
+ * Run a deferred update job for a changed title instance to re-parse the content
+ * of those associated titles and make sure that its content (incl. any
+ * self-reference) is correctly represented.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangeTitleUpdate implements DeferrableUpdate {
+
+ /**
+ * @var Title|null
+ */
+ private $oldTitle;
+
+ /**
+ * @var Title|null
+ */
+ private $newTitle;
+
+ /**
+ * @since 3.0
+ *
+ * @param Title|null $oldTitle
+ * @param Title|null $newTitle
+ */
+ public function __construct( Title $oldTitle = null, Title $newTitle = null ) {
+ $this->oldTitle = $oldTitle;
+ $this->newTitle = $newTitle;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title|null $oldTitle
+ * @param Title|null $newTitle
+ */
+ public static function addUpdate( Title $oldTitle = null, Title $newTitle = null ) {
+
+ // Avoid deferring the update on CLI (and the DeferredUpdates::tryOpportunisticExecute)
+ // since we use a Job instance to carry out the change
+ if ( Site::isCommandLineMode() ) {
+ $changeTitleUpdate = new self( $oldTitle, $newTitle );
+ $changeTitleUpdate->doUpdate();
+ } else {
+ DeferredUpdates::addUpdate( new self( $oldTitle, $newTitle ) );
+ }
+ }
+
+ /**
+ * @see DeferrableUpdate::doUpdate
+ *
+ * @since 3.0
+ */
+ public function doUpdate() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $jobFactory = $applicationFactory->newJobFactory();
+
+ $parameters = [
+ UpdateJob::FORCED_UPDATE => true,
+
+ // Run purge job after the change has happened since no post-edit event
+ // will be triggered on a changed/redirect title
+ Enum::PURGE_ASSOC_PARSERCACHE => true,
+
+ 'origin' => 'ChangeTitleUpdate'
+ ];
+
+ if ( $this->oldTitle !== null ) {
+ $jobFactory->newUpdateJob( $this->oldTitle, $parameters )->run();
+ }
+
+ if ( $this->newTitle !== null ) {
+ $jobFactory->newUpdateJob( $this->newTitle, $parameters )->run();
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/TransactionalCallableUpdate.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/TransactionalCallableUpdate.php
new file mode 100644
index 00000000..d98d6983
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Deferred/TransactionalCallableUpdate.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace SMW\MediaWiki\Deferred;
+
+use Closure;
+use SMW\MediaWiki\Database;
+
+/**
+ * Extends DeferredCallableUpdate to allow handling of transaction related tasks
+ * or isolations to ensure an undisturbed update process before and after
+ * MediaWiki::preOutputCommit.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TransactionalCallableUpdate extends CallableUpdate {
+
+ /**
+ * @var Database|null
+ */
+ private $connection;
+
+ /**
+ * @var boolean
+ */
+ private $onTransactionIdle = false;
+
+ /**
+ * @var int|null
+ */
+ private $transactionTicket = null;
+
+ /**
+ * @var array
+ */
+ private $preCommitableCallbacks = [];
+
+ /**
+ * @var array
+ */
+ private $postCommitableCallbacks = [];
+
+ /**
+ * @var boolean
+ */
+ private $autoCommit = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $callback|null
+ * @param Database|null $connection
+ */
+ public function __construct( callable $callback = null, Database $connection ) {
+ parent::__construct( $callback );
+ $this->connection = $connection;
+ $this->connection->onTransactionResolution( [ $this, 'cancelOnRollback' ], __METHOD__ );
+ }
+
+ /**
+ * @note MW 1.29+ showed transaction collisions (Exception thrown with
+ * an uncommitted database transaction), use 'onTransactionIdle' to isolate
+ * the update execution.
+ *
+ * @since 2.5
+ */
+ public function waitOnTransactionIdle() {
+ $this->onTransactionIdle = !$this->isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function runAsAutoCommit() {
+ $this->autoCommit = true;
+ }
+
+ /**
+ * It tries to fetch a transactionTicket to assert whether transaction writes
+ * are active or not and if available will process Database::commitAndWaitForReplication
+ * during DeferredCallableUpdate::doUpdate to safely post commits to the
+ * master.
+ *
+ * @note If the commandLine is active then continue an update without a ticket
+ * to avoid any update lag or possible transaction lock.
+ *
+ * @since 3.0
+ */
+ public function commitWithTransactionTicket() {
+ if ( $this->isCommandLineMode === false && $this->isDeferrableUpdate === true ) {
+ $this->transactionTicket = $this->connection->getEmptyTransactionTicket( $this->getOrigin() );
+ }
+ }
+
+ /**
+ * Attaches a callback pre-execution of the source callback and is scheduled
+ * to be executed before the source callback.
+ *
+ * @since 3.0
+ *
+ * @param string $fname
+ * @param Closure $callback
+ */
+ public function addPreCommitableCallback( $fname, callable $callback ) {
+ if ( is_callable( $callback ) ) {
+ $this->preCommitableCallbacks[$fname] = $callback;
+ }
+ }
+
+ /**
+ * Attaches a callback post execution of the source callback and is scheduled
+ * to be executed after the source callback.
+ *
+ * @since 3.0
+ *
+ * @param string $fname
+ * @param Closure $callback
+ */
+ public function addPostCommitableCallback( $fname, callable $callback ) {
+ if ( is_callable( $callback ) ) {
+ $this->postCommitableCallbacks[$fname] = $callback;
+ }
+ }
+
+ /**
+ * @see DeferrableUpdate::doUpdate
+ *
+ * @since 3.0
+ */
+ public function doUpdate() {
+
+ if ( $this->onTransactionIdle ) {
+ return $this->runOnTransactionIdle();
+ }
+
+ $this->runPreCommitCallbacks();
+
+ $e = null;
+ $autoTrx = null;
+
+ if ( $this->autoCommit ) {
+ $this->logger->info( [ 'DeferrableUpdate', 'Transactional, as auto commit', 'Update' ] );
+ $autoTrx = $this->connection->getFlag( DBO_TRX );
+ $this->connection->clearFlag( DBO_TRX );
+ }
+
+ try {
+ parent::doUpdate();
+ } catch ( \Exception $e ) {
+ }
+
+ if ( $this->autoCommit && $autoTrx ) {
+ $this->connection->setFlag( DBO_TRX );
+ }
+
+ if ( $e ) {
+ throw $e;
+ }
+
+ $this->runPostCommitCallbacks();
+
+ if ( $this->transactionTicket !== null ) {
+ $this->connection->commitAndWaitForReplication( $this->getOrigin(), $this->transactionTicket );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function cancelOnRollback( $trigger ) {
+ if ( $trigger === Database::TRIGGER_ROLLBACK ) {
+ $this->callback = [ $this, 'emptyCancelCallback' ];
+ }
+ }
+
+ protected function addUpdate( $update ) {
+
+ if ( $this->onTransactionIdle ) {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Transactional',
+ 'Added: {origin} (onTransactionIdle)'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin()
+ ]
+ );
+
+ return $this->connection->onTransactionIdle( function() use( $update ) {
+ $update->onTransactionIdle = false;
+ parent::addUpdate( $update );
+ } );
+ }
+
+ parent::addUpdate( $update );
+ }
+
+ protected function getLoggableContext() {
+ return parent::getLoggableContext() + [
+ 'transactionTicket' => $this->transactionTicket
+ ];
+ }
+
+ private function runOnTransactionIdle() {
+ $this->connection->onTransactionIdle( function() {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Transactional',
+ 'Update: {origin} (onTransactionIdle)'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin()
+ ]
+ );
+ $this->onTransactionIdle = false;
+ $this->doUpdate();
+ } );
+ }
+
+ private function runPreCommitCallbacks() {
+ foreach ( $this->preCommitableCallbacks as $fname => $preCallback ) {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Transactional',
+ 'Update: {origin} (pre-commitable callback: {fname})'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin(),
+ 'fname' => $fname
+ ]
+ );
+
+ call_user_func( $preCallback, $this->transactionTicket );
+ }
+ }
+
+ private function runPostCommitCallbacks() {
+ foreach ( $this->postCommitableCallbacks as $fname => $postCallback ) {
+ $this->logger->info(
+ [
+ 'DeferrableUpdate',
+ 'Transactional',
+ 'Update: {origin} (post-commitable callback: {fname})'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $this->getOrigin(),
+ 'fname' => $fname
+ ]
+ );
+
+ call_user_func( $postCallback, $this->transactionTicket );
+ }
+ }
+
+ protected function emptyCancelCallback() {
+ $this->logger->info(
+ [ 'DeferrableUpdate', 'cancelOnRollback' ],
+ [ 'role' => 'developer', 'method' => __METHOD__ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/EditInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/EditInfoProvider.php
new file mode 100644
index 00000000..00c2a6ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/EditInfoProvider.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Revision;
+use SMW\ParserData;
+use User;
+use WikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class EditInfoProvider {
+
+ /**
+ * @var WikiPage
+ */
+ private $wikiPage = null;
+
+ /**
+ * @var Revision
+ */
+ private $revision = null;
+
+ /**
+ * @var User
+ */
+ private $user = null;
+
+ /**
+ * @var ParserOutput
+ */
+ private $parserOutput = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param WikiPage $wikiPage
+ * @param Revision $revision
+ * @param User|null $user
+ */
+ public function __construct( WikiPage $wikiPage, Revision $revision, User $user = null ) {
+ $this->wikiPage = $wikiPage;
+ $this->revision = $revision;
+ $this->user = $user;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return ParserOutput|null
+ */
+ public function getOutput() {
+ return $this->parserOutput;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SemanticData|null
+ */
+ public function fetchSemanticData() {
+
+ $parserOutput = $this->fetchEditInfo()->getOutput();
+
+ if ( $parserOutput === null ) {
+ return null;
+ }
+
+ return $parserOutput->getExtensionData( ParserData::DATA_ID );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return EditInfoProvider
+ */
+ public function fetchEditInfo() {
+
+ $editInfo = $this->hasContentForEditMethod() ? $this->prepareContentForEdit() : $this->prepareTextForEdit();
+
+ $this->parserOutput = isset( $editInfo->output ) ? $editInfo->output : null;
+
+ return $this;
+ }
+
+ /**
+ * FIXME MW 1.21-
+ */
+ protected function hasContentForEditMethod() {
+ return method_exists( 'WikiPage', 'prepareContentForEdit' );
+ }
+
+ private function prepareContentForEdit() {
+
+ if ( !$this->revision instanceof Revision ) {
+ return null;
+ }
+
+ $content = $this->revision->getContent();
+
+ return $this->wikiPage->prepareContentForEdit(
+ $content,
+ null,
+ $this->user,
+ $content->getContentHandler()->getDefaultFormat()
+ );
+ }
+
+ private function prepareTextForEdit() {
+ // keep backwards compatibility with MediaWiki 1.19 by deciding, if the
+ // newer Revision::getContent() method (MW 1.20 and above) or the bc
+ // method Revision::getRawText() is used.
+ if ( method_exists( $this->revision, 'getContent' ) ) {
+ $text = $this->revision->getContent( Revision::RAW );
+ } else {
+ // FIXME: Isn't needed after drop of support for MW 1.19
+ $text = $this->revision->getRawText();
+ }
+ return $this->wikiPage->prepareTextForEdit(
+ $text,
+ null,
+ $this->user
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Exception/ExtendedPermissionsError.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Exception/ExtendedPermissionsError.php
new file mode 100644
index 00000000..9b4fb547
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Exception/ExtendedPermissionsError.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace SMW\MediaWiki\Exception;
+
+use PermissionsError;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExtendedPermissionsError extends PermissionsError {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function __construct( $permission, $errors = [] ) {
+ parent::__construct( $permission, [] );
+
+ // Push SMW specific messages to appear first, PermissionsError will
+ // generate a list of required permissions
+ array_unshift( $this->errors, $errors );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleDelete.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleDelete.php
new file mode 100644
index 00000000..5d77c30d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleDelete.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\EventHandler;
+use SMW\MediaWiki\Jobs\UpdateDispatcherJob;
+use SMW\SemanticData;
+use SMW\Store;
+use Title;
+use Wikipage;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleDelete
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ArticleDelete extends HookHandler {
+
+ /**
+ * @var
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Wikipage $wikiPage
+ *
+ * @return true
+ */
+ public function process( Wikipage $wikiPage ) {
+
+ $deferredCallableUpdate = ApplicationFactory::getInstance()->newDeferredCallableUpdate( function() use( $wikiPage ) {
+ $this->doDelete( $wikiPage->getTitle() );
+ } );
+
+ $deferredCallableUpdate->setOrigin( __METHOD__ );
+ $deferredCallableUpdate->pushUpdate();
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ */
+ public function doDelete( Title $title ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $semanticDataSerializer = $applicationFactory->newSerializerFactory()->newSemanticDataSerializer();
+ $jobFactory = $applicationFactory->newJobFactory();
+
+ // Instead of Store::getSemanticData, construct the SemanticData by
+ // attaching only the incoming properties indicating which entities
+ // carry an actual reference to this subject
+ $semanticData = new SemanticData(
+ $subject
+ );
+
+ $properties = $this->store->getInProperties( $subject );
+
+ foreach ( $properties as $property ) {
+ // Avoid doing $propertySubjects = $store->getPropertySubjects( $property, $subject );
+ // as it may produce a too large pool of entities and ultimately
+ // block the delete transaction
+ // Use the subject as dataItem with the UpdateDispatcherJob because
+ // Store::getAllPropertySubjects is only scanning the property
+ $semanticData->addPropertyObjectValue( $property, $subject );
+ }
+
+ $parameters['semanticData'] = $semanticDataSerializer->serialize(
+ $semanticData
+ );
+
+ $parameters['origin'] = 'ArticleDelete';
+
+ // Fetch the ID before the delete process marks it as outdated to help
+ // run a dispatch process on secondary tables
+ $parameters['_id'] = $this->store->getObjectIds()->getId(
+ $subject
+ );
+
+ // Restricted to the available SemanticData
+ $parameters[UpdateDispatcherJob::RESTRICTED_DISPATCH_POOL] = true;
+
+ $updateDispatcherJob = $jobFactory->newUpdateDispatcherJob( $title, $parameters );
+ $updateDispatcherJob->insert();
+
+ $parserCachePurgeJob = $jobFactory->newParserCachePurgeJob(
+ $title,
+ [
+ 'idlist' => [
+ $parameters['_id']
+ ],
+ 'origin' => 'ArticleDelete',
+
+ // Insert will only be done for when the links store is active
+ // otherwise the job wouldn't have any work to do
+ 'is.enabled' => $this->getOption( 'smwgEnabledQueryDependencyLinksStore' )
+ ]
+ );
+
+ $parserCachePurgeJob->insert();
+
+ $this->store->deleteSubject( $title );
+
+ $eventHandler = EventHandler::getInstance();
+ $dispatchContext = $eventHandler->newDispatchContext();
+
+ $dispatchContext->set( 'title', $title );
+ $dispatchContext->set( 'subject', $subject );
+ $dispatchContext->set( 'context', 'ArticleDelete' );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+
+ // Removes any related update marker
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.update.marker.delete',
+ $dispatchContext
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleFromTitle.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleFromTitle.php
new file mode 100644
index 00000000..495a7cf6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleFromTitle.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Page;
+use SMW\Page\PageFactory;
+use SMW\Store;
+use Title;
+
+/**
+ * Register special classes for displaying semantic content on Property and
+ * Concept pages.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleFromTitle
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ArticleFromTitle extends HookHandler {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title &$title
+ * @param Page|null &$page
+ *
+ * @return true
+ */
+ public function process( Title &$title, Page &$page = null ) {
+
+ $ns = $title->getNamespace();
+
+ if ( $ns !== SMW_NS_PROPERTY && $ns !== SMW_NS_CONCEPT ) {
+ return true;
+ }
+
+ $pageFactory = new PageFactory(
+ $this->store
+ );
+
+ $page = $pageFactory->newPageFromTitle(
+ $title
+ );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleProtectComplete.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleProtectComplete.php
new file mode 100644
index 00000000..bbd9de18
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleProtectComplete.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\EditInfoProvider;
+use SMW\Message;
+use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
+use Title;
+
+/**
+ * Occurs after the protect article request has been processed
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleProtectComplete
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ArticleProtectComplete extends HookHandler {
+
+ /**
+ * Whether the update should be restricted or not. Which means that when
+ * no other change is required then categorize the update as restricted
+ * to avoid unnecessary cascading updates.
+ */
+ const RESTRICTED_UPDATE = 'articleprotectcomplete.restricted.update';
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var EditInfoProvider
+ */
+ private $editInfoProvider;
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param EditInfoProvider $editInfoProvider
+ */
+ public function __construct( Title $title, EditInfoProvider $editInfoProvider ) {
+ parent::__construct();
+ $this->title = $title;
+ $this->editInfoProvider = $editInfoProvider;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $protections
+ * @param string $reason
+ */
+ public function process( $protections, $reason ) {
+
+ if ( Message::get( 'smw-edit-protection-auto-update' ) === $reason ) {
+ return $this->log( __METHOD__ . ' No changes required, invoked by own process!' );
+ }
+
+ $this->editInfoProvider->fetchEditInfo();
+
+ $output = $this->editInfoProvider->getOutput();
+
+ if ( $output === null ) {
+ return $this->log( __METHOD__ . ' Missing ParserOutput!' );
+ }
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $this->title,
+ $output
+ );
+
+ $this->doPrepareData( $protections, $parserData );
+ $parserData->setOrigin( 'ArticleProtectComplete' );
+
+ $parserData->updateStore(
+ true
+ );
+ }
+
+ private function doPrepareData( $protections, $parserData ) {
+
+ $isRestrictedUpdate = true;
+ $isAnnotationBySystem = false;
+
+ $dataItemFactory = ApplicationFactory::getInstance()->getDataItemFactory();
+ $property = $dataItemFactory->newDIProperty( '_EDIP' );
+
+ $dataItems = $parserData->getSemanticData()->getPropertyValues( $property );
+ $dataItem = end( $dataItems );
+
+ if ( $dataItem ) {
+ $isAnnotationBySystem = $dataItem->getOption( EditProtectedPropertyAnnotator::SYSTEM_ANNOTATION );
+ }
+
+ $editProtectionRight = $this->getOption( 'smwgEditProtectionRight', false );
+
+ // No _EDIP annotation but a selected protection matches the
+ // `EditProtectionRight` setting
+ if ( !$dataItem && isset( $protections['edit'] ) && $protections['edit'] === $editProtectionRight ) {
+ $this->log( 'ArticleProtectComplete addProperty `Is edit protected`' );
+
+ $isRestrictedUpdate = false;
+ $parserData->getSemanticData()->addPropertyObjectValue(
+ $property,
+ $dataItemFactory->newDIBoolean( true )
+ );
+ }
+
+ // _EDIP exists and was set by the EditProtectedPropertyAnnotator (which
+ // means that is has been set by the system and is not a "human" added
+ // annotation) but since the selected protection doesn't match the
+ // `EditProtectionRight` setting, remove the annotation
+ if ( $dataItem && $isAnnotationBySystem && isset( $protections['edit'] ) && $protections['edit'] !== $editProtectionRight ) {
+ $this->log( 'ArticleProtectComplete removeProperty `Is edit protected`' );
+
+ $isRestrictedUpdate = false;
+ $parserData->getSemanticData()->removePropertyObjectValue(
+ $property,
+ $dataItemFactory->newDIBoolean( true )
+ );
+ }
+
+ $parserData->getSemanticData()->setOption(
+ self::RESTRICTED_UPDATE,
+ $isRestrictedUpdate
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticlePurge.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticlePurge.php
new file mode 100644
index 00000000..26ecfa8a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticlePurge.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\EventHandler;
+use WikiPage;
+
+/**
+ * A function hook being executed before running "&action=purge"
+ *
+ * A temporary cache entry is created to mark and identify the
+ * Article that has been purged.
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/ArticlePurge
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ArticlePurge {
+
+ /**
+ * @since 1.9
+ *
+ * @return true
+ */
+ public function process( WikiPage &$wikiPage ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $pageId = $wikiPage->getTitle()->getArticleID();
+ $settings = $applicationFactory->getSettings();
+
+ $cache = $applicationFactory->getCache();
+ $cacheFactory = $applicationFactory->newCacheFactory();
+
+ if ( $pageId > 0 ) {
+ $cache->save(
+ $cacheFactory->getPurgeCacheKey( $pageId ),
+ $settings->get( 'smwgAutoRefreshOnPurge' )
+ );
+ }
+
+ $dispatchContext = EventHandler::getInstance()->newDispatchContext();
+ $dispatchContext->set( 'title', $wikiPage->getTitle() );
+ $dispatchContext->set( 'context', 'ArticlePurge' );
+
+ if ( $settings->isFlagSet( 'smwgFactboxFeatures', SMW_FACTBOX_PURGE_REFRESH ) ) {
+ EventHandler::getInstance()->getEventDispatcher()->dispatch(
+ 'factbox.cache.delete',
+ $dispatchContext
+ );
+ }
+
+ if ( $settings->get( 'smwgQueryResultCacheRefreshOnPurge' ) ) {
+
+ $dispatchContext->set( 'ask', $applicationFactory->getStore()->getPropertyValues(
+ DIWikiPage::newFromTitle( $wikiPage->getTitle() ),
+ new DIProperty( '_ASK') )
+ );
+
+ EventHandler::getInstance()->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleViewHeader.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleViewHeader.php
new file mode 100644
index 00000000..962adc78
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ArticleViewHeader.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Html;
+use Page;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+use SMW\Message;
+use SMW\Store;
+use Title;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleViewHeader
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleViewHeader extends HookHandler {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Page $page
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Page $page
+ * @param boolean &$outputDone
+ * @param boolean &$useParserCache
+ */
+ public function process( Page $page, &$outputDone, &$useParserCache ) {
+
+ $title = $page->getTitle();
+
+ $changePropagationWatchlist = array_flip(
+ $this->getOption( 'smwgChangePropagationWatchlist', [] )
+ );
+
+ // Only act when `_SUBC` is maintained as watchable property
+ if ( isset( $changePropagationWatchlist['_SUBC'] ) && $title->getNamespace() === NS_CATEGORY ) {
+ $useParserCache = $this->updateCategoryTop( $title, $page->getContext()->getOutput() );
+ }
+
+ return true;
+ }
+
+ private function updateCategoryTop( $title, $output ) {
+
+ $message = '';
+
+ $subject = DIWikiPage::newFromTitle(
+ $title
+ );
+
+ $semanticData = $this->store->getSemanticData(
+ $subject
+ );
+
+ if ( $semanticData->hasProperty( new DIProperty( DIProperty::TYPE_CHANGE_PROP ) ) ) {
+ $severity = $this->getOption( 'smwgChangePropagationProtection', true ) ? 'error' : 'warning';
+
+ $message .= $this->message(
+ $severity,
+ [
+ 'smw-category-change-propagation-locked-' . $severity,
+ str_replace( '_', ' ', $subject->getDBKey() )
+ ]
+ );
+ }
+
+ if ( $message === '' && ChangePropagationDispatchJob::hasPendingJobs( $subject ) ) {
+ $message .= $this->message(
+ 'warning',
+ [
+ 'smw-category-change-propagation-pending',
+ ChangePropagationDispatchJob::getPendingJobsCount( $subject )
+ ]
+ );
+ }
+
+ $output->addHTML( $message );
+
+ // No Message means `useParserCache`otherwise refresh the output to
+ // display the latest update
+ return $message === '';
+ }
+
+ private function message( $type, array $message ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => $message[0],
+ 'class' => 'plainlinks ' . ( $type !== '' ? 'smw-callout smw-callout-'. $type : '' )
+ ],
+ Message::get( $message, Message::PARSE, Message::USER_LANGUAGE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BaseTemplateToolbox.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BaseTemplateToolbox.php
new file mode 100644
index 00000000..57956de6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BaseTemplateToolbox.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\NamespaceExaminer;
+use SMWInfolink as Infolink;
+use SpecialPage;
+use Title;
+
+/**
+ * Hook: Called by BaseTemplate when building the toolbox array and
+ * returning it for the skin to output.
+ *
+ * Add a link to the toolbox to view the properties of the current page in
+ * Special:Browse. The links has the CSS id "t-smwbrowselink" so that it can be
+ * skinned or hidden with all standard mechanisms (also by individual users
+ * with custom CSS).
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/BaseTemplateToolbox
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BaseTemplateToolbox extends HookHandler {
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @since 1.9
+ *
+ * @param NamespaceExaminer $namespaceExaminer
+ */
+ public function __construct( NamespaceExaminer $namespaceExaminer ) {
+ $this->namespaceExaminer = $namespaceExaminer;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param $skinTemplate
+ * @param &$toolbox
+ *
+ * @return boolean
+ */
+ public function process( $skinTemplate, &$toolbox ) {
+
+ $title = $skinTemplate->getSkin()->getTitle();
+
+ if ( $this->canProcess( $title, $skinTemplate ) ) {
+ $this->performUpdate( $title, $toolbox );
+ }
+
+ return true;
+ }
+
+ private function canProcess( Title $title, $skinTemplate ) {
+
+ if ( $title->isSpecialPage() || !$this->namespaceExaminer->isSemanticEnabled( $title->getNamespace() ) ) {
+ return false;
+ }
+
+ if ( !$this->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_TLINK ) || !$skinTemplate->data['isarticle'] ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private function performUpdate( $title, &$toolbox ) {
+
+ $link = Infolink::encodeParameters(
+ [
+ $title->getPrefixedDBkey()
+ ],
+ true
+ );
+
+ $toolbox['smw-browse'] = [
+ 'text' => wfMessage( 'smw_browselink' )->text(),
+ 'href' => SpecialPage::getTitleFor( 'Browse', ':' . $link )->getLocalUrl(),
+ 'id' => 't-smwbrowselink',
+ 'rel' => 'search'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforeDisplayNoArticleText.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforeDisplayNoArticleText.php
new file mode 100644
index 00000000..66c05ce5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforeDisplayNoArticleText.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+
+/**
+ * Before displaying noarticletext or noarticletext-nopermission messages
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeDisplayNoArticleText
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class BeforeDisplayNoArticleText {
+
+ /**
+ * @var Page
+ */
+ private $article;
+
+ /**
+ * @since 2.0
+ *
+ * @param Page $article
+ */
+ public function __construct( $article ) {
+ $this->article = $article;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return boolean
+ */
+ public function process() {
+
+ // Avoid having "noarticletext" info being generated for predefined
+ // properties as we are going to display an introductory text
+ if ( $this->article->getTitle()->getNamespace() === SMW_NS_PROPERTY ) {
+ return DIProperty::newFromUserLabel( $this->article->getTitle()->getText() )->isUserDefined();
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforePageDisplay.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforePageDisplay.php
new file mode 100644
index 00000000..34324af1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/BeforePageDisplay.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use OutputPage;
+use Skin;
+use SpecialPage;
+use Title;
+use SMW\Message;
+use Html;
+
+/**
+ * BeforePageDisplay hook which allows last minute changes to the
+ * output page, e.g. adding of CSS or JavaScript
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BeforePageDisplay extends HookHandler {
+
+ /**
+ * @since 1.9
+ *
+ * @param OutputPage $outputPage,
+ * @param Skin $skin
+ *
+ * @return boolean
+ */
+ public function process( OutputPage $outputPage, Skin $skin ) {
+
+ $title = $outputPage->getTitle();
+ $user = $outputPage->getUser();
+
+ // MW 1.26 / T107399 / Async RL causes style delay
+ $outputPage->addModuleStyles(
+ [
+ 'ext.smw.style',
+ 'ext.smw.tooltip.styles'
+ ]
+ );
+
+ // Add style resources to avoid unstyled content
+ $outputPage->addModules( 'ext.smw.style' );
+
+ // #2726
+ if ( $user->getOption( 'smw-prefs-general-options-suggester-textinput' ) ) {
+ $outputPage->addModules( 'ext.smw.suggester.textInput' );
+ }
+
+ if ( ( $tasks = $this->getOption( 'installer.incomplete_tasks', [] ) ) !== [] ) {
+ $outputPage->prependHTML( $this->incompleteTasksHTML( $tasks ) );
+ }
+
+ // Add export link to the head
+ if ( $title instanceof Title && !$title->isSpecialPage() ) {
+ $link['rel'] = 'alternate';
+ $link['type'] = 'application/rdf+xml';
+ $link['title'] = $title->getPrefixedText();
+ $link['href'] = SpecialPage::getTitleFor( 'ExportRDF', $title->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' );
+ $outputPage->addLink( $link );
+ }
+
+ $request = $skin->getContext()->getRequest();
+
+ if ( in_array( $request->getVal( 'action' ), [ 'delete', 'edit', 'protect', 'unprotect', 'diff', 'history' ] ) || $request->getVal( 'diff' ) ) {
+ return true;
+ }
+
+ return true;
+ }
+
+ private function incompleteTasksHTML( array $messages ) {
+
+ $html = '';
+
+ foreach ( $messages as $message ) {
+ $html .= Html::rawElement( 'li', [], Message::get( $message, Message::PARSE ) );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error plainlinks'
+ ],
+ Message::get( 'smw-install-incomplete-intro' ) . "<ul>$html</ul>"
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/EditPageForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/EditPageForm.php
new file mode 100644
index 00000000..b5d88f42
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/EditPageForm.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use EditPage;
+use Html;
+use SMW\DIProperty;
+use SMW\Message;
+use SMW\NamespaceExaminer;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPage::showEditForm:initial
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EditPageForm extends HookHandler {
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @since 2.5
+ *
+ * @param NamespaceExaminer $namespaceExaminer
+ */
+ public function __construct( NamespaceExaminer $namespaceExaminer ) {
+ $this->namespaceExaminer = $namespaceExaminer;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param EditPage $editPage
+ *
+ * @return boolean
+ */
+ public function process( EditPage $editPage ) {
+
+ if ( !$this->getOption( 'smwgEnabledEditPageHelp', false ) || $this->getOption( 'prefs-disable-editpage', false ) ) {
+ return true;
+ }
+
+ $this->updateEditPage( $editPage );
+
+ return true;
+ }
+
+ private function updateEditPage( $editPage ) {
+
+ $msgKey = $this->getMessageKey(
+ $editPage->getTitle()
+ );
+
+ $message = Message::get(
+ $msgKey,
+ Message::PARSE,
+ Message::USER_LANGUAGE
+ );
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-editpage-help'
+ ],
+ Html::rawElement(
+ 'p',
+ [
+ 'data-msgKey' => $msgKey
+ ],
+ $message
+ )
+ );
+
+ $editPage->editFormPageTop .= $html;
+ }
+
+ private function getMessageKey( $title ) {
+
+ $text = $title->getText();
+ $namespace = $title->getNamespace();
+
+ if ( $namespace === SMW_NS_PROPERTY ) {
+ if ( DIProperty::newFromUserLabel( $text )->isUserDefined() ) {
+ return 'smw-editpage-property-annotation-enabled';
+ } else {
+ return 'smw-editpage-property-annotation-disabled';
+ }
+ } elseif ( $namespace === SMW_NS_CONCEPT ) {
+ return 'smw-editpage-concept-annotation-enabled';
+ } elseif ( $this->namespaceExaminer->isSemanticEnabled( $namespace ) ) {
+ return 'smw-editpage-annotation-enabled';
+ }
+
+ return 'smw-editpage-annotation-disabled';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionSchemaUpdates.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionSchemaUpdates.php
new file mode 100644
index 00000000..02c7ba1c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionSchemaUpdates.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use DatabaseUpdater;
+use Maintenance;
+use ReflectionProperty;
+use SMW\Options;
+use SMW\SQLStore\Installer;
+
+/**
+ * Schema update to set up the needed database tables
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/LoadExtensionSchemaUpdates
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExtensionSchemaUpdates {
+
+ /**
+ * @var DatabaseUpdater
+ */
+ protected $updater = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param DatabaseUpdater $updater = null
+ */
+ public function __construct( DatabaseUpdater $updater = null ) {
+ $this->updater = $updater;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return true
+ */
+ public function process() {
+
+ $verbose = true;
+
+ $options = new Options(
+ [
+ Installer::OPT_SCHEMA_UPDATE => true,
+ Installer::OPT_TABLE_OPTIMIZE => true,
+ Installer::OPT_IMPORT => true,
+ Installer::OPT_SUPPLEMENT_JOBS => true
+ ]
+ );
+
+ if ( $this->hasMaintenanceArg( 'skip-optimize' ) ) {
+ $options->set( Installer::OPT_TABLE_OPTIMIZE, false );
+ }
+
+ // Needs a static caller otherwise the DatabaseUpdater returns with:
+ // "Warning: call_user_func_array() expects parameter 1 to be a
+ // valid callback ..."
+ //
+ // DatabaseUpdater notes "... $callback is the method to call; either a
+ // DatabaseUpdater method name or a callable. Must be serializable (ie.
+ // no anonymous functions allowed). The rest of the parameters (if any)
+ // will be passed to the callback. ..."
+ $this->updater->addExtensionUpdate(
+ [
+ 'SMWStore::setupStore',
+ [
+ 'verbose' => $verbose,
+ 'options' => $options
+ ]
+ ]
+ );
+
+ return true;
+ }
+
+ private function hasMaintenanceArg( $key ) {
+
+ $maintenance = null;
+
+ // We don't have access to the `update.php` internals due to lack
+ // of public methods ... it is far from a clean approach but the only
+ // way to fetch arguments invoked during the execution of `update.php`
+ // Check required due to missing property in MW 1.29-
+ if ( property_exists( $this->updater, 'maintenance' ) ) {
+ $reflectionProperty = new ReflectionProperty( $this->updater, 'maintenance' );
+ $reflectionProperty->setAccessible( true );
+ $maintenance = $reflectionProperty->getValue( $this->updater );
+ }
+
+ if ( $maintenance instanceof Maintenance ) {
+ $reflectionProperty = new ReflectionProperty( $maintenance, 'mOptions' );
+ $reflectionProperty->setAccessible( true );
+ $options = $reflectionProperty->getValue( $maintenance );
+ return isset( $options[$key] );
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionTypes.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionTypes.php
new file mode 100644
index 00000000..7b5d46fe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ExtensionTypes.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+/**
+ * Called when generating the extensions credits, use this to change the tables headers
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ExtensionTypes
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExtensionTypes extends HookHandler {
+
+ /**
+ * @since 2.0
+ *
+ * @param array $extensionTypes
+ *
+ * @return boolean
+ */
+ public function process( array &$extensionTypes ) {
+
+ if ( !is_array( $extensionTypes ) ) {
+ $extensionTypes = [];
+ }
+
+ $extensionTypes = array_merge(
+ [ 'semantic' => wfMessage( 'version-semantic' )->text() ],
+ $extensionTypes
+ );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/FileUpload.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/FileUpload.php
new file mode 100644
index 00000000..97d3bd0b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/FileUpload.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use File;
+use Hooks;
+use ParserOptions;
+use SMW\ApplicationFactory;
+use SMW\Localizer;
+use SMW\NamespaceExaminer;
+use Title;
+use User;
+
+/**
+ * Fires when a local file upload occurs
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/FileUpload
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FileUpload extends HookHandler {
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @since 1.9
+ *
+ * @param NamespaceExaminer $namespaceExaminer
+ */
+ public function __construct( NamespaceExaminer $namespaceExaminer ) {
+ $this->namespaceExaminer = $namespaceExaminer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param File $file
+ * @param boolean $reUploadStatus
+ *
+ * @return true
+ */
+ public function process( File $file, $reUploadStatus = false ) {
+
+ if ( $this->canProcess( $file->getTitle() ) ) {
+ $this->doProcess( $file, $reUploadStatus );
+ }
+
+ return true;
+ }
+
+ private function canProcess( $title ) {
+ return $title !== null && $this->namespaceExaminer->isSemanticEnabled( $title->getNamespace() );
+ }
+
+ private function doProcess( $file, $reUploadStatus = false ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $filePage = $this->makeFilePage( $file, $reUploadStatus );
+
+ // Avoid WikiPage.php: The supplied ParserOptions are not safe to cache.
+ // Fix the options or set $forceParse = true.
+ $forceParse = true;
+
+ $parserData = $applicationFactory->newParserData(
+ $file->getTitle(),
+ $filePage->getParserOutput( $this->makeCanonicalParserOptions(), null, $forceParse )
+ );
+
+ $pageInfoProvider = $applicationFactory->newMwCollaboratorFactory()->newPageInfoProvider(
+ $filePage
+ );
+
+ $propertyAnnotatorFactory = $applicationFactory->singleton( 'PropertyAnnotatorFactory' );
+
+ $semanticData = $parserData->getSemanticData();
+ $semanticData->setOption( 'is.fileupload', true );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newNullPropertyAnnotator(
+ $semanticData
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newPredefinedPropertyAnnotator(
+ $propertyAnnotator,
+ $pageInfoProvider
+ );
+
+ $propertyAnnotator->addAnnotation();
+
+ // 2.4+
+ Hooks::run( 'SMW::FileUpload::BeforeUpdate', [ $filePage, $semanticData ] );
+
+ $parserData->setOrigin( 'FileUpload' );
+
+ $parserData->pushSemanticDataToParserOutput();
+ $parserData->updateStore( true );
+
+ return true;
+ }
+
+ private function makeFilePage( $file, $reUploadStatus ) {
+
+ $filePage = ApplicationFactory::getInstance()->newPageCreator()->createFilePage(
+ $file->getTitle()
+ );
+
+ $filePage->setFile( $file );
+ $filePage->smwFileReUploadStatus = $reUploadStatus;
+
+ return $filePage;
+ }
+
+ /**
+ * Anonymous user with default preferences and content language
+ */
+ private function makeCanonicalParserOptions() {
+ return ParserOptions::newFromUserAndLang(
+ new User(),
+ Localizer::getInstance()->getContentLanguage()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/GetPreferences.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/GetPreferences.php
new file mode 100644
index 00000000..e9af0f41
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/GetPreferences.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Hooks;
+use User;
+use Xml;
+
+/**
+ * Hook: GetPreferences adds user preference
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class GetPreferences extends HookHandler {
+
+ /**
+ * @var User
+ */
+ private $user;
+
+ /**
+ * @since 2.0
+ *
+ * @param User $user
+ */
+ public function __construct( User $user ) {
+ $this->user = $user;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param array &$preferences
+ *
+ * @return true
+ */
+ public function process( array &$preferences ) {
+
+ // Intro text
+ $preferences['smw-prefs-intro'] =
+ [
+ 'type' => 'info',
+ 'label' => '&#160;',
+ 'default' => Xml::tags( 'tr', [ 'class' => 'plainlinks' ],
+ Xml::tags( 'td', [ 'colspan' => 2 ],
+ wfMessage( 'smw-prefs-intro-text' )->parseAsBlock() ) ),
+ 'section' => 'smw',
+ 'raw' => 1,
+ 'rawrow' => 1,
+ ];
+
+ // Preference to allow time correction
+ $preferences['smw-prefs-general-options-time-correction'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-general-options-time-correction',
+ 'section' => 'smw/general-options',
+ ];
+
+ $preferences['smw-prefs-general-options-disable-editpage-info'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-general-options-disable-editpage-info',
+ 'section' => 'smw/general-options',
+ 'disabled' => !$this->getOption( 'smwgEnabledEditPageHelp', false )
+ ];
+
+ $preferences['smw-prefs-general-options-disable-search-info'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-general-options-disable-search-info',
+ 'section' => 'smw/general-options',
+ 'disabled' => $this->getOption( 'wgSearchType' ) !== 'SMWSearch'
+ ];
+
+ $preferences['smw-prefs-general-options-jobqueue-watchlist'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-general-options-jobqueue-watchlist',
+ 'help-message' => 'smw-prefs-help-general-options-jobqueue-watchlist',
+ 'section' => 'smw/general-options',
+ 'disabled' => $this->getOption( 'smwgJobQueueWatchlist', [] ) === []
+ ];
+
+ // Option to enable input assistance
+ $preferences['smw-prefs-general-options-suggester-textinput'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-general-options-suggester-textinput',
+ 'help-message' => 'smw-prefs-help-general-options-suggester-textinput',
+ 'section' => 'smw/general-options',
+ ];
+
+ // Option to enable tooltip info
+ $preferences['smw-prefs-ask-options-tooltip-display'] = [
+ 'type' => 'toggle',
+ 'label-message' => 'smw-prefs-ask-options-tooltip-display',
+ 'section' => 'smw/ask-options',
+ ];
+
+ Hooks::run( 'SMW::GetPreferences', [ $this->user, &$preferences ] );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookHandler.php
new file mode 100644
index 00000000..477220f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookHandler.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Psr\Log\LoggerAwareTrait;
+use Psr\Log\LoggerInterface;
+use SMW\Options;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class HookHandler {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @since 2.5
+ */
+ public function __construct() {
+ $this->options = new Options();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $options
+ */
+ public function setOptions( array $options ) {
+ $this->options = new Options( $options );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( $this->options === null ) {
+ $this->setOptions( [] );
+ }
+
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $flag
+ *
+ * @return boolean
+ */
+ public function isFlagSet( $key, $flag ) {
+ return $this->options->isFlagSet( $key, $flag );
+ }
+
+ protected function log( $message, $context = [] ) {
+ if ( $this->logger instanceof LoggerInterface ) {
+ $this->logger->info( $message, $context );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookListener.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookListener.php
new file mode 100644
index 00000000..ed8eefcb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookListener.php
@@ -0,0 +1,840 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Parser;
+use ParserHooks\HookRegistrant;
+use SMW\ApplicationFactory;
+use SMW\ParserFunctions\DocumentationParserFunction;
+use SMW\ParserFunctions\InfoParserFunction;
+use SMW\ParserFunctions\SectionTag;
+use SMW\MediaWiki\Search\SearchProfileForm;
+use SMW\SQLStore\Installer;
+use SMW\Site;
+use SMW\Store;
+use SMW\Options;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HookListener {
+
+ /**
+ * @var array
+ */
+ private $vars;
+
+ /**
+ * @var string
+ */
+ private $basePath;
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$vars
+ * @param string $basePath
+ */
+ public function __construct( &$vars = [], $basePath = '' ) {
+ $this->vars = $vars;
+ $this->basePath = $basePath;
+ }
+
+ /**
+ * Hook: ParserAfterTidy to add some final processing to the fully-rendered page output
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterTidy
+ */
+ public function onParserAfterTidy( &$parser, &$text ) {
+
+ $parserAfterTidy = new ParserAfterTidy(
+ $parser
+ );
+
+ $parserAfterTidy->isCommandLineMode(
+ Site::isCommandLineMode()
+ );
+
+ // #3341
+ // When running as part of the install don't try to access the DB
+ // or update the Store
+ $parserAfterTidy->isReadOnly(
+ Site::isBlocked()
+ );
+
+ $parserAfterTidy->process( $text );
+
+ return true;
+ }
+
+ /**
+ * Hook: Called by BaseTemplate when building the toolbox array and
+ * returning it for the skin to output.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BaseTemplateToolbox
+ */
+ public function onBaseTemplateToolbox( $skinTemplate, &$toolbox ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $baseTemplateToolbox = new BaseTemplateToolbox(
+ $applicationFactory->getNamespaceExaminer()
+ );
+
+ $baseTemplateToolbox->setOptions(
+ [
+ 'smwgBrowseFeatures' => $applicationFactory->getSettings()->get( 'smwgBrowseFeatures' )
+ ]
+ );
+
+ $baseTemplateToolbox->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ return $baseTemplateToolbox->process( $skinTemplate, $toolbox );
+ }
+
+ /**
+ * Hook: Allows extensions to add text after the page content and article
+ * metadata.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinAfterContent
+ */
+ public function onSkinAfterContent( &$data, $skin = null ) {
+
+ $skinAfterContent = new SkinAfterContent(
+ $skin
+ );
+
+ return $skinAfterContent->performUpdate( $data );
+ }
+
+ /**
+ * Hook: Called after parse, before the HTML is added to the output
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/OutputPageParserOutput
+ */
+ public function onOutputPageParserOutput( &$outputPage, $parserOutput ) {
+
+ $outputPageParserOutput = new OutputPageParserOutput(
+ $outputPage,
+ $parserOutput
+ );
+
+ return $outputPageParserOutput->process();
+ }
+
+ /**
+ * Hook: When checking if the page has been modified since the last visit
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/OutputPageCheckLastModified
+ */
+ public function onOutputPageCheckLastModified( &$lastModified ) {
+
+ // Required to ensure that ViewAction doesn't bail out with
+ // "ViewAction::show: done 304" and hereby neglects to run the
+ // ArticleViewHeader hook
+
+ // Required on 1.28- for the $outputPage->checkLastModified check
+ // that would otherwise prevent running the ArticleViewHeader hook
+ $lastModified['smw'] = wfTimestamp( TS_MW, time() );
+
+ return true;
+ }
+
+ /**
+ * Hook: Allow an extension to disable file caching on pages
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/IsFileCacheable
+ */
+ public function onIsFileCacheable( &$article ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ if ( !$applicationFactory->getNamespaceExaminer()->isSemanticEnabled( $article->getTitle()->getNamespace() ) ) {
+ return true;
+ }
+
+ // Disallow the file cache to avoid skipping the ArticleViewHeader hook
+ // on Article::tryFileCache
+ return true;
+ }
+
+ /**
+ * Hook: Add changes to the output page, e.g. adding of CSS or JavaScript
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay
+ */
+ public function onBeforePageDisplay( &$outputPage, &$skin ) {
+
+ $beforePageDisplay = new BeforePageDisplay();
+
+ $beforePageDisplay->setOptions(
+ [
+ 'installer.incomplete_tasks' => Installer::incompleteTasks( $GLOBALS )
+ ]
+ );
+
+ return $beforePageDisplay->process( $outputPage, $skin );
+ }
+
+ /**
+ * Hook: Called immediately before returning HTML on the search results page
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchResultsPrepend
+ */
+ public function onSpecialSearchResultsPrepend( $specialSearch, $outputPage, $term ) {
+
+ $user = $outputPage->getUser();
+
+ $specialSearchResultsPrepend = new SpecialSearchResultsPrepend(
+ $specialSearch,
+ $outputPage
+ );
+
+ $specialSearchResultsPrepend->setOptions(
+ [
+ 'prefs-suggester-textinput' => $user->getOption( 'smw-prefs-general-options-suggester-textinput' ),
+ 'prefs-disable-search-info' => $user->getOption( 'smw-prefs-general-options-disable-search-info' )
+ ]
+ );
+
+ return $specialSearchResultsPrepend->process( $term );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchProfiles
+ */
+ public function onSpecialSearchProfiles( array &$profiles ) {
+
+ SearchProfileForm::addProfile(
+ $GLOBALS['wgSearchType'],
+ $profiles
+ );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchProfileForm
+ */
+ public function onSpecialSearchProfileForm( $specialSearch, &$form, $profile, $term, $opts ) {
+
+ if ( $profile !== SearchProfileForm::PROFILE_NAME ) {
+ return true;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $searchProfileForm = new SearchProfileForm(
+ $applicationFactory->getStore(),
+ $specialSearch
+ );
+
+ $searchProfileForm->setSearchableNamespaces(
+ \MediaWiki\MediaWikiServices::getInstance()->getSearchEngineConfig()->searchableNamespaces()
+ );
+
+ $searchProfileForm->getForm( $form, $opts );
+
+ return false;
+ }
+
+ /**
+ * Hook: InternalParseBeforeLinks is used to process the expanded wiki
+ * code after <nowiki>, HTML-comments, and templates have been treated.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks
+ */
+ public function onInternalParseBeforeLinks( &$parser, &$text, &$stripState ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $internalParseBeforeLinks = new InternalParseBeforeLinks(
+ $parser,
+ $stripState
+ );
+
+ $internalParseBeforeLinks->setOptions(
+ [
+ 'smwgEnabledSpecialPage' => $applicationFactory->getSettings()->get( 'smwgEnabledSpecialPage' )
+ ]
+ );
+
+ return $internalParseBeforeLinks->process( $text );
+ }
+
+ /**
+ * Hook: NewRevisionFromEditComplete called when a revision was inserted
+ * due to an edit
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/NewRevisionFromEditComplete
+ */
+ public function onNewRevisionFromEditComplete( $wikiPage, $revision, $baseId, $user ) {
+
+ $mwCollaboratorFactory = ApplicationFactory::getInstance()->newMwCollaboratorFactory();
+
+ $editInfoProvider = $mwCollaboratorFactory->newEditInfoProvider(
+ $wikiPage,
+ $revision,
+ $user
+ );
+
+ $pageInfoProvider = $mwCollaboratorFactory->newPageInfoProvider(
+ $wikiPage,
+ $revision,
+ $user
+ );
+
+ $newRevisionFromEditComplete = new NewRevisionFromEditComplete(
+ $wikiPage->getTitle(),
+ $editInfoProvider,
+ $pageInfoProvider
+ );
+
+ return $newRevisionFromEditComplete->process();
+ }
+
+ /**
+ * Hook: Occurs after the protect article request has been processed
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleProtectComplete
+ */
+ public function onArticleProtectComplete( &$wikiPage, &$user, $protections, $reason ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $editInfoProvider = $applicationFactory->newMwCollaboratorFactory()->newEditInfoProvider(
+ $wikiPage,
+ $wikiPage->getRevision(),
+ $user
+ );
+
+ $articleProtectComplete = new ArticleProtectComplete(
+ $wikiPage->getTitle(),
+ $editInfoProvider
+ );
+
+ $articleProtectComplete->setOptions(
+ [
+ 'smwgEditProtectionRight' => $applicationFactory->getSettings()->get( 'smwgEditProtectionRight' )
+ ]
+ );
+
+ $articleProtectComplete->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ $articleProtectComplete->process( $protections, $reason );
+
+ return true;
+ }
+
+ /**
+ * Hook: Occurs when an articleheader is shown
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleViewHeader
+ */
+ public function onArticleViewHeader( &$page, &$outputDone, &$useParserCache ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $articleViewHeader = new ArticleViewHeader(
+ $applicationFactory->getStore()
+ );
+
+ $articleViewHeader->setOptions(
+ [
+ 'smwgChangePropagationProtection' => $settings->get( 'smwgChangePropagationProtection' ),
+ 'smwgChangePropagationWatchlist' => $settings->get( 'smwgChangePropagationWatchlist' )
+ ]
+ );
+
+ $articleViewHeader->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ $articleViewHeader->process( $page, $outputDone, $useParserCache );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/RejectParserCacheValue
+ */
+ public function onRejectParserCacheValue( $value, $wikiPage, $popts ) {
+
+ $queryDependencyLinksStoreFactory = ApplicationFactory::getInstance()->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $rejectParserCacheValue = new RejectParserCacheValue(
+ $queryDependencyLinksStoreFactory->newDependencyLinksUpdateJournal()
+ );
+
+ // Return false to reject the parser cache
+ // The log will contain something like "[ParserCache] ParserOutput
+ // key valid, but rejected by RejectParserCacheValue hook handler."
+ return $rejectParserCacheValue->process( $wikiPage->getTitle() );
+ }
+
+ /**
+ * Hook: TitleMoveComplete occurs whenever a request to move an article
+ * is completed
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleMoveComplete
+ */
+ public function onTitleMoveComplete( $oldTitle, $newTitle, $user, $oldId, $newId ) {
+
+ $titleMoveComplete = new TitleMoveComplete(
+ $oldTitle,
+ $newTitle,
+ $user,
+ $oldId,
+ $newId
+ );
+
+ return $titleMoveComplete->process();
+ }
+
+ /**
+ * Hook: ArticlePurge executes before running "&action=purge"
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticlePurge
+ */
+ public function onArticlePurge( &$wikiPage ) {
+
+ $articlePurge = new ArticlePurge();
+
+ return $articlePurge->process( $wikiPage );
+ }
+
+ /**
+ * Hook: ArticleDelete occurs whenever the software receives a request
+ * to delete an article
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleDelete
+ */
+ public function onArticleDelete( &$wikiPage, &$user, &$reason, &$error ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $articleDelete = new ArticleDelete(
+ $applicationFactory->getStore()
+ );
+
+ $articleDelete->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ $articleDelete->setOptions(
+ [
+ 'smwgEnabledQueryDependencyLinksStore' => $applicationFactory->getSettings()->get( 'smwgEnabledQueryDependencyLinksStore' )
+ ]
+ );
+
+ return $articleDelete->process( $wikiPage );
+ }
+
+ /**
+ * Hook: LinksUpdateConstructed called at the end of LinksUpdate() construction
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdateConstructed
+ */
+ public function onLinksUpdateConstructed( $linksUpdate ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $linksUpdateConstructed = new LinksUpdateConstructed(
+ $applicationFactory->getNamespaceExaminer()
+ );
+
+ $linksUpdateConstructed->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ // #3341
+ // When running as part of the install don't try to access the DB
+ // or update the Store
+ $linksUpdateConstructed->isReadOnly(
+ Site::isBlocked()
+ );
+
+ $linksUpdateConstructed->process( $linksUpdate );
+
+ return true;
+ }
+
+ /**
+ * Hook: Occurs when an articleheader is shown
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ContentHandlerForModelID
+ */
+ public function onContentHandlerForModelID( $modelId, &$contentHandler ) {
+
+ // 'rule-json' being a legacy model, remove with 3.1
+ if ( $modelId === 'rule-json' || $modelId === 'smw/schema' ) {
+ $contentHandler = new \SMW\Schema\Content\ContentHandler();
+ }
+
+ return true;
+ }
+
+ /**
+ * Hook: Add extra statistic at the end of Special:Statistics
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialStatsAddExtra
+ */
+ public function onSpecialStatsAddExtra( &$extraStats ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $specialStatsAddExtra = new SpecialStatsAddExtra(
+ $applicationFactory->getStore()
+ );
+
+ $specialStatsAddExtra->setOptions(
+ [
+ 'smwgSemanticsEnabled' => $applicationFactory->getSettings()->get( 'smwgSemanticsEnabled' )
+ ]
+ );
+
+ return $specialStatsAddExtra->process( $extraStats );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/FileUpload
+ */
+ public function onFileUpload( $file, $reupload ) {
+
+ $fileUpload = new FileUpload(
+ ApplicationFactory::getInstance()->getNamespaceExaminer()
+ );
+
+ return $fileUpload->process( $file, $reupload );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderGetConfigVars
+ */
+ public function onResourceLoaderGetConfigVars( &$vars ) {
+
+ $resourceLoaderGetConfigVars = new ResourceLoaderGetConfigVars();
+
+ return $resourceLoaderGetConfigVars->process( $vars );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences
+ */
+ public function onGetPreferences( $user, &$preferences ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $getPreferences = new GetPreferences(
+ $user
+ );
+
+ $getPreferences->setOptions(
+ [
+ 'smwgEnabledEditPageHelp' => $settings->get( 'smwgEnabledEditPageHelp' ),
+ 'wgSearchType' => $GLOBALS['wgSearchType'],
+ 'smwgJobQueueWatchlist' => $settings->get( 'smwgJobQueueWatchlist' )
+ ]
+ );
+
+ $getPreferences->process( $preferences);
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/PersonalUrls
+ */
+ public function onPersonalUrls( array &$personal_urls, $title, $skinTemplate ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $personalUrls = new PersonalUrls(
+ $skinTemplate,
+ $applicationFactory->getJobQueue()
+ );
+
+ $user = $skinTemplate->getUser();
+
+ $personalUrls->setOptions(
+ [
+ 'smwgJobQueueWatchlist' => $applicationFactory->getSettings()->get( 'smwgJobQueueWatchlist' ),
+ 'prefs-jobqueue-watchlist' => $user->getOption( 'smw-prefs-general-options-jobqueue-watchlist' )
+ ]
+ );
+
+ $personalUrls->process( $personal_urls );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinTemplateNavigation
+ */
+ public function onSkinTemplateNavigation( &$skinTemplate, &$links ) {
+
+ $skinTemplateNavigation = new SkinTemplateNavigation(
+ $skinTemplate,
+ $links
+ );
+
+ return $skinTemplateNavigation->process();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/LoadExtensionSchemaUpdates
+ */
+ public function onLoadExtensionSchemaUpdates( $databaseUpdater ) {
+
+ $extensionSchemaUpdates = new ExtensionSchemaUpdates(
+ $databaseUpdater
+ );
+
+ return $extensionSchemaUpdates->process();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
+ */
+ public function onResourceLoaderTestModules( &$testModules, &$resourceLoader ) {
+
+ $resourceLoaderTestModules = new ResourceLoaderTestModules(
+ $resourceLoader,
+ $this->basePath,
+ $this->vars['IP']
+ );
+
+ return $resourceLoaderTestModules->process( $testModules );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ExtensionTypes
+ */
+ public function onExtensionTypes( &$extTypes ) {
+
+ $extensionTypes = new ExtensionTypes();
+
+ return $extensionTypes->process( $extTypes);
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleIsAlwaysKnown
+ */
+ public function onTitleIsAlwaysKnown( $title, &$result ) {
+
+ $titleIsAlwaysKnown = new TitleIsAlwaysKnown(
+ $title,
+ $result
+ );
+
+ return $titleIsAlwaysKnown->process();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleFromTitle
+ */
+ public function onArticleFromTitle( &$title, &$article ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $articleFromTitle = new ArticleFromTitle(
+ $applicationFactory->getStore()
+ );
+
+ return $articleFromTitle->process( $title, $article );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleIsMovable
+ */
+ public function onTitleIsMovable( $title, &$isMovable ) {
+
+ $titleIsMovable = new TitleIsMovable(
+ $title
+ );
+
+ return $titleIsMovable->process( $isMovable );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeDisplayNoArticleText
+ */
+ public function onBeforeDisplayNoArticleText( $article ) {
+
+ $beforeDisplayNoArticleText = new BeforeDisplayNoArticleText(
+ $article
+ );
+
+ return $beforeDisplayNoArticleText->process();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/EditPage::showEditForm:initial
+ */
+ public function onEditPageShowEditFormInitial( $editPage, $output ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $user = $output->getUser();
+
+ $editPageForm = new EditPageForm(
+ $applicationFactory->getNamespaceExaminer()
+ );
+
+ $editPageForm->setOptions(
+ [
+ 'smwgEnabledEditPageHelp' => $applicationFactory->getSettings()->get( 'smwgEnabledEditPageHelp' ),
+ 'prefs-disable-editpage' => $user->getOption( 'smw-prefs-general-options-disable-editpage-info' )
+ ]
+ );
+
+ return $editPageForm->process( $editPage );
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleQuickPermissions
+ *
+ * "...Quick permissions are checked first in the Title::checkQuickPermissions
+ * function. Quick permissions are the most basic of permissions needed
+ * to perform an action ..."
+ */
+ public function onTitleQuickPermissions( $title, $user, $action, &$errors, $rigor, $short ) {
+
+ $permissionPthValidator = ApplicationFactory::getInstance()->singleton( 'PermissionPthValidator' );
+
+ $ret = $permissionPthValidator->checkQuickPermission(
+ $title,
+ $user,
+ $action,
+ $errors
+ );
+
+ return $ret;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserOptionsRegister (Only 1.30+)
+ */
+ public function onParserOptionsRegister( &$defaults, &$inCacheKey ) {
+
+ // #2509
+ // Register a new options key, used in connection with #ask/#show
+ // where the use of a localTime invalidates the ParserCache to avoid
+ // stalled settings for users with different preferences
+ $defaults['localTime'] = false;
+ $inCacheKey['localTime'] = true;
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserFirstCallInit
+ */
+ public function onParserFirstCallInit( &$parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $parserFunctionFactory = $applicationFactory->newParserFunctionFactory();
+ $parserFunctionFactory->registerFunctionHandlers( $parser );
+
+ $hookRegistrant = new HookRegistrant( $parser );
+
+ $infoFunctionDefinition = InfoParserFunction::getHookDefinition();
+ $infoFunctionHandler = new InfoParserFunction();
+ $hookRegistrant->registerFunctionHandler( $infoFunctionDefinition, $infoFunctionHandler );
+ $hookRegistrant->registerHookHandler( $infoFunctionDefinition, $infoFunctionHandler );
+
+ $docsFunctionDefinition = DocumentationParserFunction::getHookDefinition();
+ $docsFunctionHandler = new DocumentationParserFunction();
+ $hookRegistrant->registerFunctionHandler( $docsFunctionDefinition, $docsFunctionHandler );
+ $hookRegistrant->registerHookHandler( $docsFunctionDefinition, $docsFunctionHandler );
+
+ /**
+ * Support for <section> ... </section>
+ */
+ SectionTag::register(
+ $parser,
+ $applicationFactory->getSettings()->get( 'smwgSupportSectionTag' )
+ );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BlockIpComplete
+ * @provided by MW 1.4
+ *
+ * "... occurs after the request to block (or change block settings of)
+ * an IP or user has been processed ..."
+ */
+ public function onBlockIpComplete( $block, $performer, $priorBlock ) {
+
+ $userChange = new UserChange(
+ ApplicationFactory::getInstance()->getNamespaceExaminer()
+ );
+
+ $userChange->setOrigin( 'BlockIpComplete' );
+ $userChange->process( $block->getTarget() );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/UnblockUserComplete
+ * @provided by MW 1.29
+ *
+ * "... occurs after the request to unblock an IP or user has been
+ * processed ..."
+ */
+ public function onUnblockUserComplete( $block, $performer ) {
+
+ $userChange = new UserChange(
+ ApplicationFactory::getInstance()->getNamespaceExaminer()
+ );
+
+ $userChange->setOrigin( 'UnblockUserComplete' );
+ $userChange->process( $block->getTarget() );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/UserGroupsChanged
+ * @provided by MW 1.26
+ *
+ * "... called after user groups are changed ..."
+ */
+ public function onUserGroupsChanged( $user ) {
+
+ $userChange = new UserChange(
+ ApplicationFactory::getInstance()->getNamespaceExaminer()
+ );
+
+ $userChange->setOrigin( 'UserGroupsChanged' );
+ $userChange->process( $user->getName() );
+
+ return true;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SoftwareInfo
+ */
+ public function onSoftwareInfo( &$software ) {
+
+ $store = ApplicationFactory::getInstance()->getStore();
+ $info = $store->getConnection( 'elastic' )->getSoftwareInfo();
+
+ if ( !isset( $software[$info['component']] ) && $info['version'] !== null ) {
+ $software[$info['component']] = $info['version'];
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookRegistry.php
new file mode 100644
index 00000000..22eb254b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/HookRegistry.php
@@ -0,0 +1,383 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Onoi\HttpRequest\HttpRequestFactory;
+use Parser;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Search\SearchProfileForm;
+use SMW\NamespaceManager;
+use SMW\SemanticData;
+use SMW\Setup;
+use SMW\Site;
+use SMW\SQLStore\QueryDependencyLinksStoreFactory;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HookRegistry {
+
+ /**
+ * @var array
+ */
+ private $handlers = [];
+
+ /**
+ * @var array
+ */
+ private $globalVars;
+
+ /**
+ * @var string
+ */
+ private $basePath;
+
+ /**
+ * @since 2.1
+ *
+ * @param array &$globalVars
+ * @param string $directory
+ */
+ public function __construct( &$globalVars = [], $directory = '' ) {
+ $this->globalVars =& $globalVars;
+ $this->basePath = $directory;
+ $this->addCallableHandlers( $directory, $globalVars );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$vars
+ */
+ public static function initExtension( array &$vars ) {
+
+ $vars['wgContentHandlers'][CONTENT_MODEL_SMW_SCHEMA] = 'SMW\Schema\Content\ContentHandler';
+
+ /**
+ * CanonicalNamespaces initialization
+ *
+ * @note According to T104954 registration via wgExtensionFunctions can be
+ * too late and should happen before that in case RequestContext::getLanguage
+ * invokes Language::getNamespaces before the `wgExtensionFunctions` execution.
+ *
+ * @see https://phabricator.wikimedia.org/T104954#2391291
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/CanonicalNamespaces
+ * @Bug 34383
+ */
+ $vars['wgHooks']['CanonicalNamespaces'][] = function( array &$namespaces ) {
+
+ NamespaceManager::initCanonicalNamespaces(
+ $namespaces
+ );
+
+ return true;
+ };
+
+ /**
+ * To add to or remove pages from the special page list. This array has
+ * the same structure as $wgSpecialPages.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialPage_initList
+ *
+ * #2813
+ */
+ $vars['wgHooks']['SpecialPage_initList'][] = function( array &$specialPages ) {
+
+ Setup::initSpecialPageList(
+ $specialPages
+ );
+
+ return true;
+ };
+
+ /**
+ * Called when ApiMain has finished initializing its module manager. Can
+ * be used to conditionally register API modules.
+ *
+ * #2813
+ */
+ $vars['wgHooks']['ApiMain::moduleManager'][] = function( $apiModuleManager ) {
+
+ $apiModuleManager->addModules(
+ Setup::getAPIModules(),
+ 'action'
+ );
+
+ return true;
+ };
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $name
+ *
+ * @return boolean
+ */
+ public function isRegistered( $name ) {
+ // return \Hooks::isRegistered( $name );
+ return isset( $this->handlers[$name] );
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function clear() {
+ foreach ( $this->getHandlerList() as $name ) {
+ \Hooks::clear( $name );
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $name
+ *
+ * @return Callable|false
+ */
+ public function getHandlerFor( $name ) {
+ return isset( $this->handlers[$name] ) ? $this->handlers[$name] : false;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getHandlerList() {
+ return array_keys( $this->handlers );
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function register() {
+ foreach ( $this->handlers as $name => $callback ) {
+ //\Hooks::register( $name, $callback );
+ $this->globalVars['wgHooks'][$name][] = $callback;
+ }
+ }
+
+ private function addCallableHandlers( $basePath, $globalVars ) {
+
+ $hookListener = new HookListener( $this->globalVars, $this->basePath );
+ $elasticFactory = ApplicationFactory::getInstance()->singleton( 'ElasticFactory' );
+
+ $hooks = [
+ 'ParserAfterTidy' => [ $hookListener, 'onParserAfterTidy' ],
+ 'ParserOptionsRegister' => [ $hookListener, 'onParserOptionsRegister' ],
+ 'ParserFirstCallInit' => [ $hookListener, 'onParserFirstCallInit' ],
+ 'InternalParseBeforeLinks' => [ $hookListener, 'onInternalParseBeforeLinks' ],
+ 'RejectParserCacheValue' => [ $hookListener, 'onRejectParserCacheValue' ],
+ 'IsFileCacheable' => [ $hookListener, 'onIsFileCacheable' ],
+
+ 'BaseTemplateToolbox' => [ $hookListener, 'onBaseTemplateToolbox' ],
+ 'SkinAfterContent' => [ $hookListener, 'onSkinAfterContent' ],
+ 'OutputPageParserOutput' => [ $hookListener, 'onOutputPageParserOutput' ],
+ 'OutputPageCheckLastModified' => [ $hookListener, 'onOutputPageCheckLastModified' ],
+ 'BeforePageDisplay' => [ $hookListener, 'onBeforePageDisplay' ],
+ 'BeforeDisplayNoArticleText' => [ $hookListener, 'onBeforeDisplayNoArticleText' ],
+ 'EditPage::showEditForm:initial' => [ $hookListener, 'onEditPageShowEditFormInitial' ],
+
+ 'TitleMoveComplete' => [ $hookListener, 'onTitleMoveComplete' ],
+ 'TitleIsAlwaysKnown' => [ $hookListener, 'onTitleIsAlwaysKnown' ],
+ 'TitleQuickPermissions' => [ $hookListener, 'onTitleQuickPermissions' ],
+ 'TitleIsMovable' => [ $hookListener, 'onTitleIsMovable' ],
+
+ 'ArticlePurge' => [ $hookListener, 'onArticlePurge' ],
+ 'ArticleDelete' => [ $hookListener, 'onArticleDelete' ],
+ 'ArticleFromTitle' => [ $hookListener, 'onArticleFromTitle' ],
+ 'ArticleProtectComplete' => [ $hookListener, 'onArticleProtectComplete' ],
+ 'ArticleViewHeader' => [ $hookListener, 'onArticleViewHeader' ],
+ 'ContentHandlerForModelID' => [ $hookListener, 'onContentHandlerForModelID' ],
+
+ 'NewRevisionFromEditComplete' => [ $hookListener, 'onNewRevisionFromEditComplete' ],
+ 'LinksUpdateConstructed' => [ $hookListener, 'onLinksUpdateConstructed' ],
+ 'FileUpload' => [ $hookListener, 'onFileUpload' ],
+
+ 'ResourceLoaderGetConfigVars' => [ $hookListener, 'onResourceLoaderGetConfigVars' ],
+ 'ResourceLoaderTestModules' => [ $hookListener, 'onResourceLoaderTestModules' ],
+ 'GetPreferences' => [ $hookListener, 'onGetPreferences' ],
+ 'PersonalUrls' => [ $hookListener, 'onPersonalUrls' ],
+ 'SkinTemplateNavigation' => [ $hookListener, 'onSkinTemplateNavigation' ],
+ 'LoadExtensionSchemaUpdates' => [ $hookListener, 'onLoadExtensionSchemaUpdates' ],
+
+ 'ExtensionTypes' => [ $hookListener, 'onExtensionTypes' ],
+ 'SpecialStatsAddExtra' => [ $hookListener, 'onSpecialStatsAddExtra' ],
+ 'SpecialSearchResultsPrepend' => [ $hookListener, 'onSpecialSearchResultsPrepend' ],
+ 'SpecialSearchProfileForm' => [ $hookListener, 'onSpecialSearchProfileForm' ],
+ 'SpecialSearchProfiles' => [ $hookListener, 'onSpecialSearchProfiles' ],
+ 'SoftwareInfo' => [ $hookListener, 'onSoftwareInfo' ],
+
+ 'BlockIpComplete' => [ $hookListener, 'onBlockIpComplete' ],
+ 'UnblockUserComplete' => [ $hookListener, 'onUnblockUserComplete' ],
+ 'UserGroupsChanged' => [ $hookListener, 'onUserGroupsChanged' ],
+
+ 'SMW::SQLStore::EntityReferenceCleanUpComplete' => [ $elasticFactory, 'onEntityReferenceCleanUpComplete' ],
+ 'SMW::Admin::TaskHandlerFactory' => [ $elasticFactory, 'onTaskHandlerFactory' ],
+ ];
+
+ foreach ( $hooks as $hook => $handler ) {
+ $this->handlers[$hook] = is_callable( $handler ) ? $handler : [ $this, $handler ];
+ }
+
+ $this->registerHooksForInternalUse();
+ }
+
+ private function registerHooksForInternalUse() {
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::SQLStore::AfterDataUpdateComplete
+ */
+ $this->handlers['SMW::SQLStore::AfterDataUpdateComplete'] = function ( $store, $semanticData, $changeOp ) {
+
+ // A delete infused change should trigger an immediate update
+ // without having to wait on the job queue
+ $isPrimaryUpdate = $semanticData->getOption( SemanticData::PROC_DELETE, false );
+
+ $queryDependencyLinksStoreFactory = ApplicationFactory::getInstance()->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $queryDependencyLinksStore = $queryDependencyLinksStoreFactory->newQueryDependencyLinksStore(
+ $store
+ );
+
+ $queryDependencyLinksStore->pruneOutdatedTargetLinks(
+ $changeOp
+ );
+
+ $entityIdListRelevanceDetectionFilter = $queryDependencyLinksStoreFactory->newEntityIdListRelevanceDetectionFilter(
+ $store,
+ $changeOp
+ );
+
+ $queryDependencyLinksStore->isPrimary( $isPrimaryUpdate );
+
+ $queryDependencyLinksStore->pushParserCachePurgeJob(
+ $entityIdListRelevanceDetectionFilter
+ );
+
+ $fulltextSearchTableFactory = new FulltextSearchTableFactory();
+
+ $textChangeUpdater = $fulltextSearchTableFactory->newTextChangeUpdater(
+ $store
+ );
+
+ $textChangeUpdater->isPrimary( $isPrimaryUpdate );
+
+ $textChangeUpdater->pushUpdates(
+ $changeOp
+ );
+
+ return true;
+ };
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::Store::BeforeQueryResultLookupComplete
+ */
+ $this->handlers['SMW::Store::BeforeQueryResultLookupComplete'] = function ( $store, $query, &$result, $queryEngine ) {
+
+ $cachedQueryResultPrefetcher = ApplicationFactory::getInstance()->singleton( 'CachedQueryResultPrefetcher' );
+
+ $cachedQueryResultPrefetcher->setQueryEngine(
+ $queryEngine
+ );
+
+ if ( !$cachedQueryResultPrefetcher->isEnabled() ) {
+ return true;
+ }
+
+ $result = $cachedQueryResultPrefetcher->getQueryResult(
+ $query
+ );
+
+ return false;
+ };
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::Store::AfterQueryResultLookupComplete
+ */
+ $this->handlers['SMW::Store::AfterQueryResultLookupComplete'] = function ( $store, &$result ) {
+
+ $queryDependencyLinksStoreFactory = ApplicationFactory::getInstance()->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $queryDependencyLinksStore = $queryDependencyLinksStoreFactory->newQueryDependencyLinksStore(
+ $store
+ );
+
+ $queryDependencyLinksStore->updateDependencies( $result );
+
+ ApplicationFactory::getInstance()->singleton( 'CachedQueryResultPrefetcher' )->recordStats();
+
+ $store->getObjectIds()->warmUpCache( $result );
+
+ return true;
+ };
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks/Browse::AfterIncomingPropertiesLookupComplete
+ */
+ $this->handlers['SMW::Browse::AfterIncomingPropertiesLookupComplete'] = function ( $store, $semanticData, $requestOptions ) {
+
+ $queryDependencyLinksStoreFactory = ApplicationFactory::getInstance()->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $queryReferenceBacklinks = $queryDependencyLinksStoreFactory->newQueryReferenceBacklinks(
+ $store
+ );
+
+ $queryReferenceBacklinks->addReferenceLinksTo(
+ $semanticData,
+ $requestOptions
+ );
+
+ return true;
+ };
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks/Browse::BeforeIncomingPropertyValuesFurtherLinkCreate
+ */
+ $this->handlers['SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate'] = function ( $property, $subject, &$html, $store ) {
+
+ $queryDependencyLinksStoreFactory = ApplicationFactory::getInstance()->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $queryReferenceBacklinks = $queryDependencyLinksStoreFactory->newQueryReferenceBacklinks(
+ $store
+ );
+
+ $doesRequireFurtherLink = $queryReferenceBacklinks->doesRequireFurtherLink(
+ $property,
+ $subject,
+ $html
+ );
+
+ // Return false in order to stop the link creation process to replace the
+ // standard link
+ return $doesRequireFurtherLink;
+ };
+
+ /**
+ * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::Store::AfterQueryResultLookupComplete
+ */
+ $this->handlers['SMW::SQLStore::Installer::AfterCreateTablesComplete'] = function ( $tableBuilder, $messageReporter, $options ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $importerServiceFactory = $applicationFactory->create( 'ImporterServiceFactory' );
+
+ $importer = $importerServiceFactory->newImporter(
+ $importerServiceFactory->newJsonContentIterator(
+ $applicationFactory->getSettings()->get( 'smwgImportFileDirs' )
+ )
+ );
+
+ $importer->isEnabled( $options->safeGet( \SMW\SQLStore\Installer::OPT_IMPORT, false ) );
+ $importer->setMessageReporter( $messageReporter );
+ $importer->doImport();
+
+ return true;
+ };
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/InternalParseBeforeLinks.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/InternalParseBeforeLinks.php
new file mode 100644
index 00000000..9994016a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/InternalParseBeforeLinks.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Parser;
+use SMW\ApplicationFactory;
+use SMW\Parser\InTextAnnotationParser;
+use StripState;
+
+/**
+ * Hook: InternalParseBeforeLinks is used to process the expanded wiki
+ * code after <nowiki>, HTML-comments, and templates have been treated.
+ *
+ * This method will be called before an article is displayed or previewed.
+ * For display and preview we strip out the semantic properties and append them
+ * at the end of the article.
+ *
+ * @note MW 1.20+ see InternalParseBeforeSanitize
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks
+ *
+ * @ingroup FunctionHook
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InternalParseBeforeLinks extends HookHandler {
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * @var StripState
+ */
+ private $stripState;
+
+ /**
+ * @since 1.9
+ *
+ * @param Parser $parser
+ * @param StripState $stripState
+ */
+ public function __construct( Parser &$parser, $stripState ) {
+ $this->parser = $parser;
+ $this->stripState = $stripState;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string $text
+ *
+ * @return true
+ */
+ public function process( &$text ) {
+
+ if ( !$this->canPerformUpdate( $text, $this->parser->getTitle() ) ) {
+ return true;
+ }
+
+ return $this->performUpdate( $text );
+ }
+
+ private function canPerformUpdate( $text, $title ) {
+
+ if ( $this->getRedirectTarget() !== null ) {
+ return true;
+ }
+
+ // #2209, #2370 Allow content to be parsed that contain [[SMW::off]]/[[SMW::on]]
+ // even in case of MediaWiki messages
+ if ( InTextAnnotationParser::hasMarker( $text ) ) {
+ return true;
+ }
+
+ // ParserOptions::getInterfaceMessage is being used to identify whether a
+ // parse was initiated by `Message::parse`
+ if ( $text === '' || $this->parser->getOptions()->getInterfaceMessage() ) {
+ return false;
+ }
+
+ if ( !$title->isSpecialPage() ) {
+ return true;
+ }
+
+ // #2529
+ foreach ( $this->getOption( 'smwgEnabledSpecialPage', [] ) as $specialPage ) {
+ if ( is_string( $specialPage ) && $title->isSpecial( $specialPage ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function performUpdate( &$text ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ /**
+ * @var ParserData $parserData
+ */
+ $parserData = $applicationFactory->newParserData(
+ $this->parser->getTitle(),
+ $this->parser->getOutput()
+ );
+
+ /**
+ * Performs [[link::syntax]] parsing and adding of property annotations
+ * to the ParserOutput
+ *
+ * @var InTextAnnotationParser
+ */
+ $inTextAnnotationParser = $applicationFactory->newInTextAnnotationParser(
+ $parserData
+ );
+
+ $stripMarkerDecoder = $applicationFactory->newMwCollaboratorFactory()->newStripMarkerDecoder(
+ $this->stripState
+ );
+
+ $inTextAnnotationParser->setStripMarkerDecoder(
+ $stripMarkerDecoder
+ );
+
+ $inTextAnnotationParser->setRedirectTarget(
+ $this->getRedirectTarget()
+ );
+
+ $inTextAnnotationParser->parse( $text );
+
+ $parserData->markParserOutput();
+
+ return true;
+ }
+
+ /**
+ * #656 / MW 1.24+
+ */
+ private function getRedirectTarget() {
+
+ if ( method_exists( $this->parser->getOptions(), 'getRedirectTarget' ) ) {
+ return $this->parser->getOptions()->getRedirectTarget();
+ }
+
+ return null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php
new file mode 100644
index 00000000..194c6916
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Hooks;
+use LinksUpdate;
+use SMW\ApplicationFactory;
+use SMW\SemanticData;
+use SMW\NamespaceExaminer;
+use Title;
+
+/**
+ * LinksUpdateConstructed hook is called at the end of LinksUpdate()
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdateConstructed
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class LinksUpdateConstructed extends HookHandler {
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var boolean
+ */
+ private $enabledDeferredUpdate = true;
+
+ /**
+ * @var boolean
+ */
+ private $isReadOnly = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param NamespaceExaminer $namespaceExaminer
+ */
+ public function __construct( NamespaceExaminer $namespaceExaminer ) {
+ $this->namespaceExaminer = $namespaceExaminer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isReadOnly
+ */
+ public function isReadOnly( $isReadOnly ) {
+ $this->isReadOnly = (bool)$isReadOnly;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function disableDeferredUpdate() {
+ $this->enabledDeferredUpdate = false;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param LinksUpdate $linksUpdate
+ *
+ * @return true
+ */
+ public function process( LinksUpdate $linksUpdate ) {
+
+ if ( $this->isReadOnly ) {
+ return false;
+ }
+
+ $title = $linksUpdate->getTitle();
+ $latestRevID = $title->getLatestRevID( Title::GAID_FOR_UPDATE );
+
+ $opts = [ 'defer' => $this->enabledDeferredUpdate ];
+
+ // Allow any third-party extension to suppress the update process
+ if ( \Hooks::run( 'SMW::LinksUpdate::ApprovedUpdate', [ $title, $latestRevID ] ) === false ) {
+ return true;
+ }
+
+ /**
+ * @var ParserData $parserData
+ */
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $title,
+ $linksUpdate->getParserOutput()
+ );
+
+ if ( $this->namespaceExaminer->isSemanticEnabled( $title->getNamespace() ) ) {
+ // #347 showed that an external process (e.g. RefreshLinksJob) can inject a
+ // ParserOutput without/cleared SemanticData which forces the Store updater
+ // to create an empty container that will clear all existing data.
+ if ( $parserData->getSemanticData()->isEmpty() ) {
+ $this->updateSemanticData( $parserData, $title, 'empty data' );
+ }
+ }
+
+ // Push updates on properties directly without delay
+ if ( $title->getNamespace() === SMW_NS_PROPERTY ) {
+ $opts['defer'] = false;
+ }
+
+ // Scan the ParserOutput for a possible externally set option
+ if ( $linksUpdate->getParserOutput()->getExtensionData( $parserData::OPT_FORCED_UPDATE ) === true ) {
+ $parserData->setOption( $parserData::OPT_FORCED_UPDATE, true );
+ }
+
+ // Update incurred by a template change and is signaled through
+ // the following condition
+ if ( $linksUpdate->mTemplates !== [] && $linksUpdate->mRecursive === false ) {
+ $parserData->setOption( $parserData::OPT_FORCED_UPDATE, true );
+ }
+
+ $parserData->setOrigin( 'LinksUpdateConstructed' );
+ $parserData->updateStore( $opts );
+
+ // Track the update on per revision because MW 1.29 made the LinksUpdate a
+ // EnqueueableDataUpdate which creates updates as JobSpecification
+ // (refreshLinksPrioritized) and posses a possibility of running an
+ // update more than once for the same RevID
+ $parserData->markUpdate( $latestRevID );
+
+ return true;
+ }
+
+ /**
+ * To ensure that for a Title and its current revision a ParserOutput
+ * object is really meant to be "empty" (e.g. delete action initiated by a
+ * human) the content is re-parsed in order to fetch the newest available data
+ *
+ * @note Parsing is expensive but it is more expensive to loose data or to
+ * expect that an external process adheres the object contract
+ */
+ private function updateSemanticData( &$parserData, $title, $reason = '' ) {
+
+ $this->log(
+ [
+ 'LinksUpdateConstructed',
+ "Required content re-parse due to $reason",
+ $title->getPrefixedDBKey()
+ ]
+ );
+
+ $semanticData = $this->reparseAndFetchSemanticData( $title );
+
+ if ( $semanticData instanceof SemanticData ) {
+ $parserData->setSemanticData( $semanticData );
+ }
+ }
+
+ private function reparseAndFetchSemanticData( $title ) {
+
+ $contentParser = ApplicationFactory::getInstance()->newContentParser( $title );
+ $parserOutput = $contentParser->parse()->getOutput();
+
+ if ( $parserOutput === null ) {
+ return null;
+ }
+
+ if ( method_exists( $parserOutput, 'getExtensionData' ) ) {
+ return $parserOutput->getExtensionData( 'smwdata' );
+ }
+
+ return $parserOutput->mSMWData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/NewRevisionFromEditComplete.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/NewRevisionFromEditComplete.php
new file mode 100644
index 00000000..b15b3fbc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/NewRevisionFromEditComplete.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\EventHandler;
+use SMW\MediaWiki\EditInfoProvider;
+use SMW\MediaWiki\PageInfoProvider;
+use Title;
+
+/**
+ * Hook: NewRevisionFromEditComplete called when a revision was inserted
+ * due to an edit
+ *
+ * Fetch additional information that is related to the saving that has just happened,
+ * e.g. regarding the last edit date. In runs where this hook is not triggered, the
+ * last DB entry (of MW) will be used to fill such properties.
+ *
+ * Called from LocalFile.php, SpecialImport.php, Article.php, Title.php
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/NewRevisionFromEditComplete
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NewRevisionFromEditComplete extends HookHandler {
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var EditInfoProvider
+ */
+ private $editInfoProvider;
+
+ /**
+ * @var PageInfoProvider
+ */
+ private $pageInfoProvider;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param EditInfoProvider $editInfoProvider
+ * @param PageInfoProvider $pageInfoProvider
+ */
+ public function __construct( Title $title, EditInfoProvider $editInfoProvider, PageInfoProvider $pageInfoProvider ) {
+ parent::__construct();
+ $this->title = $title;
+ $this->editInfoProvider = $editInfoProvider;
+ $this->pageInfoProvider = $pageInfoProvider;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function process() {
+
+ $parserOutput = $this->editInfoProvider->fetchEditInfo()->getOutput();
+ $schema = null;
+
+ if ( !$parserOutput instanceof ParserOutput ) {
+ return true;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $parserData = $applicationFactory->newParserData(
+ $this->title,
+ $parserOutput
+ );
+
+ if ( $this->title->getNamespace() === SMW_NS_SCHEMA ) {
+ $schemaFactory = $applicationFactory->singleton( 'SchemaFactory' );
+
+ try {
+ $schema = $schemaFactory->newSchema(
+ $this->title->getDBKey(),
+ $this->pageInfoProvider->getNativeData()
+ );
+ } catch( \Exception $e ) {
+ // Do nothing!
+ }
+ }
+
+ $this->addPredefinedPropertyAnnotation(
+ $applicationFactory,
+ $parserData,
+ $schema
+ );
+
+ $dispatchContext = EventHandler::getInstance()->newDispatchContext();
+ $dispatchContext->set( 'title', $this->title );
+ $dispatchContext->set( 'context', 'NewRevisionFromEditComplete' );
+
+ EventHandler::getInstance()->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+
+ // If the concept was altered make sure to delete the cache
+ if ( $this->title->getNamespace() === SMW_NS_CONCEPT ) {
+ $applicationFactory->getStore()->deleteConceptCache( $this->title );
+ }
+
+ $parserData->pushSemanticDataToParserOutput();
+
+ return true;
+ }
+
+ private function addPredefinedPropertyAnnotation( $applicationFactory, $parserData, $schema = null ) {
+
+ $propertyAnnotatorFactory = $applicationFactory->singleton( 'PropertyAnnotatorFactory' );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newNullPropertyAnnotator(
+ $parserData->getSemanticData()
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newPredefinedPropertyAnnotator(
+ $propertyAnnotator,
+ $this->pageInfoProvider
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newSchemaPropertyAnnotator(
+ $propertyAnnotator,
+ $schema
+ );
+
+ $propertyAnnotator->addAnnotation();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/OutputPageParserOutput.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/OutputPageParserOutput.php
new file mode 100644
index 00000000..f0448c3e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/OutputPageParserOutput.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use OutputPage;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\Query\QueryRefFinder;
+use Title;
+
+/**
+ * OutputPageParserOutput hook is called after parse, before the HTML is
+ * added to the output
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/OutputPageParserOutput
+ *
+ * @note This hook copies SMW's custom data from the given ParserOutput object to
+ * the given OutputPage object, since otherwise it is not possible to access
+ * it later on to build a Factbox.
+ *
+ * @ingroup FunctionHook
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class OutputPageParserOutput {
+
+ /**
+ * @var OutputPage
+ */
+ protected $outputPage = null;
+
+ /**
+ * @var ParserOutput
+ */
+ protected $parserOutput = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param OutputPage $outputPage
+ * @param ParserOutput $parserOutput
+ */
+ public function __construct( OutputPage &$outputPage, ParserOutput $parserOutput ) {
+ $this->outputPage = $outputPage;
+ $this->parserOutput = $parserOutput;
+ }
+
+ /**
+ * @see FunctionHook::process
+ *
+ * @since 1.9
+ *
+ * @return true
+ */
+ public function process() {
+
+ $title = $this->outputPage->getTitle();
+
+ if ( $title->isSpecialPage() ||
+ $title->isRedirect() ||
+ !$this->isSemanticEnabledNamespace( $title ) ) {
+ return true;
+ }
+
+ $request = $this->outputPage->getContext()->getRequest();
+
+ $this->factbox( $request );
+ $this->postProc( $title, $request );
+ }
+
+ private function postProc( $title, $request) {
+
+ if ( in_array( $request->getVal( 'action' ), [ 'delete', 'purge', 'protect', 'unprotect', 'history', 'edit' ] ) ) {
+ return '';
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $postProcHandler = $applicationFactory->create( 'PostProcHandler', $this->parserOutput );
+
+ $html = $postProcHandler->getHtml(
+ $title,
+ $request
+ );
+
+ if ( $html !== '' ) {
+ $this->outputPage->addModules( $postProcHandler->getModules() );
+ $this->outputPage->addHtml( $html );
+ }
+ }
+
+ protected function factbox( $request ) {
+
+ if ( isset( $this->outputPage->mSMWFactboxText ) && $request->getCheck( 'wpPreview' ) ) {
+ return '';
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $cachedFactbox = $applicationFactory->singleton( 'FactboxFactory' )->newCachedFactbox();
+
+ $cachedFactbox->prepareFactboxContent(
+ $this->outputPage,
+ $this->outputPage->getLanguage(),
+ $this->getParserOutput()
+ );
+
+ return true;
+ }
+
+ protected function getParserOutput() {
+
+ if ( $this->outputPage->getContext()->getRequest()->getInt( 'oldid' ) ) {
+
+ $text = $this->parserOutput->getText();
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $this->outputPage->getTitle(),
+ $this->parserOutput
+ );
+
+ $inTextAnnotationParser = ApplicationFactory::getInstance()->newInTextAnnotationParser( $parserData );
+ $inTextAnnotationParser->parse( $text );
+
+ return $parserData->getOutput();
+ }
+
+ return $this->parserOutput;
+ }
+
+ private function isSemanticEnabledNamespace( Title $title ) {
+ return ApplicationFactory::getInstance()->getNamespaceExaminer()->isSemanticEnabled( $title->getNamespace() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ParserAfterTidy.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ParserAfterTidy.php
new file mode 100644
index 00000000..96ee83d1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ParserAfterTidy.php
@@ -0,0 +1,258 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Parser;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\MediaWiki;
+use SMW\ParserData;
+use SMW\SemanticData;
+
+/**
+ * Hook: ParserAfterTidy to add some final processing to the
+ * fully-rendered page output
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterTidy
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserAfterTidy extends HookHandler {
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isReadOnly = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param Parser $parser
+ */
+ public function __construct( Parser &$parser ) {
+ $this->parser = $parser;
+ $this->namespaceExaminer = ApplicationFactory::getInstance()->getNamespaceExaminer();
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = (bool)$isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isReadOnly
+ */
+ public function isReadOnly( $isReadOnly ) {
+ $this->isReadOnly = (bool)$isReadOnly;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string $text
+ *
+ * @return true
+ */
+ public function process( &$text ) {
+
+ if ( $this->canPerformUpdate() ) {
+ $this->performUpdate( $text );
+ }
+
+ return true;
+ }
+
+ private function canPerformUpdate() {
+
+ // #2432 avoid access to the DBLoadBalancer while being in readOnly mode
+ // when for example Title::isProtected is accessed
+ if ( $this->isReadOnly ) {
+ return false;
+ }
+
+ $title = $this->parser->getTitle();
+
+ if ( !$this->namespaceExaminer->isSemanticEnabled( $title->getNamespace() ) ) {
+ return false;
+ }
+
+ // Avoid an update for the SCHEMA NS to ensure errors remain present without
+ // the need the rerun the schema validator again.
+ if ( $title->getNamespace() === SMW_NS_SCHEMA ) {
+ return false;
+ }
+
+ // ParserOptions::getInterfaceMessage is being used to identify whether a
+ // parse was initiated by `Message::parse`
+ if ( $title->isSpecialPage() || $this->parser->getOptions()->getInterfaceMessage() ) {
+ return false;
+ }
+
+ $parserOutput = $this->parser->getOutput();
+
+ if ( $parserOutput->getProperty( 'displaytitle' ) ||
+ $parserOutput->getExtensionData( 'translate-translation-page' ) ||
+ $parserOutput->getCategoryLinks() ) {
+ return true;
+ }
+
+ if ( ParserData::hasSemanticData( $parserOutput ) ||
+ $title->isProtected( 'edit' ) ||
+ $this->parser->getDefaultSort() ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function performUpdate( &$text ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $parserData = $applicationFactory->newParserData(
+ $this->parser->getTitle(),
+ $this->parser->getOutput()
+ );
+
+ $semanticData = $parserData->getSemanticData();
+
+ $this->addPropertyAnnotations(
+ $applicationFactory->singleton( 'PropertyAnnotatorFactory' ),
+ $semanticData
+ );
+
+ $parserData->copyToParserOutput();
+ $subject = $semanticData->getSubject();
+
+ // Only carry out a purge where the InTextAnnotationParser have set
+ // an appropriate context reference otherwise it is assumed that the hook
+ // call is part of another non SMW related parse
+ if ( $subject->getContextReference() !== null || $subject->getNamespace() === SMW_NS_SCHEMA ) {
+ $this->checkPurgeRequest( $parserData );
+ }
+ }
+
+ private function addPropertyAnnotations( $propertyAnnotatorFactory, $semanticData ) {
+
+ $parserOutput = $this->parser->getOutput();
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newNullPropertyAnnotator(
+ $semanticData
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newCategoryPropertyAnnotator(
+ $propertyAnnotator,
+ $parserOutput->getCategoryLinks()
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newMandatoryTypePropertyAnnotator(
+ $propertyAnnotator
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newEditProtectedPropertyAnnotator(
+ $propertyAnnotator,
+ $this->parser->getTitle()
+ );
+
+ // Special case! belongs to the EditProtectedPropertyAnnotator instance
+ $propertyAnnotator->addTopIndicatorTo(
+ $parserOutput
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newDisplayTitlePropertyAnnotator(
+ $propertyAnnotator,
+ $parserOutput->getProperty( 'displaytitle' ),
+ $this->parser->getDefaultSort()
+ );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newSortKeyPropertyAnnotator(
+ $propertyAnnotator,
+ $this->parser->getDefaultSort()
+ );
+
+ // #2300
+ $propertyAnnotator = $propertyAnnotatorFactory->newTranslationPropertyAnnotator(
+ $propertyAnnotator,
+ $parserOutput->getExtensionData( 'translate-translation-page' )
+ );
+
+ $propertyAnnotator->addAnnotation();
+ }
+
+ /**
+ * @note Article purge: In case an article was manually purged/moved
+ * the store is updated as well; for all other cases LinksUpdateConstructed
+ * will handle the store update
+ *
+ * @note The purge action is isolated from any other request therefore using
+ * a static variable or any other messaging that is not persistent will not
+ * work hence the reliance on the cache as temporary persistence marker
+ */
+ private function checkPurgeRequest( $parserData ) {
+
+ $cache = ApplicationFactory::getInstance()->getCache();
+ $start = microtime( true );
+
+ $key = ApplicationFactory::getInstance()->getCacheFactory()->getPurgeCacheKey(
+ $this->parser->getTitle()->getArticleID()
+ );
+
+ if( $cache->contains( $key ) && $cache->fetch( $key ) ) {
+ $cache->delete( $key );
+
+ // Avoid a Parser::lock for when a PurgeRequest remains intact
+ // during an update process while being executed from the cmdLine
+ if ( $this->isCommandLineMode ) {
+ return true;
+ }
+
+ $parserData->setOrigin( 'ParserAfterTidy' );
+
+ // Set an explicit timestamp to create a new hash for the property
+ // table change row differ and force a data comparison (this doesn't
+ // change the _MDAT annotation)
+ $parserData->getSemanticData()->setOption(
+ SemanticData::OPT_LAST_MODIFIED,
+ wfTimestamp( TS_UNIX )
+ );
+
+ $parserData->setOption(
+ $parserData::OPT_FORCED_UPDATE,
+ true
+ );
+
+ $parserData->updateStore( true );
+
+ $parserData->addLimitReport(
+ 'pagepurge-storeupdatetime',
+ number_format( ( microtime( true ) - $start ), 3 )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/PersonalUrls.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/PersonalUrls.php
new file mode 100644
index 00000000..5af209bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/PersonalUrls.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SkinTemplate;
+use SMW\MediaWiki\JobQueue;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/PersonalUrls
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PersonalUrls extends HookHandler {
+
+ /**
+ * @var SkinTemplate
+ */
+ private $skin;
+
+ /**
+ * @var JobQueue
+ */
+ private $jobQueue;
+
+ /**
+ * @since 3.0
+ *
+ * @param SkinTemplate $skin
+ * @param JobQueue $jobQueue
+ */
+ public function __construct( SkinTemplate $skin, JobQueue $jobQueue ) {
+ $this->skin = $skin;
+ $this->jobQueue = $jobQueue;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$personalUrls
+ *
+ * @return true
+ */
+ public function process( array &$personalUrls ) {
+
+ $watchlist = $this->getOption( 'smwgJobQueueWatchlist', [] );
+
+ if ( $this->getOption( 'prefs-jobqueue-watchlist' ) !== null && $watchlist !== [] ) {
+ $this->addJobQueueWatchlist( $watchlist, $personalUrls );
+ }
+
+ return true;
+ }
+
+ private function addJobQueueWatchlist( $watchlist, &$personalUrls ) {
+
+ $queue = [];
+
+ foreach ( $watchlist as $job ) {
+ $size = $this->jobQueue->getQueueSize( $job );
+
+ if ( $size > 0 ) {
+ $queue[$job] = $this->humanReadable( $size );
+ }
+ }
+
+ $out = $this->skin->getOutput();
+ $personalUrl = [];
+
+ $out->addModules( 'ext.smw.personal' );
+ $out->addJsConfigVars( 'smwgJobQueueWatchlist', $queue );
+
+ $personalUrl['smw-jobqueue-watchlist'] = [
+ 'text' => 'â…‰ [ ' . ( $queue === [] ? '0' : implode( ' | ', $queue ) ) . ' ]' ,
+ 'href' => '#',
+ 'class' => 'smw-personal-jobqueue-watchlist is-disabled',
+ 'active' => true
+ ];
+
+ $keys = array_keys( $personalUrls );
+
+ // Insert the link before the watchlist
+ $personalUrls = $this->splice(
+ $personalUrls,
+ $personalUrl,
+ array_search( 'watchlist', $keys )
+ );
+ }
+
+ // https://stackoverflow.com/questions/1783089/array-splice-for-associative-arrays
+ private function splice( $array, $values, $offset ) {
+ return array_slice( $array, 0, $offset, true ) + $values + array_slice( $array, $offset, NULL, true );
+ }
+
+ private function humanReadable( $num, $decimals = 0 ) {
+
+ if ( $num < 1000 ) {
+ $num = number_format( $num );
+ } else if ( $num < 1000000) {
+ $num = number_format( $num / 1000, $decimals ) . 'K';
+ } else {
+ $num = number_format( $num / 1000000, $decimals ) . 'M';
+ }
+
+ return $num;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/README.md b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/README.md
new file mode 100644
index 00000000..0f56b3ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/README.md
@@ -0,0 +1,39 @@
+[Hooks][hooks] are so-called event handlers that allow custom code to be executed. Semantic MediaWiki (SMW) uses several of those hooks to enable specific process logic to be integrated with MediaWiki. SMW currently uses the following hooks:
+
+#### ArticlePurge
+ArticlePurge is executed during a manual purge action of an article and depending on available settings is being used to track a page refresh.
+
+#### BeforePageDisplay
+BeforePageDisplay allows last minute changes to the output page and is being used to render a ExportRDF link into each individual article.
+
+#### InternalParseBeforeLinks
+InternalParseBeforeLinks is used to process and expand text content, and in case of SMW it is used to identify and resolve the property annotation syntax ([[link::syntax]]), returning a modified content component and storing annotations within the ParserOutput object.
+
+#### LinksUpdateConstructed
+LinksUpdateConstructed is called at the end of LinksUpdate and is being used to initiate a store update for data that were held by the ParserOutput object.
+
+#### NewRevisionFromEditComplete
+NewRevisionFromEditComplete called when a new revision was inserted due to an edit and used to update the ParserOuput with the latests special property annotation.
+
+#### ParserAfterTidy
+ParserAfterTidy is used to re-introduce content, update base annotations (e.g. special properties, categories etc.) and in case of a manual article purge initiates a store update (LinksUpdateConstructed wouldn't work because it acts only on link changes and therefore would not trigger a LinksUpdateConstructed event).
+
+#### SpecialStatsAddExtra
+SpecialStatsAddExtra is used to add additional statistic being shown at Special:Statistics.
+
+#### SkinAfterContent
+Extend the display with content from the Factbox.
+
+#### OutputPageParserOutput
+Rendering the Factbox and updating the FactboxCache.
+
+#### TitleMoveComplete
+Update the Store after an article has been deleted.
+
+#### ResourceLoaderGetConfigVars
+
+#### GetPreferences
+
+#### SkinTemplateNavigation
+
+[hooks]: https://www.mediawiki.org/wiki/Hooks "Manual:Hooks" \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/RejectParserCacheValue.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/RejectParserCacheValue.php
new file mode 100644
index 00000000..b2b21af2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/RejectParserCacheValue.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal;
+use Title;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/RejectParserCacheValue
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RejectParserCacheValue extends HookHandler {
+
+ /**
+ * @var DependencyLinksUpdateJournal
+ */
+ private $dependencyLinksUpdateJournal;
+
+ /**
+ * @since 3.0
+ *
+ * @param DependencyLinksUpdateJournal $dependencyLinksUpdateJournal
+ */
+ public function __construct( DependencyLinksUpdateJournal $dependencyLinksUpdateJournal ) {
+ $this->dependencyLinksUpdateJournal = $dependencyLinksUpdateJournal;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function process( Title $title ) {
+
+ if ( $this->dependencyLinksUpdateJournal->has( $title ) ) {
+ $this->dependencyLinksUpdateJournal->delete( $title );
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderGetConfigVars.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderGetConfigVars.php
new file mode 100644
index 00000000..1117347d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderGetConfigVars.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use MWNamespace;
+use SMW\Localizer;
+
+/**
+ * Hook: ResourceLoaderGetConfigVars called right before
+ * ResourceLoaderStartUpModule::getConfig and exports static configuration
+ * variables to JavaScript. Things that depend on the current
+ * page/request state should use MakeGlobalVariablesScript instead
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderGetConfigVars
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ResourceLoaderGetConfigVars extends HookHandler {
+
+ /**
+ * @since 1.9
+ *
+ * @param array $vars
+ *
+ * @return boolean
+ */
+ public function process( array &$vars ) {
+
+ $vars['smw-config'] = [
+ 'version' => SMW_VERSION,
+ 'namespaces' => [],
+ 'settings' => [
+ 'smwgQMaxLimit' => $GLOBALS['smwgQMaxLimit'],
+ 'smwgQMaxInlineLimit' => $GLOBALS['smwgQMaxInlineLimit'],
+ ]
+ ];
+
+ $localizer = Localizer::getInstance();
+
+ // Available semantic namespaces
+ foreach ( array_keys( $GLOBALS['smwgNamespacesWithSemanticLinks'] ) as $ns ) {
+ $name = MWNamespace::getCanonicalName( $ns );
+ $vars['smw-config']['settings']['namespace'][$name] = $ns;
+ $vars['smw-config']['namespaces']['canonicalName'][$ns] = $name;
+ $vars['smw-config']['namespaces']['localizedName'][$ns] = $localizer->getNamespaceTextById( $ns );
+ }
+
+ foreach ( array_keys( $GLOBALS['smwgResultFormats'] ) as $format ) {
+ $vars['smw-config']['formats'][$format] = htmlspecialchars( $format );
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderTestModules.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderTestModules.php
new file mode 100644
index 00000000..6fcc5acf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/ResourceLoaderTestModules.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use ResourceLoader;
+
+/**
+ * Add new JavaScript/QUnit testing modules
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ResourceLoaderTestModules extends HookHandler {
+
+ /**
+ * @var ResourceLoader
+ */
+ private $resourceLoader;
+
+ /**
+ * @var string
+ */
+ private $path;
+
+ /**
+ * @var string
+ */
+ private $ip;
+
+ /**
+ * @since 2.0
+ *
+ * @param ResourceLoader $resourceLoader object
+ * @param string $path
+ * @param string $ip
+ */
+ public function __construct( ResourceLoader &$resourceLoader, $path = '', $ip = '' ) {
+ $this->resourceLoader = $resourceLoader;
+ $this->path = $path;
+ $this->ip = $ip;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array &$testModules
+ *
+ * @return boolean
+ */
+ public function process( array &$testModules ) {
+
+ $testModules['qunit']['ext.smw.tests'] = [
+ 'scripts' => [
+ 'tests/qunit/smw/ext.smw.test.js',
+ 'tests/qunit/smw/util/ext.smw.util.tooltip.test.js',
+
+ // dataItem tests
+ 'tests/qunit/smw/data/ext.smw.dataItem.wikiPage.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.uri.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.time.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.property.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.unknown.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.number.test.js',
+ 'tests/qunit/smw/data/ext.smw.dataItem.text.test.js',
+
+ // dataValues
+ 'tests/qunit/smw/data/ext.smw.dataValue.quantity.test.js',
+
+ // Api / Query
+ 'tests/qunit/smw/data/ext.smw.data.test.js',
+ 'tests/qunit/smw/api/ext.smw.api.test.js',
+ 'tests/qunit/smw/query/ext.smw.query.test.js',
+ ],
+ 'dependencies' => [
+ 'ext.smw',
+ 'ext.smw.tooltip',
+ 'ext.smw.query',
+ 'ext.smw.data',
+ 'ext.smw.api'
+ ],
+ 'position' => 'top',
+ 'localBasePath' => $this->path,
+ 'remoteExtPath' => 'SemanticMediaWiki',
+ ];
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinAfterContent.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinAfterContent.php
new file mode 100644
index 00000000..a6fd902b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinAfterContent.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Skin;
+use SMW\ApplicationFactory;
+
+/**
+ * SkinAfterContent hook to add text after the page content and
+ * article metadata
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinAfterContent
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SkinAfterContent {
+
+ /**
+ * @var Skin
+ */
+ private $skin = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param Skin|null $skin
+ */
+ public function __construct( Skin $skin = null ) {
+ $this->skin = $skin;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string &$data
+ *
+ * @return true
+ */
+ public function performUpdate( &$data ) {
+
+ if ( $this->canAddFactbox() ) {
+ $this->addFactboxTo( $data );
+ }
+
+ return true;
+ }
+
+ private function canAddFactbox() {
+
+ if ( !$this->skin instanceof Skin || !ApplicationFactory::getInstance()->getSettings()->get( 'smwgSemanticsEnabled' ) ) {
+ return false;
+ }
+
+ $request = $this->skin->getContext()->getRequest();
+
+ if ( in_array( $request->getVal( 'action' ), [ 'delete', 'purge', 'protect', 'unprotect', 'history' ] ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private function addFactboxTo( &$data ) {
+
+ $cachedFactbox = ApplicationFactory::getInstance()->singleton( 'FactboxFactory' )->newCachedFactbox();
+
+ $data .= $cachedFactbox->retrieveContent(
+ $this->skin->getOutput()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinTemplateNavigation.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinTemplateNavigation.php
new file mode 100644
index 00000000..a1a5b109
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SkinTemplateNavigation.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SkinTemplate;
+
+/**
+ * Alter the structured navigation links in SkinTemplates.
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinTemplateNavigation
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SkinTemplateNavigation {
+
+ /**
+ * @var SkinTemplate
+ */
+ private $skinTemplate = null;
+
+ /**
+ * @var array
+ */
+ private $links;
+
+ /**
+ * @since 2.0
+ *
+ * @param SkinTemplate $skinTemplate
+ * @param array $links
+ */
+ public function __construct( SkinTemplate &$skinTemplate, array &$links ) {
+ $this->skinTemplate = $skinTemplate;
+ $this->links =& $links;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return true
+ */
+ public function process() {
+
+ if ( $this->skinTemplate->getUser()->isAllowed( 'purge' ) ) {
+ $this->skinTemplate->getOutput()->addModules( 'ext.smw.purge' );
+ $this->links['actions']['purge'] = [
+ 'class' => 'is-disabled',
+ 'text' => $this->skinTemplate->msg( 'smw_purge' )->text(),
+ 'href' => $this->skinTemplate->getTitle()->getLocalUrl( [ 'action' => 'purge' ] )
+ ];
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialSearchResultsPrepend.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialSearchResultsPrepend.php
new file mode 100644
index 00000000..c5831cd6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialSearchResultsPrepend.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use Html;
+use OutputPage;
+use SMW\MediaWiki\Search\Search as SMWSearch;
+use SMW\Message;
+use SMW\Utils\HtmlModal;
+use SpecialSearch;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchResultsPrepend
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SpecialSearchResultsPrepend extends HookHandler {
+
+ /**
+ * @var SpecialSearch
+ */
+ private $specialSearch;
+
+ /**
+ * @var OutputPage
+ */
+ private $outputPage;
+
+ /**
+ * @since 3.0
+ *
+ * @param SpecialSearch $specialSearch
+ * @param OutputPage &$outputPage
+ */
+ public function __construct( SpecialSearch $specialSearch, OutputPage $outputPage ) {
+ $this->specialSearch = $specialSearch;
+ $this->outputPage = $outputPage;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $term
+ *
+ * @return boolean
+ */
+ public function process( $term ) {
+
+ $html = '';
+
+ if ( $this->specialSearch->getSearchEngine() instanceof SMWSearch ) {
+ $this->outputPage->addModuleStyles( [ 'smw.ui.styles', 'smw.special.search.styles' ] );
+ $this->outputPage->addModules( [ 'smw.special.search', 'smw.ui' ] );
+
+ $this->outputPage->addModuleStyles( HtmlModal::getModuleStyles() );
+ $this->outputPage->addModules( HtmlModal::getModules() );
+
+ $html .= HtmlModal::link(
+ '<span class="smw-icon-info" style="margin-left: -5px; padding: 10px 12px 12px 12px;"></span>',
+ [
+ 'data-id' => 'smw-search-cheat-sheet'
+ ]
+ );
+
+ $html .= Message::get(
+ 'smw-search-syntax-support',
+ Message::PARSE,
+ Message::USER_LANGUAGE
+ );
+
+ if ( $this->getOption( 'prefs-suggester-textinput' ) ) {
+ $html .= ' ' . Message::get(
+ 'smw-search-input-assistance',
+ Message::PARSE,
+ Message::USER_LANGUAGE
+ );
+ }
+
+ $html .= HtmlModal::modal(
+ Message::get( 'smw-cheat-sheet', Message::TEXT, Message::USER_LANGUAGE ),
+ $this->search_sheet( $this->getOption( 'prefs-suggester-textinput' ) ),
+ [
+ 'id' => 'smw-search-cheat-sheet',
+ 'class' => 'plainlinks',
+ 'style' => 'display:none;'
+ ]
+ );
+ }
+
+ if ( $html !== '' && !$this->getOption( 'prefs-disable-search-info' ) ) {
+ $this->outputPage->addHtml(
+ "<div class='smw-search-results-prepend plainlinks'>$html</div>"
+ );
+ }
+
+ return true;
+ }
+
+ private function search_sheet( $inputAssistance ) {
+
+ $text = $this->msg( 'smw-search-help-intro' );
+ $text .= $this->section( 'smw-search-input' );
+
+ $text .= $this->msg( 'smw-search-help-structured' );
+ $text .= $this->msg( 'smw-search-help-proximity' );
+
+ if ( $inputAssistance ) {
+ $text .= $this->section( 'smw-ask-input-assistance' );
+ $text .= $this->msg( 'smw-search-help-input-assistance' );
+ }
+
+ $text .= $this->section( 'smw-search-syntax' );
+ $text .= $this->msg( 'smw-search-help-ask' );
+
+ return $text;
+ }
+
+ private function section( $msg, $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-text-strike',
+ 'style' => 'padding: 5px 0 5px 0;'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'style' => 'font-size: 1.2em; margin-left:0px'
+ ],
+ Message::get( $msg, Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+ }
+
+ private function msg( $msg, $html = '', $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => $msg
+ ] + $attributes,
+ Message::get( $msg, Message::PARSE, Message::USER_LANGUAGE ) . $html
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialStatsAddExtra.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialStatsAddExtra.php
new file mode 100644
index 00000000..42389e28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/SpecialStatsAddExtra.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\DataTypeRegistry;
+use SMW\Store;
+
+/**
+ * Add extra statistic at the end of Special:Statistics
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SpecialStatsAddExtra
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SpecialStatsAddExtra extends HookHandler {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var string[]
+ */
+ private $messageMapper = [
+ 'PROPUSES' => 'smw-statistics-property-instance',
+ 'ERRORUSES' => 'smw-statistics-error-count',
+ 'TOTALPROPS' => 'smw-statistics-property-total',
+ 'USEDPROPS' => 'smw-statistics-property-used',
+ 'OWNPAGE' => 'smw-statistics-property-page',
+ 'DECLPROPS' => 'smw-statistics-property-type',
+ 'DELETECOUNT' => 'smw-statistics-delete-count',
+ 'SUBOBJECTS' => 'smw-statistics-subobject-count',
+ 'QUERY' => 'smw-statistics-query-inline',
+ 'CONCEPTS' => 'smw-statistics-concept-count',
+ ];
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array &$extraStats
+ *
+ * @return true
+ */
+ public function process( array &$extraStats ) {
+
+ if ( !$this->getOption( 'smwgSemanticsEnabled', false ) ) {
+ return true;
+ }
+
+ $this->copyStatistics( $extraStats );
+
+ return true;
+ }
+
+ private function copyStatistics( &$extraStats ) {
+
+ $statistics = $this->store->getStatistics();
+
+ $extraStats['smw-statistics'] = [];
+
+ foreach ( $this->messageMapper as $key => $message ) {
+ if ( isset( $statistics[$key] ) ) {
+ $extraStats['smw-statistics'][$message] = $statistics[$key];
+ }
+ }
+
+ $count = count(
+ DataTypeRegistry::getInstance()->getKnownTypeLabels()
+ );
+
+ $extraStats['smw-statistics']['smw-statistics-datatype-count'] = $count;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsAlwaysKnown.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsAlwaysKnown.php
new file mode 100644
index 00000000..31a16dfc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsAlwaysKnown.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+use Title;
+
+/**
+ * Allows overriding default behaviour for determining if a page exists
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleIsAlwaysKnown
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TitleIsAlwaysKnown {
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var mixed
+ */
+ private $result;
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ * @param mixed &$result
+ */
+ public function __construct( Title $title, &$result ) {
+ $this->title = $title;
+ $this->result =& $result;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return boolean
+ */
+ public function process() {
+
+ // Two possible ways of going forward:
+ //
+ // The FIRST seen here is to use the hook to override the known status
+ // for predefined properties in order to avoid any edit link
+ // which makes no-sense for predefined properties
+ //
+ // The SECOND approach is to inject SMWWikiPageValue with a setLinkOptions setter
+ // that enables to set the custom options 'known' for each invoked linker during
+ // getShortHTMLText
+ // $linker->link( $this->getTitle(), $caption, $customAttributes, $customQuery, $customOptions )
+ //
+ // @see also HooksTest::testOnTitleIsAlwaysKnown
+
+ if ( $this->title->getNamespace() === SMW_NS_PROPERTY ) {
+ if ( !DIProperty::newFromUserLabel( $this->title->getText() )->isUserDefined() ) {
+ $this->result = true;
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsMovable.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsMovable.php
new file mode 100644
index 00000000..3a3670d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleIsMovable.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+use Title;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleIsMovable
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TitleIsMovable extends HookHandler {
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ */
+ public function __construct( Title $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param boolean &$isMovable
+ *
+ * @return boolean
+ */
+ public function process( &$isMovable ) {
+
+ // We don't allow rule pages to be moved as we cannot track JSON content
+ // as redirects and therefore invalidate any rule assignment without a
+ // possibility to automatically reassign IDs
+ if ( $this->title->getNamespace() === SMW_NS_SCHEMA ) {
+ $isMovable = false;
+ }
+
+ if ( $this->title->getNamespace() !== SMW_NS_PROPERTY ) {
+ return true;
+ }
+
+ // Predefined properties cannot be moved!
+ if ( !DIProperty::newFromUserLabel( $this->title->getText() )->isUserDefined() ) {
+ $isMovable = false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleMoveComplete.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleMoveComplete.php
new file mode 100644
index 00000000..d3858062
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/TitleMoveComplete.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\EventHandler;
+use SMW\Factbox\FactboxCache;
+
+/**
+ * TitleMoveComplete occurs whenever a request to move an article
+ * is completed
+ *
+ * This method will be called whenever an article is moved so that
+ * semantic properties are moved accordingly.
+ *
+ * @see http://www.mediawiki.org/wiki/Manual:Hooks/TitleMoveComplete
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class TitleMoveComplete {
+
+ /**
+ * @var Title
+ */
+ protected $oldTitle = null;
+
+ /**
+ * @var Title
+ */
+ protected $newTitle = null;
+
+ /**
+ * @var User
+ */
+ protected $user = null;
+
+ /**
+ * @var integer
+ */
+ protected $oldId;
+
+ /**
+ * @var integer
+ */
+ protected $newId;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $oldTitle old title
+ * @param Title $newTitle: new title
+ * @param Use $user user who did the move
+ * @param $oldId database ID of the page that's been moved
+ * @param $newId database ID of the created redirect
+ */
+ public function __construct( &$oldTitle, &$newTitle, &$user, $oldId, $newId ) {
+ $this->oldTitle = $oldTitle;
+ $this->newTitle = $newTitle;
+ $this->user = $user;
+ $this->oldId = $oldId;
+ $this->newId = $newId;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return true
+ */
+ public function process() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ // Delete all data for a non-enabled target NS
+ if ( !$applicationFactory->getNamespaceExaminer()->isSemanticEnabled( $this->newTitle->getNamespace() ) || $this->newId == 0 ) {
+
+ $applicationFactory->getStore()->deleteSubject(
+ $this->oldTitle
+ );
+
+ } else {
+
+ // Using a different approach since the hook is not triggered
+ // by #REDIRECT which can cause inconsistencies
+ // @see 2.3 / StoreUpdater
+
+ // $applicationFactory->getStore()->changeTitle(
+ // $this->oldTitle,
+ // $this->newTitle,
+ // $this->oldId,
+ // $this->newId
+ // );
+ }
+
+ $eventHandler = EventHandler::getInstance();
+
+ $dispatchContext = $eventHandler->newDispatchContext();
+ $dispatchContext->set( 'title', $this->oldTitle );
+ $dispatchContext->set( 'context', 'ArticleMove' );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+
+ $dispatchContext = $eventHandler->newDispatchContext();
+ $dispatchContext->set( 'title', $this->newTitle );
+ $dispatchContext->set( 'context', 'ArticleMove' );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/UserChange.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/UserChange.php
new file mode 100644
index 00000000..b86a2a80
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/UserChange.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\NamespaceExaminer;
+use Title;
+use User;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/BlockIpComplete
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/UnblockUserComplete
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/UserGroupsChanged
+ *
+ * Act on events that happen outside of the normal parser process to ensure that
+ * changes to pre-defined properties related to a user status can be invoked.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UserChange extends HookHandler {
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param NamespaceExaminer $namespaceExaminer
+ */
+ public function __construct( NamespaceExaminer $namespaceExaminer ) {
+ $this->namespaceExaminer = $namespaceExaminer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param User|string $user
+ */
+ public function process( $user ) {
+
+ if ( !$this->namespaceExaminer->isSemanticEnabled( NS_USER ) ) {
+ return false;
+ }
+
+ if ( $user instanceof User ) {
+ $user = $user->getName();
+ }
+
+ $updateJob = ApplicationFactory::getInstance()->newJobFactory()->newUpdateJob(
+ Title::newFromText( $user, NS_USER ),
+ [
+ UpdateJob::FORCED_UPDATE => true,
+ 'origin' => $this->origin
+ ]
+ );
+
+ $updateJob->insert();
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Job.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Job.php
new file mode 100644
index 00000000..cb289e86
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Job.php
@@ -0,0 +1,259 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Job as MediaWikiJob;
+use JobQueueGroup;
+use SMW\ApplicationFactory;
+use SMW\Site;
+use SMW\Store;
+use Title;
+
+/**
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+abstract class Job extends MediaWikiJob {
+
+ /**
+ * @var boolean
+ */
+ protected $isEnabledJobQueue = true;
+
+ /**
+ * @var JobQueue
+ */
+ protected $jobQueue;
+
+ /**
+ * @var Job
+ */
+ protected $jobs = [];
+
+ /**
+ * @var Store
+ */
+ protected $store = null;
+
+ /**
+ * @since 2.1
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * Whether to insert jobs into the JobQueue is enabled or not
+ *
+ * @since 1.9
+ *
+ * @param boolean|true $enableJobQueue
+ *
+ * @return AbstractJob
+ */
+ public function isEnabledJobQueue( $enableJobQueue = true ) {
+ $this->isEnabledJobQueue = (bool)$enableJobQueue;
+ return $this;
+ }
+
+ /**
+ * @note Job::batchInsert was deprecated in MW 1.21
+ * JobQueueGroup::singleton()->push( $job );
+ *
+ * @since 1.9
+ */
+ public function pushToJobQueue() {
+ $this->isEnabledJobQueue ? self::batchInsert( $this->jobs ) : null;
+ }
+
+ /**
+ * @note Job::getType was introduced with MW 1.21
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->command;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return integer
+ */
+ public function getJobCount() {
+ return count( $this->jobs );
+ }
+
+ /**
+ * @note Job::getTitle() in MW 1.19 does not exist
+ *
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param mixed $key
+ *
+ * @return boolean
+ */
+
+ public function hasParameter( $key ) {
+
+ if ( !is_array( $this->params ) ) {
+ return false;
+ }
+
+ return isset( $this->params[$key] ) || array_key_exists( $key, $this->params );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param mixed $key
+ *
+ * @return boolean
+ */
+ public function getParameter( $key, $default = false ) {
+ return $this->hasParameter( $key ) ? $this->params[$key] : $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ public function setParameter( $key, $value ) {
+ $this->params[$key] = $value;
+ }
+
+ /**
+ * @see https://gerrit.wikimedia.org/r/#/c/162009
+ *
+ * @param self[] $jobs
+ *
+ * @return boolean
+ */
+ public static function batchInsert( $jobs ) {
+ return ApplicationFactory::getInstance()->getJobQueue()->push( $jobs );
+ }
+
+ /**
+ * @see Job::insert
+ */
+ public function insert() {
+ if ( $this->isEnabledJobQueue ) {
+ return self::batchInsert( [ $this ] );
+ }
+ }
+
+ /**
+ * @see JobQueueGroup::lazyPush
+ *
+ * @note Registered jobs are pushed using JobQueueGroup::pushLazyJobs at the
+ * end of MediaWiki::restInPeace
+ *
+ * @since 3.0
+ */
+ public function lazyPush() {
+ if ( $this->isEnabledJobQueue ) {
+ return $this->getJobQueue()->lazyPush( $this );
+ }
+ }
+
+ /**
+ * @see Translate::TTMServerMessageUpdateJob
+ * @since 3.0
+ *
+ * @param integer $delay
+ */
+ public function setDelay( $delay ) {
+
+ $isDelayedJobsEnabled = $this->getJobQueue()->isDelayedJobsEnabled(
+ $this->getType()
+ );
+
+ if ( !$delay || !$isDelayedJobsEnabled ) {
+ return;
+ }
+
+ $oldTime = $this->getReleaseTimestamp();
+ $newTime = time() + $delay;
+
+ if ( $oldTime !== null && $oldTime >= $newTime ) {
+ return;
+ }
+
+ $this->params[ 'jobReleaseTimestamp' ] = $newTime;
+ }
+
+ /**
+ * @see Job::newRootJobParams
+ * @since 3.0
+ */
+ public static function newRootJobParams( $key = '', $title = '' ) {
+
+ if ( $title instanceof Title ) {
+ $title = $title->getPrefixedDBkey();
+ }
+
+ return parent::newRootJobParams( "job:{$key}:root:{$title}" );
+ }
+
+ /**
+ * @see Job::ignoreDuplicates
+ * @since 3.0
+ */
+ public function ignoreDuplicates() {
+
+ if ( isset( $this->params['waitOnCommandLine'] ) ) {
+ return $this->params['waitOnCommandLine'] > 1;
+ }
+
+ return $this->removeDuplicates;
+ }
+
+ /**
+ * Only run the job via commandLine or the cronJob and avoid execution via
+ * Special:RunJobs as it can cause the script to timeout.
+ */
+ public function waitOnCommandLineMode() {
+
+ if ( !$this->hasParameter( 'waitOnCommandLine' ) || Site::isCommandLineMode() ) {
+ return false;
+ }
+
+ if ( $this->hasParameter( 'waitOnCommandLine' ) ) {
+ $this->params['waitOnCommandLine'] = $this->getParameter( 'waitOnCommandLine' ) + 1;
+ } else {
+ $this->params['waitOnCommandLine'] = 1;
+ }
+
+ $job = new static( $this->title, $this->params );
+ $job->insert();
+
+ return true;
+ }
+
+ protected function getJobQueue() {
+
+ if ( $this->jobQueue === null ) {
+ $this->jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+ }
+
+ return $this->jobQueue;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobFactory.php
new file mode 100644
index 00000000..6fab53e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobFactory.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use SMW\MediaWiki\Jobs\NullJob;
+use SMW\MediaWiki\Jobs\RefreshJob;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\MediaWiki\Jobs\UpdateDispatcherJob;
+use SMW\MediaWiki\Jobs\ParserCachePurgeJob;
+use SMW\MediaWiki\Jobs\EntityIdDisposerJob;
+use SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob;
+use SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob;
+use SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob;
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+use SMW\MediaWiki\Jobs\ChangePropagationUpdateJob;
+use SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob;
+use RuntimeException;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class JobFactory {
+
+ /**
+ * @since 2.5
+ *
+ * @param array $jobs
+ */
+ public static function batchInsert( array $jobs ) {
+ Job::batchInsert( $jobs );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ * @param Title|null $title
+ * @param array $parameters
+ *
+ * @return Job
+ * @throws RuntimeException
+ */
+ public function newByType( $type, Title $title = null, array $parameters = [] ) {
+
+ if ( $title === null ) {
+ return new NullJob( null );
+ }
+
+ switch ( $type ) {
+ case 'SMW\RefreshJob':
+ case 'smw.refresh':
+ return $this->newRefreshJob( $title, $parameters );
+ case 'SMW\UpdateJob':
+ case 'smw.update':
+ return $this->newUpdateJob( $title, $parameters );
+ case 'SMW\UpdateDispatcherJob':
+ case 'smw.updateDispatcher':
+ return $this->newUpdateDispatcherJob( $title, $parameters );
+ case 'SMW\ParserCachePurgeJob':
+ case 'smw.parserCachePurge':
+ return $this->newParserCachePurgeJob( $title, $parameters );
+ case 'SMW\EntityIdDisposerJob':
+ case 'smw.entityIdDisposer':
+ return $this->newEntityIdDisposerJob( $title, $parameters );
+ case 'SMW\PropertyStatisticsRebuildJob':
+ case 'smw.propertyStatisticsRebuild':
+ return $this->newPropertyStatisticsRebuildJob( $title, $parameters );
+ case 'SMW\FulltextSearchTableUpdateJob':
+ case 'smw.fulltextSearchTableUpdate':
+ return $this->newFulltextSearchTableUpdateJob( $title, $parameters );
+ case 'SMW\FulltextSearchTableRebuildJob':
+ case 'smw.fulltextSearchTableRebuild':
+ return $this->newFulltextSearchTableRebuildJob( $title, $parameters );
+ case 'SMW\ChangePropagationDispatchJob':
+ case 'smw.changePropagationDispatch':
+ return $this->newChangePropagationDispatchJob( $title, $parameters );
+ case 'SMW\ChangePropagationUpdateJob':
+ case 'smw.changePropagationUpdate':
+ return $this->newChangePropagationUpdateJob( $title, $parameters );
+ case 'SMW\ChangePropagationClassUpdateJob':
+ case 'smw.changePropagationClassUpdate':
+ return $this->newChangePropagationClassUpdateJob( $title, $parameters );
+ }
+
+ throw new RuntimeException( "Unable to match $type to a valid Job type" );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return RefreshJob
+ */
+ public function newRefreshJob( Title $title, array $parameters = [] ) {
+ return new RefreshJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return UpdateJob
+ */
+ public function newUpdateJob( Title $title, array $parameters = [] ) {
+ return new UpdateJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return UpdateDispatcherJob
+ */
+ public function newUpdateDispatcherJob( Title $title, array $parameters = [] ) {
+ return new UpdateDispatcherJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return ParserCachePurgeJob
+ */
+ public function newParserCachePurgeJob( Title $title, array $parameters = [] ) {
+ return new ParserCachePurgeJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return FulltextSearchTableUpdateJob
+ */
+ public function newFulltextSearchTableUpdateJob( Title $title, array $parameters = [] ) {
+ return new FulltextSearchTableUpdateJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return EntityIdDisposerJob
+ */
+ public function newEntityIdDisposerJob( Title $title, array $parameters = [] ) {
+ return new EntityIdDisposerJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return PropertyStatisticsRebuildJob
+ */
+ public function newPropertyStatisticsRebuildJob( Title $title, array $parameters = [] ) {
+ return new PropertyStatisticsRebuildJob( $title, $parameters );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return FulltextSearchTableRebuildJob
+ */
+ public function newFulltextSearchTableRebuildJob( Title $title, array $parameters = [] ) {
+ return new FulltextSearchTableRebuildJob( $title, $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return ChangePropagationDispatchJob
+ */
+ public function newChangePropagationDispatchJob( Title $title, array $parameters = [] ) {
+ return new ChangePropagationDispatchJob( $title, $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return ChangePropagationUpdateJob
+ */
+ public function newChangePropagationUpdateJob( Title $title, array $parameters = [] ) {
+ return new ChangePropagationUpdateJob( $title, $parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return ChangePropagationClassUpdateJob
+ */
+ public function newChangePropagationClassUpdateJob( Title $title, array $parameters = [] ) {
+ return new ChangePropagationClassUpdateJob( $title, $parameters );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobQueue.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobQueue.php
new file mode 100644
index 00000000..2e580459
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/JobQueue.php
@@ -0,0 +1,213 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use JobQueueGroup;
+
+/**
+ * MediaWiki's JobQueue contains mostly final methods making it difficult to use
+ * an instance during tests hence this class provides a reduced interface with
+ * mockable methods.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class JobQueue {
+
+ /**
+ * @var JobQueueGroup
+ */
+ private $jobQueueGroup;
+
+ /**
+ * @var boolean
+ */
+ private $disableCache = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param JobQueueGroup $jobQueueGroup
+ */
+ public function __construct( JobQueueGroup $jobQueueGroup ) {
+ $this->jobQueueGroup = $jobQueueGroup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $disableCache
+ */
+ public function disableCache( $disableCache = true ) {
+ $this->disableCache = (bool)$disableCache;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function isDelayedJobsEnabled( $type ) {
+ return $this->jobQueueGroup->get( $this->mapLegacyType( $type ) )->delayedJobsEnabled();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $list
+ *
+ * @return []
+ */
+ public function runFromQueue( array $list ) {
+
+ $log = [];
+
+ foreach ( $list as $type => $amount ) {
+
+ if ( $amount == 0 || $amount === false ) {
+ continue;
+ }
+
+ $jobs = array_fill( 0, $amount, $type );
+ $log[$type] = [];
+
+ foreach ( $jobs as $job ) {
+ $j = $this->pop( $job );
+
+ if ( $j === false ) {
+ break;
+ }
+
+ $log[$type][] = $j->getTitle()->getPrefixedDBKey();
+
+ $j->run();
+ $this->ack( $j );
+ }
+ }
+
+ return $log;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return Job|boolean
+ */
+ public function pop( $type ) {
+ return $this->jobQueueGroup->get( $this->mapLegacyType( $type ) )->pop();
+ }
+
+ /**
+ * Acknowledge that a job was completed
+ *
+ * @since 3.0
+ *
+ * @param Job $job
+ */
+ public function ack( \Job $job ) {
+ $this->jobQueueGroup->get( $job->getType() )->ack( $job );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function delete( $type ) {
+
+ $jobQueue = $this->jobQueueGroup->get( $this->mapLegacyType( $type ) );
+ $jobQueue->delete();
+
+ if ( $this->disableCache ) {
+ $jobQueue->flushCaches();
+ $this->disableCache = false;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Job|Job[] $jobs
+ */
+ public function push( $jobs ) {
+ $this->jobQueueGroup->push( $jobs );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Job|Job[] $jobs
+ */
+ public function lazyPush( $jobs ) {
+
+ if ( !method_exists( $this->jobQueueGroup, 'lazyPush' ) ) {
+ return $this->push( $jobs );
+ }
+
+ $this->jobQueueGroup->lazyPush( $jobs );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getQueueSizes() {
+ return $this->jobQueueGroup->getQueueSizes();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return integer
+ */
+ public function getQueueSize( $type ) {
+
+ $jobQueue = $this->jobQueueGroup->get( $this->mapLegacyType( $type ) );
+
+ if ( $this->disableCache ) {
+ $jobQueue->flushCaches();
+ $this->disableCache = false;
+ }
+
+ return $jobQueue->getSize();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function hasPendingJob( $type ) {
+ return $this->getQueueSize( $type ) > 0;
+ }
+
+ /**
+ * @note FIXME Remove with 3.1
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function mapLegacyType( $type ) {
+
+ // Legacy names
+ if ( strpos( $type, 'SMW\\' ) !== false ) {
+ $type = 'smw.' . lcfirst( str_replace( [ 'SMW\\', 'Job' ], '', $type ) );
+ }
+
+ return $type;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationClassUpdateJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationClassUpdateJob.php
new file mode 100644
index 00000000..70e15fdc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationClassUpdateJob.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use Title;
+
+/**
+ * Isolate instance to count update jobs in connection with a category related
+ * update.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationClassUpdateJob extends ChangePropagationUpdateJob {
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+
+ $params = $params + [
+ 'origin' => 'ChangePropagationClassUpdateJob'
+ ];
+
+ parent::__construct( $title, $params, 'smw.changePropagationClassUpdate' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationDispatchJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationDispatchJob.php
new file mode 100644
index 00000000..07b42ff8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationDispatchJob.php
@@ -0,0 +1,424 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\ChangePropagationEntityFinder;
+use SMWExporter as Exporter;
+use Title;
+
+/**
+ * `ChangePropagationDispatchJob` dispatches update jobs via `ChangePropagationUpdateJob`
+ * to allow isolating the execution and count pending jobs without using an extra
+ * tracking mechanism during an update process.
+ *
+ * `ChangePropagationUpdateJob` (and hereby ChangePropagationClassUpdateJob) itself
+ * relies on the `UpdateJob` to initiate the update.
+ *
+ * `ChangePropagationDispatchJob` is responsible for:
+ *
+ * - Select entities that are being connected to a property specification
+ * change
+ * - Once the selection process has been finalized, update the property with the
+ * new specification (which has been locked before this update)
+ *
+ * Due to the possibility that a large list of entities can be connected to a
+ * property and its change, an iterative or recursive processing is not viable
+ * (as the changed specification should be available as soon as possible) therefore
+ * the selection process will move the result of entities to chunked temp files
+ * to avoid having to use a DB connection during the process (has been observed
+ * during tests that would lead to an out-of-memory) to store a list of
+ * entities that require an update.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationDispatchJob extends Job {
+
+ /**
+ * Size of rows stored in a temp file
+ */
+ const CHUNK_SIZE = 1000;
+
+ /**
+ * Temp marker namespace
+ */
+ const CACHE_NAMESPACE = 'smw:chgprop';
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.changePropagationDispatch', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * Called from PropertyChangePropagationNotifier
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ * @param array $params
+ *
+ * @return boolean
+ */
+ public static function planAsJob( DIWikiPage $subject, $params = [] ) {
+
+ Exporter::getInstance()->resetCacheBy( $subject );
+ ApplicationFactory::getInstance()->getPropertySpecificationLookup()->resetCacheBy(
+ $subject
+ );
+
+ $changePropagationDispatchJob = new self( $subject->getTitle(), $params );
+ $changePropagationDispatchJob->lazyPush();
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ */
+ public static function cleanUp( DIWikiPage $subject ) {
+
+ $namespace = $subject->getNamespace();
+
+ if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) {
+ return;
+ }
+
+ ApplicationFactory::getInstance()->getCache()->delete(
+ smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $subject->getHash()
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return boolean
+ */
+ public static function hasPendingJobs( DIWikiPage $subject ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $jobType = 'smw.changePropagationUpdate';
+
+ if ( $subject->getNamespace() === NS_CATEGORY ) {
+ $jobType = 'smw.changePropagationClassUpdate';
+ }
+
+ if ( $applicationFactory->getJobQueue()->hasPendingJob( $jobType ) ) {
+ return true;
+ }
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $subject->getHash()
+ );
+
+ return $applicationFactory->getCache()->fetch( $key ) > 0;
+ }
+
+ /**
+ * Use as very simple heuristic to count pending jobs for the overall change
+ * propagation. The count will indicate any job related to the change propagation
+ * and does not distinguish by changes to a specific property.
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return integer
+ */
+ public static function getPendingJobsCount( DIWikiPage $subject ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $jobType = 'smw.changePropagationUpdate';
+
+ if ( $subject->getNamespace() === NS_CATEGORY ) {
+ $jobType = 'smw.changePropagationClassUpdate';
+ }
+
+ $count = $applicationFactory->getJobQueue()->getQueueSize( $jobType );
+
+ // Fallback for when JobQueue::getQueueSize doesn't yet contain the
+ // updated stats
+ if ( $count == 0 && self::hasPendingJobs( $subject ) ) {
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $subject->getHash()
+ );
+
+ $count = $applicationFactory->getCache()->fetch( $key );
+ }
+
+ return $count;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 3.0
+ */
+ public function run() {
+
+ $subject = DIWikiPage::newFromTitle( $this->getTitle() );
+
+ if ( $this->hasParameter( 'dataFile' ) ) {
+ return $this->dispatchFromFile( $subject, $this->getParameter( 'dataFile' ) );
+ }
+
+ $this->findAndDispatch();
+
+ return true;
+ }
+
+ private function findAndDispatch() {
+
+ $namespace = $this->getTitle()->getNamespace();
+
+ if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) {
+ return;
+ }
+
+ $subject = DIWikiPage::newFromTitle( $this->getTitle() );
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $iteratorFactory = $applicationFactory->getIteratorFactory();
+
+ $applicationFactory->getMediaWikiLogger()->info(
+ 'ChangePropagationDispatchJob on ' . $subject->getHash()
+ );
+
+ $changePropagationEntityFinder = new ChangePropagationEntityFinder(
+ $applicationFactory->getStore(),
+ $iteratorFactory
+ );
+
+ $changePropagationEntityFinder->isTypePropagation(
+ $this->getParameter( 'isTypePropagation' )
+ );
+
+ if ( $namespace === SMW_NS_PROPERTY ) {
+ $entity = DIProperty::newFromUserLabel( $this->getTitle()->getText() );
+ } elseif ( $namespace === NS_CATEGORY ) {
+ $entity = $subject;
+ }
+
+ $appendIterator = $changePropagationEntityFinder->findAll(
+ $entity
+ );
+
+ // Refresh the property page once more on the last dispatch
+ $appendIterator->add(
+ [ $subject ]
+ );
+
+ // After relevant subjects has been selected, commit the changes to the
+ // property so that the lock can be removed and any new specification
+ // (type, allows values etc.) are available upon executing individual
+ // jobs.
+ $this->commitSpecificationChangePropagationAsJob(
+ $subject,
+ $appendIterator->count()
+ );
+
+ $chunkedIterator = $iteratorFactory->newChunkedIterator(
+ $appendIterator,
+ self::CHUNK_SIZE
+ );
+
+ $i = 0;
+ $tempFile = $applicationFactory->create( 'TempFile' );
+
+ $file = $tempFile->generate(
+ 'smw_chgprop_',
+ $subject->getHash(),
+ uniqid()
+ );
+
+ foreach ( $chunkedIterator as $chunk ) {
+ $this->pushChangePropagationDispatchJob( $tempFile, $file, $i++, $chunk );
+ }
+ }
+
+ private function pushChangePropagationDispatchJob( $tempFile, $file, $num, $chunk ) {
+
+ $data = [];
+ $file .= "_$num.tmp";
+
+ // Filter any subobject
+ foreach ( $chunk as $val ) {
+ $data[] = ( $val instanceof DIWikiPage ? $val->asBase()->getHash() : $val );
+ }
+
+ // Filter duplicates and write the temp file
+ $tempFile->write(
+ $file,
+ implode( "\n", array_keys( array_flip( $data ) ) )
+ );
+
+ $checkSum = $tempFile->getCheckSum( $file );
+
+ // Use the checkSum as verification method to avoid manipulation of the
+ // contents by third-parties
+ $changePropagationDispatchJob = new ChangePropagationDispatchJob(
+ $this->getTitle(),
+ [
+ 'dataFile' => $file,
+ 'checkSum' => $checkSum
+ ] + self::newRootJobParams(
+ "ChangePropagationDispatchJob:$file:$checkSum"
+ )
+ );
+
+ $changePropagationDispatchJob->lazyPush();
+ }
+
+ private function dispatchFromFile( $subject, $file ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $cache = $applicationFactory->getCache();
+
+ $property = DIProperty::newFromUserLabel(
+ $this->getTitle()->getText()
+ );
+
+ $semanticData = $applicationFactory->getStore()->getSemanticData(
+ $subject
+ );
+
+ $tempFile = $applicationFactory->create( 'TempFile' );
+ $key = smwfCacheKey( self::CACHE_NAMESPACE, $subject->getHash() );
+
+ // SemanticData hasn't been updated, re-enter the cycle to ensure that
+ // the update of the property took place
+ if ( $cache->fetch( $key ) === false ) {
+
+ $cache->save( $key, 1, 60 * 60 * 24 );
+ $params = $this->params;
+
+ $changePropagationDispatchJob = new ChangePropagationDispatchJob(
+ $this->getTitle(),
+ $params
+ );
+
+ $changePropagationDispatchJob->insert();
+
+ $applicationFactory->getMediaWikiLogger()->info(
+ 'ChangePropagationDispatchJob missing update marker, retry on ' . $subject->getHash()
+ );
+
+ return true;
+ }
+
+ $contents = $tempFile->read(
+ $file,
+ $this->getParameter( 'checkSum' )
+ );
+
+ // @see ChangePropagationDispatchJob::pushChangePropagationDispatchJob
+ $dataItems = explode( "\n", $contents );
+
+ $this->scheduleChangePropagationUpdateJobFromList(
+ $dataItems
+ );
+
+ $tempFile->delete( $file );
+
+ return true;
+ }
+
+ private function scheduleChangePropagationUpdateJobFromList( $dataItems ) {
+
+ foreach ( $dataItems as $dataItem ) {
+
+ if ( $dataItem === '' ) {
+ continue;
+ }
+
+ $title = DIWikiPage::doUnserialize( $dataItem )->getTitle();
+
+ $changePropagationUpdateJob = $this->newChangePropagationUpdateJob(
+ $title,
+ [
+ UpdateJob::FORCED_UPDATE => true
+ ]
+ );
+
+ $changePropagationUpdateJob->insert();
+ }
+ }
+
+ private function commitSpecificationChangePropagationAsJob( $subject, $count ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $connection = $applicationFactory->getStore()->getConnection( 'mw.db' );
+ $transactionTicket = $connection->getEmptyTransactionTicket( __METHOD__ );
+
+ $changePropagationUpdateJob = $this->newChangePropagationUpdateJob(
+ $subject->getTitle(),
+ [
+ UpdateJob::CHANGE_PROP => $subject->getSerialization(),
+ UpdateJob::FORCED_UPDATE => true
+ ]
+ );
+
+ $changePropagationUpdateJob->run();
+
+ // Make sure changes are committed before continuing processing
+ $connection->commitAndWaitForReplication( __METHOD__, $transactionTicket );
+
+ // Add temporary update marker
+ // 24h ttl and it is expected that the JobQueue will run within this time
+ // frame so that the JobQueueGroup::getSize can catch up with the update
+ // marker.
+ //
+ // The marker will be removed after running the ChangePropagationUpdateJob
+ // on the same subject.
+ $applicationFactory->getCache()->save(
+ smwfCacheKey( self::CACHE_NAMESPACE, $subject->getHash() ),
+ $count,
+ 60 * 60 * 24
+ );
+
+ $applicationFactory->getPropertySpecificationLookup()->resetCacheBy( $subject );
+
+ // Make sure the cache is reset in case runJobs.php --wait is used to avoid
+ // reusing outdated type assignments
+ $applicationFactory->getStore()->clear();
+ }
+
+ private function newChangePropagationUpdateJob( $title, $parameters ) {
+
+ $namespace = $this->getTitle()->getNamespace();
+ $parameters = $parameters + [ 'origin' => 'ChangePropagationDispatchJob' ];
+
+ if ( $namespace === NS_CATEGORY ) {
+ return new ChangePropagationClassUpdateJob( $title, $parameters );
+ }
+
+ return new ChangePropagationUpdateJob(
+ $title,
+ $parameters
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationUpdateJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationUpdateJob.php
new file mode 100644
index 00000000..33734f10
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ChangePropagationUpdateJob.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use Title;
+use SMW\DIWikiPage;
+
+/**
+ * Make sufficient use of the job table by only tracking remaining jobs without
+ * any detail on an individual update count.
+ *
+ * Use `ChangePropagationUpdateJob` to easily count the jobs and distinguish them
+ * from other `UpdateJob`.
+ *
+ * `JobQueueGroup::singleton()->get( 'SMW\ChangePropagationUpdateJob' )->getSize()`
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationUpdateJob extends Job {
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [], $jobType = null ) {
+
+ if ( $jobType === null ) {
+ $jobType = 'smw.changePropagationUpdate';
+ }
+
+ parent::__construct( $jobType, $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 3.0
+ */
+ public function run() {
+
+ ChangePropagationDispatchJob::cleanUp(
+ DIWikiPage::newFromTitle( $this->getTitle() )
+ );
+
+ $updateJob = new UpdateJob(
+ $this->getTitle(),
+ $this->params + [ 'origin' => 'ChangePropagationUpdateJob' ]
+ );
+
+ $updateJob->run();
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/EntityIdDisposerJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/EntityIdDisposerJob.php
new file mode 100644
index 00000000..ba83499a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/EntityIdDisposerJob.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use Hooks;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\PropertyTableIdReferenceDisposer;
+use SMW\SQLStore\SQLStore;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EntityIdDisposerJob extends Job {
+
+ /**
+ * Commit chunk size
+ */
+ const CHUNK_SIZE = 200;
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.entityIdDisposer', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ResultIterator
+ */
+ public function newOutdatedEntitiesResultIterator() {
+ return $this->newPropertyTableIdReferenceDisposer()->newOutdatedEntitiesResultIterator();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|stdClass $id
+ */
+ public function dispose( $id ) {
+
+ $propertyTableIdReferenceDisposer = $this->newPropertyTableIdReferenceDisposer();
+
+ if ( is_int( $id ) ) {
+ return $propertyTableIdReferenceDisposer->cleanUpTableEntriesById( $id );
+ }
+
+ $propertyTableIdReferenceDisposer->cleanUpTableEntriesByRow( $id );
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.5
+ */
+ public function run() {
+
+ $propertyTableIdReferenceDisposer = $this->newPropertyTableIdReferenceDisposer();
+
+ // MW 1.29+ Avoid transaction collisions during Job execution
+ $propertyTableIdReferenceDisposer->waitOnTransactionIdle();
+
+ if ( $this->hasParameter( 'id' ) ) {
+ $this->dispose( $this->getParameter( 'id' ) );
+ } else {
+ $this->dispose_all( $this->newOutdatedEntitiesResultIterator() );
+ }
+
+ return true;
+ }
+
+ private function dispose_all( $outdatedEntitiesResultIterator ) {
+
+ // Make sure the script is only executed from the command line to avoid
+ // Special:RunJobs to execute a queued job
+ if ( $this->waitOnCommandLineMode() ) {
+ return true;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $connection = $applicationFactory->getStore()->getConnection( 'mw.db' );
+
+ $chunkedIterator = $applicationFactory->getIteratorFactory()->newChunkedIterator(
+ $outdatedEntitiesResultIterator,
+ self::CHUNK_SIZE
+ );
+
+ foreach ( $chunkedIterator as $chunk ) {
+
+ $transactionTicket = $connection->getEmptyTransactionTicket( __METHOD__ );
+
+ foreach ( $chunk as $row ) {
+ $this->dispose( $row );
+ }
+
+ $connection->commitAndWaitForReplication( __METHOD__, $transactionTicket );
+ }
+ }
+
+ private function newPropertyTableIdReferenceDisposer() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $store = $applicationFactory->getStore();
+
+ // Expect access to the SQL table structure therefore enforce the
+ // SQLStore that provides those methods
+ if ( !is_a( $store, SQLStore::class ) ) {
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+ }
+
+ return new PropertyTableIdReferenceDisposer( $store );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableRebuildJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableRebuildJob.php
new file mode 100644
index 00000000..031280f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableRebuildJob.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableRebuildJob extends Job {
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.fulltextSearchTableRebuild', $title, $params );
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.5
+ */
+ public function run() {
+
+ if ( $this->waitOnCommandLineMode() ) {
+ return true;
+ }
+
+ $fulltextSearchTableFactory = new FulltextSearchTableFactory();
+
+ // Only the SQLStore is supported
+ $searchTableRebuilder = $fulltextSearchTableFactory->newSearchTableRebuilder(
+ ApplicationFactory::getInstance()->getStore( '\SMW\SQLStore\SQLStore' )
+ );
+
+ if ( $this->hasParameter( 'table' ) ) {
+ $searchTableRebuilder->rebuildByTable( $this->getParameter( 'table' ) );
+ } elseif ( $this->hasParameter( 'mode' ) && $this->getParameter( 'mode' ) === 'full' ) {
+ $searchTableRebuilder->rebuild();
+ } else {
+ $searchTableRebuilder->flushTable();
+ $this->createJobsFromTableList( $searchTableRebuilder->getQualifiedTableList() );
+ }
+
+ return true;
+ }
+
+ private function createJobsFromTableList( $tableList ) {
+
+ if ( $tableList === [] ) {
+ return;
+ }
+
+ foreach ( $tableList as $tableName ) {
+ $fulltextSearchTableRebuildJob = new self( $this->getTitle(), [
+ 'table' => $tableName
+ ] );
+
+ $fulltextSearchTableRebuildJob->insert();
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableUpdateJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableUpdateJob.php
new file mode 100644
index 00000000..8dfb4dd8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/FulltextSearchTableUpdateJob.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use Hooks;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableUpdateJob extends Job {
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.fulltextSearchTableUpdate', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.5
+ */
+ public function run() {
+
+ $fulltextSearchTableFactory = new FulltextSearchTableFactory();
+
+ $textChangeUpdater = $fulltextSearchTableFactory->newTextChangeUpdater(
+ ApplicationFactory::getInstance()->getStore( '\SMW\SQLStore\SQLStore' )
+ );
+
+ $textChangeUpdater->pushUpdatesFromJobParameters(
+ $this->params
+ );
+
+ Hooks::run( 'SMW::Job::AfterFulltextSearchTableUpdateComplete', [ $this ] );
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/NullJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/NullJob.php
new file mode 100644
index 00000000..15ccf610
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/NullJob.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class NullJob extends Job {
+
+ /**
+ * @since 2.5
+ *
+ * @param Title|null $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title = null, $params = [] ) {}
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.5
+ */
+ public function run() {
+ return true;
+ }
+
+ /**
+ * @see Job::insert
+ *
+ * @since 2.5
+ */
+ public function insert() {}
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ParserCachePurgeJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ParserCachePurgeJob.php
new file mode 100644
index 00000000..333c5b45
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/ParserCachePurgeJob.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use Hooks;
+use SMW\ApplicationFactory;
+use SMW\HashBuilder;
+use SMW\RequestOptions;
+use SMW\SQLStore\QueryDependencyLinksStoreFactory;
+use SMW\Utils\Timer;
+use SMW\DIWikiPage;
+use SMWQuery as Query;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ParserCachePurgeJob extends Job {
+
+ /**
+ * A balanced size that should be carefully monitored in order to not have a
+ * negative impact when running the initial update in online mode.
+ */
+ const CHUNK_SIZE = 300;
+
+ /**
+ * Using DB update execution mode to immediately execute the purge which may
+ * cause a surge in DB inserts.
+ */
+ const EXEC_DB = 'exec.db';
+
+ /**
+ * Using journal update execution mode to pause the execution and temporary
+ * store until an actual page is viewed.
+ */
+ const EXEC_JOURNAL = 'exec.journal';
+
+ /**
+ * @var ApplicationFactory
+ */
+ protected $applicationFactory;
+
+ /**
+ * @var integer
+ */
+ private $limit = self::CHUNK_SIZE;
+
+ /**
+ * @var integer
+ */
+ private $offset = 0;
+
+ /**
+ * @var PageUpdater
+ */
+ protected $pageUpdater;
+
+ /**
+ * @since 2.3
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.parserCachePurge', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ */
+ public function insert() {
+
+ if (
+ $this->hasParameter( 'is.enabled' ) &&
+ $this->getParameter( 'is.enabled' ) === false ) {
+ return;
+ }
+
+ parent::insert();
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.3
+ */
+ public function run() {
+
+ Timer::start( __METHOD__ );
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->pageUpdater = $this->applicationFactory->newPageUpdater();
+
+ $count = 0;
+ $linksCount = 0;
+
+ if ( $this->hasParameter( 'limit' ) ) {
+ $this->limit = $this->getParameter( 'limit' );
+ }
+
+ if ( $this->hasParameter( 'offset' ) ) {
+ $this->offset = $this->getParameter( 'offset' );
+ }
+
+ if ( $this->hasParameter( 'idlist' ) ) {
+ $this->purgeTargetLinksFromList( $this->getParameter( 'idlist' ), $count, $linksCount );
+ }
+
+ if ( $this->getParameter( 'exec.mode' ) !== self::EXEC_JOURNAL ) {
+ $this->pageUpdater->addPage( $this->getTitle() );
+ $this->pageUpdater->setOrigin( __METHOD__ );
+ $this->pageUpdater->doPurgeParserCacheAsPool();
+ }
+
+ Hooks::run( 'SMW::Job::AfterParserCachePurgeComplete', [ $this ] );
+
+ $this->applicationFactory->getMediaWikiLogger()->info(
+ [
+ 'Job',
+ "ParserCachePurgeJob",
+ "List count:{count}",
+ "Links count:{linksCount}",
+ "Limit:{limit}",
+ "Offset:{offset}",
+ "procTime in sec: {procTime}"
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 7 ),
+ 'limit' => $this->limit,
+ 'offset' => $this->offset,
+ 'count' => $count,
+ 'linksCount' => $linksCount
+ ]
+ );
+
+ return true;
+ }
+
+ /**
+ * Based on the CHUNK_SIZE, target links are purged in an instant if those
+ * selected entities are < CHUNK_SIZE which should be enough for most
+ * common queries that only share a limited amount of dependencies, yet for
+ * queries that expect a large subject/dependency pool, doing an online update
+ * for all at once is not feasible hence the iterative process of creating
+ * batches that run through the job scheduler.
+ *
+ * @param array|string $idList
+ */
+ private function purgeTargetLinksFromList( $idList, &$listCount, &$linksCount ) {
+
+ if ( is_string( $idList ) && strpos( $idList, '|' ) !== false ) {
+ $idList = explode( '|', $idList );
+ }
+
+ if ( $idList === [] ) {
+ return true;
+ }
+
+ $queryDependencyLinksStoreFactory = $this->applicationFactory->singleton(
+ 'QueryDependencyLinksStoreFactory'
+ );
+
+ $queryDependencyLinksStore = $queryDependencyLinksStoreFactory->newQueryDependencyLinksStore(
+ $this->applicationFactory->getStore()
+ );
+
+ $dependencyLinksUpdateJournal = $queryDependencyLinksStoreFactory->newDependencyLinksUpdateJournal();
+
+ $requestOptions = new RequestOptions();
+
+ // +1 to look ahead
+ $requestOptions->setLimit( $this->limit + 1 );
+ $requestOptions->setOffset( $this->offset );
+ $requestOptions->setOption( 'links.count', 0 );
+
+ $hashList = $queryDependencyLinksStore->findDependencyTargetLinks(
+ $idList,
+ $requestOptions
+ );
+
+ $linksCount = $requestOptions->getOption( 'links.count' );
+
+ // If more results are available then use an iterative increase to fetch
+ // the remaining updates by creating successive jobs
+ if ( $linksCount > $this->limit ) {
+ $job = new self(
+ $this->getTitle(),
+ [
+ 'idlist' => $idList,
+ 'limit' => $this->limit,
+ 'offset' => $this->offset + self::CHUNK_SIZE,
+ 'exec.mode' => $this->getParameter( 'exec.mode' )
+ ]
+ );
+
+ $job->run();
+ }
+
+ if ( $hashList === [] ) {
+ return true;
+ }
+
+ list( $hashList, $queryList ) = $this->splitList( $hashList );
+ $listCount = count( $hashList );
+
+ $cachedQueryResultPrefetcher = $this->applicationFactory->singleton(
+ 'CachedQueryResultPrefetcher'
+ );
+
+ $cachedQueryResultPrefetcher->resetCacheBy(
+ $queryList,
+ 'ParserCachePurgeJob'
+ );
+
+ if ( $this->getParameter( 'exec.mode' ) === self::EXEC_JOURNAL ) {
+ $dependencyLinksUpdateJournal->updateFromList( $hashList, $this->getTitle()->getLatestRevID() );
+ } else{
+ $this->addPagesToUpdater( $hashList );
+ }
+ }
+
+ public function splitList( $hashList ) {
+
+ $targetLinksList = [];
+ $queryList = [];
+
+ foreach ( $hashList as $hash ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ list( $title, $namespace, $iw, $subobjectname ) = explode( '#', $hash, 4 );
+
+ // QueryResultCache stores queries with they queryID = $subobjectname
+ if ( strpos( $subobjectname, Query::ID_PREFIX ) !== false ) {
+ $queryList[$subobjectname] = true;
+ }
+
+ // We make an assumption (as we avoid to query the DB) about that a
+ // query is bind to its subject by simply removing the subobject
+ // identifier (_QUERY*) and creating the base (or root) subject for
+ // the selected target (embedded query)
+ $targetLinksList[HashBuilder::createHashIdFromSegments( $title, $namespace, $iw )] = true;
+ }
+
+ return [ array_keys( $targetLinksList ), array_keys( $queryList ) ];
+ }
+
+ private function addPagesToUpdater( array $hashList ) {
+ foreach ( $hashList as $hash ) {
+ $this->pageUpdater->addPage(
+ HashBuilder::newTitleFromHash( $hash )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/PropertyStatisticsRebuildJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/PropertyStatisticsRebuildJob.php
new file mode 100644
index 00000000..f1899c69
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/PropertyStatisticsRebuildJob.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatisticsRebuildJob extends Job {
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ */
+ public function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.propertyStatisticsRebuild', $title, $params );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 2.5
+ */
+ public function run() {
+
+ if ( $this->waitOnCommandLineMode() ) {
+ return true;
+ }
+
+ $deferredCallableUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate(
+ [ $this, 'rebuild' ]
+ );
+
+ $deferredCallableUpdate->setOrigin( __METHOD__ );
+ $deferredCallableUpdate->runAsAutoCommit();
+ $deferredCallableUpdate->pushUpdate();
+
+ return true;
+ }
+
+ public function rebuild() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $maintenanceFactory = $applicationFactory->newMaintenanceFactory();
+
+ // Use a fixed store to avoid issues like "Call to undefined method
+ // SMW\SPARQLStore\SPARQLStore::getDataItemHandlerForDIType" because
+ // the property statistics table and hereby its update is bound to
+ // the SQLStore
+ $propertyStatisticsRebuilder = $maintenanceFactory->newPropertyStatisticsRebuilder(
+ $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' )
+ );
+
+ $propertyStatisticsRebuilder->rebuild();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/RefreshJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/RefreshJob.php
new file mode 100644
index 00000000..eda69170
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/RefreshJob.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+
+/**
+ * RefreshJob iterates over all page ids of the wiki, to perform an update
+ * action for all of them in sequence. This corresponds to the in-wiki version
+ * of the SMW_refreshData.php script for updating the whole wiki.
+ *
+ * @note This class ignores $smwgEnableUpdateJobs and always creates updates.
+ * In fact, it might be needed specifically on wikis that do not use update
+ * jobs in normal operation.
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class RefreshJob extends Job {
+
+ /**
+ * Constructor. The parameters optionally specified in the second
+ * argument of this constructor use the following array keys:
+ *
+ * - 'spos' : (start index, default 1),
+ * - 'prog' : (progress indicator, default 0),
+ * - 'rc' : (number of runs to be done, default 1)
+ *
+ * If more than one run is done, then the first run will restrict to properties
+ * and types. The progress indication refers to the current run, not to the
+ * overall job.
+ *
+ * @param Title $title
+ * @param array $params
+ */
+ public function __construct( $title, $params = [ 'spos' => 1, 'prog' => 0, 'rc' => 1 ] ) {
+ parent::__construct( 'smw.refresh', $title, $params );
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @return boolean
+ */
+ public function run() {
+
+ if ( $this->hasParameter( 'spos' ) ) {
+ $this->refreshData( $this->getParameter( 'spos' ) );
+ }
+
+ return true;
+ }
+
+ /**
+ * Report the estimated progress status of this job as a number between
+ * 0 and 1 (0% to 100%). The progress refers to the state before
+ * processing this job.
+ *
+ * @return double
+ */
+ public function getProgress() {
+
+ $prog = $this->hasParameter( 'prog' ) ? $this->getParameter( 'prog' ) : 0;
+ $run = $this->hasParameter( 'run' ) ? $this->getParameter( 'run' ): 1;
+ $rc = $this->hasParameter( 'rc' ) ? $this->getParameter( 'rc' ) : 1;
+
+ return round( ( $run - 1 + $prog ) / $rc, 1 );
+ }
+
+ /**
+ * @param $spos start index
+ */
+ protected function refreshData( $spos ) {
+
+ $run = $this->hasParameter( 'run' ) ? $this->getParameter( 'run' ) : 1;
+
+ $entityRebuildDispatcher = ApplicationFactory::getInstance()->getStore()->refreshData(
+ $spos,
+ 20,
+ $this->getNamespace( $run )
+ );
+
+ $entityRebuildDispatcher->rebuild( $spos );
+ $prog = $entityRebuildDispatcher->getEstimatedProgress();
+
+ if ( $spos > 0 ) {
+
+ $this->createNextJob( [
+ 'spos' => $spos,
+ 'prog' => $prog,
+ 'rc' => $this->getParameter( 'rc' ),
+ 'run' => $run
+ ] );
+
+ } elseif ( $this->hasParameter( 'rc' ) && $this->getParameter( 'rc' ) > $run ) { // do another run from the beginning
+
+ $this->createNextJob( [
+ 'spos' => 1,
+ 'prog' => 0,
+ 'rc' => $this->getParameter( 'rc' ),
+ 'run' => $run + 1
+ ] );
+
+ }
+
+ return true;
+ }
+
+ protected function createNextJob( array $parameters ) {
+
+ $job = new self(
+ $this->getTitle(),
+ $parameters
+ );
+
+ $job->isEnabledJobQueue( $this->isEnabledJobQueue );
+ $job->insert();
+ }
+
+ protected function getNamespace( $run ) {
+
+ if ( !$this->hasParameter( 'rc' ) ) {
+ return false;
+ }
+
+ return ( ( $this->getParameter( 'rc' ) > 1 ) && ( $run == 1 ) ) ? [ SMW_NS_PROPERTY ] : false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateDispatcherJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateDispatcherJob.php
new file mode 100644
index 00000000..8022830f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateDispatcherJob.php
@@ -0,0 +1,368 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use Hooks;
+use SMW\MediaWiki\Job;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\DataTypeRegistry;
+use SMW\RequestOptions;
+use SMW\Enum;
+use SMW\Exception\DataItemDeserializationException;
+use SMWDataItem as DataItem;
+use Title;
+
+/**
+ * Dispatcher to find and create individual UpdateJob instances for a specific
+ * subject and its linked entities.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class UpdateDispatcherJob extends Job {
+
+ /**
+ * Restrict dispatch process to an available pool of data
+ */
+ const RESTRICTED_DISPATCH_POOL = 'restricted.disp.pool';
+
+ /**
+ * Parameter for the secondary run to contain a list of update jobs to be
+ * inserted at once.
+ */
+ const JOB_LIST = 'job-list';
+
+ /**
+ * Size of chunks used when invoking the secondary dispatch run
+ */
+ const CHUNK_SIZE = 500;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param array $params job parameters
+ * @param integer $id job id
+ */
+ public function __construct( Title $title, $params = [], $id = 0 ) {
+ parent::__construct( 'smw.updateDispatcher', $title, $params, $id );
+ $this->removeDuplicates = true;
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function run() {
+
+ $this->initServices();
+
+ /**
+ * Retrieved a job list (most likely from a secondary dispatch run) and
+ * push each list entry into the job queue to spread the work independently
+ * from the actual dispatch process.
+ */
+ if ( $this->hasParameter( self::JOB_LIST ) ) {
+ return $this->push_jobs_from_list( $this->getParameter( self::JOB_LIST ) );
+ }
+
+ /**
+ * Using an entity ID to initiate some work (which if send from the DELETE
+ * will have no valid ID_TABLE reference by the time this job is run) on
+ * some secondary tables.
+ */
+ if ( $this->hasParameter( '_id' ) ) {
+ $this->dispatch_by_id( $this->getParameter( '_id' ) );
+ }
+
+ if ( $this->getTitle()->getNamespace() === SMW_NS_PROPERTY ) {
+ $this->dispatchUpdateForProperty(
+ DIProperty::newFromUserLabel( $this->getTitle()->getText() )
+ );
+
+ $this->jobs[] = DIWikiPage::newFromTitle( $this->getTitle() )->getHash();
+ } else {
+ $this->dispatchUpdateForSubject(
+ DIWikiPage::newFromTitle( $this->getTitle() )
+ );
+ }
+
+ /**
+ * Create a secondary run by pushing collected jobs into a chunked queue
+ */
+ if ( $this->jobs !== [] ) {
+ $this->create_secondary_dispatch_run( $this->jobs );
+ }
+
+ Hooks::run( 'SMW::Job::AfterUpdateDispatcherJobComplete', [ $this ] );
+
+ return true;
+ }
+
+ private function initServices() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $this->setStore( $applicationFactory->getStore() );
+
+ $this->serializerFactory = $applicationFactory->newSerializerFactory();
+
+ $this->isEnabledJobQueue(
+ $applicationFactory->getSettings()->get( 'smwgEnableUpdateJobs' )
+ );
+ }
+
+ private function dispatch_by_id( $id ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $queryDependencyLinksStoreFactory = $applicationFactory->singleton( 'QueryDependencyLinksStoreFactory' );
+
+ $queryDependencyLinksStore = $queryDependencyLinksStoreFactory->newQueryDependencyLinksStore(
+ $applicationFactory->getStore()
+ );
+
+ $count = $queryDependencyLinksStore->countDependencies(
+ $id
+ );
+
+ if ( $count === 0 ) {
+ return;
+ }
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit(
+ $count
+ );
+
+ $dependencyTargetLinks = $queryDependencyLinksStore->findDependencyTargetLinks(
+ [ $id ],
+ $requestOptions
+ );
+
+ foreach ( $dependencyTargetLinks as $targetLink ) {
+ list( $title, $namespace, $iw, $subobjectname ) = explode( '#', $targetLink, 4 );
+
+ // @see DIWikiPage::doUnserialize
+ if ( !isset( $this->jobs[( $title . '#' . $namespace . '#' . $iw . '#' )] ) ) {
+ $this->jobs[( $title . '#' . $namespace . '#' . $iw . '#' )] = true;
+ }
+ }
+ }
+
+ private function create_secondary_dispatch_run( $jobs ) {
+
+ $origin = $this->getTitle()->getPrefixedText();
+
+ foreach ( array_chunk( $jobs, self::CHUNK_SIZE, true ) as $jobList ) {
+ $job = new self(
+ Title::newFromText( 'UpdateDispatcher/SecondaryRun/' . md5( json_encode( $jobList ) ) ),
+ [
+ self::JOB_LIST => $jobList,
+ 'origin' => $origin,
+
+ // We expect entities to exists that are send through the
+ // dispatch to avoid creating "dead" ids on non existing (or
+ // already deleted) entities
+ 'check_exists' => true
+ ]
+ );
+
+ $job->insert();
+ }
+ }
+
+ private function dispatchUpdateForSubject( DIWikiPage $subject ) {
+
+ if ( $this->getParameter( self::RESTRICTED_DISPATCH_POOL ) !== true ) {
+ $this->addUpdateJobsForProperties(
+ $this->store->getProperties( $subject )
+ );
+
+ $this->addUpdateJobsForProperties(
+ $this->store->getInProperties( $subject )
+ );
+ }
+
+ $this->addUpdateJobsFromDeserializedSemanticData();
+ }
+
+ private function dispatchUpdateForProperty( DIProperty $property ) {
+ $this->addUpdateJobsForProperties( [ $property ] );
+ $this->addUpdateJobsForSubjectsThatContainTypeError();
+ $this->addUpdateJobsFromDeserializedSemanticData();
+ }
+
+ private function addUpdateJobsForProperties( array $properties ) {
+ foreach ( $properties as $property ) {
+
+ if ( !$property->isUserDefined() ) {
+ continue;
+ }
+
+ // Before doing some work, make sure to only use page type properties
+ // as a means to generate a resource (job) action
+ $type = DataTypeRegistry::getInstance()->getDataItemByType(
+ $property->findPropertyTypeId()
+ );
+
+ if ( $type !== DataItem::TYPE_WIKIPAGE ) {
+ continue;
+ }
+
+ $requestOptions = new RequestOptions();
+
+ // No need for a warmup since we want to keep the iterator for as
+ // long as possible to only access one item at a time
+ $requestOptions->setOption( Enum::SUSPEND_CACHE_WARMUP, true );
+
+ // If we have an ID then use it to restrict the range of mactches
+ // against that object reference (aka `o_id`). Of course, in case of
+ // a delete action it is required that the disposer job (that removes
+ // all pending references from any active table for that reference)
+ // is called only after the job queue has been cleared otherwise
+ // the `o_id` can no longer be a matchable ID.
+ if ( $this->hasParameter( '_id' ) ) {
+ $requestOptions->addExtraCondition( [ 'o_id' => $this->getParameter( '_id' ) ] );
+ }
+
+ // Best effort to find all entities to a selected property
+ $subjects = $this->store->getAllPropertySubjects( $property, $requestOptions );
+
+ $this->add_job(
+ $this->apply_filter( $property, $subjects )
+ );
+ }
+ }
+
+ private function apply_filter( $property, $subjects ) {
+
+ // If the an ID was provided it already restricted the list of references
+ // hence avoid any further work
+ if ( $this->hasParameter( '_id' ) ) {
+ return $subjects;
+ }
+
+ if ( $this->getParameter( self::RESTRICTED_DISPATCH_POOL ) !== true ) {
+ return $subjects;
+ }
+
+ $list = [];
+
+ // Identify the source as base for a comparison
+ $source = DIWikiPage::newFromTitle( $this->getTitle() );
+
+ foreach ( $subjects as $subject ) {
+
+ // #3322
+ // Investigate which subjects have an actual connection to the
+ // subject
+ $dataItems = $this->store->getPropertyValues( $subject, $property );
+
+ foreach ( $dataItems as $dataItem ) {
+ // Make a judgment based on a literal comparison for the
+ // values assigned and the now deleted entity
+ if ( $dataItem instanceof DIWikiPage && $dataItem->equals( $source ) ) {
+ $list[] = $subject;
+ }
+ }
+ }
+
+ return $list;
+ }
+
+ private function addUpdateJobsForSubjectsThatContainTypeError() {
+
+ $subjects = $this->store->getPropertySubjects(
+ new DIProperty( DIProperty::TYPE_ERROR ),
+ DIWikiPage::newFromTitle( $this->getTitle() )
+ );
+
+ $this->add_job(
+ $subjects
+ );
+ }
+
+ private function addUpdateJobsFromDeserializedSemanticData() {
+
+ if ( !$this->hasParameter( 'semanticData' ) ) {
+ return;
+ }
+
+ $semanticData = $this->serializerFactory->newSemanticDataDeserializer()->deserialize(
+ $this->getParameter( 'semanticData' )
+ );
+
+ $this->addUpdateJobsForProperties(
+ $semanticData->getProperties()
+ );
+ }
+
+ private function add_job( $subjects = [] ) {
+
+ foreach ( $subjects as $subject ) {
+
+ // Not trying to get the title here as it is waste of resources
+ // as makeTitleSafe is expensive for large lists
+ // $title = $subject->getTitle();
+
+ if ( !$subject instanceof DIWikiPage ) {
+ continue;
+ }
+
+ // Do not use the full subject as hash as we don't care about subobjects
+ // since the root subject is enough to update all related subobjects
+ // The format is the same as expected by DIWikiPage::doUnserialize
+ $hash = $subject->getDBKey() . '#' . $subject->getNamespace() . '#' . $subject->getInterwiki() . '#';
+
+ if ( !isset( $this->jobs[$hash] ) ) {
+ $this->jobs[$hash] = true;
+ }
+ }
+ }
+
+ private function push_jobs_from_list( array $subjects ) {
+
+ $check_exists = $this->getParameter( 'check_exists', false );
+
+ $parameters = [
+ UpdateJob::FORCED_UPDATE => true,
+ 'origin' => $this->getParameter( 'origin', 'UpdateDispatcherJob' )
+ ];
+
+ // We expect non-duplicate subjects in the list and therefore deserialize
+ // without any extra validation
+ foreach ( $subjects as $key => $subject ) {
+
+ if ( is_string( $key ) ) {
+ $subject = $key;
+ }
+
+ try {
+ $subject = DIWikiPage::doUnserialize( $subject );
+ } catch( DataItemDeserializationException $e ) {
+ continue;
+ }
+
+ if ( $check_exists && !$this->store->getObjectIds()->exists( $subject ) ) {
+ continue;
+ }
+
+ if ( ( $title = $subject->getTitle() ) === null ) {
+ continue;
+ }
+
+ $this->jobs[] = new UpdateJob( $title, $parameters );
+ }
+
+ $this->pushToJobQueue();
+
+ return true;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateJob.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateJob.php
new file mode 100644
index 00000000..6fbf269d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Jobs/UpdateJob.php
@@ -0,0 +1,322 @@
+<?php
+
+namespace SMW\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Job;
+use LinkCache;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Enum;
+use SMW\EventHandler;
+use Title;
+
+/**
+ * UpdateJob is responsible for the asynchronous update of semantic data
+ * using MediaWiki's JobQueue infrastructure.
+ *
+ * Update jobs are created if, when saving an article,
+ * it is detected that the content of other pages must be re-parsed as well (e.g.
+ * due to some type change).
+ *
+ * @note This job does not update the page display or parser cache, so in general
+ * it might happen that part of the wiki page still displays old data (e.g.
+ * formatting in-page values based on a datatype thathas since been changed), whereas
+ * the Factbox and query/browsing interfaces might already show the updated records.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Daniel M. Herzig
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class UpdateJob extends Job {
+
+ /**
+ * Enforces an update independent of the update marker status
+ */
+ const FORCED_UPDATE = 'forcedUpdate';
+
+ /**
+ * Indicates the use of the _CHGPRO property as base for the SemanticData
+ */
+ const CHANGE_PROP = 'changeProp';
+
+ /**
+ * Indicates the use of the semanticData parameter
+ */
+ const SEMANTIC_DATA = 'semanticData';
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param array $params
+ */
+ function __construct( Title $title, $params = [] ) {
+ parent::__construct( 'smw.update', $title, $params );
+ $this->removeDuplicates = true;
+
+ $this->isEnabledJobQueue(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgEnableUpdateJobs' )
+ );
+ }
+
+ /**
+ * @see Job::run
+ *
+ * @return boolean
+ */
+ public function run() {
+
+ // #2199 ("Invalid or virtual namespace -1 given")
+ if ( $this->getTitle()->isSpecialPage() ) {
+ return true;
+ }
+
+ LinkCache::singleton()->clear();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ if ( !$this->hasParameter( self::FORCED_UPDATE ) && $this->matchesLastModified( $this->getTitle() ) ) {
+ return true;
+ }
+
+ if ( $this->getTitle()->exists() ) {
+ return $this->doUpdate();
+ }
+
+ $this->applicationFactory->getStore()->clearData(
+ DIWikiPage::newFromTitle( $this->getTitle() )
+ );
+
+ return true;
+ }
+
+ private function matchesLastModified( $title ) {
+
+ if ( !$this->getParameter( 'shallowUpdate' ) ) {
+ return false;
+ }
+
+ $lastModified = $this->getLastModifiedTimestamp(
+ DIWikiPage::newFromTitle( $title )
+ );
+
+ if ( $lastModified !== \WikiPage::factory( $title )->getTimestamp() ) {
+ return false;
+ }
+
+ $pageUpdater = $this->applicationFactory->newPageUpdater();
+ $pageUpdater->addPage( $title );
+ $pageUpdater->waitOnTransactionIdle();
+ $pageUpdater->doPurgeParserCache();
+
+ return true;
+ }
+
+ private function doUpdate() {
+
+ // ChangePropagationJob
+ if ( $this->hasParameter( self::CHANGE_PROP ) ) {
+ return $this->change_propagation( $this->getParameter( self::CHANGE_PROP ) );
+ }
+
+ if ( $this->hasParameter( self::SEMANTIC_DATA ) ) {
+ return $this->set_data( $this->getParameter( self::SEMANTIC_DATA ) );
+ }
+
+ return $this->parse_content();
+ }
+
+ private function change_propagation( $dataItem ) {
+
+ $this->setParameter( 'updateType', 'ChangePropagation' );
+ $subject = DIWikiPage::doUnserialize( $dataItem );
+
+ // Read the _CHGPRO property and fetch the serialized
+ // SemanticData object
+ $pv = $this->applicationFactory->getStore()->getPropertyValues(
+ $subject,
+ new DIProperty( DIProperty::TYPE_CHANGE_PROP )
+ );
+
+ if ( $pv === [] ) {
+ return;
+ }
+
+ // PropertySpecificationChangeNotifier encodes the serialized content
+ // using the JSON format
+ $semanticData = json_decode( end( $pv )->getString(), true );
+
+ $this->set_data(
+ $semanticData
+ );
+ }
+
+ private function set_data( $semanticData ) {
+
+ $this->setParameter( 'updateType', 'SemanticData' );
+
+ $semanticData = $this->applicationFactory->newSerializerFactory()->newSemanticDataDeserializer()->deserialize(
+ $semanticData
+ );
+
+ $semanticData->removeProperty(
+ new DIProperty( DIProperty::TYPE_CHANGE_PROP )
+ );
+
+ $parserData = $this->applicationFactory->newParserData(
+ $this->getTitle(),
+ new ParserOutput()
+ );
+
+ $parserData->setSemanticData( $semanticData );
+
+ $parserData->setOption(
+ Enum::OPT_SUSPEND_PURGE,
+ false
+ );
+
+ return $this->updateStore( $parserData );
+ }
+
+ private function parse_content() {
+
+ $this->setParameter( 'updateType', 'ContentParse' );
+
+ $contentParser = $this->applicationFactory->newContentParser( $this->getTitle() );
+ $contentParser->parse();
+
+ if ( !( $contentParser->getOutput() instanceof ParserOutput ) ) {
+ $this->setLastError( $contentParser->getErrors() );
+ return false;
+ }
+
+ $parserData = $this->applicationFactory->newParserData(
+ $this->getTitle(),
+ $contentParser->getOutput()
+ );
+
+ // Suspend the purge as any preceding parse process most likely has
+ // invalidated the cache for a selected subject
+ $parserData->setOption(
+ Enum::OPT_SUSPEND_PURGE,
+ true
+ );
+
+ return $this->updateStore( $parserData );
+ }
+
+ private function updateStore( $parserData ) {
+
+ $this->applicationFactory->getMediaWikiLogger()->info(
+ [
+ 'Job',
+ 'UpdateJob',
+ '{title}',
+ 'Type: {updateType}',
+ 'Origin: {origin}',
+ 'isForcedUpdate: {forcedUpdate}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'title' => $this->getTitle()->getPrefixedDBKey(),
+ 'origin' => $this->getParameter( 'origin', 'N/A' ),
+ 'updateType' => $this->getParameter( 'updateType' ),
+ 'forcedUpdate' => $this->getParameter( self::FORCED_UPDATE )
+ ]
+ );
+
+ $eventHandler = EventHandler::getInstance();
+
+ $dispatchContext = $eventHandler->newDispatchContext();
+ $dispatchContext->set( 'title', $this->getTitle() );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'factbox.cache.delete',
+ $dispatchContext
+ );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.propertyvalues.prefetcher.reset',
+ $dispatchContext
+ );
+
+ // TODO
+ // Rebuild the factbox
+
+ $origin[] = 'UpdateJob';
+
+ if ( $this->hasParameter( 'origin' ) ) {
+ $origin[] = $this->getParameter( 'origin' );
+ }
+
+ if ( $this->hasParameter( 'ref' ) ) {
+ $origin[] = $this->getParameter( 'ref' );
+ }
+
+ $parserData->setOrigin( $origin );
+
+ $parserData->setOption(
+ Enum::OPT_SUSPEND_PURGE,
+ $this->getParameter( Enum::OPT_SUSPEND_PURGE )
+ );
+
+ $parserData->setOption(
+ $parserData::OPT_FORCED_UPDATE,
+ $this->getParameter( self::FORCED_UPDATE )
+ );
+
+ $parserData->setOption(
+ $parserData::OPT_CHANGE_PROP_UPDATE,
+ $this->getParameter( self::CHANGE_PROP )
+ );
+
+ $parserData->getSemanticData()->setOption(
+ \SMW\SemanticData::OPT_LAST_MODIFIED,
+ wfTimestamp( TS_UNIX )
+ );
+
+ $parserData->setOption(
+ $parserData::OPT_CREATE_UPDATE_JOB,
+ false
+ );
+
+ $parserData->getSemanticData()->setOption(
+ Enum::PURGE_ASSOC_PARSERCACHE,
+ (bool)$this->getParameter( Enum::PURGE_ASSOC_PARSERCACHE )
+ );
+
+ $parserData->updateStore();
+
+ return true;
+ }
+
+ /**
+ * Convenience method to find last modified MW timestamp for a subject that
+ * has been added using the storage-engine.
+ */
+ private function getLastModifiedTimestamp( DIWikiPage $wikiPage ) {
+
+ $dataItems = $this->applicationFactory->getStore()->getPropertyValues(
+ $wikiPage,
+ new DIProperty( '_MDAT' )
+ );
+
+ if ( $dataItems !== [] ) {
+ return end( $dataItems )->getMwTimestamp( TS_MW );
+ }
+
+ return 0;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/LocalTime.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/LocalTime.php
new file mode 100644
index 00000000..86070a80
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/LocalTime.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use DateInterval;
+use DateTime;
+use DateTimeZone;
+use User;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class LocalTime {
+
+ /**
+ * @see $GLOBALS['wgLocalTZoffset']
+ * @var integer
+ */
+ private static $localTimeOffset = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $localTimeOffset
+ */
+ public static function setLocalTimeOffset( $localTimeOffset ) {
+ self::$localTimeOffset = $localTimeOffset;
+ }
+
+ /**
+ * @see Language::userAdjust
+ *
+ * Language::userAdjust cannot be used as entirely relies on the timestamp
+ * premises making < 1970 return invalid results hence we copy the relevant
+ * part on work with the DateInterval instead.
+ *
+ * @since 3.0
+ *
+ * @param DateTime $dateTime
+ * @param User|null $user
+ *
+ * @return DateTime
+ */
+ public static function getLocalizedTime( DateTime $dateTime, User $user = null ) {
+
+ $tz = $user instanceof User ? $user->getOption( 'timecorrection' ) : false;
+ $data = explode( '|', $tz, 3 );
+
+ // DateTime is mutable, keep track of possible changes
+ $dateTime->hasLocalTimeCorrection = false;
+
+ if ( $data[0] == 'ZoneInfo' ) {
+ try {
+ $userTZ = new DateTimeZone( $data[2] );
+ $dateTime->setTimezone( $userTZ );
+ $dateTime->hasLocalTimeCorrection = true;
+ return $dateTime;
+ } catch ( \Exception $e ) {
+ // Unrecognized timezone, default to 'Offset' with the stored offset.
+ $data[0] = 'Offset';
+ }
+ }
+
+ if ( $data[0] == 'System' || $tz == '' ) {
+ # Global offset in minutes.
+ $minDiff = self::$localTimeOffset;
+ } elseif ( $data[0] == 'Offset' ) {
+ $minDiff = intval( $data[1] );
+ } else {
+ $data = explode( ':', $tz );
+ if ( count( $data ) == 2 ) {
+ $data[0] = intval( $data[0] );
+ $data[1] = intval( $data[1] );
+ $minDiff = abs( $data[0] ) * 60 + $data[1];
+ if ( $data[0] < 0 ) {
+ $minDiff = -$minDiff;
+ }
+ } else {
+ $minDiff = intval( $data[0] ) * 60;
+ }
+ }
+
+ # No difference ?
+ if ( 0 == $minDiff ) {
+ return $dateTime;
+ }
+
+ $dateInterval = new DateInterval( "PT" . abs( $minDiff ) . "M" );
+
+ if ( $minDiff > 0 ) {
+ $dateTime->add( $dateInterval );
+ } else {
+ $dateTime->sub( $dateInterval );
+ }
+
+ $dateTime->hasLocalTimeCorrection = true;
+
+ return $dateTime;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MagicWordsFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MagicWordsFinder.php
new file mode 100644
index 00000000..56733321
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MagicWordsFinder.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use MagicWord;
+use ParserOutput;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class MagicWordsFinder {
+
+ /**
+ * @var ParserOutput
+ */
+ private $parserOutput = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param ParserOutput|null $parserOutput
+ */
+ public function __construct( ParserOutput $parserOutput = null ) {
+ $this->parserOutput = $parserOutput;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param ParserOutput $parserOutput
+ *
+ * @return self
+ */
+ public function setOutput( ParserOutput $parserOutput ) {
+ $this->parserOutput = $parserOutput;
+ return $this;
+ }
+
+ /**
+ * Find the magic word and have it removed from the text
+ *
+ * @since 2.0
+ *
+ * @param $magicWord
+ * @param &$text
+ *
+ * @return string
+ */
+ public function findMagicWordInText( $magicWord, &$text ) {
+
+ $mw = MagicWord::get( $magicWord );
+
+ if ( $mw->matchAndRemove( $text ) ) {
+ return $magicWord;
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param array $words
+ */
+ public function pushMagicWordsToParserOutput( array $words ) {
+
+ $this->parserOutput->setTimestamp( wfTimestampNow() );
+
+ // Filter empty lines
+ $words = array_values( array_filter( $words ) );
+
+ if ( $this->hasExtensionData() && $words !== [] ) {
+ return $this->parserOutput->setExtensionData( 'smwmagicwords', $words );
+ }
+
+ return $this->parserOutput->mSMWMagicWords = $words;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return array
+ */
+ public function getMagicWords() {
+
+ if ( $this->hasExtensionData() ) {
+ return $this->parserOutput->getExtensionData( 'smwmagicwords' );
+ }
+
+ if ( isset( $this->parserOutput->mSMWMagicWords ) ) {
+ return $this->parserOutput->mSMWMagicWords;
+ }
+
+ return [];
+ }
+
+ /**
+ * FIXME Remove when MW 1.21 becomes mandatory
+ */
+ protected function hasExtensionData() {
+ return method_exists( $this->parserOutput, 'getExtensionData' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/ManualEntryLogger.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/ManualEntryLogger.php
new file mode 100644
index 00000000..a8e92649
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/ManualEntryLogger.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use LogEntry;
+use ManualLogEntry;
+use Title;
+use User;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ManualEntryLogger {
+
+ /**
+ * @var logEntry
+ */
+ private $logEntry = null;
+
+ /**
+ * @var array
+ */
+ private $eventTypes = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param LogEntry|null $logEntry
+ */
+ public function __construct( LogEntry $logEntry = null ) {
+ $this->logEntry = $logEntry;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $eventTypes
+ */
+ public function registerLoggableEventType( $eventType ) {
+ $this->eventTypes[$eventType] = true;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ * @param string $performer
+ * @param string $target
+ * @param string $comment
+ *
+ * @return integer|null
+ */
+ public function log( $type, $performer, $target, $comment ) {
+
+ if ( !isset( $this->eventTypes[$type] ) || !$this->eventTypes[$type] ) {
+ return null;
+ }
+
+ $logEntry = $this->newManualLogEntryForType( $type );
+ $logEntry->setTarget( Title::newFromText( $target ) );
+
+ if ( is_string( $performer) ) {
+ $performer = User::newFromName( $performer );
+ }
+
+ $logEntry->setPerformer( $performer );
+ $logEntry->setParameters( [] );
+ $logEntry->setComment( $comment );
+
+ return $logEntry->insert();
+ }
+
+ protected function newManualLogEntryForType( $type ) {
+
+ if ( $this->logEntry !== null ) {
+ return $this->logEntry;
+ }
+
+ return new ManualLogEntry( 'smw', $type );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MediaWikiNsContentReader.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MediaWikiNsContentReader.php
new file mode 100644
index 00000000..3b2c74cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MediaWikiNsContentReader.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Revision;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MediaWikiNsContentReader {
+
+ /**
+ * @var boolean
+ */
+ private $skipMessageCache = false;
+
+ /**
+ * @since 2.3
+ */
+ public function skipMessageCache() {
+ $this->skipMessageCache = true;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function read( $name ) {
+
+ $content = '';
+
+ if ( !$this->skipMessageCache && wfMessage( $name )->exists() ) {
+ $content = wfMessage( $name )->inContentLanguage()->text();
+ }
+
+ if ( $content === '' ) {
+ $content = $this->readFromDatabase( $name );
+ }
+
+ return $content;
+ }
+
+ private function readFromDatabase( $name ) {
+
+ $title = Title::makeTitleSafe( NS_MEDIAWIKI, ucfirst( $name ) );
+
+ if ( $title === null ) {
+ return '';
+ }
+
+ // Revision::READ_LATEST is not specified in MW 1.19
+ $revisionReadFlag = defined( 'Revision::READ_LATEST' ) ? Revision::READ_LATEST : 0;
+
+ $revision = Revision::newFromTitle( $title, false, $revisionReadFlag );
+
+ if ( $revision === null ) {
+ return '';
+ }
+
+ if ( class_exists( 'WikitextContent' ) ) {
+ return $revision->getContent()->getNativeData();
+ }
+
+ if ( method_exists( $revision, 'getContent') ) {
+ return $revision->getContent( Revision::RAW );
+ }
+
+ return $revision->getRawText();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MessageBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MessageBuilder.php
new file mode 100644
index 00000000..5c25d48c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MessageBuilder.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use IContextSource;
+use Language;
+use Message;
+use RuntimeException;
+use Title;
+
+/**
+ * Convenience class to build language dependent messages and special text
+ * components and decrease depdencency on the Language object with SMW's code
+ * base
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class MessageBuilder {
+
+ /**
+ * @var Language
+ */
+ private $language = null;
+
+ /**
+ * @since 2.1
+ *
+ * @param Language|null $language
+ */
+ public function __construct( Language $language = null ) {
+ $this->language = $language;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Language $language
+ *
+ * @return MessageBuilder
+ */
+ public function setLanguage( Language $language ) {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param IContextSource $context
+ *
+ * @return MessageBuilder
+ */
+ public function setLanguageFromContext( IContextSource $context ) {
+ $this->language = $context->getLanguage();
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param mixed $number
+ * @param boolean $useForSpecialNumbers set to true for numbers like dates
+ *
+ * @return string
+ */
+ public function formatNumberToText( $number, $useForSpecialNumbers = false ) {
+ return $this->getLanguage()->formatNum( $number, $useForSpecialNumbers );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $list
+ *
+ * @return string
+ */
+ public function listToCommaSeparatedText( array $list ) {
+ return $this->getLanguage()->listToText( $list );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title,
+ * @param integer $offset,
+ * @param integer $offset,
+ * @param array $query,
+ * @param boolean|null $isAtTheEnd
+ *
+ * @return string
+ */
+ public function prevNextToText( Title $title, $limit, $offset, array $query, $isAtTheEnd ) {
+ return $this->getLanguage()->viewPrevNext( $title, $offset, $limit, $query, $isAtTheEnd );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $key
+ *
+ * @return Message
+ */
+ public function getMessage( $key ) {
+
+ $params = func_get_args();
+ array_shift( $params );
+
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+
+ $message = new Message( $key, $params );
+
+ return $message->inLanguage( $this->getLanguage() )->title( $GLOBALS['wgTitle'] );
+ }
+
+ private function getLanguage() {
+
+ if ( $this->language instanceof Language ) {
+ return $this->language;
+ }
+
+ throw new RuntimeException( 'Expected a valid language object' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MwCollaboratorFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MwCollaboratorFactory.php
new file mode 100644
index 00000000..1c0d0a22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/MwCollaboratorFactory.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Language;
+use Parser;
+use Revision;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Connection\LoadBalancerConnectionProvider;
+use SMW\MediaWiki\Connection\ConnectionProvider;
+use SMW\MediaWiki\Renderer\HtmlColumnListRenderer;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\MediaWiki\Renderer\HtmlTableRenderer;
+use SMW\MediaWiki\Renderer\HtmlTemplateRenderer;
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+use StripState;
+use Title;
+use User;
+use WikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class MwCollaboratorFactory {
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory;
+
+ /**
+ * @since 2.1
+ *
+ * @param ApplicationFactory $applicationFactory
+ */
+ public function __construct( ApplicationFactory $applicationFactory ) {
+ $this->applicationFactory = $applicationFactory;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Language|null $language
+ *
+ * @return MessageBuilder
+ */
+ public function newMessageBuilder( Language $language = null ) {
+ return new MessageBuilder( $language );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MagicWordsFinder
+ */
+ public function newMagicWordsFinder() {
+ return new MagicWordsFinder();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return RedirectTargetFinder
+ */
+ public function newRedirectTargetFinder() {
+ return new RedirectTargetFinder();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DeepRedirectTargetResolver
+ */
+ public function newDeepRedirectTargetResolver() {
+ return new DeepRedirectTargetResolver( $this->applicationFactory->newPageCreator() );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ * @param Language|null $language
+ *
+ * @return HtmlFormRenderer
+ */
+ public function newHtmlFormRenderer( Title $title, Language $language = null ) {
+
+ if ( $language === null ) {
+ $language = $title->getPageLanguage();
+ }
+
+ $messageBuilder = $this->newMessageBuilder( $language );
+
+ return new HtmlFormRenderer( $title, $messageBuilder );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlTableRenderer
+ */
+ public function newHtmlTableRenderer() {
+ return new HtmlTableRenderer();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function newHtmlColumnListRenderer() {
+ return new HtmlColumnListRenderer();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return LoadBalancerConnectionProvider
+ */
+ public function newLoadBalancerConnectionProvider( $connectionType ) {
+ return new LoadBalancerConnectionProvider( $connectionType );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $provider
+ *
+ * @return ConnectionProvider
+ */
+ public function newConnectionProvider( $provider = null ) {
+
+ $connectionProvider = new ConnectionProvider(
+ $provider
+ );
+
+ $connectionProvider->setLocalConnectionConf(
+ $this->applicationFactory->getSettings()->get( 'smwgLocalConnectionConf' )
+ );
+
+ $connectionProvider->setLogger(
+ $this->applicationFactory->getMediaWikiLogger()
+ );
+
+ return $connectionProvider;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param WikiPage $wkiPage
+ * @param Revision|null $revision
+ * @param User|null $user
+ *
+ * @return PageInfoProvider
+ */
+ public function newPageInfoProvider( WikiPage $wkiPage, Revision $revision = null, User $user = null ) {
+ return new PageInfoProvider( $wkiPage, $revision, $user );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param WikiPage $wkiPage
+ * @param Revision $revision
+ * @param User|null $user
+ *
+ * @return EditInfoProvider
+ */
+ public function newEditInfoProvider( WikiPage $wkiPage, Revision $revision, User $user = null ) {
+ return new EditInfoProvider( $wkiPage, $revision, $user );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return WikitextTemplateRenderer
+ */
+ public function newWikitextTemplateRenderer() {
+ return new WikitextTemplateRenderer();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Parser $parser
+ *
+ * @return HtmlTemplateRenderer
+ */
+ public function newHtmlTemplateRenderer( Parser $parser ) {
+ return new HtmlTemplateRenderer(
+ $this->newWikitextTemplateRenderer(),
+ $parser
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return MediaWikiNsContentReader
+ */
+ public function newMediaWikiNsContentReader() {
+ return new MediaWikiNsContentReader();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param StripState $stripState
+ *
+ * @return StripMarkerDecoder
+ */
+ public function newStripMarkerDecoder( StripState $stripState ) {
+
+ $stripMarkerDecoder = new StripMarkerDecoder(
+ $stripState
+ );
+
+ $stripMarkerDecoder->isSupported(
+ $this->applicationFactory->getSettings()->isFlagSet( 'smwgParserFeatures', SMW_PARSER_UNSTRIP )
+ );
+
+ return $stripMarkerDecoder;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageCreator.php
new file mode 100644
index 00000000..9a3331e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageCreator.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Title;
+use WikiFilePage;
+use WikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PageCreator {
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ *
+ * @return WikiPage
+ */
+ public function createPage( Title $title ) {
+ return WikiPage::factory( $title );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Title $title
+ *
+ * @return WikiFilePage
+ */
+ public function createFilePage( Title $title ) {
+ return new WikiFilePage( $title );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageInfoProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageInfoProvider.php
new file mode 100644
index 00000000..4fe69a5b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageInfoProvider.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Revision;
+use SMW\PageInfo;
+use User;
+use WikiPage;
+
+/**
+ * Provide access to MediaWiki objects relevant for the predefined property
+ * annotation process
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PageInfoProvider implements PageInfo {
+
+ /**
+ * @var WikiPage
+ */
+ private $wikiPage = null;
+
+ /**
+ * @var Revision
+ */
+ private $revision = null;
+
+ /**
+ * @var User
+ */
+ private $user = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param WikiPage $wikiPage
+ * @param Revision|null $revision
+ * @param User|null $user
+ */
+ public function __construct( WikiPage $wikiPage, Revision $revision = null, User $user = null ) {
+ $this->wikiPage = $wikiPage;
+ $this->revision = $revision;
+ $this->user = $user;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return integer
+ */
+ public function getModificationDate() {
+ return $this->wikiPage->getTimestamp();
+ }
+
+ /**
+ * @note getFirstRevision() is expensive as it initiates a read on the
+ * revision table which is not cached
+ *
+ * @since 1.9
+ *
+ * @return integer
+ */
+ public function getCreationDate() {
+ return $this->wikiPage->getTitle()->getFirstRevision()->getTimestamp();
+ }
+
+ /**
+ * @note Using isNewPage() is expensive due to access to the database
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function isNewPage() {
+
+ if ( $this->isFilePage() ) {
+ return isset( $this->wikiPage->smwFileReUploadStatus ) ? !$this->wikiPage->smwFileReUploadStatus : false;
+ }
+
+ if ( $this->revision ) {
+ return $this->revision->getParentId() === null;
+ }
+
+ return $this->wikiPage->getRevision()->getParentId() === null;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getLastEditor() {
+ return $this->user ? $this->user->getUserPage() : null;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return boolean
+ */
+ public function isFilePage() {
+ return $this->wikiPage instanceof \WikiFilePage;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return text
+ */
+ public function getNativeData() {
+
+ if ( $this->wikiPage->getContent() === null ) {
+ return '';
+ }
+
+ $content = $this->wikiPage->getContent();
+
+ if ( $content instanceof \SMW\Schema\Content\Content ) {
+ return $content->toJson();
+ }
+
+ return $content->getNativeData();
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return string|null
+ */
+ public function getMediaType() {
+
+ if ( $this->isFilePage() === false ) {
+ return null;
+ }
+
+ return $this->wikiPage->getFile()->getMediaType();
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return string|null
+ */
+ public function getMimeType() {
+
+ if ( $this->isFilePage() === false ) {
+ return null;
+ }
+
+ return $this->wikiPage->getFile()->getMimeType();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageUpdater.php
new file mode 100644
index 00000000..c65727ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/PageUpdater.php
@@ -0,0 +1,357 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use DeferrableUpdate;
+use DeferredpendingUpdates;
+use Psr\Log\LoggerAwareTrait;
+use SMW\MediaWiki\Deferred\TransactionalCallableUpdate;
+use SMW\Utils\Timer;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageUpdater implements DeferrableUpdate {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var TransactionalCallableUpdate
+ */
+ private $transactionalCallableUpdate;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var Title[]
+ */
+ private $titles = [];
+
+ /**
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @var string|null
+ */
+ private $fingerprint = null;
+
+ /**
+ * @var boolean
+ */
+ private $isHtmlCacheUpdate = true;
+
+ /**
+ * @var boolean
+ */
+ private $onTransactionIdle = false;
+
+ /**
+ * @var boolean
+ */
+ private $asPoolPurge = false;
+
+ /**
+ * @var boolean
+ */
+ private $isPending = false;
+
+ /**
+ * @var array
+ */
+ private $pendingUpdates = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param Database|null $connection
+ * @param TransactionalCallableUpdate|null $transactionalCallableUpdate
+ */
+ public function __construct( Database $connection = null, TransactionalCallableUpdate $transactionalCallableUpdate = null ) {
+ $this->connection = $connection;
+ $this->transactionalCallableUpdate = $transactionalCallableUpdate;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $fingerprint
+ */
+ public function setFingerprint( $fingerprint = null ) {
+ $this->fingerprint = $fingerprint;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isHtmlCacheUpdate
+ */
+ public function isHtmlCacheUpdate( $isHtmlCacheUpdate ) {
+ $this->isHtmlCacheUpdate = $isHtmlCacheUpdate;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param booloan $isPending
+ */
+ public function markAsPending() {
+ $this->isPending = true;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title|null $title
+ */
+ public function addPage( Title $title = null ) {
+
+ if ( $title === null ) {
+ return;
+ }
+
+ $this->titles[$title->getDBKey()] = $title;
+ }
+
+ /**
+ * @note MW 1.29+ runs Title::invalidateCache in AutoCommitUpdate which has
+ * been shown to cause transaction issues when executed while a transaction
+ * hasn't finished therefore use 'onTransactionIdle' to isolate the
+ * execution.
+ *
+ * @since 2.5
+ */
+ public function waitOnTransactionIdle() {
+ $this->onTransactionIdle = true;
+ }
+
+ /**
+ * Controls the purge to use a direct DB access to make changes to avoid
+ * racing conditions for a large number of title entities.
+ *
+ * @since 3.0
+ */
+ public function doPurgeParserCacheAsPool() {
+ if ( $this->connection !== null ) {
+ $this->connection->onTransactionIdle( function() {
+ $this->doPoolPurge();
+ } );
+ } else {
+ $this->doPoolPurge();
+ }
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function clear() {
+ $this->titles = [];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return boolean
+ */
+ public function canUpdate() {
+ return !wfReadOnly();
+ }
+
+ /**
+ * Push pendingUpdates to be either deferred or direct executable, pending
+ * the setting invoked by PageUPdater::markAsPending.
+ *
+ * @since 3.0
+ */
+ public function pushUpdate() {
+
+ if ( $this->transactionalCallableUpdate === null ) {
+ return $this->log( __METHOD__ . ' it is not possible to push updates as DeferredTransactionalUpdate)' );
+ }
+
+ $this->transactionalCallableUpdate->setCallback( function(){
+ $this->doUpdate();
+ } );
+
+ if ( $this->onTransactionIdle ) {
+ $this->transactionalCallableUpdate->waitOnTransactionIdle();
+ }
+ if ( $this->isPending ) {
+ $this->transactionalCallableUpdate->markAsPending();
+ }
+
+ $this->transactionalCallableUpdate->setFingerprint(
+ $this->fingerprint
+ );
+
+ $this->transactionalCallableUpdate->setOrigin( [
+ __METHOD__,
+ $this->origin
+ ] );
+
+ $this->transactionalCallableUpdate->pushUpdate();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function doUpdate() {
+ $this->isPending = false;
+ $this->onTransactionIdle = false;
+
+ foreach ( array_keys( $this->pendingUpdates ) as $update ) {
+ call_user_func( [ $this, $update ] );
+ }
+
+ $this->pendingUpdates = [];
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function doPurgeParserCache() {
+
+ $method = __METHOD__;
+
+ if ( $this->isPending || $this->onTransactionIdle ) {
+ return $this->pendingUpdates['doPurgeParserCache'] = true;
+ }
+
+ foreach ( $this->titles as $title ) {
+ $title->invalidateCache();
+ }
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function doPurgeHtmlCache() {
+
+ if ( $this->isHtmlCacheUpdate === false ) {
+ return;
+ }
+
+ if ( $this->isPending || $this->onTransactionIdle ) {
+ return $this->pendingUpdates['doPurgeHtmlCache'] = true;
+ }
+
+ $method = __METHOD__;
+
+ // Calls HTMLCacheUpdate, HTMLCacheUpdateJob including HTMLFileCache,
+ // CdnCacheUpdate
+ foreach ( $this->titles as $title ) {
+ $title->touchLinks();
+ }
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function doPurgeWebCache() {
+
+ $method = __METHOD__;
+
+ if ( $this->isPending || $this->onTransactionIdle ) {
+ return $this->pendingUpdates['doPurgeWebCache'] = true;
+ }
+
+ foreach ( $this->titles as $title ) {
+ $title->purgeSquid();
+ }
+ }
+
+ /**
+ * Copied from PurgeJobUtils to avoid the AutoCommitUpdate from
+ * Title::invalidateCache introduced with MW 1.28/1.29 on a large update pool
+ */
+ private function doPoolPurge() {
+
+ Timer::start( __METHOD__ );
+
+ // #3413
+ $byNamespace = [];
+
+ foreach ( $this->titles as $title ) {
+ $namespace = $title->getNamespace();
+ $pagename = $title->getDBkey();
+ $byNamespace[$namespace][] = $pagename;
+ }
+
+ $conds = [];
+
+ foreach ( $byNamespace as $namespaces => $pagenames ) {
+
+ $cond = [
+ 'page_namespace' => $namespaces,
+ 'page_title' => $pagenames,
+ ];
+
+ $conds[] = $this->connection->makeList( $cond, LIST_AND );
+ }
+
+ $titleConds = $this->connection->makeList( $conds, LIST_OR );
+
+ // Required due to postgres and "Error: 22007 ERROR: invalid input
+ // syntax for type timestamp with time zone: "20170408113703""
+ $now = $this->connection->timestamp();
+ $res = $this->connection->select(
+ 'page',
+ 'page_id',
+ [
+ $titleConds,
+ 'page_touched < ' . $this->connection->addQuotes( $now )
+ ],
+ __METHOD__
+ );
+
+ if ( $res === false ) {
+ return;
+ }
+
+ $ids = [];
+
+ foreach ( $res as $row ) {
+ $ids[] = $row->page_id;
+ }
+
+ if ( $ids === [] ) {
+ return;
+ }
+
+ $this->connection->update(
+ 'page',
+ [ 'page_touched' => $now ],
+ [
+ 'page_id' => $ids,
+ 'page_touched < ' . $this->connection->addQuotes( $now )
+ ],
+ __METHOD__
+ );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 7 ),
+ 'role' => 'developer'
+ ];
+
+ $this->logger->info( 'Page update, pool update (procTime in sec: {procTime})', $context );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/RedirectTargetFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/RedirectTargetFinder.php
new file mode 100644
index 00000000..38b61896
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/RedirectTargetFinder.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use ContentHandler;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RedirectTargetFinder {
+
+ /**
+ * @var Title|null
+ */
+ private $redirectTarget = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param string $text
+ *
+ * @return Title|null
+ */
+ public function findRedirectTargetFromText( $text ) {
+
+ if ( $this->redirectTarget === null ) {
+ $this->redirectTarget = $this->findFromText( $text );
+ }
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title|null
+ */
+ public function setRedirectTarget( Title $redirectTarget = null ) {
+ $this->redirectTarget = $redirectTarget;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return Title|null
+ */
+ public function getRedirectTarget() {
+ return $this->redirectTarget;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return boolean
+ */
+ public function hasRedirectTarget() {
+ return $this->redirectTarget instanceof Title;
+ }
+
+ private function findFromText( $text ) {
+
+ if ( $this->hasContentHandler() ) {
+ return ContentHandler::makeContent( $text, null, CONTENT_MODEL_WIKITEXT )->getRedirectTarget();
+ }
+
+ return Title::newFromRedirect( $text );
+ }
+
+ protected function hasContentHandler() {
+ return defined( 'CONTENT_MODEL_WIKITEXT' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlColumnListRenderer.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlColumnListRenderer.php
new file mode 100644
index 00000000..8f8a85f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlColumnListRenderer.php
@@ -0,0 +1,296 @@
+<?php
+
+namespace SMW\MediaWiki\Renderer;
+
+use Html;
+
+/**
+ * Simple list formatter to transform an indexed array (e.g. array( 'F' => array( 'Foo', 'Bar' ) )
+ * into a column divided list.
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class HtmlColumnListRenderer {
+
+ /**
+ * @var integer
+ */
+ private $numberOfColumns = 1;
+
+ /**
+ * @var array
+ */
+ private $contentsByIndex = [];
+
+ /**
+ * @var array
+ */
+ private $itemAttributes = [];
+
+ /**
+ * @var integer
+ */
+ private $numRows = 0;
+
+ /**
+ * @var integer
+ */
+ private $numberOfResults = 0;
+
+ /**
+ * @var integer
+ */
+ private $rowsPerColumn = 0;
+
+ /**
+ * @var integer
+ */
+ private $columnWidth = 0;
+
+ /**
+ * @var string
+ */
+ private $listType = 'ul';
+
+ /**
+ * @var string
+ */
+ private $olType = '';
+
+ /**
+ * @var string
+ */
+ private $columnListClass = 'smw-columnlist-container';
+
+ /**
+ * @var string
+ */
+ private $columnClass = 'smw-column';
+
+ /**
+ * @var boolean
+ */
+ private $isRTL = false;
+
+ /**
+ * @since 2.2
+ *
+ * @param string $columnListClass
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function setColumnListClass( $columnListClass ) {
+ $this->columnListClass = htmlspecialchars( $columnListClass );
+ return $this;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $columnListClass
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function setColumnClass( $columnClass ) {
+ $this->columnClass = htmlspecialchars( $columnClass );
+ return $this;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param boolean $isRTL
+ */
+ public function setColumnRTLDirectionalityState( $isRTL ) {
+ $this->isRTL = (bool)$isRTL;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $numberOfColumns
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function setNumberOfColumns( $numberOfColumns ) {
+ $this->numberOfColumns = $numberOfColumns;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $listType
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function setListType( $listType, $olType = '' ) {
+
+ if ( in_array( $listType, [ 'ul', 'ol' ] ) ) {
+ $this->listType = $listType;
+ }
+
+ if ( $this->listType === 'ol' && in_array( $olType, [ '1', 'a', 'A', 'i', 'I' ] ) ) {
+ $this->olType = $olType;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Allows to define attributes for a item such as:
+ *
+ * [md5( $itemContent )] = [
+ * 'id' => 'Foo'
+ * ]
+ *
+ * @since 3.0
+ *
+ * @param array $itemAttributes
+ */
+ public function setItemAttributes( array $itemAttributes ) {
+ $this->itemAttributes = $itemAttributes;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string[] $contentsByNoIndex
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function addContentsByNoIndex( array $contentsByNoIndex ) {
+
+ $contentsByEmptyIndex[''] = [];
+
+ foreach ( $contentsByNoIndex as $value ) {
+ $contentsByEmptyIndex[''][] = $value;
+ }
+
+ return $this->addContentsByIndex( $contentsByEmptyIndex );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string[] $contentsByIndex
+ *
+ * @return HtmlColumnListRenderer
+ */
+ public function addContentsByIndex( array $contentsByIndex ) {
+ $this->contentsByIndex = $contentsByIndex;
+ $this->numberOfResults = count( $this->contentsByIndex, COUNT_RECURSIVE ) - count( $this->contentsByIndex );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ $result = '';
+ $usedColumnCloser = false;
+ $this->numRows = 0;
+
+ // Class to determine whether we want responsive columns width
+ if ( strpos( $this->columnClass, 'responsive' ) !== false ) {
+ $this->columnWidth = 100;
+ $this->numberOfColumns = 1;
+ } else {
+ $this->columnWidth = floor( 100 / $this->numberOfColumns );
+ }
+
+ $this->rowsPerColumn = ceil( $this->numberOfResults / $this->numberOfColumns );
+ $listContinuesAbbrev = wfMessage( 'listingcontinuesabbrev' )->text();
+
+ foreach ( $this->contentsByIndex as $key => $resultItems ) {
+
+ if ( $resultItems === [] ) {
+ continue;
+ }
+
+ $result .= $this->makeList(
+ $key,
+ $listContinuesAbbrev,
+ $resultItems,
+ $usedColumnCloser
+ );
+ }
+
+ if ( !$usedColumnCloser ) {
+ $result .= "</{$this->listType}></div> <!-- end column -->";
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => $this->columnListClass,
+ 'dir' => $this->isRTL ? 'rtl' : 'ltr'
+ ],
+ $result . "\n" . '<br style="clear: both;"/>'
+ );
+ }
+
+ private function makeList( $key, $listContinuesAbbrev, $items, &$usedColumnCloser ) {
+
+ $result = '';
+ $previousKey = "";
+ $dir = $this->isRTL ? 'rtl' : 'ltr';
+
+ foreach ( $items as $item ) {
+
+ $attributes = [];
+
+ if ( $this->itemAttributes !== [] ) {
+ $hash = md5( $item );
+
+ if ( isset( $this->itemAttributes[$hash] ) ) {
+ $attributes = $this->itemAttributes[$hash];
+ }
+ }
+
+ if ( $this->numRows % $this->rowsPerColumn == 0 ) {
+ $result .= "<div class=\"$this->columnClass\" style=\"width:$this->columnWidth%;\" dir=\"$dir\">";
+
+ $numRowsInColumn = $this->numRows + 1;
+ $type = $this->olType !== '' ? " type={$this->olType}" : '';
+
+ if ( $key == $previousKey ) {
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength.MaxExceeded
+ $result .= $key !== '' ? Html::element( 'div', [ 'class' => 'smw-column-header' ], "$key $listContinuesAbbrev" ) : '';
+ $result .= "<{$this->listType}$type start={$numRowsInColumn}>";
+ // @codingStandardsIgnoreEnd
+ }
+ }
+
+ // if we're at a new first letter, end
+ // the last list and start a new one
+ if ( $key != $previousKey ) {
+ $result .= $this->numRows % $this->rowsPerColumn > 0 ? "</{$this->listType}>" : '';
+ $result .= ( $key !== '' ? Html::element( 'div', [ 'class' => 'smw-column-header' ], $key ) : '' ) . "<{$this->listType}>";
+ }
+
+ $previousKey = $key;
+ $result .= Html::rawElement( 'li', $attributes, $item );
+ $usedColumnCloser = false;
+
+ if ( ( $this->numRows + 1 ) % $this->rowsPerColumn == 0 && ( $this->numRows + 1 ) < $this->numberOfResults ) {
+ $result .= "</{$this->listType}></div> <!-- end column -->";
+ $usedColumnCloser = true;
+ }
+
+ $this->numRows++;
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlFormRenderer.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlFormRenderer.php
new file mode 100644
index 00000000..23bf19a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlFormRenderer.php
@@ -0,0 +1,513 @@
+<?php
+
+namespace SMW\MediaWiki\Renderer;
+
+use Html;
+use SMW\MediaWiki\MessageBuilder;
+use Title;
+use Xml;
+
+/**
+ * Convenience class to build a html form by using a fluid interface
+ *
+ * @par Example:
+ * @code
+ * $htmlFormRenderer = new HtmlFormRenderer( $this->title, new MessageBuilder() );
+ * $htmlFormRenderer
+ * ->setName( 'Foo' )
+ * ->setParameter( 'foo', 'someValue' )
+ * ->addPaging( 10, 0, 5 )
+ * ->addHorizontalRule()
+ * ->addInputField( 'BarLabel', 'bar', 'someValue' )
+ * ->addSubmitButton()
+ * ->getForm();
+ * @endcode
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HtmlFormRenderer {
+
+ /**
+ * @var Title
+ */
+ private $title = null;
+
+ /**
+ * @var MessageBuilder
+ */
+ private $messageBuilder = null;
+
+ /**
+ * @var array
+ */
+ private $queryParameters = [];
+
+ /**
+ * @var string
+ */
+ private $name ='';
+
+ /**
+ * @var string|boolean
+ */
+ private $method = false;
+
+ /**
+ * @var string|boolean
+ */
+ private $useFieldset = false;
+
+ /**
+ * @var string|boolean
+ */
+ private $actionUrl = false;
+
+ /**
+ * @var string[]
+ */
+ private $content = [];
+
+ /**
+ * @var string
+ */
+ private $defaultPrefix = 'smw-form';
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ * @param MessageBuilder $messageBuilder
+ */
+ public function __construct( Title $title, MessageBuilder $messageBuilder ) {
+ $this->title = $title;
+ $this->messageBuilder = $messageBuilder;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlFormRenderer
+ */
+ public function clear() {
+ $this->queryParameters = [];
+ $this->content = [];
+ $this->name = '';
+ $this->method = false;
+ $this->useFieldset = false;
+ $this->actionUrl = false;
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MessageBuilder
+ */
+ public function getMessageBuilder() {
+ return $this->messageBuilder;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $name
+ *
+ * @return HtmlFormRenderer
+ */
+ public function setName( $name ) {
+ $this->name = $name;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $actionUrl
+ *
+ * @return HtmlFormRenderer
+ */
+ public function setActionUrl( $actionUrl ) {
+ $this->actionUrl = $actionUrl;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlFormRenderer
+ */
+ public function withFieldset() {
+ $this->useFieldset = true;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $method
+ *
+ * @return HtmlFormRenderer
+ */
+ public function setMethod( $method ) {
+ $this->method = strtolower( $method );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $key
+ * @param string $value
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addQueryParameter( $key, $value ) {
+ $this->queryParameters[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getQueryParameter() {
+ return $this->queryParameters;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $description
+ * @param array $attributes
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addParagraph( $text, $attributes = [] ) {
+
+ if ( $attributes === [] ) {
+ $attributes = [ 'class' => $this->defaultPrefix . '-paragraph' ];
+ }
+
+ $this->content[] = Xml::tags( 'p', $attributes, $text );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $attributes
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addHorizontalRule( $attributes = [] ) {
+
+ if ( $attributes === [] ) {
+ $attributes = [ 'class' => $this->defaultPrefix . '-horizontalrule' ];
+ }
+
+ $this->content[] = Xml::tags( 'hr', $attributes, '' );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param $level
+ * @param $text
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addHeader( $level, $text ) {
+
+ $level = strtolower( $level );
+ $level = in_array( $level, [ 'h2', 'h3', 'h4' ] ) ? $level : 'h2';
+
+ $this->content[] = Html::element( $level, [], $text );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addLineBreak() {
+ $this->content[] = Html::element( 'br', [], '' );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addNonBreakingSpace() {
+ $this->content[] = '&nbsp;';
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $text
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addSubmitButton( $text, $attributes = [] ) {
+ $this->content[] = Xml::submitButton( $text, $attributes );
+ return $this;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $element
+ * @param array $attributes
+ *
+ * @return HtmlFormRenderer
+ */
+ public function openElement( $element = 'div', array $attributes = [] ) {
+ $this->content[] = Html::openElement( $element, $attributes );
+ return $this;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $element
+ * @param array $attributes
+ *
+ * @return HtmlFormRenderer
+ */
+ public function closeElement( $element = 'div', array $attributes = [] ) {
+ $this->content[] = Html::closeElement( $element, $attributes );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $label
+ * @param string $name
+ * @param string $value
+ * @param string|null $id
+ * @param integer $length
+ * @param array $attributes
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addInputField( $label, $name, $value, $id = null, $size = 20, array $attributes = [] ) {
+
+ if ( $id === null ) {
+ $id = $name;
+ }
+
+ $this->addQueryParameter( $name, $value );
+
+ if ( !isset( $attributes['class'] ) ) {
+ $attributes['class'] = $this->defaultPrefix . '-input';
+ }
+
+ $label = Xml::label( $label, $id, [] );
+ $input = Xml::input( $name, $size, $value, [ 'id' => $id ] + $attributes );
+
+ $this->content[] = $label . '&#160;' . $input;
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $inputName
+ * @param string $inputValue
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addHiddenField( $inputName, $inputValue ) {
+
+ $this->addQueryParameter( $inputName, $inputValue );
+
+ $this->content[] = Html::hidden( $inputName, $inputValue );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $label
+ * @param string $inputName
+ * @param string $inputValue
+ * @param array $options
+ * @param string|null $id
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addOptionSelectList( $label, $inputName, $inputValue, $options, $id = null ) {
+
+ if ( $id === null ) {
+ $id = $inputName;
+ }
+
+ $this->addQueryParameter( $inputName, $inputValue );
+
+ ksort( $options );
+
+ $html = '';
+ $optionsHtml = [];
+
+ foreach ( $options as $internalId => $name ) {
+ $optionsHtml[] = Html::element(
+ 'option', [
+ // 'disabled' => false,
+ 'value' => $internalId,
+ 'selected' => $internalId == $inputValue,
+ ], $name
+ );
+ }
+
+ $html .= Html::element( 'label', [ 'for' => $id ], $label ) . '&#160;';
+
+ $html .= Html::openElement(
+ 'select',
+ [
+ 'name' => $inputName,
+ 'id' => $id,
+ 'class' => $this->defaultPrefix . '-select' ] ) . "\n" .
+ implode( "\n", $optionsHtml ) . "\n" .
+ Html::closeElement( 'select' );
+
+ $this->content[] = $html;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $label
+ * @param string $inputName
+ * @param string $inputValue
+ * @param boolean $isChecked
+ * @param string|null $id
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addCheckbox( $label, $inputName, $inputValue, $isChecked = false, $id = null, $attributes = [] ) {
+
+ if ( $id === null ) {
+ $id = $inputName;
+ }
+
+ $this->addQueryParameter( $inputName, $inputValue );
+
+ $html = Xml::checkLabel(
+ $label,
+ $inputName,
+ $id,
+ $isChecked,
+ [
+ 'id' => $id,
+ 'class' => $this->defaultPrefix . '-checkbox',
+ 'value' => $inputValue
+ ] + ( $isChecked ? [ 'checked' => 'checked' ] : [] )
+ );
+
+ $this->content[] = Html::rawElement( 'span', $attributes, $html );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @note Encapsulate as closure to ensure that the build contains all query
+ * parameters that are necessary to build the paging links
+ *
+ * @param integer $limit
+ * @param integer $offset
+ * @param integer $count
+ * @param integer|null $messageCount
+ *
+ * @return HtmlFormRenderer
+ */
+ public function addPaging( $limit, $offset, $count, $messageCount = null ) {
+
+ $title = $this->title;
+
+ $this->content[] = function( $instance ) use ( $title, $limit, $offset, $count, $messageCount ) {
+
+ if ( $messageCount === null ) {
+ $messageCount = ( $count > $limit ? $count - 1 : $count );
+ }
+
+ $resultCount = $instance->getMessageBuilder()
+ ->getMessage( 'showingresults' )
+ ->numParams( $messageCount, $offset + 1 )
+ ->parse();
+
+ $paging = $instance->getMessageBuilder()->prevNextToText(
+ $title,
+ $limit,
+ $offset,
+ $instance->getQueryParameter(),
+ $count < $limit
+ );
+
+ return Xml::tags( 'p', [], $resultCount ) . Xml::tags( 'p', [], $paging );
+ };
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getForm() {
+
+ $content = '';
+
+ foreach ( $this->content as $value ) {
+ $content .= is_callable( $value ) ? $value( $this ) : $value;
+ }
+
+ if ( $this->useFieldset ) {
+ $content = Xml::fieldset(
+ $this->messageBuilder->getMessage( $this->name )->text(),
+ $content,
+ [
+ 'id' => $this->defaultPrefix . "-fieldset-{$this->name}"
+ ]
+ );
+ }
+
+ $form = Xml::tags( 'form', [
+ 'id' => $this->defaultPrefix . "-{$this->name}",
+ 'name' => $this->name,
+ 'method' => in_array( $this->method, [ 'get', 'post' ] ) ? $this->method : 'get',
+ 'action' => htmlspecialchars( $this->actionUrl ? $this->actionUrl : $GLOBALS['wgScript'] )
+ ], Html::hidden(
+ 'title',
+ strtok( $this->title->getPrefixedText(), '/' )
+ ) . $content );
+
+ $this->clear();
+
+ return $form;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function renderForm() {
+ return $this->getForm();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTableRenderer.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTableRenderer.php
new file mode 100644
index 00000000..00c9d4ba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTableRenderer.php
@@ -0,0 +1,290 @@
+<?php
+
+namespace SMW\MediaWiki\Renderer;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class HtmlTableRenderer {
+
+ /**
+ * @var array
+ */
+ private $headerItems = [];
+
+ /**
+ * @var array
+ */
+ private $tableRows = [];
+
+ /**
+ * @var array
+ */
+ private $rawRows = [];
+
+ /**
+ * @var array
+ */
+ private $tableHeaders = [];
+
+ /**
+ * @var array
+ */
+ private $rawHeaders = [];
+
+ /**
+ * @var array
+ */
+ private $tableCells = [];
+
+ /**
+ * @var array
+ */
+ private $transpose = false;
+
+ /**
+ * @par Example:
+ * @code
+ * $tableBuilder = new TableBuilder();
+ *
+ * $tableBuilder
+ * ->addHeader( 'Foo' )
+ * ->addHeader( 'Bar' )
+ * ->addCell( 'Lula' )
+ * ->addCell( 'Lala' )
+ * ->addRow();
+ *
+ * $tableBuilder->getHtml()
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param boolean $htmlContext
+ */
+ public function __construct( $htmlContext = false ) {
+ $this->htmlContext = $htmlContext;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param boolean $htmlContext
+ */
+ public function setHtmlContext( $htmlContext ) {
+ $this->htmlContext = $htmlContext;
+ return $this;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param boolean $transpose
+ *
+ * @return TableBuilder
+ */
+ public function transpose( $transpose = true ) {
+ $this->transpose = $transpose;
+ return $this;
+ }
+
+ /**
+ * Adds an arbitrary header item to an internal array
+ *
+ * @since 1.9
+ *
+ * @param string $element
+ * @param string $content
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function addHeaderItem( $element, $content = '', $attributes = [] ) {
+ $this->headerItems[] = Html::rawElement( $element, $attributes, $content );
+ }
+
+ /**
+ * Returns concatenated header items
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHeaderItems() {
+ return implode( '', $this->headerItems );
+ }
+
+ /**
+ * Collects and adds table cells
+ *
+ * @since 1.9
+ *
+ * @param string $content
+ * @param array $attributes
+ *
+ * @return TableBuilder
+ */
+ public function addCell( $content = '', $attributes = [] ) {
+ if ( $content !== '' ) {
+ $this->tableCells[] = $this->createCell( $content, $attributes );
+ }
+ return $this;
+ }
+
+ /**
+ * Collects and adds table headers
+ *
+ * @since 1.9
+ *
+ * @param string $content
+ * @param array $attributes
+ *
+ * @return TableBuilder
+ */
+ public function addHeader( $content = '', $attributes = [] ) {
+ if ( $content !== '' ) {
+ $this->rawHeaders[] = [ 'content' => $content, 'attributes' => $attributes ];
+ }
+ return $this;
+ }
+
+ /**
+ * Build a row from invoked cells, copy them into a new associated array
+ * and delete those cells as they are now part of a row
+ *
+ * @par Example:
+ * @code
+ * ...
+ * $TableBuilder->addCell( 'Lula' )->addCell( 'Lala' )->addRow()
+ * ...
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @param array $attributes
+ *
+ * @return TableBuilder
+ */
+ public function addRow( $attributes = [] ) {
+ if ( $this->tableCells !== [] ) {
+ $this->rawRows[] = [ 'cells' => $this->tableCells, 'attributes' => $attributes ];
+ $this->tableCells = [];
+ }
+ return $this;
+ }
+
+ /**
+ * Returns a table
+ *
+ * @since 1.9
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function getHtml( $attributes = [] ) {
+
+ $table = $this->transpose ? $this->buildTransposedTable() : $this->buildStandardTable();
+
+ if ( $this->transpose ) {
+ $attributes['data-transpose'] = true;
+ }
+
+ if ( $table !== '' ) {
+ return Html::rawElement( 'table', $attributes, $table );
+ }
+
+ return '';
+ }
+
+ private function createRow( $content = '', $attributes = [] ) {
+ $alternate = count( $this->tableRows ) % 2 == 0 ? 'row-odd' : 'row-even';
+
+ if ( isset( $attributes['class'] ) ) {
+ $attributes['class'] = $attributes['class'] . ' ' . $alternate;
+ } else {
+ $attributes['class'] = $alternate;
+ }
+
+ return Html::rawElement( 'tr', $attributes, $content );
+ }
+
+ private function createCell( $content = '', $attributes = [] ) {
+ return Html::rawElement( 'td', $attributes, $content );
+ }
+
+ private function createHeader( $content = '', $attributes = [] ) {
+ return Html::rawElement( 'th', $attributes, $content );
+ }
+
+ private function doConcatenatedHeader() {
+
+ if ( $this->htmlContext ) {
+ return Html::rawElement( 'thead', [], implode( '', $this->tableHeaders ) );
+ }
+
+ return implode( '', $this->tableHeaders );
+ }
+
+ private function doConcatenatedRows() {
+
+ if ( $this->htmlContext ) {
+ return Html::rawElement( 'tbody', [], implode( '', $this->tableRows ) );
+ }
+
+ return implode( '', $this->tableRows );
+ }
+
+ private function buildStandardTable() {
+ $this->tableHeaders = [];
+ $this->tableRows = [];
+
+ foreach( $this->rawHeaders as $i => $header ) {
+ $this->tableHeaders[] = $this->createHeader( $header['content'], $header['attributes'] );
+ }
+
+ foreach( $this->rawRows as $row ) {
+ $this->tableRows[] = $this->createRow( implode( '', $row['cells'] ), $row['attributes'] );
+ }
+
+ return $this->doConcatenatedHeader() . $this->doConcatenatedRows();
+ }
+
+ private function buildTransposedTable() {
+ $this->tableRows = [];
+
+ foreach( $this->rawHeaders as $hIndex => $header ) {
+ $cells = [];
+ $headerItem = $this->createHeader( $header['content'], $header['attributes'] );
+
+ foreach( $this->rawRows as $rIndex => $row ) {
+ $cells[] = $this->getTransposedCell( $hIndex, $row );
+ }
+
+ // Collect new rows
+ $this->tableRows[] = $this->createRow( $headerItem . implode( '', $cells ) );
+ }
+
+ return $this->doConcatenatedHeader() . $this->doConcatenatedRows();
+ }
+
+ private function getTransposedCell( $index, $row ) {
+
+ if ( isset( $row['cells'][$index] ) ) {
+ return $row['cells'][$index];
+ }
+
+ $attributes = [];
+
+ if ( isset( $row['attributes']['class'] ) && $row['attributes']['class'] === 'smwfooter' ) {
+ $attributes = [ 'class' => 'footer-cell' ];
+ }
+
+ return $this->createCell( '', $attributes );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTemplateRenderer.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTemplateRenderer.php
new file mode 100644
index 00000000..1f4c273b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/HtmlTemplateRenderer.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\MediaWiki\Renderer;
+
+use Parser;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class HtmlTemplateRenderer {
+
+ /**
+ * @var WikitextTemplateRenderer
+ */
+ private $wikitextTemplateRenderer;
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * @since 2.2
+ *
+ * @param WikitextTemplateRenderer $wikitextTemplateRenderer
+ * @param Parser $parser
+ */
+ public function __construct( WikitextTemplateRenderer $wikitextTemplateRenderer, Parser $parser ) {
+ $this->wikitextTemplateRenderer = $wikitextTemplateRenderer;
+ $this->parser = $parser;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $field
+ * @param mixed $value
+ */
+ public function addField( $field, $value ) {
+ $this->wikitextTemplateRenderer->addField( $field, $value );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $templateName
+ */
+ public function packFieldsForTemplate( $templateName ) {
+ $this->wikitextTemplateRenderer->packFieldsForTemplate( $templateName );
+ }
+
+ /**
+ * @since since 2.2
+ *
+ * @return string
+ */
+ public function render() {
+
+ $wikiText = $this->wikitextTemplateRenderer->render();
+
+ if ( $wikiText === '' ) {
+ return '';
+ }
+
+ return $this->parser->recursiveTagParse( $wikiText );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/WikitextTemplateRenderer.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/WikitextTemplateRenderer.php
new file mode 100644
index 00000000..3c6dfbcc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Renderer/WikitextTemplateRenderer.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\MediaWiki\Renderer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class WikitextTemplateRenderer {
+
+ /**
+ * @var array
+ */
+ private $fields = [];
+
+ /**
+ * @var string
+ */
+ private $template = '';
+
+ /**
+ * @since 2.2
+ *
+ * @param string $field
+ * @param mixed $value
+ */
+ public function addField( $field, $value ) {
+ $this->fields[$field] = $value;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $templateName
+ */
+ public function packFieldsForTemplate( $templateName ) {
+
+ $this->template .= '{{'. $templateName;
+
+ foreach ( $this->fields as $key => $value ) {
+ $this->template .= "\n|$key=$value";
+ }
+
+ $this->template .= '}}';
+ $this->fields = [];
+ }
+
+ /**
+ * @since since 2.2
+ *
+ * @return string
+ */
+ public function render() {
+ $wikiText = $this->template;
+ $this->template = '';
+ $this->fields = [];
+ return $wikiText;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/CustomForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/CustomForm.php
new file mode 100644
index 00000000..84bac0cd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/CustomForm.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use SMW\DIProperty;
+use Title;
+use WebRequest;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CustomForm {
+
+ /**
+ * @var WebRequest
+ */
+ private $request;
+
+ /**
+ * @var Field
+ */
+ private $field;
+
+ /**
+ * @var boolean
+ */
+ private $isActiveForm = false;
+
+ /**
+ * @var []
+ */
+ private $parameters = [];
+
+ /**
+ * @var []
+ */
+ private $fieldCounter = [];
+
+ /**
+ * @var []
+ */
+ private $html5TypeMap = [
+ '_txt' => 'text',
+ '_uri' => 'url',
+ '_dat' => 'date',
+ '_tel' => 'tel',
+ '_ema' => 'email',
+ '_num' => 'number'
+ ];
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ */
+ public function __construct( WebRequest $request ) {
+ $this->request = $request;
+ $this->field = new Field();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isActiveForm
+ */
+ public function isActiveForm( $isActiveForm ) {
+ $this->isActiveForm = (bool)$isActiveForm;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $definition
+ */
+ public function makeFields( $definition ) {
+
+ $fields = [];
+ $this->parameters = [];
+ $nameList = [];
+
+ foreach ( $definition as $property ) {
+ $options = [];
+
+ // Simple list, or does a property have some options?
+ if ( is_array( $property ) ) {
+ foreach ( $property as $p => $options ) {
+ $property = $p;
+ }
+ }
+
+ // Transforms (Foo bar -> foobar), better URL query conformity
+ $name = FormsBuilder::toLowerCase( $property );
+ $value = '';
+
+ // Field with the same name should only appear once in a form
+ if ( isset( $nameList[$name] ) ) {
+ continue;
+ }
+
+ // Each form definition may contain properties that are also defined
+ // in other forms therefore count its member position so that only
+ // values for the active form are fetched. The counter is used as
+ // positioning for the value array index.
+ if ( !isset( $this->fieldCounter[$name] ) ) {
+ $this->fieldCounter[$name] = 0;
+ } else {
+ $this->fieldCounter[$name]++;
+ }
+
+ // Find request related value for the active form
+ if ( $this->isActiveForm ) {
+ $vals = $this->request->getArray( $name );
+
+ $i = $this->fieldCounter[$name];
+ $value = isset( $vals[$i] ) ? $vals[$i] : $vals[0];
+ $this->parameters[$name] = $value;
+ }
+
+ $nameList[$name] = true;
+ $fields[] = $this->makeField( $name, $property, $value, $options );
+ }
+
+ return implode( '', $fields );
+ }
+
+ private function makeField( $name, $property, $value, $options ) {
+
+ $display = $this->isActiveForm ? 'inline-block' : 'none';
+ $options = !is_array( $options ) ? [] : $options;
+
+ if ( !isset( $options['placeholder'] ) ) {
+ $options['placeholder'] = "$property ...";
+ }
+
+ if ( !isset( $options['class'] ) ) {
+ $options['class'] = "";
+ }
+
+ if ( isset( $options['autocomplete'] ) && $options['autocomplete'] ) {
+ $options['class'] .= " smw-propertyvalue-input autocomplete-arrow";
+ }
+
+ if ( isset( $options['type'] ) ) {
+ $type = $options['type'];
+ } else {
+ $typeID = DIProperty::newFromUserLabel( $property )->findPropertyTypeID();
+ $type = 'text';
+
+ if ( isset( $this->html5TypeMap[$typeID] ) ) {
+ $type = $this->html5TypeMap[$typeID];
+ }
+ }
+
+ // Numeric names are not useful and may have been caused by an invalid
+ // JSON array/object definition
+ if ( is_numeric( $name ) ) {
+ $options[] = 'disabled';
+ }
+
+ $attributes = [
+ 'name' => $name,
+ 'value' => $value,
+ 'type' => $type,
+ 'display' => $display,
+ 'placeholder' => $options['placeholder'],
+ 'data-property' => $property,
+ 'title' => $property,
+ 'multifield' => true,
+ ] + $options;
+
+ return $this->field->create( 'input', $attributes );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/Field.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/Field.php
new file mode 100644
index 00000000..cf521f7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/Field.php
@@ -0,0 +1,242 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use SMW\Highlighter;
+use SMW\Message;
+use Title;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Field {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function create( $type, $attributes = [] ) {
+
+ $attributes['class'] = "smw-$type" . ( isset( $attributes['class'] ) ? ' ' . $attributes['class'] : '' );
+
+ if ( isset( $attributes['tooltip'] ) ) {
+ $attributes['tooltip'] = $this->tooltip( $attributes );
+ $attributes['class'] .= " smw-$type-tooltip";
+ }
+
+ if ( $type === 'input' ) {
+ return $this->input( $attributes );
+ }
+
+ if ( $type === 'select' ) {
+ return $this->select( $attributes );
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function tooltip( $attributes = [] ) {
+
+ $highlighter = Highlighter::factory( Highlighter::TYPE_NOTE );
+ $msg = '';
+
+ // Simple text, or is it message-key?
+ if ( isset( $attributes['tooltip'] ) && Message::exists( $attributes['tooltip'] ) ) {
+ $msg = Message::get( $attributes['tooltip'], Message::PARSE, Message::USER_LANGUAGE );
+ } elseif ( isset( $attributes['tooltip'] ) ) {
+ $msg = $attributes['tooltip'];
+ }
+
+ $highlighter->setContent(
+ [
+ 'content' => $msg,
+ 'style' => 'margin-left:10px;vertical-align:-1px;'
+ ]
+ );
+
+ return $highlighter->getHtml();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function select( $attributes = [] ) {
+
+ $list = [];
+ $html = [];
+ $selected = false;
+
+ if ( isset( $attributes['list'] ) ) {
+ $list = $attributes['list'];
+ unset( $attributes['list'] );
+ }
+
+ if ( isset( $attributes['selected'] ) ) {
+ $selected = $attributes['selected'];
+ unset( $attributes['selected'] );
+ }
+
+ foreach ( $list as $key => $value ) {
+
+ $opt = '';
+ $val = $value;
+
+ if ( is_array( $value ) ) {
+ $val = $value[0];
+ $opt = ' ' . $value[1];
+ }
+
+ if ( $selected === $key ) {
+ $opt = ' selected';
+ }
+
+ $html[] = "<option value='$key'$opt>$val</option>";
+ }
+
+ $style = '';
+ $name = '';
+ $label = '';
+ $class = '';
+
+ if ( isset( $attributes['class'] ) ) {
+ $class = $attributes['class'];
+ unset( $attributes['class'] );
+ }
+
+ if ( isset( $attributes['name'] ) ) {
+ $name = $attributes['name'];
+ }
+
+ if ( isset( $attributes['style'] ) ) {
+ $style .= $attributes['style'];
+ unset( $attributes['style'] );
+ }
+
+ if ( isset( $attributes['display'] ) ) {
+ $style = 'display:' . $attributes['display'] . ';';
+ unset( $attributes['display'] );
+ }
+
+ if ( isset( $attributes['multifield'] ) ) {
+ $name = $attributes['name'] . "[]";
+ unset( $attributes['multifield'] );
+ }
+
+ if ( isset( $attributes['label'] ) ) {
+ $label = "<label for='$name'>" . $attributes['label'] . "</label>";
+ unset( $attributes['label'] );
+ }
+
+ return $label . Html::rawElement(
+ 'select',
+ [
+ 'class' => $class,
+ 'name' => $name,
+ ] + ( $style !== '' ? [ 'style' => $style ] : [] ) + $attributes,
+ implode( '', $html )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function input( $attributes = [] ) {
+
+ $class = isset( $attributes['class'] ) ? $attributes['class'] : '';
+ $type = 'text';
+ $tooltip = '';
+ $required = false;
+ $placeholder = '';
+ $value = '';
+ $style = '';
+ $name = '';
+
+ if ( isset( $attributes['style'] ) ) {
+ $style .= $attributes['style'];
+ unset( $attributes['style'] );
+ }
+
+ if ( isset( $attributes['display'] ) ) {
+ $style = 'display:' . $attributes['display'] . ';';
+ unset( $attributes['display'] );
+ }
+
+ if ( isset( $attributes['name'] ) ) {
+ $name = $attributes['name'];
+ unset( $attributes['name'] );
+ }
+
+ if ( $name !== '' && isset( $attributes['multifield'] ) ) {
+ $name .= "[]";
+ unset( $attributes['multifield'] );
+ }
+
+ if ( isset( $attributes['required'] ) ) {
+ $required = (bool) $attributes['required'];
+ unset( $attributes['required'] );
+ }
+
+ if ( isset( $attributes['placeholder'] ) ) {
+ $placeholder = $attributes['placeholder'];
+ }
+
+ if ( isset( $attributes['tooltip'] ) ) {
+ $tooltip = $attributes['tooltip'];
+ unset( $attributes['tooltip'] );
+ }
+
+ if ( isset( $attributes['type'] ) ) {
+ $type = $attributes['type'];
+ }
+
+ if ( isset( $attributes['value'] ) ) {
+ $value = $attributes['value'];
+ }
+
+ $attr = [
+ 'class' => $class,
+ 'name' => $name,
+ 'type' => $type,
+ 'value' => $value,
+ 'placeholder' => $placeholder,
+ 'data-required' => $required
+ ] + $attributes;
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-input-field',
+ ] + ( $style !== '' ? [ 'style' => $style ] : [] ),
+ Html::rawElement(
+ 'input',
+ $attr
+ ) . $tooltip
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsBuilder.php
new file mode 100644
index 00000000..35ea47d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsBuilder.php
@@ -0,0 +1,358 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use RuntimeException;
+use SMW\Message;
+use Title;
+use WebRequest;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsBuilder {
+
+ /**
+ * @var WebRequest
+ */
+ private $request;
+
+ /**
+ * @var FormsFactory
+ */
+ private $formsFactory;
+
+ /**
+ * @var OpenForm
+ */
+ private $openForm;
+
+ /**
+ * @var CustomForm
+ */
+ private $customForm;
+
+ /**
+ * @var string
+ */
+ private $defaultForm = '';
+
+ /**
+ * @var []
+ */
+ private $formList = [];
+
+ /**
+ * @var []
+ */
+ private $preselectNsList = [];
+
+ /**
+ * @var []
+ */
+ private $hiddenNsList = [];
+
+ /**
+ * @var []
+ */
+ private $parameters = [];
+
+ /**
+ * @var []
+ */
+ private $termPrefixes = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ * @param FormsFactory $formsFactory
+ */
+ public function __construct( WebRequest $request, FormsFactory $formsFactory ) {
+ $this->request = $request;
+ $this->formsFactory = $formsFactory;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $form
+ *
+ * @return string
+ */
+ public static function toLowerCase( $key ) {
+ return strtolower( str_replace( [ ' ' ], [ '' ], $key ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getTermPrefixes() {
+ return $this->termPrefixes;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getHiddenNsList() {
+ return $this->hiddenNsList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getPreselectNsList() {
+
+ $activeForm = $this->request->getVal( 'smw-form', $this->defaultForm );
+
+ if ( $activeForm === null ) {
+ return [];
+ }
+
+ $activeForm = self::toLowerCase( $activeForm );
+
+ if ( isset( $this->preselectNsList[$activeForm] )) {
+ return $this->preselectNsList[$activeForm];
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function buildFormList() {
+
+ $list = [];
+ $name = '';
+ $value = '';
+
+ foreach ( $this->formList as $k => $options ) {
+
+ if ( $k === '' ) {
+ continue;
+ }
+
+ if ( $options['selected'] ) {
+ $name = $options['name'];
+ $value = $k;
+ }
+
+ $list[] = [ 'id' => $k, 'name' => $options['name'], 'desc' => $options['name'] ];
+ }
+
+ return Html::rawElement(
+ 'button',
+ [
+ 'type' => 'button',
+ 'id' => 'smw-search-forms',
+ 'class' => 'smw-selectmenu-button is-disabled',
+ 'title' => Message::get( 'smw-search-profile-extended-section-form', Message::TEXT, Message::USER_LANGUAGE ),
+ 'name' => 'smw-form',
+ 'value' => $value,
+ 'data-list' => json_encode( $list ),
+ 'data-nslist' => json_encode( $this->preselectNsList )
+ ],
+ $name === '' ? 'Form' : $name
+ ) . Html::rawElement(
+ 'input',
+ [
+ 'type' => 'hidden',
+ 'name' => 'smw-form',
+ 'value' => $value,
+ ]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $data
+ *
+ * @return string
+ */
+ public function buildForm( array $data ) {
+
+ if ( !isset( $data['forms'] ) ) {
+ throw new RuntimeException( "Missing forms definition" );
+ }
+
+ if ( isset( $data['default_form'] ) ) {
+ $this->defaultForm = self::toLowerCase( $data['default_form'] );
+ }
+
+ if ( isset( $data['term_parser']['prefix'] ) ) {
+ $this->termPrefixes = $data['term_parser']['prefix'];
+ }
+
+ $activeForm = $this->request->getVal( 'smw-form', $this->defaultForm );
+
+ $divider = "<div class='divider' style='display:none;'></div>";
+
+ if ( $activeForm !== null && $activeForm !== '' ) {
+ $divider = "<div class='divider'></div>";
+ }
+
+ $this->formList = [];
+ $this->preselectNsList = [];
+ $this->parameters = [];
+
+ if ( $activeForm === null || $activeForm === '' ) {
+ $forms = [ '' => '' ] + $data['forms'];
+ } else {
+ $forms = $data['forms'];
+ }
+
+ $formDefinitions = [];
+
+ if ( $this->openForm === null ) {
+ $this->openForm = $this->formsFactory->newOpenForm( $this->request );
+ }
+
+ if ( $this->customForm === null ) {
+ $this->customForm = $this->formsFactory->newCustomForm( $this->request );
+ }
+
+ ksort( $forms );
+
+ foreach ( $forms as $name => $definition ) {
+ $formDefinitions[] = $this->form_fields( $data, $activeForm, $name, $definition );
+ }
+
+ if ( isset( $data['namespaces']['preselect'] ) && is_array( $data['namespaces']['preselect'] ) ) {
+ $this->preselect_namespaces( $data['namespaces']['preselect'] );
+ }
+
+ if ( isset( $data['namespaces']['hidden'] ) && is_array( ) ) {
+ $this->hidden_namespaces( $data['namespaces']['hidden'] );
+ }
+
+ if ( isset( $data['namespaces']['hide'] ) && is_array( $data['namespaces']['hide'] ) ) {
+ $this->hidden_namespaces( $data['namespaces']['hide'] );
+ }
+
+ return $divider . Html::rawElement(
+ 'div',
+ [
+ 'id' => 'smw-form-definitions',
+ 'class' => 'is-disabled'
+ ],
+ implode( '', $formDefinitions )
+ );
+ }
+
+ private function form_fields( $data, $activeForm, $name, $definition ) {
+
+ // Short form, URL query conform
+ $s = self::toLowerCase( $name );
+ $this->formList[$s] = [ 'name' => $name, 'selected' => $activeForm === $s ];
+
+ if ( !is_array( $definition ) ) {
+ return;
+ }
+
+ $description = '';
+ $isActiveForm = $s === $activeForm;
+
+ if ( isset( $data['descriptions'] ) ) {
+ $description = $this->findDescription( $data['descriptions'], $name, $isActiveForm );
+ }
+
+ if ( $s === 'open' ) {
+ $this->openForm->isActiveForm( $isActiveForm );
+ $fields = $this->openForm->makeFields();
+ $this->parameters = array_merge( $this->parameters, $this->openForm->getParameters() );
+ } else {
+ $this->customForm->isActiveForm( $isActiveForm );
+ $fields = $this->customForm->makeFields( $definition );
+ $this->parameters = array_merge( $this->parameters, $this->customForm->getParameters() );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => "smw-form-{$s}",
+ 'class' => 'smw-fields'
+ ],
+ $description . $fields
+ );
+ }
+
+ private function preselect_namespaces( $preselect ) {
+ foreach ( $preselect as $k => $values ) {
+ $k = self::toLowerCase( $k );
+ $this->preselectNsList[$k] = [];
+
+ foreach ( $values as $ns ) {
+ if ( is_string( $ns ) && defined( $ns ) ) {
+ $this->preselectNsList[$k][] = constant( $ns );
+ }
+
+ if ( is_numeric( $ns ) ) {
+ $this->preselectNsList[$k][] = $ns;
+ }
+ }
+ }
+ }
+
+ private function hidden_namespaces( $hidden ) {
+ foreach ( $hidden as $ns ) {
+ if ( is_string( $ns ) && defined( $ns ) ) {
+ $this->hiddenNsList[] = constant( $ns );
+ }
+
+ if ( is_numeric( $ns ) ) {
+ $this->hiddenNsList[] = $ns;
+ }
+ }
+ }
+
+ private function findDescription( $descriptions, $name, $isActiveForm ) {
+
+ if ( !isset( $descriptions[$name] ) ) {
+ return '';
+ }
+
+ $display = $isActiveForm ? 'inline-block' : 'none';
+
+ // Simple text, or is it message-key?
+ if ( Message::exists( $descriptions[$name] ) ) {
+ $description = Message::get( $descriptions[$name], Message::PARSE, Message::USER_LANGUAGE );
+ } else{
+ $description = $descriptions[$name];
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-form-description',
+ 'style' => "display:$display;"
+ ],
+ $description
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFactory.php
new file mode 100644
index 00000000..f9c50f08
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFactory.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use WebRequest;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsFactory {
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ *
+ * @return OpenForm
+ */
+ public function newOpenForm( WebRequest $request ) {
+ return new OpenForm( $request );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ *
+ * @return CustomForm
+ */
+ public function newCustomForm( WebRequest $request ) {
+ return new CustomForm( $request );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ *
+ * @return SortForm
+ */
+ public function newSortForm( WebRequest $request ) {
+ return new SortForm( $request );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return NamespaceForm
+ */
+ public function newNamespaceForm() {
+ return new NamespaceForm();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFinder.php
new file mode 100644
index 00000000..6eda37e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/FormsFinder.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Search\SearchProfileForm;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMWDIBlob as DIBlob;
+use Title;
+use WikiPage;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getFormDefinitions() {
+
+ $data = [];
+ $requestOptions = new RequestOptions();
+ $requestOptions->setOption( 'DISTINCT', false );
+
+ $subjects = $this->store->getPropertySubjects(
+ new DIProperty( '_SCHEMA_TYPE' ),
+ new DIBlob( SearchProfileForm::SCHEMA_TYPE ),
+ $requestOptions
+ );
+
+ foreach ( $subjects as $subject ) {
+
+ if ( ( $nativeData = $this->getNativeData( $subject->getTitle() ) ) === '' ) {
+ continue;
+ }
+
+ $d = json_decode( $nativeData, true );
+
+ if ( json_last_error() !== JSON_ERROR_NONE ) {
+ continue;
+ }
+
+ $data = array_merge_recursive( $data, $d );
+ }
+
+ return $data;
+ }
+
+ protected function getNativeData( $title ) {
+
+ if ( $title === null ) {
+ return '';
+ }
+
+ $content = WikiPage::factory( $title )->getContent();
+
+ if ( $content === null ) {
+ return '';
+ }
+
+ return $content->getNativeData();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/NamespaceForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/NamespaceForm.php
new file mode 100644
index 00000000..571dda8a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/NamespaceForm.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use MWNamespace;
+use SMW\Message;
+use SpecialSearch;
+use Xml;
+
+/**
+ * @note Copied from SearchFormWidget::powerSearchBox, #3126 contains the reason
+ * why we need to copy the code!
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NamespaceForm {
+
+ /**
+ * @var []
+ */
+ private $activeNamespaces = [];
+
+ /**
+ * @var []
+ */
+ private $hiddenNamespaces = [];
+
+ /**
+ * @var []
+ */
+ private $searchableNamespaces = [];
+
+ /**
+ * @var null|string
+ */
+ private $token;
+
+ /**
+ * @var null|string
+ */
+ private $hideList = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param array $activeNamespaces
+ */
+ public function setActiveNamespaces( array $activeNamespaces ) {
+ $this->activeNamespaces = $activeNamespaces;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $hideList
+ */
+ public function setHideList( $hideList ) {
+ $this->hideList = (bool)$hideList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $hiddenNamespaces
+ */
+ public function setHiddenNamespaces( array $hiddenNamespaces ) {
+ $this->hiddenNamespaces = $hiddenNamespaces;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $searchableNamespaces
+ */
+ public function setSearchableNamespaces( array $searchableNamespaces ) {
+ $this->searchableNamespaces = $searchableNamespaces;
+ }
+
+ /**
+ * @see SearchFormWidget
+ *
+ * @since 3.0
+ *
+ * @param SpecialSearch $specialSearch
+ */
+ public function checkNamespaceEditToken( SpecialSearch $specialSearch ) {
+
+ $user = $specialSearch->getUser();
+
+ if ( !$user->isLoggedIn() ) {
+ return;
+ }
+
+ $this->token = $user->getEditToken( 'searchnamespace', $specialSearch->getRequest() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function makeFields() {
+ global $wgContLang;
+
+ $divider = "<div class='divider'></div>";
+ $rows = [];
+ $tableRows = [];
+
+ $hiddenNamespaces = array_flip( $this->hiddenNamespaces );
+
+ foreach ( $this->searchableNamespaces as $namespace => $name ) {
+ $subject = MWNamespace::getSubject( $namespace );
+
+ if ( MWNamespace::isTalk( $namespace ) ) {
+ // continue;
+ }
+
+ if ( isset( $hiddenNamespaces[$namespace] ) ) {
+ continue;
+ }
+
+ if ( !isset( $rows[$subject] ) ) {
+ $rows[$subject] = "";
+ }
+
+ $name = $wgContLang->getConverter()->convertNamespace( $namespace );
+
+ if ( $name === '' ) {
+ $name = Message::get( 'blanknamespace', Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ $isChecked = in_array( $namespace, $this->activeNamespaces );
+
+ $rows[$subject] .= Html::rawElement(
+ 'td',
+ [],
+ Xml::checkLabel( $name, "ns{$namespace}", "mw-search-ns{$namespace}", $isChecked )
+ );
+ }
+
+ // Lays out namespaces in multiple floating two-column tables so they'll
+ // be arranged nicely while still accomodating diferent screen widths
+ foreach ( $rows as $row ) {
+ $tableRows[] = "<tr>{$row}</tr>";
+ }
+
+ $namespaceTables = [];
+ $display = $this->hideList ? 'none' : 'block';
+
+ foreach ( array_chunk( $tableRows, 4 ) as $chunk ) {
+ $namespaceTables[] = implode( '', $chunk );
+ }
+
+ $showSections = [
+ 'namespaceTables' => "<table>" . implode( '</table><table>', $namespaceTables ) . '</table>',
+ ];
+
+ // Stuff to feed SpecialSearch::saveNamespaces()
+ $remember = '';
+
+ if ( $this->token ) {
+ $remember = $divider . Xml::checkLabel(
+ Message::get( 'powersearch-remember', Message::TEXT, Message::USER_LANGUAGE ),
+ 'nsRemember',
+ 'mw-search-powersearch-remember',
+ false,
+ // The token goes here rather than in a hidden field so it
+ // is only sent when necessary (not every form submission)
+ [ 'value' => $this->token ]
+ );
+ }
+
+ return "<fieldset id='mw-searchoptions'>" .
+ "<legend>" . Message::get( 'powersearch-legend', Message::ESCAPED, Message::USER_LANGUAGE ) . '</legend>' .
+ "<h4>" . Message::get( 'powersearch-ns', Message::PARSE, Message::USER_LANGUAGE ) . '</h4>' .
+ // populated by js if available
+ "<div id='smw-search-togglensview'></div>" .
+ "<div id='mw-search-togglebox'></div>" .
+ "<div id='mw-search-ns' style='display:$display'>" . $divider .
+ implode(
+ $divider,
+ $showSections
+ ) .
+ $remember . "</div>" .
+ "</fieldset>";
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/OpenForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/OpenForm.php
new file mode 100644
index 00000000..10a14f2b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/OpenForm.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use Title;
+use WebRequest;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class OpenForm {
+
+ /**
+ * @var WebRequest
+ */
+ private $request;
+
+ /**
+ * @var Field
+ */
+ private $field;
+
+ /**
+ * @var boolean
+ */
+ private $isActiveForm = false;
+
+ /**
+ * @var []
+ */
+ private $parameters = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ */
+ public function __construct( WebRequest $request ) {
+ $this->request = $request;
+ $this->field = new Field();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isActiveForm
+ */
+ public function isActiveForm( $isActiveForm ) {
+ $this->isActiveForm = (bool)$isActiveForm;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $definition
+ */
+ public function makeFields( $definition = [] ) {
+
+ $this->parameters = [];
+
+ $group = '';
+ $properties = [];
+ $values = [];
+ $op = [];
+
+ if ( $this->isActiveForm ) {
+ $properties = $this->request->getArray( 'property', [] );
+ $values = $this->request->getArray( 'pvalue', [] );
+ $op = $this->request->getArray( 'op', [] );
+ }
+
+ $this->parameters = [
+ 'property' => [],
+ 'pvalue' => [],
+ 'op' => []
+ ];
+
+ foreach ( $properties as $i => $property ) {
+
+ if ( $property === '' ) {
+ continue;
+ }
+
+ $this->parameters['property'][] = $property;
+ $this->parameters['pvalue'][] = $values[$i];
+ $this->parameters['op'][] = $op[$i];
+
+ $group .= $this->makeFieldGroup( $property, $values[$i], $op[$i] );
+ }
+
+ // At least one empty group
+ $group .= $this->makeFieldGroup( '', '', '' );
+
+ return $group;
+ }
+
+ private function makeFieldGroup( $property, $value, $op ) {
+
+ $display = $this->isActiveForm ? 'inline-block' : 'none';
+
+ $attributes = [
+ 'multifield' => true,
+ 'display' => $display,
+ 'name' => 'property',
+ 'value' => $property,
+ 'data-autocomplete-indicator' => true,
+ 'placeholder' => 'Property ...',
+ 'class' => 'smw-property-input autocomplete-arrow'
+ ];
+
+ $prop = $this->field->create( 'input', $attributes );
+
+ $attributes = [
+ 'multifield' => true,
+ 'display' => $display,
+ 'name' => 'pvalue',
+ 'value' => $value,
+ 'data-autocomplete-indicator' => true,
+ 'data-property' => $property,
+ 'placeholder' => 'Value ...',
+ 'class' => 'smw-propertyvalue-input autocomplete-arrow'
+ ];
+
+ if ( $value === '' ) {
+ $attributes['class'] .= ' is-disabled';
+ }
+
+ $pvalue = $this->field->create( 'input', $attributes );
+ $disabled = $property === '' && $value === '' ? 'disabled' : '';
+
+ $list = [
+ '' => '',
+ 'OR' => 'OR',
+ ' ' => [ '————', 'disabled' ],
+ 'del' => [ 'del', $disabled ]
+ ];
+
+ $attributes = [
+ 'list' => $list,
+ 'selected' => $op,
+ 'multifield' => true,
+ 'name' => 'op',
+ 'display' => $display,
+ 'class' => 'smw-select-field'
+ ];
+
+ $select = $this->field->create( 'select', $attributes );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-input-group'
+ ],
+ $prop . $pvalue . $select
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/SortForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/SortForm.php
new file mode 100644
index 00000000..820cc1e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Form/SortForm.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\MediaWiki\Search\Form;
+
+use Html;
+use WebRequest;
+use SMW\Message;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortForm {
+
+ /**
+ * @var WebRequest
+ */
+ private $request;
+
+ /**
+ * @var Field
+ */
+ private $field;
+
+ /**
+ * @var []
+ */
+ private $parameters = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ */
+ public function __construct( WebRequest $request ) {
+ $this->request = $request;
+ $this->field = new Field();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getParameters() {
+ return $this->parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $features
+ */
+ public function makeFields( $features = [] ) {
+
+ $default = isset( $features['best'] ) && $features['best'] ? 'best' : 'title';
+ $sort = $this->request->getVal( 'sort', $default );
+
+ $this->parameters['sort'] = $sort;
+
+ $list = [];
+ $name = '';
+
+ foreach ( $this->sortList( $features ) as $key => $value ) {
+
+ if ( $key === $sort ) {
+ $name = $value;
+ }
+
+ $list[] = [ 'id' => $key, 'name' => $value, 'desc' => $value ];
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-search-sort'
+ ],
+ Html::rawElement(
+ 'button',
+ [
+ 'type' => 'button',
+ 'id' => 'smw-search-sort',
+ 'class' => 'smw-selectmenu-button is-disabled',
+ 'name' => 'sort',
+ 'value' => $sort,
+ 'data-list' => json_encode( $list ),
+ 'title' => Message::get( 'smw-search-profile-extended-section-sort', Message::TEXT, Message::USER_LANGUAGE ),
+ ],
+ $sort === '' ? 'Sort' : $name
+ ) . Html::rawElement(
+ 'input',
+ [
+ 'type' => 'hidden',
+ 'name' => 'sort',
+ 'value' => $sort,
+ ]
+ )
+ );
+ }
+
+ private function sortList( $features ) {
+
+ $list = [];
+
+ if ( isset( $features['best'] ) && $features['best'] ) {
+ $list['best'] = Message::get( 'smw-search-profile-sort-best', Message::TEXT, Message::USER_LANGUAGE );
+
+ $list += [
+ 'recent' => Message::get( 'smw-search-profile-sort-recent', Message::TEXT, Message::USER_LANGUAGE ),
+ 'title' => Message::get( 'smw-search-profile-sort-title', Message::TEXT, Message::USER_LANGUAGE )
+ ];
+
+ } else{
+ $list = [
+ 'title' => Message::get( 'smw-search-profile-sort-title', Message::TEXT, Message::USER_LANGUAGE ),
+ 'recent' => Message::get( 'smw-search-profile-sort-recent', Message::TEXT, Message::USER_LANGUAGE )
+ ];
+ }
+
+ return $list;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/QueryBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/QueryBuilder.php
new file mode 100644
index 00000000..34b2a238
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/QueryBuilder.php
@@ -0,0 +1,290 @@
+<?php
+
+namespace SMW\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\Form\FormsBuilder;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Parser\TermParser;
+use SMW\Store;
+use SMWQuery as Query;
+use SMWQueryProcessor as QueryProcessor;
+use Title;
+use WebRequest;
+use WikiPage;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryBuilder {
+
+ /**
+ * @var WebRequest
+ */
+ private $request;
+
+ /**
+ * @var array
+ */
+ private $data = [];
+
+ /**
+ * @var array
+ */
+ private $queryCache = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest|null $request
+ * @param array|null $data
+ */
+ public function __construct( WebRequest $request = null, array $data = [] ) {
+ $this->request = $request;
+ $this->data = $data;
+
+ if ( $this->request === null ) {
+ $this->request = $GLOBALS['wgRequest'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $term
+ *
+ * @return Query|null
+ */
+ public function getQuery( $term ) {
+
+ if ( !is_string( $term ) || trim( $term ) === '' ) {
+ return null;
+ }
+
+ if ( !array_key_exists( $term, $this->queryCache ) ) {
+
+ $params = QueryProcessor::getProcessedParams( [] );
+ $query = QueryProcessor::createQuery( $term, $params );
+
+ $description = $query->getDescription();
+
+ if ( $description === null || is_a( $description, 'SMWThingDescription' ) ) {
+ $query = null;
+ }
+
+ $this->queryCache[$term] = $query;
+ }
+
+ return $this->queryCache[$term];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ * @param array $searchableNamespaces
+ */
+ public function addNamespaceCondition( Query $query = null, $searchableNamespaces = [] ) {
+
+ if ( $query === null ) {
+ return;
+ }
+
+ $namespaces = [];
+
+ foreach ( $searchableNamespaces as $ns => $name ) {
+ if ( $this->request->getCheck( 'ns' . $ns ) ) {
+ $namespaces[] = $ns;
+ }
+ }
+
+ $namespacesDisjunction = new Disjunction(
+ array_map( function ( $ns ) {
+ return new NamespaceDescription( $ns );
+ }, $namespaces )
+ );
+
+ $description = new Conjunction( [ $query->getDescription(), $namespacesDisjunction ] );
+ $query->setDescription( $description );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ */
+ public function addSort( Query $query = null ) {
+
+ if ( $query === null ) {
+ return;
+ }
+
+ // @see SortForm
+ $sort = $this->request->getVal( 'sort' );
+
+ if ( $sort === 'recent' ) {
+ $query->setSortKeys( [ '_MDAT' => 'desc' ] );
+ } elseif ( $sort === 'title' ) {
+ $query->setSortKeys( [ '' => 'asc' ] );
+ } else {
+ // Sort by score/relevance if it is supported otherwise the default
+ // by title sort will be used instead.
+ $query->setOption( Query::SCORE_SORT, 'desc' );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getQueryString( Store $store, $term ) {
+
+ // Special invisible char which is set by the JS component to allow to
+ // push a forms submit through the SearchEngine without an actual "search
+ // term" to avoid being blocked on an empty request which only contains
+ // structured searches.
+ $term = rtrim( $term, " " );
+ $prefix_map = [];
+
+ if ( $this->data === [] ) {
+ $data = SearchProfileForm::getFormDefinitions( $store );
+ } else {
+ $data = $this->data;
+ }
+
+ if ( isset( $data['term_parser']['prefix'] ) && $data['term_parser']['prefix'] ) {
+ $prefix_map = (array)$data['term_parser']['prefix'];
+ }
+
+ $termParser = new TermParser( $prefix_map );
+ $term = $termParser->parse( $term );
+
+ $form = $this->request->getVal( 'smw-form' );
+
+ if ( ( $data = $this->fetchFieldValues( $form, $data ) ) === [] && trim( $term ) ) {
+ return $term;
+ }
+
+ $queryString = '';
+ $lastOr = '';
+
+ foreach ( $data as $key => $values ) {
+
+ if ( !is_array( $values ) ) {
+ continue;
+ }
+
+ foreach ( $values as $k => $value ) {
+
+ if ( !isset( $value[0] ) || $value[0] === '' ) {
+ continue;
+ }
+
+ $val = $value[0];
+ $op = strtolower( $value[1] ) === 'or' ? ' OR ' : '';
+
+ $queryString .= "[[$key::$val]]$op";
+ }
+ }
+
+ // Remove last OR to ensure <q></q> has no open OR expression
+ if ( substr( $queryString, -3 ) === 'OR ' ) {
+ $lastOr = $term !== '' ? 'OR' : '';
+ $queryString = substr( $queryString, 0, -3 );
+ }
+
+ if ( $queryString === '' ) {
+ return $term;
+ }
+
+ return "<q>$queryString</q> $lastOr $term";
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $form
+ * @param array $data
+ *
+ * @return []
+ */
+ public function fetchFieldValues( $form, array $data ) {
+
+ $fieldValues = [];
+
+ if ( !isset( $data['forms'] ) ) {
+ return [];
+ }
+
+ if ( $form === 'open' ) {
+ $properties = $this->request->getArray( 'property' );
+ $pvalues = $this->request->getArray( 'pvalue' );
+ $op = $this->request->getArray( 'op' );
+
+ foreach ( $properties as $i => $property ) {
+
+ if ( !isset( $fieldValues[$property] ) ) {
+ $fieldValues[$property] = [];
+ }
+
+ $fieldValues[$property][] = [ $pvalues[$i], $op[$i] ];
+ }
+
+ return $fieldValues;
+ }
+
+ $fieldsCounter = [];
+
+ foreach ( $data['forms'] as $key => $value ) {
+
+ // @see FormsBuilder
+ $k = FormsBuilder::toLowerCase( $key );
+
+ foreach ( $value as $property ) {
+
+ if ( is_array( $property ) ) {
+ foreach ( $property as $p => $options ) {
+ $property = $p;
+ }
+ }
+
+ $name = FormsBuilder::toLowerCase( $property );
+
+ if ( !isset( $fieldsCounter[$name] ) ) {
+ $fieldsCounter[$name] = 0;
+ } else {
+ $fieldsCounter[$name]++;
+ }
+
+ if ( $form !== $k ) {
+ continue;
+ }
+
+ $vals = $this->request->getArray(
+ FormsBuilder::toLowerCase( $property )
+ );
+
+ if ( !isset( $vals[$fieldsCounter[$name]] ) ) {
+ continue;
+ }
+
+ $val = $vals[$fieldsCounter[$name]];
+
+ // Conditions from custom forms are conjunctive
+ $fieldValues[$property][] = [ $val, 'and' ];
+ }
+ }
+
+ return $fieldValues;
+ }
+
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/README.md b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/README.md
new file mode 100644
index 00000000..93dadca8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/README.md
@@ -0,0 +1,161 @@
+# SMWSearch
+
+[SMWSearch](https://www.semantic-mediawiki.org/wiki/Help:SMWSearch) is the `SearchEngine` interface to provide classes and functions to integrate Semantic MediaWiki with `Special:Search`.
+
+It adds support for using #ask queries in the search input field and provides an extended search profile where user defined forms can empower users to find and match entities using property and value input fields.
+
+## Extended search profile
+
+![image](https://user-images.githubusercontent.com/1245473/43684698-7748fd76-9894-11e8-971f-3125892dc9ed.png)
+
+### Defining forms and form fields
+
+Form definitions are expected be created in the [rule namespace](https://www.semantic-mediawiki.org/wiki/Help:Rule) with the [`SEARCH_FORM_DEFINITION_RULE`](https://www.semantic-mediawiki.org/wiki/Help:Rule/Type/SEARCH_FORM_DEFINITION_RULE) as mandatory type to declare field constraints and validation checkpoints (see the `$smwgRuleTypes` setting) required by the type.
+
+Definitions are structured using the JSON format and in the following example represent:
+
+- `"type"` requires `SEARCH_FORM_DEFINITION_RULE`
+- `"forms"` defines a collection of forms
+ - `"Books and journals"` as title of a form
+ - `"Has title"` is a simple input field without any constraints
+ - `"Publication type"` is a input field with additional attributes
+
+<pre>
+{
+ "type": "SEARCH_FORM_DEFINITION_RULE",
+ "forms": {
+ "Books and journals": [
+ "Has title",
+ "Has author",
+ "Has year",
+ {
+ "Publication type": {
+ "autocomplete": true,
+ "tooltip": "Some context to be shown ...",
+ "required": true
+ }
+ },
+ {
+ "Publisher": {
+ "autocomplete": true
+ "tooltip": "message-can-be-a-msg-key"
+ }
+ }
+ ],
+ "Media and files": [ ]
+ }
+}
+</pre>
+
+| Attributes | Values | Description |
+|--------------|-----------------|-----------------------------------|
+| autocomplete | true, false | whether the field should add an autocomplete function or not |
+| tooltip | text or msg key | shows a tooltip with either a text or retrieves information from a message key |
+| placeholder | text | shown instead of the property name |
+| required | true, false | whether the field input is required before submitting or not |
+| type | HTML5 | preselect a specific type field |
+
+`default_form` can define a default form that is displayed when no other form was preselected.
+
+<pre>
+{
+ "type": "SEARCH_FORM_DEFINITION_RULE",
+ "default_form": "Books and journals",
+ ...
+}
+</pre>
+
+### Term parser
+
+The `term_parser` prefix can be used to shorten the input cycle and summarize frequent properties so that a user can write:
+ - `(in:foobar || phrase:foo bar) lang:fr` instead of
+ - `<q>[[in:foobar]] || [[phrase:foo bar]]</q><q>[[Language code::fr]] OR [[Document language::fr]] OR [[File attachment.Content language::fr]] OR [[Has interlanguage link.Page content language::fr]]</q>`
+
+<pre>
+{
+ "type": "SEARCH_FORM_DEFINITION_RULE",
+ "term_parser": {
+ "prefix": {
+ "lang": [
+ "Language code",
+ "Document language",
+ "File attachment.Content language",
+ "Has interlanguage link.Page content language"
+ ]
+ }
+ }
+}
+</pre>
+
+Prefixes are only applicable (and usable as means the shorten the search term) from within the extended search form.
+
+### Namespaces
+
+- ` "namespaces"`
+ - `default_hide` hides the namespace box by default on the extended profile form
+ - `"hide"` identify namespaces that should be hidden from appearing in any SMW related form
+ - `"preselect"` assign a pre-selection of namespaces to a specific form
+ - `"Books and journals"` specific form the pre-selection should be enacted
+
+<pre>
+{
+ "type": "SEARCH_FORM_DEFINITION_RULE",
+ "namespaces": {
+ "default_hide": true,
+ "hide": [
+ "NS_PROJECT",
+ "NS_PROJECT_TALK"
+ ],
+ "preselect": {
+ "Books and journals": [
+ "NS_CUSTOM_BOOKS",
+ "NS_FILE"
+ ],
+ "Media and files": [
+ "NS_FILE"
+ ]
+ }
+ }
+}
+</pre>
+
+
+### Descriptions
+
+Describes a form and is shown at the top of the form fields to inform users about the intent of
+the form.
+
+<pre>
+{
+ "descriptions": {
+ "Books and journals": "Short description to be shown on top of a selected form"
+ }
+}
+</pre>
+
+## Technical notes
+
+### Search engine
+
+Classes that provide an interface to support MW's `SearchEngine` by transforming a search term into a SMW equivalent expression of query elements.
+
+<pre>
+SMW\MediaWiki\Search
+┃
+â” â” QueryBuilder
+â” â” Search # Implements the `SearchEngine`
+â” â” SearchResult # Individual result representation
+┕┠SearchResultSet # Contains a set of results return from the `QueryEngine`
+</pre>
+
+### Search profile and form builder
+
+Classes that provide an additional search form to support structured searches in `Special:Search` with the help of the [`SpecialSearchProfileForm`](https://www.mediawiki.org/wiki/Manual:Hooks/SpecialSearchProfileForm) hook.
+
+<pre>
+SMW\MediaWiki\Search
+┃ ┃
+┃ ┕┠Form # Classes to generate a HTML from a JSON definition
+┃
+┕┠SearchProfileForm # Interface to the `SpecialSearchProfileForm` hook
+</pre>
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Search.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Search.php
new file mode 100644
index 00000000..5e18e7eb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/Search.php
@@ -0,0 +1,474 @@
+<?php
+
+namespace SMW\MediaWiki\Search;
+
+use Content;
+use DatabaseBase;
+use RuntimeException;
+use SearchEngine;
+use SMW\ApplicationFactory;
+use SMWQuery;
+use SMWQueryResult as QueryResult;
+use Title;
+
+/**
+ * Search engine that will try to find wiki pages by interpreting the search
+ * term as an SMW query.
+ *
+ * If successful, the pages according to the query will be returned.
+ * If not it falls back to the default search engine.
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Stephan Gambke
+ */
+class Search extends SearchEngine {
+
+ private $fallbackSearch = null;
+
+ private $database = null;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var QueryBuilder
+ */
+ private $queryBuilder;
+
+ /**
+ * @var string
+ */
+ private $queryString = '';
+
+ /**
+ * @var InfoLink
+ */
+ private $queryLink;
+
+ /**
+ * @see SearchEngine::getValidSorts
+ *
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getValidSorts() {
+ return [
+
+ // SemanticMediaWiki supported
+ 'title', 'recent', 'best',
+
+ // MediaWiki default
+ 'relevance'
+ ];
+ }
+
+ /**
+ * @param null|SearchEngine $fallbackSearch
+ */
+ public function setFallbackSearchEngine( SearchEngine $fallbackSearch = null ) {
+ $this->fallbackSearch = $fallbackSearch;
+ }
+
+ /**
+ * @param $type
+ */
+ private function assertValidFallbackSearchEngineType( $type ) {
+
+ if ( !class_exists( $type ) ) {
+ throw new RuntimeException( "$type does not exist." );
+ }
+
+ if ( $type === 'SMWSearch' ) {
+ throw new RuntimeException( 'SMWSearch is not a valid fallback search engine type.' );
+ }
+
+ if ( $type !== 'SearchEngine' && !is_subclass_of( $type, 'SearchEngine' ) ) {
+ throw new RuntimeException( "$type is not a valid fallback search engine type." );
+ }
+ }
+
+ /**
+ * @return SearchEngine
+ */
+ public function getFallbackSearchEngine() {
+
+ if ( $this->fallbackSearch === null ) {
+
+ $type = ApplicationFactory::getInstance()->getSettings()->get( 'smwgFallbackSearchType' );
+
+ $dbr = $this->getDB();
+
+ if ( $type === null ) {
+ $type = ApplicationFactory::getInstance()->create( 'DefaultSearchEngineTypeForDB', $dbr );
+ }
+
+ $this->assertValidFallbackSearchEngineType( $type );
+
+ $this->fallbackSearch = new $type( $dbr );
+ }
+
+ return $this->fallbackSearch;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getQueryString() {
+ return $this->queryString;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getQueryLink() {
+ return $this->queryLink;
+ }
+
+ /**
+ * @param DatabaseBase $connection
+ */
+ public function setDB( DatabaseBase $connection ) {
+ $this->database = $connection;
+ $this->fallbackSearch = null;
+ }
+
+ /**
+ * @return \IDatabase
+ */
+ public function getDB() {
+
+ if ( $this->database === null ) {
+ $this->database = ApplicationFactory::getInstance()->getLoadBalancer()->getConnection( defined( 'DB_REPLICA' ) ? DB_REPLICA : DB_SLAVE );
+ }
+
+ return $this->database;
+ }
+
+ /**
+ * @param String $term
+ *
+ * @return SMWQuery | null
+ */
+ private function getSearchQuery( $term ) {
+
+ if ( $this->queryBuilder === null ) {
+ $this->queryBuilder = new QueryBuilder();
+ }
+
+ $this->queryString = $this->queryBuilder->getQueryString(
+ ApplicationFactory::getInstance()->getStore(),
+ $term
+ );
+
+ $query = $this->queryBuilder->getQuery(
+ $this->queryString
+ );
+
+ $this->queryBuilder->addSort( $query );
+
+ $this->queryBuilder->addNamespaceCondition(
+ $query,
+ $this->searchableNamespaces()
+ );
+
+ return $query;
+ }
+
+ private function searchFallbackSearchEngine( $term, $fulltext ) {
+
+ $f = $this->getFallbackSearchEngine();
+ $f->prefix = $this->prefix;
+ $f->namespaces = $this->namespaces;
+
+ $term = $f->transformSearchTerm( $term );
+ $term = $f->replacePrefixes( $term );
+
+ return $fulltext ? $f->searchText( $term ) : $f->searchTitle( $term );
+ }
+
+ /**
+ * Perform a title-only search query and return a result set.
+ *
+ * This method will try to find wiki pages by interpreting the search term as an SMW query.
+ *
+ * If successful, the pages according to the query will be returned.
+ * If not, it falls back to the default search engine.
+ *
+ * @param string $term Raw search term
+ *
+ * @return SearchResultSet|null
+ */
+ public function searchTitle( $term ) {
+
+ if ( $this->getSearchQuery( $term ) !== null ) {
+ return null;
+ }
+
+ return $this->searchFallbackSearchEngine( $term, false );
+ }
+
+ /**
+ * Perform a full text search query and return a result set.
+ * If title searches are not supported or disabled, return null.
+ *
+ * @param string $term Raw search term
+ *
+ * @return SearchResultSet|\Status|null
+ */
+ public function searchText( $term ) {
+
+ if ( $this->getSearchQuery( $term ) !== null ) {
+ return $this->newSearchResultSet( $term );
+ }
+
+ return $this->searchFallbackSearchEngine( $term, true );
+ }
+
+ /**
+ * @see SearchEngine::completionSearchBackend
+ *
+ * Perform a completion search.
+ *
+ * @param string $search
+ *
+ * @return SearchSuggestionSet
+ */
+ protected function completionSearchBackend( $search ) {
+
+ $searchResultSet = null;
+
+ // Avoid MW's auto formatting of title entities
+ if ( $search !== '' ) {
+ $search{0} = strtolower( $search{0} );
+ }
+
+ $searchEngine = $this->getFallbackSearchEngine();
+
+ if ( !$this->hasPrefixAndMinLenForCompletionSearch( $search, 3 ) ) {
+ return $searchEngine->completionSearch( $search );
+ }
+
+ if ( $this->getSearchQuery( $search ) !== null ) {
+ $searchResultSet = $this->newSearchResultSet( $search, false, false );
+ }
+
+ if ( $searchResultSet instanceof SearchResultSet ) {
+ return $searchResultSet->newSearchSuggestionSet();
+ }
+
+ return $searchEngine->completionSearch( $search );
+ }
+
+ private function hasPrefixAndMinLenForCompletionSearch( $term, $minLen ) {
+
+ // Only act on when `in:foo`, `has:SomeProperty`, or `phrase:some text`
+ // is actively used as prefix
+
+ if ( strpos( $term, 'in:' ) !== false && mb_strlen( $term ) >= ( 3 + $minLen ) ) {
+ return true;
+ }
+
+ if ( strpos( $term, 'has:' ) !== false && mb_strlen( $term ) >= ( 4 + $minLen ) ) {
+ return true;
+ }
+
+ if ( strpos( $term, 'phrase:' ) !== false && mb_strlen( $term ) >= ( 7 + $minLen ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function newSearchResultSet( $term, $count = true, $highlight = true ) {
+
+ $query = $this->getSearchQuery( $term );
+
+ if ( $query === null ) {
+ return null;
+ }
+
+ $query->setOffset( $this->offset );
+ $query->setLimit( $this->limit, false );
+ $this->queryString = $query->getQueryString();
+
+ $store = ApplicationFactory::getInstance()->getStore();
+ $query->clearErrors();
+ $query->setOption( 'highlight.fragment', $highlight );
+
+ $result = $store->getQueryResult( $query );
+ $this->errors = $query->getErrors();
+ $this->queryLink = $result->getQueryLink();
+ $this->queryLink->setParameter( $this->offset, 'offset' );
+ $this->queryLink->setParameter( $this->limit, 'limit' );
+
+ if ( $count ) {
+ $query->querymode = SMWQuery::MODE_COUNT;
+ $query->setOffset( 0 );
+
+ $queryResult = $store->getQueryResult( $query );
+ $count = $queryResult instanceof QueryResult ? $queryResult->getCountValue() : $queryResult;
+ } else {
+ $count = 0;
+ }
+
+ return new SearchResultSet( $result, $count );
+ }
+
+ /**
+ * @param string $feature
+ *
+ * @return bool
+ */
+ public function supports( $feature ) {
+ return $this->getFallbackSearchEngine()->supports( $feature );
+ }
+
+ /**
+ * May performs database-specific conversions on text to be used for
+ * searching or updating search index.
+ *
+ * @param string $string String to process
+ *
+ * @return string
+ */
+ public function normalizeText( $string ) {
+ return $this->getFallbackSearchEngine()->normalizeText( $string );
+ }
+
+ public function getTextFromContent( Title $t, Content $c = null ) {
+ return $this->getFallbackSearchEngine()->getTextFromContent( $t, $c );
+ }
+
+ public function textAlreadyUpdatedForIndex() {
+ return $this->getFallbackSearchEngine()->textAlreadyUpdatedForIndex();
+ }
+
+ /**
+ * Create or update the search index record for the given page.
+ * Title and text should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ * @param string $text
+ */
+ public function update( $id, $title, $text ) {
+ $this->getFallbackSearchEngine()->update( $id, $title, $text );
+ }
+
+ /**
+ * Update a search index record's title only.
+ * Title should be pre-processed.
+ *
+ * @param int $id
+ * @param string $title
+ */
+ public function updateTitle( $id, $title ) {
+ $this->getFallbackSearchEngine()->updateTitle( $id, $title );
+ }
+
+ /**
+ * Delete an indexed page
+ * Title should be pre-processed.
+ *
+ * @param int $id Page id that was deleted
+ * @param string $title Title of page that was deleted
+ */
+ public function delete( $id, $title ) {
+ $this->getFallbackSearchEngine()->delete( $id, $title );
+ }
+
+ public function setFeatureData( $feature, $data ) {
+ parent::setFeatureData( $feature, $data );
+ $this->getFallbackSearchEngine()->setFeatureData( $feature, $data );
+ }
+
+ /**
+ * @param String $feature
+ *
+ * @return array|null
+ */
+ public function getFeatureData( $feature ) {
+
+ if ( array_key_exists( $feature, $this->features ) ) {
+ return $this->features[$feature];
+ }
+
+ return null;
+ }
+
+ /**
+ * SMW queries do not have prefixes. Returns query as is.
+ *
+ * @param string $query
+ *
+ * @return string
+ */
+ public function replacePrefixes( $query ) {
+ return $query;
+ }
+
+ /**
+ * No Transformation needed. Returns term as is.
+ * @param $term
+ * @return mixed
+ */
+ public function transformSearchTerm( $term ) {
+ return $term;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLimit() {
+ return $this->limit;
+ }
+
+ /**
+ * @return int
+ */
+ public function getOffset() {
+ return $this->offset;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function getShowSuggestion() {
+ return $this->showSuggestion;
+ }
+
+ public function setLimitOffset( $limit, $offset = 0 ) {
+ parent::setLimitOffset( $limit, $offset );
+ $this->getFallbackSearchEngine()->setLimitOffset( $limit, $offset );
+ }
+
+ public function setNamespaces( $namespaces ) {
+ parent::setNamespaces( $namespaces );
+ $this->getFallbackSearchEngine()->setNamespaces( $namespaces );
+ }
+
+ public function setShowSuggestion( $showSuggestion ) {
+ parent::setShowSuggestion( $showSuggestion );
+ $this->getFallbackSearchEngine()->setShowSuggestion( $showSuggestion );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchProfileForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchProfileForm.php
new file mode 100644
index 00000000..1f6ebce9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchProfileForm.php
@@ -0,0 +1,433 @@
+<?php
+
+namespace SMW\MediaWiki\Search;
+
+use Html;
+use MWNamespace;
+use SMW;
+use SMW\MediaWiki\Search\Form\FormsBuilder;
+use SMW\MediaWiki\Search\Form\FormsFactory;
+use SMW\MediaWiki\Search\Form\FormsFinder;
+use SMW\ProcessingErrorMsgHandler;
+use SMW\Utils\HtmlModal;
+use SMW\Store;
+use SMW\Message;
+use SpecialSearch;
+use Title;
+use WikiPage;
+use Xml;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SearchProfileForm {
+
+ const PROFILE_NAME = 'smw';
+
+ /**
+ * Page that hosts the form/forms definition
+ */
+ const SCHEMA_TYPE = 'SEARCH_FORM_SCHEMA';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SpecialSearch
+ */
+ private $specialSearch;
+
+ /**
+ * @var FormsFactory
+ */
+ private $formsFactory;
+
+ /**
+ * @var []
+ */
+ private $searchableNamespaces = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param SpecialSearch $specialSearch
+ */
+ public function __construct( Store $store, SpecialSearch $specialSearch ) {
+ $this->store = $store;
+ $this->specialSearch = $specialSearch;
+ $this->formsFactory = new FormsFactory();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array &$profiles
+ */
+ public static function addProfile( $type, array &$profiles ) {
+
+ if ( $type !== 'SMWSearch' ) {
+ return;
+ }
+
+ $profiles[self::PROFILE_NAME] = [
+ 'message' => 'smw-search-profile',
+ 'tooltip' => 'smw-search-profile-tooltip',
+ 'namespaces' => \SearchEngine::defaultNamespaces()
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ *
+ * @return array
+ */
+ public static function getFormDefinitions( Store $store ) {
+
+ static $data = null;
+
+ if ( $data !== null ) {
+ return $data;
+ }
+
+ $formsFinder = new FormsFinder( $store );
+ $data = $formsFinder->getFormDefinitions();
+
+ return $data;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $searchableNamespaces
+ */
+ public function setSearchableNamespaces( array $searchableNamespaces ) {
+ $this->searchableNamespaces = $searchableNamespaces;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string &$form
+ * @param array $opts
+ */
+ public function getForm( &$form, array $opts = [] ) {
+
+ $hidden = '';
+ $html = '';
+
+ $context = $this->specialSearch->getContext();
+ $request = $context->getRequest();
+
+ foreach ( $opts as $key => $value ) {
+ $hidden .= Html::hidden( $key, $value );
+ }
+
+ $outputPage = $context->getOutput();
+
+ $outputPage->addModuleStyles( [ 'smw.ui.styles', 'smw.special.search.styles' ] );
+ $outputPage->addModules(
+ [
+ 'smw.ui',
+ 'smw.special.search',
+ 'ext.smw.tooltip',
+ 'ext.smw.autocomplete.property'
+ ]
+ );
+
+ // Set active form
+ $this->specialSearch->setExtraParam( 'smw-form', $request->getVal( 'smw-form' ) );
+
+ $searchEngine = $this->specialSearch->getSearchEngine();
+
+ if ( ( $queryLink = $searchEngine->getQueryLink() ) instanceof \SMWInfolink ) {
+ $queryLink->setCaption( $this->msg( 'smw-search-profile-link-caption-query', Message::TEXT ) );
+ $queryLink->setLinkAttributes(
+ [
+ 'title' => 'Special:Ask'
+ ]
+ );
+ }
+
+ list( $searchForms, $formList, $termPrefixes, $preselectNamespaces, $hiddenNamespaces ) = $this->buildSearchForms(
+ $request
+ );
+
+ $sortForm = $this->buildSortForm( $request );
+
+ $namespaceForm = $this->buildNamespaceForm(
+ $request,
+ $searchEngine,
+ $preselectNamespaces,
+ $hiddenNamespaces,
+ $hidden
+ );
+
+ $options = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-search-options'
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'style' => 'color: #586069;position: relative;display: inline-block; padding-top: 5px; padding-bottom: 2px;'
+ ],
+ ''
+ ) . $sortForm . $formList . HtmlModal::link(
+ '<span class="smw-icon-info"></span>',
+ [
+ 'data-id' => 'smw-search-profile-extended-cheat-sheet'
+ ]
+ )
+ );
+
+ $errors = $this->findErrors( $searchEngine );
+
+ $modal = HtmlModal::modal(
+ Message::get( 'smw-cheat-sheet', Message::TEXT, Message::USER_LANGUAGE ),
+ $this->profile_sheet( $searchEngine->getQueryString(), $queryLink, $termPrefixes ),
+ [
+ 'id' => 'smw-search-profile-extended-cheat-sheet',
+ 'class' => 'plainlinks',
+ 'style' => 'display:none;'
+ ]
+ );
+
+ $form .= Html::rawElement(
+ 'fieldset',
+ [
+ 'id' => 'smw-searchoptions'
+ ],
+ $hidden . $errors . $modal . $options . $searchForms
+ );
+
+ // Different fieldset therefore it is used as last element
+ $form .= $namespaceForm;
+ }
+
+ private function buildNamespaceForm( $request, $searchEngine, $preselectNamespaces, $hiddenNamespaces, &$hidden ) {
+
+ $activeNamespaces = array_merge( $this->specialSearch->getNamespaces(), $preselectNamespaces );
+ $default = false;
+
+ $data = $this->getFormDefinitions( $this->store );
+
+ foreach ( $this->searchableNamespaces as $ns => $name ) {
+
+ if ( $request->getCheck( 'ns' . $ns ) ) {
+ $activeNamespaces[] = $ns;
+ $this->specialSearch->setExtraParam( 'ns' . $ns, true );
+ }
+ }
+
+ if ( $searchEngine !== null ) {
+ $searchEngine->setNamespaces( $activeNamespaces );
+ }
+
+ // Contains the copied Advanced namespace form
+ $namespaceForm = $this->formsFactory->newNamespaceForm();
+
+ $namespaceForm->setActiveNamespaces(
+ $activeNamespaces
+ );
+
+ $namespaceForm->setHiddenNamespaces(
+ $hiddenNamespaces
+ );
+
+ if ( isset( $data['namespaces']['default_hide'] ) ) {
+ $default = $data['namespaces']['default_hide'];
+ }
+
+ $namespaceForm->setHideList(
+ $request->getVal( 'ns-list', $default )
+ );
+
+ $namespaceForm->setSearchableNamespaces(
+ $this->searchableNamespaces
+ );
+
+ $namespaceForm->checkNamespaceEditToken(
+ $this->specialSearch
+ );
+
+ // Carry over the status (hide/show) of the ns section during a search
+ // request so we don't have to set a cookie while still being able to
+ // retain its status on whether the users has the NS hidden or not.
+ $hidden .= Html::hidden( 'ns-list', $request->getVal( 'ns-list', $default ) );
+
+ return $namespaceForm->makeFields();
+ }
+
+ private function buildSearchForms( $request ) {
+
+ $data = $this->getFormDefinitions( $this->store );
+
+ if ( $data === [] ) {
+ return [ '', '', [], [], [] ];
+ }
+
+ $formsBuilder = new FormsBuilder( $request, $this->formsFactory );
+
+ $form = $formsBuilder->buildForm( $data );
+ $parameters = $formsBuilder->getParameters();
+
+ // Set parameters so that any link to a (... 20, 50 ...) list carries
+ // those parameters, using them as hidden elements is not sufficient
+ foreach ( $parameters as $key => $value ) {
+ $this->specialSearch->setExtraParam( $key, $value );
+ }
+
+ $formList = $formsBuilder->buildFormList();
+
+ return [
+ $form,
+ $formList,
+ $formsBuilder->getTermPrefixes(),
+ $formsBuilder->getPreselectNsList(),
+ $formsBuilder->getHiddenNsList()
+ ];
+ }
+
+ private function findErrors( $searchEngine ) {
+
+ if ( ( $errors = $searchEngine->getErrors() ) === [] ) {
+ return '';
+ }
+
+ $divider = "<div class='divider'></div>";
+
+ $list = ProcessingErrorMsgHandler::normalizeAndDecodeMessages(
+ $errors
+ );
+
+ return Html::rawElement(
+ 'ul',
+ [
+ 'class' => 'smw-errors',
+ 'style' => 'color:#b32424;'
+ ],
+ '<li>' . implode( '</li><li>', $list ) . '</li>'
+ ) . $divider;
+ }
+
+ private function buildSortForm( $request ) {
+
+ $sortForm = $this->formsFactory->newSortForm( $request );
+
+ // TODO this information should come from the store and not being
+ // derived from a class! How should such characteristic be represented?
+ $features = [
+ 'best' => is_a( $this->store, "SMWElasticStore" )
+ ];
+
+ $form = $sortForm->makeFields( $features );
+ $parameters = $sortForm->getParameters();
+
+ foreach ( $parameters as $key => $value ) {
+ $this->specialSearch->setExtraParam( $key, $value );
+ }
+
+ return $form;
+ }
+
+ private function profile_sheet( $query, $queryLink, $termPrefixes ) {
+
+ $text = Message::get( 'smw-search-profile-extended-help-intro', Message::PARSE, Message::USER_LANGUAGE );
+
+ $link = $queryLink !== null ? $queryLink->getHtml() : '';
+
+ if ( $link !== '' ) {
+ $text .= $this->section( 'smw-search-profile-extended-section-query' );
+ $text .= $this->msg( [ 'smw-search-profile-extended-help-query', trim( $query ) ] ) . '&nbsp;';
+ $text .= $this->msg( [ 'smw-search-profile-extended-help-query-link', $link ], Message::TEXT );
+ }
+
+ $text .= $this->section( 'smw-search-profile-extended-section-search-syntax' );
+ $text .= $this->msg( 'smw-search-profile-extended-help-search-syntax', Message::TEXT );
+
+ $syntax = $this->msg( 'smw-search-profile-extended-help-search-syntax-simplified-in' );
+ $syntax .= $this->msg( 'smw-search-profile-extended-help-search-syntax-simplified-phrase' );
+ $syntax .= $this->msg( 'smw-search-profile-extended-help-search-syntax-simplified-has' );
+ $syntax .= $this->msg( 'smw-search-profile-extended-help-search-syntax-simplified-not' );
+
+ if ( $termPrefixes !== [] ) {
+ $prefixes = '';
+
+ foreach ( array_keys( $termPrefixes ) as $pref ) {
+ $prefixes .= ( $prefixes === '' ? '' : ', ' ) . "<code>$pref:</code>";
+ }
+
+ $syntax .= $this->msg( [ 'smw-search-profile-extended-help-search-syntax-prefix', $prefixes ] );
+ }
+
+ $syntax .= $this->msg( [ 'smw-search-profile-extended-help-search-syntax-reserved', "'&&', 'AND', '||', 'OR', '(', ')', '[[', ']]'" ] );
+
+ $text .= Html::rawElement( 'div', [ 'id' => 'smw-search-synatx-list' ],
+ $syntax
+ );
+
+ $text .= Html::rawElement( 'p', [] ,
+ $this->msg( 'smw-search-profile-extended-help-search-syntax-note' )
+ );
+
+ $text .= $this->section( 'smw-search-profile-extended-section-sort' );
+ $text .= $this->msg( 'smw-search-profile-extended-help-sort' );
+ $sort = $this->msg( 'smw-search-profile-extended-help-sort-title' );
+ $sort .= $this->msg( 'smw-search-profile-extended-help-sort-recent' );
+
+ if ( is_a( $this->store, "SMWElasticStore" ) ) {
+ $sort .= $this->msg( 'smw-search-profile-extended-help-sort-best' );
+ }
+
+ $text .= Html::rawElement( 'div', [ 'id' => 'smw-search-sort-list' ],
+ $sort
+ );
+
+ $formLink = Html::element(
+ 'a',
+ [
+ 'href' => Title::newFromText( 'Special:SearchByProperty/Schema type/' . self::SCHEMA_TYPE )->getFullUrl()
+ ],
+ $this->msg( 'smw-search-profile-extended-help-find-forms' )
+ );
+
+ $text .= $this->section( 'smw-search-profile-extended-section-form' );
+ $text .= $this->msg( [ 'smw-search-profile-extended-help-form', $formLink ], Message::TEXT );
+ $text .= $this->section( 'smw-search-profile-extended-section-namespace' );
+ $text .= $this->msg( 'smw-search-profile-extended-help-namespace' );
+
+ return $text;
+ }
+
+ private function section( $msg, $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-text-strike',
+ 'style' => 'padding: 5px 0 5px 0;'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'style' => 'font-size: 1.2em; margin-left:0px'
+ ],
+ Message::get( $msg, Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+ }
+
+ private function msg( $msg, $type = Message::PARSE, $lang = Message::USER_LANGUAGE ) {
+ return Message::get( $msg, $type, $lang );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResult.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResult.php
new file mode 100644
index 00000000..28f650d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResult.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\MediaWiki\Search;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+
+/**
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SearchResult extends \SearchResult {
+
+ /**
+ * @var boolean
+ */
+ private $hasHighlight = false;
+
+ /**
+ * @see SearchResult::getTextSnippet
+ */
+ function getTextSnippet( $terms ) {
+
+ if ( $this->hasHighlight ) {
+ return str_replace( [ '<em>', '</em>' ], [ "<span class='searchmatch'>", '</span>' ], $this->mText );
+ }
+
+ return parent::getTextSnippet( $terms );
+ }
+
+ /**
+ * @see SearchResult::getSectionTitle
+ */
+ function getSectionTitle() {
+
+ if ( !isset( $this->mTitle ) || $this->mTitle->getFragment() === '' ) {
+ return null;
+ }
+
+ return $this->mTitle;
+ }
+
+ /**
+ * Set a text excerpt retrieved from a different back-end.
+ *
+ * @param string $text|null
+ * @param boolean $hasHighlight
+ */
+ public function setExcerpt( $text = null, $hasHighlight = false ) {
+ $this->mText = $text;
+ $this->hasHighlight = $hasHighlight;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getExcerpt() {
+ return $this->mText;
+ }
+
+ /**
+ * @see SearchResult::getTitleSnippet
+ */
+ public function getTitleSnippet() {
+
+ if ( !isset( $this->mTitle ) ) {
+ return '';
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ DIWikiPage::newFromTitle( $this->mTitle )
+ );
+
+ // Will return the DISPLAYTITLE, if available
+ return $dataValue->getPreferredCaption();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResultSet.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResultSet.php
new file mode 100644
index 00000000..6843891b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Search/SearchResultSet.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\MediaWiki\Search;
+
+use SMW\DIWikiPage;
+use SMW\Utils\CharExaminer;
+
+/**
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Stephan Gambke
+ */
+class SearchResultSet extends \SearchResultSet {
+
+ /**
+ * @var DIWikiPage[]|[]
+ */
+ private $pages;
+
+ /**
+ * @var QueryToken
+ */
+ private $queryToken;
+
+ /**
+ * @var Excerpts
+ */
+ private $excerpts;
+
+ private $count = null;
+
+ public function __construct( \SMWQueryResult $result, $count = null ) {
+ $this->pages = $result->getResults();
+ $this->queryToken = $result->getQuery()->getQueryToken();
+ $this->excerpts = $result->getExcerpts();
+ $this->count = $count;
+ }
+
+ /**
+ * Return number of rows included in this result set.
+ *
+ * @return int|void
+ */
+ public function numRows() {
+ return count( $this->pages );
+ }
+
+ /**
+ * Return true if results are included in this result set.
+ *
+ * @return bool
+ */
+ public function hasResults() {
+ return $this->numRows() > 0;
+ }
+
+ /**
+ * Fetches next search result, or false.
+ *
+ * @return SearchResult
+ */
+ public function next() {
+
+ $page = current( $this->pages );
+ $searchResult = false;
+
+ if ( $page instanceof DIWikiPage ) {
+ $searchResult = SearchResult::newFromTitle( $page->getTitle() );
+ }
+
+ // Attempt to use excerpts available from a different back-end
+ if ( $searchResult && $this->excerpts !== null ) {
+ if ( ( $excerpt = $this->excerpts->getExcerpt( $page ) ) !== false ) {
+ $searchResult->setExcerpt( $excerpt, $this->excerpts->hasHighlight() );
+ }
+ }
+
+ next( $this->pages );
+
+ return $searchResult;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return SearchSuggestionSet
+ */
+ public function newSearchSuggestionSet() {
+
+ $suggestions = [];
+ $hasMoreResults = false;
+ $score = count( $this->pages );
+
+ foreach ( $this->pages as $page ) {
+ if ( ( $title = $page->getTitle() ) && $title->exists() ) {
+ $suggestions[] = \SearchSuggestion::fromTitle( $score--, $title );
+ }
+ }
+
+ return new \SearchSuggestionSet( $suggestions, $hasMoreResults );
+ }
+
+ /**
+ * @see SearchResultSet::extractResults
+ *
+ * @since 3.0
+ */
+ public function extractResults() {
+
+ // #3204
+ // https://github.com/wikimedia/mediawiki/commit/720fdfa7901cbba93b5695ed5f00f982272ced27
+ //
+ // MW 1.32+:
+ // - Remove SearchResultSet::next, SearchResultSet::numRows
+ // - Move QueryResult::getResults, QueryResult::getExcerpts into this
+ // method to avoid constructor work
+
+ if ( $this->pages === [] ) {
+ return $this->results = [];
+ }
+
+ foreach ( $this->pages as $page ) {
+
+ if ( $page instanceof DIWikiPage ) {
+ $searchResult = SearchResult::newFromTitle( $page->getTitle() );
+ }
+
+ // Attempt to use excerpts available from a different back-end
+ if ( $searchResult && $this->excerpts !== null ) {
+ if ( ( $excerpt = $this->excerpts->getExcerpt( $page ) ) !== false ) {
+ $searchResult->setExcerpt( $excerpt, $this->excerpts->hasHighlight() );
+ }
+ }
+
+ $this->results[] = $searchResult;
+ }
+
+ return $this->results;
+ }
+
+ /**
+ * Returns true, so Special:Search won't offer the user a link to a create
+ * a page named by the search string because the name would contain the
+ * search syntax, i.e. the SMW query.
+ *
+ * @return bool
+ */
+ public function searchContainedSyntax() {
+ return true;
+ }
+
+ public function getTotalHits() {
+ return $this->count;
+ }
+
+ /**
+ * Return an array of regular expression fragments for matching
+ * the search terms as parsed by the engine in a text extract.
+ *
+ * This is a temporary hack for MW versions that can not cope
+ * with no search term being returned (<1.24).
+ *
+ * @deprecated remove once min supported MW version has \SearchHighlighter::highlightNone()
+ *
+ * @return string[]
+ */
+ public function termMatches() {
+
+ if ( ( $tokens = $this->getTokens() ) !== [] ) {
+ return $tokens;
+ }
+
+ if ( method_exists( '\SearchHighlighter', 'highlightNone' ) ) {
+ return [];
+ }
+
+ // Will cause the highlighter to match every line start, thus returning the first few lines of found pages.
+ return [ '^' ];
+ }
+
+ private function getTokens() {
+
+ $tokens = [];
+
+ if ( $this->queryToken === null ) {
+ return $tokens;
+ }
+
+ // Use tokens gathered from a query context [[in:Foo]] (~~*Foo*), a filter context
+ // such as [[Category:Foo]] is not considered eligible to provide a
+ // token.
+ foreach ( $this->queryToken->getTokens() as $key => $value ) {
+ // Avoid add \b boundary checks for CJK where whitespace is not used
+ // as word break
+ $tokens[] = CharExaminer::isCJK( $key ) ? "$key" : "\b$key\b";
+ }
+
+ return $tokens;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandler.php
new file mode 100644
index 00000000..b5b4a0e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandler.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\Message;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CacheStatisticsListTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @since 3.0
+ *
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( OutputFormatter $outputFormatter ) {
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'stats/cache';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-operational-statistics-cache-title' ),
+ [ 'action' => 'stats/cache' ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-operational-statistics-cache-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle(
+ $this->msg( 'smw-admin-supplementary-operational-statistics-cache-title' )
+ );
+
+ $this->outputFormatter->addParentLink(
+ [ 'action' => 'stats' ],
+ 'smw-admin-supplementary-operational-statistics-title'
+ );
+
+ $this->outputQueryCacheStatistics();
+ }
+
+ private function outputQueryCacheStatistics() {
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h2', [], $this->msg( 'smw-admin-statistics-querycache-title' ) )
+ );
+
+ $cachedQueryResultPrefetcher = ApplicationFactory::getInstance()->singleton( 'CachedQueryResultPrefetcher' );
+
+ if ( !$cachedQueryResultPrefetcher->isEnabled() ) {
+ $msg = $this->msg(
+ [ 'smw-admin-statistics-querycache-disabled' ],
+ Message::PARSE
+ );
+
+ return $this->outputFormatter->addHTML(
+ Html::rawElement( 'p', [], $msg )
+ );
+ }
+
+ $msg = $this->msg(
+ [ 'smw-admin-statistics-querycache-explain' ],
+ Message::PARSE
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement( 'p', [], $msg )
+ );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson( $cachedQueryResultPrefetcher->getStats() )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/ConfigurationListTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/ConfigurationListTaskHandler.php
new file mode 100644
index 00000000..d0321697
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/ConfigurationListTaskHandler.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\Message;
+use SMW\NamespaceManager;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ConfigurationListTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @since 2.5
+ *
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( OutputFormatter $outputFormatter ) {
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'settings';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-settings-title' ),
+ [
+ 'action' => 'settings'
+ ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-settings-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle(
+ $this->msg( 'smw-admin-supplementary-settings-title' )
+ );
+
+ $this->outputFormatter->addParentLink(
+ [ 'tab' => 'supplement' ]
+ );
+
+ $this->outputFormatter->addHtml(
+ Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $this->msg( 'smw-admin-settings-docu', Message::PARSE )
+ )
+ );
+
+ $options = ApplicationFactory::getInstance()->getSettings()->toArray();
+
+ $this->outputFormatter->addAsPreformattedText(
+ str_replace( '\\\\', '\\', $this->outputFormatter->encodeAsJson( $this->cleanPath( $options ) ) )
+ );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson(
+ [
+ 'canonicalNames' => NamespaceManager::getCanonicalNames()
+ ]
+ )
+ );
+ }
+
+ private function cleanPath( array &$options ) {
+
+ foreach ( $options as $key => &$value ) {
+ if ( is_array( $value ) ) {
+ $this->cleanPath( $value );
+ }
+
+ if ( is_string( $value ) && strpos( $value , 'SemanticMediaWiki/') !== false ) {
+ $value = preg_replace('/[\s\S]+?SemanticMediaWiki/', '.../SemanticMediaWiki', $value );
+ }
+ }
+
+ return $options;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DataRefreshJobTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DataRefreshJobTaskHandler.php
new file mode 100644
index 00000000..82d059cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DataRefreshJobTaskHandler.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use Title;
+use WebRequest;
+use SMW\MediaWiki\Job;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataRefreshJobTaskHandler extends TaskHandler {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var null|Job
+ */
+ private $refreshjob = null;
+
+ /**
+ * @since 2.5
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_DATAREPAIR;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'refreshstore';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $this->htmlFormRenderer
+ ->addHeader( 'h4', $this->msg( 'smw_smwadmin_datarefresh' ) )
+ ->addParagraph( $this->msg( 'smw_smwadmin_datarefreshdocu' ) );
+
+ if ( !$this->isEnabledFeature( SMW_ADM_REFRESH ) ) {
+ $this->htmlFormRenderer->addParagraph( $this->msg( 'smw-admin-feature-disabled' ) );
+ } elseif ( $this->getRefreshJob() !== null ) {
+ var_dump('expression');
+ $this->htmlFormRenderer
+ ->setMethod( 'post' )
+ ->addHiddenField( 'action', 'refreshstore' )
+ ->addParagraph( $this->msg( 'smw_smwadmin_datarefreshprogress' ) )
+ ->addParagraph( $this->getProgressBar( $this->getRefreshJob()->getProgress() ) )
+ ->addLineBreak()
+ ->addSubmitButton(
+ $this->msg( 'smw_smwadmin_datarefreshstop' ),
+ [
+ 'class' => ''
+ ]
+ )
+ ->addCheckbox(
+ $this->msg( 'smw_smwadmin_datarefreshstopconfirm' ),
+ 'rfsure',
+ 'stop'
+ );
+ } elseif ( $this->getRefreshJob() === null ) {
+ $this->htmlFormRenderer
+ ->setMethod( 'post' )
+ ->addHiddenField( 'action', 'refreshstore' )
+ ->addHiddenField( 'rfsure', 'yes' )
+ ->addSubmitButton(
+ $this->msg( 'smw_smwadmin_datarefreshbutton' ),
+ [
+ 'class' => ''
+ ]
+ );
+ }
+
+ return Html::rawElement( 'div', [], $this->htmlFormRenderer->getForm() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_REFRESH ) ) {
+ return '';
+ }
+
+ $sure = $webRequest->getText( 'rfsure' );
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ if ( $sure == 'yes' ) {
+ $refreshjob = $this->getRefreshJob();
+
+ if ( $refreshjob === null ) { // careful, there might be race conditions here
+
+ $newjob = $applicationFactory->newJobFactory()->newByType(
+ 'SMW\RefreshJob',
+ \SpecialPage::getTitleFor( 'SMWAdmin' ),
+ [ 'spos' => 1, 'prog' => 0, 'rc' => 2 ]
+ );
+
+ $newjob->insert();
+ }
+
+ } elseif ( $sure == 'stop' ) {
+ $jobQueue = $applicationFactory->getJobQueue();
+ $jobQueue->disableCache();
+ $jobQueue->delete( 'SMW\RefreshJob' );
+ }
+
+ $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ private function getProgressBar( $prog ) {
+ return Html::rawElement(
+ 'div',
+ [ 'style' => 'float: left; background: #DDDDDD; border: 1px solid grey; width: 300px;' ],
+ Html::rawElement( 'div', [ 'style' => 'background: #AAF; width: ' . round( $prog * 300 ) . 'px; height: 20px; ' ], '' )
+ ) . '&#160;' . round( $prog * 100, 4 ) . '%';
+ }
+
+ private function getRefreshJob() {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_REFRESH ) ) {
+ return null;
+ }
+
+ if ( $this->refreshjob !== null ) {
+ return $this->refreshjob;
+ }
+
+ $jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+
+ if ( !$jobQueue->hasPendingJob( 'SMW\RefreshJob' ) ) {
+ return null;
+ }
+
+ // Pop and acknowledge the job to fetch progress details
+ // from itself
+ $refreshJob = $jobQueue->pop( 'SMW\RefreshJob' );
+
+ if ( $refreshJob instanceof Job ) {
+ $refreshJob->run();
+ $jobQueue->ack( $refreshJob );
+ $this->refreshjob = $refreshJob;
+ }
+
+ return $this->refreshjob;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandler.php
new file mode 100644
index 00000000..7842b9c4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandler.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\Message;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DeprecationNoticeTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var array
+ */
+ private $deprecationNoticeList = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param OutputFormatter $outputFormatter
+ * @param array $deprecationNoticeList
+ */
+ public function __construct( OutputFormatter $outputFormatter, array $deprecationNoticeList = [] ) {
+ $this->outputFormatter = $outputFormatter;
+ $this->deprecationNoticeList = $deprecationNoticeList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_DEPRECATION;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $html = '';
+
+ // Push `smw` to the top
+ uksort( $this->deprecationNoticeList, function( $a, $b ) {
+ return $b === 'smw';
+ } );
+
+ foreach ( $this->deprecationNoticeList as $section => $deprecationNoticeList ) {
+ $html .= $this->buildSection( $section, $deprecationNoticeList );
+ }
+
+ if ( $html === '' ) {
+ return '';
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-admin-deprecation'
+ ],
+ Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $this->msg( 'smw-admin-deprecation-notice-docu' )
+ ) . $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {}
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {}
+
+ private function buildSection( $section, $deprecationNoticeList ) {
+
+ $noticeConfigList = [];
+ $replacementConfigList = [];
+ $removedConfigList = [];
+ $html = '';
+
+ if ( isset( $deprecationNoticeList['notice'] ) ) {
+ $noticeConfigList = $deprecationNoticeList['notice'];
+ }
+
+ if ( isset( $deprecationNoticeList['replacement'] ) ) {
+ $replacementConfigList = $deprecationNoticeList['replacement'];
+ }
+
+ if ( isset( $deprecationNoticeList['removal'] ) ) {
+ $removedConfigList = $deprecationNoticeList['removal'];
+ }
+
+ $sectionList = $this->build_list(
+ $section,
+ $noticeConfigList,
+ $replacementConfigList,
+ $removedConfigList
+ );
+
+ if ( $sectionList === [] ) {
+ return '';
+ }
+
+ if ( $section !== 'smw' ) {
+ $html .= Html::rawElement(
+ 'h2',
+ [
+ 'class' => "$section-admin-deprecation-notice-section"
+ ],
+ $this->msg( "$section-admin-deprecation-notice-section" )
+ );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => "$section-admin-deprecation-section"
+ ],
+ $html . implode( '', $sectionList )
+ );
+ }
+
+ private function build_list( $section, $noticeConfigList, $replacementConfigList, $removedConfigList ) {
+
+ $noticeList = [];
+ $list = [];
+
+ // Replacements
+ foreach ( $replacementConfigList as $setting => $value ) {
+ if ( $setting === 'options' ) {
+ $list[] = $this->createListItems( "$section-admin-deprecation-notice-config-replacement", $value );
+ } elseif ( isset( $GLOBALS[$setting] ) ) {
+ $list[] = $this->createListItem( [ "$section-admin-deprecation-notice-config-replacement", '$' . $setting, '$' . $value ] );
+ }
+ }
+
+ if ( $list !== [] && ( $mList = $this->mergeList( "$section-admin-deprecation-notice-title-replacement", $section, $list ) ) !== null ) {
+ $noticeList[] = $mList;
+ }
+
+ // Changes
+ foreach ( $noticeConfigList as $setting => $value ) {
+ if ( $setting === 'options' ) {
+ $list[] = $this->createListItems( "$section-admin-deprecation-notice-config-notice", $value );
+ } elseif ( isset( $GLOBALS[$setting] ) ) {
+ $list[] = $this->createListItem( [ "$section-admin-deprecation-notice-config-notice", '$' . $setting, $value ] );
+ }
+ }
+
+ if ( $list !== [] && ( $mList = $this->mergeList( "$section-admin-deprecation-notice-title-notice", $section, $list ) ) !== null ) {
+ $noticeList[] = $mList;
+ }
+
+ // Removals
+ foreach ( $removedConfigList as $setting => $msg ) {
+ if ( isset( $GLOBALS[$setting] ) ) {
+ $list[] = $this->createListItem( [ "$section-admin-deprecation-notice-config-removal", '$' . $setting, $msg ] );
+ }
+ }
+
+ if ( $list !== [] && ( $mList = $this->mergeList( "$section-admin-deprecation-notice-title-removal", $section, $list ) ) !== null ) {
+ $noticeList[] = $mList;
+ }
+
+ return $noticeList;
+ }
+
+ private function mergeList( $title, $section, &$list ) {
+
+ if ( $list === [] || ( $items = implode( '', $list ) ) === '' ) {
+ return;
+ }
+
+ $html = Html::rawElement(
+ 'h3',
+ [],
+ $this->msg( $title )
+ ) . Html::rawElement(
+ 'p',
+ [
+ 'class' => "$section-admin-deprecation-notice-section-explanation",
+ 'style' => 'margin-bottom:10px;'
+ ],
+ $this->msg( $title . '-explanation' )
+ ) . Html::rawElement(
+ 'ul',
+ [
+ 'style' => 'margin-bottom:10px;'
+ ],
+ $items
+ );
+
+ $list = [];
+
+ return $html;
+ }
+
+ private function createListItem( $message ) {
+ return Html::rawElement( 'li', [], $this->msg( $message, Message::PARSE ) );
+ }
+
+ private function createListItems( $message, $values ) {
+
+ $list = [];
+
+ if ( !is_array( $values ) ) {
+ return '';
+ }
+
+ foreach ( $values as $setting => $options ) {
+
+ if ( !is_array( $options ) ) {
+ continue;
+ }
+
+ $opt = [];
+
+ foreach ( $options as $option => $v ) {
+ if ( $this->hasOption( $setting, $option ) ) {
+ $opt[] = $this->createListItem(
+ [
+ $message . '-option-list',
+ $option,
+ $v
+ ]
+ );
+ }
+ }
+
+ if ( $opt !== [] ) {
+ $list[] = $this->createListItem(
+ [
+ $message . '-option',
+ '$' . $setting,
+ count( $opt )
+ ]
+ ) . '<ul>' . implode( '', $opt ) . '</ul>';
+ }
+ }
+
+ return implode( '', $list );
+ }
+
+ private function hasOption( $setting, $option ) {
+ return isset( $GLOBALS[$setting][$option] ) || ( is_array( $GLOBALS[$setting] ) && array_search( $option, $GLOBALS[$setting] ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DisposeJobTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DisposeJobTaskHandler.php
new file mode 100644
index 00000000..3b0df798
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DisposeJobTaskHandler.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use Title;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DisposeJobTaskHandler extends TaskHandler {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var null|Job
+ */
+ private $refreshjob = null;
+
+ /**
+ * @var boolean
+ */
+ public $isApiTask = true;
+
+ /**
+ * @since 2.5
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_DATAREPAIR;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isApiTask() {
+ return $this->isApiTask;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'dispose';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $subject = DIWikiPage::newFromTitle( \SpecialPage::getTitleFor( 'SMWAdmin' ) );
+
+ // smw-admin-outdateddisposal
+ $this->htmlFormRenderer
+ ->addHeader( 'h4', $this->msg( 'smw-admin-outdateddisposal-title' ) )
+ ->addParagraph(
+ $this->msg( 'smw-admin-outdateddisposal-intro', Message::PARSE ),
+ [
+ 'id' => 'smw-admin-outdated-disposal',
+ 'class' => 'plainlinks'
+ ]
+ );
+
+ if ( $this->isEnabledFeature( SMW_ADM_DISPOSAL ) && !$this->hasPendingJob() ) {
+ $this->htmlFormRenderer
+ ->setMethod( 'post' )
+ ->addHiddenField( 'action', 'dispose' )
+ ->addSubmitButton(
+ $this->msg( 'smw-admin-outdateddisposal-button' ),
+ [
+ 'class' => $this->isApiTask() ? 'smw-admin-api-job-task' : '',
+ 'data-job' => 'SMW\EntityIdDisposerJob',
+ 'data-subject' => $subject->getHash()
+ ]
+ );
+ } elseif ( $this->isEnabledFeature( SMW_ADM_DISPOSAL ) ) {
+ $this->htmlFormRenderer->addParagraph(
+ Html::element(
+ 'span',
+ [
+ 'class' => 'smw-admin-circle-orange'
+ ]
+ ) . Html::element(
+ 'span',
+ [
+ 'style' => 'font-style:italic; margin-left:25px;'
+ ],
+ $this->msg( 'smw-admin-outdateddisposal-active' )
+ ),
+ [ 'id' => 'smw-admin-outdated-disposal-status' ]
+ );
+ } else {
+ $this->htmlFormRenderer->addParagraph(
+ $this->msg( 'smw-admin-feature-disabled' )
+ );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [],
+ $this->htmlFormRenderer->getForm()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_DISPOSAL ) || $this->hasPendingJob() || $this->isApiTask() ) {
+ return $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ $job = ApplicationFactory::getInstance()->newJobFactory()->newByType(
+ 'smw.entityIdDisposer',
+ \SpecialPage::getTitleFor( 'SMWAdmin' )
+ );
+
+ $job->insert();
+
+ $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ private function hasPendingJob() {
+ return ApplicationFactory::getInstance()->getJobQueue()->hasPendingJob( 'smw.entityIdDisposer' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DuplicateLookupTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DuplicateLookupTaskHandler.php
new file mode 100644
index 00000000..d893ce31
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/DuplicateLookupTaskHandler.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\Message;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DuplicateLookupTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @since 3.0
+ *
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( OutputFormatter $outputFormatter ) {
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'duplookup';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-duplookup-title' ),
+ [
+ 'action' => 'duplookup'
+ ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-duplookup-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle(
+ $this->msg( 'smw-admin-supplementary-duplookup-title' )
+ );
+
+ $this->outputFormatter->addParentLink(
+ [
+ 'tab' => 'supplement'
+ ]
+ );
+
+ $this->outputFormatter->addHelpLink(
+ $this->msg( 'smw-admin-supplementary-duplookup-helplink' )
+ );
+
+ $this->outputFormatter->addHtml(
+ Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $this->msg( 'smw-admin-supplementary-duplookup-docu', Message::PARSE )
+ )
+ );
+
+ // Ajax is doing the query and result display to avoid a timeout issue
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-admin-supplementary-duplookup',
+ 'style' => 'opacity:0.5;position: relative;',
+ 'data-config' => json_encode(
+ [
+ 'contentClass' => 'smw-admin-supplementary-duplookup-content',
+ 'errorClass' => 'smw-admin-supplementary-duplookup-error'
+ ]
+ )
+ ],
+ Html::element(
+ 'div',
+ [
+ 'class' => 'smw-admin-supplementary-duplookup-error'
+ ]
+ ) . Html::rawElement(
+ 'pre',
+ [
+ 'class' => 'smw-admin-supplementary-duplookup-content'
+ ],
+ $this->msg( 'smw-data-lookup-with-wait' ) .
+ "\n\n\n" . $this->msg( 'smw-processing' ) . "\n" .
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-overlay-spinner medium',
+ 'style' => 'transform: translate(-50%, -50%);'
+ ]
+ )
+ )
+ );
+
+ $this->outputFormatter->addHtml( $html );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/EntityLookupTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/EntityLookupTaskHandler.php
new file mode 100644
index 00000000..6b8e9de3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/EntityLookupTaskHandler.php
@@ -0,0 +1,325 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EntityLookupTaskHandler extends TaskHandler {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var User|null
+ */
+ private $user;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( Store $store, HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->store = $store;
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'lookup';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function setUser( $user = null ) {
+ $this->user = $user;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->createSpecialPageLink(
+ $this->msg( 'smw-admin-supplementary-idlookup-title' ),
+ [
+ 'action' => 'lookup'
+ ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-idlookup-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $this->outputFormatter->setPageTitle( $this->msg( 'smw-admin-supplementary-idlookup-title' ) );
+ $this->outputFormatter->addParentLink( [ 'tab' => 'supplement' ] );
+
+ // https://phabricator.wikimedia.org/T109652#1562641
+ if ( !$this->user->matchEditToken( $webRequest->getVal( 'wpEditToken' ) ) ) {
+ return $this->outputFormatter->addHtml( $this->msg( 'sessionfailure' ) );
+ }
+
+ $id = $webRequest->getText( 'id' );
+
+ if ( $this->isEnabledFeature( SMW_ADM_DISPOSAL ) && $id > 0 && $webRequest->getText( 'dispose' ) === 'yes' ) {
+ $this->doDispose( $id );
+ }
+
+ $this->outputFormatter->addHtml( $this->getForm( $webRequest, $id ) );
+ }
+
+ /**
+ * @param integer $id
+ * @param User|null $use
+ */
+ private function doDispose( $id ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $entityIdDisposerJob = $applicationFactory->newJobFactory()->newEntityIdDisposerJob(
+ \Title::newFromText( __METHOD__ )
+ );
+
+ $entityIdDisposerJob->dispose( intval( $id ) );
+
+ $manualEntryLogger = $applicationFactory->create( 'ManualEntryLogger' );
+ $manualEntryLogger->registerLoggableEventType( 'admin' );
+ $manualEntryLogger->log( 'admin', $this->user, 'Special:SMWAdmin', 'Forced removal of ID '. $id );
+ }
+
+ private function getForm( $webRequest, $id ) {
+
+ list( $result, $error ) = $this->createInfoMessageById( $webRequest, $id );
+
+ if ( $id < 1 ) {
+ $id = null;
+ }
+
+ $html = $this->htmlFormRenderer
+ ->setName( 'idlookup' )
+ ->setMethod( 'get' )
+ ->addHiddenField( 'action', 'lookup' )
+ ->addParagraph( $error )
+ ->addHeader( 'h2', $this->msg( 'smw-admin-idlookup-title' ) )
+ ->addParagraph( $this->msg( 'smw-admin-idlookup-docu' ) )
+ ->addInputField(
+ $this->msg( 'smw-admin-objectid' ),
+ 'id',
+ $id
+ )
+ ->addNonBreakingSpace()
+ ->addSubmitButton( $this->msg( 'smw-ask-search' ) )
+ ->addParagraph( $result )
+ ->getForm();
+
+ $html .= Html::element( 'p', [], '' );
+
+ if ( $id > 0 && $webRequest->getText( 'dispose' ) == 'yes' ) {
+ $result = $this->msg( ['smw-admin-iddispose-done', $id ] );
+ $id = null;
+ }
+
+ if ( !$this->isEnabledFeature( SMW_ADM_DISPOSAL ) ) {
+ return $html;
+ }
+
+ $html .= $this->htmlFormRenderer
+ ->setName( 'iddispose' )
+ ->setMethod( 'get' )
+ ->addHiddenField( 'action', 'lookup' )
+ ->addHiddenField( 'id', $id )
+ ->addHeader( 'h2', $this->msg( 'smw-admin-iddispose-title' ) )
+ ->addParagraph( $this->msg( 'smw-admin-iddispose-docu', Message::PARSE ), [ 'class' => 'plainlinks' ] )
+ ->addInputField(
+ $this->msg( 'smw-admin-objectid' ),
+ 'id',
+ $id,
+ null,
+ 20,
+ [ 'disabled' => true ]
+ )
+ ->addNonBreakingSpace()
+ ->addSubmitButton( $this->msg( 'allpagessubmit' ) )
+ ->addCheckbox(
+ $this->msg( 'smw_smwadmin_datarefreshstopconfirm', Message::ESCAPED ),
+ 'dispose',
+ 'yes'
+ )
+ ->getForm();
+
+ return $html . Html::element( 'p', [], '' );
+ }
+
+ private function createInfoMessageById( $webRequest, &$id ) {
+
+ if ( $webRequest->getText( 'action' ) !== 'lookup' || $id === '' ) {
+ return [ '', '' ];
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( ctype_digit( $id ) ) {
+ $condition = 'smw_id=' . intval( $id );
+ } else {
+ $op = strpos( $id, '*' ) !== false ? ' LIKE ' : '=';
+ $condition = "smw_sortkey $op " . $connection->addQuotes( str_replace( [ '_', '*' ], [ ' ', '%' ], $id ) );
+ }
+
+ $rows = $connection->select(
+ \SMWSql3SmwIds::TABLE_NAME,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey'
+ ],
+ $condition,
+ __METHOD__
+ );
+
+ return $this->createMessageFromRows( $id, $rows );
+ }
+
+ private function createMessageFromRows( &$id, $rows ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $references = [];
+ $formattedRows = [];
+ $output = '';
+ $error = '';
+
+ if ( $rows !== [] ) {
+ foreach ( $rows as $row ) {
+ $id = $row->smw_id;
+
+ $references[$id] = $this->store->getPropertyTableIdReferenceFinder()->searchAllTablesToFindAtLeastOneReferenceById(
+ $id
+ );
+
+ $formattedRows[$id] = (array)$row;
+
+ $row = $connection->selectRow(
+ SQLStore::FT_SEARCH_TABLE,
+ [
+ 's_id',
+ 'p_id',
+ 'o_text'
+ ],
+ [
+ 's_id' => $id
+ ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $references[$id][SQLStore::FT_SEARCH_TABLE] = (array)$row;
+ }
+ }
+ }
+
+ // ID is not unique
+ if ( count( $formattedRows ) > 1 ) {
+ $id = '';
+ }
+
+ if ( $formattedRows !== [] ) {
+ $output = '<pre>' . $this->outputFormatter->encodeAsJson( $formattedRows ) . '</pre>';
+ }
+ if ( $references !== [] ) {
+
+ $msg = $id === '' ? 'smw-admin-iddispose-references-multiple' : 'smw-admin-iddispose-references';
+ $count = isset( $references[$id] ) ? count( $references[$id] ) + 1 : 0;
+ $output .= Html::rawElement(
+ 'p',
+ [],
+ $this->msg( [ $msg, $id, $count ], Message::PARSE )
+ );
+ $output .= '<pre>' . $this->outputFormatter->encodeAsJson( $references ) . '</pre>';
+ } else {
+ $error .= Html::element(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-warning'
+ ],
+ $this->msg( [ 'smw-admin-iddispose-no-references', $id ] )
+ );
+
+ $id = '';
+ }
+
+ return [ $output, $error ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandler.php
new file mode 100644
index 00000000..a2dab6d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandler.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use Title;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableRebuildJobTaskHandler extends TaskHandler {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var boolean
+ */
+ public $isApiTask = true;
+
+ /**
+ * @since 2.5
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_DATAREPAIR;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isApiTask() {
+ return $this->isApiTask;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'fulltrebuild';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $subject = DIWikiPage::newFromTitle( \SpecialPage::getTitleFor( 'SMWAdmin' ) );
+
+ if ( $this->isEnabledFeature( SMW_ADM_FULLT ) && !$this->hasPendingJob() ) {
+ $this->htmlFormRenderer
+ ->addHeader( 'h4', $this->msg( 'smw-admin-fulltext-title' ) )
+ ->addParagraph( $this->msg( 'smw-admin-fulltext-intro', Message::PARSE ), [ 'class' => 'plainlinks' ] )
+ ->setMethod( 'post' )
+ ->addHiddenField( 'action', 'fulltrebuild' )
+ ->addSubmitButton(
+ $this->msg( 'smw-admin-fulltext-button' ),
+ [
+ 'class' => $this->isApiTask() ? 'smw-admin-api-job-task' : '',
+ 'data-job' => 'SMW\FulltextSearchTableRebuildJob',
+ 'data-subject' => $subject->getHash(),
+ 'data-parameters' => json_encode( [ 'mode' => '' ] )
+ ]
+ );
+ } elseif ( $this->isEnabledFeature( SMW_ADM_FULLT ) ) {
+ $this->htmlFormRenderer
+ ->addHeader( 'h4', $this->msg( 'smw-admin-fulltext-title' ) )
+ ->addParagraph( $this->msg( 'smw-admin-fulltext-intro', Message::PARSE ), [ 'class' => 'plainlinks' ] )
+ ->addParagraph(
+ Html::element(
+ 'span',
+ [
+ 'class' => 'smw-admin-circle-orange'
+ ]
+ ) . Html::element(
+ 'span',
+ [
+ 'style' => 'font-style:italic; margin-left:25px;'
+ ],
+ $this->msg( 'smw-admin-fulltext-active' )
+ )
+ );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [],
+ $this->htmlFormRenderer->getForm()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_FULLT ) || $this->hasPendingJob() || $this->isApiTask() ) {
+ return $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ $job = ApplicationFactory::getInstance()->newJobFactory()->newByType(
+ 'smw.fulltextSearchTableRebuild',
+ \SpecialPage::getTitleFor( 'SMWAdmin' ),
+ [
+ 'mode' => 'full'
+ ]
+ );
+
+ $job->insert();
+
+ $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ private function hasPendingJob() {
+ return ApplicationFactory::getInstance()->getJobQueue()->hasPendingJob( 'smw.fulltextSearchTableRebuild' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandler.php
new file mode 100644
index 00000000..f08311b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandler.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\Message;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OperationalStatisticsListTaskHandler extends TaskHandler {
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var TaskHandler[]
+ */
+ private $taskHandlers = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param OutputFormatter $outputFormatter
+ * @param TaskHandler[] $taskHandlers
+ */
+ public function __construct( OutputFormatter $outputFormatter, array $taskHandlers = [] ) {
+ $this->outputFormatter = $outputFormatter;
+ $this->taskHandlers = $taskHandlers;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPLEMENT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+
+ $actions = [
+ 'stats',
+ 'stats/cache'
+ ];
+
+ return in_array( $task, $actions );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $link = $this->outputFormatter->getSpecialPageLinkWith(
+ $this->msg( 'smw-admin-supplementary-operational-statistics-title' ),
+ [ 'action' => 'stats' ]
+ );
+
+ return Html::rawElement(
+ 'li',
+ [],
+ $this->msg(
+ [
+ 'smw-admin-supplementary-operational-statistics-intro',
+ $link
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ $action = $webRequest->getText( 'action' );
+
+ if ( $action === 'stats' ) {
+ $this->outputHead();
+ } else {
+ foreach ( $this->taskHandlers as $taskHandler ) {
+ if ( $taskHandler->isTaskFor( $action ) ) {
+ $taskHandler->setStore( $this->getStore());
+ return $taskHandler->handleRequest( $webRequest );
+ }
+ }
+ }
+
+ $this->outputSemanticStatistics();
+ $this->outputJobStatistics();
+ $this->outputInfo();
+ }
+
+ private function outputHead() {
+
+ $this->outputFormatter->setPageTitle(
+ $this->msg( 'smw-admin-supplementary-operational-statistics-title' )
+ );
+
+ $this->outputFormatter->addParentLink(
+ [ 'tab' => 'supplement' ]
+ );
+ }
+
+ private function outputInfo() {
+
+ $list = '';
+
+ foreach ( $this->taskHandlers as $taskHandler ) {
+ $list .= $taskHandler->getHtml();
+ }
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h2', [], $this->msg( 'smw-admin-other-functions' ) )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement( 'ul', [], $list )
+ );
+ }
+
+ private function outputSemanticStatistics() {
+
+ $semanticStatistics = $this->getStore()->getStatistics();
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement( 'p', [], $this->msg( [ 'smw-admin-operational-statistics' ], Message::PARSE ) )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h2', [], $this->msg( 'smw-statistics' ) )
+ );
+
+ $this->outputFormatter->addAsPreformattedText(
+ $this->outputFormatter->encodeAsJson(
+ [
+ 'propertyValues' => $semanticStatistics['PROPUSES'],
+ 'errorCount' => $semanticStatistics['ERRORUSES'],
+ 'totalProperties' => $semanticStatistics['TOTALPROPS'],
+ 'usedProperties' => $semanticStatistics['USEDPROPS'],
+ 'ownPage' => $semanticStatistics['OWNPAGE'],
+ 'declaredType' => $semanticStatistics['DECLPROPS'],
+ 'oudatedEntities' => $semanticStatistics['DELETECOUNT'],
+ 'subobjects' => $semanticStatistics['SUBOBJECTS'],
+ 'queries' => $semanticStatistics['QUERY'],
+ 'concepts' => $semanticStatistics['CONCEPTS'],
+ ]
+ )
+ );
+ }
+
+ private function outputJobStatistics() {
+
+ $this->outputFormatter->addHTML(
+ Html::element( 'h2', [], $this->msg( 'smw-admin-statistics-job-title' ) )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement( 'p', [], $this->msg( 'smw-admin-statistics-job-docu', Message::PARSE ) )
+ );
+
+ $this->outputFormatter->addHTML(
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-admin-statistics-job',
+ 'data-config' => json_encode( [
+ 'contentClass' => 'smw-admin-statistics-job-content',
+ 'errorClass' => 'smw-admin-statistics-job-error'
+ ] ),
+ ],
+ Html::element( 'div', [ 'class' => 'smw-admin-statistics-job-error' ], '' ) .
+ Html::element( 'div', [ 'class' => 'smw-admin-statistics-job-content' ], $this->msg( 'smw-data-lookup' ) )
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OutputFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OutputFormatter.php
new file mode 100644
index 00000000..dbcada58
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/OutputFormatter.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use FormatJson;
+use Html;
+use OutputPage;
+use SMW\Message;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OutputFormatter {
+
+ /**
+ * @var OutputPage
+ */
+ private $outputPage;
+
+ /**
+ * @since 2.5
+ *
+ * @param OutputPage $outputPage
+ */
+ public function __construct( OutputPage $outputPage ) {
+ $this->outputPage = $outputPage;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $query
+ */
+ public function addParentLink( $query = [], $title = 'smw-admin-tab-supplement' ) {
+ $this->outputPage->prependHTML( $this->createParentLink( $query, $title ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $url
+ */
+ public function addHelpLink( $url ) {
+ $this->outputPage->addHelpLink( $url, true );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $title
+ */
+ public function setPageTitle( $title ) {
+ $this->outputPage->setArticleRelated( false );
+ $this->outputPage->setPageTitle( $title );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ */
+ public function addAsPreformattedText( $html ) {
+ $this->outputPage->addHTML( '<pre>' . $html . '</pre>' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $css
+ */
+ public function addInlineStyle( $css ) {
+ $this->outputPage->addInlineStyle( $css );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $html
+ */
+ public function addHTML( $html ) {
+ $this->outputPage->addHTML( $html );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ */
+ public function addWikiText( $text ) {
+ $this->outputPage->addWikiText( $text );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $fragment
+ */
+ public function redirectToRootPage( $fragment = '', $query = [] ) {
+
+ $title = \SpecialPage::getTitleFor( 'SMWAdmin' );
+ $title->setFragment( ' ' . $fragment );
+
+ $this->outputPage->redirect( $title->getFullURL( $query ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $caption
+ * @param array $query
+ */
+ public function getSpecialPageLinkWith( $caption = '', $query = [] ) {
+ return $this->createSpecialPageLink( $caption, $query );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $caption
+ * @param array $query
+ */
+ public function createSpecialPageLink( $caption = '', $query = [] ) {
+ return '<a href="' . htmlspecialchars( \SpecialPage::getTitleFor( 'SMWAdmin' )->getFullURL( $query ) ) . '">' . $caption . '</a>';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param callable $text
+ */
+ public function formatAsRaw( callable $text ) {
+ $this->outputPage->disable(); // raw output
+ ob_start();
+
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength.MaxExceeded
+ print "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" dir=\"ltr\">\n<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title>Semantic MediaWiki</title></head><body><p><pre>";
+ // @codingStandardsIgnoreEnd
+ // header( "Content-type: text/html; charset=UTF-8" );
+ $text( $this );
+ print '</pre></p>';
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength.MaxExceeded
+ print '<b> ' . $this->getSpecialPageReturnLink() . "</b>\n";
+ // @codingStandardsIgnoreEnd
+ print '</body></html>';
+
+ ob_flush();
+ flush();
+ }
+
+ /**
+ *@note JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, and
+ * JSON_UNESCAPED_UNICOD were only added with 5.4
+ *
+ * @since 2.5
+ *
+ * @param array $input
+ *
+ * @return string
+ */
+ public function encodeAsJson( array $input ) {
+
+ if ( defined( 'JSON_PRETTY_PRINT' ) ) {
+ return json_encode( $input, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ return FormatJson::encode( $input, true );
+ }
+
+ private function createParentLink( $query = [], $title = 'smwadmin' ) {
+ return Html::rawElement(
+ 'div',
+ [ 'class' => 'smw-breadcrumb-link' ],
+ Html::rawElement(
+ 'span',
+ [ 'class' => 'smw-breadcrumb-arrow-right' ],
+ ''
+ ) .
+ Html::rawElement(
+ 'a',
+ [ 'href' => \SpecialPage::getTitleFor( 'SMWAdmin')->getFullURL( $query ) ],
+ Message::get( $title, Message::TEXT, Message::USER_LANGUAGE )
+ ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandler.php
new file mode 100644
index 00000000..c0b94382
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandler.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use Title;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatsRebuildJobTaskHandler extends TaskHandler {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @var boolean
+ */
+ public $isApiTask = true;
+
+ /**
+ * @since 2.5
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_DATAREPAIR;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isApiTask() {
+ return $this->isApiTask;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'pstatsrebuild';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $subject = DIWikiPage::newFromTitle( \SpecialPage::getTitleFor( 'SMWAdmin' ) );
+
+ // smw-admin-propertystatistics
+ $this->htmlFormRenderer
+ ->addHeader( 'h4', $this->msg( 'smw-admin-propertystatistics-title' ) )
+ ->addParagraph( $this->msg( 'smw-admin-propertystatistics-intro', Message::PARSE ), [ 'class' => 'plainlinks' ] );
+
+ if ( $this->isEnabledFeature( SMW_ADM_PSTATS ) && !$this->hasPendingJob() ) {
+ $this->htmlFormRenderer
+ ->setMethod( 'post' )
+ ->addHiddenField( 'action', 'pstatsrebuild' )
+ ->addSubmitButton(
+ $this->msg( 'smw-admin-propertystatistics-button' ),
+ [
+ 'class' => $this->isApiTask() ? 'smw-admin-api-job-task' : '',
+ 'data-job' => 'SMW\PropertyStatisticsRebuildJob',
+ 'data-subject' => $subject->getHash()
+ ]
+ );
+ } elseif ( $this->isEnabledFeature( SMW_ADM_PSTATS ) ) {
+ $this->htmlFormRenderer->addParagraph(
+ Html::element(
+ 'span',
+ [
+ 'class' => 'smw-admin-circle-orange'
+ ]
+ ) . Html::element(
+ 'span',
+ [
+ 'style' => 'font-style:italic; margin-left:25px;'
+ ],
+ $this->msg( 'smw-admin-propertystatistics-active' )
+ )
+ );
+ } else {
+ $this->htmlFormRenderer->addParagraph(
+ $this->msg( 'smw-admin-feature-disabled' )
+ );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [],
+ $this->htmlFormRenderer->getForm()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_PSTATS ) || $this->hasPendingJob() || $this->isApiTask() ) {
+ return $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ $job = ApplicationFactory::getInstance()->newJobFactory()->newByType(
+ 'smw.propertyStatisticsRebuild',
+ \SpecialPage::getTitleFor( 'SMWAdmin' )
+ );
+
+ $job->insert();
+
+ $this->outputFormatter->redirectToRootPage( '', [ 'tab' => 'rebuild' ] );
+ }
+
+ private function hasPendingJob() {
+ return ApplicationFactory::getInstance()->getJobQueue()->hasPendingJob( 'smw.propertyStatisticsRebuild' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/SupportListTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/SupportListTaskHandler.php
new file mode 100644
index 00000000..91b4065f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/SupportListTaskHandler.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SupportListTaskHandler extends TaskHandler {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @since 2.5
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SUPPORT;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $html = $this->createSupportForm() . $this->createRegistryForm();
+ $html .= Html::element( 'p', [], '' );
+
+ return Html::rawElement( 'div', [], $html );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function createSupportForm() {
+ $this->htmlFormRenderer
+ ->setName( 'support' )
+ ->addHeader( 'h3', $this->msg('smw-admin-support' ) )
+ ->addParagraph( $this->msg( 'smw-admin-supportdocu' ) )
+ ->addParagraph(
+ Html::rawElement( 'ul', [],
+ Html::rawElement( 'li', [], $this->msg( 'smw-admin-installfile' ) ) .
+ Html::rawElement( 'li', [], $this->msg( 'smw-admin-smwhomepage' ) ) .
+ Html::rawElement( 'li', [], $this->msg( 'smw-admin-bugsreport' ) ) .
+ Html::rawElement( 'li', [], $this->msg( 'smw-admin-questions' ) )
+ )
+ );
+
+ return $this->htmlFormRenderer->getForm();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function createRegistryForm() {
+
+ $this->htmlFormRenderer
+ ->setName( 'announce' )
+ ->setMethod( 'get' )
+ ->setActionUrl( 'https://wikiapiary.com/wiki/WikiApiary:Semantic_MediaWiki_Registry' )
+ ->addHeader( 'h3', $this->msg( 'smw-admin-announce' ) )
+ ->addParagraph( $this->msg( 'smw-admin-announce-text' ) )
+ ->addSubmitButton(
+ $this->msg( 'smw-admin-announce' ),
+ [
+ 'class' => ''
+ ]
+ );
+
+ return $this->htmlFormRenderer->getForm();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TableSchemaTaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TableSchemaTaskHandler.php
new file mode 100644
index 00000000..592528d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TableSchemaTaskHandler.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use Html;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Store;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableSchemaTaskHandler extends TaskHandler {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( Store $store, HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->store = $store;
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getSection() {
+ return self::SECTION_SCHEMA;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function hasAction() {
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function isTaskFor( $task ) {
+ return $task === 'updatetables';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getHtml() {
+
+ $this->htmlFormRenderer
+ ->setName( 'buildtables' )
+ ->setMethod( 'get' )
+ ->addHiddenField( 'action', 'updatetables' )
+ ->addHeader( 'h3', $this->msg( 'smw-admin-db' ) )
+ ->addParagraph( $this->msg( 'smw-admin-dbdocu' ) );
+
+ if ( $this->isEnabledFeature( SMW_ADM_SETUP ) ) {
+ $this->htmlFormRenderer
+ ->addHiddenField( 'udsure', 'yes' )
+ ->addSubmitButton(
+ $this->msg( 'smw-admin-dbbutton' ),
+ [
+ 'class' => ''
+ ]
+ );
+ } else {
+ $this->htmlFormRenderer
+ ->addParagraph( $this->msg( 'smw-admin-feature-disabled' ) );
+ }
+
+ return Html::rawElement( 'div', [], $this->htmlFormRenderer->getForm() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function handleRequest( WebRequest $webRequest ) {
+
+ if ( !$this->isEnabledFeature( SMW_ADM_SETUP ) ) {
+ return;
+ }
+
+ $this->outputFormatter->setPageTitle( $this->msg( 'smw-admin-db' ) );
+ $this->outputFormatter->addParentLink( [ 'tab' => 'rebuild' ] );
+
+ $messageReporter = MessageReporterFactory::getInstance()->newObservableMessageReporter();
+
+ $messageReporter->registerReporterCallback(
+ [
+ $this,
+ 'reportMessage'
+ ]
+ );
+
+ $this->store->setMessageReporter(
+ $messageReporter
+ );
+
+ $preparation = $webRequest->getVal( 'prep' );
+ $result = false;
+
+ $msg = Html::rawElement(
+ 'p',
+ [],
+ $this->msg( 'smw-admin-permissionswarn' )
+ );
+
+ // Reload (via JS) the page once content is displayed as separate page to inform
+ // the user about a possible delay in processing
+ if ( $preparation !== 'done' ) {
+ $this->outputFormatter->addHTML(
+ $msg . Html::rawElement(
+ 'div',
+ [
+ 'style' => 'opacity:0.5;position: relative;'
+ ],
+ Html::rawElement(
+ 'pre',
+ [
+ 'class' => 'smw-admin-db-preparation'
+ ],
+ $this->msg( 'smw-admin-db-preparation' ) .
+ "\n\n" . $this->msg( 'smw-processing' ) . "\n" .
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-overlay-spinner medium',
+ 'style' => 'transform: translate(-50%, -50%);'
+ ]
+ )
+ )
+ )
+ );
+ } else {
+ $this->outputFormatter->addHTML( $msg );
+ $this->outputFormatter->addHTML( '<pre>' );
+ $this->store->setup();
+ $this->outputFormatter->addHTML( '</pre>' );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ $this->outputFormatter->addHTML( $message );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandler.php
new file mode 100644
index 00000000..0a8afbc1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandler.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use SMW\Message;
+use SMW\Store;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+abstract class TaskHandler {
+
+ /**
+ * Identifies an individual section to where the task is associated with.
+ */
+ const SECTION_SUPPLEMENT = 'section.supplement';
+ const SECTION_SCHEMA = 'section.schema';
+ const SECTION_DATAREPAIR = 'section.datarepair';
+ const SECTION_DEPRECATION ='section.deprecation';
+ const SECTION_SUPPORT ='section.support';
+
+ /**
+ * @var integer
+ */
+ private $enabledFeatures = 0;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var boolean
+ */
+ protected $isApiTask = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function isEnabledFeature( $feature ) {
+ return ( ( (int)$this->enabledFeatures & $feature ) == $feature );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $enabledFeatures
+ */
+ public function setEnabledFeatures( $enabledFeatures ) {
+ $this->enabledFeatures = $enabledFeatures;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getSection() {
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isApiTask() {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasAction() {
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ abstract public function isTaskFor( $task );
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ abstract public function getHtml();
+
+ /**
+ * @since 2.5
+ *
+ * @param WebRequest $webRequest
+ */
+ abstract public function handleRequest( WebRequest $webRequest );
+
+ protected function msg( $key, $type = Message::TEXT ) {
+ return Message::get( $key, $type, Message::USER_LANGUAGE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandlerFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandlerFactory.php
new file mode 100644
index 00000000..e60b6085
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Admin/TaskHandlerFactory.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TaskHandlerFactory {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var OutputFormatter
+ */
+ private $outputFormatter;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param OutputFormatter $outputFormatter
+ */
+ public function __construct( Store $store, HtmlFormRenderer $htmlFormRenderer, OutputFormatter $outputFormatter ) {
+ $this->store = $store;
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->outputFormatter = $outputFormatter;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return []
+ */
+ public function getTaskHandlerList( $user, $adminFeatures ) {
+
+ $taskHandlers = [
+ // TaskHandler::SECTION_SCHEMA
+ $this->newTableSchemaTaskHandler(),
+
+ // TaskHandler::SECTION_DATAREPAIR
+ $this->newDataRefreshJobTaskHandler(),
+ $this->newDisposeJobTaskHandler(),
+ $this->newPropertyStatsRebuildJobTaskHandler(),
+ $this->newFulltextSearchTableRebuildJobTaskHandler(),
+
+ // TaskHandler::SECTION_DEPRECATION
+ $this->newDeprecationNoticeTaskHandler(),
+
+ // TaskHandler::SECTION_SUPPLEMENT
+ $this->newConfigurationListTaskHandler(),
+ $this->newOperationalStatisticsListTaskHandler(),
+ $this->newDuplicateLookupTaskHandler(),
+ $this->newEntityLookupTaskHandler( $user ),
+
+ // TaskHandler::SECTION_SUPPORT
+ $this->newSupportListTaskHandler()
+ ];
+
+ \Hooks::run( 'SMW::Admin::TaskHandlerFactory', [ &$taskHandlers, $this->store, $this->outputFormatter, $user ] );
+
+ $taskHandlerList = [
+ TaskHandler::SECTION_SCHEMA => [],
+ TaskHandler::SECTION_DATAREPAIR => [],
+ TaskHandler::SECTION_DEPRECATION => [],
+ TaskHandler::SECTION_SUPPLEMENT => [],
+ TaskHandler::SECTION_SUPPORT => [],
+ 'actions' => []
+ ];
+
+ foreach ( $taskHandlers as $taskHandler ) {
+
+ if ( !is_a( $taskHandler, 'SMW\MediaWiki\Specials\Admin\TaskHandler' ) ) {
+ continue;
+ }
+
+ $taskHandler->setEnabledFeatures(
+ $adminFeatures
+ );
+
+ $taskHandler->setStore(
+ $this->store
+ );
+
+ switch ( $taskHandler->getSection() ) {
+ case TaskHandler::SECTION_SCHEMA:
+ $taskHandlerList[TaskHandler::SECTION_SCHEMA][] = $taskHandler;
+ break;
+ case TaskHandler::SECTION_DATAREPAIR:
+ $taskHandlerList[TaskHandler::SECTION_DATAREPAIR][] = $taskHandler;
+ break;
+ case TaskHandler::SECTION_DEPRECATION:
+ $taskHandlerList[TaskHandler::SECTION_DEPRECATION][] = $taskHandler;
+ break;
+ case TaskHandler::SECTION_SUPPLEMENT:
+ $taskHandlerList[TaskHandler::SECTION_SUPPLEMENT][] = $taskHandler;
+ break;
+ case TaskHandler::SECTION_SUPPORT:
+ $taskHandlerList[TaskHandler::SECTION_SUPPORT][] = $taskHandler;
+ break;
+ }
+
+ if ( $taskHandler->hasAction() ) {
+ $taskHandlerList['actions'][] = $taskHandler;
+ }
+ }
+
+ return $taskHandlerList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return TableSchemaTaskHandler
+ */
+ public function newTableSchemaTaskHandler() {
+ return new TableSchemaTaskHandler( $this->store, $this->htmlFormRenderer, $this->outputFormatter );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SupportListTaskHandler
+ */
+ public function newSupportListTaskHandler() {
+ return new SupportListTaskHandler( $this->htmlFormRenderer );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ConfigurationListTaskHandler
+ */
+ public function newConfigurationListTaskHandler() {
+ return new ConfigurationListTaskHandler( $this->outputFormatter );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return OperationalStatisticsListTaskHandler
+ */
+ public function newOperationalStatisticsListTaskHandler() {
+
+ $taskHandlers = [
+ new CacheStatisticsListTaskHandler( $this->outputFormatter )
+ ];
+
+ return new OperationalStatisticsListTaskHandler( $this->outputFormatter, $taskHandlers );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return EntityLookupTaskHandler
+ */
+ public function newEntityLookupTaskHandler( $user = null ) {
+
+ $entityLookupTaskHandler = new EntityLookupTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $entityLookupTaskHandler->setUser(
+ $user
+ );
+
+ return $entityLookupTaskHandler;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataRefreshJobTaskHandler
+ */
+ public function newDataRefreshJobTaskHandler() {
+ return new DataRefreshJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DisposeJobTaskHandler
+ */
+ public function newDisposeJobTaskHandler() {
+ return new DisposeJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyStatsRebuildJobTaskHandler
+ */
+ public function newPropertyStatsRebuildJobTaskHandler() {
+ return new PropertyStatsRebuildJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return FulltextSearchTableRebuildJobTaskHandler
+ */
+ public function newFulltextSearchTableRebuildJobTaskHandler() {
+ return new FulltextSearchTableRebuildJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DeprecationNoticeTaskHandler
+ */
+ public function newDeprecationNoticeTaskHandler() {
+ return new DeprecationNoticeTaskHandler( $this->outputFormatter, $GLOBALS['smwgDeprecationNotices'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DuplicateLookupTaskHandler
+ */
+ public function newDuplicateLookupTaskHandler() {
+ return new DuplicateLookupTaskHandler( $this->outputFormatter );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/DownloadLinksWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/DownloadLinksWidget.php
new file mode 100644
index 00000000..e46f26aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/DownloadLinksWidget.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMWInfolink as Infolink;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DownloadLinksWidget {
+
+ /**
+ * @since 3.0
+ *
+ * @param Infolink|null $infolink
+ *
+ * @return string
+ */
+ public static function downloadLinks( Infolink $infolink = null ) {
+
+ if ( $infolink === null ) {
+ return '';
+ }
+
+ // Avoid modifying the original object
+ $infolink = clone $infolink;
+ $downloadLinks = [];
+
+ $infolink->setParameter( 'true', 'prettyprint' );
+ $infolink->setParameter( 'true', 'unescape' );
+ $infolink->setParameter( 'json', 'format' );
+ $infolink->setParameter( 'JSON', 'searchlabel' );
+ $infolink->setCaption( 'JSON' );
+
+ $infolink->setLinkAttributes(
+ [
+ 'title' => Message::get( [ 'smw-ask-download-link-desc', 'JSON' ], Message::TEXT, Message::USER_LANGUAGE ),
+ 'class' => 'page-link'
+ ]
+ );
+
+ $downloadLinks[] = $infolink->getHtml();
+
+ $infolink->setCaption( 'CSV' );
+ $infolink->setParameter( 'csv', 'format' );
+ $infolink->setParameter( 'CSV', 'searchlabel' );
+
+ $infolink->setLinkAttributes(
+ [
+ 'title' => Message::get( [ 'smw-ask-download-link-desc', 'CSV' ], Message::TEXT, Message::USER_LANGUAGE ),
+ 'class' => 'page-link'
+ ]
+ );
+
+ $downloadLinks[] = $infolink->getHtml();
+
+ $infolink->setCaption( 'RSS' );
+ $infolink->setParameter( 'rss', 'format' );
+ $infolink->setParameter( 'RSS', 'searchlabel' );
+
+ $infolink->setLinkAttributes(
+ [
+ 'title' => Message::get( [ 'smw-ask-download-link-desc', 'RSS' ], Message::TEXT, Message::USER_LANGUAGE ),
+ 'class' => 'page-link'
+ ]
+ );
+
+ $downloadLinks[] = $infolink->getHtml();
+
+ $infolink->setCaption( 'RDF' );
+ $infolink->setParameter( 'rdf', 'format' );
+ $infolink->setParameter( 'RDF', 'searchlabel' );
+
+ $infolink->setLinkAttributes(
+ [
+ 'title' => Message::get( [ 'smw-ask-download-link-desc', 'RDF' ], Message::TEXT, Message::USER_LANGUAGE ),
+ 'class' => 'page-link'
+ ]
+ );
+
+ $downloadLinks[] = $infolink->getHtml();
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'ask-export-links',
+ 'class' => 'smw-ask-downloadlinks export-links'
+ ],
+ '<div class="smw-ui-pagination">' . implode( '', $downloadLinks ) . '</div>'
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ErrorWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ErrorWidget.php
new file mode 100644
index 00000000..2834f177
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ErrorWidget.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMW\ProcessingErrorMsgHandler;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorWidget {
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function disabled() {
+ return Html::element(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error'
+ ],
+ Message::get( 'smw_iq_disabled', Message::TEXT, Message::USER_LANGUAGE )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public static function noResult() {
+ return Html::element(
+ 'div',
+ [
+ 'id' => 'no-result',
+ 'class' => 'smw-callout smw-callout-info'
+ ],
+ Message::get( 'smw_result_noresults', Message::TEXT, Message::USER_LANGUAGE )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function noScript() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'ask-status',
+ 'class' => 'smw-ask-status plainlinks'
+ ],
+ Html::rawElement(
+ 'noscript',
+ [],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error',
+ ],
+ Message::get( 'smw-noscript', Message::PARSE, Message::USER_LANGUAGE )
+ )
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function sessionFailure() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error'
+ ],
+ Message::get( 'sessionfailure', Message::TEXT, Message::USER_LANGUAGE )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Query|null $query
+ *
+ * @return string
+ */
+ public static function queryError( Query $query = null ) {
+
+ if ( $query === null || !is_array( $query->getErrors() ) || $query->getErrors() === [] ) {
+ return '';
+ }
+
+ $errors = [];
+
+ foreach ( ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $query->getErrors() ) as $value ) {
+
+ if ( $value === '' ) {
+ continue;
+ }
+
+ if ( is_array( $value ) ) {
+ $value = implode( " ", $value );
+ }
+
+ $errors[] = $value;
+ }
+
+ if ( count( $errors ) > 1 ) {
+ $error = '<ul><li>' . implode( '</li><li>', $errors ) . '</li></ul>';
+ } else {
+ $error = implode( ' ', $errors );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'result-error',
+ 'class' => 'smw-callout smw-callout-error'
+ ],
+ $error
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/FormatListWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/FormatListWidget.php
new file mode 100644
index 00000000..83b3cbcb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/FormatListWidget.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMWQueryProcessor as QueryProcessor;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormatListWidget {
+
+ /**
+ * @var array
+ */
+ private static $resultFormats = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $resultFormats
+ */
+ public static function setResultFormats( array $resultFormats ) {
+ self::$resultFormats = $resultFormats;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $params
+ *
+ * @return string
+ */
+ public static function selectList( Title $title, array $params ) {
+
+ $result = '';
+
+ // Default
+ $printer = QueryProcessor::getResultPrinter(
+ 'broadtable',
+ QueryProcessor::SPECIAL_PAGE
+ );
+
+ $url = $title->getLocalURL( 'showformatoptions=this.value' );
+
+ foreach ( $params as $param => $value ) {
+ if ( $param !== 'format' ) {
+ $url .= '&params[' . rawurlencode( $param ) . ']=' . rawurlencode( $value );
+ }
+ }
+
+ $defaultLocalizedName = htmlspecialchars( $printer->getName() ) . ' (' . Message::get( 'smw_ask_defaultformat', Message::TEXT, Message::USER_LANGUAGE ) . ')';
+ $defaultName = $printer->getName();
+
+ $default = '';
+ $selectedFormat = isset( $params['format'] ) ? $params['format'] : 'broadtable';
+
+ $formatList = self::formatList(
+ $url,
+ $selectedFormat,
+ $default,
+ $defaultName,
+ $defaultLocalizedName
+ );
+
+ $result = Html::rawElement(
+ 'span',
+ [
+ 'class' => "smw-ask-format-list"
+ ],
+ Html::hidden( 'eq', 'yes' ) . $formatList
+ );
+
+ return $result;
+ }
+
+ private static function formatList( $url, $selectedFormat, &$default, $defaultName, $defaultLocalizedName ) {
+
+ $formatList = Html::rawElement(
+ 'option',
+ [
+ 'value' => 'broadtable'
+ ] + ( $selectedFormat == 'broadtable' ? [ 'selected' ] : [] ),
+ $defaultLocalizedName
+ );
+
+ $formats = [];
+
+ foreach ( array_keys( self::$resultFormats ) as $format ) {
+ // Special formats "count" and "debug" currently not supported.
+ if ( $format != 'broadtable' && $format != 'count' && $format != 'debug' ) {
+ $printer = QueryProcessor::getResultPrinter(
+ $format,
+ QueryProcessor::SPECIAL_PAGE
+ );
+
+ $formats[] = [
+ 'format' => $format,
+ 'name' => htmlspecialchars( $printer->getName() ),
+ 'export' => $printer->isExportFormat()
+ ];
+ }
+ }
+
+ usort( $formats, function( $x, $y ) {
+ return strcasecmp( $x['name'] , $y['name'] );
+ } );
+
+ $default = $defaultName;
+
+ foreach ( $formats as $format ) {
+
+ $formatList .= Html::rawElement(
+ 'option',
+ [
+ 'data-isexport' => $format['export'],
+ 'value' => $format['format']
+ ] + ( $selectedFormat == $format['format'] ? [ 'selected' ] : [] ),
+ $format['name']
+ );
+
+ if ( $selectedFormat == $format['format'] ) {
+ $default = $format['name'];
+ }
+ }
+
+ $default = Html::rawElement(
+ 'a',
+ [
+ 'href' => 'https://semantic-mediawiki.org/wiki/Help:' . $selectedFormat . ' format'
+ ],
+ $default
+ );
+
+ return Html::rawElement(
+ 'select',
+ [
+ 'id' => 'formatSelector', // Used in JS as selector
+ 'class' => 'smw-ask-button smw-ask-button-lgrey smw-ask-format-selector',
+ 'name' => 'p[format]',
+ 'data-url' => $url
+ ],
+ $formatList
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HelpWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HelpWidget.php
new file mode 100644
index 00000000..3a583848
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HelpWidget.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMW\Utils\HtmlModal;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HelpWidget {
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function html() {
+
+ $format = 'broadtable' ;
+ $text = Message::get( 'smw-ask-help', Message::PARSE, Message::USER_LANGUAGE );
+
+ $text .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'strike',
+ 'style' => 'padding: 5px 0 5px 0;'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'style' => 'font-size: 1.2em; margin-left:0px'
+ ],
+ Message::get( 'smw-ask-format', Message::TEXT, Message::USER_LANGUAGE )
+ ) . Html::rawElement(
+ 'ul',
+ [],
+ Html::rawElement(
+ 'li',
+ [
+ 'class' => 'smw-ask-format-help-link'
+ ],
+ Message::get( [ 'smw-ask-format-help-link', $format ], Message::PARSE, Message::USER_LANGUAGE )
+ )
+ )
+ );
+
+ $text .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'strike',
+ 'style' => 'padding: 5px 0 5px 0;'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'style' => 'font-size: 1.2em; margin-left:0px'
+ ],
+ Message::get( 'smw-ask-input-assistance', Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+
+ $text .= Message::get( 'smw-ask-condition-input-assistance', Message::PARSE, Message::USER_LANGUAGE );
+
+ $text .= Html::rawElement(
+ 'ul',
+ [],
+ Html::rawElement(
+ 'li',
+ [],
+ Message::get( 'smw-ask-condition-input-assistance-property', Message::TEXT, Message::USER_LANGUAGE )
+ ) .
+ Html::rawElement(
+ 'li',
+ [],
+ Message::get( 'smw-ask-condition-input-assistance-category', Message::TEXT, Message::USER_LANGUAGE )
+ ) .
+ Html::rawElement(
+ 'li',
+ [],
+ Message::get( 'smw-ask-condition-input-assistance-concept', Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+
+ $html = HtmlModal::modal(
+ Message::get( 'smw-cheat-sheet', Message::TEXT, Message::USER_LANGUAGE ),
+ $text,
+ [
+ 'id' => 'ask-help',
+ 'class' => 'plainlinks',
+ 'style' => 'display:none;'
+ ]
+ );
+
+ return $html;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HtmlForm.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HtmlForm.php
new file mode 100644
index 00000000..8c4eaf88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/HtmlForm.php
@@ -0,0 +1,381 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use Title;
+use SMWQueryResult as QueryResult;
+use SMW\Utils\HtmlTabs;
+use SMW\Query\QueryLinker;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlForm {
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @var string
+ */
+ private $queryString = '';
+
+ /**
+ * @var Query
+ */
+ private $query;
+
+ /**
+ * @var array
+ */
+ private $callbacks = [];
+
+ /**
+ * @var boolean
+ */
+ private $isEditMode = true;
+
+ /**
+ * @var boolean
+ */
+ private $isBorrowedMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isPostSubmit = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ */
+ public function __construct( Title $title ) {
+ $this->title = $title;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ */
+ public function setParameters( array $parameters ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $queryString
+ */
+ public function setQueryString( $queryString ) {
+ $this->queryString = $queryString;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ */
+ public function setQuery( Query $query = null ) {
+ $this->query = $query;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $callbacks
+ */
+ public function setCallbacks( array $callbacks ) {
+ $this->callbacks = $callbacks;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isEditMode
+ */
+ public function isEditMode( $isEditMode ) {
+ $this->isEditMode = (bool)$isEditMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isBorrowedMode
+ */
+ public function isBorrowedMode( $isBorrowedMode ) {
+ $this->isBorrowedMode = (bool)$isBorrowedMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isPostSubmit
+ */
+ public function isPostSubmit( $isPostSubmit ) {
+ $this->isPostSubmit = (bool)$isPostSubmit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param UrlArgs $urlArgs
+ * @param QueryResult|string|null $queryResult
+ *
+ * @return string
+ */
+ public function getForm( UrlArgs $urlArgs, $queryResult = null, $text = '' ) {
+
+ $html = $this->buildHTML( $urlArgs, $queryResult, $text );
+
+ if ( $this->isPostSubmit ) {
+ $params = [
+ 'action' => $this->title->getLocalUrl( wfArrayToCGI( $urlArgs ) . '#search' ),
+ 'name' => 'ask',
+ 'method' => 'post'
+ ];
+ } else {
+ $params = [
+ 'action' => $GLOBALS['wgScript'],
+ 'name' => 'ask',
+ 'method' => 'get'
+ ];
+ }
+
+ return Html::rawElement( 'form', $params, $html );
+ }
+
+ private function buildHTML( $urlArgs, $queryResult, $infoText ) {
+
+ $navigation = '';
+ $queryLink = null;
+ $isFromCache = false;
+
+ if ( $queryResult instanceof QueryResult ) {
+ $navigation = NavigationLinksWidget::navigationLinks(
+ $this->title,
+ $urlArgs,
+ $queryResult->getCount(),
+ $queryResult->hasFurtherResults()
+ );
+
+ $isFromCache = $queryResult->isFromCache();
+
+ if ( $this->query !== null ) {
+ $queryLink = QueryLinker::get( $this->query, $this->parameters );
+ } elseif ( ( $query = $queryResult->getQuery() ) !== null ) {
+ $queryLink = QueryLinker::get( $query, $this->parameters );
+ }
+ }
+
+ $html = '';
+ $hideForm = false;
+ $urlArgs->set( 'eq', 'yes' );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'ask' );
+ $htmlTabs->setActiveTab( 'smw-askt-result' );
+
+ if ( $this->isEditMode ) {
+ $html = $this->editElements( $urlArgs );
+ $hideForm = true;
+ }
+
+ $isEmpty = $queryLink === null;
+ $editLink = $this->title->getLocalURL( $urlArgs );
+
+ // Submit
+ $html .= LinksWidget::resultSubmitLink(
+ $hideForm
+ );
+
+ if ( !$this->isEditMode && !$isEmpty ) {
+ $htmlTabs->tab(
+ 'smw-askt-edit',
+ LinksWidget::editLink( $editLink ),
+ [
+ 'hide' => $this->isBorrowedMode,
+ 'class' => 'edit-action'
+ ]
+ );
+ } elseif ( !$isEmpty ) {
+ $htmlTabs->tab(
+ 'smw-askt-compact',
+ LinksWidget::hideLink( $editLink ),
+ [
+ 'hide' => $this->isBorrowedMode,
+ 'class' => 'compact-action'
+ ]
+ );
+ }
+
+ $htmlTabs->tab(
+ 'smw-askt-result',
+ wfMessage( 'smw-ask-tab-result' )->text(),
+ [
+ 'hide' => $isEmpty,
+ 'class' => $isFromCache ? ' result-cache' : ''
+ ]
+ );
+
+ $links = [];
+
+ $htmlTabs->tab(
+ 'smw-askt-code',
+ wfMessage( 'smw-ask-tab-code' )->text(),
+ [
+ 'hide' => $this->isBorrowedMode || $isEmpty
+ ]
+ );
+
+ $code = '';
+
+ if ( isset( $this->callbacks['code_handler'] ) && is_callable( $this->callbacks['code_handler'] ) ) {
+ $code = $this->callbacks['code_handler']();
+ }
+
+ $htmlTabs->content(
+ 'smw-askt-code',
+ '<div style="margin-top:15px; margin-bottom:15px;">' .
+ LinksWidget::embeddedCodeBlock( $code, true ) . '</div>'
+ );
+
+ $clipboardLink = LinksWidget::clipboardLink( $queryLink );
+
+ $htmlTabs->tab(
+ 'smw-askt-clipboard',
+ $clipboardLink,
+ [
+ 'hide' => $clipboardLink === '',
+ 'class' => 'clipboard-bookmark smw-tab-right'
+ ]
+ );
+
+ if ( !isset( $this->parameters['source'] ) || $this->parameters['source'] === '' ) {
+ $debugLink = LinksWidget::debugLink( $this->title, $urlArgs, $isEmpty, true );
+
+ $htmlTabs->tab(
+ 'smw-askt-debug',
+ $debugLink,
+ [
+ 'hide' => $debugLink === '' || !$this->isEditMode ,
+ 'class' => 'smw-tab-right'
+ ]
+ );
+ }
+
+ if ( isset( $this->callbacks['borrowed_msg_handler'] ) && is_callable( $this->callbacks['borrowed_msg_handler'] ) ) {
+ $this->callbacks['borrowed_msg_handler']( $links, $infoText );
+ }
+
+ $basicLinks = NavigationLinksWidget::basicLinks(
+ $navigation,
+ $queryLink
+ );
+
+ $htmlTabs->content( 'smw-askt-result', $basicLinks );
+
+ if ( !$isEmpty ) {
+ $htmlTabs->tab(
+ 'smw-askt-extra',
+ wfMessage( 'smw-ask-tab-extra' )->text(),
+ [
+ 'class' => 'smw-tab-right'
+ ]
+ );
+
+ if ( is_array( $links ) ) {
+ $links[] = $infoText;
+
+ // External source cannot disable the cache
+ if ( isset( $this->parameters['source'] ) && $this->parameters['source'] !== '' ) {
+ $isFromCache = false;
+ }
+
+ if ( ( $noCacheLink = LinksWidget::noQCacheLink( $this->title, $urlArgs, $isFromCache ) ) !== '' ) {
+ $links[] = $noCacheLink;
+ }
+
+ $infoText = '<ul><li>' . implode( '</li><li>', $links ) . '</li></ul>';
+ } else {
+ $infoText = $links;
+ }
+
+ $htmlTabs->content(
+ 'smw-askt-extra',
+ '<div style="margin-top:15px;margin-bottom:20px;">' . $infoText . '</div>'
+ );
+ }
+
+ $html .= $htmlTabs->buildHTML(
+ [
+ 'id' => 'search',
+ 'class' => $this->isEditMode ? 'smw-ask-search-edit' . ( $isEmpty ? ' empty-result' : '' ) : 'smw-ask-search-compact'
+ ]
+ );
+
+ return $html;
+ }
+
+ private function editElements( $urlArgs ) {
+ $html = '';
+
+ $html .= Html::hidden( 'title', $this->title->getPrefixedDBKey() );
+ $html .= Html::hidden( '_action', 'submit' );
+
+ // Table for main query and printouts.
+ $html .= Html::rawElement(
+ 'div',
+ [
+ 'id' => 'query',
+ 'class' => 'smw-ask-query'
+ ],
+ QueryInputWidget::table(
+ $this->queryString,
+ $urlArgs->get( 'po' )
+ )
+ );
+
+ // Format selection
+ $html .= Html::rawElement(
+ 'div',
+ [
+ 'id' => 'format',
+ 'class' => "smw-ask-format"
+ ],
+ ''
+ );
+
+ // Other options fieldset
+ $html .= Html::rawElement(
+ 'div',
+ [
+ 'id' => 'options',
+ 'class' => 'smw-ask-options'
+ ],
+ ParametersWidget::fieldset(
+ $this->title,
+ $this->parameters
+ )
+ );
+
+ $urlArgs->set( 'eq', 'no' );
+
+ return $html;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/LinksWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/LinksWidget.php
new file mode 100644
index 00000000..923fb662
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/LinksWidget.php
@@ -0,0 +1,375 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMWInfolink as Infolink;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksWidget {
+
+ /**
+ * @return array
+ */
+ public static function getModules() {
+ return [ 'onoi.clipboard' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ *
+ * @return string
+ */
+ public static function fieldset( $html = '' ) {
+
+ $html = '<p></p>' . $html;
+
+ return Html::rawElement(
+ 'fieldset',
+ [],
+ Html::rawElement(
+ 'legend',
+ [],
+ Message::get( 'smw-ask-search', Message::TEXT, Message::USER_LANGUAGE )
+ ) . $html
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isEmpty
+ *
+ * @return string
+ */
+ public static function embeddedCodeLink( $isEmpty = false ) {
+
+ if ( $isEmpty ) {
+ return '';
+ }
+
+ //show|hide inline embed code
+ $embedShow = "document.getElementById('inlinequeryembed').style.display='block';" .
+ "document.getElementById('embed_hide').style.display='inline';" .
+ "document.getElementById('embed_show').style.display='none';" .
+ "document.getElementById('inlinequeryembedarea').select();";
+
+ $embedHide = "document.getElementById('inlinequeryembed').style.display='none';" .
+ "document.getElementById('embed_show').style.display='inline';" .
+ "document.getElementById('embed_hide').style.display='none';";
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'id' => 'ask-embed',
+ 'class' => 'smw-ask-button smw-ask-button-lblue'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'id' => 'embed_show'
+ ], Html::rawElement(
+ 'a',
+ [
+ 'href' => '#embed_show',
+ 'rel' => 'nofollow',
+ 'onclick' => $embedShow
+ ], wfMessage( 'smw_ask_show_embed' )->escaped()
+ )
+ ) . Html::rawElement(
+ 'span',
+ [
+ 'id' => 'embed_hide',
+ 'style' => 'display: none;'
+ ], Html::rawElement(
+ 'a',
+ [
+ 'href' => '#embed_hide',
+ 'rel' => 'nofollow',
+ 'onclick' => $embedHide
+ ], wfMessage( 'smw_ask_hide_embed' )->escaped()
+ )
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $href
+ *
+ * @return string
+ */
+ public static function editLink( $href ) {
+ return Html::rawElement(
+ 'a',
+ [
+ 'href' => $href . '#search',
+ 'rel' => 'href',
+ 'style' => 'display:block; width:60px'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-icon-pen',
+ 'title' => wfMessage( 'smw_ask_editquery' )->text(),
+ ],
+ ''
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $href
+ *
+ * @return string
+ */
+ public static function hideLink( $href ) {
+ return Html::rawElement(
+ 'a',
+ [
+ 'href' => $href,
+ 'rel' => 'nofollow',
+ 'style' => 'display:block; width:60px'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-icon-compact',
+ 'title' => wfMessage( 'smw_ask_hidequery' )->text()
+ ],
+ ''
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $code
+ *
+ * @return string
+ */
+ public static function embeddedCodeBlock( $code, $raw = false ) {
+
+ $code = Html::rawElement(
+ 'pre',
+ [
+ 'id' => 'inlinequeryembedarea',
+ 'readonly' => 'yes',
+ 'cols' => 20,
+ 'rows' => substr_count( $code, "\n" ) + 1,
+ 'onclick' => 'this.select()'
+ ],
+ $code
+ );
+
+ if ( $raw ) {
+ return '<p>' . wfMessage( 'smw_ask_embed_instr' )->escaped() . '</p>' . $code;
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'inlinequeryembed',
+ 'style' => 'display: none;'
+ ], Html::rawElement(
+ 'div',
+ [
+ 'id' => 'inlinequeryembedinstruct'
+ ], wfMessage( 'smw_ask_embed_instr' )->escaped()
+ ) . $code
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isEmpty
+ *
+ * @return string
+ */
+ public static function resultSubmitLink( $isEmpty = false ) {
+
+ if ( !$isEmpty ) {
+ return '';
+ }
+
+ return Html::rawElement( 'div', [ 'class' => 'smw-ask-button-submit' ], Html::element(
+ 'input',
+ [
+ 'type' => 'submit',
+ 'class' => '',
+ 'value' => wfMessage( 'smw_ask_submit' )->escaped()
+ ], ''
+ ) . ' ' . Html::element(
+ 'input',
+ [
+ 'type' => 'hidden',
+ 'name' => 'eq',
+ 'value' => 'yes'
+ ], ''
+ ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ * @param string $urlTail
+ * @param boolean $hideForm
+ * @param boolean $isEmpty
+ *
+ * @return string
+ */
+ public static function showHideLink( Title $title, UrlArgs $urlArgs, $hideForm = false, $isEmpty = false ) {
+
+ if ( $isEmpty || $hideForm === false ) {
+ return '';
+ }
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'id' => 'ask-showhide',
+ 'class' => 'smw-ask-button smw-ask-button-lblue'
+ ], Html::element(
+ 'a',
+ [
+ 'href' => $title->getLocalURL( $urlArgs ),
+ 'rel' => 'nofollow'
+ ],
+ wfMessage( ( $hideForm ? 'smw_ask_hidequery' : 'smw_ask_editquery' ) )->text()
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param string $urlTail
+ * @param boolean $isEmpty
+ *
+ * @return string
+ */
+ public static function debugLink( Title $title, UrlArgs $urlArgs, $isEmpty = false, $raw = false ) {
+
+ if ( $isEmpty ) {
+ return '';
+ }
+
+ $urlArgs->set( 'eq', 'yes' );
+ $urlArgs->set( 'debug', 'true' );
+ $urlArgs->setFragment( 'search' );
+
+ $link = Html::element(
+ 'a',
+ [
+ 'class' => '',
+ 'href' => $title->getLocalURL( $urlArgs ),
+ 'rel' => 'nofollow',
+ 'title' => Message::get( 'smw-ask-debug-desc', Message::TEXT, Message::USER_LANGUAGE )
+ ],
+ $raw ? Message::get( 'smw-ask-debug', Message::TEXT, Message::USER_LANGUAGE ) : 'ℹ'
+ );
+
+ if ( $raw ) {
+ return $link;
+ }
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'id' => 'ask-debug',
+ 'class' => 'smw-ask-button smw-ask-button-right',
+ 'title' => Message::get( 'smw-ask-debug-desc', Message::TEXT, Message::USER_LANGUAGE )
+ ],
+ $link
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param string $urlTail
+ * @param boolean $isFromCache
+ *
+ * @return string
+ */
+ public static function noQCacheLink( Title $title, UrlArgs $urlArgs, $isFromCache = false ) {
+
+ if ( $isFromCache === false ) {
+ return '';
+ }
+
+ $urlArgs->set( 'cache', 'no' );
+ $urlArgs->delete( 'debug' );
+
+ $urlArgs->setFragment( 'search' );
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'id' => 'ask-cache',
+ 'class' => '',
+ 'title' => Message::get( 'smw-ask-no-cache-desc', Message::TEXT, Message::USER_LANGUAGE )
+ ],
+ Html::element(
+ 'a',
+ [
+ 'class' => '',
+ 'href' => $title->getLocalURL( $urlArgs ),
+ 'rel' => 'nofollow'
+ ],
+ Message::get( 'smw-ask-no-cache', Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Infolink|null $infolink
+ *
+ * @return string
+ */
+ public static function clipboardLink( Infolink $infolink = null ) {
+
+ if ( $infolink === null ) {
+ return '';
+ }
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'id' => 'ask-clipboard ',
+ // 'class' => 'smw-ask-button smw-ask-button-right smw-ask-button-lgrey'
+ ],
+ Html::element(
+ 'a',
+ [
+ 'data-clipboard-action' => 'copy',
+ 'data-clipboard-target' => '.clipboard',
+ 'data-onoi-clipboard-field' => 'value',
+ 'class' => 'clipboard smw-icon-bookmark',
+ 'value' => $infolink->getURL(),
+ 'title' => wfMessage( 'smw-clipboard-copy-link' )->text()
+ ]
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/NavigationLinksWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/NavigationLinksWidget.php
new file mode 100644
index 00000000..95420a77
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/NavigationLinksWidget.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Localizer;
+use SMW\Message;
+use SMW\Utils\HtmlModal;
+use SMW\Page\ListPager;
+use SMWInfolink as Infolink;
+use Title;
+use SMW\Utils\HtmlTabs;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NavigationLinksWidget {
+
+ /**
+ * @var integer
+ */
+ private static $maxInlineLimit = 500;
+
+ /**
+ * @since 3.0
+ *
+ * @param string $maxInlineLimit
+ */
+ public static function setMaxInlineLimit( $maxInlineLimit ) {
+ self::$maxInlineLimit = $maxInlineLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title,
+ * @param array $visibleLinks
+ *
+ * @return string
+ */
+ public static function topLinks( Title $title, $visibleLinks = [], $isEditMode = true ) {
+
+ if ( $visibleLinks === [] ) {
+ return '';
+ }
+
+ $lLinks = [];
+ $rLinks = [];
+
+ $lLinks['options'] = Html::rawElement(
+ 'a',
+ [
+ 'href' => '#options'
+ ],
+ Message::get( 'smw-ask-options', Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ $lLinks['search'] = Html::rawElement(
+ 'a',
+ [
+ 'href' => '#search'
+ ],
+ Message::get( 'smw-ask-search', Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ $lLinks['result'] = Html::rawElement(
+ 'a',
+ [
+ 'href' => '#result'
+ ],
+ Message::get( 'smw-ask-result', Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ $rLinks['empty'] = Html::rawElement(
+ 'a',
+ [
+ 'href' => $title->getLocalURL()
+ ],
+ Message::get( 'smw-ask-empty', Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ $rLinks['help'] = HtmlModal::link(
+ '<span class="smw-icon-info" style="padding: 0 0 3px 18px;background-position-x: center;"></span>',
+ [
+ 'data-id' => 'ask-help'
+ ]
+ );
+
+ $visibleLinks = array_flip( $visibleLinks );
+
+ foreach ( $lLinks as $key => $value ) {
+ if ( !isset( $visibleLinks[$key] ) ) {
+ unset( $lLinks[$key] );
+ }
+ }
+
+ foreach ( $rLinks as $key => $value ) {
+ if ( !isset( $visibleLinks[$key] ) ) {
+ unset( $rLinks[$key] );
+ }
+ }
+
+ $sep = Html::rawElement(
+ 'span',
+ [
+ 'style' => 'color:#aaa;font-size: 95%;margin-top: 2px;'
+ ],
+ '&#160;&#160;|&#160;&#160;'
+ );
+
+ $left = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'float-left'
+ ],
+ implode( "$sep", $lLinks )
+ );
+
+ $right = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'float-right'
+ ],
+ implode( "$sep", $rLinks )
+ );
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'id' => 'ask-toplinks',
+ 'class' => 'smw-ask-toplinks' . ( !$isEditMode ? ' hide-mode' : '' )
+ ],
+ $left . '&#160;' . $right
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'class' => 'clear-both'
+ ]
+ );
+
+ return $html;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title,
+ * @param UrlArgs $urlArgs
+ * @param integer $count
+ * @param boolean $hasFurtherResults
+ *
+ * @return string
+ */
+ public static function navigationLinks( Title $title, UrlArgs $urlArgs, $count, $hasFurtherResults = false ) {
+
+ if ( $count == 0 ) {
+ return '';
+ }
+
+ $urlArgs = clone $urlArgs;
+ $limit = $urlArgs->get( 'limit' );
+ $offset = $urlArgs->get( 'offset' );
+
+ // Remove any contents that is cruft
+ if ( strpos( $urlArgs->get( 'p' ), 'cl=' ) !== false ) {
+ $urlArgs->set( 'p', mb_substr( $urlArgs->get( 'p' ), stripos( $urlArgs->get( 'p' ), '/' ) + 1 ) );
+ }
+
+ $userLanguage = Localizer::getInstance()->getUserLanguage();
+
+ $html = '<b>' .
+ Message::get( 'smw_result_results', Message::TEXT, Message::USER_LANGUAGE ) . ' ' . $userLanguage->formatNum( $offset + 1 ) .
+ ' &#150; ' .
+ $userLanguage->formatNum( $offset + $count ) .
+ '</b>&#160;';
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'ask-pagination'
+ ],
+ ListPager::pagination( $title, $limit, $offset, $count, $urlArgs->toArray() + [ '_target' => '#search' ] , $html )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $navigation
+ * @param string $infoText
+ * @param Infolink|null $infoLink
+ * @param string $editHref
+ *
+ * @return string
+ */
+ public static function basicLinks( $navigation = '', Infolink $infoLink = null ) {
+
+ if ( $navigation === '' ) {
+ return '';
+ }
+
+ $downloadLink = DownloadLinksWidget::downloadLinks(
+ $infoLink
+ );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-ask-actions-nav'
+ ],
+ $navigation . $downloadLink
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParameterInput.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParameterInput.php
new file mode 100644
index 00000000..5a505ed3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParameterInput.php
@@ -0,0 +1,326 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use ParamProcessor\ParamDefinition;
+use Xml;
+
+/**
+ * Simple class to get a HTML input for the parameter.
+ * Usable for when creating a GUI from a parameter list.
+ *
+ * Based on 'addOptionInput' from Special:Ask in SMW 1.5.6.
+ *
+ * TODO: nicify HTML
+ *
+ * @since 1.9
+ *
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ParameterInput {
+
+ /**
+ * The parameter to print an input for.
+ *
+ * @since 1.9
+ *
+ * @var ParamDefinition
+ */
+ protected $param;
+
+ /**
+ * The current value for the parameter. When provided,
+ * it'll be used as value for the input, otherwise the
+ * parameters default value will be used.
+ *
+ * @since 1.9
+ *
+ * @var mixed: string or false
+ */
+ protected $currentValue;
+
+ /**
+ * Name for the input.
+ *
+ * @since 1.9
+ *
+ * @var string
+ */
+ protected $inputName;
+
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * Constructor.
+ *
+ * @since 1.9
+ *
+ * @param ParamDefinition $param
+ * @param mixed $currentValue
+ */
+ public function __construct( ParamDefinition $param, $currentValue = false ) {
+ $this->currentValue = $currentValue;
+ $this->inputName = $param->getName();
+ $this->param = $param;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @since 1.9
+ *
+ * @param mixed $currentValue
+ */
+ public function setCurrentValue( $currentValue ) {
+ $this->currentValue = $currentValue;
+ }
+
+ /**
+ * Sets the name for the input; defaults to the name of the parameter.
+ *
+ * @since 1.9
+ *
+ * @param string $name
+ */
+ public function setInputName( $name ) {
+ $this->inputName = $name;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ */
+ public function setAttributes( array $attributes ) {
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * Returns the HTML for the parameter input.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getHtml() {
+ $valueList = [];
+
+ if ( is_array( $this->param->getAllowedValues() ) ) {
+ $valueList = $this->param->getAllowedValues();
+ }
+
+ if ( $valueList === [] ) {
+ switch ( $this->param->getType() ) {
+ case 'char':
+ case 'float':
+ case 'integer':
+ case 'number':
+ $html = $this->getNumberInput();
+ break;
+ case 'boolean':
+ $html = $this->getBooleanInput();
+ break;
+ case 'string':
+ default:
+ $html = $this->getStrInput();
+ break;
+ }
+ } else {
+ $html = $this->param->isList() ? $this->getCheckboxListInput( $valueList ) : $this->getSelectInput( $valueList );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Returns the value to initially display with the input.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function getValueToUse() {
+ $value = $this->currentValue === false ? $this->param->getDefault() : $this->currentValue;
+
+ if ( $this->param->isList() && is_array( $value ) ) {
+ $value = implode( $this->param->getDelimiter(), $value );
+ }
+
+ // #1473
+ if ( $value === [] ) {
+ $value = '';
+ }
+
+ return $value;
+ }
+
+ /**
+ * Gets a short text input suitable for numbers.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function getNumberInput() {
+
+ $attributes = [
+ 'class' => 'parameter-number-input',
+ 'size' => 6,
+ 'style' => "width: 95%;"
+ ];
+
+ if ( $this->attributes !==[] ) {
+ $attributes = $this->attributes;
+ }
+
+ return Html::input(
+ $this->inputName,
+ $this->getValueToUse(),
+ 'text',
+ $attributes
+ );
+ }
+
+ /**
+ * Gets a text input for a string.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function getStrInput() {
+
+ $attributes = [
+ 'class' => 'parameter-string-input',
+ 'size' => 20,
+ 'style' => "width: 95%;"
+ ];
+
+ if ( $this->attributes !==[] ) {
+ $attributes = $this->attributes;
+ }
+
+ return Html::input(
+ $this->inputName,
+ $this->getValueToUse(),
+ 'text',
+ $attributes
+ );
+ }
+
+ /**
+ * Gets a checkbox.
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ protected function getBooleanInput() {
+
+ $attributes = [
+ 'class' => 'parameter-boolean-input'
+ ];
+
+ if ( $this->attributes !==[] ) {
+ $attributes = $this->attributes;
+ }
+
+ return Xml::check(
+ $this->inputName,
+ $this->getValueToUse(),
+ $attributes
+ );
+ }
+
+ /**
+ * Gets a select menu for the provided values.
+ *
+ * @since 1.9
+ *
+ * @param array $valueList
+ *
+ * @return string
+ */
+ protected function getSelectInput( array $valueList ) {
+ $options = [];
+ $options[] = '<option value=""></option>';
+
+ $currentValues = (array)$this->getValueToUse();
+ if ( is_null( $currentValues ) ) {
+ $currentValues = [];
+ }
+
+ foreach ( $valueList as $value ) {
+ $options[] =
+ '<option value="' . htmlspecialchars( $value ) . '"' .
+ ( in_array( $value, $currentValues ) ? ' selected="selected"' : '' ) . '>' . htmlspecialchars( $value ) .
+ '</option>';
+ }
+
+ return Html::rawElement(
+ 'select',
+ [
+ 'name' => $this->inputName,
+ 'class'=> 'parameter-select-input'
+ ],
+ implode( "\n", $options )
+ );
+ }
+
+ /**
+ * Gets a list of input boxes for the provided values.
+ *
+ * @since 1.9
+ *
+ * @param array $valueList
+ *
+ * @return string
+ */
+ protected function getCheckboxListInput( array $valueList ) {
+ $boxes = [];
+ $currentValues = [];
+
+ $values = $this->getValueToUse();
+
+ // List of comma separated values, see ParametersProcessor::getParameterList
+ if ( strpos( $values, ',' ) !== false ) {
+ $currentValues = array_flip(
+ array_map( 'trim', explode( ',', $values ) )
+ );
+ } elseif ( $values !== '' ) {
+ $currentValues[$values] = true;
+ }
+
+ foreach ( $valueList as $value ) {
+
+ // Use a value not a simple "true"
+ $attr = [
+ 'type' => 'checkbox',
+ 'name' => $this->inputName . '[]',
+ 'value' => $value
+ ];
+
+ $boxes[] = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'parameter-checkbox-input',
+ 'style' => 'white-space: nowrap; padding-right: 5px;'
+ ],
+ Html::rawElement(
+ 'input',
+ $attr + ( isset( $currentValues[$value] ) ? [ 'checked' ] : [] )
+ ) . Html::element( 'tt', [], $value )
+ );
+ }
+
+ return implode( "\n", $boxes );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersProcessor.php
new file mode 100644
index 00000000..19d86c2e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersProcessor.php
@@ -0,0 +1,273 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use SMWInfolink as Infolink;
+use SMWQueryProcessor as QueryProcessor;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParametersProcessor {
+
+ /**
+ * @var integer
+ */
+ private static $defaultLimit = 50;
+
+ /**
+ * @var integer
+ */
+ private static $maxInlineLimit = 500;
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $defaultLimit
+ */
+ public static function setDefaultLimit( $defaultLimit ) {
+ self::$defaultLimit = $defaultLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $maxInlineLimit
+ */
+ public static function setMaxInlineLimit( $maxInlineLimit ) {
+ self::$maxInlineLimit = $maxInlineLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param WebRequest $request
+ * @param array|null $params
+ *
+ * @return string
+ */
+ public static function process( WebRequest $request, $params ) {
+
+ // First make all inputs into a simple parameter list that can again be
+ // parsed into components later.
+ $parameterList = self::getParameterList( $request, $params );
+ $printouts = [];
+
+ // Check for q= query string, used whenever this special page calls
+ // itself (via submit or plain link):
+ if ( ( $q = $request->getText( 'q' ) ) !== '' ) {
+ $parameterList[] = $q;
+ }
+
+ // Parameters separated by newlines here (compatible with text-input for
+ // printouts)
+ if ( ( $po = $request->getText( 'po' ) ) !== '' ) {
+ $printouts = explode( "\n", $po );
+ }
+
+ // Check for param strings in po (printouts), appears in some links
+ // and in submits:
+ $parameterList = self::checkParameterList(
+ $request,
+ $parameterList,
+ $printouts
+ );
+
+ list( $queryString, $parameters, $printouts ) = QueryProcessor::getComponentsFromFunctionParams(
+ $parameterList,
+ false
+ );
+
+ unset( $parameters['cl'] );
+
+ // Try to complete undefined parameter values from dedicated URL params.
+ if ( !array_key_exists( 'format', $parameters ) ) {
+ $parameters['format'] = 'broadtable';
+ }
+
+ $sort_count = 0;
+ $empty_first_sort = false;
+
+ // First check whether the sorting options input send an
+ // request data as array
+ if ( ( $sort_values = $request->getArray( 'sort_num', [] ) ) !== [] ) {
+
+ // Find out whether something like `|?sort=,Has text` was used
+ if ( $sort_values[0] === '' ) {
+ $empty_first_sort = true;
+ }
+
+ if ( is_array( $sort_values ) ) {
+
+ // Filter all empty values
+ $sort = array_filter( $sort_values );
+ $sort_count = count( $sort );
+
+ // Add an empty element on the first position which got filter
+ // and was to prevent countless empty elements when no other sort
+ // was metioned
+ if ( $sort_count > 0 && $empty_first_sort ) {
+ array_unshift( $sort, '' );
+ $sort_count++;
+ }
+
+ $parameters['sort'] = implode( ',', $sort );
+ }
+ } elseif ( $request->getCheck( 'sort' ) ) {
+ $parameters['sort'] = $request->getVal( 'sort', '' );
+ }
+
+ // First check whether the order options input send an
+ // request data as array
+ if ( ( $order_values = $request->getArray( 'order_num', [] ) ) !== [] ) {
+
+ // Count doesn't match means we have a order from an
+ // empty (#subject) carrying around which we don't permit when
+ // sorting via columns
+ if ( is_array( $order_values ) && count( $order_values ) != $sort_count ) {
+ array_pop( $order_values );
+ }
+
+ if ( is_array( $order_values ) ) {
+ $order = array_filter( $order_values );
+ $parameters['order'] = implode( ',', $order );
+ }
+
+ } elseif ( $request->getCheck( 'order' ) ) {
+ $parameters['order'] = $request->getVal( 'order', '' );
+ } elseif ( !array_key_exists( 'order', $parameters ) ) {
+ $parameters['order'] = 'asc';
+ $parameters['sort'] = '';
+ }
+
+ if ( !array_key_exists( 'offset', $parameters ) ) {
+ $parameters['offset'] = $request->getVal( 'offset', 0 );
+ }
+
+ if ( !array_key_exists( 'limit', $parameters ) ) {
+ $parameters['limit'] = $request->getVal( 'limit', self::$defaultLimit );
+ }
+
+ $parameters['limit'] = min( $parameters['limit'], self::$maxInlineLimit );
+
+ return [ $queryString, $parameters, $printouts ];
+ }
+
+ private static function getParameterList( $request, $params ) {
+
+ // Called from wiki, get all parameters
+ if ( !$request->getCheck( 'q' ) ) {
+ return Infolink::decodeParameters( $params, true );
+ }
+
+ // Called by own Special, ignore full param string in that case
+ $query_val = $request->getVal( 'p' );
+
+ if ( !empty( $query_val ) ) {
+ // p is used for any additional parameters in certain links.
+ $parameterList = Infolink::decodeParameters( $query_val, false );
+ } else {
+ $query_values = $request->getArray( 'p' );
+
+ if ( is_array( $query_values ) ) {
+ foreach ( $query_values as $key => $val ) {
+ if ( empty( $val ) ) {
+ unset( $query_values[$key] );
+ }
+ }
+ }
+
+ // p is used for any additional parameters in certain links.
+ $parameterList = Infolink::decodeParameters( $query_values, false );
+ }
+
+ foreach ( $parameterList as $key => $value ) {
+ // Concatenate checkbox values into a simple comma separated list
+ if ( is_array( $value ) ) {
+ $parameterList[$key] = implode( ',', $value );
+ }
+ }
+
+ return $parameterList;
+ }
+
+ private static function checkParameterList( $request, $parameterList, $printouts ) {
+
+ // Add initial ? if omitted (all params considered as printouts)
+ foreach ( $printouts as $param ) {
+ $param = trim( $param );
+
+ if ( ( $param !== '' ) && ( $param { 0 } != '?' ) ) {
+ $param = '?' . $param;
+ }
+
+ $parameterList[] = $param;
+ }
+
+ $parameters = [];
+ unset( $parameterList['title'] );
+
+ // MW's internal token
+ unset( $parameterList['wpEditToken'] );
+
+ foreach ( $parameterList as $key => $value ) {
+ if ( self::hasPipe( $key, $value ) ) {
+
+ // #3523 `?TestAsk=[[Foo|Bar]]` replace `|`
+ if ( self::hasLink( $value ) ) {
+ $value = self::replace( '|', '0x7C', $value );
+ }
+
+ // #1407 Split: `?Has property=Foo|+index=1` into a [ '?Has property=Foo', '+index=1' ])
+ foreach ( explode( '|', $value ) as $k => $val ) {
+
+ // #3523 `?TestAsk=[[Foo|Bar]]|+index=1` decode
+ // the part that contains `0x7C`
+ if ( strpos( $val, '0x7C' ) !== false ) {
+ $val = self::replace( '0x7C', '|', $val );
+ }
+
+ $parameters[] = $k == 0 && $key{0} == '?' ? $key . '=' . $val : $val;
+ }
+ } elseif ( is_string( $key ) ) {
+ $parameters[$key] = $value;
+ } else {
+ $parameters[] = $value;
+ }
+ }
+
+ return $parameters;
+ }
+
+ private static function hasPipe( $key, $value ) {
+
+ if ( $key !== '' && $key{0} == '?' && strpos( $value, '|' ) !== false ) {
+ return true;
+ }
+
+ if ( is_string( $value ) && $value !== '' && $value{0} == '?' && strpos( $value, '|' ) !== false ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static function hasLink( $value ) {
+ return strpos( $value, '[[' ) !== false && strpos( $value, ']]' ) !== false ;
+ }
+
+ private static function replace( $source, $target, $value ) {
+ return preg_replace_callback(
+ '/\[\[([^\[\]]*)\]\]/xu',
+ function( array $matches ) use ( $source, $target ) {
+ return str_replace( [ $source ], [ $target ], $matches[0] );
+ },
+ $value
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersWidget.php
new file mode 100644
index 00000000..b465d588
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/ParametersWidget.php
@@ -0,0 +1,359 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use ParamProcessor\ParamDefinition;
+use SMW\Message;
+use SMW\Utils\HtmlDivTable;
+use SMWQueryProcessor as QueryProcessor;
+use Title;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class ParametersWidget {
+
+ /**
+ * @var boolean
+ */
+ private static $isTooltipDisplay = false;
+
+ /**
+ * @var integer
+ */
+ private static $defaultLimit = 50;
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isTooltipDisplay
+ */
+ public static function setTooltipDisplay( $isTooltipDisplay ) {
+ self::$isTooltipDisplay = (bool)$isTooltipDisplay;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $defaultLimit
+ */
+ public static function setDefaultLimit( $defaultLimit ) {
+ self::$defaultLimit = $defaultLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public static function fieldset( Title $title, array $parameters ) {
+
+ $toggle = Html::rawElement(
+ 'span',
+ [
+ 'style' => 'margin-left:5px;'
+ ],
+ '&#160;[' . Html::rawElement(
+ 'span',
+ [
+ 'class' => 'options-toggle-action',
+ ],
+ Html::rawElement(
+ 'label',
+ [
+ 'for' => 'options-toggle',
+ 'title' => Message::get( 'smw-section-expand', Message::TEXT, Message::USER_LANGUAGE )
+ ],
+ '+'
+ )
+ ) . ']&#160;'
+ );
+
+ $options = Html::rawElement(
+ 'div',
+ [
+ 'id' => 'parameter-title',
+ 'class' => 'strike'
+ ],
+ Html::rawElement(
+ 'span',
+ [],
+ Message::get( 'smw-ask-parameters', Message::TEXT, Message::USER_LANGUAGE ) . $toggle
+ )
+ ) . Html::rawElement(
+ 'div',
+ [],
+ '<input type="checkbox" id="options-toggle"/>' . Html::rawElement(
+ 'div',
+ [
+ 'id' => 'options-list',
+ 'class' => 'options-list'
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'options-parameter-list'
+ ],
+ self::parameterList( $parameters )
+ )
+ )
+ );
+
+ return Html::rawElement(
+ 'fieldset',
+ [],
+ Html::element(
+ 'legend',
+ [],
+ Message::get( 'smw-ask-options', Message::TEXT, Message::USER_LANGUAGE )
+ ). FormatListWidget::selectList(
+ $title,
+ $parameters
+ ) . $options . SortWidget::sortSection( $parameters )
+ );
+ }
+
+ /**
+ * Display a form section showing the options for a given format,
+ * based on the getParameters() value for that format's query printer.
+ *
+ * @since 1.8
+ *
+ * @param string $format
+ * @param array $parameters The current values for the parameters (name => value)
+ *
+ * @return string
+ */
+ public static function parameterList( array $values ) {
+
+ $format = 'broadtable';
+
+ if ( isset( $values['format'] ) ) {
+ $format = $values['format'];
+ }
+
+ $optionList = self::optionList(
+ QueryProcessor::getFormatParameters( $format ),
+ $values
+ );
+
+ $i = 0;
+ $n = 0;
+
+ $rowHtml = '';
+ $resultHtml = '';
+
+ // Top info text for a collapsed option box
+ if ( self::$isTooltipDisplay === true ){
+ $resultHtml .= Html::element(
+ 'div',
+ [
+ 'style' => 'margin-bottom:10px;'
+ ],
+ Message::get( 'smw-ask-otheroptions-info', Message::TEXT, Message::USER_LANGUAGE )
+ );
+ }
+
+ // Table
+ $resultHtml = HtmlDivTable::open(
+ [
+ 'class' => 'smw-ask-options-list',
+ 'width' => '100%'
+ ]
+ );
+
+ while ( $option = array_shift( $optionList ) ) {
+ $i++;
+
+ // Collect elements for a row
+ $rowHtml .= $option;
+
+ // Create table row
+ if ( $i % 3 == 0 ) {
+ $resultHtml .= HtmlDivTable::row(
+ $rowHtml,
+ [
+ 'class' => $i % 6 == 0 ? 'smw-ask-options-row-even' : 'smw-ask-options-row-odd',
+ ]
+ );
+ $rowHtml = '';
+ $n++;
+ }
+ }
+
+ // Ensure left over elements are collected as well
+ $resultHtml .= HtmlDivTable::row(
+ $rowHtml,
+ [
+ 'class' => $n % 2 == 0 ? 'smw-ask-options-row-odd' : 'smw-ask-options-row-even',
+ ]
+ );
+
+ $resultHtml .= HtmlDivTable::close();
+
+ return $resultHtml;
+ }
+
+ private static function optionList( $definitions, $values ) {
+
+ $html = [];
+
+ /**
+ * @var \ParamProcessor\ParamDefinition $definition
+ */
+ foreach ( $definitions as $name => $definition ) {
+
+ // Ignore the format parameter, as we got a special control in the GUI for it already.
+ if ( $name == 'format' ) {
+ continue;
+ }
+
+ // Handle sort, order separate as the generated checkbox are suboptimal, and the single
+ // field interferes with the GET request on multiple sort setters
+ if ( in_array( $name, [ 'sort', 'order' ] ) ) {
+ continue;
+ }
+
+ // Maybe there is a better way but somehow I couldn't find one therefore
+ // 'source' display will be omitted where no alternative source was found or
+ // a source that was marked as default but had no other available options
+ $allowedValues = $definition->getAllowedValues();
+
+ if ( $name == 'source' && (
+ count( $allowedValues ) == 0 ||
+ in_array( 'default', $allowedValues ) && count( $allowedValues ) < 2
+ ) ) {
+
+ continue;
+ }
+
+ $currentValue = false;
+
+ if ( array_key_exists( $name, $values ) ) {
+ $currentValue = $values[$name];
+ }
+
+ // Set default values
+ if ( $name === 'limit' && ( $currentValue === null || $currentValue === false ) ) {
+ $currentValue = self::$defaultLimit;
+ }
+
+ if ( $name === 'offset' && ( $currentValue === null || $currentValue === false ) ) {
+ $currentValue = 0;
+ }
+
+ $html[] = '<td>' . self::field( $definition, $name ) . '</td>' . self::input( $definition, $currentValue );
+ }
+
+ return $html;
+ }
+
+ private static function field( ParamDefinition $definition, $name ) {
+
+ $info = '';
+ $class = '';
+
+ if ( self::$isTooltipDisplay === true ) {
+ $class = 'smw-ask-info';
+ }
+
+ if ( $definition->getMessage() !== null ) {
+ $info = Message::get( $definition->getMessage(), Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ return HtmlDivTable::cell(
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => $class,
+ 'word-wrap' => 'break-word',
+ 'data-info' => $info
+ ],
+ htmlspecialchars( $name ) . ': '
+ ),
+ [
+ 'overflow' => 'hidden',
+ 'style' => 'border:none;'
+ ]
+ );
+ }
+
+ private static function input( ParamDefinition $definition, $currentValue ) {
+
+ $description = '';
+ $info = '';
+
+ $input = new ParameterInput( $definition );
+ $input->setInputName( 'p[' . $definition->getName() . ']' );
+ //$input->setInputClass( 'smw-ask-input-' . str_replace( ' ', '-', $definition->getName() ) );
+
+ $opts = $definition->getOptions();
+ $attributes = [];
+
+ if ( isset( $opts['style'] ) ) {
+ $attributes['style'] = $opts['style'];
+ }
+
+ if ( isset( $opts['size'] ) ) {
+ $attributes['size'] = $opts['size'];
+ }
+
+ // [ 'data-props' => [
+ // 'property' => Foo, 'value' => 'Bar', 'title-prefix' => 'false'
+ // ] ]
+ if ( isset( $opts['data-props'] ) && is_array( $opts['data-props'] ) ) {
+ foreach ( $opts['data-props'] as $key => $value ) {
+ if ( is_string( $key ) ) {
+ $attributes["data-$key"] = $value;
+ }
+ }
+ }
+
+ if ( isset( $opts['class'] ) ) {
+ $attributes['class'] = $opts['class'];
+ }
+
+ if ( $attributes !== [] ) {
+ $input->setAttributes( $attributes );
+ }
+
+ if ( $currentValue !== false ) {
+ $input->setCurrentValue( $currentValue );
+ }
+
+ // Parameters description text
+ if ( !self::$isTooltipDisplay ) {
+
+ if ( $definition->getMessage() !== null ) {
+ $info = Message::get( $definition->getMessage(), Message::PARSE, Message::USER_LANGUAGE );
+ }
+
+ $description = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-ask-parameter-description'
+ ],
+ '<br />' . $info
+ );
+ }
+
+ return HtmlDivTable::cell(
+ $input->getHtml() . $description,
+ [
+ 'overflow' => 'hidden',
+ 'style' => 'width:33%;border:none;'
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/QueryInputWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/QueryInputWidget.php
new file mode 100644
index 00000000..9f6cb532
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/QueryInputWidget.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+use SMW\Utils\HtmlDivTable;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryInputWidget {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $queryString
+ * @param string $printoutString
+ *
+ * @return string
+ */
+ public static function table( $queryString , $printoutString ) {
+
+ $table = HtmlDivTable::open( [ 'style' => "width: 100%;" ] );
+
+ $table .= HtmlDivTable::row(
+ HtmlDivTable::cell(
+ "<fieldset><legend>" . Message::get( 'smw_ask_queryhead', Message::TEXT, Message::USER_LANGUAGE ) . "</legend>" .
+ '<textarea id="ask-query-condition" class="smw-ask-query-condition" name="q" rows="6" placeholder="...">' .
+ htmlspecialchars( $queryString ) . '</textarea></fieldset>',
+ [ 'class' => 'smw-ask-condition slowfade' ]
+ ) . HtmlDivTable::cell(
+ '',
+ [
+ 'style' => 'width:10px; border:0px; padding: 0px;'
+ ]
+ ) . HtmlDivTable::cell(
+ "<fieldset><legend>" . Message::get( 'smw_ask_printhead', Message::TEXT, Message::USER_LANGUAGE ) . "</legend>" .
+ '<textarea id="smw-property-input" class="smw-ask-query-printout" name="po" rows="6" placeholder="...">' .
+ htmlspecialchars( $printoutString ) . '</textarea></fieldset>',
+ [ 'class' => 'smw-ask-printhead slowfade' ]
+ )
+ );
+
+ $table .= HtmlDivTable::close();
+
+ return $table;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/SortWidget.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/SortWidget.php
new file mode 100644
index 00000000..cafecb9f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/SortWidget.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+use Html;
+use SMW\Message;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortWidget {
+
+ /**
+ * @var boolean
+ */
+ private static $sortingSupport = false;
+
+ /**
+ * @var boolean
+ */
+ private static $randSortingSupport = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $sortingSupport
+ */
+ public static function setSortingSupport( $sortingSupport ) {
+ self::$sortingSupport = (bool)$sortingSupport;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $randSortingSupport
+ */
+ public static function setRandSortingSupport( $randSortingSupport ) {
+ self::$randSortingSupport = (bool)$randSortingSupport;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $params
+ *
+ * @return string
+ */
+ public static function sortSection( array $params ) {
+
+ if ( self::$sortingSupport === false ) {
+ return '';
+ }
+
+ if ( !array_key_exists( 'sort', $params ) || !array_key_exists( 'order', $params ) ) {
+ $orders = [];
+ $sorts = [];
+ } else {
+ $sorts = explode( ',', $params['sort'] );
+ $orders = explode( ',', $params['order'] );
+ reset( $sorts );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'options-sort',
+ 'class' => 'smw-ask-options-sort'
+ ], Html::rawElement(
+ 'div',
+ [
+ 'id' => 'sorting-title',
+ 'class' => 'strike'
+ ],
+ Html::rawElement(
+ 'span',
+ [],
+ Message::get( 'smw-ask-options-sort', Message::TEXT, Message::USER_LANGUAGE )
+ )
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'id' => 'sorting-input', 'class' => ''
+ ],
+ self::sortingOptions( $sorts, $orders )
+ )
+ );
+ }
+
+ private static function sortingOptions( array $sorts, array $orders ) {
+
+ $result = '';
+
+ foreach ( $orders as $i => $order ) {
+
+ if ( in_array( $order, [ 'ASC', 'asc', 'ascending' ] )) {
+ $order = 'asc';
+ }
+
+ if ( in_array( $order, [ 'DESC', 'desc', 'descending' ] )) {
+ $order = 'desc';
+ }
+
+ if ( in_array( $order, [ 'RAND', 'rand', 'random' ] )) {
+ $order = 'rand';
+ }
+
+ if ( !isset( $sorts[$i] ) ) {
+ $sorts[$i] = '';
+ }
+
+ $html = Html::rawElement(
+ 'input',
+ [
+ 'type' => 'text',
+ 'name' => "sort_num[]",
+ 'size' => '35',
+ 'class' => 'smw-property-input autocomplete-arrow',
+ 'value' => htmlspecialchars( $sorts[$i] )
+ ]
+ );
+
+ $html .= '<select name="order_num[]"><option ';
+
+ if ( $order == 'asc' ) {
+ $html .= 'selected="selected" ';
+ }
+
+ $html .= 'value="asc">' . Message::get( 'smw_ask_ascorder', Message::TEXT, Message::USER_LANGUAGE ) . '</option><option ';
+
+ if ( $order == 'desc' ) {
+ $html .= 'selected="selected" ';
+ }
+
+ $html .= 'value="desc">' . Message::get( 'smw_ask_descorder', Message::TEXT, Message::USER_LANGUAGE ) . "</option>";
+
+ if ( self::$randSortingSupport ) {
+ $html .= '<option ';
+
+ if ( $order == 'rand' ) {
+ $html .= 'selected="selected" ';
+ }
+
+ $html .= 'value="rand">' . Message::get( 'smw-ask-order-rand', Message::TEXT, Message::USER_LANGUAGE ) . '</option>';
+ }
+
+ $html .= '</select>';
+ $html .= '<span class="smw-ask-sort-delete"><a class="smw-ask-sort-delete-action" data-target="sort_div_' . $i . '" >' . Message::get( 'delete', Message::TEXT, Message::USER_LANGUAGE ) . '</a></span>';
+
+ $result .= Html::rawElement( 'div', [ 'id' => "sort_div_$i", 'class' => "smw-ask-sort-input" ], $html );
+ }
+
+ $result .= '<div id="sorting_starter" style="display: none"><input type="text" name="sort_num[]" size="35" class="smw-property-input autocomplete-arrow" />';
+ $result .= '<select name="order_num[]">' . "\n";
+ $result .= ' <option value="asc">' . Message::get( 'smw_ask_ascorder', Message::TEXT, Message::USER_LANGUAGE ) . "</option>\n";
+ $result .= ' <option value="desc">' . Message::get( 'smw_ask_descorder', Message::TEXT, Message::USER_LANGUAGE ) . "</option>\n";
+
+ if ( self::$randSortingSupport ) {
+ $result .= ' <option value="rand">' . Message::get( 'smw-ask-order-rand', Message::TEXT, Message::USER_LANGUAGE ) . "</option>\n";
+ }
+
+ $result .= "</select>";
+ $result .= "</div>";
+ $result .= '<div id="sorting_main"></div>' . "\n";
+
+ return $result . Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-ask-sort-add'
+ ],
+ Html::rawElement(
+ 'a',
+ [
+ 'class' => 'smw-ask-sort-add-action'
+ ],
+ Message::get( 'smw-ask-sort-add-action', Message::TEXT, Message::USER_LANGUAGE )
+ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/UrlArgs.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/UrlArgs.php
new file mode 100644
index 00000000..dee980a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Ask/UrlArgs.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Ask;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UrlArgs {
+
+ /**
+ * @var array
+ */
+ private $args = [];
+
+ /**
+ * @var array
+ */
+ private $fragment = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function set( $key, $value ) {
+ $this->args[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function get( $key ) {
+ return isset( $this->args[$key] ) ? $this->args[$key] : null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ */
+ public function delete( $key ) {
+ unset( $this->args[$key] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $fragment
+ */
+ public function setFragment( $fragment ) {
+ $this->fragment = $fragment;
+ }
+
+ /**
+ * @see __toString
+ */
+ public function toArray() {
+ return $this->args;
+ }
+
+ /**
+ * @see __toString
+ */
+ public function __toString() {
+ return wfArrayToCGI( $this->args ) . ( $this->fragment !== '' ? '#' . $this->fragment : '' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/FieldBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/FieldBuilder.php
new file mode 100644
index 00000000..49928301
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/FieldBuilder.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Browse;
+
+use Html;
+use SMW\Message;
+use SpecialPage;
+
+/**
+ * @private
+ *
+ * This class should eventually be injected instead of relying on static methods,
+ * for now this is the easiest way to unclutter the mammoth Browse class and
+ * splitting up responsibilities.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FieldBuilder {
+
+ /**
+ * Creates the query form in order to quickly switch to a specific article.
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public static function createQueryForm( $articletext = '' ) {
+
+ $title = SpecialPage::getTitleFor( 'Browse' );
+ $dir = $title->getPageLanguage()->isRTL() ? 'rtl' : 'ltr';
+
+ $html = "<div class=\"smwb-form\">". Html::rawElement(
+ 'div',
+ [ 'style' => 'margin-top:15px;' ],
+ ''
+ );
+
+ $html .= Html::rawElement(
+ 'form',
+ [
+ 'name' => 'smwbrowse',
+ 'action' => htmlspecialchars( $title->getLocalURL() ),
+ 'method' => 'get'
+ ],
+ Html::rawElement(
+ 'input',
+ [
+ 'type' => 'hidden',
+ 'name' => 'title',
+ 'value' => $title->getPrefixedText()
+ ],
+ Message::get( 'smw_browse_article', Message::ESCAPED, Message::USER_LANGUAGE )
+ ) .
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwb-input'
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'input-field'
+ ],
+ Html::rawElement(
+ 'input',
+ [
+ 'type' => 'text',
+ 'dir' => $dir,
+ 'name' => 'article',
+ 'size' => 40,
+ 'id' => 'smw-page-input',
+ 'class' => 'input smw-page-input autocomplete-arrow mw-ui-input',
+ 'value' => htmlspecialchars( $articletext )
+ ]
+ )
+ ) .
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'button-field'
+ ],
+ Html::rawElement(
+ 'input',
+ [
+ 'type' => 'submit',
+ 'class' => 'input-button mw-ui-button',
+ 'value' => Message::get( 'smw_browse_go', Message::ESCAPED, Message::USER_LANGUAGE )
+ ]
+ )
+ )
+ )
+ );
+
+ return $html . "</div>";
+ }
+
+ /**
+ * Creates the HTML for a link to this page, with some parameters set.
+ *
+ * @since 2.5
+ *
+ * @param string $linkMsg
+ * @param array $parameters
+ *
+ * @return string
+ */
+ public static function createLink( $linkMsg, array $parameters ) {
+
+ $title = SpecialPage::getSafeTitleFor( 'Browse' );
+ $fragment = $linkMsg === 'smw_browse_show_incoming' ? '#smw_browse_incoming' : '';
+
+ return Html::element(
+ 'a',
+ [
+ 'href' => $title->getLocalURL( $parameters ) . $fragment,
+ 'class' => $linkMsg
+ ],
+ Message::get( $linkMsg, Message::TEXT, Message::USER_LANGUAGE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/GroupFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/GroupFormatter.php
new file mode 100644
index 00000000..0a7885b1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/GroupFormatter.php
@@ -0,0 +1,237 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Browse;
+
+use Html;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\PropertySpecificationLookup;
+use SMWDataItem as DataItem;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class GroupFormatter {
+
+ /**
+ * Identifies a group label
+ */
+ const MESSAGE_GROUP_LABEL = 'smw-property-group-label-';
+
+ /**
+ * Identifies a group label
+ */
+ const MESSAGE_GROUP_DESCRIPTION = 'smw-property-group-description-';
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var boolean
+ */
+ private $showGroup = true;
+
+ /**
+ * @var string
+ */
+ private $lastGroup = '';
+
+ /**
+ * @var array
+ */
+ private $groupLinks = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertySpecificationLookup $propertySpecificationLookup
+ */
+ public function __construct( PropertySpecificationLookup $propertySpecificationLookup ) {
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $showGroup
+ */
+ public function showGroup( $showGroup ) {
+ $this->showGroup = $showGroup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isLastGroup( $group ) {
+ return $this->lastGroup === $group;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasGroups() {
+ return $this->groupLinks !== [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array &$properties
+ */
+ public function findGroupMembership( array &$properties ) {
+
+ $groupedProperties = [];
+ $this->groupLinks = [];
+
+ foreach ( $properties as $key => $property ) {
+
+ $group = $this->findGroup( $property );
+
+ if ( !isset( $groupedProperties[$group] ) ) {
+ $groupedProperties[$group] = [];
+ }
+
+ $groupedProperties[$group][] = $property;
+ }
+
+ ksort( $groupedProperties, SORT_NATURAL | SORT_FLAG_CASE );
+ $properties = $groupedProperties;
+
+ $keys = array_keys( $groupedProperties );
+ $this->lastGroup = end( $keys );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $group
+ *
+ * @return string
+ */
+ public function getGroupLink( $group ) {
+
+ if ( !isset( $this->groupLinks[$group] ) || $this->groupLinks[$group] === '' ) {
+ return $group;
+ }
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'class' => 'group-link'
+ ],
+ $this->groupLinks[$group]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param DIWikiPage $dataItem
+ *
+ * @return string
+ */
+ public function getMessageClassLink( $id, DIWikiPage $dataItem ) {
+
+ $gr = str_replace( '_', ' ', $dataItem->getDBKey() );
+ $key = mb_strtolower( str_replace( ' ', '-', $gr ) );
+
+ return Html::rawElement(
+ 'a',
+ [
+ 'href' => DIWikiPage::newFromText( $id . $key, NS_MEDIAWIKI )->getTitle()->getFullURL(),
+ 'class' => !Message::exists( $id . $key ) ? 'new' : ''
+ ],
+ $id . $key
+ );
+ }
+
+ private function findGroup( $property ) {
+
+ if ( $this->showGroup === false ) {
+ return '';
+ }
+
+ $group = null;
+
+ // Special handling for a `Category` property instance that itself cannot
+ // be annotated with a `Is property group` therefor use the fixed
+ // `smw-category-group` message to point to a group
+ if ( $property->getKey() === '_INST' && Message::exists( 'smw-category-group' ) ) {
+ $gr = Message::get( 'smw-category-group' );
+ } elseif( ( $group = $this->propertySpecificationLookup->getPropertyGroup( $property ) ) instanceof DataItem ) {
+ $gr = str_replace( '_', ' ', $group->getDBKey() );
+ } else {
+ return '';
+ }
+
+ $desc = '';
+ $link = '';
+
+ // Convention key to allow a category to transtable using the
+ // `smw-group-...` as key and transforms a group `Foo bar` to
+ // `smw-group-foo-bar`
+ $key = mb_strtolower( str_replace( ' ', '-', $gr ) );
+
+ if ( Message::exists( self::MESSAGE_GROUP_LABEL . $key ) ) {
+ $gr = Message::get(
+ self::MESSAGE_GROUP_LABEL . $key,
+ Message::TEXT,
+ Message::USER_LANGUAGE
+ );
+ }
+
+ if ( Message::exists( self::MESSAGE_GROUP_DESCRIPTION . $key ) ) {
+ $desc = Message::get(
+ self::MESSAGE_GROUP_DESCRIPTION . $key,
+ Message::TEXT,
+ Message::USER_LANGUAGE
+ );
+ }
+
+ if ( $group instanceof DataItem ) {
+ $link = Html::rawElement(
+ 'a',
+ [
+ 'href' => $group->getTitle()->getFullURL()
+ ],
+ $gr
+ );
+ }
+
+ if ( $desc !== '' ) {
+ $link = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-highlighter smwttinline',
+ 'data-state' => 'inline'
+ ],
+ $link . Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smwttcontent'
+ ],
+ $desc
+ )
+ );
+ }
+
+ if ( !isset( $this->groupLinks[$gr] ) || $this->groupLinks[$gr] === '' ) {
+ $this->groupLinks[$gr] = $link;
+ }
+
+ return $gr;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/HtmlBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/HtmlBuilder.php
new file mode 100644
index 00000000..9a8a71a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/HtmlBuilder.php
@@ -0,0 +1,947 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Browse;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+use SMW\Store;
+use SMW\Utils\HtmlDivTable;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Denny Vrandecic
+ * @author mwjames
+ */
+class HtmlBuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @var boolean
+ */
+ private $showoutgoing = true;
+
+ /**
+ * To display incoming values?
+ *
+ * @var boolean
+ */
+ private $showincoming = false;
+
+ /**
+ * At which incoming property are we currently?
+ * @var integer
+ */
+ private $offset = 0;
+
+ /**
+ * How many incoming values should be asked for
+ * @var integer
+ */
+ private $incomingValuesCount = 8;
+
+ /**
+ * How many outgoing values should be asked for
+ *
+ * @var integer
+ */
+ private $outgoingValuesCount = 200;
+
+ /**
+ * How many incoming properties should be asked for
+ * @var integer
+ */
+ private $incomingPropertiesCount = 21;
+
+ /**
+ * @var array
+ */
+ private $extraModules = [];
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param DIWikiPage $subject
+ */
+ public function __construct( Store $store, DIWikiPage $subject ) {
+ $this->store = $store;
+ $this->subject = $subject;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $options
+ */
+ public function setOptions( array $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getOptions() {
+ return $this->options;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( isset( $this->options[$key] ) ) {
+ return $this->options[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function legacy() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'data-subject' => $this->subject->getHash(),
+ 'data-options' => json_encode( $this->options )
+ ],
+ $this->buildHTML()
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function placeholder() {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwb-container',
+ 'data-subject' => $this->subject->getHash(),
+ 'data-options' => json_encode( $this->options )
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwb-status'
+ ],
+ Html::rawElement(
+ 'noscript',
+ [],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error',
+ ],
+ Message::get( 'smw-noscript', Message::PARSE )
+ )
+ )
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwb-emptysheet is-disabled'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-overlay-spinner large inline'
+ ]
+ ) . $this->buildEmptyHTML()
+ )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function buildHTML() {
+
+ if ( ( $offset = $this->getOption( 'offset' ) ) ) {
+ $this->offset = $offset;
+ }
+
+ $this->outgoingValuesCount = $this->getOption( 'valuelistlimit.out', 200 );
+
+ if ( $this->getOption( 'showAll' ) ) {
+ $this->incomingValuesCount = $this->getOption( 'valuelistlimit.in', 21 );
+ $this->incomingPropertiesCount = - 1;
+ $this->showoutgoing = true;
+ $this->showincoming = true;
+ }
+
+ if ( $this->getOption( 'dir' ) === 'both' || $this->getOption( 'dir' ) === 'in' ) {
+ $this->showincoming = true;
+ }
+
+ if ( $this->getOption( 'dir' ) === 'in' ) {
+ $this->showoutgoing = false;
+ }
+
+ if ( $this->getOption( 'dir' ) === 'out' ) {
+ $this->showincoming = false;
+ }
+
+ return $this->createHTML();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function buildEmptyHTML() {
+
+ $html = '';
+ $form = '';
+
+ $this->dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $this->subject
+ );
+
+ $semanticData = new SemanticData( $this->subject );
+ $this->articletext = $this->dataValue->getWikiValue();
+
+ if ( $this->getOption( 'showAll' ) ) {
+ $this->showoutgoing = true;
+ $this->showincoming = true;
+ }
+
+ $html .= $this->displayHead();
+ $html .= $this->displayActions();
+ $html .= $this->displayData( $semanticData, true, false, true );
+ $html .= $this->displayBottom( false );
+
+ if ( $this->getOption( 'printable' ) !== 'yes' && !$this->getOption( 'including' ) ) {
+ $form = FieldBuilder::createQueryForm( $this->articletext );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smwb-content'
+ ], $html
+ ) . $form;
+ }
+
+ /**
+ * Create and output HTML including the complete factbox, based on the extracted
+ * parameters in the execute comment.
+ */
+ private function createHTML() {
+
+ $html = "<div class=\"smwb-datasheet smwb-theme-light\">";
+
+ $leftside = true;
+ $modules = [];
+
+ $this->dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $this->subject
+ );
+
+ if ( !$this->dataValue->isValid() ) {
+ return $html;
+ }
+
+ $semanticData = new SemanticData(
+ $this->dataValue->getDataItem()
+ );
+
+ $html .= $this->displayHead();
+ $html .= $this->displayActions();
+
+ if ( $this->showoutgoing ) {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( $this->outgoingValuesCount + 1 );
+ $requestOptions->sort = true;
+
+ // Restrict the request otherwise the entire SemanticData record
+ // is fetched which can in case of a subject with a large
+ // subobject/subpage pool create excessive DB queries
+ $requestOptions->conditionConstraint = true;
+
+ $semanticData = $this->store->getSemanticData(
+ $this->dataValue->getDataItem(),
+ $requestOptions
+ );
+
+ $html .= $this->displayData( $semanticData, $leftside );
+ }
+
+ $html .= $this->displayCenter();
+
+ if ( $this->showincoming ) {
+ list( $indata, $more ) = $this->getInData();
+
+ if ( !$this->getOption( 'showInverse' ) ) {
+ $leftside = !$leftside;
+ }
+
+ $html .= $this->displayData( $indata, $leftside, true );
+ $html .= $this->displayBottom( $more );
+ }
+
+ $this->articletext = $this->dataValue->getWikiValue();
+ $html .= "</div>";
+
+ \Hooks::run(
+ 'SMW::Browse::AfterDataLookupComplete',
+ [
+ $this->store,
+ $semanticData,
+ &$html,
+ &$this->extraModules
+ ]
+ );
+
+ if ( $this->getOption( 'printable' ) !== 'yes' && !$this->getOption( 'including' ) ) {
+ $html .= FieldBuilder::createQueryForm( $this->articletext ) ;
+ }
+
+ $html .= Html::element(
+ 'div',
+ [
+ 'class' => 'smwb-modules',
+ 'data-modules' => json_encode( $this->extraModules )
+ ]
+ );
+
+ return $html;
+ }
+
+ /**
+ * Creates the HTML table displaying the data of one subject.
+ */
+ private function displayData( SemanticData $semanticData, $left = true, $incoming = false, $isLoading = false ) {
+
+ // Some of the CSS classes are different for the left or the right side.
+ // In this case, there is an "i" after the "smwb-". This is set here.
+ $dirPrefix = $left ? 'smwb-' : 'smwb-i';
+ $noresult = true;
+
+ $contextPage = $semanticData->getSubject();
+ $diProperties = $semanticData->getProperties();
+
+ $showGroup = $this->getOption( 'showGroup' ) && $this->getOption( 'group' ) !== 'hide';
+
+ $groupFormatter = new GroupFormatter(
+ ApplicationFactory::getInstance()->getPropertySpecificationLookup()
+ );
+
+ $groupFormatter->showGroup( $showGroup );
+ $groupFormatter->findGroupMembership( $diProperties );
+
+ $html = HtmlDivTable::open(
+ [
+ 'class' => "{$dirPrefix}factbox" . ( $groupFormatter->hasGroups() ? '' : ' smwb-bottom' )
+ ]
+ );
+
+ foreach ( $diProperties as $group => $properties ) {
+
+ if ( $group !== '' ) {
+
+ $c = HtmlDivTable::cell(
+ $groupFormatter->getGroupLink( $group ) . '<span></span>',
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ );
+
+ $html .= HtmlDivTable::close();
+ $html .= HtmlDivTable::open(
+ [
+ 'class' => "{$dirPrefix}factbox smwb-group"
+ ]
+ );
+
+ $html .= HtmlDivTable::row(
+ $c,
+ [
+ "class" => "{$dirPrefix}propvalue"
+ ]
+ );
+
+ $html .= HtmlDivTable::close();
+ $class = ( $groupFormatter->isLastGroup( $group ) ? ' smwb-bottom' : '' );
+
+ $html .= HtmlDivTable::open(
+ [
+ 'class' => "{$dirPrefix}factbox{$class}"
+ ]
+ );
+ }
+
+ $html .= $this->buildHtmlFromData(
+ $semanticData,
+ $properties,
+ $group,
+ $incoming,
+ $left,
+ $dirPrefix,
+ $noresult
+ );
+ }
+
+ if ( !$isLoading && !$incoming && $showGroup ) {
+ $html .= $this->getGroupMessageClassLinks(
+ $groupFormatter,
+ $semanticData,
+ $dirPrefix
+ );
+ }
+
+ if ( $noresult ) {
+ $noMsgKey = $incoming ? 'smw_browse_no_incoming' : 'smw_browse_no_outgoing';
+
+ $rColumn = HtmlDivTable::cell(
+ '',
+ [
+ "class" => 'smwb-cell smwb-prophead'
+ ]
+ );
+
+ $lColumn = HtmlDivTable::cell(
+ wfMessage( $isLoading ? 'smw-browse-from-backend' : $noMsgKey )->escaped(),
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ );
+
+ $html .= HtmlDivTable::row(
+ ( $left ? ( $rColumn . $lColumn ):( $lColumn . $rColumn ) ),
+ [
+ "class" => "{$dirPrefix}propvalue"
+ ]
+ );
+ }
+
+ $html .= HtmlDivTable::close();
+
+ return $html;
+ }
+
+ /**
+ * Builds HTML content that matches a group of properties and creates the
+ * display of assigned values.
+ */
+ private function buildHtmlFromData( $semanticData, $properties, $group, $incoming, $left, $dirPrefix, &$noresult ) {
+
+ $html = '';
+ $group = mb_strtolower( str_replace( ' ', '-', $group ) );
+
+ $contextPage = $semanticData->getSubject();
+ $showInverse = $this->getOption( 'showInverse' );
+ $showSort = $this->getOption( 'showSort' );
+
+ $comma = Message::get(
+ 'comma-separator',
+ Message::ESCAPED,
+ Message::USER_LANGUAGE
+ );
+
+ $and = Message::get(
+ 'and',
+ Message::ESCAPED,
+ Message::USER_LANGUAGE
+ );
+
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ foreach ( $properties as $diProperty ) {
+
+ $dvProperty = $dataValueFactory->newDataValueByItem(
+ $diProperty,
+ null
+ );
+
+ $dvProperty->setContextPage(
+ $contextPage
+ );
+
+ $propertyLabel = ValueFormatter::getPropertyLabel(
+ $dvProperty,
+ $incoming,
+ $showInverse
+ );
+
+ // Make the sortkey visible which is otherwise hidden from the user
+ if ( $showSort && $diProperty->getKey() === '_SKEY' ) {
+ $propertyLabel = Message::get( 'smw-property-predefined-label-skey', Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ if ( $propertyLabel === null ) {
+ continue;
+ }
+
+ $head = HtmlDivTable::cell(
+ $propertyLabel,
+ [
+ "class" => 'smwb-cell smwb-prophead' . ( $group !== '' ? " smwb-group-$group" : '' )
+ ]
+ );
+
+ $values = $semanticData->getPropertyValues( $diProperty );
+
+ if ( $incoming && ( count( $values ) >= $this->incomingValuesCount ) ) {
+ $moreIncoming = true;
+ $moreOutgoing = false;
+ array_pop( $values );
+ } elseif ( !$incoming && ( count( $values ) >= $this->outgoingValuesCount ) ) {
+ $moreIncoming = false;
+ $moreOutgoing = true;
+ array_pop( $values );
+ } else {
+ $moreIncoming = false;
+ $moreOutgoing = false;
+ }
+
+ $list = [];
+ $value_html = '';
+
+ foreach ( $values as $dataItem ) {
+ if ( $incoming ) {
+ $dv = $dataValueFactory->newDataValueByItem( $dataItem, null );
+ } else {
+ $dv = $dataValueFactory->newDataValueByItem( $dataItem, $diProperty );
+ }
+
+ $list[] = Html::rawElement(
+ 'span',
+ [
+ 'class' => "{$dirPrefix}value"
+ ],
+ ValueFormatter::getFormattedValue( $dv, $dvProperty, $incoming )
+ );
+ }
+
+ $last = array_pop( $list );
+ $value_html = implode( $comma, $list );
+
+ if ( ( $moreOutgoing || $moreIncoming ) && $last !== '' ) {
+ $value_html .= $comma . $last;
+ } elseif( $list !== [] && $last !== '' ) {
+ $value_html .= '&nbsp;' . $and . '&nbsp;' . $last;
+ } else {
+ $value_html .= $last;
+ }
+
+ $hook = false;
+
+ if ( $moreIncoming ) {
+ // Added in 2.3
+ // link to the remaining incoming pages
+ $hook = \Hooks::run(
+ 'SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate',
+ [
+ $diProperty,
+ $contextPage,
+ &$value_html,
+ $this->store
+ ]
+ );
+ }
+
+ if ( $hook ) {
+ $value_html .= Html::element(
+ 'a',
+ [
+ 'href' => \SpecialPage::getSafeTitleFor( 'SearchByProperty' )->getLocalURL( [
+ 'property' => $dvProperty->getWikiValue(),
+ 'value' => $this->dataValue->getWikiValue()
+ ] )
+ ],
+ wfMessage( 'smw_browse_more' )->text()
+ );
+ }
+
+ if ( $moreOutgoing ) {
+ $value_html .= Html::element(
+ 'a',
+ [
+ 'href' => \SpecialPage::getSafeTitleFor( 'PageProperty' )->getLocalURL( [
+ 'type' => $dvProperty->getWikiValue(),
+ 'from' => $this->dataValue->getWikiValue()
+ ] )
+ ],
+ wfMessage( 'smw_browse_more' )->text()
+ );
+ }
+
+ $body = HtmlDivTable::cell(
+ $value_html,
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ );
+
+ // display row
+ $html .= HtmlDivTable::row(
+ ( $left ? ( $head . $body ) : ( $body . $head ) ),
+ [
+ "class" => "{$dirPrefix}propvalue"
+ ]
+ );
+
+ $noresult = false;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Displays the subject that is currently being browsed to.
+ */
+ private function displayHead() {
+ return HtmlDivTable::table(
+ HtmlDivTable::row(
+ ValueFormatter::getFormattedSubject( $this->dataValue ),
+ [
+ 'class' => 'smwb-title'
+ ]
+ ),
+ [
+ 'class' => 'smwb-factbox'
+ ]
+ );
+ }
+
+ /**
+ * Creates the HTML for the center bar including the links with further
+ * navigation options.
+ */
+ private function displayActions() {
+
+ $html = '';
+ $group = $this->getOption( 'group' );
+ $article = $this->dataValue->getLongWikiText();
+
+ if ( $this->getOption( 'showGroup' ) ) {
+
+ if ( $group === 'hide' ) {
+ $parameters = [
+ 'offset' => 0,
+ 'dir' => $this->showincoming ? 'both' : 'out',
+ 'article' => $article,
+ 'group' => 'show'
+ ];
+
+ $linkMsg = 'smw-browse-show-group';
+ } else {
+ $parameters = [
+ 'offset' => $this->offset,
+ 'dir' => $this->showincoming ? 'both' : 'out',
+ 'article' => $article,
+ 'group' => 'hide'
+ ];
+
+ $linkMsg = 'smw-browse-hide-group';
+ }
+
+ $html .= FieldBuilder::createLink( $linkMsg, $parameters );
+ $html .= '<span class="smwb-action-separator">&nbsp;</span>';
+ }
+
+ if ( $this->showoutgoing ) {
+
+ if ( $this->showincoming ) {
+ $parameters = [
+ 'offset' => 0,
+ 'dir' => 'out',
+ 'article' => $article,
+ 'group' => $group
+ ];
+
+ $linkMsg = 'smw_browse_hide_incoming';
+ } else {
+ $parameters = [
+ 'offset' => $this->offset,
+ 'dir' => 'both',
+ 'article' => $article,
+ 'group' => $group
+ ];
+
+ $linkMsg = 'smw_browse_show_incoming';
+ }
+
+ $html .= FieldBuilder::createLink( $linkMsg, $parameters );
+ }
+
+ return HtmlDivTable::table(
+ HtmlDivTable::row(
+ $html . "&#160;\n",
+ [
+ 'class' => 'smwb-actions'
+ ]
+ ),
+ [
+ 'class' => 'smwb-factbox'
+ ]
+ );
+ }
+
+ private function displayCenter() {
+ return HtmlDivTable::table(
+ HtmlDivTable::row(
+ "&#160;\n",
+ [
+ 'class' => 'smwb-center'
+ ]
+ ),
+ [
+ 'class' => 'smwb-factbox'
+ ]
+ );
+ }
+
+ /**
+ * Creates the HTML for the bottom bar including the links with further
+ * navigation options.
+ */
+ private function displayBottom( $more ) {
+
+ $article = $this->dataValue->getLongWikiText();
+
+ $open = HtmlDivTable::open(
+ [
+ 'class' => 'smwb-factbox'
+ ]
+ );
+
+ $html = HtmlDivTable::row(
+ '&#160;',
+ [
+ 'class' => 'smwb-center'
+ ]
+ );
+
+ $close = HtmlDivTable::close();
+
+ if ( $this->getOption( 'showAll' ) ) {
+ return $open . $html . $close;
+ }
+
+ if ( ( $this->offset > 0 ) || $more ) {
+ $offset = max( $this->offset - $this->incomingPropertiesCount + 1, 0 );
+
+ $parameters = [
+ 'offset' => $offset,
+ 'dir' => $this->showoutgoing ? 'both' : 'in',
+ 'article' => $article
+ ];
+
+ $linkMsg = 'smw_result_prev';
+
+ $html .= ( $this->offset == 0 ) ? wfMessage( $linkMsg )->escaped() : FieldBuilder::createLink( $linkMsg, $parameters );
+
+ $offset = $this->offset + $this->incomingPropertiesCount - 1;
+
+ $parameters = [
+ 'offset' => $offset,
+ 'dir' => $this->showoutgoing ? 'both' : 'in',
+ 'article' => $article
+ ];
+
+ $linkMsg = 'smw_result_next';
+
+ $html .= " &#160;&#160;&#160; <strong>" . wfMessage( 'smw_result_results' )->escaped() . " " . ( $this->offset + 1 ) .
+ " – " . ( $offset ) . "</strong> &#160;&#160;&#160; ";
+ $html .= $more ? FieldBuilder::createLink( $linkMsg, $parameters ) : wfMessage( $linkMsg )->escaped();
+
+ $html = HtmlDivTable::row(
+ $html,
+ [
+ 'class' => 'smwb-center'
+ ]
+ );
+ }
+
+ return $open . $html . $close;
+ }
+
+ /**
+ * Creates a Semantic Data object with the incoming properties instead of the
+ * usual outgoing properties.
+ */
+ private function getInData() {
+
+ $indata = new SemanticData(
+ $this->dataValue->getDataItem()
+ );
+
+ $propRequestOptions = new RequestOptions();
+ $propRequestOptions->sort = true;
+ $propRequestOptions->setLimit( $this->incomingPropertiesCount );
+
+ if ( $this->offset > 0 ) {
+ $propRequestOptions->offset = $this->offset;
+ }
+
+ $incomingProperties = $this->store->getInProperties(
+ $this->dataValue->getDataItem(),
+ $propRequestOptions
+ );
+
+ $more = false;
+
+ if ( count( $incomingProperties ) == $this->incomingPropertiesCount ) {
+ $more = true;
+
+ // drop the last one
+ array_pop( $incomingProperties );
+ }
+
+ $valRequestOptions = new RequestOptions();
+ $valRequestOptions->sort = true;
+ $valRequestOptions->setLimit( $this->incomingValuesCount );
+
+ foreach ( $incomingProperties as $property ) {
+
+ $values = $this->store->getPropertySubjects(
+ $property,
+ $this->dataValue->getDataItem(),
+ $valRequestOptions
+ );
+
+ foreach ( $values as $dataItem ) {
+ $indata->addPropertyObjectValue( $property, $dataItem );
+ }
+ }
+
+ // Added in 2.3
+ // Whether to show a more link or not can be set via
+ // SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate
+ \Hooks::run(
+ 'SMW::Browse::AfterIncomingPropertiesLookupComplete',
+ [
+ $this->store,
+ $indata,
+ $valRequestOptions
+ ]
+ );
+
+ return [ $indata, $more ];
+ }
+
+ /**
+ * Returns HTML fragments for message classes in connection with categories
+ * linked to a property group.
+ */
+ private function getGroupMessageClassLinks( $groupFormatter, $semanticData, $dirPrefix ) {
+
+ $contextPage = $semanticData->getSubject();
+
+ if ( $contextPage->getNamespace() !== NS_CATEGORY || !$semanticData->hasProperty( new DIProperty( '_PPGR' ) ) ) {
+ return '';
+ }
+
+ $group = '';
+ $html = '';
+
+ $list = [
+ 'label' => $groupFormatter->getMessageClassLink(
+ GroupFormatter::MESSAGE_GROUP_LABEL,
+ $contextPage
+ ),
+ 'description' => $groupFormatter->getMessageClassLink(
+ GroupFormatter::MESSAGE_GROUP_DESCRIPTION,
+ $contextPage
+ )
+ ];
+
+ foreach ( $list as $k => $val ) {
+
+ if ( $val === '' ) {
+ continue;
+ }
+
+ $h = HtmlDivTable::cell(
+ wfMessage( 'smw-browse-property-group-' . $k )->text(),
+ [
+ "class" => 'smwb-cell smwb-prophead'
+ ]
+ ) . HtmlDivTable::cell(
+ $val,
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ );
+
+ $group .= HtmlDivTable::row(
+ $h,
+ [
+ "class" => "{$dirPrefix}propvalue"
+ ]
+ );
+ }
+
+ if ( $group !== '' ) {
+ $h = HtmlDivTable::cell(
+ wfMessage( 'smw-browse-property-group-title' )->text(),
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ ) . HtmlDivTable::cell(
+ '',
+ [
+ "class" => 'smwb-cell smwb-propval'
+ ]
+ );
+
+ $html = HtmlDivTable::row(
+ $h,
+ [
+ "class" => "{$dirPrefix}propvalue smwb-group-links"
+ ]
+ ) . $group;
+ }
+
+ return $html;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/ValueFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/ValueFormatter.php
new file mode 100644
index 00000000..98a87e48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/Browse/ValueFormatter.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\Browse;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\PropertyValue;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMWDataValue as DataValue;
+use SMWInfolink as Infolink;
+
+/**
+ * @private
+ *
+ * This class should eventually be injected instead of relying on static methods,
+ * for now this is the easiest way to unclutter the mammoth Browse class and
+ * splitting up responsibilities.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ValueFormatter {
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValue $value
+ *
+ * @return string
+ */
+ public static function getFormattedSubject( DataValue $dataValue ) {
+
+ $extra = '';
+
+ if ( $dataValue->getDataItem()->getNamespace() === SMW_NS_PROPERTY ) {
+
+ $dv = DataValueFactory::getInstance()->newDataValueByItem(
+ DIProperty::newFromUserLabel( $dataValue->getDataItem()->getDBKey() )
+ );
+
+ $label = $dv->getFormattedLabel( DataValueFormatter::WIKI_LONG );
+
+ // Those with a formatted displayTitle
+ // foaf:homepage&nbsp;<span style="font-size:small;">(Foaf:homepage)</span>
+ if ( strpos( $label, '&nbsp;<span' ) !== false ) {
+ list( $label, $extra ) = explode( '&nbsp;', $label );
+ $extra = '&nbsp;' . $extra;
+ }
+
+ $dataValue->setCaption( $label );
+ }
+
+ return $dataValue->getLongHTMLText( smwfGetLinker() ) . $extra;
+ }
+
+ /**
+ * Displays a value, including all relevant links (browse and search by property)
+ *
+ * @since 2.5
+ *
+ * @param DataValue $value
+ * @param PropertyValue $property
+ * @param boolean $incoming
+ *
+ * @return string
+ */
+ public static function getFormattedValue( DataValue $dataValue, PropertyValue $propertyValue, $incoming = false, $user = null ) {
+
+ $linker = smwfGetLinker();
+ $dataItem = $dataValue->getContextPage();
+
+ // Allow the DV formatter to access a specific language code
+ $dataValue->setOption(
+ DataValue::OPT_CONTENT_LANGUAGE,
+ Localizer::getInstance()->getPreferredContentLanguage( $dataItem )->getCode()
+ );
+
+ $dataValue->setOption(
+ DataValue::OPT_USER_LANGUAGE,
+ Localizer::getInstance()->getUserLanguage()->getCode()
+ );
+
+ $outputFormat = $dataValue->getOutputFormat();
+
+ if ( $outputFormat === false ) {
+ $outputFormat = 'LOCL';
+
+ if ( Localizer::getInstance()->hasLocalTimeOffsetPreference( $user ) ) {
+ $outputFormat .= '#TO';
+ }
+ }
+
+ // Use LOCL formatting where appropriate (date)
+ $dataValue->setOutputFormat( $outputFormat );
+
+ // For a redirect, disable the DisplayTitle to show the original (aka source) page
+ if ( $propertyValue->isValid() && $propertyValue->getDataItem()->getKey() == '_REDI' ) {
+ $dataValue->setOption( 'smwgDVFeatures', ( $dataValue->getOption( 'smwgDVFeatures' ) & ~SMW_DV_WPV_DTITLE ) );
+ }
+
+ $html = $dataValue->getLongHTMLText( $linker );
+
+ if ( $dataValue->getOption( DataValue::OPT_DISABLE_INFOLINKS, false ) === true ) {
+ return $html;
+ }
+
+ $isCompactLink = $dataValue->getOption( DataValue::OPT_COMPACT_INFOLINKS, false );
+ $noInfolinks = [ '_INST', '_SKEY' ];
+
+ if ( in_array( $dataValue->getTypeID(), [ '_wpg', '_wpp', '__sob'] ) ) {
+ $infolink = Infolink::newBrowsingLink( '+', $dataValue->getLongWikiText() );
+ $infolink->setCompactLink( $isCompactLink );
+ $html .= "&#160;" . $infolink->getHTML( $linker );
+ } elseif ( $incoming && $propertyValue->isVisible() ) {
+ $infolink = Infolink::newInversePropertySearchLink( '+', $dataValue->getTitle(), $propertyValue->getDataItem()->getLabel(), 'smwsearch' );
+ $infolink->setCompactLink( $isCompactLink );
+ $html .= "&#160;" . $infolink->getHTML( $linker );
+ } elseif ( $dataValue->getProperty() instanceof DIProperty && !in_array( $dataValue->getProperty()->getKey(), $noInfolinks ) ) {
+ $html .= $dataValue->getInfolinkText( SMW_OUTPUT_HTML, $linker );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Figures out the label of the property to be used. For outgoing ones it is just
+ * the text, for incoming ones we try to figure out the inverse one if needed,
+ * either by looking for an explicitly stated one or by creating a default one.
+ *
+ * @since 2.5
+ *
+ * @param PropertyValue $property
+ * @param boolean $incoming
+ * @param boolean $showInverse
+ *
+ * @return string
+ */
+ public static function getPropertyLabel( PropertyValue $propertyValue, $incoming = false, $showInverse = false ) {
+
+ $proptext = null;
+
+ $linker = smwfGetLinker();
+ $property = $propertyValue->getDataItem();
+
+ if ( $propertyValue->isVisible() ) {
+ $propertyValue->setCaption( self::findPropertyLabel( $propertyValue, $incoming, $showInverse ) );
+ $proptext = $propertyValue->getShortHTMLText( $linker ) . "\n";
+ } elseif ( $property->getKey() == '_INST' ) {
+ $proptext = $linker->specialLink( 'Categories', 'smw-category' );
+ } elseif ( $property->getKey() == '_REDI' ) {
+ $proptext = $linker->specialLink( 'Listredirects', 'isredirect' );
+ }
+
+ return $proptext;
+ }
+
+ private static function findPropertyLabel( PropertyValue $propertyValue, $incoming = false, $showInverse = false ) {
+
+ $property = $propertyValue->getDataItem();
+ $contextPage = $propertyValue->getContextPage();
+
+ // Change caption for the incoming, Has query instance
+ if ( $incoming && $property->getKey() === '_ASK' && strpos( $contextPage->getSubobjectName(), '_QUERY' ) === false ) {
+ return self::addNonBreakingSpace( wfMessage( 'smw-query-reference-link-label' )->text() );
+ }
+
+ if ( !$incoming || !$showInverse ) {
+ return self::addNonBreakingSpace( $propertyValue->getWikiValue() );
+ }
+
+ $inverseProperty = DataValueFactory::getInstance()->newPropertyValueByLabel( wfMessage( 'smw_inverse_label_property' )->text() );
+
+ $dataItems = ApplicationFactory::getInstance()->getStore()->getPropertyValues(
+ $property->getDiWikiPage(),
+ $inverseProperty->getDataItem()
+ );
+
+ if ( $dataItems !== [] ) {
+ $text = str_replace( '_', ' ', end( $dataItems )->getDBKey() );
+ } else {
+ $text = wfMessage( 'smw_inverse_label_default', $propertyValue->getWikiValue() )->text();
+ }
+
+ return self::addNonBreakingSpace( $text );
+ }
+
+ /**
+ * Replace the last two space characters with unbreakable spaces for beautification.
+ *
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public static function addNonBreakingSpace( $text ) {
+
+ $nonBreakingSpace = html_entity_decode( '&#160;', ENT_NOQUOTES, 'UTF-8' );
+ $text = preg_replace( '/[\s]/u', $nonBreakingSpace, $text, - 1, $count );
+
+ if ( $count > 2) {
+ return preg_replace( '/($nonBreakingSpace)/u', ' ', $text, max( 0, $count - 2 ) );
+ }
+
+ return $text;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PageProperty/PageBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PageProperty/PageBuilder.php
new file mode 100644
index 00000000..adf8ec0b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PageProperty/PageBuilder.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\PageProperty;
+
+use Html;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use SMW\Options;
+use SMWInfolink as Infolink;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PageBuilder {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var Linker
+ */
+ private $linker;
+
+ /**
+ * @since 3.0
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param Options $options
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, Options $options ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->options = $options;
+ $this->linker = smwfGetLinker();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $count
+ *
+ * @return string
+ */
+ public function buildForm( $count = 0 ) {
+
+ $html = Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ Message::get( 'smw-special-pageproperty-description', Message::PARSE, Message::USER_LANGUAGE )
+ );
+
+ $html .= $this->createForm( $count );
+
+ $html .= Html::element(
+ 'h2',
+ [],
+ Message::get( 'smw-sp-searchbyproperty-resultlist-header', Message::PARSE, Message::USER_LANGUAGE )
+ );
+
+ return $html;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage[]|[] $results
+ *
+ * @return string
+ */
+ public function buildHtml( array $results ) {
+
+ if ( count( $results ) == 0 ) {
+ return Message::get( 'smw_result_noresults', Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ $limit = $this->options->get( 'limit' );
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ $propertyValue = $dataValueFactory->newPropertyValueByLabel(
+ $this->options->get( 'property' )
+ );
+
+ $property = $propertyValue->getDataItem();
+
+ $isBrowsableType = DataTypeRegistry::getInstance()->isBrowsableType(
+ $property->findPropertyTypeID()
+ );
+
+ $list = [];
+ $count = $limit + 1;
+
+ foreach ( $results as $dataItem ) {
+ $count--;
+ $link = '';
+
+ if ( $count < 1 ) {
+ continue;
+ }
+
+ $dataValue = $dataValueFactory->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $link = $dataValue->getLongHTMLText( $this->linker );
+
+ if ( $isBrowsableType && $this->options->safeGet( 'from', '' ) !== '' ) {
+ $val = $dataValue->getLongWikiText();
+ $infolink = Infolink::newBrowsingLink( '+', $val );
+ $infolink->setLinkAttributes( [ 'title' => $val ] );
+ } else {
+ $val = $dataValue->getWikiValue();
+ $infolink = Infolink::newPropertySearchLink( '+', $property->getLabel(), $val );
+ $infolink->setLinkAttributes( [ 'title' => $val ] );
+ }
+
+ $link .= '&#160;&#160;' . $infolink->getHTML( $this->linker );
+ $list[] = $link;
+ }
+
+ return Html::rawElement(
+ 'ul',
+ [],
+ '<li>' . implode('</li><li>', $list ) . '</li>'
+ );
+ }
+
+ private function createForm( $count ) {
+
+ // Precaution to avoid any inline breakage caused by a div element
+ // within a paragraph (e.g Highlighter content)
+ // $resultMessage = str_replace( 'div', 'span', $resultMessage );
+
+ $this->htmlFormRenderer
+ ->setName( 'pageproperty' )
+ ->withFieldset()
+ ->addParagraph( Message::get( 'smw_pp_docu', Message::TEXT, Message::USER_LANGUAGE ) )
+ ->addPaging(
+ $this->options->safeGet( 'limit', 20 ),
+ $this->options->safeGet( 'offset', 0 ),
+ $count )
+ ->addHorizontalRule()
+ ->openElement( 'div', [ 'class' => 'smw-special-pageproperty-input' ] )
+ ->addInputField(
+ Message::get( 'smw_pp_from', Message::TEXT, Message::USER_LANGUAGE ),
+ 'from',
+ $this->options->safeGet( 'from', '' ),
+ 'smw-article-input',
+ 30,
+ [ 'class' => 'is-disabled' ] )
+ ->addNonBreakingSpace()
+ ->addInputField(
+ Message::get( 'smw_sbv_property', Message::TEXT, Message::USER_LANGUAGE ),
+ 'type',
+ $this->options->safeGet( 'type', '' ),
+ 'smw-property-input',
+ 20,
+ [ 'class' => 'is-disabled' ] )
+ ->addNonBreakingSpace()
+ ->addSubmitButton( Message::get( 'smw_sbv_submit', Message::TEXT, Message::USER_LANGUAGE ) )
+ ->closeElement( 'div' );
+
+ return $this->htmlFormRenderer->renderForm();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilder.php
new file mode 100644
index 00000000..c0efb3ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilder.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\PropertyLabelSimilarity;
+
+use Html;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Message;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ContentsBuilder {
+
+ /**
+ * @var PropertyLabelSimilarityLookup
+ */
+ private $propertyLabelSimilarityLookup;
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @since 2.5
+ *
+ * @param PropertyLabelSimilarityLookup $propertyLabelSimilarityLookup
+ * @param HtmlFormRenderer $htmlFormRenderer
+ */
+ public function __construct( PropertyLabelSimilarityLookup $propertyLabelSimilarityLookup, HtmlFormRenderer $htmlFormRenderer ) {
+ $this->propertyLabelSimilarityLookup = $propertyLabelSimilarityLookup;
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param RequestOptions $requestOption
+ */
+ public function getHtml( RequestOptions $requestOptions ) {
+
+ $threshold = 90;
+ $type = '';
+
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ if ( isset( $extraCondition['type'] ) ) {
+ $type = $extraCondition['type'];
+ }
+
+ if ( isset( $extraCondition['threshold'] ) ) {
+ $threshold = $extraCondition['threshold'];
+ }
+ }
+
+ $this->propertyLabelSimilarityLookup->setThreshold(
+ $threshold
+ );
+
+ $result = $this->propertyLabelSimilarityLookup->compareAndFindLabels(
+ $requestOptions
+ );
+
+ $resultCount = is_array( $result ) ? count( $result ) : 0;
+
+ $html = $this->getForm(
+ $requestOptions->getLimit(),
+ $requestOptions->getOffset(),
+ $resultCount,
+ $threshold,
+ $type
+ );
+
+ if ( $result !== [] ) {
+ $html .= '<pre>' . json_encode( $result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</pre>';
+ } else {
+ $html .= $this->msg( 'smw-property-label-similarity-noresult' );
+ }
+
+ return $html;
+ }
+
+ private function getForm( $limit, $offset, $resultCount, $threshold, $type ) {
+
+ $exemptionProperty = $this->propertyLabelSimilarityLookup->getExemptionProperty();
+ $lookupCount = $this->propertyLabelSimilarityLookup->getLookupCount();
+
+ // Allow for an extra range since the property pool may be larger than
+ // the reductive comparison matches, +1 is to request additional paging
+ if ( $limit + $offset < $this->propertyLabelSimilarityLookup->getPropertyMaxCount() ) {
+ $lookupCount = $limit + $offset + 1;
+ }
+
+ $html = $this->msg(
+ [ 'smw-property-label-similarity-docu', $exemptionProperty ],
+ Message::PARSE
+ );
+
+ $html .= $this->htmlFormRenderer
+ ->setName( 'smw-property-label-similarity-title' )
+ ->setMethod( 'get' )
+ ->withFieldset()
+ ->addPaging(
+ $limit,
+ $offset,
+ $lookupCount,
+ $resultCount )
+ ->addHiddenField( 'limit', $limit )
+ ->addHiddenField( 'offset', $offset )
+ ->addInputField(
+ $this->msg( 'smw-property-label-similarity-threshold' ),
+ 'threshold',
+ $threshold,
+ '',
+ 5
+ )
+ ->addNonBreakingSpace()
+ ->addCheckbox(
+ $this->msg( 'smw-property-label-similarity-type' ),
+ 'type',
+ 'yes',
+ $type === 'yes',
+ null,
+ [
+ 'style' => 'float:right'
+ ]
+ )
+ ->addQueryParameter( 'type', $type )
+ ->addSubmitButton( $this->msg( 'allpagessubmit' ) )
+ ->getForm();
+
+ return Html::rawElement( 'div', [ 'class' => 'plainlinks'], $html ) . Html::element( 'p', [], '' );
+ }
+
+ private function msg( $parameters, $type = Message::TEXT ) {
+ return Message::get( $parameters, $type, Message::USER_LANGUAGE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageBuilder.php
new file mode 100644
index 00000000..7c2a7e5a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageBuilder.php
@@ -0,0 +1,395 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\SearchByProperty;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DataValues\StringValue;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\MessageBuilder;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\ProcessingErrorMsgHandler;
+use SMWDataValue as DataValue;
+use SMWInfolink as Infolink;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Denny Vrandecic
+ * @author Daniel Herzig
+ * @author Markus Kroetzsch
+ * @author mwjames
+ */
+class PageBuilder {
+
+ /**
+ * @var HtmlFormRenderer
+ */
+ private $htmlFormRenderer;
+
+ /**
+ * @var PageRequestOptions
+ */
+ private $pageRequestOptions;
+
+ /**
+ * @var QueryResultLookup
+ */
+ private $queryResultLookup;
+
+ /**
+ * @var MessageBuilder
+ */
+ private $messageBuilder;
+
+ /**
+ * @var Linker
+ */
+ private $linker;
+
+ /**
+ * @since 2.1
+ *
+ * @param HtmlFormRenderer $htmlFormRenderer
+ * @param PageRequestOptions $pageRequestOptions
+ * @param QueryResultLookup $queryResultLookup
+ */
+ public function __construct( HtmlFormRenderer $htmlFormRenderer, PageRequestOptions $pageRequestOptions, QueryResultLookup $queryResultLookup ) {
+ $this->htmlFormRenderer = $htmlFormRenderer;
+ $this->pageRequestOptions = $pageRequestOptions;
+ $this->queryResultLookup = $queryResultLookup;
+ $this->linker = smwfGetLinker();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ $this->pageRequestOptions->initialize();
+ $this->messageBuilder = $this->htmlFormRenderer->getMessageBuilder();
+
+ list( $resultMessage, $resultList, $resultCount ) = $this->getResultHtml();
+
+ if ( ( $resultList === '' || $resultList === null ) &&
+ $this->pageRequestOptions->property->getDataItem() instanceof DIProperty &&
+ $this->pageRequestOptions->valueString === '' ) {
+ list( $resultMessage, $resultList, $resultCount ) = $this->tryToFindAtLeastOnePropertyTableReferenceFor(
+ $this->pageRequestOptions->property->getDataItem()
+ );
+ }
+
+ if ( $resultList === '' || $resultList === null ) {
+ $resultList = $this->messageBuilder->getMessage( 'smw_result_noresults' )->text();
+ }
+
+ $pageDescription = Html::rawElement(
+ 'p',
+ [ 'class' => 'smw-sp-searchbyproperty-description' ],
+ $this->messageBuilder->getMessage( 'smw-sp-searchbyproperty-description' )->parse()
+ );
+
+ $resultListHeader = Html::element(
+ 'h2',
+ [],
+ $this->messageBuilder->getMessage( 'smw-sp-searchbyproperty-resultlist-header' )->text()
+ );
+
+ return $pageDescription . $this->getHtmlForm( $resultMessage, $resultCount ) . $resultListHeader . $resultList;
+ }
+
+ private function getHtmlForm( $resultMessage, $resultCount ) {
+
+ // Precaution to avoid any inline breakage caused by a div element
+ // within a paragraph (e.g Highlighter content)
+ $resultMessage = str_replace( 'div', 'span', $resultMessage );
+
+ $html = $this->htmlFormRenderer
+ ->setName( 'searchbyproperty' )
+ ->withFieldset()
+ ->addParagraph( $resultMessage )
+ ->addPaging(
+ $this->pageRequestOptions->limit,
+ $this->pageRequestOptions->offset,
+ $resultCount )
+ ->addHorizontalRule()
+ ->addInputField(
+ $this->messageBuilder->getMessage( 'smw_sbv_property' )->text(),
+ 'property',
+ $this->pageRequestOptions->propertyString,
+ 'smw-property-input' )
+ ->addNonBreakingSpace()
+ ->addInputField(
+ $this->messageBuilder->getMessage( 'smw_sbv_value' )->text(),
+ 'value',
+ $this->pageRequestOptions->valueString,
+ 'smw-value-input' )
+ ->addNonBreakingSpace()
+ ->addSubmitButton( $this->messageBuilder->getMessage( 'smw_sbv_submit' )->text() )
+ ->getForm();
+
+ return $html;
+ }
+
+ private function getResultHtml() {
+
+ $resultList = '';
+ $resultMessage = '';
+
+ if ( $this->pageRequestOptions->propertyString === '' || !$this->pageRequestOptions->propertyString ) {
+ return [ $this->messageBuilder->getMessage( 'smw_sbv_docu' )->text(), '', 0 ];
+ }
+
+ // #1728
+ if ( !$this->pageRequestOptions->property->isValid() ) {
+ return [ ProcessingErrorMsgHandler::getMessagesAsString( $this->pageRequestOptions->property->getErrors() ), '', 0 ];
+ }
+
+ if ( $this->pageRequestOptions->valueString !== '' && !$this->pageRequestOptions->value->isValid() ) {
+ return [ ProcessingErrorMsgHandler::getMessagesAsString( $this->pageRequestOptions->value->getErrors() ), '', 0 ];
+ }
+
+ // Find out where the subject is used in connection with a query
+ if ( $this->isAskQueryLinksRelatedRequest() ) {
+ $exactResults = $this->queryResultLookup->doQueryLinksReferences( $this->pageRequestOptions );
+ $exactCount = count( $exactResults );
+ $resultList = $this->makeResultList( $exactResults, $this->pageRequestOptions->limit, true );
+ return [ str_replace( '_', ' ', $resultMessage ), $resultList, $exactCount ];
+ }
+
+ $exactResults = $this->queryResultLookup->doQuery( $this->pageRequestOptions );
+ $exactCount = count( $exactResults );
+
+ if ( $this->canQueryNearbyResults( $exactCount ) ) {
+ return $this->getNearbyResults( $exactResults, $exactCount );
+ }
+
+ if ( $this->pageRequestOptions->valueString === '' ) {
+ $resultMessageKey = 'smw-sp-searchbyproperty-nonvaluequery';
+ } else {
+ $resultMessageKey = 'smw-sp-searchbyproperty-valuequery';
+ }
+
+ $resultMessage = $this->messageBuilder->getMessage(
+ $resultMessageKey,
+ $this->pageRequestOptions->property->getShortHTMLText( $this->linker ),
+ $this->pageRequestOptions->value->getShortHTMLText( $this->linker ) )->text();
+
+ if ( $exactCount > 0 ) {
+ $resultList = $this->makeResultList( $exactResults, $this->pageRequestOptions->limit, true );
+ }
+
+ return [ str_replace( '_', ' ', $resultMessage ), $resultList, $exactCount ];
+ }
+
+ private function getNearbyResults( $exactResults, $exactCount ) {
+
+ $resultList = '';
+
+ $greaterResults = $this->queryResultLookup->doQueryForNearbyResults(
+ $this->pageRequestOptions,
+ $exactCount,
+ true
+ );
+
+ $smallerResults = $this->queryResultLookup->doQueryForNearbyResults(
+ $this->pageRequestOptions,
+ $exactCount,
+ false
+ );
+
+ // Calculate how many greater and smaller results should be displayed
+ $greaterCount = count( $greaterResults );
+ $smallerCount = count( $smallerResults );
+
+ if ( ( $greaterCount + $smallerCount + $exactCount ) > $this->pageRequestOptions->limit ) {
+ $lhalf = round( ( $this->pageRequestOptions->limit - $exactCount ) / 2 );
+
+ if ( $lhalf < $greaterCount ) {
+ if ( $lhalf < $smallerCount ) {
+ $smallerCount = $lhalf;
+ $greaterCount = $lhalf;
+ } else {
+ $greaterCount = $this->pageRequestOptions->limit - ( $exactCount + $smallerCount );
+ }
+ } else {
+ $smallerCount = $this->pageRequestOptions->limit - ( $exactCount + $greaterCount );
+ }
+ }
+
+ if ( ( $greaterCount + $smallerCount + $exactCount ) == 0 ) {
+ return [ '', $resultList, 0 ];
+ }
+
+ $resultMessage = $this->messageBuilder->getMessage(
+ 'smw_sbv_displayresultfuzzy',
+ $this->pageRequestOptions->property->getShortHTMLText( $this->linker ),
+ $this->pageRequestOptions->value->getShortHTMLText( $this->linker ) )->text();
+
+ $resultList .= $this->makeResultList( $smallerResults, $smallerCount, false );
+
+ if ( $exactCount == 0 ) {
+ $resultList .= "&#160;<em><strong><small>" . $this->messageBuilder->getMessage( 'parentheses' )
+ ->rawParams( $this->pageRequestOptions->value->getLongHTMLText() )
+ ->escaped() . "</small></strong></em>";
+ } else {
+ $resultList .= $this->makeResultList( $exactResults, $exactCount, true, true );
+ }
+
+ $resultList .= $this->makeResultList( $greaterResults, $greaterCount, true );
+
+ return [ $resultMessage, $resultList, $greaterCount + $exactCount ];
+ }
+
+ /**
+ * Creates the HTML for a bullet list with all the results of the set
+ * query. Values can be highlighted to show exact matches among nearby
+ * ones.
+ *
+ * @param array $results (array of (array of one or two SMWDataValues))
+ * @param integer $number How many results should be displayed? -1 for all
+ * @param boolean $first If less results should be displayed than
+ * given, should they show the first $number results, or the last
+ * $number results?
+ * @param boolean $highlight Should the results be highlighted?
+ *
+ * @return string HTML with the bullet list, including header
+ */
+ private function makeResultList( $results, $number, $first, $highlight = false ) {
+
+ if ( $number > 0 ) {
+ $results = $first ?
+ array_slice( $results, 0, $number ) :
+ array_slice( $results, $number );
+ }
+
+ $html = '';
+
+ foreach ( $results as $result ) {
+
+ $outputFormat = $result[0]->getOutputFormat();
+ $result[0]->setOutputFormat( $outputFormat ? $outputFormat : 'LOCL' );
+
+ $listitem = $result[0]->getLongHTMLText( $this->linker );
+
+ if ( $this->canShowSearchByPropertyLink( $result[0] ) ) {
+
+ // Copy the instance for the InfoLinker
+ $res = clone $result[0];
+ $res->setOutputFormat( '' );
+
+ $value = $res instanceof StringValue && $res->getLength() < 72 ? $res->getWikiValue() : mb_substr( $res->getWikiValue(), 0, 72 );
+
+ $listitem .= '&#160;&#160;' . Infolink::newPropertySearchLink(
+ '+',
+ $this->pageRequestOptions->propertyString,
+ $value
+ )->getHTML( $this->linker );
+ } elseif ( $result[0]->getTypeID() === '_wpg' ) {
+
+ // Add browsing link for wikipage results
+ // Note: non-wikipage results are possible using inverse properties
+ $listitem .= '&#160;&#160;' . Infolink::newBrowsingLink(
+ '+',
+ $result[0]->getLongWikiText()
+ )->getHTML( $this->linker );
+ }
+
+ // Show value if not equal to the value that was searched
+ // or if the current results are to be highlighted:
+ if ( array_key_exists( 1, $result ) &&
+ ( $result[1] instanceof DataValue ) &&
+ ( !$result[1]->getDataItem() instanceof \SMWDIError ) &&
+ ( !$this->pageRequestOptions->value->getDataItem()->equals( $result[1]->getDataItem() )
+ || $highlight ) ) {
+
+ $outputFormat = $result[1]->getOutputFormat();
+ $result[1]->setOutputFormat( $outputFormat ? $outputFormat : 'LOCL' );
+
+ $listitem .= "&#160;<em><small>" . $this->messageBuilder->getMessage( 'parentheses' )
+ ->rawParams( $result[1]->getLongHTMLText( $this->linker ) )
+ ->escaped() . "</small></em>";
+ }
+
+ // Highlight values
+ if ( $highlight ) {
+ $listitem = "<strong>$listitem</strong>";
+ }
+
+ $html .= "<li>$listitem</li>";
+ }
+
+ return "<ul>$html</ul>";
+ }
+
+ private function canQueryNearbyResults( $exactCount ) {
+ return $exactCount < ( $this->pageRequestOptions->limit / 3 ) && $this->pageRequestOptions->nearbySearch && $this->pageRequestOptions->valueString !== '';
+ }
+
+ private function canShowSearchByPropertyLink ( DataValue $dataValue ) {
+
+ $dataTypeClass = DataTypeRegistry::getInstance()->getDataTypeClassById(
+ $dataValue->getTypeID()
+ );
+
+ return $this->pageRequestOptions->value instanceof $dataTypeClass && $this->pageRequestOptions->valueString === '';
+ }
+
+ private function tryToFindAtLeastOnePropertyTableReferenceFor( DIProperty $property ) {
+
+ $resultList = '';
+ $resultMessage = '';
+ $resultCount = 0;
+ $extra = '';
+
+ $dataItem = ApplicationFactory::getInstance()->getStore()->getPropertyTableIdReferenceFinder()->tryToFindAtLeastOneReferenceForProperty(
+ $property
+ );
+
+ if ( !$dataItem instanceof DIWikiPage ) {
+ $resultMessage = 'No reference found.';
+ return [ $resultMessage, $resultList, $resultCount ];
+ }
+
+ // In case the item has already been marked as deleted but is yet pending
+ // for removal
+ if ( $dataItem->getInterWiki() === ':smw-delete' ) {
+ $resultMessage = 'Item reference "' . $dataItem->getSubobjectName() . '" has already been marked for removal.';
+ $dataItem = new DIWikiPage( $dataItem->getDBKey(), $dataItem->getNamespace() );
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem
+ );
+
+ $outputFormat = $dataValue->getOutputFormat();
+ $dataValue->setOutputFormat( $outputFormat ? $outputFormat : 'LOCL' );
+
+ if ( $dataValue->isValid() ) {
+ //$resultMessage = 'Item reference for a zero-marked property.';
+ $resultList = $dataValue->getShortHtmlText( $this->linker ) . ' ' . $extra;
+ $resultCount++;
+
+ $resultList .= '&#160;&#160;' . Infolink::newBrowsingLink(
+ '+',
+ $dataValue->getLongWikiText()
+ )->getHTML( $this->linker );
+ }
+
+ return [ $resultMessage, $resultList, $resultCount ];
+ }
+
+ private function isAskQueryLinksRelatedRequest() {
+ return $this->pageRequestOptions->property !== '' &&
+ $this->pageRequestOptions->property->getDataItem()->getKey() === '_ASK' &&
+ $this->pageRequestOptions->value->isValid() &&
+ strpos( $this->pageRequestOptions->value->getWikiValue(), '_QUERY' ) === false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageRequestOptions.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageRequestOptions.php
new file mode 100644
index 00000000..0d16bf25
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/PageRequestOptions.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\SearchByProperty;
+
+use SMW\DataValueFactory;
+use SMW\DataValues\TelephoneUriValue;
+use SMW\Encoder;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageRequestOptions {
+
+ /**
+ * @var string
+ */
+ private $queryString;
+
+ /**
+ * @var array
+ */
+ private $requestOptions;
+
+ /**
+ * @var Encoder
+ */
+ private $urlEncoder;
+
+ /**
+ * @var PropertyValue
+ */
+ public $property;
+
+ /**
+ * @var string
+ */
+ public $propertyString;
+
+ /**
+ * @var string
+ */
+ public $valueString;
+
+ /**
+ * @var DataValue
+ */
+ public $value;
+
+ /**
+ * @var integer
+ */
+ public $limit = 20;
+
+ /**
+ * @var integer
+ */
+ public $offset = 0;
+
+ /**
+ * @var boolean
+ */
+ public $nearbySearch = false;
+
+ /**
+ * @since 2.1
+ *
+ * @param string $queryString
+ * @param array $requestOptions
+ */
+ public function __construct( $queryString, array $requestOptions ) {
+ $this->queryString = $queryString;
+ $this->requestOptions = $requestOptions;
+ $this->urlEncoder = new Encoder();
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function initialize() {
+
+ $params = explode( '/', $this->queryString );
+ reset( $params );
+ $escaped = false;
+
+ // Remove empty elements
+ $params = array_filter( $params, 'strlen' );
+
+ $property = isset( $this->requestOptions['property'] ) ? $this->requestOptions['property'] : current( $params );
+ $value = isset( $this->requestOptions['value'] ) ? $this->requestOptions['value'] : next( $params );
+
+ // Auto-generated link is marked with a leading :
+ if ( $property !== '' && $property{0} === ':' ) {
+ $escaped = true;
+ $property = $this->urlEncoder->unescape( ltrim( $property, ':' ) );
+ }
+
+ $this->property = DataValueFactory::getInstance()->newPropertyValueByLabel(
+ str_replace( [ '_' ], [ ' ' ], $property )
+ );
+
+ if ( !$this->property->isValid() ) {
+ $this->propertyString = $property;
+ $this->value = null;
+ $this->valueString = $value;
+ } else {
+ $this->propertyString = $this->property->getDataItem()->getLabel();
+ $this->valueString = $this->getValue( (string)$value, $escaped );
+ }
+
+ $this->setLimit();
+ $this->setOffset();
+ $this->setNearbySearch();
+ }
+
+ private function getValue( $value, $escaped ) {
+
+ $this->value = DataValueFactory::getInstance()->newDataValueByProperty(
+ $this->property->getDataItem()
+ );
+
+ $value = $this->unescape( $value, $escaped );
+ $this->value->setUserValue( $value );
+
+ return $this->value->isValid() ? $this->value->getWikiValue() : $value;
+ }
+
+ private function unescape( $value, $escaped ) {
+
+ if ( $this->value instanceof NumberValue ) {
+ $value = $escaped ? str_replace( [ '-20', '-2D' ], [ ' ', '-' ], $value ) : $value;
+ // Do not try to decode things like 1.2e-13
+ // Signals that we don't want any precision limitation
+ $this->value->setOption( NumberValue::NO_DISP_PRECISION_LIMIT, true );
+ } elseif ( $this->value instanceof TelephoneUriValue ) {
+ $value = $escaped ? str_replace( [ '-20', '-2D' ], [ ' ', '-' ], $value ) : $value;
+ // No encoding to avoid turning +1-201-555-0123
+ // into +1 1U523 or further obfuscate %2B1-2D201-2D555-2D0123 ...
+ } else {
+ $value = $escaped ? $this->urlEncoder->unescape( $value ) : $value;
+ }
+
+ return $value;
+ }
+
+ private function setLimit() {
+ if ( isset( $this->requestOptions['limit'] ) ) {
+ $this->limit = intval( $this->requestOptions['limit'] );
+ }
+ }
+
+ private function setOffset() {
+ if ( isset( $this->requestOptions['offset'] ) ) {
+ $this->offset = intval( $this->requestOptions['offset'] );
+ }
+ }
+
+ private function setNearbySearch() {
+
+ if ( $this->value === null ) {
+ return null;
+ }
+
+ if ( isset( $this->requestOptions['nearbySearchForType'] ) && is_array( $this->requestOptions['nearbySearchForType'] ) ) {
+ $this->nearbySearch = in_array( $this->value->getTypeID(), $this->requestOptions['nearbySearchForType'] );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/QueryResultLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/QueryResultLookup.php
new file mode 100644
index 00000000..2b689877
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SearchByProperty/QueryResultLookup.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW\MediaWiki\Specials\SearchByProperty;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\SQLStore\QueryDependencyLinksStoreFactory;
+use SMW\Store;
+use SMWQuery as Query;
+use SMWRequestOptions as RequestOptions;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Denny Vrandecic
+ * @author Daniel Herzig
+ * @author Markus Kroetzsch
+ * @author mwjames
+ */
+class QueryResultLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.1
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryOptions $pageRequestOptions
+ *
+ * @return array
+ */
+ public function doQueryLinksReferences( PageRequestOptions $pageRequestOptions ) {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( $pageRequestOptions->limit + 1 );
+ $requestOptions->setOffset( $pageRequestOptions->offset );
+ $requestOptions->sort = true;
+
+ $queryDependencyLinksStoreFactory = new QueryDependencyLinksStoreFactory();
+
+ $queryReferenceLinks = $queryDependencyLinksStoreFactory->newQueryReferenceBacklinks(
+ $this->store
+ );
+
+ $queryBacklinks = $queryReferenceLinks->findReferenceLinks(
+ $pageRequestOptions->value->getDataItem(),
+ $requestOptions
+ );
+
+ $results = [];
+
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ foreach ( $queryBacklinks as $result ) {
+ $results[] = [
+ $dataValueFactory->newDataValueByItem( DIWikiPage::doUnserialize( $result ), null ),
+ $pageRequestOptions->value
+ ];
+ }
+
+ return $results;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param QueryOptions $pageRequestOptions
+ *
+ * @return array of array(SMWWikiPageValue, SMWDataValue) with the
+ * first being the entity, and the second the value
+ */
+ public function doQuery( PageRequestOptions $pageRequestOptions ) {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->limit = $pageRequestOptions->limit + 1;
+ $requestOptions->offset = $pageRequestOptions->offset;
+ $requestOptions->sort = true;
+
+ if ( $pageRequestOptions->value === null || !$pageRequestOptions->value->isValid() ) {
+ $res = $this->doQueryForNonValue( $pageRequestOptions, $requestOptions );
+ } else {
+ $res = $this->doQueryForExactValue( $pageRequestOptions, $requestOptions );
+ }
+
+ $results = [];
+
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ foreach ( $res as $result ) {
+ $results[] = [
+ $dataValueFactory->newDataValueByItem( $result, null ),
+ $pageRequestOptions->value
+ ];
+ }
+
+ return $results;
+ }
+
+ /**
+ * Returns all results that have a value near to the searched for value
+ * on the property, ordered, and sorted by ending with the smallest
+ * one.
+ *
+ * @param QueryOptions $pageRequestOptions
+ * @param integer $count How many entities have the exact same value on the property?
+ * @param integer $greater Should the values be bigger? Set false for smaller values.
+ *
+ * @return array of array of SMWWikiPageValue, SMWDataValue with the
+ * first being the entity, and the second the value
+ */
+ public function doQueryForNearbyResults( PageRequestOptions $pageRequestOptions, $count, $greater = true ) {
+
+ $comparator = $greater ? SMW_CMP_GRTR : SMW_CMP_LESS;
+ $sortOrder = $greater ? 'ASC' : 'DESC';
+
+ if ( $pageRequestOptions->value !== null && $pageRequestOptions->value->getTypeID() === '_txt' && strlen( $pageRequestOptions->valueString ) > 72 ) {
+ $comparator = SMW_CMP_LIKE;
+ }
+
+ $descriptionFactory = new DescriptionFactory();
+
+ if ( $pageRequestOptions->valueString === '' || $pageRequestOptions->valueString === null ) {
+ $description = $descriptionFactory->newThingDescription();
+ } else {
+ $description = $descriptionFactory->newValueDescription(
+ $pageRequestOptions->value->getDataItem(),
+ $pageRequestOptions->property->getDataItem(),
+ $comparator
+ );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $pageRequestOptions->property->getDataItem(),
+ $description
+ );
+ }
+
+ $query = new Query( $description );
+
+ $query->setLimit( $pageRequestOptions->limit );
+ $query->setOffset( $pageRequestOptions->offset );
+ $query->sort = true;
+ $query->sortkeys = [
+ $pageRequestOptions->property->getDataItem()->getKey() => $sortOrder
+ ];
+
+ // Note: printrequests change the caption of properties they
+ // get (they expect properties to be given to them).
+ // Since we want to continue using the property for our
+ // purposes, we give a clone to the print request.
+ $printouts = [
+ new PrintRequest( PrintRequest::PRINT_THIS, '' ),
+ new PrintRequest( PrintRequest::PRINT_PROP, '', clone $pageRequestOptions->property )
+ ];
+
+ $query->setExtraPrintouts( $printouts );
+
+ $queryResults = $this->store->getQueryResult( $query );
+
+ $result = [];
+
+ while ( $resultArrays = $queryResults->getNext() ) {
+ $r = [];
+
+ foreach ( $resultArrays as $resultArray ) {
+ $r[] = $resultArray->getNextDataValue();
+ }
+ // Note: if results have multiple values for the property
+ // then this code just pick the first, which may not be
+ // the reason why the result is shown here, i.e., it could
+ // be out of order.
+ $result[] = $r;
+ }
+
+ if ( !$greater ) {
+ $result = array_reverse( $result );
+ }
+
+ return $result;
+ }
+
+ private function doQueryForNonValue( PageRequestOptions $pageRequestOptions, RequestOptions $requestOptions ) {
+ return $this->store->getPropertyValues(
+ null,
+ $pageRequestOptions->property->getDataItem(),
+ $requestOptions
+ );
+ }
+
+ private function doQueryForExactValue( PageRequestOptions $pageRequestOptions, RequestOptions $requestOptions ) {
+
+ $pageRequestOptions->value->setOption( 'is.search', true );
+
+ return $this->store->getPropertySubjects(
+ $pageRequestOptions->property->getDataItem(),
+ $pageRequestOptions->value->getDataItem(),
+ $requestOptions
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAdmin.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAdmin.php
new file mode 100644
index 00000000..d2677549
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAdmin.php
@@ -0,0 +1,260 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Exception\ExtendedPermissionsError;
+use SMW\MediaWiki\Specials\Admin\OutputFormatter;
+use SMW\MediaWiki\Specials\Admin\TaskHandler;
+use SMW\MediaWiki\Specials\Admin\TaskHandlerFactory;
+use SMW\Message;
+use SMW\Utils\HtmlTabs;
+use SpecialPage;
+
+/**
+ * This special page for MediaWiki provides an administrative interface
+ * that allows to execute certain functions related to the maintenance
+ * of the semantic database.
+ *
+ * Access to the special page and its function is limited to users with the
+ * `smw-admin` right.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class SpecialAdmin extends SpecialPage {
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'SMWAdmin', 'smw-admin' );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function doesWrites() {
+ return true;
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ if ( !$this->userCanExecute( $this->getUser() ) ) {
+ // $this->mRestriction is private MW 1.23-
+ throw new ExtendedPermissionsError( 'smw-admin', [ 'smw-admin-permission-missing' ] );
+ }
+
+ // https://phabricator.wikimedia.org/T109652#1562641
+ $this->getRequest()->setVal(
+ 'wpEditToken',
+ $this->getUser()->getEditToken()
+ );
+
+ $this->setHeaders();
+ $output = $this->getOutput();
+ $output->setPageTitle( $this->msg_text( 'smw-title' ) );
+
+ $output->addModuleStyles( 'ext.smw.special.style' );
+ $output->addModules( 'ext.smw.admin' );
+
+ if ( $query !== null ) {
+ $this->getRequest()->setVal( 'action', $query );
+ }
+
+ $action = $this->getRequest()->getText( 'action' );
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $mwCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
+
+ $htmlFormRenderer = $mwCollaboratorFactory->newHtmlFormRenderer(
+ $this->getContext()->getTitle(),
+ $this->getLanguage()
+ );
+
+ // Some functions require methods only provided by the SQLStore (or any
+ // inherit class thereof)
+ if ( !is_a( ( $store = $applicationFactory->getStore() ), '\SMW\SQLStore\SQLStore' ) ) {
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+ }
+
+ $outputFormatter = new OutputFormatter(
+ $this->getOutput()
+ );
+
+ $adminFeatures = $applicationFactory->getSettings()->get( 'smwgAdminFeatures' );
+
+ // Disable the feature in case the function is not supported
+ if ( $applicationFactory->getSettings()->get( 'smwgEnabledFulltextSearch' ) === false ) {
+ $adminFeatures = $adminFeatures & ~SMW_ADM_FULLT;
+ }
+
+ $taskHandlerFactory = new TaskHandlerFactory(
+ $store,
+ $htmlFormRenderer,
+ $outputFormatter
+ );
+
+ $taskHandlerList = $taskHandlerFactory->getTaskHandlerList(
+ $this->getUser(),
+ $adminFeatures
+ );
+
+ foreach ( $taskHandlerList['actions'] as $actionTask ) {
+ if ( $actionTask->isTaskFor( $action ) ) {
+ return $actionTask->handleRequest( $this->getRequest() );
+ }
+ }
+
+ $output->addHTML(
+ $this->buildHTML( $taskHandlerList )
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+ private function buildHTML( $taskHandlerList ) {
+
+ $tableSchemaTaskList = $taskHandlerList[TaskHandler::SECTION_SCHEMA];
+
+ $dataRebuildSection = end( $tableSchemaTaskList )->getHtml();
+ $dataRebuildSection .= Html::rawElement(
+ 'hr',
+ [
+ 'class' => 'smw-admin-hr'
+ ],
+ ''
+ ) . Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks',
+ 'style' => 'margin-top:0.8em;'
+ ],
+ $this->msg_text( 'smw-admin-job-scheduler-note', Message::PARSE )
+ );
+
+ $list = '';
+ $dataRepairTaskList = $taskHandlerList[TaskHandler::SECTION_DATAREPAIR];
+
+ foreach ( $dataRepairTaskList as $dataRepairTask ) {
+ $list .= $dataRepairTask->getHtml();
+ }
+
+ $dataRebuildSection .= Html::rawElement( 'div', [ 'class' => 'smw-admin-data-repair-section' ],
+ $list
+ );
+
+ $supplementarySection = Html::rawElement(
+ 'p',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $this->msg_text( 'smw-admin-supplementary-section-intro', Message::PARSE )
+ ) . Html::rawElement(
+ 'h3',
+ [],
+ $this->msg_text( 'smw-admin-supplementary-section-subtitle' )
+ );
+
+ $list = '';
+ $supplementaryTaskList = $taskHandlerList[TaskHandler::SECTION_SUPPLEMENT];
+
+ foreach ( $supplementaryTaskList as $supplementaryTask ) {
+ $list .= $supplementaryTask->getHtml();
+ }
+
+ $supplementarySection .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-admin-supplementary-section'
+ ],
+ Html::rawElement( 'ul', [], $list )
+ );
+
+ $deprecationNoticeTaskList = $taskHandlerList[TaskHandler::SECTION_DEPRECATION];
+ $deprecationNoticeTaskHandler = end( $deprecationNoticeTaskList );
+
+ $deprecationNotices = $deprecationNoticeTaskHandler->getHtml();
+ $htmlTabs = new HtmlTabs();
+
+ $default = $deprecationNotices === '' ? 'general' : 'notices';
+
+ // If we want to remain on a specific tab on a GET request, use the `tab`
+ // parameter since we are unable to fetch any #href hash from a request
+ $htmlTabs->setActiveTab(
+ $this->getRequest()->getVal( 'tab', $default )
+ );
+
+ $htmlTabs->tab( 'general', $this->msg_text( 'smw-admin-tab-general' ) );
+
+ $htmlTabs->tab(
+ 'notices',
+ 'âš  ' . $this->msg_text( 'smw-admin-tab-notices' ),
+ [
+ 'hide' => $deprecationNotices === '' ? true : false,
+ 'class' => 'smw-tab-warning'
+ ]
+ );
+
+ $htmlTabs->tab( 'rebuild', $this->msg_text( 'smw-admin-tab-rebuild' ) );
+ $htmlTabs->tab( 'supplement', $this->msg_text( 'smw-admin-tab-supplement' ) );
+
+ $supportTaskList = $taskHandlerList[TaskHandler::SECTION_SUPPORT];
+ $supportListTaskHandler = end( $supportTaskList );
+
+ $html = Html::rawElement(
+ 'p',
+ [],
+ $this->msg_text( 'smw-admin-docu' )
+ ) . Html::rawElement(
+ 'h3',
+ [],
+ $this->msg_text( 'smw-admin-environment' )
+ ) . Html::rawElement(
+ 'pre',
+ [],
+ json_encode( $this->getInfo(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
+ ) . $supportListTaskHandler->createSupportForm() .
+ $supportListTaskHandler->createRegistryForm();
+
+ $htmlTabs->content( 'general', $html );
+ $htmlTabs->content( 'notices', $deprecationNotices );
+ $htmlTabs->content( 'rebuild', $dataRebuildSection );
+ $htmlTabs->content( 'supplement', $supplementarySection );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-admin' ]
+ );
+
+ return $html;
+ }
+
+ private function getInfo() {
+
+ $store = ApplicationFactory::getInstance()->getStore();
+
+ return $store->getInfo() + [
+ 'smw' => SMW_VERSION,
+ 'mediawiki' => $GLOBALS['wgVersion']
+ ] + (
+ defined( 'HHVM_VERSION' ) ? [ 'hhvm' => HHVM_VERSION ] : [ 'php' => PHP_VERSION ]
+ );
+ }
+
+ private function msg_text( $key, $type = Message::TEXT) {
+ return Message::get( $key, $type , Message::USER_LANGUAGE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAsk.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAsk.php
new file mode 100644
index 00000000..fe2d817d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialAsk.php
@@ -0,0 +1,714 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use Html;
+use ParamProcessor\Param;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Specials\Ask\ErrorWidget;
+use SMW\MediaWiki\Specials\Ask\FormatListWidget;
+use SMW\MediaWiki\Specials\Ask\HelpWidget;
+use SMW\MediaWiki\Specials\Ask\LinksWidget;
+use SMW\MediaWiki\Specials\Ask\NavigationLinksWidget;
+use SMW\MediaWiki\Specials\Ask\ParametersProcessor;
+use SMW\MediaWiki\Specials\Ask\ParametersWidget;
+use SMW\MediaWiki\Specials\Ask\QueryInputWidget;
+use SMW\MediaWiki\Specials\Ask\SortWidget;
+use SMW\MediaWiki\Specials\Ask\UrlArgs;
+use SMW\MediaWiki\Specials\Ask\HtmlForm;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryLinker;
+use SMW\Query\RemoteRequest;
+use SMW\Query\Result\StringResult;
+use SMW\Utils\HtmlModal;
+use SMWInfolink as Infolink;
+use SMWOutputs;
+use SMWQuery;
+use SMWQueryProcessor as QueryProcessor;
+use SMWQueryResult as QueryResult;
+use SpecialPage;
+use SMW\Utils\HtmlTabs;
+use SMW\Message;
+
+/**
+ * This special page for MediaWiki implements a customisable form for executing
+ * queries outside of articles.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ * @author Yaron Koren
+ * @author Sanyam Goyal
+ * @author Jeroen De Dauw
+ */
+class SpecialAsk extends SpecialPage {
+
+ /**
+ * @var QuerySourceFactory
+ */
+ private $querySourceFactory;
+
+ /**
+ * @var string
+ */
+ private $queryString = '';
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @var array
+ */
+ private $printouts = [];
+
+ /**
+ * @var boolean
+ */
+ private $isEditMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isBorrowedMode = false;
+
+ /**
+ * @var Param[]
+ */
+ private $params = [];
+
+ public function __construct() {
+ parent::__construct( 'Ask' );
+ $this->querySourceFactory = ApplicationFactory::getInstance()->getQuerySourceFactory();
+ }
+
+ /**
+ * @see SpecialPage::doesWrites
+ *
+ * @return boolean
+ */
+ public function doesWrites() {
+ return true;
+ }
+
+ /**
+ * @see SpecialPage::execute
+ *
+ * @param string $p
+ */
+ public function execute( $p ) {
+
+ $this->setHeaders();
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $out = $this->getOutput();
+ $request = $this->getRequest();
+ $title = SpecialPage::getSafeTitleFor( 'Ask' );
+
+ // A GET form submit cannot use a fragment (aka anchor) to repositioning
+ // to a specific target after a request has completed, use a redirect
+ // with the posted query values from the submit form to add an anchor
+ // point
+ if ( $settings->is( 'smwgSpecialAskFormSubmitMethod', SMW_SASK_SUBMIT_GET_REDIRECT ) && $request->getVal( '_action' ) === 'submit' ) {
+ $vals = $request->getQueryValues();
+
+ unset( $vals['_action'] );
+ unset( $vals['title'] );
+
+ return $out->redirect(
+ $title->getLocalUrl( wfArrayToCGI( $vals ) . '#search' )
+ );
+ }
+
+ $request->setVal( 'wpEditToken',
+ $this->getUser()->getEditToken()
+ );
+
+ if ( !$GLOBALS['smwgQEnabled'] ) {
+ return $out->addHtml( ErrorWidget::disabled() );
+ }
+
+ // Administrative block when used in combination with the `RemoteRequest`.
+ // It is not to be mistaken with an auth block as you always can fetch
+ // the content from a public wiki via cURL.
+ if ( $request->getVal( 'request_type', '' ) !== '' && !$settings->isFlagSet( 'smwgRemoteReqFeatures', SMW_REMOTE_REQ_SEND_RESPONSE ) ) {
+ $out->disable();
+ return print RemoteRequest::SOURCE_DISABLED;
+ }
+
+ $this->init();
+
+ if ( $request->getCheck( 'showformatoptions' ) ) {
+ // handle Ajax action
+ $params = $request->getArray( 'params' );
+ $params['format'] = $request->getVal( 'showformatoptions' );
+ $out->disable();
+ echo ParametersWidget::parameterList( $params );
+ } else {
+ $this->extractQueryParameters( $p );
+
+ if ( $this->isBorrowedMode ) {
+ $visibleLinks = [];
+ } elseif( $request->getVal( 'eq', '' ) === 'no' || $p !== null || $request->getVal( 'x' ) || $request->getVal( 'cl' ) ) {
+ $visibleLinks = [ 'search', 'empty' ];
+ } else {
+ $visibleLinks = [ 'options', 'search', 'help', 'empty' ];
+ }
+
+ $out->addHTML(
+ NavigationLinksWidget::topLinks(
+ $title,
+ $visibleLinks,
+ $this->isEditMode
+ )
+ );
+
+ $this->makeHTMLResult();
+ }
+
+ $out->addHTML( HelpWidget::html() );
+ $this->addHelpLink( wfMessage( 'smw_ask_doculink' )->escaped(), true );
+
+ // make sure locally collected output data is pushed to the output!
+ SMWOutputs::commitToOutputPage( $out );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+ private function init() {
+ $out = $this->getOutput();
+ $request = $this->getRequest();
+
+ $out->addModuleStyles( 'ext.smw.style' );
+ $out->addModuleStyles( 'ext.smw.ask.styles' );
+ $out->addModuleStyles( 'ext.smw.table.styles' );
+ $out->addModuleStyles( 'ext.smw.page.styles' );
+
+ $out->addModuleStyles(
+ HtmlModal::getModuleStyles()
+ );
+
+ $out->addModules( 'ext.smw.ask' );
+ $out->addModules( 'ext.smw.autocomplete.property' );
+
+ $out->addModules(
+ LinksWidget::getModules()
+ );
+
+ $out->addModules(
+ HtmlModal::getModules()
+ );
+
+ $out->addHTML( ErrorWidget::noScript() );
+
+ // #2590
+ if ( !$this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
+ return $out->addHtml( ErrorWidget::sessionFailure() );
+ }
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ NavigationLinksWidget::setMaxInlineLimit(
+ $GLOBALS['smwgQMaxInlineLimit']
+ );
+
+ FormatListWidget::setResultFormats(
+ $GLOBALS['smwgResultFormats']
+ );
+
+ ParametersWidget::setTooltipDisplay(
+ $this->getUser()->getOption( 'smw-prefs-ask-options-tooltip-display' )
+ );
+
+ ParametersWidget::setDefaultLimit(
+ $GLOBALS['smwgQDefaultLimit']
+ );
+
+ SortWidget::setSortingSupport(
+ $settings->isFlagSet( 'smwgQSortFeatures', SMW_QSORT )
+ );
+
+ // @see #835
+ SortWidget::setRandSortingSupport(
+ $settings->isFlagSet( 'smwgQSortFeatures', SMW_QSORT_RANDOM )
+ );
+
+ ParametersProcessor::setDefaultLimit(
+ $GLOBALS['smwgQDefaultLimit']
+ );
+
+ ParametersProcessor::setMaxInlineLimit(
+ $GLOBALS['smwgQMaxInlineLimit']
+ );
+
+ $this->isBorrowedMode = $request->getCheck( 'bTitle' ) || $request->getCheck( 'btitle' );
+ }
+
+ /**
+ * @param string $p
+ */
+ protected function extractQueryParameters( $p ) {
+
+ $request = $this->getRequest();
+ $this->isEditMode = false;
+
+ if ( $request->getText( 'cl', '' ) !== '' ) {
+ $p = Infolink::decodeCompactLink( 'cl:' . $request->getText( 'cl' ) );
+ } else {
+ $p = Infolink::decodeCompactLink( $p );
+ }
+
+ list( $this->queryString, $this->parameters, $this->printouts ) = ParametersProcessor::process(
+ $request,
+ $p
+ );
+
+ if ( isset( $this->parameters['btitle'] ) ) {
+ $this->isBorrowedMode = true;
+ }
+
+ if ( ( $request->getVal( 'eq' ) == 'yes' ) || ( $this->queryString === '' ) ) {
+ $this->isEditMode = true;
+ }
+ }
+
+ protected function makeHTMLResult() {
+
+ $result = '';
+ $res = null;
+ $settings = ApplicationFactory::getInstance()->getSettings();
+ $queryobj = null;
+
+ $navigation = '';
+ $urlArgs = $this->newUrlArgs();
+
+ $isFromCache = false;
+ $duration = 0;
+
+ $error = '';
+ $printer = null;
+
+ if ( $this->queryString !== '' ) {
+ list( $result, $res, $duration ) = $this->fetchResults(
+ $printer,
+ $queryobj,
+ $urlArgs
+ );
+ }
+
+ if ( $printer !== null && $printer->isExportFormat() ) {
+
+ // Avoid a possible "Cannot modify header information - headers already sent by ..."
+ if ( defined( 'MW_PHPUNIT_TEST' ) && method_exists( $printer, 'disableHttpHeader' ) ) {
+ $printer->disableHttpHeader();
+ }
+
+ $this->getOutput()->disable();
+ $request_type = $this->getRequest()->getVal( 'request_type' );
+
+ if ( $request_type === 'embed' ) {
+ // Just send a furthers link output for an embedded remote request
+ echo $printer->getResult( $res, $this->params, SMW_OUTPUT_HTML ) . RemoteRequest::REQUEST_ID;
+ } elseif ( $request_type === 'special_page' ) {
+ // Generate raw content when being requested from a remote special_page
+ echo $printer->getResult( $res, $this->params, SMW_OUTPUT_FILE ) . RemoteRequest::REQUEST_ID;
+ } else {
+ return $printer->outputAsFile( $res, $this->params );
+ }
+ }
+
+ if ( $this->queryString ) {
+ $this->getOutput()->setHTMLtitle( $this->queryString );
+ } else {
+ $this->getOutput()->setHTMLtitle( wfMessage( 'ask' )->text() );
+ }
+
+ $urlArgs->set( 'offset', $this->parameters['offset'] );
+ $urlArgs->set( 'limit', $this->parameters['limit'] );
+ $urlArgs->set( 'eq', $this->isEditMode ? 'yes' : 'no' );
+
+ $result = Html::rawElement(
+ 'div',
+ [
+ 'id' => 'result',
+ 'class' => 'smw-ask-result' . ( $this->isBorrowedMode ? ' is-disabled' : '' )
+ ],
+ $result
+ );
+
+ if ( $res instanceof QueryResult ) {
+ $isFromCache = $res->isFromCache();
+ $error = ErrorWidget::queryError( $queryobj );
+ } elseif ( is_string( $res ) ) {
+ $error = $res;
+ }
+
+ $infoText = $this->getInfoText(
+ $duration,
+ $isFromCache
+ );
+
+ $htmlForm = new HtmlForm(
+ SpecialPage::getSafeTitleFor( 'Ask' )
+ );
+
+ $htmlForm->setParameters( $this->parameters );
+ $htmlForm->setQueryString( $this->queryString );
+ $htmlForm->setQuery( $queryobj );
+
+ $htmlForm->setCallbacks(
+ [
+ 'borrowed_msg_handler' => function( &$html, &$searchInfoText ) {
+ return $this->print_borrowed_msg( $html, $searchInfoText );
+ },
+ 'code_handler' => function() {
+ return $this->print_code();
+ }
+ ]
+ );
+
+ $htmlForm->isPostSubmit(
+ $settings->is( 'smwgSpecialAskFormSubmitMethod', SMW_SASK_SUBMIT_POST )
+ );
+
+ $htmlForm->isEditMode( $this->isEditMode );
+ $htmlForm->isBorrowedMode( $this->isBorrowedMode );
+
+ $form = $htmlForm->getForm(
+ $urlArgs,
+ $res,
+ $infoText
+ );
+
+ // The overall form is "soft-disabled" so that when JS is fully
+ // loaded, the ask module will remove this class and releases the form
+ // for input
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'id' => 'ask',
+ "class" => ( $this->isBorrowedMode ? '' : 'is-disabled' )
+ ],
+ $form . $error . $result
+ );
+
+ $this->getOutput()->addHTML(
+ $html
+ );
+ }
+
+ private function fetchResults( &$printer, &$queryobj, &$urlArgs ) {
+
+ list( $res, $debug, $duration, $queryobj, $native_result ) = $this->getQueryResult();
+
+ $printer = QueryProcessor::getResultPrinter(
+ $this->parameters['format'],
+ QueryProcessor::SPECIAL_PAGE
+ );
+
+ $printer->setShowErrors( false );
+
+ $hidequery = $this->getRequest()->getVal( 'eq' ) == 'no';
+ $request_type = $this->getRequest()->getVal( 'request_type', '' );
+ $result = '';
+
+ if ( isset( $this->parameters['request_type'] ) ) {
+ $request_type = $this->parameters['request_type'];
+ }
+
+ if ( !$printer->isExportFormat() ) {
+ if ( $request_type !== '' ) {
+ $this->getOutput()->disable();
+ $query_result = '';
+
+ if ( $res->getCount() > 0 ) {
+
+ if ( $request_type === 'raw' ) {
+ $query_result = $printer->getResult( $res, $this->params, SMW_OUTPUT_RAW );
+ } else {
+ $query_result = $printer->getResult( $res, $this->params, SMW_OUTPUT_HTML );
+ }
+
+ } elseif ( $res->getCountValue() > 0 ) {
+ $query_result = $res->getCountValue();
+ }
+
+ // Don't send an ID for a raw type but for all others add one
+ // so that the `RemoteRequest` can respond appropriately and
+ // filter those back-ends that don't send a clean output.
+ if ( $request_type !== 'raw' ) {
+ $query_result .= RemoteRequest::REQUEST_ID;
+ }
+
+ return print $query_result;
+ } elseif ( ( $res instanceof QueryResult && $res->getCount() > 0 ) || $res instanceof StringResult ) {
+ if ( $this->isEditMode ) {
+ $urlArgs->set( 'eq', 'yes' );
+ } elseif ( $hidequery ) {
+ $urlArgs->set( 'eq', 'no' );
+ }
+
+ $query_result = $printer->getResult( $res, $this->params, SMW_OUTPUT_HTML );
+ $result .= is_string( $debug ) ? $debug : '';
+
+ if ( is_array( $query_result ) ) {
+ $result .= $query_result[0];
+ } else {
+ $result .= $query_result;
+ }
+ } else {
+ $result = ErrorWidget::noResult();
+ $result .= is_string( $debug ) ? $debug : '';
+ }
+ }
+
+ if ( $this->getRequest()->getVal( 'score_set', false ) && ( $scoreSet = $res->getScoreSet() ) !== null ) {
+ $table = $scoreSet->asTable( 'sortable wikitable smwtable-striped broadtable' );
+
+ if ( $table !== '' ) {
+ $result .= '<h2>Score set</h2>' . $table;
+ };
+ }
+
+ if ( $native_result !== '' ) {
+ $result .= '<h2>Native result</h2>' . '<pre>' . $native_result . '</pre>';
+ }
+
+ return [ $result, $res, $duration ];
+ }
+
+ private function getInfoText( $duration, $isFromCache = false ) {
+
+ $infoText = '';
+ $source = null;
+
+ if ( isset( $this->parameters['source'] ) ) {
+ $source = $this->parameters['source'];
+ }
+
+ if ( $this->getRequest()->getVal( 'q_engine' ) === 'sql_store' ) {
+ $source = 'sql_store';
+ }
+
+ $querySource = $this->querySourceFactory->toString(
+ $source
+ );
+
+ if ( $duration > 0 ) {
+ $infoText = Message::get(
+ [ 'smw-ask-query-search-info', $this->queryString, $querySource, $isFromCache, $duration],
+ Message::PARSE,
+ $this->getLanguage()
+ );
+ }
+
+ return $infoText;
+ }
+
+ private function print_code() {
+
+ $code = $this->queryString ? htmlspecialchars( $this->queryString ) . "\n" : "\n";
+
+ foreach ( $this->printouts as $printout ) {
+ if ( ( $serialization = $printout->getSerialisation( true ) ) !== '' ) {
+ $code .= ' |' . $serialization . "\n";
+ }
+ }
+
+ foreach ( $this->params as $param ) {
+
+ if ( !isset( $this->parameters[$param->getName()] ) ) {
+ continue;
+ }
+
+ if ( !$param->wasSetToDefault() ) {
+ $code .= ' |' . htmlspecialchars( $param->getName() ) . '=';
+ $code .= htmlspecialchars( $this->parameters[$param->getName()] ) . "\n";
+ }
+ }
+
+ return '{{#ask: ' . $code . '}}';
+ }
+
+ private function print_borrowed_msg( &$html, &$searchInfoText ) {
+
+ if ( !$this->isBorrowedMode ) {
+ return;
+ }
+
+ $borrowedMessage = $this->getRequest()->getVal( 'bMsg' );
+
+ if ( isset( $this->parameters['bmsg'] ) ) {
+ $borrowedMessage = $this->parameters['bmsg'];
+ }
+
+ $searchInfoText = '';
+
+ if ( $borrowedMessage !== null && wfMessage( $borrowedMessage )->exists() ) {
+ $html = wfMessage( $borrowedMessage, $this->queryString )->parse();
+ }
+
+ $borrowedTitle = $this->getRequest()->getVal( 'bTitle' );
+
+ if ( isset( $this->parameters['btitle'] ) ) {
+ $borrowedTitle = $this->parameters['btitle'];
+ }
+
+ if ( $borrowedTitle !== null && wfMessage( $borrowedTitle )->exists() ) {
+ $this->getOutput()->setPageTitle( wfMessage( $borrowedTitle )->text() );
+ }
+ }
+
+ private function newUrlArgs() {
+
+ $urlArgs = new UrlArgs();
+
+ // build parameter strings for URLs, based on current settings
+ $urlArgs->set( 'q', $this->queryString );
+
+ $tmp_parray = [];
+
+ foreach ( $this->parameters as $key => $value ) {
+ if ( !in_array( $key, [ 'sort', 'order', 'limit', 'offset', 'title' ] ) ) {
+ $tmp_parray[$key] = $value;
+ }
+ }
+
+ $urlArgs->set( 'p', Infolink::encodeParameters( $tmp_parray ) );
+ $printoutstring = '';
+
+ /**
+ * @var PrintRequest $printout
+ */
+ foreach ( $this->printouts as $printout ) {
+ $printoutstring .= $printout->getSerialisation( true ) . "\n";
+ }
+
+ if ( $printoutstring !== '' ) {
+ $urlArgs->set( 'po', $printoutstring );
+ }
+
+ if ( array_key_exists( 'sort', $this->parameters ) ) {
+ $urlArgs->set( 'sort', $this->parameters['sort'] );
+ }
+
+ if ( array_key_exists( 'order', $this->parameters ) ) {
+ $urlArgs->set( 'order', $this->parameters['order'] );
+ }
+
+ if ( $this->getRequest()->getCheck( 'bTitle' ) ) {
+ $urlArgs->set( 'bTitle', $this->getRequest()->getVal( 'bTitle' ) );
+ $urlArgs->set( 'bMsg', $this->getRequest()->getVal( 'bMsg' ) );
+ }
+
+ if ( isset( $this->parameters['btitle'] ) ) {
+ $urlArgs->set( 'bTitle', $this->parameters['btitle'] );
+ $urlArgs->set( 'bMsg', $this->parameters['bmsg'] );
+ }
+
+ return $urlArgs;
+ }
+
+ private function getQueryResult() {
+
+ $res = null;
+ $debug = '';
+ $duration = 0;
+ $queryobj = null;
+ $native_result = '';
+
+ // Copy the printout to retain the original state while in case of no
+ // specific subject (THIS) request extend the query with a
+ // `PrintRequest::PRINT_THIS` column
+
+ QueryProcessor::addThisPrintout( $this->printouts, $this->parameters );
+
+ $params = QueryProcessor::getProcessedParams(
+ $this->parameters,
+ $this->printouts
+ );
+
+ $this->parameters['format'] = $params['format']->getValue();
+ $this->params = $params;
+
+ $queryobj = QueryProcessor::createQuery(
+ $this->queryString,
+ $params,
+ QueryProcessor::SPECIAL_PAGE,
+ $this->parameters['format'],
+ $this->printouts
+ );
+
+ if ( $this->getRequest()->getVal( 'cache' ) === 'no' ) {
+ $queryobj->setOption( SMWQuery::NO_CACHE, true );
+ }
+
+ if ( $this->getRequest()->getVal( 'native_result', false ) ) {
+ $queryobj->setOption( 'native_result', true );
+ }
+
+ $queryobj->setOption( SMWQuery::PROC_CONTEXT, 'SpecialAsk' );
+ $source = $params['source']->getValue();
+ $noSource = $source === '';
+
+ if ( $this->getRequest()->getVal( 'q_engine' ) === 'sql_store' ) {
+ $source = 'sql_store';
+ }
+
+ $qp = [];
+
+ foreach ( $params as $key => $value) {
+ $qp[$key] = $value->getValue();
+ }
+
+ $queryobj->setOption( 'query.params', $qp );
+
+ /**
+ * @var QueryEngine $queryEngine
+ */
+ $queryEngine = $this->querySourceFactory->get(
+ $source
+ );
+
+ // Measure explicit to account for a federated (sourced) query
+ $duration = microtime( true );
+
+ /**
+ * @var QueryResult $res
+ */
+ $res = $queryEngine->getQueryResult(
+ $queryobj
+ );
+
+ if ( $this->getRequest()->getVal( 'native_result', false ) && isset( $queryobj->native_result ) ) {
+ $native_result = $queryobj->native_result;
+ }
+
+ $duration = number_format( ( microtime( true ) - $duration ), 4, '.', '' );
+
+ // Allow to generate a debug output
+ if ( $this->getRequest()->getVal( 'debug' ) && $noSource ) {
+
+ $queryobj = QueryProcessor::createQuery(
+ $this->queryString,
+ $params,
+ QueryProcessor::SPECIAL_PAGE,
+ 'debug',
+ $this->printouts
+ );
+
+ $debug = $queryEngine->getQueryResult( $queryobj );
+ }
+
+ return [ $res, $debug, $duration, $queryobj, $native_result ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialBrowse.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialBrowse.php
new file mode 100644
index 00000000..dcea1342
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialBrowse.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\Encoder;
+use SMW\MediaWiki\Specials\Browse\HtmlBuilder;
+use SMW\MediaWiki\Specials\Browse\FieldBuilder;
+use SMW\Message;
+use SMWInfolink as Infolink;
+use SpecialPage;
+
+/**
+ * A factbox view on one specific article, showing all the Semantic data about it
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author mwjames
+ */
+class SpecialBrowse extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ */
+ public function __construct() {
+ parent::__construct( 'Browse', '', true, false, 'default', true );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ *
+ * @param string $query string
+ */
+ public function execute( $query ) {
+
+ $this->setHeaders();
+ $webRequest = $this->getRequest();
+
+ // get the GET parameters
+ $articletext = $webRequest->getVal( 'article' );
+
+ if ( $webRequest->getText( 'cl', '' ) !== '' ) {
+ $query = Infolink::decodeCompactLink( 'cl:'. $webRequest->getText( 'cl' ) );
+ } else {
+ $query = Infolink::decodeCompactLink( $query );
+ }
+
+ $isEmptyRequest = $query === null && ( $webRequest->getVal( 'article' ) === '' || $webRequest->getVal( 'article' ) === null );
+
+ // @see SMWInfolink::encodeParameters
+ if ( $query === null && $this->getRequest()->getCheck( 'x' ) ) {
+ $query = $this->getRequest()->getVal( 'x' );
+ }
+
+ // Auto-generated link is marked with a leading :
+ if ( $query !== '' && $query{0} === ':' ) {
+ $articletext = Encoder::unescape( $query );
+ } elseif ( $articletext === null ) {
+ $articletext = $query;
+ }
+
+ // no GET parameters? Then try the URL
+ if ( $articletext === null ) {
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newTypeIDValue(
+ '_wpg',
+ $articletext
+ );
+
+ $out = $this->getOutput();
+ $out->setHTMLTitle( $dataValue->getTitle() );
+
+ $out->addModuleStyles( [
+ 'mediawiki.ui',
+ 'mediawiki.ui.button',
+ 'mediawiki.ui.input',
+ 'ext.smw.browse.styles'
+ ] );
+
+ $out->addModules( [
+ 'ext.smw.browse',
+ 'ext.smw.tooltip'
+ ] );
+
+ $out->addHTML(
+ $this->buildHTML( $webRequest, $dataValue, $isEmptyRequest )
+ );
+
+ $this->addExternalHelpLinks( $dataValue );
+ }
+
+ private function buildHTML( $webRequest, $dataValue, $isEmptyRequest ) {
+
+ if ( $isEmptyRequest && !$this->including() ) {
+ return Message::get( 'smw-browse-intro', Message::TEXT, Message::USER_LANGUAGE ) . FieldBuilder::createQueryForm();
+ }
+
+ if ( !$dataValue->isValid() ) {
+
+ foreach ( $dataValue->getErrors() as $error ) {
+ $error = Message::decode( $error, Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-callout smw-callout-error'
+ ],
+ Message::get( [ 'smw-browse-invalid-subject', $error ], Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ if ( !$this->including() ) {
+ $html .= FieldBuilder::createQueryForm( $webRequest->getVal( 'article' ) );
+ }
+
+ return $html;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $dataItem = $dataValue->getDataItem();
+
+ $htmlBuilder = $this->newHtmlBuilder(
+ $webRequest,
+ $dataItem,
+ $applicationFactory->getStore(),
+ $applicationFactory->getSettings()
+ );
+
+ $options = $htmlBuilder->getOptions();
+
+ if ( $webRequest->getVal( 'format' ) === 'json' ) {
+ $semanticDataSerializer = $applicationFactory->newSerializerFactory()->newSemanticDataSerializer();
+ $res = $semanticDataSerializer->serialize(
+ $applicationFactory->getStore()->getSemanticData( $dataItem )
+ );
+
+ $this->getOutput()->disable();
+ header( 'Content-type: ' . 'application/json' . '; charset=UTF-8' );
+ echo json_encode( $res, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ if ( $webRequest->getVal( 'output' ) === 'legacy' || !$htmlBuilder->getOption( 'api' ) ) {
+ return $htmlBuilder->legacy();
+ }
+
+ // Ajax/API is doing the data fetch
+ return $htmlBuilder->placeholder();
+ }
+
+ private function newHtmlBuilder( $webRequest, $dataItem, $store, $settings ) {
+
+ $htmlBuilder = new HtmlBuilder(
+ $store,
+ $dataItem
+ );
+
+ $htmlBuilder->setOptions(
+ [
+ 'dir' => $webRequest->getVal( 'dir' ),
+ 'group' => $webRequest->getVal( 'group' ),
+ 'printable' => $webRequest->getVal( 'printable' ),
+ 'offset' => $webRequest->getVal( 'offset' ),
+ 'including' => $this->including(),
+ 'showInverse' => $settings->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_SHOW_INVERSE ),
+ 'showAll' => $settings->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_SHOW_INCOMING ),
+ 'showGroup' => $settings->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_SHOW_GROUP ),
+ 'showSort' => $settings->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_SHOW_SORTKEY ),
+ 'api' => $settings->isFlagSet( 'smwgBrowseFeatures', SMW_BROWSE_USE_API ),
+
+ // WebRequest::getGPCVal/getVal doesn't understand `.` as in
+ // `valuelistlimit.out`
+
+ 'valuelistlimit.out' => $webRequest->getVal(
+ 'valuelistlimit-out',
+ $settings->dotGet( 'smwgPagingLimit.browse.valuelist.outgoing' )
+ ),
+ 'valuelistlimit.in' => $webRequest->getVal(
+ 'valuelistlimit-in',
+ $settings->dotGet( 'smwgPagingLimit.browse.valuelist.incoming' )
+ ),
+ ]
+ );
+
+ return $htmlBuilder;
+ }
+
+ private function addExternalHelpLinks( $dataValue ) {
+
+ if ( $this->getRequest()->getVal( 'printable' ) === 'yes' ) {
+ return null;
+ }
+
+ if ( $dataValue->isValid() ) {
+ $link = SpecialPage::getTitleFor( 'ExportRDF', $dataValue->getTitle()->getPrefixedText() );
+
+ $this->getOutput()->setIndicators( [
+ 'browse' => Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-indicator-rdflink'
+ ],
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => $link->getLocalUrl( 'syntax=rdf' )
+ ],
+ 'RDF'
+ )
+ )
+ ] );
+ }
+
+ $this->addHelpLink( wfMessage( 'smw-specials-browse-helplink' )->escaped(), true );
+ }
+
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialDeferredRequestDispatcher.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialDeferredRequestDispatcher.php
new file mode 100644
index 00000000..c38343d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialDeferredRequestDispatcher.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SpecialPage;
+use Title;
+
+/**
+ * This class is the receiving endpoint for the `DeferredRequestDispatchManager` invoked
+ * job request.
+ *
+ * This special page is not expected to interact with a user and therefore it is
+ * unlisted.
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SpecialDeferredRequestDispatcher extends SpecialPage {
+
+ /**
+ * @var boolean
+ */
+ private $allowedToModifyHttpHeader = true;
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'DeferredRequestDispatcher', '', false );
+ }
+
+ /**
+ * SpecialPage::doesWrites
+ *
+ * @return boolean
+ */
+ public function doesWrites() {
+ return true;
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'maintenance';
+ }
+
+ /**
+ * Only used during unit testing
+ *
+ * @since 2.3
+ */
+ public function disallowToModifyHttpHeader() {
+ $this->allowedToModifyHttpHeader = false;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return string
+ */
+ public static function getTargetURL() {
+ return SpecialPage::getTitleFor( 'DeferredRequestDispatcher')->getFullURL();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ public static function getRequestToken( $key ) {
+ return md5( $key . $GLOBALS['wgSecretKey'] );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ $this->getOutput()->disable();
+
+ if ( wfReadOnly() ) {
+ return $this->modifyHttpHeader( "HTTP/1.0 423 Locked", 'Wiki is in read-only mode.' );
+ }
+
+ if ( !$this->isHttpRequestMethod( 'HEAD' ) && !$this->isHttpRequestMethod( 'POST' ) ) {
+ return $this->modifyHttpHeader( "HTTP/1.0 400 Bad Request", 'The special page requires a POST/HEAD request.' );
+ }
+
+ $parameters = json_decode(
+ $this->getRequest()->getVal( 'parameters' ),
+ true
+ );
+
+ if ( $this->isHttpRequestMethod( 'POST' ) && self::getRequestToken( $parameters['timestamp'] ) !== $parameters['requestToken'] ) {
+ return $this->modifyHttpHeader( "HTTP/1.0 400 Bad Request", 'Invalid or staled requestToken was provided for the request' );
+ }
+
+ $this->modifyHttpHeader( "HTTP/1.0 202 Accepted" );
+
+ if ( !isset( $parameters['async-job'] ) ) {
+ return;
+ }
+
+ return $this->doRunJob( $parameters, ApplicationFactory::getInstance()->getMediaWikiLogger() );
+ }
+
+ private function modifyHttpHeader( $header, $message = '' ) {
+
+ if ( !$this->allowedToModifyHttpHeader ) {
+ return null;
+ }
+
+ ignore_user_abort( true );
+ header( $header );
+ print $message;
+ ob_flush();
+ flush();
+
+ // @see SpecialRunJobs
+ // MW 1.27 / https://phabricator.wikimedia.org/T115413
+ // Once the client receives this response, it can disconnect
+ set_error_handler( function ( $errno, $errstr ) {
+ if ( strpos( $errstr, 'Cannot modify header information' ) !== false ) {
+ return true; // bug T115413
+ }
+ // Delegate unhandled errors to the default handlers
+ return false;
+ } );
+ }
+
+ private function doRunJob( $parameters, $logger ) {
+
+ $type = $parameters['async-job']['type'];
+ $title = Title::newFromDBkey( $parameters['async-job']['title'] );
+
+ if ( $title === null ) {
+ return $logger->info( __METHOD__ . " invalid title" );
+ }
+
+ $logger->info( __METHOD__ . ' ' . $type . ' :: ' . $title->getPrefixedDBkey() . '#' . $title->getNamespace() );
+
+ $job = ApplicationFactory::getInstance()->newJobFactory()->newByType(
+ $type,
+ $title,
+ $parameters
+ );
+
+ $job->run();
+
+ return true;
+ }
+
+ // 1.19 doesn't have a getMethod
+ private function isHttpRequestMethod( $key ) {
+
+ if ( method_exists( $this->getRequest(), 'getMethod') ) {
+ return $this->getRequest()->getMethod() == $key;
+ }
+
+ return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] == $key : false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPageProperty.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPageProperty.php
new file mode 100644
index 00000000..d1b287f0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPageProperty.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\Encoder;
+use SMW\MediaWiki\Specials\PageProperty\PageBuilder;
+use SMW\Options;
+use SMW\RequestOptions;
+use SMWInfolink as Infolink;
+use SpecialPage;
+
+/**
+ * This special page implements a view on a object-relation pair, i.e. a page that
+ * shows all the values of a property for a certain page.
+ *
+ * This is typically used for overflow results from other dynamic output pages.
+ *
+ * @license GNU GPL v2+
+ * @since 1.4
+ *
+ * @author Denny Vrandecic
+ * @author mwjames
+ */
+class SpecialPageProperty extends SpecialPage {
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'PageProperty', '', false );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ $request = $this->getRequest();
+
+ if ( $request->getText( 'cl', '' ) !== '' ) {
+ $query = Infolink::decodeCompactLink( 'cl:'. $request->getText( 'cl' ) );
+ } else {
+ $query = Infolink::decodeCompactLink( $query );
+ }
+
+ if ( $query !== '' ) {
+ $query = Encoder::unescape( $query );
+ }
+
+ // Get parameters
+ $pagename = $request->getVal( 'from' );
+ $propname = $request->getVal( 'type' );
+
+ // No GET parameters? Try the URL with the convention `PageName::PropertyName`
+ if ( $propname == '' ) {
+ $queryparts = explode( '::', $query );
+ $propname = $query;
+ if ( count( $queryparts ) > 1 ) {
+ $pagename = $queryparts[0];
+ $propname = implode( '::', array_slice( $queryparts, 1 ) );
+ }
+ }
+
+ $options = new Options(
+ [
+ 'from' => $pagename,
+ 'type' => $propname,
+ 'property' => $propname,
+ 'limit' => $request->getVal( 'limit', 20 ),
+ 'offset' => $request->getVal( 'offset', 0 ),
+ ]
+ );
+
+ $this->addHelpLink(
+ wfMessage( 'smw-special-pageproperty-helplink' )->escaped(),
+ true
+ );
+
+ $this->load( $options );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+ private function load( $options ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ $subject = $dataValueFactory->newTypeIDValue(
+ '_wpg',
+ $options->get( 'from' )
+ );
+
+ $propertyValue = $dataValueFactory->newPropertyValueByLabel(
+ $options->get( 'property' )
+ );
+
+ $pagename = '';
+ $propname = '';
+
+ if ( $subject->isValid() ) {
+ $pagename = $subject->getPrefixedText();
+ }
+
+ if ( $propertyValue->isValid() ) {
+ $propname = $propertyValue->getWikiValue();
+ }
+
+ $options->set( 'from', $pagename );
+ $options->set( 'property', $propname );
+ $options->set( 'type', $propname );
+
+ $htmlFormRenderer = $applicationFactory->newMwCollaboratorFactory()->newHtmlFormRenderer(
+ $this->getContext()->getTitle(),
+ $this->getLanguage()
+ );
+
+ $pageBuilder = new PageBuilder(
+ $htmlFormRenderer,
+ $options
+ );
+
+ $html = '';
+
+ // No property given, no results
+ if ( $propname === '' ) {
+ $html .= $pageBuilder->buildForm();
+ $html .= wfMessage( 'smw_result_noresults' )->text();
+ } else {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( $options->get( 'limit' ) + 1 );
+ $requestOptions->setOffset( $options->get( 'offset' ) );
+ $requestOptions->sort = true;
+
+ // Restrict the request otherwise the entire SemanticData record
+ // is fetched which can in case of a subject with a large
+ // subobject/subpage pool create excessive DB queries that are not
+ // used for the display
+ $requestOptions->conditionConstraint = true;
+
+ $dataItem = $pagename !== '' ? $subject->getDataItem() : null;
+
+ $results = $applicationFactory->getStore()->getPropertyValues(
+ $dataItem,
+ $propertyValue->getDataItem(),
+ $requestOptions
+ );
+
+ $html .= $pageBuilder->buildForm( count( $results ) );
+ $html .= $pageBuilder->buildHtml( $results );
+ }
+
+ $output = $this->getOutput();
+ $output->setPagetitle( wfMessage( 'pageproperty' )->text() );
+
+ $output->addModuleStyles( 'ext.smw.special.style' );
+ $output->addModules( 'ext.smw.tooltip' );
+
+ $output->addModules( 'ext.smw.autocomplete.property' );
+ $output->addModules( 'ext.smw.autocomplete.article' );
+
+ $output->addHTML( $html );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialProcessingErrorList.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialProcessingErrorList.php
new file mode 100644
index 00000000..6236f338
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialProcessingErrorList.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SpecialPage;
+
+/**
+ * Convenience special page that just redirects to Special:Ask with a preset
+ * of necessary parameters to query the processing error list.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpecialProcessingErrorList extends SpecialPage {
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'ProcessingErrorList' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ $limit = ApplicationFactory::getInstance()->getSettings()->dotGet( 'smwgPagingLimit.errorlist' );
+
+ $this->getOutput()->redirect(
+ $this->getLocalAskRedirectUrl( $limit )
+ );
+
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $limit
+ *
+ * @return string
+ */
+ public function getLocalAskRedirectUrl( $limit = 20 ) {
+ return SpecialPage::getTitleFor( 'Ask' )->getLocalUrl(
+ [
+ 'q' => '[[Has processing error text::+]]',
+ 'po' => '?Has improper value for|?Has processing error text',
+ 'p' => 'class=sortable-20wikitable-20smwtable-2Dstriped',
+ 'eq' => 'no',
+ 'limit' => $limit,
+ 'bTitle' => 'processingerrorlist',
+ 'bMsg' => 'smw-processingerrorlist-intro'
+ ]
+ );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPropertyLabelSimilarity.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPropertyLabelSimilarity.php
new file mode 100644
index 00000000..870eccec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialPropertyLabelSimilarity.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Specials\PropertyLabelSimilarity\ContentsBuilder;
+use SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup;
+use SpecialPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpecialPropertyLabelSimilarity extends SpecialPage {
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'PropertyLabelSimilarity' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ $this->setHeaders();
+ $output = $this->getOutput();
+ $webRequest = $this->getRequest();
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $store = $applicationFactory->getStore( '\SMW\SQLStore\SQLStore' );
+
+ $propertyLabelSimilarityLookup = new PropertyLabelSimilarityLookup(
+ $store
+ );
+
+ $propertyLabelSimilarityLookup->setExemptionProperty(
+ $applicationFactory->getSettings()->get( 'smwgSimilarityLookupExemptionProperty' )
+ );
+
+ $htmlFormRenderer = $applicationFactory->newMwCollaboratorFactory()->newHtmlFormRenderer(
+ $this->getContext()->getTitle(),
+ $this->getLanguage()
+ );
+
+ $contentsBuilder = new ContentsBuilder(
+ $propertyLabelSimilarityLookup,
+ $htmlFormRenderer
+ );
+
+ $threshold = (int)$webRequest->getText( 'threshold', 90 );
+ $type = $webRequest->getText( 'type', false );
+
+ $offset = (int)$webRequest->getText( 'offset', 0 );
+ $limit = (int)$webRequest->getText( 'limit', 50 );
+
+ $requestOptions = $applicationFactory->getQueryFactory()->newRequestOptions();
+ $requestOptions->setLimit( $limit );
+ $requestOptions->setOffset( $offset );
+
+ $requestOptions->addExtraCondition(
+ [
+ 'type' => $type,
+ 'threshold' => $threshold
+ ]
+ );
+
+ $output->addHtml(
+ $contentsBuilder->getHtml( $requestOptions )
+ );
+
+ return true;
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialSearchByProperty.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialSearchByProperty.php
new file mode 100644
index 00000000..edd805a7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialSearchByProperty.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Specials\SearchByProperty\PageBuilder;
+use SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions;
+use SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup;
+use SMWInfolink as Infolink;
+use SpecialPage;
+
+/**
+ * A special page to search for entities that have a certain property with
+ * a certain value.
+ *
+ * This special page for Semantic MediaWiki implements a view on a
+ * relation-object pair,i.e. a typed backlink. For example, it shows me all
+ * persons born in Croatia, or all winners of the Academy Award for best actress.
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SpecialSearchByProperty extends SpecialPage {
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public function __construct() {
+ parent::__construct( 'SearchByProperty' );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ */
+ public function execute( $query ) {
+
+ $this->setHeaders();
+ $output = $this->getOutput();
+ $request = $this->getRequest();
+
+ $output->setPageTitle( $this->msg( 'searchbyproperty' )->text() );
+ $output->addModules( 'ext.smw.tooltip' );
+ $output->addModules( 'ext.smw.autocomplete.property' );
+
+ list( $limit, $offset ) = $this->getLimitOffset();
+
+ if ( $request->getText( 'cl', '' ) !== '' ) {
+ $query = Infolink::decodeCompactLink( 'cl:'. $request->getText( 'cl' ) );
+ } else {
+ $query = Infolink::decodeCompactLink( $query );
+ }
+
+ // @see SMWInfolink::encodeParameters
+ if ( $query === null && $this->getRequest()->getCheck( 'x' ) ) {
+ $query = $this->getRequest()->getVal( 'x' );
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $requestOptions = [
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'property' => $this->getRequest()->getVal( 'property' ),
+ 'value' => $this->getRequest()->getVal( 'value' ),
+ 'nearbySearchForType' => $applicationFactory->getSettings()->get( 'smwgSearchByPropertyFuzzy' )
+ ];
+
+ $htmlFormRenderer = $applicationFactory->newMwCollaboratorFactory()->newHtmlFormRenderer(
+ $this->getContext()->getTitle(),
+ $this->getLanguage()
+ );
+
+ $pageBuilder = new PageBuilder(
+ $htmlFormRenderer,
+ new PageRequestOptions( $query, $requestOptions ),
+ new QueryResultLookup( $applicationFactory->getStore() )
+ );
+
+ $output->addHTML( $pageBuilder->getHtml() );
+ }
+
+ private function getLimitOffset() {
+ return $this->getRequest()->getLimitOffset();
+ }
+
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialURIResolver.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialURIResolver.php
new file mode 100644
index 00000000..ed1d30dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Specials/SpecialURIResolver.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\MediaWiki\Specials;
+
+use SMW\Exporter\Escaper;
+use SpecialPage;
+use Title;
+
+/**
+ * Resolve (redirect) pretty URIs (or "short URIs") to the equivalent full MediaWiki
+ * representation.
+ *
+ * @license GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Denny Vrandecic
+ */
+class SpecialURIResolver extends SpecialPage {
+
+ /**
+ * @see SpecialPage::__construct
+ */
+ public function __construct() {
+ parent::__construct( 'URIResolver', '', false );
+ }
+
+ /**
+ * @see SpecialPage::execute
+ *
+ * @param string $query string
+ */
+ public function execute( $query ) {
+ $out = $this->getOutput();
+
+ // #2344, It is believed that when no HTTP_ACCEPT is available then a
+ // request came from a "defect" mobile device without a correct accept
+ // header
+ if ( !isset( $_SERVER['HTTP_ACCEPT'] ) ) {
+ $_SERVER['HTTP_ACCEPT'] = '';
+ }
+
+ if ( $query === null || trim( $query ) === '' ) {
+ if ( stristr( $_SERVER['HTTP_ACCEPT'], 'RDF' ) ) {
+ $out->redirect( SpecialPage::getTitleFor( 'ExportRDF' )->getFullURL( [ 'stats' => '1' ] ), '303' );
+ } else {
+ $this->setHeaders();
+ $out->addHTML(
+ '<p>' .
+ wfMessage( 'smw_uri_doc', 'https://www.w3.org/2001/tag/issues.html#httpRange-14' )->parse() .
+ '</p>'
+ );
+ }
+ } else {
+ $query = Escaper::decodeUri( $query );
+ $query = str_replace( '_', '%20', $query );
+ $query = urldecode( $query );
+ $title = Title::newFromText( $query );
+
+ // In case the title doesn't exist throw an error page
+ if ( $title === null ) {
+ $out->showErrorPage( 'badtitle', 'badtitletext' );
+ } elseif ( stristr( $_SERVER['HTTP_ACCEPT'], 'RDF' ) ) {
+ $out->redirect(
+ SpecialPage::getTitleFor( 'ExportRDF', $title->getPrefixedText() )->getFullURL( [ 'xmlmime' => 'rdf' ] )
+ );
+ } else {
+ $out->redirect( $title->getFullURL(), '303' );
+ }
+ }
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ */
+ protected function getGroupName() {
+ return 'smw_group';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/StripMarkerDecoder.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/StripMarkerDecoder.php
new file mode 100644
index 00000000..ec52d8b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/StripMarkerDecoder.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Parser;
+use StripState;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class StripMarkerDecoder {
+
+ /**
+ * @var StripState
+ */
+ private $stripState;
+
+ /**
+ * @var boolean
+ */
+ private $isSupported = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param StripState $stripState
+ */
+ public function __construct( StripState $stripState ) {
+ $this->stripState = $stripState;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $decoderState
+ */
+ public function isSupported( $isSupported ) {
+ $this->isSupported = $isSupported;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function canUse() {
+ return $this->isSupported;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return boolean
+ */
+ public function hasStripMarker( $text ) {
+ return strpos( $text, Parser::MARKER_SUFFIX );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public function decode( $value ) {
+
+ $hasStripMarker = false;
+
+ if ( $this->canUse() ) {
+ $hasStripMarker = $this->hasStripMarker( $value );
+ }
+
+ if ( $hasStripMarker ) {
+ $value = $this->unstrip( $value );
+ }
+
+ return $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return text
+ */
+ public function unstrip( $text ) {
+
+ // Escape the text case to avoid any HTML elements
+ // cause an issue during parsing
+ return str_replace(
+ [ '<', '>', ' ', '[', '{', '=', "'", ':', "\n" ],
+ [ '&lt;', '&gt;', ' ', '&#x005B;', '&#x007B;', '&#x003D;', '&#x0027;', '&#58;', "<br />" ],
+ $this->doUnstrip( $text )
+ );
+ }
+
+ public function doUnstrip( $text ) {
+
+ if ( ( $value = $this->stripState->unstripNoWiki( $text ) ) !== '' && !$this->hasStripMarker( $value ) ) {
+ return $this->addNoWikiToUnstripValue( $value );
+ }
+
+ if ( ( $value = $this->stripState->unstripGeneral( $text ) ) !== '' && !$this->hasStripMarker( $value ) ) {
+ return $value;
+ }
+
+ return $this->doUnstrip( $value );
+ }
+
+ private function addNoWikiToUnstripValue( $text ) {
+ return '<nowiki>' . $text . '</nowiki>';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleFactory.php
new file mode 100644
index 00000000..73035cb0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleFactory.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TitleFactory {
+
+ /**
+ * @since 2.0
+ *
+ * @param string $text
+ *
+ * @return Title|null
+ */
+ public function newFromText( $text, $namespace = null ) {
+
+ if ( $namespace === null ) {
+ $namespace = NS_MAIN;
+ }
+
+ return Title::newFromText( $text, $namespace );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ *
+ * @return Title|null
+ */
+ public function newFromID( $id ) {
+ return Title::newFromID( $id );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $ids
+ *
+ * @return Title[]
+ */
+ public function newFromIDs( $ids ) {
+ return Title::newFromIDs( $ids );
+ }
+ /**
+ * @since 3.0
+ *
+ * @param int $ns
+ * @param string $title
+ * @param string $fragment
+ * @param string $interwiki
+ *
+ * @return Title|null
+ */
+ public function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
+ return Title::makeTitleSafe( $ns, $title, $fragment, $interwiki );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleLookup.php
new file mode 100644
index 00000000..15f0377f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/TitleLookup.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace SMW\MediaWiki;
+
+use RuntimeException;
+use Title;
+
+/**
+ * A convenience class to encapsulate MW related database interaction
+ *
+ * @note This is an internal class and should not be used outside of smw-core
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class TitleLookup {
+
+ /**
+ * @var Database
+ */
+ private $connection = null;
+
+ /**
+ * @var integer
+ */
+ private $namespace = null;
+
+ /**
+ * @since 1.9.2
+ *
+ * @param Database $connection
+ */
+ public function __construct( Database $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param int $namespace
+ *
+ * @return TitleLookup
+ */
+ public function setNamespace( $namespace ) {
+ $this->namespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return Title[]
+ * @throws RuntimeException
+ */
+ public function selectAll() {
+
+ if ( $this->namespace === null ) {
+ throw new RuntimeException( 'Unrestricted selection without a namespace is not supported' );
+ }
+
+ if ( $this->namespace === NS_CATEGORY ) {
+ $tableName = 'category';
+ $fields = [ 'cat_title' ];
+ $conditions = '';
+ $options = [ 'USE INDEX' => 'cat_title' ];
+ } else {
+ $tableName = 'page';
+ $fields = [ 'page_namespace', 'page_title' ];
+ $conditions = [ 'page_namespace' => $this->namespace ];
+ $options = [ 'USE INDEX' => 'PRIMARY' ];
+ }
+
+ $res = $this->connection->select(
+ $tableName,
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ return $this->makeTitlesFromSelection( $res );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Title[]
+ */
+ public function getRedirectPages() {
+
+ $conditions = [];
+ $options = [];
+
+ $res = $this->connection->select(
+ [ 'page', 'redirect' ],
+ [ 'page_namespace', 'page_title' ],
+ $conditions,
+ __METHOD__,
+ $options,
+ [ 'page' => [ 'INNER JOIN', [ 'page_id=rd_from' ] ] ]
+ );
+
+ return $this->makeTitlesFromSelection( $res );
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param int $startId
+ * @param int $endId
+ *
+ * @return Title[]
+ * @throws RuntimeException
+ */
+ public function selectByIdRange( $startId = 0, $endId = 0 ) {
+
+ if ( $this->namespace === null ) {
+ throw new RuntimeException( 'Unrestricted selection without a namespace is not supported' );
+ }
+
+ if ( $this->namespace === NS_CATEGORY ) {
+ $tableName = 'category';
+ $fields = [ 'cat_title', 'cat_id' ];
+ $conditions = [ "cat_id BETWEEN $startId AND $endId" ];
+ $options = [ 'ORDER BY' => 'cat_id ASC', 'USE INDEX' => 'cat_title' ];
+ } else {
+ $tableName = 'page';
+ $fields = [ 'page_namespace', 'page_title', 'page_id' ];
+ $conditions = [ "page_id BETWEEN $startId AND $endId" ] + [ 'page_namespace' => $this->namespace ];
+ $options = [ 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ];
+ }
+
+ $res = $this->connection->select(
+ $tableName,
+ $fields,
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ return $this->makeTitlesFromSelection( $res );
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return int
+ */
+ public function getMaxId() {
+
+ if ( $this->namespace === NS_CATEGORY ) {
+ $tableName = 'category';
+ $var = 'MAX(cat_id)';
+ } else {
+ $tableName = 'page';
+ $var = 'MAX(page_id)';
+ }
+
+ return (int)$this->connection->selectField(
+ $tableName,
+ $var,
+ false,
+ __METHOD__
+ );
+ }
+
+ protected function makeTitlesFromSelection( $res ) {
+
+ $pages = [];
+
+ if ( $res === false ) {
+ return $pages;
+ }
+
+ foreach ( $res as $row ) {
+ $pages[] = $this->newTitleFromRow( $row );
+ }
+
+ return $pages;
+ }
+
+ private function newTitleFromRow( $row ) {
+
+ if ( $this->namespace === NS_CATEGORY ) {
+ $ns = NS_CATEGORY;
+ $title = $row->cat_title;
+ } elseif ( isset( $row->rd_namespace ) ) {
+ $ns = $row->rd_namespace;
+ $title = $row->rd_title;
+ } else {
+ $ns = $row->page_namespace;
+ $title = $row->page_title;
+ }
+
+ return Title::makeTitle( $ns, $title );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Message.php b/www/wiki/extensions/SemanticMediaWiki/src/Message.php
new file mode 100644
index 00000000..7548a673
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Message.php
@@ -0,0 +1,254 @@
+<?php
+
+namespace SMW;
+
+use Closure;
+use Language;
+
+/**
+ * @private
+ *
+ * Object agnostic handler class that encapsulates a foreign Message object
+ * (e.g MW's Message class). It is expected that a registered handler returns a
+ * simple string representation for the parameters, type, and language given.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class Message {
+
+ /**
+ * @var array
+ */
+ private static $messageCache = null;
+
+ /**
+ * PoolCache ID
+ */
+ const POOLCACHE_ID = 'message.cache';
+
+ /**
+ * MW processing mode
+ */
+ const TEXT = 0x2;
+ const ESCAPED = 0x4;
+ const PARSE = 0x8;
+
+ /**
+ * Predefined language mode
+ */
+ const CONTENT_LANGUAGE = 0x32;
+ const USER_LANGUAGE = 0x64;
+
+ /**
+ * @var array
+ */
+ private static $messageHandler = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param $type
+ * @param Closure $handler
+ */
+ public static function registerCallbackHandler( $type, Closure $handler ) {
+ self::$messageHandler[$type] = $handler;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param $type
+ */
+ public static function deregisterHandlerFor( $type ) {
+ unset( self::$messageHandler[$type] );
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function clear() {
+ self::$messageCache = null;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return FixedInMemoryLruCache
+ */
+ public static function getCache() {
+
+ if ( self::$messageCache === null ) {
+ self::$messageCache = InMemoryPoolCache::getInstance()->getPoolCacheById( self::POOLCACHE_ID, 1000 );
+ }
+
+ return self::$messageCache;
+ }
+
+ /**
+ * Encodes a message into a JSON representation that can transferred,
+ * transformed, and stored while allowing to add an infinite amount of
+ * arguments.
+ *
+ * '[2,"Foo", "Bar"]' => Preferred output type, Message ID, Argument $1 ... $
+ *
+ * @since 2.5
+ *
+ * @param string|array $parameters
+ * @param integer|null $type
+ *
+ * @return string
+ */
+ public static function encode( $message, $type = null ) {
+
+ if ( is_string( $message ) && json_decode( $message ) && json_last_error() === JSON_ERROR_NONE ) {
+ return $message;
+ }
+
+ if ( $type === null ) {
+ $type = self::TEXT;
+ }
+
+ if ( $message === [] ) {
+ return '';
+ }
+
+ $message = (array)$message;
+ $encode = [];
+ $encode[] = $type;
+
+ foreach ( $message as $value ) {
+ // Check if the value is already encoded, and if decode to keep the
+ // structure intact
+ if ( substr( $value, 0, 1 ) === '[' && ( $dc = json_decode( $value, true ) ) && json_last_error() === JSON_ERROR_NONE ) {
+ $encode += $dc;
+ } else {
+ // Normalize arguments like "<strong>Expression error:
+ // Unrecognized word "yyyy".</strong>"
+ $value = strip_tags( htmlspecialchars_decode( $value, ENT_QUOTES ) );
+
+ // - Internally encoded to circumvent the strip_tags which would
+ // remove <, > from values that represent a range
+ // - Encode `::` to prevent the annotation parser to pick the
+ // message value
+ $value = str_replace( [ '%3C', '%3E', "::" ], [ '>', '<', "&#58;&#58;" ], $value );
+
+ $encode[] = $value;
+ }
+ }
+
+ return json_encode( $encode );
+ }
+
+ /**
+ * @FIXME Needs to be MW agnostic !
+ *
+ * @since 2.5
+ *
+ * @param string $messageId
+ *
+ * @return boolean
+ */
+ public static function exists( $message ) {
+ return wfMessage( $message )->exists();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $json
+ * @param integer|null $type
+ * @param integer|null $language
+ *
+ * @return string|boolean
+ */
+ public static function decode( $message, $type = null, $language = null ) {
+
+ $message = json_decode( $message );
+ $asType = null;
+
+ if ( json_last_error() !== JSON_ERROR_NONE || $message === '' || $message === null ) {
+ return false;
+ }
+
+ // If the first element is numeric then its signals the expected message
+ // formatter type
+ if ( isset( $message[0] ) && is_numeric( $message[0] ) ) {
+ $asType = array_shift( $message );
+ }
+
+ // Is it a msgKey or a simple text?
+ if ( isset( $message[0] ) && !self::exists( $message[0] ) ) {
+ return $message[0];
+ }
+
+ return self::get( $message, ( $type !== null ? $type: $asType ), $language );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|array $parameters
+ * @param integer|null $type
+ * @param integer|null $language
+ *
+ * @return string
+ */
+ public static function get( $parameters, $type = null, $language = null ) {
+
+ $handler = null;
+ $parameters = (array)$parameters;
+
+ if ( $type === null ) {
+ $type = self::TEXT;
+ }
+
+ if ( $language === null || !$language ) {
+ $language = self::CONTENT_LANGUAGE;
+ }
+
+ $hash = self::getHash( $parameters, $type, $language );
+
+ if ( $content = self::getCache()->fetch( $hash ) ) {
+ return $content;
+ }
+
+ if ( isset( self::$messageHandler[$type] ) && is_callable( self::$messageHandler[$type] ) ) {
+ $handler = self::$messageHandler[$type];
+ }
+
+ if ( $handler === null ) {
+ return '';
+ }
+
+ $message = call_user_func_array(
+ $handler,
+ [ $parameters, $language ]
+ );
+
+ self::getCache()->save( $hash, $message );
+
+ return $message;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $parameters
+ * @param integer $type
+ * @param integer|string|Language $language
+ *
+ * @return string
+ */
+ public static function getHash( $parameters, $type = null, $language = null ) {
+
+ if ( $language instanceof Language ) {
+ $language = $language->getCode();
+ }
+
+ return md5( json_encode( $parameters ) . '#' . $type . '#' . $language );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/NamespaceExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceExaminer.php
new file mode 100644
index 00000000..113110a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceExaminer.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use MWNamespace;
+
+/**
+ * Examines if a specific namespace is enabled for the usage of the
+ * Semantic MediaWiki extension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NamespaceExaminer {
+
+ /** @var array */
+ private static $instance = null;
+
+ /** @var array */
+ private $registeredNamespaces = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param array $registeredNamespaces
+ */
+ public function __construct( array $registeredNamespaces ) {
+ $this->registeredNamespaces = $registeredNamespaces;
+ }
+
+ /**
+ * Returns a static instance with an invoked global settings array
+ *
+ * @par Example:
+ * @code
+ * \SMW\NamespaceExaminer::getInstance()->isSemanticEnabled( NS_MAIN )
+ * @endcode
+ *
+ * @note Used in smwfIsSemanticsProcessed
+ *
+ * @since 1.9
+ *
+ * @return NamespaceExaminer
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = self::newFromArray( Settings::newFromGlobals()->get( 'smwgNamespacesWithSemanticLinks' ) );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Registers an array of available namespaces
+ *
+ * @par Example:
+ * @code
+ * \SMW\NamespaceExaminer::newFromArray( array( ... ) )->isSemanticEnabled( NS_MAIN )
+ * @endcode
+ *
+ * @since 1.9
+ *
+ * @return NamespaceExaminer
+ */
+ public static function newFromArray( $registeredNamespaces ) {
+ return new self( $registeredNamespaces );
+ }
+
+ /**
+ * Resets static instance
+ *
+ * @since 1.9
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * Returns if a namespace is enabled for semantic processing
+ *
+ * @since 1.9
+ *
+ * @param integer $namespace
+ *
+ * @return boolean
+ * @throws InvalidArgumentException
+ */
+ public function isSemanticEnabled( $namespace ) {
+
+ if ( !is_int( $namespace ) ) {
+ throw new InvalidArgumentException( "{$namespace} is not a number" );
+ }
+
+ if ( !in_array( $namespace, MWNamespace::getValidNamespaces() ) ) {
+ // Bug 51435
+ return false;
+ }
+
+ return $this->isEnabled( $namespace );
+ }
+
+ /**
+ * Asserts if a namespace is enabled
+ *
+ * @since 1.9
+ *
+ * @param integer $namespace
+ *
+ * @return boolean
+ */
+ protected function isEnabled( $namespace ) {
+ return !empty( $this->registeredNamespaces[$namespace] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/NamespaceManager.php b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceManager.php
new file mode 100644
index 00000000..d29c81da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceManager.php
@@ -0,0 +1,268 @@
+<?php
+
+namespace SMW;
+
+use SMW\Lang\Lang;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author others
+ */
+class NamespaceManager {
+
+ /**
+ * @var Lang
+ */
+ private $lang;
+
+ /**
+ * @since 1.9
+ *
+ * @param Lang|null $lang
+ */
+ public function __construct( Lang $lang = null ) {
+ $this->lang = $lang;
+
+ if ( $this->lang === null ) {
+ $this->lang = Lang::getInstance();
+ }
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param &$vars
+ */
+ public function init( &$vars ) {
+
+ if ( !$this->isDefinedConstant( 'SMW_NS_PROPERTY' ) ) {
+ $this->initCustomNamespace( $vars );
+ }
+
+ // Legacy seeting in case some extension request a `smwgContLang` reference
+ if ( empty( $vars['smwgContLang'] ) ) {
+ $vars['smwgContLang'] = $this->lang->fetch( $vars['wgLanguageCode'] );
+ }
+
+ $this->addNamespaceSettings( $vars );
+ $this->addExtraNamespaceSettings( $vars );
+ }
+
+ /**
+ * @see Hooks:CanonicalNamespaces
+ * CanonicalNamespaces initialization
+ *
+ * @note According to T104954 registration via wgExtensionFunctions is to late
+ * and should happen before that
+ *
+ * @see https://phabricator.wikimedia.org/T104954#2391291
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/CanonicalNamespaces
+ * @Bug 34383
+ *
+ * @since 2.5
+ *
+ * @param array &$namespaces
+ */
+ public static function initCanonicalNamespaces( array &$namespaces ) {
+
+ $canonicalNames = self::initCustomNamespace( $GLOBALS )->getCanonicalNames();
+ $namespacesByName = array_flip( $namespaces );
+
+ // https://phabricator.wikimedia.org/T160665
+ // Find any namespace that uses the same canonical name and remove it
+ foreach ( $canonicalNames as $id => $name ) {
+ if ( isset( $namespacesByName[$name] ) ) {
+ unset( $namespaces[$namespacesByName[$name]] );
+ }
+ }
+
+ $namespaces += $canonicalNames;
+
+ return true;
+ }
+
+ /**
+ * @see Hooks:CanonicalNamespaces
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public static function getCanonicalNames() {
+
+ $canonicalNames = [
+ SMW_NS_PROPERTY => 'Property',
+ SMW_NS_PROPERTY_TALK => 'Property_talk',
+ SMW_NS_CONCEPT => 'Concept',
+ SMW_NS_CONCEPT_TALK => 'Concept_talk',
+ SMW_NS_SCHEMA => 'smw/schema',
+ SMW_NS_SCHEMA_TALK => 'smw/schema_talk',
+ SMW_NS_RULE => 'Rule',
+ SMW_NS_RULE_TALK => 'Rule_talk'
+ ];
+
+ return $canonicalNames;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param integer offset
+ *
+ * @return array
+ */
+ public static function buildNamespaceIndex( $offset ) {
+
+ // 100 and 101 used to be occupied by SMW's now obsolete namespaces
+ // "Relation" and "Relation_Talk"
+
+ // 106 and 107 are occupied by the Semantic Forms, we define them here
+ // to offer some (easy but useful) support to SF
+
+ $namespaceIndex = [
+ 'SMW_NS_PROPERTY' => $offset + 2,
+ 'SMW_NS_PROPERTY_TALK' => $offset + 3,
+ //'SF_NS_FORM' => $offset + 6,
+ //'SF_NS_FORM_TALK' => $offset + 7,
+ 'SMW_NS_CONCEPT' => $offset + 8,
+ 'SMW_NS_CONCEPT_TALK' => $offset + 9,
+
+ // #3019 notes "Conflicts with the DPLforum extension ..."
+ //'SMW_NS_SCHEMA' => $offset + 10,
+ //'SMW_NS_SCHEMA_TALK' => $offset + 11,
+
+ 'SMW_NS_SCHEMA' => $offset + 12,
+ 'SMW_NS_SCHEMA_TALK' => $offset + 13,
+
+ 'SMW_NS_RULE' => $offset + 14,
+ 'SMW_NS_RULE_TALK' => $offset + 15,
+ ];
+
+ return $namespaceIndex;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array &$vars
+ * @param Lang|null $lang
+ */
+ public static function initCustomNamespace( &$vars, Lang $lang = null ) {
+
+ $instance = new self( $lang );
+
+ if ( !isset( $vars['smwgNamespaceIndex'] ) ) {
+ $vars['smwgNamespaceIndex'] = 100;
+ }
+
+ $defaultSettings = [
+ 'wgNamespaceAliases',
+ 'wgExtraNamespaces',
+ 'wgNamespacesWithSubpages',
+ 'smwgNamespacesWithSemanticLinks',
+ 'smwgNamespaceIndex',
+ 'wgCanonicalNamespaceNames'
+ ];
+
+ foreach ( $defaultSettings as $key ) {
+ $vars[$key] = !isset( $vars[$key] ) ? [] : $vars[$key];
+ }
+
+ foreach ( $instance->buildNamespaceIndex( $vars['smwgNamespaceIndex'] ) as $ns => $index ) {
+ if ( !$instance->isDefinedConstant( $ns ) ) {
+ define( $ns, $index );
+ };
+ }
+
+ $extraNamespaces = $instance->getNamespacesByLanguageCode(
+ $vars['wgLanguageCode']
+ );
+
+ $namespaceAliases = $instance->getNamespaceAliasesByLanguageCode(
+ $vars['wgLanguageCode']
+ );
+
+ $vars['wgCanonicalNamespaceNames'] += $instance->getCanonicalNames();
+ $vars['wgExtraNamespaces'] += $extraNamespaces + $instance->getCanonicalNames();
+ $vars['wgNamespaceAliases'] = $namespaceAliases + array_flip( $extraNamespaces ) + array_flip( $instance->getCanonicalNames() ) + $vars['wgNamespaceAliases'];
+
+ $instance->addNamespaceSettings( $vars );
+
+ return $instance;
+ }
+
+ private function addNamespaceSettings( &$vars ) {
+
+ /**
+ * Default settings for the SMW specific NS which can only
+ * be defined after SMW_NS_PROPERTY is declared
+ */
+ $smwNamespacesSettings = [
+ SMW_NS_PROPERTY => true,
+ SMW_NS_PROPERTY_TALK => false,
+ SMW_NS_CONCEPT => true,
+ SMW_NS_CONCEPT_TALK => false,
+ SMW_NS_SCHEMA => true,
+ SMW_NS_SCHEMA_TALK => false,
+ ];
+
+ // Combine default values with values specified in other places
+ // (LocalSettings etc.)
+ $vars['smwgNamespacesWithSemanticLinks'] = array_replace(
+ $smwNamespacesSettings,
+ $vars['smwgNamespacesWithSemanticLinks']
+ );
+
+ $vars['wgNamespaceContentModels'][SMW_NS_SCHEMA] = CONTENT_MODEL_SMW_SCHEMA;
+ }
+
+ private function addExtraNamespaceSettings( &$vars ) {
+
+ /**
+ * Indicating which namespaces allow sub-pages
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:$wgNamespacesWithSubpages
+ */
+ $vars['wgNamespacesWithSubpages'] = $vars['wgNamespacesWithSubpages'] + [
+ SMW_NS_PROPERTY_TALK => true,
+ SMW_NS_CONCEPT_TALK => true,
+ ];
+
+ /**
+ * Allow custom namespaces to be acknowledged as containing useful content
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:$wgContentNamespaces
+ */
+ $vars['wgContentNamespaces'] = $vars['wgContentNamespaces'] + [
+ SMW_NS_PROPERTY,
+ SMW_NS_CONCEPT
+ ];
+
+ /**
+ * To indicate which namespaces are enabled for searching by default
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:$wgNamespacesToBeSearchedDefault
+ */
+ $vars['wgNamespacesToBeSearchedDefault'] = $vars['wgNamespacesToBeSearchedDefault'] + [
+ SMW_NS_PROPERTY => true,
+ SMW_NS_CONCEPT => true
+ ];
+ }
+
+ protected function isDefinedConstant( $constant ) {
+ return defined( $constant );
+ }
+
+ protected function getNamespacesByLanguageCode( $languageCode ) {
+ $GLOBALS['smwgContLang'] = $this->lang->fetch( $languageCode );
+ return $GLOBALS['smwgContLang']->getNamespaces();
+ }
+
+ private function getNamespaceAliasesByLanguageCode( $languageCode ) {
+ return $this->lang->fetch( $languageCode )->getNamespaceAliases();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/NamespaceUriFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceUriFinder.php
new file mode 100644
index 00000000..b1635548
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/NamespaceUriFinder.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NamespaceUriFinder {
+
+ /**
+ * @var array
+ */
+ private static $namespaceUriList = [
+ 'owl' => 'http://www.w3.org/2002/07/owl#',
+ 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
+ 'swivt' => 'http://semantic-mediawiki.org/swivt/1.0#',
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
+ 'skos' => 'http://www.w3.org/2004/02/skos/core#',
+ 'foaf' => 'http://xmlns.com/foaf/0.1/',
+ 'dc' => 'http://purl.org/dc/elements/1.1/'
+ ];
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return false|string
+ */
+ public static function getUri( $key ) {
+
+ $key = strtolower( $key );
+
+ if ( isset( self::$namespaceUriList[$key] ) ) {
+ return self::$namespaceUriList[$key];
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Options.php b/www/wiki/extensions/SemanticMediaWiki/src/Options.php
new file mode 100644
index 00000000..763a8070
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Options.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class Options {
+
+ /**
+ * @var array
+ */
+ protected $options = [];
+
+ /**
+ * @since 2.3
+ */
+ public function __construct( array $options = [] ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ */
+ public function delete( $key ) {
+ unset( $this->options[ $key ] );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function has( $key ) {
+ return isset( $this->options[$key] ) || array_key_exists( $key, $this->options );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public function is( $key, $value ) {
+ return $this->get( $key ) === $value;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return $this->options[$key];
+ }
+
+ throw new InvalidArgumentException( "{$key} is an unregistered option" );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function safeGet( $key, $default = false ) {
+ return $this->has( $key ) ? $this->options[$key] : $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function dotGet( $key, $default = false ) {
+ return $this->digDeep( $this->options, $key, $default );
+ }
+
+ private function digDeep( $array, $key, $default ) {
+
+ if ( strpos( $key, '.' ) !== false ) {
+ $list = explode( '.', $key, 2 );
+
+ foreach ( $list as $k => $v ) {
+ if ( isset( $array[$v] ) ) {
+ return $this->digDeep( $array[$v], $list[$k+1], $default );
+ }
+ }
+ }
+
+ if ( isset( $array[$key] ) ) {
+ return $array[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param integer $flag
+ *
+ * @return boolean
+ */
+ public function isFlagSet( $key, $flag ) {
+ return ( ( (int)$this->safeGet( $key, 0 ) & $flag ) == $flag );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function toArray() {
+ return $this->options;
+ }
+
+ /**
+ * @deprecated since 3.0, use Options::toArray
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getOptions() {
+ return $this->toArray();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $keys
+ *
+ * @return array
+ */
+ public function filter( array $keys ) {
+
+ $options = [];
+
+ foreach ( $keys as $key ) {
+ if ( isset( $this->options[$key] ) ) {
+ $options[$key] = $this->options[$key];
+ }
+ }
+
+ return $options;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/ConceptPage.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/ConceptPage.php
new file mode 100644
index 00000000..56ebf905
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/ConceptPage.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace SMW\Page;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\MediaWiki\Collator;
+use SMW\Message;
+use SMWDataItem as DataItem;
+use SMW\Utils\HtmlTabs;
+use SMW\Page\ListBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConceptPage extends Page {
+
+ /**
+ * @var DIProperty
+ */
+ private $property;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var DataValue
+ */
+ private $propertyValue;
+
+ /**
+ * @see Page::initParameters()
+ *
+ * @note We use a smaller limit here; property pages might become large.
+ */
+ protected function initParameters() {
+ $this->limit = $this->getOption( 'pagingLimit' );
+ }
+
+ /**
+ * Returns the HTML which is added to $wgOut after the article text.
+ *
+ * @return string
+ */
+ protected function getHtml() {
+
+ $context = $this->getContext();
+ $context->getOutput()->addModuleStyles( 'ext.smw.page.styles' );
+
+ $request = $context->getRequest();
+ $store = ApplicationFactory::getInstance()->getStore();
+
+ // limit==0: configuration setting to disable this completely
+ if ( $this->limit > 0 ) {
+ $descriptionFactory = ApplicationFactory::getInstance()->getQueryFactory()->newDescriptionFactory();
+
+ $description = $descriptionFactory->newConceptDescription( $this->getDataItem() );
+ $query = \SMWPageLister::getQuery( $description, $this->limit, $this->from, $this->until );
+
+ $query->setLimit( $request->getVal( 'limit', $this->getOption( 'pagingLimit' ) ) );
+ $query->setOffset( $request->getVal( 'offset', '0' ) );
+ $query->setContextPage( $this->getDataItem() );
+ $query->setOption( $query::NO_DEPENDENCY_TRACE, true );
+ $query->setOption( $query::NO_CACHE, true );
+
+ $queryResult = $store->getQueryResult( $query );
+
+ $diWikiPages = $queryResult->getResults();
+
+ if ( $this->until !== '' ) {
+ $diWikiPages = array_reverse( $diWikiPages );
+ }
+
+ $errors = $queryResult->getErrors();
+ } else {
+ $diWikiPages = [];
+ $errors = [];
+ }
+
+ // Make navigation point to the result list.
+ $this->mTitle->setFragment( '#smw-result' );
+
+ $titleText = htmlspecialchars( $this->mTitle->getText() );
+ $resultCount = count( $diWikiPages );
+
+ $limit = $request->getVal( 'limit', $this->getOption( 'pagingLimit' ) );
+ $offset = $request->getVal( 'offset', '0' );
+
+ $query = [
+ 'from' => $request->getVal( 'from', '' ),
+ 'until' => $request->getVal( 'until', '' ),
+ 'value' => $request->getVal( 'value', '' )
+ ];
+
+ $navigationLinks = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-navigation'
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'clearfix'
+ ],
+ ListPager::pagination( $this->mTitle, $limit, $offset, $resultCount, $query + [ '_target' => '#smw-result' ] )
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'style' => 'margin-top:10px;margin-bottom:10px;'
+ ],
+ wfMessage( 'smw_conceptarticlecount', ( $resultCount < $limit ? $resultCount : $limit ) )->parse()
+ )
+ );
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'concept' );
+
+ if ( $this->mTitle->exists() ) {
+
+ $listBuilder = new ListBuilder(
+ $store
+ );
+
+ $html = $navigationLinks . $listBuilder->getColumnList( $diWikiPages );
+ } else {
+ $html = '';
+ }
+
+ $htmlTabs->tab(
+ 'smw-concept-list',
+ $this->msg( 'smw-concept-tab-list' ) . $this->getCachedCount( $store ),
+ [
+ 'hide' => $html === ''
+ ]
+ );
+
+ $htmlTabs->content( 'smw-concept-list', $html );
+
+ // Improperty values
+ $html = smwfEncodeMessages( $errors );
+
+ $htmlTabs->tab( 'smw-concept-errors', $this->msg( 'smw-concept-tab-errors' ), [ 'hide' => $html === '' ] );
+ $htmlTabs->content( 'smw-concept-errors', $html );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-concept clearfix' ]
+ );
+
+ return Html::element(
+ 'div',
+ [
+ 'id' => 'smwfootbr'
+ ]
+ ) . Html::element(
+ 'a',
+ [
+ 'name' => 'smw-result'
+ ],
+ null
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'id' => 'mw-pages'
+ ],
+ $html
+ );
+ }
+
+ private function getCachedCount( $store ) {
+
+ $concept = $store->getConceptCacheStatus(
+ $this->getDataItem()
+ );
+
+ if ( !$concept instanceof DIConcept || $concept->getCacheStatus() !== 'full' ) {
+ return '';
+ }
+
+ $cacheCount = $concept->getCacheCount();
+ $date = $this->getContext()->getLanguage()->timeanddate( $concept->getCacheDate() );
+
+ $countMsg = Message::get( [ 'smw-concept-indicator-cache-update', $date ] );
+ $indicatorClass = ( $cacheCount < 25000 ? ( $cacheCount > 5000 ? ' moderate' : '' ) : ' high' );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'title' => $countMsg,
+ 'class' => 'usage-count' . $indicatorClass
+ ],
+ $cacheCount
+ );
+ }
+
+ private function msg( $params, $type = Message::TEXT, $lang = Message::USER_LANGUAGE ) {
+ return Message::get( $params, $type, $lang );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder.php
new file mode 100644
index 00000000..0aacd65a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW\Page;
+
+use SMW\Store;
+use SMW\Message;
+use SMW\MediaWiki\Collator;
+use SMW\DataValueFactory;
+use SMWInfolink as Infolink;
+use SMWDataItem as DataItem;
+use SMW\Utils\HtmlColumns;
+use Linker;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListBuilder {
+
+ /**
+ * @var Store
+ */
+ public $store;
+
+ /**
+ * @var Collator
+ */
+ public $collator;
+
+ /**
+ * @var callable
+ */
+ public $itemFormatter;
+
+ /**
+ * @var Linker
+ */
+ public $linker = false;
+
+ /**
+ * @var integer
+ */
+ public $sort = SORT_NATURAL;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param Collator|null $collator
+ */
+ public function __construct( Store $store, Collator $collator = null ) {
+ $this->store = $store;
+ $this->collator = $collator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param callable $itemFormatter
+ */
+ public function setItemFormatter( callable $itemFormatter ) {
+ $this->itemFormatter = $itemFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Linker|false $linker
+ */
+ public function setLinker( $linker ) {
+ $this->linker = $linker;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $sort
+ */
+ public function sort( $sort ) {
+ $this->sort = $sort;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage[] $dataItems
+ *
+ * @return array
+ */
+ public function getList( array $dataItems ) {
+ return $this->buildList( $dataItems );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage[] $dataItems
+ *
+ * @return string
+ */
+ public function getColumnList( array $dataItems ) {
+
+ $htmlColumns = new HtmlColumns();
+
+ if ( count( $dataItems ) > 10 ) {
+ $htmlColumns->setColumnClass( 'smw-column-responsive' );
+ }
+
+ $htmlColumns->setContinueAbbrev(
+ Message::get( 'listingcontinuesabbrev', Message::PARSE, Message::USER_LANGUAGE )
+ );
+
+ $htmlColumns->setColumns( 1 );
+
+ $htmlColumns->setContents(
+ $this->buildList( $dataItems ),
+ HtmlColumns::INDEXED_LIST
+ );
+
+ return $htmlColumns->getHtml();
+ }
+
+ private function buildList( $dataItems ) {
+
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ if ( $this->linker === false ) {
+ $this->linker = smwfGetLinker();
+ }
+
+ if ( $this->collator === null ) {
+ $this->collator = Collator::singleton();
+ }
+
+ $contents = [];
+
+ foreach ( $dataItems as $dataItem ) {
+
+ $dataValue = $dataValueFactory->newDataValueByItem( $dataItem, null );
+ $startChar = $this->getFirstLetter( $dataItem );
+
+ if ( $startChar === '' ) {
+ $startChar = '...';
+ }
+
+ if ( !isset( $contents[$startChar] ) ) {
+ $contents[$startChar] = [];
+ }
+
+ if ( is_callable( $this->itemFormatter ) ) {
+ // Use of ( ... )( ) only possible with PHP7
+ // $contents[$startChar][] = ( $this->itemFormatter )( $dataValue, $this->linker );
+ $contents[$startChar][] = call_user_func_array( $this->itemFormatter, [ $dataValue, $this->linker ] );
+ } else {
+ $searchlink = Infolink::newBrowsingLink( '+', $dataValue->getWikiValue() );
+ $contents[$startChar][] = $dataValue->getLongHTMLText( $this->linker ) . '&#160;' . $searchlink->getHTML( $this->linker );
+ }
+ }
+
+ ksort( $contents, $this->sort );
+
+ return $contents;
+ }
+
+ private function getFirstLetter( DataItem $dataItem ) {
+
+ $sortKey = $dataItem->getSortKey();
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $sortKey = $this->store->getWikiPageSortKey( $dataItem );
+ }
+
+ return $this->collator->getFirstLetter( $sortKey );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ListBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ListBuilder.php
new file mode 100644
index 00000000..98185608
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ListBuilder.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW\Page\ListBuilder;
+
+use Html;
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWPageLister as PageLister;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListBuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var string
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @var integer
+ */
+ private $listLimit = 0;
+
+ /**
+ * @var string
+ */
+ private $listHeader = '';
+
+ /**
+ * @var boolean
+ */
+ private $isUserDefined = false;
+
+ /**
+ * @var boolean
+ */
+ private $checkProperty = true;
+
+ /**
+ * @var integer
+ */
+ private $itemCount = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $languageCode
+ */
+ public function setLanguageCode( $languageCode ) {
+ $this->languageCode = $languageCode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isUserDefined
+ */
+ public function isUserDefined( $isUserDefined ) {
+ $this->isUserDefined = $isUserDefined;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $listLimit
+ */
+ public function setListLimit( $listLimit ) {
+ $this->listLimit = $listLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $listHeader
+ */
+ public function setListHeader( $listHeader ) {
+ $this->listHeader = $listHeader;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $checkProperty
+ */
+ public function checkProperty( $checkProperty ) {
+ $this->checkProperty = $checkProperty;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getItemCount() {
+ return $this->itemCount;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param DataItem $dataItem
+ * @param RequestOptions $requestOptions
+ *
+ * @return string
+ */
+ public function createHtml( DIProperty $property, DataItem $dataItem, RequestOptions $requestOptions ) {
+
+ $subjectList = $this->store->getPropertySubjects(
+ $property,
+ $dataItem,
+ $requestOptions
+ );
+
+ // May return an iterator
+ if ( $subjectList instanceof \Iterator ) {
+ $subjectList = iterator_to_array( $subjectList );
+ }
+
+ $more = false;
+
+ // Pop the +1 look ahead from the list
+ if ( is_array( $subjectList ) && count( $subjectList ) > $this->listLimit ) {
+ array_pop( $subjectList );
+ $more = true;
+ }
+
+ $result = '';
+ $this->itemCount = is_array( $subjectList ) ? count( $subjectList ) : 0;
+
+ $callback = null;
+ $message = wfMessage( 'smw-propertylist-count', $this->itemCount )->text();
+
+ if ( $more ) {
+ $callback = function() use ( $property, $dataItem ) {
+ return \Html::element(
+ 'a',
+ [
+ 'href' => \SpecialPage::getSafeTitleFor( 'SearchByProperty' )->getLocalURL( [
+ 'property' => $property->getLabel(),
+ 'value' => $dataItem->getDBKey()
+ ] )
+ ],
+ wfMessage( 'smw_browse_more' )->text()
+ );
+ };
+
+ $message = wfMessage( 'smw-propertylist-count-more-available', $this->itemCount )->text();
+ }
+
+ if ( $this->itemCount > 0 ) {
+ $titleText = htmlspecialchars( str_replace( '_', ' ', $dataItem->getDBKey() ) );
+ $result .= "<div id=\"{$this->listHeader}\">" . "\n<p>";
+
+ $result .= $message . "</p>";
+ $property = $this->checkProperty ? $property : null;
+
+ if ( $this->itemCount < 6 ) {
+ $result .= PageLister::getShortList( 0, $this->itemCount, $subjectList, $property, $callback );
+ } else {
+ $result .= PageLister::getColumnList( 0, $this->itemCount, $subjectList, $property, $callback );
+ }
+
+ $result .= "\n</div>";
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ValueListBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ValueListBuilder.php
new file mode 100644
index 00000000..1abe84a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListBuilder/ValueListBuilder.php
@@ -0,0 +1,392 @@
+<?php
+
+namespace SMW\Page\ListBuilder;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\MediaWiki\Collator;
+use SMW\Message;
+use SMW\Page\ListPager;
+use SMW\Query\Language\SomeProperty;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMW\Utils\HtmlDivTable;
+use SMW\Utils\NextPager;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWInfolink as Infolink;
+use SMWPageLister as PageLister;
+use WebRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ValueListBuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var integer
+ */
+ private $pagingLimit = 0;
+
+ /**
+ * @var integer
+ */
+ private $maxPropertyValues = 3;
+
+ /**
+ * @var string
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $pagingLimit
+ */
+ public function setPagingLimit( $pagingLimit ) {
+ $this->pagingLimit = $pagingLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $languageCode
+ */
+ public function setLanguageCode( $languageCode ) {
+ $this->languageCode = $languageCode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $maxPropertyValues
+ */
+ public function setMaxPropertyValues( $maxPropertyValues ) {
+ $this->maxPropertyValues = $maxPropertyValues;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param DataItem $dataItem
+ *
+ * @return string
+ */
+ public function createHtml( DIProperty $property, DataItem $dataItem, array $query = [] ) {
+
+ $limit = isset( $query['limit'] ) ? (int)$query['limit'] : 0;
+ $offset = isset( $query['offset'] ) ? (int)$query['offset'] : 0;
+ $from = isset( $query['from'] ) ? $query['from'] : 0;
+ $until = isset( $query['until'] ) ? $query['until'] : 0;
+ $filter = isset( $query['filter'] ) ? $query['filter'] : '';
+
+ // limit==0: configuration setting to disable this completely
+ if ( $limit < 1 ) {
+ return '';
+ }
+
+ $dataItems = [];
+ $isValueSearch = false;
+
+ $options = PageLister::getRequestOptions( $limit, $from, $until );
+ $options->setOffset( $offset );
+
+ if ( $filter !== '' ) {
+ $dataItems = $this->filterByValue( $property, $filter, $options );
+ $isValueSearch = true;
+ } else {
+ $dataItems = $this->store->getAllPropertySubjects( $property, $options );
+ }
+
+ if ( $dataItems instanceof \Traversable ) {
+ $dataItems = iterator_to_array( $dataItems );
+ }
+
+ if ( !$options->ascending ) {
+ $dataItems = array_reverse( $dataItems );
+ }
+
+ $result = '';
+
+ if ( count( $dataItems ) < 1 && !$isValueSearch ) {
+ return $result;
+ }
+
+ $title = $dataItem->getTitle();
+ $title->setFragment( '#SMWResults' );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $property
+ );
+
+ // Allow the DV formatter to access a specific language code
+ $dataValue->setOption(
+ DataValue::OPT_USER_LANGUAGE,
+ $this->languageCode
+ );
+
+ $titleText = htmlspecialchars( $dataValue->getWikiValue() );
+ $resultCount = count( $dataItems );
+
+ $topic = $isValueSearch ? 'smw-property-page-list-search-count' : 'smw-property-page-list-count';
+
+ $navNote = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-nav-note'
+ ],
+ Message::get(
+ [ $topic, ( $resultCount < $limit ? $resultCount : $limit ), $filter ],
+ Message::PARSE,
+ $this->languageCode
+ ) . Html::rawElement(
+ 'div',
+ [],
+ ''
+ )
+ );
+
+ $objectList = $this->createValueList(
+ $property,
+ $dataItem,
+ $dataItems,
+ $limit,
+ $until
+ );
+
+ $navContainer = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-nav-container'
+ ],
+ Html::rawElement(
+ 'div' ,
+ [
+ 'class' => 'smw-page-nav-left'
+ ],
+ ListPager::pagination( $title, $limit, $offset, $resultCount, $query )
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-nav-right'
+ ],
+ ListPager::filter( $title, $limit, $offset, $filter )
+ )
+ );
+
+ $result .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-page-navigation'
+ ],
+ $navContainer . $navNote
+ ) . $objectList;
+
+ return Html::rawElement(
+ 'a',
+ [ 'name' => 'SMWResults' ],
+ ''
+ ) . Html::rawElement(
+ 'div',
+ [ 'id' => 'mw-pages' ],
+ $result
+ );
+ }
+
+ private function createValueList( DIProperty $property, DataItem $dataItem, $diWikiPages, $limit, $until ) {
+
+ if ( $diWikiPages instanceof \Iterator ) {
+ $diWikiPages = iterator_to_array( $diWikiPages );
+ }
+
+ $ac = count( $diWikiPages );
+ //$contentLanguage = Localizer::getInstance()->getContentLanguage();
+ $title = $dataItem->getTitle();
+
+ if ( $ac > $limit ) {
+ if ( $until !== '' ) {
+ $start = 1;
+ } else {
+ $start = 0;
+ $ac = $ac - 1;
+ }
+ } else {
+ $start = 0;
+ }
+
+ $html = '';
+ $prev_start_char = 'None';
+
+ for ( $index = $start; $index < $ac; $index++ ) {
+ $diWikiPage = $diWikiPages[$index];
+ $dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null );
+
+ $sortKey = $this->store->getWikiPageSortKey( $diWikiPage );
+ $start_char = Collator::singleton()->getFirstLetter( $sortKey );
+
+ // Header for index letters
+ if ( $start_char != $prev_start_char ) {
+ $html .= HtmlDivTable::row(
+ HtmlDivTable::cell(
+ '<div id="' . htmlspecialchars( $start_char ) . '">' . htmlspecialchars( $start_char ) . "</div>",
+ [
+ 'class' => "header-title"
+ ]
+ ) . HtmlDivTable::cell(
+ '<div></div>',
+ [
+ 'class' => "header-title"
+ ]
+ ),
+ [
+ 'class' => "header-row"
+ ]
+ );
+ $prev_start_char = $start_char;
+ }
+
+ // Property values
+ $ropts = new RequestOptions();
+ $ropts->limit = $this->maxPropertyValues + 1;
+
+ // Restrict the request otherwise the entire SemanticData record
+ // is fetched which can in case of a subject with a large
+ // subobject/subpage pool create excessive DB queries that are not
+ // used for the display
+ $ropts->conditionConstraint = true;
+
+ $values = $this->store->getPropertyValues( $diWikiPage, $property, $ropts );
+
+ // May return an iterator
+ if ( $values instanceof \Iterator ) {
+ $values = iterator_to_array( $values );
+ }
+
+ $hasLocalTimeOffsetPreference = Localizer::getInstance()->hasLocalTimeOffsetPreference();
+
+ $i = 0;
+ $pvCells = '';
+
+ foreach ( $values as $di ) {
+ if ( $i != 0 ) {
+ $pvCells .= ', ';
+ }
+ $i++;
+
+ if ( $i < $this->maxPropertyValues + 1 ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $di, $property );
+ $outputFormat = $dataValue->getOutputFormat();
+
+ if ( $outputFormat === false ) {
+ $outputFormat = 'LOCL' . ( $hasLocalTimeOffsetPreference ? '#TO' : '' );
+ }
+
+ $dataValue->setOutputFormat( $outputFormat );
+
+ $pvCells .= $dataValue->getShortHTMLText( smwfGetLinker() ) . $dataValue->getInfolinkText( SMW_OUTPUT_HTML, smwfGetLinker() );
+ } else {
+ $searchlink = Infolink::newInversePropertySearchLink( '…', $dvWikiPage->getWikiValue(), $title->getText() );
+ $pvCells .= $searchlink->getHTML( smwfGetLinker() );
+ }
+ }
+
+ // Property name
+ $searchlink = Infolink::newBrowsingLink( '+', $dvWikiPage->getWikiValue() );
+ $html .= HtmlDivTable::row(
+ HtmlDivTable::cell(
+ $dvWikiPage->getShortHTMLText( smwfGetLinker() ) . '&#160;' . $searchlink->getHTML( smwfGetLinker() ),
+ [
+ 'class' => "smwpropname",
+ 'data-list-index' => $index
+ ]
+ ) . HtmlDivTable::cell(
+ $pvCells,
+ [
+ 'class' => "smwprops"
+ ]
+ ),
+ [
+ 'class' => "value-row"
+ ]
+ );
+ }
+
+ return HtmlDivTable::table(
+ $html,
+ [
+ 'class' => "smw-property-page-results",
+ 'style' => "width: 100%;"
+ ]
+ );
+ }
+
+ private function filterByValue( $property, $value, $options ) {
+
+ $queryFactory = ApplicationFactory::getInstance()->getQueryFactory();
+ $queryParser = $queryFactory->newQueryParser();
+
+ $description = $queryParser->getQueryDescription(
+ $queryParser->createCondition( $property, $value )
+ );
+
+ if ( $queryParser->getErrors() !== [] ) {
+ return [];
+ }
+
+ // Make sure that no subproperty is included while executing the
+ // query
+ if ( $description instanceof SomeProperty ) {
+ $description->setHierarchyDepth( 0 );
+ }
+
+ $query = $queryFactory->newQuery( $description );
+ $query->setLimit( $options->limit );
+ $query->setOffset( $options->offset );
+
+ // We are not sorting via the backend as an ORDER BY will cause a
+ // SQL filesort and means for a large pool of value assignments a
+ // slow query
+ $res = $this->store->getQueryResult( $query );
+ $results = $res->getResults();
+
+ $sort = [];
+ $collator = Collator::singleton();
+
+ foreach ( $results as $result ) {
+
+ $firstLetter = $collator->getFirstLetter(
+ $this->store->getWikiPageSortKey( $result )
+ );
+
+ $sort[$firstLetter . '#' . $result->getHash()] = $result;
+ }
+
+ // Sort on the spot via PHP, which should be enough for the search
+ // and match functionality
+ ksort( $sort );
+
+ return array_values( $sort );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/ListPager.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListPager.php
new file mode 100644
index 00000000..b900a8db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/ListPager.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\Page;
+
+use Html;
+use SMW\Localizer;
+use SMW\Message;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListPager {
+
+ /**
+ * @var string
+ */
+ public static $language = '';
+
+ /**
+ * @since 2.4
+ */
+ public static function pagination( Title $title, $limit, $offset = 0, $count = 0, array $query = [], $prefix = '' ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-ui-pagination'
+ ],
+ self::getPagingLinks( $title, $limit, $offset, $count, $query, $prefix )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return string
+ */
+ public static function filter( Title $title, $limit = 0, $offset = 0, $filter = '' ) {
+
+ $form = \Xml::tags(
+ 'form',
+ [
+ 'id' => 'search',
+ 'name' => 'foo',
+ 'action' => $GLOBALS['wgScript']
+ ],
+ Html::hidden(
+ 'title',
+ strtok( $title->getPrefixedText(), '/' )
+ ) . Html::hidden(
+ 'limit',
+ $limit
+ ) . Html::hidden(
+ 'offset',
+ $offset
+ )
+ );
+
+ $label = Message::get( 'smw-filter', Message::TEXT, Message::USER_LANGUAGE );
+
+ $form .= Html::rawElement(
+ 'label',
+ [],
+ $label .
+ Html::rawElement(
+ 'input',
+ [
+ 'type' => 'search',
+ 'name' => 'filter',
+ 'value' => $filter,
+ 'form' => 'search',
+ 'autocomplete' => 'off',
+ 'placeholder' => '...'
+ ]
+ )
+ );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-ui-input-filter'
+ ],
+ $form
+ );
+ }
+
+ /**
+ * Generate (prev x| next x) (20|50|100...) type links for paging
+ *
+ * @param Title $title Title object to link
+ * @param int $offset
+ * @param int $limit
+ * @param integer $count
+ * @param array $query Optional URL query parameter string
+ * @return string
+ */
+ public static function getPagingLinks( Title $title, $limit, $offset, $count = 0, array $query = [], $prefix = '' ) {
+
+ $list = [];
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+ $count = (int)$count;
+
+ $atend = $count < $limit;
+ $disabled = $count > 0 ? '' : ' disabled';
+
+ if ( self::$language === '' ) {
+ $language = Localizer::getInstance()->getUserLanguage();
+ } else {
+ $language = Localizer::getInstance()->getLanguage( self::$language );
+ }
+
+ if ( $prefix !== '' ) {
+ $prefix = Html::rawElement( 'a', [ 'class' => 'page-link link-disabled' ], $prefix );
+ }
+
+ # Make 'previous' link
+ $prev = wfMessage( 'prevn' )->inLanguage( $language )->title( $title )->numParams( $limit )->text();
+
+ if ( $offset > 0 ) {
+ $plink = self::numLink( $title, max( $offset - $limit, 0 ), $limit, $query, $prev, 'prevn-title', 'mw-prevlink', $disabled, $language );
+ } else {
+ $plink = Html::element( 'a', [ 'class' => 'page-link link-disabled' ], htmlspecialchars( $prev ) );
+ }
+
+ # Make 'next' link
+ $next = wfMessage( 'nextn' )->inLanguage( $language )->title( $title )->numParams( $limit )->text();
+
+ if ( $atend ) {
+ $nlink = Html::element( 'a', [ 'class' => 'page-link link-disabled' ], htmlspecialchars( $next ) );
+ } else {
+ $nlink = self::numLink( $title, $offset + $limit, $limit, $query, $next, 'nextn-title', 'mw-nextlink', $disabled, $language );
+ }
+
+ # Make links to set number of items per page
+
+ foreach ( [ 20, 50, 100, 250, 500 ] as $num ) {
+ $list[] = self::numLink(
+ $title,
+ $offset,
+ $num,
+ $query,
+ $language->formatNum( $num ),
+ 'shown-title',
+ 'mw-numlink',
+ $disabled,
+ $language,
+ $num === $limit
+ );
+ }
+
+ return $prefix . $plink . implode( '', $list ) . $nlink;
+ }
+
+ /**
+ * Helper function for viewPrevNext() that generates links
+ *
+ * @param Title $title Title object to link
+ * @param int $offset
+ * @param int $limit
+ * @param array $query Extra query parameters
+ * @param string $link Text to use for the link; will be escaped
+ * @param string $tooltipMsg Name of the message to use as tooltip
+ * @param string $class Value of the "class" attribute of the link
+ * @return string HTML fragment
+ */
+ private static function numLink( Title $title, $offset, $limit, array $query, $link, $tooltipMsg, $class, $disabled, $language, $active = false ) {
+ $query = [ 'limit' => $limit, 'offset' => $offset ] + $query;
+
+ $tooltip = wfMessage( $tooltipMsg )->inLanguage( $language )->title( $title )->numParams( $limit )->text();
+ $target = '';
+
+ if ( isset( $query['_target' ] ) ) {
+ $target = $query['_target' ];
+ unset( $query['_target' ] );
+ }
+
+ return Html::element( 'a',
+ [
+ 'href' => $title->getLocalURL( $query ) . $target,
+ 'title' => $tooltip,
+ 'class' => 'page-link' . ( $active ? ' link-active' : '' )
+ ],
+ $link
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/Page.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/Page.php
new file mode 100644
index 00000000..5e682193
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/Page.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\Page;
+
+use Article;
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMWOutputs as Outputs;
+
+/**
+ * Abstract subclass of MediaWiki's Article that handles the common tasks of
+ * article pages for Concept and Property pages. This is mainly parameter
+ * handling and some very basic output control.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Nikolas Iwan
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+abstract class Page extends Article {
+
+ /**
+ * Limit for results per page.
+ *
+ * @var integer
+ */
+ protected $limit;
+
+ /**
+ * Start string: print $limit results from here.
+ *
+ * @var string
+ */
+ protected $from;
+
+ /**
+ * End string: print $limit results strictly before this article.
+ *
+ * @var string
+ */
+ protected $until;
+
+ /**
+ * Cache for the current skin, obtained from $wgUser.
+ *
+ * @var Skin
+ */
+ protected $skin;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * Overwrite Article::view to add additional HTML to the output.
+ *
+ * @see Article::view
+ */
+ public function view() {
+
+ $outputPage = $this->getContext()->getOutput();
+ $outputPage->addModuleStyles( 'ext.smw.page.styles' );
+
+ if ( !$this->getOption( 'smwgSemanticsEnabled' ) ) {
+ $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
+ $outputPage->addHTML( wfMessage( 'smw-semantics-not-enabled' )->text() );
+ return;
+ }
+
+ if ( ( $redirectTargetURL = $this->getRedirectTargetURL() ) !== false ) {
+ $outputPage->redirect( $redirectTargetURL );
+ }
+
+ $this->initParameters();
+
+ // Copied from CategoryPage
+ $user = $this->getContext()->getUser();
+ $request = $this->getContext()->getRequest();
+
+ $diff = $request->getVal( 'diff' );
+ $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
+
+ if ( !isset( $diff ) || !$diffOnly ) {
+ // MW 1.25+
+ if ( method_exists( $outputPage, 'setIndicators' ) && ( $indicators = $this->getTopIndicators() ) !== '' ) {
+ $outputPage->setIndicators( $indicators );
+ }
+
+ $outputPage->addHTML( $this->initHtml() );
+ $outputPage->addHTML( $this->beforeView() );
+ }
+
+ if ( $this->isLockedView() === false ) {
+ parent::view();
+ }
+
+ if ( !isset( $diff ) || !$diffOnly ) {
+ $this->showList();
+ }
+
+ $outputPage->addHTML( $this->afterHtml() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->safeGet( $key, false );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|boolean
+ */
+ protected function getRedirectTargetURL() {
+ return false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ protected function getTopIndicators() {
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ protected function initHtml() {
+ return $this->getIntroductoryText();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ protected function getIntroductoryText() {
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ protected function isLockedView() {
+ return false;
+ }
+
+ /**
+ * Main method for adding all additional HTML to the output stream.
+ */
+ protected function showList() {
+
+ $outputPage = $this->getContext()->getOutput();
+ $request = $this->getContext()->getRequest();
+
+ $this->from = $request->getVal( 'from', '' );
+ $this->until = $request->getVal( 'until', '' );
+
+ $outputPage->addHTML( $this->getHtml() );
+
+ Outputs::commitToOutputPage( $outputPage );
+ }
+
+ /**
+ * Initialise some parameters that might be changed by subclasses
+ * (e.g. $limit). Method can be overwritten in this case.
+ * If the method returns false, nothing will be printed besides
+ * the original article.
+ *
+ * @return true
+ */
+ protected function initParameters() {
+ $this->limit = 20;
+ }
+
+ /**
+ * Returns HTML to be displayed before the article text.
+ *
+ * @return string
+ */
+ protected function beforeView() {
+ return '';
+ }
+
+ /**
+ * Returns HTML to be displayed after the list display.
+ *
+ * @return string
+ */
+ protected function afterHtml() {
+ return '';
+ }
+
+ /**
+ * Returns the HTML which is added to $wgOut after the article text.
+ *
+ * @return string
+ */
+ protected abstract function getHtml();
+
+ /**
+ * Like Article's getTitle(), but returning a suitable SMWDIWikiPage.
+ *
+ * @since 1.6
+ *
+ * @return SMWDIWikiPage
+ */
+ protected function getDataItem() {
+ return DIWikiPage::newFromTitle( $this->getTitle() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/PageFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/PageFactory.php
new file mode 100644
index 00000000..00d6caa4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/PageFactory.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Page;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\PropertySpecificationReqExaminer;
+use SMW\PropertySpecificationReqMsgBuilder;
+use SMW\Store;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PageFactory {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ *
+ * @return PageView
+ * @throws RuntimeException
+ */
+ public function newPageFromTitle( Title $title ) {
+
+ if ( $title->getNamespace() === SMW_NS_PROPERTY ) {
+ return $this->newPropertyPage( $title );
+ } elseif ( $title->getNamespace() === SMW_NS_CONCEPT ) {
+ return $this->newConceptPage( $title );
+ }
+
+ throw new RuntimeException( 'No supported ContentPage instance for namespace ' . $title->getNamespace() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ *
+ * @return PropertyPage
+ */
+ public function newPropertyPage( Title $title ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $propertySpecificationReqExaminer = new PropertySpecificationReqExaminer(
+ $this->store,
+ $applicationFactory->singleton( 'ProtectionValidator' )
+ );
+
+ $propertySpecificationReqExaminer->setChangePropagationProtection(
+ $settings->get( 'smwgChangePropagationProtection' )
+ );
+
+ $propertySpecificationReqMsgBuilder = new PropertySpecificationReqMsgBuilder(
+ $this->store,
+ $propertySpecificationReqExaminer
+ );
+
+ $propertySpecificationReqMsgBuilder->setPropertyReservedNameList(
+ $settings->get( 'smwgPropertyReservedNameList' )
+ );
+
+ $propertyPage = new PropertyPage(
+ $title,
+ $this->store,
+ $propertySpecificationReqMsgBuilder
+ );
+
+ $propertyPage->setOption(
+ 'smwgSemanticsEnabled',
+ $settings->get( 'smwgSemanticsEnabled' )
+ );
+
+ $propertyPage->setOption(
+ 'pagingLimit',
+ $settings->dotGet( 'smwgPagingLimit.property' )
+ );
+
+ $propertyPage->setOption(
+ 'smwgPropertyListLimit',
+ $settings->get( 'smwgPropertyListLimit' )
+ );
+
+ $propertyPage->setOption(
+ 'smwgMaxPropertyValues',
+ $settings->get( 'smwgMaxPropertyValues' )
+ );
+
+ return $propertyPage;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ *
+ * @return ConceptPage
+ */
+ public function newConceptPage( Title $title ) {
+
+ $conceptPage = new ConceptPage( $title );
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $conceptPage->setOption(
+ 'smwgSemanticsEnabled',
+ $settings->get( 'smwgSemanticsEnabled' )
+ );
+
+ $conceptPage->setOption(
+ 'pagingLimit',
+ $settings->dotGet( 'smwgPagingLimit.concept' )
+ );
+
+ return $conceptPage;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Page/PropertyPage.php b/www/wiki/extensions/SemanticMediaWiki/src/Page/PropertyPage.php
new file mode 100644
index 00000000..c8580337
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Page/PropertyPage.php
@@ -0,0 +1,380 @@
+<?php
+
+namespace SMW\Page;
+
+use Html;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueFormatters\DataValueFormatter;
+use SMW\DIProperty;
+use SMW\Message;
+use SMW\Page\ListBuilder\ListBuilder as SimpleListBuilder;
+use SMW\Page\ListBuilder\ValueListBuilder;
+use SMW\PropertyRegistry;
+use SMW\PropertySpecificationReqMsgBuilder;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMW\StringCondition;
+use Title;
+use SMW\Utils\HtmlTabs;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyPage extends Page {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertySpecificationReqMsgBuilder
+ */
+ private $propertySpecificationReqMsgBuilder;
+
+ /**
+ * @var DIProperty
+ */
+ private $property;
+
+ /**
+ * @var DataValue
+ */
+ private $propertyValue;
+
+ /**
+ * @var ListBuilder
+ */
+ private $listBuilder;
+
+ /**
+ * @see 3.0
+ *
+ * @param Title $title
+ * @param Store $store
+ * @param PropertySpecificationReqMsgBuilder $propertySpecificationReqMsgBuilder
+ */
+ public function __construct( Title $title, Store $store, PropertySpecificationReqMsgBuilder $propertySpecificationReqMsgBuilder ) {
+ parent::__construct( $title );
+ $this->store = $store;
+ $this->propertySpecificationReqMsgBuilder = $propertySpecificationReqMsgBuilder;
+ }
+
+ /**
+ * @see Page::initParameters()
+ */
+ protected function initParameters() {
+ // We use a smaller limit here; property pages might become large
+ $this->limit = $this->getOption( 'pagingLimit' );
+ $this->property = DIProperty::newFromUserLabel( $this->getTitle()->getText() );
+ $this->propertyValue = DataValueFactory::getInstance()->newDataValueByItem( $this->property );
+ }
+
+ /**
+ * @see Page::getIntroductoryText
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ protected function getIntroductoryText() {
+
+ $redirectTarget = $this->store->getRedirectTarget( $this->property );
+
+ if ( !$redirectTarget->equals( $this->property ) ) {
+ return '';
+ }
+
+ $this->propertySpecificationReqMsgBuilder->setSemanticData(
+ $this->fetchSemanticDataFromEditInfo()
+ );
+
+ $this->propertySpecificationReqMsgBuilder->check(
+ $this->property
+ );
+
+ return $this->propertySpecificationReqMsgBuilder->getMessage();
+ }
+
+ /**
+ * @see Page::isLockedView
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ protected function isLockedView() {
+ return $this->propertySpecificationReqMsgBuilder->reqLock();
+ }
+
+ /**
+ * @see Page::getRedirectTargetURL
+ *
+ * @since 3.0
+ *
+ * @return string|boolean
+ */
+ protected function getRedirectTargetURL() {
+
+ $label = $this->getTitle()->getText();
+
+ $property = new DIProperty(
+ PropertyRegistry::getInstance()->findPropertyIdByLabel( $label )
+ );
+
+ // Ensure to redirect to `Property:Modification date` and not using
+ // a possible user contextualized version such as `Property:Date de modification`
+ $canonicalLabel = $property->getCanonicalLabel();
+
+ if ( $canonicalLabel !== '' && $label !== $canonicalLabel ) {
+ return $property->getCanonicalDiWikiPage()->getTitle()->getFullURL();
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the HTML which is added to $wgOut after the article text.
+ *
+ * @return string
+ */
+ protected function getHtml() {
+
+ if ( !$this->store->getRedirectTarget( $this->property )->equals( $this->property ) ) {
+ return '';
+ }
+
+ $context = $this->getContext();
+ $languageCode = $context->getLanguage()->getCode();
+
+ $html = '';
+ $matches = [];
+
+ $context->getOutput()->addModuleStyles( 'ext.smw.page.styles' );
+ $context->getOutput()->addModules( 'smw.property.page' );
+
+ $context->getOutput()->setPageTitle(
+ $this->propertyValue->getFormattedLabel( DataValueFormatter::WIKI_LONG )
+ );
+
+ $this->listBuilder = new SimpleListBuilder(
+ $this->store
+ );
+
+ $this->listBuilder->setLanguageCode(
+ $languageCode
+ );
+
+ $this->listBuilder->isUserDefined(
+ $this->property->isUserDefined()
+ );
+
+ if ( $this->mParserOutput instanceof \ParserOutput ) {
+ preg_match_all(
+ "/" . "<section class=\"smw-property-specification\"(.*)?>([\s\S]*?)<\/section>" . "/m",
+ $this->mParserOutput->getText(),
+ $matches
+ );
+ }
+
+ $isFirst = true;
+
+ $htmlTabs = new HtmlTabs();
+ $htmlTabs->setGroup( 'property' );
+
+ $html = $this->makeValueList( $languageCode );
+ $isFirst = $html === '';
+
+ $htmlTabs->tab( 'smw-property-value', $this->msg( 'smw-property-tab-usage' ) . $this->getUsageCount(), [ 'hide' => $html === '' ] );
+ $htmlTabs->content( 'smw-property-value', $html );
+
+ // Redirects
+ list( $html, $itemCount ) = $this->makeList( 'redirect', '_REDI', true );
+ $isFirst = $isFirst && $html === '';
+
+ $htmlTabs->tab( 'smw-property-redi', $this->msg( 'smw-property-tab-redirects' ) . $itemCount, [ 'hide' => $html === '' ] );
+ $htmlTabs->content( 'smw-property-redi', $html );
+
+ // Subproperties
+ list( $html, $itemCount ) = $this->makeList( 'subproperty', '_SUBP', true );
+ $isFirst = $isFirst && $html === '';
+
+ $htmlTabs->tab( 'smw-property-subp', $this->msg( 'smw-property-tab-subproperties' ) . $itemCount, [ 'hide' => $html === '' ] );
+ $htmlTabs->content( 'smw-property-subp', $html );
+
+ // Improperty values
+ list( $html, $itemCount ) = $this->makeList( 'error', '_ERRP', false );
+ $isFirst = $isFirst && $html === '';
+
+ $htmlTabs->tab( 'smw-property-errp', $this->msg( 'smw-property-tab-errors' ) . $itemCount, [ 'hide' => $html === '', 'class' => 'smw-tab-warning' ] );
+ $htmlTabs->content( 'smw-property-errp', $html );
+
+ if ( isset( $matches[2] ) && $matches[2] !== [] ) {
+ $html = "<div>" . implode('</div><div>', $matches[2] ) . "</div>";
+ } else {
+ $html = '';
+ }
+
+ $htmlTabs->tab(
+ 'smw-property-spec',
+ $this->msg( 'smw-property-tab-specification' ),
+ [
+ 'hide' => $html === '',
+ 'class' => $isFirst ? 'smw-tab-spec' : 'smw-tab-spec smw-tab-right'
+ ]
+ );
+
+ $htmlTabs->content( 'smw-property-spec', $html );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-property clearfix' ]
+ );
+
+ return $html;
+ }
+
+ private function fetchSemanticDataFromEditInfo() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ if ( $this->getPage()->getRevision() === null ) {
+ return null;
+ }
+
+ $editInfoProvider = $applicationFactory->newMwCollaboratorFactory()->newEditInfoProvider(
+ $this->getPage(),
+ $this->getPage()->getRevision()
+ );
+
+ return $editInfoProvider->fetchSemanticData();
+ }
+
+ private function makeList( $key, $propertyKey, $checkProperty = true ) {
+
+ // Ignore the list when a filter is present
+ if ( $this->getContext()->getRequest()->getVal( 'filter', '' ) !== '' ) {
+ return [ '', '' ];
+ }
+
+ $propertyListLimit = $this->getOption( 'smwgPropertyListLimit' );
+ $listLimit = $propertyListLimit[$key];
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->ascending = true;
+
+ // +1 look ahead
+ $requestOptions->setLimit(
+ $listLimit + 1
+ );
+
+ $this->listBuilder->setListLimit(
+ $listLimit
+ );
+
+ $this->listBuilder->setListHeader(
+ 'smw-propertylist-' . $key
+ );
+
+ $this->listBuilder->checkProperty(
+ $checkProperty
+ );
+
+ $html = $this->listBuilder->createHtml(
+ new DIProperty( $propertyKey ),
+ $this->getDataItem(),
+ $requestOptions
+ );
+
+ $itemCount = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'item-count'
+ ],
+ $this->listBuilder->getItemCount()
+ );
+
+ return [ $html, $itemCount ];
+ }
+
+ private function makeValueList( $languageCode ) {
+
+ $request = $this->getContext()->getRequest();
+
+ $valueListBuilder = new ValueListBuilder(
+ $this->store
+ );
+
+ $valueListBuilder->setLanguageCode(
+ $languageCode
+ );
+
+ $valueListBuilder->setPagingLimit(
+ $this->getOption( 'pagingLimit' )
+ );
+
+ $valueListBuilder->setMaxPropertyValues(
+ $this->getOption( 'smwgMaxPropertyValues' )
+ );
+
+ return $valueListBuilder->createHtml(
+ $this->property,
+ $this->getDataItem(),
+ [
+ 'limit' => $request->getVal( 'limit', $this->getOption( 'pagingLimit' ) ),
+ 'offset' => $request->getVal( 'offset', '0' ),
+ 'from' => $request->getVal( 'from', '' ),
+ 'until' => $request->getVal( 'until', '' ),
+ 'filter' => $request->getVal( 'filter', '' )
+ ]
+ );
+ }
+
+ private function msg( $params, $type = Message::TEXT, $lang = Message::USER_LANGUAGE ) {
+ return Message::get( $params, $type, $lang );
+ }
+
+ private function getUsageCount() {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( 1 );
+
+ // Label that corresponds to the display and sort characteristics
+ if ( $this->property->isUserDefined() ) {
+ $searchLabel = $this->propertyValue->getSearchLabel();
+ } else {
+ $searchLabel = $this->property->getKey();
+ $requestOptions->setOption( RequestOptions::SEARCH_FIELD, 'smw_title' );
+ }
+
+ $requestOptions->addStringCondition( $searchLabel, StringCondition::COND_EQ );
+
+ $cachedLookupList = $this->store->getPropertiesSpecial( $requestOptions );
+ $usageList = $cachedLookupList->fetchList();
+
+ if ( !$usageList || $usageList === [] ) {
+ return '';
+ }
+
+ $usage = end( $usageList );
+ $usageCount = $usage[1];
+ $date = $this->getContext()->getLanguage()->timeanddate( $cachedLookupList->getTimestamp() );
+
+ $countMsg = Message::get( [ 'smw-property-indicator-last-count-update', $date ] );
+ $indicatorClass = ( $usageCount < 25000 ? ( $usageCount > 5000 ? ' moderate' : '' ) : ' high' );
+
+ return Html::rawElement(
+ 'span',
+ [
+ 'title' => $countMsg,
+ 'class' => 'usage-count' . $indicatorClass
+ ],
+ $usageCount
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PageInfo.php b/www/wiki/extensions/SemanticMediaWiki/src/PageInfo.php
new file mode 100644
index 00000000..3cb75e34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PageInfo.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Facade interface to specify access to page information
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+interface PageInfo {
+
+ /**
+ * Returns a modification date
+ *
+ * @since 1.9
+ *
+ * @return integer
+ */
+ public function getModificationDate();
+
+ /**
+ * Returns a creation date
+ *
+ * @since 1.9
+ *
+ * @return integer
+ */
+ public function getCreationDate();
+
+ /**
+ * Whether the page object is new or not
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function isNewPage();
+
+ /**
+ * Returns a user object for the last editor
+ *
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getLastEditor();
+
+ /**
+ * @since 1.9.1
+ *
+ * @return boolean
+ */
+ public function isFilePage();
+
+ /**
+ * @see File::getMediaType
+ *
+ * @since 1.9.1
+ *
+ * @return string|null
+ */
+ public function getMediaType();
+
+ /**
+ * @see File::getMimeType
+ *
+ * @since 1.9.1
+ *
+ * @return string|null
+ */
+ public function getMimeType();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParameterListDocBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/ParameterListDocBuilder.php
new file mode 100644
index 00000000..369d66c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParameterListDocBuilder.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW;
+
+use ParamProcessor\ParamDefinition;
+
+/**
+ * @since 2.4
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ParameterListDocBuilder {
+
+ /**
+ * @var callable
+ */
+ private $msg;
+
+ /**
+ * @param callable $messageFunction
+ */
+ public function __construct( callable $messageFunction ) {
+ $this->msg = $messageFunction;
+ }
+
+ /**
+ * Returns the wikitext for a table listing the provided parameters.
+ *
+ * @param ParamDefinition[] $paramDefinitions
+ *
+ * @return string
+ */
+ public function getParameterTable( array $paramDefinitions ) {
+ $tableRows = [];
+ $hasAliases = $this->containsAliases( $paramDefinitions );
+
+ foreach ( $paramDefinitions as $parameter ) {
+ if ( $parameter->getName() !== 'format' ) {
+ $tableRows[] = $this->getDescriptionRow( $parameter, $hasAliases );
+ }
+ }
+
+ if ( empty( $tableRows ) ) {
+ return '';
+ }
+
+ $tableRows = array_merge( [
+ '!' . $this->msg( 'validator-describe-header-parameter' ) ."\n" .
+ ( $hasAliases ? '!' . $this->msg( 'validator-describe-header-aliases' ) ."\n" : '' ) .
+ '!' . $this->msg( 'validator-describe-header-type' ) ."\n" .
+ '!' . $this->msg( 'validator-describe-header-default' ) ."\n" .
+ '!' . $this->msg( 'validator-describe-header-description' )
+ ], $tableRows );
+
+ return '{| class="wikitable sortable"' . "\n" .
+ implode( "\n|-\n", $tableRows ) .
+ "\n|}";
+ }
+
+ /**
+ * @param ParamDefinition[] $paramDefinitions
+ *
+ * @return boolean
+ */
+ private function containsAliases( array $paramDefinitions ) {
+ foreach ( $paramDefinitions as $parameter ) {
+ if ( !empty( $parameter->getAliases() ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the wikitext for a table row describing a single parameter.
+ *
+ * @param ParamDefinition $parameter
+ * @param boolean $hasAliases
+ *
+ * @return string
+ */
+ private function getDescriptionRow( ParamDefinition $parameter, $hasAliases ) {
+ if ( $hasAliases ) {
+ $aliases = $parameter->getAliases();
+ $aliases = count( $aliases ) > 0 ? implode( ', ', $aliases ) : ' -';
+ }
+
+ $description = $this->msg( $parameter->getMessage() );
+
+ $type = $this->msg( $parameter->getTypeMessage() );
+
+ $default = $parameter->isRequired() ? "''" . $this->msg( 'validator-describe-required' ) . "''" : $parameter->getDefault();
+ if ( is_array( $default ) ) {
+ $default = implode( ', ', $default );
+ }
+ elseif ( is_bool( $default ) ) {
+ $default = $default ? 'yes' : 'no';
+ }
+
+ if ( $default === '' ) {
+ $default = "''" . $this->msg( 'validator-describe-empty' ) . "''";
+ }
+
+ return "|{$parameter->getName()}\n"
+ . ( $hasAliases ? '|' . $aliases . "\n" : '' ) .
+ <<<EOT
+|{$type}
+|{$default}
+|{$description}
+EOT;
+ }
+
+ private function msg() {
+ return call_user_func_array( $this->msg, func_get_args() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParameterProcessorFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/ParameterProcessorFactory.php
new file mode 100644
index 00000000..c9db2b54
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParameterProcessorFactory.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParameterProcessorFactory {
+
+ /**
+ * @since 1.9
+ *
+ * @param array $parameters
+ *
+ * @return ParserParameterProcessor
+ */
+ public static function newFromArray( array $parameters ) {
+ $instance = new self();
+ return $instance->newParserParameterProcessor( $parameters );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $parameters
+ *
+ * @return ParserParameterProcessor
+ */
+ public function newParserParameterProcessor( array $parameters ) {
+
+ if ( isset( $parameters[0] ) && is_object( $parameters[0] ) ) {
+ array_shift( $parameters );
+ }
+
+ return new ParserParameterProcessor( $parameters );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parameters.php b/www/wiki/extensions/SemanticMediaWiki/src/Parameters.php
new file mode 100644
index 00000000..5088d29d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parameters.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Parameters {
+
+ /**
+ * @var array
+ */
+ private $parameters = [];
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( array $parameters = [] ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+ $this->parameters[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param array $value
+ */
+ public function merge( $key, array $value ) {
+
+ if ( !isset( $this->parameters[$key] ) ) {
+ $this->parameters[$key] = [];
+ }
+
+ $this->parameters[$key] += $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function has( $key ) {
+ return isset( $this->parameters[$key] ) || array_key_exists( $key, $this->parameters );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return $this->parameters[$key];
+ }
+
+ throw new InvalidArgumentException( "$key is an unregistered key." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parser/InTextAnnotationParser.php b/www/wiki/extensions/SemanticMediaWiki/src/Parser/InTextAnnotationParser.php
new file mode 100644
index 00000000..f38ff33e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parser/InTextAnnotationParser.php
@@ -0,0 +1,462 @@
+<?php
+
+namespace SMW\Parser;
+
+use Hooks;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\Localizer;
+use SMW\MediaWiki\MagicWordsFinder;
+use SMW\MediaWiki\RedirectTargetFinder;
+use SMW\MediaWiki\StripMarkerDecoder;
+use SMW\ParserData;
+use SMW\Utils\Timer;
+use SMWOutputs;
+use Title;
+
+/**
+ * Class collects all functions for wiki text parsing / processing that are
+ * relevant for SMW
+ *
+ * This class is contains all functions necessary for parsing wiki text before
+ * it is displayed or previewed while identifying SMW related annotations.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Denny Vrandecic
+ * @author mwjames
+ */
+class InTextAnnotationParser {
+
+ /**
+ * Internal state for switching SMW link annotations off/on during parsing
+ * ([[SMW::on]] and [[SMW:off]])
+ */
+ const OFF = '[[SMW::off]]';
+ const ON = '[[SMW::on]]';
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var LinksProcessor
+ */
+ private $linksProcessor;
+
+ /**
+ * @var MagicWordsFinder
+ */
+ private $magicWordsFinder;
+
+ /**
+ * @var RedirectTargetFinder
+ */
+ private $redirectTargetFinder;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory = null;
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory = null;
+
+ /**
+ * @var StripMarkerDecoder
+ */
+ private $stripMarkerDecoder;
+
+ /**
+ * @var boolean
+ */
+ protected $isEnabledNamespace;
+
+ /**
+ * Internal state for switching SMW link annotations off/on during parsing
+ * ([[SMW::on]] and [[SMW:off]])
+ * @var boolean
+ */
+ protected $isAnnotation = true;
+
+ /**
+ * @var boolean|integer
+ */
+ private $isLinksInValues = false;
+
+ /**
+ * @var boolean
+ */
+ private $showErrors = true;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param LinksProcessor $linksProcessor
+ * @param MagicWordsFinder $magicWordsFinder
+ * @param RedirectTargetFinder $redirectTargetFinder
+ */
+ public function __construct( ParserData $parserData, LinksProcessor $linksProcessor, MagicWordsFinder $magicWordsFinder, RedirectTargetFinder $redirectTargetFinder ) {
+ $this->parserData = $parserData;
+ $this->linksProcessor = $linksProcessor;
+ $this->magicWordsFinder = $magicWordsFinder;
+ $this->redirectTargetFinder = $redirectTargetFinder;
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isLinksInValues
+ */
+ public function isLinksInValues( $isLinksInValues ) {
+ $this->isLinksInValues = $isLinksInValues;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $showErrors
+ */
+ public function showErrors( $showErrors ) {
+ $this->showErrors = (bool)$showErrors;
+ }
+
+ /**
+ * Parsing text before an article is displayed or previewed, strip out
+ * semantic properties and add them to the ParserOutput object
+ *
+ * @since 1.9
+ *
+ * @param string &$text
+ */
+ public function parse( &$text ) {
+
+ $title = $this->parserData->getTitle();
+ Timer::start( __CLASS__ );
+
+ // Identifies the current parser run (especially when called recursively)
+ $this->parserData->getSubject()->setContextReference( 'intp:' . uniqid() );
+
+ $this->doStripMagicWordsFromText( $text );
+
+ $this->isEnabledNamespace = $this->isSemanticEnabledForNamespace( $title );
+
+ $this->addRedirectTargetAnnotationFromText(
+ $text
+ );
+
+ // Obscure [/] to find a set of [[ :: ... ]] while those in-between are left for
+ // decoding in a post-processing so that the regex can split the text
+ // appropriately
+ if ( $this->isLinksInValues ) {
+ $text = LinksEncoder::findAndEncodeLinks( $text, $this );
+ }
+
+ // No longer used with 3.0 given that the LinksEncoder is safer and faster
+ $linksInValuesPcre = false;
+
+ $text = preg_replace_callback(
+ $this->getRegexpPattern( $linksInValuesPcre ),
+ $linksInValuesPcre ? 'self::process' : 'self::preprocess',
+ $text
+ );
+
+ // Ensure remaining encoded entities are decoded again
+ $text = LinksEncoder::removeLinkObfuscation( $text );
+
+ if ( $this->isEnabledNamespace ) {
+ $this->parserData->getOutput()->addModules( $this->getModules() );
+ $this->parserData->addExtraParserKey( 'userlang' );
+ }
+
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ $this->parserData->addLimitReport(
+ 'intext-parsertime',
+ Timer::getElapsedTime( __CLASS__, 3 )
+ );
+
+ SMWOutputs::commitToParserOutput( $this->parserData->getOutput() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return boolean
+ */
+ public static function hasMarker( $text ) {
+ return strpos( $text, self::OFF ) !== false || strpos( $text, self::ON ) !== false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function decodeSquareBracket( $text ) {
+ return LinksEncoder::decodeSquareBracket( $text );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function obfuscateAnnotation( $text ) {
+ return LinksEncoder::obfuscateAnnotation( $text );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function removeAnnotation( $text ) {
+ return LinksEncoder::removeAnnotation( $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param StripMarkerDecoder $stripMarkerDecoder
+ */
+ public function setStripMarkerDecoder( StripMarkerDecoder $stripMarkerDecoder ) {
+ $this->stripMarkerDecoder = $stripMarkerDecoder;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title|null $redirectTarget
+ */
+ public function setRedirectTarget( Title $redirectTarget = null ) {
+ $this->redirectTargetFinder->setRedirectTarget( $redirectTarget );
+ }
+
+ protected function addRedirectTargetAnnotationFromText( $text ) {
+
+ if ( !$this->isEnabledNamespace ) {
+ return;
+ }
+
+ $this->redirectTargetFinder->findRedirectTargetFromText( $text );
+
+ $propertyAnnotatorFactory = $this->applicationFactory->singleton( 'PropertyAnnotatorFactory' );
+
+ $propertyAnnotator = $propertyAnnotatorFactory->newNullPropertyAnnotator(
+ $this->parserData->getSemanticData()
+ );
+
+ $redirectPropertyAnnotator = $propertyAnnotatorFactory->newRedirectPropertyAnnotator(
+ $propertyAnnotator,
+ $this->redirectTargetFinder
+ );
+
+ $redirectPropertyAnnotator->addAnnotation();
+ }
+
+ /**
+ * Returns required resource modules
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ protected function getModules() {
+ return [
+ 'ext.smw.style',
+ 'ext.smw.tooltips'
+ ];
+ }
+
+ /**
+ * @see LinksProcessor::getRegexpPattern
+ * @since 1.9
+ *
+ * @param boolean $linksInValues
+ *
+ * @return string
+ */
+ public function getRegexpPattern( $linksInValues = false ) {
+ return LinksProcessor::getRegexpPattern( $linksInValues );
+ }
+
+ /**
+ * @see linksProcessor::preprocess
+ * @since 1.9
+ *
+ * @param array $semanticLink expects (linktext, properties, value|caption)
+ *
+ * @return string
+ */
+ public function preprocess( array $semanticLink ) {
+
+ $semanticLinks = $this->linksProcessor->preprocess( $semanticLink );
+
+ if ( is_string( $semanticLinks ) ) {
+ return $semanticLinks;
+ }
+
+ return $this->process( $semanticLinks );
+ }
+
+ /**
+ * @see linksProcessor::process
+ * @since 1.9
+ *
+ * @param array $semanticLink expects (linktext, properties, value|caption)
+ *
+ * @return string
+ */
+ protected function process( array $semanticLink ) {
+
+ $valueCaption = false;
+ $property = '';
+ $value = '';
+
+ $semanticLinks = $this->linksProcessor->process(
+ $semanticLink
+ );
+
+ $this->isAnnotation = $this->linksProcessor->isAnnotation();
+
+ if ( is_string( $semanticLinks ) ) {
+ return $semanticLinks;
+ }
+
+ list( $properties, $value, $valueCaption ) = $semanticLinks;
+
+ $subject = $this->parserData->getSubject();
+
+ if ( ( $propertyLink = $this->getPropertyLink( $subject, $properties, $value, $valueCaption ) ) !== '' ) {
+ return $propertyLink;
+ }
+
+ return $this->addPropertyValue( $subject, $properties, $value, $valueCaption );
+ }
+
+ /**
+ * Adds property values to the ParserOutput instance
+ *
+ * @since 1.9
+ *
+ * @param array $properties
+ *
+ * @return string
+ */
+ protected function addPropertyValue( $subject, array $properties, $value, $valueCaption ) {
+
+ $origValue = $value;
+
+ if ( $this->stripMarkerDecoder !== null ) {
+ $value = $this->stripMarkerDecoder->decode( $value );
+ }
+
+ // Add properties to the semantic container
+ foreach ( $properties as $property ) {
+ $dataValue = $this->dataValueFactory->newDataValueByText(
+ $property,
+ $value,
+ $valueCaption,
+ $subject
+ );
+
+ if (
+ $this->isEnabledNamespace &&
+ $this->isAnnotation &&
+ $this->parserData->canUse() ) {
+ $this->parserData->addDataValue( $dataValue );
+ }
+ }
+
+ // Return the wikitext or the unmodified text representation in case of
+ // a strip marker in order for the standard Parser to work its magic since
+ // we were only interested in the value for the annotation
+ if ( $origValue !== $value ) {
+ $result = $origValue;
+ } else {
+ $result = $dataValue->getShortWikitext( true );
+ }
+
+ // If necessary add an error text
+ if ( ( $this->showErrors &&
+ $this->isEnabledNamespace && $this->isAnnotation ) &&
+ ( !$dataValue->isValid() ) ) {
+ // Encode `:` to avoid a comment block and instead of the nowiki tag
+ // use &#58; as placeholder
+ $result = str_replace( ':', '&#58;', $result ) . $dataValue->getErrorText();
+ }
+
+ return $result;
+ }
+
+ protected function doStripMagicWordsFromText( &$text ) {
+
+ $words = [];
+
+ $this->magicWordsFinder->setOutput( $this->parserData->getOutput() );
+
+ $magicWords = [
+ 'SMW_NOFACTBOX',
+ 'SMW_SHOWFACTBOX'
+ ];
+
+ Hooks::run( 'SMW::Parser::BeforeMagicWordsFinder', [ &$magicWords ] );
+
+ foreach ( $magicWords as $magicWord ) {
+ $words[] = $this->magicWordsFinder->findMagicWordInText( $magicWord, $text );
+ }
+
+ $this->magicWordsFinder->pushMagicWordsToParserOutput( $words );
+
+ return $words;
+ }
+
+ private function isSemanticEnabledForNamespace( Title $title ) {
+ return $this->applicationFactory->getNamespaceExaminer()->isSemanticEnabled( $title->getNamespace() );
+ }
+
+ private function getPropertyLink( $subject, $properties, $value, $valueCaption ) {
+
+ // #1855
+ if ( substr( $value, 0, 3 ) !== '@@@' ) {
+ return '';
+ }
+
+ $property = end( $properties );
+
+ $dataValue = $this->dataValueFactory->newPropertyValueByLabel(
+ $property,
+ $valueCaption,
+ $subject
+ );
+
+ if ( ( $lang = Localizer::getAnnotatedLanguageCodeFrom( $value ) ) !== false ) {
+ $dataValue->setOption( $dataValue::OPT_USER_LANGUAGE, $lang );
+ $dataValue->setCaption(
+ $valueCaption === false ? $dataValue->getWikiValue() : $valueCaption
+ );
+ }
+
+ $dataValue->setOption( $dataValue::OPT_HIGHLIGHT_LINKER, true );
+
+ return $dataValue->getShortWikitext( smwfGetLinker() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksEncoder.php b/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksEncoder.php
new file mode 100644
index 00000000..eb31a0c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksEncoder.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace SMW\Parser;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksEncoder {
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ * @param InTextAnnotationParser $parser
+ *
+ * @return text
+ */
+ public static function findAndEncodeLinks( $text, InTextAnnotationParser $parser ) {
+
+ // #2193
+ // Use &#x005B; instead of &#91; to distinguish it from the MW's Sanitizer
+ // who uses the same decode sequence and avoid issues when removing links
+ // after obfuscation
+
+ // #2671
+ // Use &#x005D; instead of &#93; (]) since CiteExtension use the later as
+ // encoding for the ref brackets
+
+ // Filter simple [ ... ] from [[ ... ]] links and ensure to find the correct
+ // start and end in case of [[Foo::[[Bar]]]] or [[Foo::[http://example.org/foo]]]
+ $text = str_replace(
+ [ '[', ']', '&#x005B;&#x005B;', '&#x005D;&#x005D;&#x005D;&#x005D;', '&#x005D;&#x005D;&#x005D;', '&#x005D;&#x005D;' ],
+ [ '&#x005B;', '&#x005D;', '[[', ']]]]', '&#x005D;]]', ']]' ],
+ $text
+ );
+
+ // Deep nesting is NOT supported as in [[Foo::[[abc]] [[Bar::123[[abc]] ]] ]]
+ return self::matchAndReplace( $text, $parser );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function removeLinkObfuscation( $text ) {
+
+ $from = [ '&#x005B;', '&#x005D;', '&#124;' ];
+ $to = [ '[', ']', '|' ];
+
+ return str_replace( $from, $to, $text );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function encodeLinks( $text ) {
+ return str_replace(
+ [ '[', ']', '|' ],
+ [ '&#x005B;', '&#x005D;', '&#124;' ],
+ $text
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function decodeSquareBracket( $text ) {
+ return str_replace( [ '%5B', '%5D' ], [ '[', ']' ], $text );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function obfuscateAnnotation( $text ) {
+ return preg_replace_callback(
+ LinksProcessor::getRegexpPattern( false ),
+ function( array $matches ) {
+ return str_replace( '[', '&#91;', $matches[0] );
+ },
+ self::decodeSquareBracket( $text )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function removeAnnotation( $text ) {
+
+ if ( strpos( $text, '::' ) === false && strpos( $text, ':=' ) === false ) {
+ return $text;
+ }
+
+ return preg_replace_callback(
+ LinksProcessor::getRegexpPattern( false ),
+ 'self::doRemoveAnnotation',
+ self::decodeSquareBracket( $text )
+ );
+ }
+
+ private static function doRemoveAnnotation( array $matches ) {
+
+ $caption = false;
+ $value = '';
+
+ // #1453
+ if ( $matches[0] === InTextAnnotationParser::OFF || $matches[0] === InTextAnnotationParser::ON ) {
+ return false;
+ }
+
+ // Strict mode matching
+ if ( array_key_exists( 1, $matches ) ) {
+ if ( strpos( $matches[1], ':' ) !== false && isset( $matches[2] ) ) {
+ list( $matches[1], $matches[2] ) = explode( '::', $matches[1] . '::' . $matches[2], 2 );
+ }
+ }
+
+ if ( array_key_exists( 2, $matches ) ) {
+
+ // #1747
+ if ( strpos( $matches[1], '|' ) !== false ) {
+ return $matches[0];
+ }
+
+ $parts = explode( '|', $matches[2] );
+ $value = array_key_exists( 0, $parts ) ? $parts[0] : '';
+ $caption = array_key_exists( 1, $parts ) ? $parts[1] : false;
+ }
+
+ // #1855
+ if ( $value === '@@@' ) {
+ $value = '';
+ }
+
+ return $caption !== false ? $caption : $value;
+ }
+
+ private static function matchAndReplace( $text, $parser ) {
+
+ /**
+ * @see http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
+ *
+ * \[{2} # find the first opening '[['.
+ * (?: # start a new group, this is so '|' below does not apply/affect the opening '['.
+ * [^\[\]]+ # skip ahead happily if no '[' or ']'.
+ * | # ...otherwise...
+ * (?R) # we may be at the start of a new group, repeat whole pattern.
+ * )
+ * * # nesting can be many levels deep.
+ * \]{2} # finally, expect a balanced closing ']]'
+ */
+ preg_match_all("/\[{2}(?:[^\[\]]+|(?R))*\]{2}/is", $text, $matches );
+ $isOffAnnotation = false;
+
+ // At this point we distinguish between a normal [[Foo::bar]] annotation
+ // and a compound construct such as [[Foo::[[Foobar::Bar]] ]] and
+ // [[Foo::[http://example.org/foo foo] [[Foo::123|Bar]] ]].
+ //
+ // Only the compound is being processed and matched as we require to
+ // identify the boundaries of the enclosing annotation
+ foreach ( $matches[0] as $match ) {
+
+ // Ignore simple links like `[[:Property:Has URL|Has URL]]` but
+ // do parse `[[Has description::[[foo]][[:bar]]]]` (:= legacy notation)
+ if ( strpos( $match, '[[' ) !== false && strpos( $match, '::' ) === false && strpos( $match, ':=' ) === false ) {
+ continue;
+ }
+
+ // Remember whether the text contains OFF/ON marker (added by
+ // recursive parser, template, embedded result printer)
+ if ( $isOffAnnotation === false ) {
+ $isOffAnnotation = $match === InTextAnnotationParser::OFF;
+ }
+
+ $annotationOpenNum = substr_count( $match, '[[' );
+
+ // Only engage if the match contains more than one [[ :: ]] pair
+ if ( $annotationOpenNum > 1 ) {
+ $replace = self::replace( $match, $parser, $isOffAnnotation );
+ $text = str_replace( $match, $replace, $text );
+ }
+ }
+
+ return $text;
+ }
+
+ private static function replace( $match, $parser, $isOffAnnotation = false ) {
+
+ // Remove the Leading and last square bracket to avoid distortion
+ // during the annotation parsing
+ $match = substr( substr( $match, 2 ), 0, -2 );
+
+ // Restore OFF/ON for the recursive processing
+ if ( $isOffAnnotation === true ) {
+ $match = InTextAnnotationParser::OFF . $match . InTextAnnotationParser::ON;
+ }
+
+ // Only match annotations of style [[...::...]] during a recursive
+ // obfuscation process, any other processing is being done by the
+ // InTextAnnotation parser hereafter
+ //
+ // [[Foo::Bar]] annotation therefore run a pattern match and
+ // obfuscate the returning [[, |, ]] result
+ $replace = self::encodeLinks( preg_replace_callback(
+ LinksProcessor::getRegexpPattern( false ),
+ [ $parser, 'preprocess' ],
+ $match
+ ) );
+
+ // Restore the square brackets
+ return '[[' . $replace . ']]';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksProcessor.php
new file mode 100644
index 00000000..f6318e70
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parser/LinksProcessor.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace SMW\Parser;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksProcessor {
+
+ /**
+ * Internal state for switching SMW link annotations off/on during parsing
+ * ([[SMW::on]] and [[SMW:off]])
+ *
+ * @var boolean
+ */
+ private $isAnnotation = true;
+
+ /**
+ * @var boolean
+ */
+ private $isStrictMode = true;
+
+ /**
+ * Whether a strict interpretation (e.g [[property::value:partOfTheValue::alsoPartOfTheValue]])
+ * or a more loose interpretation (e.g. [[property1::property2::value]]) for
+ * annotations is expected.
+ *
+ * @since 2.3
+ *
+ * @param boolean $isStrictMode
+ */
+ public function isStrictMode( $isStrictMode ) {
+ $this->isStrictMode = (bool)$isStrictMode;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isAnnotation() {
+ return $this->isAnnotation;
+ }
+
+ /**
+ * $smwgLinksInValues (default = false) determines which regexp pattern
+ * is returned, either a more complex (lib PCRE may cause segfaults if text
+ * is long) or a simpler (no segfaults found for those, but no links
+ * in values) pattern.
+ *
+ * If enabled (SMW accepts inputs like [[property::Some [[link]] in value]]),
+ * this may lead to PHP crashes (!) when very long texts are
+ * used as values. This is due to limitations in the library PCRE that
+ * PHP uses for pattern matching.
+ *
+ * @since 1.9
+ *
+ * @param boolean $linksInValues
+ *
+ * @return string
+ */
+ public static function getRegexpPattern( $linksInValues = false ) {
+
+ if ( $linksInValues ) {
+ return '/\[\[ # Beginning of the link
+ (?:([^:][^]]*):[=:])+ # Property name (or a list of those)
+ ( # After that:
+ (?:[^|\[\]] # either normal text (without |, [ or ])
+ |\[\[[^]]*\]\] # or a [[link]]
+ |\[[^]]*\] # or an [external link]
+ )*) # all this zero or more times
+ (?:\|([^]]*))? # Display text (like "text" in [[link|text]]), optional
+ \]\] # End of link
+ /xu';
+ }
+
+ return '/\[\[ # Beginning of the link
+ (?:([^:][^]]*):[=:])+ # Property name (or a list of those)
+ ([^\[\]]*) # content: anything but [, |, ]
+ \]\] # End of link
+ /xu';
+ }
+
+ /**
+ * A method that precedes the process method, it takes care of separating
+ * value and caption (instead of leaving this to a more complex regexp).
+ *
+ * @since 1.9
+ *
+ * @param array $semanticLink expects (linktext, properties, value|caption)
+ *
+ * @return string
+ */
+ public function preprocess( array $semanticLink ) {
+
+ $value = '';
+ $caption = false;
+
+ if ( array_key_exists( 2, $semanticLink ) ) {
+
+ // #1747 avoid a mismatch on an annotation like [[Foo|Bar::Foobar]]
+ // where the left part of :: is split and would contain "Foo|Bar"
+ // hence this type is categorized as no value annotation
+ if ( strpos( $semanticLink[1], '|' ) !== false ) {
+ return $semanticLink[0];
+ }
+
+ $parts = explode( '|', $semanticLink[2] );
+
+ if ( array_key_exists( 0, $parts ) ) {
+ $value = $parts[0];
+ }
+ if ( array_key_exists( 1, $parts ) ) {
+ $caption = $parts[1];
+ }
+ }
+
+ if ( $caption !== false ) {
+ return [ $semanticLink[0], $semanticLink[1], $value, $caption ];
+ }
+
+ return [ $semanticLink[0], $semanticLink[1], $value ];
+ }
+
+ /**
+ * Function strips out the semantic attributes from a wiki link.
+ *
+ * @since 1.9
+ *
+ * @param array $semanticLink expects (linktext, properties, value|caption)
+ *
+ * @return string
+ */
+ public function process( array $semanticLink ) {
+
+ $valueCaption = false;
+ $property = '';
+ $value = '';
+
+ if ( array_key_exists( 1, $semanticLink ) ) {
+
+ // Use case [[Foo::=Bar]] (:= being the legacy notation < 1.4) where
+ // the regex splits it into `Foo:` and `Bar` loosing `=` from the value.
+ // Restore the link to its previous form of `Foo::=Bar` and reapply
+ // a simple split.
+ if( strpos( $semanticLink[0], '::=' ) && substr( $semanticLink[1], -1 ) == ':' ) {
+ list( $semanticLink[1], $semanticLink[2] ) = explode( '::', $semanticLink[1] . ':=' . $semanticLink[2], 2 );
+ }
+
+ // #1252 Strict mode being disabled for support of multi property
+ // assignments (e.g. [[property1::property2::value]])
+
+ // #1066 Strict mode is to check for colon(s) produced by something
+ // like [[Foo::Bar::Foobar]], [[Foo:::0049 30 12345678]]
+ // In case a colon appears (in what is expected to be a string without a colon)
+ // then concatenate the string again and split for the first :: occurrence
+ // only
+ if ( $this->isStrictMode && strpos( $semanticLink[1], ':' ) !== false && isset( $semanticLink[2] ) ) {
+ list( $semanticLink[1], $semanticLink[2] ) = explode( '::', $semanticLink[1] . '::' . $semanticLink[2], 2 );
+ }
+
+ $property = $semanticLink[1];
+ }
+
+ if ( array_key_exists( 2, $semanticLink ) ) {
+ $value = $semanticLink[2];
+ }
+
+ $value = LinksEncoder::removeLinkObfuscation( $value );
+
+ if ( $value === '' ) { // silently ignore empty values
+ return '';
+ }
+
+ if ( $property == 'SMW' ) {
+ return $this->setAnnotation( $value );
+ }
+
+ if ( array_key_exists( 3, $semanticLink ) ) {
+ $valueCaption = $semanticLink[3];
+ }
+
+ // Extract annotations and create tooltip.
+ $properties = preg_split( '/:[=:]/u', $property );
+
+ return [ $properties, $value, $valueCaption ];
+ }
+
+ private function setAnnotation( $value ) {
+
+ switch ( $value ) {
+ case 'on':
+ $this->isAnnotation = true;
+ break;
+ case 'off':
+ $this->isAnnotation = false;
+ break;
+ }
+
+ return '';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parser/RecursiveTextProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/Parser/RecursiveTextProcessor.php
new file mode 100644
index 00000000..52973d40
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parser/RecursiveTextProcessor.php
@@ -0,0 +1,360 @@
+<?php
+
+namespace SMW\Parser;
+
+use Parser;
+use ParserOptions;
+use ParserOutput;
+use RuntimeException;
+use SMW\Localizer;
+use SMW\ParserData;
+use Title;
+
+/**
+ * @private
+ *
+ * Helper class in processing content that requires to be parsed internally and
+ * recursively mostly in connection with templates.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RecursiveTextProcessor {
+
+ /**
+ * @see Special:ExpandTemplates
+ * @var int Maximum size in bytes to include. 50MB allows fixing those huge pages
+ */
+ const MAX_INCLUDE_SIZE = 50000000;
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * Incremented while expanding templates inserted during printout; stop
+ * expansion at some point
+ *
+ * @var integer
+ */
+ private $recursionDepth = 0;
+
+ /**
+ * @var integer
+ */
+ private $maxRecursionDepth = 2;
+
+ /**
+ * @var boolean
+ */
+ private $recursiveAnnotation = false;
+
+ /**
+ * @var integer
+ */
+ private $uniqid;
+
+ /**
+ * @var string
+ */
+ private $error = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Parser $parser|null
+ */
+ public function __construct( Parser $parser = null ) {
+ $this->parser = $parser;
+
+ if ( $this->parser === null ) {
+ $this->parser = $GLOBALS['wgParser'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Parser
+ */
+ public function getParser() {
+ return $this->parser;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getError() {
+ return $this->error;
+ }
+
+ /**
+ * Track recursive processing
+ *
+ * @since 3.0
+ *
+ * @param string|integer|null $uniqid
+ */
+ public function uniqid( $uniqid = null ) {
+
+ if ( $uniqid === null ) {
+ $uniqid = uniqid();
+ }
+
+ $this->uniqid = $uniqid;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $maxRecursionDepth
+ */
+ public function setMaxRecursionDepth( $maxRecursionDepth ) {
+ $this->maxRecursionDepth = $maxRecursionDepth;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $transcludeAnnotation
+ */
+ public function transcludeAnnotation( $transcludeAnnotation ) {
+
+ if ( $this->parser->getOutput() === null || $transcludeAnnotation === true ) {
+ return;
+ }
+
+ if ( $this->uniqid === null ) {
+ throw new RuntimeException( "Expected a uniqid and not null." );
+ }
+
+ $parserOutput = $this->parser->getOutput();
+ $track = $parserOutput->getExtensionData( ParserData::ANNOTATION_BLOCK );
+
+ if ( $track === null ) {
+ $track = [];
+ }
+
+ // Track each embedded #ask process to ensure to remove
+ // blocks on the correct recursive iteration (e.g Page A containing
+ // #ask is transcluded in Page B using a #ask -> is embedded ...
+ // etc.)
+ $track[$this->uniqid] = true;
+
+ $parserOutput->setExtensionData( ParserData::ANNOTATION_BLOCK, $track );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function releaseAnnotationBlock() {
+
+ if ( $this->parser->getOutput() === null ) {
+ return;
+ }
+
+ $parserOutput = $this->parser->getOutput();
+ $track = $parserOutput->getExtensionData( ParserData::ANNOTATION_BLOCK );
+
+ if ( $track !== [] ) {
+ unset( $track[$this->uniqid] );
+ }
+
+ // No recursive tracks left, set it to false
+ if ( $track === [] ) {
+ $track = false;
+ }
+
+ $parserOutput->setExtensionData( ParserData::ANNOTATION_BLOCK, $track );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function releaseAnyAnnotationBlock() {
+ if ( $this->parser->getOutput() !== null ) {
+ $this->parser->getOutput()->setExtensionData( ParserData::ANNOTATION_BLOCK, false );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $recursiveAnnotation
+ */
+ public function setRecursiveAnnotation( $recursiveAnnotation ) {
+ $this->recursiveAnnotation = (bool)$recursiveAnnotation;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ParserData $parserData
+ */
+ public function copyData( ParserData $parserData ) {
+ if ( $this->recursiveAnnotation ) {
+ $parserData->importFromParserOutput( $this->parser->getOutput() );
+ }
+ }
+
+ /**
+ * @see Special:ExpandTemplates
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public function expandTemplates( $text ) {
+
+ if ( $this->parser === null ) {
+ throw new RuntimeException( 'Missing a parser instance!' );
+ }
+
+ $options = $this->parser->getOptions();
+
+ if ( !$options instanceof ParserOptions ) {
+ $options = new ParserOptions();
+ $options->setRemoveComments( true );
+ $options->setTidy( true );
+ $options->setMaxIncludeSize( self::MAX_INCLUDE_SIZE );
+ }
+
+ $title = $this->parser->getTitle();
+
+ if ( !$title instanceof Title ) {
+ $title = $GLOBALS['wgTitle'];
+
+ if ( !$title instanceof Title ) {
+ $title = Title::newFromText( 'UNKNOWN_TITLE' );
+ }
+ }
+
+ $text = $this->parser->preprocess( $text, $title, $options );
+
+ $text = str_replace(
+ [ '_&lt;nowiki&gt;_', '_&lt;/nowiki&gt;_', '_&lt;nowiki */&gt;_', '<nowiki>', '</nowiki>' ],
+ '',
+ $text
+ );
+
+ return $text;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public function recursivePreprocess( $text ) {
+
+ // not during parsing, no preprocessing needed, still protect the result
+ if ( $this->parser === null || !$this->parser->getTitle() instanceof Title || !$this->parser->getOptions() instanceof ParserOptions ) {
+ return $this->recursiveAnnotation ? $text : '[[SMW::off]]' . $text . '[[SMW::on]]';
+ }
+
+ $this->recursionDepth++;
+
+ // restrict recursion
+ if ( $this->recursionDepth <= $this->maxRecursionDepth && $this->recursiveAnnotation ) {
+ $text = $this->parser->recursivePreprocess( $text );
+ } elseif ( $this->recursionDepth <= $this->maxRecursionDepth ) {
+ $text = '[[SMW::off]]' . $this->parser->replaceVariables( $text ) . '[[SMW::on]]';
+ } else {
+ $this->error = [ 'smw-parser-recursion-level-exceeded', $this->maxRecursionDepth ];
+ $text = '';
+ }
+
+ // During a block request remove any categories from the text since we
+ // cannot block the annotation during a parse, this ensures that
+ // categories don't appear in the source text and hereby in any successive
+ // parse
+ $this->pruneCategory( $text );
+
+ $this->recursionDepth--;
+
+ return $text;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public function recursiveTagParse( $text ) {
+
+ if ( $this->parser === null ) {
+ throw new RuntimeException( 'Missing a parser instance!' );
+ }
+
+ $this->recursionDepth++;
+ $isValid = $this->parser->getTitle() instanceof Title && $this->parser->getOptions() instanceof ParserOptions;
+
+ if ( $this->recursionDepth <= $this->maxRecursionDepth && $isValid ) {
+ $text = $this->parser->recursiveTagParse( $text );
+ } elseif ( $this->recursionDepth <= $this->maxRecursionDepth ) {
+ $title = $GLOBALS['wgTitle'];
+
+ if ( $title === null ) {
+ $title = Title::newFromText( 'UNKNOWN_TITLE' );
+ }
+
+ $popt = new ParserOptions();
+
+ // FIXME: Remove the if block once compatibility with MW <1.31 is dropped
+ if ( ! defined( '\ParserOutput::SUPPORTS_STATELESS_TRANSFORMS' ) || \ParserOutput::SUPPORTS_STATELESS_TRANSFORMS !== 1 ) {
+ $popt->setEditSection( false );
+ }
+ $parserOutput = $this->parser->parse( $text . '__NOTOC__', $title, $popt );
+
+ // Maybe better to use Parser::recursiveTagParseFully ??
+
+ /// NOTE: as of MW 1.14SVN, there is apparently no better way to hide the TOC
+ \SMWOutputs::requireFromParserOutput( $parserOutput );
+ $text = $parserOutput->getText( [ 'enableSectionEditLinks' => false ] );
+ } else {
+ $this->error = [ 'smw-parser-recursion-level-exceeded', $this->maxRecursionDepth ];
+ $text = '';
+ }
+
+ $this->recursionDepth--;
+
+ return $text;
+ }
+
+ private function pruneCategory( &$text ) {
+
+ if ( $this->parser->getOutput() === null ) {
+ return;
+ }
+
+ $parserOutput = $this->parser->getOutput();
+
+ if ( ( $track = $parserOutput->getExtensionData( ParserData::ANNOTATION_BLOCK ) ) === false ) {
+ return;
+ }
+
+ // Content language dep. category name
+ $category = Localizer::getInstance()->getNamespaceTextById(
+ NS_CATEGORY
+ );
+
+ if ( isset( $track[$this->uniqid] ) ) {
+ $text = preg_replace(
+ "/\[\[(Category|{$category}):(.*)\]\]/U",
+ '',
+ $text
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Parser/SemanticLinksParser.php b/www/wiki/extensions/SemanticMediaWiki/src/Parser/SemanticLinksParser.php
new file mode 100644
index 00000000..277f86ea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Parser/SemanticLinksParser.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Parser;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SemanticLinksParser {
+
+ /**
+ * @var LinksProcessor
+ */
+ private $linksProcessor;
+
+ /**
+ * @since 2.5
+ *
+ * @param LinksProcessor $linksProcessor
+ */
+ public function __construct( LinksProcessor $linksProcessor ) {
+ $this->linksProcessor = $linksProcessor;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param $text
+ *
+ * @return array
+ */
+ public function parse( $text ) {
+
+ $matches = [];
+
+ preg_match(
+ $this->linksProcessor->getRegexpPattern(),
+ $text,
+ $matches
+ );
+
+ if ( $matches === [] ) {
+ return [];
+ }
+
+ $semanticLinks = $this->linksProcessor->preprocess( $matches );
+
+ if ( is_string( $semanticLinks ) ) {
+ return [];
+ }
+
+ $semanticLinks = $this->linksProcessor->process( $semanticLinks );
+
+ if ( is_string( $semanticLinks ) ) {
+ return [];
+ }
+
+ return $semanticLinks;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php
new file mode 100644
index 00000000..03d8d45f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php
@@ -0,0 +1,512 @@
+<?php
+
+namespace SMW;
+
+use Onoi\Cache\Cache;
+use ParserOptions;
+use ParserOutput;
+use Psr\Log\LoggerAwareTrait;
+use SMWDataValue as DataValue;
+use Title;
+
+/**
+ * Handling semantic data exchange with a ParserOutput object
+ *
+ * Provides access to a semantic data container that is generated
+ * either from the ParserOutput or is a newly created container
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class ParserData {
+
+ use LoggerAwareTrait;
+
+ /**
+ * Identifies the extension data
+ */
+ const DATA_ID = 'smwdata';
+
+ /**
+ * Identifies the cache namespace for update markers
+ */
+ const CACHE_NAMESPACE = 'smw:update';
+
+ /**
+ * Option that allows to force an update even in cases where an update
+ * marker exists
+ */
+ const OPT_FORCED_UPDATE = 'smw:opt.forced.update';
+
+ /**
+ * Option whether creation of iteratibe update jobs are allowed
+ */
+ const OPT_CREATE_UPDATE_JOB = 'smw:opt.create.update.job';
+
+ /**
+ * Indicates that an update was caused by a change propagation request
+ */
+ const OPT_CHANGE_PROP_UPDATE = 'smw:opt.change.prop.update';
+
+ /**
+ * Indicates that no #ask dependency tracking should occur
+ */
+ const NO_QUERY_DEPENDENCY_TRACE = 'no.query.dependency.trace';
+
+ /**
+ * Indicates that no #ask dependency tracking should occur
+ */
+ const ANNOTATION_BLOCK = 'smw-blockannotation';
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var ParserOutput
+ */
+ private $parserOutput;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var ParserOptions
+ */
+ private $parserOptions;
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var $canCreateUpdateJob
+ */
+ private $canCreateUpdateJob = true;
+
+ /**
+ * Identifies the origin of a request.
+ *
+ * @var string
+ */
+ private $origin = '';
+
+ /**
+ * @var Options
+ */
+ private $options = null;
+
+ /**
+ * @since 1.9
+ *
+ * @param Title $title
+ * @param ParserOutput $parserOutput
+ * @param Cache|null $cache
+ */
+ public function __construct( Title $title, ParserOutput $parserOutput, Cache $cache = null ) {
+ $this->title = $title;
+ $this->parserOutput = $parserOutput;
+ $this->cache = $cache;
+
+ if ( $this->cache === null ) {
+ $this->cache = ApplicationFactory::getInstance()->getCache();
+ }
+
+ $this->initSemanticData();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( !$this->options instanceof Options ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $origin
+ */
+ public function setOrigin( $origin ) {
+ $this->origin = $origin;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->getSemanticData()->getSubject();
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserOutput
+ */
+ public function getOutput() {
+ return $this->parserOutput;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ParserOptions $parserOptions
+ */
+ public function setParserOptions( ParserOptions $parserOptions ) {
+ $this->parserOptions = $parserOptions;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ParserOptions|null
+ */
+ public function addExtraParserKey( $key ) {
+ // Looks odd in 1.30 "Saved in parser cache ... idhash:19989-0!canonical!userlang!dateformat!userlang!dateformat!userlang!dateformat!userlang!dateformat and ..."
+ // threfore use the ParserOutput::recordOption instead
+ if ( $key === 'userlang' || $key === 'dateformat' ) {
+ $this->parserOutput->recordOption( $key );
+ } elseif ( $this->parserOptions !== null ) {
+ $this->parserOptions->addExtraKey( $key );
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function isBlocked() {
+
+ // ParserOutput::getExtensionData returns null if no value was set for this key
+ if ( $this->parserOutput->getExtensionData( self::ANNOTATION_BLOCK ) !== null &&
+ $this->parserOutput->getExtensionData( self::ANNOTATION_BLOCK ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function canUse() {
+ return !$this->isBlocked();
+ }
+
+ /**
+ * Returns collected errors occurred during processing
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function addError( $error ) {
+ $this->errors = array_merge( $this->errors, (array)$error );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param SemanticData $semanticData
+ */
+ public function setSemanticData( SemanticData $semanticData ) {
+ $this->semanticData = $semanticData;
+ }
+
+ /**
+ * @deprecated since 2.0, use setSemanticData
+ */
+ public function setData( SemanticData $semanticData ) {
+ $this->setSemanticData( $semanticData );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData() {
+ return $this->semanticData;
+ }
+
+ /**
+ * @deprecated since 2.0, use getSemanticData
+ */
+ public function getData() {
+ return $this->getSemanticData();
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function setEmptySemanticData() {
+ $this->setSemanticData( new SemanticData( DIWikiPage::newFromTitle( $this->title ) ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param ParserOutput|null
+ */
+ public function importFromParserOutput( ParserOutput $parserOutput = null ) {
+
+ if ( $parserOutput === null ) {
+ return;
+ }
+
+ $semanticData = $parserOutput->getExtensionData( self::DATA_ID );
+
+ // Only import data that is known to be different
+ if ( $semanticData !== null &&
+ $this->getSubject()->equals( $semanticData->getSubject() ) &&
+ $semanticData->getHash() !== $this->getSemanticData()->getHash() ) {
+
+ $this->getSemanticData()->importDataFrom( $semanticData );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function copyToParserOutput() {
+
+ // Ensure that errors are reported and recorded
+ $processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $this->getSubject()
+ );
+
+ foreach ( $this->errors as $error ) {
+ $processingErrorMsgHandler->addToSemanticData(
+ $this->semanticData,
+ $processingErrorMsgHandler->newErrorContainerFromMsg( $error )
+ );
+ }
+
+ $this->markParserOutput();
+ $this->parserOutput->setExtensionData( self::DATA_ID, $this->semanticData );
+ }
+
+ /**
+ * @deprecated since 3.0, use copyToParserOutput
+ */
+ public function pushSemanticDataToParserOutput() {
+ $this->copyToParserOutput();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function markParserOutput() {
+
+ $this->parserOutput->setTimestamp( wfTimestampNow() );
+
+ $this->parserOutput->setProperty(
+ 'smw-semanticdata-status',
+ $this->semanticData->getProperties() !== []
+ );
+ }
+
+ /**
+ * @deprecated since 3.0, use pushSemanticDataToParserOutput
+ */
+ public function setSemanticDataStateToParserOutputProperty() {
+ $this->markParserOutput();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ParserOutput $parserOutput
+ *
+ * @return boolean
+ */
+ public static function hasSemanticData( ParserOutput $parserOutput ) {
+ return (bool)$parserOutput->getProperty( 'smw-semanticdata-status' );
+ }
+
+ /**
+ * @see SemanticData::addDataValue
+ *
+ * @since 1.9
+ *
+ * @param SMWDataValue $dataValue
+ */
+ public function addDataValue( DataValue $dataValue ) {
+ $this->semanticData->addDataValue( $dataValue );
+ }
+
+ /**
+ * Persistent marker to identify an update with a revision ID and allow
+ * to filter successive updates with that very same ID.
+ *
+ * @see LinksUpdateConstructed::process
+ *
+ * @since 3.0
+ *
+ * @param integer $rev
+ */
+ public function markUpdate( $rev ) {
+ $this->cache->save( smwfCacheKey( self::CACHE_NAMESPACE, $this->semanticData->getSubject()->getHash() ), $rev, 3600 );
+ }
+
+ /**
+ * @private This method is not for public use
+ *
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function updateStore( $opts = [] ) {
+
+ $isDeferrableUpdate = false;
+
+ // @legacy
+ if ( $opts === true ) {
+ $isDeferrableUpdate = true;
+ }
+
+ if ( isset( $opts['defer'] ) && $opts['defer'] ) {
+ $isDeferrableUpdate = true;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $latestRevID = $this->title->getLatestRevID( Title::GAID_FOR_UPDATE );
+
+ if ( $this->skipUpdate( $latestRevID ) ) {
+
+ $this->logger->info(
+ [ 'Update', 'Skipping update', 'Found revision', '{revID}' ],
+ [ 'role' => 'user', 'revID' => $latestRevID ]
+ );
+
+ return false;
+ }
+
+ $this->semanticData->setOption(
+ Enum::OPT_SUSPEND_PURGE,
+ $this->getOption( Enum::OPT_SUSPEND_PURGE )
+ );
+
+ $dataUpdater = $applicationFactory->newDataUpdater(
+ $this->semanticData
+ );
+
+ $dataUpdater->canCreateUpdateJob(
+ $this->getOption( self::OPT_CREATE_UPDATE_JOB, true )
+ );
+
+ $dataUpdater->isChangeProp(
+ $this->getOption( self::OPT_CHANGE_PROP_UPDATE )
+ );
+
+ $dataUpdater->isDeferrableUpdate(
+ $isDeferrableUpdate
+ );
+
+ $dataUpdater->setOrigin(
+ $this->origin
+ );
+
+ $dataUpdater->doUpdate();
+
+ return true;
+ }
+
+ /**
+ * @note ParserOutput::setLimitReportData
+ *
+ * @since 2.4
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function addLimitReport( $key, $value ) {
+ $this->parserOutput->setLimitReportData( 'smw-limitreport-' . $key, $value );
+ }
+
+ /**
+ * Setup the semantic data container either from the ParserOutput or
+ * if not available create an empty container
+ */
+ private function initSemanticData() {
+
+ $this->semanticData = $this->parserOutput->getExtensionData( self::DATA_ID );
+
+ if ( !( $this->semanticData instanceof SemanticData ) ) {
+ $this->setEmptySemanticData();
+ }
+ }
+
+ private function skipUpdate( $rev ) {
+
+ if ( $this->getOption( self::OPT_FORCED_UPDATE, false ) ) {
+ return false;
+ }
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $this->semanticData->getSubject()->getHash()
+ );
+
+ return $this->cache->fetch( $key ) === $rev;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctionFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctionFactory.php
new file mode 100644
index 00000000..a38f404c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctionFactory.php
@@ -0,0 +1,521 @@
+<?php
+
+namespace SMW;
+
+// Fatal error: Cannot use SMW\ParserFunctions\SubobjectParserFunction as SubobjectParserFunction because the name is already in use
+use Parser;
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\ParserFunctions\AskParserFunction;
+use SMW\ParserFunctions\ConceptParserFunction;
+use SMW\ParserFunctions\DeclareParserFunction;
+use SMW\ParserFunctions\ExpensiveFuncExecutionWatcher;
+use SMW\ParserFunctions\RecurringEventsParserFunction as RecurringEventsParserFunc;
+use SMW\ParserFunctions\SetParserFunction;
+use SMW\ParserFunctions\ShowParserFunction;
+use SMW\ParserFunctions\SubobjectParserFunction as SubobjectParserFunc;
+use SMW\Utils\CircularReferenceGuard;
+
+/**
+ * @see http://www.semantic-mediawiki.org/wiki/Help:ParserFunction
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserFunctionFactory {
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * @since 1.9
+ *
+ * @param Parser|null $parser
+ */
+ public function __construct( Parser $parser = null ) {
+ $this->parser = $parser;
+ }
+
+ /**
+ * Convenience instantiation of a ParserFunctionFactory object
+ *
+ * @since 1.9
+ *
+ * @param Parser $parser
+ *
+ * @return ParserFunctionFactory
+ */
+ public static function newFromParser( Parser $parser ) {
+ return new self( $parser );
+ }
+
+ /**
+ * @deprecated since 2.1, use newSubobjectParserFunction
+ */
+ public function getSubobjectParser() {
+ return $this->newSubobjectParserFunction( $this->parser );
+ }
+
+ /**
+ * @deprecated since 2.1, use newRecurringEventsParserFunction
+ */
+ public function getRecurringEventsParser() {
+ return $this->newRecurringEventsParserFunction( $this->parser );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parser $parser
+ */
+ public function registerFunctionHandlers( Parser $parser ) {
+
+ list( $name, $definition, $flag ) = $this->getAskParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getShowParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getSubobjectParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getSetRecurringEventParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getSetParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getConceptParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+
+ list( $name, $definition, $flag ) = $this->getDeclareParserFunctionDefinition();
+ $parser->setFunctionHook( $name, $definition, $flag );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return AskParserFunction
+ */
+ public function newAskParserFunction( Parser $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $circularReferenceGuard = new CircularReferenceGuard( 'ask-parser' );
+ $circularReferenceGuard->setMaxRecursionDepth( 2 );
+
+ $parserData = $applicationFactory->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ if ( isset( $parser->getOptions()->smwAskNoDependencyTracking ) ) {
+ $parserData->setOption( $parserData::NO_QUERY_DEPENDENCY_TRACE, $parser->getOptions()->smwAskNoDependencyTracking );
+ }
+
+ // Avoid possible actions during for example stashedit etc.
+ $parserData->setOption( 'request.action', $GLOBALS['wgRequest']->getVal( 'action' ) );
+
+ $parserData->setParserOptions(
+ $parser->getOptions()
+ );
+
+ $messageFormatter = new MessageFormatter(
+ $parser->getTargetLanguage()
+ );
+
+ $expensiveFuncExecutionWatcher = new ExpensiveFuncExecutionWatcher(
+ $parserData
+ );
+
+ $expensiveFuncExecutionWatcher->setExpensiveThreshold(
+ $applicationFactory->getSettings()->get( 'smwgQExpensiveThreshold' )
+ );
+
+ $expensiveFuncExecutionWatcher->setExpensiveExecutionLimit(
+ $applicationFactory->getSettings()->get( 'smwgQExpensiveExecutionLimit' )
+ );
+
+ $askParserFunction = new AskParserFunction(
+ $parserData,
+ $messageFormatter,
+ $circularReferenceGuard,
+ $expensiveFuncExecutionWatcher
+ );
+
+ $askParserFunction->setPostProcHandler(
+ $applicationFactory->create( 'PostProcHandler', $parser->getOutput() )
+ );
+
+ $askParserFunction->setRecursiveTextProcessor(
+ new RecursiveTextProcessor( $parser )
+ );
+
+ return $askParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return ShowParserFunction
+ */
+ public function newShowParserFunction( Parser $parser ) {
+
+ $showParserFunction = new ShowParserFunction(
+ $this->newAskParserFunction( $parser )
+ );
+
+ return $showParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return SetParserFunction
+ */
+ public function newSetParserFunction( Parser $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $parserData = $applicationFactory->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $messageFormatter = new MessageFormatter(
+ $parser->getTargetLanguage()
+ );
+
+ $mediaWikiCollaboratorFactory = $applicationFactory->newMwCollaboratorFactory();
+
+ $stripMarkerDecoder = $mediaWikiCollaboratorFactory->newStripMarkerDecoder(
+ $parser->mStripState
+ );
+
+ $setParserFunction = new SetParserFunction(
+ $parserData,
+ $messageFormatter,
+ $mediaWikiCollaboratorFactory->newWikitextTemplateRenderer()
+ );
+
+ $setParserFunction->setStripMarkerDecoder(
+ $stripMarkerDecoder
+ );
+
+ return $setParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return ConceptParserFunction
+ */
+ public function newConceptParserFunction( Parser $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $parserData = $applicationFactory->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $messageFormatter = new MessageFormatter(
+ $parser->getTargetLanguage()
+ );
+
+ $conceptParserFunction = new ConceptParserFunction(
+ $parserData,
+ $messageFormatter
+ );
+
+ $conceptParserFunction->setPostProcHandler(
+ $applicationFactory->create( 'PostProcHandler', $parser->getOutput() )
+ );
+
+ return $conceptParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return SubobjectParserFunction
+ */
+ public function newSubobjectParserFunction( Parser $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $parserData = $applicationFactory->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $subobject = new Subobject( $parser->getTitle() );
+
+ $messageFormatter = new MessageFormatter(
+ $parser->getTargetLanguage()
+ );
+
+ $subobjectParserFunction = new SubobjectParserFunc(
+ $parserData,
+ $subobject,
+ $messageFormatter
+ );
+
+ $subobjectParserFunction->isCapitalLinks(
+ Site::isCapitalLinks()
+ );
+
+ $subobjectParserFunction->isComparableContent(
+ $applicationFactory->getSettings()->get( 'smwgUseComparableContentHash' )
+ );
+
+ $stripMarkerDecoder = $applicationFactory->newMwCollaboratorFactory()->newStripMarkerDecoder(
+ $parser->mStripState
+ );
+
+ $subobjectParserFunction->setStripMarkerDecoder(
+ $stripMarkerDecoder
+ );
+
+ return $subobjectParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return RecurringEventsParserFunction
+ */
+ public function newRecurringEventsParserFunction( Parser $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $parserData = $applicationFactory->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $subobject = new Subobject( $parser->getTitle() );
+
+ $messageFormatter = new MessageFormatter(
+ $parser->getTargetLanguage()
+ );
+
+ $recurringEvents = new RecurringEvents();
+
+ $recurringEvents->setDefaultNumRecurringEvents(
+ $settings->get( 'smwgDefaultNumRecurringEvents' )
+ );
+
+ $recurringEvents->setMaxNumRecurringEvents(
+ $settings->get( 'smwgMaxNumRecurringEvents' )
+ );
+
+ $recurringEventsParserFunction = new RecurringEventsParserFunc(
+ $parserData,
+ $subobject,
+ $messageFormatter,
+ $recurringEvents
+ );
+
+ $recurringEventsParserFunction->isCapitalLinks(
+ Site::isCapitalLinks()
+ );
+
+ $recurringEventsParserFunction->isComparableContent(
+ $settings->get( 'smwgUseComparableContentHash' )
+ );
+
+ return $recurringEventsParserFunction;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Parser $parser
+ *
+ * @return DeclareParserFunction
+ */
+ public function newDeclareParserFunction( Parser $parser ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $declareParserFunction = new DeclareParserFunction(
+ $parserData
+ );
+
+ return $declareParserFunction;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getAskParserFunctionDefinition() {
+
+ $askParserFunctionDefinition = function( $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $askParserFunction = $this->newAskParserFunction(
+ $parser
+ );
+
+ if ( !$settings->get( 'smwgQEnabled' ) ) {
+ return $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_INL_ERROR ) ? $askParserFunction->isQueryDisabled(): '';
+ }
+
+ return $askParserFunction->parse( func_get_args() );
+ };
+
+ return [ 'ask', $askParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getShowParserFunctionDefinition() {
+
+ $showParserFunctionDefinition = function( $parser ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $showParserFunction = $this->newShowParserFunction(
+ $parser
+ );
+
+ if ( !$settings->get( 'smwgQEnabled' ) ) {
+ return $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_INL_ERROR ) ? $showParserFunction->isQueryDisabled(): '';
+ }
+
+ return $showParserFunction->parse( func_get_args() );
+ };
+
+ return [ 'show', $showParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getSubobjectParserFunctionDefinition() {
+
+ $subobjectParserFunctionDefinition = function( $parser ) {
+
+ $subobjectParserFunction = $this->newSubobjectParserFunction(
+ $parser
+ );
+
+ return $subobjectParserFunction->parse(
+ ParameterProcessorFactory::newFromArray( func_get_args() )
+ );
+ };
+
+ return [ 'subobject', $subobjectParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getSetRecurringEventParserFunctionDefinition() {
+
+ $recurringEventsParserFunctionDefinition = function( $parser ) {
+
+ $recurringEventsParserFunction = $this->newRecurringEventsParserFunction(
+ $parser
+ );
+
+ return $recurringEventsParserFunction->parse(
+ ParameterProcessorFactory::newFromArray( func_get_args() )
+ );
+ };
+
+ return [ 'set_recurring_event', $recurringEventsParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getSetParserFunctionDefinition() {
+
+ $setParserFunctionDefinition = function( $parser ) {
+
+ $setParserFunction = $this->newSetParserFunction(
+ $parser
+ );
+
+ return $setParserFunction->parse(
+ ParameterProcessorFactory::newFromArray( func_get_args() )
+ );
+ };
+
+ return [ 'set', $setParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getConceptParserFunctionDefinition() {
+
+ $conceptParserFunctionDefinition = function( $parser ) {
+
+ $conceptParserFunction = $this->newConceptParserFunction(
+ $parser
+ );
+
+ return $conceptParserFunction->parse( func_get_args() );
+ };
+
+ return [ 'concept', $conceptParserFunctionDefinition, 0 ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getDeclareParserFunctionDefinition() {
+
+ $declareParserFunctionDefinition = function( $parser, $frame, $args ) {
+
+ $declareParserFunction = $this->newDeclareParserFunction(
+ $parser
+ );
+
+ return $declareParserFunction->parse( $frame, $args );
+ };
+
+ return [ 'declare', $declareParserFunctionDefinition, Parser::SFH_OBJECT_ARGS ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/AskParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/AskParserFunction.php
new file mode 100644
index 00000000..6f0b79d3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/AskParserFunction.php
@@ -0,0 +1,450 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Parser;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\MessageFormatter;
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\ParserData;
+use SMW\PostProcHandler;
+use SMW\ProcessingErrorMsgHandler;
+use SMW\Query\Deferred;
+use SMW\Utils\CircularReferenceGuard;
+use SMWQuery as Query;
+use SMWQueryProcessor as QueryProcessor;
+
+/**
+ * Provides the {{#ask}} parser function
+ *
+ * @see http://www.semantic-mediawiki.org/wiki/Help:Ask
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class AskParserFunction {
+
+ /**
+ * Fixed identifier for a deferred query request
+ */
+ const DEFERRED_REQUEST = '@deferred';
+
+ /**
+ * Fixed identifier
+ */
+ const NO_TRACE = '@notrace';
+
+ /**
+ * Fixed identifier to signal to the PostProcHandler that a post update is
+ * required with the output being used as input value for an annotation.
+ */
+ const IS_ANNOTATION = '@annotation';
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var MessageFormatter
+ */
+ private $messageFormatter;
+
+ /**
+ * @var CircularReferenceGuard
+ */
+ private $circularReferenceGuard;
+
+ /**
+ * @var ExpensiveFuncExecutionWatcher
+ */
+ private $expensiveFuncExecutionWatcher;
+
+ /**
+ * @var boolean
+ */
+ private $showMode = false;
+
+ /**
+ * @var integer
+ */
+ private $context = QueryProcessor::INLINE_QUERY;
+
+ /**
+ * @var PostProcHandler
+ */
+ private $postProcHandler;
+
+ /**
+ * @var RecursiveTextProcessor
+ */
+ private $recursiveTextProcessor;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param MessageFormatter $messageFormatter
+ * @param CircularReferenceGuard $circularReferenceGuard
+ * @param ExpensiveFuncExecutionWatcher $expensiveFuncExecutionWatcher
+ */
+ public function __construct( ParserData $parserData, MessageFormatter $messageFormatter, CircularReferenceGuard $circularReferenceGuard, ExpensiveFuncExecutionWatcher $expensiveFuncExecutionWatcher ) {
+ $this->parserData = $parserData;
+ $this->messageFormatter = $messageFormatter;
+ $this->circularReferenceGuard = $circularReferenceGuard;
+ $this->expensiveFuncExecutionWatcher = $expensiveFuncExecutionWatcher;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PostProcHandler $postProcHandler
+ */
+ public function setPostProcHandler( PostProcHandler $postProcHandler ) {
+ $this->postProcHandler = $postProcHandler;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param RecursiveTextProcessor $recursiveTextProcessor
+ */
+ public function setRecursiveTextProcessor( RecursiveTextProcessor $recursiveTextProcessor ) {
+ $this->recursiveTextProcessor = $recursiveTextProcessor;
+ }
+
+ /**
+ * Enable showMode (normally only invoked by {{#show}})
+ *
+ * @since 1.9
+ *
+ * @return AskParserFunction
+ */
+ public function setShowMode( $mode ) {
+ $this->showMode = $mode;
+ return $this;
+ }
+
+ /**
+ * {{#ask}} is disabled (see $smwgQEnabled)
+ *
+ * @since 1.9
+ *
+ * @return string|null
+ */
+ public function isQueryDisabled() {
+ return $this->messageFormatter->addFromKey( 'smw_iq_disabled' )->getHtml();
+ }
+
+ /**
+ * Parse parameters, return results from the query printer and update the
+ * ParserOutput with meta data from the query
+ *
+ * FIXME $rawParams use IParameterFormatter -> QueryParameterFormatter class
+ * Parse parameters and return query results to the ParserOutput
+ * object and output result data from the QueryProcessor
+ *
+ * @todo $rawParams should be of IParameterFormatter
+ * QueryParameterFormatter class
+ *
+ * @since 1.9
+ *
+ * @param array $functionParams
+ *
+ * @return string|null
+ */
+ public function parse( array $functionParams ) {
+
+ // Do we still need this?
+ // Reference found in SRF_Exhibit.php, SRF_Ploticus.php, SRF_Timeline.php, SRF_JitGraph.php
+ $GLOBALS['smwgIQRunningNumber']++;
+ $result = '';
+
+ list( $functionParams, $extraKeys ) = $this->prepareFunctionParameters(
+ $functionParams
+ );
+
+ if ( !isset( $extraKeys[self::NO_TRACE] ) ) {
+ $extraKeys[self::NO_TRACE] = $this->parserData->getOption( ParserData::NO_QUERY_DEPENDENCY_TRACE );
+ }
+
+ // No trace on queries invoked by special pages
+ if ( $this->parserData->getTitle()->getNamespace() === NS_SPECIAL ) {
+ $extraKeys[self::NO_TRACE] = true;
+ }
+
+ $result = $this->doFetchResultsFromFunctionParameters(
+ $functionParams,
+ $extraKeys
+ );
+
+ if ( $this->context === QueryProcessor::DEFERRED_QUERY ) {
+ Deferred::registerResources( $this->parserData->getOutput() );
+ }
+
+ $this->parserData->copyToParserOutput();
+
+ // 'userlang' will trigger a cache fragmentation by user language
+ $this->parserData->addExtraParserKey( 'userlang' );
+
+ // 'dateformat' will trigger a cache fragmentation by date preference
+ $this->parserData->addExtraParserKey( 'dateformat' );
+
+ return $result;
+ }
+
+ private function prepareFunctionParameters( array $functionParams ) {
+
+ // Remove parser object from parameters array
+ if( isset( $functionParams[0] ) && $functionParams[0] instanceof Parser ) {
+ array_shift( $functionParams );
+ }
+
+ $extraKeys = [];
+ $previous = false;
+
+ // Filter invalid parameters
+ foreach ( $functionParams as $key => $value ) {
+
+ if ( $value === self::DEFERRED_REQUEST ) {
+ $this->context = QueryProcessor::DEFERRED_QUERY;
+ unset( $functionParams[$key] );
+ continue;
+ }
+
+ if ( $value === self::NO_TRACE ) {
+ $extraKeys[self::NO_TRACE] = true;
+ unset( $functionParams[$key] );
+ continue;
+ }
+
+ if ( $value === self::IS_ANNOTATION ) {
+ $extraKeys[self::IS_ANNOTATION] = true;
+ unset( $functionParams[$key] );
+ continue;
+ }
+
+ // @see ParserOptionsRegister hook, use registered `localTime` key
+ if ( strpos( $value, '#LOCL#TO' ) !== false ) {
+ $this->parserData->addExtraParserKey( 'localTime' );
+ }
+
+ // Skip the first (being the condition) and other marked
+ // printrequests
+ if ( $key == 0 || ( $value !== '' && $value{0} === '?' ) ) {
+ continue;
+ }
+
+ // The MW parser swallows any `|` char (as it is used as field
+ // separator) hence make an educated guess about the condition when
+ // it contains `[[`...`]]` and the previous value was empty (expected
+ // due to ||) then the string should be concatenated and interpret
+ // as [[Foo]] || [[Bar]]
+ if (
+ ( $key > 0 && $previous === '' ) &&
+ ( strpos( $value, '[[' ) !== false && strpos( $value, ']]' ) !== false ) ) {
+ $functionParams[0] .= " || $value";
+ unset( $functionParams[$key] );
+ }
+
+ // Filter parameters that can not be split into
+ // argument=value
+ if ( strpos( $value, '=' ) === false ) {
+ unset( $functionParams[$key] );
+ }
+
+ $previous = $value;
+ }
+
+ return [ $functionParams, $extraKeys ];
+ }
+
+ private function doFetchResultsFromFunctionParameters( array $functionParams, array $extraKeys ) {
+
+ $contextPage = $this->parserData->getSubject();
+ $action = $this->parserData->getOption( 'request.action' );
+ $status = [];
+
+ if ( $extraKeys[self::NO_TRACE] === true ) {
+ $contextPage = null;
+ }
+
+ list( $query, $this->params ) = QueryProcessor::getQueryAndParamsFromFunctionParams(
+ $functionParams,
+ SMW_OUTPUT_WIKI,
+ $this->context,
+ $this->showMode,
+ $contextPage
+ );
+
+ if ( ( $result = $this->hasReachedExpensiveExecutionLimit( $query ) ) !== false ) {
+ return $result;
+ }
+
+ $query->setOption( Query::PROC_CONTEXT, 'AskParserFunction' );
+ $query->setOption( Query::NO_DEPENDENCY_TRACE, $extraKeys[self::NO_TRACE] );
+ $query->setOption( 'request.action', $action );
+
+ $queryHash = $query->getHash();
+
+ if ( $this->postProcHandler !== null && isset( $extraKeys[self::IS_ANNOTATION] ) ) {
+ $status[] = 100;
+ $this->postProcHandler->addUpdate( $query );
+ }
+
+ if ( $this->context === QueryProcessor::DEFERRED_QUERY ) {
+ $status[] = 200;
+ }
+
+ $this->circularReferenceGuard->mark( $queryHash );
+
+ // If we caught in a circular loop (due to a template referencing to itself)
+ // then we stop here before the next query execution to avoid an infinite
+ // self-reference
+ if ( $this->circularReferenceGuard->isCircular( $queryHash ) ) {
+ return '';
+ }
+
+ // #3230
+ // If the query contains a self reference (embedding page is part of the
+ // query condition) for a `edit` action then set an extra key so that the
+ // parser uses a different parser cache hereby allows for an additional
+ // parse on the next GET request to retrieve newly stored values that may
+ // have been appended during the `edit`.
+ if ( ( $action === 'submit' || $action === 'stashedit' ) && $query->getOption( 'self.reference' ) ) {
+ $this->parserData->addExtraParserKey( 'smwq' );
+ }
+
+ QueryProcessor::setRecursiveTextProcessor(
+ $this->recursiveTextProcessor
+ );
+
+ $params = [];
+
+ foreach ( $this->params as $key => $value) {
+ $params[$key] = $value->getValue();
+ }
+
+ $query->setOption( 'query.params', $params );
+
+ // Only request a result_hash in case the `check-query` is enabled
+ if ( $this->postProcHandler !== null ) {
+ $query->setOption( 'calc.result_hash', $this->postProcHandler->getOption( 'check-query' ) );
+ }
+
+ $result = QueryProcessor::getResultFromQuery(
+ $query,
+ $this->params,
+ SMW_OUTPUT_WIKI,
+ $this->context
+ );
+
+ if ( $this->postProcHandler !== null && $this->context !== QueryProcessor::DEFERRED_QUERY ) {
+ $this->postProcHandler->addCheck( $query );
+ }
+
+ $format = $this->params['format']->getValue();
+
+ if ( $this->recursiveTextProcessor !== null ) {
+ $this->recursiveTextProcessor->copyData( $this->parserData );
+ }
+
+ $this->circularReferenceGuard->unmark( $queryHash );
+ $this->expensiveFuncExecutionWatcher->incrementExpensiveCount( $query );
+
+ // In case of an query error add a marker to the subject for discoverability
+ // of a failed query, don't bail-out as we can have results and errors
+ // at the same time
+ $this->addProcessingError( $query->getErrors() );
+
+ $query->setOption( Query::PROC_STATUS_CODE, $status );
+
+ $this->addQueryProfile(
+ $query,
+ $format,
+ $extraKeys
+ );
+
+ return $result;
+ }
+
+ private function hasReachedExpensiveExecutionLimit( $query ) {
+
+ if ( $this->expensiveFuncExecutionWatcher->hasReachedExpensiveLimit( $query ) === false ) {
+ return false;
+ }
+
+ // Adding to error in order to be discoverable
+ $this->addProcessingError( [ 'smw-parser-function-expensive-execution-limit' ] );
+
+ return $this->messageFormatter->addFromKey( 'smw-parser-function-expensive-execution-limit' )->getHtml();
+ }
+
+ private function addQueryProfile( $query, $format, $extraKeys ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ // If the smwgQueryProfiler is marked with FALSE then just don't create a profile.
+ if ( $settings->get( 'smwgQueryProfiler' ) === false || $extraKeys[self::NO_TRACE] === true ) {
+ return;
+ }
+
+ if ( !$settings->isFlagSet( 'smwgQueryProfiler', SMW_QPRFL_DUR ) ) {
+ $query->setOption( Query::PROC_QUERY_TIME, 0 );
+ }
+
+ if ( $settings->isFlagSet( 'smwgQueryProfiler', SMW_QPRFL_PARAMS ) ) {
+ $query->setOption( Query::OPT_PARAMETERS, true );
+ }
+
+ $query->setContextPage(
+ $this->parserData->getSubject()
+ );
+
+ $profileAnnotatorFactory = $applicationFactory->getQueryFactory()->newProfileAnnotatorFactory();
+
+ $profileAnnotator = $profileAnnotatorFactory->newProfileAnnotator(
+ $query,
+ $format
+ );
+
+ $profileAnnotator->pushAnnotationsTo(
+ $this->parserData->getSemanticData()
+ );
+ }
+
+ private function addProcessingError( $errors ) {
+
+ if ( $errors === [] ) {
+ return;
+ }
+
+ $processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $this->parserData->getSubject()
+ );
+
+ foreach ( $errors as $error ) {
+
+ if ( ( $property = $processingErrorMsgHandler->grepPropertyFromRestrictionErrorMsg( $error ) ) === null ) {
+ $property = new DIProperty( '_ASK' );
+ }
+
+ $container = $processingErrorMsgHandler->newErrorContainerFromMsg(
+ $error,
+ $property
+ );
+
+ $processingErrorMsgHandler->addToSemanticData(
+ $this->parserData->getSemanticData(),
+ $container
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ConceptParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ConceptParserFunction.php
new file mode 100644
index 00000000..39acd316
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ConceptParserFunction.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Html;
+use Parser;
+use SMW\ApplicationFactory;
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\PostProcHandler;
+use SMWInfolink;
+use SMWQueryProcessor as QueryProcessor;
+use Title;
+
+/**
+ * Class that provides the {{#concept}} parser function
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class ConceptParserFunction {
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var MessageFormatter
+ */
+ private $messageFormatter;
+
+ /**
+ * @var PostProcHandler
+ */
+ private $postProcHandler;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param MessageFormatter $messageFormatter
+ */
+ public function __construct( ParserData $parserData, MessageFormatter $messageFormatter ) {
+ $this->parserData = $parserData;
+ $this->messageFormatter = $messageFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PostProcHandler $postProcHandler
+ */
+ public function setPostProcHandler( PostProcHandler $postProcHandler ) {
+ $this->postProcHandler = $postProcHandler;
+ }
+
+ /**
+ * Parse parameters, return concept information box and update the
+ * ParserOutput with the concept object
+ *
+ * @since 1.9
+ *
+ * @param array $params
+ *
+ * @return string|null
+ */
+ public function parse( array $rawParams ) {
+ $this->parserData->getOutput()->addModules( 'ext.smw.style' );
+
+ $title = $this->parserData->getTitle();
+ $property = new DIProperty( '_CONC' );
+
+ if ( !( $title->getNamespace() === SMW_NS_CONCEPT ) ) {
+ return $this->messageFormatter->addFromKey( 'smw_no_concept_namespace' )->getHtml();
+ } elseif ( count( $this->parserData->getSemanticData()->getPropertyValues( $property ) ) > 0 ) {
+ return $this->messageFormatter->addFromKey( 'smw_multiple_concepts' )->getHtml();
+ }
+
+ // Remove parser object from parameters array
+ if( isset( $rawParams[0] ) && $rawParams[0] instanceof Parser ) {
+ array_shift( $rawParams );
+ }
+
+ // Use first parameter as concept (query) string
+ $conceptQuery = array_shift( $rawParams );
+
+ // Use second parameter, if any as a description
+ $conceptDocu = array_shift( $rawParams );
+
+ $query = $this->buildQuery( $conceptQuery );
+
+ $conceptQueryString = $query->getDescription()->getQueryString();
+
+ $this->parserData->getSemanticData()->addPropertyObjectValue(
+ $property,
+ new DIConcept(
+ $conceptQueryString,
+ $conceptDocu,
+ $query->getDescription()->getQueryFeatures(),
+ $query->getDescription()->getSize(),
+ $query->getDescription()->getDepth()
+ )
+ );
+
+ $this->messageFormatter
+ ->addFromArray( $query->getErrors() )
+ ->addFromArray( $this->parserData->getErrors() );
+
+ if ( $this->postProcHandler !== null ) {
+ $this->postProcHandler->addCheck( $query );
+ }
+
+ $this->addQueryProfile( $query );
+
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ if ( $this->messageFormatter->exists() ) {
+ return $this->messageFormatter->getHtml();
+ }
+
+ return $this->createHtml( $title, $conceptQueryString, $conceptDocu );
+ }
+
+ private function createHtml( Title $title, $queryString, $documentation ) {
+
+ $message = '';
+
+ if ( wfMessage( 'smw-concept-introductory-message' )->exists() ) {
+ $message = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'plainlinks smw-callout smw-callout-info'
+ ],
+ wfMessage( 'smw-concept-introductory-message', $title->getText() )->text()
+ );
+ }
+
+ return $message . Html::rawElement( 'div', [ 'class' => 'smwfact' ],
+ Html::rawElement( 'span', [ 'class' => 'smwfactboxhead' ],
+ wfMessage( 'smw_concept_description', $title->getText() )->text() ) .
+ Html::rawElement( 'span', [ 'class' => 'smwrdflink' ], $this->getRdfLink( $title )->getWikiText() ) .
+ Html::element( 'br', [] ) .
+ Html::element( 'p', [ 'class' => 'concept-documenation' ], $documentation ? $documentation : '' ) .
+ Html::rawElement( 'pre', [], str_replace( '[', '&#91;', $queryString ) ) .
+ Html::element( 'br', [] )
+ );
+ }
+
+ private function getRdfLink( Title $title ) {
+ return SMWInfolink::newInternalLink(
+ wfMessage( 'smw_viewasrdf' )->text(),
+ $title->getPageLanguage()->getNsText( NS_SPECIAL ) . ':ExportRDF/' . $title->getPrefixedText(), 'rdflink'
+ );
+ }
+
+ private function buildQuery( $conceptQueryString ) {
+ $rawParams = [ $conceptQueryString ];
+
+ list( $query, ) = QueryProcessor::getQueryAndParamsFromFunctionParams(
+ $rawParams,
+ SMW_OUTPUT_WIKI,
+ QueryProcessor::CONCEPT_DESC,
+ false
+ );
+
+ return $query;
+ }
+
+ private function addQueryProfile( $query ) {
+
+ // If the smwgQueryProfiler is marked with FALSE then just don't create a profile.
+ if ( ApplicationFactory::getInstance()->getSettings()->get( 'smwgQueryProfiler' ) === false ) {
+ return;
+ }
+
+ $query->setContextPage(
+ $this->parserData->getSemanticData()->getSubject()
+ );
+
+ $profileAnnotatorFactory = ApplicationFactory::getInstance()->getQueryFactory()->newProfileAnnotatorFactory();
+
+ $descriptionProfileAnnotator = $profileAnnotatorFactory->newDescriptionProfileAnnotator(
+ $query
+ );
+
+ $descriptionProfileAnnotator->pushAnnotationsTo(
+ $this->parserData->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DeclareParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DeclareParserFunction.php
new file mode 100644
index 00000000..e0dc8cb2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DeclareParserFunction.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Parser;
+use PPFrame;
+use SMW\DataValueFactory;
+use SMW\ParserData;
+use SMWPropertyValue as PropertyValue;
+
+/**
+ * Class that provides the {{#declare}} parser function
+ *
+ * @see http://semantic-mediawiki.org/wiki/Help:Argument_declaration_in_templates
+ *
+ * @license GNU GPL v2+
+ * @since 1.5.3
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class DeclareParserFunction {
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @since 2.1
+ *
+ * @param ParserData $parserData
+ */
+ public function __construct( ParserData $parserData ) {
+ $this->parserData = $parserData;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param PPFrame $frame
+ * @param array $args
+ */
+ public function parse( PPFrame $frame, array $args ) {
+
+ // @todo Save as metadata
+ if ( !$frame->isTemplate() ) {
+ return '';
+ }
+
+ $this->subject = $this->parserData->getSemanticData()->getSubject();
+
+ foreach ( $args as $arg ) {
+ if ( trim( $arg ) !== '' ) {
+ $expanded = trim( $frame->expand( $arg ) );
+ $parts = explode( '=', $expanded, 2 );
+
+ if ( count( $parts ) == 1 ) {
+ $propertystring = $expanded;
+ $argumentname = $expanded;
+ } else {
+ $propertystring = $parts[0];
+ $argumentname = $parts[1];
+ }
+
+ $propertyValue = DataValueFactory::getInstance()->newPropertyValueByLabel( $propertystring );
+ $argument = $frame->getArgument( $argumentname );
+ $valuestring = $frame->expand( $argument );
+
+ if ( $propertyValue->isValid() ) {
+ $this->matchValueArgument( $propertyValue, $propertystring, $valuestring );
+ }
+ }
+ }
+
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ return '';
+ }
+
+ private function matchValueArgument( PropertyValue $propertyValue, $propertystring, $valuestring ) {
+
+ if ( $propertyValue->getPropertyTypeID() === '_wpg' ) {
+ $matches = [];
+ preg_match_all( '/\[\[([^\[\]]*)\]\]/u', $valuestring, $matches );
+ $objects = $matches[1];
+
+ if ( count( $objects ) == 0 ) {
+ if ( trim( $valuestring ) !== '' ) {
+ $this->addDataValue( $propertystring, $valuestring );
+ }
+ } else {
+ foreach ( $objects as $object ) {
+ $this->addDataValue( $propertystring, $object );
+ }
+ }
+ } elseif ( trim( $valuestring ) !== '' ) {
+ $this->addDataValue( $propertystring, $valuestring );
+ }
+
+ // $value = \SMW\DataValueFactory::getInstance()->newDataValueByProperty( $property->getDataItem(), $valuestring );
+ // if (!$value->isValid()) continue;
+ }
+
+ private function addDataValue( $property, $value ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $property,
+ $value,
+ false,
+ $this->subject
+ );
+
+ $this->parserData->addDataValue( $dataValue );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DocumentationParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DocumentationParserFunction.php
new file mode 100644
index 00000000..fa739b8a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/DocumentationParserFunction.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use ParamProcessor\ParamDefinition;
+use ParamProcessor\ProcessedParam;
+use ParamProcessor\ProcessingError;
+use ParamProcessor\ProcessingResult;
+use Parser;
+use ParserHooks\HookDefinition;
+use ParserHooks\HookHandler;
+use SMW\ParameterListDocBuilder;
+use SMWQueryProcessor as QueryProcessor;
+
+/**
+ * Class that provides the {{#smwdoc}} parser function, which displays parameter
+ * documentation for a specified result format.
+ *
+ * @ingroup ParserFunction
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DocumentationParserFunction implements HookHandler {
+
+ /**
+ * @var string
+ */
+ private $language = 'en';
+
+ /**
+ * @param Parser $parser
+ * @param ProcessingResult $result
+ *
+ * @return mixed
+ */
+ public function handle( Parser $parser, ProcessingResult $result ) {
+
+ if ( $result->hasFatal() ) {
+ return $this->getOutputForErrors( $result->getErrors() );
+ }
+
+ $parameters = $result->getParameters();
+ $format = $parameters['format']->getValue();
+
+ $formatParameters = QueryProcessor::getFormatParameters(
+ $format
+ );
+
+ $this->language = $parameters['language']->getValue();
+
+ if ( $formatParameters === [] ) {
+ return $this->msg( 'smw-smwdoc-default-no-parameter-list', $format );
+ }
+
+ return $this->buildParameterListDocumentation( $parameters, $formatParameters );
+ }
+
+ /**
+ * @return HookDefinition
+ */
+ public static function getHookDefinition() {
+ return new HookDefinition(
+ 'smwdoc',
+ [
+ [
+ 'name' => 'format',
+ 'message' => 'smw-smwdoc-par-format',
+ 'values' => array_keys( $GLOBALS['smwgResultFormats'] ),
+ ],
+ [
+ 'name' => 'language',
+ 'message' => 'smw-smwdoc-par-language',
+ 'default' => $GLOBALS['wgLanguageCode'],
+ ],
+ [
+ 'name' => 'parameters',
+ 'message' => 'smw-smwdoc-par-parameters',
+ 'values' => [ 'all', 'specific', 'base' ],
+ 'default' => 'specific',
+ ],
+ ],
+ [ 'format', 'language', 'parameters' ]
+ );
+ }
+
+ /**
+ * @param ProcessedParam[] $parameters
+ *
+ * @return string
+ */
+ private function buildParameterListDocumentation( array $parameters, $formatParameters ) {
+
+ if ( $parameters['parameters']->getValue() === 'specific' ) {
+ foreach ( array_keys( QueryProcessor::getParameters() ) as $name ) {
+ unset( $formatParameters[$name] );
+ }
+ } elseif ( $parameters['parameters']->getValue() === 'base' ) {
+ foreach ( array_diff_key( $formatParameters, QueryProcessor::getParameters() ) as $param ) {
+ unset( $formatParameters[$param->getName()] );
+ }
+ }
+
+ $docBuilder = new ParameterListDocBuilder(
+ [ $this, 'msg' ]
+ );
+
+ if ( ( $parameterTable = $docBuilder->getParameterTable( $formatParameters ) ) !== '' ) {
+ return $parameterTable;
+ }
+
+ return $this->msg( 'smw-smwdoc-default-no-parameter-list', $parameters['format']->getValue() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ...$args
+ *
+ * @return string
+ */
+ public function msg( ...$args ) {
+ return wfMessage( array_shift( $args ) )->params( $args )->useDatabase( true )->inLanguage( $this->language )->text();
+ }
+
+ /**
+ * @param ProcessingError[] $errors
+ * @return string
+ */
+ private function getOutputForErrors( $errors ) {
+ // TODO: see https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1485
+ return 'A fatal error occurred in the #smwdoc parser function';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ExpensiveFuncExecutionWatcher.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ExpensiveFuncExecutionWatcher.php
new file mode 100644
index 00000000..4fe50cec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ExpensiveFuncExecutionWatcher.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use SMW\ParserData;
+use SMWQuery as Query;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ExpensiveFuncExecutionWatcher {
+
+ /**
+ * Idenitifer
+ */
+ const EXPENSIVE_COUNTER = 'smw-expensiveparsercount';
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var integer
+ */
+ private $expensiveThreshold = 10;
+
+ /**
+ * @var integer|boolean
+ */
+ private $expensiveExecutionLimit = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param ParserData $parserData
+ */
+ public function __construct( ParserData $parserData ) {
+ $this->parserData = $parserData;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $expensiveThreshold
+ */
+ public function setExpensiveThreshold( $expensiveThreshold ) {
+ $this->expensiveThreshold = $expensiveThreshold;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer|boolean $expensiveExecutionLimit
+ */
+ public function setExpensiveExecutionLimit( $expensiveExecutionLimit ) {
+ $this->expensiveExecutionLimit = $expensiveExecutionLimit;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return boolean
+ */
+ public function hasReachedExpensiveLimit( Query $query ) {
+
+ if ( $this->expensiveExecutionLimit === false ) {
+ return false;
+ }
+
+ if ( $query->getLimit() == 0 ) {
+ return false;
+ }
+
+ if ( $this->parserData->getOutput()->getExtensionData( self::EXPENSIVE_COUNTER ) < $this->expensiveExecutionLimit ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return boolean
+ */
+ public function incrementExpensiveCount( Query $query ) {
+
+ if ( $this->expensiveExecutionLimit === false || $query->getLimit() == 0 || $query->getOption( Query::PROC_QUERY_TIME ) < $this->expensiveThreshold ) {
+ return;
+ }
+
+ $output = $this->parserData->getOutput();
+ $expensiveCount = $output->getExtensionData( self::EXPENSIVE_COUNTER );
+
+ if ( !is_int( $expensiveCount ) ) {
+ $expensiveCount = 0;
+ }
+
+ $expensiveCount++;
+ $output->setExtensionData( self::EXPENSIVE_COUNTER, $expensiveCount );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/InfoParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/InfoParserFunction.php
new file mode 100644
index 00000000..482007d9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/InfoParserFunction.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use ParamProcessor\ProcessingError;
+use ParamProcessor\ProcessingResult;
+use Parser;
+use ParserHooks\HookDefinition;
+use ParserHooks\HookHandler;
+use SMWOutputs;
+
+/**
+ * Class that provides the {{#info}} parser function
+ *
+ * @ingroup ParserFunction
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class InfoParserFunction implements HookHandler {
+
+ /**
+ * @param Parser $parser
+ * @param ProcessingResult $result
+ *
+ * @return mixed
+ */
+ public function handle( Parser $parser, ProcessingResult $result ) {
+ if ( $result->hasFatal() ) {
+ return $this->getOutputForErrors( $result->getErrors() );
+ }
+
+ $parameters = $result->getParameters();
+
+ if ( !isset( $parameters['message'] ) ) {
+ return '';
+ }
+
+ $message = $parser->mStripState ? $parser->mStripState->unstripBoth( $parameters[ 'message' ]->getValue() ) : $parameters[ 'message' ]->getValue();
+
+ if ( $message === '' ) {
+ return '';
+ }
+
+ /**
+ * Non-escaping is safe bacause a user's message is passed through parser, which will
+ * handle unsafe HTM elements.
+ */
+ $result = smwfEncodeMessages(
+ [ $message ],
+ $parameters['icon']->getValue(),
+ ' <!--br-->',
+ false // No escaping.
+ );
+
+ if ( !is_null( $parser->getTitle() ) && $parser->getTitle()->isSpecialPage() ) {
+ global $wgOut;
+ SMWOutputs::commitToOutputPage( $wgOut );
+ }
+ else {
+ SMWOutputs::commitToParser( $parser );
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param ProcessingError[] $errors
+ * @return string
+ */
+ private function getOutputForErrors( $errors ) {
+ // TODO: see https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1485
+ return 'A fatal error occurred in the #info parser function';
+ }
+
+ public static function getHookDefinition() {
+ return new HookDefinition(
+ 'info',
+ [
+ [
+ 'name' => 'message',
+ 'message' => 'smw-info-par-message',
+ ],
+ [
+ 'name' => 'icon',
+ 'message' => 'smw-info-par-icon',
+ 'default' => 'info',
+ 'values' => [ 'info', 'warning', 'error', 'note' ],
+ ],
+ ],
+ [
+ 'message',
+ 'icon'
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/RecurringEventsParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/RecurringEventsParserFunction.php
new file mode 100644
index 00000000..9a161e0c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/RecurringEventsParserFunction.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\ParserParameterProcessor;
+use SMW\RecurringEvents;
+use SMW\Subobject;
+
+/**
+ * @private This class should not be instantiated directly, please use
+ * ParserFunctionFactory::newRecurringEventsParserFunction
+ *
+ * Class that provides the {{#set_recurring_event}} parser function
+ *
+ * @see http://semantic-mediawiki.org/wiki/Help:Recurring_events
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RecurringEventsParserFunction extends SubobjectParserFunction {
+
+ /**
+ * @var RecurringEvents
+ */
+ private $recurringEvents;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param Subobject $subobject
+ * @param MessageFormatter $messageFormatter
+ * @param RecurringEvents $recurringEvents
+ */
+ public function __construct( ParserData $parserData, Subobject $subobject, MessageFormatter $messageFormatter, RecurringEvents $recurringEvents ) {
+ parent::__construct ( $parserData, $subobject, $messageFormatter );
+ $this->recurringEvents = $recurringEvents;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserParameterProcessor $parameters
+ *
+ * @return string|null
+ */
+ public function parse( ParserParameterProcessor $parameters ) {
+
+ $this->useFirstElementAsPropertyLabel( true );
+
+ $this->recurringEvents->parse(
+ $parameters->toArray()
+ );
+
+ $this->messageFormatter->addFromArray(
+ $this->recurringEvents->getErrors()
+ );
+
+ foreach ( $this->recurringEvents->getDates() as $date_str ) {
+
+ // Override existing parameters array with the returned
+ // pre-processed parameters array from recurring events
+ $parameters->setParameters( $this->recurringEvents->getParameters() );
+
+ // Add the date string as individual property / value parameter
+ $parameters->addParameter(
+ $this->recurringEvents->getProperty(),
+ $date_str
+ );
+
+ // @see SubobjectParserFunction::addDataValuesToSubobject
+ // Each new $parameters set will add an additional subobject
+ // to the instance
+ if ( $this->addDataValuesToSubobject( $parameters ) ) {
+ $this->parserData->getSemanticData()->addSubobject( $this->subobject );
+ }
+
+ // Collect errors that occurred during processing
+ $this->messageFormatter->addFromArray( $this->subobject->getErrors() );
+ }
+
+ // Update ParserOutput
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ $this->messageFormatter->addFromArray(
+ $this->parserData->getErrors()
+ );
+
+ return $this->messageFormatter->getHtml();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SectionTag.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SectionTag.php
new file mode 100644
index 00000000..f37aea24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SectionTag.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Parser;
+use PPFrame;
+use Html;
+
+/**
+ * To support the generation of <section> ... </section>
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SectionTag {
+
+ /**
+ * @var Parser
+ */
+ private $parser;
+
+ /**
+ * @var PPFrame
+ */
+ private $frame;
+
+ /**
+ * @since 3.0
+ *
+ * @param Parser $parser
+ * @param PPFrame $frame
+ */
+ public function __construct( Parser $parser, PPFrame $frame ) {
+ $this->parser = $parser;
+ $this->frame = $frame;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Parser $parser
+ * @param boolean $supportSectionTag
+ *
+ * @return boolean
+ */
+ public static function register( Parser $parser, $supportSectionTag = true ) {
+
+ if ( $supportSectionTag === false ) {
+ return false;
+ }
+
+ $parser->setHook( 'section', function( $input, array $args, Parser $parser, PPFrame $frame ) {
+ return ( new self( $parser, $frame ) )->parse( $input, $args );
+ } );
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $input
+ * @param array $args
+ *
+ * @return string
+ */
+ public function parse( $input, array $args ) {
+
+ $attributes = [];
+ $title = $this->parser->getTitle();
+
+ foreach( $args as $name => $value ) {
+ $value = htmlspecialchars( $value );
+
+ if ( $name === 'class' ) {
+ $attributes['class'] = $value;
+ }
+
+ if ( $name === 'id' ) {
+ $attributes['id'] = $value;
+ }
+ }
+
+ if ( $title !== null && $title->getNamespace() === SMW_NS_PROPERTY ) {
+ $attributes['class'] = ( isset( $attributes['class'] ) ? ' ' : '' ) . "smw-property-specification";
+ }
+
+ return Html::rawElement(
+ 'section',
+ $attributes,
+ $this->parser->recursiveTagParse( $input, $this->frame )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SetParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SetParserFunction.php
new file mode 100644
index 00000000..32e37896
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SetParserFunction.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Parser;
+use SMW\DataValueFactory;
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+use SMW\MediaWiki\StripMarkerDecoder;
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\ParserParameterProcessor;
+
+/**
+ * Class that provides the {{#set}} parser function
+ *
+ * @see http://semantic-mediawiki.org/wiki/Help:Properties_and_types#Silent_annotations_using_.23set
+ * @see http://www.semantic-mediawiki.org/wiki/Help:Setting_values
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class SetParserFunction {
+
+ /**
+ * @var ParserData
+ */
+ private $parserData;
+
+ /**
+ * @var MessageFormatter
+ */
+ private $messageFormatter;
+
+ /**
+ * @var WikitextTemplateRenderer
+ */
+ private $templateRenderer;
+
+ /**
+ * @var StripMarkerDecoder
+ */
+ private $stripMarkerDecoder;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param MessageFormatter $messageFormatter
+ * @param WikitextTemplateRenderer $templateRenderer
+ */
+ public function __construct( ParserData $parserData, MessageFormatter $messageFormatter, WikitextTemplateRenderer $templateRenderer ) {
+ $this->parserData = $parserData;
+ $this->messageFormatter = $messageFormatter;
+ $this->templateRenderer = $templateRenderer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param StripMarkerDecoder $stripMarkerDecoder
+ */
+ public function setStripMarkerDecoder( StripMarkerDecoder $stripMarkerDecoder ) {
+ $this->stripMarkerDecoder = $stripMarkerDecoder;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserParameterProcessor $parameters
+ *
+ * @return string|null
+ */
+ public function parse( ParserParameterProcessor $parameters ) {
+
+ $count = 0;
+ $template = '';
+ $subject = $this->parserData->getSemanticData()->getSubject();
+
+ $parametersToArray = $parameters->toArray();
+
+ if ( isset( $parametersToArray['template'] ) ) {
+ $template = $parametersToArray['template'][0];
+ unset( $parametersToArray['template'] );
+ }
+
+ foreach ( $parametersToArray as $property => $values ) {
+
+ $last = count( $values ) - 1; // -1 because the key starts with 0
+
+ foreach ( $values as $key => $value ) {
+
+ if ( $this->stripMarkerDecoder !== null ) {
+ $value = $this->stripMarkerDecoder->decode( $value );
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $property,
+ $value,
+ false,
+ $subject
+ );
+
+ if ( $this->parserData->canUse() ) {
+ $this->parserData->addDataValue( $dataValue );
+ }
+
+ $this->messageFormatter->addFromArray( $dataValue->getErrors() );
+
+ $this->addFieldsToTemplate(
+ $template,
+ $dataValue,
+ $property,
+ $value,
+ $last == $key,
+ $count
+ );
+ }
+ }
+
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ $html = $this->templateRenderer->render() . $this->messageFormatter
+ ->addFromArray( $parameters->getErrors() )
+ ->getHtml();
+
+ return [ $html, 'noparse' => $template === '', 'isHTML' => false ];
+ }
+
+ private function addFieldsToTemplate( $template, $dataValue, $property, $value, $isLastElement, &$count ) {
+
+ if ( $template === '' || !$dataValue->isValid() ) {
+ return '';
+ }
+
+ $this->templateRenderer->addField( 'property', $property );
+ $this->templateRenderer->addField( 'value', $value );
+ $this->templateRenderer->addField( 'last-element', $isLastElement );
+ $this->templateRenderer->addField( '#', $count++ );
+ $this->templateRenderer->packFieldsForTemplate( $template );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ShowParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ShowParserFunction.php
new file mode 100644
index 00000000..81d149f0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/ShowParserFunction.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+/**
+ * Class that provides the {{#show}} parser function
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ShowParserFunction {
+
+ /**
+ * @var AskParserFunction
+ */
+ private $askParserFunction;
+
+ /**
+ * @since 1.9
+ *
+ * @param AskParserFunction $askParserFunction
+ */
+ public function __construct( AskParserFunction $askParserFunction ) {
+ $this->askParserFunction = $askParserFunction;
+ }
+
+ /**
+ * Parse parameters, return results from the query printer and update the
+ * ParserOutput with meta data from the query
+ *
+ * @note The {{#show}} parser function internally uses the AskParserFunction
+ * and while an extra ShowParserFunction constructor is not really necessary
+ * it allows for separate unit testing
+ *
+ * @since 1.9
+ *
+ * @param array $params
+ *
+ * @return string|null
+ */
+ public function parse( array $rawParams ) {
+ $this->askParserFunction->setShowMode( true );
+ return $this->askParserFunction->parse( $rawParams );
+ }
+
+ /**
+ * Returns a message about inline queries being disabled
+ * @see $smwgQEnabled
+ *
+ * @since 1.9
+ *
+ * @return string|null
+ */
+ public function isQueryDisabled() {
+ return $this->askParserFunction->isQueryDisabled();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SubobjectParserFunction.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SubobjectParserFunction.php
new file mode 100644
index 00000000..c2bf1cd9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserFunctions/SubobjectParserFunction.php
@@ -0,0 +1,336 @@
+<?php
+
+namespace SMW\ParserFunctions;
+
+use Parser;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\HashBuilder;
+use SMW\MediaWiki\StripMarkerDecoder;
+use SMW\Message;
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\ParserParameterProcessor;
+use SMW\SemanticData;
+use SMW\Subobject;
+
+/**
+ * @private This class should not be instantiated directly, please use
+ * ParserFunctionFactory::newSubobjectParserFunction
+ *
+ * Provides the {{#subobject}} parser function
+ *
+ * @see http://www.semantic-mediawiki.org/wiki/Help:ParserFunction
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SubobjectParserFunction {
+
+ /**
+ * Fixed identifier that describes the sortkey annotation parameter
+ */
+ const PARAM_SORTKEY = '@sortkey';
+
+ /**
+ * Fixed identifier that describes a category parameter
+ *
+ * Those will not be visible by the "standard" category list as the handling
+ * of assigned categories is SMW specific for subobjects.
+ */
+ const PARAM_CATEGORY = '@category';
+
+ /**
+ * Fixed identifier that describes a property that can auto-linked the
+ * embeddedding subject
+ */
+ const PARAM_LINKWITH = '@linkWith';
+
+ /**
+ * @var ParserData
+ */
+ protected $parserData;
+
+ /**
+ * @var Subobject
+ */
+ protected $subobject;
+
+ /**
+ * @var MessageFormatter
+ */
+ protected $messageFormatter;
+
+ /**
+ * @var StripMarkerDecoder
+ */
+ private $stripMarkerDecoder;
+
+ /**
+ * @var boolean
+ */
+ private $useFirstElementAsPropertyLabel = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCapitalLinks = true;
+
+ /**
+ * @var boolean
+ */
+ private $isComparableContent = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserData $parserData
+ * @param Subobject $subobject
+ * @param MessageFormatter $messageFormatter
+ */
+ public function __construct( ParserData $parserData, Subobject $subobject, MessageFormatter $messageFormatter ) {
+ $this->parserData = $parserData;
+ $this->subobject = $subobject;
+ $this->messageFormatter = $messageFormatter;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param StripMarkerDecoder $stripMarkerDecoder
+ */
+ public function setStripMarkerDecoder( StripMarkerDecoder $stripMarkerDecoder ) {
+ $this->stripMarkerDecoder = $stripMarkerDecoder;
+ }
+
+ /**
+ * @see $wgCapitalLinks
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCapitalLinks
+ */
+ public function isCapitalLinks( $isCapitalLinks ) {
+ $this->isCapitalLinks = $isCapitalLinks;
+ }
+
+ /**
+ * Ensures that unordered parameters and property names are normalized and
+ * sorted to produce the same hash even if elements of the same literal
+ * representation are placed differently.
+ *
+ * @since 3.0
+ *
+ * @param boolean $isComparableContent
+ */
+ public function isComparableContent( $isComparableContent = true ) {
+ $this->isComparableContent = (bool)$isComparableContent;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param boolean $useFirstElementAsPropertyLabel
+ *
+ * @return SubobjectParserFunction
+ */
+ public function useFirstElementAsPropertyLabel( $useFirstElementAsPropertyLabel = true ) {
+ $this->useFirstElementAsPropertyLabel = (bool)$useFirstElementAsPropertyLabel;
+ return $this;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param ParserParameterProcessor $params
+ *
+ * @return string|null
+ */
+ public function parse( ParserParameterProcessor $parameters ) {
+
+ if (
+ $this->parserData->canUse() &&
+ $this->addDataValuesToSubobject( $parameters ) &&
+ $this->subobject->getSemanticData()->isEmpty() === false ) {
+ $this->parserData->getSemanticData()->addSubobject( $this->subobject );
+ }
+
+ $this->parserData->pushSemanticDataToParserOutput();
+
+ $html = $this->messageFormatter->addFromArray( $this->subobject->getErrors() )
+ ->addFromArray( $this->parserData->getErrors() )
+ ->addFromArray( $parameters->getErrors() )
+ ->getHtml();
+
+ // An empty output in MW forces an extra <br> element.
+ //if ( $html == '' ) {
+ // $html = '<p></p>';
+ //}
+
+ return $html;
+ }
+
+ protected function addDataValuesToSubobject( ParserParameterProcessor $parserParameterProcessor ) {
+
+ // Named subobjects containing a "." in the first five characters are
+ // reserved to be used by extensions only in order to separate them from
+ // user land and avoid having them accidentally to refer to the same
+ // named ID (i.e. different access restrictions etc.)
+ if ( strpos( mb_substr( $parserParameterProcessor->getFirst(), 0, 5 ), '.' ) !== false ) {
+ return $this->parserData->addError(
+ Message::encode( [ 'smw-subobject-parser-invalid-naming-scheme', $parserParameterProcessor->getFirst() ] )
+ );
+ }
+
+ list( $parameters, $id ) = $this->getParameters(
+ $parserParameterProcessor
+ );
+
+ $this->subobject->setEmptyContainerForId(
+ $id
+ );
+
+ $subject = $this->subobject->getSubject();
+
+ foreach ( $parameters as $property => $values ) {
+
+ if ( $property === self::PARAM_SORTKEY ) {
+ $property = DIProperty::TYPE_SORTKEY;
+ }
+
+ if ( $property === self::PARAM_CATEGORY ) {
+ $property = DIProperty::TYPE_CATEGORY;
+ }
+
+ foreach ( $values as $value ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $property,
+ $value,
+ false,
+ $subject
+ );
+
+ $this->subobject->addDataValue( $dataValue );
+ }
+ }
+
+ $this->augment( $this->subobject->getSemanticData() );
+
+ return true;
+ }
+
+ private function getParameters( ParserParameterProcessor $parserParameterProcessor ) {
+
+ $id = $parserParameterProcessor->getFirst();
+ $isAnonymous = in_array( $id, [ null, '' ,'-' ] );
+
+ $useFirst = $this->useFirstElementAsPropertyLabel && !$isAnonymous;
+
+ $parameters = $this->preprocess(
+ $parserParameterProcessor,
+ $useFirst
+ );
+
+ // FIXME remove the check with 3.1, should be standard by then!
+ if ( !$this->isComparableContent ) {
+ $p = $parameters;
+ } else {
+ $p = $parameters;
+ // Sort the copy not the parameters itself
+ $parserParameterProcessor->sort( $p );
+ }
+
+ // Reclaim the ID to be content hash based
+ if ( $useFirst || $isAnonymous ) {
+ $id = HashBuilder::createFromContent( $p, '_' );
+ }
+
+ return [ $parameters, $id ];
+ }
+
+ private function preprocess( ParserParameterProcessor $parserParameterProcessor, $useFirst ) {
+
+ if ( $parserParameterProcessor->hasParameter( self::PARAM_LINKWITH ) ) {
+ $val = $parserParameterProcessor->getParameterValuesByKey( self::PARAM_LINKWITH );
+ $parserParameterProcessor->addParameter(
+ end( $val ),
+ $this->parserData->getTitle()->getPrefixedText()
+ );
+
+ $parserParameterProcessor->removeParameterByKey( self::PARAM_LINKWITH );
+ }
+
+ if ( $useFirst ) {
+ $parserParameterProcessor->addParameter(
+ $parserParameterProcessor->getFirst(),
+ $this->parserData->getTitle()->getPrefixedText()
+ );
+ }
+
+ $parameters = $this->decode(
+ $parserParameterProcessor->toArray()
+ );
+
+ foreach ( $parameters as $property => $values ) {
+
+ $prop = $property;
+
+ // Normalize property names to generate the same hash for when
+ // CapitalLinks is enabled (has foo === Has foo)
+ if ( $property !== '' && $property{0} !== '@' && $this->isCapitalLinks ) {
+ $property = mb_strtoupper( mb_substr( $property, 0, 1 ) ) . mb_substr( $property, 1 );
+ }
+
+ unset( $parameters[$prop] );
+ $parameters[$property] = $values;
+ }
+
+ return $parameters;
+ }
+
+ private function decode( $parameters ) {
+
+ if ( $this->stripMarkerDecoder === null || !$this->stripMarkerDecoder->canUse() ) {
+ return $parameters;
+ }
+
+ // Any decoding has to happen before the subject ID is generated otherwise
+ // the value would contain something like `UNIQ--nowiki-00000011-QINU`
+ // and be part of the hash. `UNIQ--nowiki-00000011-QINU` isn't stable
+ // and changes to text will create new marker positions therefore it
+ // cannot be part of the hash computation
+ foreach ( $parameters as $property => &$values ) {
+ foreach ( $values as &$value ) {
+ $value = $this->stripMarkerDecoder->decode( $value );
+ }
+ }
+
+ return $parameters;
+ }
+
+ private function augment( $semanticData ) {
+
+ // Data block created by a user
+ $semanticData->setOption( SemanticData::PROC_USER, true );
+
+ $sortkey = new DIProperty( DIProperty::TYPE_SORTKEY );
+ $displayTitle = new DIProperty( DIProperty::TYPE_DISPLAYTITLE );
+
+ if ( $semanticData->hasProperty( $sortkey ) || !$semanticData->hasProperty( $displayTitle ) ) {
+ return null;
+ }
+
+ $pv = $semanticData->getPropertyValues(
+ $displayTitle
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $sortkey,
+ end( $pv )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserParameterProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserParameterProcessor.php
new file mode 100644
index 00000000..a6866225
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserParameterProcessor.php
@@ -0,0 +1,336 @@
+<?php
+
+namespace SMW;
+
+use SMW\Utils\ErrorCodeFormatter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserParameterProcessor {
+
+ /**
+ * @var string
+ */
+ private $defaultSeparator = ',';
+
+ /**
+ * @var array
+ */
+ private $rawParameters;
+
+ /**
+ * @var array
+ */
+ private $parameters;
+
+ /**
+ * @var null
+ */
+ private $first = null;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param array $rawParameters
+ */
+ public function __construct( array $rawParameters = [] ) {
+ $this->rawParameters = $rawParameters;
+ $this->parameters = $this->doMap( $rawParameters );
+ }
+
+ /**
+ * Returns collected errors
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * Adds an error
+ *
+ * @since 1.9
+ *
+ * @param mixed $error
+ */
+ public function addError( $error ) {
+ $this->errors = array_merge( (array)$error === $error ? $error : [ $error ], $this->errors );
+ }
+
+ /**
+ * @deprecated since 2.3, use ParserParameterProcessor::getFirstParameter
+ */
+ public function getFirst() {
+ return $this->getFirstParameter();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getFirstParameter() {
+ return $this->first;
+ }
+
+ /**
+ * Returns raw parameters
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getRaw() {
+ return $this->rawParameters;
+ }
+
+ /**
+ * Returns remapped parameters
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function toArray() {
+ return $this->parameters;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function hasParameter( $key ) {
+ return isset( $this->parameters[$key] ) || array_key_exists( $key, $this->parameters );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ */
+ public function removeParameterByKey( $key ) {
+ unset( $this->parameters[$key] );
+ }
+
+ /**
+ * @deprecated since 2.5, use ParserParameterProcessor::getParameterValuesByKey
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getParameterValuesFor( $key ) {
+ return $this->getParameterValuesByKey( $key );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ *
+ * @return array
+ */
+ public function getParameterValuesByKey( $key ) {
+
+ if ( $this->hasParameter( $key ) ) {
+ return $this->parameters[$key];
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array $parameters
+ */
+ public function setParameters( array $parameters ) {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function addParameter( $key, $value ) {
+ if( $key !== '' && $value !== '' ) {
+ $this->parameters[$key][] = $value;
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $key
+ * @param array $values
+ */
+ public function setParameter( $key, array $values ) {
+ if ( $key !== '' && $values !== [] ) {
+ $this->parameters[$key] = $values;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ * @param boolean $associative
+ */
+ public static function sort( array &$parameters, $associative = true ) {
+
+ // Associative vs. simple index array sort
+ if ( $associative ) {
+ ksort( $parameters );
+ } else {
+ sort( $parameters );
+ }
+
+ foreach ( $parameters as $key => &$value ) {
+ if ( is_array( $value ) ) {
+ self::sort( $value, is_int( $key ) );
+ }
+ }
+ }
+
+ /**
+ * Map raw parameters array into an 2n-array for simplified
+ * via [key] => [value1, value2]
+ */
+ private function doMap( array $params ) {
+ $results = [];
+ $previousProperty = null;
+
+ while ( key( $params ) !== null ) {
+
+ $pipe = false;
+ $values = [];
+
+ // Only strings are allowed for processing
+ if( !is_string( current ( $params ) ) ) {
+ next( $params );
+ }
+
+ // Get the current element and divide it into parts
+ $currentElement = explode( '=', trim( current ( $params ) ), 2 );
+
+ // Looking to the next element for comparison
+ $separator = $this->lookAheadOnNextElement( $params, $pipe );
+
+ // First named parameter
+ if ( count( $currentElement ) == 1 && $previousProperty === null ) {
+ $this->first = str_replace( ' ', '_', $currentElement[0] );
+ }
+
+ // Here we allow to support assignments of type |Has property=Test1|Test2|Test3
+ // for multiple values with the same preceding property
+ if ( count( $currentElement ) == 1 && $previousProperty !== null ) {
+ $currentElement[1] = $currentElement[0];
+ $currentElement[0] = $previousProperty;
+ } else {
+ $previousProperty = $currentElement[0];
+ }
+
+ // Reassign values
+ if ( $separator !== '' && isset( $currentElement[1] ) ) {
+ $values = explode( $separator, $currentElement[1] );
+ } elseif ( isset( $currentElement[1] ) ) {
+ $values[] = $currentElement[1];
+ }
+
+ // Remap properties and values to output a simple array
+ foreach ( $values as $value ) {
+ if ( $value !== '' ) {
+ $results[$currentElement[0]][] = trim( $value );
+ }
+ }
+
+ // +pipe indicates that elements are expected to be concatenated
+ // with a | that was removed during a #parserFunction invocation
+ if ( $pipe ) {
+ $results[$currentElement[0]] = [ implode( '|', $results[$currentElement[0]] ) ];
+ }
+ }
+
+ return $this->parseFromJson( $results );
+ }
+
+ private function lookAheadOnNextElement( &$params, &$pipe ) {
+
+ $separator = '';
+
+ if( !next( $params ) ) {
+ return $separator;
+ }
+
+ $nextElement = explode( '=', trim( current( $params ) ), 2 );
+
+ if ( $nextElement !== [] ) {
+ // This allows assignments of type |Has property=Test1,Test2|+sep=,
+ // as a means to support multiple value declaration
+ if ( substr( $nextElement[0], - 5 ) === '+sep' ) {
+ $separator = isset( $nextElement[1] ) ? $nextElement[1] !== '' ? $nextElement[1] : $this->defaultSeparator : $this->defaultSeparator;
+ next( $params );
+ }
+ }
+
+ if ( current( $params ) === '+pipe' ) {
+ $pipe = true;
+ next( $params );
+ }
+
+ return $separator;
+ }
+
+ private function parseFromJson( $results ) {
+
+ if ( !isset( $results['@json'] ) || !isset( $results['@json'][0] ) ) {
+ return $results;
+ }
+
+ // Restrict the depth to avoid resolving recursive assignment
+ // that can not be handled beyond the 2:n
+ $depth = 3;
+ $params = json_decode( $results['@json'][0], true, $depth );
+
+ if ( $params === null || json_last_error() !== JSON_ERROR_NONE ) {
+ $this->addError( Message::encode(
+ [
+ 'smw-parser-invalid-json-format',
+ ErrorCodeFormatter::getStringFromJsonErrorCode( json_last_error() )
+ ]
+ ) );
+ return $results;
+ }
+
+ array_walk( $params, function( &$value, $key ) {
+
+ if ( $value === '' ) {
+ $value = [];
+ }
+
+ if ( !is_array( $value ) ) {
+ $value = [ $value ];
+ }
+ } );
+
+ unset( $results['@json'] );
+ return array_merge( $results, $params );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PermissionPthValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/PermissionPthValidator.php
new file mode 100644
index 00000000..f8a5e005
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PermissionPthValidator.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW;
+
+use SMW\DataValues\AllowsPatternValue;
+use SMW\Protection\ProtectionValidator;
+use Title;
+use User;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PermissionPthValidator {
+
+ /**
+ * @var ProtectionValidator
+ */
+ private $protectionValidator;
+
+ /**
+ * @since 2.5
+ *
+ * @param ProtectionValidator $protectionValidator
+ */
+ public function __construct( ProtectionValidator $protectionValidator ) {
+ $this->protectionValidator = $protectionValidator;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title &$title
+ * @param User $user
+ * @param string $action
+ * @param array &$errors
+ *
+ * @return boolean
+ */
+ public function checkQuickPermission( Title &$title, User $user, $action, &$errors ) {
+ return $this->hasUserPermission( $title, $user, $action, $errors );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Title &$title
+ * @param User $user
+ * @param string $action
+ * @param array &$errors
+ *
+ * @return boolean
+ */
+ public function hasUserPermission( Title &$title, User $user, $action, &$errors ) {
+
+ if ( $title->getNamespace() === SMW_NS_SCHEMA ) {
+ return $this->checkSchemaNamespacePermission( $title, $user, $action, $errors );
+ }
+
+ if ( $action !== 'edit' && $action !== 'delete' && $action !== 'move' && $action !== 'upload' ) {
+ return true;
+ }
+
+ if ( $title->getNamespace() === NS_MEDIAWIKI ) {
+ return $this->checkMwNamespacePatternEditPermission( $title, $user, $action, $errors );
+ }
+
+ if ( $this->protectionValidator->getCreateProtectionRight() && $title->getNamespace() === SMW_NS_PROPERTY ) {
+ return $this->checkPropertyNamespaceCreatePermission( $title, $user, $action, $errors );
+ }
+
+ if ( $title->getNamespace() === NS_CATEGORY ) {
+ return $this->checkChangePropagationProtection( $title, $user, $action, $errors );
+ }
+
+ if ( !$title->exists() ) {
+ return true;
+ }
+
+ if ( $title->getNamespace() === SMW_NS_PROPERTY ) {
+ return $this->checkPropertyNamespaceEditPermission( $title, $user, $action, $errors );
+ }
+
+ if ( $this->protectionValidator->hasEditProtectionOnNamespace( $title ) ) {
+ return $this->checkEditPermissionOn( $title, $user, $action, $errors );
+ }
+
+ return true;
+ }
+
+ private function checkMwNamespacePatternEditPermission( Title &$title, User $user, $action, &$errors ) {
+
+ // @see https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_pattern
+ if ( $title->getDBKey() !== AllowsPatternValue::REFERENCE_PAGE_ID || $user->isAllowed( 'smw-patternedit' ) ) {
+ return true;
+ }
+
+ $errors[] = [ 'smw-patternedit-protection', 'smw-patternedit' ];
+
+ return false;
+ }
+
+ private function checkSchemaNamespacePermission( Title &$title, User $user, $action, &$errors ) {
+
+ if ( !$user->isAllowed( 'smw-schemaedit' ) ) {
+ $errors[] = [ 'smw-schema-namespace-edit-protection', 'smw-schemaedit' ];
+ return false;
+ }
+
+ // Disallow to change the content model
+ if ( $action === 'editcontentmodel' ) {
+ $errors[] = [ 'smw-schema-namespace-editcontentmodel-disallowed' ];
+ return false;
+ }
+
+ return true;
+ }
+
+ private function checkPropertyNamespaceCreatePermission( Title &$title, User $user, $action, &$errors ) {
+
+ $createProtectionRight = $this->protectionValidator->getCreateProtectionRight();
+
+ if ( $user->isAllowed( $createProtectionRight ) ) {
+ return $this->checkPropertyNamespaceEditPermission( $title, $user, $action, $errors );;
+ }
+
+ $msg = 'smw-create-protection';
+
+ if ( $title->exists() ) {
+ $msg = 'smw-create-protection-exists';
+ }
+
+ $errors[] = [ $msg, $title->getText(), $createProtectionRight ];
+
+ return false;
+ }
+
+ private function checkPropertyNamespaceEditPermission( Title &$title, User $user, $action, &$errors ) {
+
+ // This renders full protection until the ChangePropagationDispatchJob was run
+ if ( !$this->protectionValidator->hasChangePropagationProtection( $title ) ) {
+ return $this->checkEditPermissionOn( $title, $user, $action, $errors );
+ }
+
+ $errors[] = [ 'smw-change-propagation-protection' ];
+
+ return false;
+ }
+
+ private function checkChangePropagationProtection( Title &$title, User $user, $action, &$errors ) {
+
+ // This renders full protection until the ChangePropagationDispatchJob was run
+ if ( !$this->protectionValidator->hasChangePropagationProtection( $title ) ) {
+ return true;
+ }
+
+ $errors[] = [ 'smw-change-propagation-protection' ];
+
+ return false;
+ }
+
+ private function checkEditPermissionOn( Title &$title, User $user, $action, &$errors ) {
+
+ $editProtectionRight = $this->protectionValidator->getEditProtectionRight();
+
+ // @see https://www.semantic-mediawiki.org/wiki/Help:Special_property_Is_edit_protected
+ if ( !$this->protectionValidator->hasProtection( $title ) || $user->isAllowed( $editProtectionRight ) ) {
+ return true;
+ }
+
+ $errors[] = [ 'smw-edit-protection', $editProtectionRight ];
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PostProcHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/PostProcHandler.php
new file mode 100644
index 00000000..beb4a0e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PostProcHandler.php
@@ -0,0 +1,362 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use Onoi\Cache\Cache;
+use ParserOutput;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+use SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal;
+use SMWQuery as Query;
+use Title;
+use WebRequest;
+
+/**
+ * Some updates require to be handled in a "post" process meaning after an update
+ * has already taken place to iterate over those results as input for a value
+ * dependency.
+ *
+ * The post process can only happen after the Store and hereby related processes
+ * have been updated. A simple null edit is in most cases inappropriate and
+ * therefore it is necessary to a complete a re-parse (triggered by the UpdateJob)
+ * to ensure consistency among the stored and displayed data.
+ *
+ * The PostProc relies on an API request to initiate related updates and once
+ * finished will handle the reload of the page.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PostProcHandler {
+
+ /**
+ * Identifier on whether an update to subject is to be carried out or not
+ * given the query reference used as part of an @annotation request.
+ */
+ const POST_EDIT_UPDATE = 'smw-postedit-update';
+
+ /**
+ * Check registered queries and its results on wether the result_hash before
+ * and after is different or not.
+ */
+ const POST_EDIT_CHECK = 'smw-postedit-check';
+
+ /**
+ * Specifies the TTL for the temporary tracking of a post edit
+ * update.
+ */
+ const POST_UPDATE_TTL = 86400;
+
+ /**
+ * @var ParserOutput
+ */
+ private $parserOutput;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var boolean
+ */
+ private $isEnabled = true;
+
+ /**
+ * @var []
+ */
+ private $options = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ParserOutput $parserOutput
+ * @param Cache $cache
+ */
+ public function __construct( ParserOutput $parserOutput, Cache $cache ) {
+ $this->parserOutput = $parserOutput;
+ $this->cache = $cache;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isEnabled
+ */
+ public function isEnabled( $isEnabled ) {
+ $this->isEnabled = (bool)$isEnabled;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $options
+ */
+ public function setOptions( array $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = false ) {
+
+ if ( isset( $this->options[$key] ) ) {
+ return $this->options[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array|string
+ */
+ public function getModules() {
+ return 'ext.smw.postproc';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param WebRequest $webRequest
+ *
+ * @return string
+ */
+ public function getHtml( Title $title, WebRequest $webRequest ) {
+
+ if ( $this->isEnabled === false ) {
+ return '';
+ }
+
+ $subject = DIWikiPage::newFromTitle(
+ $title
+ );
+
+ $attributes = [
+ 'class' => 'smw-postproc',
+ 'data-subject' => $subject->getHash()
+ ];
+
+ // Ensure to detect the post edit process to distinguish between an edit
+ // event and any other post, get request in order to only sent a html
+ // fragment once on the edit request and avoid an infinite loop when the
+ // page is reloaded using an API request
+ // @see Article::view
+ $postEdit = $webRequest->getCookie(
+ \EditPage::POST_EDIT_COOKIE_KEY_PREFIX . $title->getLatestRevID()
+ );
+
+ $jobs = [];
+
+ if ( $postEdit !== null && isset( $this->options['run-jobs'] ) ) {
+ $jobs = $this->find_jobs( $this->options['run-jobs'] );
+ }
+
+ if ( $jobs !== [] ) {
+ $attributes['data-jobs'] = json_encode( $jobs );
+ }
+
+ // Was the edit SMW specific or contains it an unrelated (e.g altered
+ // some text unrelated to any property/value annotation) change?
+ if ( $postEdit !== null && ( $changeDiff = ChangeDiff::fetch( $this->cache, $subject ) ) !== false ) {
+ $postEdit = $this->checkDiff( $changeDiff );
+ }
+
+ // Is `@annotation` available as part of a #ask query?
+ $refs = $this->parserOutput->getExtensionData( self::POST_EDIT_UPDATE );
+
+ if ( $refs !== null && $refs !== [] ) {
+ $postEdit = $this->checkRef( $title, $postEdit );
+ }
+
+ if ( $postEdit !== null && $refs !== null && $refs !== [] ) {
+ $attributes['data-ref'] = json_encode( array_keys( $refs ) );
+ }
+
+ if (
+ $postEdit !== null &&
+ isset( $this->options['check-query'] ) &&
+ ( $queries = $this->parserOutput->getExtensionData( self::POST_EDIT_CHECK ) ) !== null ) {
+ $attributes['data-query'] = json_encode( $queries );
+ }
+
+ // The element is only added temporarily in the event of a postEdit, a
+ // reload of the page will not have the cookie being set and is therefore
+ // neglected
+ if ( $postEdit !== null || $jobs !== [] ) {
+ return Html::rawElement( 'div', $attributes );
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ */
+ public function addUpdate( Query $query ) {
+
+ // Query:getHash returns a hash based on a fingerprint
+ // (when $smwgQueryResultCacheType is set) that eliminates duplicate
+ // queries, yet for the post processing it is necessary to know each
+ // single query (same-condition, different printout) to allow running
+ // alternating updates as in case of cascading value dependencies
+ $queryRef = HashBuilder::createFromArray( $query->toArray() );
+
+ $data = $this->parserOutput->getExtensionData( self::POST_EDIT_UPDATE );
+
+ if ( $data === null ) {
+ $data = [];
+ }
+
+ $data[$queryRef] = true;
+
+ $this->parserOutput->setExtensionData(
+ self::POST_EDIT_UPDATE,
+ $data
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ */
+ public function addCheck( Query $query ) {
+
+ if ( !isset( $this->options['check-query'] ) || $this->options['check-query'] === false ) {
+ return;
+ }
+
+ $q_array = $query->toArray();
+
+ // Build a concatenated hash from the query and the result_hash
+ $hash = md5( json_encode( $q_array ) ) . '#';
+ $data = $this->parserOutput->getExtensionData( self::POST_EDIT_CHECK );
+
+ if ( $data === null ) {
+ $data = [];
+ }
+
+ // Use the result hash to determine whether results differ during the
+ // post-edit examination when running the same query
+ if ( $query->getOption( 'result_hash' ) ) {
+ $hash .= $query->getOption( 'result_hash' );
+ }
+
+ $data[$hash] = $q_array;
+
+ $this->parserOutput->setExtensionData(
+ self::POST_EDIT_CHECK,
+ $data
+ );
+ }
+
+ private function checkRef( $title, $postEdit ) {
+
+ $key = DependencyLinksUpdateJournal::makeKey( $title );
+
+ // Is a postEdit, mark the update to avoid running in circles
+ // when the pageCache is purged, use the latestRevID to distinguish
+ // content changes
+ if ( $postEdit !== null ) {
+
+ $record = [
+ $title->getLatestRevID() => true
+ ];
+
+ $this->cache->save( $key . ':post', $record, self::POST_UPDATE_TTL );
+
+ return $postEdit;
+ }
+
+ // Run outside of a postEdit, check if the dependency journal contains an
+ // active reference to the article and run once (== hash that set by the
+ // dependency journal which is == revID that initiated the change)
+ $hash = $this->cache->fetch( $key );
+ $record = $this->cache->fetch( $key . ':post' );
+
+ if ( $hash !== false && ( $record === false || !isset( $record[$hash] ) ) ) {
+ $postEdit = true;
+
+ if ( !is_array( $record ) ) {
+ $record = [];
+ }
+
+ $record[$hash] = true;
+
+ // Add an update marker (1h) to avoid running twice in case the
+ // journal reference hasn't been deleted yet as result of an existing
+ // PostProcHandler update request.
+ $this->cache->save( $key . ':post', $record, self::POST_UPDATE_TTL );
+ }
+
+ return $postEdit;
+ }
+
+ private function checkDiff( $changeDiff ) {
+
+ $propertyList = $changeDiff->getPropertyList(
+ 'flip'
+ );
+
+ // Investigate whether the changeDiff contains a user invoked modification
+ // and if so, allow the postEdit process to continue in order to act
+ // on SMW data and not on text that doesn't involve changes to a property
+ // value pair.
+ foreach ( $changeDiff->getTableChangeOps() as $tableChangeOp ) {
+ foreach ( $tableChangeOp->getFieldChangeOps() as $fieldChangeOp ) {
+ $pid = $fieldChangeOp->get( 'p_id' );
+
+ if ( !isset( $propertyList[$pid] ) ) {
+ continue;
+ }
+
+ // Does the change involve an operation with a user defined
+ // property?
+ //
+ // Some data were altered but since we cannot (within the request
+ // framework and without further computation) anticipate whether
+ // this influences a query or not, it is a good enough heuristic
+ // to allow to continue the postProc.
+ if ( $propertyList[$pid]{0} !== '_' ) {
+ return true;
+ }
+
+ if ( $propertyList[$pid] === '_INST' || $propertyList[$pid] === '_ASK' ) {
+ return true;
+ }
+ }
+ }
+
+ // Avoid any update since the condition of the diff containing any altered
+ // SMW data was not meet.
+ return null;
+ }
+
+ private function find_jobs( $jobs ) {
+
+ // Not enabled, no need to invoke a job!
+ if ( isset( $this->options['smwgEnabledQueryDependencyLinksStore'] ) && $this->options['smwgEnabledQueryDependencyLinksStore'] === false ) {
+ unset( $jobs['smw.parserCachePurge'] );
+ }
+
+ if ( isset( $this->options['smwgEnabledFulltextSearch'] ) && $this->options['smwgEnabledFulltextSearch'] === false ) {
+ unset( $jobs['smw.fulltextSearchTableUpdate'] );
+ }
+
+ return $jobs;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ProcessingErrorMsgHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/ProcessingErrorMsgHandler.php
new file mode 100644
index 00000000..eb1edfa2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/ProcessingErrorMsgHandler.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace SMW;
+
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDataValue as DataValue;
+use SMWDIBlob as DIBlob;
+use SMWDIContainer as DIContainer;
+
+/**
+ * The handler encodes errors into a representation that can be retrieved from
+ * the back-end and turn it into a string representation at a convenient time.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ProcessingErrorMsgHandler {
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ */
+ public function __construct( DIWikiPage $subject ) {
+ $this->subject = $subject;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $message
+ *
+ * @return DIProperty|null
+ */
+ public static function grepPropertyFromRestrictionErrorMsg( $message ) {
+ return PropertyRestrictionExaminer::grepPropertyFromRestrictionErrorMsg( $message );
+ }
+
+ /**
+ * Turns an encoded array of messages or text elements into a compacted array
+ * with msg keys and arguments.
+ *
+ * @since 2.5
+ *
+ * @param array $messages
+ * @param integer|null $type
+ * @param integer|null $language
+ *
+ * @return array
+ */
+ public static function normalizeAndDecodeMessages( array $messages, $type = null, $language = null ) {
+
+ $normalizedMessages = [];
+
+ if ( $type === null ) {
+ $type = Message::TEXT;
+ }
+
+ if ( $language === null ) {
+ $language = Message::USER_LANGUAGE;
+ }
+
+ foreach ( $messages as $message ) {
+
+ if ( is_array( $message ) ) {
+ foreach ( self::normalizeAndDecodeMessages( $message ) as $msg ) {
+ if ( is_string( $msg ) ) {
+ $normalizedMessages[md5($msg)] = $msg;
+ } else {
+ $normalizedMessages[] = $msg;
+ }
+ }
+ continue;
+ }
+
+ $exists = false;
+
+ if ( is_string( $message ) && ( $decodedMessage = Message::decode( $message, $type, $language ) ) !== false ) {
+ $message = $decodedMessage;
+ $exists = true;
+ }
+
+ if ( !$exists && is_string( $message ) && wfMessage( $message )->exists() ) {
+ $message = Message::get( $message, $type, $language );
+ }
+
+ if ( is_string( $message ) ) {
+ $normalizedMessages[md5($message)] = $message;
+ } else {
+ $normalizedMessages[] = $message;
+ }
+ }
+
+ return array_values( $normalizedMessages );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $messages
+ * @param integer|null $type
+ * @param integer|null $language
+ *
+ * @return string
+ */
+ public static function getMessagesAsString( array $messages, $type = null, $language = null ) {
+
+ $normalizedMessages = self::normalizeAndDecodeMessages( $messages, $type, $language );
+ $msg = [];
+
+ foreach ( $normalizedMessages as $message ) {
+
+ if ( !is_string( $message ) ) {
+ continue;
+ }
+
+ $msg[] = $message;
+ }
+
+ return implode( ',', $msg );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ * @param DIContainer|null $container
+ */
+ public function addToSemanticData( SemanticData $semanticData, DIContainer $container = null ) {
+
+ if ( $container === null ) {
+ return;
+ }
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_ERRC' ),
+ $container
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array|string $errorMsg
+ * @param DIProperty|null $property
+ *
+ * @return DIContainer
+ */
+ public function newErrorContainerFromMsg( $error, DIProperty $property = null ) {
+
+ if ( $property !== null && $property->isInverse() ) {
+ $property = new DIProperty( $property->getKey() );
+ }
+
+ $error = Message::encode( $error );
+ $hash = $error;
+
+ if ( $property !== null ) {
+ $hash .= $property->getKey();
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData( $hash );
+
+ $this->addToContainerSemanticData(
+ $containerSemanticData,
+ $property,
+ $error
+ );
+
+ return new DIContainer( $containerSemanticData );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValue $dataValue
+ *
+ * @return DIContainer|null
+ */
+ public function newErrorContainerFromDataValue( DataValue $dataValue ) {
+
+ if ( $dataValue->getErrors() === [] ) {
+ return null;
+ }
+
+ $property = $dataValue->getProperty();
+
+ if ( $property !== null ) {
+ $hash = $property->getKey();
+ } else {
+ $hash = $dataValue->getDataItem()->getHash();
+ }
+
+ $containerSemanticData = $this->newContainerSemanticData( $hash );
+
+ foreach ( $dataValue->getErrors() as $error ) {
+ $this->addToContainerSemanticData( $containerSemanticData, $property, Message::encode( $error ) );
+ }
+
+ return new DIContainer( $containerSemanticData );
+ }
+
+ private function addToContainerSemanticData( $containerSemanticData, $property, $error ) {
+
+ if ( $property !== null ) {
+ $containerSemanticData->addPropertyObjectValue(
+ new DIProperty( '_ERRP' ),
+ new DIWikiPage( $property->getKey(), SMW_NS_PROPERTY )
+ );
+ }
+
+ $containerSemanticData->addPropertyObjectValue(
+ new DIProperty( '_ERRT' ),
+ new DIBlob( $error )
+ );
+ }
+
+ private function newContainerSemanticData( $hash ) {
+
+ if ( $this->subject === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ $containerSemanticData->skipAnonymousCheck();
+ } else {
+ $subobjectName = '_ERR' . md5( $hash );
+
+ $subject = new DIWikiPage(
+ $this->subject->getDBkey(),
+ $this->subject->getNamespace(),
+ $this->subject->getInterwiki(),
+ $subobjectName
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return $containerSemanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAliasFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAliasFinder.php
new file mode 100644
index 00000000..d43c057d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAliasFinder.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace SMW;
+
+use Onoi\Cache\Cache;
+
+/**
+ * @license GNU GPL v2
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyAliasFinder {
+
+ /**
+ * Identifies the cache namespace
+ */
+ const CACHE_NAMESPACE = 'smw:property:alias';
+
+ /**
+ * Identifies the cache TTL (one week)
+ */
+ const CACHE_TTL = 604800;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * Array with entries "property alias" => "property id"
+ *
+ * @var string[]
+ */
+ private $propertyAliases = [];
+
+ /**
+ * @var string[]
+ */
+ private $propertyAliasesByMsgKey = [];
+
+ /**
+ * @var string[]
+ */
+ private $canonicalPropertyAliases = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param Cache $cache
+ * @param array $propertyAliases
+ * @param array $canonicalPropertyAliases
+ */
+ public function __construct( Cache $cache, array $propertyAliases = [], array $canonicalPropertyAliases = [] ) {
+ $this->cache = $cache;
+ $this->canonicalPropertyAliases = $canonicalPropertyAliases;
+
+ foreach ( $propertyAliases as $alias => $id ) {
+ $this->registerAliasByFixedLabel( $id, $alias );
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getKnownPropertyAliases() {
+ return $this->propertyAliases;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getKnownPropertyAliasesWithMsgKey() {
+ return $this->propertyAliasesByMsgKey;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $languageCode
+ *
+ * @return array
+ */
+ public function getKnownPropertyAliasesByLanguageCode( $languageCode = 'en' ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ $languageCode,
+ $this->propertyAliasesByMsgKey
+ ]
+ );
+
+ if ( ( $propertyAliases = $this->cache->fetch( $key ) ) !== false ) {
+ return $propertyAliases;
+ }
+
+ $propertyAliases = [];
+
+ foreach ( $this->propertyAliasesByMsgKey as $msgKey => $id ) {
+ $propertyAliases[Message::get( $msgKey, Message::TEXT, $languageCode )] = $id;
+ }
+
+ $this->cache->save( $key, $propertyAliases, self::CACHE_TTL );
+
+ return $propertyAliases;
+ }
+
+ /**
+ * Add a new alias label to an existing property ID. Note that every ID
+ * should have a primary label.
+ *
+ * @param string $id string
+ * @param string $label
+ */
+ public function registerAliasByFixedLabel( $id, $label ) {
+
+ // Prevent an extension to register an already known
+ // label
+ if ( isset( $this->canonicalPropertyAliases[$label] ) && $this->canonicalPropertyAliases[$label] !== $id ) {
+ return;
+ }
+
+ // Indicates an untranslated MW message key
+ if ( $label !== '' && $label{0} === '<' ) {
+ return null;
+ }
+
+ $this->propertyAliases[$label] = $id;
+ }
+
+ /**
+ * Register an alias using a message key to allow fetching localized
+ * labels dynamically.
+ *
+ * @since 2.4
+ *
+ * @param string $id
+ * @param string $msgKey
+ */
+ public function registerAliasByMsgKey( $id, $msgKey ) {
+ $this->propertyAliasesByMsgKey[$msgKey] = $id;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ *
+ * @return string|boolean
+ */
+ public function findCanonicalPropertyAliasById( $id ) {
+ return array_search( $id, $this->canonicalPropertyAliases );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ *
+ * @return string|boolean
+ */
+ public function findPropertyAliasById( $id ) {
+ return array_search( $id, $this->propertyAliases );
+ }
+
+ /**
+ * Find and return the ID for the pre-defined property of the given
+ * local label. If the label does not belong to a pre-defined property,
+ * return false.
+ *
+ * @param string $alias
+ *
+ * @return string|boolean
+ */
+ public function findPropertyIdByAlias( $alias ) {
+
+ if ( isset( $this->propertyAliases[$alias] ) ) {
+ return $this->propertyAliases[$alias];
+ } elseif ( isset( $this->canonicalPropertyAliases[$alias] ) ) {
+ return $this->canonicalPropertyAliases[$alias];
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotator.php
new file mode 100644
index 00000000..045f8959
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotator.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Interface specifying available methods to interact with the Decorator
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+interface PropertyAnnotator {
+
+ /**
+ * Returns a SemanticData container
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData();
+
+ /**
+ * Add annotations to the SemanticData container
+ *
+ * @since 1.9
+ *
+ * @return PropertyAnnotator
+ */
+ public function addAnnotation();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotatorFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotatorFactory.php
new file mode 100644
index 00000000..ae19b43c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotatorFactory.php
@@ -0,0 +1,226 @@
+<?php
+
+namespace SMW;
+
+use SMw\MediaWiki\RedirectTargetFinder;
+use SMW\PropertyAnnotators\CategoryPropertyAnnotator;
+use SMW\PropertyAnnotators\DisplayTitlePropertyAnnotator;
+use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
+use SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\PredefinedPropertyAnnotator;
+use SMW\PropertyAnnotators\RedirectPropertyAnnotator;
+use SMW\PropertyAnnotators\SchemaPropertyAnnotator;
+use SMW\PropertyAnnotators\SortKeyPropertyAnnotator;
+use SMW\PropertyAnnotators\TranslationPropertyAnnotator;
+use SMW\Store;
+use SMW\Schema\Schema;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PropertyAnnotatorFactory {
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return NullPropertyAnnotator
+ */
+ public function newNullPropertyAnnotator( SemanticData $semanticData ) {
+ return new NullPropertyAnnotator( $semanticData );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ * @param RedirectTargetFinder $redirectTargetFinder
+ *
+ * @return RedirectPropertyAnnotator
+ */
+ public function newRedirectPropertyAnnotator( PropertyAnnotator $propertyAnnotator, RedirectTargetFinder $redirectTargetFinder ) {
+ return new RedirectPropertyAnnotator(
+ $propertyAnnotator,
+ $redirectTargetFinder
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param Schema $schema
+ *
+ * @return SchemaPropertyAnnotator
+ */
+ public function newSchemaPropertyAnnotator( PropertyAnnotator $propertyAnnotator, Schema $schema = null ) {
+
+ $schemaPropertyAnnotator = new SchemaPropertyAnnotator(
+ $propertyAnnotator,
+ $schema
+ );
+
+ return $schemaPropertyAnnotator;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ * @param PageInfo $pageInfo
+ *
+ * @return PredefinedPropertyAnnotator
+ */
+ public function newPredefinedPropertyAnnotator( PropertyAnnotator $propertyAnnotator, PageInfo $pageInfo ) {
+
+ $predefinedPropertyAnnotator = new PredefinedPropertyAnnotator(
+ $propertyAnnotator,
+ $pageInfo
+ );
+
+ $predefinedPropertyAnnotator->setPredefinedPropertyList(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgPageSpecialProperties' )
+ );
+
+ return $predefinedPropertyAnnotator;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ * @param Title $title
+ *
+ * @return EditProtectedPropertyAnnotator
+ */
+ public function newEditProtectedPropertyAnnotator( PropertyAnnotator $propertyAnnotator, Title $title ) {
+
+ $editProtectedPropertyAnnotator = new EditProtectedPropertyAnnotator(
+ $propertyAnnotator,
+ $title
+ );
+
+ $editProtectedPropertyAnnotator->setEditProtectionRight(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgEditProtectionRight' )
+ );
+
+ return $editProtectedPropertyAnnotator;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ * @param string $sortkey
+ *
+ * @return SortKeyPropertyAnnotator
+ */
+ public function newSortKeyPropertyAnnotator( PropertyAnnotator $propertyAnnotator, $sortkey ) {
+ return new SortKeyPropertyAnnotator(
+ $propertyAnnotator,
+ $sortkey
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param SemanticData $semanticData
+ * @param arrat|null $translation
+ *
+ * @return TranslationPropertyAnnotator
+ */
+ public function newTranslationPropertyAnnotator( PropertyAnnotator $propertyAnnotator, $translation ) {
+
+ $translationPropertyAnnotator = new TranslationPropertyAnnotator(
+ $propertyAnnotator,
+ $translation
+ );
+
+ $translationPropertyAnnotator->setPredefinedPropertyList(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgPageSpecialProperties' )
+ );
+
+ return $translationPropertyAnnotator;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param SemanticData $semanticData
+ * @param string|false $displayTitle
+ * @param string $defaultSort
+ *
+ * @return DisplayTitlePropertyAnnotator
+ */
+ public function newDisplayTitlePropertyAnnotator( PropertyAnnotator $propertyAnnotator, $displayTitle, $defaultSort ) {
+
+ $displayTitlePropertyAnnotator = new DisplayTitlePropertyAnnotator(
+ $propertyAnnotator,
+ $displayTitle,
+ $defaultSort
+ );
+
+ $displayTitlePropertyAnnotator->canCreateAnnotation(
+ ( ApplicationFactory::getInstance()->getSettings()->get( 'smwgDVFeatures' ) & SMW_DV_WPV_DTITLE ) != 0
+ );
+
+ return $displayTitlePropertyAnnotator;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ * @param array $categories
+ *
+ * @return CategoryPropertyAnnotator
+ */
+ public function newCategoryPropertyAnnotator( PropertyAnnotator $propertyAnnotator, array $categories ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $categoryPropertyAnnotator = new CategoryPropertyAnnotator(
+ $propertyAnnotator,
+ $categories
+ );
+
+ $categoryPropertyAnnotator->showHiddenCategories(
+ $settings->isFlagSet( 'smwgParserFeatures', SMW_PARSER_HID_CATS )
+ );
+
+ $categoryPropertyAnnotator->useCategoryInstance(
+ $settings->isFlagSet( 'smwgCategoryFeatures', SMW_CAT_INSTANCE )
+ );
+
+ $categoryPropertyAnnotator->useCategoryHierarchy(
+ $settings->isFlagSet( 'smwgCategoryFeatures', SMW_CAT_HIERARCHY )
+ );
+
+ $categoryPropertyAnnotator->useCategoryRedirect(
+ $settings->isFlagSet( 'smwgCategoryFeatures', SMW_CAT_REDIRECT )
+ );
+
+ return $categoryPropertyAnnotator;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return MandatoryTypePropertyAnnotator
+ */
+ public function newMandatoryTypePropertyAnnotator( PropertyAnnotator $propertyAnnotator ) {
+ return new MandatoryTypePropertyAnnotator(
+ $propertyAnnotator
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/CategoryPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/CategoryPropertyAnnotator.php
new file mode 100644
index 00000000..2b2201f3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/CategoryPropertyAnnotator.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\ProcessingErrorMsgHandler;
+use SMW\PropertyAnnotator;
+
+/**
+ * Handling category annotation
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class CategoryPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var array
+ */
+ private $categories;
+
+ /**
+ * @var array|null
+ */
+ private $hiddenCategories = null;
+
+ /**
+ * @var boolean
+ */
+ private $showHiddenCategories = true;
+
+ /**
+ * @var boolean
+ */
+ private $useCategoryInstance = true;
+
+ /**
+ * @var boolean
+ */
+ private $useCategoryHierarchy = true;
+
+ /**
+ * @var boolean
+ */
+ private $useCategoryRedirect = true;
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param array $categories
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, array $categories ) {
+ parent::__construct( $propertyAnnotator );
+ $this->categories = $categories;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param boolean $showHiddenCategories
+ */
+ public function showHiddenCategories( $showHiddenCategories ) {
+ $this->showHiddenCategories = (bool)$showHiddenCategories;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param boolean $useCategoryInstance
+ */
+ public function useCategoryInstance( $useCategoryInstance ) {
+ $this->useCategoryInstance = (bool)$useCategoryInstance;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param boolean $useCategoryHierarchy
+ */
+ public function useCategoryHierarchy( $useCategoryHierarchy ) {
+ $this->useCategoryHierarchy = (bool)$useCategoryHierarchy;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $useCategoryRedirect
+ */
+ public function useCategoryRedirect( $useCategoryRedirect ) {
+ $this->useCategoryRedirect = (bool)$useCategoryRedirect;
+ }
+
+ /**
+ * @see PropertyAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+
+ $namespace = $this->getSemanticData()->getSubject()->getNamespace();
+ $property = null;
+
+ $this->processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $this->getSemanticData()->getSubject()
+ );
+
+ if ( $this->useCategoryInstance && ( $namespace !== NS_CATEGORY ) ) {
+ $property = new DIProperty( DIProperty::TYPE_CATEGORY );
+ }
+
+ if ( $this->useCategoryHierarchy && ( $namespace === NS_CATEGORY ) ) {
+ $property = new DIProperty( DIProperty::TYPE_SUBCATEGORY );
+ }
+
+ foreach ( $this->categories as $catname ) {
+
+ if ( ( !$this->showHiddenCategories && $this->isHiddenCategory( $catname ) ) || $property === null ) {
+ continue;
+ }
+
+ $this->modifySemanticData( $property, $catname );
+ }
+ }
+
+ private function modifySemanticData( $property, $catname ) {
+
+ $cat = new DIWikiPage( $catname, NS_CATEGORY );
+
+ if ( ( $cat = $this->getRedirectTarget( $cat ) ) && $cat->getNamespace() === NS_CATEGORY ) {
+ return $this->getSemanticData()->addPropertyObjectValue(
+ $property,
+ $cat
+ );
+ }
+
+ $container = $this->processingErrorMsgHandler->newErrorContainerFromMsg(
+ [
+ 'smw-category-invalid-redirect-target',
+ str_replace( '_', ' ', $catname )
+ ]
+ );
+
+ $this->processingErrorMsgHandler->addToSemanticData(
+ $this->getSemanticData(),
+ $container
+ );
+ }
+
+ private function isHiddenCategory( $catName ) {
+
+ if ( $this->hiddenCategories === null ) {
+
+ $wikipage = ApplicationFactory::getInstance()->newPageCreator()->createPage(
+ $this->getSemanticData()->getSubject()->getTitle()
+ );
+
+ $this->hiddenCategories = $wikipage->getHiddenCategories();
+ }
+
+ foreach ( $this->hiddenCategories as $hiddenCategory ) {
+
+ if ( $hiddenCategory->getText() === $catName ) {
+ return true;
+ };
+
+ }
+
+ return false;
+ }
+
+ private function getRedirectTarget( $subject ) {
+
+ if ( $this->useCategoryRedirect ) {
+ return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $subject );
+ }
+
+ return $subject;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/DisplayTitlePropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/DisplayTitlePropertyAnnotator.php
new file mode 100644
index 00000000..bc7bbd8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/DisplayTitlePropertyAnnotator.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\PropertyAnnotator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DisplayTitlePropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var string|false
+ */
+ private $displayTitle;
+
+ /**
+ * @var string
+ */
+ private $defaultSort;
+
+ /**
+ * @var boolean
+ */
+ private $canCreateAnnotation = true;
+
+ /**
+ * @since 2.4
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param string|false $displayTitle
+ * @param string $defaultSort
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, $displayTitle = false, $defaultSort = '' ) {
+ parent::__construct( $propertyAnnotator );
+ $this->displayTitle = $displayTitle;
+ $this->defaultSort = $defaultSort;
+ }
+
+ /**
+ * @see SMW_DV_WPV_DTITLE in $GLOBALS['smwgDVFeatures']
+ *
+ * @since 2.5
+ *
+ * @param boolean $canCreateAnnotation
+ */
+ public function canCreateAnnotation( $canCreateAnnotation ) {
+ $this->canCreateAnnotation = (bool)$canCreateAnnotation;
+ }
+
+ protected function addPropertyValues() {
+
+ if ( !$this->canCreateAnnotation || !$this->displayTitle || $this->displayTitle === '' ) {
+ return;
+ }
+
+ // #1439, #1611
+ $dataItem = $this->dataItemFactory->newDIBlob(
+ strip_tags( htmlspecialchars_decode( $this->displayTitle, ENT_QUOTES ) )
+ );
+
+ $this->getSemanticData()->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_DTITLE' ),
+ $dataItem
+ );
+
+ // If the defaultSort is empty then no explicit sortKey was expected
+ // therefore use the title content before the SortKeyPropertyAnnotator
+ if ( $this->defaultSort === '' ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_SKEY' ),
+ $dataItem
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/EditProtectedPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/EditProtectedPropertyAnnotator.php
new file mode 100644
index 00000000..8bb392f0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/EditProtectedPropertyAnnotator.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use Html;
+use ParserOutput;
+use SMW\Message;
+use SMW\PropertyAnnotator;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EditProtectedPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * Indicates whether the annotation was maintained by
+ * the system or not.
+ */
+ const SYSTEM_ANNOTATION = 'editprotectedpropertyannotator.system.annotation';
+
+ /**
+ * @var Title
+ */
+ private $title;
+
+ /**
+ * @var boolean
+ */
+ private $editProtectionRight = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param Title $title
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, Title $title ) {
+ parent::__construct( $propertyAnnotator );
+ $this->title = $title;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|boolean $editProtectionRight
+ */
+ public function setEditProtectionRight( $editProtectionRight ) {
+ $this->editProtectionRight = $editProtectionRight;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ParserOutput
+ */
+ public function addTopIndicatorTo( ParserOutput $parserOutput ) {
+
+ if ( $this->editProtectionRight === false ) {
+ return false;
+ }
+
+ // FIXME 3.0; Only MW 1.25+ (ParserOutput::setIndicator)
+ if ( !method_exists( $parserOutput, 'setIndicator' ) ) {
+ return false;
+ }
+
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ if ( !$this->isEnabledProtection( $property ) && !$this->hasEditProtection() ) {
+ return;
+ }
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-edit-protection',
+ 'title' => Message::get( 'smw-edit-protection-enabled', Message::TEXT, Message::USER_LANGUAGE )
+ ], ''
+ );
+
+ $parserOutput->setIndicator(
+ 'smw-protection-indicator',
+ Html::rawElement( 'div', [ 'class' => 'smw-protection-indicator' ], $html )
+ );
+ }
+
+ /**
+ * @see PropertyAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+
+ if ( $this->editProtectionRight === false ) {
+ return false;
+ }
+
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ if ( $this->getSemanticData()->hasProperty( $property ) || !$this->hasEditProtection() ) {
+ return;
+ }
+
+ // Notify preceding processes that this property is set as part of the
+ // protection restriction detection in order to decide whether this
+ // property was added manually or by the system
+ $dataItem = $this->dataItemFactory->newDIBoolean( true );
+ $dataItem->setOption( self::SYSTEM_ANNOTATION, true );
+
+ // Since edit protection is active, add the property as indicator this is
+ // especially to retain the status when purging a page
+ $this->getSemanticData()->addPropertyObjectValue(
+ $property,
+ $dataItem
+ );
+ }
+
+ private function hasEditProtection() {
+
+ //$this->title->flushRestrictions();
+
+ if ( !$this->title->isProtected( 'edit' ) ) {
+ return false;
+ }
+
+ $restrictions = array_flip( $this->title->getRestrictions( 'edit' ) );
+
+ // There could by any edit protections but the `Is edit protected` is
+ // bound to the `smwgEditProtectionRight` setting
+ return isset( $restrictions[$this->editProtectionRight] );
+ }
+
+ private function isEnabledProtection( $property ) {
+
+ if ( !$this->getSemanticData()->hasProperty( $property ) ) {
+ return false;
+ }
+
+ $semanticData = $this->getSemanticData();
+
+ $dataItems = $semanticData->getPropertyValues( $property );
+ $isEnabledProtection = false;
+
+ // In case of two competing values, true always wins
+ foreach ( $dataItems as $dataItem ) {
+
+ $isEnabledProtection = $dataItem->getBoolean();
+
+ if ( $isEnabledProtection ) {
+ break;
+ }
+ }
+
+ return $isEnabledProtection;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/MandatoryTypePropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/MandatoryTypePropertyAnnotator.php
new file mode 100644
index 00000000..f18ff017
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/MandatoryTypePropertyAnnotator.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MandatoryTypePropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * Indicates a forced removal
+ */
+ const IMPO_REMOVED_TYPE = 'mandatorytype.propertyannotator.impo.removed.type';
+
+ protected function addPropertyValues() {
+
+ $subject = $this->getSemanticData()->getSubject();
+
+ if ( $subject->getNamespace() !== SMW_NS_PROPERTY ) {
+ return;
+ }
+
+ $property = DIProperty::newFromUserLabel(
+ str_replace( '_', ' ', $subject->getDBKey() )
+ );
+
+ if ( !$property->isUserDefined() ) {
+ return;
+ }
+
+ $this->findMandatoryTypeForImportVocabulary();
+ }
+
+ private function findMandatoryTypeForImportVocabulary() {
+
+ $property = new DIProperty( '_IMPO' );
+
+ $dataItems = $this->getSemanticData()->getPropertyValues(
+ $property
+ );
+
+ if ( $dataItems === null || $dataItems === [] ) {
+ return;
+ }
+
+ $this->addTypeFromImportVocabulary( $property, current( $dataItems ) );
+ }
+
+ private function addTypeFromImportVocabulary( $property, $dataItem ) {
+
+ $importValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ if ( strpos( $importValue->getTermType(), ':' ) === false ) {
+ return;
+ }
+
+ $property = new DIProperty( '_TYPE' );
+
+ list( $ns, $type ) = explode( ':', $importValue->getTermType(), 2 );
+
+ $typeId = DataTypeRegistry::getInstance()->findTypeId( $type );
+
+ if ( $typeId === '' ) {
+ return;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $property,
+ $typeId
+ );
+
+ $this->replaceAnyTypeByImportType( $property, $dataValue );
+ }
+
+ private function replaceAnyTypeByImportType( DIProperty $property, $dataValue ) {
+
+ foreach ( $this->getSemanticData()->getPropertyValues( $property ) as $dataItem ) {
+ $this->getSemanticData()->setOption( self::IMPO_REMOVED_TYPE, $dataItem );
+
+ $this->getSemanticData()->removePropertyObjectValue(
+ $property,
+ $dataItem
+ );
+ }
+
+ $this->getSemanticData()->addDataValue( $dataValue );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/NullPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/NullPropertyAnnotator.php
new file mode 100644
index 00000000..ea92a40f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/NullPropertyAnnotator.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\PropertyAnnotator;
+use SMW\SemanticData;
+
+/**
+ * Root object representing the initial data transfer object to interact with
+ * a Decorator
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NullPropertyAnnotator implements PropertyAnnotator {
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData;
+
+ /**
+ * @since 1.9
+ *
+ * @param SemanticData $semanticData
+ */
+ public function __construct( SemanticData $semanticData ) {
+ $this->semanticData = $semanticData;
+ }
+
+ /**
+ * @see PropertyAnnotator::getSemanticData
+ *
+ * @since 1.9
+ */
+ public function getSemanticData() {
+ return $this->semanticData;
+ }
+
+ /**
+ * @see PropertyAnnotator::addAnnotation
+ *
+ * @since 1.9
+ */
+ public function addAnnotation() {
+ return $this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PredefinedPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PredefinedPropertyAnnotator.php
new file mode 100644
index 00000000..9b8576e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PredefinedPropertyAnnotator.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\PageInfo;
+use SMW\PropertyAnnotator;
+use SMW\PropertyRegistry;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDITime as DITime;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var PageInfo
+ */
+ private $pageInfo;
+
+ /**
+ * @var array
+ */
+ private $predefinedPropertyList = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param PageInfo $pageInfo
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, PageInfo $pageInfo ) {
+ parent::__construct( $propertyAnnotator );
+ $this->pageInfo = $pageInfo;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $predefinedPropertyList
+ */
+ public function setPredefinedPropertyList( array $predefinedPropertyList ) {
+ $this->predefinedPropertyList = $predefinedPropertyList;
+ }
+
+ protected function addPropertyValues() {
+
+ $cachedProperties = [];
+
+ foreach ( $this->predefinedPropertyList as $propertyId ) {
+
+ if ( $this->isRegisteredPropertyId( $propertyId, $cachedProperties ) ) {
+ continue;
+ }
+
+ $propertyDI = new DIProperty( $propertyId );
+
+ if ( $this->getSemanticData()->getPropertyValues( $propertyDI ) !== [] ) {
+ $cachedProperties[$propertyId] = true;
+ continue;
+ }
+
+ $dataItem = $this->createDataItemByPropertyId( $propertyId );
+
+ if ( $dataItem instanceof DataItem ) {
+ $cachedProperties[$propertyId] = true;
+ $this->getSemanticData()->addPropertyObjectValue( $propertyDI, $dataItem );
+ }
+ }
+ }
+
+ protected function isRegisteredPropertyId( $propertyId, $cachedProperties ) {
+ return ( PropertyRegistry::getInstance()->getPropertyValueTypeById( $propertyId ) === '' ) ||
+ array_key_exists( $propertyId, $cachedProperties );
+ }
+
+ protected function createDataItemByPropertyId( $propertyId ) {
+
+ $dataItem = null;
+
+ switch ( $propertyId ) {
+ case DIProperty::TYPE_MODIFICATION_DATE :
+ $dataItem = DITime::newFromTimestamp( $this->pageInfo->getModificationDate() );
+ break;
+ case DIProperty::TYPE_CREATION_DATE :
+ $dataItem = DITime::newFromTimestamp( $this->pageInfo->getCreationDate() );
+ break;
+ case DIProperty::TYPE_NEW_PAGE :
+ $dataItem = new DIBoolean( $this->pageInfo->isNewPage() );
+ break;
+ case DIProperty::TYPE_LAST_EDITOR :
+ $dataItem = $this->pageInfo->getLastEditor() ? DIWikiPage::newFromTitle( $this->pageInfo->getLastEditor() ) : null;
+ break;
+ case DIProperty::TYPE_MEDIA : // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ $dataItem = $this->pageInfo->isFilePage() && $this->pageInfo->getMediaType() !== '' && $this->pageInfo->getMediaType() !== null ? new DIBlob( $this->pageInfo->getMediaType() ) : null;
+ // @codingStandardsIgnoreEnd
+ break;
+ case DIProperty::TYPE_MIME : // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ $dataItem = $this->pageInfo->isFilePage() && $this->pageInfo->getMimeType() !== '' && $this->pageInfo->getMimeType() !== null ? new DIBlob( $this->pageInfo->getMimeType() ) : null;
+ // @codingStandardsIgnoreEnd
+ break;
+ }
+
+ return $dataItem;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PropertyAnnotatorDecorator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PropertyAnnotatorDecorator.php
new file mode 100644
index 00000000..6f5d73ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/PropertyAnnotatorDecorator.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DataItemFactory;
+use SMW\PropertyAnnotator;
+
+/**
+ * Decorator that contains the reference to the invoked PropertyAnnotator
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+abstract class PropertyAnnotatorDecorator implements PropertyAnnotator {
+
+ /**
+ * @var PropertyAnnotator
+ */
+ protected $propertyAnnotator;
+
+ /**
+ * @var DataItemFactory
+ */
+ protected $dataItemFactory;
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator ) {
+ $this->propertyAnnotator = $propertyAnnotator;
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ /**
+ * @see PropertyAnnotator::getSemanticData
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData() {
+ return $this->propertyAnnotator->getSemanticData();
+ }
+
+ /**
+ * @see PropertyAnnotator::addAnnotation
+ *
+ * @since 1.9
+ *
+ * @return PropertyAnnotator
+ */
+ public function addAnnotation() {
+
+ $this->propertyAnnotator->addAnnotation();
+ $this->addPropertyValues();
+
+ return $this;
+ }
+
+ /**
+ * @since 1.9
+ */
+ protected abstract function addPropertyValues();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/RedirectPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/RedirectPropertyAnnotator.php
new file mode 100644
index 00000000..e06c3618
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/RedirectPropertyAnnotator.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\RedirectTargetFinder;
+use SMW\PropertyAnnotator;
+
+/**
+ * Handling redirect annotation
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RedirectPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var RedirectTargetFinder
+ */
+ private $redirectTargetFinder;
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param RedirectTargetFinder $redirectTargetFinder
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, RedirectTargetFinder $redirectTargetFinder ) {
+ parent::__construct( $propertyAnnotator );
+ $this->redirectTargetFinder = $redirectTargetFinder;
+ }
+
+ /**
+ * @see PropertyAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+
+ if ( !$this->redirectTargetFinder->hasRedirectTarget() ) {
+ return;
+ }
+
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ DIWikiPage::newFromTitle( $this->redirectTargetFinder->getRedirectTarget() )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SchemaPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SchemaPropertyAnnotator.php
new file mode 100644
index 00000000..1a84ccf5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SchemaPropertyAnnotator.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\PropertyAnnotator;
+use SMW\Schema\Schema;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var Schema
+ */
+ private $schema;
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param Schema $schema
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, Schema $schema = null ) {
+ parent::__construct( $propertyAnnotator );
+ $this->schema = $schema;
+ }
+
+ protected function addPropertyValues() {
+
+ if ( $this->schema === null ) {
+ return;
+ }
+
+ $semanticData = $this->getSemanticData();
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SCHEMA_TYPE' ),
+ $this->dataItemFactory->newDIBlob( $this->schema->get( Schema::SCHEMA_TYPE ) )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SCHEMA_DEF' ),
+ $this->dataItemFactory->newDIBlob( $this->schema )
+ );
+
+ if ( ( $desc = $this->schema->get( Schema::SCHEMA_DESCRIPTION, '' ) ) !== '' ) {
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SCHEMA_DESC' ),
+ $this->dataItemFactory->newDIBlob( $desc )
+ );
+ }
+
+ foreach ( $this->schema->get( Schema::SCHEMA_TAG, [] ) as $tag ) {
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SCHEMA_TAG' ),
+ $this->dataItemFactory->newDIBlob( mb_strtolower( $tag ) )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SortKeyPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SortKeyPropertyAnnotator.php
new file mode 100644
index 00000000..387608f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/SortKeyPropertyAnnotator.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\PropertyAnnotator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SortKeyPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var string
+ */
+ private $defaultSort;
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param string $defaultSort
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, $defaultSort ) {
+ parent::__construct( $propertyAnnotator );
+ $this->defaultSort = $defaultSort;
+ }
+
+ protected function addPropertyValues() {
+
+ $sortkey = $this->defaultSort ? $this->defaultSort : $this->getSemanticData()->getSubject()->getSortKey();
+
+ $property = $this->dataItemFactory->newDIProperty(
+ DIProperty::TYPE_SORTKEY
+ );
+
+ if ( !$this->getSemanticData()->hasProperty( $property ) ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ $property,
+ $this->dataItemFactory->newDIBlob( $sortkey )
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/TranslationPropertyAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/TranslationPropertyAnnotator.php
new file mode 100644
index 00000000..2eb6fbab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyAnnotators/TranslationPropertyAnnotator.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\PropertyAnnotators;
+
+use SMW\PropertyAnnotator;
+use SMW\DataModel\ContainerSemanticData;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TranslationPropertyAnnotator extends PropertyAnnotatorDecorator {
+
+ /**
+ * @var array|null
+ */
+ private $translation;
+
+ /**
+ * @var array
+ */
+ private $predefinedPropertyList = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyAnnotator $propertyAnnotator
+ * @param array|null $translation
+ */
+ public function __construct( PropertyAnnotator $propertyAnnotator, $translation ) {
+ parent::__construct( $propertyAnnotator );
+ $this->translation = $translation;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $predefinedPropertyList
+ */
+ public function setPredefinedPropertyList( array $predefinedPropertyList ) {
+ $this->predefinedPropertyList = array_flip( $predefinedPropertyList );
+ }
+
+ protected function addPropertyValues() {
+
+ // Expected identifiers, @see https://gerrit.wikimedia.org/r/387548
+ if ( !is_array( $this->translation ) || !isset( $this->predefinedPropertyList['_TRANS'] ) ) {
+ return;
+ }
+
+ $containerSemanticData = null;
+
+ if ( isset( $this->translation['languagecode'] ) ) {
+ $languageCode = $this->translation['languagecode'];
+ $containerSemanticData = $this->newContainerSemanticData( $languageCode );
+
+ // Translation.Language code
+ $containerSemanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_LCODE' ),
+ $this->dataItemFactory->newDIBlob( $languageCode )
+ );
+ }
+
+ if ( isset( $this->translation['sourcepagetitle'] ) && $this->translation['sourcepagetitle'] instanceof Title ) {
+ // Translation.Translation source
+ $containerSemanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_TRANS_SOURCE' ),
+ $this->dataItemFactory->newDIWikiPage( $this->translation['sourcepagetitle'] )
+ );
+ }
+
+ if ( isset( $this->translation['messagegroupid'] ) ) {
+ // Translation.Translation group
+ $containerSemanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_TRANS_GROUP' ),
+ $this->dataItemFactory->newDIBlob( $this->translation['messagegroupid'] )
+ );
+ }
+
+ if ( $containerSemanticData !== null ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_TRANS' ),
+ $this->dataItemFactory->newDIContainer( $containerSemanticData )
+ );
+ }
+ }
+
+ private function newContainerSemanticData( $languageCode ) {
+
+ $dataItem = $this->getSemanticData()->getSubject();
+ $subobjectName = 'trans.' . $languageCode;
+
+ $subject = $this->dataItemFactory->newDIWikiPage(
+ $dataItem->getDBkey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $subobjectName
+ );
+
+ return $this->dataItemFactory->newContainerSemanticData( $subject );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php
new file mode 100644
index 00000000..a0834810
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyChangePropagationNotifier.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW;
+
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+use SMWDataItem;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Before a new set of data (type, constraints etc.) is stored about a property
+ * the class tries to compare old and new specifications (values about that property)
+ * and notifies a dispatcher about a change.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class PropertyChangePropagationNotifier {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SerializerFactory
+ */
+ private $serializerFactory;
+
+ /**
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var boolean
+ */
+ private $hasDiff = false;
+
+ /**
+ * @var boolean
+ */
+ private $isTypePropagation = false;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param Store $store
+ * @param SerializerFactory $serializerFactory
+ */
+ public function __construct( Store $store, SerializerFactory $serializerFactory ) {
+ $this->store = $store;
+ $this->serializerFactory = $serializerFactory;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyList
+ */
+ public function setPropertyList( array $propertyList ) {
+ $this->propertyList = $propertyList;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode.
+ *
+ * @since 3.0
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return boolean
+ */
+ public function hasDiff() {
+ return $this->hasDiff;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ */
+ public function notify( DIWikiPage $subject ) {
+
+ $namespace = $subject->getNamespace();
+
+ if ( !$this->hasDiff() || ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) ) {
+ return false;
+ }
+
+ $params = [];
+
+ if ( $this->isTypePropagation ) {
+ $params['isTypePropagation'] = true;
+ }
+
+ return ChangePropagationDispatchJob::planAsJob( $subject, $params );
+ }
+
+ /**
+ * Compare and detect differences between the invoked semantic data
+ * and the current stored data
+ *
+ * @note Compare on extra properties from `smwgChangePropagationWatchlist`
+ * (e.g '_PLIST') to find a possible specification change
+ *
+ * @since 1.9
+ */
+ public function checkAndNotify( SemanticData &$semanticData ) {
+
+ $namespace = $semanticData->getSubject()->getNamespace();
+
+ if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) {
+ return;
+ }
+
+ $this->hasDiff = false;
+
+ // Check the type first
+ $propertyList = array_merge(
+ [
+ '_TYPE',
+ '_CONV',
+ '_UNIT',
+ '_REDI'
+ ],
+ $this->propertyList
+ );
+
+ foreach ( $propertyList as $key ) {
+
+ // No need to keep comparing once a diff has been
+ // detected
+ if ( $this->hasDiff() ) {
+ break;
+ }
+
+ $this->doCompare( $semanticData, $key );
+ }
+
+ $this->doNotifyAndPostpone( $semanticData );
+ }
+
+ private function doCompare( $semanticData, $key ) {
+
+ $property = new DIProperty( $key );
+
+ $newValues = $semanticData->getPropertyValues( $property );
+
+ $oldValues = $this->store->getPropertyValues(
+ $semanticData->getSubject(),
+ $property
+ );
+
+ $this->setDiff( !$this->isEqual( $oldValues, $newValues ), $key );
+ }
+
+ private function setDiff( $hasDiff = true, $key ) {
+
+ if ( !$hasDiff || $this->hasDiff ) {
+ return;
+ }
+
+ $this->hasDiff = true;
+ $this->isTypePropagation = $key === '_TYPE';
+ }
+
+ /**
+ * Helper function that compares two arrays of data values to check whether
+ * they contain the same content. Returns true if the two arrays contain the
+ * same data values (irrespective of their order), false otherwise.
+ *
+ * @param SMWDataItem[] $oldDataValue
+ * @param SMWDataItem[] $newDataValue
+ *
+ * @return boolean
+ */
+ private function isEqual( array $oldDataValue, array $newDataValue ) {
+
+ // The hashes of all values of both arrays are taken, then sorted
+ // and finally concatenated, thus creating one long hash out of each
+ // of the data value arrays. These are compared.
+ $values = [];
+ foreach ( $oldDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $oldDataValueHash = implode( '___', $values );
+
+ $values = [];
+ foreach ( $newDataValue as $v ) {
+ $values[] = $v->getHash();
+ }
+
+ sort( $values );
+ $newDataValueHash = implode( '___', $values );
+
+ return $oldDataValueHash == $newDataValueHash;
+ }
+
+ private function doNotifyAndPostpone( SemanticData &$semanticData ) {
+
+ if ( !$this->hasDiff() ) {
+ return;
+ }
+
+ $this->notify( $semanticData->getSubject() );
+
+ // If executed from the commandLine (cronJob etc.), do not
+ // suspend the update
+ if ( $this->isCommandLineMode === true ) {
+ return;
+ }
+
+ $previous = $this->store->getSemanticData(
+ $semanticData->getSubject()
+ );
+
+ $semanticDataSerializer = $this->serializerFactory->newSemanticDataSerializer();
+
+ $new = $semanticDataSerializer->serialize(
+ $semanticData
+ );
+
+ // Encode and store the new version of the SemanticData and suspend
+ // the update until ChangePropagationDispatchJob was able to select
+ // all connected entities
+ $previous->addPropertyObjectValue(
+ new DIProperty( DIProperty::TYPE_CHANGE_PROP ),
+ new DIBlob( json_encode( $new ) )
+ );
+
+ $semanticData = $previous;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyLabelFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyLabelFinder.php
new file mode 100644
index 00000000..4af23d67
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyLabelFinder.php
@@ -0,0 +1,242 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class PropertyLabelFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * Array with entries "property id" => "property label"
+ *
+ * @var string[]
+ */
+ private $languageDependentPropertyLabels = [];
+
+ /**
+ * Array with entries "property label" => "property id"
+ *
+ * @var string[]
+ */
+ private $canonicalPropertyLabels = [];
+
+ /**
+ * @var string[]
+ */
+ private $canonicalDatatypeLabels = [];
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param array $languageDependentPropertyLabels
+ * @param array $canonicalPropertyLabels
+ */
+ public function __construct( Store $store, array $languageDependentPropertyLabels = [], array $canonicalPropertyLabels = [], array $canonicalDatatypeLabels = [] ) {
+ $this->store = $store;
+ $this->languageDependentPropertyLabels = $languageDependentPropertyLabels;
+ $this->canonicalPropertyLabels = $canonicalPropertyLabels;
+ $this->canonicalDatatypeLabels = $canonicalDatatypeLabels;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getKownPredefinedPropertyLabels() {
+ return $this->languageDependentPropertyLabels;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ *
+ * @return string|boolean
+ */
+ public function findCanonicalPropertyLabelById( $id ) {
+
+ // Due to mapped lists avoid possible mismatch on dataTypes
+ // (e.g. Text -> _TEXT vs. Text -> _txt)
+ if ( ( $label = array_search( $id, $this->canonicalDatatypeLabels ) ) ) {
+ return $label;
+ }
+
+ return array_search( $id, $this->canonicalPropertyLabels );
+ }
+
+ /**
+ * @note An empty string is returned for incomplete translation (language
+ * bug) or deliberately invisible property
+ *
+ * @since 2.2
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function findPropertyLabelById( $id ) {
+
+ if ( array_key_exists( $id, $this->languageDependentPropertyLabels ) ) {
+ return $this->languageDependentPropertyLabels[$id];
+ }
+
+ return '';
+ }
+
+ /**
+ * @note An empty string is returned for incomplete translation (language
+ * bug) or deliberately invisible property
+ *
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function findPropertyLabelFromIdByLanguageCode( $id, $languageCode = '' ) {
+
+ if ( $languageCode === '' ) {
+ return $this->findPropertyLabelById( $id );
+ }
+
+ $lang = Localizer::getInstance()->getLang(
+ mb_strtolower( trim( $languageCode ) )
+ );
+
+ $labels = $lang->getPropertyLabels() + $lang->getDatatypeLabels();
+
+ if ( isset( $labels[$id] ) ) {
+ return $labels[$id];
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function findPreferredPropertyLabelByLanguageCode( $id, $languageCode = '' ) {
+
+ if ( $id === '' || $id === false ) {
+ return '';
+ }
+
+ // Lookup is cached in PropertySpecificationLookup
+ $propertySpecificationLookup = ApplicationFactory::getInstance()->getPropertySpecificationLookup();
+
+ $preferredPropertyLabel = $propertySpecificationLookup->getPreferredPropertyLabelBy(
+ new DIProperty( str_replace( ' ', '_', $id ) ),
+ $languageCode
+ );
+
+ return $preferredPropertyLabel;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ * @param string $languageCode
+ *
+ * @return DIProperty[]|[]
+ */
+ public function findPropertyListFromLabelByLanguageCode( $text, $languageCode = '' ) {
+
+ if ( $text === '' ) {
+ return [];
+ }
+
+ if ( $languageCode === '' ) {
+ $languageCode = Localizer::getInstance()->getContentLanguage()->getCode();
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ new DIProperty( '_PPLB' )
+ );
+
+ $dataValue->setUserValue(
+ $dataValue->getTextWithLanguageTag( $text, $languageCode )
+ );
+
+ $queryFactory = ApplicationFactory::getInstance()->getQueryFactory();
+ $descriptionFactory = $queryFactory->newDescriptionFactory();
+
+ $description = $descriptionFactory->newConjunction( [
+ $descriptionFactory->newNamespaceDescription( SMW_NS_PROPERTY ),
+ $descriptionFactory->newFromDataValue( $dataValue )
+ ] );
+
+ $propertyList = [];
+
+ $query = $queryFactory->newQuery( $description );
+ $query->setOption( $query::PROC_CONTEXT, 'PropertyLabelFinder' );
+ $query->setLimit( 100 );
+
+ $queryResult = $this->store->getQueryResult(
+ $query
+ );
+
+ if ( !$queryResult instanceof \SMWQueryResult ) {
+ return $propertyList;
+ }
+
+ foreach ( $queryResult->getResults() as $result ) {
+ $propertyList[] = DIProperty::newFromUserLabel( $result->getDBKey() );
+ }
+
+ return $propertyList;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $label
+ *
+ * @return string|false
+ */
+ public function searchPropertyIdByLabel( $label ) {
+ return array_search( $label, $this->languageDependentPropertyLabels );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $id
+ * @param string $label
+ */
+ public function registerPropertyLabel( $id, $label, $asCanonical = true ) {
+
+ // Prevent an extension from overriding an already registered
+ // canonical label that may point to a different ID
+ if ( isset( $this->canonicalPropertyLabels[$label] ) && $this->canonicalPropertyLabels[$label] !== $id ) {
+ return;
+ }
+
+ $this->languageDependentPropertyLabels[$id] = $label;
+
+ // This is done so extensions can register the property id/label as being
+ // canonical in their representation while the alias may hold translated
+ // language depedendant matches
+ if ( $asCanonical ) {
+ $this->canonicalPropertyLabels[$label] = $id;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyRegistry.php
new file mode 100644
index 00000000..ab5f521a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyRegistry.php
@@ -0,0 +1,499 @@
+<?php
+
+namespace SMW;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class PropertyRegistry {
+
+ /**
+ * @var PropertyRegistry
+ */
+ private static $instance = null;
+
+ /**
+ * @var PropertyLabelFinder
+ */
+ private $propertyLabelFinder = null;
+
+ /**
+ * Array for assigning types to predefined properties. Each
+ * property is associated with an array with the following
+ * elements:
+ *
+ * * ID of datatype to be used for this property
+ *
+ * * Boolean, stating if this property is shown in Factbox, Browse, and
+ * similar interfaces; (note that this is only relevant if the
+ * property can be displayed at all, i.e. has a translated label in
+ * the wiki language; invisible properties are never shown).
+ *
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var string[]
+ */
+ private $datatypeLabels = [];
+
+ /**
+ * @var string[]
+ */
+ private $propertyDescriptionMsgKeys = [];
+
+ /**
+ * @var PropertyAliasFinder
+ */
+ private $propertyAliasFinder;
+
+ /**
+ * @var string[]
+ */
+ private $dataTypePropertyExemptionList = [];
+
+ /**
+ * @since 2.1
+ *
+ * @return PropertyRegistry
+ */
+ public static function getInstance() {
+
+ if ( self::$instance !== null ) {
+ return self::$instance;
+ }
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $lang = Localizer::getInstance()->getLang();
+
+ $propertyAliasFinder = new PropertyAliasFinder(
+ $applicationFactory->getCache(),
+ $lang->getPropertyAliases(),
+ $lang->getCanonicalPropertyAliases()
+ );
+
+ $settings = $applicationFactory->getSettings();
+
+ self::$instance = new self(
+ DataTypeRegistry::getInstance(),
+ $applicationFactory->getPropertyLabelFinder(),
+ $propertyAliasFinder,
+ $settings->get( 'smwgDataTypePropertyExemptionList' )
+ );
+
+ self::$instance->initProperties(
+ TypesRegistry::getPropertyList(
+ $settings->isFlagSet( 'smwgCategoryFeatures', SMW_CAT_HIERARCHY )
+ )
+ );
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DataTypeRegistry $datatypeRegistry
+ * @param PropertyLabelFinder $propertyLabelFinder
+ * @param PropertyAliasFinder $propertyAliasFinder
+ * @param array $dataTypePropertyExemptionList
+ */
+ public function __construct( DataTypeRegistry $datatypeRegistry, PropertyLabelFinder $propertyLabelFinder, PropertyAliasFinder $propertyAliasFinder, array $dataTypePropertyExemptionList = [] ) {
+
+ $this->datatypeLabels = $datatypeRegistry->getKnownTypeLabels();
+ $this->propertyLabelFinder = $propertyLabelFinder;
+ $this->propertyAliasFinder = $propertyAliasFinder;
+
+ // To get an index access
+ $this->dataTypePropertyExemptionList = array_flip( $dataTypePropertyExemptionList );
+
+ foreach ( $this->datatypeLabels as $id => $label ) {
+
+ if ( isset( $this->dataTypePropertyExemptionList[$label] ) ) {
+ continue;
+ }
+
+ $this->registerPropertyLabel( $id, $label );
+ }
+
+ foreach ( $datatypeRegistry->getKnownTypeAliases() as $alias => $id ) {
+
+ if ( isset( $this->dataTypePropertyExemptionList[$alias] ) ) {
+ continue;
+ }
+
+ $this->registerPropertyAlias( $id, $alias );
+ }
+ }
+
+ /**
+ * @since 2.1
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getPropertyList() {
+ return $this->propertyList;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getKnownPropertyAliases() {
+ return $this->propertyAliasFinder->getKnownPropertyAliases();
+ }
+
+ /**
+ * A method for registering/overwriting predefined properties for SMW.
+ * It should be called from within the hook 'smwInitProperties' only.
+ * IDs should start with three underscores "___" to avoid current and
+ * future confusion with SMW built-ins.
+ *
+ * @param string $id
+ * @param string $valueType SMW type id
+ * @param string|bool $label user label or false (internal property)
+ * @param boolean $isVisible only used if label is given, see isShown()
+ * @param boolean $isAnnotable
+ */
+ public function registerProperty( $id, $valueType, $label = false, $isVisible = false, $isAnnotable = true ) {
+
+ $this->propertyList[$id] = [ $valueType, $isVisible, $isAnnotable ];
+
+ if ( $label !== false ) {
+ $this->registerPropertyLabel( $id, $label );
+ }
+ }
+
+ /**
+ * Add a new alias label to an existing property ID. Note that every ID
+ * should have a primary label, either provided by SMW or registered
+ * with registerProperty().
+ *
+ * @param $id string id of a property
+ * @param $label string alias label for the property
+ *
+ * @note Always use registerProperty() for the first label. No property
+ * that has used "false" for a label on registration should have an
+ * alias.
+ */
+ public function registerPropertyAlias( $id, $label ) {
+ $this->propertyAliasFinder->registerAliasByFixedLabel( $id, $label );
+ }
+
+ /**
+ * Register an alias using a message key to allow fetching localized
+ * labels dynamically (for when the user language is changed etc).
+ *
+ * @since 2.4
+ *
+ * @param string $id
+ * @param string $msgKey
+ */
+ public function registerPropertyAliasByMsgKey( $id, $msgKey ) {
+ $this->propertyAliasFinder->registerAliasByMsgKey( $id, $msgKey );
+ }
+
+ /**
+ * Register a description message key for allowing it to be displayed in a
+ * localized context.
+ *
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string $msgKey
+ */
+ public function registerPropertyDescriptionByMsgKey( $id, $msgKey ) {
+ $this->propertyDescriptionMsgKeys[$id] = $msgKey;
+ }
+
+ /**
+ * @deprecated since 3.0, use PropertyRegistry::registerPropertyDescriptionByMsgKey
+ */
+ public function registerPropertyDescriptionMsgKeyById( $id, $msgKey ) {
+ $this->registerPropertyDescriptionByMsgKey( $id, $msgKey );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function findPropertyDescriptionMsgKeyById( $id ) {
+ return isset( $this->propertyDescriptionMsgKeys[$id] ) ? $this->propertyDescriptionMsgKeys[$id] : '';
+ }
+
+ /**
+ * Get the translated user label for a given internal property ID.
+ * Returns empty string for properties without a translation (these are
+ * usually internal, generated by SMW but not shown to the user).
+ *
+ * @note An empty string is returned for incomplete translation (language
+ * bug) or deliberately invisible property
+ *
+ * @since 2.1
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function findPropertyLabelById( $id ) {
+
+ // This is a hack but there is no other good way to make it work without
+ // open a whole new can of worms
+ // '__' indicates predefined properties of extensions that contain alias
+ // and translated labels and if available we want the translated label
+ if ( ( substr( $id, 0, 2 ) === '__' ) &&
+ ( $label = $this->propertyAliasFinder->findPropertyAliasById( $id ) ) ) {
+ return $label;
+ }
+
+ // core has dedicated files per language so the label is available over
+ // the invoked language
+ return $this->propertyLabelFinder->findPropertyLabelById( $id );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function findCanonicalPropertyLabelById( $id ) {
+ return $this->propertyLabelFinder->findCanonicalPropertyLabelById( $id );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function findPropertyLabelFromIdByLanguageCode( $id, $languageCode = '' ) {
+ return $this->propertyLabelFinder->findPropertyLabelFromIdByLanguageCode( $id, $languageCode );
+ }
+
+ /**
+ * @deprecated since 2.1 use findPropertyLabelById instead
+ */
+ public function findPropertyLabel( $id ) {
+ return $this->findPropertyLabelById( $id );
+ }
+
+ /**
+ * Get the type ID of a predefined property, or '' if the property
+ * is not predefined.
+ * The function is guaranteed to return a type ID for keys of
+ * properties where isUserDefined() returns false.
+ *
+ * @param string $id
+ *
+ * @return string
+ */
+ public function getPropertyValueTypeById( $id ) {
+
+ if ( $this->isRegistered( $id ) ) {
+ return $this->propertyList[$id][0];
+ }
+
+ return '';
+ }
+
+ /**
+ * @deprecated since 3.0, use PropertyRegistry::getPropertyValueTypeById instead
+ */
+ public function getPropertyTypeId( $id ) {
+ return $this->getPropertyValueTypeById( $id );
+ }
+
+ /**
+ * @deprecated since 2.1 use getPropertyValueTypeById instead
+ */
+ public function getPredefinedPropertyTypeId( $id ) {
+ return $this->getPropertyValueTypeById( $id );
+ }
+
+ /**
+ * Find and return the ID for the pre-defined property of the given
+ * local label. If the label does not belong to a pre-defined property,
+ * return false.
+ *
+ * @param string $label normalized property label
+ * @param boolean $useAlias determining whether to check if the label is an alias
+ *
+ * @return mixed string property ID or false
+ */
+ public function findPropertyIdByLabel( $label, $useAlias = true ) {
+
+ $id = $this->propertyLabelFinder->searchPropertyIdByLabel( $label );
+
+ if ( $id !== false ) {
+ return $id;
+ } elseif ( $useAlias && $this->propertyAliasFinder->findPropertyIdByAlias( $label ) ) {
+ return $this->propertyAliasFinder->findPropertyIdByAlias( $label );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $label
+ * @param string $languageCode
+ *
+ * @return mixed string property ID or false
+ */
+ public function findPropertyIdFromLabelByLanguageCode( $label, $languageCode = '' ) {
+
+ $languageCode = mb_strtolower( trim( $languageCode ) );
+
+ // Match the canonical form
+ if ( $languageCode === '' ) {
+ return $this->findPropertyIdByLabel( $label );
+ }
+
+ $lang = Localizer::getInstance()->getLang(
+ $languageCode
+ );
+
+ // Language dep. stored as aliases
+ $aliases = $lang->getPropertyLabels() + $lang->getDatatypeLabels();
+
+ if ( ( $id = array_search( $label, $aliases ) ) !== false && !isset( $this->dataTypePropertyExemptionList[$label] ) ) {
+ return $id;
+ }
+
+ // Those are mostly from extension that register a msgKey as no dedicated
+ // lang. file exists; maybe this should be cached somehow?
+ foreach ( $this->propertyAliasFinder->getKnownPropertyAliasesByLanguageCode( $languageCode ) as $alias => $id ) {
+ if ( $label === $alias ) {
+ return $id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $id
+ * @param string|null $languageCode
+ *
+ * @return string
+ */
+ public function findPreferredPropertyLabelFromIdByLanguageCode( $id, $languageCode = '' ) {
+
+ if ( $languageCode === false || $languageCode === '' ) {
+ $languageCode = Localizer::getInstance()->getUserLanguage()->getCode();
+ }
+
+ return $this->propertyLabelFinder->findPreferredPropertyLabelByLanguageCode( $id, $languageCode );
+ }
+
+ /**
+ * @deprecated since 2.1 use findPropertyIdByLabel instead
+ */
+ public function findPropertyId( $label, $useAlias = true ) {
+ return $this->findPropertyIdByLabel( $label, $useAlias );
+ }
+
+ /**
+ * @deprecated since 3.0 use isRegistered instead
+ */
+ public function isKnownPropertyId( $id ) {
+ return $this->isRegistered( $id );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $id
+ *
+ * @return boolean
+ */
+ public function isRegistered( $id ) {
+ return isset( $this->propertyList[$id] ) || array_key_exists( $id, $this->propertyList );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $id
+ *
+ * @return boolean
+ */
+ public function isVisible( $id ) {
+ return $this->isRegistered( $id ) ? $this->propertyList[$id][1] : false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ *
+ * @return boolean
+ */
+ public function isAnnotable( $id ) {
+ return $this->isRegistered( $id ) ? $this->propertyList[$id][2] : false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ *
+ * @return boolean
+ */
+ public function isDeclarative( $id ) {
+
+ if ( !$this->isRegistered( $id ) ) {
+ return false;
+ }
+
+ return isset( $this->propertyList[$id][3] ) ? $this->propertyList[$id][3] : false;
+ }
+
+ /**
+ * @note All ids must start with underscores. The translation for each ID,
+ * if any, is defined in the language files. Properties without translation
+ * cannot be entered by or displayed to users, whatever their "show" value
+ * below.
+ */
+ protected function initProperties( array $propertyList ) {
+
+ $this->propertyList = $propertyList;
+
+ foreach ( $this->datatypeLabels as $id => $label ) {
+ $this->propertyList[$id] = [ $id, true, true, false ];
+ }
+
+ // @deprecated since 2.1
+ \Hooks::run( 'smwInitProperties' );
+
+ \Hooks::run( 'SMW::Property::initProperties', [ $this ] );
+ }
+
+ private function registerPropertyLabel( $id, $label, $asCanonical = true ) {
+ $this->propertyLabelFinder->registerPropertyLabel( $id, $label, $asCanonical );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertyRestrictionExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertyRestrictionExaminer.php
new file mode 100644
index 00000000..0ab9881f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertyRestrictionExaminer.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace SMW;
+
+use Title;
+use User;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyRestrictionExaminer {
+
+ const CREATE_RESTRICTION = 'smw-datavalue-property-create-restriction';
+
+ /**
+ * @var array
+ */
+ private $error = [];
+
+ /**
+ * @var User|null
+ */
+ private $user;
+
+ /**
+ * @var boolean|string
+ */
+ private $createProtectionRight = false;
+
+ /**
+ * @var boolean
+ */
+ private $isQueryContext = false;
+
+ /**
+ * @var array
+ */
+ private $exists = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param User $user
+ */
+ public function setUser( User $user ) {
+ $this->user = $user;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|boolean $createProtectionRight
+ */
+ public function setCreateProtectionRight( $createProtectionRight ) {
+ $this->createProtectionRight = $createProtectionRight;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isQueryContext
+ */
+ public function isQueryContext( $isQueryContext ) {
+ $this->isQueryContext = (bool)$isQueryContext;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasRestriction() {
+ return $this->error !== [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array
+ */
+ public function getError() {
+ return $this->error;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $error
+ *
+ * @return DIProperty|null
+ */
+ public static function grepPropertyFromRestrictionErrorMsg( $errorMsg ) {
+
+ if ( strpos( $errorMsg, self::CREATE_RESTRICTION ) === false ) {
+ return null;
+ }
+
+ $error = json_decode( $errorMsg, true );
+
+ return isset( $error[2] ) ? DIProperty::newFromUserLabel( $error[2] ) : null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param DIWikiPage|null $contextPage
+ */
+ public function checkRestriction( DIProperty $property, DIWikiPage $contextPage = null ) {
+
+ $this->error = [];
+
+ if ( $this->isDeclarative( $property, $contextPage ) ) {
+ return;
+ }
+
+ if ( $this->isAnnotationRestricted( $property ) ) {
+ return;
+ }
+
+ if ( $this->isCreateProtected( $property ) ) {
+ return;
+ }
+ }
+
+ private function isDeclarative( $property, $contextPage = null ) {
+
+ if ( $this->isQueryContext || $contextPage === null ) {
+ return false;
+ }
+
+ $ns = $contextPage->getNamespace();
+
+ // Property, category page are allowed to carry declarative properties
+ if ( $ns === SMW_NS_PROPERTY || $ns === NS_CATEGORY ) {
+ return false;
+ }
+
+ if ( !PropertyRegistry::getInstance()->isDeclarative( $property->getKey() ) ) {
+ return false;
+ }
+
+ return $this->error = Message::encode(
+ [
+ 'smw-datavalue-property-restricted-declarative-use',
+ $property->getLabel()
+ ],
+ Message::PARSE
+ );
+ }
+
+ private function isAnnotationRestricted( $property ) {
+
+ if ( $this->isQueryContext || $property->isUserDefined() ) {
+ return false;
+ }
+
+ if ( $property->isUserAnnotable() ) {
+ return false;
+ }
+
+ return $this->error = [
+ 'smw-datavalue-property-restricted-annotation-use',
+ $property->getLabel()
+ ];
+ }
+
+ private function isCreateProtected( $property ) {
+
+ if ( $this->user === null || $this->createProtectionRight === false ) {
+ return false;
+ }
+
+ $key = $property->getKey();
+
+ // Non-existing property?
+ if ( !isset( $this->exists[$key] ) ) {
+ $this->exists[$key] = $property->isUserDefined() && $property->getDiWikiPage()->getTitle()->exists();
+ }
+
+ if ( $this->exists[$key] || $this->user->isAllowed( $this->createProtectionRight ) ) {
+ return false;
+ }
+
+ // A user without the appropriate right cannot use a non-existing property
+ return $this->error = Message::encode(
+ [
+ self::CREATE_RESTRICTION,
+ $property->getLabel(),
+ $this->createProtectionRight
+ ],
+ Message::PARSE
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php
new file mode 100644
index 00000000..8de435ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationLookup.php
@@ -0,0 +1,507 @@
+<?php
+
+namespace SMW;
+
+use Onoi\Cache\Cache;
+use RuntimeException;
+use SMW\Query\DescriptionFactory;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWQuery as Query;
+
+/**
+ * This class should be accessed via ApplicationFactory::getPropertySpecificationLookup
+ * to ensure a singleton instance.
+ *
+ * Changes to a property should trigger a PropertySpecificationLookup::resetCacheBy to
+ * evict all cached item store to that property.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertySpecificationLookup {
+
+ /**
+ * Reference used in InMemoryPoolCache
+ */
+ const POOLCACHE_ID = 'property.specification.lookup';
+
+ /**
+ * @var CachedPropertyValuesPrefetcher
+ */
+ private $cachedPropertyValuesPrefetcher;
+
+ /**
+ * @var string
+ */
+ private $languageCode = 'en';
+
+ /**
+ * @var Cache
+ */
+ private $intermediaryMemoryCache;
+
+ /**
+ * @since 2.4
+ *
+ * @param CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher
+ * @param Cache $intermediaryMemoryCache
+ */
+ public function __construct( CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher, Cache $intermediaryMemoryCache ) {
+ $this->cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher;
+ $this->intermediaryMemoryCache = $intermediaryMemoryCache;
+ $this->languageCode = Localizer::getInstance()->getContentLanguage()->getCode();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject
+ */
+ public function resetCacheBy( DIWikiPage $subject ) {
+ $this->cachedPropertyValuesPrefetcher->resetCacheBy( $subject );
+ $this->intermediaryMemoryCache->delete( $subject->getHash() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty|DIWikiPage $source
+ * @param DIProperty $target
+ *
+ * @return []|DataItem[]
+ */
+ public function getSpecification( $source, DIProperty $target ) {
+
+ if ( $source instanceof DIProperty ) {
+ $dataItem = $source->getCanonicalDiWikiPage();
+ } elseif( $source instanceof DIWikiPage ) {
+ $dataItem = $source;
+ } else {
+ throw new RuntimeException( "Invalid request instance type" );
+ }
+
+ $hash = $dataItem->getHash();
+ $key = $target->getKey();
+
+ $definition = $this->intermediaryMemoryCache->fetch( $hash );
+
+ if ( $definition === false ) {
+ $definition = [];
+ }
+
+ if ( isset( $definition[$key] ) ) {
+ return $definition[$key];
+ }
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $dataItem,
+ $target
+ );
+
+ if ( !is_array( $dataItems ) ) {
+ $dataItems = [];
+ }
+
+ $definition[$key] = $dataItems;
+ $this->intermediaryMemoryCache->save( $hash, $definition );
+
+ return $dataItems;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return false|DataItem
+ */
+ public function getFieldListBy( DIProperty $property ) {
+
+ $fieldList = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_LIST' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $fieldList = end( $dataItems );
+ }
+
+ return $fieldList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ * @param string $languageCode
+ *
+ * @return string
+ */
+ public function getPreferredPropertyLabelBy( DIProperty $property, $languageCode = '' ) {
+
+ $languageCode = $languageCode === '' ? $this->languageCode : $languageCode;
+ $key = 'ppl:' . $languageCode . ':'. $property->getKey();
+
+ // Guard against high frequency lookup
+ if ( ( $preferredPropertyLabel = $this->intermediaryMemoryCache->fetch( $key ) ) !== false ) {
+ return $preferredPropertyLabel;
+ }
+
+ $preferredPropertyLabel = $this->findPreferredPropertyLabel(
+ $property,
+ $languageCode
+ );
+
+ $this->intermediaryMemoryCache->save( $key, $preferredPropertyLabel );
+
+ return $preferredPropertyLabel;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $displayTitle
+ *
+ * @return DIProperty|false
+ */
+ public function getPropertyFromDisplayTitle( $displayTitle ) {
+
+ $descriptionFactory = new DescriptionFactory();
+
+ $description = $descriptionFactory->newSomeProperty(
+ new DIProperty( '_DTITLE' ),
+ $descriptionFactory->newValueDescription( new DIBlob( $displayTitle ) )
+ );
+
+ $query = new Query( $description );
+ $query->setLimit( 1 );
+ $query->setOption( Query::PROC_CONTEXT, 'PropertySpecificationLookup' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->queryPropertyValuesFor(
+ $query
+ );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+
+ // Cache results as a linked list attached to
+ // the property so that it can be purged all together
+
+ return new DIProperty( $dataItem->getDBKey() );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasUniquenessConstraint( DIProperty $property ) {
+
+ $hasUniquenessConstraint = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVUC' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $hasUniquenessConstraint = end( $dataItems )->getBoolean();
+ }
+
+ return $hasUniquenessConstraint;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ *
+ * @return DataItem|null
+ */
+ public function getPropertyGroup( DIProperty $property ) {
+
+ $dataItem = null;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_INST' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+
+ foreach ( $dataItems as $dataItem ) {
+ $pv = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $dataItem,
+ new DIProperty( '_PPGR' )
+ );
+
+ $di = end( $pv );
+
+ if ( $di instanceof DIBoolean && $di->getBoolean() ) {
+ return $dataItem;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return DataItem|null
+ */
+ public function getExternalFormatterUri( DIProperty $property ) {
+
+ $dataItem = null;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PEFU' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+ }
+
+ return $dataItem;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public function getAllowedPatternBy( DIProperty $property ) {
+
+ $allowsPattern = '';
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAP' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsPattern = end( $dataItems )->getString();
+ }
+
+ return $allowsPattern;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getAllowedValues( DIProperty $property ) {
+
+ $allowsValues = [];
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAL' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsValues = $dataItems;
+ }
+
+ return $allowsValues;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getAllowedListValues( DIProperty $property ) {
+
+ $allowsListValue = [];
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PVALI' ) );
+
+ if ( is_array( $dataItems ) && $dataItems !== [] ) {
+ $allowsListValue = $dataItems;
+ }
+
+ return $allowsListValue;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return integer|false
+ */
+ public function getDisplayPrecision( DIProperty $property ) {
+
+ $displayPrecision = false;
+ $dataItems = $this->getSpecification( $property, new DIProperty( '_PREC' ) );
+
+ if ( $dataItems !== false && $dataItems !== [] ) {
+ $dataItem = end( $dataItems );
+ $displayPrecision = abs( (int)$dataItem->getNumber() );
+ }
+
+ return $displayPrecision;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return array
+ */
+ public function getDisplayUnits( DIProperty $property ) {
+
+ $units = [];
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ new DIProperty( '_UNIT' )
+ );
+
+ if ( $dataItems !== false && $dataItems !== [] ) {
+ foreach ( $dataItems as $dataItem ) {
+ $units = array_merge( $units, preg_split( '/\s*,\s*/u', $dataItem->getString() ) );
+ }
+ }
+
+ return $units;
+ }
+
+ /**
+ * We try to cache anything to avoid unnecessary store connections or DB
+ * lookups. For cases where a property was changed, the EventDipatcher will
+ * receive a 'property.specification.change' event (emitted as soon as the content of
+ * a property page was altered) with PropertySpecificationLookup::resetCacheBy
+ * being invoked to remove the cache entry for that specific property.
+ *
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ * @param string $languageCode
+ * @param mixed|null $linker
+ *
+ * @return string
+ */
+ public function getPropertyDescriptionByLanguageCode( DIProperty $property, $languageCode = '', $linker = null ) {
+
+ // Take the linker into account (Special vs. in page rendering etc.)
+ $languageCode = $languageCode === '' ? $this->languageCode : $languageCode;
+ $key = '--pdesc:' . $languageCode . ':' . ( $linker === null ? '0' : '1' );
+
+ $blobStore = $this->cachedPropertyValuesPrefetcher->getBlobStore();
+
+ $container = $blobStore->read(
+ $this->cachedPropertyValuesPrefetcher->getRootHashFrom( $property->getCanonicalDiWikiPage() )
+ );
+
+ if ( $container->has( $key ) ) {
+ return $container->get( $key );
+ }
+
+ $localPropertyDescription = $this->findLocalPropertyDescription(
+ $property,
+ $linker,
+ $languageCode
+ );
+
+ // If a local property description wasn't available for a predefined property
+ // the try to find a system translation
+ if ( trim( $localPropertyDescription ) === '' && !$property->isUserDefined() ) {
+ $localPropertyDescription = $this->getPredefinedPropertyDescription( $property, $linker, $languageCode );
+ }
+
+ $container->set( $key, $localPropertyDescription );
+
+ $blobStore->save(
+ $container
+ );
+
+ return $localPropertyDescription;
+ }
+
+ private function getPredefinedPropertyDescription( $property, $linker, $languageCode ) {
+
+ $description = '';
+ $key = $property->getKey();
+
+ if ( ( $msgKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) === '' ) {
+ $msgKey = 'smw-property-predefined' . str_replace( '_', '-', strtolower( $key ) );
+ }
+
+ if ( !Message::exists( $msgKey ) ) {
+ return $description;
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $property
+ );
+
+ $label = $dataValue->getFormattedLabel();
+
+ $message = Message::get(
+ [ $msgKey, $label ],
+ $linker === null ? Message::ESCAPED : Message::PARSE,
+ $languageCode
+ );
+
+ return $message;
+ }
+
+ private function findLocalPropertyDescription( $property, $linker, $languageCode ) {
+
+ $text = '';
+ $descriptionProperty = new DIProperty( '_PDESC' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ $descriptionProperty
+ );
+
+ if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $descriptionProperty, $languageCode ) ) !== null ) {
+ $text = $dataValue->getShortWikiText( $linker );
+ }
+
+ return $text;
+ }
+
+ private function findPreferredPropertyLabel( $property, $languageCode ) {
+
+ $text = '';
+ $preferredProperty = new DIProperty( '_PPLB' );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $property->getCanonicalDiWikiPage(),
+ $preferredProperty
+ );
+
+ if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $preferredProperty, $languageCode ) ) !== null ) {
+ $text = $dataValue->getShortWikiText();
+ }
+
+ return $text;
+ }
+
+ private function findTextValueByLanguage( $dataItems, $property, $languageCode ) {
+
+ if ( $dataItems === null || $dataItems === [] ) {
+ return null;
+ }
+
+ foreach ( $dataItems as $dataItem ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ // Here a MonolingualTextValue was retunred therefore the method
+ // can be called without validation
+ $dv = $dataValue->getTextValueByLanguage( $languageCode );
+
+ if ( $dv !== null ) {
+ return $dv;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqExaminer.php
new file mode 100644
index 00000000..9f184a17
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqExaminer.php
@@ -0,0 +1,295 @@
+<?php
+
+namespace SMW;
+
+use SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator;
+use SMW\Protection\ProtectionValidator;
+use SMWDataItem as DataItem;
+
+/**
+ * Examines codified requirements for listed types of property specifications which
+ * in case of a violation returns a message with the details of that violation.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationReqExaminer {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var ProtectionValidator
+ */
+ private $protectionValidator;
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData;
+
+ /**
+ * @var boolean
+ */
+ private $changePropagationProtection = true;
+
+ /**
+ * @var DataItemFactory
+ */
+ private $dataItemFactory;
+
+ /**
+ * @var boolean
+ */
+ private $reqLock = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param ProtectionValidator $protectionValidator
+ */
+ public function __construct( Store $store, ProtectionValidator $protectionValidator ) {
+ $this->store = $store;
+ $this->protectionValidator = $protectionValidator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param SemanticData|null $semanticData
+ */
+ public function setSemanticData( SemanticData $semanticData = null ) {
+ $this->semanticData = $semanticData;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $changePropagationProtection
+ */
+ public function setChangePropagationProtection( $changePropagationProtection ) {
+ $this->changePropagationProtection = (bool)$changePropagationProtection;
+ }
+
+ /**
+ * Whether a specific property requires a lock nor not.
+ *
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function reqLock() {
+ return $this->reqLock;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return array|null
+ */
+ public function check( DIProperty $property ) {
+
+ $subject = $property->getCanonicalDiWikiPage();
+ $title = $subject->getTitle();
+
+ $semanticData = $this->store->getSemanticData( $subject );
+
+ if ( $this->semanticData === null ) {
+ $this->semanticData = $semanticData;
+ }
+
+ $this->reqLock = false;
+ $this->dataItemFactory = new DataItemFactory();
+
+ if ( $semanticData->hasProperty( new DIProperty( DIProperty::TYPE_CHANGE_PROP ) ) ) {
+ $severity = $this->changePropagationProtection ? 'error' : 'warning';
+ $this->reqLock = true;
+ return [
+ $severity,
+ 'smw-property-req-violation-change-propagation-locked-' . $severity,
+ $property->getLabel()
+ ];
+ }
+
+ if ( $this->reqLock === false && $this->protectionValidator->hasCreateProtection( $title ) ) {
+ $msg = 'smw-create-protection';
+
+ if ( $title->exists() ) {
+ $msg = 'smw-create-protection-exists';
+ }
+
+ return [
+ 'warning',
+ $msg,
+ $property->getLabel(),
+ $this->protectionValidator->getCreateProtectionRight()
+ ];
+ }
+
+ if ( $this->reqLock === false && $this->protectionValidator->hasEditProtection( $title ) ) {
+ return [
+ $property->isUserDefined() ? 'error' : 'warning',
+ 'smw-edit-protection',
+ $this->protectionValidator->getEditProtectionRight()
+ ];
+ }
+
+ if ( !$property->isUserDefined() ) {
+ return $this->checkTypeForPredefinedProperty( $property );
+ }
+
+ $type = $property->findPropertyTypeID();
+
+ if ( $type === '_ref_rec' || $type === '_rec' ) {
+ return $this->checkFieldList( $property );
+ }
+
+ if ( $type === '_eid' ) {
+ return $this->checkExternalFormatterUri( $property );
+ }
+
+ if ( $type === '_geo' ) {
+ return $this->checkMaps( $property );
+ }
+
+ if ( $this->semanticData->getOption( MandatoryTypePropertyAnnotator::IMPO_REMOVED_TYPE ) ) {
+ return $this->checkImportedVocabType( $property );
+ }
+ }
+
+ /**
+ * A violation occurs when a predefined property contains a `Has type` annotation
+ * that is incompatible with the default type.
+ */
+ private function checkTypeForPredefinedProperty( $property ) {
+
+ if ( $property->getKey() === '_EDIP' ) {
+ return $this->checkEditProtectionRight( $property );
+ }
+
+ if ( !$this->semanticData->hasProperty( $this->dataItemFactory->newDIProperty( '_TYPE' ) ) ) {
+ return;
+ }
+
+ $typeValues = $this->semanticData->getPropertyValues(
+ $this->dataItemFactory->newDIProperty( '_TYPE' )
+ );
+
+ if ( $typeValues !== [] ) {
+ list( $url, $type ) = explode( "#", end( $typeValues )->getSerialization() );
+ }
+
+ if ( DataTypeRegistry::getInstance()->isEqualByType( $type, $property->findPropertyTypeID() ) ) {
+ return;
+ }
+
+ $prop = $this->dataItemFactory->newDIProperty( $type );
+
+ return [
+ 'error',
+ 'smw-property-req-violation-predefined-type',
+ $property->getCanonicalLabel(),
+ $prop->getCanonicalLabel()
+ ];
+ }
+
+ /**
+ * Examines whether the setting `smwgEditProtectionRight` contains an appropriate
+ * value or is disabled in order for the `Is edit protected` property to function.
+ */
+ private function checkEditProtectionRight( $property ) {
+
+ if ( $this->protectionValidator->getEditProtectionRight() !== false ) {
+ return;
+ }
+
+ return [
+ 'warning',
+ 'smw-edit-protection-disabled',
+ $property->getCanonicalLabel()
+ ];
+ }
+
+ /**
+ * A violation occurs when a Reference or Record typed property does not denote
+ * a `Has fields` declaration.
+ */
+ private function checkFieldList( $property ) {
+
+ if ( $this->semanticData->hasProperty( $this->dataItemFactory->newDIProperty( '_LIST' ) ) ) {
+ return;
+ }
+
+ $prop = $this->dataItemFactory->newDIProperty( $property->findPropertyTypeID() );
+
+ return [
+ 'error',
+ 'smw-property-req-violation-missing-fields',
+ $property->getLabel(),
+ $prop->getCanonicalLabel()
+ ];
+ }
+
+ /**
+ * A violation occurs when the External Identifier typed property does not declare
+ * a `External formatter URI` declaration.
+ */
+ private function checkExternalFormatterUri( $property ) {
+
+ if ( $this->semanticData->hasProperty( $this->dataItemFactory->newDIProperty( '_PEFU' ) ) ) {
+ return;
+ }
+
+ return [
+ 'error',
+ 'smw-property-req-violation-missing-formatter-uri',
+ $property->getLabel()
+ ];
+ }
+
+ private function checkMaps( $property ) {
+
+ if ( defined( 'SM_VERSION' ) ) {
+ return;
+ }
+
+ return [
+ 'error',
+ 'smw-property-req-violation-missing-maps-extension',
+ $property->getLabel()
+ ];
+ }
+
+ /**
+ * A violation occurs when the `Imported from` property detects an incompatible
+ * `Has type` declaration.
+ */
+ private function checkImportedVocabType( $property ) {
+
+ $typeValues = $this->semanticData->getPropertyValues(
+ $this->dataItemFactory->newDIProperty( '_TYPE' )
+ );
+
+ $dataItem = $this->semanticData->getOption(
+ MandatoryTypePropertyAnnotator::IMPO_REMOVED_TYPE
+ );
+
+ if ( $dataItem instanceof DataItem && end( $typeValues )->equals( $dataItem ) ) {
+ return;
+ }
+
+ return [
+ 'warning',
+ 'smw-property-req-violation-import-type',
+ $property->getLabel()
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqMsgBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqMsgBuilder.php
new file mode 100644
index 00000000..aa17253b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/PropertySpecificationReqMsgBuilder.php
@@ -0,0 +1,323 @@
+<?php
+
+namespace SMW;
+
+use Html;
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationReqMsgBuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData;
+
+ /**
+ * @var PropertySpecificationReqExaminer
+ */
+ private $propertySpecificationReqExaminer;
+
+ /**
+ * @var array
+ */
+ private $propertyReservedNameList = [];
+
+ /**
+ * @var string
+ */
+ private $message = '';
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param PropertySpecificationReqExaminer $propertySpecificationReqExaminer
+ */
+ public function __construct( Store $store, PropertySpecificationReqExaminer $propertySpecificationReqExaminer ) {
+ $this->store = $store;
+ $this->propertySpecificationReqExaminer = $propertySpecificationReqExaminer;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData|null $semanticData
+ */
+ public function setSemanticData( SemanticData $semanticData = null ) {
+ $this->semanticData = $semanticData;
+
+ $this->propertySpecificationReqExaminer->setSemanticData(
+ $semanticData
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $propertyReservedNameList
+ */
+ public function setPropertyReservedNameList( array $propertyReservedNameList ) {
+
+ foreach ( $propertyReservedNameList as $name ) {
+
+ if ( strpos( $name, 'smw-property-reserved' ) !== false ) {
+ $name = Message::get( $name, Message::TEXT, Message::CONTENT_LANGUAGE );
+ }
+
+ $this->propertyReservedNameList[$name] = true;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function reqLock() {
+ return $this->propertySpecificationReqExaminer->reqLock();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string
+ */
+ public function getMessage() {
+ return $this->message;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ */
+ public function check( DIProperty $property ) {
+
+ $subject = $property->getCanonicalDiWikiPage();
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $property
+ );
+
+ $propertyName = $dataValue->getFormattedLabel();
+
+ $this->message = $this->checkUniqueness( $property, $propertyName );
+
+ if ( isset( $this->propertyReservedNameList[$propertyName] ) ) {
+ $this->message .= $this->createMessage(
+ [
+ 'error',
+ 'smw-property-name-reserved',
+ $propertyName
+ ]
+ );
+ }
+
+ $this->message .= $this->createMessage(
+ $this->propertySpecificationReqExaminer->check( $property )
+ );
+
+ if ( $this->propertySpecificationReqExaminer->reqLock() === false && ChangePropagationDispatchJob::hasPendingJobs( $subject ) ) {
+ $this->message .= $this->createMessage(
+ [
+ 'warning',
+ 'smw-property-req-violation-change-propagation-pending',
+ ChangePropagationDispatchJob::getPendingJobsCount( $subject )
+ ]
+ );
+ }
+
+ if ( $this->semanticData !== null && $this->semanticData->hasProperty( new DIProperty( '_ERRC' ) ) ) {
+ $this->message .= $this->findErrorMessages();
+ }
+
+ if ( $this->semanticData !== null && ( $props = $this->semanticData->getPropertyValues( new DIProperty( '_TYPE' ) ) ) && count( $props ) > 1 ) {
+ $this->message .= $this->createMessage(
+ [
+ 'warning',
+ 'smw-property-req-violation-type'
+ ]
+ );
+ }
+
+ if ( $property->isUserDefined() && wfMessage( 'smw-property-introductory-message-user' )->exists() ) {
+ $this->message .= $this->createIntroductoryMessage( 'smw-property-introductory-message-user', $propertyName );
+ }
+
+ if ( !$property->isUserDefined() && wfMessage( 'smw-property-introductory-message-special' )->exists() ) {
+ $this->message .= $this->createIntroductoryMessage( 'smw-property-introductory-message-special', $propertyName );
+ }
+
+ if ( wfMessage( 'smw-property-introductory-message' )->exists() ) {
+ $this->message .= $this->createIntroductoryMessage( 'smw-property-introductory-message', $propertyName );
+ }
+
+ if ( $property->isUserDefined() && $this->store->getPropertyTableInfoFetcher()->isFixedTableProperty( $property ) ) {
+ $this->message .= $this->createFixedTableMessage( $propertyName );
+ }
+
+ if ( !$property->isUserDefined() ) {
+ $this->message .= $this->createPredefinedPropertyMessage( $property, $propertyName );
+ }
+
+ $label = mb_strtolower( str_replace( ' ', '-', $propertyName ) );
+
+ if ( wfMessage( "smw-property-message-$label" )->exists() ) {
+ $this->message .= $this->createIntroductoryMessage( "smw-property-message-$label", $propertyName, false );
+ }
+ }
+
+ private function createMessage( $messsage ) {
+
+ if ( !is_array( $messsage ) ) {
+ return '';
+ }
+
+ $type = array_shift( $messsage );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => $messsage[0],
+ 'class' => 'plainlinks ' . ( $type !== '' ? 'smw-callout smw-callout-'. $type : '' )
+ ],
+ Message::get( $messsage, Message::PARSE, Message::USER_LANGUAGE )
+ );
+ }
+
+ private function createIntroductoryMessage( $msgKey, $propertyName, $class = true ) {
+
+ $message = wfMessage( $msgKey, $propertyName )->parse();
+
+ if ( $message === '' ) {
+ return '';
+ }
+
+ if ( $class === true ) {
+ $class = 'smw-callout smw-callout-info';
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => "$msgKey",
+ 'class' => "plainlinks $msgKey " . $class
+ ],
+ $message
+ );
+ }
+
+ private function createFixedTableMessage( $propertyName ) {
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'smw-property-content-fixedtable-message',
+ 'class' => 'plainlinks smw-callout smw-callout-info'
+ ],
+ wfMessage( 'smw-property-userdefined-fixedtable', $propertyName )->parse()
+ );
+ }
+
+ /**
+ * Returns an introductory text for a predefined property
+ *
+ * @note In order to enable a more detailed description for a specific
+ * predefined property a concatenated message key can be used (e.g
+ * 'smw-property-predefined' + <internal property key> => '_asksi' ) but
+ * because translatewiki.net doesn't handle `_` well, convert `_` to `-`
+ * resulting in 'smw-property-predefined-asksi' as translatable key
+ */
+ private function createPredefinedPropertyMessage( $property, $propertyName ) {
+
+ $key = $property->getKey();
+ $message = '';
+
+ if ( $property->isUserDefined() ) {
+ return $message;
+ }
+
+ if ( ( $messageKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) !== '' ) {
+ $messageKeyLong = $messageKey . '-long';
+ } else {
+ $messageKey = 'smw-property-predefined' . str_replace( '_', '-', strtolower( $key ) );
+ $messageKeyLong = 'smw-property-predefined-long' . str_replace( '_', '-', strtolower( $key ) );
+ }
+
+ if ( wfMessage( $messageKey )->exists() ) {
+ $message .= wfMessage( $messageKey, $propertyName )->parse();
+ } else {
+ $message .= wfMessage( 'smw-property-predefined-default', $propertyName )->parse();
+ }
+
+ if ( wfMessage( $messageKeyLong )->exists() ) {
+ $message .= ' ' . wfMessage( $messageKeyLong )->parse();
+ }
+
+ $message .= ' ' . wfMessage( 'smw-property-predefined-common' )->parse();
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'smw-property-content-predefined-message',
+ 'class' => 'smw-property-predefined-intro plainlinks'
+ ],
+ $message
+ );
+ }
+
+ private function findErrorMessages() {
+
+ $pv = $this->semanticData->getPropertyValues( new DIProperty( '_ERRC' ) );
+ $errors = [];
+
+ foreach ( $pv as $v ) {
+ $subSemanticData = $this->semanticData->findSubSemanticData(
+ $v->getSubobjectName()
+ );
+
+ foreach ( $subSemanticData->getPropertyValues( new DIProperty( '_ERRT' ) ) as $error ) {
+ $errors[] = Message::decode( $error->getString(), Message::PARSE, Message::USER_LANGUAGE );
+ }
+ }
+
+ if ( $errors === [] ) {
+ return '';
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'smw-property-error-list',
+ 'class' => 'plainlinks smw-callout smw-callout-error'
+ ],
+ count( $errors ) > 1 ? '<ul><li>' . implode( '</li><li>', $errors ) . '</li></ul>' : implode( '', $errors )
+ );
+ }
+
+ private function checkUniqueness( DIProperty $property, $propertyName ) {
+
+ if ( $this->store->getObjectIds()->isUnique( $property ) ) {
+ return '';
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => 'smw-property-uniqueness',
+ 'class' => 'smw-callout smw-callout-error plainlinks'
+ ],
+ Message::get( [ 'smw-property-label-uniqueness', $propertyName ], Message::PARSE, Message::USER_LANGUAGE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Protection/EditProtectionUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/Protection/EditProtectionUpdater.php
new file mode 100644
index 00000000..c5336abd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Protection/EditProtectionUpdater.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace SMW\Protection;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use SMW\DIProperty;
+use SMW\MediaWiki\Hooks\ArticleProtectComplete;
+use SMW\Message;
+use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
+use SMW\SemanticData;
+use User;
+use WikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EditProtectionUpdater implements LoggerAwareInterface {
+
+ /**
+ * @var WikiPage
+ */
+ private $wikiPage;
+
+ /**
+ * @var User
+ */
+ private $user;
+
+ /**
+ * @var boolean
+ */
+ private $isRestrictedUpdate = false;
+
+ /**
+ * @var boolean|string
+ */
+ private $editProtectionRight = false;
+
+ /**
+ * LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * @since 2.5
+ *
+ * @param WikiPage $wikiPage
+ * @param User|null $user
+ */
+ public function __construct( WikiPage $wikiPage, User $user = null ) {
+ $this->wikiPage = $wikiPage;
+ $this->user = $user;
+
+ if ( $this->user === null ) {
+ $this->user = $GLOBALS['wgUser'];
+ }
+ }
+
+ /**
+ * @see LoggerAwareInterface::setLogger
+ *
+ * @since 2.5
+ *
+ * @param LoggerInterface $logger
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|boolean $editProtectionRight
+ */
+ public function setEditProtectionRight( $editProtectionRight ) {
+ $this->editProtectionRight = $editProtectionRight;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isRestrictedUpdate() {
+ return $this->isRestrictedUpdate;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ */
+ public function doUpdateFrom( SemanticData $semanticData ) {
+
+ // Do nothing
+ if ( $this->editProtectionRight === false ) {
+ return;
+ }
+
+ list( $isEditProtected, $isAnnotationBySystem ) = $this->fetchEditProtectedInfo( $semanticData );
+
+ $title = $this->wikiPage->getTitle();
+
+ if ( $title === null ) {
+ return;
+ }
+
+ $restrictions = array_flip( $title->getRestrictions( 'edit' ) );
+
+ // No `Is edit protected` was found and the restriction doesn't contain
+ // a matchable `editProtectionRight`
+ if ( $isEditProtected === null && !isset( $restrictions[$this->editProtectionRight] ) ) {
+ return $this->log( __METHOD__ . ' no update required' );
+ }
+
+ if ( $isEditProtected && !isset( $restrictions[$this->editProtectionRight] ) && !$isAnnotationBySystem ) {
+ return $this->doUpdateRestrictions( $isEditProtected );
+ }
+
+ if ( $isEditProtected && $title->isProtected( 'edit' ) || !$isEditProtected && !$title->isProtected( 'edit' ) ) {
+ return $this->log( __METHOD__ . ' Status already set, no update required' );
+ }
+
+ $this->doUpdateRestrictions( $isEditProtected );
+ }
+
+ private function fetchEditProtectedInfo( $semanticData ) {
+
+ // Whether or not the update was invoked by the ArticleProtectComplete hook
+ $this->isRestrictedUpdate = $semanticData->getOption( ArticleProtectComplete::RESTRICTED_UPDATE ) === true;
+ $property = new DIProperty( '_EDIP' );
+
+ $isEditProtected = null;
+ $isAnnotationBySystem = false;
+
+ $dataItems = $semanticData->getPropertyValues(
+ $property
+ );
+
+ if ( $dataItems !== [] ) {
+ $isEditProtected = false;
+
+ // In case of two competing values, true always wins
+ foreach ( $dataItems as $dataItem ) {
+
+ $isEditProtected = $dataItem->getBoolean();
+
+ if ( $isEditProtected ) {
+ break;
+ }
+ }
+
+ $isAnnotationBySystem = $dataItem->getOption( EditProtectedPropertyAnnotator::SYSTEM_ANNOTATION );
+ }
+
+ return [ $isEditProtected, $isAnnotationBySystem ];
+ }
+
+ private function doUpdateRestrictions( $isEditProtected ) {
+
+ $protections = [];
+ $expiry = [];
+
+ if ( $isEditProtected ) {
+ $this->log( __METHOD__ . ' add protection on edit, move' );
+
+ $protections = [
+ 'edit' => $this->editProtectionRight,
+ 'move' => $this->editProtectionRight
+ ];
+
+ $expiry = [
+ 'edit' => 'infinity',
+ 'move' => 'infinity'
+ ];
+ } else {
+ $this->log( __METHOD__ . ' remove protection on edit, move' );
+ $protections = [];
+ $expiry = [];
+ }
+
+ $reason = Message::get( 'smw-edit-protection-auto-update' );
+ $cascade = false;
+
+ $status = $this->wikiPage->doUpdateRestrictions(
+ $protections,
+ $expiry,
+ $cascade,
+ $reason,
+ $this->user
+ );
+ }
+
+ private function log( $message, $context = [] ) {
+
+ if ( $this->logger === null ) {
+ return;
+ }
+
+ $this->logger->info( $message, $context );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Protection/ProtectionValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/Protection/ProtectionValidator.php
new file mode 100644
index 00000000..ff8103ea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Protection/ProtectionValidator.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW\Protection;
+
+use Onoi\Cache\Cache;
+use SMW\CachedPropertyValuesPrefetcher;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use Title;
+
+/**
+ * Handles protection validation.
+ *
+ * The lookup is cached using the `CachedPropertyValuesPrefetcher` to avoid a
+ * continued access to the Store or DB layer.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ProtectionValidator {
+
+ /**
+ * Reference used in InMemoryPoolCache
+ */
+ const POOLCACHE_ID = 'protection.validator';
+
+ /**
+ * @var CachedPropertyValuesPrefetcher
+ */
+ private $cachedPropertyValuesPrefetcher;
+
+ /**
+ * @var Cache
+ */
+ private $intermediaryMemoryCache;
+
+ /**
+ * @var boolean|string
+ */
+ private $editProtectionRight = false;
+
+ /**
+ * @var boolean|string
+ */
+ private $createProtectionRight = false;
+
+ /**
+ * @var boolean|string
+ */
+ private $changePropagationProtection = true;
+
+ /**
+ * @since 2.5
+ *
+ * @param CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher
+ * @param Cache $intermediaryMemoryCache
+ */
+ public function __construct( CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher, Cache $intermediaryMemoryCache ) {
+ $this->cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher;
+ $this->intermediaryMemoryCache = $intermediaryMemoryCache;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|boolean $editProtectionRight
+ */
+ public function setEditProtectionRight( $editProtectionRight ) {
+ $this->editProtectionRight = $editProtectionRight;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|false
+ */
+ public function getEditProtectionRight() {
+ return $this->editProtectionRight;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|boolean $createProtectionRight
+ */
+ public function setCreateProtectionRight( $createProtectionRight ) {
+ $this->createProtectionRight = $createProtectionRight;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string|false
+ */
+ public function getCreateProtectionRight() {
+ return $this->createProtectionRight;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $changePropagationProtection
+ */
+ public function setChangePropagationProtection( $changePropagationProtection ) {
+ $this->changePropagationProtection = (bool)$changePropagationProtection;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ */
+ private function resetCacheBy( DIWikiPage $subject ) {
+ $this->cachedPropertyValuesPrefetcher->resetCacheBy( $subject );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function hasEditProtectionOnNamespace( Title $title ) {
+ return $this->editProtectionRight && $this->checkProtection( DIWikiPage::newFromTitle( $title )->asBase() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function hasChangePropagationProtection( Title $title ) {
+
+ $subject = DIWikiPage::newFromTitle( $title )->asBase();
+ $namespace = $subject->getNamespace();
+
+ if ( ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) || $this->changePropagationProtection === false ) {
+ return false;
+ }
+
+ return $this->checkProtection( $subject, new DIProperty( '_CHGPRO' ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function hasProtection( Title $title ) {
+ return $this->checkProtection( DIWikiPage::newFromTitle( $title )->asBase() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function hasCreateProtection( Title $title ) {
+ return $this->createProtectionRight && !$title->userCan( 'edit' );
+ }
+
+ /**
+ * @note There is not direct validation of the permission within this method,
+ * it is done by the Title::userCan when probing against the User and hooks
+ * that carry out the permission check including the validation provided by
+ * SMW's `PermissionPthValidator`.
+ *
+ * @since 2.5
+ *
+ * @param Title $title
+ *
+ * @return boolean
+ */
+ public function hasEditProtection( Title $title ) {
+ return !$title->userCan( 'edit' ) && $this->checkProtection( DIWikiPage::newFromTitle( $title )->asBase() );
+ }
+
+ private function checkProtection( $subject, $property = null ) {
+
+ if ( $property === null ) {
+ $property = new DIProperty( '_EDIP' );
+ }
+
+ $key = $subject->getHash() . $property->getKey();
+ $hasProtection = false;
+
+ if ( $this->intermediaryMemoryCache->contains( $key ) ) {
+ return $this->intermediaryMemoryCache->fetch( $key );
+ }
+
+ // Set editProtectionRight to influence the key to detect changes
+ // before the cache is evicted
+ $requestOptions = new RequestOptions();
+ $requestOptions->addExtraCondition( $this->editProtectionRight );
+
+ $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues(
+ $subject,
+ $property,
+ $requestOptions
+ );
+
+ if ( $dataItems !== null && $dataItems !== [] ) {
+ $hasProtection = $property->getKey() === '_EDIP' ? end( $dataItems )->getBoolean() : true;
+ }
+
+ $this->intermediaryMemoryCache->save( $key, $hasProtection );
+
+ return $hasProtection;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/DebugFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/DebugFormatter.php
new file mode 100644
index 00000000..d9c84dbf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/DebugFormatter.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\ProcessingErrorMsgHandler;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class DebugFormatter {
+
+ const JSON_FORMAT = 'json';
+
+ /**
+ * @var boolean
+ */
+ private static $explainFormat = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $explainFormat
+ */
+ public static function setExplainFormat( $explainFormat ) {
+ if ( $explainFormat === self::JSON_FORMAT ) {
+ self::$explainFormat = $explainFormat;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function getFormat( $type ) {
+
+ $format = '';
+
+ // Use a more expressive explain output
+ // https://dev.mysql.com/doc/refman/5.6/en/explain.html
+ // https://mariadb.com/kb/en/mariadb/explain-formatjson-in-mysql/
+ if ( $type === 'mysql' && self::$explainFormat === self::JSON_FORMAT ) {
+ $format = 'FORMAT=json';
+ }
+
+ return $format;
+ }
+
+ /**
+ * Generate textual debug output that shows an arbitrary list of informative
+ * fields. Used for formatting query debug output.
+ *
+ * @note All strings given must be usable and safe in wiki and HTML
+ * contexts.
+ *
+ * @param $storeName string name of the storage backend for which this is generated
+ * @param $entries array of name => value of informative entries to display
+ * @param $query SMWQuery or null, if given add basic data about this query as well
+ *
+ * @return string
+ */
+ public static function getStringFrom( $storeName, array $entries, Query $query = null ) {
+
+ if ( $query instanceof Query ) {
+ $preEntries = [];
+ $preEntries['ASK Query'] = '<div class="smwpre">' . str_replace( '[', '&#91;', $query->getDescription()->getQueryString() ) . '</div>';
+ $entries = array_merge( $preEntries, $entries );
+ $entries['Query Metrics'] = 'Query-Size:' . $query->getDescription()->getSize() . '<br />' .
+ 'Query-Depth:' . $query->getDescription()->getDepth();
+ $errors = '';
+
+ $queryErrors = ProcessingErrorMsgHandler::normalizeAndDecodeMessages(
+ $query->getErrors()
+ );
+
+ foreach ( $queryErrors as $error ) {
+ $errors .= $error . '<br />';
+ }
+
+ if ( $errors === '' ) {
+ $errors = 'None';
+ }
+
+ $entries['Errors and Warnings'] = $errors;
+ }
+
+ $result = '<div class="smw-debug" style="border: 5px dotted #ffcc00; background: #FFF0BD; padding: 20px; margin-bottom: 10px;">' .
+ "<div class='smw-column-header'><big>$storeName debug output</big></div>";
+
+ foreach ( $entries as $header => $information ) {
+ $result .= "<div class='smw-column-header'>$header</div>";
+
+ if ( $information !== '' ) {
+ $result .= "$information";
+ }
+ }
+
+ $result .= '</div>';
+
+ return $result;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ * @param array $rows
+ *
+ * @return string
+ */
+ public static function prettifyExplain( $type, $res ) {
+
+ $output = '';
+
+ // https://dev.mysql.com/doc/refman/5.0/en/explain-output.html
+ if ( $type === 'mysql' ) {
+ $output .= '<div class="smwpre" style="word-break:normal;">' .
+ '<table class="" style="border-spacing: 5px;"><tr>' .
+ '<th style="text-align: left;">ID</th>'.
+ '<th style="text-align: left;">select_type</th>'.
+ '<th style="text-align: left;">table</th>'.
+ '<th style="text-align: left;">type</th>'.
+ '<th style="text-align: left;">possible_keys</th>'.
+ '<th style="text-align: left;">key</th>'.
+ '<th style="text-align: left;">key_len</th>'.
+ '<th style="text-align: left;">ref</th>'.
+ '<th style="text-align: left;">rows</th>'.
+ '<th style="text-align: left;">Extra</th></tr>';
+
+ foreach ( $res as $row ) {
+
+ if ( isset( $row->EXPLAIN ) ) {
+ return '<div class="smwpre">' . $row->EXPLAIN . '</div>';
+ }
+
+ $output .= "<tr><td>" . $row->id .
+ "</td><td>" . $row->select_type .
+ "</td><td>" . $row->table .
+ "</td><td>" . $row->type .
+ "</td><td>" . $row->possible_keys .
+ "</td><td>" . $row->key .
+ "</td><td>" . $row->key_len .
+ "</td><td>" . $row->ref .
+ "</td><td>" . $row->rows .
+ "</td><td>" . $row->Extra . "</td></tr>";
+ }
+
+ $output .= '</table></div>';
+ }
+
+ if ( $type === 'postgres' ) {
+ $output .= '<div class="smwpre">';
+
+ foreach ( $res as $row ) {
+ foreach ( $row as $key => $value ) {
+ $output .= str_replace( [ ' ', '->' ], [ '&nbsp;', '└── ' ], $value ) .'<br>';
+ }
+ }
+
+ $output .= '</div>';
+ }
+
+ // SQlite doesn't support this
+ if ( $type === 'sqlite' ) {
+ $output .= 'Not supported.';
+ }
+
+ return $output;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $sparql
+ *
+ * @return string
+ */
+ public static function prettifySparql( $sparql ) {
+
+ $sparql = str_replace(
+ [
+ '[',
+ ':',
+ ' ',
+ '<',
+ '>'
+ ],
+ [
+ '&#91;',
+ '&#x003A;',
+ '&#x0020;',
+ '&#x3C;',
+ '&#x3E;'
+ ],
+ $sparql
+ );
+
+ return '<div class="smwpre">' . $sparql . '</div>';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $sql
+ * @param string $alias
+ *
+ * @return string
+ */
+ public static function prettifySql( $sql, $alias ) {
+
+ $sql = str_replace(
+ [
+ "SELECT DISTINCT",
+ "FROM",
+ "INNER JOIN",
+ "LEFT OUTER JOIN",
+ "LEFT JOIN",
+ "RIGHT JOIN",
+ "WHERE",
+ "ORDER BY",
+ "GROUP BY",
+ "LIMIT",
+ "OFFSET",
+ "AND $alias.smw_",
+ ",$alias.smw_",
+ "AND (",
+ "))",
+ "(("
+ ],
+ [
+ "SELECT DISTINCT<br>&nbsp;",
+ "<br>FROM<br>&nbsp;",
+ "<br>INNER JOIN<br>&nbsp;",
+ "<br>LEFT OUTER JOIN<br>&nbsp;",
+ "<br>LEFT JOIN<br>&nbsp;",
+ "<br>RIGHT JOIN<br>&nbsp;",
+ "<br>WHERE<br>&nbsp;",
+ "<br>ORDER BY<br>&nbsp;",
+ "<br>GROUP BY<br>&nbsp;",
+ "<br>LIMIT<br>&nbsp;",
+ "<br>OFFSET<br>&nbsp;",
+ "<br>&nbsp;&nbsp;AND $alias.smw_",
+ ",<br>&nbsp;&nbsp;$alias.smw_",
+ "<br>&nbsp;&nbsp;&nbsp;AND (",
+ ")<br>&nbsp;&nbsp;)",
+ "(<br>&nbsp;&nbsp;&nbsp;("
+ ],
+ $sql
+ );
+
+ return '<div class="smwpre">' . $sql . '</div>';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Deferred.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Deferred.php
new file mode 100644
index 00000000..23ed1382
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Deferred.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Query;
+
+use Html;
+use ParserOutput;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Deferred {
+
+ /**
+ * Identifies the showMode
+ */
+ const SHOW_MODE = 'dq.showmode';
+
+ /**
+ * Identifies unparsed parameters
+ */
+ const QUERY_PARAMETERS = 'dq.parameters';
+
+ /**
+ * Identifies the @control element
+ */
+ const CONTROL_ELEMENT = 'dq.control';
+
+ /**
+ * @since 3.0
+ *
+ * @param ParserOutput $parserOutput
+ */
+ public static function registerResources( ParserOutput $parserOutput ) {
+ $parserOutput->addModuleStyles( 'ext.smw.deferred.styles' );
+ $parserOutput->addModules( 'ext.smw.deferred' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return string
+ */
+ public static function buildHTML( Query $query ) {
+
+ $isShowMode = $query->getOption( self::SHOW_MODE );
+ $params = $query->getOption( 'query.params' );
+
+ // Ensures that a generated string can appear next to another text
+ $element = $isShowMode ? 'span' : 'div';
+
+ $result = Html::rawElement(
+ $element,
+ [
+ 'class' => 'smw-deferred-query' . ( isset( $params['class'] ) ? ' ' . $params['class'] : '' ),
+ 'data-query' => json_encode(
+ [
+ 'query' => trim( $query->getOption( self::QUERY_PARAMETERS ) ),
+ 'params' => $params,
+ 'limit' => $query->getLimit(),
+ 'offset' => $query->getOffset(),
+ 'max' => $GLOBALS['smwgQMaxInlineLimit'],
+ 'cmd' => $isShowMode ? 'show' : 'ask'
+ ]
+ )
+ ],
+ Html::rawElement(
+ $element,
+ [
+ 'id' => 'deferred-control',
+ 'data-control' => $isShowMode ? '' : $query->getOption( self::CONTROL_ELEMENT )
+ ]
+ ) . Html::rawElement(
+ $element,
+ [
+ 'id' => 'deferred-output',
+ 'class' => 'smw-loading-image-dots'
+ ]
+ )
+ );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/DescriptionFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/DescriptionFactory.php
new file mode 100644
index 00000000..2a294abe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/DescriptionFactory.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DescriptionFactory {
+
+ /**
+ * @since 2.4
+ *
+ * @param DataItem $dataItem
+ * @param DIProperty|null $property = null
+ * @param integer $comparator
+ *
+ * @return ValueDescription
+ */
+ public function newValueDescription( DataItem $dataItem, DIProperty $property = null, $comparator = SMW_CMP_EQ ) {
+ return new ValueDescription( $dataItem, $property, $comparator );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ * @param Description $description
+ *
+ * @return SomeProperty
+ */
+ public function newSomeProperty( DIProperty $property, Description $description ) {
+ return new SomeProperty( $property, $description );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return ThingDescription
+ */
+ public function newThingDescription() {
+ return new ThingDescription();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Description[] $descriptions
+ *
+ * @return Disjunction
+ */
+ public function newDisjunction( $descriptions = [] ) {
+ return new Disjunction( $descriptions );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Description[] $descriptions
+ *
+ * @return Conjunction
+ */
+ public function newConjunction( $descriptions = [] ) {
+ return new Conjunction( $descriptions );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $ns
+ *
+ * @return NamespaceDescription
+ */
+ public function newNamespaceDescription( $ns ) {
+ return new NamespaceDescription( $ns );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage|[] $category
+ *
+ * @return ClassDescription
+ */
+ public function newClassDescription( $category ) {
+ return new ClassDescription( $category );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $concept
+ *
+ * @return ConceptDescription
+ */
+ public function newConceptDescription( DIWikiPage $concept ) {
+ return new ConceptDescription( $concept );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DataValue $dataValue
+ *
+ * @return Description
+ */
+ public function newFromDataValue( DataValue $dataValue ) {
+
+ if ( !$dataValue->isValid() ) {
+ return $this->newThingDescription();
+ }
+
+ // Avoid circular reference when called from outside of the DV context
+ $dataValue->setOption( DataValue::OPT_QUERY_CONTEXT, true );
+
+ $description = $dataValue->getQueryDescription( $dataValue->getWikiValue() );
+
+ if ( $dataValue->getProperty() === null ) {
+ return $description;
+ }
+
+ return $this->newSomeProperty(
+ $dataValue->getProperty(),
+ $description
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/FingerprintNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/FingerprintNotFoundException.php
new file mode 100644
index 00000000..903516c7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/FingerprintNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Query\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FingerprintNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/ResultFormatNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/ResultFormatNotFoundException.php
new file mode 100644
index 00000000..06040ed9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Exception/ResultFormatNotFoundException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\Query\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ResultFormatNotFoundException extends RuntimeException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Excerpts.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Excerpts.php
new file mode 100644
index 00000000..d16f5e8f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Excerpts.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DIWikiPage;
+
+/**
+ * Record excerpts for query results that support an excerpt retrieval function.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Excerpts {
+
+ /**
+ * @var []
+ */
+ protected $excerpts = [];
+
+ /**
+ * @var boolean
+ */
+ protected $noHighlight = false;
+
+ /**
+ * @var boolean
+ */
+ protected $hasHighlight = false;
+
+ /**
+ * @var boolean
+ */
+ protected $stripTags = true;
+
+ /**
+ * @since 3.0
+ */
+ public function noHighlight() {
+ $this->noHighlight = true;
+ }
+
+ /**
+ * @note The hash is expected to be equivalent to DIWikiPage::getHash to
+ * easily match result subjects available in an QueryResult instance.
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage|string $hash
+ * @param string|integer $score
+ */
+ public function addExcerpt( $hash, $excerpt ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ $this->excerpts[] = [ $hash, $excerpt ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|string $hash
+ *
+ * @return string|integer|false
+ */
+ public function getExcerpt( $hash ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ foreach ( $this->excerpts as $map ) {
+ if ( $map[0] === $hash ) {
+ return $map[1];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getExcerpts() {
+ return $this->excerpts;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasHighlight() {
+ return $this->hasHighlight;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ExportPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ExportPrinter.php
new file mode 100644
index 00000000..bb4e3871
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ExportPrinter.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\Query;
+
+use SMWQueryResult as QueryResult;
+
+/**
+ * Interface for SMW export related result printers
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface ExportPrinter extends ResultPrinter {
+
+ /**
+ * Outputs the result as file.
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $queryResult
+ * @param array $params
+ */
+ public function outputAsFile( QueryResult $queryResult, array $params );
+
+ /**
+ * Some printers do not mainly produce embeddable HTML or Wikitext, but
+ * produce stand-alone files. An example is RSS or iCalendar. This function
+ * returns the mimetype string that this file would have, or FALSE if no
+ * standalone files are produced.
+ *
+ * If this function returns something other than FALSE, then the printer will
+ * not be regarded as a printer that displays in-line results. This is used to
+ * determine if a file output should be generated in Special:Ask.
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $queryResult
+ *
+ * @return string
+ */
+ public function getMimeType( QueryResult $queryResult );
+
+ /**
+ * Some printers can produce not only embeddable HTML or Wikitext, but
+ * can also produce stand-alone files. An example is RSS or iCalendar.
+ * This function returns a filename that is to be sent to the caller
+ * in such a case (the default filename is created by browsers from the
+ * URL, and it is often not pretty).
+ *
+ * @param QueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( QueryResult $queryResult );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ClassDescription.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ClassDescription.php
new file mode 100644
index 00000000..273193ac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ClassDescription.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use Exception;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+
+/**
+ * Description of a single class as given by a wiki category, or of a
+ * disjunction of such classes. Corresponds to (disjunctions of) atomic classes
+ * in OWL and to (unions of) classes in RDF.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class ClassDescription extends Description {
+
+ /**
+ * @var array of DIWikiPage
+ */
+ protected $m_diWikiPages;
+
+ /**
+ * @var integer|null
+ */
+ protected $hierarchyDepth;
+
+ /**
+ * Constructor.
+ *
+ * @param mixed $content DIWikiPage or array of DIWikiPage
+ *
+ * @throws Exception
+ */
+ public function __construct( $content ) {
+ if ( $content instanceof DIWikiPage ) {
+ $this->m_diWikiPages = [ $content ];
+ } elseif ( is_array( $content ) ) {
+ $this->m_diWikiPages = $content;
+ } else {
+ throw new Exception( "ClassDescription::__construct(): parameter must be an DIWikiPage object or an array of such objects." );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $hierarchyDepth
+ */
+ public function setHierarchyDepth( $hierarchyDepth ) {
+
+ if ( $hierarchyDepth > $GLOBALS['smwgQSubcategoryDepth'] ) {
+ $hierarchyDepth = $GLOBALS['smwgQSubcategoryDepth'];
+ }
+
+ $this->hierarchyDepth = $hierarchyDepth;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer|null
+ */
+ public function getHierarchyDepth() {
+ return $this->hierarchyDepth;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ClassDescription $description
+ *
+ * @return boolean
+ */
+ public function isMergableDescription( ClassDescription $description ) {
+
+ if ( isset( $this->isNegation ) && isset( $description->isNegation ) ) {
+ return true;
+ }
+
+ if ( !isset( $this->isNegation ) && !isset( $description->isNegation ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $dataItem
+ */
+ public function addClass( DIWikiPage $dataItem ) {
+ $this->m_diWikiPages[] = $dataItem;
+ }
+
+ /**
+ * @param ClassDescription $description
+ */
+ public function addDescription( ClassDescription $description ) {
+ $this->m_diWikiPages = array_merge( $this->m_diWikiPages, $description->getCategories() );
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+
+ $hash = [];
+
+ foreach ( $this->m_diWikiPages as $subject ) {
+ $hash[$subject->getHash()] = true;
+ }
+
+ ksort( $hash );
+ $extra = ( isset( $this->isNegation ) ? '|' . $this->isNegation : '' );
+
+ return 'Cl:' . md5( implode( '|', array_keys( $hash ) ) . $this->hierarchyDepth . $extra );
+ }
+
+ /**
+ * @return array of DIWikiPage
+ */
+ public function getCategories() {
+ return $this->m_diWikiPages;
+ }
+
+ public function getQueryString( $asValue = false ) {
+
+ $first = true;
+ $namespaceText = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ foreach ( $this->m_diWikiPages as $wikiPage ) {
+ $wikiValue = DataValueFactory::getInstance()->newDataValueByItem( $wikiPage, null );
+ if ( $first ) {
+ $result = '[[' . $namespaceText . ':' . ( isset( $this->isNegation ) ? '!' : '' ) . $wikiValue->getText();
+ $first = false;
+ } else {
+ $result .= '||' . ( isset( $this->isNegation ) ? '!' : '' ) . $wikiValue->getText();
+ }
+ }
+
+ if ( $this->hierarchyDepth !== null ) {
+ $result .= '|+depth=' . $this->hierarchyDepth;
+ }
+
+ $result .= ']]';
+
+ if ( $asValue ) {
+ return ' <q>' . $result . '</q> ';
+ }
+
+ return $result;
+ }
+
+ public function isSingleton() {
+ return false;
+ }
+
+ public function getSize() {
+
+ if ( $GLOBALS['smwgQSubcategoryDepth'] > 0 ) {
+ return 1; // disj. of cats should not cause much effort if we compute cat-hierarchies anyway!
+ }
+
+ return count( $this->m_diWikiPages );
+ }
+
+ public function getQueryFeatures() {
+
+ if ( count( $this->m_diWikiPages ) > 1 ) {
+ return SMW_CATEGORY_QUERY | SMW_DISJUNCTION_QUERY;
+ }
+
+ return SMW_CATEGORY_QUERY;
+ }
+
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+
+ if ( $maxsize >= $this->getSize() ) {
+ $maxsize = $maxsize - $this->getSize();
+ return $this;
+ } elseif ( $maxsize <= 0 ) {
+ $log[] = $this->getQueryString();
+ $result = new ThingDescription();
+ } else {
+ $result = new ClassDescription( array_slice( $this->m_diWikiPages, 0, $maxsize ) );
+ $rest = new ClassDescription( array_slice( $this->m_diWikiPages, $maxsize ) );
+
+ $result->setHierarchyDepth(
+ $this->getHierarchyDepth()
+ );
+
+ $log[] = $rest->getQueryString();
+ $maxsize = 0;
+ }
+
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ConceptDescription.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ConceptDescription.php
new file mode 100644
index 00000000..482c04d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ConceptDescription.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+
+/**
+ * Description of a single class as described by a concept page in the wiki.
+ * Corresponds to classes in (the EL fragment of) OWL DL, and to some extent to
+ * tree-shaped queries in SPARQL.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class ConceptDescription extends Description {
+
+ /**
+ * @var DIWikiPage
+ */
+ private $concept;
+
+ /**
+ * @param DIWikiPage $concept
+ */
+ public function __construct( DIWikiPage $concept ) {
+ $this->concept = $concept;
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+ return 'Co:' . md5( $this->concept->getHash() );
+ }
+
+ /**
+ * @return DIWikiPage
+ */
+ public function getConcept() {
+ return $this->concept;
+ }
+
+ public function getQueryString( $asValue = false ) {
+
+ $pageValue = DataValueFactory::getInstance()->newDataValueByItem( $this->concept, null );
+ $result = '[[' . $pageValue->getPrefixedText() . ']]';
+
+ if ( $asValue ) {
+ return ' <q>' . $result . '</q> ';
+ }
+
+ return $result;
+ }
+
+ public function isSingleton() {
+ return false;
+ }
+
+ public function getQueryFeatures() {
+ return SMW_CONCEPT_QUERY;
+ }
+
+ ///NOTE: getSize and getDepth /could/ query the store to find the real size
+ /// of the concept. But it is not clear if this is desirable anyway, given that
+ /// caching structures may be established for retrieving concepts more quickly.
+ /// Inspecting those would require future requests to the store, and be very
+ /// store specific.
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Conjunction.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Conjunction.php
new file mode 100644
index 00000000..be5e8dbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Conjunction.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace SMW\Query\Language;
+
+/**
+ * Description of a collection of many descriptions, all of which
+ * must be satisfied (AND, conjunction).
+ *
+ * Corresponds to conjunction in OWL and SPARQL. Not available in RDFS.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class Conjunction extends Description {
+
+ /**
+ * @var Description[]
+ */
+ protected $descriptions = [];
+
+ /**
+ * @since 1.6
+ *
+ * @param array $descriptions
+ */
+ public function __construct( array $descriptions = [] ) {
+ foreach ( $descriptions as $description ) {
+ $this->addDescription( $description );
+ }
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+
+ if ( $this->fingerprint !== null ) {
+ return $this->fingerprint;
+ }
+
+ $fingerprint = [];
+
+ // Filter equal signatures
+ foreach ( $this->descriptions as $description ) {
+ $fingerprint[$description->getFingerprint()] = true;
+ }
+
+ // Sorting to generate a constant fingerprint independent of its
+ // position within a conjunction ( [Foo]][[Bar]], [[Bar]][[Foo]])
+ ksort( $fingerprint );
+
+ return $this->fingerprint = 'C:' . md5( implode( '|', array_keys( $fingerprint ) ) );
+ }
+
+ public function getDescriptions() {
+ return $this->descriptions;
+ }
+
+ public function addDescription( Description $description ) {
+
+ $this->fingerprint = null;
+
+ if ( ! ( $description instanceof ThingDescription ) ) {
+ if ( $description instanceof Conjunction ) { // absorb sub-conjunctions
+ foreach ( $description->getDescriptions() as $subdesc ) {
+ $this->descriptions[$subdesc->getFingerprint()] = $subdesc;
+ }
+ } else {
+ $this->descriptions[$description->getFingerprint()] = $description;
+ }
+
+ // move print descriptions downwards
+ ///TODO: This may not be a good solution, since it does modify $description and since it does not react to future changes
+ $this->m_printreqs = array_merge( $this->m_printreqs, $description->getPrintRequests() );
+ $description->setPrintRequests( [] );
+ }
+
+ $fingerprint = $this->getFingerprint();
+
+ foreach ( $this->descriptions as $description ) {
+ $description->setMembership( $fingerprint );
+ }
+ }
+
+ public function getQueryString( $asvalue = false ) {
+ $result = '';
+
+ foreach ( $this->descriptions as $desc ) {
+ $result .= ( $result ? ' ' : '' ) . $desc->getQueryString( false );
+ }
+
+ if ( $result === '' ) {
+ return $asvalue ? '+' : '';
+ }
+
+ // <q> not needed for stand-alone conjunctions (AND binds stronger than OR)
+ return $asvalue ? " <q>{$result}</q> " : $result;
+ }
+
+ public function isSingleton() {
+ foreach ( $this->descriptions as $d ) {
+ if ( $d->isSingleton() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function getSize() {
+ $size = 0;
+
+ foreach ( $this->descriptions as $desc ) {
+ $size += $desc->getSize();
+ }
+
+ return $size;
+ }
+
+ public function getDepth() {
+ $depth = 0;
+
+ foreach ( $this->descriptions as $desc ) {
+ $depth = max( $depth, $desc->getDepth() );
+ }
+
+ return $depth;
+ }
+
+ public function getQueryFeatures() {
+ $result = SMW_CONJUNCTION_QUERY;
+
+ foreach ( $this->descriptions as $desc ) {
+ $result = $result | $desc->getQueryFeatures();
+ }
+
+ return $result;
+ }
+
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+ if ( $maxsize <= 0 ) {
+ $log[] = $this->getQueryString();
+ return new ThingDescription();
+ }
+
+ $prunelog = [];
+ $newdepth = $maxdepth;
+ $result = new Conjunction();
+
+ foreach ( $this->descriptions as $desc ) {
+ $restdepth = $maxdepth;
+ $result->addDescription( $desc->prune( $maxsize, $restdepth, $prunelog ) );
+ $newdepth = min( $newdepth, $restdepth );
+ }
+
+ if ( count( $result->getDescriptions() ) > 0 ) {
+ $log = array_merge( $log, $prunelog );
+ $maxdepth = $newdepth;
+
+ if ( count( $result->getDescriptions() ) == 1 ) { // simplify unary conjunctions!
+ $descriptions = $result->getDescriptions();
+ $result = array_shift( $descriptions );
+ }
+
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ } else {
+ $log[] = $this->getQueryString();
+
+ $result = new ThingDescription();
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Description.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Description.php
new file mode 100644
index 00000000..5e5a0ffc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Description.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use SMW\Query\Exception\FingerprintNotFoundException;
+use SMW\Query\PrintRequest;
+
+/**
+ * Abstract base class for all descriptions
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+abstract class Description {
+
+ /**
+ * @var PrintRequest[]
+ */
+ protected $m_printreqs = [];
+
+ /**
+ * @var string|null
+ */
+ protected $fingerprint = null;
+
+ /**
+ * @var string
+ */
+ private $membership = '';
+
+ /**
+ * Get the (possibly empty) array of all print requests that
+ * exist for the entities that fit this description.
+ *
+ * @return PrintRequest[]
+ */
+ public function getPrintRequests() {
+ return $this->m_printreqs;
+ }
+
+ /**
+ * Set the array of print requests completely.
+ *
+ * @param PrintRequest[] $printRequests
+ */
+ public function setPrintRequests( array $printRequests ) {
+ $this->m_printreqs = $printRequests;
+ }
+
+ /**
+ * Add a single SMW\Query\PrintRequest.
+ *
+ * @param PrintRequest $printRequest
+ */
+ public function addPrintRequest( PrintRequest $printRequest ) {
+ $this->m_printreqs[] = $printRequest;
+ }
+
+ /**
+ * Add a new print request, but at the beginning of the list of requests
+ * (thus it will be printed first).
+ *
+ * @param PrintRequest $printRequest
+ */
+ public function prependPrintRequest( PrintRequest $printRequest ) {
+ array_unshift( $this->m_printreqs, $printRequest );
+ }
+
+ /**
+ * Returns a compound signature that identifies the canonized
+ * description. It builds a fingerrint so that [[Foo::123]][[Bar::abc]]
+ * returns the same signature as for [[Bar::abc]][[Foo::123]].
+ *
+ * @note An extension to a description should not rely on the query string
+ * as sole representation for a fingerprint.
+ *
+ * @since 2.5
+ *
+ * @return string
+ * @throws FingerprintNotFoundException
+ */
+ public function getFingerprint() {
+
+ if ( $this->fingerprint !== null ) {
+ return $this->fingerprint;
+ }
+
+ throw new FingerprintNotFoundException( "Missing a fingerprint, a signature was expected for the current description instance." );
+ }
+
+ /**
+ * Identifies an arbitrary membership to a wider circle of descriptions that
+ * mostly occurs in connection with a Conjunction, Disjunction, or
+ * SomeProperty.
+ *
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getMembership() {
+ return $this->membership;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $membership
+ */
+ public function setMembership( $membership ) {
+ $this->membership = $membership;
+ }
+
+ /**
+ * Return a string expressing this query.
+ * Some descriptions have different syntax in property value positions. The
+ * parameter $asvalue specifies whether the serialisation should take that into
+ * account.
+ *
+ * Example: The SMWValueDescription [[Paris]] returns the single result "Paris"
+ * but can also be used as value in [[has location::Paris]] which is preferred
+ * over the canonical [[has location::\<q\>[[Paris]]\</q\>]].
+ *
+ * The result should be a plain query string that SMW is able to parse,
+ * without any kind of HTML escape sequences.
+ *
+ * @param boolean $asValue
+ *
+ * @return string
+ */
+ abstract public function getQueryString( $asValue = false );
+
+ /**
+ * Return true if the description is required to encompass at most a single
+ * result, independently of the knowledge base.
+ *
+ * @return boolean
+ */
+ abstract public function isSingleton();
+
+ /**
+ * Compute the size of the description. Default is 1.
+ *
+ * @return integer
+ */
+ public function getSize() {
+ return 1;
+ }
+
+ /**
+ * Compute the depth of the description. Default is 0.
+ *
+ * @return integer
+ */
+ public function getDepth() {
+ return 0;
+ }
+
+ /**
+ * Report on query features used in description. Return values are (sums of)
+ * query feature constants such as SMW_PROPERTY_QUERY.
+ */
+ public function getQueryFeatures() {
+ return 0;
+ }
+
+ /**
+ * Recursively restrict query to a maximal size and depth as given.
+ * Returns a possibly changed description that should be used as a replacement.
+ * Reduce values of parameters to account for the returned descriptions size.
+ * Default implementation for non-nested descriptions of size 1.
+ * The parameter $log contains a list of all pruned conditions, updated when some
+ * description was reduced.
+ *
+ * @note Objects must not do changes on $this during pruning, since $this can be
+ * reused in multiple places of one or many queries. Make new objects to reflect
+ * changes!
+ */
+ public function prune( &$maxsize, &$maxDepth, &$log ) {
+
+ if ( ( $maxsize < $this->getSize() ) || ( $maxDepth < $this->getDepth() ) ) {
+ $log[] = $this->getQueryString();
+
+ $result = new ThingDescription();
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+ $maxsize = $maxsize - $this->getSize();
+ $maxDepth = $maxDepth - $this->getDepth();
+
+ return $this;
+ }
+
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getQueryString();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Disjunction.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Disjunction.php
new file mode 100644
index 00000000..04fddc15
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/Disjunction.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace SMW\Query\Language;
+
+/**
+ * Description of a collection of many descriptions, at least one of which
+ * must be satisfied (OR, disjunction).
+ *
+ * Corresponds to disjunction in OWL and SPARQL. Not available in RDFS.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class Disjunction extends Description {
+
+ /**
+ * @var Description[]
+ */
+ private $descriptions = [];
+
+ /**
+ * contains a single class description if any such disjunct was given;
+ * disjunctive classes are aggregated therei
+ * n
+ * @var null|ClassDescription
+ */
+ private $classDescription = null;
+
+ /**
+ * Used if disjunction is trivially true already
+ *
+ * @var boolean
+ */
+ private $isTrue = false;
+
+ public function __construct( array $descriptions = [] ) {
+ foreach ( $descriptions as $desc ) {
+ $this->addDescription( $desc );
+ }
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+
+ // Avoid a recursive tree
+ if ( $this->fingerprint !== null ) {
+ return $this->fingerprint;
+ }
+
+ $fingerprint = [];
+
+ foreach ( $this->descriptions as $description ) {
+ $fingerprint[$description->getFingerprint()] = true;
+ }
+
+ ksort( $fingerprint );
+
+ return $this->fingerprint = 'D:' . md5( implode( '|', array_keys( $fingerprint ) ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $hierarchyDepth
+ */
+ public function setHierarchyDepth( $hierarchyDepth ) {
+
+ $this->fingerprint = null;
+
+ if ( $this->classDescription !== null ) {
+ $this->classDescription->setHierarchyDepth( $hierarchyDepth );
+ }
+
+ foreach ( $this->descriptions as $key => $description ) {
+ if ( $description instanceof SomeProperty ) {
+ $description->setHierarchyDepth( $hierarchyDepth );
+ }
+ }
+ }
+
+ public function getDescriptions() {
+ return $this->descriptions;
+ }
+
+ public function addDescription( Description $description ) {
+
+ $this->fingerprint = null;
+ $fingerprint = $description->getFingerprint();
+
+ if ( $description instanceof ThingDescription ) {
+ $this->isTrue = true;
+ $this->descriptions = []; // no conditions any more
+ $this->classDescription = null;
+ }
+
+ if ( !$this->isTrue ) {
+ // Combine class descriptions only when those describe the same state
+ if ( $description instanceof ClassDescription ) {
+ if ( is_null( $this->classDescription ) ) { // first class description
+ $this->classDescription = $description;
+ $this->descriptions[$description->getFingerprint()] = $description;
+ } elseif ( $this->classDescription->isMergableDescription( $description ) ) {
+ $this->classDescription->addDescription( $description );
+ } else {
+ $this->descriptions[$description->getFingerprint()] = $description;
+ }
+ } elseif ( $description instanceof Disjunction ) { // absorb sub-disjunctions
+ foreach ( $description->getDescriptions() as $subdesc ) {
+ $this->descriptions[$subdesc->getFingerprint()] = $subdesc;
+ }
+ // } elseif ($description instanceof SMWSomeProperty) {
+ ///TODO: use subdisjunct. for multiple SMWSomeProperty descs with same property
+ } else {
+ $this->descriptions[$fingerprint] = $description;
+ }
+ }
+
+ // move print descriptions downwards
+ ///TODO: This may not be a good solution, since it does modify $description and since it does not react to future cahges
+ $this->m_printreqs = array_merge( $this->m_printreqs, $description->getPrintRequests() );
+ $description->setPrintRequests( [] );
+ }
+
+ public function getQueryString( $asValue = false ) {
+
+ if ( $this->isTrue ) {
+ return '+';
+ }
+
+ $result = '';
+ $sep = $asValue ? '||':' OR ';
+
+ foreach ( $this->descriptions as $desc ) {
+ $subdesc = $desc->getQueryString( $asValue );
+
+ if ( $desc instanceof SomeProperty ) { // enclose in <q> for parsing
+ if ( $asValue ) {
+ $subdesc = ' <q>[[' . $subdesc . ']]</q> ';
+ } else {
+ $subdesc = ' <q>' . $subdesc . '</q> ';
+ }
+ }
+
+ $result .= ( $result ? $sep:'' ) . $subdesc;
+ }
+
+ if ( $asValue ) {
+ return $result;
+ }
+
+ return ' <q>' . $result . '</q> ';
+ }
+
+ public function isSingleton() {
+ /// NOTE: this neglects the unimportant case where several disjuncts describe the same object.
+ if ( count( $this->descriptions ) != 1 ) {
+ return false;
+ }
+
+ return $this->descriptions[0]->isSingleton();
+ }
+
+ public function getSize() {
+ $size = 0;
+
+ foreach ( $this->descriptions as $desc ) {
+ $size += $desc->getSize();
+ }
+
+ return $size;
+ }
+
+ public function getDepth() {
+ $depth = 0;
+
+ foreach ( $this->descriptions as $desc ) {
+ $depth = max( $depth, $desc->getDepth() );
+ }
+
+ return $depth;
+ }
+
+ public function getQueryFeatures() {
+ $result = SMW_DISJUNCTION_QUERY;
+
+ foreach ( $this->descriptions as $desc ) {
+ $result = $result | $desc->getQueryFeatures();
+ }
+
+ return $result;
+ }
+
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+
+ if ( $maxsize <= 0 ) {
+ $log[] = $this->getQueryString();
+ return new ThingDescription();
+ }
+
+ $prunelog = [];
+ $newdepth = $maxdepth;
+ $result = new Disjunction();
+
+ foreach ( $this->descriptions as $desc ) {
+ $restdepth = $maxdepth;
+ $result->addDescription( $desc->prune( $maxsize, $restdepth, $prunelog ) );
+ $newdepth = min( $newdepth, $restdepth );
+ }
+
+ if ( count( $result->getDescriptions() ) > 0 ) {
+ $log = array_merge( $log, $prunelog );
+ $maxdepth = $newdepth;
+
+ if ( count( $result->getDescriptions() ) == 1 ) { // simplify unary disjunctions!
+ $descriptions = $result->getDescriptions();
+ $result = array_shift( $descriptions );
+ }
+
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+ $log[] = $this->getQueryString();
+
+ $result = new ThingDescription();
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/NamespaceDescription.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/NamespaceDescription.php
new file mode 100644
index 00000000..405f8a40
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/NamespaceDescription.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use SMW\Localizer;
+
+/**
+ * Description of all pages within a given wiki namespace, given by a numerical
+ * constant. Corresponds to a class restriction with a special class that
+ * characterises the given namespace (or at least that is how one could map
+ * this to OWL etc.).
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class NamespaceDescription extends Description {
+
+ /**
+ * @var integer
+ */
+ private $namespace;
+
+ /**
+ * @param integer $namespace
+ */
+ public function __construct( $namespace ) {
+ $this->namespace = $namespace;
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+ // Avoid a simple `int` which may interfere with an associative array
+ // when compounding hash strings from different descriptions
+ return 'N:' . md5( $this->namespace );
+ }
+
+ /**
+ * @return integer
+ */
+ public function getNamespace() {
+ return $this->namespace;
+ }
+
+ public function getQueryString( $asValue = false ) {
+
+ $localizedNamespaceText = Localizer::getInstance()->getNamespaceTextById( $this->namespace );
+
+ $prefix = $this->namespace == NS_CATEGORY ? ':' : '';
+
+ if ( $asValue ) {
+ return ' <q>[[' . $prefix . $localizedNamespaceText . ':+]]</q> ';
+ }
+
+ return '[[' . $prefix . $localizedNamespaceText . ':+]]';
+ }
+
+ public function isSingleton() {
+ return false;
+ }
+
+ public function getQueryFeatures() {
+ return SMW_NAMESPACE_QUERY;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/SomeProperty.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/SomeProperty.php
new file mode 100644
index 00000000..7260c48d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/SomeProperty.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use SMW\DIProperty;
+
+/**
+ * Description of a set of instances that have an attribute with some value
+ * that fits another (sub)description.
+ *
+ * Corresponds to existential quantification ("SomeValuesFrom" restriction) on
+ * properties in OWL. In conjunctive queries (OWL) and SPARQL (RDF), it is
+ * represented by using variables in the object part of such properties.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class SomeProperty extends Description {
+
+ /**
+ * @var Description
+ */
+ protected $description;
+
+ /**
+ * @var DIProperty
+ */
+ protected $property;
+
+ /**
+ * @var integer|null
+ */
+ protected $hierarchyDepth;
+
+ /**
+ * @since 1.6
+ *
+ * @param DIProperty $property
+ * @param Description $description
+ */
+ public function __construct( DIProperty $property, Description $description ) {
+ $this->property = $property;
+ $this->description = $description;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $hierarchyDepth
+ */
+ public function setHierarchyDepth( $hierarchyDepth ) {
+
+ if ( $hierarchyDepth > $GLOBALS['smwgQSubpropertyDepth'] ) {
+ $hierarchyDepth = $GLOBALS['smwgQSubpropertyDepth'];
+ }
+
+ $this->hierarchyDepth = $hierarchyDepth;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer|null
+ */
+ public function getHierarchyDepth() {
+ return $this->hierarchyDepth;
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+
+ // Avoid a recursive tree
+ if ( $this->fingerprint !== null ) {
+ return $this->fingerprint;
+ }
+
+ $subDescription = $this->description;
+ $property = $this->property->getSerialization();
+
+ // Resolve property.chains and connect its members
+ while ( $subDescription instanceof SomeProperty ) {
+ $subDescription = $subDescription->getDescription();
+ $subDescription->setMembership( $property );
+ }
+
+ // During a recursive chain use the hash from a stored
+ // member to distinguish Foo.Bar.Foobar.Bam from Foo.Bar.Foobar
+ $membership = $this->getMembership() . $subDescription->getMembership();
+
+ return $this->fingerprint = 'S:' . md5( $property . '|' . $membership . '|' . $this->description->getFingerprint() . $this->hierarchyDepth );
+ }
+
+ /**
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return Description
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return string
+ */
+ public function getQueryString( $asValue = false ) {
+ $subDescription = $this->description;
+
+ // Use the canonical label to ensure that conditions contain
+ // language indep. references
+ $propertyChainString = $this->property->getCanonicalLabel();
+ $propertyname = $propertyChainString;
+ $final = '';
+
+ while ( ( $propertyname !== '' ) && ( $subDescription instanceof SomeProperty ) ) { // try to use property chain syntax
+ $propertyname = $subDescription->getProperty()->getCanonicalLabel();
+
+ if ( $propertyname !== '' ) {
+ $propertyChainString .= '.' . $propertyname;
+ $subDescription = $subDescription->getDescription();
+ }
+ }
+
+ if ( $this->hierarchyDepth !== null ) {
+ $final = '|+depth=' . $this->hierarchyDepth;
+ }
+
+ if ( $asValue ) {
+ return '<q>[[' . $propertyChainString . '::' . $subDescription->getQueryString( true ) . $final . ']]</q>';
+ }
+
+ return '[[' . $propertyChainString . '::' . $subDescription->getQueryString( true ) . $final . ']]';
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return boolean
+ */
+ public function isSingleton() {
+ return false;
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getSize() {
+ return 1 + $this->getDescription()->getSize();
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getDepth() {
+ return 1 + $this->getDescription()->getDepth();
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return integer
+ */
+ public function getQueryFeatures() {
+ return SMW_PROPERTY_QUERY | $this->description->getQueryFeatures();
+ }
+
+ /**
+ * @since 1.6
+ *
+ * @return SomeProperty
+ */
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+
+ if ( ( $maxsize <= 0 ) || ( $maxdepth <= 0 ) ) {
+ $log[] = $this->getQueryString();
+ return new ThingDescription();
+ }
+
+ $maxsize--;
+ $maxdepth--;
+
+ $result = new SomeProperty(
+ $this->property,
+ $this->description->prune( $maxsize, $maxdepth, $log )
+ );
+
+ $result->setHierarchyDepth( $this->getHierarchyDepth() );
+ $result->setPrintRequests( $this->getPrintRequests() );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ThingDescription.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ThingDescription.php
new file mode 100644
index 00000000..3419df19
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ThingDescription.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Query\Language;
+
+/**
+ * A dummy description that describes any object. Corresponds to
+ * owl:thing, the class of all abstract objects. Note that it is
+ * not used for datavalues of attributes in order to support type
+ * hinting in the API: descriptions of data are always
+ * ValueDescription objects.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class ThingDescription extends Description {
+
+ public function getQueryString( $asValue = false ) {
+ return $asValue ? ( isset( $this->isNegation ) ? '!' : '' ) . '+' : '';
+ }
+
+ public function isSingleton() {
+ return false;
+ }
+
+ public function getSize() {
+ return 0; // no real condition, no size or depth
+ }
+
+ public function prune( &$maxsize, &$maxdepth, &$log ) {
+ return $this;
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+ // Avoid a simple 0 which may interfere with an associative array
+ // when compounding hash strings from different descriptions
+ return 'T:' . md5( 0 ) . ( isset( $this->isNegation ) ? '!' : '' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ValueDescription.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ValueDescription.php
new file mode 100644
index 00000000..be0161d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Language/ValueDescription.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Query\Language;
+
+use SMW\DataValueFactory;
+use SMw\DIProperty;
+use SMW\Query\QueryComparator;
+use SMWDataItem as DataItem;
+use SMWNumberValue as NumberValue;
+use SMWURIValue as UriValue;
+
+/**
+ * Description of one data value, or of a range of data values.
+ *
+ * Technically this usually corresponds to nominal predicates or to unary
+ * concrete domain predicates in OWL which are parametrised by one constant
+ * from the concrete domain.
+ * In RDF, concrete domain predicates that define ranges (like "greater or
+ * equal to") are not directly available.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class ValueDescription extends Description {
+
+ /**
+ * @var DataItem
+ */
+ private $dataItem;
+
+ /**
+ * @var integer element in the SMW_CMP_ enum
+ */
+ private $comparator;
+
+ /**
+ * @var null|DIProperty
+ */
+ private $property = null;
+
+ /**
+ * @param DataItem $dataItem
+ * @param null|DIProperty $property
+ * @param integer $comparator
+ */
+ public function __construct( DataItem $dataItem, DIProperty $property = null, $comparator = SMW_CMP_EQ ) {
+ $this->dataItem = $dataItem;
+ $this->comparator = $comparator;
+ $this->property = $property;
+ }
+
+ /**
+ * @see Description::getFingerprint
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getFingerprint() {
+
+ $property = null;
+
+ if ( $this->property !== null ) {
+ $property = $this->property->getSerialization();
+ }
+
+ // A change to the order does also change the signature and renders a
+ // different query ID
+ return 'V:' . md5( $this->comparator . '|' . $this->dataItem->getHash() . '|' . $property );
+ }
+
+ /**
+ * @deprecated Use getDataItem() and \SMW\DataValueFactory::getInstance()->newDataValueByItem() if needed. Vanishes before SMW 1.7
+ * @return DataItem
+ */
+ public function getDataValue() {
+ // FIXME: remove
+ return $this->dataItem;
+ }
+
+ /**
+ * @return DataItem
+ */
+ public function getDataItem() {
+ return $this->dataItem;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIProperty|null
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getComparator() {
+ return $this->comparator;
+ }
+
+ /**
+ * @param bool $asValue
+ *
+ * @return string
+ */
+ public function getQueryString( $asValue = false ) {
+
+ $comparator = QueryComparator::getInstance()->getStringForComparator(
+ $this->comparator
+ );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $this->dataItem,
+ $this->property
+ );
+
+ // Set option to ensure that the output doesn't alter the display
+ // characteristics of a value
+ $dataValue->setOption( UriValue::VALUE_RAW, true );
+ $dataValue->setOption( NumberValue::NO_DISP_PRECISION_LIMIT, true );
+
+ if ( $asValue ) {
+ return $comparator . $dataValue->getWikiValue();
+ }
+
+ // this only is possible for values of Type:Page
+ if ( $comparator === '' ) { // some extra care for Category: pages
+ return '[[:' . $dataValue->getWikiValue() . ']]';
+ }
+
+ return '[[' . $comparator . $dataValue->getWikiValue() . ']]';
+ }
+
+ public function isSingleton() {
+ return $this->comparator == SMW_CMP_EQ;
+ }
+
+ public function getSize() {
+ return 1;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser.php
new file mode 100644
index 00000000..d34f6905
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\Query\Language\Description;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+interface Parser {
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty|string $property
+ * @param string $value
+ *
+ * @return string
+ */
+ public function createCondition( $property, $value );
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getErrors();
+
+ /**
+ * Describes a processed description instance in terms of the existence of
+ * a self reference in connection with the context page a query is
+ * embedded.
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function containsSelfReference();
+
+ /**
+ * @since 3.0
+ *
+ * @param string $condition
+ *
+ * @return Description
+ */
+ public function getQueryDescription( $condition );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/DescriptionProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/DescriptionProcessor.php
new file mode 100644
index 00000000..45af6deb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/DescriptionProcessor.php
@@ -0,0 +1,307 @@
+<?php
+
+namespace SMW\Query\Parser;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\ValueDescription;
+use SMW\Site;
+use SMWDataValue as DataValue;
+use SMW\Query\QueryComparator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class DescriptionProcessor {
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @var DescriptionFactory
+ */
+ private $descriptionFactory;
+
+ /**
+ * @var integer
+ */
+ private $queryFeatures;
+
+ /**
+ * @var DIWikiPage|null
+ */
+ private $contextPage;
+
+ /**
+ * @var boolean
+ */
+ private $selfReference = false;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $queryFeatures
+ */
+ public function __construct( $queryFeatures = false ) {
+ $this->queryFeatures = $queryFeatures === false ? $GLOBALS['smwgQFeatures'] : $queryFeatures;
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->descriptionFactory = new DescriptionFactory();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage|null $contextPage
+ */
+ public function setContextPage( DIWikiPage $contextPage = null ) {
+ $this->contextPage = $contextPage;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function clear() {
+ $this->errors = [];
+ $this->selfReference = false;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function containsSelfReference() {
+ return $this->selfReference;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array|string $error
+ */
+ public function addError( $error ) {
+
+ if ( !is_array( $error ) ) {
+ $error = (array)$error;
+ }
+
+ if ( $error !== [] ) {
+ $this->errors[] = Message::encode( $error );
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $msgKey
+ */
+ public function addErrorWithMsgKey( $msgKey /*...*/ ) {
+ $this->errors[] = Message::encode( func_get_args() );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ * @param string $chunk
+ *
+ * @return Description|null
+ */
+ public function newDescriptionForPropertyObjectValue( DIProperty $property, $chunk ) {
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty( $property );
+ $dataValue->setContextPage( $this->contextPage );
+
+ // Indicates whether a value is being used by a query condition or not which
+ // can lead to a modified validation of a value.
+ $dataValue->setOption( DataValue::OPT_QUERY_CONTEXT, true );
+ $dataValue->setOption( 'isCapitalLinks', Site::isCapitalLinks() );
+
+ $description = $dataValue->getQueryDescription( $chunk );
+ $this->addError( $dataValue->getErrors() );
+
+ return $description;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $chunk
+ *
+ * @return Description|null
+ */
+ public function newDescriptionForWikiPageValueChunk( $chunk ) {
+
+ // Only create a simple WpgValue to initiate the query description target
+ // operation. If the chunk contains something like "≤Issue/1220" then the
+ // WpgValue would return with an error as it cannot parse ≤ as/ legal
+ // character, the chunk itself is processed by
+ // DataValue::getQueryDescription hence no need to use it as input for
+ // the factory instance
+ $dataValue = $this->dataValueFactory->newTypeIDValue( '_wpg', 'QP_WPG_TITLE' );
+ $dataValue->setContextPage( $this->contextPage );
+
+ $dataValue->setOption( DataValue::OPT_QUERY_CONTEXT, true );
+
+ // #3587
+ // Requesting capital links is influenced by two factors, `wgCapitalLinks`
+ // is enabled sitewide and the `WikiPageValue` condition is identified
+ // as SMW_CMP_EQ/NEQ (e.g. [[Foo]], [[!Foo]]) with other expressions
+ // (e.g. [[~foo*]]) to remain in the form of the user input
+ $queryComparator = QueryComparator::getInstance();
+
+ if ( Site::isCapitalLinks() && (
+ $queryComparator->containsComparator( $chunk, SMW_CMP_EQ ) ||
+ $queryComparator->containsComparator( $chunk, SMW_CMP_NEQ ) ) ) {
+ $dataValue->setOption( 'isCapitalLinks', true );
+ }
+
+ $description = null;
+
+ $description = $dataValue->getQueryDescription( $chunk );
+ $this->addError( $dataValue->getErrors() );
+
+ if ( !$this->selfReference && $this->contextPage !== null && $description instanceof ValueDescription ) {
+ $this->selfReference = $description->getDataItem()->equals( $this->contextPage );
+ }
+
+ return $description;
+ }
+
+ /**
+ * The method was supposed to be named just `or` and `and` but this works
+ * only on PHP 7.1 therefore ...
+ */
+
+ /**
+ * @since 2.4
+ *
+ * @param Description|null $currentDescription
+ * @param Description|null $newDescription
+ *
+ * @return Description|null
+ */
+ public function asOr( Description $currentDescription = null, Description $newDescription = null ) {
+ return $this->newCompoundDescription( $currentDescription, $newDescription, SMW_DISJUNCTION_QUERY );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Description|null $currentDescription
+ * @param Description|null $newDescription
+ *
+ * @return Description|null
+ */
+ public function asAnd( Description $currentDescription = null, Description $newDescription = null ) {
+ return $this->newCompoundDescription( $currentDescription, $newDescription, SMW_CONJUNCTION_QUERY );
+ }
+
+ /**
+ * Extend a given description by a new one, either by adding the new description
+ * (if the old one is a container description) or by creating a new container.
+ * The parameter $conjunction determines whether the combination of both descriptions
+ * should be a disjunction or conjunction.
+ *
+ * In the special case that the current description is NULL, the new one will just
+ * replace the current one.
+ *
+ * The return value is the expected combined description. The object $currentDescription will
+ * also be changed (if it was non-NULL).
+ */
+ private function newCompoundDescription( Description $currentDescription = null, Description $newDescription = null, $compoundType = SMW_CONJUNCTION_QUERY ) {
+
+ $notallowedmessage = 'smw_noqueryfeature';
+
+ if ( $newDescription instanceof SomeProperty ) {
+ $allowed = $this->queryFeatures & SMW_PROPERTY_QUERY;
+ } elseif ( $newDescription instanceof ClassDescription ) {
+ $allowed = $this->queryFeatures & SMW_CATEGORY_QUERY;
+ } elseif ( $newDescription instanceof ConceptDescription ) {
+ $allowed = $this->queryFeatures & SMW_CONCEPT_QUERY;
+ } elseif ( $newDescription instanceof Conjunction ) {
+ $allowed = $this->queryFeatures & SMW_CONJUNCTION_QUERY;
+ $notallowedmessage = 'smw_noconjunctions';
+ } elseif ( $newDescription instanceof Disjunction ) {
+ $allowed = $this->queryFeatures & SMW_DISJUNCTION_QUERY;
+ $notallowedmessage = 'smw_nodisjunctions';
+ } else {
+ $allowed = true;
+ }
+
+ if ( !$allowed ) {
+ $this->addErrorWithMsgKey( $notallowedmessage, $newDescription->getQueryString() );
+ return $currentDescription;
+ }
+
+ if ( $newDescription === null ) {
+ return $currentDescription;
+ } elseif ( $currentDescription === null ) {
+ return $newDescription;
+ } else { // we already found descriptions
+ return $this->newCompoundDescriptionByType( $compoundType, $currentDescription, $newDescription );
+ }
+ }
+
+ private function newCompoundDescriptionByType( $compoundType, $currentDescription, $newDescription ) {
+
+ if ( ( ( $compoundType & SMW_CONJUNCTION_QUERY ) != 0 && ( $currentDescription instanceof Conjunction ) ) ||
+ ( ( $compoundType & SMW_DISJUNCTION_QUERY ) != 0 && ( $currentDescription instanceof Disjunction ) ) ) { // use existing container
+ $currentDescription->addDescription( $newDescription );
+ return $currentDescription;
+ } elseif ( ( $compoundType & SMW_CONJUNCTION_QUERY ) != 0 ) { // make new conjunction
+ return $this->newConjunction( $currentDescription, $newDescription );
+ } elseif ( ( $compoundType & SMW_DISJUNCTION_QUERY ) != 0 ) { // make new disjunction
+ return $this->newDisjunction( $currentDescription, $newDescription );
+ }
+ }
+
+ private function newConjunction( $currentDescription, $newDescription ) {
+
+ if ( $this->queryFeatures & SMW_CONJUNCTION_QUERY ) {
+ return $this->descriptionFactory->newConjunction( [ $currentDescription, $newDescription ] );
+ }
+
+ $this->addErrorWithMsgKey( 'smw_noconjunctions', $newDescription->getQueryString() );
+
+ return $currentDescription;
+ }
+
+ private function newDisjunction( $currentDescription, $newDescription ) {
+
+ if ( $this->queryFeatures & SMW_DISJUNCTION_QUERY ) {
+ return $this->descriptionFactory->newDisjunction( [ $currentDescription, $newDescription ] );
+ }
+
+ $this->addErrorWithMsgKey( 'smw_nodisjunctions', $newDescription->getQueryString() );
+
+ return $currentDescription;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php
new file mode 100644
index 00000000..fa3c6096
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/LegacyParser.php
@@ -0,0 +1,847 @@
+<?php
+
+namespace SMW\Query\Parser;
+
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Parser;
+use SMW\Query\QueryToken;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ */
+class LegacyParser implements Parser {
+
+ /**
+ * @var DescriptionProcessor
+ */
+ private $descriptionProcessor;
+
+ /**
+ * @var QueryToken
+ */
+ private $queryToken;
+
+ /**
+ * @var Tokenizer
+ */
+ private $tokenizer;
+
+ /**
+ * @var DescriptionFactory
+ */
+ private $descriptionFactory;
+
+ /**
+ * @var DataTypeRegistry
+ */
+ private $dataTypeRegistry;
+
+ /**
+ * Description of the default namespace restriction, or NULL if not used
+ *
+ * @var array|null
+ */
+ private $defaultNamespace;
+
+ /**
+ * List of open blocks ("parentheses") that need closing at current step
+ *
+ * @var array
+ */
+ private $separatorStack = [];
+
+ /**
+ * Remaining string to be parsed (parsing eats query string from the front)
+ *
+ * @var string
+ */
+ private $currentString;
+
+ /**
+ * Cache label of category namespace . ':'
+ *
+ * @var string
+ */
+ private $categoryPrefix;
+
+ /**
+ * Cache label of concept namespace . ':'
+ *
+ * @var string
+ */
+ private $conceptPrefix;
+
+ /**
+ * Cache canonnical label of category namespace . ':'
+ *
+ * @var string
+ */
+ private $categoryPrefixCannonical;
+
+ /**
+ * Cache canonnical label of concept namespace . ':'
+ *
+ * @var string
+ */
+ private $conceptPrefixCannonical;
+
+ /**
+ * @var DIWikiPage|null
+ */
+ private $contextPage;
+
+ /**
+ * @var boolean
+ */
+ private $selfReference = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param DescriptionProcessor $descriptionProcessor
+ * @param Tokenizer $tokenizer
+ * @param QueryToken $queryToken
+ */
+ public function __construct( DescriptionProcessor $descriptionProcessor, Tokenizer $tokenizer, QueryToken $queryToken ) {
+ $this->descriptionProcessor = $descriptionProcessor;
+ $this->tokenizer = $tokenizer;
+ $this->queryToken = $queryToken;
+ $this->descriptionFactory = new DescriptionFactory();
+ $this->dataTypeRegistry = DataTypeRegistry::getInstance();
+ $this->setDefaultPrefix();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|null $contextPage
+ */
+ public function setContextPage( DIWikiPage $contextPage = null ) {
+ $this->contextPage = $contextPage;
+ }
+
+ /**
+ * Provide an array of namespace constants that are used as default restrictions.
+ * If NULL is given, no such default restrictions will be added (faster).
+ *
+ * @since 1.6
+ */
+ public function setDefaultNamespaces( $namespaces ) {
+ $this->defaultNamespace = null;
+
+ if ( !is_array( $namespaces ) ) {
+ return;
+ }
+
+ foreach ( $namespaces as $namespace ) {
+ $this->defaultNamespace = $this->descriptionProcessor->asOr(
+ $this->defaultNamespace,
+ $this->descriptionFactory->newNamespaceDescription( $namespace )
+ );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $languageCode
+ */
+ public function setDefaultPrefix( $languageCode = null ) {
+
+ $localizer = Localizer::getInstance();
+
+ if ( $languageCode === null ) {
+ $language = $localizer->getContentLanguage();
+ } else {
+ $language = $localizer->getLanguage( $languageCode );
+ }
+
+ $this->categoryPrefix = $language->getNsText( NS_CATEGORY ) . ':';
+ $this->conceptPrefix = $language->getNsText( SMW_NS_CONCEPT ) . ':';
+
+ $this->categoryPrefixCannonical = 'Category:';
+ $this->conceptPrefixCannonical = 'Concept:';
+
+ $this->tokenizer->setDefaultPattern(
+ [
+ $this->categoryPrefix,
+ $this->conceptPrefix,
+ $this->categoryPrefixCannonical,
+ $this->conceptPrefixCannonical
+ ]
+ );
+ }
+
+ /**
+ * Return array of error messages (possibly empty).
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->descriptionProcessor->getErrors();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function containsSelfReference() {
+
+ if ( $this->selfReference ) {
+ return true;
+ }
+
+ return $this->descriptionProcessor->containsSelfReference();
+ }
+
+ /**
+ * Return error message or empty string if no error occurred.
+ *
+ * @return string
+ */
+ public function getErrorString() {
+ throw new \RuntimeException( "Shouldnot be used, remove getErrorString usage!" );
+ return smwfEncodeMessages( $this->getErrors() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return QueryToken
+ */
+ public function getQueryToken() {
+ return $this->queryToken;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function createCondition( $property, $value ) {
+
+ if ( $property instanceOf DIProperty ) {
+ $property = $property->getLabel();
+ }
+
+ return "[[$property::$value]]";
+ }
+
+ /**
+ * Compute an SMWDescription from a query string. Returns whatever descriptions could be
+ * wrestled from the given string (the most general result being SMWThingDescription if
+ * no meaningful condition was extracted).
+ *
+ * @param string $queryString
+ *
+ * @return Description
+ */
+ public function getQueryDescription( $queryString ) {
+
+ if ( $queryString === '' ) {
+ $this->descriptionProcessor->addErrorWithMsgKey(
+ 'smw-query-condition-empty'
+ );
+
+ return $this->descriptionFactory->newThingDescription();
+ }
+
+ $this->descriptionProcessor->clear();
+ $this->descriptionProcessor->setContextPage( $this->contextPage );
+
+ $this->currentString = $queryString;
+ $this->separatorStack = [];
+
+ $this->selfReference = false;
+ $setNS = false;
+
+ $description = $this->getSubqueryDescription( $setNS );
+
+ // add default namespaces if applicable
+ if ( !$setNS ) {
+ $description = $this->descriptionProcessor->asAnd(
+ $this->defaultNamespace,
+ $description
+ );
+ }
+
+ // parsing went wrong, no default namespaces
+ if ( $description === null ) {
+ $description = $this->descriptionFactory->newThingDescription();
+ }
+
+ return $description;
+ }
+
+ /**
+ * Compute an SMWDescription for current part of a query, which should
+ * be a standalone query (the main query or a subquery enclosed within
+ * "\<q\>...\</q\>". Recursively calls similar methods and returns NULL upon error.
+ *
+ * The call-by-ref parameter $setNS is a boolean. Its input specifies whether
+ * the query should set the current default namespace if no namespace restrictions
+ * were given. If false, the calling super-query is happy to set the required
+ * NS-restrictions by itself if needed. Otherwise the subquery has to impose the defaults.
+ * This is so, since outermost queries and subqueries of disjunctions will have to set
+ * their own default restrictions.
+ *
+ * The return value of $setNS specifies whether or not the subquery has a namespace
+ * specification in place. This might happen automatically if the query string imposes
+ * such restrictions. The return value is important for those callers that otherwise
+ * set up their own restrictions.
+ *
+ * Note that $setNS is no means to switch on or off default namespaces in general,
+ * but just controls query generation. For general effect, the default namespaces
+ * should be set to NULL.
+ *
+ * @return Description|null
+ */
+ private function getSubqueryDescription( &$setNS ) {
+
+ $conjunction = null; // used for the current inner conjunction
+ $disjuncts = []; // (disjunctive) array of subquery conjunctions
+
+ $hasNamespaces = false; // does the current $conjnuction have its own namespace restrictions?
+ $mustSetNS = $setNS; // must NS restrictions be set? (may become true even if $setNS is false)
+
+ $continue = ( $chunk = $this->readChunk() ) !== ''; // skip empty subquery completely, thorwing an error
+
+ while ( $continue ) {
+ $setsubNS = false;
+
+ switch ( $chunk ) {
+ case '[[': // start new link block
+ $ld = $this->getLinkDescription( $setsubNS );
+
+ if ( !is_null( $ld ) ) {
+ $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $ld );
+ }
+ break;
+ case 'AND':
+ case '<q>': // enter new subquery, currently irrelevant but possible
+ $this->pushDelimiter( '</q>' );
+ $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $this->getSubqueryDescription( $setsubNS ) );
+ break;
+ case 'OR':
+ case '||':
+ case '':
+ case '</q>': // finish disjunction and maybe subquery
+ if ( !is_null( $this->defaultNamespace ) ) { // possibly add namespace restrictions
+ if ( $hasNamespaces && !$mustSetNS ) {
+ // add NS restrictions to all earlier conjunctions (all of which did not have them yet)
+ $mustSetNS = true; // enforce NS restrictions from now on
+ $newdisjuncts = [];
+
+ foreach ( $disjuncts as $conj ) {
+ $newdisjuncts[] = $this->descriptionProcessor->asAnd( $conj, $this->defaultNamespace );
+ }
+
+ $disjuncts = $newdisjuncts;
+ } elseif ( !$hasNamespaces && $mustSetNS ) {
+ // add ns restriction to current result
+ $conjunction = $this->descriptionProcessor->asAnd( $conjunction, $this->defaultNamespace );
+ }
+ }
+
+ $disjuncts[] = $conjunction;
+ // start anew
+ $conjunction = null;
+ $hasNamespaces = false;
+
+ // finish subquery?
+ if ( $chunk == '</q>' ) {
+ if ( $this->popDelimiter( '</q>' ) ) {
+ $continue = false; // leave the loop
+ } else {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_toomanyclosing', $chunk );
+ return null;
+ }
+ } elseif ( $chunk === '' ) {
+ $continue = false;
+ }
+ break;
+ case '+': // "... AND true" (ignore)
+ break;
+ default: // error: unexpected $chunk
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $chunk );
+ // return null; // Try to go on, it can only get better ...
+ }
+
+ if ( $setsubNS ) { // namespace restrictions encountered in current conjunct
+ $hasNamespaces = true;
+ }
+
+ if ( $continue ) { // read on only if $continue remained true
+ $chunk = $this->readChunk();
+ }
+ }
+
+ if ( count( $disjuncts ) > 0 ) { // make disjunctive result
+ $result = null;
+
+ foreach ( $disjuncts as $d ) {
+ if ( is_null( $d ) ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_emptysubquery' );
+ $setNS = false;
+ return null;
+ } else {
+ $result = $this->descriptionProcessor->asOr( $result, $d );
+ }
+ }
+ } else {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_emptysubquery' );
+ $setNS = false;
+ return null;
+ }
+
+ // NOTE: also false if namespaces were given but no default NS descs are available
+ $setNS = $mustSetNS;
+
+ return $result;
+ }
+
+ /**
+ * Compute an SMWDescription for current part of a query, which should
+ * be the content of "[[ ... ]]". Returns NULL upon error.
+ *
+ * Parameters $setNS has the same use as in getSubqueryDescription().
+ */
+ private function getLinkDescription( &$setNS ) {
+ // This method is called when we encountered an opening '[['. The following
+ // block could be a Category-statement, fixed object, or property statement.
+
+ // NOTE: untrimmed, initial " " escapes prop. chains
+ $chunk = $this->readChunk( '', true, false );
+
+ if ( $this->hasClassPrefix( $chunk ) ) {
+ return $this->getClassDescription( $setNS, $this->isClass( $chunk ) );
+ }
+
+ // fixed subject, namespace restriction, property query, or subquery
+
+ // Do not consume hit, "look ahead"
+ $sep = $this->readChunk( '', false );
+
+ if ( ( $sep == '::' ) || ( $sep == ':=' ) ) {
+ if ( $chunk{0} != ':' ) { // property statement
+ return $this->getPropertyDescription( $chunk, $setNS );
+ } else { // escaped article description, read part after :: to get full contents
+ $chunk .= $this->readChunk( '\[\[|\]\]|\|\||\|' );
+ return $this->getArticleDescription( trim( $chunk ), $setNS );
+ }
+ }
+
+ // Fixed article/namespace restriction. $sep should be ]] or ||
+ return $this->getArticleDescription( trim( $chunk ), $setNS );
+ }
+
+ /**
+ * Parse a category description (the part of an inline query that
+ * is in between "[[Category:" and the closing "]]" and create a
+ * suitable description.
+ */
+ private function getClassDescription( &$setNS, $category = true ) {
+
+ // No subqueries allowed here, inline disjunction allowed, wildcards allowed
+ $description = null;
+ $continue = true;
+ $invalidName = false;
+
+ while ( $continue ) {
+ $chunk = $this->readChunk();
+
+ if ( $chunk == '+' ) {
+ $desc = $this->descriptionFactory->newNamespaceDescription( $category ? NS_CATEGORY : SMW_NS_CONCEPT );
+ $description = $this->descriptionProcessor->asOr( $description, $desc );
+ } else { // assume category/concept title
+ $isNegation = false;
+
+ // [[Category:!Foo]]
+ // Only the ElasticStore does actively support this construct
+ if ( $chunk{0} === '!' ) {
+ $chunk = substr( $chunk, 1 );
+ $isNegation = true;
+ }
+
+ // We add a prefix to prevent problems with, e.g., [[Category:Template:Test]]
+ $prefix = $category ? $this->categoryPrefix : $this->conceptPrefix;
+ $title = Title::newFromText( $prefix . $chunk );
+
+ // Something like [[Category::Foo]] doesn't produce any meaningful
+ // results
+ if ( strpos( $prefix . $chunk, '::' ) !== false ) {
+ $invalidName .= "{$prefix}{$chunk}";
+ } elseif ( $invalidName ) {
+ $invalidName .= "||{$chunk}";
+ }
+
+ if ( $title !== null ) {
+ $diWikiPage = new DIWikiPage( $title->getDBkey(), $title->getNamespace(), '' );
+
+ if ( !$this->selfReference && $this->contextPage !== null ) {
+ $this->selfReference = $diWikiPage->equals( $this->contextPage );
+ }
+
+ $desc = $category ? $this->descriptionFactory->newClassDescription( $diWikiPage ) : $this->descriptionFactory->newConceptDescription( $diWikiPage );
+
+ if ( $isNegation ) {
+ $desc->isNegation = $isNegation;
+ }
+
+ $description = $this->descriptionProcessor->asOr( $description, $desc );
+ }
+ }
+
+ $chunk = $this->readChunk();
+
+ // Disjunctions only for categories
+ $continue = ( $chunk == '||' ) && $category;
+ }
+
+ if ( $invalidName ) {
+ return $this->descriptionProcessor->addErrorWithMsgKey( 'smw-category-invalid-value-assignment', "[[{$invalidName}]]" );
+ }
+
+ return $this->finishLinkDescription( $chunk, false, $description, $setNS );
+ }
+
+ /**
+ * Parse a property description (the part of an inline query that
+ * is in between "[[Some property::" and the closing "]]" and create a
+ * suitable description. The "::" is the first chunk on the current
+ * string.
+ */
+ private function getPropertyDescription( $propertyName, &$setNS ) {
+
+ // Consume separator ":=" or "::"
+ $this->readChunk();
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ // First process property chain syntax (e.g. "property1.property2::value"),
+ // escaped by initial " ":
+ $propertynames = ( $propertyName{0} == ' ' ) ? [ $propertyName ] : explode( '.', $propertyName );
+ $propertyValueList = [];
+
+ $typeid = '_wpg';
+ $inverse = false;
+
+ // After iteration, $property and $typeid correspond to last value
+ foreach ( $propertynames as $name ) {
+
+ // Non-final property in chain was no wikipage: not allowed
+ if ( !$this->isPagePropertyType( $typeid ) ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_valuesubquery', $name );
+
+ // TODO: read some more chunks and try to finish [[ ]]
+ return null;
+ }
+
+ $propertyValue = $dataValueFactory->newPropertyValueByLabel( $name );
+
+ // Illegal property identifier
+ if ( !$propertyValue->isValid() ) {
+ $this->descriptionProcessor->addError( $propertyValue->getErrors() );
+
+ // TODO: read some more chunks and try to finish [[ ]]
+ return null;
+ }
+
+ // Set context to allow evading restriction checks for specific
+ // entities that handle the context such as pre-defined properties
+ // (Has query, Modification date etc.)
+ $propertyValue->setOption( $propertyValue::OPT_QUERY_CONTEXT, true );
+
+ // Check restriction
+ if ( $propertyValue->isRestricted() ) {
+ $this->descriptionProcessor->addError( $propertyValue->getRestrictionError() );
+ return null;
+ }
+
+ $property = $propertyValue->getDataItem();
+ $propertyValueList[] = $propertyValue;
+
+ $typeid = $property->findPropertyTypeID();
+ $inverse = $property->isInverse();
+ }
+
+ $innerdesc = null;
+ $continue = true;
+
+ while ( $continue ) {
+ $chunk = $this->readChunk();
+
+ switch ( $chunk ) {
+ // !+
+ case '!+':
+ $desc = $this->descriptionFactory->newThingDescription();
+ $desc->isNegation = true;
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $desc );
+ $chunk = $this->readChunk();
+ break;
+ // wildcard, add namespaces for page-type properties
+ case '+':
+ if ( !is_null( $this->defaultNamespace ) && ( $this->isPagePropertyType( $typeid ) || $inverse ) ) {
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->defaultNamespace );
+ } else {
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() );
+ }
+ $chunk = $this->readChunk();
+ break;
+ // subquery, set default namespaces
+ case '<q>':
+ if ( $this->isPagePropertyType( $typeid ) || $inverse ) {
+ $this->pushDelimiter( '</q>' );
+ $setsubNS = true;
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->getSubqueryDescription( $setsubNS ) );
+ } else { // no subqueries allowed for non-pages
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_valuesubquery', end( $propertynames ) );
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() );
+ }
+ $chunk = $this->readChunk();
+ break;
+ // normal object value
+ default:
+ // read value(s), possibly with inner [[...]]
+ $open = 1;
+ $value = $chunk;
+ $continue2 = true;
+ // read value with inner [[, ]], ||
+ while ( ( $open > 0 ) && ( $continue2 ) ) {
+ $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' );
+ switch ( $chunk ) {
+ case '[[': // open new [[ ]]
+ $open++;
+ break;
+ case ']]': // close [[ ]]
+ $open--;
+ break;
+ case '|':
+ case '||': // terminates only outermost [[ ]]
+ if ( $open == 1 ) {
+ $open = 0;
+ }
+ break;
+ case '': ///TODO: report error; this is not good right now
+ $continue2 = false;
+ break;
+ }
+ if ( $open != 0 ) {
+ $value .= $chunk;
+ }
+ } ///NOTE: at this point, we normally already read one more chunk behind the value
+ $outerDesription = $this->descriptionProcessor->newDescriptionForPropertyObjectValue(
+ $propertyValue->getDataItem(),
+ $value
+ );
+
+ $this->queryToken->addFromDesciption( $outerDesription );
+ $innerdesc = $this->descriptionProcessor->asOr(
+ $innerdesc,
+ $outerDesription
+ );
+
+ }
+ $continue = ( $chunk == '||' );
+ }
+
+ // No description, make a wildcard search
+ if ( $innerdesc === null ) {
+ if ( $this->defaultNamespace !== null && $this->isPagePropertyType( $typeid ) ) {
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->defaultNamespace );
+ } else {
+ $innerdesc = $this->descriptionProcessor->asOr( $innerdesc, $this->descriptionFactory->newThingDescription() );
+ }
+
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_propvalueproblem', $propertyValue->getWikiValue() );
+ }
+
+ $propertyValueList = array_reverse( $propertyValueList );
+
+ foreach ( $propertyValueList as $propertyValue ) {
+ $innerdesc = $this->descriptionFactory->newSomeProperty( $propertyValue->getDataItem(), $innerdesc );
+ }
+
+ $result = $innerdesc;
+
+ return $this->finishLinkDescription( $chunk, false, $result, $setNS );
+ }
+
+ /**
+ * Parse an article description (the part of an inline query that
+ * is in between "[[" and the closing "]]" assuming it is not specifying
+ * a category or property) and create a suitable description.
+ * The first chunk behind the "[[" has already been read and is
+ * passed as a parameter.
+ */
+ private function getArticleDescription( $firstChunk, &$setNS ) {
+
+ $chunk = $firstChunk;
+ $description = null;
+
+ $continue = true;
+ $localizer = Localizer::getInstance();
+
+ while ( $continue ) {
+
+ // No subqueries of the form [[<q>...</q>]] (not needed)
+ if ( $chunk == '<q>' ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_misplacedsubquery' );
+ return null;
+ }
+
+ // ":Category:Foo" "User:bar" ":baz" ":+"
+ $list = preg_split( '/:/', $chunk, 3 );
+
+ if ( ( $list[0] === '' ) && ( count( $list ) == 3 ) ) {
+ $list = array_slice( $list, 1 );
+ }
+
+ // try namespace restriction
+ if ( ( count( $list ) == 2 ) && ( $list[1] == '+' ) ) {
+
+ $idx = $localizer->getNamespaceIndexByName( $list[0] );
+
+ if ( $idx !== false ) {
+ $description = $this->descriptionProcessor->asOr(
+ $description,
+ $this->descriptionFactory->newNamespaceDescription( $idx )
+ );
+ }
+ } else {
+ $outerDesription = $this->descriptionProcessor->newDescriptionForWikiPageValueChunk(
+ $chunk
+ );
+
+ $this->queryToken->addFromDesciption( $outerDesription );
+
+ $description = $this->descriptionProcessor->asOr(
+ $description,
+ $outerDesription
+ );
+ }
+
+ $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' );
+
+ if ( $chunk == '||' ) {
+ $chunk = $this->readChunk( '\[\[|\]\]|\|\||\|' );
+ $continue = true;
+ } else {
+ $continue = false;
+ }
+ }
+
+ return $this->finishLinkDescription( $chunk, true, $description, $setNS );
+ }
+
+ private function finishLinkDescription( $chunk, $hasNamespaces, $description, &$setNS ) {
+
+ if ( is_null( $description ) ) { // no useful information or concrete error found
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $chunk ); // was smw_badqueryatom
+ } elseif ( !$hasNamespaces && $setNS && !is_null( $this->defaultNamespace ) ) {
+ $description = $this->descriptionProcessor->asAnd( $description, $this->defaultNamespace );
+ $hasNamespaces = true;
+ }
+
+ $setNS = $hasNamespaces;
+
+ if ( $chunk == '|' ) { // skip content after single |, but report a warning
+ // Note: Using "|label" in query atoms used to be a way to set the mainlabel in SMW <1.0; no longer supported now
+ $chunk = $this->readChunk( '\]\]' );
+ $labelpart = '|';
+ $hasError = true;
+
+ // Set an individual hierarchy depth
+ if ( strpos( $chunk, '+depth=' ) !== false ) {
+ list( $k, $depth ) = explode( '=', $chunk, 2 );
+
+ if ( $description instanceOf ClassDescription || $description instanceOf SomeProperty || $description instanceOf Disjunction ) {
+ $description->setHierarchyDepth( $depth );
+ }
+
+ $chunk = $this->readChunk( '\]\]' );
+ $hasError = false;
+ }
+
+ if ( $chunk != ']]' ) {
+ $labelpart .= $chunk;
+ $chunk = $this->readChunk( '\]\]' );
+ }
+
+ if ( $hasError ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_unexpectedpart', $labelpart );
+ }
+ }
+
+ if ( $chunk != ']]' ) {
+ // What happended? We found some chunk that could not be processed as
+ // link content (as in [[Category:Test<q>]]), or the closing ]] are
+ // just missing entirely.
+ if ( $chunk !== '' ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_misplacedsymbol', $chunk );
+
+ // try to find a later closing ]] to finish this misshaped subpart
+ $chunk = $this->readChunk( '\]\]' );
+
+ if ( $chunk != ']]' ) {
+ $chunk = $this->readChunk( '\]\]' );
+ }
+ }
+ if ( $chunk === '' ) {
+ $this->descriptionProcessor->addErrorWithMsgKey( 'smw_noclosingbrackets' );
+ }
+ }
+
+ return $description;
+ }
+
+ /**
+ * @see Tokenizer::read
+ */
+ private function readChunk( $stoppattern = '', $consume = true, $trim = true ) {
+ return $this->tokenizer->getToken( $this->currentString, $stoppattern, $consume, $trim );
+ }
+
+ /**
+ * Enter a new subblock in the query, which must at some time be terminated by the
+ * given $endstring delimiter calling popDelimiter();
+ */
+ private function pushDelimiter( $endstring ) {
+ array_push( $this->separatorStack, $endstring );
+ }
+
+ /**
+ * Exit a subblock in the query ending with the given delimiter.
+ * If the delimiter does not match the top-most open block, false
+ * will be returned. Otherwise return true.
+ */
+ private function popDelimiter( $endstring ) {
+ $topdelim = array_pop( $this->separatorStack );
+ return ( $topdelim == $endstring );
+ }
+
+ private function isPagePropertyType( $typeid ) {
+ return $typeid == '_wpg' || $this->dataTypeRegistry->isSubDataType( $typeid );
+ }
+
+ private function hasClassPrefix( $chunk ) {
+ return in_array( smwfNormalTitleText( $chunk ), [ $this->categoryPrefix, $this->conceptPrefix, $this->categoryPrefixCannonical, $this->conceptPrefixCannonical ] );
+ }
+
+ private function isClass( $chunk ) {
+ return smwfNormalTitleText( $chunk ) == $this->categoryPrefix || smwfNormalTitleText( $chunk ) == $this->categoryPrefixCannonical;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/TermParser.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/TermParser.php
new file mode 100644
index 00000000..96b889a3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/TermParser.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace SMW\Query\Parser;
+
+/**
+ * The term parser uses a simplified string to build an #ask conform query
+ * string, for example:
+ * - `in:foo bar || (phrase:bar && not:foo)` becomes `[[in:
+ * foo bar]] || <q>[[phrase:bar]] && [[not:foo]]</q>`
+ * - `in:(foo && bar)`becomes [[in:foo]] && [[in:bar]]
+ *
+ * A custom prefix map allows to create assignments between a custom prefix and
+ * a property set and hereby simplifies the search input process.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TermParser {
+
+ /**
+ * @var []
+ */
+ private $standard_prefix = [ 'in:', 'phrase:', 'not:', 'has:', 'category:' ];
+
+ /**
+ * @var []
+ */
+ private static $cache = [];
+
+ /**
+ * The `prefix_map` is expected to contain assignments of prefixes that link
+ * to a collection of properties. The prefix is used as short-cut to cover a
+ * range of disjunctive query declarations to simplify the creation of a
+ * query construct such as:
+ *
+ * - Prefix map: `'keyword' => [ 'Has keyword', 'Keyword' ]`
+ * - Input: `keyword:foo bar`
+ * - Output: `([[Has keyword::foo bar]] || [[Keyword::foo bar]])`
+ *
+ * @var []
+ */
+ private $prefix_map = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $prefix_map
+ */
+ public function __construct( array $prefix_map = [] ) {
+ $this->prefix_map = $prefix_map;
+ }
+
+ /**
+ * @param string $term
+ *
+ * @return string
+ */
+ public function parse( $term ) {
+
+ $hash = md5( $term );
+
+ if ( isset( self::$cache[$hash] ) ) {
+ return self::$cache[$hash];
+ }
+
+ $pattern = '';
+ $custom_prefix = [];
+
+ foreach ( array_keys( $this->prefix_map ) as $p ) {
+
+ // Just in case, `in:`, `phrase:`, `has:`, and `not:` are not
+ // permitted to be overridden by a prefix assignment, `category:`
+ // can.
+ if ( in_array( $p, [ 'in', 'phrase', 'not', 'has' ] ) ) {
+ continue;
+ }
+
+ $pattern .= '|(' . $p . ':)';
+ $custom_prefix[] = "$p:";
+ }
+
+ // in:(A&&b)-> in:A && in:b
+ $this->normalize_compact_form( 'in', $pattern, $term );
+
+ // has:(A&&b) -> has:A && has:b
+ $this->normalize_compact_form( 'has', $pattern, $term );
+
+ // Simplify the processing by normalizing expressions
+ $term = str_replace( [ '<q>', '</q>' ], [ '(', ')' ], $term );
+
+ $terms = preg_split(
+ "/(in:)|(phrase:)|(not:)|(has:)|(category:)$pattern|(&&)|(AND)|(OR)|(\|\|)|(\()|(\)|(\[\[))/",
+ $term,
+ -1,
+ PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
+ );
+
+ $affix = array_merge(
+ [ '&&', 'AND', '||', 'OR', '(', ')', '[[' ],
+ $this->standard_prefix,
+ $custom_prefix
+ );
+
+ $term = '';
+ $custom = '';
+ $prefix = '';
+ $k = 0;
+
+ while ( key( $terms ) !== null ) {
+
+ $t_term = current( $terms );
+ $new = trim( $t_term );
+
+ $continue = true;
+ $space = $t_term{0} == ' ' ? ' ' : '';
+
+ // Look ahead
+ $next = next( $terms );
+ $last = substr( $term, -2 );
+
+ if ( $new === '' ) {
+ continue;
+ }
+
+ if ( $new === '[[' && $next === '[[' ) {
+ continue;
+ }
+
+ if ( in_array( $new, $custom_prefix ) ) {
+ $custom = "[[$new";
+ $prefix = $new;
+ } elseif( in_array( $new, $this->standard_prefix ) ) {
+ $term .= "[[$new";
+ } elseif ( $custom !== '' ) {
+ $custom .= $new;
+ $last = substr( $new, -2 );
+ } else {
+ $term .= "{$space}{$new}";
+ }
+
+ // has:Property Foo -> [[Property Foo::+]]
+ if ( $new === 'has:' ) {
+ $next = trim( $next );
+ $last = ']]';
+ // Already using the next element to set the property,
+ // skip in case other terms are to be found
+ $continue = !next( $terms );
+ $term = str_replace( 'has:', "$next::+$last", $term );
+ }
+
+ if ( $continue && $last === ']]' || $new === '(' || $new === '||' ) {
+ continue;
+ }
+
+ // Check next element, close expression in case of a matching
+ // affix
+ if ( $k > 0 && in_array( $next, $affix ) ) {
+ $term .= $this->close( $custom, $prefix );
+ }
+
+ // Last element
+ if ( $next === false && !in_array( $last, [ '&&', 'AND', '||', 'OR', ']]' ] ) ) {
+ if ( $custom === '' && mb_substr_count( $term, '[[' ) > mb_substr_count( $term, ']]' ) ) {
+ $term .= $this->close( $custom, $prefix );
+ } elseif ( $custom !== '' ) {
+ $term .= $this->close( $custom, $prefix );
+ }
+ }
+
+ $k++;
+ }
+
+ return self::$cache[$hash] = $this->normalize( $term );
+ }
+
+ private function close( &$custom, $prefix ) {
+
+ // Standard closing
+ if ( $custom === '' ) {
+ return "]]";
+ }
+
+ $term = "$custom]]";
+ $custom = '';
+ $terms = [];
+ $p_map = str_replace( ':', '', $prefix );
+
+ if ( !isset( $this->prefix_map[$p_map] ) ) {
+ return $term;
+ }
+
+ // A custom prefix adds additional disjunctive conditions to broaden the
+ // search radius for all its assigned properties.
+ foreach ( $this->prefix_map[$p_map] as $val ) {
+ $terms[] = str_replace( $prefix, $val . '::', $term );
+ }
+
+ // `keyword:foo bar` -> ([[Has keyword::foo bar]] || [[Keyword::foo bar]])
+ return '(' . implode( '||', $terms ) . ')';
+ }
+
+ private function normalize( $term ) {
+ return str_replace(
+ [ ')[[', ']](', '(', ')', '||', '&&', 'AND', 'OR', ']][[', '[[[[', ']]]]', ' ' ],
+ [ ') [[', ']] (', '<q>', '</q>', ' || ', ' && ', ' AND ', ' OR ', ']] [[', '[[', ']]', ' ' ],
+ $term
+ );
+ }
+
+ private function normalize_compact_form( $exp, $pattern, &$term ) {
+
+ if ( strpos( $term, "$exp:(" ) === false ) {
+ return;
+ }
+
+ preg_match_all("/$exp:\((.*?)\)/", $term, $matches );
+
+ foreach ( $matches[0] as $match ) {
+ $orig = $match;
+ $match = str_replace( "$exp:(", '', $match );
+
+ if ( substr( $match, -1 ) === ')' ) {
+ $match = substr( $match, 0, -1 );
+ }
+
+ $terms = preg_split(
+ "/(in:)|(phrase:)|(not:)|(has:)|(category:)$pattern|(&&)|(AND)|(OR)|(\|\|)|(\()|(\)|(\[\[))/",
+ $match,
+ -1,
+ PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
+ );
+
+ $replace = '';
+
+ foreach ( $terms as $t ) {
+ $t = trim( $t );
+
+ if ( in_array( $t, [ '&&', 'AND', '||', 'OR' ] ) ) {
+ $replace .= " $t ";
+ } elseif ( $t === ')' ) {
+ $replace .= "$t";
+ } else {
+ $replace .= "$exp:$t";
+ }
+ }
+
+ $term = str_replace( $orig, $replace, $term );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/Tokenizer.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/Tokenizer.php
new file mode 100644
index 00000000..101812fa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Parser/Tokenizer.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Query\Parser;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ */
+class Tokenizer {
+
+ /**
+ * @var string
+ */
+ private $defaultPattern = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param array $prefixes
+ */
+ public function setDefaultPattern( array $prefixes ) {
+
+ $pattern = '';
+
+ foreach ( $prefixes as $pref ) {
+ $pattern .= '|^' . $pref;
+ }
+
+ $this->defaultPattern = '\[\[|\]\]|::|:=|<q>|<\/q>' . $pattern . '|\|\||\|';
+ }
+
+ /**
+ * Get the next unstructured string chunk from the query string.
+ * Chunks are delimited by any of the special strings used in inline queries
+ * (such as [[, ]], <q>, ...). If the string starts with such a delimiter,
+ * this delimiter is returned. Otherwise the first string in front of such a
+ * delimiter is returned.
+ * Trailing and initial spaces are ignored if $trim is true, and chunks
+ * consisting only of spaces are not returned.
+ * If there is no more qurey string left to process, the empty string is
+ * returned (and in no other case).
+ *
+ * The stoppattern can be used to customise the matching, especially in order to
+ * overread certain special symbols.
+ *
+ * $consume specifies whether the returned chunk should be removed from the
+ * query string.
+ *
+ * @param string $currentString
+ * @param string $stoppattern
+ * @param boolean $consume
+ * @param boolean $trim
+ *
+ * @return string
+ */
+ public function getToken( &$currentString, $stoppattern = '', $consume = true, $trim = true ) {
+
+ if ( $stoppattern === '' ) {
+ $stoppattern = $this->defaultPattern;
+ }
+
+ $chunks = preg_split( '/[\s]*(' . $stoppattern . ')/iu', $currentString, 2, PREG_SPLIT_DELIM_CAPTURE );
+
+ if ( count( $chunks ) == 1 ) { // no matches anymore, strip spaces and finish
+ if ( $consume ) {
+ $currentString = '';
+ }
+
+ return $trim ? trim( $chunks[0] ) : $chunks[0];
+ } elseif ( count( $chunks ) == 3 ) { // this should generally happen if count is not 1
+ if ( $chunks[0] === '' ) { // string started with delimiter
+ if ( $consume ) {
+ $currentString = $chunks[2];
+ }
+
+ return $trim ? trim( $chunks[1] ) : $chunks[1];
+ } else {
+ if ( $consume ) {
+ $currentString = $chunks[1] . $chunks[2];
+ }
+
+ return $trim ? trim( $chunks[0] ) : $chunks[0];
+ }
+ }
+
+ // should never happen
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest.php
new file mode 100644
index 00000000..24542c92
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest.php
@@ -0,0 +1,362 @@
+<?php
+
+namespace SMW\Query;
+
+use InvalidArgumentException;
+use SMW\DataValues\PropertyChainValue;
+use SMW\Localizer;
+use SMW\Query\PrintRequest\Deserializer;
+use SMW\Query\PrintRequest\Formatter;
+use SMW\Query\PrintRequest\Serializer;
+use SMWDataValue;
+use SMWPropertyValue as PropertyValue;
+use Title;
+
+/**
+ * Container class for request for printout, as used in queries to
+ * obtain additional information for the retrieved results.
+ *
+ * @ingroup SMWQuery
+ * @author Markus Krötzsch
+ */
+class PrintRequest {
+
+ /**
+ * Query mode to print all direct categories of the current element.
+ */
+ const PRINT_CATS = 0;
+
+ /**
+ * Query mode to print all property values of a certain attribute of the
+ * current element.
+ */
+ const PRINT_PROP = 1;
+
+ /**
+ * Query mode to print the current element (page in result set).
+ */
+ const PRINT_THIS = 2;
+
+ /**
+ * Query mode to print whether current element is in given category
+ * (Boolean printout).
+ */
+ const PRINT_CCAT = 3;
+
+ /**
+ * Query mode indicating a chainable property value entity, with the last
+ * element to represent the printable output
+ */
+ const PRINT_CHAIN = 4;
+
+ protected $m_mode; // type of print request
+
+ protected $m_label; // string for labelling results, contains no markup
+
+ protected $m_data; // data entries specifyin gwhat was requested (mixed type)
+
+ protected $m_typeid = false; // id of the datatype of the printed objects, if applicable
+
+ protected $m_outputformat; // output format string for formatting results, if applicable
+
+ protected $m_hash = false; // cache your hash (currently useful since SMWQueryResult accesses the hash many times, might be dropped at some point)
+
+ protected $m_params = [];
+
+ /**
+ * Identifies whether this instance was used/added and is diconnected to
+ * the original query where it was added.
+ *
+ * Mostly used in cases where QueryProcessor::addThisPrintout was executed.
+ */
+ private $isDisconnected = false;
+
+ /**
+ * Whether the label was marked with an extra `#` identifier.
+ */
+ private $labelMarker = false;
+
+ /**
+ * Create a print request.
+ *
+ * @param integer $mode a constant defining what to printout
+ * @param string $label the string label to describe this printout
+ * @param mixed $data optional data for specifying some request, might be a property object, title, or something else; interpretation depends on $mode
+ * @param mixed $outputformat optional string for specifying an output format, e.g. an output unit
+ * @param array|null $params optional array of further, named parameters for the print request
+ */
+ public function __construct( $mode, $label, $data = null, $outputformat = false, array $params = null ) {
+ if ( ( ( $mode == self::PRINT_CATS || $mode == self::PRINT_THIS ) &&
+ !is_null( $data ) ) ||
+ ( $mode == self::PRINT_PROP &&
+ ( !( $data instanceof PropertyValue ) || !$data->isValid() ) ) ||
+ ( $mode == self::PRINT_CHAIN &&
+ ( !( $data instanceof PropertyChainValue ) || !$data->isValid() ) ) ||
+ ( $mode == self::PRINT_CCAT &&
+ !( $data instanceof Title ) )
+ ) {
+ throw new InvalidArgumentException( 'Data provided for print request does not fit the type of printout.' );
+ }
+
+ $this->m_mode = $mode;
+ $this->m_data = $data;
+ $this->m_outputformat = $outputformat;
+
+ if ( $mode == self::PRINT_CCAT && !$outputformat ) {
+ $this->m_outputformat = 'x'; // changed default for Boolean case
+ }
+
+ $this->setLabel( $label );
+
+ if ( $params !== null ) {
+ $this->m_params = $params;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isDisconnected
+ */
+ public function isDisconnected( $isDisconnected ) {
+ $this->isDisconnected = (bool)$isDisconnected;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ */
+ public function markThisLabel( $text ) {
+
+ if ( $this->m_mode !== self::PRINT_THIS ) {
+ return;
+ }
+
+ $this->labelMarker = $text !== '' && $text{0} === '#';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasLabelMarker() {
+ return $this->labelMarker;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $mode
+ *
+ * @return boolean
+ */
+ public function isMode( $mode ) {
+ return $this->m_mode === $mode;
+ }
+
+ public function getMode() {
+ return $this->m_mode;
+ }
+
+ public function getLabel() {
+ return $this->m_label;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getCanonicalLabel() {
+
+ if ( $this->m_mode === self::PRINT_PROP ) {
+ return $this->m_data->getDataItem()->getCanonicalLabel();
+ } elseif ( $this->m_mode === self::PRINT_CHAIN ) {
+ return $this->m_data->getDataItem()->getString();
+ } elseif ( $this->m_mode === self::PRINT_CATS ) {
+ return Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+ } elseif ( $this->m_mode === self::PRINT_CCAT ) {
+ return $this->m_data->getPrefixedText();
+ }
+
+ return $this->m_label;
+ }
+
+ /**
+ * Obtain an HTML-formatted representation of the label.
+ * The $linker is a Linker object used for generating hyperlinks.
+ * If it is NULL, no links will be created.
+ */
+ public function getHTMLText( $linker = null ) {
+ return Formatter::format( $this, $linker, Formatter::FORMAT_HTML );
+ }
+
+ /**
+ * Obtain a Wiki-formatted representation of the label.
+ */
+ public function getWikiText( $linker = false ) {
+ return Formatter::format( $this, $linker, Formatter::FORMAT_WIKI );
+ }
+
+ /**
+ * Convenience method for accessing the text in either HTML or Wiki format.
+ */
+ public function getText( $outputMode, $linker = null ) {
+ return Formatter::format( $this, $linker, $outputMode );
+ }
+
+ /**
+ * Return additional data related to the print request. The result might be
+ * an object of class PropertyValue or Title, or simply NULL if no data
+ * is required for the given type of printout.
+ */
+ public function getData() {
+ return $this->m_data;
+ }
+
+ public function getOutputFormat() {
+ return $this->m_outputformat;
+ }
+
+ /**
+ * If this print request refers to some property, return the type id of this property.
+ * Otherwise return '_wpg' since all other types of print request return wiki pages.
+ *
+ * @return string
+ */
+ public function getTypeID() {
+
+ if ( $this->m_typeid !== false ) {
+ return $this->m_typeid;
+ }
+
+ if ( $this->m_mode == self::PRINT_PROP ) {
+ $this->m_typeid = $this->m_data->getDataItem()->findPropertyTypeID();
+ } elseif ( $this->m_mode == self::PRINT_CHAIN ) {
+ $this->m_typeid = $this->m_data->getLastPropertyChainValue()->getDataItem()->findPropertyTypeID();
+ } else {
+ $this->m_typeid = '_wpg';
+ }
+
+ return $this->m_typeid;
+ }
+
+ /**
+ * Return a hash string that is used to eliminate duplicate
+ * print requests. The hash also includes the chosen label,
+ * so it is possible to print the same date with different
+ * labels.
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ if ( $this->m_hash !== false ) {
+ return $this->m_hash;
+ }
+
+ $this->m_hash = $this->m_mode . ':' . $this->m_label . ':';
+
+ if ( $this->m_data instanceof Title ) {
+ $this->m_hash .= $this->m_data->getPrefixedText() . ':';
+ }
+ elseif ( $this->m_data instanceof SMWDataValue ) {
+ $this->m_hash .= $this->m_data->getHash() . ':';
+ }
+
+ $this->m_hash .= $this->m_outputformat . ':' . implode( '|', $this->m_params );
+
+ return $this->m_hash;
+ }
+
+ /**
+ * Serialise this object like print requests given in \#ask.
+ *
+ * @param $params boolean that sets if the serialization should
+ * include the extra print request parameters
+ */
+ public function getSerialisation( $showparams = false ) {
+
+ // In case of disconnected instance (QueryProcessor::addThisPrintout as
+ // part of a post-processing) return an empty serialization when the
+ // mainLabel is available to avoid an extra `?...`
+ if ( $this->isMode( self::PRINT_THIS ) && $this->isDisconnected ) {
+ return '';
+ }
+
+ return Serializer::serialize( $this, $showparams );
+ }
+
+ /**
+ * Returns the value of a named parameter.
+ *
+ * @param $key string the name of the parameter key
+ *
+ * @return string Value of the paramer, if set (else FALSE)
+ */
+ public function getParameter( $key ) {
+ return array_key_exists( $key, $this->m_params ) ? $this->m_params[$key] : false;
+ }
+
+ /**
+ * Returns the array of parameters, where a string is mapped to a string.
+ *
+ * @return array Map of parameter names to values.
+ */
+ public function getParameters() {
+ return $this->m_params;
+ }
+
+ /**
+ * Sets a print request parameter.
+ *
+ * @param $key string Name of the parameter
+ * @param $value string Value for the parameter
+ */
+ public function setParameter( $key, $value ) {
+ $this->m_params[$key] = $value;
+ }
+
+ /**
+ * Removes a request parameter
+ *
+ * @since 3.0
+ *
+ * @param string $key
+ */
+ public function removeParameter( $key ) {
+ unset( $this->m_params[$key] );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @note $this->m_data = clone $data; // we assume that the caller denotes
+ * the object ot us; else he needs provide us with a clone
+ *
+ * @param string $label
+ */
+ public function setLabel( $label ) {
+ $this->m_label = $label;
+
+ if ( $this->m_data instanceof SMWDataValue ) {
+ $this->m_data->setCaption( $label );
+ }
+ }
+
+ /**
+ * @see Deserializer::deserialize
+ * @since 2.4
+ *
+ * @param string $text
+ * @param $showMode = false
+ *
+ * @return PrintRequest|null
+ */
+ public static function newFromText( $text, $showMode = false ) {
+ return Deserializer::deserialize( $text, $showMode );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Deserializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Deserializer.php
new file mode 100644
index 00000000..480d6a77
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Deserializer.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace SMW\Query\PrintRequest;
+
+use InvalidArgumentException;
+use SMW\DataValueFactory;
+use SMW\DataValues\PropertyChainValue;
+use SMW\Localizer;
+use SMW\Query\PrintRequest;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class Deserializer {
+
+ /**
+ * Create an PrintRequest object from a string description as one
+ * would normally use in #ask and related inputs. The string must start
+ * with a "?" and may contain label and formatting parameters after "="
+ * or "#", respectively. However, further parameters, given in #ask by
+ * "|+param=value" are not allowed here; they must be added
+ * individually.
+ *
+ * @since 2.5
+ *
+ * @param string $text
+ * @param boolean $showMode = false
+ *
+ * @return PrintRequest|null
+ */
+ public static function deserialize( $text, $showMode = false ) {
+
+ list( $parts, $outputFormat, $printRequestLabel ) = self::getPartsFromText(
+ $text
+ );
+
+ $data = null;
+
+ if ( $printRequestLabel === '' ) { // print "this"
+ $printmode = PrintRequest::PRINT_THIS;
+
+ // default
+ $label = '';
+
+ // Distinguish the case of an empty format
+ if ( $outputFormat === '' ) {
+ $outputFormat = null;
+ }
+
+ } elseif ( self::isCategory( $printRequestLabel ) ) { // print categories
+ $printmode = PrintRequest::PRINT_CATS;
+ $label = $showMode ? '' : Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY ); // default
+ } elseif ( PropertyChainValue::isChained( $printRequestLabel ) ) {
+
+ $data = DataValueFactory::getInstance()->newDataValueByType( PropertyChainValue::TYPE_ID );
+ $data->setUserValue( $printRequestLabel );
+
+ $printmode = PrintRequest::PRINT_CHAIN;
+ $label = $showMode ? '' : $data->getLastPropertyChainValue()->getWikiValue(); // default
+
+ } else { // print property or check category
+ $title = Title::newFromText( $printRequestLabel, SMW_NS_PROPERTY ); // trim needed for \n
+
+ // not a legal property/category name; give up
+ if ( $title === null ) {
+ return null;
+ }
+
+ if ( $title->getNamespace() == NS_CATEGORY ) {
+ $printmode = PrintRequest::PRINT_CCAT;
+ $data = $title;
+ $label = $showMode ? '' : $title->getText(); // default
+ } else { // enforce interpretation as property (even if it starts with something that looks like another namespace)
+ $printmode = PrintRequest::PRINT_PROP;
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( $printRequestLabel );
+ if ( !$data->isValid() ) { // not a property; give up
+ return null;
+ }
+ $label = $showMode ? '' : $data->getWikiValue(); // default
+ }
+ }
+
+ // "plain printout"
+ // @docu mentions that `?foo#` is equal to `?foo#-` and avoid an
+ // empty string to distinguish it from "false"
+ if ( $outputFormat === '' ) {
+ $outputFormat = '-';
+ }
+
+ // label found, use this instead of default
+ if ( count( $parts ) > 1 ) {
+ $label = trim( $parts[1] );
+ }
+
+ if ( $printmode === PrintRequest::PRINT_THIS ) {
+
+ // Cover the case of `?#Test=#-`
+ if ( strrpos( $label, '#' ) !== false ) {
+ list( $label, $outputFormat ) = explode( '#', $label );
+
+ // `?#=foo#` is equal to `?#=foo#-`
+ if ( $outputFormat === '' ) {
+ $outputFormat = '-';
+ }
+ }
+ }
+
+ try {
+ $printRequest = new PrintRequest( $printmode, $label, $data, trim( $outputFormat ) );
+ $printRequest->markThisLabel( $text );
+ } catch ( InvalidArgumentException $e ) {
+ // something still went wrong; give up
+ $printRequest = null;
+ }
+
+ return $printRequest;
+ }
+
+ private static function isCategory( $text ) {
+
+ // Check for the canonical form (singular, plural)
+ if ( $text == 'Category' || $text == 'Categories' ) {
+ return true;
+ }
+
+ return Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY ) == mb_convert_case( $text, MB_CASE_TITLE );
+ }
+
+ private static function getPartsFromText( $text ) {
+
+ // #1464
+ // Temporary encode "=" within a <> entity (<span>...</span>)
+ $text = preg_replace_callback( "/(<(.*?)>(.*?)>)/u", function( $matches ) {
+ foreach ( $matches as $match ) {
+ return str_replace( [ '=' ], [ '-3D' ], $match );
+ }
+ }, $text );
+
+ $parts = explode( '=', $text, 2 );
+
+ // Restore temporary encoding
+ $parts[0] = str_replace( [ '-3D' ], [ '=' ], $parts[0] );
+
+ if ( isset( $parts[1] ) ) {
+ $parts[1] = str_replace( [ '-3D' ], [ '=' ], $parts[1] );
+ }
+
+ $propparts = explode( '#', $parts[0], 2 );
+ $printRequestLabel = trim( $propparts[0] );
+ $outputFormat = isset( $propparts[1] ) ? trim( $propparts[1] ) : false;
+
+ return [ $parts, $outputFormat, $printRequestLabel ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Formatter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Formatter.php
new file mode 100644
index 00000000..49ad3e9d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Formatter.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Query\PrintRequest;
+
+use Linker;
+use SMW\Query\PrintRequest;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class Formatter {
+
+ const FORMAT_WIKI = SMW_OUTPUT_WIKI;
+ const FORMAT_HTML = SMW_OUTPUT_HTML;
+
+ /**
+ * Obtain an HTML-formatted or Wiki-formatted representation of the label.
+ * The $linker is a Linker object used for generating hyperlinks.
+ * If it is NULL, no links will be created.
+ *
+ * @since 2.5
+ *
+ * @param PrintRequest $printRequest
+ * @param Linker|null $linker
+ * @param integer|null $outputType
+ *
+ * @return string
+ */
+ public static function format( PrintRequest $printRequest, $linker = null, $outputType = null ) {
+
+ if ( $outputType === self::FORMAT_WIKI || $outputType === SMW_OUTPUT_WIKI ) {
+ return self::getWikiText( $printRequest, $linker );
+ }
+
+ return self::getHTMLText( $printRequest, $linker );
+ }
+
+ private static function getHTMLText( $printRequest, $linker = null ) {
+
+ $label = htmlspecialchars( $printRequest->getLabel() );
+
+ if ( $linker === null || $linker === false || $label === '' ) {
+ return $label;
+ }
+
+ switch ( $printRequest->getMode() ) {
+ case PrintRequest::PRINT_CATS:
+ return Linker::link( Title::newFromText( 'Categories', NS_SPECIAL ), $label );
+ case PrintRequest::PRINT_CCAT:
+ return Linker::link( $printRequest->getData(), $label );
+ case PrintRequest::PRINT_CHAIN:
+ case PrintRequest::PRINT_PROP:
+ return $printRequest->getData()->getShortHTMLText( $linker );
+ case PrintRequest::PRINT_THIS:
+ default:
+ return $label;
+ }
+ }
+
+ private static function getWikiText( $printRequest, $linker = false ) {
+
+ $label = $printRequest->getLabel();
+
+ if ( $linker === null || $linker === false || $label === '' ) {
+ return $label;
+ }
+
+ switch ( $printRequest->getMode() ) {
+ case PrintRequest::PRINT_CATS:
+ return '[[:' . 'Special:Categories' . '|' . $label . ']]';
+ case PrintRequest::PRINT_CHAIN:
+ case PrintRequest::PRINT_PROP:
+ return $printRequest->getData()->getShortWikiText( $linker );
+ case PrintRequest::PRINT_CCAT:
+ return '[[:' . $printRequest->getData()->getPrefixedText() . '|' . $label . ']]';
+ case PrintRequest::PRINT_THIS:
+ default:
+ return $label;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Serializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Serializer.php
new file mode 100644
index 00000000..85523c9c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequest/Serializer.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace SMW\Query\PrintRequest;
+
+use SMW\Localizer;
+use SMW\Query\PrintRequest;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class Serializer {
+
+ /**
+ * @since 2.5
+ *
+ * @param PrintRequest $printRequest
+ * @param boolean $showparams that sets if the serialization should include
+ * the extra print request parameters
+ *
+ * @return string
+ */
+ public static function serialize( PrintRequest $printRequest, $showparams = false ) {
+ $parameters = '';
+
+ if ( $showparams ) {
+
+ // #2037 index is required as helper parameter during the result
+ // display but is not part of the original request
+ if ( $printRequest->getParameter( 'lang' ) ) {
+ $printRequest->removeParameter( 'index' );
+ };
+
+ foreach ( $printRequest->getParameters() as $key => $value ) {
+ $parameters .= "|+" . $key . "=" . $value;
+ }
+ }
+
+ switch ( $printRequest->getMode() ) {
+ case PrintRequest::PRINT_CATS:
+ return self::doSerializeCat( $printRequest, $parameters );
+ case PrintRequest::PRINT_CCAT:
+ return self::doSerializeCcat( $printRequest, $parameters );
+ case PrintRequest::PRINT_CHAIN:
+ case PrintRequest::PRINT_PROP:
+ return self::doSerializeProp( $printRequest, $parameters );
+ case PrintRequest::PRINT_THIS:
+ return self::doSerializeThis( $printRequest, $parameters );
+ default:
+ return '';
+ }
+
+ return ''; // no current serialisation
+ }
+
+ private static function doSerializeCat( $printRequest, $parameters ) {
+
+ $catlabel = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+ $result = '?' . $catlabel;
+
+ if ( $printRequest->getLabel() != $catlabel ) {
+ $result .= '=' . $printRequest->getLabel();
+ }
+
+ return $result . $parameters;
+ }
+
+ private static function doSerializeCcat( $printRequest, $parameters ) {
+
+ $printname = $printRequest->getData()->getPrefixedText();
+ $result = '?' . $printname;
+
+ if ( $printRequest->getOutputFormat() != 'x' ) {
+ $result .= '#' . $printRequest->getOutputFormat();
+ }
+
+ if ( $printRequest->getLabel() != $printname ) {
+ $result .= '=' . $printRequest->getLabel();
+ }
+
+ return $result . $parameters;
+ }
+
+ private static function doSerializeProp( $printRequest, $parameters ) {
+
+ $printname = '';
+
+ $label = $printRequest->getLabel();
+ $data = $printRequest->getData();
+
+ if ( $data->isVisible() ) {
+ // #1564
+ // Use the canonical form for predefined properties to ensure
+ // that local representations are for display but points to
+ // the correct property
+ if ( $printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ $printname = $data->getDataItem()->getString();
+ // If the preferred label and invoked label are the same
+ // then no additional label is required as the label is
+ // recognized as being available by the system
+ if ( $label === $data->getLastPropertyChainValue()->getDataItem()->getPreferredLabel() ) {
+ $label = $printname;
+ }
+ } else {
+
+ $printname = $data->getDataItem()->getCanonicalLabel();
+
+ if ( $label === $data->getDataItem()->getPreferredLabel() ) {
+ $label = $printname;
+ }
+
+ // Don't carry a localized label for a predefined property
+ // (fetched via the wikiValue)
+ if ( !$data->getDataItem()->isUserDefined() && $label === $data->getWikiValue() ) {
+ $label = $data->getDataItem()->getCanonicalLabel();
+ }
+ }
+ }
+
+ $result = '?' . $printname;
+
+ if ( $printRequest->getOutputFormat() !== '' ) {
+ $result .= '#' . $printRequest->getOutputFormat();
+ }
+
+ if ( $printname != $label && $label !== '' ) {
+ $result .= '=' . $label;
+ }
+
+ return $result . $parameters;
+ }
+
+ private static function doSerializeThis( $printRequest, $parameters ) {
+
+ $result = '?';
+
+ // Has leading ?#
+ if ( $printRequest->hasLabelMarker() ) {
+ $result .= '#';
+ }
+
+ if ( $printRequest->getLabel() !== '' ) {
+ $result .= '=' . $printRequest->getLabel();
+ }
+
+ $outputFormat = $printRequest->getOutputFormat();
+
+ if ( $outputFormat !== '' && $outputFormat !== false && $outputFormat !== null ) {
+
+ // Handle ?, ?#- vs. ?#Foo=#-
+ if ( $printRequest->getLabel() !== '' ) {
+ $result .= '#';
+ }
+
+ $result .= $outputFormat;
+ }
+
+ return $result . $parameters;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequestFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequestFactory.php
new file mode 100644
index 00000000..2d60b6f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/PrintRequestFactory.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMWPropertyValue as PropertyValue;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PrintRequestFactory {
+
+ /**
+ * @since 2.1
+ *
+ * @param DIProperty $property
+ *
+ * @return PrintRequest
+ */
+ public function newFromProperty( DIProperty $property ) {
+
+ $propertyValue = DataValueFactory::getInstance()->newDataValueByType( PropertyValue::TYPE_ID );
+ $propertyValue->setDataItem( $property );
+
+ $instance = new PrintRequest(
+ PrintRequest::PRINT_PROP,
+ $propertyValue->getWikiValue(),
+ $propertyValue
+ );
+
+ return $instance;
+ }
+
+ /**
+ * @see PrintRequest::newFromText
+ *
+ * @since 2.4
+ *
+ * @param string $text
+ * @param $showMode = false
+ *
+ * @return PrintRequest|null
+ */
+ public function newFromText( $text, $showMode = false ) {
+ return PrintRequest::newFromText( $text, $showMode );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $label
+ * @param array $parameters
+ *
+ * @return PrintRequest
+ */
+ public function newThisPrintRequest( $label = '', array $parameters = [] ) {
+ return new PrintRequest( PrintRequest::PRINT_THIS, $label, null, false, $parameters );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/DefaultParamDefinition.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/DefaultParamDefinition.php
new file mode 100644
index 00000000..6f02b379
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/DefaultParamDefinition.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace SMW\Query\Processor;
+
+use SMW\Query\ResultPrinter;
+use SMW\Message;
+use ParamProcessor\ParamDefinition;
+use SMW\Query\QueryContext;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DefaultParamDefinition {
+
+ /**
+ * Produces a list of allowed parameters of a query using any specific format.
+ *
+ * @since 3.0
+ *
+ * @param integer|null $context
+ * @param ResultPrinter|null $resultPrinter
+ *
+ * @return IParamDefinition[]
+ */
+ public static function getParamDefinitions( $context = null, ResultPrinter $resultPrinter = null ) {
+ return self::buildParamDefinitions( $GLOBALS, $context, $resultPrinter );
+ }
+
+ /**
+ * @private
+ *
+ * Give grep a chance to find the msg usages:
+ *
+ * smw-paramdesc-format, smw-paramdesc-source, smw-paramdesc-limit,
+ * smw-paramdesc-offset, smw-paramdesc-link, smw-paramdesc-sort,
+ * smw-paramdesc-order, smw-paramdesc-headers, smw-paramdesc-mainlabel,
+ * smw-paramdesc-intro, smw-paramdesc-outro, smw-paramdesc-searchlabel,
+ * smw-paramdesc-default
+ *
+ * @since 3.0
+ *
+ * @param integer|null $context
+ * @param ResultPrinter|null $resultPrinter
+ *
+ * @return IParamDefinition[]
+ */
+ public static function buildParamDefinitions( $vars, $context = null, ResultPrinter $resultPrinter = null ) {
+ $params = [];
+
+ $allowedFormats = $vars['smwgResultFormats'];
+
+ foreach ( $vars['smwgResultAliases'] as $aliases ) {
+ $allowedFormats += $aliases;
+ }
+
+ $allowedFormats[] = 'auto';
+
+ $params['format'] = [
+ 'type' => 'smwformat',
+ 'default' => 'auto',
+ ];
+
+ // TODO $params['format']->setToLower( true );
+ // TODO $allowedFormats
+
+ $params['source'] = self::getSourceParam( $vars );
+
+ $params['limit'] = [
+ 'type' => 'integer',
+ 'default' => $vars['smwgQDefaultLimit'],
+ 'negatives' => false,
+ ];
+
+ $params['offset'] = [
+ 'type' => 'integer',
+ 'default' => 0,
+ 'negatives' => false,
+ 'upperbound' => $vars['smwgQUpperbound'],
+ ];
+
+ $params['link'] = [
+ 'default' => 'all',
+ 'values' => [ 'all', 'subject', 'none' ],
+ ];
+
+ // The empty string represents the page itself, which should be sorted by default.
+ $params['sort'] = [
+ 'islist' => true,
+ 'default' => [ '' ]
+ ];
+
+ $params['order'] = [
+ 'islist' => true,
+ 'default' => [],
+ 'values' => [ 'descending', 'desc', 'asc', 'ascending', 'rand', 'random' ],
+ ];
+
+ $params['headers'] = [
+ 'default' => 'show',
+ 'values' => [ 'show', 'hide', 'plain' ],
+ ];
+
+ $params['mainlabel'] = [
+ 'default' => false,
+ ];
+
+ $params['intro'] = [
+ 'default' => '',
+ ];
+
+ $params['outro'] = [
+ 'default' => '',
+ ];
+
+ $params['searchlabel'] = [
+ 'default' => Message::get( 'smw_iq_moreresults', Message::TEXT, Message::USER_LANGUAGE )
+ ];
+
+ $params['default'] = [
+ 'default' => '',
+ ];
+
+ if ( $context === QueryContext::DEFERRED_QUERY ) {
+ $params['@control'] = [
+ 'default' => '',
+ 'values' => [ 'slider' ],
+ ];
+ }
+
+ if ( !( $resultPrinter instanceof ResultPrinter ) || $resultPrinter->supportsRecursiveAnnotation() ) {
+ $params['import-annotation'] = [
+ 'message' => 'smw-paramdesc-import-annotation',
+ 'type' => 'boolean',
+ 'default' => false
+ ];
+ }
+
+ foreach ( $params as $name => &$param ) {
+ if ( is_array( $param ) ) {
+ $param['message'] = 'smw-paramdesc-' . $name;
+ }
+ }
+
+ return ParamDefinition::getCleanDefinitions( $params );
+ }
+
+ private static function getSourceParam( $vars ) {
+ $sourceValues = is_array( $vars['smwgQuerySources'] ) ? array_keys( $vars['smwgQuerySources'] ) : [];
+
+ return [
+ 'default' => array_key_exists( 'default', $sourceValues ) ? 'default' : '',
+ 'values' => $sourceValues,
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/ParamListProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/ParamListProcessor.php
new file mode 100644
index 00000000..d7c0b22d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/ParamListProcessor.php
@@ -0,0 +1,263 @@
+<?php
+
+namespace SMW\Query\Processor;
+
+use SMW\Query\PrintRequestFactory;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParamListProcessor {
+
+ /**
+ * Format type
+ */
+ const FORMAT_LEGACY = 'format.legacy';
+
+ /**
+ * Identify the PrintThis instance
+ */
+ const PRINT_THIS = 'print.this';
+
+ /**
+ * @var PrintRequestFactory
+ */
+ private $printRequestFactory;
+
+ /**
+ * @since 3.0
+ *
+ * @param PrintRequestFactory|null $printRequestFactory
+ */
+ public function __construct( PrintRequestFactory $printRequestFactory = null ) {
+ $this->printRequestFactory = $printRequestFactory;
+
+ if ( $this->printRequestFactory === null ) {
+ $this->printRequestFactory = new PrintRequestFactory();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $paramList
+ * @param string $type
+ *
+ * @return array
+ */
+ public function format( array $paramList, $type ) {
+
+ if ( $type === self::FORMAT_LEGACY ) {
+ return $this->legacy_format( $paramList );
+ }
+
+ return $paramList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ * @param boolean $showMode
+ *
+ * @return array
+ */
+ public function preprocess( array $parameters, $showMode = false ) {
+
+ $previousPrintout = null;
+
+ $serialization = [
+ 'showMode' => $showMode,
+ 'templateArgs' => false,
+ 'query' => '',
+ 'this' => [],
+ 'printouts' => [],
+ 'parameters' => []
+ ];
+
+ foreach ( $parameters as $name => $param ) {
+
+ // special handling for arrays - this can happen if the
+ // parameter came from a checkboxes input in Special:Ask:
+ if ( is_array( $param ) ) {
+ $param = implode( ',', array_keys( $param ) );
+ }
+
+ $param = $this->encodeEq( $param );
+
+ // #1258 (named_args -> named args)
+ // accept 'name' => 'value' just as '' => 'name=value':
+ if ( is_string( $name ) && ( $name !== '' ) ) {
+ $param = str_replace( "_", " ", $name ) . '=' . $param;
+ }
+
+ // Find out whether this is a mainlabel, and if so store related
+ // parameters separate since QueryProcessor::addThisPrintout is
+ // added in isolation !!??!!
+ // $isMainlabel = strpos( $param, 'mainlabel=' ) !== false;
+
+ // mainlable=Foo |+with=200 ... is currently not support
+ // use
+ // |?=Foo |+width=200 ...
+ // |mainlabel=-
+ $isMainlabel = false;
+
+ if ( $param === '' ) {
+ } elseif ( $isMainlabel ) {
+ $this->addThisPrintRequest( $name, $param, $previousPrintout, $serialization );
+ } elseif ( $param[0] == '?' ) {
+ $this->addPrintRequest( $name, $param, $previousPrintout, $serialization );
+ } elseif ( $param[0] == '+' ) {
+ $this->addPrintRequestParameter( $name, $param, $previousPrintout, $serialization );
+ } else {
+ $this->addOtherParameters( $name, $param, $serialization, $showMode );
+ }
+ }
+
+ $serialization['query'] = str_replace(
+ [ '&lt;', '&gt;', '0x003D' ],
+ ['<', '>', '=' ],
+ $serialization['query']
+ );
+
+ if ( $showMode ) {
+ $serialization['query'] = '[[:' . $serialization['query'] . ']]';
+ }
+
+ return $serialization;
+ }
+
+ private function legacy_format( array $paramList ) {
+
+ $printouts = [];
+
+ foreach ( $paramList['printouts'] as $k => $request ) {
+
+ if ( !isset( $request['label'] ) ) {
+ continue;
+ }
+
+ // #502
+ // In case of template arguments suppress the showMode to allow for
+ // labels to be generated and to be transfered to the invoked template
+ // otherwise labels will be empty and not be accessible in a template
+ $showMode = $paramList['templateArgs'] ? false : $paramList['showMode'];
+
+ $printRequest = $this->printRequestFactory->newFromText(
+ $request['label'],
+ $showMode
+ );
+
+ if ( $printRequest === null ) {
+ continue;
+ }
+
+ foreach ( $request['params'] as $key => $value ) {
+ $printRequest->setParameter( $key, $value );
+ }
+
+ $printouts[] = $printRequest;
+ }
+
+ return [
+ $paramList['query'],
+ $paramList['parameters'],
+ $printouts
+ ];
+ }
+
+ private function encodeEq ( $param ) {
+ // Bug 32955 / #640
+ // Modify (e.g. replace `=`) a condition string only if enclosed by
+ // [[ ... ]]
+ //
+ // #3560
+ // Instead of `-3D` as temporary replacement, use the UTF representation
+ // to decode the `=` sign and eliminate possible collisions with a search
+ // request that contains `-3D` string
+ return preg_replace_callback(
+ '/\[\[([^\[\]]*)\]\]/xu',
+ function( array $matches ) {
+ return str_replace( [ '=' ], [ '0x003D' ], $matches[0] );
+ },
+ $param
+ );
+ }
+
+ private function addPrintRequest( $name, $param, &$previousPrintout, array &$serialization ) {
+
+ $param = substr( $param, 1 );
+
+ // Currently we don't filter any duplicates hence the additional
+ // $name is added to distinguish printouts with the same configuration
+ $hash = md5( json_encode( $param ) . $name );
+ $previousPrintout = $hash;
+
+ $serialization['printouts'][$hash] = [
+ 'label' => $param,
+ 'params' => []
+ ];
+ }
+
+ private function addThisPrintRequest( $name, $param, &$previousPrintout, array &$serialization ) {
+
+ $param = substr( $param, 1 );
+
+ $parts = explode( '=', $param, 2 );
+ $serialization['parameters']['mainlabel'] = count( $parts ) >= 2 ? $parts[1] : null;
+ $previousPrintout = self::PRINT_THIS;
+ }
+
+ private function addPrintRequestParameter( $name, $param, $previousPrintout, array &$serialization ) {
+
+ if ( $previousPrintout === null ) {
+ return;
+ }
+
+ $param = substr( $param, 1 );
+ $parts = explode( '=', $param, 2 );
+
+ if ( $previousPrintout === self::PRINT_THIS ) {
+ if ( count( $parts ) == 2 ) {
+ $serialization['this'] = [ trim( $parts[0] ) => $parts[1] ];
+ } else {
+ $serialization['this'] = [ trim( $parts[0] ) => null ];
+ }
+ } else {
+ if ( count( $parts ) == 2 ) {
+ $serialization['printouts'][$previousPrintout]['params'][trim( $parts[0] )] = $parts[1];
+ } else {
+ $serialization['printouts'][$previousPrintout]['params'][trim( $parts[0] )] = null;
+ }
+ }
+ }
+
+ private function addOtherParameters( $name, $param, array &$serialization, $showMode ) {
+
+ // #1645
+ $parts = $showMode && $name == 0 ? $param : explode( '=', $param, 2 );
+
+ if ( is_array( $parts ) && count( $parts ) >= 2 ) {
+ $p = strtolower( trim( $parts[0] ) );
+
+ if ( $p === 'template' ) {
+ $serialization['templateArgs'] = true;
+ }
+
+ // Don't trim here, some parameters care for " "
+ //
+ // #3196
+ // Ensure to decode `0x003D` from encodeEq to support things like
+ // `|intro=[[File:Foo.png|link=Bar]]`
+ $serialization['parameters'][$p] = str_replace( [ '0x003D' ], [ '=' ], $parts[1] );
+ } else {
+ $serialization['query'] .= $param;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/QueryCreator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/QueryCreator.php
new file mode 100644
index 00000000..7d2ac2be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Processor/QueryCreator.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\Query\Processor;
+
+use SMW\DataValueFactory;
+use SMW\Localizer;
+use SMW\Query\QueryContext;
+use SMW\QueryFactory;
+use SMWPropertyValue as PropertyValue;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryCreator implements QueryContext {
+
+ /**
+ * @var QueryFactory
+ */
+ private $queryFactory;
+
+ /**
+ * @var array
+ */
+ private $params = [];
+
+ /**
+ * @see smwgQDefaultNamespaces
+ * @var null|array
+ */
+ private $defaultNamespaces = null;
+
+ /**
+ * @see smwgQDefaultLimit
+ * @var integer
+ */
+ private $defaultLimit = 0;
+
+ /**
+ * @see smwgQFeatures
+ * @var integer
+ */
+ private $queryFeatures = 0;
+
+ /**
+ * @see smwgQConceptFeatures
+ * @var integer
+ */
+ private $conceptFeatures = 0;
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryFactory $queryFactory
+ * @param array|null $defaultNamespaces
+ * @param integer $defaultLimit
+ */
+ public function __construct( QueryFactory $queryFactory, $defaultNamespaces = null, $defaultLimit = 50 ) {
+ $this->queryFactory = $queryFactory;
+ $this->defaultNamespaces = $defaultNamespaces;
+ $this->defaultLimit = $defaultLimit;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $queryFeatures
+ */
+ public function setQFeatures( $queryFeatures ) {
+ $this->queryFeatures = $queryFeatures;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $conceptFeatures
+ */
+ public function setQConceptFeatures( $conceptFeatures ) {
+ $this->conceptFeatures = $conceptFeatures;
+ }
+
+ /**
+ * Parse a query string given in SMW's query language to create an Query.
+ * Parameters are given as key-value-pairs in the given array. The parameter
+ * $context defines in what context the query is used, which affects certaim
+ * general settings.
+ *
+ * @since 2.5
+ *
+ * @param string $queryString
+ * @param array $params
+ *
+ * @return Query
+ */
+ public function create( $queryString, array $params = [] ) {
+
+ $this->params = $params;
+ $context = $this->getParam( 'context', self::INLINE_QUERY );
+
+ $queryParser = $this->queryFactory->newQueryParser(
+ $context == self::CONCEPT_DESC ? $this->conceptFeatures : $this->queryFeatures
+ );
+
+ $contextPage = $this->getParam( 'contextPage', null );
+ $queryMode = $this->getParam( 'queryMode', self::MODE_INSTANCES );
+
+ $queryParser->setContextPage( $contextPage );
+ $queryParser->setDefaultNamespaces( $this->defaultNamespaces );
+
+ $query = $this->queryFactory->newQuery(
+ $queryParser->getQueryDescription( $queryString ),
+ $context
+ );
+
+ $query->setQueryToken( $queryParser->getQueryToken() );
+ $query->setQueryString( $queryString );
+ $query->setContextPage( $contextPage );
+ $query->setQueryMode( $queryMode );
+
+ $query->setExtraPrintouts(
+ $this->getParam( 'extraPrintouts', [] )
+ );
+
+ $query->setMainLabel(
+ $this->getParam( 'mainLabel', '' )
+ );
+
+ $query->setQuerySource(
+ $this->getParam( 'source', null )
+ );
+
+ $query->setOption(
+ 'self.reference',
+ $queryParser->containsSelfReference()
+ );
+
+ // keep parsing or other errors for later output
+ $query->addErrors(
+ $queryParser->getErrors()
+ );
+
+ // set sortkeys, limit, and offset
+ $query->setOffset(
+ max( 0, trim( $this->getParam( 'offset', 0 ) ) + 0 )
+ );
+
+ $query->setLimit(
+ max( 0, trim( $this->getParam( 'limit', $this->defaultLimit ) ) + 0 ),
+ $queryMode != self::MODE_COUNT
+ );
+
+ $sortKeys = $this->getSortKeys(
+ $this->getParam( 'sort', [] ),
+ $this->getParam( 'order', [] ),
+ $this->getParam( 'defaultSort', 'ASC' )
+ );
+
+ $query->addErrors(
+ $sortKeys['errors']
+ );
+
+ $query->setSortKeys(
+ $sortKeys['keys']
+ );
+
+ return $query;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $sortParameters
+ * @param array $orderParameters
+ * @param string $defaultSort
+ *
+ * @return array ( keys => array(), errors => array() )
+ */
+ private function getSortKeys( array $sortParameters, array $orderParameters, $defaultSort ) {
+
+ $sortKeys = [];
+ $sortErros = [];
+
+ $orders = $this->normalize_order( $orderParameters );
+
+ foreach ( $sortParameters as $sort ) {
+ $sortKey = false;
+
+ // An empty string indicates we mean the page, such as element 0 on the next line.
+ // sort=,Some property
+ if ( trim( $sort ) === '' ) {
+ $sortKey = '';
+ } else {
+
+ $propertyValue = DataValueFactory::getInstance()->newDataValueByType( PropertyValue::TYPE_ID );
+ $propertyValue->setOption( PropertyValue::OPT_QUERY_CONTEXT, true );
+
+ $propertyValue->setUserValue(
+ $this->normalize_sort( trim( $sort ) )
+ );
+
+ if ( $propertyValue->isValid() ) {
+ $sortKey = $propertyValue->getDataItem()->getKey();
+ } else {
+ $sortErros = array_merge( $sortErros, $propertyValue->getErrors() );
+ }
+ }
+
+ if ( $sortKey !== false ) {
+ $order = empty( $orders ) ? $defaultSort : array_shift( $orders );
+ $sortKeys[$sortKey] = $order;
+ }
+ }
+
+ // If more sort arguments are provided then properties, assume the first one is for the page.
+ // TODO: we might want to add errors if there is more then one.
+ if ( !array_key_exists( '', $sortKeys ) && !empty( $orders ) ) {
+ $sortKeys[''] = array_shift( $orders );
+ }
+
+ return [ 'keys' => $sortKeys, 'errors' => $sortErros ];
+ }
+
+ private function normalize_order( $orderParameters ) {
+ $orders = [];
+
+ foreach ( $orderParameters as $key => $order ) {
+ $order = strtolower( trim( $order ) );
+ if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) {
+ $orders[$key] = 'DESC';
+ } elseif ( ( $order == 'random' ) || ( $order == 'rand' ) ) {
+ $orders[$key] = 'RANDOM';
+ } else {
+ $orders[$key] = 'ASC';
+ }
+ }
+
+ return $orders;
+ }
+
+ private function normalize_sort( $sort ) {
+ return Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY ) == mb_convert_case( $sort, MB_CASE_TITLE ) ? '_INST' : $sort;
+ }
+
+ private function getParam( $key, $default ) {
+ return isset( $this->params[$key] ) ? $this->params[$key] : $default;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotator.php
new file mode 100644
index 00000000..6390af5d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotator.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\PropertyAnnotator;
+
+/**
+ * Specifying the ProfileAnnotator interface
+ *
+ * @ingroup SMW
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+interface ProfileAnnotator extends PropertyAnnotator {
+
+ /**
+ * Returns the query meta data property
+ *
+ * @since 1.9
+ *
+ * @return DIProperty
+ */
+ public function getProperty();
+
+ /**
+ * Returns the query meta data container
+ *
+ * @since 1.9
+ *
+ * @return DIContainer
+ */
+ public function getContainer();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotatorFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotatorFactory.php
new file mode 100644
index 00000000..52f1e618
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotatorFactory.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\DescriptionProfileAnnotator;
+use SMW\Query\ProfileAnnotators\DurationProfileAnnotator;
+use SMW\Query\ProfileAnnotators\FormatProfileAnnotator;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Query\ProfileAnnotators\ParametersProfileAnnotator;
+use SMW\Query\ProfileAnnotators\SourceProfileAnnotator;
+use SMW\Query\ProfileAnnotators\StatusCodeProfileAnnotator;
+use SMW\Query\ProfileAnnotators\SchemaLinkProfileAnnotator;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ProfileAnnotatorFactory {
+
+ /**
+ * @since 2.1
+ *
+ * @param Query $query
+ * @param string $format
+ *
+ * @return ProfileAnnotator
+ */
+ public function newProfileAnnotator( Query $query, $format ) {
+
+ $profileAnnotator = $this->newDescriptionProfileAnnotator(
+ $query
+ );
+
+ $profileAnnotator = $this->newFormatProfileAnnotator(
+ $profileAnnotator,
+ $format
+ );
+
+ $profileAnnotator = $this->newParametersProfileAnnotator(
+ $profileAnnotator,
+ $query
+ );
+
+ $profileAnnotator = $this->newDurationProfileAnnotator(
+ $profileAnnotator,
+ $query->getOption( Query::PROC_QUERY_TIME )
+ );
+
+ $profileAnnotator = $this->newSourceProfileAnnotator(
+ $profileAnnotator,
+ $query->getQuerySource()
+ );
+
+ $profileAnnotator = $this->newStatusCodeProfileAnnotator(
+ $profileAnnotator,
+ $query->getOption( Query::PROC_STATUS_CODE )
+ );
+
+ $profileAnnotator = $this->newSchemaLinkProfileAnnotator(
+ $profileAnnotator,
+ $query->getOption( 'schema_link' )
+ );
+
+ return $profileAnnotator;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Query $query
+ *
+ * @return DescriptionProfileAnnotator
+ */
+ public function newDescriptionProfileAnnotator( Query $query ) {
+
+ $profileAnnotator = new NullProfileAnnotator(
+ $this->newDIContainer( $query )
+ );
+
+ $profileAnnotator = new DescriptionProfileAnnotator(
+ $profileAnnotator,
+ $query->getDescription()
+ );
+
+ return $profileAnnotator;
+ }
+
+ private function newFormatProfileAnnotator( $profileAnnotator, $format ) {
+ return new FormatProfileAnnotator( $profileAnnotator, $format );
+ }
+
+ private function newParametersProfileAnnotator( $profileAnnotator, $query ) {
+
+ if ( $query->getOption( Query::OPT_PARAMETERS ) === false ) {
+ return $profileAnnotator;
+ }
+
+ return new ParametersProfileAnnotator( $profileAnnotator, $query );
+ }
+
+ private function newDurationProfileAnnotator( $profileAnnotator, $duration ) {
+
+ if ( $duration == 0 ) {
+ return $profileAnnotator;
+ }
+
+ return new DurationProfileAnnotator( $profileAnnotator, $duration );
+ }
+
+ private function newSourceProfileAnnotator( $profileAnnotator, $querySource ) {
+
+ if ( $querySource === '' || $querySource === null ) {
+ return $profileAnnotator;
+ }
+
+ return new SourceProfileAnnotator( $profileAnnotator, $querySource );
+ }
+
+ private function newStatusCodeProfileAnnotator( $profileAnnotator, $statusCodes ) {
+
+ if ( $statusCodes === false || $statusCodes === null || $statusCodes === [] ) {
+ return $profileAnnotator;
+ }
+
+ return new StatusCodeProfileAnnotator( $profileAnnotator, $statusCodes );
+ }
+
+ private function newSchemaLinkProfileAnnotator( $profileAnnotator, $schemaLink ) {
+
+ if ( $schemaLink === false || $schemaLink === null ) {
+ return $profileAnnotator;
+ }
+
+ return new SchemaLinkProfileAnnotator( $profileAnnotator, $schemaLink );
+ }
+
+ /**
+ * #1416 create container manually to avoid any issues that may arise from
+ * a failed Title::makeTitleSafe.
+ */
+ private function newDIContainer( Query $query ) {
+
+ $subject = $query->getContextPage();
+
+ if ( $subject === null ) {
+ $containerSemanticData = ContainerSemanticData::makeAnonymousContainer();
+ } else {
+ $subject = new DIWikiPage(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $query->getQueryId()
+ );
+
+ $containerSemanticData = new ContainerSemanticData( $subject );
+ }
+
+ return new DIContainer(
+ $containerSemanticData
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DescriptionProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DescriptionProfileAnnotator.php
new file mode 100644
index 00000000..6823eeec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DescriptionProfileAnnotator.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\Language\Description;
+use SMW\Query\ProfileAnnotator;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DescriptionProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var Description
+ */
+ private $description;
+
+ /**
+ * @since 1.9
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param Description $description
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, Description $description ) {
+ parent::__construct( $profileAnnotator );
+ $this->description = $description;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+ $this->addQueryString( $this->description->getQueryString() );
+ $this->addQuerySize( $this->description->getSize() );
+ $this->addQueryDepth( $this->description->getDepth() );
+ }
+
+ private function addQueryString( $queryString ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKST' ),
+ new DIBlob( $queryString )
+ );
+ }
+
+ private function addQuerySize( $size ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKSI' ),
+ new DINumber( $size )
+ );
+ }
+
+ private function addQueryDepth( $depth ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKDE' ),
+ new DINumber( $depth )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DurationProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DurationProfileAnnotator.php
new file mode 100644
index 00000000..cf237c23
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/DurationProfileAnnotator.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDINumber as DINumber;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DurationProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var integer
+ */
+ private $duration;
+
+ /**
+ * @since 1.9
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param integer $duration
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, $duration ) {
+ parent::__construct( $profileAnnotator );
+ $this->duration = $duration;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+ if ( $this->duration > 0 ) {
+ $this->addGreaterThanZeroQueryDuration( $this->duration );
+ }
+ }
+
+ private function addGreaterThanZeroQueryDuration( $duration ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKDU' ),
+ new DINumber( $duration )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/FormatProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/FormatProfileAnnotator.php
new file mode 100644
index 00000000..8ba1ced4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/FormatProfileAnnotator.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FormatProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var string
+ */
+ private $format;
+
+ /**
+ * @since 1.9
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param string $format
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, $format ) {
+ parent::__construct( $profileAnnotator );
+ $this->format = $format;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+ $this->addQueryFormat( $this->format );
+ }
+
+ private function addQueryFormat( $format ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKFO' ),
+ new DIBlob( $format )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/NullProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/NullProfileAnnotator.php
new file mode 100644
index 00000000..bd5b686a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/NullProfileAnnotator.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NullProfileAnnotator implements ProfileAnnotator {
+
+ /**
+ * @var DIContainer
+ */
+ private $container;
+
+ /**
+ * @since 1.9
+ *
+ * @param DIContainer $container
+ */
+ public function __construct( DIContainer $container ) {
+ $this->container = $container;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->getSemanticData()->getErrors();
+ }
+
+ /**
+ * ProfileAnnotator::getProperty
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getProperty() {
+ return new DIProperty( '_ASK' );
+ }
+
+ /**
+ * ProfileAnnotator::getContainer
+ *
+ * @since 1.9
+ *
+ * @return DIContainer
+ */
+ public function getContainer() {
+ return $this->container;
+ }
+
+ /**
+ * ProfileAnnotator::getSemanticData
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData() {
+ return $this->container->getSemanticData();
+ }
+
+ /**
+ * ProfileAnnotator::addAnnotation
+ *
+ * @since 1.9
+ */
+ public function addAnnotation() {
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ParametersProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ParametersProfileAnnotator.php
new file mode 100644
index 00000000..0f59128e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ParametersProfileAnnotator.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDIBlob as DIBlob;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ParametersProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var Query
+ */
+ private $query;
+
+ /**
+ * @since 2.5
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param Query $query
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, Query $query ) {
+ parent::__construct( $profileAnnotator );
+ $this->query = $query;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+
+ list( $sort, $order ) = $this->doSerializeSortKeys( $this->query );
+
+ $options = [
+ 'limit' => $this->query->getLimit(),
+ 'offset' => $this->query->getOffset(),
+ 'sort' => $sort,
+ 'order' => $order,
+ 'mode' => $this->query->getQueryMode()
+ ];
+
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKPA' ),
+ new DIBlob( json_encode( $options ) )
+ );
+ }
+
+ private function doSerializeSortKeys( $query ) {
+
+ $sort = [];
+ $order = [];
+
+ if ( $query->getSortKeys() === null ) {
+ return [ $sort, $order ];
+ }
+
+ foreach ( $query->getSortKeys() as $key => $value ) {
+ $sort[] = $key;
+ $order[] = strtolower( $value );
+ }
+
+ return [ $sort, $order ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ProfileAnnotatorDecorator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ProfileAnnotatorDecorator.php
new file mode 100644
index 00000000..ef607550
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/ProfileAnnotatorDecorator.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\Query\ProfileAnnotator;
+use SMW\SemanticData;
+
+/**
+ * Decorator implementing the ProfileAnnotator interface
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+abstract class ProfileAnnotatorDecorator implements ProfileAnnotator {
+
+ /**
+ * @var ProfileAnnotator
+ */
+ protected $profileAnnotator;
+
+ /**
+ * @since 1.9
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator ) {
+ $this->profileAnnotator = $profileAnnotator;
+ }
+
+ /**
+ * ProfileAnnotator::getProperty
+ *
+ * @since 1.9
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return $this->profileAnnotator->getProperty();
+ }
+
+ /**
+ * ProfileAnnotator::getContainer
+ *
+ * @since 1.9
+ *
+ * @return DIContainer
+ */
+ public function getContainer() {
+ return $this->profileAnnotator->getContainer();
+ }
+
+ /**
+ * @see ProfileAnnotator::getSemanticData
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData() {
+ return $this->profileAnnotator->getSemanticData();
+ }
+
+ /**
+ * ProfileAnnotator::addAnnotation
+ *
+ * @since 1.9
+ *
+ * @return ProfileAnnotator
+ */
+ public function addAnnotation() {
+ $this->profileAnnotator->addAnnotation();
+ $this->addPropertyValues();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ */
+ public function pushAnnotationsTo( SemanticData $semanticData ) {
+
+ $this->addAnnotation();
+
+ $semanticData->addPropertyObjectValue(
+ $this->profileAnnotator->getProperty(),
+ $this->profileAnnotator->getContainer()
+ );
+ }
+
+ /**
+ * @since 1.9
+ */
+ protected abstract function addPropertyValues();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SchemaLinkProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SchemaLinkProfileAnnotator.php
new file mode 100644
index 00000000..111d79a0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SchemaLinkProfileAnnotator.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotator;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaLinkProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var string
+ */
+ private $schemaLink = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param string $SchemaLink
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, $schemaLink ) {
+ parent::__construct( $profileAnnotator );
+ $this->schemaLink = $schemaLink;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+
+ if ( $this->schemaLink === '' ) {
+ return;
+ }
+
+ if ( !is_string( $this->schemaLink ) ) {
+ throw new RuntimeException( "Expected a string as `Schema link` value!" );
+ }
+
+ $this->addSchemaLinkAnnotation( $this->schemaLink );
+ }
+
+ private function addSchemaLinkAnnotation( $schemaLink ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_SCHEMA_LINK' ),
+ new DIWikiPage( $schemaLink, SMW_NS_SCHEMA )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SourceProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SourceProfileAnnotator.php
new file mode 100644
index 00000000..31837010
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/SourceProfileAnnotator.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SourceProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var string
+ */
+ private $querySource;
+
+ /**
+ * @since 2.5
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param string $querySource
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, $querySource = '' ) {
+ parent::__construct( $profileAnnotator );
+ $this->querySource = $querySource;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+ if ( $this->querySource !== '' ) {
+ $this->addQuerySource( $this->querySource );
+ }
+ }
+
+ private function addQuerySource( $querySource ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKSC' ),
+ new DIBlob( $querySource )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/StatusCodeProfileAnnotator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/StatusCodeProfileAnnotator.php
new file mode 100644
index 00000000..8afc5ce4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ProfileAnnotators/StatusCodeProfileAnnotator.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Query\ProfileAnnotators;
+
+use SMW\DIProperty;
+use SMW\Query\ProfileAnnotator;
+use SMWDINumber as DINumber;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class StatusCodeProfileAnnotator extends ProfileAnnotatorDecorator {
+
+ /**
+ * @var array
+ */
+ private $statusCodes = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param ProfileAnnotator $profileAnnotator
+ * @param array $statusCodes
+ */
+ public function __construct( ProfileAnnotator $profileAnnotator, array $statusCodes = [] ) {
+ parent::__construct( $profileAnnotator );
+ $this->statusCodes = $statusCodes;
+ }
+
+ /**
+ * ProfileAnnotatorDecorator::addPropertyValues
+ */
+ protected function addPropertyValues() {
+ if ( $this->statusCodes !== [] ) {
+ foreach ( $this->statusCodes as $statusCode ) {
+ $this->addStatusCodeAnnotation( $statusCode );
+ }
+ }
+ }
+
+ private function addStatusCodeAnnotation( $statusCode ) {
+ $this->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( '_ASKCO' ),
+ new DINumber( $statusCode )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryComparator.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryComparator.php
new file mode 100644
index 00000000..f2fb168f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryComparator.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace SMW\Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.5.3
+ *
+ * @author mwjames
+ * @author Jeroen De Dauw
+ */
+class QueryComparator {
+
+ /**
+ * @var QueryComparator
+ */
+ private static $instance = null;
+
+ /**
+ * @var array
+ */
+ private $comparators = null;
+
+ /**
+ * @var array
+ */
+ private $reverseCache = [];
+
+ /**
+ * @since 2.3
+ *
+ * @param string $comparatorList
+ * @param boolean $strictComparators
+ */
+ public function __construct( $comparatorList, $strictComparators ) {
+ $this->comparators = $this->getEnabledComparators( $comparatorList, $strictComparators );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return self
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self(
+ $GLOBALS['smwgQComparators'],
+ $GLOBALS['smwStrictComparators']
+ );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * Gets an array with all supported comparator strings.
+ * The string for SMW_CMP_EQ, which is an empty string, is not in this list.
+ *
+ * @since 1.5.3
+ *
+ * @return array
+ */
+ public function getComparatorStrings() {
+ return array_keys( $this->comparators );
+ }
+
+ /**
+ * Gets the SMW_CMP_ for a string comparator, falling back to the
+ * $defaultComparator when none is found.
+ *
+ * @since 1.5.3
+ *
+ * @param string $string
+ * @param integer $defaultComparator Item of the SMW_CMP_ enum
+ *
+ * @return integer Item of the SMW_CMP_ enum
+ */
+ public function getComparatorFromString( $string, $defaultComparator = SMW_CMP_EQ ) {
+
+ if ( $string === '' ) {
+ return SMW_CMP_EQ;
+ }
+
+ return array_key_exists( $string, $this->comparators ) ? $this->comparators[$string] : $defaultComparator;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $value
+ * @param integer $comparator
+ *
+ * @return boolean
+ */
+ public function containsComparator( $value, $comparator = SMW_CMP_EQ ) {
+ return $this->extractComparatorFromString( $value ) === $comparator;
+ }
+
+ /**
+ * Extract possible comparators from a value and alter it to consist
+ * only of the remaining effective value string (without the comparator).
+ *
+ * @since 2.4
+ *
+ * @param $value
+ *
+ * @return integer
+ */
+ public function extractComparatorFromString( &$value ) {
+
+ $comparator = SMW_CMP_EQ;
+
+ foreach ( $this->getComparatorStrings() as $string ) {
+ if ( strpos( $value, $string ) === 0 ) {
+ $comparator = $this->getComparatorFromString( substr( $value, 0, strlen( $string ) ) );
+ $value = substr( $value, strlen( $string ) );
+ break;
+ }
+ }
+
+ return $comparator;
+ }
+
+ /**
+ * Gets the comparator string for a comparator.
+ *
+ * @since 1.5.3
+ *
+ * @param $comparator
+ *
+ * @return string
+ */
+ public function getStringForComparator( $comparator ) {
+
+ if ( $this->reverseCache === [] ) {
+ $this->reverseCache = array_flip( $this->comparators );
+ }
+
+ if ( $comparator == SMW_CMP_EQ ) {
+ return '';
+ } elseif ( array_key_exists( $comparator, $this->reverseCache ) ) {
+ return $this->reverseCache[$comparator];
+ }
+
+ throw new Exception( "Comparator $comparator does not have a string representatation" );
+ }
+
+ private function getEnabledComparators( $comparatorList, $strictComparators ) {
+
+ // Note: Comparators that contain other comparators at the beginning of
+ // the string need to be at beginning of the array.
+ $comparators = [
+ 'like:' => SMW_CMP_PRIM_LIKE,
+ 'nlike:' => SMW_CMP_PRIM_NLKE,
+ 'in:' => SMW_CMP_IN,
+ 'not:' => SMW_CMP_NOT,
+ 'phrase:' => SMW_CMP_PHRASE,
+ '!~' => SMW_CMP_NLKE,
+ '<<' => SMW_CMP_LESS,
+ '>>' => SMW_CMP_GRTR,
+ '<' => $strictComparators ? SMW_CMP_LESS : SMW_CMP_LEQ,
+ '>' => $strictComparators ? SMW_CMP_GRTR : SMW_CMP_GEQ,
+ '≤' => SMW_CMP_LEQ,
+ '≥' => SMW_CMP_GEQ,
+ '!' => SMW_CMP_NEQ,
+ '~' => SMW_CMP_LIKE,
+ ];
+
+ if ( strpos( $comparatorList, '|' ) === false ) {
+ return $comparators;
+ }
+
+ $allowedComparators = explode( '|', $comparatorList );
+
+ // Remove the comparators that are not allowed.
+ foreach ( $comparators as $string => $comparator ) {
+ if ( !in_array( $string, $allowedComparators ) ) {
+ unset( $comparators[$string] );
+ }
+ }
+
+ return $comparators;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryContext.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryContext.php
new file mode 100644
index 00000000..936c15ed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryContext.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Query;
+
+/**
+ * "Query contexts" define restrictions during query parsing and
+ * are used to preconfigure query (e.g. special pages show no further
+ * results link)
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ */
+interface QueryContext {
+
+ /**
+ * Query for special page
+ */
+ const SPECIAL_PAGE = 1000;
+
+ /**
+ * Query for inline use
+ */
+ const INLINE_QUERY = 1001;
+
+ /**
+ * Deferred query definition
+ */
+ const DEFERRED_QUERY = 1002;
+
+ /**
+ * Query for concept definition
+ */
+ const CONCEPT_DESC = 1003;
+
+ /**
+ * normal instance retrieval
+ */
+ const MODE_INSTANCES = 1;
+
+ /**
+ * find result count only
+ */
+ const MODE_COUNT = 2;
+
+ /**
+ * prepare query, but show debug data instead of executing it
+ */
+ const MODE_DEBUG = 3;
+
+ /**
+ * do nothing with the query
+ */
+ const MODE_NONE = 4;
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryLinker.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryLinker.php
new file mode 100644
index 00000000..e7bb42d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryLinker.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\Message;
+use SMW\DIProperty;
+use SMWInfolink as Infolink;
+use SMWQuery as Query;
+
+/**
+ * Representing a Special:Ask query link to further query results
+ *
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryLinker {
+
+ /**
+ * @since 2.4
+ *
+ * @param Query $query
+ * @param array $parameters
+ *
+ * @return Infolink
+ */
+ public static function get( Query $query, array $parameters = [] ) {
+
+ $link = Infolink::newInternalLink( '', ':Special:Ask', false, [] );
+ $link->setCompactLink( $GLOBALS['smwgCompactLinkSupport'] );
+
+ foreach ( $parameters as $key => $value ) {
+
+ if ( !is_string( $key ) ) {
+ continue;
+ }
+
+ $link->setParameter( $value, $key );
+ }
+
+ $params = self::getParameters( $query );
+
+ foreach ( $params as $key => $param ) {
+ $link->setParameter( $param, is_string( $key ) ? $key : false );
+ }
+
+ $link->setCaption(
+ ' ' . Message::get( 'smw_iq_moreresults', Message::TEXT, Message::USER_LANGUAGE )
+ );
+
+ return $link;
+ }
+
+ private static function getParameters( $query ) {
+
+ $params = [ trim( $query->getQueryString( true ) ) ];
+
+ foreach ( $query->getExtraPrintouts() as /* PrintRequest */ $printout ) {
+ if ( ( $serialisation = $printout->getSerialisation( true ) ) !== '' ) {
+ $params[] = $serialisation;
+ }
+ }
+
+ if ( $query->getMainLabel() !== false ) {
+ $params['mainlabel'] = $query->getMainLabel();
+ }
+
+ if ( $query->getQuerySource() !== '' ) {
+ $params['source'] = $query->getQuerySource();
+ }
+
+ $params['offset'] = $query->getOffset();
+
+ if ( $params['offset'] === 0 ) {
+ unset( $params['offset'] );
+ }
+
+ if ( $query->getLimit() > 0 ) {
+ $params['limit'] = $query->getLimit();
+ }
+
+ $sortKeys = $query->getSortKeys();
+ $count = count( $sortKeys );
+
+ if ( $count == 0 ) {
+ return $params;
+ }
+
+ $order = [];
+ $sort = [];
+
+ foreach ( $sortKeys as $key => $order_by ) {
+
+ $order_by = strtolower( $order_by );
+
+ // Default mode, skip
+ if ( $count == 1 && $key === '' && $order_by === 'asc' ) {
+ continue;
+ }
+
+ // Avoid predefined properties to appear as key as in _MDAT
+ if ( $key !== '' && $key{0} === '_' ) {
+ $key = DIProperty::newFromUserLabel( $key )->getLabel();
+ } else {
+ $key = str_replace( '_', ' ', $key );
+ }
+
+ $order[] = $order_by;
+ $sort[] = $key;
+ }
+
+ if ( $sort !== [] ) {
+ $params['order'] = implode( ',', $order );
+ $params['sort'] = implode( ',', $sort );
+ }
+
+ return $params;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QuerySourceFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QuerySourceFactory.php
new file mode 100644
index 00000000..8c4a4f0c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QuerySourceFactory.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace SMW\Query;
+
+use RuntimeException;
+use SMW\QueryEngine;
+use SMW\Store;
+use SMW\StoreAware;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QuerySourceFactory {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var array
+ */
+ private $querySources = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param array $querySources
+ */
+ public function __construct( Store $store, $querySources = [] ) {
+ $this->store = $store;
+ $this->querySources = $querySources;
+
+ // Standard store
+ $this->querySources['sql_store'] = 'SMW\SQLStore\SQLStore';
+ }
+
+ /**
+ * @see DefaultSettings::$smwgQuerySources
+ *
+ * @since 2.5
+ *
+ * @param string|null $source
+ *
+ * @return QueryEngine|Store
+ * @throws RuntimeException
+ */
+ public function get( $source = null ) {
+
+ $params = [];
+
+ if ( $source !== '' && isset( $this->querySources[$source] ) ) {
+
+ $querySource = $this->querySources[$source];
+
+ // [ '\SMW\FooHandler', ... parameters ],
+ if ( is_array( $querySource ) ) {
+ $source = array_shift( $querySource );
+ $params = $querySource;
+ } else {
+ $source = $this->querySources[$source];
+ }
+ }
+
+ // Fallback to the default store
+ if ( $source === null || !class_exists( $source ) ) {
+ $source = $this->store;
+ } elseif ( $params !== [] ) {
+ $source = new $source( $params );
+ } else {
+ $source = new $source;
+ }
+
+ if ( !$source instanceof QueryEngine && !$source instanceof Store ) {
+ throw new RuntimeException( get_class( $source ) . " does not match the expected QueryEngine interface." );
+ }
+
+ if ( $source instanceof StoreAware ) {
+ $source->setStore( $this->store );
+ }
+
+ return $source;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|null $source
+ *
+ * @return string
+ */
+ public function toString( $source = null ) {
+
+ if ( $source === 'sql_store' ) {
+ return 'SMWSQLStore';
+ }
+
+ if ( $source !== '' && $source !== null ) {
+ return $source;
+ }
+
+ return json_encode( $this->store->getInfo( 'store' ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryStringifier.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryStringifier.php
new file mode 100644
index 00000000..9f1c1ee1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryStringifier.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Query;
+
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryStringifier {
+
+ /**
+ * @since 2.5
+ *
+ * @param Query $query
+ *
+ * @return string
+ */
+ public static function rawUrlEncode( Query $query ) {
+ return rawurlencode( self::toString( $query ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ * @param boolean $printParameters
+ *
+ * @return string
+ */
+ public static function toArray( Query $query, $printParameters = false ) {
+
+ $serialized = [];
+ $serialized['conditions'] = $query->getQueryString();
+
+ $serialized['parameters'] = [
+ 'limit' => $query->getLimit(),
+ 'offset' => $query->getOffset(),
+ 'mainlabel' => $query->getMainlabel()
+ ];
+
+ if ( $query->getQuerySource() !== null && $query->getQuerySource() !== '' ) {
+ $serialized['parameters']['source'] = $query->getQuerySource();
+ }
+
+ list( $serialized['sort'], $serialized['order'] ) = self::sortKeys(
+ $query
+ );
+
+ if ( $serialized['sort'] !== [] ) {
+ $serialized['parameters']['sort'] = implode( ',', $serialized['sort'] );
+ }
+
+ if ( $serialized['order'] !== [] ) {
+ $serialized['parameters']['order'] = implode( ',', $serialized['order'] );
+ }
+
+ unset( $serialized['sort'] );
+ unset( $serialized['order'] );
+
+ $serialized['printouts'] = self::printouts(
+ $query,
+ $printParameters
+ );
+
+ return $serialized;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Query $query
+ *
+ * @return string
+ */
+ public static function toJson( Query $query, $printParameters = false ) {
+ return json_encode( self::toArray( $query, $printParameters ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Query $query
+ *
+ * @return string
+ */
+ public static function toString( Query $query, $printParameters = false ) {
+
+ $serialized = self::toArray( $query, $printParameters );
+
+ $string = $serialized['conditions'];
+
+ if ( $serialized['printouts'] !== [] ) {
+ $string .= '|' . implode( '|', $serialized['printouts'] );
+ }
+
+ foreach ( $serialized['parameters'] as $key => $value ) {
+ $string .= "|$key=$value";
+ }
+
+ return $string;
+ }
+
+ private static function printouts( $query, $showParams = false ) {
+
+ $printouts = [];
+
+ if ( $query->getExtraPrintouts() === null ) {
+ return $printouts;
+ }
+
+ foreach ( $query->getExtraPrintouts() as $printout ) {
+ if ( ( $serialisation = $printout->getSerialisation( $showParams ) ) !== '' ) {
+ $printouts[] = $serialisation;
+ }
+ }
+
+ return $printouts;
+ }
+
+ private static function sortKeys( $query ) {
+
+ $sort = [];
+ $order = [];
+
+ if ( $query->getSortKeys() === null ) {
+ return [ $sort, $order ];
+ }
+
+ foreach ( $query->getSortKeys() as $key => $value ) {
+
+ if ( $key === '' ) {
+ continue;
+ }
+
+ $sort[] = str_replace( '_', ' ', $key );
+ $order[] = strtolower( $value );
+ }
+
+ return [ $sort, $order ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryToken.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryToken.php
new file mode 100644
index 00000000..d6deef12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/QueryToken.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DIWikiPage;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Utils\Tokenizer;
+use SMWDIBlob as DIBlob;
+
+/**
+ * For a wildcard search, build tokens from the query string, and allow to highlight
+ * them in the result set.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryToken {
+
+ // TokensHighlighter
+ // QueryTokensHighlighter
+
+ /**
+ * Highlighter marker type
+ */
+ const HL_WIKI = 'HL_WIKI';
+ const HL_BOLD = 'HL_BOLD';
+ const HL_SPAN = 'HL_SPAN';
+ const HL_UNDERLINE = 'HL_UNDERLINE';
+
+ /**
+ * @var array
+ */
+ private $tokens = [];
+
+ /**
+ * @var array
+ */
+ private $minHighlightTokenLength = 4;
+
+ /**
+ * @var array
+ */
+ private $highlightType = 4;
+
+ /**
+ * @var string
+ */
+ private $outputFormat;
+
+ /**
+ * @since 2.5
+ *
+ * @param array $tokens
+ */
+ public function __construct( array $tokens = [] ) {
+ $this->tokens = $tokens;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getTokens() {
+ return $this->tokens;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Description $description
+ */
+ public function addFromDesciption( Description $description ) {
+
+ if ( $description instanceof Conjunction ) {
+ foreach ( $description->getDescriptions() as $desc ) {
+ return $this->addFromDesciption( $desc );
+ }
+ }
+
+ if ( $description instanceof SomeProperty ) {
+ return $this->addFromDesciption( $description->getDescription() );
+ }
+
+ if ( !$description instanceof ValueDescription ) {
+ return;
+ }
+
+ $isProximate = $description->getComparator() === SMW_CMP_LIKE || $description->getComparator() === SMW_CMP_PRIM_LIKE;
+
+ // [[SomeProperty::~*Foo*]] / [[SomeProperty::like:*Foo*]]
+ if ( $isProximate && $description->getDataItem() instanceof DIBlob ) {
+ return $this->addTokensFromText( $description->getDataItem()->getString() );
+ }
+
+ // [[~~* ... *]]
+ if ( $description->getDataItem() instanceof DIWikiPage && strpos( $description->getDataItem()->getDBKey(), '~' ) !== false ) {
+ return $this->addTokensFromText( $description->getDataItem()->getDBKey() );
+ }
+ }
+
+ /**
+ * Sets format information (|?Foo#-hl) from a result printer
+ *
+ * @since 2.5
+ *
+ * @param string $outputFormat
+ */
+ public function setOutputFormat( $outputFormat ) {
+ $this->outputFormat = $outputFormat;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ * @param type $text
+ *
+ * @return string
+ */
+ public function highlight( $text, $type = self::HL_BOLD ) {
+
+ if ( $this->tokens === [] || strpos( strtolower( $this->outputFormat ), '-hl' ) === false ) {
+ return $text;
+ }
+
+ return $this->doHighlight( $text, $type, array_keys( $this->tokens ) );
+ }
+
+ private function doHighlight( $text, $type, $tokens ) {
+
+ if ( $type === self::HL_BOLD ) {
+ $replacement = "<b>$0</b>";
+ } elseif ( $type === self::HL_UNDERLINE ) {
+ $replacement = "<u>$0</u>";
+ } elseif ( $type === self::HL_SPAN ) {
+ $replacement = "<span class='smw-query-token'>$0</span>";
+ } else {
+ $replacement = "'''$0'''";
+ }
+
+ // Match all tokens except those within [ ... ] to avoid breaking links
+ // and annotations
+ $pattern = '/(' . implode( '|', $tokens ) . ')+(?![^\[]*\])/iu';
+
+ return preg_replace( $pattern, $replacement, $text );
+ }
+
+ private function addTokensFromText( $text ) {
+
+ // Remove query related chars
+ $text = str_replace(
+ [ '*', '"', '~', '_', '+', '-' ],
+ [ '', '', '', ' ', '', '' ],
+ $text
+ );
+
+ return $this->tokens += array_flip( Tokenizer::tokenize( $text ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/RemoteRequest.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/RemoteRequest.php
new file mode 100644
index 00000000..325d0c7b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/RemoteRequest.php
@@ -0,0 +1,336 @@
+<?php
+
+namespace SMW\Query;
+
+use Html;
+use Onoi\HttpRequest\CachedCurlRequest;
+use Onoi\HttpRequest\CurlRequest;
+use Onoi\HttpRequest\HttpRequest;
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\Message;
+use SMW\Query\Result\StringResult;
+use SMW\QueryEngine;
+use SMW\Site;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RemoteRequest implements QueryEngine {
+
+ /**
+ * Send by a remote source when remote access has been disabled.
+ */
+ const SOURCE_DISABLED = "\x7fsmw-remote-response-disabled\x7f";
+
+ /**
+ * Identifies a source to support a remote request.
+ */
+ const REQUEST_ID = "\x7fsmw-remote-request\x7f";
+
+ /**
+ * @var []
+ */
+ private $parameters = [];
+
+ /**
+ * @var HttpRequest
+ */
+ private $httpRequest;
+
+ /**
+ * @var []
+ */
+ private $features = [];
+
+ /**
+ * @var []
+ */
+ private static $isConnected;
+
+ /**
+ * @since 3.0
+ *
+ * @param array $parameters
+ * @param HttpRequest|null $httpRequest
+ */
+ public function __construct( array $parameters = [], HttpRequest $httpRequest = null ) {
+ $this->parameters = $parameters;
+ $this->httpRequest = $httpRequest;
+ $this->features = $GLOBALS['smwgRemoteReqFeatures'];
+
+ if ( isset( $this->parameters['smwgRemoteReqFeatures'] ) ) {
+ $this->features = $this->parameters['smwgRemoteReqFeatures'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function clear() {
+ self::$isConnected = null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $flag
+ *
+ * @return boolean
+ */
+ public function hasFeature( $flag ) {
+ return ( ( (int)$this->features & $flag ) == $flag );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Query $query
+ *
+ * @return StringResult|string
+ */
+ public function getQueryResult( Query $query ) {
+
+ if ( $query->isEmbedded() && $query->getLimit() == 0 ) {
+ return $this->further_link( $query );
+ }
+
+ if ( !isset( $this->parameters['url'] ) ) {
+ throw new RuntimeException( "Missing a remote URL for $source" );
+ }
+
+ $source = $query->getQuerySource();
+ $this->init();
+
+ if ( !$this->canConnect( $this->parameters['url'] ) ) {
+ return $this->error( 'smw-remote-source-unavailable', $this->parameters['url'] );
+ }
+
+ $result = $this->fetch( $query );
+
+ $isFromCache = false;
+ $isDisabled = false;
+
+ if ( $this->httpRequest instanceof CachedCurlRequest ) {
+ $isFromCache = $this->httpRequest->isFromCache();
+ }
+
+ if ( $result === self::SOURCE_DISABLED ) {
+ $result = $this->error( 'smw-remote-source-disabled', $source );
+ $isDisabled = true;
+ }
+
+ // Find out whether the source has send an ID and hereby produces an output
+ // that can be used by the `RemoteRequest`
+ if ( strpos( $result, self::REQUEST_ID ) === false ) {
+ $result = $this->error( 'smw-remote-source-unmatched-id', $source );
+ $isDisabled = true;
+ } else {
+ $result = str_replace( self::REQUEST_ID, '', $result );
+ }
+
+ // Add an information note depending on the context before the actual output
+ $callback = function( $result, array $options ) use( $isFromCache, $isDisabled, $source ) {
+
+ $options['source'] = $source;
+ $options['is.cached'] = $isFromCache;
+ $options['is.disabled'] = $isDisabled;
+
+ return $this->format_result( $result, $options );
+ };
+
+ $stringResult = new StringResult( $result, $query );
+ $stringResult->setPreOutputCallback( $callback );
+ $stringResult->setFromCache( $isFromCache );
+
+ if ( $query->getQueryMode() === Query::MODE_COUNT ) {
+ $stringResult->setCountValue( $result );
+ }
+
+ return $stringResult;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $result
+ * @param array $options
+ *
+ * @return string
+ */
+ public function format_result( $result, array $options ) {
+
+ // No changes to any export related output
+ if ( ( isset( $options['is.disabled'] ) && $options['is.disabled'] ) || !$this->hasFeature( SMW_REMOTE_REQ_SHOW_NOTE ) ) {
+ return $result;
+ }
+
+ if ( ( isset( $options['is.exportformat'] ) && $options['is.exportformat'] ) ) {
+ return $result;
+ }
+
+ $msg = $options['is.cached'] ? 'smw-remote-request-note-cached' : 'smw-remote-request-note';
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-note smw-remote-query',
+ 'style' => 'margin-top:12px;'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-icon-info',
+ 'style' => 'margin-left: -5px; padding: 10px 12px 12px 12px;'
+ ]
+ ) . Message::get( [ $msg, $options['source'] ], Message::PARSE, Message::USER_LANGUAGE )
+ ) . $result;
+ }
+
+ private function further_link( $query ) {
+
+ $link = QueryLinker::get( $query );
+
+ // Find remaining parameters, format, template etc.
+ $extraParameters = $query->getOption( 'query.params' );
+
+ foreach ( $extraParameters as $key => $value ) {
+
+ if ( $key === 'limit' || $value === '' ) {
+ continue;
+ }
+
+ if ( is_array( $value ) ) {
+ $value = implode( ',', $value );
+ }
+
+ $link->setParameter( $value, $key );
+ }
+
+ return $link->getText( SMW_OUTPUT_WIKI );
+ }
+
+ private function init() {
+
+ if ( $this->httpRequest === null && isset( $this->parameters['cache'] ) ) {
+ $this->httpRequest = new CachedCurlRequest(
+ curl_init(),
+ ApplicationFactory::getInstance()->getCache()
+ );
+
+ $this->httpRequest->setOption(
+ ONOI_HTTP_REQUEST_RESPONSECACHE_TTL,
+ $this->parameters['cache']
+ );
+
+ $this->httpRequest->setOption(
+ ONOI_HTTP_REQUEST_RESPONSECACHE_PREFIX,
+ Site::id( 'smw:query:remote:' )
+ );
+ }
+
+ if ( $this->httpRequest === null ) {
+ $this->httpRequest = new CurlRequest( curl_init() );
+ }
+ }
+
+ private function error() {
+ $params = func_get_args();
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'id' => $params[0],
+ 'class' => 'smw-callout smw-callout-error'
+ ],
+ Message::get( $params, Message::PARSE, Message::USER_LANGUAGE )
+ );
+ }
+
+ private function canConnect( $url ) {
+
+ $this->httpRequest->setOption( CURLOPT_URL, $url );
+
+ if ( self::$isConnected === null ) {
+ self::$isConnected = $this->httpRequest->ping();
+ }
+
+ return self::$isConnected;
+ }
+
+ private function fetch( $query ) {
+
+ $parameters = $query->toArray();
+ $default = '';
+ $params = [ 'title' => 'Special:Ask', 'q' => '', 'po' => '', 'p' => [] ];
+
+ if ( isset( $parameters['conditions'] ) ) {
+ $params['q'] = $parameters['conditions'];
+ }
+
+ if ( isset( $parameters['printouts'] ) ) {
+ $params['po'] = implode( '|', $parameters['printouts'] );
+ }
+
+ if ( !isset( $parameters['parameters'] ) ) {
+ $parameters['parameters'] = [];
+ }
+
+ // Find remaining parameters, format, template etc.
+ $extraParameters = $query->getOption( 'query.params' );
+
+ if ( is_array( $extraParameters ) ) {
+ $parameters['parameters'] = array_merge( $parameters['parameters'], $extraParameters );
+ }
+
+ foreach ( $parameters['parameters'] as $key => $value ) {
+
+ if ( $key === 'default' ) {
+ $default = $value;
+ }
+
+ if ( $value === '' ) {
+ continue;
+ }
+
+ if ( is_array( $value ) ) {
+ $value = implode( ',', $value );
+ }
+
+ $params['p'][] = "$key=$value";
+ }
+
+ $params['request_type'] = $query->isEmbedded() ? 'embed' : 'special_page';
+ $output = '';
+
+ $options = [
+ CURLOPT_SSL_VERIFYPEER => false,
+ CURLOPT_POST => true,
+ CURLOPT_POSTFIELDS => http_build_query( $params ),
+ CURLOPT_RETURNTRANSFER => 1
+ ];
+
+ foreach ( $options as $key => $value ) {
+ $this->httpRequest->setOption( $key, $value );
+ }
+
+ $output = $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastError() !== '' ) {
+ $output = $this->httpRequest->getLastError();
+ }
+
+ // The remote Special:Ask doesn't return a default output hence it is done
+ // at this point
+ if ( $output === '' ) {
+ $output = $default;
+ }
+
+ return $output;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/CachedQueryResultPrefetcher.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/CachedQueryResultPrefetcher.php
new file mode 100644
index 00000000..673c5aeb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/CachedQueryResultPrefetcher.php
@@ -0,0 +1,552 @@
+<?php
+
+namespace SMW\Query\Result;
+
+use Onoi\BlobStore\BlobStore;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\QueryEngine;
+use SMW\QueryFactory;
+use SMW\Store;
+use SMW\Utils\BufferedStatsdCollector;
+use SMW\Utils\Timer;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * The prefetcher only caches the subject list from a computed a query
+ * condition. The result is processed before an individual query printer has
+ * access to the query result hence it does not interfere with the final string
+ * output manipulation.
+ *
+ * The main objective is to avoid unnecessary computing of results for queries
+ * that have the same query signature. PrintRequests as part of a QueryResult
+ * object are not cached and are not part of a query signature.
+ *
+ * Cache eviction is carried out either manually (action=purge) or executed
+ * through the QueryDepedencyLinksStore.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class CachedQueryResultPrefetcher implements QueryEngine, LoggerAwareInterface {
+
+ /**
+ * Update this version number when the serialization format
+ * changes.
+ */
+ const VERSION = '1';
+
+ /**
+ * Namespace occupied by the BlobStore
+ */
+ const CACHE_NAMESPACE = 'smw:query:store';
+
+ /**
+ * ID used by the bufferedStatsdCollector, requires to be changed in case
+ * the data schema is modified
+ *
+ * PHP 5.6 can do self::CACHE_NAMESPACE . ':' . self::VERSION
+ */
+ const STATSD_ID = 'smw:query:store:1:d:';
+
+ /**
+ * ID for the tempCache
+ */
+ const POOLCACHE_ID = 'queryresult.prefetcher';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var QueryFactory
+ */
+ private $queryFactory;
+
+ /**
+ * @var BlobStore
+ */
+ private $blobStore;
+
+ /**
+ * @var QueryEngine
+ */
+ private $queryEngine;
+
+ /**
+ * @var BufferedStatsdCollector
+ */
+ private $bufferedStatsdCollector;
+
+ /**
+ * @var integer|boolean
+ */
+ private $nonEmbeddedCacheLifetime = false;
+
+ /**
+ * @var boolean
+ */
+ private $enabledCache = true;
+
+ /**
+ * @var loggerInterface
+ */
+ private $logger;
+
+ /**
+ * Keep a temp cache to hold on query results that aren't stored yet.
+ *
+ * If for example the retrieval is executed in deferred mode then a request
+ * may occur in the same transaction cycle without being stored to the actual
+ * back-end, yet queries with the same signature may have been retrieved
+ * already therefore allow to recall the result from tempCache.
+ *
+ * @var InMemoryCache
+ */
+ private $tempCache;
+
+ /**
+ * An internal change to the query execution may occur without being detected
+ * by the Description hash (which is the desired behaviour) and to avoid a
+ * stalled cache on an altered execution plan, use this modifier to generate
+ * a new hash.
+ *
+ * @var string/integer
+ */
+ private $dependantHashIdExtension = '';
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param QueryFactory $queryFactory
+ * @param BlobStore $blobStore
+ * @param BufferedStatsdCollector $bufferedStatsdCollector
+ */
+ public function __construct( Store $store, QueryFactory $queryFactory, BlobStore $blobStore, BufferedStatsdCollector $bufferedStatsdCollector ) {
+ $this->store = $store;
+ $this->queryFactory = $queryFactory;
+ $this->blobStore = $blobStore;
+ $this->bufferedStatsdCollector = $bufferedStatsdCollector;
+ $this->tempCache = ApplicationFactory::getInstance()->getInMemoryPoolCache()->getPoolCacheById( self::POOLCACHE_ID );
+
+ $this->initStats( date( 'Y-m-d H:i:s' ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getStats() {
+
+ $stats = array_filter( $this->bufferedStatsdCollector->getStats(), function( $key ) {
+ return $key !== false;
+ } );
+
+ if ( !isset( $stats['misses'] ) || ! isset( $stats['hits'] ) ) {
+ return $stats;
+ }
+
+ $misses = $this->sum( 0, $stats['misses'] );
+ $hits = $this->sum( 0, $stats['hits'] );
+
+ $stats['ratio'] = [];
+ $stats['ratio']['hit'] = $hits > 0 ? round( $hits / ( $hits + $misses ), 4 ) : 0;
+ $stats['ratio']['miss'] = $hits > 0 ? round( 1 - $stats['ratio']['hit'], 4 ) : 1;
+
+ // Move to last
+ $meta = $stats['meta'];
+ unset( $stats['meta'] );
+ $stats['meta'] = $meta;
+
+ return $stats;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $dependantHashIdExtension
+ */
+ public function setDependantHashIdExtension( $dependantHashIdExtension ) {
+ $this->dependantHashIdExtension = $dependantHashIdExtension;
+ }
+
+ /**
+ * @see LoggerAwareInterface::setLogger
+ *
+ * @since 2.5
+ *
+ * @param LoggerInterface $logger
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryEngine $queryEngine
+ */
+ public function setQueryEngine( QueryEngine $queryEngine ) {
+ $this->queryEngine = $queryEngine;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean
+ */
+ public function isEnabled() {
+ return $this->blobStore->canUse();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryEngine $queryEngine
+ */
+ public function disableCache() {
+ $this->enabledCache = false;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function recordStats() {
+ $this->bufferedStatsdCollector->recordStats();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|boolean $nonEmbeddedCacheLifetime
+ */
+ public function setNonEmbeddedCacheLifetime( $nonEmbeddedCacheLifetime ) {
+ $this->nonEmbeddedCacheLifetime = $nonEmbeddedCacheLifetime;
+ }
+
+ /**
+ * @see QueryEngine::getQueryResult
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getQueryResult( Query $query ) {
+
+ if ( !$this->queryEngine instanceof QueryEngine ) {
+ throw new RuntimeException( "Missing a QueryEngine instance." );
+ }
+
+ if ( !$this->canUse( $query ) || $query->getLimit() < 1 || $query->getOption( Query::NO_CACHE ) === true ) {
+ $this->bufferedStatsdCollector->incr( $this->getNoCacheId( $query ) );
+ return $this->queryEngine->getQueryResult( $query );
+ }
+
+ Timer::start( __CLASS__ );
+
+ $queryId = $this->getHashFrom( $query->getQueryId() );
+
+ $container = $this->blobStore->read(
+ $queryId
+ );
+
+ if ( $this->tempCache->contains( $queryId ) || $container->has( 'results' ) ) {
+ return $this->newQueryResultFromCache( $queryId, $query, $container );
+ }
+
+ $queryResult = $this->queryEngine->getQueryResult( $query );
+
+ $this->tempCache->save(
+ $queryId,
+ $queryResult
+ );
+
+ $this->log(
+ __METHOD__ . ' from backend in (sec): ' . Timer::getElapsedTime( __CLASS__, 5 ) . " ($queryId)"
+ );
+
+ if ( $this->canUse( $query ) && $queryResult instanceof QueryResult ) {
+ $this->addQueryResultToCache( $queryResult, $queryId, $container, $query );
+ }
+
+ return $queryResult;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage|array $items
+ * @param string $context
+ */
+ public function resetCacheBy( $items, $context = '' ) {
+
+ if ( !$this->blobStore->canUse() ) {
+ return;
+ }
+
+ if ( !is_array( $items ) ) {
+ $items = [ $items ];
+ }
+
+ $recordStats = false;
+ $context = $context === '' ? 'Undefined' : $context;
+
+ if ( is_array( $context ) ) {
+ $context = implode( '.', $context );
+ }
+
+ foreach ( $items as $item ) {
+ $id = $this->getHashFrom( $item );
+ $this->tempCache->delete( $id );
+
+ if ( $this->blobStore->exists( $id ) ) {
+ $recordStats = true;
+ $this->bufferedStatsdCollector->incr( 'deletes.on' . $context );
+ $this->blobStore->delete( $id );
+ }
+ }
+
+ if ( $recordStats ) {
+ $this->bufferedStatsdCollector->recordStats();
+ }
+ }
+
+ private function canUse( $query ) {
+ return $this->enabledCache && $this->blobStore->canUse() && ( $query->getContextPage() !== null || ( $query->getContextPage() === null && $this->nonEmbeddedCacheLifetime > 0 ) );
+ }
+
+ private function newQueryResultFromCache( $queryId, $query, $container ) {
+
+ $results = [];
+ $incrStats = 'hits.Undefined';
+ $resolverJournal = null;
+
+ if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) === false ) {
+ $context = 'Undefined';
+ }
+
+ // Check if the tempCache is available for result that have not yet been
+ // stored to the cache back-end
+ if ( ( $queryResult = $this->tempCache->fetch( $queryId ) ) !== false ) {
+ $this->log( __METHOD__ . ' using tempCache ' . "($queryId)" );
+
+ if ( !$queryResult instanceof QueryResult ) {
+ return $queryResult;
+ }
+
+ $incrStats = 'hits.tempCache.' . ( $query->getContextPage() !== null ? 'embedded' : 'nonEmbedded' );
+
+ $queryResult->reset();
+ $results = $queryResult->getResults();
+
+ $hasFurtherResults = $queryResult->hasFurtherResults();
+ $countValue = $queryResult->getCountValue();
+ $resolverJournal = $queryResult->getResolverJournal();
+ } else {
+
+ $incrStats = ( $query->getContextPage() !== null ? 'hits.embedded.' : 'hits.nonEmbedded.' ) . $context;
+
+ foreach ( $container->get( 'results' ) as $hash ) {
+ $results[] = DIWikiPage::doUnserialize( $hash );
+ }
+
+ $hasFurtherResults = $container->get( 'continue' );
+ $countValue = $container->get( 'count' );
+ }
+
+ $queryResult = $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ $results,
+ $hasFurtherResults
+ );
+
+ $queryResult->setCountValue( $countValue );
+ $queryResult->setFromCache( true );
+
+ if ( $resolverJournal !== null ) {
+ $queryResult->setResolverJournal( $resolverJournal );
+ }
+
+ $time = Timer::getElapsedTime( __CLASS__, 5 );
+
+ $this->bufferedStatsdCollector->incr( $incrStats );
+
+ $this->bufferedStatsdCollector->calcMedian(
+ 'medianRetrievalResponseTime.cached',
+ $time
+ );
+
+ $this->log( __METHOD__ . ' (sec): ' . $time . " ($queryId)" );
+
+ return $queryResult;
+ }
+
+ private function addQueryResultToCache( $queryResult, $queryId, $container, $query ) {
+
+ if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) === false ) {
+ $context = 'Undefined';
+ }
+
+ $this->bufferedStatsdCollector->incr(
+ ( $query->getContextPage() !== null ? 'misses.embedded.' : 'misses.nonEmbedded.' ) . $context
+ );
+
+ $this->bufferedStatsdCollector->calcMedian(
+ 'medianRetrievalResponseTime.uncached',
+ Timer::getElapsedTime( __CLASS__, 5 )
+ );
+
+ $callback = function() use( $queryResult, $queryId, $container, $query ) {
+ $this->doCacheQueryResult( $queryResult, $queryId, $container, $query );
+ };
+
+ $deferredTransactionalUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate(
+ $callback
+ );
+
+ $deferredTransactionalUpdate->setOrigin( __METHOD__ );
+ $deferredTransactionalUpdate->setFingerprint( __METHOD__ . $queryId );
+ $deferredTransactionalUpdate->waitOnTransactionIdle();
+
+ // Make sure that in any event the collector is executed after
+ // the process has finished
+ $deferredTransactionalUpdate->addPostCommitableCallback(
+ BufferedStatsdCollector::class,
+ [ $this, 'recordStats' ]
+ );
+
+ $deferredTransactionalUpdate->pushUpdate();
+ }
+
+ private function doCacheQueryResult( $queryResult, $queryId, $container, $query ) {
+
+ $results = [];
+
+ // Keep the simple string representation to avoid unnecessary data cruft
+ // during using PHP serialize( ... )
+ foreach ( $queryResult->getResults() as $dataItem ) {
+ $results[] = $dataItem->getSerialization();
+ }
+
+ $container->set( 'results', $results );
+ $container->set( 'continue', $queryResult->hasFurtherResults() );
+ $container->set( 'count', $queryResult->getCountValue() );
+
+ $queryResult->reset();
+ $contextPage = $query->getContextPage();
+
+ if ( $contextPage === null ) {
+ $container->setExpiryInSeconds( $this->nonEmbeddedCacheLifetime );
+ $hash = 'nonEmbedded';
+ } else {
+ $this->addToLinkedList( $contextPage, $queryId );
+ $hash = $contextPage->getHash();
+ }
+
+ $this->blobStore->save(
+ $container
+ );
+
+ $this->tempCache->delete( $queryId );
+
+ $this->log(
+ __METHOD__ . ' cache storage (sec): ' . Timer::getElapsedTime( __CLASS__, 5 ) . " ($queryId)"
+ );
+
+ return $queryResult;
+ }
+
+ private function addToLinkedList( $contextPage, $queryId ) {
+
+ // Ensure that without QueryDependencyLinksStore being enabled recorded
+ // subjects related to a query can be discoverable and purged separately
+ $container = $this->blobStore->read(
+ $this->getHashFrom( $contextPage )
+ );
+
+ // If a subject gets purged then the linked list of queries associated
+ // with that subject allows for an immediate associated removal
+ $container->addToLinkedList( $queryId );
+
+ $this->blobStore->save(
+ $container
+ );
+ }
+
+ private function getHashFrom( $subject ) {
+
+ if ( $subject instanceof DIWikiPage ) {
+ // In case the we detect a _QUERY subobject, use it directly
+ if ( ( $subobjectName = $subject->getSubobjectName() ) !== '' && strpos( $subobjectName, Query::ID_PREFIX ) !== false ) {
+ $subject = $subobjectName;
+ } else {
+ $subject = $subject->asBase()->getHash();
+ }
+ }
+
+ return md5( $subject . self::VERSION . $this->dependantHashIdExtension );
+ }
+
+ private function log( $message, $context = [] ) {
+
+ if ( $this->logger === null ) {
+ return;
+ }
+
+ $this->logger->info( $message, $context );
+ }
+
+ private function getNoCacheId( $query ) {
+
+ $id = 'noCache.misc';
+
+ if ( !$this->canUse( $query ) ) {
+ $id = 'noCache.disabled';
+ }
+
+ if ( $query->getLimit() < 1 ) {
+ $id = 'noCache.byLimit';
+ }
+
+ if ( $query->getOption( Query::NO_CACHE ) === true ) {
+ $id = 'noCache.byOption';
+ }
+
+ if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) !== false ) {
+ $id .= '.' . $context;
+ }
+
+ return $id;
+ }
+
+ private function initStats( $date ) {
+
+ $this->bufferedStatsdCollector->shouldRecord( $this->isEnabled() );
+
+ $this->bufferedStatsdCollector->init( 'misses', [] );
+ $this->bufferedStatsdCollector->init( 'hits', [] );
+ $this->bufferedStatsdCollector->init( 'deletes', [] );
+ $this->bufferedStatsdCollector->init( 'noCache', [] );
+ $this->bufferedStatsdCollector->init( 'medianRetrievalResponseTime', [] );
+ $this->bufferedStatsdCollector->set( 'meta.version', self::VERSION );
+ $this->bufferedStatsdCollector->set( 'meta.cacheLifetime.embedded', $GLOBALS['smwgQueryResultCacheLifetime'] );
+ $this->bufferedStatsdCollector->set( 'meta.cacheLifetime.nonEmbedded', $GLOBALS['smwgQueryResultNonEmbeddedCacheLifetime'] );
+ $this->bufferedStatsdCollector->init( 'meta.collectionDate.start', $date );
+ $this->bufferedStatsdCollector->set( 'meta.collectionDate.update', $date );
+ }
+
+ // http://stackoverflow.com/questions/3777995/php-array-recursive-sum
+ private static function sum( $value, $container ) {
+ return $value + ( is_array( $container ) ? array_reduce( $container, 'self::sum' ) : $container );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResolverJournal.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResolverJournal.php
new file mode 100644
index 00000000..d306a353
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResolverJournal.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Query\Result;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMWDataItem as DataItem;
+
+/**
+ * This class records selected entities used in a QueryResult by the time the
+ * ResultArray creates an object instance which avoids unnecessary work in the
+ * QueryResultDependencyListResolver (in terms of recursive processing of the
+ * QueryResult) to find related "column" entities (those related to a
+ * printrequest).
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ResolverJournal {
+
+ /**
+ * @var array
+ */
+ private $dataItems = [];
+
+ /**
+ * @var array
+ */
+ private $properties = [];
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getEntityList() {
+ return $this->dataItems;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getPropertyList() {
+ return $this->properties;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function prune() {
+ $this->dataItems = [];
+ $this->properties = [];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DataItem $dataItem
+ */
+ public function recordItem( DataItem $dataItem ) {
+ if ( $dataItem instanceof DIWikiPage ) {
+ $this->dataItems[$dataItem->getHash()] = $dataItem;
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty|null $property
+ */
+ public function recordProperty( DIProperty $property = null ) {
+ if ( $property !== null ) {
+ $this->properties[$property->getKey()] = $property;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php
new file mode 100644
index 00000000..46e87508
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/ResultFieldMatchFinder.php
@@ -0,0 +1,357 @@
+<?php
+
+namespace SMW\Query\Result;
+
+use SMW\DataValueFactory;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Parser\InTextAnnotationParser;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryToken;
+use SMW\RequestOptions;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+
+/**
+ * Returns the result content (DI objects) for a single PrintRequest, representing
+ * as cell of the intersection between a subject row and a print column.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class ResultFieldMatchFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PrintRequest
+ */
+ private $printRequest;
+
+ /**
+ * @var QueryToken
+ */
+ private $queryToken;
+
+ /**
+ * @var boolean|array
+ */
+ private static $catCacheObj = false;
+
+ /**
+ * @var boolean|array
+ */
+ private static $catCache = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param PrintRequest $printRequest
+ */
+ public function __construct( Store $store, PrintRequest $printRequest ) {
+ $this->printRequest = $printRequest;
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryToken|null $queryToken
+ */
+ public function setQueryToken( QueryToken $queryToken = null ) {
+
+ if ( $queryToken === null ) {
+ return;
+ }
+
+ $this->queryToken = $queryToken;
+
+ $this->queryToken->setOutputFormat(
+ $this->printRequest->getOutputFormat()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataItem $dataItem
+ *
+ * @param DataItem[]|[]
+ */
+ public function findAndMatch( DataItem $dataItem ) {
+
+ $content = [];
+
+ // Request the current element (page in result set).
+ // The limit is ignored here.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_THIS ) ) {
+ return [ $dataItem ];
+ }
+
+ // Request all direct categories of the current element
+ // Always recompute cache here to ensure output format is respected.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_CATS ) ) {
+ self::$catCache = $this->store->getPropertyValues(
+ $dataItem,
+ new DIProperty( '_INST' ),
+ $this->getRequestOptions( false )
+ );
+
+ self::$catCacheObj = $dataItem->getHash();
+
+ $limit = $this->printRequest->getParameter( 'limit' );
+
+ return ( $limit === false ) ? ( self::$catCache ) : array_slice( self::$catCache, 0, $limit );
+ }
+
+ // Request to whether current element is in given category (Boolean printout).
+ // The limit is ignored here.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_CCAT ) ) {
+ if ( self::$catCacheObj !== $dataItem->getHash() ) {
+ self::$catCache = $this->store->getPropertyValues(
+ $dataItem,
+ new DIProperty( '_INST' )
+ );
+ self::$catCacheObj = $dataItem->getHash();
+ }
+
+ $found = false;
+ $prkey = $this->printRequest->getData()->getDBkey();
+
+ foreach ( self::$catCache as $cat ) {
+ if ( $cat->getDBkey() == $prkey ) {
+ $found = true;
+ break;
+ }
+ }
+
+ return [ new DIBoolean( $found ) ];
+ }
+
+ // Request all property values of a certain attribute of the current element.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_PROP ) || $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ return $this->getResultsForProperty( $dataItem );
+ }
+
+ return $content;
+ }
+
+ /**
+ * Make a request option object based on the given parameters, and
+ * return NULL if no such object is required. The parameter defines
+ * if the limit should be taken into account, which is not always desired
+ * (especially if results are to be cached for future use).
+ *
+ * @param boolean $useLimit
+ *
+ * @return RequestOptions|null
+ */
+ public function getRequestOptions( $useLimit = true ) {
+ $limit = $useLimit ? $this->printRequest->getParameter( 'limit' ) : false;
+ $order = trim( $this->printRequest->getParameter( 'order' ) );
+ $options = null;
+
+ // Important: use "!=" for order, since trim() above does never return "false", use "!==" for limit since "0" is meaningful here.
+ if ( ( $limit !== false ) || ( $order != false ) ) {
+ $options = new RequestOptions();
+
+ if ( $limit !== false ) {
+ $options->limit = trim( $limit );
+ }
+
+ // Expecting a natural sort behaviour (n-asc, n-desc)?
+ if ( strpos( $order, 'n-' ) !== false ) {
+ $order = str_replace( 'n-', '', $order );
+ $options->natural = true;
+ }
+
+ if ( ( $order == 'descending' ) || ( $order == 'reverse' ) || ( $order == 'desc' ) ) {
+ $options->sort = true;
+ $options->ascending = false;
+ } elseif ( ( $order == 'ascending' ) || ( $order == 'asc' ) ) {
+ $options->sort = true;
+ $options->ascending = true;
+ }
+ }
+
+ return $options;
+ }
+
+ private function getResultsForProperty( $dataItem ) {
+
+ $content = $this->getResultContent(
+ $dataItem
+ );
+
+ if ( !$this->isMultiValueWithParameter( 'index' ) && !$this->isMultiValueWithParameter( 'lang' ) ) {
+ return $content;
+ }
+
+ // Print one component of a multi-valued string.
+ //
+ // Known limitation: the printrequest still is of type _rec, so if
+ // printers check for this then they will not recognize that it returns
+ // some more concrete type.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ $propertyValue = $this->printRequest->getData()->getLastPropertyChainValue();
+ } else {
+ $propertyValue = $this->printRequest->getData();
+ }
+
+ $index = $this->printRequest->getParameter( 'index' );
+ $lang = $this->printRequest->getParameter( 'lang' );
+ $newcontent = [];
+
+ // Replace content with specific content from a Container/MultiValue
+ foreach ( $content as $diContainer ) {
+
+ /* AbstractMultiValue */
+ $multiValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $diContainer,
+ $propertyValue->getDataItem()
+ );
+
+ $multiValue->setOption( $multiValue::OPT_QUERY_CONTEXT, true );
+
+ if ( $multiValue instanceof MonolingualTextValue && $lang !== false && ( $textValue = $multiValue->getTextValueByLanguage( $lang ) ) !== null ) {
+
+ // Return the text representation without a language reference
+ // (tag) since the value has been filtered hence only matches
+ // that language
+ $newcontent[] = $this->applyContentManipulation( $textValue->getDataItem() );
+
+ // Set the index so ResultArray::getNextDataValue can
+ // find the correct PropertyDataItem (_TEXT;_LCODE) position
+ // to match the DI
+ $this->printRequest->setParameter( 'index', 1 );
+ } elseif ( $lang === false && $index !== false && ( $dataItemByRecord = $multiValue->getDataItemByIndex( $index ) ) !== null ) {
+ $newcontent[] = $this->applyContentManipulation( $dataItemByRecord );
+ }
+ }
+
+ $content = $newcontent;
+ unset( $newcontent );
+
+ return $content;
+ }
+
+ private function isMultiValueWithParameter( $parameter ) {
+ return strpos( $this->printRequest->getTypeID(), '_rec' ) !== false && $this->printRequest->getParameter( $parameter ) !== false;
+ }
+
+ private function getResultContent( DataItem $dataItem ) {
+
+ $dataValue = $this->printRequest->getData();
+ $dataItems = [ $dataItem ];
+
+ if ( !$dataValue->isValid() ) {
+ return [];
+ }
+
+ // If it is a chain then try to find a connected DIWikiPage subject that
+ // matches the property on the chained PrintRequest.
+ // For example, Number.Date.SomeThing will not return any meaningful results
+ // because Number will return a DINumber object and not a DIWikiPage.
+ // If on the other hand Has page.Number (with Number being the Last and
+ // `Has page` is of type Page) then the iteration will lookup on results
+ // for `Has page` and try to match a Number annotation on the results
+ // retrieved from `Has page`.
+ if ( $this->printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+
+ // Output of the previous iteration is the input for the next iteration
+ foreach ( $dataValue->getPropertyChainValues() as $pv ) {
+ $dataItems = $this->doFetchPropertyValues( $dataItems, $pv );
+
+ // If the results return empty then it means that for this element
+ // the chain has no matchable items hence we stop
+ if ( $dataItems === [] ) {
+ return [];
+ }
+ }
+
+ $dataValue = $dataValue->getLastPropertyChainValue();
+ }
+
+ return $this->doFetchPropertyValues( $dataItems, $dataValue );
+ }
+
+ private function doFetchPropertyValues( $dataItems, $dataValue ) {
+
+ $propertyValues = [];
+
+ foreach ( $dataItems as $dataItem ) {
+
+ if ( !$dataItem instanceof DIWikiPage ) {
+ continue;
+ }
+
+ $pv = $this->store->getPropertyValues(
+ $dataItem,
+ $dataValue->getDataItem(),
+ $this->getRequestOptions()
+ );
+
+ if ( $pv instanceof \Iterator ) {
+ $pv = iterator_to_array( $pv );
+ }
+
+ $propertyValues = array_merge( $propertyValues, $pv );
+ unset( $pv );
+ }
+
+ array_walk( $propertyValues, function( &$dataItem ) {
+ $dataItem = $this->applyContentManipulation( $dataItem );
+ } );
+
+ return $propertyValues;
+ }
+
+ private function applyContentManipulation( $dataItem ) {
+
+ if ( !$dataItem instanceof DIBlob ) {
+ return $dataItem;
+ }
+
+ $type = $this->printRequest->getTypeID();
+
+ // Avoid `_cod`, `_eid` or similar types that use the DIBlob as storage
+ // object
+ if ( $type !== '_txt' && strpos( $type, '_rec' ) === false ) {
+ return $dataItem;
+ }
+
+ $outputFormat = $this->printRequest->getOutputFormat();
+
+ // #2325
+ // Output format marked with -raw are allowed to retain a possible [[ :: ]]
+ // annotation
+ // '-ia' is deprecated use `-raw`
+ if ( strpos( $outputFormat, '-raw' ) !== false || strpos( $outputFormat, '-ia' ) !== false ) {
+ return $dataItem;
+ }
+
+ // #1314
+ $string = InTextAnnotationParser::removeAnnotation(
+ $dataItem->getString()
+ );
+
+ // #2253
+ if ( $this->queryToken !== null ) {
+ $string = $this->queryToken->highlight( $string );
+ }
+
+ return new DIBlob( $string );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/StringResult.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/StringResult.php
new file mode 100644
index 00000000..69bd055a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/Result/StringResult.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Query\Result;
+
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class StringResult extends QueryResult {
+
+ /**
+ * @var array
+ */
+ private $result = '';
+
+ /**
+ * @var Query
+ */
+ private $query;
+
+ /**
+ * @var callable
+ */
+ private $preOutputCallback;
+
+ /**
+ * @var array
+ */
+ private $options = [
+ 'noparse' => true,
+ 'isHTML' => true
+ ];
+
+ /**
+ * @since 3.0
+ *
+ * @param string $result
+ */
+ public function __construct( $result = '', Query $query ) {
+ $this->result = $result;
+ $this->query = $query;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * Manipulate or transform the result before the actual output.
+ *
+ * @since 3.0
+ *
+ * @param callable $preOutputCallback
+ */
+ public function setPreOutputCallback( callable $preOutputCallback ) {
+ $this->preOutputCallback = $preOutputCallback;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getResults() {
+
+ $result = $this->result;
+
+ if ( is_callable( $this->preOutputCallback ) ) {
+ $result = call_user_func_array( $this->preOutputCallback, [ $result, $this->options ] );
+ }
+
+ // Inline representation requires a different handling for results already
+ // being parsed by for example a remote request.
+ if ( $this->query->isEmbedded() ) {
+ return [ $result, 'noparse' => $this->options['noparse'], 'isHTML' => $this->options['isHTML'] ];
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinter.php
new file mode 100644
index 00000000..c7d06ef4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinter.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Query;
+
+use SMWQueryResult as QueryResult;
+
+/**
+ * Interface for SMW result printers.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Markus Krötzsch
+ */
+interface ResultPrinter {
+
+ // Constructor restriction:
+ // Needs to have exactly one required argument $formatName.
+ // Is allowed to have additional optional arguments.
+
+ /**
+ * Main entry point: takes an QueryResult and parameters given as key-value-pairs in an array,
+ * and returns the serialised version of the results, formatted as HTML or Wiki or whatever is
+ * specified. Normally this is not overwritten by subclasses.
+ *
+ * If the outputmode is SMW_OUTPUT_WIKI, then the function will return something that is suitable
+ * for being used in a MediaWiki parser function, i.e. a wikitext strong *or* an array with flags
+ * and the string as entry 0. See Parser::setFunctionHook() for documentation on this. In all other
+ * cases, the function returns just a string.
+ *
+ * For outputs SMW_OUTPUT_WIKI and SMW_OUTPUT_HTML, error messages or standard "further results" links
+ * are directly generated and appended. For SMW_OUTPUT_FILE, only the plain generated text is returned.
+ *
+ * @note A note on recursion: some query printers may return wiki code that comes from other pages,
+ * e.g. from templates that are used in formatting or from embedded result pages. Both kinds of pages
+ * may contain \#ask queries that do again use new pages, so we must care about recursion. We do so
+ * by simply counting how often this method starts a subparse and stopping at depth 2. There is one
+ * special case: if this method is called outside parsing, and the concrete printer returns wiki text,
+ * and wiki text is requested, then we may return wiki text with sub-queries to the caller. If the
+ * caller parses this (which is likely) then this will again call us in parse-context and all recursion
+ * checks catch. Only the first level of parsing is done outside and thus not counted. Thus you
+ * effectively can get down to level 3. The basic maximal depth of 2 can be changed by setting the
+ * variable SMWResultPrinter::$maxRecursionDepth (in LocalSettings.php, after enableSemantics()).
+ * Do this at your own risk.
+ *
+ * @param $results QueryResult
+ * @param $fullParams array
+ * @param $outputMode integer
+ *
+ * @return string
+ */
+ public function getResult( QueryResult $results, array $fullParams, $outputMode );
+
+ /**
+ * This function determines the query mode that is to be used for this printer in
+ * various contexts. The query mode influences how queries to that printer should
+ * be processed to obtain a result. Possible values are SMWQuery::MODE_INSTANCES
+ * (retrieve instances), SMWQuery::MODE_NONE (do nothing), SMWQuery::MODE_COUNT
+ * (get number of results), SMWQuery::MODE_DEBUG (return debugging text).
+ * Possible values for context are SMWQueryProcessor::SPECIAL_PAGE,
+ * SMWQueryProcessor::INLINE_QUERY, SMWQueryProcessor::CONCEPT_DESC.
+ *
+ * The default implementation always returns SMWQuery::MODE_INSTANCES. File exports
+ * like RSS will use MODE_INSTANCES on special pages (so that instances are
+ * retrieved for the export) and MODE_NONE otherwise (displaying just a download link).
+ *
+ * @param $context
+ *
+ * @return integer
+ */
+ public function getQueryMode( $context );
+
+ /**
+ * Get a human readable label for this printer. The default is to
+ * return just the format identifier. Concrete implementations may
+ * refer to messages here. The format name is normally not used in
+ * wiki text but only in forms etc. hence the user language should be
+ * used when retrieving messages.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Set whether errors should be shown. By default they are.
+ *
+ * @param boolean $show
+ */
+ public function setShowErrors( $show );
+
+ /**
+ * Takes a list of parameter definitions and adds those supported by this
+ * result printer. Most result printers should override this method.
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions );
+
+ /**
+ * Returns if the format is an export format.
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isExportFormat();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CategoryResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CategoryResultPrinter.php
new file mode 100644
index 00000000..63acdf6f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CategoryResultPrinter.php
@@ -0,0 +1,322 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use SMW\MediaWiki\Collator;
+use SMWDataItem as DataItem;
+use SMWQueryResult as QueryResult;
+use SMW\Utils\HtmlColumns;
+use SMW\ApplicationFactory;
+
+/**
+ * Print query results in alphabetic groups displayed in columns, a la the
+ * standard Category pages.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author David Loomer
+ * @author Yaron Koren
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CategoryResultPrinter extends ResultPrinter {
+
+ /**
+ * @var string
+ */
+ private $delim;
+
+ /**
+ * @var string
+ */
+ private $template;
+
+ /**
+ * @var string
+ */
+ private $userParam;
+
+ /**
+ * @var integer
+ */
+ private $numColumns;
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return wfMessage( 'smw_printername_' . $this->mFormat )->text();
+ }
+
+ /**
+ * @see ResultPrinter::isDeferrable
+ *
+ * {@inheritDoc}
+ */
+ public function isDeferrable() {
+ return true;
+ }
+
+ /**
+ * @see ResultPrinter::supportsRecursiveAnnotation
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function supportsRecursiveAnnotation() {
+ return true;
+ }
+
+ /**
+ * @see ResultPrinter::getParamDefinitions
+ *
+ * {@inheritDoc}
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $definitions = parent::getParamDefinitions( $definitions );
+
+ $definitions[] = [
+ 'name' => 'columns',
+ 'type' => 'integer',
+ 'message' => 'smw-paramdesc-columns',
+ 'negatives' => false,
+ 'default' => 3,
+ ];
+
+ $definitions[] = [
+ 'name' => 'delim',
+ 'message' => 'smw-paramdesc-category-delim',
+ 'default' => '',
+ ];
+
+ $definitions[] = [
+ 'name' => 'template',
+ 'message' => 'smw-paramdesc-category-template',
+ 'default' => '',
+ ];
+
+ $definitions[] = [
+ 'name' => 'userparam',
+ 'message' => 'smw-paramdesc-category-userparam',
+ 'default' => '',
+ ];
+
+ $definitions[] = [
+ 'name' => 'named args',
+ 'type' => 'boolean',
+ 'message' => 'smw-paramdesc-named_args',
+ 'default' => false,
+ ];
+
+ return $definitions;
+ }
+
+ /**
+ * @see ResultPrinter::handleParameters
+ *
+ * {@inheritDoc}
+ */
+ protected function handleParameters( array $params, $outputmode ) {
+ parent::handleParameters( $params, $outputmode );
+
+ $this->userParam = isset( $params['userparam'] ) ? trim( $params['userparam'] ) : '';
+ $this->delim = isset( $params['delim'] ) ? trim( $params['delim'] ) : '';
+ $this->numColumns = isset( $params['columns'] ) ? $params['columns'] : 3;
+ $this->template = isset( $params['template'] ) ? $params['template'] : '';
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected function initServices() {
+ $mwCollaboratorFactory = ApplicationFactory::getInstance()->newMwCollaboratorFactory();
+
+ $this->htmlColumns = new HtmlColumns();
+ $this->templateRenderer = $mwCollaboratorFactory->newWikitextTemplateRenderer();
+ $this->collator = Collator::singleton();
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * {@inheritDoc}
+ */
+ protected function getResultText( QueryResult $res, $outputMode ) {
+
+ $this->initServices();
+ $contents = $this->getContents( $res, $outputMode );
+
+ if ( $contents === [] ) {
+ return $res->addErrors( [ 'smw-qp-empty-data' ] );
+ }
+
+ $this->htmlColumns->setContinueAbbrev( wfMessage( 'listingcontinuesabbrev' )->text() );
+ $this->htmlColumns->setColumns( $this->numColumns );
+
+ // 0 indicates to use responsive columns
+ if ( $this->params['columns'] == 0 ) {
+ $this->htmlColumns->setColumnClass( 'smw-column-responsive' );
+ $this->htmlColumns->setColumns( 1 );
+ }
+
+ $this->htmlColumns->addContents( $contents, HtmlColumns::INDX_CONTENT );
+
+ return $this->htmlColumns->getHtml();
+ }
+
+ private function getContents( QueryResult $res, $outputMode ) {
+ $contents = [];
+
+ // Print all result rows:
+ $rowindex = 0;
+ $row = $res->getNext();
+
+ while ( $row !== false ) {
+ $nextrow = $res->getNext(); // look ahead
+
+ if ( !isset( $row[0] ) ) {
+ $row = $nextrow;
+ continue;
+ }
+
+ $content = $row[0]->getContent();
+
+ if ( !isset( $content[0] ) || !( $content[0] instanceof DataItem ) ) {
+ $row = $nextrow;
+ continue;
+ }
+
+ $first_letter = $this->first_letter( $res, $content[0] );
+
+ if ( !isset( $contents[$first_letter] ) ) {
+ $contents[$first_letter] = [];
+ $last_letter = $first_letter;
+ }
+
+ if ( $this->template !== '' ) { // build template code
+
+ $first_col = true;
+ $this->hasTemplates = true;
+
+ if ( $this->userParam ) {
+ $this->templateRenderer->addField( 'userparam', $this->userParam );
+ }
+
+ $this->row_to_template( $row, $res, $first_col );
+
+ $this->templateRenderer->addField( '#', $rowindex );
+ $this->templateRenderer->packFieldsForTemplate( $this->template );
+
+ // str_replace('|', '&#x007C;', // encode '|' for use in templates (templates fail otherwise) --
+ // this is not the place for doing this, since even DV-Wikitexts contain proper "|"!
+ $contents[$first_letter][] = $this->templateRenderer->render();
+ } else { // build simple list
+ $first_col = true;
+ $contents[$first_letter][] = $this->row_to_contents( $row, $first_col );
+ }
+
+ $row = $nextrow;
+ $rowindex++;
+ }
+
+ // Make label for finding further results
+ if ( $this->linkFurtherResults( $res ) ) {
+ $contents[$last_letter][] = $this->getFurtherResultsLink( $res, $outputMode )->getText( SMW_OUTPUT_WIKI, $this->mLinker );
+ }
+
+ return $contents;
+ }
+
+ private function first_letter( QueryResult $res, DataItem $dataItem ) {
+
+ $sortKey = $dataItem->getSortKey();
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $sortKey = $res->getStore()->getWikiPageSortKey( $dataItem );
+ }
+
+ return $this->collator->getFirstLetter( $sortKey );
+ }
+
+ private function row_to_contents( $row, &$first_col ) {
+
+ // has anything but the first column been printed?
+ $found_values = false;
+ $result = '';
+
+ foreach ( $row as $field ) {
+ $first_value = true;
+ $fieldValues = [];
+
+ while ( ( $text = $field->getNextText( SMW_OUTPUT_WIKI, $this->getLinker( $first_col ) ) ) !== false ) {
+
+ // first values after first column
+ if ( !$first_col && !$found_values ) {
+ $result .= '(';
+ $found_values = true;
+ }
+
+ // first value in any column, print header
+ if ( $first_value ) {
+ $first_value = false;
+ $printRequest = $field->getPrintRequest();
+
+ if ( $this->mShowHeaders && ( $printRequest->getLabel() !== '' ) ) {
+ $linker = $this->mShowHeaders === SMW_HEADERS_PLAIN ? null : $this->mLinker;
+ $result .= $printRequest->getText( SMW_OUTPUT_WIKI, $linker );
+ $result .= ' ';
+ }
+ }
+
+ $fieldValues[] = $text;
+ }
+
+ $first_col = false;
+
+ // Always sort the column value list in the same order
+ natsort( $fieldValues );
+ $result .= implode( ( $this->delim ? $this->delim : ',' ) . ' ', $fieldValues ) . ' ';
+ }
+
+ if ( $found_values ) {
+ $result = trim( $result ) . ')';
+ }
+
+ return $result;
+ }
+
+ private function row_to_template( $row, $res, &$first_col ) {
+
+ // explicitly number parameters for more robust parsing (values may contain "=")
+ $i = 0;
+
+ foreach ( $row as $field ) {
+ $i++;
+
+ $fieldName = '';
+
+ if ( $this->params['named args'] ) {
+ $fieldName = $field->getPrintRequest()->getLabel();
+ }
+
+ if ( $fieldName === '' || $fieldName === '?' ) {
+ $fieldName = $fieldName . $i;
+ }
+
+ $fieldValues = [];
+
+ while ( ( $text = $field->getNextText( SMW_OUTPUT_WIKI, $this->getLinker( $first_col ) ) ) !== false ) {
+ $fieldValues[] = $text;
+ }
+
+ natsort( $fieldValues );
+
+ $this->templateRenderer->addField( $fieldName, implode( $this->delim . ' ', $fieldValues ) );
+ $first_col = false;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CsvFileExportPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CsvFileExportPrinter.php
new file mode 100644
index 00000000..1f67f12c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/CsvFileExportPrinter.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use Sanitizer;
+use SMW\Utils\Csv;
+use SMWQueryResult as QueryResult;
+
+/**
+ * CSV export support
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Nathan R. Yergler
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class CsvFileExportPrinter extends FileExportPrinter {
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return $this->msg( 'smw_printername_csv' )->text();
+ }
+
+ /**
+ * @see FileExportPrinter::getMimeType
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getMimeType( QueryResult $queryResult ) {
+ return 'text/csv';
+ }
+
+ /**
+ * @see FileExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFileName( QueryResult $queryResult ) {
+ return $this->params['filename'];
+ }
+
+ /**
+ * @see ResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $definitions['searchlabel']->setDefault(
+ $this->msg( 'smw_csv_link' )->inContentLanguage()->text()
+ );
+
+ $params[] = [
+ 'name' => 'sep',
+ 'message' => 'smw-paramdesc-csv-sep',
+ 'default' => ',',
+ ];
+
+ $params['valuesep'] = [
+ 'message' => 'smw-paramdesc-csv-valuesep',
+ 'default' => ',',
+ ];
+
+ $params['showsep'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-showsep',
+ ];
+
+ $params[] = [
+ 'name' => 'filename',
+ 'message' => 'smw-paramdesc-filename',
+ 'default' => 'result.csv',
+ ];
+
+ $params['merge'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-csv-merge',
+ ];
+
+ $params['bom'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-csv-bom',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * {@inheritDoc}
+ */
+ protected function getResultText( QueryResult $res, $outputMode ) {
+
+ // Always return a link for when the output mode is not a file request,
+ // a file request is normally only initiated when resolving the query
+ // via Special:Ask
+ if ( $outputMode !== SMW_OUTPUT_FILE ) {
+ return $this->getCsvLink( $res, $outputMode );
+ }
+
+ $csv = new Csv(
+ $this->params['showsep'],
+ $this->params['bom']
+ );
+
+ return $this->getCsv( $csv, $res );
+ }
+
+ private function getCsvLink( QueryResult $res, $outputMode ) {
+
+ // Can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = $outputMode == SMW_OUTPUT_HTML;
+
+ $link = $this->getLink(
+ $res,
+ $outputMode
+ );
+
+ return $link->getText( $outputMode, $this->mLinker );
+ }
+
+ private function getCsv( Csv $csv, $res ) {
+
+ $sep = str_replace( '_', ' ', $this->params['sep'] );
+ $vsep = str_replace( '_', ' ', $this->params['valuesep'] );
+
+ $header = [];
+ $rows = [];
+
+ if ( $this->mShowHeaders ) {
+ foreach ( $res->getPrintRequests() as $pr ) {
+ $header[] = $pr->getLabel();
+ }
+ }
+
+ while ( $row = $res->getNext() ) {
+ $row_items = [];
+
+ foreach ( $row as /* SMWResultArray */ $field ) {
+ $growing = [];
+
+ while ( ( $object = $field->getNextDataValue() ) !== false ) {
+ $growing[] = Sanitizer::decodeCharReferences( $object->getWikiValue() );
+ }
+
+ $row_items[] = implode( $vsep, $growing );
+ }
+
+ $rows[] = $row_items;
+ }
+
+ if ( $this->params['merge'] === true ) {
+ $rows = $csv->merge( $rows, $vsep );
+ }
+
+ return $csv->toString(
+ $header,
+ $rows,
+ $sep
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FeedExportPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FeedExportPrinter.php
new file mode 100644
index 00000000..ece089d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FeedExportPrinter.php
@@ -0,0 +1,453 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use FeedItem;
+use ParserOptions;
+use Sanitizer;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Query\ExportPrinter;
+use SMW\Query\Result\StringResult;
+use SMW\Site;
+use SMWQueryResult as QueryResult;
+use TextContent;
+use Title;
+use WikiPage;
+
+/**
+ * Result printer that exports query results as RSS/Atom feed
+ *
+ * @since 1.8
+ *
+ * @license GNU GPL v2 or later
+ * @author mwjames
+ */
+final class FeedExportPrinter extends ResultPrinter implements ExportPrinter {
+
+ /**
+ * @var boolean
+ */
+ private $httpHeader = true;
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return $this->msg( 'smw-printername-feed' )->text();
+ }
+
+ /**
+ * @see ExportPrinter::isExportFormat
+ *
+ * {@inheritDoc}
+ */
+ public function isExportFormat() {
+ return true;
+ }
+
+ /**
+ * @see 3.0
+ */
+ public function disableHttpHeader() {
+ $this->httpHeader = false;
+ }
+
+ /**
+ * @see ExportPrinter::getMimeType
+ *
+ * {@inheritDoc}
+ */
+ public function getMimeType( QueryResult $queryResult ) {
+ return $this->params['type'] === 'atom' ? 'application/atom+xml' : 'application/rss+xml';
+ }
+
+ /**
+ * @see ExportPrinter::getFileName
+ *
+ * {@inheritDoc}
+ */
+ public function getFileName( QueryResult $queryResult ) {
+ return false;
+ }
+
+ /**
+ * @see ExportPrinter::outputAsFile
+ *
+ * {@inheritDoc}
+ */
+ public function outputAsFile( QueryResult $queryResult, array $params ) {
+ $result = $this->getResult( $queryResult, $params, SMW_OUTPUT_FILE );
+
+ if ( Site::isCommandLineMode() || $queryResult instanceof StringResult ) {
+
+ if ( $this->httpHeader ) {
+ header( 'Content-type: ' . 'text/xml' . '; charset=UTF-8' );
+ }
+
+ echo $result;
+ }
+ }
+
+ /**
+ * The export uses MODE_INSTANCES on special pages (so that instances are
+ * retrieved for the export) otherwise use MODE_NONE (displaying just a
+ * download link).
+ *
+ * @param $mode
+ *
+ * @return integer
+ */
+ public function getQueryMode( $mode ) {
+
+ if ( $mode == \SMWQueryProcessor::SPECIAL_PAGE ) {
+ return \SMWQuery::MODE_INSTANCES;
+ }
+ return \SMWQuery::MODE_NONE;
+
+ }
+
+ /**
+ * @see ResultPrinter::getParamDefinitions
+ *
+ * {@inheritDoc}
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( $this->msg( 'smw-label-feed-link' )->inContentLanguage()->text() );
+
+ $params['type'] = [
+ 'type' => 'string',
+ 'default' => 'rss',
+ 'message' => 'smw-paramdesc-feedtype',
+ 'values' => [ 'rss', 'atom' ],
+ ];
+
+ $params['title'] = [
+ 'message' => 'smw-paramdesc-feedtitle',
+ 'default' => '',
+ 'aliases' => [ 'rsstitle' ],
+ ];
+
+ $params['description'] = [
+ 'message' => 'smw-paramdesc-feeddescription',
+ 'default' => '',
+ 'aliases' => [ 'rssdescription' ],
+ ];
+
+ $params['page'] = [
+ 'message' => 'smw-paramdesc-feedpagecontent',
+ 'default' => 'none',
+ 'values' => [ 'none', 'full', 'abstract' ],
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @since 2.5
+ * @see ResultPrinter::getDefaultSort
+ *
+ * {@inheritDoc}
+ */
+ public function getDefaultSort() {
+ return 'DESC';
+ }
+
+ /**
+ * Returns a string that is to be sent to the caller
+ *
+ * @param QueryResult $res
+ * @param integer $outputMode
+ *
+ * @return string
+ */
+ protected function getResultText( QueryResult $res, $outputMode ) {
+
+ if ( $outputMode !== SMW_OUTPUT_FILE ) {
+ return $this->getFeedLink( $res, $outputMode );
+ }
+
+ if ( $res->getCount() == 0 ){
+ $res->addErrors( [ $this->msg( 'smw_result_noresults' )->inContentLanguage()->text() ] );
+ }
+
+ return $this->getFeed( $res, $this->params['type'] );
+ }
+
+ /**
+ * Build a feed
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $results
+ * @param $type
+ *
+ * @return string
+ */
+ protected function getFeed( QueryResult $results, $type ) {
+ global $wgFeedClasses;
+
+ if( !isset( $wgFeedClasses[$type] ) ) {
+ $results->addErrors( [ $this->msg( 'feed-invalid' )->inContentLanguage()->text() ] );
+ return '';
+ }
+
+ /**
+ * @var \ChannelFeed $feed
+ */
+ $feed = new $wgFeedClasses[$type](
+ $this->feedTitle(),
+ $this->feedDescription(),
+ $this->feedURL()
+ );
+
+ // Create feed header
+ if ( $this->httpHeader ) {
+ $feed->outHeader();
+ }
+
+ // Create feed items
+ while ( $row = $results->getNext() ) {
+ $feed->outItem( $this->feedItem( $row ) );
+ }
+
+ // Create feed footer
+ $feed->outFooter();
+ }
+
+ /**
+ * Returns feed title
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ protected function feedTitle() {
+
+ if ( $this->params['title'] === '' ) {
+ return $GLOBALS['wgSitename'];
+ }
+
+ return $this->params['title'];
+ }
+
+ /**
+ * Returns feed description
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ protected function feedDescription() {
+
+ if ( $this->params['description'] !== '' ) {
+ return $this->msg( 'smw-label-feed-description', $this->params['description'], $this->params['type'] )->text();
+ }
+
+ return $this->msg( 'tagline' )->text();
+ }
+
+ /**
+ * Returns feed URL
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ protected function feedURL() {
+
+ if ( $GLOBALS['wgTitle'] instanceof Title ) {
+ return $GLOBALS['wgTitle']->getFullUrl();
+ }
+
+ return Title::newFromText( 'Feed' )->getFullUrl();
+ }
+
+ /**
+ * Returns feed item
+ *
+ * @since 1.8
+ *
+ * @param array $row
+ *
+ * @return array
+ */
+ protected function feedItem( array $row ) {
+
+ $rowItems = [];
+ $subject = false;
+
+ /**
+ * Loop over all properties within a row
+ *
+ * @var \SMWResultArray $field
+ * @var \SMWDataValue $object
+ */
+ foreach ( $row as $field ) {
+ $itemSegments = [];
+
+ $subject = $field->getResultSubject()->getTitle();
+
+ // Loop over all values for the property.
+ while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
+ if ( $dataValue->getDataItem() instanceof DIWikiPage ) {
+
+ $linker = null;
+
+ if ( $dataValue->getDataItem()->getSubobjectName() === '' && $this->params['link'] !== 'none' ) {
+ $linker = smwfGetLinker();
+ }
+
+ $itemSegments[] = Sanitizer::decodeCharReferences( $dataValue->getLongWikiText( $linker ) );
+ } else {
+ $itemSegments[] = Sanitizer::decodeCharReferences( $dataValue->getWikiValue() );
+ }
+ }
+
+ // Join all property values into a single string, separated by a comma
+ if ( $itemSegments !== [] ) {
+ $rowItems[] = $this->parse( $subject, implode( ', ', $itemSegments ) );
+ }
+ }
+
+ if ( $subject instanceof Title ) {
+ return $this->newFeedItem( $subject, $rowItems );
+ }
+
+ return [];
+ }
+
+ /**
+ * Returns page content
+ *
+ * @since 1.8
+ *
+ * @param WikiPage $wikiPage
+ *
+ * @return string
+ */
+ protected function getPageContent( WikiPage $wikiPage ) {
+
+ if ( !in_array( $this->params['page'], [ 'abstract', 'full' ] ) ) {
+ return '';
+ }
+
+ if ( method_exists( $wikiPage, 'getContent' ) ) {
+ $content = $wikiPage->getContent();
+
+ if ( $content instanceof TextContent ) {
+ $text = $content->getNativeData();
+ } else {
+ return '';
+ }
+ } else {
+ $text = $wikiPage->getText();
+ }
+
+ return $this->parse( $wikiPage->getTitle(), $text );
+ }
+
+ /**
+ * Feed item description and property value output manipulation
+ *
+ * @note FeedItem will do an FeedItem::xmlEncode therefore no need
+ * to be overly cautious here
+ *
+ * @since 1.8
+ *
+ * @param array $items
+ * @param string $pageContent
+ *
+ * @return string
+ */
+ protected function feedItemDescription( $items, $pageContent ) {
+
+ $text = FeedItem::stripComment( implode( '', $items ) ) . FeedItem::stripComment( $pageContent );
+
+ // Abstract of the first 200 chars
+ if ( $this->params['page'] === 'abstract' ) {
+ $text = preg_replace('/\s+?(\S+)?$/', '', substr( $text, 0, 201 ) ) . ' ...';
+ }
+
+ return $text;
+ }
+
+ /**
+ * According to MW documentation, the comment field is only implemented for RSS
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ protected function feedItemComments( ) {
+ return '';
+ }
+
+ private function newFeedItem( $title, $rowItems ) {
+ $wikiPage = WikiPage::newFromID( $title->getArticleID() );
+
+ if ( $wikiPage !== null && $wikiPage->exists() ){
+
+ // #1741
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ DIWikipage::newFromTitle( $title )
+ );
+
+ // Ensures that the namespace prefix (Help:...) is used in cases where
+ // no display title is available.
+ $dataValue->setOption( 'prefixed.preferred.caption', true );
+
+ $feedItem = new FeedItem(
+ $dataValue->getPreferredCaption(),
+ $this->feedItemDescription( $rowItems, $this->getPageContent( $wikiPage ) ),
+ $title->getFullURL(),
+ $wikiPage->getTimestamp(),
+ $wikiPage->getUserText(),
+ $this->feedItemComments()
+ );
+ } else {
+ // #1562
+ $feedItem = new FeedItem(
+ $title->getPrefixedText(),
+ '',
+ $title->getFullURL()
+ );
+ }
+
+ return $feedItem;
+ }
+
+ private function parse( Title $title = null, $text ) {
+
+ if ( $title === null ) {
+ return $text;
+ }
+
+ $parserOptions = new ParserOptions();
+
+ // FIXME: Remove the if block once compatibility with MW <1.31 is dropped
+ if ( ! defined( '\ParserOutput::SUPPORTS_STATELESS_TRANSFORMS' ) || \ParserOutput::SUPPORTS_STATELESS_TRANSFORMS !== 1 ) {
+ $parserOptions->setEditSection( false );
+ }
+
+ return $GLOBALS['wgParser']->parse( $text, $title, $parserOptions )->getText( [ 'enableSectionEditLinks' => false ] );
+ }
+
+ private function getFeedLink( QueryResult $res, $outputMode ) {
+
+ // Can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = $outputMode == SMW_OUTPUT_HTML;
+
+ $link = $this->getLink(
+ $res,
+ $outputMode
+ );
+
+ return $link->getText( $outputMode, $this->mLinker );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FileExportPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FileExportPrinter.php
new file mode 100644
index 00000000..b58e62ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/FileExportPrinter.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use SMW\Query\ExportPrinter;
+use SMWQuery;
+use SMWQueryProcessor;
+use SMWQueryResult;
+
+/**
+ * Base class for file export result printers
+ *
+ * @since 1.8
+ * @license GNU GPL v2+
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class FileExportPrinter extends ResultPrinter implements ExportPrinter {
+
+ /**
+ * @var boolean
+ */
+ private $httpHeader = true;
+
+ /**
+ * @see ExportPrinter::isExportFormat
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isExportFormat() {
+ return true;
+ }
+
+ /**
+ * @see 3.0
+ */
+ public function disableHttpHeader() {
+ $this->httpHeader = false;
+ }
+
+ /**
+ * @see ExportPrinter::outputAsFile
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ * @param array $params
+ */
+ public function outputAsFile( SMWQueryResult $queryResult, array $params ) {
+ $result = $this->getResult( $queryResult, $params, SMW_OUTPUT_FILE );
+
+ if ( $this->httpHeader ) {
+ header( 'Content-type: ' . $this->getMimeType( $queryResult ) . '; charset=UTF-8' );
+ }
+
+ $fileName = $this->getFileName( $queryResult );
+
+ if ( $fileName !== false ) {
+ $utf8Name = rawurlencode( $fileName );
+ $fileName = iconv( "UTF-8", "ASCII//TRANSLIT", $fileName );
+
+ if ( $this->httpHeader ) {
+ header( "content-disposition: attachment; filename=\"$fileName\"; filename*=UTF-8''$utf8Name;" );
+ }
+ }
+
+ echo $result;
+ }
+
+ /**
+ * @see ExportPrinter::getFileName
+ *
+ * @since 1.8
+ *
+ * @param SMWQueryResult $queryResult
+ *
+ * @return string|boolean
+ */
+ public function getFileName( SMWQueryResult $queryResult ) {
+ return false;
+ }
+
+ /**
+ * File exports use MODE_INSTANCES on special pages (so that instances are
+ * retrieved for the export) and MODE_NONE otherwise (displaying just a download link).
+ *
+ * @param $mode
+ *
+ * @return integer
+ */
+ public function getQueryMode( $mode ) {
+ return $mode == SMWQueryProcessor::SPECIAL_PAGE ? SMWQuery::MODE_INSTANCES : SMWQuery::MODE_NONE;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter.php
new file mode 100644
index 00000000..6c24bc68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use ParamProcessor\ParamDefinition;
+use SMW\Message;
+use SMW\Query\ResultPrinters\ListResultPrinter\ListResultBuilder;
+use SMWQueryResult;
+
+/**
+ * Print query results in lists.
+ *
+ * @license GNU GPL v2+
+ *
+ * @author Markus Krötzsch
+ */
+
+/**
+ * SMW's printer for results in lists.
+ * The implementation covers comma-separated lists, ordered and unordered lists.
+ * List items may be formatted using templates.
+ *
+ * In the code below, one list item (with all extra information displayed for
+ * it) is called a "row", while one entry in this row is called a "field".
+ * Every field may in turn contain many "values".
+ */
+class ListResultPrinter extends ResultPrinter {
+
+ /**
+ * Get a human readable label for this printer.
+ *
+ * @return string
+ */
+ public function getName() {
+ // Give grep a chance to find the usages:
+ // smw_printername_list, smw_printername_ol,smw_printername_ul, smw_printername_plainlist, smw_printername_template
+ return Message::get( 'smw_printername_' . $this->mFormat, Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ /**
+ * @see ResultPrinter::isDeferrable
+ *
+ * {@inheritDoc}
+ */
+ public function isDeferrable() {
+ return true;
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * @param SMWQueryResult $queryResult
+ * @param $outputMode
+ *
+ * @return string
+ */
+ protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
+
+ $builder = $this->getBuilder( $queryResult );
+
+ $this->hasTemplates = $this->hasTemplates();
+
+ return $builder->getResultText() . $this->getFurtherResultsText( $queryResult, $outputMode );
+ }
+
+ /**
+ * @param SMWQueryResult $queryResult
+ *
+ * @return ListResultBuilder
+ */
+ private function getBuilder( SMWQueryResult $queryResult ) {
+
+ $builder = new ListResultBuilder( $queryResult, $this->mLinker );
+
+ $builder->set( $this->params );
+
+ $builder->set( [
+ 'link-first' => $this->mLinkFirst,
+ 'link-others' => $this->mLinkOthers,
+ 'show-headers' => $this->mShowHeaders,
+ ] );
+
+ if ( $this->params[ 'template' ] !== '' && isset( $this->fullParams[ 'sep' ] ) && $this->fullParams[ 'sep' ]->wasSetToDefault() === true ) {
+ $builder->set( 'sep', '' );
+ }
+
+ return $builder;
+ }
+
+ /**
+ * @return bool
+ */
+ private function hasTemplates() {
+ return $this->params[ 'template' ] !== '' || $this->params[ 'introtemplate' ] !== '' || $this->params[ 'outrotemplate' ] !== '';
+ }
+
+
+ /**
+ * Get text for further results link. Used only during getResultText().
+ *
+ * @since 1.9
+ * @param SMWQueryResult $res
+ * @param integer $outputMode
+ * @return string
+ */
+ private function getFurtherResultsText( SMWQueryResult $res, $outputMode ) {
+
+ if ( $this->linkFurtherResults( $res) ) {
+
+ $link = $this->getFurtherResultsLink( $res, $outputMode );
+ return $link->getText( SMW_OUTPUT_WIKI, $this->mLinker );
+
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function supportsRecursiveAnnotation() {
+ return true;
+ }
+
+ /**
+ * @see SMWIResultPrinter::getParamDefinitions
+ *
+ * @since 3.0
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return ParamDefinition[]
+ * @throws \Exception
+ */
+ public function getParamDefinitions( array $definitions ) {
+
+ $listFormatDefinitions = [
+
+ 'propsep' => [
+ 'message' => 'smw-paramdesc-propsep',
+ 'default' => Message::get( 'smw-format-list-property-separator' ),
+ ],
+
+ 'valuesep' => [
+ 'message' => 'smw-paramdesc-valuesep',
+ 'default' => Message::get( 'smw-format-list-value-separator' ),
+ ],
+
+ 'template' => [
+ 'message' => 'smw-paramdesc-template',
+ 'default' => '',
+ 'trim' => true,
+ ],
+
+ 'named args' => [
+ 'type' => 'boolean',
+ 'message' => 'smw-paramdesc-named_args',
+ 'default' => false,
+ ],
+
+ 'userparam' => [
+ 'message' => 'smw-paramdesc-userparam',
+ 'default' => '',
+ ],
+
+ 'class' => [
+ 'message' => 'smw-paramdesc-class',
+ 'default' => '',
+ ],
+
+ 'introtemplate' => [
+ 'message' => 'smw-paramdesc-introtemplate',
+ 'default' => '',
+ ],
+
+ 'outrotemplate' => [
+ 'message' => 'smw-paramdesc-outrotemplate',
+ 'default' => '',
+ ],
+
+ ];
+
+ if ( $this->mFormat !== 'ul' && $this->mFormat !== 'ol' ) {
+
+ $listFormatDefinitions[ 'sep' ] =
+ [
+ 'message' => 'smw-paramdesc-sep',
+ 'default' => ', ',
+ ];
+ }
+
+ return array_merge( $definitions, ParamDefinition::getCleanDefinitions( $listFormatDefinitions ) );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ListResultBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ListResultBuilder.php
new file mode 100644
index 00000000..6cf2bfc1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ListResultBuilder.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+use Linker;
+use SMW\Message;
+use SMWQueryResult;
+
+/**
+ * Class ListResultBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class ListResultBuilder {
+
+ private static $defaultConfigurations = [
+ '*' => [
+ 'value-open-tag' => '<span class="smw-value">',
+ 'value-close-tag' => '</span>',
+ 'field-open-tag' => '<span class="smw-field">',
+ 'field-close-tag' => '</span>',
+ 'field-label-open-tag' => '<span class="smw-field-label">',
+ 'field-label-close-tag' => '</span>',
+ 'field-label-separator' => ': ',
+ 'other-fields-open' => ' (',
+ 'other-fields-close' => ')',
+ ],
+ 'list' => [
+ 'row-open-tag' => '<span class="smw-row">',
+ 'row-close-tag' => '</span>',
+ 'result-open-tag' => '<span class="smw-format list-format $CLASS$">',
+ 'result-close-tag' => '</span>',
+ ],
+ 'ol' => [
+ 'row-open-tag' => "<li class=\"smw-row\">",
+ 'row-close-tag' => '</li>',
+ 'result-open-tag' => '<ol class="smw-format ol-format $CLASS$" start="$START$">',
+ 'result-close-tag' => '</ol>',
+ ],
+ 'ul' => [
+ 'row-open-tag' => '<li class="smw-row">',
+ 'row-close-tag' => '</li>',
+ 'result-open-tag' => '<ul class="smw-format ul-format $CLASS$">',
+ 'result-close-tag' => '</ul>',
+ ],
+ 'plainlist' => [
+ 'value-open-tag' => '',
+ 'value-close-tag' => '',
+ 'field-open-tag' => '',
+ 'field-close-tag' => '',
+ 'field-label-open-tag' => '',
+ 'field-label-close-tag' => '',
+ 'row-open-tag' => '',
+ 'row-close-tag' => '',
+ 'result-open-tag' => '',
+ 'result-close-tag' => '',
+ ],
+ ];
+
+ /** @var Linker|null */
+ private $linker = null;
+
+ /** @var SMWQueryResult */
+ private $queryResult;
+
+ /** @var ParameterDictionary */
+ private $configuration;
+
+ private $templateRendererFactory;
+
+ /**
+ * ListResultBuilder constructor.
+ *
+ * @param SMWQueryResult $queryResult
+ * @param Linker $linker
+ */
+ public function __construct( SMWQueryResult $queryResult, Linker $linker ) {
+ $this->linker = $linker;
+ $this->queryResult = $queryResult;
+ $this->configuration = new ParameterDictionary();
+ }
+
+ /**
+ * @return string
+ */
+ public function getResultText() {
+
+ $this->prepareBuilt();
+
+ return
+ $this->getTemplateCall( 'introtemplate' ) .
+ $this->get( 'result-open-tag' ) .
+
+ join( $this->get( 'sep' ), $this->getRowTexts() ) .
+
+ $this->get( 'result-close-tag' ) .
+ $this->getTemplateCall( 'outrotemplate' );
+ }
+
+ private function prepareBuilt() {
+
+ $format = $this->getEffectiveFormat();
+
+ $this->configuration->setDefault(
+ array_merge(
+ self::$defaultConfigurations[ '*' ],
+ self::$defaultConfigurations[ $format ],
+ $this->getDefaultsFromI18N() )
+ );
+
+ if ( $this->get( 'template' ) !== '' ) {
+
+ $this->set( [ 'value-open-tag' => '', 'value-close-tag' => '' ] );
+
+ }
+
+ $this->set( 'result-open-tag', $this->replaceVariables( $this->get( 'result-open-tag' ) ) );
+ }
+
+ /**
+ * @return string
+ */
+ private function getEffectiveFormat() {
+
+ $format = $this->get( 'format' );
+
+ if ( in_array( $format, [ 'ol', 'ul', 'plainlist' ] ) ) {
+ return $format;
+ }
+
+ if ( $this->get( 'template' ) !== '' ) {
+ return 'plainlist';
+ }
+
+ return 'list';
+ }
+
+ /**
+ * @param string $setting
+ * @param string $default
+ *
+ * @return mixed
+ */
+ protected function get( $setting, $default = '' ) {
+ return $this->configuration->get( $setting, $default );
+ }
+
+ /**
+ * @param string|string[] $setting
+ * @param string|null $value
+ */
+ public function set( $setting, $value = null ) {
+ $this->configuration->set( $setting, $value );
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getDefaultsFromI18N() {
+ return [
+ 'field-label-separator' => Message::get( 'smw-format-list-field-label-separator' ),
+ 'other-fields-open' => Message::get( 'smw-format-list-other-fields-open' ),
+ 'other-fields-close' => Message::get( 'smw-format-list-other-fields-close' ),
+ ];
+ }
+
+ /**
+ * @param string $subject
+ *
+ * @return string
+ */
+ private function replaceVariables( $subject ) {
+ return str_replace( [ '$START$', '$CLASS$' ], [ htmlspecialchars( $this->get( 'offset' ) + 1 ), htmlspecialchars( $this->get( 'class' ) ) ], $subject );
+ }
+
+ /**
+ * @param string $param
+ *
+ * @return string
+ */
+ private function getTemplateCall( $param ) {
+
+ $templatename = $this->get( $param );
+
+ if ( $templatename === '' ) {
+ return '';
+ }
+
+ $templateRenderer = $this->getTemplateRendererFactory()->getTemplateRenderer();
+ $templateRenderer->packFieldsForTemplate( $templatename );
+
+ return $templateRenderer->render();
+
+ }
+
+ /**
+ * @return TemplateRendererFactory
+ */
+ private function getTemplateRendererFactory() {
+
+ if ( $this->templateRendererFactory === null ) {
+ $this->templateRendererFactory = new TemplateRendererFactory( $this->getQueryResult() );
+ $this->templateRendererFactory->setUserparam( $this->get( 'userparam' ) );
+ }
+
+ return $this->templateRendererFactory;
+ }
+
+ /**
+ * @return SMWQueryResult
+ */
+ private function getQueryResult() {
+ return $this->queryResult;
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getRowTexts() {
+
+ $queryResult = $this->getQueryResult();
+ $queryResult->reset();
+
+ $rowTexts = [];
+ $num = $queryResult->getQuery()->getOffset();
+ $rowBuilder = $this->getRowBuilder();
+
+ while ( ( $row = $queryResult->getNext() ) !== false ) {
+
+ $rowTexts[] =
+ $this->get( 'row-open-tag' ) .
+ $rowBuilder->getRowText( $row, $num ) .
+ $this->get( 'row-close-tag' );
+
+ $num++;
+ }
+
+ return $rowTexts;
+ }
+
+ /**
+ * @return RowBuilder
+ */
+ private function getRowBuilder() {
+
+ if ( $this->get( 'template' ) === '' ) {
+ $rowBuilder = new SimpleRowBuilder();
+ $rowBuilder->setLinker( $this->linker );
+ } else {
+ $rowBuilder = new TemplateRowBuilder( $this->getTemplateRendererFactory() );
+ }
+
+ $valueTextsBuilder = new ValueTextsBuilder();
+ $valueTextsBuilder->setLinker( $this->linker );
+ $valueTextsBuilder->setConfiguration( $this->configuration );
+
+ $rowBuilder->setValueTextsBuilder( $valueTextsBuilder );
+ $rowBuilder->setConfiguration( $this->configuration );
+
+ return $rowBuilder;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionary.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionary.php
new file mode 100644
index 00000000..0f25baed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionary.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+/**
+ * Class ParameterDictionary
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class ParameterDictionary {
+
+ private $configuration = [];
+
+ /**
+ * @param string|string[] $setting
+ * @param mixed $value
+ */
+ public function set( $setting, $value = null ) {
+
+ if ( !is_array( $setting ) ) {
+ $setting = [ $setting => $value ];
+ }
+
+ $this->configuration = array_replace( $this->configuration, $setting );
+ }
+
+ /**
+ * @param string $setting
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get( $setting, $default = '' ) {
+ return isset( $this->configuration[ $setting ] ) ? $this->configuration[ $setting ] : $default;
+ }
+
+ /**
+ * @param string|string[] $setting
+ * @param mixed $value
+ */
+ public function setDefault( $setting, $value = null ) {
+
+ if ( !is_array( $setting ) ) {
+ $setting = [ $setting => $value ];
+ }
+
+ $this->configuration = array_replace( $setting, $this->configuration );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryUser.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryUser.php
new file mode 100644
index 00000000..1ba8588d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryUser.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+/**
+ * Class RowBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+trait ParameterDictionaryUser {
+
+ /** @var ParameterDictionary */
+ private $configuration;
+
+ /**
+ * @param ParameterDictionary $configuration
+ */
+ public function setConfiguration( ParameterDictionary &$configuration ) {
+ $this->configuration = $configuration;
+ }
+
+ /**
+ * @param string $setting
+ * @param string $default
+ *
+ * @return mixed
+ */
+ protected function get( $setting, $default = '' ) {
+ return $this->configuration->get( $setting, $default );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/RowBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/RowBuilder.php
new file mode 100644
index 00000000..c0f5b14c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/RowBuilder.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+/**
+ * Class RowBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+abstract class RowBuilder {
+
+ use ParameterDictionaryUser;
+
+ private $valueTextsBuilder;
+
+ /**
+ * @param \SMWResultArray[] $fields
+ *
+ * @param int $rownum
+ *
+ * @return string
+ */
+ abstract public function getRowText( array $fields, $rownum = 0 );
+
+ /**
+ * @return mixed
+ */
+ protected function getValueTextsBuilder() {
+ return $this->valueTextsBuilder;
+ }
+
+ /**
+ * @param mixed $valueTextsBuilder
+ */
+ public function setValueTextsBuilder( $valueTextsBuilder ) {
+ $this->valueTextsBuilder = $valueTextsBuilder;
+ }
+
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/SimpleRowBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/SimpleRowBuilder.php
new file mode 100644
index 00000000..fc37c46a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/SimpleRowBuilder.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+use Linker;
+use SMWResultArray;
+
+/**
+ * Class SimpleRowBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class SimpleRowBuilder extends RowBuilder {
+
+ private $linker;
+
+ /**
+ * @param \SMWResultArray[] $fields
+ *
+ * @param int $rownum
+ *
+ * @return string
+ */
+ public function getRowText( array $fields, $rownum = 0 ) {
+
+ $fieldTexts = $this->getFieldTexts( $fields );
+
+ $firstFieldText = array_shift( $fieldTexts );
+
+ if ( $firstFieldText === null ) {
+ return '';
+ }
+
+ if ( count( $fieldTexts ) > 0 ) {
+
+ $otherFieldsText =
+ $this->get( 'other-fields-open' ) .
+ join( $this->get( 'propsep' ), $fieldTexts ) .
+ $this->get( 'other-fields-close' );
+
+ } else {
+ $otherFieldsText = '';
+ }
+
+ return
+ $firstFieldText .
+ $otherFieldsText;
+ }
+
+ /**
+ * @param string[] $fields
+ *
+ * @return array
+ */
+ private function getFieldTexts( array $fields ) {
+
+ $columnNumber = 0;
+ $fieldTexts = [];
+
+ foreach ( $fields as $field ) {
+
+ $valuesText = $this->getValueTextsBuilder()->getValuesText( $field, $columnNumber );
+
+ if ( $valuesText !== '' ) {
+ $fieldTexts[] =
+ $this->get( 'field-open-tag' ) .
+ $this->getFieldLabel( $field ) .
+ $valuesText .
+ $this->get( 'field-close-tag' );
+ }
+
+ $columnNumber++;
+ }
+
+ return $fieldTexts;
+ }
+
+ /**
+ * @param SMWResultArray $field
+ *
+ * @return string
+ */
+ private function getFieldLabel( SMWResultArray $field ) {
+
+ $showHeaders = $this->get( 'show-headers' );
+
+ if ( $showHeaders === SMW_HEADERS_HIDE || $field->getPrintRequest()->getLabel() === '' ) {
+ return '';
+ }
+
+ $linker = $showHeaders === SMW_HEADERS_PLAIN ? null : $this->getLinker();
+
+ return
+ $this->get( 'field-label-open-tag' ) .
+ $field->getPrintRequest()->getText( SMW_OUTPUT_WIKI, $linker ) .
+ $this->get( 'field-label-close-tag' ) .
+ $this->get( 'field-label-separator' );
+
+ }
+
+ /**
+ * @return Linker
+ */
+ protected function getLinker() {
+ return $this->linker;
+ }
+
+ /**
+ * @param Linker $linker
+ */
+ public function setLinker( Linker $linker ) {
+ $this->linker = $linker;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRendererFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRendererFactory.php
new file mode 100644
index 00000000..ce689032
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRendererFactory.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+
+/**
+ * Class TemplateRendererFactory
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class TemplateRendererFactory {
+
+ private $templateRenderer;
+
+ private $queryResult;
+ private $numberOfPages;
+ private $userparam = '';
+
+ /**
+ * TemplateRendererFactory constructor.
+ *
+ * @param $queryResult
+ */
+ public function __construct( $queryResult ) {
+ $this->queryResult = $queryResult;
+ }
+
+ /**
+ * @param mixed $userparam
+ */
+ public function setUserparam( $userparam ) {
+ $this->userparam = $userparam;
+ }
+
+ /**
+ * @return WikitextTemplateRenderer
+ */
+ public function getTemplateRenderer() {
+
+ if ( $this->templateRenderer === null ) {
+ $this->templateRenderer = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newWikitextTemplateRenderer();
+ $this->addCommonTemplateFields( $this->templateRenderer );
+ }
+
+ return clone( $this->templateRenderer );
+ }
+
+ /**
+ * @param WikitextTemplateRenderer $templateRenderer
+ */
+ private function addCommonTemplateFields( WikitextTemplateRenderer $templateRenderer ) {
+
+ if ( $this->userparam !== '' ) {
+
+ $templateRenderer->addField(
+ '#userparam',
+ $this->userparam
+ );
+ }
+
+ $query = $this->getQueryResult()->getQuery();
+
+ $templateRenderer->addField(
+ '#querycondition',
+ $query->getQueryString()
+ );
+
+ $templateRenderer->addField(
+ '#querylimit',
+ $query->getLimit()
+ );
+
+ $templateRenderer->addField(
+ '#resultoffset',
+ $query->getOffset()
+ );
+
+ $templateRenderer->addField(
+ '#rowcount',
+ $this->getRowCount()
+ //$query->getCount() // FIXME: Re-activate if another query takes too long.
+ );
+ }
+
+ /**
+ * @return \SMWQueryResult
+ */
+ private function getQueryResult() {
+ return $this->queryResult;
+ }
+
+ /**
+ * @return int
+ */
+ private function getRowCount() {
+
+ if ( $this->numberOfPages === null ) {
+
+ $queryResult = $this->getQueryResult();
+
+ $countQuery = \SMWQueryProcessor::createQuery( $queryResult->getQueryString(), \SMWQueryProcessor::getProcessedParams( [] ) );
+ $countQuery->querymode = \SMWQuery::MODE_COUNT;
+
+ $countQueryResult = $queryResult->getStore()->getQueryResult( $countQuery );
+
+ $this->numberOfPages = $countQueryResult instanceof \SMWQueryResult ? $countQueryResult->getCountValue() : $countQueryResult;
+ }
+
+ return $this->numberOfPages;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRowBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRowBuilder.php
new file mode 100644
index 00000000..68abca19
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/TemplateRowBuilder.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+use SMWResultArray;
+
+/**
+ * Class TemplateRowBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class TemplateRowBuilder extends RowBuilder {
+
+ private $templateRendererFactory;
+
+ /**
+ * TemplateRowBuilder constructor.
+ *
+ * @param TemplateRendererFactory $templateRendererFactory
+ */
+ public function __construct( TemplateRendererFactory $templateRendererFactory ) {
+ $this->templateRendererFactory = $templateRendererFactory;
+ }
+
+ /**
+ * Returns text for one result row, formatted as a template call.
+ *
+ * @param \SMWResultArray[] $fields
+ *
+ * @param int $rownum
+ *
+ * @return string
+ */
+ public function getRowText( array $fields, $rownum = 0 ) {
+
+ $templateRenderer = $this->templateRendererFactory->getTemplateRenderer();
+
+ foreach ( $fields as $column => $field ) {
+
+ $fieldLabel = $this->getFieldLabel( $field, $column );
+ $fieldText = $this->getValueTextsBuilder()->getValuesText( $field, $column );
+
+ $templateRenderer->addField( $fieldLabel, $fieldText );
+ }
+
+ /** @deprecated since SMW 3.0 */
+ $templateRenderer->addField( '#', $rownum );
+
+ $templateRenderer->addField( '#rownumber', $rownum + 1 );
+ $templateRenderer->packFieldsForTemplate( $this->get( 'template' ) );
+
+ return $templateRenderer->render();
+
+ }
+
+ /**
+ * @param SMWResultArray $field
+ * @param int $column
+ *
+ * @return string
+ */
+ private function getFieldLabel( SMWResultArray $field, $column ) {
+
+ if ( $this->get( 'named args' ) === false ) {
+ return intval( $column + 1 );
+ }
+
+ $label = $field->getPrintRequest()->getLabel();
+
+ if ( $label === '' ) {
+ return intval( $column + 1 );
+ }
+
+ return $label;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ValueTextsBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ValueTextsBuilder.php
new file mode 100644
index 00000000..f328772d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ListResultPrinter/ValueTextsBuilder.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Query\ResultPrinters\ListResultPrinter;
+
+use Linker;
+use SMWDataValue;
+use SMWResultArray;
+
+/**
+ * Class ValueTextsBuilder
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class ValueTextsBuilder {
+
+ use ParameterDictionaryUser;
+
+ private $linker;
+
+ /**
+ * @param SMWResultArray $field
+ * @param int $column
+ *
+ * @return string
+ */
+ public function getValuesText( SMWResultArray $field, $column = 0 ) {
+
+ $valueTexts = $this->getValueTexts( $field, $column );
+
+ return join( $this->get( 'valuesep' ), $valueTexts );
+
+ }
+
+ /**
+ * @param SMWResultArray $field
+ * @param int $column
+ *
+ * @return string[]
+ */
+ private function getValueTexts( SMWResultArray $field, $column ) {
+
+ $valueTexts = [];
+
+ $field->reset();
+
+ while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
+
+ $valueTexts[] =
+ $this->get( 'value-open-tag' ) .
+ $this->getValueText( $dataValue, $column ) .
+ $this->get( 'value-close-tag' );
+ }
+
+ return $valueTexts;
+ }
+
+ /**
+ * @param SMWDataValue $value
+ * @param int $column
+ *
+ * @return string
+ */
+ private function getValueText( SMWDataValue $value, $column = 0 ) {
+
+ $text = $value->getShortText( SMW_OUTPUT_WIKI, $this->getLinkerForColumn( $column ) );
+
+ return $this->sanitizeValueText( $text );
+ }
+
+ /**
+ * Depending on current linking settings, returns a linker object
+ * for making hyperlinks or NULL if no links should be created.
+ *
+ * @param int $columnNumber Column number
+ *
+ * @return \Linker|null
+ */
+ private function getLinkerForColumn( $columnNumber ) {
+
+ if ( ( $columnNumber === 0 && $this->get( 'link-first' ) ) ||
+ ( $columnNumber > 0 && $this->get( 'link-others' ) ) ) {
+ return $this->getLinker();
+ }
+
+ return null;
+ }
+
+ /**
+ * @return Linker
+ */
+ protected function getLinker() {
+ return $this->linker;
+ }
+
+ /**
+ * @param Linker $linker
+ */
+ public function setLinker( Linker $linker ) {
+ $this->linker = $linker;
+ }
+
+ /**
+ * @param $text
+ *
+ * @return string
+ */
+ private function sanitizeValueText( $text ) {
+
+ if ( $this->isSimpleList() ) {
+ return $text;
+ }
+
+ return \Sanitizer::removeHTMLtags( $text, null, [], [], [ 'table', 'tr', 'th', 'td', 'dl', 'dd', 'ul', 'li', 'ol' ] );
+ }
+
+ /**
+ * @return bool
+ */
+ private function isSimpleList() {
+ $format = $this->get( 'format' );
+ return $format !== 'ul' && $format !== 'ol';
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/NullResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/NullResultPrinter.php
new file mode 100644
index 00000000..66f2ba3f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/NullResultPrinter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NullResultPrinter extends ResultPrinter {
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return 'null';
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * {@inheritDoc}
+ */
+ protected function getResultText( QueryResult $queryResult, $outputMode ) {
+ return '';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ResultPrinter.php
new file mode 100644
index 00000000..55845919
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/ResultPrinter.php
@@ -0,0 +1,749 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use Linker;
+use ParamProcessor\ParamDefinition;
+use ParserOptions;
+use Sanitizer;
+use SMW\Message;
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\Query\Result\StringResult;
+use SMW\Query\ResultPrinter as IResultPrinter;
+use SMWInfolink;
+use SMWOutputs as ResourceManager;
+use SMWQuery;
+use SMWQueryResult as QueryResult;
+use Title;
+
+/**
+ * Abstract base class for SMW's novel query printing mechanism. It implements
+ * part of the former functionality of SMWInlineQuery (everything related to
+ * output formatting and the corresponding parameters) and is subclassed by concrete
+ * printers that provide the main formatting functionality.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+abstract class ResultPrinter implements IResultPrinter {
+
+ /**
+ * Individual printers can decide what sort of deferrable mode is used for
+ * the output. `DEFERRED_DATA` signals that the format expects only the data
+ * component to be loaded from the backend.
+ */
+ const DEFERRED_DATA = 'deferred.data';
+
+ /**
+ * List of parameters, set by handleParameters.
+ * param name (lower case, trimmed) => param value (mixed)
+ *
+ * @since 1.7
+ *
+ * @var array
+ */
+ protected $params;
+
+ /**
+ * List of parameters, set by handleParameters.
+ * param name (lower case, trimmed) => IParam object
+ *
+ * @since 1.8
+ *
+ * @var \IParam[]
+ */
+ protected $fullParams;
+
+ /**
+ * @since 1.8
+ *
+ * @var
+ */
+ protected $outputMode;
+
+ /**
+ * The query result being displayed.
+ *
+ * @since 1.8
+ *
+ * @var QueryResult
+ */
+ protected $results;
+
+ /**
+ * Text to print *before* the output in case it is *not* empty; assumed to be wikitext.
+ * Normally this is handled in SMWResultPrinter and can be ignored by subclasses.
+ */
+ protected $mIntro = '';
+
+ /**
+ * Text to print *after* the output in case it is *not* empty; assumed to be wikitext.
+ * Normally this is handled in SMWResultPrinter and can be ignored by subclasses.
+ */
+ protected $mOutro = '';
+
+ /**
+ * Text to use for link to further results, or empty if link should not be shown.
+ * Unescaped! Use @see SMWResultPrinter::getSearchLabel()
+ * and @see SMWResultPrinter::linkFurtherResults()
+ * instead of accessing this directly.
+ */
+ protected $mSearchlabel = null;
+
+ /** Default return value for empty queries. Unescaped. Normally not used in sub-classes! */
+ protected $mDefault = '';
+
+ // parameters relevant for printers in general:
+ protected $mFormat; // a string identifier describing a valid format
+ protected $mLinkFirst; // should article names of the first column be linked?
+ protected $mLinkOthers; // should article names of other columns (besides the first) be linked?
+ protected $mShowHeaders = SMW_HEADERS_SHOW; // should the headers (property names) be printed?
+ protected $mShowErrors = true; // should errors possibly be printed?
+ protected $mInline; // is this query result "inline" in some page (only then a link to unshown results is created, error handling may also be affected)
+ protected $mLinker; // Linker object as needed for making result links. Might come from some skin at some time.
+
+ /**
+ * List of errors that occurred while processing the parameters.
+ *
+ * @since 1.6
+ *
+ * @var array
+ */
+ protected $mErrors = [];
+
+ /**
+ * If set, treat result as plain HTML. Can be used by printer classes if wiki mark-up is not enough.
+ * This setting is used only after the result text was generated.
+ * @note HTML query results cannot be used as parameters for other templates or in any other way
+ * in combination with other wiki text. The result will be inserted on the page literally.
+ */
+ protected $isHTML = false;
+
+ /**
+ * If set, take the necessary steps to make sure that things like {{templatename| ...}} are properly
+ * processed if they occur in the result. Clearly, this is only relevant if the output is not HTML, i.e.
+ * it is ignored if SMWResultPrinter::$is_HTML is true. This setting is used only after the result
+ * text was generated.
+ * @note This requires extra processing and may make the result less useful for being used as a
+ * parameter for further parser functions. Use only if required.
+ */
+ protected $hasTemplates = false;
+ /// Incremented while expanding templates inserted during printout; stop expansion at some point
+ private static $mRecursionDepth = 0;
+ /// This public variable can be set to higher values to allow more recursion; do this at your own risk!
+ /// This can be set in LocalSettings.php, but only after enableSemantics().
+ public static $maxRecursionDepth = 2;
+
+ /**
+ * @var RecursiveTextProcessor
+ */
+ protected $recursiveTextProcessor;
+
+ /**
+ * @var boolean
+ */
+ private $recursiveAnnotation = false;
+
+ /**
+ * For certaing activities (embedded pages etc.) make sure that annotations
+ * are not tranclucded (imported) into the target page when resolving a
+ * query.
+ *
+ * @var boolean
+ */
+ protected $transcludeAnnotation = true;
+
+ /**
+ * Return serialised results in specified format.
+ * Implemented by subclasses.
+ */
+ abstract protected function getResultText( QueryResult $res, $outputMode );
+
+ /**
+ * Constructor. The parameter $format is a format string
+ * that may influence the processing details.
+ *
+ * Do not override in deriving classes.
+ *
+ * @param string $format
+ * @param boolean $inline Optional since 1.9
+ */
+ public function __construct( $format, $inline = true ) {
+ global $smwgQDefaultLinking;
+
+ $this->mFormat = $format;
+ $this->mInline = $inline;
+ $this->mLinkFirst = ( $smwgQDefaultLinking != 'none' );
+ $this->mLinkOthers = ( $smwgQDefaultLinking == 'all' );
+ $this->mLinker = new Linker(); ///TODO: how can we get the default or user skin here (depending on context)?
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $queryContext
+ */
+ public function setQueryContext( $queryContext ) {
+ $this->mInline = $queryContext != QueryContext::SPECIAL_PAGE;
+ }
+
+ /**
+ * This method is added temporary measures to avoid breaking those that relied
+ * on the removed ContextSource interface.
+ *
+ * @since 3.0
+ *
+ * @return Message
+ */
+ public function msg() {
+ return wfMessage( func_get_args() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param RecursiveTextProcessor $recursiveTextProcessor
+ */
+ public function setRecursiveTextProcessor( RecursiveTextProcessor $recursiveTextProcessor ) {
+ $this->recursiveTextProcessor = $recursiveTextProcessor;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function isEnabledFeature( $feature ) {
+ return ( (int)$GLOBALS['smwgResultFormatsFeatures'] & $feature ) != 0;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function expandTemplates( $text ) {
+ return $this->recursiveTextProcessor->expandTemplates( $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $modules
+ * @param array $styleModules
+ */
+ public function registerResources( array $modules = [], array $styleModules = [] ) {
+
+ foreach ( $modules as $module ) {
+ ResourceManager::requireResource( $module );
+ }
+
+ foreach ( $styleModules as $styleModule ) {
+ ResourceManager::requireStyle( $styleModule );
+ }
+ }
+
+ /**
+ * @see IResultPrinter::getResult
+ *
+ * @note: since 1.8 this method is final, since it's the entry point.
+ * Most logic has been moved out to buildResult, which you can override.
+ *
+ * @param $results QueryResult
+ * @param $fullParams array
+ * @param $outputMode integer
+ *
+ * @return string
+ */
+ public final function getResult( QueryResult $results, array $fullParams, $outputMode ) {
+ $this->outputMode = $outputMode;
+ $this->results = $results;
+
+ $params = [];
+ $modules = [];
+ $styles = [];
+
+ /**
+ * @var \ParamProcessor\Param $param
+ */
+ foreach ( $fullParams as $param ) {
+ $params[$param->getName()] = $param->getValue();
+ }
+
+ $this->params = $params;
+ $this->fullParams = $fullParams;
+
+ $this->postProcessParameters();
+ $this->handleParameters( $this->params, $outputMode );
+
+ $resources = $this->getResources();
+
+ if ( isset( $resources['modules'] ) ) {
+ $modules = $resources['modules'];
+ }
+
+ if ( isset( $resources['styles'] ) ) {
+ $styles = $resources['styles'];
+ }
+
+ // Register possible default modules at this point to allow for content
+ // retrieved from a remote source to use required JS/CSS modules from the
+ // local entry point
+ $this->registerResources( $modules, $styles );
+
+ if ( $results instanceof StringResult ) {
+ $results->setOption( 'is.exportformat', $this->isExportFormat() );
+ return $results->getResults();
+ }
+
+ return $this->buildResult( $results );
+ }
+
+ /**
+ * Build and return the HTML result.
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $results
+ *
+ * @return string
+ */
+ protected function buildResult( QueryResult $results ) {
+ $this->isHTML = false;
+ $this->hasTemplates = false;
+
+ $outputMode = $this->outputMode;
+
+ // Default output for normal printers:
+ if ( $outputMode !== SMW_OUTPUT_FILE && $results->getCount() == 0 ) {
+ if ( !$results->hasFurtherResults() ) {
+ return $this->escapeText( $this->mDefault, $outputMode )
+ . $this->getErrorString( $results );
+ } elseif ( $this->mInline && $this->isDeferrable() !== self::DEFERRED_DATA ) {
+
+ if ( !$this->linkFurtherResults( $results ) ) {
+ return '';
+ }
+
+ return $this->getFurtherResultsLink( $results, $outputMode )->getText( $outputMode, $this->mLinker )
+ . $this->getErrorString( $results );
+ }
+ }
+
+ // Get output from printer:
+ $result = $this->getResultText( $results, $outputMode );
+
+ if ( $outputMode !== SMW_OUTPUT_FILE ) {
+ $result = $this->handleNonFileResult( $result, $results, $outputMode );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Continuation of getResult that only gets executed for non file outputs.
+ *
+ * @since 1.6
+ *
+ * @param string $result
+ * @param QueryResult $results
+ * @param integer $outputmode
+ *
+ * @return string
+ */
+ protected function handleNonFileResult( $result, QueryResult $results, $outputmode ) {
+
+ // append errors
+ $result .= $this->getErrorString( $results );
+
+ // Should not happen, used as fallback which in case the parser state
+ // relies on the $GLOBALS['wgParser']
+ if ( $this->recursiveTextProcessor === null ) {
+ $this->recursiveTextProcessor = new RecursiveTextProcessor();
+ }
+
+ $this->recursiveTextProcessor->uniqid();
+
+ $this->recursiveTextProcessor->setMaxRecursionDepth(
+ self::$maxRecursionDepth
+ );
+
+ $this->recursiveTextProcessor->transcludeAnnotation(
+ $this->transcludeAnnotation
+ );
+
+ $this->recursiveTextProcessor->setRecursiveAnnotation(
+ $this->recursiveAnnotation
+ );
+
+ // Apply intro parameter
+ if ( ( $this->mIntro ) && ( $results->getCount() > 0 ) ) {
+ if ( $outputmode == SMW_OUTPUT_HTML ) {
+ $result = Message::get( [ 'smw-parse', $this->mIntro ], Message::PARSE ) . $result;
+ } elseif ( $outputmode !== SMW_OUTPUT_RAW ) {
+ $result = $this->mIntro . $result;
+ }
+ }
+
+ // Apply outro parameter
+ if ( ( $this->mOutro ) && ( $results->getCount() > 0 ) ) {
+ if ( $outputmode == SMW_OUTPUT_HTML ) {
+ $result = $result . Message::get( [ 'smw-parse', $this->mOutro ], Message::PARSE );
+ } elseif ( $outputmode !== SMW_OUTPUT_RAW ) {
+ $result = $result . $this->mOutro;
+ }
+ }
+
+ // Preprocess embedded templates if needed
+ if ( ( !$this->isHTML ) && ( $this->hasTemplates ) ) {
+ $result = $this->recursiveTextProcessor->recursivePreprocess( $result );
+ }
+
+ if ( ( $this->isHTML ) && ( $outputmode == SMW_OUTPUT_WIKI ) ) {
+ $result = [ $result, 'isHTML' => true ];
+ } elseif ( ( !$this->isHTML ) && ( $outputmode == SMW_OUTPUT_HTML ) ) {
+ $result = $this->recursiveTextProcessor->recursiveTagParse( $result );
+ }
+
+ if ( $this->mShowErrors && $this->recursiveTextProcessor->getError() !== [] ) {
+ $result .= Message::get( $this->recursiveTextProcessor->getError(), Message::TEXT, Message::USER_LANGUAGE );
+ }
+
+ $this->recursiveTextProcessor->releaseAnnotationBlock();
+
+ return $result;
+ }
+
+ /**
+ * Does any additional parameter handling that needs to be done before the
+ * actual result is build. This includes cleaning up parameter values
+ * and setting class fields.
+ *
+ * Since 1.6 parameter handling should happen via validator based on the parameter
+ * definitions returned in getParameters. Therefore this method should likely
+ * not be used in any new code. It's mainly here for legacy reasons.
+ *
+ * @since 1.6
+ *
+ * @param array $params
+ * @param $outputMode
+ */
+ protected function handleParameters( array $params, $outputMode ) {
+ // No-op
+ }
+
+ /**
+ * Similar to handleParameters.
+ *
+ * @since 1.8
+ */
+ protected function postProcessParameters() {
+ $params = $this->params;
+
+ $this->mIntro = isset( $params['intro'] ) ? str_replace( '_', ' ', $params['intro'] ) : '';
+ $this->mOutro = isset( $params['outro'] ) ? str_replace( '_', ' ', $params['outro'] ) : '';
+
+ $this->mSearchlabel = !isset( $params['searchlabel'] ) || $params['searchlabel'] === false ? null : $params['searchlabel'];
+ $link = isset( $params['link'] ) ? $params['link'] : '';
+
+ switch ( $link ) {
+ case 'head': case 'subject':
+ $this->mLinkFirst = true;
+ $this->mLinkOthers = false;
+ break;
+ case 'all':
+ $this->mLinkFirst = true;
+ $this->mLinkOthers = true;
+ break;
+ case 'none':
+ $this->mLinkFirst = false;
+ $this->mLinkOthers = false;
+ break;
+ }
+
+ $this->mDefault = isset( $params['default'] ) ? str_replace( '_', ' ', $params['default'] ) : '';
+ $headers = isset( $params['headers'] ) ? $params['headers'] : '';
+
+ if ( $headers == 'hide' ) {
+ $this->mShowHeaders = SMW_HEADERS_HIDE;
+ } elseif ( $headers == 'plain' ) {
+ $this->mShowHeaders = SMW_HEADERS_PLAIN;
+ } else {
+ $this->mShowHeaders = SMW_HEADERS_SHOW;
+ }
+
+ $this->recursiveAnnotation = isset( $params['import-annotation'] ) ? $params['import-annotation'] : false;
+ }
+
+ /**
+ * Depending on current linking settings, returns a linker object
+ * for making hyperlinks or NULL if no links should be created.
+ *
+ * @param boolean $firstcol True of this is the first result column (having special linkage settings).
+ * @return Linker|null
+ */
+ protected function getLinker( $firstcol = false ) {
+ if ( ( $firstcol && $this->mLinkFirst ) || ( !$firstcol && $this->mLinkOthers ) ) {
+ return $this->mLinker;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Gets a SMWInfolink object that allows linking to a display of the query result.
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $res
+ * @param $outputMode
+ * @param string $classAffix
+ *
+ * @return SMWInfolink
+ */
+ protected function getLink( QueryResult $res, $outputMode, $classAffix = '' ) {
+ $link = $res->getQueryLink( $this->getSearchLabel( $outputMode ) );
+
+ if ( $classAffix !== '' ){
+ $link->setStyle( 'smw-' . $this->params['format'] . '-' . Sanitizer::escapeClass( $classAffix ) );
+ }
+
+ if ( isset( $this->params['format'] ) ) {
+ $link->setParameter( $this->params['format'], 'format' );
+ }
+
+ /**
+ * @var \IParam $param
+ */
+ foreach ( $this->fullParams as $param ) {
+ if ( !$param->wasSetToDefault() && !( $param->getName() == 'limit' && $param->getValue() === 0 ) ) {
+ $link->setParameter( $param->getOriginalValue(), $param->getName() );
+ }
+ }
+
+ return $link;
+ }
+
+ /**
+ * Gets a SMWInfolink object that allows linking to further results for the query.
+ *
+ * @since 1.8
+ *
+ * @param QueryResult $res
+ * @param $outputMode
+ *
+ * @return SMWInfolink
+ */
+ protected function getFurtherResultsLink( QueryResult $res, $outputMode ) {
+ $link = $this->getLink( $res, $outputMode, 'furtherresults' );
+ $link->setParameter( $this->params['offset'] + $res->getCount(), 'offset' );
+ return $link;
+ }
+
+ /**
+ * @see IResultPrinter::getQueryMode
+ *
+ * @param $context
+ *
+ * @return integer
+ */
+ public function getQueryMode( $context ) {
+ // TODO: Now that we are using RequestContext object maybe
+ // $context is misleading
+ return SMWQuery::MODE_INSTANCES;
+ }
+
+ /**
+ * @see IResultPrinter::getName
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->mFormat;
+ }
+
+ /**
+ * Provides a simple formatted string of all the error messages that occurred.
+ * Can be used if not specific error formatting is desired. Compatible with HTML
+ * and Wiki.
+ *
+ * @param QueryResult $res
+ *
+ * @return string
+ */
+ protected function getErrorString( QueryResult $res ) {
+ return $this->mShowErrors ? smwfEncodeMessages( array_merge( $this->mErrors, $res->getErrors() ) ) : '';
+ }
+
+ /**
+ * @see IResultPrinter::setShowErrors
+ *
+ * @param boolean $show
+ */
+ public function setShowErrors( $show ) {
+ $this->mShowErrors = $show;
+ }
+
+ /**
+ * Individual printer can override this method to allow for unified loading
+ * practice.
+ *
+ * Styles are loaded first to avoid a possible FOUC (Flash of unstyled content).
+ *
+ * @since 3.0
+ *
+ * @return []
+ */
+ protected function getResources() {
+ return [ 'modules' => [], 'styles' => [] ];
+ }
+
+ /**
+ * If $outputmode is SMW_OUTPUT_HTML, escape special characters occurring in the
+ * given text. Otherwise return text as is.
+ *
+ * @param string $text
+ * @param $outputmode
+ *
+ * @return string
+ */
+ protected function escapeText( $text, $outputmode ) {
+ return $outputmode == SMW_OUTPUT_HTML ? htmlspecialchars( $text ) : $text;
+ }
+
+ /**
+ * Get the string the user specified as a text for the "further results" link,
+ * properly escaped for the current output mode.
+ *
+ * @param $outputmode
+ *
+ * @return string
+ */
+ protected function getSearchLabel( $outputmode ) {
+ return $this->escapeText( $this->mSearchlabel, $outputmode );
+ }
+
+ /**
+ * Check whether a "further results" link would normally be generated for this
+ * result set with the given parameters. Individual result printers may decide to
+ * create or hide such a link independent of that, but this is the default.
+ *
+ * @param QueryResult $results
+ *
+ * @return boolean
+ */
+ protected function linkFurtherResults( QueryResult $results ) {
+ return $this->mInline && $results->hasFurtherResults() && $this->mSearchlabel !== '';
+ }
+
+ /**
+ * Adds an error message for a parameter handling error so a list
+ * of errors can be created later on.
+ *
+ * @since 1.6
+ *
+ * @param string $errorMessage
+ */
+ protected function addError( $errorMessage ) {
+ $this->mErrors[] = $errorMessage;
+ }
+
+ /**
+ * A function to describe the allowed parameters of a query using
+ * any specific format - most query printers should override this
+ * function.
+ *
+ * @deprecated since 1.8, use getParamDefinitions instead.
+ *
+ * @since 1.5
+ *
+ * @return array
+ */
+ public function getParameters() {
+ return [];
+ }
+
+ /**
+ * @see IResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * @param ParamDefinition[] $definitions
+ *
+ * @return array
+ */
+ public function getParamDefinitions( array $definitions ) {
+ return array_merge( $definitions, $this->getParameters() );
+ }
+
+ /**
+ * Returns the parameter definitions as an associative array where
+ * the keys hold the parameter names and point to their full definitions.
+ * array( name => array|IParamDefinition )
+ *
+ * @since 1.8
+ *
+ * @param array $definitions List of definitions to prepend to the result printers list before further processing.
+ *
+ * @return array
+ */
+ public final function getNamedParameters( array $definitions = [] ) {
+ $params = [];
+
+ foreach ( $this->getParamDefinitions( $definitions ) as $param ) {
+ $params[is_array( $param ) ? $param['name'] : $param->getName()] = $param;
+ }
+
+ return $params;
+ }
+
+ /**
+ * @see IResultPrinter::isExportFormat
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isExportFormat() {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function isDeferrable() {
+ return false;
+ }
+
+ /**
+ * Returns if the result printer supports using a "full parse" instead of a
+ * '[[SMW::off]]' . $wgParser->replaceVariables( $result ) . '[[SMW::on]]'
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function supportsRecursiveAnnotation() {
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getDefaultSort() {
+ return 'ASC';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TableResultPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TableResultPrinter.php
new file mode 100644
index 00000000..208e9102
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TableResultPrinter.php
@@ -0,0 +1,380 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use Html;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\Query\PrintRequest;
+use SMW\Query\QueryStringifier;
+use SMW\Utils\HtmlTable;
+use SMWDataValue;
+use SMWDIBlob as DIBlob;
+use SMWQueryResult as QueryResult;
+use SMWResultArray as ResultArray;
+
+/**
+ * Print query results in tables
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class TableResultPrinter extends ResultPrinter {
+
+ /**
+ * @var HtmlTable
+ */
+ private $htmlTable;
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return $this->msg( 'smw_printername_' . $this->mFormat )->text();
+ }
+
+ /**
+ * @see ResultPrinter::isDeferrable
+ *
+ * {@inheritDoc}
+ */
+ public function isDeferrable() {
+ return true;
+ }
+
+ /**
+ * @see ResultPrinter::getParamDefinitions
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getParamDefinitions( array $definitions ) {
+
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['class'] = [
+ 'name' => 'class',
+ 'message' => 'smw-paramdesc-table-class',
+ 'default' => 'sortable wikitable smwtable',
+ ];
+
+ $params['transpose'] = [
+ 'type' => 'boolean',
+ 'default' => false,
+ 'message' => 'smw-paramdesc-table-transpose',
+ ];
+
+ $params['sep'] = [
+ 'message' => 'smw-paramdesc-sep',
+ 'default' => '',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * {@inheritDoc}
+ */
+ protected function getResultText( QueryResult $res, $outputMode ) {
+
+ $this->isHTML = ( $outputMode === SMW_OUTPUT_HTML );
+ $this->isDataTable = false;
+ $class = isset( $this->params['class'] ) ? $this->params['class'] : '';
+
+ if ( strpos( $class, 'datatable' ) !== false && $this->mShowHeaders !== SMW_HEADERS_HIDE ) {
+ $this->isDataTable = true;
+ }
+
+ $this->htmlTable = new HtmlTable();
+
+ $columnClasses = [];
+ $headerList = [];
+
+ // Default cell value separator
+ if ( !isset( $this->params['sep'] ) || $this->params['sep'] === '' ) {
+ $this->params['sep'] = '<br>';
+ }
+
+ // building headers
+ if ( $this->mShowHeaders != SMW_HEADERS_HIDE ) {
+ $isPlain = $this->mShowHeaders == SMW_HEADERS_PLAIN;
+ foreach ( $res->getPrintRequests() as /* SMWPrintRequest */ $pr ) {
+ $attributes = [];
+ $columnClass = str_replace( [ ' ', '_' ], '-', strip_tags( $pr->getText( SMW_OUTPUT_WIKI ) ) );
+ $attributes['class'] = $columnClass;
+ // Also add this to the array of classes, for
+ // use in displaying each row.
+ $columnClasses[] = $columnClass;
+
+ // #2702 Use a fixed output on a requested plain printout
+ $mode = $this->isHTML && $isPlain ? SMW_OUTPUT_WIKI : $outputMode;
+ $text = $pr->getText( $mode, ( $isPlain ? null : $this->mLinker ) );
+ $headerList[] = $pr->getCanonicalLabel();
+ $this->htmlTable->header( ( $text === '' ? '&nbsp;' : $text ), $attributes );
+ }
+ }
+
+ $rowNumber = 0;
+
+ while ( $subject = $res->getNext() ) {
+ $rowNumber++;
+ $this->getRowForSubject( $subject, $outputMode, $columnClasses );
+
+ $this->htmlTable->row(
+ [
+ 'data-row-number' => $rowNumber
+ ]
+ );
+ }
+
+ // print further results footer
+ if ( $this->linkFurtherResults( $res ) ) {
+ $link = $this->getFurtherResultsLink( $res, $outputMode );
+
+ $this->htmlTable->cell(
+ $link->getText( $outputMode, $this->mLinker ),
+ [ 'class' => 'sortbottom', 'colspan' => $res->getColumnCount() ]
+ );
+
+ $this->htmlTable->row( [ 'class' => 'smwfooter' ] );
+ }
+
+ $tableAttrs = [ 'class' => $class ];
+
+ if ( $this->mFormat == 'broadtable' ) {
+ $tableAttrs['width'] = '100%';
+ $tableAttrs['class'] .= ' broadtable';
+ }
+
+ if ( $this->isDataTable ) {
+ $this->addDataTableAttrs(
+ $res,
+ $headerList,
+ $tableAttrs
+ );
+ }
+
+ $transpose = $this->mShowHeaders !== SMW_HEADERS_HIDE && $this->params['transpose'];
+
+ $html = $this->htmlTable->table(
+ $tableAttrs,
+ $transpose,
+ $this->isHTML
+ );
+
+ if ( $this->isDataTable ) {
+
+ // Simple approximation to avoid a massive text reflow once the DT JS
+ // has finished processing the HTML table
+ $count = $this->params['transpose'] ? $res->getColumnCount() : $res->getCount();
+ $height = ( min( ( $count + ( $res->hasFurtherResults() ? 1 : 0 ) ), 10 ) * 50 ) + 40;
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-datatable smw-placeholder is-disabled smw-flex-center' . (
+ $this->params['class'] !== '' ? ' ' . $this->params['class'] : ''
+ ),
+ 'style' => "height:{$height}px;"
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-overlay-spinner medium flex'
+ ]
+ ) . $html
+ );
+ }
+
+ return $html;
+ }
+
+ /**
+ * Gets a single table row for a subject, ie page.
+ *
+ * @since 1.6.1
+ *
+ * @param SMWResultArray[] $subject
+ * @param int $outputMode
+ * @param string[] $columnClasses
+ *
+ * @return string
+ */
+ private function getRowForSubject( array $subject, $outputMode, array $columnClasses ) {
+ foreach ( $subject as $i => $field ) {
+ // $columnClasses will be empty if "headers=hide"
+ // was set.
+ if ( array_key_exists( $i, $columnClasses ) ) {
+ $columnClass = $columnClasses[$i];
+ } else {
+ $columnClass = null;
+ }
+
+ $this->getCellForPropVals( $field, $outputMode, $columnClass );
+ }
+ }
+
+ /**
+ * Gets a table cell for all values of a property of a subject.
+ *
+ * @since 1.6.1
+ *
+ * @param SMWResultArray $resultArray
+ * @param int $outputMode
+ * @param string $columnClass
+ *
+ * @return string
+ */
+ protected function getCellForPropVals( ResultArray $resultArray, $outputMode, $columnClass ) {
+ /** @var SMWDataValue[] $dataValues */
+ $dataValues = [];
+
+ while ( ( $dv = $resultArray->getNextDataValue() ) !== false ) {
+ $dataValues[] = $dv;
+ }
+
+ $printRequest = $resultArray->getPrintRequest();
+ $printRequestType = $printRequest->getTypeID();
+
+ $cellTypeClass = " smwtype$printRequestType";
+
+ // We would like the cell class to always be defined, even if the cell itself is empty
+ $attributes = [
+ 'class' => $columnClass . $cellTypeClass
+ ];
+
+ $content = null;
+
+ if ( count( $dataValues ) > 0 ) {
+ $sortKey = $dataValues[0]->getDataItem()->getSortKey();
+ $dataValueType = $dataValues[0]->getTypeID();
+
+ // The data value type might differ from the print request type - override in this case
+ if ( $dataValueType !== '' && $dataValueType !== $printRequestType ) {
+ $attributes['class'] = "$columnClass smwtype$dataValueType";
+ }
+
+ if ( is_numeric( $sortKey ) ) {
+ $attributes['data-sort-value'] = $sortKey;
+ }
+
+ if ( $this->isDataTable && $sortKey !== '' ) {
+ $attributes['data-order'] = $sortKey;
+ }
+
+ $alignment = trim( $printRequest->getParameter( 'align' ) );
+
+ if ( in_array( $alignment, [ 'right', 'left', 'center' ] ) ) {
+ $attributes['style'] = "text-align:$alignment;";
+ }
+
+ $width = htmlspecialchars(
+ trim( $printRequest->getParameter( 'width' ) ),
+ ENT_QUOTES
+ );
+
+ if ( $width ) {
+ $attributes['style'] = ( isset( $attributes['style'] ) ? $attributes['style'] . ' ' : '' ) . "width:$width;";
+ }
+
+ $content = $this->getCellContent(
+ $dataValues,
+ $outputMode,
+ $printRequest->getMode() == PrintRequest::PRINT_THIS
+ );
+ }
+
+ // Sort the cell HTML attributes, to make test behavior more deterministic
+ ksort( $attributes );
+
+ $this->htmlTable->cell( $content, $attributes );
+ }
+
+ /**
+ * Gets the contents for a table cell for all values of a property of a subject.
+ *
+ * @since 1.6.1
+ *
+ * @param SMWDataValue[] $dataValues
+ * @param $outputMode
+ * @param boolean $isSubject
+ *
+ * @return string
+ */
+ protected function getCellContent( array $dataValues, $outputMode, $isSubject ) {
+ $values = [];
+
+ foreach ( $dataValues as $dv ) {
+
+ // Restore output in Special:Ask on:
+ // - file/image parsing
+ // - text formatting on string elements including italic, bold etc.
+ if ( $outputMode === SMW_OUTPUT_HTML && $dv->getDataItem() instanceof DIWikiPage && $dv->getDataItem()->getNamespace() === NS_FILE ||
+ $outputMode === SMW_OUTPUT_HTML && $dv->getDataItem() instanceof DIBlob ) {
+ // Too lazy to handle the Parser object and besides the Message
+ // parse does the job and ensures no other hook is executed
+ $value = Message::get(
+ [ 'smw-parse', $dv->getShortText( SMW_OUTPUT_WIKI, $this->getLinker( $isSubject ) ) ],
+ Message::PARSE
+ );
+ } else {
+ $value = $dv->getShortText( $outputMode, $this->getLinker( $isSubject ) );
+ }
+
+
+ $values[] = $value === '' ? '&nbsp;' : $value;
+ }
+
+ return implode( $this->params['sep'], $values );
+ }
+
+ /**
+ * @see ResultPrinter::getResources
+ */
+ protected function getResources() {
+
+ $class = isset( $this->params['class'] ) ? $this->params['class'] : '';
+
+ if ( strpos( $class, 'datatable' ) === false ) {
+ return [];
+ }
+
+ return [
+ 'modules' => [
+ 'smw.tableprinter.datatable'
+ ],
+ 'styles' => [
+ 'onoi.dataTables.styles',
+ 'smw.tableprinter.datatable.styles'
+ ]
+ ];
+ }
+
+ private function addDataTableAttrs( $res, $headerList, &$tableAttrs ) {
+
+ $tableAttrs['width'] = '100%';
+ $tableAttrs['style'] = 'opacity:.0';
+
+ $tableAttrs['data-column-sort'] = json_encode(
+ [
+ 'list' => $headerList,
+ 'sort' => $this->params['sort'],
+ 'order' => $this->params['order']
+ ]
+ );
+
+ $tableAttrs['data-query'] = QueryStringifier::toJson(
+ $res->getQuery()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TemplateFileExportPrinter.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TemplateFileExportPrinter.php
new file mode 100644
index 00000000..af739f35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ResultPrinters/TemplateFileExportPrinter.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\Query\ResultPrinters;
+
+use Sanitizer;
+use SMW\ApplicationFactory;
+use SMWQueryResult as QueryResult;
+
+/**
+ * Exports data as file in a format that is defined by its invoked templates.
+ * Custom specifications and requirements can be specified freely by relying on
+ * the available template system.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TemplateFileExportPrinter extends FileExportPrinter {
+
+ /**
+ * @var integer
+ */
+ private $numRows = 0;
+
+ /**
+ * @var TemplateRenderer
+ */
+ private $templateRenderer;
+
+ /**
+ * @see ResultPrinter::getName
+ *
+ * {@inheritDoc}
+ */
+ public function getName() {
+ return $this->msg( 'smw_printername_templatefile' )->text();
+ }
+
+ /**
+ * @see FileExportPrinter::getMimeType
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getMimeType( QueryResult $queryResult ) {
+
+ if ( $this->params['mimetype'] !== '' ) {
+ return $this->params['mimetype'];
+ }
+
+ return 'text/plain';
+ }
+
+ /**
+ * @see FileExportPrinter::getFileName
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getFileName( QueryResult $queryResult ) {
+ return $this->params['filename'];
+ }
+
+ /**
+ * @see ResultPrinter::getParamDefinitions
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getParamDefinitions( array $definitions ) {
+ $params = parent::getParamDefinitions( $definitions );
+
+ $params['searchlabel']->setDefault( 'templateFile' );
+
+ $params['template arguments'] = [
+ 'message' => 'smw-paramdesc-template-arguments',
+ 'default' => 'legacy',
+ 'values' => [ 'numbered', 'named', 'legacy' ],
+ ];
+
+ $params['template'] = [
+ 'type' => 'string',
+ 'default' => '',
+ 'message' => 'smw-paramdesc-template',
+ ];
+
+ $params['valuesep'] = [
+ 'message' => 'smw-paramdesc-sep',
+ 'default' => ',',
+ ];
+
+ $params['userparam'] = [
+ 'message' => 'smw-paramdesc-userparam',
+ 'default' => '',
+ ];
+
+ $params['introtemplate'] = [
+ 'message' => 'smw-paramdesc-introtemplate',
+ 'default' => '',
+ ];
+
+ $params['outrotemplate'] = [
+ 'message' => 'smw-paramdesc-outrotemplate',
+ 'default' => '',
+ ];
+
+ $params['filename'] = [
+ 'message' => 'smw-paramdesc-filename',
+ 'default' => 'file.txt',
+ ];
+
+ $params['mimetype'] = [
+ 'type' => 'string',
+ 'message' => 'smw-paramdesc-mimetype',
+ 'default' => 'text/plain',
+ ];
+
+ return $params;
+ }
+
+ /**
+ * @see ResultPrinter::getResultText
+ *
+ * {@inheritDoc}
+ */
+ protected function getResultText( QueryResult $queryResult, $outputMode ) {
+
+ // Always return a link for when the output mode is not a file request,
+ // a file request is normally only initiated when resolving the query
+ // via Special:Ask
+ if ( $outputMode !== SMW_OUTPUT_FILE ) {
+ return $this->getFileLink( $queryResult, $outputMode );
+ }
+
+ $text = $this->expandTemplates(
+ $this->getText( $queryResult )
+ );
+
+ return trim( $text, "\n" );
+ }
+
+ private function getFileLink( QueryResult $queryResult, $outputMode ) {
+
+ // Can be viewed as HTML if requested, no more parsing needed
+ $this->isHTML = $outputMode == SMW_OUTPUT_HTML;
+
+ $link = $this->getLink(
+ $queryResult,
+ $outputMode
+ );
+
+ return $link->getText( $outputMode, $this->mLinker );
+ }
+
+ private function getText( $queryResult ) {
+
+ $this->templateRenderer = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newWikitextTemplateRenderer();
+ $result = '';
+
+ $link = $this->getLink(
+ $queryResult,
+ SMW_OUTPUT_RAW
+ );
+
+ $link = $link->getText( SMW_OUTPUT_RAW, $this->mLinker );
+
+ // Extra fields include:
+ // - {{{userparam}}}
+ // - {{{querylink}}}
+
+ if ( $this->params['introtemplate'] !== '' ) {
+ $this->templateRenderer->addField( 'userparam', $this->params['userparam'] );
+ $this->templateRenderer->addField( 'querylink', $link );
+
+ $this->templateRenderer->packFieldsForTemplate(
+ $this->params['introtemplate']
+ );
+
+ $result .= $this->templateRenderer->render();
+ }
+
+ while ( $row = $queryResult->getNext() ) {
+ $result .= $this->row( $queryResult, $row );
+ }
+
+ // Extra fields include:
+ // - {{{userparam}}}
+ // - {{{querylink}}}
+
+ if ( $this->params['outrotemplate'] !== '' ) {
+ $this->templateRenderer->addField( 'userparam', $this->params['userparam'] );
+ $this->templateRenderer->addField( 'querylink', $link );
+
+ $this->templateRenderer->packFieldsForTemplate(
+ $this->params['outrotemplate']
+ );
+
+ $result .= $this->templateRenderer->render();
+ }
+
+ return $result;
+ }
+
+ private function row( QueryResult $queryResult, array $row ) {
+
+ $this->numRows + 1;
+ $this->addFields( $row );
+
+ $this->templateRenderer->packFieldsForTemplate(
+ $this->params['template']
+ );
+
+ return $this->templateRenderer->render();
+ }
+
+ private function addFields( $row ) {
+
+ foreach ( $row as $i => $field ) {
+
+ $value = '';
+ $fieldName = '';
+
+ // {{{?Foo}}}
+ if ( $this->params['template arguments'] === 'legacy' ) {
+ $fieldName = '?' . $field->getPrintRequest()->getLabel();
+ }
+
+ // {{{Foo}}}
+ if ( $this->params['template arguments'] === 'named' ) {
+ $fieldName = $field->getPrintRequest()->getLabel();
+ }
+
+ // {{{1}}}
+ if ( $fieldName === '' || $fieldName === '?' || $this->params['template arguments'] === 'numbered' ) {
+ $fieldName = intval( $i + 1 );
+ }
+
+ while ( ( $text = $field->getNextText( SMW_OUTPUT_WIKI, $this->getLinker( $i == 0 ) ) ) !== false ) {
+ $value .= $value === '' ? $text : $this->params['valuesep'] . ' ' . $text;
+ }
+
+ $this->templateRenderer->addField( $fieldName, $value );
+ }
+
+ $this->templateRenderer->addField( '#', $this->numRows );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Query/ScoreSet.php b/www/wiki/extensions/SemanticMediaWiki/src/Query/ScoreSet.php
new file mode 100644
index 00000000..5ac38724
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Query/ScoreSet.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\Query;
+
+use SMW\DIWikiPage;
+
+/**
+ * Record scores for query results retrieved from stores that support the computation
+ * of relevance scores.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ScoreSet {
+
+ /**
+ * @var []
+ */
+ private $scores = [];
+
+ /**
+ * @var integer|null
+ */
+ private $max_score = null;
+
+ /**
+ * @var integer|null
+ */
+ private $min_score = null;
+
+ /**
+ * @since 3.0
+ *
+ * @param string|integer $max_score
+ */
+ public function max_score( $max_score ) {
+ $this->max_score = $max_score;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|integer $min_score
+ */
+ public function min_score( $min_score ) {
+ $this->min_score = $min_score;
+ }
+
+ /**
+ * @note The hash is expected to match DIWikiPage::getHash to easily match
+ * result subjects available in an QueryResult instance.
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage|string $hash
+ * @param string|integer $score
+ */
+ public function addScore( $hash, $score, $pos = null ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ if ( $pos === null ) {
+ $this->scores[] = [ $hash, $score ];
+ } else {
+ $this->scores[$pos] = [ $hash, $score ];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|string $hash
+ *
+ * @return string|integer|false
+ */
+ public function getScore( $hash ) {
+
+ if ( $hash instanceof DIWikiPage ) {
+ $hash = $hash->getHash();
+ }
+
+ foreach ( $this->scores as $map ) {
+ if ( $map[0] === $hash ) {
+ return $map[1];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getScores() {
+ return $this->scores;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $usort
+ */
+ public function usort( $usort ) {
+
+ if ( !$usort|| $this->scores === [] ) {
+ return;
+ }
+
+ usort( $this->scores, function( $a, $b ) {
+
+ if ( $a[1] == $b[1] ) {
+ return 0;
+ }
+
+ return ( $a[1] > $b[1] ) ? -1 : 1;
+ } );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $class
+ *
+ * @return string
+ */
+ public function asTable( $class = '' ) {
+
+ if ( $this->scores === [] ) {
+ return '';
+ }
+
+ $table = "<table class='$class'><thead>";
+ $table .= "<th>Score</th><th>Subject</th><th><span title='Sorting position'>Pos</span></th>";
+ $table .= "</thead><tbody>";
+
+ ksort( $this->scores );
+
+ foreach ( $this->scores as $pos => $set ) {
+ $table .= '<tr><td>' . $set[1] . '</td><td>' . $set[0] . '</td><td>' . $pos . '</td></tr>';
+ }
+
+ $table .= '</tbody></table>';
+
+ return $table;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/QueryEngine.php b/www/wiki/extensions/SemanticMediaWiki/src/QueryEngine.php
new file mode 100644
index 00000000..b25541c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/QueryEngine.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace SMW;
+
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * Interface for query answering that depend on concrete implementations to
+ * provide the filtering and matching process for specific conditions against a
+ * select back-end.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface QueryEngine {
+
+ /**
+ * Returns a QueryResult object that matches the condition described by a
+ * query.
+ *
+ * @note If the request was made for a debug (querymode MODE_DEBUG) query
+ * then a simple HTML-compatible string is returned.
+ *
+ * @since 2.5
+ *
+ * @param Query $query
+ *
+ * @return QueryResult|string
+ */
+ public function getQueryResult( Query $query );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/QueryFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/QueryFactory.php
new file mode 100644
index 00000000..ca0a36dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/QueryFactory.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW;
+
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\Description;
+use SMW\Query\Parser as QueryParser;
+use SMW\Query\Parser\DescriptionProcessor;
+use SMW\Query\Parser\LegacyParser;
+use SMW\Query\Parser\Tokenizer;
+use SMW\Query\PrintRequestFactory;
+use SMW\Query\ProfileAnnotatorFactory;
+use SMW\Query\QueryCreator;
+use SMW\Query\QueryToken;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryFactory {
+
+ /**
+ * @since 2.5
+ *
+ * @return ProfileAnnotatorFactory
+ */
+ public function newProfileAnnotatorFactory() {
+ return new ProfileAnnotatorFactory();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Description $description
+ * @param integer|false $context
+ *
+ * @return Query
+ */
+ public function newQuery( Description $description, $context = false ) {
+ return new Query( $description, $context );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DescriptionFactory
+ */
+ public function newDescriptionFactory() {
+ return new DescriptionFactory();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return PrintRequestFactory
+ */
+ public function newPrintRequestFactory() {
+ return new PrintRequestFactory();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return RequestOptions
+ */
+ public function newRequestOptions() {
+ return new RequestOptions();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $string
+ * @param integer $condition
+ * @param boolean $isDisjunctiveCondition
+ *
+ * @return StringCondition
+ */
+ public function newStringCondition( $string, $condition, $isDisjunctiveCondition = false ) {
+ return new StringCondition( $string, $condition, $isDisjunctiveCondition );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer|boolean $queryFeatures
+ *
+ * @return QueryParser
+ */
+ public function newQueryParser( $queryFeatures = false ) {
+ return $this->newLegacyQueryParser( $queryFeatures );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer|boolean $queryFeatures
+ *
+ * @return QueryParser
+ */
+ public function newLegacyQueryParser( $queryFeatures = false ) {
+
+ if ( $queryFeatures === false ) {
+ $queryFeatures = Applicationfactory::getInstance()->getSettings()->get( 'smwgQFeatures' );
+ }
+
+ return new LegacyParser(
+ new DescriptionProcessor( $queryFeatures ),
+ new Tokenizer(),
+ new QueryToken()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param Query $query
+ * @param DIWikiPage[]|[] $results = array()
+ * @param boolean $continue
+ *
+ * @return QueryResult
+ */
+ public function newQueryResult( Store $store, Query $query, $results = [], $continue = false ) {
+
+ $queryResult = new QueryResult(
+ $query->getDescription()->getPrintrequests(),
+ $query,
+ $results,
+ $store,
+ $continue
+ );
+
+ return $queryResult;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/RequestOptions.php b/www/wiki/extensions/SemanticMediaWiki/src/RequestOptions.php
new file mode 100644
index 00000000..a668ac5a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/RequestOptions.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Container object for various options that can be used when retrieving
+ * data from the store. These options are mostly relevant for simple,
+ * direct requests -- inline queries may require more complex options due
+ * to their more complex structure.
+ * Options that should not be used or where default values should be used
+ * can be left as initialised.
+ *
+ * @license GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Markus Krötzsch
+ */
+class RequestOptions {
+
+ const SEARCH_FIELD = 'search_field';
+
+ /**
+ * The maximum number of results that should be returned.
+ */
+ public $limit = -1;
+
+ /**
+ * A numerical offset. The first $offset results are skipped.
+ * Note that this does not imply a defined order of results
+ * (see SMWRequestOptions->$sort below).
+ */
+ public $offset = 0;
+
+ /**
+ * Should the result be ordered? The employed order is defined
+ * by the type of result that are requested: wiki pages and strings
+ * are ordered alphabetically, whereas other data is ordered
+ * numerically. Usually, the order should be fairly "natural".
+ */
+ public $sort = false;
+
+ /**
+ * If SMWRequestOptions->$sort is true, this parameter defines whether
+ * the results are ordered in ascending or descending order.
+ */
+ public $ascending = true;
+
+ /**
+ * Specifies a lower or upper bound for the values returned by the query.
+ * Whether it is lower or upper is specified by the parameter "ascending"
+ * (true->lower, false->upper).
+ */
+ public $boundary = null;
+
+ /**
+ * Specifies whether or not the requested boundary should be returned
+ * as a result.
+ */
+ public $include_boundary = true;
+
+ /**
+ * An array of string conditions that are applied if the result has a
+ * string label that can be subject to those patterns.
+ *
+ * @var StringCondition[]
+ */
+ private $stringConditions = [];
+
+ /**
+ * Contains extra conditions which a consumer is being allowed to interpret
+ * freely to modify a search condition.
+ *
+ * @var array
+ */
+ private $extraConditions = [];
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @since 1.0
+ *
+ * @param string $string to match
+ * @param integer $condition one of STRCOND_PRE, STRCOND_POST, STRCOND_MID
+ * @param boolean $isOr
+ * @param boolean $isNot
+ */
+ public function addStringCondition( $string, $condition, $isOr = false, $isNot = false ) {
+ $this->stringConditions[] = new StringCondition( $string, $condition, $isOr, $isNot );
+ }
+
+ /**
+ * Return the specified array of SMWStringCondition objects.
+ *
+ * @since 1.0
+ *
+ * @return array
+ */
+ public function getStringConditions() {
+ return $this->stringConditions;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param mixed $extraCondition
+ */
+ public function addExtraCondition( $extraCondition ) {
+ $this->extraConditions[] = $extraCondition;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getExtraConditions() {
+ return $this->extraConditions;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function setOption( $key, $value ) {
+ $this->options[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( isset( $this->options[$key] ) ) {
+ return $this->options[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $limit
+ */
+ public function setLimit( $limit ) {
+ $this->limit = (int)$limit;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getLimit() {
+ return (int)$this->limit;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $offset
+ */
+ public function setOffset( $offset ) {
+ $this->offset = (int)$offset;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getOffset() {
+ return (int)$this->offset;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ $stringConditions = '';
+
+ foreach ( $this->stringConditions as $stringCondition ) {
+ $stringConditions .= $stringCondition->getHash();
+ }
+
+ return json_encode( [
+ $this->limit,
+ $this->offset,
+ $this->sort,
+ $this->ascending,
+ $this->boundary,
+ $this->include_boundary,
+ $stringConditions,
+ $this->extraConditions,
+ $this->options,
+ ] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Rule/Rule.php b/www/wiki/extensions/SemanticMediaWiki/src/Rule/Rule.php
new file mode 100644
index 00000000..3722bb28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Rule/Rule.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Rule;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Rule {
+
+ /**
+ * @var string
+ */
+ private $name = '';
+
+ /**
+ * @var []
+ */
+ private $if = [];
+
+ /**
+ * @var []
+ */
+ private $then = [];
+
+ /**
+ * @var []
+ */
+ private $dependencies = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ */
+ public function __construct( $name, array $if, array $then, array $dependencies = [] ) {
+ $this->name = $name;
+ $this->if = $if;
+ $this->then = $then;
+ $this->dependencies = $dependencies;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getDependencies() {
+ return $this->dependencies;
+ }
+
+ /**
+ * @note < 7.1 unexpected 'if' (T_IF), expecting identifier (T_STRING) ...
+ *
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function when( $key = null ) {
+
+ if ( $key === null ) {
+ return $this->if;
+ }
+
+ if ( isset( $this->if[$key] ) ) {
+ return $this->if[$key];
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function then( $key = null ) {
+
+ if ( $key === null ) {
+ return $this->then;
+ }
+
+ if ( isset( $this->then[$key] ) ) {
+ return $this->then[$key];
+ }
+
+ return [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/BadHttpEndpointResponseException.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/BadHttpEndpointResponseException.php
new file mode 100644
index 00000000..5686fc03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/BadHttpEndpointResponseException.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\SPARQLStore\Exception;
+
+/**
+ * Class to escalate SPARQL query errors to the interface. We only do this for
+ * malformed queries, permission issues, etc. Connection problems are usually
+ * ignored so as to keep the wiki running even if the SPARQL backend is down.
+ *
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class BadHttpEndpointResponseException extends \Exception {
+
+ /// Error code: malformed query
+ const ERROR_MALFORMED = 1;
+ /// Error code: service refused to handle the request
+ const ERROR_REFUSED = 2;
+ /// Error code: the query required a graph that does not exist
+ const ERROR_GRAPH_NOEXISTS = 3;
+ /// Error code: some existing graph should not exist to run this query
+ const ERROR_GRAPH_EXISTS = 4;
+ /// Error code: unknown error
+ const ERROR_OTHER = 5;
+ /// Error code: required service not known
+ const ERROR_NOSERVICE = 6;
+
+ /**
+ * SPARQL query that caused the problem.
+ * @var string
+ */
+ public $queryText;
+
+ /**
+ * Error code
+ * @var integer
+ */
+ public $errorCode;
+
+ /**
+ * Constructor that creates an error message based on the given data.
+ *
+ * @param $errorCode integer error code as defined in this class
+ * @param $queryText string with the original SPARQL query/update
+ * @param $endpoint string URL of the endpoint
+ * @param $httpCode mixed integer HTTP error code or some string to print there
+ */
+ function __construct( $errorCode, $queryText, $endpoint, $httpCode = '<not given>' ) {
+
+ switch ( $errorCode ) {
+ case self::ERROR_MALFORMED:
+ $errorName = 'Malformed query';
+ break;
+ case self::ERROR_REFUSED:
+ $errorName = 'Query refused';
+ break;
+ case self::ERROR_GRAPH_NOEXISTS:
+ $errorName = 'Graph not existing';
+ break;
+ case self::ERROR_GRAPH_EXISTS:
+ $errorName = 'Graph already exists';
+ break;
+ case self::ERROR_NOSERVICE:
+ $errorName = 'Required service has not been defined';
+ break;
+ default:
+ $errorCode = self::ERROR_OTHER;
+ $errorName = 'Unkown error';
+ }
+
+ $message = "A SPARQL query error has occurred\n" .
+ "Query: $queryText\n" .
+ "Error: $errorName\n" .
+ "Endpoint: $endpoint\n" .
+ "HTTP response code: $httpCode\n";
+
+ parent::__construct( $message, $errorCode );
+ $this->errorCode = $errorCode;
+ $this->queryText = $queryText;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/HttpEndpointConnectionException.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/HttpEndpointConnectionException.php
new file mode 100644
index 00000000..75c347c4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/HttpEndpointConnectionException.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\SPARQLStore\Exception;
+
+/**
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HttpEndpointConnectionException extends \Exception {
+
+ /**
+ * @since 2.1
+ *
+ * @param string $endpoint
+ * @param integer $errorCode
+ * @param string $errorText
+ */
+ public function __construct( $endpoint, $errorCode, $errorText ) {
+ parent::__construct( "Failed to communicate to Endpoint: $endpoint\n" . "due to curl error: $errorCode ($errorText).\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/XmlParserException.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/XmlParserException.php
new file mode 100644
index 00000000..a81e7c73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/Exception/XmlParserException.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\SPARQLStore\Exception;
+
+/**
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class XmlParserException extends \Exception {
+
+ /**
+ * @since 2.1
+ *
+ * @param string $errorText
+ * @param integer $errorLine
+ * @param integer $errorColumn
+ */
+ public function __construct( $errorText, $errorLine, $errorColumn ) {
+ parent::__construct( "Failed with $errorText on line $errorLine and column $errorColumn .\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseErrorMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseErrorMapper.php
new file mode 100644
index 00000000..080cef7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseErrorMapper.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use Exception;
+use Onoi\HttpRequest\HttpRequest;
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+use SMW\SPARQLStore\Exception\HttpEndpointConnectionException;
+
+/**
+ * Post-processing for a bad inbound responses
+ *
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class HttpResponseErrorMapper {
+
+ private $httpRequest = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param HttpRequest $httpRequest
+ */
+ public function __construct( HttpRequest $httpRequest ) {
+ $this->httpRequest = $httpRequest;
+ }
+
+ /**
+ * Either throw a suitable exception or fall through if the error should be
+ * handled gracefully. It is attempted to throw exceptions for all errors that
+ * can generally be prevented by proper coding or configuration (e.g. query
+ * syntax errors), and to be silent on all errors that might be caused by
+ * network issues or temporary overloading of the server. In this case, calling
+ * methods rather return something that helps to make the best out of the situation.
+ *
+ * @since 2.0
+ *
+ * @param $endpoint string URL of endpoint that was used
+ * @param $sparql string query that caused the problem
+ *
+ * @throws Exception
+ * @throws SparqlDatabaseException
+ */
+ public function mapErrorResponse( $endpoint, $sparql ) {
+ $error = $this->httpRequest->getLastErrorCode();
+
+ switch ( $error ) {
+ case 22: // equals CURLE_HTTP_RETURNED_ERROR but this constant is not defined in PHP
+ $this->createResponseToHttpError( $this->httpRequest->getLastTransferInfo( CURLINFO_HTTP_CODE ), $endpoint, $sparql );
+ break;
+ case 52:
+ case CURLE_GOT_NOTHING:
+ break; // happens when 4Store crashes, do not bother the wiki
+ case CURLE_COULDNT_CONNECT:
+ break; // fail gracefully if backend is down
+ default:
+ throw new HttpEndpointConnectionException(
+ $endpoint,
+ $error,
+ $this->httpRequest->getLastError()
+ );
+ }
+ }
+
+ private function createResponseToHttpError( $httpCode, $endpoint, $sparql ) {
+
+ /// TODO We are guessing the meaning of HTTP codes here -- the SPARQL 1.1 spec does not yet provide this information for updates (April 15 2011)
+
+ if ( $httpCode == 400 ) { // malformed query
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_MALFORMED, $sparql, $endpoint, $httpCode );
+ } elseif ( $httpCode == 500 ) { // query refused; maybe fail gracefully here (depending on how stores use this)
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_REFUSED, $sparql, $endpoint, $httpCode );
+ } elseif ( $httpCode == 404 ) {
+ return; // endpoint not found, maybe down; fail gracefully
+ }
+
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_OTHER, $sparql, $endpoint, $httpCode );
+ }
+
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseParser.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseParser.php
new file mode 100644
index 00000000..7e94f737
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/HttpResponseParser.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+/**
+ * Provides an interface for which responses from a http client (repositor
+ * connection) are parsed into a unified format
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface HttpResponseParser {
+
+ /**
+ * @since 2.2
+ *
+ * @param string $response
+ *
+ * @return RepositoryResult
+ */
+ public function parse( $response );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/Condition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/Condition.php
new file mode 100644
index 00000000..82fe9b22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/Condition.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+/**
+ * Abstract class that represents a SPARQL (sub-)pattern and relevant pieces
+ * of associated information for using it in query building.
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+abstract class Condition {
+
+ /**
+ * If results could be ordered by the things that this condition
+ * matches, then this is the name of the variable to use in ORDER BY.
+ * Otherwise it is ''.
+ * @note SPARQL variable names do not include the initial "?" or "$".
+ * @var string
+ */
+ public $orderByVariable = '';
+
+ /**
+ * Array that relates sortkeys (given by the users, i.e. property
+ * names) to variable names in the generated SPARQL query.
+ * Format sortkey => variable name
+ * @var array
+ */
+ public $orderVariables = [];
+
+ /**
+ * Associative array of additional conditions that should not narrow
+ * down the set of results, but that introduce some relevant variable,
+ * typically for ordering. For instance, selecting the sortkey of a
+ * page needs only be done once per query. The array is indexed by the
+ * name of the (main) selected variable, e.g. "v42sortkey" to allow
+ * elimination of duplicate weak conditions that aim to introduce this
+ * variable.
+ * @var array of format "condition identifier" => "condition"
+ */
+ public $weakConditions = [];
+
+ /**
+ * Associative array of additional conditions that should can narrow
+ * down the set of results,
+ *
+ * @var array of format "condition identifier" => "condition"
+ */
+ public $cogentConditions = [];
+
+ /**
+ * Associative array of additional namespaces that this condition
+ * requires to be declared
+ * @var array of format "shortName" => "namespace URI"
+ */
+ public $namespaces = [];
+
+ /**
+ * Get the SPARQL condition string that this object represents. This
+ * does not include the weak conditions, or additional formulations to
+ * match singletons (see SMWSparqlSingletonCondition).
+ *
+ * @return string
+ */
+ abstract public function getCondition();
+
+ /**
+ * Tell whether the condition string returned by getCondition() is safe
+ * in the sense that it can be used alone in a SPARQL query. This
+ * requires that all filtered variables occur in some graph pattern,
+ * and that the condition is not empty.
+ *
+ * @return boolean
+ */
+ abstract public function isSafe();
+
+ public function addNamespaces( array $namespaces ) {
+ $this->namespaces = array_merge( $this->namespaces, $namespaces );
+ }
+
+ public function getWeakConditionString() {
+ return implode( '', $this->weakConditions );
+ }
+
+ public function getCogentConditionString() {
+ return implode( '', $this->cogentConditions );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FalseCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FalseCondition.php
new file mode 100644
index 00000000..2446cd02
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FalseCondition.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+/**
+ * Represents a condition that cannot match anything.
+ * Ordering is not relevant, as there is nothing to order.
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class FalseCondition extends Condition {
+
+ public function getCondition() {
+ return "<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .\n";
+ }
+
+ public function isSafe() {
+ return true;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FilterCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FilterCondition.php
new file mode 100644
index 00000000..e03f90c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/FilterCondition.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+/**
+ * A SPARQL condition that consists in a FILTER term only (possibly with some
+ * weak conditions to introduce the variables that the filter acts on).
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class FilterCondition extends Condition {
+
+ /**
+ * Additional filter condition, i.e. a string that could be placed in
+ * "FILTER( ... )".
+ * @var string
+ */
+ public $filter;
+
+ public function __construct( $filter, $namespaces = [] ) {
+ $this->filter = $filter;
+ $this->namespaces = $namespaces;
+ }
+
+ public function getCondition() {
+ return "FILTER( {$this->filter} )\n";
+ }
+
+ public function isSafe() {
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/SingletonCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/SingletonCondition.php
new file mode 100644
index 00000000..a7ba530b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/SingletonCondition.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+use SMWExpElement;
+
+/**
+ * A SPARQL condition that can match only a single element, or nothing at all.
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class SingletonCondition extends Condition {
+
+ /**
+ * Pattern string. Anything that can be used as a WHERE condition
+ * when put between "{" and "}". Can be empty if the result
+ * unconditionally is the given element.
+ * @var string
+ */
+ public $condition;
+
+ /**
+ * The single element that this condition may possibly match.
+ * @var SMWExpElement
+ */
+ public $matchElement;
+
+ /**
+ * Whether this condition is safe.
+ * @see SMWSparqlCondition::isSafe().
+ * @var boolean
+ */
+ public $isSafe;
+
+ public function __construct( SMWExpElement $matchElement, $condition = '', $isSafe = false, $namespaces = [] ) {
+ $this->matchElement = $matchElement;
+ $this->condition = $condition;
+ $this->isSafe = $isSafe;
+ $this->namespaces = $namespaces;
+ }
+
+ public function getCondition() {
+ return $this->condition;
+ }
+
+ public function isSafe() {
+ return $this->isSafe;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/TrueCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/TrueCondition.php
new file mode 100644
index 00000000..5ec622d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/TrueCondition.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+/**
+ * Represents a condition that matches everything. Weak conditions (see
+ * SMWSparqlCondition::$weakConditions) might be still be included to
+ * enable ordering (selecting sufficient data to order by).
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class TrueCondition extends Condition {
+
+ public function getCondition() {
+ return '';
+ }
+
+ public function isSafe() {
+ return false;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/WhereCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/WhereCondition.php
new file mode 100644
index 00000000..002282c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/Condition/WhereCondition.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\Condition;
+
+/**
+ * Container class that represents a SPARQL (sub-)pattern and relevant pieces
+ * of associated information for using it in query building.
+ *
+ * @ingroup SMWStore
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class WhereCondition extends Condition {
+
+ /**
+ * The pattern string. Anything that can be used as a WHERE condition
+ * when put between "{" and "}".
+ * @var string
+ */
+ public $condition;
+
+ /**
+ * Whether this condition is safe.
+ * @see SMWSparqlCondition::isSafe().
+ * @var boolean
+ */
+ public $isSafe;
+
+ public function __construct( $condition, $isSafe, $namespaces = [] ) {
+ $this->condition = $condition;
+ $this->isSafe = $isSafe;
+ $this->namespaces = $namespaces;
+ }
+
+ public function getCondition() {
+ return $this->condition;
+ }
+
+ public function isSafe() {
+ return $this->isSafe;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/ConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/ConditionBuilder.php
new file mode 100644
index 00000000..0f8587e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/ConditionBuilder.php
@@ -0,0 +1,629 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\DataTypeRegistry;
+use SMW\DataValues\PropertyChainValue;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\HierarchyLookup;
+use SMW\Message;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\Description;
+use SMW\SPARQLStore\HierarchyFinder;
+use SMW\SPARQLStore\QueryEngine\Condition\Condition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\TrueCondition;
+use SMW\Utils\CircularReferenceGuard;
+use SMWDataItem as DataItem;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * Build an internal representation for a SPARQL condition from individual query
+ * descriptions
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ConditionBuilder {
+
+ /**
+ * @var EngineOptions
+ */
+ private $engineOptions;
+
+ /**
+ * @var DispatchingDescriptionInterpreter
+ */
+ private $dispatchingDescriptionInterpreter;
+
+ /**
+ * @var CircularReferenceGuard
+ */
+ private $circularReferenceGuard;
+
+ /**
+ * @var HierarchyLookup
+ */
+ private $hierarchyLookup;
+
+ /**
+ * @var DescriptionFactory
+ */
+ private $descriptionFactory;
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * Counter used to generate globally fresh variables.
+ * @var integer
+ */
+ private $variableCounter = 0;
+
+ /**
+ * sortKeys that are being used while building the query conditions
+ * @var array
+ */
+ private $sortKeys = [];
+
+ /**
+ * The name of the SPARQL variable that represents the query result
+ * @var string
+ */
+ private $resultVariable = 'result';
+
+ /**
+ * @var string
+ */
+ private $joinVariable;
+
+ /**
+ * @var DIProperty|null
+ */
+ private $orderByProperty;
+
+ /**
+ * @var array
+ */
+ private $redirectByVariableReplacementMap = [];
+
+ /**
+ * @since 2.2
+ *
+ * @param DescriptionInterpreterFactory $descriptionInterpreterFactory
+ * @param EngineOptions|null $engineOptions
+ */
+ public function __construct( DescriptionInterpreterFactory $descriptionInterpreterFactory, EngineOptions $engineOptions = null ) {
+ $this->dispatchingDescriptionInterpreter = $descriptionInterpreterFactory->newDispatchingDescriptionInterpreter( $this );
+ $this->engineOptions = $engineOptions;
+
+ if ( $this->engineOptions === null ) {
+ $this->engineOptions = new EngineOptions();
+ }
+
+ $this->descriptionFactory = new DescriptionFactory();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $resultVariable
+ */
+ public function setResultVariable( $resultVariable ) {
+ $this->resultVariable = $resultVariable;
+ return $this;
+ }
+
+ /**
+ * Get a fresh unused variable name for building SPARQL conditions.
+ *
+ * @return string
+ */
+ public function getNextVariable( $prefix = 'v' ) {
+ return $prefix . ( ++$this->variableCounter );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param array $sortKeys
+ */
+ public function setSortKeys( $sortKeys ) {
+ $this->sortKeys = $sortKeys;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getSortKeys() {
+ return $this->sortKeys;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $error
+ */
+ public function addError( $error, $type = Message::TEXT ) {
+ $this->errors[Message::getHash( $error, $type )] = Message::encode( $error, $type );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param CircularReferenceGuard $circularReferenceGuard
+ */
+ public function setCircularReferenceGuard( CircularReferenceGuard $circularReferenceGuard ) {
+ $this->circularReferenceGuard = $circularReferenceGuard;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return CircularReferenceGuard
+ */
+ public function getCircularReferenceGuard() {
+ return $this->circularReferenceGuard;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param HierarchyLookup $hierarchyLookup
+ */
+ public function setHierarchyLookup( HierarchyLookup $hierarchyLookup ) {
+ $this->hierarchyLookup = $hierarchyLookup;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return HierarchyLookup
+ */
+ public function getHierarchyLookup() {
+ return $this->hierarchyLookup;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $joinVariable name of the variable that conditions
+ * will refer to
+ */
+ public function setJoinVariable( $joinVariable ) {
+ $this->joinVariable = $joinVariable;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getJoinVariable() {
+ return $this->joinVariable;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DIProperty|null $orderByProperty if given then
+ * this is the property the values of which this condition will refer
+ * to, and the condition should also enable ordering by this value
+ */
+ public function setOrderByProperty( $orderByProperty ) {
+ $this->orderByProperty = $orderByProperty;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DIProperty|null
+ */
+ public function getOrderByProperty() {
+ return $this->orderByProperty;
+ }
+
+ /**
+ * Get a Condition object for a Description.
+ *
+ * This conversion is implemented by a number of recursive functions,
+ * and this is the main entry point for this recursion. In particular,
+ * it resets global variables that are used for the construction.
+ *
+ * If property value variables should be recorded for ordering results
+ * later on, the keys of the respective properties need to be given in
+ * sortKeys earlier.
+ *
+ * @param Description $description
+ *
+ * @return Condition
+ */
+ public function getConditionFrom( Description $description ) {
+ $this->variableCounter = 0;
+
+ $this->setJoinVariable( $this->resultVariable );
+ $this->setOrderByProperty( null );
+
+ $condition = $this->mapDescriptionToCondition( $description );
+
+ $this->addMissingOrderByConditions(
+ $condition
+ );
+
+ $this->addPropertyPathToMatchRedirectTargets(
+ $condition
+ );
+
+ $this->addFilterToRemoveEntitiesThatContainRedirectPredicate(
+ $condition
+ );
+
+ return $condition;
+ }
+
+ /**
+ * Recursively create a Condition from a Description
+ *
+ * @param Description $description
+ *
+ * @return Condition
+ */
+ public function mapDescriptionToCondition( Description $description ) {
+ return $this->dispatchingDescriptionInterpreter->interpretDescription( $description );
+ }
+
+ /**
+ * Build the condition (WHERE) string for a given Condition.
+ * The function also expresses the single value of
+ * SingletonCondition objects in the condition, which may
+ * lead to additional namespaces for serializing its URI.
+ *
+ * @param Condition $condition
+ *
+ * @return string
+ */
+ public function convertConditionToString( Condition &$condition ) {
+
+ $conditionAsString = $condition->getWeakConditionString();
+
+ if ( ( $conditionAsString === '' ) && !$condition->isSafe() ) {
+ $swivtPageResource = Exporter::getInstance()->getSpecialNsResource( 'swivt', 'page' );
+ $conditionAsString = '?' . $this->resultVariable . ' ' . $swivtPageResource->getQName() . " ?url .\n";
+ }
+
+ $conditionAsString .= $condition->getCondition();
+ $conditionAsString .= $condition->getCogentConditionString();
+
+ if ( $condition instanceof SingletonCondition ) { // prepare for ASK, maybe rather use BIND?
+
+ $matchElement = $condition->matchElement;
+
+ if ( $matchElement instanceof ExpElement ) {
+ $matchElementName = TurtleSerializer::getTurtleNameForExpElement( $matchElement );
+ } else {
+ $matchElementName = $matchElement;
+ }
+
+ if ( $matchElement instanceof ExpNsResource ) {
+ $condition->namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
+ }
+
+ $conditionAsString = str_replace( '?' . $this->resultVariable . ' ', "$matchElementName ", $conditionAsString );
+ }
+
+ return $conditionAsString;
+ }
+
+ /**
+ * Create an Condition from an empty (true) description.
+ * May still require helper conditions for ordering.
+ *
+ * @param $joinVariable string name, see mapDescriptionToCondition()
+ * @param $orderByProperty mixed DIProperty or null, see mapDescriptionToCondition()
+ *
+ * @return Condition
+ */
+ public function newTrueCondition( $joinVariable, $orderByProperty ) {
+ $result = new TrueCondition();
+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty );
+ return $result;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param DataItem|null $dataItem
+ *
+ * @return string|null
+ */
+ public function tryToFindRedirectVariableForDataItem( DataItem $dataItem = null ) {
+
+ if ( !$dataItem instanceof DIWikiPage || !$this->isSetFlag( SMW_SPARQL_QF_REDI ) ) {
+ return null;
+ }
+
+ // Maybe there is a better way to verify the "isRedirect" state other
+ // than by using the Title object
+ if ( $dataItem->getTitle() === null || !$dataItem->getTitle()->isRedirect() ) {
+ return null;
+ }
+
+ $redirectExpElement = Exporter::getInstance()->getResourceElementForWikiPage( $dataItem );
+
+ // If the resource was matched to an imported vocab then no redirect is required
+ if ( $redirectExpElement->isImported() ) {
+ return null;
+ }
+
+ $valueName = TurtleSerializer::getTurtleNameForExpElement( $redirectExpElement );
+
+ // Add unknow redirect target/variable for value
+ if ( !isset( $this->redirectByVariableReplacementMap[$valueName] ) ) {
+
+ $namespaces[$redirectExpElement->getNamespaceId()] = $redirectExpElement->getNamespace();
+ $redirectByVariable = '?' . $this->getNextVariable( 'r' );
+
+ $this->redirectByVariableReplacementMap[$valueName] = [
+ $redirectByVariable,
+ $namespaces
+ ];
+ }
+
+ // Reuse an existing variable for the value to allow to be used more than
+ // once when referring to the same property/value redirect
+ list( $redirectByVariable, $namespaces ) = $this->redirectByVariableReplacementMap[$valueName];
+
+ return $redirectByVariable;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $featureFlag
+ *
+ * @return boolean
+ */
+ public function isSetFlag( $featureFlag ) {
+
+ $canUse = true;
+
+ // Adhere additional condition
+ if ( $featureFlag === SMW_SPARQL_QF_SUBP ) {
+ $canUse = $this->engineOptions->get( 'smwgQSubpropertyDepth' ) > 0;
+ }
+
+ if ( $featureFlag === SMW_SPARQL_QF_SUBC ) {
+ $canUse = $this->engineOptions->get( 'smwgQSubcategoryDepth' ) > 0;
+ }
+
+ return $this->engineOptions->get( 'smwgSparqlQFeatures' ) === ( (int)$this->engineOptions->get( 'smwgSparqlQFeatures' ) | (int)$featureFlag ) && $canUse;
+ }
+
+ /**
+ * Extend the given SPARQL condition by a suitable order by variable,
+ * if an order by property is set.
+ *
+ * @param Condition $sparqlCondition condition to modify
+ * @param string $mainVariable the variable that represents the value to be ordered
+ * @param mixed $orderByProperty DIProperty or null
+ * @param integer $diType DataItem type id if known, or DataItem::TYPE_NOTYPE to determine it from the property
+ */
+ public function addOrderByDataForProperty( Condition &$sparqlCondition, $mainVariable, $orderByProperty, $diType = DataItem::TYPE_NOTYPE ) {
+ if ( is_null( $orderByProperty ) ) {
+ return;
+ }
+
+ if ( $diType == DataItem::TYPE_NOTYPE ) {
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $orderByProperty->findPropertyTypeID() );
+ }
+
+ $this->addOrderByData( $sparqlCondition, $mainVariable, $diType );
+ }
+
+ /**
+ * Extend the given SPARQL condition by a suitable order by variable,
+ * possibly adding conditions if required for the type of data.
+ *
+ * @param Condition $sparqlCondition condition to modify
+ * @param string $mainVariable the variable that represents the value to be ordered
+ * @param integer $diType DataItem type id
+ */
+ public function addOrderByData( Condition &$condition, $mainVariable, $diType ) {
+
+ if ( $diType !== DataItem::TYPE_WIKIPAGE ) {
+ return $condition->orderByVariable = $mainVariable;
+ }
+
+ $condition->orderByVariable = $mainVariable . 'sk';
+
+ if ( $this->isSetFlag( SMW_SPARQL_QF_COLLATION ) ) {
+ $skeyExpElement = Exporter::getInstance()->getSpecialNsResource( 'swivt', 'sort' );
+ } else {
+ $skeyExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_SKEY' );
+ }
+
+ $weakConditions = [
+ $condition->orderByVariable =>"?$mainVariable " . $skeyExpElement->getQName() . " ?{$condition->orderByVariable} .\n"
+ ];
+
+ $condition->weakConditions += $weakConditions;
+ }
+
+ /**
+ * Extend the given Condition with additional conditions to
+ * ensure that it can be ordered by all requested properties. After
+ * this operation, every key in sortKeys is assigned to a query
+ * variable by $sparqlCondition->orderVariables.
+ *
+ * @param Condition $condition condition to modify
+ */
+ protected function addMissingOrderByConditions( Condition &$condition ) {
+ foreach ( $this->sortKeys as $propertyKey => $order ) {
+
+ if ( !is_string( $propertyKey ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( strpos( $propertyKey, " " ) !== false ) {
+ throw new RuntimeException( "Expected the canonical form of {$propertyKey} (without any whitespace)" );
+ }
+
+ if ( !array_key_exists( $propertyKey, $condition->orderVariables ) ) { // Find missing property to sort by.
+ $this->addOrderForUnknownPropertyKey( $condition, $propertyKey, $order );
+ }
+ }
+ }
+
+ private function addOrderForUnknownPropertyKey( Condition &$condition, $propertyKey, $order ) {
+
+ if ( $propertyKey === '' || $propertyKey === '#' ) { // order by result page sortkey
+
+ $this->addOrderByData(
+ $condition,
+ $this->resultVariable,
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ $condition->orderVariables[$propertyKey] = $condition->orderByVariable;
+ return;
+ } elseif ( PropertyChainValue::isChained( $propertyKey ) ) { // Try to extend query.
+ $propertyChainValue = new PropertyChainValue();
+ $propertyChainValue->setUserValue( $propertyKey );
+
+ if ( !$propertyChainValue->isValid() ) {
+ return $description;
+ }
+
+ $lastDataItem = $propertyChainValue->getLastPropertyChainValue()->getDataItem();
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $lastDataItem,
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ foreach ( $propertyChainValue->getPropertyChainValues() as $val ) {
+ $description = $this->descriptionFactory->newSomeProperty(
+ $val->getDataItem(),
+ $description
+ );
+ }
+
+ // Add and replace Foo.Bar=asc with Bar=asc as we ultimately only
+ // order to the result of the last element
+ $this->sortKeys[$lastDataItem->getKey()] = $order;
+ unset( $this->sortKeys[$propertyKey] );
+ $propertyKey = $lastDataItem->getKey();
+
+ $auxDescription = $description;
+ } else {
+ $auxDescription = $this->descriptionFactory->newSomeProperty(
+ new DIProperty( $propertyKey ),
+ $this->descriptionFactory->newThingDescription()
+ );
+ }
+
+ $this->setJoinVariable( $this->resultVariable );
+ $this->setOrderByProperty( null );
+
+ $auxCondition = $this->mapDescriptionToCondition(
+ $auxDescription
+ );
+
+ // orderVariables MUST be set for $propertyKey -- or there is a bug; let it show!
+ $condition->orderVariables[$propertyKey] = $auxCondition->orderVariables[$propertyKey];
+ $condition->weakConditions[$condition->orderVariables[$propertyKey]] = $auxCondition->getWeakConditionString() . $auxCondition->getCondition();
+ $condition->namespaces = array_merge( $condition->namespaces, $auxCondition->namespaces );
+ }
+
+ /**
+ * @see http://www.w3.org/TR/sparql11-query/#propertypaths
+ *
+ * Query of:
+ *
+ * SELECT DISTINCT ?result WHERE {
+ * ?result swivt:wikiPageSortKey ?resultsk .
+ * {
+ * ?result property:FOO ?v1 .
+ * FILTER( ?v1sk >= "=BAR" )
+ * ?v1 swivt:wikiPageSortKey ?v1sk .
+ * } UNION {
+ * ?result property:FOO ?v2 .
+ * }
+ * }
+ *
+ * results in:
+ *
+ * SELECT DISTINCT ?result WHERE {
+ * ?result swivt:wikiPageSortKey ?resultsk .
+ * ?r2 ^swivt:redirectsTo property:FOO .
+ * {
+ * ?result ?r2 ?v1 .
+ * FILTER( ?v1sk >= "=BAR" )
+ * ?v1 swivt:wikiPageSortKey ?v1sk .
+ * } UNION {
+ * ?result ?r2 ?v3 .
+ * }
+ * }
+ */
+ private function addPropertyPathToMatchRedirectTargets( Condition &$condition ) {
+
+ if ( $this->redirectByVariableReplacementMap === [] ) {
+ return;
+ }
+
+ $weakConditions = [];
+ $namespaces = [];
+
+ $rediExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_REDI' );
+ $namespaces[$rediExpElement->getNamespaceId()] = $rediExpElement->getNamespace();
+
+ foreach ( $this->redirectByVariableReplacementMap as $valueName => $content ) {
+ list( $redirectByVariable, $ns ) = $content;
+ $weakConditions[] = "$redirectByVariable " . "^" . $rediExpElement->getQName() . " $valueName .\n";
+ $namespaces = array_merge( $namespaces, $ns );
+ }
+
+ $condition->namespaces = array_merge( $condition->namespaces, $namespaces );
+ $condition->weakConditions += $weakConditions;
+ }
+
+ /**
+ * @see https://www.w3.org/TR/rdf-sparql-query/#func-bound
+ *
+ * Remove entities that contain a "swivt:redirectsTo" predicate
+ */
+ private function addFilterToRemoveEntitiesThatContainRedirectPredicate( Condition &$condition ) {
+
+ $rediExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_REDI' );
+ $namespaces[$rediExpElement->getNamespaceId()] = $rediExpElement->getNamespace();
+
+ $boundVariable = '?' . $this->getNextVariable( 'o' );
+ $cogentCondition = " OPTIONAL { ?$this->resultVariable " . $rediExpElement->getQName() . " $boundVariable } .\n FILTER ( !bound( $boundVariable ) ) .\n";
+
+ $condition->addNamespaces( $namespaces );
+ $condition->cogentConditions[$boundVariable] = $cogentCondition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreter.php
new file mode 100644
index 00000000..68c87df2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use SMW\Query\Language\Description;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface DescriptionInterpreter {
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description );
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( Description $description );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreterFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreterFactory.php
new file mode 100644
index 00000000..1ebdfb23
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreterFactory.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DispatchingDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DescriptionInterpreterFactory {
+
+ /**
+ * @since 2.5
+ *
+ * @param ConditionBuilder $conditionBuilder
+ *
+ * @return DispatchingDescriptionInterpreter
+ */
+ public function newDispatchingDescriptionInterpreter( ConditionBuilder $conditionBuilder ) {
+
+ $dispatchingDescriptionInterpreter = new DispatchingDescriptionInterpreter();
+
+ $dispatchingDescriptionInterpreter->addDefaultInterpreter(
+ new ThingDescriptionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new SomePropertyInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ConjunctionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new DisjunctionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new NamespaceDescriptionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ClassDescriptionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ValueDescriptionInterpreter( $conditionBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ConceptDescriptionInterpreter( $conditionBuilder )
+ );
+
+ return $dispatchingDescriptionInterpreter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
new file mode 100644
index 00000000..8edc933e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Description;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWDataItem as DataItem;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ClassDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ClassDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ list( $condition, $namespaces ) = $this->mapCategoriesToConditionElements(
+ $description->getCategories(),
+ $description->getHierarchyDepth(),
+ $joinVariable
+ );
+
+ // empty disjunction: always false, no results to order
+ if ( $condition === '' ) {
+ return new FalseCondition();
+ }
+
+ $result = new WhereCondition( $condition, true, $namespaces );
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty,
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ return $result;
+ }
+
+ private function mapCategoriesToConditionElements( array $categories, $depth, $joinVariable ) {
+
+ $condition = '';
+ $namespaces = [];
+ $instExpElement = $this->exporter->getSpecialPropertyResource( '_INST' );
+
+ foreach( $categories as $category ) {
+
+ $categoryExpElement = $this->exporter->getResourceElementForWikiPage( $category );
+ $categoryExpName = TurtleSerializer::getTurtleNameForExpElement( $categoryExpElement );
+
+ $namespaces[$categoryExpElement->getNamespaceId()] = $categoryExpElement->getNamespace();
+
+ $classHierarchyPattern = $this->tryToAddClassHierarchyPattern(
+ $category,
+ $depth,
+ $categoryExpName
+ );
+
+ $newcondition = $classHierarchyPattern === '' ? "{ " : "{\n" . $classHierarchyPattern;
+ $newcondition .= "?$joinVariable " . $instExpElement->getQName() . " $categoryExpName . }\n";
+
+ if ( $condition === '' ) {
+ $condition = $newcondition;
+ } else {
+ $condition .= "UNION\n$newcondition";
+ }
+ }
+
+ return [ $condition, $namespaces ];
+ }
+
+ private function tryToAddClassHierarchyPattern( $category, $depth, &$categoryExpName ) {
+
+ if ( !$this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_SUBC ) || ( $depth !== null && $depth < 1 ) ) {
+ return '';
+ }
+
+ if ( $this->conditionBuilder->getHierarchyLookup() === null || !$this->conditionBuilder->getHierarchyLookup()->hasSubcategory( $category ) ) {
+ return '';
+ }
+
+ $subClassExpElement = $this->exporter->getSpecialPropertyResource( '_SUBC' );
+
+ // @see notes in SomePropertyInterpreter
+ $pathOp = $depth > 1 || $depth === null ? '*' : '?';
+
+ $classHierarchyByVariable = "?" . $this->conditionBuilder->getNextVariable( 'sc' );
+ $condition = "$classHierarchyByVariable " . $subClassExpElement->getQName() . "$pathOp $categoryExpName .\n";
+ $categoryExpName = "$classHierarchyByVariable";
+
+ return $condition;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
new file mode 100644
index 00000000..8c78c922
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWExporter as Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ConceptDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $conceptDescription = $this->getConceptDescription(
+ $description->getConcept()
+ );
+
+ if ( $conceptDescription === '' ) {
+ return new FalseCondition();
+ }
+
+ $hash = 'concept-' . $conceptDescription->getQueryString();
+
+ $this->conditionBuilder->getCircularReferenceGuard()->mark( $hash );
+
+ if ( $this->conditionBuilder->getCircularReferenceGuard()->isCircular( $hash ) ) {
+
+ $this->conditionBuilder->addError(
+ [ 'smw-query-condition-circular', $description->getQueryString() ]
+ );
+
+ return new FalseCondition();
+ }
+
+ $this->conditionBuilder->setJoinVariable( $joinVariable );
+ $this->conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $condition = $this->conditionBuilder->mapDescriptionToCondition(
+ $conceptDescription
+ );
+
+ $this->conditionBuilder->getCircularReferenceGuard()->unmark( $hash );
+
+ return $condition;
+ }
+
+ private function getConceptDescription( DIWikiPage $concept ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $value = $applicationFactory->getStore()->getSemanticData( $concept )->getPropertyValues(
+ new DIProperty( '_CONC' )
+ );
+
+ if ( $value === null || $value === [] ) {
+ return '';
+ }
+
+ $value = end( $value );
+
+ $description = $applicationFactory->newQueryParser()->getQueryDescription(
+ $value->getConceptQuery()
+ );
+
+ $this->findCircularDescription( $concept, $description );
+
+ return $description;
+ }
+
+ private function findCircularDescription( $concept, $description ) {
+
+ if ( $description instanceof ConceptDescription ) {
+ if ( $description->getConcept()->equals( $concept ) ) {
+ $this->conditionBuilder->addError(
+ [ 'smw-query-condition-circular', $description->getQueryString() ]
+ );
+ return;
+ }
+ }
+
+ if ( $description instanceof Conjunction || $description instanceof Disjunction ) {
+ foreach ( $description->getDescriptions() as $desc ) {
+ $this->findCircularDescription( $concept, $desc );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php
new file mode 100644
index 00000000..0c8896ed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreter.php
@@ -0,0 +1,248 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\TrueCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ConjunctionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof Conjunction;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $subDescriptions = $description->getDescriptions();
+
+ $result = $this->doPreliminarySubDescriptionCheck(
+ $subDescriptions,
+ $joinVariable,
+ $orderByProperty
+ );
+
+ if ( $result !== null ) {
+ return $result;
+ }
+
+ $subConditionElements = $this->doResolveSubDescriptionsRecursively(
+ $subDescriptions,
+ $joinVariable
+ );
+
+ if ( $subConditionElements instanceof FalseCondition ) {
+ return $subConditionElements;
+ }
+
+ $result = $this->createConditionFromSubConditionElements( $subConditionElements );
+
+ $result->weakConditions = $subConditionElements->weakConditions;
+ $result->orderVariables = $subConditionElements->orderVariables;
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty
+ );
+
+ return $result;
+ }
+
+ private function doPreliminarySubDescriptionCheck( $subDescriptions, $joinVariable, $orderByProperty ) {
+
+ $count = count( $subDescriptions );
+
+ // empty conjunction: true
+ if ( $count == 0 ) {
+ return $this->conditionBuilder->newTrueCondition(
+ $joinVariable,
+ $orderByProperty
+ );
+ }
+
+ // conjunction with one element
+ if ( $count == 1 ) {
+
+ $this->conditionBuilder->setJoinVariable( $joinVariable );
+ $this->conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ return $this->conditionBuilder->mapDescriptionToCondition(
+ reset( $subDescriptions )
+ );
+ }
+
+ return null;
+ }
+
+ private function doResolveSubDescriptionsRecursively( $subDescriptions, $joinVariable ) {
+
+ // Using a stdClass as data container for simpler handling in follow-up tasks
+ // and as the class is not exposed publicly we don't need to create
+ // an extra "real" class to manage its elements
+ $subConditionElements = new \stdClass;
+
+ $subConditionElements->condition = '';
+ $subConditionElements->filter = '';
+ $subConditionElements->singletonMatchElement = null;
+
+ $namespaces = $weakConditions = $orderVariables = [];
+ $singletonMatchElementName = '';
+ $hasSafeSubconditions = false;
+
+ foreach ( $subDescriptions as $subDescription ) {
+
+ $this->conditionBuilder->setJoinVariable( $joinVariable );
+ $this->conditionBuilder->setOrderByProperty( null );
+
+ $subCondition = $this->conditionBuilder->mapDescriptionToCondition(
+ $subDescription
+ );
+
+ if ( $subCondition instanceof FalseCondition ) {
+ return new FalseCondition();
+ } elseif ( $subCondition instanceof TrueCondition ) {
+ // ignore true conditions in a conjunction
+ } elseif ( $subCondition instanceof WhereCondition ) {
+ $subConditionElements->condition .= $subCondition->condition;
+ } elseif ( $subCondition instanceof FilterCondition ) {
+ $subConditionElements->filter .= ( $subConditionElements->filter ? ' && ' : '' ) . $subCondition->filter;
+ } elseif ( $subCondition instanceof SingletonCondition ) {
+ $matchElement = $subCondition->matchElement;
+
+ if ( $matchElement instanceof ExpElement ) {
+ $matchElementName = TurtleSerializer::getTurtleNameForExpElement( $matchElement );
+ } else {
+ $matchElementName = $matchElement;
+ }
+
+ if ( $matchElement instanceof ExpNsResource ) {
+ $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
+ }
+
+ if ( ( $subConditionElements->singletonMatchElement !== null ) &&
+ ( $singletonMatchElementName !== $matchElementName ) ) {
+ return new FalseCondition();
+ }
+
+ $subConditionElements->condition .= $subCondition->condition;
+ $subConditionElements->singletonMatchElement = $subCondition->matchElement;
+ $singletonMatchElementName = $matchElementName;
+ }
+
+ $hasSafeSubconditions = $hasSafeSubconditions || $subCondition->isSafe();
+ $namespaces = array_merge( $namespaces, $subCondition->namespaces );
+ $weakConditions = array_merge( $weakConditions, $subCondition->weakConditions );
+ $orderVariables = array_merge( $orderVariables, $subCondition->orderVariables );
+ }
+
+ $subConditionElements->hasSafeSubconditions = $hasSafeSubconditions;
+ $subConditionElements->namespaces = $namespaces;
+ $subConditionElements->weakConditions = $weakConditions;
+ $subConditionElements->orderVariables = $orderVariables;
+
+ return $subConditionElements;
+ }
+
+ private function createConditionFromSubConditionElements( $subConditionElements ) {
+
+ if ( $subConditionElements->singletonMatchElement instanceof ExpElement ) {
+ return $this->createSingletonCondition( $subConditionElements );
+ }
+
+ if ( $subConditionElements->condition === '' ) {
+ return $this->createFilterCondition( $subConditionElements );
+ }
+
+ return $this->createWhereCondition( $subConditionElements );
+ }
+
+ private function createSingletonCondition( $subConditionElements ) {
+
+ if ( $subConditionElements->filter !== '' ) {
+ $subConditionElements->condition .= "FILTER( $subConditionElements->filter )";
+ }
+
+ $result = new SingletonCondition(
+ $subConditionElements->singletonMatchElement,
+ $subConditionElements->condition,
+ $subConditionElements->hasSafeSubconditions,
+ $subConditionElements->namespaces
+ );
+
+ return $result;
+ }
+
+ private function createFilterCondition( $subConditionElements ) {
+ return new FilterCondition(
+ $subConditionElements->filter,
+ $subConditionElements->namespaces
+ );
+ }
+
+ private function createWhereCondition( $subConditionElements ) {
+
+ if ( $subConditionElements->filter !== '' ) {
+ $subConditionElements->condition .= "FILTER( $subConditionElements->filter )";
+ }
+
+ $result = new WhereCondition(
+ $subConditionElements->condition,
+ $subConditionElements->hasSafeSubconditions,
+ $subConditionElements->namespaces
+ );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php
new file mode 100644
index 00000000..90af7fd1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreter.php
@@ -0,0 +1,256 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\TrueCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class DisjunctionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof Disjunction;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $subDescriptions = $description->getDescriptions();
+
+ $result = $this->doPreliminarySubDescriptionCheck(
+ $subDescriptions,
+ $joinVariable,
+ $orderByProperty
+ );
+
+ if ( $result !== null ) {
+ return $result;
+ }
+
+ $subConditionElements = $this->doResolveSubDescriptionsRecursively(
+ $subDescriptions,
+ $joinVariable,
+ $orderByProperty
+ );
+
+ if ( $subConditionElements instanceof TrueCondition ) {
+ return $subConditionElements;
+ }
+
+ if ( ( $subConditionElements->unionCondition === '' ) && ( $subConditionElements->filter === '' ) ) {
+ return new FalseCondition();
+ }
+
+ $result = $this->createConditionFromSubConditionElements(
+ $subConditionElements,
+ $joinVariable
+ );
+
+ $result->weakConditions = $subConditionElements->weakConditions;
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty
+ );
+
+ return $result;
+ }
+
+ private function doPreliminarySubDescriptionCheck( $subDescriptions, $joinVariable, $orderByProperty ) {
+
+ $count = count( $subDescriptions );
+
+ // empty Disjunction: true
+ if ( $count == 0 ) {
+ return new FalseCondition();
+ }
+
+ // Disjunction with one element
+ // else: proper disjunction; note that orderVariables found in subconditions cannot be used for the whole disjunction
+ if ( $count == 1 ) {
+
+ $this->conditionBuilder->setJoinVariable( $joinVariable );
+ $this->conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ return $this->conditionBuilder->mapDescriptionToCondition(
+ reset( $subDescriptions )
+ );
+ }
+
+ return null;
+ }
+
+ private function doResolveSubDescriptionsRecursively( $subDescriptions, $joinVariable, $orderByProperty ) {
+
+ // Using a stdClass as data container for simpler handling in follow-up tasks
+ // and as the class is not exposed publicly we don't need to create
+ // an extra "real" class to manage its elements
+ $subConditionElements = new \stdClass;
+
+ $subConditionElements->unionCondition = '';
+ $subConditionElements->filter = '';
+
+ $namespaces = $weakConditions = [];
+ $hasSafeSubconditions = false;
+
+ foreach ( $subDescriptions as $subDescription ) {
+
+ $this->conditionBuilder->setJoinVariable( $joinVariable );
+ $this->conditionBuilder->setOrderByProperty( null );
+
+ $subCondition = $this->conditionBuilder->mapDescriptionToCondition(
+ $subDescription
+ );
+
+ if ( $subCondition instanceof FalseCondition ) {
+ // empty parts in a disjunction can be ignored
+ } elseif ( $subCondition instanceof TrueCondition ) {
+ return $this->conditionBuilder->newTrueCondition(
+ $joinVariable,
+ $orderByProperty
+ );
+ } elseif ( $subCondition instanceof WhereCondition ) {
+ $hasSafeSubconditions = $hasSafeSubconditions || $subCondition->isSafe();
+ $subConditionElements->unionCondition .= ( $subConditionElements->unionCondition ? ' UNION ' : '' ) .
+ "{\n" . $subCondition->condition . "}";
+ } elseif ( $subCondition instanceof FilterCondition ) {
+ $subConditionElements->filter .= ( $subConditionElements->filter ? ' || ' : '' ) . $subCondition->filter;
+ } elseif ( $subCondition instanceof SingletonCondition ) {
+
+ $hasSafeSubconditions = $hasSafeSubconditions || $subCondition->isSafe();
+ $matchElement = $subCondition->matchElement;
+
+ if ( $matchElement instanceof ExpElement ) {
+ $matchElementName = TurtleSerializer::getTurtleNameForExpElement( $matchElement );
+ } else {
+ $matchElementName = $matchElement;
+ }
+
+ if ( $matchElement instanceof ExpNsResource ) {
+ $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
+ }
+
+ if ( $subCondition->condition === '' ) {
+ $subConditionElements->filter .= ( $subConditionElements->filter ? ' || ' : '' ) . "?$joinVariable = $matchElementName";
+ } else {
+ $subConditionElements->unionCondition .= ( $subConditionElements->unionCondition ? ' UNION ' : '' ) .
+ "{\n" . $subCondition->condition . " FILTER( ?$joinVariable = $matchElementName ) }";
+ }
+
+ // Relates to wikipage [[Foo::~*a*||~*A*]] in value regex disjunction
+ // where a singleton is required to search against the sortkey but
+ // replacing the filter with the condition temporary stored in
+ // weakconditions
+ if ( $subConditionElements->unionCondition && $subCondition->weakConditions !== [] ) {
+ $weakCondition = array_shift( $subCondition->weakConditions );
+ $subConditionElements->unionCondition = str_replace(
+ "FILTER( ?$joinVariable = $matchElementName )",
+ $weakCondition,
+ $subConditionElements->unionCondition
+ );
+ }
+ }
+
+ $namespaces = array_merge( $namespaces, $subCondition->namespaces );
+ $weakConditions = array_merge( $weakConditions, $subCondition->weakConditions );
+ }
+
+ $subConditionElements->namespaces = $namespaces;
+ $subConditionElements->weakConditions = $weakConditions;
+ $subConditionElements->hasSafeSubconditions = $hasSafeSubconditions;
+
+ return $subConditionElements;
+ }
+
+ private function createConditionFromSubConditionElements( $subConditionElements, $joinVariable ) {
+
+ if ( $subConditionElements->unionCondition === '' ) {
+ return $this->createFilterCondition( $subConditionElements );
+ }
+
+ if ( $subConditionElements->filter === '' ) {
+ return $this->createWhereCondition( $subConditionElements );
+ }
+
+ $subJoinVariable = $this->conditionBuilder->getNextVariable();
+
+ $subConditionElements->unionCondition = str_replace(
+ "?$joinVariable ",
+ "?$subJoinVariable ",
+ $subConditionElements->unionCondition
+ );
+
+ $subConditionElements->filter .= " || ?$joinVariable = ?$subJoinVariable";
+ $subConditionElements->hasSafeSubconditions = false;
+
+ $subConditionElements->unionCondition = "OPTIONAL { $subConditionElements->unionCondition }\n FILTER( $subConditionElements->filter )\n";
+
+ return $this->createWhereCondition( $subConditionElements );
+ }
+
+ private function createFilterCondition( $subConditionElements ) {
+ return new FilterCondition(
+ $subConditionElements->filter,
+ $subConditionElements->namespaces
+ );
+ }
+
+ private function createWhereCondition( $subConditionElements ) {
+ return new WhereCondition(
+ $subConditionElements->unionCondition,
+ $subConditionElements->hasSafeSubconditions,
+ $subConditionElements->namespaces
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php
new file mode 100644
index 00000000..7c105477
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DispatchingDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var DescriptionInterpreter[]
+ */
+ private $interpreters = [];
+
+ /**
+ * @var DescriptionInterpreter
+ */
+ private $defaultInterpreter = null;
+
+ /**
+ * @param Description $description
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+
+ foreach ( $this->interpreters as $interpreter ) {
+ if ( $interpreter->canInterpretDescription( $description ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param Description $description
+ *
+ * @return Condition
+ */
+ public function interpretDescription( Description $description ) {
+
+ foreach ( $this->interpreters as $interpreter ) {
+ if ( $interpreter->canInterpretDescription( $description ) ) {
+ return $interpreter->interpretDescription( $description );
+ }
+ }
+
+ // Instead of throwing an exception we return a ThingDescriptionInterpreter
+ // for all unregistered/unknown descriptions
+ return $this->defaultInterpreter->interpretDescription( $description );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DescriptionInterpreter $interpreter
+ */
+ public function addInterpreter( DescriptionInterpreter $interpreter ) {
+ $this->interpreters[] = $interpreter;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DescriptionInterpreter $defaultInterpreter
+ */
+ public function addDefaultInterpreter( DescriptionInterpreter $defaultInterpreter ) {
+ $this->defaultInterpreter = $defaultInterpreter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
new file mode 100644
index 00000000..a898a733
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWDataItem as DataItem;
+use SMWExpLiteral as ExpLiteral;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof NamespaceDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $nspropExpElement = $this->exporter->getSpecialNsResource( 'swivt', 'wikiNamespace' );
+ $nsExpElement = new ExpLiteral( strval( $description->getNamespace() ), 'http://www.w3.org/2001/XMLSchema#integer' );
+
+ $nsName = TurtleSerializer::getTurtleNameForExpElement( $nsExpElement );
+ $condition = "{ ?$joinVariable " . $nspropExpElement->getQName() . " $nsName . }\n";
+
+ $result = new WhereCondition( $condition, true, [] );
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty,
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
new file mode 100644
index 00000000..9350a7c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
@@ -0,0 +1,268 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\SomeProperty;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWDataItem as DataItem;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class SomePropertyInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof SomeProperty;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+
+ $property = $description->getProperty();
+
+ list( $innerOrderByProperty, $innerCondition, $innerJoinVariable ) = $this->doResolveInnerConditionRecursively(
+ $property,
+ $description->getDescription()
+ );
+
+ if ( $innerCondition instanceof FalseCondition ) {
+ return new FalseCondition();
+ }
+
+ $namespaces = $innerCondition->namespaces;
+
+ $objectName = $this->findObjectNameFromInnerCondition(
+ $innerCondition,
+ $innerJoinVariable,
+ $namespaces
+ );
+
+ list ( $subjectName, $objectName, $nonInverseProperty ) = $this->doExchangeForWhenInversePropertyIsUsed(
+ $property,
+ $objectName,
+ $joinVariable
+ );
+
+ $propertyName = $this->findMostSuitablePropertyRepresentation(
+ $property,
+ $nonInverseProperty,
+ $namespaces
+ );
+
+ $this->tryToAddPropertyPathForSaturatedHierarchy(
+ $innerCondition,
+ $nonInverseProperty,
+ $propertyName,
+ $description->getHierarchyDepth()
+ );
+
+ $condition = $this->concatenateToConditionString(
+ $subjectName,
+ $propertyName,
+ $objectName,
+ $innerCondition
+ );
+
+ $result = new WhereCondition( $condition, true, $namespaces );
+
+ // Record inner ordering variable if found
+ $result->orderVariables = $innerCondition->orderVariables;
+
+ if ( $innerOrderByProperty !== null && $innerCondition->orderByVariable !== '' ) {
+ $result->orderVariables[$property->getKey()] = $innerCondition->orderByVariable;
+ }
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $result,
+ $joinVariable,
+ $orderByProperty,
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ return $result;
+ }
+
+ private function doResolveInnerConditionRecursively( DIProperty $property, Description $description ) {
+
+ $innerOrderByProperty = null;
+
+ // Find out if we should order by the values of this property
+ if ( array_key_exists( $property->getKey(), $this->conditionBuilder->getSortKeys() ) ) {
+ $innerOrderByProperty = $property;
+ }
+
+ // Prepare inner condition
+ $innerJoinVariable = $this->conditionBuilder->getNextVariable();
+
+ $this->conditionBuilder->setJoinVariable( $innerJoinVariable );
+ $this->conditionBuilder->setOrderByProperty( $innerOrderByProperty );
+
+ $innerCondition = $this->conditionBuilder->mapDescriptionToCondition(
+ $description
+ );
+
+ return [ $innerOrderByProperty, $innerCondition, $innerJoinVariable ];
+ }
+
+ private function findObjectNameFromInnerCondition( $innerCondition, $innerJoinVariable, &$namespaces ) {
+
+ if ( !$innerCondition instanceof SingletonCondition ) {
+ return '?' . $innerJoinVariable;
+ }
+
+ $matchElement = $innerCondition->matchElement;
+
+ if ( $matchElement instanceof ExpElement ) {
+ $objectName = TurtleSerializer::getTurtleNameForExpElement( $matchElement );
+ } else {
+ $objectName = $matchElement;
+ }
+
+ if ( $matchElement instanceof ExpNsResource ) {
+ $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
+ }
+
+ return $objectName;
+ }
+
+ private function findMostSuitablePropertyRepresentation( DIProperty $property, DIProperty $nonInverseProperty, &$namespaces ) {
+
+ $redirectByVariable = $this->conditionBuilder->tryToFindRedirectVariableForDataItem(
+ $nonInverseProperty->getDiWikiPage()
+ );
+
+ // If the property is represented by a redirect then use the variable instead
+ if ( $redirectByVariable !== null ) {
+ return $redirectByVariable;
+ }
+
+ // Use helper properties in encoding values, refer to this helper property:
+ if ( $this->exporter->hasHelperExpElement( $property ) ) {
+ $propertyExpElement = $this->exporter->getResourceElementForProperty( $nonInverseProperty, true );
+ } elseif( !$property->isUserDefined() ) {
+ $propertyExpElement = $this->exporter->getSpecialPropertyResource(
+ $nonInverseProperty->getKey(),
+ SMW_NS_PROPERTY
+ );
+ } else {
+ $propertyExpElement = $this->exporter->getResourceElementForProperty( $nonInverseProperty );
+ }
+
+ if ( $propertyExpElement instanceof ExpNsResource ) {
+ $namespaces[$propertyExpElement->getNamespaceId()] = $propertyExpElement->getNamespace();
+ }
+
+ return TurtleSerializer::getTurtleNameForExpElement( $propertyExpElement );
+ }
+
+ private function doExchangeForWhenInversePropertyIsUsed( DIProperty $property, $objectName, $joinVariable ) {
+
+ $subjectName = '?' . $joinVariable;
+ $nonInverseProperty = $property;
+
+ // Exchange arguments when property is inverse
+ // don't check if this really makes sense
+ if ( $property->isInverse() ) {
+ $subjectName = $objectName;
+ $objectName = '?' . $joinVariable;
+ $nonInverseProperty = new DIProperty( $property->getKey(), false );
+ }
+
+ return [ $subjectName, $objectName, $nonInverseProperty ];
+ }
+
+ private function concatenateToConditionString( $subjectName, $propertyName, $objectName, $innerCondition ) {
+
+ $condition = "$subjectName $propertyName $objectName .\n";
+
+ $innerConditionString = $innerCondition->getCondition() . $innerCondition->getWeakConditionString();
+
+ if ( $innerConditionString === '' ) {
+ return $condition;
+ }
+
+ if ( $innerCondition instanceof FilterCondition ) {
+ return $condition . $innerConditionString;
+ }
+
+ return $condition . "{ $innerConditionString}\n";
+ }
+
+ /**
+ * @note rdfs:subPropertyOf* where * means a property path of arbitrary length
+ * can be found using the "zero or more" will resolve the complete path
+ *
+ * @see http://www.w3.org/TR/sparql11-query/#propertypath-arbitrary-length
+ */
+ private function tryToAddPropertyPathForSaturatedHierarchy( &$condition, DIProperty $property, &$propertyName, $depth ) {
+
+ if ( !$this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_SUBP ) || !$property->isUserDefined() || ( $depth !== null && $depth < 1 ) ) {
+ return null;
+ }
+
+ if ( $this->conditionBuilder->getHierarchyLookup() == null || !$this->conditionBuilder->getHierarchyLookup()->hasSubproperty( $property ) ) {
+ return null;
+ }
+
+ $subPropExpElement = $this->exporter->getSpecialPropertyResource( '_SUBP', SMW_NS_PROPERTY );
+
+ // A discret depth other than 0 or 1 is difficult to achieve
+ // @see https://stackoverflow.com/questions/18126949/limit-the-sparql-query-result-to-first-level-in-hierarchy
+ // Path operator is defined as:
+ // - elt* ZeroOrMorePath
+ // - elt? ZeroOrOnePath
+ $pathOp = $depth > 1 || $depth === null ? '*' : '?';
+
+ $propertyByVariable = '?' . $this->conditionBuilder->getNextVariable( 'sp' );
+ $condition->weakConditions[$propertyName] = "\n". "$propertyByVariable " . $subPropExpElement->getQName() . "$pathOp $propertyName .\n"."";
+ $propertyName = $propertyByVariable;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php
new file mode 100644
index 00000000..4ca00f6a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\Query\Language\ThingDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWExporter as Exporter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ThingDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ThingDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+ return $this->conditionBuilder->newTrueCondition(
+ $this->conditionBuilder->getJoinVariable(),
+ $this->conditionBuilder->getOrderByProperty()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
new file mode 100644
index 00000000..0f85bf25
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIWikiPage;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreter;
+use SMWDIBlob as DIBlob;
+use SMWDIUri as DIUri;
+use SMWExpElement as ExpElement;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class ValueDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var Exporter
+ */
+ private $exporter;
+
+ /**
+ * @since 2.1
+ *
+ * @param ConditionBuilder|null $conditionBuilder
+ */
+ public function __construct( ConditionBuilder $conditionBuilder = null ) {
+ $this->conditionBuilder = $conditionBuilder;
+ $this->exporter = Exporter::getInstance();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ValueDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * {@inheritDoc}
+ */
+ public function interpretDescription( Description $description ) {
+
+ $joinVariable = $this->conditionBuilder->getJoinVariable();
+ $orderByProperty = $this->conditionBuilder->getOrderByProperty();
+ $asNoCase = $this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_NOCASE );
+
+ $dataItem = $description->getDataItem();
+ $property = $description->getProperty();
+
+ switch ( $description->getComparator() ) {
+ case SMW_CMP_EQ: $comparator = '=';
+ break;
+ case SMW_CMP_LESS: $comparator = '<';
+ break;
+ case SMW_CMP_GRTR: $comparator = '>';
+ break;
+ case SMW_CMP_LEQ: $comparator = '<=';
+ break;
+ case SMW_CMP_GEQ: $comparator = '>=';
+ break;
+ case SMW_CMP_NEQ: $comparator = '!=';
+ break;
+ case SMW_CMP_PRIM_LIKE;
+ case SMW_CMP_LIKE: $comparator = 'regex';
+ break;
+ case SMW_CMP_PRIM_NLKE;
+ case SMW_CMP_NLKE: $comparator = '!regex';
+ break;
+ default: $comparator = ''; // unkown, unsupported
+ }
+
+ if ( $comparator === '' ) {
+ return $this->createConditionForEmptyComparator( $joinVariable, $orderByProperty );
+ } elseif ( $comparator == '=' && $asNoCase === false ) {
+ return $this->createConditionForEqualityComparator( $dataItem, $property, $joinVariable, $orderByProperty );
+ } elseif ( $comparator == 'regex' || $comparator == '!regex' ) {
+ return $this->createConditionForRegexComparator( $dataItem, $joinVariable, $orderByProperty, $comparator );
+ }
+
+ return $this->createFilterConditionForAnyOtherComparator(
+ $dataItem,
+ $joinVariable,
+ $orderByProperty,
+ $comparator
+ );
+ }
+
+ private function createConditionForEmptyComparator( $joinVariable, $orderByProperty ) {
+ return $this->conditionBuilder->newTrueCondition( $joinVariable, $orderByProperty );
+ }
+
+ private function createConditionForEqualityComparator( $dataItem, $property, $joinVariable, $orderByProperty ) {
+
+ $expElement = $this->exporter->getDataItemHelperExpElement( $dataItem );
+
+ if ( $expElement === null ) {
+ $expElement = $this->exporter->getDataItemExpElement( $dataItem );
+ }
+
+ if ( $expElement === null || !$expElement instanceof ExpElement ) {
+ return new FalseCondition();
+ }
+
+ $condition = new SingletonCondition( $expElement );
+
+ $redirectByVariable = $this->conditionBuilder->tryToFindRedirectVariableForDataItem(
+ $dataItem
+ );
+
+ // If it is a standalone value (e.g [[:Foo]] with no property) construct a
+ // filter condition otherwise just assign the variable and the succeeding
+ // process the ensure the replacement
+ if ( $redirectByVariable !== null && $property === null ) {
+
+ $condition = $this->createFilterConditionForAnyOtherComparator(
+ $dataItem,
+ $joinVariable,
+ $orderByProperty,
+ '='
+ );
+
+ $condition->filter = "?$joinVariable = $redirectByVariable";
+ } elseif ( $redirectByVariable !== null ) {
+ $condition->matchElement = $redirectByVariable;
+ }
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $condition,
+ $joinVariable,
+ $orderByProperty,
+ $dataItem->getDIType()
+ );
+
+ return $condition;
+ }
+
+ private function createConditionForRegexComparator( $dataItem, $joinVariable, $orderByProperty, $comparator ) {
+
+ if ( !$dataItem instanceof DIBlob && !$dataItem instanceof DIWikiPage && !$dataItem instanceof DIUri ) {
+ return $this->conditionBuilder->newTrueCondition( $joinVariable, $orderByProperty );
+ }
+
+ if ( $dataItem instanceof DIBlob ) {
+ $search = $dataItem->getString();
+ } else {
+ $search = $dataItem->getSortKey();
+ }
+
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ $pattern = '^' . str_replace(
+ [ 'https://', 'http://', '%2A', '.', '+', '{', '}', '(', ')', '|', '^', '$', '[', ']', '*', '?', "'", '\\\.', '\\', '"', '\\\\\\\"' ],
+ [ '*', '*', '*', '\.', '\+', '\{', '\}', '\(', '\)', '\|', '\^', '\$', '\[', '\]', '.*', '.' , "\'", '\\\\\.', '\\\\', '\\\\\"', '\\\\\\\\\\\"' ],
+ $search
+ ) . '$';
+ // @codingStandardsIgnoreEnd
+
+ $condition = $this->createFilterConditionToMatchRegexPattern(
+ $dataItem,
+ $joinVariable,
+ $comparator,
+ $pattern
+ );
+
+ $redirectByVariable = $this->conditionBuilder->tryToFindRedirectVariableForDataItem(
+ $dataItem
+ );
+
+ if ( $redirectByVariable !== null ) {
+ $condition->matchElement = $redirectByVariable;
+ }
+
+ $this->conditionBuilder->addOrderByDataForProperty(
+ $condition,
+ $joinVariable,
+ $orderByProperty,
+ $dataItem->getDIType()
+ );
+
+ return $condition;
+ }
+
+ private function createFilterConditionForAnyOtherComparator( $dataItem, $joinVariable, $orderByProperty, $comparator ) {
+
+ $result = new FilterCondition( '', [] );
+
+ $this->conditionBuilder->addOrderByData(
+ $result,
+ $joinVariable,
+ $dataItem->getDIType()
+ );
+
+ $orderByVariable = '?' . $result->orderByVariable;
+
+ if ( $dataItem instanceof DIWikiPage ) {
+ $expElement = $this->exporter->getDataItemExpElement( new DIBlob( $dataItem->getSortKey() ) );
+ } else {
+ $expElement = $this->exporter->getDataItemHelperExpElement( $dataItem );
+ if ( is_null( $expElement ) ) {
+ $expElement = $this->exporter->getDataItemExpElement( $dataItem );
+ }
+ }
+
+ $valueName = TurtleSerializer::getTurtleNameForExpElement( $expElement );
+
+ if ( $expElement instanceof ExpNsResource ) {
+ $result->namespaces[$expElement->getNamespaceId()] = $expElement->getNamespace();
+ $dataItem = $expElement->getDataItem();
+ }
+
+ $this->lcase( $dataItem, $orderByVariable, $valueName );
+
+ $result->filter = "$orderByVariable $comparator $valueName";
+
+ return $result;
+ }
+
+ private function createFilterConditionToMatchRegexPattern( $dataItem, &$joinVariable, $comparator, $pattern ) {
+
+ $flag = $this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_NOCASE ) ? 'i' : 's';
+
+ if ( $dataItem instanceof DIBlob ) {
+ return new FilterCondition( "$comparator( ?$joinVariable, \"$pattern\", \"$flag\")", [] );
+ }
+
+ if ( $dataItem instanceof DIUri ) {
+ return new FilterCondition( "$comparator( str( ?$joinVariable ), \"$pattern\", \"i\")", [] );
+ }
+
+ // Pattern search for a wikipage object can only be done on the sortkey
+ // literal and not on it's resource
+ $skeyExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_SKEY' );
+
+ $expElement = $this->exporter->getDataItemExpElement( $dataItem->getSortKeyDataItem() );
+ $condition = new SingletonCondition( $expElement );
+
+ $filterVariable = $this->conditionBuilder->getNextVariable();
+
+ $condition->condition = "?$joinVariable " . $skeyExpElement->getQName(). " ?$filterVariable .\n";
+ $condition->matchElement = "?$joinVariable";
+
+ $filterCondition = new FilterCondition( "$comparator( ?$filterVariable, \"$pattern\", \"$flag\")", [] );
+
+ $condition->weakConditions = [ $filterVariable => $filterCondition->getCondition() ];
+
+ return $condition;
+ }
+
+ private function lcase( $dataItem, &$orderByVariable, &$valueName ) {
+
+ $isValidDataItem = $dataItem instanceof DIBlob || $dataItem instanceof DIUri || $dataItem instanceof DIWikiPage;
+
+ // https://stackoverflow.com/questions/10660030/how-to-write-sparql-query-that-efficiently-matches-string-literals-while-ignorin
+ if ( $this->conditionBuilder->isSetFlag( SMW_SPARQL_QF_NOCASE ) && $isValidDataItem ) {
+ $orderByVariable = "lcase(str($orderByVariable) )";
+ $valueName = mb_strtolower( $valueName );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/EngineOptions.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/EngineOptions.php
new file mode 100644
index 00000000..8fe65bdd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/EngineOptions.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use SMW\Options;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EngineOptions extends Options {
+
+ /**
+ * @since 2.2
+ */
+ public function __construct() {
+ parent::__construct( [
+ 'smwgIgnoreQueryErrors' => $GLOBALS['smwgIgnoreQueryErrors'],
+ 'smwgQSortFeatures' => $GLOBALS['smwgQSortFeatures'],
+ 'smwgQSubpropertyDepth' => $GLOBALS['smwgQSubpropertyDepth'],
+ 'smwgQSubcategoryDepth' => $GLOBALS['smwgQSubcategoryDepth'],
+ 'smwgSparqlQFeatures' => $GLOBALS['smwgSparqlQFeatures']
+ ] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php
new file mode 100644
index 00000000..046f4ce8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\Exporter\Element;
+use SMW\Query\DebugFormatter;
+use SMW\Query\Language\ThingDescription;
+use SMW\QueryEngine as QueryEngineInterface;
+use SMW\SPARQLStore\QueryEngine\Condition\Condition;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\RepositoryConnection;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * Class mapping SMWQuery objects to SPARQL, and for controlling the execution
+ * of these queries to obtain suitable QueryResult objects.
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class QueryEngine implements QueryEngineInterface {
+
+ /**
+ * The name of the SPARQL variable that represents the query result.
+ */
+ const RESULT_VARIABLE = 'result';
+
+ /**
+ * @var RepositoryConnection
+ */
+ private $connection;
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var QueryResultFactory
+ */
+ private $queryResultFactory;
+
+ /**
+ * @var EngineOptions
+ */
+ private $engineOptions;
+
+ /**
+ * @var array
+ */
+ private $sortKeys = [];
+
+ /**
+ * @since 2.0
+ *
+ * @param RepositoryConnection $connection
+ * @param ConditionBuilder $conditionBuilder
+ * @param QueryResultFactory $queryResultFactory
+ * @param EngineOptions|null $EngineOptions
+ */
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ public function __construct( RepositoryConnection $connection, ConditionBuilder $conditionBuilder, QueryResultFactory $queryResultFactory, EngineOptions $engineOptions = null ) {
+ // @codingStandardsIgnoreEnd
+ $this->connection = $connection;
+ $this->conditionBuilder = $conditionBuilder;
+ $this->queryResultFactory = $queryResultFactory;
+ $this->engineOptions = $engineOptions;
+
+ if ( $this->engineOptions === null ) {
+ $this->engineOptions = new EngineOptions();
+ }
+
+ $this->conditionBuilder->setResultVariable( self::RESULT_VARIABLE );
+ }
+
+ /**
+ * @since 2.0
+ * @param Query $query
+ *
+ * @return QueryResult|string
+ */
+ public function getQueryResult( Query $query ) {
+
+ if ( ( !$this->engineOptions->get( 'smwgIgnoreQueryErrors' ) || $query->getDescription() instanceof ThingDescription ) &&
+ $query->querymode != Query::MODE_DEBUG &&
+ count( $query->getErrors() ) > 0 ) {
+ return $this->queryResultFactory->newEmptyQueryResult( $query, false );
+ }
+
+ // don't query, but return something to the printer
+ if ( $query->querymode == Query::MODE_NONE || $query->getLimit() < 1 ) {
+ return $this->queryResultFactory->newEmptyQueryResult( $query, true );
+ }
+
+ $this->sortKeys = $query->sortkeys;
+ $this->conditionBuilder->setSortKeys( $this->sortKeys );
+
+ $compoundCondition = $this->conditionBuilder->getConditionFrom(
+ $query->getDescription()
+ );
+
+ $query->addErrors(
+ $this->conditionBuilder->getErrors()
+ );
+
+ if ( $query->querymode == Query::MODE_DEBUG ) {
+ return $this->getDebugQueryResult( $query, $compoundCondition );
+ } elseif ( $query->querymode == Query::MODE_COUNT ) {
+ return $this->getCountQueryResult( $query, $compoundCondition );
+ }
+
+ return $this->getInstanceQueryResult( $query, $compoundCondition );
+ }
+
+ private function getCountQueryResult( Query $query, Condition $compoundCondition ) {
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ return 1;
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $askQueryResult = $this->connection->ask( $condition, $namespaces );
+
+ return $askQueryResult->isBooleanTrue() ? 1 : 0;
+ }
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ return 0;
+ }
+
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $repositoryResult = $this->connection->selectCount(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+
+ return $this->queryResultFactory->newQueryResult( $repositoryResult, $query );
+ }
+
+ private function getInstanceQueryResult( Query $query, Condition $compoundCondition ) {
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ $matchElement = $compoundCondition->matchElement;
+
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ $results = [ [ $matchElement ] ];
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $askQueryResult = $this->connection->ask( $condition, $namespaces );
+ $results = $askQueryResult->isBooleanTrue() ? [ [ $matchElement ] ] : [];
+ }
+
+ $repositoryResult = new RepositoryResult( [ self::RESULT_VARIABLE => 0 ], $results );
+
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ $repositoryResult = new RepositoryResult( [ self::RESULT_VARIABLE => 0 ], [] );
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $repositoryResult = $this->connection->select(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+ }
+
+ return $this->queryResultFactory->newQueryResult( $repositoryResult, $query );
+ }
+
+ private function getDebugQueryResult( Query $query, Condition $compoundCondition ) {
+
+ $entries = [];
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ $sparql = 'None (no conditions).';
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $sparql = $this->connection->getSparqlForAsk( $condition, $namespaces );
+ }
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ $sparql = 'None (conditions can not be satisfied by anything).';
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $sparql = $this->connection->getSparqlForSelect(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+ }
+
+ $entries['SPARQL Query'] = DebugFormatter::prettifySparql( $sparql );
+
+ return DebugFormatter::getStringFrom( 'SPARQLStore', $entries, $query );
+ }
+
+ private function isSingletonConditionWithElementMatch( $condition ) {
+ return $condition instanceof SingletonCondition && $condition->matchElement instanceof Element;
+ }
+
+ /**
+ * Get a SPARQL option array for the given query.
+ *
+ * @param Query $query
+ * @param Condition $compoundCondition (storing order by variable names)
+ *
+ * @return array
+ */
+ protected function getOptions( Query $query, Condition $compoundCondition ) {
+
+ $options = [
+ 'LIMIT' => $query->getLimit() + 1,
+ 'OFFSET' => $query->getOffset()
+ ];
+
+ // Build ORDER BY options using discovered sorting fields.
+ if ( !$this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT ) || !is_array( $this->sortKeys ) ) {
+ return $options;
+ }
+
+ $orderByString = '';
+
+ foreach ( $this->sortKeys as $propkey => $order ) {
+
+ if ( !is_string( $propkey ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $compoundCondition->orderVariables ) ) {
+ $orderByString .= "$order(?" . $compoundCondition->orderVariables[$propkey] . ") ";
+ } elseif ( ( $order == 'RANDOM' ) && $this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT_RANDOM ) ) {
+ // not supported in SPARQL; might be possible via function calls in some stores
+ }
+ }
+
+ if ( $orderByString !== '' ) {
+ $options['ORDER BY'] = $orderByString;
+ }
+
+ return $options;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryResultFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryResultFactory.php
new file mode 100644
index 00000000..1c6c944d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryResultFactory.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use SMW\Exporter\Element\ExpElement;
+use SMW\Store;
+use SMWExporter as Exporter;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class QueryResultFactory {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Query $query QueryResults hold a reference to original query
+ * @param boolean $hasFurtherResults
+ *
+ * @return QueryResult
+ */
+ public function newEmptyQueryResult( Query $query, $hasFurtherResults = false ) {
+ return new QueryResult(
+ $query->getDescription()->getPrintrequests(),
+ $query,
+ [],
+ $this->store,
+ $hasFurtherResults
+ );
+ }
+
+ /**
+ * This function is used to generate instance query results, and the given
+ * result wrapper must have an according format (one result column that
+ * contains URIs of wiki pages).
+ *
+ * @param RepositoryResult|null $repositoryResult
+ * @param Query $query QueryResults hold a reference to original query
+ *
+ * @return QueryResult
+ */
+ public function newQueryResult( RepositoryResult $repositoryResult = null , Query $query ) {
+
+ if ( $repositoryResult === null ) {
+ return $this->newEmptyQueryResult( $query );
+ }
+
+ if ( $query->querymode === Query::MODE_COUNT ) {
+ return $this->makeQueryResultForCount( $repositoryResult, $query );
+ }
+
+ return $this->makeQueryResultForInstance( $repositoryResult, $query );
+ }
+
+ private function makeQueryResultForCount( RepositoryResult $repositoryResult, Query $query ) {
+
+ $queryResult = new QueryResult(
+ $query->getDescription()->getPrintrequests(),
+ $query,
+ [],
+ $this->store,
+ false
+ );
+
+ if ( $repositoryResult->getErrorCode() === RepositoryResult::ERROR_NOERROR ) {
+ $queryResult->setCountValue( $repositoryResult->getNumericValue() );
+ } else {
+ $queryResult->addErrors( [ wfMessage( 'smw_db_sparqlqueryproblem' )->inContentLanguage()->text() ] );
+ }
+
+ return $queryResult;
+ }
+
+ private function makeQueryResultForInstance( RepositoryResult $repositoryResult, Query $query ) {
+
+ $resultDataItems = [];
+
+ foreach ( $repositoryResult as $resultRow ) {
+
+ if ( count( $resultRow ) > 0 && $resultRow[0] instanceof ExpElement ) {
+ $dataItem = Exporter::getInstance()->findDataItemForExpElement( $resultRow[0] );
+
+ if ( !is_null( $dataItem ) ) {
+ $resultDataItems[] = $dataItem;
+ }
+ }
+ }
+
+ if ( $repositoryResult->numRows() > $query->getLimit() ) {
+ if ( count( $resultDataItems) > 1 ) {
+ array_pop( $resultDataItems );
+ }
+ $hasFurtherResults = true;
+ } else {
+ $hasFurtherResults = false;
+ }
+
+ $result = new QueryResult(
+ $query->getDescription()->getPrintrequests(),
+ $query,
+ $resultDataItems,
+ $this->store,
+ $hasFurtherResults
+ );
+
+ switch ( $repositoryResult->getErrorCode() ) {
+ case RepositoryResult::ERROR_NOERROR:
+ break;
+ case RepositoryResult::ERROR_INCOMPLETE:
+ $result->addErrors( [ wfMessage( 'smw_db_sparqlqueryincomplete' )->inContentLanguage()->text() ] );
+ break;
+ default:
+ $result->addErrors( [ wfMessage( 'smw_db_sparqlqueryproblem' )->inContentLanguage()->text() ] );
+ break;
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/RepositoryResult.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/RepositoryResult.php
new file mode 100644
index 00000000..db157cb4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/RepositoryResult.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use Iterator;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * Class for accessing SPARQL query results in a unified form. The data is
+ * structured in tabular form, with each cell containing some SMWExpElement.
+ * Rows should always have the same number of columns, but the datatype of the
+ * cells in each column may not be uniform throughout the result.
+ *
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class RepositoryResult implements Iterator {
+
+ /// Error code: no errors occurred.
+ const ERROR_NOERROR = 0;
+ /// Error code: service unreachable; result will be empty
+ const ERROR_UNREACHABLE = 1;
+ /// Error code: results might be incomplete (e.g. due to some resource limit being reached)
+ const ERROR_INCOMPLETE = 2;
+
+ /**
+ * Associative array mapping SPARQL variable names to column indices.
+ * @var array of integer
+ */
+ protected $header;
+
+ /**
+ * List of result rows. Individual entries can be null if a cell in the
+ * SPARQL result table is empty (this is different from finding a blank
+ * node).
+ * @var array of array of (SMWExpElement or null)
+ */
+ protected $data;
+
+ /**
+ * List of comment strings found in the XML file (without surrounding
+ * markup, i.e. the actual string only).
+ * @var array of string
+ */
+ protected $comments;
+
+ /**
+ * Error code.
+ * @var integer
+ */
+ protected $errorCode;
+
+ /**
+ * Initialise a result set from a result string in SPARQL XML format.
+ *
+ * @param $header array mapping SPARQL variable names to column indices
+ * @param $data array of array of (SMWExpElement or null)
+ * @param $comments array of string comments if the result contained any
+ * @param $errorCode integer an error code
+ */
+ public function __construct( array $header = [], array $data = [], array $comments = [], $errorCode = self::ERROR_NOERROR ) {
+ $this->header = $header;
+ $this->data = $data;
+ $this->comments = $comments;
+ $this->errorCode = $errorCode;
+ reset( $this->data );
+ }
+
+ /**
+ * Get the number of rows in the result object.
+ *
+ * @return integer number of result rows
+ */
+ public function numRows() {
+ return count( $this->data );
+ }
+
+ /**
+ * Return error code. SMWSparqlResultWrapper::ERROR_NOERROR (0)
+ * indicates that no error occurred.
+ *
+ * @return integer error code
+ */
+ public function getErrorCode() {
+ return $this->errorCode;
+ }
+
+ /**
+ * Set the error code of this result set. This is used for allowing
+ * callers to add additional errors discovered only later on. It does
+ * not allow removing existing errors, since it will not accept
+ * SMWSparqlResultWrapper::ERROR_NOERROR as a parameter.
+ *
+ * @param $errorCode integer error code
+ */
+ public function setErrorCode( $errorCode ) {
+ if ( $errorCode != self::ERROR_NOERROR ) {
+ $this->errorCode = $errorCode;
+ }
+ }
+
+ /**
+ * Return a list of comment strings found in the SPARQL result. Comments
+ * are used by some RDF stores to provide additional information or
+ * warnings that can thus be accessed.
+ *
+ * @return array of string
+ */
+ public function getComments() {
+ return $this->comments;
+ }
+
+ /**
+ * Check if the result is what one would get for a SPARQL ASK query
+ * that returned true. Returns false in all other cases (including
+ * the case that the results do not look at all like the result of
+ * an ASK query).
+ *
+ * @return boolean
+ */
+ public function isBooleanTrue() {
+ if ( count( $this->data ) == 1 ) {
+ $row = reset( $this->data );
+ $expElement = reset( $row );
+ if ( ( count( $row ) == 1 ) && ( $expElement instanceof ExpLiteral ) &&
+ ( $expElement->getLexicalForm() == 'true' ) &&
+ ( $expElement->getDatatype() == 'http://www.w3.org/2001/XMLSchema#boolean' ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if the result is what one would get for a SPARQL SELECT COUNT
+ * query, and return the corresponding integer value. Returns 0 in all
+ * other cases (including the case that the results do not look at all
+ * like the result of a SELECT COUNT query).
+ *
+ * @return integer
+ */
+ public function getNumericValue() {
+ if ( count( $this->data ) == 1 ) {
+ $row = reset( $this->data );
+ $expElement = reset( $row );
+ if ( ( count( $row ) == 1 ) && ( $expElement instanceof ExpLiteral ) &&
+ ( $expElement->getDatatype() == 'http://www.w3.org/2001/XMLSchema#integer' ) ) {
+ return (int)$expElement->getLexicalForm();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Reset iterator to position 0. Standard method of Iterator.
+ */
+ public function rewind() {
+ reset( $this->data );
+ }
+
+ /**
+ * Return the current result row. Standard method of Iterator.
+ *
+ * @return array of (SMWExpElement or null), or false at end of data
+ */
+ public function current() {
+ return current( $this->data );
+ }
+
+ /**
+ * Return the next result row and advance the internal pointer.
+ * Standard method of Iterator.
+ *
+ * @return array of (SMWExpElement or null), or false at end of data
+ */
+ public function next() {
+ return next( $this->data );
+ }
+
+ /**
+ * Return the next result row and advance the internal pointer.
+ * Standard method of Iterator.
+ *
+ * @return array of (SMWExpElement or null), or false at end of data
+ */
+ public function key() {
+ return key( $this->data );
+ }
+
+ /**
+ * Return true if the internal pointer refers to a valid element.
+ * Standard method of Iterator.
+ *
+ * @return boolean
+ */
+ public function valid() {
+ return ( current( $this->data ) !== false );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/XmlResponseParser.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/XmlResponseParser.php
new file mode 100644
index 00000000..24182051
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/XmlResponseParser.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\Exception\XmlParserException;
+use SMW\SPARQLStore\HttpResponseParser;
+use SMWExpLiteral as ExpLiteral;
+use SMWExpResource as ExpResource;
+
+/**
+ * Class for parsing SPARQL results in XML format
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class XmlResponseParser implements HttpResponseParser {
+
+ /**
+ * @var resource
+ */
+ private $parser;
+
+ /**
+ * Associative array mapping SPARQL variable names to column indices.
+ * @var array of integer
+ */
+ private $header;
+
+ /**
+ * List of result rows. Individual entries can be null if a cell in the
+ * SPARQL result table is empty (this is different from finding a blank
+ * node).
+ * @var array of array of (SMWExpElement or null)
+ */
+ private $data;
+
+ /**
+ * List of comment strings found in the XML file (without surrounding
+ * markup, i.e. the actual string only).
+ * @var array of string
+ */
+ private $comments;
+
+ /**
+ * Stack of open XML tags during parsing.
+ * @var array of string
+ */
+ private $xmlOpenTags;
+
+ /**
+ * Integer index of the column that the current result binding fills.
+ * @var integer
+ */
+ private $xmlBindIndex;
+
+ /**
+ * Datatype URI for the current literal, or empty if none.
+ * @var string
+ */
+ private $currentDataType;
+
+ /**
+ * @since 2.0
+ */
+ public function __construct() {
+ $this->parser = xml_parser_create();
+
+ xml_parser_set_option( $this->parser, XML_OPTION_SKIP_WHITE, 0 );
+ xml_parser_set_option( $this->parser, XML_OPTION_TARGET_ENCODING, 'UTF-8' );
+ xml_parser_set_option( $this->parser, XML_OPTION_CASE_FOLDING, 0 );
+ xml_set_object( $this->parser, $this );
+ xml_set_element_handler( $this->parser, 'handleOpenElement', 'handleCloseElement' );
+ xml_set_character_data_handler( $this->parser, 'handleCharacterData' );
+ xml_set_default_handler( $this->parser, 'handleDefault' );
+ //xml_set_start_namespace_decl_handler($parser, 'handleNsDeclaration' );
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function __destruct() {
+ xml_parser_free( $this->parser );
+ }
+
+ /**
+ * Parse the given XML result and return an RepositoryResult for
+ * the contained data.
+ *
+ * @param string $response
+ *
+ * @return RepositoryResult
+ * @throws XmlParserException
+ */
+ public function parse( $response ) {
+
+ $this->xmlOpenTags = [];
+ $this->header = [];
+ $this->data = [];
+ $this->comments = [];
+
+ // Sesame can return "" result
+ if ( $response === '' ) {
+ $this->data = [ [ new ExpLiteral( 'false', 'http://www.w3.org/2001/XMLSchema#boolean' ) ] ];
+ }
+
+ // #626 Virtuoso
+ if ( $response == 'true' ) {
+ $this->data = [ [ new ExpLiteral( 'true', 'http://www.w3.org/2001/XMLSchema#boolean' ) ] ];
+ }
+
+ // #474 Virtuoso allows `false` to be a valid raw result
+ if ( $response === '' || $response == 'false' || $response == 'true' || is_bool( $response ) || $this->parseXml( $response ) ) {
+ return new RepositoryResult(
+ $this->header,
+ $this->data,
+ $this->comments
+ );
+ }
+
+ throw new XmlParserException(
+ $this->getLastError(),
+ $this->getLastLineNumber(),
+ $this->getLastColumnNumber()
+ );
+ }
+
+ private function parseXml( $xmlResultData ) {
+ return xml_parse( $this->parser, $xmlResultData, true );
+ }
+
+ private function getLastError() {
+ return xml_error_string( xml_get_error_code( $this->parser ) );
+ }
+
+ private function getLastLineNumber() {
+ return xml_get_current_line_number( $this->parser );
+ }
+
+ private function getLastColumnNumber() {
+ return xml_get_current_column_number ( $this->parser );
+ }
+
+ private function handleDefault( $parser, $data ) {
+ if ( substr( $data, 0, 4 ) == '<!--' ) {
+ $comment = substr( $data, 4, strlen( $data ) - 7 );
+ $this->comments[] = trim( $comment );
+ }
+ }
+
+ /**
+ * @see xml_set_element_handler
+ */
+ private function handleOpenElement( $parser, $elementTag, $attributes ) {
+
+ $this->currentDataType = '';
+
+ $prevTag = end( $this->xmlOpenTags );
+ $this->xmlOpenTags[] = $elementTag;
+
+ switch ( $elementTag ) {
+ case 'binding' && ( $prevTag == 'result' ):
+ if ( ( array_key_exists( 'name', $attributes ) ) &&
+ ( array_key_exists( $attributes['name'], $this->header ) ) ) {
+ $this->xmlBindIndex = $this->header[$attributes['name']];
+ }
+ break;
+ case 'result' && ( $prevTag == 'results' ):
+ $this->data[] = array_fill( 0, count( $this->header ), null );
+ break;
+ case 'literal' && ( $prevTag == 'binding' ):
+ if ( array_key_exists( 'datatype', $attributes ) ) {
+ $this->currentDataType = $attributes['datatype'];
+ }
+ /// TODO handle xml:lang attributes here as well?
+ break;
+ case 'variable' && ( $prevTag == 'head' ):
+ if ( array_key_exists( 'name', $attributes ) ) {
+ $this->header[$attributes['name']] = count( $this->header );
+ }
+ break;
+ }
+ }
+
+ /**
+ * @see xml_set_element_handler
+ */
+ private function handleCloseElement( $parser, $elementTag ) {
+ array_pop( $this->xmlOpenTags );
+ }
+
+ /**
+ * @see xml_set_character_data_handler
+ */
+ private function handleCharacterData( $parser, $characterData ) {
+
+ $prevTag = end( $this->xmlOpenTags );
+ $rowcount = count( $this->data ) - 1;
+
+ // UTF-8 is being split therefore concatenate the string (use row as indicator
+ // to detect a sliced string)
+ if ( isset( $this->data[$rowcount] ) && ( $element = end( $this->data[$rowcount] ) ) !== null ) {
+ switch ( $prevTag ) {
+ case 'uri':
+ $characterData = $element->getUri() . $characterData;
+ break;
+ case 'literal':
+ $characterData = $element->getLexicalForm() . $characterData;
+ }
+ }
+
+ switch ( $prevTag ) {
+ case 'uri':
+ $this->data[$rowcount][$this->xmlBindIndex] = new ExpResource( $characterData );
+ break;
+ case 'literal':
+ $this->data[$rowcount][$this->xmlBindIndex] = new ExpLiteral( $characterData, $this->currentDataType );
+ break;
+ case 'bnode':
+ $this->data[$rowcount][$this->xmlBindIndex] = new ExpResource( '_' . $characterData );
+ break;
+ case 'boolean':
+ // no "results" in this case
+ $literal = new ExpLiteral( $characterData, 'http://www.w3.org/2001/XMLSchema#boolean' );
+
+ // ?? Really !!
+ $this->data = [ [ $literal ] ];
+ $this->header = [ '' => 0 ];
+ break;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/README.md b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/README.md
new file mode 100644
index 00000000..ec7da3d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/README.md
@@ -0,0 +1,101 @@
+# SPARQLStore
+
+The `SPARQLStore` is the name for the component that can establish a connection between a [RDF triple store][tdb] and Semantic MediaWiki (a more general introduction can be found [here](https://www.semantic-mediawiki.org/wiki/Help:Using SPARQL and RDF stores)).
+
+The `SPARQLStore` is composed of a base store (by default using the existing `SQLStore`), a `QueryEngine`, and a connector to the RDF back-end. Currently, the base store takes the position of accumulating information about properties, value annotations, and statistics.
+
+## Overview
+
+```
+ SPARQLStore
+ |- SPARQLStoreFactory
+ |- ConnectionManager
+ | |- RepositoryConnectionProvider
+ | |- RepositoryClient
+ | |- RepositoryConnection
+ | |- FourstoreRepositoryConnector
+ | |- FusekiRepositoryConnector
+ | |- GenericRepositoryConnector
+ | |- VirtuosoRepositoryConnector
+ |- TurtleTriplesBuilder
+ |- RepositoryRedirectLookup
+ |- ReplicationDataTruncator
+ |- QueryEngine
+ |- HttpResponseParser
+ |- XmlResponseParser
+ |- ConditionBuilder
+ |- DescriptionInterpreter
+```
+
+## Repository connector
+
+A repository connector is responsible for establishing a communication between Semantic MediaWiki and an external [TDB][tdb] with the main objective to transfer/update triples from SMW to the back-end and to return result matches for a query request.
+
+The following client repositories have been tested:
+
+- [Jena Fuseki][fuseki]
+- [Virtuoso][virtuoso]
+- [Blazegraph][blazegraph]
+- [Sesame][sesame]
+- [4Store][4store]
+
+### Create a connection
+<pre>
+$connectionManager = new ConnectionManager();
+
+$connectionManager->registerConnectionProvider(
+ 'sparql',
+ new RepositoryConnectionProvider( 'fuseki' )
+);
+
+$connection = $connectionManager->getConnection( 'sparql' )
+</pre>
+
+## QueryEngine
+
+The `QueryEngine` is responsible for transforming an `#ask` description object into a qualified
+[`SPARQL` query][sparql-query] expression.
+
+- The `ConditionBuilder` builds a SPARQL condition from an `#ask` query artefact (aka [`Description`][ask query] object)
+- The condition is transformed into a qualified `SPARQL` statement for which the [repository connector][connector] is making a http request to the back-end while awaiting an expected list of subjects that matched the condition in form of a `XML` or `JSON` response
+- The raw results are being parsed by a `HttpResponseParser` to provide a unified `RepositoryResult` object
+- During the final step, the `QueryResultFactory` converts the `RepositoryResult` into a SMW specific `QueryResult` object which will fetch the remaining data (those selected as printrequests) from the base store and make them available to a [`QueryResultPrinter`][resultprinter]
+
+### Create a query request
+<pre>
+/**
+ * Equivalent to [[Foo::+]]
+ *
+ * SELECT DISTINCT ?result WHERE {
+ * ?result swivt:wikiPageSortKey ?resultsk .
+ * ?result property:Foo ?v1 .
+ * }
+ * ORDER BY ASC(?resultsk)
+ */
+$description = new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+);
+
+$query = new Query( $description );
+
+$sparqlStoreFactory = new SPARQLStoreFactory(
+ new SPARQLStore()
+);
+
+$queryEngine = $sparqlStoreFactory->newMasterQueryEngine();
+$queryResult = $queryEngine->getQueryResult( $query );
+</pre>
+
+[fuseki]: https://jena.apache.org/
+[fuseki-dataset]: https://jena.apache.org/documentation/tdb/dynamic_datasets.html
+[sparql-query]:http://www.w3.org/TR/sparql11-query/
+[sparql-dataset]: https://www.w3.org/TR/sparql11-query/#specifyingDataset
+[virtuoso]: https://github.com/openlink/virtuoso-opensource
+[4store]: https://github.com/garlik/4store
+[tdb]: http://en.wikipedia.org/wiki/Triplestore
+[sesame]: http://rdf4j.org/
+[blazegraph]: https://wiki.blazegraph.com/wiki/index.php/Main_Page
+[ask query]: https://www.semantic-mediawiki.org/wiki/Query_language
+[connector]: https://www.semantic-mediawiki.org/wiki/Help:SPARQLStore/RepositoryConnector
+[resultprinter]: https://www.semantic-mediawiki.org/wiki/Help:SPARQLStore/RepositoryConnector
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/ReplicationDataTruncator.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/ReplicationDataTruncator.php
new file mode 100644
index 00000000..a6d6d97c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/ReplicationDataTruncator.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use SMW\DIProperty;
+use SMW\SemanticData;
+
+/**
+ * Truncate a SemanticData instance for the replication process
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReplicationDataTruncator {
+
+ /**
+ * @var array
+ */
+ private $propertyExemptionList = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyExemptionList
+ */
+ public function setPropertyExemptionList( array $propertyExemptionList ) {
+ $this->propertyExemptionList = str_replace( ' ', '_', $propertyExemptionList );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticDat
+ *
+ * @return SemanticData
+ */
+ public function doTruncate( SemanticData $semanticData ) {
+
+ if ( $this->propertyExemptionList === [] ) {
+ return $semanticData;
+ }
+
+ foreach ( $this->propertyExemptionList as $property ) {
+ $semanticData->removeProperty( DIProperty::newFromUserLabel( $property ) );
+ }
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryClient.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryClient.php
new file mode 100644
index 00000000..c3ab27ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryClient.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+/**
+ * Provides information about the client and how to communicate with
+ * its services
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class RepositoryClient {
+
+ /**
+ * The URI of the default graph that is used to store data.
+ * Can be the empty string to omit this information in all requests
+ * (not supported by all stores).
+ *
+ * @var string
+ */
+ private $defaultGraph = '';
+
+ /**
+ * The URL of the endpoint for executing read queries.
+ *
+ * @var string
+ */
+ private $queryEndpoint = '';
+
+ /**
+ * The URL of the endpoint for executing update queries, or empty if
+ * update is not allowed/supported.
+ *
+ * @var string
+ */
+ private $updateEndpoint = '';
+
+ /**
+ * The URL of the endpoint for using the SPARQL Graph Store HTTP
+ * Protocol with, or empty if this method is not allowed/supported.
+ *
+ * @var string
+ */
+ private $dataEndpoint = '';
+
+ /**
+ * @var string
+ */
+ private $name = '';
+
+ /**
+ * @since 2.2
+ *
+ * @param string $defaultGraph
+ * @param string $queryEndpoint
+ * @param string $updateEndpoint
+ * @param string $dataEndpoint
+ */
+ public function __construct( $defaultGraph, $queryEndpoint, $updateEndpoint = '', $dataEndpoint = '' ) {
+ $this->defaultGraph = $defaultGraph;
+ $this->queryEndpoint = $queryEndpoint;
+ $this->updateEndpoint = $updateEndpoint;
+ $this->dataEndpoint = $dataEndpoint;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ */
+ public function setName( $name ) {
+ $this->name = $name;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getDefaultGraph() {
+ return $this->defaultGraph;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string|false
+ */
+ public function getQueryEndpoint() {
+ return $this->queryEndpoint;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getUpdateEndpoint() {
+ return $this->updateEndpoint;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getDataEndpoint() {
+ return $this->dataEndpoint;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnection.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnection.php
new file mode 100644
index 00000000..84b1ae22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnection.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+interface RepositoryConnection {
+
+ /**
+ * The function returns connection details required for establishing an active
+ * repository connection.
+ *
+ * @since 2.5
+ *
+ * @return RepositoryClient
+ */
+ public function getRepositoryClient();
+
+ /**
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $vars mixed array or string, field name(s) to be retrieved, can be '*'
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $options array (associative) of options, e.g. array( 'LIMIT' => '10' )
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return RepositoryResult
+ */
+ public function select( $vars, $where, $options = [], $extraNamespaces = [] );
+
+ /**
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return RepositoryResult
+ */
+ public function ask( $where, $extraNamespaces = [] );
+
+ /**
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $deletePattern string CONSTRUCT pattern of tripples to delete
+ * @param $where string condition for data to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function delete( $deletePattern, $where, $extraNamespaces = [] );
+
+ /**
+ * Execute a SPARQL query and return an RepositoryResult object
+ * that contains the results. The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then an empty result with an error
+ * code is returned.
+ *
+ * @note This function sets the graph that is to be used as part of the
+ * request. Queries should not include additional graph information.
+ *
+ * @param string $sparql complete SPARQL query (SELECT or ASK)
+ *
+ * @return RepositoryResult
+ */
+ public function doQuery( $sparql );
+
+ /**
+ * Execute a SPARQL update and return a boolean to indicate if the
+ * operations was successful. The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then false is returned.
+ *
+ * @note When this is written, it is not clear if the update protocol
+ * supports a default-graph-uri parameter. Hence the target graph for
+ * all updates is generally encoded in the query string and not fixed
+ * when sending the query. Direct callers to this function must include
+ * the graph information in the queries that they build.
+ *
+ * @param string $sparql complete SPARQL update query (INSERT or DELETE)
+ *
+ * @return boolean
+ */
+ public function doUpdate( $sparql );
+
+ /**
+ * Execute a HTTP-based SPARQL POST request according to
+ * http://www.w3.org/2009/sparql/docs/http-rdf-update/.
+ * The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then an empty result with an error
+ * code is returned.
+ *
+ * @note This protocol is not part of the SPARQL standard and may not
+ * be supported by all stores. To avoid using it, simply do not provide
+ * a data endpoint URL when configuring the SPARQL database. If used,
+ * the protocol might lead to a better performance since there is less
+ * parsing required to fetch the data from the request.
+ * @note Some stores (e.g. 4Store) support another mode of posting data
+ * that may be implemented in a special database handler.
+ *
+ * @param string $payload Turtle serialization of data to send
+ *
+ * @return boolean
+ */
+ public function doHttpPost( $payload );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectionProvider.php
new file mode 100644
index 00000000..c7d6984d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectionProvider.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use Onoi\HttpRequest\CurlRequest;
+use RuntimeException;
+use SMW\Connection\ConnectionProvider;
+use SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector;
+use SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector;
+use SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector;
+use SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector;
+
+/**
+ * @private
+ *
+ * Provides a RepositoryConnection on the available settings.
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryConnectionProvider implements ConnectionProvider {
+
+ /**
+ * List of supported standard connectors
+ *
+ * @var array
+ */
+ private $repositoryConnectors = [
+ 'default' => GenericRepositoryConnector::class,
+ 'generic' => GenericRepositoryConnector::class,
+ 'sesame' => GenericRepositoryConnector::class,
+ 'fuseki' => FusekiRepositoryConnector::class,
+ 'virtuoso' => VirtuosoRepositoryConnector::class,
+ '4store' => FourstoreRepositoryConnector::class,
+ ];
+
+ /**
+ * @var RepositoryConnection
+ */
+ private $connection = null;
+
+ /**
+ * @var string|null
+ */
+ private $connectorId = null;
+
+ /**
+ * @var string|null
+ */
+ private $defaultGraph = null;
+
+ /**
+ * @var string|null
+ */
+ private $queryEndpoint = null;
+
+ /**
+ * @var string|null
+ */
+ private $updateEndpoint = null;
+
+ /**
+ * @var string|null
+ */
+ private $dataEndpoint = null;
+
+ /**
+ * @var HttpRequest
+ */
+ private $httpRequest;
+
+ /**
+ * @var boolean|integer
+ */
+ private $httpVersion = false;
+
+ /**
+ * @since 2.0
+ *
+ * @param string|null $connectorId
+ * @param string|null $defaultGraph
+ * @param string|null $queryEndpoint
+ * @param string|null $updateEndpoint
+ * @param string|null $dataEndpoint
+ */
+ public function __construct( $connectorId = null, $defaultGraph = null, $queryEndpoint = null, $updateEndpoint = null, $dataEndpoint = null ) {
+ $this->connectorId = $connectorId;
+ $this->defaultGraph = $defaultGraph;
+ $this->queryEndpoint = $queryEndpoint;
+ $this->updateEndpoint = $updateEndpoint;
+ $this->dataEndpoint = $dataEndpoint;
+
+ if ( $this->connectorId === null ) {
+ $this->connectorId = $GLOBALS['smwgSparqlRepositoryConnector'];
+ }
+
+ if ( $this->defaultGraph === null ) {
+ $this->defaultGraph = $GLOBALS['smwgSparqlDefaultGraph'];
+ }
+
+ if ( $this->queryEndpoint === null ) {
+ $this->queryEndpoint = $GLOBALS['smwgSparqlEndpoint']['query'];
+ }
+
+ if ( $this->updateEndpoint === null ) {
+ $this->updateEndpoint = $GLOBALS['smwgSparqlEndpoint']['update'];
+ }
+
+ if ( $this->dataEndpoint === null ) {
+ $this->dataEndpoint = $GLOBALS['smwgSparqlEndpoint']['data'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return HttpRequest $httpRequest
+ */
+ public function setHttpRequest( HttpRequest $httpRequest ) {
+ $this->httpRequest = $httpRequest;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return integer $httpVersion
+ */
+ public function setHttpVersionTo( $httpVersion ) {
+ $this->httpVersion = $httpVersion;
+ }
+
+ /**
+ * @see ConnectionProvider::getConnection
+ *
+ * @since 2.0
+ *
+ * @return SparqlDatabase
+ * @throws RuntimeException
+ */
+ public function getConnection() {
+
+ if ( $this->connection === null ) {
+ $this->connection = $this->connectTo( strtolower( $this->connectorId ) );
+ }
+
+ return $this->connection;
+ }
+
+ /**
+ * @see ConnectionProvider::releaseConnection
+ *
+ * @since 2.0
+ */
+ public function releaseConnection() {
+ $this->connection = null;
+ }
+
+ private function connectTo( $id ) {
+
+ if ( $this->httpRequest === null ) {
+ $this->httpRequest = new CurlRequest( curl_init() );
+ }
+
+ // https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/1306
+ if ( $this->httpVersion ) {
+ $this->httpRequest->setOption( CURLOPT_HTTP_VERSION, $this->httpVersion );
+ }
+
+ $repositoryClient = new RepositoryClient(
+ $this->defaultGraph,
+ $this->queryEndpoint,
+ $this->updateEndpoint,
+ $this->dataEndpoint
+ );
+
+ $repositoryClient->setName( $id );
+
+ $repositoryConnector = $this->createRepositoryConnector(
+ $id,
+ $repositoryClient
+ );
+
+ if ( $this->isRepositoryConnection( $repositoryConnector ) ) {
+ return $repositoryConnector;
+ }
+
+ throw new RuntimeException( 'Expected a RepositoryConnection instance' );
+ }
+
+ private function createRepositoryConnector( $id, $repositoryClient ) {
+
+ $repositoryConnector = $this->repositoryConnectors['default'];
+
+ if ( isset( $this->repositoryConnectors[$id] ) ) {
+ $repositoryConnector = $this->repositoryConnectors[$id];
+ }
+
+ if ( $id === 'custom' ) {
+ $repositoryConnector = $GLOBALS['smwgSparqlCustomConnector'];
+ }
+
+ if ( !class_exists( $repositoryConnector ) ) {
+ throw new RuntimeException( "{$repositoryConnector} is not available" );
+ }
+
+ return new $repositoryConnector( $repositoryClient, $this->httpRequest );
+ }
+
+ private function isRepositoryConnection( $connection ) {
+ return $connection instanceof RepositoryConnection;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnector.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnector.php
new file mode 100644
index 00000000..a5a5bfaf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnector.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+use SMW\SPARQLStore\QueryEngine\RepositoryResult;
+use SMW\SPARQLStore\QueryEngine\XmlResponseParser;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * Specific modifications of the SPARQL database implementation for 4Store.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class FourstoreRepositoryConnector extends GenericRepositoryConnector {
+
+ /**
+ * Execute a SPARQL query and return an RepositoryResult object
+ * that contains the results. Compared to GenericHttpDatabaseConnector::doQuery(),
+ * this also supports the parameter "restricted=1" which 4Store provides
+ * to enforce strict resource bounds on query answering. The method also
+ * checks if these bounds have been met, and records this in the query
+ * result.
+ *
+ * @note The restricted option in 4Store mainly enforces the given soft
+ * limit more strictly. To disable/configure it, simply change the soft
+ * limit settings of your 4Store server.
+ *
+ * @param $sparql string with the complete SPARQL query (SELECT or ASK)
+ * @return RepositoryResult
+ */
+ public function doQuery( $sparql ) {
+
+ if ( $this->repositoryClient->getQueryEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, ['Accept: application/sparql-results+xml,application/xml;q=0.8' ]);
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $parameterString = "query=" . urlencode( $sparql ) . "&restricted=1" .
+ ( ( $defaultGraph !== '' )? '&default-graph-uri=' . urlencode( $defaultGraph ) : '' );
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+
+ $httpResponse = $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ $xmlResponseParser = new XmlResponseParser();
+ $result = $xmlResponseParser->parse( $httpResponse );
+ } else {
+ $this->mapHttpRequestError( $this->repositoryClient->getQueryEndpoint(), $sparql );
+ $result = new RepositoryResult();
+ $result->setErrorCode( RepositoryResult::ERROR_UNREACHABLE );
+ }
+
+ foreach ( $result->getComments() as $comment ) {
+ if ( strpos( $comment, 'warning: hit complexity limit' ) === 0 ||
+ strpos( $comment, 'some results have been dropped' ) === 0 ) {
+ $result->setErrorCode( RepositoryResult::ERROR_INCOMPLETE );
+ } //else debug_zval_dump($comment);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Complex SPARQL Update delete operations are not supported in 4Store
+ * as of v1.1.3, hence this implementation uses a less efficient method
+ * for accomplishing this.
+ *
+ * @param $propertyName string Turtle name of marking property
+ * @param $objectName string Turtle name of marking object/value
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @return boolean stating whether the operations succeeded
+ */
+ public function deleteContentByValue( $propertyName, $objectName, $extraNamespaces = [] ) {
+ $affectedObjects = $this->select( '*', "?s $propertyName $objectName", [], $extraNamespaces );
+ $success = ( $affectedObjects->getErrorCode() == RepositoryResult::ERROR_NOERROR );
+
+ foreach ( $affectedObjects as $expElements ) {
+ if ( count( $expElements ) > 0 ) {
+ $turtleName = TurtleSerializer::getTurtleNameForExpElement( reset( $expElements ) );
+ $success = $this->delete( "$turtleName ?p ?o", "$turtleName ?p ?o", $extraNamespaces ) && $success;
+ }
+ }
+
+ return $success;
+ }
+
+ /**
+ * Execute a HTTP-based SPARQL POST request according to
+ * http://www.w3.org/2009/sparql/docs/http-rdf-update/.
+ * The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then an empty result with an error
+ * code is returned.
+ *
+ * This method is specific to 4Store since it uses POST parameters that
+ * are not given in the specification.
+ *
+ * @param $payload string Turtle serialization of data to send
+ *
+ * @return boolean
+ */
+ public function doHttpPost( $payload ) {
+
+ if ( $this->repositoryClient->getDataEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, "SPARQL POST with data: $payload", 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getDataEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $parameterString = "data=" . urlencode( $payload ) . '&graph=' .
+ ( ( $defaultGraph !== '' )? urlencode( $defaultGraph ) : 'default' ) .
+ '&mime-type=application/x-turtle';
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getDataEndpoint(), $payload );
+ return false;
+ }
+
+ /**
+ * @see GenericHttpDatabaseConnector::doUpdate
+ *
+ * @note 4store 1.1.4 breaks on update if charset is set in the Content-Type header
+ *
+ * @since 2.0
+ */
+ public function doUpdate( $sparql ) {
+
+ if ( $this->repositoryClient->getUpdateEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $parameterString = "update=" . urlencode( $sparql );
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded' ] );
+
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getUpdateEndpoint(), $sparql );
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnector.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnector.php
new file mode 100644
index 00000000..1ffdc94b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnector.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+use SMW\SPARQLStore\QueryEngine\RepositoryResult;
+use SMW\SPARQLStore\QueryEngine\XmlResponseParser;
+
+/**
+ * @see https://jena.apache.org/documentation/serving_data/index.html
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FusekiRepositoryConnector extends GenericRepositoryConnector {
+
+ /**
+ * @see GenericRepositoryConnector::doQuery
+ */
+ public function doQuery( $sparql ) {
+
+ if ( $this->repositoryClient->getQueryEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() );
+
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, [
+ 'Accept: application/sparql-results+xml,application/xml;q=0.8',
+ 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
+ ] );
+
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $parameterString = "query=" . urlencode( $sparql ) .
+ ( ( $defaultGraph !== '' )? '&default-graph-uri=' . urlencode( $defaultGraph ) : '' ) . '&output=xml';
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+
+ $httpResponse = $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ $xmlResponseParser = new XmlResponseParser();
+ return $xmlResponseParser->parse( $httpResponse );
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getQueryEndpoint(), $sparql );
+
+ $repositoryResult = new RepositoryResult();
+ $repositoryResult->setErrorCode( RepositoryResult::ERROR_UNREACHABLE );
+
+ return $repositoryResult;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/GenericRepositoryConnector.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/GenericRepositoryConnector.php
new file mode 100644
index 00000000..76361a4c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/GenericRepositoryConnector.php
@@ -0,0 +1,594 @@
+<?php
+
+namespace SMW\SPARQLStore\RepositoryConnectors;
+
+use Onoi\HttpRequest\HttpRequest;
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+use SMW\SPARQLStore\HttpResponseErrorMapper;
+use SMW\SPARQLStore\QueryEngine\RepositoryResult;
+use SMW\SPARQLStore\QueryEngine\XmlResponseParser;
+use SMW\SPARQLStore\RepositoryClient;
+use SMW\SPARQLStore\RepositoryConnection;
+use SMWExporter as Exporter;
+
+/**
+ * Basic database connector for exchanging data via SPARQL.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class GenericRepositoryConnector implements RepositoryConnection {
+
+ /**
+ * Flag denoting endpoints being capable of querying
+ */
+ const ENDP_QUERY = 1;
+
+ /**
+ * Flag denoting endpoints being capable of updating
+ */
+ const ENDP_UPDATE = 2;
+
+ /**
+ * Flag denoting endpoints being capable of SPARQL HTTP graph management
+ */
+ const ENDP_DATA = 4;
+
+ /**
+ * @var RepositoryClient
+ */
+ protected $repositoryClient;
+
+ /**
+ * @note Handles the curl handle and is reused throughout the instance to
+ * safe some initialization effort
+ *
+ * @var HttpRequest
+ */
+ protected $httpRequest;
+
+ /**
+ * @var HttpResponseErrorMapper
+ */
+ private $badHttpResponseMapper;
+
+ /**
+ * @note It is suggested to use the RepositoryConnectionProvider to create
+ * a valid instance
+ *
+ * @since 2.2
+ *
+ * @param RepositoryClient $repositoryClient
+ * @param HttpRequest $httpRequest
+ */
+ public function __construct( RepositoryClient $repositoryClient, HttpRequest $httpRequest ) {
+ $this->repositoryClient = $repositoryClient;
+ $this->httpRequest = $httpRequest;
+
+ $this->httpRequest->setOption( CURLOPT_FORBID_REUSE, false );
+ $this->httpRequest->setOption( CURLOPT_FRESH_CONNECT, false );
+ $this->httpRequest->setOption( CURLOPT_RETURNTRANSFER, true ); // put result into variable
+ $this->httpRequest->setOption( CURLOPT_FAILONERROR, true );
+
+ $this->setConnectionTimeout( 10 );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return RepositoryClient
+ */
+ public function getRepositoryClient() {
+ return $this->repositoryClient;
+ }
+
+ /**
+ * Get the URI of the default graph that this database connector is
+ * using, or the empty string if none is used (no graph related
+ * statements in queries/updates).
+ *
+ * @return string graph UIR or empty
+ */
+ public function getDefaultGraph() {
+ return $this->repositoryClient->getDefaultGraph();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param integer $timeout in seconds
+ */
+ public function setConnectionTimeout( $timeout = 10 ) {
+ $this->httpRequest->setOption( CURLOPT_CONNECTTIMEOUT, $timeout );
+ }
+
+ /**
+ * Check if the database can be contacted.
+ *
+ * @todo SPARQL endpoints sometimes return errors if no (valid) query
+ * is posted. The current implementation tries to catch this, but this
+ * might not be entirely correct. Especially, the SPARQL 1.1 HTTP error
+ * codes for Update are not defined yet (April 15 2011).
+ *
+ * @param $pingQueryEndpoint boolean true if the query endpoint should be
+ * pinged, false if the update endpoint should be pinged
+ *
+ * @return boolean to indicate success
+ */
+ public function ping( $endpointType = self::ENDP_QUERY ) {
+ if ( $endpointType == self::ENDP_QUERY ) {
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_NOBODY, true );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+ } elseif ( $endpointType == self::ENDP_UPDATE ) {
+
+ if ( $this->repositoryClient->getUpdateEndpoint() === '' ) {
+ return false;
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() );
+
+ // 4Store gives 404 instead of 500 with CURLOPT_NOBODY
+ $this->httpRequest->setOption( CURLOPT_NOBODY, false );
+
+ } else { // ( $endpointType == self::ENDP_DATA )
+
+ if ( $this->repositoryClient->getDataEndpoint() === '' ) {
+ return false;
+ }
+
+ // try an empty POST
+ return $this->doHttpPost( '' );
+ }
+
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ // Valid HTTP responses from a complaining SPARQL endpoint that is
+ // alive and kicking
+ $httpCode = $this->httpRequest->getLastTransferInfo( CURLINFO_HTTP_CODE );
+
+ return ( ( $httpCode == 500 ) || ( $httpCode == 400 ) );
+ }
+
+ /**
+ * SELECT wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $vars mixed array or string, field name(s) to be retrieved, can be '*'
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $options array (associative) of options, e.g. array( 'LIMIT' => '10' )
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return RepositoryResult
+ */
+ public function select( $vars, $where, $options = [], $extraNamespaces = [] ) {
+ return $this->doQuery( $this->getSparqlForSelect( $vars, $where, $options, $extraNamespaces ) );
+ }
+
+ /**
+ * Build the SPARQL query that is used by GenericHttpDatabaseConnector::select().
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return string SPARQL query
+ */
+ public function getSparqlForSelect( $vars, $where, $options = [], $extraNamespaces = [] ) {
+
+ $sparql = self::getPrefixString( $extraNamespaces ) . 'SELECT ';
+
+ if ( array_key_exists( 'DISTINCT', $options ) ) {
+ $sparql .= 'DISTINCT ';
+ }
+
+ if ( is_array( $vars ) ) {
+ $sparql .= implode( ',', $vars );
+ } else {
+ $sparql .= $vars;
+ }
+
+ $sparql .= " WHERE {\n" . $where . "\n}";
+
+ if ( array_key_exists( 'ORDER BY', $options ) ) {
+ $sparql .= "\nORDER BY " . $options['ORDER BY'];
+ }
+
+ if ( array_key_exists( 'OFFSET', $options ) ) {
+ $sparql .= "\nOFFSET " . $options['OFFSET'];
+ }
+
+ if ( array_key_exists( 'LIMIT', $options ) ) {
+ $sparql .= "\nLIMIT " . $options['LIMIT'];
+ }
+
+ return $sparql;
+ }
+
+ /**
+ * ASK wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return RepositoryResult
+ */
+ public function ask( $where, $extraNamespaces = [] ) {
+ return $this->doQuery( $this->getSparqlForAsk( $where, $extraNamespaces ) );
+ }
+
+ /**
+ * Build the SPARQL query that is used by GenericHttpDatabaseConnector::ask().
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return string SPARQL query
+ */
+ public function getSparqlForAsk( $where, $extraNamespaces = [] ) {
+ return self::getPrefixString( $extraNamespaces ) . "ASK {\n" . $where . "\n}";
+ }
+
+ /**
+ * SELECT wrapper for counting results.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $variable string variable name or '*'
+ * @param $where string WHERE part of the query, without surrounding { }
+ * @param $options array (associative) of options, e.g. array('LIMIT' => '10')
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return RepositoryResult
+ */
+ public function selectCount( $variable, $where, $options = [], $extraNamespaces = [] ) {
+
+ $sparql = self::getPrefixString( $extraNamespaces ) . 'SELECT (COUNT(';
+
+ if ( array_key_exists( 'DISTINCT', $options ) ) {
+ $sparql .= 'DISTINCT ';
+ }
+
+ $sparql .= $variable . ") AS ?count) WHERE {\n" . $where . "\n}";
+
+ if ( array_key_exists( 'OFFSET', $options ) ) {
+ $sparql .= "\nOFFSET " . $options['OFFSET'];
+ }
+
+ if ( array_key_exists( 'LIMIT', $options ) ) {
+ $sparql .= "\nLIMIT " . $options['LIMIT'];
+ }
+
+ return $this->doQuery( $sparql );
+ }
+
+ /**
+ * DELETE wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $deletePattern string CONSTRUCT pattern of tripples to delete
+ * @param $where string condition for data to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function delete( $deletePattern, $where, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) .
+ ( ( $defaultGraph !== '' )? "WITH <{$defaultGraph}> " : '' ) .
+ "DELETE { $deletePattern } WHERE { $where }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * Convenience method for deleting all triples that have a subject that
+ * occurs in a triple with the given property and object. This is used
+ * in SMW to delete subobjects with all their data. Some RDF stores fail
+ * on complex delete queries, hence a wrapper function is provided to
+ * allow more pedestrian implementations.
+ *
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $propertyName string Turtle name of marking property
+ * @param $objectName string Turtle name of marking object/value
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function deleteContentByValue( $propertyName, $objectName, $extraNamespaces = [] ) {
+ return $this->delete( "?s ?p ?o", "?s $propertyName $objectName . ?s ?p ?o", $extraNamespaces );
+ }
+
+ /**
+ * Convenience method for deleting all triples of the entire store
+ *
+ * @return boolean
+ */
+ public function deleteAll() {
+ return $this->delete( "?s ?p ?o", "?s ?p ?o" );
+ }
+
+ /**
+ * INSERT DELETE wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $insertPattern string CONSTRUCT pattern of tripples to insert
+ * @param $deletePattern string CONSTRUCT pattern of tripples to delete
+ * @param $where string condition for data to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function insertDelete( $insertPattern, $deletePattern, $where, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) .
+ ( ( $defaultGraph !== '' )? "WITH <{$defaultGraph}> " : '' ) .
+ "DELETE { $deletePattern } INSERT { $insertPattern } WHERE { $where }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * INSERT DATA wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $triples string of triples to insert
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function insertData( $triples, $extraNamespaces = [] ) {
+
+ if ( $this->repositoryClient->getDataEndpoint() !== '' ) {
+ $turtle = self::getPrefixString( $extraNamespaces, false ) . $triples;
+ return $this->doHttpPost( $turtle );
+ }
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces, true ) .
+ "INSERT DATA " .
+ ( ( $defaultGraph !== '' )? " { GRAPH <{$defaultGraph}> " : '' ) .
+ "{ $triples } " .
+ ( ( $defaultGraph !== '' )? " } " : '' );
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * DELETE DATA wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $triples string of triples to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ *
+ * @return boolean stating whether the operations succeeded
+ */
+ public function deleteData( $triples, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) .
+ "DELETE DATA { " .
+ ( ( $defaultGraph !== '' )? "GRAPH <{$defaultGraph}> " : '' ) .
+ "{ $triples } }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+
+ /**
+ * Execute a SPARQL query and return an RepositoryResult object
+ * that contains the results. The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then an empty result with an error
+ * code is returned.
+ *
+ * @note This function sets the graph that is to be used as part of the
+ * request. Queries should not include additional graph information.
+ *
+ * @param $sparql string with the complete SPARQL query (SELECT or ASK)
+ *
+ * @return RepositoryResult
+ */
+ public function doQuery( $sparql ) {
+
+ if ( $this->repositoryClient->getQueryEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getQueryEndpoint() );
+
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, [
+ 'Accept: application/sparql-results+xml,application/xml;q=0.8',
+ 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
+ ] );
+
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $parameterString = "query=" . urlencode( $sparql ) .
+ ( ( $defaultGraph !== '' )? '&default-graph-uri=' . urlencode( $defaultGraph ) : '' );
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+
+ $httpResponse = $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ $xmlResponseParser = new XmlResponseParser();
+ return $xmlResponseParser->parse( $httpResponse );
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getQueryEndpoint(), $sparql );
+
+ $repositoryResult = new RepositoryResult();
+ $repositoryResult->setErrorCode( RepositoryResult::ERROR_UNREACHABLE );
+
+ return $repositoryResult;
+ }
+
+ /**
+ * Execute a SPARQL update and return a boolean to indicate if the
+ * operations was successful. The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then false is returned.
+ *
+ * @note When this is written, it is not clear if the update protocol
+ * supports a default-graph-uri parameter. Hence the target graph for
+ * all updates is generally encoded in the query string and not fixed
+ * when sending the query. Direct callers to this function must include
+ * the graph information in the queries that they build.
+ *
+ * @param $sparql string with the complete SPARQL update query (INSERT or DELETE)
+ *
+ * @return boolean
+ */
+ public function doUpdate( $sparql ) {
+
+ if ( $this->repositoryClient->getUpdateEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $parameterString = "update=" . urlencode( $sparql );
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, [ 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' ] );
+
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getUpdateEndpoint(), $sparql );
+ return false;
+ }
+
+ /**
+ * Execute a HTTP-based SPARQL POST request according to
+ * http://www.w3.org/2009/sparql/docs/http-rdf-update/.
+ * The method throws exceptions based on
+ * GenericHttpDatabaseConnector::mapHttpRequestError(). If errors occur and this
+ * method does not throw anything, then an empty result with an error
+ * code is returned.
+ *
+ * @note This protocol is not part of the SPARQL standard and may not
+ * be supported by all stores. To avoid using it, simply do not provide
+ * a data endpoint URL when configuring the SPARQL database. If used,
+ * the protocol might lead to a better performance since there is less
+ * parsing required to fetch the data from the request.
+ * @note Some stores (e.g. 4Store) support another mode of posting data
+ * that may be implemented in a special database handler.
+ *
+ * @param $payload string Turtle serialization of data to send
+ *
+ * @return boolean
+ */
+ public function doHttpPost( $payload ) {
+
+ if ( $this->repositoryClient->getDataEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, "SPARQL POST with data: $payload", 'not specified' );
+ }
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getDataEndpoint() .
+ ( ( $defaultGraph !== '' )? '?graph=' . urlencode( $defaultGraph ) : '?default' ) );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ // POST as file (fails in 4Store)
+ $payloadFile = tmpfile();
+ fwrite( $payloadFile, $payload );
+ fseek( $payloadFile, 0 );
+
+ $this->httpRequest->setOption( CURLOPT_INFILE, $payloadFile );
+ $this->httpRequest->setOption( CURLOPT_INFILESIZE, strlen( $payload ) );
+ $this->httpRequest->setOption( CURLOPT_HTTPHEADER, [ 'Content-Type: application/x-turtle' ] );
+
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ // TODO The error reporting based on SPARQL (Update) is not adequate for the HTTP POST protocol
+ $this->mapHttpRequestError( $this->repositoryClient->getDataEndpoint(), $payload );
+ return false;
+ }
+
+ /**
+ * Create the standard PREFIX declarations for SPARQL or Turtle,
+ * possibly with additional namespaces involved.
+ *
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @param $forSparql boolean true to use SPARQL prefix syntax, false to use Turtle prefix syntax
+ *
+ * @return string
+ */
+ public static function getPrefixString( $extraNamespaces = [], $forSparql = true ) {
+ $prefixString = '';
+ $prefixIntro = $forSparql ? 'PREFIX ' : '@prefix ';
+ $prefixOutro = $forSparql ? "\n" : " .\n";
+
+ foreach ( [ 'wiki', 'rdf', 'rdfs', 'owl', 'swivt', 'property', 'xsd' ] as $shortname ) {
+ $prefixString .= "{$prefixIntro}{$shortname}: <" . Exporter::getInstance()->getNamespaceUri( $shortname ) . ">$prefixOutro";
+ unset( $extraNamespaces[$shortname] ); // avoid double declaration
+ }
+
+ foreach ( $extraNamespaces as $shortname => $uri ) {
+ $prefixString .= "{$prefixIntro}{$shortname}: <$uri>$prefixOutro";
+ }
+
+ return $prefixString;
+ }
+
+ /**
+ * @param $endpoint string URL of endpoint that was used
+ * @param $sparql string query that caused the problem
+ */
+ protected function mapHttpRequestError( $endpoint, $sparql ) {
+
+ if ( $this->badHttpResponseMapper === null ) {
+ $this->badHttpResponseMapper = new HttpResponseErrorMapper( $this->httpRequest );
+ }
+
+ $this->badHttpResponseMapper->mapErrorResponse( $endpoint, $sparql );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnector.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnector.php
new file mode 100644
index 00000000..e7a57e4d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnector.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace SMW\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+
+/**
+ * Virtuoso specific adjustments for GenericRepositoryConnector
+ *
+ * Specific modifications of the SPARQL database implementation for Virtuoso.
+ * In particular, Virtuoso does not support SPARQL Update but only the non-standard
+ * SPARUL protocol that requires different syntax for update queries.
+ * If future versions of Virtuoso support SPARQL Update, the standard SPARQL
+ * database connector should work properly.
+ *
+ * Virtuoso uses the SPARQL query endpoint for updates as well. So both
+ * - $smwgSparqlEndpoint['update'] and
+ * - $smwgSparqlEndpoint['query'] should be something like 'http://localhost:8890/sparql/'.
+ * - $smwgSparqlEndpoint['data'] should be left empty.
+ *
+ * A graph is always needed, i.e., $smwgSparqlDefaultGraph must be set to some
+ * graph name (URI).
+ *
+ * Known limitations:
+ * (might be fixed in recent Virtuoso versions, please let us know)
+ *
+ * - Data endpoint not tested: $smwgSparqlEndpoint['data'] should be left empty
+ * - Numerical datatypes are not supported properly, and Virtuoso
+ * will miss query results when query conditions require number values.
+ * This also affects Type:Date properties since the use numerical values for
+ * querying.
+ * - Some edit (insert) queries fail for unknown reasons, probably related to
+ * unusual/complex input data (e.g., using special characters in strings);
+ * errors will occur when trying to store such values on a page.
+ * - Virtuoso stumbles over XSD dates with negative years, even if they have
+ * only four digits as per ISO. Trying to store such data will cause errors.
+ *
+ * @ingroup Sparql
+ *
+ * @license GNU GPL v2+
+ * @since 1.7.1
+ *
+ * @author Markus Krötzsch
+ */
+class VirtuosoRepositoryConnector extends GenericRepositoryConnector {
+
+ /**
+ * DELETE wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $deletePattern string CONSTRUCT pattern of tripples to delete
+ * @param $where string condition for data to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @return boolean stating whether the operations succeeded
+ */
+ public function delete( $deletePattern, $where, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) . "DELETE" .
+ ( ( $defaultGraph !== '' )? " FROM <{$defaultGraph}> " : '' ) .
+ "{ $deletePattern } WHERE { $where }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * INSERT DELETE wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $insertPattern string CONSTRUCT pattern of tripples to insert
+ * @param $deletePattern string CONSTRUCT pattern of tripples to delete
+ * @param $where string condition for data to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @return boolean stating whether the operations succeeded
+ */
+ public function insertDelete( $insertPattern, $deletePattern, $where, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) . "MODIFY" .
+ ( ( $defaultGraph !== '' )? " GRAPH <{$defaultGraph}> " : '' ) .
+ "DELETE { $deletePattern } INSERT { $insertPattern } WHERE { $where }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * INSERT DATA wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $triples string of triples to insert
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @return boolean stating whether the operations succeeded
+ */
+ public function insertData( $triples, $extraNamespaces = [] ) {
+
+ if ( $this->repositoryClient->getDataEndpoint() !== '' ) {
+ $turtle = self::getPrefixString( $extraNamespaces, false ) . $triples;
+ return $this->doHttpPost( $turtle );
+ }
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces, true ) .
+ "INSERT DATA " .
+ ( ( $defaultGraph !== '' )? "INTO GRAPH <{$defaultGraph}> " : '' ) .
+ "{ $triples }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * DELETE DATA wrapper.
+ * The function declares the standard namespaces wiki, swivt, rdf, owl,
+ * rdfs, property, xsd, so these do not have to be included in
+ * $extraNamespaces.
+ *
+ * @param $triples string of triples to delete
+ * @param $extraNamespaces array (associative) of namespaceId => namespaceUri
+ * @return boolean stating whether the operations succeeded
+ */
+ public function deleteData( $triples, $extraNamespaces = [] ) {
+
+ $defaultGraph = $this->repositoryClient->getDefaultGraph();
+
+ $sparql = self::getPrefixString( $extraNamespaces ) .
+ "DELETE DATA " .
+ ( ( $defaultGraph !== '' )? "FROM GRAPH <{$defaultGraph}> " : '' ) .
+ "{ $triples }";
+
+ return $this->doUpdate( $sparql );
+ }
+
+ /**
+ * Execute a SPARQL update and return a boolean to indicate if the
+ * operations was successful.
+ *
+ * Virtuoso expects SPARQL updates to be posted using the "query"
+ * parameter (rather than "update").
+ *
+ * @param $sparql string with the complete SPARQL update query (INSERT or DELETE)
+ * @return boolean
+ */
+ public function doUpdate( $sparql ) {
+
+ if ( $this->repositoryClient->getUpdateEndpoint() === '' ) {
+ throw new BadHttpEndpointResponseException( BadHttpEndpointResponseException::ERROR_NOSERVICE, $sparql, 'not specified' );
+ }
+
+ $this->httpRequest->setOption( CURLOPT_URL, $this->repositoryClient->getUpdateEndpoint() );
+ $this->httpRequest->setOption( CURLOPT_POST, true );
+
+ $parameterString = "query=" . urlencode( $sparql );
+
+ $this->httpRequest->setOption( CURLOPT_POSTFIELDS, $parameterString );
+ $this->httpRequest->execute();
+
+ if ( $this->httpRequest->getLastErrorCode() == 0 ) {
+ return true;
+ }
+
+ $this->mapHttpRequestError( $this->repositoryClient->getUpdateEndpoint(), $sparql );
+
+ return false;
+ }
+
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryRedirectLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryRedirectLookup.php
new file mode 100644
index 00000000..6dc83555
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/RepositoryRedirectLookup.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\InMemoryPoolCache;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWExpResource as ExpResource;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class RepositoryRedirectLookup {
+
+ /**
+ * ID used for the InMemoryPoolCache
+ */
+ const POOLCACHE_ID = 'sparql.repository.redirectLookup';
+
+ /**
+ * @var RepositoryConnection
+ */
+ private $repositoryConnection;
+
+ /**
+ * @since 2.0
+ *
+ * @param RepositoryConnection $repositoryConnection
+ */
+ public function __construct( RepositoryConnection $repositoryConnection ) {
+ $this->repositoryConnection = $repositoryConnection;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public static function reset() {
+ InMemoryPoolCache::getInstance()->resetPoolCacheById( self::POOLCACHE_ID );
+ }
+
+ /**
+ * Find the redirect target of an ExpNsResource
+ *
+ * Returns an SMWExpNsResource object the input redirects to, the input
+ * itself if there is no redirect (or it cannot be used for making a resource
+ * with a prefix).
+ *
+ * @since 1.6
+ *
+ * @param ExpNsResource $expNsResource string URI to check
+ * @param boolean $existsthat is set to true if $expNsResource is in the
+ * store; always false for blank nodes; always true for subobjects
+ *
+ * @return ExpNsResource
+ * @throws RuntimeException
+ */
+ public function findRedirectTargetResource( ExpNsResource $expNsResource, &$exists ) {
+
+ $exists = true;
+
+ if ( $expNsResource->isBlankNode() || $this->isNonRedirectableResource( $expNsResource ) ) {
+ $exists = false;
+ return $expNsResource;
+ }
+
+ if ( ( $expNsResource->getDataItem() instanceof DIWikiPage ) &&
+ $expNsResource->getDataItem()->getSubobjectName() !== '' ) {
+ return $expNsResource;
+ }
+
+ $firstRow = $this->doLookupResourceUriTargetFor( $expNsResource );
+
+ if ( $firstRow === false ) {
+ $exists = false;
+ return $expNsResource;
+ }
+
+ if ( is_array( $firstRow ) && count( $firstRow ) > 1 && !is_null( $firstRow[1] ) ) {
+ return $this->getResourceForTargetElement( $expNsResource, $firstRow[1] );
+ }
+
+ return $expNsResource;
+ }
+
+ private function doLookupResourceUriTargetFor( ExpNsResource $expNsResource ) {
+
+ $poolCache = InMemoryPoolCache::getInstance()->getPoolCacheById( self::POOLCACHE_ID );
+
+ if ( !$poolCache->contains( $expNsResource->getUri() ) ) {
+ $poolCache->save(
+ $expNsResource->getUri(),
+ $this->lookupResourceUriTargetFromDatabase( $expNsResource )
+ );
+ }
+
+ return $poolCache->fetch( $expNsResource->getUri() );
+ }
+
+ private function isNonRedirectableResource( ExpNsResource $expNsResource ) {
+ return $expNsResource->getNamespaceId() === 'swivt' ||
+ $expNsResource->getNamespaceId() === 'rdf' ||
+ $expNsResource->getNamespaceId() === 'rdfs' ||
+ ( $expNsResource->getNamespaceId() === 'property' && strrpos( $expNsResource->getLocalName(), 'aux' ) ) ||
+ ( isset( $expNsResource->isUserDefined ) && !$expNsResource->isUserDefined );
+ }
+
+ private function lookupResourceUriTargetFromDatabase( ExpNsResource $expNsResource ) {
+
+ $resourceUri = TurtleSerializer::getTurtleNameForExpElement( $expNsResource );
+ $rediUri = TurtleSerializer::getTurtleNameForExpElement( Exporter::getInstance()->getSpecialPropertyResource( '_REDI' ) );
+ $skeyUri = TurtleSerializer::getTurtleNameForExpElement( Exporter::getInstance()->getSpecialPropertyResource( '_SKEY' ) );
+
+ $respositoryResult = $this->repositoryConnection->select(
+ '*',
+ "$resourceUri $skeyUri ?s OPTIONAL { $resourceUri $rediUri ?r }",
+ [ 'LIMIT' => 1 ],
+ [ $expNsResource->getNamespaceId() => $expNsResource->getNamespace() ]
+ );
+
+ return $respositoryResult->current();
+ }
+
+ private function getResourceForTargetElement( ExpNsResource $expNsResource, $rediTargetElement ) {
+
+ if ( !$rediTargetElement instanceof ExpResource ) {
+ throw new RuntimeException( 'Expected a ExpResource instance' );
+ }
+
+ $rediTargetUri = $rediTargetElement->getUri();
+ $wikiNamespace = Exporter::getInstance()->getNamespaceUri( 'wiki' );
+
+ if ( strpos( $rediTargetUri, $wikiNamespace ) === 0 ) {
+ return new ExpNsResource( substr( $rediTargetUri, strlen( $wikiNamespace ) ), $wikiNamespace, 'wiki' );
+ }
+
+ return $expNsResource;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php
new file mode 100644
index 00000000..d83d32c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php
@@ -0,0 +1,486 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMWQuery as Query;
+use SMWTurtleSerializer as TurtleSerializer;
+use Title;
+
+/**
+ * Storage and query access point for a SPARQL supported RepositoryConnector to
+ * enable SMW to communicate with a SPARQL endpoint.
+ *
+ * The store uses a base store to update certain aspects of the data that is not
+ * yet modelled and supported by a RepositoryConnector, which may become optional
+ * in future.
+ *
+ * @license GNU GPL v2+
+ * @since 1.6
+ *
+ * @author Markus Krötzsch
+ */
+class SPARQLStore extends Store {
+
+ /**
+ * @var SPARQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * Class to be used as an underlying base store. This can be changed in
+ * LocalSettings.php (after enableSemantics()) to use another base
+ * store.
+ *
+ * @since 1.8
+ * @var string
+ */
+ static public $baseStoreClass = 'SMWSQLStore3';
+
+ /**
+ * Underlying store to use for basic read operations.
+ *
+ * @since 1.8
+ * @var Store
+ */
+ private $baseStore;
+
+ /**
+ * @since 1.8
+ *
+ * @param Store $baseStore
+ */
+ public function __construct( Store $baseStore = null ) {
+ $this->factory = new SPARQLStoreFactory( $this );
+ $this->baseStore = $baseStore;
+
+ if ( $this->baseStore === null ) {
+ $this->baseStore = $this->factory->getBaseStore( self::$baseStoreClass );
+ }
+ }
+
+ /**
+ * @see Store::getSemanticData()
+ * @since 1.8
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+ return $this->baseStore->getSemanticData( $subject, $filter );
+ }
+
+ /**
+ * @see Store::getPropertyValues()
+ * @since 1.8
+ */
+ public function getPropertyValues( $subject, DIProperty $property, $requestoptions = null ) {
+ return $this->baseStore->getPropertyValues( $subject, $property, $requestoptions);
+ }
+
+ /**
+ * @see Store::getPropertySubjects()
+ * @since 1.8
+ */
+ public function getPropertySubjects( DIProperty $property, $value, $requestoptions = null ) {
+ return $this->baseStore->getPropertySubjects( $property, $value, $requestoptions );
+ }
+
+ /**
+ * @see Store::getAllPropertySubjects()
+ * @since 1.8
+ */
+ public function getAllPropertySubjects( DIProperty $property, $requestoptions = null ) {
+ return $this->baseStore->getAllPropertySubjects( $property, $requestoptions );
+ }
+
+ /**
+ * @see Store::getProperties()
+ * @since 1.8
+ */
+ public function getProperties( DIWikiPage $subject, $requestoptions = null ) {
+ return $this->baseStore->getProperties( $subject, $requestoptions );
+ }
+
+ /**
+ * @see Store::getInProperties()
+ * @since 1.8
+ */
+ public function getInProperties( DataItem $object, $requestoptions = null ) {
+ return $this->baseStore->getInProperties( $object, $requestoptions );
+ }
+
+ /**
+ * @see Store::deleteSubject()
+ * @since 1.6
+ */
+ public function deleteSubject( Title $subject ) {
+ $this->doSparqlDataDelete( DIWikiPage::newFromTitle( $subject ) );
+ $this->baseStore->deleteSubject( $subject );
+ }
+
+ /**
+ * @see Store::changeTitle()
+ * @since 1.6
+ */
+ public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) {
+
+ $oldWikiPage = DIWikiPage::newFromTitle( $oldtitle );
+ $newWikiPage = DIWikiPage::newFromTitle( $newtitle );
+ $oldExpResource = Exporter::getInstance()->getDataItemExpElement( $oldWikiPage );
+ $newExpResource = Exporter::getInstance()->getDataItemExpElement( $newWikiPage );
+ $namespaces = [ $oldExpResource->getNamespaceId() => $oldExpResource->getNamespace() ];
+ $namespaces[$newExpResource->getNamespaceId()] = $newExpResource->getNamespace();
+ $oldUri = TurtleSerializer::getTurtleNameForExpElement( $oldExpResource );
+ $newUri = TurtleSerializer::getTurtleNameForExpElement( $newExpResource );
+
+ // do this only here, so Imported from is not moved too early
+ $this->baseStore->changeTitle(
+ $oldtitle,
+ $newtitle,
+ $pageid,
+ $redirid
+ );
+
+ $sparqlDatabase = $this->getConnection();
+ $sparqlDatabase->insertDelete( "?s ?p $newUri", "?s ?p $oldUri", "?s ?p $oldUri", $namespaces );
+
+ if ( $oldtitle->getNamespace() === SMW_NS_PROPERTY ) {
+ $sparqlDatabase->insertDelete( "?s $newUri ?o", "?s $oldUri ?o", "?s $oldUri ?o", $namespaces );
+ }
+
+ /**
+ * @since 2.3 Moved UpdateJob to the base-store to ensurethat both stores
+ * operate similar when dealing with redirects
+ *
+ * @note Note that we cannot change oldUri to newUri in triple subjects,
+ * since some triples change due to the move.
+ */
+
+ // #566 $redirid == 0 indicates a `move` not a redirect action
+ if ( $redirid == 0 ) {
+ $this->doSparqlDataDelete( $oldWikiPage );
+ }
+ }
+
+ /**
+ * Update the Sparql back-end.
+ *
+ * This method can be called independently to force an update of the Sparql
+ * database. In general it is suggested to use updateData to carry out a
+ * synchronized update of the base and Sparql store.
+ *
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ */
+ public function doSparqlDataUpdate( SemanticData $semanticData ) {
+
+ $replicationDataTruncator = $this->factory->newReplicationDataTruncator();
+ $semanticData = $replicationDataTruncator->doTruncate( $semanticData );
+
+ $turtleTriplesBuilder = $this->factory->newTurtleTriplesBuilder();
+
+ $this->doSparqlFlatDataUpdate( $semanticData, $turtleTriplesBuilder );
+
+ foreach( $semanticData->getSubSemanticData() as $subSemanticData ) {
+ $subSemanticData = $replicationDataTruncator->doTruncate( $subSemanticData );
+ $this->doSparqlFlatDataUpdate( $subSemanticData, $turtleTriplesBuilder );
+ }
+
+ //wfDebugLog( 'smw', ' InMemoryPoolCache: ' . json_encode( \SMW\InMemoryPoolCache::getInstance()->getStats() ) );
+
+ // Reset internal cache
+ $turtleTriplesBuilder->reset();
+ }
+
+ /**
+ * @param SemanticData $semanticData
+ * @param TurtleTriplesBuilder $turtleTriplesBuilder
+ */
+ private function doSparqlFlatDataUpdate( SemanticData $semanticData, TurtleTriplesBuilder $turtleTriplesBuilder ) {
+
+ $turtleTriplesBuilder->doBuildTriplesFrom( $semanticData );
+
+ if ( !$turtleTriplesBuilder->hasTriples() ) {
+ return;
+ }
+
+ if ( $semanticData->getSubject()->getSubobjectName() === '' ) {
+ $this->doSparqlDataDelete( $semanticData->getSubject() );
+ }
+
+ foreach( $turtleTriplesBuilder->getChunkedTriples() as $chunkedTriples ) {
+ $this->getConnection()->insertData(
+ $chunkedTriples,
+ $turtleTriplesBuilder->getPrefixes()
+ );
+ }
+ }
+
+ /**
+ * @see Store::doDataUpdate()
+ * @since 1.6
+ */
+ protected function doDataUpdate( SemanticData $semanticData ) {
+ $this->baseStore->doDataUpdate( $semanticData );
+ $this->doSparqlDataUpdate( $semanticData );
+ }
+
+ /**
+ * Delete a dataitem from the Sparql back-end together with all data that is
+ * associated resources
+ *
+ * @since 2.0
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ public function doSparqlDataDelete( DataItem $dataItem ) {
+
+ $extraNamespaces = [];
+
+ $expResource = Exporter::getInstance()->getDataItemExpElement( $dataItem );
+ $resourceUri = TurtleSerializer::getTurtleNameForExpElement( $expResource );
+
+ if ( $expResource instanceof ExpNsResource ) {
+ $extraNamespaces = [ $expResource->getNamespaceId() => $expResource->getNamespace() ];
+ }
+
+ $masterPageProperty = Exporter::getInstance()->getSpecialNsResource( 'swivt', 'masterPage' );
+ $masterPagePropertyUri = TurtleSerializer::getTurtleNameForExpElement( $masterPageProperty );
+
+ $success = $this->getConnection()->deleteContentByValue( $masterPagePropertyUri, $resourceUri, $extraNamespaces );
+
+ if ( $success ) {
+ return $this->getConnection()->delete( "$resourceUri ?p ?o", "$resourceUri ?p ?o", $extraNamespaces );
+ }
+
+ return false;
+ }
+
+ /**
+ * @note Move hooks to the base class in 3.*
+ *
+ * @see Store::getQueryResult
+ * @since 1.6
+ */
+ public function getQueryResult( Query $query ) {
+
+ // Use a fallback QueryEngine in case the QueryEndpoint is inaccessible
+ if ( !$this->hasQueryEndpoint() ) {
+ return $this->baseStore->getQueryResult( $query );
+ }
+
+ $result = null;
+ $start = microtime( true );
+
+ if ( \Hooks::run( 'SMW::Store::BeforeQueryResultLookupComplete', [ $this, $query, &$result, $this->factory->newMasterQueryEngine() ] ) ) {
+ $result = $this->fetchQueryResult( $query );
+ }
+
+ \Hooks::run( 'SMW::Store::AfterQueryResultLookupComplete', [ $this, &$result ] );
+
+ $query->setOption( Query::PROC_QUERY_TIME, microtime( true ) - $start );
+
+ return $result;
+ }
+
+ protected function fetchQueryResult( Query $query ) {
+ return $this->factory->newMasterQueryEngine()->getQueryResult( $query );
+ }
+
+ /**
+ * @see Store::getPropertiesSpecial()
+ * @since 1.8
+ */
+ public function getPropertiesSpecial( $requestoptions = null ) {
+ return $this->baseStore->getPropertiesSpecial( $requestoptions );
+ }
+
+ /**
+ * @see Store::getUnusedPropertiesSpecial()
+ * @since 1.8
+ */
+ public function getUnusedPropertiesSpecial( $requestoptions = null ) {
+ return $this->baseStore->getUnusedPropertiesSpecial( $requestoptions );
+ }
+
+ /**
+ * @see Store::getWantedPropertiesSpecial()
+ * @since 1.8
+ */
+ public function getWantedPropertiesSpecial( $requestoptions = null ) {
+ return $this->baseStore->getWantedPropertiesSpecial( $requestoptions );
+ }
+
+ /**
+ * @see Store::getStatistics()
+ * @since 1.8
+ */
+ public function getStatistics() {
+ return $this->baseStore->getStatistics();
+ }
+
+ /**
+ * @see Store::refreshConceptCache()
+ * @since 1.8
+ */
+ public function refreshConceptCache( Title $concept ) {
+ return $this->baseStore->refreshConceptCache( $concept );
+ }
+
+ /**
+ * @see Store::deleteConceptCache()
+ * @since 1.8
+ */
+ public function deleteConceptCache( $concept ) {
+ return $this->baseStore->deleteConceptCache( $concept );
+ }
+
+ /**
+ * @see Store::getConceptCacheStatus()
+ * @since 1.8
+ */
+ public function getConceptCacheStatus( $concept ) {
+ return $this->baseStore->getConceptCacheStatus( $concept );
+ }
+
+ /**
+ * @see Store::service
+ *
+ * {@inheritDoc}
+ */
+ public function service( $service, ...$args ) {
+ return $this->baseStore->service( $service, ...$args );
+ }
+
+ /**
+ * @see Store::setup()
+ * @since 1.8
+ */
+ public function setup( $verbose = true ) {
+
+ // Only copy required options to the base store
+ $options = $this->getOptions()->filter(
+ [
+ \SMW\SQLStore\Installer::OPT_TABLE_OPTIMIZE,
+ \SMW\SQLStore\Installer::OPT_IMPORT,
+ \SMW\SQLStore\Installer::OPT_SCHEMA_UPDATE,
+ \SMW\SQLStore\Installer::OPT_SUPPLEMENT_JOBS
+ ]
+ );
+
+ foreach ( $options as $key => $value ) {
+ $this->baseStore->setOption( $key, $value );
+ }
+
+ $this->baseStore->setMessageReporter( $this->messageReporter );
+ $this->baseStore->setup( $verbose );
+ }
+
+ /**
+ * @see Store::drop()
+ * @since 1.6
+ */
+ public function drop( $verbose = true ) {
+ $this->baseStore->drop( $verbose );
+ $this->getConnection()->deleteAll();
+ }
+
+ /**
+ * @see Store::refreshData()
+ * @since 1.8
+ */
+ public function refreshData( &$index, $count, $namespaces = false, $usejobs = true ) {
+ return $this->baseStore->refreshData( $index, $count, $namespaces, $usejobs );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyTableInfoFetcher
+ */
+ public function getPropertyTableInfoFetcher() {
+ return $this->baseStore->getPropertyTableInfoFetcher();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function getPropertyTables() {
+ return $this->baseStore->getPropertyTables();
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function getObjectIds() {
+ return $this->baseStore->getObjectIds();
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function getPropertyTableIdReferenceFinder() {
+ return $this->baseStore->getPropertyTableIdReferenceFinder();
+ }
+
+ /**
+ * @since 1.9.2
+ */
+ public function clear() {
+ $this->baseStore->clear();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getInfo( $type = null ) {
+
+ $client = $this->getConnection( 'sparql' )->getRepositoryClient();
+
+ if ( $type === 'store' ) {
+ return [ 'SMWSPARQLStore', $client->getName() ];
+ }
+
+ $connection = $this->getConnection( 'mw.db' );
+
+ if ( $type === 'db' ) {
+ return $connection->getInfo();
+ }
+
+ return [
+ 'SMWSPARQLStore' => $connection->getInfo() + [ $client->getName() => 'n/a' ]
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ *
+ * @return mixed
+ */
+ public function getConnection( $type = 'sparql' ) {
+
+ if ( $this->connectionManager === null ) {
+ $this->setConnectionManager( $this->factory->getConnectionManager() );
+ }
+
+ return parent::getConnection( $type );
+ }
+
+ private function hasQueryEndpoint() {
+ return $this->getConnection( 'sparql' )->getRepositoryClient()->getQueryEndpoint() !== false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStoreFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStoreFactory.php
new file mode 100644
index 00000000..0c9bd16b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStoreFactory.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\Connection\ConnectionManager;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\SPARQLStore\QueryEngine\QueryEngine;
+use SMW\SPARQLStore\QueryEngine\QueryResultFactory;
+use SMW\Store;
+use SMW\StoreFactory;
+use SMW\Utils\CircularReferenceGuard;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class SPARQLStoreFactory {
+
+ /**
+ * @var SPARQLStore
+ */
+ private $store;
+
+ /**
+ * @since 2.2
+ *
+ * @param SPARQLStore $store
+ */
+ public function __construct( SPARQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $storeClass
+ *
+ * @return Store
+ */
+ public function getBaseStore( $storeClass ) {
+ return StoreFactory::getStore( $storeClass );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QueryEngine
+ */
+ public function newMasterQueryEngine() {
+
+ $engineOptions = new EngineOptions();
+
+ $circularReferenceGuard = new CircularReferenceGuard( 'sparql-queryengine' );
+ $circularReferenceGuard->setMaxRecursionDepth( 2 );
+
+ $conditionBuilder = new ConditionBuilder(
+ new DescriptionInterpreterFactory(),
+ $engineOptions
+ );
+
+ $conditionBuilder->setCircularReferenceGuard(
+ $circularReferenceGuard
+ );
+
+ $conditionBuilder->setHierarchyLookup(
+ ApplicationFactory::getInstance()->newHierarchyLookup()
+ );
+
+ $queryEngine = new QueryEngine(
+ $this->store->getConnection( 'sparql' ),
+ $conditionBuilder,
+ new QueryResultFactory( $this->store ),
+ $engineOptions
+ );
+
+ return $queryEngine;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return RepositoryRedirectLookup
+ */
+ public function newRepositoryRedirectLookup() {
+ return new RepositoryRedirectLookup( $this->store->getConnection( 'sparql' ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return TurtleTriplesBuilder
+ */
+ public function newTurtleTriplesBuilder() {
+
+ $turtleTriplesBuilder = new TurtleTriplesBuilder(
+ $this->newRepositoryRedirectLookup()
+ );
+
+ $turtleTriplesBuilder->setTriplesChunkSize( 80 );
+
+ return $turtleTriplesBuilder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ReplicationDataTruncator
+ */
+ public function newReplicationDataTruncator() {
+
+ $replicationDataTruncator = new ReplicationDataTruncator();
+
+ $replicationDataTruncator->setPropertyExemptionList(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgSparqlReplicationPropertyExemptionList' )
+ );
+
+ return $replicationDataTruncator;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ConnectionManager
+ */
+ public function getConnectionManager() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $repositoryConnectionProvider = new RepositoryConnectionProvider(
+ $settings->get( 'smwgSparqlRepositoryConnector' ),
+ $settings->get( 'smwgSparqlDefaultGraph' ),
+ $settings->dotGet( 'smwgSparqlEndpoint.query' ),
+ $settings->dotGet( 'smwgSparqlEndpoint.update', '' ),
+ $settings->dotGet( 'smwgSparqlEndpoint.data', '' )
+ );
+
+ $repositoryConnectionProvider->setHttpVersionTo(
+ $settings->get( 'smwgSparqlRepositoryConnectorForcedHttpVersion' )
+ );
+
+ $repositoryConnectionProvider = new RepositoryConnectionProvider();
+
+ $connectionManager = $applicationFactory->getConnectionManager();
+ $connectionManager->registerConnectionProvider(
+ 'sparql',
+ $repositoryConnectionProvider
+ );
+
+ return $connectionManager;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/TurtleTriplesBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/TurtleTriplesBuilder.php
new file mode 100644
index 00000000..fa6e9659
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/TurtleTriplesBuilder.php
@@ -0,0 +1,333 @@
+<?php
+
+namespace SMW\SPARQLStore;
+
+use Onoi\Cache\Cache;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Exporter\Element;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\Element\ExpResource;
+use SMW\SemanticData;
+use SMWExpData as ExpData;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class TurtleTriplesBuilder {
+
+ /**
+ * ID used for the InMemoryPoolCache
+ */
+ const POOLCACHE_ID = 'sparql.turtle.triplesbuilder';
+
+ /**
+ * @var SemanticData
+ */
+ private $semanticData = null;
+
+ /**
+ * @var RepositoryRedirectLookup
+ */
+ private $repositoryRedirectLookup = null;
+
+ /**
+ * @var null|string
+ */
+ private $triples = null;
+
+ /**
+ * @var array
+ */
+ private $prefixes = [];
+
+ /**
+ * @var boolean
+ */
+ private $hasTriplesForUpdate = false;
+
+ /**
+ * @var integer
+ */
+ private $triplesChunkSize = 80;
+
+ /**
+ * @var Cache
+ */
+ private $dataItemExportInMemoryCache;
+
+ /**
+ * @since 2.0
+ *
+ * @param RepositoryRedirectLookup $repositoryRedirectLookup
+ * @param Cache|null $cache
+ */
+ public function __construct( RepositoryRedirectLookup $repositoryRedirectLookup, Cache $cache = null ) {
+ $this->repositoryRedirectLookup = $repositoryRedirectLookup;
+ $this->dataItemExportInMemoryCache = ApplicationFactory::getInstance()->getInMemoryPoolCache()->getPoolCacheById( self::POOLCACHE_ID );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $chunkSize
+ */
+ public function setTriplesChunkSize( $triplesChunkSize ) {
+ $this->triplesChunkSize = (int)$triplesChunkSize;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param SemanticData $semanticData
+ */
+ public function doBuildTriplesFrom( SemanticData $semanticData ) {
+
+ $this->hasTriplesForUpdate = false;
+ $this->triples = '';
+ $this->prefixes = [];
+
+ $this->doSerialize( $semanticData );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return boolean
+ */
+ public function hasTriples() {
+ return $this->hasTriplesForUpdate;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return string
+ */
+ public function getTriples() {
+ return $this->triples === null ? '' : $this->triples;
+ }
+
+ /**
+ * Split the triples into group of chunks as it can happen that some subjects
+ * contain SPARQL strings that exceed 1800 lines which may reach the capacity
+ * limit of a RespositoryConnector (#1110).
+ *
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getChunkedTriples() {
+
+ $chunkedTriples = [];
+
+ if ( $this->triples === null ) {
+ return $chunkedTriples;
+ }
+
+ if ( strpos( $this->triples, " ." ) === false ) {
+ return $chunkedTriples;
+ }
+
+ $triplesArrayChunks = array_chunk(
+ explode( " .", $this->triples ), $this->triplesChunkSize
+ );
+
+ foreach( $triplesArrayChunks as $triplesChunk ) {
+ $chunkedTriples[] = implode( " .", $triplesChunk ) . "\n";
+ }
+
+ return $chunkedTriples;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return array
+ */
+ public function getPrefixes() {
+ return $this->prefixes;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static function reset() {
+ TurtleSerializer::reset();
+ }
+
+ private function doSerialize( SemanticData $semanticData ) {
+
+ $expDataArray = $this->prepareUpdateExpData( $semanticData );
+
+ if ( count( $expDataArray ) > 0 ) {
+
+ $this->hasTriplesForUpdate = true;
+
+ $turtleSerializer = new TurtleSerializer( true );
+ $turtleSerializer->startSerialization();
+
+ foreach ( $expDataArray as $expData ) {
+ $turtleSerializer->serializeExpData( $expData );
+ }
+
+ $turtleSerializer->finishSerialization();
+
+ $this->triples = $turtleSerializer->flushContent();
+ $this->prefixes = $turtleSerializer->flushSparqlPrefixes();
+ }
+ }
+
+ /**
+ * Prepare an array of SMWExpData elements that should be written to
+ * the SPARQL store. The result is empty if no updates should be done.
+ * Note that this is different from writing an SMWExpData element that
+ * has no content.
+ * Otherwise, the first SMWExpData object in the array is a translation
+ * of the given input data, but with redirects resolved. Further
+ * SMWExpData objects might be included in the resulting list to
+ * capture necessary stub declarations for objects that do not have
+ * any data in the RDF store yet.
+ *
+ * @since 1.6
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return array of SMWExpData
+ */
+ private function prepareUpdateExpData( SemanticData $semanticData ) {
+
+ $result = [];
+
+ $expData = Exporter::getInstance()->makeExportData( $semanticData );
+ $newExpData = $this->expandUpdateExpData( $expData, $result, false );
+ array_unshift( $result, $newExpData );
+
+ return $result;
+ }
+
+ /**
+ * Find a normalized representation of the given SMWExpElement that can
+ * be used in an update of the stored data. Normalization uses
+ * redirects. The type of the ExpElement might change, especially into
+ * SMWExpData in order to store auxiliary properties.
+ * Moreover, the method records any auxiliary data that should be
+ * written to the store when including this SMWExpElement into updates.
+ * This auxiliary data is collected in a call-by-ref array.
+ *
+ * @since 1.6
+ *
+ * @param Element $expElement object containing the update data
+ * @param $auxiliaryExpData array of SMWExpData
+ *
+ * @return ExpElement
+ */
+ private function expandUpdateExpElement( Element $expElement, array &$auxiliaryExpData ) {
+
+ if ( $expElement instanceof ExpResource ) {
+ return $this->expandUpdateExpResource( $expElement, $auxiliaryExpData );
+ }
+
+ if ( $expElement instanceof ExpData ) {
+ return $this->expandUpdateExpData( $expElement, $auxiliaryExpData, true );
+ }
+
+ return $expElement;
+ }
+
+ /**
+ * Find a normalized representation of the given SMWExpResource that can
+ * be used in an update of the stored data. Normalization uses
+ * redirects. The type of the ExpElement might change, especially into
+ * SMWExpData in order to store auxiliary properties.
+ * Moreover, the method records any auxiliary data that should be
+ * written to the store when including this SMWExpElement into updates.
+ * This auxiliary data is collected in a call-by-ref array.
+ *
+ * @since 1.6
+ *
+ * @param ExpResource $expResource object containing the update data
+ * @param $auxiliaryExpData array of SMWExpData
+ *
+ * @return ExpElement
+ */
+ private function expandUpdateExpResource( ExpResource $expResource, array &$auxiliaryExpData ) {
+
+ $exists = true;
+
+ if ( $expResource instanceof ExpNsResource ) {
+ $elementTarget = $this->repositoryRedirectLookup->findRedirectTargetResource( $expResource, $exists );
+ } else {
+ $elementTarget = $expResource;
+ }
+
+ if ( !$exists && $elementTarget->getDataItem() instanceof DIWikiPage && $elementTarget->getDataItem()->getDBKey() !== '' ) {
+
+ $diWikiPage = $elementTarget->getDataItem();
+ $hash = $diWikiPage->getHash();
+
+ if ( !$this->dataItemExportInMemoryCache->contains( $hash ) ) {
+ $this->dataItemExportInMemoryCache->save( $hash, Exporter::getInstance()->makeExportDataForSubject( $diWikiPage, true ) );
+ }
+
+ $auxiliaryExpData[$hash] = $this->dataItemExportInMemoryCache->fetch( $hash );
+ }
+
+ return $elementTarget;
+ }
+
+ /**
+ * Find a normalized representation of the given SMWExpData that can
+ * be used in an update of the stored data. Normalization uses
+ * redirects.
+ * Moreover, the method records any auxiliary data that should be
+ * written to the store when including this SMWExpElement into updates.
+ * This auxiliary data is collected in a call-by-ref array.
+ *
+ * @since 1.6
+ * @param ExpData $expData object containing the update data
+ * @param $auxiliaryExpData array of SMWExpData
+ * @param $expandSubject boolean controls if redirects/auxiliary data should also be sought for subject
+ *
+ * @return ExpData
+ */
+ private function expandUpdateExpData( ExpData $expData, array &$auxiliaryExpData, $expandSubject ) {
+
+ $subjectExpResource = $expData->getSubject();
+
+ if ( $expandSubject ) {
+
+ $expandedExpElement = $this->expandUpdateExpElement( $subjectExpResource, $auxiliaryExpData );
+
+ if ( $expandedExpElement instanceof ExpData ) {
+ $newExpData = $expandedExpElement;
+ } else { // instanceof SMWExpResource
+ $newExpData = new ExpData( $subjectExpResource );
+ }
+ } else {
+ $newExpData = new ExpData( $subjectExpResource );
+ }
+
+ foreach ( $expData->getProperties() as $propertyResource ) {
+
+ $propertyTarget = $this->expandUpdateExpElement( $propertyResource, $auxiliaryExpData );
+
+ foreach ( $expData->getValues( $propertyResource ) as $element ) {
+ $newExpData->addPropertyObjectValue(
+ $propertyTarget,
+ $this->expandUpdateExpElement( $element, $auxiliaryExpData )
+ );
+ }
+ }
+
+ return $newExpData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeDiff.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeDiff.php
new file mode 100644
index 00000000..7c6fe8d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeDiff.php
@@ -0,0 +1,255 @@
+<?php
+
+namespace SMW\SQLStore\ChangeOp;
+
+use Onoi\Cache\Cache;
+use SMW\DIWikiPage;
+use SMW\Utils\HmacSerializer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ChangeDiff {
+
+ /**
+ * Identifies the cache namespace
+ */
+ const CACHE_NAMESPACE = 'smw:store:diff';
+
+ /**
+ * Identifies the cache TTL (one week)
+ */
+ const CACHE_TTL = 604800;
+
+ /**
+ * @var string
+ */
+ private $time;
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @var array
+ */
+ private $tableChangeOps = [];
+
+ /**
+ * @var array
+ */
+ private $dataOps = [];
+
+ /**
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var array
+ */
+ private $textItems = [];
+
+ /**
+ * @var array
+ */
+ private $changeList = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ * @param array $tableChangeOps
+ * @param array $dataOps
+ * @param array $propertyList
+ * @param array $textItems
+ */
+ public function __construct( DIWikiPage $subject, array $tableChangeOps, array $dataOps, array $propertyList, array $textItems = [] ) {
+ $this->time = time();
+ $this->subject = $subject;
+ $this->tableChangeOps = $tableChangeOps;
+ $this->dataOps = $dataOps;
+ $this->propertyList = $propertyList;
+ $this->textItems = $textItems;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->subject;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TableChangeOps[]
+ */
+ public function getTableChangeOps() {
+ return $this->tableChangeOps;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TableChangeOps[]
+ */
+ public function getDataOps() {
+ return $this->dataOps;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getTextItems() {
+ return $this->textItems;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $op
+ *
+ * @return []
+ */
+ public function getPropertyList( $op = false ) {
+
+ if ( $op === true || $op === 'flip' ) {
+ $list = [];
+
+ foreach ( $this->propertyList as $key => $value ) {
+ if ( is_array( $value ) ) {
+ $list[$value['_id']] = $key;
+ } else {
+ $list[$value] = $key;
+ }
+ }
+
+ return $list;
+ }
+
+ if ( $op === 'id' ) {
+ $list = [];
+
+ foreach ( $this->propertyList as $key => $value ) {
+ if ( is_array( $value ) ) {
+ $list[$value['_id']] = [ '_key' => $key, '_type'=> $value['_type'] ];
+ }
+ }
+
+ return $list;
+ }
+
+ return $this->propertyList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ * @param array $changes
+ */
+ public function setChangeList( $type, array $changes ) {
+ $this->changeList[$type] = $changes;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ public function getChangeListByType( $type ) {
+ return isset( $this->changeList[$type] ) ? $this->changeList[$type] : [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function serialize() {
+ return HmacSerializer::compress( $this );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function toJson( $prettify = false ) {
+
+ $changes = [];
+
+ foreach ( $this->tableChangeOps as $tableChangeOp ) {
+ $changes[] = $tableChangeOp->toArray();
+ }
+
+ $data = [];
+
+ foreach ( $this->dataOps as $dataOp ) {
+ $data[] = $dataOp->toArray();
+ }
+
+ $flags = $prettify ? JSON_PRETTY_PRINT : 0;
+
+ return json_encode(
+ [
+ 'time' => $this->time,
+ 'subject' => $this->subject->getHash(),
+ 'changes' => $changes,
+ 'change_list' => $this->changeList,
+ 'data' => $data,
+ 'text_items' => $this->textItems,
+ 'property_list' => $this->propertyList
+ ],
+ $flags
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Cache $cache
+ */
+ public function save( Cache $cache ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $this->subject->getHash()
+ );
+
+ // Keep it a week
+ $cache->save( $key, HmacSerializer::compress( $this ), self::CACHE_TTL );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Cache $cache
+ * @param DIWikiPage $subject
+ */
+ public static function fetch( Cache $cache, DIWikiPage $subject ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ $subject->getHash()
+ );
+
+ if ( ( $diff = $cache->fetch( $key ) ) !== false ) {
+ return HmacSerializer::uncompress( $diff );
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeOp.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeOp.php
new file mode 100644
index 00000000..30726851
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/ChangeOp.php
@@ -0,0 +1,371 @@
+<?php
+
+namespace SMW\SQLStore\ChangeOp;
+
+use ArrayIterator;
+use IteratorAggregate;
+use SMW\DIWikiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ChangeOp implements IteratorAggregate {
+
+ /**
+ * Type of change operations
+ */
+ const OP_INSERT = 'insert';
+ const OP_DELETE = 'delete';
+
+ /**
+ * @var array
+ */
+ private $diff = [];
+
+ /**
+ * @var array
+ */
+ private $data = [];
+
+ /**
+ * @var array
+ */
+ private $textItems = [];
+
+ /**
+ * @var array
+ */
+ private $orderedDiff = [];
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @var array
+ */
+ private $fixedPropertyRecords = [];
+
+ /**
+ * @var array
+ */
+ private $propertyList = [];
+
+ /**
+ * @var boolean
+ */
+ private $textItemsFlag = false;
+
+ /**
+ * @since 2.3
+ *
+ * @param DIWikiPage|null $subject
+ * @param array $diff
+ */
+ public function __construct( DIWikiPage $subject = null, array $diff = [] ) {
+ $this->subject = $subject;
+ $this->diff = $diff;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $textItemsFlag
+ */
+ public function setTextItemsFlag( $textItemsFlag ) {
+ $this->textItemsFlag = (bool)$textItemsFlag;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->subject;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return ArrayIterator
+ */
+ public function getIterator() {
+ return new ArrayIterator( $this->diff );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->subject->getHash();
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $fixedPropertyRecord
+ */
+ public function addFixedPropertyRecord( $tableName, array $fixedPropertyRecord ) {
+ $this->fixedPropertyRecords[$tableName] = $fixedPropertyRecord;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getFixedPropertyRecords() {
+ return $this->fixedPropertyRecords;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function addPropertyList( $propertyList ) {
+ $this->propertyList = array_merge( $this->propertyList, $propertyList );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getPropertyList() {
+ return $this->propertyList;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $hash
+ * @param array $data
+ */
+ public function addDataOp( $hash, array $data ) {
+ $this->data[$hash] = $data;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TableChangeOp[]
+ */
+ public function getDataOps() {
+
+ $dataChangeOps = [];
+
+ foreach ( $this->data as $hash => $data ) {
+ foreach ( $data as $tableName => $d ) {
+
+ if ( isset( $this->fixedPropertyRecords[$tableName] ) ) {
+ $d['property'] = $this->fixedPropertyRecords[$tableName];
+ }
+
+ $dataChangeOps[] = new TableChangeOp( $tableName, $d );
+ }
+ }
+
+ return $dataChangeOps;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param array $data
+ */
+ public function addTextItems( $id, array $textItems ) {
+ if ( $this->textItemsFlag ) {
+ $this->textItems[$id] = $textItems;
+ }
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $insertOp
+ * @param array $deleteOp
+ */
+ public function addDiffOp( array $insertOp, array $deleteOp ) {
+
+ $diff = [
+ 'insert' => $insertOp,
+ 'delete' => $deleteOp
+ ];
+
+ $this->diff[] = $diff;
+ }
+
+ /**
+ * ChangeOp (TableChangeOp/FieldChangeOp) representation of the composite
+ * diff.
+ *
+ * @since 2.4
+ *
+ * @param string|null $table
+ *
+ * @return TableChangeOp[]|[]
+ */
+ public function getTableChangeOps( $table = null ) {
+
+ $tableChangeOps = [];
+
+ foreach ( $this->getOrderedDiffByTable( $table ) as $tableName => $diff ) {
+ $tableChangeOps[] = new TableChangeOp( $tableName, $diff );
+ }
+
+ return $tableChangeOps;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ChangeDiff
+ */
+ public function newChangeDiff() {
+
+ $changeDiff = new ChangeDiff(
+ $this->subject,
+ $this->getTableChangeOps(),
+ $this->getDataOps(),
+ $this->getPropertyList(),
+ $this->textItems
+ );
+
+ $changeDiff->setChangeList(
+ self::OP_INSERT,
+ $this->getChangedEntityIdListByType( self::OP_INSERT )
+ );
+
+ $changeDiff->setChangeList(
+ self::OP_DELETE,
+ $this->getChangedEntityIdListByType( self::OP_DELETE )
+ );
+
+ return $changeDiff;
+ }
+
+ /**
+ * Simplified (ordered by table) diff array to allow for an easier
+ * post-processing
+ *
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getOrderedDiffByTable( $table = null ) {
+
+ if ( $table === null && $this->orderedDiff !== [] ) {
+ return $this->orderedDiff;
+ }
+
+ $ordered = [];
+
+ foreach ( $this as $diff ) {
+ foreach ( $diff as $key => $value ) {
+ foreach ( $value as $tableName => $val ) {
+
+ if ( $val === [] || ( $table !== null && $table !== $tableName ) ) {
+ continue;
+ }
+
+ if ( isset( $this->fixedPropertyRecords[$tableName] ) ) {
+ $ordered[$tableName]['property'] = $this->fixedPropertyRecords[$tableName];
+ }
+
+ if ( !isset( $ordered[$tableName] ) ) {
+ $ordered[$tableName] = [];
+ }
+
+ if ( !isset( $ordered[$tableName][$key] ) ) {
+ $ordered[$tableName][$key] = [];
+ }
+
+ foreach ( $val as $v ) {
+ $ordered[$tableName][$key][] = $v;
+ }
+ }
+ }
+ }
+
+ if ( $table === null ) {
+ $this->orderedDiff = $ordered;
+ }
+
+ return $ordered;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getChangedEntityIdListByType( $type = null ) {
+
+ $changedEntities = [];
+
+ foreach ( $this->getOrderedDiffByTable() as $diff ) {
+
+ if ( ( $type === 'insert' || $type === null ) && isset( $diff['insert'] ) ) {
+ $this->addToIdList( $changedEntities, $diff['insert'] );
+ }
+
+ if ( ( $type === 'delete' || $type === null ) && isset( $diff['delete'] ) ) {
+ $this->addToIdList( $changedEntities, $diff['delete'] );
+ }
+
+ if ( $type === null && isset( $diff['property'] ) ) {
+ $changedEntities[$diff['property']['p_id']] = true;
+ }
+ }
+
+ return $changedEntities;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getChangedEntityIdSummaryList() {
+ return array_keys( $this->getChangedEntityIdListByType() );
+ }
+
+ /**
+ * @deprecated since 3.0, use ChangeOp::getChangedEntityIdSummaryList
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getCombinedIdListOfChangedEntities() {
+ return $this->getChangedEntityIdSummaryList();
+ }
+
+ private function addToIdList( &$list, $value ) {
+ foreach ( $value as $element ) {
+
+ if ( isset( $element['p_id'] ) ) {
+ $list[$element['p_id']] = true;
+ }
+
+ if ( isset( $element['s_id'] ) ) {
+ $list[$element['s_id']] = true;
+ }
+
+ if ( isset( $element['o_id'] ) ) {
+ $list[$element['o_id']] = true;
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/FieldChangeOp.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/FieldChangeOp.php
new file mode 100644
index 00000000..293138d9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/FieldChangeOp.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\SQLStore\ChangeOp;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class FieldChangeOp {
+
+ /**
+ * @var array
+ */
+ private $changeOp = [];
+
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @since 2.4
+ *
+ * @param array $changeOp
+ * @param string|null $type
+ */
+ public function __construct( array $changeOp = [], $type = null ) {
+ $this->changeOp = $changeOp;
+ $this->type = $type;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+ $this->changeOp[$key] = $value;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function has( $key ) {
+ return isset( $this->changeOp[$key] ) || array_key_exists( $key, $this->changeOp );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return string
+ * @throws InvalidArgumentException
+ */
+ public function get( $key ) {
+
+ if ( $this->has( $key ) ) {
+ return $this->changeOp[$key];
+ }
+
+ throw new InvalidArgumentException( "{$key} is an unregistered field" );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getChangeOp() {
+ return $this->changeOp;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function __toString() {
+ return json_encode( [ $this->type => $this->changeOp ] );
+ }
+
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/TableChangeOp.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/TableChangeOp.php
new file mode 100644
index 00000000..3923b770
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangeOp/TableChangeOp.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace SMW\SQLStore\ChangeOp;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TableChangeOp {
+
+ const OP_INSERT = 'insert';
+ const OP_DELETE = 'delete';
+
+ /**
+ * @var string
+ */
+ private $tableName;
+
+ /**
+ * @var array
+ */
+ private $changeOps;
+
+ /**
+ * @since 2.4
+ *
+ * @param string $tableName
+ * @param array $changeOps
+ */
+ public function __construct( $tableName, array $changeOps ) {
+ $this->tableName = $tableName;
+ $this->changeOps = $changeOps;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getTableName() {
+ return $this->tableName;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return boolean
+ */
+ public function isFixedPropertyOp() {
+ return isset( $this->changeOps['property'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $field
+ *
+ * @return null|string
+ */
+ public function getFixedPropertyValByField( $field ) {
+
+ if ( $this->isFixedPropertyOp() && isset( $this->changeOps['property'][$field] ) ) {
+ return $this->changeOps['property'][$field];
+ }
+
+ return null;
+ }
+
+ /**
+ * @deprecated since 3.0, use TableChangeOp::getFixedPropertyValByField
+ * @since 2.4
+ *
+ * @param string $field
+ *
+ * @return null|string
+ */
+ public function getFixedPropertyValueBy( $field ) {
+ return $this->getFixedPropertyValByField( $field );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $opType
+ *
+ * @return boolean
+ */
+ public function hasChangeOp( $opType ) {
+ return isset( $this->changeOps[$opType] );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|null $opType
+ * @param array $filter
+ *
+ * @return FieldChangeOp[]|[]
+ */
+ public function getFieldChangeOps( $opType = null, $filter = [] ) {
+
+ if ( $opType !== null && !$this->hasChangeOp( $opType ) ) {
+ return [];
+ }
+
+ $fieldOps = [];
+ $changeOps = $this->changeOps;
+
+ if ( $opType !== null ) {
+ $changeOps = $this->changeOps[$opType];
+ } elseif ( !isset( $this->changeOps[self::OP_DELETE] ) && !isset( $this->changeOps[self::OP_INSERT] ) ) {
+ $changeOps = $this->changeOps;
+ } else {
+ return array_merge(
+ $this->getFieldChangeOps( self::OP_DELETE, $filter ),
+ $this->getFieldChangeOps( self::OP_INSERT, $filter )
+ );
+ }
+
+ unset( $changeOps['property'] );
+
+ foreach ( $changeOps as $changeOp ) {
+
+ // Filter defined as: [ 's_id' => [ 42 => true, 1001 => true ] ]
+ if ( isset( $filter['s_id' ] ) && isset( $changeOp['s_id'] ) && isset( $filter['s_id'][$changeOp['s_id']] ) ) {
+ continue;
+ }
+
+ if ( isset( $this->changeOps['property'] ) ) {
+ $changeOp['p_id'] = $this->changeOps['property']['p_id'];
+ }
+
+ $fieldOps[] = new FieldChangeOp( $changeOp, $opType );
+ }
+
+ return $fieldOps;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+ return json_encode( $this->toArray() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function toArray() {
+ return [ $this->tableName => $this->changeOps ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangePropagationEntityFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangePropagationEntityFinder.php
new file mode 100644
index 00000000..c2e3994e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ChangePropagationEntityFinder.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\IteratorFactory;
+use SMW\Store;
+
+/**
+ * Find all entities related to a change propagation (only expected
+ * to be used by `ChangePropagationDispatchJob`).
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationEntityFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @var boolean
+ */
+ private $isTypePropagation = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param IteratorFactory $iteratorFactory
+ */
+ public function __construct( Store $store, IteratorFactory $iteratorFactory ) {
+ $this->store = $store;
+ $this->iteratorFactory = $iteratorFactory;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isTypePropagation
+ */
+ public function isTypePropagation( $isTypePropagation ) {
+ $this->isTypePropagation = (bool)$isTypePropagation;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty|DIWikiPage $entity
+ *
+ * @return Iterator
+ * @throws RuntimeException
+ */
+ public function findAll( $entity ) {
+
+ if ( $entity instanceof DIProperty ) {
+ return $this->findByProperty( $entity );
+ } elseif ( $entity instanceof DIWikiPage ) {
+ return $this->findByCategory( $entity );
+ }
+
+ throw new RuntimeException( 'Cannot match the entity type.' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ *
+ * @return Iterator
+ */
+ public function findByProperty( DIProperty $property ) {
+
+ $dataItems = [];
+ $appendIterator = $this->iteratorFactory->newAppendIterator();
+
+ $res = $this->store->getAllPropertySubjects(
+ $property
+ );
+
+ $appendIterator->add(
+ $res
+ );
+
+ // Select any remaining references that are hidden or have been left out
+ // during an update
+ $appendIterator->add(
+ $this->fetchOtherReferencesOnTypePropagation( $property )
+ );
+
+ $dataItems = $this->store->getPropertySubjects(
+ new DIProperty( DIProperty::TYPE_ERROR ),
+ $property->getCanonicalDiWikiPage()
+ );
+
+ $appendIterator->add(
+ $dataItems
+ );
+
+ return $appendIterator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $category
+ *
+ * @return Iterator
+ */
+ public function findByCategory( DIWikiPage $category ) {
+
+ $appendIterator = $this->iteratorFactory->newAppendIterator();
+
+ $property = new DIProperty( '_INST' );
+
+ $appendIterator->add(
+ $this->store->getPropertySubjects( $property, $category )
+ );
+
+ // Only direct antecedents
+ $dataItems = $this->store->getPropertyValues(
+ $category,
+ new DIProperty( '_SUBC' )
+ );
+
+ foreach ( $dataItems as $dataItem ) {
+ $appendIterator->add(
+ $this->store->getPropertySubjects( $property, $dataItem )
+ );
+ }
+
+ return $appendIterator;
+ }
+
+ private function fetchOtherReferencesOnTypePropagation( $property ) {
+
+ // Find other references only on a type propagation (which causes a
+ // change of table/id assignments) for entity references
+ if ( $this->isTypePropagation === false ) {
+ return [];
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $pid = $this->store->getObjectIds()->getSMWPropertyID( $property );
+
+ $dataItemTables = $this->store->getPropertyTableInfoFetcher()->getDefaultDataItemTables();
+ $idList = [];
+
+ // Matches may temporary create duplicates in regrads to
+ // Store::getAllPropertySubjects but it will be dealt with by the
+ // deduplication in the ChangePropagationUpdateJob
+ foreach ( $dataItemTables as $tableName ) {
+
+ // Select any references that are hidden or remained active
+ $rows = $connection->select(
+ $connection->tableName( $tableName ),
+ [
+ 's_id'
+ ],
+ [
+ 'p_id' => $pid
+ ],
+ __METHOD__
+ );
+
+ foreach ( $rows as $row ) {
+ $idList[] = $row->s_id;
+ }
+ }
+
+ if ( $idList === [] ) {
+ return $idList;
+ }
+
+ return $this->store->getObjectIds()->getDataItemPoolHashListFor( $idList );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ConceptCache.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ConceptCache.php
new file mode 100644
index 00000000..f228ff88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/ConceptCache.php
@@ -0,0 +1,270 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\ProcessingErrorMsgHandler;
+use SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder;
+use SMWSQLStore3;
+use SMWWikiPageValue;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ConceptCache {
+
+ /**
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @var ConceptQuerySegmentBuilder
+ */
+ private $conceptQuerySegmentBuilder;
+
+ /**
+ * @var integer
+ */
+ private $upperLimit = 50;
+
+ /**
+ * @since 2.2
+ *
+ * @param SMWSQLStore3 $store
+ * @param ConceptQuerySegmentBuilder $conceptQueryResolver
+ */
+ public function __construct( SMWSQLStore3 $store, ConceptQuerySegmentBuilder $conceptQuerySegmentBuilder ) {
+ $this->store = $store;
+ $this->conceptQuerySegmentBuilder = $conceptQuerySegmentBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param integer $upperLimit
+ */
+ public function setUpperLimit( $upperLimit ) {
+ $this->upperLimit = (int)$upperLimit;
+ }
+
+ /**
+ * Refresh the concept cache for the given concept.
+ *
+ * @since 1.8
+ * @param $concept Title
+ * @return array of error strings (empty if no errors occurred)
+ */
+ public function refreshConceptCache( Title $concept ) {
+
+ $errors = array_merge(
+ $this->conceptQuerySegmentBuilder->getErrors(),
+ $this->refresh( $concept )
+ );
+
+ $this->conceptQuerySegmentBuilder->cleanUp();
+
+ return ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $errors );
+ }
+
+ /**
+ * Delete the concept cache for the given concept.
+ *
+ * @param $concept Title
+ */
+ public function deleteConceptCache( $concept ) {
+ $this->delete( $concept );
+ }
+
+ /**
+ * @param Title $concept
+ *
+ * @return string[] array with error messages
+ */
+ public function refresh( Title $concept ) {
+ global $wgDBtype;
+
+ $db = $this->store->getConnection();
+
+ $cid = $this->store->smwIds->getSMWPageID( $concept->getDBkey(), SMW_NS_CONCEPT, '', '' );
+ $cid_c = $this->getIdOfConcept( $concept );
+
+ if ( $cid !== $cid_c ) {
+ return [ "Skipping redirect concept." ];
+ }
+
+ $conceptQueryText = $this->getConceptCacheText( $concept );
+
+ if ( $conceptQueryText === false ) {
+ $this->deleteConceptById( $cid );
+
+ return [ "No concept description found." ];
+ }
+
+ // Pre-process query:
+ $querySegment = $this->conceptQuerySegmentBuilder->getQuerySegmentFrom(
+ $conceptQueryText
+ );
+
+ if ( $querySegment === null || $querySegment->joinfield === '' || $querySegment->joinTable === '' ) {
+ return [];
+ }
+
+ // TODO: catch db exception
+ $db->delete(
+ SMWSQLStore3::CONCEPT_CACHE_TABLE,
+ [ 'o_id' => $cid ],
+ __METHOD__
+ );
+
+ $concCacheTableName = $db->tablename( SMWSQLStore3::CONCEPT_CACHE_TABLE );
+
+ // MySQL just uses INSERT IGNORE, no extra conditions
+ $where = $querySegment->where;
+
+ if ( $wgDBtype == 'postgres' ) {
+ // PostgresQL: no INSERT IGNORE, check for duplicates explicitly
+ // This code doesn't work and has created all sorts of issues therefore use LEFT JOIN instead
+ // http://people.planetpostgresql.org/dfetter/index.php?/archives/48-Adding-Only-New-Rows-INSERT-IGNORE,-Done-Right.html
+ // $where = $querySegment->where . ( $querySegment->where ? ' AND ' : '' ) .
+ // "NOT EXISTS (SELECT NULL FROM $concCacheTableName" .
+ // " WHERE {$concCacheTableName}.s_id = {$querySegment->alias}.s_id " .
+ // " AND {$concCacheTableName}.o_id = {$querySegment->alias}.o_id )";
+ $querySegment->from = str_replace( 'INNER JOIN', 'LEFT JOIN', $querySegment->from );
+ }
+
+ $db->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) .
+ "INTO $concCacheTableName" .
+ " SELECT DISTINCT {$querySegment->joinfield} AS s_id, $cid AS o_id FROM " .
+ $db->tableName( $querySegment->joinTable ) . " AS {$querySegment->alias}" .
+ $querySegment->from .
+ ( $where ? ' WHERE ' : '' ) . $where . " LIMIT ". $this->upperLimit,
+ __METHOD__
+ );
+
+ $db->update(
+ 'smw_fpt_conc',
+ [ 'cache_date' => strtotime( "now" ), 'cache_count' => $db->affectedRows() ],
+ [ 's_id' => $cid ],
+ __METHOD__
+ );
+
+ return [];
+ }
+
+ /**
+ * @param Title $concept
+ *
+ * @return string
+ */
+ public function getConceptCacheText( Title $concept ) {
+ $values = $this->store->getPropertyValues(
+ DIWikiPage::newFromTitle( $concept ),
+ new DIProperty( '_CONC' )
+ );
+
+ /**
+ * @var bool|DIConcept $di
+ */
+ $di = end( $values );
+ $conceptQueryText = $di === false ?: $di->getConceptQuery();
+
+ return $conceptQueryText;
+ }
+
+ public function delete( Title $concept ) {
+ $this->deleteConceptById( $this->getIdOfConcept( $concept ) );
+ }
+
+ /**
+ * @param Title $concept
+ *
+ * @return int
+ */
+ private function getIdOfConcept( Title $concept ) {
+ return $this->store->smwIds->getSMWPageID(
+ $concept->getDBkey(),
+ SMW_NS_CONCEPT,
+ '',
+ '',
+ false
+ );
+ }
+
+ /**
+ * @param int $conceptId
+ */
+ private function deleteConceptById( $conceptId ) {
+ // TODO: exceptions should be caught
+
+ $db = $this->store->getConnection();
+
+ $db->delete(
+ SMWSQLStore3::CONCEPT_CACHE_TABLE,
+ [ 'o_id' => $conceptId ],
+ __METHOD__
+ );
+
+ $db->update(
+ 'smw_fpt_conc',
+ [ 'cache_date' => null, 'cache_count' => null ],
+ [ 's_id' => $conceptId ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * @param Title|SMWWikiPageValue|DIWikiPage $concept
+ *
+ * @return DIConcept|null
+ */
+ public function getStatus( $concept ) {
+ $db = $this->store->getConnection();
+
+ $cid = $this->store->smwIds->getSMWPageID(
+ $concept->getDBkey(),
+ $concept->getNamespace(),
+ '',
+ '',
+ false
+ );
+
+ // TODO: catch db exception
+
+ $row = $db->selectRow(
+ 'smw_fpt_conc',
+ [ 'concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date', 'cache_count' ],
+ [ 's_id' => $cid ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ return null;
+ }
+
+ $dataItem = new DIConcept(
+ $concept,
+ null,
+ $row->concept_features,
+ $row->concept_size,
+ $row->concept_depth
+ );
+
+ if ( $row->cache_date ) {
+ $dataItem->setCacheStatus( 'full' );
+ $dataItem->setCacheDate( $row->cache_date );
+ $dataItem->setCacheCount( $row->cache_count );
+ } else {
+ $dataItem->setCacheStatus( 'empty' );
+ }
+
+ return $dataItem;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityRebuildDispatcher.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityRebuildDispatcher.php
new file mode 100644
index 00000000..7ee18256
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityRebuildDispatcher.php
@@ -0,0 +1,512 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Hooks;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\PropertyRegistry;
+use SMW\SemanticData;
+use SMW\Utils\Lru;
+use SMW\MediaWiki\TitleFactory;
+use Title;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ * @author mwjames
+ */
+class EntityRebuildDispatcher {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var TitleFactory
+ */
+ private $titleFactory;
+
+ /**
+ * @var PropertyTableIdReferenceDisposer
+ */
+ private $propertyTableIdReferenceDisposer;
+
+ /**
+ * @var JobFactory
+ */
+ private $jobFactory;
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var array
+ */
+ private $options;
+
+ /**
+ * @var array|false
+ */
+ private $namespaces = false;
+
+ /**
+ * @var integer
+ */
+ private $iterationLimit = 1;
+
+ /**
+ * @var integer
+ */
+ private $progress = 1;
+
+ /**
+ * @var array
+ */
+ private $dispatchedEntities = [];
+
+ /**
+ * @var array
+ */
+ private $updateJobs = [];
+
+ /**
+ * @var Lru
+ */
+ private $lru;
+
+ /**
+ * @since 2.3
+ *
+ * @param SQLStore $store
+ * @param TitleFactory $titleFactory
+ */
+ public function __construct( SQLStore $store, TitleFactory $titleFactory ) {
+ $this->store = $store;
+ $this->titleFactory = $titleFactory;
+ $this->propertyTableIdReferenceDisposer = new PropertyTableIdReferenceDisposer( $store );
+ $this->jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+ $this->namespaceExaminer = ApplicationFactory::getInstance()->getNamespaceExaminer();
+ $this->lru = new Lru( 10000 );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $options
+ */
+ public function setOptions( array $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array|false $namespaces
+ */
+ public function setRestrictionToNamespaces( $namespaces ) {
+ $this->namespaces = $namespaces;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $iterationLimit
+ */
+ public function setDispatchRangeLimit( $iterationLimit ) {
+ $this->iterationLimit = (int)$iterationLimit;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return integer
+ */
+ public function getMaxId() {
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ $maxByPageId = (int)$db->selectField(
+ 'page',
+ 'MAX(page_id)',
+ '',
+ __METHOD__
+ );
+
+ $maxBySmwId = (int)$db->selectField(
+ \SMWSql3SmwIds::TABLE_NAME,
+ 'MAX(smw_id)',
+ '',
+ __METHOD__
+ );
+
+ return max( $maxByPageId, $maxBySmwId );
+ }
+
+ /**
+ * Decimal between 0 and 1 to indicate the overall progress of the rebuild
+ * process
+ *
+ * @since 2.3
+ *
+ * @return integer
+ */
+ public function getEstimatedProgress() {
+ return $this->progress;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getDispatchedEntities() {
+ return $this->dispatchedEntities;
+ }
+
+ /**
+ * Dispatching of a single or a chunk of ids in either online or batch mode
+ * using the JoblruScheduler
+ *
+ * @since 2.3
+ *
+ * @param integer &$id
+ */
+ public function rebuild( &$id ) {
+
+ $this->updateJobs = [];
+ $this->dispatchedEntities = [];
+
+ // was nothing done in this run?
+ $emptyRange = true;
+
+ $this->match_title( $id );
+
+ if ( $this->updateJobs !== [] ) {
+ $emptyRange = false;
+ }
+
+ $this->match_subject( $id, $emptyRange );
+
+ // Deprecated since 2.3, use 'SMW::SQLStore::BeforeDataRebuildJobInsert'
+ \Hooks::run('smwRefreshDataJobs', [ &$this->updateJobs ] );
+
+ Hooks::run( 'SMW::SQLStore::BeforeDataRebuildJobInsert', [ $this->store, &$this->updateJobs ] );
+
+ if ( isset( $this->options['use-job'] ) && $this->options['use-job'] ) {
+ $this->jobFactory->batchInsert( $this->updateJobs );
+ } else {
+ foreach ( $this->updateJobs as $job ) {
+ $job->run();
+ }
+ }
+
+ // -1 means that no next position is available
+ $this->next_position( $id, $emptyRange );
+
+ return $this->progress = $id > 0 ? $id / $this->getMaxId() : 1;
+ }
+
+ /**
+ * @param integer $id
+ * @param UpdateJob[] &$updateJobs
+ */
+ private function match_title( $id ) {
+
+ // Update by MediaWiki page id --> make sure we get all pages.
+ $tids = [];
+
+ // Array of ids
+ for ( $i = $id; $i < $id + $this->iterationLimit; $i++ ) {
+ $tids[] = $i;
+ }
+
+ $titles = $this->titleFactory->newFromIDs( $tids );
+
+ foreach ( $titles as $title ) {
+
+ if ( $this->lru->get( $title->getDBKey() . '#' . $title->getNamespace() ) !== null ) {
+ continue;
+ }
+
+ if ( ( $this->namespaces == false ) || ( in_array( $title->getNamespace(), $this->namespaces ) ) ) {
+ $this->add_update( $title );
+ }
+
+ $this->dispatchedEntities[] = [ 't' => $title->getPrefixedDBKey() ];
+ }
+ }
+
+ private function match_subject( $id, &$emptyRange ) {
+
+ // update by internal SMW id --> make sure we get all objects in SMW
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // MW 1.29+ "Exception thrown with an uncommitted database transaction ...
+ // MWCallableUpdate::doUpdate: transaction round 'SMW\MediaWiki\Jobs\RefreshJob::run' already started"
+ $this->propertyTableIdReferenceDisposer->waitOnTransactionIdle();
+
+ $res = $db->select(
+ \SMWSql3SmwIds::TABLE_NAME,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey',
+ 'smw_proptable_hash',
+ 'smw_rev'
+ ],
+ [
+ "smw_id >= $id ",
+ " smw_id < " . $db->addQuotes( $id + $this->iterationLimit )
+ ],
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ $emptyRange = false; // note this even if no jobs were created
+
+ if ( $this->namespaces && !in_array( $row->smw_namespace, $this->namespaces ) ) {
+ continue;
+ }
+
+ // If the reference is for some reason created as part of a not
+ // supported namespace, check and clean it!
+ //
+ // The check is required to ensure that annotations let's say
+ // [[Foo::SomeNS:Bar]] (where SomeNS is not enabled for SMW) are not
+ // removed and is kept as long as a reference to `SomeNS:Bar` exists
+ if ( !$this->namespaceExaminer->isSemanticEnabled( (int)$row->smw_namespace ) ) {
+ $this->propertyTableIdReferenceDisposer->removeOutdatedEntityReferencesById( $row->smw_id );
+ continue;
+ }
+
+ // Find page to refresh, even for special properties:
+ if ( $row->smw_title != '' && $row->smw_title{0} != '_' ) {
+ $titleKey = $row->smw_title;
+ } elseif ( $row->smw_namespace == SMW_NS_PROPERTY && $row->smw_iw == '' && $row->smw_subobject == '' ) {
+ $titleKey = str_replace( ' ', '_', PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $row->smw_title ) );
+ } else {
+ $titleKey = '';
+ }
+
+ $hash = $titleKey . '#' . $row->smw_namespace;
+
+ if ( $row->smw_subobject !== '' && $row->smw_iw !== SMW_SQL3_SMWDELETEIW ) {
+
+ $title = $this->titleFactory->makeTitleSafe( $row->smw_namespace, $titleKey );
+
+ // Remove tangling subobjects without a real page (created by a
+ // page preview etc.) otherwise leave subobjects alone; they ought
+ // to be changed with their pages
+ if ( $title !== null && !$title->exists() ) {
+ $this->propertyTableIdReferenceDisposer->cleanUpTableEntriesById( $row->smw_id );
+ } else {
+ $this->dispatchedEntities[] = [ 's' => $row->smw_title . '#' . $row->smw_namespace . '#' .$row->smw_subobject ];
+ }
+ } elseif ( $this->isPlainObjectValue( $row ) ) {
+ $this->propertyTableIdReferenceDisposer->removeOutdatedEntityReferencesById( $row->smw_id );
+ } elseif ( $row->smw_iw === '' && $titleKey != '' ) {
+
+ if ( $this->lru->get( $hash ) !== null ) {
+ continue;
+ }
+
+ // objects representing pages
+ $title = $this->titleFactory->makeTitleSafe( $row->smw_namespace, $titleKey );
+
+ if ( $title !== null ) {
+ $this->dispatchedEntities[] = [ 's' => $title->getPrefixedDBKey() ];
+ $this->add_update( $title, $row );
+ }
+ } elseif ( $row->smw_iw == SMW_SQL3_SMWREDIIW && $titleKey != '' ) {
+
+ if ( $this->lru->get( $hash ) !== null ) {
+ continue;
+ }
+
+ // TODO: special treatment of redirects needed, since the store will
+ // not act on redirects that did not change according to its records
+ $title = $this->titleFactory->makeTitleSafe( $row->smw_namespace, $titleKey );
+
+ if ( $title !== null && !$title->exists() ) {
+ $this->dispatchedEntities[] = [ 's' => $title->getPrefixedDBKey() ];
+ $this->add_update( $title, $row );
+ }
+
+ $this->propertyTableIdReferenceDisposer->cleanUpTableEntriesById( $row->smw_id );
+ } elseif ( $row->smw_iw == SMW_SQL3_SMWIW_OUTDATED || $row->smw_iw == SMW_SQL3_SMWDELETEIW ) { // remove outdated internal object references
+ $this->propertyTableIdReferenceDisposer->cleanUpTableEntriesById( $row->smw_id );
+ } elseif ( $titleKey != '' ) { // "normal" interwiki pages or outdated internal objects -- delete
+
+ if ( $this->lru->get( $hash ) !== null ) {
+ continue;
+ }
+
+ $subject = new DIWikiPage( $titleKey, $row->smw_namespace, $row->smw_iw );
+ $this->store->updateData( new SemanticData( $subject ) );
+ $this->dispatchedEntities[] = [ 's' => $subject ];
+ }
+
+ if ( $row->smw_namespace == SMW_NS_PROPERTY && $row->smw_iw == '' && $row->smw_subobject == '' ) {
+ $this->findDuplicateProperties( $row );
+ }
+ }
+
+ $db->freeResult( $res );
+ }
+
+ private function isPlainObjectValue( $row ) {
+
+ // A rogue title should never happen
+ if ( $row->smw_title === '' && $row->smw_proptable_hash === null ) {
+ return true;
+ }
+
+ return $row->smw_iw != SMW_SQL3_SMWDELETEIW &&
+ $row->smw_iw != SMW_SQL3_SMWREDIIW &&
+ $row->smw_iw != SMW_SQL3_SMWIW_OUTDATED &&
+ // Leave any pre-defined property (_...) untouched
+ $row->smw_title != '' &&
+ $row->smw_title{0} != '_' &&
+ // smw_proptable_hash === null means it is not a subject but an object value
+ $row->smw_proptable_hash === null;
+ }
+
+ private function findDuplicateProperties( $row ) {
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // Use the sortkey (comparing the label and not the "_..." key) in order
+ // to match possible duplicate properties by label (not by key)
+ $duplicates = $db->select(
+ \SMWSql3SmwIds::TABLE_NAME,
+ [
+ 'smw_id',
+ 'smw_title' ],
+ [
+ "smw_id !=" . $db->addQuotes( $row->smw_id ),
+ "smw_sortkey =" . $db->addQuotes( $row->smw_sortkey ),
+ "smw_namespace =" . $row->smw_namespace,
+ "smw_subobject =" . $db->addQuotes( $row->smw_subobject )
+ ],
+ __METHOD__,
+ [
+ 'ORDER BY' => "smw_id ASC"
+ ]
+ );
+
+ if ( $duplicates === false ) {
+ return;
+ }
+
+ // Instead of copying ID's across DB tables have the re-parse to ensure
+ // that all property value ID's are reassigned together while the duplicate
+ // is marked for removal until the next run
+ foreach ( $duplicates as $duplicate ) {
+
+ // If titles don't match then continue because it could be that
+ // Property:Foo with displaytitle foobar -> sortkey ->foobar
+ // Property:Bar with displaytitle foobar -> sortkey ->foobar
+ if ( $row->smw_title !== $duplicate->smw_title ) {
+ continue;
+ }
+
+ $this->store->getObjectIds()->updateInterwikiField(
+ $duplicate->smw_id,
+ new DIWikiPage( $row->smw_title, $row->smw_namespace, SMW_SQL3_SMWDELETEIW )
+ );
+ }
+ }
+
+ private function next_position( &$id, $emptyRange ) {
+
+ $nextPosition = $id + $this->iterationLimit;
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // nothing found, check if there will be more pages later on
+ if ( $emptyRange && $nextPosition > \SMWSql3SmwIds::FXD_PROP_BORDER_ID ) {
+
+ $nextByPageId = (int)$db->selectField(
+ 'page',
+ 'page_id',
+ "page_id >= $nextPosition",
+ __METHOD__,
+ [
+ 'ORDER BY' => "page_id ASC"
+ ]
+ );
+
+ $nextBySmwId = (int)$db->selectField(
+ \SMWSql3SmwIds::TABLE_NAME,
+ 'smw_id',
+ "smw_id >= $nextPosition",
+ __METHOD__,
+ [
+ 'ORDER BY' => "smw_id ASC"
+ ]
+ );
+
+ // Next position is determined by the pool with the maxId
+ $nextPosition = $nextBySmwId != 0 && $nextBySmwId > $nextByPageId ? $nextBySmwId : $nextByPageId;
+ }
+
+ $id = $nextPosition ? $nextPosition : -1;
+ }
+
+ private function add_update( $title, $row = false ) {
+
+ $hash = $title->getDBKey() . '#' . $title->getNamespace();
+ $this->lru->set( $hash, true );
+
+ if ( isset( $this->options['revision-mode'] ) && $this->options['revision-mode'] && !$this->options['force-update'] && $this->matchesLatestRevID( $title, $row ) ) {
+ return $this->dispatchedEntities[] = [ 'skipped' => $title->getPrefixedDBKey() ];
+ }
+
+ $params = [
+ 'origin' => 'EntityRebuildDispatcher'
+ ];
+
+ if ( isset( $this->options['shallow-update'] ) && $this->options['shallow-update'] ) {
+ $params += [ 'shallowUpdate' => true ];
+ } elseif ( isset( $this->options['force-update'] ) && $this->options['force-update'] ) {
+ $params += [ 'forcedUpdate' => true ];
+ }
+
+ $updateJob = $this->jobFactory->newUpdateJob(
+ $title,
+ $params
+ );
+
+ $this->updateJobs[] = $updateJob;
+ }
+
+ private function matchesLatestRevID( $title, $row = false ) {
+
+ $latestRevID = $title->getLatestRevID( Title::GAID_FOR_UPDATE );
+
+ if ( $row !== false ) {
+ return $latestRevID == $row->smw_rev;
+ };
+
+ $rev = $this->store->getObjectIds()->findAssociatedRev(
+ $title->getDBKey(),
+ $title->getNamespace(),
+ $title->getInterwiki()
+ );
+
+ return $latestRevID == $rev;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingEntityLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingEntityLookup.php
new file mode 100644
index 00000000..a94e28a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingEntityLookup.php
@@ -0,0 +1,437 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Onoi\BlobStore\BlobStore;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\EntityLookup;
+use SMW\HashBuilder;
+use SMW\Localizer;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+use SMW\SQLStore\Lookup\RedirectTargetLookup;
+use SMWDataItem as DataItem;
+
+/**
+ * Intermediary (fast) access to serialized blob values to avoid DB access on
+ * objects that are static until it is altered. An intermediary object will
+ * invalidate itself on any delete, update, change, or move operation.
+ *
+ * Each subject (including its subobjects) will be stored as individual blob with
+ * each operation belonging to that subject extending its blob to be able
+ * to discard the entire entity at once.
+ *
+ * Each operation request will either fill the cache or return the result from
+ * the cache until the subject is changed and the whole container is being
+ * flushed.
+ *
+ * The class could be decorator but due to the nature of the current Store design
+ * it is called from within each method. The class is not for public use and it
+ * is expected to be accessed only by a Store operation.
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class CachingEntityLookup implements EntityLookup {
+
+ /**
+ * Update this version number when the serialization format
+ * changes.
+ */
+ const VERSION = '1.1';
+
+ /**
+ * @var EntityLookup
+ */
+ private $entityLookup;
+
+ /**
+ * @var RedirectTargetLookup
+ */
+ private $redirectTargetLookup;
+
+ /**
+ * @var BlobStore
+ */
+ private $blobStore;
+
+ /**
+ * @var integer
+ */
+ private $lookupFeatures = 0;
+
+ /**
+ * @since 2.3
+ *
+ * @param EntityLookup $entityLookup
+ * @param RedirectTargetLookup $redirectTargetLookup
+ * @param BlobStore $blobStore
+ */
+ public function __construct( EntityLookup $entityLookup, RedirectTargetLookup $redirectTargetLookup, BlobStore $blobStore ) {
+ $this->entityLookup = $entityLookup;
+ $this->redirectTargetLookup = $redirectTargetLookup;
+ $this->blobStore = $blobStore;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $lookupFeatures
+ */
+ public function setLookupFeatures( $lookupFeatures ) {
+ $this->lookupFeatures = $lookupFeatures;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param integer $lookupFeatures
+ *
+ * @return boolean
+ */
+ public function isEnabledFeature( $lookupFeatures ) {
+ return $this->lookupFeatures === ( $this->lookupFeatures | $lookupFeatures );
+ }
+
+ /**
+ * @see EntityLookup::getSemanticData
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+
+ if ( !$this->blobStore->canUse() || !$this->isEnabledFeature( SMW_VL_SD ) ) {
+ return $this->entityLookup->getSemanticData( $subject, $filter );
+ }
+
+ // Use a separate container otherwise large serializations of subobjects
+ // will decrease performance when combined with other lists
+ $sid = $this->getHashFrom(
+ $subject,
+ $subject->getSubobjectName()
+ );
+
+ $container = $this->blobStore->read( $sid );
+
+ // Make sure that when switching user languages, user labels etc.
+ // are appropriately generated
+ $userLang = Localizer::getInstance()->getUserLanguage()->getCode();
+
+ $sdid = HashBuilder::createFromContent(
+ [
+ (array)$filter,
+ self::VERSION
+ ],
+ 'sd:'. $userLang . ':'
+ );
+
+ if ( $container->has( $sdid ) ) {
+ return $container->get( $sdid );
+ }
+
+ $semanticData = $this->entityLookup->getSemanticData(
+ $subject,
+ $filter
+ );
+
+ $semanticData->setOption(
+ SemanticData::OPT_LAST_MODIFIED,
+ wfTimestamp( TS_UNIX )
+ );
+
+ $container->set( $sdid, $semanticData );
+
+ $this->blobStore->save(
+ $container
+ );
+
+ $this->appendToList( $sid, $subject );
+
+ return $semanticData;
+ }
+
+ /**
+ * @see EntityLookup::getProperties
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getProperties( DIWikiPage $subject, RequestOptions $requestOptions = null ) {
+
+ if ( !$this->blobStore->canUse() || !$this->isEnabledFeature( SMW_VL_PL ) ) {
+ return $this->entityLookup->getProperties( $subject, $requestOptions );
+ }
+
+ $container = $this->blobStore->read(
+ $this->getHashFrom( $subject )
+ );
+
+ $plid = HashBuilder::createFromContent(
+ [
+ $subject->getSubobjectName(),
+ (array)$requestOptions,
+ self::VERSION
+ ],
+ 'pl:'
+ );
+
+ if ( $container->has( $plid ) ) {
+ return $this->resolveRedirectTargets( $container->get( $plid ) );
+ }
+
+ $result = $this->entityLookup->getProperties(
+ $subject,
+ $requestOptions
+ );
+
+ $container->set( $plid, $result );
+
+ $this->blobStore->save(
+ $container
+ );
+
+ return $result;
+ }
+
+ /**
+ * @see EntityLookup::getPropertyValues
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getPropertyValues( DIWikiPage $subject = null, DIProperty $property, RequestOptions $requestOptions = null ) {
+
+ // The cache is not used for $subject === null (means all values for
+ // the given property are returned)
+ if ( $subject === null || !$this->blobStore->canUse() || !$this->isEnabledFeature( SMW_VL_PV ) ) {
+ return $this->entityLookup->getPropertyValues( $subject, $property, $requestOptions );
+ }
+
+ // Too many subobjects in one list can kill the performance therefore split
+ // the container by subobject
+ $sid = $this->getHashFrom(
+ $subject,
+ $subject->getSubobjectName()
+ );
+
+ $container = $this->blobStore->read( $sid );
+
+ $pvid = HashBuilder::createFromContent(
+ [
+ $property->getKey(),
+ $property->isInverse(),
+ (array)$requestOptions,
+ self::VERSION
+ ],
+ 'pv:'
+ );
+
+ if ( $container->has( $pvid ) ) {
+ return $this->resolveRedirectTargets( $container->get( $pvid ) );
+ }
+
+ $result = $this->entityLookup->getPropertyValues(
+ $subject,
+ $property,
+ $requestOptions
+ );
+
+ // Returns a possible iterator, avoid "Serialization of 'Closure' is not allowed"
+ if ( $result instanceof \Iterator ) {
+ $result = iterator_to_array( $result );
+ }
+
+ $container->set( $pvid, $result );
+
+ $this->blobStore->save(
+ $container
+ );
+
+ $this->appendToList( $sid, $subject );
+
+ return $result;
+ }
+
+ /**
+ * @see EntityLookup::getPropertySubjects
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getPropertySubjects( DIProperty $property, DataItem $dataItem = null, RequestOptions $requestOptions = null ) {
+
+ // The cache is not used for $dataItem === null (means all values for
+ // the given property are returned)
+ if ( $dataItem === null || !$dataItem instanceof DIWikiPage || !$this->blobStore->canUse() || !$this->isEnabledFeature( SMW_VL_PS ) ) {
+ return $this->entityLookup->getPropertySubjects( $property, $dataItem, $requestOptions );
+ }
+
+ // Added as linked list as we keep the container ttl different from
+ // that of the main container
+ $sid = $this->getHashFrom(
+ $dataItem,
+ 'ps'
+ );
+
+ $container = $this->blobStore->read( $sid );
+
+ $psid = HashBuilder::createFromContent(
+ [
+ $property->getKey(),
+ $property->isInverse(),
+ (array)$requestOptions,
+ self::VERSION
+ ],
+ 'ps:'
+ );
+
+ if ( $container->has( $psid ) ) {
+ return $container->get( $psid );
+ }
+
+ $result = $this->entityLookup->getPropertySubjects(
+ $property,
+ $dataItem,
+ $requestOptions
+ );
+
+ // Returns an iterator, avoid "Serialization of 'Closure' is not allowed"
+ if ( $result instanceof \Iterator ) {
+ $result = iterator_to_array( $result );
+ }
+
+ $container->set( $psid, $result );
+
+ // We set a short lifetime (5 min) in order to cache repeated requests but
+ // avoiding a complex invalidation during a subject update otherwise all
+ // properties of a container would require scanning and removal
+ $container->setExpiryInSeconds( 60 * 5 );
+
+ $this->blobStore->save(
+ $container
+ );
+
+ $this->appendToList( $sid, $dataItem );
+
+ return $result;
+ }
+
+ /**
+ * @see EntityLookup::getAllPropertySubjects
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getAllPropertySubjects( DIProperty $property, RequestOptions $requestOptions = null ) {
+ return $this->entityLookup->getAllPropertySubjects( $property, $requestOptions );
+ }
+
+ /**
+ * @see EntityLookup::getInProperties
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getInProperties( DataItem $object, RequestOptions $requestOptions = null ) {
+ return $this->entityLookup->getInProperties( $object, $requestOptions );
+ }
+
+ /**
+ * Remove a cache item that appears during an alteration action (update,
+ * change, delete) to ensure that we always have the correct set of matches.
+ *
+ * @since 2.3
+ */
+ public function invalidateCache() {
+
+ $args = func_get_args();
+ $subject = array_shift( $args );
+
+ if ( !$this->blobStore->canUse() || !$subject instanceof DIWikiPage ) {
+ return null;
+ }
+
+ // Remove a redirect target subject directly
+ $redirects = $this->getSemanticData( $subject )->getPropertyValues(
+ new DIProperty( '_REDI' )
+ );
+
+ foreach ( $redirects as $redirectTarget ) {
+ $this->blobStore->delete( $this->getHashFrom(
+ $redirectTarget
+ ) );
+ }
+
+ $sid = $this->getHashFrom( $subject );
+
+ // Remove all linked objects
+ $container = $this->blobStore->read( $sid );
+
+ if ( $container->has( 'list' ) ) {
+ foreach ( array_keys( $container->get( 'list' ) ) as $id ) {
+ $this->blobStore->delete( $id );
+ }
+ }
+
+ $this->blobStore->delete( $sid );
+ }
+
+ /**
+ * Ensures that new redirects are resolved while a value is still kept
+ * in cache (internally it uses getPropertyValues hence we don't loose
+ * much as objects are reused during the lookup).
+ */
+ private function resolveRedirectTargets( array $results ) {
+
+ $dataItems = [];
+
+ foreach ( $results as $dataItem ) {
+ $dataItems[] = $this->redirectTargetLookup->findRedirectTarget( $dataItem );
+ }
+
+ return $dataItems;
+ }
+
+ /**
+ * The subobject is attached to a root subject therefore using the root as
+ * identifier to allow it to be invalidated at once with all other subobjects
+ * that relate to a subject
+ */
+ private function getHashFrom( DIWikiPage $subject, $suffix = '' ) {
+ return md5( HashBuilder::createHashIdFromSegments(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki()
+ ) . $suffix );
+ }
+
+ private function appendToList( $id, $subject ) {
+
+ // Store the id with the main subject
+ $container = $this->blobStore->read(
+ $this->getHashFrom( $subject )
+ );
+
+ // Use the id as key to avoid unnecessary duplicate entries when
+ // employing append
+ $container->append(
+ 'list',
+ [ $id => true ]
+ );
+
+ $this->blobStore->save(
+ $container
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php
new file mode 100644
index 00000000..3826ed82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/CachingSemanticDataLookup.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableDefinition;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingSemanticDataLookup {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * Cache for SemanticData dataItems, indexed by SMW ID.
+ *
+ * @var array
+ */
+ private static $data = [];
+
+ /**
+ * Like SMWSQLStore3::data, but containing flags indicating
+ * completeness of the SemanticData objs.
+ *
+ * @var array
+ */
+ private static $state = [];
+
+ /**
+ * >0 while getSemanticData runs, used to prevent nested calls from clearing
+ * the cache while another call runs and is about to fill it with data
+ *
+ * @var int
+ */
+ private static $lookupCount = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param SemanticDataLookup $semanticDataLookup
+ * @param Cache|null $cache
+ */
+ public function __construct( SemanticDataLookup $semanticDataLookup, Cache $cache = null ) {
+ $this->semanticDataLookup = $semanticDataLookup;
+ $this->cache = $cache;
+
+ if ( $this->cache === null ) {
+ $this->cache = new NullCache();
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function lockCache() {
+ self::$lookupCount++;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function unlockCache() {
+ self::$lookupCount--;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ */
+ public function invalidateCache( $id ) {
+ unset( self::$data[$id] );
+ unset( self::$state[$id] );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public static function clear() {
+ self::$data = [];
+ self::$state = [];
+ self::$lookupCount = 0;
+ }
+
+ /**
+ * Helper method to make sure there is a cache entry for the data about
+ * the given subject with the given ID.
+ *
+ * @todo The management of this cache should be revisited.
+ *
+ * @since 3.0
+ *
+ * @param int $id
+ * @param DIWikiPage $subject
+ */
+ public function initLookupCache( $id, DIWikiPage $subject ) {
+
+ // *** Prepare the cache ***//
+ if ( !isset( self::$data[$id] ) ) {
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData( $subject );
+ self::$state[$id] = [];
+ }
+
+ // Issue #622
+ // If a redirect was cached preceding this request and points to the same
+ // subject id ensure that in all cases the requested subject matches with
+ // the selected DB id
+ if ( self::$data[$id]->getSubject()->getHash() !== $subject->getHash() ) {
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData( $subject );
+ self::$state[$id] = [];
+ }
+
+ // It is not so easy to find the sweet spot between cache size and
+ // performance gains (both memory and time), The value of 20 was chosen
+ // by profiling runtimes for large inline queries and heavily annotated
+ // pages. However, things might have changed in the meantime ...
+ if ( ( count( self::$data ) > 20 ) && ( self::$lookupCount == 1 ) ) {
+ self::$data = [ $id => self::$data[$id] ];
+ self::$state = [ $id => self::$state[$id] ];
+ }
+ }
+
+ /**
+ * Set the semantic data lookup cache to hold exactly the given value for the
+ * given ID.
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param SemanticData $semanticData
+ */
+ public function setLookupCache( $id, SemanticData $semanticData ) {
+
+ self::$data[$id] = $this->semanticDataLookup->newStubSemanticData(
+ $semanticData
+ );
+
+ self::$state[$id] = $this->semanticDataLookup->getTableUsageInfo(
+ $semanticData
+ );
+ }
+
+ /**
+ * Helper method to make sure there is a cache entry for the data about
+ * the given subject with the given ID.
+ *
+ * @since 3.0
+ *
+ * @param int $id
+ * @param DIWikiPage $subject
+ */
+ public function getSemanticDataById( $id ) {
+
+ if ( !isset( self::$data[$id] ) ) {
+ throw new RuntimeException( 'Data are not initialized.' );
+ }
+
+ return self::$data[$id];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param DIProperty $property
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return RequestOptions|null
+ */
+ public function newRequestOptions( PropertyTableDefinition $propertyTableDef, DIProperty $property, RequestOptions $requestOptions = null ) {
+ return $this->semanticDataLookup->newRequestOptions( $propertyTableDef, $property, $requestOptions );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param DataItem $dataItem
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param RequestOptions $requestOptions
+ *
+ * @return RequestOptions|null
+ */
+ public function fetchSemanticData( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef, RequestOptions $requestOptions = null ) {
+ return $this->semanticDataLookup->fetchSemanticData( $id, $dataItem, $propertyTableDef, $requestOptions );
+ }
+
+ /**
+ * Fetch and cache the data about one subject for one particular table
+ *
+ * @param integer $id
+ * @param DIWikiPage $subject
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return SemanticData
+ */
+ public function getSemanticDataFromTable( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef, RequestOptions $requestOptions = null ) {
+
+ // Avoid the cache when a request is constrainted
+ if ( $requestOptions !== null || !$dataItem instanceof DIWikiPage ) {
+ return $this->semanticDataLookup->getSemanticData( $id, $dataItem, $propertyTableDef, $requestOptions );
+ }
+
+ return $this->fetchFromCache( $id, $dataItem, $propertyTableDef );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return StubSemanticData
+ */
+ public function newStubSemanticData( DIWikiPage $subject ) {
+ return $this->semanticDataLookup->newStubSemanticData( $subject );
+ }
+
+ private function fetchFromCache( $id, DataItem $dataItem = null, PropertyTableDefinition $propertyTableDef ) {
+
+ // Do not clear the cache when called recursively.
+ $this->lockCache();
+ $this->initLookupCache( $id, $dataItem );
+
+ // @see also setLookupCache
+ $name = $propertyTableDef->getName();
+
+ if ( isset( self::$state[$id][$name] ) ) {
+ $this->unlockCache();
+ return self::$data[$id];
+ }
+
+ $data = $this->semanticDataLookup->fetchSemanticData(
+ $id,
+ $dataItem,
+ $propertyTableDef
+ );
+
+ foreach ( $data as $d ) {
+ self::$data[$id]->addPropertyStubValue( reset( $d ), end( $d ) );
+ }
+
+ self::$state[$id][$name] = true;
+
+ $this->unlockCache();
+
+ return self::$data[$id];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBlobHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBlobHandler.php
new file mode 100644
index 00000000..f88ed878
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBlobHandler.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+
+/**
+ * This class implements Store access to blob (string) data items.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DIBlobHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => $this->getCharFieldType()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => $this->getCharFieldType()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+
+ 's_id,o_hash',
+
+ // pvalue select
+ // SELECT p_id,o_hash FROM `smw_di_blob` WHERE p_id = '310174' AND ( o_hash LIKE '%test%' ) LIMIT 11
+ 'p_id,o_hash',
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexHint( $key ) {
+
+ // Store::getPropertySubjects has seen to choose the wrong index
+
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids`
+ // INNER JOIN `smw_di_blob` AS t1 FORCE INDEX(s_id) ON t1.s_id=smw_id
+ // WHERE t1.p_id='310174' AND smw_iw!=':smw'
+ // AND smw_iw!=':smw-delete' AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id LIMIT 26
+ //
+ // 137.4161ms SMWSQLStore3Readers::getPropertySubjects
+ //
+ // vs.
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids`
+ // INNER JOIN `smw_di_blob` AS t1 ON t1.s_id=smw_id
+ // WHERE t1.p_id='310174' AND smw_iw!=':smw' AND smw_iw!=':smw-delete'
+ // AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id LIMIT 26
+ //
+ // 23482.1451ms SMWSQLStore3Readers::getPropertySubjects
+ if ( 'property.subjects' && $this->isDbType( 'mysql' ) ) {
+ return 's_id';
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+
+ $isKeyword = $dataItem->getOption( 'is.keyword' );
+ $text = $dataItem->getString();
+
+ return [
+ 'o_hash' => $isKeyword ? $dataItem->normalize( $text ) : $this->makeHash( $text )
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+
+ $isKeyword = $dataItem->getOption( 'is.keyword' );
+
+ $text = htmlspecialchars_decode( trim( $dataItem->getString() ), ENT_QUOTES );
+ $hash = $isKeyword ? $dataItem->normalize( $text ) : $this->makeHash( $text );
+
+ if ( $this->isDbType( 'postgres' ) ) {
+ $text = pg_escape_bytea( $text );
+ }
+
+ if ( mb_strlen( $text ) <= $this->getMaxLength() && !$isKeyword ) {
+ $text = null;
+ }
+
+ return [
+ 'o_blob' => $text,
+ 'o_hash' => $hash,
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_hash';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_hash';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( !is_array( $dbkeys ) || count( $dbkeys ) != 2 ) {
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+ if ( $this->isDbType( 'postgres' ) ) {
+ $dbkeys[0] = pg_unescape_bytea( $dbkeys[0] );
+ }
+
+ // empty blob: use "hash" string
+ if ( $dbkeys[0] == '' ) {
+ return new DIBlob( $dbkeys[1] );
+ }
+
+ return new DIBlob( $dbkeys[0] );
+ }
+
+ /**
+ * Method to make a hashed representation for strings of length greater
+ * than DIBlobHandler::getMaxLength to be used for selecting and sorting.
+ *
+ * @since 1.8
+ * @param $string string
+ *
+ * @return string
+ */
+ private function makeHash( $string ) {
+
+ $length = $this->getMaxLength();
+
+ if( mb_strlen( $string ) <= $length ) {
+ return $string;
+ }
+
+ return mb_substr( $string, 0, $length - 32 ) . md5( $string );
+ }
+
+ /**
+ * Maximal number of bytes (chars) to be stored in the hash field of
+ * the table. Must not be bigger than 255 (the length of our VARCHAR
+ * field in the DB). Strings that are longer than this will be stored
+ * as a blob, and the hash will only start with the original string
+ * but the last 32 bytes are used for a hash. So the minimal portion
+ * of the string that is stored literally in the hash is 32 chars
+ * less.
+ *
+ * The value of 72 was chosen since it leads to a smaller index size
+ * at the cost of needing more blobs in cases where many strings are
+ * of length 73 to 255. But keeping the index small seems more
+ * important than saving disk space. Also, with 72 bytes there are at
+ * least 40 bytes of content available for sorting and prefix matching,
+ * which should be more than enough in most contexts.
+ *
+ * @since 1.8
+ *
+ * Using `SMW_FIELDT_CHAR_LONG` as option in `smwgFieldTypeFeatures`
+ * will extend the field size to 300 and expands the maximum matchable
+ * string length to 300-32 for LIKE/NLIKE queries.
+ *
+ * @since 3.0
+ */
+ private function getMaxLength() {
+
+ $length = 72;
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) ) {
+ $length = FieldType::CHAR_LONG_LENGTH;
+ }
+
+ return $length;
+ }
+
+ private function getCharFieldType() {
+
+ $fieldType = FieldType::FIELD_TITLE;
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_NOCASE ) ) {
+ $fieldType = FieldType::TYPE_CHAR_NOCASE;
+ }
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) ) {
+ $fieldType = FieldType::TYPE_CHAR_LONG;
+ }
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) && $this->isEnabledFeature( SMW_FIELDT_CHAR_NOCASE ) ) {
+ $fieldType = FieldType::TYPE_CHAR_LONG_NOCASE;
+ }
+
+ return $fieldType;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBooleanHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBooleanHandler.php
new file mode 100644
index 00000000..5b6a2b12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIBooleanHandler.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDIBoolean as DIBoolean;
+
+/**
+ * This class implements Store access to Boolean data items.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DIBooleanHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_value' => FieldType::TYPE_BOOL
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_value' => FieldType::TYPE_BOOL
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [
+ 'o_value' => $dataItem->getBoolean() ? 1 : 0,
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+ return [
+ 'o_value' => $dataItem->getBoolean() ? 1 : 0,
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_value';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_value';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+ global $wgDBtype;
+
+ //PgSQL returns as t and f and need special handling http://archives.postgresql.org/pgsql-php/2010-02/msg00005.php
+ if ( $wgDBtype == 'postgres' ) {
+ $value = ( $dbkeys == 't' );
+ } else {
+ $value = ( $dbkeys == '1' );
+ }
+
+ return new DIBoolean( $value );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIConceptHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIConceptHandler.php
new file mode 100644
index 00000000..a9c07193
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIConceptHandler.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\DIConcept;
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+
+/**
+ * This class implements Store access to Concept data items.
+ *
+ * @note The table layout and behavior of this class is not coherent with the
+ * way that other DIs work. This is because of the unfortunate use of the
+ * concept table to store extra cache data, but also due to the design of
+ * concept DIs. This will be cleaned up at some point.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DIConceptHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'concept_txt' => FieldType::TYPE_BLOB,
+ 'concept_docu' => FieldType::TYPE_BLOB,
+ 'concept_features' => FieldType::FIELD_NAMESPACE,
+ 'concept_size' => FieldType::FIELD_NAMESPACE,
+ 'concept_depth' => FieldType::FIELD_NAMESPACE,
+ 'cache_date' => FieldType::TYPE_INT_UNSIGNED,
+ 'cache_count' => FieldType::TYPE_INT_UNSIGNED
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'concept_txt' => FieldType::TYPE_BLOB,
+ 'concept_docu' => FieldType::TYPE_BLOB,
+ 'concept_features' => FieldType::FIELD_NAMESPACE,
+ 'concept_size' => FieldType::FIELD_NAMESPACE,
+ 'concept_depth' => FieldType::FIELD_NAMESPACE,
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [
+ 'concept_txt' => $dataItem->getConceptQuery(),
+ 'concept_docu' => $dataItem->getDocumentation(),
+ 'concept_features' => $dataItem->getQueryFeatures(),
+ 'concept_size' => $dataItem->getSize(),
+ 'concept_depth' => $dataItem->getDepth()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+ return [
+ 'concept_txt' => $dataItem->getConceptQuery(),
+ 'concept_docu' => $dataItem->getDocumentation(),
+ 'concept_features' => $dataItem->getQueryFeatures(),
+ 'concept_size' => $dataItem->getSize(),
+ 'concept_depth' => $dataItem->getDepth()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'concept_txt';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'concept_txt';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( is_array( $dbkeys) && count( $dbkeys ) == 5 ) {
+ return new DIConcept(
+ $dbkeys[0],
+ smwfXMLContentEncode( $dbkeys[1] ),
+ $dbkeys[2],
+ $dbkeys[3],
+ $dbkeys[4]
+ );
+ }
+
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIGeoCoordinateHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIGeoCoordinateHandler.php
new file mode 100644
index 00000000..74dcfd07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIGeoCoordinateHandler.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDIGeoCoord as DIGeoCoord;
+
+/**
+ * This class implements store access to DIGeoCoord data items.
+ *
+ * @note The table layout and behavior of this class is not coherent with the
+ * way that other DIs work. This is because of the unfortunate use of the
+ * concept table to store extra cache data, but also due to the design of
+ * concept DIs. This will be cleaned up at some point.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DIGeoCoordinateHandler extends DataItemHandler {
+
+ /**
+ * Coordinates have three fields: a string version to keep the
+ * serialized value (exact), and two floating point columns for
+ * latitude and longitude (inexact, useful for bounding box selects).
+ * Altitude is not stored in an extra column since no operation uses
+ * this for anything so far.
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE,
+ 'o_lat' => FieldType::TYPE_DOUBLE,
+ 'o_lon' => FieldType::TYPE_DOUBLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+ 'p_id,o_serialized',
+ 'o_lat,o_lon'
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [
+ 'o_serialized' => $dataItem->getSerialization()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+ return [
+ 'o_serialized' => $dataItem->getSerialization(),
+ 'o_lat' => (string)$dataItem->getLatitude(),
+ 'o_lon' => (string)$dataItem->getLongitude()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_serialized';
+ }
+
+ /**
+ * Coordinates do not have a general string version that
+ * could be used for string search, so this method returns
+ * no label column (empty string).
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return '';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( is_string( $dbkeys ) ) {
+ return DIGeoCoord::doUnserialize( $dbkeys );
+ }
+
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DINumberHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DINumberHandler.php
new file mode 100644
index 00000000..11a952aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DINumberHandler.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDINumber as DINumber;
+
+/**
+ * This class implements Store access to Number data items.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DINumberHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE,
+ 'o_sortkey' => FieldType::TYPE_DOUBLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+
+ // API module pvalue lookup
+ 'p_id,o_serialized',
+ 'p_id,o_sortkey',
+
+ // QueryEngine::getInstanceQueryResult
+ 's_id,p_id,o_sortkey',
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexHint( $key ) {
+
+ // Store::getPropertySubjects has seen to choose the wrong index
+
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids` INNER JOIN `smw_di_number` AS t1 FORCE INDEX(s_id) ON t1.s_id=smw_id
+ // WHERE t1.p_id='310194' AND smw_iw!=':smw' AND smw_iw!=':smw-delete' AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id
+ // LIMIT 26
+ //
+ // 584.9450ms SMWSQLStore3Readers::getPropertySubjects
+ //
+ // vs.
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids`
+ // INNER JOIN `smw_di_number` AS t1 ON t1.s_id=smw_id
+ // WHERE t1.p_id='310194' AND smw_iw!=':smw' AND smw_iw!=':smw-delete' AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id
+ // LIMIT 26
+ //
+ // 21448.2622ms SMWSQLStore3Readers::getPropertySubjects
+ if ( 'property.subjects' && $this->isDbType( 'mysql' ) ) {
+ return 's_id';
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [
+ 'o_sortkey' => floatval( $dataItem->getNumber() )
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+ return [
+ 'o_serialized' => $dataItem->getSerialization(),
+ 'o_sortkey' => floatval( $dataItem->getNumber() )
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_sortkey';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_serialized';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( is_string( $dbkeys ) ) {
+ return DINumber::doUnserialize( $dbkeys );
+ }
+
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DITimeHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DITimeHandler.php
new file mode 100644
index 00000000..f9805c24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DITimeHandler.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDITime as DITime;
+
+/**
+ * This class implements Store access to Time data items.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DITimeHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE,
+ 'o_sortkey' => FieldType::TYPE_DOUBLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_serialized' => FieldType::FIELD_TITLE
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+
+ // API module pvalue lookup
+ 'p_id,o_serialized',
+ 'p_id,o_sortkey',
+
+ // SMWSQLStore3Readers::fetchSemanticData
+ // SELECT p.smw_title as prop,o_serialized AS v0, o_sortkey AS v2
+ // FROM `smw_di_time` INNER JOIN `smw_object_ids` AS p ON
+ // p_id=p.smw_id WHERE s_id='104822' 7.9291ms
+ // ... FROM `smw_fpt_sobj` INNER JOIN `smw_object_ids` AS o0 ON
+ // o_id=o0.smw_id WHERE s_id='104322'
+ 's_id,p_id,o_sortkey,o_serialized',
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexHint( $key ) {
+
+ if ( 'property.subjects' && $this->isDbType( 'mysql' ) ) {
+ return 's_id';
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [ 'o_sortkey' => $dataItem->getSortKey() ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+ return [
+ 'o_serialized' => $dataItem->getSerialization(),
+ 'o_sortkey' => $dataItem->getSortKey()
+ ];
+ }
+
+ /**
+ * This type is sorted by a numerical sortkey that maps time values to
+ * a time line.
+ *
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_sortkey';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_serialized';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( is_string( $dbkeys ) ) {
+ return DITime::doUnserialize( $dbkeys );
+ }
+
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIUriHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIUriHandler.php
new file mode 100644
index 00000000..f9269b3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIUriHandler.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+use SMWDIUri as DIUri;
+
+/**
+ * This class implements Store access to Uri data items.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+class DIUriHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => $this->getCharFieldType()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => $this->getCharFieldType()
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+ 'p_id,o_serialized',
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexHint( $key ) {
+
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids`
+ // INNER JOIN `smw_di_uri` AS t1
+ // FORCE INDEX(s_id) ON t1.s_id=smw_id
+ // WHERE t1.p_id='310165' AND smw_iw!=':smw' AND smw_iw!=':smw-delete' AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id LIMIT 26
+ //
+ // 606.8370ms SMWSQLStore3Readers::getPropertySubjects
+ //
+ // vs.
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids`
+ // INNER JOIN `smw_di_uri` AS t1 ON t1.s_id=smw_id
+ // WHERE t1.p_id='310165' AND smw_iw!=':smw' AND smw_iw!=':smw-delete' AND smw_iw!=':smw-redi'
+ // GROUP BY smw_sort, smw_id LIMIT 26
+ //
+ // 8052.2099ms SMWSQLStore3Readers::getPropertySubjects
+ if ( 'property.subjects' && $this->isDbType( 'mysql' ) ) {
+ return 's_id';
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+ return [ 'o_serialized' => rawurldecode( $dataItem->getSerialization() ) ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+
+ $serialization = rawurldecode( $dataItem->getSerialization() );
+ $text = mb_strlen( $serialization ) <= $this->getMaxLength() ? null : $serialization;
+
+ // bytea type handling
+ if ( $text !== null && $this->isDbType( 'postgres' ) ) {
+ $text = pg_escape_bytea( $text );
+ }
+
+ return [
+ 'o_blob' => $text,
+ 'o_serialized' => $serialization,
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_serialized';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_serialized';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( !is_array( $dbkeys ) || count( $dbkeys ) != 2 ) {
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+ if ( $this->isDbType( 'postgres' ) ) {
+ $dbkeys[0] = pg_unescape_bytea( $dbkeys[0] );
+ }
+
+ return DIUri::doUnserialize( $dbkeys[0] == '' ? $dbkeys[1] : $dbkeys[0] );
+ }
+
+ private function getMaxLength() {
+
+ $length = 255;
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) ) {
+ $length = FieldType::CHAR_LONG_LENGTH;
+ }
+
+ return $length;
+ }
+
+ private function getCharFieldType() {
+
+ $fieldType = FieldType::FIELD_TITLE;
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_NOCASE ) ) {
+ $fieldType = FieldType::TYPE_CHAR_NOCASE;
+ }
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) ) {
+ $fieldType = FieldType::TYPE_CHAR_LONG;
+ }
+
+ if ( $this->isEnabledFeature( SMW_FIELDT_CHAR_LONG ) && $this->isEnabledFeature( SMW_FIELDT_CHAR_NOCASE ) ) {
+ $fieldType = FieldType::TYPE_CHAR_LONG_NOCASE;
+ }
+
+ return $fieldType;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIWikiPageHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIWikiPageHandler.php
new file mode 100644
index 00000000..dcad8b7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DIHandlers/DIWikiPageHandler.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\DIHandlers;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+
+/**
+ * DataItemHandler for dataitems of type DIWikiPage.
+ *
+ * This handler is slightly different from other handlers since wikipages are
+ * stored in a separate table and referred to by numeric IDs. The handler thus
+ * returns IDs in most cases, but expects data from the SMW IDs table (with
+ * DBkey, namespace, interwiki, subobjectname) to be given for creating new
+ * dataitems. The store recognizes this special behavior from the field type
+ * 'p' that the handler reports for its only data field.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ * @author Markus Kroetzsch
+ */
+class DIWikiPageHandler extends DataItemHandler {
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableFields() {
+ return [ 'o_id' => FieldType::FIELD_ID ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getFetchFields() {
+ return [ 'o_id' => FieldType::FIELD_ID ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getTableIndexes() {
+ return [
+ 'o_id',
+
+ // SMWSQLStore3Readers::getPropertySubjects
+ 'p_id,s_id',
+
+ // SMWSQLStore3Readers::fetchSemanticData
+ // ... FROM `smw_fpt_sobj` INNER JOIN `smw_object_ids` AS o0 ON
+ // o_id=o0.smw_id WHERE s_id='104322'
+ 's_id,o_id',
+
+ // SMWSQLStore3Readers::fetchSemanticData
+ // ... FROM `smw_di_wikipage` INNER JOIN `smw_object_ids` AS p ON
+ // p_id=p.smw_id INNER JOIN `smw_object_ids` AS o0 ON o_id=o0.smw_id
+ // WHERE s_id='104815'
+ 's_id,p_id,o_id',
+
+ // QueryEngine::getInstanceQueryResult
+ // ... INNER JOIN `smw_fpt_inst` AS t3 ON t2.smw_id=t3.s_id WHERE
+ // (t2.smw_namespace='0' AND (t3.o_id='56')
+ 'o_id,s_id',
+
+ // QueryEngine::getInstanceQueryResult
+ //'p_id,o_id,s_sort',
+
+ // SMWSQLStore3Readers::getPropertySubjects
+ // SELECT DISTINCT s_id FROM `smw_fpt_sobj` ORDER BY s_sort
+ //'s_sort,s_id',
+
+ // SELECT DISTINCT s_id FROM `smw_fpt_subp` WHERE o_id='96' ORDER BY s_sort
+ //'o_id,s_sort,s_id',
+
+ // In-property lookup
+ 'o_id,p_id',
+ //'o_id,p_id,s_sort',
+
+ // SMWSQLStore3Readers::getPropertySubjects
+ // SELECT DISTINCT s_id FROM `smw_di_wikipage` WHERE (p_id='64' AND o_id='104') ORDER BY s_sort ASC
+ //'o_id,p_id,s_id,s_sort'
+ ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getWhereConds( DataItem $dataItem ) {
+
+ $oid = $this->store->getObjectIds()->getSMWPageID(
+ $dataItem->getDBkey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $dataItem->getSubobjectName()
+ );
+
+ return [ 'o_id' => $oid ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getInsertValues( DataItem $dataItem ) {
+
+ $oid = $this->store->getObjectIds()->makeSMWPageID(
+ $dataItem->getDBkey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $dataItem->getSubobjectName()
+ );
+
+ return [ 'o_id' => $oid ];
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getIndexField() {
+ return 'o_id';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function getLabelField() {
+ return 'o_id';
+ }
+
+ /**
+ * @since 1.8
+ *
+ * {@inheritDoc}
+ */
+ public function dataItemFromDBKeys( $dbkeys ) {
+
+ if ( !is_array( $dbkeys ) || count( $dbkeys ) != 5 ) {
+ throw new DataItemHandlerException( 'Failed to create data item from DB keys.' );
+ }
+
+ $namespace = intval( $dbkeys[1] );
+
+ // Correctly interpret internal property keys
+ if ( $namespace == SMW_NS_PROPERTY && $dbkeys[0] != '' &&
+ $dbkeys[0]{0} == '_' && $dbkeys[2] == '' ) {
+
+ try {
+ $property = new DIProperty( $dbkeys[0] );
+ } catch( PredefinedPropertyLabelMismatchException $e ) {
+ // Most likely an outdated, no longer existing predefined
+ // property, mark it as outdate
+ $dbkeys[2] = SMW_SQL3_SMWIW_OUTDATED;
+
+ return $this->newDiWikiPage( $dbkeys );
+ }
+
+ $wikipage = $property->getCanonicalDiWikiPage( $dbkeys[4] );
+
+ if ( !is_null( $wikipage ) ) {
+ return $wikipage;
+ }
+ }
+
+ return $this->newDiWikiPage( $dbkeys );
+ }
+
+ private function newDiWikiPage( $dbkeys ) {
+
+ $diWikiPage = new DIWikiPage(
+ $dbkeys[0],
+ intval( $dbkeys[1] ),
+ $dbkeys[2],
+ $dbkeys[4]
+ );
+
+ $diWikiPage->setSortKey( $dbkeys[3] );
+
+ return $diWikiPage;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandler.php
new file mode 100644
index 00000000..6293807b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandler.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+
+/**
+ * Classes extending this represent all store layout that is known about a certain dataitem
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ */
+abstract class DataItemHandler {
+
+ /**
+ * @var SQLStore
+ */
+ protected $store;
+
+ /**
+ * @var integer
+ */
+ protected $fieldTypeFeatures = false;
+
+ /**
+ * @var null|string
+ */
+ private $dbType;
+
+ /**
+ * @since 1.8
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $fieldTypeFeatures
+ */
+ public function setFieldTypeFeatures( $fieldTypeFeatures ) {
+ $this->fieldTypeFeatures = $fieldTypeFeatures;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function isEnabledFeature( $feature ) {
+ return ( (int)$this->fieldTypeFeatures & $feature ) != 0;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function isDbType( $dbType ) {
+
+ if ( $this->dbType === null ) {
+ $this->dbType = $this->store->getConnection( 'mw.db' )->getType();
+ }
+
+ return $this->dbType === $dbType;
+ }
+
+ /**
+ * Return array of fields for a DI type.
+ *
+ * Tables declare value columns ("object fields") by specifying their
+ * name and type. Types are given using letters:
+ * - t for strings of the same maximal length as MediaWiki title names,
+ * - l for arbitrarily long strings; searching/sorting with such data
+ * may be limited for performance reasons,
+ * - w for strings as used in MediaWiki for encoding interwiki prefixes
+ * - n for namespace numbers (or other similar integers)
+ * - f for floating point numbers of double precision
+ * - p for a reference to an SMW ID as stored in the SMW IDs table;
+ * this corresponds to a data entry of ID "tnwt".
+ *
+ * @since 1.8
+ * @return array
+ */
+ abstract public function getTableFields();
+
+ /**
+ * Return an array with all the field names and types that need to be
+ * retrieved from the database in order to create a dataitem using
+ * dataItemFromDBKeys(). The result format is the same as for
+ * getTableFields(), but usually with fewer field names.
+ *
+ * @note In the future, we will most likely use a method that return
+ * only a single field name. Currently, we still need an array for
+ * concepts.
+ *
+ * @since 1.8
+ * @return array
+ */
+ abstract public function getFetchFields();
+
+ /**
+ * Return an array of additional indexes that should be provided for
+ * the table using this DI handler. By default, SMWSQLStore3 will
+ * already create indexes for all standard select operations, based
+ * on the indexfield provided by getIndexField(). Hence, most handlers
+ * do not need to define any indexes.
+ *
+ * @since 1.8
+ * @return array
+ */
+ public function getTableIndexes() {
+ return [];
+ }
+
+ /**
+ * Provides a possibility to return a specific index hint for a domain.
+ *
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ public function getIndexHint( $key ) {
+ return '';
+ }
+
+ /**
+ * Return an array of fields=>values to conditions (WHERE part) in SQL
+ * queries for the given DataItem. This method can return fewer
+ * fields than getInstertValues as long as they are enough to identify
+ * an item for search.
+ *
+ * @since 1.8
+ * @param DataItem $dataItem
+ * @return array
+ */
+ abstract public function getWhereConds( DataItem $dataItem );
+
+ /**
+ * Return an array of fields=>values that is to be inserted when
+ * writing the given DataItem to the database. Values should be set
+ * for all columns, even if NULL. This array is used to perform all
+ * insert operations into the DB.
+ *
+ * @since 1.8
+ * @param DataItem $dataItem
+ * @return array
+ */
+ abstract public function getInsertValues( DataItem $dataItem );
+
+ /**
+ * Return the field used to select this type of DataItem. In
+ * particular, this identifies the column that is used to sort values
+ * of this kind. Every type of data returns a non-empty string here.
+ *
+ * @since 1.8
+ * @return string
+ */
+ abstract public function getIndexField();
+
+ /**
+ * Return the label field for this type of DataItem. This should be
+ * a string column in the database table that can be used for selecting
+ * values using criteria such as "starts with". The return value can be
+ * empty if this is not supported. This is preferred for DataItem
+ * classes that do not have an obvious canonical string writing anyway.
+ *
+ * The return value can be a column name or the empty string (if the
+ * give type of DataItem does not have a label field).
+ *
+ * @since 1.8
+ * @return string
+ */
+ abstract public function getLabelField();
+
+ /**
+ * Returns the expected sort field.
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getSortField() {
+ return '';
+ }
+
+ /**
+ * Create a dataitem from an array of DB keys or a single DB key
+ * string. May throw an DataItemException if the given DB keys
+ * cannot be converted back into a dataitem. Each implementation
+ * of this method must otherwise run without errors for both array
+ * and string inputs.
+ *
+ * @since 1.8
+ * @param array|string $dbkeys
+ * @throws DataItemException
+ * @return DataItem
+ */
+ abstract public function dataItemFromDBKeys( $dbkeys );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandlerDispatcher.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandlerDispatcher.php
new file mode 100644
index 00000000..b17ce0cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/DataItemHandlerDispatcher.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\SQLStore\EntityStore\DIHandlers\DIBlobHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DIBooleanHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DIConceptHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DIGeoCoordinateHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DINumberHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DITimeHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DIUriHandler;
+use SMW\SQLStore\EntityStore\DIHandlers\DIWikiPageHandler;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemHandlerDispatcher {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var array
+ */
+ private $handlers = [];
+
+ /**
+ * @var integer
+ */
+ private $fieldTypeFeatures = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $fieldTypeFeatures
+ */
+ public function setFieldTypeFeatures( $fieldTypeFeatures ) {
+ $this->fieldTypeFeatures = $fieldTypeFeatures;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ *
+ * @return DIHandler
+ * @throws RuntimeException
+ */
+ public function getHandlerByType( $type ) {
+
+ if ( !isset( $this->handlers[$type] ) ) {
+ $this->handlers[$type] = $this->newHandlerByType( $type );
+ }
+
+ // $this->handlers[$type]->setFieldTypeFeatures(
+ // $this->fieldTypeFeatures
+ // );
+
+ return $this->handlers[$type];
+ }
+
+ private function newHandlerByType( $type ) {
+
+ switch ( $type ) {
+ case DataItem::TYPE_NUMBER:
+ $handler = new DINumberHandler( $this->store );
+ break;
+ case DataItem::TYPE_BLOB:
+ $handler = new DIBlobHandler( $this->store );
+ break;
+ case DataItem::TYPE_BOOLEAN:
+ $handler = new DIBooleanHandler( $this->store );
+ break;
+ case DataItem::TYPE_URI:
+ $handler = new DIUriHandler( $this->store );
+ break;
+ case DataItem::TYPE_TIME:
+ $handler = new DITimeHandler( $this->store );
+ break;
+ case DataItem::TYPE_GEO:
+ $handler = new DIGeoCoordinateHandler( $this->store );
+ break;
+ case DataItem::TYPE_WIKIPAGE:
+ $handler = new DIWikiPageHandler( $this->store );
+ break;
+ case DataItem::TYPE_CONCEPT:
+ $handler = new DIConceptHandler( $this->store );
+ break;
+ case DataItem::TYPE_PROPERTY:
+ throw new DataItemHandlerException( "There is no DI handler for DataItem::TYPE_PROPERTY." );
+ case DataItem::TYPE_CONTAINER:
+ throw new DataItemHandlerException( "There is no DI handler for DataItem::TYPE_CONTAINER." );
+ case DataItem::TYPE_ERROR:
+ throw new DataItemHandlerException( "There is no DI handler for DataItem::TYPE_ERROR." );
+ default:
+ throw new DataItemHandlerException( "The value \"$type\" is not a valid dataitem ID." );
+ }
+
+ $handler->setFieldTypeFeatures(
+ $this->fieldTypeFeatures
+ );
+
+ return $handler;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/Exception/DataItemHandlerException.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/Exception/DataItemHandlerException.php
new file mode 100644
index 00000000..514bef65
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/Exception/DataItemHandlerException.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemHandlerException extends RuntimeException {
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdCacheManager.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdCacheManager.php
new file mode 100644
index 00000000..ca02bafb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdCacheManager.php
@@ -0,0 +1,226 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\SQLStore\SQLStore;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IdCacheManager {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param array $caches
+ */
+ public function __construct( array $caches ) {
+ $this->caches = $caches;
+
+ if ( !isset( $this->caches['entity.id'] ) ) {
+ throw new RuntimeException( "Missing 'entity.id' instance.");
+ }
+
+ if ( !isset( $this->caches['entity.sort'] ) ) {
+ throw new RuntimeException( "Missing 'entity.sort' instance.");
+ }
+
+ if ( !isset( $this->caches['entity.lookup'] ) ) {
+ throw new RuntimeException( "Missing 'entity.lookup' instance.");
+ }
+
+ if ( !isset( $this->caches['table.hash'] ) ) {
+ throw new RuntimeException( "Missing 'table.hash' instance.");
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|array $args
+ *
+ * @return string
+ */
+ public static function computeSha1( $args = '' ) {
+ return sha1( json_encode( $args ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return boolean
+ */
+ public function get( $key ) {
+
+ if ( !isset( $this->caches[$key] ) ) {
+ throw new RuntimeException( "$key is unknown");
+ }
+
+ return $this->caches[$key];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $hash
+ *
+ * @return boolean
+ */
+ public function hasCache( $hash ) {
+
+ if ( !is_string( $hash ) ) {
+ return false;
+ }
+
+ return $this->caches['entity.id']->contains( $hash );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ * @param integer $id
+ * @param string $sortkey
+ */
+ public function setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey ) {
+
+ if ( strpos( $title, ' ' ) !== false ) {
+ throw new RuntimeException( "Somebody tried to use spaces in a cache title! ($title)");
+ }
+
+ $hash = $this->computeSha1(
+ [ $title, (int)$namespace, $interwiki, $subobject ]
+ );
+
+ $this->caches['entity.id']->save( $hash, $id );
+ $this->caches['entity.sort']->save( $hash, $sortkey );
+
+ $dataItem = new DIWikiPage( $title, $namespace, $interwiki, $subobject );
+ $dataItem->setId( $id );
+ $dataItem->setSortKey( $sortkey );
+
+ $this->caches['entity.lookup']->save( $id, $dataItem );
+
+ // Speed up detection of redirects when fetching IDs
+ if ( $interwiki == SMW_SQL3_SMWREDIIW ) {
+ $this->setCache( $title, $namespace, '', $subobject, 0, '' );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ */
+ public function deleteCache( $title, $namespace, $interwiki, $subobject ) {
+
+ $hash = $this->computeSha1(
+ [ $title, (int)$namespace, $interwiki, $subobject ]
+ );
+
+ $this->caches['entity.id']->delete( $hash );
+ $this->caches['entity.sort']->delete( $hash );
+
+ if ( ( $id = $this->caches['entity.id']->fetch( $hash ) ) !== false ) {
+ $this->caches['entity.lookup']->delete( $id );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ */
+ public function deleteCacheById( $id ) {
+
+ $dataItem = $this->caches['entity.lookup']->fetch( $id );
+
+ if ( !$dataItem instanceof DIWikiPage ) {
+ return;
+ }
+
+ $hash = $this->computeSha1(
+ [
+ $dataItem->getDBKey(),
+ (int)$dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ $dataItem->getSubobjectName()
+ ]
+ );
+
+ $this->caches['entity.id']->delete( $hash );
+ $this->caches['entity.sort']->delete( $hash );
+ $this->caches['entity.lookup']->delete( $id );
+ }
+
+ /**
+ * Get a cached SMW ID, or false if no cache entry is found.
+ *
+ * @since 3.0
+ *
+ * @param DIWikiPage|array $args
+ *
+ * @return integer|boolean
+ */
+ public function getId( $args ) {
+
+ if ( $args instanceof DIWikiPage ) {
+ $args = [
+ $args->getDBKey(),
+ (int)$args->getNamespace(),
+ $args->getInterwiki(),
+ $args->getSubobjectName()
+ ];
+ }
+
+ $hash = $this->computeSha1( $args );
+
+ if ( ( $id = $this->caches['entity.id']->fetch( $hash ) ) !== false ) {
+ return (int)$id;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get a cached SMW sortkey, or false if no cache entry is found.
+ *
+ * @since 3.0
+ *
+ * @param string $title
+ * @param integer $namespace
+ * @param string $interwiki
+ * @param string $subobject
+ *
+ * @return string|boolean
+ */
+ public function getSort( $args ) {
+
+ $hash = $this->computeSha1( $args );
+
+ if ( ( $sort = $this->caches['entity.sort']->fetch( $hash ) ) !== false ) {
+ return $sort;
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdChanger.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdChanger.php
new file mode 100644
index 00000000..3823a906
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdChanger.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use RuntimeException;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\TableBuilder\FieldType;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IdChanger {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * Change an SMW page id across all relevant tables. The redirect table
+ * is also updated (without much effect if the change happended due to
+ * some redirect, since the table should not contain the id of the
+ * redirected page). If namespaces are given, then they are used to
+ * delete any entries that are limited to one particular namespace (e.g.
+ * only properties can be used as properties) instead of moving them.
+ *
+ * The id in the SMW IDs table is not touched.
+ *
+ * @note This method only changes internal page IDs in SMW. It does not
+ * assume any change in (title-related) data, as e.g. in a page move.
+ * Internal objects (subobject) do not need to be updated since they
+ * refer to the title of their parent page, not to its ID.
+ *
+ * @since 1.8
+ *
+ * @param integer $old_id numeric ID that is to be changed
+ * @param integer $new_id numeric ID to which the records are to be changed
+ * @param integer $old_ns namespace of old id's page (-1 to ignore it)
+ * @param integer $new_ns namespace of new id's page (-1 to ignore it)
+ * @param boolean $s_data stating whether to update subject references
+ * @param boolean $po_data stating if to update property/object references
+ */
+ public function change( $old_id, $new_id, $old_ns = -1, $new_ns = -1, $s_data = true, $po_data = true ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // Change all id entries in property tables:
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+
+ if ( $s_data && $proptable->usesIdSubject() ) {
+ $connection->update(
+ $proptable->getName(),
+ [ 's_id' => $new_id ],
+ [ 's_id' => $old_id ],
+ __METHOD__
+ );
+ }
+
+ if ( $po_data ) {
+ if ( ( ( $old_ns == -1 ) || ( $old_ns == SMW_NS_PROPERTY ) ) && ( !$proptable->isFixedPropertyTable() ) ) {
+ if ( ( $new_ns == -1 ) || ( $new_ns == SMW_NS_PROPERTY ) ) {
+ $connection->update(
+ $proptable->getName(),
+ [ 'p_id' => $new_id ],
+ [ 'p_id' => $old_id ],
+ __METHOD__
+ );
+ } else {
+ $connection->delete(
+ $proptable->getName(),
+ [ 'p_id' => $old_id ],
+ __METHOD__
+ );
+ }
+ }
+
+ foreach ( $proptable->getFields( $this->store ) as $fieldName => $fieldType ) {
+ if ( $fieldType === FieldType::FIELD_ID ) {
+ $connection->update(
+ $proptable->getName(),
+ [ $fieldName => $new_id ],
+ [ $fieldName => $old_id ],
+ __METHOD__
+ );
+ }
+ }
+ }
+ }
+
+ $this->update_concept( $old_id, $new_id, $old_ns, $new_ns, $s_data, $po_data );
+ }
+
+ private function update_concept( $old_id, $new_id, $old_ns, $new_ns, $s_data, $po_data ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( $s_data && ( ( $old_ns == -1 ) || ( $old_ns == SMW_NS_CONCEPT ) ) ) {
+ if ( ( $new_ns == -1 ) || ( $new_ns == SMW_NS_CONCEPT ) ) {
+ $connection->update(
+ SQLStore::CONCEPT_TABLE,
+ [ 's_id' => $new_id ],
+ [ 's_id' => $old_id ],
+ __METHOD__
+ );
+
+ $connection->update(
+ SQLStore::CONCEPT_CACHE_TABLE,
+ [ 's_id' => $new_id ],
+ [ 's_id' => $old_id ],
+ __METHOD__
+ );
+ } else {
+ $connection->delete(
+ SQLStore::CONCEPT_TABLE,
+ [ 's_id' => $old_id ],
+ __METHOD__
+ );
+
+ $connection->delete(
+ SQLStore::CONCEPT_CACHE_TABLE,
+ [ 's_id' => $old_id ],
+ __METHOD__
+ );
+ }
+ }
+
+ if ( $po_data ) {
+ $connection->update(
+ SQLStore::CONCEPT_CACHE_TABLE,
+ [ 'o_id' => $new_id ],
+ [ 'o_id' => $old_id ],
+ __METHOD__
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdEntityFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdEntityFinder.php
new file mode 100644
index 00000000..f081f27d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/IdEntityFinder.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Onoi\Cache\Cache;
+use SMW\DIWikiPage;
+use SMW\IteratorFactory;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class IdEntityFinder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @var IdCacheManager
+ */
+ private $idCacheManager;
+
+ /**
+ * @since 2.1
+ *
+ * @param Store $store
+ * @param IteratorFactory $iteratorFactory
+ * @param IdCacheManager $idCacheManager
+ */
+ public function __construct( Store $store, IteratorFactory $iteratorFactory, IdCacheManager $idCacheManager ) {
+ $this->store = $store;
+ $this->iteratorFactory = $iteratorFactory;
+ $this->idCacheManager = $idCacheManager;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $idList
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DIWikiPage[]
+ */
+ public function getDataItemsFromList( array $idList, RequestOptions $requestOptions = null ) {
+
+ if ( $idList === [] ) {
+ return [];
+ }
+
+ $conditions = [
+ 'smw_id' => $idList,
+ ];
+
+ if ( $requestOptions !== null ) {
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ $conditions[] = $extraCondition;
+ }
+ }
+
+ $rows = $this->fetchFromTable(
+ $conditions
+ );
+
+ if ( $rows === false ) {
+ return [];
+ }
+
+ return $this->iteratorFactory->newMappingIterator(
+ $this->iteratorFactory->newResultIterator( $rows ),
+ [ $this, 'newFromRow' ]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param stdClass $row
+ *
+ * @return DIWikiPage
+ */
+ public function newFromRow( $row ) {
+
+ $dataItem = new DIWikiPage(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject
+ );
+
+ $dataItem->setId( $row->smw_id );
+
+ if ( isset( $row->smw_sortkey ) ) {
+ $dataItem->setSortKey( $row->smw_sortkey );
+ }
+
+ if ( isset( $row->smw_sort ) ) {
+ $dataItem->setOption( 'sort', $row->smw_sort );
+ }
+
+ if ( !$this->idCacheManager->hasCache( $row->smw_hash ) ) {
+ $sortkey = $row->smw_sort === null ? '' : $row->smw_sortkey;
+
+ $this->idCacheManager->setCache(
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_subobject,
+ $row->smw_id,
+ $sortkey
+ );
+ }
+
+ return $dataItem;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $id
+ *
+ * @return DIWikiPage|null
+ */
+ public function getDataItemById( $id ) {
+
+ if ( ( $dataItem = $this->get( (int)$id ) ) !== false ) {
+ return $dataItem;
+ }
+
+ return null;
+ }
+
+ private function get( $id ) {
+
+ $cache = $this->idCacheManager->get( 'entity.lookup' );
+
+ if ( ( $dataItem = $cache->fetch( $id ) ) !== false ) {
+ return $dataItem;
+ }
+
+ $rows = $this->fetchFromTable(
+ [ 'smw_id' => $id ],
+ [ 'LIMIT' => 1 ]
+ );
+
+ if ( $rows === false ) {
+ return false;
+ }
+
+ foreach ( $rows as $row ) {
+
+ if ( !isset( $row->smw_title ) ) {
+ continue;
+ }
+
+ if ( $row->smw_title !== '' && $row->smw_title{0} === '_' && (int)$row->smw_namespace === SMW_NS_PROPERTY ) {
+ // $row->smw_title = str_replace( ' ', '_', PropertyRegistry::getInstance()->findPropertyLabelById( $row->smw_title ) );
+ }
+
+ $row->smw_id = $id;
+ $dataItem = $this->newFromRow( $row );
+ }
+
+ $cache->save( $id, $dataItem );
+
+ return $dataItem;
+ }
+
+ private function fetchFromTable( $conditions, $options = [] ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ return $connection->select(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey',
+ 'smw_sort',
+ 'smw_hash'
+ ],
+ $conditions,
+ __METHOD__,
+ $options
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/NativeEntityLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/NativeEntityLookup.php
new file mode 100644
index 00000000..36a335f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/NativeEntityLookup.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\EntityLookup;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class NativeEntityLookup implements EntityLookup {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @see Store::getSemanticData
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+ return $this->store->getReader()->getSemanticData( $subject, $filter );
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getProperties( DIWikiPage $subject, RequestOptions $requestOptions = null ) {
+ return $this->store->getReader()->getProperties( $subject, $requestOptions );
+ }
+
+ /**
+ * @see Store::getPropertyValues
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getPropertyValues( DIWikiPage $subject = null, DIProperty $property, RequestOptions $requestOptions = null ) {
+ return $this->store->getReader()->getPropertyValues( $subject, $property, $requestOptions );
+ }
+
+ /**
+ * @see Store::getPropertySubjects
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getPropertySubjects( DIProperty $property, DataItem $dataItem = null, RequestOptions $requestOptions = null ) {
+ return $this->store->getReader()->getPropertySubjects( $property, $dataItem, $requestOptions );
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getAllPropertySubjects( DIProperty $property, RequestOptions $requestOptions = null ) {
+ return $this->store->getReader()->getAllPropertySubjects( $property, $requestOptions );
+ }
+
+ /**
+ * @see Store::getInProperties
+ *
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getInProperties( DataItem $object, RequestOptions $requestOptions = null ) {
+ return $this->store->getReader()->getInProperties( $object, $requestOptions );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function invalidateCache() {}
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertiesLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertiesLookup.php
new file mode 100644
index 00000000..87d0d748
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertiesLookup.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\PropertyTableDefinition as TableDefinition;
+use SMWDataItem as DataItem;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertiesLookup {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return RequestOptions|null
+ */
+ public function newRequestOptions( RequestOptions $requestOptions = null ) {
+
+ if ( $requestOptions !== null ) {
+ $clone = clone $requestOptions;
+ $clone->limit = $requestOptions->limit + $requestOptions->offset;
+ $clone->offset = 0;
+ } else {
+ $clone = null;
+ }
+
+ return $clone;
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function fetchFromTable( DIWikiPage $subject, TableDefinition $propertyTable, RequestOptions $requestOptions = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->type( 'SELECT' );
+ $query->table( $propertyTable->getName() );
+
+ if ( $propertyTable->usesIdSubject() ) {
+ $query->condition( $query->eq( 's_id', $subject->getId() ) );
+ } elseif ( $subject->getInterwiki() === '' ) {
+ $query->condition( $query->eq( 's_title', $subject->getDBkey() ) );
+ $query->condition( $query->eq( 's_namespace', $subject->getNamespace() ) );
+ } else {
+ // subjects with non-empty interwiki cannot have properties
+ return [];
+ }
+
+ if ( $propertyTable->isFixedPropertyTable() ) {
+ return $this->fetchFromFixedTable( $query, $propertyTable->getFixedProperty() );
+ }
+
+ $query->join(
+ 'INNER JOIN',
+ [ SQLStore::ID_TABLE => "ON smw_id=p_id" ]
+ );
+
+ $query->fields( [ 'smw_title', 'smw_sortkey' ] );
+
+ // (select sortkey since it might be used in ordering (needed by Postgres))
+ $query->condition( $this->store->getSQLConditions(
+ $requestOptions,
+ 'smw_sortkey',
+ 'smw_sortkey'
+ ) );
+
+ $opt = $this->store->getSQLOptions(
+ $requestOptions,
+ 'smw_sortkey'
+ );
+
+ $query->options( $opt + [ 'DISTINCT' => true ] );
+
+ return $query->execute( __METHOD__ );
+ }
+
+ private function fetchFromFixedTable( $query, $title ) {
+
+ // just check if subject occurs in table
+ $query->options(
+ [ 'LIMIT' => 1 ]
+ );
+
+ $query->field( '*' );
+ $res = $query->execute( __METHOD__ );
+
+ if ( $res->numRows() > 0 ) {
+ return [ $title ];
+ }
+
+ return [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertySubjectsLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertySubjectsLookup.php
new file mode 100644
index 00000000..4866c18b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/PropertySubjectsLookup.php
@@ -0,0 +1,318 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\PropertyTableDefinition as TableDefinition;
+use SMWDataItem as DataItem;
+use SMW\DIContainer;
+use SMW\RequestOptions;
+use SMW\Options;
+use SMW\MediaWiki\DatabaseHelper;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\RequestOptionsProc;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertySubjectsLookup {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var DataItemHandler
+ */
+ private $dataItemHandler;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ $this->iteratorFactory = ApplicationFactory::getInstance()->getIteratorFactory();
+ }
+
+ /**
+ * @see Store::getPropertySubjects
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function fetchFromTable( $pid, TableDefinition $proptable, DataItem $dataItem = null, RequestOptions $requestOptions = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $group = false;
+
+ $dataItemHandler = $this->store->getDataItemHandlerForDIType(
+ $proptable->getDiType()
+ );
+
+ $sortField = $dataItemHandler->getSortField();
+ $query = $connection->newQuery();
+ $query->type( 'SELECT' );
+
+ if ( $requestOptions === null ) {
+ $requestOptions = new RequestOptions();
+ } else{
+ // Clone a `RequestOptions` instance so that it can be modified freely
+ // for the current request without a possible interference on an
+ // upcoming request (as in case where it is called from within a loop
+ // with the same initial RequestOptions instance)
+ $requestOptions = clone $requestOptions;
+ }
+
+ if ( $sortField === '' ) {
+ $sortField = 'smw_sort';
+ }
+
+ $index = '';
+
+ // For certain tables (blob) the query planner chooses a suboptimal plan
+ // and causes an unacceptable query time therefore force an index for
+ // those tables where the behaviour has been observed.
+ if ( $dataItemHandler->getIndexHint( 'property.subjects' ) !== '' && $dataItem === null ) {
+
+ // For tables with only a few entries, the index hint seems to create
+ // a disadvantage, yet when the amount reaches a certain level the
+ // index hint becomes necessary to retain an acceptable response
+ // time.
+ //
+ // Table with < 100 entries
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids` INNER JOIN `smw_di_number` AS t1 ON t1.s_id=smw_id
+ // WHERE (t1.p_id='196959') AND (smw_iw!=':smw') AND (smw_iw!=':smw-delete') AND (smw_iw!=':smw-redi')
+ // GROUP BY smw_sort, smw_id LIMIT 21 8.2510ms (without index hint)
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids` INNER JOIN `smw_di_number` AS t1 FORCE INDEX(s_id) ON t1.s_id=smw_id
+ // WHERE (t1.p_id='196959') AND (smw_iw!=':smw') AND (smw_iw!=':smw-delete') AND (smw_iw!=':smw-redi')
+ // GROUP BY smw_sort, smw_id LIMIT 21 7548.6171ms (with index hint)
+ //
+ // vs.
+ //
+ // Table with > 5000 entries
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids` INNER JOIN `smw_di_blob` AS t1 FORCE INDEX(s_id) ON t1.s_id=smw_id
+ // WHERE (t1.p_id='310170') AND (smw_iw!=':smw') AND (smw_iw!=':smw-delete') AND (smw_iw!=':smw-redi')
+ // GROUP BY smw_sort, smw_id LIMIT 21 62.6249ms (with index hint)
+ //
+ // SELECT smw_id, smw_title, smw_namespace, smw_iw, smw_subobject, smw_sortkey, smw_sort
+ // FROM `smw_object_ids` INNER JOIN `smw_di_blob` AS t1 ON t1.s_id=smw_id
+ // WHERE (t1.p_id='310170') AND (smw_iw!=':smw') AND (smw_iw!=':smw-delete') AND (smw_iw!=':smw-redi')
+ // GROUP BY smw_sort, smw_id LIMIT 21 8856.1242ms (without index hint)
+ //
+ $cq = $connection->newQuery();
+ $cq->type( 'SELECT' );
+ $cq->table( SQLStore::PROPERTY_STATISTICS_TABLE );
+ $cq->field( 'usage_count' );
+ $cq->condition( $cq->eq( 'p_id', $pid ) );
+ $res = $cq->execute( __METHOD__ );
+
+ foreach ( $res as $r ) {
+ // 5000? It just showed to be a sweet spot while doing some
+ // exploratory queries
+ if ( $r->usage_count > 5000 ) {
+ $index = 'FORCE INDEX(' . $dataItemHandler->getIndexHint( 'property.subjects' ) . ')';
+ }
+ }
+ }
+
+ $result = [];
+
+ if ( $proptable->usesIdSubject() ) {
+ $group = true;
+
+ $query->table( SQLStore::ID_TABLE );
+
+ $query->join(
+ 'INNER JOIN',
+ [ $proptable->getName() => "t1 $index ON t1.s_id=smw_id" ]
+ );
+
+ $query->fields(
+ [
+ 'smw_id',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject',
+ 'smw_sortkey',
+ 'smw_sort'
+ ]
+ );
+
+ } else { // no join needed, title+namespace as given in proptable
+ $query->table( $proptable->getName(), "t1" );
+
+ $query->fields(
+ [
+ 's_title AS smw_title',
+ 's_namespace AS smw_namespace',
+ '\'\' AS smw_iw',
+ '\'\' AS smw_subobject',
+ 's_title AS smw_sortkey',
+ 's_title AS smw_sort'
+ ]
+ );
+
+ $requestOptions->setOption( 'ORDER BY', false );
+ }
+
+ if ( !$proptable->isFixedPropertyTable() ) {
+ $query->condition( $query->eq( "t1.p_id", $pid ) );
+ }
+
+ $this->getWhereConds( $query, $dataItem );
+
+ if ( $requestOptions !== null ) {
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ if ( isset( $extraCondition['o_id'] ) ) {
+ $query->condition( $query->eq( 't1.o_id', $extraCondition['o_id'] ) );
+ }
+
+ if ( is_callable( $extraCondition ) ) {
+ $extraCondition( $query );
+ }
+ }
+ }
+
+ if ( $proptable->usesIdSubject() ) {
+ foreach ( [ SMW_SQL3_SMWIW_OUTDATED, SMW_SQL3_SMWDELETEIW, SMW_SQL3_SMWREDIIW ] as $v ) {
+ $query->condition( $query->neq( "smw_iw", $v ) );
+ }
+ }
+
+ if ( $group && $connection->isType( 'postgres') ) {
+ // Avoid a "... 42803 ERROR: column "s....smw_title" must appear in
+ // the GROUP BY clause or be used in an aggregate function ..."
+ // https://stackoverflow.com/questions/1769361/postgresql-group-by-different-from-mysql
+ $requestOptions->setOption( 'DISTINCT', 'ON (smw_sort, smw_id)' );
+ $requestOptions->setOption( 'ORDER BY', false );
+ } elseif ( $group ) {
+ // Using GROUP BY will sort on the field and since we disinguish smw_sort
+ // and the ID at the end of the field, we ensure
+ // the filter duplicates while sorting the list without using DISTINCT which
+ // would cause a filesort
+ // http://www.mysqltutorial.org/mysql-distinct.aspx
+ $requestOptions->setOption( 'GROUP BY', $sortField . ', smw_id' );
+ $requestOptions->setOption( 'ORDER BY', false );
+ } else {
+ $requestOptions->setOption( 'DISTINCT', true );
+ }
+
+ $cond = $this->store->getSQLConditions(
+ $requestOptions,
+ 'smw_sortkey',
+ 'smw_sortkey',
+ false
+ );
+
+ $query->condition( $cond );
+
+ $opts = $this->store->getSQLOptions(
+ $requestOptions,
+ $sortField
+ );
+
+ $query->options( $opts );
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ $this->dataItemHandler = $this->store->getDataItemHandlerForDIType(
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ // Return an iterator and avoid resolving the resources directly as it
+ // may contain a large list of possible matches
+ $res = $this->iteratorFactory->newMappingIterator(
+ $this->iteratorFactory->newResultIterator( $res ),
+ [ $this, 'newFromRow' ]
+ );
+
+ return $res;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param stdClass $row
+ *
+ * @return DIWikiPage
+ */
+ public function newFromRow( $row ) {
+
+ try {
+ if ( $row->smw_iw === '' || $row->smw_iw{0} != ':' ) { // filter special objects
+
+ $keys = [
+ $row->smw_title,
+ $row->smw_namespace,
+ $row->smw_iw,
+ $row->smw_sort,
+ $row->smw_subobject
+
+ ];
+
+ $dataItem = $this->dataItemHandler->dataItemFromDBKeys( $keys );
+
+ if ( isset( $row->smw_id ) ) {
+ $dataItem->setId( $row->smw_id );
+ }
+
+ return $dataItem;
+ }
+ } catch ( DataItemHandlerException $e ) {
+ // silently drop data, should be extremely rare and will usually fix itself at next edit
+ }
+
+ $title = ( $row->smw_title !== '' ? $row->smw_title : 'Empty' ) . '/' . $row->smw_namespace;
+
+ // Avoid null return in Iterator
+ return $this->dataItemHandler->dataItemFromDBKeys( [ 'Blankpage/' . $title, NS_SPECIAL, '', '', '' ] );
+ }
+
+ private function getWhereConds( $query, $dataItem ) {
+
+ $conds = '';
+
+ if ( $dataItem instanceof \SMWDIContainer ) {
+ throw new RuntimeException( 'SMWDIContainer support is missing!');
+ }
+
+ if ( $dataItem !== null ) {
+ $dataItemHandler = $this->store->getDataItemHandlerForDIType(
+ $dataItem->getDIType()
+ );
+
+ foreach ( $dataItemHandler->getWhereConds( $dataItem ) as $fieldname => $value ) {
+ $query->condition( $query->eq( "t1.$fieldname", $value ) );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SemanticDataLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SemanticDataLookup.php
new file mode 100644
index 00000000..ca1d95fa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SemanticDataLookup.php
@@ -0,0 +1,478 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Psr\Log\LoggerAwareTrait;
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableDefinition;
+use SMW\SQLStore\SQLStore;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SemanticDataLookup {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param PropertyTableDefinition $propertyTableDef
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return RequestOptions|null
+ */
+ public function newRequestOptions( PropertyTableDefinition $propertyTableDef, DIProperty $property, RequestOptions $requestOptions = null ) {
+
+ if ( $requestOptions === null || !isset( $requestOptions->conditionConstraint ) ) {
+ return null;
+ }
+
+ $ropts = new RequestOptions();
+
+ $ropts->setLimit( $requestOptions->getLimit() );
+ $ropts->setOffset( $requestOptions->getOffset() );
+
+ if ( $propertyTableDef->isFixedPropertyTable() ) {
+ return $ropts;
+ }
+
+ $pid = $this->store->getObjectIds()->getSMWPropertyID(
+ $property
+ );
+
+ if ( $pid > 0 ) {
+ $ropts->addExtraCondition( [ 'p_id' => $pid ] );
+ }
+
+ return $ropts;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|SemanticData $object
+ *
+ * @return StubSemanticData
+ * @throws RuntimeException
+ */
+ public function newStubSemanticData( $object ) {
+
+ if ( $object instanceof DIWikiPage ) {
+ return new StubSemanticData( $object, $this->store, false );
+ }
+
+ if ( $object instanceof SemanticData ) {
+ return StubSemanticData::newFromSemanticData( $object, $this->store );
+ }
+
+ throw new RuntimeException( 'Expectd either a DIWikiPage or SemanticData object!' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ public function getTableUsageInfo( SemanticData $semanticData ) {
+ $state = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $state[$this->store->findPropertyTableID( $property )] = true;
+ }
+
+ return $state;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param DataItem $dataItem
+ * @param PropertyTableDefinition $propTable
+ * @param RequestOptions $requestOptions
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData( $id, DataItem $dataItem = null, PropertyTableDefinition $propTable, RequestOptions $requestOptions = null ) {
+
+ if ( !$dataItem instanceof DIWikiPage ) {
+ throw new RuntimeException( 'Expected a DIWikiPage instance' );
+ }
+
+ $stubSemanticData = $this->newStubSemanticData( $dataItem );
+
+ $data = $this->fetchSemanticData(
+ $id,
+ $dataItem,
+ $propTable,
+ $requestOptions
+ );
+
+ foreach ( $data as $d ) {
+ $stubSemanticData->addPropertyStubValue( reset( $d ), end( $d ) );
+ }
+
+ return $stubSemanticData;
+ }
+
+ /**
+ * Helper function for reading all data for from a given property table
+ * (specified by an SMWSQLStore3Table dataItem), based on certain
+ * restrictions. The function can filter data based on the subject (1)
+ * or on the property it belongs to (2) -- but one of those must be
+ * done. The Boolean $issubject is true for (1) and false for (2).
+ *
+ * In case (1), the first two parameters are taken to refer to a
+ * subject; in case (2) they are taken to refer to a property. In any
+ * case, the retrieval is limited to the specified $proptable. The
+ * parameters are an internal $id (of a subject or property), and an
+ * $dataItem (being an DIWikiPage or SMWDIProperty). Moreover, when
+ * filtering by property, it is assumed that the given $proptable
+ * belongs to the property: if it is a table with fixed property, it
+ * will not be checked that this is the same property as the one that
+ * was given in $dataItem.
+ *
+ * In case (1), the result in general is an array of pairs (arrays of
+ * size 2) consisting of a property key (string), and DB keys (array if
+ * many, string if one) from which a datvalue dataItem for this value can
+ * be built. It is possible that some of the DB keys are based on
+ * internal dataItems; these will be represented by similar result arrays
+ * of (recursive calls of) fetchSemanticData().
+ *
+ * In case (2), the result is simply an array of DB keys (array)
+ * without the property keys. Container dataItems will be encoded with
+ * nested arrays like in case (1).
+ *
+ * @param integer $id
+ * @param DataItem $dataItem
+ * @param PropertyTableDefinition $propTable
+ * @param RequestOptions $requestOptions
+ *
+ * @return array
+ */
+ public function fetchSemanticData( $id, DataItem $dataItem = null, PropertyTableDefinition $propTable, RequestOptions $requestOptions = null ) {
+
+ $isSubject = $dataItem instanceof DIWikiPage || $dataItem === null;
+
+ // stop if there is not enough data:
+ // properties always need to be given as dataItem,
+ // subjects at least if !$proptable->idsubject
+ if ( ( $id == 0 ) ||
+ ( $dataItem === null && ( !$isSubject || !$propTable->usesIdSubject() ) ) ||
+ ( $propTable->getDIType() === null ) ) {
+ return [];
+ }
+
+ $result = [];
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // Build something like:
+ //
+ // SELECT o_id AS id0,o0.smw_title AS v0,o0.smw_namespace AS v1,o0.smw_iw
+ // AS v2,o0.smw_sortkey AS v3,o0.smw_subobject AS v4
+ // FROM `smw_fpt_sobj`
+ // INNER JOIN `smw_object_ids` AS o0 ON o_id=o0.smw_id
+ // WHERE s_id='852'
+ // LIMIT 4
+ //
+ // or
+ //
+ // SELECT p.smw_title as prop,o_blob AS v0,o_hash AS v1 FROM `smw_di_blob`
+ // INNER JOIN `smw_object_ids` AS p ON p_id=p.smw_id
+ // WHERE s_id='80' AND p.smw_iw!=':smw' AND p.smw_iw!=':smw-delete'
+
+ $query = $this->newQuery(
+ $propTable,
+ $id,
+ $isSubject,
+ $dataItem
+ );
+
+ if ( $requestOptions !== null ) {
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ if ( isset( $extraCondition['p_id'] ) ) {
+ $query->condition( $query->eq( 'p_id', $extraCondition['p_id'] ) );
+ }
+ }
+ } else {
+ $requestOptions = new RequestOptions();
+ }
+
+ $valueCount = 0;
+ $fieldname = '';
+
+ $diHandler = $this->store->getDataItemHandlerForDIType(
+ $propTable->getDiType()
+ );
+
+ $valueField = $diHandler->getIndexField();
+ $labelField = $diHandler->getLabelField();
+
+ $fields = $diHandler->getFetchFields();
+
+ $this->addFields(
+ $query,
+ $fields,
+ $valueField,
+ $labelField,
+ $valueCount,
+ $fieldname
+ );
+
+ // Don't use DISTINCT for subject related value match but make sure
+ // (#3531) it is used when requesting other values in order to retrieve
+ // all available unique values within the range of the limit
+ if ( !$isSubject ) {
+ $requestOptions->setOption( 'DISTINCT', true );
+
+ // Don't sort, this avoids a SQL `filesort`/`temporary table` usage
+ // in combination with DISTINCT, values will be listed as-is instead
+ // of a lexical representation but can be compensated by selecting a
+ // wider range in case this is used as retrieving "all" values
+ // for a property
+
+ // SELECT DISTINCT o_id AS id0, o0.smw_title AS v0, o0.smw_namespace
+ // AS v1, o0.smw_iw AS v2, o0.smw_sortkey AS v3, o0.smw_subobject AS
+ // v4 FROM `smw_di_wikipage` INNER JOIN `smw_object_ids` AS o0 ON
+ // o_id=o0.smw_id WHERE (p_id='x') LIMIT 51
+ //
+ // 8.6281ms
+ //
+ // vs.
+ //
+ // SELECT DISTINCT o_id AS id0, o0.smw_title AS v0, o0.smw_namespace
+ // AS v1, o0.smw_iw AS v2, o0.smw_sortkey AS v3, o0.smw_subobject AS
+ // v4 FROM `smw_di_wikipage` INNER JOIN `smw_object_ids` AS o0 ON
+ // o_id=o0.smw_id WHERE (p_id='x') ORDER BY o_id LIMIT 51
+ //
+ // 24189.0128ms
+ //
+ // PS: In case of a `TYPE_WIKIPAGE` entity, sorting by `o_id`
+ // wouldn't make much sense as it does not guarantee any lexical order
+ $requestOptions->setOption( 'ORDER BY', false );
+ }
+
+ // Apply sorting/string matching; only with given property
+ if ( !$isSubject ) {
+ $conds = $this->store->getSQLConditions(
+ $requestOptions,
+ $valueField,
+ $labelField,
+ $query->hasCondition()
+ );
+
+ $query->condition( $conds );
+ } else {
+ $valueField = '';
+ }
+
+ $query->options(
+ $this->store->getSQLOptions( $requestOptions, $valueField )
+ );
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ $propertykey = '';
+
+ // use joined or predefined property name
+ if ( $isSubject ) {
+ $propertykey = $propTable->isFixedPropertyTable() ? $propTable->getFixedProperty() : $row->prop;
+ }
+
+ $this->resultFromRow(
+ $result,
+ $row,
+ $fields,
+ $fieldname,
+ $valueCount,
+ $isSubject,
+ $propertykey
+ );
+ }
+
+ $connection->freeResult( $res );
+
+ // Sorting via PHP for an explicit disabled `ORDER BY` to ensure that
+ // the result set has at least a lexical order applied for the range of
+ // retrieved values
+ if ( $requestOptions->getOption( 'ORDER BY' ) === false ) {
+ sort( $result );
+ }
+
+ return $result;
+ }
+
+ private function newQuery( $propTable, $id, $isSubject, $dataItem ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->type( 'select' );
+ $query->table( $propTable->getName() );
+
+ // Restrict property only
+ if ( !$isSubject && !$propTable->isFixedPropertyTable() ) {
+ $query->condition( $query->eq( 'p_id', $id ) );
+ }
+
+ // Restrict subject, select property
+ if ( $isSubject && $propTable->usesIdSubject() ) {
+ $query->condition( $query->eq( 's_id', $id ) );
+ } elseif ( $isSubject ) {
+ $query->condition( $query->eq( 's_title', $dataItem->getDBkey() ) );
+ $query->condition( $query->eq( 's_namespace', $dataItem->getNamespace() ) );
+ }
+
+ // Select property name
+ // In case of a fixed property, no select needed
+ if ( $isSubject && !$propTable->isFixedPropertyTable() ) {
+ $query->join(
+ 'INNER JOIN',
+ [ SQLStore::ID_TABLE => 'p ON p_id=p.smw_id' ]
+ );
+
+ $query->field( 'p.smw_title', 'prop' );
+
+ // Avoid displaying any property that has been marked deleted or outdated
+ $query->condition( $query->neq( "p.smw_iw", SMW_SQL3_SMWIW_OUTDATED ) );
+ $query->condition( $query->neq( "p.smw_iw", SMW_SQL3_SMWDELETEIW ) );
+ }
+
+ return $query;
+ }
+
+ private function addFields( &$query, $fields, $valueField, $labelField, &$valueCount, &$fieldname ) {
+
+ // Select dataItem column(s)
+ foreach ( $fields as $fieldname => $fieldType ) {
+
+ // Get data from ID table
+ if ( $fieldType === FieldType::FIELD_ID ) {
+ $query->join(
+ 'INNER JOIN',
+ [ SQLStore::ID_TABLE => "o$valueCount ON $fieldname=o$valueCount.smw_id" ]
+ );
+
+ $query->field( "$fieldname AS id$valueCount" );
+ $query->field( "o$valueCount.smw_title AS v$valueCount" );
+ $query->field( "o$valueCount.smw_namespace AS v" . ( $valueCount + 1 ) );
+ $query->field( "o$valueCount.smw_iw AS v" . ( $valueCount + 2 ) );
+ $query->field( "o$valueCount.smw_sortkey AS v" . ( $valueCount + 3 ) );
+ $query->field( "o$valueCount.smw_subobject AS v" . ( $valueCount + 4 ) );
+
+ if ( $valueField == $fieldname ) {
+ $valueField = "o$valueCount.smw_sortkey";
+ }
+ if ( $labelField == $fieldname ) {
+ $labelField = "o$valueCount.smw_sortkey";
+ }
+
+ $valueCount += 4;
+ } else {
+ $query->field( $fieldname, "v$valueCount" );
+ }
+
+ $valueCount += 1;
+ }
+
+ // Postgres
+ // Function: SMWSQLStore3Readers::fetchSemanticData
+ // Error: 42P10 ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
+ if ( !$query->hasField( $valueField ) ) {
+ $query->field( $valueField, "v" . ( $valueCount + 1 ) );
+ }
+ }
+
+ private function resultFromRow( &$result, $row, $fields, $fieldname, $valueCount, $isSubject, $propertykey ) {
+
+ $hash = '';
+
+ if ( $isSubject ) { // use joined or predefined property name
+ $hash = $propertykey;
+ }
+
+ // Use enclosing array only for results with many values:
+ if ( $valueCount > 1 ) {
+ $valueKeys = [];
+ for ( $i = 0; $i < $valueCount; $i += 1 ) { // read the value fields from the current row
+ $fieldname = "v$i";
+ $valueKeys[] = $row->$fieldname;
+ }
+ } else {
+ $valueKeys = $row->v0;
+ }
+
+ // #Issue 615
+ // If the iw field contains a redirect marker then remove it
+ if ( isset( $valueKeys[2] ) && ( $valueKeys[2] === SMW_SQL3_SMWREDIIW || $valueKeys[2] === SMW_SQL3_SMWDELETEIW ) ) {
+ $valueKeys[2] = '';
+ }
+
+ // The hash prevents from inserting duplicate entries of the same content
+ if ( $valueCount > 1 ) {
+ $hash = md5( $hash . implode( '#', $valueKeys ) );
+ } else {
+ $hash = md5( $hash . $valueKeys );
+ }
+
+ // Filter out any accidentally retrieved internal things (interwiki starts with ":"):
+ if ( $valueCount < 3 ||
+ implode( '', $fields ) !== FieldType::FIELD_ID ||
+ $valueKeys[2] === '' ||
+ $valueKeys[2]{0} != ':' ) {
+
+ if ( isset( $result[$hash] ) ) {
+ $this->reportDuplicate( $propertykey, $valueKeys );
+ }
+
+ if ( $isSubject ) {
+ $result[$hash] = [ $propertykey, $valueKeys ];
+ } else{
+ $result[$hash] = $valueKeys;
+ }
+ }
+ }
+
+ private function reportDuplicate( $propertykey, $valueKeys ) {
+ $this->logger->info(
+ "Found duplicate entry for {propertykey} with {valueKeys}",
+ [
+ 'method' => __METHOD__,
+ 'role' => 'user',
+ 'propertykey' => $propertykey,
+ 'valueKeys' => ( is_array( $valueKeys ) ? implode( ',', $valueKeys ) : $valueKeys )
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/StubSemanticData.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/StubSemanticData.php
new file mode 100644
index 00000000..c049b0bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/StubSemanticData.php
@@ -0,0 +1,384 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exception\DataItemException;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\SQLStore;
+use SMW\StoreFactory;
+use SMWDataItem as DataItem;
+use SMWSemanticData as SemanticData;
+
+/**
+ * This class provides a subclass of SemanticData that can store prefetched values
+ * from the SQL store, and unstub this data on demand when it is accessed.
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Markus Krötzs
+ * @author mwjames
+ */
+class StubSemanticData extends SemanticData {
+
+ /**
+ * @var SQLStore
+ */
+ protected $store;
+
+ /**
+ * Stub property data that is not part of $mPropVals and $mProperties
+ * yet. Entries use property keys as keys. The value is an array of
+ * DBkey-arrays that define individual datavalues. The stubs will be
+ * set up when first accessed.
+ *
+ * @since 1.8
+ *
+ * @var array
+ */
+ protected $mStubPropVals = [];
+
+ /**
+ * DIWikiPage object that is the subject of this container.
+ * Subjects that are null are used to represent "internal objects"
+ * only.
+ *
+ * @since 1.8
+ *
+ * @var DIWikiPage
+ */
+ protected $mSubject;
+
+ /**
+ * Whether SubSemanticData have been requested and added
+ *
+ * @var boolean
+ */
+ private $subSemanticDataInit = false;
+
+ /**
+ * @since 1.8
+ *
+ * @param DIWikiPage $subject to which this data refers
+ * @param SQLStore $store (the parent store)
+ * @param boolean $noDuplicates stating if duplicate data should be avoided
+ */
+ public function __construct( DIWikiPage $subject, SQLStore $store, $noDuplicates = true ) {
+ $this->store = $store;
+ parent::__construct( $subject, $noDuplicates );
+ }
+
+ /**
+ * Required to support php-serialization
+ *
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function __sleep() {
+ return [ 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mStubPropVals', 'options', 'extensionData' ];
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function __wakeup() {
+ $this->store = StoreFactory::getStore( 'SMW\SQLStore\SQLStore' );
+ }
+
+ /**
+ * Create a new StubSemanticData object that holds the data of a
+ * given SemanticData object. Array assignments create copies in PHP
+ * so the arrays are distinct in input and output object. The object
+ * references are copied as references in a shallow way. This is
+ * sufficient as the data items used there are immutable.
+ *
+ * @since 1.8
+ *
+ * @param $semanticData SemanticData
+ * @param SQLStore $store
+ *
+ * @return StubSemanticData
+ */
+ public static function newFromSemanticData( SemanticData $semanticData, SQLStore $store ) {
+ $result = new self( $semanticData->getSubject(), $store );
+ $result->mPropVals = $semanticData->mPropVals;
+ $result->mProperties = $semanticData->mProperties;
+ $result->mHasVisibleProps = $semanticData->mHasVisibleProps;
+ $result->mHasVisibleSpecs = $semanticData->mHasVisibleSpecs;
+ $result->stubObject = $semanticData->stubObject;
+ return $result;
+ }
+
+ /**
+ * Get the array of all properties that have stored values.
+ *
+ * @since 1.8
+ *
+ * @return array of SMWDIProperty objects
+ */
+ public function getProperties() {
+ $this->unstubProperties();
+ return parent::getProperties();
+ }
+
+ /**
+ * @see SemanticData::hasProperty
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function hasProperty( DIProperty $property ) {
+ $this->unstubProperties();
+ return parent::hasProperty( $property );
+ }
+
+ /**
+ * Get the array of all stored values for some property.
+ *
+ * @since 1.8
+ *
+ * @param DIProperty $property
+ *
+ * @return array of DataItem
+ */
+ public function getPropertyValues( DIProperty $property ) {
+ if ( $property->isInverse() ) { // we never have any data for inverses
+ return [];
+ }
+
+ if ( array_key_exists( $property->getKey(), $this->mStubPropVals ) ) {
+ // Not catching exception here; the
+ $this->unstubProperty( $property->getKey(), $property );
+ $propertyTypeId = $property->findPropertyTypeID();
+ $propertyDiId = DataTypeRegistry::getInstance()->getDataItemId( $propertyTypeId );
+
+ foreach ( $this->mStubPropVals[$property->getKey()] as $dbkeys ) {
+ try {
+ $diHandler = $this->store->getDataItemHandlerForDIType( $propertyDiId );
+ $di = $diHandler->dataItemFromDBKeys( $dbkeys );
+
+ if ( $this->mNoDuplicates ) {
+ $this->mPropVals[$property->getKey()][$di->getHash()] = $di;
+ } else {
+ $this->mPropVals[$property->getKey()][] = $di;
+ }
+ } catch ( DataItemHandlerException $e ) {
+ // ignore data
+ }
+ }
+
+ unset( $this->mStubPropVals[$property->getKey()] );
+ }
+
+ return parent::getPropertyValues( $property );
+ }
+
+ /**
+ * @see SemanticData::getSubSemanticData
+ *
+ * @note SubSemanticData are added only on request to avoid unnecessary DB
+ * transactions
+ *
+ * @since 2.0
+ */
+ public function getSubSemanticData() {
+
+ if ( $this->subSemanticDataInit ) {
+ return parent::getSubSemanticData();
+ }
+
+ $this->subSemanticDataInit = true;
+
+ foreach ( $this->getProperties() as $property ) {
+
+ // #619 Do not resolve subobjects for redirects
+ if ( !DataTypeRegistry::getInstance()->isSubDataType( $property->findPropertyTypeID() ) || $this->isRedirect() ) {
+ continue;
+ }
+
+ $this->initSubSemanticData( $property );
+ }
+
+ return parent::getSubSemanticData();
+ }
+
+ /**
+ * @see SemanticData::hasSubSemanticData
+ *
+ * @note This method will initialize SubSemanticData first if it wasn't done
+ * yet to ensure data consistency
+ *
+ * @since 2.0
+ */
+ public function hasSubSemanticData( $subobjectName = null ) {
+
+ if ( !$this->subSemanticDataInit ) {
+ $this->getSubSemanticData();
+ }
+
+ return parent::hasSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * @see SemanticData::findSubSemanticData
+ *
+ * @since 2.5
+ */
+ public function findSubSemanticData( $subobjectName ) {
+
+ if ( !$this->subSemanticDataInit ) {
+ $this->getSubSemanticData();
+ }
+
+ return parent::findSubSemanticData( $subobjectName );
+ }
+
+ /**
+ * Remove a value for a property identified by its DataItem object.
+ * This method removes a property-value specified by the property and
+ * dataitem. If there are no more property-values for this property it
+ * also removes the property from the mProperties.
+ *
+ * @note There is no check whether the type of the given data item
+ * agrees with the type of the property. Since property types can
+ * change, all parts of SMW are prepared to handle mismatched data item
+ * types anyway.
+ *
+ * @param $property SMWDIProperty
+ * @param $dataItem DataItem
+ *
+ * @since 1.8
+ */
+ public function removePropertyObjectValue( DIProperty $property, DataItem $dataItem ) {
+ $this->unstubProperties();
+ $this->getPropertyValues( $property );
+ parent::removePropertyObjectValue($property, $dataItem);
+ }
+
+ /**
+ * Return true if there are any visible properties.
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function hasVisibleProperties() {
+ $this->unstubProperties();
+ return parent::hasVisibleProperties();
+ }
+
+ /**
+ * Return true if there are any special properties that can
+ * be displayed.
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function hasVisibleSpecialProperties() {
+ $this->unstubProperties();
+ return parent::hasVisibleSpecialProperties();
+ }
+
+ /**
+ * Add data in abbreviated form so that it is only expanded if needed.
+ * The property key is the DB key (string) of a property value, whereas
+ * valuekeys is an array of DBkeys for the added value that will be
+ * used to initialize the value if needed at some point. If there is
+ * only one valuekey, a single string can be used.
+ *
+ * @since 1.8
+ * @param string $propertyKey
+ * @param array|string $valueKeys
+ */
+ public function addPropertyStubValue( $propertyKey, $valueKeys ) {
+ $this->mStubPropVals[$propertyKey][] = $valueKeys;
+ }
+
+ /**
+ * Delete all data other than the subject.
+ *
+ * @since 1.8
+ */
+ public function clear() {
+ $this->mStubPropVals = [];
+ parent::clear();
+ }
+
+ /**
+ * Process all mProperties that have been added as stubs.
+ * Associated data may remain in stub form.
+ *
+ * @since 1.8
+ */
+ protected function unstubProperties() {
+ foreach ( $this->mStubPropVals as $pkey => $values ) { // unstub property values only, the value lists are still kept as stubs
+ try {
+ $this->unstubProperty( $pkey );
+ } catch ( DataItemException $e ) {
+ // Likely cause: a property name from the DB is no longer valid.
+ // Do nothing; we could unset the data, but it will never be
+ // unstubbed anyway if there is no valid property DI for it.
+ }
+ }
+ }
+
+ /**
+ * Unstub a single property from the stub data array. If available, an
+ * existing object for that property might be provided, so we do not
+ * need to make a new one. It is not checked if the object matches the
+ * property name.
+ *
+ * @since 1.8
+ *
+ * @param string $propertyKey
+ * @param SMWDIProperty $diProperty if available
+ *
+ * @throws DataItemException if property key is not valid
+ * and $diProperty is null
+ */
+ protected function unstubProperty( $propertyKey, $diProperty = null ) {
+ if ( !array_key_exists( $propertyKey, $this->mProperties ) ) {
+ if ( is_null( $diProperty ) ) {
+ $diProperty = new DIProperty( $propertyKey, false );
+ }
+
+ $this->mProperties[$propertyKey] = $diProperty;
+
+ if ( !$diProperty->isUserDefined() ) {
+ if ( $diProperty->isShown() ) {
+ $this->mHasVisibleSpecs = true;
+ $this->mHasVisibleProps = true;
+ }
+ } else {
+ $this->mHasVisibleProps = true;
+ }
+ }
+ }
+
+ protected function isRedirect() {
+ return $this->store->getObjectIds()->isRedirect( $this->mSubject );
+ }
+
+ private function initSubSemanticData( DIProperty $property ) {
+ foreach ( $this->getPropertyValues( $property ) as $value ) {
+
+ if ( !$value instanceof DIWikiPage || $value->getSubobjectName() === '' ) {
+ continue;
+ }
+
+ if ( $this->hasSubSemanticData( $value->getSubobjectName() ) ) {
+ continue;
+ }
+
+ $this->addSubSemanticData( $this->store->getSemanticData( $value ) );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SubobjectListFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SubobjectListFinder.php
new file mode 100644
index 00000000..f26c3b20
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/SubobjectListFinder.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\IteratorFactory;
+use SMW\SQLStore\SQLStore;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SubobjectListFinder {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @var DIWikiPage
+ */
+ private $subject;
+
+ /**
+ * @var []
+ */
+ private $mappingIterator = [];
+
+ /**
+ * @var []
+ */
+ private $skipConditions = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ * @param IteratorFactory $iteratorFactory
+ */
+ public function __construct( SQLStore $store, IteratorFactory $iteratorFactory ) {
+ $this->store = $store;
+ $this->iteratorFactory = $iteratorFactory;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return MappingIterator
+ */
+ public function find( DIWikiPage $subject ) {
+
+ $key = $subject->getHash() . ':' . $subject->getId();
+
+ if ( !isset( $this->mappingIterator[$key] ) ) {
+ $this->mappingIterator[$key] = $this->newMappingIterator( $subject );
+ }
+
+ return $this->mappingIterator[$key];
+ }
+
+ /**
+ * Fetch all subobjects for a given subject using a lazy-mapping iterator
+ * in order to only resolve one subobject per iteration step.
+ *
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return MappingIterator
+ */
+ private function newMappingIterator( DIWikiPage $subject ) {
+
+ $callback = function( $row ) use ( $subject ) {
+
+ // #1955
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY ) {
+ $property = new DIProperty( $subject->getDBkey() );
+ $subobject = $property->getCanonicalDiWikiPage( $row->smw_subobject );
+ } else {
+ $subobject = new DIWikiPage(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $row->smw_subobject
+ );
+ }
+
+ $subobject->setSortKey( $row->smw_sortkey );
+ $subobject->setId( $row->smw_id );
+
+ return $subobject;
+ };
+
+ return $this->iteratorFactory->newMappingIterator(
+ $this->newResultIterator( $subject ),
+ $callback
+ );
+ }
+
+ private function newResultIterator( DIWikiPage $subject ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $key = $subject->getDBkey();
+
+ // #1955 Ensure to match a possible predefined property
+ // (Modification date -> _MDAT)
+ if ( $subject->getNamespace() === SMW_NS_PROPERTY ) {
+ $key = DIProperty::newFromUserLabel( $key )->getKey();
+ }
+
+ $conditions = [
+ 'smw_title=' . $connection->addQuotes( $key ),
+ 'smw_namespace=' . $connection->addQuotes( $subject->getNamespace() ),
+ 'smw_iw=' . $connection->addQuotes( $subject->getInterwiki() ),
+ 'smw_subobject!=' . $connection->addQuotes( '' )
+ ];
+
+ foreach ( $this->skipConditions as $skipOn ) {
+ $conditions[] = 'smw_subobject!=' . $connection->addQuotes( $skipOn );
+ }
+
+ $res = $connection->select(
+ $connection->tablename( SQLStore::ID_TABLE ),
+ [
+ 'smw_id',
+ 'smw_subobject',
+ 'smw_sortkey'
+ ],
+ implode( ' AND ' , $conditions ),
+ __METHOD__
+ );
+
+ return $this->iteratorFactory->newResultIterator( $res );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/TraversalPropertyLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/TraversalPropertyLookup.php
new file mode 100644
index 00000000..1ca99e48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/TraversalPropertyLookup.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use RuntimeException;
+use SMW\DIContainer;
+use SMW\MediaWiki\Connection\OptionsBuilder;
+use SMW\Options;
+use SMW\RequestOptions;
+use SMW\SQLStore\PropertyTableDefinition as PropertyTableDef;
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TraversalPropertyLookup {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @see Store::getInProperties
+ *
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function fetchFromTable( PropertyTableDef $propertyTableDef, DataItem $dataItem, RequestOptions $requestOptions = null ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( $dataItem instanceof DIContainer ) {
+ throw new RuntimeException( "DIContainer: " . $dataItem->getSerialization() );
+ }
+
+ // Potentially need to get more results, since options apply to union.
+ if ( $requestOptions !== null ) {
+ $subOptions = clone $requestOptions;
+ $subOptions->limit = $requestOptions->limit + $requestOptions->offset;
+ $subOptions->offset = 0;
+ } else {
+ $subOptions = null;
+ }
+
+ if ( !$propertyTableDef->isFixedPropertyTable() ) {
+
+ $cond = $this->getWhereConds( $dataItem );
+ $conditions = '';
+
+ // No sorting
+ $options = $this->store->getSQLOptions( $subOptions, '' );
+
+ // Avoid any limit or offset for the sub-query in order to find all
+ // incoming properties
+ unset( $options['LIMIT'] );
+ unset( $options['OFFSET'] );
+
+ // Ensure to group same IDs to reduce the amount of data transferred
+ // from the inner join
+ $options['GROUP BY'] = 'p_id';
+
+ $opt = OptionsBuilder::toString( $options );
+
+ $cond = ( $cond !== '' ? ' WHERE ' : '' ) . $cond;
+
+ // Use a subquery to match all possible IDs, no ORDER BY or DISTINCT to avoid
+ // a filesort
+ $from = $connection->tableName( SQLStore::ID_TABLE ) .
+ " INNER JOIN (" .
+ " SELECT p_id FROM " . $connection->tableName( $propertyTableDef->getName() ) .
+ " $cond $opt ) AS t1 ON t1.p_id=smw_id";
+
+ $conditions .= ( $conditions ? ' AND ' : ' ' ) .
+ " smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ) .
+ " AND smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWDELETEIW );
+
+ $conditions .= $this->store->getSQLConditions( $subOptions, 'smw_sortkey', 'smw_sortkey', $conditions !== '' );
+
+ $options = $this->store->getSQLOptions( $subOptions, '' ) + [ 'DISTINCT' ];
+
+ $result = $connection->select(
+ $from,
+ ' smw_title,smw_sortkey,smw_iw',
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ } else {
+ $from = $connection->tableName( $propertyTableDef->getName() ) . " AS t1";
+ $where = $this->getWhereConds( $dataItem );
+ $fields = $propertyTableDef->usesIdSubject() ? 's_id' : '*';
+
+ $result = $connection->select(
+ $from,
+ $fields,
+ $where,
+ __METHOD__,
+ [ 'LIMIT' => 1 ]
+ );
+
+ if ( $result->numRows() > 0 ) {
+ $res = new \stdClass;
+ $res->smw_title = $propertyTableDef->getFixedProperty();
+ $result = [ $res ];
+ }
+ }
+
+ return $result;
+ }
+
+ private function getWhereConds( $dataItem ) {
+
+ $where = '';
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ if ( $dataItem !== null ) {
+ $dataItemHandler = $this->store->getDataItemHandlerForDIType( $dataItem->getDIType() );
+ foreach ( $dataItemHandler->getWhereConds( $dataItem ) as $fieldname => $value ) {
+ $where .= ( $where ? ' AND ' : '' ) . "$fieldname=" . $connection->addQuotes( $value );
+ }
+ }
+
+ return $where;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/UniquenessLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/UniquenessLookup.php
new file mode 100644
index 00000000..00767015
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityStore/UniquenessLookup.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\SQLStore\EntityStore;
+
+use Onoi\Cache\Cache;
+use SMW\DIWikiPage;
+use SMW\IteratorFactory;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use InvalidArgumentException;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UniquenessLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param IteratorFactory $iteratorFactory
+ */
+ public function __construct( Store $store, IteratorFactory $iteratorFactory ) {
+ $this->store = $store;
+ $this->iteratorFactory = $iteratorFactory;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataItem $dataItem
+ *
+ * @return boolean
+ */
+ public function isUnique( DataItem $dataItem ) {
+
+ $type = $dataItem->getDIType();
+
+ if ( $type !== DataItem::TYPE_WIKIPAGE && $type !== DataItem::TYPE_PROPERTY ) {
+ throw new InvalidArgumentException( 'Expects a DIProperty or DIWikiPage object.' );
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->type( 'SELECT' );
+ $query->options( [ 'LIMIT' => 2 ] );
+
+ $query->table( SQLStore::ID_TABLE );
+
+ // Only find entities
+ $query->fields( [ 'smw_id', 'smw_sortkey' ] );
+
+ if ( $type === DataItem::TYPE_WIKIPAGE ) {
+ $query->condition( $query->eq( 'smw_title', $dataItem->getDBKey() ) );
+ $query->condition( $query->eq( 'smw_namespace', $dataItem->getNamespace() ) );
+ $query->condition( $query->eq( 'smw_subobject', $dataItem->getSubobjectName() ) );
+ } else {
+ $query->condition( $query->eq( 'smw_sortkey', $dataItem->getCanonicalLabel() ) );
+ $query->condition( $query->eq( 'smw_namespace', SMW_NS_PROPERTY ) );
+ $query->condition( $query->eq( 'smw_subobject', '' ) );
+ }
+
+ $query->condition( $query->neq( 'smw_iw', SMW_SQL3_SMWIW_OUTDATED ) );
+ $query->condition( $query->neq( 'smw_iw', SMW_SQL3_SMWDELETEIW ) );
+ $query->condition( $query->neq( 'smw_iw', SMW_SQL3_SMWREDIIW ) );
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ return $res->numRows() < 2;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Iterator|[]
+ */
+ public function findDuplicates() {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->type( 'SELECT' );
+ $query->table( SQLStore::ID_TABLE );
+
+ $query->fields(
+ [
+ 'COUNT(*) as count',
+ 'smw_title',
+ 'smw_namespace',
+ 'smw_iw',
+ 'smw_subobject'
+ ]
+ );
+
+ $query->condition( $query->neq( 'smw_iw', SMW_SQL3_SMWIW_OUTDATED ) );
+ $query->condition( $query->neq( 'smw_iw', SMW_SQL3_SMWDELETEIW ) );
+
+ $query->options(
+ [
+ 'GROUP BY' => 'smw_title, smw_namespace, smw_iw, smw_subobject',
+
+ // @see https://stackoverflow.com/questions/8119489/postgresql-where-count-condition
+ // "HAVING count > 1"; doesn't work with postgres
+ 'HAVING' => 'count(*) > 1'
+ ]
+ );
+
+ $rows = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ if ( $rows === false ) {
+ return [];
+ }
+
+ $resultIterator = $this->iteratorFactory->newResultIterator(
+ $rows
+ );
+
+ $mappingIterator = $this->iteratorFactory->newMappingIterator( $resultIterator, function( $row ) {
+ return [
+ 'count'=> $row->count,
+ 'smw_title'=> $row->smw_title,
+ 'smw_namespace'=> $row->smw_namespace,
+ 'smw_iw'=> $row->smw_iw,
+ 'smw_subobject'=> $row->smw_subobject
+ ];
+ } );
+
+ return $mappingIterator;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityValueUniquenessConstraintChecker.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityValueUniquenessConstraintChecker.php
new file mode 100644
index 00000000..01a634e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/EntityValueUniquenessConstraintChecker.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMW\RequestOptions;
+use SMW\IteratorFactory;
+use InvalidArgumentException;
+use RuntimeException;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class EntityValueUniquenessConstraintChecker {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var IteratorFactory
+ */
+ private $iteratorFactory;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ * @param IteratorFactory $iteratorFactory
+ */
+ public function __construct( Store $store, IteratorFactory $iteratorFactory ) {
+ $this->store = $store;
+ $this->iteratorFactory = $iteratorFactory;
+ }
+
+ /**
+ * Find references (all or limited by RequestOptions) for the combination of
+ * a property and a value. This can be used to identify uniqueness violations
+ * amongst entities where the same value (+property) is assigned to different
+ * subjects or it be used to count the cardinality for a specific value
+ * representation.
+ *
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param DataItem $dataItem
+ * @param RequestOptions $requestOptions
+ *
+ * @return Iterator|[]
+ */
+ public function checkConstraint( DIProperty $property, DataItem $dataItem, RequestOptions $requestOptions ) {
+
+ $propTableId = $this->store->getPropertyTableInfoFetcher()->findTableIdForProperty(
+ $property
+ );
+
+ $proptables = $this->store->getPropertyTables();
+ $propertyTable = $proptables[$propTableId];
+
+ if ( !isset( $proptables[$propTableId] ) || !$propertyTable->usesIdSubject() ) {
+ return [];
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->index = 1;
+ $query->alias = 't';
+ $i = $query->index;
+
+ $query->table( $propertyTable->getName(), "{$query->alias}{$i}" );
+
+ // Only find entities
+ $query->field( "{$query->alias}{$i}.s_id" );
+
+ $this->resolve_value_condition( $propertyTable, $property, $dataItem, $query );
+
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ if ( is_callable( $extraCondition ) ) {
+ $query->condition( $extraCondition( $this->store, $query, "{$query->alias}{$i}" ) );
+ } else {
+ throw new RuntimeException( "Expected a callable at this point!" );
+ }
+ }
+
+ $query->type( 'SELECT' );
+ $query->options( [ 'LIMIT' => $requestOptions->getLimit() ] );
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ $result = $this->iteratorFactory->newMappingIterator(
+ $this->iteratorFactory->newResultIterator( $res ),
+ function( $row ) {
+ return $this->store->getObjectIds()->getDataItemById( $row->s_id );
+ }
+ );
+
+ return $result;
+ }
+
+ private function resolve_value_condition( $propertyTable, $property, $dataItem, $query ) {
+
+ // Collect conditions to appear as
+ // `... (t1.p_id='121913' AND t1.o_sortkey='3520062') ...`
+ $conditions = [];
+
+ // Keep the index in case of a recursive iteration
+ $i = $query->index;
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+
+ $pid = $this->store->getObjectIds()->getSMWPropertyID(
+ $property
+ );
+
+ $conditions[] = $query->eq( "{$query->alias}{$i}.p_id", $pid );
+ }
+
+ $diHandler = $this->store->getDataItemHandlerForDIType(
+ $propertyTable->getDiType()
+ );
+
+ if ( !$dataItem instanceof DIContainer ) {
+ foreach ( $diHandler->getWhereConds( $dataItem ) as $fieldName => $value ) {
+ $conditions[] = $query->eq( "{$query->alias}{$i}.$fieldName", $value );
+ }
+ } else {
+
+ /**
+ * For a container based property/value pair we expected something similar
+ * to:
+ *
+ * SELECT t1.s_id FROM `smw_di_wikipage` AS t1
+ * INNER JOIN `smw_di_wikipage` AS t2 ON t2.s_id=t1.o_id
+ * INNER JOIN `smw_di_wikipage` AS t3 ON t3.s_id=t1.o_id
+ * INNER JOIN `smw_di_number` AS t4 ON t4.s_id=t1.o_id
+ * WHERE
+ * (t2.p_id='333615' AND t2.o_id='302096') AND
+ * (t3.p_id='333611' AND t3.o_id='193213') AND
+ * (t4.p_id='121913' AND t4.o_sortkey='3520062') AND
+ * (t1.p_id='310161') AND (t1.s_id!='333608')
+ * LIMIT 2
+ */
+
+ // Handle containers recursively
+ $this->resolve_container_conditions( $propertyTable, $dataItem, $query );
+ }
+
+ $query->condition( $query->asAnd( $conditions ) );
+ }
+
+ private function resolve_container_conditions( $propertyTable, $dataItem, $query ) {
+
+ $proptables = $this->store->getPropertyTables();
+ $semanticData = $dataItem->getSemanticData();
+
+ $alias = $query->alias;
+ $i = $query->index;
+
+ // ought to be a type 'p' object
+ $keys = array_keys( $propertyTable->getFields( $this->store ) );
+ $joinfield = "{$alias}{$i}." . reset( $keys );
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ $tableid = $this->store->findPropertyTableID( $property );
+ $subproptable = $proptables[$tableid];
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $subvalue ) {
+ // Increase the index for each iteration to ensure that each
+ // condition has its own alias
+ $i++;
+
+ if ( $subproptable->usesIdSubject() ) {
+ // simply add property table to check values
+ $query->join(
+ 'INNER JOIN',
+ [
+ // e.g. `... INNER JOIN `smw_di_wikipage` AS t2 ON t2.s_id=t1.o_id ...`
+ $subproptable->getName() => "{$alias}{$i} ON {$alias}{$i}.s_id=$joinfield"
+ ]
+ );
+ } else {
+ // Rare case with a table that uses subject title+namespace
+ // in a container object (should never happen in SMW core!!)
+ $query->join(
+ 'INNER JOIN',
+ [
+ SQLStore::ID_TABLE => "ids{$i} ON ids{$i}.smw_id=$joinfield"
+ ]
+ );
+ $query->join(
+ 'INNER JOIN',
+ [
+ $subproptable->getName() => "{$alias}{$i} ON {$alias}{$i}.s_title=ids{$alias}{$i}.smw_title AND {$alias}{$i}.s_namespace=ids{$alias}{$i}.smw_namespace"
+ ]
+ );
+ }
+
+ $query->index = $i;
+ $this->resolve_value_condition( $subproptable, $property, $subvalue, $query );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/PropertyStatisticsInvalidArgumentException.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/PropertyStatisticsInvalidArgumentException.php
new file mode 100644
index 00000000..af143789
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/PropertyStatisticsInvalidArgumentException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace SMW\SQLStore\Exception;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatisticsInvalidArgumentException extends InvalidArgumentException {
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/TableMissingIdFieldException.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/TableMissingIdFieldException.php
new file mode 100644
index 00000000..9c7cf1aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Exception/TableMissingIdFieldException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace SMW\SQLStore\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TableMissingIdFieldException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( $name ) {
+ parent::__construct( "Operation is not supported for a table ({$name}) without subject IDs." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php
new file mode 100644
index 00000000..ec68ddf4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php
@@ -0,0 +1,463 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Hooks;
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\CompatibilityMode;
+use SMW\MediaWiki\Jobs\EntityIdDisposerJob;
+use SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob;
+use SMW\Options;
+use SMW\Site;
+use SMW\Utils\File;
+use SMW\Exception\FileNotWritableException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Installer implements MessageReporter {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * Optimize option
+ */
+ const OPT_TABLE_OPTIMIZE = 'installer.table.optimize';
+
+ /**
+ * Job option
+ */
+ const OPT_SUPPLEMENT_JOBS = 'installer.supplement.jobs';
+
+ /**
+ * Import option
+ */
+ const OPT_IMPORT = 'installer.import';
+
+ /**
+ * Related to ExtensionSchemaUpdates
+ */
+ const OPT_SCHEMA_UPDATE = 'installer.schema.update';
+
+ /**
+ * `smw_hash` field population
+ */
+ const POPULATE_HASH_FIELD_COMPLETE = 'populate.smw_hash_field_complete';
+
+ /**
+ * @var TableSchemaManager
+ */
+ private $tableSchemaManager;
+
+ /**
+ * @var TableBuilder
+ */
+ private $tableBuilder;
+
+ /**
+ * @var TableIntegrityExaminer
+ */
+ private $tableIntegrityExaminer;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var File
+ */
+ private $file;
+
+ /**
+ * @since 2.5
+ *
+ * @param TableSchemaManager $tableSchemaManager
+ * @param TableBuilder $tableBuilder
+ * @param TableIntegrityExaminer $tableIntegrityExaminer
+ */
+ public function __construct( TableSchemaManager $tableSchemaManager, TableBuilder $tableBuilder, TableIntegrityExaminer $tableIntegrityExaminer ) {
+ $this->tableSchemaManager = $tableSchemaManager;
+ $this->tableBuilder = $tableBuilder;
+ $this->tableIntegrityExaminer = $tableIntegrityExaminer;
+ $this->options = new Options();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Options|array $options
+ */
+ public function setOptions( $options ) {
+
+ if ( !$options instanceof Options ) {
+ $options = new Options( $options );
+ }
+
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param File $file
+ */
+ public function setFile( File $file ) {
+ $this->file = $file;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $verbose
+ */
+ public function install( $verbose = true ) {
+
+ // If for some reason the enableSemantics was not yet enabled
+ // still allow to run the tables create in order for the
+ // setup to be completed
+ if ( CompatibilityMode::extensionNotEnabled() ) {
+ CompatibilityMode::enableTemporaryCliUpdateMode();
+ }
+
+ $messageReporter = $this->newMessageReporter( $verbose );
+
+ $messageReporter->reportMessage( "\nSelected storage engine: \"SMWSQLStore3\" (or an extension thereof)\n" );
+ $messageReporter->reportMessage( "\nSetting up standard database configuration for SMW ...\n\n" );
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $this->tableIntegrityExaminer->setMessageReporter(
+ $messageReporter
+ );
+
+ foreach ( $this->tableSchemaManager->getTables() as $table ) {
+ $this->tableBuilder->create( $table );
+ }
+
+ $this->tableIntegrityExaminer->checkOnPostCreation( $this->tableBuilder );
+
+ $messageReporter->reportMessage( "\nDatabase initialized completed.\n" );
+
+ $this->table_optimization( $messageReporter );
+ $this->supplement_jobs( $messageReporter );
+
+ $file = $this->file !== null ? $this->file : new File();
+
+ self::setUpgradeKey( $GLOBALS, $messageReporter, $file );
+
+ Hooks::run(
+ 'SMW::SQLStore::Installer::AfterCreateTablesComplete',
+ [
+ $this->tableBuilder,
+ $messageReporter,
+ $this->options
+ ]
+ );
+
+ if ( $this->options->has( self::OPT_SCHEMA_UPDATE ) ) {
+ $messageReporter->reportMessage( "\n" );
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $verbose
+ */
+ public function uninstall( $verbose = true ) {
+
+ $messageReporter = $this->newMessageReporter( $verbose );
+
+ $messageReporter->reportMessage( "\nSelected storage engine: \"SMWSQLStore3\" (or an extension thereof)\n" );
+ $messageReporter->reportMessage( "\nDeleting all database content and tables generated by SMW ...\n\n" );
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ foreach ( $this->tableSchemaManager->getTables() as $table ) {
+ $this->tableBuilder->drop( $table );
+ }
+
+ $this->tableIntegrityExaminer->checkOnPostDestruction( $this->tableBuilder );
+
+ Hooks::run(
+ 'SMW::SQLStore::Installer::AfterDropTablesComplete',
+ [
+ $this->tableBuilder,
+ $messageReporter,
+ $this->options
+ ]
+ );
+
+ $messageReporter->reportMessage( "\nStandard and auxiliary tables with all corresponding data\n" );
+ $messageReporter->reportMessage( "have been removed successfully.\n" );
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $vars
+ */
+ public static function loadSchema( &$vars ) {
+
+ // @see #3506
+ $file = File::dir( $vars['smwgConfigFileDir'] . '/.smw.json' );
+
+ // Doesn't exist? The `Setup::init` will take care of it by trying to create
+ // a new file and if it fails or unable to do so wail raise an exception
+ // as we expect to have access to it.
+ if ( is_readable( $file ) ) {
+ $vars['smw.json'] = json_decode( file_get_contents( $file ), true );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isCli
+ *
+ * @return boolean
+ */
+ public static function isGoodSchema( $isCli = false ) {
+
+ if ( $isCli && defined( 'MW_PHPUNIT_TEST' ) ) {
+ return true;
+ }
+
+ if ( $isCli === false && ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) ) {
+ return true;
+ }
+
+ // #3563, Use the specific wiki-id as identifier for the instance in use
+ $id = Site::id();
+
+ if ( !isset( $GLOBALS['smw.json'][$id]['upgrade_key'] ) ) {
+ return false;
+ }
+
+ return self::makeUpgradeKey( $GLOBALS ) === $GLOBALS['smw.json'][$id]['upgrade_key'];
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param array $vars
+ *
+ * @return []
+ */
+ public static function incompleteTasks( $vars ) {
+
+ $id = Site::id();
+ $tasks = [];
+
+ // Key field => [ value that constitutes the `INCOMPLETE` state, error msg ]
+ $checks = [
+ self::POPULATE_HASH_FIELD_COMPLETE => [ false, 'smw-install-incomplete-populate-hash-field' ]
+ ];
+
+ foreach ( $checks as $key => $value ) {
+ if ( isset( $vars['smw.json'][$id][$key] ) && $vars['smw.json'][$id][$key] === $value[0] ) {
+ $tasks[] = $value[1];
+ }
+ }
+
+ return $tasks;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $vars
+ *
+ * @return string
+ */
+ public static function makeUpgradeKey( $vars ) {
+
+ // The following settings influence the "shape" of the tables required
+ // therefore use the content to compute a key that reflects any
+ // changes to them
+
+ // Only recognize those properties that require a fixed table
+ $pageSpecialProperties = array_intersect(
+ $vars['smwgPageSpecialProperties'],
+ PropertyTableInfoFetcher::getFixedSpecialPropertyList()
+ );
+
+ // Sort to ensure the key contains the same order
+ sort( $vars['smwgFixedProperties'] );
+ sort( $pageSpecialProperties );
+
+ return sha1(
+ json_encode(
+ [
+ $vars['smwgUpgradeKey'],
+ $vars['smwgFixedProperties'],
+ $pageSpecialProperties
+ ]
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $vars
+ * @param MessageReporter $messageReporter|null
+ * @param File $file|null
+ */
+ public static function setUpgradeKey( $vars, MessageReporter $messageReporter = null, File $file = null ) {
+
+ // #3563, Use the specific wiki-id as identifier for the instance in use
+ $key = self::makeUpgradeKey( $vars );
+ $id = Site::id();
+
+ if (
+ isset( $vars['smw.json'][$id]['upgrade_key'] ) &&
+ $key === $vars['smw.json'][$id]['upgrade_key'] ) {
+ return false;
+ }
+
+ if ( $messageReporter !== null ) {
+ $messageReporter->reportMessage( "\nSetting $id upgrade key ..." );
+ }
+
+ self::setUpgradeFile( $vars, [ 'upgrade_key' => $key ], $file );
+
+ if ( $messageReporter !== null ) {
+ $messageReporter->reportMessage( "\n ... done.\n" );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param File $file
+ * @param array $vars
+ */
+ public static function setUpgradeFile( $vars, $args = [], File $file = null ) {
+
+ $configFile = $vars['smwgConfigFileDir'] . '/.smw.json';
+
+ if ( $file === null ) {
+ $file = new File();
+ }
+
+ $id = Site::id();
+
+ if ( !isset( $vars['smw.json'] ) ) {
+ $vars['smw.json'] = [];
+ }
+
+ foreach ( $args as $key => $value ) {
+ $vars['smw.json'][$id][$key] = $value;
+ }
+
+ try {
+ $file->write(
+ $configFile,
+ json_encode( $vars['smw.json'], JSON_PRETTY_PRINT )
+ );
+ } catch( FileNotWritableException $e ) {
+ // Users may not have `wgShowExceptionDetails` enabled and would
+ // therefore not see the exception error message hence we fail hard
+ // and die
+ die(
+ "\n\nERROR: " . $e->getMessage() . "\n" .
+ "\n The \"smwgConfigFileDir\" setting should point to a" .
+ "\n directory that is persistent and writable!\n"
+ );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+ ob_start();
+ print $message;
+ ob_flush();
+ flush();
+ ob_end_clean();
+ }
+
+ private function newMessageReporter( $verbose = true ) {
+
+ if ( $this->messageReporter !== null && !$this->options->safeGet( self::OPT_SCHEMA_UPDATE, false ) ) {
+ return $this->messageReporter;
+ }
+
+ $messageReporterFactory = MessageReporterFactory::getInstance();
+
+ if ( !$verbose ) {
+ $messageReporter = $messageReporterFactory->newNullMessageReporter();
+ } else {
+ $messageReporter = $messageReporterFactory->newObservableMessageReporter();
+ $messageReporter->registerReporterCallback( [ $this, 'reportMessage' ] );
+ }
+
+ return $messageReporter;
+ }
+
+ private function table_optimization( $messageReporter ) {
+
+ if ( !$this->options->safeGet( self::OPT_TABLE_OPTIMIZE, false ) ) {
+ return $messageReporter->reportMessage( "\nSkipping the table optimization.\n" );
+ }
+
+ $messageReporter->reportMessage( "\nRunning table optimization (this may take a moment) ...\n\n" );
+
+ foreach ( $this->tableSchemaManager->getTables() as $table ) {
+ $this->tableBuilder->optimize( $table );
+ }
+
+ $messageReporter->reportMessage( "\nOptimization completed.\n" );
+ }
+
+ private function supplement_jobs( $messageReporter ) {
+
+ if ( !$this->options->safeGet( self::OPT_SUPPLEMENT_JOBS, false ) ) {
+ return $messageReporter->reportMessage( "\nSkipping supplement job creation.\n" );
+ }
+
+ $messageReporter->reportMessage( "\nAdding property statistics rebuild job ...\n" );
+
+ $title = \Title::newFromText( 'SMW\SQLStore\Installer' );
+
+ $job = new PropertyStatisticsRebuildJob(
+ $title,
+ PropertyStatisticsRebuildJob::newRootJobParams( 'smw.propertyStatisticsRebuild', $title ) + [ 'waitOnCommandLine' => true ]
+ );
+
+ $job->insert();
+
+ $messageReporter->reportMessage( " ... done.\n" );
+ $messageReporter->reportMessage( "\nAdding entity disposer job ...\n" );
+
+ $job = new EntityIdDisposerJob(
+ $title,
+ EntityIdDisposerJob::newRootJobParams( 'smw.entityIdDisposer', $title ) + [ 'waitOnCommandLine' => true ]
+ );
+
+ $job->insert();
+
+ $messageReporter->reportMessage( " ... done.\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/CachedListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/CachedListLookup.php
new file mode 100644
index 00000000..d15bca3a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/CachedListLookup.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use Onoi\Cache\Cache;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CachedListLookup implements ListLookup {
+
+ const VERSION = '0.2';
+
+ /**
+ * @var ListLookup
+ */
+ private $listLookup;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var stdClass
+ */
+ private $cacheOptions;
+
+ /**
+ * @var boolean
+ */
+ private $isFromCache = false;
+
+ /**
+ * @var integer
+ */
+ private $timestamp;
+
+ /**
+ * @var string
+ */
+ private $cachePrefix = 'smw:store:lookup:';
+
+ /**
+ * @since 2.2
+ *
+ * @param ListLookup $listLookup
+ * @param Cache $cache
+ * @param stdClass $cacheOptions
+ */
+ public function __construct( ListLookup $listLookup, Cache $cache, \stdClass $cacheOptions ) {
+ $this->listLookup = $listLookup;
+ $this->cache = $cache;
+ $this->cacheOptions = $cacheOptions;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $cachePrefix
+ */
+ public function setCachePrefix( $cachePrefix ) {
+ $this->cachePrefix = $cachePrefix . ':' . $this->cachePrefix;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function fetchList() {
+
+ list( $key, $optionsKey ) = $this->getCacheKey( $this->listLookup->getHash() );
+
+ if ( $this->cacheOptions->useCache && ( ( $result = $this->tryFetchFromCache( $key, $optionsKey ) ) !== null ) ) {
+ return $result;
+ }
+
+ $list = $this->listLookup->fetchList();
+
+ $this->saveToCache(
+ $key,
+ $optionsKey,
+ $list,
+ $this->listLookup->getTimestamp(),
+ $this->cacheOptions->ttl
+ );
+
+ return $list;
+ }
+
+ /**
+ * FIXME NEEDS TO BE REMOVED QUICK
+ * https://github.com/wikimedia/mediawiki-extensions-SemanticForms/blob/master/specials/SF_CreateTemplate.php#L36
+ */
+ public function runCollector() {
+ return $this->fetchList();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return $this->isFromCache;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return $this->timestamp;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->listLookup->getHash();
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function deleteCache() {
+
+ list( $id, $optionsKey ) = $this->getCacheKey(
+ $this->listLookup->getHash()
+ );
+
+ $data = unserialize( $this->cache->fetch( $id ) );
+
+ if ( $data && $data !== [] ) {
+ foreach ( $data as $key => $value ) {
+ $this->cache->delete( $key );
+ }
+ }
+
+ $this->cache->delete( $id );
+ }
+
+ private function tryFetchFromCache( $key, $optionsKey ) {
+
+ if ( !$this->cache->contains( $key ) ) {
+ return null;
+ }
+
+ $data = unserialize( $this->cache->fetch( $optionsKey ) );
+
+ if ( $data === [] ) {
+ return null;
+ }
+
+ $this->isFromCache = true;
+ $this->timestamp = $data['time'];
+
+ return $data['list'];
+ }
+
+ private function saveToCache( $key, $optionsKey, $list, $time, $ttl ) {
+
+ $this->timestamp = $time;
+ $this->isFromCache = false;
+
+ // Collect the options keys
+ $data = unserialize( $this->cache->fetch( $key ) );
+ $data[$optionsKey] = true;
+ $this->cache->save( $key, serialize( $data ), $ttl );
+
+ $data = [
+ 'time' => $this->timestamp,
+ 'list' => $list
+ ];
+
+ $this->cache->save( $optionsKey, serialize( $data ), $ttl );
+ }
+
+ private function getCacheKey( $id ) {
+
+ $optionsKey = '';
+
+ if ( strpos( $id, '#' ) !== false ) {
+ list( $id, $optionsKey ) = explode( '#', $id, 2 );
+ }
+
+ return [
+ $this->cachePrefix . md5( $id . self::VERSION ),
+ $this->cachePrefix . md5( $id . $optionsKey . self::VERSION )
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ListLookup.php
new file mode 100644
index 00000000..5772c841
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ListLookup.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+/**
+ * A simple interface for fetching a list from either a DB or being used as
+ * decorator to cache results
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface ListLookup {
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function fetchList();
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache();
+
+ /**
+ * A unique identifier that can describe a specific lookup instance to
+ * distinguish it from other lookup's of the same list
+ *
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash();
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyLabelSimilarityLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyLabelSimilarityLookup.php
new file mode 100644
index 00000000..16bef594
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyLabelSimilarityLookup.php
@@ -0,0 +1,318 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use Exception;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\PropertySpecificationLookup;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyLabelSimilarityLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ private $propertySpecificationLookup;
+
+ /**
+ * @var integer/float
+ */
+ private $threshold = 50;
+
+ /**
+ * @var DIProperty|null
+ */
+ private $exemptionProperty;
+
+ /**
+ * @var integer
+ */
+ private $lookupCount = 0;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param PropertySpecificationLookup|null $propertySpecificationLookup
+ */
+ public function __construct( Store $store, PropertySpecificationLookup $propertySpecificationLookup = null ) {
+ $this->store = $store;
+ $this->propertySpecificationLookup = $propertySpecificationLookup;
+
+ if ( $this->propertySpecificationLookup === null ) {
+ $this->propertySpecificationLookup = ApplicationFactory::getInstance()->getPropertySpecificationLookup();
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $threshold
+ *
+ * @return boolean
+ */
+ public function setThreshold( $threshold ) {
+ $this->threshold = $threshold;
+ }
+
+ /**
+ * @note A property that when annotated as part of a property specification
+ * will be used as exemption marker during the similarity comparison.
+ *
+ * @since 2.5
+ *
+ * @param string $exemptionProperty
+ */
+ public function setExemptionProperty( $exemptionProperty ) {
+
+ if ( $exemptionProperty === '' ) {
+ return;
+ }
+
+ $this->exemptionProperty = DataValueFactory::getInstance()->newPropertyValueByLabel( $exemptionProperty )->getDataItem();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DIProperty|null
+ */
+ public function getExemptionProperty() {
+ return $this->exemptionProperty;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getLookupCount() {
+ return $this->lookupCount;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getPropertyMaxCount() {
+ $statistics = $this->store->getStatistics();
+
+ if ( isset( $statistics['TOTALPROPS'] ) ) {
+ return $statistics['TOTALPROPS'];
+ }
+
+ return 0;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return array
+ */
+ public function compareAndFindLabels( RequestOptions $requestOptions = null ) {
+
+ $withType = false;
+ $propertyList = $this->getPropertyList( $requestOptions );
+
+ if ( $requestOptions !== null ) {
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ if ( isset( $extraCondition['type'] ) ) {
+ $withType = $extraCondition['type'];
+ }
+ }
+ }
+
+ $this->lookupCount = count( $propertyList );
+ $similarities = $this->matchLabels( $propertyList, $withType );
+
+ usort( $similarities, function ( $a, $b ) {
+ return $a['similarity'] < $b['similarity'];
+ } );
+
+ return $similarities;
+ }
+
+ private function matchLabels( $propertyList, $withType ) {
+
+ $similarities = [];
+ $lookupComplete = [];
+
+ foreach ( $propertyList as $first ) {
+
+ if ( !$first->isUserDefined() ) {
+ continue;
+ }
+
+ foreach ( $propertyList as $second ) {
+
+ // Was already completed when used as first element
+ if ( isset( $lookupComplete[$second->getKey()] ) ) {
+ continue;
+ }
+
+ if ( $first->getKey() === $second->getKey() || !$second->isUserDefined() ) {
+ continue;
+ }
+
+ $hash = $this->getHash( $first, $second );
+
+ if ( $this->isExempted( $first, $second ) || isset( $similarities[$hash] ) ) {
+ continue;
+ }
+
+ $percent = '';
+
+ similar_text( $first->getLabel(), $second->getLabel(), $percent );
+
+ if ( $percent >= $this->threshold ) {
+ $similarities[$hash] = $this->getSummary( $first, $second, $percent, $withType );
+ }
+ }
+
+ $lookupComplete[$first->getKey()] = true;
+ }
+
+ return $similarities;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $first
+ * @param DIProperty $second
+ *
+ * @return boolean
+ */
+ private function isExempted( DIProperty $first, DIProperty $second ) {
+
+ if ( $this->exemptionProperty === null ) {
+ return false;
+ }
+
+ $definedBy = $this->propertySpecificationLookup->getSpecification(
+ $first,
+ $this->exemptionProperty
+ );
+
+ foreach ( $definedBy as $dataItem ) {
+ if ( $dataItem->equals( $second->getCanonicalDiWikiPage() ) ) {
+ return true;
+ }
+ }
+
+ $definedBy = $this->propertySpecificationLookup->getSpecification(
+ $second,
+ $this->exemptionProperty
+ );
+
+ foreach ( $definedBy as $dataItem ) {
+ if ( $dataItem->equals( $first->getCanonicalDiWikiPage() ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function getHash( DIProperty $first, DIProperty $second ) {
+
+ $hashing = [];
+ $hashing[] = $first->getKey();
+ $hashing[] = $second->getKey();
+
+ sort( $hashing );
+
+ return md5( implode( '', $hashing ) );
+ }
+
+ private function getSummary( DIProperty $first, DIProperty $second, $percent, $withType ) {
+
+ $summary = [];
+
+ if ( $withType ) {
+ $summary[] = [
+ 'label' => $first->getLabel(),
+ 'type' => $first->findPropertyTypeID()
+ ];
+ } else {
+ $summary[] = $first->getLabel();
+ }
+
+ if ( $withType ) {
+ $summary[] = [
+ 'label' => $second->getLabel(),
+ 'type' => $second->findPropertyTypeID()
+ ];
+ } else {
+ $summary[] = $second->getLabel();
+ }
+
+ return [
+ 'property' => $summary,
+ 'similarity' => round( $percent, 2 )
+ ];
+ }
+
+ private function getPropertyList( RequestOptions $requestOptions = null ) {
+
+ $propertyList = [];
+
+ // the query needs to do the filtering of internal properties, else LIMIT is wrong
+ $options = [ 'ORDER BY' => 'smw_sort' ];
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ if ( $requestOptions !== null && $requestOptions->getLimit() > 0 ) {
+ $options['LIMIT'] = $requestOptions->getLimit();
+ $options['OFFSET'] = max( $requestOptions->getOffset(), 0 );
+ }
+
+ if ( $requestOptions !== null && $requestOptions->getStringConditions() ) {
+ $conditions[] = $this->store->getSQLConditions( $requestOptions, '', 'smw_sortkey', false );
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $res = $connection->select(
+ SQLStore::ID_TABLE,
+ [ 'smw_id', 'smw_title' ],
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ foreach ( $res as $row ) {
+
+ try {
+ $propertyList[] = new DIProperty( str_replace( ' ', '_', $row->smw_title ) );
+ } catch ( Exception $e ) {
+ // Do nothing ...
+ }
+ }
+
+ return $propertyList;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyUsageListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyUsageListLookup.php
new file mode 100644
index 00000000..819d92e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/PropertyUsageListLookup.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\Exception\PropertyLabelNotResolvedException;
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMWDIError as DIError;
+use SMWRequestOptions as RequestOptions;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class PropertyUsageListLookup implements ListLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @var RequestOptions
+ */
+ private $requestOptions;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ * @param RequestOptions $requestOptions|null
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore, RequestOptions $requestOptions = null ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ $this->requestOptions = $requestOptions;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DIProperty[]
+ * @throws RuntimeException
+ */
+ public function fetchList() {
+
+ if ( $this->requestOptions === null ) {
+ throw new RuntimeException( "Missing requestOptions" );
+ }
+
+ return $this->getPropertyList( $this->doQueryPropertyTable() );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return false;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return wfTimestamp( TS_UNIX );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return __METHOD__ . '#' . ( $this->requestOptions !== null ? $this->requestOptions->getHash() : '' );
+ }
+
+ private function doQueryPropertyTable() {
+
+ // the query needs to do the filtering of internal properties, else LIMIT is wrong
+ $options = [ 'ORDER BY' => 'smw_sort' ];
+ $search_field = 'smw_sortkey';
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ if ( $this->requestOptions->limit > 0 ) {
+ $options['LIMIT'] = $this->requestOptions->limit;
+ $options['OFFSET'] = max( $this->requestOptions->offset, 0 );
+ }
+
+ if ( $this->requestOptions->getOption( RequestOptions::SEARCH_FIELD ) ) {
+ $search_field = $this->requestOptions->getOption( RequestOptions::SEARCH_FIELD );
+ }
+
+ if ( $this->requestOptions->getStringConditions() ) {
+ $conditions[] = $this->store->getSQLConditions( $this->requestOptions, '', $search_field, false );
+ }
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ $res = $db->select(
+ [ $db->tableName( SQLStore::ID_TABLE ), $db->tableName( SQLStore::PROPERTY_STATISTICS_TABLE ) ],
+ [ 'smw_id', 'smw_title', 'usage_count' ],
+ $conditions,
+ __METHOD__,
+ $options,
+ [ $db->tableName( SQLStore::ID_TABLE ) => [ 'INNER JOIN', [ 'smw_id=p_id' ] ] ]
+ );
+
+ return $res;
+ }
+
+ private function getPropertyList( $res ) {
+
+ $result = [];
+
+ foreach ( $res as $row ) {
+
+ try {
+ $property = new DIProperty( str_replace( ' ', '_', $row->smw_title ) );
+ } catch ( PropertyLabelNotResolvedException $e ) {
+ $property = new DIError( new \Message( 'smw_noproperty', [ $row->smw_title ] ) );
+ }
+
+ $property->id = isset( $row->smw_id ) ? $row->smw_id : -1;
+ $result[] = [ $property, (int)$row->usage_count ];
+ }
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ProximityPropertyValueLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ProximityPropertyValueLookup.php
new file mode 100644
index 00000000..04610184
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/ProximityPropertyValueLookup.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Store;
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\RequestOptions;
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+use SMWDITime as DITime;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ProximityPropertyValueLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param $search,
+ * @param RequestOptions $opts
+ *
+ * @return array
+ */
+ public function lookup( DIProperty $property, $search, RequestOptions $opts ) {
+ return $this->fetchFromTable( $property, $search, $opts );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIProperty $property
+ * @param $search,
+ * @param RequestOptions $opts
+ *
+ * @return array
+ */
+ public function fetchFromTable( DIProperty $property, $search, RequestOptions $opts ) {
+
+ $options = [];
+ $list = [];
+
+ $table = $this->store->findPropertyTableID(
+ $property
+ );
+
+ $pid = $this->store->getObjectIds()->getSMWPropertyID( $property );
+ $continueOffset = 0;
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $query = $connection->newQuery();
+
+ $query->type( 'SELECT' );
+ $query->table( $table );
+
+ list( $field, $diType ) = $this->getField( $property );
+
+ // look ahead +1
+ $limit = $opts->getLimit() + 1;
+ $offset = $opts->getOffset();
+ $sort = $opts->sort;
+
+ $options = [
+ 'LIMIT' => $limit,
+ 'OFFSET' => $offset
+ ];
+
+ if ( $diType === DataItem::TYPE_WIKIPAGE ) {
+ return $this->fetchFromIDTable( $query, $pid, $table, $field, $options, $search, $sort, $limit, $offset );
+ }
+
+ $query->field( $field );
+
+ if ( trim( $search ) !== '' ) {
+ if ( $diType === DataItem::TYPE_BLOB || $diType === DataItem::TYPE_URI ) {
+ $this->build_like( $query, $field, $search );
+ } else {
+ $query->condition( $query->like( $field, '%' . $search . '%' ) );
+ }
+ } else {
+ $query->condition( $query->neq( $field, 'NULL' ) );
+ }
+
+ if ( $this->isFixedPropertyTable( $table ) === false ) {
+ $query->condition( $query->asAnd( $query->eq( 'p_id', $pid ) ) );
+
+ // To make the MySQL query planner happy to pick the right index!
+ $query->field( 'p_id' );
+ }
+
+ if ( $sort ) {
+ $options['ORDER BY'] = "$field $sort";
+ }
+
+ $options['DISTINCT'] = true;
+
+ $query->options( $options );
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+
+ $value = $row->{$field};
+
+ // The internal serialization doesn't mean much to a user so
+ // transformed it!
+ if ( $diType === DataItem::TYPE_TIME ) {
+ $value = DataValueFactory::getInstance()->newDataValueByItem(
+ DITime::doUnserialize( $value ), $property )->getWikiValue();
+ }
+
+ $list[] = $value;
+ }
+
+ return $list;
+ }
+
+ private function fetchFromIDTable( $query, $pid, $table, $field, $options, $search, $sort, $limit, $offset ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $continueOffset = 0;
+ $res = [];
+
+ if ( trim( $search ) !== '' ) {
+ $this->build_like( $query, 'smw_sortkey', $search );
+ }
+
+ if ( $sort ) {
+ $options['ORDER BY'] = "smw_title $sort";
+ }
+
+ $options['DISTINCT'] = true;
+
+ $query->options( $options );
+ $query->fields( [ 'smw_id', 'smw_title', 'smw_sortkey' ] );
+
+ // Benchmarks showed that different select schema yield better results
+ // for the following use cases
+ if ( $this->isFixedPropertyTable( $table ) === false && $search !== '' ) {
+
+ /**
+ * SELECT DISTINCT smw_id,smw_title,smw_sortkey
+ * FROM `smw_object_ids`
+ * INNER JOIN (
+ * SELECT o_id FROM `smw_di_wikipage` WHERE p_id='310167' GROUP BY o_id
+ * ) AS t1 ON t1.o_id=smw_id
+ * WHERE ( smw_sortkey LIKE '%foo%' OR smw_sortkey LIKE '%Foo%' OR smw_sortkey LIKE '%FOO%')
+ * LIMIT 11
+ */
+
+ $query->table( SQLStore::ID_TABLE );
+
+ $query->join(
+ 'INNER JOIN',
+ '( SELECT o_id FROM ' . $connection->tableName( $table ) .
+ ' WHERE p_id=' . $connection->addQuotes( $pid ) .
+ ' GROUP BY o_id )' .
+ ' AS t1 ON t1.o_id=smw_id'
+ );
+
+ } elseif ( $this->isFixedPropertyTable( $table ) === false ) {
+
+ $query->condition( $query->asAnd( $query->eq( 'p_id', $pid ) ) );
+
+ // To make the MySQL query planner happy to pick the right index!
+ $query->field( 'p_id' );
+
+ $query->join(
+ 'INNER JOIN',
+ [ SQLStore::ID_TABLE => 'ON (smw_id=o_id)' ]
+ );
+
+ } else {
+
+ /**
+ * SELECT DISTINCT smw_id,smw_title,smw_sortkey
+ * FROM `smw_fpt_sobj`
+ * INNER JOIN `smw_object_ids` ON ((smw_id=o_id))
+ * WHERE ( smw_sortkey LIKE '%foo%' OR smw_sortkey LIKE '%Foo%' OR smw_sortkey LIKE '%FOO%' )
+ * LIMIT 11
+ */
+ $query->join(
+ 'INNER JOIN',
+ [ SQLStore::ID_TABLE => 'ON (smw_id=o_id)' ]
+ );
+ }
+
+ $res = $connection->query(
+ $query,
+ __METHOD__
+ );
+
+ $list = [];
+
+ foreach ( $res as $row ) {
+ $list[] = str_replace( '_', ' ', $row->smw_title );
+ }
+
+ return $list;
+ }
+
+ private function isFixedPropertyTable( $table ) {
+
+ $propertyTables = $this->store->getPropertyTables();
+
+ foreach ( $propertyTables as $propertyTable ) {
+ if ( $propertyTable->getName() === $table ) {
+ return $propertyTable->isFixedPropertyTable();
+ }
+ }
+
+ return false;
+ }
+
+ private function getField( $property ) {
+
+ $typeId = $property->findPropertyTypeID();
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $typeId );
+
+ $diHandler = $this->store->getDataItemHandlerForDIType(
+ $diType
+ );
+
+ return [ $diHandler->getLabelField(), $diType ];
+ }
+
+ private function build_like( $query, $field, $search ) {
+
+ $conds = [
+ '%' . $search . '%',
+ '%' . ucfirst( $search ) . '%',
+ '%' . strtoupper( $search ) . '%'
+ ] + ( $search !== strtolower( $search ) ? [ '%' . strtolower( $search ) . '%' ] : [] );
+
+ $cond = [];
+
+ foreach ( $conds as $c ) {
+ $query->condition( $query->asOr( $query->like( $field, $c ) ) );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/RedirectTargetLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/RedirectTargetLookup.php
new file mode 100644
index 00000000..b3aff967
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/RedirectTargetLookup.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Store;
+use SMW\Utils\CircularReferenceGuard;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RedirectTargetLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var CircularReferenceGuard
+ */
+ private $circularReferenceGuard;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param CircularReferenceGuard $circularReferenceGuard
+ */
+ public function __construct( Store $store, CircularReferenceGuard $circularReferenceGuard ) {
+ $this->store = $store;
+ $this->circularReferenceGuard = $circularReferenceGuard;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param $dataItem
+ *
+ * @return DataItem
+ */
+ public function findRedirectTarget( $dataItem ) {
+
+ if ( !$dataItem instanceof DIWikiPage && !$dataItem instanceof DIProperty ) {
+ return $dataItem;
+ }
+
+ $hash = $dataItem->getSerialization();
+
+ // Guard against a dataItem that points to itself
+ $this->circularReferenceGuard->mark( $hash );
+
+ if ( !$this->circularReferenceGuard->isCircular( $hash ) ) {
+ $dataItem = $this->store->getRedirectTarget( $dataItem );
+ }
+
+ $this->circularReferenceGuard->unmark( $hash );
+
+ return $dataItem;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UndeclaredPropertyListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UndeclaredPropertyListLookup.php
new file mode 100644
index 00000000..d5c8435c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UndeclaredPropertyListLookup.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\Exception\PropertyLabelNotResolvedException;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMWDIError as DIError;
+use SMWRequestOptions as RequestOptions;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ * @author Nischay Nahata
+ */
+class UndeclaredPropertyListLookup implements ListLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var string
+ */
+ private $defaultPropertyType;
+
+ /**
+ * @var RequestOptions
+ */
+ private $requestOptions;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param string $defaultPropertyType
+ * @param RequestOptions $requestOptions|null
+ */
+ public function __construct( Store $store, $defaultPropertyType, RequestOptions $requestOptions = null ) {
+ $this->store = $store;
+ $this->defaultPropertyType = $defaultPropertyType;
+ $this->requestOptions = $requestOptions;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DIProperty[]
+ * @throws RuntimeException
+ */
+ public function fetchList() {
+
+ if ( $this->requestOptions === null ) {
+ throw new RuntimeException( "Missing requestOptions" );
+ }
+
+ // Wanted Properties must have the default type
+ $propertyTable = $this->getPropertyTableForType( $this->defaultPropertyType );
+
+ if ( $propertyTable->isFixedPropertyTable() ) {
+ return [];
+ }
+
+ return $this->buildPropertyList( $this->selectPropertiesFromTable( $propertyTable ) );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return false;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return wfTimestamp( TS_UNIX );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return __METHOD__ . '#' . ( $this->requestOptions !== null ? $this->requestOptions->getHash() : '' );
+ }
+
+ private function selectPropertiesFromTable( $propertyTable ) {
+
+ $options = $this->store->getSQLOptions( $this->requestOptions, 'title' );
+ $idTable = SQLStore::ID_TABLE;
+
+ $options['ORDER BY'] = 'count DESC';
+
+ // Postgres Error: 42803 ERROR: ...smw_title must appear in the GROUP BY
+ // clause or be used in an aggregate function
+ $options['GROUP BY'] = 'smw_id, smw_title';
+
+ $conditions = [
+ 'smw_id > ' . SQLStore::FIXED_PROPERTY_ID_UPPERBOUND,
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_proptable_hash IS NULL',
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ $joinCond = 'p_id';
+
+ foreach ( $this->requestOptions->getExtraConditions() as $extaCondition ) {
+ if ( isset( $extaCondition['filter.unapprove'] ) ) {
+ $joinCond = 'o_id';
+ }
+ }
+
+ $res = $this->store->getConnection( 'mw.db' )->select(
+ [ $idTable, $propertyTable->getName() ],
+ [ 'smw_id', 'smw_title', 'COUNT(*) as count' ],
+ $conditions,
+ __METHOD__,
+ $options,
+ [
+ $idTable => [
+ 'INNER JOIN', "$joinCond=smw_id"
+ ]
+ ]
+ );
+
+ return $res;
+ }
+
+ private function buildPropertyList( $res ) {
+
+ $result = [];
+
+ foreach ( $res as $row ) {
+ $result[] = [ $this->addPropertyFor( $row->smw_title ), $row->count ];
+ }
+
+ return $result;
+ }
+
+ private function addPropertyFor( $title ) {
+
+ try {
+ $property = new DIProperty( $title );
+ } catch ( PropertyLabelNotResolvedException $e ) {
+ $property = new DIError( new \Message( 'smw_noproperty', [ $title ] ) );
+ }
+
+ return $property;
+ }
+
+ private function getPropertyTableForType( $type ) {
+
+ $propertyTables = $this->store->getPropertyTables();
+ $tableIdForType = $this->store->findTypeTableId( $type );
+
+ if ( isset( $propertyTables[$tableIdForType] ) ) {
+ return $propertyTables[$tableIdForType];
+ }
+
+ throw new RuntimeException( "Tried to access a table that doesn't exist for {$type}." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UnusedPropertyListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UnusedPropertyListLookup.php
new file mode 100644
index 00000000..ca3b8285
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UnusedPropertyListLookup.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\Exception\PropertyLabelNotResolvedException;
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMWDIError as DIError;
+use SMWRequestOptions as RequestOptions;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ * @author Nischay Nahata
+ */
+class UnusedPropertyListLookup implements ListLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @var RequestOptions
+ */
+ private $requestOptions;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ * @param RequestOptions $requestOptions|null
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore, RequestOptions $requestOptions = null ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ $this->requestOptions = $requestOptions;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DIProperty[]
+ * @throws RuntimeException
+ */
+ public function fetchList() {
+
+ if ( $this->requestOptions === null ) {
+ throw new RuntimeException( "Missing requestOptions" );
+ }
+
+ return $this->buildPropertyList( $this->selectPropertiesFromTable() );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return false;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return wfTimestamp( TS_UNIX );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return __METHOD__ . '#' . ( $this->requestOptions !== null ? $this->requestOptions->getHash() : '' );
+ }
+
+ private function selectPropertiesFromTable() {
+
+ // the query needs to do the filtering of internal properties, else LIMIT is wrong
+ $options = [ 'ORDER BY' => 'smw_sort' ];
+
+ if ( $this->requestOptions->limit > 0 ) {
+ $options['LIMIT'] = $this->requestOptions->limit;
+ $options['OFFSET'] = max( $this->requestOptions->offset, 0 );
+ }
+
+ $conditions = [
+ "smw_title NOT LIKE '\_%'", // #2182, exclude predefined properties
+ 'smw_id > ' . SQLStore::FIXED_PROPERTY_ID_UPPERBOUND,
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_proptable_hash IS NOT NULL'
+ ];
+
+ $conditions['usage_count'] = 0;
+
+ if ( $this->requestOptions->getStringConditions() ) {
+ $conditions[] = $this->store->getSQLConditions( $this->requestOptions, '', 'smw_sortkey', false );
+ }
+
+ $idTable = $this->store->getObjectIds()->getIdTable();
+
+ $res = $this->store->getConnection( 'mw.db' )->select(
+ [ $idTable ,$this->propertyStatisticsStore->getStatisticsTable() ],
+ [ 'smw_title', 'usage_count' ],
+ $conditions,
+ __METHOD__,
+ $options,
+ [ $idTable => [ 'INNER JOIN', [ 'smw_id=p_id' ] ] ]
+ );
+
+ return $res;
+ }
+
+ private function buildPropertyList( $res ) {
+
+ $result = [];
+
+ foreach ( $res as $row ) {
+ $result[] = $this->addPropertyFor( $row->smw_title );
+ }
+
+ return $result;
+ }
+
+ private function addPropertyFor( $title ) {
+
+ try {
+ $property = new DIProperty( $title );
+ } catch ( PropertyLabelNotResolvedException $e ) {
+ $property = new DIError( new \Message( 'smw_noproperty', [ $title ] ) );
+ }
+
+ return $property;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UsageStatisticsListLookup.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UsageStatisticsListLookup.php
new file mode 100644
index 00000000..34529efb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Lookup/UsageStatisticsListLookup.php
@@ -0,0 +1,352 @@
+<?php
+
+namespace SMW\SQLStore\Lookup;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class UsageStatisticsListLookup implements ListLookup {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ }
+
+ /**
+ * Returns a list with statistical information where keys are matched to:
+ *
+ * - 'PROPUSES': Number of property instances (value assignments) in the connection
+ * - 'USEDPROPS': Number of properties that are used with at least one value
+ * - 'DECLPROPS': Number of properties that have been declared (i.e. assigned a type)
+ * - 'OWNPAGE': Number of properties with their own page
+ * - 'QUERY': Number of inline queries
+ * - 'QUERYSIZE': Represents collective query size
+ * - 'CONCEPTS': Number of declared concepts
+ * - 'SUBOBJECTS': Number of declared subobjects
+ * - 'QUERYFORMATS': Array of used formats and its usage count
+ * - 'TOTALPROPS': Total number of registered properties
+ * - 'ERRORUSES': Number of annotations with an error
+ * - 'DELETECOUNT': Number of "marked for deletion"
+ *
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function fetchList() {
+ return [
+ 'OWNPAGE' => $this->getPropertyPageCount(),
+ 'QUERY' => $this->getQueryCount(),
+ 'QUERYSIZE' => $this->getQuerySize(),
+ 'QUERYFORMATS' => $this->getQueryFormatsCount(),
+ 'CONCEPTS' => $this->getConceptCount(),
+ 'SUBOBJECTS' => $this->getSubobjectCount(),
+ 'DECLPROPS' => $this->getDeclaredPropertiesCount(),
+ 'PROPUSES' => $this->getPropertyUsageCount(),
+ 'USEDPROPS' => $this->getUsedPropertiesCount(),
+ 'TOTALPROPS' => $this->getTotalPropertiesCount(),
+ 'ERRORUSES' => $this->getImproperValueForCount(),
+ 'DELETECOUNT' => $this->getDeleteCount()
+ ];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return false;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getTimestamp() {
+ return wfTimestamp( TS_UNIX );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getHash() {
+ return 'statistics-lookup';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return number
+ */
+ public function getImproperValueForCount() {
+ return $this->propertyStatisticsStore->getUsageCount(
+ $this->store->getObjectIds()->getSMWPropertyID( new DIProperty( '_ERRP' ) )
+ );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getQueryCount() {
+ return $this->count( '_ASK' );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getQuerySize() {
+ return $this->count( '_ASKSI' );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getConceptCount() {
+ return $this->count( '_CONC' );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getSubobjectCount() {
+ return $this->count( DIProperty::TYPE_SUBOBJECT );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getDeclaredPropertiesCount() {
+ return $this->count( DIProperty::TYPE_HAS_TYPE );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return int[]
+ */
+ public function getQueryFormatsCount() {
+ $count = [];
+
+ $res = $this->store->getConnection()->select(
+ $this->findPropertyTableByType( '_ASKFO' )->getName(),
+ 'o_hash, COUNT(s_id) AS count',
+ [],
+ __METHOD__,
+ [
+ 'ORDER BY' => 'count DESC',
+ 'GROUP BY' => 'o_hash'
+ ]
+ );
+
+ foreach ( $res as $row ) {
+ $count[$row->o_hash] = (int)$row->count;
+ }
+
+ return $count;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getPropertyPageCount() {
+
+ $options = [];
+
+ // Only match entities that have a NOT null smw_proptable_hash entry
+ // which indicates that it is not a object but a subject value (has
+ // annotations such as `has type` == page was created with ... etc.)
+ $conditions = [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_proptable_hash IS NOT NULL'
+ ];
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // Select object ID's against known property ID's that match the conditions
+ $res = $db->select(
+ [ $db->tableName( SQLStore::ID_TABLE ), $db->tableName( SQLStore::PROPERTY_STATISTICS_TABLE ) ],
+ 'smw_id',
+ $conditions,
+ __METHOD__,
+ $options,
+ [ $db->tableName( SQLStore::ID_TABLE ) => [ 'INNER JOIN', [ 'smw_id=p_id' ] ] ]
+ );
+
+ return $res->numRows();
+ }
+
+ /**
+ * Count property uses by summing up property statistics table
+ *
+ * @note subproperties that are part of container values are counted
+ * individually and it does not seem to be important to filter them by
+ * adding more conditions.
+ *
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getPropertyUsageCount() {
+ $count = 0;
+
+ $row = $this->store->getConnection()->selectRow(
+ [ $this->store->getStatisticsTable() ],
+ 'SUM( usage_count ) AS count',
+ [],
+ __METHOD__
+ );
+
+ $count = $row ? $row->count : $count;
+
+ return (int)$count;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return number
+ */
+ public function getTotalPropertiesCount() {
+
+ $count = 0;
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ $row = $this->store->getConnection()->selectRow(
+ SQLStore::ID_TABLE,
+ 'Count( * ) AS count',
+ $conditions,
+ __METHOD__
+ );
+
+ $count = $row ? $row->count : $count;
+
+ return (int)$count;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return number
+ */
+ public function getUsedPropertiesCount() {
+
+ $options = [];
+
+ $conditions = [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'usage_count > 0'
+ ];
+
+ $db = $this->store->getConnection( 'mw.db' );
+
+ // Select object ID's against known property ID's that match the conditions
+ $res = $db->select(
+ [ $db->tableName( SQLStore::ID_TABLE ), $db->tableName( SQLStore::PROPERTY_STATISTICS_TABLE ) ],
+ 'smw_id',
+ $conditions,
+ __METHOD__,
+ $options,
+ [
+ $db->tableName( SQLStore::ID_TABLE ) => [ 'INNER JOIN', [ 'smw_id=p_id' ] ]
+ ]
+ );
+
+ return $res->numRows();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return number
+ */
+ public function getDeleteCount() {
+ $count = 0;
+
+ $row = $this->store->getConnection()->selectRow(
+ SQLStore::ID_TABLE,
+ 'Count( * ) AS count',
+ [ 'smw_iw' => ':smw-delete' ],
+ __METHOD__
+ );
+
+ $count = $row ? $row->count : $count;
+
+ return (int)$count;
+ }
+
+ private function count( $type ) {
+
+ $res = $this->store->getConnection()->select(
+ $this->findPropertyTableByType( $type )->getName(),
+ 'COUNT(s_id) AS count',
+ [],
+ __METHOD__
+ );
+
+ $row = $this->store->getConnection()->fetchObject( $res );
+
+ return isset( $row->count ) ? (int)$row->count : 0;
+ }
+
+ private function findPropertyTableByType( $type ) {
+ $propertyTables = $this->store->getPropertyTables();
+
+ $tableIdForType = $this->store->findPropertyTableID( new DIProperty( $type ) );
+
+ if ( isset( $propertyTables[$tableIdForType] ) ) {
+ return $propertyTables[$tableIdForType];
+ }
+
+ throw new RuntimeException( "Tried to access a table that doesn't exist for {$tableIdForType}." );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyStatisticsStore.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyStatisticsStore.php
new file mode 100644
index 00000000..f54bd283
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyStatisticsStore.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use MWException;
+use Psr\Log\LoggerAwareTrait;
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException;
+
+/**
+ * Simple implementation of PropertyStatisticsTable using MediaWikis
+ * database abstraction layer and a single table.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Nischay Nahata
+ */
+class PropertyStatisticsStore {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $onTransactionIdle = false;
+
+ /**
+ * @since 1.9
+ *
+ * @param Database $connection
+ */
+ public function __construct( Database $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode or not.
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function waitOnTransactionIdle() {
+ $this->onTransactionIdle = !$this->isCommandLineMode;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getStatisticsTable() {
+ return SQLStore::PROPERTY_STATISTICS_TABLE;
+ }
+
+ /**
+ * Change the usage count for the property of the given ID by the given
+ * value. The method does nothing if the count is 0.
+ *
+ * @since 1.9
+ *
+ * @param integer $propertyId
+ * @param integer $value
+ *
+ * @return boolean Success indicator
+ */
+ public function addToUsageCount( $pid, $value ) {
+
+ $usageVal = 0;
+ $nullVal = 0;
+
+ if ( is_array( $value ) ) {
+ $usageVal = $value[0];
+ $nullVal = $value[1];
+ } else {
+ $usageVal = $value;
+ }
+
+ if ( !is_int( $usageVal ) || !is_int( $nullVal ) ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The value to add must be an integer' );
+ }
+
+ if ( !is_int( $pid ) || $pid <= 0 ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The property id to add must be a positive integer' );
+ }
+
+ if ( $usageVal == 0 && $nullVal == 0 ) {
+ return true;
+ }
+
+ try {
+ $this->connection->update(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'usage_count = usage_count ' . ( $usageVal > 0 ? '+ ' : '- ' ) . $this->connection->addQuotes( abs( $usageVal ) ),
+ 'null_count = null_count ' . ( $nullVal > 0 ? '+ ' : '- ' ) . $this->connection->addQuotes( abs( $nullVal ) ),
+ ],
+ [
+ 'p_id' => $pid
+ ],
+ __METHOD__
+ );
+ } catch ( \DBQueryError $e ) {
+ // #2345 Do nothing as it most likely an "Error: 1264 Out of range
+ // value for column" in strict mode
+ // As an unsigned int, we expected it to be 0
+ $this->setUsageCount( $pid, [ 0, 0 ] );
+ }
+
+ return true;
+ }
+
+ /**
+ * Increase the usage counts of multiple properties.
+ *
+ * The $additions parameter should be an array with integer
+ * keys that are property ids, and associated integer values
+ * that are the amount the usage count should be increased.
+ *
+ * @since 1.9
+ *
+ * @param array $additions
+ *
+ * @return boolean Success indicator
+ */
+ public function addToUsageCounts( array $additions ) {
+
+ $success = true;
+
+ if ( $additions === [] ) {
+ return $success;
+ }
+
+ $method = __METHOD__;
+
+ if ( $this->onTransactionIdle ) {
+ $this->connection->onTransactionIdle( function () use( $method, $additions ) {
+ $this->log( $method . ' (onTransactionIdle)' );
+ $this->onTransactionIdle = false;
+ $this->addToUsageCounts( $additions );
+ } );
+
+ return $success;
+ }
+
+ foreach ( $additions as $pid => $addition ) {
+
+ if ( is_array( $addition ) ) {
+ // We don't check this, have it fail in case this isn't set correctly
+ $addition = [ $addition['usage'], $addition['null'] ];
+ }
+
+ $success = $this->addToUsageCount( $pid, $addition ) && $success;
+ }
+
+ return $success;
+ }
+
+ /**
+ * Updates an existing usage count.
+ *
+ * @since 1.9
+ *
+ * @param integer $propertyId
+ * @param integer $value
+ *
+ * @return boolean Success indicator
+ * @throws PropertyStatisticsInvalidArgumentException
+ */
+ public function setUsageCount( $propertyId, $value ) {
+
+ $usageCount = 0;
+ $nullCount = 0;
+
+ if ( is_array( $value ) ) {
+ $usageCount = $value[0];
+ $nullCount = $value[1];
+ } else {
+ $usageCount = $value;
+ }
+
+ if ( !is_int( $usageCount ) || $usageCount < 0 || !is_int( $nullCount ) || $nullCount < 0 ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The value to add must be a positive integer' );
+ }
+
+ if ( !is_int( $propertyId ) || $propertyId <= 0 ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The property id to add must be a positive integer' );
+ }
+
+ return $this->connection->update(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'usage_count' => $usageCount,
+ 'null_count' => $nullCount,
+ ],
+ [
+ 'p_id' => $propertyId
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * Adds a new usage count.
+ *
+ * @since 1.9
+ *
+ * @param integer $propertyId
+ * @param integer $value
+ *
+ * @return boolean Success indicator
+ * @throws PropertyStatisticsInvalidArgumentException
+ */
+ public function insertUsageCount( $propertyId, $value ) {
+
+ $usageCount = 0;
+ $nullCount = 0;
+
+ if ( is_array( $value ) ) {
+ $usageCount = $value[0];
+ $nullCount = $value[1];
+ } else {
+ $usageCount = $value;
+ }
+
+ if ( !is_int( $usageCount ) || $usageCount < 0 || !is_int( $nullCount ) || $nullCount < 0 ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The value to add must be a positive integer' );
+ }
+
+ if ( !is_int( $propertyId ) || $propertyId <= 0 ) {
+ throw new PropertyStatisticsInvalidArgumentException( 'The property id to add must be a positive integer' );
+ }
+
+ try {
+ $this->connection->insert(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'usage_count' => $usageCount,
+ 'null_count' => $nullCount,
+ 'p_id' => $propertyId,
+ ],
+ __METHOD__
+ );
+ } catch ( \DBQueryError $e ) {
+ // Most likely hit "Error: 1062 Duplicate entry ..."
+ $this->setUsageCount( $propertyId, $value );
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the usage count for a provided property id.
+ *
+ * @since 2.2
+ *
+ * @param integer $propertyId
+ *
+ * @return integer
+ */
+ public function getUsageCount( $propertyId ) {
+
+ if ( !is_int( $propertyId ) ) {
+ return 0;
+ }
+
+ $row = $this->connection->selectRow(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'usage_count'
+ ],
+ [
+ 'p_id' => $propertyId,
+ ],
+ __METHOD__
+ );
+
+ return $row !== false ? (int)$row->usage_count : 0;
+ }
+
+ /**
+ * Returns the usage counts of the provided properties.
+ *
+ * The returned array contains integer keys which are property ids,
+ * with the associated values being their usage count (also integers).
+ *
+ * Properties for which no usage count is found will not have
+ * an entry in the result array.
+ *
+ * @since 1.9
+ *
+ * @param array $propertyIds
+ *
+ * @return array
+ */
+ public function getUsageCounts( array $propertyIds ) {
+ if ( $propertyIds === [] ) {
+ return [];
+ }
+
+ $propertyStatistics = $this->connection->select(
+ $this->connection->tablename( SQLStore::PROPERTY_STATISTICS_TABLE ),
+ [
+ 'usage_count',
+ 'p_id',
+ ],
+ [
+ 'p_id' => $propertyIds,
+ ],
+ __METHOD__
+ );
+
+ $usageCounts = [];
+
+ foreach ( $propertyStatistics as $propertyStatistic ) {
+ assert( ctype_digit( $propertyStatistic->p_id ) );
+ assert( ctype_digit( $propertyStatistic->usage_count ) );
+
+ $usageCounts[(int)$propertyStatistic->p_id] = (int)$propertyStatistic->usage_count;
+ }
+
+ return $usageCounts;
+ }
+
+ /**
+ * Deletes all rows in the table.
+ *
+ * @since 1.9
+ *
+ * @return boolean Success indicator
+ */
+ public function deleteAll() {
+ return $this->connection->delete(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ '*',
+ __METHOD__
+ );
+ }
+
+ private function log( $message, $context = [] ) {
+
+ if ( $this->logger === null ) {
+ return;
+ }
+
+ $this->logger->info( $message, $context );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinition.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinition.php
new file mode 100644
index 00000000..9cb77c84
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinition.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use OutOfBoundsException;
+
+/**
+ * Simple data container for storing information about property tables. A
+ * property table is a DB table that is used to store subject-property-value
+ * records about data in SMW. Tables mostly differ in the composition of the
+ * value, but also in whether the property is explicitly named (or fixed),
+ * and in the way subject pages are referred to.
+ *
+ *
+ * @license GNU GPL v2+
+ * @since 1.8
+ *
+ * @author Nischay Nahata
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PropertyTableDefinition {
+
+ /**
+ * Name of the table in the DB.
+ *
+ * @since 1.8
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * DIType of this table.
+ *
+ * @since 1.8
+ * @var integer
+ */
+ protected $diType;
+
+ /**
+ * If the table is only for one property, this field holds its key.
+ * Empty otherwise. Tables without a fixed property have a column "p_id"
+ * for storing the SMW page id of the property.
+ *
+ * @note It is important that this is the DB key form or special
+ * property key, not the label. This is not checked eagerly in SMW but
+ * can lead to spurious errors when properties are compared to each
+ * other or to the contents of the store.
+ *
+ * @since 1.8
+ * @var string|boolean false
+ */
+ protected $fixedProperty;
+
+ /**
+ * Boolean that states how subjects are stored. If true, a column "s_id"
+ * with an SMW page id is used. If false, two columns "s_title" and
+ * "s_namespace" are used. The latter de-normalized form cannot store
+ * sortkeys and interwiki prefixes, and is used only for the redirect
+ * table. New tables should really keep the default "true" here.
+ *
+ * @since 1.8
+ * @var boolean
+ */
+ protected $idSubject = true;
+
+ /**
+ * Factory method to create an instance for a given
+ * DI type and the given table name.
+ *
+ * @since 1.8
+ *
+ * @param integer $DIType constant
+ * @param string $tableName logocal table name (not the DB version)
+ * @param string|false $fixedProperty property key if any
+ */
+ public function __construct( $DIType, $tableName, $fixedProperty = false ) {
+ $this->name = $tableName;
+ $this->fixedProperty = $fixedProperty;
+ $this->diType = $DIType;
+ }
+
+ /**
+ * Method to return the fields for this table
+ *
+ * @since 1.8
+ *
+ * @param SQLStore $store
+ *
+ * @return array
+ */
+ public function getFields( SQLStore $store ) {
+ $diHandler = $store->getDataItemHandlerForDIType( $this->diType );
+ return $diHandler->getTableFields();
+ }
+
+ /**
+ * @see $idSubject
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function usesIdSubject() {
+ return $this->idSubject;
+ }
+
+ /**
+ * @see $idSubject
+ *
+ * @param $usesIdSubject
+ *
+ * @since 1.8
+ */
+ public function setUsesIdSubject( $usesIdSubject ) {
+ $this->idSubject = $usesIdSubject;
+ }
+
+ /**
+ * Returns the name of the fixed property which this table is for.
+ * Throws an exception when called on a table not for any fixed
+ * property, so call @see isFixedPropertyTable first when appropriate.
+ *
+ * @see $fixedProperty
+ *
+ * @since 1.8
+ *
+ * @return string
+ * @throws OutOfBoundsException
+ */
+ public function getFixedProperty() {
+
+ if ( $this->fixedProperty === false ) {
+ throw new OutOfBoundsException( 'Attempt to get the fixed property from a table that does not hold one' );
+ }
+
+ return $this->fixedProperty;
+ }
+
+ /**
+ * Returns if the table holds a fixed property or is a general table.
+ *
+ * @see $fixedProperty
+ *
+ * @since 1.8
+ *
+ * @return boolean
+ */
+ public function isFixedPropertyTable() {
+ return $this->fixedProperty !== false;
+ }
+
+ /**
+ * Returns the name of the table in the database.
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns @see $diType
+ *
+ * @since 1.8
+ *
+ * @return integer
+ */
+ public function getDiType() {
+ return $this->diType;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinitionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinitionBuilder.php
new file mode 100644
index 00000000..49812cc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableDefinitionBuilder.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Hooks;
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\PropertyRegistry;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PropertyTableDefinitionBuilder {
+
+ /**
+ * Fixed property table prefix
+ */
+ const PROPERTY_TABLE_PREFIX = 'smw_fpt';
+
+ /**
+ * @var PropertyTypeFinder
+ */
+ private $propertyTypeFinder;
+
+ /**
+ * @var TableDefinition[]
+ */
+ protected $propertyTables = [];
+
+ /**
+ * @var array
+ */
+ protected $fixedPropertyTableIds = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param PropertyTypeFinder $propertyTypeFinder
+ */
+ public function __construct( PropertyTypeFinder $propertyTypeFinder ) {
+ $this->propertyTypeFinder = $propertyTypeFinder;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param array $diType
+ * @param array $specialProperties
+ * @param array $userDefinedFixedProperties
+ */
+ public function doBuild( $diTypes, $specialProperties, $userDefinedFixedProperties ) {
+
+ $this->addTableDefinitionForDiTypes( $diTypes );
+
+ $this->addTableDefinitionForFixedProperties(
+ $specialProperties
+ );
+
+ $customFixedProperties = [];
+ $fixedPropertyTablePrefix = [];
+
+ // Allow to alter the prefix by an extension
+ Hooks::run( 'SMW::SQLStore::AddCustomFixedPropertyTables', [ &$customFixedProperties, &$fixedPropertyTablePrefix ] );
+
+ $this->addTableDefinitionForFixedProperties(
+ $customFixedProperties,
+ $fixedPropertyTablePrefix
+ );
+
+ $this->addRedirectTableDefinition();
+
+ $this->addTableDefinitionForUserDefinedFixedProperties(
+ $userDefinedFixedProperties
+ );
+
+ Hooks::run( 'SMW::SQLStore::updatePropertyTableDefinitions', [ &$this->propertyTables ] );
+
+ $this->createFixedPropertyTableIdIndex();
+ }
+
+ /**
+ * Returns table prefix
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getTablePrefix() {
+ return self::PROPERTY_TABLE_PREFIX;
+ }
+
+ /**
+ * Returns fixed properties table Ids
+ *
+ * @since 1.9
+ *
+ * @return array|null
+ */
+ public function getFixedPropertyTableIds() {
+ return $this->fixedPropertyTableIds;
+ }
+
+ /**
+ * Returns property table definitions
+ *
+ * @since 1.9
+ *
+ * @return TableDefinition[]
+ */
+ public function getTableDefinitions() {
+ return $this->propertyTables;
+ }
+
+ /**
+ * Returns new table definition
+ *
+ * @since 1.9
+ *
+ * @param $diType
+ * @param $tableName
+ * @param $fixedProperty
+ *
+ * @return TableDefinition
+ */
+ public function newTableDefinition( $diType, $tableName, $fixedProperty = false ) {
+ return new TableDefinition( $diType, $tableName, $fixedProperty );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $tableName
+ *
+ * @return string
+ */
+ public function createTableNameFrom( $tableName ) {
+ return self::PROPERTY_TABLE_PREFIX . strtolower( $tableName );
+ }
+
+ /**
+ * @see http://stackoverflow.com/questions/3763728/shorter-php-cipher-than-md5
+ * @since 2.5
+ *
+ * @param string $tableName
+ *
+ * @return string
+ */
+ public function createHashedTableNameFrom( $tableName ) {
+ return self::PROPERTY_TABLE_PREFIX . '_' . substr( base_convert( md5( $tableName ), 16, 32 ), 0, 12 );
+ }
+
+ /**
+ * Add property table definition
+ *
+ * @since 1.9
+ *
+ * @param $diType
+ * @param $tableName
+ * @param $fixedProperty
+ */
+ protected function addPropertyTable( $diType, $tableName, $fixedProperty = false ) {
+ $this->propertyTables[$tableName] = $this->newTableDefinition( $diType, $tableName, $fixedProperty );
+ }
+
+ /**
+ * @param array $diTypes
+ */
+ private function addTableDefinitionForDiTypes( array $diTypes ) {
+ foreach( $diTypes as $tableDIType => $tableName ) {
+ $this->addPropertyTable( $tableDIType, $tableName );
+ }
+ }
+
+ private function addTableDefinitionForFixedProperties( array $properties, array $fixedPropertyTablePrefix = [] ) {
+ foreach( $properties as $propertyKey => $propertyTableSuffix ) {
+
+ $tablePrefix = isset( $fixedPropertyTablePrefix[$propertyKey] ) ? $fixedPropertyTablePrefix[$propertyKey] : self::PROPERTY_TABLE_PREFIX;
+
+ // Either as plain index array containing the property key or as associated
+ // array with property key => tableSuffix
+ $propertyKey = is_int( $propertyKey ) ? $propertyTableSuffix : $propertyKey;
+
+ $this->addPropertyTable(
+ DataTypeRegistry::getInstance()->getDataItemByType( PropertyRegistry::getInstance()->getPropertyValueTypeById( $propertyKey ) ),
+ $tablePrefix . strtolower( $propertyTableSuffix ),
+ $propertyKey
+ );
+ }
+ }
+
+ private function addRedirectTableDefinition() {
+ // Redirect table uses another subject scheme for historic reasons
+ // TODO This should be changed if possible
+ $redirectTableName = $this->createTableNameFrom( '_REDI' );
+
+ if ( isset( $this->propertyTables[$redirectTableName]) ) {
+ $this->propertyTables[$redirectTableName]->setUsesIdSubject( false );
+ }
+ }
+
+ /**
+ * Get all the tables for the properties that are declared as fixed
+ * (overly used and thus having separate tables)
+ *
+ * @param array $fixedProperties
+ */
+ private function addTableDefinitionForUserDefinedFixedProperties( array $fixedProperties ) {
+
+ $this->propertyTypeFinder->setTypeTableName(
+ $this->createTableNameFrom( '_TYPE' )
+ );
+
+ foreach( $fixedProperties as $propertyKey ) {
+
+ // Normalize the key to be independent from a possible MW setting
+ // (has area == Has_area <> Has_Area)
+ $propertyKey = str_replace( ' ', '_', ucfirst( $propertyKey ) );
+ $property = new DIProperty( $propertyKey );
+
+ $this->addPropertyTable(
+ DataTypeRegistry::getInstance()->getDataItemByType( $this->propertyTypeFinder->findTypeID( $property ) ),
+ $this->createHashedTableNameFrom( $propertyKey ),
+ $propertyKey
+ );
+ }
+ }
+
+ private function createFixedPropertyTableIdIndex() {
+
+ foreach ( $this->propertyTables as $tid => $propTable ) {
+ if ( $propTable->isFixedPropertyTable() ) {
+ $this->fixedPropertyTableIds[$propTable->getFixedProperty()] = $tid;
+ }
+ }
+
+ // Specifically set properties that must not be stored in any
+ // property table to null here. Any function that hits this
+ // null unprepared is doing something wrong anyway.
+ $this->fixedPropertyTableIds['_SKEY'] = null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceDisposer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceDisposer.php
new file mode 100644
index 00000000..ab7336c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceDisposer.php
@@ -0,0 +1,284 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\EventHandler;
+use SMW\Iterators\ResultIterator;
+
+/**
+ * @private
+ *
+ * Class responsible for the clean-up (aka disposal) of any outdated table entries
+ * that are contained in either the ID_TABLE or related property tables with
+ * reference to a matchable ID.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyTableIdReferenceDisposer {
+
+ /**
+ * @var SQLStore
+ */
+ private $store = null;
+
+ /**
+ * @var Database
+ */
+ private $connection = null;
+
+ /**
+ * @var boolean
+ */
+ private $onTransactionIdle = false;
+
+ /**
+ * @var boolean
+ */
+ private $redirectRemoval = false;
+
+ /**
+ * @since 2.4
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ $this->connection = $this->store->getConnection( 'mw.db' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $redirectRemoval
+ */
+ public function setRedirectRemoval( $redirectRemoval ) {
+ $this->redirectRemoval = $redirectRemoval;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function waitOnTransactionIdle() {
+ $this->onTransactionIdle = true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ *
+ * @return boolean
+ */
+ public function isDisposable( $id ) {
+ return $this->store->getPropertyTableIdReferenceFinder()->hasResidualReferenceForId( $id ) === false;
+ }
+
+ /**
+ * Use case: After a property changed its type (_wpg -> _txt), object values in the
+ * ID table are not removed at the time of the conversion process.
+ *
+ * Before an attempt to remove the ID from entity tables, it is secured that no
+ * references exists for the ID.
+ *
+ * @note This method does not check for an ID being object or subject value
+ * and has to be done prior calling this routine.
+ *
+ * @since 2.4
+ *
+ * @param integer $id
+ */
+ public function removeOutdatedEntityReferencesById( $id ) {
+
+ if ( $this->store->getPropertyTableIdReferenceFinder()->hasResidualReferenceForId( $id ) ) {
+ return null;
+ }
+
+ $this->cleanUpSecondaryReferencesById( $id, false );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ResultIterator
+ */
+ public function newOutdatedEntitiesResultIterator() {
+
+ $res = $this->connection->select(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' ],
+ [ 'smw_iw' => SMW_SQL3_SMWDELETEIW ],
+ __METHOD__
+ );
+
+ return new ResultIterator( $res );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param stdClass $row
+ */
+ public function cleanUpTableEntriesByRow( $row ) {
+
+ if ( !isset( $row->smw_id ) ) {
+ return;
+ }
+
+ $this->cleanUpTableEntriesById( $row->smw_id );
+ }
+
+ /**
+ * @note This method does not make any assumption about the ID state and therefore
+ * has to be validated before this method is called.
+ *
+ * @since 2.4
+ *
+ * @param integer $id
+ */
+ public function cleanUpTableEntriesById( $id ) {
+
+ if ( $this->onTransactionIdle ) {
+ return $this->connection->onTransactionIdle( function() use ( $id ) {
+ $this->cleanUpReferencesById( $id );
+ } );
+ } else {
+ $this->cleanUpReferencesById( $id );
+ }
+ }
+
+ private function cleanUpReferencesById( $id ) {
+
+ $subject = $this->store->getObjectIds()->getDataItemById( $id );
+ $isRedirect = false;
+
+ if ( $subject instanceof DIWikiPage ) {
+ $isRedirect = $subject->getInterwiki() === SMW_SQL3_SMWREDIIW;
+
+ // Use the subject without an internal 'smw-delete' iw marker
+ $subject = new DIWikiPage(
+ $subject->getDBKey(),
+ $subject->getNamespace(),
+ '',
+ $subject->getSubobjectName()
+ );
+ }
+
+ $this->triggerCleanUpEvents( $subject );
+
+ $this->connection->beginAtomicTransaction( __METHOD__ );
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+ if ( $proptable->usesIdSubject() ) {
+ $this->connection->delete(
+ $proptable->getName(),
+ [ 's_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ if ( !$proptable->isFixedPropertyTable() ) {
+ $this->connection->delete(
+ $proptable->getName(),
+ [ 'p_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ $fields = $proptable->getFields( $this->store );
+
+ // Match tables (including ftp_redi) that contain an object reference
+ if ( isset( $fields['o_id'] ) ) {
+ $this->connection->delete(
+ $proptable->getName(),
+ [ 'o_id' => $id ],
+ __METHOD__
+ );
+ }
+ }
+
+ $this->cleanUpSecondaryReferencesById( $id, $isRedirect );
+ $this->connection->endAtomicTransaction( __METHOD__ );
+
+ \Hooks::run(
+ 'SMW::SQLStore::EntityReferenceCleanUpComplete',
+ [ $this->store, $id, $subject, $isRedirect ]
+ );
+ }
+
+ private function cleanUpSecondaryReferencesById( $id, $isRedirect ) {
+
+ // When marked as redirect, don't remove the reference
+ if ( $isRedirect === false || ( $isRedirect && $this->redirectRemoval ) ) {
+ $this->connection->delete(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ $this->connection->delete(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [ 'p_id' => $id ],
+ __METHOD__
+ );
+
+ $this->connection->delete(
+ SQLStore::QUERY_LINKS_TABLE,
+ [ 's_id' => $id ],
+ __METHOD__
+ );
+
+ $this->connection->delete(
+ SQLStore::QUERY_LINKS_TABLE,
+ [ 'o_id' => $id ],
+ __METHOD__
+ );
+
+ // Avoid Query: DELETE FROM `smw_ft_search` WHERE s_id = '92575'
+ // Error: 126 Incorrect key file for table '.\mw@002d25@002d01\smw_ft_search.MYI'; ...
+ try {
+ $this->connection->delete(
+ SQLStore::FT_SEARCH_TABLE,
+ [ 's_id' => $id ],
+ __METHOD__
+ );
+ } catch ( \DBError $e ) {
+ ApplicationFactory::getInstance()->getMediaWikiLogger()->info( __METHOD__ . ' reported: ' . $e->getMessage() );
+ }
+ }
+
+ private function triggerCleanUpEvents( $subject ) {
+
+ if ( !$subject instanceof DIWikiPage ) {
+ return;
+ }
+
+ // Skip any reset for subobjects where it is expected that the base
+ // subject is cleaning up all related cache entries
+ if ( $subject->getSubobjectName() !== '' ) {
+ return;
+ }
+
+ $eventHandler = EventHandler::getInstance();
+
+ $dispatchContext = $eventHandler->newDispatchContext();
+ $dispatchContext->set( 'subject', $subject );
+ $dispatchContext->set( 'context', 'PropertyTableIdReferenceDisposal' );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'cached.prefetcher.reset',
+ $dispatchContext
+ );
+
+ $eventHandler->getEventDispatcher()->dispatch(
+ 'factbox.cache.delete',
+ $dispatchContext
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceFinder.php
new file mode 100644
index 00000000..bd36d587
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableIdReferenceFinder.php
@@ -0,0 +1,267 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyTableIdReferenceFinder {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var boolean
+ */
+ private $isCapitalLinks = true;
+
+ /**
+ * @since 2.4
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ $this->connection = $this->store->getConnection( 'mw.db' );
+ $this->namespaceExaminer = ApplicationFactory::getInstance()->getNamespaceExaminer();
+ }
+
+ /**
+ * @note If $wgCapitalLinks is set false then it will avoid forcing the first
+ * letter of page titles (including included pages, images and categories)
+ * to capitals
+ *
+ * @since 2.4
+ *
+ * @param booelan $isCapitalLinks
+ */
+ public function isCapitalLinks( $isCapitalLinks ) {
+ $this->isCapitalLinks = $isCapitalLinks;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIProperty $property
+ *
+ * @return DataItem|false
+ */
+ public function tryToFindAtLeastOneReferenceForProperty( DIProperty $property ) {
+
+ $dataItem = $property->getDiWikiPage();
+
+ $sid = $this->store->getObjectIds()->getSMWPageID(
+ $dataItem->getDBkey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ ''
+ );
+
+ // Lets see if we have some lower/upper case matching for
+ // when wgCapitalLinks setting was involved
+ if ( !$this->isCapitalLinks && $sid == 0 ) {
+ $sid = $this->store->getObjectIds()->getSMWPageID(
+ lcfirst( $dataItem->getDBkey() ),
+ $dataItem->getNamespace(),
+ $dataItem->getInterwiki(),
+ ''
+ );
+ }
+
+ return $this->findAtLeastOneActiveReferenceById( $sid );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ *
+ * @return boolean
+ */
+ public function hasResidualPropertyTableReference( $id ) {
+
+ if ( $id == SQLStore::FIXED_PROPERTY_ID_UPPERBOUND ) {
+ return true;
+ }
+
+ return (bool)$this->findAtLeastOneActiveReferenceById( $id, false );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $id
+ *
+ * @return boolean
+ */
+ public function hasResidualReferenceForId( $id ) {
+
+ if ( $id == SQLStore::FIXED_PROPERTY_ID_UPPERBOUND ) {
+ return true;
+ }
+
+ return (bool)$this->findAtLeastOneActiveReferenceById( $id );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $id
+ *
+ * @return array
+ */
+ public function searchAllTablesToFindAtLeastOneReferenceById( $id ) {
+
+ $references = [];
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+ $reference = false;
+
+ if ( ( $reference = $this->findReferenceByPropertyTable( $proptable, $id ) ) !== false ) {
+ $references[$proptable->getName()] = $reference;
+ }
+ }
+
+ if ( ( $reference = $this->findQueryLinksTableReferenceById( $id ) ) !== false ) {
+ $references[SQLStore::QUERY_LINKS_TABLE] = $reference;
+ }
+
+ return $references;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $id
+ * @param boolean $secondary_ref
+ *
+ * @return DataItem|false
+ */
+ public function findAtLeastOneActiveReferenceById( $id, $secondary_ref = true ) {
+
+ $reference = false;
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+
+ if ( ( $reference = $this->findReferenceByPropertyTable( $proptable, $id ) ) !== false ) {
+
+ // If null is returned it means that a reference was found but no DI could
+ // be matched therefore is categorized as false positive
+ if ( isset( $reference->s_id ) ) {
+ $reference = $this->store->getObjectIds()->getDataItemById( $reference->s_id );
+
+ // If the reference is for some reason not part of a supported namespace,
+ // it is assumed to be invalid
+ if ( $reference !== null && !$this->namespaceExaminer->isSemanticEnabled( $reference->getNamespace() ) ) {
+ $reference = false;
+ }
+ }
+ }
+
+ if ( $reference instanceof DataItem ) {
+ return $reference;
+ }
+ }
+
+ if ( $secondary_ref && !isset( $reference->s_id ) ) {
+ $reference = $this->findQueryLinksTableReferenceById( $id );
+ }
+
+ if ( isset( $reference->s_id ) ) {
+ $reference = $this->store->getObjectIds()->getDataItemById( $reference->s_id );
+ }
+
+ if ( $reference === false || $reference === null ) {
+ return false;
+ }
+
+ return $reference;
+ }
+
+ private function findReferenceByPropertyTable( $proptable, $id ) {
+
+ $row = false;
+
+ if ( $proptable->usesIdSubject() ) {
+ $row = $this->connection->selectRow(
+ $proptable->getName(),
+ [ 's_id' ],
+ [ 's_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ if ( $row !== false ) {
+ return $row;
+ }
+
+ $fields = $proptable->getFields( $this->store );
+
+ // Check whether an object reference exists or not
+ if ( isset( $fields['o_id'] ) ) {
+
+ // This next time someone ... I'm going to Alaska
+ $field = strpos( $proptable->getName(), 'redi' ) ? [ 's_title', 's_namespace' ] : [ 's_id' ];
+
+ $row = $this->connection->selectRow(
+ $proptable->getName(),
+ $field,
+ [ 'o_id' => $id ],
+ __METHOD__
+ );
+
+ if ( $row !== false && strpos( $proptable->getName(), 'redi' ) ) {
+ $row->s_id = $this->store->getObjectIds()->findRedirect( $row->s_title, $row->s_namespace );
+ }
+ }
+
+ // If the property table is not a fixed table (== assigns a whole
+ // table to a specific property with the p_id column being suppressed)
+ // then check for the p_id field
+ if ( $row === false && !$proptable->isFixedPropertyTable() ) {
+ $row = $this->connection->selectRow(
+ $proptable->getName(),
+ [ 's_id' ],
+ [ 'p_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ return $row;
+ }
+
+ private function findQueryLinksTableReferenceById( $id ) {
+
+ // If the query table contains a reference then we keep the object (could
+ // be a subject, property, or printrequest) where in case the query is
+ // removed the object will also loose its reference
+ $row = $this->connection->selectRow(
+ SQLStore::QUERY_LINKS_TABLE,
+ [ 's_id' ],
+ [ 'o_id' => $id ],
+ __METHOD__
+ );
+
+ return $row;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableInfoFetcher.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableInfoFetcher.php
new file mode 100644
index 00000000..41ee2d12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableInfoFetcher.php
@@ -0,0 +1,276 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class PropertyTableInfoFetcher {
+
+ /**
+ * @var PropertyTypeFinder
+ */
+ private $propertyTypeFinder;
+
+ /**
+ * Array for keeping property table table data, indexed by table id.
+ * Access this only by calling getPropertyTables().
+ *
+ * @var TableDefinition[]|null
+ */
+ private $propertyTableDefinitions = null;
+
+ /**
+ * Array to cache "propkey => table id" associations for fixed property
+ * tables. Initialized by getPropertyTables(), which must be called
+ * before accessing this.
+ *
+ * @var array|null
+ */
+ private $fixedPropertyTableIds = null;
+
+ /**
+ * Keys of special properties that should have their own
+ * fixed property table.
+ *
+ * @var array
+ */
+ private static $customizableSpecialProperties = [
+ '_MDAT', '_CDAT', '_NEWP', '_LEDT', '_MIME', '_MEDIA',
+ ];
+
+ /**
+ * @var array
+ */
+ private $customSpecialPropertyList = [];
+
+ /**
+ * @var array
+ */
+ private $fixedSpecialProperties = [
+ // property declarations
+ '_TYPE', '_UNIT', '_CONV', '_PVAL', '_LIST', '_SERV', '_PREC', '_PPLB',
+ // query statistics (very frequently used)
+ '_ASK', '_ASKDE', '_ASKSI', '_ASKFO', '_ASKST', '_ASKDU', '_ASKPA',
+ // subproperties, classes, and instances
+ '_SUBP', '_SUBC', '_INST',
+ // redirects
+ '_REDI',
+ // has sub object
+ '_SOBJ',
+ // vocabulary import and URI assignments
+ '_IMPO', '_URI',
+ // Concepts
+ '_CONC',
+ // Monolingual text
+ '_LCODE', '_TEXT',
+ // Display title of
+ '_DTITLE'
+ ];
+
+ /**
+ * @var array
+ */
+ private $customFixedPropertyList = [];
+
+ /**
+ * Default tables to use for storing data of certain types.
+ *
+ * @var array
+ */
+ private $defaultDiTypeTableIdMap = [
+ DataItem::TYPE_NUMBER => 'smw_di_number',
+ DataItem::TYPE_BLOB => 'smw_di_blob',
+ DataItem::TYPE_BOOLEAN => 'smw_di_bool',
+ DataItem::TYPE_URI => 'smw_di_uri',
+ DataItem::TYPE_TIME => 'smw_di_time',
+ DataItem::TYPE_GEO => 'smw_di_coords', // currently created only if Semantic Maps are installed
+ DataItem::TYPE_WIKIPAGE => 'smw_di_wikipage',
+ //DataItem::TYPE_CONCEPT => '', // _CONC is the only property of this type
+ ];
+
+ /**
+ * @since 2.5
+ *
+ * @param PropertyTypeFinder $propertyTypeFinder
+ */
+ public function __construct( PropertyTypeFinder $propertyTypeFinder ) {
+ $this->propertyTypeFinder = $propertyTypeFinder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getFixedSpecialPropertyList() {
+ return self::$customizableSpecialProperties;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $customFixedProperties
+ */
+ public function setCustomFixedPropertyList( array $customFixedProperties ) {
+ $this->customFixedPropertyList = $customFixedProperties;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $customSpecialProperties
+ */
+ public function setCustomSpecialPropertyList( array $customSpecialProperties ) {
+ $this->customSpecialPropertyList = $customSpecialProperties;
+ }
+
+ /**
+ * Find the id of a property table that is suitable for storing values of
+ * the given type. The type is specified by an SMW type id such as '_wpg'.
+ * An empty string is returned if no matching table could be found.
+ *
+ * @since 2.2
+ *
+ * @param string $dataTypeTypeId
+ *
+ * @return string
+ */
+ public function findTableIdForDataTypeTypeId( $dataTypeTypeId ) {
+ return $this->findTableIdForDataItemTypeId(
+ DataTypeRegistry::getInstance()->getDataItemId( $dataTypeTypeId )
+ );
+ }
+
+ /**
+ * Find the id of a property table that is normally used to store
+ * data items of the given type. The empty string is returned if
+ * no such table exists.
+ *
+ * @since 2.2
+ *
+ * @param integer $dataItemId
+ *
+ * @return string
+ */
+ public function findTableIdForDataItemTypeId( $dataItemId ) {
+
+ if ( array_key_exists( $dataItemId, $this->defaultDiTypeTableIdMap ) ) {
+ return $this->defaultDiTypeTableIdMap[$dataItemId];
+ }
+
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getDefaultDataItemTables() {
+ return array_values( $this->defaultDiTypeTableIdMap );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function isFixedTableProperty( DIProperty $property ) {
+
+ if ( $this->fixedPropertyTableIds === null ) {
+ $this->buildDefinitionsForPropertyTables();
+ }
+
+ return array_key_exists( $property->getKey(), $this->fixedPropertyTableIds );
+ }
+
+ /**
+ * Retrieve the id of the property table that is to be used for storing
+ * values for the given property object.
+ *
+ * @since 2.2
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ */
+ public function findTableIdForProperty( DIProperty $property ) {
+
+ if ( $this->fixedPropertyTableIds === null ) {
+ $this->buildDefinitionsForPropertyTables();
+ }
+
+ $propertyKey = $property->getKey();
+
+ if ( array_key_exists( $propertyKey, $this->fixedPropertyTableIds ) ) {
+ return $this->fixedPropertyTableIds[$propertyKey];
+ }
+
+ return $this->findTableIdForDataTypeTypeId( $property->findPropertyTypeID() );
+ }
+
+ /**
+ * Return the array of predefined property table declarations, initialising
+ * it if necessary. The result is an array of SMWSQLStore3Table objects
+ * indexed by table ids.
+ *
+ * It is ensured that the keys of the returned array agree with the name of
+ * the table that they refer to.
+ *
+ * @since 2.2
+ *
+ * @return TableDefinition[]
+ */
+ public function getPropertyTableDefinitions() {
+
+ if ( $this->propertyTableDefinitions === null ) {
+ $this->buildDefinitionsForPropertyTables();
+ }
+
+ return $this->propertyTableDefinitions;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function clearCache() {
+ $this->propertyTableDefinitions = null;
+ $this->fixedPropertyTableIds = null;
+ }
+
+ private function buildDefinitionsForPropertyTables() {
+
+ $enabledSpecialProperties = $this->fixedSpecialProperties;
+ $customizableSpecialProperties = array_flip( self::$customizableSpecialProperties );
+
+ foreach ( $this->customSpecialPropertyList as $property ) {
+ if ( isset( $customizableSpecialProperties[$property] ) ) {
+ $enabledSpecialProperties[] = $property;
+ }
+ }
+
+ $definitionBuilder = new PropertyTableDefinitionBuilder(
+ $this->propertyTypeFinder
+ );
+
+ $definitionBuilder->doBuild(
+ $this->defaultDiTypeTableIdMap,
+ $enabledSpecialProperties,
+ $this->customFixedPropertyList
+ );
+
+ $this->propertyTableDefinitions = $definitionBuilder->getTableDefinitions();
+ $this->fixedPropertyTableIds = $definitionBuilder->getFixedPropertyTableIds();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowDiffer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowDiffer.php
new file mode 100644
index 00000000..599f5fd7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowDiffer.php
@@ -0,0 +1,313 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use InvalidArgumentException;
+use SMW\DIProperty;
+use SMW\Exception\DataItemException;
+use SMW\SemanticData;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\Store;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author Markus Krötzsch
+ * @author Nischay Nahata
+ * @author mwjames
+ */
+class PropertyTableRowDiffer {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyTableRowMapper
+ */
+ private $propertyTableRowMapper;
+
+ /**
+ * @var ChangeOp
+ */
+ private $changeOp;
+
+ /**
+ * @since 2.3
+ *
+ * @param Store $store
+ * @param PropertyTableRowMapper $propertyTableRowMapper
+ */
+ public function __construct( Store $store, PropertyTableRowMapper $propertyTableRowMapper ) {
+ $this->store = $store;
+ $this->propertyTableRowMapper = $propertyTableRowMapper;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ChangeOp|null $changeOp
+ */
+ public function setChangeOp( ChangeOp $changeOp = null ) {
+ $this->changeOp = $changeOp;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ChangeOp
+ */
+ public function getChangeOp() {
+ return $this->changeOp;
+ }
+
+ /**
+ * Compute necessary insertions, deletions, and new table hashes for
+ * updating the database to contain $newData for the subject with ID
+ * $sid. Insertions and deletions are returned in as an array mapping
+ * table names to arrays of table rows. Each row is an array mapping
+ * column names to values as usual. The table hashes are returned as
+ * an array mapping table names to hash values.
+ *
+ * It is ensured that table names (keys) in the returned insert
+ * data are exaclty the same as the table names (keys) in the delete
+ * data, even if one of them maps to an empty array (no changes). If
+ * a table needs neither insertions nor deletions, then it will not
+ * be mentioned as a key anywhere.
+ *
+ * The given database is only needed for reading the data that is
+ * related assigned to sid.
+ *
+ * @since 2.3
+ *
+ * @param integer $sid
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ public function computeTableRowDiff( $sid, SemanticData $semanticData ) {
+
+ $tablesDeleteRows = [];
+ $tablesInsertRows = [];
+
+ $propertyList = [];
+ $textItems = [];
+
+ $newHashes = [];
+
+ if ( $this->changeOp === null ) {
+ $this->setChangeOp( new ChangeOp( $semanticData->getSubject() ) );
+ }
+
+ list( $newData, $textItems, $propertyList, $fixedPropertyList ) = $this->propertyTableRowMapper->mapToRows(
+ $sid,
+ $semanticData
+ );
+
+ $this->changeOp->addPropertyList( $propertyList );
+
+ $oldHashes = $this->fetchPropertyTableHashesById(
+ $sid
+ );
+
+ $propertyTables = $this->store->getPropertyTables();
+
+ foreach ( $propertyTables as $propertyTable ) {
+
+ if ( !$propertyTable->usesIdSubject() ) { // ignore; only affects redirects anyway
+ continue;
+ }
+
+ $tableName = $propertyTable->getName();
+ $fixedProperty = false;
+
+ // Fixed property tables have no p_id declared, the auxiliary
+ // information is provided to easily map fixed tables and
+ // its assigned property/id
+ if ( $propertyTable->isFixedPropertyTable() ) {
+ $fixedProperty['key'] = $propertyTable->getFixedProperty();
+
+ // Isn't registered therefore leave it alone (property was removed etc.)
+ try {
+ $property = new DIProperty( $fixedProperty['key'] );
+ $fixedProperty['p_id'] = $this->store->getObjectIds()->getSMWPropertyID(
+ $property
+ );
+ } catch ( DataItemException $e ) {
+ $fixedProperty = false;
+ }
+ }
+
+ if ( $fixedProperty ) {
+ $this->changeOp->addFixedPropertyRecord( $tableName, $fixedProperty );
+ }
+
+ if ( array_key_exists( $tableName, $newData ) ) {
+ // Note: the order within arrays should remain the same while page is not updated.
+ // Hence we do not sort before serializing. It is hoped that this assumption is valid.
+ $newHashes[$tableName] = $this->createHash(
+ $tableName,
+ $newData,
+ $semanticData->getOption( SemanticData::OPT_LAST_MODIFIED )
+ );
+
+ if ( array_key_exists( $tableName, $oldHashes ) && $newHashes[$tableName] == $oldHashes[$tableName] ) {
+ // Table contains data and should contain the same data after update
+ continue;
+ } else { // Table contains no data or contains data that is different from the new
+ list( $tablesInsertRows[$tableName], $tablesDeleteRows[$tableName] ) = $this->arrayDeleteMatchingValues(
+ $this->fetchCurrentContentsForPropertyTable( $sid, $propertyTable ),
+ $newData[$tableName],
+ $propertyTable
+ );
+ }
+ } elseif ( array_key_exists( $tableName, $oldHashes ) ) {
+ // Table contains data but should not contain any after update
+ $tablesInsertRows[$tableName] = [];
+ $tablesDeleteRows[$tableName] = $this->fetchCurrentContentsForPropertyTable(
+ $sid,
+ $propertyTable
+ );
+ }
+ }
+
+ $this->changeOp->addTextItems(
+ $sid,
+ $textItems
+ );
+
+ $this->changeOp->addDataOp(
+ $semanticData->getSubject()->getHash(),
+ $newData
+ );
+
+ $this->changeOp->addDiffOp(
+ $tablesInsertRows,
+ $tablesDeleteRows
+ );
+
+ return [ $tablesInsertRows, $tablesDeleteRows, $newHashes ];
+ }
+
+ private function fetchPropertyTableHashesById( $sid ) {
+ return $this->store->getObjectIds()->getPropertyTableHashes( $sid );
+ }
+
+ /**
+ * @note The hashMutator can be used to force a modification in order to detect
+ * content edits where text has been changed but the md5 table hash remains
+ * unchanged and therefore would not re-compute the diff and misses out
+ * critical updates on property tables.
+ *
+ * The phenomenon has been observed in connection with a page turned from
+ * a redirect to a normal page or for undeleted pages.
+ */
+ private function createHash( $tableName, $newData, $hashMutator = '' ) {
+ return md5( serialize( array_values( $newData[$tableName] ) ) . $hashMutator );
+ }
+
+ /**
+ * Get the current data stored for the given ID in the given database
+ * table. The result is an array of updates, formatted like the one of
+ * the table insertion arrays created by preparePropertyTableInserts().
+ *
+ * @note Tables without IDs as subject are not supported. They will
+ * hopefully vanish soon anyway.
+ *
+ * @since 1.8
+ * @param integer $sid
+ * @param TableDefinition $tableDeclaration
+ * @return array
+ */
+ private function fetchCurrentContentsForPropertyTable( $sid, TableDefinition $propertyTable ) {
+
+ if ( !$propertyTable->usesIdSubject() ) { // does not occur, but let's be strict
+ throw new InvalidArgumentException('Operation not supported for tables without subject IDs.');
+ }
+
+ $contents = [];
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $result = $connection->select(
+ $connection->tablename( $propertyTable->getName() ),
+ '*',
+ [ 's_id' => $sid ],
+ __METHOD__
+ );
+
+ foreach( $result as $row ) {
+ if ( is_object( $row ) ) {
+
+ $resultRow = (array)$row;
+
+ // Always make sure to use int values for ids so
+ // that the compare/hash will be of the same type
+ if ( isset( $resultRow['s_id'] ) ) {
+ $resultRow['s_id'] = (int)$resultRow['s_id'];
+ }
+
+ if ( isset( $resultRow['p_id'] ) ) {
+ $resultRow['p_id'] = (int)$resultRow['p_id'];
+ }
+
+ if ( isset( $resultRow['o_id'] ) ) {
+ $resultRow['o_id'] = (int)$resultRow['o_id'];
+ }
+
+ $hash = $this->propertyTableRowMapper->makeHash( $resultRow );
+ $contents[$hash] = $resultRow;
+ }
+ }
+
+ return $contents;
+ }
+
+ /**
+ * Delete all matching values from old and new arrays and return the
+ * remaining new values as insert values and the remaining old values as
+ * delete values.
+ *
+ * @param array $oldValues
+ * @param array $newValues
+ * @param PropertyTableDefinition $propertyTable
+ *
+ * @return array
+ */
+ private function arrayDeleteMatchingValues( $oldValues, $newValues, $propertyTable ) {
+
+ $isString = $propertyTable->getDIType() === DataItem::TYPE_BLOB;
+
+ // Cycle through old values
+ foreach ( $oldValues as $oldKey => $oldValue ) {
+
+ // Cycle through new values
+ foreach ( $newValues as $newKey => $newValue ) {
+
+ // #2061
+ // Loose comparison on a string will fail for cases like 011 == 0011
+ // therefore use the strict comparison and have the values
+ // remain if they don't match
+ if ( $isString && $newValue !== $oldValue ) {
+ continue;
+ }
+
+ // Delete matching values
+ // use of == is intentional to account for oldValues only
+ // containing strings while new values might also contain other
+ // types
+ if ( $newValue == $oldValue ) {
+ unset( $newValues[$newKey] );
+ unset( $oldValues[$oldKey] );
+ }
+ }
+ };
+
+ // Arrays have to be renumbered because database functions expect an
+ // element with index 0 to be present in the array
+ return [ array_values( $newValues ), array_values( $oldValues ) ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php
new file mode 100644
index 00000000..b12554ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableRowMapper.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use RuntimeException;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\SemanticData;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\Store;
+use SMWDataItem as DataItem;
+use SMWDIError as DIError;
+
+/**
+ * Builds a table row representation for a SemanticData object.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableRowMapper {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.3
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param SemanticData $semanticData
+ *
+ * @return ChangeOp
+ */
+ public function newChangeOp( $id, SemanticData $semanticData ) {
+
+ list( $dataArray, $textItems, $propertyList, $fixedPropertyList ) = $this->mapToRows(
+ $id,
+ $semanticData
+ );
+
+ $subject = $semanticData->getSubject();
+ $changeOp = new ChangeOp( $subject );
+
+ foreach ( $fixedPropertyList as $key => $record ) {
+ $changeOp->addFixedPropertyRecord( $key, $record );
+ }
+
+ $changeOp->addPropertyList( $propertyList );
+
+ $changeOp->addDataOp(
+ $subject->getHash(),
+ $dataArray
+ );
+
+ return $changeOp;
+ }
+
+ /**
+ * Create an array of rows to insert into property tables in order to
+ * store the given SemanticData. The given $sid (subject page id) is
+ * used directly and must belong to the subject of the data container.
+ * Sortkeys are ignored since they are not stored in a property table
+ * but in the ID table.
+ *
+ * The returned array uses property table names as keys and arrays of
+ * table rows as values. Each table row is an array mapping column
+ * names to values.
+ *
+ * @note Property tables that do not use ids as subjects are ignored.
+ * This just excludes redirects that are handled differently anyway;
+ * it would not make a difference to include them here.
+ *
+ * @since 3.0
+ *
+ * @param integer $sid
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ public function mapToRows( $sid, SemanticData $semanticData ) {
+
+ list( $rows, $textItems, $propertyList, $fixedPropertyList ) = $this->mapData(
+ $sid,
+ $semanticData
+ );
+
+ return [ $rows, $textItems, $propertyList, $fixedPropertyList ];
+ }
+
+ /**
+ * Create a string key for hashing an array of values that represents a
+ * row in the database. Used to eliminate duplicates and to support
+ * diff computation. This is not stored in the database, so it can be
+ * changed without causing any problems with legacy data.
+ *
+ * @since 3.0
+ *
+ * @param array $fieldArray
+ *
+ * @return string
+ */
+ public function makeHash( array $array ) {
+ return md5( implode( '#', $array ) );;
+ }
+
+ /**
+ * Create an array of rows to insert into property tables in order to
+ * store the given SMWSemanticData. The given $sid (subject page id) is
+ * used directly and must belong to the subject of the data container.
+ * Sortkeys are ignored since they are not stored in a property table
+ * but in the ID table.
+ *
+ * The returned array uses property table names as keys and arrays of
+ * table rows as values. Each table row is an array mapping column
+ * names to values.
+ *
+ * @note Property tables that do not use ids as subjects are ignored.
+ * This just excludes redirects that are handled differently anyway;
+ * it would not make a difference to include them here.
+ *
+ * @since 1.8
+ *
+ * @param integer $sid
+ * @param SemanticData $semanticData
+ *
+ * @return array
+ */
+ private function mapData( $sid, SemanticData $semanticData ) {
+
+ $subject = $semanticData->getSubject();
+ $propertyTables = $this->store->getPropertyTables();
+
+ $rows = [];
+
+ // Keep the list for the Diff to avoid having to lookup any property ID
+ // reference during a post processing
+ $propertyList = [];
+ $fixedPropertyList = [];
+ $textItems = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ $tableId = $this->store->findPropertyTableID( $property );
+
+ // not stored in a property table, e.g., sortkeys
+ if ( $tableId === null ) {
+ continue;
+ }
+
+ // "Notice: Undefined index"
+ if ( !isset( $propertyTables[$tableId] ) ) {
+ throw new RuntimeException( "Unable to find a property table for " . $property->getKey() );
+ }
+
+ $propertyTable = $propertyTables[$tableId];
+
+ // not using subject ids, e.g., redirects
+ if ( !$propertyTable->usesIdSubject() ) {
+ continue;
+ }
+
+ $insertValues = [ 's_id' => $sid ];
+ $p_type = $property->findPropertyValueType();
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $insertValues['p_id'] = $this->store->getObjectIds()->makeSMWPropertyID(
+ $property
+ );
+
+ $propertyList[$property->getKey()] = [ '_id' => $insertValues['p_id'], '_type' => $p_type ];
+ } else {
+ $pid = $this->store->getObjectIds()->makeSMWPropertyID(
+ $property
+ );
+
+ $fixedPropertyList[$tableId] = [
+ 'key' => $property->getKey(),
+ 'p_id' => $pid,
+ ];
+
+ $propertyList[$property->getKey()] = [ '_id' => $pid, '_type' => $p_type ];
+ }
+
+ $pid = $propertyList[$property->getKey()]['_id'];
+
+ if ( !isset( $textItems[$pid] ) ) {
+ $textItems[$pid] = [];
+ }
+
+ // Avoid issues when an expected predefined property is no longer
+ // available (i.e. an extension that defined that property was disabled)
+ try {
+ $propertyValues = $semanticData->getPropertyValues( $property );
+ } catch( PredefinedPropertyLabelMismatchException $e ) {
+ continue;
+ }
+
+ foreach ( $propertyValues as $dataItem ) {
+
+ if ( $dataItem instanceof DIError ) { // ignore error values
+ continue;
+ }
+
+ $tableName = $propertyTable->getName();
+
+ if ( !array_key_exists( $tableName, $rows ) ) {
+ $rows[$tableName] = [];
+ }
+
+ if ( $dataItem->getDIType() === DataItem::TYPE_BLOB ) {
+ $textItems[$pid][] = $dataItem->getString();
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_URI ) {
+ $textItems[$pid][] = $dataItem->getSortKey();
+ } elseif ( $dataItem->getDIType() === DataItem::TYPE_WIKIPAGE ) {
+ $textItems[$pid][] = $dataItem->getSortKey();
+ }
+
+ $dataItemValues = $this->store->getDataItemHandlerForDIType( $dataItem->getDIType() )->getInsertValues( $dataItem );
+
+ // Ensure that the sortkey is a string
+ if ( isset( $dataItemValues['o_sortkey'] ) ) {
+ $dataItemValues['o_sortkey'] = (string)$dataItemValues['o_sortkey'];
+ }
+
+ $insertValues = array_merge( $insertValues, $dataItemValues );
+
+ // Make sure to build a unique set without duplicates which could happen
+ // if an annotation is made to a property that has a redirect pointing
+ // to the same p_id
+ $hash = $this->makeHash(
+ $insertValues
+ );
+
+ $rows[$tableName][$hash] = $insertValues;
+ }
+
+ // Unused
+ if ( $textItems[$pid] === [] ) {
+ unset( $textItems[$pid] );
+ }
+ }
+
+ // Special handling of Concepts
+ if ( $subject->getNamespace() === SMW_NS_CONCEPT && $subject->getSubobjectName() == '' ) {
+ $this->mapConceptTable( $sid, $rows );
+ }
+
+ return [ $rows, $textItems, $propertyList, $fixedPropertyList ];
+ }
+
+ /**
+ * Add cache information to concept data and make sure that there is
+ * exactly one value for the concept table.
+ *
+ * @note This code will vanish when concepts have a more standard
+ * handling. So not point in optimizing this much now.
+ *
+ * @since 1.8
+ * @param integer $sid
+ * @param &array $insertData
+ */
+ private function mapConceptTable( $sid, &$insertData ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // Make sure that there is exactly one row to be written:
+ if ( array_key_exists( 'smw_fpt_conc', $insertData ) && !empty( $insertData['smw_fpt_conc'] ) ) {
+ $insertValues = end( $insertData['smw_fpt_conc'] );
+ } else {
+ $insertValues = [
+ 's_id' => $sid,
+ 'concept_txt' => '',
+ 'concept_docu' => '',
+ 'concept_features' => 0,
+ 'concept_size' => -1,
+ 'concept_depth' => -1
+ ];
+ }
+
+ // Add existing cache status data to this row:
+ $row = $connection->selectRow(
+ 'smw_fpt_conc',
+ [ 'cache_date', 'cache_count' ],
+ [ 's_id' => $sid ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ $insertValues['cache_date'] = null;
+ $insertValues['cache_count'] = null;
+ } else {
+ $insertValues['cache_date'] = $row->cache_date;
+ $insertValues['cache_count'] = $row->cache_count;
+ }
+
+ $insertData['smw_fpt_conc'] = [ $insertValues ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php
new file mode 100644
index 00000000..3b9d33ed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\Store;
+use SMW\ChangePropListener;
+use SMW\Parameters;
+use SMW\DIProperty;
+use SMW\SQLStore\Exception\TableMissingIdFieldException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableUpdater {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @var array
+ */
+ private $stats = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ }
+
+ /**
+ * Update all property tables and any dependent data (hashes,
+ * statistics, etc.) by inserting/deleting the given values. The ID of
+ * the page that is updated, and the hashes of the properties must be
+ * given explicitly (the hashes could not be computed from the insert
+ * and delete data alone anyway).
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param Parameters $parameters
+ */
+ public function update( $id, Parameters $parameters ) {
+
+ $this->stats = [];
+
+ $insert_rows = $parameters->get( 'insert_rows' );
+ $delete_rows = $parameters->get( 'delete_rows' );
+
+ $this->doUpdate( $insert_rows, $delete_rows );
+ $new_hashes = $parameters->get( 'new_hashes' );
+
+ // If only rows are marked for deletion then modify hashs to ensure that
+ // any inbalance can be corrected by the next insert operation for which
+ // the new_hashes are computed (seen in connection with redirects)
+ if ( $insert_rows === [] && $delete_rows !== [] ) {
+ foreach ( $new_hashes as $key => $hash ) {
+ $new_hashes[$key] = $hash . '.d';
+ }
+ }
+
+ if ( $insert_rows !== [] || $delete_rows !== [] ) {
+ $this->store->getObjectIds()->setPropertyTableHashes( $id, $new_hashes );
+ }
+
+ $this->propertyStatisticsStore->addToUsageCounts(
+ $this->stats
+ );
+ }
+
+ /**
+ * It is assumed and required that the tables mentioned in
+ * $tablesInsertRows and $tablesDeleteRows are the same, and that all
+ * $rows in these datasets refer to the same subject ID.
+ *
+ * @param array $insert_rows
+ * @param array $delete_rows
+ */
+ private function doUpdate( array $insert_rows, array $delete_rows ) {
+
+ $propertyTables = $this->store->getPropertyTables();
+
+ // Note: by construction, the inserts and deletes have the same table keys.
+ // Note: by construction, the inserts and deletes are currently disjoint;
+ // yet we delete first to make the method more robust/versatile.
+ foreach ( $insert_rows as $tableName => $insertRows ) {
+
+ $propertyTable = $propertyTables[$tableName];
+
+ // Should not occur, but let's be strict
+ if ( !$propertyTable->usesIdSubject() ) {
+ throw new TableMissingIdFieldException( $propertyTable->getName() );
+ }
+
+ // Delete
+ $this->update_rows( $propertyTable, $delete_rows[$tableName], false );
+
+ // Insert
+ $this->update_rows( $propertyTable, $insertRows, true );
+ }
+ }
+
+ /**
+ * Update one property table by inserting or deleting rows, and compute
+ * the changes that this entails for the property usage counts. The
+ * given rows are inserted into the table if $insert is true; otherwise
+ * they are deleted. The property usage counts are recorded in the
+ * call-by-ref parameter $propertyUseIncrements.
+ *
+ * The method assumes that all of the given rows are about the same
+ * subject. This is ensured by callers.
+ *
+ * @param PropertyTableDefinition $propertyTable
+ * @param array $rows array of rows to insert/delete
+ * @param boolean $insert
+ */
+ private function update_rows( PropertyTableDefinition $propertyTable, array $rows, $insert ) {
+
+ if ( empty( $rows ) ) {
+ return;
+ }
+
+ if ( $insert ) {
+ $this->insert( $propertyTable, $rows );
+ } else {
+ $this->delete( $propertyTable, $rows );
+ }
+
+ if ( $propertyTable->isFixedPropertyTable() ) {
+
+ $property = new DIProperty(
+ $propertyTable->getFixedProperty()
+ );
+
+ $pid = $this->store->getObjectIds()->makeSMWPropertyID( $property );
+ }
+
+ foreach ( $rows as $row ) {
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $pid = $row['p_id'];
+ }
+
+ ChangePropListener::record(
+ $pid,
+ [
+ 'row' => $row,
+ 'is_insert' => $insert
+ ]
+ );
+
+ if ( !array_key_exists( $pid, $this->stats ) ) {
+ $this->stats[$pid] = 0;
+ }
+
+ $this->stats[$pid] += ( $insert ? 1 : -1 );
+ }
+ }
+
+ private function insert( PropertyTableDefinition $propertyTable, array $rows ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $tableName = $propertyTable->getName();
+
+ $connection->insert(
+ $tableName,
+ $rows,
+ __METHOD__ . "-$tableName"
+ );
+ }
+
+ private function delete( PropertyTableDefinition $propertyTable, array $rows ) {
+
+ $condition = '';
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // We build a condition that mentions s_id only once,
+ // since it must be the same for all rows. This should
+ // help the DBMS in selecting the rows (it would not be
+ // easy for to detect that all tuples share one s_id).
+ $sid = false;
+ $tableName = $propertyTable->getName();
+
+ foreach ( $rows as $row ) {
+ if ( $sid === false ) {
+ if ( !array_key_exists( 's_id', (array)$row ) ) {
+ // FIXME: The assumption that s_id is present does not hold.
+ // This return is there to prevent fatal errors, but does
+ // not fix the issue of this code being broken
+ return;
+ }
+
+ // 's_id' exists for all tables with $propertyTable->usesIdSubject()
+ $sid = $row['s_id'];
+ }
+
+ unset( $row['s_id'] );
+
+ if ( $condition != '' ) {
+ $condition .= ' OR ';
+ }
+
+ $condition .= '(' . $connection->makeList( $row, LIST_AND ) . ')';
+ }
+
+ $condition = "s_id=" . $connection->addQuotes( $sid ) . " AND ($condition)";
+
+ $connection->delete(
+ $tableName,
+ [ $condition ],
+ __METHOD__ . "-$tableName"
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTypeFinder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTypeFinder.php
new file mode 100644
index 00000000..f65c8e7b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTypeFinder.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\MediaWiki\Database;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyTypeFinder {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var string
+ */
+ private $typeTableName = '';
+
+ /**
+ * @since 2.5
+ *
+ * @param Database $connection
+ */
+ public function __construct( Database $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $typeTableName
+ */
+ public function setTypeTableName( $typeTableName ) {
+ $this->typeTableName = $typeTableName;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function findTypeID( DIProperty $property ) {
+
+ try {
+ $row = $this->connection->selectRow(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id'
+ ],
+ [
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_title' => $property->getKey(),
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ],
+ __METHOD__
+ );
+ } catch ( \Exception $e ) {
+ $row = false;
+ }
+
+ if ( !isset( $row->smw_id ) ) {
+ return $GLOBALS['smwgPDefaultType'];
+ }
+
+ if ( $this->typeTableName === '' ) {
+ throw new RuntimeException( "Missing a table name" );
+ }
+
+ // The Finder is executed before tables are initialized with a corresponding
+ // and matchable DIHandler therefore using Store::getPropertyValue cannot
+ // be used at this point as it would create a circular reference during
+ // the table initialization.
+ //
+ // We expect it to be a URI table with `o_serialized` containing the
+ // type string
+ $row = $this->connection->selectRow(
+ $this->typeTableName,
+ [
+ 'o_serialized'
+ ],
+ [
+ 's_id' => $row->smw_id
+ ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ return $GLOBALS['smwgPDefaultType'];
+ }
+
+ // e.g. http://semantic-mediawiki.org/swivt/1.0#_num
+ list( $url, $fragment ) = explode( "#", $row->o_serialized );
+
+ return $fragment;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksTableUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksTableUpdater.php
new file mode 100644
index 00000000..7a623771
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksTableUpdater.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\DIWikiPage;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DependencyLinksTableUpdater {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var array
+ */
+ private static $updateList = [];
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function clear() {
+ self::$updateList = [];
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $sid
+ * @param array|null $dependencyList
+ */
+ public function addToUpdateList( $sid, array $dependencyList = null ) {
+
+ if ( $sid == 0 || $dependencyList === null || $dependencyList === [] ) {
+ return null;
+ }
+
+ if ( !isset( self::$updateList[$sid] ) ) {
+ return self::$updateList[$sid] = $dependencyList;
+ }
+
+ self::$updateList[$sid] = array_merge( self::$updateList[$sid], $dependencyList );
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function doUpdate() {
+ foreach ( self::$updateList as $sid => $dependencyList ) {
+
+ if ( $dependencyList === [] ) {
+ continue;
+ }
+
+ $this->updateDependencyList( $sid, $dependencyList );
+ self::$updateList[$sid] = [];
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $dependencyList
+ */
+ public function deleteDependenciesFromList( array $deleteIdList ) {
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'list' => implode( ' ,', $deleteIdList )
+ ];
+
+ $this->logger->info( '[QueryDependency] Delete dependencies: {list}', $context );
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ $connection->delete(
+ SQLStore::QUERY_LINKS_TABLE,
+ [
+ 's_id' => $deleteIdList
+ ],
+ __METHOD__
+ );
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param integer $sid
+ * @param array $dependencyList
+ */
+ private function updateDependencyList( $sid, array $dependencyList ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ // Before an insert, delete all entries that for the criteria which is
+ // cheaper then doing an individual upsert or selectRow, this also ensures
+ // that entries are self-corrected for dependencies matched
+ $connection->delete(
+ SQLStore::QUERY_LINKS_TABLE,
+ [
+ 's_id' => $sid
+ ],
+ __METHOD__
+ );
+
+ if ( $sid == 0 ) {
+ return $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ $inserts = [];
+
+ foreach ( $dependencyList as $dependency ) {
+
+ if ( !$dependency instanceof DIWikiPage ) {
+ continue;
+ }
+
+ $oid = $this->getId( $dependency );
+
+ // If the ID_TABLE didn't contained an valid ID then we create one ourselves
+ // to ensure that object entities are tracked from the start
+ // This can happen when a query is added with object reference that have not
+ // yet been referenced as annotation and therefore do not recognized as
+ // value annotation
+ if ( $oid < 1 && ( ( $oid = $this->createId( $dependency ) ) < 1 ) ) {
+ continue;
+ }
+
+ $inserts[$sid . $oid] = [
+ 's_id' => $sid,
+ 'o_id' => $oid
+ ];
+ }
+
+ if ( $inserts === [] ) {
+ return $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ // MW's multi-array insert needs a numeric dimensional array but the key
+ // was used with a hash to avoid duplicate entries hence the re-copy
+ $inserts = array_values( $inserts );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'id' => $sid
+ ];
+
+ $this->logger->info( '[QueryDependency] Table insert: {id} ID', $context );
+
+ $connection->insert(
+ SQLStore::QUERY_LINKS_TABLE,
+ $inserts,
+ __METHOD__
+ );
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject, $subobjectName
+ * @param string $subobjectName
+ */
+ public function getId( DIWikiPage $subject, $subobjectName = '' ) {
+
+ if ( $subobjectName !== '' ) {
+ $subject = new DIWikiPage(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subobjectName
+ );
+ }
+
+ $id = $this->store->getObjectIds()->getId(
+ $subject
+ );
+
+ return $id;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param DIWikiPage $subject, $subobjectName
+ * @param string $subobjectName
+ */
+ public function createId( DIWikiPage $subject, $subobjectName = '' ) {
+
+ $id = $this->store->getObjectIds()->makeSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subobjectName,
+ false
+ );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'id' => $id,
+ 'origin' => $subject->getHash() . $subobjectName
+
+ ];
+
+ $this->logger->info( '[QueryDependency] Table update: new {id} ID; {origin}', $context );
+
+ return $id;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksUpdateJournal.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksUpdateJournal.php
new file mode 100644
index 00000000..ff9d8526
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/DependencyLinksUpdateJournal.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use Onoi\Cache\Cache;
+use Psr\Log\LoggerAwareTrait;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Deferred\CallableUpdate;
+use Title;
+
+/**
+ * Temporary storage of entities that are expected to be refreshed (or updated)
+ * during an article view due to being a dependency of an altered query.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DependencyLinksUpdateJournal {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var string
+ */
+ const VERSION = '0.1';
+
+ /**
+ * Namespace for the cache instance
+ */
+ const CACHE_NAMESPACE = 'smw:update:qdep';
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var CallableUpdate
+ */
+ private $callableUpdate;
+
+ /**
+ * @since 3.0
+ *
+ * @param Cache $cache
+ * @param callableUpdate $callableUpdate
+ */
+ public function __construct( Cache $cache, CallableUpdate $callableUpdate ) {
+ $this->cache = $cache;
+ $this->callableUpdate = $callableUpdate;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return string
+ */
+ public static function makeKey( $subject ) {
+
+ $segments = [];
+
+ if ( $subject instanceof DIWikiPage || $subject instanceof Title ) {
+ $segments = [ $subject->getDBKey(), $subject->getNamespace(), $subject->getInterwiki(), '' ];
+ }
+
+ return smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ implode( '#', $segments ),
+ self::VERSION
+ ]
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $hashList
+ * @param integer|true $revID
+ */
+ public function updateFromList( array $hashList, $revID = true ) {
+
+ foreach ( $hashList as $hash ) {
+
+ $key = smwfCacheKey(
+ self::CACHE_NAMESPACE,
+ [
+ $hash,
+ self::VERSION
+ ]
+ );
+
+ $this->cache->save( $key, $revID );
+ }
+
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|Title $subject
+ * @param integer|true $revID
+ */
+ public function update( $subject, $revID = true ) {
+ $this->cache->save( self::makeKey( $subject ), $revID );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage|Title $subject
+ */
+ public function has( $subject ) {
+ return $this->cache->contains( self::makeKey( $subject ) ) === true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ */
+ public function delete( $subject ) {
+
+ if ( !$subject instanceof Title && !$subject instanceof DIWikiPage ) {
+ throw new RuntimeException( "Invalid subject instance" );
+ }
+
+ // Avoid interference with any other process during a preOutputCommit
+ // stage especially when CACHE_DB is used as instance
+ $this->callableUpdate->setCallback( function() use( $subject ) {
+ $this->cache->delete( self::makeKey( $subject ) );
+ } );
+
+ $this->callableUpdate->setOrigin(
+ [
+ __METHOD__,
+ $subject->getDBKey() . '#' . $subject->getNamespace() . '#' . $subject->getInterwiki()
+ ]
+ );
+
+ $this->callableUpdate->pushUpdate();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilter.php
new file mode 100644
index 00000000..ab103fb2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilter.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\Store;
+use SMW\Utils\Timer;
+
+/**
+ * This class filters entities recorded in the ChangeOp
+ * and applies a relevance rule set by:
+ *
+ * - Remove exempted properties (not relevant)
+ * - Add properties that are affiliated on a relational change
+ *
+ * By affiliation implies that a property listed is not directly related to a query
+ * dependency, yet it is monitored and can, if altered trigger a dependency update
+ * that normally is only reserved to dependent properties.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class EntityIdListRelevanceDetectionFilter {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store = null;
+
+ /**
+ * @var ChangeOp
+ */
+ private $changeOp = null;
+
+ /**
+ * @var array
+ */
+ private $propertyExemptionList = [];
+
+ /**
+ * @var array
+ */
+ private $affiliatePropertyDetectionList = [];
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param ChangeOp $changeOp
+ */
+ public function __construct( Store $store, ChangeOp $changeOp ) {
+ $this->store = $store;
+ $this->changeOp = $changeOp;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DIWikiPage
+ */
+ public function getSubject() {
+ return $this->changeOp->getSubject();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $propertyExemptionList
+ */
+ public function setPropertyExemptionList( array $propertyExemptionList ) {
+ $this->propertyExemptionList = array_flip(
+ str_replace( ' ', '_', $propertyExemptionList )
+ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $affiliatePropertyDetectionList
+ */
+ public function setAffiliatePropertyDetectionList( array $affiliatePropertyDetectionList ) {
+ $this->affiliatePropertyDetectionList = array_flip(
+ str_replace( ' ', '_', $affiliatePropertyDetectionList )
+ );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return array
+ */
+ public function getFilteredIdList() {
+
+ Timer::start( __CLASS__ );
+
+ $changedEntityIdSummaryList = array_flip(
+ $this->changeOp->getChangedEntityIdSummaryList()
+ );
+
+ $affiliateEntityList = [];
+ $tableChangeOps = $this->changeOp->getTableChangeOps();
+
+ foreach ( $tableChangeOps as $tableChangeOp ) {
+ $this->applyFilterToTableChangeOp(
+ $tableChangeOp,
+ $affiliateEntityList,
+ $changedEntityIdSummaryList
+ );
+ }
+
+ $filteredIdList = array_merge(
+ array_keys( $changedEntityIdSummaryList ),
+ array_keys( $affiliateEntityList )
+ );
+
+ $this->logger->info(
+ [
+ 'QueryDependency',
+ 'EntityIdListRelevanceDetectionFilter',
+ 'Filter changeOp list',
+ 'procTime in sec: {procTime}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'procTime' => Timer::getElapsedTime( __CLASS__, 6 )
+ ]
+ );
+
+ return $filteredIdList;
+ }
+
+ private function applyFilterToTableChangeOp( $tableChangeOp, &$affiliateEntityList, &$changedEntityIdSummaryList ) {
+
+ foreach ( $tableChangeOp->getFieldChangeOps( 'insert' ) as $insertFieldChangeOp ) {
+
+ // Copy fields temporarily
+ if ( $tableChangeOp->isFixedPropertyOp() ) {
+ $insertFieldChangeOp->set( 'p_id', $tableChangeOp->getFixedPropertyValueBy( 'p_id' ) );
+ $insertFieldChangeOp->set( 'key', $tableChangeOp->getFixedPropertyValueBy( 'key' ) );
+ }
+
+ $this->modifyEntityList( $insertFieldChangeOp, $affiliateEntityList, $changedEntityIdSummaryList );
+ }
+
+ foreach ( $tableChangeOp->getFieldChangeOps( 'delete' ) as $deleteFieldChangeOp ) {
+
+ if ( $tableChangeOp->isFixedPropertyOp() ) {
+ $deleteFieldChangeOp->set( 'p_id', $tableChangeOp->getFixedPropertyValueBy( 'p_id' ) );
+ $deleteFieldChangeOp->set( 'key', $tableChangeOp->getFixedPropertyValueBy( 'key' ) );
+ }
+
+ $this->modifyEntityList( $deleteFieldChangeOp, $affiliateEntityList, $changedEntityIdSummaryList );
+ }
+ }
+
+ private function modifyEntityList( $fieldChangeOp, &$affiliateEntityList, &$changedEntityIdSummaryList ) {
+ $key = '';
+
+ if ( $fieldChangeOp->has( 'key' ) ) {
+ $key = $fieldChangeOp->get( 'key' );
+ } elseif ( $fieldChangeOp->has( 'p_id' ) ) {
+ $dataItem = $this->store->getObjectIds()->getDataItemById( $fieldChangeOp->get( 'p_id' ) );
+ $key = $dataItem !== null ? $dataItem->getDBKey() : null;
+ }
+
+ // Exclusion before inclusion
+ if ( isset( $this->propertyExemptionList[$key]) ) {
+ $this->unsetEntityList( $fieldChangeOp, $changedEntityIdSummaryList );
+ return;
+ }
+
+ if ( isset( $this->affiliatePropertyDetectionList[$key] ) && $fieldChangeOp->has( 's_id' ) ) {
+ $affiliateEntityList[$fieldChangeOp->get( 's_id' )] = true;
+ }
+ }
+
+ private function unsetEntityList( $fieldChangeOp, &$changedEntityIdSummaryList ) {
+ // Remove matched blacklisted property reference
+ if ( $fieldChangeOp->has( 'p_id' ) ) {
+ unset( $changedEntityIdSummaryList[$fieldChangeOp->get( 'p_id' )] );
+ }
+
+ // Remove associated subject ID's
+ if ( $fieldChangeOp->has( 's_id' ) ) {
+ unset( $changedEntityIdSummaryList[$fieldChangeOp->get( 's_id' )] );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryDependencyLinksStore.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryDependencyLinksStore.php
new file mode 100644
index 00000000..7c1cc359
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryDependencyLinksStore.php
@@ -0,0 +1,567 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ParserCachePurgeJob;
+use SMW\RequestOptions;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\SQLStore\SQLStore;
+use SMW\Store;
+use SMW\Utils\Timer;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class QueryDependencyLinksStore {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var DependencyLinksTableUpdater
+ */
+ private $dependencyLinksTableUpdater;
+
+ /**
+ * @var QueryResultDependencyListResolver
+ */
+ private $queryResultDependencyListResolver;
+
+ /**
+ * @var NamespaceExaminer
+ */
+ private $namespaceExaminer;
+
+ /**
+ * @var boolean
+ */
+ private $isEnabled = true;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isPrimary = false;
+
+ /**
+ * Time factor to be used to determine whether an update should actually occur
+ * or not. The comparison is made against the page_touched timestamp (updated
+ * by the ParserCachePurgeJob) to a previous update to avoid unnecessary DB
+ * transactions if it takes place within the computed time frame.
+ *
+ * @var integer
+ */
+ private $skewFactorForDependencyUpdateInSeconds = 10;
+
+ /**
+ * @since 2.3
+ *
+ * @param QueryResultDependencyListResolver $queryResultDependencyListResolver
+ * @param DependencyLinksTableUpdater $dependencyLinksTableUpdater
+ */
+ public function __construct( QueryResultDependencyListResolver $queryResultDependencyListResolver, DependencyLinksTableUpdater $dependencyLinksTableUpdater ) {
+ $this->queryResultDependencyListResolver = $queryResultDependencyListResolver;
+ $this->dependencyLinksTableUpdater = $dependencyLinksTableUpdater;
+ $this->store = $this->dependencyLinksTableUpdater->getStore();
+ $this->namespaceExaminer = ApplicationFactory::getInstance()->getNamespaceExaminer();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ * Indicates whether MW is running in command-line mode.
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = $isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isPrimary
+ */
+ public function isPrimary( $isPrimary ) {
+ $this->isPrimary = $isPrimary;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ return $this->isEnabled;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param boolean $isEnabled
+ */
+ public function setEnabled( $isEnabled ) {
+ $this->isEnabled = (bool)$isEnabled;
+ }
+
+ /**
+ * This method is called from the `SMW::SQLStore::AfterDataUpdateComplete` hook and
+ * removes outdated query ID's from the table if the diff contains a `delete`
+ * entry for the _ask table.
+ *
+ * @since 2.3
+ *
+ * @param ChangeOp $changeOp
+ */
+ public function pruneOutdatedTargetLinks( ChangeOp $changeOp ) {
+
+ if ( !$this->isEnabled() ) {
+ return null;
+ }
+
+ Timer::start( __METHOD__ );
+ $hash = null;
+
+ $tableName = $this->store->getPropertyTableInfoFetcher()->findTableIdForProperty(
+ new DIProperty( '_ASK' )
+ );
+
+ $tableChangeOps = $changeOp->getTableChangeOps( $tableName );
+
+ // Remove any dependency for queries that are no longer used
+ foreach ( $tableChangeOps as $tableChangeOp ) {
+
+ if ( !$tableChangeOp->hasChangeOp( 'delete' ) ) {
+ continue;
+ }
+
+ $deleteIdList = [];
+
+ foreach ( $tableChangeOp->getFieldChangeOps( 'delete' ) as $fieldChangeOp ) {
+ $deleteIdList[] = $fieldChangeOp->get( 'o_id' );
+ }
+
+ $this->dependencyLinksTableUpdater->deleteDependenciesFromList( $deleteIdList );
+ }
+
+ if ( ( $subject = $changeOp->getSubject() ) !== null ) {
+ $hash = $subject->getHash();
+ }
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $hash,
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 7 )
+ ];
+
+ $this->logger->info(
+ '[QueryDependency] Prune links completed: {origin} (procTime in sec: {procTime})',
+ $context
+ );
+
+ return true;
+ }
+
+ /**
+ * Build the ParserCachePurgeJob parameters on filtered entities to minimize
+ * necessary update work.
+ *
+ * @since 2.3
+ *
+ * @param EntityIdListRelevanceDetectionFilter $entityIdListRelevanceDetectionFilter
+ */
+ public function pushParserCachePurgeJob( EntityIdListRelevanceDetectionFilter $entityIdListRelevanceDetectionFilter ) {
+
+ if ( !$this->isEnabled() ) {
+ return;
+ }
+
+ $filteredIdList = $entityIdListRelevanceDetectionFilter->getFilteredIdList();
+
+ if ( $filteredIdList === [] ) {
+ return;
+ }
+
+ $parserCachePurgeJob = ApplicationFactory::getInstance()->newJobFactory()->newParserCachePurgeJob(
+ $entityIdListRelevanceDetectionFilter->getSubject()->getTitle(),
+ [
+ 'idlist' => $filteredIdList,
+ 'exec.mode' => ParserCachePurgeJob::EXEC_JOURNAL
+ ]
+ );
+
+ if ( $this->isPrimary || $this->isCommandLineMode ) {
+ $parserCachePurgeJob->run();
+ } else {
+ $parserCachePurgeJob->lazyPush();
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return array
+ */
+ public function findEmbeddedQueryIdListBySubject( DIWikiPage $subject, RequestOptions $requestOptions = null ) {
+
+ $embeddedQueryIdList = [];
+
+ $dataItems = $this->store->getPropertyValues(
+ $subject,
+ new DIProperty( '_ASK' ),
+ $requestOptions
+ );
+
+ foreach ( $dataItems as $dataItem ) {
+ $embeddedQueryIdList[$dataItem->getHash()] = $this->dependencyLinksTableUpdater->getId( $dataItem );
+ }
+
+ return $embeddedQueryIdList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param RequestOptions $requestOptions
+ *
+ * @return array
+ */
+ public function findDependencyTargetLinksForSubject( DIWikiPage $subject, RequestOptions $requestOptions ) {
+ return $this->findDependencyTargetLinks(
+ [ $this->dependencyLinksTableUpdater->getId( $subject ) ],
+ $requestOptions
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer|array $id
+ *
+ * @return integer
+ */
+ public function countDependencies( $id ) {
+
+ $count = 0;
+ $ids = !is_array( $id ) ? (array)$id : $id;
+
+ if ( $ids === [] || !$this->isEnabled() ) {
+ return $count;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ SQLStore::QUERY_LINKS_TABLE,
+ [
+ 'COUNT(s_id) AS count'
+ ],
+ [
+ 'o_id' => $ids
+ ],
+ __METHOD__
+ );
+
+ $count = $row ? $row->count : $count;
+
+ return (int)$count;
+ }
+
+ /**
+ * Finds a partial list (given limit and offset) of registered subjects that
+ * that represent a dependency on something like a subject in a query list,
+ * a property, or a printrequest.
+ *
+ * `s_id` contains the subject id that links to the query that fulfills one
+ * of the conditions cited above.
+ *
+ * Prefetched Ids are turned into a hash list that can later be split into
+ * chunks to work either in online or batch mode without creating a huge memory
+ * foothold.
+ *
+ * @note Select a list is crucial for performance as any selectRow would /
+ * single Id select would strain the system on large list connected to a
+ * query
+ *
+ * @since 2.3
+ *
+ * @param array $idlist
+ * @param RequestOptions $requestOptions
+ *
+ * @return array
+ */
+ public function findDependencyTargetLinks( array $idlist, RequestOptions $requestOptions ) {
+
+ if ( $idlist === [] || !$this->isEnabled() ) {
+ return [];
+ }
+
+ $options = [
+ 'LIMIT' => $requestOptions->getLimit(),
+ 'OFFSET' => $requestOptions->getOffset(),
+ ] + [ 'DISTINCT' ];
+
+ $conditions = [
+ 'o_id' => $idlist
+ ];
+
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+ $conditions[] = $extraCondition;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $rows = $connection->select(
+ SQLStore::QUERY_LINKS_TABLE,
+ [ 's_id' ],
+ $conditions,
+ __METHOD__,
+ $options
+ );
+
+ $targetLinksIdList = [];
+
+ foreach ( $rows as $row ) {
+ $targetLinksIdList[] = $row->s_id;
+ }
+
+ if ( $targetLinksIdList === [] ) {
+ return [];
+ }
+
+ // Return the expected count of targets
+ $requestOptions->setOption( 'links.count', count( $targetLinksIdList ) );
+
+ $poolRequestOptions = new RequestOptions();
+
+ $poolRequestOptions->addExtraCondition(
+ 'smw_iw !=' . $connection->addQuotes( SMW_SQL3_SMWREDIIW ) . ' AND '.
+ 'smw_iw !=' . $connection->addQuotes( SMW_SQL3_SMWDELETEIW )
+ );
+
+ return $this->store->getObjectIds()->getDataItemPoolHashListFor(
+ $targetLinksIdList,
+ $poolRequestOptions
+ );
+ }
+
+ /**
+ * This method is called from the `SMW::Store::AfterQueryResultLookupComplete` hook
+ * to resolve and update dependencies fetched from an embedded query and its
+ * QueryResult object.
+ *
+ * @since 2.3
+ *
+ * @param QueryResult|string $queryResult
+ */
+ public function updateDependencies( $queryResult ) {
+
+ if ( !$this->canUpdateDependencies( $queryResult ) ) {
+ return null;
+ }
+
+ Timer::start( __METHOD__ );
+
+ $subject = $queryResult->getQuery()->getContextPage();
+ $hash = $queryResult->getQuery()->getQueryId();
+
+ $sid = $this->dependencyLinksTableUpdater->getId(
+ $subject,
+ $hash
+ );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'id' => $sid
+ ];
+
+ if ( $this->isRegistered( $sid, $subject ) ) {
+ return $this->logger->info(
+ '[QueryDependency] Skipping update: {id} (already registered, no dependency update)',
+ $context
+ );
+ }
+
+ // Executed as DeferredTransactionalUpdate
+ $callback = function() use( $queryResult, $subject, $sid, $hash ) {
+ $this->doUpdate( $queryResult, $subject, $sid, $hash );
+ };
+
+ $deferredTransactionalUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate(
+ $callback
+ );
+
+ $origin = $subject->getHash();
+
+ $deferredTransactionalUpdate->setOrigin( [ __METHOD__, $origin ] );
+ $deferredTransactionalUpdate->markAsPending( $this->isCommandLineMode );
+ $deferredTransactionalUpdate->setFingerprint( $hash );
+
+ $deferredTransactionalUpdate->enabledDeferredUpdate( true );
+ $deferredTransactionalUpdate->waitOnTransactionIdle();
+
+ $deferredTransactionalUpdate->pushUpdate();
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $origin,
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 7 )
+ ];
+
+ $this->logger->info(
+ '[QueryDependency] Update dependencies registered: {origin} (procTime in sec: {procTime})',
+ $context
+ );
+
+ return true;
+ }
+
+ private function doUpdate( $queryResult, $subject, $sid, $hash ) {
+
+ $dependencyList = $this->queryResultDependencyListResolver->getDependencyListFrom(
+ $queryResult
+ );
+
+ // Add extra dependencies which we only get "late" after the QueryResult
+ // object as been resolved by the ResultPrinter, this is done to
+ // avoid having to process the QueryResult recursively on its own
+ // (which would carry a performance penalty)
+ $dependencyListByLateRetrieval = $this->queryResultDependencyListResolver->getDependencyListByLateRetrievalFrom(
+ $queryResult
+ );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'origin' => $hash
+ ];
+
+ if ( $dependencyList === [] && $dependencyListByLateRetrieval === [] ) {
+ return $this->logger->info(
+ '[QueryDependency] no update: {origin} (no dependency list available)',
+ $context
+ );
+ }
+
+ // SID < 0 means the storage update/process has not been finalized
+ // (new object hasn't been registered)
+ if ( $sid < 1 || ( $sid = $this->dependencyLinksTableUpdater->getId( $subject, $hash ) ) < 1 ) {
+ $sid = $this->dependencyLinksTableUpdater->createId( $subject, $hash );
+ }
+
+ $this->dependencyLinksTableUpdater->addToUpdateList(
+ $sid,
+ $dependencyList
+ );
+
+ $this->dependencyLinksTableUpdater->addToUpdateList(
+ $sid,
+ $dependencyListByLateRetrieval
+ );
+
+ $this->dependencyLinksTableUpdater->doUpdate();
+ }
+
+ private function canUpdateDependencies( $queryResult ) {
+
+ if ( !$this->isEnabled() || !$queryResult instanceof QueryResult ) {
+ return false;
+ }
+
+ $query = $queryResult->getQuery();
+
+ $actions = [
+ // #2484 Avoid any update activities during a stashedit API access
+ 'stashedit',
+
+ // Avoid update on `submit` during a preview
+ 'submit',
+
+ // Avoid update on `parse` during a wikieditor preview
+ 'parse'
+ ];
+
+ if ( in_array( $query->getOption( 'request.action' ), $actions ) ) {
+ return false;
+ }
+
+ if ( $query === null || $query->getContextPage() === null ) {
+ return false;
+ }
+
+ // Make sure that when a query is embedded in a not supported NS to bail
+ // out
+ if ( !$this->namespaceExaminer->isSemanticEnabled( $query->getContextPage()->getNamespace() ) ) {
+ return false;
+ }
+
+ return $query->getLimit() > 0 && $query->getOption( Query::NO_DEPENDENCY_TRACE ) !== true;
+ }
+
+ private function isRegistered( $sid, $subject ) {
+
+ static $suppressUpdateCache = [];
+ $hash = $subject->getHash();
+
+ if ( $sid < 1 ) {
+ return false;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ SQLStore::QUERY_LINKS_TABLE,
+ [
+ 's_id'
+ ],
+ [ 's_id' => $sid ],
+ __METHOD__
+ );
+
+ $title = $subject->getTitle();
+
+ // https://phabricator.wikimedia.org/T167943
+ if ( !isset( $suppressUpdateCache[$hash] ) && $title !== null ) {
+ $suppressUpdateCache[$hash] = wfTimestamp( TS_MW, $title->getTouched() ) + $this->skewFactorForDependencyUpdateInSeconds;
+ }
+
+ // Check whether the query has already been registered and only then
+ // check for a possible divergent time
+ return $row !== false && $suppressUpdateCache[$hash] > wfTimestamp( TS_MW );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryReferenceBacklinks.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryReferenceBacklinks.php
new file mode 100644
index 00000000..b683627c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryReferenceBacklinks.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Message;
+use SMW\RequestOptions;
+use SMW\SemanticData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryReferenceBacklinks {
+
+ /**
+ * @var QueryDependencyLinksStore
+ */
+ private $queryDependencyLinksStore = null;
+
+ /**
+ * @since 2.5
+ *
+ * @param QueryDependencyLinksStore $queryDependencyLinksStore
+ */
+ public function __construct( QueryDependencyLinksStore $queryDependencyLinksStore ) {
+ $this->queryDependencyLinksStore = $queryDependencyLinksStore;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param SemanticData $semanticData
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return boolean
+ */
+ public function addReferenceLinksTo( SemanticData $semanticData, RequestOptions $requestOptions = null ) {
+
+ if ( !$this->queryDependencyLinksStore->isEnabled() ) {
+ return false;
+ }
+
+ // Don't display a reference where the requesting page is
+ // part of the list that contains queries (suppress self-embedded queries)
+ foreach ( $this->queryDependencyLinksStore->findEmbeddedQueryIdListBySubject( $semanticData->getSubject() ) as $key => $qid ) {
+ $requestOptions->addExtraCondition( 's_id!=' . $qid );
+ }
+
+ $referenceLinks = $this->findReferenceLinks( $semanticData->getSubject(), $requestOptions );
+
+ $property = new DIProperty(
+ '_ASK'
+ );
+
+ foreach ( $referenceLinks as $subject ) {
+ $semanticData->addPropertyObjectValue( $property, DIWikiPage::doUnserialize( $subject ) );
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIWikiPage $subject
+ * @param integer $limit
+ * @param integer $offset
+ *
+ * @return array
+ */
+ public function findReferenceLinks( DIWikiPage $subject, RequestOptions $requestOptions = null ) {
+
+ $queryTargetLinksHashList = $this->queryDependencyLinksStore->findDependencyTargetLinksForSubject(
+ $subject,
+ $requestOptions
+ );
+
+ return $queryTargetLinksHashList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ * @param DIWikiPage $subject
+ *
+ * @return boolean
+ */
+ public function doesRequireFurtherLink( DIProperty $property, DIWikiPage $subject, &$html ) {
+
+ if ( $property->getKey() !== '_ASK' ) {
+ return true;
+ }
+
+ $localURL = \SpecialPage::getSafeTitleFor( 'SearchByProperty' )->getLocalURL(
+ [
+ 'property' => $property->getLabel(),
+ 'value' => $subject->getTitle()->getPrefixedText()
+ ]
+ );
+
+ $html .= \Html::element(
+ 'a',
+ [ 'href' => $localURL ],
+ Message::get( 'smw_browse_more' )
+ );
+
+ // Return false in order to stop the link creation process the replace the
+ // generate link
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryResultDependencyListResolver.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryResultDependencyListResolver.php
new file mode 100644
index 00000000..a56c4103
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependency/QueryResultDependencyListResolver.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace SMW\SQLStore\QueryDependency;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\HierarchyLookup;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class QueryResultDependencyListResolver {
+
+ /**
+ * @var HierarchyLookup
+ */
+ private $hierarchyLookup;
+
+ /**
+ * Specifies a list of property keys to be excluded from the detection
+ * process.
+ *
+ * @var array
+ */
+ private $propertyDependencyExemptionlist = [];
+
+ /**
+ * @since 2.3
+ *
+ * @param $queryResult Can be a string for when format=Debug
+ * @param HierarchyLookup $hierarchyLookup
+ */
+ public function __construct( HierarchyLookup $hierarchyLookup ) {
+ $this->hierarchyLookup = $hierarchyLookup;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param array $propertyDependencyExemptionlist
+ */
+ public function setPropertyDependencyExemptionlist( array $propertyDependencyExemptionlist ) {
+ // Make sure that user defined properties are correctly normalized and flip
+ // to build an index based map
+ $this->propertyDependencyExemptionlist = array_flip(
+ str_replace( ' ', '_', $propertyDependencyExemptionlist )
+ );
+ }
+
+ /**
+ * At the point where the QueryResult instantiates results by means of the
+ * ResultArray, record the objects with the help of the ResolverJournal.
+ *
+ * When the `... updateDependencies` is executed in deferred mode it allows
+ * a "late" access to track dependencies of column/row entities without having
+ * to resolve the QueryResult object on its own, see
+ * ResultArray::getNextDataValue/ResultArray::getNextDataItem.
+ *
+ * @since 2.4
+ *
+ * @param QueryResult|string $queryResult
+ *
+ * @return DIWikiPage[]|[]
+ */
+ public function getDependencyListByLateRetrievalFrom( $queryResult ) {
+
+ if ( !$this->canResolve( $queryResult ) ) {
+ return [];
+ }
+
+ $resolverJournal = $queryResult->getResolverJournal();
+
+ $dependencyList = $resolverJournal->getEntityList();
+ $resolverJournal->prune();
+
+ return $dependencyList;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param QueryResult|string $queryResult
+ *
+ * @return DIWikiPage[]|[]
+ */
+ public function getDependencyListFrom( $queryResult ) {
+
+ if ( !$this->canResolve( $queryResult ) ) {
+ return [];
+ }
+
+ $description = $queryResult->getQuery()->getDescription();
+
+ $dependencySubjectList = [
+ $queryResult->getQuery()->getContextPage()
+ ];
+
+ // Find entities described by the query
+ $this->doResolveDependenciesFromDescription(
+ $dependencySubjectList,
+ $queryResult->getStore(),
+ $description
+ );
+
+ $this->doResolveDependenciesFromPrintRequest(
+ $dependencySubjectList,
+ $description->getPrintRequests()
+ );
+
+ $dependencySubjectList = array_merge(
+ $dependencySubjectList,
+ $queryResult->getResults()
+ );
+
+ $queryResult->reset();
+
+ return $dependencySubjectList;
+ }
+
+ /**
+ * Resolving dependencies for non-embedded queries or limit=0 (which only
+ * links to Special:Ask via further results) is not required
+ */
+ private function canResolve( $queryResult ) {
+ return $queryResult instanceof QueryResult && $queryResult->getQuery() !== null && $queryResult->getQuery()->getContextPage() !== null && $queryResult->getQuery()->getLimit() > 0;
+ }
+
+ private function doResolveDependenciesFromDescription( &$subjects, $store, $description ) {
+
+ // Ignore entities that use a comparator other than SMW_CMP_EQ
+ // [[Has page::~Foo*]] or similar is going to be ignored
+ if ( $description instanceof ValueDescription &&
+ $description->getDataItem() instanceof DIWikiPage &&
+ $description->getComparator() === SMW_CMP_EQ ) {
+ $subjects[] = $description->getDataItem();
+ }
+
+ if ( $description instanceof ConceptDescription && $concept = $description->getConcept() ) {
+ if ( $concept === null || !isset( $subjects[$concept->getHash()] ) ) {
+ $subjects[$concept->getHash()] = $concept;
+ $this->doResolveDependenciesFromDescription(
+ $subjects,
+ $store,
+ $this->getConceptDescription( $store, $concept )
+ );
+ }
+ }
+
+ if ( $description instanceof ClassDescription ) {
+ foreach ( $description->getCategories() as $category ) {
+
+ if ( $this->hierarchyLookup->hasSubcategory( $category ) ) {
+ $this->doMatchSubcategory( $subjects, $category );
+ }
+
+ $subjects[] = $category;
+ }
+ }
+
+ if ( $description instanceof SomeProperty ) {
+ $this->doResolveDependenciesFromDescription( $subjects, $store, $description->getDescription() );
+ $this->doMatchProperty( $subjects, $description->getProperty() );
+ }
+
+ if ( $description instanceof Conjunction || $description instanceof Disjunction ) {
+ foreach ( $description->getDescriptions() as $description ) {
+ $this->doResolveDependenciesFromDescription( $subjects, $store, $description );
+ }
+ }
+ }
+
+ private function doMatchProperty( &$subjects, DIProperty $property ) {
+
+ if ( $property->isInverse() ) {
+ $property = new DIProperty( $property->getKey() );
+ }
+
+ $subject = $property->getCanonicalDiWikiPage();
+
+ if ( $this->hierarchyLookup->hasSubproperty( $property ) ) {
+ $this->doMatchSubproperty( $subjects, $subject, $property );
+ }
+
+ // Use the key here do match against pre-defined properties (e.g. _MDAT)
+ $key = str_replace( ' ', '_', $property->getKey() );
+
+ if ( !isset( $this->propertyDependencyExemptionlist[$key] ) ) {
+ $subjects[$subject->getHash()] = $subject;
+ }
+ }
+
+ private function doMatchSubcategory( &$subjects, DIWikiPage $category ) {
+
+ $hash = $category->getHash();
+ $subcategories = [];
+
+ // #1713
+ // Safeguard against a possible category (or redirect thereof) to point
+ // to itself by relying on tracking the hash of already inserted objects
+ if ( !isset( $subjects[$hash] ) ) {
+ $subcategories = $this->hierarchyLookup->getConsecutiveHierarchyList( $category );
+ }
+
+ foreach ( $subcategories as $subcategory ) {
+ $subjects[$subcategory->getHash()] = $subcategory;
+
+ if ( $this->hierarchyLookup->hasSubcategory( $subcategory ) ) {
+ $this->doMatchSubcategory( $subjects, $subcategory );
+ }
+ }
+ }
+
+ private function doMatchSubproperty( &$subjects, $subject, DIProperty $property ) {
+
+ $subproperties = [];
+
+ // Using the DBKey as short-cut, as we don't expect to match sub-properties for
+ // pre-defined properties instead it should be sufficient for user-defined
+ // properties to rely on the normalized DBKey (e.g Has_page)
+ if (
+ !isset( $subjects[$subject->getHash()] ) &&
+ !isset( $this->propertyDependencyExemptionlist[$subject->getDBKey()] ) ) {
+ $subproperties = $this->hierarchyLookup->getConsecutiveHierarchyList( $property );
+ }
+
+ foreach ( $subproperties as $subproperty ) {
+
+ if ( isset( $this->propertyDependencyExemptionlist[$subproperty->getKey()] ) ) {
+ continue;
+ }
+
+ $subject = $subproperty->getCanonicalDiWikiPage();
+ $subjects[$subject->getHash()] = $subject;
+
+ $this->doMatchProperty( $subjects, $subproperty );
+ }
+ }
+
+ private function doResolveDependenciesFromPrintRequest( &$subjects, array $printRequests ) {
+
+ foreach ( $printRequests as $printRequest ) {
+ $data = $printRequest->getData();
+
+ if ( $data instanceof \SMWPropertyValue ) {
+ $subjects[] = $data->getDataItem()->getCanonicalDiWikiPage();
+ }
+
+ // Category
+ if ( $data instanceof \Title ) {
+ $subjects[] = DIWikiPage::newFromTitle( $data );
+ }
+ }
+ }
+
+ private function getConceptDescription( $store, DIWikiPage $concept ) {
+
+ $value = $store->getPropertyValues(
+ $concept,
+ new DIProperty( '_CONC' )
+ );
+
+ if ( $value === null || $value === [] ) {
+ return new ThingDescription();
+ }
+
+ $value = end( $value );
+
+ return ApplicationFactory::getInstance()->newQueryParser()->getQueryDescription(
+ $value->getConceptQuery()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependencyLinksStoreFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependencyLinksStoreFactory.php
new file mode 100644
index 00000000..d557a8a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryDependencyLinksStoreFactory.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\Site;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater;
+use SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal;
+use SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter;
+use SMW\SQLStore\QueryDependency\QueryDependencyLinksStore;
+use SMW\SQLStore\QueryDependency\QueryReferenceBacklinks;
+use SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver;
+use SMW\Store;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryDependencyLinksStoreFactory {
+
+ /**
+ * @since 3.0
+ *
+ * @return DependencyLinksUpdateJournal
+ */
+ public function newDependencyLinksUpdateJournal() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $dependencyLinksUpdateJournal = new DependencyLinksUpdateJournal(
+ $applicationFactory->getCache(),
+ $applicationFactory->newDeferredCallableUpdate()
+ );
+
+ $dependencyLinksUpdateJournal->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ return $dependencyLinksUpdateJournal;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return QueryResultDependencyListResolver
+ */
+ public function newQueryResultDependencyListResolver() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $queryResultDependencyListResolver = new QueryResultDependencyListResolver(
+ $applicationFactory->newHierarchyLookup()
+ );
+
+ $queryResultDependencyListResolver->setPropertyDependencyExemptionlist(
+ $applicationFactory->getSettings()->get( 'smwgQueryDependencyPropertyExemptionList' )
+ );
+
+ return $queryResultDependencyListResolver;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ *
+ * @return QueryDependencyLinksStore
+ */
+ public function newQueryDependencyLinksStore( Store $store ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $logger = $applicationFactory->getMediaWikiLogger();
+
+ $dependencyLinksTableUpdater = new DependencyLinksTableUpdater(
+ $store
+ );
+
+ $dependencyLinksTableUpdater->setLogger(
+ $logger
+ );
+
+ $queryDependencyLinksStore = new QueryDependencyLinksStore(
+ $this->newQueryResultDependencyListResolver(),
+ $dependencyLinksTableUpdater
+ );
+
+ $queryDependencyLinksStore->setLogger(
+ $logger
+ );
+
+ $queryDependencyLinksStore->setEnabled(
+ $applicationFactory->getSettings()->get( 'smwgEnabledQueryDependencyLinksStore' )
+ );
+
+ $queryDependencyLinksStore->isCommandLineMode(
+ Site::isCommandLineMode()
+ );
+
+ return $queryDependencyLinksStore;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param Store $store
+ * @param ChangeOp $changeOp
+ *
+ * @return EntityIdListRelevanceDetectionFilter
+ */
+ public function newEntityIdListRelevanceDetectionFilter( Store $store, ChangeOp $changeOp ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $entityIdListRelevanceDetectionFilter = new EntityIdListRelevanceDetectionFilter(
+ $store,
+ $changeOp
+ );
+
+ $entityIdListRelevanceDetectionFilter->setLogger(
+ ApplicationFactory::getInstance()->getMediaWikiLogger()
+ );
+
+ $entityIdListRelevanceDetectionFilter->setPropertyExemptionList(
+ $settings->get( 'smwgQueryDependencyPropertyExemptionList' )
+ );
+
+ $entityIdListRelevanceDetectionFilter->setAffiliatePropertyDetectionList(
+ $settings->get( 'smwgQueryDependencyAffiliatePropertyDetectionList' )
+ );
+
+ return $entityIdListRelevanceDetectionFilter;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return QueryReferenceBacklinks
+ */
+ public function newQueryReferenceBacklinks( Store $store ) {
+ return new QueryReferenceBacklinks( $this->newQueryDependencyLinksStore( $store ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/ConceptQuerySegmentBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/ConceptQuerySegmentBuilder.php
new file mode 100644
index 00000000..107c6b3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/ConceptQuerySegmentBuilder.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\Query\Parser as QueryParser;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ConceptQuerySegmentBuilder {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var QuerySegmentListProcessor
+ */
+ private $querySegmentListProcessor;
+
+ /**
+ * @var QueryParser
+ */
+ private $queryParser;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ * @param QuerySegmentListProcessor $querySegmentListProcessor
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder, QuerySegmentListProcessor $querySegmentListProcessor ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->querySegmentListProcessor = $querySegmentListProcessor;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param QueryParser $queryParser
+ */
+ public function setQueryParser( QueryParser $queryParser ) {
+ $this->queryParser = $queryParser;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $conceptDescriptionText
+ *
+ * @return QuerySegment|null
+ */
+ public function getQuerySegmentFrom( $conceptDescriptionText ) {
+
+ QuerySegment::$qnum = 0;
+
+ $querySegmentListBuilder = $this->querySegmentListBuilder;
+ $querySegmentListBuilder->setSortKeys( [] );
+
+ if ( $this->queryParser === null ) {
+ throw new RuntimeException( 'Missing a QueryParser instance' );
+ }
+
+ $querySegmentListBuilder->getQuerySegmentFrom(
+ $this->queryParser->getQueryDescription( $conceptDescriptionText )
+ );
+
+ $qid = $querySegmentListBuilder->getLastQuerySegmentId();
+ $querySegmentList = $querySegmentListBuilder->getQuerySegmentList();
+
+ if ( $qid < 0 ) {
+ return null;
+ }
+
+ // execute query tree, resolve all dependencies
+ $this->querySegmentListProcessor->setQueryMode(
+ Query::MODE_INSTANCES
+ );
+
+ $this->querySegmentListProcessor->setQuerySegmentList(
+ $querySegmentList
+ );
+
+ $this->querySegmentListProcessor->process( $qid );
+
+ return $querySegmentList[$qid];
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function cleanUp() {
+ $this->querySegmentListProcessor->setQueryMode( Query::MODE_INSTANCES );
+ $this->querySegmentListProcessor->cleanUp();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->querySegmentListBuilder->getErrors();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreter.php
new file mode 100644
index 00000000..e5c5241f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreter.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use SMW\Query\Language\Description;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+interface DescriptionInterpreter {
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description );
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreterFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreterFactory.php
new file mode 100644
index 00000000..391553a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreterFactory.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use SMW\ApplicationFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\DisjunctionConjunctionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\DispatchingDescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DescriptionInterpreterFactory {
+
+ /**
+ * @since 2.4
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ *
+ * @return DispatchingDescriptionInterpreter
+ */
+ public function newDispatchingDescriptionInterpreter( QuerySegmentListBuilder $querySegmentListBuilder ) {
+
+ $pplicationFactory = ApplicationFactory::getInstance();
+ $dispatchingDescriptionInterpreter = new DispatchingDescriptionInterpreter();
+
+ $dispatchingDescriptionInterpreter->addDefaultInterpreter(
+ new ThingDescriptionInterpreter( $querySegmentListBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new SomePropertyInterpreter( $querySegmentListBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new DisjunctionConjunctionInterpreter( $querySegmentListBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new NamespaceDescriptionInterpreter( $querySegmentListBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ClassDescriptionInterpreter( $querySegmentListBuilder )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ new ValueDescriptionInterpreter( $querySegmentListBuilder )
+ );
+
+ $conceptDescriptionInterpreter = new ConceptDescriptionInterpreter(
+ $querySegmentListBuilder
+ );
+
+ $conceptDescriptionInterpreter->setQueryParser(
+ $pplicationFactory->getQueryFactory()->newQueryParser(
+ $pplicationFactory->getSettings()->get( 'smwgQConceptFeatures' )
+ )
+ );
+
+ $dispatchingDescriptionInterpreter->addInterpreter(
+ $conceptDescriptionInterpreter
+ );
+
+ return $dispatchingDescriptionInterpreter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
new file mode 100644
index 00000000..e936bf94
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreter.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Description;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class ClassDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ClassDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+
+ $cqid = QuerySegment::$qnum;
+ $cquery = new QuerySegment();
+ $cquery->type = QuerySegment::Q_CLASS_HIERARCHY;
+ $cquery->joinfield = [];
+ $cquery->depth = $description->getHierarchyDepth();
+
+ foreach ( $description->getCategories() as $category ) {
+
+ $categoryId = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID(
+ $category->getDBkey(),
+ NS_CATEGORY,
+ $category->getInterwiki(),
+ ''
+ );
+
+ if ( $categoryId != 0 ) {
+ $cquery->joinfield[] = $categoryId;
+ }
+ }
+
+ if ( count( $cquery->joinfield ) == 0 ) { // Empty result.
+ $query->type = QuerySegment::Q_VALUE;
+ $query->joinTable = '';
+ $query->joinfield = '';
+ } else { // Instance query with disjunction of classes (categories)
+ $query->joinTable = $this->querySegmentListBuilder->getStore()->findPropertyTableID( new DIProperty( '_INST' ) );
+ $query->joinfield = "$query->alias.s_id";
+ $query->components[$cqid] = "$query->alias.o_id";
+
+ $this->querySegmentListBuilder->addQuerySegment( $cquery );
+ }
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapper.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapper.php
new file mode 100644
index 00000000..e32ac8c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapper.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use RuntimeException;
+use SMW\Query\Language\ValueDescription;
+use SMWDIUri as DIUri;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ComparatorMapper {
+
+ /**
+ * @since 2.2
+ *
+ * @param ValueDescription $description
+ * @param string &$value
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function mapComparator( ValueDescription $description, &$value ) {
+
+ $comparatorMap = [
+ SMW_CMP_EQ => '=',
+ SMW_CMP_LESS => '<',
+ SMW_CMP_GRTR => '>',
+ SMW_CMP_LEQ => '<=',
+ SMW_CMP_GEQ => '>=',
+ SMW_CMP_NEQ => '!=',
+ SMW_CMP_LIKE => ' LIKE ',
+ SMW_CMP_PRIM_LIKE => ' LIKE ',
+ SMW_CMP_NLKE => ' NOT LIKE ',
+ SMW_CMP_PRIM_NLKE => ' NOT LIKE '
+ ];
+
+ $comparator = $description->getComparator();
+
+ if ( !isset( $comparatorMap[$comparator] ) ) {
+ throw new RuntimeException( "Unsupported comparator $comparator in value description." );
+ }
+
+ if ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE || $comparator === SMW_CMP_PRIM_LIKE || $comparator === SMW_CMP_PRIM_NLKE ) {
+
+ if ( $description->getDataItem() instanceof DIUri ) {
+ $value = str_replace( [ 'http://', 'https://', '%2A' ], [ '*', '*', '*' ], $value );
+ }
+
+ // Escape to prepare string matching:
+ $value = str_replace(
+ [ '\\', '%', '_', '*', '?' ],
+ [ '\\\\', '\%', '\_', '%', '_' ],
+ $value
+ );
+ }
+
+ return $comparatorMap[$comparator];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
new file mode 100644
index 00000000..82955af1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreter.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use RuntimeException;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Parser as QueryParser;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMWSQLStore3;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var QueryParser
+ */
+ private $queryParser;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ConceptDescription;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param QueryParser $queryParser
+ */
+ public function setQueryParser( QueryParser $queryParser ) {
+ $this->queryParser = $queryParser;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+ $concept = $description->getConcept();
+
+ $conceptId = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID(
+ $concept->getDBkey(),
+ SMW_NS_CONCEPT,
+ '',
+ ''
+ );
+
+ $hash = 'concept-' . $conceptId;
+
+ $this->querySegmentListBuilder->getCircularReferenceGuard()->mark( $hash );
+
+ if ( $this->querySegmentListBuilder->getCircularReferenceGuard()->isCircular( $hash ) ) {
+
+ $this->querySegmentListBuilder->addError(
+ [ 'smw-query-condition-circular', $description->getQueryString() ]
+ );
+
+ return $query;
+ }
+
+ $db = $this->querySegmentListBuilder->getStore()->getConnection( 'mw.db.queryengine' );
+ $row = $this->getConceptForId( $db, $conceptId );
+
+ // No description found, concept does not exist.
+ if ( $row === false ) {
+ $this->querySegmentListBuilder->getCircularReferenceGuard()->unmark( 'concept-' . $conceptId );
+ // keep the above query object, it yields an empty result
+ // TODO: announce an error here? (maybe not, since the query processor can check for
+ // non-existing concept pages which is probably the main reason for finding nothing here)
+ return $query;
+ };
+
+ global $smwgQConceptCaching, $smwgQMaxSize, $smwgQMaxDepth, $smwgQFeatures, $smwgQConceptCacheLifetime;
+
+ $may_be_computed = ( $smwgQConceptCaching == CONCEPT_CACHE_NONE ) ||
+ ( ( $smwgQConceptCaching == CONCEPT_CACHE_HARD ) && ( ( ~( ~( $row->concept_features + 0 ) | $smwgQFeatures ) ) == 0 ) &&
+ ( $smwgQMaxSize >= $row->concept_size ) && ( $smwgQMaxDepth >= $row->concept_depth ) );
+
+ if ( $row->cache_date &&
+ ( ( $row->cache_date > ( strtotime( "now" ) - $smwgQConceptCacheLifetime * 60 ) ) ||
+ !$may_be_computed ) ) { // Cached concept, use cache unless it is dead and can be revived.
+
+ $query->joinTable = SMWSQLStore3::CONCEPT_CACHE_TABLE;
+ $query->joinfield = "$query->alias.s_id";
+ $query->where = "$query->alias.o_id=" . $db->addQuotes( $conceptId );
+ } elseif ( $row->concept_txt ) { // Parse description and process it recursively.
+ if ( $may_be_computed ) {
+ $description = $this->getConceptQueryDescriptionFrom( $row->concept_txt );
+
+ $this->findCircularDescription(
+ $concept,
+ $description
+ );
+
+ $qid = $this->querySegmentListBuilder->getQuerySegmentFrom( $description );
+
+ if ($qid != -1) {
+ $query = $this->querySegmentListBuilder->findQuerySegment( $qid );
+ } else { // somehow the concept query is no longer valid; maybe some syntax changed (upgrade) or global settings were modified since storing it
+ $this->querySegmentListBuilder->addError( 'smw_emptysubquery' ); // not the right message, but this case is very rare; let us not make detailed messages for this
+ }
+ } else {
+ $this->querySegmentListBuilder->addError(
+ [ 'smw_concept_cache_miss', $concept->getDBkey() ]
+ );
+ }
+ } // else: no cache, no description (this may happen); treat like empty concept
+
+ $this->querySegmentListBuilder->getCircularReferenceGuard()->unmark( $hash );
+
+ return $query;
+ }
+
+ /**
+ * We bypass the storage interface here (which is legal as we control it,
+ * and safe if we are careful with changes ...)
+ *
+ * This should be faster, but we must implement the unescaping that concepts
+ * do on getWikiValue
+ */
+ private function getConceptForId( $db, $id ) {
+ return $db->selectRow(
+ 'smw_fpt_conc',
+ [ 'concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date' ],
+ [ 's_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * No defaultnamespaces here; If any, these are already in the concept.
+ * Unescaping is the same as in SMW_DV_Conept's getWikiValue().
+ */
+ private function getConceptQueryDescriptionFrom( $conceptQuery ) {
+
+ if ( $this->queryParser === null ) {
+ throw new RuntimeException( 'Missing a QueryParser instance' );
+ }
+
+ return $this->queryParser->getQueryDescription(
+ str_replace( [ '&lt;', '&gt;', '&amp;' ], [ '<', '>', '&' ], $conceptQuery )
+ );
+ }
+
+ private function findCircularDescription( $concept, &$description ) {
+
+ if ( $description instanceof ConceptDescription ) {
+ if ( $description->getConcept()->equals( $concept ) ) {
+ $this->querySegmentListBuilder->addError(
+ [ 'smw-query-condition-circular', $description->getQueryString() ]
+ );
+ return;
+ }
+ }
+
+ if ( $description instanceof Conjunction || $description instanceof Disjunction ) {
+ foreach ( $description->getDescriptions() as $desc ) {
+ $this->findCircularDescription( $concept, $desc );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreter.php
new file mode 100644
index 00000000..9f2286d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreter.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class DisjunctionConjunctionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof Conjunction || $description instanceof Disjunction;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+ $query->type = $description instanceof Conjunction ? QuerySegment::Q_CONJUNCTION : QuerySegment::Q_DISJUNCTION;
+
+ foreach ( $description->getDescriptions() as $subDescription ) {
+
+ $subQueryId = $this->querySegmentListBuilder->getQuerySegmentFrom( $subDescription );
+
+ if ( $subQueryId >= 0 ) {
+ $query->components[$subQueryId] = true;
+ }
+ }
+
+ // All subconditions failed, drop this as well.
+ if ( count( $query->components ) == 0 ) {
+ $query->type = QuerySegment::Q_NOQUERY;
+ }
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php
new file mode 100644
index 00000000..1bfe71a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/DispatchingDescriptionInterpreter.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DispatchingDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var DescriptionInterpreter[]
+ */
+ private $interpreters = [];
+
+ /**
+ * @var DescriptionInterpreter
+ */
+ private $defaultInterpreter = null;
+
+ /**
+ * @param Description $description
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+
+ foreach ( $this->interpreters as $interpreter ) {
+ if ( $interpreter->canInterpretDescription( $description ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param Description $description
+ *
+ * @return QuerySegment
+ * @throws InvalidArgumentException
+ */
+ public function interpretDescription( Description $description ) {
+
+ foreach ( $this->interpreters as $interpreter ) {
+ if ( $interpreter->canInterpretDescription( $description ) ) {
+ return $interpreter->interpretDescription( $description );
+ }
+ }
+
+ // Instead of throwing an exception we return a ThingDescriptionInterpreter
+ // for all unregistered/unknown descriptions
+ return $this->defaultInterpreter->interpretDescription( $description );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DescriptionInterpreter $defaultInterpreter
+ */
+ public function addInterpreter( DescriptionInterpreter $interpreter ) {
+ $this->interpreters[] = $interpreter;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param DescriptionInterpreter $defaultInterpreter
+ */
+ public function addDefaultInterpreter( DescriptionInterpreter $defaultInterpreter ) {
+ $this->defaultInterpreter = $defaultInterpreter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
new file mode 100644
index 00000000..b7b52d24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreter.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMWSql3SmwIds;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof NamespaceDescription;
+ }
+
+ /**
+ * TODO: One instance of the SMW IDs table on s_id always suffices (swm_id is KEY)! Doable in execution ... (PERFORMANCE)
+ *
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $db = $this->querySegmentListBuilder->getStore()->getConnection( 'mw.db.queryengine' );
+
+ $query = new QuerySegment();
+ $query->joinTable = SMWSql3SmwIds::TABLE_NAME;
+ $query->joinfield = "$query->alias.smw_id";
+ $query->where = "$query->alias.smw_namespace=" . $db->addQuotes( $description->getNamespace() );
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
new file mode 100644
index 00000000..39dfa3ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreter.php
@@ -0,0 +1,319 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use RuntimeException;
+use SMW\DataTypeRegistry;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\SQLStore\EntityStore\DataItemHandler;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMWDataItem as DataItem;
+use SMWSql3SmwIds;
+use SMWSQLStore3Table;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class SomePropertyInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var ComparatorMapper
+ */
+ private $comparatorMapper;
+
+ /**
+ * @var FulltextSearchTableFactory
+ */
+ private $fulltextSearchTableFactory;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->comparatorMapper = new ComparatorMapper();
+ $this->fulltextSearchTableFactory = new FulltextSearchTableFactory();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof SomeProperty;
+ }
+
+ /**
+ * @todo The case of nominal classes (top-level ValueDescription) still
+ * makes some assumptions about the table structure, especially about the
+ * name of the joinfield (o_id). Better extend
+ * compilePropertyValueDescription to deal with this case.
+ *
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+
+ $this->interpretPropertyConditionForDescription(
+ $query,
+ $description
+ );
+
+ return $query;
+ }
+
+ /**
+ * Modify the given query object to account for some property condition for
+ * the given property. If it is not possible to generate a query for the
+ * given data, the query type is changed to QueryContainer::Q_NOQUERY. Callers need
+ * to check for this and discard the query in this case.
+ *
+ * @note This method does not support sortkey (_SKEY) property queries,
+ * since they do not have a normal property table. This should not be a
+ * problem since comparators on sortkeys are supported indirectly when
+ * using comparators on wikipages. There is no reason to create any
+ * query with _SKEY ad users cannot do so either (no user label).
+ *
+ * @since 1.8
+ */
+ private function interpretPropertyConditionForDescription( QuerySegment $query, SomeProperty $description ) {
+
+ $db = $this->querySegmentListBuilder->getStore()->getConnection( 'mw.db.queryengine' );
+
+ $property = $description->getProperty();
+
+ $tableid = $this->querySegmentListBuilder->getStore()->findPropertyTableID( $property );
+
+ if ( $tableid === '' ) { // Give up
+ $query->type = QuerySegment::Q_NOQUERY;
+ return;
+ }
+
+ $proptables = $this->querySegmentListBuilder->getStore()->getPropertyTables();
+ $proptable = $proptables[$tableid];
+
+ if ( !$proptable->usesIdSubject() ) {
+ // no queries with such tables
+ // (only redirects are affected in practice)
+ $query->type = QuerySegment::Q_NOQUERY;
+ return;
+ }
+
+ $typeid = $property->findPropertyTypeID();
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $typeid );
+
+ if ( $property->isInverse() && $diType !== DataItem::TYPE_WIKIPAGE ) {
+ // can only invert properties that point to pages
+ $query->type = QuerySegment::Q_NOQUERY;
+ return;
+ }
+
+ $diHandler = $this->querySegmentListBuilder->getStore()->getDataItemHandlerForDIType( $diType );
+ $indexField = $diHandler->getIndexField();
+
+ // TODO: strictly speaking, the DB key is not what we want here,
+ // since sortkey is based on a "wiki value"
+ $sortkey = $property->getKey();
+
+ // *** Now construct the query ... ***//
+ $query->joinTable = $proptable->getName();
+ $query->depth = $description->getHierarchyDepth();
+
+ // *** Add conditions for selecting rows for this property ***//
+ if ( !$proptable->isFixedPropertyTable() ) {
+ $pid = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPropertyID( $property );
+
+ // Construct property hierarchy:
+ $pqid = QuerySegment::$qnum;
+ $pquery = new QuerySegment();
+ $pquery->type = QuerySegment::Q_PROP_HIERARCHY;
+ $pquery->joinfield = [ $pid ];
+ $pquery->depth = $description->getHierarchyDepth();
+ $query->components[$pqid] = "{$query->alias}.p_id";
+
+ $this->querySegmentListBuilder->addQuerySegment( $pquery );
+
+ // Alternative code without property hierarchies:
+ // $query->where = "{$query->alias}.p_id=" . $this->m_dbs->addQuotes( $pid );
+ } // else: no property column, no hierarchy queries
+
+ // *** Add conditions on the value of the property ***//
+ if ( $diType === DataItem::TYPE_WIKIPAGE ) {
+ $o_id = $indexField;
+ if ( $property->isInverse() ) {
+ $s_id = $o_id;
+ $o_id = 's_id';
+ } else {
+ $s_id = 's_id';
+ }
+ $query->joinfield = "{$query->alias}.{$s_id}";
+
+ // process page description like main query
+ $sub = $this->querySegmentListBuilder->getQuerySegmentFrom(
+ $description->getDescription()
+ );
+
+ if ( $sub >= 0 ) {
+ $subQuery = $this->querySegmentListBuilder->findQuerySegment(
+ $sub
+ );
+
+ $o_id = $subQuery->indexField !== '' ? $subQuery->indexField : $o_id;
+ $query->components[$sub] = "{$query->alias}.{$o_id}";
+ }
+
+ if ( array_key_exists( $sortkey, $this->querySegmentListBuilder->getSortKeys() ) ) {
+ // TODO: This SMW IDs table is possibly duplicated in the query.
+ // Example: [[has capital::!Berlin]] with sort=has capital
+ // Can we prevent that? (PERFORMANCE)
+ $query->from = ' INNER JOIN ' . $db->tableName( SMWSql3SmwIds::TABLE_NAME ) .
+ " AS ids{$query->alias} ON ids{$query->alias}.smw_id={$query->alias}.{$o_id}";
+ $query->sortfields[$sortkey] = "ids{$query->alias}.smw_sort";
+ }
+ } else { // non-page value description
+ $query->joinfield = "{$query->alias}.s_id";
+ $this->compilePropertyValueDescription( $query, $description->getDescription(), $proptable, $diHandler, 'AND' );
+ if ( array_key_exists( $sortkey, $this->querySegmentListBuilder->getSortKeys() ) ) {
+ $query->sortfields[$sortkey] = isset( $query->sortIndexField ) ? $query->sortIndexField : "{$query->alias}.{$indexField}";
+ }
+ }
+ }
+
+ /**
+ * Given an Description that is just a conjunction or disjunction of
+ * ValueDescription objects, create and return a plain WHERE condition
+ * string for it.
+ *
+ * @param $query
+ * @param Description $description
+ * @param SMWSQLStore3Table $proptable
+ * @param DataItemHandler $diHandler for that table
+ * @param string $operator SQL operator "AND" or "OR"
+ */
+ private function compilePropertyValueDescription(
+ $query, Description $description, SMWSQLStore3Table $proptable, DataItemHandler $diHandler, $operator ) {
+
+ if ( $description instanceof ValueDescription ) {
+ $this->mapValueDescription( $query, $description, $diHandler, $operator );
+ } elseif ( ( $description instanceof Conjunction ) ||
+ ( $description instanceof Disjunction ) ) {
+ $op = ( $description instanceof Conjunction ) ? 'AND' : 'OR';
+
+ // #556 ensure correct parentheses are applied for something
+ // like "(a OR b OR c) AND d AND e"
+ if ( $query->where && substr( $query->where, -1 ) != '(' ) {
+ $query->where .= " $operator ";
+ }
+
+ $query->where .= "(";
+
+ foreach ( $description->getDescriptions() as $subdesc ) {
+ $this->compilePropertyValueDescription( $query, $subdesc, $proptable, $diHandler, $op );
+ }
+
+ $query->where .= ")";
+
+ } elseif ( $description instanceof ThingDescription ) {
+ // nothing to do
+ } else {
+ throw new RuntimeException( "Cannot process this type of Description." );
+ }
+ }
+
+ /**
+ * Given an Description that is just a conjunction or disjunction of
+ * ValueDescription objects, create and return a plain WHERE condition
+ * string for it.
+ *
+ * @param $query
+ * @param ValueDescription $description
+ * @param DataItemHandler $diHandler for that table
+ * @param string $operator SQL operator "AND" or "OR"
+ */
+ private function mapValueDescription(
+ $query, ValueDescription $description, DataItemHandler $diHandler, $operator ) {
+
+ $where = '';
+ $dataItem = $description->getDataItem();
+ $db = $this->querySegmentListBuilder->getStore()->getConnection( 'mw.db.queryengine' );
+
+ $valueMatchConditionBuilder = $this->fulltextSearchTableFactory->newValueMatchConditionBuilderByType(
+ $this->querySegmentListBuilder->getStore()
+ );
+
+ // TODO Better get the handle from the property type
+ // Some comparators (e.g. LIKE) could use DI values of
+ // a different type; we care about the property table, not
+ // about the value
+
+ // Do not support smw_id joined data for now.
+ $indexField = $diHandler->getIndexField();
+
+ //Hack to get to the field used as index
+ $keys = $diHandler->getWhereConds( $dataItem );
+ $value = $keys[$indexField];
+
+ // See if the getSQLCondition method exists and call it if this is the case.
+ // Invoked by SMAreaValueDescription, SMGeoCoordsValueDescription
+ if ( method_exists( $description, 'getSQLCondition' ) ) {
+ $fields = $diHandler->getTableFields();
+
+ $where = $description->getSQLCondition(
+ $query->alias,
+ array_keys( $fields ),
+ $this->querySegmentListBuilder->getStore()->getConnection( DB_SLAVE )
+ );
+ }
+
+ if ( $where == '' && $valueMatchConditionBuilder->canApplyFulltextSearchMatchCondition( $description ) ) {
+ $query->joinTable = $valueMatchConditionBuilder->getTableName();
+ $query->sortIndexField = $valueMatchConditionBuilder->getSortIndexField( $query->alias );
+ $query->components = [];
+ $where = $valueMatchConditionBuilder->getWhereCondition( $description, $query->alias );
+ } elseif ( $where == '' ) {
+
+ $comparator = $this->comparatorMapper->mapComparator(
+ $description,
+ $value
+ );
+
+ $where = "$query->alias.{$indexField}{$comparator}" . $db->addQuotes( $value );
+ }
+
+ if ( $where !== '' ) {
+
+ if ( $query->where && substr( $query->where, -1 ) != '(' ) {
+ $query->where .= " $operator ";
+ }
+
+ $query->where .= "($where)";
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php
new file mode 100644
index 00000000..e4d3dde7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreter.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\Description;
+use SMW\Query\Language\ThingDescription;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ThingDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ThingDescription;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+ $query->type = QuerySegment::Q_NOQUERY;
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
new file mode 100644
index 00000000..0ea4ec12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreter.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIWikiPage;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\ValueDescription;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreter;
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMWDIBlob as DIBlob;
+use SMWSql3SmwIds;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class ValueDescriptionInterpreter implements DescriptionInterpreter {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var ComparatorMapper
+ */
+ private $comparatorMapper;
+
+ /**
+ * @var FulltextSearchTableFactory
+ */
+ private $fulltextSearchTableFactory;
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->comparatorMapper = new ComparatorMapper();
+ $this->fulltextSearchTableFactory = new FulltextSearchTableFactory();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function canInterpretDescription( Description $description ) {
+ return $description instanceof ValueDescription;
+ }
+
+ /**
+ * Only type '_wpg' objects can appear on query level (essentially as nominal classes)
+ *
+ * @since 2.2
+ *
+ * @param Description $description
+ *
+ * @return QuerySegment
+ */
+ public function interpretDescription( Description $description ) {
+
+ $query = new QuerySegment();
+
+ if ( !$description->getDataItem() instanceof DIWikiPage ) {
+ return $query;
+ }
+
+ $comparator = $description->getComparator();
+ $property = $description->getProperty();
+ $value = $description->getDataItem()->getSortKey();
+
+ // A simple value match using the `~~Foo` will initiate a fulltext
+ // search without being bound to a property allowing a broad match
+ // search
+ if ( ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) ) {
+
+ $fulltextSearchSupport = $this->addFulltextSearchCondition(
+ $description,
+ $query,
+ $comparator,
+ $value
+ );
+
+ if ( $fulltextSearchSupport ) {
+ return $query;
+ }
+ }
+
+ if ( $comparator === SMW_CMP_EQ ) {
+ $query->type = QuerySegment::Q_VALUE;
+
+ $oid = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID(
+ $description->getDataItem()->getDBkey(),
+ $description->getDataItem()->getNamespace(),
+ $description->getDataItem()->getInterwiki(),
+ $description->getDataItem()->getSubobjectName()
+ );
+
+ $query->joinfield = [ $oid ];
+ } else { // Join with SMW IDs table needed for other comparators (apply to title string).
+ $query->joinTable = SMWSql3SmwIds::TABLE_NAME;
+ $query->joinfield = "{$query->alias}.smw_id";
+
+ $comparator = $this->comparatorMapper->mapComparator(
+ $description,
+ $value
+ );
+
+ $db = $this->querySegmentListBuilder->getStore()->getConnection( 'mw.db.queryengine' );
+
+ $query->where = "{$query->alias}.smw_sortkey$comparator" . $db->addQuotes( $value );
+ }
+
+ return $query;
+ }
+
+ private function addFulltextSearchCondition( $description, $query, $comparator, &$value ) {
+
+ // Uses ~~ wide proximity?
+ $usesWidePromixity = false;
+
+ // If a remaining ~ is present then the user searched with a ~~ string
+ // where the Comparator already matched/removed the first one
+ if ( substr( $value, 0, 1 ) === '~' ) {
+ $value = substr( $value, 1 );
+ $usesWidePromixity = true;
+ }
+
+ // If it is not a wide proximity search and it doesn't have a property then
+ // don't try to match using the fulltext index (redirect [[~Foo]] to LIKE)
+ if ( !$usesWidePromixity && $description->getProperty() === null ) {
+ return false;
+ }
+
+ $valueMatchConditionBuilder = $this->fulltextSearchTableFactory->newValueMatchConditionBuilderByType(
+ $this->querySegmentListBuilder->getStore()
+ );
+
+ if ( !$valueMatchConditionBuilder->isEnabled() || !$valueMatchConditionBuilder->hasMinTokenLength( $value ) ) {
+ return false;
+ }
+
+ if ( !$usesWidePromixity && !$valueMatchConditionBuilder->canApplyFulltextSearchMatchCondition( $description ) ) {
+ return false;
+ }
+
+ $query->joinTable = $valueMatchConditionBuilder->getTableName();
+ $query->joinfield = "{$query->alias}.s_id";
+ $query->indexField = 's_id';
+ $query->components = [];
+
+ $query->where = $valueMatchConditionBuilder->getWhereCondition(
+ new ValueDescription( new DIBlob( $value ), null, $comparator ),
+ $query->alias
+ );
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/EngineOptions.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/EngineOptions.php
new file mode 100644
index 00000000..55558454
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/EngineOptions.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use SMW\Options;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EngineOptions extends Options {
+
+ /**
+ * @since 2.2
+ */
+ public function __construct() {
+ parent::__construct( [
+ 'smwgIgnoreQueryErrors' => $GLOBALS['smwgIgnoreQueryErrors'],
+ 'smwgQSortFeatures' => $GLOBALS['smwgQSortFeatures'],
+ 'smwgQFilterDuplicates' => $GLOBALS['smwgQFilterDuplicates']
+ ] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilder.php
new file mode 100644
index 00000000..9580e2d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilder.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use SMW\Query\Language\ValueDescription;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MySQLValueMatchConditionBuilder extends ValueMatchConditionBuilder {
+
+ /**
+ * @see ValueMatchConditionBuilder::canApplyFulltextSearchMatchCondition
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ *
+ * @return boolean
+ */
+ public function canApplyFulltextSearchMatchCondition( ValueDescription $description ) {
+
+ if ( !$this->isEnabled() ) {
+ return false;
+ }
+
+ if ( $description->getProperty() !== null && $this->isExemptedProperty( $description->getProperty() ) ) {
+ return false;
+ }
+
+ if ( !$this->searchTable->isValidByType( $description->getDataItem()->getDiType() ) ) {
+ return false;
+ }
+
+ $matchableText = $this->getMatchableTextFromDescription(
+ $description
+ );
+
+ $comparator = $description->getComparator();
+
+ if ( $matchableText && ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) ) {
+
+ // http://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
+ // innodb_ft_min_token_size and innodb_ft_max_token_size are used
+ // for InnoDB search indexes. ft_min_word_len and ft_max_word_len
+ // are used for MyISAM search indexes
+
+ // Don't count any wildcard
+ return $this->hasMinTokenLength( str_replace( '*', '', $matchableText ) );
+ }
+
+ return false;
+ }
+
+ /**
+ * @see ValueMatchConditionBuilder::getWhereCondition
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ * @param string $temporaryTable
+ *
+ * @return string
+ */
+ public function getWhereCondition( ValueDescription $description, $temporaryTable = '' ) {
+
+ $affix = '';
+ $matchableText = $this->getMatchableTextFromDescription(
+ $description
+ );
+
+ // Any query modifier? Take care of it before any tokenizer or ngrams
+ // distort the marker
+ if (
+ ( $pos = strrpos( $matchableText, '&BOL' ) ) !== false ||
+ ( $pos = strrpos( $matchableText, '&INL' ) ) !== false ||
+ ( $pos = strrpos( $matchableText, '&QEX' ) ) !== false ) {
+ $affix = mb_strcut( $matchableText, $pos );
+ $matchableText = str_replace( $affix, '', $matchableText );
+ }
+
+ $value = $this->textSanitizer->sanitize(
+ $matchableText,
+ true
+ );
+
+ $value .= $affix;
+
+ // A leading or trailing minus sign indicates that this word must not
+ // be present in any of the rows that are returned.
+ // InnoDB only supports leading minus signs.
+ if ( $description->getComparator() === SMW_CMP_NLKE ) {
+ $value = '-' . $value;
+ }
+
+ $temporaryTable = $temporaryTable !== '' ? $temporaryTable . '.' : '';
+ $column = $temporaryTable . $this->searchTable->getIndexField();
+
+ $property = $description->getProperty();
+ $propertyCondition = '';
+
+ // Full text is collected in a single table therefore limit the match
+ // process by adding the PID as an additional condition
+ if ( $property !== null ) {
+ $propertyCondition = 'AND ' . $temporaryTable . 'p_id=' . $this->searchTable->getIdByProperty( $property );
+ }
+
+ $querySearchModifier = $this->getQuerySearchModifier(
+ $value
+ );
+
+ return "MATCH($column) AGAINST (" . $this->searchTable->addQuotes( $value ) . " $querySearchModifier) $propertyCondition";
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string &$value
+ *
+ * @return string
+ */
+ public function getQuerySearchModifier( &$value ) {
+
+ // @see http://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
+ // "MySQL can perform boolean full-text searches using the IN BOOLEAN
+ // MODE modifier. With this modifier, certain characters have special
+ // meaning at the beginning or end of words ..."
+ if ( strpos( $value, '&BOL' ) !== false ) {
+ $value = str_replace( '&BOL', '', $value );
+ return 'IN BOOLEAN MODE';
+ }
+
+ if ( strpos( $value, '&INL' ) !== false ) {
+ $value = str_replace( '&INL', '', $value );
+ return 'IN NATURAL LANGUAGE MODE';
+ }
+
+ if ( strpos( $value, '&QEX' ) !== false ) {
+ $value = str_replace( '&QEX', '', $value );
+ return 'WITH QUERY EXPANSION';
+ }
+
+ return 'IN BOOLEAN MODE';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilder.php
new file mode 100644
index 00000000..5357da68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilder.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use SMW\Query\Language\ValueDescription;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SQLiteValueMatchConditionBuilder extends ValueMatchConditionBuilder {
+
+ /**
+ * @see ValueMatchConditionBuilder::canApplyFulltextSearchMatchCondition
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ *
+ * @return boolean
+ */
+ public function canApplyFulltextSearchMatchCondition( ValueDescription $description ) {
+
+ if ( !$this->isEnabled() ) {
+ return false;
+ }
+
+ if ( $description->getProperty() !== null && $this->isExemptedProperty( $description->getProperty() ) ) {
+ return false;
+ }
+
+ if ( !$this->searchTable->isValidByType( $description->getDataItem()->getDiType() ) ) {
+ return false;
+ }
+
+ $matchableText = $this->getMatchableTextFromDescription(
+ $description
+ );
+
+ $comparator = $description->getComparator();
+
+ if ( $matchableText && ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) ) {
+ return $this->hasMinTokenLength( str_replace( '*', '', $matchableText ) );
+ }
+
+ return false;
+ }
+
+ /**
+ * @see ValueMatchConditionBuilder::getWhereCondition
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ * @param string $temporaryTable
+ *
+ * @return string
+ */
+ public function getWhereCondition( ValueDescription $description, $temporaryTable = '' ) {
+
+ $matchableText = $this->getMatchableTextFromDescription(
+ $description
+ );
+
+ $value = $this->textSanitizer->sanitize(
+ $matchableText,
+ true
+ );
+
+ // A leading or trailing minus sign indicates that this word must not
+ // be present in any of the rows that are returned.
+ // InnoDB only supports leading minus signs.
+ if ( $description->getComparator() === SMW_CMP_NLKE ) {
+ $value = '-' . $value;
+ }
+
+ // Something like [[Has text::!~database]] will cause a
+ // "malformed MATCH expression" due to "An FTS query may not consist
+ // entirely of terms or term-prefix queries with unary "-" operators
+ // attached to them." and doing "NOT database" will result in an empty
+ // result set
+
+ $temporaryTable = $temporaryTable !== '' ? $temporaryTable . '.' : '';
+ $column = $temporaryTable . $this->searchTable->getIndexField();
+
+ $property = $description->getProperty();
+ $propertyCondition = '';
+
+ // Full text is collected in a single table therefore limit the match
+ // process by adding the PID as an additional condition
+ if ( $property !== null ) {
+ $propertyCondition = ' AND ' . $temporaryTable . 'p_id=' . $this->searchTable->addQuotes(
+ $this->searchTable->getIdByProperty( $property )
+ );
+ }
+
+ return $column . " MATCH " . $this->searchTable->addQuotes( $value ) . "$propertyCondition";
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTable.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTable.php
new file mode 100644
index 00000000..3a62be87
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTable.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\SQLStore;
+use SMWDataItem as DataItem;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTable {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var boolean
+ */
+ private $isEnabled = false;
+
+ /**
+ * @var integer
+ */
+ private $minTokenSize = 3;
+
+ /**
+ * @var integer
+ */
+ private $indexableDataTypes = 0;
+
+ /**
+ * @var array
+ */
+ private $propertyExemptionList = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ $this->connection = $store->getConnection( 'mw.db.queryengine' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyExemptionList
+ */
+ public function setPropertyExemptionList( array $propertyExemptionList ) {
+ $this->propertyExemptionList = array_flip(
+ str_replace( ' ', '_', $propertyExemptionList )
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $indexableDataTypes
+ */
+ public function setIndexableDataTypes( $indexableDataTypes ) {
+ $this->indexableDataTypes = $indexableDataTypes;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getPropertyExemptionList() {
+ return array_keys( $this->propertyExemptionList );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $id
+ *
+ * @return boolean
+ */
+ public function isExemptedPropertyById( $id ) {
+
+ $dataItem = $this->getDataItemById( $id );
+
+ if ( !$dataItem instanceof DIWikiPage || $dataItem->getDBKey() === '' ) {
+ return false;
+ }
+
+ try {
+ $property = DIProperty::newFromUserLabel(
+ $dataItem->getDBKey()
+ );
+ } catch( PredefinedPropertyLabelMismatchException $e ) {
+ // The property no longer exists (or is no longer available) therefore
+ // exempt it.
+ return true;
+ }
+
+ return $this->isExemptedProperty( $property );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function isExemptedProperty( DIProperty $property ) {
+
+ $dataItemTypeId = DataTypeRegistry::getInstance()->getDataItemId(
+ $property->findPropertyTypeID()
+ );
+
+ // Property does not belong to a valid type which means to be exempted
+ if ( !$this->isValidByType( $dataItemTypeId ) ) {
+ return true;
+ }
+
+ return isset( $this->propertyExemptionList[$property->getKey()] );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return boolean
+ */
+ public function isValidByType( $type ) {
+
+ $indexType = SMW_FT_NONE;
+
+ if ( $type === DataItem::TYPE_BLOB ) {
+ $indexType = SMW_FT_BLOB;
+ }
+
+ if ( $type === DataItem::TYPE_URI ) {
+ $indexType = SMW_FT_URI;
+ }
+
+ if ( $type === DataItem::TYPE_WIKIPAGE ) {
+ $indexType = SMW_FT_WIKIPAGE;
+ }
+
+ return ( $this->indexableDataTypes & $indexType ) != 0;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $enabled
+ */
+ public function setEnabled( $enabled ) {
+ $this->isEnabled = (bool)$enabled;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ return $this->isEnabled;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getTableName() {
+ return SQLStore::FT_SEARCH_TABLE;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getIndexField() {
+ return 'o_text';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getSortField() {
+ return 'o_sort';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer
+ */
+ public function getMinTokenSize() {
+ return $this->minTokenSize;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return integer $minTokenSize
+ */
+ public function setMinTokenSize( $minTokenSize ) {
+ $this->minTokenSize = (int)$minTokenSize;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $token
+ *
+ * @return boolean
+ */
+ public function hasMinTokenLength( $token ) {
+ return mb_strlen( $token ) >= $this->minTokenSize;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DIProperty $property
+ *
+ * @return integer
+ */
+ public function getIdByProperty( DIProperty $property ) {
+ return $this->store->getObjectIds()->getId( $property->getCanonicalDiWikiPage() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $id
+ *
+ * @return DIWikiPage|null
+ */
+ public function getDataItemById( $id ) {
+ return $this->store->getObjectIds()->getDataItemById( $id );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getPropertyTables() {
+ return $this->store->getPropertyTables();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public function addQuotes( $value ) {
+ return $this->connection->addQuotes( $value );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableRebuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableRebuilder.php
new file mode 100644
index 00000000..e4368052
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableRebuilder.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\DIProperty;
+use SMW\MediaWiki\Database;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTableRebuilder {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var SearchTableUpdater
+ */
+ private $searchTableUpdater;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var boolean
+ */
+ private $reportVerbose = false;
+
+ /**
+ * @var boolean
+ */
+ private $optimization = false;
+
+ /**
+ * @var array
+ */
+ private $skippedTables = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param SearchTableUpdater $searchTableUpdater
+ * @param Database $connection
+ */
+ public function __construct( Database $connection, SearchTableUpdater $searchTableUpdater ) {
+ $this->connection = $connection;
+ $this->searchTableUpdater = $searchTableUpdater;
+ $this->messageReporter = MessageReporterFactory::getInstance()->newNullMessageReporter();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SearchTable
+ */
+ public function getSearchTable() {
+ return $this->searchTableUpdater->getSearchTable();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $reportVerbose
+ */
+ public function reportVerbose( $reportVerbose ) {
+ $this->reportVerbose = (bool)$reportVerbose;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $optimization
+ */
+ public function requestOptimization( $optimization ) {
+ $this->optimization = (bool)$optimization;
+ }
+
+ /**
+ * @see RebuildFulltextSearchTable::execute
+ *
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function rebuild() {
+
+ if ( !$this->searchTableUpdater->isEnabled() ) {
+ return $this->reportMessage( "\n" . "FullText search indexing is not enabled or supported." ."\n" );
+ }
+
+ if ( $this->optimization ) {
+ return $this->doOptimize();
+ }
+
+ $this->doRebuild();
+
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function flushTable() {
+ if ( $this->searchTableUpdater->isEnabled() ) {
+ $this->searchTableUpdater->flushTable();
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getQualifiedTableList() {
+
+ $tableList = [];
+
+ if ( !$this->searchTableUpdater->isEnabled() ) {
+ return $tableList;
+ }
+
+ foreach ( $this->searchTableUpdater->getPropertyTables() as $proptable ) {
+
+ if ( !$this->getSearchTable()->isValidByType( $proptable->getDiType() ) ) {
+ continue;
+ }
+
+ $tableList[] = $proptable->getName();
+ }
+
+ return $tableList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $tableName
+ */
+ public function rebuildByTable( $tableName ) {
+ foreach ( $this->searchTableUpdater->getPropertyTables() as $proptable ) {
+ if ( $proptable->getName() === $tableName && $this->getSearchTable()->isValidByType( $proptable->getDiType() ) ) {
+ $this->doRebuildByPropertyTable( $proptable );
+ }
+ }
+ }
+
+ private function doOptimize() {
+
+ $this->reportMessage( "\nOptimization ...\n" );
+
+ $this->reportMessage(
+ "\nRunning table optimization (Depending on the SQL back-end " .
+ "\nthis operation may lock the table and suspend any inserts or" .
+ "\ndeletes during the process.)\n"
+ );
+
+ if ( $this->searchTableUpdater->optimize() ) {
+ $this->reportMessage( "\n ... optimization has finished.\n" );
+ } else {
+ $this->reportMessage( "\nThe SQL back-end does not support this operation.\n" );
+ }
+
+ return true;
+ }
+
+ private function doRebuild() {
+
+ $this->reportMessage(
+ "\nThe entire index table is going to be purged first and it may\n" .
+ "take a moment before the rebuild is completed due to varying\n" .
+ "table contents.\n"
+ );
+
+ $this->reportMessage( "\nIndex process ..." );
+ $this->reportMessage( "\n" . " ... purging the index table ..." );
+
+ $this->searchTableUpdater->flushTable();
+ $this->reportMessage( "\n" . " ... rebuilding (finished/expected) ..." );
+
+ foreach ( $this->searchTableUpdater->getPropertyTables() as $proptable ) {
+
+ // Only care for Blob/Uri tables
+ if ( !$this->getSearchTable()->isValidByType( $proptable->getDiType() ) ) {
+ $this->skippedTables[$proptable->getName()] = 'Not a valid DI type';
+ continue;
+ }
+
+ $this->doRebuildByPropertyTable( $proptable );
+ }
+
+ $this->reportMessage( "\n ... done." );
+ $this->reportMessage( "\n ... report unindexed table(s) ...", $this->reportVerbose );
+
+ foreach ( $this->skippedTables as $tableName => $reason ) {
+ $this->reportMessage( "\n". sprintf( "%-38s%s", " ... {$tableName}", $reason ), $this->reportVerbose );
+ }
+
+ $this->reportMessage( "\n" );
+ }
+
+ private function doRebuildByPropertyTable( $proptable ) {
+
+ $searchTable = $this->getSearchTable();
+
+ if ( $proptable->getDiType() === DataItem::TYPE_URI ) {
+ $fetchFields = [ 's_id', 'p_id', 'o_blob', 'o_serialized' ];
+ } elseif ( $proptable->getDiType() === DataItem::TYPE_WIKIPAGE ) {
+ $fetchFields = [ 's_id', 'p_id', 'o_id' ];
+ } else {
+ $fetchFields = [ 's_id', 'p_id', 'o_blob', 'o_hash' ];
+ }
+
+ $table = $proptable->getName();
+ $pid = '';
+
+ // Fixed tables don't have a p_id column therefore get it
+ // from the ID TABLE
+ if ( $proptable->isFixedPropertyTable() ) {
+ unset( $fetchFields[1] ); // p_id
+
+ $property = new DIProperty( $proptable->getFixedProperty() );
+
+ if ( $property->getLabel() === '' ) {
+ return $this->skippedTables[$table] = 'Fixed property, ' . $property->getKey() . ' is invalid';
+ }
+
+ $pid = $searchTable->getIdByProperty(
+ $property
+ );
+
+ if ( $searchTable->isExemptedPropertyById( $pid ) ) {
+ return $this->skippedTables[$table] = 'Fixed property table, belongs to exempted ' . $proptable->getFixedProperty() . ' property';
+ }
+ }
+
+ $rows = $this->connection->select(
+ $table,
+ $fetchFields,
+ [],
+ __METHOD__
+ );
+
+ if ( $rows === false || $rows === null ) {
+ return $this->skippedTables[$table] = 'Empty table';
+ }
+
+ $this->doRebuildFromRows( $searchTable, $table, $pid, $rows );
+ }
+
+ private function doRebuildFromRows( $searchTable, $table, $pid, $rows ) {
+
+ $i = 0;
+ $expected = $rows->numRows();
+
+ if ( $expected == 0 ) {
+ return $this->skippedTables[$table] = 'Empty table';
+ }
+
+ $this->reportMessage( "\n" );
+
+ foreach ( $rows as $row ) {
+ $i++;
+
+ $sid = $row->s_id;
+ $pid = !isset( $row->p_id ) ? $pid : $row->p_id;
+
+ $indexableText = $this->getIndexableTextFromRow(
+ $searchTable,
+ $row
+ );
+
+ if ( $searchTable->isExemptedPropertyById( $pid ) || !$searchTable->hasMinTokenLength( $indexableText ) ) {
+ continue;
+ }
+
+ $this->reportMessage(
+ "\r". sprintf( "%-38s%s", " ... {$table}", sprintf( "%4.0f%% (%s/%s)", ( $i / $expected ) * 100, $i, $expected ) )
+ );
+
+ $text = $this->searchTableUpdater->read( $sid, $pid );
+
+ // Unknown, so let's create the row
+ if ( $text === false ) {
+ $this->searchTableUpdater->insert( $sid, $pid );
+ }
+
+ $this->searchTableUpdater->update( $sid, $pid, trim( $text ) . ' ' . $indexableText );
+ }
+ }
+
+ private function reportMessage( $message, $verbose = true ) {
+ if ( $verbose ) {
+ $this->messageReporter->reportMessage( $message );
+ }
+ }
+
+ private function getIndexableTextFromRow( $searchTable, $row ) {
+
+ $indexableText = '';
+
+ // Page, Uri, or blob?
+ if ( isset( $row->o_id ) ) {
+ $dataItem = $searchTable->getDataItemById( $row->o_id );
+ $indexableText = $dataItem instanceof DataItem ? $dataItem->getSortKey() : '';
+ } elseif ( isset( $row->o_serialized ) ) {
+ $indexableText = $row->o_blob === null ? $row->o_serialized : $row->o_blob;
+ } elseif ( isset( $row->o_blob ) ) {
+ $indexableText = $row->o_blob;
+ } elseif ( isset( $row->o_hash ) ) {
+ $indexableText = $row->o_hash;
+ }
+
+ return trim( $indexableText );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableUpdater.php
new file mode 100644
index 00000000..a329d664
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/SearchTableUpdater.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use SMW\MediaWiki\Database;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTableUpdater {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var SearchTable
+ */
+ private $searchTable;
+
+ /**
+ * @var TextSanitizer
+ */
+ private $textSanitizer;
+
+ /**
+ * @since 2.5
+ *
+ * @param Database $connection
+ * @param SearchTable $searchTable
+ * @param TextSanitizer $textSanitizer
+ */
+ public function __construct( Database $connection, SearchTable $searchTable, TextSanitizer $textSanitizer ) {
+ $this->connection = $connection;
+ $this->searchTable = $searchTable;
+ $this->textSanitizer = $textSanitizer;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SearchTable
+ */
+ public function getSearchTable() {
+ return $this->searchTable;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ return $this->searchTable->isEnabled();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getPropertyTables() {
+ return $this->searchTable->getPropertyTables();
+ }
+
+ /**
+ * @see http://dev.mysql.com/doc/refman/5.7/en/fulltext-fine-tuning.html
+ * @see http://dev.mysql.com/doc/refman/5.7/en/optimize-table.html
+ *
+ * "Running OPTIMIZE TABLE on a table with a full-text index rebuilds the
+ * full-text index, removing deleted Document IDs and consolidating multiple
+ * entries for the same word, where possible."
+ *
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function optimize() {
+
+ if ( !$this->connection->isType( 'mysql' ) ) {
+ return false;
+ }
+
+ $this->connection->query(
+ "OPTIMIZE TABLE " . $this->searchTable->getTableName(),
+ __METHOD__
+ );
+
+ return true;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $sid
+ * @param integer $pid
+ *
+ * @return boolean
+ */
+ public function exists( $sid, $pid ) {
+
+ $row = $this->connection->selectRow(
+ $this->searchTable->getTableName(),
+ [ 's_id' ],
+ [
+ 's_id' => (int)$sid,
+ 'p_id' => (int)$pid
+ ],
+ __METHOD__
+ );
+
+ return $row !== false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $sid
+ * @param integer $pid
+ *
+ * @return false|string
+ */
+ public function read( $sid, $pid ) {
+ $row = $this->connection->selectRow(
+ $this->searchTable->getTableName(),
+ [ 'o_text' ],
+ [
+ 's_id' => (int)$sid,
+ 'p_id' => (int)$pid
+ ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ return false;
+ }
+
+ return $this->textSanitizer->sanitize( $row->o_text );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $sid
+ * @param integer $pid
+ * @param string $text
+ */
+ public function update( $sid, $pid, $text ) {
+
+ if ( trim( $text ) === '' || ( $indexableText = $this->textSanitizer->sanitize( $text ) ) === '' ) {
+ return $this->delete( $sid, $pid );
+ }
+
+ $this->connection->update(
+ $this->searchTable->getTableName(),
+ [
+ 'o_text' => $indexableText,
+ 'o_sort' => mb_substr( $text, 0, 32 )
+ ],
+ [
+ 's_id' => (int)$sid,
+ 'p_id' => (int)$pid
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $sid
+ * @param integer $pid
+ */
+ public function insert( $sid, $pid ) {
+ $this->connection->insert(
+ $this->searchTable->getTableName(),
+ [
+ 's_id' => (int)$sid,
+ 'p_id' => (int)$pid,
+ 'o_text' => ''
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $sid
+ * @param integer $pid
+ */
+ public function delete( $sid, $pid ) {
+ $this->connection->delete(
+ $this->searchTable->getTableName(),
+ [
+ 's_id' => (int)$sid,
+ 'p_id' => (int)$pid
+ ],
+ __METHOD__
+ );
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function flushTable() {
+ $this->connection->delete(
+ $this->searchTable->getTableName(),
+ '*',
+ __METHOD__
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextChangeUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextChangeUpdater.php
new file mode 100644
index 00000000..25e94d07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextChangeUpdater.php
@@ -0,0 +1,312 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use Onoi\Cache\Cache;
+use Psr\Log\LoggerAwareTrait;
+use SMW\MediaWiki\Database;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\SQLStore\ChangeOp\TableChangeOp;
+use SMW\Utils\Timer;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextChangeUpdater {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var SearchTableUpdater
+ */
+ private $searchTableUpdater;
+
+ /**
+ * @var boolean
+ */
+ private $asDeferredUpdate = true;
+
+ /**
+ * @var boolean
+ */
+ private $isCommandLineMode = false;
+
+ /**
+ * @var boolean
+ */
+ private $isPrimary = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param Database $connection
+ * @param Cache $cache
+ * @param SearchTableUpdater $searchTableUpdater
+ * @param TextSanitizer $textSanitizer
+ */
+ public function __construct( Database $connection, Cache $cache, SearchTableUpdater $searchTableUpdater ) {
+ $this->connection = $connection;
+ $this->cache = $cache;
+ $this->searchTableUpdater = $searchTableUpdater;
+ }
+
+ /**
+ * @note See comments in the DefaultSettings.php on the smwgFulltextDeferredUpdate setting
+ *
+ * @since 2.5
+ *
+ * @param boolean $asDeferredUpdate
+ */
+ public function asDeferredUpdate( $asDeferredUpdate ) {
+ $this->asDeferredUpdate = (bool)$asDeferredUpdate;
+ }
+
+ /**
+ * When running from commandLine, push updates directly to avoid overhead when
+ * it is known that within that mode transactions are FIFO (i.e. the likelihood
+ * for race conditions of unfinished updates are diminishable).
+ *
+ * @since 2.5
+ *
+ * @param boolean $isCommandLineMode
+ */
+ public function isCommandLineMode( $isCommandLineMode ) {
+ $this->isCommandLineMode = (bool)$isCommandLineMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isPrimary
+ */
+ public function isPrimary( $isPrimary ) {
+ $this->isPrimary = $isPrimary;
+ }
+
+ /**
+ * @see SMW::SQLStore::AfterDataUpdateComplete hook
+ *
+ * @since 2.5
+ *
+ * @param ChangeOp $changeOp
+ */
+ public function pushUpdates( ChangeOp $changeOp ) {
+
+ if ( !$this->searchTableUpdater->isEnabled() ) {
+ return;
+ }
+
+ Timer::start( __METHOD__ );
+
+ // Update within the same transaction as started by SMW::SQLStore::AfterDataUpdateComplete
+ if ( !$this->asDeferredUpdate || $this->isCommandLineMode || $this->isPrimary ) {
+ return $this->doUpdateFromChangeDiff( $changeOp->newChangeDiff() );
+ }
+
+ if ( !$this->canPostUpdate( $changeOp ) ) {
+ return;
+ }
+
+ $fulltextSearchTableUpdateJob = ApplicationFactory::getInstance()->newJobFactory()->newFulltextSearchTableUpdateJob(
+ $changeOp->getSubject()->getTitle(),
+ [
+ 'slot:id' => $changeOp->getSubject()->getHash()
+ ]
+ );
+
+ $fulltextSearchTableUpdateJob->lazyPush();
+
+ $this->logger->info(
+ [
+ 'Fulltext',
+ 'TextChangeUpdater',
+ 'Table update (as job) scheduled',
+ 'procTime in sec: {procTime}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 5 )
+ ]
+ );
+ }
+
+ /**
+ * @see SearchTableUpdateJob::run
+ *
+ * @since 2.5
+ *
+ * @param array|boolan $parameters
+ */
+ public function pushUpdatesFromJobParameters( $parameters ) {
+
+ if ( !$this->searchTableUpdater->isEnabled() || !isset( $parameters['slot:id'] ) || $parameters['slot:id'] === false ) {
+ return;
+ }
+
+ $subject = DIWikiPage::doUnserialize( $parameters['slot:id'] );
+ $changeDiff = ChangeDiff::fetch( $this->cache, $subject );
+
+ if ( $changeDiff !== false ) {
+ return $this->doUpdateFromChangeDiff( $changeDiff );
+ }
+
+ $this->logger->info(
+ [
+ 'Fulltext',
+ 'TextChangeUpdater',
+ 'Failed update (ChangeDiff) on {id}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'id' => $parameters['slot:id']
+ ]
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ChangeOp $changeOp
+ */
+ public function doUpdateFromChangeDiff( ChangeDiff $changeDiff ) {
+
+ if ( !$this->searchTableUpdater->isEnabled() ) {
+ return;
+ }
+
+ Timer::start( __METHOD__ );
+
+ $textItems = $changeDiff->getTextItems();
+ $diffChangeOps = $changeDiff->getTableChangeOps();
+
+ $changeList = $changeDiff->getChangeListByType( 'insert' );
+ $updates = [];
+
+ // Ensure that any delete operation is being accounted for to avoid that
+ // removed value annotation remain
+ if ( $diffChangeOps !== [] ) {
+ $this->doDeleteFromTableChangeOps( $diffChangeOps );
+ }
+
+ // Build a composite of replacements where a change occurred, this my
+ // contain some false positives
+ foreach ( $textItems as $sid => $textItem ) {
+
+ if ( !isset( $changeList[$sid] ) ) {
+ continue;
+ }
+
+ $this->collectUpdates( $sid, $textItem, $changeList, $updates );
+ }
+
+ foreach ( $updates as $key => $value ) {
+ list( $sid, $pid ) = explode( ':', $key, 2 );
+
+ if ( $this->searchTableUpdater->exists( $sid, $pid ) === false ) {
+ $this->searchTableUpdater->insert( $sid, $pid );
+ }
+
+ $this->searchTableUpdater->update(
+ $sid,
+ $pid,
+ $value
+ );
+ }
+
+ $this->logger->info(
+ [
+ 'Fulltext',
+ 'TextChangeUpdater',
+ 'Table update completed',
+ 'procTime in sec: {procTime}'
+ ],
+ [
+ 'method' => __METHOD__,
+ 'role' => 'developer',
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 5 )
+ ]
+ );
+ }
+
+ private function collectUpdates( $sid, array $textItem, $changeList, &$updates ) {
+
+ $searchTable = $this->searchTableUpdater->getSearchTable();
+
+ foreach ( $textItem as $pid => $text ) {
+
+ // Exempted property -> out
+ if ( $searchTable->isExemptedPropertyById( $pid ) ) {
+ continue;
+ }
+
+ $text = implode( ' ', $text );
+ $key = $sid . ':' . $pid;
+
+ $updates[$key] = !isset( $updates[$key] ) ? $text : $updates[$key] . ' ' . $text;
+ }
+ }
+
+ private function doDeleteFromTableChangeOps( array $tableChangeOps ) {
+ foreach ( $tableChangeOps as $tableChangeOp ) {
+ $this->doDeleteFromTableChangeOp( $tableChangeOp );
+ }
+ }
+
+ private function doDeleteFromTableChangeOp( TableChangeOp $tableChangeOp ) {
+
+ foreach ( $tableChangeOp->getFieldChangeOps( 'delete' ) as $fieldChangeOp ) {
+
+ // Replace s_id for subobjects etc. with the o_id
+ if ( $tableChangeOp->isFixedPropertyOp() ) {
+ $fieldChangeOp->set( 's_id', $fieldChangeOp->has( 'o_id' ) ? $fieldChangeOp->get( 'o_id' ) : $fieldChangeOp->get( 's_id' ) );
+ $fieldChangeOp->set( 'p_id', $tableChangeOp->getFixedPropertyValueBy( 'p_id' ) );
+ }
+
+ if ( !$fieldChangeOp->has( 'p_id' ) ) {
+ continue;
+ }
+
+ $this->searchTableUpdater->delete(
+ $fieldChangeOp->get( 's_id' ),
+ $fieldChangeOp->get( 'p_id' )
+ );
+ }
+ }
+
+ private function canPostUpdate( $changeOp ) {
+
+ $searchTable = $this->searchTableUpdater->getSearchTable();
+ $canPostUpdate = false;
+
+ // Find out whether we should actual initiate an update
+ foreach ( $changeOp->getChangedEntityIdSummaryList() as $id ) {
+ if ( ( $dataItem = $searchTable->getDataItemById( $id ) ) instanceof DIWikiPage && $dataItem->getNamespace() === SMW_NS_PROPERTY ) {
+ if ( !$searchTable->isExemptedPropertyById( $id ) ) {
+ $canPostUpdate = true;
+ break;
+ }
+ }
+ }
+
+ return $canPostUpdate;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextSanitizer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextSanitizer.php
new file mode 100644
index 00000000..54c54208
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/TextSanitizer.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use Onoi\Tesa\Normalizer;
+use Onoi\Tesa\Sanitizer;
+use Onoi\Tesa\SanitizerFactory;
+use Onoi\Tesa\Tokenizer\Tokenizer;
+use Onoi\Tesa\Transliterator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextSanitizer {
+
+ /**
+ * @var SanitizerFactory
+ */
+ private $sanitizerFactory;
+
+ /**
+ * @var array
+ */
+ private $languageDetection = [];
+
+ /**
+ * @var integer
+ */
+ private $minTokenSize = 3;
+
+ /**
+ * @since 2.5
+ *
+ * @param SanitizerFactory $sanitizerFactory
+ */
+ public function __construct( SanitizerFactory $sanitizerFactory ) {
+ $this->sanitizerFactory = $sanitizerFactory;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getVersions() {
+
+ $languageDetector = '(Disabled)';
+
+ if ( isset( $this->languageDetection['TextCatLanguageDetector'] ) ) {
+ $languageDetector = 'TextCatLanguageDetector (' . implode(', ', $this->languageDetection['TextCatLanguageDetector'] ) . ')';
+ }
+
+ return [
+ 'ICU (Intl) PHP-extension' => ( extension_loaded( 'intl' ) ? INTL_ICU_VERSION : '(Disabled)' ),
+ 'Tesa::Sanitizer' => Sanitizer::VERSION,
+ 'Tesa::Transliterator' => Transliterator::VERSION,
+ 'Tesa::LanguageDetector' => $languageDetector
+ ];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $languageDetection
+ */
+ public function setLanguageDetection( array $languageDetection ) {
+ $this->languageDetection = $languageDetection;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $minTokenSize
+ */
+ public function setMinTokenSize( $minTokenSize ) {
+ $this->minTokenSize = $minTokenSize;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ * @param boolean $isSearchTerm
+ *
+ * @return string
+ */
+ public function sanitize( $text, $isSearchTerm = false ) {
+ $start = microtime( true );
+ $text = rawurldecode( trim( $text ) );
+
+ $exemptionList = '';
+
+ // Those have special meaning when running a match search against
+ // the fulltext index (wildcard, phrase matching markers etc.)
+ if ( $isSearchTerm ) {
+ $exemptionList = [ '*', '"', '+', '-', '&', ',', '@', '~' ];
+ }
+
+ $sanitizer = $this->sanitizerFactory->newSanitizer( $text );
+ $sanitizer->toLowercase();
+ $sanitizer->applyTransliteration();
+ $sanitizer->convertDoubleWidth();
+
+ $sanitizer->replace(
+ [ 'http://', 'https://', 'mailto:', '%2A', '_', '&#x005B;', '&#91;', "\n", "\t" ],
+ [ '', '', '', '*', ' ', '[', '[', "", "" ]
+ );
+
+ $language = $this->predictLanguage( $text );
+
+ $sanitizer->setOption(
+ Sanitizer::WHITELIST,
+ $exemptionList
+ );
+
+ $sanitizer->setOption(
+ Sanitizer::MIN_LENGTH,
+ $this->minTokenSize
+ );
+
+ $tokenizer = $this->sanitizerFactory->newPreferredTokenizerByLanguage(
+ $text,
+ $language
+ );
+
+ $tokenizer->setOption(
+ Tokenizer::REGEX_EXEMPTION,
+ $exemptionList
+ );
+
+ $text = $sanitizer->sanitizeWith(
+ $tokenizer,
+ $this->sanitizerFactory->newStopwordAnalyzerByLanguage( $language ),
+ $this->sanitizerFactory->newSynonymizerByLanguage( $language )
+ );
+
+ // Remove possible spaces added by the tokenizer
+ $text = str_replace(
+ [ ' *', '* ', ' "', '" ', '+ ', '- ', '@ ', '~ ', '*+', '*-', '*~' ],
+ [ '*', '*', '"', '"', '+', '-', '@', '~' ,'* +', '* -', '* ~' ],
+ $text
+ );
+
+ //var_dump( $language, $text, (microtime( true ) - $start ) );
+ return $text;
+ }
+
+ private function predictLanguage( $text ) {
+
+ if ( $this->languageDetection === [] ) {
+ return null;
+ }
+
+ $languageDetector = $this->sanitizerFactory->newNullLanguageDetector();
+
+ if ( isset( $this->languageDetection['TextCatLanguageDetector'] ) ) {
+ $languageDetector = $this->sanitizerFactory->newTextCatLanguageDetector();
+ $languageDetector->setLanguageCandidates( $this->languageDetection['TextCatLanguageDetector'] );
+ }
+
+ return $languageDetector->detect(
+ Normalizer::reduceLengthTo( $text, 200 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilder.php
new file mode 100644
index 00000000..56e6fa7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilder.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ValueDescription;
+use SMWDIBlob as DIBlob;
+use SMWDIUri as DIUri;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ValueMatchConditionBuilder {
+
+ /**
+ * @var TextSanitizer
+ */
+ protected $textSanitizer;
+
+ /**
+ * @var SearchTable
+ */
+ protected $searchTable;
+
+ /**
+ * @since 2.5
+ *
+ * @param TextSanitizer $textSanitizer
+ * @param SearchTable $searchTable
+ */
+ public function __construct( TextSanitizer $textSanitizer, SearchTable $searchTable ) {
+ $this->textSanitizer = $textSanitizer;
+ $this->searchTable = $searchTable;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ return $this->searchTable->isEnabled();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getTableName() {
+ return $this->searchTable->getTableName();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $value
+ *
+ * @return boolean
+ */
+ public function hasMinTokenLength( $value ) {
+ return $this->searchTable->hasMinTokenLength( $value );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $property
+ *
+ * @return boolean
+ */
+ public function isExemptedProperty( DIProperty $property ) {
+ return $this->searchTable->isExemptedProperty( $property );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $temporaryTable
+ *
+ * @return string
+ */
+ public function getSortIndexField( $temporaryTable = '' ) {
+ return ( $temporaryTable !== '' ? $temporaryTable . '.' : '' ) . $this->searchTable->getSortField();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ *
+ * @return boolean
+ */
+ public function canApplyFulltextSearchMatchCondition( ValueDescription $description ) {
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param ValueDescription $description
+ * @param string $temporaryTable
+ *
+ * @return string
+ */
+ public function getWhereCondition( ValueDescription $description, $temporaryTable = '' ) {
+ return '';
+ }
+
+ protected function getMatchableTextFromDescription( ValueDescription $description ) {
+
+ $matchableText = false;
+
+ if ( $description->getDataItem() instanceof DIBlob ) {
+ $matchableText = $description->getDataItem()->getString();
+ }
+
+ if ( $description->getDataItem() instanceof DIUri || $description->getDataItem() instanceof DIWikiPage ) {
+ $matchableText = $description->getDataItem()->getSortKey();
+ }
+
+ return $matchableText;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/FulltextSearchTableFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/FulltextSearchTableFactory.php
new file mode 100644
index 00000000..1e7403f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/FulltextSearchTableFactory.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use Onoi\Tesa\SanitizerFactory;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\MySQLValueMatchConditionBuilder;
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTable;
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTableRebuilder;
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater;
+use SMW\SQLStore\QueryEngine\Fulltext\SQLiteValueMatchConditionBuilder;
+use SMW\SQLStore\QueryEngine\Fulltext\TextChangeUpdater;
+use SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer;
+use SMW\SQLStore\QueryEngine\Fulltext\ValueMatchConditionBuilder;
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableFactory {
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return ValueMatchConditionBuilder
+ */
+ public function newValueMatchConditionBuilderByType( Store $store ) {
+
+ $type = $store->getConnection( 'mw.db' )->getType();
+
+ switch ( $type ) {
+ case 'mysql':
+ return new MySQLValueMatchConditionBuilder(
+ $this->newTextSanitizer(),
+ $this->newSearchTable( $store )
+ );
+ break;
+ case 'sqlite':
+ return new SQLiteValueMatchConditionBuilder(
+ $this->newTextSanitizer(),
+ $this->newSearchTable( $store )
+ );
+ break;
+ }
+
+ return new ValueMatchConditionBuilder( $this->newTextSanitizer(), $this->newSearchTable( $store ) );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return SearchTable
+ */
+ public function newTextSanitizer() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $textSanitizer = new TextSanitizer(
+ new SanitizerFactory()
+ );
+
+ $textSanitizer->setLanguageDetection(
+ $settings->get( 'smwgFulltextLanguageDetection' )
+ );
+
+ $textSanitizer->setMinTokenSize(
+ $settings->get( 'smwgFulltextSearchMinTokenSize' )
+ );
+
+ return $textSanitizer;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return SearchTable
+ */
+ public function newSearchTable( Store $store ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $searchTable = new SearchTable(
+ $store
+ );
+
+ $searchTable->setEnabled(
+ $settings->get( 'smwgEnabledFulltextSearch' )
+ );
+
+ $searchTable->setPropertyExemptionList(
+ $settings->get( 'smwgFulltextSearchPropertyExemptionList' )
+ );
+
+ $searchTable->setMinTokenSize(
+ $settings->get( 'smwgFulltextSearchMinTokenSize' )
+ );
+
+ $searchTable->setIndexableDataTypes(
+ $settings->get( 'smwgFulltextSearchIndexableDataTypes' )
+ );
+
+ return $searchTable;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return SearchTableUpdater
+ */
+ public function newSearchTableUpdater( Store $store ) {
+ return new SearchTableUpdater(
+ $store->getConnection( 'mw.db' ),
+ $this->newSearchTable( $store ),
+ $this->newTextSanitizer()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return TextChangeUpdater
+ */
+ public function newTextChangeUpdater( Store $store ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+
+ $textChangeUpdater = new TextChangeUpdater(
+ $store->getConnection( 'mw.db' ),
+ $applicationFactory->getCache(),
+ $this->newSearchTableUpdater( $store )
+ );
+
+ $textChangeUpdater->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ $textChangeUpdater->asDeferredUpdate(
+ $settings->get( 'smwgFulltextDeferredUpdate' )
+ );
+
+ // https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
+ $textChangeUpdater->isCommandLineMode(
+ $GLOBALS['wgCommandLineMode']
+ );
+
+ return $textChangeUpdater;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return SearchTableRebuilder
+ */
+ public function newSearchTableRebuilder( Store $store ) {
+ return new SearchTableRebuilder(
+ $store->getConnection( 'mw.db' ),
+ $this->newSearchTableUpdater( $store )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/HierarchyTempTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/HierarchyTempTableBuilder.php
new file mode 100644
index 00000000..8f0edf37
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/HierarchyTempTableBuilder.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\TableBuilder\TemporaryTableBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class HierarchyTempTableBuilder {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var TemporaryTableBuilder
+ */
+ private $temporaryTableBuilder;
+
+ /**
+ * Cache of computed hierarchy queries for reuse ("catetgory/property value
+ * string" => "tablename").
+ *
+ * @var string[]
+ */
+ private $hierarchyCache = [];
+
+ /**
+ * @var array
+ */
+ private $hierarchyTypeTable = [];
+
+ /**
+ * @since 2.3
+ *
+ * @param Database $connection
+ * @param TemporaryTableBuilder $temporaryTableBuilder
+ */
+ public function __construct( Database $connection, TemporaryTableBuilder $temporaryTableBuilder ) {
+ $this->connection = $connection;
+ $this->temporaryTableBuilder = $temporaryTableBuilder;
+ }
+
+ /**
+ * @since 2.3
+ */
+ public function emptyHierarchyCache() {
+ $this->hierarchyCache = [];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return array
+ */
+ public function getHierarchyCache() {
+ return $this->hierarchyCache;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $table
+ * @param integer $depth
+ */
+ public function setPropertyHierarchyTableDefinition( $table, $depth ) {
+ $this->hierarchyTypeTable['property'] = [ $this->connection->tableName( $table ), $depth ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $table
+ * @param integer $depth
+ */
+ public function setClassHierarchyTableDefinition( $table, $depth ) {
+ $this->hierarchyTypeTable['class'] = [ $this->connection->tableName( $table ), $depth ];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $type
+ *
+ * @return array
+ * @throws RuntimeException
+ */
+ public function getHierarchyTableDefinitionForType( $type ) {
+
+ if ( !isset( $this->hierarchyTypeTable[$type] ) ) {
+ throw new RuntimeException( "$type is unknown" );
+ }
+
+ return $this->hierarchyTypeTable[$type];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param string $type
+ * @param string $tablename
+ * @param string $valueComposite
+ * @param integer|null $depth
+ *
+ * @throws RuntimeException
+ */
+ public function createHierarchyTempTableFor( $type, $tablename, $valueComposite, $depth = null ) {
+
+ $this->temporaryTableBuilder->create( $tablename );
+
+ list( $smwtable, $d ) = $this->getHierarchyTableDefinitionForType( $type );
+
+ if ( $depth === null ) {
+ $depth = $d;
+ }
+
+ if ( array_key_exists( $valueComposite, $this->hierarchyCache ) ) { // Just copy known result.
+
+ $this->connection->query(
+ "INSERT INTO $tablename (id) SELECT id" . ' FROM ' . $this->hierarchyCache[$valueComposite],
+ __METHOD__
+ );
+
+ return;
+ }
+
+ $this->buildTempTable( $tablename, $valueComposite, $smwtable, $depth );
+ }
+
+ /**
+ * @note we use two helper tables. One holds the results of each new iteration, one holds the
+ * results of the previous iteration. One could of course do with only the above result table,
+ * but then every iteration would use all elements of this table, while only the new ones
+ * obtained in the previous step are relevant. So this is a performance measure.
+ */
+ private function buildTempTable( $tablename, $values, $smwtable, $depth ) {
+
+ $db = $this->connection;
+
+ $tmpnew = 'smw_new';
+ $tmpres = 'smw_res';
+
+ $this->temporaryTableBuilder->create( $tmpnew );
+ $this->temporaryTableBuilder->create( $tmpres );
+
+ // Adding multiple values for the same column in sqlite is not supported
+ foreach ( explode( ',', $values ) as $value ) {
+
+ $db->query(
+ "INSERT " . "IGNORE" . " INTO $tablename (id) VALUES $value",
+ __METHOD__
+ );
+
+ $db->query(
+ "INSERT " . "IGNORE" . " INTO $tmpnew (id) VALUES $value",
+ __METHOD__
+ );
+ }
+
+ for ( $i = 0; $i < $depth; $i++ ) {
+ $db->query(
+ "INSERT " . 'IGNORE ' . "INTO $tmpres (id) SELECT s_id" . '@INT' . " FROM $smwtable, $tmpnew WHERE o_id=id",
+ __METHOD__
+ );
+
+ if ( $db->affectedRows() == 0 ) { // no change, exit loop
+ break;
+ }
+
+ $db->query(
+ "INSERT " . 'IGNORE ' . "INTO $tablename (id) SELECT $tmpres.id FROM $tmpres",
+ __METHOD__
+ );
+
+ if ( $db->affectedRows() == 0 ) { // no change, exit loop
+ break;
+ }
+
+ // empty "new" table
+ $db->query(
+ 'TRUNCATE TABLE ' . $tmpnew,
+ __METHOD__
+ );
+
+ $tmpname = $tmpnew;
+ $tmpnew = $tmpres;
+ $tmpres = $tmpname;
+ }
+
+ $this->hierarchyCache[$values] = $tablename;
+
+ $this->temporaryTableBuilder->drop( $tmpnew );
+ $this->temporaryTableBuilder->drop( $tmpres );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php
new file mode 100644
index 00000000..10e974a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DataValues\PropertyChainValue;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\Description;
+
+/**
+ * Modifies a given query object at $qid to account for all ordering conditions
+ * in the Query $query. It is always required that $qid is the id of a query
+ * that joins with the SMW ID_TABELE so that the field alias.smw_title is
+ * available for default sorting.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class OrderCondition {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var DescriptionFactory
+ */
+ private $descriptionFactory;
+
+ /**
+ * Array of sorting requests ("Property_name" => "ASC"/"DESC"). Used during query
+ * processing (where these property names are searched while compiling the query
+ * conditions).
+ *
+ * @var string[]
+ */
+ private $sortKeys = [];
+
+ /**
+ * @var boolean
+ */
+ private $isSupported = true;
+
+ /**
+ * @var boolean
+ */
+ private $asUnconditional = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->descriptionFactory = new DescriptionFactory();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $sortKeys
+ */
+ public function setSortKeys( $sortKeys ) {
+ $this->sortKeys = $sortKeys;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string[]
+ */
+ public function getSortKeys() {
+ return $this->sortKeys;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->querySegmentListBuilder->getErrors();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isSupported
+ */
+ public function isSupported( $isSupported ) {
+ $this->isSupported = $isSupported;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $asUnconditional
+ */
+ public function asUnconditional( $asUnconditional ) {
+ $this->asUnconditional = $asUnconditional;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $qid
+ *
+ * @return QuerySegment[]
+ */
+ public function apply( $qid ) {
+
+ if ( !$this->isSupported ) {
+ return $this->querySegmentListBuilder->getQuerySegmentList();
+ }
+
+ $querySegment = $this->querySegmentListBuilder->findQuerySegment(
+ $qid
+ );
+
+ $extraDescriptions = $this->collectExtraDescriptionsFromSortKeys(
+ $querySegment
+ );
+
+ if ( $extraDescriptions !== [] ) {
+ $this->addConjunctionFromExtraDescriptions( $querySegment, $extraDescriptions );
+ }
+
+ return $this->querySegmentListBuilder->getQuerySegmentList();
+ }
+
+ private function collectExtraDescriptionsFromSortKeys( $querySegment ) {
+
+ $extraDescriptions = [];
+
+ foreach ( $this->sortKeys as $label => $order ) {
+
+ if ( !is_string( $label ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( ( $description = $this->findExtraDescriptionBy( $querySegment, $label, $order ) ) instanceof Description ) {
+ $extraDescriptions[] = $description;
+ }
+ }
+
+ return $extraDescriptions;
+ }
+
+ private function findExtraDescriptionBy( $querySegment, $label, $order ) {
+
+ $description = null;
+
+ // Is assigned, leave ...
+ if ( array_key_exists( $label, $querySegment->sortfields ) ) {
+ return $description;
+ }
+
+ // Find missing property to sort by.
+ if ( $label === '' ) { // Sort by first result column (page titles).
+ $querySegment->sortfields[$label] = "$querySegment->alias.smw_sort";
+ } elseif ( $label === '#' ) { // Sort by first result column (page titles).
+ // PHP7 showed a rather erratic behaviour where in cases
+ // the sortkey contains the same string for comparison, the
+ // result returned from the DB was mixed in order therefore
+ // using # as indicator to search for additional fields if
+ // no specific property is given (see test cases in #1534)
+ $querySegment->sortfields[$label] = "$querySegment->alias.smw_sort,$querySegment->alias.smw_title,$querySegment->alias.smw_subobject";
+ } elseif ( PropertyChainValue::isChained( $label ) ) { // Try to extend query.
+ $propertyChainValue = DataValueFactory::getInstance()->newDataValueByType( PropertyChainValue::TYPE_ID );
+ $propertyChainValue->setUserValue( $label );
+
+ if ( !$propertyChainValue->isValid() ) {
+ return $description;
+ }
+
+ $lastDataItem = $propertyChainValue->getLastPropertyChainValue()->getDataItem();
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $lastDataItem,
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ // #2176, Set a different membership in case duplicate detection is
+ // enabled, the fingerprint will be distinguishable from a condition
+ // with another ThingDescription for the same property that would
+ // otherwise create a "Error: 1066 Not unique table/alias: 't3'"
+ $description->setMembership( $label );
+
+ foreach ( $propertyChainValue->getPropertyChainValues() as $val ) {
+ $description = $this->descriptionFactory->newSomeProperty(
+ $val->getDataItem(),
+ $description
+ );
+ }
+
+ // Add and replace Foo.Bar=asc with Bar=asc as we ultimately only
+ // order to the result of the last element
+ $this->sortKeys[$lastDataItem->getKey()] = $order;
+ unset( $this->sortKeys[$label] );
+ } else { // Try to extend query.
+ $sortprop = DataValueFactory::getInstance()->newPropertyValueByLabel( $label );
+
+ if ( $sortprop->isValid() ) {
+ $description = $this->descriptionFactory->newSomeProperty(
+ $sortprop->getDataItem(),
+ $this->descriptionFactory->newThingDescription()
+ );
+ }
+ }
+
+ return $description;
+ }
+
+ private function addConjunctionFromExtraDescriptions( $querySegment, array $extraDescriptions ) {
+
+ $this->querySegmentListBuilder->setSortKeys(
+ $this->sortKeys
+ );
+
+ $this->querySegmentListBuilder->getQuerySegmentFrom(
+ $this->descriptionFactory->newConjunction( $extraDescriptions )
+ );
+
+ // This is always an QuerySegment::Q_CONJUNCTION ...
+ $newQuerySegment = $this->querySegmentListBuilder->findQuerySegment(
+ $this->querySegmentListBuilder->getLastQuerySegmentId()
+ );
+
+ // ... so just re-wire its dependencies
+ foreach ( $newQuerySegment->components as $cid => $field ) {
+ $querySegment->components[$cid] = $querySegment->joinfield;
+
+ if ( $this->asUnconditional ) {
+ $this->querySegmentListBuilder->findQuerySegment( $cid )->joinType = 'LEFT OUTER';
+ }
+
+ $querySegment->sortfields = array_merge(
+ $querySegment->sortfields,
+ $this->querySegmentListBuilder->findQuerySegment( $cid )->sortfields
+ );
+ }
+
+ $this->querySegmentListBuilder->addQuerySegment( $querySegment );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QueryEngine.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QueryEngine.php
new file mode 100644
index 00000000..9f9c7b8c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QueryEngine.php
@@ -0,0 +1,573 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\Query\DebugFormatter;
+use SMW\Query\Language\ThingDescription;
+use SMW\QueryEngine as QueryEngineInterface;
+use SMW\QueryFactory;
+use SMWDataItem as DataItem;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+use SMWSQLStore3 as SQLStore;
+
+/**
+ * Class that implements query answering for SQLStore.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class QueryEngine implements QueryEngineInterface, LoggerAwareInterface {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * Query mode copied from given query. Some submethods act differently when
+ * in Query::MODE_DEBUG.
+ *
+ * @var int
+ */
+ private $queryMode;
+
+ /**
+ * Array of generated QuerySegment query descriptions (index => object)
+ *
+ * @var QuerySegment[]
+ */
+ private $querySegmentList = [];
+
+ /**
+ * Array of sorting requests ("Property_name" => "ASC"/"DESC"). Used during
+ * query processing (where these property names are searched while compiling
+ * the query conditions).
+ *
+ * @var string[]
+ */
+ private $sortKeys;
+
+ /**
+ * Local collection of error strings, passed on to callers if possible.
+ *
+ * @var string[]
+ */
+ private $errors = [];
+
+ /**
+ * @var QuerySegmentListBuildManager
+ */
+ private $querySegmentListBuildManager;
+
+ /**
+ * @var QuerySegmentListProcessor
+ */
+ private $querySegmentListProcessor;
+
+ /**
+ * @var EngineOptions
+ */
+ private $engineOptions;
+
+ /**
+ * @var QueryFactory
+ */
+ private $queryFactory;
+
+ /**
+ * @since 2.2
+ *
+ * @param SQLStore $store
+ * @param QuerySegmentListBuildManager $querySegmentListBuildManager
+ * @param QuerySegmentListProcessor $querySegmentListProcessor
+ * @param EngineOptions $engineOptions
+ */
+ public function __construct( SQLStore $store, QuerySegmentListBuildManager $querySegmentListBuildManager, QuerySegmentListProcessor $querySegmentListProcessor, EngineOptions $engineOptions ) {
+ $this->store = $store;
+ $this->querySegmentListBuildManager = $querySegmentListBuildManager;
+ $this->querySegmentListProcessor = $querySegmentListProcessor;
+ $this->engineOptions = $engineOptions;
+ $this->queryFactory = new QueryFactory();
+ }
+
+ /**
+ * @see LoggerAwareInterface::setLogger
+ *
+ * @since 2.5
+ *
+ * @param LoggerInterface $logger
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * The new SQL store's implementation of query answering. This function
+ * works in two stages: First, the nested conditions of the given query
+ * object are preprocessed to compute an abstract representation of the
+ * SQL query that is to be executed. Since query conditions correspond to
+ * joins with property tables in most cases, this abstract representation
+ * is essentially graph-like description of how property tables are joined.
+ * Moreover, this graph is tree-shaped, since all query conditions are
+ * tree-shaped. Each part of this abstract query structure is represented
+ * by an QuerySegment object in the array querySegmentList.
+ *
+ * As a second stage of processing, the thus prepared SQL query is actually
+ * executed. Typically, this means that the joins are collapsed into one
+ * SQL query to retrieve results. In some cases, such as in dbug mode, the
+ * execution might be restricted and not actually perform the whole query.
+ *
+ * The two-stage process helps to separate tasks, and it also allows for
+ * better optimisations: it is left to the execution engine how exactly the
+ * query result is to be obtained. For example, one could pre-compute
+ * partial suib-results in temporary tables (or even cache them somewhere),
+ * instead of passing one large join query to the DB (of course, it might
+ * be large only if the configuration of SMW allows it). For some DBMS, a
+ * step-wise execution of the query might lead to better performance, since
+ * it exploits the tree-structure of the joins, which is important for fast
+ * processing -- not all DBMS might be able in seeing this by themselves.
+ *
+ * @param Query $query
+ *
+ * @return mixed depends on $query->querymode
+ */
+ public function getQueryResult( Query $query ) {
+
+ if ( ( !$this->engineOptions->get( 'smwgIgnoreQueryErrors' ) || $query->getDescription() instanceof ThingDescription ) &&
+ $query->querymode != Query::MODE_DEBUG &&
+ count( $query->getErrors() ) > 0 ) {
+ return $this->queryFactory->newQueryResult( $this->store, $query, [], false );
+ // NOTE: we check this here to prevent unnecessary work, but we check
+ // it after query processing below again in case more errors occurred.
+ } elseif ( $query->querymode == Query::MODE_NONE || $query->getLimit() < 1 ) {
+ // don't query, but return something to printer
+ return $this->queryFactory->newQueryResult( $this->store, $query, [], true );
+ }
+
+ $connection = $this->store->getConnection( 'mw.db.queryengine' );
+
+ $this->queryMode = $query->querymode;
+ $this->querySegmentList = [];
+
+ $this->errors = [];
+ QuerySegment::$qnum = 0;
+ $this->sortKeys = $query->sortkeys;
+
+ $rootid = $this->querySegmentListBuildManager->getQuerySegmentFrom(
+ $query
+ );
+
+ $this->querySegmentList = $this->querySegmentListBuildManager->getQuerySegmentList();
+ $this->sortKeys = $this->querySegmentListBuildManager->getSortKeys();
+ $this->errors = $this->querySegmentListBuildManager->getErrors();
+
+ // Possibly stop if new errors happened:
+ if ( !$this->engineOptions->get( 'smwgIgnoreQueryErrors' ) &&
+ $query->querymode != Query::MODE_DEBUG &&
+ count( $this->errors ) > 0 ) {
+ $query->addErrors( $this->errors );
+ return $this->queryFactory->newQueryResult( $this->store, $query, [], false );
+ }
+
+ // *** Now execute the computed query ***//
+ $this->querySegmentListProcessor->setQueryMode( $this->queryMode );
+ $this->querySegmentListProcessor->setQuerySegmentList( $this->querySegmentList );
+
+ // execute query tree, resolve all dependencies
+ $this->querySegmentListProcessor->process(
+ $rootid
+ );
+
+ $this->applyExtraWhereCondition(
+ $connection,
+ $rootid
+ );
+
+ // #835
+ // SELECT DISTINCT and ORDER BY RANDOM causes an issue for postgres
+ // Disable RANDOM support for postgres
+ if ( $connection->isType( 'postgres' ) ) {
+ $this->engineOptions->set(
+ 'smwgQSortFeatures',
+ $this->engineOptions->get( 'smwgQSortFeatures' ) & ~SMW_QSORT_RANDOM
+ );
+ }
+
+ switch ( $query->querymode ) {
+ case Query::MODE_DEBUG:
+ $result = $this->getDebugQueryResult( $query, $rootid );
+ break;
+ case Query::MODE_COUNT:
+ $result = $this->getCountQueryResult( $query, $rootid );
+ break;
+ default:
+ $result = $this->getInstanceQueryResult( $query, $rootid );
+ break;
+ }
+
+ $this->querySegmentListProcessor->cleanUp();
+ $query->addErrors( $this->errors );
+
+ return $result;
+ }
+
+ /**
+ * Using a preprocessed internal query description referenced by $rootid, compute
+ * the proper debug output for the given query.
+ *
+ * @param Query $query
+ * @param integer $rootid
+ *
+ * @return string
+ */
+ private function getDebugQueryResult( Query $query, $rootid ) {
+
+ $qobj = $this->querySegmentList[$rootid];
+ $entries = [];
+
+ $sqlOptions = $this->getSQLOptions( $query, $rootid );
+
+ $entries['SQL Query'] = '';
+ $entries['SQL Explain'] = '';
+
+ $this->doExecuteDebugQueryResult( $qobj, $sqlOptions, $entries );
+ $auxtables = '';
+
+ foreach ( $this->querySegmentListProcessor->getExecutedQueries() as $table => $log ) {
+ $auxtables .= "<li>Temporary table $table";
+ foreach ( $log as $q ) {
+ $auxtables .= "<br />&#160;&#160;<tt>$q</tt>";
+ }
+ $auxtables .= '</li>';
+ }
+
+ if ( $auxtables ) {
+ $entries['Auxilliary Tables'] = "<ul>$auxtables</ul>";
+ } else {
+ $entries['Auxilliary Tables'] = 'No auxilliary tables used.';
+ }
+
+ return DebugFormatter::getStringFrom( 'SQLStore', $entries, $query );
+ }
+
+ private function doExecuteDebugQueryResult( $qobj, $sqlOptions, &$entries ) {
+
+ if ( !isset( $qobj->joinfield ) || $qobj->joinfield === '' ) {
+ return $entries['SQL Query'] = 'Empty result, no SQL query created.';
+ }
+
+ $connection = $this->store->getConnection( 'mw.db.queryengine' );
+ list( $startOpts, $useIndex, $tailOpts ) = $connection->makeSelectOptions( $sqlOptions );
+
+ $sortfields = implode( $qobj->sortfields, ',' );
+ $sortfields = $sortfields ? ', ' . $sortfields : '';
+
+ $format = DebugFormatter::getFormat(
+ $connection->getType()
+ );
+
+ $sql = "SELECT DISTINCT ".
+ "$qobj->alias.smw_id AS id," .
+ "$qobj->alias.smw_title AS t," .
+ "$qobj->alias.smw_namespace AS ns," .
+ "$qobj->alias.smw_iw AS iw," .
+ "$qobj->alias.smw_subobject AS so," .
+ "$qobj->alias.smw_sortkey AS sortkey" .
+ "$sortfields " .
+ "FROM " .
+ $connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from .
+ ( $qobj->where === '' ? '':' WHERE ' ) . $qobj->where . "$tailOpts $startOpts $useIndex ".
+ "LIMIT " . $sqlOptions['LIMIT'] . ' ' .
+ "OFFSET " . $sqlOptions['OFFSET'];
+
+ $res = $connection->query(
+ "EXPLAIN $format $sql",
+ __METHOD__
+ );
+
+ $entries['SQL Explain'] = DebugFormatter::prettifyExplain( $connection->getType(), $res );
+ $entries['SQL Query'] = DebugFormatter::prettifySql( $sql, $qobj->alias );
+
+ $connection->freeResult( $res );
+ }
+
+ /**
+ * Using a preprocessed internal query description referenced by $rootid, compute
+ * the proper counting output for the given query.
+ *
+ * @param Query $query
+ * @param integer $rootid
+ *
+ * @return integer
+ */
+ private function getCountQueryResult( Query $query, $rootid ) {
+
+ $queryResult = $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ [],
+ false
+ );
+
+ $queryResult->setCountValue( 0 );
+
+ $qobj = $this->querySegmentList[$rootid];
+
+ if ( $qobj->joinfield === '' ) { // empty result, no query needed
+ return $queryResult;
+ }
+
+ $connection = $this->store->getConnection( 'mw.db.queryengine' );
+
+ $sql_options = [ 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() ];
+
+ $res = $connection->select(
+ $connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from,
+ "COUNT(DISTINCT $qobj->alias.smw_id) AS count",
+ $qobj->where,
+ __METHOD__,
+ $sql_options
+ );
+
+ $row = $connection->fetchObject( $res );
+ $count = 0;
+
+ if ( $row !== false ) {
+ $count = $row->count;
+ }
+
+ $connection->freeResult( $res );
+
+ $queryResult->setCountValue( $count );
+
+ return $queryResult;
+ }
+
+ /**
+ * Using a preprocessed internal query description referenced by $rootid,
+ * compute the proper result instance output for the given query.
+ * @todo The SQL standard requires us to select all fields by which we sort, leading
+ * to wrong results regarding the given limit: the user expects limit to be applied to
+ * the number of distinct pages, but we can use DISTINCT only to whole rows. Thus, if
+ * rows contain sortfields, then pages with multiple values for that field are distinct
+ * and appear multiple times in the result. Filtering duplicates in post processing
+ * would still allow such duplicates to push aside wanted values, leading to less than
+ * "limit" results although there would have been "limit" really distinct results. For
+ * this reason, we select sortfields only for POSTGRES. MySQL is able to perform what
+ * we want here. It would be nice if we could eliminate the bug in POSTGRES as well.
+ *
+ * @param Query $query
+ * @param integer $rootid
+ *
+ * @return QueryResult
+ */
+ private function getInstanceQueryResult( Query $query, $rootid ) {
+
+ $connection = $this->store->getConnection( 'mw.db.queryengine' );
+ $qobj = $this->querySegmentList[$rootid];
+
+ // Empty result, no query needed
+ if ( $qobj->joinfield === '' ) {
+ return $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ [],
+ false
+ );
+ }
+
+ $sql_options = $this->getSQLOptions( $query, $rootid );
+
+ // Selecting those is required in standard SQL (but MySQL does not require it).
+ $sortfields = implode( $qobj->sortfields, ',' );
+ $sortfields = $sortfields ? ',' . $sortfields : '';
+
+ $res = $connection->select(
+ $connection->tableName( $qobj->joinTable ) . " AS $qobj->alias" . $qobj->from,
+ "DISTINCT ".
+ "$qobj->alias.smw_id AS id," .
+ "$qobj->alias.smw_title AS t," .
+ "$qobj->alias.smw_namespace AS ns," .
+ "$qobj->alias.smw_iw AS iw," .
+ "$qobj->alias.smw_subobject AS so," .
+ "$qobj->alias.smw_sortkey AS sortkey" .
+ "$sortfields",
+ $qobj->where,
+ __METHOD__,
+ $sql_options
+ );
+
+ $results = [];
+ $dataItemCache = [];
+
+ $logToTable = [];
+ $hasFurtherResults = false;
+
+ // Number of fetched results ( != number of valid results in
+ // array $results)
+ $count = 0;
+ $missedCount = 0;
+
+ $diHandler = $this->store->getDataItemHandlerForDIType(
+ DataItem::TYPE_WIKIPAGE
+ );
+
+ while ( ( $count < $query->getLimit() ) && ( $row = $connection->fetchObject( $res ) ) ) {
+ if ( $row->iw === '' || $row->iw{0} != ':' ) {
+
+ // Catch exception for non-existing predefined properties that
+ // still registered within non-updated pages (@see bug 48711)
+ try {
+ $dataItem = $diHandler->dataItemFromDBKeys( [
+ $row->t,
+ intval( $row->ns ),
+ $row->iw,
+ '',
+ $row->so
+ ] );
+
+ // Register the ID in an event the post-proceesing
+ // fails (namespace no longer valid etc.)
+ $dataItem->setId( $row->id );
+ } catch ( PredefinedPropertyLabelMismatchException $e ) {
+ $logToTable[$row->t] = "issue creating a {$row->t} dataitem from a database row";
+ $this->log( __METHOD__ . ' ' . $e->getMessage() );
+ $dataItem = '';
+ }
+
+ if ( $dataItem instanceof DIWikiPage && !isset( $dataItemCache[$dataItem->getHash()] ) ) {
+ $count++;
+ $dataItemCache[$dataItem->getHash()] = true;
+ $results[] = $dataItem;
+ // These IDs are usually needed for displaying the page (esp. if more property values are displayed):
+ $this->store->smwIds->setCache( $row->t, $row->ns, $row->iw, $row->so, $row->id, $row->sortkey );
+ } else {
+ $missedCount++;
+ $logToTable[$row->t] = "skip result for {$row->t} existing cache entry / query " . $query->getHash();
+ }
+ } else {
+ $missedCount++;
+ $logToTable[$row->t] = "skip result for {$row->t} due to an internal `{$row->iw}` pointer / query " . $query->getHash();
+ }
+ }
+
+ if ( $connection->fetchObject( $res ) ) {
+ $count++;
+ }
+
+ if ( $logToTable !== [] ) {
+ $this->log( __METHOD__ . ' ' . implode( ',', $logToTable ) );
+ }
+
+ if ( $count > $query->getLimit() || ( $count + $missedCount ) > $query->getLimit() ) {
+ $hasFurtherResults = true;
+ };
+
+ $connection->freeResult( $res );
+
+ $queryResult = $this->queryFactory->newQueryResult(
+ $this->store,
+ $query,
+ $results,
+ $hasFurtherResults
+ );
+
+ return $queryResult;
+ }
+
+ private function applyExtraWhereCondition( $connection, $qid ) {
+
+ if ( !isset( $this->querySegmentList[$qid] ) ) {
+ return null;
+ }
+
+ $qobj = $this->querySegmentList[$qid];
+
+ // Filter elements that should never appear in a result set
+ $extraWhereCondition = [
+ 'del' => "$qobj->alias.smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ) . " AND $qobj->alias.smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWDELETEIW ),
+ 'redi' => "$qobj->alias.smw_iw!=" . $connection->addQuotes( SMW_SQL3_SMWREDIIW )
+ ];
+
+ if ( strpos( $qobj->where, SMW_SQL3_SMWIW_OUTDATED ) === false ) {
+ $qobj->where .= $qobj->where === '' ? $extraWhereCondition['del'] : " AND " . $extraWhereCondition['del'];
+ }
+
+ if ( strpos( $qobj->where, SMW_SQL3_SMWREDIIW ) === false ) {
+ $qobj->where .= $qobj->where === '' ? $extraWhereCondition['redi'] : " AND " . $extraWhereCondition['redi'];
+ }
+
+ $this->querySegmentList[$qid] = $qobj;
+ }
+
+ /**
+ * Get a SQL option array for the given query and preprocessed query object at given id.
+ *
+ * @param Query $query
+ * @param integer $rootId
+ *
+ * @return array
+ */
+ private function getSQLOptions( Query $query, $rootId ) {
+
+ $result = [
+ 'LIMIT' => $query->getLimit() + 5,
+ 'OFFSET' => $query->getOffset()
+ ];
+
+ if ( !$this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT ) ) {
+ return $result;
+ }
+
+ // Build ORDER BY options using discovered sorting fields.
+ $qobj = $this->querySegmentList[$rootId];
+
+ foreach ( $this->sortKeys as $propkey => $order ) {
+
+ if ( !is_string( $propkey ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $qobj->sortfields ) ) { // Field was successfully added.
+
+ $list = $qobj->sortfields[$propkey];
+
+ // Contains a compound list of sortfields without order?
+ if ( strpos( $list, ',' ) !== false && strpos( $list, $order ) === false ) {
+ $list = str_replace( ',', " $order,", $list );
+ }
+
+ $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . $list . " $order ";
+ } elseif ( ( $order == 'RANDOM' ) && $this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT_RANDOM ) ) {
+ $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . ' RAND() ';
+ }
+ }
+
+ return $result;
+ }
+
+ private function log( $message, $context = [] ) {
+
+ if ( $this->logger === null ) {
+ return;
+ }
+
+ $this->logger->info( $message, $context );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegment.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegment.php
new file mode 100644
index 00000000..ce176410
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegment.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+/**
+ * Class for representing a single (sub)query description.
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ */
+class QuerySegment {
+
+ /**
+ * Type of empty query without usable condition, dropped as soon as
+ * discovered. This is used only during preparing the query (no
+ * queries of this type should ever be added).
+ */
+ const Q_NOQUERY = 0;
+
+ /**
+ * Type of query that is a join with a query (jointable: internal
+ * table name; joinfield/components/where use alias.fields;
+ * from uses external table names, components interpreted
+ * conjunctively (JOIN)).
+ */
+ const Q_TABLE = 1;
+
+ /**
+ * Type of query that matches a constant value (joinfield is a
+ * disjunctive array of unquoted values, jointable empty, components
+ * empty).
+ */
+ const Q_VALUE = 2;
+
+ /**
+ * Type of query that is a disjunction of other queries
+ * (joinfield/jointable empty; only components relevant)
+ */
+ const Q_DISJUNCTION = 3;
+
+ /**
+ * Type of query that is a conjunction of other queries
+ * (joinfield/jointable empty; only components relevant).
+ */
+ const Q_CONJUNCTION = 4;
+
+ /**
+ * Type of query that creates a temporary table of all superclasses
+ * of given classes (only joinfield relevant: (disjunctive) array of
+ * unquoted values).
+ */
+ const Q_CLASS_HIERARCHY = 5;
+
+ /**
+ * Type of query that creates a temporary table of all superproperties
+ * of given properties (only joinfield relevant: (disjunctive) array
+ * of unquoted values).
+ */
+ const Q_PROP_HIERARCHY = 6;
+
+ /**
+ * @var integer
+ */
+ public $type = self::Q_TABLE;
+
+ /**
+ * @var integer|null
+ */
+ public $depth;
+
+ /**
+ * @var string
+ */
+ public $fingerprint = '';
+
+ /**
+ * @var boolean
+ */
+ public $null = false;
+
+ /**
+ * @var boolean
+ */
+ public $not = false;
+
+ /**
+ * @var string
+ */
+ public $joinType = '';
+
+ /**
+ * @var string
+ */
+ public $joinTable = '';
+
+ /**
+ * @var string|array
+ */
+ public $joinfield = '';
+
+ /**
+ * Allows to define an index field, for example in case when a sub-query rewires
+ * a match condition.
+ *
+ * @var string
+ */
+ public $indexField = '';
+
+ /**
+ * @var string
+ */
+ public $from = '';
+
+ /**
+ * @var string
+ */
+ public $where = '';
+
+ /**
+ * @var string[]
+ */
+ public $components = [];
+
+ /**
+ * The alias to be used for jointable; read-only after construct!
+ * @var string
+ */
+ public $alias;
+
+ /**
+ * property dbkey => db field; passed down during query execution.
+ * @var string[]
+ */
+ public $sortfields = [];
+
+ /**
+ * @var integer
+ */
+ public $queryNumber;
+
+ /**
+ * @var integer
+ */
+ public static $qnum = 0;
+
+ /**
+ * @since 2.2
+ */
+ public function __construct() {
+ $this->queryNumber = self::$qnum;
+ $this->alias = 't' . self::$qnum;
+ self::$qnum++;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function reset() {
+ self::$qnum = 0;
+
+ $this->queryNumber = self::$qnum;
+ $this->alias = 't' . self::$qnum;
+ self::$qnum++;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuildManager.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuildManager.php
new file mode 100644
index 00000000..fd9c8577
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuildManager.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\SQLStore;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class QuerySegmentListBuildManager {
+
+ /**
+ * @var QuerySegment[]
+ */
+ private $querySegmentList = [];
+
+ /**
+ * @var string[]
+ */
+ private $errors = [];
+
+ /**
+ * @var string[]
+ */
+ private $sortKeys;
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var OrderCondition
+ */
+ private $orderCondition;
+
+ /**
+ * @since 2.5
+ *
+ * @param Database $connection
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ * @param OrderCondition $orderCondition
+ */
+ public function __construct( Database $connection, QuerySegmentListBuilder $querySegmentListBuilder, OrderCondition $orderCondition ) {
+ $this->connection = $connection;
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->orderCondition = $orderCondition;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string[]
+ */
+ public function getSortKeys() {
+ return $this->sortKeys;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getQuerySegmentList() {
+ return $this->querySegmentList;
+ }
+
+ /**
+ * Compute abstract representation of the query (compilation)
+ *
+ * @param Query $query
+ *
+ * @return integer
+ */
+ public function getQuerySegmentFrom( Query $query ) {
+
+ $this->sortKeys = $query->sortkeys;
+
+ // Anchor IT_TABLE as root element
+ $rootSegmentNumber = QuerySegment::$qnum;
+ $rootSegment = new QuerySegment();
+ $rootSegment->joinTable = SQLStore::ID_TABLE;
+ $rootSegment->joinfield = "$rootSegment->alias.smw_id";
+
+ $this->querySegmentListBuilder->addQuerySegment(
+ $rootSegment
+ );
+
+ $this->querySegmentListBuilder->setSortKeys(
+ $this->sortKeys
+ );
+
+ // compile query, build query "plan"
+ $this->querySegmentListBuilder->getQuerySegmentFrom(
+ $query->getDescription()
+ );
+
+ $qid = $this->querySegmentListBuilder->getLastQuerySegmentId();
+ $this->querySegmentList = $this->querySegmentListBuilder->getQuerySegmentList();
+ $this->errors = $this->querySegmentListBuilder->getErrors();
+
+ // no valid/supported condition; ensure that at least only proper pages
+ // are delivered
+ if ( $qid < 0 ) {
+ $qid = $rootSegmentNumber;
+ $qobj = $this->querySegmentList[$rootSegmentNumber];
+ $qobj->where = "$qobj->alias.smw_iw!=" . $this->connection->addQuotes( SMW_SQL3_SMWIW_OUTDATED ) .
+ " AND $qobj->alias.smw_iw!=" . $this->connection->addQuotes( SMW_SQL3_SMWREDIIW ) .
+ " AND $qobj->alias.smw_iw!=" . $this->connection->addQuotes( SMW_SQL3_SMWBORDERIW ) .
+ " AND $qobj->alias.smw_iw!=" . $this->connection->addQuotes( SMW_SQL3_SMWINTDEFIW );
+ $this->querySegmentListBuilder->addQuerySegment( $qobj );
+ }
+
+ if ( isset( $this->querySegmentList[$qid]->joinTable ) && $this->querySegmentList[$qid]->joinTable != SQLStore::ID_TABLE ) {
+ // manually make final root query (to retrieve namespace,title):
+ $rootid = $rootSegmentNumber;
+ $qobj = $this->querySegmentList[$rootSegmentNumber];
+ $qobj->components = [ $qid => "$qobj->alias.smw_id" ];
+ $qobj->sortfields = $this->querySegmentList[$qid]->sortfields;
+ $this->querySegmentListBuilder->addQuerySegment( $qobj );
+ } else { // not such a common case, but worth avoiding the additional inner join:
+ $rootid = $qid;
+ }
+
+ $this->orderCondition->setSortKeys(
+ $this->sortKeys
+ );
+
+ // Include order conditions (may extend query if needed for sorting):
+ $this->querySegmentList = $this->orderCondition->apply(
+ $rootid
+ );
+
+ $this->sortKeys = $this->orderCondition->getSortKeys();
+ $this->errors = $this->orderCondition->getErrors();
+
+ return $rootid;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuilder.php
new file mode 100644
index 00000000..dbe02754
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListBuilder.php
@@ -0,0 +1,276 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use InvalidArgumentException;
+use OutOfBoundsException;
+use SMW\Message;
+use SMW\Query\Language\Conjuncton;
+use SMW\Query\Language\Description;
+use SMW\Store;
+use SMW\Utils\CircularReferenceGuard;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class QuerySegmentListBuilder {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var DispatchingDescriptionInterpreter
+ */
+ private $dispatchingDescriptionInterpreter = null;
+
+ /**
+ * @var boolean
+ */
+ private $isFilterDuplicates = true;
+
+ /**
+ * Array of generated QueryContainer query descriptions (index => object).
+ *
+ * @var QuerySegment[]
+ */
+ private $querySegments = [];
+
+ /**
+ * Array of sorting requests ("Property_name" => "ASC"/"DESC"). Used during query
+ * processing (where these property names are searched while compiling the query
+ * conditions).
+ *
+ * @var string[]
+ */
+ private $sortKeys = [];
+
+ /**
+ * @var string[]
+ */
+ private $errors = [];
+
+ /**
+ * @var integer
+ */
+ private $lastQuerySegmentId = -1;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ * @param DescriptionInterpreterFactory $descriptionInterpreterFactory
+ */
+ public function __construct( Store $store, DescriptionInterpreterFactory $descriptionInterpreterFactory ) {
+ $this->store = $store;
+ $this->dispatchingDescriptionInterpreter = $descriptionInterpreterFactory->newDispatchingDescriptionInterpreter( $this );
+ $this->circularReferenceGuard = new CircularReferenceGuard( 'sql-query' );
+ $this->circularReferenceGuard->setMaxRecursionDepth( 2 );
+
+ QuerySegment::$qnum = 0;
+ }
+
+ /**
+ * Filter dulicate segments that represent the same query and to be identified
+ * by the same hash.
+ *
+ * @since 2.5
+ *
+ * @param boolean $isFilterDuplicates
+ */
+ public function isFilterDuplicates( $isFilterDuplicates ) {
+ $this->isFilterDuplicates = (bool)$isFilterDuplicates;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $sortKeys
+ *
+ * @return $this
+ */
+ public function setSortKeys( $sortKeys ) {
+ $this->sortKeys = $sortKeys;
+ return $this;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string[]
+ */
+ public function getSortKeys() {
+ return $this->sortKeys;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return CircularReferenceGuard
+ */
+ public function getCircularReferenceGuard() {
+ return $this->circularReferenceGuard;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param int $id
+ *
+ * @return QuerySegment
+ * @throws InvalidArgumentException
+ * @throws OutOfBoundsException
+ */
+ public function findQuerySegment( $id ) {
+
+ if ( !is_int( $id ) ) {
+ throw new InvalidArgumentException( '$id needs to be an integer' );
+ }
+
+ if ( !array_key_exists( $id, $this->querySegments ) ) {
+ throw new OutOfBoundsException( 'There is no query segment with id ' . $id );
+ }
+
+ return $this->querySegments[$id];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QuerySegment[]
+ */
+ public function getQuerySegmentList() {
+ return $this->querySegments;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param QuerySegment $query
+ */
+ public function addQuerySegment( QuerySegment $query ) {
+ $this->querySegments[$query->queryNumber] = $query;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getLastQuerySegmentId() {
+ return $this->lastQuerySegmentId;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $error
+ */
+ public function addError( $error, $type = Message::TEXT ) {
+ $this->errors[Message::getHash( $error, $type )] = Message::encode( $error, $type );
+ }
+
+ /**
+ * Create a new QueryContainer object that can be used to obtain results
+ * for the given description. The result is stored in $this->queries
+ * using a numeric key that is returned as a result of the function.
+ * Returns -1 if no query was created.
+ *
+ * @param Description $description
+ *
+ * @return integer
+ */
+ public function getQuerySegmentFrom( Description $description ) {
+
+ $fingerprint = $description->getFingerprint();
+
+ // Get membership of descriptions that are resolved recursively
+ if ( $description->getMembership() !== '' ) {
+ $fingerprint = $fingerprint . $description->getMembership();
+ }
+
+ if ( ( $querySegment = $this->findDuplicates( $fingerprint ) ) ) {
+ return $querySegment;
+ }
+
+ $querySegment = $this->dispatchingDescriptionInterpreter->interpretDescription(
+ $description
+ );
+
+ $querySegment->fingerprint = $fingerprint;
+ //$querySegment->membership = $description->getMembership();
+ //$querySegment->queryString = $description->getQueryString();
+
+ $this->lastQuerySegmentId = $this->registerQuerySegment(
+ $querySegment
+ );
+
+ return $this->lastQuerySegmentId;
+ }
+
+ /**
+ * Register a query object to the internal query list, if the query is
+ * valid. Also make sure that sortkey information is propagated down
+ * from subqueries of this query.
+ *
+ * @param QuerySegment $query
+ */
+ private function registerQuerySegment( QuerySegment $query ) {
+ if ( $query->type === QuerySegment::Q_NOQUERY ) {
+ return -1;
+ }
+
+ $this->addQuerySegment( $query );
+
+ // Propagate sortkeys from subqueries:
+ if ( $query->type !== QuerySegment::Q_DISJUNCTION ) {
+ // Sortkeys are killed by disjunctions (not all parts may have them),
+ // NOTE: preprocessing might try to push disjunctions downwards to safe sortkey, but this seems to be minor
+ foreach ( $query->components as $cid => $field ) {
+ $query->sortfields = array_merge( $this->findQuerySegment( $cid )->sortfields, $query->sortfields );
+ }
+ }
+
+ return $query->queryNumber;
+ }
+
+ private function findDuplicates( $fingerprint ) {
+
+ if ( $this->errors !== [] || $this->isFilterDuplicates === false ) {
+ return false;
+ }
+
+ foreach ( $this->querySegments as $querySegment ) {
+ if ( $querySegment->fingerprint === $fingerprint ) {
+ return $querySegment->queryNumber;
+ };
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListProcessor.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListProcessor.php
new file mode 100644
index 00000000..ad1f65a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/QuerySegmentListProcessor.php
@@ -0,0 +1,349 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\TableBuilder\TemporaryTableBuilder;
+use SMWQuery as Query;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author mwjames
+ */
+class QuerySegmentListProcessor {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var TemporaryTableBuilder
+ */
+ private $temporaryTableBuilder;
+
+ /**
+ * @var HierarchyTempTableBuilder
+ */
+ private $hierarchyTempTableBuilder;
+
+ /**
+ * Array of arrays of executed queries, indexed by the temporary table names
+ * results were fed into.
+ *
+ * @var array
+ */
+ private $executedQueries = [];
+
+ /**
+ * Query mode copied from given query. Some submethods act differently when
+ * in Query::MODE_DEBUG.
+ *
+ * @var int
+ */
+ private $queryMode;
+
+ /**
+ * @var array
+ */
+ private $querySegmentList = [];
+
+ /**
+ * @param Database $connection
+ * @param TemporaryTableBuilder $temporaryTableBuilder
+ * @param HierarchyTempTableBuilder $hierarchyTempTableBuilder
+ */
+ public function __construct( Database $connection, TemporaryTableBuilder $temporaryTableBuilder, HierarchyTempTableBuilder $hierarchyTempTableBuilder ) {
+ $this->connection = $connection;
+ $this->temporaryTableBuilder = $temporaryTableBuilder;
+ $this->hierarchyTempTableBuilder = $hierarchyTempTableBuilder;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getExecutedQueries() {
+ return $this->executedQueries;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param &$querySegmentList
+ */
+ public function setQuerySegmentList( &$querySegmentList ) {
+ $this->querySegmentList =& $querySegmentList;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param integer
+ */
+ public function setQueryMode( $queryMode ) {
+ $this->queryMode = $queryMode;
+ }
+
+ /**
+ * Process stored queries and change store accordingly. The query obj is modified
+ * so that it contains non-recursive description of a select to execute for getting
+ * the actual result.
+ *
+ * @param integer $id
+ * @throws RuntimeException
+ */
+ public function process( $id ) {
+
+ $this->hierarchyTempTableBuilder->emptyHierarchyCache();
+ $this->executedQueries = [];
+
+ // Should never happen
+ if ( !isset( $this->querySegmentList[$id] ) ) {
+ throw new RuntimeException( "$id doesn't exist" );
+ }
+
+ $this->resolve( $this->querySegmentList[$id] );
+ }
+
+ private function resolve( QuerySegment &$query ) {
+
+ switch ( $query->type ) {
+ case QuerySegment::Q_TABLE: // Normal query with conjunctive subcondition.
+ foreach ( $query->components as $qid => $joinField ) {
+ $subQuery = $this->querySegmentList[$qid];
+ $this->resolve( $subQuery );
+
+ if ( $subQuery->joinTable !== '' ) { // Join with jointable.joinfield
+ $op = $subQuery->not ? '!' : '';
+
+ $joinType = $subQuery->joinType ? $subQuery->joinType : 'INNER';
+ $t = $this->connection->tableName( $subQuery->joinTable ) ." AS $subQuery->alias";
+
+ if ( $subQuery->from ) {
+ $t = "($t $subQuery->from)";
+ }
+
+ $query->from .= " $joinType JOIN $t ON $joinField$op=" . $subQuery->joinfield;
+
+ if ( $joinType === 'LEFT' ) {
+ $query->where .= ( ( $query->where === '' ) ? '' : ' AND ' ) . '(' . $subQuery->joinfield . ' IS NULL)';
+ }
+
+ } elseif ( $subQuery->joinfield !== '' ) { // Require joinfield as "value" via WHERE.
+ $condition = '';
+
+ if ( $subQuery->null === true ) {
+ $condition .= ( $condition ? ' OR ': '' ) . "$joinField IS NULL";
+ } else {
+ foreach ( $subQuery->joinfield as $value ) {
+ $op = $subQuery->not ? '!' : '';
+ $condition .= ( $condition ? ' OR ': '' ) . "$joinField$op=" . $this->connection->addQuotes( $value );
+ }
+ }
+
+ if ( count( $subQuery->joinfield ) > 1 ) {
+ $condition = "($condition)";
+ }
+
+ $query->where .= ( ( $query->where === '' || $subQuery->where === null ) ? '' : ' AND ' ) . $condition;
+ $query->from .= $subQuery->from;
+ } else { // interpret empty joinfields as impossible condition (empty result)
+ $query->joinfield = ''; // make whole query false
+ $query->joinTable = '';
+ $query->where = '';
+ $query->from = '';
+ break;
+ }
+
+ if ( $subQuery->where !== '' && $subQuery->where !== null ) {
+ if ( $subQuery->joinType === 'LEFT' || $subQuery->joinType == 'LEFT OUTER' ) {
+ $query->from .= ' AND (' . $subQuery->where . ')';
+ } else {
+ $query->where .= ( ( $query->where === '' ) ? '' : ' AND ' ) . '(' . $subQuery->where . ')';
+ }
+ }
+ }
+
+ $query->components = [];
+ break;
+ case QuerySegment::Q_CONJUNCTION:
+ reset( $query->components );
+ $key = false;
+
+ // Pick one subquery as anchor point ...
+ foreach ( $query->components as $qkey => $qid ) {
+ $key = $qkey;
+
+ if ( $this->querySegmentList[$qkey]->joinTable !== '' ) {
+ break;
+ }
+ }
+
+ $result = $this->querySegmentList[$key];
+ unset( $query->components[$key] );
+
+ // Execute it first (may change jointable and joinfield, e.g. when making temporary tables)
+ $this->resolve( $result );
+
+ // ... and append to this query the remaining queries.
+ foreach ( $query->components as $qid => $joinfield ) {
+ $result->components[$qid] = $result->joinfield;
+ }
+
+ // Second execute, now incorporating remaining conditions.
+ $this->resolve( $result );
+ $query = $result;
+ break;
+ case QuerySegment::Q_DISJUNCTION:
+ if ( $this->queryMode !== Query::MODE_NONE ) {
+ $this->temporaryTableBuilder->create( $this->connection->tableName( $query->alias ) );
+ }
+
+ $this->executedQueries[$query->alias] = [];
+
+ foreach ( $query->components as $qid => $joinField ) {
+ $subQuery = $this->querySegmentList[$qid];
+ $this->resolve( $subQuery );
+ $sql = '';
+
+ if ( $subQuery->joinTable !== '' ) {
+ $sql = 'INSERT ' . 'IGNORE ' . 'INTO ' .
+ $this->connection->tableName( $query->alias ) .
+ " SELECT DISTINCT $subQuery->joinfield FROM " . $this->connection->tableName( $subQuery->joinTable ) .
+ " AS $subQuery->alias $subQuery->from" . ( $subQuery->where ? " WHERE $subQuery->where":'' );
+ } elseif ( $subQuery->joinfield !== '' ) {
+ // NOTE: this works only for single "unconditional" values without further
+ // WHERE or FROM. The execution must take care of not creating any others.
+ $values = '';
+
+ // This produces an error on postgres with
+ // pg_query(): Query failed: ERROR: duplicate key value violates
+ // unique constraint "sunittest_t3_pkey" DETAIL: Key (id)=(274) already exists.
+
+ foreach ( $subQuery->joinfield as $value ) {
+ $values .= ( $values ? ',' : '' ) . '(' . $this->connection->addQuotes( $value ) . ')';
+ }
+
+ $sql = 'INSERT ' . 'IGNORE ' . 'INTO ' . $this->connection->tableName( $query->alias ) . " (id) VALUES $values";
+ } // else: // interpret empty joinfields as impossible condition (empty result), ignore
+ if ( $sql ) {
+ $this->executedQueries[$query->alias][] = $sql;
+
+ if ( $this->queryMode !== Query::MODE_NONE ) {
+ $this->connection->query(
+ $sql,
+ __METHOD__
+ );
+ }
+ }
+ }
+
+ $query->type = QuerySegment::Q_TABLE;
+ $query->where = '';
+ $query->components = [];
+
+ $query->joinTable = $query->alias;
+ $query->joinfield = "$query->alias.id";
+ $query->sortfields = []; // Make sure we got no sortfields.
+ // TODO: currently this eliminates sortkeys, possibly keep them (needs different temp table format though, maybe not such a good thing to do)
+ break;
+ case QuerySegment::Q_PROP_HIERARCHY:
+ case QuerySegment::Q_CLASS_HIERARCHY: // make a saturated hierarchy
+ $this->resolveHierarchy( $query );
+ break;
+ case QuerySegment::Q_VALUE:
+ break; // nothing to do
+ }
+ }
+
+ /**
+ * Find subproperties or subcategories. This may require iterative computation,
+ * and temporary tables are used in many cases.
+ *
+ * @param QuerySegment $query
+ */
+ private function resolveHierarchy( QuerySegment &$query ) {
+
+ switch ( $query->type ) {
+ case QuerySegment::Q_PROP_HIERARCHY:
+ $type = 'property';
+ break;
+ case QuerySegment::Q_CLASS_HIERARCHY:
+ $type = 'class';
+ break;
+ }
+
+ list( $smwtable, $depth ) = $this->hierarchyTempTableBuilder->getHierarchyTableDefinitionForType(
+ $type
+ );
+
+ // An individual depth was annotated as part of the query
+ if ( $query->depth !== null ) {
+ $depth = $query->depth;
+ }
+
+ if ( $depth <= 0 ) { // treat as value, no recursion
+ $query->type = QuerySegment::Q_VALUE;
+ return;
+ }
+
+ $values = '';
+ $valuecond = '';
+
+ foreach ( $query->joinfield as $value ) {
+ $values .= ( $values ? ',':'' ) . '(' . $this->connection->addQuotes( $value ) . ')';
+ $valuecond .= ( $valuecond ? ' OR ':'' ) . 'o_id=' . $this->connection->addQuotes( $value );
+ }
+
+ // Try to safe time (SELECT is cheaper than creating/dropping 3 temp tables):
+ $res = $this->connection->select(
+ $smwtable,
+ 's_id',
+ $valuecond,
+ __METHOD__,
+ [ 'LIMIT' => 1 ]
+ );
+
+ if ( !$this->connection->fetchObject( $res ) ) { // no subobjects, we are done!
+ $this->connection->freeResult( $res );
+ $query->type = QuerySegment::Q_VALUE;
+ return;
+ }
+
+ $this->connection->freeResult( $res );
+ $tablename = $this->connection->tableName( $query->alias );
+ $this->executedQueries[$query->alias] = [
+ "Recursively computed hierarchy for element(s) $values.",
+ "SELECT s_id FROM $smwtable WHERE $valuecond LIMIT 1"
+ ];
+
+ $query->joinTable = $query->alias;
+ $query->joinfield = "$query->alias.id";
+
+ $this->hierarchyTempTableBuilder->createHierarchyTempTableFor(
+ $type,
+ $tablename,
+ $values,
+ $depth
+ );
+ }
+
+ /**
+ * After querying, make sure no temporary database tables are left.
+ * @todo I might be better to keep the tables and possibly reuse them later
+ * on. Being temporary, the tables will vanish with the session anyway.
+ */
+ public function cleanUp() {
+ foreach ( $this->executedQueries as $table => $log ) {
+ $this->temporaryTableBuilder->drop( $this->connection->tableName( $table ) );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/README.md b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/README.md
new file mode 100644
index 00000000..a6d9dc21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/README.md
@@ -0,0 +1,61 @@
+# QueryEngine
+
+The `QueryEngine` handles the transformation of the `ask` query language into a `SQL` construct and is also
+responsible to return query results from the `SQL` back-end with the help of the following components:
+
+- The `QuerySegmentListBuilder` transforms `ask` descriptions into individual `QuerySegment`'s (aka `QuerySegmentList`)
+- The `DescriptionInterpreter` interface describes classes that are responsible to interpret a specific
+ `Description` object and turn it into an abstract `SQL` construct (a `QuerySegment`)
+- The `QuerySegmentListProcessor` flattens and transforms a list of `QuerySegment`'s into a non-recursive
+ tree of `SQL` statements (including resolving of property/category hierarchies)
+- The `ConceptQueryResolver` encapsulates query processing of a concept description in connection
+ with the `ConceptCache` class
+
+## Overview
+
+![image](https://cloud.githubusercontent.com/assets/1245473/10050078/ca42ff12-621a-11e5-84c3-5fd04d945c6c.png)
+
+### Examples
+```php
+/**
+ * Equivalent to [[Category:Foo]]
+ */
+$classDescription = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+);
+
+/**
+ * Equivalent to [[:+]]
+ */
+$namespaceDescription = new NamespaceDescription(
+ NS_MAIN
+);
+
+/**
+ * Equivalent to [[Foo::+]]
+ */
+$someProperty = new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+);
+
+/**
+ * Equivalent to [[:+]][[Category:Foo]][[Foo::+]]
+ */
+$description = new Conjunction( [
+ $namespaceDescription,
+ $classDescription,
+ $someProperty
+] );
+```
+```php
+$query = new Query( $description );
+$query->setLimit( 10 );
+
+$sqlStorefactory = new SQLStoreFactory(
+ new SQLStore()
+);
+
+$queryEngine = $sqlStorefactory->newMasterQueryEngine();
+$queryResult = $queryEngine->getQueryResult( $query );
+```
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngineFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngineFactory.php
new file mode 100644
index 00000000..185399aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngineFactory.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SQLStore\QueryEngine\EngineOptions;
+use SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder;
+use SMW\SQLStore\QueryEngine\OrderCondition;
+use SMW\SQLStore\QueryEngine\QueryEngine;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuildManager;
+use SMW\SQLStore\QueryEngine\QuerySegmentListProcessor;
+use SMW\SQLStore\TableBuilder\TemporaryTableBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryEngineFactory {
+
+ /**
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @since 2.4
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return QuerySegmentListBuilder
+ */
+ public function newQuerySegmentListBuilder() {
+
+ $querySegmentListBuilder = new QuerySegmentListBuilder(
+ $this->store,
+ new DescriptionInterpreterFactory()
+ );
+
+ $querySegmentListBuilder->isFilterDuplicates(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgQFilterDuplicates' )
+ );
+
+ return $querySegmentListBuilder;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return QuerySegmentListProcessor
+ */
+ public function newQuerySegmentListProcessor() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $connection = $this->store->getConnection( 'mw.db.queryengine' );
+ $temporaryTableBuilder = $this->newTemporaryTableBuilder();
+
+ $hierarchyTempTableBuilder = new HierarchyTempTableBuilder(
+ $connection,
+ $temporaryTableBuilder
+ );
+
+ $hierarchyTempTableBuilder->setPropertyHierarchyTableDefinition(
+ $this->store->findPropertyTableID( new DIProperty( '_SUBP' ) ),
+ $settings->get( 'smwgQSubpropertyDepth' )
+ );
+
+ $hierarchyTempTableBuilder->setClassHierarchyTableDefinition(
+ $this->store->findPropertyTableID( new DIProperty( '_SUBC' ) ),
+ $settings->get( 'smwgQSubcategoryDepth' )
+ );
+
+ $querySegmentListProcessor = new QuerySegmentListProcessor(
+ $connection,
+ $temporaryTableBuilder,
+ $hierarchyTempTableBuilder
+ );
+
+ return $querySegmentListProcessor;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return QueryEngine
+ */
+ public function newQueryEngine() {
+
+ $querySegmentListBuilder = $this->newQuerySegmentListBuilder();
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $settings = $applicationFactory->getSettings();
+
+ $orderCondition = new OrderCondition(
+ $querySegmentListBuilder
+ );
+
+ $orderCondition->isSupported(
+ $settings->isFlagSet( 'smwgQSortFeatures', SMW_QSORT )
+ );
+
+ $orderCondition->asUnconditional(
+ $settings->isFlagSet( 'smwgQSortFeatures', SMW_QSORT_UNCONDITIONAL )
+ );
+
+ $querySegmentListBuildManager = new QuerySegmentListBuildManager(
+ $this->store->getConnection( 'mw.db.queryengine' ),
+ $querySegmentListBuilder,
+ $orderCondition
+ );
+
+ $queryEngine = new QueryEngine(
+ $this->store,
+ $querySegmentListBuildManager,
+ $this->newQuerySegmentListProcessor(),
+ new EngineOptions()
+ );
+
+ $queryEngine->setLogger(
+ $applicationFactory->getMediaWikiLogger()
+ );
+
+ return $queryEngine;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ConceptQuerySegmentBuilder
+ */
+ public function newConceptQuerySegmentBuilder() {
+
+ $pplicationFactory = ApplicationFactory::getInstance();
+
+ $conceptQuerySegmentBuilder = new ConceptQuerySegmentBuilder(
+ $this->newQuerySegmentListBuilder(),
+ $this->newQuerySegmentListProcessor()
+ );
+
+ $conceptQuerySegmentBuilder->setQueryParser(
+ $pplicationFactory->getQueryFactory()->newQueryParser(
+ $pplicationFactory->getSettings()->get( 'smwgQConceptFeatures' )
+ )
+ );
+
+ return $conceptQuerySegmentBuilder;
+ }
+
+ private function newTemporaryTableBuilder() {
+
+ $temporaryTableBuilder = new TemporaryTableBuilder(
+ $this->store->getConnection( 'mw.db.queryengine' )
+ );
+
+ $temporaryTableBuilder->setAutoCommitFlag(
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgQTemporaryTablesAutoCommitMode' )
+ );
+
+ return $temporaryTableBuilder;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/README.md b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/README.md
new file mode 100644
index 00000000..6c938074
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/README.md
@@ -0,0 +1,12 @@
+# SQLStore
+
+The `SQLStore` consists of a storage and query engine to manage semantic data structures with the help of a `SQL` back-end.
+
+## Overview
+
+![image](https://cloud.githubusercontent.com/assets/1245473/20031413/49b336f6-a377-11e6-90f5-9fbe25b27812.png)
+
+## See also
+
+- [Installer](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/docs/technical/sqlstore.installer.md)
+- [QueryEngine](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/src/SQLStore/QueryEngine/README.md) \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RedirectStore.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RedirectStore.php
new file mode 100644
index 00000000..11065976
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RedirectStore.php
@@ -0,0 +1,274 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Onoi\Cache\Cache;
+use SMW\HashBuilder;
+use SMW\InMemoryPoolCache;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\Store;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class RedirectStore {
+
+ const TABLE_NAME = 'smw_fpt_redi';
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Cache
+ */
+ private $cache;
+
+ /**
+ * @var boolean
+ */
+ private $hasEqualitySupport = false;
+
+ /**
+ * @since 2.1
+ *
+ * @param Store $store
+ * @param Cache|null $cache
+ */
+ public function __construct( Store $store, Cache $cache = null ) {
+ $this->store = $store;
+ $this->cache = $cache;
+
+ if ( $this->cache === null ) {
+ $this->cache = InMemoryPoolCache::getInstance()->getPoolCacheById( 'sql.store.redirect.infostore' );
+ }
+
+ $this->setEqualitySupportFlag( $GLOBALS['smwgQEqualitySupport'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $equalitySupport
+ */
+ public function setEqualitySupportFlag( $equalitySupport ) {
+ $this->hasEqualitySupport = $equalitySupport != SMW_EQ_NONE;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $title DB key
+ * @param integer $namespace
+ *
+ * @return boolean
+ */
+ public function isRedirect( $title, $namespace ) {
+ return $this->findRedirect( $title, $namespace ) != 0;
+ }
+
+ /**
+ * Returns an id for a redirect if no redirect is found 0 is returned
+ *
+ * @since 2.1
+ *
+ * @param string $title DB key
+ * @param integer $namespace
+ *
+ * @return integer
+ */
+ public function findRedirect( $title, $namespace ) {
+
+ $hash = HashBuilder::createHashIdFromSegments(
+ $title,
+ $namespace
+ );
+
+ if ( $this->cache->contains( $hash ) ) {
+ return $this->cache->fetch( $hash );
+ }
+
+ $id = $this->select( $title, $namespace );
+
+ $this->cache->save( $hash, $id );
+
+ return $id;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function addRedirect( $id, $title, $namespace ) {
+
+ $this->insert( $id, $title, $namespace );
+
+ $hash = HashBuilder::createHashIdFromSegments(
+ $title,
+ $namespace
+ );
+
+ $this->cache->save( $hash, $id );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function updateRedirect( $id, $title, $namespace ) {
+
+ $this->deleteRedirect( $title, $namespace );
+
+ if ( !$this->canCreateUpdateJobs() || !$this->hasEqualitySupport ) {
+ return;
+ }
+
+ // Entries that refer to old target may in fact refer to subject,
+ // but we don't know which: schedule affected pages for update
+ $propertyTables = $this->store->getPropertyTables();
+ $connection = $this->store->getConnection( 'mw.db' );
+ $jobs = [];
+
+ foreach ( $propertyTables as $proptable ) {
+
+ // Can be skipped safely
+ if ( $proptable->getName() == self::TABLE_NAME ) {
+ continue;
+ }
+
+ $query = [
+ 'from' => '',
+ 'fields' => ''
+ ];
+
+ $query['condition'] = [ 'p_id' => $id ];
+
+ if ( $proptable->usesIdSubject() ) {
+ $query['from'] .= $connection->tableName( $proptable->getName() );
+ $query['from'] .= ' INNER JOIN ';
+ $query['from'] .= $connection->tableName( SQLStore::ID_TABLE ) . ' ON s_id=smw_id';
+ $query['fields'] = 'DISTINCT smw_title AS t,smw_namespace AS ns';
+ } else {
+ $query['from'] = $connection->tableName( $proptable->getName() );
+ $query['fields'] = 'DISTINCT s_title AS t,s_namespace AS ns';
+ }
+
+ if ( $namespace === SMW_NS_PROPERTY && !$proptable->isFixedPropertyTable() ) {
+ $this->findUpdateJobs( $connection, $query, $jobs );
+ }
+
+ foreach ( $proptable->getFields( $this->store ) as $fieldName => $fieldType ) {
+
+ if ( $fieldType !== FieldType::FIELD_ID ) {
+ continue;
+ }
+
+ $query['condition'] = [ $fieldName => $id ];
+ $this->findUpdateJobs( $connection, $query, $jobs );
+ }
+ }
+
+ foreach ( $jobs as $job ) {
+ $job->insert();
+ }
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $title
+ * @param integer $namespace
+ */
+ public function deleteRedirect( $title, $namespace ) {
+
+ $this->delete( $title, $namespace );
+
+ $hash = HashBuilder::createHashIdFromSegments(
+ $title,
+ $namespace
+ );
+
+ $this->cache->delete( $hash );
+ }
+
+ private function select( $title, $namespace ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $row = $connection->selectRow(
+ self::TABLE_NAME,
+ 'o_id',
+ [
+ 's_title' => $title,
+ 's_namespace' => $namespace
+ ],
+ __METHOD__
+ );
+
+ return $row !== false && isset( $row->o_id ) ? (int)$row->o_id : 0;
+ }
+
+ private function insert( $id, $title, $namespace ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $connection->insert(
+ self::TABLE_NAME,
+ [
+ 's_title' => $title,
+ 's_namespace' => $namespace,
+ 'o_id' => $id ],
+ __METHOD__
+ );
+ }
+
+ private function delete( $title, $namespace ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $connection->delete(
+ self::TABLE_NAME,
+ [
+ 's_title' => $title,
+ 's_namespace' => $namespace ],
+ __METHOD__
+ );
+ }
+
+ private function canCreateUpdateJobs() {
+ return $this->store->getOption( Store::OPT_CREATE_UPDATE_JOB, true ) && $this->store->getOption( 'smwgEnableUpdateJobs' );
+ }
+
+ private function findUpdateJobs( $connection, $query, &$jobs ) {
+
+ $res = $connection->select(
+ $query['from'],
+ $query['fields'],
+ $query['condition'],
+ __METHOD__
+ );
+
+ foreach ( $res as $row ) {
+ $title = Title::makeTitleSafe( $row->ns, $row->t );
+
+ if ( $title !== null ) {
+ $jobs[] = new UpdateJob( $title );
+ }
+ }
+
+ $connection->freeResult( $res );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RequestOptionsProc.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RequestOptionsProc.php
new file mode 100644
index 00000000..8a8cc949
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/RequestOptionsProc.php
@@ -0,0 +1,304 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\DIWikiPage;
+use SMW\Store;
+use SMWDIBlob as DIBlob;
+use SMWRequestOptions as RequestOptions;
+use SMWStringCondition as StringCondition;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class RequestOptionsProc {
+
+ /**
+ * Transform input parameters into a suitable array of SQL options.
+ * The parameter $valuecol defines the string name of the column to which
+ * sorting requests etc. are to be applied.
+ *
+ * @since 1.8
+ *
+ * @param RequestOptions|null $requestOptions
+ * @param string $valueCol
+ *
+ * @return array
+ */
+ public static function getSQLOptions( RequestOptions $requestOptions = null, $valueCol = '' ) {
+ $sqlConds = [];
+
+ if ( $requestOptions === null ) {
+ return $sqlConds;
+ }
+
+ if ( $requestOptions->getLimit() > 0 ) {
+ $sqlConds['LIMIT'] = $requestOptions->getLimit();
+ }
+
+ if ( $requestOptions->getOffset() > 0 ) {
+ $sqlConds['OFFSET'] = $requestOptions->getOffset();
+ }
+
+ if ( ( $valueCol !== '' ) && ( $requestOptions->sort ) ) {
+ $sqlConds['ORDER BY'] = $requestOptions->ascending ? $valueCol : $valueCol . ' DESC';
+ }
+
+ if ( $requestOptions->getOption( 'GROUP BY' ) ) {
+ $sqlConds['GROUP BY'] = $requestOptions->getOption( 'GROUP BY' );
+ }
+
+ if ( $requestOptions->getOption( 'DISTINCT' ) ) {
+ $sqlConds['DISTINCT'] = $requestOptions->getOption( 'DISTINCT' );
+ }
+
+ // Avoid a possible filesort (likely caused by ORDER BY) when limit is
+ // less than 2
+ if ( $requestOptions->limit < 2 || $requestOptions->getOption( 'ORDER BY' ) === false ) {
+ unset( $sqlConds['ORDER BY'] );
+ }
+
+ return $sqlConds;
+ }
+
+ /**
+ * Transform input parameters into a suitable string of additional SQL
+ * conditions. The parameter $valuecol defines the string name of the
+ * column to which value restrictions etc. are to be applied.
+ *
+ * @since 1.8
+ *
+ * @param Store $store
+ * @param RequestOptions|null $requestOptions
+ * @param string $valueCol name of SQL column to which conditions apply
+ * @param string $labelCol name of SQL column to which string conditions apply, if any
+ * @param boolean $addAnd indicate whether the string should begin with " AND " if non-empty
+ *
+ * @return string
+ */
+ public static function getSQLConditions( Store $store, RequestOptions $requestOptions = null, $valueCol = '', $labelCol = '', $addAnd = true ) {
+ $sqlConds = '';
+
+ if ( $requestOptions === null ) {
+ return $sqlConds;
+ }
+
+ $connection = $store->getConnection( 'mw.db' );
+
+ // Apply value boundary
+ if ( ( $valueCol !== '' ) && ( $requestOptions->boundary !== null ) ) {
+
+ if ( $requestOptions->ascending ) {
+ $op = $requestOptions->include_boundary ? ' >= ' : ' > ';
+ } else {
+ $op = $requestOptions->include_boundary ? ' <= ' : ' < ';
+ }
+
+ $sqlConds .= ( $addAnd ? ' AND ' : '' ) . $valueCol . $op . $connection->addQuotes( $requestOptions->boundary );
+ }
+
+ // Apply string conditions
+ if ( $labelCol !== '' ) {
+ foreach ( $requestOptions->getStringConditions() as $strcond ) {
+ $string = str_replace( '_', '\_', $strcond->string );
+ $condition = 'LIKE';
+
+ switch ( $strcond->condition ) {
+ case StringCondition::COND_PRE:
+ $string .= '%';
+ break;
+ case StringCondition::COND_POST:
+ $string = '%' . $string;
+ break;
+ case StringCondition::COND_MID:
+ $string = '%' . $string . '%';
+ break;
+ case StringCondition::COND_EQ:
+ $string = $strcond->string;
+ $condition = '=';
+ break;
+ }
+
+ $conditionOperator = $strcond->isOr ? ' OR ' : ' AND ';
+
+ if ( $strcond->isNot ) {
+ $sqlConds = " ($sqlConds) AND ($labelCol NOT $condition ". $connection->addQuotes( $string ) . ") ";
+ } else {
+ $sqlConds .= ( ( $addAnd || ( $sqlConds !== '' ) ) ? $conditionOperator : '' ) . "$labelCol $condition " . $connection->addQuotes( $string );
+ }
+ }
+ }
+
+ foreach ( $requestOptions->getExtraConditions() as $extraCondition ) {
+
+ $expr = $addAnd ? 'AND' : '';
+
+ if ( is_array( $extraCondition ) ) {
+ foreach ( $extraCondition as $k => $v ) {
+ $expr = $k;
+ $extraCondition = $v;
+ }
+ }
+
+ $sqlConds .= ( ( $addAnd || ( $sqlConds !== '' ) ) ? " $expr " : '' ) . $extraCondition;
+ }
+
+ return $sqlConds;
+ }
+
+ /**
+ * Not in all cases can requestoptions be forwarded to the DB using
+ * getSQLConditions() and getSQLOptions(): some data comes from caches
+ * that do not respect the options yet. This method takes an array of
+ * results (SMWDataItem objects) *of the same type* and applies the
+ * given requestoptions as appropriate.
+ *
+ * @since 1.8
+ *
+ * @param Store $store
+ * @param array $data array of SMWDataItem objects
+ * @param SMWRequestOptions|null $requestoptions
+ *
+ * @return SMWDataItem[]
+ */
+ public static function applyRequestOptions( Store $store, array $data, RequestOptions $requestOptions = null ) {
+
+ if ( $data === [] || $requestOptions === null ) {
+ return $data;
+ }
+
+ $result = [];
+ $sortres = [];
+
+ $sampleDataItem = reset( $data );
+ $isNumeric = is_numeric( $sampleDataItem->getSortKey() );
+
+ $i = 0;
+
+ foreach ( $data as $item ) {
+
+ list( $label, $value ) = self::getSortKeyForItem( $store, $item );
+
+ $keepDataValue = self::applyBoundaryConditions( $requestOptions, $value, $isNumeric );
+ $keepDataValue = self::applyStringConditions( $requestOptions, $label, $keepDataValue );
+
+ if ( $keepDataValue ) {
+ $result[$i] = $item;
+ $sortres[$i] = $value;
+ $i++;
+ }
+ }
+
+ self::applySortRestriction( $requestOptions, $result, $sortres, $isNumeric );
+ self::applyLimitRestriction( $requestOptions, $result );
+
+ return $result;
+ }
+
+ private static function applyStringConditions( $requestOptions, $label, $keepDataValue ) {
+
+ foreach ( $requestOptions->getStringConditions() as $strcond ) { // apply string conditions
+ switch ( $strcond->condition ) {
+ case StringCondition::STRCOND_PRE:
+ $keepDataValue = $keepDataValue && ( strpos( $label, $strcond->string ) === 0 );
+ break;
+ case StringCondition::STRCOND_POST:
+ $keepDataValue = $keepDataValue && ( strpos( strrev( $label ), strrev( $strcond->string ) ) === 0 );
+ break;
+ case StringCondition::STRCOND_MID:
+ $keepDataValue = $keepDataValue && ( strpos( $label, $strcond->string ) !== false );
+ break;
+ }
+ }
+
+ return $keepDataValue;
+ }
+
+ private static function applyBoundaryConditions( $requestOptions, $value, $isNumeric ) {
+ $keepDataValue = true; // keep datavalue only if this remains true
+
+ if ( $requestOptions->boundary === null ) {
+ return $keepDataValue;
+ }
+
+ // apply value boundary
+ $strc = $isNumeric ? 0 : strcmp( $value, $requestOptions->boundary );
+
+ if ( $requestOptions->ascending ) {
+ if ( $requestOptions->include_boundary ) {
+ $keepDataValue = $isNumeric ? ( $value >= $requestOptions->boundary ) : ( $strc >= 0 );
+ } else {
+ $keepDataValue = $isNumeric ? ( $value > $requestOptions->boundary ) : ( $strc > 0 );
+ }
+ } else {
+ if ( $requestOptions->include_boundary ) {
+ $keepDataValue = $isNumeric ? ( $value <= $requestOptions->boundary ) : ( $strc <= 0 );
+ } else {
+ $keepDataValue = $isNumeric ? ( $value < $requestOptions->boundary ) : ( $strc < 0 );
+ }
+ }
+
+ return $keepDataValue;
+ }
+
+ private static function getSortKeyForItem( $store, $item ) {
+
+ if ( $item instanceof DIWikiPage ) {
+ $label = $store->getWikiPageSortKey( $item );
+ $value = $label;
+ } else {
+ $label = ( $item instanceof DIBlob ) ? $item->getString() : '';
+ $value = $item->getSortKey();
+ }
+
+ return [ $label, $value ];
+ }
+
+ private static function applySortRestriction( $requestOptions, &$result, $sortres, $isNumeric ) {
+
+ if ( !$requestOptions->sort ) {
+ return null;
+ }
+
+ $flag = $isNumeric ? SORT_NUMERIC : SORT_LOCALE_STRING;
+
+ // SORT_NATURAL is selected on n-asc, n-desc
+ if ( isset( $requestOptions->natural ) ) {
+ $flag = SORT_NATURAL;
+ }
+
+ if ( $requestOptions->ascending ) {
+ asort( $sortres, $flag );
+ } else {
+ arsort( $sortres, $flag );
+ }
+
+ $newres = [];
+
+ foreach ( $sortres as $key => $value ) {
+ $newres[] = $result[$key];
+ }
+
+ $result = $newres;
+ }
+
+ private static function applyLimitRestriction( $requestOptions, &$result ) {
+
+ // In case of a `conditionConstraint` the restriction is set forth by the
+ // SELECT statement.
+ if ( isset( $requestOptions->conditionConstraint ) ) {
+ return $result;
+ }
+
+ if ( $requestOptions->limit > 0 ) {
+ return $result = array_slice( $result, $requestOptions->offset, $requestOptions->limit );
+ }
+
+ $result = array_slice( $result, $requestOptions->offset );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/SQLStoreFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/SQLStoreFactory.php
new file mode 100644
index 00000000..0cba4d0e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/SQLStoreFactory.php
@@ -0,0 +1,762 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Onoi\Cache\Cache;
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\NullMessageReporter;
+use SMW\ApplicationFactory;
+use SMW\ChangePropListener;
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\Site;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\SQLStore\EntityStore\CachingEntityLookup;
+use SMW\SQLStore\EntityStore\CachingSemanticDataLookup;
+use SMW\SQLStore\EntityStore\DataItemHandlerDispatcher;
+use SMW\SQLStore\EntityStore\IdCacheManager;
+use SMW\SQLStore\EntityStore\IdEntityFinder;
+use SMW\SQLStore\EntityStore\IdChanger;
+use SMW\SQLStore\EntityStore\UniquenessLookup;
+use SMW\SQLStore\EntityStore\NativeEntityLookup;
+use SMW\SQLStore\EntityStore\SemanticDataLookup;
+use SMW\SQLStore\EntityStore\SubobjectListFinder;
+use SMW\SQLStore\EntityStore\TraversalPropertyLookup;
+use SMW\SQLStore\EntityStore\PropertySubjectsLookup;
+use SMW\SQLStore\EntityStore\PropertiesLookup;
+use SMW\SQLStore\Lookup\CachedListLookup;
+use SMW\SQLStore\Lookup\ListLookup;
+use SMW\SQLStore\Lookup\PropertyUsageListLookup;
+use SMW\SQLStore\Lookup\RedirectTargetLookup;
+use SMW\SQLStore\Lookup\UndeclaredPropertyListLookup;
+use SMW\SQLStore\Lookup\UnusedPropertyListLookup;
+use SMW\SQLStore\Lookup\UsageStatisticsListLookup;
+use SMW\SQLStore\Lookup\ProximityPropertyValueLookup;
+use SMW\SQLStore\TableBuilder\TableBuilder;
+use SMW\SQLStore\TableBuilder\Examiner\HashField;
+use SMW\Utils\CircularReferenceGuard;
+use SMWRequestOptions as RequestOptions;
+use SMWSql3SmwIds as EntityIdManager;
+use SMW\Services\ServicesContainer;
+use SMW\RequestData;
+use SMWSQLStore3;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class SQLStoreFactory {
+
+ /**
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var QueryEngineFactory
+ */
+ private $queryEngineFactory;
+
+ /**
+ * @since 2.2
+ *
+ * @param SMWSQLStore3 $store
+ * @param MessageReporter|null $messageReporter
+ */
+ public function __construct( SMWSQLStore3 $store, MessageReporter $messageReporter = null ) {
+ $this->store = $store;
+ $this->messageReporter = $messageReporter;
+
+ if ( $this->messageReporter === null ) {
+ $this->messageReporter = new NullMessageReporter();
+ }
+
+ $this->queryEngineFactory = new QueryEngineFactory( $store );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QueryEngine
+ */
+ public function newMasterQueryEngine() {
+ return $this->queryEngineFactory->newQueryEngine();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QueryEngine
+ */
+ public function newSlaveQueryEngine() {
+ return $this->newMasterQueryEngine();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return EntityIdManager
+ */
+ public function newEntityTable() {
+ return new EntityIdManager( $this->store, $this );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return PropertyTableUpdater
+ */
+ public function newPropertyTableUpdater() {
+ return new PropertyTableUpdater( $this->store, $this->newPropertyStatisticsStore() );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ConceptCache
+ */
+ public function newMasterConceptCache() {
+
+ $conceptCache = new ConceptCache(
+ $this->store,
+ $this->queryEngineFactory->newConceptQuerySegmentBuilder()
+ );
+
+ $conceptCache->setUpperLimit(
+ $GLOBALS['smwgQMaxLimit']
+ );
+
+ return $conceptCache;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ConceptCache
+ */
+ public function newSlaveConceptCache() {
+ return $this->newMasterConceptCache();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ListLookup
+ */
+ public function newUsageStatisticsCachedListLookup() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $usageStatisticsListLookup = new UsageStatisticsListLookup(
+ $this->store,
+ $this->newPropertyStatisticsStore()
+ );
+
+ return $this->newCachedListLookup(
+ $usageStatisticsListLookup,
+ $settings->safeGet( 'special.statistics' ),
+ $settings->safeGet( 'special.statistics' )
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function newPropertyUsageCachedListLookup( RequestOptions $requestOptions = null ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $propertyUsageListLookup = new PropertyUsageListLookup(
+ $this->store,
+ $this->newPropertyStatisticsStore(),
+ $requestOptions
+ );
+
+ return $this->newCachedListLookup(
+ $propertyUsageListLookup,
+ $settings->safeGet( 'special.properties' ),
+ $settings->safeGet( 'special.properties' )
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function newUnusedPropertyCachedListLookup( RequestOptions $requestOptions = null ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $unusedPropertyListLookup = new UnusedPropertyListLookup(
+ $this->store,
+ $this->newPropertyStatisticsStore(),
+ $requestOptions
+ );
+
+ return $this->newCachedListLookup(
+ $unusedPropertyListLookup,
+ $settings->safeGet( 'special.unusedproperties' ),
+ $settings->safeGet( 'special.unusedproperties' )
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return CachedListLookup
+ */
+ public function newUndeclaredPropertyCachedListLookup( RequestOptions $requestOptions = null ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $undeclaredPropertyListLookup = new UndeclaredPropertyListLookup(
+ $this->store,
+ $settings->get( 'smwgPDefaultType' ),
+ $requestOptions
+ );
+
+ return $this->newCachedListLookup(
+ $undeclaredPropertyListLookup,
+ $settings->safeGet( 'special.wantedproperties' ),
+ $settings->safeGet( 'special.wantedproperties' )
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param ListLookup $listLookup
+ * @param boolean $useCache
+ * @param integer $cacheExpiry
+ *
+ * @return ListLookup
+ */
+ public function newCachedListLookup( ListLookup $listLookup, $useCache, $cacheExpiry ) {
+
+ $cacheFactory = ApplicationFactory::getInstance()->newCacheFactory();
+
+ if ( is_int( $useCache ) ) {
+ $useCache = true;
+ }
+
+ $cacheOptions = $cacheFactory->newCacheOptions( [
+ 'useCache' => $useCache,
+ 'ttl' => $cacheExpiry
+ ] );
+
+ $cachedListLookup = new CachedListLookup(
+ $listLookup,
+ $cacheFactory->newMediaWikiCompositeCache( $cacheFactory->getMainCacheType() ),
+ $cacheOptions
+ );
+
+ $cachedListLookup->setCachePrefix( $cacheFactory->getCachePrefix() );
+
+ return $cachedListLookup;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return DeferredCallableUpdate
+ */
+ public function newDeferredCallableCachedListLookupUpdate() {
+
+ $deferredTransactionalUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate( function() {
+ $this->newPropertyUsageCachedListLookup()->deleteCache();
+ $this->newUnusedPropertyCachedListLookup()->deleteCache();
+ $this->newUndeclaredPropertyCachedListLookup()->deleteCache();
+ $this->newUsageStatisticsCachedListLookup()->deleteCache();
+ } );
+
+ return $deferredTransactionalUpdate;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return EntityRebuildDispatcher
+ */
+ public function newEntityRebuildDispatcher() {
+ return new EntityRebuildDispatcher(
+ $this->store,
+ ApplicationFactory::getInstance()->newTitleFactory()
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return EntityLookup
+ */
+ public function newEntityLookup() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $settings = $applicationFactory->getSettings();
+ $nativeEntityLookup = new NativeEntityLookup( $this->store );
+
+ if ( $settings->get( 'smwgEntityLookupCacheType' ) === CACHE_NONE ) {
+ return $nativeEntityLookup;
+ }
+
+ $circularReferenceGuard = new CircularReferenceGuard( 'store:entitylookup' );
+ $circularReferenceGuard->setMaxRecursionDepth( 2 );
+
+ $cacheFactory = $applicationFactory->newCacheFactory();
+
+ $blobStore = $cacheFactory->newBlobStore(
+ 'smw:store:entitylookup:',
+ $settings->get( 'smwgEntityLookupCacheType' ),
+ $settings->get( 'smwgEntityLookupCacheLifetime' )
+ );
+
+ $cachingEntityLookup = new CachingEntityLookup(
+ $nativeEntityLookup,
+ new RedirectTargetLookup( $this->store, $circularReferenceGuard ),
+ $blobStore
+ );
+
+ $cachingEntityLookup->setLookupFeatures(
+ $settings->get( 'smwgEntityLookupFeatures' )
+ );
+
+ return $cachingEntityLookup;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return PropertyTableInfoFetcher
+ */
+ public function newPropertyTableInfoFetcher() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $propertyTableInfoFetcher = new PropertyTableInfoFetcher(
+ new PropertyTypeFinder( $this->store->getConnection( 'mw.db' ) )
+ );
+
+ $propertyTableInfoFetcher->setCustomFixedPropertyList(
+ $settings->get( 'smwgFixedProperties' )
+ );
+
+ $propertyTableInfoFetcher->setCustomSpecialPropertyList(
+ $settings->get( 'smwgPageSpecialProperties' )
+ );
+
+ return $propertyTableInfoFetcher;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return PropertyTableIdReferenceFinder
+ */
+ public function newPropertyTableIdReferenceFinder() {
+
+ $propertyTableIdReferenceFinder = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $propertyTableIdReferenceFinder->isCapitalLinks(
+ Site::isCapitalLinks()
+ );
+
+ return $propertyTableIdReferenceFinder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return Installer
+ */
+ public function newInstaller() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $tableBuilder = TableBuilder::factory(
+ $this->store->getConnection( DB_MASTER )
+ );
+
+ $tableBuilder->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $tableIntegrityExaminer = new TableIntegrityExaminer(
+ $this->store,
+ new HashField( $this->store )
+ );
+
+ $tableSchemaManager = new TableSchemaManager(
+ $this->store
+ );
+
+ $tableSchemaManager->setFeatureFlags(
+ $settings->get( 'smwgFieldTypeFeatures' )
+ );
+
+ $installer = new Installer(
+ $tableSchemaManager,
+ $tableBuilder,
+ $tableIntegrityExaminer
+ );
+
+ $installer->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $installer->setOptions(
+ $this->store->getOptions()->filter(
+ [
+ Installer::OPT_TABLE_OPTIMIZE,
+ Installer::OPT_IMPORT,
+ Installer::OPT_SCHEMA_UPDATE,
+ Installer::OPT_SUPPLEMENT_JOBS
+ ]
+ )
+ );
+
+ return $installer;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DataItemHandlerDispatcher
+ */
+ public function newDataItemHandlerDispatcher() {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+
+ $dataItemHandlerDispatcher = new DataItemHandlerDispatcher(
+ $this->store
+ );
+
+ $dataItemHandlerDispatcher->setFieldTypeFeatures(
+ $settings->get( 'smwgFieldTypeFeatures' )
+ );
+
+ return $dataItemHandlerDispatcher;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return LoggerInterface
+ */
+ public function getLogger() {
+ return ApplicationFactory::getInstance()->getMediaWikiLogger();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TraversalPropertyLookup
+ */
+ public function newTraversalPropertyLookup() {
+ return new TraversalPropertyLookup( $this->store );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return PropertySubjectsLookup
+ */
+ public function newPropertySubjectsLookup() {
+ return new PropertySubjectsLookup( $this->store );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return PropertiesLookup
+ */
+ public function newPropertiesLookup() {
+ return new PropertiesLookup( $this->store );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertyStatisticsStore
+ */
+ public function newPropertyStatisticsStore() {
+
+ $propertyStatisticsStore = new PropertyStatisticsStore(
+ $this->store->getConnection( 'mw.db' )
+ );
+
+ $propertyStatisticsStore->setLogger(
+ $this->getLogger()
+ );
+
+ $propertyStatisticsStore->isCommandLineMode(
+ Site::isCommandLineMode()
+ );
+
+ return $propertyStatisticsStore;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return IdCacheManager
+ */
+ public function newIdCacheManager( $id, array $config ) {
+
+ $inMemoryPoolCache = ApplicationFactory::getInstance()->getInMemoryPoolCache();
+ $caches = [];
+
+ foreach ( $config as $key => $cacheSize ) {
+ $inMemoryPoolCache->resetPoolCacheById(
+ "$id.$key"
+ );
+
+ $caches[$key] = $inMemoryPoolCache->getPoolCacheById(
+ "$id.$key",
+ $cacheSize
+ );
+ }
+
+ return new IdCacheManager( $caches );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return PropertyTableRowDiffer
+ */
+ public function newPropertyTableRowDiffer() {
+
+ $propertyTableRowMapper = new PropertyTableRowMapper(
+ $this->store
+ );
+
+ $propertyTableRowDiffer = new PropertyTableRowDiffer(
+ $this->store,
+ $propertyTableRowMapper
+ );
+
+ return $propertyTableRowDiffer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param IdCacheManager $idCacheManager
+ *
+ * @return IdEntityFinder
+ */
+ public function newIdEntityFinder( IdCacheManager $idCacheManager ) {
+
+ $idMatchFinder = new IdEntityFinder(
+ $this->store,
+ $this->getIteratorFactory(),
+ $idCacheManager
+ );
+
+ return $idMatchFinder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return IdChanger
+ */
+ public function newIdChanger() {
+
+ $idChanger = new IdChanger(
+ $this->store
+ );
+
+ return $idChanger;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return UniquenessLookup
+ */
+ public function newUniquenessLookup() {
+
+ $uniquenessLookup = new UniquenessLookup(
+ $this->store,
+ $this->getIteratorFactory()
+ );
+
+ return $uniquenessLookup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return HierarchyLookup
+ */
+ public function newHierarchyLookup() {
+ return ApplicationFactory::getInstance()->newHierarchyLookup();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return SubobjectListFinder
+ */
+ public function newSubobjectListFinder() {
+
+ $subobjectListFinder = new SubobjectListFinder(
+ $this->store,
+ $this->getIteratorFactory()
+ );
+
+ return $subobjectListFinder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return SemanticDataLookup
+ */
+ public function newSemanticDataLookup() {
+
+ $semanticDataLookup = new SemanticDataLookup(
+ $this->store
+ );
+
+ $semanticDataLookup->setLogger(
+ $this->getLogger()
+ );
+
+ $cachingSemanticDataLookup = new CachingSemanticDataLookup(
+ $semanticDataLookup,
+ ApplicationFactory::getInstance()->getCache()
+ );
+
+ return $cachingSemanticDataLookup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TableFieldUpdater
+ */
+ public function newTableFieldUpdater() {
+
+ $tableFieldUpdater = new TableFieldUpdater(
+ $this->store
+ );
+
+ return $tableFieldUpdater;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return RedirectStore
+ */
+ public function newRedirectStore() {
+
+ $redirectStore = new RedirectStore(
+ $this->store
+ );
+
+ return $redirectStore;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ChangePropListener
+ */
+ public function newChangePropListener() {
+ return new ChangePropListener();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $subject
+ *
+ * @return ChangeOp
+ */
+ public function newChangeOp( DIWikiPage $subject ) {
+
+ $settings = ApplicationFactory::getInstance()->getSettings();
+ $changeOp = new ChangeOp( $subject );
+
+ $changeOp->setTextItemsFlag(
+ $settings->get( 'smwgEnabledFulltextSearch' )
+ );
+
+ return $changeOp;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ProximityPropertyValueLookup
+ */
+ public function newProximityPropertyValueLookup() {
+ return new ProximityPropertyValueLookup( $this->store );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return EntityValueUniquenessConstraintChecker
+ */
+ public function newEntityValueUniquenessConstraintChecker() {
+ return new EntityValueUniquenessConstraintChecker(
+ $this->store,
+ $this->getIteratorFactory()
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ServicesContainer
+ */
+ public function newServicesContainer() {
+
+ $servicesContainer = new ServicesContainer(
+ [
+ 'ProximityPropertyValueLookup' => [
+ '_service' => [ $this, 'newProximityPropertyValueLookup' ],
+ '_type' => ProximityPropertyValueLookup::class
+ ],
+ 'EntityValueUniquenessConstraintChecker' => [
+ '_service' => [ $this, 'newEntityValueUniquenessConstraintChecker' ],
+ '_type' => EntityValueUniquenessConstraintChecker::class
+ ],
+ 'PropertyTableIdReferenceFinder' => function() {
+ static $singleton;
+ return $singleton = $singleton === null ? $this->newPropertyTableIdReferenceFinder() : $singleton;
+ }
+ ]
+ );
+
+ return $servicesContainer;
+ }
+
+ private function getIteratorFactory() {
+ return ApplicationFactory::getInstance()->getIteratorFactory();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder.php
new file mode 100644
index 00000000..c87c82ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\SQLStore\TableBuilder\Table;
+
+/**
+ * @private
+ *
+ * Provides generic creation and updating function for database tables. A builder
+ * that implements this interface is expected to define Database specific
+ * operations and allowing it to be executed on a specific RDBMS back-end.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface TableBuilder {
+
+ /**
+ * Common prefix used by all Semantic MediaWiki tables
+ */
+ const TABLE_PREFIX = 'smw_';
+
+ /**
+ * Processing field activity status
+ */
+ const PROC_FIELD_NEW = 'field.new';
+
+ /**
+ * Processing field activity status
+ */
+ const PROC_FIELD_UPD = 'field.update';
+
+ /**
+ * Processing field activity status
+ */
+ const PROC_FIELD_DROP = 'field.drop';
+
+ /**
+ * On before the creation of tables and indices
+ */
+ const PRE_CREATION = 'pre.creation';
+
+ /**
+ * On after creation of all tables
+ */
+ const POST_CREATION = 'post.creation';
+
+ /**
+ * On after dropping all tables
+ */
+ const POST_DESTRUCTION = 'post.destruction';
+
+ /**
+ * Generic creation and updating function for database tables. Ideally, it
+ * would be able to modify a table's signature in arbitrary ways, but it will
+ * fail for some changes. Its string-based interface is somewhat too
+ * impoverished for a permanent solution. It would be possible to go for update
+ * scripts (specific to each change) in the style of MediaWiki instead.
+ *
+ * Make sure the table of the given name has the given fields, provided
+ * as an array with entries fieldname => typeparams. typeparams should be
+ * in a normalised form and order to match to existing values.
+ *
+ * The function returns an array that includes all columns that have been
+ * changed. For each such column, the array contains an entry
+ * columnname => action, where action is one of 'up', 'new', or 'del'
+ *
+ * @note The function partly ignores the order in which fields are set up.
+ * Only if the type of some field changes will its order be adjusted explicitly.
+ *
+ * @since 2.5
+ *
+ * @param Table $table
+ */
+ public function create( Table $table );
+
+ /**
+ * Removes a table from the RDBMS backend.
+ *
+ * @since 2.5
+ *
+ * @param Table $table
+ */
+ public function drop( Table $table );
+
+ /**
+ * Performs analysis on a key distribution and stores the distribution so
+ * that the query planner can use these statistics to help determine the
+ * most efficient execution plans for queries.
+ *
+ * @since 3.0
+ *
+ * @param Table $table
+ */
+ public function optimize( Table $table );
+
+ /**
+ * Database backends often have different types that need to be used
+ * repeatedly in (Semantic) MediaWiki. This function provides the
+ * preferred type (as a string) for various common kinds of columns.
+ * The input is one of the following strings: 'id' (page id numbers or
+ * similar), 'title' (title strings or similar), 'namespace' (namespace
+ * numbers), 'blob' (longer text blobs), 'iw' (interwiki prefixes).
+ *
+ * @since 2.5
+ *
+ * @param string|FieldType $fieldType
+ *
+ * @return string|false SQL type declaration
+ */
+ public function getStandardFieldType( $fieldType );
+
+ /**
+ * Returns a list of process activities
+ *
+ * @since 3.0
+ *
+ * @param array
+ */
+ public function getLog();
+
+ /**
+ * Allows to check and validate the build on specific events
+ *
+ * @since 2.5
+ *
+ * @param string $event
+ */
+ public function checkOn( $event );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Examiner/HashField.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Examiner/HashField.php
new file mode 100644
index 00000000..0e866368
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Examiner/HashField.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder\Examiner;
+
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use SMW\SQLStore\SQLStore;
+use SMW\Maintenance\PopulateHashField;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.1
+ *
+ * @author mwjames
+ */
+class HashField {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var PopulateHashField
+ */
+ private $populateHashField;
+
+ /**
+ * @since 3.1
+ *
+ * @param SQLStore $store
+ * @param PopulateHashField|null $populateHashField
+ */
+ public function __construct( SQLStore $store, PopulateHashField $populateHashField = null ) {
+ $this->store = $store;
+ $this->populateHashField = $populateHashField;
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @return integer
+ */
+ public static function threshold() {
+ return PopulateHashField::COUNT_SCRIPT_EXECUTION_THRESHOLD;
+ }
+
+ /**
+ * @since 3.1
+ *
+ * @param array $opts
+ */
+ public function check( array $opts = [] ) {
+
+ $this->messageReporter->reportMessage( "Checking smw_hash field consistency ...\n" );
+ require_once $GLOBALS['smwgMaintenanceDir'] . "/populateHashField.php";
+
+ if ( $this->populateHashField === null ) {
+ $this->populateHashField = new PopulateHashField();
+ }
+
+ $this->populateHashField->setStore( $this->store );
+ $this->populateHashField->setMessageReporter( $this->messageReporter );
+
+ $rows = $this->populateHashField->fetchRows();
+ $count = 0;
+
+ if ( $rows !== null ) {
+ $count = $rows->numRows();
+ }
+
+ if ( $count > self::threshold() ) {
+ $this->messageReporter->reportMessage( " ... missing $count rows ...\n" );
+ $this->messageReporter->reportMessage( " ... skipping the `smw_hash` field population ...\n" );
+
+ $this->populateHashField->setComplete( false );
+ } elseif ( $count != 0 ) {
+ $this->populateHashField->populate( $rows );
+ } else {
+ $this->populateHashField->setComplete( true );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/FieldType.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/FieldType.php
new file mode 100644
index 00000000..8b5ba88f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/FieldType.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FieldType {
+
+ /**
+ * @var string
+ */
+ const FIELD_ID = 'id';
+
+ /**
+ * @var string
+ */
+ const FIELD_ID_PRIMARY = 'id_primary';
+
+ /**
+ * @var string
+ */
+ const FIELD_ID_UNSIGNED = 'id_unsigned';
+
+ /**
+ * @var string
+ */
+ const FIELD_TITLE = 'title';
+
+ /**
+ * @var string
+ */
+ const FIELD_HASH = 'hash';
+
+ /**
+ * @var string
+ */
+ const FIELD_NAMESPACE = 'namespace';
+
+ /**
+ * @var string
+ */
+ const FIELD_INTERWIKI = 'interwiki';
+
+ /**
+ * @var string
+ */
+ const FIELD_USAGE_COUNT = 'usage_count';
+
+ /**
+ * @var string
+ */
+ const TYPE_CHAR_NOCASE = 'char_nocase';
+
+ /**
+ * @var string
+ */
+ const TYPE_CHAR_LONG = 'char_long';
+
+ /**
+ * @var string
+ */
+ const TYPE_CHAR_LONG_NOCASE = 'char_long_nocase';
+
+ /**
+ * @var integer
+ */
+ const CHAR_LONG_LENGTH = 300;
+
+ /**
+ * @var string
+ */
+ const TYPE_BOOL = 'boolean';
+
+ /**
+ * @var string
+ */
+ const TYPE_INT = 'integer';
+
+ /**
+ * @var string
+ */
+ const TYPE_INT_UNSIGNED = 'integer_unsigned';
+
+ /**
+ * @var string
+ */
+ const TYPE_TEXT = 'text';
+
+ /**
+ * @var string
+ */
+ const TYPE_BLOB = 'blob';
+
+ /**
+ * @var string
+ */
+ const TYPE_DOUBLE = 'double';
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $type
+ * @param array $fieldTypes
+ */
+ public static function mapType( $type, $fieldTypes = [] ) {
+
+ $fieldType = $type;
+ $auxilary = '';
+
+ // [ FieldType::FIELD_ID, 'NOT NULL' ]
+ if ( is_array( $type ) && count( $type ) > 1 ) {
+ $fieldType = $type[0];
+ $auxilary = ' ' . $type[1];
+ } elseif ( is_array( $type ) ) {
+ $fieldType = $type[0];
+ }
+
+ if ( isset( $fieldTypes[$fieldType] ) ) {
+ $fieldType = $fieldTypes[$fieldType];
+ }
+
+ return $fieldType . $auxilary;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/MySQLTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/MySQLTableBuilder.php
new file mode 100644
index 00000000..7c9a5ebc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/MySQLTableBuilder.php
@@ -0,0 +1,402 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ * @author Marcel Gsteiger
+ * @author Jeroen De Dauw
+ */
+class MySQLTableBuilder extends TableBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getStandardFieldType( $fieldType ) {
+
+ $charLongLength = FieldType::CHAR_LONG_LENGTH;
+
+ $fieldTypes = [
+ // like page_id in MW page table
+ 'id' => 'INT(11) UNSIGNED',
+ // like page_id in MW page table
+ 'id_primary' => 'INT(11) UNSIGNED NOT NULL KEY AUTO_INCREMENT',
+
+ // (see postgres on the difference)
+ 'id_unsigned' => 'INT(11) UNSIGNED',
+
+ // like page_namespace in MW page table
+ 'namespace' => 'INT(11)',
+ // like page_title in MW page table
+ 'title' => 'VARBINARY(255)',
+ // like iw_prefix in MW interwiki table
+ 'interwiki' => 'VARBINARY(32)',
+ 'iw' => 'VARBINARY(32)',
+ 'hash' => 'VARBINARY(40)',
+ // larger blobs of character data, usually not subject to SELECT conditions
+ 'blob' => 'MEDIUMBLOB',
+ 'text' => 'TEXT',
+ 'boolean' => 'TINYINT(1)',
+ 'double' => 'DOUBLE',
+ 'integer' => 'INT(8)',
+ 'char_long' => "VARBINARY($charLongLength)",
+ 'char_nocase' => 'VARCHAR(255) CHARSET utf8 COLLATE utf8_general_ci',
+ 'char_long_nocase' => "VARCHAR($charLongLength) CHARSET utf8 COLLATE utf8_general_ci",
+ 'usage_count' => 'INT(8) UNSIGNED',
+ 'integer_unsigned' => 'INT(8) UNSIGNED'
+ ];
+
+ return FieldType::mapType( $fieldType, $fieldTypes );
+ }
+
+ /** Create */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateTable( $tableName, array $attributes = null ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $sql = '';
+
+ $fieldSql = [];
+ $fields = $attributes['fields'];
+
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $fieldSql[] = "$fieldName " . $this->getStandardFieldType( $fieldType );
+ }
+
+ // @see $wgDBname
+ $dbName = isset( $this->config['wgDBname'] ) ? "`". $this->config['wgDBname'] . "`." : '';
+
+ $sql .= 'CREATE TABLE ' . $dbName . $tableName . ' (' . implode( ',', $fieldSql ) . ') ';
+ $sql .= $this->sql_from( $attributes );
+
+ $this->connection->query( $sql, __METHOD__ );
+ }
+
+ private function sql_from( array $attributes ) {
+
+ // $smwgFulltextSearchTableOptions can define:
+ // - 'mysql' => array( 'ENGINE=MyISAM, DEFAULT CHARSET=utf8' )
+ // - 'mysql' => array( 'ENGINE=MyISAM, DEFAULT CHARSET=utf8', 'WITH PARSER ngram' )
+ if ( isset( $attributes['fulltextSearchTableOptions']['mysql'] ) ) {
+
+ $tableOption = $attributes['fulltextSearchTableOptions']['mysql'];
+
+ // By convention the first index has table specific relevance
+ if ( is_array( $tableOption ) ) {
+ $tableOption = isset( $tableOption[0] ) ? $tableOption[0] : '';
+ }
+
+ return $tableOption;
+ }
+
+ // @see $wgDBTableOptions, This replacement is needed for compatibility,
+ // http://bugs.mysql.com/bug.php?id=17501
+ if ( isset( $this->config['wgDBTableOptions'] ) ) {
+ return str_replace( 'TYPE', 'ENGINE', $this->config['wgDBTableOptions'] );
+ }
+ }
+
+ /** Update */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doUpdateTable( $tableName, array $attributes = null ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $currentFields = $this->getCurrentFields( $tableName );
+
+ $fields = $attributes['fields'];
+ $position = 'FIRST';
+
+ // Loop through all the field definitions, and handle each definition
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $this->doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, $attributes );
+
+ $position = "AFTER $fieldName";
+ $currentFields[$fieldName] = false;
+ }
+
+ // The updated fields have their value set to false, so if a field has a value
+ // that differs from false, it's an obsolete one that should be removed.
+ foreach ( $currentFields as $fieldName => $value ) {
+ if ( $value !== false ) {
+ $this->doDropField( $tableName, $fieldName );
+ }
+ }
+ }
+
+ private function getCurrentFields( $tableName ) {
+
+ $sql = 'DESCRIBE ' . $tableName;
+
+ $res = $this->connection->query( $sql, __METHOD__ );
+ $currentFields = [];
+
+ foreach ( $res as $row ) {
+ $type = strtoupper( $row->Type );
+
+ if ( substr( $type, 0, 8 ) == 'VARCHAR(' ) {
+ $type .= ' binary'; // just assume this to be the case for VARCHAR, though DESCRIBE will not tell us
+ }
+
+ if ( $row->Null != 'YES' ) {
+ $type .= ' NOT NULL';
+ }
+
+ if ( $row->Key == 'PRI' ) { /// FIXME: updating "KEY" is not possible, the below query will fail in this case.
+ $type .= ' KEY';
+ }
+
+ if ( $row->Extra == 'auto_increment' ) {
+ $type .= ' AUTO_INCREMENT';
+ }
+
+ $currentFields[$row->Field] = $type;
+ }
+
+ return $currentFields;
+ }
+
+ private function doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, array $attributes ) {
+
+ if ( !isset( $this->activityLog[$tableName] ) ) {
+ $this->activityLog[$tableName] = [];
+ }
+
+ $fieldType = $this->getStandardFieldType( $fieldType );
+ $default = '';
+
+ if ( isset( $attributes['defaults'][$fieldName] ) ) {
+ $default = "DEFAULT '" . $attributes['defaults'][$fieldName] . "'";
+ }
+
+ if ( !array_key_exists( $fieldName, $currentFields ) ) {
+ $this->doCreateField( $tableName, $fieldName, $position, $fieldType, $default );
+ } elseif ( $currentFields[$fieldName] != $fieldType ) {
+ $this->doUpdateFieldType( $tableName, $fieldName, $position, $currentFields[$fieldName], $fieldType );
+ } else {
+ $this->reportMessage( " ... field $fieldName is fine.\n" );
+ }
+ }
+
+ private function doCreateField( $tableName, $fieldName, $position, $fieldType, $default ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_NEW;
+
+ $this->reportMessage( " ... creating field $fieldName ... " );
+ $this->connection->query( "ALTER TABLE $tableName ADD `$fieldName` $fieldType $default $position", __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doUpdateFieldType( $tableName, $fieldName, $position, $oldFieldType, $newFieldType ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_UPD;
+
+ // Continue to alter the type but silence the output since we cannot get
+ // any better information from MySQL about the types hence we a hack the
+ // message
+ if ( strpos( $oldFieldType, 'binary' ) !== false && strpos( $newFieldType, 'CHARSET utf8 COLLATE utf8_general_ci' ) !== false ) {
+ $this->reportMessage( " ... changing to a CHARSET utf8 field type ... " );
+ } else {
+ $this->reportMessage( " ... changing type of field $fieldName from '$oldFieldType' to '$newFieldType' ... " );
+ }
+
+ // To avoid Error: 1068 Multiple primary key defined when a PRIMARY is involved
+ if ( strpos( $newFieldType, 'AUTO_INCREMENT' ) !== false ) {
+ $this->connection->query( "ALTER TABLE $tableName DROP PRIMARY KEY", __METHOD__ );
+ }
+
+ $this->connection->query( "ALTER TABLE $tableName CHANGE `$fieldName` `$fieldName` $newFieldType $position", __METHOD__ );
+
+ // http://stackoverflow.com/questions/1873085/how-to-convert-from-varbinary-to-char-varchar-in-mysql
+ // http://bugs.mysql.com/bug.php?id=34564
+ if ( strpos( $oldFieldType, 'VARBINARY' ) !== false && strpos( $newFieldType, 'VARCHAR' ) !== false ) {
+ // $this->connection->query( "SELECT CAST($fieldName AS CHAR) from $tableName", __METHOD__ );
+ }
+
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doDropField( $tableName, $fieldName ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_DROP;
+
+ $this->reportMessage( " ... deleting obsolete field $fieldName ... " );
+ $this->connection->query( "ALTER TABLE $tableName DROP COLUMN `$fieldName`", __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ /** Index */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateIndices( $tableName, array $indexOptions = null ) {
+
+ $indices = $indexOptions['indices'];
+
+ // First remove possible obsolete indices
+ $this->doDropObsoleteIndices( $tableName, $indices );
+
+ // Add new indexes.
+ foreach ( $indices as $indexName => $index ) {
+ // If the index is an array, it contains the column
+ // name as first element, and index type as second one.
+ if ( is_array( $index ) ) {
+ $columns = $index[0];
+ $indexType = count( $index ) > 1 ? $index[1] : 'INDEX';
+ } else {
+ $columns = $index;
+ $indexType = 'INDEX';
+ }
+
+ $this->doCreateIndex( $tableName, $indexType, $indexName, $columns, $indexOptions );
+ }
+ }
+
+ private function doDropObsoleteIndices( $tableName, array &$indices ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $currentIndices = $this->getIndexInfo( $tableName );
+
+ $idx = [];
+
+ // #2717
+ // The index info doesn't return length information (...idx1(200),idx2...)
+ // for an index hence to avoid a constant remove/create cycle we eliminate
+ // the length information from the temporary mirror when comparing new and
+ // old; of course we won't detect length changes!
+ foreach ( $indices as $k => $columns ) {
+ $idx[$k] = preg_replace("/\([^)]+\)/", "", $columns );
+ }
+
+ foreach ( $currentIndices as $indexName => $indexColumn ) {
+ // Indices may contain something like array( 'id', 'UNIQUE INDEX' )
+ $id = $this->recursive_array_search( $indexColumn, $idx );
+ if ( $id !== false || $indexName == 'PRIMARY' ) {
+ $this->reportMessage( " ... index $indexColumn is fine.\n" );
+
+ if ( $id !== false ) {
+ unset( $indices[$id] );
+ unset( $idx[$id] );
+ }
+
+ } else { // Duplicate or unrequired index.
+ $this->doDropIndex( $tableName, $indexName, $indexColumn );
+ }
+ }
+ }
+
+ /**
+ * Get the information about all indexes of a table. The result is an
+ * array of format indexname => indexcolumns. The latter is a comma
+ * separated list.
+ *
+ * @return array indexname => columns
+ */
+ private function getIndexInfo( $tableName ) {
+
+ $indices = [];
+
+ $res = $this->connection->query( 'SHOW INDEX FROM ' . $tableName, __METHOD__ );
+
+ if ( !$res ) {
+ return $indices;
+ }
+
+ foreach ( $res as $row ) {
+ if ( !array_key_exists( $row->Key_name, $indices ) ) {
+ $indices[$row->Key_name] = $row->Column_name;
+ } else {
+ $indices[$row->Key_name] .= ',' . $row->Column_name;
+ }
+ }
+
+ return $indices;
+ }
+
+ private function doDropIndex( $tableName, $indexName, $columns ) {
+ $this->reportMessage( " ... removing index $columns ..." );
+ $this->connection->query( 'DROP INDEX ' . $indexName . ' ON ' . $tableName, __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doCreateIndex( $tableName, $indexType, $indexName, $columns, array $indexOptions ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $indexOption = '';
+
+ $this->reportMessage( " ... creating new index $columns ..." );
+
+ // @see MySQLTableBuilder::createExtraSQLFromattributes
+ // @see https://dev.mysql.com/doc/refman/5.7/en/fulltext-search-ngram.html
+ if ( isset( $indexOptions['fulltextSearchTableOptions']['mysql'] ) ) {
+ $indexOption = $indexOptions['fulltextSearchTableOptions']['mysql'];
+
+ // By convention the second index has index specific relevance
+ if ( is_array( $indexOption ) ) {
+ $indexOption = isset( $indexOption[1] ) ? $indexOption[1] : '';
+ }
+ }
+
+ if ( $indexType === 'FULLTEXT' ) {
+ $this->connection->query( "ALTER TABLE $tableName ADD $indexType $columns ($columns) $indexOption", __METHOD__ );
+ } else {
+ $this->connection->query( "ALTER TABLE $tableName ADD $indexType ($columns)", __METHOD__ );
+ }
+
+ $this->reportMessage( "done.\n" );
+ }
+
+ /** Drop */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doDropTable( $tableName ) {
+ $this->connection->query( 'DROP TABLE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ protected function doOptimize( $tableName ) {
+
+ $this->reportMessage( "Checking table $tableName ...\n" );
+
+ // https://dev.mysql.com/doc/refman/5.7/en/analyze-table.html
+ // Performs a key distribution analysis and stores the distribution for
+ // the named table or tables
+ $this->reportMessage( " ... analyze" );
+ $this->connection->query( 'ANALYZE TABLE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+
+ // https://dev.mysql.com/doc/refman/5.7/en/optimize-table.html
+ // Reorganizes the physical storage of table data and associated index data,
+ // to reduce storage space and improve I/O efficiency
+ $this->reportMessage( ", optimize " );
+ $this->connection->query( 'OPTIMIZE TABLE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+
+ $this->reportMessage( "done.\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/PostgresTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/PostgresTableBuilder.php
new file mode 100644
index 00000000..b0547c34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/PostgresTableBuilder.php
@@ -0,0 +1,423 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+use SMW\SQLStore\SQLStore;
+use SMW\MediaWiki\Connection\Sequence;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ * @author Marcel Gsteiger
+ * @author Jeroen De Dauw
+ */
+class PostgresTableBuilder extends TableBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getStandardFieldType( $fieldType ) {
+
+ // serial is a 4 bytes autoincrementing integer (1 to 2147483647)
+
+ $fieldTypes = [
+ // like page_id in MW page table
+ 'id' => 'SERIAL',
+ // like page_id in MW page table
+ 'id_primary' => 'SERIAL NOT NULL PRIMARY KEY',
+
+ // not autoincrementing integer
+ 'id_unsigned' => 'INTEGER',
+
+ // like page_namespace in MW page table
+ 'namespace' => 'BIGINT',
+ // like page_title in MW page table
+ 'title' => 'TEXT',
+ // like iw_prefix in MW interwiki table
+ 'interwiki' => 'TEXT',
+ 'iw' => 'TEXT',
+ 'hash' => 'TEXT',
+ // larger blobs of character data, usually not subject to SELECT conditions
+ 'blob' => 'BYTEA',
+ 'text' => 'TEXT',
+ 'boolean' => 'BOOLEAN',
+ 'double' => 'DOUBLE PRECISION',
+ 'integer' => 'bigint',
+ 'char_long' => 'TEXT',
+ // Requires citext extension
+ 'char_nocase' => 'citext NOT NULL',
+ 'char_long_nocase' => 'citext NOT NULL',
+ 'usage_count' => 'bigint',
+ 'integer_unsigned' => 'INTEGER'
+ ];
+
+ return FieldType::mapType( $fieldType, $fieldTypes );
+ }
+
+ /** Create */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateTable( $tableName, array $attributes = null ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+
+ $fieldSql = [];
+ $fields = $attributes['fields'];
+
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $fieldSql[] = "$fieldName " . $this->getStandardFieldType( $fieldType );
+ }
+
+ $sql = 'CREATE TABLE ' . $tableName . ' (' . implode( ',', $fieldSql ) . ') ';
+
+ $this->connection->query( $sql, __METHOD__ );
+ }
+
+ /** Update */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doUpdateTable( $tableName, array $attributes = null ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $currentFields = $this->getCurrentFields( $tableName );
+
+ $fields = $attributes['fields'];
+ $position = 'FIRST';
+
+ if ( !isset( $this->activityLog[$tableName] ) ) {
+ $this->activityLog[$tableName] = [];
+ }
+
+ // Loop through all the field definitions, and handle each definition
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $this->doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, $attributes );
+
+ $position = "AFTER $fieldName";
+ $currentFields[$fieldName] = false;
+ }
+
+ // The updated fields have their value set to false, so if a field has a value
+ // that differs from false, it's an obsolete one that should be removed.
+ foreach ( $currentFields as $fieldName => $value ) {
+ if ( $value !== false ) {
+ $this->doDropField( $tableName, $fieldName );
+ }
+ }
+ }
+
+ private function getCurrentFields( $tableName ) {
+
+ $tableName = str_replace( '"', '', $tableName );
+ // Use the data dictionary in postgresql to get an output comparable to DESCRIBE.
+/*
+ $sql = <<<EOT
+SELECT
+ a.attname as "Field",
+ upper(pg_catalog.format_type(a.atttypid, a.atttypmod)) as "Type",
+ (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
+ FROM pg_catalog.pg_attrdef d
+ WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as "Extra",
+ case when a.attnotnull THEN 'NO'::text else 'YES'::text END as "Null", a.attnum
+ FROM pg_catalog.pg_attribute a
+ WHERE a.attrelid = (
+ SELECT c.oid
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relname ~ '^($tableName)$'
+ AND pg_catalog.pg_table_is_visible(c.oid)
+ LIMIT 1
+ ) AND a.attnum > 0 AND NOT a.attisdropped
+ ORDER BY a.attnum
+EOT;
+*/
+
+ $sql = "SELECT a.attname as \"Field\","
+ . " upper(pg_catalog.format_type(a.atttypid, a.atttypmod)) as \"Type\","
+ . " (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
+ . " FROM pg_catalog.pg_attrdef d"
+ . " WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as \"Extra\", "
+ . " case when a.attnotnull THEN 'NO'::text else 'YES'::text END as \"Null\", a.attnum"
+ . " FROM pg_catalog.pg_attribute a"
+ . " WHERE a.attrelid = (SELECT c.oid"
+ . " FROM pg_catalog.pg_class c"
+ . " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace"
+ . " WHERE c.relname ~ '^(" . $tableName . ")$'"
+ . " AND pg_catalog.pg_table_is_visible(c.oid)"
+ . " LIMIT 1) AND a.attnum > 0 AND NOT a.attisdropped"
+ . " ORDER BY a.attnum";
+
+ $res = $this->connection->query( $sql, __METHOD__ );
+ $currentFields = [];
+
+ foreach ( $res as $row ) {
+ $type = strtoupper( $row->Type );
+
+ if ( preg_match( '/^nextval\\(.+\\)/i', $row->Extra ) ) {
+ $type = 'SERIAL NOT NULL';
+ } elseif ( $row->Null != 'YES' ) {
+ $type .= ' NOT NULL';
+ }
+
+ $currentFields[$row->Field] = $type;
+ }
+
+ return $currentFields;
+ }
+
+ private function doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, array $attributes ) {
+
+ $fieldType = $this->getStandardFieldType( $fieldType );
+ $keypos = strpos( $fieldType, ' PRIMARY KEY' );
+
+ if ( $keypos > 0 ) {
+ $fieldType = substr( $fieldType, 0, $keypos );
+ }
+
+ $fieldType = strtoupper( $fieldType );
+ $default = '';
+
+ if ( isset( $attributes['defaults'][$fieldName] ) ) {
+ $default = "DEFAULT '" . $attributes['defaults'][$fieldName] . "'";
+ }
+
+ if ( !array_key_exists( $fieldName, $currentFields ) ) {
+ $this->doCreateField( $tableName, $fieldName, $position, $fieldType, $default );
+ } elseif ( $currentFields[$fieldName] != $fieldType ) {
+ $this->reportMessage( " ... changing type of field $fieldName from '$currentFields[$fieldName]' to '$fieldType' ... " );
+
+ $notnullposnew = strpos( $fieldType, ' NOT NULL' );
+
+ if ( $notnullposnew > 0 ) {
+ $fieldType = substr( $fieldType, 0, $notnullposnew );
+ }
+
+ $notnullposold = strpos( $currentFields[$fieldName], ' NOT NULL' );
+ $typeold = strtoupper( ( $notnullposold > 0 ) ? substr( $currentFields[$fieldName], 0, $notnullposold ) : $currentFields[$fieldName] );
+
+ // Added USING statement to avoid
+ // "Query: ALTER TABLE "smw_object_ids" ALTER COLUMN "smw_proptable_hash" TYPE BYTEA ...
+ // Error: 42804 ERROR: column "smw_proptable_hash" cannot be cast automatically to type bytea
+ // HINT: You might need to specify "USING smw_proptable_hash::bytea"."
+
+ if ( $typeold != $fieldType ) {
+ $sql = "ALTER TABLE " . $tableName . " ALTER COLUMN \"" . $fieldName . "\" TYPE " . $fieldType . " USING \"$fieldName\"::$fieldType";
+ $this->connection->query( $sql, __METHOD__ );
+ }
+
+ if ( $notnullposold != $notnullposnew ) {
+ $sql = "ALTER TABLE " . $tableName . " ALTER COLUMN \"" . $fieldName . "\" " . ( $notnullposnew > 0 ? 'SET' : 'DROP' ) . " NOT NULL";
+ $this->connection->query( $sql, __METHOD__ );
+ }
+
+ $this->reportMessage( "done.\n" );
+ } else {
+ $this->reportMessage( " ... field $fieldName is fine.\n" );
+ }
+ }
+
+ private function doCreateField( $tableName, $fieldName, $position, $fieldType, $default ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_NEW;
+
+ $this->reportMessage( " ... creating field $fieldName ... " );
+ $this->connection->query( "ALTER TABLE $tableName ADD \"" . $fieldName . "\" $fieldType $default", __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doDropField( $tableName, $fieldName ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_DROP;
+
+ $this->reportMessage( " ... deleting obsolete field $fieldName ... " );
+ $this->connection->query( 'ALTER TABLE ' . $tableName . ' DROP COLUMN "' . $fieldName . '"', __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ /** Index */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateIndices( $tableName, array $indexOptions = null ) {
+
+ $indices = $indexOptions['indices'];
+ $ix = [];
+
+ // In case an index has a length restriction indexZ(200), remove it since
+ // Postgres doesn't know such syntax
+ foreach ( $indices as $k => $columns ) {
+ $ix[$k] = preg_replace("/\([^)]+\)/", "", $columns );
+ }
+
+ $indices = $ix;
+
+ // First remove possible obsolete indices
+ $this->doDropObsoleteIndices( $tableName, $indices );
+
+ // Add new indexes.
+ foreach ( $indices as $indexName => $index ) {
+ // If the index is an array, it contains the column
+ // name as first element, and index type as second one.
+ if ( is_array( $index ) ) {
+ $columns = $index[0];
+ $indexType = count( $index ) > 1 ? $index[1] : 'INDEX';
+ } else {
+ $columns = $index;
+ $indexType = 'INDEX';
+ }
+
+ $this->doCreateIndex( $tableName, $indexType, $indexName, $columns, $indexOptions );
+ }
+ }
+
+ private function doDropObsoleteIndices( $tableName, array &$indices ) {
+
+ $tableName = $this->connection->tableName( $tableName, 'raw' );
+ $currentIndices = $this->getIndexInfo( $tableName );
+
+ foreach ( $currentIndices as $indexName => $indexColumn ) {
+ // Indices may contain something like array( 'id', 'UNIQUE INDEX' )
+ $id = $this->recursive_array_search( $indexColumn, $indices );
+ if ( $id !== false || $indexName == 'PRIMARY' ) {
+ $this->reportMessage( " ... index $indexColumn is fine.\n" );
+
+ if ( $id !== false ) {
+ unset( $indices[$id] );
+ }
+
+ } else { // Duplicate or unrequired index.
+ $this->doDropIndex( $tableName, $indexName, $indexColumn );
+ }
+ }
+ }
+
+ private function doCreateIndex( $tableName, $indexType, $indexName, $columns, array $indexOptions ) {
+
+ if ( $indexType === 'FULLTEXT' ) {
+ return $this->reportMessage( " ... skipping the fulltext index creation ..." );
+ }
+
+ $tableName = $this->connection->tableName( $tableName, 'raw' );
+ $indexName = $this->getCumulatedIndexName( $tableName, $columns );
+
+ $this->reportMessage( " ... creating new index $columns ..." );
+
+ if ( $this->connection->indexInfo( $tableName, $indexName ) === false ) {
+ $this->connection->query( "CREATE $indexType $indexName ON $tableName ($columns)", __METHOD__ );
+ }
+
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function getCumulatedIndexName( $tableName, $columns ) {
+ // Identifiers -- table names, column names, constraint names,
+ // etc. -- are limited to a maximum length of 63 bytes
+ return str_replace( '__' , '_', "{$tableName}_idx_" . str_replace( [ '_', 'smw', ',' ], [ '', '_', '_' ], $columns ) );
+ }
+
+ private function getIndexInfo( $tableName ) {
+
+ $indices = [];
+
+ $sql = "SELECT i.relname AS indexname,"
+ . " pg_get_indexdef(i.oid) AS indexdef, "
+ . " replace(substring(pg_get_indexdef(i.oid) from E'\\\\((.*)\\\\)'), ' ' , '') AS indexcolumns"
+ . " FROM pg_index x"
+ . " JOIN pg_class c ON c.oid = x.indrelid"
+ . " JOIN pg_class i ON i.oid = x.indexrelid"
+ . " LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
+ . " LEFT JOIN pg_tablespace t ON t.oid = i.reltablespace"
+ . " WHERE c.relkind = 'r'::\"char\" AND i.relkind = 'i'::\"char\""
+ . " AND c.relname = '" . $tableName . "'"
+ . " AND NOT pg_get_indexdef(i.oid) ~ '^CREATE UNIQUE INDEX'";
+
+ $res = $this->connection->query( $sql, __METHOD__ );
+
+ if ( !$res ) {
+ return [];
+ }
+
+ foreach ( $res as $row ) {
+ $indices[$row->indexname] = $row->indexcolumns;
+ }
+
+ return $indices;
+ }
+
+ private function doDropIndex( $tableName, $indexName, $columns ) {
+ $this->reportMessage( " ... removing index $columns ..." );
+ $this->connection->query( 'DROP INDEX IF EXISTS ' . $indexName, __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ /** Drop */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doDropTable( $tableName ) {
+ // Function: SMW\SQLStore\TableBuilder\PostgresTableBuilder::doDropTable
+ // Error: 2BP01 ERROR: cannot drop table smw_object_ids because other objects depend on it
+ // DETAIL: default for table sunittest_smw_object_ids column smw_id depends on sequence smw_object_ids_smw_id_seq
+ // HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ $this->connection->query( 'DROP TABLE IF EXISTS ' . $this->connection->tableName( $tableName ) . ' CASCADE', __METHOD__ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ protected function doOptimize( $tableName ) {
+
+ $this->reportMessage( "Checking table $tableName ...\n" );
+
+ // https://www.postgresql.org/docs/9.0/static/sql-analyze.html
+ $this->reportMessage( " ... analyze " );
+ $this->connection->query( 'ANALYZE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+
+ $this->reportMessage( "done.\n" );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function checkOn( $event ) {
+ if ( $event === self::POST_CREATION ) {
+ $this->doCheckOnPostCreation();
+ }
+ }
+
+ private function doCheckOnPostCreation() {
+
+ $sequence = new Sequence( $this->connection );
+
+ // To avoid things like:
+ // "Error: 23505 ERROR: duplicate key value violates unique constraint "smw_object_ids_pkey""
+ $seq_num = $sequence->restart( SQLStore::ID_TABLE, 'smw_id' );
+
+ $this->reportMessage( "Checking `smw_id` sequence consistency ...\n" );
+ $this->reportMessage( " ... setting sequence to {$seq_num} ...\n" );
+ $this->reportMessage( " ... done.\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/SQLiteTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/SQLiteTableBuilder.php
new file mode 100644
index 00000000..acd7e3c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/SQLiteTableBuilder.php
@@ -0,0 +1,373 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ * @author Marcel Gsteiger
+ * @author Jeroen De Dauw
+ */
+class SQLiteTableBuilder extends TableBuilder {
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getStandardFieldType( $fieldType ) {
+
+ $charLongLength = FieldType::CHAR_LONG_LENGTH;
+
+ $fieldTypes = [
+ // like page_id in MW page table
+ 'id' => 'INTEGER',
+ 'id_primary' => 'INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT',
+
+ // (see postgres, mysql on the difference)
+ 'id_unsigned' => 'INTEGER',
+
+ // like page_namespace in MW page table
+ 'namespace' => 'INT(11)',
+ // like page_title in MW page table
+ 'title' => 'VARBINARY(255)',
+ // like iw_prefix in MW interwiki table
+ 'interwiki' => 'TEXT',
+ 'iw' => 'TEXT',
+ 'hash' => 'VARBINARY(40)',
+ // larger blobs of character data, usually not subject to SELECT conditions
+ 'blob' => 'MEDIUMBLOB',
+ 'text' => 'TEXT',
+ 'boolean' => 'TINYINT(1)',
+ 'double' => 'DOUBLE',
+ 'integer' => 'INT(8)',
+ 'char_long' => "VARBINARY($charLongLength)",
+ 'char_nocase' => 'VARCHAR(255) NOT NULL COLLATE NOCASE',
+ 'char_long_nocase' => "VARCHAR($charLongLength) NOT NULL COLLATE NOCASE",
+ 'usage_count' => 'INT(8)',
+ 'integer_unsigned' => 'INTEGER'
+ ];
+
+ return FieldType::mapType( $fieldType, $fieldTypes );
+ }
+
+ /** Create */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateTable( $tableName, array $attributes = null ) {
+
+ $mode = '';
+ $option = '';
+
+ $ftsOptions = null;
+ $tableName = $this->connection->tableName( $tableName );
+
+ if ( isset( $attributes['fulltextSearchTableOptions']['sqlite'] ) ) {
+ $ftsOptions = $attributes['fulltextSearchTableOptions']['sqlite'];
+ }
+
+ // Filter extra module options
+ // @see https://www.sqlite.org/fts3.html#fts4_options
+ //
+ // $smwgFulltextSearchTableOptions can define:
+ // - 'sqlite' => array( 'FTS4' )
+ // - 'sqlite' => array( 'FTS4', 'tokenize=porter' )
+ if ( $ftsOptions !== null && is_array( $ftsOptions ) ) {
+ $mode = isset( $ftsOptions[0] ) ? $ftsOptions[0] : '';
+ $option = isset( $ftsOptions[1] ) ? $ftsOptions[1] : '';
+ } elseif ( $ftsOptions !== null ) {
+ $mode = $ftsOptions;
+ }
+
+ $fieldSql = [];
+ $fields = $attributes['fields'];
+
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $fieldSql[] = "$fieldName " . $this->getStandardFieldType( $fieldType );
+ }
+
+ if ( $mode === '' ) {
+ $sql = 'CREATE TABLE ' . $tableName .'(' . implode( ',', $fieldSql ) . ') ';
+ } else {
+ $sql = 'CREATE VIRTUAL TABLE ' . $tableName . ' USING ' . strtolower( $mode ) .'(' . implode( ',', $fieldSql ) . $option . ') ';
+ }
+
+ $this->connection->query( $sql, __METHOD__ );
+ }
+
+ /** Update */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doUpdateTable( $tableName, array $attributes = null ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $currentFields = $this->getCurrentFields( $tableName );
+
+ $fields = $attributes['fields'];
+ $position = 'FIRST';
+
+ // Loop through all the field definitions, and handle each definition for either postgres or MySQL.
+ foreach ( $fields as $fieldName => $fieldType ) {
+ $this->doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, $attributes );
+
+ $position = "AFTER $fieldName";
+ $currentFields[$fieldName] = false;
+ }
+
+ // The updated fields have their value set to false, so if a field has a value
+ // that differs from false, it's an obsolete one that should be removed.
+ foreach ( $currentFields as $fieldName => $value ) {
+ if ( $value !== false ) {
+ $this->doDropField( $tableName, $fieldName, $attributes );
+ }
+ }
+ }
+
+ private function getCurrentFields( $tableName ) {
+
+ $sql = 'PRAGMA table_info(' . $tableName . ')';
+
+ $res = $this->connection->query( $sql, __METHOD__ );
+ $currentFields = [];
+
+ foreach ( $res as $row ) {
+ $row->Field = $row->name;
+ $row->Type = $row->type;
+ $type = $row->type;
+
+ if ( $row->notnull == '1' ) {
+ $type .= ' NOT NULL';
+ }
+
+ if ( $row->pk == '1' ) {
+ $type .= ' PRIMARY KEY AUTOINCREMENT';
+ }
+
+ $currentFields[$row->Field] = $type;
+ }
+
+ return $currentFields;
+ }
+
+ private function doUpdateField( $tableName, $fieldName, $fieldType, $currentFields, $position, array $attributes ) {
+
+ if ( !isset( $this->activityLog[$tableName] ) ) {
+ $this->activityLog[$tableName] = [];
+ }
+
+ $fieldType = $this->getStandardFieldType( $fieldType );
+ $default = '';
+
+ if ( isset( $attributes['defaults'][$fieldName] ) ) {
+ $default = "DEFAULT '" . $attributes['defaults'][$fieldName] . "'";
+ }
+
+ if ( !array_key_exists( $fieldName, $currentFields ) ) {
+ $this->doCreateField( $tableName, $fieldName, $position, $fieldType, $default );
+ } elseif ( $currentFields[$fieldName] != $fieldType ) {
+ $this->doUpdateFieldType( $tableName, $fieldName, $position, $currentFields[$fieldName], $fieldType );
+ } else {
+ $this->reportMessage( " ... field $fieldName is fine.\n" );
+ }
+ }
+
+ private function doCreateField( $tableName, $fieldName, $position, $fieldType, $default ) {
+
+ if ( strpos( $tableName, 'ft_search' ) !== false ) {
+ return $this->reportMessage( " ... virtual tables can not be altered in SQLite ...\n" );
+ }
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_NEW;
+
+ if ( $default === '' ) {
+ // @see https://www.sqlite.org/lang_altertable.html states that
+ // "If a NOT NULL constraint is specified, then the column must have a default value other than NULL."
+ $default = "DEFAULT NULL";
+
+ // Add DEFAULT '' to avoid
+ // Query: ALTER TABLE sunittest_rdbms_test ADD `t_num` INT(8) NOT NULL
+ // Function: SMW\SQLStore\TableBuilder\SQLiteTableBuilder::doCreateField
+ // Error: 1 Cannot add a NOT NULL column with default value NULL
+ if ( strpos( $fieldType, 'NOT NULL' ) !== false ) {
+ $default = "DEFAULT ''";
+ }
+ }
+
+ $this->reportMessage( " ... creating field $fieldName ... " );
+ $this->connection->query( "ALTER TABLE $tableName ADD `$fieldName` $fieldType $default", __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doUpdateFieldType( $tableName, $fieldName, $position, $oldFieldType, $newFieldType ) {
+ $this->reportMessage( " ... changing field type is not supported in SQLite (http://www.sqlite.org/omitted.html) \n" );
+ $this->reportMessage( " Please delete and reinitialize the tables to remove obsolete data, or just keep it.\n" );
+ }
+
+ private function doDropField( $tableName, $fieldName, $attributes ) {
+
+ $this->activityLog[$tableName][$fieldName] = self::PROC_FIELD_DROP;
+
+ $fields = $attributes['fields'];
+ $temp_table = "{$tableName}_temp";
+
+ // https://stackoverflow.com/questions/5938048/delete-column-from-sqlite-table
+ // Deleting obsolete fields is not possible in SQLite therefore create a
+ // temp table, copy the content, remove the table with obsolete/ fields,
+ // and rename the temp table
+ $field_def = [];
+ $field_list = [];
+
+ foreach ( $fields as $field => $type ) {
+ $field_def[] = "$field " . $this->getStandardFieldType( $type );
+ $field_list[] = $field;
+ }
+
+ $this->reportMessage( " ... field $fieldName is obsolete ...\n" );
+ $this->reportMessage( " ... creating a temporary table ...\n" );
+ $this->connection->query( 'DROP TABLE IF EXISTS ' . $temp_table, __METHOD__ );
+ $this->connection->query( 'CREATE TABLE ' . $temp_table .' (' . implode( ',', $field_def ) . ') ', __METHOD__ );
+ $this->reportMessage( " ... copying table contents ...\n" );
+ $this->connection->query( 'INSERT INTO ' . $temp_table . ' SELECT ' . implode( ',', $field_list ) . ' FROM ' . $tableName, __METHOD__ );
+ $this->reportMessage( " ... dropping table with obsolete field definitions ...\n" );
+ $this->connection->query( 'DROP TABLE IF EXISTS ' . $tableName, __METHOD__ );
+ $this->reportMessage( " ... renaming temporary table to $tableName ...\n" );
+ $this->connection->query( 'ALTER TABLE ' . $temp_table . ' RENAME TO ' . $tableName, __METHOD__ );
+ $this->reportMessage( " ... done.\n" );
+ }
+
+ /** Index */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doCreateIndices( $tableName, array $indexOptions = null ) {
+
+ $indices = $indexOptions['indices'];
+ $ix = [];
+
+ // In case an index has a length restriction indexZ(200), remove it since
+ // SQLite doesn't know such syntax
+ foreach ( $indices as $k => $columns ) {
+ $ix[$k] = preg_replace("/\([^)]+\)/", "", $columns );
+ }
+
+ $indices = $ix;
+
+ // First remove possible obsolete indices
+ $this->doDropObsoleteIndices( $tableName, $indices );
+
+ // Add new indexes.
+ foreach ( $indices as $indexName => $index ) {
+ // If the index is an array, it contains the column
+ // name as first element, and index type as second one.
+ if ( is_array( $index ) ) {
+ $columns = $index[0];
+ $indexType = count( $index ) > 1 ? $index[1] : 'INDEX';
+ } else {
+ $columns = $index;
+ $indexType = 'INDEX';
+ }
+
+ $this->doCreateIndex( $tableName, $indexType, $indexName, $columns, $indexOptions );
+ }
+ }
+
+ private function doDropObsoleteIndices( $tableName, array &$indices ) {
+
+ $currentIndices = $this->getIndexInfo( $tableName );
+
+ // TODO We do not currently get the right column definitions in
+ // SQLite; hence we can only drop all indexes. Wasteful.
+ foreach ( $currentIndices as $indexName => $indexColumn ) {
+ $this->doDropIndex( $tableName, $indexName, $indexColumn );
+ }
+ }
+
+ private function getIndexInfo( $tableName ) {
+
+ $tableName = $this->connection->tableName( $tableName );
+ $indices = [];
+
+ $res = $this->connection->query( 'PRAGMA index_list(' . $tableName . ')', __METHOD__ );
+
+ if ( !$res ) {
+ return [];
+ }
+
+ foreach ( $res as $row ) {
+ /// FIXME The value should not be $row->name below?!
+ if ( !array_key_exists( $row->name, $indices ) ) {
+ $indices[$row->name] = $row->name;
+ } else {
+ $indices[$row->name] .= ',' . $row->name;
+ }
+ }
+
+ return $indices;
+ }
+
+ private function doDropIndex( $tableName, $indexName, $columns ) {
+ $this->reportMessage( " ... removing index $columns ..." );
+ $this->connection->query( 'DROP INDEX ' . $indexName, __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ private function doCreateIndex( $tableName, $indexType, $indexName, $columns, array $indexOptions ) {
+
+ if ( $indexType === 'FULLTEXT' ) {
+ return $this->reportMessage( " ... skipping the fulltext index creation ..." );
+ }
+
+ if ( strpos( $tableName, 'ft_search' ) !== false ) {
+ return $this->reportMessage( " ... virtual tables can not be altered in SQLite ...\n" );
+ }
+
+ $tableName = $this->connection->tableName( $tableName );
+ $indexName = "{$tableName}_index{$indexName}";
+
+ $this->reportMessage( " ... creating new index $columns ..." );
+ $this->connection->query( "CREATE $indexType $indexName ON $tableName ($columns)", __METHOD__ );
+ $this->reportMessage( "done.\n" );
+ }
+
+ /** Drop */
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ protected function doDropTable( $tableName ) {
+ $this->connection->query( 'DROP TABLE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ protected function doOptimize( $tableName ) {
+
+ $this->reportMessage( "Checking table $tableName ...\n" );
+
+ // https://sqlite.org/lang_analyze.html
+ $this->reportMessage( " ... analyze " );
+ $this->connection->query( 'ANALYZE ' . $this->connection->tableName( $tableName ), __METHOD__ );
+
+ $this->reportMessage( "done.\n" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Table.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Table.php
new file mode 100644
index 00000000..5565bda9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/Table.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+use RuntimeException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Table {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param string $name
+ */
+ public function __construct( $name ) {
+ $this->name = $name;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string
+ */
+ public function getHash() {
+ return json_encode( $this->attributes );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getAttributes() {
+ return $this->attributes;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @param mixed
+ */
+ public function get( $key ) {
+
+ if ( !isset( $this->attributes[$key] ) ) {
+ throw new RuntimeException( "$key is a reserved option key." );
+ }
+
+ return $this->attributes[$key];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $fieldName
+ * @param string|array $fieldType
+ */
+ public function addColumn( $fieldName, $fieldType ) {
+ $this->attributes['fields'][$fieldName] = $fieldType;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $index
+ * @param string|null $key
+ */
+ public function addIndex( $index, $key = null ) {
+ if ( $key !== null ) {
+ $this->attributes['indices'][$key] = $index;
+ } else {
+ $this->attributes['indices'][] = $index;
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $fieldName
+ * @param string|int $default
+ */
+ public function addDefault( $fieldName, $default ) {
+ $this->attributes['defaults'][$fieldName] = $default;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $key
+ * @param string|array $option
+ *
+ * @throws RuntimeException
+ */
+ public function addOption( $key, $option ) {
+
+ if ( $key === 'fields' || $key === 'indices' || $key === 'defaults' ) {
+ throw new RuntimeException( "$key is a reserved option key." );
+ }
+
+ $this->attributes[$key] = $option;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TableBuilder.php
new file mode 100644
index 00000000..9f39a22c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TableBuilder.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+use DatabaseBase;
+use Onoi\MessageReporter\MessageReporter;
+use Onoi\MessageReporter\MessageReporterAware;
+use RuntimeException;
+use SMW\SQLStore\TableBuilder as TableBuilderInterface;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+abstract class TableBuilder implements TableBuilderInterface, MessageReporterAware, MessageReporter {
+
+ /**
+ * @var DatabaseBase
+ */
+ protected $connection;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var array
+ */
+ protected $config = [];
+
+ /**
+ * @var array
+ */
+ protected $activityLog = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param DatabaseBase $connection
+ */
+ protected function __construct( DatabaseBase $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DatabaseBase $connection
+ *
+ * @return TableBuilder
+ * @throws RuntimeException
+ */
+ public static function factory( DatabaseBase $connection ) {
+
+ $instance = null;
+
+ switch ( $connection->getType() ) {
+ case 'mysql':
+ $instance = new MySQLTableBuilder( $connection );
+ break;
+ case 'sqlite':
+ $instance = new SQLiteTableBuilder( $connection );
+ break;
+ case 'postgres':
+ $instance = new PostgresTableBuilder( $connection );
+ break;
+ }
+
+ if ( $instance === null ) {
+ throw new RuntimeException( "Unknown or unsupported DB type " . $connection->getType() );
+ }
+
+ $instance->addConfig( 'wgDBname', $GLOBALS['wgDBname'] );
+ $instance->addConfig( 'wgDBTableOptions', $GLOBALS['wgDBTableOptions'] );
+
+ return $instance;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|integer $key
+ * @param mixed
+ */
+ public function addConfig( $key, $value ) {
+ $this->config[$key] = $value;
+ }
+
+ /**
+ * @see MessageReporterAware::setMessageReporter
+ *
+ * @since 2.5
+ *
+ * @param MessageReporter $messageReporter
+ */
+ public function setMessageReporter( MessageReporter $messageReporter ) {
+ $this->messageReporter = $messageReporter;
+ }
+
+ /**
+ * @see MessageReporter::reportMessage
+ *
+ * @since 2.5
+ *
+ * @param string $message
+ */
+ public function reportMessage( $message ) {
+
+ if ( $this->messageReporter === null ) {
+ return;
+ }
+
+ $this->messageReporter->reportMessage( $message );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function getStandardFieldType( $fieldType ) {
+ return false;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function create( Table $table ) {
+
+ $attributes = $table->getAttributes();
+ $tableName = $table->getName();
+
+ $this->reportMessage( "Checking table $tableName ...\n" );
+
+ if ( $this->connection->tableExists( $tableName ) === false ) { // create new table
+ $this->reportMessage( " Table not found, now creating...\n" );
+ $this->doCreateTable( $tableName, $attributes );
+ } else {
+ $this->reportMessage( " Table already exists, checking structure ...\n" );
+ $this->doUpdateTable( $tableName, $attributes );
+ }
+
+ $this->reportMessage( " ... done.\n" );
+
+ if ( !isset( $attributes['indices'] ) ) {
+ return $this->reportMessage( "No index structures for table $tableName ...\n" );
+ }
+
+ $this->reportMessage( "Checking index structures for table $tableName ...\n" );
+ $this->doCreateIndices( $tableName, $attributes );
+
+ $this->reportMessage( " ... done.\n" );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function drop( Table $table ) {
+
+ $tableName = $table->getName();
+
+ if ( $this->connection->tableExists( $tableName ) === false ) { // create new table
+ return $this->reportMessage( " ... $tableName not found, skipping removal.\n" );
+ }
+
+ $this->doDropTable( $tableName );
+ $this->reportMessage( " ... dropped table $tableName.\n" );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function optimize( Table $table ) {
+ $this->doOptimize( $table->getName() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $event
+ */
+ public function checkOn( $event ) {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function getLog() {
+ return $this->activityLog;
+ }
+
+ /**
+ * @param string $tableName
+ * @param array $tableOptions
+ */
+ abstract protected function doCreateTable( $tableName, array $tableOptions = null);
+
+ /**
+ * @param string $tableName
+ * @param array $tableOptions
+ */
+ abstract protected function doUpdateTable( $tableName, array $tableOptions = null );
+
+ /**
+ * @param string $tableName
+ * @param array $indexOptions
+ */
+ abstract protected function doCreateIndices( $tableName, array $indexOptions = null );
+
+ /**
+ * @param string $tableName
+ */
+ abstract protected function doDropTable( $tableName );
+
+ /**
+ * @param string $tableName
+ */
+ abstract protected function doOptimize( $tableName );
+
+ // #1978
+ // http://php.net/manual/en/function.array-search.php
+ protected function recursive_array_search( $needle, $haystack ) {
+ foreach( $haystack as $key => $value ) {
+ $current_key = $key;
+
+ if ( $needle === $value or ( is_array( $value ) && $this->recursive_array_search( $needle, $value ) !== false ) ) {
+ return $current_key;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TemporaryTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TemporaryTableBuilder.php
new file mode 100644
index 00000000..fac1bd90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableBuilder/TemporaryTableBuilder.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\SQLStore\TableBuilder;
+
+use SMW\MediaWiki\Database;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+class TemporaryTableBuilder {
+
+ /**
+ * @var Database
+ */
+ private $connection;
+
+ /**
+ * @var boolean
+ */
+ private $autoCommitFlag = false;
+
+ /**
+ * @since 2.3
+ *
+ * @param Database $connection
+ */
+ public function __construct( Database $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * @see $smwgQTemporaryTablesWithAutoCommit
+ * @since 2.5
+ *
+ * @param boolean $autoCommitFlag
+ */
+ public function setAutoCommitFlag( $autoCommitFlag ) {
+ $this->autoCommitFlag = (bool)$autoCommitFlag;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $tableName
+ */
+ public function create( $tableName ) {
+
+ if ( $this->autoCommitFlag ) {
+ $this->connection->setFlag( Database::AUTO_COMMIT );
+ }
+
+ $this->connection->query(
+ $this->getSQLCodeFor( $tableName ),
+ __METHOD__,
+ false
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $tableName
+ */
+ public function drop( $tableName ) {
+
+ if ( $this->autoCommitFlag ) {
+ $this->connection->setFlag( Database::AUTO_COMMIT );
+ }
+
+ $this->connection->query(
+ "DROP TEMPORARY TABLE " . $tableName,
+ __METHOD__,
+ false
+ );
+ }
+
+ /**
+ * Get SQL code suitable to create a temporary table of the given name, used
+ * to store ids.
+ *
+ * MySQL can do that simply by creating new temporary tables. PostgreSQL first
+ * checks if such a table exists, so the code is ready to reuse existing tables
+ * if the code was modified to keep them after query answering. Also, PostgreSQL
+ * tables will use a RULE to achieve built-in duplicate elimination. The latter
+ * is done using INSERT IGNORE in MySQL.
+ *
+ * @param string $tableName
+ *
+ * @return string
+ */
+ private function getSQLCodeFor( $tableName ) {
+ // PostgreSQL: no memory tables, use RULE to emulate INSERT IGNORE
+ if ( $this->connection->isType( 'postgres' ) ) {
+
+ // Remove any double quotes from the name
+ $tableName = str_replace( '"', '', $tableName );
+
+ return "DO \$\$BEGIN "
+ . " IF EXISTS(SELECT NULL FROM pg_tables WHERE tablename='{$tableName}' AND schemaname = ANY (current_schemas(true))) "
+ . " THEN DELETE FROM {$tableName}; "
+ . " ELSE "
+ . " CREATE TEMPORARY TABLE {$tableName} (id SERIAL); "
+ . " CREATE RULE {$tableName}_ignore AS ON INSERT TO {$tableName} WHERE (EXISTS (SELECT 1 FROM {$tableName} "
+ . " WHERE ({$tableName}.id = new.id))) DO INSTEAD NOTHING; "
+ . " END IF; "
+ . "END\$\$";
+ }
+
+ // MySQL_ just a temporary table, use INSERT IGNORE later
+ return "CREATE TEMPORARY TABLE " . $tableName . "( id INT UNSIGNED KEY ) ENGINE=MEMORY";
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableFieldUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableFieldUpdater.php
new file mode 100644
index 00000000..d9fbcfdb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableFieldUpdater.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\MediaWiki\Collator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TableFieldUpdater {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var Collator
+ */
+ private $collator;
+
+ /**
+ * @since 3.0
+ *
+ * @param SQLStore $store
+ * @param Collator|null $collator
+ */
+ public function __construct( SQLStore $store, Collator $collator = null ) {
+ $this->store = $store;
+ $this->collator = $collator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param string $searchKey
+ */
+ public function updateSortField( $id, $searchKey ) {
+
+ if ( $this->collator === null ) {
+ $this->collator = Collator::singleton();
+ }
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $connection->beginAtomicTransaction( __METHOD__ );
+
+ // #2089 (MySQL 5.7 complained with "Data too long for column")
+ $searchKey = mb_substr( $searchKey, 0, 254 );
+
+ $connection->update(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_sortkey' => $searchKey,
+ 'smw_sort' => $this->collator->getSortKey( $searchKey )
+ ],
+ [ 'smw_id' => $id ],
+ __METHOD__
+ );
+
+ $connection->endAtomicTransaction( __METHOD__ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param intege $rev_id
+ */
+ public function updateRevField( $sid, $rev_id ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ $connection->update(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_rev' => $rev_id
+ ],
+ [
+ 'smw_id' => $sid
+ ],
+ __METHOD__
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php
new file mode 100644
index 00000000..690a3d97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php
@@ -0,0 +1,344 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Onoi\MessageReporter\NullMessageReporter;
+use SMW\DIProperty;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\MediaWiki\Collator;
+use SMW\PropertyRegistry;
+use SMW\SQLStore\TableBuilder\Table;
+use SMW\SQLStore\Installer;
+use SMW\SQLStore\TableBuilder\Examiner\HashField;
+use SMWSql3SmwIds;
+
+/**
+ * @private
+ *
+ * Allows to execute SQLStore or table specific examination tasks that are
+ * expected to be part of the installation or removal routine.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableIntegrityExaminer {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var HashField
+ */
+ private $hashField;
+
+ /**
+ * @var array
+ */
+ private $predefinedProperties = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ * @param HashField $hashField
+ */
+ public function __construct( SQLStore $store, HashField $hashField ) {
+ $this->store = $store;
+ $this->hashField = $hashField;
+ $this->messageReporter = new NullMessageReporter();
+ $this->setPredefinedPropertyList( PropertyRegistry::getInstance()->getPropertyList() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyList
+ */
+ public function setPredefinedPropertyList( array $propertyList ) {
+
+ $fixedPropertyList = SMWSql3SmwIds::$special_ids;
+ $predefinedPropertyList = [];
+
+ foreach ( $propertyList as $key => $val ) {
+ $predefinedPropertyList[$key] = null;
+
+ if ( isset( $fixedPropertyList[$key] ) ) {
+ $predefinedPropertyList[$key] = $fixedPropertyList[$key];
+ } elseif ( is_integer( $val ) ) {
+ $predefinedPropertyList[$key] = $val;
+ }
+ }
+
+ $this->predefinedPropertyList = $predefinedPropertyList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param TableBuilder $tableBuilder
+ */
+ public function checkOnPostCreation( TableBuilder $tableBuilder ) {
+
+ $this->checkPredefinedPropertyIndices();
+
+ $this->hashField->setMessageReporter( $this->messageReporter );
+ $this->hashField->check();
+
+ $this->checkSortField( $tableBuilder->getLog() );
+
+ // Call out for RDBMS specific implementations
+ $tableBuilder->checkOn( TableBuilder::POST_CREATION );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param TableBuilder $tableBuilder
+ */
+ public function checkOnPostDestruction( TableBuilder $tableBuilder ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Find orphaned tables that have not been removed but were produced and
+ // handled by SMW
+ foreach ( $connection->listTables() as $table ) {
+ if ( strpos( $table, TableBuilder::TABLE_PREFIX ) !== false ) {
+
+ // Remove any MW specific prefix at this point which will be
+ // handled by the DB class (abcsmw_foo -> smw_foo)
+ $tableBuilder->drop( new Table( strstr( $table, TableBuilder::TABLE_PREFIX ) ) );
+ }
+ }
+
+ // Call out for RDBMS specific implementations
+ $tableBuilder->checkOn( TableBuilder::POST_DESTRUCTION );
+ }
+
+ /**
+ * Create some initial DB entries for important built-in properties. Having
+ * the DB contents predefined allows us to safe DB calls when certain data
+ * is needed. At the same time, the entries in the DB make sure that DB-based
+ * functions work as with all other properties.
+ */
+ private function checkPredefinedPropertyIndices() {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ $this->messageReporter->reportMessage( "Checking predefined properties ...\n" );
+ $this->checkPredefinedPropertyUpperbound();
+
+ // now write actual properties; do that each time, it is cheap enough
+ // and we can update sortkeys by current language
+ $this->messageReporter->reportMessage( " ... initialize predefined properties ...\n" );
+
+ foreach ( $this->predefinedPropertyList as $prop => $id ) {
+
+ try{
+ $property = new DIProperty( $prop );
+ } catch ( PredefinedPropertyLabelMismatchException $e ) {
+ $property = null;
+ $this->messageReporter->reportMessage( " ... skipping {$prop} due to invalid registration ...\n" );
+ }
+
+ if ( $property === null ) {
+ continue;
+ }
+
+ $this->updatePredefinedProperty( $property, $id );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ private function checkPredefinedPropertyUpperbound() {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Check if we already have this structure
+ $upperbound = SQLStore::FIXED_PROPERTY_ID_UPPERBOUND;
+ $legacyBound = 50;
+
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ 'smw_id',
+ 'smw_iw=' . $connection->addQuotes( SMW_SQL3_SMWBORDERIW )
+ );
+
+ if ( $row !== false && $row->smw_id == $upperbound ) {
+ return $this->messageReporter->reportMessage( " ... space for internal properties already allocated.\n" );
+ } elseif ( $row === false ) {
+ $currentUpperbound = $legacyBound;
+ } else {
+ $currentUpperbound = $row->smw_id;
+
+ // Delete the current upperbound to avoid having a duplicate border
+ $connection->delete(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' => $currentUpperbound ],
+ __METHOD__
+ );
+ }
+
+ $this->messageReporter->reportMessage( " ... allocating space for internal properties ...\n" );
+ $this->store->getObjectIds()->moveSMWPageID( $upperbound );
+
+ $connection->insert(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id' => $upperbound,
+ 'smw_title' => '',
+ 'smw_namespace' => 0,
+ 'smw_iw' => SMW_SQL3_SMWBORDERIW,
+ 'smw_subobject' => '',
+ 'smw_sortkey' => ''
+ ],
+ __METHOD__
+ );
+
+ if ( $currentUpperbound == $upperbound ) {
+ return $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ if ( $currentUpperbound < $upperbound ) {
+ $this->messageReporter->reportMessage( " ... moving from $currentUpperbound to $upperbound upperbound (may take a moment) ..." );
+ $this->messageReporter->reportMessage( " " );
+ }
+
+ for ( $i = $currentUpperbound; $i < $upperbound; $i++ ) {
+
+ if ( ( $i - $currentUpperbound ) % 60 === 0 ) {
+ $this->messageReporter->reportMessage( "\n " );
+ }
+
+ $this->messageReporter->reportMessage( "." );
+ $this->store->getObjectIds()->moveSMWPageID( $i );
+ }
+
+ $this->messageReporter->reportMessage( "\n ... done.\n" );
+ }
+
+ private function checkSortField( $log ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ $tableName = $connection->tableName( SQLStore::ID_TABLE );
+ $this->messageReporter->reportMessage( "Checking smw_sortkey, smw_sort fields ...\n" );
+
+ // #2429, copy smw_sortkey content to the new smw_sort field once
+ if ( isset( $log[$tableName]['smw_sort'] ) && $log[$tableName]['smw_sort'] === TableBuilder::PROC_FIELD_NEW ) {
+ $emptyField = 'smw_sort';
+ $copyField = 'smw_sortkey';
+
+ $this->messageReporter->reportMessage( " Table " . SQLStore::ID_TABLE . " ...\n" );
+ $this->messageReporter->reportMessage( " ... copying $copyField to $emptyField ... " );
+ $connection->query( "UPDATE $tableName SET $emptyField = $copyField", __METHOD__ );
+ $this->messageReporter->reportMessage( "done.\n" );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ private function updatePredefinedProperty( $property, $id ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Try to find the ID for a non-fixed predefined property
+ if ( $id === null ) {
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id'
+ ],
+ [
+ 'smw_title' => $property->getKey(),
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_subobject' => ''
+ ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+ }
+ }
+
+ if ( $id === null ) {
+ return;
+ }
+
+ $label = $property->getCanonicalLabel();
+
+ $iw = $this->store->getObjectIds()->getPropertyInterwiki(
+ $property
+ );
+
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_proptable_hash',
+ 'smw_hash'
+ ],
+ [
+ 'smw_id' => $id
+ ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ $row = (object)[ 'smw_proptable_hash' => null, 'smw_hash' => null ];
+ }
+
+ $connection->replace(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' ],
+ [
+ 'smw_id' => $id,
+ 'smw_title' => $property->getKey(),
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => '',
+ 'smw_sortkey' => $label,
+ 'smw_sort' => Collator::singleton()->getSortKey( $label ),
+ 'smw_proptable_hash' => $row->smw_proptable_hash,
+ 'smw_hash' => $row->smw_hash
+ ],
+ __METHOD__
+ );
+
+ if ( $id === null ) {
+ return;
+ }
+
+ $row = $connection->selectRow(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [ 'p_id' ],
+ [ 'p_id' => $id ],
+ __METHOD__
+ );
+
+ // Entry is available therefore don't try to override the count
+ // value
+ if ( $row !== false ) {
+ return;
+ }
+
+ $connection->insert(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'p_id' => $id,
+ 'usage_count' => 0,
+ 'null_count' => 0
+ ],
+ __METHOD__
+ );
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php
new file mode 100644
index 00000000..16be15ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php
@@ -0,0 +1,346 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\SQLStore\TableBuilder\Table;
+use SMWDataItem as DataItem;
+
+/**
+ * @private
+ *
+ * Database type agnostic table/schema definition manager
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableSchemaManager {
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var MessageReporter
+ */
+ private $messageReporter;
+
+ /**
+ * @var Table[]
+ */
+ private $tables = [];
+
+ /**
+ * @var integer
+ */
+ private $featureFlags = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ */
+ public function __construct( SQLStore $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getHash() {
+
+ $hash = [];
+
+ foreach ( $this->getTables() as $table ) {
+ $hash[$table->getName()] = $table->getHash();
+ }
+
+ // Avoid by-chance sorting with an eventual differing hash
+ sort( $hash );
+
+ return md5( json_encode( $hash ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $featureFlags
+ */
+ public function setFeatureFlags( $featureFlags ) {
+ $this->featureFlags = $featureFlags;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $feature
+ *
+ * @return boolean
+ */
+ public function hasFeatureFlag( $feature ) {
+ return ( (int)$this->featureFlags & $feature ) != 0;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $tableName
+ *
+ * @return Table|null
+ */
+ public function findTable( $tableName ) {
+
+ foreach ( $this->getTables() as $table ) {
+ if ( $table->getName() === $tableName ) {
+ return $table;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return Table[]
+ */
+ public function getTables() {
+
+ if ( $this->tables !== [] ) {
+ return $this->tables;
+ }
+
+ $this->addTable( $this->newEntityIdTable() );
+ $this->addTable( $this->newConceptCacheTable() );
+ $this->addTable( $this->newQueryLinksTable() );
+ $this->addTable( $this->newFulltextSearchTable() );
+ $this->addTable( $this->newPropertyStatisticsTable() );
+
+ foreach ( $this->store->getPropertyTables() as $propertyTable ) {
+
+ // Only extensions that aren't setup correctly can force an exception
+ // and to avoid a failure during setup, ensure that standard tables
+ // are correctly initialized otherwise SMW can't recover
+ try {
+ $diHandler = $this->store->getDataItemHandlerForDIType( $propertyTable->getDiType() );
+ } catch ( \Exception $e ) {
+ continue;
+ }
+
+ $this->addTable( $this->newPropertyTable( $propertyTable, $diHandler ) );
+ }
+
+ return $this->tables;
+ }
+
+ private function newEntityIdTable() {
+
+ // ID_TABLE
+ $table = new Table( SQLStore::ID_TABLE );
+
+ $table->addColumn( 'smw_id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 'smw_namespace', [ FieldType::FIELD_NAMESPACE, 'NOT NULL' ] );
+ $table->addColumn( 'smw_title', [ FieldType::FIELD_TITLE, 'NOT NULL' ] );
+ $table->addColumn( 'smw_iw', [ FieldType::FIELD_INTERWIKI, 'NOT NULL' ] );
+ $table->addColumn( 'smw_subobject', [ FieldType::FIELD_TITLE, 'NOT NULL' ] );
+
+ $table->addColumn( 'smw_sortkey', [
+ $this->hasFeatureFlag( SMW_FIELDT_CHAR_NOCASE ) ? FieldType::TYPE_CHAR_NOCASE : FieldType::FIELD_TITLE,
+ 'NOT NULL'
+ ] );
+
+ $table->addColumn( 'smw_sort', [ FieldType::FIELD_TITLE ] );
+ $table->addColumn( 'smw_proptable_hash', FieldType::TYPE_BLOB );
+ $table->addColumn( 'smw_hash', FieldType::FIELD_HASH );
+ $table->addColumn( 'smw_rev', FieldType::FIELD_ID_UNSIGNED );
+
+ $table->addIndex( 'smw_id' );
+ $table->addIndex( 'smw_id,smw_sortkey' );
+ $table->addIndex( 'smw_hash,smw_id' );
+
+ // IW match lookup
+ $table->addIndex( 'smw_iw' );
+ $table->addIndex( 'smw_iw,smw_id' );
+
+ // ID lookup
+ $table->addIndex( 'smw_title,smw_namespace,smw_iw,smw_subobject' );
+
+ // InProperty lookup
+ // $table->addIndex( 'smw_iw,smw_id,smw_title,smw_sortkey,smw_sort' );
+
+ // Select by sortkey (range queries)
+ $table->addIndex( 'smw_sortkey' );
+
+ // Sort related indices, Store::getPropertySubjects (GROUP BY)
+ // $table->addIndex( 'smw_sort' );
+ $table->addIndex( 'smw_sort,smw_id' );
+
+ // API smwbrowse primary lookup
+ // SMW\MediaWiki\Api\Browse\ListLookup::fetchFromTable
+ $table->addIndex( 'smw_namespace,smw_sortkey' );
+
+ // Interfered with the API lookup index, couldn't find a use case
+ // that would require the this index
+ // $table->addIndex( 'smw_sort,smw_id,smw_iw' );
+
+ $table->addIndex( 'smw_rev,smw_id' );
+
+ return $table;
+ }
+
+ private function newConceptCacheTable() {
+
+ // CONCEPT_CACHE_TABLE (member elements (s)->concepts (o) )
+ $table = new Table( SQLStore::CONCEPT_CACHE_TABLE );
+
+ $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+ $table->addColumn( 'o_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+
+ $table->addIndex( 'o_id' );
+
+ return $table;
+ }
+
+ private function newQueryLinksTable() {
+
+ // QUERY_LINKS_TABLE
+ $table = new Table( SQLStore::QUERY_LINKS_TABLE );
+
+ $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+ $table->addColumn( 'o_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+
+ $table->addIndex( 's_id' );
+ $table->addIndex( 'o_id' );
+ $table->addIndex( 's_id,o_id' );
+
+ return $table;
+ }
+
+ private function newFulltextSearchTable() {
+
+ // FT_SEARCH_TABLE
+ // TEXT and BLOB is stored off the table with the table just having a pointer
+ // VARCHAR is stored inline with the table
+ $table = new Table( SQLStore::FT_SEARCH_TABLE );
+
+ $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+ $table->addColumn( 'p_id', [ FieldType::FIELD_ID, 'NOT NULL' ] );
+ $table->addColumn( 'o_text', FieldType::TYPE_TEXT );
+ $table->addColumn( 'o_sort', FieldType::FIELD_TITLE );
+
+ $table->addIndex( 's_id' );
+ $table->addIndex( 'p_id' );
+ $table->addIndex( 'o_sort' );
+ $table->addIndex( [ 'o_text', 'FULLTEXT' ] );
+
+ $table->addOption(
+ 'fulltextSearchTableOptions',
+ $GLOBALS['smwgFulltextSearchTableOptions']
+ );
+
+ return $table;
+ }
+
+ private function newPropertyStatisticsTable() {
+
+ // PROPERTY_STATISTICS_TABLE
+ $table = new Table( SQLStore::PROPERTY_STATISTICS_TABLE );
+
+ $table->addColumn( 'p_id', FieldType::FIELD_ID );
+ $table->addColumn( 'usage_count', FieldType::FIELD_USAGE_COUNT );
+ $table->addColumn( 'null_count', FieldType::FIELD_USAGE_COUNT );
+
+ $table->addDefault( 'usage_count', 0 );
+ $table->addDefault( 'null_count', 0 );
+
+ $table->addIndex( [ 'p_id', 'UNIQUE INDEX' ] );
+ $table->addIndex( 'usage_count' );
+ $table->addIndex( 'null_count' );
+
+ return $table;
+ }
+
+ private function newPropertyTable( $propertyTable, $diHandler ) {
+
+ // Prepare indexes. By default, property-value tables
+ // have the following indexes:
+ //
+ // sp: getPropertyValues(), getSemanticData(), getProperties()
+ // po: ask, getPropertySubjects()
+ //
+ // The "p" component is omitted for tables with fixed property.
+ $indexes = [];
+ if ( $propertyTable->usesIdSubject() ) {
+ $fieldarray = [
+ 's_id' => [ FieldType::FIELD_ID, 'NOT NULL' ]
+ ];
+
+ $indexes['sp'] = 's_id';
+ } else {
+ $fieldarray = [
+ 's_title' => [ FieldType::FIELD_TITLE, 'NOT NULL' ],
+ 's_namespace' => [ FieldType::FIELD_NAMESPACE, 'NOT NULL' ]
+ ];
+
+ $indexes['sp'] = 's_title,s_namespace';
+ }
+
+ $indexes['po'] = $diHandler->getIndexField();
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $fieldarray['p_id'] = [ FieldType::FIELD_ID, 'NOT NULL' ];
+ $indexes['sp'] = $indexes['sp'] . ',p_id';
+ }
+
+ // TODO Special handling; concepts should be handled differently
+ // in the future. See comments in SMW_DIHandler_Concept.php.
+ if ( $propertyTable->getDiType() === DataItem::TYPE_CONCEPT ) {
+ unset( $indexes['po'] );
+ }
+
+ foreach ( $diHandler->getTableIndexes() as $value ) {
+
+ if ( strpos( $value, 'p_id' ) !== false && $propertyTable->isFixedPropertyTable() ) {
+ continue;
+ }
+
+ if ( strpos( $value, 'o_id' ) !== false && !$propertyTable->usesIdSubject() ) {
+ continue;
+ }
+
+ if ( strpos( $value, 's_id' ) !== false && !$propertyTable->usesIdSubject() ) {
+ continue;
+ }
+
+ $indexes = array_merge( $indexes, [ $value ] );
+ }
+
+ $indexes = array_unique( $indexes );
+
+ foreach ( $diHandler->getTableFields() as $fieldname => $fieldType ) {
+ $fieldarray[$fieldname] = $fieldType;
+ }
+
+ $table = new Table( $propertyTable->getName() );
+
+ foreach ( $fieldarray as $fieldName => $fieldType ) {
+ $table->addColumn( $fieldName, $fieldType );
+ }
+
+ foreach ( $indexes as $key => $index ) {
+ $table->addIndex( $index, $key );
+ }
+
+ return $table;
+ }
+
+ private function addTable( Table $table ) {
+ $this->tables[] = $table;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/Content.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/Content.php
new file mode 100644
index 00000000..e5d2a283
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/Content.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace SMW\Schema\Content;
+
+use SMW\Schema\SchemaFactory;
+use SMW\Schema\Exception\SchemaTypeNotFoundException;
+use SMW\Schema\Schema;
+use SMW\ParserData;
+use Symfony\Component\Yaml\Yaml;
+use Symfony\Component\Yaml\Exception\ParseException;
+use JsonContent;
+use Title;
+use User;
+use ParserOptions;
+use ParserOutput;
+use Html;
+
+/**
+ * The content model supports both JSON and YAML (as a superset of JSON), allowing
+ * for its content to be represented in JSON when required while a user may choose
+ * YAML to edit/store the native content (due to improve readability or
+ * aid others with additional inline comments).
+ *
+ * Comments (among other elements) will not be represented in JSON output when
+ * requested by the `Content::toJson` method.
+ *
+ * @see https://en.wikipedia.org/wiki/YAML#Comparison_with_JSON
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Content extends JsonContent {
+
+ /**
+ * @var SchemaFactory
+ */
+ private $schemaFactory;
+
+ /**
+ * @var ContentFormatter
+ */
+ private $contentFormatter;
+
+ /**
+ * @var array
+ */
+ private $parse;
+
+ /**
+ * @var boolean
+ */
+ private $isYaml = false;
+
+ /**
+ * @var boolean
+ */
+ private $isValid;
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function __construct( $text ) {
+ parent::__construct( $text, CONTENT_MODEL_SMW_SCHEMA );
+ }
+
+ /**
+ * `Content::getNativeData` will return the "native" text representation which
+ * in case of YAML is just the text and not a JSON string. Therefore
+ * `getNativeData` preserves the original user input.
+ *
+ * Instead, use this method to retrieve a JSON compatible string for both
+ * JSON and YAML for when the data is valid.
+ *
+ * @since 3.0
+ *
+ * @return null|string
+ */
+ public function toJson() {
+
+ if ( $this->isValid() ) {
+ return json_encode( $this->parse );
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function isYaml() {
+
+ if ( $this->isValid() ) {
+ return $this->isYaml;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see Content::isValid
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function isValid() {
+
+ if ( $this->isValid === null ) {
+ $this->decode_content();
+ }
+
+ return $this->isValid;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function fillParserOutput( Title $title, $revId, ParserOptions $options, $generateHtml, ParserOutput &$output ) {
+
+ if ( !$generateHtml || !$this->isValid() ) {
+ return;
+ }
+
+ $this->initServices();
+
+ $output->addModuleStyles(
+ $this->contentFormatter->getModuleStyles()
+ );
+
+ $parserData = new ParserData( $title, $output );
+ $schema = null;
+
+ try {
+ $schema = $this->schemaFactory->newSchema(
+ $title->getDBKey(),
+ $this->toJson()
+ );
+ } catch ( SchemaTypeNotFoundException $e ) {
+
+ $this->contentFormatter->setUnknownType(
+ $e->getType()
+ );
+
+ $output->setText(
+ $this->contentFormatter->getText( $this->mText, $this->isYaml )
+ );
+
+ $parserData->addError(
+ [ [ 'smw-schema-error-type-unknown', $e->getType() ] ]
+ );
+
+ $parserData->copyToParserOutput();
+ }
+
+ if ( $schema === null ) {
+ return ;
+ }
+
+ $output->setIndicator(
+ 'mw-helplink',
+ $this->contentFormatter->getHelpLink( $schema )
+ );
+
+ $errors = $this->schemaFactory->newSchemaValidator()->validate(
+ $schema
+ );
+
+ $this->contentFormatter->setType(
+ $this->schemaFactory->getType( $schema->get( 'type' ) )
+ );
+
+ $output->setText(
+ $this->contentFormatter->getText( $this->mText, $this->isYaml, $schema, $errors )
+ );
+
+ foreach ( $errors as $error ) {
+ if ( isset( $error['property'] ) && isset( $error['message'] ) ) {
+ $parserData->addError(
+ [ ['smw-schema-error-violation', $error['property'], $error['message'] ] ]
+ );
+ }
+ }
+
+ $parserData->copyToParserOutput();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
+ // FIXME: WikiPage::doEditContent invokes PST before validation. As such, native data
+ // may be invalid (though PST result is discarded later in that case).
+ if ( !$this->isValid() ) {
+ return $this;
+ }
+
+ if ( !$this->isYaml ) {
+ $text = self::normalizeLineEndings(
+ json_encode( $this->parse, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE )
+ );
+ } else {
+ $text = self::normalizeLineEndings( $this->mText );
+ }
+
+ return new static( $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param SchemaFactory $schemaFactory
+ * @param ContentFormatter $contentFormatter
+ */
+ public function setServices( SchemaFactory $schemaFactory, ContentFormatter $contentFormatter ) {
+ $this->schemaFactory = $schemaFactory;
+ $this->contentFormatter = $contentFormatter;
+ }
+
+ /**
+ * @see TextContent::normalizeLineEndings (MW 1.28+)
+ *
+ * @param $text
+ * @return string
+ */
+ public static function normalizeLineEndings( $text ) {
+ return str_replace( [ "\r\n", "\r" ], "\n", rtrim( $text ) );
+ }
+
+ private function initServices() {
+
+ if ( $this->schemaFactory === null ) {
+ $this->schemaFactory = new SchemaFactory();
+ }
+
+ if ( $this->contentFormatter === null ) {
+ $this->contentFormatter = new ContentFormatter();
+ }
+ }
+
+ private function decode_content() {
+
+ // Support either JSON or YAML, if the class is available! Do a quick
+ // check on `{ ... }` to decide whether it is a non-JSON string.
+ if ( $this->mText !== '' && $this->mText[0] !== '{' && substr( $this->mText, -1 ) !== '}' && class_exists( '\Symfony\Component\Yaml\Yaml' ) ) {
+
+ try {
+ $this->parse = Yaml::parse( $this->mText );
+ $this->isYaml = true;
+ } catch ( ParseException $e ) {
+ $this->isYaml = false;
+ $this->parse = null;
+ }
+
+ return $this->isValid = $this->isYaml;
+ } elseif ( $this->mText !== '' ) {
+
+ // Note that this parses it without casting objects to associative arrays.
+ // Objects and arrays are kept as distinguishable types in the PHP values.
+ $this->parse = json_decode( $this->mText );
+ $this->isValid = json_last_error() === JSON_ERROR_NONE;
+
+ return $this->isValid;
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentFormatter.php
new file mode 100644
index 00000000..ef004b61
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentFormatter.php
@@ -0,0 +1,270 @@
+<?php
+
+namespace SMW\Schema\Content;
+
+use SMW\Schema\Schema;
+use SMW\Schema\SchemaFactory;
+use SMW\Message;
+use SMWInfolink as Infolink;
+use Onoi\CodeHighlighter\Highlighter as CodeHighlighter;
+use Onoi\CodeHighlighter\Geshi;
+use Html;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentFormatter {
+
+ /**
+ * @var HtmlBuilder
+ */
+ private $htmlBuilder;
+
+ /**
+ * @var []
+ */
+ private $type = [];
+
+ /**
+ * @var string|null
+ */
+ private $unknownType = false;
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function __construct() {
+ $this->htmlBuilder = new HtmlBuilder();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function setType( $type ) {
+ $this->type = $type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getModuleStyles() {
+ return [ 'mediawiki.helplink', 'smw.content.schema', 'mediawiki.content.json' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Schema $schema
+ *
+ * @return string
+ */
+ public function getHelpLink( Schema $schema ) {
+
+ $key = [
+ 'smw-schema-type-help-link',
+ $schema->get( Schema::SCHEMA_TYPE )
+ ];
+
+ $params = [
+ 'href' => $this->msg( $key )
+ ];
+
+ return $this->htmlBuilder->build( 'schema_help_link', $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function setUnknownType( $type ) {
+ $this->unknownType = $type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Schema $schema
+ *
+ * @return string
+ */
+ public function getText( $text, $isYaml = false, Schema $schema = null, array $errors = [] ) {
+
+ $methods = [
+ 'head' => [ $schema, $errors ],
+ 'body' => [ $text, $isYaml ],
+ 'footer' => [ $schema ]
+ ];
+
+ $html = '';
+
+ if ( $this->unknownType !== false ) {
+ $html = $this->unknown_type( $this->unknownType );
+ }
+
+ foreach ( $methods as $method => $element ) {
+ $html .= $this->{$method}( ...$element );
+ }
+
+ return $html;
+ }
+
+ private function head( $schema, array $errors ) {
+
+ if ( $schema === null ) {
+ return '';
+ }
+
+ $schema_link = str_replace( '.json', '', substr(
+ $schema->getValidationSchema(),
+ strrpos( $schema->getValidationSchema(), '/' ) + 1
+ ) );
+
+ $errorCount = count( $errors );
+ $error = $this->error_text( $schema_link, $errors );
+
+ $type = $schema->get( 'type', '' );
+ $description = '';
+
+ if ( isset( $this->type['type_description'] ) ) {
+ $description = $this->msg( $this->type['type_description'], Message::PARSE );
+ }
+
+ $params = [
+ 'link' => '',
+ 'description' => $schema->get( Schema::SCHEMA_DESCRIPTION, '' ),
+ 'type_description' => $description,
+ 'schema-title' => $this->msg( 'smw-schema-title' ),
+ 'error' => $error,
+ 'error-title' => $this->msg( [ 'smw-schema-error', $errorCount ] )
+ ];
+
+ return $this->htmlBuilder->build( 'schema_head', $params );
+ }
+
+ private function body( $text, $isYaml ) {
+
+ $codeHighlighter = null;
+
+ if ( class_exists( '\Onoi\CodeHighlighter\Highlighter' ) ) {
+ $codeHighlighter = new CodeHighlighter();
+
+ // `yaml` works well enough for both JSON and YAML
+ $codeHighlighter->setLanguage( 'yaml' );
+ $codeHighlighter->addOption( Geshi::SET_OVERALL_CLASS, 'content-highlight' );
+ }
+
+ if ( $codeHighlighter !== null && $isYaml ) {
+ $text = $codeHighlighter->highlight( $text );
+ } elseif ( $codeHighlighter !== null ) {
+ $codeHighlighter->addOption( Geshi::SET_STRINGS_STYLE, 'color: #000' );
+ $text = $codeHighlighter->highlight( $text );
+ } else {
+ if ( !$isYaml ) {
+ $text = json_encode( json_decode( $text ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ $text = Html::rawElement( 'pre', [ 'class' => 'content-no-highlight' ], $text );
+ }
+
+ $params = [
+ 'text' => $text,
+ 'unknown_type' => $this->unknownType
+ ];
+
+ return $this->htmlBuilder->build( 'schema_body', $params );
+ }
+
+ private function footer( $schema ) {
+
+ if ( $schema === null ) {
+ return '';
+ }
+
+ $tags = [];
+
+ if ( ( $tags = $schema->get( Schema::SCHEMA_TAG, [] ) ) !== [] ) {
+ foreach ( $tags as $k => $tag ) {
+ $tags[$k] = Infolink::newPropertySearchLink( $tag, 'Schema tag', $tag, '' )->getHtml();
+ }
+ }
+
+ $type = $schema->get( 'type', '' );
+ $link = Infolink::newPropertySearchLink( $type, 'Schema type', $type, '' );
+
+ $params = [
+ 'href_type' => Title::newFromText( 'Schema type', SMW_NS_PROPERTY )->getLocalUrl(),
+ 'msg_type' => $this->msg( [ 'smw-schema-type' ] ),
+ 'link_type' => $link->getHtml(),
+ 'href_tag' => Title::newFromText( 'Schema tag', SMW_NS_PROPERTY )->getLocalUrl(),
+ 'msg_tag' => $this->msg( [ 'smw-schema-tag', count( $tags ) ] ),
+ 'tags' => $tags
+ ];
+
+ return $this->htmlBuilder->build( 'schema_footer', $params );
+ }
+
+ private function error_text( $validator_schema, array $errors = [] ) {
+
+ if ( $errors === [] ) {
+ return '';
+ }
+
+ $list = [];
+
+ foreach ( $errors as $error ) {
+
+ if ( !isset( $error['property'] ) ) {
+ continue;
+ }
+
+ $params = [
+ 'msg' => $error['message'],
+ 'text' => $error['property']
+ ];
+
+ $list[] = $this->htmlBuilder->build( 'schema_error', $params );
+ }
+
+ if ( $list === [] ) {
+ return '';
+ }
+
+ $params = [
+ 'list' => $list,
+ 'schema' => $this->msg( [ 'smw-schema-error-schema', $validator_schema ], Message::PARSE )
+ ];
+
+ return $this->htmlBuilder->build( 'schema_error_text', $params );
+ }
+
+ private function unknown_type( $type ) {
+
+ if ( $type === '' || $type === null ) {
+ $key = 'smw-schema-error-type-missing';
+ } else {
+ $key = [ 'smw-schema-error-type-unknown', $type ];
+ }
+
+ $params = [
+ 'msg' => $this->msg( $key, Message::PARSE )
+ ];
+
+ return $this->htmlBuilder->build( 'schema_unknown_type', $params );
+ }
+
+ private function msg( $key, $type = Message::TEXT, $lang = Message::USER_LANGUAGE ) {
+ return Message::get( $key, $type, $lang );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentHandler.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentHandler.php
new file mode 100644
index 00000000..82a4ce4e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/ContentHandler.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Schema\Content;
+
+use JsonContentHandler;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentHandler extends JsonContentHandler {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function __construct() {
+ parent::__construct( CONTENT_MODEL_SMW_SCHEMA, [ CONTENT_FORMAT_JSON ] );
+ }
+
+ /**
+ * Returns true, because wikitext supports caching using the
+ * ParserCache mechanism.
+ *
+ * @since 1.21
+ *
+ * @return bool Always true.
+ *
+ * @see ContentHandler::isParserCacheSupported
+ */
+ public function isParserCacheSupported() {
+ return true;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ protected function getContentClass() {
+ return Content::class;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function supportsSections() {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function supportsCategories() {
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function supportsRedirects() {
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/HtmlBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/HtmlBuilder.php
new file mode 100644
index 00000000..c014ecf8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Content/HtmlBuilder.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace SMW\Schema\Content;
+
+use Html;
+use SMW\Utils\HtmlTabs;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlBuilder {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param array $params
+ *
+ * @return string
+ */
+ public function build( $key, array $params ) {
+ return $this->{$key}( $params );
+ }
+
+ private function schema_head( $params ) {
+
+ $list = [];
+ $text = '';
+ $type_description = '';
+
+ if ( $params['link'] !== '' ) {
+ $list[] = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'plainlinks'
+ ],
+ $params['link']
+ );
+ }
+
+ if ( isset( $params['type_description'] ) ) {
+ $type_description .= Html::rawElement(
+ 'p',
+ [
+ 'class' => 'smw-schema-type-description plainlinks'
+
+ ],
+ $params['type_description']
+ );
+ }
+
+ if ( $params['description'] !== '' ) {
+ $type_description .= Html::rawElement(
+ 'p',
+ [
+ 'class' => 'smw-schema-description plainlinks'
+ ],
+ $params['description']
+ );
+ }
+
+ $htmlTabs = new HtmlTabs();
+
+ $htmlTabs->setActiveTab(
+ $params['error'] !== '' ? 'schema-error' : 'schema-summary'
+ );
+
+ $htmlTabs->tab( 'schema-summary', $params['schema-title'] );
+ $htmlTabs->tab(
+ 'schema-error',
+ $params['error-title'],
+ [
+ 'hide' => $params['error'] === '', 'class' => 'error-label'
+ ]
+ );
+
+ $htmlTabs->content( 'schema-summary', $text );
+ $htmlTabs->content( 'schema-error', $params['error'] );
+
+ $html = $htmlTabs->buildHTML(
+ [ 'class' => 'smw-schema' ]
+ );
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'schema-head'
+ ],
+ $type_description . $html
+ );
+ }
+
+ private function schema_body( $params ) {
+
+ $class = $params['unknown_type'] !== false ? ' unknown-type' : '';
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'schema-body' . $class
+ ],
+ $params['text']
+ );
+ }
+
+ private function schema_error_text( $params ) {
+
+ $html = Html::rawElement(
+ 'ul',
+ [
+ 'class' => 'smw-schema-validation-error-list'
+ ],
+ '<li>' . implode( '</li><li>', $params['list'] ) . '</li>'
+ );
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-schema-validation-error'
+ ],
+ $params['schema']
+ ) . $html;
+
+ return $html;
+ }
+
+ private function schema_error( $params ) {
+
+ $html = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'schema-error'
+ ],
+ $params['text']
+ );
+
+ return $html . '&nbsp;' . Html::rawElement(
+ 'span',
+ [],
+ ":&nbsp;" . $params['msg']
+ );
+ }
+
+ private function schema_footer( $params ) {
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'schema-tags'
+ ],
+ Html::rawElement(
+ 'div',
+ [],
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => $params['href_type']
+ ],
+ $params['msg_type']
+ ) . ':&nbsp;' . $params['link_type']
+ )
+ );
+
+ if ( $params['tags'] !== [] ) {
+ $html .= Html::rawElement(
+ 'div',
+ [
+ 'class' => 'schema-tags'
+ ],
+ Html::rawElement(
+ 'div',
+ [],
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => $params['href_tag']
+ ],
+ $params['msg_tag']
+ ) . ':' . '<ul><li>' . implode( '</li><li>', $params['tags'] ) . '</li></ul>'
+ )
+ );
+ }
+
+ return Html::rawElement(
+ 'div',
+ [
+ 'class' => 'schema-footer'
+ ],
+ $html
+ );
+ }
+
+ private function schema_unknown_type( $params ) {
+ return Html::rawElement(
+ 'p',
+ [
+ 'class' => 'smw-callout smw-callout-error plainlinks'
+ ],
+ $params['msg']
+ );
+ }
+
+ private function schema_help_link( $params ) {
+ return Html::rawElement(
+ 'a',
+ [
+ 'href' => $params['href'],
+ 'target' => '_blank',
+ 'class' => 'mw-helplink',
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaConstructionFailedException.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaConstructionFailedException.php
new file mode 100644
index 00000000..fc452f4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaConstructionFailedException.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Schema\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaConstructionFailedException extends RuntimeException {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function __construct( $type ) {
+ parent::__construct( "$type couldn't construct a Schema instance!" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaTypeNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaTypeNotFoundException.php
new file mode 100644
index 00000000..9f5b67e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Exception/SchemaTypeNotFoundException.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\Schema\Exception;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaTypeNotFoundException extends RuntimeException {
+
+ /**
+ * @var string
+ */
+ private $type = '';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ */
+ public function __construct( $type ) {
+ parent::__construct( "$type is an unrecognized schema type." );
+ $this->type = $type;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/README.md b/www/wiki/extensions/SemanticMediaWiki/src/Schema/README.md
new file mode 100644
index 00000000..0dfaf12b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/README.md
@@ -0,0 +1,26 @@
+The objective of the `SMW_NS_SCHEMA` (aka Schema) namespace is to allow for a structured definition of different schemata where types define the interpreter, syntax elements, and constraints.
+
+The namespace expects a JSON format (or if available, YAML as superset of JSON) as input format to ensure that content elements are structured and a validation a [JSON schema][json:schema] can help enforce requirements and constraints for a specific type.
+
+The following properties are provided to make elements of a schema definition discoverable.
+
+* Schema type (`_SCHEMA_TYPE` )
+* Schema definition (`_SCHEMA_DEF`)
+* Schema description (`_SCHEMA_DESC`)
+* Schema tag (`_SCHEMA_TAG`)
+* Schema link (`_SCHEMA_LINK`)
+
+## Registration
+
+Extensibility for new schema types and interpreters is provided by adding a type to the `$smwgSchemaTypes` setting.
+
+<pre>
+$GLOBALS['smwgSchemaTypes'] = [
+ 'LINK_FORMAT_schema' => [
+ 'validator_schema => __DIR__ . '/data/schema/...',
+ 'group' => SMW_schema_GROUP_FORMAT,
+ ]
+];
+</pre>
+
+[json:schema]: http://json-schema.org/
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/Schema.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Schema.php
new file mode 100644
index 00000000..0bd5d218
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/Schema.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Schema;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+interface Schema {
+
+ const SCHEMA_TYPE = 'type';
+ const SCHEMA_DESCRIPTION = 'description';
+ const SCHEMA_TAG = 'tags';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function get( $key, $default = null );
+
+ /**
+ * Returns the name of the schema which is equivalent with the page name
+ * without the namespace prefix.
+ *
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getValidationSchema();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaDefinition.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaDefinition.php
new file mode 100644
index 00000000..e32443e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaDefinition.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Schema;
+
+use JsonSerializable;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaDefinition implements Schema, JsonSerializable {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var array
+ */
+ protected $definition = [];
+
+ /**
+ * @var string|null
+ */
+ private $validation_schema;
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ * @param array $definition
+ * @param string|null $validation_schema
+ */
+ public function __construct( $name, array $definition, $validation_schema = null ) {
+ $this->name = $name;
+ $this->definition = $definition;
+ $this->validation_schema = $validation_schema;
+ }
+
+ /**
+ * @see Schema::get
+ * @since 3.0
+ *
+ * @return mixed|null
+ */
+ public function get( $key, $default = null ) {
+ return $this->digDeep( $this->definition, $key, $default );
+ }
+
+ /**
+ * @see Schema::getName
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getName() {
+ return str_replace( '_', ' ', $this->name );
+ }
+
+ /**
+ * @see Schema::getValidationSchema
+ * @since 3.0
+ *
+ * @return string|null
+ */
+ public function getValidationSchema() {
+ return $this->validation_schema;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function jsonSerialize() {
+ return json_encode( $this->definition );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->jsonSerialize();
+ }
+
+ private function digDeep( $array, $key, $default ) {
+
+ if ( strpos( $key, '.' ) !== false ) {
+ $list = explode( '.', $key, 2 );
+
+ foreach ( $list as $k => $v ) {
+ if ( isset( $array[$v] ) ) {
+ return $this->digDeep( $array[$v], $list[$k+1], $default );
+ }
+ }
+ }
+
+ if ( isset( $array[$key] ) ) {
+ return $array[$key];
+ }
+
+ return $default;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaFactory.php
new file mode 100644
index 00000000..0dd13dad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaFactory.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\Schema;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\Schema\Exception\SchemaTypeNotFoundException;
+use SMW\Schema\Exception\SchemaConstructionFailedException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaFactory {
+
+ /**
+ * @var []
+ */
+ private $schemaTypes = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param array $schemaTypes
+ */
+ public function __construct( array $schemaTypes = [] ) {
+ $this->schemaTypes = $schemaTypes;
+
+ if ( $this->schemaTypes === [] ) {
+ $this->schemaTypes = $GLOBALS['smwgSchemaTypes'];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return []
+ */
+ public function getType( $type ) {
+ return isset( $this->schemaTypes[$type] ) ? $this->schemaTypes[$type] : [];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return boolean
+ */
+ public function isRegisteredType( $type ) {
+ return isset( $this->schemaTypes[$type] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getRegisteredTypes() {
+ return array_keys( $this->schemaTypes );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|array $group
+ *
+ * @return []
+ */
+ public function getRegisteredTypesByGroup( $group ) {
+
+ $registeredTypes = [];
+ $groups = (array)$group;
+
+ foreach ( $this->schemaTypes as $type => $val ) {
+ if ( isset( $val['group'] ) && in_array( $val['group'], $groups ) ) {
+ $registeredTypes[] = $type;
+ }
+ }
+
+ return $registeredTypes;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ * @param array|string $data
+ *
+ * @return Schema
+ * @throws RuntimeException
+ */
+ public function newSchema( $name, $data ) {
+
+ if ( is_string( $data ) ) {
+ if ( ( $data = json_decode( $data, true ) ) === null || json_last_error() !== JSON_ERROR_NONE ) {
+ throw new RuntimeException( "Invalid JSON format." );
+ }
+ }
+
+ $type = null;
+ $validation_schema = null;
+
+ if ( isset( $data['type'] ) ) {
+ $type = $data['type'];
+ }
+
+ if ( !isset( $this->schemaTypes[$type] ) ) {
+ throw new SchemaTypeNotFoundException( $type );
+ }
+
+ if ( isset( $this->schemaTypes[$type]['validation_schema'] ) ) {
+ $validation_schema = $this->schemaTypes[$type]['validation_schema'];
+ }
+
+ if ( isset( $this->schemaTypes[$type]['__factory'] ) && is_callable( $this->schemaTypes[$type]['__factory'] ) ) {
+ $schema = $this->schemaTypes[$type]['__factory']( $name, $data );
+ } else {
+ $schema = new SchemaDefinition( $name, $data, $validation_schema );
+ }
+
+ if ( !$schema instanceof Schema ) {
+ throw new SchemaConstructionFailedException( $type );
+ }
+
+ return $schema;
+ }
+
+ public static function newTest( $name, $data ) {
+ return '';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return SchemaValidator
+ */
+ public function newSchemaValidator() {
+ return new SchemaValidator(
+ ApplicationFactory::getInstance()->create( 'JsonSchemaValidator' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaValidator.php
new file mode 100644
index 00000000..a37094f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Schema/SchemaValidator.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SMW\Schema;
+
+use SMW\Utils\JsonSchemaValidator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaValidator {
+
+ /**
+ * @var JsonSchemaValidator
+ */
+ private $validator;
+
+ /**
+ * @since 3.0
+ *
+ * @param JsonSchemaValidator $validator
+ */
+ public function __construct( JsonSchemaValidator $validator ) {
+ $this->validator = $validator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Schema|null $schema
+ *
+ * @return []
+ */
+ public function validate( Schema $schema = null ) {
+
+ if ( $schema === null || !is_string( $schema->getValidationSchema() ) ) {
+ return [];
+ }
+
+ $this->validator->validate(
+ $schema,
+ $schema->getValidationSchema()
+ );
+
+ if ( $this->validator->isValid() ) {
+ return [];
+ }
+
+ return $this->validator->getErrors();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SerializerFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/SerializerFactory.php
new file mode 100644
index 00000000..56c322d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SerializerFactory.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW;
+
+use Deserializers\Deserializer;
+use OutOfBoundsException;
+use Serializers\Serializer;
+use SMW\Deserializers\ExpDataDeserializer;
+use SMW\Deserializers\SemanticDataDeserializer;
+use SMW\Serializers\ExpDataSerializer;
+use SMW\Serializers\QueryResultSerializer;
+use SMW\Serializers\SemanticDataSerializer;
+use SMWExpData as ExpData;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SerializerFactory {
+
+ /**
+ * Method that assigns registered serializers to an object
+ *
+ * @since 2.2
+ *
+ * @param mixed $object
+ *
+ * @return Serializer
+ */
+ public function getSerializerFor( $object ) {
+
+ $serializer = null;
+
+ if ( $object instanceof SemanticData ) {
+ $serializer = $this->newSemanticDataSerializer();
+ } elseif ( $object instanceof QueryResult ) {
+ $serializer = $this->newQueryResultSerializer();
+ } elseif ( $object instanceof ExpData ) {
+ $serializer = $this->newExpDataSerializer();
+ }
+
+ if ( !$serializer instanceof Serializer ) {
+ throw new OutOfBoundsException( 'No serializer can be matched to the object' );
+ }
+
+ return $serializer;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $serialization
+ *
+ * @return Deserializer
+ */
+ public function getDeserializerFor( array $serialization ) {
+
+ $deserializer = null;
+
+ if ( isset( $serialization['serializer'] ) ) {
+
+ switch ( $serialization['serializer'] ) {
+ case 'SMW\Serializers\SemanticDataSerializer':
+ $deserializer = $this->newSemanticDataDeserializer();
+ break;
+ case 'SMW\Serializers\ExpDataSerializer':
+ $deserializer = $this->newExpDataDeserializer();
+ break;
+ }
+ }
+
+ if ( !$deserializer instanceof Deserializer ) {
+ throw new OutOfBoundsException( 'No deserializer can be matched to the serialization format' );
+ }
+
+ return $deserializer;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return SemanticDataSerializer
+ */
+ public function newSemanticDataSerializer() {
+ return new SemanticDataSerializer();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return SemanticDataDeserializer
+ */
+ public function newSemanticDataDeserializer() {
+ return new SemanticDataDeserializer();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QueryResultSerializer
+ */
+ public function newQueryResultSerializer() {
+ return new QueryResultSerializer();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ExpDataSerializer
+ */
+ public function newExpDataSerializer() {
+ return new ExpDataSerializer();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return ExpDataDeserializer
+ */
+ public function newExpDataDeserializer() {
+ return new ExpDataDeserializer();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Serializers/ExpDataSerializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/ExpDataSerializer.php
new file mode 100644
index 00000000..1f3a031d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/ExpDataSerializer.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace SMW\Serializers;
+
+use OutOfBoundsException;
+use Serializers\Serializer;
+use SMW\Exporter\Element\ExpElement;
+use SMWExpData as ExpData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpDataSerializer implements Serializer {
+
+ /**
+ * @see Serializer::serialize
+ *
+ * @since 2.2
+ */
+ public function serialize( $expData ) {
+
+ if ( !$expData instanceof ExpData ) {
+ throw new OutOfBoundsException( 'Object is not supported' );
+ }
+
+ return $this->doSerialize( $expData ) + [ 'serializer' => __CLASS__, 'version' => 0.1 ];
+ }
+
+ private function doSerialize( $expData ) {
+
+ $serialization = [
+ 'subject' => $expData->getSubject()->getSerialization()
+ ];
+
+ $properties = [];
+
+ foreach ( $expData->getProperties() as $property ) {
+ $properties[$property->getUri()] = [
+ 'property' => $property->getSerialization(),
+ 'children' => $this->doSerializeChildren( $expData->getValues( $property ) )
+ ];
+ }
+
+ return $serialization + [ 'data' => $properties ];
+ }
+
+ private function doSerializeChildren( array $elements ) {
+
+ $children = [];
+
+ if ( $elements === [] ) {
+ return $children;
+ }
+
+ foreach ( $elements as $element ) {
+ $children[] = $element instanceof ExpElement ? $element->getSerialization() : $this->doSerialize( $element );
+ }
+
+ return $children;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Serializers/FlatSemanticDataSerializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/FlatSemanticDataSerializer.php
new file mode 100644
index 00000000..30f298a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/FlatSemanticDataSerializer.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace SMW\Serializers;
+
+/**
+ * Only returns the head of the subobject without serializing associated
+ * dataItems.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FlatSemanticDataSerializer extends SemanticDataSerializer {
+
+ /**
+ * @see SemanticDataSerializer::doSerializeSubobject
+ *
+ * @return array
+ */
+ protected function doSerializeSubSemanticData( $subSemanticData ) {
+
+ $subobjects = [];
+
+ foreach ( $subSemanticData as $semanticData ) {
+ $subobjects[] = $semanticData->getSubject()->getSerialization();
+ }
+
+ return $subobjects;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Serializers/QueryResultSerializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/QueryResultSerializer.php
new file mode 100644
index 00000000..ecba7d30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/QueryResultSerializer.php
@@ -0,0 +1,290 @@
+<?php
+
+namespace SMW\Serializers;
+
+use OutOfBoundsException;
+use Serializers\DispatchableSerializer;
+use SMW\DataValueFactory;
+use SMW\Query\PrintRequest;
+use SMWDataItem as DataItem;
+use SMWQueryResult as QueryResult;
+use SMWResultArray;
+use Title;
+
+/**
+ * Class for serializing SMWDataItem and SMWQueryResult objects to a context
+ * independent object consisting of arrays and associative arrays, which can
+ * be fed directly to json_encode, the MediaWiki API, and similar serializers.
+ *
+ * This class is distinct from SMWSerializer and the SMWExpData object
+ * it takes, in that here semantic context is lost.
+ *
+ * @ingroup Serializers
+ *
+ * @licence GNU GPL v2+
+ * @since 1.7
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class QueryResultSerializer implements DispatchableSerializer {
+
+ /**
+ * @var integer
+ */
+ private static $version = 2;
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $version
+ */
+ public function version( $version ) {
+ self::$version = (int)$version;
+ }
+
+ /**
+ * @see SerializerInterface::serialize
+ *
+ * @since 1.9
+ *
+ * @return array
+ * @throws OutOfBoundsException
+ */
+ public function serialize( $queryResult ) {
+
+ if ( !( $this->isSerializerFor( $queryResult ) ) ) {
+ throw new OutOfBoundsException( 'Object was not identified as a QueryResult instance' );
+ }
+
+ return $this->getSerializedQueryResult( $queryResult ) + [ 'serializer' => __CLASS__, 'version' => self::$version ];
+ }
+
+ /**
+ * @see Serializers::isSerializerFor
+ *
+ * @since 1.9
+ */
+ public function isSerializerFor( $queryResult ) {
+ return $queryResult instanceof QueryResult;
+ }
+
+ /**
+ * Get the serialization for the provided data item.
+ *
+ * @since 1.7
+ *
+ * @param SMWDataItem $dataItem
+ *
+ * @return mixed
+ */
+ public static function getSerialization( DataItem $dataItem, $printRequest = null ) {
+ switch ( $dataItem->getDIType() ) {
+ case DataItem::TYPE_WIKIPAGE:
+
+ // Support for a deserializable _rec type with 0.6
+ if ( $printRequest !== null && strpos( $printRequest->getTypeID(), '_rec' ) !== false ) {
+ $recordValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem,
+ $printRequest->getData()->getDataItem()
+ );
+
+ $recordDiValues = [];
+
+ foreach ( $recordValue->getPropertyDataItems() as $property ) {
+ $label = $property->getLabel();
+
+ $recordDiValues[$label] = [
+ 'label' => $label,
+ 'key' => $property->getKey(),
+ 'typeid' => $property->findPropertyTypeID(),
+ 'item' => []
+ ];
+
+ foreach ( $recordValue->getDataItem()->getSemanticData()->getPropertyValues( $property ) as $value ) {
+
+ if ( $property->findPropertyTypeID() === '_qty' ) {
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $value, $property );
+
+ $recordDiValues[$label]['item'][] = [
+ 'value' => $dataValue->getNumber(),
+ 'unit' => $dataValue->getUnit()
+ ];
+ } else {
+ $recordDiValues[$label]['item'][] = self::getSerialization( $value );
+ }
+ }
+ }
+ $result = $recordDiValues;
+ } else {
+ $title = $dataItem->getTitle();
+
+ $wikiPageValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $dataItem
+ );
+
+ $result = [
+ 'fulltext' => $title->getFullText(),
+ 'fullurl' => $title->getFullUrl(),
+ 'namespace' => $title->getNamespace(),
+ 'exists' => strval( $title->isKnown() ),
+ 'displaytitle' => $wikiPageValue->getDisplayTitle()
+ ];
+ }
+ break;
+ case DataItem::TYPE_NUMBER:
+ // dataitems and datavalues
+ // Quantity is a datavalue type that belongs to dataitem
+ // type number which means in order to identify the correct
+ // unit, we have re-factor the corresponding datavalue otherwise
+ // we will not be able to determine the unit
+ // (unit is part of the datavalue object)
+ if ( $printRequest !== null && $printRequest->getTypeID() === '_qty' ) {
+ $diProperty = $printRequest->getData()->getDataItem();
+
+ if ( $printRequest->isMode( \SMW\Query\PrintRequest::PRINT_CHAIN ) ) {
+ $diProperty = $printRequest->getData()->getLastPropertyChainValue()->getDataItem();
+ }
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $diProperty );
+
+ $result = [
+ 'value' => $dataValue->getNumber(),
+ 'unit' => $dataValue->getUnit()
+ ];
+ } else {
+ $result = $dataItem->getNumber();
+ }
+ break;
+ case DataItem::TYPE_GEO:
+ $result = $dataItem->getCoordinateSet();
+ break;
+ case DataItem::TYPE_TIME:
+ $result = [
+ 'timestamp' => $dataItem->getMwTimestamp(),
+ 'raw' => $dataItem->getSerialization()
+ ];
+ break;
+ default:
+ $result = $dataItem->getSerialization();
+ break;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the serialization for a SMWQueryResult object.
+ *
+ * @since 1.7
+ *
+ * @param SMWQueryResult $result
+ *
+ * @return array
+ */
+ public static function getSerializedQueryResult( QueryResult $queryResult ) {
+ $results = [];
+ $printRequests = [];
+
+ foreach ( $queryResult->getPrintRequests() as $printRequest ) {
+ $printRequests[] = self::serialize_printrequest( $printRequest );
+ }
+
+ /**
+ * @var DIWikiPage $diWikiPage
+ * @var PrintRequest $printRequest
+ */
+ foreach ( $queryResult->getResults() as $diWikiPage ) {
+
+ if ( $diWikiPage === null || !($diWikiPage->getTitle() instanceof Title ) ) {
+ continue;
+ }
+
+ $result = [ 'printouts' => [] ];
+
+ foreach ( $queryResult->getPrintRequests() as $printRequest ) {
+ $resultArray = new SMWResultArray( $diWikiPage, $printRequest, $queryResult->getStore() );
+
+ if ( $printRequest->getMode() === PrintRequest::PRINT_THIS ) {
+ $dataItems = $resultArray->getContent();
+ $result += self::getSerialization( array_shift( $dataItems ), $printRequest );
+ } elseif ( $resultArray->getContent() !== [] ) {
+ $values = [];
+
+ foreach ( $resultArray->getContent() as $dataItem ) {
+ $values[] = self::getSerialization( $dataItem, $printRequest );
+ }
+ $result['printouts'][$printRequest->getLabel()] = $values;
+ } else {
+ // For those objects that are empty return an empty array
+ // to keep the output consistent
+ $result['printouts'][$printRequest->getLabel()] = [];
+ }
+ }
+
+ $id = $diWikiPage->getTitle()->getFullText();
+
+ /**
+ * #3038
+ *
+ * Version 2: ... "results": { "Foo": {} ... }
+ * Version 3: ... "results": [ { "Foo": {} } ... ]
+ */
+ if ( self::$version >= 3 ) {
+ $results[] = [ $id => $result ];
+ } else{
+ $results[$id] = $result;
+ }
+ }
+
+ $serialization = [
+ 'printrequests' => $printRequests,
+ 'results' => $results,
+
+ // If we wanted to be able to deserialize a serialized QueryResult,
+ // we would need to following information as well.
+ // 'ask' => $queryResult->getQuery()->toArray()
+ ];
+
+ return $serialization;
+ }
+
+ private static function serialize_printrequest( $printRequest ) {
+
+ $serialized = [
+ 'label' => $printRequest->getLabel(),
+ 'key' => '',
+ 'redi' => '',
+ 'typeid' => $printRequest->getTypeID(),
+ 'mode' => $printRequest->getMode(),
+ 'format' => $printRequest->getOutputFormat()
+ ];
+
+ $data = $printRequest->getData();
+
+ if ( $printRequest->isMode( PrintRequest::PRINT_CHAIN ) ) {
+ $serialized['chain'] = $data->getDataItem()->getString();
+ $serialized['key'] = $data->getLastPropertyChainValue()->getDataItem()->getKey();
+ }
+
+ if ( !$printRequest->isMode( PrintRequest::PRINT_PROP ) ) {
+ return $serialized;
+ }
+
+ if ( $data === null ) {
+ return $serialized;
+ }
+
+ $serialized['redi'] = '';
+
+ // To match forwarded redirects
+ if ( !$data->getInceptiveProperty()->equals( $data->getDataItem() ) ) {
+ $serialized['redi'] = $data->getInceptiveProperty()->getKey();
+ }
+
+ // To match internal properties like _MDAT
+ $serialized['key'] = $data->getDataItem()->getKey();
+
+ return $serialized;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Serializers/SemanticDataSerializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/SemanticDataSerializer.php
new file mode 100644
index 00000000..63085584
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Serializers/SemanticDataSerializer.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace SMW\Serializers;
+
+use OutOfBoundsException;
+use Serializers\Serializer;
+use SMW\SemanticData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataSerializer implements Serializer {
+
+ /**
+ * @see Serializer::serialize
+ *
+ * @since 1.9
+ */
+ public function serialize( $semanticData ) {
+
+ if ( !$semanticData instanceof SemanticData ) {
+ throw new OutOfBoundsException( 'Object is not supported' );
+ }
+
+ return $this->doSerialize( $semanticData ) + [ 'serializer' => __CLASS__, 'version' => 2 ];
+ }
+
+ private function doSerialize( SemanticData $semanticData ) {
+
+ $data = [
+ 'subject' => $semanticData->getSubject()->getSerialization(),
+ 'data' => $this->doSerializeProperty( $semanticData )
+ ];
+
+ $subobjects = $this->doSerializeSubSemanticData(
+ $semanticData->getSubSemanticData()
+ );
+
+ if ( $subobjects !== [] ) {
+ $data['sobj'] = $subobjects;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Build property and dataItem serialization record
+ *
+ * @return array
+ */
+ private function doSerializeProperty( $semanticData ) {
+
+ $properties = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $properties[] = [
+ 'property' => $property->getSerialization(),
+ 'dataitem' => $this->doSerializeDataItem( $semanticData, $property )
+ ];
+ }
+
+ return $properties;
+ }
+
+ /**
+ * Returns DataItem serialization
+ *
+ * @note 'type' is added to ensure that during unserialization the type
+ * definition of the requested data is in alignment with the definition found
+ * in the system (type changes that can occur during the time between
+ * serialization and unserialization)
+ *
+ * @return array
+ */
+ private function doSerializeDataItem( $semanticData, $property ) {
+
+ $dataItems = [];
+
+ foreach ( $semanticData->getPropertyValues( $property ) as $dataItem ) {
+ $dataItems[] = [
+ 'type' => $dataItem->getDIType(),
+ 'item' => $dataItem->getSerialization()
+ ];
+ }
+
+ return $dataItems;
+ }
+
+ /**
+ * Returns all subobjects of a SemanticData instance
+ *
+ * @return array
+ */
+ protected function doSerializeSubSemanticData( $subSemanticData ) {
+
+ $subobjects = [];
+
+ foreach ( $subSemanticData as $semanticData ) {
+ $subobjects[] = $this->doSerialize( $semanticData );
+ }
+
+ return $subobjects;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServiceFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServiceFactory.php
new file mode 100644
index 00000000..bcba0db8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServiceFactory.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace SMW\Services;
+
+use Onoi\CallbackContainer\ContainerBuilder;
+use SMW\DataValueFactory;
+use SMW\DataValues\InfoLinksProvider;
+use SMW\DataValues\StringValue;
+use SMW\DataValues\ValueFormatters\DispatchingDataValueFormatter;
+use SMW\DataValues\ValueFormatters\NoValueFormatter;
+use SMW\DataValues\ValueFormatters\ValueFormatter;
+use SMW\DataValues\ValueParsers\ValueParser;
+use SMW\DataValues\ValueValidators\ConstraintValueValidator;
+use SMW\PropertyRestrictionExaminer;
+use SMW\PropertySpecificationLookup;
+use SMWDataValue as DataValue;
+use SMWNumberValue as NumberValue;
+use SMWTimeValue as TimeValue;
+
+/**
+ * @private
+ *
+ * This class provides service and factory functions for DataValue objects and
+ * are only to be used for those objects.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataValueServiceFactory {
+
+ /**
+ * Indicates a DataValue service
+ */
+ const SERVICE_FILE = 'DataValueServices.php';
+
+ /**
+ * Indicates a DataValue service
+ */
+ const TYPE_INSTANCE = '__dv.';
+
+ /**
+ * Indicates a ValueParser service
+ */
+ const TYPE_PARSER = '__dv.parser.';
+
+ /**
+ * Indicates a ValueFormatter service
+ */
+ const TYPE_FORMATTER = '__dv.formatter.';
+
+ /**
+ * Indicates a ValueValidator service
+ */
+ const TYPE_VALIDATOR = '__dv.validator.';
+
+ /**
+ * Extraneous service
+ */
+ const TYPE_EXT_FUNCTION = '__dv.ext.func.';
+
+ /**
+ * @var ContainerBuilder
+ */
+ private $containerBuilder;
+
+ /**
+ * @var DispatchingDataValueFormatter
+ */
+ private $dispatchingDataValueFormatter = null;
+
+ /**
+ * @since 2.5
+ */
+ public function __construct( ContainerBuilder $containerBuilder ) {
+ $this->containerBuilder = $containerBuilder;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValue $dataValue
+ *
+ * @return InfoLinksProvider
+ */
+ public function newInfoLinksProvider( DataValue $dataValue ) {
+ return new InfoLinksProvider( $dataValue, $this->getPropertySpecificationLookup() );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return DataValueFactory
+ */
+ public function getDataValueFactory() {
+ return DataValueFactory::getInstance();
+ }
+
+ /**
+ * Imported functions registered with DataTypeRegistry::registerExtraneousFunction
+ *
+ * @since 2.5
+ *
+ * @param array $extraneousFunctions
+ */
+ public function importExtraneousFunctions( array $extraneousFunctions ) {
+ foreach ( $extraneousFunctions as $serviceName => $calllback ) {
+ $this->containerBuilder->registerCallback( self::TYPE_EXT_FUNCTION . $serviceName, $calllback );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $serviceName
+ *
+ * @return mixed
+ */
+ public function newExtraneousFunctionByName( $serviceName ) {
+ return $this->containerBuilder->create( self::TYPE_EXT_FUNCTION . $serviceName );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $typeId
+ * @param string $class
+ *
+ * @return DataValue
+ */
+ public function newDataValueByType( $typeId, $class ) {
+
+ if ( $this->containerBuilder->isRegistered( self::TYPE_INSTANCE . $typeId ) ) {
+ return $this->containerBuilder->create( self::TYPE_INSTANCE . $typeId );
+ }
+
+ // Legacy invocation, for those that have not been defined yet!s
+ return new $class( $typeId );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValue $dataValue
+ *
+ * @return ValueParser
+ */
+ public function getValueParser( DataValue $dataValue ) {
+ return $this->containerBuilder->singleton( self::TYPE_PARSER . $dataValue->getTypeID() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DataValue $dataValue
+ *
+ * @return ValueFormatter
+ */
+ public function getValueFormatter( DataValue $dataValue ) {
+
+ $id = self::TYPE_FORMATTER . $dataValue->getTypeID();
+
+ if ( $this->containerBuilder->isRegistered( $id ) ) {
+ $dataValueFormatter = $this->containerBuilder->singleton( $id );
+ } else {
+ $dataValueFormatter = $this->getDispatchableValueFormatter( $dataValue );
+ }
+
+ $dataValueFormatter->setDataValue(
+ $dataValue
+ );
+
+ return $dataValueFormatter;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return ConstraintValueValidator
+ */
+ public function getConstraintValueValidator() {
+ return $this->containerBuilder->singleton( self::TYPE_VALIDATOR . 'CompoundConstraintValueValidator' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return PropertySpecificationLookup
+ */
+ public function getPropertySpecificationLookup() {
+ return $this->containerBuilder->singleton( 'PropertySpecificationLookup' );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return PropertyRestrictionExaminer
+ */
+ public function getPropertyRestrictionExaminer() {
+
+ $propertyRestrictionExaminer = $this->containerBuilder->singleton( 'PropertyRestrictionExaminer' );
+
+ $propertyRestrictionExaminer->setUser(
+ $GLOBALS['wgUser']
+ );
+
+ return $propertyRestrictionExaminer;
+ }
+
+ private function getDispatchableValueFormatter( $dataValue ) {
+
+ if ( $this->dispatchingDataValueFormatter === null ) {
+ $this->dispatchingDataValueFormatter = $this->newDispatchingDataValueFormatter();
+ }
+
+ return $this->dispatchingDataValueFormatter->getDataValueFormatterFor( $dataValue );
+ }
+
+ private function newDispatchingDataValueFormatter() {
+
+ $dispatchingDataValueFormatter = new DispatchingDataValueFormatter();
+
+ // To be checked only after DispatchingDataValueFormatter::addDataValueFormatter did
+ // not match any previous registered DataValueFormatters
+ $dispatchingDataValueFormatter->addDefaultDataValueFormatter(
+ $this->containerBuilder->singleton( self::TYPE_FORMATTER . StringValue::TYPE_ID )
+ );
+
+ $dispatchingDataValueFormatter->addDefaultDataValueFormatter(
+ $this->containerBuilder->singleton( self::TYPE_FORMATTER . NumberValue::TYPE_ID )
+ );
+
+ $dispatchingDataValueFormatter->addDefaultDataValueFormatter(
+ $this->containerBuilder->singleton( self::TYPE_FORMATTER . TimeValue::TYPE_ID )
+ );
+
+ $dispatchingDataValueFormatter->addDefaultDataValueFormatter( new NoValueFormatter() );
+
+ return $dispatchingDataValueFormatter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServices.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServices.php
new file mode 100644
index 00000000..99b399b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/DataValueServices.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace SMW\Services;
+
+use SMW\DataValues\AllowsListValue;
+use SMW\DataValues\AllowsPatternValue;
+use SMW\DataValues\ImportValue;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DataValues\ReferenceValue;
+use SMW\DataValues\StringValue;
+use SMW\DataValues\ValueFormatters\CodeStringValueFormatter;
+use SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter;
+use SMW\DataValues\ValueFormatters\NumberValueFormatter;
+use SMW\DataValues\ValueFormatters\PropertyValueFormatter;
+use SMW\DataValues\ValueFormatters\ReferenceValueFormatter;
+use SMW\DataValues\ValueFormatters\StringValueFormatter;
+use SMW\DataValues\ValueFormatters\TimeValueFormatter;
+use SMW\DataValues\ValueParsers\AllowsListValueParser;
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+use SMW\DataValues\ValueParsers\ImportValueParser;
+use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
+use SMW\DataValues\ValueParsers\PropertyValueParser;
+use SMW\DataValues\ValueParsers\TimeValueParser;
+use SMW\DataValues\ValueValidators\AllowsListConstraintValueValidator;
+use SMW\DataValues\ValueValidators\CompoundConstraintValueValidator;
+use SMW\DataValues\ValueValidators\PatternConstraintValueValidator;
+use SMW\DataValues\ValueValidators\PropertySpecificationConstraintValueValidator;
+use SMW\DataValues\ValueValidators\UniquenessConstraintValueValidator;
+use SMWNumberValue as NumberValue;
+use SMWPropertyValue as PropertyValue;
+use SMWQuantityValue as QuantityValue;
+use SMWTimeValue as TimeValue;
+use SMW\Site;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * Services defined in this file SHOULD only be accessed via DataValueServiceFactory
+ * with services being expected to require a prefix to match each individual instance
+ * to a specific DataValue.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+return [
+
+ /**
+ * PropertyValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . PropertyValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . PropertyValue::TYPE_ID,
+ PropertyValueParser::class
+ );
+
+ $propertyValueParser = new PropertyValueParser();
+
+ $propertyValueParser->setInvalidCharacterList(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgPropertyInvalidCharacterList' )
+ );
+
+ $propertyValueParser->isCapitalLinks(
+ Site::isCapitalLinks()
+ );
+
+ return $propertyValueParser;
+ },
+
+ /**
+ * PropertyValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . PropertyValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . PropertyValue::TYPE_ID,
+ PropertyValueFormatter::class
+ );
+
+ return new PropertyValueFormatter( $containerBuilder->singleton( 'PropertySpecificationLookup' ) );
+ },
+
+ /**
+ * AllowsPatternValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . AllowsPatternValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . AllowsPatternValue::TYPE_ID,
+ AllowsPatternValueParser::class
+ );
+
+ return new AllowsPatternValueParser( $containerBuilder->singleton( 'MediaWikiNsContentReader' ) );
+ },
+
+ /**
+ * AllowsListValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . AllowsListValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . AllowsListValue::TYPE_ID,
+ AllowsListValueParser::class
+ );
+
+ return new AllowsListValueParser( $containerBuilder->singleton( 'MediaWikiNsContentReader' ) );
+ },
+
+ /**
+ * CompoundConstraintValueValidator
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_VALIDATOR . 'CompoundConstraintValueValidator' => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_VALIDATOR . 'CompoundConstraintValueValidator',
+ CompoundConstraintValueValidator::class
+ );
+
+ $compoundConstraintValueValidator = new CompoundConstraintValueValidator();
+
+ // Any registered ConstraintValueValidator becomes weaker(diminished) in the context
+ // of a preceding validator
+ $compoundConstraintValueValidator->registerConstraintValueValidator(
+ new UniquenessConstraintValueValidator(
+ $containerBuilder->singleton( 'Store' ),
+ $containerBuilder->singleton( 'PropertySpecificationLookup' )
+ )
+ );
+
+ $patternConstraintValueValidator = new PatternConstraintValueValidator(
+ $containerBuilder->create( DataValueServiceFactory::TYPE_PARSER . AllowsPatternValue::TYPE_ID )
+ );
+
+ $compoundConstraintValueValidator->registerConstraintValueValidator(
+ $patternConstraintValueValidator
+ );
+
+ $allowsListConstraintValueValidator = new AllowsListConstraintValueValidator(
+ $containerBuilder->create( DataValueServiceFactory::TYPE_PARSER . AllowsListValue::TYPE_ID ),
+ $containerBuilder->singleton( 'PropertySpecificationLookup' )
+ );
+
+ $compoundConstraintValueValidator->registerConstraintValueValidator(
+ $allowsListConstraintValueValidator
+ );
+
+ $compoundConstraintValueValidator->registerConstraintValueValidator(
+ new PropertySpecificationConstraintValueValidator()
+ );
+
+ return $compoundConstraintValueValidator;
+ },
+
+ /**
+ * ImportValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . ImportValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . ImportValue::TYPE_ID,
+ ImportValueParser::class
+ );
+
+ return new ImportValueParser( $containerBuilder->singleton( 'MediaWikiNsContentReader' ) );
+ },
+
+ /**
+ * StringValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_ID,
+ StringValueFormatter::class
+ );
+
+ $containerBuilder->registerAlias(
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_ID,
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_LEGACY_ID
+ );
+
+ return new StringValueFormatter();
+ },
+
+ /**
+ * CodeStringValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_COD_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_COD_ID,
+ CodeStringValueFormatter::class
+ );
+
+ return new CodeStringValueFormatter();
+ },
+
+ /**
+ * ReferenceValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . ReferenceValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . ReferenceValue::TYPE_ID,
+ ReferenceValueFormatter::class
+ );
+
+ return new ReferenceValueFormatter();
+ },
+
+ /**
+ * MonolingualTextValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . MonolingualTextValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . MonolingualTextValue::TYPE_ID,
+ MonolingualTextValueParser::class
+ );
+
+ return new MonolingualTextValueParser();
+ },
+
+ /**
+ * MonolingualTextValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . MonolingualTextValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . MonolingualTextValue::TYPE_ID,
+ MonolingualTextValueFormatter::class
+ );
+
+ return new MonolingualTextValueFormatter();
+ },
+
+ /**
+ * NumberValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . QuantityValue::TYPE_ID => function( $containerBuilder ) {
+ return $containerBuilder->create( DataValueServiceFactory::TYPE_FORMATTER . NumberValue::TYPE_ID );
+ },
+
+ DataValueServiceFactory::TYPE_FORMATTER . NumberValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . NumberValue::TYPE_ID,
+ NumberValueFormatter::class
+ );
+
+ return new NumberValueFormatter();
+ },
+
+ /**
+ * TimeValueFormatter
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_FORMATTER . TimeValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_FORMATTER . TimeValue::TYPE_ID,
+ TimeValueFormatter::class
+ );
+
+ return new TimeValueFormatter();
+ },
+
+ /**
+ * TimeValueParser
+ *
+ * @return callable
+ */
+ DataValueServiceFactory::TYPE_PARSER . TimeValue::TYPE_ID => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ DataValueServiceFactory::TYPE_PARSER . TimeValue::TYPE_ID,
+ TimeValueParser::class
+ );
+
+ return new TimeValueParser();
+ },
+
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/Exception/ServiceNotFoundException.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/Exception/ServiceNotFoundException.php
new file mode 100644
index 00000000..664ea144
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/Exception/ServiceNotFoundException.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Services\Exception;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ServiceNotFoundException extends InvalidArgumentException {
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function __construct( $service ) {
+ parent::__construct( "`$service` is not registered as service!" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServiceFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServiceFactory.php
new file mode 100644
index 00000000..cc9b71bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServiceFactory.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Services;
+
+use ImportSource;
+use Onoi\CallbackContainer\ContainerBuilder;
+use SMW\Importer\ContentIterator;
+
+/**
+ * @private
+ *
+ * This class provides service and factory functions for Import objects.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ImporterServiceFactory {
+
+ /**
+ * @var ContainerBuilder
+ */
+ private $containerBuilder;
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( ContainerBuilder $containerBuilder ) {
+ $this->containerBuilder = $containerBuilder;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $source
+ *
+ * @return ImportStringSource
+ */
+ public function newImportStringSource( $source ) {
+ return $this->containerBuilder->create( 'ImportStringSource', $source );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $source
+ *
+ * @return ImportStreamSource
+ */
+ public function newImportStreamSource( $source ) {
+ return $this->containerBuilder->create( 'ImportStreamSource', $source );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ImportSource $importSource
+ *
+ * @return WikiImporter
+ */
+ public function newWikiImporter( ImportSource $importSource ) {
+ return $this->containerBuilder->create( 'WikiImporter', $importSource );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ContentIterator $contentIterator
+ *
+ * @return Importer
+ */
+ public function newImporter( ContentIterator $contentIterator ) {
+ return $this->containerBuilder->create( 'Importer', $contentIterator );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return JsonContentIterator
+ */
+ public function newJsonContentIterator( $importFileDir ) {
+ return $this->containerBuilder->create( 'JsonContentIterator', $importFileDir );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServices.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServices.php
new file mode 100644
index 00000000..fb8b9b10
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/ImporterServices.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace SMW\Services;
+
+use SMW\Importer\ContentCreators\DispatchingContentCreator;
+use SMW\Importer\ContentCreators\TextContentCreator;
+use SMW\Importer\ContentCreators\XmlContentCreator;
+use SMW\Importer\ContentIterator;
+use SMW\Importer\ContentModeller;
+use SMW\Importer\Importer;
+use SMW\Importer\JsonContentIterator;
+use SMW\Importer\JsonImportContentsFileDirReader;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * Services defined in this file SHOULD only be accessed either via the
+ * ApplicationFactory or a different factory instance.
+ *
+ * @license GNU GPL v2
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+return [
+
+ /**
+ * ImporterServiceFactory
+ *
+ * @return callable
+ */
+ 'ImporterServiceFactory' => function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ImporterServiceFactory', '\SMW\Services\ImporterServiceFactory' );
+ return new ImporterServiceFactory( $containerBuilder );
+ },
+
+ /**
+ * XmlContentCreator
+ *
+ * @return callable
+ */
+ 'XmlContentCreator' => function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'XmlContentCreator', '\SMW\Importer\ContentCreators\XmlContentCreator' );
+ return new XmlContentCreator( $containerBuilder->create( 'ImporterServiceFactory' ) );
+ },
+
+ /**
+ * TextContentCreator
+ *
+ * @return callable
+ */
+ 'TextContentCreator' => function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'TextContentCreator', '\SMW\Importer\ContentCreators\TextContentCreator' );
+
+ $connectionManager = $containerBuilder->singleton( 'ConnectionManager' );
+
+ $textContentCreator = new TextContentCreator(
+ $containerBuilder->create( 'PageCreator' ),
+ $connectionManager->getConnection( 'mw.db' )
+ );
+
+ return $textContentCreator;
+ },
+
+ /**
+ * Importer
+ *
+ * @return callable
+ */
+ 'Importer' => function( $containerBuilder, ContentIterator $contentIterator ) {
+ $containerBuilder->registerExpectedReturnType( 'Importer', '\SMW\Importer\Importer' );
+
+ $dispatchingContentCreator = new DispatchingContentCreator(
+ [
+ $containerBuilder->create( 'XmlContentCreator' ),
+ $containerBuilder->create( 'TextContentCreator' )
+ ]
+ );
+
+ $importer = new Importer(
+ $contentIterator,
+ $dispatchingContentCreator
+ );
+
+ $importer->setReqVersion(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgImportReqVersion' )
+ );
+
+ return $importer;
+ },
+
+ /**
+ * JsonContentIterator
+ *
+ * @return callable
+ */
+ 'JsonContentIterator' => function( $containerBuilder, $importFileDirs ) {
+ $containerBuilder->registerExpectedReturnType( 'JsonContentIterator', '\SMW\Importer\JsonContentIterator' );
+
+ $jsonImportContentsFileDirReader = new JsonImportContentsFileDirReader(
+ new ContentModeller(),
+ $importFileDirs
+ );
+
+ return new JsonContentIterator( $jsonImportContentsFileDirReader );
+ },
+
+]; \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/MediaWikiServices.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/MediaWikiServices.php
new file mode 100644
index 00000000..1616fe0f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/MediaWikiServices.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace SMW\Services;
+
+use ImportStreamSource;
+use ImportStringSource;
+use JobQueueGroup;
+use LBFactory;
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
+use Psr\Log\NullLogger;
+use SMW\Utils\Logger;
+use WikiImporter;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * Services defined in this file SHOULD only be accessed either via the
+ * ApplicationFactory or a different factory instance.
+ *
+ * @license GNU GPL v2
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+return [
+
+ /**
+ * ImportStringSource
+ *
+ * @return callable
+ */
+ 'ImportStringSource' => function( $containerBuilder, $source ) {
+ $containerBuilder->registerExpectedReturnType( 'ImportStringSource', '\ImportStringSource' );
+ return new ImportStringSource( $source );
+ },
+
+ /**
+ * ImportStreamSource
+ *
+ * @return callable
+ */
+ 'ImportStreamSource' => function( $containerBuilder, $source ) {
+ $containerBuilder->registerExpectedReturnType( 'ImportStreamSource', '\ImportStreamSource' );
+ return new ImportStreamSource( $source );
+ },
+
+ /**
+ * WikiImporter
+ *
+ * @return callable
+ */
+ 'WikiImporter' => function( $containerBuilder, \ImportSource $importSource ) {
+ $containerBuilder->registerExpectedReturnType( 'WikiImporter', '\WikiImporter' );
+ return new WikiImporter( $importSource, $containerBuilder->create( 'MainConfig' ) );
+ },
+
+ /**
+ * WikiPage
+ *
+ * @return callable
+ */
+ 'WikiPage' => function( $containerBuilder, \Title $title ) {
+ $containerBuilder->registerExpectedReturnType( 'WikiPage', '\WikiPage' );
+ return \WikiPage::factory( $title );
+ },
+
+ /**
+ * Config
+ *
+ * @return callable
+ */
+ 'MainConfig' => function( $containerBuilder ) {
+
+ // > MW 1.27
+ if ( class_exists( '\MediaWiki\MediaWikiServices' ) && method_exists( '\MediaWiki\MediaWikiServices', 'getMainConfig' ) ) {
+ return MediaWikiServices::getInstance()->getMainConfig();
+ }
+
+ return \ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+ },
+
+ /**
+ * LBFactory
+ *
+ * @return callable
+ */
+ 'DBLoadBalancerFactory' => function( $containerBuilder ) {
+
+ if ( class_exists( '\Wikimedia\Rdbms\LBFactory' ) ) {
+ $containerBuilder->registerExpectedReturnType( 'DBLoadBalancerFactory', '\Wikimedia\Rdbms\LBFactory' );
+ } else {
+ $containerBuilder->registerExpectedReturnType( 'DBLoadBalancerFactory', '\LBFactory' );
+ }
+
+ // > MW 1.28
+ if ( class_exists( '\MediaWiki\MediaWikiServices' ) && method_exists( '\MediaWiki\MediaWikiServices', 'getDBLoadBalancerFactory' ) ) {
+ return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ }
+
+ return LBFactory::singleton();
+ },
+
+ /**
+ * DBLoadBalancer
+ *
+ * @return callable
+ */
+ 'DBLoadBalancer' => function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'DBLoadBalancer', '\LoadBalancer' );
+
+ // > MW 1.27
+ if ( class_exists( '\MediaWiki\MediaWikiServices' ) && method_exists( '\MediaWiki\MediaWikiServices', 'getDBLoadBalancer' ) ) {
+ return MediaWikiServices::getInstance()->getDBLoadBalancer();
+ }
+
+ return LBFactory::singleton()->getMainLB();
+ },
+
+ /**
+ * DBLoadBalancer
+ *
+ * @return callable
+ */
+ 'DefaultSearchEngineTypeForDB' => function( $containerBuilder, \IDatabase $db ) {
+
+ // MW > 1.27
+ if ( class_exists( '\MediaWiki\MediaWikiServices' ) && method_exists( 'SearchEngineFactory', 'getSearchEngineClass' ) ) {
+ return MediaWikiServices::getInstance()->getSearchEngineFactory()->getSearchEngineClass( $db );
+ }
+
+ return $db->getSearchEngine();
+ },
+
+ /**
+ * MediaWikiLogger
+ *
+ * @return callable
+ */
+ 'MediaWikiLogger' => function( $containerBuilder, $channel = 'smw', $role = Logger::ROLE_DEVELOPER ) {
+
+ $containerBuilder->registerExpectedReturnType( 'MediaWikiLogger', '\Psr\Log\LoggerInterface' );
+
+ if ( class_exists( '\MediaWiki\Logger\LoggerFactory' ) ) {
+ $logger = LoggerFactory::getInstance( $channel );
+ } else {
+ $logger = new NullLogger();
+ }
+
+ return new Logger( $logger, $role );
+ },
+
+ /**
+ * JobQueueGroup
+ *
+ * @return callable
+ */
+ 'JobQueueGroup' => function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType( 'JobQueueGroup', '\JobQueueGroup' );
+
+ return JobQueueGroup::singleton();
+ },
+
+];
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/README.md b/www/wiki/extensions/SemanticMediaWiki/src/Services/README.md
new file mode 100644
index 00000000..97debc21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/README.md
@@ -0,0 +1,27 @@
+Services contain object definitions that with the help of a [ContainerBuilder](https://github.com/onoi/callback-container)
+will manage the object build process and provides instance reuse if necessary. Object instances are normally accessed using
+dedicated factory methods.
+
+## Service files and specification
+
+* `DataValueServiceFactory` provides service and factory functions for
+ `DataValue` objects that are specified in `DataValueServices.php`
+* `ImporterServices.php` provides services for the [Importer](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/src/Importer)
+* `MediaWikiServices.php` isolates MediaWiki specific functions and services
+* `SharedServicesContainer.php` contains common and shared object definitions used
+ throughout the Semantic MediaWiki code base and are accessible via `ApplicationFactory`
+
+## ContainerBuilder
+
+<pre>
+$containerBuilder = new CallbackContainerFactory();
+$containerBuilder = $callbackContainerFactory->newCallbackContainerBuilder();
+
+$containerBuilder->registerCallbackContainer( new SharedServicesContainer() );
+$containerBuilder->registerFromFile(
+ $GLOBALS['smwgServicesFileDir'] . '/' . 'MediaWikiServices.php'
+);
+</pre>
+
+[`$smwgServicesFileDir`](https://www.semantic-mediawiki.org/wiki/Help:$smwgServicesFileDir) describes the location of the
+service directory.
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/ServicesContainer.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/ServicesContainer.php
new file mode 100644
index 00000000..d5d763a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/ServicesContainer.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Services;
+
+use RuntimeException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ServicesContainer {
+
+ /**
+ * @var callable[]
+ */
+ private $services;
+
+ /**
+ * @since 3.0
+ */
+ public function __construct( array $services = [] ) {
+ $this->services = $services;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function get( $key, ...$args ) {
+
+ if ( !isset( $this->services[$key] ) ) {
+ throw new RuntimeException( "$key is an unknown service!" );
+ };
+
+ $type = null;
+ $service = $this->services[$key];
+
+ if ( !is_callable( $service ) && isset( $service['_type'] ) && isset( $service['_service'] ) ) {
+ $type = $service['_type'];
+ $service = $service['_service'];
+ }
+
+ if ( !is_callable( $service ) ) {
+ throw new RuntimeException( "$key is not a callable service!" );
+ };
+
+ $instance = $service( ...$args );
+
+ if ( $type !== null && !is_a( $instance, $type ) ) {
+ throw new RuntimeException( "Service $key is not of the expected $type type!" );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param callable $service
+ */
+ public function add( $key, callable $service ) {
+ $this->services[$key] = $service;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Services/SharedServicesContainer.php b/www/wiki/extensions/SemanticMediaWiki/src/Services/SharedServicesContainer.php
new file mode 100644
index 00000000..4f703fed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Services/SharedServicesContainer.php
@@ -0,0 +1,705 @@
+<?php
+
+namespace SMW\Services;
+
+use JsonSchema\Validator as SchemaValidator;
+use Onoi\BlobStore\BlobStore;
+use Onoi\CallbackContainer\CallbackContainer;
+use Onoi\CallbackContainer\ContainerBuilder;
+use SMW\CachedPropertyValuesPrefetcher;
+use SMW\CacheFactory;
+use SMW\ContentParser;
+use SMW\DataItemFactory;
+use SMW\Factbox\FactboxFactory;
+use SMW\HierarchyLookup;
+use SMW\InMemoryPoolCache;
+use SMW\IteratorFactory;
+use SMW\Localizer;
+use SMW\MediaWiki\Database;
+use SMW\Connection\ConnectionManager;
+use SMW\MediaWiki\Connection\ConnectionProvider;
+use SMW\MediaWiki\Deferred\CallableUpdate;
+use SMW\MediaWiki\Deferred\TransactionalCallableUpdate;
+use SMW\MediaWiki\JobQueue;
+use SMW\MediaWiki\JobFactory;
+use SMW\MediaWiki\ManualEntryLogger;
+use SMW\MediaWiki\MediaWikiNsContentReader;
+use SMW\MediaWiki\PageCreator;
+use SMW\MediaWiki\PageUpdater;
+use SMW\MediaWiki\TitleFactory;
+use SMW\MessageFormatter;
+use SMW\NamespaceExaminer;
+use SMW\Parser\LinksProcessor;
+use SMW\PermissionPthValidator;
+use SMW\ParserData;
+use SMW\PostProcHandler;
+use SMW\PropertyAnnotatorFactory;
+use SMW\PropertyLabelFinder;
+use SMW\PropertyRestrictionExaminer;
+use SMW\PropertySpecificationLookup;
+use SMW\Protection\EditProtectionUpdater;
+use SMW\Protection\ProtectionValidator;
+use SMW\Query\QuerySourceFactory;
+use SMW\Query\Result\CachedQueryResultPrefetcher;
+use SMW\Schema\SchemaFactory;
+use SMW\Settings;
+use SMW\Options;
+use SMW\StoreFactory;
+use SMW\Utils\BufferedStatsdCollector;
+use SMW\Utils\JsonSchemaValidator;
+use SMW\Utils\TempFile;
+use SMW\Elastic\ElasticFactory;
+use SMW\SQLStore\QueryDependencyLinksStoreFactory;
+use SMW\QueryFactory;
+use SMW\Query\Processor\QueryCreator;
+use SMW\Query\Processor\ParamListProcessor;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SharedServicesContainer implements CallbackContainer {
+
+ /**
+ * @see CallbackContainer::register
+ *
+ * @since 2.3
+ */
+ public function register( ContainerBuilder $containerBuilder ) {
+
+ $containerBuilder->registerCallback( 'Store', [ $this, 'newStore' ] );
+
+ $this->registerCallbackHandlers( $containerBuilder );
+ $this->registerCallableFactories( $containerBuilder );
+ $this->registerCallbackHandlersByConstructedInstance( $containerBuilder );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Store
+ */
+ public function newStore( $containerBuilder, $storeClass = null ) {
+
+ $containerBuilder->registerExpectedReturnType( 'Store', '\SMW\Store' );
+ $settings = $containerBuilder->singleton( 'Settings' );
+
+ if ( $storeClass === null || $storeClass === '' ) {
+ $storeClass = $settings->get( 'smwgDefaultStore' );
+ }
+
+ $store = StoreFactory::getStore( $storeClass );
+
+ $configs = [
+ 'smwgDefaultStore',
+ 'smwgSemanticsEnabled',
+ 'smwgAutoRefreshSubject',
+ 'smwgEnableUpdateJobs',
+ 'smwgQEqualitySupport',
+ 'smwgElasticsearchConfig'
+ ];
+
+ foreach ( $configs as $config ) {
+ $store->setOption( $config, $settings->get( $config ) );
+ }
+
+ $store->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ return $store;
+ }
+
+ private function registerCallbackHandlers( $containerBuilder ) {
+
+ $containerBuilder->registerCallback( 'Settings', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'Settings', '\SMW\Settings' );
+ return Settings::newFromGlobals();
+ } );
+
+ /**
+ * ConnectionManager
+ *
+ * @return callable
+ */
+ $containerBuilder->registerCallback( 'ConnectionManager', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ConnectionManager', ConnectionManager::class );
+ return new ConnectionManager();
+ } );
+
+ $containerBuilder->registerCallback( 'Cache', function( $containerBuilder, $cacheType = null ) {
+ $containerBuilder->registerExpectedReturnType( 'Cache', '\Onoi\Cache\Cache' );
+ return $containerBuilder->create( 'CacheFactory' )->newMediaWikiCompositeCache( $cacheType );
+ } );
+
+ $containerBuilder->registerCallback( 'NamespaceExaminer', function() use ( $containerBuilder ) {
+ return NamespaceExaminer::newFromArray( $containerBuilder->singleton( 'Settings' )->get( 'smwgNamespacesWithSemanticLinks' ) );
+ } );
+
+ $containerBuilder->registerCallback( 'ParserData', function( $containerBuilder, \Title $title, \ParserOutput $parserOutput ) {
+ $containerBuilder->registerExpectedReturnType( 'ParserData', ParserData::class );
+
+ $parserData = new ParserData( $title, $parserOutput );
+
+ $parserData->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ return $parserData;
+ } );
+
+ $containerBuilder->registerCallback( 'LinksProcessor', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'LinksProcessor', '\SMW\Parser\LinksProcessor' );
+ return new LinksProcessor();
+ } );
+
+ $containerBuilder->registerCallback( 'MessageFormatter', function( $containerBuilder, \Language $language ) {
+ $containerBuilder->registerExpectedReturnType( 'MessageFormatter', '\SMW\MessageFormatter' );
+ return new MessageFormatter( $language );
+ } );
+
+ $containerBuilder->registerCallback( 'MediaWikiNsContentReader', function() use ( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'MediaWikiNsContentReader', '\SMW\MediaWiki\MediaWikiNsContentReader' );
+ return new MediaWikiNsContentReader();
+ } );
+
+ $containerBuilder->registerCallback( 'PageCreator', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PageCreator', '\SMW\MediaWiki\PageCreator' );
+ return new PageCreator();
+ } );
+
+ $containerBuilder->registerCallback( 'PageUpdater', function( $containerBuilder, $connection, TransactionalCallableUpdate $transactionalCallableUpdate = null ) {
+ $containerBuilder->registerExpectedReturnType( 'PageUpdater', '\SMW\MediaWiki\PageUpdater' );
+ return new PageUpdater( $connection, $transactionalCallableUpdate );
+ } );
+
+ /**
+ * JobQueue
+ *
+ * @return callable
+ */
+ $containerBuilder->registerCallback( 'JobQueue', function( $containerBuilder ) {
+
+ $containerBuilder->registerExpectedReturnType(
+ 'JobQueue',
+ '\SMW\MediaWiki\JobQueue'
+ );
+
+ return new JobQueue(
+ $containerBuilder->create( 'JobQueueGroup' )
+ );
+ } );
+
+ $containerBuilder->registerCallback( 'ManualEntryLogger', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ManualEntryLogger', '\SMW\MediaWiki\ManualEntryLogger' );
+ return new ManualEntryLogger();
+ } );
+
+ $containerBuilder->registerCallback( 'TitleFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'TitleFactory', '\SMW\MediaWiki\TitleFactory' );
+ return new TitleFactory();
+ } );
+
+ $containerBuilder->registerCallback( 'ContentParser', function( $containerBuilder, \Title $title ) {
+ $containerBuilder->registerExpectedReturnType( 'ContentParser', '\SMW\ContentParser' );
+ return new ContentParser( $title );
+ } );
+
+ $containerBuilder->registerCallback( 'DeferredCallableUpdate', function( $containerBuilder, callable $callback = null ) {
+ $containerBuilder->registerExpectedReturnType( 'DeferredCallableUpdate', '\SMW\MediaWiki\Deferred\CallableUpdate' );
+ $containerBuilder->registerAlias( 'CallableUpdate', CallableUpdate::class );
+
+ return new CallableUpdate( $callback );
+ } );
+
+ $containerBuilder->registerCallback( 'DeferredTransactionalCallableUpdate', function( $containerBuilder, callable $callback = null, Database $connection = null ) {
+ $containerBuilder->registerExpectedReturnType( 'DeferredTransactionalUpdate', '\SMW\MediaWiki\Deferred\TransactionalCallableUpdate' );
+ $containerBuilder->registerAlias( 'DeferredTransactionalUpdate', TransactionalCallableUpdate::class );
+
+ return new TransactionalCallableUpdate( $callback, $connection );
+ } );
+
+ /**
+ * @var InMemoryPoolCache
+ */
+ $containerBuilder->registerCallback( 'InMemoryPoolCache', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'InMemoryPoolCache', '\SMW\InMemoryPoolCache' );
+ return InMemoryPoolCache::getInstance();
+ } );
+
+ /**
+ * @var PropertyAnnotatorFactory
+ */
+ $containerBuilder->registerCallback( 'PropertyAnnotatorFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PropertyAnnotatorFactory', '\SMW\PropertyAnnotatorFactory' );
+ return new PropertyAnnotatorFactory();
+ } );
+
+ /**
+ * @var ConnectionProvider
+ */
+ $containerBuilder->registerAlias( 'ConnectionProvider', 'DBConnectionProvider' );
+
+ $containerBuilder->registerCallback( 'ConnectionProvider', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ConnectionProvider', ConnectionProvider::class );
+
+ $connectionProvider = new ConnectionProvider();
+
+ $connectionProvider->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ return $connectionProvider;
+ } );
+
+ /**
+ * @var TempFile
+ */
+ $containerBuilder->registerCallback( 'TempFile', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'TempFile', '\SMW\Utils\TempFile' );
+ return new TempFile();
+ } );
+
+ /**
+ * @var PostProcHandler
+ */
+ $containerBuilder->registerCallback( 'PostProcHandler', function( $containerBuilder, \ParserOutput $parserOutput ) {
+ $containerBuilder->registerExpectedReturnType( 'PostProcHandler', PostProcHandler::class );
+
+ $settings = $containerBuilder->singleton( 'Settings' );
+
+ $postProcHandler = new PostProcHandler(
+ $parserOutput,
+ $containerBuilder->singleton( 'Cache' )
+ );
+
+ $postProcHandler->setOptions(
+ $settings->get( 'smwgPostEditUpdate' ) +
+ [ 'smwgEnabledQueryDependencyLinksStore' => $settings->get( 'smwgEnabledQueryDependencyLinksStore' ) ] +
+ [ 'smwgEnabledFulltextSearch' => $settings->get( 'smwgEnabledFulltextSearch' ) ]
+ );
+
+ return $postProcHandler;
+ } );
+
+ /**
+ * @var JsonSchemaValidator
+ */
+ $containerBuilder->registerCallback( 'JsonSchemaValidator', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'JsonSchemaValidator', JsonSchemaValidator::class );
+ $containerBuilder->registerAlias( 'JsonSchemaValidator', JsonSchemaValidator::class );
+
+ $schemaValidator = null;
+
+ // justinrainbow/json-schema
+ if ( class_exists( SchemaValidator::class ) ) {
+ $schemaValidator = new SchemaValidator();
+ }
+
+ $jsonSchemaValidator = new JsonSchemaValidator(
+ $schemaValidator
+ );
+
+ return $jsonSchemaValidator;
+ } );
+
+ /**
+ * @var SchemaFactory
+ */
+ $containerBuilder->registerCallback( 'SchemaFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'SchemaFactory', SchemaFactory::class );
+
+ $settings = $containerBuilder->singleton( 'Settings' );
+
+ $schemaFactory = new SchemaFactory(
+ $settings->get( 'smwgSchemaTypes' )
+ );
+
+ return $schemaFactory;
+ } );
+
+ /**
+ * @var ElasticFactory
+ */
+ $containerBuilder->registerCallback( 'ElasticFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ElasticFactory', ElasticFactory::class );
+ return new ElasticFactory();
+ } );
+
+ /**
+ * @var Creator
+ */
+ $containerBuilder->registerCallback( 'QueryCreator', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'QueryCreator', QueryCreator::class );
+
+ $settings = $containerBuilder->singleton( 'Settings' );
+
+ $queryCreator = new QueryCreator(
+ $containerBuilder->singleton( 'QueryFactory' ),
+ $settings->get( 'smwgQDefaultNamespaces' ),
+ $settings->get( 'smwgQDefaultLimit' )
+ );
+
+ $queryCreator->setQFeatures(
+ $settings->get( 'smwgQFeatures' )
+ );
+
+ $queryCreator->setQConceptFeatures(
+ $settings->get( 'smwgQConceptFeatures' )
+ );
+
+ return $queryCreator;
+ } );
+
+ /**
+ * @var ParamListProcessor
+ */
+ $containerBuilder->registerCallback( 'ParamListProcessor', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ParamListProcessor', ParamListProcessor::class );
+
+ $paramListProcessor = new ParamListProcessor(
+ //$containerBuilder->singleton( 'PrintRequestFactory' )
+ );
+
+ return $paramListProcessor;
+ } );
+ }
+
+ private function registerCallableFactories( $containerBuilder ) {
+
+ /**
+ * @var CacheFactory
+ */
+ $containerBuilder->registerCallback( 'CacheFactory', function( $containerBuilder, $mainCacheType = null ) {
+ $containerBuilder->registerExpectedReturnType( 'CacheFactory', '\SMW\CacheFactory' );
+ return new CacheFactory( $mainCacheType );
+ } );
+
+ /**
+ * @var IteratorFactory
+ */
+ $containerBuilder->registerCallback( 'IteratorFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'IteratorFactory', '\SMW\IteratorFactory' );
+ return new IteratorFactory();
+ } );
+
+ /**
+ * @var JobFactory
+ */
+ $containerBuilder->registerCallback( 'JobFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'JobFactory', '\SMW\MediaWiki\JobFactory' );
+ return new JobFactory();
+ } );
+
+ /**
+ * @var FactboxFactory
+ */
+ $containerBuilder->registerCallback( 'FactboxFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'FactboxFactory', '\SMW\Factbox\FactboxFactory' );
+ return new FactboxFactory();
+ } );
+
+ /**
+ * @var QuerySourceFactory
+ */
+ $containerBuilder->registerCallback( 'QuerySourceFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'QuerySourceFactory', '\SMW\Query\QuerySourceFactory' );
+
+ return new QuerySourceFactory(
+ $containerBuilder->singleton( 'Store', null ),
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgQuerySources' )
+ );
+ } );
+
+ /**
+ * @var QueryFactory
+ */
+ $containerBuilder->registerCallback( 'QueryFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'QueryFactory', '\SMW\QueryFactory' );
+ return new QueryFactory();
+ } );
+
+ /**
+ * @var DataItemFactory
+ */
+ $containerBuilder->registerCallback( 'DataItemFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'DataItemFactory', '\SMW\DataItemFactory' );
+ return new DataItemFactory();
+ } );
+
+ /**
+ * @var DataValueServiceFactory
+ */
+ $containerBuilder->registerCallback( 'DataValueServiceFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'DataValueServiceFactory', '\SMW\Services\DataValueServiceFactory' );
+
+ $containerBuilder->registerFromFile(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgServicesFileDir' ) . '/' . DataValueServiceFactory::SERVICE_FILE
+ );
+
+ $dataValueServiceFactory = new DataValueServiceFactory(
+ $containerBuilder
+ );
+
+ return $dataValueServiceFactory;
+ } );
+
+ /**
+ * @var QueryDependencyLinksStoreFactory
+ */
+ $containerBuilder->registerCallback( 'QueryDependencyLinksStoreFactory', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'QueryDependencyLinksStoreFactory', '\SMW\SQLStore\QueryDependencyLinksStoreFactory' );
+ return new QueryDependencyLinksStoreFactory();
+ } );
+
+ }
+
+ private function registerCallbackHandlersByConstructedInstance( $containerBuilder ) {
+
+ /**
+ * @var BlobStore
+ */
+ $containerBuilder->registerCallback( 'BlobStore', function( $containerBuilder, $namespace, $cacheType = null, $ttl = 0 ) {
+ $containerBuilder->registerExpectedReturnType( 'BlobStore', '\Onoi\BlobStore\BlobStore' );
+
+ $cacheFactory = $containerBuilder->create( 'CacheFactory' );
+
+ $blobStore = new BlobStore(
+ $namespace,
+ $cacheFactory->newMediaWikiCompositeCache( $cacheType )
+ );
+
+ $blobStore->setNamespacePrefix(
+ $cacheFactory->getCachePrefix()
+ );
+
+ $blobStore->setExpiryInSeconds(
+ $ttl
+ );
+
+ $blobStore->setUsageState(
+ $cacheType !== CACHE_NONE && $cacheType !== false
+ );
+
+ return $blobStore;
+ } );
+
+ /**
+ * @var CachedQueryResultPrefetcher
+ */
+ $containerBuilder->registerCallback( 'CachedQueryResultPrefetcher', function( $containerBuilder, $cacheType = null ) {
+ $containerBuilder->registerExpectedReturnType( 'CachedQueryResultPrefetcher', '\SMW\Query\Result\CachedQueryResultPrefetcher' );
+
+ $settings = $containerBuilder->singleton( 'Settings' );
+ $cacheType = $cacheType === null ? $settings->get( 'smwgQueryResultCacheType' ) : $cacheType;
+
+ $cachedQueryResultPrefetcher = new CachedQueryResultPrefetcher(
+ $containerBuilder->singleton( 'Store', null ),
+ $containerBuilder->singleton( 'QueryFactory' ),
+ $containerBuilder->create(
+ 'BlobStore',
+ CachedQueryResultPrefetcher::CACHE_NAMESPACE,
+ $cacheType,
+ $settings->get( 'smwgQueryResultCacheLifetime' )
+ ),
+ $containerBuilder->singleton(
+ 'BufferedStatsdCollector',
+ CachedQueryResultPrefetcher::STATSD_ID
+ )
+ );
+
+ $cachedQueryResultPrefetcher->setDependantHashIdExtension(
+ // If the mix of dataTypes changes then modify the hash
+ $settings->get( 'smwgFulltextSearchIndexableDataTypes' ) .
+
+ // If the collation is altered then modify the hash as it
+ // is likely that the sort order of results change
+ $settings->get( 'smwgEntityCollation' ) .
+
+ // Changing the sobj has computation should invalidate
+ // existing caches to avoid oudated references SOBJ IDs
+ $settings->get( 'smwgUseComparableContentHash' )
+ );
+
+ $cachedQueryResultPrefetcher->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ $cachedQueryResultPrefetcher->setNonEmbeddedCacheLifetime(
+ $settings->get( 'smwgQueryResultNonEmbeddedCacheLifetime' )
+ );
+
+ return $cachedQueryResultPrefetcher;
+ } );
+
+ /**
+ * @var CachedPropertyValuesPrefetcher
+ */
+ $containerBuilder->registerCallback( 'CachedPropertyValuesPrefetcher', function( $containerBuilder, $cacheType = null, $ttl = 604800 ) {
+ $containerBuilder->registerExpectedReturnType( 'CachedPropertyValuesPrefetcher', CachedPropertyValuesPrefetcher::class );
+
+ $cachedPropertyValuesPrefetcher = new CachedPropertyValuesPrefetcher(
+ $containerBuilder->singleton( 'Store', null ),
+ $containerBuilder->create( 'BlobStore', CachedPropertyValuesPrefetcher::CACHE_NAMESPACE, $cacheType, $ttl )
+ );
+
+ return $cachedPropertyValuesPrefetcher;
+ } );
+
+ /**
+ * @var BufferedStatsdCollector
+ */
+ $containerBuilder->registerCallback( 'BufferedStatsdCollector', function( $containerBuilder, $id ) {
+ $containerBuilder->registerExpectedReturnType( 'BufferedStatsdCollector', '\SMW\Utils\BufferedStatsdCollector' );
+
+ // Explicitly use the DB to access a SqlBagOstuff instance
+ $cacheType = CACHE_DB;
+ $ttl = 0;
+
+ $bufferedStatsdCollector = new BufferedStatsdCollector(
+ $containerBuilder->create( 'BlobStore', BufferedStatsdCollector::CACHE_NAMESPACE, $cacheType, $ttl ),
+ $id
+ );
+
+ return $bufferedStatsdCollector;
+ } );
+
+ /**
+ * @var PropertySpecificationLookup
+ */
+ $containerBuilder->registerCallback( 'PropertySpecificationLookup', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PropertySpecificationLookup', '\SMW\PropertySpecificationLookup' );
+
+ $propertySpecificationLookup = new PropertySpecificationLookup(
+ $containerBuilder->singleton( 'CachedPropertyValuesPrefetcher' ),
+ $containerBuilder->singleton( 'InMemoryPoolCache' )->getPoolCacheById( PropertySpecificationLookup::POOLCACHE_ID )
+ );
+
+ return $propertySpecificationLookup;
+ } );
+
+ /**
+ * @var ProtectionValidator
+ */
+ $containerBuilder->registerCallback( 'ProtectionValidator', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'ProtectionValidator', '\SMW\Protection\ProtectionValidator' );
+
+ $protectionValidator = new ProtectionValidator(
+ $containerBuilder->singleton( 'CachedPropertyValuesPrefetcher' ),
+ $containerBuilder->singleton( 'InMemoryPoolCache' )->getPoolCacheById( ProtectionValidator::POOLCACHE_ID )
+ );
+
+ $protectionValidator->setEditProtectionRight(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgEditProtectionRight' )
+ );
+
+ $protectionValidator->setCreateProtectionRight(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgCreateProtectionRight' )
+ );
+
+ $protectionValidator->setChangePropagationProtection(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgChangePropagationProtection' )
+ );
+
+ return $protectionValidator;
+ } );
+
+ /**
+ * @var PermissionPthValidator
+ */
+ $containerBuilder->registerCallback( 'PermissionPthValidator', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PermissionPthValidator', '\SMW\PermissionPthValidator' );
+
+ $permissionPthValidator = new PermissionPthValidator(
+ $containerBuilder->create( 'ProtectionValidator' )
+ );
+
+ return $permissionPthValidator;
+ } );
+
+
+ /**
+ * @var EditProtectionUpdater
+ */
+ $containerBuilder->registerCallback( 'EditProtectionUpdater', function( $containerBuilder, \WikiPage $wikiPage, \User $user = null ) {
+ $containerBuilder->registerExpectedReturnType( 'EditProtectionUpdater', '\SMW\Protection\EditProtectionUpdater' );
+
+ $editProtectionUpdater = new EditProtectionUpdater(
+ $wikiPage,
+ $user
+ );
+
+ $editProtectionUpdater->setEditProtectionRight(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgEditProtectionRight' )
+ );
+
+ $editProtectionUpdater->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ return $editProtectionUpdater;
+ } );
+
+ /**
+ * @var PropertyRestrictionExaminer
+ */
+ $containerBuilder->registerCallback( 'PropertyRestrictionExaminer', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PropertyRestrictionExaminer', '\SMW\PropertyRestrictionExaminer' );
+
+ $propertyRestrictionExaminer = new PropertyRestrictionExaminer();
+
+ $propertyRestrictionExaminer->setCreateProtectionRight(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgCreateProtectionRight' )
+ );
+
+ return $propertyRestrictionExaminer;
+ } );
+
+ /**
+ * @var HierarchyLookup
+ */
+ $containerBuilder->registerCallback( 'HierarchyLookup', function( $containerBuilder, $store = null, $cacheType = null ) {
+ $containerBuilder->registerExpectedReturnType( 'HierarchyLookup', '\SMW\HierarchyLookup' );
+
+ $hierarchyLookup = new HierarchyLookup(
+ $containerBuilder->singleton( 'Store', null ),
+ $containerBuilder->singleton( 'Cache', $cacheType )
+ );
+
+ $hierarchyLookup->setLogger(
+ $containerBuilder->singleton( 'MediaWikiLogger' )
+ );
+
+ $hierarchyLookup->setSubcategoryDepth(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgQSubcategoryDepth' )
+ );
+
+ $hierarchyLookup->setSubpropertyDepth(
+ $containerBuilder->singleton( 'Settings' )->get( 'smwgQSubpropertyDepth' )
+ );
+
+ return $hierarchyLookup;
+ } );
+
+ /**
+ * @var PropertyLabelFinder
+ */
+ $containerBuilder->registerCallback( 'PropertyLabelFinder', function( $containerBuilder ) {
+ $containerBuilder->registerExpectedReturnType( 'PropertyLabelFinder', '\SMW\PropertyLabelFinder' );
+
+ $lang = Localizer::getInstance()->getLang();
+
+ $propertyLabelFinder = new PropertyLabelFinder(
+ $containerBuilder->singleton( 'Store', null ),
+ $lang->getPropertyLabels(),
+ $lang->getCanonicalPropertyLabels(),
+ $lang->getCanonicalDatatypeLabels()
+ );
+
+ return $propertyLabelFinder;
+ } );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Site.php b/www/wiki/extensions/SemanticMediaWiki/src/Site.php
new file mode 100644
index 00000000..c2bd3281
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Site.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW;
+
+use SiteStats;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Site {
+
+ /**
+ * Check whether the wiki is in read-only mode.
+ *
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public static function isReadOnly() {
+
+ // MediaWiki\Services\ServiceDisabledException from line 340 of
+ // ...\ServiceContainer.php: Service disabled: DBLoadBalancer
+ try {
+ $isReadOnly = wfReadOnly();
+ } catch( \MediaWiki\Services\ServiceDisabledException $e ) {
+ $isReadOnly = true;
+ }
+
+ return $isReadOnly;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public static function isBlocked() {
+ return defined( 'MEDIAWIKI_INSTALL' ) && MEDIAWIKI_INSTALL;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function name() {
+ return $GLOBALS['wgSitename'];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function wikiurl() {
+ return $GLOBALS['wgServer'] . str_replace( '$1', '', $GLOBALS['wgArticlePath'] );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function languageCode() {
+ return $GLOBALS['wgLanguageCode'];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public static function isCommandLineMode() {
+
+ // MW 1.27 wgCommandLineMode isn't set correctly
+ if ( ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) ) {
+ return true;
+ }
+
+ return $GLOBALS['wgCommandLineMode'];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public static function isCapitalLinks() {
+ return $GLOBALS['wgCapitalLinks'];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param $affix string
+ *
+ * @return string
+ */
+ public static function id( $affix = '' ) {
+
+ if ( $affix !== '' && $affix{0} !== ':' ) {
+ $affix = ':' . $affix;
+ }
+
+ return wfWikiID() . $affix;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public static function stats() {
+ return [
+ 'pageCount' => SiteStats::pages(),
+ 'contentPageCount' => SiteStats::articles(),
+ 'mediaCount' => SiteStats::images(),
+ 'editCount' => SiteStats::edits(),
+ 'userCount' => SiteStats::users(),
+ 'adminCount' => SiteStats::numberingroup( 'sysop' )
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $typeFilter
+ *
+ * @return array
+ */
+ public static function getJobClasses( $typeFilter = '' ) {
+
+ if ( $typeFilter === 'SMW' ) {
+ $typeFilter = 'smw.';
+ }
+
+ $jobList = $GLOBALS['wgJobClasses'];
+
+ foreach ( $jobList as $type => $class ) {
+
+ if ( $typeFilter === '' ) {
+ continue;
+ }
+
+ if ( strpos( $type, $typeFilter ) === false ) {
+ unset( $jobList[$type] );
+ }
+ }
+
+ return $jobList;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Store.php b/www/wiki/extensions/SemanticMediaWiki/src/Store.php
new file mode 100644
index 00000000..4f1598f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Store.php
@@ -0,0 +1,582 @@
+<?php
+
+namespace SMW;
+
+use InvalidArgumentException;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Psr\Log\LoggerAwareTrait;
+use SMW\Connection\ConnectionManager;
+use SMW\Utils\Timer;
+use SMWDataItem as DataItem;
+use SMWQuery;
+use SMWQueryResult;
+use SMWRequestOptions;
+use SMWSemanticData;
+use SMW\Services\Exception\ServiceNotFoundException;
+use Title;
+
+/**
+ * This group contains all parts of SMW that relate to storing and retrieving
+ * semantic data. SMW components that relate to semantic querying only have
+ * their own group.
+ *
+ * @defgroup SMWStore SMWStore
+ * @ingroup SMW
+ */
+
+/**
+ * The abstract base class for all classes that implement access to some
+ * semantic store. Besides the relevant interface, this class provides default
+ * implementations for some optional methods, which inform the caller that
+ * these methods are not implemented.
+ *
+ * @ingroup SMWStore
+ *
+ * @author Markus Krötzsch
+ */
+abstract class Store implements QueryEngine {
+
+ use MessageReporterAwareTrait;
+ use LoggerAwareTrait;
+
+ /**
+ * Option to define whether creating updates jobs is allowed for a request
+ * or not.
+ */
+ const OPT_CREATE_UPDATE_JOB = 'opt.create.update.job';
+
+ /**
+ * @var ConnectionManager
+ */
+ protected $connectionManager = null;
+
+ /**
+ * @var Options
+ */
+ protected $options = null;
+
+///// Reading methods /////
+
+ /**
+ * @see EntityLookup::getSemanticData
+ *
+ * @param DIWikiPage $subject
+ * @param string[]|bool $filter
+ */
+ public abstract function getSemanticData( DIWikiPage $subject, $filter = false );
+
+ /**
+ * @see EntityLookup::getPropertyValues
+ *
+ * @param $subject mixed SMWDIWikiPage or null
+ * @param $property DIProperty
+ * @param $requestoptions SMWRequestOptions
+ *
+ * @return array of DataItem
+ */
+ public abstract function getPropertyValues( $subject, DIProperty $property, $requestoptions = null );
+
+ /**
+ * @see EntityLookup::getPropertySubjects
+ *
+ * @return DIWikiPage[]
+ */
+ public abstract function getPropertySubjects( DIProperty $property, $value, $requestoptions = null );
+
+ /**
+ * Get an array of all subjects that have some value for the given
+ * property. The result is an array of DIWikiPage objects.
+ *
+ * @return DIWikiPage[]
+ */
+ public abstract function getAllPropertySubjects( DIProperty $property, $requestoptions = null );
+
+ /**
+ * @see EntityLookup::getProperties
+ *
+ * @param DIWikiPage $subject denoting the subject
+ * @param SMWRequestOptions|null $requestOptions optionally defining further options
+ *
+ * @return DataItem
+ */
+ public abstract function getProperties( DIWikiPage $subject, $requestOptions = null );
+
+ /**
+ * @see EntityLookup::getInProperties
+ *
+ * @param DataItem $object
+ * @param RequestOptions|null $requestOptions
+ *
+ * @return DataItem[]|[]
+ */
+ public abstract function getInProperties( DataItem $object, $requestoptions = null );
+
+ /**
+ * Convenience method to find the sortkey of an SMWDIWikiPage. The
+ * result is based on the contents of this store, and may differ from
+ * the MediaWiki database entry about a Title objects sortkey. If no
+ * sortkey is stored, the default sortkey (title string) is returned.
+ *
+ * @param DIWikiPage $dataItem
+ *
+ * @return string sortkey
+ */
+ public function getWikiPageSortKey( DIWikiPage $dataItem ) {
+
+ $dataItems = $this->getPropertyValues( $dataItem, new DIProperty( '_SKEY' ) );
+
+ if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) {
+ return end( $dataItems )->getString();
+ }
+
+ return str_replace( '_', ' ', $dataItem->getDBkey() );
+ }
+
+ /**
+ * Convenience method to find the redirect target of a DIWikiPage
+ * or DIProperty object. Returns a dataitem of the same type that
+ * the input redirects to, or the input itself if there is no redirect.
+ *
+ * @param DataItem $dataItem
+ *
+ * @return DataItem
+ */
+ public function getRedirectTarget( DataItem $dataItem ) {
+
+ $type = $dataItem->getDIType();
+
+ if ( $type !== DataItem::TYPE_WIKIPAGE && $type !== DataItem::TYPE_PROPERTY ) {
+ throw new InvalidArgumentException( 'Store::getRedirectTarget expects a DIProperty or DIWikiPage object.' );
+ }
+
+ if ( $type === DataItem::TYPE_PROPERTY ) {
+
+ if ( !$dataItem->isUserDefined() ) {
+ return $dataItem;
+ }
+
+ $wikipage = $dataItem->getDiWikiPage();
+ } elseif ( $type === DataItem::TYPE_WIKIPAGE ) {
+ $wikipage = $dataItem;
+ }
+
+ $dataItems = $this->getPropertyValues( $wikipage, new DIProperty( '_REDI' ) );
+
+ if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) {
+
+ $redirectDataItem = end( $dataItems );
+
+ if ( $type == DataItem::TYPE_PROPERTY && $redirectDataItem instanceof DIWikiPage ) {
+ $dataItem = DIProperty::newFromUserLabel( $redirectDataItem->getDBkey() );
+ } else {
+ $dataItem = $redirectDataItem;
+ }
+ }
+
+ return $dataItem;
+ }
+
+///// Writing methods /////
+
+ /**
+ * Delete all semantic properties that the given subject has. This
+ * includes relations, attributes, and special properties. This does
+ * not delete the respective text from the wiki, but only clears the
+ * stored data.
+ *
+ * @param Title $subject
+ */
+ public abstract function deleteSubject( Title $subject );
+
+ /**
+ * Update the semantic data stored for some individual. The data is
+ * given as a SemanticData object, which contains all semantic data
+ * for one particular subject.
+ *
+ * @param SemanticData $data
+ */
+ protected abstract function doDataUpdate( SemanticData $data );
+
+ /**
+ * Update the semantic data stored for some individual. The data is
+ * given as a SemanticData object, which contains all semantic data
+ * for one particular subject.
+ *
+ * @param SemanticData $semanticData
+ */
+ public function updateData( SemanticData $semanticData ) {
+
+ if ( !$this->getOption( 'smwgSemanticsEnabled' ) ) {
+ return;
+ }
+
+ Timer::start( __METHOD__ );
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $subject = $semanticData->getSubject();
+ $hash = $subject->getHash();
+
+ /**
+ * @since 1.6
+ */
+ \Hooks::run( 'SMWStore::updateDataBefore', [ $this, $semanticData ] );
+
+ $this->doDataUpdate( $semanticData );
+
+ /**
+ * @since 1.6
+ */
+ \Hooks::run( 'SMWStore::updateDataAfter', [ $this, $semanticData ] );
+
+ $context = [
+ 'method' => __METHOD__,
+ 'role' => 'production',
+ 'origin' => $hash,
+ 'procTime' => Timer::getElapsedTime( __METHOD__, 5 ),
+ ];
+
+ $this->logger->info( '[Store] Update completed: {origin} (procTime in sec: {procTime})', $context );
+
+ if ( !$this->getOption( 'smwgAutoRefreshSubject' ) || $semanticData->getOption( Enum::OPT_SUSPEND_PURGE ) ) {
+ return $this->logger->info( '[Store] Skipping html, parser cache purge', [ 'role' => 'user' ] );
+ }
+
+ $pageUpdater = $applicationFactory->newPageUpdater();
+
+ $pageUpdater->addPage( $subject->getTitle() );
+ $pageUpdater->waitOnTransactionIdle();
+ $pageUpdater->markAsPending();
+ $pageUpdater->setOrigin( __METHOD__ );
+
+ $pageUpdater->doPurgeParserCache();
+ $pageUpdater->doPurgeHtmlCache();
+ $pageUpdater->pushUpdate();
+ }
+
+ /**
+ * Clear all semantic data specified for some page.
+ *
+ * @param DIWikiPage $di
+ */
+ public function clearData( DIWikiPage $di ) {
+ $this->updateData( new SMWSemanticData( $di ) );
+ }
+
+ /**
+ * Update the store to reflect a renaming of some article. Normally
+ * this happens when moving pages in the wiki, and in this case there
+ * is also a new redirect page generated at the old position. The title
+ * objects given are only used to specify the name of the title before
+ * and after the move -- do not use their IDs for anything! The ID of
+ * the moved page is given in $pageid, and the ID of the newly created
+ * redirect, if any, is given by $redirid. If no new page was created,
+ * $redirid will be 0.
+ */
+ public abstract function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 );
+
+///// Query answering /////
+
+ /**
+ * @note Change the signature in 3.* to avoid for subclasses to manage the
+ * hooks; keep the current signature to adhere semver for the 2.* branch
+ *
+ * Execute the provided query and return the result as an
+ * SMWQueryResult if the query was a usual instance retrieval query. In
+ * the case that the query asked for a plain string (querymode
+ * MODE_COUNT or MODE_DEBUG) a plain wiki and HTML-compatible string is
+ * returned.
+ *
+ * @param SMWQuery $query
+ *
+ * @return SMWQueryResult
+ */
+ public abstract function getQueryResult( SMWQuery $query );
+
+ /**
+ * @note Change the signature to abstract for the 3.* branch
+ *
+ * @since 2.1
+ *
+ * @param SMWQuery $query
+ *
+ * @return SMWQueryResult
+ */
+ protected function fetchQueryResult( SMWQuery $query ) {
+ }
+
+///// Special page functions /////
+
+ /**
+ * Return all properties that have been used on pages in the wiki. The
+ * result is an array of arrays, each containing a property data item
+ * and a count. The expected order is alphabetical w.r.t. to property
+ * names.
+ *
+ * If there is an error on creating some property object, then a
+ * suitable SMWDIError object might be returned in its place. Even if
+ * there are errors, the function should always return the number of
+ * results requested (otherwise callers might assume that there are no
+ * further results to ask for).
+ *
+ * @param SMWRequestOptions $requestoptions
+ *
+ * @return array of array( DIProperty|SMWDIError, integer )
+ */
+ public abstract function getPropertiesSpecial( $requestoptions = null );
+
+ /**
+ * Return all properties that have been declared in the wiki but that
+ * are not used on any page. Stores might restrict here to those
+ * properties that have been given a type if they have no efficient
+ * means of accessing the set of all pages in the property namespace.
+ *
+ * If there is an error on creating some property object, then a
+ * suitable SMWDIError object might be returned in its place. Even if
+ * there are errors, the function should always return the number of
+ * results requested (otherwise callers might assume that there are no
+ * further results to ask for).
+ *
+ * @param SMWRequestOptions $requestoptions
+ *
+ * @return array of DIProperty|SMWDIError
+ */
+ public abstract function getUnusedPropertiesSpecial( $requestoptions = null );
+
+ /**
+ * Return all properties that are used on some page but that do not
+ * have any page describing them. Stores that have no efficient way of
+ * accessing the set of all existing pages can extend this list to all
+ * properties that are used but do not have a type assigned to them.
+ *
+ * @param SMWRequestOptions $requestoptions
+ *
+ * @return array of array( DIProperty, int )
+ */
+ public abstract function getWantedPropertiesSpecial( $requestoptions = null );
+
+ /**
+ * Return statistical information as an associative array with the
+ * following keys:
+ * - 'PROPUSES': Number of property instances (value assignments) in the datatbase
+ * - 'USEDPROPS': Number of properties that are used with at least one value
+ * - 'DECLPROPS': Number of properties that have been declared (i.e. assigned a type)
+ * - 'OWNPAGE': Number of properties with their own page
+ * - 'QUERY': Number of inline queries
+ * - 'QUERYSIZE': Represents collective query size
+ * - 'CONCEPTS': Number of declared concepts
+ * - 'SUBOBJECTS': Number of declared subobjects
+ *
+ * @return array
+ */
+ public abstract function getStatistics();
+
+ /**
+ * Store administration
+ */
+
+ /**
+ * @private
+ *
+ * Returns store specific services. Services are registered with the store
+ * implementation and may provide different services that are only available
+ * for a particular store.
+ *
+ * @since 3.0
+ *
+ * @param string $service
+ *
+ * @return mixed
+ * @throws ServiceNotFoundException
+ */
+ public function service( $service, ...$args ) {
+ throw new ServiceNotFoundException( $service );
+ }
+
+ /**
+ * Setup all storage structures properly for using the store. This
+ * function performs tasks like creation of database tables. It is
+ * called upon installation as well as on upgrade: hence it must be
+ * able to upgrade existing storage structures if needed. It should
+ * return "true" if successful and return a meaningful string error
+ * message otherwise.
+ *
+ * The parameter $verbose determines whether the procedure is allowed
+ * to report on its progress. This is doen by just using print and
+ * possibly ob_flush/flush. This is also relevant for preventing
+ * timeouts during long operations. All output must be valid in an HTML
+ * context, but should preferably be plain text, possibly with some
+ * linebreaks and weak markup.
+ *
+ * @param boolean $verbose
+ *
+ * @return boolean Success indicator
+ */
+ public abstract function setup( $verbose = true );
+
+ /**
+ * Drop (delete) all storage structures created by setup(). This will
+ * delete all semantic data and possibly leave the wiki uninitialised.
+ *
+ * @param boolean $verbose
+ */
+ public abstract function drop( $verbose = true );
+
+ /**
+ * Refresh some objects in the store, addressed by numerical ids. The
+ * meaning of the ids is private to the store, and does not need to
+ * reflect the use of IDs elsewhere (e.g. page ids). The store is to
+ * refresh $count objects starting from the given $index. Typically,
+ * updates are achieved by generating update jobs. After the operation,
+ * $index is set to the next index that should be used for continuing
+ * refreshing, or to -1 for signaling that no objects of higher index
+ * require refresh. The method returns a decimal number between 0 and 1
+ * to indicate the overall progress of the refreshing (e.g. 0.7 if 70%
+ * of all objects were refreshed).
+ *
+ * The optional parameter $namespaces may contain an array of namespace
+ * constants. If given, only objects from those namespaces will be
+ * refreshed. The default value FALSE disables this feature.
+ *
+ * The optional parameter $usejobs indicates whether updates should be
+ * processed later using MediaWiki jobs, instead of doing all updates
+ * immediately. The default is TRUE.
+ *
+ * @param $index integer
+ * @param $count integer
+ * @param $namespaces mixed array or false
+ * @param $usejobs boolean
+ *
+ * @return float between 0 and 1 to indicate the overall progress of the refreshing
+ */
+ public abstract function refreshData( &$index, $count, $namespaces = false, $usejobs = true );
+
+ /**
+ * Setup the store.
+ *
+ * @since 1.8
+ *
+ * @param bool $verbose
+ * @param Options|null $options
+ *
+ * @return boolean Success indicator
+ */
+ public static function setupStore( $verbose = true, $options = null ) {
+
+ // See notes in ExtensionSchemaUpdates
+ if ( is_bool( $verbose ) ) {
+ $verbose = $verbose;
+ }
+
+ if ( isset( $options['verbose'] ) ) {
+ $verbose = $options['verbose'];
+ }
+
+ if ( isset( $options['options'] ) ) {
+ $options = $options['options'];
+ }
+
+ $store = StoreFactory::getStore();
+
+ if ( $options instanceof Options ) {
+ foreach ( $options->getOptions() as $key => $value ) {
+ $store->getOptions()->set( $key, $value );
+ }
+ }
+
+ return $store->setup( $verbose );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return Options
+ */
+ public function getOptions() {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function setOption( $key, $value ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->set( $key, $value );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ * @param mixed $default
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = null ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function clear() {
+
+ if ( $this->connectionManager !== null ) {
+ $this->connectionManager->releaseConnections();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|null $type
+ *
+ * @return array
+ */
+ public function getInfo( $type = null ) {
+ return [];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param ConnectionManager $connectionManager
+ */
+ public function setConnectionManager( ConnectionManager $connectionManager ) {
+ $this->connectionManager = $connectionManager;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ *
+ * @return mixed
+ */
+ public function getConnection( $type ) {
+
+ if ( $this->connectionManager === null ) {
+ $this->connectionManager = ApplicationFactory::getInstance()->getConnectionManager();
+ }
+
+ return $this->connectionManager->getConnection( $type );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/StoreAware.php b/www/wiki/extensions/SemanticMediaWiki/src/StoreAware.php
new file mode 100644
index 00000000..fb7cba14
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/StoreAware.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Describes an instance that is aware of a Store object.
+ *
+ * @license GNU GPL v2
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface StoreAware {
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ */
+ public function setStore( Store $store );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/StoreFactory.php b/www/wiki/extensions/SemanticMediaWiki/src/StoreFactory.php
new file mode 100644
index 00000000..e6251185
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/StoreFactory.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW;
+
+use RuntimeException;
+use SMW\Exception\StoreNotFoundException;
+use Onoi\MessageReporter\NullMessageReporter;
+use Psr\Log\NullLogger;
+
+/**
+ * Factory method that returns an instance of the default store, or an
+ * alternative store instance.
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class StoreFactory {
+
+ /**
+ * @var array
+ */
+ private static $instance = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param string|null $class
+ *
+ * @return Store
+ * @throws RuntimeException
+ * @throws StoreNotFoundException
+ */
+ public static function getStore( $class = null ) {
+
+ if ( $class === null ) {
+ $class = $GLOBALS['smwgDefaultStore'];
+ }
+
+ if ( !isset( self::$instance[$class] ) ) {
+ self::$instance[$class] = self::newFromClass( $class );
+ }
+
+ return self::$instance[$class];
+ }
+
+ /**
+ * @since 1.9
+ */
+ public static function clear() {
+ self::$instance = [];
+ }
+
+ private static function newFromClass( $class ) {
+
+ if ( !class_exists( $class ) ) {
+ throw new RuntimeException( "{$class} was not found!" );
+ }
+
+ $instance = new $class;
+
+ if ( !( $instance instanceof Store ) ) {
+ throw new StoreNotFoundException( "{$class} cannot be used as a store instance!" );
+ }
+
+ $instance->setMessageReporter( new NullMessageReporter() );
+ $instance->setLogger( new NullLogger() );
+
+ return $instance;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/StringCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/StringCondition.php
new file mode 100644
index 00000000..950caf3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/StringCondition.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Small data container class for describing filtering conditions on the string
+ * label of some entity. States that a given string should either be prefix,
+ * postfix, or some arbitrary part of labels.
+ *
+ * @license GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Markus Krötzsch
+ */
+class StringCondition {
+
+ /**
+ * String matches prefix
+ */
+ const COND_PRE = 0;
+ const STRCOND_PRE = self::COND_PRE; // Deprecated
+
+ /**
+ * String matches postfix
+ */
+ const COND_POST = 1;
+ const STRCOND_POST = self::COND_POST; // Deprecated
+
+ /**
+ * String matches to some inner part
+ */
+ const COND_MID = 2;
+ const STRCOND_MID = self::COND_MID; // Deprecated
+
+ /**
+ * String matches as equal
+ */
+ const COND_EQ = 3;
+
+ /**
+ * String to match.
+ *
+ * @var string
+ */
+ public $string;
+
+ /**
+ * Whether to match the strings as conjunction or
+ * disjunction.
+ *
+ * @var boolean
+ */
+ public $isOr;
+
+ /**
+ * @var boolean
+ */
+ public $isNot;
+
+ /**
+ * @var integer
+ */
+ public $condition;
+
+ /**
+ * @since 1.0
+ *
+ * @param srting $string
+ * @param integer $condition
+ * @param boolean $isOr
+ */
+ public function __construct( $string, $condition, $isOr = false, $isNot = false ) {
+ $this->string = $string;
+ $this->condition = $condition;
+ $this->isOr = $isOr;
+ $this->isNot = $isNot;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return string
+ */
+ public function getHash() {
+ return $this->string . '#' . $this->condition . '#' . $this->isOr . '#' . $this->isNot;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/TypesRegistry.php b/www/wiki/extensions/SemanticMediaWiki/src/TypesRegistry.php
new file mode 100644
index 00000000..90afb490
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/TypesRegistry.php
@@ -0,0 +1,310 @@
+<?php
+
+namespace SMW;
+
+use SMW\DataValues\AllowsListValue;
+use SMW\DataValues\AllowsPatternValue;
+use SMW\DataValues\AllowsValue;
+use SMW\DataValues\BooleanValue;
+use SMW\DataValues\ErrorMsgTextValue;
+use SMW\DataValues\ExternalFormatterUriValue;
+use SMW\DataValues\ExternalIdentifierValue;
+use SMW\DataValues\ImportValue;
+use SMW\DataValues\KeywordValue;
+use SMW\DataValues\LanguageCodeValue;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DataValues\PropertyChainValue;
+use SMW\DataValues\PropertyValue;
+use SMW\DataValues\ReferenceValue;
+use SMW\DataValues\StringValue;
+use SMW\DataValues\TelephoneUriValue;
+use SMW\DataValues\TemperatureValue;
+use SMW\DataValues\TypesValue;
+use SMW\DataValues\UniquenessConstraintValue;
+use SMWDataItem as DataItem;
+use SMWNumberValue as NumberValue;
+use SMWQuantityValue as QuantityValue;
+use SMWTimeValue as TimeValue;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TypesRegistry {
+
+ /**
+ * @note All IDs must start with an underscore, two underscores indicate a
+ * truly internal (non user-interacted type). All others should also get a
+ * translation in the language files, or they won't be available for users.
+ *
+ * @since 2.5
+ *
+ * @return array
+ */
+ public static function getDataTypeList() {
+ return [
+
+ // ID => [ Class, DI type, isSubDataType, isBrowsable ]
+
+ // Special import vocabulary type
+ ImportValue::TYPE_ID => [ ImportValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Property chain
+ PropertyChainValue::TYPE_ID => [ PropertyChainValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Property type (possibly predefined, not always based on a page)
+ PropertyValue::TYPE_ID => [ PropertyValue::class, DataItem::TYPE_PROPERTY, false, false ],
+ // Text type
+ StringValue::TYPE_ID => [ StringValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Code type
+ StringValue::TYPE_COD_ID => [ StringValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Legacy string ID `_str`
+ StringValue::TYPE_LEGACY_ID => [ StringValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Email type
+ '_ema' => [ 'SMWURIValue', DataItem::TYPE_URI, false, false ],
+ // URL/URI type
+ '_uri' => [ 'SMWURIValue', DataItem::TYPE_URI, false, false ],
+ // Annotation URI type
+ '_anu' => [ 'SMWURIValue', DataItem::TYPE_URI, false, false ],
+ // Phone number (URI) type
+ '_tel' => [ TelephoneUriValue::class, DataItem::TYPE_URI, false, false ],
+ // Page type
+ '_wpg' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Property page type TODO: make available to user space
+ '_wpp' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Category page type TODO: make available to user space
+ '_wpc' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Form page type for Semantic Forms
+ '_wpf' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Rule page
+ '_wps' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Number type
+ NumberValue::TYPE_ID => [ NumberValue::class, DataItem::TYPE_NUMBER, false, false ],
+ // Temperature type
+ TemperatureValue::TYPE_ID => [ TemperatureValue::class, DataItem::TYPE_NUMBER, false, false ],
+ // Time type
+ TimeValue::TYPE_ID => [ TimeValue::class, DataItem::TYPE_TIME, false, false ],
+ // Boolean type
+ '_boo' => [ BooleanValue::class, DataItem::TYPE_BOOLEAN, false, false ],
+ // Value list type (replacing former nary properties)
+ '_rec' => [ 'SMWRecordValue', DataItem::TYPE_WIKIPAGE, true, false ],
+ MonolingualTextValue::TYPE_ID => [ MonolingualTextValue::class, DataItem::TYPE_WIKIPAGE, true, false ],
+ ReferenceValue::TYPE_ID => [ ReferenceValue::class, DataItem::TYPE_WIKIPAGE, true, false ],
+ // Geographical coordinates
+ '_geo' => [ null, DataItem::TYPE_GEO, false, false ],
+ // Geographical polygon
+ '_gpo' => [ null, DataItem::TYPE_BLOB, false, false ],
+ // External identifier
+ ExternalIdentifierValue::TYPE_ID => [ ExternalIdentifierValue::class, DataItem::TYPE_BLOB, false, false ],
+ // KeywordValue
+ KeywordValue::TYPE_ID => [ KeywordValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Type for numbers with units of measurement
+ QuantityValue::TYPE_ID => [ QuantityValue::class, DataItem::TYPE_NUMBER, false, false ],
+ // Special types are not avaialble directly for users (and have no local language name):
+ // Special type page type
+ TypesValue::TYPE_ID => [ TypesValue::class, DataItem::TYPE_URI, false, false ],
+ // Special type list for decalring _rec properties
+ '__pls' => [ 'SMWPropertyListValue', DataItem::TYPE_BLOB, false, false ],
+ // Special concept page type
+ '__con' => [ 'SMWConceptValue', DataItem::TYPE_CONCEPT, false, false ],
+ // Special string type
+ '__sps' => [ StringValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Special uri type
+ '__spu' => [ 'SMWURIValue', DataItem::TYPE_URI, false, false ],
+ // Special subobject type
+ '__sob' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, true, true ],
+ // Special subproperty type
+ '__sup' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Special subcategory type
+ '__suc' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Special Form page type for Semantic Forms
+ '__spf' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Special instance of type
+ '__sin' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Special redirect type
+ '__red' => [ 'SMWWikiPageValue', DataItem::TYPE_WIKIPAGE, false, true ],
+ // Special error type
+ '__err' => [ 'SMWErrorValue', DataItem::TYPE_ERROR, false, false ],
+ // Special error type
+ '__errt' => [ ErrorMsgTextValue::class, DataItem::TYPE_BLOB, false, false ],
+ // Sort key of a page
+ '__key' => [ StringValue::class, DataItem::TYPE_BLOB, false, false ],
+ LanguageCodeValue::TYPE_ID => [ LanguageCodeValue::class, DataItem::TYPE_BLOB, false, false ],
+ AllowsValue::TYPE_ID => [ AllowsValue::class, DataItem::TYPE_BLOB, false, false ],
+ AllowsListValue::TYPE_ID => [ AllowsListValue::class, DataItem::TYPE_BLOB, false, false ],
+ AllowsPatternValue::TYPE_ID => [ AllowsPatternValue::class, DataItem::TYPE_BLOB, false, false ],
+ '__pvuc' => [ UniquenessConstraintValue::class, DataItem::TYPE_BOOLEAN, false, false ],
+ '__pefu' => [ ExternalFormatterUriValue::class, DataItem::TYPE_URI, false, false ]
+ ];
+ }
+
+ /**
+ * @note All ids must start with underscores. The translation for each ID,
+ * if any, is defined in the language files. Properties without translation
+ * cannot be entered by or displayed to users, whatever their "show" value
+ * below.
+ *
+ * @since 3.0
+ *
+ * @param boolean $useCategoryHierarchy
+ *
+ * @return array
+ */
+ public static function getPropertyList( $useCategoryHierarchy = true ) {
+ return [
+
+ // ID => [ valueType, isVisible, isAnnotable, isDeclarative ]
+
+ '_TYPE' => [ '__typ', true, true, true ], // "has type"
+ '_URI' => [ '__spu', true, true, false ], // "equivalent URI"
+ '_INST' => [ '__sin', false, true, false ], // instance of a category
+ '_UNIT' => [ '__sps', true, true, true ], // "displays unit"
+ '_IMPO' => [ '__imp', true, true, true ], // "imported from"
+ '_CONV' => [ '__sps', true, true, true ], // "corresponds to"
+ '_SERV' => [ '__sps', true, true, true ], // "provides service"
+ '_PVAL' => [ '__pval', true, true, true ], // "allows value"
+ '_REDI' => [ '__red', true, true, false ], // redirects to some page
+ '_SUBP' => [ '__sup', true, true, true ], // "subproperty of"
+ '_SUBC' => [ '__suc', !$useCategoryHierarchy, true, true ], // "subcategory of"
+ '_CONC' => [ '__con', false, true, false ], // associated concept
+ '_MDAT' => [ '_dat', false, false, false ], // "modification date"
+ '_CDAT' => [ '_dat', false, false, false ], // "creation date"
+ '_NEWP' => [ '_boo', false, false, false ], // "is a new page"
+ '_EDIP' => [ '_boo', true, true, false ], // "is edit protected"
+ '_LEDT' => [ '_wpg', false, false, false ], // "last editor is"
+ '_ERRC' => [ '__sob', false, false, false ], // "has error"
+ '_ERRT' => [ '__errt', false, false, false ], // "has error text"
+ '_ERRP' => [ '_wpp', false, false, false ], // "has improper value for"
+ '_LIST' => [ '__pls', true, true, true ], // "has fields"
+ '_SKEY' => [ '__key', false, true, false ], // sort key of a page
+
+ // FIXME SF related properties to be removed with 3.0
+ '_SF_DF' => [ '__spf', true, true, false ], // Semantic Form's default form property
+ '_SF_AF' => [ '__spf', true, true, false ], // Semantic Form's alternate form property
+
+ '_SOBJ' => [ '__sob', true, false, false ], // "has subobject"
+ '_ASK' => [ '__sob', false, false, false ], // "has query"
+ '_ASKST' => [ '_cod', true, false, false ], // "Query string"
+ '_ASKFO' => [ '_txt', true, false, false ], // "Query format"
+ '_ASKSI' => [ '_num', true, false, false ], // "Query size"
+ '_ASKDE' => [ '_num', true, false, false ], // "Query depth"
+ '_ASKDU' => [ '_num', true, false, false ], // "Query duration"
+ '_ASKSC' => [ '_txt', true, false, false ], // "Query source"
+ '_ASKPA' => [ '_cod', true, false, false ], // "Query parameters"
+ '_ASKCO' => [ '_num', true, false, false ], // "Query scode"
+ '_MEDIA' => [ '_txt', true, false, false ], // "has media type"
+ '_MIME' => [ '_txt', true, false, false ], // "has mime type"
+ '_PREC' => [ '_num', true, true, true ], // "Display precision of"
+ '_LCODE' => [ '__lcode', true, true, false ], // "Language code"
+ '_TEXT' => [ '_txt', true, true, false ], // "Text"
+ '_PDESC' => [ '_mlt_rec', true, true, true ], // "Property description"
+ '_PVAP' => [ '__pvap', true, true, true ], // "Allows pattern"
+ '_PVALI' => [ '__pvali', true, true, true ], // "Allows value list"
+ '_DTITLE' => [ '_txt', false, true, false ], // "Display title of"
+ '_PVUC' => [ '__pvuc', true, true, true ], // Uniqueness constraint
+ '_PEID' => [ '_eid', true, true, false ], // External identifier
+ '_PEFU' => [ '__pefu', true, true, true ], // External formatter uri
+ '_PPLB' => [ '_mlt_rec', true, true, true ], // Preferred property label
+ '_CHGPRO' => [ '_cod', true, false, true ], // "Change propagation"
+ '_PPGR' => [ '_boo', true, true, true ], // "Property group"
+
+ // Schema
+ '_SCHEMA_TYPE' => [ '_txt', true, false, false ], // "Schema type"
+ '_SCHEMA_DEF' => [ '_cod', true, false, false ], // "Schema definition"
+ '_SCHEMA_DESC' => [ '_txt', true, false, false ], // "Schema description"
+ '_SCHEMA_TAG' => [ '_txt', true, false, false ], // "Schema tag"
+ '_SCHEMA_LINK' => [ '_wps', true, false, false ], // "Schema link"
+
+ //
+ '_FORMAT_SCHEMA' => [ '_wps', true, true, false ], // "Formatter schema"
+
+ // File attachment
+ '_FILE_ATTCH' => [ '__sob', false, false, false ], // "File attachment"
+ '_CONT_TYPE' => [ '_txt', true, true, false ], // "Content type"
+ '_CONT_AUTHOR' => [ '_txt', true, true, false ], // "Content author"
+ '_CONT_LEN' => [ '_num', true, true, false ], // "Content length"
+ '_CONT_LANG' => [ '__lcode', true, true, false ], // "Content language"
+ '_CONT_TITLE' => [ '_txt', true, true, false ], // "Content title"
+ '_CONT_DATE' => [ '_dat', true, true, false ], // "Content date",
+ '_CONT_KEYW' => [ '_keyw', true, true, false ], // "Content keyword"
+
+ // Translation
+ '_TRANS' => [ '__sob', false, false, false ], // "Translation"
+ '_TRANS_SOURCE' => [ '_wpg', true, false, false ], // "Translation source"
+ '_TRANS_GROUP' => [ '_txt', true, false, false ], // "Translation group"
+ ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getTypesByGroup( $group = '' ) {
+
+ if ( $group === 'primitive' ) {
+ return [ '_txt' => true , '_boo' => true , '_num' => true, '_dat' => true ];
+ }
+
+ if ( $group === 'compound' ) {
+ return [ '_ema' => true, '_tel' => true, '_tem' => true ];
+ }
+
+ return [];
+ }
+
+ /**
+ * Use pre-defined ids for Very Important Properties, avoiding frequent
+ * ID lookups for those.
+ *
+ * @note These constants also occur in the store. Changing them will
+ * require to run setup.php again.
+ *
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getFixedPropertyIdList() {
+ return [
+ '_TYPE' => 1,
+ '_URI' => 2,
+ '_INST' => 4,
+ '_UNIT' => 7,
+ '_IMPO' => 8,
+ '_PPLB' => 9,
+ '_PDESC' => 10,
+ '_PREC' => 11,
+ '_CONV' => 12,
+ '_SERV' => 13,
+ '_PVAL' => 14,
+ '_REDI' => 15,
+ '_DTITLE' => 16,
+ '_SUBP' => 17,
+ '_SUBC' => 18,
+ '_CONC' => 19,
+ '_ERRP' => 22,
+ // '_1' => 23, // properties for encoding (short) lists
+ // '_2' => 24,
+ // '_3' => 25,
+ // '_4' => 26,
+ // '_5' => 27,
+ // '_SOBJ' => 27
+ '_LIST' => 28,
+ '_MDAT' => 29,
+ '_CDAT' => 30,
+ '_NEWP' => 31,
+ '_LEDT' => 32,
+ // properties related to query management
+ '_ASK' => 33,
+ '_ASKST' => 34,
+ '_ASKFO' => 35,
+ '_ASKSI' => 36,
+ '_ASKDE' => 37,
+ '_ASKPA' => 38,
+ '_ASKSC' => 39,
+ '_LCODE' => 40,
+ '_TEXT' => 41,
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/BufferedStatsdCollector.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/BufferedStatsdCollector.php
new file mode 100644
index 00000000..10d4b508
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/BufferedStatsdCollector.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace SMW\Utils;
+
+use Onoi\BlobStore\BlobStore;
+use SMW\ApplicationFactory;
+
+/**
+ * Collect statistics in a provisional schema-free storage that depends on the
+ * availability of the cache back-end.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class BufferedStatsdCollector {
+
+ /**
+ * Update this version number when the serialization format
+ * changes.
+ */
+ const VERSION = '0.2';
+
+ /**
+ * Available operations
+ */
+ const STATS_INIT = 'init';
+ const STATS_INCR = 'incr';
+ const STATS_SET = 'set';
+ const STATS_MEDIAN = 'median';
+
+ /**
+ * Namespace occupied by the BlobStore
+ */
+ const CACHE_NAMESPACE = 'smw:stats:store';
+
+ /**
+ * @var BlobStore
+ */
+ private $blobStore;
+
+ /**
+ * @var string|integer
+ */
+ private $statsdId;
+
+ /**
+ * @var boolean
+ */
+ private $shouldRecord = true;
+
+ /**
+ * @var array
+ */
+ private $stats = [];
+
+ /**
+ * Identifies an update fingerprint to compare invoked deferred updates
+ * against each other and filter those with the same print to avoid recording
+ * duplicate stats.
+ *
+ * @var string
+ */
+ private $fingerprint = null;
+
+ /**
+ * @var array
+ */
+ private $operations = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param BlobStore $blobStore
+ * @param string $statsdId
+ */
+ public function __construct( BlobStore $blobStore, $statsdId ) {
+ $this->blobStore = $blobStore;
+ $this->statsdId = $statsdId;
+ $this->fingerprint = $statsdId . uniqid();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $shouldRecord
+ */
+ public function shouldRecord( $shouldRecord ) {
+ $this->shouldRecord = (bool)$shouldRecord;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getStats() {
+
+ $container = $this->blobStore->read(
+ md5( $this->statsdId . self::VERSION )
+ );
+
+ return StatsFormatter::getStatsFromFlatKey( $container->getData(), '.' );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $key
+ */
+ public function incr( $key ) {
+
+ if ( !isset( $this->stats[$key] ) ) {
+ $this->stats[$key] = 0;
+ }
+
+ $this->stats[$key]++;
+ $this->operations[$key] = self::STATS_INCR;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $key
+ * @param string|integer $default
+ */
+ public function init( $key, $default ) {
+ $this->stats[$key] = $default;
+ $this->operations[$key] = self::STATS_INIT;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $key
+ * @param string|integer $value
+ */
+ public function set( $key, $value ) {
+ $this->stats[$key] = $value;
+ $this->operations[$key] = self::STATS_SET;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string|array $key
+ * @param integer $value
+ */
+ public function calcMedian( $key, $value ) {
+
+ if ( !isset( $this->stats[$key] ) ) {
+ $this->stats[$key] = $value;
+ } else {
+ $this->stats[$key] = ( $this->stats[$key] + $value ) / 2;
+ }
+
+ $this->operations[$key] = self::STATS_MEDIAN;
+ }
+
+ /**
+ * @since 2.5
+ */
+ public function saveStats() {
+
+ if ( $this->stats === [] ) {
+ return;
+ }
+
+ $container = $this->blobStore->read(
+ md5( $this->statsdId . self::VERSION )
+ );
+
+ foreach ( $this->stats as $key => $value ) {
+
+ $old = $container->has( $key ) ? $container->get( $key ) : 0;
+
+ if ( $this->operations[$key] === self::STATS_INIT && $old != 0 ) {
+ $value = $old;
+ }
+
+ if ( $this->operations[$key] === self::STATS_INCR ) {
+ $value = $old + $value;
+ }
+
+ // Use as-is
+ // $this->operations[$key] === self::STATS_SET
+
+ if ( $this->operations[$key] === self::STATS_MEDIAN ) {
+ $value = $old > 0 ? ( $old + $value ) / 2 : $value;
+ }
+
+ $container->set( $key, $value );
+ }
+
+ $this->blobStore->save(
+ $container
+ );
+
+ $this->stats = [];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $asPending
+ */
+ public function recordStats( $asPending = false ) {
+
+ if ( $this->shouldRecord === false ) {
+ return $this->stats = [];
+ }
+
+ // #2046
+ // __destruct as event trigger has shown to be unreliable in a MediaWiki
+ // environment therefore rely on the deferred update and any caller
+ // that invokes the recordStats method
+
+ $deferredTransactionalUpdate = ApplicationFactory::getInstance()->newDeferredTransactionalCallableUpdate(
+ function() { $this->saveStats();
+ }
+ );
+
+ $deferredTransactionalUpdate->setOrigin( __METHOD__ );
+ $deferredTransactionalUpdate->waitOnTransactionIdle();
+
+ $deferredTransactionalUpdate->setFingerprint(
+ __METHOD__ . $this->fingerprint
+ );
+
+ $deferredTransactionalUpdate->markAsPending( $asPending );
+ $deferredTransactionalUpdate->pushUpdate();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharArmor.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharArmor.php
new file mode 100644
index 00000000..9a1e20c0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharArmor.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CharArmor {
+
+ /**
+ * Remove invisible control characters and unused code points (using a
+ * negated character class to avoid removing spaces)
+ *
+ * @see http://www.regular-expressions.info/unicode.html#category
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function removeControlChars( $text ) {
+ return preg_replace('/[^\PC\s]/u', '', $text );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return text
+ */
+ public static function removeSpecialChars( $text ) {
+ return str_replace(
+ [ '&shy;', '&lrm;', " ", " ", " " ],
+ [ '', '', ' ', ' ', ' ' ],
+ $text
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharExaminer.php
new file mode 100644
index 00000000..e7d07c48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CharExaminer.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CharExaminer {
+
+ const CYRILLIC = 'CYRILLIC';
+ const LATIN = 'LATIN';
+ const HIRAGANA_KATAKANA = 'HIRAGANA_KATAKANA';
+ const HANGUL = 'HANGUL';
+ const CJK_UNIFIED = 'CJK_UNIFIED';
+ const HAN = 'HAN';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return boolean
+ */
+ public static function isCJK( $text ) {
+
+ if ( self::contains( self::HAN, $text ) ) {
+ return true;
+ }
+
+ if ( self::contains( self::HIRAGANA_KATAKANA, $text ) ) {
+ return true;
+ }
+
+ if ( self::contains( self::HANGUL, $text ) ) {
+ return true;
+ }
+
+ if ( self::contains( self::CJK_UNIFIED, $text ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see http://jrgraphix.net/research/unicode_blocks.php
+ * @since 0.1
+ *
+ * @param string $type
+ * @param string $text
+ *
+ * @return boolean
+ */
+ public static function contains( $type, $text ) {
+
+ if ( $type === self::CYRILLIC ) {
+ return preg_match('/\p{Cyrillic}/u', $text ) > 0;
+ }
+
+ if ( $type === self::LATIN ) {
+ return preg_match('/\p{Latin}/u', $text ) > 0;
+ }
+
+ if ( $type === self::HAN ) {
+ return preg_match('/\p{Han}/u', $text ) > 0;
+ }
+
+ if ( $type === self::HIRAGANA_KATAKANA ) {
+ return preg_match('/[\x{3040}-\x{309F}]/u', $text ) > 0 || preg_match('/[\x{30A0}-\x{30FF}]/u', $text ) > 0; // isHiragana || isKatakana
+ }
+
+ if ( $type === self::HANGUL ) {
+ return preg_match('/[\x{3130}-\x{318F}]/u', $text ) > 0 || preg_match('/[\x{AC00}-\x{D7AF}]/u', $text ) > 0;
+ }
+
+ // @see https://en.wikipedia.org/wiki/CJK_Unified_Ideographs
+ // Chinese, Japanese and Korean (CJK) scripts share common characters
+ // known as CJK characters
+
+ if ( $type === self::CJK_UNIFIED ) {
+ return preg_match('/[\x{4e00}-\x{9fa5}]/u', $text ) > 0;
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/CircularReferenceGuard.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CircularReferenceGuard.php
new file mode 100644
index 00000000..26cb35ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/CircularReferenceGuard.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CircularReferenceGuard {
+
+ /**
+ * @var array
+ */
+ private static $circularRefGuard = [];
+
+ /**
+ * @var string
+ */
+ private $namespace = '';
+
+ /**
+ * @var integer
+ */
+ private $maxRecursionDepth = 1;
+
+ /**
+ * @since 2.2
+ *
+ * @param string $namespace
+ */
+ public function __construct( $namespace = '' ) {
+ $this->namespace = $namespace;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param integer $maxRecursionDepth
+ */
+ public function setMaxRecursionDepth( $maxRecursionDepth ) {
+ $this->maxRecursionDepth = (int)$maxRecursionDepth;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $hash
+ */
+ public function mark( $hash ) {
+
+ if ( !isset( self::$circularRefGuard[$this->namespace][$hash] ) ) {
+ self::$circularRefGuard[$this->namespace][$hash] = 0;
+ }
+
+ self::$circularRefGuard[$this->namespace][$hash]++;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $hash
+ */
+ public function unmark( $hash ) {
+
+ if ( isset( self::$circularRefGuard[$this->namespace][$hash] ) && self::$circularRefGuard[$this->namespace][$hash] > 0 ) {
+ return self::$circularRefGuard[$this->namespace][$hash]--;
+ }
+
+ unset( self::$circularRefGuard[$this->namespace][$hash] );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $hash
+ *
+ * @return boolean
+ */
+ public function isCircular( $hash ) {
+ return $this->get( $hash ) > $this->maxRecursionDepth;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $hash
+ *
+ * @return integer
+ */
+ public function get( $hash ) {
+
+ if ( isset( self::$circularRefGuard[$this->namespace][$hash] ) ) {
+ return self::$circularRefGuard[$this->namespace][$hash];
+ }
+
+ return 0;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $namespace
+ */
+ public function reset( $namespace ) {
+ self::$circularRefGuard[$namespace] = [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Csv.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Csv.php
new file mode 100644
index 00000000..3a3c0309
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Csv.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Csv {
+
+ const DEFAULT_SEP = ',';
+
+ /**
+ * @var boolean
+ */
+ private $show = false;
+
+ /**
+ * @var boolean
+ */
+ private $bom = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $show
+ * @param boolean $bom
+ */
+ public function __construct( $show = false, $bom = false ) {
+ $this->show = $show;
+ $this->bom = $bom;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $header
+ * @param array $rows
+ * @param string $sep
+ *
+ * @return string
+ */
+ public function toString( array $header, array $rows, $sep = self::DEFAULT_SEP ) {
+
+ $handle = fopen( 'php://temp', 'r+' );
+
+ // fputcsv(): delimiter must be a single character
+ $sep = $sep !== '' ? $sep{0} : self::DEFAULT_SEP;
+
+ // https://en.wikipedia.org/wiki/Comma-separated_values#Standardization
+ // http://php.net/manual/en/function.fputcsv.php
+ if ( $this->bom ) {
+ fputs( $handle, ( chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) ) );
+ }
+
+ // https://en.wikipedia.org/wiki/Comma-separated_values#Application_support
+ if ( $this->show ) {
+ fputs( $handle, "sep=" . $sep . "\n" );
+ }
+
+ if ( $header !== [] ) {
+ fputcsv( $handle, $header, $sep );
+ }
+
+ foreach ( $rows as $row ) {
+ fputcsv( $handle, $row, $sep );
+ }
+
+ rewind( $handle );
+
+ return stream_get_contents( $handle );
+ }
+
+ /**
+ * Merge row and column values where the subject (first column) uses the same
+ * identifier.
+ *
+ * @since 3.0
+ *
+ * @param array $rows
+ * @param string $sep
+ *
+ * @return array
+ */
+ public function merge( $rows, $sep = ',' ) {
+
+ $map = [];
+ $order = [];
+
+ foreach ( $rows as $key => $row ) {
+
+ // First column is used to build the hash index to find rows with
+ // the same hash
+ $hash = md5( $row[0] );
+
+ // Retain the order
+ if ( !isset( $order[$hash] ) ) {
+ $order[$hash] = $key;
+ }
+
+ if ( !isset( $map[$hash] ) ) {
+ $map[$hash] = $row;
+ } else {
+ $concat = [];
+
+ foreach ( $map[$hash] as $k => $v ) {
+ // Index 0 represents the first column, same hash, only
+ // concatenate the rest of the columns
+ if ( $k != 0 ) {
+ $v = $v . ( isset( $row[$k] ) ? "$sep" . $row[$k] : '' );
+ // Filter duplicate values
+ $v = array_flip( explode( $sep, $v ) );
+ // Make it a simple list
+ $v = implode( $sep, array_keys( $v ) );
+ }
+
+ $concat[$k] = $v;
+ }
+
+ $map[$hash] = $concat;
+ }
+ }
+
+ $order = array_flip( $order );
+
+ foreach ( $order as $key => $hash ) {
+ $order[$key] = $map[$hash];
+ }
+
+ return $order;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/ErrorCodeFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/ErrorCodeFormatter.php
new file mode 100644
index 00000000..354902ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/ErrorCodeFormatter.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * Convenience method to retrieved stringified error codes.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorCodeFormatter {
+
+ /**
+ * @var array
+ */
+ private static $constants = [];
+
+ /**
+ * @var array
+ */
+ private static $jsonErrors = [];
+
+ /**
+ * @see http://php.net/manual/en/function.json-decode.php
+ * @since 2.5
+ *
+ * @param integer $errorCode
+ *
+ * @return string
+ */
+ public static function getStringFromJsonErrorCode( $errorCode ) {
+
+ if ( self::$constants === [] ) {
+ self::$constants = get_defined_constants( true );
+ }
+
+ if ( isset( self::$constants["json"] ) && self::$jsonErrors === [] ) {
+ foreach ( self::$constants["json"] as $name => $value ) {
+ if ( !strncmp( $name, "JSON_ERROR_", 11 ) ) {
+ self::$jsonErrors[$value] = $name;
+ }
+ }
+ }
+
+ return isset( self::$jsonErrors[$errorCode] ) ? self::$jsonErrors[$errorCode] : 'UNKNOWN';
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $errorCode
+ *
+ * @return string
+ */
+ public static function getMessageFromJsonErrorCode( $errorCode ) {
+
+ $errorMessages = [
+ JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch, malformed JSON',
+ JSON_ERROR_CTRL_CHAR => 'Unexpected control character found, possibly incorrectly encoded',
+ JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
+ JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded'
+ ];
+
+ if ( !isset( $errorMessages[$errorCode] ) ) {
+ return self::getStringFromJsonErrorCode( $errorCode );
+ }
+
+ return sprintf(
+ "Expected a JSON compatible format but failed with '%s'",
+ $errorMessages[$errorCode]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/File.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/File.php
new file mode 100644
index 00000000..f17154dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/File.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Utils;
+
+use RuntimeException;
+use SMW\Exception\FileNotWritableException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class File {
+
+ /**
+ * @since 3.1
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ public static function dir( $file ) {
+ return str_replace( [ '\\', '//', '/' ], DIRECTORY_SEPARATOR, $file );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ * @param string $content
+ * @param integer $flags
+ */
+ public function write( $file, $contents, $flags = 0 ) {
+
+ $file = self::dir( $file );
+
+ if ( !is_writable( dirname( $file ) ) ) {
+ throw new FileNotWritableException( "$file" );
+ }
+
+ file_put_contents( $file, $contents, $flags );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ *
+ * @return boolean
+ */
+ public function exists( $file ) {
+ return file_exists( $file );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ * @param integer|null $checkSum
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function read( $file, $checkSum = null ) {
+
+ if ( !is_readable( $file ) ) {
+ throw new RuntimeException( "$file is not readable." );
+ }
+
+ if ( $checkSum !== null && $this->getCheckSum( $file ) !== $checkSum ) {
+ throw new RuntimeException( "Processing of $file failed with a checkSum error." );
+ }
+
+ return file_get_contents( $file );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ */
+ public function delete( $file ) {
+ @unlink( $file );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ *
+ * @return integer
+ */
+ public function getCheckSum( $file ) {
+ return md5_file( $file );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HmacSerializer.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HmacSerializer.php
new file mode 100644
index 00000000..80d560b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HmacSerializer.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * Serialize/encode a data element with a hmac hash to verify that the output are
+ * in fact the same as the input data, minimizing an attack vector on injecting
+ * malicious content when retrieving the data from en external systems (like
+ * a cache).
+ *
+ * The shared secret key to generate the HMAC is by default MediaWiki's
+ * $wgSecretKey.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HmacSerializer {
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return string|boolean
+ */
+ public static function encode( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ $data = json_encode( $data );
+ $hash = hash_hmac( $algo, $data, $key );
+
+ if ( $hash !== false ) {
+ return json_encode( [ 'hmac' => $hash, 'data' => $data ] );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return string|boolean
+ */
+ public static function decode( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ if ( !is_string( $data ) ) {
+ return false;
+ }
+
+ $hash = '';
+ $data = json_decode( $data, true );
+
+ // Timing attack safe string comparison
+ if ( isset( $data['hmac'] ) && hash_equals( hash_hmac( $algo, $data['data'], $key ), $data['hmac'] ) ) {
+ return json_decode( $data['data'], true );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return string|boolean
+ */
+ public static function serialize( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ $data = serialize( $data );
+ $hash = hash_hmac( $algo, $data, $key );
+
+ if ( $hash !== false ) {
+ return "$hash|$data";
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return mixed|boolean
+ */
+ public static function unserialize( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ if ( !is_string( $data ) ) {
+ return false;
+ }
+
+ $hash = '';
+
+ if ( strpos( $data, '|' ) !== false ) {
+ list( $hash, $data ) = explode( '|', $data, 2 );
+ }
+
+ // Timing attack safe string comparison
+ if ( hash_equals( hash_hmac( $algo, $data, $key ), $hash ) ) {
+ return unserialize( $data );
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param mixed $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return string|boolean
+ */
+ public static function compress( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ $key = $key . 'compress';
+
+ return gzcompress( self::serialize( $data, $key, $algo ), 9 );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $data
+ * @param string $key
+ * @param string $algo = 'md5'
+ *
+ * @return mixed|boolean
+ */
+ public static function uncompress( $data, $key = null, $algo = 'md5' ) {
+
+ if ( $key === null ) {
+ $key = $GLOBALS['wgSecretKey'];
+ }
+
+ $key = $key . 'compress';
+
+ return self::unserialize( @gzuncompress( $data ), $key, $algo );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlColumns.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlColumns.php
new file mode 100644
index 00000000..d490db71
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlColumns.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace SMW\Utils;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlColumns {
+
+ /**
+ * Indexed content
+ */
+ const INDX_CONTENT = 'indexed.list';
+
+ /**
+ * Indexed content
+ */
+ const INDEXED_LIST = 'indexed.list';
+
+ /**
+ * List content
+ */
+ const LIST_CONTENT = 'list.content';
+
+ /**
+ * List content
+ */
+ const PLAIN_LIST = 'plain.list';
+
+ /**
+ * @var integer
+ */
+ private $columns = 1;
+
+ /**
+ * @var array
+ */
+ private $contents = [];
+
+ /**
+ * @var array
+ */
+ private $itemAttributes = [];
+
+ /**
+ * @var integer
+ */
+ private $numRows = 0;
+
+ /**
+ * @var integer
+ */
+ private $count = 0;
+
+ /**
+ * @var integer
+ */
+ private $rowsPerColumn = 0;
+
+ /**
+ * @var integer
+ */
+ private $columnWidth = 0;
+
+ /**
+ * @var string
+ */
+ private $listType = 'ul';
+
+ /**
+ * @var string
+ */
+ private $olType = '';
+
+ /**
+ * @var string
+ */
+ private $continueAbbrev = '';
+
+ /**
+ * @var string
+ */
+ private $columnListClass = 'smw-columnlist-container';
+
+ /**
+ * @var string
+ */
+ private $columnClass = 'smw-column';
+
+ /**
+ * @var boolean
+ */
+ private $isRTL = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param string $columnListClass
+ */
+ public function setColumnListClass( $columnListClass ) {
+ $this->columnListClass = htmlspecialchars( $columnListClass );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $columnListClass
+ */
+ public function setColumnClass( $columnClass ) {
+ $this->columnClass = htmlspecialchars( $columnClass );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $isRTL
+ */
+ public function isRTL( $isRTL ) {
+ $this->isRTL = (bool)$isRTL;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $columns
+ */
+ public function setColumns( $columns ) {
+ $this->columns = $columns;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $listType
+ * @param string $olType
+ */
+ public function setListType( $listType, $olType = '' ) {
+
+ if ( in_array( $listType, [ 'ul', 'ol' ] ) ) {
+ $this->listType = $listType;
+ }
+
+ if ( $this->listType === 'ol' && in_array( $olType, [ '1', 'a', 'A', 'i', 'I' ] ) ) {
+ $this->olType = $olType;
+ }
+ }
+
+ /**
+ * Allows to define attributes for an item such as:
+ *
+ * [md5( $itemContent )] = [
+ * 'id' => 'Foo'
+ * ]
+ *
+ * @since 3.0
+ *
+ * @param array $itemAttributes
+ */
+ public function setItemAttributes( array $itemAttributes ) {
+ $this->itemAttributes = $itemAttributes;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $continueAbbrev
+ */
+ public function setContinueAbbrev( $continueAbbrev ) {
+ $this->continueAbbrev = $continueAbbrev;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string[] $cnts
+ * @param string $type
+ */
+ public function addContents( array $cnts, $type = self::LIST_CONTENT ) {
+ $this->setContents( $cnts, $type );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string[] $cnts
+ * @param string $type
+ */
+ public function setContents( array $cnts, $type = self::LIST_CONTENT ) {
+
+ if ( $type === self::LIST_CONTENT ) {
+ $contents[''] = [];
+
+ foreach ( $cnts as $value ) {
+ $contents[''][] = $value;
+ }
+
+ } elseif ( $type === self::INDX_CONTENT ) {
+ $contents = $cnts;
+ } else {
+ throw new InvalidArgumentException( 'Missing a recognized type!');
+ }
+
+ $this->contents = $contents;
+ $this->count = count( $this->contents, COUNT_RECURSIVE ) - count( $this->contents );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ $result = '';
+ $usedColumnCloser = false;
+ $this->numRows = 0;
+
+ // Class to determine whether we want responsive columns width
+ if ( strpos( $this->columnClass, 'responsive' ) !== false ) {
+ $this->columnWidth = 100;
+ $this->columns = 1;
+ } else {
+ $this->columnWidth = floor( 100 / $this->columns );
+ }
+
+ $this->rowsPerColumn = ceil( $this->count / $this->columns );
+
+ foreach ( $this->contents as $key => $items ) {
+
+ if ( $items === [] ) {
+ continue;
+ }
+
+ $result .= $this->makeList(
+ $key,
+ $items,
+ $usedColumnCloser
+ );
+ }
+
+ if ( !$usedColumnCloser ) {
+ $result .= "</{$this->listType}></div> <!-- end column -->";
+ }
+
+ return $this->element(
+ 'div',
+ [
+ 'class' => $this->columnListClass,
+ 'dir' => $this->isRTL ? 'rtl' : 'ltr'
+ ],
+ $result . "\n" . '<br style="clear: both;"/>'
+ );
+ }
+
+ private function makeList( $key, $items, &$usedColumnCloser ) {
+
+ $result = '';
+ $previousKey = "";
+ $dir = $this->isRTL ? 'rtl' : 'ltr';
+
+ foreach ( $items as $item ) {
+
+ $attributes = [];
+
+ if ( $this->itemAttributes !== [] ) {
+ $hash = md5( $item );
+
+ if ( isset( $this->itemAttributes[$hash] ) ) {
+ $attributes = $this->itemAttributes[$hash];
+ }
+ }
+
+ if ( $this->numRows % $this->rowsPerColumn == 0 ) {
+ $result .= "<div class=\"$this->columnClass\" style=\"width:$this->columnWidth%;\" dir=\"$dir\">";
+
+ $numRowsInColumn = $this->numRows + 1;
+ $type = $this->olType !== '' ? " type={$this->olType}" : '';
+
+ if ( $key == $previousKey ) {
+ if ( $key !== '' ) {
+ $result .= $this->element(
+ 'div',
+ [
+ 'class' => 'smw-column-header'
+ ],
+ "$key {$this->continueAbbrev}"
+ );
+ }
+
+ $result .= "<{$this->listType}$type start={$numRowsInColumn}>";
+ }
+ }
+
+ // if we're at a new first letter, end
+ // the last list and start a new one
+ if ( $key != $previousKey ) {
+ $result .= $this->numRows % $this->rowsPerColumn > 0 ? "</{$this->listType}>" : '';
+ $result .= ( $key !== '' ? $this->element( 'div', [ 'class' => 'smw-column-header' ], $key ) : '' ) . "<{$this->listType}>";
+ }
+
+ $previousKey = $key;
+ $result .= $this->element( 'li', $attributes, $item );
+ $usedColumnCloser = false;
+
+ if ( ( $this->numRows + 1 ) % $this->rowsPerColumn == 0 && ( $this->numRows + 1 ) < $this->count ) {
+ $result .= "</{$this->listType}></div> <!-- end column -->";
+ $usedColumnCloser = true;
+ }
+
+ $this->numRows++;
+ }
+
+ return $result;
+ }
+
+ private function element( $type, $attributes, $content ) {
+
+ $attr = '';
+ $attributes = (array)$attributes;
+
+ if ( $attributes !== [] ) {
+ foreach ( $attributes as $key => $value ) {
+ $attr .= ' ' . $key . '="' . $value . '"';
+ }
+ }
+
+ return "<$type$attr>$content</$type>";
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlDivTable.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlDivTable.php
new file mode 100644
index 00000000..d968919b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlDivTable.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace SMW\Utils;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlDivTable {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function table( $html = '', array $attributes = [] ) {
+ return self::open( $attributes ) . $html . self::close();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function open( array $attributes = [] ) {
+ return Html::openElement(
+ 'div',
+ self::mergeAttributes( 'smw-table', $attributes ),
+ ''
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function header( $html = '', array $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ self::mergeAttributes( 'smw-table-header', $attributes ),
+ $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function body( $html = '', array $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ self::mergeAttributes( 'smw-table-body', $attributes ),
+ $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function footer( $html = '', array $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ self::mergeAttributes( 'smw-table-footer', $attributes ),
+ $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function row( $html = '', array $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ self::mergeAttributes( 'smw-table-row', $attributes ),
+ $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function cell( $html = '', array $attributes = [] ) {
+ return Html::rawElement(
+ 'div',
+ self::mergeAttributes( 'smw-table-cell', $attributes ),
+ $html
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ */
+ public static function close() {
+ return Html::closeElement(
+ 'div'
+ );
+ }
+
+ private static function mergeAttributes( $class, $attr ) {
+
+ $attributes = [];
+
+ // A bit of attribute order
+ if ( isset( $attr['id'] ) ) {
+ $attributes['id'] = $attr['id'];
+ }
+
+ if ( isset( $attr['class'] ) ) {
+ $attributes['class'] = $class . ' ' . $attr['class'];
+ } else {
+ $attributes['class'] = $class;
+ }
+
+ return $attributes += $attr;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlModal.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlModal.php
new file mode 100644
index 00000000..b8b99997
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlModal.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\Utils;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlModal {
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getModules() {
+ return [ 'ext.smw.modal' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getModuleStyles() {
+ return [ 'ext.smw.modal.styles' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function link( $name, array $attributes = [] ) {
+
+ $attributes = self::mergeAttributes(
+ 'smw-modal-link is-disabled',
+ $attributes
+ );
+
+ return Html::rawElement(
+ 'span',
+ $attributes,
+ Html::rawElement(
+ 'a',
+ [
+ 'href' => '#help',
+ 'rel' => 'nofollow'
+ ],
+ $name
+ )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function modal( $title = '', $html = '', array $attributes = [] ) {
+
+ $attributes = self::mergeAttributes(
+ 'smw-modal',
+ $attributes
+ );
+
+ $title = Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-modal-title'
+ ],
+ $title
+ );
+
+ $html = Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-modal-content'
+ ],
+ Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-modal-header'
+ ],
+ Html::rawElement(
+ 'span',
+ [
+ 'class' => 'smw-modal-close'
+ ],
+ '&#215;'
+ ) . $title
+ ). Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-modal-body'
+ ],
+ $html
+ ) . Html::rawElement(
+ 'div',
+ [
+ 'class' => 'smw-modal-footer'
+ ],
+ ''
+ )
+ );
+
+ return Html::rawElement(
+ 'div',
+ $attributes,
+ $html
+ );
+ }
+
+ private static function mergeAttributes( $class, $attr ) {
+
+ $attributes = [];
+
+ // A bit of attribute order
+ if ( isset( $attr['id'] ) ) {
+ $attributes['id'] = $attr['id'];
+ }
+
+ if ( isset( $attr['class'] ) ) {
+ $attributes['class'] = $class . ' ' . $attr['class'];
+ } else {
+ $attributes['class'] = $class;
+ }
+
+ return $attributes += $attr;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTable.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTable.php
new file mode 100644
index 00000000..9a42386b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTable.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace SMW\Utils;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlTable {
+
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @var array
+ */
+ private $cells = [];
+
+ /**
+ * @var array
+ */
+ private $rows = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param string $content
+ * @param array $attributes
+ */
+ public function header( $content = '', $attributes = [] ) {
+ if ( $content !== '' ) {
+ $this->headers[] = [ 'content' => $content, 'attributes' => $attributes ];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $content
+ * @param array $attributes
+ */
+ public function cell( $content = '', $attributes = [] ) {
+ if ( $content !== '' ) {
+ $this->cells[] = Html::rawElement( 'td', $attributes, $content );
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return TableBuilder
+ */
+ public function row( $attributes = [] ) {
+ if ( $this->cells !== [] ) {
+ $this->rows[] = [ 'cells' => $this->cells, 'attributes' => $attributes ];
+ $this->cells = [];
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function table( $attributes = [], $transpose = false, $htmlContext = false ) {
+
+ $table = $this->buildTable( $transpose, $htmlContext );
+
+ if ( $transpose ) {
+ $attributes['data-transpose'] = true;
+ }
+
+ $this->headers = [];
+ $this->rows = [];
+ $this->cells = [];
+
+ if ( $table !== '' ) {
+ return Html::rawElement( 'table', $attributes, $table );
+ }
+
+ return '';
+ }
+
+ private function buildTable( $transpose, $htmlContext ) {
+
+ if ( $transpose ) {
+ return $this->transpose( $htmlContext );
+ }
+
+ $headers = [];
+ $rows = [];
+
+ foreach( $this->headers as $i => $header ) {
+ $headers[] = Html::rawElement( 'th', $header['attributes'], $header['content'] );
+ }
+
+ foreach( $this->rows as $row ) {
+ $rows[] = $this->createRow( implode( '', $row['cells'] ), $row['attributes'], count( $rows ) );
+ }
+
+ return $this->concatenateHeaders( $headers, $htmlContext ) . $this->concatenateRows( $rows, $htmlContext );
+ }
+
+ private function transpose( $htmlContext ) {
+
+ $rows = [];
+
+ foreach( $this->headers as $hIndex => $header ) {
+ $cells = [];
+ $headerItem = Html::rawElement( 'th', $header['attributes'], $header['content'] );
+
+ foreach( $this->rows as $rIndex => $row ) {
+ $cells[] = $this->getTransposedCell( $hIndex, $row );
+ }
+
+ // Collect new rows
+ $rows[] = $this->createRow( $headerItem . implode( '', $cells ), [], count( $rows ) );
+ }
+
+ return $this->concatenateRows( $rows, $htmlContext );
+ }
+
+ private function createRow( $content = '', $attributes = [], $count ) {
+
+ $alternate = $count % 2 == 0 ? 'row-odd' : 'row-even';
+
+ if ( isset( $attributes['class'] ) ) {
+ $attributes['class'] = $attributes['class'] . ' ' . $alternate;
+ } else {
+ $attributes['class'] = $alternate;
+ }
+
+ return Html::rawElement( 'tr', $attributes, $content );
+ }
+
+ private function concatenateHeaders( $headers, $htmlContext ) {
+
+ if ( $htmlContext ) {
+ return Html::rawElement( 'thead', [], implode( '', $headers ) );
+ }
+
+ return implode( '', $headers );
+ }
+
+ private function concatenateRows( $rows, $htmlContext ) {
+
+ if ( $htmlContext ) {
+ return Html::rawElement( 'tbody', [], implode( '', $rows ) );
+ }
+
+ return implode( '', $rows );
+ }
+
+ private function getTransposedCell( $index, $row ) {
+
+ if ( isset( $row['cells'][$index] ) ) {
+ return $row['cells'][$index];
+ }
+
+ $attributes = [];
+
+ if ( isset( $row['attributes']['class'] ) && $row['attributes']['class'] === 'smwfooter' ) {
+ $attributes = [ 'class' => 'footer-cell' ];
+ }
+
+ return Html::rawElement( 'td', $attributes, '' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTabs.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTabs.php
new file mode 100644
index 00000000..8cca3bf8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlTabs.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\Utils;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlTabs {
+
+ /**
+ * @var []
+ */
+ private $tabs = [];
+
+ /**
+ * @var []
+ */
+ private $contents = [];
+
+ /**
+ * @var []
+ */
+ private $hidden = [];
+
+ /**
+ * @var string|null
+ */
+ private $activeTab = null;
+
+ /**
+ * @var string
+ */
+ private $group = 'tabs';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $activeTab
+ */
+ public function setActiveTab( $activeTab ) {
+ $this->activeTab = $activeTab;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $group
+ */
+ public function setGroup( $group ) {
+ $this->group = $group;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function buildHTML( array $attributes = [] ) {
+
+ $tabs = $this->tabs;
+ $contents = $this->contents;
+
+ $this->tabs = [];
+ $this->contents = [];
+
+ $attributes = $this->mergeAttributes( 'smw-tabs', $attributes );
+
+ return Html::rawElement(
+ 'div',
+ $attributes,
+ implode( '', $tabs ) . implode( '', $contents )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $name
+ * @param array $params
+ *
+ * @return string
+ */
+ public function html( $html, array $params = [] ) {
+
+ if ( isset( $params['hide'] ) && $params['hide'] ) {
+ return;
+ }
+
+ $this->tabs[] = $html;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $name
+ * @param array $params
+ *
+ * @return string
+ */
+ public function tab( $id, $name = '', array $params = [] ) {
+
+ if ( isset( $params['hide'] ) && $params['hide'] ) {
+ return $this->hidden[$id] = true;
+ }
+
+ $isChecked = false;
+
+ // No acive tab means, select the first tab being added
+ if ( $this->activeTab === null ) {
+ $this->activeTab = $id;
+ }
+
+ if ( $id === $this->activeTab ) {
+ $isChecked = true;
+ }
+
+ $this->tabs[] = Html::rawElement(
+ 'input',
+ [
+ 'id' => "tab-$id",
+ 'class' => 'nav-tab',
+ 'type' => 'radio',
+ 'name' => $this->group
+ ] + ( $isChecked ? [ 'checked' => 'checked' ] : [] )
+ ) . Html::rawElement(
+ 'label',
+ [
+ 'id' => "tab-label-$id",
+ 'for' => "tab-$id"
+ ] + $this->mergeAttributes( 'nav-label', $params ),
+ $name
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $content
+ */
+ public function content( $id, $content ) {
+
+ // Tab hidden?
+ if ( isset( $this->hidden[$id] ) ) {
+ return;
+ }
+
+ $this->contents[] = Html::rawElement(
+ 'section',
+ [
+ 'id' => "tab-content-$id"
+ ],
+ $content
+ );
+ }
+
+ private function mergeAttributes( $class, $attr ) {
+
+ $attributes = [];
+
+ // A bit of attribute order
+ if ( isset( $attr['id'] ) ) {
+ $attributes['id'] = $attr['id'];
+ }
+
+ if ( isset( $attr['class'] ) ) {
+ $attributes['class'] = $class . ' ' . $attr['class'];
+ } else {
+ $attributes['class'] = $class;
+ }
+
+ return $attributes += $attr;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlVTabs.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlVTabs.php
new file mode 100644
index 00000000..25ced9bc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/HtmlVTabs.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\Utils;
+
+use Html;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlVTabs {
+
+ /**
+ * Identifies which link/content to be active
+ */
+ const IS_ACTIVE = 'active';
+
+ /**
+ * Match an active status against a id
+ */
+ const FIND_ACTIVE_LINK = 'find';
+
+ /**
+ * Hide content
+ */
+ const IS_HIDDEN = 'hidden';
+
+ /**
+ * @var string
+ */
+ private static $active = '';
+
+ /**
+ * @var string
+ */
+ private static $direction = 'right';
+
+ /**
+ * @since 3.0
+ */
+ public static function init() {
+ self::$active = '';
+ self::$direction = 'right';
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getModules() {
+ return [ 'ext.smw.vtabs' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public static function getModuleStyles() {
+ return [ 'ext.smw.vtabs.styles' ];
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $direction
+ */
+ public static function setDirection( $direction ) {
+ self::$direction = $direction;
+ }
+
+ /**
+ * Encapsulate generate tab links into a navigation container.
+ *
+ * @since 3.0
+ *
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function nav( $html = '', array $attributes = [] ) {
+
+ $direction = self::$direction === 'right' ? 'nav-right' : 'nav-left';
+
+ $attributes = self::mergeAttributes( "smw-vtab-nav", $attributes );
+ $attributes['class'] .= " $direction";
+
+ return Html::rawElement(
+ 'div',
+ $attributes,
+ $html
+ );
+ }
+
+ /**
+ * Generate an individual tab link.
+ *
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $label
+ * @param string|array $flag
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function navLink( $id, $label = '', $flag = false, array $attributes = [] ) {
+
+ if ( $flag === self::IS_HIDDEN ) {
+ return '';
+ }
+
+ // Match an active status against an id
+ if ( is_array( $flag ) && isset( $flag[self::FIND_ACTIVE_LINK] ) && $flag[self::FIND_ACTIVE_LINK] === $id ) {
+ $flag = self::IS_ACTIVE;
+ }
+
+ $id = 'tab-' . $id;
+ $direction = self::$direction === 'right' ? 'nav-right' : 'nav-left';
+
+ $attributes['data-id'] = $id;
+ $attributes['id'] = 'vtab-item-' . $id;
+
+ $attributes = self::mergeAttributes( "smw-vtab-link", $attributes );
+ $attributes['class'] .= " $direction";
+
+ if ( $flag === self::IS_ACTIVE && self::$active == '' ) {
+ $attributes['class'] .= ' active';
+ self::$active = $id;
+ }
+
+ return Html::rawElement(
+ 'button',
+ $attributes,
+ Html::rawElement( 'a', [ 'href' => '#' . $id ], $label )
+ );
+ }
+
+ /**
+ * Encapsulate the content that relates to a tab link using the ID as identifier
+ * to distinguish content sections.
+ *
+ * @since 3.0
+ *
+ * @param string $id
+ * @param string $html
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public static function content( $id, $html = '', array $attributes = [] ) {
+
+ $id = 'tab-' . $id;
+ $attributes['id'] = $id;
+
+ if ( self::$active !== $id ) {
+ if ( !isset( $attributes['style'] ) ) {
+ $attributes['style'] = 'display:none;';
+ } else {
+ $attributes['style'] .= ' display:none;';
+ }
+ }
+
+ $attributes = self::mergeAttributes( 'smw-vtab-content', $attributes );
+
+ return Html::rawElement(
+ 'div',
+ $attributes,
+ $html
+ );
+ }
+
+ private static function mergeAttributes( $class, $attr ) {
+
+ $attributes = [];
+
+ // A bit of attribute order
+ if ( isset( $attr['id'] ) ) {
+ $attributes['id'] = $attr['id'];
+ }
+
+ if ( isset( $attr['class'] ) ) {
+ $attributes['class'] = $class . ' ' . $attr['class'];
+ } else {
+ $attributes['class'] = $class;
+ }
+
+ return $attributes += $attr;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Image.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Image.php
new file mode 100644
index 00000000..46cb65c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Image.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\Utils;
+
+use SMW\DIWikiPage;
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Image {
+
+ /**
+ * @see http://php.net/manual/en/function.image-type-to-extension.php
+ *
+ * @var []
+ */
+ private static $images_types = [
+ 'gif' => 'image/gif',
+ 'jpg' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'svg' => 'image/svg+xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'swc' => 'application/x-shockwave-flash',
+ 'psd' => 'image/psd',
+ 'bmp' => 'image/bmp',
+ 'jpc' => 'application/octet-stream',
+ 'jp2' => 'image/jp2',
+ 'jpf' => 'application/octet-stream',
+ 'jb2' => 'application/octet-stream',
+ 'xbm' => 'image/xbm',
+ 'tiff' => 'image/tiff',
+ 'aiff' => 'image/iff',
+ 'wbmp' => 'image/vnd.wap.wbmp'
+ ];
+
+ /**
+ * @since 3.0
+ *
+ * @param DIWikiPage $dataItem
+ *
+ * @return boolean
+ */
+ public static function isImage( DIWikiPage $dataItem ) {
+
+ if ( $dataItem->getNamespace() !== NS_FILE || $dataItem->getSubobjectName() !== '' ) {
+ return false;
+ }
+
+ $extension = strtolower(
+ substr( strrchr( $dataItem->getDBKey(), "." ) , 1 )
+ // pathinfo( $dataItem->getDBKey(), PATHINFO_EXTENSION )
+ );
+
+ return in_array( $extension, array_keys( self::$images_types ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/JsonSchemaValidator.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/JsonSchemaValidator.php
new file mode 100644
index 00000000..8a849ef6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/JsonSchemaValidator.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Utils;
+
+use JsonSchema\Exception\ResourceNotFoundException;
+use JsonSchema\Validator as SchemaValidator;
+use JsonSerializable;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class JsonSchemaValidator {
+
+ /**
+ * @var SchemaValidator
+ */
+ private $schemaValidator;
+
+ /**
+ * @var boolen
+ */
+ private $isValid = true;
+
+ /**
+ * @var []
+ */
+ private $errors = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param SchemaValidator|null $schemaValidator
+ */
+ public function __construct( SchemaValidator $schemaValidator = null ) {
+ $this->schemaValidator = $schemaValidator;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param JsonSerializable $data
+ * @param string|null $schemaLink
+ */
+ public function validate( JsonSerializable $data, $schemaLink = null ) {
+
+ if ( $this->schemaValidator === null || $schemaLink === null ) {
+ return;
+ }
+
+ // https://github.com/justinrainbow/json-schema/issues/203
+ $data = json_decode( $data->jsonSerialize() );
+
+ // https://github.com/justinrainbow/json-schema
+ try {
+ $this->schemaValidator->check(
+ $data,
+ (object)[ '$ref' => 'file://' . $schemaLink ]
+ );
+
+ $this->isValid = $this->schemaValidator->isValid();
+ $this->errors = $this->schemaValidator->getErrors();
+ } catch ( ResourceNotFoundException $e ) {
+ $this->isValid = false;
+ $this->errors[] = $e->getMessage();
+ }
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function hasSchemaValidator() {
+ return $this->schemaValidator !== null;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean
+ */
+ public function isValid() {
+ return $this->isValid;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Logger.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Logger.php
new file mode 100644
index 00000000..97ce40af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Logger.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Utils;
+
+use Psr\Log\AbstractLogger;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Logger extends AbstractLogger {
+
+ const ROLE_DEVELOPER = 'developer';
+ const ROLE_USER = 'user';
+ const ROLE_PRODUCTION = 'production';
+
+ /**
+ * @var LoggerInterface
+ */
+ protected $logger;
+
+ /**
+ * @var string
+ */
+ protected $role;
+
+ /**
+ * @since 3.0
+ *
+ * @param LoggerInterface $logger
+ * @param string $role
+ */
+ public function __construct( LoggerInterface $logger, $role = self::ROLE_DEVELOPER ) {
+ $this->logger = $logger;
+ $this->role = $role;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * {@inheritDoc}
+ */
+ public function log( $level, $message, array $context = [] ) {
+
+ $shouldLog = false;
+
+ // Everthings goes for the developer role!
+ if ( $this->role === self::ROLE_DEVELOPER ) {
+ $shouldLog = true;
+ } elseif ( isset( $context['role'] ) && $context['role'] === $this->role ) {
+ $shouldLog = true;
+ } elseif ( isset( $context['role'] ) && $context['role'] === self::ROLE_PRODUCTION && $this->role === self::ROLE_USER ) {
+ $shouldLog = true;
+ }
+
+ if ( !$shouldLog ) {
+ return;
+ }
+
+ // For convenience
+ if ( isset( $context['procTime'] ) ) {
+ $context['procTime'] = round( $context['procTime'], 5 );
+ }
+
+ if ( isset( $context['time'] ) ) {
+ $context['time'] = round( $context['time'], 5 );
+ }
+
+ if ( is_array( $message ) ) {
+ $message = array_shift( $message ) . ': ' . json_encode( $message );
+ }
+
+ foreach ( $context as $key => $value ) {
+ if ( is_array( $value ) ) {
+ $context[$key] = json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+ }
+
+ $this->logger->log( $level, $message, $context );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Lru.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Lru.php
new file mode 100644
index 00000000..2f785bbf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Lru.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Lru {
+
+ /**
+ * @var integer
+ */
+ private $size;
+
+ /**
+ * @var array
+ */
+ private $cache = [];
+
+ /**
+ * @var array
+ */
+ private $count = 0;
+
+ /**
+ * @since 3.0
+ *
+ * @param integer size
+ */
+ public function __construct( $size = 1000 ) {
+ $this->size = $size;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|integer $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+
+ $this->count++;
+
+ if ( isset( $this->cache[$key] ) ) {
+ $this->count--;
+ $value = $this->cache[$key];
+ unset( $this->cache[$key] );
+ } elseif ( $this->count > $this->size ) {
+ $this->count--;
+ reset( $this->cache );
+ unset( $this->cache[ key( $this->cache ) ] );
+ }
+
+ $this->cache[$key] = $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|integer $key
+ *
+ * @return mixed
+ */
+ public function get( $key, $default = null ) {
+
+ if ( !isset( $this->cache[$key] ) ) {
+ return $default;
+ }
+
+ $value = $this->cache[$key];
+ unset( $this->cache[$key] );
+ $this->cache[$key] = $value;
+
+ return $value;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string|integer $key
+ */
+ public function delete( $key ) {
+
+ if ( !isset( $this->cache[$key] ) ) {
+ return $default;
+ }
+
+ $this->count--;
+ unset( $this->cache[$key] );
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function toArray() {
+ return $this->cache;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Normalizer.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Normalizer.php
new file mode 100644
index 00000000..bb8a5fe8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Normalizer.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Normalizer {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public static function toLowercase( $text ) {
+ return mb_strtolower( $text, mb_detect_encoding( $text ) );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $text
+ * @param integer|null $length
+ *
+ * @return string
+ */
+ public static function reduceLengthTo( $text, $length = null ) {
+
+ if ( $length === null || mb_strlen( $text ) <= $length ) {
+ return $text;
+ }
+
+ $encoding = mb_detect_encoding( $text );
+ $lastWholeWordPosition = $length;
+
+ if ( strpos( $text, ' ' ) !== false ) {
+ $lastWholeWordPosition = strrpos( mb_substr( $text, 0, $length, $encoding ), ' ' ); // last whole word
+ }
+
+ if ( $lastWholeWordPosition > 0 ) {
+ $length = $lastWholeWordPosition;
+ }
+
+ return mb_substr( $text, 0, $length, $encoding );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/StatsFormatter.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/StatsFormatter.php
new file mode 100644
index 00000000..021cd443
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/StatsFormatter.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class StatsFormatter {
+
+ /**
+ * Stats as plain string
+ */
+ const FORMAT_PLAIN = 'plain';
+
+ /**
+ * Stats as JSON output
+ */
+ const FORMAT_JSON = 'json';
+
+ /**
+ * Stats as HTML list output
+ */
+ const FORMAT_HTML = 'html';
+
+ /**
+ * @since 2.5
+ *
+ * @param array $stats
+ * @param string|null $format
+ *
+ * @return string|array
+ */
+ public static function format( array $stats, $format = null ) {
+
+ $output = '';
+
+ if ( $format === self::FORMAT_PLAIN ) {
+ foreach ( $stats as $key => $value ) {
+ $output .= '- ' . $key . "\n";
+
+ if ( !is_array( $value ) ) {
+ continue;
+ }
+
+ foreach ( $value as $k => $v ) {
+ $output .= ' - ' . $k . ': ' . $v . "\n";
+ }
+ }
+ }
+
+ if ( $format === self::FORMAT_HTML ) {
+ $output .= '<ul>';
+ foreach ( $stats as $key => $value ) {
+ $output .= '<li>' . $key . '<ul>';
+
+ if ( !is_array( $value ) ) {
+ continue;
+ }
+
+ foreach ( $value as $k => $v ) {
+ $output .= '<li>' . $k . ': ' . $v . "</li>";
+ }
+ $output .= '</ul></li>';
+ }
+ $output .= '</ul>';
+ }
+
+ if ( $format === self::FORMAT_JSON ) {
+ $output .= json_encode( $stats, JSON_PRETTY_PRINT );
+ }
+
+ if ( $format === null ) {
+ $output = $stats;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $stats
+ * @param string $separator
+ *
+ * @return array
+ */
+ public static function getStatsFromFlatKey( array $stats, $separator = '.' ) {
+
+ $data = $stats;
+ $stats = [];
+
+ foreach ( $data as $key => $value ) {
+ if ( strpos( $key, $separator ) !== false ) {
+ $stats = array_merge_recursive( $stats, self::stringToArray( $separator, $key, $value ) );
+ } else {
+ $stats[$key] = $value;
+ }
+ }
+
+ return $stats;
+ }
+
+ // http://stackoverflow.com/questions/10123604/multstatsdIdimensional-array-from-string
+ private static function stringToArray( $separator, $path, $value ) {
+
+ $pos = strpos( $path, $separator );
+
+ if ( $pos === false ) {
+ return [ $path => $value ];
+ }
+
+ $key = substr( $path, 0, $pos );
+ $path = substr( $path, $pos + 1 );
+
+ $result = [
+ $key => self::stringToArray( $separator, $path, $value )
+ ];
+
+ return $result;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/TempFile.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/TempFile.php
new file mode 100644
index 00000000..518f624d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/TempFile.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW\Utils;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TempFile extends File {
+
+ /**
+ * @since 3.0
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function generate() {
+
+ $args = func_get_args();
+ $key = array_shift( $args );
+
+ if ( $args === [] ) {
+ $key = '';
+ }
+
+ return $this->get(
+ $key . substr( base_convert( md5( json_encode( $args ) ), 16, 32 ), 0, 12 )
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ *
+ * @return string
+ * @throws RuntimeException
+ */
+ public function get( $file ) {
+
+ $tmpDir = [];
+ $path = '';
+
+ if ( isset( $GLOBALS['wgTmpDirectory'] ) ) {
+ $tmpDir[] = $GLOBALS['wgTmpDirectory'];
+ }
+
+ $tmpDir[] = sys_get_temp_dir();
+ $tmpDir[] = ini_get( 'upload_tmp_dir' );
+
+ foreach ( $tmpDir as $tmp ) {
+ if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) {
+ $path = $tmp;
+ break;
+ }
+ }
+
+ if ( $path !== '' ) {
+ return str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $path . '/' . $file );
+ }
+
+ throw new RuntimeException( 'No writable temporary directory could be found.' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Timer.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Timer.php
new file mode 100644
index 00000000..c9871e30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Timer.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Timer {
+
+ /**
+ * @var float|integer
+ */
+ private static $start = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param integer $outputType
+ * @param integer $ts
+ *
+ * @return string|bool
+ */
+ public static function getTimestamp( $outputType = TS_UNIX, $ts = 0 ) {
+ return wfTimestamp( $outputType, $ts );
+ }
+
+ /**
+ * @since 2.5
+ */
+ public static function start( $name ) {
+ self::$start[$name] = microtime( true );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $name
+ * @param integer|null $round
+ *
+ * @return float|integer
+ */
+ public static function getElapsedTime( $name, $round = null ) {
+
+ if ( !isset( self::$start[$name] ) ) {
+ return 0;
+ }
+
+ $time = microtime( true ) - self::$start[$name];
+
+ if ( $round === null ) {
+ return $time;
+ }
+
+ return round( $time, $round );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ * @param integer|null $round
+ *
+ * @return string
+ */
+ public static function getElapsedTimeAsLoggableMessage( $name, $round = null ) {
+ return $name . ' (procTime in sec: '. self::getElapsedTime( $name, $round ) . ')';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php
new file mode 100644
index 00000000..0db427a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Utils/Tokenizer.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Utils;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Tokenizer {
+
+ /**
+ * @since 2.5
+ *
+ * @param string $text
+ *
+ * @return array
+ */
+ public static function tokenize( $text ) {
+
+ if ( !class_exists( '\IntlRuleBasedBreakIterator' ) ) {
+ return explode( ' ', $text );
+ }
+
+ // As for CJK, this returns better results as trying to split tokens
+ // by a single character
+ $intlRuleBasedBreakIterator = \IntlRuleBasedBreakIterator::createWordInstance( 'en' );
+ $intlRuleBasedBreakIterator->setText( $text );
+
+ $prev = 0;
+ $tokens = [];
+
+ foreach ( $intlRuleBasedBreakIterator as $token ) {
+
+ if ( $token == 0 ) {
+ continue;
+ }
+
+ $res = substr( $text, $prev, $token - $prev );
+
+ if ( $res !== '' && $res !== ' ' ) {
+ $tokens[] = $res;
+ }
+
+ $prev = $token;
+ }
+
+ return $tokens;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/README.md b/www/wiki/extensions/SemanticMediaWiki/tests/README.md
new file mode 100644
index 00000000..8ffee500
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/README.md
@@ -0,0 +1,126 @@
+Tests are commonly divided into a manual (without using any tool or automated
+script) and an automated scripted test approach.
+
+# Manual testing
+
+If you want to run some manual tests (either as scripted or exploratory test procedure) then you just have to:
+
+1. Download a related branch using `composer require "mediawiki/semantic-media-wiki:dev-foo` (where `foo` refers to the branch name) or in case you want to test the current master, use `@dev` or `dev-master` as version together with the `minimum-stability: dev` flag so that the branch/master can be fetched without any stability limitations.
+2. Run `composer dump-autoload` to ensure that all registered classes are correctly initialized before starting any test procedure.
+
+# Automated testing (PHPUnit)
+
+For the automated approach, Semantic MediaWiki relies on [PHPUnit][phpunit] as scripted testing methodology. Scripted tests are used to verify that an expected behaviour occurs for codified requirements on the given conditions.
+
+- Unit test refers to a script that verifies results for a unit, module, or class against an expected outcome in an isolated environment
+- Integration test (or functional test) normally combines multiple components into a single process and verifies the results in a semi-production like environment (including DB access, sample data etc.)
+- System test (and its individual modules) is treated as "black-box" to observe behaviour as a whole rather than its units
+
+## Running tests
+
+1. Verify that PHUnit is installed and in case it is not, use `composer require phpunit/phpunit:~4.8 --update-with-dependencies` to add the package
+2. Verify that your MediaWiki installation comes with its test files and folders (e.g. `/myMediawikiFolder/tests` ) in order for Semantic MediaWiki to have access to registered MW-core classes. If the `tests` folder is missing then you may follow the [release source](https://github.com/wikimedia/mediawiki/releases) to download the missing files.
+3. Run `composer phpunit` from the Semantic MediaWiki base directory (e.g. `/extensions/SemanticMediaWiki`) using a standard command line tool which should output something like:
+
+<pre>
+$ composer phpunit
+
+Semantic MediaWiki: 2.5.0-alpha (SMWSQLStore3, sqlite)
+MediaWiki: 1.28.0-alpha (Extension vendor autoloader)
+Site language: en
+
+Execution time: 2015-01-01 01:00
+Xdebug: Disabled (or not installed)
+
+PHPUnit 4.8.27 by Sebastian Bergmann and contributors.
+
+Runtime: PHP 5.6.8
+Configuration: /home/travis/build/SemanticMediaWiki/mw/extensions/SemanticMediaWiki/phpunit.xml.dist
+
+............................................................. 61 / 4069 ( 1%)
+............................................................. 122 / 4069 ( 2%)
+</pre>
+
+Information about PHPUnit in connection with MediaWiki can be found at [smw.org][smw] and [mediawiki.org][mw-phpunit-testing].
+
+## Writing tests
+
+Writing meaningful tests isn't difficult but requires some diligence on how to setup a test and its environment. One simple rule is to avoid the use of hidden expectations or inheritance as remedy for the "less code is good code" aesthetics. Allow the code to be readable and if possible follow the [arrange, act, assert][aaa] pattern.
+
+For a short introduction on "How to write a test for Semantic MediaWiki", have a look at [this](https://www.youtube.com/watch?v=v6JRfk5ZmsI) video.
+
+### Test cases
+
+The use of `MediaWikiTestCase` is discouraged (as its binds tests and the test
+environment to MediaWiki) and it is best to rely on `PHPUnit_Framework_TestCase`
+and where a MW database connection is required, use the `MwDBaseUnitTestCase`
+instead.
+
+* `QueryPrinterTestCase` base class for all query and result printers
+* `SpecialPageTestCase` derives from `SemanticMediaWikiTestCase`
+
+## Integration tests
+
+Integration tests are vital to confirm the behaviour of a component from an
+integrative perspective that occurs through an interplay with its surroundings.
+
+Those tests don't replace unit tests, they complement them to verify that
+an expected outcome does actually occur in combination with MediaWiki and
+other services.
+
+Integration tests can help reduce the recurrence of regressions or bugs, given
+that a developers follows a simple process:
+
+- Make a conjecture or hypothesis about the cause of the bug or regression
+- Find a minimal test case (using wiki text at this point should make it much
+easier to replicate a deviated behaviour)
+- Write a `JSON` test and have it __fail__
+- Apply a fix
+- Run the test again and then run all other integration tests to ensure nothing
+else was altered by accidentally introducing another regression not directly
+related to the one that has been fixed
+
+`SMW\Tests\Integration\` hosts most of the tests that target the validation of
+reciprocity with MediaWiki and/or other services such as:
+
+- Triple-stores (necessary for the `SPARQLStore`)
+- Extensions (`SESP`, `SBL` etc.)
+
+Some details about the integration test environment can be found [here](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/tests/travis/README.md).
+
+### JSONScript integration tests
+
+One best practice approach in Semantic MediaWiki is to write integration tests as
+pseudo `JSONScript` to allow non-developers to review and understand the setup and
+requirements of its test scenarios.
+
+The `JSON` format was introduced as abstraction layer to lower the barrier of
+understanding of what is being tested by using the wikitext markup to help design
+test cases quicker without the need to learn how `PHPUnit` or internal `MediaWiki`
+objects work.
+
+A detailed description of the `JSONScript` together with a list of available test
+files can be found [here](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/README.md).
+
+## Benchmark tests
+
+For details, please have a look at the [benchmark guide](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Benchmark/README.md) document.
+
+# JavaScript (QUnit)
+
+Running qunit tests in connection with MediaWiki requires to execute
+[Special:JavaScriptTest][mw-qunit-testing]. QUnit tests are currently not
+executed on Travis (see [#136][issue-136]).
+
+# Miscellaneous
+* [Writing testable code](https://semantic-mediawiki.org/wiki/Help:Writing_testable_code)
+* [Code coverage in a nutshell](https://semantic-mediawiki.org/wiki/Help:Code_coverage_in_a_nutshell)
+* [Test Doubles](http://www.martinfowler.com/bliki/TestDouble.html) (mocks, stubs etc.) and [how to write them](http://phpunit.de/manual/4.1/en/test-doubles.html)
+
+[phpunit]: http://phpunit.de/manual/4.1/en/index.html
+[smw]: https://www.semantic-mediawiki.org/wiki/PHPUnit_tests
+[mw-phpunit-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing
+[mw-qunit-testing]: https://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing
+[issue-136]: https://github.com/SemanticMediaWiki/SemanticMediaWiki/pull/136
+[phpunit-fixtures]: http://phpunit.de/manual/current/en/fixtures.html
+[aaa]: http://c2.com/cgi/wiki?ArrangeActAssert
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/autoloader.php b/www/wiki/extensions/SemanticMediaWiki/tests/autoloader.php
new file mode 100644
index 00000000..4c605390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/autoloader.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * Convenience autoloader to pre-register test classes
+ *
+ * Third-party users that require SMW as integration platform should
+ * add the following to the bootstrap.php
+ *
+ * require __DIR__ . '/../../SemanticMediaWiki/tests/autoloader.php'
+ */
+if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
+ die( 'Not an entry point' );
+}
+
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'MediaWiki is not available.' );
+}
+
+if ( !class_exists( 'SemanticMediaWiki' ) || SemanticMediaWiki::getVersion() === null ) {
+ die( "\nSemantic MediaWiki is not available, please check your LocalSettings or Composer settings.\n" );
+}
+
+if ( is_readable( $path = __DIR__ . '/../vendor/autoload.php' ) ) {
+ $autoloadType = "Extension vendor autoloader";
+} elseif ( is_readable( $path = __DIR__ . '/../../../vendor/autoload.php' ) ) {
+ $autoloadType = "MediaWiki vendor autoloader";
+} else {
+ die( 'To run the test suite it is required that packages are installed using Composer.' );
+}
+
+require __DIR__ . '/phpUnitEnvironment.php';
+$phpUnitEnvironment = new PHPUnitEnvironment();
+
+if ( $phpUnitEnvironment->hasDebugRequest( $GLOBALS['argv'] ) === false ) {
+ $phpUnitEnvironment->emptyDebugVars();
+}
+
+$phpUnitEnvironment->writeNewLn( "Semantic MediaWiki:", $phpUnitEnvironment->getVersion( 'smw' ) );
+$phpUnitEnvironment->writeLn( "MediaWiki:", $phpUnitEnvironment->getVersion( 'mw', [ 'type' => $autoloadType ] ) );
+$phpUnitEnvironment->writeLn( "Site language:", $phpUnitEnvironment->getSiteLanguageCode() );
+$phpUnitEnvironment->writeNewLn( "Execution time:", $phpUnitEnvironment->executionTime() );
+$phpUnitEnvironment->writeLn( "Debug logs:", ( $phpUnitEnvironment->enabledDebugLogs() ? 'Enabled' : 'Disabled' ) );
+$phpUnitEnvironment->writeLn( "Xdebug:", ( ( $version = $phpUnitEnvironment->getXdebugInfo() ) ? $version : 'Disabled (or not installed)' ) );
+$phpUnitEnvironment->writeNewLn();
+
+unset( $phpUnitEnvironment );
+
+/**
+ * Available to aid third-party extensions therefore any change should be made with
+ * care
+ *
+ * @since 2.0
+ */
+$autoloader = require $path;
+
+$autoloader->addPsr4( 'SMW\\Tests\\Utils\\', __DIR__ . '/phpunit/Utils' );
+
+$autoloader->addClassMap( [
+ 'SMW\Tests\TestEnvironment' => __DIR__ . '/phpunit/TestEnvironment.php',
+ 'SMW\Tests\TestConfig' => __DIR__ . '/phpunit/TestConfig.php',
+ 'SMW\Tests\PHPUnitCompat' => __DIR__ . '/phpunit/PHPUnitCompat.php',
+ 'SMW\Tests\DatabaseTestCase' => __DIR__ . '/phpunit/DatabaseTestCase.php',
+ 'SMW\Tests\JsonTestCaseScriptRunner' => __DIR__ . '/phpunit/JsonTestCaseScriptRunner.php',
+ 'SMW\Tests\JsonTestCaseFileHandler' => __DIR__ . '/phpunit/JsonTestCaseFileHandler.php',
+ 'SMW\Tests\JsonTestCaseContentHandler' => __DIR__ . '/phpunit/JsonTestCaseContentHandler.php',
+ 'SMW\Test\QueryPrinterTestCase' => __DIR__ . '/phpunit/QueryPrinterTestCase.php',
+ 'SMW\Test\QueryPrinterRegistryTestCase' => __DIR__ . '/phpunit/QueryPrinterRegistryTestCase.php',
+ 'SMW\Tests\SPARQLStore\RepositoryConnectors\ElementaryRepositoryConnectorTest' => __DIR__ . '/phpunit/Unit/SPARQLStore/RepositoryConnectors/ElementaryRepositoryConnectorTest.php',
+] );
+
+// 3.0
+class_alias( '\SMW\Tests\DatabaseTestCase', '\SMW\Tests\MwDBaseUnitTestCase' );
+
+return $autoloader;
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/bootstrap.php b/www/wiki/extensions/SemanticMediaWiki/tests/bootstrap.php
new file mode 100644
index 00000000..57c890f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/bootstrap.php
@@ -0,0 +1,47 @@
+<?php
+
+use SMW\MediaWiki\Connection\Sequence;
+use SMW\ApplicationFactory;
+use SMW\SQLStore\SQLStore;
+
+if ( PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ) {
+ die( 'Not an entry point' );
+}
+
+error_reporting( -1 );
+ini_set( 'display_errors', '1' );
+
+$autoloader = require __DIR__ . '/autoloader.php';
+
+$autoloader->addPsr4( 'SMW\\Test\\', __DIR__ . '/phpunit' );
+$autoloader->addPsr4( 'SMW\\Tests\\', __DIR__ . '/phpunit' );
+
+$autoloader->addClassMap( [
+ 'SMW\Tests\DataItemTest' => __DIR__ . '/phpunit/includes/dataitems/DataItemTest.php',
+ 'SMW\Maintenance\RebuildConceptCache' => __DIR__ . '/../maintenance/rebuildConceptCache.php',
+ 'SMW\Maintenance\RebuildData' => __DIR__ . '/../maintenance/rebuildData.php',
+ 'SMW\Maintenance\RebuildPropertyStatistics' => __DIR__ . '/../maintenance/rebuildPropertyStatistics.php',
+ 'SMW\Maintenance\RebuildFulltextSearchTable' => __DIR__ . '/../maintenance/rebuildFulltextSearchTable.php',
+ 'SMW\Maintenance\DumpRdf' => __DIR__ . '/../maintenance/dumpRDF.php',
+ 'SMW\Maintenance\SetupStore' => __DIR__ . '/../maintenance/setupStore.php',
+ 'SMW\Maintenance\UpdateEntityCollation' => __DIR__ . '/../maintenance/updateEntityCollation.php',
+ 'SMW\Maintenance\RemoveDuplicateEntities' => __DIR__ . '/../maintenance/removeDuplicateEntities.php'
+] );
+
+/**
+ * Register a shutdown function the invoke a final clean-up
+ */
+register_shutdown_function( function() {
+
+ if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+ return;
+ }
+
+ // Reset any sequence modified during the test
+ $sequence = new Sequence(
+ ApplicationFactory::getInstance()->getConnectionManager()->getConnection( 'mw.db' )
+ );
+
+ $sequence->tablePrefix( '' );
+ $sequence->restart( SQLStore::ID_TABLE, 'smw_id' );
+} );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpUnitEnvironment.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpUnitEnvironment.php
new file mode 100644
index 00000000..0a05c762
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpUnitEnvironment.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PHPUnitEnvironment {
+
+ /**
+ * @var array
+ */
+ private $gitHead = [];
+
+ /**
+ * @param array $args
+ *
+ * @return boolean
+ */
+ public function hasDebugRequest( $args ) {
+ return array_search( '--debug', $args ) || array_search( '--debug-tests', $args );
+ }
+
+ public function emptyDebugVars() {
+ $GLOBALS['wgDebugLogGroups'] = [];
+ $GLOBALS['wgDebugLogFile'] = '';
+ }
+
+ /**
+ * @return boolean
+ */
+ public function enabledDebugLogs() {
+ return $GLOBALS['wgDebugLogGroups'] !== [] || $GLOBALS['wgDebugLogFile'] !== '';
+ }
+
+ /**
+ * @return boolean|integer
+ */
+ public function getXdebugInfo() {
+
+ if ( extension_loaded( 'xdebug' ) && xdebug_is_enabled() ) {
+ return phpversion( 'xdebug' );
+ }
+
+ return false;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSiteLanguageCode() {
+ return $GLOBALS['wgLanguageCode'];
+ }
+
+ /**
+ * @return string
+ */
+ public function executionTime() {
+ $dateTimeUtc = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
+ return $dateTimeUtc->format( 'Y-m-d h:i' );
+ }
+
+ /**
+ * @param string $id
+ *
+ * @return array
+ */
+ public function getVersion( $id, $extra = [] ) {
+
+ $info = [];
+
+ if ( $id === 'smw' ) {
+ $store = str_replace(
+ [ '{', '}', '"', '(SMW', ':(', '))', ',' ],
+ [ '(', ')', '', 'SMW', ' (', ')', ', ' ],
+ json_encode( smwfGetStore()->getInfo(), JSON_UNESCAPED_SLASHES )
+ );
+
+ $info = [
+ SemanticMediaWiki::getVersion(),
+ 'git: ' . $this->getGitInfo( 'smw' ),
+ $store
+ ] + $extra;
+ }
+
+ if ( $id === 'mw' ) {
+ $info = [
+ $GLOBALS['wgVersion'],
+ 'git: ' . $this->getGitInfo( 'mw' )
+ ] + $extra;
+ }
+
+ return implode( ', ', $info );
+ }
+
+ /**
+ * @param string $id
+ *
+ * @return string
+ */
+ public function getGitInfo( $id ) {
+
+ if ( $this->gitHead === [] && class_exists( 'GitInfo' ) ) {
+ $this->gitHead = [
+ 'mw' => '',
+ 'smw' => ''
+ ];
+
+ $this->gitHead['mw'] = GitInfo::headSHA1();
+
+ if ( $this->gitHead['mw'] ) {
+ $this->gitHead['mw'] = substr( $this->gitHead['mw'], 0, 7 );
+ } else {
+ $this->gitHead['mw'] = 'N/A';
+ }
+
+ $gitInfo = new GitInfo( __DIR__ . '/..' );
+ $this->gitHead['smw'] = $gitInfo->getHeadSHA1();
+
+ if ( $this->gitHead['smw'] ) {
+ $this->gitHead['smw'] = substr( $this->gitHead['smw'], 0, 7 );
+ }
+ }
+
+ if ( isset( $this->gitHead[$id] ) ) {
+ return $this->gitHead[$id];
+ }
+ }
+
+ /**
+ * @param string $arg1
+ * @param string|array $arg2
+ *
+ * @return string
+ */
+ public function writeLn( $arg1, $arg2 ) {
+ return print sprintf( "%-20s%s\n", $arg1, $arg2 );
+ }
+
+ /**
+ * @param string $arg1
+ * @param string|array $arg2
+ *
+ * @return string
+ */
+ public function writeNewLn( $arg1 = '', $arg2 = '' ) {
+
+ if ( $arg1 === '' && $arg2 === '' ) {
+ return print "\n";
+ }
+
+ return print sprintf( "\n%-20s%s\n", $arg1, $arg2 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkJsonScriptRunnerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkJsonScriptRunnerTest.php
new file mode 100644
index 00000000..b95e6617
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkJsonScriptRunnerTest.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\JsonTestCaseFileHandler;
+use SMW\Tests\JsonTestCaseScriptRunner;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class BenchmarkJsonScriptRunnerTest extends JsonTestCaseScriptRunner {
+
+ /**
+ * @var PageImportBenchmarkRunner
+ */
+ private $pageImportBenchmarkRunner;
+
+ /**
+ * @var PageContentCopyBenchmarkRunner
+ */
+ private $pageContentCopyBenchmarkRunner;
+
+ /**
+ * @var PageEditCopyBenchmarkRunner
+ */
+ private $pageEditCopyBenchmarkRunner;
+
+ /**
+ * @var JobQueueBenchmarkRunner
+ */
+ private $jobQueueBenchmarkRunner;
+
+ /**
+ * @var MaintenanceBenchmarkRunner
+ */
+ private $maintenanceBenchmarkRunner;
+
+ /**
+ * @var QueryBenchmarkRunner
+ */
+ private $queryBenchmarkRunner;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReports = [];
+
+ /**
+ * @see JsonTestCaseScriptRunner::$deletePagesOnTearDown
+ */
+ protected $deletePagesOnTearDown = true;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+ $benchmarker = new Benchmarker();
+
+ $this->pageImportBenchmarkRunner = new PageImportBenchmarkRunner(
+ $utilityFactory->newRunnerFactory()->newXmlImportRunner(),
+ $benchmarker
+ );
+
+ $this->pageContentCopyBenchmarkRunner = new PageContentCopyBenchmarkRunner(
+ $this->pageImportBenchmarkRunner,
+ $benchmarker,
+ $utilityFactory->newPageCreator(),
+ $utilityFactory->newPageReader()
+ );
+
+ // Variable set via phpunit.xml
+ $this->pageContentCopyBenchmarkRunner->setCopyCount(
+ isset( $GLOBALS['benchmarkPageCopyCount'] ) ? $GLOBALS['benchmarkPageCopyCount'] : null
+ );
+
+ $this->pageEditCopyBenchmarkRunner = new PageEditCopyBenchmarkRunner(
+ $this->pageImportBenchmarkRunner,
+ $benchmarker,
+ $utilityFactory->newPageCreator(),
+ $utilityFactory->newPageReader()
+ );
+
+ // Variable set via phpunit.xml
+ $this->pageEditCopyBenchmarkRunner->setEditRepetitionCount(
+ isset( $GLOBALS['benchmarkPageEditRepetitionCount'] ) ? $GLOBALS['benchmarkPageEditRepetitionCount'] : null
+ );
+
+ $this->jobQueueBenchmarkRunner = new JobQueueBenchmarkRunner(
+ ApplicationFactory::getInstance()->newJobFactory(),
+ $utilityFactory->newRunnerFactory()->newJobQueueRunner(),
+ $benchmarker
+ );
+
+ $this->maintenanceBenchmarkRunner = new MaintenanceBenchmarkRunner(
+ $utilityFactory->newRunnerFactory(),
+ $benchmarker
+ );
+
+ $this->queryBenchmarkRunner = new QueryBenchmarkRunner(
+ $this->getStore(),
+ ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser(),
+ $benchmarker
+ );
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getTestCaseLocation
+ */
+ protected function getTestCaseLocation() {
+ return __DIR__ . '/TestCases';
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getTestCaseLocation
+ */
+ protected function getRequiredJsonTestCaseMinVersion() {
+ return '1';
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getAllowedTestCaseFiles
+ */
+ protected function getAllowedTestCaseFiles() {
+ return [];
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::runTestCaseFile
+ *
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ protected function runTestCaseFile( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ $this->pageImportBenchmarkRunner->setTestCaseLocation(
+ $this->getTestCaseLocation()
+ );
+
+ $this->doRunImportBenchmarks( $jsonTestCaseFileHandler );
+ $this->doRunContentCopyBenchmarks( $jsonTestCaseFileHandler );
+ $this->doRunEditCopyBenchmarks( $jsonTestCaseFileHandler );
+ $this->doRunJobQueueBenchmarks( $jsonTestCaseFileHandler );
+ $this->doRunMaintenanceBenchmarks( $jsonTestCaseFileHandler );
+ $this->doRunQueryBenchmarks( $jsonTestCaseFileHandler );
+
+ $this->assertNotEmpty(
+ $this->benchmarkReports
+ );
+
+ $report = [
+ 'mediawiki' => $GLOBALS['wgVersion'],
+ 'semantic-mediawiki' => \SemanticMediaWiki::getVersion(),
+ 'environment' => $this->getStore()->getInfo(),
+ 'benchmarks' => $this->benchmarkReports
+ ];
+
+ $cliOutputFormatter = new CliOutputFormatter(
+ CliOutputFormatter::FORMAT_JSON
+ );
+
+ return print "\n\n" . $cliOutputFormatter->format( $report );
+ }
+
+ private function doRunImportBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'import' ) as $case ) {
+ $this->pageImportBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->pageImportBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+ private function doRunContentCopyBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'contentCopy' ) as $case ) {
+ $this->pageContentCopyBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->pageContentCopyBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+ private function doRunEditCopyBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'editCopy' ) as $case ) {
+ $this->pageEditCopyBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->pageEditCopyBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+ private function doRunJobQueueBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'job' ) as $case ) {
+ $this->jobQueueBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->jobQueueBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+ private function doRunMaintenanceBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'maintenance' ) as $case ) {
+ $this->maintenanceBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->maintenanceBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+ private function doRunQueryBenchmarks( $jsonTestCaseFileHandler ) {
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'query' ) as $case ) {
+ $this->queryBenchmarkRunner->run( $case );
+ $this->benchmarkReports[md5(json_encode( $case ) )] = $this->queryBenchmarkRunner->getBenchmarkReport();
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkReporter.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkReporter.php
new file mode 100644
index 00000000..2881ce78
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/BenchmarkReporter.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+interface BenchmarkReporter {
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport();
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Benchmarker.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Benchmarker.php
new file mode 100644
index 00000000..41f4c9a0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Benchmarker.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class Benchmarker {
+
+ /**
+ * @var array
+ */
+ private $container = [];
+
+ /**
+ * @var boolean
+ */
+ private $useAsSample = false;
+
+ /**
+ * @var integer
+ */
+ private $roundFactor;
+
+ /**
+ * @since 2.1
+ */
+ public function __construct( $roundFactor = 7 ) {
+ $this->roundFactor = $roundFactor;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function clear() {
+ $this->container = [];
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param boalean $state false when takling about the entire population;
+ * true when it is a sample (a selection from a population)
+ */
+ public function useAsSample( $state = true ) {
+ $this->useAsSample = $state;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $roundFactor
+ */
+ public function roundBy( $roundFactor ) {
+ $this->roundFactor = $roundFactor;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $point
+ * @param integer $decimals
+ */
+ public function addBenchmarkPoint( $point, $decimals = 10 ) {
+ $this->container[] = number_format( $point, $decimals );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer[] $poins
+ */
+ public function addBenchmarkPoints( array $poins ) {
+ $this->container = $poins;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return integer
+ */
+ public function getSum() {
+ return $this->round( array_sum( $this->container ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return integer
+ */
+ public function getMean() {
+ return $this->round( $this->getSum() / count( $this->container ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return integer
+ */
+ public function getVariance() {
+
+ $mean = $this->getMean();
+ $count = count( $this->container );
+
+ $sumOfSquares = 0;
+
+ foreach ( $this->container as $value ) {
+ $sumOfSquares += pow( $value - $mean, 2 );
+ }
+
+ return $sumOfSquares / ( $this->useAsSample ? $count - 1 : $count );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return integer
+ */
+ public function getStandardDeviation() {
+ return $this->round( (float)sqrt( $this->getVariance() ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $basis
+ *
+ * @return integer
+ */
+ public function getStandardScoreBy( $basis = 0 ) {
+ return $this->round( ( $basis - $this->getMean() ) / $this->getStandardDeviation() );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param integer $basis
+ *
+ * @return integer
+ */
+ public function getNormalizedValueBy( $basis = 1 ) {
+ return $this->round( $this->getMean() / $basis );
+ }
+
+ private function round( $value ) {
+ return round( $value, $this->roundFactor );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/CliOutputFormatter.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/CliOutputFormatter.php
new file mode 100644
index 00000000..9afd5484
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/CliOutputFormatter.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class CliOutputFormatter {
+
+ const FORMAT_TREE = 'format.tree';
+ const FORMAT_JSON = 'format.json';
+
+ /**
+ * @var string
+ */
+ private $formatType;
+
+ /**
+ * @since 2.5
+ *
+ * @param string $formatType
+ */
+ public function __construct( $formatType = self::FORMAT_TREE ) {
+ $this->formatType = $formatType;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $report
+ */
+ public function format( array $report ) {
+
+ if ( $this->formatType === self::FORMAT_TREE ) {
+ return $this->doFormatAsTree( $report );
+ }
+
+ return json_encode( $report, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
+ }
+
+ private function doFormatAsTree( $report, $label = '', $level = 1 ) {
+ $output = '';
+
+ if ( is_string( $label ) && $label !== '' ) {
+ $output .= sprintf( "%s- %s\n", str_repeat( ' ', $level ), $label );
+ $level++;
+ }
+
+ foreach ( $report as $key => $value ) {
+
+ $isDeeper = false;
+
+ if ( is_array( $value ) ) {
+ foreach ( $value as $p => $v ) {
+ if ( is_array( $v ) ) {
+ $isDeeper = true;
+ }
+ }
+ }
+
+ if ( $isDeeper ) {
+ $output .= $this->doFormatAsTree( $value, $key, $level + 1 );
+ } else {
+ $output .= sprintf( "%s- %s: %s\n", str_repeat( ' ', $level ), $key, json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
+ }
+ }
+
+ return $output;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-001.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-001.xml
new file mode 100644
index 00000000..7335eee6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-001.xml
@@ -0,0 +1,342 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:Has annotation uri</title>
+ <ns>102</ns>
+ <id>503</id>
+ <sha1>ifvt9xhpsfkv04ehcnhpw3i88s0fsb8</sha1>
+ <revision>
+ <id>1342</id>
+ <timestamp>2014-03-29T16:51:25Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="53">[[Has type::Annotation URI]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has boolean</title>
+ <ns>102</ns>
+ <id>502</id>
+ <sha1>l3b3eelfib1em61uurletnc4kb1xlzh</sha1>
+ <revision>
+ <id>1338</id>
+ <timestamp>2014-03-29T16:47:13Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Boolean]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="46">[[Has type::Boolean]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has date</title>
+ <ns>102</ns>
+ <id>14</id>
+ <sha1>473nf5knraq99k8zsrg6z7o3812tu61</sha1>
+ <revision>
+ <id>1330</id>
+ <timestamp>2014-03-29T16:38:59Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Date]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has email</title>
+ <ns>102</ns>
+ <id>500</id>
+ <sha1>9puenjhr9dntyg9obgtwhvaenn98rav</sha1>
+ <revision>
+ <id>1334</id>
+ <timestamp>2014-03-29T16:42:38Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Email]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="44">[[Has type::Email]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has number</title>
+ <ns>102</ns>
+ <id>494</id>
+ <sha1>9fqf0pft61g8qqb8qtz1i5ayr1rmu30</sha1>
+ <revision>
+ <id>1321</id>
+ <timestamp>2014-03-29T16:30:26Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Number]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="45">[[Has type::Number]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has page</title>
+ <ns>102</ns>
+ <id>489</id>
+ <sha1>4sq0eljhm3yty9cifkvel3mv646ag0y</sha1>
+ <revision>
+ <id>1319</id>
+ <timestamp>2014-03-29T16:29:46Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Page]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has quantity</title>
+ <ns>102</ns>
+ <id>495</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1323</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Quantity]] [[Category:Lorem ipsum]]
+
+* [[Corresponds to::1 km²]]
+* [[Corresponds to::0.38610 sq mi]]
+* [[Corresponds to::1000 m²]]
+* [[Corresponds to::247.1054 acre]]
+* [[Corresponds to::988.4215 rood]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has wattage</title>
+ <ns>102</ns>
+ <id>503</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1355</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Quantity]] [[Display units::kW]] [[Category:Lorem ipsum]]
+
+* [[Corresponds to::1 W, Watt, Watts]]
+* [[Corresponds to::0.001 kW]]
+* [[Corresponds to::0.0013410220 hp, bhp, horsepower]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has status</title>
+ <ns>102</ns>
+ <id>504</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1365</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Text]] [[Category:Lorem ipsum]]
+
+* [[Allows value::open]]
+* [[Allows value::closed]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has telephone number</title>
+ <ns>102</ns>
+ <id>505</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1375</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Telephone number]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record</title>
+ <ns>102</ns>
+ <id>506</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1385</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Record]] [[Has fields::Has text; Has status]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has temperature</title>
+ <ns>102</ns>
+ <id>501</id>
+ <sha1>925639l5wc4pwdmufjgza10rstpb2oa</sha1>
+ <revision>
+ <id>1336</id>
+ <timestamp>2014-03-29T16:45:35Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Temperature]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="50">[[Has type::Temperature]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has text</title>
+ <ns>102</ns>
+ <id>491</id>
+ <sha1>qi7oxp4101tdesdm3vavpgpfn1qe68u</sha1>
+ <revision>
+ <id>1320</id>
+ <timestamp>2014-03-29T16:30:07Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Text]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has Url</title>
+ <ns>102</ns>
+ <id>499</id>
+ <sha1>1fw7q2wts7v11q1z6d31g89zorhco0i</sha1>
+ <revision>
+ <id>1332</id>
+ <timestamp>2014-03-29T16:40:28Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::URL]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="42">[[Has type::URL]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem ipsum</title>
+ <ns>0</ns>
+ <id>493</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1358</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">[[Has text::Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis neque leo scelerisque nec habitasse a lacus mattis. Accumsan tincidunt Sed adipiscing nec facilisis tortor Nunc Sed ipsum tellus.]] [[Has page::Turpis pellentesque]] id sociis pede ipsum Quisque. Eu sed sed porta Integer pulvinar accumsan leo in semper lobortis. Urna vel mattis [[Has page::Pellentesque ante]] iaculis tincidunt et consequat Aliquam dictum.
+
+* Has page: [[Has page::{{PAGENAME}}]]
+* Has number: [[Has number::1001]]
+* Has quantity: [[Has quantity::10.25 km²]]
+* Has date: [[Has date::1 Jan 2014]]
+* Has Url: [[Has Url::http://loremipsum.org/]]
+* Has annotation uri: [[Has annotation uri::http://loremipsum.org/foaf.rdf]]
+* Has email: [[Has email::Lorem@ipsum.org]]
+* Has temperature: [[Has temperature::100 °C]]
+* Has wattage: [[Has wattage::9001]], [[Has wattage::9002 W]], [[Has wattage::10 kW]]
+* Has telephone number: [[Has telephone number::+1-201-555-0123]]
+* Has record: [[Has record::Lorem ipsum; closed]]
+* Has status: [[Has status::open]]
+* Has boolean: [[Has boolean::true]]
+
+{{#subobject:
+ |Has page={{PAGENAME}}
+ |Has number=1111
+ |Has quantity=25 sqmi
+ |Has date=January 4, 2010 7:00 pm
+ |Has Url=http://example.org/some/
+ |Has annotation uri=http://example.org/foaf.rdf
+ |Has email=Lorem@example.org
+ |Has temperature=100 °F
+ |Has wattage=42
+ |Has wattage=42 hp
+ |Has telephone number=+1-201-555-5555
+ |Has record=Lorem enim; closed
+ |Has status=closed
+ |Has boolean=false
+}}
+
+[[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem Ipsum</title>
+ <ns>0</ns>
+ <id>600</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1400</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">#REDIRECT [[Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Lorem ipsum</title>
+ <ns>14</ns>
+ <id>496</id>
+ <sha1>sir97j6uzt9ev2uyhaz1aj4i3spogih</sha1>
+ <revision>
+ <id>1355</id>
+ <timestamp>2014-04-04T22:29:18Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="17">[[Category:Main]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-002.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-002.xml
new file mode 100644
index 00000000..eac01775
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/Fixtures/import-002.xml
@@ -0,0 +1,214 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Lorem donec</title>
+ <ns>0</ns>
+ <id>1490</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1550</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">
+* Has page: [[Has page::{{PAGENAME}}]]
+* Has number: [[Has number::1001]]
+* Has quantity: [[Has quantity::10.25 km²]]
+* Has date: [[Has date::1 Jan 2014]]
+* Has Url: [[Has Url::http://loremipsum.org/]]
+* Has annotation uri: [[Has annotation uri::http://loremipsum.org/foaf.rdf]]
+* Has email: [[Has email::Lorem@ipsum.org]]
+* Has temperature: [[Has temperature::100 °C]]
+* Has wattage: [[Has wattage::9001]], [[Has wattage::9002 W]], [[Has wattage::10 kW]]
+* Has telephone number: [[Has telephone number::+1-201-555-0123]]
+* Has record: [[Has record::Lorem ipsum; closed]]
+* Has status: [[Has status::open]]
+* Has boolean: [[Has boolean::true]]
+
+[[Category:Lorem donec]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem enim</title>
+ <ns>0</ns>
+ <id>1493</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1558</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">
+{{#subobject:
+ |Has page={{PAGENAME}}
+ |Has number=1111
+ |Has quantity=25 sqmi
+ |Has date=January 4, 2010 7:00 pm
+ |Has Url=http://example.org/some/
+ |Has annotation uri=http://example.org/foaf.rdf
+ |Has email=Lorem@example.org
+ |Has temperature=100 °F
+ |Has wattage=42
+ |Has wattage=42 hp
+ |Has telephone number=+1-201-555-5555
+ |Has record=Lorem enim; closed
+ |Has status=closed
+ |Has boolean=false
+}}
+
+[[Category:Lorem enim]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Template:Laoreet tortor</title>
+ <ns>10</ns>
+ <id>1600</id>
+ <sha1>c68evl9y2r71xng73mzd5z4c2u72a8h</sha1>
+ <revision>
+ <id>1601</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="494">&lt;includeonly&gt;
+{{#set:
+ |Has page={{{Has page}}}
+ |Has number={{{Has number}}}
+ |Has quantity={{{Has quantity}}}
+ |Has date={{{Has date}}}
+ |Has Url={{{Has Url}}}
+ |Has annotation uri={{{Has annotation uri}}}
+ |Has email={{{Has email}}}
+ |Has temperature={{{Has temperature}}}
+ |Has wattage={{{Has wattage}}}
+ |Has telephone number={{{Has telephone number}}}
+ |Has record={{{Has record}}}
+ |Has status={{{Has status}}}
+ |Has boolean={{{Has boolean}}}
+}}
+[[Category:Laoreet tortor]]&lt;/includeonly&gt;</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem sit</title>
+ <ns>0</ns>
+ <id>1610</id>
+ <sha1>0rqia1jo6xts3vfzow5e2ulxapdo039</sha1>
+ <revision>
+ <id>1611</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="431">{{Laoreet tortor
+ |Has page={{PAGENAME}}
+ |Has number=1111
+ |Has quantity=25 sqmi
+ |Has date=January 4, 2010 7:00 pm
+ |Has Url=http://example.org/some/
+ |Has annotation uri=http://example.org/foaf.rdf
+ |Has email=Lorem@example.org
+ |Has temperature=100 °F
+ |Has wattage=42
+ |Has wattage=42 hp
+ |Has telephone number=+1-201-555-5555
+ |Has record=Lorem enim; closed
+ |Has status=closed
+ |Has boolean=false
+}}
+
+[[Category:lorem sit]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem tempor</title>
+ <ns>0</ns>
+ <id>1620</id>
+ <sha1>0rqia1jo6xts3vfzow5e2ulxapdo039</sha1>
+ <revision>
+ <id>1621</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="431">{{#ask: [[Category:Lorem sit]][[Has page::+]]
+ |?Has page
+ |?Has number
+ |?Has quantity
+ |?Has date
+ |?Has Url
+ |?Has annotation uri
+ |?Has email
+ |?Has temperature
+ |?Has wattage
+ |limit=50
+}}
+
+{{#ask: [[Category:Lorem tortor]][[Has number::+]]
+ |?Has page
+ |?Has number
+ |?Has quantity
+ |?Has date
+ |?Has Url
+ |?Has annotation uri
+ |?Has email
+ |?Has temperature
+ |?Has wattage
+ |limit=50
+}}
+
+{{#ask: [[Category:Lorem enim]][[Has date::+]]
+ |?Has page
+ |?Has number
+ |?Has quantity
+ |?Has date
+ |?Has Url
+ |?Has annotation uri
+ |?Has email
+ |?Has temperature
+ |?Has wattage
+ |limit=50
+}}</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/JobQueueBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/JobQueueBenchmarkRunner.php
new file mode 100644
index 00000000..650d027c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/JobQueueBenchmarkRunner.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\MediaWiki\JobFactory;
+use SMW\Tests\Utils\Runners\JobQueueRunner;
+use Title;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JobQueueBenchmarkRunner implements BenchmarkReporter {
+
+ /**
+ * @var JobFactory
+ */
+ private $jobFactory;
+
+ /**
+ * @var JobQueueRunner
+ */
+ private $jobQueueRunner;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @var string
+ */
+ private $testCaseLocation;
+
+ /**
+ * @since 2.5
+ *
+ * @param JobFactory $jobFactory
+ * @param JobQueueRunner $jobQueueRunner
+ * @param Benchmarker $benchmarker
+ */
+ public function __construct( JobFactory $jobFactory, JobQueueRunner $jobQueueRunner, Benchmarker $benchmarker ) {
+ $this->jobFactory = $jobFactory;
+ $this->jobQueueRunner = $jobQueueRunner;
+ $this->benchmarker = $benchmarker;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+
+ if ( !isset( $case['job'] ) ) {
+ throw new RuntimeException( 'No job name is available.' );
+ }
+
+ if ( !isset( $case['repetitionCount'] ) ) {
+ throw new RuntimeException( 'No repetitionCount is available.' );
+ }
+
+ $job = $this->jobFactory->newByType(
+ $case['job'],
+ Title::newFromText( __METHOD__ . $case['job'] )
+ );
+
+ $job->insert();
+
+ $this->doRunJob( $case );
+ }
+
+ private function doRunJob( array $case ) {
+
+ $this->jobQueueRunner->setType( $case['job'] );
+ $memoryBefore = memory_get_peak_usage( false );
+
+ for ( $i = 0; $i < $case['repetitionCount']; $i++ ) {
+
+ $start = microtime( true );
+
+ $this->jobQueueRunner->run();
+
+ $this->benchmarker->addBenchmarkPoint(
+ microtime( true ) - $start
+ );
+ }
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'case' => $case['job'],
+ 'repetitionCount' => $case['repetitionCount'],
+ 'memory' => memory_get_peak_usage( false ) - $memoryBefore,
+ 'time' => [
+ 'sum' => $this->benchmarker->getSum(),
+ 'mean' => $this->benchmarker->getMean(),
+ 'sd' => $this->benchmarker->getStandardDeviation(),
+ 'norm' => $this->benchmarker->getNormalizedValueBy( $case['repetitionCount'] )
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/MaintenanceBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/MaintenanceBenchmarkRunner.php
new file mode 100644
index 00000000..5299fba4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/MaintenanceBenchmarkRunner.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\Tests\Utils\Runners\RunnerFactory;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MaintenanceBenchmarkRunner implements BenchmarkReporter {
+
+ /**
+ * @var RunnerFactory
+ */
+ private $runnerFactory;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param RunnerFactory $runnerFactory
+ * @param Benchmarker $benchmarker
+ */
+ public function __construct( RunnerFactory $runnerFactory, Benchmarker $benchmarker ) {
+ $this->runnerFactory = $runnerFactory;
+ $this->benchmarker = $benchmarker;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+
+ if ( !isset( $case['script'] ) ) {
+ throw new RuntimeException( 'Script name is not available.' );
+ }
+
+ if ( !isset( $case['repetitionCount'] ) ) {
+ throw new RuntimeException( 'No repetitionCount is available.' );
+ }
+
+ if ( !isset( $case['options'] ) ) {
+ throw new RuntimeException( 'No options are available.' );
+ }
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner(
+ $case['script']
+ );
+
+ $maintenanceRunner->setQuiet();
+ $maintenanceRunner->setOptions( $case['options'] );
+
+ $this->doRunMaintenance( $maintenanceRunner, $case );
+ }
+
+ private function doRunMaintenance( $maintenanceRunner, array $case ) {
+
+ $memoryBefore = memory_get_peak_usage( false );
+
+ for ( $i = 0; $i < $case['repetitionCount']; $i++ ) {
+
+ $start = microtime( true );
+
+ $maintenanceRunner->run();
+
+ $this->benchmarker->addBenchmarkPoint(
+ microtime( true ) - $start
+ );
+ }
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'case' => $case['script'],
+ 'repetitionCount' => $case['repetitionCount'],
+ 'memory' => memory_get_peak_usage( false ) - $memoryBefore,
+ 'time' => [
+ 'sum' => $this->benchmarker->getSum(),
+ 'mean' => $this->benchmarker->getMean(),
+ 'sd' => $this->benchmarker->getStandardDeviation(),
+ 'norm' => $this->benchmarker->getNormalizedValueBy( $case['repetitionCount'] )
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageContentCopyBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageContentCopyBenchmarkRunner.php
new file mode 100644
index 00000000..57154279
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageContentCopyBenchmarkRunner.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageReader;
+use Title;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PageContentCopyBenchmarkRunner {
+
+ /**
+ * @var PageImportBenchmarkRunner
+ */
+ private $pageImportBenchmarkRunner;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator;
+
+ /**
+ * @var PageReader
+ */
+ private $pageReader;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @var integer|count
+ */
+ private $copyCount = null;
+
+ /**
+ * @since 2.5
+ *
+ * @param PageImportBenchmarkRunner $pageImportBenchmarkRunner
+ * @param Benchmarker $benchmarker
+ * @param PageCreator $pageCreator
+ * @param PageReader $pageReader
+ */
+ public function __construct( PageImportBenchmarkRunner $pageImportBenchmarkRunner, Benchmarker $benchmarker, PageCreator $pageCreator, PageReader $pageReader ) {
+ $this->pageImportBenchmarkRunner = $pageImportBenchmarkRunner;
+ $this->benchmarker = $benchmarker;
+ $this->pageCreator = $pageCreator;
+ $this->pageReader = $pageReader;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|null $copyCount
+ */
+ public function setCopyCount( $copyCount = null ) {
+ $this->copyCount = $copyCount;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+ $start = microtime( true );
+
+ $this->pageImportBenchmarkRunner->run( $case );
+ $importBenchmarkReport = $this->pageImportBenchmarkRunner->getBenchmarkReport();
+
+ if ( !isset( $case['copyFrom'] ) ) {
+ throw new RuntimeException( 'Copy title is not available.' );
+ }
+
+ if ( !isset( $case['copyCount'] ) ) {
+ throw new RuntimeException( 'Copy count is not available.' );
+ }
+
+ $copyFrom = Title::newFromText( $case['copyFrom'] );
+
+ if ( !$copyFrom->exists() ) {
+ throw new RuntimeException( $case['copyFrom'] . ' is not available or readable for the copy process.' );
+ }
+
+ if ( !$this->canOverrideCount( $case ) ) {
+ $this->copyCount = $case['copyCount'];
+ }
+
+ $copyMemory = $this->doCopy( $copyFrom, $case );
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'source' => $case['name'],
+ 'import' => [
+ 'memory' => $importBenchmarkReport['memory'],
+ 'time' => $importBenchmarkReport['time']
+ ],
+ 'copy' => [
+ 'copyFrom' => $case['copyFrom'],
+ 'copyCount' => $this->copyCount,
+ 'memory' => $copyMemory,
+ 'time' => [
+ 'sum' => $this->benchmarker->getSum(),
+ 'mean' => $this->benchmarker->getMean(),
+ 'sd' => $this->benchmarker->getStandardDeviation(),
+ 'norm' => $this->benchmarker->getNormalizedValueBy( $this->copyCount )
+ ]
+ ],
+ 'time' => microtime( true ) - $start
+ ];
+ }
+
+ private function doCopy( $copyFrom, array $case ) {
+
+ $copyText = $this->pageReader->getContentAsText( $copyFrom );
+ $copyName = 'BenchmarkCopy-' . $copyFrom->getText();
+
+ $memoryBefore = memory_get_peak_usage( false );
+
+ for ( $i = 0; $i < $this->copyCount; $i++ ) {
+
+ $start = microtime( true );
+
+ $this->pageCreator->createPage(
+ Title::newFromText( $copyName . '-' . $i ),
+ $copyText
+ );
+
+ $this->benchmarker->addBenchmarkPoint( microtime( true ) - $start );
+ }
+
+ return memory_get_peak_usage( false ) - $memoryBefore;
+ }
+
+ private function canOverrideCount( $case ) {
+ return isset( $case['canOverrideCopyCount'] ) && $case['canOverrideCopyCount'] && $this->copyCount !== null;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageEditCopyBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageEditCopyBenchmarkRunner.php
new file mode 100644
index 00000000..91fe7f31
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageEditCopyBenchmarkRunner.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageReader;
+use Title;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PageEditCopyBenchmarkRunner {
+
+ /**
+ * @var PageImportBenchmarkRunner
+ */
+ private $pageImportBenchmarkRunner;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator;
+
+ /**
+ * @var PageReader
+ */
+ private $pageReader;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @var integer|count
+ */
+ private $editRepetitionCount = null;
+
+ /**
+ * @since 2.5
+ *
+ * @param PageImportBenchmarkRunner $pageImportBenchmarkRunner
+ * @param Benchmarker $benchmarker
+ * @param PageCreator $pageCreator
+ * @param PageReader $pageReader
+ */
+ public function __construct( PageImportBenchmarkRunner $pageImportBenchmarkRunner, Benchmarker $benchmarker, PageCreator $pageCreator, PageReader $pageReader ) {
+ $this->pageImportBenchmarkRunner = $pageImportBenchmarkRunner;
+ $this->benchmarker = $benchmarker;
+ $this->pageCreator = $pageCreator;
+ $this->pageReader = $pageReader;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|null $copyCount
+ */
+ public function setEditRepetitionCount( $editRepetitionCount = null ) {
+ $this->editRepetitionCount = $editRepetitionCount;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+
+ $start = microtime( true );
+
+ $this->pageImportBenchmarkRunner->run( $case );
+ $importBenchmarkReport = $this->pageImportBenchmarkRunner->getBenchmarkReport();
+
+ if ( !isset( $case['edit'] ) || !is_array( $case['edit'] ) ) {
+ throw new RuntimeException( 'Edit title is not available.' );
+ }
+
+ if ( !isset( $case['editRepetitionCount'] ) ) {
+ throw new RuntimeException( 'editRepetitionCount is not available.' );
+ }
+
+ if ( !$this->canOverrideCount( $case ) ) {
+ $this->editRepetitionCount = $case['editRepetitionCount'];
+ }
+
+ $editReports = [];
+
+ foreach ( $case['edit'] as $title ) {
+
+ $editTitle = Title::newFromText( $title );
+
+ if ( !$editTitle->exists() ) {
+ throw new RuntimeException( $title . ' is not available or readable for the edit process.' );
+ }
+
+ $editReports[$title] = $this->doEdit( $editTitle, $case );
+ }
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'source' => $case['name'],
+ 'import' => [
+ 'memory' => $importBenchmarkReport['memory'],
+ 'time' => $importBenchmarkReport['time']
+ ],
+ 'editTask' => $editReports,
+ 'time' => microtime( true ) - $start
+ ];
+ }
+
+ private function doEdit( $editTitle, array $case ) {
+
+ $copyText = $this->pageReader->getContentAsText( $editTitle );
+ $this->benchmarker->clear();
+
+ $memoryBefore = memory_get_peak_usage( false );
+
+ for ( $i = 0; $i < $this->editRepetitionCount; $i++ ) {
+
+ $start = microtime( true );
+
+ $this->pageCreator->createPage(
+ $editTitle,
+ $copyText
+ );
+
+ $this->benchmarker->addBenchmarkPoint( microtime( true ) - $start );
+ }
+
+ return [
+ 'editRepetitionCount' => $this->editRepetitionCount,
+ "memory" => memory_get_peak_usage( false ) - $memoryBefore,
+ "time" => [
+ 'sum' => $this->benchmarker->getSum(),
+ 'mean' => $this->benchmarker->getMean(),
+ 'sd' => $this->benchmarker->getStandardDeviation(),
+ 'norm' => $this->benchmarker->getNormalizedValueBy( $this->editRepetitionCount )
+ ]
+ ];
+ }
+
+ private function canOverrideCount( $case ) {
+ return isset( $case['canOverrideEditCount'] ) && $case['canOverrideEditCount'] && $this->editRepetitionCount !== null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageImportBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageImportBenchmarkRunner.php
new file mode 100644
index 00000000..758dd7f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/PageImportBenchmarkRunner.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\Tests\Utils\Runners\XmlImportRunner;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PageImportBenchmarkRunner implements BenchmarkReporter {
+
+ /**
+ * @var XmlImportRunner
+ */
+ private $xmlImportRunner;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @var string
+ */
+ private $testCaseLocation;
+
+ /**
+ * @since 2.5
+ *
+ * @param XmlImportRunner $xmlImportRunner
+ * @param Benchmarker $benchmarker
+ */
+ public function __construct( XmlImportRunner $xmlImportRunner, Benchmarker $benchmarker ) {
+ $this->xmlImportRunner = $xmlImportRunner;
+ $this->benchmarker = $benchmarker;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $testCaseLocation
+ */
+ public function setTestCaseLocation( $testCaseLocation ) {
+ $this->testCaseLocation = $testCaseLocation;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+
+ if ( !isset( $case['importFrom'] ) ) {
+ throw new RuntimeException( 'No import file is available.' );
+ }
+
+ $file = $this->testCaseLocation . $case['importFrom'];
+
+ if ( !is_readable( $file ) ) {
+ throw new RuntimeException( $file . ' as import file is not available.' );
+ }
+
+ $ext = pathinfo( $file, PATHINFO_EXTENSION );
+
+ switch ( $ext ) {
+ case 'xml':
+ return $this->doXmlImport( $file, $case );
+ break;
+ default:
+ # code...
+ break;
+ }
+ }
+
+ private function doXmlImport( $file, array $case ) {
+
+ $this->xmlImportRunner->setFile( $file );
+ $this->xmlImportRunner->setVerbose( true );
+
+ $memoryBefore = memory_get_peak_usage( false );
+
+ if ( !$this->xmlImportRunner->run() ) {
+ $this->xmlImportRunner->reportFailedImport();
+ }
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'source' => $case['name'],
+ 'memory' => memory_get_peak_usage( false ) - $memoryBefore,
+ 'time' => [
+ 'sum' => $this->xmlImportRunner->getElapsedImportTimeInSeconds()
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/QueryBenchmarkRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/QueryBenchmarkRunner.php
new file mode 100644
index 00000000..2ff457dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/QueryBenchmarkRunner.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\Tests\Benchmark;
+
+use RuntimeException;
+use SMW\DIProperty;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Store;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+use SMWQueryParser as QueryParser;
+use Title;
+
+/**
+ * @group semantic-mediawiki-benchmark
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryBenchmarkRunner implements BenchmarkReporter {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var QueryParser
+ */
+ private $queryParser;
+
+ /**
+ * @var Benchmarker
+ */
+ private $benchmarker;
+
+ /**
+ * @var array
+ */
+ private $benchmarkReport = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ * @param QueryParser $queryParser
+ * @param Benchmarker $benchmarker
+ */
+ public function __construct( Store $store, QueryParser $queryParser, Benchmarker $benchmarker ) {
+ $this->store = $store;
+ $this->queryParser = $queryParser;
+ $this->benchmarker = $benchmarker;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array
+ */
+ public function getBenchmarkReport() {
+ return $this->benchmarkReport;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $case
+ */
+ public function run( array $case ) {
+
+ $this->benchmarkReport = [];
+ $this->benchmarker->clear();
+
+ if ( !isset( $case['query'] ) && !is_array( $case['query'] ) ) {
+ throw new RuntimeException( 'Query specification is not available.' );
+ }
+
+ if ( !isset( $case['repetitionCount'] ) ) {
+ throw new RuntimeException( 'repetitionCount is not available.' );
+ }
+
+ $start = microtime( true );
+ $queryReports = [];
+
+ $queryReports['count'] = $this->doQuery(
+ $case, $this->createQuery( $case, Query::MODE_COUNT )
+ );
+
+ $queryReports['instance'] = $this->doQuery(
+ $case, $this->createQuery( $case, Query::MODE_INSTANCES )
+ );
+
+ $this->benchmarkReport = [
+ 'type' => $case['type'],
+ 'note' => $case['query']['condition'] . ( isset( $case['note'] ) ? ' (' . $case['note'] . ')' : '' ),
+ 'query' => $queryReports,
+ 'time' => microtime( true ) - $start
+ ];
+ }
+
+ private function doQuery( array $case, $query ) {
+
+ $this->benchmarker->clear();
+
+ $memoryBefore = memory_get_peak_usage( false );
+
+ for ( $i = 0; $i < $case['repetitionCount']; $i++ ) {
+
+ $start = microtime( true );
+
+ $queryResult = $this->store->getQueryResult( $query );
+
+ $this->benchmarker->addBenchmarkPoint( microtime( true ) - $start );
+ }
+
+ $count = $query->querymode === Query::MODE_COUNT ? $queryResult->getCountValue() : $queryResult->getCount();
+ $columnCount = $queryResult->getColumnCount();
+
+ return [
+ 'rowCount' => $count,
+ 'columnCount' => $columnCount,
+ 'repetitionCount' => $case['repetitionCount'],
+ "memory" => memory_get_peak_usage( false ) - $memoryBefore,
+ "time" => [
+ 'sum' => $this->benchmarker->getSum(),
+ 'mean' => $this->benchmarker->getMean(),
+ 'sd' => $this->benchmarker->getStandardDeviation(),
+ 'norm' => $this->benchmarker->getNormalizedValueBy( $case['repetitionCount'] )
+ ]
+ ];
+ }
+
+ private function createQuery( array $case, $mode, array $printouts = [] ) {
+
+ $description = $this->queryParser->getQueryDescription(
+ $case['query']['condition']
+ );
+
+ foreach ( $case['query']['printouts'] as $printout ) {
+ $property = DIProperty::newFromUserLabel( $printout );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+ }
+
+ $query = new Query(
+ $description
+ );
+
+ $query->setUnboundlimit(
+ isset( $case['query']['parameters']['limit'] ) ? $case['query']['parameters']['limit'] : 500
+ );
+
+ $query->setOffset(
+ isset( $case['query']['parameters']['offset'] ) ? $case['query']['parameters']['offset'] : 0
+ );
+
+ $query->setQueryMode( $mode );
+
+ return $query;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/README.md b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/README.md
new file mode 100644
index 00000000..f19bb949
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/README.md
@@ -0,0 +1,56 @@
+Benchmark tests are to use `PHPUnit` as integration platform and do not always
+represent the best tool for a performance comparison (as it depends on environmental
+factors such as hardware and software constraints which might not be under the
+control of the tester) but it can help to identify performance regressions among
+newly introduced features that run with the same environmental specification.
+
+Benchmarks are not performed in isolation and therefore run in concert with the
+`MediaWiki` infrastructure to determine the overall performance impact during
+execution.
+
+When using `git`, it is relatively easy to run tests and see if a change
+introduces a significant regression or improvement in terms of performance over
+the existing `master` branch by comparing test results of the `master` against
+a `feature` branch.
+
+## Designing benchmark tests
+
+The definition of what benchmarks are executed is specified by a `JSONScript`
+found in the `TestCases` directory. Supported types are:
+
+- `import` to import data from an external source
+- `contentCopy` copy content from an internal source
+- `editCopy` edit content from an internal source
+- `job` running selected jobs
+- `maintenance` running selected maintenance scripts
+- `query` executing `#ask` queries
+
+## Running benchmark tests
+
+Running `composer benchmark` from the Semantic MediaWiki base directory should
+output something similar to what can be seen below.
+
+```
+- mediawiki: "1.28.0-alpha"
+- semantic-mediawiki: "2.5.0-alpha"
+- environment: {"store":"SMWSQLStore3","db":"mysql"}
+- benchmarks
+- 35a205a6fa1db2cda4c484d3007953b3
+ - type: "import"
+ - source: "import-001.xml"
+ - memory: 5564360
+ - time: {"sum":5.9888241}
+- 054543b5702e6fcbccafd00bf6dd27ac
+ - type: "contentCopy"
+ - source: "import-001.xml"
+ - import
+ - memory: 351392
+ - time: {"sum":0.900929}
+ - copy
+ - copyFrom: "Lorem ipsum"
+ - copyCount: 10
+ - memory: 75056
+ - time: {"sum":7.9295225,"mean":0.7929523,"sd":0.1008703,"norm":0.0792952}
+ - time: 8.8402690887451
+
+```
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-001.json
new file mode 100644
index 00000000..f50e75c7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-001.json
@@ -0,0 +1,53 @@
+{
+ "description": "Standard import and rebuild benchmarks",
+ "tests": [
+ {
+ "type": "import",
+ "name": "import-001.xml",
+ "importFrom": "/../Fixtures/import-001.xml"
+ },
+ {
+ "type": "contentCopy",
+ "name": "import-001.xml",
+ "importFrom": "/../Fixtures/import-001.xml",
+ "copyFrom": "Lorem ipsum",
+ "canOverrideCopyCount": true,
+ "copyCount": 1000
+ },
+ {
+ "type": "editCopy",
+ "name": "import-002.xml",
+ "importFrom": "/../Fixtures/import-002.xml",
+ "edit": [
+ "Lorem ipsum",
+ "Lorem donec",
+ "Lorem enim",
+ "Lorem sit",
+ "Lorem tempor"
+ ],
+ "canOverrideEditCount": true,
+ "editRepetitionCount": 50
+ },
+ {
+ "type": "job",
+ "job": "SMW\\RefreshJob",
+ "repetitionCount": 1
+ },
+ {
+ "type": "job",
+ "job": "SMW\\UpdateJob",
+ "repetitionCount": 1
+ },
+ {
+ "type": "maintenance",
+ "script": "rebuildData",
+ "options": {
+ "f": true
+ },
+ "repetitionCount": 1
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-002.json
new file mode 100644
index 00000000..bbcfc186
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/TestCases/b-002.json
@@ -0,0 +1,186 @@
+{
+ "description": "Query related benchmarks",
+ "tests": [
+ {
+ "type": "query",
+ "note": "Select all possible entities for the MAIN namespace",
+ "query": {
+ "condition": "[[:+]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": []
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Select entities for a specific category",
+ "query": {
+ "condition": "[[Category: Lorem ipsum]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": []
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Category and namespace search conjunction",
+ "query": {
+ "condition": "[[Category: Lorem ipsum]] AND [[Property:+]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": []
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Simple property wildcard value search including subobjects",
+ "query": {
+ "condition": "[[Has Url::+]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has Url"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Simple property wildcard value search including subobjects",
+ "query": {
+ "condition": "[[Has quantity::+]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has quantity"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Simple property wildcard value search that does not include subobjects",
+ "query": {
+ "condition": "[[Has Url::+]][[Category: Lorem ipsum]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has Url"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Distinct value search that only includes subobjects",
+ "query": {
+ "condition": "[[Has number::1111]][[Has quantity::25 sqmi]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has number",
+ "Has quantity"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Distinct value disjuntived search that only includes subobjects",
+ "query": {
+ "condition": "[[Has number::1111]] OR [[Has quantity::25 sqmi]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has number",
+ "Has quantity"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Distinct value search that does not include subobjects",
+ "query": {
+ "condition": "[[Has date::1 Jan 2014]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has date"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Wildcard value search that does not include subobjects",
+ "query": {
+ "condition": "[[Has text::~Lorem ipsum dolor*]][[Category: Lorem ipsum]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has text"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Distinct value search on subobject entities",
+ "query": {
+ "condition": "[[Has subobject.Has temperature::100 °F]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has date",
+ "Has quantity",
+ "Has date",
+ "Has Url",
+ "Has annotation uri",
+ "Has wattage",
+ "Has temperature",
+ "Has text"
+ ]
+ },
+ "repetitionCount": 5
+ },
+ {
+ "type": "query",
+ "note": "Complex disjuntived query",
+ "query": {
+ "condition": "<q>[[Has page.Has number::1001]][[Has page.Has telephone number::+1-201-555-0123]]</q> OR [[Has subobject.Has temperature::100 °F]][[Category:!Lorem enim]]",
+ "parameters": {
+ "limit": 500
+ },
+ "printouts": [
+ "Has date",
+ "Has quantity",
+ "Has date",
+ "Has Url",
+ "Has annotation uri",
+ "Has wattage",
+ "Has temperature",
+ "Has text"
+ ]
+ },
+ "repetitionCount": 5
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/phpunit.quick.xml.dist b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/phpunit.quick.xml.dist
new file mode 100644
index 00000000..59f26e7c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Benchmark/phpunit.quick.xml.dist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ bootstrap="../../bootstrap.php"
+ cacheTokens="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ stopOnError="false"
+ stopOnFailure="false"
+ stopOnIncomplete="false"
+ stopOnSkipped="false"
+ beStrictAboutTestsThatDoNotTestAnything="true"
+ verbose="true">
+ <testsuites>
+ <testsuite name="semantic-mediawiki-benchmark">
+ <directory>../../phpunit/Benchmark</directory>
+ </testsuite>
+ </testsuites>
+ <groups>
+ <exclude>
+ <group>semantic-mediawiki-benchmark</group>
+ </exclude>
+ </groups>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">src</directory>
+ <directory suffix=".php">includes</directory>
+ <directory suffix=".php">maintenance</directory>
+ </whitelist>
+ </filter>
+ <php>
+ <var name="smwgSemanticsEnabled" value="true"/>
+ <var name="smwgEnabledFulltextSearch" value="false"/>
+ <var name="benchmarkPageCopyCount" value="200"/>
+ <var name="benchmarkPageEditRepetitionCount" value="2"/>
+ </php>
+</phpunit>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/DatabaseTestCase.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/DatabaseTestCase.php
new file mode 100644
index 00000000..16a5d7cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/DatabaseTestCase.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace SMW\Tests;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\NamespaceExaminer;
+use SMW\PropertyRegistry;
+use SMW\Settings;
+use SMW\StoreFactory;
+use SMW\Tests\Utils\Connection\TestDatabaseTableBuilder;
+use SMWExporter as Exporter;
+
+/**
+ * @group semantic-mediawiki
+ * @group mediawiki-database
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+abstract class DatabaseTestCase extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var TestEnvironment
+ */
+ protected $testEnvironment;
+
+ /**
+ * @var TestDatabaseTableBuilder
+ */
+ protected $testDatabaseTableBuilder;
+
+ /**
+ * @var array|null
+ */
+ protected $databaseToBeExcluded = null;
+
+ /**
+ * @var array|null
+ */
+ protected $storesToBeExcluded = null;
+
+ /**
+ * @var boolean
+ */
+ protected $destroyDatabaseTablesBeforeRun = false;
+
+ /**
+ * @var boolean
+ */
+ protected $destroyDatabaseTablesAfterRun = false;
+
+ /**
+ * @var boolean
+ */
+ protected $isUsableUnitTestDatabase = true;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->addConfiguration( 'smwgEnabledDeferredUpdate', false );
+
+ PropertyRegistry::clear();
+
+ $this->checkIfDatabaseCanBeUsedOtherwiseSkipTest();
+ $this->checkIfStoreCanBeUsedOtherwiseSkipTest();
+
+ ApplicationFactory::getInstance()->registerObject( 'Store', $this->getStore() );
+
+ ApplicationFactory::getInstance()->registerObject(
+ 'Cache',
+ ApplicationFactory::getInstance()->newCacheFactory()->newFixedInMemoryCache()
+ );
+
+ // Avoid surprise on revisions etc.
+ // @see MediaWikiTestCase::doLightweightServiceReset
+ $this->testEnvironment->resetMediaWikiService( 'MainObjectStash' );
+ $this->testEnvironment->resetMediaWikiService( 'LocalServerObjectCache' );
+ $this->testEnvironment->resetMediaWikiService( 'MainWANObjectCache' );
+
+ $this->testEnvironment->clearPendingDeferredUpdates();
+ }
+
+ protected function tearDown() {
+
+ // If setUp is skipped early this might not be initialized
+ if ( $this->testEnvironment !== null ) {
+ $this->testEnvironment->tearDown();
+ }
+
+ ApplicationFactory::clear();
+ NamespaceExaminer::clear();
+ PropertyRegistry::clear();
+ Settings::clear();
+ Exporter::getInstance()->clear();
+
+ parent::tearDown();
+ }
+
+ /**
+ * It is assumed that each test that makes use of the TestCase is requesting
+ * a "real" DB connection
+ *
+ * By default, the database tables are being re-used but it is possible to
+ * request a trear down so that the next test can rebuild the tables from
+ * scratch
+ */
+ public function run( \PHPUnit_Framework_TestResult $result = null ) {
+
+ $this->getStore()->clear();
+
+ $this->testDatabaseTableBuilder = TestDatabaseTableBuilder::getInstance(
+ $this->getStore()
+ );
+
+ $this->testDatabaseTableBuilder->removeAvailableDatabaseType(
+ $this->databaseToBeExcluded
+ );
+
+ $this->destroyDatabaseTables( $this->destroyDatabaseTablesBeforeRun );
+
+ try {
+ $this->testDatabaseTableBuilder->doBuild();
+ } catch ( RuntimeException $e ) {
+ $this->isUsableUnitTestDatabase = false;
+ }
+
+ parent::run( $result );
+
+ $this->destroyDatabaseTables( $this->destroyDatabaseTablesAfterRun );
+ }
+
+ protected function removeDatabaseTypeFromTest( $databaseToBeExcluded ) {
+ $this->databaseToBeExcluded = $databaseToBeExcluded;
+ }
+
+ protected function destroyDatabaseTablesAfterRun() {
+ $this->destroyDatabaseTablesAfterRun = true;
+ }
+
+ protected function getStore() {
+ return StoreFactory::getStore();
+ }
+
+ protected function setStoresToBeExcluded( array $storesToBeExcluded ) {
+ return $this->storesToBeExcluded = $storesToBeExcluded;
+ }
+
+ protected function skipTestForMediaWikiVersionLowerThan( $version, $message = '' ) {
+
+ if ( $message === '' ) {
+ $message = "This test is skipped for MediaWiki version {$GLOBALS['wgVersion']}";
+ }
+
+ if ( version_compare( $GLOBALS['wgVersion'], $version, '<' ) ) {
+ $this->markTestSkipped( $message );
+ }
+ }
+
+ protected function skipTestForDatabase( $excludedDatabase, $message = '' ) {
+
+ if ( is_string( $excludedDatabase ) ) {
+ $excludedDatabase = [ $excludedDatabase ];
+ }
+
+ if ( $message === '' ) {
+ $message = "Database was excluded and is not expected to support this test";
+ }
+
+ if ( in_array( $this->getDBConnection()->getType(), $excludedDatabase ) ) {
+ $this->markTestSkipped( $message );
+ }
+ }
+
+ protected function skipTestForStore( $excludeStore ) {
+
+ $store = get_class( $this->getStore() );
+
+ if ( $store == $excludeStore ) {
+ $this->markTestSkipped(
+ "{$store} was excluded and is not expected to support the test"
+ );
+ }
+ }
+
+ protected function getDBConnection() {
+ return $this->testDatabaseTableBuilder->getDBConnection();
+ }
+
+ protected function getConnectionProvider() {
+ return $this->testDatabaseTableBuilder->getConnectionProvider();
+ }
+
+ protected function isUsableUnitTestDatabase() {
+ return $this->isUsableUnitTestDatabase;
+ }
+
+ protected function checkIfDatabaseCanBeUsedOtherwiseSkipTest() {
+
+ if ( !$this->isUsableUnitTestDatabase ) {
+ $this->markTestSkipped(
+ "Database was excluded and is not expected to support the test"
+ );
+ }
+ }
+
+ protected function checkIfStoreCanBeUsedOtherwiseSkipTest() {
+
+ $store = get_class( $this->getStore() );
+
+ if ( in_array( $store, (array)$this->storesToBeExcluded ) ) {
+ $this->markTestSkipped(
+ "{$store} was excluded and is not expected to support the test"
+ );
+ }
+ }
+
+ private function destroyDatabaseTables( $destroyDatabaseTables ) {
+
+ if ( $this->isUsableUnitTestDatabase && $destroyDatabaseTables ) {
+ try {
+ $this->testDatabaseTableBuilder->doDestroy();
+ } catch ( \Exception $e ) { // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.CodeAnalysis.EmptyStatement
+ // Do nothing because an instance was not available
+ } // @codingStandardsIgnoreEnd
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/ExecutionTimeTestListener.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/ExecutionTimeTestListener.php
new file mode 100644
index 00000000..6d541b28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/ExecutionTimeTestListener.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests;
+
+use Exception;
+use PHPUnit_Framework_AssertionFailedError;
+use PHPUnit_Framework_Test;
+use PHPUnit_Framework_TestListener;
+use PHPUnit_Framework_TestSuite;
+use PHPUnit_Framework_Warning;
+
+class ExecutionTimeTestListener implements PHPUnit_Framework_TestListener {
+
+ protected $testCollector = [];
+ protected $executionTimeThresholdInSeconds = 10;
+ protected $isEnabledToListen = true;
+
+ public function __construct( $isEnabledToListen, $executionTimeThresholdInSeconds ) {
+ $this->isEnabledToListen = $isEnabledToListen;
+ $this->executionTimeThresholdInSeconds = $executionTimeThresholdInSeconds;
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::startTest
+ */
+ public function startTest( PHPUnit_Framework_Test $test ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::endTest
+ */
+ public function endTest( PHPUnit_Framework_Test $test, $length ) {
+ if ( $this->isEnabledToListen && ( $length > $this->executionTimeThresholdInSeconds ) ) {
+ $this->testCollector[$test->getName()] = round( $length, 3 );
+ }
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addError
+ */
+ public function addError( PHPUnit_Framework_Test $test, Exception $e, $time ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addError
+ */
+ public function addWarning( PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) {
+
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addFailure
+ */
+ public function addFailure( PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addError
+ */
+ public function addIncompleteTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addRiskyTest
+ * @since 4.0.0
+ */
+ public function addRiskyTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::addSkippedTest
+ */
+ public function addSkippedTest( PHPUnit_Framework_Test $test, Exception $e, $time ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::startTestSuite
+ */
+ public function startTestSuite( PHPUnit_Framework_TestSuite $suite ) {
+ }
+
+ /**
+ * @see PHPUnit_Framework_TestListener::endTestSuite
+ */
+ public function endTestSuite( PHPUnit_Framework_TestSuite $suite ) {
+ foreach ( $this->testCollector as $name => $length ) {
+ print ( "\n" . $suite->getName() . " {$name} ran for {$length} seconds" . "\n" );
+ unset( $this->testCollector[$name] );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/EncodingIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/EncodingIntegrationTest.php
new file mode 100644
index 00000000..34cdb918
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/EncodingIntegrationTest.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Hooks\BaseTemplateToolbox;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\BaseTemplateToolbox
+ * @covers \SMWInfolink
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class EncodingIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider baseTemplateToolboxDataProvider
+ */
+ public function testBaseTemplateToolboxURLEncoding( $setup, $expected ) {
+
+ $toolbox = [];
+
+ foreach ( $setup['settings'] as $key => $value) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ $instance = new BaseTemplateToolbox(
+ ApplicationFactory::getInstance()->getNamespaceExaminer()
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgBrowseFeatures' => $setup['settings']['smwgBrowseFeatures']
+ ]
+ );
+
+ $instance->process( $setup['skinTemplate'], $toolbox );
+
+ $this->assertContains(
+ $expected,
+ $toolbox['smw-browse']['href']
+ );
+
+ ApplicationFactory::clear();
+ }
+
+ public function baseTemplateToolboxDataProvider() {
+
+ $specialName = str_replace( '%3A', ':',
+ \SMW\Encoder::encode( \SpecialPage::getTitleFor( 'Browse' )->getPrefixedText() )
+ );
+
+ $provider = [];
+
+ $provider[] = [ $this->newBaseTemplateToolboxSetup( '2013/11/05' ), "$specialName/:2013-2F11-2F05" ];
+ $provider[] = [ $this->newBaseTemplateToolboxSetup( '2013-06-30' ), "$specialName/:2013-2D06-2D30" ];
+ $provider[] = [ $this->newBaseTemplateToolboxSetup( '2013$06&30' ), "$specialName/:2013-2406-2630" ];
+ $provider[] = [ $this->newBaseTemplateToolboxSetup( '2013\Foo' ), "$specialName/:2013-5CFoo" ];
+
+ return $provider;
+ }
+
+ private function newBaseTemplateToolboxSetup( $text ) {
+
+ $settings = [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgBrowseFeatures' => SMW_BROWSE_TLINK
+ ];
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( Title::newFromText( $text, NS_MAIN ) ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'msg' )
+ ->will( $this->returnValue( $message ) );
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $skin ) );
+
+ $skinTemplate->data['isarticle'] = true;
+
+ return [ 'settings' => $settings, 'skinTemplate' => $skinTemplate ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/content.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/content.json
new file mode 100644
index 00000000..9dd4dc68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/content.json
@@ -0,0 +1,26 @@
+{
+ "description": "Test import",
+ "import": [
+ {
+ "page": "Smw import foaf",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "foaf.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Foaf:knows",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": "* [[Imported from::foaf:knows]]",
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/foaf.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/foaf.txt
new file mode 100644
index 00000000..15106fc0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidTextContent/foaf.txt
@@ -0,0 +1,13 @@
+http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]
+ name|Type:Text
+ homepage|Type:URL
+ mbox|Type:Email
+ mbox_sha1sum|Type:Text
+ depiction|Type:URL
+ phone|Type:Text
+ Person|Category
+ Organization|Category
+ knows|Type:Page
+ member|Type:Page
+
+[[Category:Imported vocabulary]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/content.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/content.json
new file mode 100644
index 00000000..932c3866
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/content.json
@@ -0,0 +1,14 @@
+{
+ "description": "Test import",
+ "import": [
+ {
+ "description": "Xml import",
+ "contents": {
+ "importFrom": "test.xml"
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/test.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/test.xml
new file mode 100644
index 00000000..69da5694
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/Fixtures/ValidXmlContent/test.xml
@@ -0,0 +1,68 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.8/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.8/ http://www.mediawiki.org/xml/export-0.8.xsd" version="0.8" xml:lang="en">
+ <siteinfo>
+ <sitename>test</sitename>
+ <base>http://localhost:8080/test/index.php/Main_Page</base>
+ <generator>MediaWiki 1.23.9</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">test</namespace>
+ <namespace key="5" case="first-letter">test talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:ImportTest</title>
+ <ns>102</ns>
+ <id>4119</id>
+ <revision>
+ <id>10260</id>
+ <timestamp>2000-03-29T10:48:42Z</timestamp>
+ <contributor>
+ <username>Importer</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Number]]&quot;</comment>
+ <text xml:space="preserve" bytes="20">[[Has type::Number]]</text>
+ <sha1>kph48zlgloy5jxc3qmxgjzz2179e04c</sha1>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ </revision>
+ </page>
+ <page>
+ <title>ImportTest</title>
+ <ns>0</ns>
+ <id>4118</id>
+ <revision>
+ <id>10259</id>
+ <timestamp>2000-03-29T10:48:21Z</timestamp>
+ <contributor>
+ <username>Importer</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[ImportTest::123]]&quot;</comment>
+ <text xml:space="preserve" bytes="19">[[ImportTest::123]]</text>
+ <sha1>az827f6th4x9hr9udej643f9p13akgf</sha1>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/ImporterIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/ImporterIntegrationTest.php
new file mode 100644
index 00000000..635abc8f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Importer/ImporterIntegrationTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Tests\Integration\Importer;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\MwDBaseUnitTestCase;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ImporterIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $spyMessageReporter;
+ private $importerServiceFactory;
+ private $stringValidator;
+ private $fixtures;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+ $this->fixtures = __DIR__ . '/Fixtures';
+
+ $this->importerServiceFactory = ApplicationFactory::getInstance()->create( 'ImporterServiceFactory' );
+ $this->spyMessageReporter = $utilityFactory->newSpyMessageReporter();
+ $this->stringValidator = $utilityFactory->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testValidTextContent() {
+
+ $importer = $this->importerServiceFactory->newImporter(
+ $this->importerServiceFactory->newJsonContentIterator( [ $this->fixtures . '/ValidTextContent' ] )
+ );
+
+ $importer->setMessageReporter( $this->spyMessageReporter );
+ $importer->setReqVersion( 1 );
+
+ $importer->doImport();
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ 'Smw import foaf',
+ 'Foaf:knows'
+ ],
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testValidXmlContent() {
+
+ if ( !interface_exists( '\ImportSource' ) ) {
+ $this->markTestSkipped( "ImportSource interface is unknown (MW 1.25-)" );
+ }
+
+ $importer = $this->importerServiceFactory->newImporter(
+ $this->importerServiceFactory->newJsonContentIterator( [ $this->fixtures . '/ValidXmlContent' ] )
+ );
+
+ $importer->setMessageReporter( $this->spyMessageReporter );
+ $importer->setReqVersion( 1 );
+
+ $importer->doImport();
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ 'ImportTest'
+ ],
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/InterwikiDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/InterwikiDBIntegrationTest.php
new file mode 100644
index 00000000..d7c94f39
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/InterwikiDBIntegrationTest.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWExportController as ExportController;
+use SMWQuery as Query;
+use SMWRDFXMLSerializer as RDFXMLSerializer;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class InterwikiDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $stringValidator;
+ private $subjects = [];
+
+ private $pageCreator;
+ private $stringBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->semanticDataFactory = $utilityFactory->newSemanticDataFactory();
+ $this->stringValidator = $utilityFactory->newValidatorFactory()->newStringValidator();
+
+ $this->pageCreator = $utilityFactory->newPageCreator();
+ $this->stringBuilder = $utilityFactory->newStringBuilder();
+
+ $this->queryResultValidator = $utilityFactory->newValidatorFactory()->newQueryResultValidator();
+ $this->queryParser = ApplicationFactory::getInstance()->newQueryParser();
+
+ // Manipulate the interwiki prefix on-the-fly
+ $GLOBALS['wgHooks']['InterwikiLoadPrefix'][] = function( $prefix, &$interwiki ) {
+
+ if ( $prefix !== 'iw-test' ) {
+ return true;
+ }
+
+ $interwiki = [
+ 'iw_prefix' => 'iw-test',
+ 'iw_url' => 'http://www.example.org/$1',
+ 'iw_api' => false,
+ 'iw_wikiid' => 'foo',
+ 'iw_local' => true,
+ 'iw_trans' => false,
+ ];
+
+ return false;
+ };
+ }
+
+ protected function tearDown() {
+
+ UtilityFactory::getInstance()->newPageDeleter()->doDeletePoolOfPages( $this->subjects );
+ unset( $GLOBALS['wgHooks']['InterwikiLoadPrefix'] );
+
+ parent::tearDown();
+ }
+
+ public function testRdfSerializationForInterwikiAnnotation() {
+
+ $this->stringBuilder
+ ->addString( '[[Has type::Page]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Use for interwiki annotation', SMW_NS_PROPERTY ) )
+ ->doEdit( $this->stringBuilder->getString() );
+
+ $this->stringBuilder
+ ->addString( '[[Use for interwiki annotation::Interwiki link]]' )
+ ->addString( '[[Use for interwiki annotation::iw-test:Interwiki link]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ ) )
+ ->doEdit( $this->stringBuilder->getString() );
+
+ $output = $this->fetchSerializedRdfOutputFor(
+ [ __METHOD__ ]
+ );
+
+ $expectedOutputContent = [
+ '<property:Use_for_interwiki_annotation rdf:resource="&wiki;Interwiki_link"/>',
+ '<property:Use_for_interwiki_annotation rdf:resource="&wiki;iw-2Dtest-3AInterwiki_link"/>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expectedOutputContent,
+ $output
+ );
+ }
+
+ public function testQueryForInterwikiAnnotation() {
+
+ $this->stringBuilder
+ ->addString( '[[Has type::Page]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Use for interwiki annotation', SMW_NS_PROPERTY ) )
+ ->doEdit( $this->stringBuilder->getString() );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ . '-1' ) )
+ ->doEdit( '[[Use for interwiki annotation::Interwiki link]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ . '-2' ) )
+ ->doEdit( '[[Use for interwiki annotation::iw-test:Interwiki link]]' );
+
+ $this->stringBuilder
+ ->addString( '[[Use for interwiki annotation::iw-test:Interwiki link]]' );
+
+ $description = $this->queryParser->getQueryDescription( $this->stringBuilder->getString() );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+ $query->setLimit( 10 );
+
+ // Expects only one result with an interwiki being used as differentiator
+ $this->subjects[] = DIWikiPage::newFromTitle(Title::newFromText( __METHOD__ . '-2' ) );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $this->subjects,
+ $this->getStore()->getQueryResult( $query )
+ );
+
+ $this->subjects[] = DIWikiPage::newFromTitle(Title::newFromText( __METHOD__ . '-1' ) );
+ }
+
+ private function fetchSerializedRdfOutputFor( array $pages ) {
+
+ $this->subjects = $pages;
+
+ $instance = new ExportController( new RDFXMLSerializer() );
+
+ ob_start();
+ $instance->printPages( $pages );
+ return ob_get_clean();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ApiTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ApiTestCaseProcessor.php
new file mode 100644
index 00000000..6ecc4c7c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ApiTestCaseProcessor.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMW\Tests\Utils\File\ContentsReader;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ApiTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var MwApiFactory
+ */
+ private $mwApiFactory;
+
+ /**
+ * @var StringValidator
+ */
+ private $stringValidator;
+
+ /**
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * @param MwApiFactory mwApiFactory
+ * @param StringValidator
+ */
+ public function __construct( $mwApiFactory, $stringValidator ) {
+ $this->mwApiFactory = $mwApiFactory;
+ $this->stringValidator = $stringValidator;
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function setDebugMode( $debugMode ) {
+ $this->debug = $debugMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $testCaseLocation
+ */
+ public function setTestCaseLocation( $testCaseLocation ) {
+ $this->testCaseLocation = $testCaseLocation;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $case
+ */
+ public function process( array $case ) {
+
+ if ( !isset( $case['api'] ) ) {
+ return;
+ }
+
+ $parameters = [];
+
+ if ( isset( $case['api']['parameters'] ) ) {
+ $parameters = $case['api']['parameters'];
+ }
+
+ $res = $this->mwApiFactory->doApiRequest(
+ $parameters
+ );
+
+ $this->assertOutputForCase( $case, json_encode( $res ) );
+ }
+
+ private function assertOutputForCase( $case, $text ) {
+
+ // Avoid issue with \r carriage return and \n new line
+ $text = str_replace( "\r\n", "\n", $text );
+
+ if ( isset( $case['assert-output']['to-contain'] ) ) {
+
+ if ( isset( $case['assert-output']['to-contain']['contents-file'] ) ) {
+ $contents = ContentsReader::readContentsFrom(
+ $this->testCaseLocation . $case['assert-output']['to-contain']['contents-file']
+ );
+ } else {
+ $contents = $case['assert-output']['to-contain'];
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $contents,
+ $text,
+ $case['about']
+ );
+ }
+
+ if ( isset( $case['assert-output']['not-contain'] ) ) {
+
+ if ( isset( $case['assert-output']['not-contain']['contents-file'] ) ) {
+ $contents = ContentsReader::readContentsFrom(
+ $this->testCaseLocation . $case['assert-output']['not-contain']['contents-file']
+ );
+ } else {
+ $contents = $case['assert-output']['not-contain'];
+ }
+
+ $this->stringValidator->assertThatStringNotContains(
+ $contents,
+ $text,
+ $case['about']
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/P106.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/P106.txt
new file mode 100644
index 00000000..cdb33450
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/P106.txt
@@ -0,0 +1,24 @@
+* Has type: [[Has type::Text]]
+
+== Preferred property labels ==
+* [[Has preferred property label::occupation@en]]
+* [[Has preferred property label::Tätigkeit@de]]
+* [[Has preferred property label::occupation@fr]]
+* [[Has preferred property label::ocupación@es]]
+* [[Has preferred property label::род занÑтий@ru]]
+* [[Has preferred property label::occupazione@it]]
+* [[Has preferred property label::èŒä¸š@zh-hans]]
+* [[Has preferred property label::è·æ¥­@ja]]
+
+<!-- Causes error as it is already defined above -->
+* [[Has preferred property label::è·ç¨®@ja]]
+
+== Property descriptions ==
+* [[Has property description::occupation of a person; see also field of work (Property:P101)@en]]
+* [[Has property description::Beruf oder andere Tätigkeit einer Person (ergänzt durch Arbeitsgebiet: Property:P101)@de]]
+* [[Has property description::actividad laboral u otra ocupación de la persona; véase también campo de trabajo (propiedad 101)@es]]
+* [[Has property description::qualité d'une personne (métier, hobby...), voir aussi domaine d'activité (Property:P101) et fonction (Property:P39)@fr]]
+* [[Has property description::attività lavorativa svolta, vedi anche campo di lavoro (Property: P101)@it]]
+* [[Has property description::профеÑÑÐ¸Ñ Ð¿ÐµÑ€Ñоны; Ñм. также ÑвойÑтво облаÑÑ‚ÑŒ деÑтельноÑти (Property:P101)@ru]]
+* [[Has property description::èŒä¸šå³èŒåœºä¸Šçš„专门行业,是对劳动的分类。@zh-hans]]
+* [[Has property description::人物ã®è·æ¥­ã€‚「専門分野ã€(Property:P101) ã‚‚å‚ç…§@ja]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.0.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.0.txt
new file mode 100644
index 00000000..49e8202d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.0.txt
@@ -0,0 +1 @@
+{"query":{"API_test_property":{"label":"API test property","key":"API_test_property","description":{"en":""},"prefLabel":{"en":""}}},"query-continue-offset":0,"version":1,"meta":{"type":"property","limit":10,"count":1,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.1.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.1.txt
new file mode 100644
index 00000000..d227be4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.1.txt
@@ -0,0 +1 @@
+{"query":{"API_test_concept":{"label":"API test concept","key":"API_test_concept"}},"query-continue-offset":0,"version":1,"meta":{"type":"concept","limit":10,"count":1,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.2.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.2.txt
new file mode 100644
index 00000000..ab4548f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.2.txt
@@ -0,0 +1 @@
+{"query":{"API_test_category":{"label":"API test category","key":"API_test_category"}},"query-continue-offset":0,"version":1,"meta":{"type":"category","limit":10,"count":1,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.3.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.3.txt
new file mode 100644
index 00000000..e5a1154b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.3.txt
@@ -0,0 +1 @@
+{"query":{"API_test_category#14":{"label":"API test category","key":"API_test_category","ns":"14"},"API_test_concept#108":{"label":"API test concept","key":"API_test_concept","ns":"108"},"API_test_property#102":{"label":"API test property","key":"API_test_property","ns":"102"}},"query-continue-offset":0,"version":1,"meta":{"type":"article","limit":10,"count":3,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.4.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.4.txt
new file mode 100644
index 00000000..ce7d3398
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.4.txt
@@ -0,0 +1 @@
+{"query":["text 1","text 2"],"query-continue-offset":0,"version":1,"meta":{"type":"pvalue","limit":10,"count":2,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.5.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.5.txt
new file mode 100644
index 00000000..7e2c00bb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.5.txt
@@ -0,0 +1 @@
+{"query":["Page 1","Page 2"],"query-continue-offset":0,"version":1,"meta":{"type":"pvalue","limit":10,"count":2,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.6.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.6.txt
new file mode 100644
index 00000000..9e704817
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/a-0001.6.txt
@@ -0,0 +1 @@
+{"query":["1 January 1970","2 January 1971"],"query-continue-offset":0,"version":1,"meta":{"type":"pvalue","limit":10,"count":2,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/file-upload.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/file-upload.txt
new file mode 100644
index 00000000..e3d33a13
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/file-upload.txt
@@ -0,0 +1 @@
+Test file for a non image upload! \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-480.png b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-480.png
new file mode 100644
index 00000000..4353df8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-480.png
Binary files differ
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-88.png b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-88.png
new file mode 100644
index 00000000..937d6493
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/image-upload-88.png
Binary files differ
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/numeric-sorting.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/numeric-sorting.txt
new file mode 100644
index 00000000..ec97f2cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/numeric-sorting.txt
@@ -0,0 +1,20 @@
+https://phabricator.wikimedia.org/T8948
+{{#subobject:
+ |Has page=Antonov An-2
+ |@category=Numeric sorting example
+}}{{#subobject:
+ |Has page=Antonov An-218
+ |@category=Numeric sorting example
+}}{{#subobject:
+ |Has page=Antonov An-22
+ |@category=Numeric sorting example
+}}{{#subobject:
+ |Has page=Antonov An-225
+ |@category=Numeric sorting example
+}}{{#subobject:
+ |Has page=Antonov An-24
+ |@category=Numeric sorting example
+}}{{#subobject:
+ |Has page=Antonov An-3
+ |@category=Numeric sorting example
+}}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.1.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.1.txt
new file mode 100644
index 00000000..f1fc0607
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.1.txt
@@ -0,0 +1,20 @@
+@source: http://www.simile-widgets.org/exhibit3/examples/other-versions/HEAD/icd/icd10-infectious.json
+
+{{#subobject:
+ |@category=P0211
+ |@json={
+ "kind": "chapter",
+ "code": "I",
+ "exclusion": "carrier or suspected carrier of infectious diseaseZ22.-",
+ "icdId": "http://id.who.int/icd/icd10/I",
+ "inclusion": "diseases generally recognized as communicable or transmissible",
+ "label": "Certain infectious and parasitic diseases",
+ "link": "http://apps.who.int/classifications/icd10/browse/2010/en#/I",
+ "subclasses": [
+ "http://id.who.int/icd/icd10/A00-A09",
+ "http://id.who.int/icd/icd10/A15-A19",
+ "http://id.who.int/icd/icd10/A20-A28"
+ ],
+ "id": "http://id.who.int/icd/icd10/I"
+}
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.2.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.2.txt
new file mode 100644
index 00000000..4bb0aad5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.2.txt
@@ -0,0 +1,27 @@
+@source: http://www.simile-widgets.org/exhibit3/examples/other-versions/HEAD/icd/icd10-infectious.json
+
+{{#set:
+ |@json={
+ "kind": "block",
+ "code": "A00-A09",
+ "icdId": "http://id.who.int/icd/icd10/A00-A09",
+ "label": "Intestinal infectious diseases",
+ "link": "http://apps.who.int/classifications/icd10/browse/2010/en#/A00-A09",
+ "superclasses": [
+ "http://id.who.int/icd/icd10/I"
+ ],
+ "subclasses": [
+ "http://id.who.int/icd/icd10/A00",
+ "http://id.who.int/icd/icd10/A01",
+ "http://id.who.int/icd/icd10/A02",
+ "http://id.who.int/icd/icd10/A03",
+ "http://id.who.int/icd/icd10/A04",
+ "http://id.who.int/icd/icd10/A05",
+ "http://id.who.int/icd/icd10/A06",
+ "http://id.who.int/icd/icd10/A07",
+ "http://id.who.int/icd/icd10/A08",
+ "http://id.who.int/icd/icd10/A09"
+ ],
+ "id": "http://id.who.int/icd/icd10/A00-A09"
+}
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.3.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.3.txt
new file mode 100644
index 00000000..b728ce6b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0211.3.txt
@@ -0,0 +1,11 @@
+3+ depth is not permitted
+
+{{#set:
+ |@json={
+ "foo": {
+ "kind": {
+ "kind": "bar"
+ }
+ }
+}
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0303.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0303.txt
new file mode 100644
index 00000000..267b4374
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0303.txt
@@ -0,0 +1,7 @@
+{{#set: Has effect=Foo,
+Bar |+sep=,
+}}
+
+{{#subobject: Has effect=Foo,
+Bar |+sep=,
+}}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.de.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.de.txt
new file mode 100644
index 00000000..5625a2e4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.de.txt
@@ -0,0 +1,11 @@
+@source http://www.gutenberg.org/files/34811/34811-h/34811-h.htm
+
+Title: Buddenbrooks - Verfall einer Familie
+
+Author: Thomas Mann
+
+Release Date: January 1, 2011 [EBook #34811]
+
+Language: German
+
+[[Has text::Und die kleine Antonie, achtjährig und zartgebaut, in einem Kleidchen aus ganz leichter changierender Seide, den hübschen Blondkopf ein wenig vom Gesichte des Großvaters abgewandt, blickte aus ihren graublauen Augen angestrengt nachdenkend und ohne etwas zu sehen ins Zimmer hinein, wiederholte noch einmal: »Was ist das«, sprach darauf langsam: »Ich glaube, daß mich Gott«, fügte, während ihr Gesicht sich aufklärte, rasch hinzu: »– geschaffen hat samt allen Kreaturen« ...]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.fr.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.fr.txt
new file mode 100644
index 00000000..96877361
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0439.fr.txt
@@ -0,0 +1,13 @@
+@source http://www.gutenberg.org/cache/epub/22768/pg22768-images.html
+
+Title: L'enfer (1 of 2) La Divine Comédie - Traduit par Rivarol
+
+Author: Dante Alighieri
+
+Translator: Antoine Rivarol (de)
+
+Release Date: September 26, 2007 [EBook #22768]
+
+Language: French
+
+[[Has text::Dès les premières heures de notre publication, nous avons annoncé le chef-d'oeuvre du poëte florentin comme devant figurer en première ligne parmi les joyaux de notre modeste écrin. Nous avons voulu, au début, donner accès à tous les ouvrages consacrés par le temps et par l'admiration universelle. Un succès constant pendant quatre longues et parfois difficiles années, nous a prouvé que nous nous étions très rarement trompé sur ...]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0444.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0444.txt
new file mode 100644
index 00000000..bce2c2dc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0444.txt
@@ -0,0 +1 @@
+[[Has text::[[Lorem ipsum]] dolor sit amet, [[Has page::consectetur adipiscing elit]]. In [http://example.org/eleifend eleifend] cursus dignissim. Suspendisse potenti. [[Has page::Curabitur aliquet|Bar]] malesuada ex sed efficitur. [[Nullam id ante|Foo]] eget lacus rutrum tincidunt eu et lacus. Aliquam sed consequat orci. Nulla sodales neque magna, nec sollicitudin erat aliquam ut. Sed sed vulputate tortor, in [[vestibulum nibh]] [[Has number::42]] [[Has date::01-01-2020]]]].
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0459-keyword-formatter-schema.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0459-keyword-formatter-schema.json
new file mode 100644
index 00000000..a4dda206
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0459-keyword-formatter-schema.json
@@ -0,0 +1,15 @@
+{
+ "description": "Specifies a formatting schema for a keyword type",
+ "type": "LINK_FORMAT_SCHEMA",
+ "rule": {
+ "link_to": "SPECIAL_ASK",
+ "parameters": {
+ "format": "list"
+ }
+ },
+ "tags": [
+ "formatter",
+ "link formatter",
+ "keyword"
+ ]
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-error.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-error.txt
new file mode 100644
index 00000000..148edfc8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-error.txt
@@ -0,0 +1,2 @@
+* Foo
+* Bar
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-other-license.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-other-license.json
new file mode 100644
index 00000000..cffd0c7d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502-other-license.json
@@ -0,0 +1,4 @@
+{
+ "CC Zero": "Public Domain Dedication",
+ "PD": "Public Domain"
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502.txt
new file mode 100644
index 00000000..fc84f4af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/p-0502.txt
@@ -0,0 +1,49 @@
+[https://www.cs.umd.edu/projects/plus/SHOE/onts/docmnt1.0.html Models documents of all kinds, but focuses on publications]
+
+* Communication|Communication
+** Document
+*** Abstract
+*** Comment
+*** Correspondence
+**** Discussion
+**** Email
+**** Letter
+**** Postcard
+*** Form
+*** Guideline
+*** Homepage
+**** OrganizationHomepage
+**** PersonalHomepage
+*** Index
+*** Lecture
+*** Manuscript
+*** Minutes
+*** Preprint
+*** Promotion
+*** Publication
+**** Advertisement
+**** Article
+***** BookArticle
+***** ConferencePaper
+***** JournalArticle
+***** WorkshopPaper
+**** Book
+**** Dictionary
+**** Editorial
+**** Manual
+**** Periodical
+***** Journal
+***** Magazine
+***** Newsletter
+***** Newspaper
+**** Proceedings
+**** Regulation
+**** Specification
+**** TechnicalReport
+**** Thesis
+***** DoctoralThesis
+***** MastersThesis
+**** Review
+** PhoneCall
+** Software
+** Speech
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.friends.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.friends.txt
new file mode 100644
index 00000000..3d712e8d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.friends.txt
@@ -0,0 +1,109 @@
+<pre>
+Andy Mars,USA,business
+Andy Mars,Prague,both
+Andy Mars,Japan,pleasure
+John Maier,Prague,both
+John Maier,USA,both
+John Maier,Greece,pleasure
+Yan Chow,Canada,pleasure
+Yan Chow,USA,business
+Yan Chow,China,business
+Mike Bloom,Amsterdam,both
+Mike Bloom,USA,both
+Charles Banning,USA,pleasure
+Charles Banning,Greece,pleasure
+Charles Banning,Andora,pleasure
+Kevin Mikaelson,USA,both
+Kevin Mikaelson,Japan,business
+Kevin Mikaelson,Andora,pleasure
+Charlotte Beans,Prague,pleasure
+Charlotte Beans,USA,both
+Charlotte Beans,Kazakhstan,pleasure
+Yumi Tanaka,USA,both
+Yumi Tanaka,Greece,pleasure
+Yumi Tanaka,Amsterdam,pleasure
+Rosalia Alvarez,USA,pleasure
+Rosalia Alvarez,Canada,pleasure
+Rosalia Alvarez,China,pleasure
+Mandy Rose,Amsterdam,pleasure
+Mandy Rose,Russia,pleasure
+James Ross,Amsterdam,business
+Evelyne Lynn,Germany,business
+Evelyne Lynn,Greece,pleasure
+Evelyne Lynn,Italy,pleasure
+Michael Chester,Amsterdam,pleasure
+Michael Chester,Germany,pleasure
+Michael Chester,Thailand,pleasure
+</pre>
+
+{{#subobject:
+ |Person=Andy Mars |Place visited=USA |Type of visit=business
+}}{{#subobject:
+ |Person=Andy Mars |Place visited=Prague |Type of visit=both
+}}{{#subobject:
+ |Person=Andy Mars |Place visited=Japan |Type of visit=pleasure
+}}{{#subobject:
+ |Person=John Maier |Place visited=Prague |Type of visit=both
+}}{{#subobject:
+ |Person=John Maier |Place visited=USA |Type of visit=both
+}}{{#subobject:
+ |Person=John Maier |Place visited=Greece |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Yan Chow |Place visited=Canada |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Yan Chow |Place visited=,USA |Type of visit=business
+}}{{#subobject:
+ |Person=Yan Chow |Place visited=China |Type of visit=business
+}}{{#subobject:
+ |Person=Mike Bloom |Place visited=Amsterdam |Type of visit=both
+}}{{#subobject:
+ |Person=Mike Bloom |Place visited=USA |Type of visit=both
+}}{{#subobject:
+ |Person=Charles Banning |Place visited=USA |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Charles Banning |Place visited=,Greece |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Charles Banning |Place visited=Andora |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Kevin Mikaelson |Place visited=USA |Type of visit=both
+}}{{#subobject:
+ |Person=Kevin Mikaelson |Place visited=Japan |Type of visit=business
+}}{{#subobject:
+ |Person=Kevin Mikaelson |Place visited=Andora |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Charlotte Beans |Place visited=Prague |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Charlotte Beans |Place visited=USA |Type of visit=both
+}}{{#subobject:
+ |Person=Charlotte Beans |Place visited=Kazakhstan |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Yumi Tanaka |Place visited=USA |Type of visit=both
+}}{{#subobject:
+ |Person=Yumi Tanaka |Place visited=Greece |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Yumi Tanaka |Place visited=Amsterdam |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Rosalia Alvarez |Place visited=USA |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Rosalia Alvarez |Place visited=Canada |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Rosalia Alvarez |Place visited=China |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Mandy Rose |Place visited=Amsterdam |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Mandy Rose |Place visited=Russia |Type of visit=pleasure
+}}{{#subobject:
+ |Person=James Ross |Place visited=Amsterdam |Type of visit=business
+}}{{#subobject:
+ |Person=Evelyne Lynn |Place visited=Germany |Type of visit=business
+}}{{#subobject:
+ |Person=Evelyne Lynn |Place visited=Greece |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Evelyne Lynn |Place visited=Italy |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Michael Chester |Place visited=Amsterdam |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Michael Chester |Place visited=Germany |Type of visit=pleasure
+}}{{#subobject:
+ |Person=Michael Chester |Place visited=Thailand |Type of visit=pleasure
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.persons.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.persons.txt
new file mode 100644
index 00000000..07186c17
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.persons.txt
@@ -0,0 +1,46 @@
+<pre>
+Andy Mars,32,male,married
+John Maier,32,male,married
+Peter Dunn,32,male,married
+Yan Chow,32,male,married
+Mike Bloom,31,male,married
+Charles Banning,33,male,single
+Kevin Mikaelson,32,male,single
+Charlotte Beans,30,female,married
+Yumi Tanaka,31,female,married
+Rosalia Alvarez,31,female,married
+Mandy Rose,31,female,married
+James Ross,26,male,single
+Evelyne Lynn,31,female,married
+Michael Chester,34,male,single
+</pre>
+
+{{#subobject:
+ |Person=Andy Mars |Age=32 |Gender= male |Material status= married
+}}{{#subobject:
+ |Person=John Maier |Age=32 |Gender=male |Material status=married
+}}{{#subobject:
+ |Person=Peter Dunn |Age=32 |Gender=male |Material status=married
+}}{{#subobject:
+ |Person=Yan Chow |Age=32 |Gender=male |Material status=married
+}}{{#subobject:
+ |Person=Mike Bloom |Age=31 |Gender=male |Material status=married
+}}{{#subobject:
+ |Person=Charles Banning |Age=33 |Gender=male |Material status=single
+}}{{#subobject:
+ |Person=Kevin Mikaelson |Age=32 |Gender=male |Material status=single
+}}{{#subobject:
+ |Person=Charlotte Beans |Age=30 |Gender=female |Material status=married
+}}{{#subobject:
+ |Person=Yumi Tanaka |Age=31 |Gender=female |Material status=married
+}}{{#subobject:
+ |Person=Rosalia Alvarez |Age=31 |Gender=female |Material status=married
+}}{{#subobject:
+ |Person=Mandy Rose |Age=31 |Gender=female |Material status=married
+}}{{#subobject:
+ |Person=James Ross |Age=26 |Gender=male |Material status=single
+}}{{#subobject:
+ |Person=Evelyne Lynn |Age=31 |Gender=female |Material status=married
+}}{{#subobject:
+ |Person=Michael Chester |Age=34 |Gender=male |Material status=single
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.visits.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.visits.txt
new file mode 100644
index 00000000..d227ce3c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0618.visits.txt
@@ -0,0 +1,27 @@
+<pre>
+Andy Mars,John Maier
+Andy Mars,Peter Dunn
+Andy Mars,Yan Chow
+Andy Mars,Mike Bloom
+Andy Mars,Charles Banning
+Andy Mars,Kevin Mikaelson
+John Maier,Charlotte Beans
+John Maier,James Ross
+John Maier,Evelyne Lynn
+Peter Dunn,Yumi Tanaka
+Peter Dunn,Michael Chester
+Yan Chow,Rosalia Alvarez
+Mike Bloom,Mandy Rose
+</pre>
+
+{{#subobject:Andy Mars
+ |Person=Andy Mars|Has friend=John Maier,Peter Dunn,Yan Chow,Mike Bloom,Charles Banning,Kevin Mikaelson |+sep=, |@category=Friends
+}}{{#subobject:John Maier
+ |Person=John Maier |Has friend=Charlotte Beans, James Ross,Evelyne Lynn |+sep=, |@category=Friends
+}}{{#subobject:Peter Dunn
+ |Person=Peter Dunn |Has friend=Yumi Tanaka,Michael Chester |+sep=, |@category=Friends
+}}{{#subobject:Yan Chow
+ |Person=Yan Chow |Has friend=Rosalia Alvarez|+sep=, |@category=Friends
+}}{{#subobject:Mike Bloom
+ |Person=Mike Bloom |Has friend=Mandy Rose|+sep=, |@category=Friends
+}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-1.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-1.txt
new file mode 100644
index 00000000..0c68a6c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-1.txt
@@ -0,0 +1,3 @@
+[[Has text::Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vulputate tortor nec magna volutpat suscipit. Aliquam erat volutpat. Nulla aliquam, lectus ut dictum sollicitudin, justo magna dapibus turpis, eget tincidunt justo tellus id libero. Aliquam tortor leo, aliquam eu feugiat in, efficitur ac nisl. Ut elit ante, rutrum ac varius vel, faucibus in magna. Mauris euismod erat sit amet vulputate placerat. Maecenas vitae urna eleifend, scelerisque metus vitae, rhoncus metus. Vestibulum luctus, tellus et blandit laoreet, mauris odio commodo mauris, at porttitor libero orci sit amet quam. Fusce at laoreet tortor.]]
+
+[[Category:Q0907]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-2.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-2.txt
new file mode 100644
index 00000000..64a968f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/q-0907-2.txt
@@ -0,0 +1,3 @@
+[[Has url::http://example.com/chart?chs=500x500&chma=0,0,100,100&cht=p&chco=FF0000%2CFFFF00%7CFF8000%2C00FF00%7C00FF00%2C0000FF&chd=t%3A122%2C42%2C17%2C10%2C8%2C7%2C7%2C7%2C7%2C6%2C6%2C6%2C6%2C5%2C5&chl=122%7C42%7C17%7C10%7C8%7C7%7C7%7C7%7C7%7C6%7C6%7C6%7C6%7C5%7C5&chdl=android%7Cjava%7Cstack-trace%7Cbroadcastreceiver%7Candroid-ndk%7Cuser-agent%7Candroid-webview%7Cwebview%7Cbackground%7Cmultithreading%7Candroid-source%7Csms%7Cadb%7Csollections%7Cactivity]]
+
+[[Category:Q0907]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.10.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.10.txt
new file mode 100644
index 00000000..9a34c336
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.10.txt
@@ -0,0 +1 @@
+{"query":["A0001\/3","A0001\/4"],"query-continue-offset":0,"version":1,"meta":{"type":"psubject","limit":10,"count":2,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.11.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.11.txt
new file mode 100644
index 00000000..05ef228f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.11.txt
@@ -0,0 +1 @@
+{"query":["A0001\/4"],"query-continue-offset":0,"version":1,"meta":{"type":"psubject","limit":10,"count":1,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.7.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.7.txt
new file mode 100644
index 00000000..dcb97b80
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.7.txt
@@ -0,0 +1 @@
+{"query":{"subject":"A0001\/1#0##","data":[{"property":"API_text_property","dataitem":[{"type":2,"item":"text 1"}]},{"property":"_MDAT","dataitem":[{"type":6,"item":".*"}]},{"property":"_SKEY","dataitem":[{"type":2,"item":"A0001\/1"}]}],"serializer":"SMW\\Serializers\\SemanticDataSerializer","version":2},"meta":{"type":"subject"}.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.8.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.8.txt
new file mode 100644
index 00000000..b8a8827b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.8.txt
@@ -0,0 +1 @@
+{"query":"<div class=\"smwb-datasheet smwb-theme-light\"><div class=\"smw-table smwb-factbox\"><div class=\"smw-table-row smwb-title\"><a href=\".*\/A0001\/1\" title=\"A0001\/1\">A0001\/1<\/a><\/div><\/div><div class=\"smw-table smwb-factbox\"><div class=\"smw-table-row smwb-actions\"><a href=\".*?title=.*&amp;offset=0&amp;dir=both&amp;article=A0001%2F1#smw_browse_incoming\" class=\"smw_browse_show_incoming\">Show incoming properties<\/a>&#160;\n<\/div><\/div><div class=\"smw-table smwb-factbox smwb-bottom\"><div class=\"smw-table-row smwb-propvalue\"><div class=\"smw-table-cell smwb-cell smwb-prophead\"><a href=\".*\/Property:API_text_property\" title=\"Property:API text property\">API\u00a0text\u00a0property<\/a>\n<\/div><div class=\"smw-table-cell smwb-cell smwb-propval\"><span class=\"smwb-value\">text 1&#160;&#160;<span class=\"smwsearch\"><a href=\".*\/.*\/:API-20text-20property\/text-201\" title=\".*\/:API-20text-20property\/text-201\">+<\/a><\/span><\/span><\/div><\/div><div class=\"smw-table-row smwb-propvalue\"><div class=\"smw-table-cell smwb-cell smwb-prophead\"><span class=\"smw-highlighter\" data-type=\"1\" data-state=\"inline\" data-title=\"Property\" title=\"&quot;Modification date&quot; is a predefined property that corresponds to the date of the last modification of a subject and is provided by Semantic MediaWiki.\"><span class=\"smwbuiltin\"><a href=\".*\/Property:Modification_date\" title=\"Property:Modification date\">Modification\u00a0date<\/a><\/span><span class=\"smwttcontent\">\"Modification date\" is a predefined property that corresponds to the date of the last modification of a subject and is provided by <a rel=\"nofollow\" class=\"external text\" href=\"https:\/\/www.semantic-mediawiki.org\/wiki\/Help:Special_properties\">Semantic MediaWiki<\/a>.<\/span><\/span>\n<\/div><div class=\"smw-table-cell smwb-cell smwb-propval\"><span class=\"smwb-value\">.*<span class=\"smwsearch\"><a href=\".*\/.*\/:Modification-20date\/.*\" title=\".*\/:Modification-20date\/.*\">+<\/a><\/span><\/span><\/div><\/div><\/div><div class=\"smw-table smwb-factbox\"><div class=\"smw-table-row smwb-center\">&#160;\n<\/div><\/div><\/div><div class=\"smwb-form\"><div style=\"margin-top:15px;\"><\/div><form name=\"smwbrowse\" action=\".*\" method=\"get\"><input type=\"hidden\" name=\"title\" value=\".*\"\/><div class=\"smwb-input\"><div class=\"input-field\"><input dir=\"ltr\" name=\"article\" size=\"40\" id=\"smw-page-input\" class=\"input smw-page-input autocomplete-arrow mw-ui-input\" value=\"A0001\/1\"\/><\/div><div class=\"button-field\"><input type=\"submit\" class=\"input-button mw-ui-button\" value=\"Go\"\/><\/div><\/div><\/form><\/div><div class=\"smwb-modules\" data-modules=\"[]\"><\/div>","meta":{"type":"subject"}.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.9.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.9.txt
new file mode 100644
index 00000000..9f533ef8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0001.9.txt
@@ -0,0 +1 @@
+{"query":["A0001\/3"],"query-continue-offset":0,"version":1,"meta":{"type":"psubject","limit":10,"count":1,"isFromCache":false,"queryTime":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.0.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.0.json
new file mode 100644
index 00000000..d4b80586
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.0.json
@@ -0,0 +1 @@
+{"query":{"printrequests":[{"label":"","key":"","redi":"","typeid":"_wpg","mode":2,"format":false},{"label":"API test page","key":"API_test_page","redi":"","typeid":"_wpg","mode":1,"format":""}],"results":{"Example\/A0002\/1":{"printouts":{"API test page":[{"fulltext":"123","fullurl":".*\/123","namespace":0,"exists":"","displaytitle":""}]},"fulltext":"Example\/A0002\/1","fullurl":".*\/Example\/A0002\/1","namespace":0,"exists":"1","displaytitle":""}},"serializer":"SMW\\Serializers\\QueryResultSerializer","version":2,"meta":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.1.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.1.json
new file mode 100644
index 00000000..ceb7b29e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.1.json
@@ -0,0 +1 @@
+{"query":{"printrequests":[{"label":"","key":"","redi":"","typeid":"_wpg","mode":2,"format":false},{"label":"API test page","key":"API_test_page","redi":"","typeid":"_wpg","mode":1,"format":""}],"results":[{"Example\/A0002\/1":{"printouts":{"API test page":[{"fulltext":"123","fullurl":".*\/123","namespace":0,"exists":"","displaytitle":""}]},"fulltext":"Example\/A0002\/1","fullurl":".*\/Example\/A0002\/1","namespace":0,"exists":"1","displaytitle":""}}],"serializer":"SMW\\Serializers\\QueryResultSerializer","version":3,"meta":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.2.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.2.json
new file mode 100644
index 00000000..55c488a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.2.json
@@ -0,0 +1 @@
+{"query":{"printrequests":[{"label":"","key":"","redi":"","typeid":"_wpg","mode":2,"format":false},{"label":"API test page","key":"API_test_page","redi":"","typeid":"_wpg","mode":1,"format":false}],"results":{"Example\/A0002\/1":{"printouts":{"API test page":[{"fulltext":"123","fullurl":".*\/123","namespace":0,"exists":"","displaytitle":""}]},"fulltext":"Example\/A0002\/1","fullurl":".*\/Example\/A0002\/1","namespace":0,"exists":"1","displaytitle":""}},"serializer":"SMW\\Serializers\\QueryResultSerializer","version":2,"meta":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.3.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.3.json
new file mode 100644
index 00000000..32b19aa7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.a-0002.3.json
@@ -0,0 +1 @@
+{"query":{"printrequests":[{"label":"","key":"","redi":"","typeid":"_wpg","mode":2,"format":false},{"label":"API test page","key":"API_test_page","redi":"","typeid":"_wpg","mode":1,"format":false}],"results":[{"Example\/A0002\/1":{"printouts":{"API test page":[{"fulltext":"123","fullurl":".*\/123","namespace":0,"exists":"","displaytitle":""}]},"fulltext":"Example\/A0002\/1","fullurl":".*\/Example\/A0002\/1","namespace":0,"exists":"1","displaytitle":""}}],"serializer":"SMW\\Serializers\\QueryResultSerializer","version":3,"meta":.*} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.0.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.0.csv
new file mode 100644
index 00000000..2dcf6f91
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.0.csv
@@ -0,0 +1,5 @@
+,"Has text","Has number"
+Example/S0022/1,"Some example",
+Example/S0022/2,,"123,345"
+Example/S0022/3,ABC,123
+Example/S0022/4,DEF,123 \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.1.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.1.csv
new file mode 100644
index 00000000..3401095c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.1.csv
@@ -0,0 +1,4 @@
+Example/S0022/1,"Some example",
+Example/S0022/2,,"123,345"
+Example/S0022/3,ABC,123
+Example/S0022/4,DEF,123 \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.2.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.2.csv
new file mode 100644
index 00000000..c0953f0a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.2.csv
@@ -0,0 +1,5 @@
+"Has number","Has text"
+,"Some example"
+"123,345",
+123,ABC
+123,DEF
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.3.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.3.csv
new file mode 100644
index 00000000..5fba776e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.3.csv
@@ -0,0 +1,4 @@
+"Has number","Has text"
+,"Some example"
+"123,345",
+123,"ABC,DEF"
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.4.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.4.csv
new file mode 100644
index 00000000..b19ab4c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.4.csv
@@ -0,0 +1,4 @@
+Example/S0022/1,"Some example",
+Example/S0022/2,,123;345
+Example/S0022/3,ABC,123
+Example/S0022/4,DEF,123 \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.5.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.5.csv
new file mode 100644
index 00000000..ee778599
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.5.csv
@@ -0,0 +1,5 @@
+|"Has number"|"Has text"
+Example/S0022/1||"Some example"
+Example/S0022/2|123+345|
+Example/S0022/3|123|ABC
+Example/S0022/4|123|DEF
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.6.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.6.csv
new file mode 100644
index 00000000..6fd9cf66
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.6.csv
@@ -0,0 +1,4 @@
+"Has number","Has text"
+,"Some example"
+123;345,
+123,ABC;DEF
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.7.csv b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.7.csv
new file mode 100644
index 00000000..cc890ed1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0022.7.csv
@@ -0,0 +1,4 @@
+"Has number"|"Has text"
+|"Some example"
+123;345|
+123|ABC;DEF
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.0.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.0.txt
new file mode 100644
index 00000000..c2c7304e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.0.txt
@@ -0,0 +1,12 @@
+#FORMAT: BEACON
+#PREFIX: http://d-nb.info/gnd/
+#TARGET:.*
+#VERSION: 0.1
+#HOMEPAGE: https://www.semantic-mediawiki.org/w/index.php?title=Help:BEACON
+#FEED: https://www.semantic-mediawiki.org/w/index.php?title=BEACON&action=render
+#LINK: http://www.w3.org/2000/01/rdf-schema#seeAlso
+#INSTITUTION: Semantic MediaWiki
+#MESSAGE: Person test data in Semantic MediaWiki
+#TIMESTAMP: .*
+#UPDATE: always
+123456789||John Doe \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.1.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.1.txt
new file mode 100644
index 00000000..8ff483d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/res.s-0025.1.txt
@@ -0,0 +1,13 @@
+#FORMAT: BEACON
+#PREFIX: http://d-nb.info/gnd/
+#TARGET:.*
+#VERSION: 0.1
+#HOMEPAGE: https://www.semantic-mediawiki.org/w/index.php?title=Help:BEACON
+#FEED: https://www.semantic-mediawiki.org/w/index.php?title=BEACON&action=render
+#LINK: http://www.w3.org/2000/01/rdf-schema#seeAlso
+#INSTITUTION: Semantic MediaWiki
+#MESSAGE: Person test data in Semantic MediaWiki
+#TIMESTAMP: .*
+#UPDATE: always
+123456789||John Doe
+987654321||Jane Doe \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.0.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.0.txt
new file mode 100644
index 00000000..f5f01811
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.0.txt
@@ -0,0 +1 @@
+{"printrequests":[{"label":"","key":"","redi":"","typeid":"_wpg","mode":2,"format":false},{"label":"Has text","key":"Has_text","redi":"","typeid":"_txt","mode":1,"format":""},{"label":"Has number","key":"Has_number","redi":"","typeid":"_num","mode":1,"format":""}],"results":{"Example/S0020/1":{"printouts":{"Has text":["Some example"],"Has number":[]},"fulltext":"Example/S0020/1","fullurl":".*Example/S0020/1","namespace":0,"exists":"1","displaytitle":""},"Example/S0020/2":{"printouts":{"Has text":[],"Has number":[123,345]},"fulltext":"Example/S0020/2","fullurl":".*Example/S0020/2","namespace":0,"exists":"1","displaytitle":""}},"serializer":"SMW\\Serializers\\QueryResultSerializer","version":2,"rows":2} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.1.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.1.txt
new file mode 100644
index 00000000..106f3d94
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.1.txt
@@ -0,0 +1 @@
+{"Example/S0020/1#0##":{"Has text":["Some example"],"Has number":[]},"Example/S0020/2#0##":{"Has text":[],"Has number":["123","345"]}} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.2.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.2.txt
new file mode 100644
index 00000000..fae10c19
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0020.2.txt
@@ -0,0 +1 @@
+[{"Has text":["Some example"],"Has number":[]},{"Has text":[],"Has number":["123","345"]}] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon-intro.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon-intro.txt
new file mode 100644
index 00000000..82db62e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon-intro.txt
@@ -0,0 +1,11 @@
+<includeonly>#FORMAT: BEACON
+#PREFIX: http://d-nb.info/gnd/
+#TARGET: {{{querylink}}}
+#VERSION: 0.1
+#HOMEPAGE: https://www.semantic-mediawiki.org/w/index.php?title=Help:BEACON
+#FEED: https://www.semantic-mediawiki.org/w/index.php?title=BEACON&action=render
+#LINK: http://www.w3.org/2000/01/rdf-schema#seeAlso
+#INSTITUTION: Semantic MediaWiki
+#MESSAGE: Person test data in Semantic MediaWiki
+#TIMESTAMP: {{CURRENTTIMESTAMP}}
+#UPDATE: always</includeonly> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon.txt
new file mode 100644
index 00000000..e4d26937
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/s-0025-beacon.txt
@@ -0,0 +1,2 @@
+<includeonly>
+{{{?GND|}}}||{{{?Name|}}}</includeonly>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/skos-import.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/skos-import.txt
new file mode 100644
index 00000000..213056e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/Fixtures/skos-import.txt
@@ -0,0 +1,33 @@
+http://www.w3.org/2004/02/skos/core#|[http://www.w3.org/TR/skos-reference/skos.rdf Simple Knowledge Organization System (SKOS)]
+ altLabel|Type:Monolingual text
+ broader|Type:Annotation URI
+ broaderTransitive|Type:Annotation URI
+ broadMatch|Type:Annotation URI
+ changeNote|Type:Text
+ closeMatch|Type:Annotation URI
+ Collection|Class
+ Concept|Class
+ ConceptScheme|Class
+ definition|Type:Text
+ editorialNote|Type:Text
+ exactMatch|Type:Annotation URI
+ example|Type:Text
+ hasTopConcept|Type:Page
+ hiddenLabel|Type:String
+ historyNote|Type:Text
+ inScheme|Type:Page
+ mappingRelation|Type:Page
+ member|Type:Page
+ memberList|Type:Page
+ narrower|Type:Annotation URI
+ narrowerTransitive|Type:Annotation URI
+ narrowMatch|Type:Annotation URI
+ notation|Type:Text
+ note|Type:Text
+ OrderedCollection|Class
+ prefLabel|Type:String
+ related|Type:Annotation URI
+ relatedMatch|Type:Annotation URI
+ scopeNote|Type:Text
+ semanticRelation|Type:Page
+ topConceptOf|Type:Page \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php
new file mode 100644
index 00000000..c622b849
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/JsonTestCaseScriptRunnerTest.php
@@ -0,0 +1,510 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\EventHandler;
+use SMW\PropertySpecificationLookup;
+use SMW\SPARQLStore\TurtleTriplesBuilder;
+use SMW\Tests\JsonTestCaseFileHandler;
+use SMW\Tests\JsonTestCaseScriptRunner;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class JsonTestCaseScriptRunnerTest extends JsonTestCaseScriptRunner {
+
+ /**
+ * @var QueryTestCaseProcessor
+ */
+ private $queryTestCaseProcessor;
+
+ /**
+ * @var RdfTestCaseProcessor
+ */
+ private $rdfTestCaseProcessor;
+
+ /**
+ * @var ParserTestCaseProcessor
+ */
+ private $parserTestCaseProcessor;
+
+ /**
+ * @var SpecialPageTestCaseProcessor
+ */
+ private $specialPageTestCaseProcessor;
+
+ /**
+ * @var ParserHtmlTestCaseProcessor
+ */
+ private $parserHtmlTestCaseProcessor;
+
+ /**
+ * @var ApiTestCaseProcessor
+ */
+ private $apiTestCaseProcessor;
+
+ /**
+ * @var RunnerFactory
+ */
+ private $runnerFactory;
+
+ /**
+ * @var EventDispatcher
+ */
+ private $eventDispatcher;
+
+ /**
+ * @see JsonTestCaseScriptRunner::$deletePagesOnTearDown
+ */
+ protected $deletePagesOnTearDown = true;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+ $this->runnerFactory = $utilityFactory->newRunnerFactory();
+
+ $validatorFactory = $utilityFactory->newValidatorFactory();
+ $stringValidator = $validatorFactory->newStringValidator();
+
+ $this->queryTestCaseProcessor = new QueryTestCaseProcessor(
+ $this->getStore(),
+ $validatorFactory->newQueryResultValidator(),
+ $stringValidator,
+ $validatorFactory->newNumberValidator()
+ );
+
+ $this->rdfTestCaseProcessor = new RdfTestCaseProcessor(
+ $this->getStore(),
+ $stringValidator,
+ $this->runnerFactory
+ );
+
+ $this->parserTestCaseProcessor = new ParserTestCaseProcessor(
+ $this->getStore(),
+ $validatorFactory->newSemanticDataValidator(),
+ $validatorFactory->newIncomingSemanticDataValidator( $this->getStore() ),
+ $stringValidator
+ );
+
+ $this->specialPageTestCaseProcessor = new SpecialPageTestCaseProcessor(
+ $this->getStore(),
+ $stringValidator
+ );
+
+ $this->parserHtmlTestCaseProcessor = new ParserHtmlTestCaseProcessor(
+ $validatorFactory->newHtmlValidator()
+ );
+
+ $this->apiTestCaseProcessor = new ApiTestCaseProcessor(
+ $utilityFactory->newMwApiFactory(),
+ $stringValidator
+ );
+
+ $this->eventDispatcher = EventHandler::getInstance()->getEventDispatcher();
+
+ // This ensures that if content is created in the NS_MEDIAWIKI namespace
+ // and an object relies on the MediaWikiNsContentReader then it uses the DB
+ ApplicationFactory::clear();
+ ApplicationFactory::getInstance()->getMediaWikiNsContentReader()->skipMessageCache();
+ DataValueFactory::getInstance()->clear();
+
+ // Reset the Title/TitleParser otherwise a singleton instance holds an outdated
+ // content language reference
+ $this->testEnvironment->resetMediaWikiService( '_MediaWikiTitleCodec' );
+ $this->testEnvironment->resetMediaWikiService( 'TitleParser' );
+
+ // #3414
+ // NameTableAccessException: Expected unused ID from database insert for
+ // 'mw-changed-redirect-target' into 'change_tag_def',
+ $this->testEnvironment->resetMediaWikiService( 'NameTableStoreFactory' );
+
+ $this->testEnvironment->resetPoolCacheById( PropertySpecificationLookup::POOLCACHE_ID );
+ $this->testEnvironment->resetPoolCacheById( TurtleTriplesBuilder::POOLCACHE_ID );
+
+ // Make sure LocalSettings don't interfere with the default settings
+ $this->testEnvironment->withConfiguration(
+ [
+ 'smwgQueryResultCacheType' => false,
+ 'smwgQFilterDuplicates' => false,
+ 'smwgExportResourcesAsIri' => false,
+ 'smwgCompactLinkSupport' => false,
+ 'smwgSparqlReplicationPropertyExemptionList' => [],
+ 'smwgPageSpecialProperties' => [ '_MDAT' ],
+ 'smwgFieldTypeFeatures' => SMW_FIELDT_NONE,
+ 'smwgDVFeatures' => $GLOBALS['smwgDVFeatures'] & ~SMW_DV_NUMV_USPACE,
+ 'smwgCacheUsage' => [
+ 'api.browse' => false
+ ] + $GLOBALS['smwgCacheUsage']
+ ]
+ );
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getTestCaseLocation
+ */
+ protected function getTestCaseLocation() {
+ return __DIR__ . '/TestCases';
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getTestCaseLocation
+ */
+ protected function getRequiredJsonTestCaseMinVersion() {
+ return '2';
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getAllowedTestCaseFiles
+ */
+ protected function getAllowedTestCaseFiles() {
+ return [];
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getDependencyDefinitions
+ */
+ protected function getDependencyDefinitions() {
+ return [
+ 'Maps' => function( $val, &$reason ) {
+
+ if ( !defined( 'SM_VERSION' ) ) {
+ $reason = "Dependency: Maps (or Semantic Maps) as requirement is not available!";
+ return false;
+ }
+
+ list( $compare, $requiredVersion ) = explode( ' ', $val );
+ $version = SM_VERSION;
+
+ if ( !version_compare( $version, $requiredVersion, $compare ) ) {
+ $reason = "Dependency: Required version of Maps ($requiredVersion $compare $version) is not available!";
+ return false;
+ }
+
+ return true;
+ }
+ ];
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::runTestCaseFile
+ *
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ protected function runTestCaseFile( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ $this->checkEnvironmentToSkipCurrentTest( $jsonTestCaseFileHandler );
+
+ // Setup
+ $this->prepareTest( $jsonTestCaseFileHandler );
+
+ // Before test execution
+ $this->doRunBeforeTest( $jsonTestCaseFileHandler );
+
+ // Run test cases
+ $this->doRunParserTests( $jsonTestCaseFileHandler );
+ $this->doRunSpecialTests( $jsonTestCaseFileHandler );
+ $this->doRunRdfTests( $jsonTestCaseFileHandler );
+ $this->doRunQueryTests( $jsonTestCaseFileHandler );
+ $this->doRunParserHtmlTests( $jsonTestCaseFileHandler );
+ $this->doRunApiTests( $jsonTestCaseFileHandler );
+ }
+
+ /**
+ * @see JsonTestCaseScriptRunner::getPermittedSettings
+ */
+ protected function getPermittedSettings() {
+ parent::getPermittedSettings();
+
+ $elasticsearchConfig = function( $val ) {
+
+ if ( $this->getStore() instanceof \SMWElasticStore ) {
+ $config = $this->getStore()->getConnection( 'elastic' )->getConfig();
+
+ foreach ( $val as $key => $value ) {
+ $config->set( $key, array_merge( $config->get( $key ), $value ) );
+ }
+
+ return $config->toArray();
+ }
+ };
+
+ $this->registerConfigValueCallback( 'smwgElasticsearchConfig', $elasticsearchConfig );
+
+ return [
+ 'smwgNamespacesWithSemanticLinks',
+ 'smwgPageSpecialProperties',
+ 'smwgNamespace',
+ 'smwgExportBCNonCanonicalFormUse',
+ 'smwgExportBCAuxiliaryUse',
+ 'smwgExportResourcesAsIri',
+ 'smwgQMaxSize',
+ 'smwgQMaxDepth',
+ 'smwStrictComparators',
+ 'smwgQSubpropertyDepth',
+ 'smwgQSubcategoryDepth',
+ 'smwgQConceptCaching',
+ 'smwgMaxNonExpNumber',
+ 'smwgDVFeatures',
+ 'smwgEnabledQueryDependencyLinksStore',
+ 'smwgEnabledFulltextSearch',
+ 'smwgFulltextDeferredUpdate',
+ 'smwgFulltextSearchIndexableDataTypes',
+ 'smwgFixedProperties',
+ 'smwgPropertyZeroCountDisplay',
+ 'smwgQueryResultCacheType',
+ 'smwgLinksInValues',
+ 'smwgQFilterDuplicates',
+ 'smwgQueryProfiler',
+ 'smwgEntityCollation',
+ 'smwgSparqlQFeatures',
+ 'smwgQExpensiveThreshold',
+ 'smwgQExpensiveExecutionLimit',
+ 'smwgFieldTypeFeatures',
+ 'smwgCreateProtectionRight',
+ 'smwgParserFeatures',
+ 'smwgCategoryFeatures',
+ 'smwgDefaultOutputFormatters',
+ 'smwgCompactLinkSupport',
+ 'smwgCacheUsage',
+ 'smwgQSortFeatures',
+ 'smwgElasticsearchConfig',
+ 'smwgDefaultNumRecurringEvents',
+
+ // MW related
+ 'wgLanguageCode',
+ 'wgContLang',
+ 'wgLang',
+ 'wgCapitalLinks',
+ 'wgAllowDisplayTitle',
+ 'wgRestrictDisplayTitle',
+ 'wgSearchType',
+ 'wgEnableUploads',
+ 'wgFileExtensions',
+ 'wgDefaultUserOptions',
+ 'wgLocalTZoffset'
+ ];
+ }
+
+ private function prepareTest( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ foreach ( $this->getPermittedSettings() as $key ) {
+ $this->changeGlobalSettingTo(
+ $key,
+ $jsonTestCaseFileHandler->getSettingsFor( $key, $this->getConfigValueCallback( $key ) )
+ );
+ }
+
+ if ( $jsonTestCaseFileHandler->hasSetting( 'smwgFieldTypeFeatures' ) ) {
+ $this->doRunTableSetupBeforeContentCreation();
+ }
+
+ // #2135
+ // On some occasions (e.g. fixed properties) and to setup the correct
+ // table schema, run the creation once before the content is created
+ $pageList = $jsonTestCaseFileHandler->getPageCreationSetupList();
+
+ if ( $jsonTestCaseFileHandler->hasSetting( 'smwgFixedProperties' ) ) {
+ foreach ( $pageList as $page ) {
+ if ( isset( $page['namespace'] ) && $page['namespace'] === 'SMW_NS_PROPERTY' ) {
+ $this->doRunTableSetupBeforeContentCreation( [ $page ] );
+ }
+ }
+ }
+
+ $this->createPagesFrom(
+ $pageList,
+ NS_MAIN
+ );
+ }
+
+ private function doRunTableSetupBeforeContentCreation( $pageList = null ) {
+
+ if ( $pageList !== null ) {
+ $this->createPagesFrom( $pageList );
+ }
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'setupStore' );
+ $maintenanceRunner->setQuiet();
+ $maintenanceRunner->run();
+ }
+
+ private function doRunBeforeTest( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ foreach ( $jsonTestCaseFileHandler->findTasksBeforeTestExecutionByType( 'maintenance-run' ) as $runner => $options ) {
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( $runner );
+ $maintenanceRunner->setQuiet();
+
+ $maintenanceRunner->setOptions(
+ (array)$options
+ );
+
+ $maintenanceRunner->run();
+
+ if ( isset( $options['quiet'] ) && $options['quiet'] === false ) {
+ print_r( $maintenanceRunner->getOutput() );
+ }
+ }
+
+ foreach ( $jsonTestCaseFileHandler->findTasksBeforeTestExecutionByType( 'job-run' ) as $jobType ) {
+ $jobQueueRunner = $this->runnerFactory->newJobQueueRunner( $jobType );
+ $jobQueueRunner->run();
+ }
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ private function doRunParserTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ $this->parserTestCaseProcessor->setDebugMode(
+ $jsonTestCaseFileHandler->getDebugMode()
+ );
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'parser' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->parserTestCaseProcessor->process( $case );
+ }
+ }
+
+ private function doRunSpecialTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ $this->specialPageTestCaseProcessor->setDebugMode(
+ $jsonTestCaseFileHandler->getDebugMode()
+ );
+
+ $this->specialPageTestCaseProcessor->setTestCaseLocation(
+ $this->getTestCaseLocation()
+ );
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'special' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->specialPageTestCaseProcessor->process( $case );
+ }
+ }
+
+ private function doRunRdfTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ // For some reason there are some random failures where
+ // the instance hasn't reset the cache in time to fetch the
+ // property definition which only happens for the SPARQLStore
+
+ // This should not be necessary because the resetcache event
+ // is triggered`
+ $this->eventDispatcher->dispatch( 'exporter.reset' );
+ $this->eventDispatcher->dispatch( 'query.comparator.reset' );
+
+ $this->rdfTestCaseProcessor->setDebugMode(
+ $jsonTestCaseFileHandler->getDebugMode()
+ );
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'rdf' ) as $case ) {
+ $this->rdfTestCaseProcessor->process( $case );
+ }
+ }
+
+ private function doRunQueryTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ // Set query parser late to ensure that expected settings are adjusted
+ // (language etc.) because the __construct relies on the context language
+ $this->queryTestCaseProcessor->setQueryParser(
+ ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser()
+ );
+
+ $this->queryTestCaseProcessor->setDebugMode(
+ $jsonTestCaseFileHandler->getDebugMode()
+ );
+
+ $i = 0;
+ $count = 0;
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'query' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->queryTestCaseProcessor->processQueryCase( new QueryTestCaseInterpreter( $case ) );
+ $i++;
+ }
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'concept' ) as $conceptCase ) {
+ $this->queryTestCaseProcessor->processConceptCase( new QueryTestCaseInterpreter( $conceptCase ) );
+ $i++;
+ }
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'format' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->queryTestCaseProcessor->processFormatCase( new QueryTestCaseInterpreter( $case ) );
+ $i++;
+ }
+
+ $count += $jsonTestCaseFileHandler->countTestCasesByType( 'query' );
+ $count += $jsonTestCaseFileHandler->countTestCasesByType( 'concept' );
+ $count += $jsonTestCaseFileHandler->countTestCasesByType( 'format' );
+
+ // Avoid tests being marked as risky when all cases were skipped
+ if ( $i == 0 && $count > 0 ) {
+ $this->markTestSkipped( 'Skipped all assertions for: ' . $this->getName() );
+ }
+ }
+
+ /**
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ private function doRunParserHtmlTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ if ( !$this->parserHtmlTestCaseProcessor->canUse() ) {
+ $this->markTestIncomplete( 'The required resource for the ParserHtmlTestCaseProcessor/HtmlValidator is not available.' );
+ }
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'parser-html' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->parserHtmlTestCaseProcessor->process( $case );
+ }
+ }
+
+ /**
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ private function doRunApiTests( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ $this->apiTestCaseProcessor->setTestCaseLocation(
+ $this->getTestCaseLocation()
+ );
+
+ foreach ( $jsonTestCaseFileHandler->findTestCasesByType( 'api' ) as $case ) {
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipFor( $case, $this->connectorId ) ) {
+ continue;
+ }
+
+ $this->apiTestCaseProcessor->process( $case );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserHtmlTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserHtmlTestCaseProcessor.php
new file mode 100644
index 00000000..5e99a630
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserHtmlTestCaseProcessor.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMW\DIWikiPage;
+use SMW\Tests\Utils\UtilityFactory;
+use SMW\Tests\Utils\Validators\HtmlValidator;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class ParserHtmlTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var HtmlValidator
+ */
+ private $htmlValidator;
+
+ /**
+ * @var PageReader
+ */
+ private $pageReader;
+
+ /**
+ * @param HtmlValidator $htmlValidator
+ */
+ public function __construct( HtmlValidator $htmlValidator ) {
+ parent::__construct();
+ $this->htmlValidator = $htmlValidator;
+ $this->pageReader = UtilityFactory::getInstance()->newPageReader();
+ }
+
+ /**
+ * @return boolean
+ */
+ public function canUse() {
+ return $this->htmlValidator->canUse();
+ }
+
+ /**
+ * @param array $case
+ */
+ public function process( array $case ) {
+
+ if ( !isset( $case[ 'subject' ] ) ) {
+ return;
+ }
+
+ if ( isset( $case[ 'about' ] ) ) {
+ $this->setName( $case[ 'about' ] );
+ }
+
+ $this->assertParserHtmlOutputForCase( $case );
+ }
+
+ /**
+ * @param array $case
+ */
+ private function assertParserHtmlOutputForCase( array $case ) {
+
+ if ( !isset( $case[ 'assert-output' ] ) ) {
+ return;
+ }
+
+ $outputText = $this->getOutputText( $case );
+
+ if ( $this->isSetAndTrueish( $case[ 'assert-output' ], 'to-be-valid-html' ) ) {
+ $this->htmlValidator->assertThatHtmlIsValid(
+ $outputText,
+ $case[ 'about' ]
+ );
+ }
+
+ if ( $this->isSetAndTrueish( $case[ 'assert-output' ], 'to-contain' ) ) {
+ $this->htmlValidator->assertThatHtmlContains(
+ $case[ 'assert-output' ][ 'to-contain' ],
+ $outputText,
+ $case[ 'about' ]
+ );
+ }
+
+ if ( $this->isSetAndTrueish( $case[ 'assert-output' ], 'not-contain' ) ) {
+ $this->htmlValidator->assertThatHtmlNotContains(
+ $case[ 'assert-output' ][ 'not-contain' ],
+ $outputText,
+ $case[ 'about' ]
+ );
+ }
+ }
+
+ /**
+ * @param array $case
+ * @return string
+ */
+ private function getOutputText( array $case ) {
+
+ $subject = DIWikiPage::newFromText(
+ $case[ 'subject' ],
+ isset( $case[ 'namespace' ] ) ? constant( $case[ 'namespace' ] ) : NS_MAIN
+ );
+
+ $parserOutput = $this->pageReader->getParserOutputFromEdit(
+ $subject->getTitle()
+ );
+
+ if ( !$this->isSetAndTrueish( $case[ 'assert-output' ], [ 'withOutputPageContext', 'onPageView' ] ) ) {
+ return $parserOutput->getText();
+ }
+
+ $context = new \RequestContext();
+ $context->setTitle( $subject->getTitle() );
+
+ if ( $this->isSetAndTrueish( $case[ 'assert-output' ], 'withOutputPageContext' ) ) {
+ // Ensures the OutputPageBeforeHTML hook is run
+ $context->getOutput()->addParserOutput( $parserOutput );
+ } else {
+ \Article::newFromTitle( $subject->getTitle(), $context )->view();
+ }
+
+ return $context->getOutput()->getHTML();
+
+ }
+
+ /**
+ * @param $array
+ * @param string | string[] $keys
+ * @return bool True if any of the $keys is defined in $array and true-ish
+ */
+ private function isSetAndTrueish( $array, $keys ) {
+
+ $keys = (array)$keys;
+
+ foreach ( $keys as $key ) {
+ if ( isset( $array[ $key ] ) && $array[ $key ] ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserTestCaseProcessor.php
new file mode 100644
index 00000000..42159a6e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/ParserTestCaseProcessor.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\MediaWikiNsContentReader;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ParserTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var SemanticDataValidator
+ */
+ private $semanticDataValidator;
+
+ /**
+ * @var IncomingSemanticDataValidator
+ */
+ private $incomingSemanticDataValidator;
+
+ /**
+ * @var StringValidator
+ */
+ private $stringValidator;
+
+ /**
+ * @var PageReader
+ */
+ private $pageReader;
+
+ /**
+ * @var SerializerFactory
+ */
+ private $serializerFactory;
+
+ /**
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * @param Store
+ * @param SemanticDataValidator
+ * @param IncomingSemanticDataValidator
+ * @param StringValidator
+ */
+ public function __construct( $store, $semanticDataValidator, $incomingSemanticDataValidator, $stringValidator ) {
+ $this->store = $store;
+ $this->semanticDataValidator = $semanticDataValidator;
+ $this->incomingSemanticDataValidator = $incomingSemanticDataValidator;
+ $this->stringValidator = $stringValidator;
+ $this->pageReader = UtilityFactory::getInstance()->newPageReader();
+ $this->serializerFactory = \SMW\ApplicationFactory::getInstance()->newSerializerFactory();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param boolean $debugMode
+ */
+ public function setDebugMode( $debugMode ) {
+ $this->debug = $debugMode;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param array $case
+ */
+ public function process( array $case ) {
+
+ if ( !isset( $case['subject'] ) ) {
+ return;
+ }
+
+ $this->assertSemanticDataForCase(
+ $case
+ );
+
+ $this->assertTextFromParserOutputForCase(
+ $case
+ );
+
+ $this->assertTextFromParsedMsgForCase(
+ $case
+ );
+ }
+
+ private function assertSemanticDataForCase( $case ) {
+
+ // Allows for data to be re-read from the DB instead of being fetched
+ // from the store-id-cache
+ if ( isset( $case['store']['clear-cache'] ) && $case['store']['clear-cache'] ) {
+ $this->store->clear();
+ }
+
+ if ( !isset( $case['assert-store'] ) || !isset( $case['assert-store']['semantic-data'] ) ) {
+ return;
+ }
+
+ $subject = $this->getSubjectFrom( $case, false );
+ $semanticData = $this->store->getSemanticData( $subject );
+
+ if ( $this->debug ) {
+ print_r(
+ $this->serializerFactory->newSemanticDataSerializer()->serialize( $semanticData )
+ );
+ }
+
+ if ( isset( $case['errors'] ) && $case['errors'] !== [] ) {
+ $this->assertNotEmpty(
+ $semanticData->getErrors()
+ );
+ }
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $case['assert-store']['semantic-data'],
+ $semanticData,
+ $case['about']
+ );
+
+ if ( !isset( $case['assert-store']['semantic-data']['incoming'] ) ) {
+ return;
+ }
+
+ $this->incomingSemanticDataValidator->assertThatIncomingDataAreSet(
+ $case['assert-store']['semantic-data']['incoming'],
+ $subject,
+ $case['about']
+ );
+ }
+
+ private function assertTextFromParserOutputForCase( $case ) {
+
+ if ( !isset( $case['assert-output'] ) ) {
+ return;
+ }
+
+ $title = $this->getSubjectFrom( $case )->getTitle();
+
+ $parserOutput = $this->pageReader->getParserOutputFromEdit(
+ $title
+ );
+
+ if ( isset( $case['assert-output']['onOutputPage'] ) && $case['assert-output']['onOutputPage'] ) {
+ $context = new \RequestContext();
+ $context->setTitle( $title );
+ // Ensures the OutputPageBeforeHTML hook is run
+ $context->getOutput()->addParserOutput( $parserOutput );
+ $output = $context->getOutput()->getHtml();
+ } elseif ( isset( $case['assert-output']['onPageView'] ) ) {
+ $parameters = isset( $case['assert-output']['onPageView']['parameters'] ) ? $case['assert-output']['onPageView']['parameters'] : [];
+ $context = \RequestContext::newExtraneousContext(
+ $title,
+ $parameters
+ );
+ \Article::newFromTitle( $title, $context )->view();
+ $output = $context->getOutput()->getHtml();
+ } else {
+ $output = $parserOutput->getText();
+ }
+
+ // Strip HTML comments
+ $output = preg_replace('/<!--(.*)-->/Uis', '', $output );
+
+ if ( isset( $case['assert-output']['to-contain'] ) ) {
+ $this->stringValidator->assertThatStringContains(
+ $case['assert-output']['to-contain'],
+ $output,
+ $case['about']
+ );
+ }
+
+ if ( isset( $case['assert-output']['not-contain'] ) ) {
+ $this->stringValidator->assertThatStringNotContains(
+ $case['assert-output']['not-contain'],
+ $output,
+ $case['about']
+ );
+ }
+ }
+
+ private function assertTextFromParsedMsgForCase( $case ) {
+
+ if ( !isset( $case['assert-msgoutput'] ) ) {
+ return;
+ }
+
+ $mediaWikiNsContentReader = new MediaWikiNsContentReader();
+ $mediaWikiNsContentReader->skipMessageCache();
+
+ $text = $mediaWikiNsContentReader->read( $case['subject'] );
+ $text = wfMessage( 'smw-parse', $text )->parse();
+
+ if ( isset( $case['assert-msgoutput']['to-contain'] ) ) {
+ $this->stringValidator->assertThatStringContains(
+ $case['assert-msgoutput']['to-contain'],
+ $text,
+ $case['about']
+ );
+ }
+
+ if ( isset( $case['assert-msgoutput']['not-contain'] ) ) {
+ $this->stringValidator->assertThatStringNotContains(
+ $case['assert-msgoutput']['not-contain'],
+ $text,
+ $case['about']
+ );
+ }
+ }
+
+ private function getSubjectFrom( $case, $checkExists = true ) {
+
+ $subject = DIWikiPage::newFromText(
+ $case['subject'],
+ isset( $case['namespace'] ) ? constant( $case['namespace'] ) : NS_MAIN
+ );
+
+ $title = $subject->getTitle();
+
+ if ( $title === null ) {
+ throw new RuntimeException( 'Could not create Title object for subject page "' . $case['subject'] . '".' );
+ }
+
+ if ( $checkExists && !$title->exists() ) {
+ throw new RuntimeException( 'Subject page "' . $case['subject'] . '" does not exist.' );
+ }
+
+ return $subject;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseInterpreter.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseInterpreter.php
new file mode 100644
index 00000000..77fa3e7c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseInterpreter.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMW\DataTypeRegistry;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDataItem as DataItem;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QueryTestCaseInterpreter {
+
+ /**
+ * @var array
+ */
+ private $contents;
+
+ /**
+ * @since 2.2
+ *
+ * @param array $contents
+ */
+ public function __construct( array $contents ) {
+ $this->contents = $contents;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function hasCondition() {
+ return isset( $this->contents['condition'] );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getCondition() {
+ return $this->hasCondition() ? $this->contents['condition'] : '';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function isAbout() {
+ return isset( $this->contents['about'] ) ? $this->contents['about'] : 'no description';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getQueryMode() {
+ return isset( $this->contents['parameters']['querymode'] ) ? constant( $this->contents['parameters']['querymode'] ) : \SMWQuery::MODE_INSTANCES;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getLimit() {
+ return isset( $this->contents['parameters']['limit'] ) ? (int)$this->contents['parameters']['limit'] : 100;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getOffset() {
+ return isset( $this->contents['parameters']['offset'] ) ? (int)$this->contents['parameters']['offset'] : 0;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return DIWikiPage|null
+ */
+ public function getSubject() {
+ return isset( $this->contents['subject'] ) ? DIWikiPage::newFromText( $this->contents['subject'] ) : null;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return boolean
+ */
+ public function isFromCache() {
+ return isset( $this->contents['assert-queryresult']['isFromCache'] ) ? (bool)$this->contents['assert-queryresult']['isFromCache'] : null;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getExtraPrintouts() {
+
+ $extraPrintouts = [];
+
+ if ( !isset( $this->contents['printouts'] ) || $this->contents['printouts'] === [] ) {
+ return $extraPrintouts;
+ }
+
+ foreach ( $this->contents['printouts'] as $printout ) {
+
+ $label = null;
+
+ if ( strpos( $printout, '#') !== false ) {
+ list( $printout, $label ) = explode( '#', $printout );
+ }
+
+ $extraPrintouts[] = new PrintRequest(
+ PrintRequest::PRINT_PROP,
+ $label,
+ DataValueFactory::getInstance()->newPropertyValueByLabel( $printout )
+ );
+ }
+
+ return $extraPrintouts;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getSortKeys() {
+
+ if ( isset( $this->contents['parameters']['sort'] ) ) {
+
+ if ( is_array( $this->contents['parameters']['sort'] ) ) {
+ return $this->contents['parameters']['sort'];
+ }
+
+ return [ $this->contents['parameters']['sort'] => 'DESC' ];
+ }
+
+ return [];
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @return boolean
+ */
+ public function isRequiredToClearStoreCache() {
+ return isset( $this->contents['store']['clear-cache'] ) && $this->contents['store']['clear-cache'];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getExpectedCount() {
+ return isset( $this->contents['assert-queryresult']['count'] ) ? (int)$this->contents['assert-queryresult']['count'] : 0;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DIWikiPage[]
+ */
+ public function getExpectedSubjects() {
+
+ $subjects = [];
+
+ if ( !isset( $this->contents['assert-queryresult']['results'] ) ) {
+ return $subjects;
+ }
+
+ foreach ( $this->contents['assert-queryresult']['results'] as $hashName ) {
+ $subjects[] = DIWikiPage::doUnserialize( str_replace( ' ', '_', $hashName ) );
+ }
+
+ return $subjects;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DataItem[]
+ */
+ public function getExpectedDataItems() {
+
+ $dataItems = [];
+
+ if ( !isset( $this->contents['assert-queryresult']['dataitems'] ) ) {
+ return $dataItems;
+ }
+
+ foreach ( $this->contents['assert-queryresult']['dataitems'] as $dataitem ) {
+ $dataItems[] = DataItem::newFromSerialization(
+ DataTypeRegistry::getInstance()->getDataItemId( $dataitem['type'] ),
+ $dataitem['value']
+ );
+ }
+
+ return $dataItems;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return DataValues[]
+ */
+ public function getExpectedDataValues() {
+
+ $dataValues = [];
+
+ if ( !isset( $this->contents['assert-queryresult']['datavalues'] ) ) {
+ return $dataValues;
+ }
+
+ foreach ( $this->contents['assert-queryresult']['datavalues'] as $datavalue ) {
+ $dataValues[] = DataValueFactory::getInstance()->newDataValueByProperty(
+ DIProperty::newFromUserLabel( $datavalue['property'] ),
+ $datavalue['value']
+ );
+ }
+
+ return $dataValues;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return integer
+ */
+ public function getExpectedErrorCount() {
+
+ if ( !isset( $this->contents['assert-queryresult']['error'] ) ) {
+ return -1;
+ }
+
+ return $this->contents['assert-queryresult']['error'];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function fetchTextFromOutputSubject() {
+
+ if ( !isset( $this->contents['subject'] ) ) {
+ return '';
+ }
+
+ $title = \Title::newFromText( $this->contents['subject'] );
+ $parserOutput = UtilityFactory::getInstance()->newPageReader()->getEditInfo( $title )->output;
+
+ return $parserOutput->getText();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getExpectedFormatOuputFor( $id ) {
+
+ $output = [];
+
+ if ( !isset( $this->contents['assert-output'] ) || !isset( $this->contents['assert-output'][$id] ) ) {
+ return $output;
+ }
+
+ return $this->contents['assert-output'][$id];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return []
+ */
+ public function getExpectedConceptCache() {
+ return isset( $this->contents['conceptcache'] ) ? $this->contents['conceptcache'] : [];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseProcessor.php
new file mode 100644
index 00000000..ebe702d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/QueryTestCaseProcessor.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMW\Query\Parser as QueryParser;
+use SMW\Store;
+use SMWQuery as Query;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QueryTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var QueryParser
+ */
+ private $fileReader;
+
+ /**
+ * @var NumberValidator
+ */
+ private $numberValidator;
+
+ /**
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * @since 2.2
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store, $queryResultValidator, $stringValidator, $numberValidator ) {
+ $this->store = $store;
+ $this->queryResultValidator = $queryResultValidator;
+ $this->stringValidator = $stringValidator;
+ $this->numberValidator = $numberValidator;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param QueryParser $queryParser
+ */
+ public function setQueryParser( QueryParser $queryParser ) {
+ $this->queryParser = $queryParser;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function setDebugMode( $debugMode ) {
+ $this->debug = $debugMode;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param QueryTestCaseInterpreter $queryTestCaseInterpreter
+ */
+ public function processQueryCase( QueryTestCaseInterpreter $queryTestCaseInterpreter ) {
+
+ if ( !$queryTestCaseInterpreter->hasCondition() ) {
+ $this->markTestSkipped( 'Found no condition for ' . $queryTestCaseInterpreter->isAbout() );
+ }
+
+ $description = $this->queryParser->getQueryDescription(
+ $queryTestCaseInterpreter->getCondition()
+ );
+
+ $this->printDescriptionToOutput(
+ $queryTestCaseInterpreter->isAbout(),
+ $description
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = $queryTestCaseInterpreter->getQueryMode();
+ $query->setLimit( $queryTestCaseInterpreter->getLimit() );
+
+ $query->setOffset( $queryTestCaseInterpreter->getOffset() );
+ $query->setExtraPrintouts( $queryTestCaseInterpreter->getExtraPrintouts() );
+
+ $query->setSortKeys( $queryTestCaseInterpreter->getSortKeys() );
+ $query->setContextPage( $queryTestCaseInterpreter->getSubject() );
+
+ if ( $queryTestCaseInterpreter->isRequiredToClearStoreCache() ) {
+ $this->getStore()->clear();
+ }
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->printQueryResultToOutput( $queryResult );
+
+ if ( is_string( $queryResult ) ) {
+ return;
+ }
+
+ $this->assertEquals(
+ $queryTestCaseInterpreter->getExpectedCount(),
+ $queryResult->getCount(),
+ 'Failed asserting query result count on ' . $queryTestCaseInterpreter->isAbout()
+ );
+
+ if ( $queryTestCaseInterpreter->getExpectedErrorCount() > -1 ) {
+ $this->numberValidator->assertThatCountComparesTo(
+ $queryTestCaseInterpreter->getExpectedErrorCount(),
+ $queryResult->getErrors(),
+ 'Failed asserting error count ' . $queryTestCaseInterpreter->isAbout()
+ );
+ }
+
+ if ( $queryTestCaseInterpreter->getExpectedErrorCount() > 0 ) {
+ return null;
+ }
+
+ if ( $queryTestCaseInterpreter->isFromCache() !== null ) {
+ $this->assertEquals(
+ $queryTestCaseInterpreter->isFromCache(),
+ $queryResult->isFromCache(),
+ 'Failed asserting isFromCache for ' . $queryTestCaseInterpreter->isAbout()
+ );
+ }
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $queryTestCaseInterpreter->getExpectedSubjects(),
+ $queryResult,
+ $queryTestCaseInterpreter->isAbout()
+ );
+
+ $this->queryResultValidator->assertThatDataItemIsSet(
+ $queryTestCaseInterpreter->getExpectedDataItems(),
+ $queryResult,
+ $queryTestCaseInterpreter->isAbout()
+ );
+
+ $this->queryResultValidator->assertThatDataValueIsSet(
+ $queryTestCaseInterpreter->getExpectedDataValues(),
+ $queryResult,
+ $queryTestCaseInterpreter->isAbout()
+ );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param QueryTestCaseInterpreter $queryTestCaseInterpreter
+ */
+ public function processConceptCase( QueryTestCaseInterpreter $queryTestCaseInterpreter ) {
+
+ if ( !$queryTestCaseInterpreter->hasCondition() ) {
+ $this->markTestSkipped( 'Found no condition for ' . $queryTestCaseInterpreter->isAbout() );
+ }
+
+ $description = $this->queryParser->getQueryDescription(
+ $queryTestCaseInterpreter->getCondition()
+ );
+
+ $this->printDescriptionToOutput( $queryTestCaseInterpreter->isAbout(), $description );
+
+ $query = new Query(
+ $description,
+ Query::CONCEPT_DESC
+ );
+
+ $query->querymode = $queryTestCaseInterpreter->getQueryMode();
+ $query->setLimit( $queryTestCaseInterpreter->getLimit() );
+ $query->setOffset( $queryTestCaseInterpreter->getOffset() );
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->printQueryResultToOutput( $queryResult );
+
+ $this->assertEquals(
+ $queryTestCaseInterpreter->getExpectedCount(),
+ $queryResult->getCount(),
+ 'Failed asserting query result count on ' . $queryTestCaseInterpreter->isAbout()
+ );
+
+ if ( $queryTestCaseInterpreter->getExpectedErrorCount() > -1 ) {
+ $this->numberValidator->assertThatCountComparesTo(
+ $queryTestCaseInterpreter->getExpectedErrorCount(),
+ $queryResult->getErrors(),
+ 'Failed asserting error count ' . $queryTestCaseInterpreter->isAbout()
+ );
+ }
+
+ foreach ( $queryTestCaseInterpreter->getExpectedConceptCache() as $expectedConceptCache ) {
+
+ $concept = Title::newFromText( $expectedConceptCache['concept'], SMW_NS_CONCEPT );
+
+ $this->getStore()->refreshConceptCache( $concept );
+
+ $this->assertEquals(
+ $expectedConceptCache['count'],
+ $this->getStore()->getConceptCacheStatus( $concept )->getCacheCount(),
+ 'Failed asserting conceptcache count on ' . $queryTestCaseInterpreter->isAbout()
+ );
+ }
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param QueryTestCaseInterpreter $queryTestCaseInterpreter
+ */
+ public function processFormatCase( QueryTestCaseInterpreter $queryTestCaseInterpreter ) {
+
+ if ( $queryTestCaseInterpreter->fetchTextFromOutputSubject() === '' ) {
+ $this->markTestSkipped( 'No content found for ' . $queryTestCaseInterpreter->isAbout() );
+ }
+
+ $textOutput = $queryTestCaseInterpreter->fetchTextFromOutputSubject();
+
+ $this->stringValidator->assertThatStringContains(
+ $queryTestCaseInterpreter->getExpectedFormatOuputFor( 'to-contain' ),
+ $textOutput,
+ $queryTestCaseInterpreter->isAbout()
+ );
+ }
+
+ private function printDescriptionToOutput( $about, $description ) {
+
+ if ( !$this->debug ) {
+ return;
+ }
+
+ print_r( $about . "\n" );
+ print_r( $description );
+ }
+
+ private function printQueryResultToOutput( $queryResult ) {
+
+ if ( is_string( $queryResult ) ) {
+ return print_r( str_replace( [ "&#x0020;", "&#x003A;" ], [ " ", ":" ], $queryResult ) );
+ }
+
+ if ( !$this->debug ) {
+ return;
+ }
+
+ print_r( 'QueryResult' . "\n" );
+ print_r( implode( ',', $queryResult->getQuery()->getErrors() ) );
+ print_r( implode( ',', $queryResult->getErrors() ) );
+ print_r( $queryResult->toArray() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/README.md b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/README.md
new file mode 100644
index 00000000..79cc9e93
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/README.md
@@ -0,0 +1,612 @@
+# JSONScript
+
+JSONScript is an abstraction from the PHPUnit layer and a best practice approach in Semantic MediaWiki to write integration tests as pseudo `JSONScript` to allow non-developers to review and understand the setup and requirements of its test scenarios.
+
+The `JSON` format was selected to lower the barrier of understanding of what is being tested by using wikitext with a schema like structure to provide an abstraction and hide testing specific PHP language elements.
+
+* [List of available test cases](#TestCases)
+* [Designing an integration test](#Designing_an_integration_test)
+* [Technical notes](#Technical_notes)
+
+<!-- Begin of generated contents by readmeContentsBuilder.php -->
+
+## TestCases
+
+Contains 280 files with a total of 1251 tests:
+
+### A
+* [a-0001.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/a-0001.json) Test API `action=smwbrowse`
+* [a-0002.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/a-0002.json) Test API `action=ask` and `action=askargs` with `api_version` 2 + 3
+* [a-0003.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/a-0003.json) Test API `action=smwbrowse`, `browse=pvalue`
+
+### F
+* [f-0001.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0001.json) Test `format=debug` output
+* [f-0101.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0101.json) Test `format=template` output using unnamed arguments (#885)
+* [f-0102.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0102.json) Test `format=template` output + unicode characters (#988, skip postgres)
+* [f-0103.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0103.json) Test `format=template` with self reference (#988, guard against template self-reference in ask/show query)
+* [f-0104.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0104.json) Test `format=list, ul, ol, template` (#2022,`wgContLang=en`, `wgLang=en`)
+* [f-0105.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0105.json) Test `format=list, ul, ol` on `_qty` property (`wgContLang=en`, `SMW_DV_NUMV_USPACE`)
+* [f-0201.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0201.json) Test `format=table` on boolean table output formatting (#896, #1464)
+* [f-0202.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0202.json) Test `format=table` with sep cell formatting, #495 (`wgContLang=en`,`wgLang=en`)
+* [f-0203.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0203.json) Test `format=table` to sort by category (#1286)
+* [f-0204.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0204.json) Test `format=table` on `_qty` for different positional unit preference (#1329, en)
+* [f-0205.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0205.json) Test `format=table` on `|+align=`/`|+limit`/`|+order`/`|+width=` extra printout parameters (T18571, en)
+* [f-0206.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0206.json) Test `format=table` to display extra property description `_PDESC` (en)
+* [f-0207.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0207.json) Test `format=table` on formatted indent when using */#/: (en)
+* [f-0208.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0208.json) Test `format=table` with `limit=0` (further result links) for user/predefined properties, `mainlabel=-`, `#show` (`wgContLang=en`, `wgLang=es`)
+* [f-0209.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0209.json) Test `format=table` on `_tem`/ `_num` with `LOCAL@...` output (#1591, `wgContLang=es`, `wgLang=en`)
+* [f-0210.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0210.json) Test `format=table` on `_qty` for unit labels with spaces (#1718, `wgContLang=en`, `SMW_DV_NUMV_USPACE`)
+* [f-0211.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0211.json) Test `format=plainlist` with `limit=0` (further result links) for `mainlabel/?#...` (#481)
+* [f-0301.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0301.json) Test `format=category` with template usage (#699, en, skip postgres)
+* [f-0302.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0302.json) Test `format=category` and defaultsort (#699, en)
+* [f-0303.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0303.json) Test `format=category` sort output using a template and DEFAULTSORT (#1459, en)
+* [f-0304.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0304.json) Test `format=category` with identity collation sort (#2065, `smwgEntityCollation=identity`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)
+* [f-0305.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0305.json) Test `format=category` with uppercase collation sort (#2065, `smwgEntityCollation=uppercase`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)
+* [f-0306.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0306.json) Test `format=category` with numeric collation sort (same as uppercase, but with numeric sorting) (#2065, `smwgEntityCollation=numeric`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)
+* [f-0307.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0307.json) Test `format=table` with natural printout sorting (n-asc, n-desc)
+* [f-0308.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0308.json) Test `format=table` with DEFAULTSORT and subject,property sorting
+* [f-0401.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0401.json) Test `format=list` output
+* [f-0402.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0402.json)* [f-0801.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0801.json) Test `format=embedded` output
+* [f-0802.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0802.json) Test `format=template` [[SMW::on/off]] regression using `named args=yes` (#1453, skip-on 1.19)
+* [f-0803.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0803.json) Test `format=template` with `sep`/`named args`/`template arguments` (#972, #2022, #2567)
+* [f-0804.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0804.json) Test `format=embedded` with template transclution
+* [f-0805.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/f-0805.json) Test `format=template`, `format=plainlist` with `#show` and template args (#502)
+
+### P
+* [p-0101.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0101.json) Test in-text annotation for use of restricted properties (#914, `wgContLang=en`, `wgLang=en`)
+* [p-0102.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0102.json) Test in-text annotation on properties with invalid names/characters (#1567, #1638, #1727 `wgContLang=en`)
+* [p-0106.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0106.json) Test #info parser output (#1019, `wgContLang=en`, `wgLang=en`)
+* [p-0107.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0107.json) Test #smwdoc parser output
+* [p-0108.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0108.json) Test `#info`, `#ask` template output (#2347, `wgContLang=en`, `wgLang=en`)
+* [p-0109.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0109.json) Test `#info`, `#ask`/`#show` with error output (`wgContLang=en`, `wgLang=en`)
+* [p-0110.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0110.json) Test tooltip with error output on `_PVUC` (`smwgDVFeatures`, `wgContLang=en`, `wgLang=en`)
+* [p-0111.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0111.json) Test reserved property names
+* [p-0202.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0202.json) Test #set parser to use template for output (#1146, en)
+* [p-0203.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0203.json) Test #set parser in combination with #subobject and template output (#1067, regression check)
+* [p-0204.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0204.json) Test #set parser to produce error output (#870, en, verify that #set calls do not affect each other with previous errors)
+* [p-0205.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0205.json) Test #set/#ask recursive annotation support (#711, #1055, recursive annotation using import-annotation=true via template)
+* [p-0206.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0206.json) Test #show parser on inverse printrequest (#1222, #1223)
+* [p-0207.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0207.json) Test that undeclared properties with references remain after a `rebuildData` run (#1216, en)
+* [p-0208.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0208.json) Test `#set` for various `_num` values without explicit precision (3 digit implicit), with/without leading zero, different printouts, negative numbers (#753, en, `smwgMaxNonExpNumber`)
+* [p-0209.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0209.json) Test `#set` for various `_qty` values without explicit precision (3 digit implicit), with/without leading zero, and different printouts (#753, en, `smwgMaxNonExpNumber`)
+* [p-0210.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0210.json) Test `#set_recurring_event` (`wgContLang=en`, `wgLang=en`)
+* [p-0211.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json) Test `#set`/`#subobject` to import annotation via `@json` syntax (`wgContLang=en`, `wgLang=en`)
+* [p-0212.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0212.json) Test `@@@` in-text annotation syntax (#1855, #1875 `wgContLang=en`, `wgLang=en`)
+* [p-0301.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0301.json) Test #subobject category annotation (#1172)
+* [p-0302.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0302.json) Test #subobject parser to use invalid assignments and create `_ERRC` (#1299, en)
+* [p-0303.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0303.json) Test `#subobject` and `#set` parser on values with spaces (`wgContLang=en`, `wgLang=en`)
+* [p-0401.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0401.json) Test annotations with disabled capital links (#673, `wgCapitalLinks=false`)
+* [p-0402.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0402.json) Test in-text parsing for double colon annotation such as `::::` or `:::` (#1066, #1075, en)
+* [p-0403.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0403.json) Test in-text annotations being disabled for when Factbox contains extra `[[ ... ]]` (#1126)
+* [p-0404.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0404.json) Test in-text annonation on different category colon identifier
+* [p-0405.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0405.json) Test in-text annotation via template and manual redirect (#895)
+* [p-0406.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0406.json) Test in-text annotation for unrestricted template parse using `import-annotation=true` (#1055)
+* [p-0407.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0407.json) Test in-text annotation for a redirect that is pointing to a deleted target (#1105)
+* [p-0408.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0408.json) Test in-text annotation for multiple property assignment using non-strict parser mode (#1252, en)
+* [p-0409.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0409.json) Test in-text annotation for `_rec`/`_mlt_rec` (+ subobject) for when record type points to another record type (`wgContLang=en`, `wgLang=en`)
+* [p-0410.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0410.json) Test in-text annotation on `_num`/`_tem`/`_qty` type with denoted precision (`_PREC`) and/or `-p<num>` printout precision marker (#1335, en)
+* [p-0411.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0411.json) Test in-text annotation (and #subobject) using a monolingual property (#1344, en)
+* [p-0412.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0412.json) Test in-text annotation for `_boo` datatype (`wgContLang=ja`, `wgLang=ja`)
+* [p-0413.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0413.json) Test in-text annotation for different `_dat` input/output (en, skip virtuoso, `smwgDVFeatures`)
+* [p-0414.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0414.json) Test in-text annotation/free format for `_dat` datatype (#1389, #1401, en, `smwgDVFeatures`)
+* [p-0415.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0415.json) Test in-text annotation on `_tem` with display unit preference (en)
+* [p-0416.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0416.json) Test in-text annotation with DISPLAYTITLE (#1410, #1611, `wgRestrictDisplayTitle`, `wgContLang=en`, `wgLang=en`)
+* [p-0417.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0417.json) Test in-text annotation for `Allows pattern` to match regular expressions (en)
+* [p-0418.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0418.json) Test in-text annotation using `_SERV` as provide service links (en)
+* [p-0419.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0419.json) Test in-text annotation for `_PVUC` to validate uniqueness (`smwgDVFeatures`)
+* [p-0420.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0420.json) Test in-text annotation for `_dat` using JL/GR annotated values (en, `smwgDVFeatures`)
+* [p-0421.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0421.json) Test in-text annotation with combined constraint validation `_PVUC` and `_PVAL` (`smwgDVFeatures`)
+* [p-0422.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0422.json) Test in-text annotation `_dat` on partial dates (#2076, `wgContLang=en`, `wgLang=en`)
+* [p-0423.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0423.json) Test in-text annotation / `#ask` (#MEDIAWIKI, #LOCL) output for `_dat` datatype (#1545, `wgContLang=en`, `wgLang=ja`)
+* [p-0424.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0424.json) Test in-text annotation for `_boo` datatype using `LOCL` (`wgContLang=en`, `wgLang=fr`, skip-on 1.25.6)
+* [p-0425.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0425.json) Test in-text annotation on `_tem`/ `_num` with different page content language (#1591, `wgContLang=es`, `wgLang=en`)
+* [p-0426.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0426.json) Test in-text annotation for `_num` on big/small numbers/scientific notation (`wgContLang=fr`, `wgLang=en`)
+* [p-0427.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0427.json) Test in-text annotation with DISPLAYTITLE / `foaf` to check on upper vs. lower case (`wgRestrictDisplayTitle`, `wgContLang=en`, `wgLang=en`)
+* [p-0428.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0428.json) Test `_TYPE` annotations on different content language (`wgContLang=fr`, `wgLang=en`)
+* [p-0429.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0429.json) Test in-text `_dat` annotation with time offset, time zone, am/pm (`wgContLang=en`, `wgLang=en`)
+* [p-0430.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0430.json) Test in-text annotation for `_eid` type (`#nowiki`) (`wgContLang=en`, `wgLang=en`)
+* [p-0431.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0431.json) Test in-text annotation `_rec` and `|+index` (`wgContLang=en`, `wgLang=en`)
+* [p-0432.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0432.json) Test in-text annotation for `_ref_rec` type (#1808, `wgContLang=en`, `wgLang=en`)
+* [p-0433.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0433.json) Test in-text annotation `::` with left pipe (#1747, `wgContLang=en`)
+* [p-0434.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0434.json) Test printrequest property chaining `|?Foo.Bar` (#1824, `wgContLang=en`, `wgLang=en`)
+* [p-0435.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0435.json) Test in-text annotation using `_txt` type with 255+ char, `#ask` to produce reduced length (#1878, `wgContLang=en`, `wgLang=en`)
+* [p-0436.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0436.json) Test in-text annotation with `_PPLB` [preferred property label] (#1879, `wgContLang=en`, `wgLang=en`)
+* [p-0437.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0437.json) Test in-text annotation with preferred property label/`_PPLB` (#1879, `wgContLang=en`, `wgLang=ja`)
+* [p-0438.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0438.json) Test in-text annotation with preferred property label/DISPLAYTITLE on user/predefined properties (`wgContLang=es`, `wgLang=de`, `wgRestrictDisplayTitle=false`)
+* [p-0439.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0439.json) Test in-text annotation using '_txt'/'_wpg' type / UTF encoding (`wgContLang=en`, `wgLang=en`)
+* [p-0440.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0440.json) Test in-text annotation `_mlt_rec` (Monolingual text) with `|+lang`/`|+order` parameter (`wgContLang=en`, `wgLang=en`)
+* [p-0441.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0441.json) Test in-text `_txt` 00 string/loose comparison (#2061)
+* [p-0442.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0442.json) Test in-text `#REDIRECT` to verify target subobject isn't removed (#, `wgContLang=en`, `wgLang=en`)
+* [p-0443.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0443.json) Test conditions and strict constraint validations for uniqueness `_PVUC` (#1463, `wgContLang=en`, `wgLang=en`, `smwgDVFeatures`)
+* [p-0444.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0444.json) Test in-text annotation with links in values (#2153, `wgContLang=en`)
+* [p-0445.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0445.json) Test in-text annotation for `_ref_rec` type with errors (#..., `wgContLang=en`)
+* [p-0446.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0446.json) Test in-text annotation `_uri`/`_ema`/`_tel` with spaces/underscore (`wgContLang=en`)
+* [p-0447.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0447.json) Test in-text annotation with IRI export (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=ru`, `wgLang=en`)
+* [p-0448.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0448.json) Test in-text legacy `:=` annotation style (#2153, `wgContLang=en`)
+* [p-0449.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0449.json) Test in-text legacy `:=` and `::` annotation style with enabled links in values (#2153, `wgContLang=en`)
+* [p-0450.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0450.json) Test in-text annotation with invisible chars (`wgContLang=en`)
+* [p-0451.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0451.json) Test in-text `_dat` datatype, time zone, and JD output (#2454, `wgContLang=en`, `wgLang=en`, `smwgDVFeatures=SMW_DV_TIMEV_CM`)
+* [p-0452.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0452.json) Test in-text `_txt` datatype in combination with an "Allows value" output (#2342, `wgContLang=en`, `wgLang=en`)
+* [p-0453.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0453.json) Test in-text `_dat` annotation with `#LOCL#TO` (`wgLocalTZoffset`, `wgContLang=en`, `wgLang=en`)
+* [p-0454.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0454.json) Test in-text annotation with enabled links in values on `&#91;`, `&#93;` (#2671, `wgContLang=en`)
+* [p-0455.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0455.json) Test paser/in-text annotation with unstripped tags (nowiki etc.) (`SMW_PARSER_UNSTRIP`)
+* [p-0456.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0456.json) Test #subobject with assigned sortkey, default order etc.
+* [p-0457.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0457.json) Test named subobject caption display (#2895)
+* [p-0458.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0458.json) Test keyword type `_keyw`
+* [p-0459.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0459.json) Test keyword type `_keyw` with a formatter schema (`smwgCompactLinkSupport`)
+* [p-0460.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0460.json) Test in-text `_num`, `_qty` in combination with an "Allows value" range, bounds
+* [p-0501.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0501.json) Test `#concept` on predefined property (`wgContLang=en`, `wgLang=es`)
+* [p-0502.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0502.json) Test in-text annotation allows value list (#2295, `wgContLang=en`, `wgLang=en`)
+* [p-0503.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0503.json) Test in-text annotation `_uri` on valid/invalid scheme
+* [p-0701.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0701.json) Test to create inverted annotation using a #ask/template combination (#711, `import-annotation=true`)
+* [p-0702.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0702.json) Test #ask with `format=table` on inverse property/printrequest (#1270, #1360)
+* [p-0703.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0703.json) Test `#ask` on `format=table` using different printrequest label output (#1270, `wgContLang=en`, `wgLang=en`)
+* [p-0704.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0704.json) Test `#ask` sanitization of printrequest labels to avoid XSS injection (`wgContLang=en`, `wgLang=en`)
+* [p-0705.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json) Test `#ask`/ NS_FILE option (`wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`, 'wgDefaultUserOptions')
+* [p-0706.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0706.json) Test `#ask` on `format=template` with message parse (`wgContLang=en`, `wgLang=en`)
+* [p-0707.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0707.json) Test `#ask` with enabled execution limit (`wgContLang=en`, `wgLang=en`, `smwgQExpensiveThreshold`, `smwgQExpensiveExecutionLimit`)
+* [p-0708.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0708.json) Test `#ask` NS_FILE and DISPLAYTITLE (`wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`, 'wgDefaultUserOptions', `wgRestrictDisplayTitle`)
+* [p-0709.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0709.json) Test #ask with `format=table` on inverse property, property path
+* [p-0710.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0710.json) Test `#ask` with `[[Category::Foo]]`
+* [p-0711.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0711.json) Test `#ask` with `||` condition (#3473)
+* [p-0901.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0901.json) Test #ask on moved redirected subject (#1086)
+* [p-0902.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0902.json) Test #ask on failed queries to produce a `_ERRC` (#1297, en)
+* [p-0903.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0903.json) Test #ask on redirected printrequest (#1290, en)
+* [p-0904.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0904.json) Test #ask with subject redirected to different NS (en)
+* [p-0905.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0905.json) Test `#ask` query-in-query construct (`_sobj`/`_dat`/`_num`) (`wgContLang=en`, `wgLang=en`)
+* [p-0906.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0906.json) Test `#ask` on category/property hierarchy with circular reference (#1713, `wgContLang=en`, `wgLang=en`, 'smwgEnabledQueryDependencyLinksStore', skip virtuoso)
+* [p-0907.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0907.json) Test the QueryResult cache feature (#1251, `wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`)
+* [p-0908.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0908.json) Test the QueryResult cache feature with different `|+lang`/`|+order` prinrequest parameters (#1251, `wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`)
+* [p-0909.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0909.json) Test the description optimization (`wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`, `smwgQFilterDuplicates=true`, `smwgQueryProfiler`)
+* [p-0910.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0910.json) Test `#ask` to highlight (`#-hl`) search token in result set (#..., `wgContLang=en`, `wgLang=en`)
+* [p-0911.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0911.json) Test the `_ASK` profile (#2270, `smwgQueryProfiler`, `smwgQueryResultCacheType`)
+* [p-0912.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0912.json) Test `#ask` with (`#-raw`) formatter using `#set` (#..., `wgContLang=en`, `wgLang=en`)
+* [p-0913.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0913.json) Test `#ask` with (`#-raw`) formatter with links in values (#..., `wgContLang=en`, `wgLang=en`)
+* [p-0914.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0914.json) Test the description optimization on `_ref_rec` type with property chain query/sort (`wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`, `smwgQFilterDuplicates=true`, `smwgQueryProfiler`)
+* [p-0915.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0915.json) Test category redirect (`SMW_CAT_REDIRECT`)
+* [p-0916.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-0916.json) Test `_ref_rec` with a `_eid` field (#2985)
+* [p-1000.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1000.json) Test property page with redirect(synonym)/displayTitle (`wgContLang=en`, `wgLang=en`, `wgAllowDisplayTitle`)
+* [p-1001.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1001.json) Test property page with parameters (#2479, `wgContLang=en`, `wgLang=en`)
+* [p-1002.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1002.json) Test property page with improper assignment list (`wgContLang=en`, `wgLang=en`)
+* [p-1003.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1003.json) Test property restriction on annotation and #ask (`wgContLang=en`, `wgLang=en`, `smwgCreateProtectionRight`)
+* [p-1004.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1004.json) Test different default output formatter `_dat` (`smwgDefaultOutputFormatters`)
+* [p-1005.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1005.json) Test property page with parameters/sort
+* [p-1006.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1006.json) Test property page sorting (`wgRestrictDisplayTitle`, `smwgEntityCollation`)
+* [p-1007.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/p-1007.json) Test sorting on Pages will not exclude non-existent pages from result (#540)
+
+### Q
+* [q-0101.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0101.json) Test `_txt` query for simple assignments, NS_HELP, and special chars
+* [q-0102.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0102.json) Test `_txt` for `~*` regex queries to validate correct escape pattern as applied in the `QueryEngine`
+* [q-0103.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0103.json) Test `_txt` for `~*` regex query with the condition to include the `\` escape character (skip sqlite, postgres)
+* [q-0104.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0104.json) Test `_txt`/`~` with enabled full-text search support (only enabled for MySQL, SQLite)
+* [q-0105.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0105.json) Test `_wpg`/`~` with enabled full-text search support (only enabled for MySQL, SQLite, `SMW_FT_WIKIPAGE`)
+* [q-0106.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0106.json) Test `_txt`/`~` with enabled full-text search support on fixed user property (only enabled for MySQL, SQLite, `smwgFixedProperties`)
+* [q-0201.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0201.json) Test `_CONC` queries (skip virtuoso)
+* [q-0202.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0202.json) Test `_CONC` for guarding against circular/self-reference which otherwise would fail with 'Maximum function nesting level ... reached, aborting' (#945, skip virtuoso)
+* [q-0203.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0203.json) Test `_CONC` to use `CONCEPT_CACHE_ALL` (#1050, skip all SPARQL repository)
+* [q-0204.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0204.json) Test `_CONC` on predefined inverse query and subobject inverse query (#1096, skip virtuoso)
+* [q-0301.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0301.json) Test `_IMPO` queries for imported foaf vocabulary (#891, en)
+* [q-0401.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0401.json) Test `_SUBP` on a simple 'family' subproperty hierarchy example query (#1003, skip virtuoso)
+* [q-0402.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0402.json) Test `_SUBP` to map DC imported vocabulary with MARC 21 bibliographic terms (#1003, http://www.loc.gov/marc/bibliographic/bd20x24x.html)
+* [q-0501.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0501.json) Test `_qty` queries for custom unit (km²/°C) property value assignments
+* [q-0502.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0502.json) Test `_qty` range queries using non strict comparators (`smwStrictComparators=false`)
+* [q-0503.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0503.json) Test `_qty` on positional unit preference in query condition (#1329, `smwStrictComparators=false`)
+* [q-0601.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0601.json) Test `_wpg` for property chain query queries
+* [q-0602.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0602.json) Test `_wpg` sort query with #subobject annotated @sortkey content
+* [q-0603.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0603.json) Test `_wpg` queries for various conditions using #set annotated content
+* [q-0604.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0604.json) Test `_wpg` queries to resolve property/values redirects (#467, skip virtuoso)
+* [q-0605.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0605.json) Test `_wpg` regex search (`!~/~*/~?`) queries (#679)
+* [q-0606.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0606.json) Test `_wpg`/`_num`/`_txt` using subqueries (#466, #627, #625)
+* [q-0607.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0607.json) Test `_wpg`/`_dat`/`_num`/`_txt` subquery example
+* [q-0608.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0608.json) Test `_wpg` for single value approximate (`~/!~`) queries (#1246)
+* [q-0609.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0609.json) Test `_wpg` for single value approximate (`~/!~`) queries with conjunctive category hierarchy (#1246, en, skip virtuoso)
+* [q-0610.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0610.json) Test `_wpg` range queries (#1291, `smwStrictComparators=false`, skip virtuoso)
+* [q-0611.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0611.json) Test `_wpg` namespace any value queries (#1301, en)
+* [q-0612.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0612.json) Test `_wpg` object value that contains `=` (equals sign) (#640, #710, #1542, #1645, `wgContLang=en`, `wgLang=en`)
+* [q-0613.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0613.json) Test single value (`~/!~`/`<`/`>`) queries on namespaced entity (#1652, `NS_HELP`, `smwStrictComparators=false`, skip-on virtuoso)
+* [q-0614.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0614.json) Test query with category hierarchy depth (#2662, `wgContLang=en`, `smwgQSubpropertyDepth`, `smwgQSubcategoryDepth`, skip virtuoso)
+* [q-0615.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0615.json) Test query with property hierarchy depth (#2662, `wgContLang=en`, `smwgQSubpropertyDepth`, `smwgQSubcategoryDepth`, skip virtuoso)
+* [q-0616.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0616.json) Test `in:` syntax on `_txt`, `_dat`, and `_num` values
+* [q-0617.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0617.json) Test range `<>` syntax on `_num` (float,double), `_dat` (millisec) values (`smwStrictComparators=true`)
+* [q-0618.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0618.json) Test deep subqueries (Friends of friends) (`smwgQMaxDepth`)
+* [q-0619.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0619.json) Test `_wpg` user case (#2982)
+* [q-0620.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0620.json) Test `_wpg` and category using subquery construct
+* [q-0621.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0621.json) Test `_wpg` and namespace using subquery construct
+* [q-0622.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0622.json) Test query with category hierarchy
+* [q-0701.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0701.json) Test `_uri` with some annotation/search pattern (T45264, #679)
+* [q-0702.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0702.json) Test `_uri` with additional annotation/search (#1129)
+* [q-0703.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0703.json) Test to map `Foaf` property from back-end / using a localized predefined property `A le type@fr` (en)
+* [q-0704.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0704.json) Test `_uri` long URL (255+) (#1872)
+* [q-0801.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0801.json) Test `_INST` query (#1004, en)
+* [q-0802.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0802.json) Test `_INST`/`_SUBC` queries (#1005, en, skip virtuoso)
+* [q-0803.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0803.json) Test `_INST`/ Nested category annotation (#1012, en, skip virtuoso) category hierarchy queries
+* [q-0804.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0804.json) Test `_INST` with namespace prefix
+* [q-0901.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0901.json) Test `_wpg`/`_txt` on various disjunction, conjunction queries (#19, #1060, #1056, #1057)
+* [q-0902.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0902.json) Test `_txt` to correctly apply parentheses for somehting like (a OR b OR c) AND d (#556)
+* [q-0903.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0903.json) Test `_wpg`/`_num`/`_txt` for disjunction OR || (T31866, #1059, en)
+* [q-0904.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0904.json) Test `_wpg`/`_txt` disjunction in connection with property hierarchies (#1060, en, skip virtuoso)
+* [q-0905.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0905.json) Test `_wpg`/`_txt` conjunction queries (#1362, #1060)
+* [q-0906.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0906.json) Test `_wpg`/`_txt` with enabled `SMW_FIELDT_CHAR_NOCASE` (#1912, `smwgFieldTypeFeatures`)
+* [q-0907.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0907.json) Test `_txt`/`_uri` with enabled `SMW_FIELDT_CHAR_LONG | SMW_FIELDT_CHAR_NOCASE` (#1912, #2499, `smwgFieldTypeFeatures`)
+* [q-0908.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0908.json) Test `_wpg`/`_txt`/`_uri` on enabled `SMW_FIELDT_CHAR_LONG | SMW_FIELDT_CHAR_NOCASE` with `like:/nlike:` (#1912, #2499, `smwgFieldTypeFeatures`, `smwgSparqlQFeatures`)
+* [q-0909.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0909.json) Test `_txt/`_uri`/`_num`/`_dat` with `!...` (NEQ)
+* [q-0910.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0910.json) Test `SMW_QSORT_UNCONDITIONAL` (`smwgQSortFeatures`, skip-on all SPARQL repositories, postgres)
+* [q-0911.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-0911.json) Test `_wpg` empty chain/subquery (AND, OR)
+* [q-1002.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1002.json) Test `_dat` range for non strict comparators (#285, `smwStrictComparators=false`, skip virtuoso)
+* [q-1003.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1003.json) Test `_dat` range for strict comparators (#285, `smwStrictComparators=true`, skip virtuoso)
+* [q-1004.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1004.json) Test `_dat` range for `~`/`!~` comparators (#1178, `smwStrictComparators=false`, skip virtuoso)
+* [q-1101.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1101.json) Test _rec for non strict comparators queries (`smwStrictComparators=false`)
+* [q-1102.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1102.json) Test `_rec` queries in combination with `_dat` `~/!~` search pattern (#1178, `smwStrictComparators=false`, skip virtuoso)
+* [q-1103.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1103.json) Test `_rec` using some additional search pattern (#1189, en)
+* [q-1104.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1104.json) Test `_rec` to find correct target for redirected property (#1244, en)
+* [q-1105.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1105.json) Test `_rec` in combination with named subobject (T49472, #1300, en, `smwStrictComparators=false`)
+* [q-1106.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1106.json) Test `_rec` with `~/!~` comparators on allowed values (#1207, `smwStrictComparators=false`)
+* [q-1107.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1107.json) Test `_rec`/`_mlt_rec`(`_PDESC`) to use property chaining (`wgContLang=en`)
+* [q-1108.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1108.json) Test conditions and constraint validations for allowed values `_LIST` and uniqueness `_PVUC` (#1207, `wgContLang=en`, `wgLang=en`)
+* [q-1200.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1200.json) Test `_wpg/`_txt` with `~*` and `.../...` queries (ES only)
+* [q-1201.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1201.json) Test `_wpg/`_txt` with `not:`/`!~` queries (ES only)
+* [q-1202.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1202.json) Test `_wpg/`_txt` with `not:`/`!~` queries (ES only, `raw.text`)
+* [q-1203.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1203.json) Test `_wpg/`_txt` with `in:/phrase:` queries (ES only)
+* [q-1204.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1204.json) Test `!` category queries (ES only, `smwgQSubcategoryDepth`)
+* [q-1205.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1205.json) Test `[[Has subobject::!]]` / `[[Has subobject::!+]]` (ES only)
+* [q-1206.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1206.json) Test `cjk.best.effort.proximity.match` (ES only)
+* [q-1300.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/q-1300.json) Test `_geo` (requires Maps)
+
+### R
+* [r-0001.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0001.json) Test RDF output for `_txt`/`_wpg`/`_dat` (#881)
+* [r-0002.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0002.json) Test RDF output for redirected pages (#882)
+* [r-0003.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0003.json) Test RDF output for imported foaf vocabulary (#884, en)
+* [r-0004.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0004.json) Test RDF output generation for `_INST`/`_SUBC` pages (#922, en)
+* [r-0005.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0005.json) Test RDF wiki-info output (#928, en)
+* [r-0006.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0006.json) Test RDF output generation for pages that contain `_rec` annotations (#1285, #1275)
+* [r-0007.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0007.json) Test RDF output for imported dc/gna vocabulary, owl:AnnotationProperty, owl:DatatypeProperty, owl:ObjectProperty, Equivalent URI (#795, `wgRestrictDisplayTitle`, en)
+* [r-0008.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0008.json) Test RDF output generation on pages that contain incoming error annotations (`wgContLang=en`, `wgLang=es`, syntax=rdf/turtle)
+* [r-0009.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0009.json) Test RDF output generation that contain a monolingual text annotations `_PDESC` (`wgContLang=en`, `wgLang=es`, syntax=rdf/turtle)
+* [r-0010.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0010.json) Test RDF output on canonical entities (`wgContLang=fr`, `wgLang=es`, syntax=rdf/turtle)
+* [r-0011.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0011.json) Test RDF output generation `skos` import/`skos:altLabel` as Monolingual text (`wgContLang=en`, `wgLang=en`)
+* [r-0012.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0012.json) Test RDF output generation on SubSemanticData traversal (#2177, `wgContLang=en`, `wgLang=en`)
+* [r-0013.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0013.json) Test RDF output generation `_uri`/`_ema`/`_tel` with spaces/underscore (`wgContLang=en`, `wgLang=en`)
+* [r-0014.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0014.json) Test RDF output generation on non-latin URI/IRI export (#2188, `smwgExportResourcesAsIri=false`, `wgContLang=ru`, `wgLang=en`)
+* [r-0015.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0015.json) Test RDF output generation on non-latin URI/IRI export (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=ru`, `wgLang=en`)
+* [r-0016.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0016.json) Test RDF output generation with special characters (#2188, `smwgExportResourcesAsIri=false`, `wgContLang=en`, `wgLang=en`)
+* [r-0017.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0017.json) Test RDF output generation with special characters (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=en`, `wgLang=en`)
+* [r-0018.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0018.json) Test RDF output generation with special characters (`smwgExportResourcesAsIri=true`, `wgContLang=en`, `wgLang=en`)
+* [r-0019.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0019.json) Test RDF output on `swivt:sort` with enabled collation (#2065, `smwgEntityCollation=uppercase`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`, `wgContLang=en`, `wgLang=en`)
+* [r-0020.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/r-0020.json) Test RDF output on `/` in porperty name (#3134)
+
+### S
+* [s-0001.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0001.json) Test output of `Special:Properties` (`wgContLang=en`, skip-on sqlite)
+* [s-0002.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0002.json) Test output from `Special:SearchByProperty` for `_num`, `_txt`, `_tel` (#1728, #2009, `wgContLang=en`, `wgLang=en`, skip-on sqlite, postgres)
+* [s-0003.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0003.json) Test `Special:Ask` output for `format=rdf`/`format=json`/DISPLAYTITLE (#1453, #1619, `wgRestrictDisplayTitle`, `wgContLang=en`)
+* [s-0004.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0004.json) Test `Special:Browse` output for `_dat` (`wgContLang=en`, `wgLang=ja`)
+* [s-0005.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0005.json) Test `Special:Browse` output for `_dat`, '_REDI' (`wgContLang=en`, `wgLang=en`, `smwgDVFeatures=SMW_DV_TIMEV_CM | SMW_DV_WPV_DTITLE`, `wgRestrictDisplayTitle=false`)
+* [s-0006.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0006.json) Test output of `Special:WantedProperties` (`wgContLang=en`, `wgLang=en`, skip-on sqlite)
+* [s-0007.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0007.json) Test output of `Special:UnusedProperties` (`wgContLang=en`, `wgLang=en`, skip-on sqlite, 1.19)
+* [s-0008.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0008.json) Test `Special:Browse` output for `_dat`, `_boo`, `_sobj`, `_uri` (`wgContLang=en`, `wgLang=es`, skip-on 1.25.6)
+* [s-0009.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0009.json) Test output in `Special:Search` for SMWSearch (`wgLanguageCode=en`, `wgContLang=en`, `wgSearchType=SMWSearch`)
+* [s-0010.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0010.json) Test output from `Special:SearchByProperty` / `_dat` (#1922, `wgContLang=en`, `wgLang=es`, skip-on sqlite)
+* [s-0011.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0011.json) Test `Special:Ask` output `#ask` intro/outro link/template parse (`wgContLang=en`, `wgLang=en`)
+* [s-0012.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0012.json) Test `Special:Ask` output `#ask` image/upload (#2009, `wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`)
+* [s-0013.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0013.json) Test `Special:Browse` output preferred label (`wgContLang=en`, `wgLang=es`)
+* [s-0014.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0014.json) Test `Special:Browse` with special characters `%'"&` (`wgContLang=en`, `wgLang=es` )
+* [s-0015.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0015.json) Test `Special:Ask` output for `_txt` with formatted text (#..., `wgContLang=en`, `wgLang=en`)
+* [s-0016.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0016.json) Test `Special:Ask` to produce correct printout position for `+|...` parameters (`wgContLang=en`, `wgLang=en`)
+* [s-0017.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0017.json) Test `Special:Types` (`wgContLang=en`, `wgLang=en`)
+* [s-0018.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0018.json) Test `Special:Ask` common output (`wgContLang=en`, `wgLang=en`)
+* [s-0019.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0019.json) Test output of `Special:WantedProperties` on unapproved property (`wgContLang=en`, `wgLang=en`, `smwgCreateProtectionRight`)
+* [s-0020.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0020.json) Test `Special:Ask` with `format=json` output (`wgContLang=en`, `wgLang=en`)
+* [s-0021.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0021.json) Test `format=table` on `Special:Ask` with `headers=plain` (#2702, `wgContLang=en`, `wgLang=en`)
+* [s-0022.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0022.json) Test `format=csv` output via `Special:Ask` (`wgContLang=en`, `wgLang=en`)
+* [s-0023.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0023.json) Test `Special:Browse` output category (`wgContLang=en`, `wgLang=en`)
+* [s-0024.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0024.json) Test `Special:Browse` with compact links (`smwgCompactLinkSupport`)
+* [s-0025.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0025.json) Test `format=templatefile` (with `_eid`) output via `Special:Ask`
+* [s-0026.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0026.json) Test output from `Special:PageProperty` (with `_dat`)
+* [s-0027.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0027.json) Test `format=feed` output via `Special:Ask` (`wgEnableUploads`, `wgFileExtensions`, `wgRestrictDisplayTitle`)
+* [s-0028.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0028.json) Test `Special:Browse` limited value list
+* [s-0029.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0029.json) Test `Special:Ask` output on `mainlabel=.../?#...`, `format=table`
+* [s-0030.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases/s-0030.json) Test `Special:Concepts`
+
+-- Last updated on 2018-09-30 by `readmeContentsBuilder.php`
+
+<!-- End of generated contents by readmeContentsBuilder.php -->
+
+## Designing an integration test
+
+The `JSONScript` follows the arrange, act, assert approach, with the `setup` section containing object definitions that are planned to be used during a test. The section expects that an entity page and its contents (generally the page content in wikitext, annotations etc.) to follow a predefined structure.
+
+It is also possible to [import](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json) larger text passages or [upload files](https://github.com/SemanticMediaWiki/SemanticMediaWiki/blob/master/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json) for a test scenario.
+
+When creating test scenarios, use disinct names and subjects to ensure that other tests will not interfer with the expected results. It may also be of advantage to split the setup of data (e.g. `Example/Test/1`) from the actual test subject (e.g. `Example/Test/Q.1`) to avoid conflicating comparisons or false positive results during the assertion process.
+
+<pre>
+"setup": [
+ {
+ "page": "Has text",
+ "namespace":"SMW_NS_PROPERTY",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Property:Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/Test/1",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has text::Some text to search]]"
+ },
+ {
+ "page": "Example/Test/Q.1",
+ "namespace":"NS_MAIN",
+ "contents": "{{#ask: [[Has text::~Some text*]] |?Has text }}"
+ }
+],
+</pre>
+
+The [bootstrap.json](https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/bootstrap.json) contains an example that can be used as starting point for a new test case.
+
+### Test assertions
+
+* The `type` provides specialized assertion methods with some of them requiring
+an extra setup to yield a comparable output but in most cases the `parser` type
+should suffice to create test assertions for common test scenarios. Available types
+are:
+ * `query`, `concept`, and `format`
+ * `parser`
+ * `parser-html`
+ * `rdf`
+ * `special`
+* The `about` describes what the test is expected to test which may help during
+ a failure to identify potential conflicts or hints on how to resolve an issue.
+* The `subject` refers to the page that was defined in the `setup` section.
+
+For example, as of version 2 the `parser` type (`ParserTestCaseProcessor`) knows
+two assertions methods:
+
+- `assert-store` is to validate data against `Store::getSemanticData`
+- `assert-output` is to validate string comparison against the `ParserOutput`
+ generated text
+
+
+#### Type `parser`
+The test result assertion provides simplified string comparison methods (mostly for
+output related assertion but expressive enough for users to understand the test
+objective and its expected results). For example, verifying that the parser
+does output a certain string, one has to the define an expected output.
+
+<pre>
+"tests": [
+ {
+ "type": "parser",
+ "about": "#0 test output of the [[ ... ]] annotation",
+ "subject": "Example/Test/1",
+ "assert-output": {
+ "to-contain": [
+ "Some text to search"
+ ],
+ "not-contain": [
+ "abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 test output of #ask query",
+ "subject": "Example/Test/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "Some text to search"
+ ],
+ "not-contain": [
+ "abc"
+ ]
+ }
+ }
+]
+</pre>
+
+#### Type `parser-html`
+
+To verify that the HTML code produced by the parser conforms to a certain
+structure the test type `parser-html` may be used. With this type the expected
+output structure may be specified as a CSS selector. The test will succeed if at
+least one element according to that selector is found in the output.
+
+Example:
+<pre>
+"tests": [
+ {
+ "type": "parser-html",
+ "about": "#0 Basic List format",
+ "subject": "Example/0401",
+ "assert-output": {
+ "to-contain": [
+ "p > a[ title='Bar' ] + a[ title='Baz' ] + a[ title='Foo' ] + a[ title='Quok' ]"
+ ]
+ }
+ }
+]
+</pre>
+
+For further details and limitations on the CSS selectors see the [description of
+the Symfony CssSelector
+Component](https://symfony.com/doc/current/components/css_selector.html) that is
+used for this test type.
+
+It is also possible to require an exact number of occurences of HTML elements by
+providing an array instead of just a CSS selector string.
+
+Example:
+<pre>
+ "assert-output": {
+ "to-contain": [
+ [ "p > a", 4 ]
+ ]
+ }
+</pre>
+
+Finally the general well-formedness of the HTML can be tested, although this
+will not fail for recoverable errors (see the [documentation on PHP's
+DOMDocument::loadHTML](http://php.net/manual/en/domdocument.loadhtml.php#refsect1-domdocument.loadhtml-errors)).
+
+Example:
+<pre>
+ "assert-output": {
+ "to-be-valid-html": true,
+ }
+</pre>
+
+
+### Preparing the test environment
+
+It can happen that an output is mixed with language dependent content (site vs.
+page content vs. user language) and therefore it is recommended to fix those
+settings for a test by adding something like:
+
+<pre>
+"settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+}
+</pre>
+
+By default not all settings parameter are enabled in `JsonTestCaseScriptRunner::prepareTest`
+and may require an extension in case a specific test case depends on additional
+customization.
+
+Each `json` file expects a `meta` section with:
+
+- `version` to correspond to the
+ `JsonTestCaseScriptRunner::getRequiredJsonTestCaseMinVersion` and controls the
+ JSON script definition that the runner is expected to support.
+- `is-incomplete` removes the file from the test plan if set `true`
+- `debug` as flag for support of intermediary debugging that may output internal
+ object state information.
+
+<pre>
+"meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+}
+</pre>
+
+### Define a dependency
+
+Some test scenarios may require an extension or another component and to check those dependencies before the actual test is run, use `requires` as in:
+
+<pre>
+"requires": {
+ "Maps": ">= 5.0"
+},
+</pre>
+
+### Skipping a test or mark as incomplete
+
+Sometimes certain data can cause inconsistencies with an environment hence it is
+possible to skip those cases by adding:
+
+<pre>
+{
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/11",
+ "contents": "[[Has date::Jan 1 300 BC]]"
+},
+</pre>
+
+<pre>
+{
+ "skip-on": {
+ "hhvm-*": "HHVM (or SQLite) shows opposite B1000, B9",
+ "mediawiki": [ ">1.30.x", "MediaWiki changed ..." ],
+ "smw": [ ">2.5.x", "SMW changed ..." ]
+ }
+}
+</pre>
+
+Constraints that include `hhvm-*` will indicate to exclude all HHVM versions while
+`>1.30.x` defines that any MW version greater than 1.30 should be ignored.
+
+It is also possible that an entire test scenario cannot be completed in a particular
+environment therefore it can be marked and skipped with:
+
+<pre>
+"meta": {
+ "skip-on": {
+ "virtuoso": "Some info as to why it is skipped.",
+ "sqlite": "...",
+ "postgres": "..."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+}
+</pre>
+
+If a test is incomplete for some reason, use the `is-incomplete` field to indicate
+the status which henceforth avoids a test execution.
+
+### File naming
+
+The naming of a test file is arbitrary but it has been a best practice to indicate
+the type of test expected to be executed. For example, `s-0001.json` would indicate that the
+test is mostly concerned with special pages while `p-0001.json` is to handle
+parser output related assertions.
+
+### Debugging and running a test
+
+Generally, tests are run together with the `composer phpunit` execution but
+it may not always be feasible especially when trying to debug or design a new test
+case.
+
+There are two methods that can help restrict the execution during the design or
+debug phase:
+
+* Modify the `JsonTestCaseScriptRunner::getAllowedTestCaseFiles` to take an argument
+such as a file name ( e.g. `s-0014.json`) to restrict the execution of a test which
+is mostly done when running from an IDE editor
+* The command line allows to invoke a filter argument to specify a case such as
+`composer integration -- --filter 's-0014.json'`
+
+<pre>
+$ composer test -- --filter 's-0014.json'
+Using PHP 5.6.8
+
+Semantic MediaWiki: 2.5.0-alpha (SMWSQLStore3, mysql)
+MediaWiki: 1.28.0-alpha (MediaWiki vendor autoloader)
+Site language: en
+
+Execution time: 2017-01-01 12:00
+Debug logs: Enabled
+Xdebug: Disabled (or not installed)
+
+phpunit 4.8.24 by Sebastian Bergmann and contributors.
+
+Runtime: PHP 5.6.8
+Configuration: ...\extensions\SemanticMediaWiki\phpunit.xml.dist
+
+.
+
+Time: 13.02 seconds, Memory: 34.00Mb
+
+OK (1 test, 16 assertions)
+</pre>
+
+The following [video](https://youtu.be/7fDKjPFaTaY) contains a very brief introduction on how
+to run and debug a JSONScript test case. For a general introduction to the test environment,
+have a look at the following [readme](https://github.com/SemanticMediaWiki/SemanticMediaWiki/edit/master/tests/README.md).
+
+## Technical notes
+
+* The `JSON` is internally transformed into a corresponding `PHPUnit` dataset with
+the help of the `JsonTestCaseContentHandler` and `JsonTestCaseScriptRunner`.
+* A test file (e.g "myTest.json") will be loaded from the specified location in
+`JsonTestCaseScriptRunner::getTestCaseLocation` and is automatically run during
+the `PHPUnit` test execution.
+* The `readmeContentsBuilder.php` can be used to update the list of available test
+cases including its descriptions.
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/RdfTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/RdfTestCaseProcessor.php
new file mode 100644
index 00000000..e9ced771
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/RdfTestCaseProcessor.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use SMWExportController as ExportController;
+use SMWRDFXMLSerializer as RDFXMLSerializer;
+use SMWTurtleSerializer as TurtleSerializer;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class RdfTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var StringValidator
+ */
+ private $stringValidator;
+
+ /**
+ * @var RunnerFactory
+ */
+ private $runnerFactory;
+
+ /**
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * @param Store
+ * @param StringValidator
+ */
+ public function __construct( $store, $stringValidator, $runnerFactory ) {
+ $this->store = $store;
+ $this->stringValidator = $stringValidator;
+ $this->runnerFactory = $runnerFactory;
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function setDebugMode( $debugMode ) {
+ $this->debug = $debugMode;
+ }
+
+ public function process( array $case ) {
+
+ // Allows for data to be re-read from the DB instead of being fetched
+ // from the store-id-cache
+ if ( isset( $case['store']['clear-cache'] ) && $case['store']['clear-cache'] ) {
+ $this->store->clear();
+ }
+
+ if ( isset( $case['dumpRDF'] ) ) {
+ $this->assertDumpRdfOutputForCase( $case );
+ }
+
+ if ( isset( $case['exportcontroller'] ) ) {
+ $this->assertExportControllerOutputForCase( $case );
+ }
+ }
+
+ private function assertDumpRdfOutputForCase( $case ) {
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\DumpRdf' );
+ $maintenanceRunner->setQuiet();
+
+ $maintenanceRunner->setOptions( $case['dumpRDF']['parameters'] );
+ $maintenanceRunner->run();
+
+ $this->assertOutputForCase(
+ $case,
+ $maintenanceRunner->getOutput()
+ );
+ }
+
+ private function assertExportControllerOutputForCase( $case ) {
+
+ if ( isset( $case['exportcontroller']['syntax'] ) && $case['exportcontroller']['syntax'] === 'turtle' ) {
+ $serializer = new TurtleSerializer();
+ } else {
+ $serializer = new RDFXMLSerializer();
+ }
+
+ $exportController = new ExportController( $serializer );
+ $exportController->enableBacklinks( $case['exportcontroller']['parameters']['backlinks'] );
+
+ ob_start();
+
+ if ( isset( $case['exportcontroller']['print-pages'] ) ) {
+ $exportController->printPages(
+ $case['exportcontroller']['print-pages'],
+ (int)$case['exportcontroller']['parameters']['recursion'],
+ $case['exportcontroller']['parameters']['revisiondate']
+ );
+ }
+
+ if ( isset( $case['exportcontroller']['wiki-info'] ) ) {
+ $exportController->printWikiInfo();
+ }
+
+ $output = ob_get_clean();
+
+ $this->assertOutputForCase( $case, $output );
+ }
+
+ private function assertOutputForCase( $case, $output ) {
+
+ if ( $this->debug ) {
+ print_r( $output );
+ }
+
+ if ( isset( $case['assert-output']['to-contain'] ) ) {
+ $this->stringValidator->assertThatStringContains(
+ $case['assert-output']['to-contain'],
+ $output,
+ $case['about']
+ );
+ }
+
+ if ( isset( $case['assert-output']['not-contain'] ) ) {
+ $this->stringValidator->assertThatStringNotContains(
+ $case['assert-output']['not-contain'],
+ $output,
+ $case['about']
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/SpecialPageTestCaseProcessor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/SpecialPageTestCaseProcessor.php
new file mode 100644
index 00000000..60c4e694
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/SpecialPageTestCaseProcessor.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+use FauxRequest;
+use Language;
+use OutputPage;
+use RequestContext;
+use SMW\Tests\Utils\File\ContentsReader;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use SpecialPage;
+use SpecialPageFactory;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class SpecialPageTestCaseProcessor extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var StringValidator
+ */
+ private $stringValidator;
+
+ /**
+ * @var boolean
+ */
+ private $debug = false;
+
+ /**
+ * @var string
+ */
+ private $testCaseLocation = '';
+
+ /**
+ * @param Store
+ * @param StringValidator
+ */
+ public function __construct( $store, $stringValidator ) {
+ $this->store = $store;
+ $this->stringValidator = $stringValidator;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function setDebugMode( $debugMode ) {
+ $this->debug = $debugMode;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $testCaseLocation
+ */
+ public function setTestCaseLocation( $testCaseLocation ) {
+ $this->testCaseLocation = $testCaseLocation;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $case
+ */
+ public function process( array $case ) {
+
+ if ( !isset( $case['special-page'] ) ) {
+ return;
+ }
+
+ if ( isset( $case['special-page']['query-parameters'] ) ) {
+ $queryParameters = $case['special-page']['query-parameters'];
+ } else {
+ $queryParameters = [];
+ }
+
+ $text = $this->getTextForRequestBy(
+ SpecialPageFactory::getPage( $case['special-page']['page'] ),
+ new FauxRequest( $case['special-page']['request-parameters'] ),
+ $queryParameters
+ );
+
+ $this->assertOutputForCase( $case, $text );
+ }
+
+ private function getTextForRequestBy( $page, $request, $queryParameters ) {
+ $response = $request->response();
+
+ $page->setContext( $this->makeRequestContext(
+ $request,
+ new MockSuperUser,
+ $this->getTitle( $page )
+ ) );
+
+ $out = $page->getOutput();
+
+ ob_start();
+ $page->execute( $queryParameters );
+
+ if ( $out->getRedirect() !== '' ) {
+ $out->output();
+ $text = ob_get_contents();
+ } elseif ( $out->isDisabled() ) {
+ $text = ob_get_contents();
+ } else {
+ $text = $out->getHTML();
+ }
+
+ ob_end_clean();
+
+ $code = $response->getStatusCode();
+
+ if ( $code > 0 ) {
+ $response->header( "Status: " . $code . ' ' . \HttpStatus::getMessage( $code ) );
+ }
+
+ return $text;
+ }
+
+ private function assertOutputForCase( $case, $text ) {
+
+ // Avoid issue with \r carriage return and \n new line
+ $text = str_replace( "\r\n", "\n", $text );
+
+ if ( isset( $case['assert-output']['to-contain'] ) ) {
+
+ if ( isset( $case['assert-output']['to-contain']['contents-file'] ) ) {
+ $contents = ContentsReader::readContentsFrom(
+ $this->testCaseLocation . $case['assert-output']['to-contain']['contents-file']
+ );
+ } else {
+ $contents = $case['assert-output']['to-contain'];
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $contents,
+ $text,
+ $case['about']
+ );
+ }
+
+ if ( isset( $case['assert-output']['not-contain'] ) ) {
+
+ if ( isset( $case['assert-output']['not-contain']['contents-file'] ) ) {
+ $contents = ContentsReader::readContentsFrom(
+ $this->testCaseLocation . $case['assert-output']['not-contain']['contents-file']
+ );
+ } else {
+ $contents = $case['assert-output']['not-contain'];
+ }
+
+ $this->stringValidator->assertThatStringNotContains(
+ $contents,
+ $text,
+ $case['about']
+ );
+ }
+ }
+
+ /**
+ * @return RequestContext
+ */
+ private function makeRequestContext( \WebRequest $request, $user, $title ) {
+
+ $context = new RequestContext();
+ $context->setRequest( $request );
+
+ $out = new OutputPage( $context );
+ $out->setTitle( $title );
+
+ $context->setOutput( $out );
+ $context->setLanguage( Language::factory( $GLOBALS['wgLanguageCode'] ) );
+
+ $user = $user === null ? new MockSuperUser() : $user;
+ $context->setUser( $user );
+
+ return $context;
+ }
+
+ /**
+ * Deprecated: Use of SpecialPage::getTitle was deprecated in MediaWiki 1.23
+ *
+ * @return Title
+ */
+ private function getTitle( SpecialPage $page ) {
+ return method_exists( $page, 'getPageTitle') ? $page->getPageTitle() : $page->getTitle();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0001.json
new file mode 100644
index 00000000..df05b93f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0001.json
@@ -0,0 +1,301 @@
+{
+ "description": "Test API `action=smwbrowse`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API test property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API text property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API date property",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "API test concept",
+ "contents": "..."
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "API test category",
+ "contents": "..."
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/1",
+ "contents": "[[API text property::text 1]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/2",
+ "contents": "[[API text property::text 2]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/3",
+ "contents": "[[API page property::page 1]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/4",
+ "contents": "[[API page property::page 2]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/5",
+ "contents": "[[API date property::1 Jan 1970]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0001/6",
+ "contents": "[[API date property::2 Jan 1971]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "api",
+ "about": "#0 `smwbrowse` property search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "property",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"search\": \"API test\", \"description\": true, \"prefLabel\": true }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.0.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#1 `smwbrowse` concept search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "concept",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"search\": \"API test\", \"description\": true, \"prefLabel\": true }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.1.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#2 `smwbrowse` category search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "category",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"search\": \"API test\", \"description\": true, \"prefLabel\": true }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.2.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "skip-on": {
+ "postgres": "Skipping, because I have no idea why this returning empty on Travis!",
+ "sqlite": "Skipping, because I have no idea why this returning empty on Travis!"
+ },
+ "about": "#3 `smwbrowse` page search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "page",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"search\": \"API test\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.3.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "skip-on": {
+ "sqlite": "Skipping, because I have no idea why this returning empty during the test!"
+ },
+ "about": "#4 `smwbrowse` pvalue (text type) search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "pvalue",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API text property\", \"search\": \"tex\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.4.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#5 `smwbrowse` pvalue (page type) search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "pvalue",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API page property\", \"search\": \"pag\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.5.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "skip-on": {
+ "sqlite": "Skipping, because I have no idea why this is returning empty during the test!"
+ },
+ "about": "#6 `smwbrowse` pvalue (date type) search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "pvalue",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API date property\", \"search\": \"197\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/a-0001.6.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#7 `smwbrowse` subject lookup, JSON",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "subject",
+ "params": "{ \"subject\": \"A0001/1\" , \"ns\": 0 }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0001.7.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#8 `smwbrowse` subject lookup, HTML",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "subject",
+ "params": "{ \"subject\": \"A0001/1\" , \"ns\": 0 , \"type\": \"html\", \"options\": {} }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0001.8.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#9 `smwbrowse` (distinct value, empty search) psubject search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "psubject",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API page property\", \"value\": \"Page 1\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0001.9.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#10 `smwbrowse` (empty value, empty search) psubject search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "psubject",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API page property\", \"value\": \"\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0001.10.txt"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#11 `smwbrowse` (empty value, distinct search) psubject search",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "psubject",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"API page property\", \"value\": \"\", \"search\": \"/4\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0001.11.txt"
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCacheUsage": {
+ "api.browse": false,
+ "api.browse.pvalue": false,
+ "api.browse.psubject": false
+ },
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_CONCEPT": true,
+ "NS_CATEGORY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0002.json
new file mode 100644
index 00000000..f8de5b1a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0002.json
@@ -0,0 +1,106 @@
+{
+ "description": "Test API `action=ask` and `action=askargs` with `api_version` 2 + 3",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API test page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "API test text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/A0002/1",
+ "contents": "[[API test page::123]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "api",
+ "about": "#0 `ask` version 2",
+ "api": {
+ "parameters": {
+ "action": "ask",
+ "format": "json",
+ "query": "[[API test page::123]] |?API test page",
+ "api_version": "2"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0002.0.json"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#1 `ask` version 3",
+ "api": {
+ "parameters": {
+ "action": "ask",
+ "format": "json",
+ "query": "[[API test page::123]] |?API test page",
+ "api_version": "3"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0002.1.json"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#2 `askargs` version 2",
+ "api": {
+ "parameters": {
+ "action": "askargs",
+ "format": "json",
+ "conditions": "API test page::123",
+ "printouts": "API test page",
+ "api_version": "2"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0002.2.json"
+ }
+ }
+ },
+ {
+ "type": "api",
+ "about": "#3 `askargs` version 3",
+ "api": {
+ "parameters": {
+ "action": "askargs",
+ "format": "json",
+ "conditions": "API test page::123",
+ "printouts": "API test page",
+ "api_version": "3"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.a-0002.3.json"
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_CONCEPT": true,
+ "NS_CATEGORY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0003.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0003.json
new file mode 100644
index 00000000..f74e5121
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/a-0003.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test API `action=smwbrowse`, `browse=pvalue`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text extra",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0003/1",
+ "contents": "[[Has text::123]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0003/2",
+ "contents": "[[Has text::1234]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "A0003/3",
+ "contents": "[[Has text extra::12345]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "api",
+ "about": "#0 `smwbrowse` pvalue (text type) search, abide or/and on property",
+ "api": {
+ "parameters": {
+ "action": "smwbrowse",
+ "format": "json",
+ "browse": "pvalue",
+ "params": "{ \"limit\": 10, \"offset\": 0, \"property\": \"Has text\", \"search\": \"123\", \"sort\": \"asc\" }"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "123",
+ "1234"
+ ],
+ "not-contain": [
+ "12345"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCacheUsage": {
+ "api.browse.pvalue": false
+ },
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_CONCEPT": true,
+ "NS_CATEGORY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0001.json
new file mode 100644
index 00000000..ee95c0a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0001.json
@@ -0,0 +1,108 @@
+{
+ "description": "Test `format=debug` output",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]] [[Display precision of::2]]"
+ },
+ {
+ "page": "Example/0001",
+ "contents": "[[Has page::Foo]] [[Has text::bar]]"
+ },
+ {
+ "page": "Example/F0001/2",
+ "contents": "[[Has number::3.555567]]"
+ },
+ {
+ "page": "Example/0001/1",
+ "contents": "{{#ask:[[Has page::Foo]] [[Has page::42]] |?Has page |?Has text |format=debug }}"
+ },
+ {
+ "page": "Example/F0001/Q.1",
+ "contents": "{{#ask: [[Has number::3.555567]] |?Has number |format=debug }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 simple debug output",
+ "subject": "Example/0001/1",
+ "skip-on": {
+ "elastic": "Different debug format"
+ },
+ "assert-output": {
+ "to-contain": [
+ "&#91;&#91;Has page::Foo]] &#91;&#91;Has page::42]]",
+ "Query-Size:4",
+ "Query-Depth:1",
+ "None"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#0.elastic simple debug output",
+ "subject": "Example/0001/1",
+ "skip-on": {
+ "elastic": [ "not", "Different debug format" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "&#91;&#91;Has page::Foo]] &#91;&#91;Has page::42]]",
+ "\"query size\": 4",
+ "\"query depth\": 1"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 value input/output not be constraint by precision",
+ "subject": "Example/F0001/Q.1",
+ "skip-on": {
+ "elastic": "Different debug format"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smwpre\">&#91;&#91;Has number::3.555567]]</div>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1.elastic value input/output not be constraint by precision",
+ "subject": "Example/F0001/Q.1",
+ "skip-on": {
+ "elastic": [ "not", "Different debug format" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "\"query\": \"&#91;&#91;Has number::3.555567]]\""
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0101.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0101.json
new file mode 100644
index 00000000..e490bf17
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0101.json
@@ -0,0 +1,125 @@
+{
+ "description": "Test `format=template` output using unnamed arguments (#885)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingUnnamedArgumentsForNonUnicode",
+ "contents": "<includeonly>[{{{#}}}]:{{{1}}}:{{{2}}}:{{{#userparam}}}:</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingUnnamedArgumentsForNonUnicodeExtra",
+ "contents": "<includeonly><div>{{{#userparam}}}</div></includeonly>"
+ },
+ {
+ "page": "Foo",
+ "contents": "[[Has page property::ABC]] [[Has page property::DEF]] [[Category:template-001]]"
+ },
+ {
+ "page": "Bar",
+ "contents": "[[Has page property::ABC]] [[Has page property::DEF]] [[Category:template-001]]"
+ },
+ {
+ "page": "123",
+ "contents": "[[Has page property::ABC]] [[Has page property::DEF]] [[Category:template-001]]"
+ },
+ {
+ "page": "yxz",
+ "contents": "[[Has page property::ABC]] [[Has page property::DEF]] [[Category:template-001]]"
+ },
+ {
+ "page": "template-001-asc-order-unnamed-args",
+ "contents": "{{#ask:[[Category:template-001]][[Has page property::ABC]] |?Has page property |sep=, |format=template |order=asc |link=none |limit=3 |searchlabel=furtherresults |userparam=[$%&*==42] |template=TemplateOutputUsingUnnamedArgumentsForNonUnicode |introtemplate=TemplateOutputUsingUnnamedArgumentsForNonUnicodeExtra |outrotemplate=TemplateOutputUsingUnnamedArgumentsForNonUnicodeExtra}}"
+ },
+ {
+ "page": "template-001-desc-order-unnamed-args",
+ "contents": "{{#ask:[[Category:template-001]][[Has page property::ABC]] |?Has page property |sep=, |format=template |order=desc |link=none |limit=3 |searchlabel=furtherresults |userparam=[$%&*==42] |template=TemplateOutputUsingUnnamedArgumentsForNonUnicode |introtemplate=TemplateOutputUsingUnnamedArgumentsForNonUnicodeExtra |outrotemplate=TemplateOutputUsingUnnamedArgumentsForNonUnicodeExtra}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 (1.31-) asc template output using unnamed arguments, #885 further results link",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "template-001-asc-order-unnamed-args",
+ "assert-output": {
+ "to-contain": [
+ "<div>[$%&amp;*==42]</div>",
+ "[0]:123:ABC, DEF:[$%&amp;*==42]:",
+ "[1]:Bar:ABC, DEF:[$%&amp;*==42]:",
+ "[2]:Foo:ABC, DEF:[$%&amp;*==42]:",
+ "<div>[$%&amp;*==42]</div><span class=\"smw-template-furtherresults\">"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#0 (1.31+) asc template output using unnamed arguments, #885 further results link",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "template-001-asc-order-unnamed-args",
+ "assert-output": {
+ "to-contain": [
+ "<div>[$%&amp;*==42]</div>",
+ "[0]:123:ABC, DEF:[$%&amp;*==42]:",
+ "[1]:Bar:ABC, DEF:[$%&amp;*==42]:",
+ "[2]:Foo:ABC, DEF:[$%&amp;*==42]:",
+ "<div>[$%&amp;*==42]</div><p><span class=\"smw-template-furtherresults\">"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31-) desc template output using unnamed arguments",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "template-001-desc-order-unnamed-args",
+ "assert-output": {
+ "to-contain": [
+ "<div>[$%&amp;*==42]</div>",
+ "[0]:Yxz:ABC, DEF:[$%&amp;*==42]:",
+ "[1]:Foo:ABC, DEF:[$%&amp;*==42]:",
+ "[2]:Bar:ABC, DEF:[$%&amp;*==42]:",
+ "<div>[$%&amp;*==42]</div><span class=\"smw-template-furtherresults\">"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31+) desc template output using unnamed arguments",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "template-001-desc-order-unnamed-args",
+ "assert-output": {
+ "to-contain": [
+ "<div>[$%&amp;*==42]</div>",
+ "[0]:Yxz:ABC, DEF:[$%&amp;*==42]:",
+ "[1]:Foo:ABC, DEF:[$%&amp;*==42]:",
+ "[2]:Bar:ABC, DEF:[$%&amp;*==42]:",
+ "<div>[$%&amp;*==42]</div><p><span class=\"smw-template-furtherresults\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0102.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0102.json
new file mode 100644
index 00000000..7e7dd0d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0102.json
@@ -0,0 +1,81 @@
+{
+ "description": "Test `format=template` output + unicode characters (#988, skip postgres)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingNamedArgumentsForUnicodeIncludedSubject",
+ "contents": "<includeonly>[{{{#}}}]:{{{1}}}:{{{Has page property}}}:{{{#userparam}}}:</includeonly>"
+ },
+ {
+ "page": "Foo",
+ "contents": "[[Has page property::一二三]] [[Has page property::456]] [[Category:template-002]]"
+ },
+ {
+ "page": "Bar",
+ "contents": "[[Has page property::一二三]] [[Has page property::456]] [[Category:template-002]]"
+ },
+ {
+ "page": "123",
+ "contents": "[[Has page property::一二三]] [[Has page property::456]] [[Category:template-002]]"
+ },
+ {
+ "page": "テスト",
+ "contents": "[[Has page property::一二三]] [[Has page property::456]] [[Category:template-002]]"
+ },
+ {
+ "page": "template-002-asc-order-named-args",
+ "contents": "{{#ask:[[Category:template-002]]<q>[[Has page property:一二三]] OR [[Has page property::456]]</q> |?Has page property |format=template |order=asc |sep=; |valuesep=; <nowiki/>|link=none |limit=10 |userparam=[$%&*==42] |template=TemplateOutputUsingNamedArgumentsForUnicodeIncludedSubject ||named args=yes}}"
+ },
+ {
+ "page": "template-002-desc-order-named-args",
+ "contents": "{{#ask:[[Category:template-002]]<q>[[Has page property:一二三]] OR [[Has page property::456]]</q> |?Has page property |format=template |order=desc |sep=; |valuesep=; <nowiki/>|link=none |limit=10 |userparam=[$%&*==42] |template=TemplateOutputUsingNamedArgumentsForUnicodeIncludedSubject ||named args=yes}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 asc output order, without further links",
+ "subject": "template-002-asc-order-named-args",
+ "assert-output": {
+ "to-contain": [
+ "[0]:123:一二三; 456:[$%&amp;*==42]:",
+ "[1]:Bar:一二三; 456:[$%&amp;*==42]:",
+ "[2]:Foo:一二三; 456:[$%&amp;*==42]:",
+ "[3]:テスト:一二三; 456:[$%&amp;*==42]:"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 desc output order, without further links",
+ "subject": "template-002-desc-order-named-args",
+ "assert-output": {
+ "to-contain": [
+ "[0]:テスト:一二三; 456:[$%&amp;*==42]:",
+ "[1]:Foo:一二三; 456:[$%&amp;*==42]:",
+ "[2]:Bar:一二三; 456:[$%&amp;*==42]:",
+ "[3]:123:一二三; 456:[$%&amp;*==42]:"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "Unicode needs special treatment in postgres"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0103.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0103.json
new file mode 100644
index 00000000..53964aad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0103.json
@@ -0,0 +1,124 @@
+{
+ "description": "Test `format=template` with self reference (#988, guard against template self-reference in ask/show query)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has blob property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page with annotation for template usage",
+ "contents": "[[Has blob property::Template one]] [[Has page property::Template two]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateWithReferenceToItself",
+ "contents": "<includeonly>{{#ask: [[Has page property::Template two]] OR [[Has page property::Template three]]|format=template|template=TemplateWithReferenceToItself}}</includeonly>"
+ },
+ {
+ "page": "PageContainsAskWithTemplateUsage",
+ "contents": "{{#ask: [[Has blob property::Template one]]|format=template|template=TemplateWithReferenceToItself|import-annotation=true}}"
+ },
+ {
+ "page": "PageContainsTemplateTransclusion",
+ "contents": "[[Has page property::Template three]] {{TemplateWithReferenceToItself}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 query profile (use invert query)",
+ "condition": "[[-Has query::PageContainsAskWithTemplateUsage]]",
+ "printouts": [
+ "Query size",
+ "Query string",
+ "Query depth"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "PageContainsAskWithTemplateUsage#0##_QUERY243ffe4b18342e8ae68152cedc5b1966",
+ "PageContainsAskWithTemplateUsage#0##_QUERYe295f9adeb904a1cbab1e30eb56d79ba"
+ ],
+ "count": "2",
+ "datavalues": [
+ {
+ "property": "Query size",
+ "value": "1"
+ },
+ {
+ "property": "Query depth",
+ "value": "1"
+ },
+ {
+ "property": "Query string",
+ "value": "[[Has blob property::Template one]]"
+ },
+ {
+ "property": "Query size",
+ "value": "4"
+ },
+ {
+ "property": "Query depth",
+ "value": "1"
+ },
+ {
+ "property": "Query string",
+ "value": " <q> <q>[[Has page property::Template two]]</q> OR <q>[[Has page property::Template three]]</q> </q> "
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 query profile on page/template transcluded page (use invert query)",
+ "condition": "[[-Has query::PageContainsTemplateTransclusion]]",
+ "printouts": [
+ "Query size",
+ "Query string",
+ "Query depth"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "PageContainsTemplateTransclusion#0##_QUERY243ffe4b18342e8ae68152cedc5b1966"
+ ],
+ "count": "1",
+ "datavalues": [
+ {
+ "property": "Query size",
+ "value": "4"
+ },
+ {
+ "property": "Query depth",
+ "value": "1"
+ },
+ {
+ "property": "Query string",
+ "value": " <q> <q>[[Has page property::Template two]]</q> OR <q>[[Has page property::Template three]]</q> </q> "
+ }
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0104.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0104.json
new file mode 100644
index 00000000..8323d1ef
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0104.json
@@ -0,0 +1,159 @@
+{
+ "description": "Test `format=list, ul, ol, template` (#2022,`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingNamedArguments",
+ "contents": "<includeonly>{{{Has page}}}:{{{Has text}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingNamedArguments-intro",
+ "contents": "<includeonly>(I)</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateOutputUsingNamedArguments-outro",
+ "contents": "<includeonly>(O)</includeonly>"
+ },
+ {
+ "page": "Example/F0104/1",
+ "contents": "[[Has page::{{PAGENAME}}]] [[Has text::123]] [[Has text::456]] [[Category:F0104]]"
+ },
+ {
+ "page": "Example/F0104/2",
+ "contents": "[[Has page::{{PAGENAME}}]] [[Has text::abc]] [[Has text::def]] [[Category:F0104]]"
+ },
+ {
+ "page": "Example/F0104/3",
+ "contents": "[[Has page::{{PAGENAME}}]] [[Has text::001]] [[Has text::0011]] [[Category:F0104]]"
+ },
+ {
+ "page": "Example/F0104/Q.1",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=list |link=none |headers=plain }}"
+ },
+ {
+ "page": "Example/F0104/Q.2",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=list |link=none |headers=plain |sep=;}}"
+ },
+ {
+ "page": "Example/F0104/Q.3",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=list |link=none |headers=plain |sep=;_}}"
+ },
+ {
+ "page": "Example/F0104/Q.4",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=ul |link=none |headers=plain }}"
+ },
+ {
+ "page": "Example/F0104/Q.5",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=ul |link=none |headers=plain |propsep=; }}"
+ },
+ {
+ "page": "Example/F0104/Q.6",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |format=template |named args=yes |introtemplate=TemplateOutputUsingNamedArguments-intro |template=TemplateOutputUsingNamedArguments |outrotemplate=TemplateOutputUsingNamedArguments-outro |link=none |headers=plain }}"
+ },
+ {
+ "page": "Example/F0104/Q.7",
+ "contents": "{{#ask: [[Category:F0104]] |?Has page |?Has text |sep=, |valuesep=; |format=template |named args=yes |introtemplate=TemplateOutputUsingNamedArguments-intro |template=TemplateOutputUsingNamedArguments |outrotemplate=TemplateOutputUsingNamedArguments-outro |link=none |headers=plain }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 (list, no sep)",
+ "subject": "Example/F0104/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-format list-format\"><span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/1</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/1</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">123</span>, <span class=\"smw-value\">456</span></span>)</span>, <span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/2</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/2</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">abc</span>, <span class=\"smw-value\">def</span></span>)</span>, <span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/3</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/3</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">001</span>, <span class=\"smw-value\">0011</span></span>)</span></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (list, sep=;)",
+ "subject": "Example/F0104/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-format list-format\"><span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/1</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/1</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">123</span>, <span class=\"smw-value\">456</span></span>)</span>;<span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/2</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/2</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">abc</span>, <span class=\"smw-value\">def</span></span>)</span>;<span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/3</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/3</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">001</span>, <span class=\"smw-value\">0011</span></span>)</span></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (list, sep=;_)",
+ "subject": "Example/F0104/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<p><span class=\"smw-format list-format\"><span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/1</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/1</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">123</span>, <span class=\"smw-value\">456</span></span>)</span>;_<span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/2</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/2</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">abc</span>, <span class=\"smw-value\">def</span></span>)</span>;_<span class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/3</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/3</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">001</span>, <span class=\"smw-value\">0011</span></span>)</span></span>\n</p>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 (ul, no sep)",
+ "subject": "Example/F0104/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/1</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/1</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">123</span>, <span class=\"smw-value\">456</span></span>)</li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/2</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/2</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">abc</span>, <span class=\"smw-value\">def</span></span>)</li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/3</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/3</span></span>, <span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">001</span>, <span class=\"smw-value\">0011</span></span>)</li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 (ul, propsep=;)",
+ "subject": "Example/F0104/Q.5",
+ "assert-output": {
+ "to-contain": [
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/1</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/1</span></span>;<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">123</span>, <span class=\"smw-value\">456</span></span>)</li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/2</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/2</span></span>;<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">abc</span>, <span class=\"smw-value\">def</span></span>)</li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Example/F0104/3</span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has page</span>: <span class=\"smw-value\">Example/F0104/3</span></span>;<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">001</span>, <span class=\"smw-value\">0011</span></span>)</li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 (template, no sep)",
+ "subject": "Example/F0104/Q.6",
+ "assert-output": {
+ "to-contain": [
+ "(I)Example/F0104/1:123, 456Example/F0104/2:abc, defExample/F0104/3:001, 0011(O)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6 (template, sep=;)",
+ "subject": "Example/F0104/Q.7",
+ "assert-output": {
+ "to-contain": [
+ "(I)Example/F0104/1:123;456,Example/F0104/2:abc;def,Example/F0104/3:001;0011(O)"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0105.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0105.json
new file mode 100644
index 00000000..d45d24fc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0105.json
@@ -0,0 +1,125 @@
+{
+ "description": "Test `format=list, ul, ol` on `_qty` property (`wgContLang=en`, `SMW_DV_NUMV_USPACE`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area",
+ "contents": "[[Has type::Quantity]], [[Corresponds to::1 km², km ²]] [[Corresponds to::0.38610 sq mi, sqmi]] [[Corresponds to::1000 m², m ²]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/F0105/1",
+ "contents": "[[Has area::10 km ²]] [[Category:F0105]]"
+ },
+ {
+ "page": "Example/F0105/2",
+ "contents": "[[Has text::Test <li>Item</li>]] [[Category:F0105]]"
+ },
+ {
+ "page": "Example/F0105/Q.1.1",
+ "contents": "{{#ask: [[Category:F0105]] [[Has area::+]] |?Has area |format=list |headers=plain }}"
+ },
+ {
+ "page": "Example/F0105/Q.1.2",
+ "contents": "{{#ask: [[Category:F0105]] [[Has area::+]] |?Has area |format=ul |headers=plain }}"
+ },
+ {
+ "page": "Example/F0105/Q.1.3",
+ "contents": "{{#ask: [[Category:F0105]] [[Has area::+]] |?Has area |format=ol |headers=plain }}"
+ },
+ {
+ "page": "Example/F0105/Q.2.1",
+ "contents": "{{#ask: [[Category:F0105]] [[Has text::+]] |?Has text |format=list |headers=plain }}"
+ },
+ {
+ "page": "Example/F0105/Q.2.2",
+ "contents": "{{#ask: [[Category:F0105]] [[Has text::+]] |?Has text |format=ul |headers=plain }}"
+ },
+ {
+ "page": "Example/F0105/Q.2.3",
+ "contents": "{{#ask: [[Category:F0105]] [[Has text::+]] |?Has text |format=ol |headers=plain }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 (format=list)",
+ "subject": "Example/F0105/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"3.861 sq mi 10,000 m²\"><span class=\"smwtext\">10&#160;km²</span><span class=\"smwttcontent\">3.861&#160;sq mi <br />10,000&#160;m² <br /></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (format=ul)",
+ "subject": "Example/F0105/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"3.861 sq mi 10,000 m²\"><span class=\"smwtext\">10&#160;km²</span><span class=\"smwttcontent\">3.861&#160;sq mi <br />10,000&#160;m² <br /></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (format=ol)",
+ "subject": "Example/F0105/Q.1.3",
+ "assert-output": {
+ "to-contain": [
+ "title=\"3.861 sq mi 10,000 m²\"><span class=\"smwtext\">10&#160;km²</span><span class=\"smwttcontent\">3.861&#160;sq mi <br />10,000&#160;m² <br /></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 (format=list) with <li> element",
+ "subject": "Example/F0105/Q.2.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/F0105/2\">Example/F0105/2</a></span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">Test <li>Item</li></span></span>)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 (format=ul) with sanitized <li> element",
+ "subject": "Example/F0105/Q.2.2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/F0105/2\">Example/F0105/2</a></span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">Test &lt;li&gt;Item&lt;/li&gt;</span></span>)</li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 (format=ol) with sanitized <li> element",
+ "subject": "Example/F0105/Q.2.3",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/F0105/2\">Example/F0105/2</a></span></span> (<span class=\"smw-field\"><span class=\"smw-field-label\">Has text</span>: <span class=\"smw-value\">Test &lt;li&gt;Item&lt;/li&gt;</span></span>)</li>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_NUMV_USPACE"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0201.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0201.json
new file mode 100644
index 00000000..9b7f90b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0201.json
@@ -0,0 +1,178 @@
+{
+ "description": "Test `format=table` on boolean table output formatting (#896, #1464)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has success state",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has boolean",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project name",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Project one",
+ "contents": "[[Category:BoolProjects]] [[Has success state::true]] [[Has project name::One]]"
+ },
+ {
+ "page": "Project two",
+ "contents": "[[Category:BoolProjects]] [[Has success state::false]] [[Has project name::Two]]"
+ },
+ {
+ "page": "Example/F0201/1",
+ "contents": "{{#ask: [[Category:BoolProjects]]|?Has success state#x|?Has project name|limit=10|format=table |link=none|headers=plain}}"
+ },
+ {
+ "page": "BooleanTable-ToLabelBoolValue",
+ "contents": "{{#ask: [[Category:BoolProjects]]|?Has success state#success,failure|limit=10|format=table}}"
+ },
+ {
+ "page": "BooleanTable-ToLabelBoolValueAndColumn",
+ "contents": "{{#ask: [[Category:BoolProjects]]|?Has success state#success,failure=Status|limit=10|format=table}}"
+ },
+ {
+ "page": "Example/F0201/3",
+ "contents": "{{#subobject: |Has boolean=true |@category=F0201 }}{{#subobject: |Has boolean=false |@category=F0201 }}"
+ },
+ {
+ "page": "Example/F0201/3a",
+ "contents": "{{#ask: [[Category:F0201]] |?Has boolean#✓,✕ }}"
+ },
+ {
+ "page": "Example/F0201/3b",
+ "contents": "{{#ask: [[Category:F0201]] |?Has boolean#○,× }}"
+ },
+ {
+ "page": "Example/F0201/3c",
+ "contents": "{{#ask: [[Category:F0201]] |?Has boolean#<span style=\"color: green; font-size: 120%;\">&#10003;</span>,<span style=\"color: #AA0000; font-size: 120%;\">&#10005;</span>=Label on (&#10003;,&#10005;) }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 Check format#x output, see #896",
+ "subject": "Example/F0201/1",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Project one</td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"1\"><span style=\"font-family: sans-serif;\">X</span></td><td class=\"Has-project-name smwtype_txt\">One</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Project two</td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"0\">&#160;</td><td class=\"Has-project-name smwtype_txt\">Two</td></tr>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31-) Check bool label output value label",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "BooleanTable-ToLabelBoolValue",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "title=\"Property:Has success state\">Has success state</a></th><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">",
+ "title=\"Project one\">Project one</a></td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"1\">success</td>",
+ "title=\"Project two\">Project two</a></td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"0\">failure</td>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31+) Check bool label output value label",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "BooleanTable-ToLabelBoolValue",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "title=\"Property:Has success state\">Has success state</a></th></tr><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">",
+ "title=\"Project one\">Project one</a></td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"1\">success</td>",
+ "title=\"Project two\">Project two</a></td><td class=\"Has-success-state smwtype_boo\" data-sort-value=\"0\">failure</td>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (1.31-) Check bool/column label output",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "BooleanTable-ToLabelBoolValueAndColumn",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "title=\"Property:Has success state\">Status</a></th><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">",
+ "title=\"Project one\">Project one</a></td><td class=\"Status smwtype_boo\" data-sort-value=\"1\">success</td>",
+ "title=\"Project two\">Project two</a></td><td class=\"Status smwtype_boo\" data-sort-value=\"0\">failure</td>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (1.31+) Check bool/column label output",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "BooleanTable-ToLabelBoolValueAndColumn",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "title=\"Property:Has success state\">Status</a></th></tr><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">",
+ "title=\"Project one\">Project one</a></td><td class=\"Status smwtype_boo\" data-sort-value=\"1\">success</td>",
+ "title=\"Project two\">Project two</a></td><td class=\"Status smwtype_boo\" data-sort-value=\"0\">failure</td>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3",
+ "subject": "Example/F0201/3a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">✓</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">✕</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4",
+ "subject": "Example/F0201/3b",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">â—‹</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">×</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 (#1464)",
+ "subject": "Example/F0201/3c",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Label-on-(&amp;#10003;,&amp;#10005;) smwtype_boo\" data-sort-value=\"1\"><span style=\"color: green; font-size: 120%;\">&#10003;</span></td>",
+ "<td class=\"Label-on-(&amp;#10003;,&amp;#10005;) smwtype_boo\" data-sort-value=\"0\"><span style=\"color: #AA0000; font-size: 120%;\">&#10005;</span></td>"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0202.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0202.json
new file mode 100644
index 00000000..6216018f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0202.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test `format=table` with sep cell formatting, #495 (`wgContLang=en`,`wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Table-page",
+ "contents": "[[Has page::Foo]] [[Has page::42]] [[Has text::bar]] [[Has text::1001]]"
+ },
+ {
+ "page": "Table-without-sep-parameter",
+ "contents": "{{#ask:[[Has page::Foo]] [[Has page::42]]\n |?Has page\n |?Has text\n |format=table\n |headers=plain\n |link=none\n }}"
+ },
+ {
+ "page": "Table-with-sep-parameter",
+ "contents": "{{#ask:[[Has page::Foo]] [[Has page::42]]\n |?Has page\n |?Has text\n |format=table\n |sep=;\n |headers=plain\n |link=none\n }}"
+ },
+ {
+ "page": "Broadtable-with-sep-parameter",
+ "contents": "{{#ask:[[Has page::Foo]] [[Has page::42]]\n |?Has page\n |?Has text\n |format=broadtable\n |sep=;\n |headers=plain\n |link=none\n }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 table without sep",
+ "subject": "Table-without-sep-parameter",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<th>&#160;</th><th class=\"Has-page\">Has page</th>",
+ "<th class=\"Has-text\">Has text</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Table-page</td>",
+ "<td class=\"Has-page smwtype_wpg\">Foo<br />42</td>",
+ "<td class=\"Has-text smwtype_txt\">bar<br />1001</td></tr>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 table with sep",
+ "subject": "Table-with-sep-parameter",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<th>&#160;</th><th class=\"Has-page\">Has page</th>",
+ "<th class=\"Has-text\">Has text</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Table-page</td>",
+ "<td class=\"Has-page smwtype_wpg\">Foo;42</td>",
+ "<td class=\"Has-text smwtype_txt\">bar;1001</td></tr>",
+ "</table>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 broadtable with sep",
+ "subject": "Broadtable-with-sep-parameter",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable broadtable\" width=\"100%\">",
+ "<th>&#160;</th><th class=\"Has-page\">Has page</th>",
+ "<th class=\"Has-text\">Has text</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Table-page</td>",
+ "<td class=\"Has-page smwtype_wpg\">Foo;42</td>",
+ "<td class=\"Has-text smwtype_txt\">bar;1001</td></tr>",
+ "</table>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0203.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0203.json
new file mode 100644
index 00000000..2e1ba511
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0203.json
@@ -0,0 +1,78 @@
+{
+ "description": "Test `format=table` to sort by category (#1286)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/F0303/A",
+ "contents": "[[Has project::Project A]] [[Category:Project group A]]"
+ },
+ {
+ "page": "Example/F0303/AA",
+ "contents": "[[Has project::Project AA]] [[Category:Project group A]]"
+ },
+ {
+ "page": "Example/F0303/B",
+ "contents": "[[Has project::Project B]] [[Category:Project group B]]"
+ },
+ {
+ "page": "Example/F0303/C",
+ "contents": "[[Has project::Project C]] [[Category:Project group C]]"
+ },
+ {
+ "page": "Example/F0303/ASC",
+ "contents": "{{#ask: [[Has project::~Project*]] |?Has project |?Category |sort=Category,Has project |order=asc,asc |link=none |format=table}}"
+ },
+ {
+ "page": "Example/F0303/DESC",
+ "contents": "{{#ask: [[Has project::~Project*]] |?Has project |?Category |sort=Category,Has project |order=desc,asc |link=none |format=table}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 asc order",
+ "subject": "Example/F0303/ASC",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/F0303/A</td><td class=\"Has-project smwtype_wpg\">Project A</td><td class=\"Category smwtype_wpg\">Category:Project group A</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0303/AA</td><td class=\"Has-project smwtype_wpg\">Project AA</td><td class=\"Category smwtype_wpg\">Category:Project group A</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/F0303/B</td><td class=\"Has-project smwtype_wpg\">Project B</td><td class=\"Category smwtype_wpg\">Category:Project group B</td></tr>",
+ "<tr data-row-number=\"4\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0303/C</td><td class=\"Has-project smwtype_wpg\">Project C</td><td class=\"Category smwtype_wpg\">Category:Project group C</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 desc order",
+ "subject": "Example/F0303/DESC",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/F0303/C</td><td class=\"Has-project smwtype_wpg\">Project C</td><td class=\"Category smwtype_wpg\">Category:Project group C</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0303/B</td><td class=\"Has-project smwtype_wpg\">Project B</td><td class=\"Category smwtype_wpg\">Category:Project group B</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/F0303/A</td><td class=\"Has-project smwtype_wpg\">Project A</td><td class=\"Category smwtype_wpg\">Category:Project group A</td></tr>",
+ "<tr data-row-number=\"4\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0303/AA</td><td class=\"Has-project smwtype_wpg\">Project AA</td><td class=\"Category smwtype_wpg\">Category:Project group A</td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0204.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0204.json
new file mode 100644
index 00000000..6836cedd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0204.json
@@ -0,0 +1,52 @@
+{
+ "description": "Test `format=table` on `_qty` for different positional unit preference (#1329, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Currency",
+ "contents": "[[Has type::Quantity]], [[Display units::€,£,¥]] [[Corresponds to::€ 1]] [[Corresponds to::1.06 US, US$, $]] [[Corresponds to::0.70 British Pound,GBP,£]] [[Corresponds to::¥,JPY,Japanese Yen 114.2121]]"
+ },
+ {
+ "page": "Example/F0204/1",
+ "contents": "[[Currency::12 €]] [[Currency::¥ 500]] [[Currency::2 £]]"
+ },
+ {
+ "page": "Example/F0204/2",
+ "contents": "[[Currency::€ 20]] [[Currency::2000 JPY]] [[Currency::0.5 GBP]]"
+ },
+ {
+ "page": "Example/F0204/3",
+ "contents": "{{#ask: [[Currency::+]] |?Currency |format=table |headers=plain }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 output with different positional preference",
+ "subject": "Example/F0204/3",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">€&#160;12</span><span class=\"smwttcontent\">8.4&#160;£ <br />¥&#160;1,370.545 <br /></span></span>",
+ "<span class=\"smwtext\">€&#160;4.378</span><span class=\"smwttcontent\">3.064&#160;£ <br />¥&#160;500 <br /></span></span>",
+ "<span class=\"smwtext\">€&#160;2.857</span><span class=\"smwttcontent\">2&#160;£ <br />¥&#160;326.32 <br /></span></span>",
+ "<span class=\"smwtext\">€&#160;20</span><span class=\"smwttcontent\">14&#160;£ <br />¥&#160;2,284.242 <br /></span></span>",
+ "<span class=\"smwtext\">€&#160;17.511</span><span class=\"smwttcontent\">12.258&#160;£ <br />¥&#160;2,000 <br /></span></span>",
+ "<span class=\"smwtext\">€&#160;0.714</span><span class=\"smwttcontent\">0.5&#160;£ <br />¥&#160;81.58 <br /></span></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0205.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0205.json
new file mode 100644
index 00000000..e53123c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0205.json
@@ -0,0 +1,93 @@
+{
+ "description": "Test `format=table` on `|+align=`/`|+limit`/`|+order`/`|+width=` extra printout parameters (T18571, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/F0205/1/1",
+ "contents": "[[Has number::1]] [[Has number::42]] [[Has number::.02]]"
+ },
+ {
+ "page": "Example/F0205/1/2",
+ "contents": "[[Has number::21]] [[Has number::1001]] [[Has number::2.02]]"
+ },
+ {
+ "page": "Example/F0205/1a",
+ "contents": "{{#ask: [[~Example/F0205/1/*]] |?Has number |+align=right |+limit=2 |+order=asc |format=table |headers=plain }}"
+ },
+ {
+ "page": "Example/F0205/1b",
+ "contents": "{{#ask: [[~Example/F0205/1/*]] |?Has number |+align=left |+limit=2 |+order=desc |format=table |headers=plain }}"
+ },
+ {
+ "page": "Example/F0205/Q.2",
+ "contents": "{{#ask: [[~Example/F0205/1/*]] |?Has number |+width=50% |+limit=2 |+order=desc |format=table |headers=plain }}"
+ },
+ {
+ "page": "Example/F0205/Q.3",
+ "contents": "{{#ask: [[~Example/F0205/1/*]] |?Has number |+width=50% |+align=center |+limit=2 |+order=desc |format=table |headers=plain }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "Example/F0205/1a",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number smwtype_num\" data-sort-value=\"0.02\" style=\"text-align:right;\">0.02<br />1",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"2.02\" style=\"text-align:right;\">2.02<br />21"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (align, left)",
+ "subject": "Example/F0205/1b",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number smwtype_num\" data-sort-value=\"42\" style=\"text-align:left;\">42<br />1",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"1001\" style=\"text-align:left;\">1,001<br />21"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (+width)",
+ "subject": "Example/F0205/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"42\" style=\"width:50%;\">42<br />1</td>",
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1001\" style=\"width:50%;\">1,001<br />21</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 (+width, +align)",
+ "subject": "Example/F0205/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"42\" style=\"text-align:center; width:50%;\">42<br />1</td>",
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1001\" style=\"text-align:center; width:50%;\">1,001<br />21</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0206.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0206.json
new file mode 100644
index 00000000..63119a24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0206.json
@@ -0,0 +1,44 @@
+{
+ "description": "Test `format=table` to display extra property description `_PDESC` (en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]] [[Has property description::A number to display ....@en]]"
+ },
+ {
+ "page": "Example/F0206/1/1",
+ "contents": "[[Has number::1001]]"
+ },
+ {
+ "page": "Example/F0206/1a",
+ "contents": "{{#ask: [[~Example/F0206/1/*]] |?Has number |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "Example/F0206/1a",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwttcontent\">A number to display ....</span>",
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1001\">1,001</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0207.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0207.json
new file mode 100644
index 00000000..0ccd8b27
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0207.json
@@ -0,0 +1,202 @@
+{
+ "description": "Test `format=table` on formatted indent when using */#/: (en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/F0207/1/1",
+ "contents": "[[Has text::* 123 \n* 345]] [[Category:F0207]]"
+ },
+ {
+ "page": "Example/F0207/1/2",
+ "contents": "{{#subobject:|Has text=* 一二三 \n* 四五六 \nsome text without indent|@category=F0207}}"
+ },
+ {
+ "page": "Example/F0207/1/3",
+ "contents": "[[Has text::# abc \n# def]] [[Category:F0207]]"
+ },
+ {
+ "page": "Example/F0207/1/4",
+ "contents": "[[Has text::: ABC \n:: DEF]] [[Category:F0207]]"
+ },
+ {
+ "page": "Example/F0207/1/5",
+ "contents": "[[Has text::Some text * without indent]]"
+ },
+ {
+ "page": "Example/F0207/1/6",
+ "contents": "[[Has text::*Without space\n#123]]"
+ },
+ {
+ "page": "Example/F0207/1a",
+ "contents": "{{#ask: [[Category:F0207]] |?Has text |format=table |link=none}}"
+ },
+ {
+ "page": "Example/F0207/1b",
+ "contents": "{{#show: Example/F0207/1/1 |?Has text }}"
+ },
+ {
+ "page": "Example/F0207/3a",
+ "contents": "{{#show: Example/F0207/1/3 |?Has text }}"
+ },
+ {
+ "page": "Example/F0207/5a",
+ "contents": "{{#show: Example/F0207/1/5 |?Has text }}"
+ },
+ {
+ "page": "Example/F0207/6a",
+ "contents": "{{#show: Example/F0207/1/6 |?Has text }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 (1.31-)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/1a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/F0207/1/1</td><td class=\"Has-text smwtype_txt\">",
+ "<li> 123",
+ "<li> 345",
+ "<td class=\"smwtype_wpg\">Example/F0207/1/2#_f3c65172820fbf16a271da866298e82b</td><td class=\"Has-text smwtype_txt\">",
+ "<li> 一二三",
+ "<li> 四五六",
+ "<p>some text without indent",
+ "<td class=\"smwtype_wpg\">Example/F0207/1/3</td><td class=\"Has-text smwtype_txt\">",
+ "<li> abc",
+ "<li> def",
+ "<tr data-row-number=\"4\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0207/1/4</td><td class=\"Has-text smwtype_txt\">",
+ "<dd> ABC",
+ "<dd> DEF"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#0 (1.31+)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/1a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/F0207/1/1</td><td class=\"Has-text smwtype_txt\">",
+ "<li>123",
+ "<li>345",
+ "<td class=\"smwtype_wpg\">Example/F0207/1/2#_f3c65172820fbf16a271da866298e82b</td><td class=\"Has-text smwtype_txt\">",
+ "<li>一二三",
+ "<li>四五六",
+ "<p>some text without indent",
+ "<td class=\"smwtype_wpg\">Example/F0207/1/3</td><td class=\"Has-text smwtype_txt\">",
+ "<li>abc",
+ "<li>def",
+ "<tr data-row-number=\"4\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/F0207/1/4</td><td class=\"Has-text smwtype_txt\">",
+ "<dd>ABC",
+ "<dd>DEF"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31-)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/1b",
+ "assert-output": {
+ "to-contain": [
+ "<ul>",
+ "<li> 123",
+ "<li> 345"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 (1.31+)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/1b",
+ "assert-output": {
+ "to-contain": [
+ "<ul>",
+ "<li>123",
+ "<li>345"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (1.31-)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/3a",
+ "assert-output": {
+ "to-contain": [
+ "<ol>",
+ "<li> abc",
+ "<li> def"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 (1.31+)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0207/3a",
+ "assert-output": {
+ "to-contain": [
+ "<ol>",
+ "<li>abc",
+ "<li>def"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3",
+ "subject": "Example/F0207/5a",
+ "assert-output": {
+ "to-contain": [
+ "<p>Some text * without indent"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4",
+ "subject": "Example/F0207/6a",
+ "assert-output": {
+ "to-contain": [
+ "<ul>",
+ "<li>Without space",
+ "<ol>",
+ "<li>123"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0208.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0208.json
new file mode 100644
index 00000000..372d25f3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0208.json
@@ -0,0 +1,180 @@
+{
+ "description": "Test `format=table` with `limit=0` (further result links) for user/predefined properties, `mainlabel=-`, `#show` (`wgContLang=en`, `wgLang=es`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/F0208/1",
+ "contents": "[[Has text::F0208]] [[Category:F0208]]"
+ },
+ {
+ "page": "Example/F0208/Q1.1",
+ "contents": "{{#ask: [[Has text::F0208]] |?Has text |?Modification date |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q2.1",
+ "contents": "{{#ask: [[Modification date::+]] |?Modification date |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q2.2",
+ "contents": "{{#ask: [[Fecha de modificación@es::+]] |?Fecha de modificación@es |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/3",
+ "contents": "[[Has page::F0208]] {{#subobject:Has text=ABC}}"
+ },
+ {
+ "page": "Example/F0208/4",
+ "contents": "[[Has page::Example/F0208/3]]"
+ },
+ {
+ "page": "Example/F0208/Q3.1",
+ "contents": "{{#ask: [[Has page::F0208]] |mainlabel |?Has page |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q3.2",
+ "contents": "{{#ask: [[Has page::F0208]] |mainlabel |?Has page#Foo |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q3.3",
+ "contents": "{{#ask: [[Has page::F0208]] |mainlabel |?Has page# |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q3.4",
+ "contents": "{{#ask: [[Has page::F0208]] |mainlabel=- |?Has page |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q3.5",
+ "contents": "{{#show: Example/F0208/3 |mainlabel=- |?Has page |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q4.1",
+ "contents": "{{#ask:[[Has page::Example/F0208/3]] |mainlabel=- |?Has page.Has subobject.Has text |format=table |headers=plain |link=none |limit=0 }}"
+ },
+ {
+ "page": "Example/F0208/Q4.2",
+ "contents": "{{#ask:[[Has page::Example/F0208/3]] |mainlabel=- |?Has page.Has subobject.Has text=SomeOtherText |format=table |headers=plain |link=none |limit=0 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "Example/F0208/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20text::F0208-5D-5D/-3FHas-20text/-3FModification-20date/mainlabel%3D/offset%3D0/format%3Dtable"
+ ],
+ "not-contain": [
+ "Special:Ask/-5B-5BHas-20text::F0208-5D-5D/-3FHas-20text-23/-3FModification-20date-23=Fecha-20de-20modificación/mainlabel=/offset=0/format=table"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 predefined properties linking to canonical properties",
+ "subject": "Example/F0208/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BModification-20date::%2B-5D-5D/-3FModification-20date/mainlabel%3D/offset%3D0/format%3Dtable"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 predefined properties linking to canonical properties",
+ "subject": "Example/F0208/Q2.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BModification-20date::%2B-5D-5D/-3FModification-20date/mainlabel%3D/offset%3D0/format%3Dtable"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3",
+ "subject": "Example/F0208/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0208-5D-5D/-3FHas-20page/mainlabel=/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4",
+ "subject": "Example/F0208/Q3.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0208-5D-5D/-3FHas-20page-23Foo/mainlabel=/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5",
+ "subject": "Example/F0208/Q3.3",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0208-5D-5D/-3FHas-20page-23-2D/mainlabel=/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6",
+ "subject": "Example/F0208/Q3.4",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0208-5D-5D/-3FHas-20page/mainlabel=-2D/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7 (#show)",
+ "subject": "Example/F0208/Q3.5",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5B:Example-2FF0208-2F3-5D-5D/-3FHas-20page/mainlabel=-2D/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#8 (property chain on printrequest)",
+ "subject": "Example/F0208/Q4.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::Example-2FF0208-2F3-5D-5D/-3FHas-20page.Has-20subobject.Has-20text=Has-20text/mainlabel=-2D/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#9 (property chain on printrequest)",
+ "subject": "Example/F0208/Q4.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::Example-2FF0208-2F3-5D-5D/-3FHas-20page.Has-20subobject.Has-20text=SomeOtherText/mainlabel=-2D/offset=0/format=table/link=none/headers=plain"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0209.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0209.json
new file mode 100644
index 00000000..2f4e9ff5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0209.json
@@ -0,0 +1,73 @@
+{
+ "description": "Test `format=table` on `_tem`/ `_num` with `LOCAL@...` output (#1591, `wgContLang=es`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature",
+ "contents": "[[Has type::Temperature]]"
+ },
+ {
+ "page": "Example/F0209/1",
+ "contents": "{{#subobject: |Has temperature = 3,928 K |@category=F0209}}{{#subobject: |Has temperature = 113,928 K |@category=F0209 }}{{#subobject: |Has temperature = 5413,928 K |@category=F0209 }}{{#subobject: |Has temperature = 44413,928 K |@category=F0209 }}"
+ },
+ {
+ "page": "Example/F0209/Q1.1",
+ "contents": "{{#ask: [[Category:F0209]] |?Has temperature |?Has temperature#LOCL@fr=fr }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 (1.31-) different lang formatting (en for user lang + fr) rules don't interfere with each other",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0209/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">5,413.928&#160;K</span><span class=\"smwttcontent\">5,140.778&#160;°C <br />9,285.4&#160;°F <br />9,745.07&#160;°R <br />",
+ "<span class=\"smwtext\">5 413,928&#160;K</span><span class=\"smwttcontent\">5 140,778&#160;°C <br />9 285,4&#160;°F <br />9 745,07&#160;°R <br />",
+ "<span class=\"smwtext\">44,413.928&#160;K</span><span class=\"smwttcontent\">44,140.778&#160;°C <br />79,485.4&#160;°F <br />79,945.07&#160;°R <br />",
+ "<span class=\"smwtext\">44 413,928&#160;K</span><span class=\"smwttcontent\">44 140,778&#160;°C <br />79 485,4&#160;°F <br />79 945,07&#160;°R <br />",
+ "<span class=\"smwtext\">3.928&#160;K</span><span class=\"smwttcontent\">-269.222&#160;°C <br />-452.6&#160;°F <br />7.07&#160;°R <br />",
+ "<span class=\"smwtext\">3,928&#160;K</span><span class=\"smwttcontent\">-269,222&#160;°C <br />-452,6&#160;°F <br />7,07&#160;°R <br />",
+ "<span class=\"smwtext\">113.928&#160;K</span><span class=\"smwttcontent\">-159.222&#160;°C <br />-254.6&#160;°F <br />205.07&#160;°R <br />",
+ "<span class=\"smwtext\">113,928&#160;K</span><span class=\"smwttcontent\">-159,222&#160;°C <br />-254,6&#160;°F <br />205,07&#160;°R <br />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#0 (1.31+) different lang formatting (en for user lang + fr) rules don't interfere with each other",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/F0209/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">5,413.928&#160;K</span><span class=\"smwttcontent\">5,140.778&#160;°C <br />9,285.4&#160;°F <br />9,745.07&#160;°R <br />",
+ "<span class=\"smwtext\">5&#160;413,928&#160;K</span><span class=\"smwttcontent\">5&#160;140,778&#160;°C <br />9&#160;285,4&#160;°F <br />9&#160;745,07&#160;°R <br />",
+ "<span class=\"smwtext\">44,413.928&#160;K</span><span class=\"smwttcontent\">44,140.778&#160;°C <br />79,485.4&#160;°F <br />79,945.07&#160;°R <br />",
+ "<span class=\"smwtext\">44&#160;413,928&#160;K</span><span class=\"smwttcontent\">44&#160;140,778&#160;°C <br />79&#160;485,4&#160;°F <br />79&#160;945,07&#160;°R <br />",
+ "<span class=\"smwtext\">3.928&#160;K</span><span class=\"smwttcontent\">-269.222&#160;°C <br />-452.6&#160;°F <br />7.07&#160;°R <br />",
+ "<span class=\"smwtext\">3,928&#160;K</span><span class=\"smwttcontent\">-269,222&#160;°C <br />-452,6&#160;°F <br />7,07&#160;°R <br />",
+ "<span class=\"smwtext\">113.928&#160;K</span><span class=\"smwttcontent\">-159.222&#160;°C <br />-254.6&#160;°F <br />205.07&#160;°R <br />",
+ "<span class=\"smwtext\">113,928&#160;K</span><span class=\"smwttcontent\">-159,222&#160;°C <br />-254,6&#160;°F <br />205,07&#160;°R <br />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "es",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0210.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0210.json
new file mode 100644
index 00000000..b0266798
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0210.json
@@ -0,0 +1,76 @@
+{
+ "description": "Test `format=table` on `_qty` for unit labels with spaces (#1718, `wgContLang=en`, `SMW_DV_NUMV_USPACE`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area with spaces",
+ "contents": "[[Has type::Quantity]], [[Corresponds to::1 km², km ²]] [[Corresponds to::0.38610 sq mi, sqmi]] [[Corresponds to::1000 m², m ²]]"
+ },
+ {
+ "page": "Example/F0210/1",
+ "contents": "[[Has area with spaces::10 km ²]] [[Has area with spaces::3 sqmi]] [[Has area with spaces::50 m ²]] [[Category:F0210]]"
+ },
+ {
+ "page": "Example/F0210/Q1.1",
+ "contents": "{{#ask: [[Category:F0210]][[Has area with spaces::+]] |?Has area with spaces |format=table |headers=plain }}"
+ },
+ {
+ "page": "Example/F0210/Q1.2",
+ "contents": "{{#ask: [[Has area with spaces::10 km ²]] |format=table |headers=plain |link=none }}"
+ },
+ {
+ "page": "Example/F0210/Q1.3",
+ "contents": "{{#ask: [[Has area with spaces::10 km²]] |format=table |headers=plain |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "Example/F0210/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">10&#160;km²</span><span class=\"smwttcontent\">3.861&#160;sq mi <br />10,000&#160;m² <br /></span></span>",
+ "<span class=\"smwtext\">7.77&#160;km²</span><span class=\"smwttcontent\">3&#160;sq mi <br />7,770.008&#160;m² <br /></span></span>",
+ "<span class=\"smwtext\">0.05&#160;km²</span><span class=\"smwttcontent\">0.0193&#160;sq mi <br />50&#160;m² <br /></span></span>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1",
+ "subject": "Example/F0210/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/F0210/1</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2",
+ "subject": "Example/F0210/Q1.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/F0210/1</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_NUMV_USPACE"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0211.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0211.json
new file mode 100644
index 00000000..f0acea4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0211.json
@@ -0,0 +1,230 @@
+{
+ "description": "Test `format=plainlist` with `limit=0` (further result links) for `mainlabel/?#...` (#481)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/F0211/1",
+ "contents": "[[Has text::F0211]] [[Has page::F0211]] [[Category:F0211]]"
+ },
+ {
+ "page": "Example/F0211/Q1.1",
+ "contents": "{{#ask: [[Has page::F0211]] |?#=Foo |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q1.2",
+ "contents": "{{#ask: [[Has page::F0211]] |?#=Foo |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 |mainlabel=- }}"
+ },
+ {
+ "page": "Example/F0211/Q1.3",
+ "contents": "{{#ask: [[Has page::F0211]] |?#=Foo# |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q1.4",
+ "contents": "{{#ask: [[Has page::F0211]] |?#=Foo#- |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q2.1",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 |mainlabel= }}"
+ },
+ {
+ "page": "Example/F0211/Q2.2",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 |mainlabel=- }}"
+ },
+ {
+ "page": "Example/F0211/Q2.3",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |format=plainlist |template=Template/F0211 |named args=yes |limit=0 |mainlabel=FOO }}"
+ },
+ {
+ "page": "Example/F0211/Q3.1",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?# | mainlabel= |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q3.2",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?#- | mainlabel= |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q3.3",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?#- | mainlabel=- |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q3.4",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?#- | mainlabel=FOO |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q4.1",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |? | mainlabel= |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q4.2",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?- | mainlabel= |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ },
+ {
+ "page": "Example/F0211/Q4.3",
+ "contents": "{{#ask: [[Has page::F0211]] |?Has page |?=Foo | mainlabel= |format=plainlist |template=Template/F0211 |named args=yes |limit=0 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "Example/F0211/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3F-23%3DFoo/-3FHas-20page/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1",
+ "subject": "Example/F0211/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3F-23%3DFoo/-3FHas-20page/mainlabel%3D-2D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2",
+ "subject": "Example/F0211/Q1.3",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3F-23%3DFoo-23-2D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 (link is the same as in #2 due to `-`)",
+ "subject": "Example/F0211/Q1.4",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3F-23%3DFoo-23-2D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 (mainlabel)",
+ "subject": "Example/F0211/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 (mainlabel)",
+ "subject": "Example/F0211/Q2.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/mainlabel%3D-2D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6 (mainlabel)",
+ "subject": "Example/F0211/Q2.3",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/mainlabel%3DFOO/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7",
+ "subject": "Example/F0211/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F-23/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#8",
+ "subject": "Example/F0211/Q3.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F-23-2D/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#9",
+ "subject": "Example/F0211/Q3.3",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F-23-2D/mainlabel%3D-2D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#10",
+ "subject": "Example/F0211/Q3.4",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F-23-2D/mainlabel%3DFOO/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#11",
+ "subject": "Example/F0211/Q4.1",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#12 (?- is not valid and is therefore ignored)",
+ "subject": "Example/F0211/Q4.2",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#13",
+ "subject": "Example/F0211/Q4.3",
+ "assert-output": {
+ "to-contain": [
+ "Special:Ask/-5B-5BHas-20page::F0211-5D-5D/-3FHas-20page/-3F%3DFoo/mainlabel%3D/offset%3D0/format%3Dplainlist/template%3DTemplate-2FF0211/named-20args%3Dyes"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0301.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0301.json
new file mode 100644
index 00000000..85f9992b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0301.json
@@ -0,0 +1,212 @@
+{
+ "description": "Test `format=category` with template usage (#699, en, skip postgres)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "UseNamedArgsTemplateForColumnOutput",
+ "contents": "<includeonly>{{{1}}}, {{{Has page property}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "UseUnnamedArgsTemplateForColumnOutput",
+ "contents": "<includeonly>{{{1}}}, {{{2}}}</includeonly>"
+ },
+ {
+ "page": "Foo",
+ "contents": "[[Has page property::Test]]"
+ },
+ {
+ "page": "Bar",
+ "contents": "[[Has page property::Test]]"
+ },
+ {
+ "page": "123",
+ "contents": "[[Has page property::Test]] [[Has page property::Test2]]"
+ },
+ {
+ "page": "テスト",
+ "contents": "[[Has page property::Test]]"
+ },
+ {
+ "page": "είναι απλά",
+ "contents": "[[Has page property::Test2]] {{DEFAULTSORT:A}}"
+ },
+ {
+ "page": "је једноÑтавно модел",
+ "contents": "[[Has page property::Test2]] {{DEFAULTSORT:B}}"
+ },
+ {
+ "page": "template-output-named-args-asc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=asc\n |link=none\n |limit=10\n |template=UseNamedArgsTemplateForColumnOutput\n ||named args=yes\n}}"
+ },
+ {
+ "page": "template-output-named-args-desc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=desc\n |link=none\n |limit=10\n |template=UseNamedArgsTemplateForColumnOutput\n ||named args=yes\n}}"
+ },
+ {
+ "page": "template-output-unnamed-args-asc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=asc\n |link=none\n |limit=10\n |template=UseUnnamedArgsTemplateForColumnOutput\n ||named args=no\n}}"
+ },
+ {
+ "page": "template-output-unnamed-args-desc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=desc\n |link=none\n |limit=10\n |template=UseUnnamedArgsTemplateForColumnOutput\n ||named args=no\n}}"
+ },
+ {
+ "page": "one-column-plainlist-output-asc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=asc\n |link=none\n |headers=plain |limit=10\n |columns=1\n}}"
+ },
+ {
+ "page": "limited-two-column-plainlist-output-desc",
+ "contents": "{{#ask:[[Has page property::Test]]\n |?Has page property\n |format=category\n |order=desc\n |link=none\n |headers=plain\n |searchlabel=Test more\n |limit=2\n |columns=2\n}}"
+ },
+ {
+ "page": "one-column-plainlist-output-with-default-sort-asc",
+ "contents": "{{#ask:[[Has page property::Test2]]\n |?Has page property\n |format=category\n |order=asc\n |link=none\n |headers=plain |limit=10\n |columns=1\n}}"
+ },
+ {
+ "page": "template-output-unnamed-args-with-default-sort-asc",
+ "contents": "{{#ask:[[Has page property::Test2]]\n |?Has page property\n |format=category\n |order=asc\n |link=none\n |limit=10\n |template=UseUnnamedArgsTemplateForColumnOutput\n ||named args=no\n}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "template-output-named-args-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">1</div><ul><li>123, Test Test2</li></ul>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Bar, Test</li></ul></div>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo, Test</li></ul>",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト, Test</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1",
+ "subject": "template-output-named-args-desc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト, Test</li></ul>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo, Test</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Bar, Test</li></ul>",
+ "<div class=\"smw-column-header\">1</div><ul><li>123, Test Test2</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2",
+ "subject": "template-output-unnamed-args-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">1</div><ul><li>123, Test Test2</li></ul>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Bar, Test</li></ul></div>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo, Test</li></ul>",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト, Test</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3",
+ "subject": "template-output-unnamed-args-desc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト, Test</li></ul>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo, Test</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Bar, Test</li></ul>",
+ "<div class=\"smw-column-header\">1</div><ul><li>123, Test Test2</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4",
+ "subject": "one-column-plainlist-output-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">1</div><ul><li>123 (Has page property Test, Test2)</li></ul>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Bar (Has page property Test)</li></ul>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo (Has page property Test)</li></ul>",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト (Has page property Test)</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5",
+ "subject": "limited-two-column-plainlist-output-desc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">テ</div><ul><li>テスト (Has page property Test)</li></ul>",
+ "<div class=\"smw-column-header\">F</div><ul><li>Foo (Has page property Test)</li></ul></div>",
+ "<div class=\"smw-column\" style=\"width:50%;\" dir=\"ltr\"><div class=\"smw-column-header\">F cont.</div><ul><li>",
+ "Special:Ask/-5B-5BHas-20page-20property::Test-5D-5D/-3FHas-20page-20property/mainlabel=/limit=2/order=desc/sort=/offset=2/format=category/link=none/headers=plain/searchlabel=Test-20more/columns=2\">Test more</a></span></li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6, refs #699",
+ "subject": "one-column-plainlist-output-with-default-sort-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column\" style=\"width:100%;\" dir=\"ltr\"><div class=\"smw-column-header\">1</div><ul><li>123 (Has page property Test, Test2)</li></ul>",
+ "<div class=\"smw-column-header\">A</div><ul><li>Είναι απλά (Has page property Test2)</li></ul>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Је једноÑтавно модел (Has page property Test2)</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7, refs #699",
+ "subject": "template-output-unnamed-args-with-default-sort-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column\" style=\"width:33%;\" dir=\"ltr\"><div class=\"smw-column-header\">1</div><ul><li>123, Test Test2</li></ul></div>",
+ "<div class=\"smw-column\" style=\"width:33%;\" dir=\"ltr\"><div class=\"smw-column-header\">A</div><ul><li>Είναι απλά, Test2</li></ul></div>",
+ "<div class=\"smw-column\" style=\"width:33%;\" dir=\"ltr\"><div class=\"smw-column-header\">B</div><ul><li>Је једноÑтавно модел, Test2</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "Unicode needs special treatment in postgres"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0302.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0302.json
new file mode 100644
index 00000000..6e82788a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0302.json
@@ -0,0 +1,108 @@
+{
+ "description": "Test `format=category` and defaultsort (#699, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Saaa",
+ "contents": "[[Has text::aaa]] [[Has text::aba]] [[Has text::abb]]"
+ },
+ {
+ "page": "Sbaa",
+ "contents": "[[Has text::baa]] {{DEFAULTSORT:Saba}}"
+ },
+ {
+ "page": "Scba",
+ "contents": "[[Has text::cba]] [[Has text::01]] [[Has text::10]]"
+ },
+ {
+ "page": "Seba",
+ "contents": "[[Has text::eba]]"
+ },
+ {
+ "page": "Seoa",
+ "contents": "[[Has text::eoa]] {{DEFAULTSORT:Saca}}"
+ },
+ {
+ "page": "Soaa",
+ "contents": "[[Has text::oaa]]"
+ },
+ {
+ "page": "one-column-plainlist-output-asc",
+ "contents": "{{#ask:[[Has text::+]]\n |?Has text\n |format=category\n |order=asc\n |link=none\n |headers=plain\n |delim=;\n |limit=10\n |columns=1\n}}"
+ },
+ {
+ "page": "one-column-plainlist-output-desc",
+ "contents": "{{#ask:[[Has text::+]]\n |?Has text\n |format=category\n |order=desc\n |link=none\n |headers=plain\n |limit=10\n |columns=1\n}}"
+ },
+ {
+ "page": "Example/0302/Further-link",
+ "contents": "{{#ask:[[Has text::+]]\n |?Has text\n |format=category\n |order=desc\n |link=none\n |headers=plain\n |limit=0\n |columns=1\n}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0",
+ "subject": "one-column-plainlist-output-asc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">S</div><ul>",
+ "<li>Saaa (Has text aaa; aba; abb)</li>",
+ "<li>Sbaa (Has text baa)</li>",
+ "<li>Seoa (Has text eoa)</li>",
+ "<li>Scba (Has text 01; 10; cba)</li>",
+ "<li>Seba (Has text eba)</li>",
+ "<li>Soaa (Has text oaa)</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1",
+ "subject": "one-column-plainlist-output-desc",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">S</div><ul>",
+ "<li>Soaa (Has text oaa)</li>",
+ "<li>Seba (Has text eba)</li>",
+ "<li>Scba (Has text 01, 10, cba)</li>",
+ "<li>Seoa (Has text eoa)</li>",
+ "<li>Sbaa (Has text baa)</li>",
+ "<li>Saaa (Has text aaa, aba, abb)</li></ul></div>",
+ "<br style=\"clear: both;\" />"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 Further link",
+ "subject": "Example/0302/Further-link",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-category-furtherresults\">",
+ "Special:Ask/-5B-5BHas-20text::+-5D-5D/-3FHas-20text/mainlabel=/order=desc/sort=/offset=0/format=category/link=none/headers=plain/columns=1"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0303.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0303.json
new file mode 100644
index 00000000..40f3cc9c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0303.json
@@ -0,0 +1,150 @@
+{
+ "description": "Test `format=category` sort output using a template and DEFAULTSORT (#1459, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has surname",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has given name",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has initials",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "CategoryListTemplate",
+ "contents": "<includeonly>{{{1}}}, {{{3}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "SetSobjWithSort",
+ "contents": "<includeonly>{{#subobject:|Has surname={{{Has surname}}}|Has given name={{{Has given name}}}|Has initials={{{Has initials}}}|@category={{{Category}}}|@sortkey={{{Has surname}}},{{{Has initials}}} }}</includeonly>"
+ },
+ {
+ "page": "Example/F0303/1",
+ "contents": "{{#subobject:|Has surname=Abbot|Has given name=John|Has initials=J.|@category=SOBJ1459}}{{#subobject: |Has surname=Arthur|Has given name=Frank Walter|Has initials=F.W.|@category=SOBJ1459}}{{#subobject: |Has surname=Baker|Has given name=George Henry|Has initials=G.H.|@category=SOBJ1459}}"
+ },
+ {
+ "page": "Example/F0303/1aq",
+ "contents": "{{#ask: [[Category:SOBJ1459]] |?Has surname# |?Has given name# |?Has initials# |format=category |mainlabel=- |template=CategoryListTemplate |sort=Has surname,Has initials }}"
+ },
+ {
+ "page": "Example/F0303/1bq",
+ "contents": "{{#ask: [[Category:SOBJ1459]] |?Has surname# |?Has given name# |?Has initials# |format=category |mainlabel=- |headers=hide |sort=Has surname,Has initials }}"
+ },
+ {
+ "page": "Example/F0303/2/1",
+ "contents": "[[Has surname::Abbot]] [[Has given name::John]] [[Has initials::J.]] [[Category:PAGE1459]] {{DEFAULTSORT:Abbot}}"
+ },
+ {
+ "page": "Example/F0303/2/2",
+ "contents": "[[Has surname::Arthur]] [[Has given name::Frank Walter]] [[Has initials::F.W.]] [[Category:PAGE1459]] {{DEFAULTSORT:Arthur}}"
+ },
+ {
+ "page": "Example/F0303/2/3",
+ "contents": "[[Has surname::Baker]] [[Has given name::George Henry]] [[Has initials::G.H.]] [[Category:PAGE1459]] {{DEFAULTSORT:Baker}}"
+ },
+ {
+ "page": "Example/F0303/2aq",
+ "contents": "{{#ask: [[Category:PAGE1459]] |?Has surname# |?Has given name# |?Has initials# |format=category |mainlabel=- |template=CategoryListTemplate |sort=Has surname,Has initials }}"
+ },
+ {
+ "page": "Example/F0303/2bq",
+ "contents": "{{#ask: [[Category:PAGE1459]] |?Has surname# |?Has given name# |?Has initials# |format=category |mainlabel=- |headers=hide |sort=Has surname,Has initials }}"
+ },
+ {
+ "page": "Example/F0303/3",
+ "contents": "{{SetSobjWithSort|Has surname=Abbot|Has given name=John|Has initials=J.|Category=SOBJSORT1459}}{{SetSobjWithSort|Has surname=Arthur|Has given name=Frank Walter|Has initials=F.W.|Category=SOBJSORT1459}}{{SetSobjWithSort|Has surname=Baker|Has given name=George Henry|Has initials=G.H.|Category=SOBJSORT1459}}"
+ },
+ {
+ "page": "Example/F0303/3aq",
+ "contents": "{{#ask: [[Category:SOBJSORT1459]] |?Has surname# |?Has given name# |?Has initials# |format=category |mainlabel=- |template=CategoryListTemplate |sort=Has surname,Has initials }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 using sobj",
+ "subject": "Example/F0303/1aq",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">A</div><ul><li>Abbot, J.</li></ul></div>",
+ "<div class=\"smw-column-header\">A cont.</div><ul><li>Arthur, F.W.</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Baker, G.H.</li></ul></div>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 using sobj",
+ "subject": "Example/F0303/1bq",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">A</div><ul><li>Abbot (John J.)</li></ul></div>",
+ "<div class=\"smw-column-header\">A cont.</div><ul><li>Arthur (Frank Walter F.W.)</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Baker (George Henry G.H.)</li></ul></div>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2",
+ "subject": "Example/F0303/2aq",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">A</div><ul><li>Abbot, J.</li></ul></div>",
+ "<div class=\"smw-column-header\">A cont.</div><ul><li>Arthur, F.W.</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Baker, G.H.</li></ul></div>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3",
+ "subject": "Example/F0303/2bq",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">A</div><ul><li>Abbot (John J.)</li></ul></div>",
+ "<div class=\"smw-column-header\">A cont.</div><ul><li>Arthur (Frank Walter F.W.)</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Baker (George Henry G.H.)</li></ul></div>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 using sobj",
+ "subject": "Example/F0303/3aq",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-columnlist-container\" dir=\"ltr\">",
+ "<div class=\"smw-column-header\">A</div><ul><li>Abbot, J.</li></ul></div>",
+ "<div class=\"smw-column-header\">A cont.</div><ul><li>Arthur, F.W.</li></ul></div>",
+ "<div class=\"smw-column-header\">B</div><ul><li>Baker, G.H.</li></ul></div>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0304.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0304.json
new file mode 100644
index 00000000..46a546ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0304.json
@@ -0,0 +1,198 @@
+{
+ "description": "Test `format=category` with identity collation sort (#2065, `smwgEntityCollation=identity`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Chiapas",
+ "contents": "[[Has page::Chiapas]] [[Category:F0304-1]]"
+ },
+ {
+ "page": "Colima",
+ "contents": "[[Has page::Colima]] [[Category:F0304-1]]"
+ },
+ {
+ "page": "Cinco Rios",
+ "contents": "[[Has page::Cinco Rios]] [[Category:F0304-1]]"
+ },
+ {
+ "page": "California",
+ "contents": "[[Has page::California]] [[Category:F0304-1]]"
+ },
+ {
+ "page": "Example/F0304/Q.1.1",
+ "contents": "{{#ask: [[Category:F0304-1]] |?Has page |format=category |sort=# |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.1.2",
+ "contents": "{{#ask: [[Category:F0304-1]] |?Has page |format=category |sort=# |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.1.3",
+ "contents": "{{#ask: [[Category:F0304-1]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.1.4",
+ "contents": "{{#ask: [[Category:F0304-1]] |?Has page |format=category |sort=Has page |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.1.5",
+ "contents": "{{#ask: [[Category:F0304-1]] |?Has page |format=category |sort=Has page |order=desc |link=none }}"
+ },
+ {
+ "page": "Robin Hood and Little John (Aslackby)",
+ "contents": "[[Has page::Robin Hood and Little John (Aslackby)]] [[Category:F0304-2]]"
+ },
+ {
+ "page": "Robin Hood Tavern (Upper Clapton)",
+ "contents": "[[Has page::Robin Hood Tavern (Upper Clapton)]] [[Category:F0304-2]]"
+ },
+ {
+ "page": "Example/F0304/Q.2.1",
+ "contents": "{{#ask: [[Category:F0304-2]] |?Has page |format=category |sort= |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.2.2",
+ "contents": "{{#ask: [[Category:F0304-2]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ },
+ {
+ "page": "b9",
+ "contents": "[[Has page::b9]] [[Category:F0304-3]]"
+ },
+ {
+ "page": "b1000",
+ "contents": "[[Has page::b1000]] [[Category:F0304-3]]"
+ },
+ {
+ "page": "Example/F0304/Q.3.1",
+ "contents": "{{#ask: [[Category:F0304-3]] |?Has page |format=category |sort= |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0304/Q.3.2",
+ "contents": "{{#ask: [[Category:F0304-3]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 asc",
+ "subject": "Example/F0304/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>California.*</li><li>Chiapas.*<li>Cinco Rios.*</li><li>Colima"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 desc",
+ "subject": "Example/F0304/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 desc",
+ "subject": "Example/F0304/Q.1.3",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 Has page,asc",
+ "subject": "Example/F0304/Q.1.4",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>California.*</li><li>Chiapas.*<li>Cinco Rios.*</li><li>Colima"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 Has page,desc",
+ "subject": "Example/F0304/Q.1.5",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6 asc",
+ "subject": "Example/F0304/Q.2.1",
+ "skip-on": {
+ "postgres": "Contains the reverted order of MySQL!",
+ "sqlite": "Contains the reverted order of MySQL!"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<ul><li>Robin Hood Tavern (Upper Clapton).*<li>Robin Hood and Little John (Aslackby)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7 desc",
+ "subject": "Example/F0304/Q.2.2",
+ "skip-on": {
+ "postgres": "Contains the reverted order of MySQL!",
+ "sqlite": "Contains the reverted order of MySQL!"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<ul><li>Robin Hood and Little John (Aslackby).*<li>Robin Hood Tavern (Upper Clapton)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#8 asc",
+ "subject": "Example/F0304/Q.3.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#9 desc",
+ "subject": "Example/F0304/Q.3.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEntityCollation": "identity",
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC",
+ "SMW_SPARQL_QF_COLLATION"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0305.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0305.json
new file mode 100644
index 00000000..e55e04e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0305.json
@@ -0,0 +1,190 @@
+{
+ "description": "Test `format=category` with uppercase collation sort (#2065, `smwgEntityCollation=uppercase`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Chiapas",
+ "contents": "[[Has page::Chiapas]] [[Category:F0305-1]]"
+ },
+ {
+ "page": "Colima",
+ "contents": "[[Has page::Colima]] [[Category:F0305-1]]"
+ },
+ {
+ "page": "Cinco Rios",
+ "contents": "[[Has page::Cinco Rios]] [[Category:F0305-1]]"
+ },
+ {
+ "page": "California",
+ "contents": "[[Has page::California]] [[Category:F0305-1]]"
+ },
+ {
+ "page": "Example/F0305/Q.1.1",
+ "contents": "{{#ask: [[Category:F0305-1]] |?Has page |format=category |sort=# |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.1.2",
+ "contents": "{{#ask: [[Category:F0305-1]] |?Has page |format=category |sort=# |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.1.3",
+ "contents": "{{#ask: [[Category:F0305-1]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.1.4",
+ "contents": "{{#ask: [[Category:F0305-1]] |?Has page |format=category |sort=Has page |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.1.5",
+ "contents": "{{#ask: [[Category:F0305-1]] |?Has page |format=category |sort=Has page |order=desc |link=none }}"
+ },
+ {
+ "page": "Robin Hood and Little John (Aslackby)/2",
+ "contents": "[[Has page::Robin Hood and Little John (Aslackby)]] [[Category:F0305-2]]"
+ },
+ {
+ "page": "Robin Hood Tavern (Upper Clapton)/2",
+ "contents": "[[Has page::Robin Hood Tavern (Upper Clapton)]] [[Category:F0305-2]]"
+ },
+ {
+ "page": "Example/F0305/Q.2.1",
+ "contents": "{{#ask: [[Category:F0305-2]] |?Has page |format=category |sort= |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.2.2",
+ "contents": "{{#ask: [[Category:F0305-2]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ },
+ {
+ "page": "b9",
+ "contents": "[[Has page::b9]] [[Category:F0305-3]]"
+ },
+ {
+ "page": "b1000",
+ "contents": "[[Has page::b1000]] [[Category:F0305-3]]"
+ },
+ {
+ "page": "Example/F0305/Q.3.1",
+ "contents": "{{#ask: [[Category:F0305-3]] |?Has page |format=category |sort= |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0305/Q.3.2",
+ "contents": "{{#ask: [[Category:F0305-3]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 asc",
+ "subject": "Example/F0305/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>California.*</li><li>Chiapas.*<li>Cinco Rios.*</li><li>Colima"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 desc",
+ "subject": "Example/F0305/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 desc",
+ "subject": "Example/F0305/Q.1.3",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 Has page,asc",
+ "subject": "Example/F0305/Q.1.4",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>California.*</li><li>Chiapas.*<li>Cinco Rios.*</li><li>Colima"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 Has page,desc",
+ "subject": "Example/F0305/Q.1.5",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">C</div><ul><li>Colima.*</li><li>Cinco Rios.*<li>Chiapas.*</li><li>California"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6 asc (different to f-0304)",
+ "subject": "Example/F0305/Q.2.1",
+ "assert-output": {
+ "to-contain": [
+ "<ul><li>Robin Hood and Little John (Aslackby).*<li>Robin Hood Tavern (Upper Clapton)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7 desc (different to f-0304)",
+ "subject": "Example/F0305/Q.2.2",
+ "assert-output": {
+ "to-contain": [
+ "<ul><li>Robin Hood Tavern (Upper Clapton).*<li>Robin Hood and Little John (Aslackby)"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#8 asc",
+ "subject": "Example/F0305/Q.3.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#9 desc",
+ "subject": "Example/F0305/Q.3.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEntityCollation": "uppercase",
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC",
+ "SMW_SPARQL_QF_COLLATION"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0306.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0306.json
new file mode 100644
index 00000000..47b7779a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0306.json
@@ -0,0 +1,234 @@
+{
+ "description": "Test `format=category` with numeric collation sort (same as uppercase, but with numeric sorting) (#2065, `smwgEntityCollation=numeric`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "b9/3",
+ "contents": "[[Has page::b9]] [[Category:F0306-1]]"
+ },
+ {
+ "page": "b1000/3",
+ "contents": "[[Has page::b1000]] [[Category:F0306-1]]"
+ },
+ {
+ "page": "Example/F0306/1",
+ "contents": {
+ "import-from": "/../Fixtures/numeric-sorting.txt"
+ }
+ },
+ {
+ "page": "Example/F0306/Q.1.1",
+ "contents": "{{#ask: [[Category:F0306-1]] |?Has page |format=category |sort= |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0306/Q.1.2",
+ "contents": "{{#ask: [[Category:F0306-1]] |?Has page |format=category |sort= |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0306/Q.1.3",
+ "contents": "{{#ask: [[Category:F0306-1]] |?Has page |format=category |sort=Has page |order=asc |link=none }}"
+ },
+ {
+ "page": "Example/F0306/Q.1.4",
+ "contents": "{{#ask: [[Category:F0306-1]] |?Has page |format=category |sort=Has page |order=desc |link=none }}"
+ },
+ {
+ "page": "Example/F0306/Q.2.1",
+ "contents": "{{#ask: [[Category:Numeric sorting example]] |?Has page |format=category |sort=Has page |order=asc |link=none |mainlabel=- |headers=hide }}"
+ },
+ {
+ "page": "Example/F0306/Q.2.2",
+ "contents": "{{#ask: [[Category:Numeric sorting example]] |?Has page |format=category |sort=Has page |order=desc |link=none |mainlabel=- |headers=hide }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 asc (different to f-0305, f-0304)",
+ "skip-on":{
+ "elastic": "Different sorting behaviour on sortkey value like B01000/03, B09/03"
+ },
+ "subject": "Example/F0306/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#0.elastic asc (different to f-0305, f-0304)",
+ "skip-on":{
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "subject": "Example/F0306/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9.*"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 desc (different to f-0305, f-0304)",
+ "skip-on":{
+ "elastic": "Different sorting behaviour on sortkey value like B01000/03, B09/03",
+ "postgres": "PostgreSQL `numeric` collation generates a different order when compared with MySQL, SQLite."
+ },
+ "subject": "Example/F0306/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1.elastic desc (different to f-0305, f-0304)",
+ "skip-on":{
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "subject": "Example/F0306/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 Has page, asc (different to f-0305, f-0304)",
+ "subject": "Example/F0306/Q.1.3",
+ "skip-on": {
+ "elastic": "Different sorting behaviour on sortkey value like B01000/03, B09/03",
+ "hhvm-*": "HHVM (or SQLite) shows opposite B1000, B9"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2.elastic Has page, asc (different to f-0305, f-0304)",
+ "subject": "Example/F0306/Q.1.3",
+ "skip-on":{
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 Has page, desc (different to f-0305, f-0304)",
+ "subject": "Example/F0306/Q.1.4",
+ "skip-on": {
+ "elastic": "Different sorting behaviour on sortkey value like B01000/03, B09/03",
+ "hhvm-*": "HHVM (or SQLite) shows opposite B9, B1000"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B1000.*<li>B9"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3.elastic Has page, desc (different to f-0305, f-0304)",
+ "subject": "Example/F0306/Q.1.4",
+ "skip-on": {
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">B</div><ul><li>B9.*<li>B1000"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 Has page, asc, imported content",
+ "subject": "Example/F0306/Q.2.1",
+ "skip-on": {
+ "elastic": "Different sorting behaviour, natural",
+ "postgres": "PostgreSQL `numeric` collation generates a different order (natural, An-2, An-218, An-22, An-225, An-24, An-3) when compared with the MySQL output."
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">A</div><ul><li>Antonov An-2.*<li>Antonov An-3 </li>.*<li>Antonov An-22 </li>.*<li>Antonov An-24 </li>.*<li>Antonov An-218 </li>.*<li>Antonov An-225 </li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4.elastic Has page, asc, imported content",
+ "subject": "Example/F0306/Q.2.1",
+ "skip-on": {
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">A</div><ul><li>Antonov An-2.*<li>Antonov An-218 </li>.*<li>Antonov An-22 </li>.*<li>Antonov An-225 </li>.*<li>Antonov An-24 </li>.*<li>Antonov An-3 </li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 Has page, desc, imported content",
+ "subject": "Example/F0306/Q.2.2",
+ "skip-on": {
+ "elastic": "Different sorting behaviour, natural",
+ "postgres": "PostgreSQL `numeric` collation generates a different order (natural, An-2, An-218, An-22, An-225, An-24, An-3) when compared with the MySQL output."
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">A</div><ul><li>Antonov An-225.*<li>Antonov An-218 </li>.*<li>Antonov An-24 </li>.*<li>Antonov An-22 </li>.*<li>Antonov An-3 </li>.*<li>Antonov An-2 </li>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5.elastic Has page, desc, imported content",
+ "subject": "Example/F0306/Q.2.2",
+ "skip-on": {
+ "elastic": [ "not", "Only relevant when used in connection with ES" ]
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-column-header\">A</div><ul><li>Antonov An-3.*<li>Antonov An-24 </li>.*<li>Antonov An-225 </li>.*<li>Antonov An-22 </li>.*<li>Antonov An-218 </li>.*<li>Antonov An-2 </li>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEntityCollation": "numeric",
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC",
+ "SMW_SPARQL_QF_COLLATION"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "mw-1.28<": "`numeric` collation only available with 1.28+"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0307.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0307.json
new file mode 100644
index 00000000..c3a9079f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0307.json
@@ -0,0 +1,150 @@
+{
+ "description": "Test `format=table` with natural printout sorting (n-asc, n-desc)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/F0307/1",
+ "contents": "[[Category:F0307-Text]] [[Has text::1.4.4.8.3.1]] [[Has text::1.4.4.8.3.2]] [[Has text::1.4.4.8.3.3]] [[Has text::1.4.4.8.3.4]] [[Has text::1.4.4.8.3.5]] [[Has text::1.4.4.8.3.6]] [[Has text::1.4.4.8.3.7]] [[Has text::1.4.4.8.3.8]] [[Has text::1.4.4.8.3.9]] [[Has text::1.4.4.8.3.10]] [[Has text::1.4.4.8.3.11]] [[Has text::1.4.4.8.3.12]] [[Has text::1.4.4.8.3.1001]]"
+ },
+ {
+ "page": "Example/F0307/2",
+ "contents": "[[Category:F0307-Number]] [[Has number::1]] [[Has number::2]] [[Has number::3]] [[Has number::4]] [[Has number::5]] [[Has number::6]] [[Has number::7]] [[Has number::8]] [[Has number::9]] [[Has number::10]] [[Has number::11]] [[Has number::12]] [[Has number::1001]]"
+ },
+ {
+ "page": "Example/F0307/Q.1.1",
+ "contents": "{{#ask: [[Category:F0307-Text]] |?Has text|+order=asc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.1.2",
+ "contents": "{{#ask: [[Category:F0307-Text]] |?Has text|+order=desc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.1.3",
+ "contents": "{{#ask: [[Category:F0307-Text]] |?Has text|+order=n-asc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.1.4",
+ "contents": "{{#ask: [[Category:F0307-Text]] |?Has text|+order=n-desc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.2.1",
+ "contents": "{{#ask: [[Category:F0307-Number]] |?Has number|+order=asc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.2.2",
+ "contents": "{{#ask: [[Category:F0307-Number]] |?Has number|+order=desc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.2.3",
+ "contents": "{{#ask: [[Category:F0307-Number]] |?Has number|+order=n-asc |format=table |link=none }}"
+ },
+ {
+ "page": "Example/F0307/Q.2.4",
+ "contents": "{{#ask: [[Category:F0307-Number]] |?Has number|+order=n-desc |format=table |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 asc printout (SORT_LOCALE_STRING)",
+ "subject": "Example/F0307/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">1.4.4.8.3.1<br />1.4.4.8.3.10<br />1.4.4.8.3.1001<br />1.4.4.8.3.11<br />1.4.4.8.3.12<br />1.4.4.8.3.2<br />1.4.4.8.3.3<br />1.4.4.8.3.4<br />1.4.4.8.3.5<br />1.4.4.8.3.6<br />1.4.4.8.3.7<br />1.4.4.8.3.8<br />1.4.4.8.3.9</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 desc printout (SORT_LOCALE_STRING)",
+ "subject": "Example/F0307/Q.1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">1.4.4.8.3.9<br />1.4.4.8.3.8<br />1.4.4.8.3.7<br />1.4.4.8.3.6<br />1.4.4.8.3.5<br />1.4.4.8.3.4<br />1.4.4.8.3.3<br />1.4.4.8.3.2<br />1.4.4.8.3.12<br />1.4.4.8.3.11<br />1.4.4.8.3.1001<br />1.4.4.8.3.10<br />1.4.4.8.3.1</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 n-asc printout (SORT_NATURAL)",
+ "subject": "Example/F0307/Q.1.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">1.4.4.8.3.1<br />1.4.4.8.3.2<br />1.4.4.8.3.3<br />1.4.4.8.3.4<br />1.4.4.8.3.5<br />1.4.4.8.3.6<br />1.4.4.8.3.7<br />1.4.4.8.3.8<br />1.4.4.8.3.9<br />1.4.4.8.3.10<br />1.4.4.8.3.11<br />1.4.4.8.3.12<br />1.4.4.8.3.1001</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 n-desc printout (SORT_NATURAL)",
+ "subject": "Example/F0307/Q.1.4",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">1.4.4.8.3.1001<br />1.4.4.8.3.12<br />1.4.4.8.3.11<br />1.4.4.8.3.10<br />1.4.4.8.3.9<br />1.4.4.8.3.8<br />1.4.4.8.3.7<br />1.4.4.8.3.6<br />1.4.4.8.3.5<br />1.4.4.8.3.4<br />1.4.4.8.3.3<br />1.4.4.8.3.2<br />1.4.4.8.3.1</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#4 asc printout (SORT_NUMERIC)",
+ "subject": "Example/F0307/Q.2.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1\">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />1,001</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#5 desc printout (SORT_NUMERIC)",
+ "subject": "Example/F0307/Q.2.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1001\">1,001<br />12<br />11<br />10<br />9<br />8<br />7<br />6<br />5<br />4<br />3<br />2<br />1</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#6 n-asc printout (SORT_NATURAL)",
+ "subject": "Example/F0307/Q.2.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1\">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />1,001</td>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#7 n-desc printout (SORT_NATURAL)",
+ "subject": "Example/F0307/Q.2.4",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-number smwtype_num\" data-sort-value=\"1001\">1,001<br />12<br />11<br />10<br />9<br />8<br />7<br />6<br />5<br />4<br />3<br />2<br />1</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0308.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0308.json
new file mode 100644
index 00000000..7b259e22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0308.json
@@ -0,0 +1,93 @@
+{
+ "description": "Test `format=table` with DEFAULTSORT and subject,property sorting",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Is performer",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "F0308/A",
+ "contents": "[[Category:F0308]] {{DEFAULTSORT:F0308/A}} [[Is performer::August]]"
+ },
+ {
+ "page": "F0308/AB",
+ "contents": "[[Category:F0308]] {{DEFAULTSORT:F0308/A}} [[Is performer::September]]"
+ },
+ {
+ "page": "F0308/Q.1",
+ "contents": "{{#ask: [[Category:F0308]] |sort=,Is performer |order=asc,asc |format=table}}"
+ },
+ {
+ "page": "F0308/Q.2",
+ "contents": "{{#ask: [[Category:F0308]] |sort=,Is performer |order=asc,desc |format=table}}"
+ },
+ {
+ "page": "F0308/Q.3",
+ "contents": "{{#ask: [[Category:F0308]] |sort=,Is performer |order=desc,asc |format=table}}"
+ },
+ {
+ "page": "F0308/Q.4",
+ "contents": "{{#ask: [[Category:F0308]] |sort=,Is performer |order=desc,desc |format=table}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 ( ,Is performer | asc,asc)",
+ "subject": "F0308/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/A\">F0308/A</a></td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/AB\">F0308/AB</a></td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#1 ( ,Is performer | asc,desc)",
+ "subject": "F0308/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/AB\">F0308/AB</a></td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/A\">F0308/A</a></td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#2 ( ,Is performer | desc,asc)",
+ "subject": "F0308/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/A\">F0308/A</a></td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/AB\">F0308/AB</a></td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "format",
+ "about": "#3 ( ,Is performer | desc,desc)",
+ "subject": "F0308/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/AB\">F0308/AB</a></td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"F0308/A\">F0308/A</a></td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0401.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0401.json
new file mode 100644
index 00000000..8fa351da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0401.json
@@ -0,0 +1,68 @@
+{
+ "description": "Test `format=list` output",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Foo",
+ "contents": "[[Has page property::ABC]]"
+ },
+ {
+ "page": "Bar",
+ "contents": "[[Has page property::ABC]]"
+ },
+ {
+ "page": "Baz",
+ "contents": "[[Has page property::ABC]]"
+ },
+ {
+ "page": "Quok",
+ "contents": "[[Has page property::ABC]]"
+ },
+ {
+ "page": "Quuz",
+ "contents": "[[Has page property::DEF]]"
+ },
+ {
+ "page": "Example/0401",
+ "contents": "{{#ask:[[Has page property::ABC]] |format=list}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser-html",
+ "about": "#0 Basic List format (structure)",
+ "subject": "Example/0401",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "span.list-format > span.smw-row > span.smw-field > span.smw-value > a[title]", 4 ]
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 Basic List format (order)",
+ "subject": "Example/0401",
+ "assert-output": {
+ "to-contain": [
+ "Bar.*Baz.*Foo.*Quok"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0402.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0402.json
new file mode 100644
index 00000000..187cabcc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0402.json
@@ -0,0 +1,540 @@
+{
+ "description": "Test `format=list` output",
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": true
+ },
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Version",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Developer",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Company",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Website",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "f-0402",
+ "contents": "<includeonly>{{{1}}}-{{{SomeMainlabel}}}; #userparam: {{{#userparam|}}}; #querycondition: {{#tag:nowiki|{{{#querycondition|}}}}}; #querylimit: {{{#querylimit|}}}; #resultoffset: {{{#resultoffset|}}}; #rowcount: {{{#rowcount|}}}; #rownumber: {{{#rownumber|}}}; Version: {{{2}}}-{{{Version}}}; Developer: {{{3}}}-{{{Developer}}}; Company: {{{4}}}-{{{Company}}}; Website: {{{5}}}-{{{Website}}};</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "f-0402/intro",
+ "contents": "<includeonly><span class='intro'>#userparam: {{{#userparam|}}}; #querycondition: {{#tag:nowiki|{{{#querycondition|}}}}}; #querylimit: {{{#querylimit|}}}; #resultoffset: {{{#resultoffset|}}}; #rowcount: {{{#rowcount|}}};</span></includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "f-0402/outro",
+ "contents": "<includeonly><span class='outro'>#userparam: {{{#userparam|}}}; #querycondition: {{#tag:nowiki|{{{#querycondition|}}}}}; #querylimit: {{{#querylimit|}}}; #resultoffset: {{{#resultoffset|}}}; #rowcount: {{{#rowcount|}}};</span></includeonly>"
+ },
+ {
+ "page": "f-0402/Asoka",
+ "contents": "[[Category:0402]][[Version::3.8]][[Developer::Shae Erley]][[Developer::Keeley Considine]][[Company::Edgewire]]"
+ },
+ {
+ "page": "f-0402/Bitwolf",
+ "contents": "[[Category:0402]][[Version::0.3.8]][[Developer::Nealy Dawes]][[Developer::Harmony Hadgraft]][[Company::Eadel]]"
+ },
+ {
+ "page": "f-0402/Domainer",
+ "contents": "[[Category:0402]][[Version::9.49]][[Developer::Maridel Murdoch]][[Developer::Wiatt Crowest]][[Developer::Lottie Cruz]][[Company::Skimia]][[Website::https://icio.us/vestibulum/rutrum/rutrum/neque.aspx]]"
+ },
+ {
+ "page": "f-0402/Home Ing",
+ "contents": "[[Category:0402]][[Version::5.0.8]][[Developer::Mimi Kemish]][[Company::Oyope]]"
+ },
+ {
+ "page": "f-0402/Lotstring",
+ "contents": "[[Category:0402]][[Version::1.5]][[Developer::Willamina Strete]][[Company::Riffwire]]"
+ },
+ {
+ "page": "f-0402/Prodder",
+ "contents": "[[Category:0402]][[Version::0.1.8]][[Developer::Nevsa Slavin]][[Company::Bluezoom]][[Website::http://yale.edu/luctus/tincidunt/nulla/mollis/molestie/lorem/quisque.js]]"
+ },
+ {
+ "page": "f-0402/Span",
+ "contents": "[[Category:0402]][[Version::2.19]][[Developer::Meredith Yakubowicz]][[Company::Devbug]]"
+ },
+ {
+ "page": "f-0402/Tin",
+ "contents": "[[Category:0402]][[Version::0.1.5]][[Developer::Jemmie Crutchley]][[Company::Rhyzio]]"
+ },
+ {
+ "page": "f-0402/Veribet",
+ "contents": "[[Category:0402]][[Version::0.89]][[Developer::Gaspar Truelock]][[Company::Gabspot]]"
+ },
+ {
+ "page": "f-0402/Zaam-Dox",
+ "contents": "[[Category:0402]][[Version::8.06]][[Developer::Lew Whittek]][[Company::Kayveo]]"
+ },
+ {
+ "page": "f-0402/Test-01",
+ "contents": "{{#ask:[[Category:0402]]}}"
+ },
+ {
+ "page": "f-0402/Test-02",
+ "contents": "{{#ask:[[Category:0402]]|format=list}}"
+ },
+ {
+ "page": "f-0402/Test-02.2",
+ "contents": "{{#ask:[[Category:0402]]|format=plainlist}}"
+ },
+ {
+ "page": "f-0402/Test-03",
+ "contents": "{{#ask:[[Category:0402]]|format=template}}"
+ },
+ {
+ "page": "f-0402/Test-04",
+ "contents": "{{#ask:[[Category:0402]]|format=ol}}"
+ },
+ {
+ "page": "f-0402/Test-05",
+ "contents": "{{#ask:[[Category:0402]]|format=ul}}"
+ },
+ {
+ "page": "f-0402/Test-06",
+ "contents": "{{#ask:[[Category:0402]]|format=list|?Version|?Developer|?Company|?Website|template=f-0402|sep=SEPARATOR|propsep=PSEP|valuesep=VSEP|searchlabel=|mainlabel=SomeMainlabel|userparam=SomeUserParam|offset=2|limit=5}}"
+ },
+ {
+ "page": "f-0402/Test-07",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Version=|?Developer|?Company|?Website|template=f-0402|sep=SEPARATOR|propsep=PSEP|valuesep=VSEP|searchlabel=|mainlabel=SomeMainlabel|userparam=SomeUserParam|offset=2|limit=5|named args=yes}}"
+ },
+ {
+ "page": "f-0402/Test-08",
+ "contents": "{{#ask:[[Category:0402]]|format=list|?Version|?Developer|?Company|?Website|sep=SEPARATOR|propsep=PSEP|valuesep=VSEP}}"
+ },
+ {
+ "page": "f-0402/Test-09",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Version|?Developer|?Company|?Website|sep=SEPARATOR|propsep=PSEP|valuesep=VSEP|offset=2|limit=5}}{{#ask:[[Category:0402]]|format=ul|?Version|?Developer|?Company|?Website|sep=SEPARATOR}}"
+ },
+ {
+ "page": "f-0402/Test-10",
+ "contents": "{{#ask:[[Category:0402]]|format=list|introtemplate=f-0402/intro|outrotemplate=f-0402/outro}}"
+ },
+ {
+ "page": "f-0402/Test-11",
+ "contents": "{{#ask:[[Category:0402]]|format=list|template=f-0402|introtemplate=f-0402/intro|outrotemplate=f-0402/outro|limit=8}}"
+ },
+ {
+ "page": "f-0402/Test-12",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Company|introtemplate=f-0402/intro|outrotemplate=f-0402/outro}}"
+ },
+ {
+ "page": "f-0402/Test-13",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Company|link=none|headers=show|searchlabel=}}"
+ },
+ {
+ "page": "f-0402/Test-14",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Company|link=subject|headers=hide|searchlabel=}}"
+ },
+ {
+ "page": "f-0402/Test-15",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|?Company|link=all|headers=plain|searchlabel=}}"
+ },
+ {
+ "page": "f-0402/Test-16",
+ "contents": "{{#ask:[[Category:0402]]|format=list|class=some-class}}"
+ },
+ {
+ "page": "f-0402/Test-17",
+ "contents": "{{#ask:[[Category:0402]]|format=plainlist|class=some-class}}"
+ },
+ {
+ "page": "f-0402/Test-18",
+ "contents": "{{#ask:[[Category:0402]]|format=ol|class=some-class}}"
+ },
+ {
+ "page": "f-0402/Test-19",
+ "contents": "{{#ask:[[Category:0402]]|format=ul|class=some-class}}"
+ }
+
+ ],
+ "tests": [
+ {
+ "type": "parser-html",
+ "about": "#1 Plainlist format is called as standard output",
+ "subject": "f-0402/Test-01",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ "a[title] ~ a[title]"
+ ],
+ "not-contain": [
+ ".smw-format",
+ ".list-format",
+ ".smw-row",
+ ".smw-field",
+ ".smw-value"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#2 List format is called when format=list",
+ "subject": "f-0402/Test-02",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [
+ "span.list-format > span.smw-row > span.smw-field > span.smw-value > a[title]",
+ 10
+ ]
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#2.2 Plainlist format is called when format=plainlist",
+ "subject": "f-0402/Test-02.2",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ "a[title] ~ a[title]"
+ ],
+ "not-contain": [
+ ".smw-format",
+ ".list-format",
+ ".smw-row",
+ ".smw-field",
+ ".smw-value"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#3 List format is called when format=template (but no actual template specified)",
+ "subject": "f-0402/Test-03",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [
+ "span.list-format > span.smw-row > span.smw-field > span.smw-value > a[title]",
+ 10
+ ]
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#4 Ol format is called when format=ol",
+ "subject": "f-0402/Test-04",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [
+ "ol.ol-format > li.smw-row > span.smw-field > span.smw-value > a[title]",
+ 10
+ ]
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#5 Ul format is called when format=ul",
+ "subject": "f-0402/Test-05",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [
+ "ul.ul-format > li.smw-row > span.smw-field > span.smw-value > a[title]",
+ 10
+ ]
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 List format with template specified (numbered args)",
+ "subject": "f-0402/Test-06",
+ "assert-output": {
+ "to-contain": [
+ "/(SEPARATOR.+?){4}/",
+ "VSEP", // just make sure there's a bunch of VSEPs, don't count
+ "#userparam: SomeUserParam;",
+ "#querycondition: [[Category:0402]];",
+ "#querylimit: 5;",
+ "#resultoffset: 2;",
+ "#rowcount: 10;",
+ "#rownumber: 3;",
+ "#rownumber: 7;",
+ "{{{SomeMainlabel}}}", // Named template params are not replaced
+ "{{{Version}}}",
+ "{{{Developer}}}",
+ "{{{Company}}}",
+ "{{{Website}}}"
+ ],
+ "not-contain": [
+ "/(SEPARATOR){10,}/",
+ "PSEP",
+ "#rownumber: 2;",
+ "#rownumber: 8;",
+ "smw-row", // no structure for list with templates
+ "smw-field",
+ "smw-value",
+ "{{{1}}}", // Numbered template params are replaced
+ "{{{2}}}",
+ "{{{3}}}",
+ "{{{4}}}",
+ "{{{5}}}"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 Ol format with template specified (named args)",
+ "subject": "f-0402/Test-07",
+ "assert-output": {
+ "to-contain": [
+ "VSEP", // just make sure there's a bunch of VSEPs, don't count
+ "#userparam: SomeUserParam;",
+ "#querycondition: [[Category:0402]];",
+ "#querylimit: 5;",
+ "#resultoffset: 2;",
+ "#rowcount: 10;",
+ "#rownumber: 3;",
+ "#rownumber: 7;",
+ "smw-row", // Templates are applied inside rows for ol with templates
+ "{{{1}}}", // Numbered template params are not replaced
+ "{{{Version}}}", // ...except if the label is empty
+ "{{{3}}}",
+ "{{{4}}}",
+ "{{{5}}}"
+ ],
+ "not-contain": [
+ "SEPARATOR",
+ "PSEP",
+ "#rownumber: 2;",
+ "#rownumber: 8;",
+ "smw-field", // no structure below row for ol with templates
+ "smw-value",
+ "{{{SomeMainlabel}}}", // Named template params are replaced
+ "{{{2}}}", // ...except if the label is empty
+ "{{{Developer}}}",
+ "{{{Company}}}",
+ "{{{Website}}}"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 List format with sep",
+ "subject": "f-0402/Test-08",
+ "assert-output": { // at least 18, but not 19 or more, i.e. exactly 18
+ "to-contain": [
+ "/(span>SEPARATOR<span.+?){9}/",
+ "PSEP",
+ "VSEP"
+ ],
+ "not-contain": [ "/(span>SEPARATOR<span.+?){10,}/" ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9.1 Ol & ul format without sep (even if specified)",
+ "subject": "f-0402/Test-09",
+ "assert-output": {
+ "to-contain": [
+ "PSEP",
+ "VSEP"
+ ],
+ "not-contain": [
+ "SEPARATOR"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#9.2 Ol format with offset",
+ "subject": "f-0402/Test-09",
+ "assert-output": {
+ "to-contain": [
+ "ol.ol-format[start=\"3\"]"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10.1 List format with introtemplate and outrotemplate",
+ "subject": "f-0402/Test-10",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"intro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 50; #resultoffset: 0; #rowcount: 10;</span>",
+ "<span class=\"outro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 50; #resultoffset: 0; #rowcount: 10;</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#10.2 List format with introtemplate and outrotemplate",
+ "subject": "f-0402/Test-10",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "span.intro + span.smw-format", 1 ],
+ [ "span.smw-format + span.outro", 1 ]
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#11.1 Template format with introtemplate and outrotemplate",
+ "subject": "f-0402/Test-11",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"intro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 8; #resultoffset: 0; #rowcount: 10;</span>",
+ "<span class=\"outro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 8; #resultoffset: 0; #rowcount: 10;</span>"
+ ],
+ "not-contain": [
+ ";, <a" //No default list separator (, ) between template calls
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#11.2 Template format with introtemplate and outrotemplate",
+ "subject": "f-0402/Test-11",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "p > span.intro:first-child + a", 1 ],
+ [ "p > span.outro + span.smw-list-furtherresults:last-child", 1 ]
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#12.1 Ol format with introtemplate and outrotemplate",
+ "subject": "f-0402/Test-12",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"intro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 50; #resultoffset: 0; #rowcount: 10;</span>",
+ "<span class=\"outro\">#userparam:&#160;; #querycondition: [[Category:0402]]; #querylimit: 50; #resultoffset: 0; #rowcount: 10;</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#12.2 (1.31-) Ol format with introtemplate and outrotemplate",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "f-0402/Test-12",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "span.intro + ol.smw-format", 1 ],
+ [ "ol.smw-format + span.outro", 1 ]
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#13",
+ "subject": "f-0402/Test-13",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "span.smw-field-label > a", 10 ] // headers = show
+ ],
+ "not-contain": [
+ "span.smw-value > a" // link = none
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#14",
+ "subject": "f-0402/Test-14",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "li.smw-row > span.smw-field:first-child > span.smw-value > a", 10 ] // link = subject
+ ],
+ "not-contain": [
+ "li.smw-row > span.smw-field:not(:first-child) > span.smw-value > a", // link = subject
+ "span.smw-field-label" // headers = hide
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#15",
+ "subject": "f-0402/Test-15",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ [ "li.smw-row > span.smw-field > span.smw-value > a", 20 ], // link = all
+ [ "span.smw-field-label", 10 ] // headers = plain
+ ],
+ "not-contain": [
+ "span.smw-field-label > a" // headers = plain
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#16 List format is called as standard output",
+ "subject": "f-0402/Test-16",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ "span.list-format.some-class"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#17 List format is called as standard output",
+ "subject": "f-0402/Test-17",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "not-contain": [
+ ".some-class"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#18 List format is called as standard output",
+ "subject": "f-0402/Test-18",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ "ol.ol-format.some-class"
+ ]
+ }
+ },
+ {
+ "type": "parser-html",
+ "about": "#19 List format is called as standard output",
+ "subject": "f-0402/Test-19",
+ "assert-output": {
+ "to-be-valid-html": true,
+ "to-contain": [
+ "ul.ul-format.some-class"
+ ]
+ }
+ }
+
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0801.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0801.json
new file mode 100644
index 00000000..8707defa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0801.json
@@ -0,0 +1,247 @@
+{
+ "description": "Test `format=embedded` output",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasSomePageProperty",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has another property",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Format/Embedded/A/1",
+ "contents": "[[HasSomePageProperty::ABC]] {{#subobject:HasSomePageProperty=123}} [[Category:Embedded format/A]]"
+ },
+ {
+ "page": "Format/Embedded/A/2",
+ "contents": "[[HasSomePageProperty::DEF]] {{#subobject:HasSomePageProperty=456}} [[Category:Embedded format/A]]"
+ },
+ {
+ "page": "Format/Embedded/B/1",
+ "contents": "[[HasSomePageProperty::ABC]] {{#subobject:HasSomePageProperty=123}} [[Category:Embedded format/B]]"
+ },
+ {
+ "page": "Format/Embedded/B/2",
+ "contents": "[[HasSomePageProperty::DEF]] {{#subobject:HasSomePageProperty=456}} [[Category:Embedded format/B]]"
+ },
+ {
+ "page": "Format/Embedded/A",
+ "contents": "{{#ask:[[Category:Embedded format/A]] |format=embedded |link=none |headers=show |embedformat=h1 }}"
+ },
+ {
+ "page": "Format/Embedded/B",
+ "contents": "{{#ask:[[Category:Embedded format/B]] |format=count}} {{#ask:[[Category:Embedded format/B]] |format=embedded |link=none |headers=show |embedformat=h1 }}"
+ },
+ {
+ "page": "Format/Embedded/C",
+ "contents": "{{:Format/Embedded/A/1}}"
+ },
+ {
+ "page": "Format/Embedded/D",
+ "contents": "[[Has another property::A123]] {{#ask:[[Category:Embedded format/B]] |format=count}} {{#ask:[[Category:Embedded format/B]] [[!Format/Embedded/B]]|format=embedded |link=none |headers=show |embedformat=h1 }} {{#set:Has another property=ABCD}}"
+ },
+ {
+ "page": "Format/Embedded/E/1",
+ "contents": "{{#ask:[[Category:Embedded format/A]] |format=embedded |link=none |headers=show |embedformat=h1 }} {{#ask:[[Category:Embedded format/B]] |format=embedded |link=none |headers=show |embedformat=h1 }} [[Category:Embedded format/E]]"
+ },
+ {
+ "page": "Format/Embedded/E",
+ "contents": "{{#ask:[[Category:Embedded format/E]] |format=embedded |link=none |headers=show |embedformat=h1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Format/Embedded/A",
+ "skip-on": {
+ "mw": [ ">1.30.x", "See #2966" ]
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "Format/Embedded/A#_QUERYc91dabd03c0f49de2dc535c5b099aa45"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA.2F1\">",
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA.2F2\">",
+ "ABC",
+ "DEF"
+ ],
+ "not-contain": [
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA\">.*selflink\">Format/Embedded/A.*</h1>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#0.x",
+ "subject": "Format/Embedded/A",
+ "skip-on": {
+ "mw": [ "<1.31", "See #2966" ]
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "Format/Embedded/A#_QUERYc91dabd03c0f49de2dc535c5b099aa45"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<h1><span id=\"Format/Embedded/A/1\"></span><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA.2F1\">",
+ "<h1><span id=\"Format/Embedded/A/2\"></span><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA.2F2\">",
+ "ABC",
+ "DEF"
+ ],
+ "not-contain": [
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FA\">.*selflink\">Format/Embedded/A.*</h1>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Format/Embedded/B",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "Format/Embedded/B#_QUERY62dcea2c67d5ddb3d9304fc34b792da2",
+ "Format/Embedded/B#_QUERYe368d606dd919a1d87cb50f61cfff289"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<h1>.*<span class=\"mw-headline\" id=\"Format.2FEmbedded.2FB.2F1\">",
+ "<h1>.*<span class=\"mw-headline\" id=\"Format.2FEmbedded.2FB.2F2\">",
+ "ABC",
+ "DEF"
+ ],
+ "not-contain": [
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FB\">.*selflink\">Format/Embedded/B.*</h1>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (manual embedded)",
+ "subject": "Format/Embedded/C",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_INST",
+ "_MDAT",
+ "_SKEY",
+ "_SOBJ",
+ "HasSomePageProperty"
+ ],
+ "propertyValues": [
+ "Embedded format/A",
+ "ABC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (ensure that annotations for the target page are not disabled, exclude embbeded query from [[!Format/Embedded/B]] otherwise we point to ourselves)",
+ "subject": "Format/Embedded/D",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK",
+ "Has_another_property"
+ ],
+ "propertyValues": [
+ "A123",
+ "ABCD"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<h1>.*<span class=\"mw-headline\" id=\"Format.2FEmbedded.2FB.2F1\">",
+ "<h1>.*<span class=\"mw-headline\" id=\"Format.2FEmbedded.2FB.2F2\">",
+ "A123",
+ "ABC",
+ "DEF"
+ ],
+ "not-contain": [
+ "<h1><span class=\"mw-headline\" id=\"Format.2FEmbedded.2FD\">.*selflink\">Format/Embedded/D.*</h1>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 embedded an #ask embbeded page",
+ "subject": "Format/Embedded/E",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<a href=.* title=\"Format/Embedded/E/1\">Format/Embedded/E/1</a>",
+ "<a href=.* title=\"Format/Embedded/A/1\">Format/Embedded/A/1</a>",
+ "<a href=.* title=\"Format/Embedded/A/2\">Format/Embedded/A/2</a>",
+ "<a href=.* title=\"Format/Embedded/B/1\">Format/Embedded/B/1</a>",
+ "<a href=.* title=\"Format/Embedded/B/2\">Format/Embedded/B/2</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [ "_MDAT" ],
+ "smwgQueryResultCacheType": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0802.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0802.json
new file mode 100644
index 00000000..32cd398f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0802.json
@@ -0,0 +1,81 @@
+{
+ "description": "Test `format=template` [[SMW::on/off]] regression using `named args=yes` (#1453, skip-on 1.19)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has publication title",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has publication author",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0802/Cite publication/result format",
+ "contents": "<includeonly><div>{{{Author|}}}, <span>‘[[{{{Page|}}}|{{{Title|}}}]]’</span></div></includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0802/Cite publication",
+ "contents": "<includeonly>{{#ask: [[{{{1|}}}]] |?=Page |?Has publication author=Author |?Has publication title=Title |format=template |template=Example/F0802/Cite publication/result format|named args=yes |link=none }}</includeonly>"
+ },
+ {
+ "do-purge": true,
+ "page": "Example/F0802/A",
+ "contents": "{{#set: |Has publication title=Mitteilungen aus irischen Handschriften. V. Aus Egerton 1782. <em>DÅ«an in chÅicat cest inn</em>|Has publication author=Meyer (Kuno)}}"
+ },
+ {
+ "page": "Example/F0802/B",
+ "contents": "{{#set: |Has text=Extra text by #set: {{Example/F0802/Cite publication|Example/F0802/A }} }} {{#ask: [[Example/F0802/B]] |?Has text=Text |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/F0802/B",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK",
+ "Has_text"
+ ],
+ "propertyValues": []
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/F0802/A\">Mitteilungen aus irischen Handschriften. V. Aus Egerton 1782. <em>DÅ«an in chÅicat cest inn</em></a>’</span></div>",
+ "<td class=\"Text smwtype_txt\">Extra text by #set: <div>Meyer (Kuno), <span>‘"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [ "_MDAT" ],
+ "smwgQueryResultCacheType": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "mw-1.19.20": "<h1> contains an extra space"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0803.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0803.json
new file mode 100644
index 00000000..bd3e9002
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0803.json
@@ -0,0 +1,169 @@
+{
+ "description": "Test `format=template` with `sep`/`named args`/`template arguments` (#972, #2022, #2567)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0803/Sep",
+ "contents": "<includeonly>{{{Has text}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0803/Numbered",
+ "contents": "<includeonly>{{{1}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0803/Named",
+ "contents": "<includeonly>{{{Has text}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0803/NamedWithMainlabel",
+ "contents": "<includeonly>{{{main}}}{{{Has text}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/F0803/Legacy",
+ "contents": "<includeonly>{{{?Has text}}}</includeonly>"
+ },
+ {
+ "page": "Example/F0803/1",
+ "contents": "{{#subobject: |@category=F0803 |Has text=123 |Has text=456 }} {{#subobject: |@category=F0803 |Has text=abc }}"
+ },
+ {
+ "page": "Example/F0803/2",
+ "contents": "[[Category:F0803]][[Has text::foo\n=bar]]"
+ },
+ {
+ "page": "Example/F0803/Q.1",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Sep |mainlabel=main |named args=yes |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; <nowiki/>|sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.3",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Legacy |mainlabel=main |named args=yes |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; |sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.4",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Named |mainlabel=main |named args=yes |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; <nowiki/>|sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.5",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/NamedWithMainlabel |mainlabel=main |named args=yes |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; <nowiki/>|sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.6",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Numbered |mainlabel=main |named args=no |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; |sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.7",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Numbered |mainlabel=- |named args=no |link=none |sep= &#32;&bull;&#32; |valuesep= &#32;&bull;&#32; <nowiki/>|sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.8",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Sep |mainlabel=main |named args=yes |link=none |sep= &#32;&bull;&#32; |sort=Has text |order=asc }}"
+ },
+ {
+ "page": "Example/F0803/Q.9",
+ "contents": "{{#ask: [[Category:F0803]] |?Has text |format=template |template=Example/F0803/Sep |named args=yes |sort=Has text |order=asc }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/F0803/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "123&#32;&#8226;&#32; 456&#32;&#8226;&#32;abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (template contains ?... )",
+ "subject": "Example/F0803/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "{{{?Has text}}}&#32;&#8226;&#32;{{{?Has text}}}"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (`named` selected)",
+ "subject": "Example/F0803/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "123&#32;&#8226;&#32; 456&#32;&#8226;&#32;abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (`named` selected with mainlabel)",
+ "subject": "Example/F0803/Q.5",
+ "assert-output": {
+ "to-contain": [
+ "Example/F0803/1#_a8833df1372ac8f410272cb680410853123&#32;&#8226;&#32; 456&#32;&#8226;&#32;Example/F0803/1#_41e021fb1955f0af8aa8b9dcb8313425abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (`numbered` selected)",
+ "subject": "Example/F0803/Q.6",
+ "assert-output": {
+ "to-contain": [
+ "Example/F0803/1#_a8833df1372ac8f410272cb680410853&#32;&#8226;&#32;Example/F0803/1#_41e021fb1955f0af8aa8b9dcb8313425"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (`numbered` selected, without mainlabel)",
+ "subject": "Example/F0803/Q.7",
+ "assert-output": {
+ "to-contain": [
+ "123&#32;&#8226;&#32; 456&#32;&#8226;&#32;abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (`valuesep` parameter omitted)",
+ "subject": "Example/F0803/Q.8",
+ "assert-output": {
+ "to-contain": [
+ "123, 456&#32;&#8226;&#32;abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (#2567)",
+ "subject": "Example/F0803/Q.9",
+ "assert-output": {
+ "to-contain": [
+ "123, 456abcfoo\n=bar"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [ "_MDAT" ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0804.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0804.json
new file mode 100644
index 00000000..1a0668b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0804.json
@@ -0,0 +1,83 @@
+{
+ "description": "Test `format=embedded` with template transclution",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasSomePageProperty",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TmplP0804",
+ "contents": "<includeonly>[[Category:P0804]]</includeonly>"
+ },
+ {
+ "page": "Example/P0804/1",
+ "contents": "[[Category:P0804]]"
+ },
+ {
+ "page": "Example/P0804/2",
+ "contents": "{{TmplP0804}} [[Category:P0804/1]]"
+ },
+ {
+ "page": "Example/P0804/3",
+ "contents": "{{#ask:[[Category:P0804]] [[Category:P0804/1]] |format=embedded }} [[Category:P0804/3]]"
+ },
+ {
+ "page": "Example/P0804/Q.1",
+ "contents": "{{#ask:[[Category:P0804]] |format=embedded }}"
+ },
+ {
+ "page": "Example/P0804/Q.2",
+ "contents": "{{#ask:[[Category:P0804/3]] |format=embedded }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 only those with [[Category:P0804]] (exludes Example/P0804/3 as no class was imported)",
+ "subject": "Example/P0804/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"mw-headline\" id=\"Example.2FP0804.2F1\"><a href=.* title=\"Example/P0804/1\">Example/P0804/1</a></span>",
+ "<span class=\"mw-headline\" id=\"Example.2FP0804.2F2\"><a href=.* title=\"Example/P0804/2\">Example/P0804/2</a></span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 only those reachable via [[Category:P0804/3]] ",
+ "subject": "Example/P0804/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"mw-headline\" id=\"Example.2FP0804.2F3\"><a href=.* title=\"Example/P0804/3\">Example/P0804/3</a></span>",
+ "<span class=\"mw-headline\" id=\"Example.2FP0804.2F2\"><a href=.* title=\"Example/P0804/2\">Example/P0804/2</a></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [ "_MDAT" ],
+ "smwgQueryResultCacheType": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0805.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0805.json
new file mode 100644
index 00000000..55428e45
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/f-0805.json
@@ -0,0 +1,63 @@
+{
+ "description": "Test `format=template`, `format=plainlist` with `#show` and template args (#502)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Template/F0805_show_text",
+ "contents": "<includeonly>{{{Has text}}}</includeonly>"
+ },
+ {
+ "page": "Example/F0805/1",
+ "contents": "[[Has text::123]]"
+ },
+ {
+ "page": "Example/F0805/Q.1",
+ "contents": "{{#show: Example/F0805/1 |?Has text |format=template |named args=yes |template=Template/F0805_show_text }}"
+ },
+ {
+ "page": "Example/F0805/Q.2",
+ "contents": "{{#show: Example/F0805/1 |?Has text |format=plainlist |named args=yes |template=Template/F0805_show_text }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (#show, `format=template` to support template parameters)",
+ "subject": "Example/F0805/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "123"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (#show, `format=plainlist` to support template parameters)",
+ "subject": "Example/F0805/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "123"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0101.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0101.json
new file mode 100644
index 00000000..18d4bc79
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0101.json
@@ -0,0 +1,174 @@
+{
+ "description": "Test in-text annotation for use of restricted properties (#914, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "To-verify-annotation-works",
+ "contents": "[[Has date::1 Jan 1970]] {{#set:|Has date=1 Jan 1970}} {{#subobject:|Has date=1 Jan 1970}}"
+ },
+ {
+ "message-cache": "clear",
+ "page": "Modification-date",
+ "contents": "[[Modification date::1 Jan 1970]] {{#set:|Modification date=1 Jan 1970}} {{#subobject:|Modification date=1 Jan 1970}}"
+ },
+ {
+ "page": "Has-query",
+ "contents": "[[Has query::Invalid]] {{#set:|Has query=Invalid}} {{#subobject:|Has query=Invalid}}"
+ },
+ {
+ "page": "Has-subobject",
+ "contents": "[[Has subobject::Invalid]] {{#set:|Has subobject=Invalid}} {{#subobject:|Has subobject=Invalid}}"
+ },
+ {
+ "page": "Example/P0101/5",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0101/6",
+ "contents": "{{#set:|Has type=Page}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 verify that annotation/parser works",
+ "subject": "To-verify-annotation-works",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_date",
+ "_MDAT",
+ "_SKEY",
+ "_SOBJ"
+ ],
+ "propertyValues": [
+ "1970-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "1 Jan 1970"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 verify no free annotation using Modification date (_MDAT) is created by the system",
+ "subject": "Modification-date",
+ "assert-store": {
+ "errors": [],
+ "semantic-data": {
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_SOBJ",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property &quot;Modification date&quot; has a restricted application area and cannot be used as annotation property by a user."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 verify no free annotation using Has query",
+ "subject": "Has-query",
+ "assert-store": {
+ "semantic-data": {
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_SOBJ",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property &quot;Has query&quot; has a restricted application area and cannot be used as annotation property by a user."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 verify no free annotation using Has subobject",
+ "subject": "Has-subobject",
+ "assert-store": {
+ "semantic-data": {
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_SOBJ",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property &quot;Has subobject&quot; has a restricted application area and cannot be used as annotation property by a user."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 error on [[ ...]](NS_MAIN) with restricted declarative property",
+ "subject": "Example/P0101/5#_ERR20a1185193cbb57b6472d45844e0abbe",
+ "assert-store": {
+ "semantic-data": {
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRP",
+ "_ERRT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[8,\"smw-datavalue-property-restricted-declarative-use\",\"Has type\"]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 error on #set(NS_MAIN) with restricted declarative property",
+ "subject": "Example/P0101/6#_ERR20a1185193cbb57b6472d45844e0abbe",
+ "assert-store": {
+ "semantic-data": {
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRP",
+ "_ERRT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[8,\"smw-datavalue-property-restricted-declarative-use\",\"Has type\"]"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0102.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0102.json
new file mode 100644
index 00000000..02aef877
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0102.json
@@ -0,0 +1,258 @@
+{
+ "description": "Test in-text annotation on properties with invalid names/characters (#1567, #1638, #1727 `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has #",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0102/1",
+ "contents": "[[&#124; &#93;&#93;&lt;/td&gt;&lt;::123]]"
+ },
+ {
+ "page": "Example/P0102/2",
+ "contents": "[[<code>Has property a</code>::ABC]] {{#set: <code>Has property b</code>=DEF }}"
+ },
+ {
+ "page": "Example/P0102/3",
+ "contents": "[[<code>Has property e::ABC]] {{#set: <code>Has property f=DEF }}"
+ },
+ {
+ "page": "Example/P0102/4",
+ "contents": "[[File:[[Picture:: Foo.png ]]"
+ },
+ {
+ "page": "Example/P0102/Q3.1",
+ "contents": "{{#ask: [[<code>Has property e::ABC]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0102/5",
+ "contents": "[[<Foo>::abc]]"
+ },
+ {
+ "page": "Example/P0102/6",
+ "contents": "[[<Foo-<Bar>::abc]]"
+ },
+ {
+ "page": "Example/P0102/7",
+ "contents": "[[-<Foo>-<Bar>::abc]]"
+ },
+ {
+ "page": "Example/P0102/8",
+ "contents": "[[Foo-<Bar::abc]]"
+ },
+ {
+ "page": "Example/P0102/9",
+ "contents": "[[Foo-<Bar>::abc]]"
+ },
+ {
+ "page": "Example/P0102/10",
+ "contents": "[[Foo\nBar::abc]] [[Foo\rBar::123]] [[Foo\r\nBar::456]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 do not allow #",
+ "subject": "Example/P0102/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 strip tags",
+ "subject": "Example/P0102/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_property_a",
+ "Has_property_b",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "ABC",
+ "DEF"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 remove broken tags",
+ "subject": "Example/P0102/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_property_e",
+ "Has_property_f",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "ABC",
+ "DEF"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 allow things like sort=#",
+ "subject": "Example/P0102/Q3.1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 `[` to produce an error",
+ "subject": "Example/P0102/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 `<>` to produce an error",
+ "subject": "Example/P0102/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 `<>` to produce an error",
+ "subject": "Example/P0102/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 `<>` to produce an error",
+ "subject": "Example/P0102/7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 to cut the name at `<`",
+ "subject": "Example/P0102/8",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Foo-",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 to cut the name at `<`",
+ "subject": "Example/P0102/9",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Foo-",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 (CR, LF are invalid)",
+ "subject": "Example/P0102/10",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0106.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0106.json
new file mode 100644
index 00000000..79f420ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0106.json
@@ -0,0 +1,67 @@
+{
+ "description": "Test #info parser output (#1019, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "message-cache": "clear",
+ "page": "P0106/Warning",
+ "contents": "{{#info: an warning text | warning }}"
+ },
+ {
+ "message-cache": "clear",
+ "page": "P0106/Error",
+ "contents": "{{#info: an error text | error }}"
+ },
+ {
+ "message-cache": "clear",
+ "page": "P0106/Note",
+ "contents": "{{#info: an info note | note }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "P0106/Warning",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"an warning text\">",
+ "<span class=\"smwtticon warning\">",
+ "<span class=\"smwttcontent\">an warning text</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "P0106/Error",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"5\" data-state=\"inline\" data-title=\"Error\" title=\"an error text\">",
+ "<span class=\"smwtticon error\">",
+ "<span class=\"smwttcontent\">an error text</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "P0106/Note",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"8\" data-state=\"inline\" data-title=\"Note\" title=\"an info note\">",
+ "<span class=\"smwtticon note\">",
+ "<span class=\"smwttcontent\">an info note</span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0107.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0107.json
new file mode 100644
index 00000000..19b4411c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0107.json
@@ -0,0 +1,145 @@
+{
+ "description": "Test #smwdoc parser output",
+ "setup": [
+ {
+ "page": "smwdoc-table",
+ "contents": "{{#smwdoc: table |parameters=base }}"
+ },
+ {
+ "page": "smwdoc-csv",
+ "contents": "{{#smwdoc: csv |parameters=all }}"
+ },
+ {
+ "page": "smwdoc-list",
+ "contents": "{{#smwdoc: list |parameters=all |language=de }}"
+ },
+ {
+ "page": "smwdoc-count",
+ "contents": "{{#smwdoc: count }}"
+ },
+ {
+ "page": "smwdoc-debug",
+ "contents": "{{#smwdoc: debug |parameters=specific |language=en }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (1.31-) (#1019)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-table",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"wikitable sortable\">",
+ "</td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#0 (1.31+) (#1019)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-table",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"wikitable sortable\">",
+ "</td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31-) (#1019)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-csv",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"wikitable sortable\">",
+ "</td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31+) (#1019)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-csv",
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"wikitable sortable\">",
+ "</td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31-) (#1019)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-list",
+ "assert-output": {
+ "to-contain": [
+ "</td>",
+ "<td>Legt fest, welche zusätzliche CSS-Klasse genutzt werden soll",
+ "</td></tr>",
+ "</td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31+) (#1019)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "smwdoc-list",
+ "assert-output": {
+ "to-contain": [
+ "</td>",
+ "<td>Legt fest, welche zusätzliche CSS-Klasse genutzt werden soll",
+ "</td></tr>",
+ "</td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (#3186)",
+ "subject": "smwdoc-count",
+ "assert-output": {
+ "to-contain": [
+ "This result format does not provide format specific parameters."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (#3186)",
+ "subject": "smwdoc-debug",
+ "assert-output": {
+ "to-contain": [
+ "This result format does not provide format specific parameters."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0108.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0108.json
new file mode 100644
index 00000000..b2dd3058
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0108.json
@@ -0,0 +1,117 @@
+{
+ "description": "Test `#info`, `#ask` template output (#2347, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "P0108.Ask.Output",
+ "contents": "<includeonly>{{{1}}} {{{2}}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "P0108.Ask",
+ "contents": "<includeonly>{{#ask: [[Has page::P0108]] |?Has page |limit=1 |format=template |link=none |template=P0108.Ask.Output }}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "P0108",
+ "contents": "{{#info: message={{P0108.Ask}} }}"
+ },
+ {
+ "page": "Test/P0108/1",
+ "contents": "[[Has page::P0108]]"
+ },
+ {
+ "page": "Test/P0108/2",
+ "contents": "{{P0108}}"
+ },
+ {
+ "page": "Test/P0108/3",
+ "contents": "{{#set: Has text=Text with an [[Has page::annotation]] }}"
+ },
+ {
+ "page": "Test/P0108/4",
+ "contents": "{{#info: message={{#show: Test/P0108/3 |?Has text }} }}"
+ },
+ {
+ "page": "Test/P0108/5",
+ "contents": "{{#info: {{#show: Test/P0108/3 |?Has text }} }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #info with template generate output including encoded (&#91;) on/off marker",
+ "subject": "Test/P0108/2",
+ "skip-on": {
+ "mw-1.23.x": "works different in 1.23+"
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Test/P0108/1 P0108\">",
+ "<span class=\"smwttcontent\">Test/P0108/1 P0108</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 $info + #show (no annotation is leaked via a text element)",
+ "subject": "Test/P0108/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Text with an annotation\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 $info + #show (no annotation is leaked via a text element)",
+ "subject": "Test/P0108/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Text with an annotation\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0109.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0109.json
new file mode 100644
index 00000000..7f31bfc6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0109.json
@@ -0,0 +1,54 @@
+{
+ "description": "Test `#info`, `#ask`/`#show` with error output (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Test/P0109/1",
+ "contents": "Info: {{#info: message=Test some error {{#show: [[Test/P0109/2]] |?Has text }} }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (1.31-) switch #info to error display",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Test/P0109/1",
+ "assert-output": {
+ "to-contain": [
+ "Info: Test some error <span class=\"smw-highlighter\" data-type=\"4\""
+ ],
+ "not-contain": [
+ "Info: <span class=\"smw-highlighter\" data-type=\"5\"",
+ "<div class=\"smwttcontent\">Test some error <span class=\"smw-highlighter\" data-type=\"4\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#0 (1.31+) switch #info to error display",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Test/P0109/1",
+ "assert-output": {
+ "to-contain": [
+ "Info: Test some error </p><span class=\"smw-highlighter\" data-type=\"4\""
+ ],
+ "not-contain": [
+ "Info: <span class=\"smw-highlighter\" data-type=\"5\"",
+ "<div class=\"smwttcontent\">Test some error <span class=\"smw-highlighter\" data-type=\"4\""
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0110.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0110.json
new file mode 100644
index 00000000..56b1b16a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0110.json
@@ -0,0 +1,43 @@
+{
+ "description": "Test tooltip with error output on `_PVUC` (`smwgDVFeatures`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has Url",
+ "contents": "[[Has type::URL]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "page": "Test/P0110/1",
+ "contents": "[[Has Url::http://example.org/Foo]]"
+ },
+ {
+ "page": "Test/P0110/2",
+ "contents": "[[Has Url::http://example.org/Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 error tooltip, title does not include <a> elements",
+ "subject": "Test/P0110/2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property &quot;Has Url&quot; only permits unique value assignments and http://example.org/Foo",
+ "<span class=\"smwttcontent\">Property \"Has Url\" only permits unique value assignments and <i><a rel=\"nofollow\" class=\"external free\" href=\"http://example.org/Foo\">http://example.org/Foo</a></i>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgDVFeatures": [
+ "SMW_DV_PVUC"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0111.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0111.json
new file mode 100644
index 00000000..2a35981b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0111.json
@@ -0,0 +1,35 @@
+{
+ "description": "Test reserved property names",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Category",
+ "contents": "[[Has type::Page]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 reserved category name",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Category",
+ "assert-output": {
+ "onPageView": {},
+ "to-contain": [
+ "<div id=\"smw-property-name-reserved\" class=\"plainlinks smw-callout smw-callout-error\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0112.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0112.json
new file mode 100644
index 00000000..7f659c20
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0112.json
@@ -0,0 +1,58 @@
+{
+ "description": "Test #set_recurring_event parser (#3541, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "John Doe's Birthdays",
+ "contents": "{{#set_recurring_event: Is birthday |property=Has date |start=1948-11-03 |end=2019-01-06 |unit=year |period=1 }}"
+ },
+ {
+ "page": "John Doe's Number of Birthdays",
+ "contents": "{{#ask: [[Is birthday::John Doe's Birthdays]] |format=count }}"
+ },
+ {
+ "page": "Jane Doe's Birthdays",
+ "contents": "{{#set_recurring_event: Is birthday |property=Has date |start=1953-05-06 |unit=year |period=1 }}"
+ },
+ {
+ "page": "Jane Doe's Number of Birthdays",
+ "contents": "{{#ask: [[Is birthday::Jane Doe's Birthdays]] |format=count }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #set_recurring_event parser with end date",
+ "subject": "John Doe's Number of Birthdays",
+ "assert-output": {
+ "to-contain": [
+ "71"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 #set_recurring_event parser without end date",
+ "subject": "Jane Doe's Number of Birthdays",
+ "assert-output": {
+ "to-contain": [
+ "111"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDefaultNumRecurringEvents": "110"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0113.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0113.json
new file mode 100644
index 00000000..d6d39ce1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0113.json
@@ -0,0 +1,126 @@
+{
+ "description": "Test #set_recurring_event parser include and exclude parameters",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Team meetings - en",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=January 14, 2019 9:30 am |end=June 30, 2019 |unit=week |period=2 |include=May 7, 2019 9:30 am; June 18, 2019 9:30 am; June 27, 2019 10:00 am |exclude=May 6, 2019 9:30 am; June 17, 2019 9:30 am }}"
+ },
+ {
+ "page": "Team meetings - fr",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=14 janvier 2019 9:30 am |end=30 juin 2019 |unit=week |period=2 |include=7 mai 2019 9:30 am; 18 juin 2019 9:30 am; 27 juin 2019 10:00 am |exclude=6 mai 2019 9:30 am; 17 juin 2019 9:30 am }}"
+ },
+ {
+ "page": "Team meetings - ISO",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=2019-01-14T09:30:00 |end=2019-06-30 |unit=week |period=2 |include=2019-05-07T09:30:00; 2019-06-18T09:30:00; 2019-06-27T10:00:00 |exclude=2019-05-06T09:30:00; 2019-06-17T09:30:00 }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 0 - en",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - en]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 1 - en",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - en]] |?Has date |format=plainlist }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 2 - fr",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - fr]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 3 - fr",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - fr]] |?Has date |format=plainlist }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 4 - ISO",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - ISO]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in first half of 2019 - 5 - ISO",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - ISO]] |?Has date |format=plainlist }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #set_recurring_event parser with end date, included and excluded dates - count",
+ "subject": "Team meetings in first half of 2019 - 0 - en",
+ "assert-output": {
+ "to-contain": [
+ "13"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 #set_recurring_event parser with end date, included and excluded dates - print",
+ "subject": "Team meetings in first half of 2019 - 1 - en",
+ "assert-output": {
+ "to-contain": [
+ "27 juin 2019 10:00:00"
+ ],
+ "not-contain": [
+ "6 mai 2019 09:30:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 #set_recurring_event parser with end date, included and excluded dates - count",
+ "subject": "Team meetings in first half of 2019 - 2 - fr",
+ "assert-output": {
+ "to-contain": [
+ "13"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #set_recurring_event parser with end date, included and excluded dates - print",
+ "subject": "Team meetings in first half of 2019 - 3 - fr",
+ "assert-output": {
+ "to-contain": [
+ "27 juin 2019 10:00:00"
+ ],
+ "not-contain": [
+ "6 mai 2019 09:30:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #set_recurring_event parser with end date, included and excluded dates - count",
+ "subject": "Team meetings in first half of 2019 - 4 - ISO",
+ "assert-output": {
+ "to-contain": [
+ "13"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #set_recurring_event parser with end date, included and excluded dates - print",
+ "subject": "Team meetings in first half of 2019 - 5 - ISO",
+ "assert-output": {
+ "to-contain": [
+ "27 juin 2019 10:00:00"
+ ],
+ "not-contain": [
+ "6 mai 2019 09:30:00"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "fr",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0114.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0114.json
new file mode 100644
index 00000000..6921be44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0114.json
@@ -0,0 +1,85 @@
+{
+ "description": "Test #set_recurring_event parser week number parameter",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Team meetings 1",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=January 15, 2019 9:30 am |end=December 31, 2019 |unit=month |week number=2 }}"
+ },
+ {
+ "page": "Team meetings 2",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=January 31, 2019 9:30 am |end=December 31, 2019 |unit=month |week number=-1 }}"
+ },
+ {
+ "page": "Team meetings in 2019 - 0",
+ "contents": "{{#ask: [[Is team meeting::Team meetings 1]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in 2019 - 1",
+ "contents": "{{#ask: [[Is team meeting::Team meetings 1]] |?Has date |format=plainlist }}"
+ },
+ {
+ "page": "Team meetings in 2019 - 2",
+ "contents": "{{#ask: [[Is team meeting::Team meetings 2]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in 2019 - 3",
+ "contents": "{{#ask: [[Is team meeting::Team meetings 2]] |?Has date |format=plainlist }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #set_recurring_event parser with end date and week number dates - count",
+ "subject": "Team meetings in 2019 - 0",
+ "assert-output": {
+ "to-contain": [
+ "12"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 #set_recurring_event parser with end date and week number dates - print",
+ "subject": "Team meetings in 2019 - 1",
+ "assert-output": {
+ "to-contain": [
+ "10 décembre 2019 09:30:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 #set_recurring_event parser with end date and week number dates - count",
+ "subject": "Team meetings in 2019 - 2",
+ "assert-output": {
+ "to-contain": [
+ "12"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #set_recurring_event parser with end date and week number dates - print",
+ "subject": "Team meetings in 2019 - 3",
+ "assert-output": {
+ "to-contain": [
+ "29 août 2019 09:30:00"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "fr",
+ "wgLang": "de"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0115.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0115.json
new file mode 100644
index 00000000..96c9c8bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0115.json
@@ -0,0 +1,130 @@
+{
+ "description": "Test `#set_recurring_event` parser for events on 29th to 31st of the month (#3598 - `wgContLang=fr`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Team meetings - en",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=January 31, 2003 9:30 am |end=December 31, 2004 9:30 am |unit=month }}"
+ },
+ {
+ "page": "Team meetings - fr",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=30 janvier 2003 9:30 am |end=31 décembre 2004 9:30 am |unit=month }}"
+ },
+ {
+ "page": "Team meetings - ISO",
+ "contents": "{{#set_recurring_event: Is team meeting |property=Has date |start=2003-01-29T09:30:00 |end=2004-12-31T09:30:00 |unit=month }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 0 - en",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - en]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 1 - en",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - en]] |?Has date |format=plainlist }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 2 - fr",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - fr]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 3 - fr",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - fr]] |?Has date |format=plainlist }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 4 - ISO",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - ISO]] |format=count }}"
+ },
+ {
+ "page": "Team meetings in 2003 and 2004 - 5 - ISO",
+ "contents": "{{#ask: [[Is team meeting::Team meetings - ISO]] |?Has date |format=plainlist }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #set_recurring_event parser for the 31st or alternatively the last day of a month - count",
+ "subject": "Team meetings in 2003 and 2004 - 0 - en",
+ "assert-output": {
+ "to-contain": [
+ "24"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 #set_recurring_event parser for the 31st or alternatively the last day of a month - print",
+ "subject": "Team meetings in 2003 and 2004 - 1 - en",
+ "assert-output": {
+ "to-contain": [
+ "31 mars 2003 09:30:00",
+ "30 avril 2003 09:30:00",
+ "30 novembre 2004 09:30:00",
+ "31 décembre 2004 09:30:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 #set_recurring_event parser for the 30th or alternatively the last day of a month - count",
+ "subject": "Team meetings in 2003 and 2004 - 2 - fr",
+ "assert-output": {
+ "to-contain": [
+ "24"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #set_recurring_event parser for the 30th or alternatively the last day of a month - print",
+ "subject": "Team meetings in 2003 and 2004 - 3 - fr",
+ "assert-output": {
+ "to-contain": [
+ "28 février 2003 09:30:00",
+ "30 mars 2003 09:30:00",
+ "29 février 2004 09:30:00",
+ "30 mars 2004 09:30:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #set_recurring_event parser for the 29th or alternatively the last day of a month - count",
+ "subject": "Team meetings in 2003 and 2004 - 4 - ISO",
+ "assert-output": {
+ "to-contain": [
+ "24"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #set_recurring_event parser for the 29th or alternatively the last day of a month - print",
+ "subject": "Team meetings in 2003 and 2004 - 5 - ISO",
+ "assert-output": {
+ "to-contain": [
+ "28 février 2003 09:30:00",
+ "29 août 2003 09:30:00",
+ "29 février 2004 09:30:00",
+ "29 août 2004 09:30:00"
+ ],
+ "not-contain": [
+ "29 février 2003 09:30:00",
+ "28 février 2004 09:30:00"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "fr",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0202.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0202.json
new file mode 100644
index 00000000..fa6e8ef2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0202.json
@@ -0,0 +1,84 @@
+{
+ "description": "Test #set parser to use template for output (#1146, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "SetParserTemplateToCreateAskLink",
+ "contents": "<includeonly>{{#ask: [[{{{property}}}::{{{value}}}]]|limit=0|searchlabel={{{value}}}|format=list }}</includeonly>"
+ },
+ {
+ "page": "Transclude-Template-Using-Set",
+ "contents": "{{#set:SetParserTemplateProperty=SetParserTemplateValue|+sep=;|template=SetParserTemplateToCreateAskLink}}"
+ },
+ {
+ "page": "Try-To-Transclude-Template-For-Invalid-PropertyValue",
+ "contents": "{{#set:Has date=NoTemplateForInvalidValue|+sep=;|template=SetParserTemplateToCreateAskLink}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #set annotation/parser with template",
+ "subject": "Transclude-Template-Using-Set",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "SetParserTemplateProperty"
+ ],
+ "propertyValues": [
+ "SetParserTemplateValue"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 invalid property value declaration causes error",
+ "subject": "Try-To-Transclude-Template-For-Invalid-PropertyValue",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #ask link to be correctly parsed, #1146",
+ "subject": "Transclude-Template-Using-Set",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-list-furtherresults\">",
+ "Special:Ask/-5B-5BSetParserTemplateProperty::SetParserTemplateValue-5D-5D/mainlabel=/offset=0/format=list/searchlabel=SetParserTemplateValue\">SetParserTemplateValue</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0203.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0203.json
new file mode 100644
index 00000000..83e3367e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0203.json
@@ -0,0 +1,104 @@
+{
+ "description": "Test #set parser in combination with #subobject and template output (#1067, regression check)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Paragraph number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Paragraph",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Paragraph backlink",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "SimpleSetTemplate",
+ "contents": "<includeonly>Lorem ipsum.{{#set:Reference=Test}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "CreateParagraphAsSubobject",
+ "contents": "<includeonly><div style=\"display:none;\">{{#subobject:|Paragraph number={{{Paragraph number|}}}|Paragraph={{{Paragraph|}}}|Paragraph backlink={{PAGENAME}} }}</div></includeonly>"
+ },
+ {
+ "page": "Transclude-Template",
+ "contents": "{{CreateParagraphAsSubobject|Paragraph number=1|Paragraph=Test1 {{SimpleSetTemplate}} }} {{SimpleSetTemplate}}"
+ },
+ {
+ "page": "Ask-For-Transclude-Template",
+ "contents": "{{#ask:[[Paragraph backlink::Transclude-Template]]|?Paragraph}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Transclude-Template",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "Reference",
+ "_SOBJ"
+ ],
+ "propertyValues": [
+ "Test",
+ "Transclude-Template#_2d7f2b93bd6e9816ff85f022cddc62eb"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 subobject check",
+ "subject": "Transclude-Template#_2d7f2b93bd6e9816ff85f022cddc62eb",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Paragraph",
+ "Paragraph_backlink",
+ "Paragraph_number",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "1",
+ "Test1 Lorem ipsum.",
+ "Transclude-Template"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 check for the ask output",
+ "subject": "Ask-For-Transclude-Template",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Transclude-Template\">Transclude-Template</a>",
+ "<td class=\"Paragraph smwtype_txt\">Test1 Lorem ipsum.</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0204.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0204.json
new file mode 100644
index 00000000..aa556f91
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0204.json
@@ -0,0 +1,54 @@
+{
+ "description": "Test #set parser to produce error output (#870, en, verify that #set calls do not affect each other with previous errors)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Set-number-with-error",
+ "contents": "{{#set:Has number=12}} {{#set:Has number=32a}} {{#set:Has number=66}} {{#set:Has number=88b}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 with error",
+ "subject": "Set-number-with-error",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_number",
+ "_ERRC",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ 12,
+ 66
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "&quot;a&quot; can not be assigned to a declared number type with value 32.",
+ "&quot;b&quot; can not be assigned to a declared number type with value 88."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0205.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0205.json
new file mode 100644
index 00000000..d69dbf4e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0205.json
@@ -0,0 +1,87 @@
+{
+ "description": "Test #set/#ask recursive annotation support (#711, #1055, recursive annotation using import-annotation=true via template)",
+ "setup": [
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "AskTemplateToAddPropertyAnnotation",
+ "contents": "<includeonly>{{#set:|ByAskTemplateToSetProperty=1234}}</includeonly>"
+ },
+ {
+ "page": "Example/0205",
+ "contents": "{{#set:|TestPropertyByAskTemplate=TestValueByAskTemplate}}"
+ },
+ {
+ "page": "Example/0205/Ask/List",
+ "contents": "{{#ask:[[TestPropertyByAskTemplate::TestValueByAskTemplate]]|link=none|sep=|format=list|template=AskTemplateToAddPropertyAnnotation|import-annotation=true}}"
+ },
+ {
+ "page": "Example/0205/Ask/Embedded/1",
+ "contents": "{{#ask:[[TestPropertyByAskTemplate::TestValueByAskTemplate]]|link=none|sep=|format=embedded|import-annotation=false}}"
+ },
+ {
+ "page": "Example/0205/Ask/Embedded/2",
+ "contents": "{{#ask:[[ByAskTemplateToSetProperty::1234]]|link=none|sep=|format=embedded|import-annotation=false}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/0205/Ask/List",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "ByAskTemplateToSetProperty"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 embbeded with disabled recursive parse support",
+ "subject": "Example/0205/Ask/Embedded/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 embbeded with disabled recursive parse support",
+ "subject": "Example/0205/Ask/Embedded/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0206.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0206.json
new file mode 100644
index 00000000..0ebac9fd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0206.json
@@ -0,0 +1,105 @@
+{
+ "description": "Test #show parser on inverse printrequest (#1222, #1223)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasWorkManifested",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasWorkManifestedAsText",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/0206/1",
+ "contents": "[[HasWorkManifested::ABC]] + {{#show:{{FULLPAGENAME}}|?-HasWorkManifested|link=none}} + {{#show:{{FULLPAGENAME}}|?HasWorkManifested|link=none}}"
+ },
+ {
+ "page": "Example/0206/2",
+ "contents": "[[HasWorkManifested::{{FULLPAGENAME}}]] + {{#show:{{FULLPAGENAME}}|?-HasWorkManifested|link=none}} + {{#show:{{FULLPAGENAME}}|?HasWorkManifested|link=none}}"
+ },
+ {
+ "page": "Example/0206/3",
+ "contents": "[[HasWorkManifestedAsText::{{FULLPAGENAME}}]] + {{#show:{{FULLPAGENAME}}|?-HasWorkManifestedAsText|link=none}} + {{#show:{{FULLPAGENAME}}|?HasWorkManifestedAsText|link=none}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 where inverse is unknown and not displayed",
+ "subject": "Example/0206/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "HasWorkManifested"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">ABC</a> + + ABC"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 inverse pointing to itself",
+ "subject": "Example/0206/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "HasWorkManifested"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Example/0206/2.* + Example/0206/2 + Example/0206/2"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2, see #1222, inverse is not displayed compared to #1",
+ "subject": "Example/0206/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "HasWorkManifestedAsText"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<p>Example/0206/3 + + Example/0206/3"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0207.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0207.json
new file mode 100644
index 00000000..331722a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0207.json
@@ -0,0 +1,89 @@
+{
+ "description": "Test that undeclared properties with references remain after a `rebuildData` run (#1216, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Test/P0207",
+ "contents": "{{#set:Has number=12}} {{#set:Undeclared property=abc}} [[Undeclared prop::P0207]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Test/P0207/1",
+ "contents": "{{:Test/P0207}}"
+ }
+ ],
+ "beforeTest": {
+ "maintenance-run": {
+ "rebuildData": true
+ }
+ },
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 Rebuild + clear cache to verify that the disposer (#1216) didn't remove undeclared properties that still contain references",
+ "subject": "Test/P0207",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "Has_number",
+ "_ERRP",
+ "_SKEY",
+ "_MDAT",
+ "Undeclared_property",
+ "Undeclared_prop"
+ ],
+ "propertyValues": [
+ 12,
+ "Abc",
+ "P0207"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 embedded page on disabled namespace",
+ "namespace": "NS_HELP",
+ "subject": "Test/P0207/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 0
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<a href=\".* title=\"P0207.*\">P0207</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_HELP": false,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0208.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0208.json
new file mode 100644
index 00000000..2e42ef6c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0208.json
@@ -0,0 +1,236 @@
+{
+ "description": "Test `#set` for various `_num` values without explicit precision (3 digit implicit), with/without leading zero, different printouts, negative numbers (#753, en, `smwgMaxNonExpNumber`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/P0208/1",
+ "contents": "{{#set:Has number=10000;2000;300;40;0;0.1;0.01;0.001;0.0001;0.02;0.003;0.0004;0.00005;4,297.31 e7|+sep=; }}"
+ },
+ {
+ "page": "Example/P0208/2",
+ "contents": "{{#set:Has number=.1;.01;.001;.00001;4.0e-4;.2e-5;.3e5|+sep=; }}"
+ },
+ {
+ "page": "Example/P0208/3",
+ "contents": "{{#set:Has number=-.1;-0.2;-.2e-5;-.3e5|+sep=; }}"
+ },
+ {
+ "page": "Example/P0208/1/1",
+ "contents": "{{#ask: [[Example/P0208/1]] |?Has number |format=table }}"
+ },
+ {
+ "page": "Example/P0208/1/2",
+ "contents": "{{#ask: [[Example/P0208/1]] |?Has number# |format=table }}"
+ },
+ {
+ "page": "Example/P0208/2/1",
+ "contents": "{{#ask: [[Example/P0208/2]] |?Has number |format=table }}"
+ },
+ {
+ "page": "Example/P0208/2/2",
+ "contents": "{{#ask: [[Example/P0208/2]] |?Has number# |format=table }}"
+ },
+ {
+ "page": "Example/P0208/3/1",
+ "contents": "{{#ask: [[Example/P0208/3]] |?Has number |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0208/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_number",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2,000",
+ "10,000",
+ "300",
+ "40",
+ "0",
+ "0.1",
+ "0.01",
+ "0.001",
+ "1.0e-4",
+ "0.02",
+ "0.003",
+ "4.0e-4",
+ "5.0e-5",
+ "42,973,100,000"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0208/2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_number",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "0.1",
+ "0.01",
+ "0.001",
+ "1.0e-5",
+ "4.0e-4",
+ "2.0e-6",
+ "30000"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 #ask number output",
+ "subject": "Example/P0208/1/1",
+ "assert-output": {
+ "to-contain": [
+ "2,000",
+ "10,000",
+ "300",
+ "40",
+ "0",
+ "0.1",
+ "0.01",
+ "0.001",
+ "1.0e-4",
+ "0.02",
+ "0.003",
+ "4.0e-4",
+ "5.0e-5",
+ "42,973,100,000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #ask plain number output",
+ "subject": "Example/P0208/1/2",
+ "assert-output": {
+ "to-contain": [
+ "2000",
+ "10000",
+ "300",
+ "40",
+ "0",
+ "0.1",
+ "0.01",
+ "0.001",
+ "0.0001",
+ "0.02",
+ "0.003",
+ "0.0004",
+ "5.0e-5",
+ "42973100000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #ask number output (non leading zero)",
+ "subject": "Example/P0208/2/1",
+ "assert-output": {
+ "to-contain": [
+ "0.1",
+ "0.01",
+ "0.001",
+ "1.0e-5",
+ "4.0e-4",
+ "2.0e-6",
+ "30,000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #ask plain number output (non leading zero)",
+ "subject": "Example/P0208/2/2",
+ "assert-output": {
+ "to-contain": [
+ "0.1",
+ "0.01",
+ "0.001",
+ "1.0e-5",
+ "0.0004",
+ "2.0e-6",
+ "30000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 on negative use",
+ "subject": "Example/P0208/3",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_number",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "-30000",
+ "-2.0e-6",
+ "-0.2",
+ "-0.1"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 #ask on negative number output",
+ "subject": "Example/P0208/3/1",
+ "assert-output": {
+ "to-contain": [
+ "-30,000",
+ "-2.0e-6",
+ "-0.2",
+ "-0.1"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgMaxNonExpNumber": 1.0e+15,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0209.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0209.json
new file mode 100644
index 00000000..94c90d46
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0209.json
@@ -0,0 +1,247 @@
+{
+ "description": "Test `#set` for various `_qty` values without explicit precision (3 digit implicit), with/without leading zero, and different printouts (#753, en, `smwgMaxNonExpNumber`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::0.38610 sq mi, sqmi]] [[Corresponds to::1000 m²]] [[Corresponds to::247.1054 acre]] [[Corresponds to::988.42155 rood]]"
+ },
+ {
+ "page": "Example/P0209/1",
+ "contents": "{{#set: Has area=25 sq mi;30 sqmi; 10 m ²|+sep=; }}"
+ },
+ {
+ "page": "Example/P0209/2",
+ "contents": "{{#set:Has area=.25 sq mi;.30 sqmi; .10 m ²|+sep=; }}"
+ },
+ {
+ "page": "Example/P0209/1/1",
+ "contents": "{{#ask: [[Example/P0209/1]] |?Has area|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/1/2",
+ "contents": "{{#ask: [[Example/P0209/1]] |?Has area#|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/1/3",
+ "contents": "{{#ask: [[Example/P0209/1]] |?Has area#-n|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/1/4",
+ "contents": "{{#ask: [[Example/P0209/1]] |?Has area#-u|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/1/5",
+ "contents": "{{#ask: [[Example/P0209/1]] |?Has area#m²|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/2/1",
+ "contents": "{{#ask: [[Example/P0209/2]] |?Has area|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/2/2",
+ "contents": "{{#ask: [[Example/P0209/2]] |?Has area#|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/2/3",
+ "contents": "{{#ask: [[Example/P0209/2]] |?Has area#-n|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/2/4",
+ "contents": "{{#ask: [[Example/P0209/2]] |?Has area#-u|+order=desc |format=table }}"
+ },
+ {
+ "page": "Example/P0209/2/5",
+ "contents": "{{#ask: [[Example/P0209/2]] |?Has area#m²|+order=desc |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0209/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_area",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "64.750064750065",
+ "77.700077700078",
+ "0.01"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0209/2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_area",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "0.64750064750065",
+ "0.77700077700078",
+ "0.0001"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 #ask quantity standard output",
+ "subject": "Example/P0209/1/1",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"77.700077700078\"",
+ "<span class=\"smwtext\">64.75&#160;km²</span>",
+ "25&#160;sqmi",
+ "64,750.065&#160;m²",
+ "16,000.091&#160;acre",
+ "64,000.359&#160;rood",
+ "<span class=\"smwtext\">77.7&#160;km²</span>",
+ "77,700.078&#160;m²",
+ "19,200.109&#160;acre",
+ "76,800.431&#160;rood",
+ "<span class=\"smwtext\">0.01&#160;km²</span>",
+ "0.00386&#160;sqmi",
+ "10&#160;m²",
+ "2.471&#160;acre",
+ "9.884&#160;rood"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #ask quantity plain output",
+ "subject": "Example/P0209/1/2",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"77.700077700078\"",
+ "64.750064750065 km²",
+ "77.700077700078 km²",
+ "0.01 km²"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #ask quantity no unit output",
+ "subject": "Example/P0209/1/3",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"77.700077700078\"",
+ "77.700077700078<br />",
+ "<br />64.750064750065<br />",
+ "<br />0.01"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #ask quantity only unit output",
+ "subject": "Example/P0209/1/4",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"77.700077700078\"",
+ "<br />km²<br />"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 #ask quantity output specific unit",
+ "subject": "Example/P0209/1/5",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"77.700077700078\"",
+ "<span class=\"smwtext\">64,750.065&#160;m²</span>",
+ "<span class=\"smwtext\">77,700.078&#160;m²</span>",
+ "<span class=\"smwtext\">10&#160;m²</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 #ask quantity standard output",
+ "subject": "Example/P0209/2/1",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"0.77700077700078\"",
+ "<span class=\"smwtext\">0.648&#160;km²</span>",
+ "<span class=\"smwtext\">0.777&#160;km²</span>",
+ "<span class=\"smwtext\">1.0e-4&#160;km²</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 #ask quantity plain unit output",
+ "subject": "Example/P0209/2/2",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"0.77700077700078\"",
+ "0.77700077700078 km²<br />",
+ "<br />0.64750064750065 km²<br />",
+ "<br />0.0001 km²"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 #ask quantity number only output",
+ "subject": "Example/P0209/2/3",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"0.77700077700078\"",
+ "0.77700077700078<br />",
+ "<br />0.64750064750065<br />",
+ "<br />0.0001"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 #ask quantity output specific unit",
+ "subject": "Example/P0209/2/5",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"0.77700077700078\"",
+ "<span class=\"smwtext\">647.501&#160;m²</span>",
+ "<span class=\"smwtext\">777.001&#160;m²</span>",
+ "<span class=\"smwtext\">0.1&#160;m²</span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgMaxNonExpNumber": 1.0e+15,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0210.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0210.json
new file mode 100644
index 00000000..4cc0fcaf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0210.json
@@ -0,0 +1,90 @@
+{
+ "description": "Test `#set_recurring_event` (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0210/1",
+ "contents": "{{#set_recurring_event:property=Has date |start=01 Feb 1970 |Has title=Recurring event |unit=year |period=12 |limit=3 }}"
+ },
+ {
+ "page": "Example/P0210/Q1",
+ "contents": "{{#ask: [[Has title::Recurring event]] |?Has date |format=table |link=none }}"
+ },
+ {
+ "page": "Example/P0210/2",
+ "contents": "{{#set_recurring_event:property=Has date |start=01 Feb 1970 |Has title=Recurring event 2 |unit=month |period=1 |limit=2 }} {{#set_recurring_event:property=Has date |start=01 Feb 1970 |Has title=Recurring event 2, Test|+sep=, |unit=month |period=1 |limit=2 }}"
+ },
+ {
+ "page": "Example/P0210/2",
+ "contents": "{{#set_recurring_event:property=Has date |start=01 Feb 1970 |Has title=Recurring event 2, Test|+sep=, |unit=month |period=1 |limit=2 }}"
+ },
+ {
+ "page": "Example/P0210/Q.2",
+ "contents": "{{#ask: [[Has title::Recurring event 2]] |?Has date |format=table |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0210/Q1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0210/1#_6c0c58959b4717205b4c1b2c3b0e3e84</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2440618.5\">1 February 1970</td>",
+ "<td class=\"smwtype_wpg\">Example/P0210/1#_999d559e938eeaee07f5f52de53638e9</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2445001.5\">1 February 1982</td>",
+ "<td class=\"smwtype_wpg\">Example/P0210/1#_f42111f2b6af198c265ef2d8330b6a13</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2449384.5\">1 February 1994</td>",
+ "<td class=\"smwtype_wpg\">Example/P0210/1#_bdfba16fe62b82d1eadcb21054d8452b</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2453767.5\">1 February 2006</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (set and replace)",
+ "subject": "Example/P0210/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0210/2#_6af7f30c51558500a93d03f795738a61</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2440618.5\">1 February 1970</td>",
+ "<td class=\"smwtype_wpg\">Example/P0210/2#_6c8f1e0c1ab31ea31cfbc3d4fa44a173</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2440677.5\">1 April 1970</td>",
+ "<td class=\"smwtype_wpg\">Example/P0210/2#_dc90c230beae622cd7e0d87ea1a93b78</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2440646.5\">1 March 1970</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0210/2#_6af7f30c51558500a93d03f795738a61",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has title",
+ "Has date",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "1970-02-01",
+ "Recurring event 2 ",
+ "Test"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json
new file mode 100644
index 00000000..2629e5fa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0211.json
@@ -0,0 +1,97 @@
+{
+ "description": "Test `#set`/`#subobject` to import annotation via `@json` syntax (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0211/1",
+ "contents": {
+ "import-from": "/../Fixtures/p-0211.1.txt"
+ }
+ },
+ {
+ "page": "Example/P0211/2",
+ "contents": {
+ "import-from": "/../Fixtures/p-0211.2.txt"
+ }
+ },
+ {
+ "page": "Example/P0211/3",
+ "contents": {
+ "import-from": "/../Fixtures/p-0211.3.txt"
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #subobject",
+ "subject": "Example/P0211/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SOBJ",
+ "_SKEY",
+ "_MDAT"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 #set",
+ "subject": "Example/P0211/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 10,
+ "propertyKeys": [
+ "Kind",
+ "_cod",
+ "IcdId",
+ "Label",
+ "Link",
+ "Superclasses",
+ "Subclasses",
+ "Id",
+ "_SKEY",
+ "_MDAT"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #set depth issue",
+ "subject": "Example/P0211/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_SKEY",
+ "_MDAT"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0212.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0212.json
new file mode 100644
index 00000000..6fb37578
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0212.json
@@ -0,0 +1,102 @@
+{
+ "description": "Test `@@@` in-text annotation syntax (#1855, #1875 `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]] {{#set: Has property description=Some text with a link to [http://example.org/ foo] and <li>stripped `li` in title element</li>@en}}"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "P106",
+ "contents": "[[Has type::Text]] [[Has preferred property label::occupation@en]] [[Has preferred property label::ì§ì—…@ko]] [[Has preferred property label::è·æ¥­@ja]] [[Has property description::人物ã®è·æ¥­ã€‚「専門分野ã€@ja]] [[Has property description::ëŒ€ìƒ ì¸ë¬¼ì˜ ì§ì—…@ko]]"
+ },
+ {
+ "page": "Example/P0212/1",
+ "contents": "[[Has date::@@@]]"
+ },
+ {
+ "page": "Example/P0212/2",
+ "contents": "[[Has date::@@@|With extra caption]]"
+ },
+ {
+ "page": "Example/P0212/3",
+ "contents": "[[P106::@@@ja]]"
+ },
+ {
+ "page": "Example/P0212/4",
+ "contents": "[[P106::@@@ko|WithCaption]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0212/1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"1\" data-state=\"inline\" data-title=\"Property\" title=\"Some text with a link to foo and stripped `li` in title element&#10;\">",
+ "<span class=\"smwttcontent\">Some text with a link to <a rel=\"nofollow\" class=\"external text\" href=\"http://example.org/\">foo</a> and <li>stripped `li` in title element</li></span>",
+ "title=\"Property:Has date\">Has date</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0212/2",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"1\" data-state=\"inline\" data-title=\"Property\" title=\"Some text with a link to foo and stripped `li` in title element&#10;\">",
+ "<span class=\"smwttcontent\">Some text with a link to <a rel=\"nofollow\" class=\"external text\" href=\"http://example.org/\">foo</a> and <li>stripped `li` in title element</li></span>",
+ "title=\"Property:Has date\">With extra caption</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31-)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0212/3",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:P106\">è·æ¥­</a></span><span class=\"smwttcontent\">人物ã®è·æ¥­ã€‚「専門分野ã€</span></span>&#160;<span title=\"P106\"><sup>áµ–</sup>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31+)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0212/3",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:P106\">è·æ¥­</a></span><span class=\"smwttcontent\">人物ã®è·æ¥­ã€‚「専門分野ã€</span></span>&#160;<span title=\"P106\"><sup>áµ–</sup>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0212/4",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:P106\">WithCaption</a></span><span class=\"smwttcontent\">ëŒ€ìƒ ì¸ë¬¼ì˜ ì§ì—…</span></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0301.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0301.json
new file mode 100644
index 00000000..e23bfc11
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0301.json
@@ -0,0 +1,62 @@
+{
+ "description": "Test #subobject category annotation (#1172)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/0301/1",
+ "contents": "{{#subobject:|Has page={{PAGENAME}}|@category=ABC;123|+sep=;}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 #subobject with category annotation",
+ "subject": "Example/0301/1#_04284bb237a62f6648c369d713da3996",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_page",
+ "_SKEY",
+ "_INST"
+ ],
+ "propertyValues": [
+ "Example/0301/1",
+ "ABC",
+ "123"
+ ]
+ }
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0 query with subobject category",
+ "condition": "[[Category:ABC]][[Has page::Example/0301/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Example/0301/1#0##_04284bb237a62f6648c369d713da3996"
+ ],
+ "count": "1"
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0302.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0302.json
new file mode 100644
index 00000000..9573fe14
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0302.json
@@ -0,0 +1,106 @@
+{
+ "description": "Test #subobject parser to use invalid assignments and create `_ERRC` (#1299, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0302/1",
+ "contents": "{{#subobject:abc.name |@category=ABC;123|+sep=;}}"
+ },
+ {
+ "page": "Example/P0302/2",
+ "contents": "{{#subobject:|Modification date= 1 Jan 1970 }}"
+ },
+ {
+ "page": "Example/P0302/3",
+ "contents": "{{#subobject:|Date= InvalidValue }}"
+ },
+ {
+ "page": "Example/P0302/4",
+ "contents": "{{#set_recurring_event:some.foo|property=Has date |start=June 8, 2010 |unit=day |period=1 |limit=10 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 dot scheme not permitted for user-defined named identifiers",
+ "subject": "Example/P0302/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 restricted property",
+ "subject": "Example/P0302/2#_6bcfd8f29c79e61086605597e7f2a8d4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "_ERRC",
+ "_SKEY"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 invalid value",
+ "subject": "Example/P0302/3#_d22883317b11ebaf00a39aaffc1d30fa",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "_ERRC",
+ "_SKEY"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 recurring events invalid name",
+ "subject": "Example/P0302/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRC",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": []
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0303.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0303.json
new file mode 100644
index 00000000..4dfbc0d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0303.json
@@ -0,0 +1,70 @@
+{
+ "description": "Test `#subobject` and `#set` parser on values with spaces (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has effect",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0303/1",
+ "contents": {
+ "import-from": "/../Fixtures/p-0303.txt"
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 values are trimmed (no leading spaces)",
+ "subject": "Example/P0303/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_effect",
+ "_SKEY",
+ "_MDAT",
+ "_SOBJ"
+ ],
+ "propertyValues": [
+ "Foo",
+ "Bar"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 values are trimmed (no leading spaces)",
+ "subject": "Example/P0303/1#_4cf77c0a97795d2717d53fa6165a0bf5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_effect",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "Foo",
+ "Bar"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0401.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0401.json
new file mode 100644
index 00000000..6193813d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0401.json
@@ -0,0 +1,153 @@
+{
+ "description": "Test annotations with disabled capital links (#673, `wgCapitalLinks=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "has number",
+ "contents": "[[has type::number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "wikidata id",
+ "contents": "[[has type::string]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "founded",
+ "contents": "[[has type::date]] [[wikidata id::P571]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "set-non-capital-property",
+ "contents": "{{#set:|has type=boolean}} [[Category:Foo]]"
+ },
+ {
+ "page": "Empty",
+ "contents": "no annotation"
+ },
+ {
+ "page": "Single-category",
+ "contents": "[[Category:Foo]]"
+ },
+ {
+ "page": "Default-sort-with-non-capital-property",
+ "contents": "{{DEFAULTSORTKEY:Bar}} [[has number::42]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Empty",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "Empty"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Single-category",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_INST",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "Single-category",
+ "Foo"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Default-sort-with-non-capital-property",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "has_number"
+ ],
+ "propertyValues": [
+ "Bar",
+ 42
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 check has type despite wgCapitalLinks setting",
+ "subject": "set-non-capital-property",
+ "namespace": "SMW_NS_PROPERTY",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_TYPE",
+ "_INST"
+ ],
+ "propertyValues": [
+ "Boolean",
+ "Foo"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 check has type despite wgCapitalLinks setting, user-defined property is kept lower case",
+ "subject": "founded",
+ "namespace": "SMW_NS_PROPERTY",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_TYPE",
+ "wikidata id"
+ ],
+ "propertyValues": [
+ "P571"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgCapitalLinks": false,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0402.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0402.json
new file mode 100644
index 00000000..dc98bfde
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0402.json
@@ -0,0 +1,239 @@
+{
+ "description": "Test in-text parsing for double colon annotation such as `::::` or `:::` (#1066, #1075, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Double-single-colon",
+ "contents": "[[Has page:::Page/::Foo:Bar]] [[Has text:::Text/::Foo:Bar]] [[Has url::http://example.org/::Foo:Bar]]"
+ },
+ {
+ "page": "Double-double-colon",
+ "contents": "//::Page/::Foo:Bar is not allowed as title; [[Has text::::Text/::Foo:Bar]] [[Has url::http://example.org/:::Foo:Bar]]"
+ },
+ {
+ "page": "Example/P0402/3",
+ "contents": "[[Has date:::1 Jan 1970]]"
+ },
+ {
+ "page": "Number-error",
+ "contents": "[[Has number:::123]]"
+ },
+ {
+ "page": "Page-no-error",
+ "contents": "[[Has page:::123|abc]] [[Has page:::xyz]]"
+ },
+ {
+ "page": "Page-error",
+ "contents": "[[Has page::::123|abc]] [[Has page::::xyz]]"
+ },
+ {
+ "page": "Example/P0402/7",
+ "contents": "[[Has date::-901 AD]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Double-single-colon",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_page",
+ "Has_text",
+ "Has_url"
+ ],
+ "propertyValues": [
+ "Page/::Foo:Bar",
+ ":Text/::Foo:Bar",
+ "http://example.org/::Foo:Bar"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Page/::Foo:Bar",
+ "Text/::Foo:Bar",
+ "http://example.org/::Foo:Bar"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (: as leading text element to be converted to an indent)",
+ "subject": "Double-double-colon",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text",
+ "Has_url"
+ ],
+ "propertyValues": [
+ "::Text/::Foo:Bar",
+ "http://example.org/:::Foo:Bar"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<dd>Text/::Foo:Bar",
+ "http://example.org/:::Foo:Bar"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Page-no-error",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_page"
+ ],
+ "propertyValues": [
+ "123",
+ "Xyz"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "abc",
+ "xyz"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Page-error",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "abc",
+ "&#58;&#58;xyz"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0402/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_date"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "1 Jan 1970"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Number-error",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "&#58;123"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 negative year with AD/CE era is not allowed",
+ "subject": "Example/P0402/7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "&quot;-901 AD&quot; contains an extrinsic dash or other characters that are invalid for a date interpretation."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0403.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0403.json
new file mode 100644
index 00000000..fd9af2cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0403.json
@@ -0,0 +1,118 @@
+{
+ "description": "Test in-text annotations being disabled for when Factbox contains extra `[[ ... ]]` (#1126)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "SetValue",
+ "contents": "<includeonly>{{{value}}}</includeonly>"
+ },
+ {
+ "page": "Example/P0403/1",
+ "contents": "[[Has url::http://example.org/api.php?action=ask&query=%5B%5BModification%20date::%2B%5D%5D%7C%3FModification%20date%7Csort%3DModification%20date%7Corder%3Ddesc|api.php?action=ask&query=]] __SHOWFACTBOX__"
+ },
+ {
+ "page": "Example/P0403/2",
+ "contents": "{{#set:|Has text=ABC [[Has page::DEF]] 123|template=SetValue}}"
+ },
+ {
+ "page": "Example/P0403/3",
+ "contents": "{{#ask: [[Example/P0403/2]] |?Has text}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 don't expected to see a Modification date annotation due to %5B%5BModification%20date::%2B%5D%5D => [[Modification::+]]",
+ "subject": "Example/P0403/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_url"
+ ],
+ "propertyValues": [
+ "Example/P0403/1",
+ "http://example.org/api.php?action=ask&query=%5B%5BModification_date::%2B%5D%5D%7C%3FModification_date%7Csort%3DModification_date%7Corder%3Ddesc"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "http://example.org/api.php?action=ask&amp;query=%5B%5BModification_date::%2B%5D%5D%7C%3FModification_date%7Csort%3DModification_date%7Corder%3Ddesc\">api.php?action=ask&amp;query=</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1, refs #1314",
+ "subject": "Example/P0403/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text",
+ "Has_page"
+ ],
+ "propertyValues": [
+ "Example/P0403/2",
+ "ABC [[Has page::DEF]] 123",
+ "DEF"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 does not contain any [[ :: ]] copied annotation values, refs #1314",
+ "subject": "Example/P0403/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ASK"
+ ],
+ "propertyValues": []
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "ABC DEF 123"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0404.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0404.json
new file mode 100644
index 00000000..e11b31e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0404.json
@@ -0,0 +1,78 @@
+{
+ "description": "Test in-text annonation on different category colon identifier",
+ "setup": [
+ {
+ "page": "Example/P0404/1",
+ "contents": "[[Category:SingleColonNotion]], [[Category::DoubleColonNotion]]"
+ },
+ {
+ "page": "Example/P0404/2",
+ "contents": "[[Category:P4040]]"
+ },
+ {
+ "page": "Example/P0404/Q.1",
+ "contents": "{{#ask: [[Category:P4040]] |?Category }}"
+ },
+ {
+ "page": "Example/P0404/Q.2",
+ "contents": "{{#ask: [[Category:P4040]] |?Categories }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 : vs. ::",
+ "subject": "Example/P0404/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Category",
+ "_MDAT",
+ "_SKEY",
+ "_INST"
+ ],
+ "propertyValues": [
+ "SingleColonNotion",
+ "DoubleColonNotion"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (printrequest singular use)",
+ "subject": "Example/P0404/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"Category\"><a href=.*>Category</a></th>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0404/2\" title=\"Example/P0404/2\">Example/P0404/2</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (printrequest plural use)",
+ "subject": "Example/P0404/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"Category\"><a href=.*>Category</a></th>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0404/2\" title=\"Example/P0404/2\">Example/P0404/2</a></td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0405.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0405.json
new file mode 100644
index 00000000..c6935414
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0405.json
@@ -0,0 +1,212 @@
+{
+ "description": "Test in-text annotation via template and manual redirect (#895)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "UseTemplateForAnnotation",
+ "contents": "<includeonly>[[Has page::{{PAGENAME}}]], [[Has page::Abc]], {{#subobject:|Has page=123}}</includeonly>"
+ },
+ {
+ "page": "Page-with-template-for-unknown-redirect-target",
+ "contents": "{{UseTemplateForAnnotation}}"
+ },
+ {
+ "page": "Page-with-template-for-unknown-redirect-target",
+ "contents": "#REDIRECT [[To-be-unknown-template-redirect-target]]"
+ },
+ {
+ "page": "Page-with-template-for-known-redirect-target",
+ "contents": "{{UseTemplateForAnnotation}}"
+ },
+ {
+ "page": "To-be-known-template-redirect-target",
+ "contents": "{{UseTemplateForAnnotation}}"
+ },
+ {
+ "page": "Page-with-template-for-known-redirect-target",
+ "contents": "#REDIRECT [[To-be-known-template-redirect-target]]"
+ },
+ {
+ "page": "Page-with-annotation-for-unknown-redirect-target",
+ "contents": "[[Has page::{{PAGENAME}}]]"
+ },
+ {
+ "page": "Page-with-annotation-for-unknown-redirect-target",
+ "contents": "#REDIRECT [[To-be-unknown-redirect-target]]"
+ },
+ {
+ "page": "Page-with-template-was-redirected-now-restored",
+ "contents": "{{UseTemplateForAnnotation}}"
+ },
+ {
+ "page": "Page-with-template-was-redirected-now-restored",
+ "contents": "#REDIRECT [[To-be-unknown-temporary-redirect-target]]"
+ },
+ {
+ "page": "Page-with-template-was-redirected-now-restored",
+ "contents": "{{UseTemplateForAnnotation}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 verify redirect for unknown target",
+ "subject": "Page-with-template-for-unknown-redirect-target",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 1,
+ "propertyKeys": [
+ "_SKEY"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ],
+ "propertyValues": [
+ "Page-with-template-for-unknown-redirect-target#0##"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 verify unknown target",
+ "subject": "To-be-unknown-template-redirect-target",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 1,
+ "propertyKeys": [
+ "_SKEY"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ],
+ "propertyValues": [
+ "Page-with-template-for-unknown-redirect-target#0##"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 verify redirect for knwown target",
+ "subject": "Page-with-template-for-known-redirect-target",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_SOBJ",
+ "Has_page"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI",
+ "Has_page"
+ ],
+ "propertyValues": [
+ "Page-with-template-for-known-redirect-target#0##",
+ "To-be-known-template-redirect-target#0##"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 verify knwown target",
+ "subject": "To-be-known-template-redirect-target",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_SOBJ",
+ "Has_page"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI",
+ "Has_page"
+ ],
+ "propertyValues": [
+ "Page-with-template-for-known-redirect-target#0##",
+ "To-be-known-template-redirect-target#0##"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 verify redirect for unknown target",
+ "subject": "Page-with-annotation-for-unknown-redirect-target",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 1,
+ "propertyKeys": [
+ "_SKEY"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ],
+ "propertyValues": [
+ "Page-with-annotation-for-unknown-redirect-target#0##"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 verify that a redirected page can be restored to host annotations",
+ "subject": "Page-with-template-was-redirected-now-restored",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_page",
+ "_SOBJ"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "Has_page"
+ ],
+ "propertyValues": [
+ "Page-with-template-was-redirected-now-restored#0##"
+ ]
+ }
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0406.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0406.json
new file mode 100644
index 00000000..218ca898
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0406.json
@@ -0,0 +1,60 @@
+{
+ "description": "Test in-text annotation for unrestricted template parse using `import-annotation=true` (#1055)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasUnrestrictedPage",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "CreateAnnotationViaAskTemplate",
+ "contents": "<includeonly>[[HasUnrestrictedPage::{{{1}}}]]</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "TemplateForUnrestrictedParse",
+ "contents": "<includeonly>{{#ask: [[HasUnrestrictedPage::{{{1}}}]]|?HasUnrestrictedPage|link=none|format=template|template=CreateAnnotationViaAskTemplate|import-annotation=true}}</includeonly>"
+ },
+ {
+ "page": "Page-with-annotation",
+ "contents": "[[HasUnrestrictedPage::SearchPageWithAnnotation]]"
+ },
+ {
+ "page": "Page-with-template",
+ "contents": "{{TemplateForUnrestrictedParse|SearchPageWithAnnotation}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Page-with-template",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "HasUnrestrictedPage",
+ "_MDAT",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "Page-with-annotation"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0407.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0407.json
new file mode 100644
index 00000000..91e64556
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0407.json
@@ -0,0 +1,61 @@
+{
+ "description": "Test in-text annotation for a redirect that is pointing to a deleted target (#1105)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasPropertyForUse",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/0407/1",
+ "contents": "#REDIRECT [[Example/0407/2]]"
+ },
+ {
+ "do-delete": true,
+ "page": "Example/0407/2",
+ "contents": "[[HasPropertyForUse::ABC]]"
+ },
+ {
+ "page": "Example/0407/3",
+ "contents": "[[HasPropertyForUse::Example/0407/1]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 check annotation for a deleted redirected target subject",
+ "subject": "Example/0407/3",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "HasPropertyForUse"
+ ],
+ "propertyValues": [
+ "Example/0407/2"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0408.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0408.json
new file mode 100644
index 00000000..a39f1e03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0408.json
@@ -0,0 +1,45 @@
+{
+ "description": "Test in-text annotation for multiple property assignment using non-strict parser mode (#1252, en)",
+ "setup": [
+ {
+ "page": "Example/P0408/1",
+ "contents": "[[Testproperty1::Testproperty2::200]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 disabled strict mode allows for multi property assignment",
+ "subject": "Example/P0408/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Testproperty1",
+ "Testproperty2",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "200"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgParserFeatures": [
+ "SMW_PARSER_NONE"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0409.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0409.json
new file mode 100644
index 00000000..86ceea5f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0409.json
@@ -0,0 +1,217 @@
+{
+ "description": "Test in-text annotation for `_rec`/`_mlt_rec` (+ subobject) for when record type points to another record type (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has monolingual text",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record one",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record two",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has record one]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record one mlt",
+ "contents": "[[Has type::Record]] [[Has fields::Has monolingual text;Has number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record two mlt",
+ "contents": "[[Has type::Record]] [[Has fields::Has monolingual text;Has record one mlt]]"
+ },
+ {
+ "page": "Example/P0409/1/1",
+ "contents": "[[Has record two::Foo;abc\\;12]]"
+ },
+ {
+ "page": "Example/P0409/1/2",
+ "contents": "{{#subobject: |Has record two=Foo;abc\\;12 }}"
+ },
+ {
+ "page": "Example/P0409/2/1",
+ "contents": "[[Has record one mlt::test@en;42]]"
+ },
+ {
+ "page": "Example/P0409/2/2",
+ "contents": "{{#subobject: |Has record one mlt=test@en;42}}"
+ },
+ {
+ "page": "Example/P0409/3/1",
+ "contents": "[[Has record two mlt::one@en;two@fr\\;123]]"
+ },
+ {
+ "page": "Example/P0409/3/2",
+ "contents": "{{#subobject: |Has record two mlt=one@en;two@fr\\;123}}"
+ },
+ {
+ "page": "Example/P0409/3/2a",
+ "contents": "{{#ask: [[Has record two mlt::one@en;two@fr\\;123]] |?Has record two mlt}}"
+ },
+ {
+ "page": "Example/P0409/3/2b",
+ "contents": "{{#ask: [[Has record two mlt::one@en;two@fr\\;123]] |?Has record two mlt|+index=1 |?Has record two mlt|+index=2}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0409/1/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_record_two",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "Foo; abc\\; 12"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0409/1/2#_863c94681079337ad25674e3a7ce33e7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_record_two",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "Foo; abc\\; 12"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0409/2/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_record_one_mlt",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "test@en; 42"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0409/2/2#_aa886e35ba32dd0c6e43a026230362f4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_record_one_mlt",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "test@en; 42"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0409/3/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_record_two_mlt",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "one@en; two@fr\\; 123"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Example/P0409/3/2#_4d3b4405d3a60255e63cde196092a367",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_record_two_mlt",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "one@en; two@fr\\; 123"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (edge case that will probably never surface but we verify that the description can be decoded without failing the parser)",
+ "subject": "Example/P0409/3/2a",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0409/3/2#_4d3b4405d3a60255e63cde196092a367",
+ "<td class=\"Has-record-two-mlt smwtype_rec\">one (en) (two (fr) (123))</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 same as #6 (query condition is identical, printoutput is different)",
+ "subject": "Example/P0409/3/2b",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0409/3/2#_4d3b4405d3a60255e63cde196092a367",
+ "<td class=\"Has-record-two-mlt smwtype_mlt_rec\">one (en)</td>",
+ "<td class=\"Has-record-two-mlt smwtype_rec\">two (fr) (123)</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0410.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0410.json
new file mode 100644
index 00000000..b060badc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0410.json
@@ -0,0 +1,458 @@
+{
+ "description": "Test in-text annotation on `_num`/`_tem`/`_qty` type with denoted precision (`_PREC`) and/or `-p<num>` printout precision marker (#1335, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature with prec4",
+ "contents": "[[Has type::Temperature]] [[Display precision of::4]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature with prec2",
+ "contents": "[[Has type::Temperature]] [[Display precision of::2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature",
+ "contents": "[[Has type::Temperature]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number with prec4",
+ "contents": "[[Has type::Number]] [[Display precision of::4]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number with prec2",
+ "contents": "[[Has type::Number]] [[Display precision of::2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::0.38610 sq mi]] [[Corresponds to::1000 m²]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area with prec4",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::0.38610 sq mi]] [[Corresponds to::1000 m²]] [[Display precision of::4]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area with prec2",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::0.38610 sq mi]] [[Corresponds to::1000 m²]] [[Display precision of::2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area with prec0",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::0.38610 sq mi]] [[Corresponds to::1000 m²]] [[Display precision of::0]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has currency with prec2",
+ "contents": "[[Has type::Quantity]] [[Display units::€, ¥]] [[Corresponds to:: € 1.00]] [[Corresponds to::¥,JPY,Japanese Yen 114.2121]] [[Display precision of::2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has currency with prec4",
+ "contents": "[[Has type::Quantity]] [[Display units::€, ¥]] [[Corresponds to:: € 1.00]] [[Corresponds to::¥,JPY,Japanese Yen 114.2121]] [[Display precision of::4]]"
+ },
+ {
+ "page": "Example/P0410/1",
+ "contents": "[[Has temperature with prec4::32 °F]] [[Has temperature with prec4::100 °C]]"
+ },
+ {
+ "page": "Example/P0410/2",
+ "contents": "[[Has temperature with prec2::32 °F]] [[Has temperature with prec2::100 °C]]"
+ },
+ {
+ "page": "Example/P0410/1/1",
+ "contents": "{{#ask: [[Has temperature with prec4::32 °F]] |?Has temperature with prec4 |+order=asc |format=table }}"
+ },
+ {
+ "page": "Example/P0410/2/1",
+ "contents": "{{#ask: [[Has temperature with prec2::32 °F]] |?Has temperature with prec2 |+order=asc |format=table }}"
+ },
+ {
+ "page": "Example/P0410/3/1",
+ "contents": "[[Has number with prec4::.001]] [[Has number with prec2::.001]] [[Has number::.003]]"
+ },
+ {
+ "page": "Example/P0410/3/2",
+ "contents": "[[Has number with prec4::.005]] [[Has number with prec2::.005]] [[Has number::.008]]"
+ },
+ {
+ "page": "Example/P0410/3/3",
+ "contents": "[[Has number with prec2::42]] [[Has number::42]]"
+ },
+ {
+ "page": "Example/P0410/3/4",
+ "contents": "[[Has number with prec2::10,000]] [[Has number::10,000]]"
+ },
+ {
+ "page": "Example/P0410/3",
+ "contents": "{{#ask: [[~Example/P0410/3/*]] |?Has number with prec4 |?Has number with prec2 |?Has number |format=table }}"
+ },
+ {
+ "page": "Example/P0410/4/1",
+ "contents": "[[Has area with prec2::10,000 m²]] [[Has area with prec4::0.02 km²]] [[Has area with prec0::3.33 km²]]"
+ },
+ {
+ "page": "Example/P0410/4",
+ "contents": "{{#ask: [[~Example/P0410/4/*]] |?Has area with prec4 |?Has area with prec2 |?Has area with prec0 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/5/1",
+ "contents": "[[Has currency with prec2::€ 5]] [[Has currency with prec4::¥ 100]]"
+ },
+ {
+ "page": "Example/P0410/5/2",
+ "contents": "[[Has currency with prec2::€ 5,000]] [[Has currency with prec4::¥ 0.50]]"
+ },
+ {
+ "page": "Example/P0410/5",
+ "contents": "{{#ask: [[~Example/P0410/5/*]] |?Has currency with prec4 |?Has currency with prec2 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/6/1",
+ "contents": "[[Has number::10,000]] [[Has number::.005]] [[Has number::42.005]] [[Has number::1001]]"
+ },
+ {
+ "page": "Example/P0410/6a",
+ "contents": "{{#ask: [[~Example/P0410/6/*]] |?Has number#-p2 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/6b",
+ "contents": "{{#ask: [[~Example/P0410/6/*]] |?Has number#-p4 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/6c",
+ "contents": "{{#ask: [[~Example/P0410/6/*]] |?Has number#-p3-n |format=table }}"
+ },
+ {
+ "page": "Example/P0410/7/1",
+ "contents": "[[Has area::10,000 m²]] [[Has area::0.02 km²]] [[Has area::3.33 km²]]"
+ },
+ {
+ "page": "Example/P0410/7a",
+ "contents": "{{#ask: [[~Example/P0410/7/*]] |?Has area#-p2 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/7b",
+ "contents": "{{#ask: [[~Example/P0410/7/*]] |?Has area#m²-p3 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/7c",
+ "contents": "{{#ask: [[~Example/P0410/7/*]] |?Has area#-n-p2 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/8/1",
+ "contents": "[[Has temperature::32 °F]] [[Has temperature::100 °C]]"
+ },
+ {
+ "page": "Example/P0410/8a",
+ "contents": "{{#ask: [[~Example/P0410/8/*]] |?Has temperature#-p2 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/8b",
+ "contents": "{{#ask: [[~Example/P0410/8/*]] |?Has temperature#°C-p3 |format=table }}"
+ },
+ {
+ "page": "Example/P0410/8c",
+ "contents": "{{#ask: [[~Example/P0410/8/*]] |?Has temperature#-n-p2 |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0410/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_temperature_with_prec4",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ 273.15,
+ 373.15
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0410/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_temperature_with_prec2",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ 273.15,
+ 373.15
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0410/1/1",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"273.15\"",
+ "<span class=\"smwtext\">273.1500&#160;K</span>",
+ "0.0000&#160;°C",
+ "32.0000&#160;°F",
+ "491.6700&#160;°R",
+ "<span class=\"smwtext\">373.1500&#160;K</span>",
+ "100.0000&#160;°C",
+ "212.0000&#160;°F",
+ "671.6700&#160;°R"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0410/2/1",
+ "assert-output": {
+ "to-contain": [
+ "data-sort-value=\"273.15\"",
+ "<span class=\"smwtext\">273.15&#160;K</span>",
+ "0.00&#160;°C",
+ "32.00&#160;°F",
+ "491.67&#160;°R",
+ "<span class=\"smwtext\">373.15&#160;K</span>",
+ "100.00&#160;°C",
+ "212.00&#160;°F",
+ "671.67&#160;°R"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 Prec2 vs Prec4 incl. rounding on output for plain number",
+ "subject": "Example/P0410/3",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number-with-prec4 smwtype_num\" data-sort-value=\"0.001\">0.0010",
+ "class=\"Has-number-with-prec2 smwtype_num\" data-sort-value=\"0.001\">0.00",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"0.003\">0.003",
+ "class=\"Has-number-with-prec4 smwtype_num\" data-sort-value=\"0.005\">0.0050",
+ "class=\"Has-number-with-prec2 smwtype_num\" data-sort-value=\"0.005\">0.01",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"0.008\">0.008",
+ "class=\"Has-number-with-prec2 smwtype_num\" data-sort-value=\"42\">42.00",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"42\">42",
+ "class=\"Has-number-with-prec2 smwtype_num\" data-sort-value=\"10000\">10,000.00",
+ "class=\"Has-number smwtype_num\" data-sort-value=\"10000\">10,000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 Prec2 vs Prec4 vs Prc0 quantity output",
+ "subject": "Example/P0410/4",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-area-with-prec4 smwtype_qty\" data-sort-value=\"0.02\"",
+ "0.0200&#160;km²",
+ "0.0077&#160;sqmi",
+ "20.0000&#160;m²",
+ "class=\"Has-area-with-prec2 smwtype_qty\" data-sort-value=\"10\"",
+ "10.00&#160;km²",
+ "3.86&#160;sqmi",
+ "10,000.00&#160;m²",
+ "class=\"Has-area-with-prec0 smwtype_qty\" data-sort-value=\"3.33\"",
+ "3&#160;km²",
+ "1&#160;sqmi",
+ "3,330&#160;m²"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 on currency output",
+ "subject": "Example/P0410/5",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-currency-with-prec4 smwtype_qty\" data-sort-value=\"0.87556397264388\"",
+ "€&#160;0.8756",
+ "Â¥&#160;100.0000",
+ "class=\"Has-currency-with-prec2 smwtype_qty\" data-sort-value=\"5\"",
+ "€&#160;5.00",
+ "Â¥&#160;571.06",
+ "class=\"Has-currency-with-prec4 smwtype_qty\" data-sort-value=\"0.0043778198632194\"",
+ "€&#160;0.0044",
+ "Â¥&#160;0.5000",
+ "class=\"Has-currency-with-prec2 smwtype_qty\" data-sort-value=\"5000\"",
+ "€&#160;5,000.00",
+ "Â¥&#160;571,060.50"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 on number output without property defined precision, uses -p4 printout marker",
+ "subject": "Example/P0410/6a",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number smwtype_num\" data-sort-value=\"10000\"",
+ "10,000.00",
+ "0.01",
+ "42.01",
+ "1,001.00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 on number output without property defined precision, uses -p4 printout marker",
+ "subject": "Example/P0410/6b",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number smwtype_num\" data-sort-value=\"10000\"",
+ "10,000.0000",
+ "0.0050",
+ "42.0050",
+ "1,001.0000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 on number output without property defined precision, uses -p3-n printout marker",
+ "subject": "Example/P0410/6c",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-number smwtype_num\" data-sort-value=\"10000\"",
+ "10000.000",
+ "0.005",
+ "42.005",
+ "1001.000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 on quantity output without property defined precision, uses -p2 printout marker",
+ "subject": "Example/P0410/7a",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-area smwtype_qty\" data-sort-value=\"10\"",
+ "10.00&#160;km²",
+ "3.86&#160;sqmi",
+ "10,000.00&#160;m²",
+ "0.02&#160;km²",
+ "0.01&#160;sqmi",
+ "20.00&#160;m²",
+ "3.33&#160;km²",
+ "1.29&#160;sqmi",
+ "3,330.00&#160;m²"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#11 on quantity output without property defined precision, uses m²-p3 printout marker",
+ "subject": "Example/P0410/7b",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-area smwtype_qty\" data-sort-value=\"10\"",
+ "10,000.000&#160;m²",
+ "10.000&#160;km²",
+ "3.861&#160;sqmi",
+ "20.000&#160;m²",
+ "0.020&#160;km²",
+ "0.008&#160;sqmi",
+ "3,330.000&#160;m²",
+ "3.330&#160;km²",
+ "1.286&#160;sqmi"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#12 on quantity output without property defined precision, uses -n-p2 printout marker",
+ "subject": "Example/P0410/7c",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-area smwtype_qty\" data-sort-value=\"10\"",
+ "10.00",
+ "0.02",
+ "3.33"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#13 on temperature output without property defined precision, uses -p2 printout marker",
+ "subject": "Example/P0410/8a",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-temperature smwtype_tem\" data-sort-value=\"273.15\"",
+ "class=\"smwtext\">273.15&#160;K",
+ "0.00&#160;°C",
+ "32.00&#160;°F",
+ "491.67&#160;°R",
+ "class=\"smwtext\">373.15&#160;K",
+ "100.00&#160;°C",
+ "212.00&#160;°F",
+ "671.67&#160;°R"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#14 on temperature output without property defined precision, uses °C-p3 printout marker",
+ "subject": "Example/P0410/8b",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-temperature smwtype_tem\" data-sort-value=\"273.15\"",
+ "class=\"smwtext\">0.000&#160;°C",
+ "273.150&#160;K",
+ "32.000&#160;°F",
+ "491.670&#160;°R",
+ "class=\"smwtext\">100.000&#160;°C",
+ "373.150&#160;K",
+ "212.000&#160;°F",
+ "671.670&#160;°R"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#15 on temperature output without property defined precision, uses -n-p2 printout marker",
+ "subject": "Example/P0410/8c",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-temperature smwtype_tem\" data-sort-value=\"273.15\"",
+ "273.15",
+ "373.15"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0411.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0411.json
new file mode 100644
index 00000000..26f07d33
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0411.json
@@ -0,0 +1,151 @@
+{
+ "description": "Test in-text annotation (and #subobject) using a monolingual property (#1344, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text with language",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "page": "Example/P0411/1",
+ "contents": "[[Has text with language::example one@en]]"
+ },
+ {
+ "page": "Example/P0411/1/1",
+ "contents": "{{#ask: [[Has text with language::example one@en]] |?Has text with language }}"
+ },
+ {
+ "page": "Example/P0411/1/2",
+ "contents": "{{#ask: [[-Has text with language::Example/P0411/1]] |?Language code |?Text }}"
+ },
+ {
+ "page": "Example/P0411/2",
+ "contents": "{{#subobject: |Has text with language=例ã®ä¸€@ja }} {{#subobject: |Has text with language=pour l'exemple@fr }}"
+ },
+ {
+ "page": "Example/P0411/2/1",
+ "contents": "{{#ask: [[Has text with language::?@ja]] |?Has text with language }}"
+ },
+ {
+ "page": "Example/P0411/2/2",
+ "contents": "{{#ask: [[Language code::ja]] |?Text }}"
+ },
+ {
+ "page": "Example/P0411/2/3",
+ "contents": "{{#ask: [[Language code::ja]] |?-Has text with language }}"
+ },
+ {
+ "page": "Example/P0411/3",
+ "contents": "{{#subobject: |Has text with language=国@zh-hans }} {{#subobject: |Has text with language=國@zh-hant }}"
+ },
+ {
+ "page": "Example/P0411/3/1",
+ "contents": "{{#ask: [[Has text with language::?@~zh*]] |?Has text with language=Text |+index=1 |?Has text with language=Code |+index=2 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0411/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_text_with_language",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": []
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "example one (en)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 match subject",
+ "subject": "Example/P0411/1/1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/1",
+ "class=\"Has-text-with-language smwtype_mlt_rec\">example one (en)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 inverse match",
+ "subject": "Example/P0411/1/2",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/1#_ML3c1364d804527cd1594b4817b01c29f0",
+ "class=\"Language-code smwtype&#95;_lcode\">en",
+ "class=\"Text smwtype_txt\">example one"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 match specific language",
+ "subject": "Example/P0411/2/1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/2#_b88331e53ca614aea6f7c8b5f0d6f876",
+ "class=\"Has-text-with-language smwtype_mlt_rec\">例ã®ä¸€ (ja)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 match specific language",
+ "subject": "Example/P0411/2/2",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/2#_ML13e9d4c2ba826927d7210acab7def9ec",
+ "class=\"Text smwtype_txt\">例ã®ä¸€"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 match specific language with inverse printout",
+ "subject": "Example/P0411/2/3",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/2#_ML13e9d4c2ba826927d7210acab7def9ec",
+ "title=\"Property:Has text with language\">-Has text with language",
+ "class=\"-Has-text-with-language smwtype_mlt_rec\">Example/P0411/2"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 using index display for columns",
+ "subject": "Example/P0411/3/1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0411/3#_a52273132da28d0c1fad5819e44958ab",
+ "class=\"Text smwtype_txt\">国",
+ "class=\"Code smwtype&#95;_lcode\">zh-Hans",
+ "Example/P0411/3#_4eac5791ab0478bb01dc9d181bc91ec8",
+ "class=\"Text smwtype_txt\">國",
+ "class=\"Code smwtype&#95;_lcode\">zh-Hant"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0412.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0412.json
new file mode 100644
index 00000000..8c6f716e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0412.json
@@ -0,0 +1,76 @@
+{
+ "description": "Test in-text annotation for `_boo` datatype (`wgContLang=ja`, `wgLang=ja`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has boolean",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "page": "Example/P0412/1",
+ "contents": "[[Has boolean::真]]"
+ },
+ {
+ "page": "Example/P0412/2",
+ "contents": "{{#set:Has boolean=å½}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 true",
+ "subject": "Example/P0412/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_boolean",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ true
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 false",
+ "subject": "Example/P0412/2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_boolean",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ false
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "ja",
+ "wgLang": "ja",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0413.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0413.json
new file mode 100644
index 00000000..5a4d28e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0413.json
@@ -0,0 +1,882 @@
+{
+ "description": "Test in-text annotation for different `_dat` input/output (en, skip virtuoso, `smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0413/1",
+ "contents": "[[Has date::Feb 11 2000 10:00:01]]"
+ },
+ {
+ "page": "Example/P0413/1a",
+ "contents": "{{#ask: [[Example/P0413/1]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/2",
+ "contents": "[[Has date::Feb 11 2000]]"
+ },
+ {
+ "page": "Example/P0413/2a",
+ "contents": "{{#ask: [[Example/P0413/2]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/3",
+ "contents": "[[Has date::2000]]"
+ },
+ {
+ "page": "Example/P0413/3a",
+ "contents": "{{#ask: [[Example/P0413/3]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/4",
+ "contents": "[[Has date::Feb 11 2000 10:00:01 PM]]"
+ },
+ {
+ "page": "Example/P0413/4a",
+ "contents": "{{#ask: [[Example/P0413/4]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/5",
+ "contents": "[[Has date::Feb 11 2000 22:00:01]]"
+ },
+ {
+ "page": "Example/P0413/5a",
+ "contents": "{{#ask: [[Example/P0413/5]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/6",
+ "contents": "[[Has date::2000-02-11T22:00:01]]"
+ },
+ {
+ "page": "Example/P0413/6a",
+ "contents": "{{#ask: [[Example/P0413/6]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/7",
+ "contents": "[[Has date::2000-02-11T22:00:01+02:00]]"
+ },
+ {
+ "page": "Example/P0413/7a",
+ "contents": "{{#ask: [[Example/P0413/7]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/8",
+ "contents": "[[Has date::2000 February 2]]"
+ },
+ {
+ "page": "Example/P0413/8a",
+ "contents": "{{#ask: [[Example/P0413/8]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/9",
+ "contents": "[[Has date::2-3-2000]]"
+ },
+ {
+ "page": "Example/P0413/9a",
+ "contents": "{{#ask: [[Example/P0413/9]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/10",
+ "contents": "[[Has date::2/3/2000]]"
+ },
+ {
+ "page": "Example/P0413/10a",
+ "contents": "{{#ask: [[Example/P0413/10]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/11",
+ "contents": "[[Has date::Jan 1 300 BC]]"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/11a",
+ "contents": "{{#ask: [[Example/P0413/11]] |?Has date |?Has date#GR |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JD=JD }}"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/12",
+ "contents": "[[Has date::14000000000 BC]] (WIN-OS only allows 2147483647)"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/12a",
+ "contents": "{{#ask: [[Example/P0413/12]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/13",
+ "contents": "[[Has date::Feb 11 2000 Jl]]"
+ },
+ {
+ "page": "Example/P0413/13a",
+ "contents": "{{#ask: [[Example/P0413/13]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JL=JL Date |?Has date#GR=GR Date }}"
+ },
+ {
+ "page": "Example/P0413/14",
+ "contents": "[[Has date::Feb 11 1492 Gr]]"
+ },
+ {
+ "page": "Example/P0413/14a",
+ "contents": "{{#ask: [[Example/P0413/14]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JL=JL Date |?Has date#GR=GR Date }}"
+ },
+ {
+ "page": "Example/P0413/15",
+ "contents": "[[Has date::Feb 11 2000 10:00 GMT]]"
+ },
+ {
+ "page": "Example/P0413/15a",
+ "contents": "{{#ask: [[Example/P0413/15]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JL=JL Date |?Has date#GR=GR Date }}"
+ },
+ {
+ "page": "Example/P0413/16",
+ "contents": "[[Has date::2000-02-22]]"
+ },
+ {
+ "page": "Example/P0413/16a",
+ "contents": "{{#ask: [[Example/P0413/16]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/17",
+ "contents": "[[Has date::11000 BC]]"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "page": "Example/P0413/17a",
+ "contents": "{{#ask: [[Example/P0413/17]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date }}"
+ },
+ {
+ "page": "Example/P0413/18",
+ "contents": "[[Has date::2488346.0804977 JD]]"
+ },
+ {
+ "page": "Example/P0413/18a",
+ "contents": "{{#ask: [[Example/P0413/18]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0413/19",
+ "contents": "[[Has date::2488346.0804977 MJD]]"
+ },
+ {
+ "page": "Example/P0413/19a",
+ "contents": "{{#ask: [[Example/P0413/19]] |?Has date |?Has date#- |?Has date#ISO=ISO Date |?Has date#MEDIAWIKI=MW Date |?Has date#JD=JD }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 Complete date with time",
+ "subject": "Example/P0413/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T10:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000 10:00:01"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0413/1a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.9166782\">11 February 2000 10:00:01</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.9166782\">2000-02-11T10:00:01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451585.9166782\">2000-02-11T10:00:01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451585.9166782\">10:00, 11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 Just a date",
+ "subject": "Example/P0413/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0413/2a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.5\">11 February 2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.5\">2000-02-11</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451585.5\">2000-02-11</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451585.5\">11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 Yearby itself",
+ "subject": "Example/P0413/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Example/P0413/3a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451544.5\">2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451544.5\">2000-01-01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451544.5\">2000-01-01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451544.5\">2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 Complete date with time, PM",
+ "subject": "Example/P0413/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T22:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000 10:00:01 PM"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7",
+ "subject": "Example/P0413/4a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">11 February 2000 22:00:01</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">22:00, 11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 Complete date with 24h time",
+ "subject": "Example/P0413/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T22:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000 22:00:01"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9",
+ "subject": "Example/P0413/5a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">11 February 2000 22:00:01</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">22:00, 11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 ISO-style date",
+ "subject": "Example/P0413/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T22:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2000-02-11T22:00:01"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#11",
+ "subject": "Example/P0413/6a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">11 February 2000 22:00:01</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">2000-02-11T22:00:01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451586.4166782\">22:00, 11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#12 ISO-style date with offset",
+ "subject": "Example/P0413/7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T20:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2000-02-11T22:00:01+02:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#13",
+ "subject": "Example/P0413/7a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.3333449\">11 February 2000 20:00:01</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451586.3333449\">2000-02-11T20:00:01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451586.3333449\">2000-02-11T20:00:01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451586.3333449\">20:00, 11 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#14 varying order of inputs",
+ "subject": "Example/P0413/8",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-02"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2000 February 2"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#15",
+ "subject": "Example/P0413/8a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451576.5\">2 February 2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451576.5\">2000-02-02</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451576.5\">2000-02-02</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451576.5\">2 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#16 preferred interpretation, month or day, sometimes depends on language settings",
+ "subject": "Example/P0413/9",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-03"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2-3-2000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#17",
+ "subject": "Example/P0413/9a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451577.5\">3 February 2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451577.5\">2000-02-03</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451577.5\">2000-02-03</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451577.5\">3 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#18 various kinds of separators (/) are recognized in all languages",
+ "subject": "Example/P0413/10",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-03"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2/3/2000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#19",
+ "subject": "Example/P0413/10a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451577.5\">3 February 2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451577.5\">2000-02-03</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451577.5\">2000-02-03</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451577.5\">3 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#20 Dates BC/Before common era",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "subject": "Example/P0413/11",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "--301-12-28"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Jan 1 300 BC"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#21",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "subject": "Example/P0413/11a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"1611848.5\">1 January 300 BC <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"1611848.5\">28 December 301 BC</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"1611848.5\">--301-12-28</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"1611848.5\">--301-12-28</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"1611848.5\">28 December 0000</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"1611848.5\">1611848.5</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#24 Julian Calendar annotated date",
+ "subject": "Example/P0413/13",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-24"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000 Jl"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#25",
+ "subject": "Example/P0413/13a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451598.5\">11 February 2000 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451598.5\">2000-02-24</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451598.5\">2000-02-24</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451598.5\">24 February 2000</td>",
+ "<td class=\"JL-Date smwtype_dat\" data-sort-value=\"2451598.5\">11 February 2000 <sup>JL</sup></td>",
+ "<td class=\"GR-Date smwtype_dat\" data-sort-value=\"2451598.5\">24 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#26 Gregorian Calendar annotated date",
+ "subject": "Example/P0413/14",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1492-02-11"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 1492 Gr"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#27",
+ "subject": "Example/P0413/14a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2266042.5\">2 February 1492 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2266042.5\">1492-02-11</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2266042.5\">1492-02-11</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2266042.5\">11 February 1492</td>",
+ "<td class=\"JL-Date smwtype_dat\" data-sort-value=\"2266042.5\">2 February 1492 <sup>JL</sup></td>",
+ "<td class=\"GR-Date smwtype_dat\" data-sort-value=\"2266042.5\">11 February 1492</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#28 Date with time zone shortcut",
+ "subject": "Example/P0413/15",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-11T10:00:00"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 2000 10:00 GMT"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#29",
+ "subject": "Example/P0413/15a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.9166667\">11 February 2000 10:00:00</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451585.9166667\">2000-02-11T10:00:00</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451585.9166667\">2000-02-11T10:00:00</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451585.9166667\">10:00, 11 February 2000</td>",
+ "<td class=\"JL-Date smwtype_dat\" data-sort-value=\"2451585.9166667\">29 January 2000 10:00:00 <sup>JL</sup></td>",
+ "<td class=\"GR-Date smwtype_dat\" data-sort-value=\"2451585.9166667\">11 February 2000 10:00:00</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#30 MySQL date format",
+ "subject": "Example/P0413/16",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2000-02-22"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2000-02-22"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#31",
+ "subject": "Example/P0413/16a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451596.5\">22 February 2000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2451596.5\">2000-02-22</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2451596.5\">2000-02-22</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2451596.5\">22 February 2000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#32 Pre-history",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "subject": "Example/P0413/17",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "--11000-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "11000 BC"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#33",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support BC/BCE dates"
+ },
+ "subject": "Example/P0413/17a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"-11001\">11000 BC</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"-11001\">--11000-01-01</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"-11001\">--11000-01-01</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"-11001\">0000</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#34 Direct JD input",
+ "subject": "Example/P0413/18",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2100-10-04T13:55:55"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2488346.0804977 JD"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#35",
+ "subject": "Example/P0413/18a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2488346.0804977\">4 October 2100 13:55:55</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2488346.0804977\">2100-10-04T13:55:55</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"2488346.0804977\">2100-10-04T13:55:55</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"2488346.0804977\">13:55, 4 October 2100</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"2488346.0804977\">2488346.0804977</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#36 Direct MJD input",
+ "subject": "Example/P0413/19",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "8671-09-27T01:55:55"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2488346.0804977 MJD"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#37",
+ "subject": "Example/P0413/19a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"4888346.5804977\">27 September 8671 01:55:55</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"4888346.5804977\">8671-09-27T01:55:55</td>",
+ "<td class=\"ISO-Date smwtype_dat\" data-sort-value=\"4888346.5804977\">8671-09-27T01:55:55</td>",
+ "<td class=\"MW-Date smwtype_dat\" data-sort-value=\"4888346.5804977\">01:55, 27 September 8671</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"4888346.5804977\">4888346.5804977</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_TIMEV_CM"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0414.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0414.json
new file mode 100644
index 00000000..817e13d9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0414.json
@@ -0,0 +1,395 @@
+{
+ "description": "Test in-text annotation/free format for `_dat` datatype (#1389, #1401, en, `smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0414/1",
+ "contents": "[[Has date::Feb 11 1389 10:00:01]]"
+ },
+ {
+ "page": "Example/P0414/1a",
+ "contents": "{{#ask: [[Example/P0414/1]] |?Has date#-F[H:i:s.u] |?Has date#-F[Y/m/d H:i] |?Has date#GR-F[Y/m/d H:i] |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0414/2",
+ "contents": "[[Has date::100000 BC]]"
+ },
+ {
+ "page": "Example/P0414/2a",
+ "contents": "{{#ask: [[Example/P0414/2]] |?Has date#-F[H:i:s.u] |?Has date#-F[Y/m/d H:i] |?Has date#GR-F[Y/m/d H:i] |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0414/3",
+ "contents": "[[Has date::1902]]"
+ },
+ {
+ "page": "Example/P0414/3a",
+ "contents": "{{#ask: [[Example/P0414/3]] |?Has date#-F[Y] |?Has date#-F[Y/m/d] |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0414/4",
+ "contents": "[[Has date::12001102120325]]"
+ },
+ {
+ "page": "Example/P0414/4a",
+ "contents": "{{#ask: [[Example/P0414/4]] |?Has date#-F[Y] |?Has date#-F[Y/m/d] |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0414/5",
+ "contents": "[[Has date::-100000]]"
+ },
+ {
+ "page": "Example/P0414/5a",
+ "contents": "{{#ask: [[Example/P0414/5]] |?Has date#-F[H:i:s.u] |?Has date#-F[Y/m/d H:i] |?Has date#GR-F[Y/m/d H:i] |?Has date#JD=JD }}"
+ },
+ {
+ "page": "Example/P0414/6",
+ "contents": "[[Has date::1902 AD]]"
+ },
+ {
+ "page": "Example/P0414/6a",
+ "contents": "{{#ask: [[Example/P0414/6]] |?Has date |?Has date#-F[Y] |?Has date#-F[Y/m/d] }}"
+ },
+ {
+ "page": "Example/P0414/7",
+ "contents": "[[Has date::2012-07-08 11:14:15.888499949]]"
+ },
+ {
+ "page": "Example/P0414/7a",
+ "contents": "{{#ask: [[Example/P0414/7]] |?Has date |?Has date#-F[H:i:s.u] }}"
+ },
+ {
+ "page": "Example/P0414/8",
+ "contents": "[[Has date::2010年1月6日 16:57]]"
+ },
+ {
+ "page": "Example/P0414/8a",
+ "contents": "{{#ask: [[Example/P0414/8]] |?Has date |?Has date#-F[Y年m月d日 H:i] }}"
+ },
+ {
+ "page": "Example/P0414/9",
+ "contents": "[[Has date::2010年1月6日 16時57分]]"
+ },
+ {
+ "page": "Example/P0414/9a",
+ "contents": "{{#ask: [[Example/P0414/9]] |?Has date |?Has date#-F[Y年m月d日 H:i] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 JL calendar date with time",
+ "subject": "Example/P0414/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1389-02-19T10:00:01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Feb 11 1389 10:00:01"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0414/1a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2228431.9166782\">10:00:01.000000</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2228431.9166782\">1389/02/11 10:00 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2228431.9166782\">1389/02/19 10:00</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"2228431.9166782\">2228431.9166782</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 Prehistory",
+ "subject": "Example/P0414/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "--100000-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "100000 BC"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0414/2a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"-100001\">--100000-01-01</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"-100001\">-34802824.5</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 year only",
+ "subject": "Example/P0414/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1902-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "1902"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Example/P0414/3a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2415750.5\">1902</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2415750.5\">1902/01/01</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"2415750.5\">2415750.5</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 timestamp input (TS_MW as 'YmdHis)",
+ "subject": "Example/P0414/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1200-11-02T12:03:25"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "12001102120325"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7",
+ "subject": "Example/P0414/4a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2159657.0023727\">1200 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2159657.0023727\">1200/10/26 <sup>JL</sup></td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"2159657.0023727\">2159657.0023727</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 negative year without BC",
+ "subject": "Example/P0414/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "--100000-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "-100000"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9",
+ "subject": "Example/P0414/5a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"-100001\">--100000-01-01</td>",
+ "<td class=\"JD smwtype_dat\" data-sort-value=\"-100001\">-34802824.5</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 positive year only with era marker",
+ "subject": "Example/P0414/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "AD 1902-01-01"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "1902 AD"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#11",
+ "subject": "Example/P0414/6a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2415750.5\">AD 1902</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2415750.5\">1902</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2415750.5\">1902/01/01</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#12 micro sec",
+ "subject": "Example/P0414/7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2012-07-08T11:14:15"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2012-07-08 11:14:15.888499949"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#13 `.888499949` is being rounded to `.888500`",
+ "subject": "Example/P0414/7a",
+ "skip-on": {
+ "hhvm-*": "Avoid .888500 vs. .888499 msec issue due to hhvm#6899"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2456116.9682395\">8 July 2012 11:14:15</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2456116.9682395\">11:14:15.888500</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#14 Japanese date format",
+ "subject": "Example/P0414/8",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "2010-01-06T16:57:00"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2010年1月6日 16:57"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#15",
+ "subject": "Example/P0414/8a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2455203.20625\">6 January 2010 16:57:00</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2455203.20625\">2010年01月06日 16:57</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#16",
+ "subject": "Example/P0414/9a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2455203.20625\">6 January 2010 16:57:00</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2455203.20625\">2010年01月06日 16:57</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_TIMEV_CM"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0415.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0415.json
new file mode 100644
index 00000000..2f19eed8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0415.json
@@ -0,0 +1,65 @@
+{
+ "description": "Test in-text annotation on `_tem` with display unit preference (en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature",
+ "contents": "[[Has type::Temperature]] [[Display unit::Celsius]]"
+ },
+ {
+ "page": "Example/P0415/1",
+ "contents": "[[Has temperature:: 32 °F]] [[Has temperature::100 °C]]"
+ },
+ {
+ "page": "Example/P0415/1a",
+ "contents": "{{#ask: [[Example/P0415/1]] |?Has temperature |?Has temperature#-=- |?Has temperature#-u=-u |?Has temperature#-n=-n |?Has temperature#°R=°R |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0415/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_temperature"
+ ],
+ "propertyValues": [
+ 273.15,
+ 373.15
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0415/1a",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">0&#160;°C</span><span class=\"smwttcontent\">273.15&#160;K <br />32&#160;°F <br />491.67&#160;°R <br /></span></span>",
+ "<span class=\"smwtext\">100&#160;°C</span><span class=\"smwttcontent\">373.15&#160;K <br />212&#160;°F <br />671.67&#160;°R <br /></span></span>",
+ "<td class=\"- smwtype_tem\" data-sort-value=\"273.15\">0 °C<br />100 °C</td>",
+ "<td class=\"-u smwtype_tem\" data-sort-value=\"273.15\">°C<br />°C</td>",
+ "<td class=\"-n smwtype_tem\" data-sort-value=\"273.15\">0 <br />100 </td>",
+ "<span class=\"smwtext\">491.67&#160;°R</span><span class=\"smwttcontent\">0&#160;°C <br />273.15&#160;K <br />32&#160;°F <br /></span></span>",
+ "<span class=\"smwtext\">671.67&#160;°R</span><span class=\"smwttcontent\">100&#160;°C <br />373.15&#160;K <br />212&#160;°F <br /></span></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0416.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0416.json
new file mode 100644
index 00000000..2f97e148
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0416.json
@@ -0,0 +1,281 @@
+{
+ "description": "Test in-text annotation with DISPLAYTITLE (#1410, #1611, `wgRestrictDisplayTitle`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Dwc:vernacularName",
+ "contents": "[[Has type::Text]] {{DISPLAYTITLE:dwc:vernacularName}}"
+ },
+ {
+ "page": "Example/P0416/1",
+ "contents": "{{DISPLAYTITLE:Foo}} {{#subobject:Bar|text=abc|Display title of=123}}"
+ },
+ {
+ "page": "Example/P0416/Q1.1",
+ "contents": "{{#ask: [[~Foo]] OR [[~123*]] |?Display title of |format=table }}"
+ },
+ {
+ "page": "Example/P0416/2",
+ "contents": "{{DISPLAYTITLE:Foobar}} {{#subobject:Bar|text=abc}}"
+ },
+ {
+ "page": "Example/P0416/Q2.1",
+ "contents": "{{#ask: [[~Example/P0416/2*]] OR [[~Foobar*]] |?Display title of |format=table }}"
+ },
+ {
+ "page": "Example/P0416/3",
+ "contents": "[[dwc:vernacularName::Gewoon struisgras]] {{DISPLAYTITLE:Agrostis capillaris}}"
+ },
+ {
+ "page": "Example/P0416/Q3.1",
+ "contents": "{{#ask: [[~Agrostis*]] |?dwc:vernacularName |format=table }}"
+ },
+ {
+ "page": "Example/P0416/4",
+ "contents": "{{DISPLAYTITLE:Foo}} {{DEFAULTSORTKEY:BAR}}"
+ },
+ {
+ "page": "Example/P0416/5",
+ "contents": "{{DISPLAYTITLE:Foo}} {{#subobject:|@sortkey=SORT}} {{#show: Example/P0416/5 |?Has subobject}}"
+ },
+ {
+ "page": "Example/P0416/6/1",
+ "contents": "{{DISPLAYTITLE:P0416}} {{#subobject:@category=P0416}} [[Category:P0416]]"
+ },
+ {
+ "page": "Example/P0416/6/2",
+ "contents": "{{DISPLAYTITLE:P0416}} {{DEFAULTSORT:BAR}} {{#subobject:@category=P0416}} [[Category:P0416]]"
+ },
+ {
+ "page": "Example/P0416/6/3",
+ "contents": "{{DISPLAYTITLE:P0416}} {{#subobject:@category=P0416|@sortkey=DEF}} [[Category:P0416]]"
+ },
+ {
+ "page": "Example/P0416/7",
+ "contents": "{{DISPLAYTITLE:ABC & DEF}}, see #1611"
+ },
+ {
+ "page": "Example/P0416/8'a",
+ "contents": "{{DISPLAYTITLE:<span style=\"position: absolute; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px);\">{{FULLPAGENAME}}</span>}} "
+ },
+ {
+ "page": "Example/P0416/Q6.1",
+ "contents": "{{#ask: [[Category:P0416]] [[~P0416*]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0416/Q6.2.1",
+ "contents": "{{#ask: [[Category:P0416]] [[~BAR]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0416/Q6.2.2",
+ "contents": "{{#ask: [[Category:P0416]] [[~BAR*]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0416/Q6.3",
+ "contents": "{{#ask: [[Category:P0416]] [[~DEF]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0416/Q7.1",
+ "contents": "{{#ask: [[Display title of::ABC & DEF]] |link=none |format=table |sort=# |order=asc}}"
+ },
+ {
+ "page": "Example/P0416/Q8.1",
+ "contents": "{{#ask: [[Display title of::Example/P0416/8'a]] |link=none |format=table |sort=# |order=asc}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0416/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_DTITLE",
+ "_SOBJ"
+ ],
+ "propertyValues": [
+ "Foo"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0416/1#Bar",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_DTITLE",
+ "_TEXT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "abc",
+ "123"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0416/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0416/1\">Foo</a></td><td class=\"Display-title-of smwtype_txt\">Foo</td>",
+ "title=\"Example/P0416/1\">123#Bar</a></span></td><td class=\"Display-title-of smwtype_txt\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0416/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0416/2\">Foobar</a></td><td class=\"Display-title-of smwtype_txt\">Foobar</td>",
+ "title=\"Example/P0416/2\">Foobar#Bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0416/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0416/3\">Agrostis capillaris</a></td><td class=\"dwc:vernacularName smwtype_txt\">Gewoon struisgras</td>",
+ "title=\"Property:Dwc:vernacularName\">dwc:vernacularName</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Example/P0416/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_DTITLE",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "Foo",
+ "BAR"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6",
+ "subject": "Example/P0416/5#_47ab3a24a41d0687c9398e1886c38fe3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 1,
+ "propertyKeys": [
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "SORT"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7",
+ "subject": "Example/P0416/5",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0416/5#_47ab3a24a41d0687c9398e1886c38fe3\" title=\"Example/P0416/5\">Foo</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (sortkey is copied from root, [1 and 3 contain both P0416 as sort while 1(sobj) contains P0416# ... ])",
+ "subject": "Example/P0416/Q6.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/6/1</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0416/6/3</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/6/1#_4c8278b5823715af48e85d55f6118d4e</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 (sortkey is copied from root, strict ~BAR)",
+ "subject": "Example/P0416/Q6.2.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/6/2</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#10 (sortkey is copied from root, ~BAR*)",
+ "subject": "Example/P0416/Q6.2.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/6/2</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0416/6/2#_4c8278b5823715af48e85d55f6118d4e</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#11 (sobj sortkey)",
+ "subject": "Example/P0416/Q6.3",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/6/3#_b6f2e00f3a822fef179c8c2ea8e0c987</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#12 Display title/query includes &",
+ "subject": "Example/P0416/Q7.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/7</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#13 Display title/query includes '",
+ "subject": "Example/P0416/Q8.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0416/8'a</td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgRestrictDisplayTitle": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0417.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0417.json
new file mode 100644
index 00000000..63bc7f5d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0417.json
@@ -0,0 +1,88 @@
+{
+ "description": "Test in-text annotation for `Allows pattern` to match regular expressions (en)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw allows pattern",
+ "contents": "...\n Whitelist|^(FooOnWhitelist|Bar|Foo bar)$\n IPv4|^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$\n"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has whitelist",
+ "contents": "[[Has type::Text]] [[Allows pattern::Whitelist]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "IPv4",
+ "contents": "[[Has type::Text]] [[Allows pattern::IPv4]]"
+ },
+ {
+ "page": "Example/P0417/1",
+ "contents": "[[Has whitelist::FooOnWhitelist]] [[Has whitelist::Foobar]]"
+ },
+ {
+ "page": "Example/P0417/2",
+ "contents": "[[IPv4::192.168.0.100]] [[IPv4::192.168.0.256]]"
+ },
+ {
+ "page": "Example/P0417/1a",
+ "contents": "{{#ask: [[Example/P0417/1]] |?Has whitelist |format=table }}"
+ },
+ {
+ "page": "Example/P0417/2a",
+ "contents": "{{#ask: [[Example/P0417/2]] |?IPv4 |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0, value Foobar is not allowed",
+ "subject": "Example/P0417/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_whitelist",
+ "_ERRC"
+ ],
+ "propertyValues": [
+ "FooOnWhitelist"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0417/1a",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0417/1\">Example/P0417/1</a></td><td class=\"Has-whitelist smwtype_txt\">FooOnWhitelist</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0417/2a",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0417/2\">Example/P0417/2</a></td><td class=\"IPv4 smwtype_txt\">192.168.0.100</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "skip-on": [],
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0418.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0418.json
new file mode 100644
index 00000000..69e8f958
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0418.json
@@ -0,0 +1,85 @@
+{
+ "description": "Test in-text annotation using `_SERV` as provide service links (en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "PropertyWithServiceLink",
+ "contents": "[[Has type::Text]] [[Provides service::some links]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "PropertyWithAnotherServiceLink",
+ "contents": "[[Has type::Text]] [[Provides service::some other links]]"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw_service_some_other_links",
+ "contents": " label text1|http://someurl.com"
+ },
+ {
+ "page": "Example/P0418/1",
+ "contents": " [[PropertyWithServiceLink::123]]"
+ },
+ {
+ "page": "Example/P0418/2",
+ "contents": " [[PropertyWithAnotherServiceLink::ABC]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "PropertyWithServiceLink",
+ "namespace": "SMW_NS_PROPERTY",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_TYPE",
+ "_MDAT",
+ "_SKEY",
+ "_SERV"
+ ],
+ "propertyValues": [
+ "some links"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "PropertyWithServiceLink",
+ "namespace": "SMW_NS_PROPERTY",
+ "assert-output": {
+ "to-contain": [
+ "some links"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "PropertyWithAnotherServiceLink",
+ "namespace": "SMW_NS_PROPERTY",
+ "assert-output": {
+ "to-contain": [
+ "some other links"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0419.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0419.json
new file mode 100644
index 00000000..bdb41456
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0419.json
@@ -0,0 +1,285 @@
+{
+ "description": "Test in-text annotation for `_PVUC` to validate uniqueness (`smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has Url",
+ "contents": "[[Has type::URL]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has monolingual text",
+ "contents": "[[Has type::Monolingual text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has numeric reference",
+ "contents": "[[Has type::Reference]] [[Has fields::Number;Date]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has uniqueness record",
+ "contents": "[[Has type::Record]] [[Has fields::Number;Date]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "page": "Example/P0419/1",
+ "contents": "[[Has Url::http://example.org/P0419]]"
+ },
+ {
+ "page": "Example/P0419/2",
+ "contents": "[[Has Url::http://example.org/P0419]] [[Has Url::http://example.org/P0419-1]]"
+ },
+ {
+ "page": "Example/P0419/3",
+ "contents": "[[Has text::P0419]]"
+ },
+ {
+ "page": "Example/P0419/4",
+ "contents": "[[Has text::P0419]]"
+ },
+ {
+ "page": "Example/P0419/5",
+ "contents": "[[Has date::1 Jan 1970 12:50:12]]"
+ },
+ {
+ "page": "Example/P0419/6",
+ "contents": "{{#subobject:|Has date=1 Jan 1970 12:50:12}}"
+ },
+ {
+ "page": "Example/P0419/7",
+ "contents": "[[Has monolingual text::Foo@en]]"
+ },
+ {
+ "page": "Example/P0419/8",
+ "contents": "{{#subobject:|Has monolingual text=Foo@en}}"
+ },
+ {
+ "page": "Example/P0419/9",
+ "contents": "[[Has numeric reference::42;1 Jan 1970]]"
+ },
+ {
+ "page": "Example/P0419/9/1",
+ "contents": "[[Has numeric reference::42;1 Jan 1970]]"
+ },
+ {
+ "page": "Example/P0419/9/2",
+ "contents": "[[Has numeric reference::42;1 Jan 1970 12:00]]"
+ },
+ {
+ "page": "Example/P0419/10",
+ "contents": "[[Has uniqueness record::42;1 Jan 1970]]"
+ },
+ {
+ "page": "Example/P0419/10/1",
+ "contents": "[[Has uniqueness record::42;1 Jan 1970]]"
+ },
+ {
+ "page": "Example/P0419/10/2",
+ "contents": "[[Has uniqueness record::42;1 Jan 1970 12:00]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0419/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_Url"
+ ],
+ "propertyValues": [
+ "http://example.org/P0419"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0419/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC",
+ "Has_Url"
+ ],
+ "propertyValues": [
+ "http://example.org/P0419-1"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0419/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text"
+ ],
+ "propertyValues": [
+ "P0419"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 Fails uniqueness for the text annotated in Example/P0419/3",
+ "subject": "Example/P0419/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 Fails uniqueness for date value annotated in Example/P0419/5",
+ "subject": "Example/P0419/6#_9fbec9d56750049a3420802d55db9a62",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "_SKEY",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 Fails uniqueness for monolingual text value annotated in Example/P0419/7",
+ "subject": "Example/P0419/8#_7d241515e1224e118e0cd09f9b055a79",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "_SKEY",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (Fails uniqueness due to same reference in Example/P0419/9)",
+ "subject": "Example/P0419/9/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (same number but different date as in Example/P0419/9)",
+ "subject": "Example/P0419/9/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has numeric reference"
+ ],
+ "propertyValues": [
+ "Example/P0419/9/2#0##_REF31fea0d5c8403916c6f1da7c68f3998f"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (Fails uniqueness due to same record in Example/P0419/10)",
+ "subject": "Example/P0419/10/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 (same number but different date as in Example/P0419/10)",
+ "subject": "Example/P0419/10/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has uniqueness record"
+ ],
+ "propertyValues": [
+ "Example/P0419/10/2#0##_fdf9ee62dae77aa0c5d7cf89d1faa64d"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgDVFeatures": [
+ "SMW_DV_PVUC"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0420.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0420.json
new file mode 100644
index 00000000..356d2f20
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0420.json
@@ -0,0 +1,108 @@
+{
+ "description": "Test in-text annotation for `_dat` using JL/GR annotated values (en, `smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0420/1",
+ "contents": "[[Has date::21 March 1685 JL]]"
+ },
+ {
+ "page": "Example/P0420/2",
+ "contents": "[[Has date::21 March 1685 Jl]]"
+ },
+ {
+ "page": "Example/P0420/3",
+ "contents": "[[Has date::21 March 1685 GR]]"
+ },
+ {
+ "page": "Example/P0420/1a",
+ "contents": "{{#ask: [[Example/P0420/1]] |?Has date |?Has date#GR |?Has date#JL }}"
+ },
+ {
+ "page": "Example/P0420/2a",
+ "contents": "{{#ask: [[Example/P0420/2]] |?Has date |?Has date#GR |?Has date#JL }}"
+ },
+ {
+ "page": "Example/P0420/3a",
+ "contents": "{{#ask: [[Example/P0420/3]] |?Has date |?Has date#GR |?Has date#JL }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 JL calendar date with time",
+ "subject": "Example/P0420/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1685-03-31"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "21 March 1685 JL"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 JL",
+ "subject": "Example/P0420/1a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">21 March 1685 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">31 March 1685</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">21 March 1685 <sup>JL</sup></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 JL",
+ "subject": "Example/P0420/2a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">21 March 1685 <sup>JL</sup></td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">31 March 1685</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336583.5\">21 March 1685 <sup>JL</sup></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 GR",
+ "subject": "Example/P0420/3a",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336573.5\">21 March 1685</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336573.5\">21 March 1685</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2336573.5\">11 March 1685 <sup>JL</sup></td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_TIMEV_CM"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0421.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0421.json
new file mode 100644
index 00000000..eecd86b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0421.json
@@ -0,0 +1,55 @@
+{
+ "description": "Test in-text annotation with combined constraint validation `_PVUC` and `_PVAL` (`smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has uniqueness url",
+ "contents": "[[Has type::URL]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]] [[Allows value::open]]"
+ },
+ {
+ "page": "Example/P0421/1",
+ "contents": "[[Has uniqueness url::http://example.org/P0421]] [[Has text::open]]"
+ },
+ {
+ "page": "Example/P0421/2",
+ "contents": "[[Has uniqueness url::http://example.org/P0421]] [[Has text::closed]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 Fails on both constraints due to Example/P0421/1",
+ "subject": "Example/P0421/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgDVFeatures": [
+ "SMW_DV_PVUC"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0422.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0422.json
new file mode 100644
index 00000000..8f795a56
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0422.json
@@ -0,0 +1,124 @@
+{
+ "description": "Test in-text annotation `_dat` on partial dates (#2076, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0422/1",
+ "contents": "[[Has date::Jan 1990]], [[Has date::02 1990]], [[Has date::1782]], [[Has date::1990å¹´6月]], [[Has date::1990å¹´4月28æ—¥ 7時01分]], [[Has date::199ï¼å¹´ï¼™æœˆ]]"
+ },
+ {
+ "page": "Example/P0422/Q/1",
+ "contents": "{{#show: Example/P0422/1 |?Has date }}"
+ },
+ {
+ "page": "Example/P0422/2",
+ "contents": "{{#subobject:|Has date=Jan 1923|@category=Partial dates}} {{#subobject:|Has date=Feb 1960|@category=Partial dates}} {{#subobject:|Has date=1645|@category=Partial dates}}"
+ },
+ {
+ "page": "Example/P0422/Q/2/1",
+ "contents": "{{#ask: [[Category:Partial dates]] |?Has date |sort=Has date|order=asc |link=none }}"
+ },
+ {
+ "page": "Example/P0422/Q/2/2",
+ "contents": "{{#ask: [[Category:Partial dates]] |?Has date |sort=Has date|order=desc |link=none }}"
+ },
+ {
+ "page": "Example/P0422/3",
+ "contents": "[[Has date::0]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0422/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_date"
+ ],
+ "propertyValues": [
+ "1990-01-01",
+ "1990-02-01",
+ "1782-01-01",
+ "1990-06-01",
+ "1990-04-28T07:01:00",
+ "1990-09-01"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0422/Q/1",
+ "assert-output": {
+ "to-contain": [
+ "January 1990",
+ "February 1990",
+ "1782",
+ "June 1990",
+ "28 April 1990 07:01:00",
+ "September 1990"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (sort asc)",
+ "subject": "Example/P0422/Q/2/1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0422/2#_eda78f1641edf981597b59b79f9a8c1f</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2321884.5\">1645</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0422/2#_23b1a59a99dd05132dabb6cf45cb40b3</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2423420.5\">January 1923</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0422/2#_f9a370d40ebd98e67c2b884fc09c7f9d</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2436965.5\">February 1960</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (sort desc)",
+ "subject": "Example/P0422/Q/2/2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0422/2#_f9a370d40ebd98e67c2b884fc09c7f9d</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2436965.5\">February 1960</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0422/2#_23b1a59a99dd05132dabb6cf45cb40b3</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2423420.5\">January 1923</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0422/2#_eda78f1641edf981597b59b79f9a8c1f</td><td class=\"Has-date smwtype_dat\" data-sort-value=\"2321884.5\">1645</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (#2076, output an error instead of an exception)",
+ "subject": "Example/P0422/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0423.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0423.json
new file mode 100644
index 00000000..6a0b4a32
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0423.json
@@ -0,0 +1,173 @@
+{
+ "description": "Test in-text annotation / `#ask` (#MEDIAWIKI, #LOCL) output for `_dat` datatype (#1545, `wgContLang=en`, `wgLang=ja`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0423/1",
+ "contents": "[[Has date::12 Jan 1957 12:05]]"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0423/2",
+ "contents": "[[Has date::12 April 1957 12:05]]"
+ },
+ {
+ "page": "Example/P0423/Q1.1",
+ "contents": "{{#ask: [[Has date::12 Jan 1957 12:05]] |?Has date |?Has date#MEDIAWIKI }}"
+ },
+ {
+ "page": "Example/P0423/Q1.2",
+ "contents": "{{#show: Example/P0423/1 |?Has date |?Has date#MEDIAWIKI }}"
+ },
+ {
+ "page": "Example/P0423/Q1.3",
+ "contents": "{{#ask: [[Has date::12 Jan 1957 12:05]] |?Has date#LOCL }}"
+ },
+ {
+ "page": "Example/P0423/Q1.4",
+ "contents": "{{#show: Example/P0423/1 |?Has date#LOCL }}"
+ },
+ {
+ "page": "Example/P0423/Q1.5",
+ "contents": "{{#show: Example/P0423/1 |?Has date#LOCL@fr }}"
+ },
+ {
+ "page": "Example/P0423/Q2.1",
+ "contents": "{{#show: Example/P0423/2 |?Has date }}"
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support `-4715-11-24T12:00:00Z\"^^xsd:dateTime` and goes '...Error: Malformed query...'"
+ },
+ "page": "Example/P0423/3",
+ "contents": "[[Has date::Jan 10000000000]]"
+ },
+ {
+ "page": "Example/P0423/Q3.1",
+ "contents": "{{#ask: [[Example/P0423/3]] |?Has date#LOCL }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0423/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_date",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "1957-01-12T12:05:00"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "12 Jan 1957 12:05"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (#ask MEDIAWIKI)",
+ "subject": "Example/P0423/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2435851.0034722\">12 January 1957 12:05:00</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2435851.0034722\">1957年1月12日 (土) 12:05</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (#show MEDIAWIKI)",
+ "subject": "Example/P0423/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_dat\" data-sort-value=\"2435851.0034722\">12 January 1957 12:05:00</td>",
+ "<td class=\"smwtype_dat\" data-sort-value=\"2435851.0034722\">1957年1月12日 (土) 12:05</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (#ask LOCL)",
+ "subject": "Example/P0423/Q1.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2435851.0034722\">1957年1月12日 (土) 12:05:00</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (#show LOCL for user lang)",
+ "subject": "Example/P0423/Q1.4",
+ "assert-output": {
+ "to-contain": [
+ "1957年1月12日 (土) 12:05:00"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (#show LOCL@fr for annotated fr lang and not for user lang)",
+ "subject": "Example/P0423/Q1.5",
+ "assert-output": {
+ "to-contain": [
+ "12:05:00, 12 janvier 1957"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 page vs global content language",
+ "subject": "Example/P0423/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "12 April 1957 12:05:00"
+ ],
+ "not-contain": [
+ "12 avril 1957 12:05:00"
+ ]
+ }
+ },
+ {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1, see Example/P0423/3 comment!"
+ },
+ "type": "parser",
+ "about": "#7 avoid a possible `DateTime ... Failed to parse time ...` on large dates, only checking some minor output since WINOS reports 784354016999.5, yet Linux returns 3652426721059.5 (float precision issue)",
+ "subject": "Example/P0423/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "class=\"Has-date smwtype_dat\" "
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "ja",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0424.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0424.json
new file mode 100644
index 00000000..b9488881
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0424.json
@@ -0,0 +1,115 @@
+{
+ "description": "Test in-text annotation for `_boo` datatype using `LOCL` (`wgContLang=en`, `wgLang=fr`, skip-on 1.25.6)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has boolean",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "page": "Example/P0424/1",
+ "contents": "[[Has boolean::true]] [[Category:P0424]]"
+ },
+ {
+ "page": "Example/P0424/2",
+ "contents": "{{#set:Has boolean=false}} [[Category:P0424]]"
+ },
+ {
+ "page": "Example/P0424/Q1.1",
+ "contents": "{{#ask: [[Category:P0424]] |?Has boolean |?Has boolean#LOCL}}"
+ },
+ {
+ "page": "Example/P0424/Q1.2",
+ "contents": "{{#ask: [[Category:P0424]] |?Has boolean |?Has boolean#LOCL@ja}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 true",
+ "subject": "Example/P0424/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_boolean",
+ "_SKEY",
+ "_MDAT",
+ "_INST"
+ ],
+ "propertyValues": [
+ true
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 false",
+ "subject": "Example/P0424/2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_boolean",
+ "_SKEY",
+ "_MDAT",
+ "_INST"
+ ],
+ "propertyValues": [
+ false
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 LOCL used the user language",
+ "subject": "Example/P0424/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">true</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">vrai</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">false</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">faux</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 LOCL@js uses a specific language",
+ "subject": "Example/P0424/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">true</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"1\">真</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">false</td>",
+ "<td class=\"Has-boolean smwtype_boo\" data-sort-value=\"0\">å½</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "fr",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "mw-1.25.6": "Somehow the content lang is not set correctly on Travis (locally works fine)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0425.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0425.json
new file mode 100644
index 00000000..e2db98de
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0425.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test in-text annotation on `_tem`/ `_num` with different page content language (#1591, `wgContLang=es`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has temperature",
+ "contents": "[[Has type::Temperature]]"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0425/1",
+ "contents": "[[Has temperature::310.928 K]]"
+ },
+ {
+ "contentlanguage": "en",
+ "page": "Example/P0425/2",
+ "contents": "[[Has temperature::310.928 K]]"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0425/3",
+ "contents": "[[Has temperature::310,928 K]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 page content language FR and . do not match, uses `en` as display language",
+ "subject": "Example/P0425/1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwttcontent\">\".928K\" is not declared as a valid unit of measurement for this property.</span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 page content language EN and . do match",
+ "subject": "Example/P0425/2",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">310.928 K</span><span class=\"smwttcontent\">37.778&#160;°C <br />100&#160;°F <br />559.67&#160;°R <br /></span></span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 page content language FR and , do match",
+ "subject": "Example/P0425/3",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwtext\">310,928 K</span><span class=\"smwttcontent\">37.778&#160;°C <br />100&#160;°F <br />559.67&#160;°R <br /></span></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgLang": "en",
+ "wgContLang": "es",
+ "wgLanguageCode": "es",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0426.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0426.json
new file mode 100644
index 00000000..a80d6f22
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0426.json
@@ -0,0 +1,104 @@
+{
+ "description": "Test in-text annotation for `_num` on big/small numbers/scientific notation (`wgContLang=fr`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number with precision 4",
+ "contents": "[[Has type::Number]] [[Display precision of::4]]"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0426/1",
+ "contents": "[[Has number::0,00000000000012]] [[Has number::1,000000000000123]] [[Has number::72,769482308]] [[Has number::5000,769482308]] [[Has number::-0,000000000004566]] [[Has number::0,2123456e+3]] [[Has number::1,334e-13]]"
+ },
+ {
+ "contentlanguage": "en",
+ "page": "Example/P0426/2",
+ "contents": "[[Has number::0.00000000000012]] [[Has number::1.000000000000123]] [[Has number::72.769482308]] [[Has number::5000.769482308]] [[Has number::-0.000000000004566]] [[Has number::0.2123456e+3]] [[Has number::1.334e-13]]"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0426/Q1.1",
+ "contents": "{{#ask: [[Example/P0426/1]] |?Has number }}"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0426/Q1.2",
+ "contents": "{{#ask: [[Example/P0426/1]] |?Has number#-p5 }}"
+ },
+ {
+ "contentlanguage": "fr",
+ "page": "Example/P0426/Q2.1",
+ "contents": "{{#ask: [[Example/P0426/2]] |?Has number }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 page content language FR, display in en",
+ "subject": "Example/P0426/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "1.2e-13",
+ "1",
+ "72.769",
+ "5,000.769",
+ "-4.566e-12",
+ "212.346",
+ "1.334e-13"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 page content language EN, display in en",
+ "subject": "Example/P0426/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "1.2e-13",
+ "1",
+ "72.769",
+ "5,000.769",
+ "-4.566e-12",
+ "212.346",
+ "1.334e-13"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 with precision display precision 5",
+ "subject": "Example/P0426/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "0.00000",
+ "1.00000",
+ "72.76948",
+ "5,000.76948",
+ "-0.00000",
+ "212.34560",
+ "0.00000"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgLang": "en",
+ "wgContLang": "fr",
+ "wgLanguageCode": "fr",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0427.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0427.json
new file mode 100644
index 00000000..898ee85c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0427.json
@@ -0,0 +1,146 @@
+{
+ "description": "Test in-text annotation with DISPLAYTITLE / `foaf` to check on upper vs. lower case (`wgRestrictDisplayTitle`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import foaf",
+ "contents": "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n homepage|Type:URL\n mbox|Type:Email\n mbox_sha1sum|Type:Text\n depiction|Type:URL\n phone|Type:Text\n Person|Category\n Organization|Category\n knows|Type:Page\n member|Type:Page\n"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:homepage",
+ "contents": "[[Imported from::foaf:homepage]] {{DISPLAYTITLE:foaf:homepage}} [[Has property description::URL representing ... @en]] [[Category:Imported vocabulary]]"
+ },
+ {
+ "page": "Example/P0427/Q0.1",
+ "contents": "{{#show: Property:Foaf:homepage |?Has property description}}"
+ },
+ {
+ "page": "Example/P0427/Q0.2",
+ "contents": "{{#ask: [[Property:+]][[Category:Imported vocabulary]] |?Has property description=Description |link=none}}"
+ },
+ {
+ "page": "Example/P0427/1",
+ "contents": "{{#subobject: |Has text=abc |display title of=ab c123 |@category=P0427 }}{{#subobject: |Has text=ABC |display title of=AB C123 |@category=P0427 }}"
+ },
+ {
+ "page": "Example/P0427/Q1.1",
+ "contents": "{{#ask: [[Category:P0427]] [[~ab c*]] |?Has text |link=none}}"
+ },
+ {
+ "page": "Example/P0427/Q1.2",
+ "contents": "{{#ask: [[Category:P0427]] [[~AB C*]] |?Has text |link=none}}"
+ },
+ {
+ "page": "Example/P0427/Q1.3",
+ "contents": "{{#ask: [[Example/P0427/1#_7cae05fedd48fa820a0ea915518cc3fd]] |?Has text |link=none}}"
+ },
+ {
+ "page": "Example/P0427/Q1.4",
+ "contents": "{{#ask: [[Category:P0427]] [[!~ab c*]] |?Has text |link=none}}"
+ },
+ {
+ "page": "Example/P0427/Q1.5",
+ "contents": "{{#ask: [[Category:P0427]] [[!~AB C*]] |?Has text |link=none}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 match foaf:homepage to Property:Foaf:homepage",
+ "subject": "Example/P0427/Q0.1",
+ "assert-output": {
+ "to-contain": [
+ "URL representing ... (en)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0427/Q0.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Property:Foaf:homepage</td>",
+ "<td class=\"Description smwtype_mlt_rec\">URL representing ... (en)</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 only match `abc*`",
+ "subject": "Example/P0427/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0427/1#_7cae05fedd48fa820a0ea915518cc3fd</td>",
+ "<td class=\"Has-text smwtype_txt\">abc</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 only match `ABC*`",
+ "subject": "Example/P0427/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0427/1#_6840005f556d165faf53ecf39be9ee97</td>",
+ "<td class=\"Has-text smwtype_txt\">ABC</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 same as #2",
+ "subject": "Example/P0427/Q1.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0427/1#_7cae05fedd48fa820a0ea915518cc3fd</td>",
+ "<td class=\"Has-text smwtype_txt\">abc</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 to be reverse to #2",
+ "subject": "Example/P0427/Q1.4",
+ "skip-on": {
+ "elastic": "ES matches both `AB C` and `ab c` which is why the result set is 0"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0427/1#_6840005f556d165faf53ecf39be9ee97</td>",
+ "<td class=\"Has-text smwtype_txt\">ABC</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 to be reverse to #3",
+ "subject": "Example/P0427/Q1.5",
+ "skip-on": {
+ "elastic": "ES matches both `AB C` and `ab c` which is why the result set is 0"
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0427/1#_7cae05fedd48fa820a0ea915518cc3fd</td>",
+ "<td class=\"Has-text smwtype_txt\">abc</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgRestrictDisplayTitle": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0428.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0428.json
new file mode 100644
index 00000000..7f087050
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0428.json
@@ -0,0 +1,47 @@
+{
+ "description": "Test `_TYPE` annotations on different content language (`wgContLang=fr`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[A le type::Texte]]"
+ },
+ {
+ "page": "Example/P0428/1",
+ "contents": "[[Has text::#-Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0428/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text"
+ ],
+ "propertyValues": [
+ "#-Foo"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "fr",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0429.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0429.json
new file mode 100644
index 00000000..e87534a3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0429.json
@@ -0,0 +1,189 @@
+{
+ "description": "Test in-text `_dat` annotation with time offset, time zone, am/pm (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0429/1",
+ "contents": "[[Has date::1 Jan 1971 13:45:23-3:30]]"
+ },
+ {
+ "page": "Example/P0429/Q.1",
+ "contents": "{{#show: Example/P0429/1 |?Has date }}"
+ },
+ {
+ "page": "Example/P0429/2",
+ "contents": "[[Has date::1 Jan 1971 13:45:23 EST]]"
+ },
+ {
+ "page": "Example/P0429/Q.2",
+ "contents": "{{#show: Example/P0429/2 |?Has date }} {{#show: Example/P0429/2 |?Has date#LOCL#TZ }}"
+ },
+ {
+ "page": "Example/P0429/3",
+ "contents": "[[Has date::1 Jan 1971 13:45:23 am]]"
+ },
+ {
+ "page": "Example/P0429/4",
+ "contents": "[[Has date::1 Jan 1971 5:05:23 pm]]"
+ },
+ {
+ "page": "Example/P0429/5",
+ "contents": "[[Has date::1 Jan 1971 13:45:23 America/New_York]]"
+ },
+ {
+ "page": "Example/P0429/Q.5.1",
+ "contents": "{{#show: Example/P0429/5 |?Has date#LOCL#TZ }}"
+ },
+ {
+ "page": "Example/P0429/Q.5.2",
+ "contents": "{{#show: Example/P0429/5 |?Has date#LOCL#TZ@ja }}"
+ },
+ {
+ "page": "Example/P0429/Q.5.3",
+ "contents": "{{#show: Example/P0429/5 |?Has date#LOCL@ja#TZ }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 timeoffset",
+ "subject": "Example/P0429/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_date"
+ ],
+ "propertyValues": [
+ "1971-01-01T16:15:23"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0429/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "1 January 1971 16:15:23"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 timezone",
+ "subject": "Example/P0429/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_date"
+ ],
+ "propertyValues": [
+ "1971-01-01T18:45:23"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0429/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "1 January 1971 18:45:23",
+ "13:45:23 EST, 1 January 1971"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 invalid am/pm",
+ "subject": "Example/P0429/3",
+ "assert-output": {
+ "to-contain": [
+ "&quot;1 Jan 1971 13:45:23 am&quot; contains &quot;13&quot; as hour element that is invalid for a 12-hour convention."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 valid am/pm",
+ "subject": "Example/P0429/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_date"
+ ],
+ "propertyValues": [
+ "1971-01-01T17:05:23"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 valid am/pm",
+ "subject": "Example/P0429/4",
+ "assert-output": {
+ "to-contain": [
+ "1 Jan 1971 5:05:23 pm"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 long timezone name",
+ "subject": "Example/P0429/Q.5.1",
+ "assert-output": {
+ "to-contain": [
+ "13:45:23 America/New_York, 1 January 1971"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 long timezone name",
+ "subject": "Example/P0429/Q.5.2",
+ "assert-output": {
+ "to-contain": [
+ "1971年1月1日 (金) 13:45:23 America/New_York"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 long timezone name",
+ "subject": "Example/P0429/Q.5.3",
+ "assert-output": {
+ "to-contain": [
+ "1971年1月1日 (金) 13:45:23 America/New_York"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0430.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0430.json
new file mode 100644
index 00000000..45ccdcc9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0430.json
@@ -0,0 +1,133 @@
+{
+ "description": "Test in-text annotation for `_eid` type (`#nowiki`) (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "NDL ID",
+ "contents": "[[Has type::External identifier]] [[External formatter uri::https://id.ndl.go.jp/auth/ndlna/$1]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Some ID",
+ "contents": "[[Has type::External identifier]] [[External formatter uri::https://example.org/$1]]"
+ },
+ {
+ "page": "Example/P0430/1",
+ "contents": "[[NDL ID::00564222]]"
+ },
+ {
+ "page": "Example/P0430/Q1.1",
+ "contents": "{{#ask: [[NDL ID::00564222]] |?NDL ID |link=none}}"
+ },
+ {
+ "page": "Example/P0430/Q1.2",
+ "contents": "{{#ask: [[NDL ID::00564222]] |?NDL ID }}"
+ },
+ {
+ "page": "Example/P0430/2",
+ "contents": "[[Some ID::W%D6LLEKLA01]]"
+ },
+ {
+ "page": "Example/P0430/Q2.1",
+ "contents": "{{#ask: [[Some ID::W%D6LLEKLA01]] |?Some ID }}"
+ },
+ {
+ "page": "Example/P0430/Q2.2",
+ "contents": "{{#ask: [[Some ID::W%D6LLEKLA01]] |?Some ID#nowiki }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0430/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "NDL_ID",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "00564222"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"plainlinks smw-eid\"><a rel=\"nofollow\" class=\"external text\" href=\"https://id.ndl.go.jp/auth/ndlna/00564222\">00564222</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (link=none)",
+ "subject": "Example/P0430/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"NDL-ID smwtype_eid\" data-sort-value=\"00564222\">00564222</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0430/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"NDL-ID smwtype_eid\" data-sort-value=\"00564222\"><span class=\"plainlinks smw-eid\"><a rel=\"nofollow\" class=\"external text\" href=\"https://id.ndl.go.jp/auth/ndlna/00564222\">00564222</a></span></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (decode,encode)",
+ "subject": "Example/P0430/2",
+ "assert-output": {
+ "to-contain": [
+ "<a rel=\"nofollow\" class=\"external text\" href=\"https://example.org/W%D6LLEKLA01\">W%D6LLEKLA01</a>"
+ ],
+ "not-contain": [
+ "<a rel=\"nofollow\" class=\"external text\" href=\"https://example.org/W%25D6LLEKLA01\">W%D6LLEKLA01</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0430/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"plainlinks smw-eid\"><a rel=\"nofollow\" class=\"external text\" href=\"https://example.org/W%D6LLEKLA01\">W%D6LLEKLA01</a></span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #nowiki",
+ "subject": "Example/P0430/Q2.2",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"plainlinks smw-eid\">https&#58;//example.org/W%D6LLEKLA01</span>"
+ ],
+ "not-contain": [
+ "<span class=\"plainlinks smw-eid\"><a rel=\"nofollow\" class=\"external text\" href=\"https://example.org/W%D6LLEKLA01\">W%D6LLEKLA01</a></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0431.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0431.json
new file mode 100644
index 00000000..e9c1c1f0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0431.json
@@ -0,0 +1,84 @@
+{
+ "description": "Test in-text annotation `_rec` and `|+index` (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]] [[Has property description::Test@en]]"
+ },
+ {
+ "page": "Example/P0431/1",
+ "contents": "[[Has record::Foo;123]]"
+ },
+ {
+ "page": "Example/P0431/Q1.1",
+ "contents": "{{#ask: [[Has record::+]] |?Has record|+index=Has text}}"
+ },
+ {
+ "page": "Example/P0431/Q1.2",
+ "contents": "{{#ask: [[Has record::+]] |?Has record|+index=Has number|?Has record|+index=Has text}}"
+ },
+ {
+ "page": "Example/P0431/Q2.1",
+ "contents": "{{#ask: [[Has property description::Test@en]] |?Has property description|+index=Language code}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0431/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0431/1",
+ "<td class=\"Has-record smwtype_txt\">Foo</td>"
+ ],
+ "not-contain": [
+ "<td class=\"Has-record smwtype_num\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0431/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0431/1",
+ "<td class=\"Has-record smwtype_txt\">Foo</td>",
+ "<td class=\"Has-record smwtype_num\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0431/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "Property:Has record",
+ "<td class=\"Property-description smwtype&#95;_lcode\">en</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0432.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0432.json
new file mode 100644
index 00000000..e5dcaf64
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0432.json
@@ -0,0 +1,186 @@
+{
+ "description": "Test in-text annotation for `_ref_rec` type (#1808, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number with ref",
+ "contents": "[[Has type::Reference]] [[Has fields::Number;Date;URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has mlt with ref",
+ "contents": "[[Has type::Reference]] [[Has fields::Monolingual text;Date;Number]]"
+ },
+ {
+ "page": "Example/P0432/1",
+ "contents": "[[Has number with ref::123;1.1.2000;http://example.org]]"
+ },
+ {
+ "page": "Example/P0432/Q1.1",
+ "contents": "{{#ask: [[Has number with ref::123]] |?Has number with ref|+index=Date|?Has number with ref|+index=URL |link=none}}"
+ },
+ {
+ "page": "Example/P0432/2",
+ "contents": "{{#subobject: |Has number with ref=456;12-04-1637;http://example.org }}"
+ },
+ {
+ "page": "Example/P0432/Q2.1",
+ "contents": "{{#ask: [[Has number with ref::456]] |?Has number with ref|+index=Date|?Has number with ref|+index=URL |link=none}}"
+ },
+ {
+ "page": "Example/P0432/3",
+ "contents": "[[Has mlt with ref::Test@en;01.01.1800;123]] {{#subobject: |Has mlt with ref::テスト@ja;01.01.1800;456 }}"
+ },
+ {
+ "page": "Example/P0432/Q3.1",
+ "contents": "{{#ask: [[Has mlt with ref::?@en]] |?Has mlt with ref|+index=Monolingual text|?Has mlt with ref|+index=Number |link=none}}"
+ },
+ {
+ "page": "Example/P0432/4",
+ "contents": "[[Has number with ref:: {{#show: Example/P0432/1 |?Has number with ref |link=none }}]]"
+ },
+ {
+ "page": "Example/P0432/5",
+ "contents": "{{#subobject: Has number with ref=999;1.1.2000 12:00;http://example.org}} {{#subobject: Has number with ref=999;1.1.2000 13:00;http://example.org}}"
+ },
+ {
+ "page": "Example/P0432/Q5.1",
+ "contents": "{{#ask: [[Has number with ref::999]] |order=asc |link=none |format=list}}"
+ },
+ {
+ "page": "Example/P0432/Q5.2",
+ "contents": "{{#ask: [[Has number with ref::999]] |order=desc |link=none |format=list}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0432/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_number_with_ref",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "123;1 January 2000;http://example.org"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "123<span class=\"smw-reference smw-reference-indicator smw-highlighter smwttinline\" data-title=\"Reference\"",
+ "January 1, 2000",
+ "href=&quot;http://example.org&quot;&gt;http://example.org",
+ "title=\"Date: January 1, 2000, URL: http://example.org\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0432/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0432/1</td>",
+ "<td class=\"Has-number-with-ref smwtype_dat\" data-sort-value=\"2451544.5\">1 January 2000</td>",
+ "<td class=\"Has-number-with-ref smwtype_uri\"><a rel=\"nofollow\" class=\"external free\" href=\"http://example.org\">http://example.org</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0432/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0432/2#_7b5db4a4195b1843f9a25639c0d7ff5c</td>",
+ "<td class=\"Has-number-with-ref smwtype_dat\" data-sort-value=\"2319299.5\">4 December 1637</td>",
+ "<td class=\"Has-number-with-ref smwtype_uri\"><a rel=\"nofollow\" class=\"external free\" href=\"http://example.org\">http://example.org</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (Monolingual text, number index output)",
+ "subject": "Example/P0432/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0432/3</td>",
+ "<td class=\"Has-mlt-with-ref smwtype_mlt_rec\">Test (en)</td>",
+ "<td class=\"Has-mlt-with-ref smwtype_num\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (#show + link=none)",
+ "subject": "Example/P0432/4",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "Has_number_with_ref",
+ "_SKEY",
+ "_MDAT",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "123;?;?"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "123<span class=\"smw-reference smw-reference-indicator smw-highlighter smwttinline\" data-title=\"Reference\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (chronological order, asc)",
+ "subject": "Example/P0432/Q5.1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Example/P0432/5#_1c26bb9aac2df0c73a22cacd64a2b56a<.*>Example/P0432/5#_f3d26f55b0c2b1edf58613dd875ba0e5<"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (chronological order, desc)",
+ "subject": "Example/P0432/Q5.2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Example/P0432/5#_f3d26f55b0c2b1edf58613dd875ba0e5<.*>Example/P0432/5#_1c26bb9aac2df0c73a22cacd64a2b56a<"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0433.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0433.json
new file mode 100644
index 00000000..f8fa2e76
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0433.json
@@ -0,0 +1,87 @@
+{
+ "description": "Test in-text annotation `::` with left pipe (#1747, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0433/1",
+ "contents": "[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]"
+ },
+ {
+ "page": "Example/P0433/2",
+ "contents": "{{#set:Has text=[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]] }}"
+ },
+ {
+ "page": "Example/P0433/Q.1",
+ "contents": "{{#ask: [[Example/P0433/2]] |?Has text |link=none}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 no annotation due to left pipe",
+ "subject": "Example/P0433/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_INST"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0433/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text"
+ ],
+ "propertyValues": [
+ "[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0433/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"File:Example.png\">Caption</a>",
+ "title=\"File:Example.png\">Bar::Foobar</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0434.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0434.json
new file mode 100644
index 00000000..eb9c75b1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0434.json
@@ -0,0 +1,215 @@
+{
+ "description": "Test printrequest property chaining `|?Foo.Bar` (#1824, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has population",
+ "contents": "[[Has type::Reference]] [[Has fields::Number;Date;URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Capital of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0434/1",
+ "contents": "[[Has population::123;1 Jan 1970;http://example.org/SomeSource]] {{#subobject:Has population=456;1 Jan 2000;http://example.org/SomeSource }} [[Category:P0434]]"
+ },
+ {
+ "page": "Example/P0434/Q1.1",
+ "contents": "{{#ask: [[Has population::+]] [[Category:P0434]] |?Has population.Number |link=none}} {{#ask: [[Has population::+]] [[Category:P0434]] |?Has population.Number |link=none |format=debug}}"
+ },
+ {
+ "page": "Example/P0434/Q1.2",
+ "contents": "{{#ask: [[Has population::+]] [[Category:P0434]] |?Has population.Date |link=none}}"
+ },
+ {
+ "page": "Example/P0434/2",
+ "contents": "[[Capital of::Example/P0434/1]] [[Located in::Example/P0434/3]] [[Category:City]] [[Category:P0434]]"
+ },
+ {
+ "page": "Example/P0434/Q2.1",
+ "contents": "{{#ask: [[Category:City]] [[Category:P0434]] |?Capital of.Has population.Number |link=none}}"
+ },
+ {
+ "page": "Example/P0434/3",
+ "contents": "[[Located in::Example/P0434/4]] [[Category:Country]] [[Category:P0434]]"
+ },
+ {
+ "page": "Example/P0434/Q3.1",
+ "contents": "{{#ask: [[Category:Country]] [[Category:P0434]] |?Located in.-Located in.-Located in.Capital of |link=subject }}"
+ },
+ {
+ "page": "Example/P0434/Q3.2",
+ "contents": "{{#ask: [[Category:Country]] [[Category:P0434]] |?Located in.-Located in.-Located in.Capital of=SomeOtherText |link=none }}"
+ },
+ {
+ "page": "Example/P0434/Q3.3",
+ "contents": "{{#ask: [[Category:Country]] [[Category:P0434]] |?Located in.-Located in.-Located in.Capital of.Has subobject.Has population.Number |link=none }}"
+ },
+ {
+ "page": "Example/P0434/4",
+ "contents": "{{#subobject:Has population=123;1 Jan 2000;http://example.org/SomeSource |@category=P0434-sort }} {{#subobject:Has population=456;1 Jan 1900;http://example.org/SomeSource |@category=P0434-sort}} {{#subobject:Has population=789;1 Jan 1999;http://example.org/SomeSource |@category=P0434-sort}}"
+ },
+ {
+ "page": "Example/P0434/Q4.1",
+ "contents": "{{#ask: [[Category:P0434-sort]] |?Has population.Date |sort=Has population.Date |order=asc |link=none}}"
+ },
+ {
+ "page": "Example/P0434/Q4.2",
+ "contents": "{{#ask: [[Category:P0434-sort]] |?Has population.Date |sort=Has population.Date |order=desc |link=none}}"
+ },
+ {
+ "page": "Example/P0434/Q4.3",
+ "contents": "{{#ask: [[Category:P0434-sort]] |?Has population.Date |sort=Has population.Date,Has population.Number |order=asc,desc |link=none}}"
+ },
+ {
+ "page": "Example/P0434/Q4.4",
+ "contents": "{{#ask: [[Category:P0434-sort]] |?Has population.Date |sort=Has population.Date,Has population.Number |order=desc,asc |link=none}}"
+ }
+
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0434/Q1.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0434/1</td><td class=\"Number smwtype_num\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31-)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0434/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0434/1</td><td class=\"Date smwtype_dat\" data-sort-value=\"2440587.5\">1 January 1970</td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31+)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0434/Q1.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0434/1</td><td class=\"Date smwtype_dat\" data-sort-value=\"2440587.5\">1 January 1970</td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 on (?Capital of.Has population.Number)",
+ "subject": "Example/P0434/Q2.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0434/2</td><td class=\"Number smwtype_num\" data-sort-value=\"123\">123</td>"
+ ],
+ "not-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0434/1</td><td class=\"Number smwtype_num\" data-sort-value=\"123\">123</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 on (?Located in.-Located in.-Located in.Capital of)",
+ "subject": "Example/P0434/Q3.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Capital-of smwtype_wpg\">Example/P0434/1</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 on (?Located in.-Located in.-Located in.Capital of=SomeOtherText)",
+ "subject": "Example/P0434/Q3.2",
+ "assert-output": {
+ "to-contain": [
+ "SomeOtherText</a>&#160;<span title=\"Located in.-Located in.-Located in.Capital of\">â ‰</span>",
+ "<td class=\"SomeOtherText smwtype_wpg\">Example/P0434/1</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 on (?Located in.-Located in.-Located in.Capital of.Has subobject.Has population.Number)",
+ "subject": "Example/P0434/Q3.3",
+ "assert-output": {
+ "to-contain": [
+ "<span title=\"Located in.-Located in.-Located in.Capital of.Has subobject.Has population.Number\">â ‰</span>",
+ "<td class=\"smwtype_wpg\">Example/P0434/3</td><td class=\"Number smwtype_num\" data-sort-value=\"456\">456</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (sort=Has population.Date, order=asc) ",
+ "subject": "Example/P0434/Q4.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_357f4a06073b86afc07cb0c6237b9f36</td><td class=\"Date smwtype_dat\" data-sort-value=\"2415020.5\">1 January 1900</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0434/4#_eb1c9a9f3a8ad677d7424e8d842b10dd</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451179.5\">1 January 1999</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_4bae31c0cd5bda46c3cf0245523a954a</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451544.5\">1 January 2000</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (sort=Has population.Date, order=desc) ",
+ "subject": "Example/P0434/Q4.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_4bae31c0cd5bda46c3cf0245523a954a</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451544.5\">1 January 2000</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0434/4#_eb1c9a9f3a8ad677d7424e8d842b10dd</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451179.5\">1 January 1999</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_357f4a06073b86afc07cb0c6237b9f36</td><td class=\"Date smwtype_dat\" data-sort-value=\"2415020.5\">1 January 1900</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (Has population.Date,Has population.Number, order=asc,desc) ",
+ "subject": "Example/P0434/Q4.3",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_357f4a06073b86afc07cb0c6237b9f36</td><td class=\"Date smwtype_dat\" data-sort-value=\"2415020.5\">1 January 1900</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0434/4#_eb1c9a9f3a8ad677d7424e8d842b10dd</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451179.5\">1 January 1999</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_4bae31c0cd5bda46c3cf0245523a954a</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451544.5\">1 January 2000</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 (Has population.Date,Has population.Number, order=desc,asc) ",
+ "subject": "Example/P0434/Q4.4",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_4bae31c0cd5bda46c3cf0245523a954a</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451544.5\">1 January 2000</td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\">Example/P0434/4#_eb1c9a9f3a8ad677d7424e8d842b10dd</td><td class=\"Date smwtype_dat\" data-sort-value=\"2451179.5\">1 January 1999</td></tr>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0434/4#_357f4a06073b86afc07cb0c6237b9f36</td><td class=\"Date smwtype_dat\" data-sort-value=\"2415020.5\">1 January 1900</td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0435.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0435.json
new file mode 100644
index 00000000..60132ae3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0435.json
@@ -0,0 +1,66 @@
+{
+ "description": "Test in-text annotation using `_txt` type with 255+ char, `#ask` to produce reduced length (#1878, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0435/1",
+ "contents": "[[Has text::TBgcHiQBTvpV3XkFiRpMcV1KQoZvQXFyiQUQMM2RoYUyJaRoSyKnsQTWwia3HATd4YVmd8BZgkL2LfL0Q6rP5E90A\\4IuV6u/KdYjL3nxZvx0pbc3tbxwa6jMW1JDNxusaKQ52ftRS7DCEY1IPTRZnuRMLPgWYwLOsEAebOsxD7BBL9IK3Z2Osfh9s0FC1SUwCVdKcLkBgurXKGi99s61qnAU2zWFXBUCEzID6533LeaHCBKW8i2BgTK2tv65LHO6zB:;%&`^WithTextLongerThan255]]"
+ },
+ {
+ "page": "Example/P0435/Q.1",
+ "contents": "{{#ask: [[Example/P0435/1]] |?Has text#20 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0435/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_text",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "TBgcHiQBTvpV3XkFiRpMcV1KQoZvQXFyiQUQMM2RoYUyJaRoSyKnsQTWwia3HATd4YVmd8BZgkL2LfL0Q6rP5E90A\\4IuV6u/KdYjL3nxZvx0pbc3tbxwa6jMW1JDNxusaKQ52ftRS7DCEY1IPTRZnuRMLPgWYwLOsEAebOsxD7BBL9IK3Z2Osfh9s0FC1SUwCVdKcLkBgurXKGi99s61qnAU2zWFXBUCEzID6533LeaHCBKW8i2BgTK2tv65LHO6zB:;%&`^WithTextLongerThan255"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "TBgcHiQBTvpV3XkFiRpMcV1KQoZvQXFyiQUQMM2RoYUyJaRoSyKnsQTWwia3HATd4YVmd8BZgkL2LfL0Q6rP5E90A\\4IuV6u/KdYjL3nxZvx0pbc3tbxwa6jMW1JDNxusaKQ52ftRS7DCEY1IPTRZnuRMLPgWYwLOsEAebOsxD7BBL9IK3Z2Osfh9s0FC1SUwCVdKcLkBgurXKGi99s61qnAU2zWFXBUCEzID6533LeaHCBKW8i2BgTK2tv65LHO6zB:;%&amp;`^WithTextLongerThan255"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0435/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">TBgcHiQBTvpV3XkFiRpM …</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0436.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0436.json
new file mode 100644
index 00000000..b591c343
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0436.json
@@ -0,0 +1,114 @@
+{
+ "description": "Test in-text annotation with `_PPLB` [preferred property label] (#1879, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "P106",
+ "contents": {
+ "import-from": "/../Fixtures/P106.txt"
+ }
+ },
+ {
+ "page": "Example/P0436/1",
+ "contents": "{{#subobject: |P106=Teacher }}{{#subobject: |P106=Actor }}"
+ },
+ {
+ "page": "Example/P0436/Q.1",
+ "contents": "{{#ask: [[P106::+]] |?P106 }}"
+ },
+ {
+ "page": "Example/P0436/Q.2",
+ "contents": "{{#ask: [[P106::+]] |?P106 |headers=plain }}"
+ },
+ {
+ "page": "Example/P0436/Q.3",
+ "contents": "{{#ask: [[P106::+]] |?P106=with a different Label |headers=plain }}"
+ },
+ {
+ "page": "Example/P0436/Q.4",
+ "contents": "{{#ask: [[P106::+]] |?P106=with a different Label }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "P106",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 6,
+ "propertyKeys": [
+ "_PPLB",
+ "_PDESC",
+ "_TYPE",
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#0 (en)",
+ "subject": "Example/P0436/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "data-title=\"Property\" title=\"occupation of a person; see also field of work (Property:P101)\">",
+ "title=\"Property:P106\">occupation</a>",
+ "<span class=\"smwttcontent\">occupation of a person; see also field of work (Property:P101)</span></span>&#160;<span title=\"P106\"><sup>áµ–</sup></span>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (en, headers=plain)",
+ "subject": "Example/P0436/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"occupation\">occupation</th>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (en, headers=plain, different label)",
+ "subject": "Example/P0436/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"with-a-different-Label\">with a different Label</th>"
+ ],
+ "not-contain": [
+ "<th class=\"occupation\">occupation</th>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (en, different label, doesn't contain a prefLabel marker)",
+ "subject": "Example/P0436/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"with-a-different-Label\">",
+ "title=\"Property:P106\">with a different Label</a>",
+ "<span class=\"smwttcontent\">occupation of a person; see also field of work (Property:P101)</span></span>"
+ ],
+ "not-contain": [
+ "<th class=\"occupation\">occupation</th>",
+ "&#160;<span title=\"P106\"><sup>áµ–</sup></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0437.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0437.json
new file mode 100644
index 00000000..9f5588df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0437.json
@@ -0,0 +1,42 @@
+{
+ "description": "Test in-text annotation with preferred property label/`_PPLB` (#1879, `wgContLang=en`, `wgLang=ja`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "P106",
+ "contents": {
+ "import-from": "/../Fixtures/P106.txt"
+ }
+ },
+ {
+ "page": "Example/P0437/1",
+ "contents": "{{#subobject: |P106=Teacher }}{{#subobject: |P106=Actor }}"
+ },
+ {
+ "page": "Example/P0437/Q.1",
+ "contents": "{{#ask: [[P106::+]] |?P106 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (ja)",
+ "subject": "Example/P0437/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "data-title=\"プロパティ\" title=\"人物ã®è·æ¥­ã€‚「専門分野ã€(Property:P101) ã‚‚å‚ç…§\">",
+ "title=\"Property:P106\">è·æ¥­</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "ja"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0438.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0438.json
new file mode 100644
index 00000000..38918bc2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0438.json
@@ -0,0 +1,61 @@
+{
+ "description": "Test in-text annotation with preferred property label/DISPLAYTITLE on user/predefined properties (`wgContLang=es`, `wgLang=de`, `wgRestrictDisplayTitle=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Modification date",
+ "contents": "[[Has preferred property label::prefLabel-123@de]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has query",
+ "contents": "{{DISPLAYTITLE:query-displaytitle}}"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has keyword",
+ "contents": "[[Has preferred property label::prefLabel-456@de]]"
+ },
+ {
+ "page": "Example/P0438/1",
+ "contents": "[[Category:P0438]] [[Has keyword::some keyword]]"
+ },
+ {
+ "page": "Example/P0438/2",
+ "contents": "[[Category:P0438]] [[Has keyword::another keyword]]"
+ },
+ {
+ "page": "Example/P0438/Q.1",
+ "contents": "{{#ask: [[Category:P0438]] |?Modification date |?Modification date=ModificationSomeLabel |?Has keyword |?Has keyword=KeywordCaption |?Has query |?Has query=QueryCaption |limit=1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (combination of prefLabel, DISPLAYTITLE, local caption)",
+ "subject": "Example/P0438/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Modification date\">prefLabel-123</a></span><span class=\"smwttcontent\">„prefLabel-123&#160;<span style=\"font-size:small;\">(Modification date)</span>“",
+ "title=\"Property:Modification date\">ModificationSomeLabel</a></span><span class=\"smwttcontent\">„prefLabel-123&#160;<span style=\"font-size:small;\">(Modification date)</span>“",
+ "&#160;<span title=\"Modification date\"><sup>áµ–</sup></span>",
+ "title=\"Property:Has keyword\">prefLabel-456</a>&#160;<span title=\"Has keyword\"><sup>áµ–</sup>",
+ "title=\"Property:Has keyword\">KeywordCaption</a>",
+ "title=\"Property:Has query\">query-displaytitle</a></span><span class=\"smwttcontent\">„query-displaytitle&#160;<span style=\"font-size:small;\">(Has query)</span>“",
+ "title=\"Property:Has query\">QueryCaption</a></span><span class=\"smwttcontent\">„query-displaytitle&#160;<span style=\"font-size:small;\">(Has query)</span>“",
+ ":Ask/-5B-5BCategoría:P0438-5D-5D/-3FModification-20date/-3FModification-20date=ModificationSomeLabel/-3FHas-20keyword/-3FHas-20keyword=KeywordCaption/-3FHas-20query/-3FHas-20query=QueryCaption/mainlabel=/limit=1/offset=1/format=table\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "es",
+ "wgLang": "de",
+ "wgRestrictDisplayTitle": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0439.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0439.json
new file mode 100644
index 00000000..311217e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0439.json
@@ -0,0 +1,122 @@
+{
+ "description": "Test in-text annotation using '_txt'/'_wpg' type / UTF encoding (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0439/1",
+ "contents": "[[Has text::ö/ä/ü]]"
+ },
+ {
+ "page": "Example/P0439/2",
+ "contents": {
+ "import-from": "/../Fixtures/p-0439.de.txt"
+ }
+ },
+ {
+ "page": "Example/P0439/3",
+ "contents": {
+ "import-from": "/../Fixtures/p-0439.fr.txt"
+ }
+ },
+ {
+ "page": "Example/P0439/ö/ä/ü",
+ "contents": "[[Has page::Some ö/ä/ü]]"
+ },
+ {
+ "page": "Example/P0439/Q.1",
+ "contents": "{{#ask: [[Has text::ö/ä/ü]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0439/Q.2",
+ "contents": "{{#ask: [[Example/P0439/2]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0439/Q.3",
+ "contents": "{{#ask: [[Example/P0439/3]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0439/Q.4",
+ "contents": "{{#ask: [[Has page::Some ö/ä/ü]] |?Has page }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0439/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_text",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "ö/ä/ü"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0439/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">ö/ä/ü</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (long text, de)",
+ "subject": "Example/P0439/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "fügte, während ihr Gesicht sich aufklärte"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (long text, fr)",
+ "subject": "Example/P0439/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "nous avons annoncé le chef-d'oeuvre du poëte"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0439/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0439/ö/ä/ü\">Example/P0439/ö/ä/ü</a>",
+ "Some ö/ä/ü"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0440.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0440.json
new file mode 100644
index 00000000..b2769e58
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0440.json
@@ -0,0 +1,187 @@
+{
+ "description": "Test in-text annotation `_mlt_rec` (Monolingual text) with `|+lang`/`|+order` parameter (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has alternative label",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "SomeProperty",
+ "contents": " [[Category:P0440]] [[Has property description::Something ...@en]] [[Has property description::何ã‹ã‚ã‚‹ã‚‚ã®ã€‚。。@ja]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "AnotherProperty",
+ "contents": " [[Category:P0440]] [[Has property description::Something else ...@en]] [[Has property description::ä»–ã®ä½•ã‹ã‚ã‚‹ã‚‚ã®ã€‚。。@ja]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has alternative record",
+ "contents": "[[Has type::Record]] [[Has fields::Text;Number]]"
+ },
+ {
+ "page": "Example/P0440/Triacylglycerol lipase",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Has alternative record::ABC;123]] [[Has alternative label::Lipase@en]], [[Has alternative label::Tributyrase@en]], [[Has alternative label::Triglyceride lipase@en]], [[Has alternative label::トリアシルグリセロールリパーゼ@ja]], [[Has alternative label::ليباز ثلاثي اسيل الغليسيرول@ar]], [[Has alternative label::Triacylglycérol lipase@fr]], [[Has alternative label::Triacilglicerol lipaza@sh]], [[Has alternative label::Triacilglicerol lipaza@sr]]"
+ },
+ {
+ "page": "Example/P0440/Q.0",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+order=desc }}"
+ },
+ {
+ "page": "Example/P0440/Q.1",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+order=asc }}"
+ },
+ {
+ "page": "Example/P0440/Q.2",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+lang=en|+order=asc }}"
+ },
+ {
+ "page": "Example/P0440/Q.3",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+lang=en|+limit=1|+order=asc }}"
+ },
+ {
+ "page": "Example/P0440/Q.4",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+lang=en|+limit=1 |limit=0 }}"
+ },
+ {
+ "page": "Example/P0440/Q.5",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has alternative label|+lang=foo|+order=asc }}"
+ },
+ {
+ "page": "Example/P0440/Q.6",
+ "contents": "{{#ask: [[Has alternative label::+]] |?Has page|+lang=en }}"
+ },
+ {
+ "page": "Example/P0440/Q.7",
+ "contents": "{{#ask: [[Has alternative record::+]] |?Has alternative record|+lang=en }}"
+ },
+ {
+ "page": "Example/P0440/Q.8",
+ "contents": "{{#ask: [[Category:P0440]] [[Has property description::+]] |?Has property description|+lang=en }}"
+ },
+ {
+ "page": "Example/P0440/Q.9",
+ "contents": "{{#ask: [[Category:P0440]] [[Has property description::+]] |?Has property description|+lang=en |?Has property description|+lang=ja |limit=1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (no filter, lang tag, desc)",
+ "subject": "Example/P0440/Q.0",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-alternative-label smwtype_mlt_rec\">トリアシルグリセロールリパーゼ (ja)<br />ليباز ثلاثي اسيل الغليسيرول (ar)<br />Triglyceride lipase (en)<br />Tributyrase (en)<br />Triacylglycérol lipase (fr)<br />Triacilglicerol lipaza (sr)<br />Triacilglicerol lipaza (sh)<br />Lipase (en)</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (no filter, lang tag, asc)",
+ "subject": "Example/P0440/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-alternative-label smwtype_mlt_rec\">Lipase (en)<br />Triacilglicerol lipaza (sh)<br />Triacilglicerol lipaza (sr)<br />Triacylglycérol lipase (fr)<br />Tributyrase (en)<br />Triglyceride lipase (en)<br />ليباز ثلاثي اسيل الغليسيرول (ar)<br />トリアシルグリセロールリパーゼ (ja)</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (filtered by language, no lang tag)",
+ "subject": "Example/P0440/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-alternative-label smwtype_txt\">Lipase<br />Tributyrase<br />Triglyceride lipase</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (column value list limited to 1)",
+ "subject": "Example/P0440/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-alternative-label smwtype_txt\">Lipase</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (no result, Special:Ask link)",
+ "subject": "Example/P0440/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BHas-20alternative-20label::+-5D-5D/-3FHas-20alternative-20label-7C+lang=en-7C+limit=1/mainlabel=/offset=0/format=table"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (invalid/unknown/unmatchable language)",
+ "subject": "Example/P0440/Q.5",
+ "assert-output": {
+ "not-contain": [
+ "<td class=\"Has-alternative-label smwtype_txt\">Lipase<br />Tributyrase<br />Triglyceride lipase</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (+lang not applied on non-Monolingual type)",
+ "subject": "Example/P0440/Q.6",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0440/Triacylglycerol lipase\">Example/P0440/Triacylglycerol lipase</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (+lang not applied on non-Monolingual type)",
+ "subject": "Example/P0440/Q.7",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0440/Triacylglycerol lipase\">Example/P0440/Triacylglycerol lipase</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8",
+ "subject": "Example/P0440/Q.8",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Property-description smwtype_txt\">Something else ...</td>",
+ "<td class=\"Property-description smwtype_txt\">Something ...</td>"
+ ],
+ "not-contain": [
+ "<td class=\"Property-description smwtype_txt\">Something ...<br />何ã‹ã‚ã‚‹ã‚‚ã®ã€‚。。</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#9 (no `|+index=...` in further result links)",
+ "subject": "Example/P0440/Q.9",
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory:P0440-5D-5D-20-5B-5BProperty-20description::+-5D-5D/-3FProperty-20description-7C+lang=en/-3FProperty-20description-7C+lang=ja/mainlabel=/limit=1/offset=1/format=table"
+ ],
+ "not-contain": [
+ "-5B-5BCategory:P0440-5D-5D-20-5B-5BProperty-20description::+-5D-5D/-3FProperty-20description-7C+lang=en-7C+index=1/-3FProperty-20description-7C+lang=ja-7C+index=1/mainlabel=/limit=1/offset=1/format=table"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0441.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0441.json
new file mode 100644
index 00000000..2d80061b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0441.json
@@ -0,0 +1,47 @@
+{
+ "description": "Test in-text `_txt` 00 string/loose comparison (#2061)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0441/1",
+ "contents": "[[Has text::011]] [[Has text::0011]]"
+ },
+ {
+ "page": "Example/P0441/1",
+ "contents": "[[Has text::011]] [[Has text::00011]]"
+ },
+ {
+ "page": "Example/P0441/2",
+ "contents": "{{#ask: [[Example/P0441/1]] |?Has text }}",
+ "message-cache": "clear"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0441/2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\" data-sort-value=\"011\">011<br />00011</td>"
+ ],
+ "no-contain": [
+ "<td class=\"Has-text smwtype_txt\" data-sort-value=\"011\">011</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0442.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0442.json
new file mode 100644
index 00000000..87edf58b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0442.json
@@ -0,0 +1,50 @@
+{
+ "description": "Test in-text `#REDIRECT` to verify target subobject isn't removed (#, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]] [[Has property description::Some text that should remain@en]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has another text",
+ "contents": "#REDIRECT [[Property:Has text]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (verify redirect doesn't delete the subobjects of a target; if it fails then propertyValues will be empty and cause an error in this test)",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has text",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_PDESC",
+ "_TYPE"
+ ],
+ "propertyValues": [
+ "Some text that should remain@en"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0443.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0443.json
new file mode 100644
index 00000000..b7897824
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0443.json
@@ -0,0 +1,198 @@
+{
+ "description": "Test conditions and strict constraint validations for uniqueness `_PVUC` on `_txt`/`_rec`/`_ref_rec` with unique field (#1463, #3547, `smwgDVFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has uniqueness one",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has uniqueness two",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Unique field",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Another unique field",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Unique record",
+ "contents": "[[Has type::Record]] [[Has fields::Unique field;Non unique field]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Unique reference",
+ "contents": "[[Has type::Reference]] [[Has fields::Unique field;Another unique field;Non unique field]]"
+ },
+ {
+ "page": "Example/P0443/1",
+ "contents": "[[Has uniqueness one::Allowed one]] [[Has uniqueness one::Not permitted]] [[Has uniqueness two::Allowed two]] [[Has uniqueness two::Not permitted]]"
+ },
+ {
+ "page": "Example/P0443/2",
+ "contents": "[[Has uniqueness one::1111]] {{#ask: [[Has uniqueness one::1111]] |link=none |format=plainlist}}"
+ },
+ {
+ "page": "Example/P0443/3",
+ "contents": "[[Unique record::abc;123]]"
+ },
+ {
+ "page": "Example/P0443/4",
+ "contents": "[[Unique record::abc;123]] (fails on abc)"
+ },
+ {
+ "page": "Example/P0443/5",
+ "contents": "[[Unique reference::abc;def;123]] (fails on abc)"
+ },
+ {
+ "page": "Example/P0443/6",
+ "contents": "[[Unique reference::abcd;def;123]]"
+ },
+ {
+ "page": "Example/P0443/7",
+ "contents": "[[Unique reference::abcde;def;123]] (fails on def)"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (verify uniqueness for only one assignment per property)",
+ "subject": "Example/P0443/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ERRC",
+ "Has uniqueness one",
+ "Has uniqueness two"
+ ],
+ "propertyValues": [
+ "Allowed one",
+ "Allowed two"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (verify declared unique value doesn't interfere with #ask within the same page)",
+ "subject": "Example/P0443/2",
+ "assert-output": {
+ "to-contain": [
+ "<p>1111 Example/P0443/2"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (verify uniqueness of field in record )",
+ "subject": "Example/P0443/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "Unique record"
+ ],
+ "propertyValues": [
+ "abc; 123"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (verify uniqueness of field in record fails for same value as used in Example/P0443/3)",
+ "subject": "Example/P0443/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (verify uniqueness of field in reference fails for same value as used in Example/P0443/3)",
+ "subject": "Example/P0443/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (verify uniqueness for reference with uniqueness fields)",
+ "subject": "Example/P0443/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "Unique reference"
+ ],
+ "propertyValues": [
+ "abcd"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (verify uniqueness of field in reference fails for same value as used in Example/P0443/6)",
+ "subject": "Example/P0443/7",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ERRC"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "smwgDVFeatures": [
+ "SMW_DV_PVUC"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0444.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0444.json
new file mode 100644
index 00000000..502f2542
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0444.json
@@ -0,0 +1,207 @@
+{
+ "description": "Test in-text annotation with links in values (#2153, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0444/1",
+ "contents": {
+ "import-from": "/../Fixtures/p-0444.txt"
+ }
+ },
+ {
+ "page": "Example/P0444/2",
+ "contents": "[[Has text::[[Lorem ipsum]][[Lorem ipsum|Bar]]]]"
+ },
+ {
+ "page": "Example/P0444/3",
+ "contents": "[[Has text::[http://example.org/eleifend eleifend]]]"
+ },
+ {
+ "page": "Example/P0444/4",
+ "contents": "[[Has text::[[Has number::42|1001]]]] [[Category:P0444]]"
+ },
+ {
+ "page": "Example/P0444/Q.1",
+ "contents": "{{#ask: [[Category:P0444]] |format=embedded }}"
+ },
+ {
+ "page": "Example/P0444/5",
+ "contents": "[[Has text::[[:Lorem ipsum]] [[:Lorem ipsum|Bar]]]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 import",
+ "subject": "Example/P0444/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 6,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has page",
+ "Has number",
+ "Has date",
+ "Has text"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 internal wiki link",
+ "subject": "Example/P0444/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "[[Lorem ipsum]][[Lorem ipsum|Bar]]"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Lorem ipsum</a>",
+ ">Bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 external link",
+ "subject": "Example/P0444/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "[http://example.org/eleifend eleifend]"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">eleifend</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 link-in annotation (piped hence annotated 42, displayed 1001)",
+ "subject": "Example/P0444/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_INST",
+ "Has text",
+ "Has number"
+ ],
+ "propertyValues": [
+ "42"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "1001"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 embbeded format to verify that [[SMW::OFF]]/[[SMW:ON]] remains intact after transclusion",
+ "subject": "Example/P0444/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ASK"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 internal wiki link (`[[:...`)",
+ "subject": "Example/P0444/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "[[:Lorem ipsum]] [[:Lorem ipsum|Bar]]"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Lorem ipsum</a>",
+ ">Bar</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS",
+ "SMW_PARSER_LINV"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0445.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0445.json
new file mode 100644
index 00000000..9b8f5884
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0445.json
@@ -0,0 +1,63 @@
+{
+ "description": "Test in-text annotation for `_ref_rec` type with errors (#..., `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number reference",
+ "contents": "[[Has type::Reference]] [[Has fields::Number;Date]]"
+ },
+ {
+ "page": "Example/P0445/1",
+ "contents": "[[Has number reference::Foo;1 Jan 1970]]"
+ },
+ {
+ "page": "Example/P0445/2",
+ "contents": "[[Has number reference::123;Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 error on number element (type mismatch)",
+ "subject": "Example/P0445/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 error on date element (type mismatch)",
+ "subject": "Example/P0445/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0446.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0446.json
new file mode 100644
index 00000000..0820df6f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0446.json
@@ -0,0 +1,257 @@
+{
+ "description": "Test in-text annotation `_uri`/`_ema`/`_tel` with spaces/underscore (`wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has telephone number",
+ "contents": "[[Has type::Telephone number]]"
+ },
+ {
+ "page": "Example/P0446/1",
+ "contents": "[[Has url::http://example.org/Foo bar]] [[Has url::http://example.org/Foo%20bar]] [[Has url::http://example.org/Foo_bar]] [[Category:P0446]]"
+ },
+ {
+ "page": "Example/P0446/Q.1.0",
+ "contents": "{{#ask: [[Has url::http://example.org/Foo bar]] [[Category:P0446]] |?Has url }}"
+ },
+ {
+ "page": "Example/P0446/Q.1.1",
+ "contents": "{{#ask: [[Has url::http://example.org/Foo bar]] [[Category:P0446]] |?Has url |link=none }}"
+ },
+ {
+ "page": "Example/P0446/2",
+ "contents": "[[Has url::http://example.org/Foo bar|Foo bar]] [[Has url::http://example.org/Foo_bar|Foo bar]] __SHOWFACTBOX__"
+ },
+ {
+ "page": "Example/P0446/3",
+ "contents": "[[Email::john_doe@example.org]]"
+ },
+ {
+ "page": "Example/P0446/Q.3",
+ "contents": "{{#ask: [[Email::john_doe@example.org]] |?Email }}"
+ },
+ {
+ "page": "Example/P0446/4",
+ "contents": "[[Has telephone number::+1 201 555 5555]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 spaces (_, , %20) produce only one URL not three",
+ "subject": "Example/P0446/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_INST",
+ "Has url"
+ ],
+ "propertyValues": [
+ "http://example.org/Foo_bar"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "href=\"http://example.org/Foo_bar\">http://example.org/Foo bar</a>",
+ "href=\"http://example.org/Foo_bar\">http://example.org/Foo%20bar</a>",
+ "href=\"http://example.org/Foo_bar\">http://example.org/Foo bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 different space encoding dont't disort the condition/result",
+ "condition": "[[Has url::http://example.org/Foo bar]] [[Category:P0446]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Example/P0446/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/Foo_bar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (same as #1)",
+ "condition": "[[Has url::http://example.org/Foo_bar]] [[Category:P0446]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Example/P0446/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/Foo_bar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (same as #1)",
+ "condition": "[[Has url::http://example.org/Foo%20bar]] [[Category:P0446]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Example/P0446/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/Foo_bar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #ask output (url with underscore)",
+ "subject": "Example/P0446/Q.1.0",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_uri\"><a rel=\"nofollow\" class=\"external text\" href=\"http://example.org/Foo_bar\">http://example.org/Foo bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #ask output (url with underscore + link=none)",
+ "subject": "Example/P0446/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_uri\"><a rel=\"nofollow\" class=\"external free\" href=\"http://example.org/Foo_bar\">http://example.org/Foo_bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6",
+ "subject": "Example/P0446/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has url"
+ ],
+ "propertyValues": [
+ "http://example.org/Foo_bar"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "href=\"http://example.org/Foo_bar\">Foo bar</a>"
+ ],
+ "not-contain": [
+ "href=\"http://example.org/Foo_bar\">http://example.org/Foo bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (email with underscore)",
+ "subject": "Example/P0446/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ema"
+ ],
+ "propertyValues": [
+ "john_doe@example.org"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "href=\"mailto:john_doe@example.org\">john_doe@example.org</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 #ask output (email with underscore)",
+ "subject": "Example/P0446/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "href=\"mailto:john_doe@example.org\">john_doe@example.org</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (tel with blank spaces)",
+ "subject": "Example/P0446/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has telephone number"
+ ],
+ "propertyValues": [
+ "+1-201-555-5555"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "href=\"tel:+1-201-555-5555\">+1 201 555 5555</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0447.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0447.json
new file mode 100644
index 00000000..571d7ba8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0447.json
@@ -0,0 +1,78 @@
+{
+ "description": "Test in-text annotation with IRI export (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=ru`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Заголовок",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Тип публикации",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Журнал",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Год",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0447/1",
+ "contents": "[[Заголовок::Pollen Limitation]] [[Журнал::Arctic, Antarctic and Alpine Research]] [[Год::2009]] [[Тип публикации::СтатьÑ]]"
+ },
+ {
+ "page": "Example/P0447/Q.1.1",
+ "contents": "{{#ask: [[Заголовок::Pollen Limitation]] |?Заголовок }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0447/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 6,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Заголовок",
+ "Журнал",
+ "Год",
+ "Тип публикации"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0447/Q.1.1",
+ "assert-output": {
+ "to-contain": [
+ "Property:%D0%97%D0%B0%D0%B3%D0%BE%D0%BB%D0%BE%D0%B2%D0%BE%D0%BA\" title=\"Property:Заголовок\">Заголовок</a>",
+ "Pollen Limitation</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "ru",
+ "wgLang": "en",
+ "smwgExportResourcesAsIri": true,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0448.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0448.json
new file mode 100644
index 00000000..1a575457
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0448.json
@@ -0,0 +1,74 @@
+{
+ "description": "Test in-text legacy `:=` annotation style (#2153, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0448/1",
+ "contents": "[[Has text:=Lorem ipsum]]"
+ },
+ {
+ "page": "Example/P0448/2",
+ "contents": "[[Has text::==Lorem ipsum==]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0448/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "Lorem ipsum"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0448/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "==Lorem ipsum=="
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0449.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0449.json
new file mode 100644
index 00000000..7b7f24d1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0449.json
@@ -0,0 +1,156 @@
+{
+ "description": "Test in-text legacy `:=` and `::` annotation style with enabled links in values (#2153, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0449/2",
+ "contents": "[[Has text:=[[Lorem ipsum]]]]"
+ },
+ {
+ "page": "Example/P0449/3",
+ "contents": "[[Has text:=[http://example.org/eleifend eleifend]]] [[Has text::[http://example.org/eleifend eleifend]]]"
+ },
+ {
+ "page": "Example/P0449/4",
+ "contents": "[[Has text:=[[Has number:=42]] [[Has number::1001]]]] [[Category:P0449]]"
+ },
+ {
+ "page": "Example/P0449/Q.1",
+ "contents": "{{#ask: [[Category:P0449]] |format=embedded }}"
+ }
+ ],
+ "tests": [
+
+ {
+ "type": "parser",
+ "about": "#1 internal wiki link",
+ "subject": "Example/P0449/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "[[Lorem ipsum]]"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Lorem ipsum</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 external link",
+ "subject": "Example/P0449/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "[http://example.org/eleifend eleifend]"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">eleifend</a>"
+ ],
+ "not-contain": [
+ "[[Has text::<a rel=\"nofollow\" class=\"external text\" href=\"http://example.org/eleifend\">eleifend</a>]]"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 link-in annotation",
+ "subject": "Example/P0449/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_INST",
+ "Has text",
+ "Has number"
+ ],
+ "propertyValues": [
+ "42",
+ "1001"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "42 1001"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 embbeded format to verify that [[SMW::OFF]]/[[SMW:ON]] remain after transclusion",
+ "subject": "Example/P0449/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ASK"
+ ],
+ "propertyValues": []
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS",
+ "SMW_PARSER_LINV"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0450.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0450.json
new file mode 100644
index 00000000..3c334e82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0450.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test in-text annotation with invisible chars (`wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0450/1",
+ "contents": "[[Left-To-Right ‎‎Mark::C++‎]] [[Left-To-Right Mark::C++]]"
+ },
+ {
+ "page": "Example/P0450/2",
+ "contents": "[[Right-To-Left â€Mark::ב×מת! -]] [[Right-To-Left Mark::ב×מת!†-]]"
+ },
+ {
+ "page": "Example/P0450/3",
+ "contents": "[[Has text::no shyness]] [[Has text::visible shy&shy;ness]] [[Has text::invisible shy­ness]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 Left-To-Right Mark chars",
+ "subject": "Example/P0450/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Left-To-Right Mark"
+ ],
+ "propertyValues": [
+ "C++"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 Right-To-Left Mark chars",
+ "subject": "Example/P0450/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Right-To-Left Mark"
+ ],
+ "propertyValues": [
+ "ב×מת! -"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 shy char",
+ "subject": "Example/P0450/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has text"
+ ],
+ "propertyValues": [
+ "no shyness",
+ "visible shyness",
+ "invisible shyness"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0451.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0451.json
new file mode 100644
index 00000000..8720ee3f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0451.json
@@ -0,0 +1,105 @@
+{
+ "description": "Test in-text `_dat` datatype, time zone, and JD output (#2454, `wgContLang=en`, `wgLang=en`, `smwgDVFeatures=SMW_DV_TIMEV_CM`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0451/1",
+ "contents": "{{#set:Has date=April 25, 2017 20:00-4:00}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/2",
+ "contents": "{{#set:Has date=April 25, 2017 20:00 EDT}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/3",
+ "contents": "{{#set:Has date=April 25, 2017 20:00-5:00}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/4",
+ "contents": "{{#set:Has date=April 25, 2017 21:00 EDT}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/5",
+ "contents": "{{#set:Has date=April 25, 2017 20:00-3:00}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/6",
+ "contents": "{{#set:Has date=April 25, 2017 19:00 EDT}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/7",
+ "contents": "{{#set:Has date=April 25, 2017 17:00-7:00}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/8",
+ "contents": "{{#set:Has date=April 25, 2017 14:00-10:00}} [[Category:P0451]]"
+ },
+ {
+ "page": "Example/P0451/Q.1",
+ "contents": "{{#ask: [[Category:P0451]] |?Has date |?Has date#LOCL#TZ |?Has date#JD=JD |format=list }}"
+ },
+ {
+ "page": "Example/P0451/9",
+ "contents": "[[Has date::2458119.500000]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 standard, TZ, and JD output",
+ "subject": "Example/P0451/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0451/1.*26 April 2017 00:00:00.*00:00:00 UTC, 26 April 2017.*2457869.5",
+ "Example/P0451/2.*26 April 2017 00:00:00.*20:00:00 EDT, 25 April 2017.*2457869.5",
+ "Example/P0451/3.*26 April 2017 01:00:00.*01:00:00 UTC, 26 April 2017.*2457869.5416667",
+ "Example/P0451/4.*26 April 2017 01:00:00.*21:00:00 EDT, 25 April 2017.*2457869.5416667",
+ "Example/P0451/5.*25 April 2017 23:00:00.*23:00:00 UTC, 25 April 2017.*2457869.4583333",
+ "Example/P0451/6.*25 April 2017 23:00:00.*19:00:00 EDT, 25 April 2017.*2457869.4583333",
+ "Example/P0451/7.*26 April 2017 00:00:00.*00:00:00 UTC, 26 April 2017.*2457869.5",
+ "Example/P0451/8.*26 April 2017 00:00:00.*00:00:00 UTC, 26 April 2017.*2457869.5"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (recognized as JD not as timestamp)",
+ "subject": "Example/P0451/9",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has date"
+ ],
+ "propertyValues": [
+ "2018-01-01T00:00:00"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "2458119.500000"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDVFeatures": [
+ "SMW_DV_TIMEV_CM"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0452.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0452.json
new file mode 100644
index 00000000..19ff7390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0452.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test in-text `_txt` datatype in combination with an \"Allows value\" output (#2342, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Has text",
+ "namespace":"SMW_NS_PROPERTY",
+ "contents": "[[Has type::Text]], [[Allows value::Doe]]"
+ },
+ {
+ "page": "Example/P0452/1",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has text::Doe]]"
+ },
+ {
+ "page": "Example/P0452/2",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has text::Do]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 test output of the valid [[Has text::Doe]] annotation",
+ "subject": "Example/P0452/1",
+ "assert-output": {
+ "to-contain": [
+ "Doe"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31-) test output of the invalid [[Has text::Do]] annotation",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0452/2",
+ "assert-output": {
+ "to-contain": [
+ "Do<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;Do&quot; is not in the list (Doe) of allowed values for the &quot;Has text&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31+) test output of the invalid [[Has text::Do]] annotation",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0452/2",
+ "assert-output": {
+ "to-contain": [
+ "Do<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;Do&quot; is not in the list (Doe) of allowed values for the &quot;Has text&quot; property.\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0453.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0453.json
new file mode 100644
index 00000000..05632c7a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0453.json
@@ -0,0 +1,41 @@
+{
+ "description": "Test in-text `_dat` annotation with `#LOCL#TO` (`wgLocalTZoffset`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P0453/1",
+ "contents": "[[Has date::1 Jan 1971 13:00]]"
+ },
+ {
+ "page": "Example/P0453/Q.1",
+ "contents": "{{#show: Example/P0453/1 |?Has date |?Has date#LOCL#TO }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0453/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_dat\" data-sort-value=\"2440953.0416667\">1 January 1971 13:00:00</td>",
+ "<td class=\"smwtype_dat\" data-sort-value=\"2440953.0416667\">15:00:00, 1 January 1971&#160;<sup title=\"ISO: 1971-01-01T13:00:00\">á´¸</sup></td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLocalTZoffset": 120
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0454.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0454.json
new file mode 100644
index 00000000..c1ed0946
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0454.json
@@ -0,0 +1,52 @@
+{
+ "description": "Test in-text annotation with enabled links in values on `&#91;`, `&#93;` (#2671, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "page": "Example/P0454/1",
+ "contents": ""
+ },
+ {
+ "page": "Example/P0454/2",
+ "contents": "[[Example/P0454/1|&#91;&#91;Foo&#93;&#93;]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 `&#91;`/`&#93;` are kept during parsing",
+ "subject": "Example/P0454/2",
+ "assert-output": {
+ "to-contain": [
+ "<a href=.* title=\"Example/P0454/1\">&#91;&#91;Foo&#93;&#93;</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS",
+ "SMW_PARSER_LINV"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0455.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0455.json
new file mode 100644
index 00000000..a829ba19
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0455.json
@@ -0,0 +1,244 @@
+{
+ "description": "Test paser/in-text annotation with unstripped tags (nowiki etc.) (`SMW_PARSER_UNSTRIP`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has description",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0455/1",
+ "contents": "[[Has description::<nowiki>Contains nowiki tag</nowiki>]]"
+ },
+ {
+ "page": "Example/P0455/2",
+ "contents": "[[Has description::<pre>Contains pre tag</pre>]]"
+ },
+ {
+ "page": "Example/P0455/3",
+ "contents": "[[Has description::<code><nowiki>{{#ask: [[Has strip::Markers]] }}</nowiki></code>]]"
+ },
+ {
+ "page": "Example/P0455/4",
+ "contents": "{{#set: Has description=<nowiki>Contains nowiki tag</nowiki> }}"
+ },
+ {
+ "page": "Example/P0455/5",
+ "contents": "{{#set: Has description=<pre>Contains pre tag</pre> }}"
+ },
+ {
+ "page": "Example/P0455/6",
+ "contents": "{{#set: Has description=<code><nowiki>{{#ask: [[Has strip::Markers]] }}</nowiki></code> }}"
+ },
+ {
+ "page": "Example/P0455/7",
+ "contents": "{{#subobject: Has description=<nowiki>Contains nowiki tag</nowiki> }}"
+ },
+ {
+ "page": "Example/P0455/8",
+ "contents": "{{#subobject: Has description=<pre>Contains pre tag</pre> }}"
+ },
+ {
+ "page": "Example/P0455/9",
+ "contents": "{{#subobject: Has description=<code><nowiki>{{#ask: [[Has strip::Markers]] }}</nowiki></code> }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 nowiki parse/unstrip",
+ "subject": "Example/P0455/1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;Contains nowiki tag&lt;/nowiki&gt;"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Contains nowiki tag"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 pre parse/unstrip",
+ "subject": "Example/P0455/2",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;pre&gt;Contains pre tag&lt;/pre&gt;"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<pre>Contains pre tag</pre>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 code/nowiki recursive parse/unstrip",
+ "subject": "Example/P0455/3",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;&lt;code&gt;&#x007B;&#x007B;#ask&#58; &#x005B;&#x005B;Has strip&#58;&#58;Markers]] }}&lt;/code&gt;&lt;/nowiki&gt;"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<code>{{#ask: [[Has strip::Markers]] }}</code>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 #set nowiki parse/unstrip",
+ "subject": "Example/P0455/4",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;Contains nowiki tag&lt;/nowiki&gt;"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 #set pre parse/unstrip",
+ "subject": "Example/P0455/5",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;pre&gt;Contains pre tag&lt;/pre&gt;"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 #set code/nowiki recursive parse/unstrip",
+ "subject": "Example/P0455/6",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "Has_description",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;&lt;code&gt;&#x007B;&#x007B;#ask&#58; &#x005B;&#x005B;Has strip&#58;&#58;Markers]] }}&lt;/code&gt;&lt;/nowiki&gt;"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 #subobject nowiki parse/unstrip",
+ "subject": "Example/P0455/7#_726a6109c10cb1ff73b7d5659ad54a18",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_description",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;Contains nowiki tag&lt;/nowiki&gt;"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 #subobject pre parse/unstrip",
+ "subject": "Example/P0455/8#_74c67f7dc0f33736ec2d8f4f2132a585",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_description",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;pre&gt;Contains pre tag&lt;/pre&gt;"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 #subobject code/nowiki recursive parse/unstrip",
+ "subject": "Example/P0455/9#_4bb289f3707ef1e08395efb545cae8f3",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 2,
+ "propertyKeys": [
+ "Has_description",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "&lt;nowiki&gt;&lt;code&gt;&#x007B;&#x007B;#ask&#58; &#x005B;&#x005B;Has strip&#58;&#58;Markers]] }}&lt;/code&gt;&lt;/nowiki&gt;"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_UNSTRIP",
+ "SMW_PARSER_STRICT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0456.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0456.json
new file mode 100644
index 00000000..568c8b38
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0456.json
@@ -0,0 +1,108 @@
+{
+ "description": "Test #subobject with assigned sortkey, default order etc.",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "page": "Example/P0456/1",
+ "contents": "{{#subobject: foo |Has text=foo }} {{#subobject: bar |Has text=bar }} {{#subobject: baz |Has text=baz }}"
+ },
+ {
+ "page": "Example/P0456/2",
+ "contents": "{{#subobject: baz |Has text=baz }} {{#subobject: foo |Has text=foo }} {{#subobject: bar |Has text=bar }}"
+ },
+ {
+ "page": "Example/P0456/Q.1",
+ "contents": "{{#ask: [[-Has subobject::Example/P0456/1]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0456/Q.2",
+ "contents": "{{#ask: [[-Has subobject::Example/P0456/2]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0456/3",
+ "contents": "{{#subobject: foo |Has text=foo }} {{#subobject: bar |Has text=bar |@sortkey=zzz }} {{#subobject: baz |Has text=baz }}"
+ },
+ {
+ "page": "Example/P0456/4",
+ "contents": "{{#subobject: baz |Has text=baz }} {{#subobject: foo |Has text=foo }} {{#subobject: bar |Has text=bar|@sortkey=zzz }}"
+ },
+ {
+ "page": "Example/P0456/Q.3",
+ "contents": "{{#ask: [[-Has subobject::Example/P0456/3]] |?Has text }}"
+ },
+ {
+ "page": "Example/P0456/Q.4",
+ "contents": "{{#ask: [[-Has subobject::Example/P0456/4]] |?Has text }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (default order without explicit sortkey)",
+ "subject": "Example/P0456/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" .*Example/P0456/1#bar\" title=\"Example/P0456/1\">Example/P0456/1#bar</a></span></td><td class=\"Has-text smwtype_txt\">bar</td></tr>",
+ "<tr data-row-number=\"2\" .*Example/P0456/1#baz\" title=\"Example/P0456/1\">Example/P0456/1#baz</a></span></td><td class=\"Has-text smwtype_txt\">baz</td></tr>",
+ "<tr data-row-number=\"3\" .*Example/P0456/1#foo\" title=\"Example/P0456/1\">Example/P0456/1#foo</a></span></td><td class=\"Has-text smwtype_txt\">foo</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 same as #0 (default order without explicit sortkey, order independent from the page position)",
+ "subject": "Example/P0456/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" .*Example/P0456/2#bar\" title=\"Example/P0456/2\">Example/P0456/2#bar</a></span></td><td class=\"Has-text smwtype_txt\">bar</td></tr>",
+ "<tr data-row-number=\"2\" .*Example/P0456/2#baz\" title=\"Example/P0456/2\">Example/P0456/2#baz</a></span></td><td class=\"Has-text smwtype_txt\">baz</td></tr>",
+ "<tr data-row-number=\"3\" .*Example/P0456/2#foo\" title=\"Example/P0456/2\">Example/P0456/2#foo</a></span></td><td class=\"Has-text smwtype_txt\">foo</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (with explicit sortkey)",
+ "subject": "Example/P0456/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" .*Example/P0456/3#baz\" title=\"Example/P0456/3\">Example/P0456/3#baz</a></span></td><td class=\"Has-text smwtype_txt\">baz</td></tr>",
+ "<tr data-row-number=\"2\" .*Example/P0456/3#foo\" title=\"Example/P0456/3\">Example/P0456/3#foo</a></span></td><td class=\"Has-text smwtype_txt\">foo</td></tr>",
+ "<tr data-row-number=\"3\" .*Example/P0456/3#bar\" title=\"Example/P0456/3\">Example/P0456/3#bar</a></span></td><td class=\"Has-text smwtype_txt\">bar</td></tr>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 same as #2 (with explicit sortkey, order independent from the page position)",
+ "subject": "Example/P0456/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" .*Example/P0456/4#baz\" title=\"Example/P0456/4\">Example/P0456/4#baz</a></span></td><td class=\"Has-text smwtype_txt\">baz</td></tr>",
+ "<tr data-row-number=\"2\" .*Example/P0456/4#foo\" title=\"Example/P0456/4\">Example/P0456/4#foo</a></span></td><td class=\"Has-text smwtype_txt\">foo</td></tr>",
+ "<tr data-row-number=\"3\" .*Example/P0456/4#bar\" title=\"Example/P0456/4\">Example/P0456/4#bar</a></span></td><td class=\"Has-text smwtype_txt\">bar</td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0457.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0457.json
new file mode 100644
index 00000000..be987d7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0457.json
@@ -0,0 +1,70 @@
+{
+ "description": "Test named subobject caption display (#2895)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "page": "Example/P0457/1",
+ "contents": "{{#subobject:Foo bar|Has text=123|@category=P0457/1}}"
+ },
+ {
+ "page": "Example/P0457/2",
+ "contents": "{{#subobject:Foo bar|Has text=123|@category=P0457/2|display title of=Bar foo}}"
+ },
+ {
+ "page": "Example/P0457/Q.1",
+ "contents": "{{#ask: [[Category:P0457/1]] }}"
+ },
+ {
+ "page": "Example/P0457/Q.2",
+ "contents": "{{#ask: [[Category:P0457/2]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (named sobj without `_` in caption)",
+ "subject": "Example/P0457/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<a href=.*Example/P0457/1#Foo_bar\" title=\"Example/P0457/1\">Example/P0457/1#Foo bar</a>"
+ ],
+ "not-contain": [
+ "<a href=.*Example/P0457/1#Foo_bar\" title=\"Example/P0457/1\">Example/P0457/1#Foo_bar</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (named sobj without `_` in caption and `display title of`)",
+ "subject": "Example/P0457/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<a href=.*Example/P0457/2#Foo_bar\" title=\"Example/P0457/2\">Bar foo#Foo bar</a>"
+ ],
+ "not-contain": [
+ "<a href=.*Example/P0457/2#Foo_bar\" title=\"Example/P0457/2\">Example/P0457/2#Foo_bar</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0458.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0458.json
new file mode 100644
index 00000000..3ac5e7e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0458.json
@@ -0,0 +1,72 @@
+{
+ "description": "Test keyword type `_keyw`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has keyword",
+ "contents": "[[Has type::Keyword]]"
+ },
+ {
+ "page": "Example/P0458/1",
+ "contents": "[[Has keyword::aBcDEF]]"
+ },
+ {
+ "page": "Example/P0458/2",
+ "contents": "[[Has keyword::abcdef]]"
+ },
+ {
+ "page": "Example/P0458/3",
+ "contents": "[[Has keyword::ABCDEF]]"
+ },
+ {
+ "page": "Example/P0458/Q.1",
+ "contents": "{{#ask: [[Has keyword::ABCDEF]] |?Has keyword }}"
+ },
+ {
+ "page": "Example/P0458/Q.2",
+ "contents": "{{#ask: [[Has keyword::abcdef]] |?Has keyword }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0458/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/1\" title=\"Example/P0458/1\">Example/P0458/1</a></td><td class=\"Has-keyword smwtype_keyw\">aBcDEF</td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/2\" title=\"Example/P0458/2\">Example/P0458/2</a></td><td class=\"Has-keyword smwtype_keyw\">abcdef</td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/3\" title=\"Example/P0458/3\">Example/P0458/3</a></td><td class=\"Has-keyword smwtype_keyw\">ABCDEF</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0458/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/1\" title=\"Example/P0458/1\">Example/P0458/1</a></td><td class=\"Has-keyword smwtype_keyw\">aBcDEF</td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/2\" title=\"Example/P0458/2\">Example/P0458/2</a></td><td class=\"Has-keyword smwtype_keyw\">abcdef</td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0458/3\" title=\"Example/P0458/3\">Example/P0458/3</a></td><td class=\"Has-keyword smwtype_keyw\">ABCDEF</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0459.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0459.json
new file mode 100644
index 00000000..7693ec54
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0459.json
@@ -0,0 +1,119 @@
+{
+ "description": "Test keyword type `_keyw` with a formatter schema (`smwgCompactLinkSupport`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_SCHEMA",
+ "page": "Keyword link formatter schema",
+ "contents": {
+ "import-from": "/../Fixtures/p-0459-keyword-formatter-schema.json"
+ }
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has keyword",
+ "contents": "[[Has type::Keyword]] [[Formatter schema::Keyword link formatter schema]]"
+ },
+ {
+ "page": "Example/P0459/1",
+ "contents": "[[Has keyword::aBcDEF]]"
+ },
+ {
+ "page": "Example/P0459/2",
+ "contents": "[[Has keyword::abcdef]]"
+ },
+ {
+ "page": "Example/P0459/3",
+ "contents": "[[Has keyword::ABCDEF]]"
+ },
+ {
+ "page": "Example/P0459/Q.1",
+ "contents": "{{#ask: [[Has keyword::ABCDEF]] |?Has keyword }}"
+ },
+ {
+ "page": "Example/P0459/Q.2",
+ "contents": "{{#ask: [[Has keyword::abcdef]] |?Has keyword }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Keyword link formatter schema",
+ "namespace": "SMW_NS_SCHEMA",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 6,
+ "propertyKeys": [
+ "_MDAT",
+ "_SCHEMA_DEF",
+ "_SCHEMA_DESC",
+ "_SCHEMA_TAG",
+ "_SCHEMA_TYPE",
+ "_SKEY"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Has keyword",
+ "namespace": "SMW_NS_PROPERTY",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_FORMAT_SCHEMA",
+ "_SKEY",
+ "_MDAT",
+ "_TYPE"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0459/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/1\" title=\"Example/P0459/1\">Example/P0459/1</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YUJjREVGLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YUJjREVGLTVELTVEL2Zvcm1hdD1saXN0\">aBcDEF</a></td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/2\" title=\"Example/P0459/2\">Example/P0459/2</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YWJjZGVmLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YWJjZGVmLTVELTVEL2Zvcm1hdD1saXN0\">abcdef</a></td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/3\" title=\"Example/P0459/3\">Example/P0459/3</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6QUJDREVGLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6QUJDREVGLTVELTVEL2Zvcm1hdD1saXN0\">ABCDEF</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0459/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/1\" title=\"Example/P0459/1\">Example/P0459/1</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YUJjREVGLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YUJjREVGLTVELTVEL2Zvcm1hdD1saXN0\">aBcDEF</a></td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/2\" title=\"Example/P0459/2\">Example/P0459/2</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YWJjZGVmLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6YWJjZGVmLTVELTVEL2Zvcm1hdD1saXN0\">abcdef</a></td>",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/P0459/3\" title=\"Example/P0459/3\">Example/P0459/3</a></td><td class=\"Has-keyword smwtype_keyw\"><a href=.*Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6QUJDREVGLTVELTVEL2Zvcm1hdD1saXN0\" title=\"Special:Ask/cl:LTVCLTVCSGFzLTIwa2V5d29yZDo6QUJDREVGLTVELTVEL2Zvcm1hdD1saXN0\">ABCDEF</a></td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCompactLinkSupport": true,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_SCHEMA": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0460.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0460.json
new file mode 100644
index 00000000..8e1dee32
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0460.json
@@ -0,0 +1,218 @@
+{
+ "description": "Test in-text `_num`, `_qty` in combination with an \"Allows value\" range, bounds",
+ "setup": [
+ {
+ "page": "Has ranged number",
+ "namespace":"SMW_NS_PROPERTY",
+ "contents": "[[Has type::Number]], [[Allows value::>1]] [[Allows value::<10]] [[Allows value::20]]"
+ },
+ {
+ "page": "Has bounded number",
+ "namespace":"SMW_NS_PROPERTY",
+ "contents": "[[Has type::Number]], [[Allows value::1..10]] [[Allows value::50...60]] [[Allows value::70]]"
+ },
+ {
+ "page": "Has bounded quantity",
+ "namespace":"SMW_NS_PROPERTY",
+ "contents": "[[Has type::Quantity]], [[Allows value::1..200]] [[Corresponds to::1 km²]] [[Corresponds to::0.38613 sq mi]] [[Corresponds to::1000 m²]]"
+ },
+ {
+ "page": "Example/P0460/1",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has ranged number::2]]"
+ },
+ {
+ "page": "Example/P0460/2",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has ranged number::20]]"
+ },
+ {
+ "page": "Example/P0460/3",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has ranged number::10]]"
+ },
+ {
+ "page": "Example/P0460/4",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded number::1]]"
+ },
+ {
+ "page": "Example/P0460/5",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded number::60]]"
+ },
+ {
+ "page": "Example/P0460/6",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded number::70]]"
+ },
+ {
+ "page": "Example/P0460/7",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded number::60.1]]"
+ },
+ {
+ "page": "Example/P0460/8",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded quantity::200m²]]"
+ },
+ {
+ "page": "Example/P0460/9",
+ "namespace":"NS_MAIN",
+ "contents": "[[Has bounded quantity::200.1km²]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (number, inside the number range)",
+ "subject": "Example/P0460/1",
+ "assert-output": {
+ "to-contain": [
+ "2"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (number, outside of number range but matches discrete value)",
+ "subject": "Example/P0460/2",
+ "assert-output": {
+ "to-contain": [
+ "20"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31-) (number, invalid, outside of specified range)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/3",
+ "assert-output": {
+ "to-contain": [
+ "10<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;10&quot; is not in the list (&gt;1, ) of allowed values for the &quot;Has ranged number&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31+) (number, invalid, outside of specified range)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/3",
+ "assert-output": {
+ "to-contain": [
+ "10<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;10&quot; is not in the list (&gt;1, ) of allowed values for the &quot;Has ranged number&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (number, inside the bounds)",
+ "subject": "Example/P0460/4",
+ "assert-output": {
+ "to-contain": [
+ "1"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (number, inside the seconds bounds)",
+ "subject": "Example/P0460/5",
+ "assert-output": {
+ "to-contain": [
+ "60"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (number, outside the bounds but matches discrete value)",
+ "subject": "Example/P0460/6",
+ "assert-output": {
+ "to-contain": [
+ "70"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (1.31-) (number, invalid, outside of specified bounds)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/7",
+ "assert-output": {
+ "to-contain": [
+ "60.1<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;60.1&quot; is not in the list (1..10, 50...60, 70) of allowed values for the &quot;Has bounded number&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6 (1.31+) (number, invalid, outside of specified bounds)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/7",
+ "assert-output": {
+ "to-contain": [
+ "60.1<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;60.1&quot; is not in the list (1..10, 50...60, 70) of allowed values for the &quot;Has bounded number&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7 (quantity, inside the bounds)",
+ "subject": "Example/P0460/8",
+ "assert-output": {
+ "to-contain": [
+ "0.2 km²"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (1.31-) (quantity, invalid, outside of specified range, bounds)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/9",
+ "assert-output": {
+ "to-contain": [
+ "200.1km²<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;200.1 km²&quot; is not within that range of &quot;1..200&quot; specified by the allows value constraint for the &quot;Has bounded quantity&quot; property.\">"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8 (1.31+) (quantity, invalid, outside of specified range, bounds)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0460/9",
+ "assert-output": {
+ "to-contain": [
+ "200.1km²<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\" data-title=\"Warning\" title=\"&quot;200.1 km²&quot; is not within that range of &quot;1..200&quot; specified by the allows value constraint for the &quot;Has bounded quantity&quot; property.\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0461.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0461.json
new file mode 100644
index 00000000..cf24f3be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0461.json
@@ -0,0 +1,92 @@
+{
+ "description": "Test `_wpg` value with lower/upper first case letter +DISPLAYTITLE (#3587, `wgRestrictDisplayTitle`, `wgCapitalLinks`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "P0461/1",
+ "contents": "[[Has number::123]] [[Has text::abc]] [[Category:P0461]] {{DISPLAYTITLE:p0461/1}}"
+ },
+ {
+ "page": "P0461/2",
+ "contents": "[[Category:P0461]] [[Has text::ABC]] {{DISPLAYTITLE:p0461/2}}"
+ },
+ {
+ "page": "P0461/Q.1",
+ "contents": "{{#show: p0461/1 |?Has number |intro=p0461: }}"
+ },
+ {
+ "page": "P0461/Q.2",
+ "contents": "{{#show: P0461/1 |?Has number |intro=P0461: }}"
+ },
+ {
+ "page": "P0461/Q.3",
+ "contents": "{{#ask: [[p0461/1]] |?Has text |intro=p0461: |format=plain |mainlabel=- |headers=hide }}"
+ },
+ {
+ "page": "P0461/Q.4",
+ "contents": "{{#ask: [[P0461/1]] |?Has text |intro=P0461: |format=plain |mainlabel=- |headers=hide }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (#show, lower case `p0461`)",
+ "subject": "P0461/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "p0461:123"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (#show, upper case `P0461`)",
+ "subject": "P0461/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "P0461:123"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (#ask, lower case `p0461`)",
+ "subject": "P0461/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "p0461:abc"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (#ask, upper case `P0461`)",
+ "subject": "P0461/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "P0461:abc"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgCapitalLinks": true,
+ "wgRestrictDisplayTitle": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0501.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0501.json
new file mode 100644
index 00000000..6eb67ec8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0501.json
@@ -0,0 +1,35 @@
+{
+ "description": "Test `#concept` on predefined property (`wgContLang=en`, `wgLang=es`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Example/P0501/1",
+ "contents": "{{#concept: [[Modification date::+]] |Modification date}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 canonical representation, not userlang specific",
+ "subject": "Example/P0501/1",
+ "namespace": "SMW_NS_CONCEPT",
+ "assert-output": {
+ "to-contain": [
+ "<p class=\"concept-documenation\">Modification date</p><pre>&#91;&#91;Modification date::+]]</pre>"
+ ],
+ "not-contain": [
+ "<p class=\"concept-documenation\">Modification date</p><pre>&#91;&#91;Fecha de modificación::+]]</pre>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0502.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0502.json
new file mode 100644
index 00000000..aecac40f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0502.json
@@ -0,0 +1,194 @@
+{
+ "description": "Test in-text annotation allows value list (#2295, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw allows list Publication categories",
+ "contents": {
+ "import-from": "/../Fixtures/p-0502.txt"
+ }
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw allows list error",
+ "contents": {
+ "import-from": "/../Fixtures/p-0502-error.txt"
+ }
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw allows list license",
+ "contents": "* BSD-2-Clause|2-clause BSD License (BSD-2-Clause)\n* Zlib|zlib/libpng license (Zlib)"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw allows list other license.json",
+ "contents": {
+ "import-from": "/../Fixtures/p-0502-other-license.json"
+ }
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has list",
+ "contents": "[[Has type::Text]] [[Allows value list::Publication categories]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has combined list",
+ "contents": "[[Has type::Text]] [[Allows value list::Publication categories]] [[Allows value::abc]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has combined error list",
+ "contents": "[[Has type::Text]] [[Allows value list::error]] [[Allows value::Foo]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has license",
+ "contents": "[[Has type::Text]] [[Allows value list::license]] [[Allows value list::other license.json]]"
+ },
+ {
+ "page": "Example/P0502/1",
+ "contents": "[[Has list::abc]]"
+ },
+ {
+ "page": "Example/P0502/2",
+ "contents": "[[Has list::Document]] [[Has list::Manuscript]]"
+ },
+ {
+ "page": "Example/P0502/3",
+ "contents": "[[Has combined list::abc]] [[Has combined list::Manuscript]]"
+ },
+ {
+ "page": "Example/P0502/4",
+ "contents": "[[Has combined error list::Foobar]]"
+ },
+ {
+ "page": "Example/P0502/5",
+ "contents": "[[Has license::Zlib]]"
+ },
+ {
+ "page": "Example/P0502/6",
+ "contents": "[[Has license::PD]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 invalid assignment",
+ "subject": "Example/P0502/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 valid assignment",
+ "subject": "Example/P0502/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has list"
+ ],
+ "propertyValues": [
+ "Document",
+ "Manuscript"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 valid assignment",
+ "subject": "Example/P0502/3",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has combined list"
+ ],
+ "propertyValues": [
+ "abc",
+ "Manuscript"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 error output",
+ "subject": "Example/P0502/4",
+ "assert-output": {
+ "to-contain": [
+ "\"Foobar\" is not in the list (Foo, Bar)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 assignment on key (* key|value)",
+ "subject": "Example/P0502/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has license"
+ ],
+ "propertyValues": [
+ "Zlib"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 assignment from JSON",
+ "subject": "Example/P0502/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has license"
+ ],
+ "propertyValues": [
+ "PD"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0503.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0503.json
new file mode 100644
index 00000000..9a908ac9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0503.json
@@ -0,0 +1,106 @@
+{
+ "description": "Test in-text annotation `_uri` on valid/invalid scheme/path",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/P0503/1",
+ "contents": "[[Has url::ftp://example.com/foo]]"
+ },
+ {
+ "page": "Example/P0503/2",
+ "contents": "[[Has url::User:Test]]"
+ },
+ {
+ "page": "Example/P0503/3",
+ "contents": "[[Has url::http:///]]"
+ },
+ {
+ "page": "Example/P0503/Q.1",
+ "contents": "{{#ask: [[Has url::User:Test]] |default=No result }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 valid ftp",
+ "subject": "Example/P0503/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has url"
+ ],
+ "propertyValues": [
+ "ftp://example.com/foo"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 invalid User:",
+ "subject": "Example/P0503/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (allow invalid scheme in query context)",
+ "subject": "Example/P0503/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "No result"
+ ],
+ "not-contain": [
+ "<span class=\"smw-highlighter\" data-type=\"4\" data-state=\"inline\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 invalid path `///`",
+ "subject": "Example/P0503/3#_ERRd3ce18ed907900b1ec8f6a2b7bce6aea",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_ERRT",
+ "_ERRP"
+ ],
+ "propertyValues": [
+ "\"http:///\" has been idendified to contain an invalid \"/\" authority or path component."
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0504.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0504.json
new file mode 100644
index 00000000..a80aa705
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0504.json
@@ -0,0 +1,49 @@
+{
+ "description": "Test Equivalent URI (`__spu`)",
+ "setup": [
+ {
+ "namespace": "NS_MAIN",
+ "page": "Test:P0504/1",
+ "contents": "[[Equivalent URI::http://www.wikidata.org/entity/Q20728]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#3601, Equivalent URI property must not be declarative, (i. e., it can be used on subject pages, as well as property and category pages)",
+ "subject": "Test:P0504/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_URI",
+ "_MDAT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "http://www.wikidata.org/entity/Q20728"
+ ]
+ }
+ },
+ "assert-output": {
+ "not-contain": [
+ "is a declarative property"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0701.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0701.json
new file mode 100644
index 00000000..58bfddfc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0701.json
@@ -0,0 +1,76 @@
+{
+ "description": "Test to create inverted annotation using a #ask/template combination (#711, `import-annotation=true`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Located in",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Location of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Location",
+ "contents": "<includeonly>[[Located in::{{{Located in}}}]]</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "InvertPropertySetter",
+ "contents": "<includeonly>{{#set:|{{{#userparam}}}={{{1}}} }}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "InvertPropertyDeclarator",
+ "contents": "<includeonly>{{#ask:[[{{{1}}}::{{PAGENAME}}]]|link=none|sep=|template=InvertPropertySetter|userparam={{{2}}}|format=template|import-annotation=true}}</includeonly>"
+ },
+ {
+ "page": "Belgium",
+ "contents": "{{Location|Located in=Europe}}"
+ },
+ {
+ "page": "Italy",
+ "contents": "{{Location|Located in=Europe}}"
+ },
+ {
+ "page": "Europe",
+ "contents": "{{InvertPropertyDeclarator|Located in|Location of}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 inverse annotation",
+ "subject": "Europe",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Location_of",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "Italy",
+ "Belgium"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgCapitalLinks": true,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0702.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0702.json
new file mode 100644
index 00000000..899c34c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0702.json
@@ -0,0 +1,99 @@
+{
+ "description": "Test #ask with `format=table` on inverse property/printrequest (#1270, #1360)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has inverse prop",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has query",
+ "contents": "(Remove set display title from p-0438)"
+ },
+ {
+ "page": "Example/P0702/1",
+ "contents": "{{#subobject:|Has inverse prop=InverseExample|Has number=4040}}"
+ },
+ {
+ "page": "Example/P0702/1/1",
+ "contents": "{{#ask: [[-Has inverse prop::<q>[[-Has subobject::Example/P0702/1]]</q>]]|?-Has inverse prop||?-Has inverse prop=LabelOnInversePrintout|format=table}}"
+ },
+ {
+ "page": "Example/P0702/2",
+ "contents": "[[-Has inverse prop::Invalid in-text annotation]]"
+ },
+ {
+ "page": "Example/P0702/3/1",
+ "contents": "{{#ask: [[-Has query::Example/P0702/1/1]]|?-Has query |format=table}}"
+ },
+ {
+ "page": "Example/P0702/3/2",
+ "contents": "{{#ask: [[-Has query::Example/P0702/1/1]]|?-Has query=AnotherLabel |format=table}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 table output using inverse property (label is inverse, link is to normal property)",
+ "subject": "Example/P0702/1/1",
+ "assert-output": {
+ "to-contain": [
+ "Property:Has_inverse_prop\" title=\"Property:Has inverse prop\">-Has inverse prop</a>",
+ ">InverseExample</a>",
+ "title=\"Example/P0702/1\">Example/P0702/1</a>",
+ ">LabelOnInversePrintout</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 in-text inverse annotation",
+ "subject": "Example/P0702/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "propertyValues": []
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (inverse indicator on predefined property)",
+ "subject": "Example/P0702/3/1",
+ "assert-output": {
+ "to-contain": [
+ "<a href=.*Property:Has_query\" title=\"Property:Has query\">-Has query</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (custom label overrides any inverse indicator from property)",
+ "subject": "Example/P0702/3/2",
+ "assert-output": {
+ "to-contain": [
+ "<a href=.*Property:Has_query\" title=\"Property:Has query\">AnotherLabel</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0703.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0703.json
new file mode 100644
index 00000000..f1f3371d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0703.json
@@ -0,0 +1,64 @@
+{
+ "description": "Test `#ask` on `format=table` using different printrequest label output (#1270, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0703/1",
+ "contents": "[[Has text::P0703]]"
+ },
+ {
+ "page": "Example/P0703/2",
+ "contents": "[[Has text::P0703]]"
+ },
+ {
+ "page": "Example/P0703/Q.1",
+ "contents": "{{#ask: [[Has text::P0703]] |?Has text=<span style=\"color: green; font-size: 120%;\">Label</span> |format=table |limit=1 }}"
+ },
+ {
+ "page": "Example/P0703/Q.2",
+ "contents": "{{#ask: [[Has text::P0703]] |?Has text=Label {{#info:Text info.}} |format=table |limit=1 }}",
+ "message-cache": "clear"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0703/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Has text\"><span style=\"color: green; font-size: 120%;\">Label</span>",
+ "Special:Ask/-5B-5BHas-20text::P0703-5D-5D/-3FHas-20text=-3Cspan-20style=&quot;color:-20green-3B-20font-2Dsize:-20120-25-3B&quot;-3ELabel-3C-2Fspan-3E/mainlabel=/limit=1/offset=1/format=table"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 with #info parser",
+ "subject": "Example/P0703/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Has text\">Label <span class=\"smw-highlighter\" data-type=\"6\" data-state=\"persistent\" data-title=\"Information\" title=\"Text info.\">",
+ "<span class=\"smwtticon info\"></span><span class=\"smwttcontent\">Text info.</span></span>",
+ "Special:Ask/-5B-5BHas-20text::P0703-5D-5D/-3FHas-20text=Label-20-3Cspan-20class=&quot;smw-2Dhighlighter&quot;-20data-2Dtype=&quot;6&quot;-20data-2Dstate=&quot;persistent&quot;-20data-2Dtitle=&quot;Information&quot;-20title=&quot;Text-20info.&quot;-3E-3Cspan-20class=&quot;smwtticon-20info&quot;-3E-3C-2Fspan-3E-3Cspan-20class=&quot;smwttcontent&quot;-3EText-20info.-3C-2Fspan-3E-3C-2Fspan-3E/mainlabel=/limit=1/offset=1/format=table"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0704.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0704.json
new file mode 100644
index 00000000..c1a3d87d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0704.json
@@ -0,0 +1,56 @@
+{
+ "description": "Test `#ask` sanitization of printrequest labels to avoid XSS injection (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0704/1",
+ "contents": "[[Has text::P0704]]"
+ },
+ {
+ "page": "Example/P0704/Q.1",
+ "contents": "{{#ask: [[Has text::P0704]] |?Has text=Some <div onmouseover=\"alert('<?php echo htmlspecialchars($xss) ?>')\"> |format=table |limit=1 }}"
+ },
+ {
+ "page": "Example/P0704/Q.2",
+ "contents": "{{#ask: [[Has text::P0704]] |?Has text=Some <a href=\"javascript:alert('<?php echo htmlspecialchars($xss) ?>')\"> |format=table |limit=1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (avoid XSS through sanitization)",
+ "subject": "Example/P0704/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Has text\">Some &lt;div onmouseover=\"alert('&lt;?php echo htmlspecialchars($xss) ?&gt;')\"&gt;"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (avoid XSS through sanitization)",
+ "subject": "Example/P0704/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Has text\">Some &lt;a href=\"javascript:alert('&lt;?php echo htmlspecialchars($xss) ?&gt;')\"&gt;"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json
new file mode 100644
index 00000000..6d678b64
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0705.json
@@ -0,0 +1,98 @@
+{
+ "description": "Test `#ask`/ NS_FILE option (`wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`, 'wgDefaultUserOptions')",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_FILE",
+ "page": "P0705.png",
+ "contents": {
+ "upload": {
+ "file" : "/../Fixtures/image-upload-480.png",
+ "text" : "[[Has file::{{FULLPAGENAME}}]] [[Has caption::123]] {{#subobject:Test|Has text=Foo}}"
+ }
+ }
+ },
+ {
+ "page": "Example/P0705/Q.1",
+ "contents": "{{#ask: [[Has file::+]] |?Has file |format=table |limit=1 }}"
+ },
+ {
+ "page": "Example/P0705/Q.2",
+ "contents": "{{#ask: [[Has file::+]] |?Has file#120px;thumb;<b>{{#show: File:P0705.png |?Has caption |link=none}}</b>[[Extra]] |format=table |limit=1 }}"
+ },
+ {
+ "page": "Example/P0705/Q.3",
+ "contents": "{{#ask: [[File:+]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0705/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "class=\"image\"><img alt=\"P0705.png\"",
+ "300px-P0705.png\" width=\"300\" height=\"300\" style=\"vertical-align: text-top\" class=\"thumbborder\""
+ ],
+ "not-contain": [
+ "P0705.png\" width=\"480\" height=\"480\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 with options",
+ "subject": "Example/P0705/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "300px-P0705.png\" width=\"300\" height=\"300\" style=\"vertical-align: text-top\" class=\"thumbborder\"",
+ "class=\"image\"><img alt=\"P0705.png\"",
+ "120px-P0705.png\" width=\"120\" height=\"120\" class=\"thumbimage\"",
+ "<b>123</b>",
+ "class=\"thumb tright\"",
+ "<div class=\"thumbcaption\"><div class=\"magnify\">",
+ "Extra"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (subobject is displayed as normal link and not with an image reference)",
+ "subject": "Example/P0705/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smw-subobject-entity\"><a href=.*P0705.png#Test\" title=.*P0705.png\">P0705.png#Test</a></span>",
+ "<a href=.*P0705.png\" class=\"image\"><img alt=\"P0705.png\" .*class=\"thumbborder\" .*</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgEnableUploads": true,
+ "wgFileExtensions": [
+ "png"
+ ],
+ "wgDefaultUserOptions": {
+ "thumbsize": 5
+ },
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_FILE": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0706.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0706.json
new file mode 100644
index 00000000..e2bf0f07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0706.json
@@ -0,0 +1,183 @@
+{
+ "description": "Test `#ask` on `format=template` with message parse (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Example/P0706/1",
+ "contents": "[[P0706::P0706]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/Tmpl0706",
+ "contents": "{{{1}}}"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/Tmpl0706withAnnotationDeep",
+ "contents": "<includeonly>[[Tmpl0706withAnnotationDeep::789]]</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/Tmpl0706withAnnotation",
+ "contents": "<includeonly>{{#set:|Tmpl0706withAnnotation=123}} [[Tmpl0706withAnnotation::456]] {{Example/Tmpl0706withAnnotationDeep}}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/Tmpl0706ResultOutput",
+ "contents": "<includeonly>{{{1}}} {{Example/Tmpl0706withAnnotation}}</includeonly>"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Test/Msg0706/1",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706 |format=template }}"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Test/Msg0706/2",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706 |format=template }}"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Test/Msg0706/3",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706 |format=list }}"
+ },
+ {
+ "page": "Test/P0706/4",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706ResultOutput |format=template }}"
+ },
+ {
+ "page": "Test/P0706/5",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706ResultOutput |format=template |import-annotation=true }}"
+ },
+ {
+ "page": "Test/P0706/6",
+ "contents": "{{#ask: [[P0706::P0706]] |?P0706 |link=none |template=Example/Tmpl0706ResultOutput |format=template }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 template format, msg parse outputs [[SMW::off]]/[[SMW::on]]",
+ "subject": "Test/Msg0706/1",
+ "assert-msgoutput": {
+ "to-contain": [
+ "Example/P0706/1"
+ ],
+ "not-contain": [
+ ">SMW::off</a>",
+ ">SMW::on</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 template format, msg parse to avoid [[SMW::off]]/[[SMW::on]]",
+ "subject": "Test/Msg0706/2",
+ "assert-msgoutput": {
+ "to-contain": [
+ "Example/P0706/1"
+ ],
+ "not-contain": [
+ ">SMW::off</a>",
+ ">SMW::on</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 list format, msg parse to avoid [[SMW::off]]/[[SMW::on]]",
+ "subject": "Test/Msg0706/3",
+ "assert-msgoutput": {
+ "to-contain": [
+ "Example/P0706/1"
+ ],
+ "not-contain": [
+ ">SMW::off</a>",
+ ">SMW::on</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (no foreign annotation import)",
+ "subject": "Test/P0706/4",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/P0706/1",
+ "456",
+ "789"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (import foreign annotation)",
+ "subject": "Test/P0706/5",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY",
+ "Tmpl0706withAnnotation",
+ "Tmpl0706withAnnotationDeep"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/P0706/1",
+ "456",
+ "789"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (no foreign annotation import)",
+ "subject": "Test/P0706/6",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ASK",
+ "_MDAT",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/P0706/1",
+ "456",
+ "789"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0707.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0707.json
new file mode 100644
index 00000000..29f4501d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0707.json
@@ -0,0 +1,45 @@
+{
+ "description": "Test `#ask` with enabled execution limit (`wgContLang=en`, `wgLang=en`, `smwgQExpensiveThreshold`, `smwgQExpensiveExecutionLimit`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0707/1",
+ "contents": "[[Has text::123]]"
+ },
+ {
+ "page": "Example/P0707/Q.1",
+ "contents": "{{#ask: [[Has text::+]] |?Has text }} {{#ask: [[Has text::123]] |?Has text }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0707/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/P0707/1\">Example/P0707/1",
+ "title=\"The parser function has reached the limit for expensive executions"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQExpensiveExecutionLimit": 1,
+ "smwgQExpensiveThreshold": 0,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0708.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0708.json
new file mode 100644
index 00000000..46858e90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0708.json
@@ -0,0 +1,61 @@
+{
+ "description": "Test `#ask` NS_FILE and DISPLAYTITLE (`wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`, 'wgDefaultUserOptions', `wgRestrictDisplayTitle`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_FILE",
+ "page": "P0708-text-file.txt",
+ "contents": {
+ "upload": {
+ "file" : "/../Fixtures/file-upload.txt",
+ "text" : "[[File type::txt]] {{DISPLAYTITLE:DIFFERENT TITLE}}"
+ }
+ }
+ },
+ {
+ "page": "Example/P0708/Q.1",
+ "contents": "{{#ask: [[File type::txt]] |format=table |limit=1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (using DISPLAYTITLE)",
+ "subject": "Example/P0708/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<a href=\".*:P0708-text-file.txt\" title=\"File:P0708-text-file.txt\">DIFFERENT TITLE</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgRestrictDisplayTitle": false,
+ "wgEnableUploads": true,
+ "wgFileExtensions": [
+ "png",
+ "txt"
+ ],
+ "wgDefaultUserOptions": {
+ "thumbsize": 5
+ },
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_FILE": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0709.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0709.json
new file mode 100644
index 00000000..9f1d5e1b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0709.json
@@ -0,0 +1,104 @@
+{
+ "description": "Test #ask with `format=table` on inverse property, property path",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "P0709/1",
+ "contents": "[[Has number::123]] [[Has page::ABC]] [[Category:P0709]]"
+ },
+ {
+ "page": "P0709/2",
+ "contents": "[[Has number::1234]] [[Has page::DEF]] [[Category:P0709]]"
+ },
+ {
+ "page": "P0709/3",
+ "contents": "{{#subobject:A |@category=P0709}} {{#subobject:B |@category=P0709}}"
+ },
+ {
+ "page": "P0709/Q.1",
+ "contents": "{{#ask: [[-Has page.Has number::123]] |?Has page |format=table}}"
+ },
+ {
+ "page": "P0709/Q.2",
+ "contents": "{{#ask: [[-Has page.Has number::+]] |?Has page |order=asc |format=table}}"
+ },
+ {
+ "page": "P0709/Q.3",
+ "contents": "{{#ask: [[-Has page.Has number::+]] |?Has page |order=desc |format=table}}"
+ },
+ {
+ "page": "P0709/Q.4",
+ "contents": "{{#ask: [[-Has subobject::+]] [[Category:P0709]] |order=asc |format=table}}"
+ },
+ {
+ "page": "P0709/Q.5",
+ "contents": "{{#ask: [[-Has number::+]] |format=table |default=is empty}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (-Has page.Has number::123)",
+ "subject": "P0709/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=\".*\">ABC</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (-Has page.Has number::+, sort=asc)",
+ "subject": "P0709/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=\".*\">ABC</a></td>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=\".*\">DEF</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (-Has page.Has number::+, sort=desc)",
+ "subject": "P0709/Q.3",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=\".*\">DEF</a></td>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=\".*\">ABC</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (-Has subobject::+, sort=asc)",
+ "subject": "P0709/Q.4",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><span class=\"smw-subobject-entity\"><a href=\".*\">P0709/3#A</a></span></td></tr>",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><span class=\"smw-subobject-entity\"><a href=\".*\">P0709/3#B</a></span></td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0710.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0710.json
new file mode 100644
index 00000000..32af1799
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0710.json
@@ -0,0 +1,66 @@
+{
+ "description": "Test `#ask` with `[[Category::Foo]]`",
+ "setup": [
+ {
+ "page": "P0710/0",
+ "namespace":"NS_MAIN",
+ "contents": "{{#ask: [[Category::Foo]] }}"
+ },
+ {
+ "page": "P0710/1",
+ "namespace":"NS_MAIN",
+ "contents": "{{#ask: [[Category::Foo||Bar]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "P0710/0",
+ "assert-output": {
+ "to-contain": [
+ "\"[[Category&#58;&#58;Foo]]\" is not recognized as valid category or value annotation."
+ ]
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_ERRC",
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": [
+ "P0710/0#0##_ERRc4925219842fff8c0078c42d61051c5e",
+ "P0710/0#0##_ERR32617756f0f702a55404d89059af32b8"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (with disjunction)",
+ "subject": "P0710/1",
+ "assert-output": {
+ "to-contain": [
+ "\"[[Category&#58;&#58;Foo||Bar]]\" is not recognized as valid category or value annotation."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0711.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0711.json
new file mode 100644
index 00000000..52c3e57f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0711.json
@@ -0,0 +1,149 @@
+{
+ "description": "Test `#ask` with `||` condition (#3473)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "P0711/0",
+ "contents": "[[Has number::123]] [[Has page::ABC]] [[Category:P0711/0]]"
+ },
+ {
+ "page": "P0711/1",
+ "contents": "[[Has number::1234]] [[Has page::DEF]] [[Category:P0711/1]]"
+ },
+ {
+ "page": "P0711/Q.1",
+ "contents": "{{#ask: [[Category:P0711/0]] || [[Category:P0711/1]] }}"
+ },
+ {
+ "page": "P0711/Q.2",
+ "contents": "{{#ask: [[Category:P0711/0]][[Has number::1234]] || [[Category:P0711/1]] |format=table |limt=1 }}"
+ },
+ {
+ "page": "P0711/Q.3",
+ "contents": "{{#ask: [[Category:P0711/0||P0711/1]] [[Has number::1234]] || [[Category:P0711/1]] |format=table |limt=1 }}"
+ },
+ {
+ "page": "P0711/Q.4",
+ "contents": "{{#ask: [[Category:P0711/0]] || [[Has number::1234]] || [[Category:P0711/1]] |format=table |limt=1 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 ([[Category:P0711/0]] || [[Category:P0711/1]])",
+ "subject": "P0711/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "P0711/0",
+ "P0711/1"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 ([[Category:P0711/0]] || [[Category:P0711/1]] )",
+ "subject": "P0711/Q.1#_QUERY242ebc6ec4e4f26fdf094cbe459c5a7c",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKDE",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKST",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Category:P0711/0||P0711/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 ( [[Category:P0711/0]][[Has number::1234]] || [[Category:P0711/1]] )",
+ "subject": "P0711/Q.2#_QUERY4d17e3c435adb842705d03a0ce6e80da",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKDE",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKST",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q>[[Category:P0711/0]] [[Has number::1234]] OR [[Category:P0711/1]]</q>"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 ( [[Category:P0711/0||P0711/1]][[Has number::1234]] || [[Category:P0711/1]] )",
+ "subject": "P0711/Q.3#_QUERY5eb422bd01f40410eaa3586b5134eb32",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKDE",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKST",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q>[[Category:P0711/0||P0711/1]] [[Has number::1234]] OR [[Category:P0711/1]]</q>"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 ( [[Category:P0711/0]] || [[Has number::1234]] || [[Category:P0711/1]] )",
+ "subject": "P0711/Q.4#_QUERYfcb13e9c99c91f99011555adf4327898",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKDE",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKST",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q>[[Category:P0711/0||P0711/1]] OR <q>[[Has number::1234]]</q> </q>"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0712.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0712.json
new file mode 100644
index 00000000..0a9c2811
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0712.json
@@ -0,0 +1,64 @@
+{
+ "description": "Test `#ask` on `format=template`/`link=none`/DISPLAYTITLE with nested template",
+ "setup": [
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/P0712/Parse 1",
+ "contents": "<includeonly>'''Parse 1 result:'''<br>Modify to link: [[{{{1|}}}]]<br>As-is output: {{{1|}}} '''Parse 2 result:'''<br>{{Example/P0712/Parse 2|{{{1|}}} }}</includeonly>"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "Example/P0712/Parse 2",
+ "contents": "<includeonly>Modify to link: [[{{{1|}}}]]<br>As-is output: {{{1|}}}</includeonly>"
+ },
+ {
+ "page": "Test:P0712/1",
+ "contents": "[[Issue::3853]] {{DISPLAYTITLE:Displaytitle P0712}}"
+ },
+ {
+ "page": "Test:P0712/Q.1",
+ "contents": "{{#ask: [[Issue::3853]] |link=none |template=Example/P0712/Parse 1 |format=template }}"
+ },
+ {
+ "page": "Test:P0712/Q.2",
+ "contents": "{{#ask: [[Issue::3853]] |template=Example/P0712/Parse 1 |format=template }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 `link=none`",
+ "subject": "Test:P0712/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<b>Parse 1 result:</b><br />Modify to link: <a href=\".*Test:P0712/1\" title=\"Test:P0712/1\">Test:P0712/1</a>",
+ "<b>Parse 2 result:</b><br />Modify to link: <a href=\".*Test:P0712/1\" title=\"Test:P0712/1\">Test:P0712/1 </a><br />As-is output: Test:P0712/1"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 ",
+ "subject": "Test:P0712/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<b>Parse 1 result:</b><br />Modify to link: [[<a href=\".*Test:P0712/1\" title=\"Test:P0712/1\">Displaytitle P0712</a>]]<br />",
+ "<b>Parse 2 result:</b><br />Modify to link: [[<a href=\".*/Test:P0712/1\" title=\"Test:P0712/1\">Displaytitle P0712</a> ]]"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgRestrictDisplayTitle": false,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0901.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0901.json
new file mode 100644
index 00000000..59fcd358
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0901.json
@@ -0,0 +1,112 @@
+{
+ "description": "Test #ask on moved redirected subject (#1086)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasPropertyForMove",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page/09/01/1",
+ "contents": "[[HasPropertyForMove::ABC]]"
+ },
+ {
+ "page": "Page/09/01/2",
+ "contents": "[[HasPropertyForMove::ABC]]"
+ },
+ {
+ "move-to": {
+ "target": "Move/09/01/4",
+ "is-redirect": false
+ },
+ "page": "Page/09/01/3",
+ "contents": "[[HasPropertyForMove::ABC]]"
+ },
+ {
+ "page": "Move/09/01/Query",
+ "contents": "{{#ask:[[HasPropertyForMove::ABC]] |format=ul |link=none |headers=show }}"
+ },
+ {
+ "move-to": {
+ "target": "Redirect/09/01/2",
+ "is-redirect": true
+ },
+ "page": "Redirect/09/01/1",
+ "contents": "[[HasPropertyForMove::123]]"
+ },
+ {
+ "page": "Redirect/09/01/3",
+ "contents": "#REDIRECT [[Redirect/09/01/4]] [[HasPropertyForMove::123]]"
+ },
+ {
+ "page": "Redirect/09/01/Query",
+ "contents": "{{#ask:[[HasPropertyForMove::ABC]] OR [[HasPropertyForMove::123]] |format=ul |link=none |headers=show }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 includes move",
+ "subject": "Move/09/01/Query",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Page/09/01/1</span></span></li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Page/09/01/2</span></span></li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Move/09/01/4</span></span></li>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 includes redirect",
+ "subject": "Redirect/09/01/Query",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Page/09/01/1</span></span></li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Page/09/01/2</span></span></li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Move/09/01/4</span></span></li>",
+ "<li class=\"smw-row\"><span class=\"smw-field\"><span class=\"smw-value\">Redirect/09/01/2</span></span></li>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "mw-master": "Skipping because redirect/move causes a failure in 1.27 (#1376) with the DeferredUpdate blocking succeeding tests"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0902.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0902.json
new file mode 100644
index 00000000..c45c9e09
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0902.json
@@ -0,0 +1,65 @@
+{
+ "description": "Test #ask on failed queries to produce a `_ERRC` (#1297, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasPropertyForMove",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0902/1",
+ "contents": "{{#ask:[[Help:]] |format=ul |link=none |headers=show }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 failed NS query",
+ "subject": "Example/P0902/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 error record on empty Help:",
+ "subject": "Example/P0902/1#_ERRaefa648d070f2edc4b171586eae9b1b0",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRP",
+ "_ERRT",
+ "_SKEY"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0903.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0903.json
new file mode 100644
index 00000000..6dd0c5e9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0903.json
@@ -0,0 +1,70 @@
+{
+ "description": "Test #ask on redirected printrequest (#1290, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has PAGE",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "#REDIRECT [[Property:Has PAGE]]"
+ },
+ {
+ "page": "Example/P0903/1",
+ "contents": "[[Has page::ABC]]"
+ },
+ {
+ "page": "Example/P0903/2",
+ "contents": "[[Has PAGE::ABC]]"
+ },
+ {
+ "page": "Example/P0903/3",
+ "contents": "{{#ask:[[Has page::ABC]] |?Has page |?Has PAGE |format=table }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0903/3",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/P0903/1",
+ "Example/P0903/2"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0904.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0904.json
new file mode 100644
index 00000000..cb6327e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0904.json
@@ -0,0 +1,98 @@
+{
+ "description": "Test #ask with subject redirected to different NS (en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0904/1",
+ "contents": "[[Has page::P0904]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Example/P0904/1",
+ "contents": "[[Has page::P0904]]"
+ },
+ {
+ "page": "Example/P0904/1",
+ "contents": "#REDIRECT [[Help:Example/P0904/1]]"
+ },
+ {
+ "page": "Example/P0904/2",
+ "contents": "{{#ask: [[Has page::P0904]] |?Has page |format=table |link=none }}"
+ },
+ {
+ "page": "Example/P0904/3",
+ "contents": "{{#ask: [[~Example/P0904*]] |?Has page |format=table |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0904/1",
+ "namespace": "NS_HELP",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_page"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0904/2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Help:Example/P0904/1</td><td class=\"Has-page smwtype_wpg\">P0904</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (redirected subject does not appear in result list)",
+ "subject": "Example/P0904/3",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"smwtype_wpg\">Help:Example/P0904/1</td><td class=\"Has-page smwtype_wpg\">P0904</td>"
+ ],
+ "not-contain": [
+ "<td class=\"smwtype_wpg\">Example/P0904/1</td><td class=\"Has-page smwtype_wpg\">P0904</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_HELP": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0905.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0905.json
new file mode 100644
index 00000000..9a45280a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0905.json
@@ -0,0 +1,103 @@
+{
+ "description": "Test `#ask` query-in-query construct (`_sobj`/`_dat`/`_num`) (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Assessment year",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Assessment value",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/P0905/1",
+ "contents": "{{#subobject: |Assessment name=Assessment 1a |Assessment year=2005 |Assessment value=13 |@category=Assessment}}{{#subobject: |Assessment name=Assessment 1b |Assessment year=2006 |Assessment value=2 |@category=Assessment}}{{#subobject: |Assessment name=Assessment 1c |Assessment year=2005 |Assessment value=14 |@category=Assessment }}"
+ },
+ {
+ "page": "Example/P0905/Q1",
+ "contents": "{{#ask: [[Category:Assessment]] |?Assessment name |?Assessment year |?Assessment value }}"
+ },
+ {
+ "page": "Example/P0905/Q2",
+ "contents": "{{#ask: [[Category:Assessment]] <q>[[Assessment value::{{#ask: [[Category:Assessment]] |?Assessment value|sort=Assessment value|order=desc|limit=1 |format=plainlist |headers=hide|mainlabel=-|link=none|searchlabel=}}]] </q> |?Assessment name |?Assessment year |?Assessment value }}"
+ },
+ {
+ "page": "Example/P0905/Q3",
+ "contents": "{{#ask: [[-Has subobject::Example/P0905/1]] <q>[[Assessment value::{{#ask: [[-Has subobject::Example/P0905/1]] |?Assessment value|sort=Assessment value|order=desc|limit=1 |format=plainlist |headers=hide|mainlabel=-|link=none|searchlabel=}}]] </q> |?Assessment name |?Assessment year |?Assessment value }}"
+ },
+ {
+ "page": "Example/P0905/Q4",
+ "contents": "{{#ask: [[Category:Assessment]] <q>[[Assessment value::{{#ask: [[Category:Assessment]] |?Assessment value|sort=Assessment value|order=asc|limit=1 |format=plainlist |headers=hide|mainlabel=-|link=none|searchlabel=}}]] </q> |?Assessment name |?Assessment year |?Assessment value }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0905/Q1",
+ "assert-output": {
+ "to-contain": [
+ "Assessment 1a</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"13\"",
+ "Assessment 1b</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453736.5\">2006</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"2\"",
+ "Assessment 1c</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"14\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 ony return highest Assessment value ",
+ "subject": "Example/P0905/Q2",
+ "assert-output": {
+ "to-contain": [
+ "Assessment 1c</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"14\""
+ ],
+ "not-contain": [
+ "Assessment 1a</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"13\"",
+ "Assessment 1b</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453736.5\">2006</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"2\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 same as #1",
+ "subject": "Example/P0905/Q3",
+ "assert-output": {
+ "to-contain": [
+ "Assessment 1c</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"14\""
+ ],
+ "not-contain": [
+ "Assessment 1a</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"13\"",
+ "Assessment 1b</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453736.5\">2006</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"2\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 return only lowest Assessment value",
+ "subject": "Example/P0905/Q4",
+ "assert-output": {
+ "to-contain": [
+ "Assessment 1b</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453736.5\">2006</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"2\""
+ ],
+ "not-contain": [
+ "Assessment 1a</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"13\"",
+ "Assessment 1c</a></td><td class=\"Assessment-year smwtype_dat\" data-sort-value=\"2453371.5\">2005</td><td class=\"Assessment-value smwtype_num\" data-sort-value=\"14\""
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0906.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0906.json
new file mode 100644
index 00000000..2331bbcd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0906.json
@@ -0,0 +1,69 @@
+{
+ "description": "Test `#ask` on category/property hierarchy with circular reference (#1713, `wgContLang=en`, `wgLang=en`, 'smwgEnabledQueryDependencyLinksStore', skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "CategoryWithCircularHierarchy",
+ "contents": "[[Category:CategoryWithCircularHierarchy]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "PropertyWithCircularHierarchy",
+ "contents": "[[Subproperty of::PropertyWithCircularHierarchy]]"
+ },
+ {
+ "page": "Example/P0906/1",
+ "contents": "[[Category:CategoryWithCircularHierarchy]]"
+ },
+ {
+ "page": "Example/P0906/Q1",
+ "contents": "{{#ask: [[Category:CategoryWithCircularHierarchy]] |link=none }}"
+ },
+ {
+ "page": "Example/P0906/2",
+ "contents": "[[PropertyWithCircularHierarchy::123]]"
+ },
+ {
+ "page": "Example/P0906/Q2",
+ "contents": "{{#ask: [[PropertyWithCircularHierarchy::+]] |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0906/Q1",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0906/1"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0906/Q2",
+ "assert-output": {
+ "to-contain": [
+ "Example/P0906/2"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgEnabledQueryDependencyLinksStore": true,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/property hierarchies are currently not implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0907.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0907.json
new file mode 100644
index 00000000..0367e258
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0907.json
@@ -0,0 +1,81 @@
+{
+ "description": "Test the QueryResult cache feature (#1251, `wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0907/1",
+ "contents": "[[Has page::ABC]]"
+ },
+ {
+ "page": "Example/P0907/2",
+ "contents": "{{#ask:[[Has page::ABC]] |sort=Has page |order=asc |limit=10 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0907/2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/P0907/1"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 query from the previous #ask with same description is fetched from cache",
+ "subject": "Foo",
+ "condition": "[[Has page::ABC]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10,
+ "sort": {
+ "Has_page": "ASC"
+ }
+ },
+ "assert-queryresult": {
+ "isFromCache": true,
+ "count": 1,
+ "results": [
+ "Example/P0907/1#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQueryResultCacheType": "hash",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0908.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0908.json
new file mode 100644
index 00000000..4de6d273
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0908.json
@@ -0,0 +1,48 @@
+{
+ "description": "Test the QueryResult cache feature with different `|+lang`/`|+order` prinrequest parameters (#1251, `wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has alternative label",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "page": "Example/P0908/1",
+ "contents": "[[Category:P0908]] [[Has page::{{FULLPAGENAME}}]] [[Has alternative label::Lipase@en]], [[Has alternative label::Tributyrase@en]], [[Has alternative label::Triglyceride lipase@en]], [[Has alternative label::トリアシルグリセロールリパーゼ@ja]], [[Has alternative label::ليباز ثلاثي اسيل الغليسيرول@ar]], [[Has alternative label::Triacylglycérol lipase@fr]], [[Has alternative label::Triacilglicerol lipaza@sh]], [[Has alternative label::Triacilglicerol lipaza@sr]]"
+ },
+ {
+ "page": "Example/P0908/Q.1",
+ "contents": "{{#ask: [[Category:P0908]] |?Has alternative label|+lang=en|+order=asc |link=none }} {{#ask: [[Category:P0908]] |?Has alternative label|+lang=ja|+order=asc |link=none }} "
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (query with same signature on same page but different prinrequest parameter outputs correct content)",
+ "subject": "Example/P0908/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0908/1</td><td class=\"Has-alternative-label smwtype_txt\">Lipase<br />Tributyrase<br />Triglyceride lipase</td></tr>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/P0908/1</td><td class=\"Has-alternative-label smwtype_txt\">トリアシルグリセロールリパーゼ</td></tr>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQueryResultCacheType": "hash",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0909.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0909.json
new file mode 100644
index 00000000..42b70a8d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0909.json
@@ -0,0 +1,265 @@
+{
+ "description": "Test the description optimization (`wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`, `smwgQFilterDuplicates=true`, `smwgQueryProfiler`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/P0909/1",
+ "contents": "[[Has page::Example/P0909/1]]"
+ },
+ {
+ "page": "Example/P0909/Q.1",
+ "contents": "{{#ask: [[Has page::Example/P0909/1]] [[Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.2",
+ "contents": "{{#ask: [[Has page::Example/P0909/1]] OR [[Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.3",
+ "contents": "{{#ask: [[Has page::Example/P0909/1]] [[Has page::Example/P0909/1]] OR [[Has page::Example/P0909/1]] [[Has page::<q>[[Has page::+]]</q>]] [[Has page::+]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.4",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] OR [[Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.5",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] [[Has page.Has page.Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.6",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] [[Has page.Has page.Has page::Example/P0909/1]] OR [[Has page.Has page.Has page::Example/P0909/1]] [[Has page.Has page.Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.7",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] [[Has page.-Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.8",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] [[-Has page.Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ },
+ {
+ "page": "Example/P0909/Q.9",
+ "contents": "{{#ask: [[Has page.Has page::Example/P0909/1]] [[-Has page.Has page::Example/P0909/1]] OR [[Has page.-Has page::Example/P0909/1]] |limit=50 |offset=0 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0909/Q.1#_QUERYced3dc3308e03e4ff0c92578d616e6a5",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Has page::Example/P0909/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "subject": "Example/P0909/Q.2#_QUERYced3dc3308e03e4ff0c92578d616e6a5",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Has page::Example/P0909/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2",
+ "subject": "Example/P0909/Q.3#_QUERY84456d5e55da8a3ba2279a5eb73ce483",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q> <q>[[Has page::Example/P0909/1]]</q> OR [[Has page::Example/P0909/1]] [[Has page.Has page::+]] [[Has page::+]]</q>"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3",
+ "subject": "Example/P0909/Q.4#_QUERYcdd649eae7a93d114d44b75d9c1ed65c",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q> <q>[[Has page.Has page::Example/P0909/1]]</q> OR <q>[[Has page::Example/P0909/1]]</q> </q>"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4",
+ "subject": "Example/P0909/Q.5#_QUERY13b773c8ea18d4839f069a8a88dbe783",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Has page.Has page::Example/P0909/1]] [[Has page.Has page.Has page::Example/P0909/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5",
+ "subject": "Example/P0909/Q.6#_QUERYfb6756e6bb1d5cf0c37b0c305caf6d6e",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q>[[Has page.Has page::Example/P0909/1]] [[Has page.Has page.Has page::Example/P0909/1]] OR <q>[[Has page.Has page.Has page::Example/P0909/1]]</q> </q>"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#6",
+ "subject": "Example/P0909/Q.7#_QUERYbcb06ea6a827c21b2ae2fbc8359bef8c",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Has page.Has page::Example/P0909/1]] [[Has page.-Has page::Example/P0909/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#7",
+ "subject": "Example/P0909/Q.8#_QUERYecd911af9ce2cf69bacfbab3041114c7",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "[[Has page.Has page::Example/P0909/1]] [[-Has page.Has page::Example/P0909/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#8",
+ "subject": "Example/P0909/Q.9#_QUERY69098f6e24f5bf124fec18d6339450ce",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "<q>[[Has page.Has page::Example/P0909/1]] [[-Has page.Has page::Example/P0909/1]] OR <q>[[Has page.-Has page::Example/P0909/1]]</q> </q>"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQueryProfiler": true,
+ "smwgQFilterDuplicates": true,
+ "smwgQueryResultCacheType": "hash",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0910.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0910.json
new file mode 100644
index 00000000..1dabdeca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0910.json
@@ -0,0 +1,134 @@
+{
+ "description": "Test `#ask` to highlight (`#-hl`) search token in result set (#..., `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has mono text",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record with text",
+ "contents": "[[Has type::Record]] [[Has fields::Text;Number]]"
+ },
+ {
+ "page": "Example/P0910/1",
+ "contents": "[[Category:P0910]] [[Has text::Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ...]]"
+ },
+ {
+ "page": "Example/P0910/Q.1",
+ "contents": "{{#ask: [[Has text::~*dolor sit amet consectetuer*]] |?Has text#-hl |link=none }} "
+ },
+ {
+ "page": "Example/P0910/2",
+ "contents": "[[Category:P0910]] [[Has mono text::Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ...@la]]"
+ },
+ {
+ "page": "Example/P0910/Q.2.1",
+ "contents": "{{#ask: [[Has mono text::~*dolor sit amet consectetuer*]] |?Has mono text#-hl |link=none }} "
+ },
+ {
+ "page": "Example/P0910/Q.2.2",
+ "contents": "{{#ask: [[Has mono text::~*dolor sit amet consectetuer*]] |?Has mono text#-hl|+lang=la |link=none }} "
+ },
+ {
+ "page": "Example/P0910/Q.2.3",
+ "contents": "{{#ask: [[Has mono text::~*dolor sit amet consectetuer*]] |?Has mono text#-hl|+index=1 |link=none }} "
+ },
+ {
+ "page": "Example/P0910/3",
+ "contents": "[[Category:P0910]] [[Has record with text::Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ...;123]]"
+ },
+ {
+ "page": "Example/P0910/Q.3.1",
+ "contents": "{{#ask: [[Has record with text::~*dolor sit amet consectetuer*]] |?Has record with text#-hl |link=none }} "
+ },
+ {
+ "page": "Example/P0910/Q.3.2",
+ "contents": "{{#ask: [[Has record with text::~*dolor sit amet consectetuer*]] |?Has record with text#-hl|+index=1 |link=none }} "
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (...)",
+ "subject": "Example/P0910/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_txt\">Lorem ipsum <b>dolor</b> <b>sit</b> <b>amet</b> <b>consectetuer</b> justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ..."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (simple record/mono text doens't display a highlight)",
+ "subject": "Example/P0910/Q.2.1",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_mlt_rec\">Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ... (la)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (on selected language |+lang, tokens are displayed)",
+ "subject": "Example/P0910/Q.2.2",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_txt\">Lorem ipsum <b>dolor</b> <b>sit</b> <b>amet</b> <b>consectetuer</b> justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ..."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (on selected index |+index, tokens are displayed)",
+ "subject": "Example/P0910/Q.2.3",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_txt\">Lorem ipsum <b>dolor</b> <b>sit</b> <b>amet</b> <b>consectetuer</b> justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ..."
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (simple record/mono text doens't display a highlight)",
+ "subject": "Example/P0910/Q.3.1",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_rec\">Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ... (123)"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#5 (on selected index |+index, tokens are displayed)",
+ "subject": "Example/P0910/Q.3.2",
+ "assert-output": {
+ "to-contain": [
+ "smwtype_txt\">Lorem ipsum <b>dolor</b> <b>sit</b> <b>amet</b> <b>consectetuer</b> justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede lorem nulla justo diam wisi. Libero Nam turpis ..."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0911.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0911.json
new file mode 100644
index 00000000..bf021791
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0911.json
@@ -0,0 +1,64 @@
+{
+ "description": "Test the `_ASK` profile (#2270, `smwgQueryProfiler`, `smwgQueryResultCacheType`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P0911/1",
+ "contents": "[[Has page::Example/P0911/1]]"
+ },
+ {
+ "page": "Example/P0911/Q.1",
+ "contents": "{{#ask: [[Has page::Example/P0911/1]] |limit=50 |offset=0 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0911/Q.1#_QUERY7f8c02d826a5987a65a9de7ba8d9abfd",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 7,
+ "propertyKeys": [
+ "_ASKST",
+ "_ASKFO",
+ "_ASKSI",
+ "_ASKDE",
+ "_ASKDU",
+ "_ASKPA",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "{\"limit\":50,\"offset\":0,\"sort\":[\"\"],\"order\":[\"asc\"],\"mode\":1}"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQueryResultCacheType": "hash",
+ "smwgQueryProfiler" : [
+ "SMW_QPRFL_DUR",
+ "SMW_QPRFL_PARAMS"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0912.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0912.json
new file mode 100644
index 00000000..7628fc47
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0912.json
@@ -0,0 +1,171 @@
+{
+ "description": "Test `#ask` with (`#-raw`) formatter using `#set` (#..., `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has mono text",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "page": "Example/P0912/1",
+ "contents": "[[Category:P0912]] {{#set: Has text=Some text with annotation [[Foo::Example/P0912/1]] }}"
+ },
+ {
+ "page": "Example/P0912/Q.1",
+ "contents": "{{#ask: [[Category:P0912]] [[Has text::+]] |?Has text |link=none }}"
+ },
+ {
+ "page": "Example/P0912/Q.2",
+ "contents": "{{#ask: [[Category:P0912]] [[Has text::+]] |?Has text#-raw |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0912/1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_INST",
+ "_SKEY",
+ "Has text"
+ ],
+ "propertyValues": [
+ "Some text with annotation [[Foo::Example/P0912/1]]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31-) (without `-raw`)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0912/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation Example/P0912/1</td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31+) (without `-raw`)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0912/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation Example/P0912/1</td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31-) (with `-raw`)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0912/Q.2",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY",
+ "Foo"
+ ],
+ "propertyValues": [
+ "Example/P0912/1"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation",
+ "title=\"Example/P0912/1\">Example/P0912/1</a></td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31+) (with `-raw`)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0912/Q.2",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY",
+ "Foo"
+ ],
+ "propertyValues": [
+ "Example/P0912/1"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation",
+ "title=\"Example/P0912/1\">Example/P0912/1</a></td></tr></tbody></table>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0913.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0913.json
new file mode 100644
index 00000000..afa991b1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0913.json
@@ -0,0 +1,221 @@
+{
+ "description": "Test `#ask` with (`#-raw`) formatter with links in values (#..., `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::text]]"
+ },
+ {
+ "page": "Example/P0913/1",
+ "contents": "[[Category:P0913]] [[Has text::Some text with annotation [[Foo::Example/P0913/1]]]]"
+ },
+ {
+ "page": "Example/P0913/Q.1",
+ "contents": "{{#ask: [[Category:P0913]] [[Has text::+]] |?Has text |link=none }}"
+ },
+ {
+ "page": "Example/P0913/Q.2",
+ "contents": "{{#ask: [[Category:P0913]] [[Has text::+]] |?Has text#-raw |link=none }}"
+ },
+ {
+ "page": "Example/P0913/Q.3",
+ "contents": "{{#ask: [[Category:P0913]] [[Has text::+]] |?Has text#-raw |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/P0913/1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 5,
+ "propertyKeys": [
+ "_MDAT",
+ "_INST",
+ "_SKEY",
+ "Has text",
+ "Foo"
+ ],
+ "propertyValues": [
+ "Some text with annotation Example/P0913/1"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31-) (without `-raw`)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (1.31+) (without `-raw`)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31-) (with `-raw`)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.2",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (1.31+) (with `-raw`)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.2",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></tbody></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (1.31-) same as #2 (with deprecated `-ia`)",
+ "skip-on": {
+ "mediawiki": [ ">1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.3",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></table>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (1.31+) same as #2 (with deprecated `-ia`)",
+ "skip-on": {
+ "mediawiki": [ "<1.30.x", "MediaWiki changed the HTML Tidy" ]
+ },
+ "subject": "Example/P0913/Q.3",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-text smwtype_txt\">Some text with annotation <a href=",
+ "title=\"Example/P0913/1\">Example/P0913/1</a></td></tr></tbody></table>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgParserFeatures": [
+ "SMW_PARSER_STRICT",
+ "SMW_PARSER_INL_ERROR",
+ "SMW_PARSER_HID_CATS",
+ "SMW_PARSER_LINV"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0914.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0914.json
new file mode 100644
index 00000000..b54ed263
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0914.json
@@ -0,0 +1,88 @@
+{
+ "description": "Test the description optimization on `_ref_rec` type with property chain query/sort (`wgContLang=en`, `wgLang=en`, `smwgQueryResultCacheType=true`, `smwgQFilterDuplicates=true`, `smwgQueryProfiler`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "ID",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Runtime",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Memory used",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Runtime benchmark",
+ "contents": "[[Has type::Reference]] [[Has fields::Date; Version; Memory used; Runtime;ID;Benchmark notes]]"
+ },
+ {
+ "page": "Example/P0914/1",
+ "contents": "{{#subobject: |Runtime benchmark=27 Nov 2016;0583b05;16690400;48.35;200;n/a }}"
+ },
+ {
+ "page": "Example/P0914/Q.1",
+ "contents": "{{#ask: [[Runtime benchmark.ID::+]] |?Runtime benchmark |?Runtime benchmark.Memory used#- |?Runtime benchmark.ID |?Runtime benchmark.Runtime|?Runtime benchmark.Benchmark notes |format=broadtable |sort=Runtime benchmark.ID,Runtime benchmark.Runtime |order=desc |limit=2 |link=none }}"
+ },
+ {
+ "page": "Example/P0914/Q.2",
+ "contents": "{{#ask: [[Runtime benchmark.ID::+]] OR [[Runtime benchmark.Runtime::+]] <q> [[Runtime benchmark.ID::+]]</q> |?Runtime benchmark |?Runtime benchmark.Memory used#- |?Runtime benchmark.ID |?Runtime benchmark.Runtime|?Runtime benchmark.Benchmark notes |format=broadtable |sort=Runtime benchmark.ID,Runtime benchmark.Runtime |order=desc |limit=2 |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (no `Error: 1066 Not unique table/alias ...`)",
+ "subject": "Example/P0914/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_ASK",
+ "_SKEY"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Runtime-benchmark smwtype_ref_rec\">27 November 2016</td><td class=\"Memory-used smwtype_num\" data-sort-value=\"16690400\">16690400</td><td class=\"ID smwtype_num\" data-sort-value=\"200\">200</td><td class=\"Runtime smwtype_num\" data-sort-value=\"48.35\">48.35</td><td class=\"Benchmark-notes smwtype_wpg\">N/a</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#0 (no `Error: 1066 Not unique table/alias ...`)",
+ "subject": "Example/P0914/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Runtime-benchmark smwtype_ref_rec\">27 November 2016</td><td class=\"Memory-used smwtype_num\" data-sort-value=\"16690400\">16690400</td><td class=\"ID smwtype_num\" data-sort-value=\"200\">200</td><td class=\"Runtime smwtype_num\" data-sort-value=\"48.35\">48.35</td><td class=\"Benchmark-notes smwtype_wpg\">N/a</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQFilterDuplicates": true,
+ "smwgQueryResultCacheType": "hash",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0915.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0915.json
new file mode 100644
index 00000000..413e9740
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0915.json
@@ -0,0 +1,112 @@
+{
+ "description": "Test category redirect (`SMW_CAT_REDIRECT`)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Example/P0915",
+ "contents": "#REDIRECT [[Example/P0915/1]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Example/P0915/SubCat",
+ "contents": "[[Category:Example/P0915]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Example/P0915/ValidCat",
+ "contents": "#REDIRECT [[:Category:Example/P0915/ValidCat2]]"
+ },
+ {
+ "page": "Example/P0915/1",
+ "contents": "[[Category:Example/P0915]]"
+ },
+ {
+ "page": "Example/P0915/2",
+ "contents": "[[Category:Example/P0915/ValidCat]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 track category with invalid (NS_MAIN) redirect target",
+ "subject": "Example/P0915/1",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 track sub-category",
+ "namespace": "NS_CATEGORY",
+ "subject": "Example/P0915/SubCat",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_ERRC"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 valid cat redirect, dsiplay the redirect target",
+ "subject": "Example/P0915/2",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "_INST"
+ ],
+ "propertyValues": [
+ "Example/P0915/ValidCat2#14##"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCategoryFeatures": [
+ "SMW_CAT_INSTANCE",
+ "SMW_CAT_REDIRECT",
+ "SMW_CAT_HIERARCHY"
+ ],
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0916.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0916.json
new file mode 100644
index 00000000..6244483a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-0916.json
@@ -0,0 +1,62 @@
+{
+ "description": "Test `_ref_rec` with a `_eid` field (#2985)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "WDID",
+ "contents": "[[Has type::External identifier]][[External formatter uri::https://www.wikidata.org/entity/$1]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "WD reference",
+ "contents": "[[Has type::Reference]] [[Has fields::URL;WDID]]"
+ },
+ {
+ "page": "Example/P0916/1",
+ "contents": "[[WD reference::https://en.wikipedia.org/wiki/Franz_Schubert;Q7312]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (correct formatting for external identifier)",
+ "subject": "Example/P0916/1",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "WD reference"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "data-content=\"&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;.*:WDID&quot;",
+ "title=&quot;.*:WDID&quot;&gt;WDID&lt;/a&gt;: &lt;a href=&quot;https://www.wikidata.org/entity/Q7312&quot; target=&quot;_blank&quot;&gt;Q7312&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;\" title=\"WDID: Q7312\">"
+ ],
+ "not-contain": [
+ "title=&quot;.*:WDID&quot;&gt;WDID&lt;/a&gt;: &lt;span class=&quot;plainlinks smw-eid&quot;&gt;<a rel=\"nofollow\" class=\"external text\" href=\"https://www.wikidata.org/entity/Q7312\">Q7312</a>&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;\" title=\"WDID: <a rel=\"nofollow\" class=\"external text\" href=\"https://www.wikidata.org/entity/Q7312\">Q7312</a>\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1000.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1000.json
new file mode 100644
index 00000000..52a5271c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1000.json
@@ -0,0 +1,127 @@
+{
+ "description": "Test property page with redirect(synonym)/displayTitle (`wgContLang=en`, `wgLang=en`, `wgAllowDisplayTitle`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has synonym",
+ "contents": "[[Has type::Page]] {{DISPLAYTITLE:SomeTest}}"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Some redirect",
+ "contents": "[[Has type::Page]] {{DISPLAYTITLE:SomePropertyRedirectTest}}"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Some redirect",
+ "contents": "#REDIRECT [[Property:Has synonym]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Some redirect2",
+ "contents": "[[Has type::Page]] {{DISPLAYTITLE:IsRedirectTarget}}"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Some redirect2",
+ "contents": "[[Has type::Page]] {{DISPLAYTITLE:SomeTest2}}",
+ "move-to":{
+ "target": "Has synonym2",
+ "is-redirect": true
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (manual redirect)",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has synonym",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_TYPE",
+ "_MDAT",
+ "_DTITLE",
+ "_SKEY"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ],
+ "propertyValues": [
+ "Some_redirect#102##"
+ ]
+ }
+ }
+ },
+ "assert-output": {
+ "onPageView": true,
+ "to-contain": [
+ "class=\"mw-redirect\" title=\"Property:Some redirect\">Property:Some redirect</a>"
+ ],
+ "not-contain": [
+ "class=\"mw-redirect\" title=\"Property:Some redirect\">SomeTest</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has synonym2",
+ "store": {
+ "clear-cache": true
+ },
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 4,
+ "propertyKeys": [
+ "_TYPE",
+ "_MDAT",
+ "_DTITLE",
+ "_SKEY"
+ ],
+ "incoming": {
+ "propertyKeys": [
+ "_REDI"
+ ],
+ "propertyValues": [
+ "Some_redirect2#102##"
+ ]
+ }
+ }
+ },
+ "assert-output": {
+ "onPageView": true,
+ "to-contain": [
+ "class=\"mw-redirect\" title=\"Property:Some redirect2\">Property:Some redirect2</a>"
+ ],
+ "not-contain": [
+ "class=\"mw-redirect\" title=\"Property:Some redirect2\">SomeTest2</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgAllowDisplayTitle": true,
+ "wgRestrictDisplayTitle": false,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1001.json
new file mode 100644
index 00000000..7f4135f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1001.json
@@ -0,0 +1,97 @@
+{
+ "description": "Test property page with parameters (#2479, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page test",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P1001/1",
+ "contents": "[[Has page test::One]]"
+ },
+ {
+ "page": "Example/P1001/2",
+ "contents": "[[Has page test::Two]]"
+ },
+ {
+ "page": "Example/P1001/3",
+ "contents": "[[Has page test::Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has page test",
+ "assert-output": {
+ "onPageView": {
+ "parameters": {
+ "limit": "1",
+ "offset": "1"
+ }
+ },
+ "to-contain": [
+ "Has_page_test&amp;limit=1&amp;offset=0",
+ "title=\"Example/P1001/2\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (see issue #2479)",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has page test",
+ "assert-output": {
+ "onPageView": {
+ "parameters": {
+ "limit": "1foo",
+ "offset": "1"
+ }
+ },
+ "to-contain": [
+ "Has_page_test&amp;limit=1&amp;offset=0",
+ "title=\"Example/P1001/2\""
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (use a filter, #2878)",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has page test",
+ "assert-output": {
+ "onPageView": {
+ "parameters": {
+ "filter": "Foo"
+ }
+ },
+ "to-contain": [
+ "=&amp;filter=Foo",
+ "title=\"Example/P1001/3\""
+ ],
+ "not-contain": [
+ "Example/P1001/1",
+ "Example/P1001/2"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1002.json
new file mode 100644
index 00000000..d938ae36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1002.json
@@ -0,0 +1,44 @@
+{
+ "description": "Test property page with improper assignment list (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "page": "Example/P1002/1",
+ "contents": "[[Has number::abc]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has number",
+ "assert-output": {
+ "onPageView": true,
+ "to-contain": [
+ "<ul><li><a href=\".*Example/P1002/1#_ERR825bfe376713b81d8177c7353181ec6e\".*title=\"Special:Browse/:Example-2FP1002-2F1-23-5FERR825bfe376713b81d8177c7353181ec6e\">+</a></span></li></ul>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1003.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1003.json
new file mode 100644
index 00000000..78d75e0e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1003.json
@@ -0,0 +1,72 @@
+{
+ "description": "Test property restriction on annotation and #ask (`wgContLang=en`, `wgLang=en`, `smwgCreateProtectionRight`)",
+ "setup": [
+ {
+ "page": "Example/P1003/1",
+ "contents": "[[Has unknown::abc]]"
+ },
+ {
+ "page": "Example/P1003/2",
+ "contents": "{{#ask: [[Has unknown::123]] |format=table |limit=10 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 on annotation attempt",
+ "subject": "Example/P1003/1#_ERR504f58c6ccf85a3bb8ccd4562463664a",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRP",
+ "_ERRT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "Has_unknown#102##",
+ "[8,\"smw-datavalue-property-create-restriction\",\"Has unknown\",\"foo\"]"
+ ]
+ }
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 on #ask attempt",
+ "subject": "Example/P1003/2#_ERR85d779fcd4429a44d5f9dd85e292a953",
+ "assert-store": {
+ "semantic-data": {
+ "strict-mode-valuematch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_ERRP",
+ "_ERRT",
+ "_SKEY"
+ ],
+ "propertyValues": [
+ "Has_unknown#102##",
+ "[2,\"smw-datavalue-property-create-restriction\",\"Has unknown\",\"foo\"]"
+ ]
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCreateProtectionRight": "foo",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1004.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1004.json
new file mode 100644
index 00000000..f1b50279
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1004.json
@@ -0,0 +1,59 @@
+{
+ "description": "Test different default output formatter `_dat` (`smwgDefaultOutputFormatters`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date 2",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/P1004/1",
+ "contents": "[[Has date::1 Jan 1970]] [[Has date 2::2 Jan 2000]] [[Category:P1004]]"
+ },
+ {
+ "page": "Example/P1004/Q.1",
+ "contents": "{{#ask: [[Category:P1004]] |?Modification date |?Has date |?Has date 2 |?Has date#ISO |limit=10 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (all `_dat` (Has date 2) -> JD, `_MDAT` (Modification date) -> ISO, First `Has date` -> MEDIAWIKI, Second `Has date` overrides default with ISO)",
+ "subject": "Example/P1004/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Modification-date smwtype_dat\" data-sort-value=.*T.*</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2440587.5\">1 January 1970</td>",
+ "<td class=\"Has-date-2 smwtype_dat\" data-sort-value=\"2451545.5\">2451545.5</td>",
+ "<td class=\"Has-date smwtype_dat\" data-sort-value=\"2440587.5\">1970-01-01</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgDefaultOutputFormatters": {
+ "_dat": "JD",
+ "_MDAT": "ISO",
+ "Has date": "MEDIAWIKI"
+ },
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1005.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1005.json
new file mode 100644
index 00000000..0a371487
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1005.json
@@ -0,0 +1,53 @@
+{
+ "description": "Test property page with parameters/sort",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page test",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P1005/A",
+ "contents": "[[Has page test::Sort]] {{DEFAULTSORT:XXX}}"
+ },
+ {
+ "page": "Example/P1005/B",
+ "contents": "[[Has page test::Sort]] {{DEFAULTSORT:AAA}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (use a filter with correct sorting )",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has page test",
+ "assert-output": {
+ "onPageView": {
+ "parameters": {
+ "filter": "Sort"
+ }
+ },
+ "to-contain": [
+ "<div class=\"smw-table-cell header-title\"><div id=\"A\">A</div>.*<div class=\"smw-table-cell smwpropname\" data-list-index=\"0\"><a href=\".*/Example/P1005/B\" title=\"Example/P1005/B\">Example/P1005/B</a>",
+ "<div class=\"smw-table-cell header-title\"><div id=\"X\">X</div>.*<div class=\"smw-table-cell smwpropname\" data-list-index=\"1\"><a href=\".*/Example/P1005/A\" title=\"Example/P1005/A\">Example/P1005/A</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1006.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1006.json
new file mode 100644
index 00000000..1ad73906
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1006.json
@@ -0,0 +1,78 @@
+{
+ "description": "Test property page sorting (`wgRestrictDisplayTitle`, `smwgEntityCollation`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page test",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P1006/1",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:AA}}"
+ },
+ {
+ "page": "Example/P1006/2",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:AA}} {{#subobject: |Has page test=456 |@sortkey=AA}}"
+ },
+ {
+ "page": "Example/P1006/3",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:BB}}"
+ },
+ {
+ "page": "Example/P1006/4",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:CC}} {{#subobject: |Has page test=456 |@sortkey=BB}}"
+ },
+ {
+ "page": "Example/P1006/5",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:DD}} {{#subobject: |Has page test=456 |@sortkey=DD}}"
+ },
+ {
+ "page": "Example/P1006/6",
+ "contents": "[[Has page test::123]] {{DISPLAYTITLE:DD}} {{#subobject: |Has page test=456 |@sortkey=DD}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (verify item list order)",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has page test",
+ "assert-output": {
+ "onPageView": {
+ "parameters": {}
+ },
+ "to-contain": [
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"0\"><a href=.*Example/P1006/1\" title=\"Example/P1006/1\">AA</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"1\"><a href=.*Example/P1006/2\" title=\"Example/P1006/2\">AA</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"2\"><a href=.*Example/P1006/2#_14d8b802a338f5900237277114a2cc2f\" .*title=\"Example/P1006/2\".*>AA</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"3\"><a href=.*Example/P1006/3\" title=\"Example/P1006/3\">BB</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"4\"><a href=.*Example/P1006/4#_6961cf334b7bc5632473cf15ebfc3094\" .*title=\"Example/P1006/4\".*>CC</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"5\"><a href=.*Example/P1006/4\" title=\"Example/P1006/4\">CC</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"6\"><a href=.*Example/P1006/5\" title=\"Example/P1006/5\">DD</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"7\"><a href=.*Example/P1006/5#_e4e502c29803cb65c90fa99b6abd99da\" .*title=\"Example/P1006/5\".*>DD</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"8\"><a href=.*Example/P1006/6\" title=\"Example/P1006/6\">DD</a>",
+ "<div class=\"smw-table-cell smwpropname\" data-list-index=\"9\"><a href=.*Example/P1006/6#_e4e502c29803cb65c90fa99b6abd99da\" .*title=\"Example/P1006/6\".*>DD</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgAllowDisplayTitle": true,
+ "wgRestrictDisplayTitle": false,
+ "smwgEntityCollation": "identity",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1007.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1007.json
new file mode 100644
index 00000000..4d2a2243
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/p-1007.json
@@ -0,0 +1,57 @@
+{
+ "description": "Test sorting on Pages will not exclude non-existent pages from result (#540)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "knows page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/P1007/1",
+ "contents": "[[knows page::Example/P1007/Ref 3]]"
+ },
+ {
+ "page": "Example/P1007/2",
+ "contents": "[[knows page::Example/P1007/Ref 2]]"
+ },
+ {
+ "page": "Example/P1007/3",
+ "contents": "[[knows page::Example/P1007/Ref 1]]"
+ },
+ {
+ "page": "Example/P1007/Ref 1",
+ "contents": "SomeContent"
+ },
+ {
+ "page": "Example/P1007/Ref 3",
+ "contents": "SomeContent"
+ },
+ {
+ "page": "Example/P1007/Test",
+ "contents": "{{#ask:[[knows page::+]]|?knows page |sort=knows page|format=plainlist}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (verify pages referencing non-existent pages are included in the result)",
+ "subject": "Example/P1007/Test",
+ "assert-output": {
+ "to-contain": "Example/P1007/3.*Example/P1007/2.*Example/P1007/1"
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0101.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0101.json
new file mode 100644
index 00000000..cc56a50b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0101.json
@@ -0,0 +1,127 @@
+{
+ "description": "Test `_txt` query for simple assignments, NS_HELP, and special chars",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has blob property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has another blob property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "abc-123&^*",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Examples/Q0101/1",
+ "contents": "[[Has blob property::Value One]], [[Has another blob property::Value One]]"
+ },
+ {
+ "page": "Examples/Q0101/2",
+ "namespace": "NS_HELP",
+ "contents": "[[Has blob property::Value Two]], [[Has another blob property::Value Two]]"
+ },
+ {
+ "page": "Examples/Q0101/3",
+ "contents": "[[Has text::foo-123#&^*%<1?=/->\"']]"
+ },
+ {
+ "page": "Examples/Q0101/4",
+ "contents": "[[abc-123&^*::foo-123#&^*%<1?=/->\"']]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 Simple disjunctive blob query",
+ "condition": "[[Has blob property::Value One]] OR [[Has another blob property::Value Two]]",
+ "printouts": [
+ "Has blob property"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Examples/Q0101/1#0##",
+ "Examples/Q0101/2#12##"
+ ],
+ "count": "2",
+ "dataitems": [
+ {
+ "type": "_txt",
+ "value": "Value Two"
+ },
+ {
+ "type": "_txt",
+ "value": "Value One"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 Simple conjunctive blob query",
+ "condition": "[[Has blob property::Value One]][[Has another blob property::Value One]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Examples/Q0101/1#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 with special chars",
+ "condition": "[[Has text::foo-123#&^*%<1?=/->\"']]",
+ "parameters" : {
+ "limit" : "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Examples/Q0101/3#0##"
+ ],
+ "count": 1
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 with special chars",
+ "condition": "[[abc-123&^*::foo-123#&^*%<1?=/->\"']]",
+ "parameters" : {
+ "limit" : "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Examples/Q0101/4#0##"
+ ],
+ "count": 1
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_HELP": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0102.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0102.json
new file mode 100644
index 00000000..e8d8fed6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0102.json
@@ -0,0 +1,263 @@
+{
+ "description": "Test `_txt` for `~*` regex queries to validate correct escape pattern as applied in the `QueryEngine`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has FirstRegexBlobSearchProperty",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has SecondRegexBlobSearchProperty",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern",
+ "contents": "[[Has FirstRegexBlobSearchProperty::{(+*. \\;)}]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern",
+ "contents": "{{#set:|Has FirstRegexBlobSearchProperty=[(*. \\=^)\"]}} {{#subobject:|Has SecondRegexBlobSearchProperty=[(+. \\:)]-|}}"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "RequiresSuperSpecialEscapePattern",
+ "contents": "{{#set:|Has FirstRegexBlobSearchProperty=Foo(\"#^$&-/)-|}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 Left curly brackets",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*{*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 Right curly brackets",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*}*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 Left parenthesis",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*(*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RequiresSuperSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "3"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 Disjunctive left parenthesis",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*(*]] OR [[Has SecondRegexBlobSearchProperty::~*(*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RequiresSuperSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##_4726701f0e7d838d922ac6b9414558dc"
+ ],
+ "count": "4"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 Right parenthesis",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*)*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RequiresSuperSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "3"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 Disjunctive right parenthesis",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*)*]] OR [[Has SecondRegexBlobSearchProperty::~*)*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RequiresSuperSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##_4726701f0e7d838d922ac6b9414558dc"
+ ],
+ "count": "4"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 Left square brackets",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*[*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 Right square brackets",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*]*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*;?}]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9",
+ "condition": "[[Has SecondRegexBlobSearchProperty::~*+.*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##_4726701f0e7d838d922ac6b9414558dc"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*=^*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#11 Composite regex search",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*{*]][[Has FirstRegexBlobSearchProperty::~*}*]][[Has FirstRegexBlobSearchProperty::~*)*]] OR [[Has SecondRegexBlobSearchProperty::~*)*]] OR [[Has FirstRegexBlobSearchProperty::~*=^*]][[Has SecondRegexBlobSearchProperty::~*+.*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RegexTextAnnotationForCharactersThatRequireSpecialEscapePattern#0##",
+ "RegexParserAnnotationForCharactersThatRequireSpecialEscapePattern#0##_4726701f0e7d838d922ac6b9414558dc"
+ ],
+ "count": "2"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#12 Pipe regex",
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*-|*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RequiresSuperSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#13 Requires special escaping effort",
+ "skip-on": {
+ "elastic": "`/` would require escaping but at the same would make it invisible to be used as subpage Foo/Boo divider on other searches"
+ },
+ "condition": "[[Has FirstRegexBlobSearchProperty::~*Foo(\"#^$&-/)-|*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "RequiresSuperSpecialEscapePattern#0##"
+ ],
+ "count": "1"
+ }
+ }
+ ],
+ "settings": {
+ "smwgQMaxSize": "25"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0103.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0103.json
new file mode 100644
index 00000000..7f31069c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0103.json
@@ -0,0 +1,223 @@
+{
+ "description": "Test `_txt` for `~*` regex query with the condition to include the `\\` escape character (skip sqlite, postgres)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has regexblob property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has simple text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Escape character blob subject-a",
+ "contents": "{{#set:|Has regexblob property=[(+*. \\.)']}} {{#subobject:FixedSubobjectName|Has regexblob property=[(+* \\:)]\\\\\"}}"
+ },
+ {
+ "page": "Escape character blob subject-b",
+ "contents": "{{#set:|Has regexblob property=[(+*. \\;)\\\\']\\\"}} {{#subobject:FixedSubobjectName|Has regexblob property=[(+* \\=)]\\\\\"\\\"}}"
+ },
+ {
+ "page": "Escape character blob subject-c",
+ "contents": "[[Has regexblob property::Foo(\\\"'#^$&-/)]]"
+ },
+ {
+ "page": "BlobValue with whitespaces",
+ "contents": "[[Has simple text:: +/-nuclear atypia ]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has regexblob property::~*.*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-a#0##",
+ "Escape character blob subject-b#0##"
+ ],
+ "count": "2"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has regexblob property::~*\\'*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-b#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has regexblob property::~*'*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-b#0##",
+ "Escape character blob subject-a#0##",
+ "Escape character blob subject-c#0##"
+ ],
+ "count": "3"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Has regexblob property::~*\\\\'*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-b#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Has regexblob property::~*\\\\\"*]]",
+ "printouts": [
+ "Has regexblob property"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-a#0##FixedSubobjectName",
+ "Escape character blob subject-b#0##FixedSubobjectName"
+ ],
+ "count": "2"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Has regexblob property::~*\\\\\"\\\"*]]",
+ "skip-on": {
+ "elastic": "Finding more than one due to the wildcard"
+ },
+ "printouts": [
+ "Has regexblob property"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-b#0##FixedSubobjectName"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Has regexblob property::~*\\\"*]]",
+ "printouts": [
+ "Has regexblob property"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-a#0##FixedSubobjectName",
+ "Escape character blob subject-b#0##",
+ "Escape character blob subject-b#0##FixedSubobjectName",
+ "Escape character blob subject-c#0##"
+ ],
+ "count": "4"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[Has regexblob property::~*\\.*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-a#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8",
+ "condition": "[[Has regexblob property::~*\\:*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-a#0##FixedSubobjectName"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9",
+ "condition": "[[Has regexblob property::~*Foo(\\\"'#^$&-/)*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Escape character blob subject-c#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10 find subject even when value annotation contained whitespaces",
+ "condition": "[[Has simple text::+/-nuclear atypia]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "BlobValue with whitespaces#0##"
+ ],
+ "count": "1"
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "skip-on": {
+ "sqlite": "Requires special \\ escape sequence on sqlite"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0104.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0104.json
new file mode 100644
index 00000000..9e863e0f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0104.json
@@ -0,0 +1,274 @@
+{
+ "description": "Test `_txt`/`~` with enabled full-text search support (only enabled for MySQL, SQLite)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/Q0104/1",
+ "contents": "{{#subobject: |Has text=MySQL vs MariaDB database}} {{#subobject: |Has text=Oracle vs MariaDB database}} {{#subobject: |Has text=PostgreSQL vs MariaDB database and more of}} {{#subobject: |Has text=MariaDB overview}}"
+ },
+ {
+ "page": "Example/Q0104/2",
+ "contents": "{{#subobject: |Has text=Elastic search}} {{#subobject: |Has text=Sphinx search}}"
+ },
+ {
+ "page": "Example/Q0104/3",
+ "contents": "{{#subobject: |Has text=...a hyphenated phrase that has special significance when it appears at the beginning of text...}} {{#subobject: |Has text=...a hyphenated phrase that has NOT any special significance when it appears at the beginning of text...}}"
+ },
+ {
+ "page": "Example/Q0104/4",
+ "contents": "{{#subobject: |Has text=Text with a category|@category=Q0104}} {{#subobject: |Has text=Text without a category}}"
+ }
+ ],
+ "beforeTest": {
+ "job-run": [
+ "SMW\\SearchTableUpdateJob"
+ ]
+ },
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 with boolean include/not include",
+ "condition": "[[Has text::~+MariaDB -database]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/1#0##_55b020117d21e4b7967cf5bf78cf6b32"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has text::~+database]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0104/1#0##_7980f674d00e51fbc91cc663a43054c5",
+ "Example/Q0104/1#0##_507a001e2e9e0e7659aa33e8c4b2d471",
+ "Example/Q0104/1#0##_1a377960fd7e6c287e97c519278761b4"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (plus sorting)",
+ "condition": "[[Has text::~DATA*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_text": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0104/1#0##_1a377960fd7e6c287e97c519278761b4",
+ "Example/Q0104/1#0##_507a001e2e9e0e7659aa33e8c4b2d471",
+ "Example/Q0104/1#0##_7980f674d00e51fbc91cc663a43054c5"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (plus sorting)",
+ "condition": "[[Has text::~sear*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_text": "ASC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0104/2#0##_977ae79c0c362dc7a2ceaf94859178e7",
+ "Example/Q0104/2#0##_29a504fbd2492700363d5491902958e9"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 include/not include",
+ "condition": "[[Has text::~sear*, -elas*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_text": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/2#0##_29a504fbd2492700363d5491902958e9"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 same as #4",
+ "condition": "[[Has text::!~elastic*, +sear*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/2#0##_29a504fbd2492700363d5491902958e9"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 phrase matching",
+ "condition": "[[Has text::~\"phrase that has special\"]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/3#0##_1aa6fcbe946e44dc061627ce545e0cd9"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 similar to #5 but not used in phrase matching mode",
+ "condition": "[[Has text::~phrase that has special]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0104/3#0##_1aa6fcbe946e44dc061627ce545e0cd9",
+ "Example/Q0104/3#0##_682a8c9f798c14548a4599b68d83b78e"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8 free search (wide proximity)",
+ "skip-on": {
+ "sqlite": "works different in comparison to MySQL, see #9",
+ "elastic": "Uses a multi_field phrase match"
+ },
+ "condition": "[[~~with a category]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0104/4#0##_5a524a435267f6e6d2d45d64a419c1da",
+ "Example/Q0104/4#0##_9f7e6f010523ae7a6d1639d40773e379"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8.elastic free search (wide proximity)",
+ "skip-on": {
+ "elastic": [ "not", "Different behaviour as SQL on the exact phrase `with a category` " ]
+ },
+ "condition": "[[~~with a category]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/4#0##_9f7e6f010523ae7a6d1639d40773e379"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9 free search (wide proximity)",
+ "condition": "[[~~with* a category]] [[~Example/Q0104/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0104/4#0##_5a524a435267f6e6d2d45d64a419c1da",
+ "Example/Q0104/4#0##_9f7e6f010523ae7a6d1639d40773e379"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10 free search (wide proximity)",
+ "condition": "[[~~with a category]] [[Category:Q0104]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0104/4#0##_9f7e6f010523ae7a6d1639d40773e379"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#11 retain spaces on +/- operators",
+ "condition": "[[Has text::~+*maria* -postgres*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0104/1#0##_7980f674d00e51fbc91cc663a43054c5",
+ "Example/Q0104/1#0##_507a001e2e9e0e7659aa33e8c4b2d471",
+ "Example/Q0104/1#0##_55b020117d21e4b7967cf5bf78cf6b32"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEnabledFulltextSearch": true,
+ "smwgFulltextDeferredUpdate": false,
+ "smwgFulltextSearchIndexableDataTypes": [
+ "SMW_FT_BLOB",
+ "SMW_FT_URI"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "Fulltext not supported by PostgreSQL.",
+ "sesame": "Fulltext not supported by SPARQLStore (Sesame).",
+ "virtuoso": "Fulltext not supported by SPARQLStore (Virtuoso).",
+ "fuseki": "Fulltext not supported by SPARQLStore (Fuskei).",
+ "blazegraph": "Fulltext not supported by SPARQLStore (Blazegraph)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0105.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0105.json
new file mode 100644
index 00000000..ba946ead
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0105.json
@@ -0,0 +1,338 @@
+{
+ "description": "Test `_wpg`/`~` with enabled full-text search support (only enabled for MySQL, SQLite, `SMW_FT_WIKIPAGE`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/Q0105/1",
+ "contents": "{{#subobject: |Has page=MySQL vs MariaDB database}} {{#subobject: |Has page=Oracle vs MariaDB database}} {{#subobject: |Has page=PostgreSQL vs MariaDB database and more of}} {{#subobject: |Has page=MariaDB overview}}"
+ },
+ {
+ "page": "Example/Q0105/2",
+ "contents": "{{#subobject: |Has page=Elastic search}} {{#subobject: |Has page=Sphinx search}}"
+ },
+ {
+ "page": "Example/Q0105/3",
+ "contents": "{{#subobject: |Has page=...a hyphenated phrase that has special significance when it appears at the beginning of text...}} {{#subobject: |Has page=...a hyphenated phrase that has NOT any special significance when it appears at the beginning of text...}}"
+ },
+ {
+ "page": "Example/Q0105/4",
+ "contents": "{{#subobject: |Has page=Text with a category|@category=Q0105}} {{#subobject: |Has page=Text without a category}}"
+ }
+ ],
+ "beforeTest": {
+ "job-run": [
+ "SMW\\SearchTableUpdateJob"
+ ]
+ },
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 with boolean include/not include",
+ "condition": "[[Has page::~+MariaDB -database]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0105/1#0##_13bfd7da17ee73035f210b926e671c83"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has page::~+database]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0105/1#0##_d1e52a56041afb2795ebc6683f9a5229",
+ "Example/Q0105/1#0##_e64ad35fdcd8d9e99f2db9a815e36be3",
+ "Example/Q0105/1#0##_4b9aef2f903c80012ed4000b519a039d"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (plus sorting)",
+ "condition": "[[Has page::~DATA*]]",
+ "skip-on": {
+ "elastic": "ES, not expect a prefix search without an explicit wildcard (*)."
+ },
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0105/1#0##_d1e52a56041afb2795ebc6683f9a5229",
+ "Example/Q0105/1#0##_e64ad35fdcd8d9e99f2db9a815e36be3",
+ "Example/Q0105/1#0##_4b9aef2f903c80012ed4000b519a039d"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2.elastic (plus sorting)",
+ "condition": "[[Has page::~*DATA*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0105/1#0##_d1e52a56041afb2795ebc6683f9a5229",
+ "Example/Q0105/1#0##_e64ad35fdcd8d9e99f2db9a815e36be3",
+ "Example/Q0105/1#0##_4b9aef2f903c80012ed4000b519a039d"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (plus sorting)",
+ "condition": "[[Has page::~sear*]]",
+ "skip-on": {
+ "elastic": "ES, not expect a prefix search without an explicit wildcard (*)."
+ },
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page": "ASC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/2#0##_4e8a7e81b361d0e14670809d9a1ff614",
+ "Example/Q0105/2#0##_7927dc49ee111e0929a91a1f357d9906"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3.elastic (plus sorting)",
+ "condition": "[[Has page::~*sear*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page": "ASC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/2#0##_4e8a7e81b361d0e14670809d9a1ff614",
+ "Example/Q0105/2#0##_7927dc49ee111e0929a91a1f357d9906"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 include/not include",
+ "condition": "[[Has page::~sear*, -elas*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0105/2#0##_7927dc49ee111e0929a91a1f357d9906"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 same as #4",
+ "condition": "[[Has page::!~elastic*, +sear*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0105/2#0##_7927dc49ee111e0929a91a1f357d9906"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 phrase matching",
+ "condition": "[[Has page::~\"phrase that has special\"]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0105/3#0##_6b717d3124b3d294797a43ed5064374c"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 similar to #5 but not used in phrase matching mode",
+ "condition": "[[Has page::~phrase that has special]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/3#0##_6b717d3124b3d294797a43ed5064374c",
+ "Example/Q0105/3#0##_1dd972ef96528fe09e971c88ed933a7a"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8 free search (wide proximity)",
+ "skip-on": {
+ "elastic": "ES matches only `with` not `without`",
+ "sqlite": "works different in comparison to MySQL, see #9"
+ },
+ "condition": "[[~~with a category]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/4#0##_9f1222a5f6669be2c5f76dd5af08a4dc",
+ "Example/Q0105/4#0##_b827401b701c08c3c3d610a3a43d18c3"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8.elastic, free search (wide proximity, also match the annotated value)",
+ "skip-on": {
+ "elastic": [ "not", "See #8" ]
+ },
+ "condition": "[[~~with a category]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/4#0##_9f1222a5f6669be2c5f76dd5af08a4dc",
+ "Text_with_a_category#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9 free search (wide proximity)",
+ "condition": "[[~~with* a category]] [[~Example/Q0105/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0105/4#0##_9f1222a5f6669be2c5f76dd5af08a4dc",
+ "Example/Q0105/4#0##_b827401b701c08c3c3d610a3a43d18c3"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10 free search (wide proximity)",
+ "condition": "[[~~with a category]] [[Category:Q0105]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0105/4#0##_9f1222a5f6669be2c5f76dd5af08a4dc"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#11 retain spaces on +/- operators",
+ "condition": "[[Has page::~+*maria* -postgres*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0105/1#0##_d1e52a56041afb2795ebc6683f9a5229",
+ "Example/Q0105/1#0##_e64ad35fdcd8d9e99f2db9a815e36be3",
+ "Example/Q0105/1#0##_13bfd7da17ee73035f210b926e671c83"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#12 same as #11 only with `,` (required by MySQL 5.7/MariaDB 10.1)",
+ "condition": "[[Has page::~+*maria*, -postgres*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0105/1#0##_d1e52a56041afb2795ebc6683f9a5229",
+ "Example/Q0105/1#0##_e64ad35fdcd8d9e99f2db9a815e36be3",
+ "Example/Q0105/1#0##_13bfd7da17ee73035f210b926e671c83"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEnabledFulltextSearch": true,
+ "smwgFulltextDeferredUpdate": false,
+ "smwgFulltextSearchIndexableDataTypes": [
+ "SMW_FT_BLOB",
+ "SMW_FT_URI",
+ "SMW_FT_WIKIPAGE"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "Fulltext not supported by PostgreSQL.",
+ "sesame": "Fulltext not supported by SPARQLStore (Sesame).",
+ "virtuoso": "Fulltext not supported by SPARQLStore (Virtuoso).",
+ "fuseki": "Fulltext not supported by SPARQLStore (Fuskei).",
+ "blazegraph": "Fulltext not supported by SPARQLStore (Blazegraph)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0106.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0106.json
new file mode 100644
index 00000000..6e5a65ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0106.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test `_txt`/`~` with enabled full-text search support on fixed user property (only enabled for MySQL, SQLite, `smwgFixedProperties`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has fixed text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has fixed page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/Q0106/1",
+ "contents": "{{#subobject: |Has fixed text=MySQL vs MariaDB database}} {{#subobject: |Has fixed text=Oracle vs MariaDB database}} {{#subobject: |Has fixed text=PostgreSQL vs MariaDB database and more of}} {{#subobject: |Has fixed text=MariaDB overview}}"
+ },
+ {
+ "page": "Example/Q0106/2",
+ "contents": "{{#subobject: |Has fixed page=Elastic search}} {{#subobject: |Has fixed page=Sphinx search}}"
+ }
+ ],
+ "beforeTest": {
+ "maintenance-run": {
+ "rebuildData": {
+ "page": "Property:Has fixed text|Property:Has fixed page|Example/Q0106/1|Example/Q0106/2"
+ }
+ }
+ },
+ "tests": [
+ {
+ "type": "query",
+ "store": {
+ "clear-cache": true
+ },
+ "about": "#0 on fixed blob user property",
+ "condition": "[[Has fixed text::~+MariaDB -database]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0106/1#0##_29f6aa337d1dc1e2f79376c7940aeab3"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "store": {
+ "clear-cache": true
+ },
+ "about": "#1 on fixed page user property",
+ "condition": "[[Has fixed page::~*search*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0106/2#0##_39c07b506d692088c97dc0d1cbfd67e6",
+ "Example/Q0106/2#0##_89e5e037d244e08b050101bb8ddb7768"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEnabledFulltextSearch": true,
+ "smwgFulltextDeferredUpdate": false,
+ "smwgFulltextSearchIndexableDataTypes": [
+ "SMW_FT_BLOB",
+ "SMW_FT_URI",
+ "SMW_FT_WIKIPAGE"
+ ],
+ "smwgFixedProperties": [
+ "Has_fixed_text",
+ "Has fixed page"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "Fulltext not supported by PostgreSQL.",
+ "sesame": "Fulltext not supported by SPARQLStore (Sesame).",
+ "virtuoso": "Fulltext not supported by SPARQLStore (Virtuoso).",
+ "fuseki": "Fulltext not supported by SPARQLStore (Fuskei).",
+ "blazegraph": "Fulltext not supported by SPARQLStore (Blazegraph)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0201.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0201.json
new file mode 100644
index 00000000..a1d404ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0201.json
@@ -0,0 +1,130 @@
+{
+ "description": "Test `_CONC` queries (skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Population",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has concept description",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Page one",
+ "contents": "[[Population::1001]], [[Has concept description::Bar]]"
+ },
+ {
+ "page": "Page two",
+ "contents": "[[Has concept description::Foo]], [[Population::9999]]"
+ },
+ {
+ "page": "Page three",
+ "contents": "[[Has concept description::Bar]]"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept for any value selection",
+ "contents": "{{#concept: [[Population::+]][[Has concept description::+]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept for any broader value selection",
+ "contents": "{{#concept: [[Has concept description::+]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept for distinct value selection",
+ "contents": "{{#concept: [[Has concept description::Foo]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Composite concept for a conjunctive condition",
+ "contents": "{{#concept: [[Concept:Concept for any value selection]][[Concept:Concept for distinct value selection]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Composite concept for a disjunctive condition",
+ "contents": "{{#concept: [[Concept:Concept for any value selection]] OR [[Concept:Concept for distinct value selection]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "concept",
+ "about": "#0 Simple concept member list",
+ "condition": "[[Concept:Concept for any value selection]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept for any value selection",
+ "count": "2"
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#1 Simple concept distinct member list",
+ "condition": "[[Concept:Concept for distinct value selection]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept for distinct value selection",
+ "count": "1"
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#2 Composite concept set by conjunctive conditions",
+ "condition": "[[Concept:Composite concept for a conjunctive condition]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1"
+ },
+ "conceptcache": [
+ {
+ "concept": "Composite concept for a conjunctive condition",
+ "count": "1"
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#3 Composite concept set by disjunctive conditions",
+ "condition": "[[Concept:Composite concept for a disjunctive condition]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2"
+ },
+ "conceptcache": [
+ {
+ "concept": "Composite concept for a disjunctive condition",
+ "count": "2"
+ }
+ ]
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso reports 'SPARQL compiler: Blank node ... is not allowed in a constant clause', see https://github.com/openlink/virtuoso-opensource/issues/126"
+ },
+ "version": "2",
+ "is-incomplete": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0202.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0202.json
new file mode 100644
index 00000000..101e4ea7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0202.json
@@ -0,0 +1,123 @@
+{
+ "description": "Test `_CONC` for guarding against circular/self-reference which otherwise would fail with 'Maximum function nesting level ... reached, aborting' (#945, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has concept description",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Page one",
+ "contents": "[[Has concept description::Bar]]"
+ },
+ {
+ "page": "Page two",
+ "contents": "[[Has concept description::Foo]]"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-is-not-circular",
+ "contents": "{{#concept: [[Has concept description::+]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-is-circular",
+ "contents": "{{#concept: [[Concept:Concept-is-circular]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-is-circular-extra",
+ "contents": "{{#concept: [[Has concept description::+]] [[Concept:Concept-is-circular-extra]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-is-circular-extra-extra",
+ "contents": "{{#concept: [[Has concept description::+]] [[Has concept description::+]] OR [[Concept:Concept-is-circular-extra]] [[Concept:Concept-is-circular]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "concept",
+ "about": "#0 Check simple concept list",
+ "condition": "[[Concept:Concept-is-not-circular]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-is-not-circular",
+ "count": "2"
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#1 Check for circular concept",
+ "condition": "[[Concept:Concept-is-circular]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "error": ">1"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-is-circular",
+ "count": null
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#2 Check for circular concept (conjunction)",
+ "condition": "[[Concept:Concept-is-circular-extra]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "error": ">1"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-is-circular-extra",
+ "count": null
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#3 Check for circular concept (disjunctions)",
+ "condition": "[[Concept:Concept-is-circular-extra-extra]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "error": ">1"
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-is-circular-extra-extra",
+ "count": 2
+ }
+ ]
+ }
+ ],
+ "settings": {
+ "smwgQueryResultCacheType": false,
+ "smwgQFilterDuplicates": false,
+ "smwgQConceptCaching": "CONCEPT_CACHE_NONE"
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso reports 'SPARQL compiler: Blank node ... is not allowed in a constant clause', see https://github.com/openlink/virtuoso-opensource/issues/126"
+ },
+ "version": "2",
+ "is-incomplete": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0203.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0203.json
new file mode 100644
index 00000000..a046c4fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0203.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test `_CONC` to use `CONCEPT_CACHE_ALL` (#1050, skip all SPARQL repository)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has description",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Page-one",
+ "contents": "[[Has description::Bar]] [[Category:Q0203]]"
+ },
+ {
+ "page": "Page-two",
+ "contents": "[[Has description::Foo]] [[Category:Q0203]]"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-for-any-description",
+ "contents": "{{#concept: [[Has description::+]] [[Category:Q0203]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 any value with CONCEPT_CACHE_ALL set (wiki configuration requires the cache to be computed off-line)",
+ "condition": "[[Concept:Concept-for-any-description]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "error": 1
+ }
+ },
+ {
+ "type": "concept",
+ "about": "#1 Check cache",
+ "condition": "[[Concept:Concept-for-any-description]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "error": 1
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-for-any-description",
+ "count": 2
+ }
+ ]
+ }
+ ],
+ "settings": {
+ "smwgQConceptCaching": "CONCEPT_CACHE_ALL"
+ },
+ "meta": {
+ "skip-on": {
+ "elastic": "CONCEPT_CACHE_ALL isn't implemented",
+ "virtuoso": "CONCEPT_CACHE_ALL isn't implemented",
+ "sesame": "CONCEPT_CACHE_ALL isn't implemented",
+ "fuseki": "CONCEPT_CACHE_ALL isn't implemented",
+ "blazegraph": "CONCEPT_CACHE_ALL isn't implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0204.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0204.json
new file mode 100644
index 00000000..0c2d4dd3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0204.json
@@ -0,0 +1,109 @@
+{
+ "description": "Test `_CONC` on predefined inverse query and subobject inverse query (#1096, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page/02/04-1",
+ "contents": "{{#subobject:|Has page=Page/02/04-1}}"
+ },
+ {
+ "page": "Page/02/04-2",
+ "contents": "[[Has page::Page/02/04-2]]"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-for-inverse-pre-defined-property",
+ "contents": "{{#concept: [[-Has page.-Has subobject::Page/02/04-1]] }}"
+ },
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "Concept-for-inverse-user-defined-property",
+ "contents": "{{#concept: [[-Has page.-Has page::Page/02/04-2]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 inverse for pre-defined property",
+ "condition": "[[-Has page.-Has subobject::Page/02/04-1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page/02/04-1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 inverse for user-defined-property",
+ "condition": "[[-Has page.-Has page::Page/02/04-2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page/02/04-2#0##"
+ ]
+ }
+ },
+ {
+ "type": "concept",
+ "about": "#2",
+ "condition": "[[Concept:Concept-for-inverse-pre-defined-property]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page/02/04-1#0##"
+ ]
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-for-inverse-pre-defined-property",
+ "count": 1
+ }
+ ]
+ },
+ {
+ "type": "concept",
+ "about": "#3",
+ "condition": "[[Concept:Concept-for-inverse-user-defined-property]]",
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page/02/04-2#0##"
+ ]
+ },
+ "conceptcache": [
+ {
+ "concept": "Concept-for-inverse-user-defined-property",
+ "count": 1
+ }
+ ]
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso reports 'SPARQL compiler: Blank node ... is not allowed in a constant clause', see https://github.com/openlink/virtuoso-opensource/issues/126"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0301.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0301.json
new file mode 100644
index 00000000..0e09d46d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0301.json
@@ -0,0 +1,134 @@
+{
+ "description": "Test `_IMPO` queries for imported foaf vocabulary (#891, en)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import foaf",
+ "contents": "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n homepage|Type:URL\n mbox|Type:Email\n mbox_sha1sum|Type:Text\n depiction|Type:URL\n phone|Type:Text\n Person|Category\n Organization|Category\n knows|Type:Page\n member|Type:Page\n"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:homepage",
+ "contents": "[[Imported from::foaf:homepage]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:name",
+ "contents": "[[Imported from::foaf:name]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:knows",
+ "contents": "[[Imported from::foaf:knows]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has name",
+ "contents": "#REDIRECT [[Property:Foaf:name]]"
+ },
+ {
+ "page": "John Doe",
+ "contents": "[[Foaf:name::John Doe]], [[Foaf:homepage::http://example.org/JohnDoe]] [[Foaf:knows::Jane Doe]]"
+ },
+ {
+ "page": "Jane Doe",
+ "contents": "[[Foaf:name::Jane Doe]], [[Foaf:homepage::http://example.org/JaneDoe]] [[Foaf:knows::John Doe]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Foaf:name::John Doe]] OR [[Foaf:name::Jane Doe]]",
+ "printouts": [
+ "Foaf:name",
+ "Foaf:knows"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "John Doe#0##",
+ "Jane Doe#0##"
+ ],
+ "count": "2",
+ "datavalues": [
+ {
+ "property": "Foaf:name",
+ "value": "John Doe"
+ },
+ {
+ "property": "Foaf:name",
+ "value": "Jane Doe"
+ },
+ {
+ "property": "Foaf:knows",
+ "value": "Jane Doe"
+ },
+ {
+ "property": "Foaf:knows",
+ "value": "John Doe"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 check auto-property types, #891",
+ "condition": "[[Foaf:name::John Doe]] OR [[Foaf:name::Jane Doe]]",
+ "printouts": [
+ "Foaf:homepage"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "John Doe#0##",
+ "Jane Doe#0##"
+ ],
+ "count": "2",
+ "dataitems": [
+ {
+ "type": "_uri",
+ "value": "http://example.org/JohnDoe"
+ },
+ {
+ "type": "_uri",
+ "value": "http://example.org/JaneDoe"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 query on redirected property",
+ "condition": "[[Has name::John Doe]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "John Doe#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0401.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0401.json
new file mode 100644
index 00000000..30096e72
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0401.json
@@ -0,0 +1,163 @@
+{
+ "description": "Test `_SUBP` on a simple 'family' subproperty hierarchy example query (#1003, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Family (http://www.cs.man.ac.uk/~stevensr/ontology/family.rdf.owl)",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Spouse",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has wife",
+ "contents": "[[Has type::Page]] [[Subproperty of::Spouse]] [[Subproperty of::Family]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has husband",
+ "contents": "[[Has type::Page]] [[Subproperty of::Spouse]] [[Subproperty of::Family]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has child",
+ "contents": "[[Has type::Page]] [[Subproperty of::Family]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has brother",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has sister",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "John Doe",
+ "contents": "[[Has wife::Jane Doe]] [[Has child::Jonnie Doe]] [[Has child::Janie Doe]]"
+ },
+ {
+ "page": "Jane Doe",
+ "contents": "[[Has husband::John Doe]]"
+ },
+ {
+ "page": "Jonnie Doe",
+ "contents": "[[Has sister::Janie Doe]]"
+ },
+ {
+ "page": "Janie Doe",
+ "contents": "[[Has brother::Jonnie Doe]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 Spouse to search any value",
+ "condition": "[[Spouse::+]]",
+ "printouts": [
+ "Has wife",
+ "Has husband"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "John Doe#0##",
+ "Jane Doe#0##"
+ ],
+ "count": "2",
+ "datavalues": [
+ {
+ "property": "Has wife",
+ "value": "Jane Doe"
+ },
+ {
+ "property": "Has husband",
+ "value": "John Doe"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 Disjunctive query for distinct spouse value",
+ "condition": "[[Spouse::John Doe]] OR [[Spouse::Jane Doe]]",
+ "printouts": [
+ "Has wife",
+ "Has husband"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "John Doe#0##",
+ "Jane Doe#0##"
+ ],
+ "count": "2",
+ "datavalues": [
+ {
+ "property": "Has wife",
+ "value": "Jane Doe"
+ },
+ {
+ "property": "Has husband",
+ "value": "John Doe"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 find members of a super property",
+ "condition": "[[Subproperty of::Property:Spouse]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Has wife#102##",
+ "Has husband#102##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 find the super properties (invert)",
+ "condition": "[[-Subproperty of::Property:Has wife]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Spouse#102##",
+ "Family#102##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgQSubpropertyDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/property hierarchies are currently not implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0402.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0402.json
new file mode 100644
index 00000000..31137378
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0402.json
@@ -0,0 +1,248 @@
+{
+ "description": "Test `_SUBP` to map DC imported vocabulary with MARC 21 bibliographic terms (#1003, http://www.loc.gov/marc/bibliographic/bd20x24x.html)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import dc",
+ "contents": "http://purl.org/dc/elements/1.1/|[http://purl.org/dc/elements/1.1/ dc]\n title|Type:Text\n type|Type:Text\n date|Type:Date\n description|Type:Text\n creator|Type:Page\n"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Dc:title",
+ "contents": "[[Imported from::dc:title]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Dc:creator",
+ "contents": "[[Imported from::dc:creator]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Marc:title",
+ "contents": "[[Has type::Text]] [[Subproperty of::Dc:title]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Marc:main entry personal name",
+ "contents": "[[Has type::Page]] [[Subproperty of::Dc:creator]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Marc:title statement",
+ "contents": "[[Has type::Text]] [[Subproperty of::Marc:title]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Marc:varying form of title",
+ "contents": "[[Has type::Text]] [[Subproperty of::Marc:title]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has author",
+ "contents": "#REDIRECT [[Property:Marc:main entry personal name]]"
+ },
+ {
+ "page": "Animal farm",
+ "contents": "[[Marc:title statement::George Orwell's Animal farm]] [[Has author::George Orwell]]"
+ },
+ {
+ "page": "War and Peace",
+ "contents": "[[Marc:title statement::War and Peace]] [[Has author::Leo Tolstoy]]"
+ },
+ {
+ "page": "King Lear",
+ "contents": "#REDIRECT [[Shakespeare's King Lear]]"
+ },
+ {
+ "page": "Shakespeare",
+ "contents": "#REDIRECT [[William Shakespeare]]"
+ },
+ {
+ "page": "Shakespeare's King Lear",
+ "contents": "[[Marc:title::King Lear]] [[Has author::William Shakespeare]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 any value for Marc:title statement",
+ "condition": "[[Marc:title statement::+]]",
+ "printouts": [
+ "Has author"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Animal farm#0##",
+ "War and Peace#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has author",
+ "value": "Leo Tolstoy"
+ },
+ {
+ "property": "Has author",
+ "value": "George Orwell"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 any value for Marc:title on level one of the subproperty hierarchy",
+ "condition": "[[Marc:title::+]]",
+ "printouts": [
+ "Has author"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Animal farm#0##",
+ "War and Peace#0##",
+ "Shakespeare's King Lear#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has author",
+ "value": "Leo Tolstoy"
+ },
+ {
+ "property": "Has author",
+ "value": "George Orwell"
+ },
+ {
+ "property": "Has author",
+ "value": "William Shakespeare"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 any value for Dc:title on level two of the subproperty hierarchy",
+ "condition": "[[Dc:title::+]]",
+ "printouts": [
+ "Has author"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Animal farm#0##",
+ "War and Peace#0##",
+ "Shakespeare's King Lear#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has author",
+ "value": "Leo Tolstoy"
+ },
+ {
+ "property": "Has author",
+ "value": "George Orwell"
+ },
+ {
+ "property": "Has author",
+ "value": "William Shakespeare"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 distinct value for Dc:title on level two of the subproperty hierarchy",
+ "condition": "[[Dc:title::~*Animal*]]",
+ "printouts": [
+ "Has author"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Animal farm#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has author",
+ "value": "George Orwell"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 distinct value for Has author / redirected value / subproperty level zero",
+ "condition": "[[Has author::Shakespeare]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Shakespeare's King Lear#0##"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 distinct value for Dc:creator / redirected value / subproperty level one",
+ "condition": "[[Dc:creator::Shakespeare]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Shakespeare's King Lear#0##"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ }
+ ],
+ "settings": {
+ "smwgQSubpropertyDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/property hierarchies are currently not implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0501.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0501.json
new file mode 100644
index 00000000..a0535128
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0501.json
@@ -0,0 +1,130 @@
+{
+ "description": "Test `_qty` queries for custom unit (km²/°C) property value assignments",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Area",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]][[Corresponds to::1000 m²]][[Corresponds to::0.38610 sq mi]][[Corresponds to::247.1054 acre]][[Corresponds to::988.4215 rood]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has average high temperature",
+ "contents": "[[Has type::Temperature]]"
+ },
+ {
+ "page": "Berlin",
+ "contents": "[[Area::891.85 km²]], [[Has average high temperature::13.4 °C]]"
+ },
+ {
+ "page": "Paris",
+ "contents": "[[Area::105.40 km²]], [[Has average high temperature::16.0 °C]]"
+ },
+ {
+ "page": "æ±äº¬éƒ½",
+ "contents": "[[Area::845 sq mi]], [[Has average high temperature::26.0 °C]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 Wildcard search for any value",
+ "condition": "[[Area::+]]",
+ "printouts": [
+ "Area"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Berlin#0##",
+ "Paris#0##",
+ "æ±äº¬éƒ½#0##"
+ ],
+ "count": "3",
+ "datavalues": [
+ {
+ "property": "Area",
+ "value": "891.85 km²"
+ },
+ {
+ "property": "Area",
+ "value": "105.40 km²"
+ },
+ {
+ "property": "Area",
+ "value": "845 sq mi"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 Search for distinct value",
+ "condition": "[[Area::891.85]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Berlin#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 Search for less than distinct value",
+ "condition": "[[Has average high temperature::<15.0 °C]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Berlin#0##"
+ ],
+ "count": "1"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 Search for greater than or equal distinct value",
+ "condition": "[[Has average high temperature::≥16.0 °C]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Paris#0##",
+ "æ±äº¬éƒ½#0##"
+ ],
+ "count": "2"
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 Search for greater than distinct value",
+ "condition": "[[Has average high temperature::>70 °F]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "æ±äº¬éƒ½#0##"
+ ],
+ "count": "1"
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "version": "2",
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0502.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0502.json
new file mode 100644
index 00000000..65bf2844
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0502.json
@@ -0,0 +1,84 @@
+{
+ "description": "Test `_qty` range queries using non strict comparators (`smwStrictComparators=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has starting wattage",
+ "contents": "[[Has type::Quantity]] [[Display units::kW]] [[Corresponds to::1 W, Watt, Watts]] [[Corresponds to::0.001 kW]] [[Corresponds to::0.0013410220 hp, bhp, horsepower]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has running wattage",
+ "contents": "[[Has type::Quantity]] [[Display units::kW]] [[Corresponds to::1 W, Watt, Watts]] [[Corresponds to::0.001 kW]] [[Corresponds to::0.0013410220 hp, bhp, horsepower]]"
+ },
+ {
+ "page": "Home appliance",
+ "contents": "{{#subobject:MicrowaveOven|Has starting wattage=1 kW|Has running wattage=1 kW}} {{#subobject:Refrigerator|Has starting wattage=1.2 kW|Has running wattage=130 W}} {{#subobject:Dishwasher|Has starting wattage=540 W|Has running wattage=200 W}} {{#subobject:Blender|Has starting wattage=840 W|Has running wattage=400 W}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has starting wattage::≥1 kW]]",
+ "printouts": [
+ "Has starting wattage"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Home appliance#0##MicrowaveOven",
+ "Home appliance#0##Refrigerator"
+ ],
+ "count": "2",
+ "datavalues": [
+ {
+ "property": "Has starting wattage",
+ "value": "1.2 kW"
+ },
+ {
+ "property": "Has starting wattage",
+ "value": "1 kW"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 Conjunctive condition",
+ "condition": "[[Has starting wattage::≥1 kW]] [[Has running wattage::< 500 W]]",
+ "printouts": [
+ "Has starting wattage",
+ "Has running wattage"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Home appliance#0##Refrigerator"
+ ],
+ "count": "1",
+ "datavalues": [
+ {
+ "property": "Has starting wattage",
+ "value": "1.2 kW"
+ },
+ {
+ "property": "Has running wattage",
+ "value": "130 W"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "version": "2",
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0503.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0503.json
new file mode 100644
index 00000000..ff650628
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0503.json
@@ -0,0 +1,87 @@
+{
+ "description": "Test `_qty` on positional unit preference in query condition (#1329, `smwStrictComparators=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Currency",
+ "contents": "[[Has type::Quantity]], [[Display units::€,£,¥]] [[Corresponds to::€ 1]] [[Corresponds to::1.06 US, US$, $]] [[Corresponds to::0.70 British Pound,GBP,£]] [[Corresponds to::¥,JPY,Japanese Yen 114.2121]]"
+ },
+ {
+ "page": "Example/Q0503/1",
+ "contents": "[[Currency::12 €]] [[Currency::¥ 500]] [[Currency::2 £]]"
+ },
+ {
+ "page": "Example/Q0503/2",
+ "contents": "[[Currency::€ 20]] [[Currency::2000 JPY]] [[Currency::0.5 GBP]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Currency::€ 20]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0503/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Currency::<Â¥300]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0503/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Currency::≥1400 JPY]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0503/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Currency::< € .8]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0503/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "version": "2",
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0601.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0601.json
new file mode 100644
index 00000000..b66d2809
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0601.json
@@ -0,0 +1,77 @@
+{
+ "description": "Test `_wpg` for property chain query queries",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Located in",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Member of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Europe",
+ "contents": "[[Member of::European Union]] {{#subobject:Member of=NATO}}"
+ },
+ {
+ "page": "France",
+ "contents": "[[Located in::Europe]]"
+ },
+ {
+ "page": "Germany",
+ "contents": "{{#set:Located in=Europe}}"
+ },
+ {
+ "page": "Italy",
+ "contents": "{{#subobject:Located in=Europe}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Located in.Member of::European Union]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "3",
+ "results": [
+ "Germany#0##",
+ "France#0##",
+ "Italy#0##_261bcb222bdef3105c6906115585880c"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Located in.Has subobject.Member of::NATO]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "3",
+ "results": [
+ "Germany#0##",
+ "France#0##",
+ "Italy#0##_261bcb222bdef3105c6906115585880c"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0602.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0602.json
new file mode 100644
index 00000000..7136cf26
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0602.json
@@ -0,0 +1,48 @@
+{
+ "description": "Test `_wpg` sort query with #subobject annotated @sortkey content",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Member of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page-with-subobject",
+ "contents": "{{#subobject:|Member of=WXYZ|@sortkey=B}}\n {{#subobject:|Member of=ABCD|@sortkey=A}}\n {{#subobject:|Member of=ABCD|@sortkey=A}}\n {{#subobject:|Member of=ABCD|@sortkey=C}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Member of::+]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Member_of": "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": "3",
+ "results": [
+ "Page-with-subobject#0##_7aa07758037723bb0d3522a1a151771f",
+ "Page-with-subobject#0##_cd630d70e4e469611f361bcda9cf919a",
+ "Page-with-subobject#0##_38a501d710af2a3167121d6282e64fe6"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0603.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0603.json
new file mode 100644
index 00000000..eba878e9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0603.json
@@ -0,0 +1,230 @@
+{
+ "description": "Test `_wpg` queries for various conditions using #set annotated content",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Member of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page-with-set-1",
+ "contents": "{{#set:|Member of=Foo}}\n {{#set:|Member of=Bar}}"
+ },
+ {
+ "page": "Page-with-set-2",
+ "contents": "{{#set:|Member of=Foobaz}} {{#set:|Member of=Bar}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Member of::Foo]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-set-1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Foo"
+ },
+ {
+ "property": "Member of",
+ "value": "Bar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Member of::~*Foo*]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2",
+ "results": [
+ "Page-with-set-1#0##",
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Foo"
+ },
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Member of::Foo]] OR [[Member of::Foobaz]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2",
+ "results": [
+ "Page-with-set-1#0##",
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Foo"
+ },
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Member of::Foo||Foobaz]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2",
+ "results": [
+ "Page-with-set-1#0##",
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Foo"
+ },
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Member of::Foobaz]] AND [[Member of::Bar]]",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Member of::Foobaz]] AND <q>[[Member of::Bar]] AND [[Member of::Foobaz]]</q>",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Member of::Foobaz]] AND <q>[[Member of::Bar]] AND <q>[[Member of::Bar]] OR [[Member of::Foobaz]]</q></q>",
+ "printouts": [
+ "Member of"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-set-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Member of",
+ "value": "Bar"
+ },
+ {
+ "property": "Member of",
+ "value": "Foobaz"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0604.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0604.json
new file mode 100644
index 00000000..c2ffd25a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0604.json
@@ -0,0 +1,192 @@
+{
+ "description": "Test `_wpg` queries to resolve property/values redirects (#467, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has firstPage",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has secondPage",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "PropertyToBeRedirected",
+ "contents": "#REDIRECT [[Property:Has firstPage]]"
+ },
+ {
+ "page": "PageValueToBeRedirected",
+ "contents": "#REDIRECT [[PageRedirectTarget]]"
+ },
+ {
+ "page": "AnotherPageValueToBeRedirected",
+ "contents": "#REDIRECT [[AnotherPageRedirectTarget]]"
+ },
+ {
+ "page": "Page-one",
+ "contents": "[[PropertyToBeRedirected::One]]"
+ },
+ {
+ "page": "Page-two",
+ "contents": "{{#subobject:PropertyToBeRedirected=Two}}"
+ },
+ {
+ "page": "Page-three",
+ "contents": "[[Has secondPage::PageValueToBeRedirected]]"
+ },
+ {
+ "page": "Page-four",
+ "contents": "{{#subobject:Has secondPage=PageValueToBeRedirected}}"
+ },
+ {
+ "page": "Page-five-combined",
+ "contents": "[[PropertyToBeRedirected::PageValueToBeRedirected]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 any value query for redirected property",
+ "condition": "[[PropertyToBeRedirected::+]]",
+ "printouts": [
+ "PropertyToBeRedirected",
+ "Has firstPage"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Page-one#0##",
+ "Page-two#0##_b60804b67d37b781ac15c6e78102d13c",
+ "Page-five-combined#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "PropertyToBeRedirected",
+ "value": "One"
+ },
+ {
+ "property": "PropertyToBeRedirected",
+ "value": "Two"
+ },
+ {
+ "property": "PropertyToBeRedirected",
+ "value": "PageValueToBeRedirected"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 any value query for redirected value",
+ "condition": "[[Has secondPage::+]]",
+ "printouts": [
+ "Has secondPage"
+ ],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-three#0##",
+ "Page-four#0##_f5bb3e35482aaa5d1eef24749c24af30"
+ ],
+ "datavalues": [
+ {
+ "property": "Has secondPage",
+ "value": "PageValueToBeRedirected"
+ },
+ {
+ "property": "Has secondPage",
+ "value": "PageValueToBeRedirected"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 disjunction query on discrete value search",
+ "condition": "[[PropertyToBeRedirected::One]] OR [[PropertyToBeRedirected::Two]] OR [[Has secondPage::PageValueToBeRedirected]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Page-one#0##",
+ "Page-two#0##_b60804b67d37b781ac15c6e78102d13c",
+ "Page-three#0##",
+ "Page-four#0##_f5bb3e35482aaa5d1eef24749c24af30"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 combined property/value redirect discrete value search",
+ "condition": "[[PropertyToBeRedirected::PageValueToBeRedirected]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-five-combined#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 single value search, refs #1007",
+ "condition": "[[:PageValueToBeRedirected]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "PageRedirectTarget#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 disjunctive single value search, refs #1007",
+ "condition": "[[:PageValueToBeRedirected]] OR [[:AnotherPageValueToBeRedirected]] OR [[:Page-one]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "PageRedirectTarget#0##",
+ "AnotherPageRedirectTarget#0##",
+ "Page-one#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not support property paths / SPARQL compiler, line 0: Invalid character in SPARQL expression at '^'"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0605.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0605.json
new file mode 100644
index 00000000..5a73a9e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0605.json
@@ -0,0 +1,141 @@
+{
+ "description": "Test `_wpg` regex search (`!~/~*/~?`) queries (#679)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has regexpage",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Page-1-with-spaces",
+ "contents": "[[Has regexpage::Annotation test with spaces]]"
+ },
+ {
+ "page": "Page-2-with-spaces",
+ "contents": "[[Has regexpage::Annotation text with spaces]]"
+ },
+ {
+ "page": "Page-3-without-spaces",
+ "contents": "[[Has regexpage::AnnotationTestWithoutSpaces]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has regexpage::~Annotation te*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-1-with-spaces#0##",
+ "Page-2-with-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has regexpage::~Annotation tes*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-1-with-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has regexpage::~An??tation*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Page-1-with-spaces#0##",
+ "Page-2-with-spaces#0##",
+ "Page-3-without-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Has regexpage::~Annotation*]] [[Has regexpage::!~Annotation tex*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-1-with-spaces#0##",
+ "Page-3-without-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Has regexpage::~Annotation tes*]] OR [[Has regexpage::~Annotation tex*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-1-with-spaces#0##",
+ "Page-2-with-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Has regexpage::~Annotation te?t with spaces]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-1-with-spaces#0##",
+ "Page-2-with-spaces#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Has regexpage::+]][[Has regexpage::!~Annotation te?t with spaces]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-3-without-spaces#0##"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0606.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0606.json
new file mode 100644
index 00000000..2887e384
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0606.json
@@ -0,0 +1,249 @@
+{
+ "description": "Test `_wpg`/`_num`/`_txt` using subqueries (#466, #627, #625)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Located in",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Member of",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has arbitrary number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has arbitrary text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::1000 m²]]"
+ },
+ {
+ "page": "City-A",
+ "contents": "[[Located in::Country-A]] {{#subobject:|Has arbitrary number=42|Has arbitrary text=abc}}"
+ },
+ {
+ "page": "City-B",
+ "contents": "[[Located in::Country-B]] {{#subobject:|Has arbitrary number=1001|Has arbitrary text=def}}"
+ },
+ {
+ "page": "City-C",
+ "contents": "[[Located in::Country-C]] {{#subobject:|Has area=891.85 km ²}}"
+ },
+ {
+ "page": "Country-A",
+ "contents": "[[Member of::EconomicUnion-A]] {{#subobject:|Member of=DefenseUnion-A}}"
+ },
+ {
+ "page": "Country-B",
+ "contents": "{{#subobject:|Member of=EconomicUnion-B}}"
+ },
+ {
+ "page": "EconomicUnion-A",
+ "contents": "[[Is part of::Community-A]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 as property chain notation",
+ "condition": "[[Located in.Member of::EconomicUnion-A]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "City-A#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 same as #0 as subquery notation",
+ "condition": "[[Located in::<q>[[Member of::EconomicUnion-A]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "City-A#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Located in.Member of.Is part of::Community-A]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "City-A#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Has subobject.Member of::EconomicUnion-B]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Country-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 SubqueryForCombinedSubobjectPropertyChain",
+ "condition": "[[Located in.Has subobject.Member of::EconomicUnion-B]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "City-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 SubqueryForCombinedSubobjectPropertyChainForWilcardSearch",
+ "condition": "[[Located in.Has subobject.Member of::+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "City-A#0##",
+ "City-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 regex search",
+ "condition": "[[Has subobject.Member of::~*Union*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Country-A#0##",
+ "Country-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 wildcard search",
+ "condition": "[[Has subobject.Member of::+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Country-A#0##",
+ "Country-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8",
+ "condition": "[[Located in::Country-A||Country-B]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "City-A#0##",
+ "City-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9 conjunctive subobject subquery ",
+ "condition": "[[Has subobject::<q>[[Has arbitrary number::42]][[Has arbitrary text::abc]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "City-A#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10",
+ "condition": "[[Has subobject::<q>[[Has arbitrary number::42||1001]] OR [[Has arbitrary text::abc||def]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "City-A#0##",
+ "City-B#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#11 disjunctive subobject subquery, #625",
+ "condition": "[[Has subobject::<q>[[Has area::891.85 km ²]] OR [[Has arbitrary text::abc||def]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "City-A#0##",
+ "City-B#0##",
+ "City-C#0##"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0607.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0607.json
new file mode 100644
index 00000000..0d9e50fd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0607.json
@@ -0,0 +1,116 @@
+{
+ "description": "Test `_wpg`/`_dat`/`_num`/`_txt` subquery example",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project manager",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project pool",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project assistant",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project name",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project due date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project level access",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has name",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "P-100",
+ "contents": "[[Category:Project]][[Has project name::P-100]][[Has project due date::10/30/2015]]"
+ },
+ {
+ "page": "山田一郎",
+ "contents": "[[Category:Project assistant]] [[Has name::{{PAGENAME}}]] [[Has project level access::5]]"
+ },
+ {
+ "page": "Clara Young",
+ "contents": "[[Category:Project assistant]] [[Has name::{{PAGENAME}}]] [[Has project level access::6]]"
+ },
+ {
+ "page": "John Doe",
+ "contents": "[[Category:Person]] [[Category:Project manager]] [[Has name::{{PAGENAME}}]] [[Has project assistant::Clara Young]] [[Has project assistant::山田一郎]]"
+ },
+ {
+ "page": "Example/0607/PP-100",
+ "contents": "{{#subobject:|@category=Project pool |Has project=P-100 |Has project manager=John Doe |Has project pool={{PAGENAME}} }} {{#subobject:|@category=Project pool |Has project=P-200 |Has project manager=John Doe |Has project pool={{PAGENAME}} }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 conjunctive/disjunctive subquery; What projects are due where Jon Doe is assigned (with either Clara Young or 山田一郎); Query-Size:15 / Query-Depth:2",
+ "condition": "[[Category:Project pool]] [[Has project::<q>[[Has project name::P-100]] [[Has project due date::>9/24/2015]]</q>]] [[Has project manager::<q>[[Has name::John Doe]] [[Has project assistant::山田一郎]] OR [[Has name::John Doe]] [[Has project assistant::Clara Young]]</q>]]",
+ "printouts": [
+ "Has project manager",
+ "Has project"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0607/PP-100#0##_43aaa7182890fb917e6cc82bf6083abb"
+ ],
+ "dataitems": [
+ {
+ "type": "_wpg",
+ "value": "John_Doe#0##"
+ },
+ {
+ "type": "_wpg",
+ "value": "P-100#0##"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 conjunctive subquery; What projects are due where Jon Doe is assigned (together with a level 5 assistant); Query-Size:12 / Query-Depth:3",
+ "condition": "[[Category:Project pool]] [[Has project::<q>[[Has project name::P-100]] [[Has project due date::>9/24/2015]]</q>]] [[Has project manager::<q>[[Has name::John Doe]] [[Has project assistant.Has project level access::5]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0607/PP-100#0##_43aaa7182890fb917e6cc82bf6083abb"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0608.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0608.json
new file mode 100644
index 00000000..41ab93e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0608.json
@@ -0,0 +1,123 @@
+{
+ "description": "Test `_wpg` for single value approximate (`~/!~`) queries (#1246)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has example page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/0608/1/1",
+ "contents": "[[Category:0608-1]]"
+ },
+ {
+ "page": "Example/0608/1/2",
+ "contents": "[[Category:0608-2]]"
+ },
+ {
+ "page": "Example/0608/3",
+ "contents": "[[Category:0608-3]] [[Has example page::Example/0608/3]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Example/0608/1/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0608/1/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Example/0608/1/1]] OR [[Example/0608/3]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/0608/1/1#0##",
+ "Example/0608/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[~Example/0608/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/0608/1/1#0##",
+ "Example/0608/1/2#0##",
+ "Example/0608/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[~Example/0608/*]][[!~Example/0608/1/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0608/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[~Example/0608/*]] [[Category:0608-2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0608/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[~Example/0608/*]] [[Has example page::Example/0608/3]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0608/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0609.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0609.json
new file mode 100644
index 00000000..78a74c37
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0609.json
@@ -0,0 +1,78 @@
+{
+ "description": "Test `_wpg` for single value approximate (`~/!~`) queries with conjunctive category hierarchy (#1246, en, skip virtuoso)",
+ "setup": [
+ {
+ "page": "Example/0609/1",
+ "contents": "[[Category:0609-1]]"
+ },
+ {
+ "page": "Example/0609/2",
+ "contents": "[[Category:0609-2]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "0609-1",
+ "contents": "[[Category:0609]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "0609-2",
+ "contents": "[[Category:0609]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[~*0609*]][[Category:0609]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/0609/1#0##",
+ "Example/0609/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[~*0609*]][[Category:0609]][[!~*0609*/2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/0609/1#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0610.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0610.json
new file mode 100644
index 00000000..2797e802
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0610.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test `_wpg` range queries (#1291, `smwStrictComparators=false`, skip virtuoso)",
+ "setup": [
+ {
+ "page": "Example/Q0610-A",
+ "contents": "[[Category:Q0610]]"
+ },
+ {
+ "page": "Example/Q0610-AAA",
+ "contents": "[[Category:Q0610]]"
+ },
+ {
+ "page": "Example/Q0610-BBB",
+ "contents": "[[Category:Q0610]]"
+ },
+ {
+ "page": "Example/Q0610-CCC",
+ "contents": "[[Category:Q0610]]"
+ },
+ {
+ "page": "Example/Q0610-DDD",
+ "contents": "[[Category:Q0610]]"
+ },
+ {
+ "page": "Example/Q0610-ZZZ",
+ "contents": "[[Category:Q0610-Z]] {{DEFAULTSORT:Example/Q0610-CCC}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 range >A <C (non strict comparator)",
+ "condition": "[[Category:Q0610]] [[>Example/Q0610-A]] [[<Example/Q0610-C]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0610-A#0##",
+ "Example/Q0610-AAA#0##",
+ "Example/Q0610-BBB#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 range >AA <CCC (non strict comparator)",
+ "condition": "[[Category:Q0610]] [[>Example/Q0610-AA]] [[<Example/Q0610-CCC]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0610-AAA#0##",
+ "Example/Q0610-BBB#0##",
+ "Example/Q0610-CCC#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 range >AA <CCC (non strict comparator), default sort change includes ZZZ object",
+ "condition": "<q>[[Category:Q0610]] OR [[Category:Q0610-Z]]</q> [[>Example/Q0610-AA]] [[<Example/Q0610-CCC]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/Q0610-AAA#0##",
+ "Example/Q0610-BBB#0##",
+ "Example/Q0610-CCC#0##",
+ "Example/Q0610-ZZZ#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 doesn't understand ranges, Failed asserting that 5 matches expected 3"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0611.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0611.json
new file mode 100644
index 00000000..d879ea63
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0611.json
@@ -0,0 +1,62 @@
+{
+ "description": "Test `_wpg` namespace any value queries (#1301, en)",
+ "setup": [
+ {
+ "namespace": "NS_HELP",
+ "page": "Example/Q0611/1",
+ "contents": "[[Category:Q0611]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Example/Q0611/2",
+ "contents": "[[Category:Q0611]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Example/Q0611/3",
+ "contents": "#REDIRECT [[Example/Q0611/4]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "do-delete": true,
+ "page": "Example/Q0611/4",
+ "contents": "[[Category:Q0611]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Help:+]] [[Category:Q0611]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0611/1#12##",
+ "Example/Q0611/2#12##"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0612.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0612.json
new file mode 100644
index 00000000..928bd4da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0612.json
@@ -0,0 +1,100 @@
+{
+ "description": "Test `_wpg` object value that contains `=` (equals sign) (#640, #710, #1542, #1645, #3560)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/Q0612/1(=)",
+ "contents": "[[Category:Q0612]] [[Has page::{{FULLPAGENAME}}]]"
+ },
+ {
+ "page": "Example/Q0612/-3D=",
+ "contents": "[[Category:Q0612]] [[Has page::{{FULLPAGENAME}}]]"
+ },
+ {
+ "page": "Example/Q0612/Q1",
+ "contents": "{{#show: Example/Q0612/1(=) |?Has page |link=none }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#2 #show not be disturbed by `=`",
+ "subject": "Example/Q0612/Q1",
+ "assert-output": {
+ "to-contain": [
+ "Example/Q0612/1(=)"
+ ],
+ "not-contain": [
+ "&quot;:&quot; cannot be used as a page name in this wiki."
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Q0612]] [[Example/Q0612/1(=)]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0612/1(=)#0##"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Category:Q0612]] [[Has page::Example/Q0612/1(=)]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0612/1(=)#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Category:Q0612]] [[Has page::Example/Q0612/-3D=]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0612/-3D=#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0613.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0613.json
new file mode 100644
index 00000000..15a030f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0613.json
@@ -0,0 +1,182 @@
+{
+ "description": "Test single value (`~/!~`/`<`/`>`) queries on namespaced entity (#1652, `NS_HELP`, `smwStrictComparators=false`, skip-on virtuoso)",
+ "setup": [
+ {
+ "page": "ABC",
+ "contents": "[[Category:0613]] (control group)"
+ },
+ {
+ "page": "ABB",
+ "contents": "[[Category:0613]] (control group)"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "ABC",
+ "contents": "[[Category:0613]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "ABB",
+ "contents": "[[Category:0613]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "AAB",
+ "contents": "[[Category:0613]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Help:~A*B]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "AAB#12##",
+ "ABB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[~Help:A*B]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "AAB#12##",
+ "ABB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Help:!~A*B]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "ABC#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[!~Help:A*B]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "ABC#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Help:>AB]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "ABC#12##",
+ "ABB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[>Help:AB]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "ABC#12##",
+ "ABB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Help:<AB]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "AAB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[<Help:AB]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "AAB#12##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8",
+ "condition": "[[<Help:<AB]] [[Category:0613]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 does not like this test"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0614.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0614.json
new file mode 100644
index 00000000..16f3ecf8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0614.json
@@ -0,0 +1,109 @@
+{
+ "description": "Test query with category hierarchy depth (#2662, `wgContLang=en`, `smwgQSubpropertyDepth`, `smwgQSubcategoryDepth`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0614",
+ "contents": "Super class"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0614/1",
+ "contents": "[[Category:Q0614]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0614/1/2",
+ "contents": "[[Category:Q0614/1]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0614/1/2/3",
+ "contents": "[[Category:Q0614/1/2]]"
+ },
+ {
+ "page": "Example/Q0614/1",
+ "contents": "[[Category:Q0614]]"
+ },
+ {
+ "page": "Example/Q0614/1/2",
+ "contents": "[[Category:Q0614/1]]"
+ },
+ {
+ "page": "Example/Q0614/1/2/3",
+ "contents": "[[Category:Q0614/1/2]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 select all members",
+ "condition": "[[Category:Q0614]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0614/1#0##",
+ "Example/Q0614/1/2#0##",
+ "Example/Q0614/1/2/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 depth=0",
+ "condition": "[[Category:Q0614|+depth=0]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0614/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 depth=1",
+ "condition": "[[Category:Q0614|+depth=1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0614/1#0##",
+ "Example/Q0614/1/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0615.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0615.json
new file mode 100644
index 00000000..c0bf8673
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0615.json
@@ -0,0 +1,148 @@
+{
+ "description": "Test query with property hierarchy depth (#2662, `wgContLang=en`, `smwgQSubpropertyDepth`, `smwgQSubcategoryDepth`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Q0615",
+ "contents": "Super property"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Q0615/1",
+ "contents": "[[Subproperty of::Q0615]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Q0615/1/2",
+ "contents": "[[Subproperty of::Q0615/1]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Q0615/1/2/3",
+ "contents": "[[Subproperty of::Q0615/1/2]]"
+ },
+ {
+ "page": "Example/Q0615/1",
+ "contents": "[[Q0615::123]]"
+ },
+ {
+ "page": "Example/Q0615/1/2.1",
+ "contents": "[[Q0615/1::123]]"
+ },
+ {
+ "page": "Example/Q0615/1/2.2",
+ "contents": "[[Q0615/1::456]]"
+ },
+ {
+ "page": "Example/Q0615/1/2/3",
+ "contents": "[[Q0615/1/2::123]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 select all members on discrete value",
+ "condition": "[[Q0615::123]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0615/1#0##",
+ "Example/Q0615/1/2.1#0##",
+ "Example/Q0615/1/2/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 select all members on any value",
+ "condition": "[[Q0615::+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/Q0615/1#0##",
+ "Example/Q0615/1/2.1#0##",
+ "Example/Q0615/1/2.2#0##",
+ "Example/Q0615/1/2/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 depth=0",
+ "condition": "[[Q0615::123|+depth=0]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0615/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 depth=1 on discrete value",
+ "condition": "[[Q0615::123|+depth=1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0615/1#0##",
+ "Example/Q0615/1/2.1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 depth=1 on any value",
+ "condition": "[[Q0615::+|+depth=1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0615/1#0##",
+ "Example/Q0615/1/2.1#0##",
+ "Example/Q0615/1/2.2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0616.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0616.json
new file mode 100644
index 00000000..62793a37
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0616.json
@@ -0,0 +1,128 @@
+{
+ "description": "Test `in:` syntax on `_txt`, `_dat`, and `_num` values",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/Q0616/1",
+ "contents": "[[Has date::1 Jan 1970 12:00]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/2",
+ "contents": "[[Has date::1 Jan 1971 12:00]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/3",
+ "contents": "[[Has text::abc def foo]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/4",
+ "contents": "[[Has text::abc foo]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/5",
+ "contents": "[[Has number::50]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/6",
+ "contents": "[[Has number::99]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/7",
+ "contents": "[[Has number::100]] [[Category:0616]]"
+ },
+ {
+ "page": "Example/Q0616/8",
+ "contents": "[[Has number::-20]] [[Category:0616]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (in: on date converted to `[[Has date:: [[≥1970]] [[<<1 January 1971 00:00:00]] ]]`)",
+ "condition": "[[Has date::in:1970]] [[Category:0616]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0616/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (in: on text converted to `[[Has text::~*abc d*]]`)",
+ "condition": "[[Has text::in:abc d]] [[Category:0616]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0616/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (in: on number converted to `[[Has number:: [[≥0]] [[≤99]] ]]`)",
+ "condition": "[[Has number::in:99]] [[Category:0616]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0616/5#0##",
+ "Example/Q0616/6#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (in: on number converted to `[[Has number:: [[≥-100]] [[≤0]] ]]`)",
+ "condition": "[[Has number::in:-100]] [[Category:0616]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0616/8#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0617.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0617.json
new file mode 100644
index 00000000..1476dc27
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0617.json
@@ -0,0 +1,127 @@
+{
+ "description": "Test range `<>` syntax on `_num` (float,double), `_dat` (millisec) values (`smwStrictComparators=true`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/Q0617/1",
+ "contents": "[[Has number::100.1000008]] [[Category:Q0617]]"
+ },
+ {
+ "page": "Example/Q0617/2",
+ "contents": "[[Has number::100.1000009]] [[Category:Q0617]]"
+ },
+ {
+ "page": "Example/Q0617/3",
+ "contents": "[[Has number::100.1000010]] [[Category:Q0617]]"
+ },
+ {
+ "page": "Example/Q0617/4",
+ "contents": "[[Has date::1 Jan 2200 12:00:00.100]] [[Category:Q0617]]"
+ },
+ {
+ "page": "Example/Q0617/5",
+ "contents": "[[Has date::1 Jan 2200 12:00:00.200]] [[Category:Q0617]]"
+ },
+ {
+ "page": "Example/Q0617/6",
+ "contents": "[[Has date::1 Jan 2200 12:00:00.300]] [[Category:Q0617]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (>100.1000008 AND <100.1000010)",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.+ just can't cope with this query!"
+ },
+ "condition": "[[Has number::>100.1000008]] [[Has number::<100.1000010]] [[Category:Q0617]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0617/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (>=100.1000008 AND <100.1000010)",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.+ just can't cope with this query!"
+ },
+ "condition": "[[Has number::>=100.1000008]] [[Has number::<100.1000010]] [[Category:Q0617]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0617/1#0##",
+ "Example/Q0617/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (1 Jan 2200 12:00:00.100)",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.+ just can't cope with this query!"
+ },
+ "condition": "[[Has date::1 Jan 2200 12:00:00.100]] [[Category:Q0617]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0617/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (>1 Jan 2200 12:00:00 AND <1 Jan 2200 12:00:00.200)",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.+ just can't cope with this query!"
+ },
+ "condition": "[[Has date::>1 Jan 2200 12:00:00]][[Has date::<1 Jan 2200 12:00:00.200]] [[Category:Q0617]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0617/4#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": true,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0618.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0618.json
new file mode 100644
index 00000000..ba1e5046
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0618.json
@@ -0,0 +1,133 @@
+{
+ "description": "Test deep subqueries (Friends of friends) (`smwgQMaxDepth`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Age",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Material status",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/Q0618/1",
+ "contents": {
+ "import-from": "/../Fixtures/q-0618.persons.txt"
+ }
+ },
+ {
+ "page": "Example/Q0618/2",
+ "contents": {
+ "import-from": "/../Fixtures/q-0618.visits.txt"
+ }
+ },
+ {
+ "page": "Example/Q0618/3",
+ "contents": {
+ "import-from": "/../Fixtures/q-0618.friends.txt"
+ }
+ },
+ {
+ "page": "Example/Q0618/4a",
+ "contents": "{{#ask: [[Person::Andy Mars]][[Age::+]] |?Age |mainlabel=- |headers=hide }} {{#ask: [[Person::Andy Mars]][[Age::+]] |?Age |mainlabel=- |headers=hide |format=debug }}"
+ },
+ {
+ "page": "Example/Q0618/4b",
+ "contents": "{{#ask: [[-Person::<q>[[Person.-Has friend.Person::Andy Mars]][[Age::>>{{#ask: [[Person::Andy Mars]][[Age::+]] |?Age |mainlabel=- |headers=hide }} ]]</q>]] }} {{#ask: [[-Person::<q>[[Person.-Has friend.Person::Andy Mars]][[Age::>>{{#ask: [[Person::Andy Mars]][[Age::+]] |?Age |mainlabel=- |headers=hide }} ]]</q>]] |format=debug}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (Show Friends of friends from Andy Mars)",
+ "condition": "[[-Has friend.Person.-Has friend.Person::Andy Mars]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 7,
+ "results": [
+ "Charlotte Beans#0##",
+ "Evelyne Lynn#0##",
+ "James Ross#0##",
+ "Mandy Rose#0##",
+ "Michael Chester#0##",
+ "Rosalia Alvarez#0##",
+ "Yumi Tanaka#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (Show Friends of friends from Andy Mars who are single and over 30)",
+ "condition": "[[-Person:: <q>[[Person.-Has friend.Person.-Has friend.Person::Andy Mars]] [[Material status::single]][[Age::> 30]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Michael Chester#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (Show Friends who have been to places Andy Mars has visited)",
+ "condition": "[[-Person.Place visited::<q>[[-Place visited.Person::<q>[[-Has friend.Person::Andy Mars]]</q>]][[-Place visited::<q>[[Person::Andy Mars]]</q>]]</q>]] [[!Andy Mars]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 7,
+ "results": [
+ "Charles Banning#0##",
+ "Charlotte Beans#0##",
+ "John Maier#0##",
+ "Kevin Mikaelson#0##",
+ "Mike Bloom#0##",
+ "Rosalia Alvarez#0##",
+ "Yumi Tanaka#0##"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (age)",
+ "subject": "Example/Q0618/4a",
+ "assert-output": {
+ "to-contain": [
+ "32"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (Show Friends from Andy Mars that are older has him)",
+ "subject": "Example/Q0618/4b",
+ "assert-output": {
+ "to-contain": [
+ "Charles Banning"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgQMaxDepth": 16,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0619.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0619.json
new file mode 100644
index 00000000..be97b411
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0619.json
@@ -0,0 +1,80 @@
+{
+ "description": "Test `_wpg` user case (#2982)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/Q0619/1",
+ "contents": "[[Has page::userCase]]"
+ },
+ {
+ "page": "Example/Q0619/user Case",
+ "contents": "[[Has page::Example/Q0619/1]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (userCase, UserCase on _wpg type)",
+ "condition": "[[Has page::userCase]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0619/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (userCase, UserCase on _wpg type)",
+ "condition": "[[Has page::UserCase]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0619/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[~*user Case*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0619/user Case#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0620.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0620.json
new file mode 100644
index 00000000..b73064f7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0620.json
@@ -0,0 +1,134 @@
+{
+ "description": "Test `_wpg` and category using subquery construct",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q0620/1",
+ "contents": "[[Has page::Q0620/1]] [[Category:Q0620]]"
+ },
+ {
+ "page": "Q0620/2",
+ "contents": "[[Category:Q0620]]"
+ },
+ {
+ "page": "Q0620/3",
+ "contents": "[[Has page::Q0620/1]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (match all with `Has page::Q0620/1`)",
+ "condition": "[[Has page::<q>[[Category:Q0620]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0620/1#0##",
+ "Q0620/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has page::<q>[[Category:Q0620]]</q>]] [[Category:Q0620]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0620/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has page::<q>[[Has page::<q>[[Category:Q0620]]</q>]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0620/1#0##",
+ "Q0620/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Has page::<q>[[Has page::<q>[[Category:Q0620]]</q>]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0620/1#0##",
+ "Q0620/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Has page.Has page::<q>[[Category:Q0620]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0620/1#0##",
+ "Q0620/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "skip-on": {
+ "elastic": [ "not", "`Category:!` only works with ES." ]
+ },
+ "condition": "[[Has page::<q>[[Has page::<q>[[Category:Q0620]]</q>]]</q>]] [[Category:!Q0620]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0620/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgQSubcategoryDepth": 10
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0621.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0621.json
new file mode 100644
index 00000000..23d9b654
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0621.json
@@ -0,0 +1,105 @@
+{
+ "description": "Test `_wpg` and namespace using subquery construct",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Q0621",
+ "contents": "[[Has page::Q0621/1]] [[Category:Q0621]]"
+ },
+ {
+ "namespace": "NS_HELP",
+ "page": "Q0621/1",
+ "contents": "[[Has page::Q0621/2]] [[Category:Q0621]]"
+ },
+ {
+ "page": "Q0621/1",
+ "contents": "[[Has page::Help:Q0621]]"
+ },
+ {
+ "page": "Q0621/2",
+ "contents": "[[Has page::Q0621/2]] [[Category:Q0621/1]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has page::<q>[[Help:+]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0621/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has page::<q>[[Help:+]] [[Has page::+]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0621/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has page::<q>[[Help:+]] [[Has page::+]] [[Category:Q0621]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0621/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[-Has page::<q>[[Help:+]] [[Has page::<q> [[Category:Q0621/1]]</q>]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0621/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0622.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0622.json
new file mode 100644
index 00000000..95d478a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0622.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test query with category hierarchy",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0622/A",
+ "contents": "[[Category:Q0622]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0622/B",
+ "contents": "[[Category:Q0622/A]] [[Category:Q0622/C]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0622/C",
+ "contents": "[[Category:Q0622]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Q0622",
+ "contents": "[[Category:Q0622]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Q0622]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0622#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0623.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0623.json
new file mode 100644
index 00000000..4ef1d39c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0623.json
@@ -0,0 +1,85 @@
+{
+ "description": "Test query with `_SUBC`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0623",
+ "contents": ""
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0623/1",
+ "contents": "[[Category:Q0623]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0623/1/1",
+ "contents": "[[Category:Q0623/1]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0623/1/2",
+ "contents": "[[Category:Q0623/1]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (without `Category:` namespace, #3550)",
+ "condition": "[[Subcategory of::Q0623/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0623/1/1#14##",
+ "Q0623/1/2#14##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (with `Category:` namespace)",
+ "condition": "[[Subcategory of::Category:Q0623/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0623/1/1#14##",
+ "Q0623/1/2#14##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_HELP": true
+ },
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0701.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0701.json
new file mode 100644
index 00000000..8d81e881
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0701.json
@@ -0,0 +1,186 @@
+{
+ "description": "Test `_uri` with some annotation/search pattern (T45264, #679)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Page-with-url-1",
+ "contents": "[[Has url::http://example.org/aaa/bbb#ccc]]"
+ },
+ {
+ "page": "Page-with-url-2",
+ "contents": "[[Has url::http://acme.test/api?query=!_:;@* #Foo&=%20-3DBar]]"
+ },
+ {
+ "page": "Page-with-url-3",
+ "contents": "[[Has url::http://example.org/よã†ã“ã#-{}]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has url::http://example.org/aaa/bbb#ccc]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/aaa/bbb#ccc"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has url::http://acme.test/api?query=!_:;@*_#Foo&=_-3DBar]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://acme.test/api?query=%21_:%3B@%2A_#Foo&=_-3DBar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has url::~*example.org/*]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Page-with-url-1#0##",
+ "Page-with-url-3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Has url::~*ccc*]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Has url::~http://*query=*]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Has url::~http://*query=*]] OR [[Has url::~*ccc*]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2",
+ "results": [
+ "Page-with-url-1#0##",
+ "Page-with-url-2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Has url::http://example.org/よã†ã“ã#-{}]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-3#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D#-%7B%7D"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[Has url::http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D#-%7B%7D]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page-with-url-3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0702.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0702.json
new file mode 100644
index 00000000..c195974b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0702.json
@@ -0,0 +1,177 @@
+{
+ "description": "Test `_uri` with additional annotation/search (#1129)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has telephone number",
+ "contents": "[[Has type::Telephone number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has email",
+ "contents": "[[Has type::Email]]"
+ },
+ {
+ "page": "Page/07/02/1",
+ "contents": "[[Has telephone number::+1-2012-555-0123]]"
+ },
+ {
+ "page": "Page/07/02/2",
+ "contents": "[[Has telephone number::+1-2012-555-5555]]"
+ },
+ {
+ "page": "Page/07/02/3",
+ "contents": "[[Has email::Lorem@ipsum.org]]"
+ },
+ {
+ "page": "Page/07/02/4",
+ "contents": "[[Has email::Lorem@123.org]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 search phone number +1-2012-555-5555",
+ "condition": "[[Has telephone number::+1-2012-555-5555]]",
+ "printouts": [
+ "Has telephone number"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/2#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has telephone number",
+ "value": "tel:+1-2012-555-5555"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 search phone number that contains 123",
+ "condition": "[[Has telephone number::~*123*]]",
+ "printouts": [
+ "Has telephone number"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has telephone number",
+ "value": "tel:+1-2012-555-0123"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 search phone number that contains 555",
+ "condition": "[[Has telephone number::~*555*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "2",
+ "results": [
+ "Page/07/02/1#0##",
+ "Page/07/02/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 search e-mail address Lorem@ipsum.org",
+ "condition": "[[Has email::Lorem@ipsum.org]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 search e-mail address that contains 123.org",
+ "condition": "[[Has email::~*123.org]]",
+ "printouts": [
+ "Has email"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/4#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has email",
+ "value": "mailto:Lorem@123.org"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 search phone number that does not contain 123",
+ "condition": "[[Has telephone number::!~*123*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 search e-mail address that does not contain 123.org",
+ "condition": "[[Has email::!~*123.org]]",
+ "printouts": [
+ "Has email"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": "1",
+ "results": [
+ "Page/07/02/3#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has email",
+ "value": "mailto:Lorem@ipsum.org"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": [],
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0703.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0703.json
new file mode 100644
index 00000000..c360aca1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0703.json
@@ -0,0 +1,77 @@
+{
+ "description": "Test to map `Foaf` property from back-end / using a localized predefined property `A le type@fr` (en)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import foaf",
+ "contents": "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n homepage|Type:URL\n mbox|Type:Email\n mbox_sha1sum|Type:Text\n depiction|Type:URL\n phone|Type:Text\n Person|Category\n Organization|Category\n knows|Type:Page\n member|Type:Page\n"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:homepage",
+ "contents": "[[Imported from::foaf:homepage]] [[Category:Import]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has type::+]] [[Category:Import]]",
+ "printouts": [
+ "Has type"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Foaf:homepage#102##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has type",
+ "value": "URL"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (A le type@fr == Has type)",
+ "condition": "[[A le type@fr::+]] [[Category:Import]]",
+ "printouts": [
+ "A le type@fr"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Foaf:homepage#102##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has type",
+ "value": "URL"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0704.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0704.json
new file mode 100644
index 00000000..b4d23f5e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0704.json
@@ -0,0 +1,52 @@
+{
+ "description": "Test `_uri` long URL (255+) (#1872)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/Q0704/1",
+ "contents": "[[Has url::http://example.org/TBgcHiQBTvpV3XkFiRpMcV1KQoZvQXFyiQUQMM2RoYUyJaRoSyKnsQTWwia3HATd4YVmd8BZgkL2LfL0Q6rP5E90A4IuV6uKdYjL3nxZvx0pbc3tbxwa6jMW1JDNxusaKQ52ftRS7DCEY1IPTRZnuRMLPgWYwLOsEAebOsxD7BBL9IK3Z2Osfh9s0FC1SUwCVdKcLkBgurXKGi99s61qnAU2zWFXBUCEzID6533LeaHCBKW8\\/\\i2BgTK2tv65LHO6zB-larger%&`^Than255ö/ä/ü/è/é]]"
+ },
+ {
+ "page": "Example/Q0704/Q.1",
+ "contents": "{{#ask: [[Has url::~*Than255]] |?Has url}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 URL 255+ chars",
+ "condition": "[[Has url::~*Than255*]]",
+ "printouts": [
+ "Has url"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0704/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Has url",
+ "value": "http://example.org/TBgcHiQBTvpV3XkFiRpMcV1KQoZvQXFyiQUQMM2RoYUyJaRoSyKnsQTWwia3HATd4YVmd8BZgkL2LfL0Q6rP5E90A4IuV6uKdYjL3nxZvx0pbc3tbxwa6jMW1JDNxusaKQ52ftRS7DCEY1IPTRZnuRMLPgWYwLOsEAebOsxD7BBL9IK3Z2Osfh9s0FC1SUwCVdKcLkBgurXKGi99s61qnAU2zWFXBUCEzID6533LeaHCBKW8%5C/%5Ci2BgTK2tv65LHO6zB-larger%&%60%5EThan255%C3%B6/%C3%A4/%C3%BC/%C3%A8/%C3%A9"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEnabledFulltextSearch": true,
+ "smwgFulltextDeferredUpdate": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0801.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0801.json
new file mode 100644
index 00000000..0abdab74
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0801.json
@@ -0,0 +1,47 @@
+{
+ "description": "Test `_INST` query (#1004, en)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Foo",
+ "contents": ""
+ },
+ {
+ "page": "Page-with-category",
+ "contents": "[[Category:Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 query pages with annotated category",
+ "condition": "[[Category:Foo]]",
+ "printouts": [
+ "_INST"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-with-category#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "_INST",
+ "value": "Foo"
+ }
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0802.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0802.json
new file mode 100644
index 00000000..9dab306f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0802.json
@@ -0,0 +1,160 @@
+{
+ "description": "Test `_INST`/`_SUBC` queries (#1005, en, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Foo",
+ "contents": ""
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Bar",
+ "contents": "[[Category:Foo]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Bam",
+ "contents": "[[Category:Foo]]"
+ },
+ {
+ "page": "Page-annotated-with-subcategory",
+ "contents": "[[Category:Bar]] [[Category:Bam]]"
+ },
+ {
+ "page": "Page-annotated-with-another-subcategory",
+ "contents": "[[Category:Bam]]"
+ },
+ {
+ "page": "Page-annotated-with-supercategory-subcategory",
+ "contents": "[[Category:Foo]] [[Category:Bam]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 query pages with annotated category(subcategory)",
+ "condition": "[[Category:Bar]]",
+ "printouts": [
+ "_INST"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-annotated-with-subcategory#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "_INST",
+ "value": "Bar"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 disjuntive query for two categories",
+ "condition": "[[Category:Bar]] OR [[Category:Bam]]",
+ "printouts": [
+ "_INST"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Page-annotated-with-subcategory#0##",
+ "Page-annotated-with-another-subcategory#0##",
+ "Page-annotated-with-supercategory-subcategory#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 conjuntive query for two categories",
+ "condition": "[[Category:Bar]][[Category:Bam]]",
+ "printouts": [
+ "_INST"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Page-annotated-with-subcategory#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 find list of subcategories for selected super",
+ "condition": "[[Subcategory of::Category:Foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Bar#14##",
+ "Bam#14##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 find the super for a selected subcategory",
+ "condition": "[[-Subcategory of::Category:Bar]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Foo#14##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 disjuntive query for two categories",
+ "condition": "[[Category:Foo]] OR [[Category:Bam]]",
+ "printouts": [
+ "_INST"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Page-annotated-with-subcategory#0##",
+ "Page-annotated-with-another-subcategory#0##",
+ "Page-annotated-with-supercategory-subcategory#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubcategoryDepth": 10
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0803.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0803.json
new file mode 100644
index 00000000..b7b69adb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0803.json
@@ -0,0 +1,140 @@
+{
+ "description": "Test `_INST`/ Nested category annotation (#1012, en, skip virtuoso) category hierarchy queries",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has trait",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has breed trait",
+ "contents": "[[Has type::Text]] [[Subproperty of::Has trait]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has group",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Animal",
+ "contents": "..."
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Canis",
+ "contents": "is a genus containing ... and is part of the [[Category:Animal]] kingdom."
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Domestic dog",
+ "contents": "[[Category:Canis]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Dog",
+ "contents": "#REDIRECT [[:Category:Domestic dog]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Wolf",
+ "contents": "[[Category:Canis]]"
+ },
+ {
+ "page": "German Shepherd",
+ "contents": "[[Category:Dog]] [[Has breed trait::Herding]] [[Has group::Herding group]]"
+ },
+ {
+ "page": "Dachshund",
+ "contents": "[[Category:Dog]] [[Has breed trait::Hunting]] [[Has group::Hound group]]"
+ },
+ {
+ "page": "Scottish Terrier",
+ "contents": "[[Category:Dog]] [[Has breed trait::Hunting]] [[Has group::Terrier group]]"
+ },
+ {
+ "page": "Irish Setter",
+ "contents": "[[Category:Dog]] [[Has breed trait::Sporting]] [[Has group::Sporting group]]"
+ },
+ {
+ "page": "Gray wolf",
+ "contents": "[[Category:Wolf]] [[Has trait::Hunting]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 all (max 10) animals with herding traits",
+ "condition": "[[Category:Animal]] AND [[Has trait::Herding]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "German Shepherd#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 all (max 10) animals with herding or hunting traits",
+ "condition": "[[Category:Animal]] <q>[[Has trait::Herding]] OR [[Has trait::Hunting]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "German Shepherd#0##",
+ "Scottish Terrier#0##",
+ "Dachshund#0##",
+ "Gray wolf#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 all (max 10) animals with herding or hunting traits (compact OR notation)",
+ "condition": "[[Category:Animal]][[Has trait::Herding||Hunting]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "German Shepherd#0##",
+ "Scottish Terrier#0##",
+ "Dachshund#0##",
+ "Gray wolf#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0804.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0804.json
new file mode 100644
index 00000000..735d2ee3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0804.json
@@ -0,0 +1,34 @@
+{
+ "description": "Test `_INST` with namespace prefix",
+ "setup": [
+ {
+ "page": "Example/Q0804/1",
+ "contents": "[[Category:Help:Q0804]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (Category: is correctly parsed despite the Help: NS prefix)",
+ "condition": "[[Category:Help:Q0804]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0804/1#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "es"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0901.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0901.json
new file mode 100644
index 00000000..017b52f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0901.json
@@ -0,0 +1,379 @@
+{
+ "description": "Test `_wpg`/`_txt` on various disjunction, conjunction queries (#19, #1060, #1056, #1057)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page-1",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page-2",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text-1",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text-2",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0901-1"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q0901-2"
+ },
+ {
+ "page": "Q0901/1",
+ "contents": "[[Category:Q0901-1]] [[Has page-1::Value 1]][[Has text-1::Value 1]] [[Has text-1::Value 3]]"
+ },
+ {
+ "page": "Q0901/1/1",
+ "contents": "[[Category:Q0901-1]] [[Has page-1::Value 1]][[Has page-2::Value 2]][[Has text-1::Value 1]][[Has text-2::Value 2]]"
+ },
+ {
+ "page": "Q0901/1/2",
+ "contents": "[[Category:Q0901-1]] [[Has page-1::Value 1]][[Has page-1::Value 2]][[Has text-1::Value 1]][[Has text-1::Value 2]]"
+ },
+ {
+ "page": "Q0901/2",
+ "contents": "[[Category:Q0901-1]] [[Has page-2::Value 1]][[Has text-2::Value 1]] [[Has page-1::Value 3]]"
+ },
+ {
+ "page": "Q0901/2/1",
+ "contents": "[[Category:Q0901-1]] [[Has page-2::Value 1]][[Has page-2::Value 2]][[Has text-2::Value 1]][[Has text-2::Value 2]]"
+ },
+ {
+ "page": "Q0901/3/1",
+ "contents": "[[Category:Q0901-1]] [[Has page-2::Q0901/1/2]]"
+ },
+ {
+ "page": "Q0901/3/2",
+ "contents": "[[Category:Q0901-2]] [[Has page-2::Q0901/2/1]]"
+ },
+ {
+ "page": "Q0901/4/1",
+ "contents": "[[Category:Q0901-1]] [[Has page-1::Q0901/3/2]]"
+ },
+ {
+ "page": "Q0901/4/2",
+ "contents": "[[Category:Q0901-2]] [[Has page-1::Q0901/3/2]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 simple conjection page/text value",
+ "condition": "[[Has page-1::Value 1]] AND [[Has text-1::Value 1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 simple conjection page/text value",
+ "condition": "[[Has page-1::Value 1]] AND [[Has text-1::Value 1]] AND [[Has page-2::+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0901/1/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 simple conjection page/text for multiple value",
+ "condition": "[[Has page-1::Value 1]] AND [[Has text-1::Value 1||Value2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 equivalent to #2",
+ "condition": "[[Has page-1::Value 1]] AND <q>[[Has text-1::Value 1]] OR [[Has text-1::Value 2]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Has page-1::Value 1]] AND <q>[[Has text-1::Value 1]] OR [[Has page-2::Value 2]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Category:Q0901-1]] AND <q>[[Has text-1::Value 1||Value 2]] OR [[Has page-2::Value 1||Value 2]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 5,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##",
+ "Q0901/2#0##",
+ "Q0901/2/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Category:Q0901-1]] AND <q>[[Has text-2::Value 1||Value 2]] AND <q>[[Has page-2::Value 1]] AND [[Has page-2::Value 2]]</q></q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0901/2/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "<q>[[Category:Q0901-1]] OR [[Category:Q0901-2]]</q> AND [[Has page-2::<q> [[Has text-1::Value 1]] OR [[Has text-2::Value 2]] </q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/3/1#0##",
+ "Q0901/3/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8 equivalent to #7",
+ "condition": "[[Category:Q0901-1||Q0901-2]] AND [[Has page-2::<q> [[Has text-1::Value 1]] OR [[Has text-2::Value 2]] </q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/3/1#0##",
+ "Q0901/3/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9",
+ "condition": "[[Has page-1::<q> [[Has page-2::<q> [[Has text-1::Value 1]] OR [[Has text-2::Value 2]] </q>]] </q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/4/1#0##",
+ "Q0901/4/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10 equivalent to #9 only in chain notation",
+ "condition": "[[Has page-1.Has page-2:: <q> [[Has text-1::Value 1]] OR [[Has text-2::Value 2]]</q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/4/1#0##",
+ "Q0901/4/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#11",
+ "condition": "[[Category:Q0901-1]] [[Has page-1.Has page-2:: <q> [[Has text-1::Value 1]] OR <q>[[Has text-2::Value 2]][[Has page-2::Value 2]]</q></q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0901/4/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#12",
+ "condition": "[[Category:Q0901-1||Q0901-2]] AND [[Has page-1.Has page-2:: <q><q>[[Has text-1::Value 1]][[Has page-2::Value 1]]</q> OR <q>[[Has text-2::Value 2]][[Has page-2::Value 2]]</q></q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/4/1#0##",
+ "Q0901/4/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#13, see issue #19",
+ "condition": "[[Has page-1::Value 1||Value 2]] [[Has text-1::Value 1||Value 2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#14 equivalent to #13",
+ "condition": "<q>[[Has page-1::Value 1]] OR [[Has page-1::Value 2]]</q> AND <q>[[Has text-1::Value 1]] OR [[Has text-1::Value 2]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/1/1#0##",
+ "Q0901/1/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#15",
+ "condition": "<q>[[Has page-1::Value 1]] OR [[Has page-2::Value 1]]</q> AND <q>[[Has text-1::Value 1]] OR [[Has text-2::Value 1]]</q> AND <q>[[Has text-1::Value 3]] OR [[Has page-1::Value 3]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/1#0##",
+ "Q0901/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#16",
+ "condition": "[[Has page-2:: <q>[[Has page-1::Value 1||Value 2]] [[Has text-1::Value 1||Value 2]]</q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0901/3/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#17",
+ "condition": "[[Has page-2:: <q>[[Has page-1::Value 1||Value 2]] [[Has text-1::Value 1||Value 2]]</q> || <q> [[Has page-2::Value 1||Value 2]]</q> ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q0901/3/1#0##",
+ "Q0901/3/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0902.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0902.json
new file mode 100644
index 00000000..0d0c7be7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0902.json
@@ -0,0 +1,65 @@
+{
+ "description": "Test `_txt` to correctly apply parentheses for somehting like (a OR b OR c) AND d (#556)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has conference type",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Conference-1",
+ "contents": "[[Has conference type::Presentation]], [[Has conference type::Tutorial]]"
+ },
+ {
+ "page": "Conference-2",
+ "contents": "[[Has conference type::Tutorial]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Conference-1]] [[Has conference type::Presentation||Tutorial]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Conference-1#0##"
+ ],
+ "count": "1",
+ "dataitems": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "<q>[[Conference-1]] [[Has conference type::Presentation||Tutorial||Another]]</q> OR [[Has conference type::Presentation||Tutorial||Another]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "results": [
+ "Conference-1#0##",
+ "Conference-2#0##"
+ ],
+ "count": "2",
+ "dataitems": []
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_HELP": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0903.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0903.json
new file mode 100644
index 00000000..32eb8c64
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0903.json
@@ -0,0 +1,347 @@
+{
+ "description": "Test `_wpg`/`_num`/`_txt` for disjunction OR || (T31866, #1059, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/Q0903/Number/1",
+ "contents": "[[Has number::1]]"
+ },
+ {
+ "page": "Example/Q0903/Number/2",
+ "contents": "[[Has number::2]]"
+ },
+ {
+ "page": "Example/Q0903/Number/3",
+ "contents": "[[Has number::3]]"
+ },
+ {
+ "page": "Example/Q0903/Title/1",
+ "contents": "[[Has text::Foo]] [[Has page::123]]"
+ },
+ {
+ "page": "Example/Q0903/Title/2",
+ "contents": "[[Has text::Foobar]] [[Has page::ABC]]"
+ },
+ {
+ "page": "Example/Q0903/Title/3",
+ "contents": "[[Has text::FOO]] [[Has page::abc]]"
+ },
+ {
+ "page": "Example/Q0903/Title/4",
+ "contents": "[[Has text::FoO]] [[Has page::aBc]]"
+ },
+ {
+ "page": "Example/Q0903/Title/5",
+ "contents": "[[Has text::FoO]] [[Has page::Example/Q0903/Title/2]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has number::1]] OR [[Has number::2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Number/1#0##",
+ "Example/Q0903/Number/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 same as #0",
+ "condition": "[[Has number::1||2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Number/1#0##",
+ "Example/Q0903/Number/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has text::~Foo*]][[Has page::123||ABC]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 same as #2",
+ "condition": "[[Has page::123||ABC]][[Has text::~Foo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 same as #2",
+ "condition": "[[Has text::~Foo*]][[Has page::123]] OR [[Has page::ABC]][[Has text::~Foo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "skip-on": {
+ "elastic" : "Finds 5 due to `oo`, `OO`, and `oO` being matched using an analyzed field"
+ },
+ "condition": "[[Has text::+]][[Has text::~*oo*||~*OO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5.elastic",
+ "skip-on": {
+ "elastic" : [ "not", "Only relevant to ES (see #5 comment)" ]
+ },
+ "condition": "[[Has text::+]][[Has text::~*oo*||~*OO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 5,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##",
+ "Example/Q0903/Title/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 same as #5",
+ "skip-on": {
+ "elastic" : "Finds 5 due to `oo`, `OO`, and `oO` being matched using an analyzed field"
+ },
+ "condition": "[[Has text::+]][[Has text::~*OO*||~*oo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6.elastic",
+ "skip-on": {
+ "elastic" : [ "not", "Only relevant to ES (see #6 comment)" ]
+ },
+ "condition": "[[Has text::+]][[Has text::~*OO*||~*oo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 5,
+ "results": [
+ "Example/Q0903/Title/1#0##",
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##",
+ "Example/Q0903/Title/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[Has page::+]][[Has page::~*a*||~*A*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##",
+ "Example/Q0903/Title/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8",
+ "skip-on": {
+ "elastic" : "Finds 3 due to `b` and `B` being matched using an analyzed field"
+ },
+ "condition": "[[Has page::+]][[Has page::~*B*]][[Has text::~*OO*||~*oo*||~*oO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8.elastic",
+ "skip-on": {
+ "elastic" : [ "not", "Only relevant to ES (see #8 comment)" ]
+ },
+ "condition": "[[Has page::+]][[Has page::~*B*]][[Has text::~*OO*||~*oo*||~*oO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9",
+ "skip-on": {
+ "elastic" : "Finds 4 due to `bc`, `Bc`, and `BC` being matched using an analyzed field"
+ },
+ "condition": "[[Has text::~*oO*]][[Has page::~*Bc*|| <q>[[Has page::~*a*||~*A*]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0903/Title/4#0##",
+ "Example/Q0903/Title/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9.elastic",
+ "skip-on": {
+ "elastic" : [ "not", "Only relevant to ES (see #9 comment)" ]
+ },
+ "condition": "[[Has text::~*oO*]][[Has page::~*Bc*|| <q>[[Has page::~*a*||~*A*]]</q>]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##",
+ "Example/Q0903/Title/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10, see issue #19",
+ "condition": "[[Has page::~*b*||~*B*]] AND [[Has text::~*oO*||~*OO*||~*oo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q0903/Title/2#0##",
+ "Example/Q0903/Title/3#0##",
+ "Example/Q0903/Title/4#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0904.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0904.json
new file mode 100644
index 00000000..649f739a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0904.json
@@ -0,0 +1,142 @@
+{
+ "description": "Test `_wpg`/`_txt` disjunction in connection with property hierarchies (#1060, en, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page-0",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page-1",
+ "contents": "[[Has type::Page]] [[Subproperty of::Has page-0]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page-2",
+ "contents": "[[Has type::Page]] [[Subproperty of::Has page-0]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text-1",
+ "contents": "[[Has type::Text]] [[Subproperty of::Has text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text-2",
+ "contents": "[[Has type::Text]] [[Subproperty of::Has text]]"
+ },
+ {
+ "page": "Sample/1/1",
+ "contents": "[[Has text-1::Value 1]] [[Has page-1::Value 1]]"
+ },
+ {
+ "page": "Sample/1/2",
+ "contents": "[[Has text-2::Value 1]] [[Has page-2::Value 1]]"
+ },
+ {
+ "page": "Sample/2/1",
+ "contents": "[[Has text-1::Value 2]] [[Has page-1::Value 2]]"
+ },
+ {
+ "page": "Sample/2/2",
+ "contents": "[[Has text-2::Value 2]] [[Has page-2::Value 2]]"
+ },
+ {
+ "page": "Sample/2/3",
+ "contents": "[[Has text-2::Value 3]] [[Has page-2::Value 3]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Has text-1::Value 1||Value 2]] AND [[Has page-0::Value 1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Sample/1/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has text-1::Value 1||Value 2]] AND [[Has page-0::Value 1||Value 2]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Sample/1/1#0##",
+ "Sample/2/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 equivalent to #1",
+ "condition": "<q>[[Has text-1::Value 1]] OR [[Has text-1::Value 2]]</q> AND <q>[[Has page-0::Value 1]] OR [[Has page-0::Value 2]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Sample/1/1#0##",
+ "Sample/2/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "<q>[[Has text-1::~*1*||~*2*]] OR [[Has text-2::Value 3]]</q> AND [[Has page-0::Value 1||Value 3]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Sample/1/1#0##",
+ "Sample/2/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgQSubpropertyDepth": 10,
+ "smwgQSubcategoryDepth": 10,
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/property hierarchies are currently not implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0905.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0905.json
new file mode 100644
index 00000000..8a0ef940
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0905.json
@@ -0,0 +1,209 @@
+{
+ "description": "Test `_wpg`/`_txt` conjunction queries (#1362, #1060)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/Q0905/1",
+ "contents": "[[Category:Q0905]] [[Has page::123]] [[Has page::ABC]] [[Has text::123]] [[Has text::ABC]]"
+ },
+ {
+ "page": "Example/Q0905/2",
+ "contents": "[[Category:Q0905]] [[Has page::1234]] [[Has page::ABCD]] [[Has text::1234]] [[Has text::ABCD]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Q0905]] [[Has page::123]] [[Has page::!ABCD]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Category:Q0905]] [[Has page::123]] [[Has page::ABCD]] [[Example/Q0905/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 regression case",
+ "condition": "[[Example/Q0905/1]] [[Has page::123]] [[Has page::ABCD]] [[Category:Q0905]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Category:Q0905]] [[Has page::123]] [[Has text::ABCD]] [[Example/Q0905/1]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 regression case",
+ "condition": "[[Example/Q0905/1]] [[Has page::123]] [[Has text::ABCD]] [[Category:Q0905]] ",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5, C AND (A OR B)",
+ "condition": "[[Category:Q0905]] [[!Example/Q0905/1]] <q>[[Has page::123]] OR [[Has page::!ABCD]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6, (A OR B) AND C",
+ "condition": "<q>[[Has page::123]] OR [[Has page::!ABCD]]</q> [[!Example/Q0905/1]] [[Category:Q0905]] ",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7, (A OR B) AND (C OR D)",
+ "condition": "[[Category:Q0905]] <q>[[Has page::123]] OR [[Has text::1234]]</q> <q>[[Has page::ABCD]] OR [[Has text::ABC]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0905/1#0##",
+ "Example/Q0905/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8, (A OR B) AND (C OR D) AND E",
+ "condition": "[[Category:Q0905]] <q>[[Has page::123]] OR [[Has text::1234]]</q> <q>[[Has page::ABCD]] OR [[Has text::ABC]]</q> [[Has page::123]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9, E AND (A OR B) AND (C OR D)",
+ "condition": "[[Category:Q0905]] [[Has page::123]] <q>[[Has page::123]] OR [[Has text::1234]]</q> <q>[[Has page::ABCD]] OR [[Has text::ABC]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10, (A OR B) AND (C OR D) AND !E AND F",
+ "condition": "<q>[[Has page::123]] OR [[Has page::ABCD]]</q><q>[[Has page::1234]] OR [[Has page::ABC]]</q>[[Has page::!123]][[Has page::ABCD]][[Category:Q0905]] ",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10, !E AND F AND (A OR B) AND (C OR D)",
+ "condition": "[[Category:Q0905]] [[Has page::!123]][[Has page::ABCD]]<q>[[Has page::123]] OR [[Has page::ABCD]]</q><q>[[Has page::1234]] OR [[Has page::ABC]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0905/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0906.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0906.json
new file mode 100644
index 00000000..5d218b48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0906.json
@@ -0,0 +1,163 @@
+{
+ "description": "Test `_wpg`/`_txt` with enabled `SMW_FIELDT_CHAR_NOCASE` (#1912, `smwgFieldTypeFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/Q0906/1",
+ "contents": "[[Category:Q0906]] [[Has page::noCASE]] [[Has text::noCASE]]"
+ },
+ {
+ "page": "Example/Q0906/2",
+ "contents": "[[Category:Q0906]] [[Has page::NOcase]] [[Has text::NOcase]]"
+ },
+ {
+ "page": "Example/Q0906/3",
+ "contents": "[[Category:Q0906]] [[Has page::NOoNcase]] [[Has text::NOoNcase]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (on _wpg type ~ match)",
+ "condition": "[[Category:Q0906]] [[Has page::~*noCa*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0906/1#0##",
+ "Example/Q0906/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (on _txt type as ~ match)",
+ "condition": "[[Category:Q0906]] [[Has text::~*NOcA*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0906/1#0##",
+ "Example/Q0906/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (on _wpg type !~ match)",
+ "condition": "[[Category:Q0906]] [[Has page::!~*noCa*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0906/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (on _txt type as !~ match)",
+ "condition": "[[Category:Q0906]] [[Has text::!~*NOcA*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0906/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "skip-on": {
+ "virtuoso": "Not suported",
+ "sesame": "Not suported",
+ "fuseki": "Not suported",
+ "blazegraph": "Not suported"
+ },
+ "about": "#4 (on _txt type as object match)",
+ "condition": "[[Category:Q0906]] [[Has text::nocase]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0906/1#0##",
+ "Example/Q0906/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "skip-on": {
+ "virtuoso": "Not suported",
+ "sesame": "Not suported",
+ "fuseki": "Not suported",
+ "blazegraph": "Not suported"
+ },
+ "about": "#5 (on _txt type as !object match)",
+ "condition": "[[Category:Q0906]] [[Has text::!nocase]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0906/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_NOCASE"
+ ],
+ "smwgFieldTypeFeatures": [
+ "SMW_FIELDT_CHAR_NOCASE"
+ ],
+ "smwgElasticsearchConfig" : {
+ "connection":{
+ "retries": 1
+ },
+ "query": {
+ "text.field.case.insensitive.eq.match": true
+ }
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "PostgreSQL requires \"citext\" otherwise it returns with \"Error: 42704 ERROR: type \"citext\" does not exist\"",
+ "sqlite": "NOCASE is not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0907.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0907.json
new file mode 100644
index 00000000..4b845b35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0907.json
@@ -0,0 +1,111 @@
+{
+ "description": "Test `_txt`/`_uri` with enabled `SMW_FIELDT_CHAR_LONG | SMW_FIELDT_CHAR_NOCASE` (#1912, #2499, `smwgFieldTypeFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/Q0907/1",
+ "contents": {
+ "import-from": "/../Fixtures/q-0907-1.txt"
+ }
+ },
+ {
+ "page": "Example/Q0907/2",
+ "contents": {
+ "import-from": "/../Fixtures/q-0907-2.txt"
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (on _txt within 300 char range)",
+ "condition": "[[Category:Q0907]] [[Has text::~*libero*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0907/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (on _txt within 300 char range, case insensitive)",
+ "condition": "[[Category:Q0907]] [[Has text::~*LIbero*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0907/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (on _uri within 300 char range)",
+ "condition": "[[Category:Q0907]] [[Has url::~*stack*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0907/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (on _uri within 300 char range)",
+ "condition": "[[Category:Q0907]] [[Has url::~*STACK*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0907/2#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_NOCASE"
+ ],
+ "smwgFieldTypeFeatures": [
+ "SMW_FIELDT_CHAR_NOCASE",
+ "SMW_FIELDT_CHAR_LONG"
+ ]
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "PostgreSQL requires \"citext\" otherwise it returns with \"Error: 42704 ERROR: type \"citext\" does not exist\"",
+ "sqlite": "NOCASE is not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0908.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0908.json
new file mode 100644
index 00000000..3632002f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0908.json
@@ -0,0 +1,219 @@
+{
+ "description": "Test `_wpg`/`_txt`/`_uri` on enabled `SMW_FIELDT_CHAR_LONG | SMW_FIELDT_CHAR_NOCASE` with `like:/nlike:` (#1912, #2499, `smwgFieldTypeFeatures`, `smwgSparqlQFeatures`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/Q0908/0",
+ "contents": "[[Category:Q0908]] [[Has page::Some title]] [[Has text::Some title]] [[Has url::http://example.org/some title]] {{#subobject: |Has page=SOME title |Has text=SOME title |Has url=http://example.org/SOME title |@category=Q0908 }}"
+ },
+ {
+ "page": "Example/Q0908/1",
+ "contents": "[[Category:Q0908]] [[Has page::Some title WiTH a VaLUE]] {{#subobject: |Has page=SOME title WITH A value |@category=Q0908 }}"
+ },
+ {
+ "page": "Example/Q0908/2",
+ "contents": "[[Category:Q0908]] [[Has text::Some title WiTH a VaLUE]] {{#subobject: |Has text=SOME title WITH A value |@category=Q0908 }}"
+ },
+ {
+ "page": "Example/Q0908/3",
+ "contents": "[[Category:Q0908]] [[Has url::http://example.org/Some title WiTH a VaLUE]] {{#subobject: |Has url=http://example.org/SOME title WITH A value |@category=Q0908 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (on _wpg, like:)",
+ "condition": "[[Category:Q0908]] [[Has page::like:some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/1#0##",
+ "Example/Q0908/1#0##_fddfd44d1be8e8a7f3ea5f4893df9963"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (on _wpg, nlike:)",
+ "condition": "[[Category:Q0908]] [[Has page::nlike:some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/0#0##",
+ "Example/Q0908/0#0##_34013e9e1a62a2138034ac5287a7cf0e"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (on _txt, equal)",
+ "condition": "[[Category:Q0908]] [[Has text::some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/2#0##",
+ "Example/Q0908/2#0##_52d6bd41c1e8946f754f9687b88ded4c"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (on _txt, like:)",
+ "condition": "[[Category:Q0908]] [[Has text::like:some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/2#0##",
+ "Example/Q0908/2#0##_52d6bd41c1e8946f754f9687b88ded4c"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (on _txt, nlike:)",
+ "condition": "[[Category:Q0908]] [[Has text::nlike:some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/0#0##",
+ "Example/Q0908/0#0##_34013e9e1a62a2138034ac5287a7cf0e"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (on _uri, equal)",
+ "skip-on": {
+ "fuseki": "Using lcase on _uri/equal is not supported",
+ "sesame": "Using lcase on _uri/equal is not supported",
+ "blazegraph": "Using lcase on _uri/equal is not supported",
+ "virtuoso": "Using lcase on _uri/equal is not supported"
+ },
+ "condition": "[[Category:Q0908]] [[Has url::http://example.org/some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/3#0##",
+ "Example/Q0908/3#0##_bc98426861e347260e7aa56265803598"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 (on _uri, like:)",
+ "condition": "[[Category:Q0908]] [[Has url::like:http://example.org/some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/3#0##",
+ "Example/Q0908/3#0##_bc98426861e347260e7aa56265803598"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 (on _uri, nlike:)",
+ "condition": "[[Category:Q0908]] [[Has url::nlike:http://example.org/some title with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/0#0##",
+ "Example/Q0908/0#0##_34013e9e1a62a2138034ac5287a7cf0e"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8 (on _uri, in:)",
+ "condition": "[[Category:Q0908]] [[Has url::in:with a value]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q0908/3#0#",
+ "Example/Q0908/3#0##_bc98426861e347260e7aa56265803598"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_NOCASE"
+ ],
+ "smwgFieldTypeFeatures": [
+ "SMW_FIELDT_CHAR_NOCASE",
+ "SMW_FIELDT_CHAR_LONG"
+ ],
+ "smwgElasticsearchConfig" : {
+ "connection":{
+ "retries": 1
+ },
+ "query": {
+ "uri.field.case.insensitive": true,
+ "text.field.case.insensitive.eq.match": true
+ }
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "postgres": "PostgreSQL requires \"citext\" otherwise it returns with \"Error: 42704 ERROR: type \"citext\" does not exist\"",
+ "sqlite": "NOCASE is not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0909.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0909.json
new file mode 100644
index 00000000..7a21d237
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0909.json
@@ -0,0 +1,180 @@
+{
+ "description": "Test `_txt/`_uri`/`_num`/`_dat` with `!...` (NEQ)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/Q0909/1",
+ "contents": "[[Has text::Foo/Bar]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/2",
+ "contents": "[[Has text::Foo/Foobar]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/3",
+ "contents": "[[Has url::http://example.org/Foo/Bar]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/4",
+ "contents": "[[Has url::http://example.org/Foo/Foobar]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/5",
+ "contents": "[[Has number::123]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/6",
+ "contents": "[[Has number::456]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/7",
+ "contents": "[[Has date::1 Jan 1970 12:00]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/8",
+ "contents": "[[Has date::1 Jan 1970 12:01]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/9",
+ "contents": "[[Has page::123]] [[Category:Q0909]]"
+ },
+ {
+ "page": "Example/Q0909/10",
+ "contents": "[[Has page::456]] [[Category:Q0909]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Q0909]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 10,
+ "results": [
+ "Example/Q0909/1#0##",
+ "Example/Q0909/2#0##",
+ "Example/Q0909/3#0##",
+ "Example/Q0909/4#0##",
+ "Example/Q0909/5#0##",
+ "Example/Q0909/6#0##",
+ "Example/Q0909/7#0##",
+ "Example/Q0909/8#0##",
+ "Example/Q0909/9#0##",
+ "Example/Q0909/10#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (! on text)",
+ "condition": "[[Category:Q0909]] [[Has text::+]] [[Has text::!Foo/Bar]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0909/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (! on uri)",
+ "condition": "[[Category:Q0909]] [[Has url::+]] [[Has url::!http://example.org/Foo/Bar]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0909/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (! on number)",
+ "condition": "[[Category:Q0909]] [[Has number::+]] [[Has number::!123]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0909/6#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (! on date)",
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.+ doesn't support this!"
+ },
+ "condition": "[[Category:Q0909]] [[Has date::+]] [[Has date::!1 Jan 1970 12:00]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0909/8#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (! on page)",
+ "condition": "[[Category:Q0909]] [[Has page::+]] [[Has page::!123]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q0909/10#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0910.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0910.json
new file mode 100644
index 00000000..2d5e04ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0910.json
@@ -0,0 +1,113 @@
+{
+ "description": "Test `SMW_QSORT_UNCONDITIONAL` (`smwgQSortFeatures`, skip-on all SPARQL repositories, postgres)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Q0910/1",
+ "contents": "[[Has page::123]] [[Category:Q0910]]"
+ },
+ {
+ "page": "Q0910/2",
+ "contents": "[[Has text::ABC]] [[Category:Q0910]]"
+ },
+ {
+ "page": "Q0910/3",
+ "contents": "[[Has text::345]] [[Category:Q0910]]"
+ },
+ {
+ "page": "Q0910/Q.1",
+ "contents": "{{#ask: [[Category:Q0910]] |?Has page |?Has text |sort=Has page,Has text |order=desc, desc }}"
+ },
+ {
+ "page": "Q0910/Q.2",
+ "contents": "{{#ask: [[Category:Q0910]] |?Has page |?Has text |sort=Has page,Has text |order=desc, asc }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (SMW_QSORT_UNCONDITIONAL display, 2, 3)",
+ "condition": "[[Category:Q0910]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10",
+ "sort": {
+ "Has_page" : "DESC"
+ }
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q0910/1#0##",
+ "Q0910/2#0##",
+ "Q0910/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#1 (SMW_QSORT_UNCONDITIONAL on Has page=desc, Has text=desc)",
+ "skip-on": {
+ "postgres": "Has different order than MySQL/SQLite, needs investigation."
+ },
+ "subject": "Q0910/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/1\">Q0910/1</a></td><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/2\">Q0910/2</a></td><td class=\"Has-page smwtype_wpg\"></td><td class=\"Has-text smwtype_txt\">ABC</td>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/3\">Q0910/3</a></td><td class=\"Has-page smwtype_wpg\"></td><td class=\"Has-text smwtype_txt\" data-sort-value=\"345\">345</td>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#2 (SMW_QSORT_UNCONDITIONAL on Has page=desc, Has text=asc)",
+ "skip-on": {
+ "postgres": "Has different order than MySQL/SQLite, needs investigation."
+ },
+ "subject": "Q0910/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/1\">Q0910/1</a></td><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<tr data-row-number=\"2\" class=\"row-even\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/3\">Q0910/3</a></td><td class=\"Has-page smwtype_wpg\"></td><td class=\"Has-text smwtype_txt\" data-sort-value=\"345\">345</td>",
+ "<tr data-row-number=\"3\" class=\"row-odd\"><td class=\"smwtype_wpg\"><a href=.* title=\"Q0910/2\">Q0910/2</a></td><td class=\"Has-page smwtype_wpg\"></td><td class=\"Has-text smwtype_txt\">ABC</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgQSortFeatures": [
+ "SMW_QSORT",
+ "SMW_QSORT_UNCONDITIONAL"
+ ],
+ "smwgElasticsearchConfig": {
+ "query": {
+ "sort.property.must.exists": false
+ }
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "SMW_QSORT_UNCONDITIONAL isn't implemented",
+ "sesame": "SMW_QSORT_UNCONDITIONAL isn't implemented",
+ "fuseki": "SMW_QSORT_UNCONDITIONAL isn't implemented",
+ "blazegraph": "SMW_QSORT_UNCONDITIONAL isn't implemented"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0911.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0911.json
new file mode 100644
index 00000000..f46b9c7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-0911.json
@@ -0,0 +1,54 @@
+{
+ "description": "Test `_wpg` empty chain/subquery (AND, OR)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q0911/1",
+ "contents": "[[Has page::Q0911]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (OR)",
+ "condition": "[[Has page::Q0911]] OR [[Some page.Another page::Foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q0911/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (AND)",
+ "condition": "[[Has page::Q0911]] [[Some page.Another page::Foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1002.json
new file mode 100644
index 00000000..7bfa5183
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1002.json
@@ -0,0 +1,167 @@
+{
+ "description": "Test `_dat` range for non strict comparators (#285, `smwStrictComparators=false`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/1002/10-Jan",
+ "contents": "[[Category:Date range]] [[Has date::10 Jan 1970]]"
+ },
+ {
+ "page": "Example/1002/12-Jan",
+ "contents": "[[Category:Date range]] [[Has date::12 Jan 1970]]"
+ },
+ {
+ "page": "Example/1002/20-Jan",
+ "contents": "[[Category:Date range]] [[Has date::20 Jan 1970]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 greater than, equal to (non strict)",
+ "condition": "[[Category:Date range]] [[Has date::>10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 greater than, equal to",
+ "condition": "[[Category:Date range]] [[Has date::≥10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 greater than",
+ "condition": "[[Category:Date range]] [[Has date::>>10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Category:Date range]] [[Has date::>11 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 smaller than, equal to (non strict)",
+ "condition": "[[Category:Date range]] [[Has date::<20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 smaller than, equal to",
+ "condition": "[[Category:Date range]] [[Has date::≤20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##",
+ "Example/1002/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 smaller than",
+ "condition": "[[Category:Date range]] [[Has date::<<20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[Category:Date range]] [[Has date::<19 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1002/10-Jan#0##",
+ "Example/1002/12-Jan#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 rdfs / subproperty/subcategory hierarchies are not supported"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1003.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1003.json
new file mode 100644
index 00000000..861760aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1003.json
@@ -0,0 +1,165 @@
+{
+ "description": "Test `_dat` range for strict comparators (#285, `smwStrictComparators=true`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/1003/10-Jan",
+ "contents": "[[Category:Date range]] [[Has date::10 Jan 1970]]"
+ },
+ {
+ "page": "Example/1003/12-Jan",
+ "contents": "[[Category:Date range]] [[Has date::12 Jan 1970]]"
+ },
+ {
+ "page": "Example/1003/20-Jan",
+ "contents": "[[Category:Date range]] [[Has date::20 Jan 1970]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 greater than",
+ "condition": "[[Category:Date range]] [[Has date::>10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/12-Jan#0##",
+ "Example/1003/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 greater than",
+ "condition": "[[Category:Date range]] [[Has date::>>10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/12-Jan#0##",
+ "Example/1003/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 greater than, equal to",
+ "condition": "[[Category:Date range]] [[Has date::≥10 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1003/10-Jan#0##",
+ "Example/1003/12-Jan#0##",
+ "Example/1003/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Category:Date range]] [[Has date::>11 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/12-Jan#0##",
+ "Example/1003/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 smaller than",
+ "condition": "[[Category:Date range]] [[Has date::<20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/10-Jan#0##",
+ "Example/1003/12-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 smaller than",
+ "condition": "[[Category:Date range]] [[Has date::<<20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/10-Jan#0##",
+ "Example/1003/12-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 smaller than, equal to",
+ "condition": "[[Category:Date range]] [[Has date::≤20 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1003/10-Jan#0##",
+ "Example/1003/12-Jan#0##",
+ "Example/1003/20-Jan#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7",
+ "condition": "[[Category:Date range]] [[Has date::<19 Jan 1970]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1003/10-Jan#0##",
+ "Example/1003/12-Jan#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": true
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 somwhow fails for >/>>"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1004.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1004.json
new file mode 100644
index 00000000..22634725
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1004.json
@@ -0,0 +1,175 @@
+{
+ "description": "Test `_dat` range for `~`/`!~` comparators (#1178, `smwStrictComparators=false`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/1004/15-Jun",
+ "contents": "{{#subobject:|@category=E-1004|Has date=15 Jun 2011 17:59:59}}{{#subobject:|@category=E-1004|Has date=15 Jun 2011 18:53}}{{#subobject:|@category=E-1004|Has date=15 Jun 2011 18:43}}{{#subobject:|@category=E-1004|Has date=15 Jun 2011 19:53}}"
+ },
+ {
+ "page": "Example/1004/16-Jun",
+ "contents": "{{#subobject:|@category=E-1004|Has date=16 Jul 2011 19:53}}"
+ },
+ {
+ "page": "Example/1004/17-Jun",
+ "contents": "{{#subobject:|@category=E-1004|Has date=17 Jun 2011 19:53:05}}{{#subobject:|@category=E-1004|Has date=17 Jun 2011 19:53:15}}{{#subobject:|@category=E-1004|Has date=17 Jun 2011 19:53:07}}{{#subobject:|@category=E-1004|Has date=17 Jun 2011 19:54:07}}"
+ },
+ {
+ "page": "Example/1004/17-Jun-1356",
+ "contents": "{{#subobject:|@category=E-1004|Has date=17 Jun 1356 19:53:05}}{{#subobject:|@category=E-1004|Has date=17 Jun 1356 19:53:15}}{{#subobject:|@category=E-1004|Has date=17 Jun 1356 19:53:07}}{{#subobject:|@category=E-1004|Has date=17 Jun 1356 19:54:07}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 for the same hour",
+ "condition": "[[Category:E-1004]] [[Has date::~15 Jun 2011 18:00]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1004/15-Jun#0##_3ac75bf67c06e184b53c2fe47dc9838f",
+ "Example/1004/15-Jun#0##_25fd2aa8d8bc9c7fe64f1161beb1e53e"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 for the same day",
+ "condition": "[[Category:E-1004]] [[Has date::~15 Jun 2011]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/1004/15-Jun#0##_3ac75bf67c06e184b53c2fe47dc9838f",
+ "Example/1004/15-Jun#0##_25fd2aa8d8bc9c7fe64f1161beb1e53e",
+ "Example/1004/15-Jun#0##_42f6ee5000aee48058fef6e89d988122",
+ "Example/1004/15-Jun#0##_93e03330595abc209f006bd389af97ca"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 for the same month",
+ "condition": "[[Category:E-1004]] [[Has date::~ Jun 2011]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 8,
+ "results": [
+ "Example/1004/15-Jun#0##_3ac75bf67c06e184b53c2fe47dc9838f",
+ "Example/1004/15-Jun#0##_25fd2aa8d8bc9c7fe64f1161beb1e53e",
+ "Example/1004/15-Jun#0##_42f6ee5000aee48058fef6e89d988122",
+ "Example/1004/15-Jun#0##_93e03330595abc209f006bd389af97ca",
+ "Example/1004/17-Jun#0##_546d85f0e65445d6ddebc327d82bc9b4",
+ "Example/1004/17-Jun#0##_d6ae457c1063eb3280a01617e331ae16",
+ "Example/1004/17-Jun#0##_97d8169c6bcb5357c7d4ed617de6b683",
+ "Example/1004/17-Jun#0##_2321a4ca8cce9a9f1200e7bcc6530b32"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 not for the same month",
+ "condition": "[[Category:E-1004]] [[Has date::~ 2011]] [[Has date::!~ Jun 2011]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1004/16-Jun#0##_c13e26365fd030c4505450df0da097ca"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 for the same minute",
+ "condition": "[[Category:E-1004]] [[Has date::~ 17 Jun 2011 19:53 ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1004/17-Jun#0##_546d85f0e65445d6ddebc327d82bc9b4",
+ "Example/1004/17-Jun#0##_d6ae457c1063eb3280a01617e331ae16",
+ "Example/1004/17-Jun#0##_97d8169c6bcb5357c7d4ed617de6b683"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 not for the same minute",
+ "condition": "[[Category:E-1004]] [[Has date::~ 17 Jun 2011 19:00]] [[Has date::!~ 17 Jun 2011 19:53]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1004/17-Jun#0##_2321a4ca8cce9a9f1200e7bcc6530b32"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 for the same sec",
+ "condition": "[[Category:E-1004]] [[Has date::~ 17 Jun 2011 19:53:05 ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1004/17-Jun#0##_546d85f0e65445d6ddebc327d82bc9b4"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 for the same year",
+ "condition": "[[Category:E-1004]] [[Has date::!~ 2011 ]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/1004/17-Jun-1356#0##_4034c283fdf86e4170912b3c85c970e8",
+ "Example/1004/17-Jun-1356#0##_c649d0effa6cd11dd6a4def82ac6523f",
+ "Example/1004/17-Jun-1356#0##_b080b00743a9e79d619f417b2268156f",
+ "Example/1004/17-Jun-1356#0##_af39ab3a77d159be5d8f2a1ddfc72d37"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 somwhow fails for >/>>"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1101.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1101.json
new file mode 100644
index 00000000..ed2a11fa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1101.json
@@ -0,0 +1,189 @@
+{
+ "description": "Test _rec for non strict comparators queries (`smwStrictComparators=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has title",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has year",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has population",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km²]] [[Corresponds to::1000 m²]] [[Corresponds to::988.42153133973 rood]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Population density",
+ "contents": "[[Has type::Record]] [[Has fields::Has population;Has area]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Book record",
+ "contents": "[[Has type::Record]] [[Has fields::Has title;Has year]]"
+ },
+ {
+ "page": "Example/1101/City A",
+ "contents": "[[Category:Record example]] [[Population density::3900;1 km²]]"
+ },
+ {
+ "page": "Example/1101/City B",
+ "contents": "[[Category:Record example]] [[Population density::20169;1 km²]]"
+ },
+ {
+ "page": "Example/1101/Book A",
+ "contents": "[[Category:Record example]] [[Book record::Title with $&%'\\;* special characters ; 2001]]"
+ },
+ {
+ "page": "Example/1101/Book B",
+ "contents": "[[Category:Record example]] [[Book record::Book B; 2000]]"
+ },
+ {
+ "page": "Example/1101/Book C",
+ "contents": "[[Category:Record example]] [[Book record::Book C;30 Dec 2001]]"
+ },
+ {
+ "page": "Example/1101/Book D",
+ "contents": "[[Category:Record example]] [[Book record::Book D;1900]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 distinct value search",
+ "condition": "[[Category:Record example]] [[Population density::3900;1 km²]]",
+ "printouts": [
+ "Population density"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1101/City A#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Population_density",
+ "value": "3900;1 km²"
+ }
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1, T23926 RecordsToContainSpecialCharactersCausedByHtmlEncoding",
+ "condition": "[[Category:Record example]] [[Book record::Title with $&%'\\;* special characters ; 2001]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1101/Book A#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2, T36019 RecordToContainFieldComparator",
+ "condition": "[[Category:Record example]] [[Book record::?;<30 Dec 2001]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/1101/Book A#0##",
+ "Example/1101/Book B#0##",
+ "Example/1101/Book C#0##",
+ "Example/1101/Book D#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "condition": "[[Category:Record example]] [[Book record::?;<1901]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1101/Book D#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "condition": "[[Category:Record example]] [[Book record::?;>30 Dec 2001]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1101/Book C#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5",
+ "condition": "[[Category:Record example]] [[Book record::?;>1901]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1101/Book A#0##",
+ "Example/1101/Book B#0##",
+ "Example/1101/Book C#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6",
+ "condition": "[[Category:Record example]] [[Book record::?;!2001]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/1101/Book B#0##",
+ "Example/1101/Book C#0##",
+ "Example/1101/Book D#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1102.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1102.json
new file mode 100644
index 00000000..854b954a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1102.json
@@ -0,0 +1,95 @@
+{
+ "description": "Test `_rec` queries in combination with `_dat` `~/!~` search pattern (#1178, `smwStrictComparators=false`, skip virtuoso)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has status",
+ "contents": "[[Has type::Text]] [[Allows value::open]] [[Allows value::closed]] [[Allows value::in progress]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has status record",
+ "contents": "[[Has type::Record]] [[Has fields::Has date; Has status]]"
+ },
+ {
+ "page": "Example/1102/15-Dec-2007",
+ "contents": "{{#subobject:|@category=E-1102|Has status record=15 Dec 2007;closed}}{{#subobject:|@category=E-1102 |Has status record=15 Dec 2007;open}}"
+ },
+ {
+ "page": "Example/1102/16-Dec-2007",
+ "contents": "{{#subobject:|@category=E-1102 |Has status record=16 Dec 2007;closed}}"
+ },
+ {
+ "page": "Example/1102/345-BC",
+ "contents": "{{#subobject:|@category=E-1102|Has status record=345 BC;closed}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 disjunctive match for any status",
+ "condition": "[[Category:E-1102]] [[Has status record::~Dec 2007;?]] OR [[Has status record::~345 BC;?]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Example/1102/15-Dec-2007#0##_32971b257b2cfa9a364ae22e21ab4adf",
+ "Example/1102/15-Dec-2007#0##_a95f61c99770cf350f1388ae46e973bb",
+ "Example/1102/16-Dec-2007#0##_f1fd6537c6d2576390a45ba86e58ed5b",
+ "Example/1102/345-BC#0##_6f2d86633d90e76f0e9fa5e5d1fe6598"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 all closed in Dec 2007",
+ "condition": "[[Category:E-1102]] [[Has status record::~Dec 2007;closed]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1102/15-Dec-2007#0##_32971b257b2cfa9a364ae22e21ab4adf",
+ "Example/1102/16-Dec-2007#0##_f1fd6537c6d2576390a45ba86e58ed5b"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 all closed not at the 15 Dec 2007",
+ "condition": "[[Category:E-1102]] [[Has status record::!~ 15 Dec 2007;closed]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/1102/16-Dec-2007#0##_f1fd6537c6d2576390a45ba86e58ed5b",
+ "Example/1102/345-BC#0##_6f2d86633d90e76f0e9fa5e5d1fe6598"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false
+ },
+ "meta": {
+ "skip-on": {
+ "virtuoso": "Virtuoso 6.1 fails for BC"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1103.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1103.json
new file mode 100644
index 00000000..444ee226
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1103.json
@@ -0,0 +1,100 @@
+{
+ "description": "Test `_rec` using some additional search pattern (#1189, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "hasWeightMeasure",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 g, gram, grams]] [[Corresponds to::0.2 teaspoon, teaspoons]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "hasIngredient",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text number record",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "hasCookingIngredient",
+ "contents": "[[Has type::Record]] [[Has fields::hasIngredient;hasWeightMeasure]]"
+ },
+ {
+ "page": "Example/1103/1",
+ "contents": "[[Category:E-1103]][[Has text number record::SomeText;123]]"
+ },
+ {
+ "page": "Example/1103/2",
+ "contents": "[[Category:E-1103]][[hasCookingIngredient::salt:0.25 teaspoon]]"
+ },
+ {
+ "page": "Example/1103/3",
+ "contents": "{{#ask: [[Category:E-1103]][[hasCookingIngredient::salt;~teaspoon]] |?hasCookingIngredient}}"
+ },
+ {
+ "page": "Example/1103/4",
+ "contents": "[[Category:E-1103]][[Has text number record::SomeText;1234]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#1 verify no cached DataValue is invoked and causes wrong object deseralization (see #1189)",
+ "subject": "Example/1103/3",
+ "assert-output": {
+ "to-contain": [
+ "&quot;teaspoon&quot; is not a number"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0 simple search that causes an error",
+ "condition": "[[Category:E-1103]] [[Has status record::SomeText;~12]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 0
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1, see issue #1207",
+ "condition": "[[Category:E-1103]] [[Has text number record::~*eT*;>123]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1103/4#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwStrictComparators": true
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1104.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1104.json
new file mode 100644
index 00000000..9391654f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1104.json
@@ -0,0 +1,86 @@
+{
+ "description": "Test `_rec` to find correct target for redirected property (#1244, en)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has status",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Project",
+ "contents": "[[Has type::Record]] [[Has fields::Has status;Has date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has project status",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has status",
+ "contents": "#REDIRECT [[Property:Has project status]]"
+ },
+ {
+ "page": "Example/1103/1",
+ "contents": "[[Category:Record redirect example]] [[Project::closed;15 Jan 1970]]"
+ },
+ {
+ "page": "Example/1103/2",
+ "contents": "{{#ask: [[Category:Record redirect example]] [[Project::closed;~1970]]|?Project=Status|+index=1|format=plainlist}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "format",
+ "about": "#0 indexed output",
+ "subject": "Example/1103/2",
+ "assert-output": {
+ "to-contain": [
+ "title=\"Property:Project\">Status</a>: closed"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Record redirect example]] [[Project::closed;~1970]]",
+ "printouts": [
+ "Project"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/1103/1#0##"
+ ],
+ "datavalues": [
+ {
+ "property": "Project",
+ "value": "closed;15 Jan 1970"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false,
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1105.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1105.json
new file mode 100644
index 00000000..33276325
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1105.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test `_rec` in combination with named subobject (T49472, #1300, en, `smwStrictComparators=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record text",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]]"
+ },
+ {
+ "page": "Example/Q1105/1",
+ "contents": "{{#subobject: Test|Has text=Foo|Has record text=abc;123}} {{#subobject: Test|Has text=Bar}}"
+ },
+ {
+ "page": "Example/Q1105/2",
+ "contents": "{{#subobject: Test|Has text=Bar}} {{#subobject: Test|Has text=Foo|Has record text=abc;456}}"
+ },
+ {
+ "page": "Example/Q1105/3",
+ "contents": "{{#subobject: Test|Has text=Foo|Has record text=def;456}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 declaration order does not matter (oppose to T49472)",
+ "condition": "[[Has record text::abc;?]]",
+ "printouts": [
+ "Has record text"
+ ],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q1105/1#0##Test",
+ "Example/Q1105/2#0##Test"
+ ],
+ "datavalues": [
+ {
+ "property": "Has record text",
+ "value": "abc;123"
+ },
+ {
+ "property": "Has record text",
+ "value": "abc;456"
+ }
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 same as #0 only to use property chain notation",
+ "condition": "[[Has record text.Has text::abc]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q1105/1#0##Test",
+ "Example/Q1105/2#0##Test"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false,
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1106.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1106.json
new file mode 100644
index 00000000..0e1c0bdd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1106.json
@@ -0,0 +1,116 @@
+{
+ "description": "Test `_rec` with `~/!~` comparators on allowed values (#1207, `smwStrictComparators=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has restricted status record",
+ "contents": "[[Has type::Text]] [[Allows value::open]], [[Allows value::closed]], [[Allows value::in progress]]"
+ },
+ {
+ "page": "Example/Q1106/1",
+ "contents": "[[Category:E-Q1106]][[Has restricted status record::open]]"
+ },
+ {
+ "page": "Example/Q1106/2",
+ "contents": "[[Category:E-Q1106]]{{#set:Has restricted status record=closed}}"
+ },
+ {
+ "page": "Example/Q1106/3",
+ "contents": "{{#subobject:Has restricted status record=in progress|@category=E-Q1106}}"
+ },
+ {
+ "page": "Example/Q1106/4",
+ "contents": "[[Category:E-Q1106]][[Has restricted status record::none of the above]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 like *en*",
+ "condition": "[[Category:E-Q1106]][[Has restricted status record::~*en*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1106/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 like cl*",
+ "condition": "[[Category:E-Q1106]][[Has restricted status record::~cl*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1106/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 not like cl*",
+ "condition": "[[Category:E-Q1106]][[Has restricted status record::!~cl*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Example/Q1106/1#0##",
+ "Example/Q1106/3#0##_5005bc588cbc65273b9c8d9b96bf5c01"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 not like cl* AND *in*",
+ "condition": "[[Category:E-Q1106]]<q>[[Has restricted status record::!~cl*]] AND [[Has restricted status record::!~*in*]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1106/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 not like cl* OR *in* (doesn't make much sense, we test it anyway)",
+ "condition": "[[Category:E-Q1106]]<q>[[Has restricted status record::!~cl*]] OR [[Has restricted status record::!~*in*]]</q>",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Example/Q1106/1#0##",
+ "Example/Q1106/2#0##",
+ "Example/Q1106/3#0##_5005bc588cbc65273b9c8d9b96bf5c01"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwStrictComparators": false,
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1107.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1107.json
new file mode 100644
index 00000000..5f2e6dce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1107.json
@@ -0,0 +1,84 @@
+{
+ "description": "Test `_rec`/`_mlt_rec`(`_PDESC`) to use property chaining (`wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]] [[Has property description::Text property@en]] [[Category:Q1107]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]] [[Has property description::Number property@en]] [[Category:Q1107]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has record text",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]]"
+ },
+ {
+ "page": "Example/Q1107/1",
+ "contents": "{{#subobject: Test|Has text=Foo|Has record text=abc;222}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "condition": "[[Category:Q1107]] [[Has property description.Language code::en]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Has text#102##",
+ "Has number#102##"
+ ]
+ },
+ "store": {
+ "clear-cache": true
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "condition": "[[Has subobject.Has record text.Has number::222]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1107/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Has record text.Has number::222]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1107/1#0##Test"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1108.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1108.json
new file mode 100644
index 00000000..467c6d6e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1108.json
@@ -0,0 +1,106 @@
+{
+ "description": "Test conditions and constraint validations for allowed values `_LIST` and uniqueness `_PVUC` (#1207, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]] [[Allows value::open]], [[Allows value::closed]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has uniqueness",
+ "contents": "[[Has type::Text]] [[Has uniqueness constraint::true]]"
+ },
+ {
+ "page": "Example/Q1108/1",
+ "contents": "[[Category:Q1108]][[Has text::open]]"
+ },
+ {
+ "page": "Example/Q1108/2",
+ "contents": "[[Category:Q1108]][[Has text::close]]"
+ },
+ {
+ "page": "Example/Q1108/3",
+ "contents": "[[Category:Q1108]][[Has text::invalid]]"
+ },
+ {
+ "page": "Example/Q1108/4",
+ "contents": "[[Category:Q1108]] [[Has uniqueness::Foo]]"
+ },
+ {
+ "page": "Example/Q1108/5",
+ "contents": "[[Category:Q1108]] [[Has uniqueness::Foo]]"
+ },
+ {
+ "page": "Example/Q1108/Q1",
+ "contents": "{{#ask: [[Category:Q1108]] [[Has uniqueness::Foo]] |link=none}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#3 embedded query with uniqueness condition works without raising an issue due to different context page",
+ "subject": "Example/Q1108/Q1",
+ "assert-output": {
+ "to-contain": [
+ "Example/Q1108/4"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0 matches one with open",
+ "condition": "[[Category:Q1108]][[Has text::open]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1108/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 matches none",
+ "condition": "[[Category:Q1108]][[Has text::invalid]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "condition": "[[Category:Q1108]] [[Has uniqueness::Foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": 10
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Example/Q1108/4#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgDVFeatures": [
+ "SMW_DV_PVUC"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1200.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1200.json
new file mode 100644
index 00000000..4a7ae9cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1200.json
@@ -0,0 +1,210 @@
+{
+ "description": "Test `_wpg/`_txt` with `~*` and `.../...` queries (ES only)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1200/1/Foo bar/Bar",
+ "contents": "[[Category:Q1200]] [[Has text::Foo bar/Bar]] [[Has page::Foo bar/Bar]]"
+ },
+ {
+ "page": "Q1200/1/Foobar/Bar/Foo",
+ "contents": "[[Category:Q1200]] [[Has text::Foobar/Bar/Foo]] [[Has page::Foobar/Bar/Foo]]"
+ },
+ {
+ "page": "Q1200/1/Foo BAR/Foo",
+ "contents": "[[Category:Q1200]] [[Has text::Foo BAR/Foo]] [[Has page::Foo BAR/Foo]]"
+ },
+ {
+ "page": "Q1200/1/Foo/Bar",
+ "contents": "[[Category:Q1200]] [[Has text::Foo/Bar]] [[Has page::Foo/Bar]]"
+ },
+ {
+ "page": "Q1200/1/Foo",
+ "contents": "[[Category:Q1200]] [[Has text::Foo]] [[Has page::Foo]]"
+ },
+ {
+ "page": "Q1200/2",
+ "contents": "[[Category:Q1200]] {{#subobject: |Has page=Q1200/2 |Has text=Q1200/2 |Q1200/2=A,B,C|+sep=, }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (~*bar*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[~*bar*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1200/1/Foo bar/Bar#0##",
+ "Q1200/1/Foobar/Bar/Foo#0##",
+ "Q1200/1/Foo BAR/Foo#0##",
+ "Q1200/1/Foo/Bar#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (~*Bar/*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[~*Bar/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1200/1/Foo bar/Bar#0##",
+ "Q1200/1/Foobar/Bar/Foo#0##",
+ "Q1200/1/Foo BAR/Foo#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (~*Foo bar/*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[~*Foo bar/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1200/1/Foo bar/Bar#0##",
+ "Q1200/1/Foo BAR/Foo#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (~F??Bar/*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[~*F??Bar/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1200/1/Foobar/Bar/Foo#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (Has page:: ~*bar*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[Has page::~*bar*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1200/1/Foo bar/Bar#0##",
+ "Q1200/1/Foobar/Bar/Foo#0##",
+ "Q1200/1/Foo BAR/Foo#0##",
+ "Q1200/1/Foo/Bar#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (Has page::~*Bar/*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1200]] [[Has page::~*Bar/*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1200/1/Foo bar/Bar#0##",
+ "Q1200/1/Foobar/Bar/Foo#0##",
+ "Q1200/1/Foo BAR/Foo#0##",
+ "Q1200/1/Foo/Bar#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 (Has subobject::~*Q1200*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has subobject::~*Q1200*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1200/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 (-Q1200/2.-Has subobject::~*Q1200*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[-Q1200/2.-Has subobject::~*Q1200*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "A#0##",
+ "B#0##",
+ "C#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1201.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1201.json
new file mode 100644
index 00000000..8f3803a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1201.json
@@ -0,0 +1,114 @@
+{
+ "description": "Test `_wpg/`_txt` with `not:`/`!~` queries (ES only)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1201/1",
+ "contents": "[[Category:Q1201]] [[Has text::Some text with foo]]"
+ },
+ {
+ "page": "Q1201/2",
+ "contents": "[[Category:Q1201]] [[Has text::Some text with foobar]]"
+ },
+ {
+ "page": "Q1201/3",
+ "contents": "[[Category:Q1201]] [[Has text::Some text with bar]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (not:foo)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1201]] [[not:foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1201/2#0##",
+ "Q1201/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (not:foo, not:bar)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1201]] [[not:foo]] [[not:bar]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1201/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (not:foo*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1201]] [[not:foo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1201/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#0 (not:FOO)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1201]] [[not:FOO]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1201/2#0##",
+ "Q1201/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1202.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1202.json
new file mode 100644
index 00000000..3e0d34f7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1202.json
@@ -0,0 +1,186 @@
+{
+ "description": "Test `_wpg/`_txt` with `not:`/`!~` queries (ES only, `raw.text`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1202/1",
+ "contents": "[[Category:Q1202]] [[Has text::Some text with foo]]"
+ },
+ {
+ "page": "Q1202/2",
+ "contents": "[[Category:Q1202]] [[Has text::Some text with foobar]]"
+ },
+ {
+ "page": "Q1202/3",
+ "contents": "[[Category:Q1202]] [[Has text::Some text with bar]]"
+ },
+ {
+ "page": "Q1202/4",
+ "contents": "[[Category:Q1202]] free text without a foo property or value"
+ },
+ {
+ "page": "Q1202/5",
+ "contents": "[[Category:Q1202]] free text without a bar property or value"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (not:foo)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[not:foo]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1202/2#0##",
+ "Q1202/3#0##",
+ "Q1202/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (not:foo, not:bar)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[not:foo]] [[not:bar]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1202/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (not:foo*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[not:foo*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1202/3#0##",
+ "Q1202/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (not:FOO)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[not:FOO]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1202/2#0##",
+ "Q1202/3#0##",
+ "Q1202/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (not:FOO*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[not:FOO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1202/3#0##",
+ "Q1202/5#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (Has text::not:FOO)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[Has text::not:FOO]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1202/2#0##",
+ "Q1202/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 (Has text::not:FOO*)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1202]] [[Has text::not:FOO*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1202/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgElasticsearchConfig": {
+ "indexer": {
+ "raw.text": true
+ }
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1203.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1203.json
new file mode 100644
index 00000000..323f8eaf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1203.json
@@ -0,0 +1,271 @@
+{
+ "description": "Test `_wpg/`_txt` with `in:/phrase:` queries (ES only)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1203/1",
+ "contents": "[[Category:Q1203]] [[Has text::fox]] [[Has page::fox]]"
+ },
+ {
+ "page": "Q1203/2",
+ "contents": "[[Category:Q1203]] [[Has text::fox and chicken both jump over ...]] [[Has page::fox and chicken both jump over ...]]"
+ },
+ {
+ "page": "Q1203/3",
+ "contents": "[[Category:Q1203]] [[Has text::fox jumps over ...]] [[Has page::fox jumps over ...]]"
+ },
+ {
+ "page": "Q1203/4",
+ "contents": "[[Category:Q1203]] [[Has text::fox-hunting is a sport ...]] [[Has page::fox-hunting is a sport ...]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0 (in:fox jump, match fox AND jump, see `minimum_should_match`)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[in:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1203/2#0##",
+ "Q1203/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (Has text::in:fox jump, match fox AND jump)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has text::in:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1203/2#0##",
+ "Q1203/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (Has page::in:fox jump, match fox AND jump)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has page::in:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1203/2#0##",
+ "Q1203/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (in:fox)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1203/1#0##",
+ "Q1203/2#0##",
+ "Q1203/3#0##",
+ "Q1203/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (Has text::in:fox)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has text::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1203/1#0##",
+ "Q1203/2#0##",
+ "Q1203/3#0##",
+ "Q1203/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (Has page::in:fox)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1203/1#0##",
+ "Q1203/2#0##",
+ "Q1203/3#0##",
+ "Q1203/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 (phrase:fox jump, mind jump vs. jumps)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[phrase:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count":0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#7 (Has text::phrase:fox jump, mind jump vs. jumps)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has text::phrase:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count":0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#8 (Has page::phrase:fox jump, mind jump vs. jumps)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has page::phrase:fox jump]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count":0,
+ "results": []
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9 (phrase:fox jump*, `phrase_prefix`)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[phrase:fox jump*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1203/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#9 (Has text::phrase:fox jump*, `phrase_prefix`)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has text::phrase:fox jump*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1203/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#10 (Has page::phrase:fox jump*, `phrase_prefix`)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1203]] [[Has page::phrase:fox jump*]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1203/3#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgElasticsearchConfig": {
+ "indexer": {
+ "raw.text": false
+ }
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1204.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1204.json
new file mode 100644
index 00000000..cd6f6bad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1204.json
@@ -0,0 +1,204 @@
+{
+ "description": "Test `!` category queries (ES only, `smwgQSubcategoryDepth`)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q1204/1",
+ "contents": "[[Category:Q1204/0]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q1204/2",
+ "contents": "[[Category:Q1204/0]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q1204/1/1",
+ "contents": "[[Category:Q1204/1]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Q1204/2/1",
+ "contents": "[[Category:Q1204/2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1204/0",
+ "contents": "[[Category:Q1204/0]]"
+ },
+ {
+ "page": "Q1204/1",
+ "contents": "[[Category:Q1204/1]] [[Has page::fox]]"
+ },
+ {
+ "page": "Q1204/2",
+ "contents": "[[Category:Q1204/2]] [[Has page::fox and chicken both jump over ...]]"
+ },
+ {
+ "page": "Q1204/3",
+ "contents": "[[Category:Q1204/2]] [[Category:Q1204/1]] [[Has page::fox jumps over ...]]"
+ },
+ {
+ "page": "Q1204/4",
+ "contents": "[[Category:Q1204/2/1]] [[Category:Q1204/1/1]] [[Has page::fox-hunting is a sport ...]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:Q1204/1]] || [[Category:Q1204/2]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 4,
+ "results": [
+ "Q1204/1#0##",
+ "Q1204/2#0##",
+ "Q1204/3#0##",
+ "Q1204/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (!Q1204/1)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:!Q1204/1]] [[Category:Q1204/2]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1204/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (!Q1204/1|+depth=0)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:!Q1204/1|+depth=0] [[Category:Q1204/2]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1204/2#0##",
+ "Q1204/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (!Q1204/2)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:Q1204/1]] [[Category:!Q1204/2]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1204/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4 (!Q1204/2|+depth=0)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:Q1204/1]] [[Category:!Q1204/2|+depth=0]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1204/1#0##",
+ "Q1204/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#5 (Q1204/1 || !Q1204/2)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "<q>[[Category:Q1204/1]] || [[Category:!Q1204/2]] </q> [[Has page::in:fox]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1204/1#0##",
+ "Q1204/3#0##",
+ "Q1204/4#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#6 (!Q1204/2)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Category:Q1204/0]] [[Category:!Q1204/2]] ",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1204/0#0##",
+ "Q1204/1#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgQSubcategoryDepth": 10,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgElasticsearchConfig": {
+ "indexer": {
+ "raw.text": false
+ }
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1205.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1205.json
new file mode 100644
index 00000000..ea54c467
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1205.json
@@ -0,0 +1,102 @@
+{
+ "description": "Test `[[Has subobject::!]]` / `[[Has subobject::!+]]` (ES only)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Q1205/1",
+ "contents": "[[Has page::Q1205]] {{#subobject: |Has page=Q1205/1}} {{#subobject: |Has page=Q1205/2}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has page::in:Q1205]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1205/1#0##",
+ "Q1205/1#0##_05d38f45f2ea3d1167f4c7c0d52f82d2",
+ "Q1205/1#0##_da6807dde88fa7a579fd5beed7b182cd"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (Has subobject::!, match entities with no subobject)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has page::in:Q1205]][[Has subobject::!]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1205/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (Has subobject::+, those entities that poses a subobject)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has page::in:Q1205]][[Has subobject::+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1205/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3 (Has subobject::!+, those entities that don't poses a subobject)",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has page::in:Q1205]][[Has subobject::!+]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1205/1#0##_05d38f45f2ea3d1167f4c7c0d52f82d2",
+ "Q1205/1#0##_da6807dde88fa7a579fd5beed7b182cd"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1206.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1206.json
new file mode 100644
index 00000000..14f04b23
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1206.json
@@ -0,0 +1,142 @@
+{
+ "description": "Test `cjk.best.effort.proximity.match` (ES only)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Q1206/1",
+ "contents": "[[Has page::æ±æ—¥æœ¬]] [[Has text::æ±æ—¥æœ¬]]"
+ },
+ {
+ "page": "Q1206/2",
+ "contents": "[[Has page::西日本]] [[Has text::西日本]]"
+ },
+ {
+ "page": "Q1206/3",
+ "contents": "[[Has text::。。。 暑ã•ãŒç¶šã„ã¦ã„る西日本ã¨æ±æ—¥æœ¬ã§ã¯ã€€ã€‚。。]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "query",
+ "about": "#0",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[Has page::in:西日本]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1206/2#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[in:西日本]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 3,
+ "results": [
+ "Q1206/2#0##",
+ "Q1206/3#0##",
+ "西日本#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[in:日本]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 5,
+ "results": [
+ "Q1206/1#0##",
+ "Q1206/2#0##",
+ "Q1206/3#0##",
+ "西日本#0##",
+ "æ±æ—¥æœ¬#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#3",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[in:æ±æ—¥æœ¬]] [[in:西日本]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1206/3#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#4",
+ "skip-on": {
+ "elastic": [ "not", "Only works with ES out of the box." ]
+ },
+ "condition": "[[in:æ±æ—¥æœ¬]] [[not:西日本]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 2,
+ "results": [
+ "Q1206/1#0##",
+ "æ±æ—¥æœ¬#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgElasticsearchConfig": {
+ "query": {
+ "cjk.best.effort.proximity.match": true
+ }
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1300.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1300.json
new file mode 100644
index 00000000..c48381dc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/q-1300.json
@@ -0,0 +1,80 @@
+{
+ "description": "Test `_geo` (requires Maps)",
+ "requires": {
+ "Maps": ">= 5.0"
+ },
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has coordinates",
+ "contents": "[[Has type::Geographic coordinate]]"
+ },
+ {
+ "page": "Q1300/1",
+ "contents": "[[Has coordinates::52°31'N, 13°24'E]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 ...",
+ "namespace": "SMW_NS_PROPERTY",
+ "subject": "Has coordinates",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_TYPE",
+ "_SKEY",
+ "_MDAT"
+ ],
+ "propertyValues": [
+ "http://semantic-mediawiki.org/swivt/1.0#_geo"
+ ]
+ }
+ }
+ },
+ {
+ "type": "query",
+ "about": "#1 (eq match)",
+ "condition": "[[Has coordinates::52°31'N, 13°24'E]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1300/1#0##"
+ ]
+ }
+ },
+ {
+ "type": "query",
+ "about": "#2 (distance match)",
+ "condition": "[[Has coordinates::52°31'N, 13°24'E (100km)]]",
+ "printouts": [],
+ "parameters": {
+ "limit": "10"
+ },
+ "assert-queryresult": {
+ "count": 1,
+ "results": [
+ "Q1300/1#0##"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0001.json
new file mode 100644
index 00000000..fc16ab4d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0001.json
@@ -0,0 +1,205 @@
+{
+ "description": "Test RDF output for `_txt`/`_wpg`/`_dat` (#881)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text property for rdf",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property for rdf",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date property for rdf",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Rdf-1",
+ "contents": "[[Has page property for rdf::Test one]] [[Has text property for rdf::Test one]]"
+ },
+ {
+ "page": "Rdf-2",
+ "contents": "[[Has page property for rdf::Test two]] [[Has text property for rdf::Test two]]"
+ },
+ {
+ "page": "Rdf-3",
+ "contents": "{{#subobject:|Has page property for rdf=I--11--O|@sortkey=X99Y}} {{#subobject:Caractères spéciaux|Has text property for rdf={({[[&,,;-]]})} }}"
+ },
+ {
+ "page": "Rdf-4",
+ "contents": "[[Has date property for rdf::31/12/2014]] {{#subobject:|Has date property for rdf=1/1/1970}}"
+ },
+ {
+ "page": "Rdf-5",
+ "contents": "Not permitted to use [[Has subobject::foo]] {{DEFAULTSORT:AA-BB-CC}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 Single subject output",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-1"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Has_page_property_for_rdf rdf:resource=\"&wiki;Test_one\"/>",
+ "<property:Has_text_property_for_rdf rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Test one</property:Has_text_property_for_rdf>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Rdf-1</swivt:wikiPageSortKey>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHas_page_property_for_rdf\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_text_property_for_rdf\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-1",
+ "Rdf-2"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D1\">",
+ "<rdfs:label>Rdf-1</rdfs:label>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D2\">",
+ "<rdfs:label>Rdf-2</rdfs:label>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2 Output for anonymous and named subobject",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-3"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D3\">",
+ "<property:Has_subobject-23aux rdf:resource=\"&wiki;Rdf-2D3-23_c14ce3099ebd07ab500052b8c661832f\"/>",
+ "<property:Has_subobject-23aux rdf:resource=\"&wiki;Rdf-2D3-23Caract-C3-A8res_sp-C3-A9ciaux\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D3-23_c14ce3099ebd07ab500052b8c661832f\">",
+ "<property:Has_page_property_for_rdf rdf:resource=\"&wiki;I-2D-2D11-2D-2DO\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D3-23Caract-C3-A8res_sp-C3-A9ciaux\">",
+ "<property:Has_text_property_for_rdf rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">{({[[&amp;,,;-]]})}</property:Has_text_property_for_rdf>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHas_subobject-23aux\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#3 Date output",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-4"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D4\">",
+ "<property:Has_date_property_for_rdf rdf:datatype=\"http://www.w3.org/2001/XMLSchema#date\">2014-12-31Z</property:Has_date_property_for_rdf>",
+ "<property:Has_date_property_for_rdf-23aux rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">2457022.5</property:Has_date_property_for_rdf-23aux>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D4-23_2c6937c91895c958f837a25691dd53c0\">",
+ "<property:Has_date_property_for_rdf rdf:datatype=\"http://www.w3.org/2001/XMLSchema#date\">1970-01-01Z</property:Has_date_property_for_rdf>",
+ "<property:Has_date_property_for_rdf-23aux rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">2440587.5</property:Has_date_property_for_rdf-23aux>",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_date_property_for_rdf\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_date_property_for_rdf-23aux\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#4 Property output, see #795",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Has text property for rdf",
+ "Property:Has page property for rdf",
+ "Property:Has date property for rdf"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_text_property_for_rdf\">",
+ "<rdfs:label>Has text property for rdf</rdfs:label>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_txt\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHas_page_property_for_rdf\">",
+ "<rdfs:label>Has page property for rdf</rdfs:label>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_wpg\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_date_property_for_rdf\">",
+ "<rdfs:label>Has date property for rdf</rdfs:label>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_dat\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#type\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#5 Check for proper output despite the use of [[Has subobject:: ... ]] as annotation",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-5"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D5\">",
+ "<rdfs:label>Rdf-5</rdfs:label>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">AA-BB-CC</swivt:wikiPageSortKey>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCAuxiliaryUse": true,
+ "smwgNamespace": "http://example.org/id/",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_HELP": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0002.json
new file mode 100644
index 00000000..d2b8221a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0002.json
@@ -0,0 +1,86 @@
+{
+ "description": "Test RDF output for redirected pages (#882)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text property for rdf",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page property for rdf",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date property for rdf",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Rdf-1-redirect",
+ "contents": "[[Has page property for rdf::Test one]] [[Has text property for rdf::Test one]] [[Has date property for rdf::1 Jan 1970]]"
+ },
+ {
+ "page": "Rdf-2-redirect",
+ "contents": "#REDIRECT [[Rdf-1-redirect]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 without backlinks",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-1-redirect",
+ "Rdf-2-redirect"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D1-2Dredirect\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D2-2Dredirect\">",
+ "<swivt:redirectsTo rdf:resource=\"&wiki;Rdf-2D1-2Dredirect\"/>\n",
+ "<owl:sameAs rdf:resource=\"&wiki;Rdf-2D1-2Dredirect\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#redirectsTo\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 with enabled backlinks",
+ "exportcontroller": {
+ "print-pages": [
+ "Rdf-1-redirect",
+ "Rdf-2-redirect"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "2",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D1-2Dredirect\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Rdf-2D2-2Dredirect\">",
+ "<swivt:redirectsTo rdf:resource=\"&wiki;Rdf-2D1-2Dredirect\"/>\n",
+ "<owl:sameAs rdf:resource=\"&wiki;Rdf-2D1-2Dredirect\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#redirectsTo\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0003.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0003.json
new file mode 100644
index 00000000..903ceeb1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0003.json
@@ -0,0 +1,208 @@
+{
+ "description": "Test RDF output for imported foaf vocabulary (#884, en)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import foaf",
+ "contents": "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n homepage|Type:URL\n mbox|Type:Email\n mbox_sha1sum|Type:Text\n depiction|Type:URL\n phone|Type:Text\n Person|Category\n Organization|Category\n knows|Type:Page\n member|Type:Page\n"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Organization",
+ "contents": "[[Imported from::foaf:Organization]] [[Category:Social entity]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:homepage",
+ "contents": "[[Imported from::foaf:homepage]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:mbox",
+ "contents": "[[Has type::Text]] [[Imported from::foaf:mbox]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:name",
+ "contents": "[[Has type::Text]] [[Imported from::foaf:name]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Foaf:knows",
+ "contents": "[[Has type::Page]] [[Imported from::foaf:knows]]"
+ },
+ {
+ "page": "John Doe",
+ "contents": "[[Foaf:name::John Doe]], [[Foaf:homepage::http://example.org/JohnDoe]] [[Foaf:knows::Jane Doe]]"
+ },
+ {
+ "page": "Jane Doe",
+ "contents": "[[Foaf:name::Jane Doe]], [[Foaf:homepage::http://example.org/JaneDoe]] [[Foaf:knows::John Doe]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "John Doe"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/John_Doe\">",
+ "xmlns:foaf=\"http://xmlns.com/foaf/0.1/",
+ "<foaf:homepage rdf:resource=\"http://example.org/JohnDoe\"/>",
+ "<foaf:knows rdf:resource=\"&wiki;Jane_Doe\"/>",
+ "<foaf:name rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">John Doe</foaf:name>",
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/homepage\" />",
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/knows\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://xmlns.com/foaf/0.1/name\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 export with backlinks",
+ "exportcontroller": {
+ "print-pages": [
+ "John Doe",
+ "Jane Doe"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "2",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/John_Doe\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Jane_Doe\">",
+ "<foaf:homepage rdf:resource=\"http://example.org/JaneDoe\"/>",
+ "<foaf:knows rdf:resource=\"&wiki;John_Doe\"/>",
+ "<foaf:name rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Jane Doe</foaf:name>",
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/knows\">",
+ "<rdfs:label>Foaf:knows</rdfs:label>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">foaf:knows|http://xmlns.com/foaf/0.1/</swivt:specialImportedFrom>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_wpg\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/homepage\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://xmlns.com/foaf/0.1/name\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#specialImportedFrom\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Foaf:knows"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/knows\">",
+ "<rdfs:label>Foaf:knows</rdfs:label>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">foaf:knows|http://xmlns.com/foaf/0.1/</swivt:specialImportedFrom>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_wpg\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#type\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#3 type definition fetched from import reference",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Foaf:homepage"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/homepage\">",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">foaf:homepage|http://xmlns.com/foaf/0.1/</swivt:specialImportedFrom>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_uri\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#specialImportedFrom\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#4 user declared type definition being replaced by import type reference",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Foaf:mbox"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:ObjectProperty rdf:about=\"http://xmlns.com/foaf/0.1/mbox\">",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">foaf:mbox|http://xmlns.com/foaf/0.1/</swivt:specialImportedFrom>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_ema\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#specialImportedFrom\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#5 owl:Class / Category encoding, see #1081",
+ "exportcontroller": {
+ "print-pages": [
+ "Category:Organization"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:Class rdf:about=\"http://xmlns.com/foaf/0.1/Organization\">",
+ "<rdfs:label>Organization</rdfs:label>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">foaf:Organization|http://xmlns.com/foaf/0.1/</swivt:specialImportedFrom>",
+ "<rdfs:subClassOf rdf:resource=\"http://example.org/id/Category-3ASocial_entity\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#specialImportedFrom\" />",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ASocial_entity\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespace": "http://example.org/id/",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_CATEGORY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0004.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0004.json
new file mode 100644
index 00000000..4788e278
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0004.json
@@ -0,0 +1,92 @@
+{
+ "description": "Test RDF output generation for `_INST`/`_SUBC` pages (#922, en)",
+ "setup": [
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Rdf-1-category",
+ "contents": "empty"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Rdf-2-subcategory",
+ "contents": "[[Category:Rdf-1-category]]"
+ },
+ {
+ "namespace": "NS_CATEGORY",
+ "page": "Rdf-3-subsubcategory",
+ "contents": "[[Category:Rdf-2-subcategory]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 cat-subcat export without backlinks",
+ "exportcontroller": {
+ "print-pages": [
+ "Category:Rdf-1-category",
+ "Category:Rdf-2-subcategory"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ARdf-2D1-2Dcategory\">",
+ "<rdfs:label>Rdf-1-category</rdfs:label>",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ARdf-2D2-2Dsubcategory\">",
+ "<rdfs:label>Rdf-2-subcategory</rdfs:label>",
+ "<rdfs:subClassOf rdf:resource=\"http://example.org/id/Category-3ARdf-2D1-2Dcategory\"/>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 cat-subcat export with backlinks (catches subsub-cat)",
+ "exportcontroller": {
+ "print-pages": [
+ "Category:Rdf-1-category",
+ "Category:Rdf-2-subcategory"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<!ENTITY category 'http://example.org/id/Category-3A'>",
+ "<!ENTITY property 'http://example.org/id/Property-3A'>",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ARdf-2D1-2Dcategory\">",
+ "<rdfs:label>Rdf-1-category</rdfs:label>",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ARdf-2D2-2Dsubcategory\">",
+ "<rdfs:label>Rdf-2-subcategory</rdfs:label>",
+ "<rdfs:subClassOf rdf:resource=\"http://example.org/id/Category-3ARdf-2D1-2Dcategory\"/>",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ARdf-2D3-2Dsubsubcategory\">",
+ "<rdfs:label>Rdf-3-subsubcategory</rdfs:label>",
+ "<rdfs:subClassOf rdf:resource=\"http://example.org/id/Category-3ARdf-2D2-2Dsubcategory\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3ASubcategory_of\">",
+ "<rdfs:label>Subcategory of</rdfs:label>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0005.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0005.json
new file mode 100644
index 00000000..1e0daae7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0005.json
@@ -0,0 +1,47 @@
+{
+ "description": "Test RDF wiki-info output (#928, en)",
+ "setup": [],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 simple output",
+ "exportcontroller": {
+ "wiki-info": true,
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Wikisite rdf:about=\"&wiki;#wiki\">",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#siteName\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#pagePrefix\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#smwVersion\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#langCode\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#pageCount\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#contentPageCount\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#mediaCount\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#editCount\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#userCount\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://semantic-mediawiki.org/swivt/1.0#adminCount\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0006.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0006.json
new file mode 100644
index 00000000..af2ebd76
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0006.json
@@ -0,0 +1,127 @@
+{
+ "description": "Test RDF output generation for pages that contain `_rec` annotations (#1285, #1275)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "HasTextNumberRecord",
+ "contents": "[[Has type::Record]] [[Has fields::Has text;Has number]]"
+ },
+ {
+ "page": "Example/R0006/1",
+ "contents": "[[Category:R0006]] [[HasTextNumberRecord::Foo;123]]"
+ },
+ {
+ "page": "Example/R0006/2",
+ "contents": "[[Category:R0006]] {{#subobject:|HasTextNumberRecord=Foo;123}}"
+ },
+ {
+ "page": "Example/R0006/3",
+ "contents": "[[Category:R0006]] [[HasTextNumberRecord::Foo;123]] {{#subobject:A1|Has number=123|HasTextNumberRecord=Foo;123}} {{#subobject:A2|Has number=456|HasTextNumberRecord=Foo;123}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 simple record annotation",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0006/1"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:HasTextNumberRecord rdf:resource=\"&wiki;Example/R0006/1-23_e594fde0f37d237549009d2ecebce80e\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0006/1-23_e594fde0f37d237549009d2ecebce80e\">",
+ "<property:Has_number rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">123</property:Has_number>",
+ "<property:Has_text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Foo</property:Has_text>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHasTextNumberRecord\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 subobject record annotation",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0006/2"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0006/2\">",
+ "<rdf:type rdf:resource=\"http://example.org/id/Category-3AR0006\"/>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0006/2-23_64dbbe1badeb217858f54cedb983c0ae\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0006/2-23_64dbbe1badeb217858f54cedb983c0ae\">",
+ "<property:HasTextNumberRecord rdf:resource=\"&wiki;Example/R0006/2-23_e594fde0f37d237549009d2ecebce80e\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0006/2-23_e594fde0f37d237549009d2ecebce80e\">",
+ "<property:Has_number rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">123</property:Has_number>",
+ "<property:Has_text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Foo</property:Has_text>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHas_subobject\" />",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHasTextNumberRecord\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2 multiple subobject record annotation, test statement uniqueness (statement order is important)",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0006/3"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "store": {
+ "clear-cache": true
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:HasTextNumberRecord rdf:resource=\"&wiki;Example/R0006/3-23_e594fde0f37d237549009d2ecebce80e\"/>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0006/3-23A1\"/>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0006/3-23A2\"/>",
+ "<property:Has_number rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">123</property:Has_number>\n\t\t<property:Has_text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Foo</property:Has_text>\n\t\t<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Example/R0006/3#Foo;123</swivt:wikiPageSortKey>",
+ "<property:HasTextNumberRecord rdf:resource=\"&wiki;Example/R0006/3-23_e594fde0f37d237549009d2ecebce80e\"/>\n\t\t<property:Has_number rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">123</property:Has_number>",
+ "<property:HasTextNumberRecord rdf:resource=\"&wiki;Example/R0006/3-23_e594fde0f37d237549009d2ecebce80e\"/>\n\t\t<property:Has_number rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">456</property:Has_number>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0007.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0007.json
new file mode 100644
index 00000000..9b56bc90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0007.json
@@ -0,0 +1,125 @@
+{
+ "description": "Test RDF output for imported dc/gna vocabulary, owl:AnnotationProperty, owl:DatatypeProperty, owl:ObjectProperty, Equivalent URI (#795, `wgRestrictDisplayTitle`, en)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import dc",
+ "contents": "http://purl.org/dc/elements/1.1/|[http://dublincore.org/documents/dces/ Dublin Core Metadata Element Set, Version 1.1] \n contributor|Type:Text \n coverage|Type:Page \n creator|Type:Text \n date|Type:Text \n description|Type:Text \n format|Type:Page \n identifier|Type:Annotation URI \n language|Type:Text \n publisher|Type:Text \n relation|Type:Page \n rights|Type:Text \n source|Type:Text \n subject|Type:Text \n title|Type:Text \n type|Type:Text \n"
+ },
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import gna",
+ "contents": "http://rs.gbif.org/terms/1.0/|[http://rs.gbif.org/extension/gbif/1.0/ GBIF Global Names Architecture (GNA)] \n Identifier|Type:Page \n isHybrid|Type:Boolean"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Dc:identifier",
+ "contents": "[[Imported from::dc:identifier]] [[Equivalent URI::http://purl.org/dc/elements/1.1/identifier]] {{DISPLAYTITLE:dc:identifier}} [[Category:Dublin Core]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Gna:isHybrid",
+ "contents": "[[Imported from::gna:isHybrid]] [[dc:identifier::http://rs.gbif.org/terms/1.0/isHybrid]] {{DISPLAYTITLE:gna:isHybrid}} [[Category:GNA]] [[Category:GBIF]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Gna:Identifier",
+ "contents": "[[Imported from::gna:Identifier]] {{DISPLAYTITLE:gna:Identifier}} [[Category:GNA]] [[Category:GBIF]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 (is owl:AnnotationProperty)",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Dc:identifier"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:AnnotationProperty rdf:about=\"http://purl.org/dc/elements/1.1/identifier\">",
+ "<owl:equivalentProperty rdf:resource=\"http://purl.org/dc/elements/1.1/identifier\"/>",
+ "<property:Display_title_of rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">dc:identifier</property:Display_title_of>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">dc:identifier|http://purl.org/dc/elements/1.1/</swivt:specialImportedFrom>",
+ "<rdf:type rdf:resource=\"http://example.org/id/Category-3ADublin_Core\"/>",
+ "<owl:Class rdf:about=\"http://example.org/id/Category-3ADublin_Core\" />",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_anu\"/>",
+ "<rdfs:label>dc:identifier</rdfs:label>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">dc:identifier</swivt:wikiPageSortKey>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 (is owl:DatatypeProperty, typed boolean)",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Gna:isHybrid"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:DatatypeProperty rdf:about=\"http://rs.gbif.org/terms/1.0/isHybrid\">",
+ "<rdfs:label>gna:isHybrid</rdfs:label>",
+ "<dc:identifier rdf:resource=\"http://rs.gbif.org/terms/1.0/isHybrid\"/>",
+ "<property:Display_title_of rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:isHybrid</property:Display_title_of>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:isHybrid|http://rs.gbif.org/terms/1.0/</swivt:specialImportedFrom>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:isHybrid</swivt:wikiPageSortKey>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_boo\"/>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2 (is owl:ObjectProperty, typed page)",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Gna:Identifier"
+ ],
+ "parameters": {
+ "backlinks": false,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:ObjectProperty rdf:about=\"http://rs.gbif.org/terms/1.0/Identifier\">",
+ "<rdfs:label>gna:Identifier</rdfs:label>",
+ "<property:Display_title_of rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:Identifier</property:Display_title_of>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:Identifier|http://rs.gbif.org/terms/1.0/</swivt:specialImportedFrom>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">gna:Identifier</swivt:wikiPageSortKey>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_wpg\"/>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgAllowDisplayTitle": true,
+ "wgRestrictDisplayTitle": false,
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespace": "http://example.org/id/",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "NS_CATEGORY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0008.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0008.json
new file mode 100644
index 00000000..2d32b97d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0008.json
@@ -0,0 +1,86 @@
+{
+ "description": "Test RDF output generation on pages that contain incoming error annotations (`wgContLang=en`, `wgLang=es`, syntax=rdf/turtle)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has area without unit",
+ "contents": "[[Has type::Quantity]]"
+ },
+ {
+ "page": "Example/R0008/1",
+ "contents": "[[Has area without unit::1 m]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 incoming (error) is exported in canonical form (especially when user language is different)",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Has area without unit"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_area_without_unit\">",
+ "<rdfs:label>Has area without unit</rdfs:label>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Has area without unit</swivt:wikiPageSortKey>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_qty\"/>"
+ ],
+ "not-contain": [
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3ATiene_valor_incorrecto_para\">",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Tiene valor incorrecto para</swivt:wikiPageSortKey>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 (turtle)",
+ "exportcontroller": {
+ "syntax": "turtle",
+ "print-pages": [
+ "Property:Has area without unit"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "property:Has_area_without_unit",
+ "rdf:type owl:DatatypeProperty ;",
+ "rdfs:label \"Has area without unit\" ;",
+ "swivt:wikiNamespace \"102\"^^xsd:integer ;",
+ "swivt:wikiPageSortKey \"Has area without unit\" ;",
+ "swivt:type <http://semantic-mediawiki.org/swivt/1.0#_qty> .",
+ "<http://example.org/id/Example/R0008/1-23_ERR3b4d92de525df0eeb65e83ea06e2a5d4>",
+ "swivt:masterPage <http://example.org/id/Example/R0008/1> ;",
+ "property:Has_improper_value_for property:Has_area_without_unit ;"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0009.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0009.json
new file mode 100644
index 00000000..263f6d69
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0009.json
@@ -0,0 +1,85 @@
+{
+ "description": "Test RDF output generation that contain a monolingual text annotations `_PDESC` (`wgContLang=en`, `wgLang=es`, syntax=rdf/turtle)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]] [[Has property description::Is a number@en]] [[Has property description::æ•°@ja]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Has number"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AHas_number\">",
+ "<skos:scopeNote xml:lang=\"en\">Is a number</skos:scopeNote>",
+ "<skos:scopeNote xml:lang=\"ja\">æ•°</skos:scopeNote>",
+ "<property:Property_description rdf:resource=\"http://example.org/id/Property-3AHas_number-23_ML13b181afba7d1e489a656a75fa7917b2\"/>",
+ "<property:Property_description rdf:resource=\"http://example.org/id/Property-3AHas_number-23_ML4c33eabf5c7dfbb1292c817504951018\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Property-3AHas_number-23_ML13b181afba7d1e489a656a75fa7917b2\">",
+ "<property:Text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Is a number</property:Text>",
+ "<property:Language_code rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">en</property:Language_code>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Property-3AHas_number-23_ML4c33eabf5c7dfbb1292c817504951018\">",
+ "<property:Text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">æ•°</property:Text>",
+ "<property:Language_code rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">ja</property:Language_code>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "syntax": "turtle",
+ "print-pages": [
+ "Property:Has number"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "rdfs:label \"Has number\" ;",
+ "skos:scopeNote \"Is a number\"@en , \"æ•°\"@ja ;",
+ "property:Property_description property:Has_number-23_ML13b181afba7d1e489a656a75fa7917b2 , property:Has_number-23_ML4c33eabf5c7dfbb1292c817504951018 ;",
+ "swivt:wikiPageSortKey \"Has number\" ;",
+ "swivt:type <http://semantic-mediawiki.org/swivt/1.0#_num> .",
+ "property:Language_code \"en\" ;",
+ "property:Text \"Is a number\" .",
+ "property:Language_code \"ja\" ;",
+ "property:Text \"æ•°\" ."
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0010.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0010.json
new file mode 100644
index 00000000..ae624221
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0010.json
@@ -0,0 +1,104 @@
+{
+ "description": "Test RDF output on canonical entities (`wgContLang=fr`, `wgLang=es`, syntax=rdf/turtle)",
+ "setup": [
+ {
+ "page": "Example/R0010/1",
+ "contents": "[[Boolean::true]]"
+ },
+ {
+ "page": "Example/R0010/2",
+ "contents": "[[Date::2034]]"
+ },
+ {
+ "page": "Example/R0010/Q1",
+ "contents": "{{#ask: [[Booléen::+]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0010/1"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Special:ExportRDF/Example/R0010/1",
+ "<property:Boolean rdf:datatype=\"http://www.w3.org/2001/XMLSchema#boolean\">true</property:Boolean>"
+ ],
+ "not-contain": [
+ "Sp%C3%A9cial:ExportRDF/Example/R0010/1",
+ "<property:Booleano rdf:datatype=\"http://www.w3.org/2001/XMLSchema#boolean\">true</property:Booleano>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0010/2"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Special:ExportRDF/Example/R0010/2",
+ "<property:Date rdf:datatype=\"http://www.w3.org/2001/XMLSchema#gYear\">2034</property:Date>",
+ "<property:Date-23aux rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">2463963.5</property:Date-23aux>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0010/Q1"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:wikiPageContentLanguage rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">fr</swivt:wikiPageContentLanguage>",
+ "<property:Has_query rdf:resource=\"&wiki;Example/R0010/Q1-23_QUERY5223a22ba7ce6ccf63e5701b6d6093b6\"/>"
+ ],
+ "not-contain": [
+ "Sp%C3%A9cial:ExportRDF/Example/R0010/2",
+ "<property:Booleano rdf:datatype=\"http://www.w3.org/2001/XMLSchema#boolean\">true</property:Booleano>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_CATEGORY": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgContLang": "fr",
+ "wgLang": "es",
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0011.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0011.json
new file mode 100644
index 00000000..f155b51a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0011.json
@@ -0,0 +1,112 @@
+{
+ "description": "Test RDF output generation `skos` import/`skos:altLabel` as Monolingual text (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "NS_MEDIAWIKI",
+ "page": "Smw import skos",
+ "contents": {
+ "import-from": "/../Fixtures/skos-import.txt"
+ }
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has alternative label",
+ "contents": "[[Imported from::skos:altLabel]]"
+ },
+ {
+ "page": "Example/P0440/Triacylglycerol lipase",
+ "contents": "[[Has alternative label::Lipase@en]], [[Has alternative label::Tributyrase@en]], [[Has alternative label::Triglyceride lipase@en]], [[Has alternative label::トリアシルグリセロールリパーゼ@ja]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/P0440/Triacylglycerol lipase"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Has_alternative_label rdf:resource=\"&wiki;Example/P0440/Triacylglycerol_lipase-23_MLb850763abfc7ff93b2753834b000b7dc\"/>",
+ "<property:Has_alternative_label rdf:resource=\"&wiki;Example/P0440/Triacylglycerol_lipase-23_MLa9c103f4379a94bfab97819dacd3c182\"/>",
+ "<property:Has_alternative_label rdf:resource=\"&wiki;Example/P0440/Triacylglycerol_lipase-23_MLcbdbdd5279ca447e5dc7f78465f45256\"/>",
+ "<property:Has_alternative_label rdf:resource=\"&wiki;Example/P0440/Triacylglycerol_lipase-23_MLca7a57d40b775013e7fcc714ec4da30d\"/>",
+ "<skos:altLabel xml:lang=\"en\">Lipase</skos:altLabel>",
+ "<skos:altLabel xml:lang=\"en\">Tributyrase</skos:altLabel>",
+ "<skos:altLabel xml:lang=\"en\">Triglyceride lipase</skos:altLabel>",
+ "<skos:altLabel xml:lang=\"ja\">トリアシルグリセロールリパーゼ</skos:altLabel>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/P0440/Triacylglycerol_lipase-23_MLb850763abfc7ff93b2753834b000b7dc\">",
+ "<property:Language_code rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">en</property:Language_code>",
+ "<property:Text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Lipase</property:Text>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AHas_alternative_label\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://www.w3.org/2004/02/skos/core#altLabel\" />"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1",
+ "exportcontroller": {
+ "syntax": "turtle",
+ "print-pages": [
+ "Example/P0440/Triacylglycerol lipase"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "property:Has_alternative_label <http://example.org/id/Example/P0440/Triacylglycerol_lipase-23_MLb850763abfc7ff93b2753834b000b7dc> , <http://example.org/id/Example/P0440/Triacylglycerol_lipase-23_MLa9c103f4379a94bfab97819dacd3c182> , <http://example.org/id/Example/P0440/Triacylglycerol_lipase-23_MLcbdbdd5279ca447e5dc7f78465f45256> , <http://example.org/id/Example/P0440/Triacylglycerol_lipase-23_MLca7a57d40b775013e7fcc714ec4da30d> ;",
+ "skos:altLabel \"Lipase\"@en , \"Tributyrase\"@en , \"Triglyceride lipase\"@en , \"トリアシルグリセロールリパーゼ\"@ja ;"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2",
+ "exportcontroller": {
+ "print-pages": [
+ "Property:Has alternative label"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<owl:DatatypeProperty rdf:about=\"http://www.w3.org/2004/02/skos/core#altLabel\">",
+ "<rdfs:label>Has alternative label</rdfs:label>",
+ "<swivt:specialImportedFrom rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">skos:altLabel|http://www.w3.org/2004/02/skos/core#</swivt:specialImportedFrom>",
+ "<swivt:type rdf:resource=\"http://semantic-mediawiki.org/swivt/1.0#_mlt_rec\"/>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0012.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0012.json
new file mode 100644
index 00000000..67ac9bf3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0012.json
@@ -0,0 +1,100 @@
+{
+ "description": "Test RDF output generation on SubSemanticData traversal (#2177, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "DeepExportRecord",
+ "contents": "[[Has type::Record]] [[Has fields::Monolingual text;Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "DeepDeepExportReference",
+ "contents": "[[Has type::Reference]] [[Has fields::Monolingual text;Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "DeepDeepExportRecord",
+ "contents": "[[Has type::Record]] [[Has fields::DeepDeepExportReference]]"
+ },
+ {
+ "page": "Example/R0012/1",
+ "contents": "{{#subobject: |DeepExportRecord=Test@en;12 |DeepExportRecord=Test@en;123 |DeepExportRecord=Test@en;1234 }}"
+ },
+ {
+ "page": "Example/R0012/2",
+ "contents": "{{#subobject: |DeepDeepExportRecord=Test@en\\;1 Jan 1970 |DeepDeepExportRecord=Test@fr\\;1 Jan 1971 }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 does not cause a SubSemanticDataException during the export",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0012/1"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/1-23_ML2c4c5edb170b331e395a01005cb65358\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/1-23_d5f308a6235110d394024f55f16835f7\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/1-23_6779e00256727fef98705300a8a7a73b\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/1-23_f6e5c74170e0046a78d2712a90970310\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/1-23_04ddc57f1473b963c04af38df2e994a4\">",
+ "<property:Language_code rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">en</property:Language_code>",
+ "<property:Text rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Test</property:Text>",
+ "<property:Monolingual_text-23aux xml:lang=\"en\">Test</property:Monolingual_text-23aux>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 stable IDs and `subobject->record->reference->monolingual text` traversal",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0012/2"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_ML2c4c5edb170b331e395a01005cb65358\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_REF1940cba4db3a967bc1d8e0c0a96bbc2e\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_7d9ab26348dedff9e03af7e8bc8d440d\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_ML18e8304d117e9f958205000e055d12f3\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_REFd17d8d8ba79f00d64f5b9c51f92e0867\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_797f86a0718f4fdec776197cf5adea93\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0012/2-23_7f24e1a1c0ee4de54d6344daae7375b5\">",
+ "<property:Date rdf:datatype=\"http://www.w3.org/2001/XMLSchema#date\">1971-01-01Z</property:Date>",
+ "<property:Date rdf:datatype=\"http://www.w3.org/2001/XMLSchema#date\">1970-01-01Z</property:Date>",
+ "<property:Monolingual_text-23aux xml:lang=\"en\">Test</property:Monolingual_text-23aux>",
+ "<property:Monolingual_text-23aux xml:lang=\"fr\">Test</property:Monolingual_text-23aux>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0013.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0013.json
new file mode 100644
index 00000000..d232c5a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0013.json
@@ -0,0 +1,105 @@
+{
+ "description": "Test RDF output generation `_uri`/`_ema`/`_tel` with spaces/underscore (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has telephone number",
+ "contents": "[[Has type::Telephone number]]"
+ },
+ {
+ "page": "Example/R0013/1",
+ "contents": "[[Has url::http://example.org/Foo bar]] [[Has url::http://example.org/Foo%20bar]] [[Has url::http://example.org/Foo_bar]]"
+ },
+ {
+ "page": "Example/R0013/2",
+ "contents": "[[Email::john_doe@example.org]]"
+ },
+ {
+ "page": "Example/R0013/3",
+ "contents": "[[Has telephone number::+1 201 555 5555]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 url with underscore",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0013/1"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Has_url rdf:resource=\"http://example.org/Foo_bar\"/>"
+ ],
+ "not-contain": [
+ "<property:Has_url rdf:resource=\"http://example.org/Foo bar\"/>",
+ "<property:Has_url rdf:resource=\"http://example.org/Foo%20bar\"/>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1 email with underscore",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0013/2"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Email rdf:resource=\"mailto:john_doe@example.org\"/>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2 tle with spaces",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0013/3"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Has_telephone_number rdf:resource=\"tel:+1-201-555-5555\"/>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0014.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0014.json
new file mode 100644
index 00000000..18b9b771
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0014.json
@@ -0,0 +1,76 @@
+{
+ "description": "Test RDF output generation on non-latin URI/IRI export (#2188, `smwgExportResourcesAsIri=false`, `wgContLang=ru`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Заголовок",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Тип публикации",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Журнал",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Год",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/R0014/Заголовок",
+ "contents": "[[Заголовок::Pollen Limitation]] [[Журнал::Arctic, Antarctic and Alpine Research]] [[Год::2009]] [[Тип публикации::СтатьÑ]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0014/Заголовок"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0014/-D0-97-D0-B0-D0-B3-D0-BE-D0-BB-D0-BE-D0-B2-D0-BE-D0-BA\">",
+ "<rdfs:label>Example/R0014/Заголовок</rdfs:label>",
+ "<wiki:Property-3A-D0-93-D0-BE-D0-B4 rdf:datatype=\"http://www.w3.org/2001/XMLSchema#gYear\">2009</wiki:Property-3A-D0-93-D0-BE-D0-B4>",
+ "<wiki:Property-3A-D0-96-D1-83-D1-80-D0-BD-D0-B0-D0-BB rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Arctic, Antarctic and Alpine Research</wiki:Property-3A-D0-96-D1-83-D1-80-D0-BD-D0-B0-D0-BB>",
+ "<wiki:Property-3A-D0-97-D0-B0-D0-B3-D0-BE-D0-BB-D0-BE-D0-B2-D0-BE-D0-BA rdf:resource=\"&wiki;Pollen_Limitation\"/>",
+ "<wiki:Property-3A-D0-A2-D0-B8-D0-BF_-D0-BF-D1-83-D0-B1-D0-BB-D0-B8-D0-BA-D0-B0-D1-86-D0-B8-D0-B8 rdf:resource=\"&wiki;-D0-A1-D1-82-D0-B0-D1-82-D1-8C-D1-8F\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3A-D0-93-D0-BE-D0-B4\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3A-D0-93-D0-BE-D0-B4-23aux\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3A-D0-96-D1-83-D1-80-D0-BD-D0-B0-D0-BB\" />",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3A-D0-97-D0-B0-D0-B3-D0-BE-D0-BB-D0-BE-D0-B2-D0-BE-D0-BA\" />",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3A-D0-A2-D0-B8-D0-BF_-D0-BF-D1-83-D0-B1-D0-BB-D0-B8-D0-BA-D0-B0-D1-86-D0-B8-D0-B8\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "ru",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgExportResourcesAsIri": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0015.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0015.json
new file mode 100644
index 00000000..2a4fa481
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0015.json
@@ -0,0 +1,77 @@
+{
+ "description": "Test RDF output generation on non-latin URI/IRI export (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=ru`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Заголовок",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Тип публикации",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Журнал",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Год",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/R0015/Заголовок",
+ "contents": "[[Заголовок::Pollen Limitation]] [[Журнал::Arctic, Antarctic and Alpine Research]] [[Год::2009]] [[Тип публикации::СтатьÑ]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0015/Заголовок"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0015/Заголовок\">",
+ "<rdfs:label>Example/R0015/Заголовок</rdfs:label>",
+ "<property:Год rdf:datatype=\"http://www.w3.org/2001/XMLSchema#gYear\">2009</property:Год>",
+ "<property:Год-23aux rdf:datatype=\"http://www.w3.org/2001/XMLSchema#double\">2454832.5</property:Год-23aux>",
+ "<property:Журнал rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Arctic, Antarctic and Alpine Research</property:Журнал>",
+ "<property:Заголовок rdf:resource=\"&wiki;Pollen_Limitation\"/>",
+ "<property:Тип_публикации rdf:resource=\"&wiki;СтатьÑ\"/>",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AГод\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AГод-23aux\" />",
+ "<owl:DatatypeProperty rdf:about=\"http://example.org/id/Property-3AЖурнал\" />",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AЗаголовок\" />",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AТип_публикации\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "ru",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgExportResourcesAsIri": true,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0016.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0016.json
new file mode 100644
index 00000000..53a199e9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0016.json
@@ -0,0 +1,105 @@
+{
+ "description": "Test RDF output generation with special characters (#2188, `smwgExportResourcesAsIri=false`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Example/R0016/1/特殊字符",
+ "contents": "{{#subobject: ^[0-9]*$ |特殊字符=^[0-9]*$ }}"
+ },
+ {
+ "page": "Example/R0016/2/特殊字符",
+ "contents": "{{#subobject: {({[[&,,;-.]]})} |特殊字符={({[[&,,;-]]})} }}"
+ },
+ {
+ "page": "Example/R0016/3/特殊 字符",
+ "contents": "{{#subobject: <> |特殊字符=<>\\'/\" }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0016/1/特殊字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/1/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6\">",
+ "<rdfs:label>Example/R0016/1/特殊字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0016/1/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6-23-5E-5B0-2D9-5D-2A-24\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/1/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<property:Has_improper_value_for rdf:resource=\"&wiki;Property-3A-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6\"/>",
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Example/R0016/1/特殊字符</swivt:wikiPageSortKey>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0016/2/特殊字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/2/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6\">",
+ "<rdfs:label>Example/R0016/2/特殊字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0016/2/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6-23-7B-28-7B-5B-5B-26-2C-2C-3B-2D.-5D-5D-7D-29-7D\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/2/-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<property:Has_improper_value_for rdf:resource=\"&wiki;Property-3A-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6\"/>"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0016/3/特殊 字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/3/-E7-89-B9-E6-AE-8A_-E5-AD-97-E7-AC-A6\">",
+ "<rdfs:label>Example/R0016/3/特殊 字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0016/3/-E7-89-B9-E6-AE-8A_-E5-AD-97-E7-AC-A6-23-3C-3E\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0016/3/-E7-89-B9-E6-AE-8A_-E5-AD-97-E7-AC-A6-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<property:Has_improper_value_for rdf:resource=\"&wiki;Property-3A-E7-89-B9-E6-AE-8A-E5-AD-97-E7-AC-A6\"/>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgExportResourcesAsIri": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0017.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0017.json
new file mode 100644
index 00000000..bb427db3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0017.json
@@ -0,0 +1,104 @@
+{
+ "description": "Test RDF output generation with special characters (#2188, `smwgExportResourcesAsIri=true`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Example/R0017/1/特殊字符",
+ "contents": "{{#subobject: ^[0-9]*$ |特殊字符=^[0-9]*$ }}"
+ },
+ {
+ "page": "Example/R0017/2/特殊字符",
+ "contents": "{{#subobject: {({[[&,,;-.]]})} |特殊字符={({[[&,,;-]]})} }}"
+ },
+ {
+ "page": "Example/R0017/3/特殊 字符",
+ "contents": "{{#subobject: <> |特殊字符=<>\\'/\" }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0017/1/特殊字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/1/特殊字符\">",
+ "<rdfs:label>Example/R0017/1/特殊字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0017/1/特殊字符-23-5E-5B0-2D9-5D-2A-24\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/1/特殊字符-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/1/特殊字符-23-5E-5B0-2D9-5D-2A-24\">"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#1",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0017/2/特殊字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/2/特殊字符\">",
+ "<rdfs:label>Example/R0017/2/特殊字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0017/2/特殊字符-23-7B-28-7B-5B-5B-26-2C-2C-3B-2D.-5D-5D-7D-29-7D\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/2/特殊字符-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/2/特殊字符-23-7B-28-7B-5B-5B-26-2C-2C-3B-2D.-5D-5D-7D-29-7D\">"
+ ]
+ }
+ },
+ {
+ "type": "rdf",
+ "about": "#2",
+ "exportcontroller": {
+ "print-pages": [
+ "Example/R0017/3/特殊 字符"
+ ],
+ "parameters": {
+ "backlinks": true,
+ "recursion": "1",
+ "revisiondate": false
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/3/特殊_字符\">",
+ "<rdfs:label>Example/R0017/3/特殊 字符</rdfs:label>",
+ "<property:Has_subobject rdf:resource=\"&wiki;Example/R0017/3/特殊_字符-23-3C-3E\"/>",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/3/特殊_字符-23_ERRb4cc1ce66e816f93b716adeeddb0fa46\">",
+ "<swivt:Subject rdf:about=\"http://example.org/id/Example/R0017/3/特殊_字符-23-3C-3E\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgExportResourcesAsIri": true,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0018.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0018.json
new file mode 100644
index 00000000..ffefb633
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0018.json
@@ -0,0 +1,46 @@
+{
+ "description": "Test RDF output generation with special characters (`smwgExportResourcesAsIri=true`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/R0018/1",
+ "contents": "[[Has url::https://example.org/public/item#Foo\"Bar\"]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0 (double quotes)",
+ "dumpRDF": {
+ "parameters": {
+ "page": "Example/R0018/1"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<property:Has_url rdf:resource=\"https://example.org/public/item#Foo%22Bar%22\"/>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgExportBCNonCanonicalFormUse": false,
+ "smwgExportResourcesAsIri": true,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0019.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0019.json
new file mode 100644
index 00000000..3e197244
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0019.json
@@ -0,0 +1,52 @@
+{
+ "description": "Test RDF output on `swivt:sort` with enabled collation (#2065, `smwgEntityCollation=uppercase`, `smwgSparqlQFeatures=SMW_SPARQL_QF_COLLATION`, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/R0019/1",
+ "contents": "[[Has url::https://example.org/Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "dumpRDF": {
+ "parameters": {
+ "page": "Example/R0019/1"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<swivt:wikiPageSortKey rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">Example/R0019/1</swivt:wikiPageSortKey>",
+ "<swivt:sort rdf:datatype=\"http://www.w3.org/2001/XMLSchema#string\">EXAMPLE/R0019/1</swivt:sort>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "smwgEntityCollation": "uppercase",
+ "smwgSparqlQFeatures": [
+ "SMW_SPARQL_QF_REDI",
+ "SMW_SPARQL_QF_SUBP",
+ "SMW_SPARQL_QF_SUBC",
+ "SMW_SPARQL_QF_COLLATION"
+ ],
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0020.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0020.json
new file mode 100644
index 00000000..07600613
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/r-0020.json
@@ -0,0 +1,45 @@
+{
+ "description": "Test RDF output on `/` in porperty name (#3134)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "R0020/URL",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/R0020/1",
+ "contents": "[[R0020/URL::https://example.org/Foo]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "rdf",
+ "about": "#0",
+ "dumpRDF": {
+ "parameters": {
+ "page": "Example/R0020/1"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<wiki:Property-3AR0020-2FURL rdf:resource=\"https://example.org/Foo\"/>",
+ "<owl:ObjectProperty rdf:about=\"http://example.org/id/Property-3AR0020-2FURL\" />"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "smwgNamespace": "http://example.org/id/"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0001.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0001.json
new file mode 100644
index 00000000..9f6cc902
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0001.json
@@ -0,0 +1,68 @@
+{
+ "description": "Test output of `Special:Properties` (`wgContLang=en`, skip-on sqlite)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has test blob property",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Is restricted property",
+ "contents": "[[Has type::Text]] [[Allows value::open]] [[Allows value::closed]]"
+ }
+ ],
+ "beforeTest": {
+ "maintenance-run": {
+ "rebuildPropertyStatistics": true
+ }
+ },
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 search for user-defined property to contain",
+ "special-page": {
+ "page": "Properties",
+ "query-parameters": [],
+ "request-parameters": {
+ "property": "test blob"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property:Has_test_blob_property\" title=\"ID:",
+ "Has test blob property"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 search for pre-defined property",
+ "special-page": {
+ "page": "Properties",
+ "query-parameters": [],
+ "request-parameters": {
+ "property": "Allow"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property:Allows_value\" title=\"ID: 14 (_PVAL)\">Allows value</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPropertyZeroCountDisplay": true
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0002.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0002.json
new file mode 100644
index 00000000..a85723ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0002.json
@@ -0,0 +1,586 @@
+{
+ "description": "Test output from `Special:SearchByProperty` for `_num`, `_txt`, `_tel` (#1728, #2009, `wgContLang=en`, `wgLang=en`, skip-on sqlite, postgres)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "P-20-25",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]] [[Display precision of::2]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has telephone number",
+ "contents": "[[Has type::Telephone number]]"
+ },
+ {
+ "page": "Example/S0002/1",
+ "contents": "[[Has text::S0002]]"
+ },
+ {
+ "page": "Example/S0002/2",
+ "contents": "[[Has text::foo bar]]"
+ },
+ {
+ "page": "Example/S0002/3",
+ "contents": "[[Has number::3.555567]]"
+ },
+ {
+ "page": "Example/S0002/4",
+ "contents": "[[Has number::1.2e-13]]"
+ },
+ {
+ "page": "Example/S0002/5",
+ "contents": "[[Has telephone number::+1-201-555-0123]]"
+ },
+ {
+ "page": "Example/S0002/6",
+ "contents": "[[Has text::foo-bar]]"
+ },
+ {
+ "page": "Example/S0002/7",
+ "contents": "[[Has text::foo-123#&^*%<1?=/->\"']]"
+ },
+ {
+ "page": "Example/S0002/8",
+ "contents": "[[Has text::foo-123_abc']]"
+ },
+ {
+ "page": "Example/S0002/9",
+ "contents": "[[Has number::-20]]"
+ },
+ {
+ "page": "Example/S0002/10",
+ "contents": "[[Has number::-25]]"
+ },
+ {
+ "page": "Example/S0002/11",
+ "contents": "[[Has text::AA-2000]]"
+ },
+ {
+ "page": "Example/S0002/12",
+ "contents": "[[Has text::AA-20-2D00]]"
+ },
+ {
+ "page": "Example/S0002/13",
+ "contents": "[[Has text::AA-20-2500]]"
+ },
+ {
+ "page": "Example/S0002/14",
+ "contents": "[[Has text::AA-2000 with_&]]"
+ },
+ {
+ "page": "Example/S0002/15",
+ "contents": "[[P-20-25::-25]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 search for user-defined property to contain",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has_text"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li>S0002&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20text/S0002\">+</a></span>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 value to contain `_`",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has text",
+ "value": "foo bar"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F2",
+ "<small>(foo bar)</small>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 value to contain ` `",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has text",
+ "value": "foo bar"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F2",
+ "<small>(foo bar)</small>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 value input not be constraint by precision",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has number",
+ "value": "3.555567"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F3",
+ "<small>(3.556)</small>"
+ ],
+ "not-contain": [
+ "value=3.56"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4 do not encode e- for a number value",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has number",
+ "value": "1.2e-13"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F4",
+ "<small>(1.2e-13)</small>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#5 same as 1.2e-13",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has number",
+ "value": "0.00000000000012"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F4",
+ "<small>(1.2e-13)</small>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6 telephone number",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has telephone number",
+ "value": "+1-201-555-0123"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F5",
+ "+1-201-555-0123"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6 telephone number",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has telephone number",
+ "value": "+1-201-555-0123"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F5",
+ "+1-201-555-0123"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#7 invalid property and avoid a `...must be an instance of SMWDIProperty, instance of SMWDIError given...`",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": ">[[Foo",
+ "value": ""
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "&gt;[[Foo"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#8 text with dash",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "foo-bar"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F6",
+ "foo-bar"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#9 text with dash",
+ "skip-on": {
+ "virtuoso": "Failed asserting that 0 matches expected 2, don't know why!"
+ },
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "foo-2Dbar"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F6",
+ "foo-bar"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#10 text with special html chars",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "foo-123#&^*%<1?=/->\"'"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F7",
+ "foo-123#&amp;^*%&lt;1?=/-&gt;\"'"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#11 text with -/_",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "foo-123_abc'"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F8",
+ "<small>(foo-123_abc')</small>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#12 Number/-20 unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has number",
+ "value": "-20"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F9",
+ "<small>(-20)</small>",
+ "value=\"-20\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#13 Number/-20 escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20number/-2D20",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F9",
+ "<small>(-20)</small>",
+ "value=\"-20\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#13 Number/-25 unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has number",
+ "value": "-25"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F10",
+ "<small>(-25)</small>",
+ "value=\"-25\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#14 Number/-25 escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20number/-2D25",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F10",
+ "<small>(-25)</small>",
+ "value=\"-25\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#15 Text/-20 unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "AA-2000"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F11",
+ "<small>(AA-2000)</small>",
+ "value=\"AA-2000\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#16 Text/-20 escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20text/AA-2D2000",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F11",
+ "<small>(AA-2000)</small>",
+ "value=\"AA-2000\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#15 Text/-20-2D unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "AA-20-2D00"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F12",
+ "<small>(AA-20-2D00)</small>",
+ "value=\"AA-20-2D00\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#16 Text/-20-2D escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20text/AA-2D20-2D2D00",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F12",
+ "<small>(AA-20-2D00)</small>",
+ "value=\"AA-20-2D00\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#17 Text/-20-25 unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "AA-20-2500"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F13",
+ "<small>(AA-20-2500)</small>",
+ "value=\"AA-20-2500\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#18 Text/-20-25 escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20text/AA-2D20-2D2500",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F13",
+ "<small>(AA-20-2500)</small>",
+ "value=\"AA-20-2500\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#19 Text/-20_& unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "Has text",
+ "value": "AA-2000 with_&"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F14",
+ "<small>(AA-2000 with_&amp;)</small>",
+ "value=\"AA-2000 with_&amp;\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#20 Text/-20_& escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:Has-20text/AA-2D2000-20with-5F-26",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F14",
+ "<small>(AA-2000 with_&amp;)</small>",
+ "value=\"AA-2000 with_&amp;\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#21 Property-20-25 unescape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "",
+ "request-parameters":{
+ "property": "P-20-25",
+ "value": "-25"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F15",
+ "<small>(-25)</small>",
+ "value=\"P-20-25\"",
+ "value=\"-25\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#22 Property-20-25 escape",
+ "special-page": {
+ "page":"SearchByProperty",
+ "query-parameters": "/:P-2D20-2D25/-2D25",
+ "request-parameters":{}
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0002-2F15",
+ "<small>(-25)</small>",
+ "value=\"P-20-25\"",
+ "value=\"-25\""
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en"
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`",
+ "postgres": "The table update lacks behind"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0003.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0003.json
new file mode 100644
index 00000000..a4fb17c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0003.json
@@ -0,0 +1,131 @@
+{
+ "description": "Test `Special:Ask` output for `format=rdf`/`format=json`/DISPLAYTITLE (#1453, #1619, `wgRestrictDisplayTitle`, `wgContLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has boolean",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "page": "Example/S0003/1",
+ "contents": "[[Has boolean::true]] [[Category:S0003]]"
+ },
+ {
+ "page": "Example/S0003/2",
+ "contents": "{{DISPLAYTITLE:FOO-S0003}} [[Category:S0003]]"
+ },
+ {
+ "page": "Example/S0003/Q1",
+ "contents": "{{#ask: [[Category:S0003]] |?Category |?Has boolean=Text |link=none |format=rdf }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/S0003/Q1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": []
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory:S0003-5D-5D/-3FCategory/-3FHas-20boolean=Text/mainlabel=/limit=100/offset=0/format=rdf/link=none\">RDF</a>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0003-5D-5D/-3FCategory/-3FHas-20boolean=Text/mainlabel=/limit=100/offset=0/format=rdf/link=none",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<rdfs:label>Example/S0003/1</rdfs:label>",
+ "<property:Has_boolean rdf:datatype=\"http://www.w3.org/2001/XMLSchema#boolean\">true</property:Has_boolean>",
+ "<swivt:type rdf:resource=\"http://example.org/id/Category-3AS0003\"/>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2",
+ "skip-on": {
+ "mw-1.19.20": "Skipping because of spaces in output fields being different",
+ "mw-1.22.12": "Skipping because of spaces in output fields being different"
+ },
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0003-5D-5D/-3FCategory/-3FHas-20boolean=Text/mainlabel=/limit=100/offset=0/format=json/unescape=true/prettyprint=true/link=none",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "\"fulltext\": \"Category:S0003\"",
+ "\"fulltext\": \"Example/S0003/1\"",
+ "\"key\": \"Has_boolean\""
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 (link=subject)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0003-5D-5D/mainlabel=/offset=0/format=table/link=subject",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "FOO-S0003"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4 (link=none)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0003-5D-5D/mainlabel=/offset=0/format=table/link=none",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/S0003/2"
+ ],
+ "not-contain": [
+ "FOO-S0003"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "wgRestrictDisplayTitle": false,
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0004.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0004.json
new file mode 100644
index 00000000..d2ea9179
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0004.json
@@ -0,0 +1,49 @@
+{
+ "description": "Test `Special:Browse` output for `_dat` (`wgContLang=en`, `wgLang=ja`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/S0004/1",
+ "contents": "[[Has date::12 Jan 1991 8:56]] [[Category:S0004]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (output is localized, info link is in accordance with content lang.)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0004/1",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">1991年1月12日 (土) 08:56:00&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20date/12-20January-201991-2008:56:00\">+</a></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "ja",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0005.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0005.json
new file mode 100644
index 00000000..9e590cd6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0005.json
@@ -0,0 +1,83 @@
+{
+ "description": "Test `Special:Browse` output for `_dat`, '_REDI' (`wgContLang=en`, `wgLang=en`, `smwgDVFeatures=SMW_DV_TIMEV_CM | SMW_DV_WPV_DTITLE`, `wgRestrictDisplayTitle=false`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/S0005/1",
+ "contents": "[[Has date::12 Jan 1991 8:56]] [[Has date::12 Jan 1345]] [[Category:S0004]]"
+ },
+ {
+ "page": "Example/S0005/2",
+ "contents": "#REDIRECT [[Example/S0005/3]]"
+ },
+ {
+ "page": "Example/S0005/3",
+ "contents": "{{DISPLAYTITLE:ABC}}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0005/1",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">08:56:00, 12 January 1991&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20date/12-20January-201991-2008:56:00\">+</a></span>",
+ "<span class=\"smwb-value\">January 12, 1345 <sup>JL</sup>&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20date/12-20January-201345-20JL\">+</a></span>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (redirect to show source page instead of DISPLAYTITLE)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0005/3",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "class=\"mw-redirect\" title=\"Example/S0005/2\">Example/S0005/2"
+ ],
+ "not-contain": [
+ "class=\"mw-redirect\" title=\"Example/S0005/2\">ABC"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ },
+ "wgRestrictDisplayTitle": false,
+ "smwgDVFeatures": [
+ "SMW_DV_TIMEV_CM",
+ "SMW_DV_WPV_DTITLE"
+ ]
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0006.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0006.json
new file mode 100644
index 00000000..e1b724a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0006.json
@@ -0,0 +1,50 @@
+{
+ "description": "Test output of `Special:WantedProperties` (`wgContLang=en`, `wgLang=en`, skip-on sqlite)",
+ "setup": [
+ {
+ "page": "Example/S0006/1",
+ "contents": "[[Has property without type::123]]"
+ },
+ {
+ "page": "Example/S0006/2",
+ "contents": "[[Has property without type::456]]"
+ },
+ {
+ "page": "Example/S0006/3",
+ "contents": "[[Has property without type::789]]"
+ }
+ ],
+ "beforeTest": {
+ "maintenance-run": {
+ "rebuildPropertyStatistics": true
+ }
+ },
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (whether to appear on the list of wanted properties)",
+ "special-page": {
+ "page": "WantedProperties",
+ "query-parameters": [],
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property:Has property without type (page does not exist)"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0007.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0007.json
new file mode 100644
index 00000000..aa7f1900
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0007.json
@@ -0,0 +1,44 @@
+{
+ "description": "Test output of `Special:UnusedProperties` (`wgContLang=en`, `wgLang=en`, skip-on sqlite, 1.19)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Unused typed property",
+ "contents": "[[Has type::Page]] [[Has property description::Typed property@en]]"
+ }
+ ],
+ "beforeTest": {
+ "maintenance-run": {
+ "rebuildPropertyStatistics": true
+ }
+ },
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (whether to appear in the list of unused properties without subproperty)",
+ "special-page": {
+ "page": "UnusedProperties",
+ "query-parameters": [],
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "Property:Unused typed property"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en"
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`",
+ "mw-1.19.20": "The table update on 1.19 lacks behind"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0008.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0008.json
new file mode 100644
index 00000000..4dbcfde9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0008.json
@@ -0,0 +1,161 @@
+{
+ "description": "Test `Special:Browse` output for `_dat`, `_boo`, `_sobj`, `_uri` (`wgContLang=en`, `wgLang=es`, skip-on 1.25.6)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has boolean",
+ "contents": "[[Has type::Boolean]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has atomic mass unit",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 u]] [[Corresponds to::1.660539040e-27 kg]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has url",
+ "contents": "[[Has type::URL]]"
+ },
+ {
+ "page": "Example/S0008/1",
+ "contents": "[[Has date::12 Jan 1991 8:56]] [[Has boolean::true]] [[Category:S0008]]"
+ },
+ {
+ "page": "Example/S0008/2",
+ "contents": "[[Has number::0.00000000000012]] [[Has number::72.769482308]] [[Has number::1.334e-13]] [[Category:S0008]]"
+ },
+ {
+ "page": "Example/S0008/3 (Hydrogen-1 atom)",
+ "contents": "[[Has atomic mass unit::1.007825 u]] [[Category:S0008]]"
+ },
+ {
+ "page": "Example/S0008/4",
+ "contents": "{{#subobject:ABC::DEF|Has number=123}}"
+ },
+ {
+ "page": "Example/S0008/5",
+ "contents": "[[Has url::http://example.org/some1::23:334/::56]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0008/1",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">verdadero&#160;&#160;",
+ "SearchByProperty/:Has-20boolean/true",
+ "<span class=\"smwb-value\">08:56:00 12 ene 1991&#160;&#160;"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (numbers)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0008/2",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">1,2e-13&#160;&#160;",
+ "<span class=\"smwb-value\">72,769&#160;&#160;",
+ "<span class=\"smwb-value\">1,334e-13&#160;&#160;",
+ "Special:SearchByProperty/:Has-20number/1.2e-2D13",
+ "Special:SearchByProperty/:Has-20number/72.769482308",
+ "Special:SearchByProperty/:Has-20number/1.334e-2D13"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (quantity)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0008/3 (Hydrogen-1 atom)",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">1,008&#160;u (1,673533e-27&#160;kg)&#160;&#160;",
+ "Special:SearchByProperty/:Has-20atomic-20mass-20unit/1.007825-20u"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 named subobject contains ::",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": ":Example-2FS0008-2F4-23ABC::DEF",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">123&#160;&#160;",
+ "Special:SearchByProperty/:Has-20number/123"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4 url contains :: (encoded with -3A)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0008/5",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "href=\"http://example.org/some1::23:334/::56\">http://example.org/some1::23:334/::56</a>&#160;&#160;",
+ "Special:SearchByProperty/:Has-20url/http-2D3A-2F-2Fexample.org-2Fsome1-2D3A-2D3A23-2D3A334-2F-2D3A-2D3A56"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "mw-1.25.6": "Somehow the content lang is not set correctly on Travis (locally works fine)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0009.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0009.json
new file mode 100644
index 00000000..70c1f55f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0009.json
@@ -0,0 +1,67 @@
+{
+ "description": "Test output in `Special:Search` for SMWSearch (`wgLanguageCode=en`, `wgContLang=en`, `wgSearchType=SMWSearch`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/S0009/1",
+ "contents": "[[Has text::Some text to search]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0",
+ "subject": "Example/S0009/1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has_text"
+ ],
+ "propertyValues": [
+ "Some text to search"
+ ]
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 results matched by the SMWSearch extension",
+ "special-page": {
+ "page": "Search",
+ "query-parameters": "",
+ "request-parameters": {
+ "search": "[[Has text::~*Some text*]]"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<ul class='mw-search-results'>",
+ "Example/S0009/1"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "wgSearchType": "SMWSearch",
+ "wgCapitalLinks" : true,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0010.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0010.json
new file mode 100644
index 00000000..185bd61a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0010.json
@@ -0,0 +1,56 @@
+{
+ "description": "Test output from `Special:SearchByProperty` / `_dat` (#1922, `wgContLang=en`, `wgLang=es`, skip-on sqlite)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/S0010/1",
+ "contents": "[[Has date::1 Jan 1970]]"
+ },
+ {
+ "page": "Example/S0010/2",
+ "contents": "[[Has date::2 Dec 2100 12:12:12]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 infolinks on value are content lang. formatted",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "property": "Has date"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li>1 ene 1970&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20date/1-20January-201970\">+</a>",
+ "<li>12:12:12 2 dic 2100&#160;&#160;",
+ "title=\"Special:SearchByProperty/:Has-20date/2-20December-202100-2012:12:12\">+</a>"
+ ],
+ "not-contain": [
+ "title=\"Special:SearchByProperty/:Has-20date/1-20ene-201970\">+</a>",
+ "title=\"Special:SearchByProperty/:Has-20date/12:12:12-202-20dic-202100\">+</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es",
+ "wgLanguageCode": "en"
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0011.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0011.json
new file mode 100644
index 00000000..7b8e4074
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0011.json
@@ -0,0 +1,136 @@
+{
+ "description": "Test `Special:Ask` output `#ask` intro/outro link/template parse (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "S0011-Template",
+ "contents": "<includeonly>{{{test}}} link [[{{{test}}}]]</includeonly>"
+ },
+ {
+ "page": "Example/S0011/1",
+ "contents": "[[Has text::abc]] [[Category:S0011]]"
+ },
+ {
+ "page": "Example/S0011/Q.1",
+ "contents": "{{#ask: [[Category:S0011]] |?Has text |limit=0 |link=none |format=table |intro=with [[intro parameter]] {{#info:intro bubble}} |outro=with [[outro parameter]] {{#info:outro bubble}} }}",
+ "message-cache": "clear"
+ },
+ {
+ "page": "Example/S0011/Q.2",
+ "contents": "{{#ask: [[Category:S0011]] |?Has text |limit=0 |link=none |format=table |intro=[[File:Foo.png|link=Bar]] }}",
+ "message-cache": "clear"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (further results)",
+ "subject": "Example/S0011/Q.1",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_MDAT",
+ "_SKEY",
+ "_ASK"
+ ],
+ "propertyValues": []
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory%3AS0011-5D-5D%2F-3FHas-20text&amp;mainlabel=&amp;offset=0&amp;format=table&amp;link=none&amp;intro=with%20%5B%5Bintro%20parameter%5D%5D%20%3Cspan%20class%3D%22smw-highlighter%22%20data-type%3D%226%22%20data-state%3D%22persistent%22%20data-title%3D%22Information%22%20title%3D%22intro%20bubble%22%3E%3Cspan%20class%3D%22smwtticon%20info%22%3E%3C%2Fspan%3E%3Cspan%20class%3D%22smwttcontent%22%3Eintro%20bubble%3C%2Fspan%3E%3C%2Fspan%3E&amp;outro=with%20%5B%5Boutro%20parameter%5D%5D%20%3Cspan%20class%3D%22smw-highlighter%22%20data-type%3D%226%22%20data-state%3D%22persistent%22%20data-title%3D%22Information%22%20title%3D%22outro%20bubble%22%3E%3Cspan%20class%3D%22smwtticon%20info%22%3E%3C%2Fspan%3E%3Cspan%20class%3D%22smwttcontent%22%3Eoutro%20bubble%3C%2Fspan%3E%3C%2Fspan%3E"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (parse [[ ... ]] as links in intro/outro)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0011-5D-5D/-3FCategory/-3FHas-20text=Text/mainlabel=/limit=100/offset=0/format=table/link=none/intro=-5B-5Bintro-parameter-5D-5D/outro=-5B-5Boutro-parameter-5D-5D",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=Intro%25parameter",
+ "class=\"new\" title=\"Intro%parameter",
+ "\">intro%parameter</a>",
+ "<td class=\"smwtype_wpg\">Example/S0011/1</td>",
+ "class=\"new\" title=\"Outro%parameter",
+ "\">outro%parameter</a>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (parse URL in intro/outro)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0011-5D-5D/-3FCategory/-3FHas-20text=Text/mainlabel=/limit=100/offset=0/format=table/link=none/intro=http:-2F-2Fexample.org-2Fintro/outro=http:-2F-2Fexample.org-2Foutro",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<a rel=\"nofollow\" class=\"external free\" href=\"http://example.org/intro\">http://example.org/intro</a>",
+ "<td class=\"smwtype_wpg\">Example/S0011/1</td>",
+ "<a rel=\"nofollow\" class=\"external free\" href=\"http://example.org/outro\">http://example.org/outro</a>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 (parse template in intro/outro)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0011-5D-5D/-3FCategory/-3FHas-20text=Text/mainlabel=/limit=100/offset=0/format=table/link=none/intro=-7B-7BS0011-2DTemplate-7Ctest%3DS0011-intro-7D-7D/outro=-7B-7BS0011-2DTemplate-7Ctest%3DS0011-outro-7D-7D",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "S0011%intro link",
+ "class=\"new\" title=\"S0011%intro",
+ "\">S0011%intro</a>",
+ "<td class=\"smwtype_wpg\">Example/S0011/1</td>",
+ "S0011%outro link",
+ "class=\"new\" title=\"S0011%outro",
+ "\">S0011%outro</a>"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (#3196, `-3D/=` decode)",
+ "subject": "Example/S0011/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory:S0011-5D-5D/-3FHas-20text/mainlabel%3D/offset%3D0/format%3Dtable/link%3Dnone/intro%3D-5B-5BFile:Foo.png-7Clink%3DBar-5D-5D"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0012.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0012.json
new file mode 100644
index 00000000..adea6b51
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0012.json
@@ -0,0 +1,66 @@
+{
+ "description": "Test `Special:Ask` output `#ask` image/upload (#2009, `wgContLang=en`, `wgLang=en`, `wgEnableUploads`, `wgFileExtensions`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has caption",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_FILE",
+ "page": "S0012.png",
+ "contents": {
+ "upload": {
+ "file" : "/../Fixtures/image-upload-480.png",
+ "text" : "[[Has file::{{FULLPAGENAME}}]] [[Has caption::Test file]] [[Category:S0012]]"
+ }
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (display image in table with 50px|thumb|123)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0012-5D-5D/-3FHas-20file#50px;thumb;123/mainlabel=-/offset=0/format=broadtable/link=all/headers=show",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<td class=\"Has-file smwtype_wpg\"><div class=\"thumb tright\"><div class=\"thumbinner\" style=\"width:52px;\">",
+ "S0012.png/50px-S0012.png\" width=\"50\" height=\"50\" class=\"thumbimage\"",
+ "<div class=\"thumbcaption\"><div class=\"magnify\">",
+ "class=\"internal\" title=\"Enlarge\">",
+ "</a></div>123</div>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgEnableUploads": true,
+ "wgFileExtensions": [
+ "png"
+ ],
+ "wgDefaultUserOptions": {
+ "thumbsize": 5
+ },
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_FILE": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0013.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0013.json
new file mode 100644
index 00000000..9862c886
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0013.json
@@ -0,0 +1,48 @@
+{
+ "description": "Test `Special:Browse` output preferred label (`wgContLang=en`, `wgLang=es`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "SomeProperty",
+ "contents": "[[Has type::Text]] [[Has preferred property label::Label with spaces@es]]"
+ },
+ {
+ "page": "Example/S0013/1",
+ "contents": "[[SomeProperty::abc]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 verify preferred label display",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0013/1",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ ">Label with spaces</a>&nbsp;<span title=\"SomeProperty\"><sup>ᵖ</sup>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0014.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0014.json
new file mode 100644
index 00000000..63d46336
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0014.json
@@ -0,0 +1,240 @@
+{
+ "description": "Test `Special:Browse` with special characters `%'\"&` (`wgContLang=en`, `wgLang=es` )",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/S0014/L'oéal",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Category:S0014]]"
+ },
+ {
+ "page": "Example/S0014/%Some@%",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Category:S0014]]"
+ },
+ {
+ "page": "Example/S0014/ABC(-3-)",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Category:S0014]]"
+ },
+ {
+ "page": "Example/S0014/\"Some\"&*^-25-20-2D",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Category:S0014]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "skip-on": {
+ "mediawiki": [ ">1.29.x", "MediaWiki changed sanitization from `'` to `&#039;`" ]
+ },
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0014/L'oéal",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0014-2FL%27o%C3%A9al",
+ "title=\"Example/S0014/L'oéal\">Example/S0014/L'oéal</a>",
+ "Example-2FS0014-2FL'oéal"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#0 (1.30+)",
+ "skip-on": {
+ "mediawiki": [ "<1.29.x", "MediaWiki changed sanitization from `'` to `&#039;`" ]
+ },
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0014/L'oéal",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0014-2FL%27o%C3%A9al",
+ "title=\"Example/S0014/L&#039;oéal\">Example/S0014/L'oéal</a>",
+ "Example-2FS0014-2FL&#039;oéal"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1",
+ "skip-on": {
+ "mediawiki": [ ">1.29.x", "MediaWiki changed sanitization from `'` to `&#039;`" ]
+ },
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "",
+ "request-parameters": {
+ "output": "legacy",
+ "article" : "Example/S0014/L'oéal"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0014-2FL%27o%C3%A9al",
+ "title=\"Example/S0014/L'oéal\">Example/S0014/L'oéal</a>",
+ "Example-2FS0014-2FL'oéal"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (1.30+)",
+ "skip-on": {
+ "mediawiki": [ "<1.29.x", "MediaWiki changed sanitization from `'` to `&#039;`" ]
+ },
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "",
+ "request-parameters": {
+ "output": "legacy",
+ "article" : "Example/S0014/L'oéal"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example-2FS0014-2FL%27o%C3%A9al",
+ "title=\"Example/S0014/L&#039;oéal\">Example/S0014/L'oéal</a>",
+ "Example-2FS0014-2FL&#039;oéal"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0014/%Some@%",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/S0014/%Some@%\">Example/S0014/%Some@%</a>",
+ "Example-2FS0014-2F-25Some@-25"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "",
+ "request-parameters": {
+ "output": "legacy",
+ "article" : "Example/S0014/%Some@%"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/S0014/%Some@%\">Example/S0014/%Some@%</a>",
+ "Example-2FS0014-2F-25Some@-25"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0014/ABC(-3-)",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/S0014/ABC(-3-)\">Example/S0014/ABC(-3-)</a>",
+ "Example-2FS0014-2FABC(-2D3-2D)"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#5",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "",
+ "request-parameters": {
+ "output": "legacy",
+ "article" : "Example/S0014/ABC(-3-)"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "title=\"Example/S0014/ABC(-3-)\">Example/S0014/ABC(-3-)</a>",
+ "Example-2FS0014-2FABC(-2D3-2D)"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0014/\"Some\"&*^-25-20-2D",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/S0014/%22Some%22%26*%5E-25-20-2D",
+ "title=\"Example/S0014/&quot;Some&quot;&amp;*^-25-20-2D\">Example/S0014/\"Some\"&*^-25-20-2D</a>",
+ "Example-2FS0014-2F&quot;Some&quot;-26*^-2D25-2D20-2D2D"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "",
+ "request-parameters": {
+ "output": "legacy",
+ "article": "Example/S0014/\"Some\"&*^-25-20-2D"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example/S0014/%22Some%22%26*%5E-25-20-2D",
+ "title=\"Example/S0014/&quot;Some&quot;&amp;*^-25-20-2D\">Example/S0014/\"Some\"&*^-25-20-2D</a>",
+ "Example-2FS0014-2F&quot;Some&quot;-26*^-2D25-2D20-2D2D"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "es",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "skip-on": {
+ "mw-1.25.6": "Somehow the content lang is not set correctly on Travis (locally works fine)."
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0015.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0015.json
new file mode 100644
index 00000000..fb5e414e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0015.json
@@ -0,0 +1,71 @@
+{
+ "description": "Test `Special:Ask` output for `_txt` with formatted text (#..., `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0015/1",
+ "contents": "[[Has text::Test '''with text formatting''']] {{#set: Has text=Link to [[Example/S0015/1]]}} [[Category:S0015]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0015/2",
+ "contents": "[[Code::Test '''with text formatting''']] {{#set: Code=Link to [[Example/S0015/1]]}} [[Category:S0015]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (`_txt` formatting, link)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0015-5D-5D-20-5B-5BHas text%3A%3A%2B-5D-5D/-3FHas text/mainlabel=-/offset=0/format=broadtable/link=all/headers=show",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "Test <b>with text formatting</b>",
+ "Link to <a",
+ "Example/S0015/1\" title=\"Example/S0015/1\">Example/S0015/1</a>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (`_cod` formatting, link)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory%3AS0015-5D-5D-20-5B-5BCode%3A%3A%2B-5D-5D/-3FCode/mainlabel=-/offset=0/format=broadtable/link=all/headers=show",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smwpre\">Link&#160;to&#160;&#91;&#91;Example/S0015/1]]</div>",
+ "<div class=\"smwpre\">Test&#160;&#x27;&#x27;&#x27;with&#160;text&#160;formatting&#x27;&#x27;&#x27;</div>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0016.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0016.json
new file mode 100644
index 00000000..6a29e659
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0016.json
@@ -0,0 +1,128 @@
+{
+ "description": "Test `Special:Ask` to produce correct printout position for `+|...` parameters (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has monolingual text",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0016/1",
+ "contents": "[[Has monolingual text::Test@en]] [[Has monolingual text::テスト@ja]] [[Category:S0016]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (column order for printouts with additional `+|...` parameter)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0016-5D-5D/-3FHas-20monolingual-20text-7C%2Blang%3Den/-3FModification-20date/mainlabel%3D/offset%3D0/format%3Dtable/link%3Dnone",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\"><thead><th>&nbsp;</th><th class=\"Has-monolingual-text\">",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td>"
+ ],
+ "not-contain": [
+ "<table class=\"sortable wikitable smwtable\"><thead><th>&nbsp;</th><th class=\"Modification-date\">",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td data-sort-value=\"2457858.0750926\" class=\"Modification-date smwtype_dat\">"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (column order for mixed printout)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0016-5D-5D/-3FHas-20monolingual-20text-7C%2Blang%3Den/-3FCategory/-3FHas-20monolingual-20text-7C%2Blang%3Dja/mainlabel%3D/offset%3D0/format%3Dtable/link%3Dnone",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th><th class=\"Category\"><a .*>Category</a></th><th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th>",
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Category smwtype_wpg\">Category:S0016</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ],
+ "not-contain": [
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (column order for mixed printout)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": [],
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "searchlabel": "some..."
+ },
+ "q": "[[Category:S0016]]",
+ "po": "?Has monolingual text|+lang=en|?Category|?Has monolingual text|+lang=ja"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th><th class=\"Category\"><a .*>Category</a></th><th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th>",
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Category smwtype_wpg\">Category:S0016</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ],
+ "not-contain": [
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 (column order for mixed printout)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": [],
+ "request-parameters": {
+ "p": [
+ "link=none",
+ "limit=10",
+ "offset=0",
+ "mainlabel=",
+ "searchlabel=some..."
+ ],
+ "q": "[[Category:S0016]]",
+ "po": "?Has monolingual text|+lang=en|?Category|?Has monolingual text|+lang=ja"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th><th class=\"Category\"><a .*>Category</a></th><th class=\"Has-monolingual-text\"><a .* title=\"Property:Has monolingual text\">Has monolingual text</a></th>",
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Category smwtype_wpg\">Category:S0016</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ],
+ "not-contain": [
+ "<tbody><tr data-row-number=\"1\" class=\"row-odd\"><td class=\"smwtype_wpg\">Example/S0016/1</td><td class=\"Has-monolingual-text smwtype_txt\">Test</td><td class=\"Has-monolingual-text smwtype_txt\">テスト</td></tr></tbody>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0017.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0017.json
new file mode 100644
index 00000000..ef55a15d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0017.json
@@ -0,0 +1,79 @@
+{
+ "description": "Test `Special:Types` (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has monolingual text",
+ "contents": "[[Has type::Monolingual text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Types",
+ "query-parameters": "",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div id=\"smw-types\" class=\"smw-tabs smw-types\"><input id=\"tab-smw-type-list\" class=\"nav-tab\" type=\"radio\" name=\"types\" checked=\"\"/>",
+ "<p class=\"plainlinks smw-types-intro\">"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (Monolingual text)",
+ "special-page": {
+ "page": "Types",
+ "query-parameters": "Monolingual text",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-breadcrumb-link\"><span class=\"smw-breadcrumb-arrow-right\">",
+ "<a href=\".*Has_monolingual_text\" title=\".*Has monolingual text\">"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (Foobar, unknown)",
+ "special-page": {
+ "page": "Types",
+ "query-parameters": "Foobar",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"plainlinks smw-type-unknown\">"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0018.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0018.json
new file mode 100644
index 00000000..6491a23c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0018.json
@@ -0,0 +1,93 @@
+{
+ "description": "Test `Special:Ask` common output (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0018/1",
+ "contents": "[[Has page::123]] [[Category:S0018]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (noscript)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": [],
+ "request-parameters": {}
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div id=\"ask-status\" class=\"smw-ask-status plainlinks\"><noscript><div class=\"smw-callout smw-callout-error\">"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (@see #3542, `format= table`)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0018-5D-5D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3D-20table",
+ "request-parameters": {}
+ },
+ "assert-output": {
+ "to-contain": [
+ "{{#ask: [[Category:S0018]]",
+ " |?Has page",
+ " |format=table",
+ " |limit=50",
+ " |offset=0",
+ " |sort=",
+ " |order=asc",
+ " |mainlabel=",
+ "}}"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (`format=TABLE`)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0018-5D-5D/-3FHas-20page/mainlabel%3D/offset%3D0/format%3DTABLE",
+ "request-parameters": {}
+ },
+ "assert-output": {
+ "to-contain": [
+ "{{#ask: [[Category:S0018]]",
+ " |?Has page",
+ " |format=table",
+ " |limit=50",
+ " |offset=0",
+ " |sort=",
+ " |order=asc",
+ " |mainlabel=",
+ "}}"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgNamespace": "http://example.org/id/",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0019.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0019.json
new file mode 100644
index 00000000..324ee61e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0019.json
@@ -0,0 +1,40 @@
+{
+ "description": "Test output of `Special:WantedProperties` on unapproved property (`wgContLang=en`, `wgLang=en`, `smwgCreateProtectionRight`)",
+ "setup": [
+ {
+ "page": "Example/S0019/1",
+ "contents": "[[SomeWantedProperty::123]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 restricted property causes unapproved listing",
+ "special-page": {
+ "page": "WantedProperties",
+ "query-parameters": [],
+ "request-parameters": {
+ "filter": "unapprove"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li><a .*SomeWantedProperty&amp;action=view\" .*SomeWantedProperty</a> (1 use)</li>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCreateProtectionRight": "foo"
+ },
+ "meta": {
+ "skip-on": {
+ "sqlite": "Returns a `database is locked`"
+ },
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0020.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0020.json
new file mode 100644
index 00000000..24534f34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0020.json
@@ -0,0 +1,121 @@
+{
+ "description": "Test `Special:Ask` with `format=json` output (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0020/1",
+ "contents": "[[Has text::Some example]] [[Category:S0020]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0020/2",
+ "contents": "[[Has number::123]] [[Has number::345]] [[Category:S0020]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "prettyprint": false,
+ "unescape": true,
+ "format": "json"
+ },
+ "q": "[[Category:S0020]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/s-0020.0.txt"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 `type=simple`",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "type": "simple",
+ "prettyprint": false,
+ "unescape": true,
+ "format": "json"
+ },
+ "q": "[[Category:S0020]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/s-0020.1.txt"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 `type=simple`, `mainlabel=-`",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "-",
+ "type": "simple",
+ "prettyprint": false,
+ "unescape": true,
+ "format": "json"
+ },
+ "q": "[[Category:S0020]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/s-0020.2.txt"
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0021.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0021.json
new file mode 100644
index 00000000..70572079
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0021.json
@@ -0,0 +1,81 @@
+{
+ "description": "Test `format=table` on `Special:Ask` with `headers=plain` (#2702, `wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has extra text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/S0021/1",
+ "contents": "[[Has extra text::S0021]]"
+ },
+ {
+ "page": "Example/S0021/2",
+ "contents": "[[Has extra text::S0021]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 with headers",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "limit": "10",
+ "offset": "0",
+ "headers": "",
+ "format": "table"
+ },
+ "q": "[[Has extra text::S0021]]",
+ "po": "?Has extra text=Modified <i>label</i> for text"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<thead><th>&nbsp;</th><th class=\"Modified-label-for-text\"><a href=.* title=\"Property:Has extra text\">Modified <i>label</i> for text</a></th></thead>",
+ "<a href=.* title=\"Example/S0021/1\">Example/S0021/1</a>",
+ "<a href=.* title=\"Example/S0021/2\">Example/S0021/2</a>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 with headers plain",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "limit": "10",
+ "offset": "0",
+ "headers": "plain",
+ "format": "table"
+ },
+ "q": "[[Has extra text::S0021]]",
+ "po": "?Has extra text=Modified <i>label</i> for text"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<thead><th>&nbsp;</th><th class=\"Modified-label-for-text\">Modified <i>label</i> for text</th></thead>",
+ "<a href=.* title=\"Example/S0021/1\">Example/S0021/1</a>",
+ "<a href=.* title=\"Example/S0021/2\">Example/S0021/2</a>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0022.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0022.json
new file mode 100644
index 00000000..946ef38d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0022.json
@@ -0,0 +1,248 @@
+{
+ "description": "Test `format=csv` output via `Special:Ask` (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0022/1",
+ "contents": "[[Has text::Some example]] [[Category:S0022]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0022/2",
+ "contents": "[[Has number::123]] [[Has number::345]] [[Category:S0022]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0022/3",
+ "contents": "[[Has text::ABC]] [[Has number::123]] [[Category:S0022]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "Example/S0022/4",
+ "contents": "[[Has text::DEF]] [[Has number::123]] [[Category:S0022]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 with header",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "csv"
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.0.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 without header",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "headers": "hide",
+ "format": "csv"
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.1.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 mainlabel",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "-",
+ "format": "csv"
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has number|?Has text"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.2.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 merge rows with identical first column identifier",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "-",
+ "format": "csv",
+ "merge": true
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has number|?Has text"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.3.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4 valuesep, headers=hide",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "headers": "hide",
+ "valuesep": ";",
+ "format": "csv"
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.4.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#5 sep, valuesep",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "format": "csv",
+ "sep": "|",
+ "valuesep": "+"
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has number|?Has text"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.5.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6 merge rows, valuesep",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "-",
+ "format": "csv",
+ "valuesep": ";",
+ "merge": true
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has number|?Has text"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.6.csv"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#7 merge rows, valuesep, sep",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "-",
+ "format": "csv",
+ "sep": "|",
+ "valuesep": ";",
+ "merge": true
+ },
+ "q": "[[Category:S0022]]",
+ "po": "?Has number|?Has text"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0022.7.csv"
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0023.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0023.json
new file mode 100644
index 00000000..b1e2a9a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0023.json
@@ -0,0 +1,44 @@
+{
+ "description": "Test `Special:Browse` output category (`wgContLang=en`, `wgLang=en`)",
+ "setup": [
+ {
+ "page": "Example/S0023/1",
+ "contents": "[[Category:S0023]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (category singular display)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0023/1",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<div class=\"smw-table-cell smwb-cell smwb-prophead\"><a href=.*>Category</a></div>",
+ "<div class=\"smw-table-cell smwb-cell smwb-propval\"><span class=\"smwb-value\"><a href=.*>S0023</a></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0024.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0024.json
new file mode 100644
index 00000000..effac312
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0024.json
@@ -0,0 +1,49 @@
+{
+ "description": "Test `Special:Browse` with compact links (`smwgCompactLinkSupport`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/S0024/L'oéal",
+ "contents": "[[Has page::{{FULLPAGENAME}}]] [[Category:S0024]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "cl:OkV4YW1wbGUtMkZTMDAyNC0yRkwnb8OpYWw",
+ "request-parameters": {
+ "output": "legacy"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwbrowse\"><a href=.*cl:OkV4YW1wbGUtMkZTMDAyNC0yRkwnb8OpYWw\" title=\"Special:Browse/cl:OkV4YW1wbGUtMkZTMDAyNC0yRkwnb8OpYWw\">+</a></span>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgCompactLinkSupport": true,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0025.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0025.json
new file mode 100644
index 00000000..42cb41c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0025.json
@@ -0,0 +1,106 @@
+{
+ "description": "Test `format=templatefile` (with `_eid`) output via `Special:Ask`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has GND",
+ "contents": "[[Has type::External identifier]] [[External formatter uri::http://d-nb.info/gnd/$1]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has name",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "BEACON",
+ "contents": {
+ "import-from": "/../Fixtures/s-0025-beacon.txt"
+ }
+ },
+ {
+ "namespace": "NS_TEMPLATE",
+ "page": "BEACON-INTRO",
+ "contents": {
+ "import-from": "/../Fixtures/s-0025-beacon-intro.txt"
+ }
+ },
+ {
+ "page": "Example/S0025/1",
+ "contents": "[[Has name::John Doe]] [[Has GND::123456789]]"
+ },
+ {
+ "page": "Example/S0025/2",
+ "contents": "[[Has name::Jane Doe]] [[Has GND::987654321]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "templatefile",
+ "template": "BEACON",
+ "introtemplate": "BEACON-INTRO"
+ },
+ "q": "[[Has GND::123456789]]",
+ "po": "?Has GND=GND|?Has name=Name"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0025.0.txt"
+ }
+ }
+ },
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "templatefile",
+ "template": "BEACON",
+ "introtemplate": "BEACON-INTRO"
+ },
+ "q": "[[Has GND::123456789]] OR [[Has GND::987654321]]",
+ "po": "?Has GND=GND|?Has name=Name"
+ }
+ },
+ "assert-output": {
+ "to-contain": {
+ "contents-file" : "/../Fixtures/res.s-0025.1.txt"
+ }
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0026.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0026.json
new file mode 100644
index 00000000..3cd361ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0026.json
@@ -0,0 +1,47 @@
+{
+ "description": "Test output from `Special:PageProperty` (with `_dat`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "S0026 date",
+ "contents": "[[Has type::Date]]"
+ },
+ {
+ "page": "Example/S0026/1",
+ "contents": "[[S0026 date::1 Jan 1970]]"
+ },
+ {
+ "page": "Example/S0026/2",
+ "contents": "[[S0026 date::2 Dec 2100 12:12:12]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "PageProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "type": "S0026 date"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li>1 January 1970&#160;&#160;<span class=\"smwsearch\"><a href=\".*:S0026-20date/1-20January-201970\" title=\"1 January 1970\">+</a></span></li>",
+ "<li>2 December 2100 12:12:12&#160;&#160;<span class=\"smwsearch\"><a href=\".*:S0026-20date/2-20December-202100-2012:12:12\" title=\"2 December 2100 12:12:12\">+</a></span></li>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0027.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0027.json
new file mode 100644
index 00000000..1cca69dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0027.json
@@ -0,0 +1,179 @@
+{
+ "description": "Test `format=feed` output via `Special:Ask` (`wgEnableUploads`, `wgFileExtensions`, `wgRestrictDisplayTitle`)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has number",
+ "contents": "[[Has type::Number]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/1",
+ "contents": "[[Has text::Some example]] [[Category:S0027]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/2",
+ "contents": "[[Has number::123]] [[Has number::345]] [[Category:S0027]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/3",
+ "contents": "[[Has text::ABC]] [[Has number::123]] [[Category:S0027]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/4",
+ "contents": "[[Has text::DEF]] [[Has number::123]] {{DISPLAYTITLE:Uses different title}} [[Category:S0027]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/Q.1",
+ "contents": "{{#ask: [[Category:S0027]] |format=feed |type=atom |limit=10 |searchlabel=feed output }}"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0027/Q.2",
+ "contents": "{{#ask: [[Category:S0027]] |format=feed |type=atom |limit=10 |offset=3 |searchlabel=feed output }}"
+ },
+ {
+ "namespace": "NS_FILE",
+ "page": "S0027.png",
+ "contents": {
+ "upload": {
+ "file" : "/../Fixtures/image-upload-480.png",
+ "text" : "[[Has file::File:S0027.png]] [[Has caption::Test file]] [[Category:S0027]]"
+ }
+ }
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 with header",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "feed"
+ },
+ "q": "[[Category:S0027]]",
+ "po": "?Has text|?Has number"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<title>S0027/1</title>",
+ "<title>S0027/2</title>",
+ "<title>S0027/3</title>",
+ "<title>Uses different title</title>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (file)",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "feed"
+ },
+ "q": "[[Has file::+]] [[Category:S0027]]",
+ "po": "?Has file|?Has caption"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<title>File:S0027.png</title>",
+ "a href=.*File:S0027.png",
+ "img alt=&quot;File:S0027.png"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (file, link=none)",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "format": "feed"
+ },
+ "q": "[[Has file::+]] [[Category:S0027]]",
+ "po": "?Has file|?Has caption"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<title>File:S0027.png</title>",
+ "File:S0027.png",
+ "Test file"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#3 (verify link, correct 0 offset)",
+ "subject": "S0027/Q.1",
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory:S0027-5D-5D/mainlabel=/limit=10/order=desc/sort=/offset=0/format=feed/searchlabel=feed-20output/type=atom"
+ ]
+ }
+ },
+ {
+ "type": "parser",
+ "about": "#4 (verify link)",
+ "subject": "S0027/Q.2",
+ "assert-output": {
+ "to-contain": [
+ "-5B-5BCategory:S0027-5D-5D/mainlabel=/offset=3/limit=10/order=desc/sort=/format=feed/searchlabel=feed-20output/type=atom"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgCompactLinkSupport": false,
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "wgEnableUploads": true,
+ "wgFileExtensions": [
+ "png"
+ ],
+ "wgDefaultUserOptions": {
+ "thumbsize": 5
+ },
+ "wgRestrictDisplayTitle": false,
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "NS_FILE": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0028.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0028.json
new file mode 100644
index 00000000..43ace00c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0028.json
@@ -0,0 +1,94 @@
+{
+ "description": "Test `Special:Browse` limited value list",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has text",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Example/S0028/1",
+ "contents": "[[Has text::S1]], [[Has text::S2]], [[Has text::S3]], [[Has text::S4]], [[Has text::S5]], [[Has text::S6]]"
+ },
+ {
+ "page": "Example/S0028/2",
+ "contents": "[[Has page::Example/S0028/5]]"
+ },
+ {
+ "page": "Example/S0028/3",
+ "contents": "[[Has page::Example/S0028/5]]"
+ },
+ {
+ "page": "Example/S0028/4",
+ "contents": "[[Has page::Example/S0028/5]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (limited outgoing value list)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0028/1",
+ "request-parameters": {
+ "output": "legacy",
+ "valuelistlimit-out": 5
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-value\">S1",
+ "<span class=\"smwb-value\">S2",
+ "<span class=\"smwb-value\">S3",
+ "<span class=\"smwb-value\">S4",
+ "<span class=\"smwb-value\">S5"
+ ],
+ "not-contain": [
+ "<span class=\"smwb-value\">S6"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (limited incoming value list)",
+ "special-page": {
+ "page": "Browse",
+ "query-parameters": "Example/S0028/5",
+ "request-parameters": {
+ "output": "legacy",
+ "valuelistlimit-in": 3
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<span class=\"smwb-ivalue\"><a href=\".*/Example/S0028/2\"",
+ "<span class=\"smwb-ivalue\"><a href=\".*/Example/S0028/3\""
+ ],
+ "not-contain": [
+ "<span class=\"smwb-ivalue\"><a href=\".*/Example/S0028/4\""
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0029.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0029.json
new file mode 100644
index 00000000..41525ec3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0029.json
@@ -0,0 +1,185 @@
+{
+ "description": "Test `Special:Ask` output on `mainlabel=.../?#...`, `format=table`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/S0029/1",
+ "contents": "[[Has page::123]] [[Category:S0029]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (?#, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th>&nbsp;</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#0.1 (?, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th>&nbsp;</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (?#, mainlabel=-, output the same as in #0)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23/mainlabel%3D-2D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th>&nbsp;</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (?#, mainlabel=FOO, ?# takes precedence over mainlabel, same output as in #0)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23/mainlabel%3DFOO/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th>&nbsp;</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#3 (?#- forces plain output, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23-2D/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th>&nbsp;</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"smwtype_wpg\">Example/S0029/1</td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4 (?#=Foo, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23%3DFoo/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th class=\"Foo\">Foo</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"Foo smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#4.1 (?=Foo, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F%3DFoo/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th class=\"Foo\">Foo</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"Foo smwtype_wpg\"><a href=.*Example/S0029/1\" title=\"Example/S0029/1\">Example/S0029/1</a></td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#5 (?#=Foo#- forces plain output, empty mainlabel)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23%3DFoo-23-2D/mainlabel%3D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th class=\"Foo\">Foo</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"Foo smwtype_wpg\">Example/S0029/1</td>"
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#6 (?#=Foo#- forces plain output, mainlabel=-, same output as in #5)",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BCategory:S0029-5D-5D/-3FHas-20page/-3F-23%3DFoo-23-2D/mainlabel%3D-2D/offset%3D0/format%3Dtable",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<table class=\"sortable wikitable smwtable\">",
+ "<a href=.*Property:Has_page\" title=\"Property:Has page\">Has page</a></th><th class=\"Foo\">Foo</th>",
+ "<tr data-row-number=\"1\" class=\"row-odd\"><td class=\"Has-page smwtype_wpg\" data-sort-value=\"123\">",
+ "<td class=\"Foo smwtype_wpg\">Example/S0029/1</td>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0030.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0030.json
new file mode 100644
index 00000000..c64c37ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0030.json
@@ -0,0 +1,50 @@
+{
+ "description": "Test `Special:Concepts`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_CONCEPT",
+ "page": "S0030/1",
+ "contents": "{{#concept: [[ConceptTest::+]] |Concept with no results}}"
+ },
+ {
+ "page": "S0030/2",
+ "contents": "{{#ask: [[Concept:S0030/Unknown]] }}"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Concepts",
+ "query-parameters": "",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "<a href=\".*Concept:S0030/1\" title=\"Concept:S0030/1\">Concept:S0030/1</a>"
+ ],
+ "not-contain": [
+ "Concept:S0030/Unknown"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true,
+ "SMW_NS_CONCEPT": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0031.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0031.json
new file mode 100644
index 00000000..829a37c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0031.json
@@ -0,0 +1,95 @@
+{
+ "description": "Test `Special:Ask` output on `?...=[[...|...]]|+index...`",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has page",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/S0031/1",
+ "contents": "[[Has page::123]] [[Category:S0031]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (#3211, label `?Has page=[[Foo|bar]]` ",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BHas-20page::123-5D-5D-20-5B-5BCategory:S0031-5D-5D/-3FHas-20page%3D-5B-5BFoo-7Cbar-5D-5D/mainlabel%3D/offset%3D0/format%3Dtable/headers%3Dplain",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "{{#ask: [[Has page::123]] [[Category:S0031]]",
+ " |?Has page=[[Foo|bar]]",
+ " |order=asc",
+ " |headers=plain",
+ " |mainlabel="
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#1 (#3211, label `?Has page=[[Foo|bar]]|+index=1` ",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": "-5B-5BHas-20page::123-5D-5D-20-5B-5BCategory:S0031-5D-5D/-3FHas-20page%3D-5B-5BFoo-7Cbar-5D-5D-7C%2Bindex%3D1/mainlabel%3D/offset%3D0/format%3Dtable/headers%3Dplain",
+ "request-parameters": []
+ },
+ "assert-output": {
+ "to-contain": [
+ "{{#ask: [[Has page::123]] [[Category:S0031]]",
+ " |?Has page=[[Foo|bar]]|+index=1",
+ " |order=asc",
+ " |headers=plain",
+ " |mainlabel="
+ ]
+ }
+ },
+ {
+ "type": "special",
+ "about": "#2 (#3211, label `?Has page=[[Foo|bar]]|+index=1` ",
+ "special-page": {
+ "page": "Ask",
+ "query-parameters": [],
+ "request-parameters": {
+ "p": {
+ "headers": "plain",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "searchlabel": ""
+ },
+ "q": "[[Has page::123]] [[Category:S0031]]",
+ "po": "?Has page=[[Foo|bar]]|+index=1"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "{{#ask: [[Has page::123]] [[Category:S0031]]",
+ " |?Has page=[[Foo|bar]]|+index=1",
+ " |order=asc",
+ " |headers=plain"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0032.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0032.json
new file mode 100644
index 00000000..dd2f11ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0032.json
@@ -0,0 +1,65 @@
+{
+ "description": "Test `format=json` output via `Special:Ask` for `_ref_rec`/`_qty` type (#3517)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Ref prop",
+ "contents": "[[Has type::Reference]] [[Has fields::Field prop;other prop]]"
+ },
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Field prop",
+ "contents": "[[Has type::Quantity]] [[Corresponds to::1 km]]"
+ },
+ {
+ "namespace": "NS_MAIN",
+ "page": "S0032/1",
+ "contents": "[[Ref prop::123;S0032/2]] [[Category:S0032]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0",
+ "special-page": {
+ "page": "Ask",
+ "request-parameters": {
+ "p": {
+ "link": "none",
+ "limit": "10",
+ "offset": "0",
+ "mainlabel": "",
+ "prettyprint": false,
+ "unescape": true,
+ "format": "json"
+ },
+ "q": "[[Category:S0032]]",
+ "po": "?Ref prop.Field prop"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "\"chain\":\"Ref prop.Field prop\"",
+ "{\"Field prop\":[{\"value\":123,\"unit\":\"km\"}]}"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "wgLanguageCode": "en",
+ "smwgPageSpecialProperties": [
+ "_MDAT"
+ ],
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0033.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0033.json
new file mode 100644
index 00000000..ad430659
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/TestCases/s-0033.json
@@ -0,0 +1,59 @@
+{
+ "description": "Test output from `Special:SearchByProperty` to show all values for a property (#3531)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has vtext",
+ "contents": "[[Has type::Text]]"
+ },
+ {
+ "page": "Test/S0033/1",
+ "contents": "[[Has vtext::123]] [[Has vtext::456]] [[Has vtext::abc]] [[Has vtext::def]] [[Has vtext::1001]] [[Has vtext::42]]"
+ },
+ {
+ "page": "Test/S0033/2",
+ "contents": "[[Has vtext::123]] [[Has vtext::456]] [[Has vtext::abc]] [[Has vtext::def]] [[Has vtext::1001]] [[Has vtext::42]]"
+ },
+ {
+ "page": "Test/S0033/3",
+ "contents": "[[Has vtext::123]] [[Has vtext::456]] [[Has vtext::abc]] [[Has vtext::def]] [[Has vtext::1001]] [[Has vtext::42]]"
+ },
+ {
+ "page": "Test/S0033/4",
+ "contents": "[[Has vtext::123]] [[Has vtext::456]] [[Has vtext::abc]] [[Has vtext::def]] [[Has vtext::1001]] [[Has vtext::42]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "special",
+ "about": "#0 (find all values for property `Has vtext`)",
+ "special-page": {
+ "page": "SearchByProperty",
+ "query-parameters": "",
+ "request-parameters": {
+ "limit": 6,
+ "property": "Has vtext"
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "<li>1001.*</li>",
+ "<li>123.*</li>",
+ "<li>42.*</li>",
+ "<li>456.*</li>",
+ "<li>abc.*</li>",
+ "<li>def.*</ul>"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLanguageCode": "en"
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/bootstrap.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/bootstrap.json
new file mode 100644
index 00000000..02e6c956
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/bootstrap.json
@@ -0,0 +1,53 @@
+{
+ "description": "Example bootstrap test case (see https://youtu.be/7fDKjPFaTaY)",
+ "setup": [
+ {
+ "namespace": "SMW_NS_PROPERTY",
+ "page": "Has example",
+ "contents": "[[Has type::Page]]"
+ },
+ {
+ "page": "Example/Bootstrap",
+ "contents": "[[Has example::Example123]]"
+ }
+ ],
+ "tests": [
+ {
+ "type": "parser",
+ "about": "#0 (page type annotation)",
+ "subject": "Example/Bootstrap",
+ "assert-store": {
+ "semantic-data": {
+ "strictPropertyValueMatch": false,
+ "propertyCount": 3,
+ "propertyKeys": [
+ "_SKEY",
+ "_MDAT",
+ "Has example"
+ ],
+ "propertyValues": [
+ "Example123"
+ ]
+ }
+ },
+ "assert-output": {
+ "to-contain": [
+ "Example123"
+ ]
+ }
+ }
+ ],
+ "settings": {
+ "wgContLang": "en",
+ "wgLang": "en",
+ "smwgNamespacesWithSemanticLinks": {
+ "NS_MAIN": true,
+ "SMW_NS_PROPERTY": true
+ }
+ },
+ "meta": {
+ "version": "2",
+ "is-incomplete": false,
+ "debug": false
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/readmeContentsBuilder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/readmeContentsBuilder.php
new file mode 100644
index 00000000..3e5fa40c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/readmeContentsBuilder.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+/**
+ * Build contents from a selected folder and replaces the content of the
+ * README.md from where the script was started.
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ReadmeContentsBuilder {
+
+ /**
+ * @var string
+ */
+ CONST REPLACE_START_MARKER = '<!-- Begin of generated contents by readmeContentsBuilder.php -->';
+ CONST REPLACE_END_MARKER = '<!-- End of generated contents by readmeContentsBuilder.php -->';
+
+ /**
+ * @var array
+ */
+ private $urlLocationMap = [
+ 'TestCases' => 'https://github.com/SemanticMediaWiki/SemanticMediaWiki/tree/master/tests/phpunit/Integration/JSONScript/TestCases'
+ ];
+
+ /**
+ * @since 2.4
+ */
+ public function run() {
+
+ $file = __DIR__ . '/README.md';
+ $dateTimeUtc = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
+
+ $replacement = self::REPLACE_START_MARKER . "\n\n";
+ $replacement .= $this->doGenerateContentFor( 'TestCases', __DIR__ . '/TestCases' );
+
+ $replacement .= "\n-- Last updated on " . $dateTimeUtc->format( 'Y-m-d' ) . " by `readmeContentsBuilder.php`". "\n";
+ $replacement .= "\n" . self::REPLACE_END_MARKER;
+
+ $contents = file_get_contents( $file );
+ $start = strpos( $contents, self::REPLACE_START_MARKER );
+ $length = strrpos( $contents, self::REPLACE_END_MARKER ) - $start + strlen( self::REPLACE_END_MARKER );
+
+ file_put_contents(
+ $file,
+ substr_replace( $contents, $replacement, $start, $length )
+ );
+ }
+
+ private function doGenerateContentFor( $title, $path ) {
+
+ $output = '';
+ $urlLocation = $this->urlLocationMap[$title];
+
+ $counter = 0;
+ $tests = 0;
+ $previousFirstKey = '';
+
+ foreach ( $this->findFilesFor( $path, 'json' ) as $key => $location ) {
+
+ if ( $previousFirstKey !== $key{0} ) {
+ $output .= "\n" . '### ' . ucfirst( $key{0} ). "\n";
+ }
+
+ $output .= '* [' . $key .'](' . $urlLocation . '/' . $key . ')';
+
+ $contents = json_decode( file_get_contents( $location ), true );
+
+ if ( $contents === null || json_last_error() !== JSON_ERROR_NONE ) {
+ continue;
+ }
+
+ if ( isset( $contents['description'] ) ) {
+ $output .= " " . $contents['description'];
+ }
+
+ if ( isset( $contents['tests'] ) ) {
+ $tests += count( $contents['tests'] );
+ }
+
+ $output .= "\n";
+ $counter++;
+ $previousFirstKey = $key{0};
+ }
+
+ return "## $title\n\n" . "Contains $counter files with a total of $tests tests:\n" . $output;
+ }
+
+ private function findFilesFor( $path, $extension ) {
+
+ $files = [];
+
+ $directoryIterator = new \RecursiveDirectoryIterator( $path );
+
+ foreach ( new \RecursiveIteratorIterator( $directoryIterator ) as $fileInfo ) {
+ if ( strtolower( substr( $fileInfo->getFilename(), -( strlen( $extension ) + 1 ) ) ) === ( '.' . $extension ) ) {
+ $files[$fileInfo->getFilename()] = $fileInfo->getPathname();
+ }
+ }
+
+ return $files;
+ }
+
+}
+
+$readmeContentsBuilder = new ReadmeContentsBuilder();
+$readmeContentsBuilder->run();
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/version2TestCaseConverter.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/version2TestCaseConverter.php
new file mode 100644
index 00000000..07744f63
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/JSONScript/version2TestCaseConverter.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW\Tests\Integration\JSONScript;
+
+/**
+ * @private
+ * @codeCoverageIgnore
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class Version2TestCaseConverter {
+
+ /**
+ * @var array
+ */
+ private $options = [];
+
+ /**
+ * @var string
+ */
+ private $contents = '';
+
+ /**
+ * @since 2.5
+ */
+ public function run() {
+
+ // http://php.net/manual/en/function.getopt.php
+ $this->options = getopt( '', [ "file::", "test" ] );
+
+ print "\nRunning Version2TestCaseConverter ..." . "\n\n";
+
+ foreach ( $this->findFilesFor( __DIR__ . '/Fixtures', 'json' ) as $file => $location ) {
+ $this->readAndConvertFile( $file, $location );
+ }
+ }
+
+ private function readAndConvertFile( $file, $location ) {
+
+ if ( isset( $this->options['file'] ) && $this->options['file'] !== $file ) {
+ return print "Skipping {$file} because it doesn't match " . $this->options['file'] . "\n";
+ }
+
+ $this->contents = json_decode( file_get_contents( $location ), true );
+
+ if ( isset( $this->contents['meta']['version'] ) && $this->contents['meta']['version'] === '2' ) {
+ // return print "Skipping {$file} because it has already been tagged with version 2.\n";
+ }
+
+ // $contents = $this->replaceSpaceIndent(
+ // $this->doConvertToVersion2()
+ // );
+
+ $contents = $this->replaceSpaceIndent(
+ $this->doConvertToVersion2Assert()
+ );
+
+ if ( !isset( $this->options['test'] ) ) {
+ file_put_contents( $location, $contents );
+ print "{$file} was converted to version 2.\n";
+ } else {
+ print $contents;
+ }
+ }
+
+ private function replaceSpaceIndent( $contents ) {
+
+ // Change the four-space indent to a tab indent
+ $contents = str_replace( "\n ", "\n\t", $contents );
+
+ while ( strpos( $contents, "\t " ) !== false ) {
+ $contents = str_replace( "\t ", "\t\t", $contents );
+ }
+
+ return $contents;
+ }
+
+ private function doConvertToVersion2Assert() {
+
+ $contents = $this->contents;
+
+ foreach ( $contents['tests'] as $key => $value ) {
+
+ if ( isset( $value['store'] ) ) {
+ $value['assert-store'] = $value['store'];
+ unset( $value['store'] );
+ }
+
+ if ( isset( $value['expected-output'] ) ) {
+ $value['assert-output'] = $value['expected-output'];
+ unset( $value['expected-output'] );
+ }
+
+ $contents['tests'][$key] = $value;
+ }
+
+ foreach ( $contents['setup'] as $key => $value ) {
+
+ if ( isset( $value['name'] ) ) {
+ $value['page'] = $value['name'];
+ unset( $value['name'] );
+ }
+
+ if ( isset( $value['contents'] ) ) {
+ $v = $value['contents'];
+ unset( $value['contents'] );
+ $value['contents'] = $v;
+ }
+
+ $contents['setup'][$key] = $value;
+ }
+
+ return json_encode( $contents, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ private function doConvertToVersion2() {
+
+ $setup = [];
+ $tests = [];
+
+ // Replace and accordance with the new Version 2 structure
+ $this->findAndReplaceEntity( 'properties', $setup );
+ $this->findAndReplaceEntity( 'subjects', $setup );
+
+ // Replace and accordance with the new Version 2 structure
+ $this->findAndReplaceTestCases( 'format-testcases', 'format', $tests );
+ $this->findAndReplaceTestCases( 'parser-testcases', 'parser', $tests );
+ $this->findAndReplaceTestCases( 'rdf-testcases', 'rdf', $tests );
+ $this->findAndReplaceTestCases( 'special-testcases', 'special', $tests );
+ $this->findAndReplaceTestCases( 'query-testcases', 'query', $tests );
+ $this->findAndReplaceTestCases( 'concept-testcases', 'concept', $tests );
+
+ // Reorder
+ $contents = [
+ 'description' => $this->contents['description'],
+ 'setup' => $setup,
+ 'beforeTest' => []
+ ];
+
+ if ( isset( $this->contents['maintenance-run'] ) ) {
+ $contents['beforeTest']['maintenance-run'] = $this->contents['maintenance-run'];
+ }
+
+ if ( isset( $this->contents['job-run'] ) ) {
+ $contents['beforeTest']['job-run'] = $this->contents['job-run'];
+ }
+
+ if ( $contents['beforeTest'] === [] ) {
+ unset( $contents['beforeTest'] );
+ }
+
+ $contents['tests'] = $tests;
+ $contents['settings'] = $this->contents['settings'];
+ $contents['meta'] = $this->contents['meta'];
+ $contents['meta']['version'] = '2';
+
+ return json_encode( $contents, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
+ }
+
+ private function findAndReplaceEntity( $name, &$entities ) {
+
+ if ( !isset( $this->contents[$name] ) || $this->contents[$name] === [] ) {
+ return;
+ }
+
+ foreach ( $this->contents[$name] as $values ) {
+
+ $entity = [];
+
+ if ( $name === 'properties' ) {
+ $entity['namespace'] = "SMW_NS_PROPERTY";
+ }
+
+ foreach ( $values as $key => $value ) {
+ $entity[$key] = $value;
+ }
+
+ $entities[] = $entity;
+ }
+ }
+
+ private function findAndReplaceTestCases( $name, $type, &$tests ) {
+
+ if ( !isset( $this->contents[$name] ) || $this->contents[$name] === [] ) {
+ return;
+ }
+
+ foreach ( $this->contents[$name] as $values ) {
+
+ $case = [];
+ $case['type'] = $type;
+
+ foreach ( $values as $key => $value ) {
+ $case[$key] = $value;
+ }
+
+ $tests[] = $case;
+ }
+ }
+
+ private function findFilesFor( $path, $extension ) {
+
+ $files = [];
+
+ $directoryIterator = new \RecursiveDirectoryIterator( $path );
+
+ foreach ( new \RecursiveIteratorIterator( $directoryIterator ) as $fileInfo ) {
+ if ( strtolower( substr( $fileInfo->getFilename(), -( strlen( $extension ) + 1 ) ) ) === ( '.' . $extension ) ) {
+ $files[$fileInfo->getFilename()] = $fileInfo->getPathname();
+ }
+ }
+
+ return $files;
+ }
+
+}
+
+$version2TestCaseConverter = new Version2TestCaseConverter();
+$version2TestCaseConverter->run();
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/en.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/en.json
new file mode 100644
index 00000000..e4c16798
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/en.json
@@ -0,0 +1,24 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": false,
+ "datatype":{
+ "labels": {},
+ "aliases": {}
+ },
+ "property":{
+ "labels": {},
+ "aliases": {}
+ },
+ "namespace": {
+ "labels": [],
+ "aliases": []
+ },
+ "date": {
+ "precision": [],
+ "format": [],
+ "months": [],
+ "days": []
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-fallback.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-fallback.json
new file mode 100644
index 00000000..414602d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-fallback.json
@@ -0,0 +1,14 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": false,
+ "datatype":{
+ "labels": {
+ "dataTypeLabels-fallback": "bar"
+ },
+ "aliases": {
+ "dataTypeAliases-fallback": "bar"
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-partial.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-partial.json
new file mode 100644
index 00000000..c69825d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/Fixtures/foo-partial.json
@@ -0,0 +1,11 @@
+{
+ "@metadata": {
+ "authors": []
+ },
+ "fallback_language": "foo-fallback",
+ "datatype": {
+ "labels": {
+ "dataTypeLabels-partial": "bar"
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/LanguageContentTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/LanguageContentTest.php
new file mode 100644
index 00000000..9e49a224
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/LanguageContentTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Tests\Integration\Lang;
+
+use SMW\Lang\Lang;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LanguageContent extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ Lang::clear();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider canonicalPropertyAliasesProvider
+ */
+ public function testGetCanonicalPropertyAliases( $languageCode, $canonicalMatch, $aliasMatch, $expected ) {
+
+ $lang = Lang::getInstance()->fetch(
+ $languageCode
+ );
+
+ $canonicalPropertyAliases = $lang->getCanonicalPropertyAliases();
+
+ $this->assertEquals(
+ $expected,
+ $canonicalPropertyAliases[$canonicalMatch]
+ );
+ }
+
+ /**
+ * @dataProvider canonicalPropertyAliasesProvider
+ */
+ public function testGetPropertyAliases( $languageCode, $canonicalMatch, $aliasMatch, $expected ) {
+
+ $lang = Lang::getInstance()->fetch(
+ $languageCode
+ );
+
+ $propertyAliases = $lang->getPropertyAliases();
+
+ $this->assertEquals(
+ $expected,
+ $propertyAliases[$aliasMatch]
+ );
+ }
+
+ /**
+ * @dataProvider canonicalPropertyLabelsProvider
+ */
+ public function testGetCanonicalPropertyLabels( $languageCode, $aliasMatch, $expected ) {
+
+ $lang = Lang::getInstance()->fetch(
+ $languageCode
+ );
+
+ $propertyLabels = $lang->getCanonicalPropertyLabels();
+
+ $this->assertEquals(
+ $expected,
+ $propertyLabels[$aliasMatch]
+ );
+ }
+
+ public function canonicalPropertyAliasesProvider() {
+
+ $provider[] = [
+ 'fr',
+ 'Query size',
+ 'Taille de la requête',
+ '_ASKSI'
+ ];
+
+ return $provider;
+ }
+
+ public function canonicalPropertyLabelsProvider() {
+
+ $provider[] = [
+ 'fr',
+ 'Boolean',
+ '_boo'
+ ];
+
+ $provider[] = [
+ 'en',
+ 'Float',
+ '_num'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/PartialLanguageFallbackTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/PartialLanguageFallbackTest.php
new file mode 100644
index 00000000..fd72660d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Lang/PartialLanguageFallbackTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Tests\Integration\Lang;
+
+use SMW\Lang\FallbackFinder;
+use SMW\Lang\JsonContentsFileReader;
+use SMW\Lang\Lang;
+use SMW\Lang\LanguageContents;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PartialLanguageFallback extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ JsonContentsFileReader::clear();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ JsonContentsFileReader::clear();
+
+ parent::tearDown();
+ }
+
+ public function testDeclarationsLoadedPartiallyFromFallback() {
+
+ $JsonContentsFileReader = new JsonContentsFileReader(
+ null,
+ __DIR__ . '/Fixtures'
+ );
+
+ $languageContents = new LanguageContents(
+ $JsonContentsFileReader,
+ new FallbackFinder( $JsonContentsFileReader )
+ );
+
+ $lang = new Lang(
+ $languageContents
+ );
+
+ $lang = $lang->fetch( 'foo-partial' );
+
+ // Loaded from foo-partial.json
+ $this->assertEquals(
+ [
+ 'dataTypeLabels-partial' => 'bar'
+ ],
+ $lang->getDatatypeLabels()
+ );
+
+ // foo-partial.json doesn't contain a `dataTypeAliases` declaration and is
+ // only available in its fallback (foo-fallback.json)
+ $this->assertEquals(
+ [
+ 'dataTypeAliases-fallback' => 'bar'
+ ],
+ $lang->getDatatypeAliases()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/Fixtures/test-import-19.7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/Fixtures/test-import-19.7.xml
new file mode 100644
index 00000000..1cd60afe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/Fixtures/test-import-19.7.xml
@@ -0,0 +1,270 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:Has annotation uri</title>
+ <ns>102</ns>
+ <id>503</id>
+ <sha1>ifvt9xhpsfkv04ehcnhpw3i88s0fsb8</sha1>
+ <revision>
+ <id>1342</id>
+ <timestamp>2014-03-29T16:51:25Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="53">[[Has type::Annotation URI]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has boolean</title>
+ <ns>102</ns>
+ <id>502</id>
+ <sha1>l3b3eelfib1em61uurletnc4kb1xlzh</sha1>
+ <revision>
+ <id>1338</id>
+ <timestamp>2014-03-29T16:47:13Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Boolean]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="46">[[Has type::Boolean]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has date</title>
+ <ns>102</ns>
+ <id>14</id>
+ <sha1>473nf5knraq99k8zsrg6z7o3812tu61</sha1>
+ <revision>
+ <id>1330</id>
+ <timestamp>2014-03-29T16:38:59Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Date]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has email</title>
+ <ns>102</ns>
+ <id>500</id>
+ <sha1>9puenjhr9dntyg9obgtwhvaenn98rav</sha1>
+ <revision>
+ <id>1334</id>
+ <timestamp>2014-03-29T16:42:38Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Email]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="44">[[Has type::Email]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has number</title>
+ <ns>102</ns>
+ <id>494</id>
+ <sha1>9fqf0pft61g8qqb8qtz1i5ayr1rmu30</sha1>
+ <revision>
+ <id>1321</id>
+ <timestamp>2014-03-29T16:30:26Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Number]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="45">[[Has type::Number]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has page</title>
+ <ns>102</ns>
+ <id>489</id>
+ <sha1>4sq0eljhm3yty9cifkvel3mv646ag0y</sha1>
+ <revision>
+ <id>1319</id>
+ <timestamp>2014-03-29T16:29:46Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Page]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has quantity</title>
+ <ns>102</ns>
+ <id>495</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1323</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Quantity]] [[Category:Lorem ipsum]]
+
+* [[Corresponds to::1 km²]]
+* [[Corresponds to::0.38610 sq mi]]
+* [[Corresponds to::1000 m²]]
+* [[Corresponds to::247.1054 acre]]
+* [[Corresponds to::988.4215 rood]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has temperature</title>
+ <ns>102</ns>
+ <id>501</id>
+ <sha1>925639l5wc4pwdmufjgza10rstpb2oa</sha1>
+ <revision>
+ <id>1336</id>
+ <timestamp>2014-03-29T16:45:35Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Temperature]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="50">[[Has type::Temperature]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has text</title>
+ <ns>102</ns>
+ <id>491</id>
+ <sha1>qi7oxp4101tdesdm3vavpgpfn1qe68u</sha1>
+ <revision>
+ <id>1320</id>
+ <timestamp>2014-03-29T16:30:07Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Text]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has Url</title>
+ <ns>102</ns>
+ <id>499</id>
+ <sha1>1fw7q2wts7v11q1z6d31g89zorhco0i</sha1>
+ <revision>
+ <id>1332</id>
+ <timestamp>2014-03-29T16:40:28Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::URL]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="42">[[Has type::URL]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem ipsum</title>
+ <ns>0</ns>
+ <id>493</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1358</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">[[Has text::Lorem ipsum dolor sit amet consectetuer Maecenas adipiscing Pellentesque id sem]]. [[Has page::Elit Aliquam urna interdum]] morbi faucibus id tellus ipsum semper wisi. [[Has page::Platea enim hendrerit]] pellentesque consectetuer scelerisque Sed est felis felis quis. Auctor Proin In dolor id et ipsum vel at vitae ut. Praesent elit convallis Praesent aliquet pellentesque vel dolor pellentesque lacinia vitae. At tortor lacus Sed In interdum pulvinar et.
+
+[[Has number::1001]] [[Has quantity::10.25 km²]] [[Has date::1 Jan 2014]] [[Has Url::http://loremipsum.org/]] [[Has annotation uri::http://loremipsum.org/foaf.rdf]] [[Has email::Lorem@ipsum.org]] [[Has temperature::100 °C]] [[Has boolean::true]]
+
+{{#subobject:|Has number=1111|Has quantity=25 sqmi|Has date=January 4, 2010 7:00 pm|Has Url=http://example.org/some/|Has annotation uri=http://example.org/foaf.rdf|Has email=Lorem@example.org|Has temperature=100 °F|Has boolean=false}}
+
+[[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Platea enim hendrerit</title>
+ <ns>0</ns>
+ <id>498</id>
+ <sha1>4vp3ppszu168lv9f0tc9eaefvgbkv8a</sha1>
+ <revision>
+ <id>1327</id>
+ <timestamp>2014-03-29T16:36:22Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="24">[[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Elit Aliquam urna interdum</title>
+ <ns>0</ns>
+ <id>497</id>
+ <sha1>c5qjdsfni2zwsd95h59jiksolm1vjiv</sha1>
+ <revision>
+ <id>1361</id>
+ <timestamp>2014-04-04T23:00:14Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="945">[[Lorem ipsum]] dolor sit amet consectetuer Maecenas adipiscing Pellentesque id sem. [[Has text::Elit Aliquam urna interdum]] morbi faucibus id tellus ipsum semper wisi. [[Has page::Platea enim hendrerit]] pellentesque consectetuer scelerisque Sed est felis felis quis. Auctor Proin In dolor id et ipsum vel at vitae ut. Praesent elit convallis Praesent aliquet pellentesque vel dolor pellentesque lacinia vitae. At tortor lacus Sed In interdum pulvinar et.
+
+[[Has number::1001]] [[Has quantity::10.25 km²]] [[Has date::1 Jan 2014]] [[Has Url::http://loremipsum.org/]] [[Has annotation uri::http://loremipsum.org/foaf.rdf]] [[Has email::Lorem@ipsum.org]] [[Has temperature::100 °C]] [[Has boolean::true]]
+
+{{#set:|Has number=9999|Has quantity=0.25 m²|Has date=22 Jan 1714|Has Url=http://example.org/|Has annotation uri=http://example.org/foaf.rdf|Has email=Lorem@example.org|Has temperature=20 K|Has boolean=false}}
+
+[[Category: Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Lorem ipsum</title>
+ <ns>14</ns>
+ <id>496</id>
+ <sha1>sir97j6uzt9ev2uyhaz1aj4i3spogih</sha1>
+ <revision>
+ <id>1355</id>
+ <timestamp>2014-04-04T22:29:18Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="17">[[Category:Main]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/PopulateHashFieldTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/PopulateHashFieldTest.php
new file mode 100644
index 00000000..a75828a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/PopulateHashFieldTest.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\Tests\Integration\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.1
+ *
+ * @author mwjames
+ */
+class PopulateHashField extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+ private $runnerFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = TestEnvironment::getUtilityFactory()->newRunnerFactory();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testRun() {
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner(
+ 'SMW\Maintenance\PopulateHashField'
+ );
+
+ $maintenanceRunner->setQuiet();
+
+ $this->assertTrue(
+ $maintenanceRunner->run()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/RemoveDuplicateEntitiesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/RemoveDuplicateEntitiesTest.php
new file mode 100644
index 00000000..17238a04
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/RemoveDuplicateEntitiesTest.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\Tests\Integration\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RemoveDuplicateEntitiesTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+ private $runnerFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = TestEnvironment::getUtilityFactory()->newRunnerFactory();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testRun() {
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner(
+ 'SMW\Maintenance\RemoveDuplicateEntities'
+ );
+
+ $maintenanceRunner->setQuiet();
+
+ $this->assertTrue(
+ $maintenanceRunner->run()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/SetupStoreMaintenanceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/SetupStoreMaintenanceTest.php
new file mode 100644
index 00000000..9cc9ce0b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Maintenance/SetupStoreMaintenanceTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SetupStoreMaintenanceTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $spyMessageReporter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = $this->testEnvironment->getUtilityFactory()->newRunnerFactory();
+ $this->titleValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newTitleValidator();
+ $this->spyMessageReporter = $this->testEnvironment->getUtilityFactory()->newSpyMessageReporter();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/Fixtures/test-import-19.7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->flushPages( $this->importedTitles );
+ parent::tearDown();
+ }
+
+ public function testSetupStore() {
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\SetupStore' );
+
+ $maintenanceRunner->setMessageReporter(
+ $this->spyMessageReporter
+ );
+
+ $maintenanceRunner->setQuiet();
+ $maintenanceRunner->run();
+
+ $this->assertContains(
+ 'Database initialized completed',
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/ApiBrowseBySubjectDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/ApiBrowseBySubjectDBIntegrationTest.php
new file mode 100644
index 00000000..ab0fc763
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/ApiBrowseBySubjectDBIntegrationTest.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use SMW\DataValueFactory;
+use SMW\MediaWiki\Api\BrowseBySubject;
+use SMW\SerializerFactory;
+use SMW\Subobject;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\MwApiFactory;
+use SMW\Tests\Utils\SemanticDataFactory;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ApiBrowseBySubjectDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $apiFactory;
+ private $dataValueFactory;
+ private $serializerFactory;
+ private $semanticDataFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->apiFactory = new MwApiFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->serializerFactory = new SerializerFactory();
+ $this->semanticDataFactory = new SemanticDataFactory();
+ }
+
+ public function testResultDataForEmptySemanticData() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $resultData = $this->newBrowseBySubject( __METHOD__ )->getResultData();
+
+ $this->assertInternalType(
+ 'array',
+ $resultData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $this->serializerFactory->getDeserializerFor( $resultData['query'] )->deserialize( $resultData['query'] )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $this->newBrowseBySubject( __METHOD__, true )->getResultData()
+ );
+ }
+
+ public function testResultDataForSingleSemanticDataValueAssignment() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByText( __METHOD__, 'Bar' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $resultData = $this->newBrowseBySubject( __METHOD__ )->getResultData();
+
+ $this->assertInternalType(
+ 'array',
+ $resultData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $this->serializerFactory->getDeserializerFor( $resultData['query'] )->deserialize( $resultData['query'] )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $this->newBrowseBySubject( __METHOD__, true )->getResultData()
+ );
+ }
+
+ public function testResultDataFoSubobjectExtendedSemanticData() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByText( __METHOD__, 'Bar' )
+ );
+
+ $subobject = new Subobject( $semanticData->getSubject()->getTitle() );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( __METHOD__, 'Bam' )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $resultData = $this->newBrowseBySubject( __METHOD__ )->getResultData();
+
+ $this->assertInternalType(
+ 'array',
+ $resultData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $this->serializerFactory->getDeserializerFor( $resultData['query'] )->deserialize( $resultData['query'] )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $this->newBrowseBySubject( __METHOD__, true )->getResultData()
+ );
+ }
+
+ private function newBrowseBySubject( $subject, $asRawMode = false ) {
+
+ $instance = new BrowseBySubject(
+ $this->apiFactory->newApiMain( [ 'subject' => $subject ] ),
+ 'browsebysubject'
+ );
+
+ // Went away with 1.26/1.27
+ if ( function_exists( 'setRawMode' ) && $asRawMode ) {
+ $instance->getMain()->getResult()->setRawMode();
+ }
+
+ $instance->execute();
+
+ // MW 1.25
+ return method_exists( $instance, 'getResult' ) ? $instance->getResult() : $instance;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/FileUploadIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/FileUploadIntegrationTest.php
new file mode 100644
index 00000000..86360952
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/FileUploadIntegrationTest.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Hooks;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Tests\MwDBaseUnitTestCase;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FileUploadIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $mwHooksHandler;
+ private $fixturesFileProvider;
+ private $semanticDataValidator;
+ private $pageEditor;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+
+ $this->fixturesFileProvider = $utilityFactory->newFixturesFactory()->newFixturesFileProvider();
+ $this->semanticDataValidator = $utilityFactory->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageEditor = $utilityFactory->newPageEditor();
+
+ $this->mwHooksHandler = $utilityFactory->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->testEnvironment->withConfiguration( [
+ 'smwgPageSpecialProperties' => [ '_MEDIA', '_MIME' ],
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true, NS_FILE => true ],
+ 'smwgMainCacheType' => 'hash',
+ ] );
+
+ $this->testEnvironment->withConfiguration( [
+ 'wgEnableUploads' => true,
+ 'wgFileExtensions' => [ 'txt' ],
+ 'wgVerifyMimeType' => true
+ ] );
+
+ $this->mwHooksHandler->register(
+ 'FileUpload',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'FileUpload' )
+ );
+
+ $this->mwHooksHandler->register(
+ 'InternalParseBeforeLinks',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'InternalParseBeforeLinks' )
+ );
+
+ $this->mwHooksHandler->register(
+ 'LinksUpdateConstructed',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'LinksUpdateConstructed' )
+ );
+
+ $this->getStore()->setup( false );
+ }
+
+ protected function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->testEnvironment->tearDown();
+
+ parent::tearDown();
+ }
+
+ public function testFileUploadForDummyTextFile() {
+ Localizer::getInstance()->clear();
+
+ $subject = new DIWikiPage( 'Foo.txt', NS_FILE );
+ $fileNS = Localizer::getInstance()->getNamespaceTextById( NS_FILE );
+
+ $dummyTextFile = $this->fixturesFileProvider->newUploadForDummyTextFile( 'Foo.txt' );
+
+ $this->assertTrue(
+ $dummyTextFile->doUpload( '[[HasFile::File:Foo.txt]]' )
+ );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $expected = [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ 'HasFile', '_MEDIA', '_MIME', '_SKEY' ],
+ 'propertyValues' => [ "$fileNS:Foo.txt", 'TEXT', 'text/plain', 'Foo.txt' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+ }
+
+ /**
+ * @depends testFileUploadForDummyTextFile
+ */
+ public function testReUploadDummyTextFileToEditFilePage() {
+
+ $subject = new DIWikiPage( 'Foo.txt', NS_FILE );
+
+ $dummyTextFile = $this->fixturesFileProvider->newUploadForDummyTextFile( 'Foo.txt' );
+ $dummyTextFile->doUpload();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->pageEditor
+ ->editPage( $subject->getTitle() )
+ ->doEdit( '[[Ichi::Maru|Kyū]]' );
+
+ // File page content is kept from the initial upload
+ $expected = [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_MEDIA', '_MIME', '_SKEY', 'Ichi' ],
+ 'propertyValues' => [ 'TEXT', 'text/plain', 'Foo.txt', 'Maru' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/InternalParseBeforeLinksIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/InternalParseBeforeLinksIntegrationTest.php
new file mode 100644
index 00000000..402a1230
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/InternalParseBeforeLinksIntegrationTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databaseless
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class InternalParseBeforeLinksIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $mwHooksHandler;
+ private $parserAfterTidyHook;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $this->mwHooksHandler = UtilityFactory::getInstance()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->mwHooksHandler->register(
+ 'InternalParseBeforeLinks',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'InternalParseBeforeLinks' )
+ );
+ }
+
+ protected function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testNonParseForInvokedMessageParse() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData->expects( $this->never() )
+ ->method( 'getSemanticData' );
+
+ $this->applicationFactory->registerObject( 'ParserData', $parserData );
+
+ wfMessage( 'properties' )->parse();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserAfterTidyIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserAfterTidyIntegrationTest.php
new file mode 100644
index 00000000..d9810415
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserAfterTidyIntegrationTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ParserAfterTidyIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $mwHooksHandler;
+ private $parserAfterTidyHook;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $this->mwHooksHandler = UtilityFactory::getInstance()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->mwHooksHandler->register(
+ 'ParserAfterTidy',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'ParserAfterTidy' )
+ );
+ }
+
+ protected function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testNonParseForInvokedMessageParse() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData->expects( $this->never() )
+ ->method( 'getSemanticData' );
+
+ $this->applicationFactory->registerObject( 'ParserData', $parserData );
+
+ wfMessage( 'properties' )->parse();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserFirstCallInitIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserFirstCallInitIntegrationTest.php
new file mode 100644
index 00000000..faaf1c9e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/ParserFirstCallInitIntegrationTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Hooks;
+
+use SMW\ContentParser;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserFirstCallInitIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $mwHooksHandler;
+ private $parserFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgMainCacheType' => CACHE_NONE
+ ] );
+
+ $this->mwHooksHandler = $this->testEnvironment->getUtilityFactory()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->parserFactory = $this->testEnvironment->getUtilityFactory()->newParserFactory();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->mwHooksHandler->register(
+ 'ParserFirstCallInit',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'ParserFirstCallInit' )
+ );
+ }
+
+ protected function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->testEnvironment->tearDown();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider textToParseProvider
+ */
+ public function testParseWithParserFunctionEnabled( $parserName, $text ) {
+
+ $expectedNullOutputFor = [
+ 'concept',
+ 'declare'
+ ];
+
+ $title = Title::newFromText( __METHOD__ );
+ $parser = $this->parserFactory->newFromTitle( $title );
+
+ $this->testEnvironment->addConfiguration( 'smwgQEnabled', true );
+
+ $instance = new ContentParser( $title, $parser );
+ $instance->parse( $text );
+
+ if ( in_array( $parserName, $expectedNullOutputFor ) ) {
+ return $this->assertNull(
+ $this->findSemanticataFromOutput( $instance->getOutput() )
+ );
+ }
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $this->findSemanticataFromOutput( $instance->getOutput() )
+ );
+ }
+
+ /**
+ * @dataProvider textToParseProvider
+ */
+ public function testParseWithParserFunctionDisabled( $parserName, $text ) {
+
+ $expectedNullOutputFor = [
+ 'concept',
+ 'declare',
+ 'ask',
+ 'show'
+ ];
+
+ $title = Title::newFromText( __METHOD__ );
+ $parser = $this->parserFactory->newFromTitle( $title );
+
+ $this->testEnvironment->addConfiguration( 'smwgQEnabled', false );
+
+ $instance = new ContentParser( $title, $parser );
+ $instance->parse( $text );
+
+ if ( in_array( $parserName, $expectedNullOutputFor ) ) {
+ return $this->assertNull(
+ $this->findSemanticataFromOutput( $instance->getOutput() )
+ );
+ }
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $this->findSemanticataFromOutput( $instance->getOutput() )
+ );
+ }
+
+ public function textToParseProvider() {
+
+ $provider = [];
+
+ #0 ask
+ $provider[] = [
+ 'ask',
+ '{{#ask: [[Modification date::+]]|limit=1}}'
+ ];
+
+ #1 show
+ $provider[] = [
+ 'show',
+ '{{#show: [[Foo]]|limit=1}}'
+ ];
+
+ #2 subobject
+ $provider[] = [
+ 'subobject',
+ '{{#subobject:|foo=bar|lila=lula,linda,luna|+sep=,}}'
+ ];
+
+ #3 set
+ $provider[] = [
+ 'set',
+ '{{#set:|foo=bar|lila=lula,linda,luna|+sep=,}}'
+ ];
+
+ #4 set_recurring_event
+ $provider[] = [
+ 'set_recurring_event',
+ '{{#set_recurring_event:some more tests|property=has date|' .
+ 'has title=Some recurring title|title2|has group=Events123|Events456|start=June 8, 2010|end=June 8, 2011|' .
+ 'unit=week|period=1|limit=10|duration=7200|include=March 16, 2010;March 23, 2010|+sep=;|' .
+ 'exclude=March 15, 2010;March 22, 2010|+sep=;}}'
+ ];
+
+ #5 declare
+ $provider[] = [
+ 'declare',
+ '{{#declare:population=Foo}}'
+ ];
+
+ #6 concept
+ $provider[] = [
+ 'concept',
+ '{{#concept:[[Modification date::+]]|Foo}}'
+ ];
+
+ return $provider;
+ }
+
+ private function findSemanticataFromOutput( $parserOutput ) {
+
+ if ( method_exists( $parserOutput, 'getExtensionData' ) ) {
+ return $parserOutput->getExtensionData( 'smwdata' );
+ }
+
+ return isset( $parserOutput->mSMWData ) ? $parserOutput->mSMWData : null;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/TitleMoveCompleteIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/TitleMoveCompleteIntegrationTest.php
new file mode 100644
index 00000000..37c80d3d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Hooks/TitleMoveCompleteIntegrationTest.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+use Title;
+use WikiPage;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TitleMoveCompleteIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $mwHooksHandler;
+ private $queryResultValidator;
+ private $applicationFactory;
+ private $toBeDeleted = [];
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->queryResultValidator = $utilityFactory->newValidatorFactory()->newQueryResultValidator();
+
+ $this->mwHooksHandler = $utilityFactory->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->mwHooksHandler->register(
+ 'TitleMoveComplete',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'TitleMoveComplete' )
+ );
+
+ $this->pageCreator = $utilityFactory->newPageCreator();
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgEnabledDeferredUpdate',
+ false
+ );
+ }
+
+ protected function tearDown() {
+
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->testEnvironment->tearDown();
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->toBeDeleted );
+
+ parent::tearDown();
+ }
+
+ public function testPageMoveWithCreationOfRedirectTarget() {
+
+ $oldTitle = Title::newFromText( __METHOD__ . '-old' );
+ $expectedNewTitle = Title::newFromText( __METHOD__ . '-new' );
+
+ $this->assertNull(
+ WikiPage::factory( $expectedNewTitle )->getRevision()
+ );
+
+ $this->pageCreator
+ ->createPage( $oldTitle );
+
+ $result = $this->pageCreator
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $expectedNewTitle, false, 'test', true );
+ $this->assertTrue( $result );
+
+ $this->assertNotNull(
+ WikiPage::factory( $oldTitle )->getRevision()
+ );
+
+ $this->assertNotNull(
+ WikiPage::factory( $expectedNewTitle )->getRevision()
+ );
+
+ $this->toBeDeleted = [
+ $oldTitle,
+ $expectedNewTitle
+ ];
+ }
+
+ public function testPageMoveWithRemovalOfOldPage() {
+
+ // Further hooks required to ensure in-text annotations can be used for queries
+ $this->mwHooksHandler->register(
+ 'InternalParseBeforeLinks',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'InternalParseBeforeLinks' )
+ );
+
+ $this->mwHooksHandler->register(
+ 'LinksUpdateConstructed',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'LinksUpdateConstructed' )
+ );
+
+ $title = Title::newFromText( __METHOD__ . '-old' );
+ $expectedNewTitle = Title::newFromText( __METHOD__ . '-new' );
+
+ $this->assertNull(
+ WikiPage::factory( $expectedNewTitle )->getRevision()
+ );
+
+ $this->pageCreator
+ ->createPage( $title )
+ ->doEdit( '[[Has function hook test::PageCompleteMove]]' );
+
+ $this->pageCreator
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $expectedNewTitle, false, 'test', false );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertNull(
+ WikiPage::factory( $title )->getRevision()
+ );
+
+ $this->assertNotNull(
+ WikiPage::factory( $expectedNewTitle )->getRevision()
+ );
+
+ /**
+ * @query {{#ask: [[Has function hook test::PageCompleteMove]] }}
+ */
+ $description = new SomeProperty(
+ DIProperty::newFromUserLabel( 'Has function hook test' ),
+ new ValueDescription( new DIWikiPage( 'PageCompleteMove', 0 ), null, SMW_CMP_EQ )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ true
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ // #566
+ $this->assertCount(
+ 1,
+ $queryResult->getResults()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ [ DIWikiPage::newFromTitle( $expectedNewTitle ) ],
+ $queryResult
+ );
+
+ $this->toBeDeleted = [
+ $title,
+ $expectedNewTitle
+ ];
+ }
+
+ public function testPredefinedPropertyPageIsNotMovable() {
+
+ $this->mwHooksHandler->register(
+ 'TitleIsMovable',
+ $this->mwHooksHandler->getHookRegistry()->getHandlerFor( 'TitleIsMovable' )
+ );
+
+ $title = Title::newFromText( 'Modification date', SMW_NS_PROPERTY );
+ $expectedNewTitle = Title::newFromText( __METHOD__, SMW_NS_PROPERTY );
+
+ $this->pageCreator
+ ->createPage( $title );
+
+ $this->pageCreator
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $expectedNewTitle, false, 'test', true );
+
+ $this->assertNotNull(
+ WikiPage::factory( $title )->getRevision()
+ );
+
+ $this->assertNull(
+ WikiPage::factory( $expectedNewTitle )->getRevision()
+ );
+
+ $this->toBeDeleted = [
+ $title,
+ $expectedNewTitle
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/CategoryInstanceAndCategoryHierarchyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/CategoryInstanceAndCategoryHierarchyTest.php
new file mode 100644
index 00000000..7134e273
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/CategoryInstanceAndCategoryHierarchyTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import;
+
+use SMW\DIProperty;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\ByPageSemanticDataFinder;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class CategoryInstanceAndCategoryHierarchyTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/'. 'Fixtures/' . 'CategoryInstanceAndCategoryHierarchyTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testCategoryHierarchies() {
+
+ $this->importedTitles = [
+ 'Category:Regression test',
+ 'Category:Regression test category',
+ 'Category:Regression test sub category',
+ 'Category:Regression test sub sub category',
+ 'CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage',
+ 'CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage/WithSubSubpage',
+ 'CategoryInstanceAndCategoryHierarchyRegressionTest'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $title = Title::newFromText( 'CategoryInstanceAndCategoryHierarchyRegressionTest' );
+
+ $expectedCategoryAsWikiValue = [
+ 'property' => new DIProperty( '_INST' ),
+ 'propertyValues' => [
+ 'Regression test',
+ 'Regression test category',
+ 'Regression test sub category',
+ 'Regression test sub sub category',
+ 'Category regression test'
+ ]
+ ];
+
+ $semanticDataFinder = new ByPageSemanticDataFinder;
+ $semanticDataFinder->setTitle( $title )->setStore( $this->getStore() );
+
+ $semanticDataBatches = [
+ $semanticDataFinder->fetchFromOutput(),
+ $semanticDataFinder->fetchFromStore()
+ ];
+
+ foreach ( $semanticDataBatches as $semanticData ) {
+
+ $this->semanticDataValidator->assertThatCategoriesAreSet(
+ $expectedCategoryAsWikiValue,
+ $semanticData
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/CategoryInstanceAndCategoryHierarchyTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/CategoryInstanceAndCategoryHierarchyTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..45c32c47
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/CategoryInstanceAndCategoryHierarchyTest-Mw-1-19-7.xml
@@ -0,0 +1,175 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Category:Regression test</title>
+ <ns>14</ns>
+ <id>420</id>
+ <sha1>4s17yzvoj5kq6dzdmgnw7t1ukpr4tkz</sha1>
+ <revision>
+ <id>1035</id>
+ <timestamp>2014-01-31T07:30:44Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>1 revision</comment>
+ <text xml:space="preserve" bytes="24">Regression test category</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Regression test category</title>
+ <ns>14</ns>
+ <id>436</id>
+ <sha1>jbu6rw2ii5g9p96hghe03cxv0jpz5cn</sha1>
+ <revision>
+ <id>1052</id>
+ <timestamp>2014-01-31T07:38:13Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Category:Regression test]]&quot;</comment>
+ <text xml:space="preserve" bytes="28">[[Category:Regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Regression test sub category</title>
+ <ns>14</ns>
+ <id>431</id>
+ <sha1>03rwhvehzgfof9pek6jnnrneqoy2iwm</sha1>
+ <revision>
+ <id>1051</id>
+ <timestamp>2014-01-31T07:37:41Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="37">[[Category:Regression test category]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Regression test sub sub category</title>
+ <ns>14</ns>
+ <id>432</id>
+ <sha1>4gttxr4kimh7dg13xhqx5fq85922n4g</sha1>
+ <revision>
+ <id>1039</id>
+ <timestamp>2014-01-31T07:30:45Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>1 revision</comment>
+ <text xml:space="preserve" bytes="41">[[Category:Regression test sub category]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage</title>
+ <ns>0</ns>
+ <id>434</id>
+ <sha1>lyd8hlp26so4fwzfqr2lofdz3n6wihy</sha1>
+ <revision>
+ <id>1047</id>
+ <timestamp>2014-01-31T07:32:12Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>Tester moved page [[CategoryAndCategoryHierarchyRegressionTest/WithSubpage]] to [[CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage]] without leaving a redirect</comment>
+ <text xml:space="preserve" bytes="79">[[Category:Regression test sub category]] [[Category:Category regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage/WithSubSubpage</title>
+ <ns>0</ns>
+ <id>435</id>
+ <sha1>55b8se56m3zu8twdemk2m5ii2o7p34w</sha1>
+ <revision>
+ <id>1048</id>
+ <timestamp>2014-01-31T07:32:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>Tester moved page [[CategoryAndCategoryHierarchyRegressionTest/WithSubpage/WithSubSubpage]] to [[CategoryInstanceAndCategoryHierarchyRegressionTest/WithSubpage/WithSubSubpage]] without leaving a redirect</comment>
+ <text xml:space="preserve" bytes="83">[[Category:Regression test sub sub category]] [[Category:Category regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>CategoryInstanceAndCategoryHierarchyRegressionTest</title>
+ <ns>0</ns>
+ <id>433</id>
+ <sha1>koevx105y5kuodft74oom7bfisa2w5r</sha1>
+ <revision>
+ <id>1055</id>
+ <timestamp>2014-01-31T07:39:55Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="1002">== Synopsis ==
+A regression test for category, sub-category and category hierarchies. A category hierarchy is specified as a collection of subordinate categories into a superordinate category. [https://semantic-mediawiki.org/wiki/Help:Special_property_Subcategory_of Subcategory of] classifies subordinate categories.
+
+== Usage ==
+[[:Category:Regression test]], [[:Category:Regression test category]], [[:Category:Regression test sub category]], [[:Category:Regression test sub sub category]]
+
+== Queries ==
+=== Search for specific a specific category ===
+{{#ask: [[Category:Regression test category]]
+ |?Category
+ |format=broadtable
+}}
+
+=== Wildcard search on 'Subcategory of' ===
+{{#ask: [[Subcategory of::+]]
+ |?Subcategory of
+ |format=broadtable
+}}
+
+=== Search for specific 'Subcategory of' ===
+{{#ask: [[Subcategory of::Regression test sub category]]
+ |?Subcategory of
+ |format=broadtable
+}}
+
+[[Category:Regression test]] [[Category:Regression test category]] [[Category:Category regression test]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/GenericLoremIpsumTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/GenericLoremIpsumTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..1cd60afe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/GenericLoremIpsumTest-Mw-1-19-7.xml
@@ -0,0 +1,270 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:Has annotation uri</title>
+ <ns>102</ns>
+ <id>503</id>
+ <sha1>ifvt9xhpsfkv04ehcnhpw3i88s0fsb8</sha1>
+ <revision>
+ <id>1342</id>
+ <timestamp>2014-03-29T16:51:25Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="53">[[Has type::Annotation URI]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has boolean</title>
+ <ns>102</ns>
+ <id>502</id>
+ <sha1>l3b3eelfib1em61uurletnc4kb1xlzh</sha1>
+ <revision>
+ <id>1338</id>
+ <timestamp>2014-03-29T16:47:13Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Boolean]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="46">[[Has type::Boolean]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has date</title>
+ <ns>102</ns>
+ <id>14</id>
+ <sha1>473nf5knraq99k8zsrg6z7o3812tu61</sha1>
+ <revision>
+ <id>1330</id>
+ <timestamp>2014-03-29T16:38:59Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Date]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has email</title>
+ <ns>102</ns>
+ <id>500</id>
+ <sha1>9puenjhr9dntyg9obgtwhvaenn98rav</sha1>
+ <revision>
+ <id>1334</id>
+ <timestamp>2014-03-29T16:42:38Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Email]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="44">[[Has type::Email]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has number</title>
+ <ns>102</ns>
+ <id>494</id>
+ <sha1>9fqf0pft61g8qqb8qtz1i5ayr1rmu30</sha1>
+ <revision>
+ <id>1321</id>
+ <timestamp>2014-03-29T16:30:26Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Number]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="45">[[Has type::Number]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has page</title>
+ <ns>102</ns>
+ <id>489</id>
+ <sha1>4sq0eljhm3yty9cifkvel3mv646ag0y</sha1>
+ <revision>
+ <id>1319</id>
+ <timestamp>2014-03-29T16:29:46Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Page]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has quantity</title>
+ <ns>102</ns>
+ <id>495</id>
+ <sha1>1ulyevjgdofvfrp7b6b17n7p0p6oy4w</sha1>
+ <revision>
+ <id>1323</id>
+ <timestamp>2014-03-29T16:34:24Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="219">[[Has type::Quantity]] [[Category:Lorem ipsum]]
+
+* [[Corresponds to::1 km²]]
+* [[Corresponds to::0.38610 sq mi]]
+* [[Corresponds to::1000 m²]]
+* [[Corresponds to::247.1054 acre]]
+* [[Corresponds to::988.4215 rood]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has temperature</title>
+ <ns>102</ns>
+ <id>501</id>
+ <sha1>925639l5wc4pwdmufjgza10rstpb2oa</sha1>
+ <revision>
+ <id>1336</id>
+ <timestamp>2014-03-29T16:45:35Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Temperature]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="50">[[Has type::Temperature]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has text</title>
+ <ns>102</ns>
+ <id>491</id>
+ <sha1>qi7oxp4101tdesdm3vavpgpfn1qe68u</sha1>
+ <revision>
+ <id>1320</id>
+ <timestamp>2014-03-29T16:30:07Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="43">[[Has type::Text]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has Url</title>
+ <ns>102</ns>
+ <id>499</id>
+ <sha1>1fw7q2wts7v11q1z6d31g89zorhco0i</sha1>
+ <revision>
+ <id>1332</id>
+ <timestamp>2014-03-29T16:40:28Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::URL]] [[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="42">[[Has type::URL]] [[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Lorem ipsum</title>
+ <ns>0</ns>
+ <id>493</id>
+ <sha1>94lztkh4kgb0mvjr87iyjfq4iv7ltlh</sha1>
+ <revision>
+ <id>1358</id>
+ <timestamp>2014-04-04T22:55:04Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="979">[[Has text::Lorem ipsum dolor sit amet consectetuer Maecenas adipiscing Pellentesque id sem]]. [[Has page::Elit Aliquam urna interdum]] morbi faucibus id tellus ipsum semper wisi. [[Has page::Platea enim hendrerit]] pellentesque consectetuer scelerisque Sed est felis felis quis. Auctor Proin In dolor id et ipsum vel at vitae ut. Praesent elit convallis Praesent aliquet pellentesque vel dolor pellentesque lacinia vitae. At tortor lacus Sed In interdum pulvinar et.
+
+[[Has number::1001]] [[Has quantity::10.25 km²]] [[Has date::1 Jan 2014]] [[Has Url::http://loremipsum.org/]] [[Has annotation uri::http://loremipsum.org/foaf.rdf]] [[Has email::Lorem@ipsum.org]] [[Has temperature::100 °C]] [[Has boolean::true]]
+
+{{#subobject:|Has number=1111|Has quantity=25 sqmi|Has date=January 4, 2010 7:00 pm|Has Url=http://example.org/some/|Has annotation uri=http://example.org/foaf.rdf|Has email=Lorem@example.org|Has temperature=100 °F|Has boolean=false}}
+
+[[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Platea enim hendrerit</title>
+ <ns>0</ns>
+ <id>498</id>
+ <sha1>4vp3ppszu168lv9f0tc9eaefvgbkv8a</sha1>
+ <revision>
+ <id>1327</id>
+ <timestamp>2014-03-29T16:36:22Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Category:Lorem ipsum]]&quot;</comment>
+ <text xml:space="preserve" bytes="24">[[Category:Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Elit Aliquam urna interdum</title>
+ <ns>0</ns>
+ <id>497</id>
+ <sha1>c5qjdsfni2zwsd95h59jiksolm1vjiv</sha1>
+ <revision>
+ <id>1361</id>
+ <timestamp>2014-04-04T23:00:14Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="945">[[Lorem ipsum]] dolor sit amet consectetuer Maecenas adipiscing Pellentesque id sem. [[Has text::Elit Aliquam urna interdum]] morbi faucibus id tellus ipsum semper wisi. [[Has page::Platea enim hendrerit]] pellentesque consectetuer scelerisque Sed est felis felis quis. Auctor Proin In dolor id et ipsum vel at vitae ut. Praesent elit convallis Praesent aliquet pellentesque vel dolor pellentesque lacinia vitae. At tortor lacus Sed In interdum pulvinar et.
+
+[[Has number::1001]] [[Has quantity::10.25 km²]] [[Has date::1 Jan 2014]] [[Has Url::http://loremipsum.org/]] [[Has annotation uri::http://loremipsum.org/foaf.rdf]] [[Has email::Lorem@ipsum.org]] [[Has temperature::100 °C]] [[Has boolean::true]]
+
+{{#set:|Has number=9999|Has quantity=0.25 m²|Has date=22 Jan 1714|Has Url=http://example.org/|Has annotation uri=http://example.org/foaf.rdf|Has email=Lorem@example.org|Has temperature=20 K|Has boolean=false}}
+
+[[Category: Lorem ipsum]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Lorem ipsum</title>
+ <ns>14</ns>
+ <id>496</id>
+ <sha1>sir97j6uzt9ev2uyhaz1aj4i3spogih</sha1>
+ <revision>
+ <id>1355</id>
+ <timestamp>2014-04-04T22:29:18Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="17">[[Category:Main]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/PageWithTemplateInclusionTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/PageWithTemplateInclusionTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..2207b2d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/PageWithTemplateInclusionTest-Mw-1-19-7.xml
@@ -0,0 +1,112 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/mw-19/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Foo-1-19-7</title>
+ <ns>0</ns>
+ <id>158</id>
+ <sha1>kyhp4t0b6nu2rvzftnzcxnbrxxwfa9d</sha1>
+ <revision>
+ <id>399</id>
+ <timestamp>2013-11-07T23:09:16Z</timestamp>
+ <contributor>
+ <ip>127.0.0.1</ip>
+ </contributor>
+ <text xml:space="preserve" bytes="267">[[Quux::garply]], {{#subobject:|Qeey=bam}}, {{#show: Foo-1-19-7|?Foo}}, {{#ask: [[Modification date::+]]|?Modification date}}
+
+{{FooTemplate|Foo=bar}}, {{FooSubobject|Qeey=bam}}, {{FooShow|Page=Foo-1-19-7}}, {{FooAsk|Query=[[Modification date::+]]}}
+
+[[Category:Regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Template:FooAsk</title>
+ <ns>10</ns>
+ <id>163</id>
+ <sha1>rxf4lv2hmwxsgr5n4wk6oiqcpgk5gcq</sha1>
+ <revision>
+ <id>394</id>
+ <timestamp>2013-11-07T19:39:59Z</timestamp>
+ <contributor>
+ <ip>127.0.0.1</ip>
+ </contributor>
+ <comment>Created page with &quot;{{#ask: {{{Query}}} |?Modification date}}&quot;</comment>
+ <text xml:space="preserve" bytes="41">{{#ask: {{{Query}}} |?Modification date}}</text>
+ </revision>
+ </page>
+ <page>
+ <title>Template:FooShow</title>
+ <ns>10</ns>
+ <id>162</id>
+ <sha1>lmye013mwzch8f6pzb1zpirqmaju4y4</sha1>
+ <revision>
+ <id>393</id>
+ <timestamp>2013-11-07T19:38:58Z</timestamp>
+ <contributor>
+ <ip>127.0.0.1</ip>
+ </contributor>
+ <comment>Created page with &quot;{{#show: {{{Page}}} |?Foo}}&quot;</comment>
+ <text xml:space="preserve" bytes="27">{{#show: {{{Page}}} |?Foo}}</text>
+ </revision>
+ </page>
+ <page>
+ <title>Template:FooSubobject</title>
+ <ns>10</ns>
+ <id>161</id>
+ <sha1>0ae8yc3i99f9n266mhtwnt63unxvsa5</sha1>
+ <revision>
+ <id>392</id>
+ <timestamp>2013-11-07T19:37:53Z</timestamp>
+ <contributor>
+ <ip>127.0.0.1</ip>
+ </contributor>
+ <comment>Created page with &quot;{{#subobject:|Qeey={{{Qeey}}} }}&quot;</comment>
+ <text xml:space="preserve" bytes="32">{{#subobject:|Qeey={{{Qeey}}} }}</text>
+ </revision>
+ </page>
+ <page>
+ <title>Template:FooTemplate</title>
+ <ns>10</ns>
+ <id>160</id>
+ <sha1>c2jf6txk9oex7bcfoggqg31n7fm9kxn</sha1>
+ <revision>
+ <id>391</id>
+ <timestamp>2013-11-07T19:34:39Z</timestamp>
+ <contributor>
+ <ip>127.0.0.1</ip>
+ </contributor>
+ <comment>Created page with &quot;[[Foo::{{{Foo}}}]]&quot;</comment>
+ <text xml:space="preserve" bytes="18">[[Foo::{{{Foo}}}]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RecordDataTypeTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RecordDataTypeTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..34efd7e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RecordDataTypeTest-Mw-1-19-7.xml
@@ -0,0 +1,279 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Category:Regression test</title>
+ <ns>14</ns>
+ <id>420</id>
+ <sha1>4s17yzvoj5kq6dzdmgnw7t1ukpr4tkz</sha1>
+ <revision>
+ <id>1035</id>
+ <timestamp>2014-01-31T07:30:44Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>1 revision</comment>
+ <text xml:space="preserve" bytes="24">Regression test category</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Record type regression test</title>
+ <ns>14</ns>
+ <id>429</id>
+ <sha1>iegvvlibf0prco1vgdvofg9se788l1c</sha1>
+ <revision>
+ <id>1016</id>
+ <timestamp>2014-01-30T08:43:10Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;..&quot;</comment>
+ <text xml:space="preserve" bytes="2">..</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record number field</title>
+ <ns>102</ns>
+ <id>426</id>
+ <sha1>r586wcwca63cjsed7r02w5qwgav0ewt</sha1>
+ <revision>
+ <id>1020</id>
+ <timestamp>2014-01-30T08:47:44Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="61">[[Has type::Number]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record page field</title>
+ <ns>102</ns>
+ <id>422</id>
+ <sha1>ioqobky2da020lawbedjfj0oh97llmj</sha1>
+ <revision>
+ <id>1021</id>
+ <timestamp>2014-01-30T08:48:02Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="59">[[Has type::Page]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record text field</title>
+ <ns>102</ns>
+ <id>423</id>
+ <sha1>ibdxt3wbzj6g3d5mx3c3khfa8nudyha</sha1>
+ <revision>
+ <id>1022</id>
+ <timestamp>2014-01-30T08:48:11Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="59">[[Has type::Text]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record type</title>
+ <ns>102</ns>
+ <id>425</id>
+ <sha1>2r74yeu1070rnprktpe8gnq4ocur15f</sha1>
+ <revision>
+ <id>1023</id>
+ <timestamp>2014-01-30T08:48:23Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="145">[[Has type::Record]] [[Has fields::Has record page field;Has record text field;Has record number field]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has record type for single test</title>
+ <ns>102</ns>
+ <id>430</id>
+ <sha1>2r74yeu1070rnprktpe8gnq4ocur15f</sha1>
+ <revision>
+ <id>1027</id>
+ <timestamp>2014-01-30T09:36:54Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Record]] [[Has fields::Has record page field;Has record text field;Has record number field]] [[Category:Record type regression test]]&quot;</comment>
+ <text xml:space="preserve" bytes="145">[[Has type::Record]] [[Has fields::Has record page field;Has record text field;Has record number field]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>RecordDataTypePage</title>
+ <ns>0</ns>
+ <id>428</id>
+ <sha1>anal6w4vsow54hlzfut4s13c93jdtkl</sha1>
+ <revision>
+ <id>1015</id>
+ <timestamp>2014-01-30T08:42:57Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>/* Usage */</comment>
+ <text xml:space="preserve" bytes="491">== Usage ==
+=== In-text annotation ===
+[[Has record type::FooPage;FooText;9001]], [[Has record type::QyuPage;?;9001]]
+
+=== Silent annotation using #set ===
+{{#set:
+ |Has record type=RecordDataTypePage;BarText;1009
+}}
+{{#set:
+ |Has record type=BooPage;?;1009
+}}
+
+=== Silent annotation using #subobject ===
+{{#subobject:
+ |Has record type=QyuPageOnSubobject;?;9999
+}}
+{{#subobject:
+ |Has record type=XeuiPageOnSubobject;XeuiTextOnSubobject;1111
+}}
+
+[[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>RecordDataTypeRegressionTest/WithSubpage</title>
+ <ns>0</ns>
+ <id>427</id>
+ <sha1>anal6w4vsow54hlzfut4s13c93jdtkl</sha1>
+ <revision>
+ <id>1017</id>
+ <timestamp>2014-01-30T08:43:30Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="491">== Usage ==
+=== In-text annotation ===
+[[Has record type::FooPage;FooText;9001]], [[Has record type::QyuPage;?;9001]]
+
+=== Silent annotation using #set ===
+{{#set:
+ |Has record type=RecordDataTypePage;BarText;1009
+}}
+{{#set:
+ |Has record type=BooPage;?;1009
+}}
+
+=== Silent annotation using #subobject ===
+{{#subobject:
+ |Has record type=QyuPageOnSubobject;?;9999
+}}
+{{#subobject:
+ |Has record type=XeuiPageOnSubobject;XeuiTextOnSubobject;1111
+}}
+
+[[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>RecordDataTypeRegressionTest</title>
+ <ns>0</ns>
+ <id>421</id>
+ <sha1>2ugdgp4h7m7pl11hzr02ba008voodsx</sha1>
+ <revision>
+ <id>1029</id>
+ <timestamp>2014-01-30T12:20:54Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="1704">== Synopsis ==
+A regression test for the record type. Semantic MediaWiki provides a record type representing other property values that consist of a short list of values of other datatypes. For more information about this data type, see [https://semantic-mediawiki.org/wiki/Help:Type_Record Help:Type Record].
+
+[[Property:Has record type]], [[Property:Has record page field]], [[Property:Has record text field]], [[Property:Has record number field]]
+
+[[RecordDataTypeRegressionTest/WithSubpage]], [[RecordDataTypePage]]
+
+== Usage ==
+=== In-text annotation ===
+[[Has record type for single test::ForSingleTestAsPage;ForSingleTestAsText;3333]] [[Has record type::FooPage;FooText;9001]], [[Has record type::QyuPage;?;9001]]
+
+=== Silent annotation using #set ===
+{{#set:
+ |Has record type=RecordDataTypePage;BarText;1009
+}}
+{{#set:
+ |Has record type=BooPage;?;1009
+}}
+
+=== Silent annotation using #subobject ===
+{{#subobject:
+ |Has record type=QyuPageOnSubobject;?;9999
+}}
+{{#subobject:
+ |Has record type=XeuiPageOnSubobject;XeuiTextOnSubobject;1111
+}}
+
+== Queries ==
+=== Simple query to search for specific value ===
+{{#ask: [[Has record page field::QyuPage]] OR [[Has record page field::QyuPageOnSubobject]]
+ |?#=Internal record page (as subobject)
+ |format=broadtable
+}}
+
+=== Wildcard search with compound listing ===
+{{#ask: [[Has record type::+]]
+ |?Has record type
+ |format=broadtable
+}}
+
+=== Wildcard search with individual field listing ===
+{{#ask: [[Has record page field::+]]
+ |?Has record page field
+ |?Has record text field
+ |?Has record number field
+ |format=broadtable
+}}
+
+[[Category:Regression test]] [[Category:Data type regression test]] [[Category:Record type regression test]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RedirectPageTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RedirectPageTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..ba0293e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/RedirectPageTest-Mw-1-19-7.xml
@@ -0,0 +1,113 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Category:Regression test</title>
+ <ns>14</ns>
+ <id>420</id>
+ <sha1>4s17yzvoj5kq6dzdmgnw7t1ukpr4tkz</sha1>
+ <revision>
+ <id>1035</id>
+ <timestamp>2014-01-31T07:30:44Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>1 revision</comment>
+ <text xml:space="preserve" bytes="24">Regression test category</text>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Simple redirect test</title>
+ <ns>14</ns>
+ <id>429</id>
+ <sha1>iegvvlibf0prco1vgdvofg9se788l1c</sha1>
+ <revision>
+ <id>1016</id>
+ <timestamp>2014-01-30T08:43:10Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;..&quot;</comment>
+ <text xml:space="preserve" bytes="2">..</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has regression test</title>
+ <ns>102</ns>
+ <id>487</id>
+ <sha1>byqsaznmj8muv1x9qm7hky0dlasesr9</sha1>
+ <revision>
+ <id>1308</id>
+ <timestamp>2014-03-19T20:18:21Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::Page]]&quot;</comment>
+ <text xml:space="preserve" bytes="18">[[Has type::Page]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>ToBeSimplePageRedirect</title>
+ <ns>0</ns>
+ <id>484</id>
+ <redirect title="SimplePageRedirectRegressionTest" />
+ <sha1>cbkrfrdppwskfozybbkna7avfn6fkz4</sha1>
+ <revision>
+ <id>1309</id>
+ <timestamp>2014-03-19T20:19:25Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ </contributor>
+ <text xml:space="preserve" bytes="80">#REDIRECT [[SimplePageRedirectRegressionTest]] [[Category:Simple redirect test]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>SimplePageRedirectRegressionTest</title>
+ <ns>0</ns>
+ <id>483</id>
+ <sha1>d4d7j2mio0bveyg8dqqmumbz5pcie9n</sha1>
+ <revision>
+ <id>1307</id>
+ <timestamp>2014-03-19T20:17:57Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ </contributor>
+ <text xml:space="preserve" bytes="184">This is part of the [[PageRedirectRegressionTest]] [[Category:Regression test]] [[Category:Redirect test]] [[Category:Simple redirect test]] {{#set:|Has regression test=Redirect test}}</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/TimeDataTypeTest-Mw-1-19-7.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/TimeDataTypeTest-Mw-1-19-7.xml
new file mode 100644
index 00000000..e2cb720f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/TimeDataTypeTest-Mw-1-19-7.xml
@@ -0,0 +1,159 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.6/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.6/ http://www.mediawiki.org/xml/export-0.6.xsd" version="0.6" xml:lang="en-gb">
+ <siteinfo>
+ <sitename>MW-19</sitename>
+ <base>http://localhost:8080/w/index.php/Main_Page</base>
+ <generator>MediaWiki 1.19.7</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">MW-19</namespace>
+ <namespace key="5" case="first-letter">MW-19 talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="104" case="first-letter">Type</namespace>
+ <namespace key="105" case="first-letter">Type talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ <namespace key="160" case="first-letter">Foo</namespace>
+ <namespace key="161" case="first-letter">Foo talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:Has calendar date</title>
+ <ns>102</ns>
+ <id>412</id>
+ <sha1>annvc3ivc6e8dmmd3ljtgkv150g9plg</sha1>
+ <revision>
+ <id>908</id>
+ <timestamp>2014-01-21T06:13:12Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::date]]&quot;</comment>
+ <text xml:space="preserve" bytes="18">[[Has type::date]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has query date</title>
+ <ns>102</ns>
+ <id>413</id>
+ <sha1>annvc3ivc6e8dmmd3ljtgkv150g9plg</sha1>
+ <revision>
+ <id>909</id>
+ <timestamp>2014-01-21T06:13:16Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;[[Has type::date]]&quot;</comment>
+ <text xml:space="preserve" bytes="18">[[Has type::date]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Has date</title>
+ <ns>102</ns>
+ <id>14</id>
+ <sha1>qry1h86awxxalbl5f7vuhstlf0l4mz1</sha1>
+ <revision>
+ <id>901</id>
+ <timestamp>2014-01-20T13:27:26Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <minor/>
+ <comment>1 revision</comment>
+ <text xml:space="preserve" bytes="18">[[has type::date]]</text>
+ </revision>
+ </page>
+ <page>
+ <title>TimeDataTypeRegressionTest</title>
+ <ns>0</ns>
+ <id>404</id>
+ <sha1>10ig4ph0ci93sq8o9l47yh5nt4j33fl</sha1>
+ <revision>
+ <id>910</id>
+ <timestamp>2014-01-21T06:13:59Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <text xml:space="preserve" bytes="2792">== Synopsis ==
+A regression test for date type properties. [http://www.semantic-mediawiki.org/wiki/Help:Type_Date Examples] of typical input dates in English language are:
+{| class=&quot;wikitable&quot;
+|-
+|'''annotation'''||'''result'''||'''remark'''
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000 10:00:01]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000 10:00:01]]||complete date with time
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000]]||just date
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2000]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2000]]||year by itself
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000 10:00:01 PM]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000 10:00:01 PM]]||complete date with time, PM
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000 22:00:01]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000 22:00:01]]||complete date with 24h time
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2000-02-11T22:00:01]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2000-02-11T22:00:01]]||ISO-style date
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2000-02-11T22:00:01+02:00]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2000-02-11T22:00:01+02:00]]||ISO-style date with offset
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2000 February 2]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2000 February 2]]||varying order of inputs is supported
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2-3-2000]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2-3-2000]]||preferred interpretation, month or day, sometimes depends on language settings
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2/3/2000]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2/3/2000]]||various kinds of separators are recognized in all languages
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Jan 1 300 BC]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Jan 1 300 BC]]||dates BC/before common era are supported
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::14000000000 BC]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::14000000000 BC]]||estimated age of the universe
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000 Jl]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000 Jl]]||date of the Julian Calendar
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 1492 Gr]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 1492 Gr]]||date is treated as Gregorian
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::Feb 11 2000 10:00 GMT]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::Feb 11 2000 10:00 GMT]]||date with time zone shortcut
+|-
+|&lt;tt&gt;&lt;nowiki&gt;[[Has date::2000-02-22]]&lt;/nowiki&gt;&lt;/tt&gt;||[[Has date::2000-02-22]]||MySQL date format
+|}
+
+{{#set:
+ |Has query date=January 4, 2010 7:00 pm
+ |Has query date=June 8, 2011
+ |Has query date=01 Jan 1980
+ |Has query date=Feb 11 2000 10:00 GMT
+ |Has query date=2/3/2000
+}}
+
+{{#set:
+ |Has calendar date=Jan 1 300 BC
+ |Has calendar date=14000000000 BC
+ |Has calendar date=Feb 11 2000 Jl
+ |Has calendar date=Feb 11 1492 Gr
+}}
+
+== Query ==
+{{#ask: [[Has query date::+]]|?Has query date|?Has query date#ISO|?Has query date#GR}}
+
+{{#ask: [[Has date::+]][[Category:Regression test]]|?Has date|?Has date#ISO=as ISO|?Has date#GR=as GR|?Has date#JL=as JL}}
+
+[[Category:Regression test]]</text>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/cicero-de-finibus.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/cicero-de-finibus.xml
new file mode 100644
index 00000000..a06523f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/cicero-de-finibus.xml
@@ -0,0 +1,69 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
+ <siteinfo>
+ <sitename>mw-test</sitename>
+ <dbname>mw-test</dbname>
+ <base>http://localhost:8080/mw-test/index.php/Main_Page</base>
+ <generator>MediaWiki 1.25.1</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">mw-test</namespace>
+ <namespace key="5" case="first-letter">mw-test talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>Property:Has text</title>
+ <ns>102</ns>
+ <id>21</id>
+ <revision>
+ <id>4694</id>
+ <parentid>4693</parentid>
+ <timestamp>2015-08-05T20:09:22Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="91">[[Has type::Text]] [[Has property description::To contain strings of arbitrary length.@en]]</text>
+ <sha1>izpf4um41xce5g993zndg9t34diz6ql</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>De Finibus Bonorum et Malorum</title>
+ <ns>0</ns>
+ <id>1298</id>
+ <revision>
+ <id>4692</id>
+ <timestamp>2015-08-05T20:06:47Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;Has text::Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et...&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="879">[[Has text::Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?]]</text>
+ <sha1>cbgy9a7vlvh30498ruk2boehmc74tz9</sha1>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/dwc-import-example.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/dwc-import-example.xml
new file mode 100644
index 00000000..e6443d49
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Fixtures/dwc-import-example.xml
@@ -0,0 +1,307 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
+ <siteinfo>
+ <sitename>mw-test</sitename>
+ <dbname>mw-test</dbname>
+ <base>http://localhost:8080/mw-test/index.php/Main_Page</base>
+ <generator>MediaWiki 1.25.1</generator>
+ <case>first-letter</case>
+ <namespaces>
+ <namespace key="-2" case="first-letter">Media</namespace>
+ <namespace key="-1" case="first-letter">Special</namespace>
+ <namespace key="0" case="first-letter" />
+ <namespace key="1" case="first-letter">Talk</namespace>
+ <namespace key="2" case="first-letter">User</namespace>
+ <namespace key="3" case="first-letter">User talk</namespace>
+ <namespace key="4" case="first-letter">mw-test</namespace>
+ <namespace key="5" case="first-letter">mw-test talk</namespace>
+ <namespace key="6" case="first-letter">File</namespace>
+ <namespace key="7" case="first-letter">File talk</namespace>
+ <namespace key="8" case="first-letter">MediaWiki</namespace>
+ <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+ <namespace key="10" case="first-letter">Template</namespace>
+ <namespace key="11" case="first-letter">Template talk</namespace>
+ <namespace key="12" case="first-letter">Help</namespace>
+ <namespace key="13" case="first-letter">Help talk</namespace>
+ <namespace key="14" case="first-letter">Category</namespace>
+ <namespace key="15" case="first-letter">Category talk</namespace>
+ <namespace key="102" case="first-letter">Property</namespace>
+ <namespace key="103" case="first-letter">Property talk</namespace>
+ <namespace key="108" case="first-letter">Concept</namespace>
+ <namespace key="109" case="first-letter">Concept talk</namespace>
+ </namespaces>
+ </siteinfo>
+ <page>
+ <title>MediaWiki:Smw import dwc</title>
+ <ns>8</ns>
+ <id>1287</id>
+ <revision>
+ <id>4669</id>
+ <timestamp>2015-08-05T18:38:11Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;http://rs.tdwg.org/dwc/terms/|[http://rs.tdwg.org/dwc/terms/ Darwin Core Terms (DwC)] scientificName|Type:Text vernacularName|Type:Monolingual text taxonID|Type:Text kingd...&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="299">http://rs.tdwg.org/dwc/terms/|[http://rs.tdwg.org/dwc/terms/ Darwin Core Terms (DwC)]
+ scientificName|Type:Text
+ vernacularName|Type:Monolingual text
+ taxonID|Type:Text
+ kingdom|Type:Text
+ phylum|Type:Text
+ family|Type:Text
+ genus|Type:Text
+ order|Type:Text
+ class|Type:Text
+
+[[Category:Dwc import]]</text>
+ <sha1>14269h4v28zrlit9yhq25mj2nhzxomp</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Category:Dwc import</title>
+ <ns>14</ns>
+ <id>1291</id>
+ <revision>
+ <id>4673</id>
+ <timestamp>2015-08-05T18:39:29Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;http://rs.tdwg.org/dwc/terms/&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="29">http://rs.tdwg.org/dwc/terms/</text>
+ <sha1>gaz4e878f2b2sie5mo6b7aeyndbq0d4</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:genus</title>
+ <ns>102</ns>
+ <id>1290</id>
+ <revision>
+ <id>4672</id>
+ <timestamp>2015-08-05T18:39:08Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;* [[Imported from::dwc:genus]] {{DISPLAYTITLE:dwc:genus}} [[Category:Dwc import]]&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="82">* [[Imported from::dwc:genus]] {{DISPLAYTITLE:dwc:genus}}
+
+[[Category:Dwc import]]</text>
+ <sha1>gsxqimgmffeoojuc7mtdx7xjyzgrdua</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:kingdom</title>
+ <ns>102</ns>
+ <id>1292</id>
+ <revision>
+ <id>4676</id>
+ <parentid>4675</parentid>
+ <timestamp>2015-08-05T18:41:15Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="775">* [[Imported from::dwc:kingdom]] {{DISPLAYTITLE:dwc:kingdom}}
+* Property description:
+** [[Has property description::The full scientific name of the kingdom in which the taxon is classified. (&quot;Animalia&quot;, &quot;Plantae&quot;)@en]]
+** [[Has property description::El nombre científico completo del reino en el que se clasifica el taxón.@es]]
+** [[Has property description::分类å•å…ƒæ‰€å±žç•Œçš„完整学å。@zh-Hans]]
+** [[Has property description::ãã®åˆ†é¡žç¾¤ãŒä½ç½®ã¥ã‘られã¦ã„ã‚‹ã€åˆ†é¡žä¸Šã®ã€Œç•Œã€ã®ãƒ•ãƒ«ãƒãƒ¼ãƒ ã€‚@ja]]
+** [[Has property description::Le nom scientifique complet du règne dans lequel le taxon est classé@fr]]
+
+Retrieved 20:32, February 29, 2016 from http://terms.tdwg.org/w/index.php?title=dwc:kingdom&amp;oldid=10142
+
+[[Category:Dwc import]]</text>
+ <sha1>jdgcwwgncncbui9un4h14p4zyqki1m3</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:order</title>
+ <ns>102</ns>
+ <id>1293</id>
+ <revision>
+ <id>4677</id>
+ <timestamp>2015-08-05T18:42:18Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;* [[Imported from::dwc:order]] {{DISPLAYTITLE:dwc:order}} [[Category:Dwc import]]&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="82">* [[Imported from::dwc:order]] {{DISPLAYTITLE:dwc:order}}
+
+[[Category:Dwc import]]</text>
+ <sha1>asv9cxea8cm6lr4luptlimsvau0oagz</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:phylum</title>
+ <ns>102</ns>
+ <id>1294</id>
+ <revision>
+ <id>4679</id>
+ <timestamp>2015-08-05T18:43:11Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;* [[Imported from::dwc:phylum]] {{DISPLAYTITLE:dwc:phylum}} [[Category:Dwc import]]&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="84">* [[Imported from::dwc:phylum]] {{DISPLAYTITLE:dwc:phylum}}
+
+[[Category:Dwc import]]</text>
+ <sha1>i9rkmrniujg125dykx2ka2fkdv85kdc</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:scientificName</title>
+ <ns>102</ns>
+ <id>1295</id>
+ <revision>
+ <id>4680</id>
+ <timestamp>2015-08-05T18:43:47Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;* [[Imported from::dwc:scientificName]] {{DISPLAYTITLE:dwc:scientificName}} [[Category:Dwc import]]&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="100">* [[Imported from::dwc:scientificName]] {{DISPLAYTITLE:dwc:scientificName}}
+
+[[Category:Dwc import]]</text>
+ <sha1>4r77lhnl3jsbazjgxqrr3i4qdvm5bil</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:taxonID</title>
+ <ns>102</ns>
+ <id>1296</id>
+ <revision>
+ <id>4681</id>
+ <timestamp>2015-08-05T18:45:07Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <comment>Created page with &quot;* [[Imported from::dwc:taxonID]] {{DISPLAYTITLE:dwc:taxonID}} [[Category:Dwc import]]&quot;</comment>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="86">* [[Imported from::dwc:taxonID]] {{DISPLAYTITLE:dwc:taxonID}}
+
+[[Category:Dwc import]]</text>
+ <sha1>gzoh496mp6y6xzeihd08xitzd6fo18c</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:vernacularName</title>
+ <ns>102</ns>
+ <id>1297</id>
+ <revision>
+ <id>4689</id>
+ <parentid>4688</parentid>
+ <timestamp>2015-08-05T18:54:18Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="663">* [[Imported from::dwc:vernacularName]] {{DISPLAYTITLE:dwc:vernacularName}}
+* Property description:
+** [[Has property description::A common or vernacular name@en]]
+** [[Has property description::Nom commun ou vernaculaire@fr]]
+** [[Has property description::Ein geräuchlicher Name oder Volksname@de]]
+** [[Has property description::Ein geräuchlicher Name oder Volksname@de]]
+** [[Has property description::一般åã€ã‚ã‚‹ã„ã¯é€šä¿—å@ja]]
+** [[Has property description::一个常è§æˆ–者俗å@zh-Hans]]
+** [[Has property description::Un nombre común o vernacular@es]]
+
+http://terms.tdwg.org/w/index.php?title=dwc:vernacularName
+
+[[Category:Dwc import]]</text>
+ <sha1>pdeh0ku8smvn732nlxvshf82km6j9km</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:class</title>
+ <ns>102</ns>
+ <id>1288</id>
+ <revision>
+ <id>4683</id>
+ <parentid>4670</parentid>
+ <timestamp>2015-08-05T18:46:00Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="82">* [[Imported from::dwc:class]] {{DISPLAYTITLE:dwc:class}}
+
+[[Category:Dwc import]]</text>
+ <sha1>mlkipjysemqufxpfqd96qro2pvrbdmq</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>Property:Dwc:family</title>
+ <ns>102</ns>
+ <id>1289</id>
+ <revision>
+ <id>4684</id>
+ <parentid>4671</parentid>
+ <timestamp>2015-08-05T18:46:11Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="84">* [[Imported from::dwc:family]] {{DISPLAYTITLE:dwc:family}}
+
+[[Category:Dwc import]]</text>
+ <sha1>ed6vrz42groes5xa8jhrrsbfdrdifmg</sha1>
+ </revision>
+ </page>
+ <page>
+ <title>GBIFID:2706490</title>
+ <ns>0</ns>
+ <id>1286</id>
+ <revision>
+ <id>4691</id>
+ <parentid>4690</parentid>
+ <timestamp>2015-08-05T18:55:46Z</timestamp>
+ <contributor>
+ <username>Tester</username>
+ <id>1</id>
+ </contributor>
+ <model>wikitext</model>
+ <format>text/x-wiki</format>
+ <text xml:space="preserve" bytes="497">[[dwc:scientificName::Agrostis capillaris L.]]
+
+* [[dwc:kingdom::Plantea]]
+* [[dwc:phylum::Magnoliophyta]]
+* [[dwc:class::Liliopsida]]
+* [[dwc:order::Poales]]
+* [[dwc:family::Poaceae]]
+* [[dwc:genus::Agrostis]]
+* [[dwc:vernacularName::Gewoon struisgras@en]] [[dwc:vernacularName::Rotes Straußgras@de]] [[dwc:vernacularName::Agrostis commun@fr]] [[dwc:vernacularName::Agrostis capillaris@es]]
+* [[dwc:taxonID::1365]]
+
+[[Category:Dwc import]] __SHOWFACTBOX__ {{DISPLAYTITLE:Agrostis capillaris L.}}</text>
+ <sha1>77qals7ynkvp7t6spigid49yy59gkje</sha1>
+ </revision>
+ </page>
+</mediawiki>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/DumpRdfMaintenanceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/DumpRdfMaintenanceTest.php
new file mode 100644
index 00000000..a0b71d30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/DumpRdfMaintenanceTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\ApplicationFactory;
+use SMW\EventHandler;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class DumpRdfMaintenanceTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( "Skipping this test, MW 1.19 doesn't clean-up the title cache correctly." );
+ }
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+
+ ApplicationFactory::getInstance()->getSettings()->set( 'smwgExportBCAuxiliaryUse', true );
+ EventHandler::getInstance()->getEventDispatcher()->dispatch( 'exporter.reset' );
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/' . 'GenericLoremIpsumTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+ ApplicationFactory::getInstance()->clear();
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testMaintenanceRdfOutput() {
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\DumpRdf' );
+ $maintenanceRunner->setQuiet();
+
+ $this->doExportForDefaultOptions( $maintenanceRunner );
+ $this->doExportForPageOption( $maintenanceRunner );
+ }
+
+ private function doExportForDefaultOptions( $maintenanceRunner ) {
+
+ $expectedOutputContent = [
+ // '<rdf:type rdf:resource="&wiki;Category-3ALorem_ipsum"/>',
+ '<rdfs:label>Lorem ipsum</rdfs:label>',
+ '<rdfs:label>Has annotation uri</rdfs:label>',
+ '<rdfs:label>Has boolean</rdfs:label>',
+ '<rdfs:label>Has date</rdfs:label>',
+ '<rdfs:label>Has email</rdfs:label>',
+ '<rdfs:label>Has number</rdfs:label>',
+ '<rdfs:label>Has page</rdfs:label>',
+ '<rdfs:label>Has quantity</rdfs:label>',
+ '<rdfs:label>Has temperature</rdfs:label>',
+ '<rdfs:label>Has text</rdfs:label>',
+ '<rdfs:label>Has Url</rdfs:label>',
+ ];
+
+ $maintenanceRunner->run();
+
+ $this->stringValidator->assertThatStringContains(
+ $expectedOutputContent,
+ $maintenanceRunner->getOutput()
+ );
+ }
+
+ private function doExportForPageOption( $maintenanceRunner ) {
+
+ $expectedOutputContent = [
+ '<rdfs:label>Lorem ipsum</rdfs:label>',
+ '<swivt:masterPage rdf:resource="&wiki;Lorem_ipsum"/>',
+ '<property:Has_subobject-23aux rdf:resource="&wiki;Lorem_ipsum-23_b704f46f7acbb89982564cc97d8e9019"/>',
+ '<swivt:wikiPageSortKey rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Lorem ipsum</swivt:wikiPageSortKey>'
+ ];
+
+ $maintenanceRunner
+ ->setOptions( [ 'page' => 'Lorem ipsum' ] )
+ ->run();
+
+ $this->stringValidator->assertThatStringContains(
+ $expectedOutputContent,
+ $maintenanceRunner->getOutput()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildConceptCacheMaintenanceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildConceptCacheMaintenanceTest.php
new file mode 100644
index 00000000..38235df1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildConceptCacheMaintenanceTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class RebuildConceptCacheMaintenanceTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->pageCreator = UtilityFactory::getInstance()->newPageCreator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/' . 'GenericLoremIpsumTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testRebuildConceptCache() {
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ // 1.19 Title/LinkCache goes nuts for when a page in a previous test got
+ // deleted
+ // $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $conceptPage = $this->createConceptPage( 'Lorem ipsum concept', '[[Category:Lorem ipsum]]' );
+ $this->importedTitles[] = $conceptPage;
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\RebuildConceptCache' );
+ $maintenanceRunner->setQuiet();
+
+ $maintenanceRunner
+ ->setOptions( [ 'status' => true ] )
+ ->run();
+
+ $this->assertInstanceOf(
+ 'SMW\DIConcept',
+ $this->getStore()->getConceptCacheStatus( $conceptPage->getTitle() )
+ );
+
+ $maintenanceRunner
+ ->setOptions( [ 'create' => true ] )
+ ->run();
+
+ $maintenanceRunner
+ ->setOptions( [ 'delete' => true ] )
+ ->run();
+
+ $maintenanceRunner
+ ->setOptions( [ 'create' => true, 's' => 1 ] )
+ ->run();
+
+ $maintenanceRunner
+ ->setOptions( [ 'create' => true, 's' => 1, 'e' => 100 ] )
+ ->run();
+
+ $maintenanceRunner
+ ->setOptions( [ 'create' => true, 'update' => true, 'old' => 1 ] )
+ ->run();
+
+ $maintenanceRunner
+ ->setOptions( [ 'delete' => true, 'concept' => 'Lorem ipsum concept' ] )
+ ->run();
+ }
+
+ protected function createConceptPage( $name, $condition ) {
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( $name, SMW_NS_CONCEPT ) )
+ ->doEdit( "{{#concept: {$condition} }}" );
+
+ return $this->pageCreator->getPage();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildDataMaintenanceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildDataMaintenanceTest.php
new file mode 100644
index 00000000..3e521e7c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildDataMaintenanceTest.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\DIProperty;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\ByPageSemanticDataFinder;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class RebuildDataMaintenanceTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/' . 'GenericLoremIpsumTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testRebuildData() {
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $main = Title::newFromText( 'Lorem ipsum' );
+
+ $expectedSomeProperties = [
+ 'properties' => [
+ new DIProperty( 'Has boolean' ),
+ new DIProperty( 'Has date' ),
+ new DIProperty( 'Has email' ),
+ new DIProperty( 'Has number' ),
+ new DIProperty( 'Has page' ),
+ new DIProperty( 'Has quantity' ),
+ new DIProperty( 'Has temperature' ),
+ new DIProperty( 'Has text' ),
+ new DIProperty( 'Has Url' ),
+ new DIProperty( 'Has annotation uri' )
+ ]
+ ];
+
+ $this->maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\RebuildData' );
+ $this->maintenanceRunner->setQuiet();
+
+ $this->semanticDataFinder = new ByPageSemanticDataFinder;
+ $this->semanticDataFinder->setTitle( $main )->setStore( $this->getStore() );
+
+ $this->assertRunWithoutOptions( $expectedSomeProperties );
+ $this->assertRunWithFullDeleteOption( $expectedSomeProperties );
+ $this->assertRunWithIdRangeOption( $expectedSomeProperties );
+ $this->assertRunWithCategoryOption( $expectedSomeProperties );
+ $this->assertRunWithSparqlStoreForPropertyOption( $expectedSomeProperties );
+ $this->assertRunWithSparqlStoreForQueryOption( $expectedSomeProperties );
+ }
+
+ protected function assertRunWithoutOptions( $expectedSomeProperties ) {
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->run()
+ );
+ }
+
+ protected function assertRunWithFullDeleteOption( $expectedSomeProperties ) {
+
+ $options = [
+ 'f' => true,
+ 'no-cache' => true,
+ 'debug' => true,
+ 'report-runtime' => true
+ ];
+
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->setOptions( $options )->run()
+ );
+ }
+
+ protected function assertRunWithIdRangeOption( $expectedSomeProperties ) {
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->setOptions( [ 's' => 1, 'e' => 10 ] )->run()
+ );
+ }
+
+ protected function assertRunWithCategoryOption( $expectedSomeProperties ) {
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->setOptions( [ 'c' => true ] )->run()
+ );
+ }
+
+ protected function assertRunWithSparqlStoreForPropertyOption( $expectedSomeProperties ) {
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->setOptions( [
+ 'p' => true,
+ 'b' => 'SMWSparqlStore' ] )->run()
+ );
+ }
+
+ protected function assertRunWithSparqlStoreForQueryOption( $expectedSomeProperties ) {
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->maintenanceRunner->setOptions( [
+ 'query' => '[[Has Url::+]]',
+ 'b' => 'SMWSparqlStore' ] )->run()
+ );
+ }
+
+ private function assertThatPropertiesAreSet( $expectedSomeProperties, $runner ) {
+
+ $this->assertTrue( $runner );
+
+ $runPropertiesAreSetAssert = $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $this->semanticDataFinder->fetchFromStore()
+ );
+
+ $this->assertTrue( $runPropertiesAreSetAssert );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildFulltextSearchTableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildFulltextSearchTableTest.php
new file mode 100644
index 00000000..e23cbd83
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildFulltextSearchTableTest.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+
+/**
+ * @group semantic-mediawiki
+ * @group large
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RebuildFulltextSearchTableTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = $this->testEnvironment->getUtilityFactory()->newRunnerFactory();
+ $this->titleValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newTitleValidator();
+
+ // Remove any predisposed settings
+ $this->testEnvironment->tearDown();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/cicero-de-finibus.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->flushPages( $this->importedTitles );
+ parent::tearDown();
+ }
+
+ public function testCanRun() {
+
+ $this->importedTitles = [
+ 'De Finibus Bonorum et Malorum'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\RebuildFulltextSearchTable' );
+ $maintenanceRunner->setQuiet()->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildPropertyStatisticsMaintenanceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildPropertyStatisticsMaintenanceTest.php
new file mode 100644
index 00000000..3fed95e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/RebuildPropertyStatisticsMaintenanceTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class RebuildPropertyStatisticsMaintenanceTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/' . 'GenericLoremIpsumTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testRebuildPropertyStatistics() {
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\RebuildPropertyStatistics' );
+ $maintenanceRunner->setQuiet()->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/UpdateEntityCollationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/UpdateEntityCollationTest.php
new file mode 100644
index 00000000..b6cb2285
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/Maintenance/UpdateEntityCollationTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import\Maintenance;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UpdateEntityCollationTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/../Fixtures/' . 'GenericLoremIpsumTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testSortFieldUpdate() {
+
+ $this->importedTitles = [
+ 'Category:Lorem ipsum',
+ 'Lorem ipsum',
+ 'Elit Aliquam urna interdum',
+ 'Platea enim hendrerit',
+ 'Property:Has Url',
+ 'Property:Has annotation uri',
+ 'Property:Has boolean',
+ 'Property:Has date',
+ 'Property:Has email',
+ 'Property:Has number',
+ 'Property:Has page',
+ 'Property:Has quantity',
+ 'Property:Has temperature',
+ 'Property:Has text'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $maintenanceRunner = $this->runnerFactory->newMaintenanceRunner( 'SMW\Maintenance\UpdateEntityCollation' );
+ $maintenanceRunner->setQuiet()->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/PageWithTemplateInclusionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/PageWithTemplateInclusionTest.php
new file mode 100644
index 00000000..c26ce126
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/PageWithTemplateInclusionTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import;
+
+use SMW\DIProperty;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\ByPageSemanticDataFinder;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class PageWithTemplateInclusionTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/'. 'Fixtures/' . 'PageWithTemplateInclusionTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testImportToVerifyAnnotationByTemplateInclusion() {
+
+ $this->importedTitles = [
+ 'Foo-1-19-7',
+ 'Template:FooAsk',
+ 'Template:FooShow',
+ 'Template:FooSubobject',
+ 'Template:FooTemplate'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $expectedProperties = [
+ 'properties' => [
+ DIProperty::newFromUserLabel( 'Foo' ),
+ DIProperty::newFromUserLabel( 'Quux' ),
+ new DIProperty( '_ASK' ),
+ new DIProperty( '_MDAT' ),
+ new DIProperty( '_SKEY' ),
+ new DIProperty( '_SOBJ' ),
+ new DIProperty( '_INST' )
+ ]
+ ];
+
+ $title = Title::newFromText( 'Foo-1-19-7' );
+
+ $semanticDataFinder = new ByPageSemanticDataFinder();
+ $semanticDataFinder
+ ->setTitle( $title )
+ ->setStore( $this->getStore() );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expectedProperties,
+ $semanticDataFinder->fetchFromOutput()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RecordDataTypeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RecordDataTypeTest.php
new file mode 100644
index 00000000..2ea25f90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RecordDataTypeTest.php
@@ -0,0 +1,228 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\ByPageSemanticDataFinder;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group Database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class RecordDataTypeTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/'. 'Fixtures/' . 'RecordDataTypeTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testImportOfRecordValues() {
+
+ $this->importedTitles = [
+ 'Property:Has record number field',
+ 'Property:Has record page field',
+ 'Property:Has record text field',
+ 'Property:Has record type',
+ 'Property:Has record type for single test',
+ 'RecordDataTypePage',
+ 'RecordDataTypeRegressionTest/WithSubpage',
+ 'RecordDataTypeRegressionTest'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $title = Title::newFromText( 'RecordDataTypeRegressionTest' );
+
+ $expectedCategoryAsWikiValue = [
+ 'property' => new DIProperty( '_INST' ),
+ 'propertyValues' => [
+ 'Regression test',
+ 'Data type regression test',
+ 'Record type regression test'
+ ]
+ ];
+
+ $expectedSomeProperties = [
+ 'properties' => [
+ DIProperty::newFromUserLabel( 'RecordDataTypePage' ),
+ DIProperty::newFromUserLabel( 'BarText' ),
+ DIProperty::newFromUserLabel( 'BooPage' ),
+ DIProperty::newFromUserLabel( 'FooPage' ),
+ DIProperty::newFromUserLabel( 'QyuPage' ),
+ new DIProperty( '_ASK' ),
+ new DIProperty( '_MDAT' ),
+ new DIProperty( '_SKEY' ),
+ new DIProperty( '_SOBJ' ),
+ new DIProperty( '_INST' )
+ ]
+ ];
+
+ $property = DIProperty::newFromUserLabel( 'Has record type for single test' );
+ $valueString = 'ForSingleTestAsPage;ForSingleTestAsText;3333';
+
+ if ( $property->findPropertyTypeID() === '_rec' ) {
+ $valueString = 'ForSingleTestAsPage; ForSingleTestAsText; 3333';
+ }
+
+ $expectedRecordTypeValuesAsWikiValue = [
+ 'subject' => DIWikiPage::newFromTitle( $title ),
+ 'record' => $property,
+ 'property' => $property,
+ 'propertyValues' => [ $valueString, '?; ?; ?' ]
+ ];
+
+ $expectedRecordPageFieldValuesAsWikiValue = [
+ 'subject' => DIWikiPage::newFromTitle( $title ),
+ 'record' => DIProperty::newFromUserLabel( 'Has record type' ),
+ 'property' => DIProperty::newFromUserLabel( 'Has record page field' ),
+ 'propertyValues' => [
+ 'FooPage',
+ 'QyuPageOnSubobject',
+ 'QyuPage',
+ 'XeuiPageOnSubobject',
+ 'RecordDataTypePage',
+ 'BooPage'
+ ]
+ ];
+
+ $expectedRecordTextFieldValuesAsWikiValue = [
+ 'subject' => DIWikiPage::newFromTitle( $title ),
+ 'record' => DIProperty::newFromUserLabel( 'Has record type' ),
+ 'property' => DIProperty::newFromUserLabel( 'Has record text field' ),
+ 'propertyValues' => [
+ 'BarText',
+ 'ForSingleTestAsText',
+ 'FooText',
+ 'XeuiTextOnSubobject'
+ ]
+ ];
+
+ $expectedRecordNumberFieldValuesAsNumber = [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'RecordDataTypeRegressionTest/WithSubpage' ) ),
+ 'record' => DIProperty::newFromUserLabel( 'Has record type' ),
+ 'property' => DIProperty::newFromUserLabel( 'Has record number field' ),
+ 'propertyValues' => [
+ 1111,
+ 9001,
+ 9999,
+ 1009
+ ]
+ ];
+
+ $semanticDataFinder = new ByPageSemanticDataFinder;
+ $semanticDataFinder->setTitle( $title )->setStore( $this->getStore() );
+
+ $semanticDataBatches = [
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $title ) ),
+ ];
+
+ foreach ( $semanticDataBatches as $semanticData ) {
+
+ $this->semanticDataValidator->assertThatCategoriesAreSet(
+ $expectedCategoryAsWikiValue,
+ $semanticData
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $semanticData
+ );
+
+ $this->assertThatSemanticDataValuesAreSet( $expectedRecordTypeValuesAsWikiValue, $semanticData );
+ }
+
+ $this->assertThatRecordValuesAreSet( $expectedRecordTypeValuesAsWikiValue );
+ $this->assertThatRecordValuesAreSet( $expectedRecordPageFieldValuesAsWikiValue );
+ $this->assertThatRecordValuesAreSet( $expectedRecordTextFieldValuesAsWikiValue );
+ $this->assertThatRecordValuesAreSet( $expectedRecordNumberFieldValuesAsNumber );
+ }
+
+ protected function assertThatSemanticDataValuesAreSet( $expected, $semanticData ) {
+
+ $runValueAssert = false;
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ if ( $property->equals( $expected['property'] ) ) {
+ $runValueAssert = true;
+ $this->semanticDataValidator->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $semanticData->getPropertyValues( $property )
+ );
+ }
+
+ }
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runValueAssert, __METHOD__ );
+ }
+
+ protected function assertThatRecordValuesAreSet( $expected ) {
+
+ $runValueAssert = false;
+ $values = [];
+
+ $container = $this->getStore()->getPropertyValues(
+ $expected['subject'],
+ $expected['record']
+ );
+
+ foreach ( $container as $record ) {
+ $values = array_merge(
+ $values,
+ $this->getStore()->getPropertyValues( $record, $expected['property'] )
+ );
+ }
+
+ $this->semanticDataValidator->assertThatPropertyValuesAreSet(
+ $expected,
+ $expected['property'],
+ $values
+ );
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runValueAssert, __METHOD__ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RedirectPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RedirectPageTest.php
new file mode 100644
index 00000000..538cb70f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/RedirectPageTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\InSemanticDataFetcher;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class RedirectPageTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+ private $pageRefresher;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->runnerFactory = $this->testEnvironment->getUtilityFactory()->newRunnerFactory();
+ $this->titleValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $this->pageRefresher = $this->testEnvironment->getUtilityFactory()->newPageRefresher();
+ $this->pageCreator = $this->testEnvironment->getUtilityFactory()->newPageCreator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/'. 'Fixtures/' . 'RedirectPageTest-Mw-1-19-7.xml'
+ );
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->flushPages( $this->importedTitles );
+ parent::tearDown();
+ }
+
+ public function testPageImportToCreateRedirect() {
+
+ $this->importedTitles = [
+ 'SimplePageRedirectRegressionTest',
+ 'ToBeSimplePageRedirect'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $main = Title::newFromText( 'SimplePageRedirectRegressionTest' );
+
+ $expectedCategoryAsWikiValue = [
+ 'property' => new DIProperty( DIProperty::TYPE_CATEGORY ),
+ 'propertyValues' => [
+ 'Regression test',
+ 'Redirect test',
+ 'Simple redirect test'
+ ]
+ ];
+
+ $expectedSomeProperties = [
+ 'properties' => [
+ new DIProperty( 'Has regression test' )
+ ]
+ ];
+
+ $expectedRedirectAsWikiValue = [
+ 'property' => new DIProperty( '_REDI' ),
+ 'propertyValues' => [
+ 'ToBeSimplePageRedirect',
+ 'NewPageRedirectRegressionTest',
+ 'NewTargetPageRedirectRegressionTest'
+ ]
+ ];
+
+ $newRedirectPage = $this->createPageWithRedirectFor(
+ 'NewPageRedirectRegressionTest',
+ 'SimplePageRedirectRegressionTest'
+ );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->movePageToTargetRedirect(
+ $newRedirectPage,
+ 'NewTargetPageRedirectRegressionTest'
+ );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->pageRefresher->doRefreshPoolOfPages( [
+ $main,
+ $newRedirectPage,
+ 'NewTargetPageRedirectRegressionTest'
+ ] );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $semanticDataBatches = [
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $main ) ),
+ ];
+
+ // Something changed in MW since 1.28 that causes a
+ // "SMW\Tests\Utils\Validators\SemanticDataValidator::assertContainsPropertyValues
+ // for '_INST' as '__sin' with (Regression test, Redirect test, Simple redirect test)
+ // Failed asserting that an array contains 'Lorem ipsum'." and since I'm not sure
+ // about the cause, this part is disabled and awaits an investigation
+
+ // $this->assertThatCategoriesAreSet(
+ // $expectedCategoryAsWikiValue,
+ // $semanticDataBatches
+ // );
+
+ $this->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $semanticDataBatches
+ );
+
+ $inSemanticDataFetcher = new InSemanticDataFetcher( $this->getStore() );
+ $inSemanticData = $inSemanticDataFetcher->getSemanticData( DIWikiPage::newFromTitle( $main ) );
+
+ // When running sqlite, the database select returns an empty result which
+ // is probably due to some DB-prefix issues in MW's DatabaseBaseSqlite
+ // implementation and for non-sqlite see #212 / bug 62856
+ if ( $inSemanticData->getProperties() === [] ) {
+ $this->markTestSkipped(
+ "Skipping test either because of sqlite or MW-{$GLOBALS['wgVersion']} / bug 62856"
+ );
+ }
+
+ $this->assertThatSemanticDataValuesForPropertyAreSet(
+ $expectedRedirectAsWikiValue,
+ $inSemanticData
+ );
+ }
+
+ protected function assertThatCategoriesAreSet( $expectedCategoryAsWikiValue, $semanticDataBatches ) {
+
+ foreach ( $semanticDataBatches as $semanticData ) {
+ $this->semanticDataValidator->assertThatCategoriesAreSet(
+ $expectedCategoryAsWikiValue,
+ $semanticData
+ );
+ }
+ }
+
+ protected function assertThatPropertiesAreSet( $expectedSomeProperties, $semanticDataBatches ) {
+
+ foreach ( $semanticDataBatches as $semanticData ) {
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expectedSomeProperties,
+ $semanticData
+ );
+ }
+ }
+
+ protected function assertThatSemanticDataValuesForPropertyAreSet( $expected, $semanticData ) {
+
+ $runValueAssert = false;
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ if ( $property->equals( $expected['property'] ) ) {
+
+ $runValueAssert = true;
+ $this->semanticDataValidator->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $semanticData->getPropertyValues( $property )
+ );
+ }
+ }
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runValueAssert, __METHOD__ );
+ }
+
+ protected function createPageWithRedirectFor( $source, $target ) {
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( $source ) )
+ ->doEdit( "#REDIRECT [[{$target}]]" );
+
+ return $this->pageCreator->getPage();
+ }
+
+ protected function movePageToTargetRedirect( $page, $target ) {
+
+ $moveToTargetTitle = Title::newFromText( $target );
+
+ return $page->getTitle()->moveTo(
+ $moveToTargetTitle,
+ false,
+ 'create redirect',
+ true
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/TimeDataTypeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/TimeDataTypeTest.php
new file mode 100644
index 00000000..a4ddc179
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Import/TimeDataTypeTest.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Import;
+
+use SMW\DIProperty;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\ByPageSemanticDataFinder;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-import
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class TimeDataTypeTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesAfterRun = true;
+
+ private $importedTitles = [];
+ private $runnerFactory;
+ private $titleValidator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ if ( strpos( strtolower( $GLOBALS['smwgSparqlRepositoryConnector'] ), 'virtuoso' ) !== false ) {
+ $this->markTestIncomplete(
+ "Virtuoso will fail for '1 January 300 BC' with 'Virtuoso 22007 Error DT006: Cannot convert -0302-12-28Z to datetime : Incorrect month field length'"
+ );
+ }
+
+ $this->runnerFactory = UtilityFactory::getInstance()->newRunnerFactory();
+ $this->titleValidator = UtilityFactory::getInstance()->newValidatorFactory()->newTitleValidator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $importRunner = $this->runnerFactory->newXmlImportRunner(
+ __DIR__ . '/'. 'Fixtures/' . 'TimeDataTypeTest-Mw-1-19-7.xml'
+ );
+
+ // Shoudl be fixed
+ if ( $GLOBALS['wgLanguageCode'] !== 'en' ) {
+ $this->markTestIncomplete( 'Skipping test because time/date comparison expects a (en) formatted string' );
+ }
+
+ if ( !$importRunner->setVerbose( true )->run() ) {
+ $importRunner->reportFailedImport();
+ $this->markTestIncomplete( 'Test was marked as incomplete because the data import failed' );
+ }
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->importedTitles );
+
+ parent::tearDown();
+ }
+
+ public function testImportOfDifferentDateWithAssortmentOfOutputConversion() {
+
+ $this->importedTitles = [
+ 'TimeDataTypeRegressionTest',
+ 'Property:Has query date',
+ 'Property:Has calendar date',
+ 'Property:Has date'
+ ];
+
+ $this->titleValidator->assertThatTitleIsKnown( $this->importedTitles );
+
+ $title = Title::newFromText( 'TimeDataTypeRegressionTest' );
+
+ $expectedCategoryAsWikiValue = [
+ 'property' => new DIProperty( '_INST' ),
+ 'propertyValues' => [
+ 'Regression test'
+ ]
+ ];
+
+ $expectedPropertiesFromImport = [
+ 'properties' => [
+ DIProperty::newFromUserLabel( 'Has date' ),
+ DIProperty::newFromUserLabel( 'Has calendar date' ),
+ DIProperty::newFromUserLabel( 'Has query date' ),
+ new DIProperty( '_ASK' ),
+ new DIProperty( '_MDAT' ),
+ new DIProperty( '_SKEY' ),
+ new DIProperty( '_SOBJ' ),
+ new DIProperty( '_INST' )
+ ]
+ ];
+
+ $expectedDateValuesAsISO = [
+ 'valueFormatter' => $this->setISO8601DateValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has query date' ),
+ 'propertyValues' => [
+ '2010-01-04T19:00:00',
+ '2011-06-08',
+ '1980-01-01',
+ '2000-02-11T10:00:00',
+ '2000-02-03'
+ ]
+ ];
+
+ $expectedDateValuesAsMediaWiki = [
+ 'valueFormatter' => $this->setMediaWikiDateValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has query date' ),
+ 'propertyValues' => [
+ '19:00, 4 January 2010',
+ '8 June 2011',
+ '1 January 1980',
+ '10:00, 11 February 2000',
+ '3 February 2000'
+ ]
+ ];
+
+ $expectedDateValuesAsWikiValue = [
+ 'valueFormatter' => $this->setWikiValueDateValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has query date' ),
+ 'propertyValues' => [
+ '4 January 2010 19:00:00',
+ '8 June 2011',
+ '1 January 1980',
+ '11 February 2000 10:00:00',
+ '3 February 2000'
+ ]
+ ];
+
+ // Note Windows vs Linux date conversion on PHP
+ // where 14000000000 BC is 2147483647 BC on Windows
+
+ $expectedCalendarSpecificDateValuesAsISO = [
+ 'valueFormatter' => $this->setISO8601DateValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has calendar date' ),
+ 'propertyValues' => [
+ '--301-12-28', // 1 January 300 BC
+ '--2147483647-01-01', // 2147483647 BC
+ '--14000000000-01-01',
+ '2000-02-24',
+ '1492-02-11'
+ ]
+ ];
+
+ $expectedCalendarSpecificDateValuesAsWikiValue = [
+ 'valueFormatter' => $this->setWikiValueDateValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has calendar date' ),
+ 'propertyValues' => [
+ '1 January 300 BC JL', // 1 January 300 BC
+ '2147483647 BC', // 2147483647 BC
+ '14000000000 BC',
+ '11 February 2000 JL',
+ '2 February 1492 JL'
+ ]
+ ];
+
+ $expectedCalendarSpecificDateValuesAsWikiValueWithGRCalendarModel = [
+ 'valueFormatter' => $this->setWikiValueDateWithGRCalendarModelValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has calendar date' ),
+ 'propertyValues' => [
+ '28 December 301 BC', // 1 January 300 BC
+ '2147483647 BC', // 2147483647 BC
+ '14000000000 BC',
+ '24 February 2000',
+ '11 February 1492'
+ ]
+ ];
+
+ $expectedCalendarSpecificDateValuesAsWikiValueWithJLCalendarModel = [
+ 'valueFormatter' => $this->setWikiValueDateWithJLCalendarModelValueFormatter(),
+ 'property' => DIProperty::newFromUserLabel( 'Has calendar date' ),
+ 'propertyValues' => [
+ '1 January 300 BC JL', // 1 January 300 BC
+ '2147483647 BC', // 2147483647 BC
+ '14000000000 BC',
+ '11 February 2000 JL',
+ '2 February 1492 JL'
+ ]
+ ];
+
+ $this->semanticDataFinder = new ByPageSemanticDataFinder;
+ $this->semanticDataFinder->setTitle( $title )->setStore( $this->getStore() );
+
+ $semanticDataBatches = [
+ $this->semanticDataFinder->fetchFromOutput(),
+ $this->semanticDataFinder->fetchFromStore()
+ ];
+
+ $expectedDateValuesBatches = [
+ $expectedDateValuesAsISO,
+ $expectedDateValuesAsMediaWiki,
+ $expectedDateValuesAsWikiValue,
+ $expectedCalendarSpecificDateValuesAsISO,
+ $expectedCalendarSpecificDateValuesAsWikiValue,
+ $expectedCalendarSpecificDateValuesAsWikiValueWithGRCalendarModel,
+ $expectedCalendarSpecificDateValuesAsWikiValueWithJLCalendarModel
+ ];
+
+ foreach ( $semanticDataBatches as $semanticData ) {
+
+ // Something changed in MW since 1.28 that causes a
+ // "SMW\Tests\Utils\Validators\SemanticDataValidator::assertContainsPropertyValues
+ // for '_INST' as '__sin' with (Regression test, Redirect test, Simple redirect test)
+ // Failed asserting that an array contains 'Lorem ipsum'." and since I'm not sure
+ // about the cause, this part is disabled and awaits an investigation
+
+ // $this->assertThatCategoriesAreSet(
+ // $expectedCategoryAsWikiValue,
+ // $semanticData
+ // );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet( $expectedPropertiesFromImport, $semanticData );
+ $this->assertBatchesOfDateValues( $expectedDateValuesBatches, $semanticData );
+ }
+
+ }
+
+ protected function assertBatchesOfDateValues( $assertionBatches, $semanticData ) {
+ foreach ( $assertionBatches as $singleAssertionBatch ) {
+ $this->assertThatDateValuesAreSet( $singleAssertionBatch, $semanticData );
+ }
+ }
+
+ protected function assertThatDateValuesAreSet( $expected, $semanticData ) {
+
+ $runDateValueAssert = false;
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ if ( $property->findPropertyTypeID() === '_dat' && $property->equals( $expected['property'] ) ) {
+ $runDateValueAssert = true;
+ $this->semanticDataValidator->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $semanticData->getPropertyValues( $property )
+ );
+ }
+
+ }
+
+ // Solve issue with single/testsuite DB setup first
+ // $this->assertTrue( $runDateValueAssert, __METHOD__ );
+ }
+
+ protected function setWikiValueDateValueFormatter() {
+ return function( $dataValue ) { return $dataValue->getWikiValue();
+ };
+ }
+
+ protected function setWikiValueDateWithGRCalendarModelValueFormatter() {
+ return function( $dataValue ) {
+ $dataValue->setOutputFormat( 'GR' );
+ return $dataValue->getWikiValue();
+ };
+ }
+
+ protected function setWikiValueDateWithJLCalendarModelValueFormatter() {
+ return function( $dataValue ) {
+ $dataValue->setOutputFormat( 'JL' );
+ return $dataValue->getWikiValue();
+ };
+ }
+
+ protected function setISO8601DateValueFormatter() {
+ return function( $dataValue ) { return $dataValue->getISO8601Date();
+ };
+ }
+
+ protected function setMediaWikiDateValueFormatter() {
+ return function( $dataValue ) { return $dataValue->getMediaWikiDate();
+ };
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php
new file mode 100644
index 00000000..c5dbf589
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Jobs;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\MwDBaseUnitTestCase;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationDispatchJob extends MwDBaseUnitTestCase {
+
+ private $job = null;
+ private $pages = [];
+
+ private $mwHooksHandler;
+ private $pageCreator;
+
+ private $jobQueueRunner;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+
+ $this->mwHooksHandler = $utilityFactory->newMwHooksHandler();
+
+ $this->mwHooksHandler->deregisterListedHooks();
+ $this->mwHooksHandler->invokeHooksFromRegistry();
+
+ $this->pageCreator = $utilityFactory->newPageCreator();
+
+ $this->jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+
+ $this->jobQueueRunner = $utilityFactory->newRunnerFactory()->newJobQueueRunner();
+ $this->jobQueueRunner->setConnectionProvider( $this->getConnectionProvider() );
+ $this->jobQueueRunner->deleteAllJobs();
+
+ $this->testEnvironment->addConfiguration( 'smwgEnableUpdateJobs', true );
+ }
+
+ protected function tearDown() {
+
+ $this->testEnvironment->flushPages(
+ $this->pages
+ );
+
+ $this->testEnvironment->tearDown();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testTriggerUpdateJob() {
+
+ $index = 1; //pass-by-reference
+
+ $this->getStore()->refreshData( $index, 1, false, true )->rebuild( $index );
+
+ $this->assertGreaterThan(
+ 0,
+ $this->jobQueue->getQueueSize( 'smw.update' )
+ );
+ }
+
+ public function testPropertyTypeChangeToCreateUpdateJob() {
+
+ $this->skipTestForDatabase(
+ 'sqlite', 'No idea why SQLite fails here with "Failed asserting that 0 is greater than 0".'
+ );
+
+ $this->skipTestForMediaWikiVersionLowerThan(
+ '1.27',
+ "Skipping test because JobQueue::getQueueSize only returns correct results on 1.27+"
+ );
+
+ $propertyPage = Title::newFromText( 'FooProperty', SMW_NS_PROPERTY );
+
+ $this->pageCreator
+ ->createPage( $propertyPage )
+ ->doEdit( '[[Has type::Page]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo', NS_MAIN ) )
+ ->doEdit( '[[FooProperty::SomePage]]' );
+
+ $this->pageCreator
+ ->createPage( $propertyPage )
+ ->doEdit( '[[Has type::Number]]' );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->jobQueue->disableCache();
+
+ $this->assertGreaterThan(
+ 0,
+ $this->jobQueue->getQueueSize( 'smw.changePropagationDispatch' )
+ );
+
+ $this->jobQueueRunner->setType( 'smw.changePropagationDispatch' )->run();
+
+ $this->jobQueue->disableCache();
+
+ $this->assertGreaterThan(
+ 0,
+ $this->jobQueue->getQueueSize( 'smw.update' )
+ );
+
+ $this->jobQueueRunner->setType( 'smw.update' )->run();
+
+ foreach ( $this->jobQueueRunner->getStatus() as $status ) {
+ $this->assertTrue( $status['status'] );
+ }
+
+ $this->pages = [
+ $propertyPage,
+ Title::newFromText( 'Foo' )
+ ];
+ }
+
+ public function testCategoryChangeToCreateUpdateJob() {
+
+ $this->skipTestForDatabase(
+ 'sqlite', 'No idea why SQLite fails here with "Failed asserting that 0 is greater than 0".'
+ );
+
+ $this->skipTestForMediaWikiVersionLowerThan(
+ '1.27',
+ "Skipping test because JobQueue::getQueueSize only returns correct results on 1.27+"
+ );
+
+ $category = Title::newFromText( 'FooCat', NS_CATEGORY );
+
+ $this->pageCreator
+ ->createPage( $category )
+ ->doEdit( '...' );
+
+ $this->pageCreator
+ ->createPage( $category )
+ ->doEdit( '[[Category:Bar]]' );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->jobQueue->disableCache();
+
+ $this->assertGreaterThan(
+ 0,
+ $this->jobQueue->getQueueSize( 'smw.changePropagationDispatch' )
+ );
+
+ $this->jobQueueRunner->setType( 'smw.changePropagationDispatch' )->run();
+
+ $this->jobQueue->disableCache();
+
+ $this->assertGreaterThan(
+ 0,
+ $this->jobQueue->getQueueSize( 'smw.update' )
+ );
+
+ $this->jobQueueRunner->setType( 'smw.update' )->run();
+
+ foreach ( $this->jobQueueRunner->getStatus() as $status ) {
+ $this->assertTrue( $status['status'] );
+ }
+
+ $this->pages = [
+ $category
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/UpdateJobRoundtripTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/UpdateJobRoundtripTest.php
new file mode 100644
index 00000000..d4c43776
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/Jobs/UpdateJobRoundtripTest.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki\Jobs;
+
+use Job;
+use SMW\ApplicationFactory;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class UpdateJobRoundtripTest extends MwDBaseUnitTestCase {
+
+ private $job = null;
+ private $applicationFactory;
+
+ private $deletePoolOfPages = [];
+ private $runnerFactory;
+
+ private $mwHooksHandler;
+ private $semanticDataValidator;
+
+ private $pageDeleter;
+ private $pageCreator;
+
+ private $jobQueueRunner;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->mwHooksHandler = $utilityFactory->newMwHooksHandler();
+
+ $this->mwHooksHandler
+ ->deregisterListedHooks()
+ ->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = $utilityFactory->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageDeleter = $utilityFactory->newPageDeleter();
+ $this->pageCreator = $utilityFactory->newPageCreator();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ // FIXME Because of SQLStore::Writer::changeTitle
+ $GLOBALS['smwgEnableUpdateJobs'] = true;
+
+ $settings = [
+ 'smwgEnableUpdateJobs' => true
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ $this->applicationFactory->getSettings()->set( $key, $value );
+ }
+
+ $this->jobQueue = $this->applicationFactory->getJobQueue();
+
+ $this->jobQueueRunner = $utilityFactory->newRunnerFactory()->newJobQueueRunner();
+ $this->jobQueueRunner->setConnectionProvider( $this->getConnectionProvider() );
+ $this->jobQueueRunner->deleteAllJobs();
+ }
+
+ protected function tearDown() {
+
+ $this->pageDeleter->doDeletePoolOfPages(
+ $this->deletePoolOfPages
+ );
+
+ $this->applicationFactory->clear();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testPageMoveTriggersUpdateJob() {
+
+ $oldTitle = Title::newFromText( __METHOD__ . '-old' );
+ $newTitle = Title::newFromText( __METHOD__ . '-new' );
+
+ $this->pageCreator
+ ->createPage( $oldTitle )
+ ->doEdit( '[[Has jobqueue test::UpdateJob]]' );
+
+ $this->pageCreator
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $newTitle, false, 'test', true );
+
+ // Execute the job directly
+ // $this->assertJob( 'SMW\UpdateJob' );
+
+ $this->assertTrue(
+ $oldTitle->isRedirect()
+ );
+
+ $this->pageDeleter->deletePage(
+ $oldTitle
+ );
+ }
+
+ public function testSQLStoreRefreshDataTriggersUpdateJob() {
+
+ $index = 1; //pass-by-reference
+
+ $this->getStore()->refreshData( $index, 1, false, true )->rebuild( $index );
+ $this->assertJob( 'smw.update' );
+ }
+
+ /**
+ * @dataProvider jobFactoryProvider
+ */
+ public function testJobFactory( $jobName, $type ) {
+
+ $job = Job::factory(
+ $jobName,
+ Title::newFromText( __METHOD__ . $jobName ),
+ []
+ );
+
+ $this->assertJob( $type, $job );
+ }
+
+ public function jobFactoryProvider() {
+
+ $provider = [];
+
+ $provider[] = [ 'SMW\UpdateJob', 'smw.update' ];
+ $provider[] = [ 'SMW\UpdateJob', 'SMW\UpdateJob' ];
+ $provider[] = [ 'SMWUpdateJob', 'SMW\UpdateJob' ];
+
+ $provider[] = [ 'SMW\RefreshJob', 'smw.refresh' ];
+ $provider[] = [ 'SMW\RefreshJob', 'SMW\RefreshJob' ];
+ $provider[] = [ 'SMWRefreshJob', 'SMW\RefreshJob' ];
+
+ return $provider;
+ }
+
+ public function titleProvider() {
+
+ $provider = [];
+
+ // #0 Simple property reference
+ $provider[] = [ [
+ 'title' => Title::newFromText( __METHOD__ . '-foo' ),
+ 'edit' => '{{#set:|DeferredJobFoo=DeferredJobBar}}'
+ ], [
+ 'title' => Title::newFromText( __METHOD__ . '-bar' ),
+ 'edit' => '{{#set:|DeferredJobFoo=DeferredJobBar}}'
+ ]
+ ];
+
+ // #1 Source page in-property reference
+ $title = Title::newFromText( __METHOD__ . '-foo' );
+
+ $provider[] = [ [
+ 'title' => $title,
+ 'edit' => ''
+ ], [
+ 'title' => Title::newFromText( __METHOD__ . '-bar' ),
+ 'edit' => '{{#set:|DeferredJobFoo=' . $title->getPrefixedText() . '}}'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ protected function assertJob( $type, Job &$job = null ) {
+
+ if ( $job === null ) {
+ $job = $this->jobQueueRunner->pop_type( $type );
+ }
+
+ if ( !$job ) {
+ $this->markTestSkipped( "Required a {$type} JobQueue entry" );
+ }
+
+ $this->job = $job;
+
+ $this->assertInstanceOf( 'Job', $job );
+ $this->assertTrue( $job->run() );
+ }
+
+ /**
+ * Issue 617
+ */
+ public function testNoInfiniteUpdateJobsForCircularRedirect() {
+
+ $this->skipTestForMediaWikiVersionLowerThan( '1.20' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-A' ) )
+ ->doEdit( '[[Foo-A::{{PAGENAME}}]] {{#ask: [[Foo-A::{{PAGENAME}}]] }}' )
+ ->doEdit( '#REDIRECT [[Foo-B]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-B' ) )
+ ->doEdit( '#REDIRECT [[Foo-C]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-C' ) )
+ ->doEdit( '#REDIRECT [[Foo-A]]' );
+
+ $this->jobQueueRunner
+ ->setType( 'SMW\UpdateJob' )
+ ->run();
+
+ foreach ( $this->jobQueueRunner->getStatus() as $status ) {
+ $this->assertTrue( $status['status'] );
+ }
+
+ $this->assertTrue(
+ Title::newFromText( 'Foo-A' )->isRedirect()
+ );
+
+ $this->deletePoolOfPages = [
+ Title::newFromText( 'Foo-A' ),
+ Title::newFromText( 'Foo-B' ),
+ Title::newFromText( 'Foo-C' )
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateEmptyParserOutputDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateEmptyParserOutputDBIntegrationTest.php
new file mode 100644
index 00000000..e2a2d0b1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateEmptyParserOutputDBIntegrationTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use LinksUpdate;
+use ParserOutput;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\PageCreator;
+use Title;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databaseless
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class LinksUpdateEmptyParserOutputDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ public function testDoUpdate() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $title )
+ ->doEdit( '[[Has some property::LinksUpdateConstructedOnEmptyParserOutput]]' );
+
+ $propertiesCountBeforeUpdate = count( $this->getStore()->getSemanticData( $subject )->getProperties() );
+
+ $linksUpdate = new LinksUpdate( $title, new ParserOutput() );
+ $linksUpdate->doUpdate();
+
+
+ $this->assertCount(
+ $propertiesCountBeforeUpdate,
+ $this->getStore()->getSemanticData( $subject )->getProperties()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateSQLStoreDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateSQLStoreDBIntegrationTest.php
new file mode 100644
index 00000000..ef819d8b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateSQLStoreDBIntegrationTest.php
@@ -0,0 +1,249 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use LinksUpdate;
+use ParserOutput;
+use Revision;
+use SMW\ContentParser;
+use SMW\DIWikiPage;
+use SMW\ParserData;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\PageCreator;
+use Title;
+use UnexpectedValueException;
+use User;
+use WikiPage;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class LinksUpdateSQLStoreDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesBeforeRun = true;
+
+ private $title = null;
+ private $mwHooksHandler;
+ private $semanticDataValidator;
+ private $pageDeleter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgPageSpecialProperties',
+ [ '_MDAT' ]
+ );
+
+ $this->mwHooksHandler = $this->testEnvironment->getUtilityFactory()->newMwHooksHandler();
+
+ $this->mwHooksHandler->deregisterListedHooks();
+ $this->mwHooksHandler->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageDeleter = $this->testEnvironment->getUtilityFactory()->newPageDeleter();
+ }
+
+ public function tearDown() {
+
+ $this->mwHooksHandler->restoreListedHooks();
+
+ if ( $this->title !== null ) {
+ $this->pageDeleter->deletePage( $this->title );
+ }
+
+ parent::tearDown();
+ }
+
+ public function testPageCreationAndRevisionHandlingBeforeLinksUpdate() {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $beforeAlterationRevId = $this->createSinglePageWithAnnotations();
+ $this->assertSemanticDataBeforeContentAlteration();
+
+ $afterAlterationRevId = $this->alterPageContentToCreateNewRevisionWithoutAnnotations();
+ $this->assertSemanticDataAfterContentAlteration();
+
+ $this->assertNotSame(
+ $beforeAlterationRevId,
+ $afterAlterationRevId
+ );
+ }
+
+ /**
+ * @depends testPageCreationAndRevisionHandlingBeforeLinksUpdate
+ * @dataProvider propertyCountProvider
+ */
+ public function testLinksUpdateAndVerifyStoreUpdate( $expected ) {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $beforeAlterationRevId = $this->createSinglePageWithAnnotations();
+ $afterAlterationRevId = $this->alterPageContentToCreateNewRevisionWithoutAnnotations();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->fetchRevisionAndRunLinksUpdater(
+ $expected['beforeAlterationRevId'],
+ $beforeAlterationRevId
+ );
+
+ $this->fetchRevisionAndRunLinksUpdater(
+ $expected['afterAlterationRevId'],
+ $afterAlterationRevId
+ );
+ }
+
+ protected function createSinglePageWithAnnotations() {
+ $pageCreator = new PageCreator();
+ $pageCreator->createPage( $this->title )->doEdit( 'Add user property Aa and Fuyu {{#set:|Aa=Bb|Fuyu=Natsu}}' );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ return $pageCreator->getPage()->getRevision()->getId();
+ }
+
+ protected function alterPageContentToCreateNewRevisionWithoutAnnotations() {
+ $pageCreator = new PageCreator();
+ $pageCreator->createPage( $this->title )->doEdit( 'No annotations' );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ return $pageCreator->getPage()->getRevision()->getId();
+ }
+
+ protected function assertSemanticDataBeforeContentAlteration() {
+
+ $wikiPage = WikiPage::factory( $this->title );
+ $revision = $wikiPage->getRevision();
+
+ $parserData = $this->retrieveAndLoadData();
+ $this->assertCount( 3, $parserData->getSemanticData()->getProperties() );
+
+ $this->assertEquals(
+ $parserData->getSemanticData()->getHash(),
+ $this->retrieveAndLoadData( $revision->getId() )->getSemanticData()->getHash(),
+ 'Asserts that data are equals with or without a revision'
+ );
+
+ $this->semanticDataValidator->assertThatSemanticDataHasPropertyCountOf(
+ 4,
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) ),
+ 'Asserts property Aa, Fuyu, _SKEY, and _MDAT exists'
+ );
+ }
+
+ protected function assertSemanticDataAfterContentAlteration() {
+ $this->semanticDataValidator->assertThatSemanticDataHasPropertyCountOf(
+ 2,
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) ),
+ 'Asserts property _SKEY and _MDAT exists'
+ );
+ }
+
+ protected function fetchRevisionAndRunLinksUpdater( array $expected, $revId ) {
+
+ $parserData = $this->retrieveAndLoadData( $revId );
+
+ // Status before the update
+ $this->assertPropertyCount( $expected['poBefore'], $expected['storeBefore'], $parserData );
+
+ $this->runLinksUpdater( $this->title, $parserData->getOutput() );
+
+ // Status after the update
+ $this->assertPropertyCount( $expected['poAfter'], $expected['storeAfter'], $parserData );
+ }
+
+ protected function assertPropertyCount( $poExpected, $storeExpected, $parserData ) {
+ $this->semanticDataValidator->assertThatSemanticDataHasPropertyCountOf(
+ $poExpected['count'],
+ $parserData->getSemanticData(),
+ $poExpected['msg']
+ );
+
+ $this->semanticDataValidator->assertThatSemanticDataHasPropertyCountOf(
+ $storeExpected['count'],
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) ),
+ $storeExpected['msg']
+ );
+ }
+
+ protected function runLinksUpdater( Title $title, $parserOutput ) {
+ $linksUpdate = new LinksUpdate( $title, $parserOutput );
+ $linksUpdate->doUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ protected function retrieveAndLoadData( $revId = null ) {
+
+ $revision = $revId ? Revision::newFromId( $revId ) : null;
+
+ $contentParser = new ContentParser( $this->title );
+ $parserOutput = $contentParser->setRevision( $revision )->parse()->getOutput();
+ $parserOutput->setExtensionData( ParserData::OPT_FORCED_UPDATE, true );
+
+ if ( $parserOutput instanceof ParserOutput ) {
+ return new ParserData( $this->title, $parserOutput );
+ }
+
+ throw new UnexpectedValueException( 'ParserOutput is missing' );
+ }
+
+ public function propertyCountProvider() {
+
+ // Property _SKEY is always present even within an empty container
+ // po = ParserOutput, before means prior LinksUpdate
+
+ $provider = [];
+
+ $provider[] = [ [
+ 'beforeAlterationRevId' => [
+ 'poBefore' => [
+ 'count' => 3,
+ 'msg' => 'Asserts property Aa, Fuyu, and _SKEY exists before the update'
+ ],
+ 'storeBefore' => [
+ 'count' => 2,
+ 'msg' => 'Asserts property _SKEY and _MDAT exists in Store before the update'
+ ],
+ 'poAfter' => [
+ 'count' => 4,
+ 'msg' => 'Asserts property Aa, Fuyu, _SKEY, and _MDAT exists after the update'
+ ],
+ 'storeAfter' => [
+ 'count' => 4,
+ 'msg' => 'Asserts property Aa, Fuyu, _SKEY, and _MDAT exists after the update'
+ ]
+ ],
+ 'afterAlterationRevId' => [
+ 'poBefore' => [
+ 'count' => 0,
+ 'msg' => 'Asserts no property exists before the update'
+ ],
+ 'storeBefore' => [
+ 'count' => 4,
+ 'msg' => 'Asserts property Aa, Fuyu, _SKEY, and _MDAT from the previous state as no update has been made yet'
+ ],
+ 'poAfter' => [
+ 'count' => 0,
+ 'msg' => 'Asserts property _MDAT exists after the update'
+ ],
+ 'storeAfter' => [
+ 'count' => 2,
+ 'msg' => 'Asserts property _SKEY, _MDAT exists after the update'
+ ]
+ ]
+ ] ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateTest.php
new file mode 100644
index 00000000..d78ce871
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/LinksUpdateTest.php
@@ -0,0 +1,216 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class LinksUpdateTest extends MwDBaseUnitTestCase {
+
+ protected $destroyDatabaseTablesBeforeRun = true;
+
+ private $title = null;
+ private $applicationFactory;
+ private $mwHooksHandler;
+ private $semanticDataValidator;
+ private $pageDeleter;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = $this->testEnvironment->getUtilityFactory()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+ $this->mwHooksHandler->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageCreator = $this->testEnvironment->getUtilityFactory()->newPageCreator();
+ $this->pageDeleter = $this->testEnvironment->getUtilityFactory()->newPageDeleter();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->testEnvironment->addConfiguration( 'smwgPageSpecialProperties', [ '_MDAT' ] );
+
+ $this->title = Title::newFromText( __METHOD__ );
+ }
+
+ public function tearDown() {
+ $this->applicationFactory->clear();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ $this->testEnvironment->flushPages( [ $this->title ] );
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testUpdateToSetPredefinedAnnotations() {
+
+ $this->pageCreator
+ ->createPage( $this->title );
+
+ $semanticData = $this->getStore()->getSemanticData(
+ DIWikiPage::newFromTitle( $this->title )
+ );
+
+ $this->assertCount(
+ 2,
+ $semanticData->getProperties()
+ );
+
+ $expected = [
+ 'propertyKeys' => [ '_SKEY', '_MDAT' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $semanticData
+ );
+ }
+
+ /**
+ * @depends testUpdateToSetPredefinedAnnotations
+ */
+ public function testDoUpdateUsingUserdefinedAnnotations() {
+
+ $this->pageCreator
+ ->createPage( $this->title )
+ ->doEdit( '[[HasFirstLinksUpdatetest::testDoUpdate]] [[HasSecondLinksUpdatetest::testDoUpdate]]' );
+
+ $parserData = $this->applicationFactory->newParserData(
+ $this->title,
+ $this->pageCreator->getEditInfo()->output
+ );
+
+ $contentParser = $this->applicationFactory->newContentParser( $this->title );
+ $contentParser->parse();
+
+ $parsedParserData = $this->applicationFactory->newParserData(
+ $this->title,
+ $contentParser->getOutput()
+ );
+
+ $this->assertCount(
+ 4,
+ $parserData->getSemanticData()->getProperties()
+ );
+
+ $this->assertCount(
+ 4,
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) )->getProperties()
+ );
+
+ /**
+ * See #347 and LinksUpdateConstructed
+ */
+ $linksUpdate = new \LinksUpdate( $this->title, new \ParserOutput() );
+ $linksUpdate->doUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ /**
+ * Asserts that before and after the update, the SemanticData container
+ * holds the same amount of properties despite the fact that the ParserOutput
+ * was invoked empty
+ */
+ $semanticData = $this->getStore()->getSemanticData(
+ DIWikiPage::newFromTitle( $this->title )
+ );
+
+ $this->assertCount(
+ 4,
+ $semanticData->getProperties()
+ );
+
+ $expected = [
+ 'propertyKeys' => [ '_SKEY', '_MDAT', 'HasFirstLinksUpdatetest', 'HasSecondLinksUpdatetest' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $semanticData
+ );
+
+ return $this->pageCreator->getPage()->getRevision();
+ }
+
+ /**
+ * @depends testDoUpdateUsingUserdefinedAnnotations
+ */
+ public function testDoUpdateUsingNoAnnotations( $firstRunRevision ) {
+
+ $this->pageCreator
+ ->createPage( $this->title )
+ ->doEdit( 'no annotation' );
+
+ $this->assertNotSame(
+ $firstRunRevision,
+ $this->pageCreator->getPage()->getRevision()
+ );
+
+ $contentParser = $this->applicationFactory->newContentParser( $this->title );
+ $contentParser->parse();
+
+ $parserData = $this->applicationFactory->newParserData(
+ $this->title,
+ $contentParser->getOutput()
+ );
+
+ if ( count( $parserData->getSemanticData()->getProperties() ) != 0 ) {
+ $this->markTestSkipped( "Something changed with MW 1.28 and I'm too lazy to investigate." );
+ }
+
+ $this->assertCount(
+ 0,
+ $parserData->getSemanticData()->getProperties()
+ );
+
+ $this->assertCount(
+ 2,
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) )->getProperties()
+ );
+
+ return $firstRunRevision;
+ }
+
+ /**
+ * @depends testDoUpdateUsingNoAnnotations
+ */
+ public function testReparseFirstRevision( $firstRunRevision ) {
+
+ $contentParser = $this->applicationFactory->newContentParser( $this->title );
+ $contentParser->setRevision( $firstRunRevision );
+ $contentParser->parse();
+
+ $parserData = $this->applicationFactory->newParserData(
+ $this->title,
+ $contentParser->getOutput()
+ );
+
+ $semanticData = $parserData->getSemanticData();
+
+ $this->assertCount(
+ 3,
+ $semanticData->getProperties()
+ );
+
+ $expected = [
+ 'propertyKeys' => [ '_SKEY', 'HasFirstLinksUpdatetest', 'HasSecondLinksUpdatetest' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $semanticData
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MWNamespaceCanonicalNameMatchTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MWNamespaceCanonicalNameMatchTest.php
new file mode 100644
index 00000000..b76cc65f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MWNamespaceCanonicalNameMatchTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use MWNamespace;
+use SMW\NamespaceManager;
+use SMW\Settings;
+use SMW\Tests\Utils\MwHooksHandler;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MWNamespaceCanonicalNameMatchTest extends \PHPUnit_Framework_TestCase {
+
+ private $mwHooksHandler;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = new MwHooksHandler();
+ }
+
+ public function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testRunNamespaceManagerWithNoConstantsDefined() {
+
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $default = [
+ 'smwgNamespacesWithSemanticLinks' => [],
+ 'wgNamespacesWithSubpages' => [],
+ 'wgExtraNamespaces' => [],
+ 'wgNamespaceAliases' => [],
+ 'wgContentNamespaces' => [],
+ 'wgNamespacesToBeSearchedDefault' => [],
+ 'wgLanguageCode' => 'en'
+ ];
+
+ $instance = $this->getMockBuilder( '\SMW\NamespaceManager' )
+ ->setMethods( [ 'isDefinedConstant' ] )
+ ->getMock();
+
+ $instance->expects( $this->atLeastOnce() )
+ ->method( 'isDefinedConstant' )
+ ->will( $this->returnValue( false ) );
+
+ $instance->init( $default );
+ }
+
+ public function testCanonicalNames() {
+
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $count = 0;
+ $index = NamespaceManager::buildNamespaceIndex( Settings::newFromGlobals()->get( 'smwgNamespaceIndex' ) );
+ $names = NamespaceManager::getCanonicalNames();
+
+ $this->assertInternalType( 'array', $names );
+ $this->assertInternalType( 'array', $index );
+
+ foreach ( $index as $ns => $idx ) {
+
+ $mwNamespace = MWNamespace::getCanonicalName( $idx );
+
+ if ( $mwNamespace && isset( $names[$idx] ) ) {
+ $this->assertEquals( $mwNamespace, $names[$idx] );
+ $count++;
+ }
+ }
+
+ $this->assertCount(
+ $count,
+ $names,
+ "Asserts that expected amount of cannonical names have been verified"
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MediaWikiIntegrationForRegisteredHookTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MediaWikiIntegrationForRegisteredHookTest.php
new file mode 100644
index 00000000..073e7fde
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/MediaWikiIntegrationForRegisteredHookTest.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use RequestContext;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\ParserData;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageDeleter;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+use WikiPage;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MediaWikiIntegrationForRegisteredHookTest extends MwDBaseUnitTestCase {
+
+ private $title;
+ private $semanticDataValidator;
+ private $applicationFactory;
+ private $mwHooksHandler;
+ private $pageDeleter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = UtilityFactory::getInstance()->newMwHooksHandler();
+
+ $this->mwHooksHandler
+ ->deregisterListedHooks()
+ ->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $settings = [
+ 'smwgPageSpecialProperties' => [ '_MDAT' ],
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgMainCacheType' => 'hash',
+ 'smwgAutoRefreshOnPurge' => true
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ $this->applicationFactory->getSettings()->set( $key, $value );
+ }
+
+ $this->pageDeleter = new PageDeleter();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ $this->pageDeleter->deletePage( $this->title );
+
+ parent::tearDown();
+ }
+
+ public function testPagePurge() {
+
+ $cacheFactory = $this->applicationFactory->newCacheFactory();
+ $cache = $cacheFactory->newFixedInMemoryCache();
+
+ $this->applicationFactory->registerObject( 'Cache', $cache );
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $this->title )
+ ->doEdit( '[[Has function hook test::page purge]]' );
+
+ $key = $cacheFactory->getPurgeCacheKey( $this->title->getArticleID() );
+
+ $pageCreator
+ ->getPage()
+ ->doPurge();
+
+ $this->assertTrue(
+ $cache->fetch( $key )
+ );
+ }
+
+ public function testPageDelete() {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $this->title )
+ ->doEdit( '[[Has function hook test::page delete]]' );
+
+ $this->semanticDataValidator->assertThatSemanticDataIsNotEmpty(
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) )
+ );
+
+ $this->pageDeleter->deletePage( $this->title );
+
+ $this->semanticDataValidator->assertThatSemanticDataIsEmpty(
+ $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) )
+ );
+ }
+
+ public function testEditPageToGetNewRevision() {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $this->title )
+ ->doEdit( '[[EditPageToGetNewRevisionHookTest::Foo]]' );
+
+ $parserOutput = $pageCreator->getEditInfo()->output;
+
+ $this->assertInstanceOf(
+ 'ParserOutput',
+ $parserOutput
+ );
+
+ $parserData = new ParserData(
+ $this->title,
+ $parserOutput
+ );
+
+ $expected = [
+ 'propertyKeys' => [ '_SKEY', '_MDAT', 'EditPageToGetNewRevisionHookTest' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testOnOutputPageParserOutputeOnDatabase() {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $this->title )
+ ->doEdit( '[[Has function hook test::output page]]' );
+
+ $parserOutput = $pageCreator->getEditInfo()->output;
+
+ $this->assertInstanceOf(
+ 'ParserOutput',
+ $parserOutput
+ );
+
+ $context = new RequestContext();
+ $context->setTitle( $this->title );
+
+ // Use of OutputPage::addParserOutputNoText was deprecated in MediaWiki 1.24
+ if ( method_exists( $context->getOutput(), 'addParserOutputMetadata' ) ) {
+ $context->getOutput()->addParserOutputMetadata( $parserOutput );
+ } else {
+ $context->getOutput()->addParserOutputNoText( $parserOutput );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/PredefinedPropertyAnnotationDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/PredefinedPropertyAnnotationDBIntegrationTest.php
new file mode 100644
index 00000000..7151de41
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/PredefinedPropertyAnnotationDBIntegrationTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDITime as DITime;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databaseless
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyAnnotationDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $semanticDataValidator;
+ private $applicationFactory;
+ private $dataValueFactory;
+ private $mwHooksHandler;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = UtilityFactory::getInstance()->newMwHooksHandler();
+
+ $this->mwHooksHandler
+ ->deregisterListedHooks()
+ ->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageCreator = UtilityFactory::getInstance()->newPageCreator();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testPredefinedModificationDatePropertyAndChangedDefaultsortForNewPage() {
+
+ $this->applicationFactory->getSettings()->set( 'smwgPageSpecialProperties', [ '_MDAT' ] );
+
+ $title = Title::newFromText( __METHOD__ );
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $this->pageCreator
+ ->createPage( $title, '{{DEFAULTSORT:SortForFoo}}' );
+
+ $dvPageModificationTime = $this->dataValueFactory->newDataValueByItem(
+ DITime::newFromTimestamp( $this->pageCreator->getPage()->getTimestamp() )
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_MDAT', '_SKEY' ],
+ 'propertyValues' => [ $dvPageModificationTime->getISO8601Date(), 'SortForFoo' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+ }
+
+ public function testAddedCategoryAndChangedDefaultsortWithoutPredefinedPropertiesForNewPage() {
+
+ $this->applicationFactory->getSettings()->set( 'smwgPageSpecialProperties', [] );
+
+ $title = Title::newFromText( __METHOD__ );
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $this->pageCreator
+ ->createPage( $title )
+ ->doEdit( '{{DEFAULTSORT:SortForFoo}} [[Category:SingleCategory]]' );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_SKEY', '_INST' ],
+ 'propertyValues' => [ 'SortForFoo', 'SingleCategory' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/RedirectTargetFinderIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/RedirectTargetFinderIntegrationTest.php
new file mode 100644
index 00000000..1cccce0e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/RedirectTargetFinderIntegrationTest.php
@@ -0,0 +1,260 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMW\Tests\PHPUnitCompat;
+use Title;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databas
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class RedirectTargetFinderIntegrationTest extends MwDBaseUnitTestCase {
+
+ use PHPUnitCompat;
+
+ private $deletePoolOfPages = [];
+
+ private $pageCreator;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgEnabledDeferredUpdate',
+ false
+ );
+
+ $this->pageCreator = UtilityFactory::getInstance()->newPageCreator();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+
+ // #3414
+ // NameTableAccessException: Expected unused ID from database insert for
+ // 'mw-changed-redirect-target' into 'change_tag_def',
+ $this->testEnvironment->resetMediaWikiService( 'NameTableStoreFactory' );
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+
+ $pageDeleter
+ ->doDeletePoolOfPages( $this->deletePoolOfPages );
+
+ parent::tearDown();
+ }
+
+ public function testRedirectParseUsingManualRedirect() {
+
+ $target = Title::newFromText( 'RedirectParseUsingManualRedirect' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ ) )
+ ->doEdit( '#REDIRECT [[RedirectParseUsingManualRedirect]]' );
+
+ $expected = [
+ new DIProperty( '_REDI' )
+ ];
+
+ $this->semanticDataValidator->assertHasProperties(
+ $expected,
+ $this->getStore()->getInProperties( DIWikiPage::newFromTitle( $target ) )
+ );
+
+ $this->deletePoolOfPages = [
+ __METHOD__,
+ 'RedirectParseUsingManualRedirect'
+ ];
+ }
+
+ public function testRedirectParseUsingMoveToPage() {
+
+ $target = Title::newFromText( 'RedirectParseUsingMoveToPage' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ ) );
+
+ $this->pageCreator
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $target, false, 'test', true );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $expected = [
+ new DIProperty( '_REDI' )
+ ];
+
+ $this->semanticDataValidator->assertHasProperties(
+ $expected,
+ $this->getStore()->getInProperties( DIWikiPage::newFromTitle( $target ) )
+ );
+
+ $this->deletePoolOfPages = [
+ __METHOD__,
+ 'RedirectParseUsingMoveToPage'
+ ];
+ }
+
+ public function testManualRemovalOfRedirectTarget() {
+
+ $source = DIWikiPage::newFromTitle(
+ Title::newFromText( __METHOD__ )
+ );
+
+ $target = DIWikiPage::newFromTitle(
+ Title::newFromText( 'ManualRemovalOfRedirectTarget' )
+ );
+
+ $target->getSortKey();
+
+ $this->pageCreator
+ ->createPage( $source->getTitle() )
+ ->doEdit( '#REDIRECT [[Property:ManualRemovalOfRedirectTarget-NotTheRealTarget]]' )
+ ->doEdit( '#REDIRECT [[ManualRemovalOfRedirectTarget]]' );
+
+ $expected = [
+ new DIProperty( '_REDI' )
+ ];
+
+ $this->assertEquals(
+ $target,
+ $this->getStore()->getRedirectTarget( $source )
+ );
+
+ $this->semanticDataValidator->assertHasProperties(
+ $expected,
+ $this->getStore()->getInProperties( $target )
+ );
+
+ $this->pageCreator
+ ->createPage( $source->getTitle() )
+ ->doEdit( 'removed redirect target' );
+
+ $this->assertEquals(
+ $source,
+ $this->getStore()->getRedirectTarget( $source )
+ );
+
+ $this->assertEmpty(
+ $this->getStore()->getInProperties( $target )
+ );
+
+ $this->deletePoolOfPages = [
+ __METHOD__,
+ 'ManualRemovalOfRedirectTarget'
+ ];
+ }
+
+ public function testDeepRedirectTargetResolverToFindTarget() {
+
+ $this->skipTestForMediaWikiVersionLowerThan(
+ '1.20',
+ "Skipping test because expected target isn't resolved correctly on 1.19"
+ );
+
+ $source = Title::newFromText( 'DeepRedirectTargetResolverToFindTarget' );
+
+ $this->pageCreator
+ ->createPage( $source )
+ ->doEdit( '#REDIRECT [[DeepRedirectTargetResolverToFindTarget/1]]' );
+
+ $intermediateTarget = Title::newFromText( 'DeepRedirectTargetResolverToFindTarget/2' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'DeepRedirectTargetResolverToFindTarget/1' ) )
+ ->getPage()
+ ->getTitle()
+ ->moveTo( $intermediateTarget, false, 'redirect test', true );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'DeepRedirectTargetResolverToFindTarget/2' ) )
+ ->doEdit( '#REDIRECT [[DeepRedirectTargetResolverToFindTarget/3]]' );
+
+ $target = Title::newFromText( 'DeepRedirectTargetResolverToFindTarget/3' );
+
+ $deepRedirectTargetResolver = ApplicationFactory::getInstance()
+ ->newMwCollaboratorFactory()
+ ->newDeepRedirectTargetResolver();
+
+ $this->assertEquals(
+ $target->getDBKey(),
+ $deepRedirectTargetResolver->findRedirectTargetFor( $source )->getDBKey()
+ );
+
+ $this->assertEquals(
+ $target->getDBKey(),
+ $this->getStore()->getRedirectTarget( DIWikiPage::newFromTitle( $source ) )->getDBKey()
+ );
+
+ $this->deletePoolOfPages = [
+ 'DeepRedirectTargetResolverToFindTarget',
+ 'DeepRedirectTargetResolverToFindTarget/1',
+ 'DeepRedirectTargetResolverToFindTarget/2',
+ 'DeepRedirectTargetResolverToFindTarget/3'
+ ];
+ }
+
+ public function testDeepRedirectTargetResolverToDetectCircularTarget() {
+
+ $this->skipTestForMediaWikiVersionLowerThan(
+ '1.20',
+ "Skipping test because circular target (RuntimeException) isn't found on 1.19"
+ );
+
+ $source = Title::newFromText( 'DeepRedirectTargetResolverToDetectCircularTarget' );
+
+ $this->pageCreator
+ ->createPage( $source )
+ ->doEdit( '#REDIRECT [[DeepRedirectTargetResolverToDetectCircularTarget/1]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'DeepRedirectTargetResolverToDetectCircularTarget/1' ) )
+ ->doEdit( '#REDIRECT [[DeepRedirectTargetResolverToDetectCircularTarget/2]]' );
+
+ // Create circular redirect
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'DeepRedirectTargetResolverToDetectCircularTarget/2' ) )
+ ->doEdit( '#REDIRECT [[DeepRedirectTargetResolverToDetectCircularTarget/1]]' );
+
+ $deepRedirectTargetResolver = ApplicationFactory::getInstance()
+ ->newMwCollaboratorFactory()
+ ->newDeepRedirectTargetResolver();
+
+ // Store will point towards the correct target
+ $expectedRedirect = DIWikiPage::newFromTitle(
+ Title::newFromText( 'DeepRedirectTargetResolverToDetectCircularTarget/1' )
+ );
+
+ $this->assertEquals(
+ $expectedRedirect->getDBKey(),
+ $this->getStore()->getRedirectTarget( DIWikiPage::newFromTitle( $source ) )->getDBKey()
+ );
+
+ // Resolver will raise an exception as actions can not act on
+ // a circular redirect oppose to a possible annotation created by the
+ // store
+ $this->setExpectedException( 'RuntimeException' );
+ $deepRedirectTargetResolver->findRedirectTargetFor( $source );
+
+ $this->deletePoolOfPages = [
+ 'DeepRedirectTargetResolverToDetectCircularTarget',
+ 'DeepRedirectTargetResolverToDetectCircularTarget/1',
+ 'DeepRedirectTargetResolverToDetectCircularTarget/2'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/SearchInPageDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/SearchInPageDBIntegrationTest.php
new file mode 100644
index 00000000..3ec632b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/MediaWiki/SearchInPageDBIntegrationTest.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\Tests\Integration\MediaWiki;
+
+use SMW\MediaWiki\Search\Search;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageDeleter;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SearchInPageDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ public function testSearchForPageValueAsTerm() {
+
+ $propertyPage = Title::newFromText( 'Has some page value', SMW_NS_PROPERTY );
+ $targetPage = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $propertyPage )
+ ->doEdit( '[[Has type::Page]]' );
+
+ $pageCreator
+ ->createPage( $targetPage )
+ ->doEdit( '[[Has some page value::Foo]]' );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $search = new Search();
+ $results = $search->searchText( '[[Has some page value::Foo]]' );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\SearchResultSet',
+ $results
+ );
+
+ $this->assertEquals(
+ 1,
+ $results->getTotalHits()
+ );
+
+ $pageDeleter = new PageDeleter();
+ $pageDeleter->deletePage( $targetPage );
+ $pageDeleter->deletePage( $propertyPage );
+ }
+
+ public function testSearchForGeographicCoordinateValueAsTerm() {
+
+ if ( !defined( 'SM_VERSION' ) ) {
+ $this->markTestSkipped( "Requires 'Geographic coordinate' to be a supported data type (see Semantic Maps)" );
+ }
+
+ $propertyPage = Title::newFromText( 'Has coordinates', SMW_NS_PROPERTY );
+ $targetPage = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $propertyPage )
+ ->doEdit( '[[Has type::Geographic coordinate]]' );
+
+ $pageCreator
+ ->createPage( $targetPage )
+ ->doEdit( "[[Has coordinates::52°31'N, 13°24'E]]" );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $search = new Search();
+ $results = $search->searchText( "[[Has coordinates::52°31'N, 13°24'E]]" );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\SearchResultSet',
+ $results
+ );
+
+ if ( is_a( $this->getStore(), '\SMW\SPARQLStore\SPARQLStore' ) ) {
+ $this->markTestIncomplete( "Test was marked as incomplete because the SPARQLStore doesn't support the Geo data type" );
+ }
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertEquals(
+ 1,
+ $results->getTotalHits()
+ );
+
+ $pageDeleter = new PageDeleter();
+ $pageDeleter->deletePage( $targetPage );
+ $pageDeleter->deletePage( $propertyPage );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Parser/InTextAnnotationParserTemplateTransclusionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Parser/InTextAnnotationParserTemplateTransclusionTest.php
new file mode 100644
index 00000000..f234f7f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Parser/InTextAnnotationParserTemplateTransclusionTest.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\Tests\Integration\Parser;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InTextAnnotationParserTemplateTransclusionTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $testEnvironment;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * Helper method for processing a template transclusion by simulating template
+ * expensions using a callback to avoid having to integrate DB read/write
+ * process in order to access a Template
+ *
+ * @note Part of the routine has been taken from MW's ExtraParserTest
+ *
+ * @return text
+ */
+ private function runTemplateTransclusion( Title $title, $text, $return ) {
+
+ $parser = new \Parser;
+ $options = new \ParserOptions;
+ $options->setTemplateCallback( function ( $title, $parser = false ) use ( $return ) {
+
+ $text = $return;
+ $deps = [];
+
+ return [
+ 'text' => $text,
+ 'finalTitle' => $title,
+ 'deps' => $deps
+ ];
+
+ } );
+
+ return $parser->preprocess( $text, $title, $options );
+ }
+
+ /**
+ * @dataProvider templateDataProvider
+ */
+ public function testPreprocessTemplateAndParse( $namespace, array $settings, $text, $tmplValue, array $expected ) {
+
+ $parserOutput = new ParserOutput();
+ $title = Title::newFromText( __METHOD__, $namespace );
+
+ $outputText = $this->runTemplateTransclusion( $title, $text, $tmplValue );
+
+ $this->testEnvironment->withConfiguration( $settings );
+
+ $parserData = $this->applicationFactory->newParserData(
+ $title,
+ $parserOutput
+ );
+
+ $instance = $this->applicationFactory->newInTextAnnotationParser(
+ $parserData
+ );
+
+ $instance->parse( $outputText );
+
+ $this->assertContains(
+ $expected['resultText'],
+ $outputText
+ );
+
+ $parserData = $this->applicationFactory->newParserData(
+ $title,
+ $parserOutput
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $parserData->getSemanticData()
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function templateDataProvider() {
+
+ $provider = [];
+
+ // #0 Bug 54967
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgLinksInValues' => false,
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ 'smwgMainCacheType' => 'hash'
+ ],
+ '[[Foo::{{Bam}}]]',
+ '?bar',
+ [
+ 'resultText' => '[[:?bar|?bar]]',
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ 'Foo' ],
+ 'propertyValues' => [ '?bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/PropertyLabelCanonicalMatchTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/PropertyLabelCanonicalMatchTest.php
new file mode 100644
index 00000000..fd310800
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/PropertyLabelCanonicalMatchTest.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\DIProperty;
+use SMW\PropertyRegistry;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyLabelCanonicalMatchTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider canonicalLabelProvider
+ */
+ public function testFindPropertyIdByLabel( $label, $expectedKey, $expectedLabel ) {
+
+ list( $labelMatch, $property ) = $this->findPropertyIdByLabel( $label );
+
+ $this->assertEquals(
+ $labelMatch,
+ $expectedLabel
+ );
+
+ $this->assertEquals(
+ $expectedKey,
+ $property->getKey()
+ );
+ }
+
+ /**
+ * @dataProvider canonicalLabelProvider
+ */
+ public function testNewFromUserLabel( $label, $expectedKey, $expectedLabel ) {
+
+ list( $labelMatch, $property ) = $this->newFromUserLabel( $label );
+
+ $this->assertEquals(
+ $labelMatch,
+ $expectedLabel
+ );
+
+ $this->assertEquals(
+ $expectedKey,
+ $property->getKey()
+ );
+ }
+
+ /**
+ * @dataProvider canonicalLabelWithLanguageProvider
+ */
+ public function testNewFromUserLabelWithLanguage( $label, $languageCode, $expectedKey, $expectedLabel ) {
+
+ list( $labelMatch, $property ) = $this->newFromUserLabel( $label, $languageCode );
+
+ $this->assertEquals(
+ $labelMatch,
+ $expectedLabel
+ );
+
+ $this->assertEquals(
+ $expectedKey,
+ $property->getKey()
+ );
+ }
+
+ private function findPropertyIdByLabel( $label ) {
+
+ $property = new DIProperty(
+ PropertyRegistry::getInstance()->findPropertyIdByLabel( $label )
+ );
+
+ $canonicalLabel = $property->getCanonicalLabel();
+
+ // #1966 and #1968
+ // In case something goes wrong, a recursive loop will kill PHP
+ // and we know we messed up
+ if ( $canonicalLabel !== '' && $label !== $canonicalLabel ) {
+ $this->findPropertyIdByLabel(
+ $property->getCanonicalDiWikiPage()->getTitle()->getText()
+ );
+ }
+
+ return [ $label, $property ];
+ }
+
+ private function newFromUserLabel( $label, $languageCode = false ) {
+
+ $property = DIProperty::newFromUserLabel( $label, false, $languageCode );
+
+ $canonicalLabel = $property->getCanonicalLabel();
+
+ // #1966 and #1968
+ // In case something goes wrong, a recursive loop will kill PHP
+ // and we know we messed up
+ if ( $canonicalLabel !== '' && $label !== $canonicalLabel ) {
+ $this->newFromUserLabel(
+ $property->getCanonicalDiWikiPage()->getTitle()->getText()
+ );
+ }
+
+ return [ $label, $property ];
+ }
+
+ public function canonicalLabelProvider() {
+
+ $provider[] = [
+ 'Number',
+ '_num',
+ 'Number',
+ ];
+
+ $provider[] = [
+ 'Float',
+ '_num',
+ 'Float'
+ ];
+
+ $provider[] = [
+ 'Telephone number',
+ '_tel',
+ 'Telephone number'
+ ];
+
+ $provider[] = [
+ 'Phone number',
+ '_tel',
+ 'Phone number'
+ ];
+
+ return $provider;
+ }
+
+ public function canonicalLabelWithLanguageProvider() {
+
+ $provider[] = [
+ 'Number',
+ 'en',
+ '_num',
+ 'Number'
+ ];
+
+ $provider[] = [
+ 'Number',
+ 'fr',
+ '_num',
+ 'Number'
+ ];
+
+ $provider[] = [
+ 'Booléen',
+ 'fr',
+ '_boo',
+ 'Booléen'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/CategoryClassQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/CategoryClassQueryDBIntegrationTest.php
new file mode 100644
index 00000000..1591ec0c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/CategoryClassQueryDBIntegrationTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class CategoryClassQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+
+ private $dataValueFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ public function testSubjects_onCategoryCondition() {
+
+ $property = new DIProperty( '_INST' );
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty( $property, 'SomeCategory' );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue( $dataValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertArrayHasKey(
+ $property->getKey(),
+ $this->getStore()->getSemanticData( $semanticData->getSubject() )->getProperties()
+ );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $semanticData->getSubject(),
+ $this->searchForResultsThatCompareEqualToClassOf( 'SomeCategory' )
+ );
+
+ $this->queryResultValidator->assertThatQueryResultContains(
+ $dataValue,
+ $this->searchForResultsThatCompareEqualToClassOf( 'SomeCategory' )
+ );
+ }
+
+ private function searchForResultsThatCompareEqualToClassOf( $categoryName ) {
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( new DIProperty( '_INST' ) );
+
+ $description = new ClassDescription(
+ new DIWikiPage( $categoryName, NS_CATEGORY, '' )
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ return $this->getStore()->getQueryResult( $query );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ComparatorFilterConditionQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ComparatorFilterConditionQueryDBIntegrationTest.php
new file mode 100644
index 00000000..88b4fcd6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ComparatorFilterConditionQueryDBIntegrationTest.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DIProperty;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMwConjunction as Conjunction;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+use SMWDITime as DITime;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ComparatorFilterConditionQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider numericConjunctionFilterProvider
+ */
+ public function testNumericConjunctionConstraints( $range, $parameters, $expected ) {
+ $this->queryPagesThatUseConjunctionConstraintsForPropertyValues( $range, $parameters, $expected );
+ }
+
+ /**
+ * @dataProvider textConjunctionFilterProvider
+ */
+ public function testTextConjunctionConstraints( $range, $parameters, $expected ) {
+ $this->queryPagesThatUseConjunctionConstraintsForPropertyValues( $range, $parameters, $expected );
+ }
+
+ /**
+ * @dataProvider dateConjunctionFilterProvider
+ */
+ public function testDateConjunctionConstraints( $range, $parameters, $expected ) {
+
+ if ( is_a( $this->getStore(), '\SMW\SPARQLStore\SPARQLStore' )
+ && is_a( $this->getStore()->getConnection( 'sparql' ), '\SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector' ) ) {
+ $this->markTestSkipped( "Date filter constraints do not work properly in Virtuoso" );
+ }
+
+ $this->queryPagesThatUseConjunctionConstraintsForPropertyValues( $range, $parameters, $expected );
+ }
+
+ public function queryPagesThatUseConjunctionConstraintsForPropertyValues( $range, $parameters, $expected ) {
+
+ $expectedSubjects = [];
+ $property = $parameters['property'];
+
+ foreach ( $range as $key => $value ) {
+
+ $semanticData = $this->semanticDataFactory
+ ->newEmptySemanticData( __METHOD__ . strval( $key ) );
+
+ $semanticData->addPropertyObjectValue( $property, $value );
+
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+ $this->getStore()->updateData( $semanticData );
+
+ if ( in_array( $key, $expected['subjects'] ) ) {
+ $expectedSubjects[] = $semanticData->getSubject();
+ }
+ }
+
+ $description = new Conjunction( [
+ new SomeProperty(
+ $property,
+ new ValueDescription( $parameters['lower'], null, $parameters['lowerComp'] ) ),
+ new SomeProperty(
+ $property,
+ new ValueDescription( $parameters['upper'], null, $parameters['upperComp'] ) ),
+ ] );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ $expected['count'],
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+ }
+
+ public function numericConjunctionFilterProvider() {
+
+ $property = new DIProperty( 'SomeNumericPropertyToFilter' );
+ $property->setPropertyTypeId( '_num' );
+
+ #0 Numeric Greater Equal, Less Equal
+ $provider[] = [
+ [
+ 1 => new DINumber( 1 ),
+ 6 => new DINumber( 6 ),
+ 10 => new DINumber( 10 )
+ ],
+ [
+ 'lower' => new DINumber( 1 ),
+ 'upper' => new DINumber( 9 ),
+ 'lowerComp' => SMW_CMP_GEQ,
+ 'upperComp' => SMW_CMP_LEQ,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ 1, 6 ]
+ ]
+ ];
+
+ #1 Numeric Greater, Equal
+ $provider[] = [
+ [
+ 1 => new DINumber( 1 ),
+ 2 => new DINumber( 2 ),
+ 6 => new DINumber( 6 ),
+ 10 => new DINumber( 10 )
+ ],
+ [
+ 'lower' => new DINumber( 1 ),
+ 'upper' => new DINumber( 10 ),
+ 'lowerComp' => SMW_CMP_GRTR,
+ 'upperComp' => SMW_CMP_LESS,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ 2, 6 ]
+ ]
+ ];
+
+ #2 Numeric Greater, Less
+ $provider[] = [
+ [
+ 1 => new DINumber( 1 ),
+ 2 => new DINumber( 2 )
+ ],
+ [
+ 'lower' => new DINumber( 1 ),
+ 'upper' => new DINumber( 2 ),
+ 'lowerComp' => SMW_CMP_GRTR,
+ 'upperComp' => SMW_CMP_LESS,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 0,
+ 'subjects' => []
+ ]
+ ];
+
+ #3 Numeric Greater, Not Like
+ $provider[] = [
+ [
+ 1 => new DINumber( 1 ),
+ 2 => new DINumber( 2 ),
+ 3 => new DINumber( 3 )
+ ],
+ [
+ 'lower' => new DINumber( 1 ),
+ 'upper' => new DINumber( 3 ),
+ 'lowerComp' => SMW_CMP_GRTR,
+ 'upperComp' => SMW_CMP_NEQ,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 1,
+ 'subjects' => [ 2 ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function textConjunctionFilterProvider() {
+
+ $property = new DIProperty( 'SomeBlobPropertyToFilter' );
+ $property->setPropertyTypeId( '_txt' );
+
+ #4 Text, Greater Equal, Less Equal
+ $provider[] = [
+ [
+ 'AA' => new DIBlob( 'AA' ),
+ 'BB' => new DIBlob( 'BB' ),
+ 'CC' => new DIBlob( 'CC' )
+ ],
+ [
+ 'lower' => new DIBlob( 'AA' ),
+ 'upper' => new DIBlob( 'BB' ),
+ 'lowerComp' => SMW_CMP_GEQ,
+ 'upperComp' => SMW_CMP_LEQ,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ 'AA', 'BB' ]
+ ]
+ ];
+
+ #5 Text, Like, Like
+ $provider[] = [
+ [
+ 'A' => new DIBlob( 'A' ),
+ 'AA' => new DIBlob( 'AA' ),
+ 'BBA' => new DIBlob( 'BBA' ),
+ 'AAC' => new DIBlob( 'AAC' )
+ ],
+ [
+ 'lower' => new DIBlob( 'A*' ),
+ 'upper' => new DIBlob( 'AA*' ),
+ 'lowerComp' => SMW_CMP_LIKE,
+ 'upperComp' => SMW_CMP_LIKE,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ 'AA', 'AAC' ]
+ ]
+ ];
+
+ #6 Text, Like, Not Like
+ $provider[] = [
+ [
+ 'AABA' => new DIBlob( 'AABA' ),
+ 'AACA' => new DIBlob( 'AACA' ),
+ 'AAAB' => new DIBlob( 'AAAB' ),
+ ],
+ [
+ 'lower' => new DIBlob( 'AA?A' ),
+ 'upper' => new DIBlob( 'AA?B' ),
+ 'lowerComp' => SMW_CMP_LIKE,
+ 'upperComp' => SMW_CMP_NLKE,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ 'AABA', 'AACA' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function dateConjunctionFilterProvider() {
+
+ $property = new DIProperty( 'SomeDatePropertyToFilter' );
+ $property->setPropertyTypeId( '_dat' );
+
+ #7 Date, Greater Equal, Less Equal
+ $provider[] = [
+ [
+ '197001' => new DITime( 1, 1970, 01, 01, 1, 1 ),
+ '197002' => new DITime( 1, 1970, 02, 01, 1, 1 ),
+ '197003' => new DITime( 1, 1970, 03, 01, 1, 1 ),
+ ],
+ [
+ 'lower' => new DITime( 1, 1970, 01, 01 ),
+ 'upper' => new DITime( 1, 1970, 03, 01 ),
+ 'lowerComp' => SMW_CMP_GEQ,
+ 'upperComp' => SMW_CMP_LEQ,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 2,
+ 'subjects' => [ '197001', '197002' ]
+ ]
+ ];
+
+ #7 Date, Greater Equal, Less Equal
+ $provider[] = [
+ [
+ '1970011' => new DITime( 1, 1970, 01, 01, 1, 1 ),
+ '1970012' => new DITime( 1, 1970, 01, 02, 1, 1 ),
+ '1970013' => new DITime( 1, 1970, 01, 03, 1, 1 ),
+ ],
+ [
+ 'lower' => new DITime( 1, 1970, 01, 01, 2 ),
+ 'upper' => new DITime( 1, 1970, 01, 02, 2 ),
+ 'lowerComp' => SMW_CMP_GRTR,
+ 'upperComp' => SMW_CMP_LESS,
+ 'property' => $property,
+ ],
+ [
+ 'count' => 1,
+ 'subjects' => [ '1970012' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ConjunctionQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ConjunctionQueryDBIntegrationTest.php
new file mode 100644
index 00000000..c20548cd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ConjunctionQueryDBIntegrationTest.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ConjunctionQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+ private $queryResultValidator;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->semanticDataFactory = $utilityFactory->newSemanticDataFactory();
+ $this->queryResultValidator = $utilityFactory->newValidatorFactory()->newQueryResultValidator();
+
+ $this->fixturesProvider = $utilityFactory->newFixturesFactory()->newFixturesProvider();
+ $this->fixturesProvider->setupDependencies( $this->getStore() );
+
+ $this->queryParser = ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser();
+ }
+
+ protected function tearDown() {
+
+ $fixturesCleaner = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesCleaner();
+ $fixturesCleaner
+ ->purgeSubjects( $this->subjectsToBeCleared )
+ ->purgeAllKnownFacts();
+
+ parent::tearDown();
+ }
+
+ /**
+ * {{#ask: [[Category:HappyPlaces]] [[LocatedIn.MemberOf::Wonderland]] }}
+ */
+ public function testConjunctionForCategoryAndPropertyChainSubqueryThatComparesEqualToSpecifiedValue() {
+
+ /**
+ * Page ...-neverland annotated with [[LocatedIn::BananaWonderland]]
+ */
+ $semanticDataOfNeverland = $this->semanticDataFactory
+ ->setTitle( __METHOD__ . '-neverland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfNeverland->addPropertyObjectValue(
+ DIProperty::newFromUserLabel( 'LocatedIn' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'BananaWonderland', NS_MAIN )
+ );
+
+ $this->getStore()->updateData( $semanticDataOfNeverland );
+
+ /**
+ * Page ...-dreamland annotated with [[Category:HappyPlaces]] [[LocatedIn::BananaWonderland]]
+ */
+ $semanticDataOfDreamland = $this->semanticDataFactory
+ ->setTitle( __METHOD__ . '-dreamland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfDreamland->addPropertyObjectValue(
+ DIProperty::newFromUserLabel( 'LocatedIn' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'BananaWonderland', NS_MAIN )
+ );
+
+ $semanticDataOfDreamland->addPropertyObjectValue(
+ new DIProperty( '_INST' ),
+ new DIWikiPage( 'HappyPlaces', NS_CATEGORY )
+ );
+
+ $this->getStore()->updateData( $semanticDataOfDreamland );
+
+ /**
+ * Page BananaWonderland annotated with [[MemberOf::Wonderland]]
+ */
+ $semanticDataOfWonderland = $this->semanticDataFactory
+ ->setTitle( 'BananaWonderland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfWonderland->addPropertyObjectValue(
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'Wonderland', NS_MAIN )
+ );
+
+ $this->getStore()->updateData( $semanticDataOfWonderland );
+
+ $someProperty = new SomeProperty(
+ DIProperty::newFromUserLabel( 'LocatedIn' )->setPropertyTypeId( '_wpg' ),
+ new SomeProperty(
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ),
+ new ValueDescription(
+ new DIWikiPage( 'Wonderland', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ), SMW_CMP_EQ
+ )
+ )
+ );
+
+ $classDescription = new ClassDescription(
+ new DIWikiPage( 'HappyPlaces', NS_CATEGORY, '' )
+ );
+
+ $description = new Conjunction();
+ $description->addDescription( $classDescription );
+ $description->addDescription( $someProperty );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:HappyPlaces]] [[LocatedIn.MemberOf::Wonderland]]' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:HappyPlaces]] [[LocatedIn::<q>[[MemberOf::Wonderland]]</q>]]' )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $expectedSubjects = [
+ $semanticDataOfDreamland->getSubject()
+ ];
+
+ $this->assertEquals(
+ 1,
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+
+ $this->subjectsToBeCleared = [
+ $semanticDataOfWonderland->getSubject(),
+ $semanticDataOfDreamland->getSubject(),
+ $semanticDataOfNeverland->getSubject()
+ ];
+ }
+
+ public function testNestedPropertyConjunction() {
+
+ /**
+ * Page annotated with [[Born in::Paris]]
+ */
+ $property = DIProperty::newFromUserLabel( 'Born in' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ . 'PageOughtToBeSelected' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'Paris', NS_MAIN )
+ );
+
+ $expectedSubjects = $semanticData->getSubject();
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->getStore()->updateData(
+ $this->fixturesProvider->getFactsheet( 'Paris' )->asEntity()
+ );
+
+ /**
+ * @query [[Born in::<q>[[Category:City]] [[Located in::France]]</q>]]
+ */
+ $cityCategory = $this->fixturesProvider->getCategory( 'city' )->asSubject();
+ $locatedInProperty = $this->fixturesProvider->getProperty( 'locatedin' );
+
+ $conjunction = new Conjunction( [
+ new ClassDescription( $cityCategory ),
+ new SomeProperty(
+ $locatedInProperty,
+ new ValueDescription(
+ $this->fixturesProvider->getFactsheet( 'France' )->asSubject(),
+ $locatedInProperty )
+ )
+ ]
+ );
+
+ $description = new SomeProperty(
+ $property,
+ $conjunction
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Born in::<q>[[Category:City]] [[Located in::France]]</q>]]' )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ 1,
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DatePropertyValueQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DatePropertyValueQueryDBIntegrationTest.php
new file mode 100644
index 00000000..d797141c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DatePropertyValueQueryDBIntegrationTest.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWExporter as Exporter;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class DatePropertyValueQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+ private $dataValueFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+
+ $this->fixturesProvider = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesProvider();
+ $this->fixturesProvider->setupDependencies( $this->getStore() );
+ }
+
+ protected function tearDown() {
+
+ $fixturesCleaner = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesCleaner();
+
+ $fixturesCleaner
+ ->purgeAllKnownFacts()
+ ->purgeSubjects( $this->subjectsToBeCleared );
+
+ parent::tearDown();
+ }
+
+ public function testUserDefinedDateProperty() {
+
+ $property = new DIProperty( 'SomeDateProperty' );
+ $property->setPropertyTypeId( '_dat' );
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ '1 January 1970'
+ );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue( $dataValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ Exporter::getInstance()->clear();
+
+ $this->assertArrayHasKey(
+ $property->getKey(),
+ $this->getStore()->getSemanticData( $semanticData->getSubject() )->getProperties()
+ );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->queryResultValidator->assertThatQueryResultContains(
+ $dataValue,
+ $queryResult
+ );
+
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+ }
+
+ /**
+ * #576
+ */
+ public function testSortableDateQuery() {
+
+ $this->getStore()->updateData(
+ $this->fixturesProvider->getFactsheet( 'Berlin' )->asEntity()
+ );
+
+ // #576 introduced resource caching, therefore make sure that the
+ // instance is cleared after data have been created before further
+ // tests are carried out
+ Exporter::getInstance()->clear();
+
+ /**
+ * @query {{#ask: [[Founded::SomeDistinctValue]] }}
+ */
+ $foundedValue = $this->fixturesProvider->getFactsheet( 'Berlin' )->getFoundedValue();
+
+ $description = new SomeProperty(
+ $foundedValue->getProperty(),
+ new ValueDescription( $foundedValue->getDataItem(), null, SMW_CMP_EQ )
+ );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $foundedValue->getProperty() );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $query->sortkeys = [
+ $foundedValue->getProperty()->getLabel() => 'ASC'
+ ];
+
+ // Be aware of
+ // Virtuoso 22023 Error SR353: Sorted TOP clause specifies more then
+ // 10001 rows to sort. Only 10000 are allowed. Either decrease the
+ // offset and/or row count or use a scrollable cursor
+ $query->setLimit( 100 );
+
+ $query->setExtraPrintouts( [
+ new PrintRequest( PrintRequest::PRINT_THIS, '' ),
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ ] );
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $this->fixturesProvider->getFactsheet( 'Berlin' )->asSubject(),
+ $queryResult
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DisjunctionQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DisjunctionQueryDBIntegrationTest.php
new file mode 100644
index 00000000..8623baf3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/DisjunctionQueryDBIntegrationTest.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class DisjunctionQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+ private $queryResultValidator;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->queryParser = ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser();
+
+ // $this->getStore()->getSparqlDatabase()->deleteAll();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * {{#ask: [[Category:WickedPlaces]] OR [[LocatedIn.MemberOf::Wonderland]] }}
+ */
+ public function testDisjunctionSubqueryForPageTypePropertyChainThatComparesEqualToValue() {
+
+ /**
+ * Page ...-dangerland annotated with [[Category:WickedPlaces]]
+ */
+ $semanticDataOfDangerland = $this->semanticDataFactory
+ ->setTitle( __METHOD__ . '-dangerland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfDangerland->addPropertyObjectValue(
+ new DIProperty( '_INST' ),
+ new DIWikiPage( 'WickedPlaces', NS_CATEGORY )
+ );
+
+ $this->subjectsToBeCleared[] = $semanticDataOfDangerland->getSubject();
+ $this->getStore()->updateData( $semanticDataOfDangerland );
+
+ /**
+ * Page ...-dreamland annotated with [[LocatedIn::BananaWonderland]]
+ */
+ $semanticDataOfDreamland = $this->semanticDataFactory
+ ->setTitle( __METHOD__ . '-dreamland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfDreamland->addPropertyObjectValue(
+ DIProperty::newFromUserLabel( 'LocatedIn' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'BananaWonderland', NS_MAIN )
+ );
+
+ $this->subjectsToBeCleared[] = $semanticDataOfDreamland->getSubject();
+ $this->getStore()->updateData( $semanticDataOfDreamland );
+
+ /**
+ * Page BananaWonderland annotated with [[MemberOf::Wonderland]]
+ */
+ $semanticDataOfWonderland = $this->semanticDataFactory
+ ->setTitle( 'BananaWonderland' )
+ ->newEmptySemanticData();
+
+ $semanticDataOfWonderland->addPropertyObjectValue(
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'Wonderland', NS_MAIN )
+ );
+
+ $this->subjectsToBeCleared[] = $semanticDataOfWonderland->getSubject();
+ $this->getStore()->updateData( $semanticDataOfWonderland );
+
+ /**
+ * Query with [[Category:WickedPlaces]] OR [[LocatedIn.MemberOf::Wonderland]]
+ */
+ $someProperty = new SomeProperty(
+ DIProperty::newFromUserLabel( 'LocatedIn' )->setPropertyTypeId( '_wpg' ),
+ new SomeProperty(
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ),
+ new ValueDescription(
+ new DIWikiPage( 'Wonderland', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'MemberOf' )->setPropertyTypeId( '_wpg' ), SMW_CMP_EQ
+ )
+ )
+ );
+
+ $classDescription = new ClassDescription(
+ new DIWikiPage( 'WickedPlaces', NS_CATEGORY, '' )
+ );
+
+ $description = new Disjunction();
+ $description->addDescription( $classDescription );
+ $description->addDescription( $someProperty );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:WickedPlaces]] OR [[LocatedIn.MemberOf::Wonderland]]' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:WickedPlaces]] OR [[LocatedIn::<q>[[MemberOf::Wonderland]]</q>]]' )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $expectedSubjects = [
+ $semanticDataOfDreamland->getSubject(),
+ $semanticDataOfDangerland->getSubject()
+ ];
+
+ $this->assertEquals(
+ 2,
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+ }
+
+ public function testSubqueryDisjunction() {
+
+ $property = new DIProperty( 'HasSomeProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ /**
+ * Page annotated with [[HasSomeProperty:Foo]]
+ */
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ . '1' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $expectedSubjects[] = $semanticData->getSubject();
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+
+ $this->getStore()->updateData( $semanticData );
+
+ /**
+ * Page annotated with [[HasSomeProperty:Bar]]
+ */
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ . '2' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'Bar', NS_MAIN )
+ );
+
+ $expectedSubjects[] = $semanticData->getSubject();
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+
+ $this->getStore()->updateData( $semanticData );
+
+ /**
+ * Query with [[HasSomeProperty::Foo||Bar]]
+ */
+ $disjunction = new Disjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ), $property ),
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ), $property )
+ ] );
+
+ $description = new SomeProperty(
+ $property,
+ $disjunction
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[HasSomeProperty::Foo||Bar]]' )
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ 2,
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/GeneralQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/GeneralQueryDBIntegrationTest.php
new file mode 100644
index 00000000..222207a8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/GeneralQueryDBIntegrationTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class GeneralQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $subject;
+
+ private $dataValueFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+
+ $this->testEnvironment->addConfiguration( 'smwgQueryResultCacheType', false );
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ public function testPropertyBeforeAfterDataRemoval() {
+
+ $property = new DIProperty( 'SomePagePropertyBeforeAfter' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $this->assertEmpty(
+ $this->searchForResultsThatCompareEqualToOnlySingularPropertyOf( $property )->getResults()
+ );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByItem( $semanticData->getSubject(), $property )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->queryResultValidator->assertThatQueryResultContains(
+ $semanticData->getSubject(),
+ $this->searchForResultsThatCompareEqualToOnlySingularPropertyOf( $property )
+ );
+
+ $this->assertNotEmpty(
+ $this->searchForResultsThatCompareEqualToOnlySingularPropertyOf( $property )->getResults()
+ );
+
+ $this->getStore()->clearData( $semanticData->getSubject() );
+
+ $this->assertEmpty(
+ $this->searchForResultsThatCompareEqualToOnlySingularPropertyOf( $property )->getResults()
+ );
+
+ $this->subjectsToBeCleared = [
+ $semanticData->getSubject()
+ ];
+ }
+
+ public function testUserDefinedPropertyUsedForInvalidValueAssignment() {
+
+ $property = new DIProperty( 'SomePropertyWithInvalidValueAssignment' );
+ $property->setPropertyTypeId( '_tem' );
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty( $property, '1 Jan 1970' );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+ $semanticData->addDataValue( $dataValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertEquals(
+ 0,
+ $this->searchForResultsThatCompareEqualToOnlySingularPropertyOf( $property )->getCount()
+ );
+
+ $this->subjectsToBeCleared = [
+ $semanticData->getSubject()
+ ];
+ }
+
+ private function searchForResultsThatCompareEqualToOnlySingularPropertyOf( DIProperty $property ) {
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ return $this->getStore()->getQueryResult( $query );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/InversePropertyRelationshipDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/InversePropertyRelationshipDBIntegrationTest.php
new file mode 100644
index 00000000..f45f5f11
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/InversePropertyRelationshipDBIntegrationTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+
+/**
+ * @see http://semantic-mediawiki.org/wiki/Inverse_Properties
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class InversePropertyRelationshipDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+
+ private $semanticDataFactory;
+ private $dataValueFactory;
+
+ private $queryResultValidator;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->queryParser = ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * {{#ask: [[-Has mother::Michael]] }}
+ */
+ public function testParentChildInverseRelationshipQuery() {
+
+ $semanticData = $this->semanticDataFactory
+ ->setTitle( 'Michael' )
+ ->newEmptySemanticData();
+
+ $semanticData->addDataValue(
+ $this->newDataValueForPagePropertyValue( 'Has mother', 'Carol' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $description = new SomeProperty(
+ DIProperty::newFromUserLabel( 'Has mother', true )->setPropertyTypeId( '_wpg' ),
+ new ValueDescription(
+ new DIWikiPage( 'Michael', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Has mother', true )->setPropertyTypeId( '_wpg' ),
+ SMW_CMP_EQ
+ )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[-Has mother::Michael]]' )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ 1,
+ $queryResult->getCount()
+ );
+
+ $expectedSubjects = [
+ new DIWikiPage( 'Carol', NS_MAIN, '' )
+ ];
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $queryResult
+ );
+
+ $this->subjectsToBeCleared = [
+ $semanticData->getSubject()
+ ];
+ }
+
+ private function newDataValueForPagePropertyValue( $property, $value ) {
+
+ $property = DIProperty::newFromUserLabel( $property );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $dataItem = new DIWikiPage( $value, NS_MAIN, '' );
+
+ return $this->dataValueFactory->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NamespaceQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NamespaceQueryDBIntegrationTest.php
new file mode 100644
index 00000000..122134bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NamespaceQueryDBIntegrationTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class NamespaceQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $fixturesProvider;
+ private $semanticDataFactory;
+
+ private $queryResultValidator;
+ private $subjects = [];
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+
+ $this->fixturesProvider = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesProvider();
+ $this->fixturesProvider->setupDependencies( $this->getStore() );
+ }
+
+ protected function tearDown() {
+
+ $fixturesCleaner = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesCleaner();
+ $fixturesCleaner
+ ->purgeSubjects( $this->subjects )
+ ->purgeAllKnownFacts();
+
+ parent::tearDown();
+ }
+
+ public function testConjunctiveNamespaceQueryThatIncludesSubobject() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+ $this->subjects[] = $semanticData->getSubject();
+
+ $factsheet = $this->fixturesProvider->getFactsheet( 'Berlin' );
+ $factsheet->setTargetSubject( $semanticData->getSubject() );
+
+ $demographicsSubobject = $factsheet->getDemographics();
+ $this->subjects[] = $demographicsSubobject->getSemanticData()->getSubject();
+
+ $semanticData->addPropertyObjectValue(
+ $demographicsSubobject->getProperty(),
+ $demographicsSubobject->getContainer()
+ );
+
+ $populationValue = $factsheet->getPopulationValue();
+ $semanticData->addDataValue( $populationValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $someProperty = new SomeProperty(
+ $populationValue->getProperty(),
+ new ValueDescription( $populationValue->getDataItem(), null, SMW_CMP_EQ )
+ );
+
+ /**
+ * @query [[Population::SomeDistinctPopulationValue]][[:+]]
+ */
+ $description = new Conjunction();
+ $description->addDescription( $someProperty );
+ $description->addDescription( new NamespaceDescription( NS_MAIN ) );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ 2,
+ $queryResult->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $this->subjects,
+ $queryResult
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NullQueryResultTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NullQueryResultTest.php
new file mode 100644
index 00000000..7e977e34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/NullQueryResultTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\ValueDescription;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class NullQueryResultTest extends \PHPUnit_Framework_TestCase {
+
+ public function testNullQueryResult() {
+
+ $term = '[[Some_string_to_query]]';
+
+ $description = new ValueDescription(
+ new DIWikiPage( $term, NS_MAIN ),
+ null
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $description = $query->getDescription();
+
+ $namespacesDisjunction = new Disjunction(
+ array_map( function ( $ns ) {
+ return new NamespaceDescription( $ns );
+ }, [ NS_MAIN ] )
+ );
+
+ $description = new Conjunction( [ $description, $namespacesDisjunction ] );
+
+ $query->setDescription( $description );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ ApplicationFactory::getInstance()->getStore()->getQueryResult( $query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ProfileAnnotators/ProfileAnnotatorWithQueryProcessorIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ProfileAnnotators/ProfileAnnotatorWithQueryProcessorIntegrationTest.php
new file mode 100644
index 00000000..6f49266f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/ProfileAnnotators/ProfileAnnotatorWithQueryProcessorIntegrationTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Tests\Integration\Query\ProfileAnnotators;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Tests\TestEnvironment;
+use SMWQueryProcessor;
+
+/**
+ * @covers \SMWQueryProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ProfileAnnotatorWithQueryProcessorIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testCreateProfile( array $rawParams, array $expected ) {
+
+ list( $query, $formattedParams ) = SMWQueryProcessor::getQueryAndParamsFromFunctionParams(
+ $rawParams,
+ SMW_OUTPUT_WIKI,
+ SMWQueryProcessor::INLINE_QUERY,
+ false
+ );
+
+ $query->setContextPage(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $profileAnnotatorFactory = ApplicationFactory::getInstance()->getQueryFactory()->newProfileAnnotatorFactory();
+
+ $profileAnnotator = $profileAnnotatorFactory->newProfileAnnotator(
+ $query,
+ $formattedParams['format']->getValue()
+ );
+
+ $profileAnnotator->addAnnotation();
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $profileAnnotator->getSemanticData()
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $profileAnnotator->getSemanticData()
+ );
+ }
+
+ public function queryDataProvider() {
+
+ $categoryNS = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $provider = [];
+
+ // #0
+ // {{#ask: [[Modification date::+]]
+ // |?Modification date
+ // |format=list
+ // }}
+ $provider[] = [
+ [
+ '',
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'list', 1, 1, '[[Modification date::+]]' ]
+ ]
+ ];
+
+ // #1
+ // {{#ask: [[Modification date::+]][[Category:Foo]]
+ // |?Modification date
+ // |?Has title
+ // |format=list
+ // }}
+ $provider[] = [
+ [
+ '',
+ '[[Modification date::+]][[Category:Foo]]',
+ '?Modification date',
+ '?Has title',
+ 'format=list'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'list', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
+ ]
+ ];
+
+ // #2 Unknown format, default table
+ // {{#ask: [[Modification date::+]][[Category:Foo]]
+ // |?Modification date
+ // |?Has title
+ // |format=bar
+ // }}
+ $provider[] = [
+ [
+ '',
+ '[[Modification date::+]][[Category:Foo]]',
+ '?Modification date',
+ '?Has title',
+ 'format=bar'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'table', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QueryResultPrinterIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QueryResultPrinterIntegrationTest.php
new file mode 100644
index 00000000..7d36ff95
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QueryResultPrinterIntegrationTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\Integration\Query\ResultPrinter;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QueryResultPrinterIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjects = [];
+ private $pageCreator;
+
+ private $stringBuilder;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->pageCreator = $utilityFactory->newPageCreator();
+ $this->stringBuilder = $utilityFactory->newStringBuilder();
+ $this->stringValidator = $utilityFactory->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ UtilityFactory::getInstance()->newPageDeleter()->doDeletePoolOfPages( $this->subjects );
+ parent::tearDown();
+ }
+
+ /**
+ * @see #755
+ * @query {{#ask: [[Modification date::+]]|limit=0|searchlabel= }}
+ */
+ public function testLimitNullWithEmptySearchlabel() {
+
+ foreach ( [ 'Foo', 'Bar', 'テスト' ] as $title ) {
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( $title ) )
+ ->doEdit( '[[Category:LimitNullForEmptySearchlabel]]');
+
+ $this->subjects[] = $this->pageCreator->getPage();
+ }
+
+ $this->stringBuilder
+ ->addString( '{{#ask:' )
+ ->addString( '[[Modification date::+]][[Category:LimitNullForEmptySearchlabel]]' )
+ ->addString( '|limit=0' )
+ ->addString( '|searchlabel=' )
+ ->addString( '}}' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ ) )
+ ->doEdit( $this->stringBuilder->getString() );
+
+ $this->subjects[] = $this->pageCreator->getPage();
+
+ $parserOutput = $this->pageCreator->getEditInfo()->output;
+
+ $this->assertNotContains(
+ '[[Special:Ask/-5B-5BModification-20date::+-5D-5D-5B-5BCategory:LimitNullForEmptySearchlabel-5D-5D/searchlabel=/offset=0|]]',
+ $parserOutput->getText()
+ );
+ }
+
+ /**
+ * @see #755
+ * @query {{#ask: [[Modification date::+]]|limit=0|searchlabel=do something }}
+ */
+ public function testLimitNullWithDescriptiveSearchlabel() {
+
+ foreach ( [ 'Foo', 'Bar', 'テスト' ] as $title ) {
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( $title ) )
+ ->doEdit( '[[Category:LimitNullForNotEmptySearchlabel]]');
+
+ $this->subjects[] = $this->pageCreator->getPage();
+ }
+
+ $this->stringBuilder
+ ->addString( '{{#ask:' )
+ ->addString( '[[Modification date::+]][[Category:LimitNullForNotEmptySearchlabel]]' )
+ ->addString( '|limit=0' )
+ ->addString( '|searchlabel=do something' )
+ ->addString( '}}' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( __METHOD__ ) )
+ ->doEdit( $this->stringBuilder->getString() );
+
+ $this->subjects[] = $this->pageCreator->getPage();
+
+ $parserOutput = $this->pageCreator->getEditInfo()->output;
+
+ $this->assertContains(
+ 'do something',
+ $parserOutput->getText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QuerySourceIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QuerySourceIntegrationTest.php
new file mode 100644
index 00000000..2b99583b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/QuerySourceIntegrationTest.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+use SMWQueryProcessor;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class QuerySourceIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $testEnvironment;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgQuerySources',
+ [
+ 'foo' => 'SMW\Tests\Utils\Mock\FakeQueryStore',
+ 'foobar' => 'SMW\Tests\Integration\Query\AnotherFakeQueryStoreWhichDoesNotImplentTheQueryEngineInterface',
+ 'bar' => 'SMW\Tests\NonExistentQueryStore',
+ ]
+ );
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testQueryProcessorWithDefaultSource() {
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $rawParams = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list',
+ 'source=default'
+ ];
+
+ $this->assertInternalType(
+ 'string',
+ $this->makeQueryResultFromRawParameters( $rawParams )
+ );
+ }
+
+ public function testQueryProcessorWithValidSource() {
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $rawParams = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list',
+ 'source=foo'
+ ];
+
+ $this->assertInternalType(
+ 'string',
+ $this->makeQueryResultFromRawParameters( $rawParams )
+ );
+ }
+
+ public function testQueryProcessorWithInvalidSourceSwitchesToDefault() {
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $rawParams = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list',
+ 'source=bar'
+ ];
+
+ $this->assertInternalType(
+ 'string',
+ $this->makeQueryResultFromRawParameters( $rawParams )
+ );
+ }
+
+ public function testQuerySourceOnCount() {
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getCountValue' )
+ ->will( $this->returnValue( 42 ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $rawParams = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=count',
+ 'source=foo'
+ ];
+
+ $this->assertInternalType(
+ 'string',
+ $this->makeQueryResultFromRawParameters( $rawParams )
+ );
+ }
+
+ public function testQueryProcessorWithInvalidSource() {
+
+ $rawParams = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list',
+ 'source=foobar'
+ ];
+
+ $this->setExpectedException( 'RuntimeException' );
+ $this->makeQueryResultFromRawParameters( $rawParams );
+ }
+
+ protected function makeQueryResultFromRawParameters( $rawParams ) {
+
+ list( $query, $params ) = SMWQueryProcessor::getQueryAndParamsFromFunctionParams(
+ $rawParams,
+ SMW_OUTPUT_WIKI,
+ SMWQueryProcessor::INLINE_QUERY,
+ false
+ );
+
+ return SMWQueryProcessor::getResultFromQuery(
+ $query,
+ $params,
+ SMW_OUTPUT_WIKI,
+ SMWQueryProcessor::INLINE_QUERY
+ );
+ }
+
+}
+
+class AnotherFakeQueryStoreWhichDoesNotImplentTheQueryEngineInterface {
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/RandomQueryResultOrderIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/RandomQueryResultOrderIntegrationTest.php
new file mode 100644
index 00000000..aeee31c4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/RandomQueryResultOrderIntegrationTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class RandomQueryResultOrderIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $fixturesProvider;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->fixturesProvider = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesProvider();
+ $this->fixturesProvider->setupDependencies( $this->getStore() );
+ }
+
+ protected function tearDown() {
+
+ $fixturesCleaner = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesCleaner();
+ $fixturesCleaner->purgeAllKnownFacts();
+
+ parent::tearDown();
+ }
+
+ public function testRandomOrder() {
+
+ $factsheet = $this->fixturesProvider->getFactsheet( 'Berlin' );
+ $populationValue = $factsheet->getPopulationValue();
+
+ $this->getStore()->updateData( $factsheet->asEntity() );
+
+ /**
+ * @query [[Population::+]]
+ */
+ $property = $this->fixturesProvider->getProperty( 'Population' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+ $query->sort = true;
+ $query->sortkeys = [ 'Population' => 'RANDOM' ];
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->assertEquals(
+ 3,
+ $queryResult->getCount()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SortableQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SortableQueryDBIntegrationTest.php
new file mode 100644
index 00000000..65545853
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SortableQueryDBIntegrationTest.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SortableQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ public function testDefaultSortedQueryResult() {
+
+ $expectedSubjects = [
+ new DIWikiPage( 'AA', NS_MAIN ),
+ new DIWikiPage( 'AB', NS_MAIN ),
+ new DIWikiPage( 'AC', NS_MAIN )
+ ];
+
+ $property = new DIProperty( 'SomePageProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $query = $this->createQueryForSamplePagesThatContain( $property, $expectedSubjects );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $expectedSubjects,
+ $this->getStore()->getQueryResult( $query )
+ );
+ }
+
+ /**
+ * Set limit to avoid:
+ *
+ * Virtuoso 22023 Error SR353: Sorted TOP clause specifies more then 10001
+ * rows to sort. Only 10000 are allowed.
+ *
+ * @see Virtuoso MaxSortedTopRows setting
+ */
+ public function testAscendingOrderedQueryResult() {
+
+ $expectedSubjects = [
+ new DIWikiPage( 'AA', NS_MAIN ),
+ new DIWikiPage( 'AB', NS_MAIN ),
+ new DIWikiPage( 'AC', NS_MAIN )
+ ];
+
+ $property = new DIProperty( 'SomeAscendingPageProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $query = $this->createQueryForSamplePagesThatContain( $property, $expectedSubjects );
+
+ $query->sort = true;
+ $query->sortkeys = [ $property->getKey() => 'ASC' ];
+ $query->setUnboundLimit( 50 );
+
+ $this->assertResultOrder(
+ $expectedSubjects,
+ $this->getStore()->getQueryResult( $query )->getResults()
+ );
+ }
+
+ public function testDescendingOrderedQueryResult() {
+
+ $expectedSubjects = [
+ new DIWikiPage( 'AA', NS_MAIN ),
+ new DIWikiPage( 'AB', NS_MAIN ),
+ new DIWikiPage( 'AC', NS_MAIN )
+ ];
+
+ $property = new DIProperty( 'SomeDescendingPageProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $query = $this->createQueryForSamplePagesThatContain( $property, $expectedSubjects );
+
+ $query->sort = true;
+ $query->sortkeys = [ $property->getKey() => 'DESC' ];
+ $query->setUnboundLimit( 50 );
+
+ $this->assertResultOrder(
+ array_reverse( $expectedSubjects ),
+ $this->getStore()->getQueryResult( $query )->getResults()
+ );
+ }
+
+ public function createQueryForSamplePagesThatContain( $property, array &$expectedSubjects ) {
+
+ foreach ( $expectedSubjects as $key => $expectedSubject ) {
+
+ $subjectTitle = $expectedSubject->getTitle()->getText() . '-' . __METHOD__;
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( $subjectTitle );
+
+ $semanticData->addPropertyObjectValue( $property, $expectedSubject );
+ $this->subjectsToBeCleared[] = $semanticData->getSubject();
+ $expectedSubjects[$key] = $semanticData->getSubject();
+
+ $this->getStore()->updateData( $semanticData );
+ }
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ return $query;
+ }
+
+ private function assertResultOrder( $expected, $results ) {
+
+ $hasSameOrder = true;
+
+ foreach ( $results as $key => $dataItem ) {
+ if ( $expected[$key]->getHash() !== $dataItem->getHash() ) {
+ $hasSameOrder = false;
+ }
+ }
+
+ $this->assertTrue(
+ $hasSameOrder,
+ 'Failed asserting that results have expected order.'
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SpecialCharactersQueryDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SpecialCharactersQueryDBIntegrationTest.php
new file mode 100644
index 00000000..5237498d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/Query/SpecialCharactersQueryDBIntegrationTest.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace SMW\Tests\Integration\Query;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMW\Subobject;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+use SMWPropertyValue as PropertyValue;
+use SMWQuery as Query;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group semantic-mediawiki-query
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SpecialCharactersQueryDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjectsToBeCleared = [];
+ private $semanticDataFactory;
+
+ private $dataValueFactory;
+ private $queryResultValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->queryResultValidator = UtilityFactory::getInstance()->newValidatorFactory()->newQueryResultValidator();
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ }
+
+ protected function tearDown() {
+
+ foreach ( $this->subjectsToBeCleared as $subject ) {
+
+ if ( $subject->getTitle() === null ) {
+ continue;
+ }
+
+ $this->getStore()->deleteSubject( $subject->getTitle() );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider specialCharactersNameProvider
+ */
+ public function testSpecialCharactersInQuery( $subject, $subobjectId, $property, $dataItem ) {
+
+ $dataValue = $this->dataValueFactory->newDataValueByItem(
+ $dataItem,
+ $property
+ );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( $subject );
+ $semanticData->addDataValue( $dataValue );
+
+ $subobject = new Subobject( $semanticData->getSubject()->getTitle() );
+ $subobject->setEmptyContainerForId( $subobjectId );
+
+ $subobject->addDataValue( $dataValue );
+ $semanticData->addSubobject( $subobject );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( $property );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ [
+ $semanticData->getSubject(),
+ $subobject->getSubject() ],
+ $this->getStore()->getQueryResult( $query )
+ );
+
+ $this->queryResultValidator->assertThatQueryResultContains(
+ $dataValue,
+ $this->getStore()->getQueryResult( $query )
+ );
+
+ $this->subjectsToBeCleared = [
+ $semanticData->getSubject(),
+ $subobject->getSubject(),
+ $property->getDIWikiPage()
+ ];
+ }
+
+ public function specialCharactersNameProvider() {
+
+ $provider[] = [
+ '特殊文字',
+ 'Nuñez',
+ DIProperty::newFromUserLabel( '特殊文字' )->setPropertyTypeId( '_txt' ),
+ new DIBlob( 'Nuñez' )
+ ];
+
+ $provider[] = [
+ '特殊字符',
+ '^[0-9]*$',
+ DIProperty::newFromUserLabel( '特殊字符' )->setPropertyTypeId( '_txt' ),
+ new DIBlob( '^[0-9]*$' )
+ ];
+
+ $provider[] = [
+ 'Caractères spéciaux',
+ 'Caractères_spéciaux',
+ DIProperty::newFromUserLabel( 'Caractères spéciaux' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( 'âêîôûëïçé', NS_MAIN )
+ ];
+
+ $provider[] = [
+ 'áéíóúñÑü¡¿',
+ 'áéíóúñÑü¡¿',
+ DIProperty::newFromUserLabel( 'áéíóúñÑü¡¿' )->setPropertyTypeId( '_num' ),
+ new DINumber( 8888 )
+ ];
+
+ $provider[] = [
+ 'Foo',
+ '{({[[&,,;-]]})}',
+ DIProperty::newFromUserLabel( '{({[[&,,;-]]})}' )->setPropertyTypeId( '_wpg' ),
+ new DIWikiPage( '{({[[&,,;-]]})}', NS_MAIN )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/QueryResultQueryProcessorIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/QueryResultQueryProcessorIntegrationTest.php
new file mode 100644
index 00000000..977382df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/QueryResultQueryProcessorIntegrationTest.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWQuery as Query;
+use SMWQueryProcessor as QueryProcessor;
+
+/**
+ * @covers \SMWQueryResult
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryResultQueryProcessorIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $subjects = [];
+ private $semanticDataFactory;
+
+ private $dataValueFactory;
+ private $queryResultValidator;
+
+ private $fixturesProvider;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->semanticDataFactory = $utilityFactory->newSemanticDataFactory();
+ $this->queryResultValidator = $utilityFactory->newValidatorFactory()->newQueryResultValidator();
+
+ $this->fixturesProvider = $utilityFactory->newFixturesFactory()->newFixturesProvider();
+ $this->fixturesProvider->setupDependencies( $this->getStore() );
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->queryParser = ApplicationFactory::getInstance()->getQueryFactory()->newQueryParser();
+ }
+
+ protected function tearDown() {
+
+ $fixturesCleaner = UtilityFactory::getInstance()->newFixturesFactory()->newFixturesCleaner();
+
+ $fixturesCleaner
+ ->purgeSubjects( $this->subjects )
+ ->purgeAllKnownFacts();
+
+ parent::tearDown();
+ }
+
+ public function testUriQueryFromRawParameters() {
+
+ $property = $this->fixturesProvider->getProperty( 'url' );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+ $this->subjects[] = $semanticData->getSubject();
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ 'http://example.org/api.php?action=Foo'
+ );
+
+ $semanticData->addDataValue( $dataValue );
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ 'http://example.org/Bar 42'
+ );
+
+ $semanticData->addDataValue( $dataValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ /**
+ * @query [[Url::http://example.org/api.php?action=Foo]][[Url::http://example.org/Bar 42]]
+ */
+ $rawParams = [
+ '[[Url::http://example.org/api.php?action=Foo]][[Url::http://example.org/Bar 42]]',
+ '?Url',
+ 'limit=1'
+ ];
+
+ list( $queryString, $parameters, $printouts ) = QueryProcessor::getComponentsFromFunctionParams(
+ $rawParams,
+ false
+ );
+
+ $description = $this->queryParser->getQueryDescription( $queryString );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $queryResult = $this->getStore()->getQueryResult( $query );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $semanticData->getSubject(),
+ $queryResult
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testCanConstructor( array $test ) {
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $this->getQueryResultFor( $test['query'] )
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testToArray( array $test, array $expected ) {
+
+ $instance = $this->getQueryResultFor( $test['query'] );
+ $results = $instance->toArray();
+
+ $this->assertEquals( $expected[0], $results['printrequests'][0] );
+ $this->assertEquals( $expected[1], $results['printrequests'][1] );
+ }
+
+ private function getQueryResultFor( $queryString ) {
+
+ list( $query, $formattedParams ) = QueryProcessor::getQueryAndParamsFromFunctionParams(
+ $queryString,
+ SMW_OUTPUT_WIKI,
+ QueryProcessor::INLINE_QUERY,
+ false
+ );
+
+ return $this->getStore()->getQueryResult( $query );
+ }
+
+ public function queryDataProvider() {
+
+ $provider = [];
+
+ // #1 Standard query
+ $provider[] =[
+ [ 'query' => [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'limit=10'
+ ]
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ],
+ [
+ 'label'=> 'Modification date',
+ 'typeid' => '_dat',
+ 'mode' => 1,
+ 'format' => '',
+ 'key' => '_MDAT',
+ 'redi' => ''
+ ]
+ ]
+ ];
+
+ // #2 Query containing a printrequest formatting
+ $provider[] =[
+ [ 'query' => [
+ '[[Modification date::+]]',
+ '?Modification date#ISO',
+ 'limit=10'
+ ]
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ],
+ [
+ 'label'=> 'Modification date',
+ 'typeid' => '_dat',
+ 'mode' => 1,
+ 'format' => 'ISO',
+ 'key' => '_MDAT',
+ 'redi' => ''
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/RdfFileResourceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/RdfFileResourceTest.php
new file mode 100644
index 00000000..131d3c82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/RdfFileResourceTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMWExportController as ExportController;
+use SMWRDFXMLSerializer as RDFXMLSerializer;
+use Title;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class RdfFileResourceTest extends MwDBaseUnitTestCase {
+
+ private $fixturesFileProvider;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+
+ $this->fixturesFileProvider = $utilityFactory->newFixturesFactory()->newFixturesFileProvider();
+ $this->stringValidator = $utilityFactory->newValidatorFactory()->newStringValidator();
+
+ $this->testEnvironment->withConfiguration( [
+ 'smwgPageSpecialProperties' => [ '_MEDIA', '_MIME' ],
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true, NS_FILE => true ],
+ 'smwgMainCacheType' => 'hash',
+ 'smwgExportBCAuxiliaryUse' => true
+ ] );
+
+ // Ensure that the DB creates the extra tables for MEDIA/MINE
+ $this->getStore()->clear();
+ $this->getStore()->setupStore( false );
+
+ // MW GLOBALS to be restored after the test
+ $this->testEnvironment->withConfiguration( [
+ 'wgEnableUploads' => true,
+ 'wgFileExtensions' => [ 'txt' ],
+ 'wgVerifyMimeType' => true
+ ] );
+
+ \SMWExporter::clear();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testFileUploadForDummyTextFile() {
+ Localizer::getInstance()->clear();
+
+ $subject = new DIWikiPage( 'RdfLinkedFile.txt', NS_FILE );
+ $fileNS = Localizer::getInstance()->getNamespaceTextById( NS_FILE );
+
+ $dummyTextFile = $this->fixturesFileProvider->newUploadForDummyTextFile(
+ 'RdfLinkedFile.txt'
+ );
+
+ $dummyTextFile->doUpload(
+ '[[HasFile::File:RdfLinkedFile.txt]]'
+ );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $exportController = new ExportController( new RDFXMLSerializer() );
+ $exportController->enableBacklinks( false );
+
+ ob_start();
+
+ $exportController->printPages(
+ [ $subject->getTitle()->getPrefixedDBKey() ]
+ );
+
+ $output = ob_get_clean();
+
+ $expected = [
+ "<rdfs:label>{$fileNS}:RdfLinkedFile.txt</rdfs:label>",
+ '<swivt:file rdf:resource="' . $dummyTextFile->getLocalFile()->getFullURL() . '"/>',
+ '<property:Media_type-23aux rdf:datatype="http://www.w3.org/2001/XMLSchema#string">TEXT</property:Media_type-23aux>',
+ '<property:MIME_type-23aux rdf:datatype="http://www.w3.org/2001/XMLSchema#string">text/plain</property:MIME_type-23aux>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $output
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/QueryResultLookupWithoutBaseStoreIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/QueryResultLookupWithoutBaseStoreIntegrationTest.php
new file mode 100644
index 00000000..af941ae4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/QueryResultLookupWithoutBaseStoreIntegrationTest.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace SMW\Tests\Integration\SPARQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\NamespaceDescription as NamespaceDescription;
+use SMW\Query\Language\SomeProperty as SomeProperty;
+use SMW\Query\Language\ThingDescription as ThingDescription;
+use SMW\Query\Language\ValueDescription as ValueDescription;
+use SMW\SPARQLStore\SPARQLStore;
+use SMW\Subobject;
+use SMW\Tests\Utils\SemanticDataFactory;
+use SMW\Tests\Utils\Validators\QueryResultValidator;
+use SMWDINumber as DINumber;
+use SMWQuery as Query;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryResultLookupWithoutBaseStoreIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $store = null;
+ private $queryResultValidator;
+ private $semanticDataFactory;
+ private $dataValueFactory;
+
+ protected function setUp() {
+
+ $this->store = ApplicationFactory::getInstance()->getStore();
+
+ if ( !$this->store instanceof SPARQLStore ) {
+ $this->markTestSkipped( "Requires a SPARQLStore instance" );
+ }
+
+ $repositoryConnection = $this->store->getConnection( 'sparql' );
+ $repositoryConnection->setConnectionTimeout( 5 );
+
+ if ( !$repositoryConnection->ping() ) {
+ $this->markTestSkipped( "Can't connect to the SPARQL repository" );
+ }
+
+ $repositoryConnection->deleteAll();
+
+ $this->queryResultValidator = new QueryResultValidator();
+ $this->semanticDataFactory = new SemanticDataFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ ApplicationFactory::getInstance()->singleton( 'CachedQueryResultPrefetcher' )->disableCache();
+ }
+
+ public function testQuerySubjects_afterUpdatingSemanticData() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $this->store->doSparqlDataUpdate( $semanticData );
+
+ $query = new Query(
+ new ValueDescription( $semanticData->getSubject() ),
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $semanticData->getSubject(),
+ $this->store->getQueryResult( $query )
+ );
+ }
+
+ public function testQueryZeroResults_afterSubjectRemoval() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $property = new DIProperty( __METHOD__ );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByProperty( $property, 'Bar' )
+ );
+
+ $this->store->doSparqlDataUpdate( $semanticData );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->assertEquals(
+ 1,
+ $this->store->getQueryResult( $query )->getCount()
+ );
+
+ $this->assertTrue(
+ $this->store->doSparqlDataDelete( $semanticData->getSubject() )
+ );
+
+ $this->assertEquals(
+ 0,
+ $this->store->getQueryResult( $query )->getCount()
+ );
+ }
+
+ /**
+ * @see http://semantic-mediawiki.org/wiki/Help:Selecting_pages#Restricting_results_to_a_namespace
+ */
+ public function testQuerySubjects_onNamspaceRestrictedCondition() {
+
+ $subjectInHelpNamespace = new DIWikiPage( __METHOD__, NS_HELP, '' );
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( $subjectInHelpNamespace )
+ ->newEmptySemanticData();
+
+ $property = new DIProperty( 'SomePageTypePropertyForNamespaceAnnotation' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByProperty( $property, 'Bar' )
+ );
+
+ $this->store->doSparqlDataUpdate( $semanticData );
+
+ $query = new Query(
+ new NamespaceDescription( NS_HELP ),
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $subjectInHelpNamespace,
+ $this->store->getQueryResult( $query )
+ );
+
+ $this->assertTrue(
+ $this->store->doSparqlDataDelete( $semanticData->getSubject() )
+ );
+
+ $this->assertSame(
+ 0,
+ $this->store->getQueryResult( $query )->getCount()
+ );
+ }
+
+ public function testQuerySubobjects_afterUpdatingWithEmptyContainerAllAssociatedEntitiesGetRemovedFromGraph() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $subobject = new Subobject( $semanticData->getSubject()->getTitle() );
+ $subobject->setEmptyContainerForId( 'SubobjectToTestReferenceAfterUpdate' );
+
+ $property = new DIProperty( 'SomeNumericPropertyToCompareReference' );
+ $property->setPropertyTypeId( '_num' );
+
+ $dataItem = new DINumber( 99999 );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByItem( $dataItem, $property )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $this->store->doSparqlDataUpdate( $semanticData );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( $dataItem, null, SMW_CMP_EQ )
+ );
+
+ $query = new Query(
+ $description,
+ false,
+ false
+ );
+
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->assertSame(
+ 1,
+ $this->store->getQueryResult( $query )->getCount()
+ );
+
+ $this->queryResultValidator->assertThatQueryResultHasSubjects(
+ $subobject->getSemanticData()->getSubject(),
+ $this->store->getQueryResult( $query )
+ );
+
+ $this->store->doSparqlDataUpdate(
+ $this->semanticDataFactory->newEmptySemanticData( __METHOD__ )
+ );
+
+ $this->assertSame(
+ 0,
+ $this->store->getQueryResult( $query )->getCount()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/RepositoryRedirectLookupActiveConnectionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/RepositoryRedirectLookupActiveConnectionTest.php
new file mode 100644
index 00000000..4e53d3f3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SPARQLStore/RepositoryRedirectLookupActiveConnectionTest.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace SMW\Tests\Integration\SPARQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SPARQLStore\RepositoryRedirectLookup;
+use SMW\SPARQLStore\SPARQLStore;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryRepositoryRedirectLookupActiveConnectionTest extends \PHPUnit_Framework_TestCase {
+
+ private $repositoryConnection;
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = ApplicationFactory::getInstance()->getStore();
+
+ if ( !$this->store instanceof SPARQLStore ) {
+ $this->markTestSkipped( "Skipping test because a SPARQLStore instance is required." );
+ }
+
+ $this->repositoryConnection = $this->store->getConnection( 'sparql' );
+ $this->repositoryConnection->setConnectionTimeout( 5 );
+
+ if ( !$this->repositoryConnection->ping() ) {
+ $this->markTestSkipped( "Can't connect to the RepositoryConnector" );
+ }
+ }
+
+ /**
+ * @dataProvider resourceProvider
+ */
+ public function testRedirectTargetLookupForNonExistingEntry( $expNsResource ) {
+
+ $instance = new RepositoryRedirectLookup( $this->repositoryConnection );
+ $instance->reset();
+
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertFalse( $exists );
+ }
+
+ public function testRedirectTargetLookupForExistingEntry() {
+
+ $property = new DIProperty( 'TestRepositoryRedirectLookup' );
+
+ $semanticData = new SemanticData( new DIWikiPage( __METHOD__, NS_MAIN ) );
+
+ $semanticData->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByProperty( $property, 'Bar' )
+ );
+
+ $this->store->doSparqlDataUpdate( $semanticData );
+
+ $expNsResource = new ExpNsResource(
+ 'TestRepositoryRedirectLookup',
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property'
+ );
+
+ $instance = new RepositoryRedirectLookup( $this->repositoryConnection );
+ $instance->reset();
+
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertTrue( $exists );
+ }
+
+ public function resourceProvider() {
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialNsResource( 'rdf', 'type' )
+ ];
+
+ $provider[] = [
+ new ExpNsResource(
+ 'FooRepositoryRedirectLookup',
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property',
+ new DIWikiPage( 'FooRepositoryRedirectLookup', SMW_NS_PROPERTY, '' )
+ )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/RefreshSQLStoreDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/RefreshSQLStoreDBIntegrationTest.php
new file mode 100644
index 00000000..6498c0ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/RefreshSQLStoreDBIntegrationTest.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\Tests\Integration\SQLStore;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\MwHooksHandler;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageDeleter;
+use Title;
+use WikiPage;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RefreshSQLStoreDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $title;
+ private $mwHooksHandler;
+ private $pageDeleter;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = new MwHooksHandler();
+ $this->pageDeleter = new PageDeleter();
+ $this->pageCreator = new PageCreator();
+ }
+
+ public function tearDown() {
+
+ $this->mwHooksHandler->restoreListedHooks();
+
+ if ( $this->title !== null ) {
+ $this->pageDeleter->deletePage( $this->title );
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testAfterPageCreation_StoreHasDataToRefreshWithoutJobs( $ns, $name, $iw ) {
+
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->title = Title::makeTitle( $ns, $name, '', $iw );
+
+ $this->pageCreator->createPage( $this->title );
+
+ $this->assertStoreHasDataToRefresh( false );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testAfterPageCreation_StoreHasDataToRefreshWitJobs( $ns, $name, $iw ) {
+
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->title = Title::makeTitle( $ns, $name, '', $iw );
+
+ $this->pageCreator->createPage( $this->title );
+
+ $this->assertStoreHasDataToRefresh( true );
+ }
+
+ protected function assertStoreHasDataToRefresh( $useJobs ) {
+ $refreshPosition = $this->title->getArticleID();
+
+ $entityRebuildDispatcher = $this->getStore()->refreshData(
+ $refreshPosition,
+ 1,
+ false,
+ $useJobs
+ );
+
+ $entityRebuildDispatcher->rebuild( $refreshPosition );
+
+ $this->assertGreaterThan(
+ 0,
+ $entityRebuildDispatcher->getEstimatedProgress()
+ );
+ }
+
+ public function titleProvider() {
+ $provider = [];
+
+ // $provider[] = array( NS_MAIN, 'withInterWiki', 'commons' );
+ $provider[] = [ NS_MAIN, 'normalTite', '' ];
+ $provider[] = [ NS_MAIN, 'useUpdateJobs', '' ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/SubSemanticDataDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/SubSemanticDataDBIntegrationTest.php
new file mode 100644
index 00000000..f6144827
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/SubSemanticDataDBIntegrationTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Integration\SQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageDeleter;
+use SMW\Tests\Utils\Validators\SemanticDataValidator;
+use Title;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SubSemanticDataDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $title;
+
+ protected function tearDown() {
+ $pageDeleter= new PageDeleter();
+
+ $pageDeleter->deletePage( $this->title );
+
+ parent::tearDown();
+ }
+
+ public function testCreatePageWithSubobject() {
+
+ $this->title = Title::newFromText( __METHOD__ );
+
+ $pageCreator = new PageCreator();
+
+ $pageCreator
+ ->createPage( $this->title )
+ ->doEdit(
+ '{{#subobject:namedSubobject|AA=Test1|@sortkey=Z}}' .
+ '{{#subobject:|BB=Test2|@sortkey=Z}}' );
+
+ $semanticData = $this->getStore()->getSemanticData( DIWikiPage::newFromTitle( $this->title ) );
+
+ $this->assertInstanceOf(
+ 'SMW\SemanticData',
+ $semanticData->findSubSemanticData( 'namedSubobject' )
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( 'AA' ),
+ new DIProperty( 'BB' ),
+ new DIProperty( '_SKEY' )
+ ],
+ 'propertyValues' => [
+ 'Test1',
+ 'Test2',
+ 'Z'
+ ]
+ ];
+
+ $semanticDataValidator = new SemanticDataValidator();
+
+ foreach ( $semanticData->getSubSemanticData() as $subSemanticData ) {
+ $semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $subSemanticData
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/TableBuilder/TableBuilderIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/TableBuilder/TableBuilderIntegrationTest.php
new file mode 100644
index 00000000..535b3faf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SQLStore/TableBuilder/TableBuilderIntegrationTest.php
@@ -0,0 +1,318 @@
+<?php
+
+namespace SMW\Tests\Integration\SQLStore\TableBuilder;
+
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\SQLStore\TableBuilder\PostgresTableBuilder;
+use SMW\SQLStore\TableBuilder\SQLiteTableBuilder;
+use SMW\SQLStore\TableBuilder\Table;
+use SMW\SQLStore\TableBuilder\TableBuilder;
+use SMW\Tests\MwDBaseUnitTestCase;
+
+/**
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableBuilderIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $messageReporterFactory;
+ private $TableBuilder;
+ private $stringValidator;
+ private $tableName = 'rdbms_test';
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->messageReporterFactory = MessageReporterFactory::getInstance();
+
+ $this->tableBuilder = TableBuilder::factory(
+ $this->getStore()->getConnection( DB_MASTER )
+ );
+
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testCreateTable() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking table rdbms_test',
+ 'Table not found, now creating',
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateTableWithNewField() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_INT, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking table rdbms_test',
+ 'Table already exists, checking structure',
+ 'creating field t_num',
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateTableWithNewFieldType() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_DOUBLE, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking table rdbms_test',
+ 'Table already exists, checking structure',
+ 'changing type of field t_num',
+ ];
+
+ // Not supported
+ if ( $this->tableBuilder instanceof SQLiteTableBuilder ) {
+ $expected = str_replace( 'changing type of field t_num', 'changing field type is not supported', $expected );
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testCreateIndex() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_DOUBLE, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $table->addIndex( 'id' );
+ $table->addIndex( 't_int' );
+ $table->addIndex( 't_num' );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking index structures for table rdbms_test',
+ 'index id is fine',
+ 'creating new index t_num'
+ ];
+
+ // ID message, Primary doesn't implicitly exists before
+ if ( $this->tableBuilder instanceof PostgresTableBuilder || $this->tableBuilder instanceof SQLiteTableBuilder ) {
+ $expected = str_replace( 'index id is fine', 'creating new index id', $expected );
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateIndex_ReplaceIndex() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_DOUBLE, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $table->addIndex( 'id' );
+ $table->addIndex( 't_num,t_int' );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking index structures for table rdbms_test',
+ 'index id is fine',
+ 'removing index t_num',
+ 'creating new index t_num,t_int',
+ ];
+
+ if ( $this->tableBuilder instanceof SQLiteTableBuilder ) {
+ $expected = str_replace( 'index id is fine', 'creating new index id', $expected );
+ $expected = 'removing index';
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateIndex_RemoveIndexWithArrayNotation() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_DOUBLE, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $table->addIndex( [ 'id', 'UNIQUE INDEX' ] );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking index structures for table rdbms_test',
+ 'index id is fine',
+ 'removing index t_num,t_int'
+ ];
+
+ if ( $this->tableBuilder instanceof SQLiteTableBuilder ) {
+ $expected = str_replace( 'index id is fine', 'creating new index id', $expected );
+ $expected = 'removing index';
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateIndex_NoIndexChange() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+ $table->addColumn( 'id', FieldType::FIELD_ID_PRIMARY );
+ $table->addColumn( 't_text', [ FieldType::TYPE_BLOB, 'NOT NULL' ] );
+ $table->addColumn( 't_num', [ FieldType::TYPE_DOUBLE, 'NOT NULL' ] );
+ $table->addColumn( 't_int', [ FieldType::TYPE_INT ] );
+
+ $table->addIndex( [ 'id', 'UNIQUE INDEX' ] );
+
+ $this->tableBuilder->create( $table );
+
+ $expected = [
+ 'Checking index structures for table rdbms_test',
+ 'index id is fine'
+ ];
+
+ if ( $this->tableBuilder instanceof SQLiteTableBuilder ) {
+ $expected = str_replace( 'index id is fine', 'creating new index id', $expected );
+ $expected = 'removing index';
+ }
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testDropTable() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( $this->tableName );
+
+ $this->tableBuilder->drop( $table );
+
+ $expected = [
+ 'dropped table rdbms_test'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testTryToDropTableWhichNotExists() {
+
+ $messageReporter = $this->messageReporterFactory->newSpyMessageReporter();
+
+ $this->tableBuilder->setMessageReporter(
+ $messageReporter
+ );
+
+ $table = new Table( 'foo_test' );
+
+ $this->tableBuilder->drop( $table );
+
+ $expected = [
+ 'foo_test not found, skipping removal'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $messageReporter->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializationDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializationDBIntegrationTest.php
new file mode 100644
index 00000000..e705afd9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializationDBIntegrationTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SerializerFactory;
+use SMW\Subobject;
+use SMW\Tests\MwDBaseUnitTestCase;
+use Title;
+
+/**
+ * @group semantic-mediawiki-integration
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SemanticDataSerializationDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ public function testRoundtripOfSerializedSemanticDataAfterStoreUpdate() {
+
+ $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticDataBeforeUpdate = new SemanticData( $subject );
+
+ $subobject = new Subobject( $subject->getTitle() );
+ $subobject->setEmptyContainerForId( 'SomeSubobjectToSerialize' );
+
+ $subobject->getSemanticData()->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $semanticDataBeforeUpdate->addSubobject( $subobject );
+
+ $this->getStore()->updateData( $semanticDataBeforeUpdate );
+
+ $semanticDataAfterUpdate = $this->getStore()->getSemanticData( $subject );
+
+ $serializerFactory = new SerializerFactory();
+
+ $serialization = $serializerFactory->getSerializerFor( $semanticDataAfterUpdate )->serialize( $semanticDataAfterUpdate );
+
+ $this->assertEquals(
+ $semanticDataAfterUpdate->getHash(),
+ $serializerFactory->getDeserializerFor( $serialization )->deserialize( $serialization )->getHash()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializerDeserializerRoundtripTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializerDeserializerRoundtripTest.php
new file mode 100644
index 00000000..9e13a517
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSerializerDeserializerRoundtripTest.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use ReflectionClass;
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\Deserializers\SemanticDataDeserializer;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Serializers\SemanticDataSerializer;
+use SMW\Subobject;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataSerializerDeserializerRoundtripTest extends \PHPUnit_Framework_TestCase {
+
+ private function newSerializerInstance() {
+ return new SemanticDataSerializer();
+ }
+
+ private function newDeserializerInstance() {
+ return new SemanticDataDeserializer();
+ }
+
+ /**
+ * @dataProvider semanticDataProvider
+ */
+ public function testSerializerDeserializerRountrip( $data ) {
+
+ $serialized = $this->newSerializerInstance()->serialize( $data );
+
+ $this->assertEquals(
+ $serialized,
+ $this->newSerializerInstance()->serialize( $this->newDeserializerInstance()->deserialize( $serialized ) )
+ );
+
+
+ $this->assertEquals(
+ $data->getHash(),
+ $this->newDeserializerInstance()->deserialize( $serialized )->getHash()
+ );
+ }
+
+ /**
+ * @dataProvider incompleteSubobjectDataProvider
+ */
+ public function testSerializerDeserializerWithIncompleteSubobjectData( $data ) {
+
+ $serialized = $this->newSerializerInstance()->serialize( $data );
+
+ $this->assertInstanceOf(
+ 'SMW\SemanticData',
+ $this->newDeserializerInstance()->deserialize( $serialized )
+ );
+ }
+
+ /**
+ * @dataProvider typeChangeSemanticDataProvider
+ */
+ public function testForcedTypeErrorDuringRountrip( $data, $type ) {
+
+ $serialized = $this->newSerializerInstance()->serialize( $data );
+ $deserializer = $this->newDeserializerInstance();
+
+ // Injects a different type to cause an error (this would normally
+ // happen when a property definition is changed such as page -> text
+ // etc.)
+ $reflector = new ReflectionClass( '\SMW\Deserializers\SemanticDataDeserializer' );
+ $property = $reflector->getProperty( 'dataItemTypeIdCache' );
+ $property->setAccessible( true );
+ $property->setValue( $deserializer, [ $type => 2 ] );
+
+ $deserialized = $deserializer->deserialize( $serialized );
+
+ $this->assertInstanceOf(
+ 'SMW\SemanticData',
+ $deserialized
+ );
+
+ $this->assertNotEmpty(
+ $deserialized->getErrors()
+ );
+ }
+
+ public function semanticDataProvider() {
+
+ ApplicationFactory::clear();
+
+ $provider = [];
+ $title = \Title::newFromText( __METHOD__ );
+
+ // #0 Empty container
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $provider[] = [ $foo ];
+
+ // #1 Single entry
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $provider[] = [ $foo ];
+
+ // #2 Single + single subobject entry
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+ $subobject->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has subobjects', 'Bam' ) );
+
+ $foo->addPropertyObjectValue( $subobject->getProperty(), $subobject->getContainer() );
+
+ $provider[] = [ $foo ];
+
+ // #3 Multiple entries
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has queez', 'Xeey' ) );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+ $subobject->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has subobjects', 'Bam' ) );
+ $subobject->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Fuz' ) );
+
+ $subobject->setSemanticData( 'Bar' );
+ $subobject->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Fuz' ) );
+
+ $foo->addPropertyObjectValue( $subobject->getProperty(), $subobject->getContainer() );
+
+ $provider[] = [ $foo ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function incompleteSubobjectDataProvider() {
+
+ $provider = [];
+
+ $title = \Title::newFromText( __METHOD__ );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $foo->addPropertyObjectValue( $subobject->getProperty(), $subobject->getSemanticData()->getSubject() );
+
+ $provider[] = [ $foo ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function typeChangeSemanticDataProvider() {
+
+ $provider = [];
+ $title = \Title::newFromText( __METHOD__ );
+
+ // #0 Single entry
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+
+ $provider[] = [ $foo, 'Has_fooQuex' ];
+
+ // #1 Single subobject entry
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+ $subobject->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fomQuex', 'Bam' ) );
+
+ $foo->addPropertyObjectValue( $subobject->getProperty(), $subobject->getContainer() );
+
+ $provider[] = [ $foo, 'Has_fomQuex' ];
+
+ // #2 Combined
+ $foo = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+ $foo->addDataValue( DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $foo->addPropertyObjectValue( $subobject->getProperty(), $subobject->getContainer() );
+
+ $provider[] = [ $foo, 'Has_fomQuex' ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSortKeyUpdateDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSortKeyUpdateDBIntegrationTest.php
new file mode 100644
index 00000000..6af6b620
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataSortKeyUpdateDBIntegrationTest.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\DIProperty;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SemanticDataSortKeyUpdateDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $semanticDataFactory;
+ private $subjects = [];
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+
+ $this->mwHooksHandler = UtilityFactory::getInstance()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+ }
+
+ protected function tearDown() {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $this->subjects );
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testSubjectSortKeySetter() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $subject = $semanticData->getSubject();
+ $subject->setSortKey( 'a_b_c' );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $semanticDataFromDB = $this->getStore()->getSemanticData( $subject );
+
+ $this->assertEquals(
+ 'a b c',
+ $semanticDataFromDB->getSubject()->getSortKey()
+ );
+
+ foreach ( $semanticDataFromDB->getPropertyValues( new DIProperty( '_SKEY' ) ) as $value ) {
+ $this->assertEquals(
+ 'a b c',
+ $value->getString()
+ );
+ }
+
+ $this->subjects[] = $semanticData->getSubject();
+ }
+
+ public function testDefinedSortKeyTakesPrecedenceOverSubjectSortKey() {
+
+ $semanticData = $this->semanticDataFactory
+ ->newEmptySemanticData( __METHOD__ );
+
+ $subject = $semanticData->getSubject();
+ $subject->setSortKey( '1_2_3' );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SKEY' ),
+ new DIBlob( 'x_y_z' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+ $this->getStore()->clear();
+
+ $semanticDataFromDB = $this->getStore()->getSemanticData( $subject );
+
+ $this->assertEquals(
+ 'x y z',
+ $semanticDataFromDB->getSubject()->getSortKey()
+ );
+
+ foreach ( $semanticDataFromDB->getPropertyValues( new DIProperty( '_SKEY' ) ) as $value ) {
+ $this->assertEquals(
+ 'x y z',
+ $value->getString()
+ );
+ }
+
+ $this->subjects[] = $semanticData->getSubject();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataStorageDBIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataStorageDBIntegrationTest.php
new file mode 100644
index 00000000..0938c9a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticDataStorageDBIntegrationTest.php
@@ -0,0 +1,327 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Subobject;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDITime as DITime;
+use Title;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-database
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SemanticDataStorageDBIntegrationTest extends MwDBaseUnitTestCase {
+
+ private $applicationFactory;
+ private $mwHooksHandler;
+
+ private $semanticDataValidator;
+ private $subjects = [];
+
+ private $pageDeleter;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = UtilityFactory::getInstance();
+
+ $this->mwHooksHandler = $utilityFactory->newMwHooksHandler();
+
+ $this->mwHooksHandler
+ ->deregisterListedHooks()
+ ->invokeHooksFromRegistry();
+
+ $this->semanticDataValidator = $utilityFactory->newValidatorFactory()->newSemanticDataValidator();
+ $this->pageDeleter = $utilityFactory->newPageDeleter();
+ $this->pageCreator = $utilityFactory->newPageCreator();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+
+ $this->pageDeleter
+ ->doDeletePoolOfPages( $this->subjects );
+
+ $this->applicationFactory->clear();
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testUserDefined_PageProperty_ToSemanticDataForStorage() {
+
+ $property = new DIProperty( 'SomePageProperty' );
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'SomePropertyPageValue', NS_MAIN, '' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertArrayHasKey(
+ $property->getKey(),
+ $this->getStore()->getSemanticData( $subject )->getProperties()
+ );
+
+ foreach ( $this->getStore()->getProperties( $subject ) as $prop ) {
+ $this->assertTrue( $prop->equals( $property ) );
+ }
+ }
+
+ public function testFixedProperty_MDAT_ToSemanticDataForStorage() {
+
+ $property = new DIProperty( '_MDAT' );
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DITime( 1, '1970', '1', '1' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertArrayHasKey(
+ $property->getKey(),
+ $this->getStore()->getSemanticData( $subject )->getProperties()
+ );
+
+ foreach ( $this->getStore()->getProperties( $subject ) as $prop ) {
+ $this->assertTrue( $prop->equals( $property ) );
+ }
+ }
+
+ public function testFixedProperty_ASK_NotForStorage() {
+
+ $property = new DIProperty( '_ASK' );
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertEmpty(
+ $this->getStore()->getProperties( $subject )
+ );
+ }
+
+ public function testAddUserDefinedBlobPropertyAsObjectToSemanticDataForStorage() {
+
+ $property = new DIProperty( 'SomeBlobProperty' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIBlob( 'SomePropertyBlobValue' )
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertArrayHasKey(
+ $property->getKey(),
+ $this->getStore()->getSemanticData( $subject )->getProperties()
+ );
+ }
+
+ public function testAddUserDefinedPropertyAsDataValueToSemanticDataForStorage() {
+
+ $propertyAsString = 'SomePropertyAsString';
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $propertyAsString,
+ 'Foo',
+ false,
+ $subject
+ );
+
+ $semanticData->addDataValue( $dataValue );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $this->assertArrayHasKey(
+ $propertyAsString,
+ $this->getStore()->getSemanticData( $subject )->getProperties()
+ );
+ }
+
+ public function testAddSubobjectToSemanticDataForStorage() {
+
+ $this->subjects[] = $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+ $semanticData = new SemanticData( $subject );
+
+ $subobject = new Subobject( $subject->getTitle() );
+ $subobject->setEmptyContainerForId( 'SomeSubobject' );
+
+ $subobject->getSemanticData()->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $this->getStore()->updateData( $semanticData );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( 'Foo' ),
+ new DIProperty( '_SKEY' )
+ ],
+ 'propertyValues' => [ 'Bar', __METHOD__ . '#SomeSubobject' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )->findSubSemanticData( 'SomeSubobject' )
+ );
+ }
+
+ public function testFetchSemanticDataForPreExistingSimpleRedirect() {
+
+ $this->applicationFactory->clear();
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-B' ) )
+ ->doEdit( '#REDIRECT [[Foo-A]]' );
+
+ $subject = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-A' ) );
+
+ $this->pageCreator
+ ->createPage( $subject->getTitle() )
+ ->doEdit( '[[HasNoDisplayRedirectInconsistencyFor::Foo-B]]' );
+
+ $expected = [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_SKEY', '_MDAT', 'HasNoDisplayRedirectInconsistencyFor' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+
+ $this->subjects = [
+ $subject,
+ Title::newFromText( 'Foo-B' )
+ ];
+ }
+
+ public function testFetchSemanticDataForPreExistingDoubleRedirect() {
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-B' ) )
+ ->doEdit( '#REDIRECT [[Foo-C]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-C' ) )
+ ->doEdit( '#REDIRECT [[Foo-A]]' );
+
+ $subject = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-A' ) );
+
+ $this->pageCreator
+ ->createPage( $subject->getTitle() )
+ ->doEdit( '[[HasNoDisplayRedirectInconsistencyFor::Foo-B]]' );
+
+ $this->pageCreator
+ ->createPage( Title::newFromText( 'Foo-C' ) )
+ ->doEdit( '[[Has page::{{PAGENAME}}' );
+
+ $expected = [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_SKEY', '_MDAT', 'HasNoDisplayRedirectInconsistencyFor' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $this->getStore()->getSemanticData( $subject )
+ );
+
+ $this->subjects = [
+ $subject,
+ Title::newFromText( 'Foo-B' ),
+ Title::newFromText( 'Foo-C' )
+ ];
+ }
+
+ /**
+ * Issue 622/619
+ */
+ public function testPrepareToFetchCorrectSemanticDataFromInternalCache() {
+
+ $redirect = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-A' ) );
+
+ $this->pageCreator
+ ->createPage( $redirect->getTitle() )
+ ->doEdit( '#REDIRECT [[Foo-C]]' );
+
+ $target = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-C' ) );
+
+ $this->pageCreator
+ ->createPage( $target->getTitle() )
+ ->doEdit( '{{#subobject:test|HasSomePageProperty=Foo-A}}' );
+
+ $this->assertEmpty(
+ $this->getStore()->getSemanticData( $redirect )->findSubSemanticData( 'test' )
+ );
+
+ $this->assertNotEmpty(
+ $this->getStore()->getSemanticData( $target )->findSubSemanticData( 'test' )
+ );
+ }
+
+ /**
+ * @depends testPrepareToFetchCorrectSemanticDataFromInternalCache
+ */
+ public function testVerifyToFetchCorrectSemanticDataFromInternalCache() {
+
+ $redirect = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-A' ) );
+ $target = DIWikiPage::newFromTitle( Title::newFromText( 'Foo-C' ) );
+
+ $this->assertEmpty(
+ $this->getStore()->getSemanticData( $redirect )->findSubSemanticData( 'test' )
+ );
+
+ $this->assertNotEmpty(
+ $this->getStore()->getSemanticData( $target )->findSubSemanticData( 'test' )
+ );
+
+ $this->subjects = [
+ $redirect,
+ $target
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticMediaWikiProvidedHookInterfaceIntegrationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticMediaWikiProvidedHookInterfaceIntegrationTest.php
new file mode 100644
index 00000000..4a74f0ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SemanticMediaWikiProvidedHookInterfaceIntegrationTest.php
@@ -0,0 +1,471 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SemanticMediaWikiProvidedHookInterfaceIntegrationTest extends \PHPUnit_Framework_TestCase {
+
+ private $mwHooksHandler;
+ private $applicationFactory;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $this->mwHooksHandler = $this->testEnvironment->getUtilityFactory()->newMwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->mwHooksHandler->restoreListedHooks();
+ $this->applicationFactory->clear();
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider storeClassProvider
+ */
+ public function testUnregisteredQueryResultHook( $storeClass ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'fetchQueryResult' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'fetchQueryResult' );
+
+ $store->getQueryResult( $query );
+ }
+
+ /**
+ * @dataProvider storeClassProvider
+ */
+ public function testRegisteredStoreBeforeQueryResultLookupCompleteHookToPreFetchQueryResult( $storeClass ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'fetchQueryResult' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'fetchQueryResult' );
+
+ $this->mwHooksHandler->register( 'SMW::Store::BeforeQueryResultLookupComplete', function( $store, $query, &$queryResult ) {
+ $queryResult = 'Foo';
+ return true;
+ } );
+
+ $this->assertNotEquals(
+ 'Foo',
+ $store->getQueryResult( $query )
+ );
+ }
+
+ /**
+ * @dataProvider storeClassProvider
+ */
+ public function testRegisteredStoreBeforeQueryResultLookupCompleteHookToSuppressDefaultQueryResultFetch( $storeClass ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'fetchQueryResult' ] )
+ ->getMock();
+
+ $store->expects( $this->never() )
+ ->method( 'fetchQueryResult' );
+
+ $this->mwHooksHandler->register( 'SMW::Store::BeforeQueryResultLookupComplete', function( $store, $query, &$queryResult ) {
+
+ $queryResult = 'Foo';
+
+ // Return false to suppress additional calls to fetchQueryResult
+ return false;
+ } );
+
+ $this->assertEquals(
+ 'Foo',
+ $store->getQueryResult( $query )
+ );
+ }
+
+ /**
+ * @dataProvider storeClassProvider
+ */
+ public function testRegisteredStoreAfterQueryResultLookupComplete( $storeClass ) {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'fetchQueryResult' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'fetchQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->mwHooksHandler->register( 'SMW::Store::AfterQueryResultLookupComplete', function( $store, &$queryResult ) {
+
+ if ( !$queryResult instanceof \SMWQueryResult ) {
+ throw new RuntimeException( 'Expected a SMWQueryResult instance' );
+ }
+
+ return true;
+ } );
+
+ $store->getQueryResult( $query );
+ }
+
+ /**
+ * @dataProvider storeClassProvider
+ */
+ public function testRegisteredFactboxBeforeContentGenerationToSuppressDefaultTableCreation( $storeClass ) {
+
+ $this->applicationFactory->getSettings()->set( 'smwgShowFactbox', SMW_FACTBOX_NONEMPTY );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( new DIWikiPage( 'Bar', NS_MAIN ) ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'hasVisibleProperties' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSemanticData', 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $this->mwHooksHandler->register( 'SMW::Factbox::BeforeContentGeneration', function( &$text, $semanticData ) {
+ $text = $semanticData->getSubject()->getTitle()->getText();
+ return false;
+ } );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = $this->applicationFactory->singleton( 'FactboxFactory' )->newFactbox( $title, new \ParserOutput() );
+ $instance->doBuild();
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->getContent()
+ );
+ }
+
+ public function testRegisteredSQLStoreBeforeChangeTitleComplete() {
+
+ // To make this work with SPARQLStore, need to inject the basestore
+ $storeClass = '\SMWSQLStore3';
+
+ $title = \Title::newFromText( __METHOD__ );
+
+ $idGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idGenerator->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'getObjectIds', 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idGenerator ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $null = 0;
+
+ $this->mwHooksHandler->register( 'SMW::SQLStore::BeforeChangeTitleComplete', function( $store, $oldTitle, $newTitle, $pageId, $redirectId ) {
+ return $store->reachedTheBeforeChangeTitleCompleteHook = true;
+ } );
+
+ $store->changeTitle( $title, $title, $null, $null );
+
+ $this->assertTrue(
+ $store->reachedTheBeforeChangeTitleCompleteHook
+ );
+ }
+
+ public function testRegisteredSQLStoreBeforeDeleteSubjectComplete() {
+
+ // To make this work with SPARQLStore, need to inject the basestore
+ $storeClass = '\SMWSQLStore3';
+
+ $title = \Title::newFromText( __METHOD__ );
+
+ $idGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [ 42 ] ) );
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'getPropertyTables', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idGenerator ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->mwHooksHandler->register( 'SMW::SQLStore::BeforeDeleteSubjectComplete', function( $store, $title ) {
+ return $store->reachedTheBeforeDeleteSubjectCompleteHook = true;
+ } );
+
+ $store->deleteSubject( $title );
+
+ $this->assertTrue(
+ $store->reachedTheBeforeDeleteSubjectCompleteHook
+ );
+ }
+
+ public function testRegisteredSQLStoreAfterDeleteSubjectComplete() {
+
+ // To make this work with SPARQLStore, need to inject the basestore
+ $storeClass = '\SMWSQLStore3';
+
+ $title = \Title::newFromText( __METHOD__ );
+
+ $idGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [ 42 ] ) );
+
+ $store = $this->getMockBuilder( $storeClass )
+ ->setMethods( [ 'getPropertyTables', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idGenerator ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->mwHooksHandler->register( 'SMW::SQLStore::AfterDeleteSubjectComplete', function( $store, $title ) {
+ return $store->reachedTheAfterDeleteSubjectCompleteHook = true;
+ } );
+
+ $store->deleteSubject( $title );
+
+ $this->assertTrue(
+ $store->reachedTheAfterDeleteSubjectCompleteHook
+ );
+ }
+
+ public function testRegisteredParserBeforeMagicWordsFinder() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = \Title::newFromText( __METHOD__ );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $parserData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromTitle( $title ) ) );
+
+ $parserData->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $parserData->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $magicWordsFinder = $this->getMockBuilder( '\SMW\MediaWiki\MagicWordsFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $magicWordsFinder->expects( $this->once() )
+ ->method( 'findMagicWordInText' )
+ ->with(
+ $this->equalTo( 'Foo' ),
+ $this->anything() )
+ ->will( $this->returnValue( [] ) );
+
+ $linksProcessor = $this->getMockBuilder( '\SMW\Parser\LinksProcessor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $redirectTargetFinder = $this->getMockBuilder( '\SMW\MediaWiki\RedirectTargetFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $inTextAnnotationParser = $this->getMockBuilder( '\SMW\Parser\InTextAnnotationParser' )
+ ->setConstructorArgs( [ $parserData, $linksProcessor, $magicWordsFinder, $redirectTargetFinder ] )
+ ->setMethods( null )
+ ->getMock();
+
+ $this->mwHooksHandler->register( 'SMW::Parser::BeforeMagicWordsFinder', function( &$magicWords ) {
+ $magicWords = [ 'Foo' ];
+
+ // Just to make MW 1.19 happy, otherwise it is not really needed
+ return true;
+ } );
+
+ $text = '';
+
+ $inTextAnnotationParser->parse( $text );
+ }
+
+ public function testRegisteredAddCustomFixedPropertyTables() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( null )
+ ->getMock();
+
+ $this->mwHooksHandler->register( 'SMW::SQLStore::AddCustomFixedPropertyTables', function( &$customFixedProperties, &$fixedPropertyTablePrefix ) {
+
+ // Standard table prefix
+ $customFixedProperties['Foo'] = '_Bar';
+
+ // Custom table prefix
+ $customFixedProperties['Foobar'] = '_Foooo';
+ $fixedPropertyTablePrefix['Foobar'] = 'smw_ext';
+
+ return true;
+ } );
+
+ $this->assertEquals(
+ 'smw_fpt_bar',
+ $store->findPropertyTableID( new \SMW\DIProperty( 'Foo' ) )
+ );
+
+ $this->assertEquals(
+ 'smw_ext_foooo',
+ $store->findPropertyTableID( new \SMW\DIProperty( 'Foobar' ) )
+ );
+ }
+
+ public function testRegisteredAfterDataUpdateComplete() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'is' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'is' )
+ ->with( $this->equalTo( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->setOption( 'smwgSemanticsEnabled', true );
+ $store->setOption( 'smwgAutoRefreshSubject', true );
+
+ $store->setLogger( $this->spyLogger );
+
+ $this->mwHooksHandler->register( 'SMW::SQLStore::AfterDataUpdateComplete', function( $store, $semanticData, $changeOp ) use ( $test ){
+ $test->is( $changeOp->getChangedEntityIdSummaryList() );
+
+ return true;
+ } );
+
+ $store->updateData(
+ new \SMW\SemanticData( DIWikiPage::newFromText( 'Foo' ) )
+ );
+ }
+
+ public function storeClassProvider() {
+
+ $provider[] = [ '\SMWSQLStore3' ];
+ $provider[] = [ '\SMW\SPARQLStore\SPARQLStore' ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SpecialAskTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SpecialAskTest.php
new file mode 100644
index 00000000..653aeaeb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/SpecialAskTest.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use DOMDocument;
+use SMW\MediaWiki\Specials\SpecialAsk;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author Stephan Gambke
+ */
+class SpecialAskTest extends \PHPUnit_Framework_TestCase {
+
+ private $oldRequestValues;
+ private $oldBodyText;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'smwgSpecialAskFormSubmitMethod' => SMW_SASK_SUBMIT_GET
+ ]
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider provideTestData
+ * @param $params
+ */
+ public function testProducesWellformedHtml( $params ) {
+
+ $this->setupGlobals( $params );
+
+ $special = new SpecialAsk();
+ $special->execute( null );
+
+ $html = $GLOBALS['wgOut']->getHtml();
+ $html = '<!DOCTYPE html><html><body>' . $html . '</body></html>';
+
+ // Known tags DOMDocument has issues with
+ $html = str_replace( [ '<nowiki>', '</nowiki>' ], '', $html );
+
+ $document = new DOMDocument();
+
+ // https://stackoverflow.com/questions/6090667/php-domdocument-errors-warnings-on-html5-tags
+ libxml_use_internal_errors(true);
+ $result = $document->loadHTML( $html );
+ libxml_clear_errors();
+
+ $this->assertTrue( $result );
+
+ $result = $document->loadXML( $html );
+ $this->assertTrue( $result );
+
+ $this->restoreGlobals( $params );
+ }
+
+ /**
+ * @return array
+ */
+ public function provideTestData() {
+ return [
+ [ [ 'eq' => 'yes', 'q' => '' ] ],
+ [ [ 'eq' => 'no', 'q' => '[[]]' ] ],
+ ];
+ }
+
+ /**
+ * @param array $params
+ */
+ protected function setupGlobals( $params ) {
+ global $wgOut, $wgRequest;
+
+ $this->oldRequestValues = [];
+
+ foreach ( $params as $key => $value ) {
+
+ $oldVal = $wgRequest->getText( $key, null );
+ if ( $oldVal !== null ) {
+ $this->oldRequestValues[$key] = $oldVal;
+ }
+
+ $wgRequest->setVal( $key, $value );
+ }
+
+ $this->oldBodyText = $wgOut->getHTML();
+ $wgOut->clearHTML();
+ }
+
+ /**
+ * @param array $params
+ */
+ protected function restoreGlobals( $params ) {
+ global $wgOut, $wgRequest;
+
+ foreach ( $params as $key => $value ) {
+ if ( method_exists( $wgRequest, 'unsetVal' ) ) {
+ $wgRequest->unsetVal( $key );
+ } else {
+ $wgRequest->setVal( $key, null );
+ }
+ }
+ foreach ( $this->oldRequestValues as $key => $value ) {
+ $wgRequest->setVal( $key, $value );
+ }
+
+ $wgOut->clearHTML();
+ $wgOut->addHTML( $this->oldBodyText );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nJsonFileIntegrityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nJsonFileIntegrityTest.php
new file mode 100644
index 00000000..ba8163aa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nJsonFileIntegrityTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\System;
+
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group semantic-mediawiki
+ * @group system-test
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class I18nJsonFileIntegrityTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider mediawikiI18nFileProvider
+ */
+ public function testMediaWikiI18NJsonDecodeEncode( $file ) {
+
+ $jsonFileReader = UtilityFactory::getInstance()->newJsonFileReader( $file );
+
+ $this->assertInternalType(
+ 'integer',
+ $jsonFileReader->getModificationTime()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $jsonFileReader->read()
+ );
+ }
+
+ /**
+ * @dataProvider semanticMediaWikiI18nFileProvider
+ */
+ public function testSemanticMediaWikiI18NJsonDecodeEncode( $file ) {
+
+ $jsonFileReader = UtilityFactory::getInstance()->newJsonFileReader( $file );
+
+ $this->assertInternalType(
+ 'integer',
+ $jsonFileReader->getModificationTime()
+ );
+
+ $contents = $jsonFileReader->read();
+
+ $this->assertInternalType(
+ 'array',
+ $contents
+ );
+
+ $expectedKeys = [
+ 'fallback_language',
+ 'datatype' => [ 'labels', 'aliases' ],
+ 'property' => [ 'labels', 'aliases' ],
+ 'namespace' => [ 'labels', 'aliases' ],
+ 'date' => [ 'precision', 'format', 'months', 'days' ]
+ ];
+
+ // If the file is marked with isLanguageRedirect then only check for the fallbackLanguage
+ if ( isset( $contents['isLanguageRedirect'] ) && $contents['isLanguageRedirect'] ) {
+ return $this->assertArrayHasKey( 'fallback_language', $contents, $file );
+ }
+
+ foreach ( $expectedKeys as $key => $val ) {
+ if ( !is_array( $val ) ) {
+ $this->assertArrayHasKey( $val, $contents, "Failed on $file with key: $val" );
+ } else {
+ foreach ( $val as $k => $v ) {
+ $this->assertArrayHasKey( $v, $contents[$key], "Failed on $file with key: $key/$v" );
+ }
+ }
+ }
+ }
+
+ public function mediawikiI18nFileProvider() {
+ return $this->findFilesIn( $GLOBALS['wgMessagesDirs']['SemanticMediaWiki'] );
+ }
+
+ public function semanticMediaWikiI18nFileProvider() {
+ return $this->findFilesIn( $GLOBALS['smwgExtraneousLanguageFileDir'] );
+ }
+
+ private function findFilesIn( $location ) {
+
+ $provider = [];
+
+ $bulkFileProvider = UtilityFactory::getInstance()->newBulkFileProvider( $location );
+ $bulkFileProvider->searchByFileExtension( 'json' );
+
+ foreach ( $bulkFileProvider->getFiles() as $file ) {
+ $provider[] = [ $file ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nMsgKeyIntegrityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nMsgKeyIntegrityTest.php
new file mode 100644
index 00000000..8804fd41
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/I18nMsgKeyIntegrityTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\System;
+
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group semantic-mediawiki
+ * @group system-test
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class I18nMsgKeyIntegrityTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider mediawikiI18nFileProvider
+ */
+ public function testDecKiloSeparatorMsgKeySetting( $file ) {
+
+ $jsonFileReader = UtilityFactory::getInstance()->newJsonFileReader( $file );
+ $contents = $jsonFileReader->read();
+
+ $isComplete = true;
+
+ if ( isset( $contents['smw_decseparator'] ) ) {
+ $isComplete = isset( $contents['smw_kiloseparator'] );
+ }
+
+ if ( $isComplete && isset( $contents['smw_kiloseparator'] ) ) {
+ $isComplete = isset( $contents['smw_decseparator'] );
+ }
+
+ $this->assertTrue(
+ $isComplete,
+ 'Failed on ' . basename( $file ) . ' with an incomplete pair of smw_decseparator/smw_kiloseparator.'
+ );
+ }
+
+ /**
+ * @dataProvider mediawikiI18nFileProvider
+ */
+ public function testDecKiloSeparatorHasDifferentValue( $file ) {
+
+ $jsonFileReader = UtilityFactory::getInstance()->newJsonFileReader( $file );
+ $contents = $jsonFileReader->read();
+
+ $hasDifferentSeparatorValue = true;
+
+ // Test on whether the values are different, otherwise fail
+ if ( isset( $contents['smw_kiloseparator'] ) && isset( $contents['smw_decseparator'] ) ) {
+ $hasDifferentSeparatorValue = $contents['smw_kiloseparator'] != $contents['smw_decseparator'];
+ }
+
+ $this->assertTrue(
+ $hasDifferentSeparatorValue,
+ 'Failed on ' . basename( $file ) . ' where smw_decseparator/smw_kiloseparator have the same set of values.'
+ );
+ }
+
+ public function mediawikiI18nFileProvider() {
+ return $this->findFilesIn( $GLOBALS['wgMessagesDirs']['SemanticMediaWiki'] );
+ }
+
+ private function findFilesIn( $location ) {
+
+ $provider = [];
+
+ $bulkFileProvider = UtilityFactory::getInstance()->newBulkFileProvider( $location );
+ $bulkFileProvider->searchByFileExtension( 'json' );
+
+ foreach ( $bulkFileProvider->getFiles() as $id => $file ) {
+ $provider[$id] = [ $file ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/InstallationConfigurationIntegrityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/InstallationConfigurationIntegrityTest.php
new file mode 100644
index 00000000..986d6419
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/InstallationConfigurationIntegrityTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\Tests\System;
+
+use SMW\ApplicationFactory;
+use SMW\Tests\Utils\GlobalsProvider;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-system
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InstallationGlobalsProviderIntegrityTest extends \PHPUnit_Framework_TestCase {
+
+ private $globalsProvider;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->globalsProvider = GlobalsProvider::getInstance();
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->globalsProvider->clear();
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testNamespaceSettingOnExampleIfSet() {
+
+ $expected = 'http://example.org/id/';
+
+ if ( $this->globalsProvider->get( 'smwgNamespace' ) !== $expected ) {
+ $this->markTestSkipped( "Skip test due to missing {$expected} setting" );
+ }
+
+ $this->assertSame(
+ $this->globalsProvider->get( 'smwgNamespace' ),
+ $this->applicationFactory->getSettings()->get( 'smwgNamespace' )
+ );
+ }
+
+ /**
+ * @dataProvider smwgNamespacesWithSemanticLinksProvider
+ */
+ public function testNamespacesWithSemanticLinksOnTravisCustomNamespace( $type, $container ) {
+
+ if ( !defined( 'NS_TRAVIS' ) ) {
+ $this->markTestSkipped( 'Test can only be executed with a specified NS_TRAVIS' );
+ }
+
+ $namespace = NS_TRAVIS;
+ $extraNamespaces = $this->globalsProvider->get( 'wgExtraNamespaces' );
+
+ $this->assertTrue(
+ isset( $extraNamespaces[$namespace] )
+ );
+
+ $foundNamespaceEntry = false;
+
+ foreach ( $container as $key => $value ) {
+ if ( $key === $namespace ) {
+ $foundNamespaceEntry = true;
+ break;
+ }
+ }
+
+ $this->assertTrue(
+ $foundNamespaceEntry,
+ "Asserts that smwgNamespacesWithSemanticLinks retrieved from {$type} contains the expected {$namespace} NS"
+ );
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function smwgNamespacesWithSemanticLinksProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'GLOBALS',
+ GlobalsProvider::getInstance()->get( 'smwgNamespacesWithSemanticLinks' )
+ ];
+
+ $provider[] = [
+ 'Settings',
+ ApplicationFactory::getInstance()->getSettings()->get( 'smwgNamespacesWithSemanticLinks' )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/LanguagesAccessibilityAndIntegrityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/LanguagesAccessibilityAndIntegrityTest.php
new file mode 100644
index 00000000..ec8616c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/LanguagesAccessibilityAndIntegrityTest.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace SMW\Tests\System;
+
+use SMW\Lang\Lang;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-system
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class LanguagesAccessibilityAndIntegrityTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider languageCodeProvider
+ */
+ public function testCommonInterfaceMethods( $langcode ) {
+
+ $methods = [
+ 'getDateFormats' => 'array',
+ 'getNamespaces' => 'array',
+ 'getNamespaceAliases' => 'array',
+ 'getDatatypeLabels' => 'array',
+ 'getDatatypeAliases' => 'array',
+ 'getPropertyLabels' => 'array',
+ 'getPropertyAliases' => 'array',
+ ];
+
+ $class = $this->loadLanguageFileAndConstructClass( $langcode );
+
+ foreach ( $methods as $method => $type ) {
+ $this->assertInternalType( $type, call_user_func( [ $class, $method ] ) );
+ }
+ }
+
+ /**
+ * @depends testCommonInterfaceMethods
+ * @dataProvider languageCodeProvider
+ */
+ public function testComparePredefinedPropertyLabels( $langcode ) {
+
+ $class = $this->loadLanguageFileAndConstructClass( $langcode );
+
+ $baseToCompareInstance = Lang::getInstance()->fetch( 'en' );
+ $targetLanguageInstance = $class;
+
+ $result = array_diff_key(
+ $baseToCompareInstance->getPropertyLabels(),
+ $targetLanguageInstance->getPropertyLabels()
+ );
+
+ $this->assertTrue(
+ $result === [],
+ "Asserts predfined property keys for the language pair EN - {$langcode} with {$this->formatAsString($result)}"
+ );
+ }
+
+ /**
+ * @dataProvider languageCodeProvider
+ */
+ public function testCompareMonthAndLabel( $langcode ) {
+
+ $class = $this->loadLanguageFileAndConstructClass( $langcode );
+
+ for ( $i=1; $i <= 12; $i++ ) {
+
+ $label = call_user_func( [ $class, 'getMonthLabel' ], $i );
+ $month = call_user_func( [ $class, 'findMonth' ], $label );
+
+ $this->assertInternalType( 'string', $label );
+ $this->assertEquals( $i, $month );
+ }
+ }
+
+ public function languageCodeProvider() {
+
+ $provider = [];
+
+ $languageCodes = [
+ 'En', 'Ar', 'Arz', 'Ca', 'De', 'Es', 'Fi',
+ 'Fr', 'He', 'Id', 'It', 'Nb', 'Nl', 'Pl',
+ 'Pt', 'Ru', 'Sk', 'Zh-cn', 'Zh-tw'
+ ];
+
+ foreach ( $languageCodes as $code ) {
+ $provider[] = [ $code ];
+ }
+
+ return $provider;
+ }
+
+ private function loadLanguageFileAndConstructClass( $langcode ) {
+ return Lang::getInstance()->fetch( $langcode );
+ }
+
+ private function formatAsString( $expected ) {
+ return is_array( $expected ) ? implode( ', ', $expected ) : $expected;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/ResourcesAccessibilityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/ResourcesAccessibilityTest.php
new file mode 100644
index 00000000..19f6ffea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/System/ResourcesAccessibilityTest.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests\System;
+
+use ResourceLoader;
+use ResourceLoaderContext;
+use ResourceLoaderModule;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ResourcesAccessibilityTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider moduleDataProvider
+ */
+ public function testModulesScriptsFilesAreAccessible( $modules, ResourceLoader $resourceLoader, $context ) {
+
+ foreach ( array_keys( $modules ) as $name ) {
+ $this->assertInternalType(
+ 'string',
+ $resourceLoader->getModule( $name )->getScript( $context )
+ );
+ }
+ }
+
+ /**
+ * @dataProvider moduleDataProvider
+ */
+ public function testModulesStylesFilesAreAccessible( $modules, ResourceLoader $resourceLoader, $context ) {
+
+ foreach ( array_keys( $modules ) as $name ) {
+
+ $styles = $resourceLoader->getModule( $name )->getStyles( $context );
+
+ foreach ( $styles as $style ) {
+ $this->assertInternalType( 'string', $style );
+ }
+ }
+ }
+
+ public function moduleDataProvider() {
+
+ $resourceLoader = new ResourceLoader();
+ $context = ResourceLoaderContext::newDummyContext();
+
+ foreach ( $GLOBALS['smwgResourceLoaderDefFiles'] as $key => $file ) {
+ $providers[] = [
+ include $file,
+ $resourceLoader,
+ $context
+ ];
+ }
+
+ return $providers;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/TempFileRoundTripTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/TempFileRoundTripTest.php
new file mode 100644
index 00000000..02b62475
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Integration/TempFileRoundTripTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Tests\Integration;
+
+use SMW\Utils\TempFile;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TempFileRoundTripTest extends \PHPUnit_Framework_TestCase {
+
+ public function testRoundTrip() {
+
+ $expected = 'Test write file';
+ $tempFile = new TempFile();
+
+ $file = $tempFile->generate( 'Test' );
+
+ $tempFile->write( $file, $expected );
+
+ $this->assertTrue(
+ $tempFile->exists( $file )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $tempFile->read( $file, $tempFile->getCheckSum( $file ) )
+ );
+
+ $tempFile->write( $file, '++plus++', FILE_APPEND );
+
+ $this->assertEquals(
+ $expected . '++plus++',
+ $tempFile->read( $file )
+ );
+
+ $tempFile->delete( $file );
+
+ $this->assertFalse(
+ $tempFile->exists( $file )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseContentHandler.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseContentHandler.php
new file mode 100644
index 00000000..91b1debc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseContentHandler.php
@@ -0,0 +1,192 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Message;
+use SMW\Tests\Utils\File\ContentsReader;
+use SMW\Tests\Utils\File\LocalFileUpload;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Tests\Utils\PageDeleter;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonTestCaseContentHandler {
+
+ /**
+ * @var PageCreator
+ */
+ private $pageCreator;
+
+ /**
+ * @var PageDeleter
+ */
+ private $pageDeleter;
+
+ /**
+ * @var LocalFileUpload
+ */
+ private $LocalFileUpload;
+
+ /**
+ * @var array
+ */
+ private $pages = [];
+
+ /**
+ * @var string
+ */
+ private $skipOn = '';
+
+ /**
+ * @var string
+ */
+ private $testCaseLocation = '';
+
+ /**
+ * @since 2.5
+ *
+ * @param PageCreator $pageCreator
+ * @param PageDeleter $pageDeleter
+ * @param LocalFileUpload $localFileUpload
+ */
+ public function __construct( PageCreator $pageCreator, PageDeleter $pageDeleter, LocalFileUpload $localFileUpload ) {
+ $this->pageCreator = $pageCreator;
+ $this->pageDeleter = $pageDeleter;
+ $this->localFileUpload = $localFileUpload;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getPages() {
+ return $this->pages;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $skipOn
+ */
+ public function skipOn( $skipOn ) {
+ $this->skipOn = $skipOn;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $testCaseLocation
+ */
+ public function setTestCaseLocation( $testCaseLocation ) {
+ $this->testCaseLocation = $testCaseLocation;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $pages
+ * @param integer $defaultNamespace
+ */
+ public function createPagesFrom( array $pages, $defaultNamespace = NS_MAIN ) {
+
+ foreach ( $pages as $page ) {
+
+ $skipOn = isset( $page['skip-on'] ) ? $page['skip-on'] : [];
+
+ if ( in_array( $this->skipOn, array_keys( $skipOn ) ) ) {
+ continue;
+ }
+
+ if ( ( !isset( $page['page'] ) && !isset( $page['name'] ) ) || !isset( $page['contents'] ) ) {
+ continue;
+ }
+
+ $namespace = isset( $page['namespace'] ) ? constant( $page['namespace'] ) : $defaultNamespace;
+
+ $this->createPage( $page, $namespace );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $page
+ * @param integer $defaultNamespace
+ */
+ public function createPage( array $page, $namespace ) {
+
+ $pageContentLanguage = isset( $page['contentlanguage'] ) ? $page['contentlanguage'] : '';
+
+ if ( isset( $page['message-cache'] ) && $page['message-cache'] === 'clear' ) {
+ Message::clear();
+ }
+
+ $name = ( isset( $page['name'] ) ? $page['name'] : $page['page'] );
+
+ $title = Title::newFromText(
+ $name,
+ $namespace
+ );
+
+ if ( $namespace === NS_FILE && isset( $page['contents']['upload'] ) ) {
+ return $this->doUploadFile( $title, $page['contents']['upload'] );
+ }
+
+ if ( is_array( $page['contents'] ) && isset( $page['contents']['import-from'] ) ) {
+ $contents = ContentsReader::readContentsFrom(
+ $this->testCaseLocation . $page['contents']['import-from']
+ );
+ } else {
+ $contents = $page['contents'];
+ }
+
+ $this->pageCreator->createPage( $title, $contents, $pageContentLanguage );
+
+ $this->pages[] = $this->pageCreator->getPage();
+
+ if ( isset( $page['move-to'] ) ) {
+ $this->doMovePage( $page, $namespace );
+ }
+
+ if ( isset( $page['do-purge'] ) ) {
+ $this->pageCreator->getPage()->doPurge();
+ }
+
+ if ( isset( $page['do-delete'] ) && $page['do-delete'] ) {
+ $this->pageDeleter->deletePage( $title );
+ }
+ }
+
+ private function doMovePage( $page, $namespace ) {
+ $target = Title::newFromText(
+ $page['move-to']['target'],
+ $namespace
+ );
+
+ $this->pageCreator->doMoveTo(
+ $target,
+ $page['move-to']['is-redirect']
+ );
+
+ $this->pages[] = $target;
+ }
+
+ private function doUploadFile( $title, array $contents ) {
+
+ $this->localFileUpload->doUploadCopyFromLocation(
+ $this->testCaseLocation . $contents['file'],
+ $title->getText(),
+ $contents['text']
+ );
+
+ TestEnvironment::executePendingDeferredUpdates();
+ $this->pages[] = $title;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseFileHandler.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseFileHandler.php
new file mode 100644
index 00000000..052dee98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseFileHandler.php
@@ -0,0 +1,466 @@
+<?php
+
+namespace SMW\Tests;
+
+use RuntimeException;
+use SMW\Tests\Utils\File\JsonFileReader;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class JsonTestCaseFileHandler {
+
+ /**
+ * @var JsonFileReader
+ */
+ private $fileReader;
+
+ /**
+ * @var string
+ */
+ private $reasonToSkip = '';
+
+ /**
+ * @since 2.2
+ *
+ * @param JsonFileReader $fileReader
+ */
+ public function __construct( JsonFileReader $fileReader ) {
+ $this->fileReader = $fileReader;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function isIncomplete() {
+
+ $meta = $this->getFileContentsFor( 'meta' );
+ $isIncomplete = isset( $meta['is-incomplete'] ) ? (bool)$meta['is-incomplete'] : false;
+
+ if ( $isIncomplete ) {
+ $this->reasonToSkip = '"'. $this->getFileContentsFor( 'description' ) . '" has been marked as incomplete.';
+ }
+
+ return $isIncomplete;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function getDebugMode() {
+
+ $meta = $this->getFileContentsFor( 'meta' );
+
+ return isset( $meta['debug'] ) ? (bool)$meta['debug'] : false;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return boolean
+ */
+ public function hasAllRequirements( $dependencyDef = [] ) {
+
+ $requires = $this->getContentsFor( 'requires' );
+
+ if ( $requires === [] ) {
+ return true;
+ }
+
+ foreach ( $requires as $key => $value ) {
+ $res = false;
+
+ if ( isset( $dependencyDef[$key] ) && is_callable( $dependencyDef[$key] ) ) {
+ $res = call_user_func_array( $dependencyDef[$key], [ $value, &$this->reasonToSkip ] );
+ }
+
+ if ( $res === false ) {
+
+ // Default msg!
+ if ( $this->reasonToSkip === '' ) {
+ $this->reasonToSkip = "$key requirements were not met!";
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $case
+ * @param string $identifier
+ *
+ * @return boolean
+ */
+ public function requiredToSkipFor( array $case, $identifier ) {
+
+ $skipOn = isset( $case['skip-on'] ) ? $case['skip-on'] : [];
+ $identifier = strtolower( $identifier );
+
+ $version = $GLOBALS['wgVersion'];
+
+ foreach ( $skipOn as $id => $value ) {
+
+ $versionToSkip = '';
+ $compare = '=';
+ $noop = '';
+
+ if ( is_array( $value ) ) {
+ $versionToSkip = $value[0];
+ $reason = $value[1];
+ } else {
+ $reason = $value;
+ }
+
+ // Suppor for { "skip-on": { "foo": [ "not", "Exclude all except foo ..." ] }
+ if ( $versionToSkip === 'not' && $identifier === $id ) {
+ continue;
+ } elseif ( $versionToSkip === 'not' && $identifier !== $id ) {
+ return true;
+ }
+
+ // Suppor for { "skip-on": { "virtuoso": "Virtuoso 6.1 ..." }
+ if ( $identifier === $id ) {
+ return true;
+ }
+
+ // Suppor for { "skip-on": { "smw->2.5.x": "Reason is ..." }
+ // or { "skip-on": { "mw->1.30.x": "Reason is ..." }
+ if ( strpos( $id, 'mw-' ) !== false ) {
+ list( $noop, $versionToSkip ) = explode( "mw-", $id, 2 );
+ }
+
+ if ( strpos( $id, 'hhvm-' ) !== false ) {
+ list( $noop, $versionToSkip ) = explode( "hhvm-", $id, 2 );
+ }
+
+ // Suppor for { "skip-on": { "mediawiki": [ ">1.29.x", "Reason is ..." ] }
+ if ( strpos( $id, 'smw' ) !== false ) {
+ $version = SMW_VERSION;
+ } elseif ( strpos( $id, 'mediawiki' ) !== false || strpos( $id, 'mw' ) !== false ) {
+ $version = $GLOBALS['wgVersion'];
+ } elseif ( strpos( $id, 'hhvm' ) !== false ) {
+ $version = defined( 'HHVM_VERSION' ) ? HHVM_VERSION : 0;
+ } elseif ( strpos( $id, 'php' ) !== false ) {
+ $version = defined( 'PHP_VERSION' ) ? PHP_VERSION : 0;
+ }
+
+ if ( $versionToSkip !== '' && ( $versionToSkip{0} === '<' || $versionToSkip{0} === '>' ) ) {
+ $compare = $versionToSkip{0};
+ $versionToSkip = substr( $versionToSkip, 1 );
+ }
+
+ if ( strpos( $versionToSkip, '.x' ) ) {
+ $versionToSkip = str_replace( '.x', '.9999', $versionToSkip );
+ $compare = $compare === '=' ? '<' : $compare;
+ }
+
+ if ( strpos( $versionToSkip, '<' ) ) {
+ $versionToSkip = str_replace( '<', '', $versionToSkip );
+ $compare = '<';
+ }
+
+ // Skip any version as in { "skip-on": { "mediawiki": [ "*", "Reason is ..." ] }
+ if ( $versionToSkip === '*' ) {
+ return true;
+ }
+
+ if ( version_compare( $version, $versionToSkip, $compare ) ) {
+ $this->reasonToSkip = "$version version is not supported ({$reason})";
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function requiredToSkipForConnector( $connectorId ) {
+
+ $connectorId = strtolower( $connectorId );
+ $meta = $this->getFileContentsFor( 'meta' );
+
+ $skipOn = isset( $meta['skip-on'] ) ? $meta['skip-on'] : [];
+
+ if ( in_array( $connectorId, array_keys( $skipOn ) ) ) {
+ $this->reasonToSkip = $skipOn[$connectorId];
+ }
+
+ return $this->reasonToSkip !== '';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function requiredToSkipForJsonVersion( $version ) {
+
+ $meta = $this->getFileContentsFor( 'meta' );
+
+ if ( version_compare( $version, $meta['version'], 'ne' ) ) {
+ $this->reasonToSkip = $meta['version'] . " is not supported due to a required {$version} test case version.";
+ }
+
+ return $this->reasonToSkip !== '';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return boolean
+ */
+ public function requiredToSkipForMwVersion( $mwVersion ) {
+
+ $meta = $this->getFileContentsFor( 'meta' );
+ $skipOn = isset( $meta['skip-on'] ) ? $meta['skip-on'] : [];
+
+ foreach ( $skipOn as $id => $reason ) {
+
+ if ( strpos( $id, 'mw-' ) === false ) {
+ continue;
+ }
+
+ list( $mw, $versionToSkip ) = explode( "mw-", $id, 2 );
+ $compare = '=';
+
+ if ( strpos( $versionToSkip, '.x' ) ) {
+ $versionToSkip = str_replace( '.x', '.9999', $versionToSkip );
+ $compare = '<';
+ }
+
+ if ( strpos( $versionToSkip, '<' ) ) {
+ $versionToSkip = str_replace( '<', '', $versionToSkip );
+ $compare = '<';
+ }
+
+ if ( version_compare( $mwVersion, $versionToSkip, $compare ) ) {
+ $this->reasonToSkip = "MediaWiki " . $mwVersion . " version is not supported ({$reason})";
+ break;
+ }
+ }
+
+ return $this->reasonToSkip !== '';
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getReasonForSkip() {
+ return $this->reasonToSkip;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return booleam
+ */
+ public function hasSetting( $key ) {
+
+ $settings = $this->getFileContentsFor( 'settings' );
+
+ return isset( $settings[$key] );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array|integer|string|boolean
+ * @throws RuntimeException
+ */
+ public function getSettingsFor( $key, $callback = null ) {
+
+ $settings = $this->getFileContentsFor( 'settings' );
+
+ if ( isset( $settings[$key] ) && is_callable( $callback ) ) {
+ return call_user_func_array( $callback, [ $settings[$key] ] );
+ }
+
+ // Needs special attention due to NS constant usage
+ if ( $key === 'smwgNamespacesWithSemanticLinks' && isset( $settings[$key] ) ) {
+ $smwgNamespacesWithSemanticLinks = [];
+
+ foreach ( $settings[$key] as $ns => $value ) {
+ $smwgNamespacesWithSemanticLinks[constant( $ns )] = (bool)$value;
+ }
+
+ return $smwgNamespacesWithSemanticLinks;
+ }
+
+ $constantFeaturesList = [
+ 'smwgSparqlQFeatures',
+ 'smwgDVFeatures',
+ 'smwgFulltextSearchIndexableDataTypes',
+ 'smwgFieldTypeFeatures',
+ 'smwgQueryProfiler',
+ 'smwgParserFeatures',
+ 'smwgCategoryFeatures',
+ 'smwgQSortFeatures'
+ ];
+
+ foreach ( $constantFeaturesList as $constantFeatures ) {
+ if ( $key === $constantFeatures && isset( $settings[$key] ) ) {
+ $features = '';
+
+ if ( !is_array( $settings[$key] ) ) {
+ return $settings[$key];
+ }
+
+ foreach ( $settings[$key] as $value ) {
+ $features = constant( $value ) | (int)$features;
+ }
+
+ return $features;
+ }
+ }
+
+ if ( $key === 'wgDefaultUserOptions' && isset( $settings[$key] ) ) {
+ return array_merge( $GLOBALS['wgDefaultUserOptions'], $settings[$key] );
+ }
+
+ // Needs special attention due to constant usage
+ if ( $key === 'smwgQConceptCaching' && isset( $settings[$key] ) ) {
+ return constant( $settings[$key] );
+ }
+
+ // Needs special attention due to constant usage
+ if ( strpos( $key, 'CacheType' ) !== false && isset( $settings[$key] ) ) {
+ return $settings[$key] === false ? CACHE_NONE : defined( $settings[$key] ) ? constant( $settings[$key] ) : $settings[$key];
+ }
+
+ if ( isset( $settings[$key] ) ) {
+ return $settings[$key];
+ }
+
+ // Return values from the global settings as default
+ if ( isset( $GLOBALS[$key] ) || array_key_exists( $key, $GLOBALS ) ) {
+ return $GLOBALS[$key];
+ }
+
+ // Key is unknown, TestConfig will remove any remains during tearDown
+ return null;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getListOfProperties() {
+ return $this->getFileContentsFor( 'properties' );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getListOfSubjects() {
+ return $this->getFileContentsFor( 'subjects' );
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return array
+ */
+ public function getPageCreationSetupList() {
+ return $this->getContentsFor( 'setup' );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ *
+ * @return array
+ */
+ public function getContentsFor( $key ) {
+
+ try{
+ $contents = $this->getFileContentsFor( $key );
+ } catch( \Exception $e ) {
+ $contents = [];
+ }
+
+ return $contents;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param string $key
+ *
+ * @return array
+ */
+ public function findTestCasesFor( $key ) {
+ return $this->getContentsFor( $key );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ public function findTasksBeforeTestExecutionByType( $type ) {
+ $contents = $this->getContentsFor( 'beforeTest' );
+ return isset( $contents[$type] ) ? $contents[$type] : [];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $type
+ *
+ * @return array
+ */
+ public function findTestCasesByType( $type ) {
+ return array_filter( $this->getContentsFor( 'tests' ), function( $contents ) use( $type ) {
+ return isset( $contents['type'] ) && $contents['type'] === $type;
+ } );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $type
+ *
+ * @return integer
+ */
+ public function countTestCasesByType( $type ) {
+ return count( $this->findTestCasesByType( $type ) );
+ }
+
+ private function getFileContentsFor( $index ) {
+
+ $contents = $this->fileReader->read();
+
+ if ( isset( $contents[$index] ) ) {
+ return $contents[$index];
+ }
+
+ throw new RuntimeException( "{$index} is unknown" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseScriptRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseScriptRunner.php
new file mode 100644
index 00000000..1fb8d779
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/JsonTestCaseScriptRunner.php
@@ -0,0 +1,322 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * The JsonTestCaseScriptRunner is a convenience provider for `Json` formatted
+ * integration tests to allow writing tests quicker without the need to setup
+ * or tear down specific data structures.
+ *
+ * The JSON format should make it also possible for novice user to understand
+ * what sort of tests are run as the content is based on wikitext rather than
+ * native PHP.
+ *
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+abstract class JsonTestCaseScriptRunner extends MwDBaseUnitTestCase {
+
+ /**
+ * @var FileReader
+ */
+ private $fileReader;
+
+ /**
+ * @var JsonTestCaseFileHandler
+ */
+ private $jsonTestCaseFileHandler;
+
+ /**
+ * @var JsonTestCaseContentHandler
+ */
+ private $jsonTestCaseContentHandler;
+
+ /**
+ * @var array
+ */
+ private $itemsMarkedForDeletion = [];
+
+ /**
+ * @var array
+ */
+ private $configValueCallback = [];
+
+ /**
+ * @var boolean
+ */
+ protected $deletePagesOnTearDown = true;
+
+ /**
+ * @var string
+ */
+ protected $searchByFileExtension = 'json';
+
+ /**
+ * @var string
+ */
+ protected $connectorId = '';
+
+ protected function setUp() {
+ parent::setUp();
+
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+ $utilityFactory->newMwHooksHandler()->deregisterListedHooks();
+ $utilityFactory->newMwHooksHandler()->invokeHooksFromRegistry();
+
+ $this->fileReader = $utilityFactory->newJsonFileReader();
+
+ $this->jsonTestCaseContentHandler = new JsonTestCaseContentHandler(
+ $utilityFactory->newPageCreator(),
+ $utilityFactory->newPageDeleter(),
+ $utilityFactory->newLocalFileUpload()
+ );
+
+ if ( $this->getStore() instanceof \SMWSparqlStore ) {
+ $this->connectorId = strtolower( $GLOBALS['smwgSparqlRepositoryConnector'] );
+ } elseif ( $this->getStore() instanceof \SMW\Elastic\ElasticStore ) {
+ $this->connectorId = 'elastic';
+ } else {
+ $this->connectorId = strtolower( $this->getDBConnection()->getType() );
+ }
+ }
+
+ protected function tearDown() {
+
+ if ( $this->deletePagesOnTearDown ) {
+ $this->testEnvironment->flushPages( $this->itemsMarkedForDeletion );
+ }
+
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getTestCaseLocation();
+
+ /**
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ abstract protected function runTestCaseFile( JsonTestCaseFileHandler $jsonTestCaseFileHandler );
+
+ /**
+ * @return string
+ */
+ protected function getRequiredJsonTestCaseMinVersion() {
+ return '0.1';
+ }
+
+ /**
+ * @return array
+ */
+ protected function getAllowedTestCaseFiles() {
+ return [];
+ }
+
+ /**
+ * Selected list of settings (internal or MediaWiki related) that are
+ * permissible for the time of the test run to be manipulated.
+ *
+ * For a configuration that requires special treatment (i.e. where a simple
+ * assignment isn't sufficient), a callback can be assigned to a settings
+ * key in order to sort out required manipulation (constants etc.).
+ *
+ * @return array
+ */
+ protected function getPermittedSettings() {
+
+ // Ensure that the context is set for a selected language
+ // and dependent objects are reset
+ $this->registerConfigValueCallback( 'wgContLang', function( $val ) {
+ \RequestContext::getMain()->setLanguage( $val );
+ \SMW\Localizer::getInstance()->clear();
+ $lang = \Language::factory( $val );
+
+ // https://github.com/wikimedia/mediawiki/commit/49ce67be93dfbb40d036703dad2278ea9843f1ad
+ $this->testEnvironment->redefineMediaWikiService( 'ContentLanguage', function () use ( $lang ) {
+ return $lang;
+ } );
+
+ return $lang;
+ } );
+
+ $this->registerConfigValueCallback( 'wgLang', function( $val ) {
+ \RequestContext::getMain()->setLanguage( $val );
+ \SMW\Localizer::getInstance()->clear();
+ return \Language::factory( $val );
+ } );
+
+ return [];
+ }
+
+ /**
+ * @param string $key
+ * @param Closure $callback
+ */
+ protected function registerConfigValueCallback( $key, \Closure $callback ) {
+ $this->configValueCallback[$key] = $callback;
+ }
+
+ /**
+ * @return callable|null
+ */
+ protected function getConfigValueCallback( $key ) {
+ return isset( $this->configValueCallback[$key] ) ? $this->configValueCallback[$key] : null;
+ }
+
+ /**
+ * Normally returns TRUE but can act on the list retrieved from
+ * JsonTestCaseScriptRunner::getAllowedTestCaseFiles (or hereof) to filter
+ * selected files and help fine tune a setup or debug a potential issue
+ * without having to run all test files at once.
+ *
+ * @param string $file
+ *
+ * @return boolean
+ */
+ protected function canTestCaseFile( $file ) {
+
+ // Filter specific files on-the-fly
+ $allowedTestCaseFiles = $this->getAllowedTestCaseFiles();
+
+ if ( $allowedTestCaseFiles === [] ) {
+ return true;
+ }
+
+ // Doesn't require the exact name
+ foreach ( $allowedTestCaseFiles as $fileName ) {
+ if ( strpos( $file, $fileName ) !== false ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @dataProvider jsonFileProvider
+ */
+ public function testCaseFile( $file ) {
+
+ if ( !$this->canTestCaseFile( $file ) ) {
+ $this->markTestSkipped( $file . ' excluded from the test run' );
+ }
+
+ $this->fileReader->setFile( $file );
+ $this->runTestCaseFile( new JsonTestCaseFileHandler( $this->fileReader ) );
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonFileProvider() {
+
+ $provider = [];
+
+ $bulkFileProvider = UtilityFactory::getInstance()->newBulkFileProvider(
+ $this->getTestCaseLocation()
+ );
+
+ $bulkFileProvider->searchByFileExtension( $this->searchByFileExtension );
+
+ foreach ( $bulkFileProvider->getFiles() as $file ) {
+ $provider[basename( $file )] = [ $file ];
+ }
+
+ return $provider;
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param mixed $key
+ * @param mixed $value
+ */
+ protected function changeGlobalSettingTo( $key, $value ) {
+ $this->testEnvironment->addConfiguration( $key, $value );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return []
+ */
+ protected function getDependencyDefinitions() {
+ return [];
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @param JsonTestCaseFileHandler $jsonTestCaseFileHandler
+ */
+ protected function checkEnvironmentToSkipCurrentTest( JsonTestCaseFileHandler $jsonTestCaseFileHandler ) {
+
+ if ( $jsonTestCaseFileHandler->isIncomplete() ) {
+ $this->markTestIncomplete( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+
+ if ( !$jsonTestCaseFileHandler->hasAllRequirements( $this->getDependencyDefinitions() ) ) {
+ $this->markTestSkipped( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipForJsonVersion( $this->getRequiredJsonTestCaseMinVersion() ) ) {
+ $this->markTestSkipped( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipForMwVersion( $GLOBALS['wgVersion'] ) ) {
+ $this->markTestSkipped( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipForConnector( $this->getDBConnection()->getType() ) ) {
+ $this->markTestSkipped( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+
+ if ( $jsonTestCaseFileHandler->requiredToSkipForConnector( $this->connectorId ) ) {
+ $this->markTestSkipped( $jsonTestCaseFileHandler->getReasonForSkip() );
+ }
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $pages
+ * @param integer $defaultNamespace
+ */
+ protected function createPagesFrom( array $pages, $defaultNamespace = NS_MAIN ) {
+
+ $this->jsonTestCaseContentHandler->skipOn(
+ $this->connectorId
+ );
+
+ $this->jsonTestCaseContentHandler->setTestCaseLocation(
+ $this->getTestCaseLocation()
+ );
+
+ $this->jsonTestCaseContentHandler->createPagesFrom(
+ $pages,
+ $defaultNamespace
+ );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->itemsMarkedForDeletion = $this->jsonTestCaseContentHandler->getPages();
+ }
+
+ /**
+ * @deprecated 2.5
+ */
+ protected function createPagesFor( array $pages, $defaultNamespace ) {
+ $this->createPagesFrom( $pages, $defaultNamespace );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/PHPUnitCompat.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/PHPUnitCompat.php
new file mode 100644
index 00000000..0ce51bd9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/PHPUnitCompat.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+trait PHPUnitCompat {
+
+ /**
+ * @see PHPUnit_Framework_TestCase::setExpectedException
+ */
+ public function setExpectedException( $name, $message = '', $code = null ) {
+ if ( is_callable( [ $this, 'expectException' ] ) ) {
+ if ( $name !== null ) {
+ $this->expectException( $name );
+ }
+ if ( $message !== '' ) {
+ $this->expectExceptionMessage( $message );
+ }
+ if ( $code !== null ) {
+ $this->expectExceptionCode( $code );
+ }
+ } else {
+ parent::setExpectedException( $name, $message, $code );
+ }
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterRegistryTestCase.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterRegistryTestCase.php
new file mode 100644
index 00000000..574faeb2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterRegistryTestCase.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Test;
+
+/**
+ * Base class for SMW\ResultPrinter tests.
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @codeCoverageIgnore
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class QueryPrinterRegistryTestCase extends QueryPrinterTestCase {
+
+ /**
+ * Returns the names of the formats supported by the
+ * \SMW\ResultPrinter being tested.
+ *
+ * @since 1.8
+ *
+ * @return array
+ */
+ public abstract function getFormats();
+
+ /**
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ $argumentLists = [];
+
+ foreach ( $this->getFormats() as $format ) {
+ $argumentLists[] = [ $format, true ];
+ $argumentLists[] = [ $format, false ];
+ }
+
+ return $argumentLists;
+ }
+
+ /**
+ * Creates and returns a new instance of the result printer.
+ *
+ * @since 1.8
+ *
+ * @param string $format
+ * @param boolean $isInline
+ *
+ * @return \SMW\ResultPrinter
+ */
+ protected function newInstance( $format, $isInline ) {
+ $class = $this->getClass();
+ return new $class( $format, $isInline );
+ }
+
+ /**
+ * @since 1.8
+ * @return array
+ */
+ public function instanceProvider() {
+ $phpFails = [ $this, 'newInstance' ];
+
+ return array_map(
+ function( array $args ) use ( $phpFails ) {
+ return call_user_func_array( $phpFails, $args );
+ },
+ $this->constructorProvider()
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ *
+ * @since 1.8
+ *
+ * @param string $format
+ * @param boolean $isInline
+ */
+ public function testConstructor( $format, $isInline ) {
+ $instance = $this->newInstance( $format, $isInline );
+
+ $this->assertInstanceOf( '\SMW\ResultPrinter', $instance );
+ }
+}
+
+/**
+ * SMWResultPrinter
+ *
+ * @deprecated since SMW 1.9
+ */
+class_alias( 'SMW\Test\QueryPrinterRegistryTestCase', 'SMW\Test\ResultPrinterTestCase' );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterTestCase.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterTestCase.php
new file mode 100644
index 00000000..69e46ad3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/QueryPrinterTestCase.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Test;
+
+use ReflectionClass;
+use SMW\ResultPrinter;
+
+/**
+ * Class contains methods to access data in connection with the QueryPrinter
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Class contains methods to access data in connection with the QueryPrinter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+abstract class QueryPrinterTestCase extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * Helper method sets result printer parameters
+ *
+ * @param ResultPrinter $instance
+ * @param array $parameters
+ *
+ * @return ResultPrinter
+ */
+ protected function setParameters( ResultPrinter $instance, array $parameters ) {
+
+ $reflector = new ReflectionClass( $this->getClass() );
+ $params = $reflector->getProperty( 'params' );
+ $params->setAccessible( true );
+ $params->setValue( $instance, $parameters );
+
+ if ( isset( $parameters['searchlabel'] ) ) {
+ $searchlabel = $reflector->getProperty( 'mSearchlabel' );
+ $searchlabel->setAccessible( true );
+ $searchlabel->setValue( $instance, $parameters['searchlabel'] );
+ }
+
+ if ( isset( $parameters['headers'] ) ) {
+ $searchlabel = $reflector->getProperty( 'mShowHeaders' );
+ $searchlabel->setAccessible( true );
+ $searchlabel->setValue( $instance, $parameters['headers'] );
+ }
+
+ return $instance;
+
+ }
+
+ protected function arrayWrap( array $elements ) {
+ return array_map(
+ function ( $element ) {
+ return [ $element ];
+ },
+ $elements
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SemanticMediaWikiTestCase.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SemanticMediaWikiTestCase.php
new file mode 100644
index 00000000..2a480dd1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SemanticMediaWikiTestCase.php
@@ -0,0 +1,260 @@
+<?php
+
+namespace SMW\Test;
+
+use FauxRequest;
+use Language;
+use ReflectionClass;
+use RequestContext;
+use SMW\DependencyContainer;
+use SMW\DIWikiPage;
+use SMW\Settings;
+use SMW\SimpleDependencyBuilder;
+use SMW\StoreFactory;
+use SMW\Tests\Utils\Mock\CoreMockObjectRepository;
+use SMW\Tests\Utils\Mock\MediaWikiMockObjectRepository;
+use SMW\Tests\Utils\Mock\MockObjectBuilder;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+use WebRequest;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * Class contains general purpose methods
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+abstract class SemanticMediaWikiTestCase extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * Returns the name of the deriving class being tested
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public abstract function getClass();
+
+ /**
+ * Helper method that returns a MockObjectBuilder object
+ *
+ * @since 1.9
+ *
+ * @return MockObjectBuilder
+ */
+ public function newMockBuilder() {
+
+ $builder = new MockObjectBuilder();
+ $builder->registerRepository( new CoreMockObjectRepository() );
+ $builder->registerRepository( new MediaWikiMockObjectRepository() );
+
+ return $builder;
+ }
+
+ /**
+ * Helper method that returns a SimpleDependencyBuilder object
+ *
+ * @since 1.9
+ *
+ * @param DependencyContainer $dependencyContainer
+ *
+ * @return SimpleDependencyBuilder
+ */
+ public function newDependencyBuilder( DependencyContainer $dependencyContainer = null ) {
+ return new SimpleDependencyBuilder( $dependencyContainer );
+ }
+
+ /**
+ * Helper method that returns a ReflectionClass object
+ *
+ * @since 1.9
+ *
+ * @param string|null $class
+ *
+ * @return ReflectionClass
+ */
+ public function newReflector( $class = null ) {
+ return new ReflectionClass( $class === null ? $this->getClass() : $class );
+ }
+
+ /**
+ * Helper method that returns a randomized Title object to avoid results
+ * are influenced by cross instantiated objects with the same title name
+ *
+ * @since 1.9
+ *
+ * @param $namespace
+ * @param $text|null
+ *
+ * @return Title
+ */
+ protected function newTitle( $namespace = NS_MAIN, $text = null ) {
+ return Title::newFromText( $text === null ? $this->newRandomString() : $text, $namespace );
+ }
+
+ /**
+ * Helper method that returns a User object
+ *
+ * @since 1.9
+ *
+ * @return User
+ */
+ protected function getUser() {
+ return $this->newMockUser();
+ }
+
+ /**
+ * Helper method that returns a User object
+ *
+ * @since 1.9
+ *
+ * @return User
+ */
+ protected function newMockUser() {
+ return new MockSuperUser();
+ }
+
+ /**
+ * Helper method that returns a Language object
+ *
+ * @since 1.9
+ *
+ * @return Language
+ */
+ protected function getLanguage( $langCode = 'en' ) {
+ return Language::factory( $langCode );
+ }
+
+ /**
+ * Returns RequestContext object
+ *
+ * @param array $params
+ *
+ * @return RequestContext
+ */
+ protected function newContext( $request = [] ) {
+
+ $context = new RequestContext();
+
+ if ( $request instanceof WebRequest ) {
+ $context->setRequest( $request );
+ } else {
+ $context->setRequest( new FauxRequest( $request, true ) );
+ }
+
+ $context->setUser( new MockSuperUser() );
+
+ return $context;
+ }
+
+ /**
+ * Helper method that returns a randomized DIWikiPage object
+ *
+ * @since 1.9
+ *
+ * @param $namespace
+ *
+ * @return DIWikiPage
+ */
+ protected function getSubject( $namespace = NS_MAIN ) {
+ return DIWikiPage::newFromTitle( $this->newTitle( $namespace ) );
+ }
+
+ /**
+ * Helper method that returns a DIWikiPage object
+ *
+ * @since 1.9
+ *
+ * @param Title|null $title
+ *
+ * @return DIWikiPage
+ */
+ protected function newSubject( Title $title = null ) {
+ return DIWikiPage::newFromTitle( $title === null ? $this->newTitle() : $title );
+ }
+
+ /**
+ * Helper method that returns a Settings object
+ *
+ * @since 1.9
+ *
+ * @param array $settings
+ *
+ * @return Settings
+ */
+ protected function newSettings( array $settings = [] ) {
+ return Settings::newFromArray( $settings );
+ }
+
+ /**
+ * Helper method that returns a random string
+ *
+ * @since 1.9
+ *
+ * @param $length
+ * @param $prefix identify a specific random string during testing
+ *
+ * @return string
+ */
+ protected function newRandomString( $length = 10, $prefix = null ) {
+ return $prefix . ( $prefix ? '-' : '' ) . substr( str_shuffle( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ), 0, $length );
+ }
+
+ /**
+ * Helper method to skip the test if it is not a SQLStore
+ *
+ * @since 1.9
+ */
+ protected function runOnlyOnSQLStore( $store = null ) {
+
+ if ( $store === null ) {
+ $store = StoreFactory::getStore();
+ }
+
+ if ( !( $store instanceof \SMWSQLStore3 ) ) {
+ $this->markTestSkipped( 'Test only applicable to SMWSQLStore3' );
+ }
+
+ }
+
+ protected function getStore() {
+ $store = StoreFactory::getStore();
+
+ if ( !( $store instanceof \SMWSQLStore3 ) ) {
+ $this->markTestSkipped( 'Test only applicable for SMWSQLStore3' );
+ }
+
+ return $store;
+ }
+
+ /**
+ * Utility method taking an array of elements and wrapping
+ * each element in it's own array. Useful for data providers
+ * that only return a single argument.
+ *
+ * @see MediaWikiTestCase::arrayWrap
+ *
+ * @since 1.9
+ *
+ * @param array $elements
+ *
+ * @return array
+ */
+ protected function arrayWrap( array $elements ) {
+ return array_map(
+ function ( $element ) {
+ return [ $element ];
+ },
+ $elements
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SpecialPageTestCase.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SpecialPageTestCase.php
new file mode 100644
index 00000000..ecc8cebc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/SpecialPageTestCase.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Test;
+
+use FauxRequest;
+use Language;
+use OutputPage;
+use RequestContext;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use SpecialPage;
+use WebRequest;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group medium
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9.0.2
+ *
+ * @author mwjames
+ */
+abstract class SpecialPageTestCase extends \PHPUnit_Framework_TestCase {
+
+ protected $obLevel;
+ protected $store = null;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->obLevel = ob_get_level();
+ }
+
+ protected function tearDown() {
+
+ $obLevel = ob_get_level();
+
+ while ( ob_get_level() > $this->obLevel ) {
+ ob_end_clean();
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @return SpecialPage
+ */
+ protected abstract function getInstance();
+
+ protected function setStore( $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * Borrowed from \Wikibase\Test\SpecialPageTestBase
+ *
+ * @param string $sub The subpage parameter to call the page with
+ * @param WebRequest $request Web request that may contain URL parameters, etc
+ */
+ protected function execute( $sub = '', WebRequest $request = null, $user = null ) {
+
+ $request = $request === null ? new FauxRequest() : $request;
+ $response = $request->response();
+
+ $page = $this->getInstance();
+
+ if ( $this->store !== null ) {
+ $page->setStore( $this->store );
+ }
+
+ $page->setContext( $this->makeRequestContext(
+ $request,
+ $user,
+ $this->getTitle( $page )
+ ) );
+
+ $out = $page->getOutput();
+
+ ob_start();
+ $page->execute( $sub );
+
+ if ( $out->getRedirect() !== '' ) {
+ $out->output();
+ $text = ob_get_contents();
+ } elseif ( $out->isDisabled() ) {
+ $text = ob_get_contents();
+ } else {
+ $text = $out->getHTML();
+ }
+
+ ob_end_clean();
+
+ $code = $response->getStatusCode();
+
+ if ( $code > 0 ) {
+ $response->header( "Status: " . $code . ' ' . \HttpStatus::getMessage( $code ) );
+ }
+
+ $this->text = $text;
+ $this->response = $response;
+ }
+
+ /**
+ * @return string
+ */
+ protected function getText() {
+ return $this->text;
+ }
+
+ /**
+ * @return FauxResponse
+ */
+ protected function getResponse() {
+ return $this->response;
+ }
+
+ /**
+ * @return RequestContext
+ */
+ private function makeRequestContext( WebRequest $request, $user, $title ) {
+
+ $context = new RequestContext();
+ $context->setRequest( $request );
+
+ $out = new OutputPage( $context );
+ $out->setTitle( $title );
+
+ $context->setOutput( $out );
+ $context->setLanguage( Language::factory( 'en' ) );
+
+ $user = $user === null ? new MockSuperUser() : $user;
+ $context->setUser( $user );
+
+ return $context;
+ }
+
+ /**
+ * Deprecated: Use of SpecialPage::getTitle was deprecated in MediaWiki 1.23
+ *
+ * @return Title
+ */
+ private function getTitle( SpecialPage $page ) {
+ return method_exists( $page, 'getPageTitle') ? $page->getPageTitle() : $page->getTitle();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestConfig.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestConfig.php
new file mode 100644
index 00000000..79394591
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestConfig.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ApplicationFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TestConfig {
+
+ /**
+ * @var Settings
+ */
+ private $settings;
+
+ /**
+ * @var array
+ */
+ private $configurations = [];
+
+ /**
+ * @var array
+ */
+ private $newKeys = [];
+
+ /**
+ * @since 3.0
+ */
+ public function __construct() {
+ $this->settings = ApplicationFactory::getInstance()->getSettings();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $configurations
+ */
+ public function set( array $configurations = [] ) {
+
+ foreach ( $configurations as $key => $value ) {
+
+ if ( array_key_exists( $key, $GLOBALS ) ) {
+ $this->configurations[$key] = $GLOBALS[$key];
+ } else {
+ $this->newKeys[] = $key;
+ }
+
+ $GLOBALS[$key] = $value;
+ $this->settings->set( $key, $value );
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public function reset() {
+
+ foreach ( $this->configurations as $key => $value ) {
+ $GLOBALS[$key] = $value;
+ $this->settings->set( $key, $value );
+ }
+
+ foreach ( $this->newKeys as $key ) {
+ unset( $GLOBALS[$key] );
+ $this->settings->delete( $key );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestEnvironment.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestEnvironment.php
new file mode 100644
index 00000000..9e938212
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/TestEnvironment.php
@@ -0,0 +1,248 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DeferredCallableUpdate;
+use SMW\Localizer;
+use SMW\Tests\Utils\Mock\ConfigurableStub;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TestEnvironment {
+
+ /**
+ * @var ApplicationFactory
+ */
+ private $applicationFactory;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @var TestConfig
+ */
+ private $testConfig;
+
+ /**
+ * @since 2.4
+ *
+ * @param array $configuration
+ */
+ public function __construct( array $configuration = [] ) {
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->testConfig = new TestConfig();
+
+ $this->withConfiguration( $configuration );
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function executePendingDeferredUpdates() {
+ DeferredCallableUpdate::releasePendingUpdates();
+ \DeferredUpdates::doUpdates();
+ }
+
+ /**
+ * @since 2.4
+ */
+ public static function clearPendingDeferredUpdates() {
+ DeferredCallableUpdate::releasePendingUpdates();
+ \DeferredUpdates::clearPendingUpdates();
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $key
+ * @param mixed $value
+ *
+ * @return self
+ */
+ public function addConfiguration( $key, $value ) {
+ return $this->withConfiguration( [ $key => $value ] );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param array $configuration
+ *
+ * @return self
+ */
+ public function withConfiguration( array $configuration = [] ) {
+ $this->testConfig->set( $configuration );
+ return $this;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $name
+ */
+ public function resetMediaWikiService( $name ) {
+
+ // MW 1.27+ (yet 1.27.0.rc has no access to "resetServiceForTesting")
+ if ( !class_exists( '\MediaWiki\MediaWikiServices' ) || !method_exists( \MediaWiki\MediaWikiServices::getInstance(), 'resetServiceForTesting' ) ) {
+ return null;
+ }
+
+ try {
+ \MediaWiki\MediaWikiServices::getInstance()->resetServiceForTesting( $name );
+ } catch( \Exception $e ) {
+ // Do nothing just avoid a
+ // MediaWiki\Services\NoSuchServiceException: No such service ...
+ }
+
+ if ( $name === 'MainWANObjectCache' ) {
+ \MediaWiki\MediaWikiServices::getInstance()->getMainWANObjectCache()->clearProcessCache();
+ }
+
+ return $this;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $name
+ * @param callable $service
+ */
+ public function redefineMediaWikiService( $name, callable $service ) {
+
+ if ( !class_exists( '\MediaWiki\MediaWikiServices' ) ) {
+ return null;
+ }
+
+ $this->resetMediaWikiService( $name );
+
+ try {
+ \MediaWiki\MediaWikiServices::getInstance()->redefineService( $name, $service );
+ } catch( \Exception $e ) {
+ // Do nothing just avoid a
+ // MediaWiki\Services\NoSuchServiceException: No such service ...
+ }
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string|array $poolCache
+ *
+ * @return self
+ */
+ public function resetPoolCacheById( $poolCache ) {
+
+ if ( is_array( $poolCache ) ) {
+ foreach ( $poolCache as $pc ) {
+ $this->resetPoolCacheById( $pc );
+ }
+ }
+
+ $this->applicationFactory->getInMemoryPoolCache()->resetPoolCacheById( $poolCache );
+
+ return $this;
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @param string $id
+ * @param mixed $object
+ *
+ * @return self
+ */
+ public function registerObject( $id, $object ) {
+ $this->applicationFactory->registerObject( $id, $object );
+ return $this;
+ }
+
+ /**
+ * @since 2.4
+ */
+ public function tearDown() {
+ $this->testConfig->reset();
+ $this->applicationFactory->clear();
+ $this->dataValueFactory->clear();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param callable $callback
+ *
+ * @return string
+ */
+ public function outputFromCallbackExec( callable $callback ) {
+ ob_start();
+ call_user_func( $callback );
+ $output = ob_get_contents();
+ ob_end_clean();
+ return $output;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $pages
+ */
+ public function flushPages( $pages ) {
+ self::getUtilityFactory()->newPageDeleter()->doDeletePoolOfPages( $pages );
+ }
+
+ /**
+ * @since 2.4
+ *
+ * @return UtilityFactory
+ */
+ public static function getUtilityFactory() {
+ return UtilityFactory::getInstance();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return ValidatorFactory
+ */
+ public static function newValidatorFactory() {
+ return UtilityFactory::getInstance()->newValidatorFactory();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return SpyLogger
+ */
+ public static function newSpyLogger() {
+ return self::getUtilityFactory()->newSpyLogger();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $index
+ * @param string $url
+ *
+ * @return string
+ */
+ public function replaceNamespaceWithLocalizedText( $index, $text ) {
+
+ $namespace = Localizer::getInstance()->getNamespaceTextById( $index );
+
+ return str_replace(
+ Localizer::getInstance()->getCanonicalNamespaceTextById( $index ) . ':',
+ $namespace . ':',
+ $text
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ApplicationFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ApplicationFactoryTest.php
new file mode 100644
index 00000000..7f24d50a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ApplicationFactoryTest.php
@@ -0,0 +1,328 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ApplicationFactory;
+
+/**
+ * @covers \SMW\ApplicationFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ApplicationFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\ApplicationFactory',
+ ApplicationFactory::getInstance()
+ );
+ }
+
+ public function testCanConstructSerializerFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\SerializerFactory',
+ $this->applicationFactory->newSerializerFactory()
+ );
+ }
+
+ public function testCanConstructJobFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\JobFactory',
+ $this->applicationFactory->newJobFactory()
+ );
+ }
+
+ public function testCanConstructParserFunctionFactory() {
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctionFactory',
+ $this->applicationFactory->newParserFunctionFactory( $parser )
+ );
+ }
+
+ public function testCanConstructQuerySourceFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\Query\QuerySourceFactory',
+ $this->applicationFactory->getQuerySourceFactory()
+ );
+ }
+
+ public function testGetStore() {
+
+ $this->assertInstanceOf(
+ '\SMW\Store',
+ $this->applicationFactory->getStore()
+ );
+ }
+
+ public function testGetSettings() {
+
+ $this->assertInstanceOf(
+ '\SMW\Settings',
+ $this->applicationFactory->getSettings()
+ );
+ }
+
+ public function testGetConnectionManager() {
+
+ $this->assertInstanceOf(
+ '\SMW\Connection\ConnectionManager',
+ $this->applicationFactory->getConnectionManager()
+ );
+ }
+
+ public function testCanConstructTitleFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\TitleFactory',
+ $this->applicationFactory->newTitleFactory()
+ );
+ }
+
+ public function testCanConstructPageCreator() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageCreator',
+ $this->applicationFactory->newPageCreator()
+ );
+ }
+
+ public function testCanConstructPageUpdater() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageUpdater',
+ $this->applicationFactory->newPageUpdater()
+ );
+ }
+
+ public function testCanConstructInTextAnnotationParser() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Parser\InTextAnnotationParser',
+ $this->applicationFactory->newInTextAnnotationParser( $parserData )
+ );
+ }
+
+ public function testCanConstructContentParser() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ContentParser',
+ $this->applicationFactory->newContentParser( $title )
+ );
+ }
+
+ public function testCanConstructMwCollaboratorFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MwCollaboratorFactory',
+ $this->applicationFactory->newMwCollaboratorFactory()
+ );
+ }
+
+ public function testCanConstructNamespaceExaminer() {
+
+ $this->assertInstanceOf(
+ '\SMW\NamespaceExaminer',
+ $this->applicationFactory->getNamespaceExaminer()
+ );
+ }
+
+ public function testCanConstructDataUpdater() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\DataUpdater',
+ $this->applicationFactory->newDataUpdater( $semanticData )
+ );
+ }
+
+ public function testCanConstructDataItemFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataItemFactory',
+ $this->applicationFactory->getDataItemFactory()
+ );
+ }
+
+ public function testCanConstructMaintenanceFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceFactory',
+ $this->applicationFactory->newMaintenanceFactory()
+ );
+ }
+
+ public function testCanConstructCacheFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\CacheFactory',
+ $this->applicationFactory->newCacheFactory()
+ );
+ }
+
+ public function testCanConstructIteratorFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\IteratorFactory',
+ $this->applicationFactory->getIteratorFactory()
+ );
+ }
+
+ public function testCanConstructDataValueFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValueFactory',
+ $this->applicationFactory->getDataValueFactory()
+ );
+ }
+
+ public function testCanConstructPropertySpecificationLookup() {
+
+ $this->assertInstanceOf(
+ '\SMW\PropertySpecificationLookup',
+ $this->applicationFactory->getPropertySpecificationLookup()
+ );
+ }
+
+ public function testCanConstructHierarchyLookup() {
+
+ $this->assertInstanceOf(
+ '\SMW\HierarchyLookup',
+ $this->applicationFactory->newHierarchyLookup()
+ );
+ }
+
+ public function testCanConstructCachedPropertyValuesPrefetcher() {
+
+ $this->assertInstanceOf(
+ '\SMW\CachedPropertyValuesPrefetcher',
+ $this->applicationFactory->getCachedPropertyValuesPrefetcher()
+ );
+ }
+
+ public function testCanConstructQueryFactory() {
+
+ $this->assertInstanceOf(
+ '\SMW\QueryFactory',
+ $this->applicationFactory->getQueryFactory()
+ );
+ }
+
+ public function testCanConstructPropertyLabelFinder() {
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyLabelFinder',
+ $this->applicationFactory->getPropertyLabelFinder()
+ );
+ }
+
+ public function testCanConstructDeferredCallableUpdate() {
+
+ $callback = function() {
+ return null;
+ };
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Deferred\CallableUpdate',
+ $this->applicationFactory->newDeferredCallableUpdate( $callback )
+ );
+ }
+
+ public function testCanConstructDeferredTransactionalCallableUpdate() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Deferred\TransactionalCallableUpdate',
+ $this->applicationFactory->newDeferredTransactionalCallableUpdate( null )
+ );
+ }
+
+ public function testCanConstructMediaWikiLogger() {
+
+ $this->assertInstanceOf(
+ '\Psr\Log\LoggerInterface',
+ $this->applicationFactory->getMediaWikiLogger()
+ );
+ }
+
+ public function testCanConstructJobQueue() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\JobQueue',
+ $this->applicationFactory->getJobQueue()
+ );
+ }
+
+ /**
+ * @dataProvider callbackContainerProvider
+ */
+ public function testCanConstructFromCallbackContainer( $service, $arguments, $expected ) {
+
+ array_unshift( $arguments, $service );
+
+ $this->assertInstanceOf(
+ $expected,
+ call_user_func_array( [ $this->applicationFactory, 'create' ], $arguments )
+ );
+ }
+
+ public function callbackContainerProvider() {
+
+ $provider[] = [
+ 'CachedQueryResultPrefetcher',
+ [],
+ '\SMW\Query\Result\CachedQueryResultPrefetcher'
+ ];
+
+ $provider[] = [
+ 'FactboxFactory',
+ [],
+ 'SMW\Factbox\FactboxFactory'
+ ];
+
+ $provider[] = [
+ 'PropertyAnnotatorFactory',
+ [],
+ 'SMW\PropertyAnnotatorFactory'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CacheFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CacheFactoryTest.php
new file mode 100644
index 00000000..93021425
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CacheFactoryTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests;
+
+use Onoi\Cache\Cache;
+use Onoi\Cache\NullCache;
+use SMW\CacheFactory;
+
+/**
+ * @covers \SMW\CacheFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CacheFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\CacheFactory',
+ new CacheFactory( 'hash' )
+ );
+ }
+
+ public function testGetMainCacheType() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertEquals(
+ 'hash',
+ $instance->getMainCacheType()
+ );
+
+ $instance = new CacheFactory( CACHE_NONE );
+
+ $this->assertEquals(
+ CACHE_NONE,
+ $instance->getMainCacheType()
+ );
+ }
+
+ public function testGetCachePrefix() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getCachePrefix()
+ );
+ }
+
+ public function testGetPurgeCacheKey() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getPurgeCacheKey( $title )
+ );
+ }
+
+ public function testCanConstructCacheOptions() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $cacheOptions = $instance->newCacheOptions( [
+ 'useCache' => true,
+ 'ttl' => 0
+ ] );
+
+ $this->assertTrue(
+ $cacheOptions->useCache
+ );
+ }
+
+ public function testIncompleteCacheOptionsThrowsException() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ $cacheOptions = $instance->newCacheOptions( [
+ 'useCache' => true
+ ] );
+ }
+
+ public function testCanConstructFixedInMemoryCache() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInstanceOf(
+ 'Onoi\Cache\Cache',
+ $instance->newFixedInMemoryCache()
+ );
+ }
+
+ public function testCanConstructNullCache() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInstanceOf(
+ 'Onoi\Cache\Cache',
+ $instance->newNullCache()
+ );
+ }
+
+ public function testCanConstructMediaWikiCompositeCache() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInstanceOf(
+ 'Onoi\Cache\Cache',
+ $instance->newMediaWikiCompositeCache( CACHE_NONE )
+ );
+
+ $this->assertInstanceOf(
+ 'Onoi\Cache\Cache',
+ $instance->newMediaWikiCompositeCache( $instance->getMainCacheType() )
+ );
+ }
+
+ public function testCanConstructMediaWikiCache() {
+
+ $instance = new CacheFactory();
+
+ $this->assertInstanceOf(
+ Cache::class,
+ $instance->newMediaWikiCache( 'hash' )
+ );
+ }
+
+ public function testCanConstructCacheByType() {
+
+ $instance = new CacheFactory();
+
+ $this->assertInstanceOf(
+ NullCache::class,
+ $instance->newCacheByType( CACHE_NONE )
+ );
+
+ $this->assertInstanceOf(
+ Cache::class,
+ $instance->newCacheByType( 'hash' )
+ );
+ }
+
+ public function testCanConstructBlobStore() {
+
+ $instance = new CacheFactory( 'hash' );
+
+ $this->assertInstanceOf(
+ 'Onoi\BlobStore\BlobStore',
+ $instance->newBlobStore( 'foo', CACHE_NONE )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CachedPropertyValuesPrefetcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CachedPropertyValuesPrefetcherTest.php
new file mode 100644
index 00000000..f4e3d50d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CachedPropertyValuesPrefetcherTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\CachedPropertyValuesPrefetcher;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\CachedPropertyValuesPrefetcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CachedPropertyValuesPrefetcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $blobStore;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->blobStore = $this->getMockBuilder( '\Onoi\BlobStore\BlobStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\CachedPropertyValuesPrefetcher',
+ new CachedPropertyValuesPrefetcher( $this->store, $this->blobStore )
+ );
+ }
+
+ public function testGetPropertyValues() {
+
+ $instance = new CachedPropertyValuesPrefetcher(
+ $this->store,
+ $this->blobStore
+ );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getPropertyValues( DIWikiPage::newFromText( 'Foo' ), new DIProperty( 'Bar' ) )
+ );
+ }
+
+ public function testQueryPropertyValuesFor() {
+
+ $expected = [
+ DIWikiPage::newFromText( 'Foo' )
+ ];
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( $expected ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new CachedPropertyValuesPrefetcher(
+ $this->store,
+ $this->blobStore
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->queryPropertyValuesFor( $query )
+ );
+ }
+
+ public function testGetPropertyValuesFromCache() {
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->atLeastOnce() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'Bar:123' ) )
+ ->will( $this->returnValue( 1001 ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $instance = new CachedPropertyValuesPrefetcher(
+ $this->store,
+ $this->blobStore
+ );
+
+ $this->assertEquals(
+ 1001,
+ $instance->getPropertyValues( DIWikiPage::newFromText( 'Foo#123' ), new DIProperty( 'Bar' ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ChangePropListenerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ChangePropListenerTest.php
new file mode 100644
index 00000000..f05ef0bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ChangePropListenerTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ChangePropListener;
+use SMW\DIProperty;
+
+/**
+ * @covers \SMW\ChangePropListener
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropListenerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChangePropListener::class,
+ new ChangePropListener()
+ );
+ }
+
+ public function testRegisterListenerAndCall() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'getSMWPropertyID' )
+ ->with( $this->equalTo( new DIProperty( __METHOD__ ) ) )
+ ->will( $this->returnValue( 42 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'execute' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'execute' )
+ ->with( $this->equalTo( [ 'Bar' ] ) );
+
+ $instance = new ChangePropListener();
+ $instance->clearListeners();
+
+ $instance->addListenerCallback( __METHOD__, function( $record ) use( $test ) {
+ $test->execute( $record );
+ } );
+
+ $instance->loadListeners( $store );
+
+ $instance->record( 42, [ 'Bar' ] );
+
+ $instance->callListeners();
+ TestEnvironment::executePendingDeferredUpdates();
+
+ $instance->clearListeners();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CompatibilityModeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CompatibilityModeTest.php
new file mode 100644
index 00000000..6ba5090e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/CompatibilityModeTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\CompatibilityMode;
+
+/**
+ * @covers \SMW\CompatibilityMode
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CompatibilityModeTest extends \PHPUnit_Framework_TestCase {
+
+ public function testExtensionNotEnabled() {
+
+ $this->assertInternalType(
+ 'boolean',
+ CompatibilityMode::extensionNotEnabled()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/CallbackConnectionProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/CallbackConnectionProviderTest.php
new file mode 100644
index 00000000..11284691
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/CallbackConnectionProviderTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW\Tests\Connection;
+
+use SMW\Connection\CallbackConnectionProvider;
+
+/**
+ * @covers \SMW\Connection\CallbackConnectionProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CallbackConnectionProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $conection;
+
+ public function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $callback = function() {};
+
+ $this->assertInstanceOf(
+ CallbackConnectionProvider::class,
+ new CallbackConnectionProvider( $callback )
+ );
+ }
+
+ public function getConnection() {
+ return $this->connection;
+ }
+
+ public function testGetConnectionFormCallback() {
+
+ $callback = function() {
+ return $this->connection;
+ };
+
+ $instance = new CallbackConnectionProvider(
+ $callback
+ );
+
+ $this->assertEquals(
+ $this->connection,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetConnectionFormStaticCallback() {
+
+ $instance = new CallbackConnectionProvider(
+ [ $this, 'getConnection' ]
+ );
+
+ $this->assertEquals(
+ $this->connection,
+ $instance->getConnection()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionManagerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionManagerTest.php
new file mode 100644
index 00000000..353032d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionManagerTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests\Connection;
+
+use SMW\Connection\ConnectionManager;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Connection\ConnectionManager
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConnectionManagerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConnectionManager::class,
+ new ConnectionManager()
+ );
+ }
+
+ public function testDefaultRegisteredConnectionProvided() {
+
+ $instance = new ConnectionManager();
+ $instance->releaseConnections();
+
+ $connection = $instance->getConnection( 'mw.db' );
+
+ $this->assertSame(
+ $connection,
+ $instance->getConnection( 'mw.db' )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Database',
+ $connection
+ );
+
+ $instance->releaseConnections();
+
+ $this->assertNotSame(
+ $connection,
+ $instance->getConnection( 'mw.db' )
+ );
+ }
+
+ public function testUnregisteredConnectionTypeThrowsException() {
+
+ $instance = new ConnectionManager();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection( 'mw.master' );
+ }
+
+ public function testRegisterConnectionProvider() {
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->once() )
+ ->method( 'getConnection' );
+
+ $instance = new ConnectionManager();
+ $instance->registerConnectionProvider( 'foo', $connectionProvider );
+
+ $instance->getConnection( 'FOO' );
+ }
+
+ public function testRegisterCallbackConnection() {
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->once() )
+ ->method( 'getConnection' );
+
+ $callback = function() use( $connectionProvider ) {
+ return $connectionProvider->getConnection();
+ };
+
+ $instance = new ConnectionManager();
+ $instance->registerCallbackConnection( 'foo', $callback );
+
+ $instance->getConnection( 'FOO' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionProviderRefTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionProviderRefTest.php
new file mode 100644
index 00000000..87690be9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Connection/ConnectionProviderRefTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\Connection;
+
+use SMW\Connection\ConnectionProviderRef;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Connection\ConnectionProviderRef
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConnectionProviderRefTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConnectionProviderRef::class,
+ new ConnectionProviderRef( [] )
+ );
+ }
+
+ public function testGetAndReleaseConnection() {
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $connectionProvider->expects( $this->once() )
+ ->method( 'releaseConnection' );
+
+ $instance = new ConnectionProviderRef(
+ [
+ 'Foo' => $connectionProvider
+ ]
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->getConnection( 'Foo' )
+ );
+
+ $instance->releaseConnection();
+ }
+
+ public function testNoMatchableConnectionProviderThrowsException() {
+
+ $instance = new ConnectionProviderRef(
+ [
+ 'Foo' => 'Bar'
+ ]
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection( 'Foo' );
+ }
+
+ public function testNoMatchableKeyThrowsException() {
+
+ $instance = new ConnectionProviderRef( [] );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataItemFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataItemFactoryTest.php
new file mode 100644
index 00000000..ad5e6db4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataItemFactoryTest.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataItemFactory;
+use SMWDITime as DITime;
+use SMWDIUri as DIUri;
+
+/**
+ * @covers \SMW\DataItemFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DataItemFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataItemFactory',
+ new DataItemFactory()
+ );
+ }
+
+ public function testCanConstructDIError() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $instance->newDIError( 'Foo' )
+ );
+ }
+
+ public function testCanConstructDIProperty() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\DIProperty',
+ $instance->newDIProperty( 'Foo bar' )
+ );
+ }
+
+ public function testCanConstructDIWikiPage() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->newDIWikiPage( 'Foo' )
+ );
+ }
+
+ public function testCanConstructDIWikiPageFromTitle() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->newDIWikiPage( $title )
+ );
+ }
+
+ public function testCanConstructDIContainer() {
+
+ $containerSemanticData = $this->getMockBuilder( '\SMWContainerSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $instance->newDIContainer( $containerSemanticData )
+ );
+ }
+
+ public function testCanConstructContainerSemanticData() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\DataModel\ContainerSemanticData',
+ $instance->newContainerSemanticData( $subject )
+ );
+ }
+
+ public function testCanConstructDINumber() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMWDINumber',
+ $instance->newDINumber( 42 )
+ );
+ }
+
+ public function testCanConstructDIBlob() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMWDIBlob',
+ $instance->newDIBlob( 'Foo' )
+ );
+ }
+
+ public function testCanConstructDIBoolean() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMWDIBoolean',
+ $instance->newDIBoolean( true )
+ );
+ }
+
+ public function testCanConstructDIConcept() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\DIConcept',
+ $instance->newDIConcept( 'Foo' )
+ );
+ }
+
+ public function testCanConstructDIUri() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ DIUri::class,
+ $instance->newDIUri( 'http', 'example.org' )
+ );
+ }
+
+ public function testCanConstructDITime() {
+
+ $instance = new DataItemFactory();
+
+ $this->assertInstanceOf(
+ DITime::class,
+ $instance->newDITime( 1, '1900' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/ContainerSemanticDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/ContainerSemanticDataTest.php
new file mode 100644
index 00000000..63389173
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/ContainerSemanticDataTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Tests\DataModel;
+
+use SMW\DataItemFactory;
+use SMW\DataModel\ContainerSemanticData;
+use SMW\SemanticData;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataModel\ContainerSemanticData
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ContainerSemanticDataTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ContainerSemanticData::class,
+ new ContainerSemanticData( $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN ) )
+ );
+ }
+
+ public function testMakeAnonymousContainer() {
+
+ $instance = ContainerSemanticData::makeAnonymousContainer();
+ $instance->skipAnonymousCheck();
+
+ $this->assertInstanceOf(
+ ContainerSemanticData::class,
+ $instance
+ );
+
+ $this->assertTrue(
+ $instance->hasAnonymousSubject()
+ );
+ }
+
+ public function testGetSubjectOnAnonymousContainerWithoutSkipThrowsException() {
+
+ $instance = ContainerSemanticData::makeAnonymousContainer();
+
+ $this->setExpectedException( '\SMW\Exception\DataItemException' );
+ $instance->getSubject();
+ }
+
+ public function testCopyDataFrom() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN );
+
+ $semanticData = new SemanticData(
+ $subject
+ );
+
+ $instance = ContainerSemanticData::makeAnonymousContainer( true, true );
+
+ $this->assertNotEquals(
+ $subject,
+ $instance->getSubject()
+ );
+
+ $instance->copyDataFrom( $semanticData );
+
+ $this->assertEquals(
+ $subject,
+ $instance->getSubject()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/SubSemanticDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/SubSemanticDataTest.php
new file mode 100644
index 00000000..28b0cedf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataModel/SubSemanticDataTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\DataModel;
+
+use SMW\DataItemFactory;
+use SMW\DataModel\ContainerSemanticData;
+use SMW\DataModel\SubSemanticData;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataModel\SubSemanticData
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SubSemanticDataTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SubSemanticData::class,
+ new SubSemanticData( $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN ) )
+ );
+ }
+
+ public function testAddSubSemanticData() {
+
+ $instance = new SubSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN )
+ );
+
+ $this->assertEmpty(
+ $instance->getSubSemanticData()
+ );
+
+ $containerSemanticData = new ContainerSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN, '', 'Foo' )
+ );
+
+ $instance->addSubSemanticData(
+ $containerSemanticData
+ );
+
+ $this->assertNotEmpty(
+ $instance->getSubSemanticData()
+ );
+ }
+
+ public function testAddSubSemanticDataWithMismatchedSubjectThrowsException() {
+
+ $instance = new SubSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN )
+ );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException');
+
+ $instance->addSubSemanticData(
+ ContainerSemanticData::makeAnonymousContainer( true, true )
+ );
+ }
+
+ public function testRemoveSubSemanticData() {
+
+ $instance = new SubSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN )
+ );
+
+ $containerSemanticData = new ContainerSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN, '', 'Foo' )
+ );
+
+ $instance->addSubSemanticData(
+ $containerSemanticData
+ );
+
+ $this->assertTrue(
+ $instance->hasSubSemanticData( 'Foo' )
+ );
+
+ $instance->removeSubSemanticData(
+ $containerSemanticData
+ );
+
+ $this->assertFalse(
+ $instance->hasSubSemanticData( 'Foo' )
+ );
+ }
+
+ public function testRemoveProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new SubSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN )
+ );
+
+ $containerSemanticData = new ContainerSemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_MAIN, '', 'Foo' )
+ );
+
+ $containerSemanticData->addPropertyObjectValue(
+ $property,
+ $this->dataItemFactory->newDIBlob( 'Bar' )
+ );
+
+ $instance->addSubSemanticData(
+ $containerSemanticData
+ );
+
+ $subSemanticData = $instance->findSubSemanticData( 'Foo' );
+
+ $this->assertTrue(
+ $subSemanticData->hasProperty( $property )
+ );
+
+ $instance->removeProperty(
+ $property
+ );
+
+ $this->assertFalse(
+ $subSemanticData->hasProperty( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataTypeRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataTypeRegistryTest.php
new file mode 100644
index 00000000..b4477bc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataTypeRegistryTest.php
@@ -0,0 +1,454 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataTypeRegistry;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\DataTypeRegistry
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DataTypeRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataTypeRegistry;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataTypeRegistry = DataTypeRegistry::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->dataTypeRegistry->clear();
+
+ parent::tearDown();
+ }
+
+ public function testGetInstance() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataTypeRegistry',
+ $this->dataTypeRegistry
+ );
+
+ $this->assertSame(
+ $this->dataTypeRegistry,
+ DataTypeRegistry::getInstance()
+ );
+
+ DataTypeRegistry::clear();
+
+ $this->assertNotSame(
+ $this->dataTypeRegistry,
+ DataTypeRegistry::getInstance()
+ );
+ }
+
+ public function testIsEqualItemType() {
+
+ $this->assertTrue(
+ $this->dataTypeRegistry->isEqualByType( '_wpg', '__sob' )
+ );
+
+ $this->assertFalse(
+ $this->dataTypeRegistry->isEqualByType( '_wpg', '_txt' )
+ );
+ }
+
+ public function testRegisterDatatype() {
+
+ $this->assertNull(
+ $this->dataTypeRegistry->getDataTypeClassById( '_foo' ),
+ 'Asserts that prior registration getDataTypeClassById() returns null'
+ );
+
+ $this->dataTypeRegistry
+ ->registerDataType( '_foo', '\SMW\Tests\FooValue', DataItem::TYPE_NOTYPE, 'FooValue' );
+
+ $this->assertEquals(
+ '\SMW\Tests\FooValue',
+ $this->dataTypeRegistry->getDataTypeClassById( '_foo' ),
+ 'Asserts that getDataTypeClassById() returns the registered class'
+ );
+
+ $this->assertEquals(
+ DataItem::TYPE_NOTYPE,
+ $this->dataTypeRegistry->getDataItemId( '_foo' )
+ );
+
+ $this->assertEquals(
+ 'FooValue',
+ $this->dataTypeRegistry->findTypeLabel( '_foo' )
+ );
+
+ $this->assertEmpty(
+ $this->dataTypeRegistry->findTypeLabel( 'FooNoLabel' )
+ );
+
+ $this->assertEquals(
+ DataItem::TYPE_NOTYPE,
+ $this->dataTypeRegistry->getDataItemId( 'FooBar' )
+ );
+ }
+
+ public function testRegisterDatatypeIdAndAlias() {
+
+ $this->dataTypeRegistry
+ ->registerDataType( '_foo', '\SMW\Tests\FooValue', DataItem::TYPE_NOTYPE, 'FooValue' );
+
+ $this->assertEmpty(
+ $this->dataTypeRegistry->findTypeByLabel( 'FooBar' )
+ );
+
+ $this->dataTypeRegistry->registerDataTypeAlias( '_foo', 'FooBar' );
+
+ $this->assertTrue(
+ $this->dataTypeRegistry->isRegistered( '_foo' )
+ );
+
+ $this->assertEquals(
+ '_foo',
+ $this->dataTypeRegistry->findTypeByLabel( 'FooBar' ),
+ 'Asserts that findTypeByLabel returns the registered alias label'
+ );
+ }
+
+ public function testGetDefaultDataItemTypeIdForValidDataItemType() {
+ $this->assertInternalType(
+ 'string',
+ $this->dataTypeRegistry->getDefaultDataItemByType( 1 )
+ );
+ }
+
+ public function testGetDefaultDataItemTypeIdForInvalidDataItemType() {
+ $this->assertNull(
+ $this->dataTypeRegistry->getDefaultDataItemByType( 9999 )
+ );
+ }
+
+ public function testFindCanonicalLabelById() {
+ $this->assertSame(
+ 'Text',
+ $this->dataTypeRegistry->findCanonicalLabelById( '_txt' )
+ );
+ }
+
+ public function testTypeIdAndLabelAsLanguageIndependantInvocation() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [ '_wpg' => 'Page' ] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [ 'URI' => '_uri' ] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $this->assertEquals(
+ '_wpg',
+ $instance->findTypeByLabel( 'Page' ),
+ 'Asserts that findTypeByLabel returns empty label'
+ );
+
+ $this->assertEquals(
+ [ '_wpg' => 'Page' ],
+ $instance->getKnownTypeLabels(),
+ 'Asserts that getKnownTypeLabels returns an array'
+ );
+ }
+
+ public function testKnownAliasAsLanguageIndependantInvocation() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [ 'URI' => '_uri' ] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $this->assertEquals(
+ [ 'URI' => '_uri' ],
+ $instance->getKnownTypeAliases(),
+ 'Asserts that getKnownTypeAliases returns an array'
+ );
+ }
+
+ public function testExtraneousCallbackFunction() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry( $lang );
+ $arg = 'foo';
+
+ $instance->registerExtraneousFunction(
+ 'foo',
+ function ( $arg ) {
+ return 'bar' . $arg;
+ }
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getExtraneousFunctions()
+ );
+ }
+
+ public function testLookupByLabelIsCaseInsensitive() {
+ $caseVariants = [
+ 'page',
+ 'Page',
+ 'PAGE',
+ 'pAgE',
+ ];
+
+ foreach ( $caseVariants as $caseVariant ) {
+ $this->assertRegistryFindsIdForLabels( $caseVariant, $caseVariants );
+ $this->assertRegistryFindsIdForAliases( $caseVariant, $caseVariants );
+ }
+ }
+
+ public function testFindTypeByLabelAndLanguage() {
+
+ $this->assertSame(
+ '_num',
+ $this->dataTypeRegistry->findTypeByLabelAndLanguage( 'Número', 'es' )
+ );
+
+ $this->assertSame(
+ '_num',
+ $this->dataTypeRegistry->findTypeByLabelAndLanguage( '数值型', 'zh-Hans' )
+ );
+ }
+
+ public function testSubDataType() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $instance->registerDataType( '_foo', 'FooValue', DataItem::TYPE_NOTYPE, false, true );
+
+ $this->assertTrue(
+ $instance->isSubDataType( '_foo' )
+ );
+ }
+
+ public function testBrowsableType() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $instance->registerDataType( '_foo', 'FooValue', DataItem::TYPE_NOTYPE, false, true, true );
+ $instance->registerDataType( '_bar', 'BarValue', DataItem::TYPE_NOTYPE );
+
+ $this->assertTrue(
+ $instance->isBrowsableType( '_foo' )
+ );
+
+ $this->assertFalse(
+ $instance->isBrowsableType( '_bar' )
+ );
+ }
+
+ public function testGetFieldType() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $instance->registerDataType( '_foo', 'FooValue', DataItem::TYPE_BLOB, false, true );
+
+ $this->assertEquals(
+ '_txt',
+ $instance->getFieldType( '_foo' )
+ );
+ }
+
+ protected function assertRegistryFindsIdForLabels( $inputLabel, array $equivalentLabels ) {
+
+ $id = '_wpg';
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [ $inputLabel => $id ] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ foreach ( $equivalentLabels as $caseVariant ) {
+ $this->assertEquals( $id, $instance->findTypeByLabel( $caseVariant ) );
+ }
+ }
+
+ protected function assertRegistryFindsIdForAliases( $inputLabel, array $equivalentLabels ) {
+ $id = '_wpg';
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [ $id => $inputLabel ] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ foreach ( $equivalentLabels as $caseVariant ) {
+ $this->assertEquals( $id, $instance->findTypeByLabel( $caseVariant ) );
+ }
+ }
+
+ public function testExtensionData() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getDatatypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getCanonicalDatatypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DataTypeRegistry(
+ $lang
+ );
+
+ $instance->registerDataType(
+ '__foo', '\SMW\Tests\FooValue', DataItem::TYPE_NOTYPE, 'FooValue'
+ );
+
+ $instance->setExtensionData( '__foo', [ 'ext.test' => 'test' ] );
+
+ $this->assertEquals(
+ [ 'ext.test' => 'test' ],
+ $instance->getExtensionData( '__foo' )
+ );
+ }
+
+}
+
+class FooValue {
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataUpdaterTest.php
new file mode 100644
index 00000000..5ee34e2d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataUpdaterTest.php
@@ -0,0 +1,352 @@
+<?php
+
+namespace SMW\Tests\Updater;
+
+use SMW\DataUpdater;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DataUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $transactionalCallableUpdate;
+ private $semanticDataFactory;
+ private $spyLogger;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgPageSpecialProperties' => [],
+ 'smwgEnableUpdateJobs' => false,
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ]
+ ] );
+
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->setLogger( $this->spyLogger );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+
+ $this->transactionalCallableUpdate = $this->getMockBuilder( '\SMW\MediaWiki\Deferred\TransactionalCallableUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'DeferredTransactionalCallableUpdate', $this->transactionalCallableUpdate );
+
+ $this->semanticDataFactory = $this->testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ DataUpdater::class,
+ new DataUpdater( $this->store, $semanticData )
+ );
+ }
+
+ public function testDoUpdateForDefaultSettings() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $instance = new DataUpdater(
+ $this->store,
+ $semanticData
+ );
+
+ $this->assertTrue(
+ $instance->doUpdate()
+ );
+ }
+
+ public function testDeferredUpdate() {
+
+ $this->transactionalCallableUpdate->expects( $this->once() )
+ ->method( 'pushUpdate' );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $instance = new DataUpdater(
+ $this->store,
+ $semanticData
+ );
+
+ $instance->isDeferrableUpdate( true );
+ $instance->doUpdate();
+ }
+
+ /**
+ * @dataProvider updateJobStatusProvider
+ */
+ public function testDoUpdateForValidRevision( $updateJobStatus ) {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'updateData' );
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $content ) );
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getRevision' )
+ ->will( $this->returnValue( $revision ) );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->atLeastOnce() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $this->testEnvironment->registerObject( 'PageCreator', $pageCreator );
+
+ $instance = new DataUpdater(
+ $store,
+ $semanticData
+ );
+
+ $instance->canCreateUpdateJob(
+ $updateJobStatus
+ );
+
+ $this->assertTrue(
+ $instance->doUpdate()
+ );
+ }
+
+ /**
+ * @dataProvider updateJobStatusProvider
+ */
+ public function testDoUpdateForNullRevision( $updateJobStatus ) {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $idTable->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'clearData', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->once() )
+ ->method( 'clearData' )
+ ->with( $this->equalTo( $semanticData->getSubject() ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->atLeastOnce() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $this->testEnvironment->registerObject( 'PageCreator', $pageCreator );
+
+ $instance = new DataUpdater(
+ $store,
+ $semanticData
+ );
+
+ $instance->canCreateUpdateJob(
+ $updateJobStatus
+ );
+
+ $this->assertTrue(
+ $instance->doUpdate()
+ );
+ }
+
+ public function testDoUpdateForTitleInUnknownNs() {
+
+ $wikiPage = new DIWikiPage(
+ 'Foo',
+ -32768, // This namespace does not exist
+ ''
+ );
+
+ $semanticData = $this->semanticDataFactory->setSubject( $wikiPage )->newEmptySemanticData();
+
+ $instance = new DataUpdater(
+ $this->store,
+ $semanticData
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->doUpdate()
+ );
+ }
+
+ public function testDoUpdateForSpecialPage() {
+
+ $wikiPage = new DIWikiPage(
+ 'Foo',
+ NS_SPECIAL,
+ ''
+ );
+
+ $semanticData = $this->semanticDataFactory->setSubject( $wikiPage )->newEmptySemanticData();
+
+ $instance = new DataUpdater(
+ $this->store,
+ $semanticData
+ );
+
+ $this->assertFalse(
+ $instance->doUpdate()
+ );
+ }
+
+ public function testForYetUnknownRedirectTarget() {
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $content ) );
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getRevision' )
+ ->will( $this->returnValue( $revision ) );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->atLeastOnce() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $this->testEnvironment->registerObject( 'PageCreator', $pageCreator );
+
+ $source = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject = new DIWikiPage(
+ 'Foo',
+ NS_MAIN
+ );
+
+ $target = new DIWikiPage(
+ 'Bar',
+ NS_MAIN
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'changeTitle' );
+
+ $store->setOption( 'smwgSemanticsEnabled', true );
+ $store->setOption( 'smwgAutoRefreshSubject', true );
+
+ $store->setLogger( $this->spyLogger );
+
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ $target
+ );
+
+ $instance = new DataUpdater(
+ $store,
+ $semanticData
+ );
+
+ $instance->canCreateUpdateJob( true );
+ $instance->doUpdate();
+ }
+
+ public function updateJobStatusProvider() {
+
+ $provider = [
+ [ true ],
+ [ false ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValueFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValueFactoryTest.php
new file mode 100644
index 00000000..65c0dcb1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValueFactoryTest.php
@@ -0,0 +1,341 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMWDataItem;
+
+/**
+ * @covers \SMW\DataValueFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DataValueFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ DataValueFactory::getInstance()->clear();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider typeIdValueDataProvider
+ */
+ public function testNewTypeIdValue( $typeId, $value, $expectedValue, $expectedInstance ) {
+
+ $dataValue = DataValueFactory::getInstance()->newTypeIdValue( $typeId, $value );
+
+ $this->assertInstanceOf(
+ $expectedInstance,
+ $dataValue
+ );
+
+ if ( $dataValue->getErrors() === [] ){
+ return $this->assertEquals(
+ $expectedValue,
+ $dataValue->getWikiValue()
+ );
+ }
+
+ $this->assertInternalType(
+ 'array',
+ $dataValue->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider propertyObjectValueDataProvider
+ */
+ public function testNewPropertyObjectValue( $propertyName, $value, $expectedValue, $expectedInstance ) {
+
+ $propertyDV = DataValueFactory::getInstance()->newPropertyValueByLabel( $propertyName );
+ $propertyDI = $propertyDV->getDataItem();
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty( $propertyDI, $value );
+
+ // Check the returned instance
+ $this->assertInstanceOf( $expectedInstance, $dataValue );
+
+ if ( $dataValue->getErrors() === [] ){
+ $this->assertInstanceOf( 'SMWDIProperty', $dataValue->getProperty() );
+ $this->assertContains( $propertyName, $dataValue->getProperty()->getLabel() );
+ if ( $dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_WIKIPAGE ){
+ $this->assertEquals( $expectedValue, $dataValue->getWikiValue() );
+ }
+ } else {
+ $this->assertInternalType( 'array', $dataValue->getErrors() );
+ }
+
+ // Check interface parameters
+ $dataValue = DataValueFactory::getInstance()->newDataValueByProperty(
+ $propertyDI,
+ $value,
+ 'FooCaption',
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $this->assertInstanceOf(
+ $expectedInstance,
+ $dataValue
+ );
+ }
+
+ /**
+ * @dataProvider propertyValueDataProvider
+ */
+ public function testAddPropertyValueByText( $propertyName, $value, $expectedValue, $expectedInstance ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText( $propertyName, $value );
+
+ // Check the returned instance
+ $this->assertInstanceOf( $expectedInstance, $dataValue );
+
+ if ( $dataValue->getErrors() === [] ){
+ $this->assertInstanceOf( 'SMWDIProperty', $dataValue->getProperty() );
+ $this->assertContains( $propertyName, $dataValue->getProperty()->getLabel() );
+ if ( $dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_WIKIPAGE ){
+ $this->assertEquals( $expectedValue, $dataValue->getWikiValue() );
+ }
+ } else {
+ $this->assertInternalType( 'array', $dataValue->getErrors() );
+ }
+
+ // Check interface parameters
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $propertyName,
+ $value,
+ 'FooCaption',
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $this->assertInstanceOf(
+ $expectedInstance,
+ $dataValue
+ );
+ }
+
+ public function testTryToCreateDataValueUsingRestrictedPropertyValue() {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText( 'Has subobject', 'Foo' );
+
+ $this->assertInstanceOf(
+ '\SMWErrorValue',
+ $dataValue
+ );
+
+ $this->assertNotEmpty(
+ $dataValue->getErrors()
+ );
+ }
+
+ public function testToCreateDataValueUsingLegacyNewPropertyValueMethod() {
+
+ $dataValue = DataValueFactory::getInstance()->newPropertyValue( 'Bar', 'Foo' );
+
+ $this->assertInstanceOf(
+ '\SMWDataValue',
+ $dataValue
+ );
+ }
+
+ /**
+ * Issue 673
+ */
+ public function testEnforceFirstUpperCaseForDisabledCapitalLinks() {
+
+ $wgCapitalLinks = $GLOBALS['wgCapitalLinks'];
+ $GLOBALS['wgCapitalLinks'] = false;
+
+ $instance = DataValueFactory::getInstance();
+
+ $dataValue = $instance->newDataValueByText(
+ 'has type',
+ 'number',
+ null,
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY )
+ );
+
+ $this->assertEquals(
+ '_TYPE',
+ $dataValue->getProperty()->getKey()
+ );
+
+ $GLOBALS['wgCapitalLinks'] = $wgCapitalLinks;
+ }
+
+ public function testNewPropertyValueByLabel() {
+
+ $dataValue = DataValueFactory::getInstance()->newPropertyValueByLabel(
+ 'Foo',
+ 'Bar',
+ new DIWikiPage( 'Foobar', SMW_NS_PROPERTY )
+ );
+
+ $this->assertInstanceOf(
+ '\SMWPropertyValue',
+ $dataValue
+ );
+
+ $this->assertSame(
+ 'Bar',
+ $dataValue->getCaption()
+ );
+ }
+
+ /**
+ * @dataProvider newDataValueByItemDataProvider
+ */
+ public function testNewDataItemValue( $setup ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $setup['dataItem'],
+ $setup['property'],
+ $setup['caption']
+ );
+
+ $this->assertInstanceOf(
+ 'SMWDataValue',
+ $dataValue
+ );
+ }
+
+ public function newDataValueByItemDataProvider() {
+
+ $provider = [];
+
+ $dataItem = new DIWikiPage( 'Foo', NS_MAIN );
+ $property = new DIProperty( 'Bar' );
+
+ // #0
+ $provider[] = [
+ [
+ 'dataItem' => $dataItem,
+ 'property' => null,
+ 'caption' => false
+ ]
+ ];
+
+ // #0
+ $provider[] = [
+ [
+ 'dataItem' => $dataItem,
+ 'property' => $property,
+ 'caption' => false
+ ]
+ ];
+
+ // #1
+ $provider[] = [
+ [
+ 'dataItem' => $dataItem,
+ 'property' => null,
+ 'caption' => 'Foo'
+ ]
+ ];
+
+ // #2
+ $provider[] = [
+ [
+ 'dataItem' => $dataItem,
+ 'property' => $property,
+ 'caption' => 'Bar'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function findTypeIdDataProvider() {
+ return [
+ [ 'URL' , '_uri' ], // #0
+ [ 'Page' , '_wpg' ], // #1
+ [ 'String' , '_txt' ], // #2
+ [ 'Text' , '_txt' ], // #3
+ [ 'Number' , '_num' ], // #4
+ [ 'Quantity' , '_qty' ], // #5
+ [ 'Date' , '_dat' ], // #6
+ [ 'Email' , '_ema' ], // #7
+ [ '' , '' ], // #8
+ ];
+ }
+
+ public function dataItemIdDataProvider() {
+ return [
+ [ '_txt' , SMWDataItem::TYPE_BLOB ], // #0
+ [ '_wpg' , SMWDataItem::TYPE_WIKIPAGE ], // #1
+ [ '_num' , SMWDataItem::TYPE_NUMBER ], // #2
+ [ '_dat' , SMWDataItem::TYPE_TIME ], // #3
+ [ '_uri' , SMWDataItem::TYPE_URI ], // #4
+ [ '_foo' , SMWDataItem::TYPE_NOTYPE ], // #5
+ ];
+ }
+
+ public function typeIdValueDataProvider() {
+ return [
+ [ '_txt' , 'Bar' , 'Bar' , 'SMWStringValue' ], // #0
+ [ '_txt' , 'Bar[[ Foo ]]' , 'Bar[[ Foo ]]' , 'SMWStringValue' ], // #1
+ [ '_txt' , '9001' , '9001' , 'SMWStringValue' ], // #2
+ [ '_txt' , 1001 , '1001' , 'SMWStringValue' ], // #3
+ [ '_txt' , '-%&$*' , '-%&$*' , 'SMWStringValue' ], // #4
+ [ '_txt' , '_Bar' , '_Bar' , 'SMWStringValue' ], // #5
+ [ '_txt' , 'bar' , 'bar' , 'SMWStringValue' ], // #6
+ [ '-_txt' , 'Bar' , 'Bar' , 'SMWErrorValue' ], // #7
+
+ [ '_wpg' , 'Bar' , 'Bar' , 'SMWWikiPageValue' ], // #8
+ [ '_wpg' , 'Bar' , 'Bar' , 'SMWWikiPageValue' ], // #9
+ [ '_wpg' , 'Bar[[ Foo ]]' , 'Bar[[ Foo ]]' , 'SMWWikiPageValue' ], // #10
+ [ '_wpg' , '9001' , '9001' , 'SMWWikiPageValue' ], // #11
+ [ '_wpg' , 1001 , '1001' , 'SMWWikiPageValue' ], // #12
+ [ '_wpg' , '-%&$*' , '-%&$*' , 'SMWWikiPageValue' ], // #13
+ [ '_wpg' , '_Bar' , 'Bar' , 'SMWWikiPageValue' ], // #14
+ [ '_wpg' , 'bar' , 'Bar' , 'SMWWikiPageValue' ], // #15
+ [ '-_wpg' , 'Bar' , 'Bar' , 'SMWErrorValue' ], // #16
+
+ [ '_dat' , '1 Jan 1970' , '1 Jan 1970' , 'SMWTimeValue' ], // #0
+ [ '_uri' , 'Foo' , 'Foo' , 'SMWURIValue' ], // #0
+ [ '_num' , 9001 , '9001' , 'SMWNumberValue' ], // #0
+ [ '_num' , 9001.5 , '9001.5' , 'SMWNumberValue' ], // #0
+ ];
+ }
+
+ public function propertyValueDataProvider() {
+ return [
+ [ 'Foo' , 'Bar' , 'Bar' , 'SMWDataValue' ], // #0
+ [ 'Foo' , 'Bar[[ Foo ]]' , 'Bar[[ Foo ]]' , 'SMWDataValue' ], // #1
+ [ 'Foo' , '9001' , '9001' , 'SMWDataValue' ], // #2
+ [ 'Foo' , 1001 , '1001' , 'SMWDataValue' ], // #3
+ [ 'Foo' , '-%&$*' , '-%&$*' , 'SMWDataValue' ], // #4
+ [ 'Foo' , '_Bar' , 'Bar' , 'SMWDataValue' ], // #5
+ [ 'Foo' , 'bar' , 'Bar' , 'SMWDataValue' ], // #6
+ [ '-Foo' , 'Bar' , '' , 'SMWErrorValue' ], // #7
+ [ '_Foo' , 'Bar' , '' , 'SMWPropertyValue' ], // #8
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ public function propertyObjectValueDataProvider() {
+ return [
+ [ 'Foo' , 'Bar' , 'Bar' , 'SMWDataValue' ], // #0
+ [ 'Foo' , 'Bar[[ Foo ]]' , 'Bar[[ Foo ]]' , 'SMWDataValue' ], // #1
+ [ 'Foo' , '9001' , '9001' , 'SMWDataValue' ], // #2
+ [ 'Foo' , 1001 , '1001' , 'SMWDataValue' ], // #3
+ [ 'Foo' , '-%&$*' , '-%&$*' , 'SMWDataValue' ], // #4
+ [ 'Foo' , '_Bar' , 'Bar' , 'SMWDataValue' ], // #5
+ [ 'Foo' , 'bar' , 'Bar' , 'SMWDataValue' ], // #6
+ [ '-Foo' , 'Bar' , 'Bar' , 'SMWWikiPageValue' ], // #7
+
+ // Will fail with "must be an instance of SMWDIProperty, instance of SMWDIError give"
+ // as propertyDI isn't checked therefore addPropertyValue() should be
+ // used as it will return a proper object
+ // array( '_Foo' , 'Bar' , '' , 'SMWDIProperty' ), // #8
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsListValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsListValueTest.php
new file mode 100644
index 00000000..27529573
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsListValueTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\AllowsListValue;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\AllowsListValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsListValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $dataValueServiceFactory;
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ AllowsListValue::class,
+ new AllowsListValue()
+ );
+ }
+
+ public function testGetShortWikiText() {
+
+ $instance = new AllowsListValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setDataItem(
+ $this->dataItemFactory->newDIBlob( 'Foo' )
+ );
+
+ $this->assertContains(
+ '[[MediaWiki:Smw_allows_list_Foo|Foo]]',
+ $instance->getShortWikiText()
+ );
+ }
+
+ public function testGetLongHtmlText() {
+
+ $instance = new AllowsListValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setDataItem(
+ $this->dataItemFactory->newDIBlob( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'MediaWiki:Smw_allows_list_Foo',
+ $instance->getLongHtmlText()
+ );
+ }
+
+ public function testGetShortHtmlText() {
+
+ $instance = new AllowsListValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setDataItem(
+ $this->dataItemFactory->newDIBlob( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'MediaWiki:Smw_allows_list_Foo',
+ $instance->getShortHtmlText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsPatternValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsPatternValueTest.php
new file mode 100644
index 00000000..8fe21499
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/AllowsPatternValueTest.php
@@ -0,0 +1,220 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\AllowsPatternValue;
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\AllowsPatternValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsPatternValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $dataValueServiceFactory;
+ private $constraintValueValidator;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'MediaWikiNsContentReader', $this->mediaWikiNsContentReader );
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $propertySpecificationLookup );
+
+ $this->constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new AllowsPatternValueParser( $this->mediaWikiNsContentReader ) ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $this->constraintValueValidator ) );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\AllowsPatternValue',
+ new AllowsPatternValue()
+ );
+ }
+
+ public function testHasErrorForMissingValue() {
+
+ $instance = new AllowsPatternValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->setUserValue( '' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testHasErrorForNonMatchingContent() {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( " \nFoo|Bar\n" ) );
+
+ $instance = new AllowsPatternValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->setUserValue( 'NotMatchable' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testHasNoErrorOnMatchableContent() {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( " \nFoo|Bar\n" ) );
+
+ $instance = new AllowsPatternValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testErrorOnNotEnabledFeatureWhenUserValueIsNotEmpty() {
+
+ $instance = new AllowsPatternValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'abc/e' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testGetShortWikiText() {
+
+ $allowsPatternValueParser = $this->getMockBuilder( '\SMW\DataValues\ValueParsers\AllowsPatternValueParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $allowsPatternValueParser->expects( $this->any() )
+ ->method( 'parse' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( $allowsPatternValueParser ) );
+
+ $dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $this->constraintValueValidator ) );
+
+ $instance = new AllowsPatternValue();
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->setDataValueServiceFactory(
+ $dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'abc/e' );
+
+ $this->assertContains(
+ 'Smw_allows_pattern',
+ $instance->getShortWikiText()
+ );
+ }
+
+ public function testGetShortHtmlText() {
+
+ $allowsPatternValueParser = $this->getMockBuilder( '\SMW\DataValues\ValueParsers\AllowsPatternValueParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $allowsPatternValueParser->expects( $this->any() )
+ ->method( 'parse' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( $allowsPatternValueParser ) );
+
+ $dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $this->constraintValueValidator ) );
+
+ $instance = new AllowsPatternValue();
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->setDataValueServiceFactory(
+ $dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'abc/e' );
+
+ $this->assertContains(
+ 'Smw_allows_pattern',
+ $instance->getShortHtmlText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/BooleanValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/BooleanValueTest.php
new file mode 100644
index 00000000..f0ed6c3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/BooleanValueTest.php
@@ -0,0 +1,205 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\BooleanValue;
+use SMW\Localizer;
+
+/**
+ * @covers \SMW\DataValues\BooleanValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class BooleanValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ BooleanValue::class,
+ new BooleanValue()
+ );
+ }
+
+ public function testGetBoolean() {
+
+ $instance = new BooleanValue();
+
+ $this->assertFalse(
+ $instance->getBoolean()
+ );
+
+ $instance->setUserValue( 'true' );
+
+ $this->assertTrue(
+ $instance->getBoolean()
+ );
+ }
+
+ public function testParseUserValueOnSpecificPageContentLanguage() {
+
+ $language = Localizer::getInstance()->getLanguage( 'ja' );
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new BooleanValue();
+
+ $instance->setContextPage( $subject );
+ $instance->setUserValue( '真' );
+
+ $this->assertTrue(
+ $instance->getBoolean()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getWikiValue()
+ );
+ }
+
+ public function testGetShortWikiTextForLocalizedOutputFormat() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'LOCL@fr' );
+
+ $this->assertEquals(
+ 'vrai',
+ $instance->getShortWikiText()
+ );
+ }
+
+ public function testGetShortHTMLTextForLocalizedOutputFormat() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'LOCL@fr' );
+
+ $this->assertEquals(
+ 'vrai',
+ $instance->getShortHTMLText()
+ );
+ }
+
+ public function testGetLongWikiTextForLocalizedOutputFormat() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'LOCL@fr' );
+
+ $this->assertEquals(
+ 'vrai',
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testGetLongWikiText_PlainFormatter() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( '-' );
+
+ $this->assertEquals(
+ 'true',
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testGetLongWikiText_NumFormatter() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'num' );
+
+ $this->assertEquals(
+ 1,
+ $instance->getLongWikiText()
+ );
+
+ $instance->setUserValue( 'false' );
+ $instance->setOutputFormat( 'num' );
+
+ $this->assertEquals(
+ 0,
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testGetLongWikiText_TickFormatter() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'tick' );
+
+ $this->assertEquals(
+ '✓',
+ $instance->getLongWikiText()
+ );
+
+ $instance->setUserValue( 'false' );
+ $instance->setOutputFormat( 'tick' );
+
+ $this->assertEquals(
+ '✕',
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testGetLongWikiText_xFormatter() {
+
+ $instance = new BooleanValue();
+
+ $instance->setUserValue( 'true' );
+ $instance->setOutputFormat( 'x' );
+
+ $this->assertEquals(
+ '<span style="font-family: sans-serif; ">X</span>',
+ $instance->getLongWikiText()
+ );
+
+ $instance->setUserValue( 'false' );
+ $instance->setOutputFormat( 'x' );
+
+ $this->assertEquals(
+ '&nbsp;',
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testRecognizeCanonicalForm() {
+
+ $instance = new BooleanValue();
+ $instance->setOption( BooleanValue::OPT_CONTENT_LANGUAGE, 'el' );
+
+ $instance->setUserValue( 'true' );
+
+ $this->assertEquals(
+ 'true',
+ $instance->getShortWikiText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ErrorMsgTextValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ErrorMsgTextValueTest.php
new file mode 100644
index 00000000..ada99376
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ErrorMsgTextValueTest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ErrorMsgTextValue;
+
+/**
+ * @covers \SMW\DataValues\ErrorMsgTextValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorMsgTextValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ErrorMsgTextValue',
+ new ErrorMsgTextValue()
+ );
+ }
+
+ public function testErrorOnEmptyUserValue() {
+
+ $instance = new ErrorMsgTextValue();
+ $instance->setUserValue( '' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testShortWikiText() {
+
+ $instance = new ErrorMsgTextValue();
+ $instance->setOption( ErrorMsgTextValue::OPT_USER_LANGUAGE, 'en' );
+ $instance->setUserValue( '[2,"smw-datavalue-uniqueness-constraint-error","Has Url","http:\/\/loremipsum.org\/2","Lorem ipsum\/2"]' );
+
+ $this->assertContains(
+ "''http://loremipsum.org/2''",
+ $instance->getShortWikiText( true )
+ );
+
+ $this->assertContains(
+ "''http://loremipsum.org/2''",
+ $instance->getShortWikiText( null )
+ );
+
+ $this->assertNotContains(
+ '<a rel="nofollow" class="external free" href="http://loremipsum.org/2">http://loremipsum.org/2</a>',
+ $instance->getShortWikiText( true )
+ );
+
+ $this->assertNotContains(
+ '<a rel="nofollow" class="external free" href="http://loremipsum.org/2">http://loremipsum.org/2</a>',
+ $instance->getShortWikiText( null )
+ );
+ }
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testValueOutput( $text, $expected ) {
+
+ $dataItem = $this->dataItemFactory->newDIBlob( $text );
+
+ $instance = new ErrorMsgTextValue();
+ $instance->setDataItem( $dataItem );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getWikiValue()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getShortWikiText()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getShortHTMLText()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getLongWikiText()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getLongHTMLText()
+ );
+ }
+
+ public function textProvider() {
+
+ $provider[] = [
+ 'Foo',
+ 'Foo'
+ ];
+
+ $provider[] = [
+ '[2,"Foo"]',
+ 'Foo'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalFormatterUriValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalFormatterUriValueTest.php
new file mode 100644
index 00000000..ef39d298
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalFormatterUriValueTest.php
@@ -0,0 +1,169 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\ExternalFormatterUriValue;
+
+/**
+ * @covers \SMW\DataValues\ExternalFormatterUriValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalFormatterUriValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ExternalFormatterUriValue::class,
+ new ExternalFormatterUriValue()
+ );
+ }
+
+ public function testTryToParseUserValueOnInvalidUrlFormat() {
+
+ $instance = new ExternalFormatterUriValue();
+ $instance->setUserValue( 'foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testTryToParseUserValueOnMissingPlaceholder() {
+
+ $instance = new ExternalFormatterUriValue();
+ $instance->setUserValue( 'http://example.org' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider uriProvider
+ */
+ public function testGetFormattedUri( $uri, $replacement, $expected ) {
+
+ $instance = new ExternalFormatterUriValue();
+ $instance->setUserValue( $uri );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getUriWithPlaceholderSubstitution( $replacement )
+ );
+ }
+
+ public function uriProvider() {
+
+ $provider[] = [
+ 'http://example.org/$1',
+ 'foo',
+ 'http://example.org/foo'
+ ];
+
+ $provider[] = [
+ 'http://example.org/$1#Bar<>',
+ 'foo',
+ 'http://example.org/foo#Bar%3C%3E'
+ ];
+
+ $provider[] = [
+ 'urn:abc:names:def:foo:dtd:xml:$1',
+ 'foo',
+ 'urn:abc:names:def:foo:dtd:xml:foo'
+ ];
+
+ // $provider[] = array(
+ // 'abc:$1',
+ // 'foo',
+ // 'http://example.org/foo'
+ // );
+
+ $provider[] = [
+ 'ftp://ftp.is.co.za.example.org/rfc/$1.txt',
+ 'foo',
+ 'ftp://ftp.is.co.za.example.org/rfc/foo.txt'
+ ];
+
+ $provider[] = [
+ 'gopher://spinaltap.micro.umn.example.edu/00/Weather/California/$1',
+ 'foo',
+ 'gopher:spinaltap.micro.umn.example.edu/00/Weather/California/foo'
+ ];
+
+ $provider[] = [
+ 'http://www.math.uio.no.example.net/faq/compression-faq/$1',
+ 'foo',
+ 'http://www.math.uio.no.example.net/faq/compression-faq/foo'
+ ];
+
+ $provider[] = [
+ 'mailto:$1@ifi.unizh.example.gov',
+ 'foo',
+ 'mailto:foo@ifi.unizh.example.gov'
+ ];
+
+ $provider[] = [
+ 'news:comp.$1.www.servers.unix',
+ 'foo',
+ 'news:comp.foo.www.servers.unix'
+ ];
+
+ $provider[] = [
+ 'telnet://melvyl.ucop.example.edu/$1',
+ 'foo',
+ 'telnet:melvyl.ucop.example.edu/foo'
+ ];
+
+ $provider[] = [
+ 'ldap://[2001:db8::7]/c=$1?objectClass?one',
+ 'foo',
+ 'ldap:%5B2001:db8::7%5D/c=foo?objectClass?one'
+ ];
+
+ $provider[] = [
+ 'mailto:$1.Doe@example.com',
+ 'foo',
+ 'mailto:foo.Doe@example.com'
+ ];
+
+ $provider[] = [
+ 'tel:+1-816-555-1212',
+ 'foo',
+ ''
+ ];
+
+ $provider[] = [
+ 'telnet://192.0.2.16:80/$1',
+ 'foo',
+ 'telnet:192.0.2.16:80/foo'
+ ];
+
+ $provider[] = [
+ 'urn:oasis:names:specification:docbook:dtd:xml:$1',
+ 'foo',
+ 'urn:oasis:names:specification:docbook:dtd:xml:foo'
+ ];
+
+ // https://phabricator.wikimedia.org/T160281
+ $provider[] = [
+ 'http://foo/bar/$1',
+ 'W%D6LLEKLA01',
+ 'http://foo/bar/W%D6LLEKLA01'
+ ];
+
+ // #3386
+ $provider[] = [
+ 'http://foo/bar/$1',
+ 'A/B/C',
+ 'http://foo/bar/A/B/C'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalIdentifierValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalIdentifierValueTest.php
new file mode 100644
index 00000000..63273da1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ExternalIdentifierValueTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\ExternalIdentifierValue;
+use SMW\PropertySpecificationLookup;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\ExternalIdentifierValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalIdentifierValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( PropertySpecificationLookup::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $externalFormatterUriValue = $this->getMockBuilder( '\SMW\DataValues\ExternalFormatterUriValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getPropertySpecificationLookup' )
+ ->will( $this->returnValue( $this->propertySpecificationLookup ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getDataValueFactory' )
+ ->will( $this->returnValue( DataValueFactory::getInstance() ) );
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ExternalIdentifierValue::class,
+ new ExternalIdentifierValue()
+ );
+ }
+
+ public function testGetShortWikiText() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getExternalFormatterUri' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIUri( 'http', 'example.org/$1' ) ) );
+
+ $instance = new ExternalIdentifierValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertEquals(
+ '<span class="plainlinks smw-eid">[http://example.org/foo foo]</span>',
+ $instance->getShortWikiText( 'linker' )
+ );
+ }
+
+ public function testGetShortWikiText_Nowiki() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getExternalFormatterUri' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIUri( 'http', 'example.org/$1' ) ) );
+
+ $instance = new ExternalIdentifierValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setOutputFormat( 'nowiki' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertEquals(
+ '<span class="plainlinks smw-eid">http&#58;//example.org/foo</span>',
+ $instance->getShortWikiText( 'linker' )
+ );
+ }
+
+ public function testGetShortHTMLText() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getExternalFormatterUri' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIUri( 'http', 'example.org/$1' ) ) );
+
+ $instance = new ExternalIdentifierValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortHTMLText()
+ );
+
+ $this->assertEquals(
+ '<a href="http://example.org/foo" target="_blank">foo</a>',
+ $instance->getShortHTMLText( 'linker' )
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testTryToGetShortHTMLTextWithLinkerOnMissingFormatterUri() {
+
+ $instance = new ExternalIdentifierValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortHTMLText()
+ );
+
+ $instance->getShortHTMLText( 'linker' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ImportValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ImportValueTest.php
new file mode 100644
index 00000000..892ac2d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ImportValueTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\ImportValue;
+use SMW\DataValues\ValueParsers\ImportValueParser;
+
+/**
+ * @covers \SMW\DataValues\ImportValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ImportValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new ImportValueParser( $mediaWikiNsContentReader ) ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ImportValue',
+ new ImportValue()
+ );
+
+ // FIXME Legacy naming remove in 3.x
+ $this->assertInstanceOf(
+ '\SMWImportValue',
+ new ImportValue()
+ );
+ }
+
+ public function testErrorForInvalidUserValue() {
+
+ $instance = new ImportValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'FooBar' );
+
+ $this->assertEquals(
+ 'FooBar',
+ $instance->getWikiValue()
+ );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/InfoLinksProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/InfoLinksProviderTest.php
new file mode 100644
index 00000000..93b297b4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/InfoLinksProviderTest.php
@@ -0,0 +1,251 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\InfoLinksProvider;
+use SMW\DataValues\StringValue;
+use SMW\Message;
+use SMW\Tests\TestEnvironment;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @covers \SMW\DataValues\InfoLinksProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class InfoLinksProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $dataValueServiceFactory;
+ private $propertySpecificationLookup;
+ private $dataValueFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ InfoLinksProvider::class,
+ new InfoLinksProvider( $dataValue, $this->propertySpecificationLookup )
+ );
+ }
+
+ public function testGetInfolinkTextOnNumberValue() {
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [] ) );
+
+ $numberValue = $this->dataValueFactory->newDataValueByType( NumberValue::TYPE_ID );
+
+ $numberValue->setOption( 'user.language', 'en' );
+ $numberValue->setOption( 'content.language', 'en' );
+
+ $numberValue->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $numberValue->setUserValue( '1000.42' );
+
+ $instance = new InfoLinksProvider( $numberValue, $this->propertySpecificationLookup );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'newInfoLinksProvider' )
+ ->will( $this->returnValue( $instance ) );
+
+ $this->assertContains(
+ '/:Foo/1000.42|+]]</span>',
+ $instance->getInfolinkText( SMW_OUTPUT_WIKI )
+ );
+
+ $this->assertContains(
+ '/:Foo/1000.42">+</a></span>',
+ $instance->getInfolinkText( SMW_OUTPUT_HTML )
+ );
+ }
+
+ public function testGetInfolinkTextOnStringValue() {
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [] ) );
+
+ $stringValue = $this->dataValueFactory->newDataValueByType( StringValue::TYPE_ID );
+
+ $stringValue->setOption( 'user.language', 'en' );
+ $stringValue->setOption( 'content.language', 'en' );
+
+ $stringValue->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $stringValue->setUserValue( 'Text with :: content' );
+
+ $instance = new InfoLinksProvider( $stringValue, $this->propertySpecificationLookup );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'newInfoLinksProvider' )
+ ->will( $this->returnValue( $instance ) );
+
+ $this->assertContains(
+ '/:Foo/Text-20with-20-2D3A-2D3A-20content|+]]</span>',
+ $instance->getInfolinkText( SMW_OUTPUT_WIKI )
+ );
+
+ $this->assertContains(
+ '/:Foo/Text-20with-20-2D3A-2D3A-20content">+</a></span>',
+ $instance->getInfolinkText( SMW_OUTPUT_HTML )
+ );
+ }
+
+ public function testGetInfolinkTextOnSobValue() {
+
+ $stringValidator = $this->testEnvironment->newValidatorFactory()->newStringValidator();
+
+ $sobValue = $this->dataValueFactory->newDataValueByType( '__sob' );
+ $sobValue->setUserValue( 'Text with :: content' );
+
+ $instance = new InfoLinksProvider( $sobValue, $this->propertySpecificationLookup );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'newInfoLinksProvider' )
+ ->will( $this->returnValue( $instance ) );
+
+ $stringValidator->assertThatStringContains(
+ '<span class="smwbrowse">[[.*/:Text-20with-20::-20content|+]]</span>',
+ $instance->getInfolinkText( SMW_OUTPUT_WIKI )
+ );
+
+ $stringValidator->assertThatStringContains(
+ '<span class="smwbrowse"><a .*/:Text-20with-20::-20content" title=".*/:Text-20with-20::-20content">+</a></span>',
+ $instance->getInfolinkText( SMW_OUTPUT_HTML )
+ );
+ }
+
+ public function testGetInfolinkTextOnTimeValueWithoutLocalizedOutput() {
+
+ $timeValue = $this->dataValueFactory->newDataValueByType( '_dat' );
+
+ $timeValue->setOption( $timeValue::OPT_USER_LANGUAGE, 'fr' );
+ $timeValue->setOption( $timeValue::OPT_CONTENT_LANGUAGE, 'en' );
+
+ // Forcibly set an output
+ $timeValue->setOutputFormat( 'LOCL' );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_dat' );
+
+ $timeValue->setProperty(
+ $property
+ );
+
+ $timeValue->setDataItem(
+ $this->dataItemFactory->newDITime( 1, 1970, 12, 12 )
+ );
+
+ $instance = new InfoLinksProvider( $timeValue, $this->propertySpecificationLookup );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'newInfoLinksProvider' )
+ ->will( $this->returnValue( $instance ) );
+
+ $this->assertContains(
+ '/:Foo/12-20December-201970|+]]</span>',
+ $instance->getInfolinkText( SMW_OUTPUT_WIKI )
+ );
+
+ $this->assertContains(
+ '/:Foo/12-20December-201970">+</a></span>',
+ $instance->getInfolinkText( SMW_OUTPUT_HTML )
+ );
+ }
+
+ public function testGetInfolinkTextOnStringValueWithServiceLinks() {
+
+ $service = 'testGetInfolinkTextOnStringValueWithServiceLinks';
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( $service ) ] ) );
+
+ // Manipulating the Message cache is a hack!!
+ $parameters = [
+ "smw_service_" . $service,
+ 'Bar'
+ ];
+
+ Message::getCache()->save(
+ Message::getHash( $parameters, Message::TEXT, Message::CONTENT_LANGUAGE ),
+ 'SERVICELINK-A|SERVICELINK-B'
+ );
+
+ $stringValue = $this->dataValueFactory->newDataValueByType( StringValue::TYPE_ID );
+
+ $stringValue->setOption( StringValue::OPT_USER_LANGUAGE, 'en' );
+ $stringValue->setOption( StringValue::OPT_CONTENT_LANGUAGE, 'en' );
+
+ $stringValue->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $stringValue->setUserValue( 'Bar' );
+
+ $instance = new InfoLinksProvider( $stringValue, $this->propertySpecificationLookup );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'newInfoLinksProvider' )
+ ->will( $this->returnValue( $instance ) );
+
+ $this->assertContains(
+ '<span class="smwttcontent">[SERVICELINK-B SERVICELINK-A]</span>',
+ $instance->getInfolinkText( SMW_OUTPUT_WIKI )
+ );
+
+ $this->assertContains(
+ '<span class="smwttcontent"><a href="SERVICELINK-B">SERVICELINK-A</a></span>',
+ $instance->getInfolinkText( SMW_OUTPUT_HTML )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/KeywordValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/KeywordValueTest.php
new file mode 100644
index 00000000..500463cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/KeywordValueTest.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\KeywordValue;
+use SMW\PropertySpecificationLookup;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\KeywordValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class KeywordValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( PropertySpecificationLookup::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $externalFormatterUriValue = $this->getMockBuilder( '\SMW\DataValues\ExternalFormatterUriValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getPropertySpecificationLookup' )
+ ->will( $this->returnValue( $this->propertySpecificationLookup ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getDataValueFactory' )
+ ->will( $this->returnValue( DataValueFactory::getInstance() ) );
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ KeywordValue::class,
+ new KeywordValue()
+ );
+ }
+
+ public function testErrorWhenLengthExceedsLimit() {
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new KeywordValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'LTVCLTVCSGFzLTIwa2V5d29yZC0yMHRlc3Q6OnRFc3QtMjAyLTVELTVEL2Zvcm1hdD1jYXRlZ29yeS8tM0ZIYXMtMjBkZXNjcmlwdGlvbgLTVCLTVCSGFzLTIwa2V5d29yZC0yMHRlc3Q6OnRFc3QtMjAyLTVELTVEL2Zvcm1hdD1jYXRlZ29yeS8tM0ZIYXMtMjBkZXNjcmlwdGlvbgLTVCLTVCSGFzLTIwaLTVCLTVCSGFzLTIwa..........' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ '',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertContains(
+ 'smw-datavalue-keyword-maximum-length',
+ implode( ', ', $instance->getErrors() )
+ );
+ }
+
+ public function testGetShortWikiText_WithoutLink() {
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new KeywordValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortWikiText( 'linker' )
+ );
+ }
+
+ public function testGetShortWikiText_WithLink() {
+
+ $data = json_encode(
+ [
+ 'type' => 'LINK_FORMAT_SCHEMA',
+ 'rule' => [ 'link_to' => 'SPECIAL_SEARCH_BY_PROPERTY' ]
+ ]
+ );
+
+ $this->propertySpecificationLookup->expects( $this->at( 0 ) )
+ ->method( 'getSpecification' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_FORMAT_SCHEMA' ) ) )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIWikiPage( 'Bar', SMW_NS_SCHEMA ) ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->at( 1 ) )
+ ->method( 'getSpecification' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_SCHEMA_DEF' ) ) )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBlob( $data ) ] ) );
+
+ $instance = new KeywordValue();
+ $instance->setDataValueServiceFactory( $this->dataValueServiceFactory );
+ $instance->setOption( KeywordValue::OPT_COMPACT_INFOLINKS, true );
+
+ $instance->setUserValue( 'foo' );
+ $instance->setProperty( $this->dataItemFactory->newDIProperty( 'Bar' ) );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertContains(
+ '[[:Special:SearchByProperty/cl:OkJhci9mb28|foo]]',
+ $instance->getShortWikiText( 'linker' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/LanguageCodeValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/LanguageCodeValueTest.php
new file mode 100644
index 00000000..61a16179
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/LanguageCodeValueTest.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\LanguageCodeValue;
+
+/**
+ * @covers \SMW\DataValues\LanguageCodeValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class LanguageCodeValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\LanguageCodeValue',
+ new LanguageCodeValue()
+ );
+ }
+
+ public function testHasErrorForMissingLanguageCode() {
+
+ $instance = new LanguageCodeValue();
+ $instance->setUserValue( '' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testHasErrorForInvalidLanguageCode() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( 'Skipping because `Language::isKnownLanguageTag` is not supported on 1.19' );
+ }
+
+ $instance = new LanguageCodeValue();
+ $instance->setUserValue( '-Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testNormalizationOnLanguageCodeOccurs() {
+
+ $mixedCase = new LanguageCodeValue();
+ $mixedCase->setUserValue( 'eN' );
+
+ $upperCase = new LanguageCodeValue();
+ $upperCase->setUserValue( 'EN' );
+
+ $this->assertEquals(
+ $mixedCase->getDataItem(),
+ $upperCase->getDataItem()
+ );
+
+ $this->assertEquals(
+ 'en',
+ $mixedCase->getDataItem()->getString()
+ );
+
+ $this->assertEquals(
+ 'en',
+ $upperCase->getDataItem()->getString()
+ );
+ }
+
+ public function testInvalidLanguageCode() {
+
+ $instance = new LanguageCodeValue();
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertContains(
+ '[2,"smw-datavalue-languagecode-invalid","foo"]',
+ $instance->getDataItem()->getString()
+ );
+ }
+
+ public function testInvalidLanguageCodeIsAllowedInQueryContext() {
+
+ $instance = new LanguageCodeValue();
+ $instance->setOption( LanguageCodeValue::OPT_QUERY_CONTEXT, true );
+
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getDataItem()->getString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/MonolingualTextValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/MonolingualTextValueTest.php
new file mode 100644
index 00000000..d402f33a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/MonolingualTextValueTest.php
@@ -0,0 +1,259 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter;
+use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
+
+/**
+ * @covers \SMW\DataValues\MonolingualTextValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new MonolingualTextValueParser() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\MonolingualTextValue',
+ new MonolingualTextValue()
+ );
+ }
+
+ public function testErrorForMissingLanguageCode() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_MLTV_LCODE );
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testNoErrorForMissingLanguageCodeWhenFeatureIsDisabled() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setOption( 'smwgDVFeatures', false );
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testErrorForInvalidLanguageCode() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( 'Skipping because `Language::isKnownLanguageTag` is not supported on 1.19' );
+ }
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@foobar' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testValidParsableUserValue() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@en' );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $instance->getDataItem()
+ );
+
+ foreach ( $instance->getDataItems() as $dataItem ) {
+ $this->assertInstanceOf(
+ '\SMWDIBlob',
+ $dataItem
+ );
+ }
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getTextValueByLanguage( 'en' )->getDataItem()->getString()
+ );
+ }
+
+ public function testTryToGetTextValueByLanguageForUnrecognizedLanguagCode() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@en' );
+
+ $this->assertNull(
+ $instance->getTextValueByLanguage( 'bar' )
+ );
+ }
+
+ public function testGetWikiValueForValidMonolingualTextValue() {
+
+ $instance = new MonolingualTextValue();
+
+ $monolingualTextValueFormatter = new MonolingualTextValueFormatter();
+ $monolingualTextValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $monolingualTextValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@en' );
+
+ $this->assertEquals(
+ 'Foo@en',
+ $instance->getWikiValue()
+ );
+ }
+
+ public function testGetWikiValueForInvalidMonolingualTextValue() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( 'Skipping because `Language::isKnownLanguageTag` is not supported on 1.19' );
+ }
+
+ $instance = new MonolingualTextValue();
+
+ $monolingualTextValueFormatter = new MonolingualTextValueFormatter();
+ $monolingualTextValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $monolingualTextValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@foobar' );
+
+ $this->assertContains(
+ 'class="smw-highlighter" data-type="4"',
+ $instance->getWikiValue()
+ );
+ }
+
+ public function testGetProperties() {
+
+ $instance = new MonolingualTextValue();
+ $properties = $instance->getPropertyDataItems();
+
+ $this->assertEquals(
+ '_TEXT',
+ $properties[0]->getKey()
+ );
+
+ $this->assertEquals(
+ '_LCODE',
+ $properties[1]->getKey()
+ );
+ }
+
+ public function testToArray() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@en' );
+
+ $this->assertEquals(
+ [
+ '_TEXT' => 'Foo',
+ '_LCODE' => 'en'
+ ],
+ $instance->toArray()
+ );
+ }
+
+ public function testToString() {
+
+ $instance = new MonolingualTextValue();
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'Foo@en' );
+
+ $this->assertSame(
+ 'Foo@en',
+ $instance->toString()
+ );
+ }
+
+ public function testGetTextWithLanguageTag() {
+
+ $instance = new MonolingualTextValue();
+
+ $this->assertSame(
+ 'foo@zh-Hans',
+ $instance->getTextWithLanguageTag( 'foo', 'zh-hans' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/IntlNumberFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/IntlNumberFormatterTest.php
new file mode 100644
index 00000000..326ed465
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/IntlNumberFormatterTest.php
@@ -0,0 +1,348 @@
+<?php
+
+namespace SMW\Tests\DataValues\Number;
+
+use Language;
+use SMW\DataValues\Number\IntlNumberFormatter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\Number\IntlNumberFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class IntlNumberFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IntlNumberFormatter::class,
+ new IntlNumberFormatter( 10000 )
+ );
+
+ $this->assertInstanceOf(
+ IntlNumberFormatter::class,
+ IntlNumberFormatter::getInstance()
+ );
+ }
+
+ /**
+ * @dataProvider numberProvider
+ */
+ public function testLocalizedFormattedNumber( $maxNonExpNumber, $number, $userLanguage, $contentLanguage, $expected ) {
+
+ $instance = new IntlNumberFormatter( $maxNonExpNumber );
+
+ $instance->setOption( IntlNumberFormatter::USER_LANGUAGE, $userLanguage );
+ $instance->setOption( IntlNumberFormatter::CONTENT_LANGUAGE, $contentLanguage );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $number )
+ );
+ }
+
+ public function testZeroPadding() {
+
+ $expected = '1000(foo)42';
+
+ $instance = new IntlNumberFormatter( 10000 );
+
+ // #2797
+ $instance->setOption( IntlNumberFormatter::DECIMAL_SEPARATOR, '(foo)' );
+ $instance->setOption( IntlNumberFormatter::THOUSANDS_SEPARATOR, '' );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( 1000.42 )
+ );
+ }
+
+ /**
+ * @dataProvider unformattedNumberByPrecisionProvider
+ */
+ public function testGetUnformattedNumberByPrecision( $maxNonExpNumber, $number, $precision, $userLanguage, $contentLanguage, $expected ) {
+
+ $instance = new IntlNumberFormatter( $maxNonExpNumber );
+
+ $instance->setOption( IntlNumberFormatter::USER_LANGUAGE, $userLanguage );
+ $instance->setOption( IntlNumberFormatter::CONTENT_LANGUAGE, $contentLanguage );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $number, $precision, IntlNumberFormatter::VALUE_FORMAT )
+ );
+ }
+
+ public function testCompareFloatValue() {
+
+ $instance = new IntlNumberFormatter( 1000 );
+
+ $instance->setOption( IntlNumberFormatter::USER_LANGUAGE, 'en' );
+ $instance->setOption( IntlNumberFormatter::CONTENT_LANGUAGE, 'en' );
+
+ $this->assertSame(
+ $instance->format( 100.0, false, IntlNumberFormatter::VALUE_FORMAT ),
+ $instance->format( 100, false, IntlNumberFormatter::VALUE_FORMAT )
+ );
+ }
+
+ /**
+ * @dataProvider separatorProvider
+ */
+ public function testgetSeparatorByLanguage( $type, $locale, $userLanguage, $contentLanguage, $expected ) {
+
+ $instance = new IntlNumberFormatter( 10000000 );
+
+ $instance->setOption( IntlNumberFormatter::USER_LANGUAGE, $userLanguage );
+ $instance->setOption( IntlNumberFormatter::CONTENT_LANGUAGE, $contentLanguage );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getSeparatorByLanguage( $type, $locale )
+ );
+ }
+
+ public function testCustomSeparator() {
+
+ $instance = new IntlNumberFormatter( 10000000 );
+
+ $instance->setOption( IntlNumberFormatter::DECIMAL_SEPARATOR, 'FOO' );
+ $instance->setOption( IntlNumberFormatter::THOUSANDS_SEPARATOR, 'BAR' );
+
+ $this->assertEquals(
+ 'FOO',
+ $instance->getSeparatorByLanguage( IntlNumberFormatter::DECIMAL_SEPARATOR, 'zzz' )
+ );
+
+ $this->assertEquals(
+ 'BAR',
+ $instance->getSeparatorByLanguage( IntlNumberFormatter::THOUSANDS_SEPARATOR, 'zzz' )
+ );
+ }
+
+ public function testTryTogetSeparatorByLanguageOnInvalidTypeThrowsException() {
+
+ $instance = new IntlNumberFormatter( 10000000 );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->getSeparatorByLanguage( 'Foo' );
+ }
+
+ public function numberProvider() {
+
+ $provider[] = [
+ 10000,
+ 1000,
+ 'en',
+ 'en',
+ '1,000'
+ ];
+
+ $provider[] = [
+ 10000,
+ 1000.42,
+ 'en',
+ 'en',
+ '1,000.42'
+ ];
+
+ $provider[] = [
+ 10000,
+ 1000000,
+ 'en',
+ 'en',
+ '1.0e+6'
+ ];
+
+ $provider[] = [
+ 10000000,
+ 1000000,
+ 'en',
+ 'en',
+ '1,000,000'
+ ];
+
+ return $provider;
+ }
+
+ public function unformattedNumberByPrecisionProvider() {
+
+ $provider['un.1'] = [
+ 10000,
+ 1000,
+ 2,
+ 'en',
+ 'en',
+ '1000.00'
+ ];
+
+ $provider['un.2'] = [
+ 10000,
+ 1000.42,
+ 3,
+ 'en',
+ 'en',
+ '1000.420'
+ ];
+
+ $provider['un.3'] = [
+ 10000,
+ 1000000,
+ 0,
+ 'en',
+ 'en',
+ '1000000'
+ ];
+
+ $provider['un.4'] = [
+ 10000000,
+ 1000000,
+ 2,
+ 'en',
+ 'en',
+ '1000000.00'
+ ];
+
+ $provider['un.5'] = [
+ 10000000,
+ 1000000,
+ false,
+ 'en',
+ 'en',
+ '1000000'
+ ];
+
+ $provider['un.6'] = [
+ 10000000,
+ 312.23545555,
+ false,
+ 'en',
+ 'en',
+ '312.23545555'
+ ];
+
+ $provider['un.7'] = [
+ 10000000,
+ 312.23545555,
+ 6,
+ 'en',
+ 'en',
+ '312.235456'
+ ];
+
+ $provider['un.8'] = [
+ 10000000,
+ 312.23545555,
+ 9,
+ 'en',
+ 'en',
+ '312.235455550'
+ ];
+
+ $provider['un.9'] = [
+ 10000000,
+ 312.23545555,
+ null,
+ 'en',
+ 'en',
+ '312.235455550'
+ ];
+
+ $provider['un.10'] = [
+ 10000000,
+ 1.334e-13,
+ false,
+ 'en',
+ 'en',
+ '1.334e-13'
+ ];
+
+ $provider['un.11'] = [
+ 10000000,
+ 1.334e-13,
+ false,
+ 'en',
+ 'fr',
+ '1,334e-13'
+ ];
+
+ return $provider;
+ }
+
+ public function separatorProvider() {
+
+ $provider['1.en'] = [
+ IntlNumberFormatter::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::USER_LANGUAGE,
+ 'en',
+ 'en',
+ '.'
+ ];
+
+ $provider['2.en'] = [
+ IntlNumberFormatter::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::USER_LANGUAGE,
+ 'en',
+ 'en',
+ ','
+ ];
+
+ $provider['3.en'] = [
+ IntlNumberFormatter::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ 'en',
+ 'en',
+ '.'
+ ];
+
+ $provider['4.en'] = [
+ IntlNumberFormatter::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ 'en',
+ 'en',
+ ','
+ ];
+
+ $provider['5.fr'] = [
+ IntlNumberFormatter::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::USER_LANGUAGE,
+ 'fr',
+ 'en',
+ ','
+ ];
+
+ $provider['6.fr'] = [
+ IntlNumberFormatter::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::USER_LANGUAGE,
+ 'fr',
+ 'en',
+ ' '
+ ];
+
+ $provider['7.fr'] = [
+ IntlNumberFormatter::DECIMAL_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ 'fr',
+ 'fr',
+ ','
+ ];
+
+ $provider['8.fr'] = [
+ IntlNumberFormatter::THOUSANDS_SEPARATOR,
+ IntlNumberFormatter::CONTENT_LANGUAGE,
+ 'fr',
+ 'fr',
+ ' '
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/UnitConverterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/UnitConverterTest.php
new file mode 100644
index 00000000..699be6cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Number/UnitConverterTest.php
@@ -0,0 +1,271 @@
+<?php
+
+namespace SMW\Tests\DataValues\Number;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\Number\UnitConverter;
+use SMW\Tests\TestEnvironment;
+use SMWNumberValue as NumberValue;
+
+/**
+ * @covers \SMW\DataValues\Number\UnitConverter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UnitConverterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ UnitConverter::class,
+ new UnitConverter( $numberValue )
+ );
+ }
+
+ public function testErrorOnMissingConversionData() {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new UnitConverter(
+ $numberValue
+ );
+
+ $instance->fetchConversionData(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider conversionDataProvider
+ */
+ public function testFetchConversionData( $thousands, $decimal, $correspondsTo, $unitIds, $unitFactors, $mainUnit, $prefixalUnitPreference ) {
+
+ $cachedPropertyValuesPrefetcher = $this->getMockBuilder( '\SMW\CachedPropertyValuesPrefetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( $correspondsTo )
+ ] ) );
+
+ $numberValue = new NumberValue();
+
+ $numberValue->setOption(
+ NumberValue::THOUSANDS_SEPARATOR,
+ $thousands
+ );
+
+ $numberValue->setOption(
+ NumberValue::DECIMAL_SEPARATOR,
+ $decimal
+ );
+
+ $instance = new UnitConverter(
+ $numberValue,
+ $cachedPropertyValuesPrefetcher
+ );
+
+ $instance->fetchConversionData(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ $this->assertEquals(
+ $unitIds,
+ $instance->getUnitIds()
+ );
+
+ $this->assertEquals(
+ $unitFactors,
+ $instance->getUnitFactors()
+ );
+
+ $this->assertEquals(
+ $mainUnit,
+ $instance->getMainUnit()
+ );
+
+ $this->assertEquals(
+ $prefixalUnitPreference,
+ $instance->getPrefixalUnitPreference()
+ );
+ }
+
+ public function testInitConversionData() {
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->any() )
+ ->method( 'has' )
+ ->will( $this->onConsecutiveCalls( false, true ) );
+
+ $blobStore = $this->getMockBuilder( '\Onoi\BlobStore\BlobStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $blobStore->expects( $this->exactly( 2 ) )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $blobStore->expects( $this->once() )
+ ->method( 'save' );
+
+ $cachedPropertyValuesPrefetcher = $this->getMockBuilder( '\SMW\CachedPropertyValuesPrefetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cachedPropertyValuesPrefetcher->expects( $this->atLeastOnce() )
+ ->method( 'getBlobStore' )
+ ->will( $this->returnValue( $blobStore ) );
+
+ $cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( 'Foo' )
+ ] ) );
+
+ $numberValue = new NumberValue();
+
+ $instance = new UnitConverter(
+ $numberValue,
+ $cachedPropertyValuesPrefetcher
+ );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance->initConversionData( $property );
+
+ // Cached
+ $instance->initConversionData( $property);
+ }
+
+ public function conversionDataProvider() {
+
+ $provider[] = [
+ ',',
+ '.',
+ 'Â¥,JPY,Japanese Yen 1.5',
+ [
+ 'Â¥' => 'Â¥',
+ 'JPY' => 'Â¥',
+ 'JapaneseYen' => 'Â¥',
+ '' => ''
+ ],
+ [
+ '' => 1,
+ 'Â¥' => 1.5
+ ],
+ '',
+ [
+ 'Â¥' => true,
+ 'JPY' => true,
+ 'JapaneseYen' => true
+ ]
+ ];
+
+ $provider[] = [
+ ',',
+ '.',
+ '0.5 British Pound, GBP, pounds sterling, £',
+ [
+ 'BritishPound' => 'BritishPound',
+ 'GBP' => 'BritishPound',
+ 'poundssterling' => 'BritishPound',
+ '£' => 'BritishPound',
+ '' => ''
+ ],
+ [
+ '' => 1,
+ 'BritishPound' => 0.5
+ ],
+ '',
+ [
+ 'BritishPound' => false,
+ 'GBP' => false,
+ 'poundssterling' => false,
+ '£' => false
+ ]
+ ];
+
+ $provider[] = [
+ '.',
+ ',',
+ '0,5 thousand rub., thousand ₽',
+ [
+ 'thousandrub.' => 'thousandrub.',
+ 'thousand₽' => 'thousandrub.',
+ '' => ''
+ ],
+ [
+ 'thousandrub.' => 0.5,
+ '' => 1
+ ],
+ '',
+ [
+ 'thousandrub.' => false,
+ 'thousand₽' => false
+ ]
+ ];
+
+ $provider[] = [
+ '.',
+ ',',
+ '€ 1',
+ [
+ '€' => '€',
+ '' => ''
+ ],
+ [
+ '€' => 1,
+ '' => 1
+ ],
+ '€',
+ [
+ '€' => true,
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyChainValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyChainValueTest.php
new file mode 100644
index 00000000..2f1691f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyChainValueTest.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\PropertyChainValue;
+use SMW\DIProperty;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\PropertyChainValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyChainValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\PropertyChainValue',
+ new PropertyChainValue()
+ );
+ }
+
+ public function testIsChained() {
+
+ $this->assertFalse(
+ PropertyChainValue::isChained( 'Foo' )
+ );
+
+ $this->assertTrue(
+ PropertyChainValue::isChained( 'Foo.Bar' )
+ );
+ }
+
+ public function testErrorOnUnchainedValue() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testGetLastPropertyChainValue() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ new DIProperty( 'Bar' ),
+ $instance->getLastPropertyChainValue()->getDataItem()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIBlob',
+ $instance->getDataItem()
+ );
+ }
+
+ public function testGetPropertyChainValues() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertCount(
+ 1,
+ $instance->getPropertyChainValues()
+ );
+ }
+
+ public function testGetWikiValue() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->getWikiValue()
+ );
+ }
+
+ public function testGetShortWikiText() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ 'Bar&nbsp;<span title="Foo.Bar">â ‰</span>',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertEquals(
+ $this->testEnvironment->replaceNamespaceWithLocalizedText( SMW_NS_PROPERTY, '[[:Property:Bar|Bar]]&nbsp;<span title="Foo.Bar">â ‰</span>' ),
+ $instance->getShortWikiText( 'linker' )
+ );
+ }
+
+ public function testGetLongWikiText() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ $this->testEnvironment->replaceNamespaceWithLocalizedText( SMW_NS_PROPERTY, 'Property:Bar&nbsp;<span title="Foo.Bar">â ‰</span>' ),
+ $instance->getLongWikiText()
+ );
+
+ $this->assertEquals(
+ $this->testEnvironment->replaceNamespaceWithLocalizedText( SMW_NS_PROPERTY, '[[:Property:Bar|Bar]]&nbsp;<span title="Foo.Bar">â ‰</span>' ),
+ $instance->getLongWikiText( 'linker' )
+ );
+ }
+
+ public function testGetShortHTMLText() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ 'Bar&nbsp;<span title="Foo.Bar">â ‰</span>',
+ $instance->getShortHTMLText()
+ );
+ }
+
+ public function testGetLongHTMLText() {
+
+ $instance = new PropertyChainValue();
+
+ $instance->setUserValue( 'Foo.Bar' );
+
+ $this->assertEquals(
+ $this->testEnvironment->replaceNamespaceWithLocalizedText( SMW_NS_PROPERTY, 'Property:Bar&nbsp;<span title="Foo.Bar">â ‰</span>' ),
+ $instance->getLongHTMLText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyValueTest.php
new file mode 100644
index 00000000..d2cf6532
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/PropertyValueTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\PropertyValue;
+
+/**
+ * @covers \SMW\DataValues\PropertyValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyValue::class,
+ new PropertyValue()
+ );
+ }
+
+ /**
+ * @dataProvider featuresProvider
+ */
+ public function testOptions( $options, $expected ) {
+
+ $instance = new PropertyValue();
+ $instance->setOption( 'smwgDVFeatures', $options );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getOption( 'smwgDVFeatures' )
+ );
+ }
+
+ public function featuresProvider() {
+
+ $provider[] = [
+ SMW_DV_PROV_REDI,
+ true
+ ];
+
+ $provider[] = [
+ SMW_DV_NONE | SMW_DV_PROV_REDI,
+ true
+ ];
+
+ $provider[] = [
+ SMW_DV_NONE,
+ false
+ ];
+
+ $provider[] = [
+ false,
+ false
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ReferenceValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ReferenceValueTest.php
new file mode 100644
index 00000000..06de9cd3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ReferenceValueTest.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ReferenceValue;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\ReferenceValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReferenceValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ReferenceValue',
+ new ReferenceValue()
+ );
+ }
+
+ public function testGetPropertyDataItems() {
+
+ $expected = [
+ $this->dataItemFactory->newDIProperty( 'Bar' ),
+ $this->dataItemFactory->newDIProperty( 'Foobar' )
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new ReferenceValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getPropertyDataItems()
+ );
+
+ $this->assertEquals(
+ $this->dataItemFactory->newDIProperty( 'Foobar' ),
+ $instance->getPropertyDataItemByIndex( 'Foobar' )
+ );
+ }
+
+ public function testParseValue() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new ReferenceValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '123;abc' );
+ $container = $instance->getDataItem();
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $container
+ );
+
+ $semanticData = $container->getSemanticData();
+
+ $this->assertTrue(
+ $semanticData->hasProperty( $this->dataItemFactory->newDIProperty( 'Foobar' ) )
+ );
+ }
+
+ public function testParseValueOnMissingValues() {
+
+ $instance = new ReferenceValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '' );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $instance->getDataItem()
+ );
+ }
+
+ public function testParseValueWithErroredDv() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new ReferenceValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( 'Foo;<>Foo' );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $instance->getDataItem()
+ );
+
+ $this->assertContains(
+ "smw-datavalue-wikipage-property-invalid-title",
+ implode( ' ', $instance->getErrors() )
+ );
+ }
+
+ public function testGetValuesFromStringWithEncodedSemicolon() {
+
+ $instance = new ReferenceValue();
+
+ $this->assertEquals(
+ [ 'abc', '1;2', 3 ],
+ $instance->getValuesFromString( 'abc;1\;2;3' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/StringValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/StringValueTest.php
new file mode 100644
index 00000000..b16a29ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/StringValueTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\StringValue;
+use SMW\DataValues\ValueFormatters\StringValueFormatter;
+
+/**
+ * @covers \SMW\DataValues\StringValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class StringValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ StringValue::class,
+ new StringValue( '_txt' )
+ );
+ }
+
+ public function testGetLength() {
+
+ $instance = new StringValue( '_txt' );
+
+ $stringValueFormatter = new StringValueFormatter();
+ $stringValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $stringValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( 'abcdefã‚ã„ã†ã‚¨ã‚ª' );
+
+ $this->assertEquals(
+ 11,
+ $instance->getLength()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TelephoneUriValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TelephoneUriValueTest.php
new file mode 100644
index 00000000..0a3e75a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TelephoneUriValueTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\TelephoneUriValue;
+
+/**
+ * @covers \SMW\DataValues\TelephoneUriValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TelephoneUriValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\TelephoneUriValue',
+ new TelephoneUriValue()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWURIValue',
+ new TelephoneUriValue()
+ );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TemperatureValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TemperatureValueTest.php
new file mode 100644
index 00000000..8ceb498a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TemperatureValueTest.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\TemperatureValue;
+use SMW\DataValues\ValueFormatters\NumberValueFormatter;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\TemperatureValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TemperatureValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getPropertySpecificationLookup' )
+ ->will( $this->returnValue( $this->propertySpecificationLookup ) );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\TemperatureValue',
+ new TemperatureValue()
+ );
+ }
+
+ public function testSetUserValueToReturnKelvinForAnyNonPreferredDisplayUnit() {
+
+ $instance = new TemperatureValue();
+
+ $numberValueFormatter = new NumberValueFormatter();
+ $numberValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $numberValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( '100 °C' );
+
+ $this->assertContains(
+ '373.15 K',
+ $instance->getWikiValue()
+ );
+
+ $instance->setUserValue( '100 Fahrenheit' );
+
+ $this->assertContains(
+ '310.92777777778 K',
+ $instance->getWikiValue()
+ );
+
+ $this->assertContains(
+ '100 Fahrenheit',
+ $instance->getShortWikiText()
+ );
+ }
+
+ public function testSetUserValueOnUnknownUnit() {
+
+ $instance = new TemperatureValue();
+
+ $numberValueFormatter = new NumberValueFormatter();
+ $numberValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $numberValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setUserValue( '100 Unknown' );
+
+ $this->assertContains(
+ 'error',
+ $instance->getWikiValue()
+ );
+ }
+
+ public function testSetUserValueToReturnOnPreferredDisplayUnit() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getDisplayUnits' )
+ ->will( $this->returnValue( [ 'Celsius' ] ) );
+
+ $instance = new TemperatureValue();
+
+ $numberValueFormatter = new NumberValueFormatter();
+ $numberValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $numberValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '100 °C' );
+
+ $this->assertContains(
+ '373.15 K',
+ $instance->getWikiValue()
+ );
+
+ $this->assertContains(
+ '100 °C',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertContains(
+ '100&#160;°C (373&#160;K, 212&#160;°F, 672&#160;°R)',
+ $instance->getLongWikiText()
+ );
+ }
+
+ public function testSetUserValueToReturnOnPreferredDisplayPrecision() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getDisplayPrecision' )
+ ->will( $this->returnValue( 0 ) );
+
+ $instance = new TemperatureValue();
+
+ $numberValueFormatter = new NumberValueFormatter();
+ $numberValueFormatter->setDataValue( $instance );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $numberValueFormatter ) );
+
+ $instance->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '100 °C' );
+
+ $this->assertContains(
+ '373 K',
+ $instance->getWikiValue()
+ );
+
+ $this->assertContains(
+ '100 °C',
+ $instance->getShortWikiText()
+ );
+
+ $this->assertContains(
+ '373&#160;K (100&#160;°C, 212&#160;°F, 672&#160;°R)',
+ $instance->getLongWikiText()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/ComponentsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/ComponentsTest.php
new file mode 100644
index 00000000..e1c4a953
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/ComponentsTest.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace SMW\Tests\DataValues\Time;
+
+use SMW\DataValues\Time\Components;
+
+/**
+ * @covers \SMW\DataValues\Time\Components
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ComponentsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testPublicProperties() {
+
+ $this->assertInternalType(
+ 'array',
+ Components::$months
+ );
+
+ $this->assertInternalType(
+ 'array',
+ Components::$monthsShort
+ );
+ }
+
+ public function testGet() {
+
+ $instance = new Components( [ 'foo' => 'bar' ] );
+
+ $this->assertEquals(
+ false,
+ $instance->get( 'bar' )
+ );
+
+ $this->assertEquals(
+ 'bar',
+ $instance->get( 'foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/IntlTimeFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/IntlTimeFormatterTest.php
new file mode 100644
index 00000000..d21d11ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/IntlTimeFormatterTest.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\Tests\DataValues\Time;
+
+use SMW\DataValues\Time\IntlTimeFormatter;
+use SMW\Localizer;
+use SMWDITime as DITime;
+
+/**
+ * @covers \SMW\DataValues\Time\IntlTimeFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class IntlTimeFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDITime' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ IntlTimeFormatter::class,
+ new IntlTimeFormatter( $dataItem )
+ );
+ }
+
+ /**
+ * @dataProvider formatProvider
+ */
+ public function testFormat( $serialization, $languageCode, $formatOption, $expected ) {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new IntlTimeFormatter(
+ DITime::doUnserialize( $serialization ),
+ Localizer::getInstance()->getLanguage( $languageCode )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $formatOption )
+ );
+ }
+
+ /**
+ * @dataProvider localizedFormatProvider
+ */
+ public function testGetLocalizedFormat( $serialization, $languageCode, $flag, $expected ) {
+
+ $instance = new IntlTimeFormatter(
+ DITime::doUnserialize( $serialization ),
+ Localizer::getInstance()->getLanguage( $languageCode )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getLocalizedFormat( $flag )
+ );
+ }
+
+ public function testContainsValidDateFormatRule() {
+
+ $formatOption = 'F Y/m/d H:i:s';
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new IntlTimeFormatter(
+ DITime::doUnserialize( '1/2000/12/12/1/1/20.200' ),
+ $language
+ );
+
+ $this->assertTrue(
+ $instance->containsValidDateFormatRule( $formatOption )
+ );
+ }
+
+ public function testFormatWithLocalizedMonthReplacement() {
+
+ // F - A full textual representation of a month, such as January or March
+ $formatOption = 'F Y/m/d H:i:s';
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'getMonthName' )
+ ->with( $this->equalTo( '12' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new IntlTimeFormatter(
+ DITime::doUnserialize( '1/2000/12/12/1/1/20.200' ),
+ $language
+ );
+
+ $this->assertEquals(
+ 'Foo 2000/12/12 01:01:20',
+ $instance->format( $formatOption )
+ );
+ }
+
+ public function formatProvider() {
+
+ #0
+ $provider[] = [
+ '1/2000/12/12/1/1/20/200',
+ 'en',
+ 'Y/m/d H:i:s',
+ '2000/12/12 01:01:20'
+ ];
+
+ #1
+ $provider[] = [
+ '2/2000/12/12/1/1/20/200',
+ 'en',
+ 'Y/m/d H:i:s',
+ '2000/12/12 01:01:20'
+ ];
+
+ #2
+ $provider[] = [
+ '1/2000/12/12/1/1/20.200',
+ 'en',
+ 'Y/m/d H:i:s.u',
+ '2000/12/12 01:01:20.200000'
+ ];
+
+ // Skip on HHVM to avoid .888500 vs. .888499 msec @see hhvm#6899
+ // https://bugs.php.net/bug.php?id=76822
+ if ( !defined( 'HHVM_VERSION' ) && version_compare( PHP_VERSION, '7.2', '<' ) ) {
+ #3
+ $provider[] = [
+ '2/1300/11/02/12/03/25.888499949',
+ 'en',
+ 'Y-m-d H:i:s.u',
+ '1300-11-02 12:03:25.888500'
+ ];
+
+ #4 time alone doesn't require a calendar model
+ $provider[] = [
+ '2/1300/11/02/12/03/25.888499949',
+ 'en',
+ 'H:i:s.u',
+ '12:03:25.888500'
+ ];
+ }
+
+ #5
+ $provider['on monthnumber 12'] = [
+ '1/2000/12/12',
+ 'en',
+ 'Y-m-d M',
+ '2000-12-12 Dec'
+ ];
+
+ #6
+ $provider['on daynumber 7'] = [
+ '1/2016/05/08/1/1/20/200',
+ 'en',
+ 'Y-m-d D',
+ '2016-05-08 Sun'
+ ];
+
+ #7
+ $provider['on timezone 1'] = [
+ '1/1970/1/12/11/43/0/14',
+ 'en',
+ 'Y-m-d H:i:s T',
+ '1970-01-12 11:43:00 UTC'
+ ];
+
+ return $provider;
+ }
+
+ public function localizedFormatProvider() {
+
+ #0
+ $provider[] = [
+ '1/2000/12/12/1/1/20/200',
+ 'en',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '01:01:20, 12 December 2000'
+ ];
+
+ #1
+ $provider[] = [
+ '1/2000/12/12/1/1/20/200',
+ 'ja',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '2000å¹´12月12æ—¥ (ç«) 01:01:20'
+ ];
+
+ #2
+ $provider[] = [
+ '1/2000/12/12/1/1/20/200',
+ 'es',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '01:01:20 12 dic 2000'
+ ];
+
+ #3
+ $provider['on daynumber 1'] = [
+ '1/2016/05/02/1/1/20/200',
+ 'ja',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '2016年5月2日 (月) 01:01:20'
+ ];
+
+ #4
+ $provider['on daynumber 7'] = [
+ '1/2016/05/08/1/1/20/200',
+ 'ja',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '2016年5月8日 (日) 01:01:20'
+ ];
+
+ #5
+ $provider['midnight-ja'] = [
+ '1/2016/05/08/00/00/00/00',
+ 'ja',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '2016年5月8日 (日) 00:00:00'
+ ];
+
+ #6
+ $provider['midnight-en'] = [
+ '1/2016/05/08/0/0/0/0',
+ 'en',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '00:00:00, 8 May 2016'
+ ];
+
+ #7
+ $provider['after-midnight'] = [
+ '1/2016/05/08/0/0/01/0',
+ 'en',
+ IntlTimeFormatter::LOCL_DEFAULT,
+ '00:00:01, 8 May 2016'
+ ];
+
+ #8
+ $provider['timezone-short'] = [
+ '1/1970/1/12/11/43/0/14',
+ 'en',
+ IntlTimeFormatter::LOCL_TIMEZONE,
+ '12:43:00 BST, 12 January 1970'
+ ];
+
+ #9
+ // -'07:43:00 America/Cuiaba, 12 January 1970'
+ // +'08:43:00 America/Cuiaba, 12 January 1970'
+ // Because of Daylight Saving Time UTC-3/Standard Time UTC-4
+ // $provider['timezone-long'] = array(
+ // '1/1970/1/12/11/43/0/America/Cuiaba',
+ // 'en',
+ // IntlTimeFormatter::LOCL_TIMEZONE,
+ // '07:43:00 America/Cuiaba, 12 January 1970'
+ // );
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/JulianDayTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/JulianDayTest.php
new file mode 100644
index 00000000..1a722329
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/JulianDayTest.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\Tests\DataValues\Time;
+
+use SMW\DataValues\Time\JulianDay;
+
+/**
+ * @covers \SMW\DataValues\Time\JulianDay
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class JulianDayTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testConvert( $calendarModel, $seralization, $jdValue ) {
+
+ list( $year, $month, $day, $hour, $minute, $second ) = explode( '/', $seralization );
+
+ $this->assertEquals(
+ $jdValue,
+ JulianDay::getJD( $calendarModel, $year, $month, $day, $hour, $minute, $second )
+ );
+ }
+
+ public function testGetJD_Issue2454() {
+
+ $offset = -4 / 24;
+
+ $this->assertSame(
+ 2457869.3333333,
+ JulianDay::getJD( 1, 2017, 4, 25, 20, 0, 0 )
+ );
+
+ $this->assertNotSame(
+ 2457869.5,
+ JulianDay::getJD( 1, 2017, 4, 25, 20, 0, 0 ) - $offset // returns 2457869.4999999665
+ );
+
+ $this->assertSame(
+ 2457869.5,
+ JulianDay::format( JulianDay::getJD( 1, 2017, 4, 25, 20, 0, 0 ) - $offset )
+ );
+ }
+
+ public function valueProvider() {
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '1352/01/01/0/0/0',
+ '2214875.500000'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_GREGORIAN,
+ '2100/10/04/0/0/0',
+ '2488345.500000'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_GREGORIAN,
+ '2100/10/04/0/0/0',
+ '2488345.500000'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '1582/10/04/0/0/0',
+ '2299159.500000'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_GREGORIAN,
+ '1582/10/15/0/0/0',
+ '2299160.5'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '-900/10/4/0/0/0',
+ '1392974.500000'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '-4713/01/02/0/0/0',
+ '0.5'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '-4713/01/02/12/0/0/0',
+ '1'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_JULIAN,
+ '-9000/10/4/0/0/0',
+ '-1565550.5'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_GREGORIAN,
+ '2100/10/4/13/55/55',
+ '2488346.0804977'
+ ];
+
+ $provider[] = [
+ JulianDay::CM_GREGORIAN,
+ '2100/10/4/13/55/55',
+ 2488346.0804977
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/TimezoneTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/TimezoneTest.php
new file mode 100644
index 00000000..68396821
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/Time/TimezoneTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests\DataValues\Time;
+
+use DateTime;
+use SMW\DataValues\Time\Timezone;
+
+/**
+ * @covers \SMW\DataValues\Time\Timezone
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TimezoneTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Timezone::class,
+ new Timezone()
+ );
+ }
+
+ public function testListShortAbbreviations() {
+
+ $this->assertInternalType(
+ 'array',
+ Timezone::listShortAbbreviations()
+ );
+ }
+
+ /**
+ * @dataProvider timezoneProvider
+ */
+ public function testIsValidAndIsMilitary( $abbrevation, $isValid, $isMilitary ) {
+
+ $this->assertEquals(
+ $isValid,
+ Timezone::isValid( $abbrevation )
+ );
+
+ $this->assertEquals(
+ $isMilitary,
+ Timezone::isMilitary( $abbrevation )
+ );
+ }
+
+ /**
+ * @dataProvider timezoneProvider
+ */
+ public function testGetIdByAbbreviation( $abbrevation, $isValid, $isMilitary, $expectedId ) {
+
+ $this->assertEquals(
+ $expectedId,
+ Timezone::getIdByAbbreviation( $abbrevation )
+ );
+ }
+
+ /**
+ * @dataProvider offsetProvider
+ */
+ public function testGetOffsetByAbbreviation( $abbrevation, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ Timezone::getOffsetByAbbreviation( $abbrevation )
+ );
+ }
+
+ public function testGetModifiedTime() {
+
+ $dti = new DateTime( '2017-08-01 10:00:00+00:00' );
+ $tz = 'Asia/Tokyo';
+
+ $dateTime = Timezone::getModifiedTime( $dti, $tz );
+
+ $this->assertEquals(
+ '2017-08-01 19:00:00',
+ $dateTime->format( 'Y-m-d H:i:s' )
+ );
+ }
+
+ public function timezoneProvider() {
+
+ $provider[] = [
+ 'UTC',
+ true,
+ false,
+ 0
+ ];
+
+ $provider[] = [
+ 'Z',
+ true,
+ true,
+ 1
+ ];
+
+ $provider[] = [
+ 'Unknown',
+ false,
+ false,
+ false
+ ];
+
+ $provider[] = [
+ 'Asia/Tokyo',
+ true,
+ false,
+ 'Asia/Tokyo'
+ ];
+
+ $provider[] = [
+ 'America/Los Angeles',
+ true,
+ false,
+ 'America/Los_Angeles'
+ ];
+
+ $provider[] = [
+ 'America/Los_Angeles',
+ true,
+ false,
+ 'America/Los_Angeles'
+ ];
+
+ return $provider;
+ }
+
+ public function offsetProvider() {
+
+ $provider[] = [
+ 'UTC',
+ 0
+ ];
+
+ $provider[] = [
+ 'Z',
+ 0
+ ];
+
+ $provider[] = [
+ 'Unknown',
+ false
+ ];
+
+ $provider[] = [
+ 'Asia/Tokyo',
+ 32400
+ ];
+
+ // Maybe return PST or PDT during Daylight Savings Time
+ /*
+ $provider[] = array(
+ 'America/Los Angeles',
+ -25200
+ );
+
+ $provider[] = array(
+ 'America/Los_Angeles',
+ -25200
+ );
+ */
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TypesValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TypesValueTest.php
new file mode 100644
index 00000000..aa593f8b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/TypesValueTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataValues\TypesValue;
+use SMW\DataValues\ValueParsers\TypesValueParser;
+
+/**
+ * @covers \SMW\DataValues\TypesValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TypesValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TypesValue::class,
+ new TypesValue()
+ );
+
+ // FIXME Legacy naming remove in 3.1.x
+ $this->assertInstanceOf(
+ '\SMWTypesValue',
+ new TypesValue()
+ );
+ }
+
+ public function testNewFromTypeId() {
+
+ $this->assertInstanceOf(
+ TypesValue::class,
+ TypesValue::newFromTypeId( '_dat' )
+ );
+ }
+
+ /**
+ * @dataProvider typeLabelProvider
+ */
+ public function testNewFromTypeId_GetWikiValue( $type, $label ) {
+
+ $this->assertEquals(
+ $label,
+ TypesValue::newFromTypeId( $type )->getWikiValue()
+ );
+ }
+
+ /**
+ * @dataProvider typeUriProvider
+ */
+ public function testGetTypeUriFromTypeId( $type, $url ) {
+
+ $this->assertEquals(
+ $url,
+ TypesValue::getTypeUriFromTypeId( $type )->getUri()
+ );
+ }
+
+ /**
+ * @dataProvider userWikiValueProvider
+ */
+ public function testSetUserValue_GetWikiValue( $value, $expected ) {
+
+ $instance = new TypesValue();
+ $instance->setUserValue( $value );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getWikiValue()
+ );
+ }
+
+ public function typeLabelProvider() {
+
+ yield [
+ '_dat',
+ 'Date'
+ ];
+
+ yield [
+ '_boo',
+ 'Boolean'
+ ];
+
+ yield [
+ 'foo',
+ ''
+ ];
+ }
+
+ public function typeUriProvider() {
+
+ yield [
+ '_dat',
+ 'http://semantic-mediawiki.org/swivt/1.0#_dat'
+ ];
+
+ yield [
+ '_boo',
+ 'http://semantic-mediawiki.org/swivt/1.0#_boo'
+ ];
+
+ yield [
+ 'foo',
+ 'http://semantic-mediawiki.org/swivt/1.0#foo'
+ ];
+ }
+
+ public function userWikiValueProvider() {
+
+ yield [
+ '_dat',
+ 'Date'
+ ];
+
+ yield [
+ 'Date',
+ 'Date'
+ ];
+
+ yield [
+ 'Foo',
+ 'Foo'
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/UniquenessConstraintValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/UniquenessConstraintValueTest.php
new file mode 100644
index 00000000..7040ee03
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/UniquenessConstraintValueTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\UniquenessConstraintValue;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\UniquenessConstraintValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\UniquenessConstraintValue',
+ new UniquenessConstraintValue()
+ );
+ }
+
+ public function testErrorForMissingFeatureSetting() {
+
+ $instance = new UniquenessConstraintValue();
+
+ $instance->setOption( 'smwgDVFeatures', '' );
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testErrorForInvalidBoolean() {
+
+ $instance = new UniquenessConstraintValue();
+
+ $instance->setOption( 'smwgDVFeatures', SMW_DV_PVUC );
+ $instance->setUserValue( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/CodeStringValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/CodeStringValueFormatterTest.php
new file mode 100644
index 00000000..b3221f9c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/CodeStringValueFormatterTest.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueFormatters\CodeStringValueFormatter;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\CodeStringValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CodeStringValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CodeStringValueFormatter::class,
+ new CodeStringValueFormatter()
+ );
+ }
+
+ /**
+ * @dataProvider stringValueProvider
+ */
+ public function testFormat( $userValue, $type, $linker, $expected ) {
+
+ $codeStringValue = DataValueFactory::getInstance()->newDataValueByType( '_cod' );
+ $codeStringValue->setUserValue( $userValue );
+
+ $instance = new CodeStringValueFormatter();
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $codeStringValue, [ $type, $linker ] )
+ );
+ }
+
+ public function stringValueProvider() {
+
+ $provider[] = [
+ 'foo',
+ CodeStringValueFormatter::VALUE,
+ null,
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ CodeStringValueFormatter::WIKI_SHORT,
+ null,
+ '<div class="smwpre">foo</div>'
+ ];
+
+ $provider[] = [
+ 'foo',
+ CodeStringValueFormatter::HTML_SHORT,
+ null,
+ '<div class="smwpre">foo</div>'
+ ];
+
+ $provider[] = [
+ 'foo',
+ CodeStringValueFormatter::WIKI_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">foo</div></div>'
+ ];
+
+ $provider[] = [
+ 'foo',
+ CodeStringValueFormatter::HTML_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">foo</div></div>'
+ ];
+
+ $provider[] = [
+ '<code><nowiki>&#x005B;&#x005B;Foo]]</nowiki></code>',
+ CodeStringValueFormatter::HTML_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">&#91;&#91;Foo]]</div></div>'
+ ];
+
+ $provider[] = [
+ '<code><nowiki>[[Foo]]</nowiki></code>',
+ CodeStringValueFormatter::HTML_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">&#91;&#91;Foo]]</div></div>'
+ ];
+
+ // > 255
+ $text = 'Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede ' .
+ 'lorem nulla justo diam wisi. Libero Nam turpis neque leo scelerisque nec habitasse a lacus mattis. Accumsan ' .
+ 'tincidunt Sed adipiscing nec facilisis tortor Nunc Sed ipsum tellus';
+
+ $expected = '<div class="smwpre"><div style="min-height:5em; overflow:auto;">Lorem&#160;ipsum&#160;dolor&#160;sit&#160;amet&#160;' .
+ 'consectetuer&#160;justo&#160;Nam&#160;quis&#160;lobortis&#160;vel.&#160;Sapien&#160;nulla&#160;enim&#160;Lorem&#160;enim&#160;' .
+ 'pede&#160;lorem&#160;nulla&#160;justo&#160;diam&#160;wisi.&#160;Libero&#160;Nam&#160;turpis&#160;neque&#160;leo&#160;' .
+ 'scelerisque&#160;nec&#160;habitasse&#160;a&#160;lacus&#160;mattis.&#160;Accumsan&#160;tincidunt&#160;Sed&#160;adipiscing&#160;' .
+ 'nec&#160;facilisis&#160;tortor&#160;Nunc&#160;Sed&#160;ipsum&#160;tellus</div></div>';
+
+ $provider[] = [
+ $text,
+ CodeStringValueFormatter::HTML_LONG,
+ null,
+ $expected
+ ];
+
+ $provider[] = [
+ $text,
+ CodeStringValueFormatter::WIKI_LONG,
+ null,
+ $expected
+ ];
+
+ // XMLContentEncode
+ $provider[] = [
+ '<foo>',
+ CodeStringValueFormatter::HTML_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">&lt;foo&gt;</div></div>'
+ ];
+
+ $provider[] = [
+ '<foo>',
+ CodeStringValueFormatter::HTML_SHORT,
+ null,
+ '<div class="smwpre">&lt;foo&gt;</div>'
+ ];
+
+ $provider[] = [
+ '*Foo',
+ CodeStringValueFormatter::WIKI_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">*Foo</div></div>'
+ ];
+
+ // JSON
+ $jsonString = '{"limit": 50,"offset": 0,"sort": [],"order": [],"mode": 1}';
+ $provider[] = [
+ $jsonString,
+ CodeStringValueFormatter::WIKI_LONG,
+ null,
+ '<div class="smwpre"><div style="min-height:5em; overflow:auto;">' . CodeStringValueFormatter::asJson( $jsonString ) . '</div></div>'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/DispatchingDataValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/DispatchingDataValueFormatterTest.php
new file mode 100644
index 00000000..77ecf082
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/DispatchingDataValueFormatterTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\ValueFormatters\DispatchingDataValueFormatter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\DispatchingDataValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DispatchingDataValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\DispatchingDataValueFormatter',
+ new DispatchingDataValueFormatter()
+ );
+ }
+
+ public function testGetDataValueFormatterForMatchableDataValue() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValueFormatter->expects( $this->once() )
+ ->method( 'isFormatterFor' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDataValueFormatter();
+ $instance->addDataValueFormatter( $dataValueFormatter );
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\DataValueFormatter',
+ $instance->getDataValueFormatterFor( $dataValue )
+ );
+ }
+
+ public function testGetDefaultDispatchingDataValueFormatterForMatchableDataValue() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValueFormatter->expects( $this->once() )
+ ->method( 'isFormatterFor' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDataValueFormatter();
+ $instance->addDefaultDataValueFormatter( $dataValueFormatter );
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\DataValueFormatter',
+ $instance->getDataValueFormatterFor( $dataValue )
+ );
+ }
+
+ public function testPrioritizeDispatchableDataValueFormatter() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValueFormatter->expects( $this->once() )
+ ->method( 'isFormatterFor' )
+ ->will( $this->returnValue( true ) );
+
+ $defaultDataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $defaultDataValueFormatter->expects( $this->never() )
+ ->method( 'isFormatterFor' );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDataValueFormatter();
+ $instance->addDefaultDataValueFormatter( $defaultDataValueFormatter );
+ $instance->addDataValueFormatter( $dataValueFormatter );
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\DataValueFormatter',
+ $instance->getDataValueFormatterFor( $dataValue )
+ );
+ }
+
+ public function testTryToGetDataValueFormatterForNonDispatchableDataValueThrowsException() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValueFormatter->expects( $this->once() )
+ ->method( 'isFormatterFor' )
+ ->will( $this->returnValue( false ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDataValueFormatter();
+ $instance->addDataValueFormatter( $dataValueFormatter );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getDataValueFormatterFor( $dataValue );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/MonolingualTextValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/MonolingualTextValueFormatterTest.php
new file mode 100644
index 00000000..9bdbd8d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/MonolingualTextValueFormatterTest.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter;
+use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new MonolingualTextValueParser() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter',
+ new MonolingualTextValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $monolingualTextValue = $this->getMockBuilder( '\SMW\DataValues\MonolingualTextValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MonolingualTextValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $monolingualTextValue )
+ );
+ }
+
+ public function testToUseCaptionOutput() {
+
+ $monolingualTextValue = new MonolingualTextValue();
+
+ $monolingualTextValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $monolingualTextValue->setCaption( 'ABC' );
+
+ $instance = new MonolingualTextValueFormatter( $monolingualTextValue );
+
+ $this->assertEquals(
+ 'ABC',
+ $instance->format( MonolingualTextValueFormatter::WIKI_SHORT )
+ );
+
+ $this->assertEquals(
+ 'ABC',
+ $instance->format( MonolingualTextValueFormatter::HTML_SHORT )
+ );
+ }
+
+ /**
+ * @dataProvider stringValueProvider
+ */
+ public function testFormat( $stringValue, $type, $linker, $expected ) {
+
+ $monolingualTextValue = new MonolingualTextValue();
+
+ $monolingualTextValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $monolingualTextValue->setUserValue( $stringValue );
+
+ $instance = new MonolingualTextValueFormatter( $monolingualTextValue );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $type, $linker )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new MonolingualTextValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( MonolingualTextValueFormatter::VALUE );
+ }
+
+ public function stringValueProvider() {
+
+ $provider[] = [
+ 'foo@en',
+ MonolingualTextValueFormatter::VALUE,
+ null,
+ 'foo@en'
+ ];
+
+ $provider[] = [
+ 'foo@en',
+ MonolingualTextValueFormatter::WIKI_SHORT,
+ null,
+ 'foo (en)'
+ ];
+
+ $provider[] = [
+ 'foo@en',
+ MonolingualTextValueFormatter::HTML_SHORT,
+ null,
+ 'foo (en)'
+ ];
+
+ $provider[] = [
+ 'foo@en',
+ MonolingualTextValueFormatter::WIKI_LONG,
+ null,
+ 'foo (en)'
+ ];
+
+ $provider[] = [
+ 'foo@en',
+ MonolingualTextValueFormatter::HTML_LONG,
+ null,
+ 'foo (en)'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NoValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NoValueFormatterTest.php
new file mode 100644
index 00000000..51ff75f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NoValueFormatterTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\ValueFormatters\NoValueFormatter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\NoValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NoValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\NoValueFormatter',
+ new NoValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new NoValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $dataValue )
+ );
+ }
+
+ public function testFormat() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSerialization' ] )
+ ->getMockForAbstractClass();
+
+ $dataItem->expects( $this->once() )
+ ->method( 'getSerialization' )
+ ->will( $this->returnValue( 'isFromSerializationMethod' ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getDataItem' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->once() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItem ) );
+
+ $instance = new NoValueFormatter( $dataValue );
+
+ $this->assertEquals(
+ 'isFromSerializationMethod',
+ $instance->format( NoValueFormatter::VALUE )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new NoValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( NoValueFormatter::VALUE );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NumberValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NumberValueFormatterTest.php
new file mode 100644
index 00000000..280b80d3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/NumberValueFormatterTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\TemperatureValue;
+use SMW\DataValues\ValueFormatters\NumberValueFormatter;
+use SMWNumberValue as NumberValue;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\NumberValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NumberValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\NumberValueFormatter',
+ new NumberValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new NumberValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $numberValue )
+ );
+ }
+
+ /**
+ * @dataProvider numberValueProvider
+ */
+ public function testNumberFormat( $numberUserValue, $type, $linker, $expected ) {
+
+ $numberValue = new NumberValue( '_num' );
+ $numberValue->setUserValue( $numberUserValue );
+
+ $numberValue->setOption( 'user.language', 'en' );
+ $numberValue->setOption( 'content.language', 'en' );
+
+ $instance = new NumberValueFormatter( $numberValue );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $type, $linker )
+ );
+ }
+
+ /**
+ * @dataProvider temperaturValueProvider
+ */
+ public function testTemperaturFormat( $numberUserValue, $type, $linker, $expected ) {
+
+ $temperatureValue = new TemperatureValue( '_num' );
+ $temperatureValue->setUserValue( $numberUserValue );
+
+ $temperatureValue->setOption( 'user.language', 'en' );
+ $temperatureValue->setOption( 'content.language', 'en' );
+
+ $instance = new NumberValueFormatter( $temperatureValue );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $type, $linker )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new NumberValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( NumberValueFormatter::VALUE );
+ }
+
+ public function testTryToFormatWithUnknownType() {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new NumberValueFormatter( $numberValue );
+
+ $this->assertEquals(
+ 'UNKNOWN',
+ $instance->format( 'Foo' )
+ );
+ }
+
+ public function numberValueProvider() {
+
+ $provider['v.1'] = [
+ 'foo',
+ NumberValueFormatter::VALUE,
+ null,
+ 'error'
+ ];
+
+ $provider['v.2'] = [
+ 100,
+ NumberValueFormatter::VALUE,
+ null,
+ 100
+ ];
+
+ $provider['v.3'] = [
+ 0.222,
+ NumberValueFormatter::VALUE,
+ null,
+ 0.222
+ ];
+
+ $provider['ws.1'] = [
+ 100,
+ NumberValueFormatter::WIKI_SHORT,
+ null,
+ 100
+ ];
+
+ $provider['ws.2'] = [
+ 100,
+ NumberValueFormatter::WIKI_SHORT,
+ 'notNull',
+ 100
+ ];
+
+ $provider['hs.1'] = [
+ 100,
+ NumberValueFormatter::HTML_SHORT,
+ null,
+ 100
+ ];
+
+ $provider['wl.1'] = [
+ 100,
+ NumberValueFormatter::WIKI_LONG,
+ null,
+ 100
+ ];
+
+ $provider['wl.2'] = [
+ 100,
+ NumberValueFormatter::WIKI_LONG,
+ 'notNull',
+ 100
+ ];
+
+ $provider['hl.1'] = [
+ 100,
+ NumberValueFormatter::HTML_LONG,
+ null,
+ 100
+ ];
+
+ return $provider;
+ }
+
+ public function temperaturValueProvider() {
+
+ $provider['v.1'] = [
+ '100 K',
+ NumberValueFormatter::VALUE,
+ null,
+ '100 K'
+ ];
+
+ $provider['ws.1'] = [
+ '100 K',
+ NumberValueFormatter::WIKI_SHORT,
+ null,
+ '100 K'
+ ];
+
+ $provider['ws.2'] = [
+ '100 K',
+ NumberValueFormatter::WIKI_SHORT,
+ 'notNull',
+ '<span class="smw-highlighter" data-type="3" data-state="inline" data-title="Unit conversion" title="-173.15 °C -279.67 °F 180 °R ">' .
+ '<span class="smwtext">100 K</span>' .
+ '<span class="smwttcontent">-173.15&#160;°C <br />-279.67&#160;°F <br />180&#160;°R <br /></span></span>'
+ ];
+
+ $provider['wl.1'] = [
+ '100 K',
+ NumberValueFormatter::WIKI_LONG,
+ null,
+ '100&#160;K (-173.15&#160;°C, -279.67&#160;°F, 180&#160;°R)'
+ ];
+
+ $provider['wl.2'] = [
+ '100 K',
+ NumberValueFormatter::WIKI_LONG,
+ 'notNull',
+ '100&#160;K (-173.15&#160;°C, -279.67&#160;°F, 180&#160;°R)'
+ ];
+
+ return $provider;
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/PropertyValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/PropertyValueFormatterTest.php
new file mode 100644
index 00000000..bc374682
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/PropertyValueFormatterTest.php
@@ -0,0 +1,521 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ValueFormatters\PropertyValueFormatter;
+use SMW\DataValues\ValueParsers\PropertyValueParser;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+use SMW\DataValues\PropertyValue;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\PropertyValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataItemFactory;
+ private $propertyLabelFinder;
+ private $propertySpecificationLookup;
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertyLabelFinder', $this->propertyLabelFinder );
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new PropertyValueParser() ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\PropertyValueFormatter',
+ new PropertyValueFormatter( $this->propertySpecificationLookup )
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $propertyValue = $this->getMockBuilder( '\SMWPropertyValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $propertyValue )
+ );
+ }
+
+ public function testFormatWithInvalidFormat() {
+
+ $propertyValue = new PropertyValue();
+ $propertyValue->setDataItem( $this->dataItemFactory->newDIProperty( 'Foo' ) );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->format( $propertyValue, [ 'Foo' ] )
+ );
+ }
+
+ public function testFormatWithCaptionOutput() {
+
+ $propertyValue = new PropertyValue();
+ $propertyValue->setDataItem( $this->dataItemFactory->newDIProperty( 'Foo' ) );
+ $propertyValue->setCaption( 'ABC[<>]' );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $this->assertEquals(
+ 'ABC[<>]',
+ $instance->format( $propertyValue, [ PropertyValueFormatter::WIKI_SHORT ] )
+ );
+
+ $this->assertEquals(
+ 'ABC[&lt;&gt;]',
+ $instance->format( $propertyValue, [ PropertyValueFormatter::HTML_SHORT ] )
+ );
+ }
+
+
+ public function testFormatWithCaptionOutputAndHighlighter() {
+
+ $propertyValue = new PropertyValue();
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, false );
+
+ $propertyValue->setDataItem( $this->dataItemFactory->newDIProperty( 'Foo' ) );
+ $propertyValue->setCaption( 'ABC[<>]' );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $this->assertContains(
+ '<span class="smwtext">ABC[<>]</span><span class="smwttcontent"></span>',
+ $instance->format( $propertyValue, [ PropertyValueFormatter::WIKI_SHORT ] )
+ );
+
+ $this->assertContains(
+ '<span class="smwtext">ABC[&lt;&gt;]</span><span class="smwttcontent"></span>',
+ $instance->format( $propertyValue, [ PropertyValueFormatter::HTML_SHORT ] )
+ );
+ }
+
+ /**
+ * @dataProvider propertyValueProvider
+ */
+ public function testFormat( $property, $type, $linker, $expected ) {
+
+ $propertyValue = new PropertyValue();
+ $propertyValue->setDataItem( $property );
+
+ $propertyValue->setOption( PropertyValue::OPT_CONTENT_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_USER_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $expected = $this->testEnvironment->replaceNamespaceWithLocalizedText(
+ SMW_NS_PROPERTY,
+ $expected
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $propertyValue, [ $type, $linker ] )
+ );
+ }
+
+ /**
+ * @dataProvider preferredLabelValueProvider
+ */
+ public function testFormatWithPreferredLabel( $property, $preferredLabel, $type, $linker, $expected ) {
+
+ // Ensures the mocked instance is injected and registered with the
+ // PropertyRegistry instance
+ \SMW\PropertyRegistry::clear();
+
+ $this->propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'findPropertyListFromLabelByLanguageCode' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'findPreferredPropertyLabelByLanguageCode' )
+ ->will( $this->returnValue( $preferredLabel ) );
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'searchPropertyIdByLabel' )
+ ->will( $this->returnValue( false ) );
+
+ $this->testEnvironment->registerObject( 'PropertyLabelFinder', $this->propertyLabelFinder );
+
+ $propertyValue = new PropertyValue();
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $propertyValue->setOption( 'smwgDVFeatures', SMW_DV_PROV_LHNT );
+ $propertyValue->setOption( PropertyValue::OPT_CONTENT_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_USER_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setUserValue( $property );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $expected = $this->testEnvironment->replaceNamespaceWithLocalizedText(
+ SMW_NS_PROPERTY,
+ $expected
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $propertyValue, [ $type, $linker ] )
+ );
+
+ \SMW\PropertyRegistry::clear();
+ }
+
+ /**
+ * @dataProvider preferredLabelAndCaptionValueProvider
+ */
+ public function testFormatWithPreferredLabelAndCaption( $property, $caption, $preferredLabel, $type, $linker, $expected ) {
+
+ // Ensures the mocked instance is injected and registered with the
+ // PropertyRegistry instance
+ \SMW\PropertyRegistry::clear();
+
+ $this->propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'findPropertyListFromLabelByLanguageCode' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'findPreferredPropertyLabelByLanguageCode' )
+ ->will( $this->returnValue( $preferredLabel ) );
+
+ $this->propertyLabelFinder->expects( $this->any() )
+ ->method( 'searchPropertyIdByLabel' )
+ ->will( $this->returnValue( false ) );
+
+ $this->testEnvironment->registerObject( 'PropertyLabelFinder', $this->propertyLabelFinder );
+
+ $propertyValue = new PropertyValue();
+
+ $propertyValue->setOption( 'smwgDVFeatures', SMW_DV_PROV_LHNT );
+ $propertyValue->setOption( PropertyValue::OPT_CONTENT_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_USER_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $propertyValue->setUserValue( $property );
+ $propertyValue->setCaption( $caption );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $expected = $this->testEnvironment->replaceNamespaceWithLocalizedText(
+ SMW_NS_PROPERTY,
+ $expected
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $propertyValue, [ $type, $linker ] )
+ );
+
+ \SMW\PropertyRegistry::clear();
+ }
+
+ /**
+ * @dataProvider formattedLabelProvider
+ */
+ public function testFormattedLabelLabel( $property, $linker, $expected ) {
+
+ $propertyValue = new PropertyValue();
+
+ $propertyValue->setOption( PropertyValue::OPT_CONTENT_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_USER_LANGUAGE, 'en' );
+ $propertyValue->setOption( PropertyValue::OPT_NO_HIGHLIGHT, true );
+
+ $propertyValue->setDataItem( $property );
+
+ $propertyValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $expected = $this->testEnvironment->replaceNamespaceWithLocalizedText(
+ SMW_NS_PROPERTY,
+ $expected
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $propertyValue, [ PropertyValue::FORMAT_LABEL, $linker ] )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new PropertyValueFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( PropertyValueFormatter::VALUE );
+ }
+
+ public function propertyValueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ PropertyValueFormatter::VALUE,
+ null,
+ 'Foo'
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ PropertyValueFormatter::WIKI_SHORT,
+ null,
+ 'Foo'
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Foo'
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ PropertyValueFormatter::WIKI_LONG,
+ null,
+ 'Property:Foo'
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ PropertyValueFormatter::HTML_LONG,
+ null,
+ 'Property:Foo'
+ ];
+
+ return $provider;
+ }
+
+ public function preferredLabelValueProvider() {
+
+ $linker = 'some';
+
+ $provider[] = [
+ 'Foo',
+ 'Bar',
+ PropertyValueFormatter::VALUE,
+ null,
+ 'Bar'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar',
+ PropertyValueFormatter::WIKI_SHORT,
+ null,
+ 'Bar&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar',
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Bar&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar',
+ PropertyValueFormatter::WIKI_LONG,
+ $linker,
+ '[[:Property:Foo|Bar]]&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar',
+ PropertyValueFormatter::HTML_LONG,
+ null,
+ 'Property:Foo&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar with',
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Bar with&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ return $provider;
+ }
+
+ public function preferredLabelAndCaptionValueProvider() {
+
+ $linker = 'some';
+
+ $provider[] = [
+ 'Foo',
+ false,
+ 'Bar',
+ PropertyValueFormatter::VALUE,
+ null,
+ 'Bar'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ false,
+ 'Bar',
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Bar'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar with',
+ 'Bar with',
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Bar with&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'Bar&nbsp;with',
+ 'Bar with',
+ PropertyValueFormatter::HTML_SHORT,
+ null,
+ 'Bar&nbsp;with&nbsp;<span title="Foo"><sup>áµ–</sup></span>'
+ ];
+
+ return $provider;
+ }
+
+ public function formattedLabelProvider() {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property->expects( $this->any() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( \SMWDataItem::TYPE_PROPERTY ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getPreferredLabel' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getLabel' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getCanonicalLabel' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $provider[] = [
+ $property,
+ null,
+ '&nbsp;<span style="font-size:small;">(Foo)</span>'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/ReferenceValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/ReferenceValueFormatterTest.php
new file mode 100644
index 00000000..ef0a447b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/ReferenceValueFormatterTest.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ReferenceValue;
+use SMW\DataValues\ValueFormatters\ReferenceValueFormatter;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\ReferenceValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReferenceValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\ReferenceValueFormatter',
+ new ReferenceValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $referenceValue = $this->getMockBuilder( '\SMW\DataValues\ReferenceValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ReferenceValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $referenceValue )
+ );
+ }
+
+ public function testToUseCaptionOutput() {
+
+ $referenceValue = new ReferenceValue();
+ $referenceValue->setCaption( 'ABC' );
+
+ $instance = new ReferenceValueFormatter( $referenceValue );
+
+ $this->assertEquals(
+ 'ABC',
+ $instance->format( ReferenceValueFormatter::WIKI_SHORT )
+ );
+
+ $this->assertEquals(
+ 'ABC',
+ $instance->format( ReferenceValueFormatter::HTML_SHORT )
+ );
+ }
+
+ /**
+ * @dataProvider stringValueProvider
+ */
+ public function testFormat( $suserValue, $type, $linker, $expected ) {
+
+ $referenceValue = new ReferenceValue();
+
+ $referenceValue->setFieldProperties( [
+ $this->dataItemFactory->newDIProperty( 'Foo' ),
+ $this->dataItemFactory->newDIProperty( 'Date' ),
+ $this->dataItemFactory->newDIProperty( 'URL' )
+ ] );
+
+ $referenceValue->setOption( ReferenceValue::OPT_CONTENT_LANGUAGE, 'en' );
+ $referenceValue->setOption( ReferenceValue::OPT_USER_LANGUAGE, 'en' );
+
+ $referenceValue->setUserValue( $suserValue );
+
+ $instance = new ReferenceValueFormatter( $referenceValue );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->format( $type, $linker )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new ReferenceValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( ReferenceValueFormatter::VALUE );
+ }
+
+ public function stringValueProvider() {
+
+ $provider[] = [
+ 'abc;12;3',
+ ReferenceValueFormatter::VALUE,
+ null,
+ 'Abc'
+ ];
+
+ $provider[] = [
+ 'abc;12;3',
+ ReferenceValueFormatter::VALUE,
+ false,
+ 'Abc;12;3'
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::WIKI_SHORT,
+ null,
+ 'Abc'
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::WIKI_SHORT,
+ false,
+ [
+ 'Abc',
+ 'class="smw-reference smw-reference-indicator smw-highlighter smwttinline"',
+ 'data-title="Reference"',
+ 'title="Date: ?, URL: ?"'
+ ]
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::HTML_SHORT,
+ null,
+ 'Abc'
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::HTML_SHORT,
+ false,
+ [
+ 'Abc',
+ 'class="smw-reference smw-reference-indicator smw-highlighter smwttinline"',
+ 'data-title="Reference"',
+ 'title="Date: ?, URL: ?"'
+ ]
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::WIKI_LONG,
+ null,
+ 'Abc'
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::WIKI_LONG,
+ false,
+ [
+ 'Abc',
+ 'class="smw-reference smw-reference-indicator smw-highlighter smwttinline"',
+ 'data-title="Reference"',
+ 'title="Date: ?, URL: ?"'
+ ]
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::HTML_LONG,
+ null,
+ 'Abc'
+ ];
+
+ $provider[] = [
+ 'abc',
+ ReferenceValueFormatter::HTML_LONG,
+ false,
+ [
+ 'Abc',
+ 'class="smw-reference smw-reference-indicator smw-highlighter smwttinline"',
+ 'data-title="Reference"',
+ 'title="Date: ?, URL: ?"'
+ ]
+ ];
+
+ // Notice: Undefined variable: dataValue in
+ $provider[] = [
+ '?;12;3',
+ ReferenceValueFormatter::VALUE,
+ null,
+ ''
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/StringValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/StringValueFormatterTest.php
new file mode 100644
index 00000000..c12b36ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/StringValueFormatterTest.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\StringValue;
+use SMW\DataValues\ValueFormatters\StringValueFormatter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\StringValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class StringValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ StringValueFormatter::class,
+ new StringValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $stringValue = $this->getMockBuilder( '\SMW\DataValues\StringValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new StringValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $stringValue )
+ );
+ }
+
+ public function testToUseCaptionOutput() {
+
+ $stringValue = new StringValue( '_txt' );
+ $stringValue->setCaption( 'ABC[<>]' );
+
+ $instance = new StringValueFormatter();
+
+ $this->assertEquals(
+ 'ABC[<>]',
+ $instance->format( $stringValue, [ StringValueFormatter::WIKI_SHORT ] )
+ );
+
+ $this->assertEquals(
+ 'ABC[&lt;&gt;]',
+ $instance->format( $stringValue, [ StringValueFormatter::HTML_SHORT ] )
+ );
+ }
+
+ /**
+ * @dataProvider stringValueProvider
+ */
+ public function testFormat( $stringUserValue, $type, $linker, $expected ) {
+
+ $stringValue = new StringValue( '_txt' );
+ $stringValue->setUserValue( $stringUserValue );
+
+ $instance = new StringValueFormatter();
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $stringValue, [ $type, $linker ] )
+ );
+ }
+
+ public function testFormatWithReducedLength() {
+
+ // > 255 / Reduced length
+ $text = 'Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede ' .
+ 'lorem nulla justo diam wisi. Libero Nam turpis neque leo scelerisque nec habitasse a lacus mattis. Accumsan ' .
+ 'tincidunt Sed adipiscing nec facilisis tortor Nunc Sed ipsum tellus';
+
+ $expected = 'Lorem ipsum dolor sit amet consectetuer …';
+
+ $stringValue = new StringValue( '_txt' );
+ $stringValue->setUserValue( $text );
+ $stringValue->setOutputFormat( 40 );
+
+ $instance = new StringValueFormatter();
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $stringValue, [ StringValueFormatter::HTML_LONG ] )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $stringValue, [ StringValueFormatter::WIKI_SHORT ] )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new StringValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( StringValueFormatter::VALUE );
+ }
+
+ public function stringValueProvider() {
+
+ $provider[] = [
+ 'foo',
+ StringValueFormatter::VALUE,
+ null,
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ StringValueFormatter::WIKI_SHORT,
+ null,
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ StringValueFormatter::HTML_SHORT,
+ null,
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ StringValueFormatter::WIKI_LONG,
+ null,
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ StringValueFormatter::HTML_LONG,
+ null,
+ 'foo'
+ ];
+
+ // > 255
+ $text = 'Lorem ipsum dolor sit amet consectetuer justo Nam quis lobortis vel. Sapien nulla enim Lorem enim pede ' .
+ 'lorem nulla justo diam wisi. Libero Nam turpis neque leo scelerisque nec habitasse a lacus mattis. Accumsan ' .
+ 'tincidunt Sed adipiscing nec facilisis tortor Nunc Sed ipsum tellus';
+
+ $provider[] = [
+ $text,
+ StringValueFormatter::HTML_LONG,
+ null,
+ 'Lorem ipsum dolor sit amet consectetuer ju <span class="smwwarning">…</span> nec facilisis tortor Nunc Sed ipsum tellus'
+ ];
+
+ $provider[] = [
+ $text,
+ StringValueFormatter::WIKI_LONG,
+ null,
+ 'Lorem ipsum dolor sit amet consectetuer ju <span class="smwwarning">…</span> nec facilisis tortor Nunc Sed ipsum tellus'
+ ];
+
+ // Avoid breaking links
+ $text = 'Lorem ipsum dolor sit amet consectetuer [[justo Nam quis lobortis vel]]. Sapien nulla enim Lorem enim pede ' .
+ 'lorem nulla justo diam wisi. Libero Nam turpis neque leo scelerisque nec habitasse a lacus mattis. Accumsan ' .
+ 'tincidunt [[Sed adipiscing nec]] facilisis tortor Nunc Sed ipsum tellus';
+
+ $provider[] = [
+ $text,
+ StringValueFormatter::HTML_LONG,
+ null,
+ 'Lorem ipsum dolor sit amet consectetuer [[justo Nam quis lobortis vel]] <span class="smwwarning">…</span> [[Sed adipiscing nec]] facilisis tortor Nunc Sed ipsum tellus'
+ ];
+
+ // XMLContentEncode
+ $provider[] = [
+ '<foo>',
+ StringValueFormatter::HTML_LONG,
+ null,
+ '&lt;foo&gt;'
+ ];
+
+ $provider[] = [
+ '<foo>',
+ StringValueFormatter::HTML_SHORT,
+ null,
+ '&lt;foo&gt;'
+ ];
+
+ $provider[] = [
+ '*Foo',
+ StringValueFormatter::WIKI_LONG,
+ null,
+ "\n" . '*Foo' . "\n"
+ ];
+
+ $provider[] = [
+ '#Foo',
+ StringValueFormatter::WIKI_LONG,
+ null,
+ "\n" . '#Foo' . "\n"
+ ];
+
+ $provider[] = [
+ ':Foo',
+ StringValueFormatter::WIKI_LONG,
+ null,
+ "\n" . ':Foo' . "\n"
+ ];
+
+ $provider[] = [
+ '* Foo',
+ StringValueFormatter::HTML_LONG,
+ null,
+ "\n" . '* Foo' . "\n"
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/TimeValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/TimeValueFormatterTest.php
new file mode 100644
index 00000000..4d174ea9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueFormatters/TimeValueFormatterTest.php
@@ -0,0 +1,548 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueFormatters;
+
+use SMW\DataValues\ValueFormatters\TimeValueFormatter;
+use SMW\DataValues\ValueParsers\TimeValueParser;
+use SMWTimeValue as TimeValue;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueFormatters\TimeValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TimeValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataValueServiceFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( new TimeValueParser() ) );
+
+ $this->dataValueServiceFactory->expects( $this->any() )
+ ->method( 'getConstraintValueValidator' )
+ ->will( $this->returnValue( $constraintValueValidator ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueFormatters\TimeValueFormatter',
+ new TimeValueFormatter()
+ );
+ }
+
+ public function testIsFormatterForValidation() {
+
+ $timeValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new TimeValueFormatter();
+
+ $this->assertTrue(
+ $instance->isFormatterFor( $timeValue )
+ );
+ }
+
+ public function testTryToFormatOnMissingDataValueThrowsException() {
+
+ $instance = new TimeValueFormatter();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->format( TimeValueFormatter::VALUE );
+ }
+
+ public function testSetGetOptionValue() {
+
+ $instance = new TimeValueFormatter();
+ $instance->setOption( 'Foo', 1001 );
+
+ $this->assertEquals(
+ 1001,
+ $instance->getOption( 'Foo' )
+ );
+ }
+
+ public function testToUseCaptionOutput() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setCaption( 'ABC[<>]' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ 'ABC[<>]',
+ $instance->format( TimeValueFormatter::WIKI_SHORT )
+ );
+ }
+
+ /**
+ * @dataProvider timeInputProvider
+ */
+ public function testFormat( $timeUserValue, $type, $format, $linker, $languageCode, $expected ) {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( $timeUserValue );
+
+ $timeValue->setOutputFormat( $format );
+
+ $timeValue->setOption( 'user.language', $languageCode );
+ $timeValue->setOption( 'content.language', $languageCode );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ $expected,
+ $instance->format( $type, $linker )
+ );
+ }
+
+ public function testGetISO8601DateForMinDefault() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2000' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2000-01-01',
+ $instance->getISO8601Date( true )
+ );
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2000-02-23 12:02' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2000-02-23T12:02:00',
+ $instance->getISO8601Date( true )
+ );
+ }
+
+ public function testGetISO8601DateForMaxDefault() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2000' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2000-12-31',
+ $instance->getISO8601Date( false )
+ );
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2000-02-23 12:02' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2000-02-23T12:02:00',
+ $instance->getISO8601Date( false )
+ );
+ }
+
+ public function testGetCaptionFromDataItemForPositiveYearWithEraMarker() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2000 AD' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ 'AD 2000',
+ $instance->getCaptionFromDataItem( $timeValue->getDataItem() )
+ );
+ }
+
+ public function testLeapYear() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2016-02-29' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ $this->assertEquals(
+ '2016-02-29',
+ $instance->getISO8601Date()
+ );
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2016-02' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2016-02-29',
+ $instance->getISO8601Date( false )
+ );
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2015-02-28',
+ $instance->getISO8601Date( false )
+ );
+ }
+
+ public function testInvalidLeapYear() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-29' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testMediaWikiDate_WithDifferentLanguage() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-28' );
+ $timeValue->setOption( 'user.language', 'en' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '28 February 2015',
+ $instance->getMediaWikiDate()
+ );
+
+ $timeValue->setOption( 'user.language', 'ja' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2015年2月28日 (土)',
+ $instance->getMediaWikiDate()
+ );
+ }
+
+ public function testLOCLOutputFormat() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-28' );
+
+ $timeValue->setOption( TimeValue::OPT_USER_LANGUAGE, 'en' );
+ $timeValue->setOutputFormat( 'LOCL' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ $instance->format( TimeValueFormatter::WIKI_LONG ),
+ $instance->getLocalizedFormat( $timeValue->getDataItem() )
+ );
+
+ $this->assertEquals(
+ $instance->format( TimeValueFormatter::HTML_LONG ),
+ $instance->getLocalizedFormat( $timeValue->getDataItem() )
+ );
+ }
+
+ public function testLOCLOutputFormatWithSpecificAnnotatedLanguage() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-28' );
+
+ $timeValue->setOption( TimeValue::OPT_USER_LANGUAGE, 'en' );
+ $timeValue->setOutputFormat( 'LOCL@ja' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2015年2月28日 (土)',
+ $instance->getLocalizedFormat( $timeValue->getDataItem() )
+ );
+ }
+
+ public function testLOCLOutputFormatWithTimeZone() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-28 12:12:00 A' );
+
+ $timeValue->setOption( TimeValue::OPT_USER_LANGUAGE, 'en' );
+ $timeValue->setOutputFormat( 'LOCL#TZ' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '12:12:00 A, 28 February 2015',
+ $instance->format( TimeValueFormatter::HTML_LONG )
+ );
+
+ $this->assertEquals(
+ '2015-02-28 12:12:00 A',
+ $instance->format( TimeValueFormatter::WIKI_SHORT )
+ );
+ }
+
+ public function testLOCLOutputFormatWithTimeZoneOnSpecificAnnotatedLanguage() {
+
+ $timeValue = new TimeValue( '_dat' );
+ $timeValue->setDataValueServiceFactory(
+ $this->dataValueServiceFactory
+ );
+
+ $timeValue->setUserValue( '2015-02-28 12:12:00 A' );
+
+ $timeValue->setOption( TimeValue::OPT_USER_LANGUAGE, 'en' );
+ $timeValue->setOutputFormat( 'LOCL@ja#TZ' );
+
+ $instance = new TimeValueFormatter( $timeValue );
+
+ $this->assertEquals(
+ '2015年2月28日 (土) 12:12:00 A',
+ $instance->format( TimeValueFormatter::HTML_LONG )
+ );
+
+ $this->assertEquals(
+ '2015-02-28 12:12:00 A',
+ $instance->format( TimeValueFormatter::WIKI_SHORT )
+ );
+ }
+
+ public function timeInputProvider() {
+
+ #0
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::VALUE,
+ '',
+ null,
+ '',
+ '2000'
+ ];
+
+ #1
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::VALUE,
+ 'ISO',
+ null,
+ '',
+ '2000'
+ ];
+
+ #2
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::WIKI_SHORT,
+ 'ISO',
+ null,
+ '',
+ '2000'
+ ];
+
+ #3
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::HTML_SHORT,
+ 'ISO',
+ null,
+ '',
+ '2000'
+ ];
+
+ #4
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::WIKI_LONG,
+ 'ISO',
+ null,
+ '',
+ '2000-01-01'
+ ];
+
+ #5
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::HTML_LONG,
+ 'ISO',
+ null,
+ '',
+ '2000-01-01'
+ ];
+
+ #6
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::WIKI_LONG,
+ 'MEDIAWIKI',
+ null,
+ '',
+ '2000'
+ ];
+
+ #7
+ $provider[] = [
+ '2000',
+ TimeValueFormatter::HTML_LONG,
+ 'MEDIAWIKI',
+ null,
+ '',
+ '2000'
+ ];
+
+ #8
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::VALUE,
+ '',
+ null,
+ '',
+ '2000-02'
+ ];
+
+ #9
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::VALUE,
+ 'ISO',
+ null,
+ '',
+ '2000-02'
+ ];
+
+ #10
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::WIKI_SHORT,
+ '',
+ null,
+ '',
+ '2000-02'
+ ];
+
+ #11
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::HTML_SHORT,
+ 'ISO',
+ null,
+ '',
+ '2000-02'
+ ];
+
+ #12
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::WIKI_LONG,
+ 'ISO',
+ null,
+ '',
+ '2000-02-01'
+ ];
+
+ #13
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::HTML_LONG,
+ 'ISO',
+ null,
+ '',
+ '2000-02-01'
+ ];
+
+ #14
+ $provider[] = [
+ '2000-02',
+ TimeValueFormatter::HTML_LONG,
+ 'LOCL',
+ null,
+ 'en',
+ 'February 2000'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsListValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsListValueParserTest.php
new file mode 100644
index 00000000..71c129a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsListValueParserTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\ValueParsers\AllowsListValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\AllowsListValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class AllowsListValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ private $mediaWikiNsContentReader;
+
+ protected function setUp() {
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueParsers\AllowsListValueParser',
+ new AllowsListValueParser( $this->mediaWikiNsContentReader )
+ );
+ }
+
+ public function testParseAndMatchFromResource() {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( " \n*Foo\n**Foobar|bar\n" ) );
+
+ $instance = new AllowsListValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $this->assertEquals(
+ [
+ 'Foo' => 'Foo',
+ 'Foobar' => 'bar'
+ ],
+ $instance->parse( 'Bar' )
+ );
+ }
+
+ public function testParseAndMatchFromJSON() {
+
+ $contents = json_encode( [ 'Foo' => 'Foo', 'Foobar' => 'fooooo bar' ] );
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new AllowsListValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $instance->clear();
+
+ $this->assertEquals(
+ [
+ 'Foo' => 'Foo',
+ 'Foobar' => 'fooooo bar'
+ ],
+ $instance->parse( 'Bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsPatternValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsPatternValueParserTest.php
new file mode 100644
index 00000000..4c8c23dd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/AllowsPatternValueParserTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\AllowsPatternValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsPatternValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ private $mediaWikiNsContentReader;
+
+ protected function setUp() {
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueParsers\AllowsPatternValueParser',
+ new AllowsPatternValueParser( $this->mediaWikiNsContentReader )
+ );
+ }
+
+ public function testParseAndMatchFromResource() {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( " \nFoo|^(Bar|Foo bar)$\n Bar|^(ABC|DEF)$\n" ) );
+
+ $instance = new AllowsPatternValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $this->assertEquals(
+ '^(ABC|DEF)$',
+ $instance->parse( 'Bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/ImportValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/ImportValueParserTest.php
new file mode 100644
index 00000000..43682acb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/ImportValueParserTest.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\ImportValue;
+use SMW\DataValues\ValueParsers\ImportValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\ImportValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ImportValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ private $mediaWikiNsContentReader;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ImportValueParser::class,
+ new ImportValueParser( $this->mediaWikiNsContentReader )
+ );
+ }
+
+ public function testTryParseForInvalidValueFormat() {
+
+ $instance = new ImportValueParser( $this->mediaWikiNsContentReader );
+ $instance->parse( 'incorrectFormat' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testTryParseForValidValueFormatErroredByNonExistingImportEntry() {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->with( $this->equalTo( ImportValue::IMPORT_PREFIX . 'Foo' ) )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ImportValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $instance->parse( 'Foo:bar' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider invalidUriContent
+ */
+ public function testTryParseForValidValueFormatErroredByUriMismatch( $content ) {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $content ) );
+
+ $instance = new ImportValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $instance->parse( 'Foo:bar' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider invalidTypeContent
+ */
+ public function testTryParseForValidValueFormatErroredByTypeMismatch( $content, $typelist ) {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $content ) );
+
+ $instance = new ImportValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $instance->parse( 'Foo:bar' );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider validMatchTypeContent
+ */
+ public function testParseForValidValueToMatchType( $content, $parseValue, $expected ) {
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $content ) );
+
+ $instance = new ImportValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $result = $instance->parse( $parseValue );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ foreach ( $result as $key => $value ) {
+ $this->assertEquals(
+ $expected[$key],
+ $value
+ );
+ }
+ }
+
+ public function invalidUriContent() {
+
+ $provider[] = [
+ ''
+ ];
+
+ // Missing head
+ $provider[] = [
+ "Foo\n name|Type:Text\n"
+ ];
+
+ return $provider;
+ }
+
+ public function invalidTypeContent() {
+
+ // Url missing
+ $provider[] = [
+ '|[http://www.foaf-project.org/ Friend Of A Friend]\n name',
+ []
+ ];
+
+ // Type missing
+ $provider[] = [
+ 'http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name',
+ []
+ ];
+
+ // Cannot match section name
+ $provider[] = [
+ "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n",
+ [ 'name' => 'Type:Text' ]
+ ];
+
+ $provider[] = [
+ '',
+ []
+ ];
+
+ $provider[] = [
+ ' ',
+ []
+ ];
+
+ return $provider;
+ }
+
+ public function validMatchTypeContent() {
+
+ #0
+ $provider[] = [
+ "http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n",
+ 'Foaf:name',
+ [
+ 'Foaf',
+ 'name',
+ 'http://xmlns.com/foaf/0.1/',
+ '[http://www.foaf-project.org/ Friend Of A Friend]',
+ 'Type:Text'
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ " http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n name|Type:Text\n",
+ 'Foaf:name',
+ [
+ 'Foaf',
+ 'name',
+ 'http://xmlns.com/foaf/0.1/',
+ '[http://www.foaf-project.org/ Friend Of A Friend]',
+ 'Type:Text'
+ ]
+ ];
+
+ #2 mbox_sha1sum
+ $provider[] = [
+ " http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]\n mbox_sha1sum|Type:Text\n",
+ 'Foaf:mbox_sha1sum',
+ [
+ 'Foaf',
+ 'mbox_sha1sum',
+ 'http://xmlns.com/foaf/0.1/',
+ '[http://www.foaf-project.org/ Friend Of A Friend]',
+ 'Type:Text'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/MonolingualTextValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/MonolingualTextValueParserTest.php
new file mode 100644
index 00000000..2025d29a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/MonolingualTextValueParserTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\MonolingualTextValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueParsers\MonolingualTextValueParser',
+ new MonolingualTextValueParser()
+ );
+ }
+
+ /**
+ * @dataProvider fullStringProvider
+ */
+ public function testFullParsableString( $value, $expectedText, $expectedLanguageCode ) {
+
+ $instance = new MonolingualTextValueParser();
+ list( $text, $languageCode ) = $instance->parse( $value );
+
+ $this->assertEquals(
+ $expectedText,
+ $text
+ );
+
+ $this->assertEquals(
+ $expectedLanguageCode,
+ $languageCode
+ );
+ }
+
+ public function testParsableStringWithMissingLanguageCode() {
+
+ $instance = new MonolingualTextValueParser();
+ list( $text, $languageCode ) = $instance->parse( 'FooBar' );
+
+ $this->assertEquals(
+ 'FooBar',
+ $text
+ );
+ }
+
+ public function fullStringProvider() {
+
+ $provider[] = [
+ 'Foo@EN',
+ 'Foo',
+ 'en'
+ ];
+
+ $provider[] = [
+ 'testWith@example.org@zh-hans',
+ 'testWith@example.org',
+ 'zh-Hans'
+ ];
+
+ $provider[] = [
+ [ 'EN' =>'Foo' ],
+ 'Foo',
+ 'en'
+ ];
+
+ $provider[] = [
+ [ 'EN', 'Foo' ],
+ 'Foo',
+ ''
+ ];
+
+ $provider[] = [
+ [ 'EN', [] ],
+ '',
+ ''
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/PropertyValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/PropertyValueParserTest.php
new file mode 100644
index 00000000..ef475a98
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/PropertyValueParserTest.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\ValueParsers\PropertyValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\PropertyValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyValueParser::class,
+ new PropertyValueParser()
+ );
+ }
+
+ /**
+ * @dataProvider nameProvider
+ */
+ public function testParse( $value, $invalidCharacterList, $expectedPropertyName, $expectedInverse ) {
+
+ $instance = new PropertyValueParser();
+ $instance->setInvalidCharacterList(
+ $invalidCharacterList
+ );
+
+ list( $propertyName, $capitalizedName, $inverse ) = $instance->parse( $value );
+
+ $this->assertSame(
+ $expectedPropertyName,
+ $propertyName
+ );
+
+ $this->assertSame(
+ $expectedInverse,
+ $inverse
+ );
+ }
+
+ public function testEnforceFirstCharUpperCase() {
+
+ $instance = new PropertyValueParser();
+ $instance->isCapitalLinks( false );
+ $instance->reqCapitalizedFirstChar( true );
+
+ list( $propertyName, $capitalizedName, $inverse ) = $instance->parse( 'foo' );
+
+ $this->assertSame(
+ 'foo',
+ $propertyName
+ );
+
+ $this->assertSame(
+ 'Foo',
+ $capitalizedName
+ );
+ }
+
+ public function nameProvider() {
+
+ $provider[] = [
+ 'Foo',
+ [],
+ 'Foo',
+ false
+ ];
+
+ $provider[] = [
+ '[ Foo',
+ [],
+ 'Foo',
+ false
+ ];
+
+ $provider[] = [
+ '[Foo',
+ [],
+ 'Foo',
+ false
+ ];
+
+ $provider[] = [
+ 'Foo ]',
+ [],
+ 'Foo',
+ false
+ ];
+
+ $provider[] = [
+ 'Foo]',
+ [],
+ 'Foo',
+ false
+ ];
+
+ $provider[] = [
+ '-Foo',
+ [],
+ 'Foo',
+ true
+ ];
+
+ $provider[] = [
+ '<Foo>',
+ [ '<', '>' ],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ '<Foo-<Bar>',
+ [ '<', '>' ],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ 'Foo-<Bar',
+ [ '<', '>' ],
+ 'Foo-',
+ false
+ ];
+
+ $provider[] = [
+ 'Foo#Bar',
+ [],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ 'Foo|Bar',
+ [ '|' ],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ 'Foo.Bar',
+ [],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ 'Foo[Bar',
+ [ '[', ']' ],
+ null,
+ null
+ ];
+
+ $provider[] = [
+ 'Foo]Bar',
+ [ '[', ']' ],
+ null,
+ null
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/TimeValueParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/TimeValueParserTest.php
new file mode 100644
index 00000000..732097a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueParsers/TimeValueParserTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueParsers;
+
+use SMW\DataValues\Time\Components;
+use SMW\DataValues\ValueParsers\TimeValueParser;
+
+/**
+ * @covers \SMW\DataValues\ValueParsers\TimeValueParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TimeValueParserTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TimeValueParser::class,
+ new TimeValueParser()
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testParse( $value, $expected, $errors ) {
+
+ $instance = new TimeValueParser();
+
+ $this->assertEquals(
+ new Components( $expected ),
+ $instance->parse( $value )
+ );
+
+ $this->assertEquals(
+ $errors,
+ $instance->getErrors()
+ );
+ }
+
+ public function valueProvider() {
+
+ yield [
+ '1 Jan 1970',
+ [
+ 'value' => '1 Jan 1970',
+ 'datecomponents' => [
+ '1', 'Jan', '1970'
+ ],
+ 'calendarmodel' => false,
+ 'era' => false,
+ 'hours' => false,
+ 'minutes' => false,
+ 'seconds' => false,
+ 'microseconds' => false,
+ 'timeoffset' => 0,
+ 'timezone' => false
+ ],
+ []
+ ];
+
+ // JD value
+ yield [
+ '2458119.500000',
+ [
+ 'value' => '2458119.500000',
+ 'datecomponents' => [
+ '2458119', '500000'
+ ],
+ 'calendarmodel' => 'JD',
+ 'era' => false,
+ 'hours' => false,
+ 'minutes' => false,
+ 'seconds' => false,
+ 'microseconds' => false,
+ 'timeoffset' => 0,
+ 'timezone' => false
+ ],
+ []
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/AllowsListConstraintValueValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/AllowsListConstraintValueValidatorTest.php
new file mode 100644
index 00000000..20ef32e1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/AllowsListConstraintValueValidatorTest.php
@@ -0,0 +1,411 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueValidators;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ValueValidators\AllowsListConstraintValueValidator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\ValueValidators\AllowsListConstraintValueValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class AllowsListConstraintValueValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $allowsListValueParser;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->allowsListValueParser = $this->getMockBuilder( '\SMW\DataValues\ValueParsers\AllowsListValueParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ AllowsListConstraintValueValidator::class,
+ new AllowsListConstraintValueValidator( $this->allowsListValueParser, $this->propertySpecificationLookup )
+ );
+ }
+
+ public function testIsAllowedValueOnTheValidatedDataValue() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'ValidAllowedValue' );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'Foo' ) ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedListValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Foo' ) ) );
+
+ $instance = new AllowsListConstraintValueValidator(
+ $this->allowsListValueParser,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertFalse(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function testIsNotAllowedValueOnTheValidatedDataValue() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'InvalidAllowedValue' );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'NOTALLOWED' ) ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedListValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Foo' ) ) );
+
+ $instance = new AllowsListConstraintValueValidator(
+ $this->allowsListValueParser,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertTrue(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function testIsNotAllowedValueWithShortenedLongList() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'InvalidAllowedValue' );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue(
+ [
+ $this->dataItemFactory->newDIBlob( 'VAL1' ),
+ $this->dataItemFactory->newDIBlob( 'VAL2' ),
+ $this->dataItemFactory->newDIBlob( 'VAL2' ),
+ $this->dataItemFactory->newDIBlob( 'VAL3' ),
+ $this->dataItemFactory->newDIBlob( 'VAL4' ),
+ $this->dataItemFactory->newDIBlob( 'VAL5' ),
+ $this->dataItemFactory->newDIBlob( 'VAL6' ),
+ $this->dataItemFactory->newDIBlob( 'VAL7' ),
+ $this->dataItemFactory->newDIBlob( 'VAL8' ),
+ $this->dataItemFactory->newDIBlob( 'VAL9' ),
+ $this->dataItemFactory->newDIBlob( 'VAL0' ),
+ $this->dataItemFactory->newDIBlob( 'VAL11' ) ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedListValues' )
+ ->will( $this->returnValue( [ ] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Foo' ) ) );
+
+ $instance = new AllowsListConstraintValueValidator(
+ $this->allowsListValueParser,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertEquals(
+ [
+ 'a3cb94437b0f3619eaebd527123a4558' => '[8,"smw-datavalue-constraint-error-allows-value-list","","VAL1, VAL2, VAL3, VAL4, VAL5, VAL6, VAL7, VAL8, VAL9, VAL0, ...","InvalidAllowedValue"]'
+ ],
+ $dataValue->getErrors()
+ );
+ }
+
+ public function testIsAllowedValueFromCombinedList() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'ValidAllowedValue' );
+
+ $this->allowsListValueParser->expects( $this->any() )
+ ->method( 'parse' )
+ ->will( $this->onConsecutiveCalls( [ 'Foo' => 'foo' ], [ 'Bar' => 'bar' ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedListValues' )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( 'list_foo' ),
+ $this->dataItemFactory->newDIBlob( 'list_bar' ) ] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $instance = new AllowsListConstraintValueValidator(
+ $this->allowsListValueParser,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertFalse(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ /**
+ * @dataProvider rangeProvider
+ */
+ public function testCompareNumberRange( $allowsValue, $dataItem, $expected ) {
+
+ $property = $this->dataItemFactory->newDIProperty( 'InvalidAllowedValue' );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue( $allowsValue ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedListValues' )
+ ->will( $this->returnValue( [ ] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID', 'getWikiValue' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_num' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getWikiValue' )
+ ->will( $this->returnValue( $dataItem->getNumber() ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItem ) );
+
+ $instance = new AllowsListConstraintValueValidator(
+ $this->allowsListValueParser,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertEquals(
+ $expected,
+ $dataValue->getErrors()
+ );
+ }
+
+ public function rangeProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ // Combinations do test that the order for a range and a discrete value
+ // doesn't matter
+
+ // Range
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '>1' ),
+ $dataItemFactory->newDIBlob( '<4' )
+ ],
+ $dataItemFactory->newDINumber( 3 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '>1' ),
+ $dataItemFactory->newDIBlob( '5' ),
+ $dataItemFactory->newDIBlob( '<4' )
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '<4' ),
+ $dataItemFactory->newDIBlob( '>1' ),
+ $dataItemFactory->newDIBlob( '5' ),
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '5' ),
+ $dataItemFactory->newDIBlob( '<4' ),
+ $dataItemFactory->newDIBlob( '>1' ),
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '>1' ),
+ $dataItemFactory->newDIBlob( '<4' )
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ [
+ '9b7a08f3296c976852260443ff290b16' => '[8,"smw-datavalue-constraint-error-allows-value-range","5","<4","InvalidAllowedValue"]'
+ ]
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '<4' ),
+ $dataItemFactory->newDIBlob( '>1' )
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ [
+ '28e3bb64ee1452b03e0e2afac787c010' => '[8,"smw-datavalue-constraint-error-allows-value-range","5","<4, >1","InvalidAllowedValue"]'
+ ]
+ ];
+
+ // Bounds
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '1...10' )
+ ],
+ $dataItemFactory->newDINumber( 5 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '1...10' )
+ ],
+ $dataItemFactory->newDINumber( 10 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '1...10' ),
+ $dataItemFactory->newDIBlob( '15' )
+ ],
+ $dataItemFactory->newDINumber( 15 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '1...10' ),
+ $dataItemFactory->newDIBlob( '50...100' )
+ ],
+ $dataItemFactory->newDINumber( 100 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '<200' ),
+ $dataItemFactory->newDIBlob( '1...10' ),
+ $dataItemFactory->newDIBlob( '15' ),
+ $dataItemFactory->newDIBlob( '>100' )
+ ],
+ $dataItemFactory->newDINumber( 101 ),
+ []
+ ];
+
+ yield [
+ [
+ $dataItemFactory->newDIBlob( '1...10' )
+ ],
+ $dataItemFactory->newDINumber( 15 ),
+ [
+ '437bd520d612acf0ccb7f460c2784275' => '[8,"smw-datavalue-constraint-error-allows-value-range","15","1...10","InvalidAllowedValue"]'
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/CompoundConstraintValueValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/CompoundConstraintValueValidatorTest.php
new file mode 100644
index 00000000..3b3a7054
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/CompoundConstraintValueValidatorTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueValidators;
+
+use SMW\DataValues\ValueValidators\CompoundConstraintValueValidator;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\DataValues\ValueValidators\CompoundConstraintValueValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CompoundConstraintValueValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueValidators\CompoundConstraintValueValidator',
+ new CompoundConstraintValueValidator()
+ );
+ }
+
+ public function testHasConstraintViolation() {
+
+ $constraintValueValidator = $this->getMockBuilder( '\SMW\DataValues\ValueValidators\ConstraintValueValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $constraintValueValidator->expects( $this->once() )
+ ->method( 'validate' )
+ ->with( $this->equalTo( 'Foo' ) );
+
+ $constraintValueValidator->expects( $this->once() )
+ ->method( 'hasConstraintViolation' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new CompoundConstraintValueValidator();
+ $instance->registerConstraintValueValidator( $constraintValueValidator );
+
+ $instance->validate( 'Foo' );
+
+ $this->assertTrue(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function testMissingConstraintValueValidatorRegThrowsException() {
+
+ $instance = new CompoundConstraintValueValidator();
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->validate( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PatternConstraintValueValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PatternConstraintValueValidatorTest.php
new file mode 100644
index 00000000..257043c7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PatternConstraintValueValidatorTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueValidators;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+use SMW\DataValues\ValueValidators\PatternConstraintValueValidator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\ValueValidators\PatternConstraintValueValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PatternConstraintValueValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $mediaWikiNsContentReader;
+ private $allowsPatternValueParser;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'MediaWikiNsContentReader', $this->mediaWikiNsContentReader );
+
+ $this->allowsPatternValueParser = new AllowsPatternValueParser(
+ $this->mediaWikiNsContentReader
+ );
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueValidators\PatternConstraintValueValidator',
+ new PatternConstraintValueValidator( $this->allowsPatternValueParser )
+ );
+ }
+
+ /**
+ * @dataProvider allowedPatternProvider
+ */
+ public function testPatternUsingMockedDataValue( $allowedPattern, $testString, $expectedConstraintViolation ) {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Has allowed pattern' );
+
+ $this->mediaWikiNsContentReader->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $allowedPattern ) );
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getAllowedPatternBy' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getTypeID' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( $testString ) ) );
+
+ $instance = new PatternConstraintValueValidator(
+ $this->allowsPatternValueParser
+ );
+
+ $dataValue->setOption( 'smwgDVFeatures', SMW_DV_PVAP );
+
+ $instance->validate( $dataValue );
+
+ $this->assertEquals(
+ $expectedConstraintViolation,
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function allowedPatternProvider() {
+
+ $provider[] = [
+ " \nFoo|^(Bar|Foo bar)$/e\n",
+ 'Foo bar',
+ false
+ ];
+
+ #1 valid
+ $provider[] = [
+ " \nFoo|(ev\d{7}\d{4})|((tt|nm|ch|co|ev)\d{7})\n",
+ 'tt0042876',
+ false
+ ];
+
+ #2 uses '/\'
+ $provider[] = [
+ " \nFoo|(ev\d{7}/\d{4})|((tt|nm|ch|co|ev)\d{7})\n",
+ 'tt0042876',
+ false
+ ];
+
+ #3 "Compilation failed: missing )", suppress error
+ $provider[] = [
+ " \nFoo|(ev\d{7}\d{4})|((tt|nm|ch|co|ev)\d{7}\n",
+ 'Foo',
+ true
+ ];
+
+ #4
+ $provider[] = [
+ " \nFoo|\d{8}\n",
+ '00564222',
+ false
+ ];
+
+ #5
+ $provider[] = [
+ " \nFoo|/\d{8}\n",
+ '00564222',
+ false
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PropertySpecificationConstraintValueValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PropertySpecificationConstraintValueValidatorTest.php
new file mode 100644
index 00000000..13e071f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/PropertySpecificationConstraintValueValidatorTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueValidators;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\DataValues\ValueValidators\PropertySpecificationConstraintValueValidator;
+
+/**
+ * @covers \SMW\DataValues\ValueValidators\PropertySpecificationConstraintValueValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationConstraintValueValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\DataValues\ValueValidators\PropertySpecificationConstraintValueValidator',
+ new PropertySpecificationConstraintValueValidator()
+ );
+ }
+
+ public function testHasNoConstraintViolationOnNonRelatedValue() {
+
+ $instance = new PropertySpecificationConstraintValueValidator();
+ $instance->validate( 'Foo' );
+
+ $this->assertFalse(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function testHasNoConstraintViolationOnDisabledPreferredLabelPropertyButWithError() {
+
+ $dataValue = $this->dataValueFactory->newDataValueByProperty(
+ $this->dataItemFactory->newDIProperty( '_PPLB' )
+ );
+
+ $dataValue->setContextPage(
+ $this->dataItemFactory->newDIWikiPage( 'Foo', SMW_NS_PROPERTY )
+ );
+
+ $dataValue->setOption( 'smwgDVFeatures', ~SMW_DV_PPLB );
+
+ $instance = new PropertySpecificationConstraintValueValidator();
+ $instance->validate( $dataValue );
+
+ $this->assertFalse(
+ $instance->hasConstraintViolation()
+ );
+
+ $this->assertNotEmpty(
+ $dataValue->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/UniquenessConstraintValueValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/UniquenessConstraintValueValidatorTest.php
new file mode 100644
index 00000000..ba1c43ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DataValues/ValueValidators/UniquenessConstraintValueValidatorTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW\Tests\DataValues\ValueValidators;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ValueValidators\UniquenessConstraintValueValidator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\DataValues\ValueValidators\UniquenessConstraintValueValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintValueValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+ private $propertySpecificationLookup;
+ private $store;
+ private $entityValueUniquenessConstraintChecker;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->entityValueUniquenessConstraintChecker = $this->getMockBuilder( '\SMW\SQLStore\EntityValueUniquenessConstraintChecker' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'service' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'service' )
+ ->with( $this->equalTo( 'EntityValueUniquenessConstraintChecker' ) )
+ ->will( $this->returnValue( $this->entityValueUniquenessConstraintChecker ) );
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ UniquenessConstraintValueValidator::class,
+ new UniquenessConstraintValueValidator( $this->store, $this->propertySpecificationLookup )
+ );
+ }
+
+ public function testCanNotValidateOnNull() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getContextPage' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( null ) );
+
+ $dataValue->expects( $this->never() )
+ ->method( 'getProperty' );
+
+ $dataValue->expects( $this->never() )
+ ->method( 'getDataItem' );
+
+ $dataValue->setOption(
+ 'smwgDVFeatures',
+ SMW_DV_PVUC
+ );
+
+ $instance = new UniquenessConstraintValueValidator(
+ $this->store,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+ }
+
+ public function testValidate_HasNoConstraintViolation() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'ValidAllowedValue' );
+
+ $this->entityValueUniquenessConstraintChecker->expects( $this->atLeastOnce() )
+ ->method( 'checkConstraint' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'hasUniquenessConstraint' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getContextPage' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIWikiPage( 'UV', NS_MAIN ) ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Foo' ) ) );
+
+ $dataValue->setOption(
+ 'smwgDVFeatures',
+ SMW_DV_PVUC
+ );
+
+ $instance = new UniquenessConstraintValueValidator(
+ $this->store,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->clear();
+ $instance->validate( $dataValue );
+
+ $this->assertFalse(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+ public function testValidate_HasConstraintViolation() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'ValidAllowedValue_2' );
+
+ $this->entityValueUniquenessConstraintChecker->expects( $this->atLeastOnce() )
+ ->method( 'checkConstraint' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ) ] ) );
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'hasUniquenessConstraint' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getProperty', 'getDataItem', 'getContextPage', 'addErrorMsg' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIWikiPage( 'UV', NS_MAIN ) ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $property ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'addErrorMsg' )
+ ->with( $this->equalTo( [ 'smw-datavalue-uniqueness-constraint-error', $property->getLabel(), '...', 'Foo' ] ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Foo' ) ) );
+
+ $dataValue->setOption(
+ 'smwgDVFeatures',
+ SMW_DV_PVUC
+ );
+
+ $instance = new UniquenessConstraintValueValidator(
+ $this->store,
+ $this->propertySpecificationLookup
+ );
+
+ $instance->validate( $dataValue );
+
+ $this->assertTrue(
+ $instance->hasConstraintViolation()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DefinesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DefinesTest.php
new file mode 100644
index 00000000..1982c1af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/DefinesTest.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DefinesTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider constantsDataProvider
+ */
+ public function testConstants( $constant, $expected ) {
+ $this->assertEquals( $expected, $constant );
+ }
+
+ public function constantsDataProvider() {
+ return [
+ [ SMW_HEADERS_SHOW, 2 ],
+ [ SMW_HEADERS_PLAIN, 1 ],
+ [ SMW_HEADERS_HIDE, 0 ],
+ [ SMW_OUTPUT_HTML, 1 ],
+ [ SMW_OUTPUT_WIKI, 2 ],
+ [ SMW_OUTPUT_FILE, 3 ],
+ [ SMW_FACTBOX_HIDDEN, 1 ],
+ [ SMW_FACTBOX_SPECIAL, 2 ],
+ [ SMW_FACTBOX_NONEMPTY, 3 ],
+ [ SMW_FACTBOX_SHOWN, 5 ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializerTest.php
new file mode 100644
index 00000000..80c58774
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/DispatchingDescriptionDeserializerTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\Deserializers\DVDescriptionDeserializer\DispatchingDescriptionDeserializer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\DispatchingDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class DispatchingDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\DispatchingDescriptionDeserializer',
+ new DispatchingDescriptionDeserializer()
+ );
+ }
+
+ public function testGetDescriptionDeserializerForMatchableDataValue() {
+
+ $descriptionDeserializer = $this->getMockBuilder( '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $descriptionDeserializer->expects( $this->once() )
+ ->method( 'isDeserializerFor' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDescriptionDeserializer();
+ $instance->addDescriptionDeserializer( $descriptionDeserializer );
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testGetDefaultDescriptionDeserializerForMatchableDataValue() {
+
+ $descriptionDeserializer = $this->getMockBuilder( '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $descriptionDeserializer->expects( $this->once() )
+ ->method( 'isDeserializerFor' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDescriptionDeserializer();
+ $instance->addDefaultDescriptionDeserializer( $descriptionDeserializer );
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testTryToGetDescriptionDeserializerForNonDispatchableDataValueThrowsException() {
+
+ $descriptionDeserializer = $this->getMockBuilder( '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $descriptionDeserializer->expects( $this->once() )
+ ->method( 'isDeserializerFor' )
+ ->will( $this->returnValue( false ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DispatchingDescriptionDeserializer();
+ $instance->addDescriptionDeserializer( $descriptionDeserializer );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getDescriptionDeserializerBy( $dataValue );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializerTest.php
new file mode 100644
index 00000000..3d3617f6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/MonolingualTextValueDescriptionDeserializerTest.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\DataValueFactory;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\Deserializers\DVDescriptionDeserializer\MonolingualTextValueDescriptionDeserializer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\MonolingualTextValueDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MonolingualTextValueDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\MonolingualTextValueDescriptionDeserializer',
+ new MonolingualTextValueDescriptionDeserializer()
+ );
+ }
+
+ public function testIsDeserializerForTimeValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMW\DataValues\MonolingualTextValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new MonolingualTextValueDescriptionDeserializer();
+
+ $this->assertTrue(
+ $instance->isDeserializerFor( $dataValue )
+ );
+ }
+
+ public function testNonStringThrowsException() {
+
+ $recordValue = $this->getMockBuilder( '\SMW\DataValues\MonolingualTextValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MonolingualTextValueDescriptionDeserializer();
+ $instance->setDataValue( $recordValue );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->deserialize( [] );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testDeserialize( $value, $decription, $queryString, $dvFeatures ) {
+
+ $monolingualTextValue = DataValueFactory::getInStance()->newDataValueByType(
+ MonolingualTextValue::TYPE_ID
+ );
+
+ $monolingualTextValue->setOption( 'smwgDVFeatures', $dvFeatures );
+
+ $instance = new MonolingualTextValueDescriptionDeserializer();
+ $instance->setDataValue( $monolingualTextValue );
+
+ $this->assertInstanceOf(
+ $decription,
+ $instance->deserialize( $value )
+ );
+
+ $this->assertEquals(
+ $queryString,
+ $instance->deserialize( $value )->getQueryString()
+ );
+ }
+
+ public function valueProvider() {
+
+ #0
+ $provider[] = [
+ 'Jan;1970',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::Jan;1970]] [[Language code::+]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #1
+ $provider[] = [
+ 'Jan;1970',
+ '\SMW\Query\Language\SomeProperty',
+ '[[Text::Jan;1970]]',
+ SMW_DV_NONE
+ ];
+
+ #2
+ $provider[] = [
+ 'Jan@en',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::Jan]] [[Language code::en]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #3
+ $provider[] = [
+ '@en',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::+]] [[Language code::en]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #4
+ $provider[] = [
+ '@EN',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::+]] [[Language code::en]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #5
+ $provider[] = [
+ '@~zh*',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::+]] [[Language code::~zh*]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #6
+ $provider[] = [
+ '?',
+ '\SMW\Query\Language\Conjunction',
+ '[[Text::+]] [[Language code::+]]',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ #7
+ $provider[] = [
+ '?',
+ '\SMW\Query\Language\SomeProperty',
+ '[[Text::+]]',
+ SMW_DV_NONE
+ ];
+
+ #8
+ $provider[] = [
+ '',
+ '\SMW\Query\Language\ThingDescription',
+ '',
+ SMW_DV_MLTV_LCODE
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializerTest.php
new file mode 100644
index 00000000..e40e2d1a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/NumberValueDescriptionDeserializerTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\Deserializers\DVDescriptionDeserializer\NumberValueDescriptionDeserializer;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\NumberValueDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NumberValueDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NumberValueDescriptionDeserializer::class,
+ new NumberValueDescriptionDeserializer()
+ );
+ }
+
+ public function testIsDeserializerForNumberValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new NumberValueDescriptionDeserializer();
+
+ $this->assertTrue(
+ $instance->isDeserializerFor( $dataValue )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testDeserialize( $value, $decription ) {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $numberValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $numberValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( new \SMWDINumber( 42 ) ) );
+
+ $numberValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( new \SMW\DIProperty( 'Foo' ) ) );
+
+ $instance = new NumberValueDescriptionDeserializer();
+ $instance->setDataValue( $numberValue );
+
+ $this->assertInstanceOf(
+ $decription,
+ $instance->deserialize( $value )
+ );
+ }
+
+ public function testInvalidNumberValueReturnsThingDescription() {
+
+ $numberValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $numberValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new NumberValueDescriptionDeserializer();
+ $instance->setDataValue( $numberValue );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Language\ThingDescription',
+ $instance->deserialize( 'Foo' )
+ );
+ }
+
+ public function valueProvider() {
+
+ $provider[] = [
+ '42',
+ '\SMW\Query\Language\ValueDescription'
+ ];
+
+ $provider[] = [
+ '~42',
+ '\SMW\Query\Language\Conjunction'
+ ];
+
+ $provider[] = [
+ '~*42*',
+ '\SMW\Query\Language\Conjunction'
+ ];
+
+ $provider[] = [
+ '~-42',
+ '\SMW\Query\Language\Conjunction'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializerTest.php
new file mode 100644
index 00000000..6589fb59
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/RecordValueDescriptionDeserializerTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\Deserializers\DVDescriptionDeserializer\RecordValueDescriptionDeserializer;
+use SMW\DIProperty;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\RecordValueDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class RecordValueDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\RecordValueDescriptionDeserializer',
+ new RecordValueDescriptionDeserializer()
+ );
+ }
+
+ public function testIsDeserializerForTimeValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWRecordValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new RecordValueDescriptionDeserializer();
+
+ $this->assertTrue(
+ $instance->isDeserializerFor( $dataValue )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testDeserialize( $value, $propertyDataItems, $decription ) {
+
+ $recordValue = $this->getMockBuilder( '\SMWRecordValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $recordValue->expects( $this->any() )
+ ->method( 'getValuesFromString' )
+ ->with( $this->stringContains( $value ) )
+ ->will( $this->returnCallback( function( $value ) {
+ return explode(';', $value );
+ } ) );
+
+ $recordValue->expects( $this->any() )
+ ->method( 'getPropertyDataItems' )
+ ->will( $this->returnValue( $propertyDataItems ) );
+
+ $instance = new RecordValueDescriptionDeserializer();
+ $instance->setDataValue( $recordValue );
+
+ $this->assertInstanceOf(
+ $decription,
+ $instance->deserialize( $value )
+ );
+ }
+
+ public function testInvalidRecordValueReturnsThingDescription() {
+
+ $recordValue = $this->getMockBuilder( '\SMWRecordValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $recordValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $recordValue->expects( $this->any() )
+ ->method( 'getPropertyDataItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new RecordValueDescriptionDeserializer();
+ $instance->setDataValue( $recordValue );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Language\ThingDescription',
+ $instance->deserialize( 'Foo' )
+ );
+ }
+
+ public function testNonStringThrowsException() {
+
+ $recordValue = $this->getMockBuilder( '\SMWRecordValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RecordValueDescriptionDeserializer();
+ $instance->setDataValue( $recordValue );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->deserialize( [] );
+ }
+
+ public function valueProvider() {
+
+ $provider[] = [
+ 'Jan;1970',
+ [ new DIProperty( 'Foo' ) ],
+ '\SMW\Query\Language\SomeProperty'
+ ];
+
+ $provider[] = [
+ 'Jan;1970',
+ [ new DIProperty( 'Foo' ), new DIProperty( 'Bar' ) ],
+ '\SMW\Query\Language\Conjunction'
+ ];
+
+ $provider[] = [
+ '?',
+ [ new DIProperty( 'Foo' ), new DIProperty( 'Bar' ) ],
+ '\SMW\Query\Language\ThingDescription'
+ ];
+
+ $provider[] = [
+ '',
+ [],
+ '\SMW\Query\Language\ThingDescription'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializerTest.php
new file mode 100644
index 00000000..9b1820a7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/SomeValueDescriptionDeserializerTest.php
@@ -0,0 +1,201 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\ApplicationFactory;
+use SMW\Deserializers\DVDescriptionDeserializer\SomeValueDescriptionDeserializer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\SomeValueDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SomeValueDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = ApplicationFactory::getInstance()->getDataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\SomeValueDescriptionDeserializer',
+ new SomeValueDescriptionDeserializer()
+ );
+ }
+
+ public function testIsDeserializerForDataValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new SomeValueDescriptionDeserializer();
+
+ $this->assertTrue(
+ $instance->isDeserializerFor( $dataValue )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testDeserialize( $value, $decription ) {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getDataItem', 'getProperty', 'setUserValue' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->once() )
+ ->method( 'setUserValue' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( false ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue($this->dataItemFactory->newDITime( 1, '1970' ) ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $instance = new SomeValueDescriptionDeserializer();
+ $instance->setDataValue( $dataValue );
+
+ $this->assertInstanceOf(
+ $decription,
+ $instance->deserialize( $value )
+ );
+ }
+
+ /**
+ * @dataProvider likeNotLikeProvider
+ */
+ public function testDeserializeForLikeNotLike( $value ) {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'setUserValue' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->once() )
+ ->method( 'setUserValue' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( false ) );
+
+ $instance = new SomeValueDescriptionDeserializer();
+ $instance->setDataValue( $dataValue );
+
+ $instance->deserialize( $value );
+ }
+
+ public function testInvalidDataValueRetunsThingDescription() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new SomeValueDescriptionDeserializer();
+ $instance->setDataValue( $dataValue );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Language\ThingDescription',
+ $instance->deserialize( 'Foo' )
+ );
+ }
+
+ public function testNonStringThrowsException() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new SomeValueDescriptionDeserializer();
+ $instance->setDataValue( $dataValue );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->deserialize( [] );
+ }
+
+ public function testWikiPageValueOnNonMainNamespace() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getDataItem', 'getProperty', 'setUserValue' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'setUserValue' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( false ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIWikiPage( '~Foo', NS_HELP ) ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $instance = new SomeValueDescriptionDeserializer();
+ $instance->setDataValue( $dataValue );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Language\Conjunction',
+ $instance->deserialize( 'Help:~Foo' )
+ );
+ }
+
+ public function valueProvider() {
+
+ $provider[] = [
+ 'Foo',
+ '\SMW\Query\Language\ValueDescription'
+ ];
+
+ return $provider;
+ }
+
+
+ public function likeNotLikeProvider() {
+
+ $provider[] = [
+ '~Foo'
+ ];
+
+ $provider[] = [
+ '!~Foo'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializerTest.php
new file mode 100644
index 00000000..12e80092
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializer/TimeValueDescriptionDeserializerTest.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace SMW\Tests\Deserializers\DVDescriptionDeserializer;
+
+use SMW\Deserializers\DVDescriptionDeserializer\TimeValueDescriptionDeserializer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializer\TimeValueDescriptionDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class TimeValueDescriptionDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\TimeValueDescriptionDeserializer',
+ new TimeValueDescriptionDeserializer()
+ );
+ }
+
+ public function testIsDeserializerForTimeValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new TimeValueDescriptionDeserializer();
+
+ $this->assertTrue(
+ $instance->isDeserializerFor( $dataValue )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testDeserialize( $value, $decription ) {
+
+ $timeValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $timeValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $timeValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( new \SMWDITime( 1, '1970' ) ) );
+
+ $timeValue->expects( $this->any() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( new \SMW\DIProperty( 'Foo' ) ) );
+
+ $instance = new TimeValueDescriptionDeserializer();
+ $instance->setDataValue( $timeValue );
+
+ $this->assertInstanceOf(
+ $decription,
+ $instance->deserialize( $value )
+ );
+ }
+
+ public function testInvalidTimeValueReturnsThingDescription() {
+
+ $timeValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $timeValue->expects( $this->any() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new TimeValueDescriptionDeserializer();
+ $instance->setDataValue( $timeValue );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Language\ThingDescription',
+ $instance->deserialize( 'Foo' )
+ );
+ }
+
+ public function testNonStringThrowsException() {
+
+ $timeValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new TimeValueDescriptionDeserializer();
+ $instance->setDataValue( $timeValue );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->deserialize( [] );
+ }
+
+ public function valueProvider() {
+
+ $provider[] = [
+ 'Jan 1970',
+ '\SMW\Query\Language\ValueDescription'
+ ];
+
+ $provider[] = [
+ '~Jan 1970',
+ '\SMW\Query\Language\Conjunction'
+ ];
+
+ $provider[] = [
+ '!~Jan 1970',
+ '\SMW\Query\Language\Disjunction'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializerRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializerRegistryTest.php
new file mode 100644
index 00000000..226402d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/DVDescriptionDeserializerRegistryTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\Tests\Deserializers;
+
+use SMW\Deserializers\DVDescriptionDeserializerRegistry;
+
+/**
+ * @covers \SMW\Deserializers\DVDescriptionDeserializerRegistry
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class DVDescriptionDeserializerRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ DVDescriptionDeserializerRegistry::getInstance()->clear();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $dispatchingDescriptionDeserializer = $this->getMockBuilder( '\SMW\Deserializers\DVDescriptionDeserializer\DispatchingDescriptionDeserializer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializerRegistry',
+ new DVDescriptionDeserializerRegistry( $dispatchingDescriptionDeserializer )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializerRegistry',
+ DVDescriptionDeserializerRegistry::getInstance()
+ );
+ }
+
+ public function testCanConstructSomeValueDescriptionDeserializer() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DVDescriptionDeserializerRegistry();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\SomeValueDescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testCanConstructTimeValueDescriptionDeserializer() {
+
+ $dataValue = $this->getMockBuilder( '\SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DVDescriptionDeserializerRegistry();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\TimeValueDescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testCanConstructNumberValueDescriptionDeserializer() {
+
+ $dataValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DVDescriptionDeserializerRegistry();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\NumberValueDescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testCanConstructRecordValueDescriptionDeserializer() {
+
+ $dataValue = $this->getMockBuilder( '\SMWRecordValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DVDescriptionDeserializerRegistry();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\RecordValueDescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+ public function testRegisterAdditionalDescriptionDeserializer() {
+
+ $descriptionDeserializer = $this->getMockBuilder( '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $descriptionDeserializer->expects( $this->once() )
+ ->method( 'isDeserializerFor' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DVDescriptionDeserializerRegistry();
+ $instance->registerDescriptionDeserializer( $descriptionDeserializer );
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\DVDescriptionDeserializer\DescriptionDeserializer',
+ $instance->getDescriptionDeserializerBy( $dataValue )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/ExpDataDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/ExpDataDeserializerTest.php
new file mode 100644
index 00000000..4dba4fdb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/ExpDataDeserializerTest.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace SMW\Tests\Deserializers;
+
+use SMW\Deserializers\ExpDataDeserializer;
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Serializers\ExpDataSerializer;
+use SMWDIBlob as DIBlob;
+use SMWExpData as ExpData;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\ExpDataDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpDataDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\ExpDataDeserializer',
+ new ExpDataDeserializer()
+ );
+ }
+
+ public function testInvalidSerializerObjectThrowsException() {
+
+ $instance = new ExpDataDeserializer();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->deserialize( 'Foo' );
+ }
+
+ public function testVersionMismatchThrowsException() {
+
+ $instance = new ExpDataDeserializer();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->deserialize( [ 'version' => 0.2 ] );
+ }
+
+ /**
+ * @dataProvider expDataProvider
+ */
+ public function testDeserialize( $seralization, $expected ) {
+
+ $instance = new ExpDataDeserializer();
+
+ $this->assertEquals(
+ $expected,
+ $instance->deserialize( $seralization )
+ );
+ }
+
+ /**
+ * @dataProvider expDataProvider
+ */
+ public function testDeserializeToCompareHash( $seralization, $expected ) {
+
+ $instance = new ExpDataDeserializer();
+
+ $this->assertEquals(
+ $expected->getHash(),
+ $instance->deserialize( $seralization )->getHash()
+ );
+ }
+
+ public function expDataProvider() {
+
+ $serializier = new ExpDataSerializer();
+
+ #0
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $provider[] = [
+ $serializier->serialize( $expData ),
+ $expData
+ ];
+
+ #1
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $provider[] = [
+ $serializier->serialize( $expData ),
+ $expData
+ ];
+
+ #2 Nested
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', new DIBlob( 'SomeText' ) ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', new DIBlob( 'SomeOtherText' ) ) )
+ );
+
+ $provider[] = [
+ $serializier->serialize( $expData ),
+ $expData
+ ];
+
+ #2 Nested level 2+3
+
+ $expDataLevel2 = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', new DIBlob( 'SomeOtherText' ) )
+ );
+
+ $expDataLevel2->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', new DIBlob( 'SomeText' ) ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expDataLevel2->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', null ) ) // 3
+ );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', new DIBlob( 'SomeText' ) ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ $expDataLevel2
+ );
+
+ $provider[] = [
+ $serializier->serialize( $expData ),
+ $expData
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/SemanticDataDeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/SemanticDataDeserializerTest.php
new file mode 100644
index 00000000..b9c0dcda
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Deserializers/SemanticDataDeserializerTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW\Tests\Deserializers;
+
+use SMW\Deserializers\SemanticDataDeserializer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Deserializers\SemanticDataDeserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataDeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\SemanticDataDeserializer',
+ new SemanticDataDeserializer()
+ );
+ }
+
+ public function testDeserializerInvalidVersionThrowsException() {
+
+ $instance = new SemanticDataDeserializer();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+
+ $instance->deserialize(
+ [ 'version' => 'Foo' ]
+ );
+ }
+
+ public function testDeserializerForInvalidSubjectThrowsException() {
+
+ $instance = new SemanticDataDeserializer();
+
+ $this->setExpectedException( '\SMW\Exception\DataItemDeserializationException' );
+
+ $instance->deserialize(
+ [ 'subject' => '--#Foo' ]
+ );
+ }
+
+ public function testDeserializerForMissingSubjectThrowsException() {
+
+ $instance = new SemanticDataDeserializer();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->deserialize( [] );
+ }
+
+ public function testDeserializerForEmptyData() {
+
+ $instance = new SemanticDataDeserializer();
+
+ $this->assertInstanceOf(
+ 'SMW\SemanticData',
+ $instance->deserialize( [ 'subject' => 'Foo#0##' ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/ElasticClientTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/ElasticClientTaskHandlerTest.php
new file mode 100644
index 00000000..833638b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/ElasticClientTaskHandlerTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\Elastic\Admin;
+
+use SMW\Elastic\Admin\ElasticClientTaskHandler;
+use SMW\Elastic\Connection\DummyClient;
+
+/**
+ * @covers \SMW\Elastic\Admin\ElasticClientTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ElasticClientTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputFormatter;
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( new DummyClient() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ElasticClientTaskHandler::class,
+ new ElasticClientTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testIsTask() {
+
+ $task = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\TaskHandler' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getTask' ] )
+ ->getMockForAbstractClass();
+
+ $task->expects( $this->once() )
+ ->method( 'getTask' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new ElasticClientTaskHandler(
+ $this->outputFormatter,
+ [
+ $task
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->isTaskFor( 'Foo' )
+ );
+ }
+
+ public function testGetHtml_OnNoAvailableNodes() {
+
+ $this->outputFormatter->expects( $this->never() )
+ ->method( 'createSpecialPageLink' );
+
+ $instance = new ElasticClientTaskHandler(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+
+ $this->assertEmpty(
+ $instance->getHtml()
+ );
+ }
+
+ public function testGetHtml_OnAvailableNodes() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'createSpecialPageLink' );
+
+ $client = $this->getMockBuilder( '\SMW\Elastic\Connection\DummyClient' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $client->expects( $this->any() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $client ) );
+
+ $instance = new ElasticClientTaskHandler(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $store );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest_OnNoAvailableNodes() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'addParentLink' )
+ ->with( $this->equalTo( [ 'tab' => 'supplement' ] ) );
+
+ $instance = new ElasticClientTaskHandler(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+ $instance->handleRequest( $this->webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/IndicesInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/IndicesInfoProviderTest.php
new file mode 100644
index 00000000..6b49f410
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/IndicesInfoProviderTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\Admin;
+
+use SMW\Elastic\Admin\IndicesInfoProvider;
+use SMW\Elastic\Connection\DummyClient;
+
+/**
+ * @covers \SMW\Elastic\Admin\IndicesInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IndicesInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputFormatter;
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( new DummyClient() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IndicesInfoProvider::class,
+ new IndicesInfoProvider( $this->outputFormatter )
+ );
+ }
+
+ public function testGetTask() {
+
+ $instance = new IndicesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertEquals(
+ 'indices',
+ $instance->getSupplementTask()
+ );
+
+ $this->assertEquals(
+ 'elastic/indices',
+ $instance->getTask()
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new IndicesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'addParentLink' )
+ ->with( $this->equalTo( [ 'action' => 'elastic' ] ) );
+
+ $instance = new IndicesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+ $instance->handleRequest( $this->webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/MappingsInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/MappingsInfoProviderTest.php
new file mode 100644
index 00000000..b5bb8669
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/MappingsInfoProviderTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\Admin;
+
+use SMW\Elastic\Admin\MappingsInfoProvider;
+use SMW\Elastic\Connection\DummyClient;
+
+/**
+ * @covers \SMW\Elastic\Admin\MappingsInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class MappingsInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputFormatter;
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( new DummyClient() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ MappingsInfoProvider::class,
+ new MappingsInfoProvider( $this->outputFormatter )
+ );
+ }
+
+ public function testGetTask() {
+
+ $instance = new MappingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertEquals(
+ 'mappings',
+ $instance->getSupplementTask()
+ );
+
+ $this->assertEquals(
+ 'elastic/mappings',
+ $instance->getTask()
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new MappingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'addParentLink' )
+ ->with( $this->equalTo( [ 'action' => 'elastic' ] ) );
+
+ $instance = new MappingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+ $instance->handleRequest( $this->webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/NodesInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/NodesInfoProviderTest.php
new file mode 100644
index 00000000..00c54674
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/NodesInfoProviderTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\Admin;
+
+use SMW\Elastic\Admin\NodesInfoProvider;
+use SMW\Elastic\Connection\DummyClient;
+
+/**
+ * @covers \SMW\Elastic\Admin\NodesInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NodesInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputFormatter;
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( new DummyClient() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NodesInfoProvider::class,
+ new NodesInfoProvider( $this->outputFormatter )
+ );
+ }
+
+ public function testGetTask() {
+
+ $instance = new NodesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertEquals(
+ 'nodes',
+ $instance->getSupplementTask()
+ );
+
+ $this->assertEquals(
+ 'elastic/nodes',
+ $instance->getTask()
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new NodesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'addParentLink' )
+ ->with( $this->equalTo( [ 'action' => 'elastic' ] ) );
+
+ $instance = new NodesInfoProvider(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+ $instance->handleRequest( $this->webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/SettingsInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/SettingsInfoProviderTest.php
new file mode 100644
index 00000000..ea2f6fb1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Admin/SettingsInfoProviderTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\Admin;
+
+use SMW\Elastic\Admin\SettingsInfoProvider;
+use SMW\Elastic\Connection\DummyClient;
+
+/**
+ * @covers \SMW\Elastic\Admin\SettingsInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SettingsInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputFormatter;
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( new DummyClient() ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SettingsInfoProvider::class,
+ new SettingsInfoProvider( $this->outputFormatter )
+ );
+ }
+
+ public function testGetTask() {
+
+ $instance = new SettingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertEquals(
+ 'settings',
+ $instance->getSupplementTask()
+ );
+
+ $this->assertEquals(
+ 'elastic/settings',
+ $instance->getTask()
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new SettingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->once() )
+ ->method( 'addParentLink' )
+ ->with( $this->equalTo( [ 'action' => 'elastic' ] ) );
+
+ $instance = new SettingsInfoProvider(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+ $instance->handleRequest( $this->webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ConfigTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ConfigTest.php
new file mode 100644
index 00000000..e44945a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ConfigTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\Elastic;
+
+use SMW\Elastic\Config;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Config
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConfigTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Config::class,
+ new Config()
+ );
+ }
+
+ public function testLoadFromJSON() {
+
+ $instance = new Config();
+ $instance->set( 'Foo', '123' );
+ $instance->set( 'Bar', '456' );
+
+ $instance->loadFromJSON( json_encode( [ 'Foo' => 456 ] ) );
+
+ $this->assertEquals(
+ 456,
+ $instance->dotGet( 'Foo' )
+ );
+
+ $this->assertEquals(
+ '456',
+ $instance->dotGet( 'Bar' )
+ );
+
+ $instance->set( 'A', [ 'B' => [ 'C', 'D' ] ] );
+
+ $this->assertEquals(
+ [ 'C', 'D' ],
+ $instance->dotGet( 'A.B' )
+ );
+
+ $instance->loadFromJSON( json_encode( [ 'A.B' => 'C' ] ) );
+
+ $this->assertEquals(
+ [ 'C', 'D' ],
+ $instance->dotGet( 'A.B' )
+ );
+
+ $instance->loadFromJSON( json_encode( [ 'A' => [ 'B' => [ 'E' ] ] ] ) );
+
+ $this->assertEquals(
+ [ 'E' ],
+ $instance->dotGet( 'A.B' )
+ );
+
+ $instance->loadFromJSON( json_encode( [ 'A' => [ 'B' => 'C' ] ] ) );
+
+ $this->assertEquals(
+ 'C',
+ $instance->dotGet( 'A.B' )
+ );
+ }
+
+ public function testReadfile_InaccessibleFileThrowsException() {
+
+ $instance = new Config();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->readFile( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ClientTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ClientTest.php
new file mode 100644
index 00000000..a5a81a32
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ClientTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Tests\Elastic\Connection;
+
+use SMW\Elastic\Connection\Client;
+use SMW\Options;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Connection\Client
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ClientTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $elasticClient;
+ private $cache;
+
+ protected function setUp() {
+
+ if ( !class_exists( '\Elasticsearch\Client' ) ) {
+ $this->markTestSkipped( "elasticsearch-php dependency is not available." );
+ }
+
+ $this->elasticClient = $this->getMockBuilder( '\Elasticsearch\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Client::class,
+ new Client( $this->elasticClient )
+ );
+ }
+
+ public function testBulkOnIllegalArgumentErrorThrowsReplicationException() {
+
+ $options = new Options (
+ [
+ 'replication' => [
+ 'throw.exception.on.illegal.argument.error' => true
+ ]
+ ]
+ );
+
+ $response = '{"took":67,"errors":true,"items":[{"index":{"_index":"smw-data-test","_type":"data","_id":"14099","status":400,"error":{"type":"illegal_argument_exception","reason":"Limit of total fields [20] in index [smw-data-test] has been exceeded"}}}]}';
+
+ $this->elasticClient->expects( $this->once() )
+ ->method( 'bulk' )
+ ->will( $this->returnValue( json_decode( $response, true ) ) );
+
+ $instance = new Client(
+ $this->elasticClient,
+ $this->cache,
+ $options
+ );
+
+ $params = [
+ 'index' => 'foo'
+ ];
+
+ $this->setExpectedException( '\SMW\Elastic\Exception\ReplicationException' );
+ $instance->bulk( $params );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ConnectionProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ConnectionProviderTest.php
new file mode 100644
index 00000000..e446aeba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Connection/ConnectionProviderTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Tests\Elastic\Connection;
+
+use SMW\Elastic\Connection\ConnectionProvider;
+use SMW\Elastic\Connection\DummyClient;
+use SMW\Elastic\Connection\Client;
+use SMW\Options;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Connection\ConnectionProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConnectionProviderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $logger;
+ private $cache;
+
+ protected function setUp() {
+
+ $this->logger = $this->getMockBuilder( '\Psr\Log\LoggerInterface' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->options = new Options();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConnectionProvider::class,
+ new ConnectionProvider( $this->options, $this->cache )
+ );
+ }
+
+ public function testGetConnection_DummyClient() {
+
+ $options = new Options (
+ [
+ 'is.elasticstore' => false,
+ 'endpoints' => []
+ ]
+ );
+
+ $instance = new ConnectionProvider(
+ $options,
+ $this->cache
+ );
+
+ $instance->setLogger( $this->logger );
+
+ $this->assertInstanceOf(
+ DummyClient::class,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetConnection_Client() {
+
+ if ( !class_exists( '\Elasticsearch\ClientBuilder' ) ) {
+ $this->markTestSkipped( "elasticsearch-php dependency is not available." );
+ }
+
+ $options = new Options (
+ [
+ 'is.elasticstore' => true,
+ 'endpoints' => []
+ ]
+ );
+
+ $instance = new ConnectionProvider(
+ $options,
+ $this->cache
+ );
+
+ $instance->setLogger( $this->logger );
+
+ $this->assertInstanceOf(
+ Client::class,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetConnectionThrowsExceptionWhenNotInstalled() {
+
+ if ( class_exists( '\Elasticsearch\ClientBuilder' ) ) {
+ $this->markTestSkipped( "No exception thrown when ClientBuilder is available!" );
+ }
+
+ $options = new Options (
+ [
+ 'is.elasticstore' => true,
+ 'endpoints' => []
+ ]
+ );
+
+ $instance = new ConnectionProvider(
+ $options,
+ $this->cache
+ );
+
+ $this->setExpectedException( '\SMW\Elastic\Exception\ClientBuilderNotFoundException' );
+ $instance->getConnection();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ElasticFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ElasticFactoryTest.php
new file mode 100644
index 00000000..687b5c01
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/ElasticFactoryTest.php
@@ -0,0 +1,244 @@
+<?php
+
+namespace SMW\Tests\Elastic;
+
+use SMW\Elastic\ElasticFactory;
+
+/**
+ * @covers \SMW\Elastic\ElasticFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ElasticFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $outputFormatter;
+ private $conditionBuilder;
+ private $connection;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ElasticFactory::class,
+ new ElasticFactory()
+ );
+ }
+
+ public function testCanConstructConfig() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Config',
+ $instance->newConfig()
+ );
+ }
+
+ public function testCanConstructConnectionProvider() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Connection\ConnectionProvider',
+ $instance->newConnectionProvider()
+ );
+ }
+
+ public function testCanConstructIndexer() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Indexer\Indexer',
+ $instance->newIndexer( $this->store )
+ );
+ }
+
+ public function testCanConstructFileIndexer() {
+
+ $indexer = $this->getMockBuilder( '\SMW\Elastic\Indexer\Indexer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Indexer\FileIndexer',
+ $instance->newFileIndexer( $indexer )
+ );
+ }
+
+ public function testCanConstructRollover() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Indexer\Rollover',
+ $instance->newRollover( $this->connection )
+ );
+ }
+
+ public function testCanConstructBulk() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Indexer\Bulk',
+ $instance->newBulk( $this->connection )
+ );
+ }
+
+ public function testCanConstructQueryEngine() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->newQueryEngine( $this->store )
+ );
+ }
+
+ public function testCanConstructRebuilder() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Indexer\Rebuilder',
+ $instance->newRebuilder( $this->store )
+ );
+ }
+
+ public function testCanConstructInfoTaskHandler() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\Admin\ElasticClientTaskHandler',
+ $instance->newInfoTaskHandler( $this->store, $this->outputFormatter )
+ );
+ }
+
+ public function testCanConstructConceptDescriptionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter',
+ $instance->newConceptDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructSomePropertyInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter',
+ $instance->newSomePropertyInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructSomeValueInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\SomeValueInterpreter',
+ $instance->newSomeValueInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructClassDescriptionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter',
+ $instance->newClassDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructNamespaceDescriptionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter',
+ $instance->newNamespaceDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructValueDescriptionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter',
+ $instance->newValueDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructConjunctionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter',
+ $instance->newConjunctionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testCanConstructDisjunctionInterpreter() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Elastic\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter',
+ $instance->newDisjunctionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testOnEntityReferenceCleanUpComplete() {
+
+ $instance = new ElasticFactory();
+
+ $this->assertTrue(
+ $instance->onEntityReferenceCleanUpComplete( $this->store, 42, null, false )
+ );
+ }
+
+ public function testOnTaskHandlerFactory() {
+
+ $instance = new ElasticFactory();
+ $taskHandlers = [];
+ $outputFormatter = null;
+
+ $this->assertTrue(
+ $instance->onTaskHandlerFactory( $taskHandlers, $this->store, $outputFormatter, null )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Exception/ClientBuilderNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Exception/ClientBuilderNotFoundExceptionTest.php
new file mode 100644
index 00000000..6cb6b234
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Exception/ClientBuilderNotFoundExceptionTest.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace SMW\Tests\Elastic\Exception;
+
+use SMW\Elastic\Exception\ClientBuilderNotFoundException;
+use RuntimeException;
+
+/**
+ * @covers \SMW\Elastic\Exception\ClientBuilderNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ClientBuilderNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RuntimeException::class,
+ new ClientBuilderNotFoundException()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/FileIndexerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/FileIndexerTest.php
new file mode 100644
index 00000000..cf891053
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/FileIndexerTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\FileIndexer;
+use SMW\DIWikiPage;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Indexer\FileIndexer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileIndexerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $indexer;
+ private $logger;
+
+ protected function setUp() {
+
+ $this->indexer = $this->getMockBuilder( '\SMW\Elastic\Indexer\Indexer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->logger = $this->getMockBuilder( '\Psr\Log\NullLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FileIndexer::class,
+ new FileIndexer( $this->indexer )
+ );
+ }
+
+ public function testIndex() {
+
+ $file = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $file->expects( $this->once() )
+ ->method( 'getFullURL' )
+ ->will( $this->returnValue( 'http://example.org/Foo.txt' ) );
+
+ $ingest = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'putPipeline' ] )
+ ->getMock();
+
+ $ingest->expects( $this->once() )
+ ->method( 'putPipeline' );
+
+ $client = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $client->expects( $this->once() )
+ ->method( 'ingest' )
+ ->will( $this->returnValue( $ingest ) );
+
+ $this->indexer->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $client ) );
+
+ $instance = new FileIndexer(
+ $this->indexer
+ );
+
+ $instance->setLogger( $this->logger );
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__, NS_FILE );
+ $dataItem->setId( 42 );
+
+ $instance->index( $dataItem, $file );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerRecoveryJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerRecoveryJobTest.php
new file mode 100644
index 00000000..1789a97f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerRecoveryJobTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\IndexerRecoveryJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Elastic\Indexer\IndexerRecoveryJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IndexerRecoveryJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+ private $title;
+ private $cache;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Cache', $this->cache );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IndexerRecoveryJob::class,
+ new IndexerRecoveryJob( $this->title )
+ );
+ }
+
+ public function testRun_Index() {
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getConfig' )
+ ->will( $this->returnValue( ( new \SMW\Options() ) ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' );
+
+ $instance = new IndexerRecoveryJob(
+ $this->title,
+ [ 'index' => 'Foo#0##']
+ );
+
+ $instance->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerTest.php
new file mode 100644
index 00000000..51edfe84
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/IndexerTest.php
@@ -0,0 +1,208 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\Indexer;
+use SMW\Services\ServicesContainer;
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\Elastic\Indexer\Indexer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IndexerTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $servicesContainer;
+ private $logger;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->servicesContainer = new ServicesContainer();
+
+ $this->logger = $this->getMockBuilder( '\Psr\Log\NullLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Indexer::class,
+ new Indexer( $this->store, $this->servicesContainer )
+ );
+ }
+
+ public function testSetup() {
+
+ $rollover = $this->getMockBuilder( '\SMW\Elastic\Indexer\Rollover' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $rollover->expects( $this->exactly( 2 ) )
+ ->method( 'update' );
+
+ $this->servicesContainer->add(
+ 'Rollover',
+ function() use( $rollover ) { return $rollover; }
+ );
+
+ $instance = new Indexer(
+ $this->store,
+ $this->servicesContainer
+ );
+
+ $instance->setup();
+ }
+
+ public function testDrop() {
+
+ $rollover = $this->getMockBuilder( '\SMW\Elastic\Indexer\Rollover' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $rollover->expects( $this->exactly( 2 ) )
+ ->method( 'delete' );
+
+ $this->servicesContainer->add(
+ 'Rollover',
+ function() use( $rollover ) { return $rollover; }
+ );
+
+ $instance = new Indexer(
+ $this->store,
+ $this->servicesContainer
+ );
+
+ $instance->drop();
+ }
+
+ public function testTextIndex() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+ $subject->setId( 42 );
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $changeDiff->expects( $this->once() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeDiff->expects( $this->once() )
+ ->method( 'getDataOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $bulk = $this->getMockBuilder( '\SMW\Elastic\Indexer\Bulk' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $bulk->expects( $this->once() )
+ ->method( 'upsert' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'text_raw' => 'Bar' ] ) );
+
+ $this->servicesContainer->add(
+ 'Bulk',
+ function() use( $bulk ) { return $bulk; }
+ );
+
+ $instance = new Indexer(
+ $this->store,
+ $this->servicesContainer
+ );
+
+ $instance->setLogger( $this->logger );
+ $instance->index( $changeDiff, 'Bar' );
+ }
+
+ /**
+ * @dataProvider textLinksProvider
+ */
+ public function testRemoveLinks( $text, $expected ) {
+
+ $instance = new Indexer(
+ $this->store,
+ $this->servicesContainer
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->removeLinks( $text )
+ );
+ }
+
+ public function textLinksProvider() {
+
+ yield [
+ 'abc',
+ 'abc'
+ ];
+
+ yield [
+ '{{DEFAULTSORT: FOO}}',
+ ''
+ ];
+
+ yield [
+ '{{Foo|bar=foobar}}',
+ 'bar=foobar'
+ ];
+
+ yield [
+ '[[Has foo::Bar]]',
+ 'Bar'
+ ];
+
+ yield [
+ '[[:foo|abc]]',
+ 'abc'
+ ];
+
+ yield [
+ 'abc [[:foo|abc]]',
+ 'abc abc'
+ ];
+
+ yield [
+ '[[:|abc]]',
+ '[[:|abc]]'
+ ];
+
+ yield [
+ '[[:abc]]',
+ ':abc'
+ ];
+
+ yield [
+ 'abc [[abc]]',
+ 'abc abc'
+ ];
+
+ yield [
+ '[[abc]] abc [[:bar|foobar]]',
+ 'abc abc foobar'
+ ];
+
+ yield [
+ '[[:Spécial%3ARequêter&cl=Yzo1jUEKwzAMBF8T3RKMS486tPTQb8iJjE3sGCSH9PlVGgpzWLRi9oPj_Wm8SUfvtr0GluH2OPF2fxkQm1TqGKTR0ikUhpJr7uids7StSKVAYlpYFDW1A5RJ5lQocMFpmgbv4i49mdo7Yd1LV5gLqb03-SmtOPKa_1nrcS2dPUITcyPpDC1G5Y4OKuXtGvgC|foo]]',
+ 'foo'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RebuilderTest.php
new file mode 100644
index 00000000..5be73b67
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RebuilderTest.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\Rebuilder;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Indexer\Rebuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+ private $indexer;
+ private $propertyTableRowMapper;
+ private $rollover;
+ private $messageReporter;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->indexer = $this->getMockBuilder( '\SMW\Elastic\Indexer\Indexer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyTableRowMapper = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableRowMapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->rollover = $this->getMockBuilder( '\SMW\Elastic\Indexer\Rollover' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\NullMessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Rebuilder::class,
+ new Rebuilder( $this->connection, $this->indexer, $this->propertyTableRowMapper, $this->rollover )
+ );
+ }
+
+ public function testSelect() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->select( $store, [] )
+ );
+ }
+
+ public function testDeleteAndSetupIndices() {
+
+ $this->indexer->expects( $this->once() )
+ ->method( 'drop' );
+
+ $this->indexer->expects( $this->once() )
+ ->method( 'setup' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->deleteAndSetupIndices();
+ }
+
+ public function testCreateIndices() {
+
+ $indices = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'delete', 'existsAlias', 'updateAliases' ] )
+ ->getMock();
+
+ $indices->expects( $this->atLeastOnce() )
+ ->method( 'updateAliases' );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indices' )
+ ->will( $this->returnValue( $indices ) );
+
+ $this->connection->expects( $this->exactly( 2 ) )
+ ->method( 'setLock' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->createIndices();
+ }
+
+ public function testSetDefaults() {
+
+ $indices = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'delete', 'close', 'open' ] )
+ ->getMock();
+
+ $indices->expects( $this->atLeastOnce() )
+ ->method( 'open' );
+
+ $indices->expects( $this->atLeastOnce() )
+ ->method( 'close' );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indices' )
+ ->will( $this->returnValue( $indices ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'hasIndex' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->exactly( 2 ) )
+ ->method( 'releaseLock' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->setDefaults();
+ }
+
+ public function testDelete() {
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'delete' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->delete( 42 );
+ }
+
+ public function testRebuild() {
+
+ $options = $this->getMockBuilder( '\SMW\Options' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'newChangeDiff' )
+ ->will( $this->returnValue( $changeDiff ) );
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $this->propertyTableRowMapper->expects( $this->any() )
+ ->method( 'newChangeOp' )
+ ->will( $this->returnValue( $changeOp ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getConfig' )
+ ->will( $this->returnValue( $options ) );
+
+ $this->indexer->expects( $this->once() )
+ ->method( 'index' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->rebuild( 42, $semanticData );
+ }
+
+ public function testRefresh() {
+
+ $this->connection->expects( $this->any() )
+ ->method( 'hasIndex' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'refresh' );
+
+ $instance = new Rebuilder(
+ $this->connection,
+ $this->indexer,
+ $this->propertyTableRowMapper,
+ $this->rollover
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->refresh();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/ReplicationStatusTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/ReplicationStatusTest.php
new file mode 100644
index 00000000..3d66e991
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/ReplicationStatusTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\ReplicationStatus;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Indexer\ReplicationStatus
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ReplicationStatusTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ReplicationStatus::class,
+ new ReplicationStatus( $this->connection )
+ );
+ }
+
+ public function testGet_OnUnknownKeyThrowsException() {
+
+ $instance = new ReplicationStatus(
+ $this->connection
+ );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->get( 'Foo' );
+ }
+
+ public function testGet_refresh_interval() {
+
+ $settings = [
+ 'Foo' => [
+ 'settings' => [
+ 'index' => [
+ 'refresh_interval' => 42
+ ]
+ ]
+ ]
+ ];
+
+ $this->connection->expects( $this->once() )
+ ->method( 'getSettings' )
+ ->will( $this->returnValue( $settings ) );
+
+ $instance = new ReplicationStatus(
+ $this->connection
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'refresh_interval' )
+ );
+ }
+
+ public function testGet_last_update() {
+
+ $res = [
+ 'hits' => [
+ 'hits' => [
+ [
+ '_source' => [
+ 'P:29' => [
+ 'datField' => [ 2458322.0910764 ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+
+ $this->connection->expects( $this->once() )
+ ->method( 'search' )
+ ->will( $this->returnValue( [ $res, [] ] ) );
+
+ $instance = new ReplicationStatus(
+ $this->connection
+ );
+
+ $this->assertEquals(
+ '2018-07-22 14:11:09',
+ $instance->get( 'last_update' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RolloverTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RolloverTest.php
new file mode 100644
index 00000000..2e8fbd7c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Indexer/RolloverTest.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\Elastic\Indexer;
+
+use SMW\Elastic\Indexer\Rollover;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\Indexer\Rollover
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RolloverTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Rollover::class,
+ new Rollover( $this->connection )
+ );
+ }
+
+ public function testRollover() {
+
+ $indices = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'delete', 'existsAlias', 'updateAliases' ] )
+ ->getMock();
+
+ $indices->expects( $this->once() )
+ ->method( 'updateAliases' );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indices' )
+ ->will( $this->returnValue( $indices ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'releaseLock' );
+
+ $instance = new Rollover(
+ $this->connection
+ );
+
+ $instance->rollover( 'Foo', 'v2' );
+ }
+
+ public function testUpdate() {
+
+ $indices = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'delete', 'existsAlias', 'updateAliases' ] )
+ ->getMock();
+
+ $indices->expects( $this->once() )
+ ->method( 'updateAliases' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indices' )
+ ->will( $this->returnValue( $indices ) );
+
+ $instance = new Rollover(
+ $this->connection
+ );
+
+ $instance->update( 'Foo' );
+ }
+
+ public function testDelete() {
+
+ $indices = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'delete', 'existsAlias' ] )
+ ->getMock();
+
+ $indices->expects( $this->exactly( 3 ) )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $indices->expects( $this->exactly( 3 ) )
+ ->method( 'delete' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indices' )
+ ->will( $this->returnValue( $indices ) );
+
+ $instance = new Rollover(
+ $this->connection
+ );
+
+ $instance->delete( 'Foo' );
+ }
+
+ public function testUpdate_OnNoConnectionThrowsException() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new Rollover(
+ $this->connection
+ );
+
+ $this->setExpectedException( '\SMW\Elastic\Exception\NoConnectionException' );
+ $instance->update( 'Foo' );
+ }
+
+ public function testDelete_OnNoConnectionThrowsException() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new Rollover(
+ $this->connection
+ );
+
+ $this->setExpectedException( '\SMW\Elastic\Exception\NoConnectionException' );
+ $instance->delete( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Lookup/ProximityPropertyValueLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Lookup/ProximityPropertyValueLookupTest.php
new file mode 100644
index 00000000..aef58370
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/Lookup/ProximityPropertyValueLookupTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\Lookup;
+
+use SMW\Elastic\Lookup\ProximityPropertyValueLookup;
+use SMW\Tests\PHPUnitCompat;
+use SMW\DIProperty;
+use SMW\RequestOptions;
+
+/**
+ * @covers \SMW\Elastic\Lookup\ProximityPropertyValueLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ProximityPropertyValueLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $logger;
+ private $store;
+ private $elasticClient;
+
+ protected function setUp() {
+
+ $this->logger = $this->getMockBuilder( '\Psr\Log\LoggerInterface' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $this->idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->onConsecutiveCalls( 42, 1001, 9000, 110001 ) );
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getConnection' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $this->idTable ) );
+
+ $this->elasticClient = $this->getMockBuilder( '\SMW\Elastic\Connection\Client' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->elasticClient ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ProximityPropertyValueLookup::class,
+ new ProximityPropertyValueLookup( $this->store )
+ );
+ }
+
+ public function testLookup_AnyValue() {
+
+ $params = [
+ 'index' => null,
+ 'type' => 'data',
+ 'body' => [
+ '_source' => [ 'P:42.wpgField' ],
+ 'from' => 0,
+ 'size' => 500,
+ 'query' => [
+ 'exists' => [ 'field' => 'P:42.wpgField' ]
+ ]
+ ]
+ ];
+
+ $this->elasticClient->expects( $this->once() )
+ ->method( 'search' )
+ ->with( $this->equalTo( $params ) );
+
+ $instance = new ProximityPropertyValueLookup(
+ $this->store
+ );
+
+ $instance->lookup( new DIProperty( 'Foo' ), '', new RequestOptions() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/AggregationsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/AggregationsTest.php
new file mode 100644
index 00000000..cdcc45da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/AggregationsTest.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine;
+
+use SMW\Elastic\QueryEngine\Aggregations;
+use SMW\Elastic\QueryEngine\FieldMapper;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\Aggregations
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class AggregationsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Aggregations::class,
+ new Aggregations()
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testResolve( $parameters, $expected ) {
+
+ $instance = new Aggregations( $parameters );
+
+ $this->assertEquals(
+ $expected,
+ (string)$instance
+ );
+ }
+
+ public function parametersProvider() {
+
+ $fieldMapper = new FieldMapper();
+
+ yield [
+ [],
+ '[]'
+ ];
+
+ yield [
+ $fieldMapper->aggs_terms( 'test_1', 'category' ),
+ '{"aggregations":{"test_1":{"terms":{"field":"category"}}}}'
+ ];
+
+ yield [
+ $fieldMapper->aggs_terms( 'test_2', 'category', [ 'size' => 5 ] ),
+ '{"aggregations":{"test_2":{"terms":{"field":"category","size":5}}}}'
+ ];
+
+ yield [
+ [
+ $fieldMapper->aggs_terms( 'test_1', 'foo' ),
+ $fieldMapper->aggs_terms( 'test_2', 'bar' )
+ ],
+ '{"aggregations":{"test_1":{"terms":{"field":"foo"}},"test_2":{"terms":{"field":"bar"}}}}'
+ ];
+
+ yield [
+ [
+ new Aggregations( $fieldMapper->aggs_terms( 'test_1', 'foo' ) ),
+ new Aggregations( $fieldMapper->aggs_terms( 'test_2', 'bar' ) )
+ ],
+ '{"aggregations":{"test_1":{"terms":{"field":"foo"}},"test_2":{"terms":{"field":"bar"}}}}'
+ ];
+
+ // https://www.elastic.co/blog/intro-to-Aggregations-pt-2-sub-Aggregations
+ $aggs = new Aggregations(
+ $fieldMapper->aggs_terms( 'all_boroughs', 'borough' )
+ );
+
+ $aggs->addSubAggregations(
+ new Aggregations(
+ $fieldMapper->aggs_terms( 'cause', 'contributing_factor_vehicle', [ 'size' => 3 ] )
+ )
+ );
+
+ yield [
+ $aggs,
+ '{"aggregations":{"all_boroughs":{"terms":{"field":"borough"},"aggregations":{"cause":{"terms":{"field":"contributing_factor_vehicle","size":3}}}}}}'
+ ];
+
+ $cause = new Aggregations(
+ $fieldMapper->aggs_terms( 'cause', 'contributing_factor_vehicle', [ 'size' => 3 ] )
+ );
+
+ $cause->addSubAggregations(
+ new Aggregations(
+ $fieldMapper->aggs_date_histogram( 'incidents_per_month', '@timestamp', 'month' )
+ )
+ );
+
+ $aggs = new Aggregations(
+ $fieldMapper->aggs_terms( 'all_boroughs', 'borough' )
+ );
+
+ $aggs->addSubAggregations(
+ $cause
+ );
+
+ yield [
+ $aggs,
+ '{"aggregations":{"all_boroughs":{"terms":{"field":"borough"},"aggregations":{"cause":{"terms":{"field":"contributing_factor_vehicle","size":3},"aggregations":{"incidents_per_month":{"date_histogram":{"field":"@timestamp","interval":"month"}}}}}}}}'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ConditionTest.php
new file mode 100644
index 00000000..53bcf587
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ConditionTest.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine;
+
+use SMW\Elastic\QueryEngine\Condition;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\Condition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Condition::class,
+ new Condition()
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testResolveConditionParameters( $paramsters, $type, $expected ) {
+
+ $instance = new Condition( $paramsters );
+ $instance->type( $type );
+
+ $this->assertEquals(
+ $expected,
+ $instance->__toString()
+ );
+ }
+
+ /**
+ * @dataProvider typeProvider
+ */
+ public function testType( $type, $expected ) {
+
+ $instance = new Condition( [] );
+ $instance->type( $type );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getType()
+ );
+ }
+
+ public function testConditionLogs() {
+
+ $cond = new Condition( [ 'foobar' ] );
+ $cond->log( 'foo_log' );
+
+ $instance = new Condition( $cond );
+ $instance->log( [ 'test' ] );
+
+ $instance->toArray();
+
+ $this->assertEquals(
+ [ [ 'test' ], [ 'foo_log' ] ],
+ $instance->getLogs()
+ );
+ }
+
+ public function parametersProvider() {
+
+ yield [
+ '',
+ '',
+ '""'
+ ];
+
+ yield [
+ [],
+ '',
+ '[]'
+ ];
+
+ yield [
+ [ 'Foo' => [ 'Bar' ] ],
+ 'must',
+ '{"bool":{"must":{"Foo":["Bar"]}}}'
+ ];
+
+ yield [
+ [ new Condition( [ 'foobar' ] ) ],
+ 'must',
+ '{"bool":{"must":[{"bool":{"must":["foobar"]}}]}}'
+ ];
+
+ $cond = new Condition( [ 'foobar' ] );
+ $cond->type( '' );
+
+ yield [
+ [ $cond ],
+ 'must',
+ '{"bool":{"must":[["foobar"]]}}'
+ ];
+
+ $cond = new Condition( [ 'foobar' ] );
+ $cond->type( null );
+
+ yield [
+ [ $cond ],
+ 'must',
+ '{"bool":{"must":[["foobar"]]}}'
+ ];
+
+ yield [
+ [ [ new Condition( [ 'foobar' ] ), new Condition( [ 'bar' ] ) ] ],
+ Condition::TYPE_SHOULD,
+ '{"bool":{"should":[[{"bool":{"must":["foobar"]}},{"bool":{"must":["bar"]}}]]}}'
+ ];
+ }
+
+ public function typeProvider() {
+
+ yield [
+ 'must',
+ Condition::TYPE_MUST
+ ];
+
+ yield [
+ 'must_not',
+ Condition::TYPE_MUST_NOT
+ ];
+
+ yield [
+ 'should',
+ Condition::TYPE_SHOULD
+ ];
+
+ yield [
+ 'filter',
+ Condition::TYPE_FILTER
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
new file mode 100644
index 00000000..a47df9f8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ClassDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $conditionBuilder;
+
+ public function setUp() {
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getID', 'findHierarchyMembers' ] )
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ClassDescriptionInterpreter::class,
+ new ClassDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ /**
+ * @dataProvider classDescriptionProvider
+ */
+ public function testInterpretDescription( $description, $isConjunction, $hierarchyMembers, $expected ) {
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'getID' )
+ ->will( $this->onConsecutiveCalls( 42, 1001, 9000, 110001 ) );
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'findHierarchyMembers' )
+ ->will( $this->returnValue( $hierarchyMembers ) );
+
+ $instance = new ClassDescriptionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $isConjunction
+ );
+
+ $this->assertEquals(
+ $expected,
+ $condition
+ );
+ }
+
+ public function classDescriptionProvider() {
+
+ $descriptionFactory = new DescriptionFactory();
+ $cat_foo = DIWikiPage::newFromText( 'Foo', NS_CATEGORY );
+ $cat_bar = DIWikiPage::newFromText( 'Bar', NS_CATEGORY );
+
+ yield [
+ $descriptionFactory->newClassDescription( $cat_foo ),
+ false,
+ [],
+ '{"bool":{"filter":[{"term":{"P:42.wpgID":1001}}]}}'
+ ];
+
+ yield [
+ $descriptionFactory->newClassDescription( $cat_foo ),
+ true,
+ [],
+ '{"bool":{"filter":[{"term":{"P:42.wpgID":1001}}]}}'
+ ];
+
+ // Categories
+ $classDescription = $descriptionFactory->newClassDescription( $cat_foo );
+ $classDescription->addClass( $cat_bar );
+
+ yield [
+ $classDescription,
+ false,
+ [],
+ '{"bool":{"should":[{"term":{"P:42.wpgID":1001}},{"term":{"P:42.wpgID":9000}}]}}'
+ ];
+
+ yield [
+ $classDescription,
+ true,
+ [],
+ '{"bool":{"should":[{"term":{"P:42.wpgID":1001}},{"term":{"P:42.wpgID":9000}}]}}'
+ ];
+
+ // HierarchyMembers
+ yield [
+ $descriptionFactory->newClassDescription( $cat_foo ),
+ false,
+ [ 5000, 5001 ],
+ '{"bool":{"filter":[{"bool":{"should":[{"term":{"P:42.wpgID":1001}},{"terms":{"P:42.wpgID":[5000,5001]}}]}}]}}'
+ ];
+
+ yield [
+ $descriptionFactory->newClassDescription( $cat_foo ),
+ true,
+ [ 5000, 5001 ],
+ '{"bool":{"filter":[{"bool":{"should":[{"term":{"P:42.wpgID":1001}},{"terms":{"P:42.wpgID":[5000,5001]}}]}}]}}'
+ ];
+
+ yield [
+ $classDescription,
+ false,
+ [ 5000, 5001 ],
+ '{"bool":{"should":[{"bool":{"should":[{"term":{"P:42.wpgID":1001}},{"terms":{"P:42.wpgID":[5000,5001]}}]}},{"bool":{"should":[{"term":{"P:42.wpgID":9000}},{"terms":{"P:42.wpgID":[5000,5001]}}]}}]}}'
+ ];
+
+ // Negation
+ $classDescription = $descriptionFactory->newClassDescription( $cat_foo );
+ $classDescription->isNegation = true;
+
+ yield [
+ $classDescription,
+ false,
+ [],
+ '{"bool":{"must_not":[{"term":{"P:42.wpgID":1001}}]}}'
+ ];
+
+ yield [
+ $classDescription,
+ true,
+ [],
+ '{"bool":{"must_not":[{"term":{"P:42.wpgID":1001}}]}}'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
new file mode 100644
index 00000000..cac182be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+use SMW\Tests\TestEnvironmentTrait;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $conditionBuilder;
+ private $descriptionFactory;
+ private $queryParser;
+
+ public function setUp() {
+
+ $this->descriptionFactory = new DescriptionFactory();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parameters = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\TermsLookup\Parameters' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->termsLookup = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\TermsLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->termsLookup->expects( $this->any() )
+ ->method( 'newParameters' )
+ ->will( $this->returnValue( $parameters ) );
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getTermsLookup', 'getStore', 'getID', 'interpretDescription' ] )
+ ->getMock();
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'getTermsLookup' )
+ ->will( $this->returnValue( $this->termsLookup ) );
+
+ $this->queryParser = $this->getMockBuilder( '\SMW\Query\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConceptDescriptionInterpreter::class,
+ new ConceptDescriptionInterpreter( $this->conditionBuilder, $this->queryParser )
+ );
+ }
+
+ public function testInterpretDescription_EmptyConcept() {
+
+ $instance = new ConceptDescriptionInterpreter(
+ $this->conditionBuilder,
+ $this->queryParser
+ );
+
+ $conceptDescription = $this->descriptionFactory->newConceptDescription(
+ DIWikiPage::newFromText( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->interpretDescription( $conceptDescription )
+ );
+ }
+
+ public function testInterpretDescription_AvailableConceptQuery() {
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'interpretDescription' )
+ ->will( $this->returnValue( $this->conditionBuilder->newCondition( [ 'Foo' ] ) ) );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $concept = $this->getMockBuilder( '\SMWDIConcept' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $concept ] ) );
+
+ $this->queryParser->expects( $this->any() )
+ ->method( 'getQueryDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new ConceptDescriptionInterpreter(
+ $this->conditionBuilder,
+ $this->queryParser
+ );
+
+ $conceptDescription = $this->descriptionFactory->newConceptDescription(
+ DIWikiPage::newFromText( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $this->assertEquals(
+ '{"bool":{"must":["Foo"]}}',
+ $instance->interpretDescription( $conceptDescription )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php
new file mode 100644
index 00000000..ce1b4fa4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConjunctionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $conditionBuilder;
+
+ public function setUp() {
+
+ $this->descriptionFactory = new DescriptionFactory();
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'interpretDescription' ] )
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConjunctionInterpreter::class,
+ new ConjunctionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testInterpretDescription_Empty() {
+
+ $instance = new ConjunctionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $condition = $instance->interpretDescription(
+ $this->descriptionFactory->newConjunction( [] )
+ );
+
+ $this->assertEquals(
+ [],
+ $condition
+ );
+ }
+
+ public function testInterpretDescription_NotEmpty() {
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'interpretDescription' )
+ ->will( $this->returnValue( $this->conditionBuilder->newCondition( [ 'Foo' ] ) ) );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->any() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new ConjunctionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $condition = $instance->interpretDescription(
+ $this->descriptionFactory->newConjunction( [ $description ] )
+ );
+
+ $this->assertEquals(
+ '{"bool":{"must":[{"bool":{"must":["Foo"]}}]}}',
+ $condition
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
new file mode 100644
index 00000000..9671b96e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $conditionBuilder;
+
+ public function setUp() {
+
+ $this->descriptionFactory = new DescriptionFactory();
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( null )
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NamespaceDescriptionInterpreter::class,
+ new NamespaceDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testInterpretDescription_NotPartOfAConjunction() {
+
+ $instance = new NamespaceDescriptionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $condition = $instance->interpretDescription(
+ $this->descriptionFactory->newNamespaceDescription( NS_MAIN ),
+ false
+ );
+
+ $this->assertEquals(
+ '{"bool":{"filter":{"term":{"subject.namespace":0}}}}',
+ $condition
+ );
+ }
+
+ public function testInterpretDescription_IsPartOfAConjunction() {
+
+ $instance = new NamespaceDescriptionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $condition = $instance->interpretDescription(
+ $this->descriptionFactory->newNamespaceDescription( NS_MAIN ),
+ true
+ );
+
+ $this->assertEquals(
+ '{"term":{"subject.namespace":0}}',
+ $condition
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreterTest.php
new file mode 100644
index 00000000..7dff7713
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/SomeValueInterpreterTest.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\SomeValueInterpreter;
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\Query\DescriptionFactory;
+use SMW\DataItemFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\SomeValueInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SomeValueInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $conditionBuilder;
+ private $descriptionFactory;
+ private $dataItemFactory;
+
+ public function setUp() {
+
+ $this->descriptionFactory = new DescriptionFactory();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getID' ] )
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SomeValueInterpreter::class,
+ new SomeValueInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ public function testInterpretDescription_MissingPropertyThrowsException() {
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $options = [];
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $this->dataItemFactory->newDIWikiPage( 'Foo' )
+ );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->interpretDescription( $description, $options );
+ }
+
+ public function testInterpretDescription_MissingPIDThrowsException() {
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $options = [
+ 'property' => $this->dataItemFactory->newDIProperty( 'Bar' )
+ ];
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $this->dataItemFactory->newDIWikiPage( 'Foo' )
+ );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->interpretDescription( $description, $options );
+ }
+
+ /**
+ * @dataProvider numberValueProvider
+ */
+ public function testInterpretDescription_NumberValue( $dataItem, $comparator, $options, $expected ) {
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ null,
+ $comparator
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $options
+ );
+
+ $this->assertEquals(
+ $expected,
+ (string)$condition
+ );
+ }
+
+ /**
+ * @dataProvider timeValueProvider
+ */
+ public function testInterpretDescription_TimeValue( $dataItem, $comparator, $options, $expected ) {
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ null,
+ $comparator
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $options
+ );
+
+ $this->assertEquals(
+ $expected,
+ (string)$condition
+ );
+ }
+
+ /**
+ * @dataProvider textValueProvider
+ */
+ public function testInterpretDescription_TextValue( $dataItem, $comparator, $options, $expected ) {
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ null,
+ $comparator
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $options
+ );
+
+ $this->assertEquals(
+ $expected,
+ (string)$condition
+ );
+ }
+
+ /**
+ * @dataProvider pageValueProvider
+ */
+ public function testInterpretDescription_PageValue( $dataItem, $comparator, $options, $expected ) {
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'getID' )
+ ->will( $this->onConsecutiveCalls( 42, 1001, 9000, 110001 ) );
+
+ $this->conditionBuilder->setOptions( new Options(
+ [
+ 'cjk.best.effort.proximity.match' => true
+ ]
+ ) );
+
+ $instance = new SomeValueInterpreter(
+ $this->conditionBuilder
+ );
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ null,
+ $comparator
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $options
+ );
+
+ $this->assertEquals(
+ $expected,
+ (string)$condition
+ );
+ }
+
+ public function numberValueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'numField',
+ 'type' => 'must'
+ ];
+
+ $dataItem = $dataItemFactory->newDINumber( 123 );
+
+ yield [
+ $dataItem,
+ SMW_CMP_EQ,
+ $options,
+ '{"bool":{"filter":{"term":{"P:42.numField":123}}}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_NEQ,
+ $options,
+ '{"bool":{"must_not":{"term":{"P:42.numField":123}}}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_LESS,
+ $options,
+ '{"bool":{"must":[{"range":{"P:42.numField":{"lt":123}}}]}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_GRTR,
+ $options,
+ '{"bool":{"must":[{"range":{"P:42.numField":{"gt":123}}}]}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_LEQ,
+ $options,
+ '{"bool":{"must":[{"range":{"P:42.numField":{"lte":123}}}]}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_GEQ,
+ $options,
+ '{"bool":{"must":[{"range":{"P:42.numField":{"gte":123}}}]}}'
+ ];
+
+ // This form is actually handled differently using a compound
+ // [[Has number::~123]] -> [[Has number:: <q>[[≥0]] [[≤123]]</q> ]]
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":[{"match":{"P:42.numField":{"query":123,"operator":"and"}}}]}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_NLKE,
+ $options,
+ '{"bool":{"must_not":[{"match":{"P:42.numField":{"query":123,"operator":"and"}}}]}}'
+ ];
+ }
+
+ public function timeValueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'datField',
+ 'type' => 'must'
+ ];
+
+ $dataItem = $dataItemFactory->newDITime( 1, 1970, 12, 12, 12, 12, 12 );
+
+ yield [
+ $dataItem,
+ SMW_CMP_EQ,
+ $options,
+ '{"bool":{"filter":{"term":{"P:42.datField":2440933.0084722}}}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_NEQ,
+ $options,
+ '{"bool":{"must_not":{"term":{"P:42.datField":2440933.0084722}}}}'
+ ];
+ }
+
+ public function textValueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'txtField',
+ 'type' => 'must'
+ ];
+
+ $dataItem = $dataItemFactory->newDIBlob( '*test*' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_EQ,
+ $options,
+ '{"bool":{"filter":{"term":{"P:42.txtField.keyword":"*test*"}}}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":{"query_string":{"fields":["P:42.txtField","P:42.txtField.keyword"],"query":"*test*"}}}}'
+ ];
+ }
+
+ public function pageValueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'wpgID',
+ 'type' => 'must'
+ ];
+
+ $dataItem = $dataItemFactory->newDIWikiPage( 'test' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_EQ,
+ $options,
+ '{"bool":{"filter":{"term":{"P:42.wpgID":42}}}}'
+ ];
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'wpgField',
+ 'type' => 'must'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":{"query_string":{"fields":["P:42.wpgField"],"query":"+test"}}}}'
+ ];
+
+ $dataItem = $dataItemFactory->newDIWikiPage( '*テスト*' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":[{"match_phrase":{"P:42.wpgField":"テスト"}}]}}'
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
new file mode 100644
index 00000000..2ce13301
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine\DescriptionInterpreters;
+
+use SMW\Elastic\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+use SMW\DataItemFactory;
+use SMW\Options;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ValueDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $conditionBuilder;
+
+ public function setUp() {
+
+ $this->descriptionFactory = new DescriptionFactory();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->conditionBuilder = $this->getMockBuilder( '\SMW\Elastic\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getID' ] )
+ ->getMock();
+
+ $this->conditionBuilder->expects( $this->any() )
+ ->method( 'getID' )
+ ->will( $this->onConsecutiveCalls( 42, 1001, 9000, 110001 ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ValueDescriptionInterpreter::class,
+ new ValueDescriptionInterpreter( $this->conditionBuilder )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testInterpretDescription( $dataItem, $comparator, $options, $expected ) {
+
+ $this->conditionBuilder->setOptions( new Options(
+ [
+ 'cjk.best.effort.proximity.match' => true
+ ]
+ ) );
+
+ $instance = new ValueDescriptionInterpreter(
+ $this->conditionBuilder
+ );
+
+ $description = $this->descriptionFactory->newValueDescription(
+ $dataItem,
+ null,
+ $comparator
+ );
+
+ $condition = $instance->interpretDescription(
+ $description,
+ $options
+ );
+
+ $this->assertEquals(
+ $expected,
+ (string)$condition
+ );
+ }
+
+ public function valueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $options = [
+ 'property' => $dataItemFactory->newDIProperty( 'Bar' ),
+ 'pid' => 'P:42',
+ 'field' => 'wpgID',
+ 'type' => 'must'
+ ];
+
+ $dataItem = $dataItemFactory->newDIWikiPage( 'test' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_EQ,
+ $options,
+ '{"bool":{"must":{"terms":{"_id":[42]}}}}'
+ ];
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":[{"match":{"subject.sortkey":"test"}}]}}'
+ ];
+
+ // wide.proximity.fields
+ $dataItem = $dataItemFactory->newDIWikiPage( '~*test*' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":{"query_string":{"fields":["text_copy"],"query":"*test*","minimum_should_match":1}}}}'
+ ];
+
+ // wide.proximity.fields
+ // cjk.best.effort.proximity.match
+ $dataItem = $dataItemFactory->newDIWikiPage( '~*テスト*' );
+
+ yield [
+ $dataItem,
+ SMW_CMP_LIKE,
+ $options,
+ '{"bool":{"must":[{"multi_match":{"fields":["text_copy"],"query":"テスト","type":"phrase"}}]}}'
+ ];
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ExcerptsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ExcerptsTest.php
new file mode 100644
index 00000000..dc3dc7bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/ExcerptsTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine;
+
+use SMW\Elastic\QueryEngine\Excerpts;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\Excerpts
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ExcerptsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Excerpts::class,
+ new Excerpts()
+ );
+ }
+
+ public function testGetExcerpt_StrippedTagsOnString() {
+
+ $instance = new Excerpts();
+
+ $instance->addExcerpt( 'Foo', '<div style="display:none;">Foo<em>bar</em></div>' );
+
+ $this->assertEquals(
+ 'Foo<em>bar</em>',
+ $instance->getExcerpt( 'Foo' )
+ );
+ }
+
+ public function testGetExcerpt_StrippedTagsOnArray() {
+
+ $instance = new Excerpts();
+
+ $instance->addExcerpt( 'Bar', [
+ 'test_field' => [ '<div style="display:none;">Foo<em>bar</em></div>', 'Fooba<em>r</em>' ]
+ ] );
+
+ $this->assertEquals(
+ 'Foo<em>bar</em> Fooba<em>r</em>',
+ $instance->getExcerpt( 'Bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/FieldMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/FieldMapperTest.php
new file mode 100644
index 00000000..8f13bde1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Elastic/QueryEngine/FieldMapperTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\Elastic\QueryEngine;
+
+use SMW\Elastic\QueryEngine\FieldMapper;
+
+/**
+ * @covers \SMW\Elastic\QueryEngine\FieldMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FieldMapperTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FieldMapper::class,
+ new FieldMapper()
+ );
+ }
+
+ public function testIsPhrase() {
+
+ $this->assertTrue(
+ FieldMapper::isPhrase( '"Foo bar"' )
+ );
+
+ $this->assertFalse(
+ FieldMapper::isPhrase( 'Foo"bar' )
+ );
+ }
+
+ public function testHasWildcard() {
+
+ $this->assertTrue(
+ FieldMapper::hasWildcard( 'Foo*' )
+ );
+
+ $this->assertFalse(
+ FieldMapper::hasWildcard( 'foo\*' )
+ );
+ }
+
+ /**
+ * @dataProvider aggregationsProvider
+ */
+ public function testAggregations( $method, $params, $expected ) {
+
+ $instance = new FieldMapper();
+
+ $this->assertEquals(
+ $expected,
+ call_user_func_array( [ $instance, $method ], $params )
+ );
+ }
+
+ public function aggregationsProvider() {
+
+ yield [
+ 'aggs',
+ [ 'Foo', 'bar' ],
+ [ 'aggregations' => [ "Foo" => 'bar' ] ]
+ ];
+
+ yield [
+ 'aggs_terms',
+ [ 'Foo', 'bar', [] ],
+ [ 'Foo' => [ 'terms' => [ "field" => 'bar' ] ] ]
+ ];
+
+ yield [
+ 'aggs_significant_terms',
+ [ 'Foo', 'bar', [] ],
+ [ 'Foo' => [ 'significant_terms' => [ "field" => 'bar' ] ] ]
+ ];
+
+ yield [
+ 'aggs_histogram',
+ [ 'Foo', 'bar', 100 ],
+ [ 'Foo' => [ 'histogram' => [ "field" => 'bar', 'interval' => 100 ] ] ]
+ ];
+
+ yield [
+ 'aggs_date_histogram',
+ [ 'Foo', 'bar', 100 ],
+ [ 'Foo' => [ 'date_histogram' => [ "field" => 'bar', 'interval' => 100 ] ] ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EncoderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EncoderTest.php
new file mode 100644
index 00000000..32dc5f14
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EncoderTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Encoder;
+
+/**
+ * @covers \SMW\Encoder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EncoderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Encoder',
+ new Encoder()
+ );
+ }
+
+ public function testEscape() {
+
+ $this->assertEquals(
+ '-3C-5B-23-26-25!~`+=-7C-2D-5F-5D-3E',
+ Encoder::escape( '<[#&%!~`+=|-_]>' )
+ );
+ }
+
+ public function testUnescape() {
+
+ $this->assertEquals(
+ '<[#&%!~`+=|-_]>',
+ Encoder::unescape( '-3C-5B-23-26-25!~`+=-7C-2D-5F-5D-3E' )
+ );
+ }
+
+ public function testEncode() {
+
+ $this->assertEquals(
+ '%3C%5B%23%26%25%21~%60%2B%3D%7C-_%5D%3E',
+ Encoder::encode( '<[#&%!~`+=|-_]>' )
+ );
+ }
+
+ /**
+ * @dataProvider stringProvider
+ */
+ public function testDecode( $input, $output ) {
+
+ $this->assertEquals(
+ $output,
+ Encoder::decode( $input )
+ );
+ }
+
+ /**
+ * @dataProvider stringProvider
+ */
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.CodeAnalysis.UnusedFunctionParameter
+ public function testEncodeDecode( $input, $output ) { // @codingStandardsIgnoreEnd
+
+ if ( $output === ' ' ) {
+ $output = '';
+ }
+
+ $this->assertEquals(
+ $output,
+ Encoder::decode( Encoder::encode( Encoder::escape( $output ) ) )
+ );
+ }
+
+ public function stringProvider() {
+
+ $provider = [];
+
+ $provider[] = [ ' ', '' ];
+ $provider[] = [ ' &nbsp;', ' ' ];
+
+ $provider[] = [ '2013/11/05', '2013/11/05' ];
+ $provider[] = [ '2013-2F11-2F05', '2013/11/05' ];
+
+ $provider[] = [ '2013$06&30', '2013$06&30' ];
+ $provider[] = [ '2013-2D06-2D30', '2013-06-30' ];
+
+ $provider[] = [ '2013-2406-2630', '2013$06&30' ];
+ $provider[] = [ '2013%2B06%2B30', '2013+06+30' ];
+ // $provider[] = array( '2013-06-30', '2013-06-30' );
+
+ $provider[] = [ '「ã€æ±äº¬', '「ã€æ±äº¬' ];
+ $provider[] = [ 'http:-2F-2F127.0.0.1-2F%E3%80%8C%E3%80%8D%E6%9D%B1%E4%BA%AC/2F-2F', 'http://127.0.0.1/「ã€æ±äº¬/2F/' ];
+
+ $provider[] = [ 'Foo(-3-)', 'Foo(-3-)' ];
+ $provider[] = [ '"Fo"o', '"Fo"o' ];
+
+ $provider[] = [ 'Foo_bar', 'Foo_bar' ];
+ $provider[] = [ 'Has-20url', 'Has url' ];
+
+ $provider[] = [ 'F &oo=?', 'F &oo=?' ];
+ $provider[] = [ 'F+%26oo%3D%3F', 'F+&oo=?' ];
+ $provider[] = [ 'F_%26oo%3D%3F', 'F_&oo=?' ];
+
+ $provider[] = [ 'search&foo=Bar&', 'search&foo=Bar&' ];
+ $provider[] = [ 'âêîôûëïçé', 'âêîôûëïçé' ];
+
+ $provider[] = [ 'Has+Foo%28-3-%29%26', 'Has+Foo(-3-)&' ];
+ $provider[] = [ 'Has_Foo(-3-)%26', 'Has_Foo(-3-)&' ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EnumTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EnumTest.php
new file mode 100644
index 00000000..e79fee8d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EnumTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Enum;
+
+/**
+ * @covers \SMW\Enum
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class EnumTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ *@dataProvider constProvider
+ */
+ public function testValidate( $const ) {
+
+ $this->assertInternalType(
+ 'string',
+ $const
+ );
+ }
+
+ public function constProvider() {
+
+ $provider[] = [
+ Enum::OPT_SUSPEND_PURGE
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventHandlerTest.php
new file mode 100644
index 00000000..0413c400
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventHandlerTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\EventHandler;
+
+/**
+ * @covers \SMW\EventHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EventHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ EventHandler::clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $eventDispatcher = $this->getMockBuilder( '\Onoi\EventDispatcher\EventDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\EventHandler',
+ new EventHandler( $eventDispatcher )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\EventHandler',
+ EventHandler::getInstance()
+ );
+ }
+
+ public function testGetEventDispatcher() {
+
+ $eventDispatcher = $this->getMockBuilder( '\Onoi\EventDispatcher\EventDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new EventHandler( $eventDispatcher );
+
+ $this->assertSame(
+ $eventDispatcher,
+ $instance->getEventDispatcher()
+ );
+
+ $this->assertInstanceOf(
+ '\Onoi\EventDispatcher\EventDispatcher',
+ EventHandler::getInstance()->getEventDispatcher()
+ );
+ }
+
+ public function testCanConstructDispatchContext() {
+
+ $eventDispatcher = $this->getMockBuilder( '\Onoi\EventDispatcher\EventDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new EventHandler( $eventDispatcher );
+
+ $this->assertInstanceOf(
+ '\Onoi\EventDispatcher\DispatchContext',
+ $instance->newDispatchContext()
+ );
+ }
+
+ public function testAddCallbackListenerForAdhocRegistration() {
+
+ $eventDispatcher = $this->getMockBuilder( '\Onoi\EventDispatcher\Dispatcher\GenericEventDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $eventDispatcher->expects( $this->once() )
+ ->method( 'addListener' )
+ ->with(
+ $this->equalTo( 'foo' ),
+ $this->anything() );
+
+ $instance = new EventHandler( $eventDispatcher );
+ $instance->addCallbackListener( 'foo', function (){} );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventListenerRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventListenerRegistryTest.php
new file mode 100644
index 00000000..25210fa0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/EventListenerRegistryTest.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace SMW\Tests;
+
+use Onoi\EventDispatcher\EventDispatcherFactory;
+use Onoi\EventDispatcher\EventListenerCollection;
+use SMW\EventListenerRegistry;
+
+/**
+ * @covers \SMW\EventListenerRegistry
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EventListenerRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $eventDispatcherFactory;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->eventDispatcherFactory = EventDispatcherFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $eventListenerCollection = $this->getMockBuilder( '\Onoi\EventDispatcher\EventListenerCollection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ EventListenerRegistry::class,
+ new EventListenerRegistry( $eventListenerCollection )
+ );
+ }
+
+ public function testListenerCollection() {
+
+ $eventListenerCollection = $this->getMockBuilder( '\Onoi\EventDispatcher\EventListenerCollection' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'registerCallback' ] )
+ ->getMockForAbstractClass();
+
+ $eventListenerCollection->expects( $this->any() )
+ ->method( 'registerCallback' );
+
+ $instance = new EventListenerRegistry( $eventListenerCollection );
+
+ $this->assertInstanceOf(
+ '\Onoi\EventDispatcher\EventListenerCollection',
+ $instance
+ );
+ }
+
+ public function testCanExecuteRegisteredListeners() {
+
+ $instance = new EventListenerRegistry(
+ $this->eventDispatcherFactory->newGenericEventListenerCollection()
+ );
+
+ $this->verifyExporterResetEvent( $instance );
+ $this->verifyFactboxCacheDeleteEvent( $instance );
+ $this->verifyFactboxCacheDeleteEventOnEmpty( $instance );
+ $this->verifyCachedPropertyValuesPrefetcherResetEvent( $instance );
+ $this->verifyCachedPrefetcherResetEvent( $instance );
+ $this->verifyCachedUpdateMarkerDeleteEvent( $instance );
+ }
+
+ public function verifyExporterResetEvent( EventListenerCollection $instance ) {
+ $this->assertListenerExecuteFor( 'exporter.reset', $instance, null );
+ }
+
+ public function verifyQueryComparatorResetEvent( EventListenerCollection $instance ) {
+ $this->assertListenerExecuteFor( 'query.comparator.reset', $instance, null );
+ }
+
+ public function verifyFactboxCacheDeleteEvent( EventListenerCollection $instance ) {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $dispatchContext = $this->eventDispatcherFactory->newDispatchContext();
+
+ $dispatchContext->set(
+ 'title',
+ $title
+ );
+
+ $this->assertListenerExecuteFor(
+ 'factbox.cache.delete',
+ $instance,
+ $dispatchContext
+ );
+ }
+
+ public function verifyFactboxCacheDeleteEventOnEmpty( EventListenerCollection $instance ) {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $dispatchContext = $this->eventDispatcherFactory->newDispatchContext();
+
+ $dispatchContext->set(
+ 'title',
+ ''
+ );
+
+ $this->assertListenerExecuteFor(
+ 'factbox.cache.delete',
+ $instance,
+ $dispatchContext
+ );
+ }
+
+ public function verifyCachedPropertyValuesPrefetcherResetEvent( EventListenerCollection $instance ) {
+
+ $dispatchContext = $this->eventDispatcherFactory->newDispatchContext();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $dispatchContext->set(
+ 'title',
+ $title
+ );
+
+ $this->assertListenerExecuteFor(
+ 'cached.propertyvalues.prefetcher.reset',
+ $instance,
+ $dispatchContext
+ );
+ }
+
+ public function verifyCachedPrefetcherResetEvent( EventListenerCollection $instance ) {
+
+ $dispatchContext = $this->eventDispatcherFactory->newDispatchContext();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $dispatchContext->set(
+ 'title',
+ $title
+ );
+
+ $this->assertListenerExecuteFor(
+ 'cached.prefetcher.reset',
+ $instance,
+ $dispatchContext
+ );
+ }
+
+ public function verifyCachedUpdateMarkerDeleteEvent( EventListenerCollection $instance ) {
+
+ $dispatchContext = $this->eventDispatcherFactory->newDispatchContext();
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->atLeastOnce() )
+ ->method( 'getHash' );
+
+ $dispatchContext->set(
+ 'subject',
+ $subject
+ );
+
+ $this->assertListenerExecuteFor(
+ 'cached.update.marker.delete',
+ $instance,
+ $dispatchContext
+ );
+ }
+
+ private function assertListenerExecuteFor( $eventName, $instance, $dispatchContext = null ) {
+
+ $executed = false;
+
+ foreach ( $instance->getCollection() as $event => $listeners ) {
+
+ if ( $eventName !== $event ) {
+ continue;
+ }
+
+ foreach ( $listeners as $listener ) {
+ $listener->execute( $dispatchContext );
+ $executed = true;
+ }
+ }
+
+ $this->assertTrue(
+ $executed,
+ "Failed asseting that '{$eventName}' was executed"
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemDeserializationExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemDeserializationExceptionTest.php
new file mode 100644
index 00000000..a3747620
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemDeserializationExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\DataItemDeserializationException;
+
+/**
+ * @covers \SMW\Exception\DataItemDeserializationException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemDeserializationExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new DataItemDeserializationException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\DataItemDeserializationException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\SMW\Exception\DataItemException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemExceptionTest.php
new file mode 100644
index 00000000..46bc76ae
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataItemExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\DataItemException;
+
+/**
+ * @covers \SMW\Exception\DataItemException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new DataItemException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\DataItemException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataTypeLookupExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataTypeLookupExceptionTest.php
new file mode 100644
index 00000000..7b7ee5e6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/DataTypeLookupExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\DataTypeLookupException;
+
+/**
+ * @covers \SMW\Exception\DataTypeLookupException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataTypeLookupExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new DataTypeLookupException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\DataTypeLookupException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotReadableExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotReadableExceptionTest.php
new file mode 100644
index 00000000..fa98e353
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotReadableExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\FileNotReadableException;
+
+/**
+ * @covers \SMW\Exception\FileNotReadableException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileNotReadableExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new FileNotReadableException( 'Foo' );
+
+ $this->assertInstanceof(
+ FileNotReadableException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotWritableExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotWritableExceptionTest.php
new file mode 100644
index 00000000..5c9f3cac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/FileNotWritableExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\FileNotWritableException;
+
+/**
+ * @covers \SMW\Exception\FileNotWritableException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileNotWritableExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new FileNotWritableException( 'Foo' );
+
+ $this->assertInstanceof(
+ FileNotWritableException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/ParameterNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/ParameterNotFoundExceptionTest.php
new file mode 100644
index 00000000..0421c3c4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/ParameterNotFoundExceptionTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\ParameterNotFoundException;
+
+/**
+ * @covers \SMW\Exception\ParameterNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParameterNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new ParameterNotFoundException( 'foo' );
+
+ $this->assertInstanceof(
+ ParameterNotFoundException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\InvalidArgumentException',
+ $instance
+ );
+ }
+
+ public function testGetName() {
+
+ $instance = new ParameterNotFoundException( 'bar' );
+
+ $this->assertEquals(
+ 'bar',
+ $instance->getName()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PredefinedPropertyLabelMismatchExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PredefinedPropertyLabelMismatchExceptionTest.php
new file mode 100644
index 00000000..d69371b7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PredefinedPropertyLabelMismatchExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+
+/**
+ * @covers \SMW\Exception\PredefinedPropertyLabelMismatchException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyLabelMismatchExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new PredefinedPropertyLabelMismatchException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\PredefinedPropertyLabelMismatchException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\SMW\Exception\PropertyLabelNotResolvedException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyLabelNotResolvedExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyLabelNotResolvedExceptionTest.php
new file mode 100644
index 00000000..8862221e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyLabelNotResolvedExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\PropertyLabelNotResolvedException;
+
+/**
+ * @covers \SMW\Exception\PropertyLabelNotResolvedException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyLabelNotResolvedExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new PropertyLabelNotResolvedException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\PropertyLabelNotResolvedException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\SMW\Exception\DataItemException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyNotFoundExceptionTest.php
new file mode 100644
index 00000000..a79cb36b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/PropertyNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\PropertyNotFoundException;
+
+/**
+ * @covers \SMW\Exception\PropertyNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new PropertyNotFoundException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\PropertyNotFoundException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/RedirectTargetUnresolvableExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/RedirectTargetUnresolvableExceptionTest.php
new file mode 100644
index 00000000..686f7085
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/RedirectTargetUnresolvableExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\RedirectTargetUnresolvableException;
+
+/**
+ * @covers \SMW\Exception\RedirectTargetUnresolvableException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RedirectTargetUnresolvableExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new RedirectTargetUnresolvableException();
+
+ $this->assertInstanceof(
+ RedirectTargetUnresolvableException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SemanticDataImportExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SemanticDataImportExceptionTest.php
new file mode 100644
index 00000000..1ae33912
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SemanticDataImportExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\SemanticDataImportException;
+
+/**
+ * @covers \SMW\Exception\SemanticDataImportException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SemanticDataImportExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SemanticDataImportException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\SemanticDataImportException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SettingNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SettingNotFoundExceptionTest.php
new file mode 100644
index 00000000..5d920793
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SettingNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\SettingNotFoundException;
+
+/**
+ * @covers \SMW\Exception\SettingNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SettingNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SettingNotFoundException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\SettingNotFoundException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/StoreNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/StoreNotFoundExceptionTest.php
new file mode 100644
index 00000000..2c5f3293
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/StoreNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\StoreNotFoundException;
+
+/**
+ * @covers \SMW\Exception\StoreNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class StoreNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new StoreNotFoundException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\StoreNotFoundException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SubSemanticDataExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SubSemanticDataExceptionTest.php
new file mode 100644
index 00000000..0d7721fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exception/SubSemanticDataExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Exception;
+
+use SMW\Exception\SubSemanticDataException;
+
+/**
+ * @covers \SMW\Exception\SubSemanticDataException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SubSemanticDataExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SubSemanticDataException();
+
+ $this->assertInstanceof(
+ '\SMW\Exception\SubSemanticDataException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ConceptMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ConceptMapperTest.php
new file mode 100644
index 00000000..a85346d2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ConceptMapperTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\ConceptMapper;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Query\DescriptionFactory;
+
+/**
+ * @covers \SMW\Exporter\ConceptMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ConceptMapperTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionFactory;
+
+ protected function setUp() {
+ $this->descriptionFactory = new DescriptionFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConceptMapper::class,
+ new ConceptMapper()
+ );
+ }
+
+ public function testIsMapperFor() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDIConcept' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConceptMapper();
+
+ $this->assertTrue(
+ $instance->isMapperFor( $dataItem )
+ );
+ }
+
+ public function testGetExpDataForSingleClassDescription() {
+
+ $instance = new ConceptMapper();
+
+ $exact = false;
+
+ $description = $this->descriptionFactory->newClassDescription(
+ DIWikiPage::newFromText( 'Foo', NS_CATEGORY )
+ );
+
+ $element = new ExpNsResource(
+ 'type',
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'rdf'
+ );
+
+ $result = $instance->getExpDataFromDescription(
+ $description,
+ $exact
+ );
+
+ $this->assertInstanceOf(
+ '\SMWExpData',
+ $result
+ );
+
+ $this->assertEquals(
+ [
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => $element
+ ],
+ $result->getProperties()
+ );
+ }
+
+ public function testGetExpDataForMultipleClassDescriptions() {
+
+ $instance = new ConceptMapper();
+
+ $exact = false;
+
+ $description = $this->descriptionFactory->newClassDescription(
+ DIWikiPage::newFromText( 'Foo', NS_CATEGORY )
+ );
+
+ $description->addDescription(
+ $this->descriptionFactory->newClassDescription(
+ DIWikiPage::newFromText( 'Bar', NS_CATEGORY )
+ )
+ );
+
+ $elementType = new ExpNsResource(
+ 'type',
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'rdf'
+ );
+
+ $elementUnionOf = new ExpNsResource(
+ 'unionOf',
+ 'http://www.w3.org/2002/07/owl#',
+ 'owl'
+ );
+
+ $result = $instance->getExpDataFromDescription(
+ $description,
+ $exact
+ );
+
+ $this->assertEquals(
+ [
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => $elementType,
+ 'http://www.w3.org/2002/07/owl#unionOf' => $elementUnionOf
+ ],
+ $result->getProperties()
+ );
+ }
+
+ public function testGetExpDataForThingDescription() {
+
+ $instance = new ConceptMapper();
+
+ $exact = false;
+
+ $description = $this->descriptionFactory->newThingDescription();
+
+ $result = $instance->getExpDataFromDescription(
+ $description,
+ $exact
+ );
+
+ $this->assertFalse(
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/DataItemMatchFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/DataItemMatchFinderTest.php
new file mode 100644
index 00000000..a0091932
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/DataItemMatchFinderTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\DataItemMatchFinder;
+
+/**
+ * @covers \SMW\Exporter\DataItemMatchFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DataItemMatchFinderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ DataItemMatchFinder::class,
+ new DataItemMatchFinder( $store )
+ );
+ }
+
+ public function testMatchExpElementOnMatchableWikiNamespaceUri() {
+
+ $dataItem = new DIWikiPage( 'Foo', NS_MAIN, '', 'Bar' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DataItemMatchFinder(
+ $store,
+ 'http://example.org/id/'
+ );
+
+ $expResource = $this->getMockBuilder( '\SMW\Exporter\Element\ExpResource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expResource->expects( $this->once() )
+ ->method( 'getUri' )
+ ->will( $this->returnValue( 'http://example.org/id/Foo#Bar' ) );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->matchExpElement( $expResource )
+ );
+ }
+
+ public function testMatchExpElementOnMatchableWikiNamespaceUriWithHelpWikiNs() {
+
+ $dataItem = new DIWikiPage( 'Foo', NS_HELP, '', 'Bar' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DataItemMatchFinder(
+ $store,
+ 'http://example.org/id/'
+ );
+
+ $expResource = $this->getMockBuilder( '\SMW\Exporter\Element\ExpResource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expResource->expects( $this->once() )
+ ->method( 'getUri' )
+ ->will( $this->returnValue( 'http://example.org/id/Help:Foo#Bar' ) );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->matchExpElement( $expResource )
+ );
+ }
+
+ public function testMatchExpElementOnUnmatchableWikiNamespaceUri() {
+
+ $dataItem = new DIWikiPage( 'UNKNOWN', NS_MAIN, '', '' );
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $repositoryConnector = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $repositoryConnector->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $store = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $repositoryConnector ) );
+
+ $instance = new DataItemMatchFinder(
+ $store,
+ 'http://example.org/id/'
+ );
+
+ $expResource = $this->getMockBuilder( '\SMW\Exporter\Element\ExpResource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expResource->expects( $this->once() )
+ ->method( 'getUri' )
+ ->will( $this->returnValue( 'http://foo.org/id/Foo#Bar' ) );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->matchExpElement( $expResource )
+ );
+ }
+
+ public function testTryToFindDataItemOnInvalidUri() {
+
+ $store = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataItemMatchFinder(
+ $store,
+ 'http://example.org/id/'
+ );
+
+ $expResource = $this->getMockBuilder( '\SMW\Exporter\Element\ExpResource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expResource->expects( $this->once() )
+ ->method( 'getUri' )
+ ->will( $this->returnValue( '_node1abjt1k9bx17' ) );
+
+ $this->assertNull(
+ $instance->matchExpElement( $expResource )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpElementTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpElementTest.php
new file mode 100644
index 00000000..d3d4d91e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpElementTest.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\Tests\Exporter\Element;
+
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\Element\ExpResource;
+
+/**
+ * @covers \SMW\Exporter\Element\ExpElement
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpElementTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = $this->getMockBuilder( '\SMW\Exporter\Element\ExpElement' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element',
+ $instance
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWExpElement',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testGetDataItem( ExpElement $element ) {
+
+ if ( $element->getDataItem() === null ) {
+ $this->assertNull(
+ $element->getDataItem()
+ );
+ } else {
+ $this->assertInstanceOf(
+ '\SMWDataItem',
+ $element->getDataItem()
+ );
+ }
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testSerielization( ExpElement $element ) {
+
+ $serialization = ExpElement::newFromSerialization(
+ $element->getSerialization()
+ );
+
+ $this->assertEquals(
+ $element->getDataItem(),
+ $serialization->getDataItem()
+ );
+ }
+
+ public function instanceProvider() {
+
+ $provider = [];
+
+ $provider[] = [ new ExpResource( 'foo' ) ];
+ $provider[] = [ new ExpResource( 'foo', null ) ];
+ $provider[] = [ new ExpResource( 'foo', new \SMWDIBlob( 'bar' ) ) ];
+
+ $provider[] = [ new ExpNsResource( 'foo', 'bar', 'baz' ) ];
+ $provider[] = [ new ExpNsResource( 'foo', 'bar', 'baz', null ) ];
+ $provider[] = [ new ExpNsResource( 'foo', 'bar', 'baz', new \SMWDIBlob( 'bar' ) ) ];
+
+ $provider[] = [ new ExpLiteral( 'foo' ) ];
+ $provider[] = [ new ExpLiteral( 'foo', '' ) ];
+ $provider[] = [ new ExpLiteral( 'foo', 'bar' ) ];
+ $provider[] = [ new ExpLiteral( 'foo', '', '', null ) ];
+ $provider[] = [ new ExpLiteral( 'foo', '', '', new \SMWDIBlob( 'bar' ) ) ];
+ $provider[] = [ new ExpLiteral( 'foo', 'baz', '', new \SMWDIBlob( 'bar' ) ) ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpLiteralTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpLiteralTest.php
new file mode 100644
index 00000000..35ec5df4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpLiteralTest.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\Tests\Exporter\Element;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpLiteral;
+use SMWDataItem as DataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Exporter\Element\ExpLiteral
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpLiteralTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element\ExpLiteral',
+ new ExpLiteral( '', '', '', null )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWExpLiteral',
+ new \SMWExpLiteral( '', '', '', null )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testAccessToMethods( $lexicalForm, $datatype, $lang, $dataItem ) {
+
+ $instance = new ExpLiteral(
+ $lexicalForm,
+ $datatype,
+ $lang,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $datatype,
+ $instance->getDatatype()
+ );
+
+ $this->assertEquals(
+ $lang,
+ $instance->getLang()
+ );
+
+ $this->assertEquals(
+ $lexicalForm,
+ $instance->getLexicalForm()
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->getDataItem()
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testSerializiation( $lexicalForm, $datatype, $lang, $dataItem, $expected ) {
+
+ $instance = new ExpLiteral(
+ $lexicalForm,
+ $datatype,
+ $lang,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getSerialization()
+ );
+
+ $this->assertEquals(
+ $instance,
+ ExpElement::newFromSerialization( $instance->getSerialization() )
+ );
+ }
+
+ /**
+ * @dataProvider invalidConstructorProvider
+ */
+ public function testInvalidConstructorThrowsException( $lexicalForm, $datatype, $lang, $dataItem ) {
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+
+ $instance = new ExpLiteral(
+ $lexicalForm,
+ $datatype,
+ $lang,
+ $dataItem
+ );
+ }
+
+ /**
+ * @dataProvider serializationMissingElementProvider
+ */
+ public function testDeserializiationForMissingElementThrowsException( $serialization ) {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ ExpElement::newFromSerialization(
+ $serialization
+ );
+ }
+
+ public function constructorProvider() {
+
+ #0
+ $provider[] = [
+ '', '', '', null,
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => '',
+ 'datatype' => '',
+ 'lang' => '',
+ 'dataitem' => null
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ 'Foo', '', '', null,
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'Foo',
+ 'datatype' => '',
+ 'lang' => '',
+ 'dataitem' => null
+ ]
+ ];
+
+ #2
+ $provider[] = [
+ 'Foo', 'bar', '', null,
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'Foo',
+ 'datatype' => 'bar',
+ 'lang' => '',
+ 'dataitem' => null
+ ]
+ ];
+
+ #3
+ $provider[] = [
+ 'Foo', 'bar', '', new DIWikiPage( 'Foo', NS_MAIN ),
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'Foo',
+ 'datatype' => 'bar',
+ 'lang' => '',
+ 'dataitem' => [
+ 'type' => DataItem::TYPE_WIKIPAGE,
+ 'item' => 'Foo#0##'
+ ]
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ 'Foo', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', 'en', new DIWikiPage( 'Foo', NS_MAIN ),
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'Foo',
+ 'datatype' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString',
+ 'lang' => 'en',
+ 'dataitem' => [
+ 'type' => DataItem::TYPE_WIKIPAGE,
+ 'item' => 'Foo#0##'
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function invalidConstructorProvider() {
+
+ #0
+ $provider[] = [
+ [], '', '', null
+ ];
+
+ #1
+ $provider[] = [
+ '', [], '', null
+ ];
+
+ #1
+ $provider[] = [
+ '', '', [], null
+ ];
+
+ return $provider;
+ }
+
+ public function serializationMissingElementProvider() {
+
+ #0
+ $provider[] = [
+ []
+ ];
+
+ #1 Missing dataitem
+ $provider[] = [
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL
+ ]
+ ];
+
+ #2 Bogus type
+ $provider[] = [
+ [
+ 'type' => 'BogusType'
+ ]
+ ];
+
+ #3 Missing uri
+ $provider[] = [
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'dataitem' => null
+ ]
+ ];
+
+ #4 Missing lexical
+ $provider[] = [
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'datatype' => 'foo',
+ 'dataitem' => null
+ ]
+ ];
+
+ #4 Missing datatype
+ $provider[] = [
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'foo',
+ 'dataitem' => null
+ ]
+ ];
+
+ #5 Missing lang
+ $provider[] = [
+ [
+ 'type' => ExpLiteral::TYPE_LITERAL,
+ 'lexical' => 'foo',
+ 'datatype' => 'foo',
+ 'dataitem' => null
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpNsResourceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpNsResourceTest.php
new file mode 100644
index 00000000..3bac57cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpNsResourceTest.php
@@ -0,0 +1,259 @@
+<?php
+
+namespace SMW\Tests\Exporter\Element;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpNsResource;
+use SMWDataItem as DataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Exporter\Element\ExpNsResource
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpNsResourceTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element\ExpNsResource',
+ new ExpNsResource( '', '', '', null )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWExpNsResource',
+ new \SMWExpNsResource( '', '', '', null )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testAccessToMethods( $localName, $namespace, $namespaceId, $dataItem ) {
+
+ $instance = new ExpNsResource(
+ $localName,
+ $namespace,
+ $namespaceId,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $namespaceId . ':' . $localName,
+ $instance->getQName()
+ );
+
+ $this->assertEquals(
+ $namespace . $localName,
+ $instance->getUri()
+ );
+
+ $this->assertEquals(
+ $localName,
+ $instance->getLocalName()
+ );
+
+ $this->assertEquals(
+ $namespace,
+ $instance->getNamespace()
+ );
+
+ $this->assertEquals(
+ $namespaceId,
+ $instance->getNamespaceId()
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->getDataItem()
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testSerializiation( $localName, $namespace, $namespaceId, $dataItem, $expected ) {
+
+ $instance = new ExpNsResource(
+ $localName,
+ $namespace,
+ $namespaceId,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getSerialization()
+ );
+
+ $this->assertEquals(
+ $instance,
+ ExpElement::newFromSerialization( $instance->getSerialization() )
+ );
+ }
+
+ /**
+ * @dataProvider invalidConstructorProvider
+ */
+ public function testInvalidConstructorThrowsException( $localName, $namespace, $namespaceId, $dataItem ) {
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+
+ $instance = new ExpNsResource(
+ $localName,
+ $namespace,
+ $namespaceId,
+ $dataItem
+ );
+ }
+
+ /**
+ * @dataProvider serializationMissingElementProvider
+ */
+ public function testDeserializiationForMissingElementThrowsException( $serialization ) {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ ExpElement::newFromSerialization(
+ $serialization
+ );
+ }
+
+ public function constructorProvider() {
+
+ #0
+ $provider[] = [
+ '', '', '', null,
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => '||',
+ 'dataitem' => null
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ 'Foo', '', '', null,
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => 'Foo||',
+ 'dataitem' => null
+ ]
+ ];
+
+ #2
+ $provider[] = [
+ 'Foo', 'Bar', '', null,
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => 'Foo|Bar|',
+ 'dataitem' => null
+ ]
+ ];
+
+ #3
+ $provider[] = [
+ 'Foo', 'Bar', 'Fum', null,
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => 'Foo|Bar|Fum',
+ 'dataitem' => null
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ 'Foo', 'Bar', 'Fum', new DIWikiPage( 'Foo', NS_MAIN ),
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => 'Foo|Bar|Fum',
+ 'dataitem' => [
+ 'type' => DataItem::TYPE_WIKIPAGE,
+ 'item' => 'Foo#0##'
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function invalidConstructorProvider() {
+
+ #0
+ $provider[] = [
+ [], '', '', null
+ ];
+
+ #1
+ $provider[] = [
+ '', [], '', null
+ ];
+
+ #2
+ $provider[] = [
+ '', '', [], null
+ ];
+
+ return $provider;
+ }
+
+ public function serializationMissingElementProvider() {
+
+ #0
+ $provider[] = [
+ []
+ ];
+
+ #1
+ $provider[] = [
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE
+ ]
+ ];
+
+ #2
+ $provider[] = [
+ [
+ 'type' => 'BogusType'
+ ]
+ ];
+
+ #3
+ $provider[] = [
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'dataitem' => null
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => '',
+ 'dataitem' => null
+ ]
+ ];
+
+ #5
+ $provider[] = [
+ [
+ 'type' => ExpNsResource::TYPE_NSRESOURCE,
+ 'uri' => '|',
+ 'dataitem' => null
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpResourceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpResourceTest.php
new file mode 100644
index 00000000..885584ac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/Element/ExpResourceTest.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace SMW\Tests\Exporter\Element;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Element\ExpElement;
+use SMW\Exporter\Element\ExpResource;
+use SMWDataItem as DataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Exporter\Element\ExpResource
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpResourceTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element\ExpResource',
+ new ExpResource( '', null )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWExpResource',
+ new \SMWExpResource( '', null )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testAccessToMethods( $uri, $dataItem, $isBlankNode ) {
+
+ $instance = new ExpResource(
+ $uri,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $isBlankNode,
+ $instance->isBlankNode()
+ );
+
+ $this->assertEquals(
+ $uri,
+ $instance->getUri()
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->getDataItem()
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testSerializiation( $uri, $dataItem, $isBlankNode, $expected ) {
+
+ $instance = new ExpResource(
+ $uri,
+ $dataItem
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getSerialization()
+ );
+
+ $this->assertEquals(
+ $instance,
+ ExpElement::newFromSerialization( $instance->getSerialization() )
+ );
+ }
+
+ /**
+ * @dataProvider invalidConstructorProvider
+ */
+ public function testInvalidConstructorThrowsException( $uri, $dataItem ) {
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+
+ $instance = new ExpResource(
+ $uri,
+ $dataItem
+ );
+ }
+
+ /**
+ * @dataProvider serializationMissingElementProvider
+ */
+ public function testDeserializiationForMissingElementThrowsException( $serialization ) {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ ExpElement::newFromSerialization(
+ $serialization
+ );
+ }
+
+ public function constructorProvider() {
+
+ #0
+ $provider[] = [
+ '', null,
+ true,
+ [
+ 'type' => ExpResource::TYPE_RESOURCE,
+ 'uri' => '',
+ 'dataitem' => null
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ 'Foo', null,
+ false,
+ [
+ 'type' => ExpResource::TYPE_RESOURCE,
+ 'uri' => 'Foo',
+ 'dataitem' => null
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ 'Foo', new DIWikiPage( 'Foo', NS_MAIN ),
+ false,
+ [
+ 'type' => ExpResource::TYPE_RESOURCE,
+ 'uri' => 'Foo',
+ 'dataitem' => [
+ 'type' => DataItem::TYPE_WIKIPAGE,
+ 'item' => 'Foo#0##'
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function invalidConstructorProvider() {
+
+ #0
+ $provider[] = [
+ [], null
+ ];
+
+ return $provider;
+ }
+
+ public function serializationMissingElementProvider() {
+
+ #0
+ $provider[] = [
+ []
+ ];
+
+ #1 Missing dataitem
+ $provider[] = [
+ [
+ 'type' => ExpResource::TYPE_RESOURCE
+ ]
+ ];
+
+ #2 Bogus type
+ $provider[] = [
+ [
+ 'type' => 'BogusType'
+ ]
+ ];
+
+ #3 Missing uri
+ $provider[] = [
+ [
+ 'type' => ExpResource::TYPE_RESOURCE,
+ 'dataitem' => null
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ElementFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ElementFactoryTest.php
new file mode 100644
index 00000000..8dcadf6f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ElementFactoryTest.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\ElementFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Exporter\ElementFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ElementFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ /**
+ * @dataProvider supportedDataItemProvider
+ */
+ public function testNewFromDataItemForSupportedTypes( $dataItem ) {
+
+ $instance = new ElementFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element',
+ $instance->newFromDataItem( $dataItem )
+ );
+ }
+
+ /**
+ * @dataProvider unsupportedDataItemProvider
+ */
+ public function testUnsupportedDataItemTypes( $dataItem ) {
+
+ $instance = new ElementFactory();
+
+ $this->assertNull(
+ $instance->newFromDataItem( $dataItem )
+ );
+ }
+
+ public function testNotSupportedEncoderResultThrowsException() {
+
+ $dataItemFactory = new DataItemFactory();
+ $instance = new ElementFactory();
+
+ $instance->registerDataItemMapper( \SMWDataItem::TYPE_BLOB, function( $datatem ) {
+ return new \stdclass;
+ } );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->newFromDataItem( $dataItemFactory->newDIBlob( 'foo' ) );
+ }
+
+ public function supportedDataItemProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ #0
+ $provider[] = [
+ $dataItemFactory->newDINumber( 42 )
+ ];
+
+ #1
+ $provider[] = [
+ $dataItemFactory->newDIBlob( 'Test' )
+ ];
+
+ #2
+ $provider[] = [
+ $dataItemFactory->newDIBoolean( true )
+ ];
+
+ #3
+ $provider[] = [
+ $dataItemFactory->newDIUri( 'http', '//example.org', '', '' )
+ ];
+
+ #4
+ $provider[] = [
+ $dataItemFactory->newDITime( 1, '1970' )
+ ];
+
+ #5
+ $provider[] = [
+ $dataItemFactory->newDIContainer( new \SMWContainerSemanticData( $dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ) ) )
+ ];
+
+ #6
+ $provider[] = [
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN )
+ ];
+
+ #7
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' )
+ ];
+
+ #8
+ $provider[] = [
+ $dataItemFactory->newDIConcept( 'Foo', '', '', '', '' )
+ ];
+
+ return $provider;
+ }
+
+ public function unsupportedDataItemProvider() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ '__toString' ] )
+ ->getMockForAbstractClass();
+
+ $dataItem->expects( $this->any() )
+ ->method( '__toString' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ #0
+ $provider[] = [
+ $dataItem
+ ];
+
+ #1
+ $provider[] = [
+ new \SMWDIGeoCoord( [ 'lat' => 52, 'lon' => 1 ] )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/EscaperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/EscaperTest.php
new file mode 100644
index 00000000..555b3534
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/EscaperTest.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Escaper;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Exporter\Escaper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EscaperTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->addConfiguration( 'smwgExportResourcesAsIri', false );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider encodePageProvider
+ */
+ public function testEncodePage( $page, $expected ) {
+
+ $this->assertSame(
+ $expected,
+ Escaper::encodePage( $page )
+ );
+ }
+
+
+ /**
+ * @dataProvider encodeUriProvider
+ */
+ public function testEncodeUri( $uri, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ Escaper::encodeUri( $uri )
+ );
+
+ $this->assertEquals(
+ $uri,
+ Escaper::decodeUri( Escaper::encodeUri( $uri ) )
+ );
+ }
+
+ /**
+ * @dataProvider decodeUriProvider
+ */
+ public function testDecodeUri( $uri, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ Escaper::decodeUri( $uri )
+ );
+
+ $this->assertEquals(
+ $uri,
+ Escaper::encodeUri( Escaper::decodeUri( $uri ) )
+ );
+ }
+
+ public function encodeUriProvider() {
+
+ $provider[] = [
+ 'Foo:"&+!%#',
+ 'Foo-3A-22-26-2B-21-25-23'
+ ];
+
+ $provider[] = [
+ "Foo'-'",
+ 'Foo-27-2D-27'
+ ];
+ return $provider;
+ }
+
+ public function decodeUriProvider() {
+
+ $provider[] = [
+ 'Foo-3A-22-26-2B-21-25-23',
+ 'Foo:"&+!%#'
+ ];
+
+ $provider[] = [
+ 'Foo-27-2D-27',
+ "Foo'-'"
+ ];
+
+ return $provider;
+ }
+
+ public function encodePageProvider() {
+
+ #0
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, '', '' )
+ , 'Foo'
+ ];
+
+ #1
+ $provider[] = [
+ new DIWikiPage( 'Foo_bar', NS_MAIN, '', '' ),
+ 'Foo_bar'
+ ];
+
+ #2
+ $provider[] = [
+ new DIWikiPage( 'Foo%bar', NS_MAIN, '', '' ),
+ 'Foo-25bar'
+ ];
+
+ #3 / #759
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, 'bar', '' ),
+ 'bar-3AFoo'
+ ];
+
+ #4 / #759
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, 'bar', 'yuu' ),
+ 'bar-3AFoo'
+ ];
+
+ #5
+ $provider[] = [
+ new DIWikiPage( 'Fooºr', NS_MAIN, '', '' ),
+ 'Foo-C2-BAr'
+ ];
+
+ #6
+ $provider[] = [
+ new DIWikiPage( 'Fooºr', SMW_NS_PROPERTY, '', '' ),
+ 'Property-3AFoo-C2-BAr'
+ ];
+
+ #7
+ $provider[] = [
+ new DIWikiPage( 'Fooºr', NS_CATEGORY, '', '' ),
+ 'Category-3AFoo-C2-BAr'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ExpResourceMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ExpResourceMapperTest.php
new file mode 100644
index 00000000..c1eeb876
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ExpResourceMapperTest.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\Element;
+use SMW\Exporter\Escaper;
+use SMW\Exporter\ExpResourceMapper;
+use SMW\InMemoryPoolCache;
+
+/**
+ * @covers \SMW\Exporter\ExpResourceMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpResourceMapperTest extends \PHPUnit_Framework_TestCase {
+
+ private $inMemoryPoolCache;
+
+ protected function setUp() {
+ $this->inMemoryPoolCache = InMemoryPoolCache::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->inMemoryPoolCache->clear();
+ }
+
+ public function testInvalidateCache() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $poolCache = $this->inMemoryPoolCache->getPoolCacheById( 'exporter.expresource.mapper' );
+
+ $poolCache->save(
+ $subject->getHash(),
+ true
+ );
+
+ $poolCache->save(
+ $subject->getHash() . ExpResourceMapper::AUX_MARKER,
+ true
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ExpResourceMapper(
+ $store
+ );
+
+ $instance->invalidateCache(
+ $subject
+ );
+
+ $this->assertFalse(
+ $poolCache->contains( $subject->getHash() )
+ );
+ }
+
+ public function testMapPropertyToResourceElement() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ExpResourceMapper(
+ $store
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Exporter\Element\ExpNsResource',
+ $instance->mapPropertyToResourceElement( new DIProperty( 'Foo' ) )
+ );
+ }
+
+ /**
+ * @dataProvider diWikiPageProvider
+ */
+ public function testMapWikiPageToResourceElement( $dataItem, $modifier, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ExpResourceMapper(
+ $store
+ );
+
+ $resource = $instance->mapWikiPageToResourceElement( $dataItem, $modifier );
+
+ $this->assertSame(
+ $expected,
+ $resource->getSerialization()
+ );
+ }
+
+ /**
+ * @dataProvider importDataProvider
+ */
+ public function testMapWikiPageToResourceElementForImportMatch( $dataItem, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will(
+ $this->returnValue( [ new \SMWDIBlob( 'foo:bar:fom:fuz' ) ] ) );
+
+ $instance = new ExpResourceMapper(
+ $store
+ );
+
+ $resource = $instance->mapWikiPageToResourceElement(
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $resource->isImported()
+ );
+
+ $this->assertSame(
+ $expected,
+ $resource->getSerialization()
+ );
+ }
+
+ public function diWikiPageProvider() {
+
+ // Constant
+ $wiki = \SMWExporter::getInstance()->getNamespaceUri( 'wiki' );
+ $property = \SMWExporter::getInstance()->getNamespaceUri( 'property' );
+
+ #0
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, '', '' ),
+ '',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "Foo|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#0##' ]
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, 'bar', '' ),
+ '',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "bar-3AFoo|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#0#bar#' ]
+ ]
+ ];
+
+ #2
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, 'bar', '1234' ),
+ '',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "bar-3AFoo-231234|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#0#bar#1234' ]
+ ]
+ ];
+
+ #3 Extra modififer doesn't not alter the object when a subobject is used
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN, 'bar', '1234' ),
+ 'abc',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "bar-3AFoo-231234|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#0#bar#1234' ]
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, '', '' ),
+ '',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "Foo|{$property}|property",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#102##' ]
+ ]
+ ];
+
+ #5
+ $provider[] = [
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, '', '' ),
+ true,
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "Foo-23aux|{$property}|property",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#102##' ]
+ ]
+ ];
+
+ #6
+ $name = Escaper::encodePage(
+ new DIWikiPage( '-Foo', SMW_NS_PROPERTY, '', '' )
+ );
+
+ $provider[] = [
+ new DIWikiPage( '-Foo', SMW_NS_PROPERTY, '', '' ),
+ true,
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "$name-23aux|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => '-Foo#102##' ]
+ ]
+ ];
+
+ #7
+ $provider[] = [
+ new DIWikiPage( 'Foo/Bar', SMW_NS_PROPERTY, '', '' ),
+ '',
+ [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "Property-3AFoo-2FBar|{$wiki}|wiki",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo/Bar#102##' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function importDataProvider() {
+
+ // || is not the result we normally would expect but mocking the
+ // dataValueFactory at this point is not worth the hassle therefore
+ // we live with || output
+ $expected = [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "||",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#102##' ]
+ ];
+
+ $provider[] = [
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, '', '' ),
+ $expected
+ ];
+
+ $expected = [
+ 'type' => Element::TYPE_NSRESOURCE,
+ 'uri' => "||",
+ 'dataitem' => [ 'type' => 9, 'item' => 'Foo#14##' ]
+ ];
+
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_CATEGORY, '', '' ),
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..45f91d3d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/AuxiliaryPropertyValueResourceBuilderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\AuxiliaryPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\AuxiliaryPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class AuxiliaryPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ AuxiliaryPropertyValueResourceBuilder::class,
+ new AuxiliaryPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonImpoProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new AuxiliaryPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForSelectedAuxiliaryProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_dat' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new AuxiliaryPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..2a69db33
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ConceptPropertyValueResourceBuilderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\ConceptPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\ConceptPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ConceptPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ ConceptPropertyValueResourceBuilder::class,
+ new ConceptPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonConcProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new ConceptPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForConcProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_CONC' );
+ $dataItem = $this->dataItemFactory->newDIConcept( 'Foo' );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new ConceptPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/DispatchingResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/DispatchingResourceBuilderTest.php
new file mode 100644
index 00000000..27156d12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/DispatchingResourceBuilderTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\ResourceBuilder;
+use SMW\Exporter\ResourceBuilders\DispatchingResourceBuilder;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\DispatchingResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DispatchingResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ DispatchingResourceBuilder::class,
+ new DispatchingResourceBuilder()
+ );
+ }
+
+ public function testIsResourceBuilderForValidMatch() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $resourceBuilder = $this->getMockBuilder( ResourceBuilder::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resourceBuilder->expects( $this->once() )
+ ->method( 'isResourceBuilderFor' )
+ ->with( $this->equalTo( $property ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DispatchingResourceBuilder();
+ $instance->addResourceBuilder( $resourceBuilder );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testIsResourceBuilderForInvalidMatch() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new DispatchingResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueOnValidMatchedResourceBuilder() {
+
+ $expData = $this->getMockBuilder( '\SMWExpData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $dataItem = $this->dataItemFactory->newDIBlob( 'Bar' );
+
+ $resourceBuilder = $this->getMockBuilder( ResourceBuilder::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resourceBuilder->expects( $this->once() )
+ ->method( 'isResourceBuilderFor' )
+ ->will( $this->returnValue( true ) );
+
+ $resourceBuilder->expects( $this->once() )
+ ->method( 'addResourceValue' );
+
+ $instance = new DispatchingResourceBuilder();
+ $instance->addResourceBuilder( $resourceBuilder );
+
+ $instance->addResourceValue( $expData, $property, $dataItem );
+ }
+
+ public function testAddResourceValueOnDefaultResourceBuilderWhenOthersCannotMatch() {
+
+ $expData = $this->getMockBuilder( '\SMWExpData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $dataItem = $this->dataItemFactory->newDIBlob( 'Bar' );
+
+ $resourceBuilder = $this->getMockBuilder( ResourceBuilder::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resourceBuilder->expects( $this->once() )
+ ->method( 'isResourceBuilderFor' )
+ ->will( $this->returnValue( false ) );
+
+ $defaultResourceBuilder = $this->getMockBuilder( ResourceBuilder::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $defaultResourceBuilder->expects( $this->once() )
+ ->method( 'addResourceValue' );
+
+ $instance = new DispatchingResourceBuilder();
+
+ $instance->addResourceBuilder( $resourceBuilder );
+ $instance->addDefaultResourceBuilder( $defaultResourceBuilder );
+
+ $instance->addResourceValue( $expData, $property, $dataItem );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..754bab14
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ExternalIdentifierPropertyValueResourceBuilderTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\ExternalIdentifierPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\ExternalIdentifierPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExternalIdentifierPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ ExternalIdentifierPropertyValueResourceBuilder::class,
+ new ExternalIdentifierPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonExternalIdentifierTypedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new ExternalIdentifierPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForValidProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_eid' );
+
+ $dataItem = $this->dataItemFactory->newDIBlob( 'Bar' );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new ExternalIdentifierPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..a37c2612
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/ImportFromPropertyValueResourceBuilderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\ImportFromPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\ImportFromPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImportFromPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ ImportFromPropertyValueResourceBuilder::class,
+ new ImportFromPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonImpoProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new ImportFromPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForImpoProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_IMPO' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN ) )
+ );
+
+ $instance = new ImportFromPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..048212b3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/KeywordPropertyValueResourceBuilderTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\KeywordPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\KeywordPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class KeywordPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ KeywordPropertyValueResourceBuilder::class,
+ new KeywordPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonExternalIdentifierTypedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new KeywordPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForValidProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_keyw' );
+
+ $dataItem = $this->dataItemFactory->newDIBlob( 'Bar' );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new KeywordPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..36753052
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/MonolingualTextPropertyValueResourceBuilderTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\MonolingualTextPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\MonolingualTextPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MonolingualTextPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ MonolingualTextPropertyValueResourceBuilder::class,
+ new MonolingualTextPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonExternalIdentifierTypedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new MonolingualTextPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForValidProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_mlt_rec' );
+
+ $monolingualTextValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ 'Bar@en'
+ );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new MonolingualTextPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $monolingualTextValue->getDataItem()
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..2d42d338
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PredefinedPropertyValueResourceBuilderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\PredefinedPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\PredefinedPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ PredefinedPropertyValueResourceBuilder::class,
+ new PredefinedPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForUserDefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new PredefinedPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForPredefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_boo' );
+ $dataItem = $this->dataItemFactory->newDIBoolean( true );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new PredefinedPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilderTest.php
new file mode 100644
index 00000000..7c0e5934
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PreferredPropertyLabelResourceBuilderTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\PreferredPropertyLabelResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\PreferredPropertyLabelResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PreferredPropertyLabelResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ PreferredPropertyLabelResourceBuilder::class,
+ new PreferredPropertyLabelResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonPreferredPropertyLabelProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new PreferredPropertyLabelResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForValidProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_PPLB' );
+
+ $monolingualTextValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ 'Bar@en'
+ );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new PreferredPropertyLabelResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $monolingualTextValue->getDataItem()
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilderTest.php
new file mode 100644
index 00000000..2cbd9275
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyDescriptionValueResourceBuilderTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\PropertyDescriptionValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\PropertyDescriptionValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyDescriptionValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ PropertyDescriptionValueResourceBuilder::class,
+ new PropertyDescriptionValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForPropertyDescription() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new PropertyDescriptionValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForPropertyDescription() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_PDESC' );
+
+ $monolingualTextValue = $this->dataValueFactory->newDataValueByProperty(
+ $property,
+ 'Some description@en'
+ );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new PropertyDescriptionValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $monolingualTextValue->getDataItem()
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..8fc83d3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/PropertyValueResourceBuilderTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\PropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\PropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ PropertyValueResourceBuilder::class,
+ new PropertyValueResourceBuilder()
+ );
+ }
+
+ public function testAddResourceValue() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new PropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..9e01f6e4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/RedirectPropertyValueResourceBuilderTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\RedirectPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\RedirectPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RedirectPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ RedirectPropertyValueResourceBuilder::class,
+ new RedirectPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonRediProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new RedirectPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForRediPropert() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_REDI' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new RedirectPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/SortPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/SortPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..41c27c34
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/SortPropertyValueResourceBuilderTest.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\SortPropertyValueResourceBuilder;
+use SMW\Serializers\ExpDataSerializer;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\SortPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $testEnvironment;
+ private $expDataSerializer;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+ $this->expDataSerializer = new ExpDataSerializer();
+
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ SortPropertyValueResourceBuilder::class,
+ new SortPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNon_SKEYProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new SortPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testIsResourceBuilderFor_SKEYProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_SKEY' );
+
+ $instance = new SortPropertyValueResourceBuilder();
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueFor_SKEYProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_SKEY' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new SortPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $res = json_encode( $this->expDataSerializer->serialize( $expData ) );
+
+ $this->assertNotContains(
+ 'sort|http:\/\/semantic-mediawiki.org\/swivt\/1.0#|swivt',
+ $res
+ );
+ }
+
+ public function testAddResourceValueFor_SKEYPropertyWithEnabledCollationField() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_SKEY' );
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new SortPropertyValueResourceBuilder();
+ $instance->enabledCollationField( true );
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $res = json_encode( $this->expDataSerializer->serialize( $expData ) );
+
+ $this->assertContains(
+ 'sort|http:\/\/semantic-mediawiki.org\/swivt\/1.0#|swivt',
+ $res
+ );
+
+ $this->assertContains(
+ 'http:\/\/www.w3.org\/2001\/XMLSchema#string',
+ $res
+ );
+ }
+
+ public function testAddResourceValueFor_SKEYPropertyWithEnabledCollationFieldOnBlobItem() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_SKEY' );
+ $dataItem = $this->dataItemFactory->newDIBlob( 'Bar' );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', '', '', null )
+ );
+
+ $instance = new SortPropertyValueResourceBuilder();
+ $instance->enabledCollationField( true );
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $dataItem
+ );
+
+ $res = json_encode( $this->expDataSerializer->serialize( $expData ) );
+
+ $this->assertContains(
+ 'sort|http:\/\/semantic-mediawiki.org\/swivt\/1.0#|swivt',
+ $res
+ );
+
+ $this->assertContains(
+ 'http:\/\/www.w3.org\/2001\/XMLSchema#string',
+ $res
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilderTest.php
new file mode 100644
index 00000000..a5f0e1be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/ResourceBuilders/UniquenessConstraintPropertyValueResourceBuilderTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Exporter\ResourceBuilders;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Exporter\ResourceBuilders\UniquenessConstraintPropertyValueResourceBuilder;
+use SMW\Tests\TestEnvironment;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMW\Exporter\ResourceBuilders\UniquenessConstraintPropertyValueResourceBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class UniquenessConstraintPropertyValueResourceBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->resetPoolCacheById( \SMWExporter::POOLCACHE_ID );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ UniquenessConstraintPropertyValueResourceBuilder::class,
+ new UniquenessConstraintPropertyValueResourceBuilder()
+ );
+ }
+
+ public function testIsNotResourceBuilderForNonUniquenessConstraintProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new UniquenessConstraintPropertyValueResourceBuilder();
+
+ $this->assertFalse(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+ public function testAddResourceValueForValidProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_PVUC' );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foobar', 'Bar', 'Mo', null )
+ );
+
+ $instance = new UniquenessConstraintPropertyValueResourceBuilder();
+
+ $instance->addResourceValue(
+ $expData,
+ $property,
+ $this->dataItemFactory->newDIBoolean( true )
+ );
+
+ $this->assertTrue(
+ $instance->isResourceBuilderFor( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/XsdValueMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/XsdValueMapperTest.php
new file mode 100644
index 00000000..ced69e1a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Exporter/XsdValueMapperTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\XsdValueMapper;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Exporter\XsdValueMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class XsdValueMapperTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ /**
+ * @dataProvider supportedDataItemProvider
+ */
+ public function testMatchSupportedTypes( $dataItem, $xsdValue, $xsdType ) {
+
+ $instance = new XsdValueMapper();
+
+ $instance->map( $dataItem );
+
+ $this->assertEquals(
+ $xsdValue,
+ $instance->getXsdValue()
+ );
+
+ $this->assertContains(
+ $xsdType,
+ $instance->getXsdType()
+ );
+ }
+
+ /**
+ * @dataProvider unsupportedDataItemProvider
+ */
+ public function testTryToMatchUnsupportedTypeThrowsException( $dataItem ) {
+
+ $instance = new XsdValueMapper();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->map( $dataItem );
+ }
+
+ public function supportedDataItemProvider() {
+
+ #0
+ $provider[] = [
+ new \SMWDINumber( 42 ),
+ '42',
+ 'double'
+ ];
+
+ #1
+ $provider[] = [
+ new \SMWDIBlob( 'Test' ),
+ 'Test',
+ 'string'
+ ];
+
+ #2
+ $provider[] = [
+ new \SMWDIBoolean( true ),
+ 'true',
+ 'boolean'
+ ];
+
+ #3
+ $provider[] = [
+ new \SMWDITime( 1, '1970' ),
+ '1970',
+ 'gYear'
+ ];
+
+ #4
+ $provider[] = [
+ new \SMWDITime( 1, '1970', '12' ),
+ '1970-12',
+ 'gYearMonth'
+ ];
+
+ #5
+ $provider[] = [
+ new \SMWDITime( 1, '1970', '12', '31' ),
+ '1970-12-31Z',
+ 'date'
+ ];
+
+ #6
+ $provider[] = [
+ new \SMWDITime( 1, '1970', '12', '31', '12' ),
+ '1970-12-31T12:00:00Z',
+ 'dateTime'
+ ];
+
+ return $provider;
+ }
+
+ public function unsupportedDataItemProvider() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ '__toString' ] )
+ ->getMockForAbstractClass();
+
+ $dataItem->expects( $this->any() )
+ ->method( '__toString' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ #0
+ $provider[] = [
+ $dataItem
+ ];
+
+ #1
+ $provider[] = [
+ new \SMWDIGeoCoord( [ 'lat' => 52, 'lon' => 1 ] )
+ ];
+
+ #2
+ $provider[] = [
+ new \SMWDIConcept( 'Foo', '', '', '', '' )
+ ];
+
+ #3
+ $provider[] = [
+ new \SMWDIUri( 'http', '//example.org', '', '' )
+ ];
+
+ #4
+ $provider[] = [
+ new \SMWDIContainer( new \SMWContainerSemanticData( new DIWikiPage( 'Foo', NS_MAIN ) ) )
+ ];
+
+ #5
+ $provider[] = [
+ new DIWikiPage( 'Foo', NS_MAIN )
+ ];
+
+ #6
+ $provider[] = [
+ new DIProperty( 'Foo' )
+ ];
+
+ #7 Not a gregorian calendar model
+ $provider[] = [
+ new \SMWDITime( 2, '1970' )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/CachedFactboxTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/CachedFactboxTest.php
new file mode 100644
index 00000000..12dce804
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/CachedFactboxTest.php
@@ -0,0 +1,535 @@
+<?php
+
+namespace SMW\Tests\Factbox;
+
+use Language;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Factbox\CachedFactbox;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\Factbox\CachedFactbox
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class CachedFactboxTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $memoryCache;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->memoryCache = ApplicationFactory::getInstance()->newCacheFactory()->newFixedInMemoryCache();
+
+ $this->testEnvironment->withConfiguration(
+ [
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgMainCacheType' => 'hash'
+ ]
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Factbox\CachedFactbox',
+ new CachedFactbox( $cache, new \stdClass )
+ );
+ }
+
+ /**
+ * @dataProvider outputDataProvider
+ */
+ public function testProcessAndRetrieveContent( $parameters, $expected ) {
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgNamespacesWithSemanticLinks',
+ $parameters['smwgNamespacesWithSemanticLinks']
+ );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgShowFactbox',
+ $parameters['smwgShowFactbox']
+ );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgFactboxFeatures',
+ $parameters['smwgFactboxFeatures']
+ );
+
+ $this->testEnvironment->registerObject( 'Store', $parameters['store'] );
+
+ $outputPage = $parameters['outputPage'];
+
+ $instance = new CachedFactbox( $this->memoryCache );
+ $instance->isEnabled( true );
+ $instance->setFeatureSet( $parameters['smwgFactboxFeatures'] );
+
+ $this->assertEmpty(
+ $instance->retrieveContent( $outputPage )
+ );
+
+ $instance->prepareFactboxContent(
+ $outputPage,
+ $parameters['language'],
+ $parameters['parserOutput']
+ );
+
+ $result = $outputPage->mSMWFactboxText;
+
+ $this->assertPreProcess(
+ $expected,
+ $result,
+ $outputPage,
+ $instance
+ );
+
+ // Re-run on the same instance
+ $instance->prepareFactboxContent(
+ $outputPage,
+ $parameters['language'],
+ $parameters['parserOutput']
+ );
+
+ $this->assertPostProcess(
+ $expected,
+ $result,
+ $outputPage,
+ $instance
+ );
+ }
+
+ public function assertPreProcess( $expected, $result, $outputPage, $instance ) {
+
+ if ( $expected['text'] ) {
+
+ $this->assertContains(
+ $expected['text'],
+ $result,
+ 'Asserts that content was altered as expected'
+ );
+
+ // Deliberately clear the outputPage property to force
+ // content to be retrieved from the cache
+ unset( $outputPage->mSMWFactboxText );
+
+ $this->assertTrue(
+ $result === $instance->retrieveContent( $outputPage ),
+ 'Asserts that cached content was retrievable'
+ );
+
+ } else {
+
+ $this->assertNull(
+ $result,
+ 'Asserts that the result is null'
+ );
+ }
+ }
+
+ public function assertPostProcess( $expected, $result, $outputPage, $instance ) {
+
+ $this->assertEquals(
+ $result,
+ $instance->retrieveContent( $outputPage ),
+ 'Asserts that content is being fetched from cache'
+ );
+
+ $this->assertTrue(
+ $result === $outputPage->mSMWFactboxText,
+ 'Asserts that content from the outputpage property and retrieveContent() is equal'
+ );
+
+ if ( $expected['text'] ) {
+
+ $this->assertTrue(
+ $instance->isCached(),
+ 'Asserts that isCached() returns true'
+ );
+
+ } else {
+
+ $this->assertFalse(
+ $instance->isCached(),
+ 'Asserts that isCached() returns false'
+ );
+ }
+ }
+
+ public function outputDataProvider() {
+
+ $language = Language::factory( 'en' );
+
+ $title = MockTitle::buildMockForMainNamespace( __METHOD__ . 'mock-subject' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'hasVisibleProperties' )
+ ->will( $this->returnValue( true ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $title ) ] ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ new DIProperty( __METHOD__ . 'property' ) ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ #0 Factbox build, being visible
+ $title = MockTitle::buildMock( __METHOD__ . 'title-being-visible' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10001 ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 10001 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( $semanticData )
+ ],
+ [
+ 'text' => $subject->getDBKey()
+ ]
+ ];
+
+ #1 Factbox build, being visible, using WebRequest oldid
+ $title = MockTitle::buildMock( __METHOD__ . 'title-with-oldid' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10002 ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 10002 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $context = new \RequestContext( );
+ $context->setRequest( new \FauxRequest( [ 'oldid' => 9001 ], true ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( $semanticData )
+ ],
+ [
+ 'text' => $subject->getDBKey()
+ ]
+ ];
+
+ #2 Factbox is expected not to be visible
+ $title = MockTitle::buildMock( __METHOD__ . 'title-ns-disabled' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10003 ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 10003 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => false ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_HIDDEN,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( $semanticData )
+ ],
+ [
+ 'text' => null
+ ]
+ ];
+
+ #3 No semantic data
+ $title = MockTitle::buildMock( __METHOD__ . 'title-empty-semanticdata' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10004 ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 10004 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'isEmpty' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( null ),
+ ],
+ [
+ 'text' => null
+ ]
+ ];
+
+ // #4 SpecialPage
+ $title = MockTitle::buildMock( __METHOD__ . 'title-specialpage' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( null ),
+ ],
+ [
+ 'text' => ''
+ ]
+ ];
+
+ // #5 does not exist
+ $title = MockTitle::buildMock( __METHOD__ . 'title-not-exists' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( false ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE | SMW_FACTBOX_PURGE_REFRESH | SMW_FACTBOX_DISPLAY_SUBOBJECT,
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'outputPage' => $outputPage,
+ 'store' => $store,
+ 'language' => $language,
+ 'parserOutput' => $this->makeParserOutput( null ),
+ ],
+ [
+ 'text' => ''
+ ]
+ ];
+
+ return $provider;
+ }
+
+ protected function makeParserOutput( $semanticData ) {
+
+ $parserOutput = new ParserOutput();
+
+ if ( method_exists( $parserOutput, 'setExtensionData' ) ) {
+ $parserOutput->setExtensionData( 'smwdata', $semanticData );
+ } else {
+ $parserOutput->mSMWData = $semanticData;
+ }
+
+ return $parserOutput;
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxFactoryTest.php
new file mode 100644
index 00000000..b4fe99ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxFactoryTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Factbox;
+
+use SMW\Factbox\FactboxFactory;
+use Title;
+
+/**
+ * @covers \SMW\Factbox\FactboxFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FactboxFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Factbox\FactboxFactory',
+ new FactboxFactory()
+ );
+ }
+
+ public function testCanConstructCachedFactbox() {
+
+ $instance = new FactboxFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Factbox\CachedFactbox',
+ $instance->newCachedFactbox()
+ );
+ }
+
+ public function testCanConstructFactbox() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new FactboxFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Factbox\Factbox',
+ $instance->newFactbox( $title, $parserOutput )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxMagicWordsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxMagicWordsTest.php
new file mode 100644
index 00000000..6df246fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxMagicWordsTest.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Tests\Factbox;
+
+use ParserOutput;
+use ReflectionClass;
+use SMW\ApplicationFactory;
+use SMW\Factbox\Factbox;
+use SMW\ParserData;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\Factbox\Factbox
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FactboxMagicWordsTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ public function testMagicWordsFromParserOutputExtension( $text, array $expected ) {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $this->testEnvironment->withConfiguration(
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ $title->getNamespace() => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT | SMW_PARSER_INL_ERROR
+ ]
+ );
+
+ $parserData = new ParserData( $title, $parserOutput );
+
+ $inTextAnnotationParser = ApplicationFactory::getInstance()->newInTextAnnotationParser( $parserData );
+ $inTextAnnotationParser->parse( $text );
+
+ $this->assertEquals(
+ $expected['magicWords'],
+ $this->getMagicwords( $parserOutput )
+ );
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.CodeAnalysis.UnusedFunctionParameter
+ public function testGetMagicWords( $text, array $expected ) { // @codingStandardsIgnoreEnd
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $this->testEnvironment->withConfiguration( [
+ 'smwgShowFactboxEdit' => SMW_FACTBOX_HIDDEN,
+ 'smwgShowFactbox' => SMW_FACTBOX_HIDDEN
+ ]
+ );
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->any() )
+ ->method( 'getExtensionData' )
+ ->will( $this->returnValue( $expected['magicWords'] ) );
+
+ // MW 1.19, 1.20
+ $parserOutput->mSMWMagicWords = $expected['magicWords'];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Factbox(
+ $store,
+ new ParserData( $title, $parserOutput ),
+ $messageBuilder
+ );
+
+ if ( isset( $expected['preview'] ) && $expected['preview'] ) {
+ $instance->setPreviewFlag( true );
+ }
+
+ $reflector = new ReflectionClass( '\SMW\Factbox\Factbox' );
+
+ $magic = $reflector->getMethod( 'getMagicWords' );
+ $magic->setAccessible( true );
+
+ $result = $magic->invoke( $instance );
+
+ $this->assertInternalType( 'integer', $result );
+ $this->assertEquals( $expected['constants'], $result );
+ }
+
+ /**
+ * @return array
+ */
+ public function textDataProvider() {
+
+ $provider = [];
+
+ // #0 __NOFACTBOX__, this test should not generate a factbox output
+ $provider[] = [
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis' .
+ ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' __NOFACTBOX__ ',
+ [
+ 'magicWords' => [ 'SMW_NOFACTBOX' ],
+ 'constants' => SMW_FACTBOX_HIDDEN,
+ 'textOutput' => ''
+ ]
+ ];
+
+ // #1 __SHOWFACTBOX__, this test should generate a factbox output
+ $provider[] = [
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis' .
+ ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' __SHOWFACTBOX__',
+ [
+ 'magicWords' => [ 'SMW_SHOWFACTBOX' ],
+ 'constants' => SMW_FACTBOX_NONEMPTY,
+ 'textOutput' => 'smwfactboxhead' // lazy check because we use assertContains
+ ]
+ ];
+
+ // #2 empty
+ $provider[] = [
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis' .
+ ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut',
+ [
+ 'magicWords' => null,
+ 'constants' => SMW_FACTBOX_HIDDEN,
+ 'textOutput' => ''
+ ]
+ ];
+
+ // #3 empty + preview option
+ $provider[] = [
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis' .
+ ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut',
+ [
+ 'magicWords' => null,
+ 'preview' => true,
+ 'constants' => SMW_FACTBOX_HIDDEN,
+ 'textOutput' => ''
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ protected function getMagicwords( $parserOutput ) {
+
+ if ( method_exists( $parserOutput, 'getExtensionData' ) ) {
+ return $parserOutput->getExtensionData( 'smwmagicwords' );
+ }
+
+ return $parserOutput->mSMWMagicWords;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxTest.php
new file mode 100644
index 00000000..faabe166
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Factbox/FactboxTest.php
@@ -0,0 +1,559 @@
+<?php
+
+namespace SMW\Tests\Factbox;
+
+use ParserOutput;
+use ReflectionClass;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Factbox\Factbox;
+use SMW\ParserData;
+use SMW\SemanticData;
+use SMW\TableFormatter;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\Factbox\Factbox
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FactboxTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+
+ $this->testEnvironment->addConfiguration( 'smwgShowFactbox', SMW_FACTBOX_NONEMPTY );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ Factbox::class,
+ new Factbox( $store, $parserData )
+ );
+ }
+
+ public function testGetContent() {
+
+ $text = __METHOD__;
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ // Build Factbox stub object to encapsulate the method
+ // without the need for other dependencies to occur
+ $instance = $this->getMockBuilder( '\SMW\Factbox\Factbox' )
+ ->setConstructorArgs( [
+ $store,
+ $parserData
+ ] )
+ ->setMethods( [ 'fetchContent', 'getMagicWords' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'getMagicWords' )
+ ->will( $this->returnValue( 'Lula' ) );
+
+ $instance->expects( $this->any() )
+ ->method( 'fetchContent' )
+ ->will( $this->returnValue( $text ) );
+
+ $this->assertFalse( $instance->isVisible() );
+
+ $instance->doBuild();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getContent()
+ );
+
+ $this->assertEquals(
+ $text,
+ $instance->getContent()
+ );
+
+ $this->assertTrue( $instance->isVisible() );
+ }
+
+ public function testGetContentRoundTripForNonEmptyContent() {
+
+ $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+
+ $this->testEnvironment->addConfiguration( 'smwgShowFactbox', SMW_FACTBOX_NONEMPTY );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'hasVisibleProperties' )
+ ->will( $this->returnValue( true ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $subject ] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ DIProperty::newFromUserLabel( 'SomeFancyProperty' ) ] ) );
+
+ $parserOutput = $this->setupParserOutput( $semanticData );
+
+ $instance = new Factbox(
+ $store,
+ new ParserData( $subject->getTitle(), $parserOutput )
+ );
+
+ $result = $instance->doBuild()->getContent();
+
+ $this->assertInternalType(
+ 'string',
+ $result
+ );
+
+ $this->assertContains(
+ $subject->getDBkey(),
+ $result
+ );
+
+ $this->assertEquals(
+ $subject->getTitle(),
+ $instance->getTitle()
+ );
+ }
+
+ public function testCreateTable() {
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new Factbox( $store, $parserData );
+
+ $reflector = new ReflectionClass( '\SMW\Factbox\Factbox' );
+ $createTable = $reflector->getMethod( 'createTable' );
+ $createTable->setAccessible( true );
+
+ $this->assertInternalType(
+ 'string',
+ $createTable->invoke( $instance, $parserData->getSemanticData() )
+ );
+ }
+
+ public function testTabs() {
+
+ $this->assertContains(
+ 'tab-facts-rendered',
+ Factbox::tabs( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'tab-facts-derived',
+ Factbox::tabs( 'Foo', 'Bar' )
+ );
+ }
+
+ /**
+ * @dataProvider fetchContentDataProvider
+ */
+ public function testFetchContent( $parserData ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new Factbox(
+ $store,
+ $parserData
+ );
+
+ $reflector = new ReflectionClass( '\SMW\Factbox\Factbox' );
+
+ $fetchContent = $reflector->getMethod( 'fetchContent' );
+ $fetchContent->setAccessible( true );
+
+ $this->assertInternalType(
+ 'string',
+ $fetchContent->invoke( $instance, SMW_FACTBOX_NONEMPTY )
+ );
+
+ $this->assertEmpty(
+ $fetchContent->invoke( $instance, SMW_FACTBOX_HIDDEN )
+ );
+ }
+
+ /**
+ * @dataProvider contentDataProvider
+ */
+ public function testGetContentDataSimulation( $setup, $expected ) {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'hasVisibleSpecialProperties' )
+ ->will( $this->returnValue( $setup['hasVisibleSpecialProperties'] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'hasVisibleProperties' )
+ ->will( $this->returnValue( $setup['hasVisibleProperties'] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'isEmpty' )
+ ->will( $this->returnValue( $setup['isEmpty'] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $parserData->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( null ) );
+
+ // Build Factbox stub object to encapsulate the method
+ // without the need for other dependencies to occur
+ $factbox = $this->getMockBuilder( '\SMW\Factbox\Factbox' )
+ ->setConstructorArgs( [
+ $store,
+ $parserData
+ ] )
+ ->setMethods( [ 'createTable' ] )
+ ->getMock();
+
+ $factbox->expects( $this->any() )
+ ->method( 'createTable' )
+ ->will( $this->returnValue( $setup['invokedContent'] ) );
+
+ $reflector = new ReflectionClass( '\SMW\Factbox\Factbox' );
+ $fetchContent = $reflector->getMethod( 'fetchContent' );
+ $fetchContent->setAccessible( true );
+
+ $this->assertInternalType(
+ 'string',
+ $fetchContent->invoke( $factbox )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $fetchContent->invoke( $factbox, $setup['showFactbox'] )
+ );
+ }
+
+ /**
+ * Conditional content switcher to test combinations of
+ * SMW_FACTBOX_NONEMPTY and SMWSemanticData etc.
+ *
+ * @return array
+ */
+ public function contentDataProvider() {
+
+ $text = __METHOD__;
+ $provider = [];
+
+ $provider[] = [
+ [
+ 'hasVisibleSpecialProperties' => true,
+ 'hasVisibleProperties' => true,
+ 'isEmpty' => false,
+ 'showFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'invokedContent' => $text,
+ ],
+ $text // expected return
+ ];
+
+ $provider[] = [
+ [
+ 'hasVisibleSpecialProperties' => true,
+ 'hasVisibleProperties' => true,
+ 'isEmpty' => true,
+ 'showFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'invokedContent' => $text,
+ ],
+ $text // expected return
+ ];
+
+ $provider[] = [
+ [
+ 'hasVisibleSpecialProperties' => false,
+ 'hasVisibleProperties' => true,
+ 'isEmpty' => false,
+ 'showFactbox' => SMW_FACTBOX_SPECIAL,
+ 'invokedContent' => $text,
+ ],
+ '' // expected return
+ ];
+
+ $provider[] = [
+ [
+ 'hasVisibleSpecialProperties' => false,
+ 'hasVisibleProperties' => false,
+ 'isEmpty' => false,
+ 'showFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'invokedContent' => $text,
+ ],
+ '' // expected return
+ ];
+
+ $provider[] = [
+ [
+ 'hasVisibleSpecialProperties' => true,
+ 'hasVisibleProperties' => false,
+ 'isEmpty' => false,
+ 'showFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'invokedContent' => $text,
+ ],
+ '' // expected return
+ ];
+
+ return $provider;
+ }
+
+ public function testGetTableHeader() {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $parserData = new ParserData(
+ $title,
+ new ParserOutput()
+ );
+
+ $parserData->setSemanticData( new SemanticData( DIWikiPage::newFromTitle( $title ) ) );
+ $parserData->getSemanticData()->addPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ DIWikiPage::newFromTitle( $title )
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new Factbox( $store, $parserData );
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ 'div class="smwrdflink"'
+ ],
+ $instance->doBuild()->getContent()
+ );
+ }
+
+ /**
+ * @dataProvider tableContentDataProvider
+ */
+ public function testGetTableContent( $test, $expected ) {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $parserData = new ParserData(
+ $title,
+ new ParserOutput()
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property->expects( $this->any() )
+ ->method( 'isUserDefined' )
+ ->will( $this->returnValue( $test['isUserDefined'] ) );
+
+ $property->expects( $this->any() )
+ ->method( 'findPropertyTypeID' )
+ ->will( $this->returnValue( '_wpg' ) );
+
+ $property->expects( $this->any() )
+ ->method( 'isShown' )
+ ->will( $this->returnValue( $test['isShown'] ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getLabel' )
+ ->will( $this->returnValue( 'Quuey' ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( \SMWDataItem::TYPE_PROPERTY ) );
+
+ $parserData->setSemanticData(
+ new SemanticData( DIWikiPage::newFromTitle( $title ) )
+ );
+
+ $parserData->getSemanticData()->addPropertyObjectValue(
+ $property,
+ DIWikiPage::newFromTitle( $title )
+ );
+
+ $instance = new Factbox( $store, $parserData );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->doBuild()->getContent()
+ );
+ }
+
+ public function tableContentDataProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ [
+ 'isShown' => true,
+ 'isUserDefined' => true,
+ ],
+ [ 'class="smw-table-cell smwprops"' ]
+ ];
+
+ $provider[] = [
+ [
+ 'isShown' => false,
+ 'isUserDefined' => true,
+ ],
+ ''
+ ];
+
+ $provider[] = [
+ [
+ 'isShown' => true,
+ 'isUserDefined' => false,
+ ],
+ [ 'class="smw-table-cell smwspecs"' ]
+ ];
+
+ $provider[] = [
+ [
+ 'isShown' => false,
+ 'isUserDefined' => false,
+ ],
+ ''
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function fetchContentDataProvider() {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $provider = [];
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'isEmpty' )
+ ->will( $this->returnValue( false ) );
+
+ $parserData = new ParserData(
+ $title,
+ new ParserOutput()
+ );
+
+ $parserData->setSemanticData( $semanticData );
+
+ $provider[] = [ $parserData ];
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ new DIProperty( '_SKEY') ] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'isEmpty' )
+ ->will( $this->returnValue( false ) );
+
+ $parserData = new ParserData(
+ $title,
+ new ParserOutput()
+ );
+
+ $parserData->setSemanticData( $semanticData );
+
+ $provider[] = [ $parserData ];
+
+ return $provider;
+ }
+
+ protected function setupParserOutput( $semanticData ) {
+
+ $parserOutput = new ParserOutput();
+
+ if ( method_exists( $parserOutput, 'setExtensionData' ) ) {
+ $parserOutput->setExtensionData( 'smwdata', $semanticData );
+ } else {
+ $parserOutput->mSMWData = $semanticData;
+ }
+
+ return $parserOutput;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/GlobalFunctionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/GlobalFunctionsTest.php
new file mode 100644
index 00000000..165c3dfb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/GlobalFunctionsTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class GlobalFunctionsTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @covers ::smwfGetLinker
+ * @test smwfGetLinker
+ *
+ * @since 1.9
+ */
+ public function testSmwfGetLinker() {
+ $instance = smwfGetLinker();
+
+ $this->assertInstanceOf( 'Linker', $instance );
+ }
+
+ /**
+ * @covers ::smwfNormalTitleDBKey
+ * @test smwfNormalTitleDBKey
+ *
+ * @since 1.9
+ */
+ public function testSmwfNormalTitleDBKey() {
+ $result = smwfNormalTitleDBKey( ' foo bar ' );
+
+ // Globals are ... but it can't be invoke ... well make my day
+ $expected = $GLOBALS['wgCapitalLinks'] ? 'Foo_bar' : 'foo_bar';
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * @covers ::smwfHTMLtoUTF8
+ * @test smwfHTMLtoUTF8
+ *
+ * @since 1.9
+ */
+ public function testSmwfHTMLtoUTF8() {
+ $result = smwfHTMLtoUTF8( "\xc4\x88io bonas dans l'\xc3\xa9cole, &#x108;io bonas dans l'&eacute;cole!" );
+
+ $expected = "Ĉio bonas dans l'école, Ĉio bonas dans l'école!";
+ $this->assertEquals( $expected, $result );
+ }
+
+ /**
+ * @test Test if global functions are accessible
+ * @dataProvider getGlobalFunctions
+ *
+ * @param $function
+ */
+ public function testGlobalFunctionsAccessibility( $function ) {
+ $this->assertTrue( function_exists( $function ) );
+ }
+
+ /**
+ * @covers ::smwfEncodeMessages
+ * @test smwfEncodeMessages
+ * @dataProvider getEncodeMessagesDataProvider
+ *
+ * @param $message
+ * @param $type
+ * @param $separator
+ * @param $escape
+ */
+ public function testSmwfEncodeMessages( $message, $type, $separator, $escape ) {
+ $results = smwfEncodeMessages( $message );
+ $this->assertFalse( is_null( $results ) );
+ $this->assertTrue( is_string( $results ) );
+
+ $results = smwfEncodeMessages( $message, $type );
+ $this->assertFalse( is_null( $results ) );
+ $this->assertTrue( is_string( $results ) );
+
+ $results = smwfEncodeMessages( $message, $type, $separator );
+ $this->assertFalse( is_null( $results ) );
+ $this->assertTrue( is_string( $results ) );
+
+ $results = smwfEncodeMessages( $message, $type, $separator, $escape );
+ $this->assertFalse( is_null( $results ) );
+ $this->assertTrue( is_string( $results ) );
+ }
+
+ public function testSwfCountDown() {
+ $this->assertTrue( function_exists( 'swfCountDown' ) );
+ }
+
+ public function testSmwfCacheKeyOnPrefixedNamespace() {
+
+ $this->assertEquals(
+ smwfCacheKey( 'foo', 'bar' ),
+ smwfCacheKey( ':foo', 'bar' )
+ );
+ }
+
+ /**
+ * Provides available global functions
+ *
+ * @return array
+ */
+ public function getGlobalFunctions() {
+ return [
+ [ 'smwfNormalTitleDBKey' ],
+ [ 'smwfNormalTitleText' ],
+ [ 'smwfXMLContentEncode' ],
+ [ 'smwfHTMLtoUTF8' ],
+ [ 'smwfNumberFormat' ],
+ [ 'smwfEncodeMessages' ],
+ [ 'smwfGetStore' ],
+ [ 'smwfGetLinker' ],
+ ];
+ }
+
+ /**
+ * Provides messages
+ *
+ * @return array
+ */
+ public function getEncodeMessagesDataProvider() {
+ return [
+ [ [ '', '', '' ] , '', '', true ],
+ [ [ 'abc', 'ABC', '<span>Test</span>' ] , '', '', true ],
+ [ [ 'abc', 'ABC', '<span>Test</span>' ] , 'warning', '', true ],
+ [ [ 'abc', 'ABC', '<span>Test</span>' ] , 'info', ',', false ],
+ [ [ 'abc', 'ABC', '<span>Test</span>' ] , null, ',', false ],
+ [ [ 'abc', 'ABC', '<span>Test</span>' ] , '<span>Test</span>', ',', true ],
+ ];
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HashBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HashBuilderTest.php
new file mode 100644
index 00000000..85558d93
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HashBuilderTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataModel\ContainerSemanticData;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\HashBuilder;
+use SMW\SemanticData;
+use Title;
+
+/**
+ * @covers \SMW\HashBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HashBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider segmentProvider
+ */
+ public function testTitleRoundTrip( $namespace, $title, $interwiki , $fragment ) {
+
+ $title = Title::makeTitle( $namespace, $title, $fragment, $interwiki );
+
+ $this->assertEquals(
+ $title,
+ HashBuilder::newTitleFromHash(
+ HashBuilder::getHashIdForTitle( $title )
+ )
+ );
+ }
+
+ /**
+ * @dataProvider segmentProvider
+ */
+ public function testDiWikiPageRoundTrip( $namespace, $title, $interwiki, $subobjectName ) {
+
+ $dataItem = new DIWikiPage( $title, $namespace, $interwiki, $subobjectName );
+
+ $this->assertEquals(
+ $dataItem,
+ HashBuilder::newDiWikiPageFromHash(
+ HashBuilder::getHashIdForDiWikiPage( $dataItem )
+ )
+ );
+ }
+
+ public function testPredefinedProperty() {
+
+ $instance = new HashBuilder();
+
+ $property = new DIProperty( '_MDAT' );
+ $dataItem = $property->getDiWikiPage();
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->newDiWikiPageFromHash(
+ $instance->getHashIdForDiWikiPage( $dataItem )
+ )
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->newDiWikiPageFromHash(
+ $instance->createHashIdFromSegments( $property->getKey(), SMW_NS_PROPERTY )
+ )
+ );
+ }
+
+ public function testContentHashId() {
+
+ $hash = HashBuilder::createFromContent( 'Foo' );
+
+ $this->assertInternalType(
+ 'string',
+ $hash
+ );
+
+ $this->assertSame(
+ $hash,
+ HashBuilder::createFromContent( [ 'Foo' ] )
+ );
+
+ $this->assertContains(
+ 'Bar',
+ HashBuilder::createFromContent( [ 'Foo' ], 'Bar' )
+ );
+ }
+
+ public function testCreateFromSemanticData() {
+
+ $semanticData = new SemanticData(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $this->assertInternalType(
+ 'string',
+ HashBuilder::createFromSemanticData( $semanticData )
+ );
+ }
+
+ public function testCreateFromSemanticDataWithSubSemanticDataAndPHPSerialization() {
+
+ $semanticData = new SemanticData(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $containerSemanticData = new ContainerSemanticData(
+ new DIWikiPage( __METHOD__, NS_MAIN, '', 'Foo' )
+ );
+
+ $containerSemanticData->addSubSemanticData(
+ new ContainerSemanticData( new DIWikiPage( __METHOD__, NS_MAIN, '', 'Foo2' ) )
+ );
+
+ $semanticData->addSubSemanticData(
+ $containerSemanticData
+ );
+
+ $semanticData->addSubSemanticData(
+ new ContainerSemanticData( new DIWikiPage( __METHOD__, NS_MAIN, '', 'Bar' ) )
+ );
+
+ $sem = serialize( $semanticData );
+
+ $this->assertInternalType(
+ 'string',
+ HashBuilder::createFromSemanticData( unserialize( $sem ) )
+ );
+ }
+
+ public function segmentProvider() {
+
+ $provider[] = [ NS_FILE, 'ichi', '', '' ];
+ $provider[] = [ NS_HELP, 'ichi', 'ni', '' ];
+ $provider[] = [ NS_MAIN, 'ichi maru', 'ni', 'san' ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HierarchyLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HierarchyLookupTest.php
new file mode 100644
index 00000000..b335a19d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/HierarchyLookupTest.php
@@ -0,0 +1,421 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\HierarchyLookup;
+
+/**
+ * @covers \SMW\HierarchyLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class HierarchyLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $cache;
+ private $spyLogger;
+
+ protected function setUp() {
+
+ $this->spyLogger = TestEnvironment::newSpyLogger();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HierarchyLookup::class,
+ new HierarchyLookup( $this->store, $this->cache )
+ );
+ }
+
+ public function testAddChangePropListener() {
+
+ $changePropListener = $this->getMockBuilder( '\SMW\ChangePropListener' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changePropListener->expects( $this->at( 0 ) )
+ ->method( 'addListenerCallback' )
+ ->with(
+ $this->equalTo( '_SUBP' ),
+ $this->anything() );
+
+ $changePropListener->expects( $this->at( 1 ) )
+ ->method( 'addListenerCallback' )
+ ->with(
+ $this->equalTo( '_SUBC' ),
+ $this->anything() );
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->addListenersTo( $changePropListener );
+ }
+
+ public function testVerifySubpropertyForOnNonCachedLookup() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new HierarchyLookup(
+ $store,
+ $cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $property = new DIProperty( 'Foo' );
+ $instance->hasSubproperty( $property );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->hasSubproperty( $property )
+ );
+ }
+
+ public function testFindSubpropertyList() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $expected = [
+ DIWikiPage::newFromText( 'Bar', SMW_NS_PROPERTY )
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBP' ) ),
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->anything() )
+ ->will( $this->returnValue( $expected ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new HierarchyLookup(
+ $store,
+ $cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->findSubpropertyList( $property )
+ );
+ }
+
+ public function testGetConsecutiveSubpropertyList() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $expected = [
+ new DIProperty( 'Bar' ),
+ new DIProperty( 'Foobar' )
+ ];
+
+ $a = DIWikiPage::newFromText( 'Bar', SMW_NS_PROPERTY );
+
+ $this->store->expects( $this->at( 0 ) )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBP' ) ),
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $a ] ) );
+
+ $b = DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY );
+
+ $this->store->expects( $this->at( 1 ) )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBP' ) ),
+ $this->equalTo( $a ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $b ] ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with(
+ $this->stringContains( ':smw:hierarchy:25840d7839e2fe0369c3fe16014d21d1' ),
+ $this->anything() );
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setSubpropertyDepth( 2 );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getConsecutiveHierarchyList( $property )
+ );
+ }
+
+ public function testGetConsecutiveCachedSubpropertyList() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $expected = [
+ new DIProperty( 'Bar' ),
+ new DIProperty( 'Foobar' )
+ ];
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( [ 'Foo' => [ 'Bar', 'Foobar' ] ] ) );
+
+ $this->cache->expects( $this->never() )
+ ->method( 'save' );
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setSubpropertyDepth( 2 );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getConsecutiveHierarchyList( $property )
+ );
+ }
+
+ public function testGetConsecutiveSubcategoryList() {
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $expected = [
+ new DIWikiPage( 'Bar', NS_CATEGORY ),
+ new DIWikiPage( 'Foobar', NS_CATEGORY )
+ ];
+
+ $a = DIWikiPage::newFromText( 'Bar', NS_CATEGORY );
+
+ $this->store->expects( $this->at( 0 ) )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBC' ) ),
+ $this->equalTo( $category ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $a ] ) );
+
+ $b = DIWikiPage::newFromText( 'Foobar', NS_CATEGORY );
+
+ $this->store->expects( $this->at( 1 ) )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBC' ) ),
+ $this->equalTo( $a ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $b ] ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with(
+ $this->stringContains( ':smw:hierarchy:d27ae8a4539ee4c7ab76aedcbf1c08c0' ),
+ $this->anything() );
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setSubcategoryDepth( 2 );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getConsecutiveHierarchyList( $category )
+ );
+ }
+
+ public function testGetConsecutiveCachedSubcategoryList() {
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $expected = [
+ new DIWikiPage( 'Bar', NS_CATEGORY ),
+ new DIWikiPage( 'Foobar', NS_CATEGORY )
+ ];
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( [ 'Foo' => [ 'Bar', 'Foobar' ] ] ) );
+
+ $this->cache->expects( $this->never() )
+ ->method( 'save' );
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setSubcategoryDepth( 2 );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getConsecutiveHierarchyList( $category )
+ );
+ }
+
+ public function testGetConsecutiveHierarchyListWithInvaidTypeThrowsException() {
+
+ $instance = new HierarchyLookup(
+ $this->store,
+ $this->cache
+ );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+
+ $instance->getConsecutiveHierarchyList( new DIWikiPage( __METHOD__, NS_MAIN ) );
+ }
+
+ public function testFindSubcategoryList() {
+
+ $category = DIWikiPage::newFromText( 'Foo', NS_CATEGORY );
+
+ $expected = [
+ DIWikiPage::newFromText( 'Bar', NS_CATEGORY )
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_SUBC' ) ),
+ $this->equalTo( $category ),
+ $this->anything() )
+ ->will( $this->returnValue( $expected ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new HierarchyLookup(
+ $store,
+ $cache
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->findSubcategoryList( $category )
+ );
+ }
+
+ public function testDisabledSubpropertyLookup() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->never() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new HierarchyLookup( $store, $cache );
+ $instance->setSubpropertyDepth( 0 );
+
+ $this->assertFalse(
+ $instance->hasSubproperty( new DIProperty( 'Foo' ) )
+ );
+ }
+
+ public function testDisabledSubcategoryLookup() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->never() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new HierarchyLookup( $store, $cache );
+ $instance->setSubcategoryDepth( 0 );
+
+ $this->assertFalse(
+ $instance->hasSubcategory( DIWikiPage::newFromText( 'Foo', NS_CATEGORY ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/DispatchingContentCreatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/DispatchingContentCreatorTest.php
new file mode 100644
index 00000000..7dfe78a7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/DispatchingContentCreatorTest.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\Tests\Importer\ContentCreators;
+
+use SMW\Importer\ContentCreators\DispatchingContentCreator;
+use SMW\Importer\ImportContents;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Importer\ContentCreators\DispatchingContentCreator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DispatchingContentCreatorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $wikiImporter;
+ private $messageReporter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $importStreamSource = $this->getMockBuilder( '\ImportStreamSource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->wikiImporter = $this->getMockBuilder( '\WikiImporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\ContentCreators\DispatchingContentCreator',
+ new DispatchingContentCreator( [] )
+ );
+ }
+
+ public function testCanCreateContentsFor() {
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( 'Foo' );
+
+ $contentCreator = $this->getMockBuilder( '\SMW\Importer\ContentCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentCreator->expects( $this->any() )
+ ->method( 'canCreateContentsFor' )
+ ->with( $this->equalTo( $importContents ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DispatchingContentCreator(
+ [
+ $contentCreator
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->canCreateContentsFor( $importContents )
+ );
+ }
+
+ public function testDoCreateFrom() {
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( 'Foo' );
+
+ $contentCreator = $this->getMockBuilder( '\SMW\Importer\ContentCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentCreator->expects( $this->any() )
+ ->method( 'canCreateContentsFor' )
+ ->will( $this->returnValue( true ) );
+
+ $contentCreator->expects( $this->any() )
+ ->method( 'create' )
+ ->with( $this->equalTo( $importContents ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DispatchingContentCreator(
+ [
+ $contentCreator
+ ]
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $this->assertTrue(
+ $instance->create( $importContents )
+ );
+ }
+
+ public function testDoCreateFromOnNonMatchableCreatorThrowsException() {
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( 'Foo' );
+
+ $contentCreator = $this->getMockBuilder( '\SMW\Importer\ContentCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentCreator->expects( $this->any() )
+ ->method( 'canCreateContentsFor' )
+ ->will( $this->returnValue( false ) );
+
+ $contentCreator->expects( $this->never() )
+ ->method( 'create' );
+
+ $instance = new DispatchingContentCreator(
+ [
+ $contentCreator
+ ]
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->create( $importContents );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/TextContentCreatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/TextContentCreatorTest.php
new file mode 100644
index 00000000..860b453c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/TextContentCreatorTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\Importer\ContentCreators;
+
+use SMW\Importer\ContentCreators\TextContentCreator;
+use SMW\Importer\ImportContents;
+
+/**
+ * @covers \SMW\Importer\ContentCreators\TextContentCreator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextContentCreatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $pageCreator;
+ private $connection;
+ private $messageReporter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\ContentCreators\TextContentCreator',
+ new TextContentCreator( $this->pageCreator, $this->connection )
+ );
+ }
+
+ public function testCanCreateContentsFor() {
+
+ $instance = new TextContentCreator(
+ $this->pageCreator,
+ $this->connection
+ );
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( ImportContents::CONTENT_TEXT );
+
+ $this->assertTrue(
+ $instance->canCreateContentsFor( $importContents )
+ );
+ }
+
+ public function testDoCreateFrom() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback ); }
+ ) );
+
+ $page = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page->expects( $this->once() )
+ ->method( 'doEditContent' );
+
+ $this->pageCreator->expects( $this->atLeastOnce() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $page ) );
+
+ $instance = new TextContentCreator(
+ $this->pageCreator,
+ $this->connection
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( ImportContents::CONTENT_TEXT );
+ $importContents->setName( 'Foo' );
+
+ $instance->create( $importContents );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/XmlContentCreatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/XmlContentCreatorTest.php
new file mode 100644
index 00000000..f9478705
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentCreators/XmlContentCreatorTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\Importer\ContentCreators;
+
+use SMW\Importer\ContentCreators\XmlContentCreator;
+use SMW\Importer\ImportContents;
+
+/**
+ * @covers \SMW\Importer\ContentCreators\XmlContentCreator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class XmlContentCreatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $importerServiceFactory;
+ private $wikiImporter;
+ private $messageReporter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ if ( !interface_exists( '\ImportSource' ) ) {
+ $this->markTestSkipped( "ImportSource interface is unknown (MW 1.25-)" );
+ }
+
+ $importStreamSource = $this->getMockBuilder( '\ImportStreamSource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->wikiImporter = $this->getMockBuilder( '\WikiImporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->importerServiceFactory = $this->getMockBuilder( '\SMW\Services\ImporterServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->importerServiceFactory->expects( $this->any() )
+ ->method( 'newImportStreamSource' )
+ ->will( $this->returnValue( $importStreamSource ) );
+
+ $this->importerServiceFactory->expects( $this->any() )
+ ->method( 'newWikiImporter' )
+ ->will( $this->returnValue( $this->wikiImporter ) );
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\ContentCreators\XmlContentCreator',
+ new XmlContentCreator( $this->importerServiceFactory )
+ );
+ }
+
+ public function testCanCreateContentsFor() {
+
+ $instance = new XmlContentCreator(
+ $this->importerServiceFactory
+ );
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( ImportContents::CONTENT_XML );
+
+ $this->assertTrue(
+ $instance->canCreateContentsFor( $importContents )
+ );
+ }
+
+ public function testDoCreateFrom() {
+
+ $this->wikiImporter->expects( $this->atLeastOnce() )
+ ->method( 'doImport' );
+
+ $instance = new XmlContentCreator(
+ $this->importerServiceFactory
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $importContents = new ImportContents();
+ $importContents->setContentType( ImportContents::CONTENT_XML );
+ $importContents->setContentsFile( 'Foo' );
+
+ $instance->create( $importContents );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentModellerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentModellerTest.php
new file mode 100644
index 00000000..968b0d61
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ContentModellerTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\Tests\Importer;
+
+use SMW\Importer\ContentModeller;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Importer\ContentModeller
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentModellerTest extends \PHPUnit_Framework_TestCase {
+
+ private $contentModeller;
+ private $testEnvironment;
+ private $fixtures;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->contentModeller = new ContentModeller();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->fixtures = __DIR__ . '/Fixtures';
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ContentModeller::class,
+ new ContentModeller()
+ );
+ }
+
+ public function testMakeContentList() {
+
+ $contents = [
+ 'description' => '...',
+ 'import' => [
+ 'page' => 'Foo',
+ 'version' => 1
+ ]
+ ];
+
+ $instance = new ContentModeller();
+
+ $contents = $instance->makeContentList( 'Foo', $contents );
+
+ foreach ( $contents as $content ) {
+ $this->assertInstanceOf(
+ '\SMW\Importer\ImportContents',
+ $content
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/InvalidJsonContent/invalid.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/InvalidJsonContent/invalid.json
new file mode 100644
index 00000000..dc3f51d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/InvalidJsonContent/invalid.json
@@ -0,0 +1,26 @@
+{
+ "description": "Test import",
+ "import": [
+ {
+ "page": "Smw import foaf",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "foaf.txt"
+ },
+ "options": {
+ "canReplace": false
+ },
+ },
+ {
+ "page": "Foaf:knows",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": "* [[Imported from::foaf:knows]]",
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/MissingSections/error.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/MissingSections/error.json
new file mode 100644
index 00000000..5bfb659a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/MissingSections/error.json
@@ -0,0 +1,16 @@
+{
+ "description": "Test import",
+ "import": [
+ {
+ "contents": {
+ "importFrom": "foaf.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/NoImportFormat/error.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/NoImportFormat/error.json
new file mode 100644
index 00000000..732ddca2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/NoImportFormat/error.json
@@ -0,0 +1,6 @@
+{
+ "description": "Test import",
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/content.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/content.json
new file mode 100644
index 00000000..9dd4dc68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/content.json
@@ -0,0 +1,26 @@
+{
+ "description": "Test import",
+ "import": [
+ {
+ "page": "Smw import foaf",
+ "namespace": "NS_MEDIAWIKI",
+ "contents": {
+ "importFrom": "foaf.txt"
+ },
+ "options": {
+ "canReplace": false
+ }
+ },
+ {
+ "page": "Foaf:knows",
+ "namespace": "SMW_NS_PROPERTY",
+ "contents": "* [[Imported from::foaf:knows]]",
+ "options": {
+ "canReplace": false
+ }
+ }
+ ],
+ "meta": {
+ "version": "1"
+ }
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/foaf.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/foaf.txt
new file mode 100644
index 00000000..15106fc0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/Fixtures/ValidTextContent/foaf.txt
@@ -0,0 +1,13 @@
+http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend]
+ name|Type:Text
+ homepage|Type:URL
+ mbox|Type:Email
+ mbox_sha1sum|Type:Text
+ depiction|Type:URL
+ phone|Type:Text
+ Person|Category
+ Organization|Category
+ knows|Type:Page
+ member|Type:Page
+
+[[Category:Imported vocabulary]] \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImportContentsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImportContentsTest.php
new file mode 100644
index 00000000..69099ee3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImportContentsTest.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace SMW\Tests\Importer;
+
+use SMW\Importer\ImportContents;
+
+/**
+ * @covers \SMW\Importer\ImportContents
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImportContentsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\ImportContents',
+ new ImportContents()
+ );
+ }
+
+ public function testDescription() {
+
+ $instance = new ImportContents();
+
+ $instance->setDescription( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getDescription()
+ );
+ }
+
+ public function testVersion() {
+
+ $instance = new ImportContents();
+
+ $instance->setVersion( '1' );
+
+ $this->assertSame(
+ 1,
+ $instance->getVersion()
+ );
+ }
+
+ public function testName() {
+
+ $instance = new ImportContents();
+
+ $instance->setName( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getName()
+ );
+ }
+
+ public function testNamespace() {
+
+ $instance = new ImportContents();
+
+ $instance->setNamespace( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getNamespace()
+ );
+ }
+
+ public function testContents() {
+
+ $instance = new ImportContents();
+
+ $instance->setContents( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getContents()
+ );
+ }
+
+ public function testContentType() {
+
+ $instance = new ImportContents();
+
+ $instance->setContentType( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getContentType()
+ );
+ }
+
+ public function testError() {
+
+ $instance = new ImportContents();
+
+ $instance->addError( 'Foo' );
+
+ $this->assertSame(
+ [ 'Foo' ],
+ $instance->getErrors()
+ );
+ }
+
+ public function testOptions() {
+
+ $instance = new ImportContents();
+
+ $instance->setOptions( 'Foo' );
+
+ $this->assertSame(
+ [ 'Foo' ],
+ $instance->getOptions()
+ );
+
+ $this->assertFalse(
+ $instance->getOption( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImporterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImporterTest.php
new file mode 100644
index 00000000..0db5f2ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/ImporterTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\Tests\Importer;
+
+use SMW\Importer\ContentIterator;
+use SMW\Importer\ImportContents;
+use SMW\Importer\Importer;
+use SMW\Importer\JsoncontentIterator;
+use SMW\Importer\JsonImportContentsFileDirReader;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Importer\Importer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImporterTest extends \PHPUnit_Framework_TestCase {
+
+ private $spyMessageReporter;
+ private $testEnvironment;
+ private $contentIterator;
+ private $jsonImportContentsFileDirReader;
+ private $contentCreator;
+ private $messageReporter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->jsonImportContentsFileDirReader = $this->getMockBuilder( JsonImportContentsFileDirReader::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->contentIterator = $this->getMockBuilder( ContentIterator::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->contentCreator = $this->getMockBuilder( '\SMW\Importer\ContentCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\Importer',
+ new Importer( $this->contentIterator, $this->contentCreator )
+ );
+ }
+
+ public function testDisabled() {
+
+ $spyMessageReporter = $this->testEnvironment->getUtilityFactory()->newSpyMessageReporter();
+
+ $instance = new Importer(
+ new JsoncontentIterator( $this->jsonImportContentsFileDirReader ),
+ $this->contentCreator
+ );
+
+ $instance->setMessageReporter( $spyMessageReporter );
+ $instance->isEnabled( false );
+
+ $instance->doImport();
+
+ $this->assertContains(
+ 'Skipping the import process',
+ $spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testDoImport() {
+
+ $importContents = new ImportContents();
+
+ $importContents->setName( 'Foo' );
+ $importContents->setVersion( 1 );
+
+ $page = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jsonImportContentsFileDirReader->expects( $this->atLeastOnce() )
+ ->method( 'getContentList' )
+ ->will( $this->returnValue( [ 'Foo' => [ $importContents ] ] ) );
+
+ $this->jsonImportContentsFileDirReader->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->contentCreator->expects( $this->atLeastOnce() )
+ ->method( 'create' );
+
+ $instance = new Importer(
+ new JsoncontentIterator( $this->jsonImportContentsFileDirReader ),
+ $this->contentCreator
+ );
+
+ $instance->setMessageReporter( $this->messageReporter );
+ $instance->setReqVersion( 1 );
+
+ $instance->doImport();
+ }
+
+ public function testDoImportWithError() {
+
+ $importContents = new ImportContents();
+
+ $importContents->addError( 'Bar' );
+ $importContents->setVersion( 1 );
+
+ $this->jsonImportContentsFileDirReader->expects( $this->atLeastOnce() )
+ ->method( 'getContentList' )
+ ->will( $this->returnValue( [ 'Foo' => [ $importContents ] ] ) );
+
+ $this->jsonImportContentsFileDirReader->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [ 'Error' ] ) );
+
+ $this->contentCreator->expects( $this->never() )
+ ->method( 'create' );
+
+ $instance = new Importer(
+ new JsoncontentIterator( $this->jsonImportContentsFileDirReader ),
+ $this->contentCreator
+ );
+
+ $instance->setMessageReporter( $this->messageReporter );
+ $instance->setReqVersion( 1 );
+
+ $instance->doImport();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonContentIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonContentIteratorTest.php
new file mode 100644
index 00000000..a7bb118a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonContentIteratorTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\Importer;
+
+use SMW\Importer\JsonContentIterator;
+use SMW\Importer\JsonImportContentsFileDirReader;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Importer\JsonContentIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonContentIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $jsonImportContentsFileDirReader;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->jsonImportContentsFileDirReader = $this->getMockBuilder( JsonImportContentsFileDirReader::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\JsonContentIterator',
+ new JsonContentIterator( $this->jsonImportContentsFileDirReader )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\ContentIterator',
+ new JsonContentIterator( $this->jsonImportContentsFileDirReader )
+ );
+ }
+
+ public function testGetIterator() {
+
+ $this->jsonImportContentsFileDirReader->expects( $this->atLeastOnce() )
+ ->method( 'getContentList' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new JsonContentIterator(
+ $this->jsonImportContentsFileDirReader
+ );
+
+ $this->assertInstanceOf(
+ '\Iterator',
+ $instance->getIterator()
+ );
+ }
+
+ public function testGetDescription() {
+
+ $instance = new JsonContentIterator(
+ $this->jsonImportContentsFileDirReader
+ );
+
+ $instance->setDescription( 'Foo' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getDescription()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonImportContentsFileDirReaderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonImportContentsFileDirReaderTest.php
new file mode 100644
index 00000000..c4965e52
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Importer/JsonImportContentsFileDirReaderTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Tests\Importer;
+
+use SMW\Importer\ContentModeller;
+use SMW\Importer\JsonImportContentsFileDirReader;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Importer\JsonImportContentsFileDirReader
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonImportContentsFileDirReaderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $contentModeller;
+ private $testEnvironment;
+ private $fixtures;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->contentModeller = new ContentModeller();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->fixtures = __DIR__ . '/Fixtures';
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ JsonImportContentsFileDirReader::class,
+ new JsonImportContentsFileDirReader( $this->contentModeller, $this->fixtures )
+ );
+ }
+
+ public function testGetContentList() {
+
+ $instance = new JsonImportContentsFileDirReader(
+ $this->contentModeller,
+ [ $this->fixtures . '/ValidTextContent' ]
+ );
+
+ $contents = $instance->getContentList();
+
+ $this->assertArrayHasKey(
+ 'content.json',
+ $contents
+ );
+
+ foreach ( $contents as $content ) {
+ foreach ( $content as $importContents ) {
+ $this->assertInstanceOf(
+ '\SMW\Importer\ImportContents',
+ $importContents
+ );
+ }
+ }
+ }
+
+ public function testGetContentListOnFalseImportFormat() {
+
+ $instance = new JsonImportContentsFileDirReader(
+ $this->contentModeller,
+ [ $this->fixtures . '/NoImportFormat' ]
+ );
+
+ $this->assertEmpty(
+ $instance->getContentList()
+ );
+ }
+
+ public function testGetContentListOnMissingSections() {
+
+ $instance = new JsonImportContentsFileDirReader(
+ $this->contentModeller,
+ [ $this->fixtures . '/MissingSections' ]
+ );
+
+ $contents = $instance->getContentList();
+
+ $this->assertArrayHasKey(
+ 'error.json',
+ $contents
+ );
+ }
+
+ public function testGetContentListWithInvalidPath() {
+
+ $instance = new JsonImportContentsFileDirReader(
+ $this->contentModeller,
+ [ __DIR__ . '/InvalidPath' ]
+ );
+
+ $this->assertEmpty(
+ $instance->getContentList()
+ );
+ }
+
+ public function testGetContentListOnInvalidJsonThrowsException() {
+
+ $instance = new JsonImportContentsFileDirReader(
+ $this->contentModeller,
+ [ $this->fixtures . '/InvalidJsonContent' ]
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getContentList();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/InMemoryPoolCacheTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/InMemoryPoolCacheTest.php
new file mode 100644
index 00000000..ea11cd54
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/InMemoryPoolCacheTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\InMemoryPoolCache;
+
+/**
+ * @covers \SMW\InMemoryPoolCache
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class InMemoryPoolCacheTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ InMemoryPoolCache::getInstance()->clear();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $cacheFactory = $this->getMockBuilder( '\SMW\CacheFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\InMemoryPoolCache',
+ new InMemoryPoolCache( $cacheFactory )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\InMemoryPoolCache',
+ InMemoryPoolCache::getInstance()
+ );
+ }
+
+ public function testPoolCache() {
+
+ $instance = InMemoryPoolCache::getInstance();
+
+ $this->assertInstanceOf(
+ '\Onoi\Cache\Cache',
+ $instance->getPoolCacheById( 'Foo' )
+ );
+
+ $instance->getPoolCacheById( 'Foo' )->save( 'Bar', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->getPoolCacheById( 'Foo' )->fetch( 'Bar' )
+ );
+
+ $instance->resetPoolCacheById( 'Foo' );
+ }
+
+ public function testGetStats() {
+
+ $instance = InMemoryPoolCache::getInstance();
+
+ $instance->getPoolCacheById( 'Foo' )->save( 'Bar', 42 );
+
+ $this->assertNotEmpty(
+ $instance->getStats()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getStats( InMemoryPoolCache::FORMAT_PLAIN )
+ );
+
+ $this->assertContains(
+ 'ul',
+ $instance->getStats( InMemoryPoolCache::FORMAT_HTML )
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getStats( InMemoryPoolCache::FORMAT_JSON )
+ );
+
+ $instance->resetPoolCacheById( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/IteratorFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/IteratorFactoryTest.php
new file mode 100644
index 00000000..f3312ae7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/IteratorFactoryTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\IteratorFactory;
+
+/**
+ * @covers \SMW\IteratorFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class IteratorFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstructResultIterator() {
+
+ $instance = new IteratorFactory();
+
+ $result = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\ResultIterator',
+ $instance->newResultIterator( $result )
+ );
+ }
+
+ public function testCanConstructMappingIterator() {
+
+ $instance = new IteratorFactory();
+
+ $iterator = $this->getMockBuilder( '\ArrayIterator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\MappingIterator',
+ $instance->newMappingIterator( $iterator, function(){
+ } )
+ );
+ }
+
+ public function testCanConstructChunkedIterator() {
+
+ $instance = new IteratorFactory();
+
+ $iterator = $this->getMockBuilder( '\Iterator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\ChunkedIterator',
+ $instance->newChunkedIterator( $iterator )
+ );
+ }
+
+ public function testCanConstructAppendIterator() {
+
+ $instance = new IteratorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\AppendIterator',
+ $instance->newAppendIterator()
+ );
+ }
+
+ public function testCanConstructCsvFileIterator() {
+
+ $instance = new IteratorFactory();
+
+ $this->setExpectedException( 'SMW\Exception\FileNotFoundException' );
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\CsvFileIterator',
+ $instance->newCsvFileIterator( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/AppendIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/AppendIteratorTest.php
new file mode 100644
index 00000000..617d244f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/AppendIteratorTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\Tests\Iterators;
+
+use SMW\Iterators\AppendIterator;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Iterators\AppendIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class AppendIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ AppendIterator::class,
+ new AppendIterator()
+ );
+ }
+
+ /**
+ * @dataProvider iterableProvider
+ */
+ public function testCount( $iterable, $expected ) {
+
+ $instance = new AppendIterator();
+ $instance->add( $iterable );
+
+ $this->assertEquals(
+ $expected,
+ $instance->count()
+ );
+ }
+
+ public function testAddOnNonIterableThrowsException() {
+
+ $instance = new AppendIterator();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->add( 'Foo' );
+ }
+
+ public function iterableProvider() {
+
+ $provider[] = [
+ [
+ 1, 42, 1001, 9999
+ ],
+ 4
+ ];
+
+ $iterator = new AppendIterator();
+ $iterator->add( [ 0 , 1 ] );
+
+ $provider[] = [
+ $iterator,
+ 2
+ ];
+
+ $iterator = new AppendIterator();
+ $iterator->add( [ 0 , 1 ] );
+ $iterator->add( $iterator );
+
+ $provider[] = [
+ $iterator,
+ 4
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ChunkedIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ChunkedIteratorTest.php
new file mode 100644
index 00000000..449adfdd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ChunkedIteratorTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace SMW\Tests\Iterators;
+
+use SMW\Iterators\ChunkedIterator;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Iterators\ChunkedIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChunkedIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChunkedIterator::class,
+ new ChunkedIterator( [] )
+ );
+ }
+
+ public function testChunkedOnArray() {
+
+ $result = [
+ 1, 42, 1001, 9999
+ ];
+
+ $instance = new ChunkedIterator( $result, 2 );
+
+ foreach ( $instance as $chunk ) {
+ $this->assertCount(
+ 2,
+ $chunk
+ );
+ }
+
+ $chunks = iterator_to_array( $instance, false );
+
+ $this->assertEquals(
+ [1, 42],
+ $chunks[0]
+ );
+
+ $this->assertEquals(
+ [1001, 9999],
+ $chunks[1]
+ );
+ }
+
+ public function testInvalidConstructorArgumentThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance = new ChunkedIterator( 2 );
+ }
+
+ public function testInvalidChunkSizeArgumentThrowsException() {
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance = new ChunkedIterator( [], -1 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/CsvFileIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/CsvFileIteratorTest.php
new file mode 100644
index 00000000..abe8299b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/CsvFileIteratorTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Tests\Iterators;
+
+use SMW\Iterators\CsvFileIterator;
+use SMW\Utils\TempFile;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Iterators\CsvFileIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CsvFileIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $file;
+ private $tempFile;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->tempFile = new TempFile();
+ $this->file = $this->tempFile->get( 'test.csv' );
+
+ $this->tempFile->write( $this->file, 'Foo' );
+ }
+
+ protected function tearDown() {
+ $this->tempFile->delete( $this->file );
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CsvFileIterator::class,
+ new CsvFileIterator( $this->file )
+ );
+ }
+
+ public function testInvalidFileThrowsException() {
+
+ $this->setExpectedException( '\SMW\Exception\FileNotFoundException' );
+ new CsvFileIterator( 'Foo' );
+ }
+
+ public function testForEachOnCsvFileWithNoHeader() {
+
+ $sample = [
+ '1,Foo,abc',
+ '2,Bar,123'
+ ];
+
+ $this->tempFile->write( $this->file, implode( "\n", $sample ) );
+
+ $instance = new CsvFileIterator(
+ $this->file,
+ false,
+ ','
+ );
+
+ $res = [];
+
+ foreach ( $instance as $row ) {
+ $res[] = $row;
+ }
+
+ $this->assertEmpty(
+ $instance->getHeader()
+ );
+
+ $this->assertEquals(
+ $res,
+ [
+ [ '1', 'Foo', 'abc' ],
+ [ '2', 'Bar', '123' ]
+ ]
+ );
+ }
+
+ public function testForEachOnCsvFileWithHeader() {
+
+ $sample = [
+ 'No,Text,Other',
+ '1,Foo,abc',
+ '2,Bar,123'
+ ];
+
+ $this->tempFile->write( $this->file, implode( "\n", $sample ) );
+
+ $instance = new CsvFileIterator(
+ $this->file,
+ true,
+ ','
+ );
+
+ $res = [];
+
+ foreach ( $instance as $row ) {
+ $res[] = $row;
+ }
+
+ $this->assertEquals(
+ $instance->getHeader(),
+ [
+ 'No',
+ 'Text',
+ 'Other'
+ ]
+ );
+
+ $this->assertEquals(
+ $res,
+ [
+ [ '1', 'Foo', 'abc' ],
+ [ '2', 'Bar', '123' ]
+ ]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/MappingIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/MappingIteratorTest.php
new file mode 100644
index 00000000..f8ad4783
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/MappingIteratorTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Iterators;
+
+use ArrayIterator;
+use SMW\Iterators\MappingIterator;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Iterators\MappingIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MappingIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ MappingIterator::class,
+ new MappingIterator( [], function() {
+ } )
+ );
+ }
+
+ public function testInvalidConstructorArgumentThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance = new MappingIterator( 2, function() {
+ } );
+ }
+
+ public function testdoIterateOnArray() {
+
+ $expected = [
+ 1 , 42
+ ];
+
+ $mappingIterator = new MappingIterator( $expected, function( $counter ) {
+ return $counter;
+ } );
+
+ foreach ( $mappingIterator as $key => $value ) {
+ $this->assertEquals(
+ $expected[$key],
+ $value
+ );
+ }
+ }
+
+ public function testdoIterateOnArrayIterator() {
+
+ $expected = [
+ 1001 , 42
+ ];
+
+ $mappingIterator = new MappingIterator( new ArrayIterator( $expected ), function( $counter ) {
+ return $counter;
+ } );
+
+ $this->assertCount(
+ 2,
+ $mappingIterator
+ );
+
+ foreach ( $mappingIterator as $key => $value ) {
+ $this->assertEquals(
+ $expected[$key],
+ $value
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ResultIteratorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ResultIteratorTest.php
new file mode 100644
index 00000000..bd56e345
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Iterators/ResultIteratorTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\Tests\Iterators;
+
+use SMW\Iterators\ResultIterator;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Iterators\ResultIterator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ResultIteratorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ResultIterator::class,
+ new ResultIterator( [] )
+ );
+ }
+
+ public function testInvalidConstructorArgumentThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance = new ResultIterator( 2 );
+ }
+
+ public function testdoIterateOnArray() {
+
+ $result = [
+ 1, 42
+ ];
+
+ $instance = new ResultIterator( $result );
+
+ $this->assertCount(
+ 2,
+ $instance
+ );
+
+ foreach ( $instance as $key => $value ) {
+ $this->assertEquals(
+ $result[$key],
+ $value
+ );
+ }
+ }
+
+ public function testdoSeekOnArray() {
+
+ $result = [
+ 1, 42, 1001
+ ];
+
+ $instance = new ResultIterator( $result );
+ $instance->seek( 1 );
+
+ $this->assertEquals(
+ 42,
+ $instance->current()
+ );
+ }
+
+ public function testdoIterateOnResultWrapper() {
+
+ $resultWrapper = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resultWrapper->expects( $this->once() )
+ ->method( 'numRows' )
+ ->will( $this->returnValue( 1 ) );
+
+ $resultWrapper->expects( $this->atLeastOnce() )
+ ->method( 'current' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new ResultIterator( $resultWrapper );
+
+ $this->assertCount(
+ 1,
+ $instance
+ );
+
+ foreach ( $instance as $key => $value ) {
+ $this->assertEquals(
+ 42,
+ $value
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/FallbackFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/FallbackFinderTest.php
new file mode 100644
index 00000000..824eeb79
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/FallbackFinderTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\Lang;
+
+use SMW\Lang\FallbackFinder;
+use SMW\Lang\JsonContentsFileReader;
+
+/**
+ * @covers \SMW\Lang\FallbackFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FallbackFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $jsonContentsFileReader;
+
+ protected function setUp() {
+
+ $this->jsonContentsFileReader = $this->getMockBuilder( JsonContentsFileReader::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FallbackFinder::class,
+ new FallbackFinder( $this->jsonContentsFileReader )
+ );
+ }
+
+ public function testGetDefaultFallbackLanguage() {
+
+ $this->jsonContentsFileReader->expects( $this->never() )
+ ->method( 'readByLanguageCode' );
+
+ $instance = new FallbackFinder(
+ $this->jsonContentsFileReader
+ );
+
+ $this->assertEquals(
+ 'en',
+ $instance->getFallbackLanguageBy( '' )
+ );
+
+ $this->assertEquals(
+ $instance->getCanonicalFallbackLanguageCode(),
+ $instance->getFallbackLanguageBy()
+ );
+ }
+
+ public function testgetFallbackLanguageByStatedFallback() {
+
+ $mockedContent = [
+ 'fallback_language' => 'Foo'
+ ];
+
+ $this->jsonContentsFileReader->expects( $this->atLeastOnce() )
+ ->method( 'readByLanguageCode' )
+ ->will( $this->returnValue( $mockedContent ) );
+
+ $instance = new FallbackFinder(
+ $this->jsonContentsFileReader
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getFallbackLanguageBy( 'unknownLanguageCode' )
+ );
+ }
+
+ public function testgetFallbackLanguageByUnknownLanguageCode() {
+
+ $this->jsonContentsFileReader->expects( $this->atLeastOnce() )
+ ->method( 'readByLanguageCode' )
+ ->will( $this->throwException( new \RuntimeException ) );
+
+ $instance = new FallbackFinder(
+ $this->jsonContentsFileReader
+ );
+
+ $this->assertEquals(
+ 'en',
+ $instance->getFallbackLanguageBy( 'unknownLanguageCode' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/JsonContentsFileReaderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/JsonContentsFileReaderTest.php
new file mode 100644
index 00000000..db42b246
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/JsonContentsFileReaderTest.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace SMW\Tests\Lang;
+
+use SMW\Lang\JsonContentsFileReader;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Lang\JsonContentsFileReader
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class JsonContentsFileReaderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ JsonContentsFileReader::class,
+ new JsonContentsFileReader()
+ );
+ }
+
+ /**
+ * @dataProvider languageCodeProvider
+ */
+ public function testReadByLanguageCode( $languageCode ) {
+
+ $instance = new JsonContentsFileReader();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->readByLanguageCode( $languageCode )
+ );
+ }
+
+ /**
+ * @dataProvider languageCodeProvider
+ */
+ public function testReadByLanguageCodeWithCache( $languageCode ) {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( true ) );
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new JsonContentsFileReader( $cache );
+ $instance->clear();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->readByLanguageCode( $languageCode )
+ );
+ }
+
+ public function testReadByLanguageCodeToUseInMemoryCache() {
+
+ $instance = $this->getMockBuilder( JsonContentsFileReader::class )
+ ->setMethods( [ 'readJSONFile', 'getFileModificationTime' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'readJSONFile' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance->expects( $this->once() )
+ ->method( 'getFileModificationTime' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance->readByLanguageCode( 'foo' );
+
+ // InMemory use
+ $instance->readByLanguageCode( 'foo' );
+ }
+
+ public function testReadByLanguageCodeIsForcedToRereadFromFile() {
+
+ $instance = $this->getMockBuilder( JsonContentsFileReader::class )
+ ->setMethods( [ 'readJSONFile', 'getFileModificationTime' ] )
+ ->getMock();
+
+ $instance->expects( $this->exactly( 2 ) )
+ ->method( 'readJSONFile' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance->expects( $this->exactly( 2 ) )
+ ->method( 'getFileModificationTime' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance->readByLanguageCode( 'bar' );
+ $instance->readByLanguageCode( 'bar', true );
+ }
+
+ public function testTryToReadInaccessibleFileByLanguageThrowsException() {
+
+ $instance = new JsonContentsFileReader();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->readByLanguageCode( 'foo', true );
+ }
+
+ /**
+ * This method is just for convenience so that one can quickly add contents to files
+ * without requiring an extra class when extending the language content. Normally the
+ * test in active
+ *
+ * @dataProvider dataExtensionProvider
+ */
+ public function WriteToFile( $topic, $extension ) {
+
+ $instance = new JsonContentsFileReader();
+ $list ='ar,arz,ca,de,es,fi,fr,he,hu,id,it,nb,nl,pl,pt,ru,sk,zh-cn,zh-tw';
+
+ foreach ( explode( ',', $list ) as $lang ) {
+ $contents = $instance->readByLanguageCode( $lang, true );
+
+ if ( $contents === '' || !isset( $contents[$topic] ) ) {
+ continue;
+ }
+
+ $contents[$topic] = $contents[$topic] + $extension;
+
+ $instance->writeByLanguageCode( $lang, $contents );
+ }
+ }
+
+ /**
+ * @dataProvider languageCodeProvider
+ */
+ public function testgetFileModificationTime( $languageCode ) {
+
+ $instance = new JsonContentsFileReader();
+
+ $this->assertInternalType(
+ 'integer',
+ $instance->getFileModificationTime( $languageCode )
+ );
+ }
+
+ public function languageCodeProvider() {
+
+ $provider[] = [
+ 'en'
+ ];
+
+ return $provider;
+ }
+
+ public function dataExtensionProvider() {
+
+ $provider[] = [
+ 'dataTypeLabels',
+ [
+ "_ref_rec" => "Reference"
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LangTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LangTest.php
new file mode 100644
index 00000000..b1330d29
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LangTest.php
@@ -0,0 +1,320 @@
+<?php
+
+namespace SMW\Tests\Lang;
+
+use SMW\Lang\Lang;
+use SMW\Lang\LanguageContents;
+
+/**
+ * @covers \SMW\Lang\Lang
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class LangTest extends \PHPUnit_Framework_TestCase {
+
+ private $languageContents;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->languageContents = $this->getMockBuilder( LanguageContents::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function tearDown() {
+ Lang::clear();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Lang::class,
+ new Lang( $this->languageContents )
+ );
+
+ $this->assertInstanceOf(
+ Lang::class,
+ Lang::getInstance()
+ );
+
+ Lang::clear();
+ }
+
+ public function testGetNamespaces() {
+
+ $contents = [
+ "SMW_NS_PROPERTY" => "Property"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'namespace.labels' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ [ SMW_NS_PROPERTY => "Property" ],
+ $instance->getNamespaces()
+ );
+ }
+
+ public function testGetNamespaceAliases() {
+
+ $contents = [
+ "Property" => "SMW_NS_PROPERTY"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'namespace.aliases' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ [ "Property" => SMW_NS_PROPERTY ],
+ $instance->getNamespaceAliases()
+ );
+ }
+
+ public function testGetPreferredDateFormatByPrecisionOnMatchedPrecision() {
+
+ $contents = [
+ "SMW_PREC_YMDT" => "d m Y"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'date.precision' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ 'd m Y',
+ $instance->getPreferredDateFormatByPrecision( SMW_PREC_YMDT )
+ );
+ }
+
+ public function testGetPreferredDateFormatOnNotMatchablePrecision() {
+
+ $contents = [
+ "Foo" => "d m Y"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'date.precision' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ 'd F Y H:i:s',
+ $instance->getPreferredDateFormatByPrecision( SMW_PREC_YMDT )
+ );
+ }
+
+ public function testGetDatatypeLabels() {
+
+ $contents = [
+ "Foo" => "Bar"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'datatype.labels' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ [ "Foo" => 'Bar' ],
+ $instance->getDatatypeLabels()
+ );
+ }
+
+ public function testFindDatatypeByLabel() {
+
+ $contents = [
+ "Bar" => "_foo"
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ '_foo',
+ $instance->findDatatypeByLabel( 'Bar' )
+ );
+ }
+
+ public function testGetPropertyIdByLabel() {
+
+ $this->languageContents->expects( $this->at( 0 ) )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'property.labels' ),
+ $this->anything() )
+ ->will( $this->returnValue( [ "_FOO" => "Foo" ] ) );
+
+ $this->languageContents->expects( $this->at( 1 ) )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'datatype.labels' ),
+ $this->anything() )
+ ->will( $this->returnValue( [] ) );
+
+ $this->languageContents->expects( $this->at( 2 ) )
+ ->method( 'get' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->languageContents->expects( $this->at( 3 ) )
+ ->method( 'get' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ '_FOO',
+ $instance->getPropertyIdByLabel( 'Foo' )
+ );
+ }
+
+ public function testGetPropertyLabelList() {
+
+ $propertyLabels = [
+ '_Foo' => 'Bar',
+ '_Foo2' => 'Baar',
+ '_Foo3' => 'Abc'
+ ];
+
+ $this->languageContents->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->onConsecutiveCalls( $propertyLabels, [], [], [] ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $instance->fetch( 'foo' );
+
+ $this->assertEquals(
+ [ 'label' => [
+ 'Bar' => '_Foo',
+ 'Baar' => '_Foo2',
+ 'Abc' => '_Foo3'
+ ] ],
+ $instance->getPropertyLabelList()
+ );
+ }
+
+ public function testGetDateFormats() {
+
+ $contents = [
+ [ 'SMW_Y' ],
+ [ 'SMW_MY', 'SMW_YM' ]
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'date.format' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ [ [ 9 ], [ 97, 76 ] ],
+ $instance->getDateFormats()
+ );
+ }
+
+ public function testFindMonthNumberByLabelWithCaseInsensitiveSearch() {
+
+ $contents = [
+ [ 'January', 'Jan' ],
+ [ 'February', 'Feb' ],
+ [ 'March', 'Mar' ]
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'date.months' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ 3,
+ $instance->findMonthNumberByLabel( 'mar' )
+ );
+ }
+
+ public function testGetMonthLabelByNumber() {
+
+ $contents = [
+ [ 'January', 'Jan' ],
+ [ 'February', 'Feb' ],
+ [ 'March', 'Mar' ]
+ ];
+
+ $this->languageContents->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->with(
+ $this->equalTo( 'date.months' ),
+ $this->anything() )
+ ->will( $this->returnValue( $contents ) );
+
+ $instance = new Lang(
+ $this->languageContents
+ );
+
+ $this->assertEquals(
+ 'March',
+ $instance->getMonthLabelByNumber( 3 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LanguageContentsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LanguageContentsTest.php
new file mode 100644
index 00000000..285205ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Lang/LanguageContentsTest.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\Tests\Lang;
+
+use SMW\Lang\FallbackFinder;
+use SMW\Lang\JsonContentsFileReader;
+use SMW\Lang\LanguageContents;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Lang\LanguageContents
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LanguageContentsTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $jsonContentsFileReader;
+ private $fallbackFinder;
+
+ protected function setUp() {
+
+ $this->jsonContentsFileReader = $this->getMockBuilder( JsonContentsFileReader::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->fallbackFinder = $this->getMockBuilder( FallbackFinder::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ LanguageContents::class,
+ new LanguageContents( $this->jsonContentsFileReader, $this->fallbackFinder )
+ );
+ }
+
+ public function testGetCanonicalFallbackLanguageCode() {
+
+ $this->fallbackFinder->expects( $this->atLeastOnce() )
+ ->method( 'getCanonicalFallbackLanguageCode' );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $instance->getCanonicalFallbackLanguageCode();
+ }
+
+ public function testPrepareWithLanguageWithoutFallback() {
+
+ $languageCode = 'Foo';
+
+ $this->jsonContentsFileReader->expects( $this->atLeastOnce() )
+ ->method( 'canReadByLanguageCode' )
+ ->will( $this->returnValue( true ) );
+
+ $this->jsonContentsFileReader->expects( $this->atLeastOnce() )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $languageCode ) );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $this->assertFalse(
+ $instance->isLoaded( $languageCode )
+ );
+
+ $instance->load( $languageCode );
+
+ $this->assertTrue(
+ $instance->isLoaded( $languageCode )
+ );
+ }
+
+ public function testGetContentsByLanguage_ID_Depth_2() {
+
+ $languageCode = 'Foo';
+
+ $this->jsonContentsFileReader->expects( $this->at( 0 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $languageCode ) )
+ ->will( $this->returnValue( [ 'Foo' => [ 'Bar' => 123 ] ] ) );
+
+ $this->fallbackFinder->expects( $this->atLeastOnce() )
+ ->method( 'getCanonicalFallbackLanguageCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $this->assertEquals(
+ 123,
+ $instance->get( 'Foo.Bar', $languageCode )
+ );
+ }
+
+ public function testGetContentsByLanguage_ID_Depth_3() {
+
+ $languageCode = 'Foo';
+
+ $this->jsonContentsFileReader->expects( $this->at( 0 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $languageCode ) )
+ ->will( $this->returnValue( [ 'Foo' => [ 'Bar' => [ 'Foobar' => 456 ] ] ] ) );
+
+ $this->fallbackFinder->expects( $this->atLeastOnce() )
+ ->method( 'getCanonicalFallbackLanguageCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $this->assertEquals(
+ 456,
+ $instance->get( 'Foo.Bar.Foobar', $languageCode )
+ );
+ }
+
+ public function testGetContentsByLanguageWithIndexWithFallback() {
+
+ $languageCode = 'Foo';
+ $fallback = 'Foobar';
+
+ $this->jsonContentsFileReader->expects( $this->at( 0 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $languageCode ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->jsonContentsFileReader->expects( $this->at( 1 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $fallback ) )
+ ->will( $this->returnValue( [ 'Bar' => 123 ] ) );
+
+ $this->fallbackFinder->expects( $this->atLeastOnce() )
+ ->method( 'getCanonicalFallbackLanguageCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $this->fallbackFinder->expects( $this->at( 1 ) )
+ ->method( 'getFallbackLanguageBy' )
+ ->will( $this->returnValue( $fallback ) );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $this->assertEquals(
+ 123,
+ $instance->get( 'Bar', $languageCode )
+ );
+ }
+
+ public function testGetContentsByLanguageWithIndexWithFallbackButMissingIndexThrowsException() {
+
+ $languageCode = 'Foo';
+ $fallback = 'Foobar';
+
+ $this->jsonContentsFileReader->expects( $this->at( 0 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $languageCode ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->jsonContentsFileReader->expects( $this->at( 1 ) )
+ ->method( 'readByLanguageCode' )
+ ->with( $this->equalTo( $fallback ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->fallbackFinder->expects( $this->atLeastOnce() )
+ ->method( 'getCanonicalFallbackLanguageCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $this->fallbackFinder->expects( $this->at( 1 ) )
+ ->method( 'getFallbackLanguageBy' )
+ ->will( $this->returnValue( $fallback ) );
+
+ $this->fallbackFinder->expects( $this->at( 3 ) )
+ ->method( 'getFallbackLanguageBy' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $instance = new LanguageContents(
+ $this->jsonContentsFileReader,
+ $this->fallbackFinder
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->get( 'Bar', $languageCode );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/LocalizerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/LocalizerTest.php
new file mode 100644
index 00000000..9b0b7e16
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/LocalizerTest.php
@@ -0,0 +1,372 @@
+<?php
+
+namespace SMW\Tests;
+
+use Language;
+use SMW\Localizer;
+
+/**
+ * @covers \SMW\Localizer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class LocalizerTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ Localizer::clear();
+ }
+
+ public function testCanConstruct() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Localizer',
+ new Localizer( $language )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Localizer',
+ Localizer::getInstance()
+ );
+ }
+
+ public function testGetContentLanguage() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $language );
+
+ $this->assertSame(
+ $language,
+ $instance->getContentLanguage()
+ );
+
+ $this->assertSame(
+ $GLOBALS['wgContLang'],
+ Localizer::getInstance()->getContentLanguage()
+ );
+ }
+
+ public function testNamespaceTextById() {
+
+ $instance = new Localizer( Language::factory( 'en') );
+
+ $this->assertEquals(
+ 'Property',
+ $instance->getNamespaceTextById( SMW_NS_PROPERTY )
+ );
+ }
+
+ public function testNamespaceIndexByName() {
+
+ $instance = new Localizer( Language::factory( 'en') );
+
+ $this->assertEquals(
+ SMW_NS_PROPERTY,
+ $instance->getNamespaceIndexByName( 'property' )
+ );
+ }
+
+ public function testSupportedLanguageForLowerCaseLetter() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( 'Skipping because `Language::isKnownLanguageTag` is not supported on 1.19' );
+ }
+
+ $this->assertTrue(
+ Localizer::isKnownLanguageTag( 'en' )
+ );
+ }
+
+ public function testSupportedLanguageForUpperCaseLetter() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( 'Skipping because `Language::isKnownLanguageTag` is not supported on 1.19' );
+ }
+
+ $this->assertTrue(
+ Localizer::isKnownLanguageTag( 'ZH-HANS' )
+ );
+ }
+
+ public function testAsBCP47FormattedLanguageCode() {
+ $this->assertEquals(
+ 'zh-Hans',
+ Localizer::asBCP47FormattedLanguageCode( 'zh-hans' )
+ );
+ }
+
+ public function testCanGetAnnotatedLanguageCodeOnValidMarkedValue() {
+
+ $value = 'Foo@en';
+
+ $this->assertEquals(
+ 'en',
+ Localizer::getAnnotatedLanguageCodeFrom( $value )
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $value
+ );
+ }
+
+ public function testCanGetAnnotatedLanguageCodeOnDoubledMarkedValue() {
+
+ $value = 'Foo@@en';
+
+ $this->assertEquals(
+ 'en',
+ Localizer::getAnnotatedLanguageCodeFrom( $value )
+ );
+
+ $this->assertEquals(
+ 'Foo@',
+ $value
+ );
+ }
+
+ public function testCanGetAnnotatedLanguageCodeOnValueWithDash() {
+
+ $value = 'Foo@zh-Hans';
+
+ $this->assertEquals(
+ 'zh-Hans',
+ Localizer::getAnnotatedLanguageCodeFrom( $value )
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $value
+ );
+ }
+
+ public function testCanNotGetAnnotatedLanguageCodeThatContainsInvalidCharacter() {
+
+ $value = 'Foo@en#bar';
+
+ $this->assertFalse(
+ Localizer::getAnnotatedLanguageCodeFrom( $value )
+ );
+ }
+
+ public function testCanNotGetLanguageCodeOnNonMarkedValue() {
+
+ $value = 'Fooen';
+
+ $this->assertFalse(
+ Localizer::getLanguageCodeFrom( $value )
+ );
+
+ $this->assertEquals(
+ 'Fooen',
+ $value
+ );
+ }
+
+ public function testCanNotGetLanguageCodeOnMissingLanguageCode() {
+
+ $value = 'Foo@';
+
+ $this->assertFalse(
+ Localizer::getLanguageCodeFrom( $value )
+ );
+
+ $this->assertEquals(
+ 'Foo@',
+ $value
+ );
+ }
+
+ public function testGetLanguageCodeByRule_OnTitleExpectedToPageLanguage() {
+
+ $contentLanguage = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $contentLanguage );
+
+ $pageLanguage = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $pageLanguage ) );
+
+ $this->assertEquals(
+ $pageLanguage,
+ $instance->getPreferredContentLanguage( $title )
+ );
+ }
+
+ public function testGetLanguageCodeByRule_OnNotProvidedTitlePageLanguageExpectedToReturnUserLanguage() {
+
+ $contentLanguage = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $contentLanguage );
+
+ $this->assertEquals(
+ $instance->getContentLanguage(),
+ $instance->getPreferredContentLanguage( null )
+ );
+ }
+
+ public function testLang() {
+
+ $instance = Localizer::getInstance();
+
+ $this->assertInstanceOf(
+ '\SMW\Lang\Lang',
+ $instance->getLang()
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Lang\Lang',
+ $instance->getLang( 'en' )
+ );
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'getCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $this->assertInstanceOf(
+ '\SMW\Lang\Lang',
+ $instance->getLang( $language )
+ );
+ }
+
+ public function testConvertDoubleWidth() {
+
+ $this->assertEquals(
+ '2000',
+ Localizer::convertDoubleWidth( 'ï¼’ï¼ï¼ï¼' )
+ );
+
+ $this->assertEquals(
+ 'aBc',
+ Localizer::getInstance()->convertDoubleWidth( 'ï½ï¼¢ï½ƒ' )
+ );
+ }
+
+ public function testCreateTextWithNamespacePrefix() {
+
+ $instance = new Localizer( Language::factory( 'en') );
+
+ $this->assertEquals(
+ 'Property:foo bar',
+ $instance->createTextWithNamespacePrefix( SMW_NS_PROPERTY, 'foo bar' )
+ );
+ }
+
+ public function testGetCanonicalizedUrlByNamespace() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->exactly( 3 ) )
+ ->method( 'getNsText' )
+ ->will( $this->returnValue( 'Spécial' ) );
+
+ $instance = new Localizer( $language );
+
+ $this->assertEquals(
+ 'http://example.org/wiki/Special:URIResolver/Property-3AHas_query',
+ $instance->getCanonicalizedUrlByNamespace( NS_SPECIAL, 'http://example.org/wiki/Sp%C3%A9cial:URIResolver/Property-3AHas_query' )
+ );
+
+ $this->assertEquals(
+ 'http://example.org/wiki/Special:URIResolver/Property-3AHas_query',
+ $instance->getCanonicalizedUrlByNamespace( NS_SPECIAL, 'http://example.org/wiki/Spécial:URIResolver/Property-3AHas_query' )
+ );
+
+ $this->assertEquals(
+ 'http://example.org/index.php?title=Special:URIResolver&Property-3AHas_query',
+ $instance->getCanonicalizedUrlByNamespace( NS_SPECIAL, 'http://example.org/index.php?title=Spécial:URIResolver&Property-3AHas_query' )
+ );
+ }
+
+ public function testGetCanonicalName() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $language );
+
+ $this->assertEquals(
+ 'Property',
+ $instance->getCanonicalNamespaceTextById( SMW_NS_PROPERTY )
+ );
+
+ $this->assertEquals(
+ 'Help',
+ $instance->getCanonicalNamespaceTextById( NS_HELP )
+ );
+ }
+
+ public function testHasLocalTimeOffsetPreference() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'smw-prefs-general-options-time-correction' ) )
+ ->will( $this->returnValue( true ) );
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $language );
+
+ $this->assertTrue(
+ $instance->hasLocalTimeOffsetPreference( $user )
+ );
+ }
+
+ public function testGetLocalTime() {
+
+ $dataTime = $this->getMockBuilder( '\DateTime' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Localizer( $language );
+
+ $this->assertInstanceOf(
+ 'DateTime',
+ $instance->getLocalTime( $dataTime, $user )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ConceptCacheRebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ConceptCacheRebuilderTest.php
new file mode 100644
index 00000000..0751d743
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ConceptCacheRebuilderTest.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use SMW\DIConcept;
+use SMW\Maintenance\ConceptCacheRebuilder;
+
+/**
+ * @covers \SMW\Maintenance\ConceptCacheRebuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class ConceptCacheRebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockForAbstractClass( '\SMW\Store' );
+
+ $settings = $this->getMockBuilder( '\SMW\Settings' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\ConceptCacheRebuilder',
+ new ConceptCacheRebuilder( $store, $settings )
+ );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildWithoutOptionsAndActions() {
+
+ $store = $this->getMockForAbstractClass( '\SMW\Store' );
+
+ $settings = $this->getMockBuilder( '\SMW\Settings' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConceptCacheRebuilder(
+ $store,
+ $settings
+ );
+
+ $this->assertFalse( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildFullConceptWithoutRangeSelectionOnMockStore( $action ) {
+
+ $concept = new DIConcept( 'Foo', '', '', '', '' );
+
+ $concept->setCacheStatus( 'full' );
+ $concept->setCacheDate( '1358515326' );
+ $concept->setCacheCount( '1000' );
+
+ $instance = $this->acquireInstanceFor( $concept );
+
+ $instance->setParameters( [
+ $action => true
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildEmptyConceptWithoutRangeSelectionOnMockStore( $action ) {
+
+ $concept = new DIConcept( 'Foo', '', '', '', '' );
+ $concept->setCacheStatus( 'empty' );
+
+ $instance = $this->acquireInstanceFor( $concept );
+
+ $instance->setParameters( [
+ $action => true
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildFullConceptWithRangeSelectionOnMockStore( $action ) {
+
+ $concept = new DIConcept( 'Foo', '', '', '', '' );
+
+ $concept->setCacheStatus( 'full' );
+ $concept->setCacheDate( '1358515326' );
+ $concept->setCacheCount( '1000' );
+
+ $instance = $this->acquireInstanceFor( $concept );
+
+ $instance->setParameters( [
+ $action => true,
+ 's' => 0,
+ 'e' => 90000
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildSingleEmptyConceptWithRangeSelectionOnMockStore( $action ) {
+
+ $concept = new DIConcept( 'Foo', '', '', '', '' );
+ $concept->setCacheStatus( 'empty' );
+
+ $instance = $this->acquireInstanceFor( $concept );
+
+ $instance->setParameters( [
+ $action => true,
+ 's' => 0,
+ 'e' => 90000
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildSingleFullConceptOnMockStore( $action ) {
+
+ $concept = new DIConcept( 'Foo', '', '', '', '' );
+
+ $concept->setCacheStatus( 'full' );
+ $concept->setCacheDate( '1358515326' );
+ $concept->setCacheCount( '1000' );
+
+ $instance = $this->acquireInstanceFor( $concept );
+
+ $instance->setParameters( [
+ $action => true,
+ 'old' => 10,
+ 'concept' => 'Bar'
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @dataProvider actionProvider
+ */
+ public function testRebuildWithNullConceptOnMockStore( $action ) {
+
+ $instance = $this->acquireInstanceFor( null );
+
+ $instance->setParameters( [
+ $action => true,
+ 'concept' => 'Bar'
+ ] );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ private function acquireInstanceFor( $concept = null ) {
+
+ $expectedToRun = $concept !== null ? $this->any() : $this->never();
+ $refreshConceptCacheReturn = $concept !== null ? $concept->getConceptQuery() : null;
+
+ $row = new \stdClass;
+ $row->page_namespace = 0;
+ $row->page_title = 1;
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $expectedToRun )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConceptCacheStatus' )
+ ->will( $this->returnValue( $concept ) );
+
+ $store->expects( $expectedToRun )
+ ->method( 'refreshConceptCache' )
+ ->will( $this->returnValue( [ $refreshConceptCacheReturn ] ) );
+
+ $store->expects( $expectedToRun )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $settings = $this->getMockBuilder( '\SMW\Settings' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConceptCacheRebuilder(
+ $store,
+ $settings
+ );
+
+ $instance->setParameters( [
+ 'quiet' => true,
+ ] );
+
+ return $instance;
+ }
+
+ public function actionProvider() {
+ return [
+ [ 'status' ],
+ [ 'create' ],
+ [ 'delete' ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DataRebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DataRebuilderTest.php
new file mode 100644
index 00000000..a3b3a441
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DataRebuilderTest.php
@@ -0,0 +1,420 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use SMW\Maintenance\DataRebuilder;
+use SMW\Options;
+use Title;
+use SMW\Tests\PHPUnitCompat;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Maintenance\DataRebuilder
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class DataRebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ protected $obLevel;
+ private $connectionManager;
+ private $testEnvironment;
+
+ // The Store writes to the output buffer during drop/setupStore, to avoid
+ // inappropriate buffer settings which can cause interference during unit
+ // testing, we clean the output buffer
+ protected function setUp() {
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'newUpdateJob' ] )
+ ->getMock();
+
+ $jobFactory->expects( $this->any() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->obLevel = ob_get_level();
+ ob_start();
+
+ parent::setUp();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ $this->testEnvironment->tearDown();
+
+ while ( ob_get_level() > $this->obLevel ) {
+ ob_end_clean();
+ }
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\DataRebuilder',
+ new DataRebuilder( $store, $titleFactory )
+ );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildAllWithoutOptions() {
+
+ $entityRebuildDispatcher = $this->getMockBuilder( '\SMW\SQLStore\EntityRebuildDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityRebuildDispatcher->expects( $this->once() )
+ ->method( 'rebuild' )
+ ->will( $this->returnCallback( [ $this, 'refreshDataOnMockCallback' ] ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getMaxId' )
+ ->will( $this->returnValue( 1000 ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getDispatchedEntities' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'refreshData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'refreshData' )
+ ->will( $this->returnValue( $entityRebuildDispatcher ) );
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ // Needs an end otherwise phpunit is caught up in an infinite loop
+ $instance->setOptions( new Options( [
+ 'e' => 1
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildAllWithFullDelete() {
+
+ $entityRebuildDispatcher = $this->getMockBuilder( '\SMW\SQLStore\EntityRebuildDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityRebuildDispatcher->expects( $this->atLeastOnce() )
+ ->method( 'rebuild' )
+ ->will( $this->returnCallback( [ $this, 'refreshDataOnMockCallback' ] ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getMaxId' )
+ ->will( $this->returnValue( 1000 ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getDispatchedEntities' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'refreshData',
+ 'drop' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'refreshData' )
+ ->will( $this->returnValue( $entityRebuildDispatcher ) );
+
+ $store->expects( $this->once() )
+ ->method( 'drop' );
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 'e' => 1,
+ 'f' => true,
+ 'verbose' => false
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildAllWithStopRangeOption() {
+
+ $entityRebuildDispatcher = $this->getMockBuilder( '\SMW\SQLStore\EntityRebuildDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityRebuildDispatcher->expects( $this->exactly( 6 ) )
+ ->method( 'rebuild' )
+ ->will( $this->returnCallback( [ $this, 'refreshDataOnMockCallback' ] ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getMaxId' )
+ ->will( $this->returnValue( 1000 ) );
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'getDispatchedEntities' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'refreshData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'refreshData' )
+ ->will( $this->returnValue( $entityRebuildDispatcher ) );
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 's' => 2,
+ 'n' => 5,
+ 'verbose' => false
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildSelectedPagesWithQueryOption() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( Title::newFromText( __METHOD__ ) ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [ $subject ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->at( 0 ) )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( 1 ) );
+
+ $store->expects( $this->at( 1 ) )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 'query' => '[[Category:Foo]]'
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ public function testRebuildSelectedPagesWithCategoryNamespaceFilter() {
+
+ $row = new \stdClass;
+ $row->cat_title = 'Foo';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->stringContains( 'category' ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 'categories' => true
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ public function testRebuildSelectedPagesWithPropertyNamespaceFilter() {
+
+ $row = new \stdClass;
+ $row->page_namespace = SMW_NS_PROPERTY;
+ $row->page_title = 'Bar';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'page_namespace' => SMW_NS_PROPERTY ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 'p' => true
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+ }
+
+ public function testRebuildSelectedPagesWithPageOption() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $titleFactory->expects( $this->at( 0 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page' ) ) );
+
+ $titleFactory->expects( $this->at( 1 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Some other page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Some other page' ) ) );
+
+ $titleFactory->expects( $this->at( 2 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Help:Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page', NS_HELP ) ) );
+
+ $titleFactory->expects( $this->at( 3 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page' ) ) );
+
+ $instance = new DataRebuilder( $store, $titleFactory );
+
+ $instance->setOptions( new Options( [
+ 'page' => 'Main page|Some other page|Help:Main page|Main page'
+ ] ) );
+
+ $this->assertTrue( $instance->rebuild() );
+
+ $this->assertEquals(
+ 3,
+ $instance->getRebuildCount()
+ );
+ }
+
+ /**
+ * @see Store::refreshData
+ */
+ public function refreshDataOnMockCallback( &$index ) {
+ $index++;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DistinctEntityDataRebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DistinctEntityDataRebuilderTest.php
new file mode 100644
index 00000000..00962275
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DistinctEntityDataRebuilderTest.php
@@ -0,0 +1,290 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use SMW\Maintenance\DistinctEntityDataRebuilder;
+use SMW\Options;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\Maintenance\DistinctEntityDataRebuilder
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DistinctEntityDataRebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ protected $obLevel;
+ private $connectionManager;
+ private $testEnvironment;
+
+ // The Store writes to the output buffer during drop/setupStore, to avoid
+ // inappropriate buffer settings which can cause interference during unit
+ // testing, we clean the output buffer
+ protected function setUp() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->setOption( 'smwgSemanticsEnabled', true );
+ $store->setOption( 'smwgAutoRefreshSubject', true );
+
+ $this->testEnvironment = new TestEnvironment();
+ $spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $store->setLogger( $spyLogger );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->obLevel = ob_get_level();
+ ob_start();
+
+ parent::setUp();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ $this->testEnvironment->tearDown();
+
+ while ( ob_get_level() > $this->obLevel ) {
+ ob_end_clean();
+ }
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\DistinctEntityDataRebuilder',
+ new DistinctEntityDataRebuilder( $store, $titleFactory )
+ );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testRebuildSelectedPagesWithQueryOption() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( Title::newFromText( __METHOD__ ) ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [ $subject ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->at( 0 ) )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( 1 ) );
+
+ $store->expects( $this->at( 1 ) )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DistinctEntityDataRebuilder(
+ $store,
+ $titleFactory
+ );
+
+ $instance->setOptions( new Options( [
+ 'query' => '[[Category:Foo]]'
+ ] ) );
+
+ $this->assertTrue(
+ $instance->doRebuild()
+ );
+ }
+
+ public function testRebuildSelectedPagesWithCategoryNamespaceFilter() {
+
+ $row = new \stdClass;
+ $row->cat_title = 'Foo';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->stringContains( 'category' ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DistinctEntityDataRebuilder(
+ $store,
+ $titleFactory
+ );
+
+ $instance->setOptions( new Options( [
+ 'categories' => true
+ ] ) );
+
+ $this->assertTrue(
+ $instance->doRebuild()
+ );
+ }
+
+ public function testRebuildSelectedPagesWithPropertyNamespaceFilter() {
+
+ $row = new \stdClass;
+ $row->page_namespace = SMW_NS_PROPERTY;
+ $row->page_title = 'Bar';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'page_namespace' => SMW_NS_PROPERTY ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DistinctEntityDataRebuilder(
+ $store,
+ $titleFactory
+ );
+
+ $instance->setOptions( new Options( [
+ 'p' => true
+ ] ) );
+
+ $this->assertTrue(
+ $instance->doRebuild()
+ );
+ }
+
+ public function testRebuildSelectedPagesWithPageOption() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $titleFactory->expects( $this->at( 0 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page' ) ) );
+
+ $titleFactory->expects( $this->at( 1 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Some other page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Some other page' ) ) );
+
+ $titleFactory->expects( $this->at( 2 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Help:Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page', NS_HELP ) ) );
+
+ $titleFactory->expects( $this->at( 3 ) )
+ ->method( 'newFromText' )
+ ->with( $this->equalTo( 'Main page' ) )
+ ->will( $this->returnValue( Title::newFromText( 'Main page' ) ) );
+
+ $instance = new DistinctEntityDataRebuilder(
+ $store,
+ $titleFactory
+ );
+
+ $instance->setOptions( new Options( [
+ 'page' => 'Main page|Some other page|Help:Main page|Main page'
+ ] ) );
+
+ $this->assertTrue(
+ $instance->doRebuild()
+ );
+
+ $this->assertEquals(
+ 3,
+ $instance->getRebuildCount()
+ );
+ }
+
+ /**
+ * @see Store::refreshData
+ */
+ public function refreshDataOnMockCallback( &$index ) {
+ $index++;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DuplicateEntitiesDisposerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DuplicateEntitiesDisposerTest.php
new file mode 100644
index 00000000..b1f6cb10
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/DuplicateEntitiesDisposerTest.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use FakeResultWrapper;
+use SMW\Maintenance\DuplicateEntitiesDisposer;
+
+/**
+ * @covers \SMW\Maintenance\DuplicateEntitiesDisposer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DuplicateEntitiesDisposerTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $cache;
+ private $messageReporter;
+ private $connection;
+ private $propertyTableIdReferenceFinder;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyTableIdReferenceFinder = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableIdReferenceFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTableIdReferenceFinder' )
+ ->will( $this->returnValue( $this->propertyTableIdReferenceFinder ) );
+
+ $this->messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DuplicateEntitiesDisposer::class,
+ new DuplicateEntitiesDisposer( $this->store )
+ );
+ }
+
+ public function testFindDuplicateEntityRecords() {
+
+ $idTable = $this->getMockBuilder( '\stdClss' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'findDuplicates' ] )
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new DuplicateEntitiesDisposer(
+ $this->store
+ );
+
+ $instance->findDuplicates();
+ }
+
+ public function testVerifyAndDispose_NoDuplicates() {
+
+ $this->store->expects( $this->never() )
+ ->method( 'getConnection' );
+
+ $instance = new DuplicateEntitiesDisposer(
+ $this->store
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->verifyAndDispose( [] );
+ }
+
+ public function testVerifyAndDispose_NonIterable() {
+
+ $this->store->expects( $this->never() )
+ ->method( 'getConnection' );
+
+ $instance = new DuplicateEntitiesDisposer(
+ $this->store
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->verifyAndDispose( 'Foo' );
+ }
+
+ public function testVerifyAndDispose_NoDuplicates_WithCache() {
+
+ $this->store->expects( $this->never() )
+ ->method( 'getConnection' );
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'delete' );
+
+ $instance = new DuplicateEntitiesDisposer(
+ $this->store,
+ $this->cache
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $instance->verifyAndDispose( [] );
+ }
+
+ public function testVerifyAndDispose_WithDuplicateRecord() {
+
+ $record = [
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => 0,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( $record ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $idTable = $this->getMockBuilder( '\stdClss' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getDataItemById' ] )
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertyTableIdReferenceFinder->expects( $this->atLeastOnce() )
+ ->method( 'hasResidualReferenceForId' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new DuplicateEntitiesDisposer(
+ $this->store
+ );
+
+ $instance->setMessageReporter(
+ $this->messageReporter
+ );
+
+ $duplicates = [
+ $record
+ ];
+
+ $instance->verifyAndDispose( $duplicates );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ExceptionFileLoggerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ExceptionFileLoggerTest.php
new file mode 100644
index 00000000..b64a75af
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/ExceptionFileLoggerTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\Maintenance\Jobs;
+
+use SMW\Maintenance\ExceptionFileLogger;
+use SMW\Options;
+
+/**
+ * @covers \SMW\Maintenance\ExceptionFileLogger
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ExceptionFileLoggerTest extends \PHPUnit_Framework_TestCase {
+
+ private $file;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->file = $this->getMockBuilder( '\SMW\Utils\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ExceptionFileLogger::class,
+ new ExceptionFileLogger()
+ );
+ }
+
+ public function testGetter() {
+
+ $instance = new ExceptionFileLogger();
+
+ $instance->setOptions( new Options( [
+ 'exception-log' => __DIR__
+ ] ) );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->getExceptionFile()
+ );
+
+ $this->assertInternalType(
+ 'integer',
+ $instance->getExceptionCount()
+ );
+ }
+
+ public function testDoWriteExceptionLog() {
+
+ $this->file->expects( $this->once() )
+ ->method( 'write' );
+
+ $instance = new ExceptionFileLogger( 'foo', $this->file );
+
+ $instance->recordException(
+ 'Foo',
+ new \Exception( 'Bar' )
+ );
+
+ $this->assertEquals(
+ 1,
+ $instance->getExceptionCount()
+ );
+
+ $instance->doWrite();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceFactoryTest.php
new file mode 100644
index 00000000..936a9c65
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceFactoryTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\Maintenance\Jobs;
+
+use SMW\Maintenance\MaintenanceFactory;
+
+/**
+ * @covers \SMW\Maintenance\MaintenanceFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MaintenanceFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceFactory',
+ new MaintenanceFactory()
+ );
+ }
+
+ public function testCanConstructMaintenanceHelper() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceHelper',
+ $instance->newMaintenanceHelper()
+ );
+ }
+
+ public function testCanConstructDataRebuilder() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\DataRebuilder',
+ $instance->newDataRebuilder( $this->store )
+ );
+ }
+
+ public function testCanConstructConceptCacheRebuilder() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\ConceptCacheRebuilder',
+ $instance->newConceptCacheRebuilder( $this->store )
+ );
+ }
+
+ public function testCanConstructPropertyStatisticsRebuilder() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\PropertyStatisticsRebuilder',
+ $instance->newPropertyStatisticsRebuilder( $this->store )
+ );
+ }
+
+ public function testCanConstructRebuildPropertyStatistics() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\RebuildPropertyStatistics',
+ $instance->newRebuildPropertyStatistics()
+ );
+ }
+
+ public function testCanConstructDuplicateEntitiesDisposer() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\DuplicateEntitiesDisposer',
+ $instance->newDuplicateEntitiesDisposer( $this->store )
+ );
+ }
+
+ public function testCanConstructMaintenanceLogger() {
+
+ $instance = new MaintenanceFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceLogger',
+ $instance->newMaintenanceLogger( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceHelperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceHelperTest.php
new file mode 100644
index 00000000..a4587621
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceHelperTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use SMW\Maintenance\MaintenanceHelper;
+
+/**
+ * @covers \SMW\Maintenance\MaintenanceHelper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MaintenanceHelperTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceHelper',
+ new MaintenanceHelper()
+ );
+ }
+
+ public function testSetGlobalForValidKey() {
+
+ $GLOBALS['FOObar'] = 42;
+
+ $instance = new MaintenanceHelper();
+ $instance->setGlobalToValue( 'FOObar', 99 );
+
+ $this->assertEquals(
+ $GLOBALS['FOObar'],
+ 99
+ );
+
+ $instance->reset();
+
+ $this->assertEquals(
+ $GLOBALS['FOObar'],
+ 42
+ );
+
+ unset( $GLOBALS['FOObar'] );
+ }
+
+ public function testTrySetGlobalForInvalidKey() {
+
+ $instance = new MaintenanceHelper();
+ $instance->setGlobalToValue( 'FOObar', 99 );
+
+ $this->assertFalse(
+ isset( $GLOBALS['FOObar'] )
+ );
+ }
+
+ /**
+ * @dataProvider runtimeKeyValueProvider
+ */
+ public function testRuntimeMonitor( $runtimeKey ) {
+
+ $instance = new MaintenanceHelper();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getRuntimeValues()
+ );
+
+ $instance->initRuntimeValues();
+
+ $this->assertArrayHasKey(
+ $runtimeKey,
+ $instance->getRuntimeValues()
+ );
+
+ $instance->reset();
+ }
+
+ public function testTransformRuntimeValuesForOutput() {
+
+ $instance = new MaintenanceHelper();
+ $instance->initRuntimeValues();
+
+ $this->assertContains(
+ 'sec',
+ $instance->getFormattedRuntimeValues()
+ );
+
+ $instance->reset();
+ }
+
+ public function runtimeKeyValueProvider() {
+
+ $provider = [
+ [ 'time' ],
+ [ 'memory-before' ],
+ [ 'memory-after' ],
+ [ 'memory-used' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceLoggerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceLoggerTest.php
new file mode 100644
index 00000000..89c89c74
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/MaintenanceLoggerTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use SMW\Maintenance\MaintenanceLogger;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Maintenance\MaintenanceLogger
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MaintenanceLoggerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $manualEntryLogger = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Maintenance\MaintenanceLogger',
+ new MaintenanceLogger( 'Foo', $manualEntryLogger )
+ );
+ }
+
+ public function testLog() {
+
+ $manualEntryLogger = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualEntryLogger->expects( $this->once() )
+ ->method( 'log' )
+ ->with(
+ $this->stringContains( 'maintenance' ),
+ $this->equalTo( 'Foo' ),
+ $this->equalTo( 'Foo' ),
+ $this->stringContains( 'bar' ) );
+
+ $instance = new MaintenanceLogger( 'Foo', $manualEntryLogger );
+ $instance->log( 'bar' );
+ }
+
+ public function testLogWithInvalidNameLengthThrowsException() {
+
+ $manualEntryLogger = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualEntryLogger->expects( $this->never() )
+ ->method( 'log' );
+
+ $instance = new MaintenanceLogger( 'Foo', $manualEntryLogger );
+ $instance->setMaxNameChars( 2 );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->log( 'bar' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/PropertyStatisticsRebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/PropertyStatisticsRebuilderTest.php
new file mode 100644
index 00000000..aa71051d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Maintenance/PropertyStatisticsRebuilderTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\Maintenance;
+
+use FakeResultWrapper;
+use SMW\Maintenance\PropertyStatisticsRebuilder;
+
+/**
+ * @covers \SMW\Maintenance\PropertyStatisticsRebuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class PropertyStatisticsRebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ PropertyStatisticsRebuilder::class,
+ new PropertyStatisticsRebuilder( $store, $propertyStatisticsStore )
+ );
+ }
+
+ public function testRebuildStatisticsStoreAndInsertCountRows() {
+
+ $tableName = 'Foobar';
+
+ $uRow = new \stdClass;
+ $uRow->count = 1111;
+
+ $nRow = new \stdClass;
+ $nRow->count = 1;
+
+ $res = [
+ 'smw_title' => 'Foo',
+ 'smw_id' => 9999
+ ];
+
+ $resultWrapper = new FakeResultWrapper(
+ [ (object)$res ]
+ );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->atLeastOnce() )
+ ->method( 'getTableFields' )
+ ->will( $this->returnValue( [] ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $database->expects( $this->atLeastOnce() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->stringContains( $tableName ),
+ $this->anything(),
+ $this->equalTo( [ 'p_id' => 9999 ] ),
+ $this->anything() )
+ ->will( $this->onConsecutiveCalls( $uRow, $nRow ) );
+
+ $store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $this->newPropertyTable( $tableName ) ] ) );
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsStore->expects( $this->atLeastOnce() )
+ ->method( 'insertUsageCount' )
+ ->with(
+ $this->equalTo( 9999 ),
+ $this->equalTo( [ 1110, 1 ] ) );
+
+ $instance = new PropertyStatisticsRebuilder(
+ $store,
+ $propertyStatisticsStore
+ );
+
+ $instance->rebuild();
+ }
+
+ protected function newPropertyTable( $propertyTableName, $fixedPropertyTable = false ) {
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isFixedPropertyTable', 'getName' ] )
+ ->getMock();
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( $fixedPropertyTable ) );
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( $propertyTableName ) );
+
+ return $propertyTable;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiQueryResultFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiQueryResultFormatterTest.php
new file mode 100644
index 00000000..53858fb2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiQueryResultFormatterTest.php
@@ -0,0 +1,344 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\ApiQueryResultFormatter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\Api\ApiQueryResultFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ApiQueryResultFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Api\ApiQueryResultFormatter',
+ new ApiQueryResultFormatter( $queryResult )
+ );
+ }
+
+ public function testInvalidSetIndexedTagNameThrowsException() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ApiQueryResultFormatter( $queryResult );
+ $instance->setIsRawMode( true );
+
+ // Used to work in PHP 5 but not with PHP 7, made the
+ // method public to test the exception without reflection
+ // $reflector = new ReflectionClass( 'SMW\MediaWiki\Api\ApiQueryResultFormatter' );
+ // $method = $reflector->getMethod( 'setIndexedTagName' );
+ // $method->setAccessible( true );
+ // $method->invoke( $instance, $arr, null )
+
+ $arr = [];
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->setIndexedTagName( $arr, null );
+ }
+
+ /**
+ * @dataProvider resultDataProvider
+ */
+ public function testResultFormat( array $parameters, array $expected ) {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( $parameters['result'] ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'hasFurtherResults' )
+ ->will( $this->returnValue( $parameters['furtherResults'] ) );
+
+ $instance = new ApiQueryResultFormatter( $queryResult );
+
+ $instance->setIsRawMode( $parameters['rawMode'] );
+ $instance->doFormat();
+
+ $this->assertEquals( 'query', $instance->getType() );
+
+ $this->assertEquals(
+ $expected['result'],
+ $instance->getResult()
+ );
+
+ $this->assertEquals(
+ $expected['continueOffset'],
+ $instance->getContinueOffset()
+ );
+ }
+
+ /**
+ * @dataProvider errorDataProvider
+ */
+ public function testErrorFormat( array $parameters, array $expected ) {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( $parameters['errors'] ) );
+
+ $instance = new ApiQueryResultFormatter( $queryResult );
+
+ $instance->setIsRawMode( $parameters['rawMode'] );
+ $instance->doFormat();
+
+ $this->assertEquals( 'error', $instance->getType() );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getResult()
+ );
+ }
+
+ public function resultDataProvider() {
+
+ $result = [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [ 'lula' => [ 'lila' ] ]
+ ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [ 'count' => 5, 'offset' => 5 ]
+ ];
+
+ $xml = [
+ 'results' => [
+ [
+ 'printouts' => [
+ [ 'label' => 'lula', 'lila', '_element' => 'value' ],
+ '_element' => 'property' ]
+ ],
+ '_element' => 'subject'
+ ],
+ 'printrequests' => [
+ 'Bar',
+ '_element' => 'printrequest'
+ ],
+ 'meta' => [ 'count' => 5, 'offset' => 5, '_element' => 'meta' ]
+ ];
+
+ $provider = [];
+
+ // #0 Without further results
+ $provider[] = [
+ [
+ 'result' => $result,
+ 'rawMode' => false,
+ 'furtherResults' => false
+ ],
+ [
+ 'result' => $result,
+ 'continueOffset' => false
+ ]
+ ];
+
+ // #1 Without further results + XML
+ $provider[] = [
+ [
+ 'result' => $result,
+ 'rawMode' => true,
+ 'furtherResults' => false
+ ],
+ [
+ 'result' => $xml,
+ 'continueOffset' => false
+ ]
+ ];
+
+ // #2 With further results
+ $provider[] = [
+ [
+ 'result' => $result,
+ 'rawMode' => false,
+ 'furtherResults' => true
+ ],
+ [
+ 'result' => $result,
+ 'continueOffset' => 10
+ ]
+ ];
+
+ // #3 With further results + XML
+ $provider[] = [
+ [
+ 'result' => $result,
+ 'rawMode' => true,
+ 'furtherResults' => true
+ ],
+ [
+ 'result' => $xml,
+ 'continueOffset' => 10
+ ]
+ ];
+
+
+ // #4 Extended subject data + XML
+ $provider[] = [
+ [
+ 'result' => [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [
+ 'lula' => [ 'lila' ] ],
+ 'fulltext' => 'Foo' ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [
+ 'count' => 5,
+ 'offset' => 5
+ ]
+ ],
+ 'rawMode' => true,
+ 'furtherResults' => true
+ ],
+ [
+ 'result' => [
+ 'results' => [
+ [
+ 'printouts' => [
+ [
+ 'label' => 'lula',
+ 'lila', '_element' => 'value'
+ ], '_element' => 'property' ],
+ 'fulltext' => 'Foo'
+ ], '_element' => 'subject'
+ ],
+ 'printrequests' => [ 'Bar', '_element' => 'printrequest' ],
+ 'meta' => [
+ 'count' => 5,
+ 'offset' => 5,
+ '_element' => 'meta'
+ ]
+ ],
+ 'continueOffset' => 10
+ ]
+ ];
+
+ // #5 printouts without values + XML
+ $provider[] = [
+ [
+ 'result' => [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [ 'lula' ],
+ 'fulltext' => 'Foo' ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [
+ 'count' => 5,
+ 'offset' => 5
+ ]
+ ],
+ 'rawMode' => true,
+ 'furtherResults' => true
+ ],
+ [
+ 'result' => [
+ 'results' => [
+ [
+ 'printouts' => [ '_element' => 'property' ],
+ 'fulltext' => 'Foo'
+ ],
+ '_element' => 'subject'
+ ],
+ 'printrequests' => [ 'Bar', '_element' => 'printrequest' ],
+ 'meta' => [
+ 'count' => 5,
+ 'offset' => 5,
+ '_element' => 'meta'
+ ]
+ ],
+ 'continueOffset' => 10
+ ]
+ ];
+
+ // #6 empty results + XML
+ $provider[] = [
+ [
+ 'result' => [
+ 'results' => [],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [
+ 'count' => 0,
+ 'offset' => 0
+ ]
+ ],
+ 'rawMode' => true,
+ 'furtherResults' => false
+ ],
+ [
+ 'result' => [
+ 'results' => [],
+ 'printrequests' => [ 'Bar', '_element' => 'printrequest' ],
+ 'meta' => [
+ 'count' => 0,
+ 'offset' => 0,
+ '_element' => 'meta'
+ ]
+ ],
+ 'continueOffset' => 0
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function errorDataProvider() {
+
+ $errors = [ 'Foo', 'Bar' ];
+
+ $provider = [];
+
+ // #0
+ $provider[] = [
+ [
+ 'rawMode'=> false,
+ 'errors' => $errors
+ ],
+ [
+ 'query' => $errors
+ ]
+ ];
+
+ // #1
+ $provider[] = [
+ [
+ 'rawMode'=> true,
+ 'errors' => $errors
+ ],
+ [
+ 'query' => array_merge( $errors, [ '_element' => 'info' ] )
+ ]
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiRequestParameterFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiRequestParameterFormatterTest.php
new file mode 100644
index 00000000..7ab1fdd9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/ApiRequestParameterFormatterTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\ApiRequestParameterFormatter;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Api\ApiRequestParameterFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ApiRequestParameterFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ public function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ public function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Api\ApiRequestParameterFormatter',
+ new ApiRequestParameterFormatter( [] )
+ );
+ }
+
+ public function testGetAskArgsApiForEmptyParameter() {
+
+ $nstance = new ApiRequestParameterFormatter( [] );
+
+ $this->assertEmpty( $nstance->getAskArgsApiParameter( 'conditions' ) );
+ $this->assertEmpty( $nstance->getAskArgsApiParameter( 'parameters' ) );
+ $this->assertEmpty( $nstance->getAskArgsApiParameter( 'printouts' ) );
+ }
+
+ /**
+ * @dataProvider requestArgsApiParametersDataProvider
+ */
+ public function testGetAskArgsApiParameter( $parameters, $type, $expected ) {
+
+ $nstance = new ApiRequestParameterFormatter( $parameters );
+
+ $this->assertEquals(
+ $expected,
+ $nstance->getAskArgsApiParameter( $type )
+ );
+ }
+
+ /**
+ * @dataProvider requestAskApiParametersDataProvider
+ */
+ public function testGetAskApiParameters( $parameters, $expected ) {
+
+ $instance = new ApiRequestParameterFormatter( $parameters );
+ $result = $instance->getAskApiParameters();
+
+ $this->assertInternalType( 'array', $result );
+ $this->assertEquals( $expected, $result );
+ }
+
+ public function requestArgsApiParametersDataProvider() {
+ return [
+ [ [ 'conditions' => [ 'Lala' ] ], 'conditions', '[[Lala]]' ],
+ [ [ 'conditions' => [ 'Lala', 'Lima' ] ], 'conditions', '[[Lala]] [[Lima]]' ],
+ [ [ 'parameters' => [ 'Lila' ] ], 'parameters', [] ],
+ [ [ 'parameters' => [ 'Lila=isFunny' ] ], 'parameters', [ 'Lila' => 'isFunny' ] ],
+ [ [ 'parameters' => [ 'Lila=isFunny', 'Lula=isHappy' ] ], 'parameters', [ 'Lila' => 'isFunny', 'Lula' => 'isHappy' ] ],
+ // array( array( 'printouts' => array( '?Linda' ) ), 'printouts', array( $this->newPrintRequest( '?Linda' ) ) ),
+ // array( array( 'printouts' => array( '?Luna', '?Mars' ) ), 'printouts', array( $this->newPrintRequest( '?Luna' ), $this->newPrintRequest( '?Mars' ) ) ),
+ ];
+ }
+
+ public function requestAskApiParametersDataProvider() {
+ return [
+ [ [], [] ],
+ [ [ 'query' => '[[Modification date::+]]' ], [ '[[Modification date::+]]' ] ],
+ [ [ 'query' => '[[Modification date::+]]|?Modification date' ], [ '[[Modification date::+]]', '?Modification date' ] ],
+ [ [ 'query' => '[[Modification date::+]]|?Modification date|sort=desc' ], [ '[[Modification date::+]]', '?Modification date', 'sort=desc' ] ],
+ ];
+ }
+
+ private function newPrintRequest( $printout ) {
+ return new \SMW\Query\PrintRequest(
+ \SMW\Query\PrintRequest::PRINT_PROP,
+ $printout,
+ \SMW\DataValueFactory::getInstance()->newPropertyValueByLabel( $printout )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskArgsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskArgsTest.php
new file mode 100644
index 00000000..80a809ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskArgsTest.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Api\AskArgs;
+use SMW\Tests\Utils\MwApiFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Api\AskArgs
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class AskArgsTest extends \PHPUnit_Framework_TestCase {
+
+ private $apiFactory;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->apiFactory = new MwApiFactory();
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ ApplicationFactory::clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new AskArgs(
+ $this->apiFactory->newApiMain( [] ),
+ 'askargs'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Api\AskArgs',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testExecuteOnStore( array $query, array $expected ) {
+
+ $results = $this->apiFactory->doApiRequest( [
+ 'action' => 'askargs',
+ 'conditions' => $query['conditions'],
+ 'printouts' => $query['printouts'],
+ 'parameters' => $query['parameters'],
+ ] );
+
+ $this->assertInternalType( 'array', $results );
+
+ if ( isset( $expected['error'] ) ) {
+ return $this->assertArrayHasKey( 'error', $results );
+ }
+
+ $this->assertEquals(
+ $expected,
+ $results['query']['printrequests']
+ );
+ }
+
+ public function testExecuteOnMockStore() {
+
+ $requestParameters = [
+ 'conditions' => 'Foo::+',
+ 'printouts' => 'Bar',
+ 'parameters' => 'sort=asc'
+ ];
+
+ $expected = [
+ 'query-continue-offset' => 10,
+ 'query' => [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [ 'lula' => [ 'lila' ] ]
+ ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [ 'count' => 5, 'offset' => 5 ]
+ ]
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnCallback( [ $this, 'mockStoreQueryResultCallback' ] ) );
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $instance = new AskArgs(
+ $this->apiFactory->newApiMain( $requestParameters ),
+ 'askargs'
+ );
+
+ $instance->execute();
+
+ // MW 1.25
+ $result = method_exists( $instance->getResult(), 'getResultData' ) ? $instance->getResult()->getResultData() : $instance->getResultData();
+
+ // This came with 1.25, no idea what this suppose to be
+ unset( $result['_type'] );
+
+ $this->assertInternalType( 'array', $result );
+ $this->assertEquals( $expected, $result );
+ }
+
+ public function mockStoreQueryResultCallback( $query ) {
+
+ $result = '';
+
+ if ( $query->getQueryString() === '[[Foo::+]]' ) {
+ $result = [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [ 'lula' => [ 'lila' ] ]
+ ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [ 'count' => 5, 'offset' => 5 ]
+ ];
+ }
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( $result ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'hasFurtherResults' )
+ ->will( $this->returnValue( true ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ return $queryResult;
+ }
+
+ public function queryDataProvider() {
+ return [
+
+ // #0 Query producing an error result
+ [
+ [
+ 'conditions' => '[[Modification date::+]]',
+ 'printouts' => null,
+ 'parameters' => null
+ ],
+ [
+ 'error' => true
+ ]
+ ],
+
+ // #1 Query producing an error result
+ [
+ [
+ 'conditions' => '[[Modification date::+]]',
+ 'printouts' => null,
+ 'parameters' => 'limit=10'
+ ],
+ [
+ 'error' => true
+ ]
+ ],
+
+ // #2 Query producing an error result
+ [
+ [
+ 'conditions' => '[[Modification date::+]]',
+ 'printouts' => 'Modification date',
+ 'parameters' => 'limit=10'
+ ],
+ [
+ 'error' => true
+ ]
+ ],
+
+ // #3 Query producing a return result
+ [
+ [
+ 'conditions' => 'Modification date::+',
+ 'printouts' => null,
+ 'parameters' => null
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ]
+ ]
+ ],
+
+ // #4 Query producing a return result
+ [
+ [
+ 'conditions' => 'Modification date::+',
+ 'printouts' => 'Modification date',
+ 'parameters' => null
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ],
+ [
+ 'label'=> 'Modification date',
+ 'typeid' => '_dat',
+ 'mode' => 1,
+ 'format' => '',
+ 'key' => '_MDAT',
+ 'redi' => ''
+ ]
+ ]
+ ],
+
+ // #5 Query producing a return result
+ [
+ [
+ 'conditions' => 'Modification date::+',
+ 'printouts' => 'Modification date',
+ 'parameters' => 'limit=1'
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ],
+ [
+ 'label'=> 'Modification date',
+ 'typeid' => '_dat',
+ 'mode' => 1,
+ 'format' => '',
+ 'key' => '_MDAT',
+ 'redi' => ''
+ ]
+ ]
+ ],
+ ];
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskTest.php
new file mode 100644
index 00000000..05804efd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/AskTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\Ask;
+use SMW\Tests\Utils\MwApiFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Ask
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class AskTest extends \PHPUnit_Framework_TestCase {
+
+ private $apiFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->apiFactory = new MwApiFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new Ask(
+ $this->apiFactory->newApiMain( [ 'query' => 'Foo' ] ),
+ 'ask'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Api\Ask',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider sampleQueryProvider
+ */
+ public function testExecute( array $query, array $expected ) {
+
+ $results = $this->apiFactory->doApiRequest( [
+ 'action' => 'ask',
+ 'query' => implode( '|', $query )
+ ] );
+
+ $this->assertInternalType( 'array', $results );
+
+ // If their is no printrequests array we expect an error array
+ if ( isset( $results['query']['printrequests'] ) ) {
+ return $this->assertEquals( $expected, $results['query']['printrequests'] );
+ }
+
+ $this->assertArrayHasKey( 'error', $results );
+ }
+
+ public function sampleQueryProvider() {
+
+ // #0 Standard query
+ $provider[] = [
+ [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'limit=10'
+ ],
+ [
+ [
+ 'label'=> '',
+ 'typeid' => '_wpg',
+ 'mode' => 2,
+ 'format' => false,
+ 'key' => '',
+ 'redi' => ''
+ ],
+ [
+ 'label'=> 'Modification date',
+ 'typeid' => '_dat',
+ 'mode' => 1,
+ 'format' => '',
+ 'key' => '_MDAT',
+ 'redi' => ''
+ ]
+ ]
+ ];
+
+ $provider[] = [
+ [
+ '[[Modification date::+!]]',
+ 'limit=3'
+ ],
+ [
+ [
+ 'error'=> 'foo',
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleAugmentorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleAugmentorTest.php
new file mode 100644
index 00000000..7b15550b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleAugmentorTest.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\Api\Browse\ArticleAugmentor;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\ArticleAugmentor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleAugmentorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ArticleAugmentor::class,
+ new ArticleAugmentor( $titleFactory )
+ );
+ }
+
+ public function testAugmentOnFullText() {
+
+ $res = [
+ 'query' => [
+ 'Foo#0' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 0
+ ]
+ ],
+ 'query-continue-offset' => 0,
+ 'version' => 1,
+ 'meta' => [
+ 'type' => 'property',
+ 'limit' => 50,
+ 'count' => 1
+ ]
+ ];
+
+ $parameters = [
+ 'fullText' => true
+ ];
+
+ $expected = [
+ 'Foo#0' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 0,
+ 'fullText' => 'NS:FOO'
+ ]
+ ];
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getFullText' )
+ ->will( $this->returnValue( 'NS:FOO' ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $titleFactory->expects( $this->any() )
+ ->method( 'newFromID' )
+ ->with( $this->equalTo( 42 ) )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new ArticleAugmentor(
+ $titleFactory
+ );
+
+ $instance->augment( $res, $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function testAugmentOnFullURL() {
+
+ $res = [
+ 'query' => [
+ 'Foo#0' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 0
+ ]
+ ],
+ 'query-continue-offset' => 0,
+ 'version' => 1,
+ 'meta' => [
+ 'type' => 'property',
+ 'limit' => 50,
+ 'count' => 1
+ ]
+ ];
+
+ $parameters = [
+ 'fullURL' => true
+ ];
+
+ $expected = [
+ 'Foo#0' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 0,
+ 'fullURL' => 'http://example.org/FOO'
+ ]
+ ];
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getFullURL' )
+ ->will( $this->returnValue( 'http://example.org/FOO' ) );
+
+ $titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $titleFactory->expects( $this->any() )
+ ->method( 'newFromID' )
+ ->with( $this->equalTo( 42 ) )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new ArticleAugmentor(
+ $titleFactory
+ );
+
+ $instance->augment( $res, $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleLookupTest.php
new file mode 100644
index 00000000..244c919e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ArticleLookupTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\Api\Browse\ArticleLookup;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\ArticleLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $articleAugmentor = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\ArticleAugmentor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ArticleLookup::class,
+ new ArticleLookup( $connection, $articleAugmentor )
+ );
+ }
+
+ /**
+ * @dataProvider articleSearchProvider
+ */
+ public function testLookup( $search, $row, $condition, $expected ) {
+
+ $articleAugmentor = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\ArticleAugmentor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->with(
+ $this->anyThing(),
+ $this->anyThing(),
+ $this->stringContains( $condition ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new ArticleLookup(
+ $connection,
+ $articleAugmentor
+ );
+
+ $parameters = [
+ 'search' => $search
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function articleSearchProvider() {
+
+ $row = new \stdClass;
+ $row->page_title = 'Foo';
+ $row->page_id = 42;
+ $row->page_namespace = 0;
+
+ $provider[] = [
+ 'Foo',
+ $row,
+ 'page_title LIKE %Foo% ESCAPE ` OR page_title LIKE %Foo% ESCAPE ` OR page_title LIKE %FOO% ESCAPE ` OR page_title LIKE %foo% ESCAPE `',
+ [
+ 'Foo#0' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 0
+ ]
+ ]
+ ];
+
+ $row = new \stdClass;
+ $row->page_title = 'Foo';
+ $row->page_id = 42;
+ $row->page_namespace = 12;
+
+ $provider[] = [
+ 'Help:Fo o',
+ $row,
+ 'page_namespace=12 AND (page_title LIKE %Fo`_o% ESCAPE ` OR page_title LIKE %Fo`_o% ESCAPE ` OR page_title LIKE %FO`_O% ESCAPE ` OR page_title LIKE %fo`_o% ESCAPE `)',
+ [
+ 'Foo#12' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'ns' => 12
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/CachingLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/CachingLookupTest.php
new file mode 100644
index 00000000..b3d3d677
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/CachingLookupTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\Api\Browse\CachingLookup;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\CachingLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lookup = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\Lookup' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ CachingLookup::class,
+ new CachingLookup( $cache, $lookup )
+ );
+ }
+
+ public function testLookupWithoutCache() {
+
+ $cacheTTL = 42;
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'save' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( $cacheTTL ) );
+
+ $lookup = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\Lookup' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getVersion', 'lookup' ] )
+ ->getMockForAbstractClass();
+
+ $lookup->expects( $this->atLeastOnce() )
+ ->method( 'lookup' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CachingLookup(
+ $cache,
+ $lookup
+ );
+
+ $instance->setCacheTTL( $cacheTTL );
+
+ $parameters = [];
+
+ $instance->lookup( $parameters );
+ }
+
+ public function testLookupWithCache() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( [] ) );
+
+ $cache->expects( $this->never() )
+ ->method( 'save' );
+
+ $lookup = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\Lookup' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getVersion', 'lookup' ] )
+ ->getMockForAbstractClass();
+
+ $lookup->expects( $this->never() )
+ ->method( 'lookup' );
+
+ $instance = new CachingLookup(
+ $cache,
+ $lookup
+ );
+
+ $parameters = [];
+
+ $instance->lookup( $parameters );
+ }
+
+ public function testLookupWithCacheBeingDisabled() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->never() )
+ ->method( 'fetch' );
+
+ $cache->expects( $this->never() )
+ ->method( 'save' );
+
+ $lookup = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\Lookup' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getVersion', 'lookup' ] )
+ ->getMockForAbstractClass();
+
+ $lookup->expects( $this->atLeastOnce() )
+ ->method( 'lookup' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CachingLookup(
+ $cache,
+ $lookup
+ );
+
+ $instance->setCacheTTL( false );
+
+ $parameters = [];
+
+ $instance->lookup( $parameters );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListAugmentorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListAugmentorTest.php
new file mode 100644
index 00000000..b6d09716
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListAugmentorTest.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\Api\Browse\ListAugmentor;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\ListAugmentor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListAugmentorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ListAugmentor::class,
+ new ListAugmentor( $store )
+ );
+ }
+
+ public function testAugmentOnDescription() {
+
+ $res = [
+ 'query' => [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ],
+ 'query-continue-offset' => 0,
+ 'version' => 1,
+ 'meta' => [
+ 'type' => 'property',
+ 'limit' => 50,
+ 'count' => 1
+ ]
+ ];
+
+ $parameters = [
+ 'description' => true,
+ 'lang' => [ 'en', 'ja' ]
+ ];
+
+ $expected = [
+ 'Foo' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'description' => [
+ 'en' => '',
+ 'ja' => ''
+ ]
+ ]
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ListAugmentor(
+ $store
+ );
+
+ $instance->augment( $res, $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function testAugmentOnPrefLabel() {
+
+ $res = [
+ 'query' => [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ],
+ 'query-continue-offset' => 0,
+ 'version' => 1,
+ 'meta' => [
+ 'type' => 'property',
+ 'limit' => 50,
+ 'count' => 1
+ ]
+ ];
+
+ $parameters = [
+ 'prefLabel' => true,
+ 'lang' => [ 'en', 'ja' ]
+ ];
+
+ $expected = [
+ 'Foo' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'prefLabel' => [
+ 'en' => '',
+ 'ja' => ''
+ ]
+ ]
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ListAugmentor(
+ $store
+ );
+
+ $instance->augment( $res, $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function testAugmentOnUsageCount() {
+
+ $res = [
+ 'query' => [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ],
+ 'query-continue-offset' => 0,
+ 'version' => 1,
+ 'meta' => [
+ 'type' => 'property',
+ 'limit' => 50,
+ 'count' => 1
+ ]
+ ];
+
+ $parameters = [
+ 'usageCount' => true
+ ];
+
+ $expected = [
+ 'Foo' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'usageCount' => 1111
+ ]
+ ];
+
+ $row = new \stdClass;
+ $row->usage_count = 1111;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $row ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new ListAugmentor(
+ $store
+ );
+
+ $instance->augment( $res, $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListLookupTest.php
new file mode 100644
index 00000000..1b9432a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/ListLookupTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\MediaWiki\Api\Browse\ListLookup;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\ListLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listAugmentor = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\ListAugmentor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ListLookup::class,
+ new ListLookup( $store, $listAugmentor )
+ );
+ }
+
+ /**
+ * @dataProvider namespaceProvider
+ */
+ public function testLookup( $ns, $title, $expected ) {
+
+ $row = new \stdClass;
+ $row->smw_title = $title;
+ $row->smw_id = 42;
+
+ $listAugmentor = $this->getMockBuilder( '\SMW\MediaWiki\Api\Browse\ListAugmentor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLOptions' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new ListLookup(
+ $store,
+ $listAugmentor
+ );
+
+ $parameters = [
+ 'ns' => $ns,
+ 'search' => 'Foo',
+ 'sort' => true
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function namespaceProvider() {
+
+ $provider[] = [
+ SMW_NS_PROPERTY,
+ 'Foo',
+ [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ]
+ ];
+
+ $provider[] = [
+ NS_CATEGORY,
+ 'Foo',
+ [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ]
+ ];
+
+ $provider[] = [
+ SMW_NS_CONCEPT,
+ 'Foo',
+ [
+ 'Foo' => [
+ 'id' => 42,
+ 'label' => 'Foo',
+ 'key' => 'Foo'
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PSubjectLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PSubjectLookupTest.php
new file mode 100644
index 00000000..6d4ad0d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PSubjectLookupTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Api\Browse\PSubjectLookup;
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\PSubjectLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PSubjectLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PSubjectLookup::class,
+ new PSubjectLookup( $this->store )
+ );
+ }
+
+ /**
+ * @dataProvider lookupProvider
+ */
+ public function testLookup( $subject, $parameters, $expected ) {
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [ $subject ] ) );
+
+ $instance = new PSubjectLookup(
+ $this->store
+ );
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ $expected
+ );
+ }
+
+ public function lookupProvider() {
+
+ yield [
+ new DIWikiPage( 'Foo bar', NS_MAIN ),
+ [
+ 'search' => 'Foo',
+ 'property' => 'Bar'
+ ],
+ [
+ 'Foo bar'
+ ]
+ ];
+
+ yield [
+ new DIWikiPage( 'Foo bar', NS_HELP ),
+ [
+ 'search' => 'Foo',
+ 'property' => 'Bar',
+ 'title-prefix' => false
+ ],
+ [
+ 'Foo bar'
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PValueLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PValueLookupTest.php
new file mode 100644
index 00000000..c071c467
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/PValueLookupTest.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Api\Browse\PValueLookup;
+use SMW\MediaWiki\Connection\Query;
+use SMW\Services\ServicesContainer;
+use FakeResultWrapper;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\PValueLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PValueLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'service' )
+ ->with( $this->equalTo( 'ProximityPropertyValueLookup' ) )
+ ->will( $this->returnValue( new \SMW\SQLStore\Lookup\ProximityPropertyValueLookup( $this->store ) ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PValueLookup::class,
+ new PValueLookup( $this->store )
+ );
+ }
+
+ public function testLookup_wpg_property() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Test';
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = new Query( $connection );
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( new FakeResultWrapper( [ $row ] ) ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'isFixedPropertyTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PValueLookup(
+ $this->store
+ );
+
+ $parameters = [
+ 'search' => 'Foo',
+ 'property' => 'Bar'
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ [
+ 'Test'
+ ]
+ );
+
+ $this->assertContains(
+ '[{"OR":"smw_sortkey LIKE %Foo%"},{"OR":"smw_sortkey LIKE %Foo%"},{"OR":"smw_sortkey LIKE %FOO%"}]',
+ $query->__toString()
+ );
+ }
+
+ public function testLookup_wpg_propertyChain() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Test';
+ $row->smw_id = 42;
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( new FakeResultWrapper( [ $row ] ) ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'isFixedPropertyTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->with( $this->equalTo( new DIProperty( 'Foobar' ) ) )
+ ->will( $this->returnValue( 42 ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PValueLookup(
+ $this->store
+ );
+
+ $parameters = [
+ 'search' => 'Foo',
+ 'property' => 'Bar.Foobar'
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ [
+ 'Test'
+ ]
+ );
+ }
+
+ public function testLookup_txt_property() {
+
+ $row = new \stdClass;
+ $row->o_hash = 'Test';
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = new Query( $connection );
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( new FakeResultWrapper( [ $row ] ) ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'isFixedPropertyTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->any() )
+ ->method( 'getLabelField' )
+ ->will( $this->returnValue( 'o_hash' ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PValueLookup(
+ $this->store
+ );
+
+ $parameters = [
+ 'search' => 'Foo',
+ 'property' => 'Text'
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertEquals(
+ $res['query'],
+ [
+ 'Test'
+ ]
+ );
+
+ $this->assertContains(
+ '[{"OR":"o_hash LIKE %Foo%"},{"OR":"o_hash LIKE %Foo%"},{"OR":"o_hash LIKE %FOO%"},{"AND":"p_id=42"}]',
+ $query->__toString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/SubjectLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/SubjectLookupTest.php
new file mode 100644
index 00000000..85082f33
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/Browse/SubjectLookupTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api\Browse;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Api\Browse\SubjectLookup;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\SubjectLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SubjectLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SubjectLookup::class,
+ new SubjectLookup( $this->store )
+ );
+ }
+
+ public function testLookup_HTML() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $instance = new SubjectLookup(
+ $this->store
+ );
+
+ $parameters = [
+ 'subject' => 'Foo',
+ 'ns' => NS_MAIN,
+ 'options' => [],
+ 'type' => 'html'
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertArrayHasKey(
+ 'query',
+ $res
+ );
+
+ $this->assertArrayHasKey(
+ 'meta',
+ $res
+ );
+ }
+
+ public function testLookup_JSON() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $instance = new SubjectLookup(
+ $this->store
+ );
+
+ $parameters = [
+ 'subject' => 'Foo',
+ 'ns' => NS_MAIN
+ ];
+
+ $res = $instance->lookup( $parameters );
+
+ $this->assertArrayHasKey(
+ 'query',
+ $res
+ );
+
+ $this->assertArrayHasKey(
+ 'meta',
+ $res
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseByPropertyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseByPropertyTest.php
new file mode 100644
index 00000000..12623cec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseByPropertyTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\MediaWiki\Api\BrowseByProperty;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Api\BrowseByProperty
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class BrowseByPropertyTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $apiFactory;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->applicationFactory->registerObject( 'Store', $this->store );
+
+ $this->apiFactory = UtilityFactory::getInstance()->newMwApiFactory();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new BrowseByProperty(
+ $this->apiFactory->newApiMain( [] ),
+ 'browsebyproperty'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Api\BrowseByProperty',
+ $instance
+ );
+ }
+
+ public function testExecute() {
+
+ $list[] = [
+ new DIProperty( 'Foo' ),
+ 42
+ ];
+
+ $list[] = [
+ new DIProperty( 'Foaf:Foo' ),
+ 1001
+ ];
+
+ $list[] = [
+ new DIProperty( 'Unknown:Foo' ),
+ 1001
+ ];
+
+ $cachedListLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\CachedListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cachedListLookup->expects( $this->once() )
+ ->method( 'fetchList' )
+ ->will( $this->returnValue( $list ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'getPropertiesSpecial' )
+ ->will( $this->returnValue( $cachedListLookup ) );
+
+ $this->applicationFactory->registerObject( 'Store', $this->store );
+
+ $result = $this->apiFactory->doApiRequest( [
+ 'action' => 'browsebyproperty',
+ 'property' => 'Foo'
+ ] );
+
+ $this->assertArrayHasKey(
+ 'query',
+ $result
+ );
+
+ $this->assertArrayHasKey(
+ 'version',
+ $result
+ );
+
+ $this->assertArrayHasKey(
+ 'query-continue-offset',
+ $result
+ );
+
+ $this->assertArrayHasKey(
+ 'meta',
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseBySubjectTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseBySubjectTest.php
new file mode 100644
index 00000000..01c0da31
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseBySubjectTest.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Api\BrowseBySubject;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Api\BrowseBySubject
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BrowseBySubjectTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $apiFactory;
+ private $semanticDataFactory;
+
+ private $applicationFactory;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $utilityFactory = $this->testEnvironment->getUtilityFactory();
+
+ $this->apiFactory = $utilityFactory->newMwApiFactory();
+ $this->semanticDataFactory = $utilityFactory->newSemanticDataFactory();
+ $this->stringValidator = $utilityFactory->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new BrowseBySubject(
+ $this->apiFactory->newApiMain( ['subject' => 'Foo' ] ),
+ 'browsebysubject'
+ );
+
+ $this->assertInstanceOf(
+ BrowseBySubject::class,
+ $instance
+ );
+ }
+
+ public function testExecuteForValidSubject() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $expectedResultToContainArrayKeys = [
+ 'error' => false,
+ 'result' => true
+ ];
+
+ $result = $this->apiFactory->doApiRequest( [
+ 'action' => 'browsebysubject',
+ 'subject' => 'Foo'
+ ] );
+
+ $this->assertToContainArrayKeys(
+ $expectedResultToContainArrayKeys,
+ $result
+ );
+ }
+
+ public function testExecuteForInvalidSubjectThrowsException() {
+ $this->setExpectedException( interface_exists( 'Throwable' ) ? 'Throwable' : 'Exception' );
+
+ $result = $this->apiFactory->doApiRequest( [
+ 'action' => 'browsebysubject',
+ 'subject' => '{}'
+ ] );
+ }
+
+ public function testRawJsonPrintOutput() {
+
+ $parameters = [ 'subject' => 'Foo', 'subobject' => 'Bar' ];
+
+ $dataItem = new DIWikiPage(
+ 'Foo',
+ NS_MAIN,
+ '',
+ 'Bar'
+ );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( $dataItem );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->with( $this->equalTo( $dataItem ) )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new BrowseBySubject(
+ $this->apiFactory->newApiMain( $parameters ),
+ 'browsebysubject'
+ );
+
+ // Went away with 1.26/1.27
+ if ( function_exists( 'setRawMode' ) ) {
+ $instance->getMain()->getResult()->setRawMode();
+ }
+
+ $instance->execute();
+
+ $printer = $instance->getMain()->createPrinterByName( 'json' );
+
+ ob_start();
+ $printer->initPrinter( false );
+ $printer->execute();
+ $printer->closePrinter();
+ $out = ob_get_clean();
+
+ $this->stringValidator->assertThatStringContains(
+ '"subject":"Foo#0##Bar"',
+ $out
+ );
+ }
+
+
+ public function testHtmlJsonPrintOutput() {
+
+ $parameters = [
+ 'subject' => 'Foo',
+ 'subobject' => 'Bar',
+ 'type' => 'html'
+ ];
+
+ $dataItem = new DIWikiPage(
+ 'Foo',
+ NS_MAIN,
+ '',
+ 'Bar'
+ );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( $dataItem );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->with( $this->equalTo( $dataItem ) )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new BrowseBySubject(
+ $this->apiFactory->newApiMain( $parameters ),
+ 'browsebysubject'
+ );
+
+ // Went away with 1.26/1.27
+ if ( function_exists( 'setRawMode' ) ) {
+ $instance->getMain()->getResult()->setRawMode();
+ }
+
+ $instance->execute();
+
+ $printer = $instance->getMain()->createPrinterByName( 'json' );
+
+ ob_start();
+ $printer->initPrinter( false );
+ $printer->execute();
+ $printer->closePrinter();
+ $out = ob_get_clean();
+
+ $this->stringValidator->assertThatStringContains(
+ '"query":"<div class=\"smwb-datasheet.*\"><div class=\"smw-table smwb-factbox\">',
+ $out
+ );
+ }
+
+ public function assertToContainArrayKeys( $setup, $result ) {
+ $this->assertInternalArrayStructure(
+ $setup, $result, 'error', 'array', function( $r ) { return $r['error'];
+ } );
+
+ $this->assertInternalArrayStructure(
+ $setup, $result, 'result', 'array', function( $r ) { return $r['query'];
+ } );
+
+ $this->assertInternalArrayStructure(
+ $setup, $result, 'subject', 'string', function( $r ) { return $r['query']['subject'];
+ } );
+
+ $this->assertInternalArrayStructure(
+ $setup, $result, 'data', 'array', function( $r ) { return $r['query']['data'];
+ } );
+
+ $this->assertInternalArrayStructure(
+ $setup, $result, 'sobj', 'array', function( $r ) { return $r['query']['sobj'];
+ } );
+ }
+
+ protected function assertInternalArrayStructure( $setup, $result, $field, $internalType, $definition ) {
+
+ if ( isset( $setup[$field] ) && $setup[$field] ) {
+
+ $this->assertInternalType(
+ $internalType,
+ is_callable( $definition ) ? $definition( $result ) : $definition
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseTest.php
new file mode 100644
index 00000000..2ce2ba21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/BrowseTest.php
@@ -0,0 +1,230 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\Browse;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class BrowseTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $apiFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'smwgCacheUsage' => [ 'api.browse' => true ]
+ ]
+ );
+
+ $this->apiFactory = $this->testEnvironment->getUtilityFactory()->newMwApiFactory();
+
+ $proximityPropertyValueLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ProximityPropertyValueLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'service' )
+ ->with( $this->equalTo( 'ProximityPropertyValueLookup' ) )
+ ->will( $this->returnValue( $proximityPropertyValueLookup ) );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new Browse(
+ $this->apiFactory->newApiMain( [] ),
+ 'smwbrowse'
+ );
+
+ $this->assertInstanceOf(
+ Browse::class,
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider browseIdProvider
+ */
+ public function testExecute( $id, $parameters = [] ) {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( false ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $resultWrapper = $this->getMockBuilder( '\FakeResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'query' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSQLOptions' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+
+ $instance = new Browse(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwbrowse',
+ 'browse' => $id,
+ 'params' => json_encode( [ 'search' => 'Foo' ] + $parameters )
+ ]
+ ),
+ 'smwbrowse'
+ );
+
+ $instance->execute();
+ }
+
+ public function testExecute_Subject() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubSemanticData' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+
+ $instance = new Browse(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwbrowse',
+ 'browse' => 'subject',
+ 'params' => json_encode( [ 'subject' => 'Bar', 'ns' => 0 ] )
+ ]
+ ),
+ 'smwbrowse'
+ );
+
+ $instance->execute();
+ }
+
+ public function browseIdProvider() {
+
+ $provider[] = [
+ 'property'
+ ];
+
+ $provider[] = [
+ 'category'
+ ];
+
+ $provider[] = [
+ 'concept'
+ ];
+
+ $provider[] = [
+ 'page'
+ ];
+
+ $provider[] = [
+ 'pvalue',
+ [ 'property' => 'Bar' ]
+ ];
+
+ $provider[] = [
+ 'psubject',
+ [ 'property' => 'Bar' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/InfoTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/InfoTest.php
new file mode 100644
index 00000000..22105713
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/InfoTest.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\Info;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Info
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InfoTest extends \PHPUnit_Framework_TestCase {
+
+ private $apiFactory;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->apiFactory = $this->testEnvironment->getUtilityFactory()->newMwApiFactory();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new Info(
+ $this->apiFactory->newApiMain( [] ),
+ 'smwinfo'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Api\Info',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider typeDataProvider
+ */
+ public function testExecuteOnStore( $queryParameters, $expectedType ) {
+
+ $result = $this->apiFactory->doApiRequest( [
+ 'action' => 'smwinfo',
+ 'info' => $queryParameters
+ ] );
+
+ if ( $expectedType === 'integer' ) {
+ return $this->assertGreaterThanOrEqual( 0, $result['info'][$queryParameters] );
+ }
+
+ $this->assertInternalType(
+ 'array',
+ $result['info'][$queryParameters]
+ );
+ }
+
+ /**
+ * @dataProvider countDataProvider
+ */
+ public function testExecuteOnMockStore( $statistics, $type, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getStatistics' )
+ ->will( $this->returnValue( $statistics ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new Info(
+ $this->apiFactory->newApiMain( [ 'info' => $type ] ),
+ 'smwinfo'
+ );
+
+ $instance->execute();
+
+ // MW 1.25
+ $result = method_exists( $instance->getResult(), 'getResultData' ) ? $instance->getResult()->getResultData() : $instance->getResultData();
+
+ // This came with 1.25, no idea what this suppose to be
+ unset( $result['_type'] );
+
+ $this->assertEquals(
+ $expected,
+ $result['info'][$type]
+ );
+ }
+
+ /**
+ * Test unknown query parameter
+ *
+ * Only valid parameters will yield an info array while an unknown parameter
+ * will produce a "warnings" array.
+ *
+ * @since 1.9
+ */
+ public function testUnknownQueryParameter() {
+
+ $data = $this->apiFactory->doApiRequest( [
+ 'action' => 'smwinfo',
+ 'info' => 'Foo'
+ ] );
+
+ $this->assertInternalType(
+ 'array',
+ $data['warnings']
+ );
+ }
+
+ public function testJobCount() {
+
+ $this->jobQueue->expects( $this->any() )
+ ->method( 'getQueueSize' )
+ ->will( $this->returnValue( 1 ) );
+
+ $result = $this->apiFactory->doApiRequest(
+ [
+ 'action' => 'smwinfo',
+ 'info' => 'jobcount'
+ ]
+ );
+
+ $this->assertArrayHasKey(
+ 'smw.update',
+ $result['info']['jobcount']
+ );
+ }
+
+ public function countDataProvider() {
+ return [
+ [ [ 'QUERYFORMATS' => [ 'table' => 3 ] ], 'formatcount', [ 'table' => 3 ] ],
+ [ [ 'PROPUSES' => 34 ], 'propcount', 34 ],
+ [ [ 'ERRORUSES' => 42 ], 'errorcount', 42 ],
+ [ [ 'USEDPROPS' => 51 ], 'usedpropcount', 51 ],
+ [ [ 'TOTALPROPS' => 52 ], 'totalpropcount', 52 ],
+ [ [ 'DECLPROPS' => 67 ], 'declaredpropcount', 67 ],
+ [ [ 'OWNPAGE' => 99 ], 'proppagecount', 99 ],
+ [ [ 'QUERY' => 11 ], 'querycount', 11 ],
+ [ [ 'QUERYSIZE' => 24 ], 'querysize', 24 ],
+ [ [ 'CONCEPTS' => 17 ], 'conceptcount', 17 ],
+ [ [ 'SUBOBJECTS' => 88 ], 'subobjectcount', 88 ],
+ ];
+ }
+
+ public function typeDataProvider() {
+ return [
+ [ 'proppagecount', 'integer' ],
+ [ 'propcount', 'integer' ],
+ [ 'errorcount', 'integer' ],
+ [ 'querycount', 'integer' ],
+ [ 'usedpropcount', 'integer' ],
+ [ 'totalpropcount', 'integer' ],
+ [ 'declaredpropcount', 'integer' ],
+ [ 'conceptcount', 'integer' ],
+ [ 'querysize', 'integer' ],
+ [ 'subobjectcount', 'integer' ],
+ [ 'formatcount', 'array' ],
+ [ 'jobcount', 'array' ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/PropertyListByApiRequestTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/PropertyListByApiRequestTest.php
new file mode 100644
index 00000000..86126390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/PropertyListByApiRequestTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\DIProperty;
+use SMW\MediaWiki\Api\PropertyListByApiRequest;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Api\PropertyListByApiRequest
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyListByApiRequestTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Api\PropertyListByApiRequest',
+ new PropertyListByApiRequest( $this->store, $propertySpecificationLookup )
+ );
+ }
+
+ public function testGetSerializedListForProperty() {
+
+ $list[] = [
+ new DIProperty( 'Foo' ),
+ 42
+ ];
+
+ $list[] = [
+ new DIProperty( 'Foaf:Foo' ),
+ 1001
+ ];
+
+ $list[] = [
+ new \SMWDIError( 'error' ),
+ -1
+ ];
+
+ $list[] = [];
+
+ $isCached = true;
+
+ $expectedSerializedPropertyList = [
+ 'Foo' => [
+ 'label' => 'Foo',
+ 'key' => 'Foo',
+ 'isUserDefined' => true,
+ 'usageCount' => 42,
+ 'description' => ''
+ ],
+ 'Foaf:Foo' => [
+ 'label' => 'Foaf:Foo',
+ 'key' => 'Foaf:Foo',
+ 'isUserDefined' => true,
+ 'usageCount' => 1001,
+ 'description' => ''
+ ]
+ ];
+
+ $expectedNamespaces = [
+ 'Foaf'
+ ];
+
+ $expectedMeta = [
+ 'limit' => 3,
+ 'count' => 2,
+ 'isCached' => $isCached
+ ];
+
+ $cachedListLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\CachedListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cachedListLookup->expects( $this->once() )
+ ->method( 'fetchList' )
+ ->will( $this->returnValue( $list ) );
+
+ $cachedListLookup->expects( $this->once() )
+ ->method( 'isFromCache' )
+ ->will( $this->returnValue( $isCached ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'getPropertiesSpecial' )
+ ->will( $this->returnValue( $cachedListLookup ) );
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyListByApiRequest( $this->store, $propertySpecificationLookup );
+ $instance->setLimit( 3 );
+
+ $this->assertTrue(
+ $instance->findPropertyListBy( 'Foo' )
+ );
+
+ $this->assertEquals(
+ $expectedSerializedPropertyList,
+ $instance->getPropertyList()
+ );
+
+ $this->assertEquals(
+ $expectedNamespaces,
+ $instance->getNamespaces()
+ );
+
+ $this->assertEquals(
+ $expectedMeta,
+ $instance->getMeta()
+ );
+
+ $this->assertEquals(
+ 3,
+ $instance->getContinueOffset()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/QueryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/QueryTest.php
new file mode 100644
index 00000000..bdbdb17c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/QueryTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use ReflectionClass;
+use SMW\ApplicationFactory;
+use SMW\Tests\Utils\MwApiFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Query
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class QueryTest extends \PHPUnit_Framework_TestCase {
+
+ private $apiFactory;
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->apiFactory = new MwApiFactory();
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ ApplicationFactory::clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\Api\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Api\Query',
+ $instance
+ );
+ }
+
+ public function testQueryAndQueryResult() {
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\Api\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reflector = new ReflectionClass( '\SMW\MediaWiki\Api\Query' );
+ $getQuery = $reflector->getMethod( 'getQuery' );
+ $getQuery->setAccessible( true );
+ $query = $getQuery->invoke( $instance, '[[Modification date::+]]', [], [] );
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ $query
+ );
+
+ $getQueryResult = $reflector->getMethod( 'getQueryResult' );
+ $getQueryResult->setAccessible( true );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $getQueryResult->invoke( $instance, $query )
+ );
+ }
+
+ public function testAddQueryResultOnMockStore() {
+
+ // Minimalistic test case to verify executability
+ // For a full coverage, use Api\QueryResultFormatterTest
+ $test = [
+ 'results' => [
+ 'Foo' => [
+ 'printouts' => [ 'lula' => [ 'lila' ] ]
+ ]
+ ],
+ 'printrequests' => [ 'Bar' ],
+ 'meta' => [ 'count' => 5, 'offset' => 5 ]
+ ];
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( $test ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'hasFurtherResults' )
+ ->will( $this->returnValue( true ) );
+
+ $apiResult = $this->apiFactory->newApiResult( [] );
+
+ $reflector = new ReflectionClass( '\SMW\MediaWiki\Api\Query' );
+ $method = $reflector->getMethod( 'addQueryResult' );
+ $method->setAccessible( true );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\Api\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->expects( $this->atLeastOnce() )
+ ->method( 'getResult' )
+ ->will( $this->returnValue( $apiResult ) );
+
+ $method->invoke( $instance, $queryResult );
+
+ // MW 1.25
+ $result = method_exists( $apiResult, 'getResultData' ) ? $apiResult->getResultData() : $instance->getData();
+
+ // This came with 1.25, no idea what this suppose to be
+ unset( $result['warnings'] );
+ unset( $result['_type'] );
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ //$this->assertEquals(
+ // array( 'query' => $test, 'query-continue-offset' => 10 ),
+ // $result
+ //);
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/TaskTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/TaskTest.php
new file mode 100644
index 00000000..c99aaf19
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Api/TaskTest.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Api;
+
+use SMW\MediaWiki\Api\Task;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Task
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TaskTest extends \PHPUnit_Framework_TestCase {
+
+ private $apiFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->apiFactory = $this->testEnvironment->getUtilityFactory()->newMwApiFactory();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain( [] ),
+ 'smwtask'
+ );
+
+ $this->assertInstanceOf(
+ Task::class,
+ $instance
+ );
+ }
+
+ public function testUpdateTask() {
+
+ $updateJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\UpdateJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $updateJob->expects( $this->atLeastOnce() )
+ ->method( 'run' );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $updateJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain( [
+ 'action' => 'smwtask',
+ 'task' => 'update',
+ 'params' => json_encode( [ 'subject' => 'Foo#0##', 'ref' => [ 'Bar' ] ] ),
+ 'token' => 'foo'
+ ]
+ ),
+ 'smwtask'
+ );
+
+ $instance->execute();
+ }
+
+ public function testDupLookupTask() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $cache->expects( $this->once() )
+ ->method( 'save' );
+
+ $entityTable = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityTable->expects( $this->atLeastOnce() )
+ ->method( 'findDuplicates' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityTable ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwtask',
+ 'task' => 'duplookup',
+ 'params' => [],
+ 'token' => 'foo'
+ ]
+ ),
+ 'smwtask'
+ );
+
+ $instance->execute();
+ }
+
+ public function testGenericJobTask() {
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $nullJob->expects( $this->atLeastOnce() )
+ ->method( 'insert' );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newByType' )
+ ->with(
+ $this->equalTo( 'Foobar' ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwtask',
+ 'task' => 'job',
+ 'params' => json_encode(
+ [
+ 'subject' => 'Foo#0##',
+ 'job' => 'Foobar'
+ ]
+ ),
+ 'token' => 'foo'
+ ]
+ ),
+ 'smwtask'
+ );
+
+ $instance->execute();
+ }
+
+ public function testRunJobListTask() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue->expects( $this->atLeastOnce() )
+ ->method( 'runFromQueue' )
+ ->with( $this->equalTo( [ 'FooJob' => 1 ] ) )
+ ->will( $this->returnValue( '--job-done' ) );
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwtask',
+ 'task' => 'run-joblist',
+ 'params' => json_encode(
+ [
+ 'subject' => 'Foo#0##',
+ 'jobs' => [ 'FooJob' => 1 ]
+ ]
+ ),
+ 'token' => 'foo'
+ ]
+ ),
+ 'smwtask'
+ );
+
+ $instance->execute();
+ }
+
+ public function testCheckQueryTask() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new Task(
+ $this->apiFactory->newApiMain(
+ [
+ 'action' => 'smwtask',
+ 'task' => 'check-query',
+ 'params' => json_encode(
+ [
+ 'subject' => 'Foo#0##',
+ 'query' => [
+ 'query_hash_1#result_hash_2' => [
+ 'parameters' => [
+ 'limit' => 5,
+ 'offset' => 0,
+ 'querymode' => 1
+ ],
+ 'conditions' => ''
+ ]
+ ]
+ ]
+ ),
+ 'token' => 'foo'
+ ]
+ ),
+ 'smwtask'
+ );
+
+ $instance->execute();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/CollatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/CollatorTest.php
new file mode 100644
index 00000000..ae26e4c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/CollatorTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\Collator;
+
+/**
+ * @covers \SMW\MediaWiki\Collator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CollatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Collator::class,
+ Collator::singleton()
+ );
+ }
+
+ public function testIsIdentical() {
+
+ $collation = $this->getMockBuilder( '\Collation' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $collation->expects( $this->exactly( 2 ) )
+ ->method( 'getSortKey' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new Collator(
+ $collation
+ );
+
+ $this->assertTrue(
+ $instance->isIdentical( 'Foo', 'Foo' )
+ );
+ }
+
+ /**
+ * @dataProvider uppercaseProvider
+ */
+ public function testGetFirstLetterOnUppercaseCollation( $text, $firstLetter, $sortKey ) {
+
+ $instance = Collator::singleton( 'uppercase' );
+
+ $this->assertSame(
+ $firstLetter,
+ $instance->getFirstLetter( $text )
+ );
+
+ $this->assertSame(
+ $sortKey,
+ $instance->getSortKey( $text )
+ );
+ }
+
+ /**
+ * @dataProvider identityProvider
+ */
+ public function testGetFirstLetterOnIdentityCollation( $text, $firstLetter, $sortKey ) {
+
+ $instance = Collator::singleton( 'identity' );
+
+ $this->assertSame(
+ $firstLetter,
+ $instance->getFirstLetter( $text )
+ );
+
+ $this->assertSame(
+ $sortKey,
+ $instance->getSortKey( $text )
+ );
+ }
+
+ /**
+ * @dataProvider armorProvider
+ */
+ public function testArmor( $collation, $text, $expected ) {
+
+ $instance = Collator::singleton( $collation );
+
+ $this->assertSame(
+ $expected,
+ $instance->armor( $instance->getSortKey( $text ) )
+ );
+ }
+
+ public function testArmorOnUCA() {
+
+ if ( !extension_loaded( 'intl' ) ) {
+ $this->markTestSkipped( 'Skipping because intl (ICU) is not availabe.' );
+ }
+
+ $instance = Collator::singleton( 'uca-default' );
+ $text = 'XmlTest';
+
+ $this->assertNotSame(
+ $text,
+ $instance->armor( $instance->getSortKey( $text ) )
+ );
+ }
+
+ public function uppercaseProvider() {
+
+ $provider[] = [
+ '',
+ '',
+ ''
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'F',
+ 'FOO'
+ ];
+
+ $provider[] = [
+ 'foo',
+ 'F',
+ 'FOO'
+ ];
+
+ $provider[] = [
+ 'テスト',
+ 'テ',
+ 'テスト'
+ ];
+
+ $provider[] = [
+ '\0テスト',
+ '\\',
+ '\0テスト'
+ ];
+
+ return $provider;
+ }
+
+ public function identityProvider() {
+
+ $provider[] = [
+ '',
+ '',
+ ''
+ ];
+
+ $provider[] = [
+ 'Foo',
+ 'F',
+ 'Foo'
+ ];
+
+ $provider[] = [
+ 'foo',
+ 'f',
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'テスト',
+ 'テ',
+ 'テスト'
+ ];
+
+ $provider[] = [
+ '\0テスト',
+ '\\',
+ '\0テスト'
+ ];
+
+ return $provider;
+ }
+
+ public function armorProvider() {
+
+ $provider[] = [
+ 'uppercase',
+ 'XmlTest',
+ 'XMLTEST'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/ConnectionProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/ConnectionProviderTest.php
new file mode 100644
index 00000000..6f1fa938
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/ConnectionProviderTest.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\MediaWiki\Connection\ConnectionProvider;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\ConnectionProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConnectionProviderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf(
+ ConnectionProvider::class,
+ new ConnectionProvider()
+ );
+ }
+
+ public function testGetConnection() {
+
+ $instance = new ConnectionProvider();
+ $instance->setLogger(
+ TestEnvironment::newSpyLogger()
+ );
+
+ $connection = $instance->getConnection();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Database',
+ $connection
+ );
+
+ $this->assertSame(
+ $connection,
+ $instance->getConnection()
+ );
+
+ $instance->releaseConnection();
+
+ $this->assertNotSame(
+ $connection,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetConnectionOnFixedConfWithSameIndex() {
+
+ $instance = new ConnectionProvider(
+ 'foo'
+ );
+
+ $instance->setLogger(
+ TestEnvironment::newSpyLogger()
+ );
+
+ $conf = [
+ 'foo' => [
+ 'read' => 'Bar',
+ 'write' => 'Bar'
+ ]
+ ];
+
+ $instance->setLocalConnectionConf( $conf );
+
+ $connection = $instance->getConnection();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Database',
+ $connection
+ );
+
+ $this->assertSame(
+ $connection,
+ $instance->getConnection()
+ );
+
+ $instance->releaseConnection();
+
+ $this->assertNotSame(
+ $connection,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetConnectionOnCallback() {
+
+ $db = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConnectionProvider(
+ 'foo'
+ );
+
+ $conf = [
+ 'foo' => [
+ 'callback' => function() use( $db ) {
+ return $db;
+ }
+ ]
+ ];
+
+ $instance->setLocalConnectionConf( $conf );
+
+ $connection = $instance->getConnection();
+
+ $this->assertSame(
+ $db,
+ $instance->getConnection()
+ );
+
+ $instance->releaseConnection();
+ }
+
+ public function testGetConnectionOnIncompleteConfThrowsException() {
+
+ $instance = new ConnectionProvider(
+ 'foo'
+ );
+
+ $conf = [
+ 'foo' => [
+ 'read' => 'Foo'
+ ]
+ ];
+
+ $instance->setLocalConnectionConf( $conf );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/DatabaseTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/DatabaseTest.php
new file mode 100644
index 00000000..58afbadb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/DatabaseTest.php
@@ -0,0 +1,609 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\Connection\ConnectionProviderRef;
+use SMW\Tests\PHPUnitCompat;
+use SMW\MediaWiki\Connection\Database;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\Database
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DatabaseTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connectionProviderRef;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->connectionProviderRef = $this->getMockBuilder( '\SMW\Connection\ConnectionProviderRef' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Database::class,
+ new Database( $this->connectionProviderRef )
+ );
+ }
+
+ public function testNewQuery() {
+
+ $instance = new Database(
+ $this->connectionProviderRef
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Connection\Query',
+ $instance->newQuery()
+ );
+ }
+
+ public function testNumRowsMethod() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'numRows' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->once() )
+ ->method( 'numRows' )
+ ->with( $this->equalTo( 'Fuyu' ) )
+ ->will( $this->returnValue( 1 ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->assertEquals(
+ 1,
+ $instance->numRows( 'Fuyu' )
+ );
+ }
+
+ public function testAddQuotesMethod() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'addQuotes' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->once() )
+ ->method( 'addQuotes' )
+ ->with( $this->equalTo( 'Fan' ) )
+ ->will( $this->returnValue( 'Fan' ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->assertEquals(
+ 'Fan',
+ $instance->addQuotes( 'Fan' )
+ );
+ }
+
+ /**
+ * @dataProvider dbTypeProvider
+ */
+ public function testTableNameMethod( $type ) {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableName', 'getType' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->any() )
+ ->method( 'tableName' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $database->expects( $this->once() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( $type ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $instance->setDBPrefix( 'bar_' );
+
+ $expected = $type === 'sqlite' ? 'bar_Foo' : 'Foo';
+
+ $this->assertEquals(
+ $expected,
+ $instance->tableName( 'Foo' )
+ );
+ }
+
+ public function testSelectMethod() {
+
+ $resultWrapper = $this->getMockBuilder( 'ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'select' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->assertInstanceOf(
+ 'ResultWrapper',
+ $instance->select( 'Foo', 'Bar', '', __METHOD__ )
+ );
+ }
+
+ public function testSelectFieldMethod() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'selectField' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->once() )
+ ->method( 'selectField' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->selectField( 'Foo', 'Bar', '', __METHOD__, [] )
+ );
+ }
+
+ /**
+ * @dataProvider querySqliteProvider
+ */
+ public function testQueryOnSQLite( $query, $expected ) {
+
+ $resultWrapper = $this->getMockBuilder( 'ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $read = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getType' ] )
+ ->getMockForAbstractClass();
+
+ $read->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'sqlite' ) );
+
+ $readConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $readConnectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $read ) );
+
+ $write = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'query' ] )
+ ->getMockForAbstractClass();
+
+ $write->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->equalTo( $expected ) )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $writeConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $writeConnectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $write ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $readConnectionProvider,
+ 'write' => $writeConnectionProvider
+ ]
+ )
+ );
+
+ $this->assertInstanceOf(
+ 'ResultWrapper',
+ $instance->query( $query )
+ );
+ }
+
+ public function querySqliteProvider() {
+
+ $provider = [
+ [ 'TEMPORARY', 'TEMP' ],
+ [ 'RAND', 'RANDOM' ],
+ [ 'ENGINE=MEMORY', '' ],
+ [ 'DROP TEMP', 'DROP' ]
+ ];
+
+ return $provider;
+ }
+
+ public function testSelectThrowsException() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'select' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->once() )
+ ->method( 'select' );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ $this->assertInstanceOf(
+ 'ResultWrapper',
+ $instance->select( 'Foo', 'Bar', '', __METHOD__ )
+ );
+ }
+
+ public function testQueryThrowsException() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'query' ] )
+ ->getMockForAbstractClass();
+
+ $databaseException = new \DBError( $database, 'foo' );
+
+ $database->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->throwException( $databaseException ) );
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionProvider->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $connectionProvider,
+ 'write' => $connectionProvider
+ ]
+ )
+ );
+
+ $this->setExpectedException( 'Exception' );
+ $instance->query( 'Foo', __METHOD__ );
+ }
+
+ public function testGetEmptyTransactionTicket() {
+
+ $readConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $writeConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $loadBalancerFactory = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getEmptyTransactionTicket', 'hasMasterChanges' ] )
+ ->getMock();
+
+ $loadBalancerFactory->expects( $this->once() )
+ ->method( 'hasMasterChanges' )
+ ->will( $this->returnValue( false ) );
+
+ $loadBalancerFactory->expects( $this->once() )
+ ->method( 'getEmptyTransactionTicket' );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $readConnectionProvider,
+ 'write' => $writeConnectionProvider
+ ]
+ ),
+ $loadBalancerFactory
+ );
+
+ $instance->getEmptyTransactionTicket( __METHOD__ );
+ }
+
+ public function testGetEmptyTransactionTicketOnMasterChanges() {
+
+ $readConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $writeConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $loadBalancerFactory = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getEmptyTransactionTicket', 'hasMasterChanges' ] )
+ ->getMock();
+
+ $loadBalancerFactory->expects( $this->once() )
+ ->method( 'hasMasterChanges' )
+ ->will( $this->returnValue( true ) );
+
+ $loadBalancerFactory->expects( $this->never() )
+ ->method( 'getEmptyTransactionTicket' );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $readConnectionProvider,
+ 'write' => $writeConnectionProvider
+ ]
+ ),
+ $loadBalancerFactory
+ );
+
+ $this->assertNull(
+ $instance->getEmptyTransactionTicket( __METHOD__ )
+ );
+ }
+
+ public function testCommitAndWaitForReplication() {
+
+ $readConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $writeConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $loadBalancerFactory = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'commitAndWaitForReplication' ] )
+ ->getMock();
+
+ $loadBalancerFactory->expects( $this->once() )
+ ->method( 'commitAndWaitForReplication' );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $readConnectionProvider,
+ 'write' => $writeConnectionProvider
+ ]
+ ),
+ $loadBalancerFactory
+ );
+
+ $instance->commitAndWaitForReplication( __METHOD__, 123 );
+ }
+
+ public function testDoQueryWithAutoCommit() {
+
+ $database = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getFlag', 'clearFlag', 'setFlag', 'getType', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $database->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $database->expects( $this->any() )
+ ->method( 'getFlag' )
+ ->will( $this->returnValue( true ) );
+
+ $database->expects( $this->once() )
+ ->method( 'clearFlag' );
+
+ $database->expects( $this->once() )
+ ->method( 'setFlag' );
+
+ $readConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $readConnectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $writeConnectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $writeConnectionProvider->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $instance = new Database(
+ new ConnectionProviderRef(
+ [
+ 'read' => $readConnectionProvider,
+ 'write' => $writeConnectionProvider
+ ]
+ )
+ );
+
+ $instance->setFlag( Database::AUTO_COMMIT );
+ $instance->query( 'foo', __METHOD__, false );
+ }
+
+ /**
+ * @dataProvider missingWriteConnectionProvider
+ */
+ public function testMissingWriteConnectionThrowsException( $func, $args ) {
+
+ $connectionProvider = $this->getMockBuilder( '\SMW\Connection\ConnectionProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Database(
+ new ConnectionProviderRef( [] )
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ call_user_func_array( [ $instance, $func ], $args );
+ }
+
+ public function dbTypeProvider() {
+ return [
+ [ 'mysql' ],
+ [ 'sqlite' ],
+ [ 'postgres' ]
+ ];
+ }
+
+ public function missingWriteConnectionProvider() {
+
+ yield [
+ 'query', [ 'foo' ]
+ ];
+
+ yield [
+ 'nextSequenceValue', [ 'foo' ]
+ ];
+
+ yield [
+ 'insertId', []
+ ];
+
+ yield [
+ 'clearFlag', [ 'Foo' ]
+ ];
+
+ yield [
+ 'getFlag', [ 'Foo' ]
+ ];
+
+ yield [
+ 'setFlag', [ 'Foo' ]
+ ];
+
+ yield [
+ 'insert', [ 'Foo', 'Bar' ]
+ ];
+
+ yield [
+ 'update', [ 'Foo', 'Bar', 'Foobar' ]
+ ];
+
+ yield [
+ 'delete', [ 'Foo', 'Bar' ]
+ ];
+
+ yield [
+ 'replace', [ 'Foo', 'Bar', 'Foobar' ]
+ ];
+
+ yield [
+ 'makeList', [ 'Foo', 'Bar' ]
+ ];
+
+ yield [
+ 'beginAtomicTransaction', [ 'Foo' ]
+ ];
+
+ yield [
+ 'endAtomicTransaction', [ 'Foo' ]
+ ];
+
+ yield [
+ 'onTransactionIdle', [ 'Foo' ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/LoadBalancerConnectionProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/LoadBalancerConnectionProviderTest.php
new file mode 100644
index 00000000..71568ed7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/LoadBalancerConnectionProviderTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use DatabaseBase;
+use ReflectionClass;
+use SMW\Tests\PHPUnitCompat;
+use SMW\MediaWiki\Connection\LoadBalancerConnectionProvider;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\LoadBalancerConnectionProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class LoadBalancerConnectionProviderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ LoadBalancerConnectionProvider::class,
+ new LoadBalancerConnectionProvider( DB_SLAVE )
+ );
+ }
+
+ public function testGetAndReleaseConnection() {
+
+ $instance = new LoadBalancerConnectionProvider(
+ DB_SLAVE
+ );
+
+ $connection = $instance->getConnection();
+
+ $this->assertInstanceOf(
+ 'DatabaseBase',
+ $instance->getConnection()
+ );
+
+ $this->assertTrue(
+ $instance->getConnection() === $connection
+ );
+
+ $instance->releaseConnection();
+ }
+
+ public function testGetConnectionThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ $instance = new LoadBalancerConnectionProvider(
+ DB_SLAVE
+ );
+
+ $reflector = new ReflectionClass(
+ LoadBalancerConnectionProvider::class
+ );
+
+ $connection = $reflector->getProperty( 'connection' );
+ $connection->setAccessible( true );
+ $connection->setValue( $instance, 'invalid' );
+
+ $this->assertInstanceOf(
+ 'DatabaseBase',
+ $instance->getConnection()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/OptionsBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/OptionsBuilderTest.php
new file mode 100644
index 00000000..1567fb0d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/OptionsBuilderTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\MediaWiki\Connection\OptionsBuilder;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\OptionsBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OptionsBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf(
+ OptionsBuilder::class,
+ new OptionsBuilder()
+ );
+ }
+
+ /**
+ * @dataProvider optionsProvider
+ */
+ public function testMakeSelectOptions( $options ) {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'array',
+ OptionsBuilder::makeSelectOptions( $connection, $options )
+ );
+ }
+
+ /**
+ * @dataProvider optionsProvider
+ */
+ public function testToString( $options ) {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ OptionsBuilder::toString( $options )
+ );
+ }
+
+ public function optionsProvider() {
+
+ $provider[] = [
+ [ 'FOR UPDATE' ]
+ ];
+
+ $provider[] = [
+ [ 'GROUP BY' => [ 'Foo', 'Bar' ] ]
+ ];
+
+ $provider[] = [
+ [ 'ORDER BY' => [ 'Foo', 'Bar' ] ]
+ ];
+
+ $provider[] = [
+ [
+ 'GROUP BY' => [ 'Foo', 'Bar' ],
+ 'ORDER BY' => [ 'Foo', 'Bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/QueryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/QueryTest.php
new file mode 100644
index 00000000..09e4fe68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/QueryTest.php
@@ -0,0 +1,321 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\MediaWiki\Connection\Query;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\Query
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableName' )
+ ->will( $this->returnArgument(0) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Query::class,
+ new Query( $this->connection )
+ );
+ }
+
+ public function testNoType_ThrowsException() {
+
+ $instance = new Query( $this->connection );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->build();
+ }
+
+ public function testNoFields_ThrowsException() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->build();
+ }
+
+ public function testNoJoinType_ThrowsException() {
+
+ $instance = new Query( $this->connection );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->join( 'foo' );
+ }
+
+ public function testTable_Field() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->field( 'bar' );
+
+ $this->assertSame(
+ 'SELECT bar FROM foo',
+ $instance->build()
+ );
+ }
+
+ public function testTable_AS() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo', 't1' );
+ $instance->field( 'bar' );
+
+ $this->assertSame(
+ 'SELECT bar FROM foo AS t1',
+ $instance->build()
+ );
+ }
+
+ public function testTable_Field_Condition() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->field( 'bar', 'b_ar' );
+ $instance->condition( 'foobar' );
+
+ $this->assertSame(
+ '{"tables":"foo","fields":[["bar","b_ar"]],"conditions":[["foobar"]],"joins":[],"options":[],"alias":"","index":0,"autocommit":false}',
+ (string)$instance
+ );
+
+ $this->assertSame(
+ 'SELECT bar AS b_ar FROM foo WHERE (foobar)',
+ $instance->build()
+ );
+ }
+
+ public function testField_HasField() {
+
+ $instance = new Query( $this->connection );
+ $instance->field( 'bar', 'b_ar' );
+
+ $this->assertTrue(
+ $instance->hasField()
+ );
+
+ $this->assertTrue(
+ $instance->hasField( 'bar' )
+ );
+
+ $this->assertFalse(
+ $instance->hasField( 'foo' )
+ );
+ }
+
+ public function testTable_Field_Conditions() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+
+ $instance->field( 'bar', 'b_ar' );
+ $instance->field( 'f', 'a' );
+
+ $instance->condition( 'foobar' );
+ $instance->condition( $instance->asAnd( 'foo_bar' ) );
+ $instance->condition( $instance->asOr( '_bar' ) );
+
+ $this->assertSame(
+ 'SELECT bar AS b_ar, f AS a FROM foo WHERE ((foobar) AND (foo_bar) OR (_bar))',
+ $instance->build()
+ );
+ }
+
+ public function testTable_Join_Field_Conditions() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->join( 'INNER JOIN', 'abc as v1' );
+ $instance->join( 'LEFT JOIN', [ 'def' => 'v2' ] );
+
+ $instance->field( 'bar', 'b_ar' );
+ $instance->field( 'f', 'a' );
+
+ $instance->condition( 'foobar' );
+ $instance->condition( $instance->asAnd( 'foo_bar' ) );
+ $instance->condition( $instance->asOr( '_bar' ) );
+ $instance->condition( $instance->asOr( '_foo' ) );
+
+ $this->assertSame(
+ 'SELECT bar AS b_ar, f AS a FROM foo INNER JOIN abc as v1 LEFT JOIN def AS v2 WHERE (((foobar) AND (foo_bar) OR (_bar)) OR (_foo))',
+ $instance->build()
+ );
+ }
+
+ public function testTable_Join_ON_Field_Conditions() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->field( 'f', 'a' );
+ $instance->join( 'LEFT JOIN', [ 'abc' => 'ON p=d' ] );
+
+ $instance->condition( $instance->asAnd( 'foo_bar' ) );
+
+ $this->assertSame(
+ 'SELECT f AS a FROM foo LEFT JOIN abc ON p=d WHERE (foo_bar)',
+ $instance->build()
+ );
+ }
+
+ public function testTable_Field_Condition_Options_Distinct_Order() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->field( 'f', 'a' );
+
+ $instance->condition( $instance->asOr( 'foo_bar' ) );
+
+ $instance->options(
+ [
+ 'DISTINCT' => true,
+ 'ORDER BY' => '_foo',
+ 'LIMIT' => 42
+ ]
+ );
+
+ $this->assertSame(
+ 'SELECT DISTINCT f AS a FROM foo WHERE (foo_bar) ORDER BY _foo LIMIT 42',
+ $instance->build()
+ );
+ }
+
+ public function testTable_Field_Condition_Options_Group_Having() {
+
+ $instance = new Query( $this->connection );
+ $instance->type( 'select' );
+
+ $instance->table( 'foo' );
+ $instance->field( 'f', 'a' );
+
+ $instance->condition( $instance->asOr( 'foo_bar' ) );
+
+ $instance->options(
+ [
+ 'HAVING' => 'COUNT(Foo) > 5',
+ 'GROUP BY' => '_foo',
+ 'LIMIT' => 42
+ ]
+ );
+
+ $this->assertSame(
+ 'SELECT f AS a FROM foo WHERE (foo_bar) GROUP BY _foo HAVING COUNT(Foo) > 5 LIMIT 42',
+ $instance->build()
+ );
+
+ }
+
+ public function testTable() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableName' )
+ ->with( $this->equalTo( 'Bar' ) );
+
+ $instance = new Query(
+ $this->connection
+ );
+
+ $instance->table( 'Bar' );
+ }
+
+ public function testJoin() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableName' )
+ ->with( $this->equalTo( 'Foo' ) );
+
+ $instance = new Query(
+ $this->connection
+ );
+
+ $instance->join( 'INNER JOIN', [ 'Foo' => 'bar ...' ] );
+ }
+
+ public function testEq() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'addQuotes' )
+ ->with( $this->equalTo( 'Bar' ) )
+ ->will( $this->returnValue( '`Bar`' ) );
+
+ $instance = new Query(
+ $this->connection
+ );
+
+ $this->assertSame(
+ 'Foo=`Bar`',
+ $instance->eq( 'Foo', 'Bar' )
+ );
+ }
+
+ public function testNeq() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'addQuotes' )
+ ->with( $this->equalTo( 'Bar' ) )
+ ->will( $this->returnValue( '`Bar`' ) );
+
+ $instance = new Query(
+ $this->connection
+ );
+
+ $this->assertSame(
+ 'Foo!=`Bar`',
+ $instance->neq( 'Foo', 'Bar' )
+ );
+ }
+
+ public function testExecute() {
+
+ $instance = new Query(
+ $this->connection
+ );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with(
+ $this->equalTo( $instance ),
+ $this->equalTo( 'Foo' ) );
+
+
+ $instance->execute( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/SequenceTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/SequenceTest.php
new file mode 100644
index 00000000..3ec0ab44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/SequenceTest.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\MediaWiki\Connection\Sequence;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\Sequence
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SequenceTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf(
+ Sequence::class,
+ new Sequence( $this->connection )
+ );
+ }
+
+ public function testConstructWithInvalidConnectionThrowsException() {
+ $this->setExpectedException( '\RuntimeException' );
+ new Sequence( 'Foo' );
+ }
+
+ public function testMakeSequence() {
+ $this->assertEquals(
+ 'Foo_bar_seq',
+ Sequence::makeSequence( 'Foo', 'bar' )
+ );
+ }
+
+ public function testNonPostgres() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'foo' ) );
+
+ $instance = new Sequence(
+ $this->connection
+ );
+
+ $this->assertEquals(
+ null,
+ $instance->restart( 'Foo', 'bar')
+ );
+ }
+
+ public function testPostgres() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'postgres' ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) { return $callback(); } ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->equalTo( 'ALTER SEQUENCE Foo_bar_seq RESTART WITH 43' ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'selectField' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new Sequence(
+ $this->connection
+ );
+
+ $this->assertEquals(
+ 43,
+ $instance->restart( 'Foo', 'bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/TransactionProfilerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/TransactionProfilerTest.php
new file mode 100644
index 00000000..72b070db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Connection/TransactionProfilerTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Connection;
+
+use SMW\MediaWiki\Connection\TransactionProfiler;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\Connection\TransactionProfiler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TransactionProfilerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->transactionProfiler = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'setSilenced' ] )
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf(
+ TransactionProfiler::class,
+ new TransactionProfiler( $this->transactionProfiler )
+ );
+ }
+
+ public function testSetSilenced_Enabled() {
+
+ $instance = new TransactionProfiler(
+ $this->transactionProfiler
+ );
+
+ $instance->silenceTransactionProfiler();
+
+ $this->transactionProfiler->expects( $this->once() )
+ ->method( 'setSilenced' );
+
+ $instance->setSilenced( true );
+ }
+
+ public function testSetSilenced_NotEnabled() {
+
+ $instance = new TransactionProfiler(
+ $this->transactionProfiler
+ );
+
+ $this->transactionProfiler->expects( $this->never() )
+ ->method( 'setSilenced' );
+
+ $instance->setSilenced( true );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/DeepRedirectTargetResolverTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/DeepRedirectTargetResolverTest.php
new file mode 100644
index 00000000..49604e38
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/DeepRedirectTargetResolverTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\DeepRedirectTargetResolver;
+use SMW\Tests\Utils\Mock\MockTitle;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\DeepRedirectTargetResolver
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DeepRedirectTargetResolverTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\DeepRedirectTargetResolver',
+ new DeepRedirectTargetResolver( $pageCreator )
+ );
+ }
+
+ public function testResolveRedirectTarget() {
+
+ $title = MockTitle::buildMock( 'Uuuuuuuuuu' );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnValue( MockTitle::buildMock( 'Ooooooo' ) ) );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->any() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\DeepRedirectTargetResolver' )
+ ->setConstructorArgs( [ $pageCreator ] )
+ ->setMethods( [ 'isValidRedirectTarget', 'isRedirect' ] )
+ ->getMock();
+
+ $instance->expects( $this->atLeastOnce() )
+ ->method( 'isValidRedirectTarget' )
+ ->will( $this->returnValue( true ) );
+
+ $instance->expects( $this->at( 0 ) )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $this->assertInstanceOf(
+ '\Title',
+ $instance->findRedirectTargetFor( $title )
+ );
+ }
+
+ public function testResolveRedirectTargetThrowsException() {
+
+ $title = MockTitle::buildMock( 'Uuuuuuuuuu' );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->never() )
+ ->method( 'getRedirectTarget' );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->any() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\DeepRedirectTargetResolver' )
+ ->setConstructorArgs( [ $pageCreator ] )
+ ->setMethods( [ 'isValidRedirectTarget', 'isRedirect' ] )
+ ->getMock();
+
+ $instance->expects( $this->atLeastOnce() )
+ ->method( 'isValidRedirectTarget' )
+ ->will( $this->returnValue( false ) );
+
+ $instance->expects( $this->at( 0 ) )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( false ) );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->findRedirectTargetFor( $title );
+ }
+
+ public function testTryToResolveCircularRedirectThrowsException() {
+
+ $title = MockTitle::buildMock( 'Uuuuuuuuuu' );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnValue( $title ) );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->any() )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\DeepRedirectTargetResolver' )
+ ->setConstructorArgs( [ $pageCreator ] )
+ ->setMethods( [ 'isRedirect' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->findRedirectTargetFor( $title );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/CallableUpdateTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/CallableUpdateTest.php
new file mode 100644
index 00000000..540045b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/CallableUpdateTest.php
@@ -0,0 +1,237 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Deferred;
+
+use SMW\MediaWiki\Deferred\CallableUpdate;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Deferred\CallableUpdate
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class CallableUpdateTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->getUtilityFactory()->newSpyLogger();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->clearPendingDeferredUpdates();
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $callback = function() {
+ return null;
+ };
+
+ $this->assertInstanceOf(
+ CallableUpdate::class,
+ new CallableUpdate( $callback )
+ );
+ }
+
+ public function testUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testUpdateOnEmptyCallback() {
+
+ $instance = new CallableUpdate();
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertContains(
+ 'Empty callback',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateOnLateCallback() {
+
+ $instance = new CallableUpdate();
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance->setCallback( $callback );
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertContains(
+ 'Added',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testWaitableUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $instance->releasePendingUpdates();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testUpdateWithDisabledDeferredUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->enabledDeferredUpdate( false );
+ $instance->pushUpdate();
+ }
+
+ public function testOrigin() {
+
+ $callback = function() {
+ };
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setOrigin( 'Foo' );
+
+ $this->assertContains(
+ 'Foo',
+ $instance->getOrigin()
+ );
+ }
+
+ public function testFilterDuplicateQueueEntryByFingerprint() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setFingerprint( __METHOD__ );
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $instance = new CallableUpdate(
+ $callback
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setFingerprint( __METHOD__ );
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testStage() {
+
+ $instance = new CallableUpdate();
+
+ $this->assertEquals(
+ 'post',
+ $instance->getStage()
+ );
+
+ $instance->asPresend();
+
+ $this->assertEquals(
+ 'pre',
+ $instance->getStage()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/ChangeTitleUpdateTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/ChangeTitleUpdateTest.php
new file mode 100644
index 00000000..a3e67e64
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/ChangeTitleUpdateTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Deferred;
+
+use SMW\MediaWiki\Deferred\ChangeTitleUpdate;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Deferred\ChangeTitleUpdate
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangeTitleUpdateTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $jobFactory;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'newUpdateJob' ] )
+ ->getMock();
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobFactory', $this->jobFactory );
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->clearPendingDeferredUpdates();
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChangeTitleUpdate::class,
+ new ChangeTitleUpdate()
+ );
+ }
+
+ public function testDoUpdate() {
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $oldTitle = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $newTitle = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ChangeTitleUpdate(
+ $oldTitle,
+ $newTitle
+ );
+
+ $instance->doUpdate();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/TransactionalCallableUpdateTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/TransactionalCallableUpdateTest.php
new file mode 100644
index 00000000..4505ba62
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Deferred/TransactionalCallableUpdateTest.php
@@ -0,0 +1,395 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Deferred;
+
+use SMW\MediaWiki\Deferred\TransactionalCallableUpdate;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Deferred\TransactionalCallableUpdate
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TransactionalCallableUpdateTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $spyLogger;
+ private $connection;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->getUtilityFactory()->newSpyLogger();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->clearPendingDeferredUpdates();
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $callback = function() {
+ return null;
+ };
+
+ $this->assertInstanceOf(
+ TransactionalCallableUpdate::class,
+ new TransactionalCallableUpdate( $callback, $this->connection )
+ );
+ }
+
+ public function testUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testUpdateOnEmptyCallback() {
+
+ $instance = new TransactionalCallableUpdate(
+ null,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertContains(
+ 'Empty callback',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testUpdateOnLateCallback() {
+
+ $instance = new TransactionalCallableUpdate(
+ null,
+ $this->connection
+ );
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance->setCallback( $callback );
+
+ $instance->setLogger( $this->spyLogger );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ $this->assertContains(
+ 'Added',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testWaitableUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $instance->releasePendingUpdates();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testUpdateWithDisabledDeferredUpdate() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->enabledDeferredUpdate( false );
+ $instance->pushUpdate();
+ }
+
+ public function testOrigin() {
+
+ $callback = function() {
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setOrigin( 'Foo' );
+
+ $this->assertContains(
+ 'Foo',
+ $instance->getOrigin()
+ );
+ }
+
+ public function testFilterDuplicateQueueEntryByFingerprint() {
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setFingerprint( __METHOD__ );
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $this->connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setFingerprint( __METHOD__ );
+ $instance->markAsPending( true );
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testUpdateOnTransactionIdle() {
+
+ $callback = function( $callback ) {
+ return call_user_func( $callback );
+ };
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( $callback ) );
+
+ $this->testEnvironment->clearPendingDeferredUpdates();
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->waitOnTransactionIdle();
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testCommitWithTransactionTicketOnDeferrableUpdate() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'getEmptyTransactionTicket' );
+
+ $this->testEnvironment->clearPendingDeferredUpdates();
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->isDeferrableUpdate( true );
+ $instance->commitWithTransactionTicket();
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testCommitWithTransactionTicketOnNonDeferrableUpdate() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->never() )
+ ->method( 'getEmptyTransactionTicket' );
+
+ $this->testEnvironment->clearPendingDeferredUpdates();
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->once() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->isDeferrableUpdate( false );
+ $instance->commitWithTransactionTicket();
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testCancelOnRollback() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->never() )
+ ->method( 'getEmptyTransactionTicket' );
+
+ $this->testEnvironment->clearPendingDeferredUpdates();
+
+ $test = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doTest' ] )
+ ->getMock();
+
+ $test->expects( $this->never() )
+ ->method( 'doTest' );
+
+ $callback = function() use ( $test ) {
+ $test->doTest();
+ };
+
+ $instance = new TransactionalCallableUpdate(
+ $callback,
+ $connection
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->isDeferrableUpdate( false );
+ $instance->commitWithTransactionTicket();
+
+ // #3765
+ $instance->cancelOnRollback( \SMW\MediaWiki\Database::TRIGGER_ROLLBACK );
+
+ $instance->pushUpdate();
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/EditInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/EditInfoProviderTest.php
new file mode 100644
index 00000000..8aa4a60b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/EditInfoProviderTest.php
@@ -0,0 +1,274 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use ParserOutput;
+use SMW\MediaWiki\EditInfoProvider;
+
+/**
+ * @covers \SMW\MediaWiki\EditInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class EditInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\EditInfoProvider',
+ new EditInfoProvider( $wikiPage, $revision, $user )
+ );
+ }
+
+ /**
+ * @dataProvider wikiPageDataProvider
+ */
+ public function testFetchContentInfo( $parameters, $expected ) {
+
+ $instance = new EditInfoProvider(
+ $parameters['wikiPage'],
+ $parameters['revision']
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->fetchEditInfo()->getOutput()
+ );
+ }
+
+ public function testFetchSemanticData() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfo = (object)[];
+ $editInfo->output = new ParserOutput();
+ $editInfo->output->setExtensionData( \SMW\ParserData::DATA_ID, $semanticData );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareContentForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+
+ $instance = new EditInfoProvider(
+ $wikiPage,
+ $this->newRevisionStub()
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $instance->fetchSemanticData()
+ );
+ }
+
+ /**
+ * @dataProvider wikiPageDataProvider
+ */
+ public function testFetchContentInfoWithDisabledContentHandler( $parameters, $expected ) {
+
+ if ( !method_exists( '\WikiPage', 'prepareTextForEdit' ) ) {
+ $this->markTestSkipped( 'WikiPage::prepareTextForEdit is no longer accessible (MW 1.29+)' );
+ }
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->setConstructorArgs( [
+ $parameters['wikiPage'],
+ $parameters['revision'],
+ null
+ ] )
+ ->setMethods( [ 'hasContentForEditMethod' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'hasContentForEditMethod' )
+ ->will( $this->returnValue( false ) );
+
+ $this->assertEquals(
+ $expected,
+ $instance->fetchEditInfo()->getOutput()
+ );
+ }
+
+ public function wikiPageDataProvider() {
+
+ // 'WikiPage::prepareTextForEdit is no longer accessible (MW 1.29+)'
+ $prepareTextForEditExists = method_exists( '\WikiPage', 'prepareTextForEdit' );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ #0 No parserOutput object
+ $editInfo = (object)[];
+ $editInfo->output = null;
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->setConstructorArgs( [ $title ] )
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareContentForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+
+ if ( $prepareTextForEditExists ) {
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareTextForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+ }
+
+ $provider[] = [
+ [
+ 'editInfo' => $editInfo,
+ 'wikiPage' => $wikiPage,
+ 'revision' => $this->newRevisionStub()
+ ],
+ null
+ ];
+
+ #1
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->setConstructorArgs( [ $title ] )
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareContentForEdit' )
+ ->will( $this->returnValue( false ) );
+
+ if ( $prepareTextForEditExists ) {
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareTextForEdit' )
+ ->will( $this->returnValue( false ) );
+ }
+
+ $provider[] = [
+ [
+ 'editInfo' => false,
+ 'wikiPage' => $wikiPage,
+ 'revision' => $this->newRevisionStub()
+ ],
+ null
+ ];
+
+ #2
+ $editInfo = (object)[];
+ $editInfo->output = new ParserOutput();
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->setConstructorArgs( [ $title ] )
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareContentForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+
+ if ( $prepareTextForEditExists ) {
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareTextForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+ }
+
+ $provider[] = [
+ [
+ 'editInfo' => $editInfo,
+ 'wikiPage' => $wikiPage,
+ 'revision' => $this->newRevisionStub()
+ ],
+ $editInfo->output
+ ];
+
+ #3
+ $editInfo = (object)[];
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->setConstructorArgs( [ $title ] )
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareContentForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+
+ if ( $prepareTextForEditExists ) {
+ $wikiPage->expects( $this->any() )
+ ->method( 'prepareTextForEdit' )
+ ->will( $this->returnValue( $editInfo ) );
+ }
+
+ $provider[] = [
+ [
+ 'editInfo' => $editInfo,
+ 'wikiPage' => $wikiPage,
+ 'revision' => $this->newRevisionStub()
+ ],
+ null
+ ];
+
+ return $provider;
+ }
+
+ private function newRevisionStub() {
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRawText', 'getContent' ] )
+ ->getMock();
+
+ $revision->expects( $this->any() )
+ ->method( 'getRawText' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $revision->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValueMap( [
+ [ \Revision::RAW, null, 'Foo' ],
+ [ \Revision::FOR_PUBLIC, null, $this->newContentStub() ],
+ ] ) );
+
+ return $revision;
+ }
+
+ private function newContentStub() {
+
+ if ( !class_exists( 'ContentHandler' ) ) {
+ return null;
+ }
+
+ $contentHandler = $this->getMockBuilder( '\ContentHandler' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentHandler->expects( $this->atLeastOnce() )
+ ->method( 'getDefaultFormat' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content->expects( $this->atLeastOnce() )
+ ->method( 'getContentHandler' )
+ ->will( $this->returnValue( $contentHandler ) );
+
+ return $content;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Exception/ExtendedPermissionsErrorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Exception/ExtendedPermissionsErrorTest.php
new file mode 100644
index 00000000..ae5ebd2b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Exception/ExtendedPermissionsErrorTest.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Exception;
+
+use SMW\MediaWiki\Exception\ExtendedPermissionsError;
+
+/**
+ * @covers \SMW\MediaWiki\Exception\ExtendedPermissionsError
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ExtendedPermissionsErrorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Exception\ExtendedPermissionsError',
+ new ExtendedPermissionsError( 'Foo' )
+ );
+
+ $this->assertInstanceOf(
+ '\PermissionsError',
+ new ExtendedPermissionsError( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleDeleteTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleDeleteTest.php
new file mode 100644
index 00000000..0bbdbddd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleDeleteTest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Hooks\ArticleDelete;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ArticleDelete
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ArticleDeleteTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $jobFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'smwgEnableUpdateJobs' => false,
+ 'smwgEnabledDeferredUpdate' => false
+ ]
+ );
+
+ $this->jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobFactory', $this->jobFactory );
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ArticleDelete( $store );
+
+ $this->assertInstanceOf(
+ ArticleDelete::class,
+ $instance
+ );
+ }
+
+ public function testProcess() {
+
+ $idTable = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $updateDispatcherJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\UpdateDispatcherJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserCachePurgeJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newUpdateDispatcherJob' )
+ ->will( $this->returnValue( $updateDispatcherJob ) );
+
+ $this->jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newParserCachePurgeJob' )
+ ->will( $this->returnValue( $parserCachePurgeJob ) );
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'deleteSubject' );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( [ new DIProperty( 'Foo' ) ] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $subject->getTitle() ) );
+
+ $instance = new ArticleDelete(
+ $store
+ );
+
+ $this->assertTrue(
+ $instance->process( $wikiPage )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleFromTitleTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleFromTitleTest.php
new file mode 100644
index 00000000..ead34554
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleFromTitleTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\ArticleFromTitle;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ArticleFromTitle
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ArticleFromTitleTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ArticleFromTitle::class,
+ new ArticleFromTitle( $this->store )
+ );
+ }
+
+ /**
+ * @dataProvider namespaceProvider
+ */
+ public function testProcess( $namespace, $expected ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( $namespace ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ArticleFromTitle( $this->store );
+ $instance->process( $title, $wikiPage );
+
+ $this->assertInstanceOf(
+ $expected,
+ $wikiPage
+ );
+ }
+
+ public function namespaceProvider() {
+
+ $provider[] = [
+ SMW_NS_PROPERTY,
+ 'SMW\Page\PropertyPage'
+ ];
+
+ $provider[] = [
+ SMW_NS_CONCEPT,
+ 'SMW\Page\ConceptPage'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleProtectCompleteTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleProtectCompleteTest.php
new file mode 100644
index 00000000..40cec922
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleProtectCompleteTest.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\DataItemFactory;
+use SMW\MediaWiki\Hooks\ArticleProtectComplete;
+use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ArticleProtectComplete
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ArticleProtectCompleteTest extends \PHPUnit_Framework_TestCase {
+
+ private $spyLogger;
+ private $testEnvironment;
+ private $semanticDataFactory;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->spyLogger = $this->testEnvironment->getUtilityFactory()->newSpyLogger();
+ $this->semanticDataFactory = $this->testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ArticleProtectComplete::class,
+ new ArticleProtectComplete( $title, $editInfoProvider )
+ );
+ }
+
+ public function testProcessOnSelfInvokedReason() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ArticleProtectComplete(
+ $title,
+ $editInfoProvider
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $protections = [];
+ $reason = \SMW\Message::get( 'smw-edit-protection-auto-update' );
+
+ $instance->process( $protections, $reason );
+
+ $this->assertContains(
+ 'No changes required, invoked by own process',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testProcessOnMatchableEditProtectionToAddAnnotation() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_SPECIAL ) );
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider->expects( $this->once() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new ArticleProtectComplete(
+ $title,
+ $editInfoProvider
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setOptions(
+ [
+ 'smwgEditProtectionRight' => 'Foo'
+ ]
+ );
+
+ $protections = [ 'edit' => 'Foo' ];
+ $reason = '';
+
+ $instance->process( $protections, $reason );
+
+ $this->assertContains(
+ 'ArticleProtectComplete addProperty `Is edit protected`',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testProcessOnUnmatchableEditProtectionToRemoveAnnotation() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ $this->dataItemFactory->newDIWikiPage( __METHOD__, NS_SPECIAL )
+ );
+
+ $dataItem = $this->dataItemFactory->newDIBoolean( true );
+ $dataItem->setOption( EditProtectedPropertyAnnotator::SYSTEM_ANNOTATION, true );
+
+ $semanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_EDIP' ),
+ $dataItem
+ );
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_SPECIAL ) );
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider->expects( $this->once() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new ArticleProtectComplete(
+ $title,
+ $editInfoProvider
+ );
+
+ $instance->setLogger( $this->spyLogger );
+
+ $instance->setOptions(
+ [
+ 'smwgEditProtectionRight' => 'Foo2'
+ ]
+ );
+
+ $protections = [ 'edit' => 'Foo' ];
+ $reason = '';
+
+ $instance->process( $protections, $reason );
+
+ $this->assertContains(
+ 'ArticleProtectComplete removeProperty `Is edit protected`',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticlePurgeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticlePurgeTest.php
new file mode 100644
index 00000000..294da778
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticlePurgeTest.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\Factbox\FactboxCache;
+use SMW\MediaWiki\Hooks\ArticlePurge;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+use WikiPage;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ArticlePurge
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ArticlePurgeTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+ private $testEnvironment;
+ private $cache;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $settings = [
+ 'smwgFactboxUseCache' => true,
+ 'smwgMainCacheType' => 'hash'
+ ];
+
+ $this->testEnvironment = new TestEnvironment( $settings );
+
+ $this->cache = $this->applicationFactory->newCacheFactory()->newFixedInMemoryCache();
+ $this->applicationFactory->registerObject( 'Cache', $this->cache );
+ }
+
+ public function tearDown() {
+ $this->applicationFactory->clear();
+ $this->testEnvironment->tearDown();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Hooks\ArticlePurge',
+ new ArticlePurge( $wikiPage )
+ );
+ }
+
+ /**
+ * @dataProvider titleDataProvider
+ */
+ public function testProcess( $setup, $expected ) {
+
+ $wikiPage = new WikiPage( $setup['title'] );
+ $pageId = $wikiPage->getTitle()->getArticleID();
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgAutoRefreshOnPurge',
+ $setup['smwgAutoRefreshOnPurge']
+ );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgFactboxFeatures',
+ SMW_FACTBOX_PURGE_REFRESH
+ );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgQueryResultCacheRefreshOnPurge',
+ $setup['smwgQueryResultCacheRefreshOnPurge']
+ );
+
+ $instance = new ArticlePurge();
+
+ $cacheFactory = $this->applicationFactory->newCacheFactory();
+ $factboxCacheKey = \SMW\Factbox\CachedFactbox::makeCacheKey( $pageId );
+ $purgeCacheKey = $cacheFactory->getPurgeCacheKey( $pageId );
+
+ $this->assertEquals(
+ $expected['autorefreshPreProcess'],
+ $this->cache->fetch( $purgeCacheKey ),
+ 'Asserts the autorefresh cache status before processing'
+ );
+
+ // Travis 210.5, 305.3
+ $travis = $this->cache->fetch( $factboxCacheKey );
+ $travisText = json_encode( $travis );
+ $this->assertEquals(
+ $expected['factboxPreProcess'],
+ $travis,
+ "Asserts the factbox cache status before processing, {$travisText}"
+ );
+
+ $this->assertFalse(
+ $this->cache->fetch( $purgeCacheKey ),
+ 'Asserts that before processing ...'
+ );
+
+ $result = $instance->process( $wikiPage );
+
+ // Post-process check
+ $this->assertTrue(
+ $result
+ );
+
+ $this->assertEquals(
+ $expected['autorefreshPostProcess'],
+ $this->cache->fetch( $purgeCacheKey ),
+ 'Asserts the autorefresh cache status after processing'
+ );
+
+ $this->assertEquals(
+ $expected['factboxPostProcess'],
+ $this->cache->fetch( $factboxCacheKey ),
+ 'Asserts the factbox cache status after processing'
+ );
+ }
+
+ public function titleDataProvider() {
+
+ $validIdTitle = MockTitle::buildMock( 'validIdTitle' );
+
+ $validIdTitle->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 9999 ) );
+
+ #0 Id = cache
+ $provider[] = [
+ [
+ 'title' => $validIdTitle,
+ 'smwgAutoRefreshOnPurge' => true,
+ 'smwgFactboxCacheRefreshOnPurge' => true,
+ 'smwgQueryResultCacheRefreshOnPurge' => false
+ ],
+ [
+ 'factboxPreProcess' => false,
+ 'autorefreshPreProcess' => false,
+ 'autorefreshPostProcess' => true,
+ 'factboxPostProcess' => false,
+ ]
+ ];
+
+ #1 Disabled setting
+ $validIdTitle = MockTitle::buildMock( 'Disabled' );
+
+ $validIdTitle->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 9099 ) );
+
+ $provider[] = [
+ [
+ 'title' => $validIdTitle,
+ 'smwgAutoRefreshOnPurge' => false,
+ 'smwgFactboxCacheRefreshOnPurge' => false,
+ 'smwgQueryResultCacheRefreshOnPurge' => false
+ ],
+ [
+ 'factboxPreProcess' => false,
+ 'autorefreshPreProcess' => false,
+ 'autorefreshPostProcess' => false,
+ 'factboxPostProcess' => false,
+ ]
+ ];
+
+ // #2 No Id
+ $nullIdTitle = MockTitle::buildMock( 'NullId' );
+
+ $nullIdTitle->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 0 ) );
+
+ $provider[] = [
+ [
+ 'title' => $nullIdTitle,
+ 'smwgAutoRefreshOnPurge' => true,
+ 'smwgFactboxCacheRefreshOnPurge' => true,
+ 'smwgQueryResultCacheRefreshOnPurge' => false
+ ],
+ [
+ 'factboxPreProcess' => false,
+ 'autorefreshPreProcess' => false,
+ 'autorefreshPostProcess' => false,
+ 'factboxPostProcess' => false,
+ ]
+ ];
+
+ #3 No Id
+ $provider[] = [
+ [
+ 'title' => $nullIdTitle,
+ 'smwgAutoRefreshOnPurge' => true,
+ 'smwgFactboxCacheRefreshOnPurge' => false,
+ 'smwgQueryResultCacheRefreshOnPurge' => false
+ ],
+ [
+ 'factboxPreProcess' => false,
+ 'autorefreshPreProcess' => false,
+ 'autorefreshPostProcess' => false,
+ 'factboxPostProcess' => false,
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleViewHeaderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleViewHeaderTest.php
new file mode 100644
index 00000000..cb1405f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ArticleViewHeaderTest.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Hooks\ArticleViewHeader;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ArticleViewHeader
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ArticleViewHeaderTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ArticleViewHeader::class,
+ new ArticleViewHeader( $this->store )
+ );
+ }
+
+ public function testProcessOnCategory() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__, NS_CATEGORY );
+ $property = new DIProperty( DIProperty::TYPE_CHANGE_PROP );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'hasProperty' )
+ ->with( $this->equalTo( $property ) )
+ ->will( $this->returnValue( true ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $output = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $output->expects( $this->once() )
+ ->method( 'addHtml' );
+
+ $context = $this->getMockBuilder( '\RequestContext' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $context->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $output ) );
+
+ $page = $this->getMockBuilder( '\Article' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $subject->getTitle() ) );
+
+ $page->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $instance = new ArticleViewHeader(
+ $this->store
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgChangePropagationWatchlist' => [ '_SUBC' ]
+ ]
+ );
+
+ $outputDone = '';
+ $useParserCache = '';
+
+ $instance->process( $page, $outputDone, $useParserCache );
+
+ $this->assertFalse(
+ $useParserCache
+ );
+ }
+
+ public function testProcessOnNoCategory() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $output = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page = $this->getMockBuilder( '\Article' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $subject->getTitle() ) );
+
+ $page->expects( $this->never() )
+ ->method( 'getContext' );
+
+ $instance = new ArticleViewHeader(
+ $this->store
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgChangePropagationWatchlist' => [ '_SUBC' ]
+ ]
+ );
+
+ $outputDone = '';
+ $useParserCache = '';
+
+ $instance->process( $page, $outputDone, $useParserCache );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BaseTemplateToolboxTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BaseTemplateToolboxTest.php
new file mode 100644
index 00000000..765ec71a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BaseTemplateToolboxTest.php
@@ -0,0 +1,237 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\BaseTemplateToolbox;
+use SMW\Tests\Utils\Mock\MockTitle;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\BaseTemplateToolbox
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BaseTemplateToolboxTest extends \PHPUnit_Framework_TestCase {
+
+ private $namespaceExaminer;
+ private $skinTemplate;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ BaseTemplateToolbox::class,
+ new BaseTemplateToolbox( $this->namespaceExaminer )
+ );
+ }
+
+ /**
+ * @dataProvider skinTemplateDataProvider
+ */
+ public function testProcess( $setup, $expected ) {
+
+ $this->namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( $setup['settings']['isEnabledNamespace'] ) );
+
+ $toolbox = [];
+
+ $instance = new BaseTemplateToolbox(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgBrowseFeatures' => $setup['settings']['smwgBrowseFeatures']
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( $setup['skinTemplate'], $toolbox )
+ );
+
+ if ( $expected['count'] == 0 ) {
+ $this->assertEmpty( $toolbox );
+ } else {
+ $this->assertCount(
+ $expected['count'],
+ $toolbox['smw-browse']
+ );
+ }
+ }
+
+ public function skinTemplateDataProvider() {
+
+ #0 Standard title
+ $settings = [
+ 'isEnabledNamespace' => true,
+ 'smwgBrowseFeatures' => SMW_BROWSE_TLINK
+ ];
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->newSkinStub() ) );
+
+ $skinTemplate->data['isarticle'] = true;
+
+ $provider[] = [
+ [
+ 'skinTemplate' => $skinTemplate,
+ 'settings' => $settings
+ ],
+ [ 'count' => 4 ],
+ ];
+
+ #1 isarticle = false
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->newSkinStub() ) );
+
+ $skinTemplate->data['isarticle'] = false;
+
+ $provider[] = [
+ [
+ 'skinTemplate' => $skinTemplate,
+ 'settings' => $settings
+ ],
+ [ 'count' => 0 ],
+ ];
+
+ #2 smwgBrowseFeatures = false
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->newSkinStub() ) );
+
+ $skinTemplate->data['isarticle'] = true;
+
+ $settings = [
+ 'isEnabledNamespace' => true,
+ 'smwgBrowseFeatures' => SMW_BROWSE_NONE
+ ];
+
+ $provider[] = [
+ [
+ 'skinTemplate' => $skinTemplate,
+ 'settings' => $settings
+ ],
+ [ 'count' => 0 ],
+ ];
+
+ #3 smwgNamespacesWithSemanticLinks = false
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->newSkinStub() ) );
+
+ $skinTemplate->data['isarticle'] = true;
+
+ $settings = [
+ 'isEnabledNamespace' => false,
+ 'smwgBrowseFeatures' => SMW_BROWSE_TLINK
+ ];
+
+ $provider[] = [
+ [
+ 'skinTemplate' => $skinTemplate,
+ 'settings' => $settings
+ ],
+ [ 'count' => 0 ],
+ ];
+
+ #4 Special page
+ $settings = [
+ 'isEnabledNamespace' => true,
+ 'smwgBrowseFeatures' => SMW_BROWSE_TLINK
+ ];
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $skin ) );
+
+ $skinTemplate->data['isarticle'] = true;
+
+ $provider[] = [
+ [
+ 'skinTemplate' => $skinTemplate,
+ 'settings' => $settings
+ ],
+ [ 'count' => 0 ],
+ ];
+
+ return $provider;
+ }
+
+ private function newSkinStub() {
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( Title::newFromText( __METHOD__ ) ) );
+
+ $skin->expects( $this->any() )
+ ->method( 'msg' )
+ ->will( $this->returnValue( $message ) );
+
+ return $skin;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforeDisplayNoArticleTextTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforeDisplayNoArticleTextTest.php
new file mode 100644
index 00000000..dbbc40f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforeDisplayNoArticleTextTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\BeforeDisplayNoArticleText;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\BeforeDisplayNoArticleText
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class BeforeDisplayNoArticleTextTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\BeforeDisplayNoArticleText',
+ new BeforeDisplayNoArticleText( $wikiPage )
+ );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testPerformUpdate( $namespace, $text, $expected ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $text ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( $namespace ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new BeforeDisplayNoArticleText( $wikiPage );
+
+ $this->assertEquals( $expected, $instance->process() );
+ }
+
+ public function titleProvider() {
+
+ $provider = [
+ [ SMW_NS_PROPERTY, 'Modification date', false ],
+ [ SMW_NS_PROPERTY, 'Foo', true ],
+ [ NS_MAIN, 'Modification date', true ],
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforePageDisplayTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforePageDisplayTest.php
new file mode 100644
index 00000000..3c025f01
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/BeforePageDisplayTest.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\BeforePageDisplay;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\BeforePageDisplay
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class BeforePageDisplayTest extends \PHPUnit_Framework_TestCase {
+
+ private $outputPage;
+ private $request;
+ private $skin;
+
+ protected function setUp() {
+
+ $this->request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestContext = $this->getMockBuilder( '\RequestContext' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestContext->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->will( $this->returnValue( $this->request ) );
+
+ $this->outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->skin->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $requestContext ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ BeforePageDisplay::class,
+ new BeforePageDisplay()
+ );
+ }
+
+ public function testModules() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->any() )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'smw-prefs-general-options-suggester-textinput' ) )
+ ->will( $this->returnValue( true ) );
+
+ $this->outputPage->expects( $this->exactly( 2 ) )
+ ->method( 'addModules' );
+
+ $this->outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $instance = new BeforePageDisplay();
+
+ $instance->process( $this->outputPage, $this->skin );
+ }
+
+ public function testPrependHTML_InstallerIncompleteTasks() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputPage->expects( $this->atLeastOnce() )
+ ->method( 'prependHTML' );
+
+ $this->outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $instance = new BeforePageDisplay();
+
+ $instance->setOptions(
+ [
+ 'installer.incomplete_tasks' => [ 'Foo', 'Bar' ]
+ ]
+ );
+
+ $instance->process( $this->outputPage, $this->skin );
+ }
+
+ /**
+ * @dataProvider titleDataProvider
+ */
+ public function testProcess( $setup, $expected ) {
+
+ $expected = $expected['result'] ? $this->atLeastOnce() : $this->never();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $this->outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $setup['title'] ) );
+
+ $this->outputPage->expects( $expected )
+ ->method( 'addLink' );
+
+ $instance = new BeforePageDisplay();
+
+ $this->assertTrue(
+ $instance->process( $this->outputPage, $this->skin )
+ );
+ }
+
+ public function titleDataProvider() {
+
+ #0 Standard title
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPrefixedText' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( false ) );
+
+ $provider[] = [
+ [
+ 'title' => $title
+ ],
+ [
+ 'result' => true
+ ]
+ ];
+
+ #1 as SpecialPage
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $provider[] = [
+ [
+ 'title' => $title
+ ],
+ [
+ 'result' => false
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/EditPageFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/EditPageFormTest.php
new file mode 100644
index 00000000..b93dbedd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/EditPageFormTest.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\EditPageForm;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\EditPageForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EditPageFormTest extends \PHPUnit_Framework_TestCase {
+
+ private $namespaceExaminer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\EditPageForm',
+ new EditPageForm( $this->namespaceExaminer )
+ );
+ }
+
+ public function testDisabledHelp() {
+
+ $editPage = $this->getMockBuilder( '\EditPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new EditPageForm(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgEnabledEditPageHelp' => false
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( $editPage )
+ );
+ }
+
+ public function testDisabledOnUserPreference() {
+
+ $editPage = $this->getMockBuilder( '\EditPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new EditPageForm(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOptions(
+ [
+ 'prefs-disable-editpage' => true
+ ]
+ );
+
+ $editPage->editFormPageTop = '';
+
+ $instance->process( $editPage );
+
+ $this->assertEmpty(
+ $editPage->editFormPageTop
+ );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testExtendEditFormPageTop( $title, $namespaces, $isSemanticEnabled, $expected ) {
+
+ $this->namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->with( $this->equalTo( $namespaces ) )
+ ->will( $this->returnValue( $isSemanticEnabled ) );
+
+ $editPage = $this->getMockBuilder( '\EditPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $editPage->editFormPageTop = '';
+
+ $instance = new EditPageForm(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgEnabledEditPageHelp' => true
+ ]
+ );
+
+ $instance->process( $editPage );
+
+ $this->assertContains(
+ $expected,
+ $editPage->editFormPageTop
+ );
+ }
+
+ public function titleProvider() {
+
+ $provider[] = [
+ Title::newFromText( 'Foo', SMW_NS_PROPERTY ),
+ SMW_NS_PROPERTY,
+ true,
+ 'smw-editpage-property-annotation-enabled'
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Modification date', SMW_NS_PROPERTY ),
+ SMW_NS_PROPERTY,
+ true,
+ 'smw-editpage-property-annotation-disabled'
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Foo', SMW_NS_CONCEPT ),
+ SMW_NS_CONCEPT,
+ true,
+ 'smw-editpage-concept-annotation-enabled'
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Foo', NS_MAIN ),
+ NS_MAIN,
+ true,
+ 'smw-editpage-annotation-enabled'
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Foo', NS_MAIN ),
+ NS_MAIN,
+ false,
+ 'smw-editpage-annotation-disabled'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionSchemaUpdatesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionSchemaUpdatesTest.php
new file mode 100644
index 00000000..fa4f09e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionSchemaUpdatesTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\ExtensionSchemaUpdates;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ExtensionSchemaUpdates
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExtensionSchemaUpdatesTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $databaseUpdater = $this->getMockBuilder( '\DatabaseUpdater' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\ExtensionSchemaUpdates',
+ new ExtensionSchemaUpdates( $databaseUpdater )
+ );
+ }
+
+ public function testProcess() {
+
+ $databaseUpdater = $this->getMockBuilder( '\DatabaseUpdater' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'addExtensionUpdate' ] )
+ ->getMockForAbstractClass();
+
+ $databaseUpdater->expects( $this->once() )
+ ->method( 'addExtensionUpdate' );
+
+ $instance = new ExtensionSchemaUpdates( $databaseUpdater );
+ $instance->process();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionTypesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionTypesTest.php
new file mode 100644
index 00000000..6caf026a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ExtensionTypesTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\ExtensionTypes;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ExtensionTypes
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExtensionTypesTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ExtensionTypes::class,
+ new ExtensionTypes()
+ );
+ }
+
+ public function testProcess() {
+
+ $extensionTypes = [];
+
+ $instance = new ExtensionTypes();
+ $instance->process( $extensionTypes );
+
+ $this->assertArrayHasKey(
+ 'semantic',
+ $extensionTypes
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/FileUploadTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/FileUploadTest.php
new file mode 100644
index 00000000..a8e38e70
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/FileUploadTest.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use ParserOutput;
+use SMW\MediaWiki\Hooks\FileUpload;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\FileUpload
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FileUploadTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgPageSpecialProperties' => [ '_MEDIA', '_MIME' ],
+ 'smwgNamespacesWithSemanticLinks' => [ NS_FILE => true ],
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false
+ ] );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ FileUpload::class,
+ new FileUpload( $namespaceExaminer )
+ );
+ }
+
+ public function testprocessEnabledNamespace() {
+
+ $title = Title::newFromText( __METHOD__, NS_FILE );
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $namespaceExaminer->expects( $this->atLeastOnce() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $file = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $file->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiFilePage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiFilePage->expects( $this->once() )
+ ->method( 'getParserOutput' )
+ ->will( $this->returnValue( new ParserOutput() ) );
+
+ $wikiFilePage->expects( $this->atLeastOnce() )
+ ->method( 'getFile' )
+ ->will( $this->returnValue( $file ) );
+
+ $pageCreator = $this->getMockBuilder( 'SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'createFilePage' ] )
+ ->getMock();
+
+ $pageCreator->expects( $this->once() )
+ ->method( 'createFilePage' )
+ ->with( $this->equalTo( $title ) )
+ ->will( $this->returnValue( $wikiFilePage ) );
+
+ $this->testEnvironment->registerObject( 'PageCreator', $pageCreator );
+
+ $instance = new FileUpload(
+ $namespaceExaminer
+ );
+
+ $reUploadStatus = true;
+
+ $this->assertTrue(
+ $instance->process( $file, $reUploadStatus )
+ );
+
+ $this->assertEquals(
+ $wikiFilePage->smwFileReUploadStatus,
+ $reUploadStatus
+ );
+ }
+
+ public function testTryToProcessDisabledNamespace() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $namespaceExaminer->expects( $this->atLeastOnce() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( false ) );
+
+ $file = $this->getMockBuilder( 'File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $file->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $pageCreator = $this->getMockBuilder( 'SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $this->never() ) // <-- never
+ ->method( 'createFilePage' );
+
+ $this->testEnvironment->registerObject( 'PageCreator', $pageCreator );
+
+ $instance = new FileUpload(
+ $namespaceExaminer
+ );
+
+ $instance->process( $file, false );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/GetPreferencesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/GetPreferencesTest.php
new file mode 100644
index 00000000..33dee2ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/GetPreferencesTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\GetPreferences;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\GetPreferences
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class GetPreferencesTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $preferences = [];
+
+ $this->assertInstanceOf(
+ GetPreferences::class,
+ new GetPreferences( $user )
+ );
+ }
+
+ /**
+ * @dataProvider keyProvider
+ */
+ public function testProcess( $key ) {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $preferences = [];
+
+ $instance = new GetPreferences( $user );
+
+ $instance->setOptions(
+ [
+ 'smwgEnabledEditPageHelp' => false
+ ]
+ );
+
+ $instance->process( $preferences );
+
+ $this->assertArrayHasKey(
+ $key,
+ $preferences
+ );
+ }
+
+ public function keyProvider() {
+
+ $provider[] = [
+ 'smw-prefs-intro'
+ ];
+
+ $provider[] = [
+ 'smw-prefs-ask-options-tooltip-display'
+ ];
+
+ $provider[] = [
+ 'smw-prefs-general-options-time-correction'
+ ];
+
+ $provider[] = [
+ 'smw-prefs-general-options-disable-editpage-info'
+ ];
+
+ $provider[] = [
+ 'smw-prefs-general-options-jobqueue-watchlist'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookHandlerTest.php
new file mode 100644
index 00000000..38f4635c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookHandlerTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\HookHandler;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\HookHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HookHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HookHandler::class,
+ new HookHandler()
+ );
+ }
+
+ public function testOptions() {
+
+ $instance = new HookHandler();
+
+ $this->assertNull(
+ $instance->getOption( 'Foo' )
+ );
+
+ $this->assertFalse(
+ $instance->getOption( 'Foo', false )
+ );
+
+ $instance->setOptions( [ 'Foo' => 42 ] );
+
+ $this->assertEquals(
+ 42,
+ $instance->getOption( 'Foo' )
+ );
+
+ $this->assertTrue(
+ $instance->isFlagSet( 'Foo', 42 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookListenerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookListenerTest.php
new file mode 100644
index 00000000..7af84c1e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookListenerTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\HookListener;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\HookListener
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HooksTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HookListener::class,
+ new HookListener()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookRegistryTest.php
new file mode 100644
index 00000000..889d03b0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/HookRegistryTest.php
@@ -0,0 +1,1733 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Hooks\HookRegistry;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\HookRegistry
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HookRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ private $parser;
+ private $title;
+ private $outputPage;
+ private $requestContext;
+ private $skin;
+ private $store;
+ private $testEnvironment;
+
+ /**
+ * Has a static signature on purpose!
+ *
+ * @var array
+ */
+ private static $handlers = [];
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $this->outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputPage->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestContext = $this->getMockBuilder( '\RequestContext' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestContext->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->outputPage ) );
+
+ $this->requestContext->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->will( $this->returnValue( $webRequest ) );
+
+ $this->skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->skin->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->requestContext ) );
+
+ $this->skin->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->outputPage ) );
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $contentParser = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentParser->expects( $this->any() )
+ ->method( 'parse' )
+ ->will( $this->returnValue( $this->parser ) );
+
+ $deferredCallableUpdate = $this->getMockBuilder( '\SMW\DeferredCallableUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ $this->testEnvironment->registerObject( 'ContentParser', $contentParser );
+ $this->testEnvironment->registerObject( 'DeferredCallableUpdate', $deferredCallableUpdate );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $vars = [];
+
+ $this->assertInstanceOf(
+ HookRegistry::class,
+ new HookRegistry( $vars, 'foo' )
+ );
+ }
+
+ public function testInitExtension() {
+
+ $vars = [];
+
+ HookRegistry::initExtension( $vars );
+
+ // CanonicalNamespaces
+ $callback = end( $vars['wgHooks']['CanonicalNamespaces'] );
+ $namespaces = [];
+
+ $this->assertThatHookIsExcutable(
+ $callback,
+ [ &$namespaces ]
+ );
+
+ // SpecialPage_initList
+ $callback = end( $vars['wgHooks']['SpecialPage_initList'] );
+ $specialPages = [];
+
+ $this->assertThatHookIsExcutable(
+ $callback,
+ [ &$specialPages ]
+ );
+
+ // ApiMain::moduleManager
+ $callback = end( $vars['wgHooks']['ApiMain::moduleManager'] );
+
+ $apiModuleManager = $this->getMockBuilder( '\ApiModuleManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertThatHookIsExcutable(
+ $callback,
+ [ $apiModuleManager ]
+ );
+ }
+
+ /**
+ * @dataProvider callMethodProvider
+ */
+ public function testRegister( $method ) {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $vars = [
+ 'IP' => 'bar',
+ 'wgVersion' => '1.24',
+ 'wgLang' => $language,
+ 'smwgEnabledDeferredUpdate' => false
+ ];
+
+ $instance = new HookRegistry( $vars, 'foo' );
+ $instance->register();
+
+ self::$handlers[] = call_user_func_array( [ $this, $method ], [ $instance ] );
+ }
+
+ /**
+ * @depends testRegister
+ */
+ public function testCheckOnMissingHandlers() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $vars = [
+ 'IP' => 'bar',
+ 'wgVersion' => '1.24',
+ 'wgLang' => $language,
+ 'smwgEnabledDeferredUpdate' => false
+ ];
+
+ $instance = new HookRegistry( $vars, 'foo' );
+ $instance->register();
+
+ $handlerList = $instance->getHandlerList();
+ $handlers = array_flip( self::$handlers );
+
+ foreach ( $handlerList as $handler ) {
+ $this->assertArrayHasKey( $handler, $handlers, "Missing a `$handler` test!" );
+ }
+
+ self::$handlers = [];
+ }
+
+ public function callMethodProvider() {
+
+ return [
+ [ 'callParserAfterTidy' ],
+ [ 'callBaseTemplateToolbox' ],
+ [ 'callSkinAfterContent' ],
+ [ 'callOutputPageParserOutput' ],
+ [ 'callBeforePageDisplay' ],
+ [ 'callSpecialSearchResultsPrepend' ],
+ [ 'callSpecialSearchProfiles' ],
+ [ 'callSpecialSearchProfileForm' ],
+ [ 'callInternalParseBeforeLinks' ],
+ [ 'callNewRevisionFromEditComplete' ],
+ [ 'callTitleMoveComplete' ],
+ [ 'callArticleProtectComplete' ],
+ [ 'callArticleViewHeader' ],
+ [ 'callArticlePurge' ],
+ [ 'callArticleDelete' ],
+ [ 'callLinksUpdateConstructed' ],
+ [ 'callSpecialStatsAddExtra' ],
+ [ 'callFileUpload' ],
+ [ 'callResourceLoaderGetConfigVars' ],
+ [ 'callGetPreferences' ],
+ [ 'callPersonalUrls' ],
+ [ 'callSkinTemplateNavigation' ],
+ [ 'callLoadExtensionSchemaUpdates' ],
+ [ 'callResourceLoaderTestModules' ],
+ [ 'callExtensionTypes' ],
+ [ 'callTitleIsAlwaysKnown' ],
+ [ 'callBeforeDisplayNoArticleText' ],
+ [ 'callArticleFromTitle' ],
+ [ 'callTitleIsMovable' ],
+ [ 'callContentHandlerForModelID' ],
+ [ 'callEditPageForm' ],
+ [ 'callParserOptionsRegister' ],
+ [ 'callParserFirstCallInit' ],
+ [ 'callTitleQuickPermissions' ],
+ [ 'callOutputPageCheckLastModified' ],
+ [ 'callIsFileCacheable' ],
+ [ 'callRejectParserCacheValue' ],
+ [ 'callSoftwareInfo' ],
+ [ 'callBlockIpComplete' ],
+ [ 'callUnblockUserComplete' ],
+ [ 'callUserGroupsChanged' ],
+ [ 'callSMWSQLStoreEntityReferenceCleanUpComplete' ],
+ [ 'callSMWAdminTaskHandlerFactory' ],
+ [ 'callSMWSQLStorAfterDataUpdateComplete' ],
+ [ 'callSMWStoreBeforeQueryResultLookupComplete' ],
+ [ 'callSMWStoreAfterQueryResultLookupComplete' ],
+ [ 'callSMWBrowseAfterIncomingPropertiesLookupComplete' ],
+ [ 'callSMWBrowseBeforeIncomingPropertyValuesFurtherLinkCreate' ],
+ [ 'callSMWSQLStoreInstallerAfterCreateTablesComplete' ],
+ ];
+ }
+
+ public function callParserAfterTidy( $instance ) {
+
+ $handler = 'ParserAfterTidy';
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $this->parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $text = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$this->parser, &$text ]
+ );
+
+ return $handler;
+ }
+
+ public function callBaseTemplateToolbox( $instance ) {
+
+ $handler = 'BaseTemplateToolbox';
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $this->skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->any() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->skin ) );
+
+ $toolbox = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $skinTemplate, &$toolbox ]
+ );
+
+ return $handler;
+ }
+
+ public function callSkinAfterContent( $instance ) {
+
+ $handler = 'SkinAfterContent';
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $this->skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $data = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$data, $this->skin ]
+ );
+
+ return $handler;
+ }
+
+ public function callOutputPageParserOutput( $instance ) {
+
+ $handler = 'OutputPageParserOutput';
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $this->outputPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$this->outputPage, $parserOutput ]
+ );
+
+ return $handler;
+ }
+
+ public function callBeforePageDisplay( $instance ) {
+
+ $handler = 'BeforePageDisplay';
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $this->outputPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$this->outputPage, &$this->skin ]
+ );
+
+ return $handler;
+ }
+
+ public function callSpecialSearchResultsPrepend( $instance ) {
+
+ $handler = 'SpecialSearchResultsPrepend';
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $specialSearch, $this->outputPage, '' ]
+ );
+
+ return $handler;
+ }
+
+ public function callSpecialSearchProfiles( $instance ) {
+
+ $handler = 'SpecialSearchProfiles';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $profiles = [];
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$profiles ]
+ );
+
+ return $handler;
+ }
+
+ public function callSpecialSearchProfileForm( $instance ) {
+
+ $handler = 'SpecialSearchProfileForm';
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine = $this->getMockBuilder( '\SMW\MediaWiki\Search\Search' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch->expects( $this->any() )
+ ->method( 'getSearchEngine' )
+ ->will( $this->returnValue( $searchEngine ) );
+
+ $specialSearch->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $specialSearch->expects( $this->any() )
+ ->method( 'getNamespaces' )
+ ->will( $this->returnValue( [] ) );
+
+ $specialSearch->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->requestContext ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $form = '';
+ $profile = 'smw';
+ $term = '';
+ $opts = [];
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $specialSearch, &$form, $profile, $term, $opts ]
+ );
+
+ return $handler;
+ }
+
+ public function callInternalParseBeforeLinks( $instance ) {
+
+ $handler = 'InternalParseBeforeLinks';
+
+ $this->title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $text = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$parser, &$text, &$stripState ]
+ );
+
+ return $handler;
+ }
+
+ public function callNewRevisionFromEditComplete( $instance ) {
+
+ $handler = 'NewRevisionFromEditComplete';
+
+ $contentHandler = $this->getMockBuilder( '\ContentHandler' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content->expects( $this->any() )
+ ->method( 'getContentHandler' )
+ ->will( $this->returnValue( $contentHandler ) );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $content ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $baseId = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $wikiPage, $revision, $baseId, $user ]
+ );
+
+ return $handler;
+ }
+
+ public function callTitleMoveComplete( $instance ) {
+
+ $handler = 'TitleMoveComplete';
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'deleteSubject' ] )
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $oldTitle = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $oldTitle->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_SPECIAL ) );
+
+ $oldTitle->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $newTitle = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $newTitle->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_SPECIAL ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $oldId = 42;
+ $newId = 0;
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$oldTitle, &$newTitle, &$user, $oldId, $newId ]
+ );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ return $handler;
+ }
+
+ public function callArticleProtectComplete( $instance ) {
+
+ $handler = 'ArticleProtectComplete';
+
+ $contentHandler = $this->getMockBuilder( '\ContentHandler' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content->expects( $this->any() )
+ ->method( 'getContentHandler' )
+ ->will( $this->returnValue( $contentHandler ) );
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $content ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getRevision' )
+ ->will( $this->returnValue( $revision ) );
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $protections = [];
+ $reason = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$wikiPage, &$user, $protections, $reason ]
+ );
+
+ return $handler;
+ }
+
+ public function callArticleViewHeader( $instance ) {
+
+ $handler = 'ArticleViewHeader';
+
+ $page = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $outputDone = '';
+ $useParserCache = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$page, &$outputDone, &$useParserCache ]
+ );
+
+ return $handler;
+ }
+
+ public function callArticlePurge( $instance ) {
+
+ $handler = 'ArticlePurge';
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$wikiPage ]
+ );
+
+ return $handler;
+ }
+
+ public function callArticleDelete( $instance ) {
+
+ $handler = 'ArticleDelete';
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubSemanticData' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reason = '';
+ $error = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$wikiPage, &$user, &$reason, &$error ]
+ );
+
+ return $handler;
+ }
+
+ public function callLinksUpdateConstructed( $instance ) {
+
+ $handler = 'LinksUpdateConstructed';
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'updateData' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->getOptions()->set( 'smwgSemanticsEnabled', false );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_SPECIAL ) );
+
+ $linksUpdate = $this->getMockBuilder( '\LinksUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $linksUpdate->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $linksUpdate->expects( $this->any() )
+ ->method( 'getParserOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $linksUpdate ]
+ );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ return $handler;
+ }
+
+ public function callSpecialStatsAddExtra( $instance ) {
+
+ $handler = 'SpecialStatsAddExtra';
+
+ $extraStats = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$extraStats ]
+ );
+
+ return $handler;
+ }
+
+ public function callFileUpload( $instance ) {
+
+ $handler = 'FileUpload';
+
+ $file = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reupload = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $file, $reupload ]
+ );
+
+ return $handler;
+ }
+
+ public function callResourceLoaderGetConfigVars( $instance ) {
+
+ $handler = 'ResourceLoaderGetConfigVars';
+
+ $vars = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$vars ]
+ );
+
+ return $handler;
+ }
+
+ public function callGetPreferences( $instance ) {
+
+ $handler = 'GetPreferences';
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $preferences = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $user, &$preferences ]
+ );
+
+ return $handler;
+ }
+
+ public function callPersonalUrls( $instance ) {
+
+ $handler = 'PersonalUrls';
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $personal_urls = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$personal_urls, $title, $skinTemplate ]
+ );
+
+ return $handler;
+ }
+
+ public function callSkinTemplateNavigation( $instance ) {
+
+ $handler = 'SkinTemplateNavigation';
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $links = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$skinTemplate, &$links ]
+ );
+
+ return $handler;
+ }
+
+ public function callLoadExtensionSchemaUpdates( $instance ) {
+
+ $handler = 'LoadExtensionSchemaUpdates';
+
+ $databaseUpdater = $this->getMockBuilder( '\DatabaseUpdater' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $databaseUpdater ]
+ );
+
+ return $handler;
+ }
+
+ public function callResourceLoaderTestModules( $instance ) {
+
+ $handler = 'ResourceLoaderTestModules';
+
+ $resourceLoader = $this->getMockBuilder( '\ResourceLoader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $testModules = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$testModules, &$resourceLoader ]
+ );
+
+ return $handler;
+ }
+
+ public function callExtensionTypes( $instance ) {
+
+ $handler = 'ExtensionTypes';
+
+ $extTypes = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$extTypes ]
+ );
+
+ return $handler;
+ }
+
+ public function callTitleIsAlwaysKnown( $instance ) {
+
+ $handler = 'TitleIsAlwaysKnown';
+
+ $result = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $this->title, &$result ]
+ );
+
+ return $handler;
+ }
+
+ public function callBeforeDisplayNoArticleText( $instance ) {
+
+ $handler = 'BeforeDisplayNoArticleText';
+
+ $article = $this->getMockBuilder( '\Article' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $article->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $article ]
+ );
+
+ return $handler;
+ }
+
+ public function callArticleFromTitle( $instance ) {
+
+ $handler = 'ArticleFromTitle';
+
+ $article = $this->getMockBuilder( '\Article' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $article->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$this->title, &$article ]
+ );
+
+ return $handler;
+ }
+
+ public function callTitleIsMovable( $instance ) {
+
+ $handler = 'TitleIsMovable';
+
+ $isMovable = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $this->title, &$isMovable ]
+ );
+
+ return $handler;
+ }
+
+ public function callContentHandlerForModelID( $instance ) {
+
+ $handler = 'ContentHandlerForModelID';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $modelId = 'smw/schema';
+ $contentHandler = '';
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $modelId, &$contentHandler ]
+ );
+
+ return $handler;
+ }
+
+ public function callEditPageForm( $instance ) {
+
+ $handler = 'EditPage::showEditForm:initial';
+
+ $title = Title::newFromText( 'Foo' );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editPage = $this->getMockBuilder( '\EditPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $editPage, $outputPage ]
+ );
+
+ return $handler;
+ }
+
+ public function callParserOptionsRegister( $instance ) {
+
+ $handler = 'ParserOptionsRegister';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $defaults = [];
+ $inCacheKey = [];
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$defaults, &$inCacheKey ]
+ );
+
+ return $handler;
+ }
+
+ public function callParserFirstCallInit( $instance ) {
+
+ $handler = 'ParserFirstCallInit';
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$parser ]
+ );
+
+ return $handler;
+ }
+
+ public function callTitleQuickPermissions( $instance ) {
+
+ $handler = 'TitleQuickPermissions';
+
+ $title = $this->title;
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $action = '';
+ $errors = [];
+ $rigor = '';
+ $short = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $title, $user, $action, &$errors, $rigor, $short ]
+ );
+
+ return $handler;
+ }
+
+ public function callOutputPageCheckLastModified( $instance ) {
+
+ $handler = 'OutputPageCheckLastModified';
+ $modifiedTimes = [];
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$modifiedTimes ]
+ );
+
+ return $handler;
+ }
+
+ public function callIsFileCacheable( $instance ) {
+
+ $handler = 'IsFileCacheable';
+
+ $article = $this->getMockBuilder( '\Article' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $article->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$article ]
+ );
+
+ return $handler;
+ }
+
+ public function callRejectParserCacheValue( $instance ) {
+
+ $handler = 'RejectParserCacheValue';
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $value = '';
+ $popts = '';
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $value, $wikiPage, $popts ]
+ );
+
+ return $handler;
+ }
+
+ public function callSoftwareInfo( $instance ) {
+
+ $handler = 'SoftwareInfo';
+
+ $software = [];
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$software ]
+ );
+
+ return $handler;
+ }
+
+ public function callBlockIpComplete( $instance ) {
+
+ $handler = 'BlockIpComplete';
+
+ $block = $this->getMockBuilder( '\Block' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $block->expects( $this->any() )
+ ->method( 'getTarget' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $performer = '';
+ $priorBlock = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $block, $performer, $priorBlock ]
+ );
+
+ return $handler;
+ }
+
+ public function callUnblockUserComplete( $instance ) {
+
+ $handler = 'UnblockUserComplete';
+
+ $block = $this->getMockBuilder( '\Block' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $block->expects( $this->any() )
+ ->method( 'getTarget' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $performer = '';
+ $priorBlock = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $block, $performer, $priorBlock ]
+ );
+
+ return $handler;
+ }
+
+ public function callUserGroupsChanged( $instance ) {
+
+ $handler = 'UserGroupsChanged';
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->any() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $user ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWStoreDropTables( $instance ) {
+
+ $handler = 'SMW::Store::dropTables';
+
+ $verbose = false;
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $verbose ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWSQLStoreEntityReferenceCleanUpComplete( $instance ) {
+
+ $handler = 'SMW::SQLStore::EntityReferenceCleanUpComplete';
+
+ if ( !$instance->getHandlerFor( $handler ) ) {
+ return $this->markTestSkipped( "$handler not used" );
+ }
+
+ $id = 42;
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+ $isRedirect = false;
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $this->store, $id, $subject, $isRedirect ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWAdminTaskHandlerFactory( $instance ) {
+
+ $handler = 'SMW::Admin::TaskHandlerFactory';
+
+ if ( !$instance->getHandlerFor( $handler ) ) {
+ return $this->markTestSkipped( "$handler not used" );
+ }
+
+ $taskHandlers = [];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( 'User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ &$taskHandlers, $store, $outputFormatter, $user ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWSQLStorAfterDataUpdateComplete( $instance ) {
+
+ $handler = 'SMW::SQLStore::AfterDataUpdateComplete';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getFixedPropertyRecords' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $store, $semanticData, $changeOp ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWStoreBeforeQueryResultLookupComplete( $instance ) {
+
+ $handler = 'SMW::Store::BeforeQueryResultLookupComplete';
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryEngine = $this->getMockBuilder( '\SMW\QueryEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $this->store, $query, &$result, $queryEngine ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWStoreAfterQueryResultLookupComplete( $instance ) {
+
+ $handler = 'SMW::Store::AfterQueryResultLookupComplete';
+
+ $idTableLookup = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'warmUpCache' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getPropertyValues' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTableLookup ) );
+
+ $result = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $store, &$result ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWBrowseAfterIncomingPropertiesLookupComplete( $instance ) {
+
+ $handler = 'SMW::Browse::AfterIncomingPropertiesLookupComplete';
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists', 'getId' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getPropertyValues' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestOptions->expects( $this->any() )
+ ->method( 'getExtraConditions' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $store, $semanticData, $requestOptions ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWBrowseBeforeIncomingPropertyValuesFurtherLinkCreate( $instance ) {
+
+ $handler = 'SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate';
+
+ $html = '';
+ $property = new DIProperty( 'Foo' );
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $property, $subject, &$html, $this->store ]
+ );
+
+ return $handler;
+ }
+
+ public function callSMWSQLStoreInstallerAfterCreateTablesComplete( $instance ) {
+
+ $handler = 'SMW::SQLStore::Installer::AfterCreateTablesComplete';
+
+ $result = '';
+
+ $this->assertTrue(
+ $instance->isRegistered( $handler )
+ );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageReporter = $this->getMockBuilder( '\Onoi\MessageReporter\MessageReporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $options = $this->getMockBuilder( '\SMW\Options' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertThatHookIsExcutable(
+ $instance->getHandlerFor( $handler ),
+ [ $tableBuilder, $messageReporter, $options ]
+ );
+
+ return $handler;
+ }
+
+ private function assertThatHookIsExcutable( callable $handler, $arguments ) {
+ $this->assertInternalType(
+ 'boolean',
+ call_user_func_array( $handler, $arguments )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/InternalParseBeforeLinksTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/InternalParseBeforeLinksTest.php
new file mode 100644
index 00000000..7665b5ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/InternalParseBeforeLinksTest.php
@@ -0,0 +1,459 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Hooks\InternalParseBeforeLinks;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\InternalParseBeforeLinks
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InternalParseBeforeLinksTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $parserFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->parserFactory = $this->testEnvironment->getUtilityFactory()->newParserFactory();
+
+ $this->stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\InternalParseBeforeLinks',
+ new InternalParseBeforeLinks( $parser, $this->stripState )
+ );
+ }
+
+ public function testNonProcessForEmptyText() {
+
+ $text = '';
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->once() )
+ ->method( 'getOptions' );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+ }
+
+ public function testDisableProcessOfInterfaceMessageOnNonSpecialPage() {
+
+ $text = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( false ) );
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOptions->expects( $this->once() )
+ ->method( 'getInterfaceMessage' )
+ ->will( $this->returnValue( true ) );
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $parserOptions ) );
+
+ $parser->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+ }
+
+ public function testProcessOfInterfaceMessageOnEnabledSpecialPage() {
+
+ $text = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecial' )
+ ->with( $this->equalTo( 'Bar' ) )
+ ->will( $this->returnValue( true ) );
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOptions->expects( $this->once() )
+ ->method( 'getInterfaceMessage' )
+ ->will( $this->returnValue( true ) );
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $parserOptions ) );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgEnabledSpecialPage' => [ 'Bar' ]
+ ]
+ );
+
+ $instance->process( $text );
+ }
+
+ public function testProcessOfInterfaceMessageOnSpecialPageWithOnOffMarker() {
+
+ $text = '[[SMW::off]]Foo[[SMW::on]]';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecial' )
+ ->with( $this->equalTo( 'Bar' ) )
+ ->will( $this->returnValue( true ) );
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $instance->process( $text );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testProcess( $title ) {
+
+ $text = 'Foo';
+ $parser = $this->parserFactory->newFromTitle( $title );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ public function testTextChangeWithParserOuputUpdateIntegration( $parameters, $expected ) {
+
+ $this->testEnvironment->withConfiguration(
+ $parameters['settings']
+ );
+
+ $text = $parameters['text'];
+ $parser = $this->parserFactory->newFromTitle( $parameters['title'] );
+
+ $instance = new InternalParseBeforeLinks(
+ $parser,
+ $this->stripState
+ );
+
+ $smwgEnabledSpecialPage = isset( $parameters['settings']['smwgEnabledSpecialPage'] ) ? $parameters['settings']['smwgEnabledSpecialPage'] : [];
+
+ $instance->setOptions(
+ [
+ 'smwgEnabledSpecialPage' => $smwgEnabledSpecialPage
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+
+ $this->assertEquals(
+ $expected['resultText'],
+ $text
+ );
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $parser->getTitle(),
+ $parser->getOutput()
+ );
+
+ $this->assertEquals(
+ $expected['propertyCount'] > 0,
+ $parserData->hasSemanticData( $parser->getOutput() )
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function titleProvider() {
+
+ #0
+ $provider[] = [ Title::newFromText( __METHOD__ ) ];
+
+ $title = MockTitle::buildMockForMainNamespace();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ #1
+ $provider[] = [ $title ];
+
+ $title = MockTitle::buildMockForMainNamespace();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+
+ #2
+ $provider[] = [ $title ];
+
+ $title = MockTitle::buildMockForMainNamespace();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ #3
+ $provider[] = [ $title ];
+
+ return $provider;
+ }
+
+ public function textDataProvider() {
+
+ $provider = [];
+
+ // #0 NS_MAIN; [[FooBar...]] with a different caption
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'settings' => [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT
+ ],
+ 'text' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ ],
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% [[:Dictumst|寒ã„]]' .
+ ' [[:Tincidunt semper|tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[:9001|9001]] et Donec.',
+ 'propertyCount' => 3,
+ 'propertyLabels' => [ 'Foo', 'Bar', 'FooBar' ],
+ 'propertyValues' => [ 'Dictumst', 'Tincidunt semper', '9001' ]
+ ]
+ ];
+
+ // #1
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'settings' => [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT
+ ],
+ 'text' => '#REDIRECT [[Foo]]',
+ ],
+ [
+ 'resultText' => '#REDIRECT [[Foo]]',
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_REDI' ],
+ 'propertyValues' => [ 'Foo' ]
+ ]
+ ];
+
+ // #2 NS_SPECIAL, processed but no annotations
+ $title = Title::newFromText( 'Ask', NS_SPECIAL );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'settings' => [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT,
+ 'smwgEnabledSpecialPage' => [ 'Ask', 'Foo' ]
+ ],
+ 'text' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ ],
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% [[:Dictumst|寒ã„]]' .
+ ' [[:Tincidunt semper|tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[:9001|9001]] et Donec.',
+ 'propertyCount' => 0
+ ]
+ ];
+
+ // #3 NS_SPECIAL, not processed, Title::isSpecial returns false
+ $title = Title::newFromText( 'Foo', NS_SPECIAL );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'settings' => [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT,
+ 'smwgEnabledSpecialPage' => [ 'Ask', 'Foo' ]
+ ],
+ 'text' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ ],
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ 'propertyCount' => 0
+ ]
+ ];
+
+ // #4 NS_SPECIAL, not processed, invalid smwgEnabledSpecialPage setting
+ $title = Title::newFromText( 'Foobar', NS_SPECIAL );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'settings' => [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_STRICT,
+ 'smwgEnabledSpecialPage' => []
+ ],
+ 'text' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ ],
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% [[FooBar::dictumst|寒ã„]]' .
+ ' [[Bar::tincidunt semper]] facilisi {{volutpat}} Ut quis' .
+ ' [[foo::9001]] et Donec.',
+ 'propertyCount' => 0
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/LinksUpdateConstructedTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/LinksUpdateConstructedTest.php
new file mode 100644
index 00000000..3e1e749a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/LinksUpdateConstructedTest.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace SMW\Test\MediaWiki\Hooks;
+
+use LinksUpdate;
+use ParserOutput;
+use SMW\MediaWiki\Hooks\LinksUpdateConstructed;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\LinksUpdateConstructed
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class LinksUpdateConstructedTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $namespaceExaminer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ LinksUpdateConstructed::class,
+ new LinksUpdateConstructed( $this->namespaceExaminer )
+ );
+ }
+
+ public function testProcess() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 11001 ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getPrefixedText' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( false ) );
+
+ $parserOutput = new ParserOutput();
+ $parserOutput->setTitleText( $title->getPrefixedText() );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $idTable->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'clearData', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'clearData' );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new LinksUpdateConstructed(
+ $this->namespaceExaminer
+ );
+
+ $instance->setLogger( $this->testEnvironment->newSpyLogger() );
+ $instance->disableDeferredUpdate();
+
+ $this->assertTrue(
+ $instance->process( new LinksUpdate( $title, $parserOutput ) )
+ );
+ }
+
+ public function testNoExtraParsingForNotEnabledNamespace() {
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgNamespacesWithSemanticLinks',
+ [ NS_HELP => false ]
+ );
+
+ $title = Title::newFromText( __METHOD__, NS_HELP );
+ $parserOutput = new ParserOutput();
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserData->expects( $this->never() )
+ ->method( 'getSemanticData' );
+
+ $parserData->expects( $this->once() )
+ ->method( 'updateStore' );
+
+ $this->testEnvironment->registerObject( 'ParserData', $parserData );
+
+ $linksUpdate = $this->getMockBuilder( '\LinksUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $linksUpdate->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $linksUpdate->expects( $this->atLeastOnce() )
+ ->method( 'getParserOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new LinksUpdateConstructed(
+ $this->namespaceExaminer
+ );
+
+ $this->assertTrue(
+ $instance->process( $linksUpdate )
+ );
+ }
+
+ public function testTemplateUpdate() {
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgNamespacesWithSemanticLinks',
+ [ NS_HELP => false ]
+ );
+
+ $title = Title::newFromText( __METHOD__, NS_HELP );
+ $parserOutput = new ParserOutput();
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSemanticData', 'updateStore', 'markUpdate' ] )
+ ->getMock();
+
+ $parserData->expects( $this->never() )
+ ->method( 'getSemanticData' );
+
+ $parserData->expects( $this->once() )
+ ->method( 'updateStore' );
+
+ $this->testEnvironment->registerObject( 'ParserData', $parserData );
+
+ $linksUpdate = $this->getMockBuilder( '\LinksUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $linksUpdate->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $linksUpdate->expects( $this->atLeastOnce() )
+ ->method( 'getParserOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $linksUpdate->mTemplates = [ 'Foo' ];
+ $linksUpdate->mRecursive = false;
+
+ $instance = new LinksUpdateConstructed(
+ $this->namespaceExaminer
+ );
+
+ $instance->process( $linksUpdate );
+
+ $this->assertTrue(
+ $parserData->getOption( $parserData::OPT_FORCED_UPDATE )
+ );
+ }
+
+ public function testIsReadOnly_DoNothing() {
+
+ $linksUpdate = $this->getMockBuilder( '\LinksUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $linksUpdate->expects( $this->never() )
+ ->method( 'getTitle' );
+
+ $instance = new LinksUpdateConstructed(
+ $this->namespaceExaminer
+ );
+
+ $instance->isReadOnly( true );
+
+ $this->assertFalse(
+ $instance->process( $linksUpdate )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/NewRevisionFromEditCompleteTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/NewRevisionFromEditCompleteTest.php
new file mode 100644
index 00000000..7e1a89c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/NewRevisionFromEditCompleteTest.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use ParserOutput;
+use Revision;
+use SMW\DIProperty;
+use SMW\MediaWiki\Hooks\NewRevisionFromEditComplete;
+use SMW\Tests\TestEnvironment;
+use Title;
+use WikiPage;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\NewRevisionFromEditComplete
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NewRevisionFromEditCompleteTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\PageInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ NewRevisionFromEditComplete::class,
+ new NewRevisionFromEditComplete( $title, $editInfoProvider, $pageInfoProvider )
+ );
+ }
+
+ /**
+ * @dataProvider wikiPageDataProvider
+ */
+ public function testProcess( $settings, $title, $editInfoProvider, $pageInfoProvider, $expected ) {
+
+ $this->testEnvironment->withConfiguration( $settings );
+
+ $instance = new NewRevisionFromEditComplete(
+ $title,
+ $editInfoProvider,
+ $pageInfoProvider
+ );
+
+ $this->assertTrue(
+ $instance->process()
+ );
+
+ if ( $expected ) {
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $editInfoProvider->fetchSemanticData()
+ );
+ }
+ }
+
+ public function wikiPageDataProvider() {
+
+ #0 No parserOutput object
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getOutput' ] )
+ ->getMock();
+
+ $pageInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\PageInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ [],
+ $title,
+ $editInfoProvider,
+ $pageInfoProvider,
+ false
+ ];
+
+ #1 With annotation
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getOutput' ] )
+ ->getMock();
+
+ $editInfoProvider->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( new ParserOutput() ) );
+
+ $pageInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\PageInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageInfoProvider->expects( $this->atLeastOnce() )
+ ->method( 'getModificationDate' )
+ ->will( $this->returnValue( 1272508903 ) );
+
+ $provider[] = [
+ [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MODIFICATION_DATE ],
+ 'smwgDVFeatures' => ''
+ ],
+ $title,
+ $editInfoProvider,
+ $pageInfoProvider,
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_MDAT',
+ 'propertyValues' => [ '2010-04-29T02:41:43' ],
+ ]
+ ];
+
+ #2 on schema page
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo_schema' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_SCHEMA ) );
+
+ $editInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\EditInfoProvider' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getOutput' ] )
+ ->getMock();
+
+ $editInfoProvider->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( new ParserOutput() ) );
+
+ $pageInfoProvider = $this->getMockBuilder( '\SMW\MediaWiki\PageInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $data = json_encode(
+ [ 'description' => 'Foobar', 'type' => 'FOO_ROLE' ]
+ );
+
+ $pageInfoProvider->expects( $this->atLeastOnce() )
+ ->method( 'getNativeData' )
+ ->will( $this->returnValue( $data ) );
+
+ $provider[] =[
+ [
+ 'smwgPageSpecialProperties' => [],
+ 'smwgDVFeatures' => '',
+ 'smwgSchemaTypes' => [ 'FOO_ROLE' => [] ]
+ ],
+ $title,
+ $editInfoProvider,
+ $pageInfoProvider,
+ [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_SCHEMA_DESC', '_SCHEMA_TYPE', '_SCHEMA_DEF' ],
+ 'propertyValues' => [ 'Foobar', 'FOO_ROLE', '{"description":"Foobar","type":"FOO_ROLE"}' ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/OutputPageParserOutputTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/OutputPageParserOutputTest.php
new file mode 100644
index 00000000..7f34cfc3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/OutputPageParserOutputTest.php
@@ -0,0 +1,360 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use Language;
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Hooks\OutputPageParserOutput;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\OutputPageParserOutput
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class OutputPageParserOutputTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $this->testEnvironment->withConfiguration(
+ [
+ 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY,
+ 'smwgFactboxUseCache' => true,
+ 'smwgMainCacheType' => 'hash'
+ ]
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\OutputPageParserOutput',
+ new OutputPageParserOutput( $outputPage, $parserOutput )
+ );
+ }
+
+ /**
+ * @dataProvider outputDataProvider
+ */
+ public function testProcess( $parameters, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgNamespacesWithSemanticLinks',
+ $parameters['smwgNamespacesWithSemanticLinks']
+ );
+
+ $outputPage = $parameters['outputPage'];
+ $parserOutput = $parameters['parserOutput'];
+
+ $instance = new OutputPageParserOutput( $outputPage, $parserOutput );
+
+ $cachedFactbox = $this->applicationFactory->create( 'FactboxFactory' )->newCachedFactbox();
+
+ $factboxFactory = $this->getMockBuilder( '\SMW\Factbox\FactboxFactory' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'newCachedFactbox' ] )
+ ->getMock();
+
+ $factboxFactory->expects( $this->any() )
+ ->method( 'newCachedFactbox' )
+ ->will( $this->returnValue( $cachedFactbox ) );
+
+ $this->applicationFactory->registerObject( 'FactboxFactory', $factboxFactory );
+
+ $this->assertEmpty(
+ $cachedFactbox->retrieveContent( $outputPage )
+ );
+
+ $instance->process();
+
+ if ( $expected['text'] == '' ) {
+ return $this->assertFalse( isset( $outputPage->mSMWFactboxText ) );
+ }
+
+ // For expected content continue to verify that the outputPage was amended and
+ // that the content is also available via the CacheStore
+ $text = $outputPage->mSMWFactboxText;
+
+ $this->assertContains( $expected['text'], $text );
+
+ $this->assertEquals(
+ $text,
+ $cachedFactbox->retrieveContent( $outputPage ),
+ 'Asserts that retrieveContent() returns an expected text'
+ );
+
+ // Deliberately clear the outputPage Property to retrieve
+ // content from the CacheStore
+ unset( $outputPage->mSMWFactboxText );
+
+ $this->assertEquals(
+ $text,
+ $cachedFactbox->retrieveContent( $outputPage ),
+ 'Asserts that retrieveContent() is returning text from cache'
+ );
+ }
+
+ public function outputDataProvider() {
+
+ $language = Language::factory( 'en' );
+
+ $title = MockTitle::buildMockForMainNamespace( __METHOD__ . 'mock-subject' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $subject = DIWikiPage::newFromTitle( $title );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'hasVisibleProperties' )
+ ->will( $this->returnValue( true ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $title ) ] ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ new DIProperty( __METHOD__ . 'property' ) ] ) );
+
+ #0 Simple factbox build, returning content
+ $title = MockTitle::buildMock( __METHOD__ . 'title-with-content' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 9098 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'outputPage' => $outputPage,
+ 'parserOutput' => $this->makeParserOutput( $semanticData ),
+ ],
+ [
+ 'text' => $subject->getDBKey()
+ ]
+ ];
+
+ #1 Disabled namespace, no return value expected
+ $title = MockTitle::buildMock( __METHOD__ . 'title-ns-disabled' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 90000 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => false ],
+ 'outputPage' => $outputPage,
+ 'parserOutput' => $this->makeParserOutput( $semanticData ),
+ ],
+ [
+ 'text' => ''
+ ]
+ ];
+
+ // #2 Specialpage, no return value expected
+ $title = MockTitle::buildMock( __METHOD__ . 'mock-specialpage' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'outputPage' => $outputPage,
+ 'parserOutput' => $this->makeParserOutput( $semanticData ),
+ ],
+ [
+ 'text' => ''
+ ]
+ ];
+
+ // #3 Redirect, no return value expected
+ $title = MockTitle::buildMock( __METHOD__ . 'mock-redirect' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'outputPage' => $outputPage,
+ 'parserOutput' => $this->makeParserOutput( $semanticData ),
+ ],
+ [
+ 'text' => ''
+ ]
+ ];
+
+ // #4 Oldid
+ $title = MockTitle::buildMockForMainNamespace( __METHOD__ . 'mock-oldid' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $context = new \RequestContext( );
+ $context->setRequest( new \FauxRequest( [ 'oldid' => 9001 ], true ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $provider[] = [
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'outputPage' => $outputPage,
+ 'parserOutput' => $this->makeParserOutput( $semanticData ),
+ ],
+ [
+ 'text' => $subject->getDBKey()
+ ]
+ ];
+
+ return $provider;
+ }
+
+ protected function makeParserOutput( $data ) {
+
+ $parserOutput = new ParserOutput();
+
+ if ( method_exists( $parserOutput, 'setExtensionData' ) ) {
+ $parserOutput->setExtensionData( 'smwdata', $data );
+ } else {
+ $parserOutput->mSMWData = $data;
+ }
+
+ return $parserOutput;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ParserAfterTidyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ParserAfterTidyTest.php
new file mode 100644
index 00000000..b4ac0c21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ParserAfterTidyTest.php
@@ -0,0 +1,412 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\DataItemFactory;
+use SMW\MediaWiki\Hooks\ParserAfterTidy;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ParserAfterTidy
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserAfterTidyTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $applicationFactory;
+ private $parserFactory;
+ private $spyLogger;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $settings = [
+ 'smwgChangePropagationWatchlist' => [],
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false
+ ];
+
+ $this->testEnvironment = new TestEnvironment( $settings );
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->spyLogger = $this->testEnvironment->getUtilityFactory()->newSpyLogger();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->parserFactory = $this->testEnvironment->getUtilityFactory()->newParserFactory();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\ParserAfterTidy',
+ new ParserAfterTidy( $parser )
+ );
+ }
+
+ public function testIsReadOnly() {
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->never() )
+ ->method( 'getTitle' );
+
+ $instance = new ParserAfterTidy( $parser );
+ $instance->isReadOnly( true );
+
+ $text = '';
+ $instance->process( $text );
+ }
+
+ public function testNotEnabledNamespace() {
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $namespaceExaminer->expects( $this->once() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( false ) );
+
+ $this->testEnvironment->registerObject( 'NamespaceExaminer', $namespaceExaminer );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // Using this step to verify that the previous NS check
+ // bailed out.
+ $title->expects( $this->never() )
+ ->method( 'isSpecialPage' );
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new ParserAfterTidy( $parser );
+
+ $text = '';
+ $instance->process( $text );
+ }
+
+ private function newMockCache( $id, $containsStatus, $fetchStatus ) {
+
+ $key = $this->applicationFactory->newCacheFactory()->getPurgeCacheKey( $id );
+
+ $cache = $this->getMockBuilder( 'Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->any() )
+ ->method( 'contains' )
+ ->with( $this->equalTo( $key ) )
+ ->will( $this->returnValue( $containsStatus ) );
+
+ $cache->expects( $this->any() )
+ ->method( 'fetch' )
+ ->with( $this->equalTo( $key ) )
+ ->will( $this->returnValue( $fetchStatus ) );
+
+ return $cache;
+ }
+
+ /**
+ * @dataProvider titleDataProvider
+ */
+ public function testProcess( $parameters ) {
+
+ $this->testEnvironment->registerObject( 'Store', $parameters['store'] );
+
+ $cache = $this->newMockCache(
+ $parameters['title']->getArticleID(),
+ $parameters['cache-contains'],
+ $parameters['cache-fetch']
+ );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $parser = $this->parserFactory->newFromTitle( $parameters['title'] );
+
+ $parser->getOutput()->setProperty(
+ 'smw-semanticdata-status',
+ $parameters['data-status']
+ );
+
+ $parser->getOutput()->setProperty(
+ 'displaytitle',
+ isset( $parameters['displaytitle'] ) ? $parameters['displaytitle'] : false
+ );
+
+ $text = '';
+
+ $instance = new ParserAfterTidy( $parser );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+ }
+
+ public function testSemanticDataParserOuputUpdateIntegration() {
+
+ $settings = [
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false,
+ 'smwgParserFeatures' => SMW_PARSER_HID_CATS,
+ 'smwgCategoryFeatures' => SMW_CAT_REDIRECT | SMW_CAT_INSTANCE
+ ];
+
+ $this->testEnvironment->withConfiguration( $settings );
+
+ $text = '';
+ $title = Title::newFromText( __METHOD__ );
+
+ $parser = $this->parserFactory->newFromTitle( $title );
+
+ $parser->getOutput()->addCategory( 'Foo', 'Foo' );
+ $parser->getOutput()->addCategory( 'Bar', 'Bar' );
+ $parser->getOutput()->setProperty( 'smw-semanticdata-status', true );
+
+ $instance = new ParserAfterTidy( $parser );
+
+ $this->assertTrue(
+ $instance->process( $text )
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_INST', '_SKEY' ],
+ 'propertyValues' => [ 'Foo', 'Bar', $title->getText() ],
+ ];
+
+ $parserData = $this->applicationFactory->newParserData(
+ $title,
+ $parser->getOutput()
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function titleDataProvider() {
+
+ #0 Runs store update
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'updateData' );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'inNamespace' )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 5001 ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => true,
+ 'cache-fetch' => true,
+ 'data-status' => true
+ ]
+ ];
+
+ #1 No cache entry, no store update
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'updateData' );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'inNamespace' )
+ ->will( $this->returnValue( false ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => false,
+ 'cache-fetch' => false,
+ 'data-status' => true
+ ]
+ ];
+
+ #2 SpecialPage, no store update
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'updateData' );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => false,
+ 'cache-fetch' => false,
+ 'data-status' => true
+ ]
+ ];
+
+ #3 NS_FILE, no store update
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'updateData' );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->any() )
+ ->method( 'inNamespace' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_FILE ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => true,
+ 'cache-fetch' => true,
+ 'data-status' => true
+ ]
+ ];
+
+ #4, 1131, No store update when fetch return FALSE
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'updateData' );
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'inNamespace' )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 5001 ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => true,
+ 'cache-fetch' => false,
+ 'data-status' => true
+ ]
+ ];
+
+ #5, 1410 displaytitle
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $title = MockTitle::buildMock( __METHOD__ );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 5001 ) );
+
+ $provider[] = [
+ [
+ 'store' => $store,
+ 'title' => $title,
+ 'cache-contains' => true,
+ 'cache-fetch' => true,
+ 'data-status' => false,
+ 'displaytitle' => 'Foo'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/PersonalUrlsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/PersonalUrlsTest.php
new file mode 100644
index 00000000..20f59cbe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/PersonalUrlsTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\PersonalUrls;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\PersonalUrls
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PersonalUrlsTest extends \PHPUnit_Framework_TestCase {
+
+ private $skinTemplate;
+ private $jobQueue;
+
+ protected function setUp() {
+
+ $this->skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PersonalUrls::class,
+ new PersonalUrls( $this->skinTemplate, $this->jobQueue )
+ );
+ }
+
+ public function testProcessOnJobQueueWatchlist() {
+
+ $output = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->skinTemplate->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $output ) );
+
+ $personalUrls = [];
+
+ $instance = new PersonalUrls(
+ $this->skinTemplate,
+ $this->jobQueue
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgJobQueueWatchlist' => [ 'Foo' ],
+ 'prefs-jobqueue-watchlist' => true
+ ]
+ );
+
+ $instance->process( $personalUrls );
+
+ $this->assertArrayHasKey(
+ 'smw-jobqueue-watchlist',
+ $personalUrls
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/RejectParserCacheValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/RejectParserCacheValueTest.php
new file mode 100644
index 00000000..7580a2cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/RejectParserCacheValueTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Hooks\RejectParserCacheValue;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\RejectParserCacheValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RejectParserCacheValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dependencyLinksUpdateJournal;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->dependencyLinksUpdateJournal = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RejectParserCacheValue::class,
+ new RejectParserCacheValue( $this->dependencyLinksUpdateJournal )
+ );
+ }
+
+ public function testProcessOnJournalEntryToReject() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $this->dependencyLinksUpdateJournal->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $this->dependencyLinksUpdateJournal->expects( $this->once() )
+ ->method( 'delete' );
+
+ $instance = new RejectParserCacheValue(
+ $this->dependencyLinksUpdateJournal
+ );
+
+ $this->assertFalse(
+ $instance->process( $subject->getTitle() )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderGetConfigVarsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderGetConfigVarsTest.php
new file mode 100644
index 00000000..41a750ac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderGetConfigVarsTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\ResourceLoaderGetConfigVars;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ResourceLoaderGetConfigVars
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ResourceLoaderGetConfigVarsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ResourceLoaderGetConfigVars::class,
+ new ResourceLoaderGetConfigVars()
+ );
+ }
+
+ public function testProcess() {
+
+ $vars = [];
+
+ $instance = new ResourceLoaderGetConfigVars();
+ $instance->process( $vars );
+
+ $this->assertArrayHasKey(
+ 'smw-config',
+ $vars
+ );
+
+ $this->assertArrayHasKey(
+ 'namespaces',
+ $vars['smw-config']
+ );
+
+ $this->assertArrayHasKey(
+ 'settings',
+ $vars['smw-config']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderTestModulesTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderTestModulesTest.php
new file mode 100644
index 00000000..dea78b79
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/ResourceLoaderTestModulesTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\ResourceLoaderTestModules;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\ResourceLoaderTestModules
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ResourceLoaderTestModulesTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $resourceLoader = $this->getMockBuilder( '\ResourceLoader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ResourceLoaderTestModules::class,
+ new ResourceLoaderTestModules( $resourceLoader )
+ );
+ }
+
+ public function testProcess() {
+
+ $resourceLoader = $this->getMockBuilder( '\ResourceLoader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $testModules = [];
+
+ $instance = new ResourceLoaderTestModules( $resourceLoader );
+ $instance->process( $testModules );
+
+ $this->assertArrayHasKey(
+ 'ext.smw.tests',
+ $testModules['qunit']
+ );
+
+ $this->assertArrayHasKey(
+ 'localBasePath',
+ $testModules['qunit']['ext.smw.tests']
+ );
+
+ $this->assertArrayHasKey(
+ 'remoteExtPath',
+ $testModules['qunit']['ext.smw.tests']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinAfterContentTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinAfterContentTest.php
new file mode 100644
index 00000000..eda42991
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinAfterContentTest.php
@@ -0,0 +1,357 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Hooks\SkinAfterContent;
+use SMW\Settings;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\SkinAfterContent
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SkinAfterContentTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $settings = Settings::newFromArray( [
+ 'smwgFactboxFeatures' => SMW_FACTBOX_CACHE,
+ 'smwgMainCacheType' => 'hash',
+ 'smwgSemanticsEnabled' => true
+ ] );
+
+ $this->applicationFactory->registerObject( 'Settings', $settings );
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\SkinAfterContent',
+ new SkinAfterContent( $skin )
+ );
+ }
+
+ public function testTryToPerformUpdateOnNullSkin() {
+
+ $data = '';
+ $instance = new SkinAfterContent( null );
+
+ $this->assertTrue(
+ $instance->performUpdate( $data )
+ );
+ }
+
+ /**
+ * @dataProvider outputDataProvider
+ */
+ public function testperformUpdateFactboxPresenterIntegration( $parameters, $expected ) {
+
+ $data = '';
+
+ $instance = new SkinAfterContent( $parameters['skin'] );
+
+ // Replace CachedFactbox instance
+ if ( isset( $parameters['title'] ) ) {
+
+ $cachedFactbox = $this->applicationFactory->create( 'FactboxFactory' )->newCachedFactbox();
+
+ $cachedFactbox->addContentToCache(
+ $cachedFactbox->makeCacheKey( $parameters['title'] ),
+ $parameters['text']
+ );
+
+ $factboxFactory = $this->getMockBuilder( '\SMW\Factbox\FactboxFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $factboxFactory->expects( $this->once() )
+ ->method( 'newCachedFactbox' )
+ ->will( $this->returnValue( $cachedFactbox ) );
+
+ $this->applicationFactory->registerObject( 'FactboxFactory', $factboxFactory );
+ }
+
+ $this->assertTrue(
+ $instance->performUpdate( $data )
+ );
+
+ $this->assertEquals(
+ $expected['text'],
+ $data
+ );
+ }
+
+ public function outputDataProvider() {
+
+ $text = __METHOD__ . 'text-0';
+
+ #0 Retrieve content from outputPage property
+ $title = MockTitle::buildMock( __METHOD__ . 'from-property' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10001 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->mSMWFactboxText = $text;
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( null ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $provider[] = [
+ [ 'skin' => $skin ],
+ [ 'text' => $text ]
+ ];
+
+ #1 Retrieve content from cache
+ $title = MockTitle::buildMock( __METHOD__ . 'from-cache' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10002 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $text = __METHOD__ . 'text-1';
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $provider[] = [
+ [ 'skin' => $skin, 'text' => $text, 'title' => $outputPage->getTitle() ],
+ [ 'text' => $text ]
+ ];
+
+ // #2 Special page
+ $text = __METHOD__ . 'text-2';
+
+ $title = MockTitle::buildMock( __METHOD__ . 'specialpage' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( true ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->mSMWFactboxText = $text;
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( new \RequestContext() ) );
+
+ $provider[] = [
+ [ 'skin' => $skin, 'text' => $text ],
+ [ 'text' => '' ]
+ ];
+
+ // #3 "edit" request
+ $text = __METHOD__ . 'text-3';
+
+ $title = MockTitle::buildMock( __METHOD__ . 'edit-request' );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( 10003 ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $outputPage->mSMWFactboxText = $text;
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $context = new \RequestContext( );
+ $context->setRequest( new \FauxRequest( [ 'action' => 'edit' ], true ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $provider[] = [
+ [ 'skin' => $skin, 'text' => $text ],
+ [ 'text' => $text ]
+ ];
+
+ // #4 "delete" request
+ $text = __METHOD__ . 'text-4';
+
+ $title = MockTitle::buildMock( __METHOD__ . 'delete-request' );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $context = new \RequestContext( );
+ $context->setRequest( new \FauxRequest( [ 'action' => 'delete' ], true ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $provider[] = [
+ [ 'skin' => $skin, 'text' => $text ],
+ [ 'text' => '' ]
+ ];
+
+ // #5 "purge" request
+ $text = __METHOD__ . 'text-purge';
+
+ $title = MockTitle::buildMock( __METHOD__ . 'purge-request' );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skin->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $skin->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $outputPage ) );
+
+ $context = new \RequestContext( );
+ $context->setRequest( new \FauxRequest( [ 'action' => 'purge' ], true ) );
+
+ $skin->expects( $this->atLeastOnce() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $context ) );
+
+ $provider[] = [
+ [ 'skin' => $skin, 'text' => $text ],
+ [ 'text' => '' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinTemplateNavigationTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinTemplateNavigationTest.php
new file mode 100644
index 00000000..f6f9547d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SkinTemplateNavigationTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\SkinTemplateNavigation;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\SkinTemplateNavigation
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SkinTemplateNavigationTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $links = [];
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\SkinTemplateNavigation',
+ new SkinTemplateNavigation( $skinTemplate, $links )
+ );
+ }
+
+ public function testProcess() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->atLeastOnce() )
+ ->method( 'isAllowed' )
+ ->will( $this->returnValue( true ) );
+
+ $output = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate = $this->getMockBuilder( '\SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $output ) );
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'msg' )
+ ->will( $this->returnValue( $message ) );
+
+ $skinTemplate->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $links = [];
+
+ $instance = new SkinTemplateNavigation( $skinTemplate, $links );
+ $instance->process();
+
+ $this->assertArrayHasKey( 'purge', $links['actions'] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialSearchResultsPrependTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialSearchResultsPrependTest.php
new file mode 100644
index 00000000..fa7d4aa3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialSearchResultsPrependTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\SpecialSearchResultsPrepend;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\SpecialSearchResultsPrepend
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SpecialSearchResultsPrependTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ SpecialSearchResultsPrepend::class,
+ new SpecialSearchResultsPrepend( $specialSearch, $outputPage )
+ );
+ }
+
+ public function testProcess() {
+
+ $search = $this->getMockBuilder( '\SMWSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch->expects( $this->atLeastOnce() )
+ ->method( 'getSearchEngine' )
+ ->will( $this->returnValue( $search ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $instance = new SpecialSearchResultsPrepend(
+ $specialSearch,
+ $outputPage
+ );
+
+ $instance->setOptions(
+ [
+ 'prefs-suggester-textinput' => true,
+ 'prefs-disable-search-info' => null
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( '' )
+ );
+ }
+
+ public function testProcess_DisabledInfo() {
+
+ $search = $this->getMockBuilder( '\SMWSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch->expects( $this->atLeastOnce() )
+ ->method( 'getSearchEngine' )
+ ->will( $this->returnValue( $search ) );
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->never() )
+ ->method( 'addHtml' );
+
+ $instance = new SpecialSearchResultsPrepend(
+ $specialSearch,
+ $outputPage
+ );
+
+ $instance->setOptions(
+ [
+ 'prefs-suggester-textinput' => true,
+ 'prefs-disable-search-info' => true
+ ]
+ );
+
+ $instance->process( '' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialStatsAddExtraTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialStatsAddExtraTest.php
new file mode 100644
index 00000000..f2ae87bb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/SpecialStatsAddExtraTest.php
@@ -0,0 +1,164 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Hooks\SpecialStatsAddExtra;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\SpecialStatsAddExtra
+ * @group smenatic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SpecialStatsAddExtraTest extends \PHPUnit_Framework_TestCase {
+
+ protected function tearDown() {
+ ApplicationFactory::clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ SpecialStatsAddExtra::class,
+ new SpecialStatsAddExtra( $store )
+ );
+ }
+
+ /**
+ * @dataProvider statisticsDataProvider
+ */
+ public function testProcess( $setup, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getStatistics' )
+ ->will( $this->returnValue( $setup['statistics'] ) );
+
+ $extraStats = $setup['extraStats'];
+
+ $instance = new SpecialStatsAddExtra( $store );
+
+ $instance->setOptions(
+ [
+ 'smwgSemanticsEnabled' => true
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( $extraStats )
+ );
+
+ $this->assertTrue(
+ $this->matchArray( $extraStats, $expected['statistics'] )
+ );
+ }
+
+ public function testProcessOnSQLStore() {
+
+ $extraStats = [];
+
+ $instance = new SpecialStatsAddExtra(
+ ApplicationFactory::getInstance()->getStore()
+ );
+
+ $instance->setOptions(
+ [
+ 'smwgSemanticsEnabled' => true
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->process( $extraStats )
+ );
+
+ // This is a "cheap" check against the SQLStore as it could return any
+ // value therefore we use a message key as only known constant to verify
+ // that the matching process was successful
+ $this->assertTrue(
+ $this->matchArray( $extraStats, 'smw-statistics-property-instance' )
+ );
+ }
+
+ public function matchArray( array $matcher, $searchValue ) {
+
+ foreach ( $matcher as $key => $value ) {
+
+ if ( $searchValue === $key || $searchValue === $value ) {
+ return true;
+ };
+
+ if ( is_array( $value ) ) {
+ return $this->matchArray( $value, $searchValue );
+ };
+ }
+
+ return $searchValue !== null ? false : true;
+ }
+
+ public function statisticsDataProvider() {
+
+ $input = [
+ 'PROPUSES' => 1001
+ ];
+
+ #0
+ $provider[] = [
+ [
+ 'extraStats' => [],
+ 'statistics' => $input
+ ],
+ [
+ 'statistics' => 1001
+ ]
+ ];
+
+ #1 unknown
+ $provider[] = [
+ [
+ 'extraStats' => [],
+ 'statistics' => [ 'Yeey' => 2002 ]
+ ],
+ [
+ 'statistics' => null
+ ]
+ ];
+
+ #2 MW 1.21+
+ $provider[] = [
+ [
+ 'extraStats' => [],
+ 'statistics' => $input
+ ],
+ [
+ 'statistics' => 1001
+ ]
+ ];
+
+ #3 MW 1.21+ - unknown
+ $provider[] = [
+ [
+ 'extraStats' => [],
+ 'statistics' => [ 'Quuxy' => 2002 ]
+ ],
+ [
+ 'statistics' => null
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsAlwaysKnownTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsAlwaysKnownTest.php
new file mode 100644
index 00000000..d3169d50
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsAlwaysKnownTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\TitleIsAlwaysKnown;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\TitleIsAlwaysKnown
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class TitleIsAlwaysKnownTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = '';
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\TitleIsAlwaysKnown',
+ new TitleIsAlwaysKnown( $title, $result )
+ );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testPerformUpdate( $namespace, $text, $expected ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( $namespace ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $text ) );
+
+ $result = '';
+
+ $instance = new TitleIsAlwaysKnown( $title, $result );
+ $this->assertTrue( $instance->process() );
+
+ $this->assertEquals( $expected, $result );
+ }
+
+ public function titleProvider() {
+
+ $provider = [
+ [ SMW_NS_PROPERTY, 'Modification date', true ],
+ [ SMW_NS_PROPERTY, 'Foo', false ],
+ [ NS_MAIN, 'Modification date', false ],
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsMovableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsMovableTest.php
new file mode 100644
index 00000000..136b094d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleIsMovableTest.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\TitleIsMovable;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\TitleIsMovable
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TitleIsMovableTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\TitleIsMovable',
+ new TitleIsMovable( $title )
+ );
+ }
+
+ public function testPredefinedPropertyPageIsNotMovable() {
+
+ $title = Title::newFromText( 'Modification date', SMW_NS_PROPERTY );
+ $isMovable = true;
+
+ $instance = new TitleIsMovable( $title );
+
+ $this->assertTrue(
+ $instance->process( $isMovable )
+ );
+
+ $this->assertFalse(
+ $isMovable
+ );
+ }
+
+ public function testUserdefinedPropertyPageIsMovable() {
+
+ $title = Title::newFromText( 'Foo', SMW_NS_PROPERTY );
+ $isMovable = true;
+
+ $instance = new TitleIsMovable( $title );
+
+ $this->assertTrue(
+ $instance->process( $isMovable )
+ );
+
+ $this->assertTrue(
+ $isMovable
+ );
+ }
+
+ public function testNonPropertyPageIsAlwaysMovable() {
+
+ $title = Title::newFromText( 'Foo', NS_MAIN );
+ $isMovable = true;
+
+ $instance = new TitleIsMovable( $title );
+
+ $this->assertTrue(
+ $instance->process( $isMovable )
+ );
+
+ $this->assertTrue(
+ $isMovable
+ );
+ }
+
+ public function testRulePageIsAlwaysNotMovable() {
+
+ $title = Title::newFromText( 'Foo', SMW_NS_SCHEMA );
+ $isMovable = true;
+
+ $instance = new TitleIsMovable( $title );
+
+ $this->assertTrue(
+ $instance->process( $isMovable )
+ );
+
+ $this->assertFalse(
+ $isMovable
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleMoveCompleteTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleMoveCompleteTest.php
new file mode 100644
index 00000000..9a60ba3e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/TitleMoveCompleteTest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\TitleMoveComplete;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\TitleMoveComplete
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class TitleMoveCompleteTest extends \PHPUnit_Framework_TestCase {
+
+ private $user;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->user = new MockSuperUser();
+
+ $settings = [
+ 'smwgMainCacheType' => 'hash',
+ 'smwgAutoRefreshOnPageMove' => true,
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true, NS_HELP => false ]
+ ];
+
+ $this->testEnvironment->withConfiguration(
+ $settings
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $oldTitle = MockTitle::buildMock( 'old' );
+ $newTitle = MockTitle::buildMock( 'new' );
+
+ $instance = new TitleMoveComplete(
+ $oldTitle,
+ $newTitle,
+ $this->user,
+ 0,
+ 0
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Hooks\TitleMoveComplete',
+ $instance
+ );
+ }
+
+ public function testChangeSubjectForSupportedSemanticNamespace() {
+
+ $oldTitle = \Title::newFromText( 'Old' );
+ $newTitle = \Title::newFromText( 'New' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'changeTitle' );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new TitleMoveComplete(
+ $oldTitle,
+ $newTitle,
+ $this->user,
+ 0,
+ 0
+ );
+
+ $this->assertTrue(
+ $instance->process()
+ );
+ }
+
+ public function testDeleteSubjectForNotSupportedSemanticNamespace() {
+
+ $oldTitle = \Title::newFromText( 'Old' );
+ $newTitle = \Title::newFromText( 'New', NS_HELP );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'deleteSubject' )
+ ->with(
+ $this->equalTo( $oldTitle ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new TitleMoveComplete(
+ $oldTitle,
+ $newTitle,
+ $this->user,
+ 0,
+ 0
+ );
+
+ $this->assertTrue(
+ $instance->process()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/UserChangeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/UserChangeTest.php
new file mode 100644
index 00000000..be037358
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Hooks/UserChangeTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Hooks;
+
+use SMW\MediaWiki\Hooks\UserChange;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Hooks\UserChange
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UserChangeTest extends \PHPUnit_Framework_TestCase {
+
+ private $namespaceExaminer;
+ private $testEnvironment;
+ private $jobFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobFactory', $this->jobFactory );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ UserChange::class,
+ new UserChange( $this->namespaceExaminer )
+ );
+ }
+
+ public function testOnEnabledUserNamespace() {
+
+ $job = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\UpdateJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->once() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $job ) );
+
+ $this->namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->with( $this->equalTo( NS_USER ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new UserChange(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOrigin( 'Foo' );
+
+ $this->assertTrue(
+ $instance->process( 'Foo' )
+ );
+ }
+
+ public function testOnEnabledUserNamespace_User() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $job = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\UpdateJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->once() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $job ) );
+
+ $this->namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->with( $this->equalTo( NS_USER ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new UserChange(
+ $this->namespaceExaminer
+ );
+
+ $instance->setOrigin( 'Foo' );
+
+ $this->assertTrue(
+ $instance->process( $user )
+ );
+ }
+
+ public function testOnDisabledUserNamespace() {
+
+ $this->jobFactory->expects( $this->never() )
+ ->method( 'newUpdateJob' );
+
+ $this->namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->with( $this->equalTo( NS_USER ) )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new UserChange(
+ $this->namespaceExaminer
+ );
+
+ $this->assertFalse(
+ $instance->process( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobFactoryTest.php
new file mode 100644
index 00000000..3ae94077
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobFactoryTest.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\JobFactory;
+use Title;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\JobFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class JobFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ JobFactory::class,
+ new JobFactory()
+ );
+ }
+
+ /**
+ * @dataProvider typeProvider
+ */
+ public function testNewByType( $type, $expected ) {
+
+ $instance = new JobFactory();
+
+ $this->assertInstanceOf(
+ $expected,
+ $instance->newByType( $type, Title::newFromText( __METHOD__ ) )
+ );
+ }
+
+ /**
+ * @dataProvider typeProvider
+ */
+ public function testNewByTypeWithNullTitle( $type ) {
+
+ $instance = new JobFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Jobs\NullJob',
+ $instance->newByType( $type, null )
+ );
+ }
+
+ public function testNewByTypeOnUnknownJobThrowsException() {
+
+ $instance = new JobFactory();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->newByType( 'Foo', Title::newFromText( __METHOD__ ) );
+ }
+
+ public function typeProvider() {
+
+ $provider[] = [
+ 'SMW\RefreshJob',
+ '\SMW\MediaWiki\Jobs\RefreshJob'
+ ];
+
+ $provider[] = [
+ 'smw.refresh',
+ '\SMW\MediaWiki\Jobs\RefreshJob'
+ ];
+
+ $provider[] = [
+ 'SMW\UpdateJob',
+ '\SMW\MediaWiki\Jobs\UpdateJob'
+ ];
+
+ $provider[] = [
+ 'smw.update',
+ '\SMW\MediaWiki\Jobs\UpdateJob'
+ ];
+
+ $provider[] = [
+ 'SMW\UpdateDispatcherJob',
+ '\SMW\MediaWiki\Jobs\UpdateDispatcherJob'
+ ];
+
+ $provider[] = [
+ 'smw.updateDispatcher',
+ '\SMW\MediaWiki\Jobs\UpdateDispatcherJob'
+ ];
+
+ $provider[] = [
+ 'SMW\ParserCachePurgeJob',
+ '\SMW\MediaWiki\Jobs\ParserCachePurgeJob'
+ ];
+
+ $provider[] = [
+ 'smw.parserCachePurge',
+ '\SMW\MediaWiki\Jobs\ParserCachePurgeJob'
+ ];
+
+ $provider[] = [
+ 'SMW\FulltextSearchTableUpdateJob',
+ '\SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob'
+ ];
+
+ $provider[] = [
+ 'smw.fulltextSearchTableUpdate',
+ '\SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob'
+ ];
+
+ $provider[] = [
+ 'SMW\EntityIdDisposerJob',
+ '\SMW\MediaWiki\Jobs\EntityIdDisposerJob'
+ ];
+
+ $provider[] = [
+ 'smw.entityIdDisposer',
+ '\SMW\MediaWiki\Jobs\EntityIdDisposerJob'
+ ];
+
+ $provider[] = [
+ 'SMW\PropertyStatisticsRebuildJob',
+ '\SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob'
+ ];
+
+ $provider[] = [
+ 'smw.propertyStatisticsRebuild',
+ '\SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob'
+ ];
+
+ $provider[] = [
+ 'SMW\FulltextSearchTableRebuildJob',
+ '\SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob'
+ ];
+
+ $provider[] = [
+ 'smw.fulltextSearchTableRebuild',
+ '\SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob'
+ ];
+
+ $provider[] = [
+ 'SMW\ChangePropagationDispatchJob',
+ '\SMW\MediaWiki\Jobs\ChangePropagationDispatchJob'
+ ];
+
+ $provider[] = [
+ 'smw.changePropagationDispatch',
+ '\SMW\MediaWiki\Jobs\ChangePropagationDispatchJob'
+ ];
+
+ $provider[] = [
+ 'SMW\ChangePropagationUpdateJob',
+ '\SMW\MediaWiki\Jobs\ChangePropagationUpdateJob'
+ ];
+
+ $provider[] = [
+ 'smw.changePropagationUpdate',
+ '\SMW\MediaWiki\Jobs\ChangePropagationUpdateJob'
+ ];
+
+ $provider[] = [
+ 'SMW\ChangePropagationClassUpdateJob',
+ '\SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob'
+ ];
+
+ $provider[] = [
+ 'smw.changePropagationClassUpdate',
+ '\SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobQueueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobQueueTest.php
new file mode 100644
index 00000000..220f1ce6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/JobQueueTest.php
@@ -0,0 +1,244 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\JobQueue;
+
+/**
+ * @covers \SMW\MediaWiki\JobQueue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class JobQueueTest extends \PHPUnit_Framework_TestCase {
+
+ private $jobQueueGroup;
+
+ protected function setup() {
+
+ $this->jobQueueGroup = $this->getMockBuilder( '\JobQueueGroup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ JobQueue::class,
+ new JobQueue( $this->jobQueueGroup )
+ );
+ }
+
+ public function testRunFromQueue() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+
+ // MediaWiki's JobQueue::pop !!!
+ try {
+ $log = $instance->runFromQueue( [ 'FakeJob' => 2 ] );
+ } catch ( \Exception $e ) {
+ // Do nothing
+ }
+ }
+
+ public function testPop() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+
+ // MediaWiki's JobQueue::pop !!!
+ try {
+ $instance->pop( 'FakeJob' );
+ } catch ( \Exception $e ) {
+ // Do nothing
+ }
+ }
+
+ public function testAck() {
+
+ $job = $this->getMockBuilder( '\Job' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getType', 'run' ] )
+ ->getMock();
+
+ $job->expects( $this->atLeastOnce() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'FakeJob' ) );
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+
+ // MediaWiki's JobQueue::ack !!!
+ try {
+ $instance->ack( $job );
+ } catch ( \Exception $e ) {
+ // Do nothing
+ }
+ }
+
+ public function testDeleteWithDisabledCache() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'assertNotReadOnly', 'doDelete', 'doFlushCaches' ] )
+ ->getMockForAbstractClass();
+
+ $jobQueue->expects( $this->any() )
+ ->method( 'assertNotReadOnly' )
+ ->will( $this->returnValue( false ) );
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doDelete' );
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doFlushCaches' );
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+ $instance->disableCache( true );
+
+ $instance->delete( 'FakeJob' );
+ }
+
+ public function testPush() {
+
+ $fakeJob = $this->getMockBuilder( '\Job' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'push' );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+ $instance->push( $fakeJob );
+ }
+
+ public function testLazyPush() {
+
+ if ( !method_exists( $this->jobQueueGroup, 'lazyPush' ) ) {
+ $this->markTestSkipped( 'JobQueueGroup::lazyPush is not supported.' );
+ }
+
+ $fakeJob = $this->getMockBuilder( '\Job' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'lazyPush' );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+ $instance->lazyPush( $fakeJob );
+ }
+
+ public function testGetQueueSizes() {
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'getQueueSizes' );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+ $instance->getQueueSizes();
+ }
+
+ public function testGetQueueSize() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doGetSize', 'doFlushCaches' ] )
+ ->getMockForAbstractClass();
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doGetSize' );
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doFlushCaches' );
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+ $instance->disableCache( true );
+
+ $instance->getQueueSize( 'FakeJob' );
+ }
+
+ public function testHasPendingJob() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doGetSize' ] )
+ ->getMockForAbstractClass();
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doGetSize' )
+ ->will( $this->returnValue( 1 ) );
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'FakeJob' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+
+ $this->assertTrue(
+ $instance->hasPendingJob( 'FakeJob' )
+ );
+ }
+
+ public function testHasPendingJobWithLegacyName() {
+
+ $jobQueue = $this->getMockBuilder( '\JobQueue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'doGetSize' ] )
+ ->getMockForAbstractClass();
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'doGetSize' )
+ ->will( $this->returnValue( 1 ) );
+
+ $this->jobQueueGroup->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'smw.fake' ) )
+ ->will( $this->returnValue( $jobQueue ) );
+
+ $instance = new JobQueue( $this->jobQueueGroup );
+
+ $this->assertTrue(
+ $instance->hasPendingJob( 'SMW\FakeJob' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationClassUpdateJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationClassUpdateJobTest.php
new file mode 100644
index 00000000..85bfd594
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationClassUpdateJobTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\ChangePropagationClassUpdateJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationClassUpdateJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ChangePropagationClassUpdateJob::class,
+ new ChangePropagationClassUpdateJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider jobProvider
+ */
+ public function testRun( $subject, $parameters ) {
+
+ $instance = new ChangePropagationClassUpdateJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function jobProvider() {
+
+ $provider[] = [
+ DIWikiPage::newFromText( __METHOD__ ),
+ []
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php
new file mode 100644
index 00000000..9609828b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationDispatchJobTest.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ChangePropagationDispatchJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\ChangePropagationDispatchJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationDispatchJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ChangePropagationDispatchJob::class,
+ new ChangePropagationDispatchJob( $title )
+ );
+ }
+
+ public function testCleanUp() {
+
+ $subject = DIWikiPage::newFromText(__METHOD__, SMW_NS_PROPERTY );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->getMockForAbstractClass();
+
+ $cache->expects( $this->once() )
+ ->method( 'delete' );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ ChangePropagationDispatchJob::cleanUp( $subject );
+ }
+
+ public function testHasPendingJobs() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->getMockForAbstractClass();
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( 42 ) );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $this->assertTrue(
+ ChangePropagationDispatchJob::hasPendingJobs( $subject )
+ );
+ }
+
+ public function testGetPendingJobsCount() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->getMockForAbstractClass();
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( 42 ) );
+
+ $this->testEnvironment->registerObject( 'Cache', $cache );
+
+ $this->assertSame(
+ 42,
+ ChangePropagationDispatchJob::getPendingJobsCount( $subject )
+ );
+ }
+
+ public function testFindAndDispatchOnNonPropertyEntity() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue->expects( $this->never() )
+ ->method( 'lazyPush' );
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $instance = new ChangePropagationDispatchJob(
+ $subject->getTitle()
+ );
+
+ $instance->run();
+ }
+
+ public function testPlanAsJob() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue->expects( $this->once() )
+ ->method( 'lazyPush' );
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ ChangePropagationDispatchJob::planAsJob( $subject );
+ }
+
+ public function testFindAndDispatchOnPropertyEntity() {
+
+ $subject = DIWikiPage::newFromText( 'Foo', SMW_NS_PROPERTY );
+
+ $tempFile = $this->getMockBuilder( '\SMW\Utils\TempFile' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tempFile->expects( $this->atLeastOnce() )
+ ->method( 'write' );
+
+ $this->testEnvironment->registerObject( 'TempFile', $tempFile );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueue->expects( $this->atLeastOnce() )
+ ->method( 'lazyPush' );
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher->expects( $this->atLeastOnce() )
+ ->method( 'getDefaultDataItemTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new ChangePropagationDispatchJob(
+ $subject->getTitle(),
+ [
+ 'isTypePropagation' => true
+ ]
+ );
+
+ $instance->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationUpdateJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationUpdateJobTest.php
new file mode 100644
index 00000000..16ef47e0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ChangePropagationUpdateJobTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ChangePropagationUpdateJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\ChangePropagationUpdateJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationUpdateJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ChangePropagationUpdateJob::class,
+ new ChangePropagationUpdateJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider jobProvider
+ */
+ public function testRun( $subject, $parameters ) {
+
+ $instance = new ChangePropagationUpdateJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function jobProvider() {
+
+ $provider[] = [
+ DIWikiPage::newFromText( __METHOD__ ),
+ []
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/EntityIdDisposerJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/EntityIdDisposerJobTest.php
new file mode 100644
index 00000000..0110b99a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/EntityIdDisposerJobTest.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\EntityIdDisposerJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\EntityIdDisposerJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EntityIdDisposerJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->getMockForAbstractClass();
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->setConnectionManager( $connectionManager );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ EntityIdDisposerJob::class,
+ new EntityIdDisposerJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testJobRun( $parameters ) {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new EntityIdDisposerJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ []
+ ];
+
+ $provider[] = [
+ [ 'id' => 42 ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableRebuildJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableRebuildJobTest.php
new file mode 100644
index 00000000..3c18b202
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableRebuildJobTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableRebuildJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob',
+ new FulltextSearchTableRebuildJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testRunJob( $parameters ) {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new FulltextSearchTableRebuildJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ []
+ ];
+
+ $provider[] = [
+ [ 'table' => 'Foo' ]
+ ];
+
+ $provider[] = [
+ [ 'mode' => 'full' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableUpdateJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableUpdateJobTest.php
new file mode 100644
index 00000000..ce085a35
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/FulltextSearchTableUpdateJobTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableUpdateJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->testEnvironment->registerObject(
+ 'Store',
+ $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )->getMockForAbstractClass()
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\FulltextSearchTableUpdateJob',
+ new FulltextSearchTableUpdateJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testJobRun( $parameters ) {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new FulltextSearchTableUpdateJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ 'diff' => [ 1, 2 ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/NullJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/NullJobTest.php
new file mode 100644
index 00000000..58a25460
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/NullJobTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\MediaWiki\Jobs\NullJob;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\NullJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class NullJobTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\NullJob',
+ new NullJob( null )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testRunJob( $parameters ) {
+
+ $instance = new NullJob(
+ null,
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ []
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ParserCachePurgeJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ParserCachePurgeJobTest.php
new file mode 100644
index 00000000..28ba1c62
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/ParserCachePurgeJobTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\ParserCachePurgeJob;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\ParserCachePurgeJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ParserCachePurgeJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->getMockForAbstractClass();
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\ParserCachePurgeJob',
+ new ParserCachePurgeJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testJobWithIdList( $parameters ) {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new ParserCachePurgeJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function testSplitList() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new ParserCachePurgeJob(
+ $subject->getTitle()
+ );
+
+ $list = [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ new DIWikiPage( 'Foobar', 0 , '', '_QUERY123' )
+ ];
+
+ $this->assertEquals(
+ [ [ 'Foo#0##', 'Bar#0##', 'Foobar#0##' ], [ '_QUERY123' ] ],
+ $instance->splitList( $list )
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ 'idlist' => [ 1, 2 ]
+ ];
+
+ $provider[] = [
+ 'idlist' => '1|2'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/PropertyStatisticsRebuildJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/PropertyStatisticsRebuildJobTest.php
new file mode 100644
index 00000000..5c6e3e6f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/PropertyStatisticsRebuildJobTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatisticsRebuildJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $row = new \stdClass;
+ $row->smw_title = 'Test';
+ $row->smw_id = 42;
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( new \FakeResultWrapper( [ $row ] ) ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob',
+ new PropertyStatisticsRebuildJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testRunJob( $parameters ) {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new PropertyStatisticsRebuildJob(
+ $subject->getTitle(),
+ $parameters
+ );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ []
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/RefreshJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/RefreshJobTest.php
new file mode 100644
index 00000000..b30e5fdf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/RefreshJobTest.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Jobs\RefreshJob;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\RefreshJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RefreshJobTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var integer */
+ protected $controlRefreshDataIndex;
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\RefreshJob',
+ new RefreshJob( $title )
+ );
+
+ // FIXME Delete SMWRefreshJob assertion after all
+ // references to SMWRefreshJob have been removed
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\RefreshJob',
+ new \SMWRefreshJob( $title )
+ );
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testRunJobOnMockStore( $parameters, $expected ) {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $expectedToRun = $expected['spos'] === null ? $this->once() : $this->once();
+
+ $entityRebuildDispatcher = $this->getMockBuilder( '\SMW\SQLStore\EntityRebuildDispatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityRebuildDispatcher->expects( $this->any() )
+ ->method( 'rebuild' )
+ ->will( $this->returnValue( $parameters['spos'] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->setMethods( [ 'refreshData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $expectedToRun )
+ ->method( 'refreshData' )
+ ->will( $this->returnValue( $entityRebuildDispatcher ) );
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $instance = new RefreshJob( $title, $parameters );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+
+ $this->assertEquals(
+ $expected['progress'],
+ $instance->getProgress(),
+ "Asserts that the getProgress() returns {$expected['progress']}"
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function parameterDataProvider() {
+
+ $provider = [];
+
+ // #0 Empty
+ $provider[] = [
+ [
+ 'spos' => null
+ ],
+ [
+ 'progress' => 0,
+ 'spos' => null
+ ]
+ ];
+
+ // #1 Initial
+ $provider[] = [
+ [
+ 'spos' => 1,
+ 'prog' => 0,
+ 'rc' => 1
+ ],
+ [
+ 'progress' => 0,
+ 'spos' => 1
+ ]
+ ];
+
+ // #2
+ $provider[] = [
+ [
+ 'spos' => 1,
+ 'run' => 1,
+ 'prog' => 10,
+ 'rc' => 1
+ ],
+ [
+ 'progress' => 10,
+ 'spos' => 1
+ ]
+ ];
+
+ // #3 Initiates another run from the beginning
+ $provider[] = [
+ [
+ 'spos' => 0,
+ 'run' => 1,
+ 'prog' => 10,
+ 'rc' => 2
+ ],
+ [
+ 'progress' => 5,
+ 'spos' => 0
+ ]
+ ];
+
+ return $provider;
+
+ }
+
+ /**
+ * @see Store::refreshData
+ *
+ * @since 1.9
+ *
+ * @param integer $index
+ */
+ public function refreshDataCallback( &$index ) {
+ $this->controlRefreshDataIndex = $index;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateDispatcherJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateDispatcherJobTest.php
new file mode 100644
index 00000000..cf721cad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateDispatcherJobTest.php
@@ -0,0 +1,421 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateDispatcherJob;
+use SMW\SemanticData;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\UpdateDispatcherJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class UpdateDispatcherJobTest extends \PHPUnit_Framework_TestCase {
+
+ protected $expectedProperty;
+ protected $expectedSubjects;
+ private $semanticDataSerializer;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataSerializer = ApplicationFactory::getInstance()->newSerializerFactory()->newSemanticDataSerializer();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false
+ ] );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\UpdateDispatcherJob',
+ new UpdateDispatcherJob( $title )
+ );
+ }
+
+ public function testPushToJobQueue() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new UpdateDispatcherJob( $title, [] );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertNull( $instance->pushToJobQueue() );
+ }
+
+ public function testChunkedJobWithListOnValidMembers() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new UpdateDispatcherJob( $title, [
+ 'job-list' => [
+ 'Foo#0##' => true,
+ 'Bar#102##'
+ ]
+ ] );
+
+ $instance->isEnabledJobQueue( false );
+ $instance->run();
+
+ $this->assertEquals(
+ 2,
+ $instance->getJobCount()
+ );
+ }
+
+ public function testChunkedJobWithListOnInvalidMembers() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new UpdateDispatcherJob( $title, [
+ 'job-list' => [
+ '|nulltitle#0##' => true,
+ 'deserlizeerror#0' => true
+ ]
+ ] );
+
+ $instance->isEnabledJobQueue( false );
+ $instance->run();
+
+ $this->assertEquals(
+ 0,
+ $instance->getJobCount()
+ );
+ }
+
+ public function testJobRunOnMainNamespace() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'getProperties',
+ 'getInProperties' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateDispatcherJob( $title, [] );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+ }
+
+ public function testJobRunOnPropertyNamespace() {
+
+ $title = Title::newFromText( __METHOD__, SMW_NS_PROPERTY );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'getProperties',
+ 'getInProperties',
+ 'getAllPropertySubjects',
+ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateDispatcherJob( $title, [] );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+ }
+
+ public function testJobRunOnRestrictedPool() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $semanticData = new SemanticData( $subject );
+ $semanticData->addPropertyObjectValue( new DIProperty( '42' ), $subject );
+
+ $parameters = [
+ 'semanticData' => $this->semanticDataSerializer->serialize( $semanticData ),
+ UpdateDispatcherJob::RESTRICTED_DISPATCH_POOL => true
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'getAllPropertySubjects',
+ ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateDispatcherJob( $title, $parameters );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ /**
+ * @dataProvider subjectDataProvider
+ */
+ public function testRunJobOnMockWithOutParameters( $setup, $expected ) {
+
+ $this->expectedProperty = $setup['property'];
+ $this->expectedSubjects = $setup['subjects'];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'getAllPropertySubjects',
+ 'getPropertyValues',
+ 'getProperties',
+ 'getInProperties',
+ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnCallback( [ $this, 'mockStoreAllPropertySubjectsCallback' ] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $setup['title'] ) ] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( $setup['properties'] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( $setup['properties'] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateDispatcherJob( $setup['title'], $setup['parameters'] );
+ $instance->isEnabledJobQueue( false );
+ $instance->run();
+
+ $this->assertEquals(
+ $expected['count'],
+ $instance->getJobCount()
+ );
+ }
+
+ /**
+ * @dataProvider subjectDataProvider
+ */
+ public function testRunJobOnMockWithParameters( $setup, $expected ) {
+
+ $semanticData = new SemanticData(
+ DIWikiPage::newFromTitle( $setup['title'] )
+ );
+
+ $parameters = [
+ 'semanticData' => $this->semanticDataSerializer->serialize( $semanticData )
+ ] + $setup['parameters'];
+
+ $this->expectedProperty = $setup['property'];
+ $this->expectedSubjects = $setup['subjects'];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [
+ 'getAllPropertySubjects',
+ 'getPropertyValues',
+ 'getProperties',
+ 'getInProperties',
+ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnCallback( [ $this, 'mockStoreAllPropertySubjectsCallback' ] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $setup['title'] ) ] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( $setup['properties'] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( $setup['properties'] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateDispatcherJob( $setup['title'], $parameters );
+ $instance->isEnabledJobQueue( false );
+ $instance->run();
+
+ $this->assertEquals(
+ $expected['count'],
+ $instance->getJobCount()
+ );
+ }
+
+ public function subjectDataProvider() {
+
+ $provider = [];
+
+ $duplicate = DIWikiPage::newFromText( 'Foo' );
+
+ $subjects = [
+ $duplicate,
+ DIWikiPage::newFromText( 'Bar' ),
+ DIWikiPage::newFromText( 'Baz' ),
+ $duplicate,
+ DIWikiPage::newFromText( 'Yon' ),
+ DIWikiPage::newFromText( 'Yon' ),
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ ];
+
+ $count = count( $subjects ) - 1; // eliminate duplicate count
+ $title = Title::newFromText( __METHOD__, SMW_NS_PROPERTY );
+ $property = DIProperty::newFromUserLabel( $title->getText() );
+
+ #0
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'subjects' => $subjects,
+ 'property' => $property,
+ 'properties' => [],
+ 'parameters' => []
+ ],
+ [
+ 'count' => 6
+ ]
+ ];
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+ $property = DIProperty::newFromUserLabel( $title->getText() );
+
+ #1
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'subjects' => [ DIWikiPage::newFromTitle( $title ) ],
+ 'property' => $property,
+ 'properties' => [ $property ],
+ 'parameters' => []
+ ],
+ [
+ 'count' => 1
+ ]
+ ];
+
+
+ #2
+ $duplicate = DIWikiPage::newFromText( 'Foo' );
+
+ $subjects = [
+ $duplicate,
+ DIWikiPage::newFromText( 'Bar' ),
+ DIWikiPage::newFromText( 'Baz' ),
+ $duplicate,
+ DIWikiPage::newFromText( 'Yon' ),
+ DIWikiPage::newFromText( 'Yon' ),
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ ];
+
+ $title = Title::newFromText( __METHOD__, SMW_NS_PROPERTY );
+ $property = DIProperty::newFromUserLabel( $title->getText() );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'subjects' => $subjects,
+ 'property' => $property,
+ 'properties' => [],
+ 'parameters' => [ UpdateDispatcherJob::RESTRICTED_DISPATCH_POOL => true ]
+ ],
+ [
+ 'count' => 6
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * Returns an array of DIWikiPage objects if the expected property
+ * and the argument property are identical
+ *
+ * @see Store::getAllPropertySubjects
+ *
+ * @return DIWikiPage[]
+ */
+ public function mockStoreAllPropertySubjectsCallback( DIProperty $property ) {
+ return $this->expectedSubjects;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateJobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateJobTest.php
new file mode 100644
index 00000000..09e2bdc1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Jobs/UpdateJobTest.php
@@ -0,0 +1,325 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Jobs;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\Tests\TestEnvironment;
+use SMWDIBlob as DIBlob;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Jobs\UpdateJob
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class UpdateJobTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $semanticDataFactory;
+ private $semanticDataSerializer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false,
+ 'smwgEnabledDeferredUpdate' => false,
+ 'smwgDVFeatures' => '',
+ 'smwgSemanticsEnabled' => false
+ ] );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getPropertyValues', 'updateData' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->semanticDataFactory = $this->testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ $this->semanticDataSerializer = \SMW\ApplicationFactory::getInstance()->newSerializerFactory()->newSemanticDataSerializer();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\UpdateJob',
+ new UpdateJob( $title )
+ );
+
+ // FIXME Delete SMWUpdateJob assertion after all
+ // references to SMWUpdateJob have been removed
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Jobs\UpdateJob',
+ new \SMWUpdateJob( $title )
+ );
+ }
+
+ public function testJobWithMissingParserOutput() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new UpdateJob( $title );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertFalse( $instance->run() );
+ }
+
+ public function testJobWithInvalidTitle() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->testEnvironment->registerObject( 'ContentParser', null );
+
+ $instance = new UpdateJob( $title );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+ }
+
+ public function testJobWithNoRevisionAvailable() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $contentParser = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentParser->expects( $this->once() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( null ) );
+
+ $this->testEnvironment->registerObject( 'ContentParser', $contentParser );
+
+ $instance = new UpdateJob( $title );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertFalse( $instance->run() );
+ }
+
+ public function testJobWithValidRevision() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBkey' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->once() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $contentParser = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentParser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( new \ParserOutput ) );
+
+ $this->testEnvironment->registerObject( 'ContentParser', $contentParser );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $idTable->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'clearData', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->once() )
+ ->method( 'clearData' );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateJob( $title );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+ }
+
+ public function testJobToCompareLastModified() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBkey' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $contentParser = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentParser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( new \ParserOutput ) );
+
+ $this->testEnvironment->registerObject( 'ContentParser', $contentParser );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyValues', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateJob( $title, [ 'shallowUpdate' => true ] );
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue( $instance->run() );
+ }
+
+ public function testJobOnSerializedSemanticData() {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'updateData' );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $semanticData = $this->semanticDataSerializer->serialize(
+ $this->semanticDataFactory->newEmptySemanticData( __METHOD__ )
+ );
+
+ $instance = new UpdateJob( $title,
+ [
+ UpdateJob::SEMANTIC_DATA => $semanticData
+ ]
+ );
+
+ $instance->isEnabledJobQueue( false );
+
+ $this->assertTrue(
+ $instance->run()
+ );
+ }
+
+ public function testJobOnChangePropagation() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY );
+
+ $semanticData = $this->semanticDataSerializer->serialize(
+ $this->semanticDataFactory->newEmptySemanticData( __METHOD__ )
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'updateData', 'getPropertyValues' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ new DIBlob( json_encode( $semanticData ) ) ] ) );
+
+ $store->expects( $this->once() )
+ ->method( 'updateData' );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new UpdateJob( $subject->getTitle(),
+ [
+ UpdateJob::CHANGE_PROP => $subject->getSerialization()
+ ]
+ );
+
+ $instance->isEnabledJobQueue( false );
+
+ $instance->run();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/LocalTimeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/LocalTimeTest.php
new file mode 100644
index 00000000..53745c88
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/LocalTimeTest.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use DateTime;
+use SMW\MediaWiki\LocalTime;
+
+/**
+ * @covers \SMW\MediaWiki\LocalTime
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class LocalTimeTest extends \PHPUnit_Framework_TestCase {
+
+ public function testNoModifiedLocalTime() {
+
+ $dateTime = LocalTime::getLocalizedTime(
+ new DateTime( '2017-08-01 10:00:00+00:00' )
+ );
+
+ $this->assertFalse(
+ $dateTime->hasLocalTimeCorrection
+ );
+ }
+
+ public function testModifiedTimeWithPositiveLocalTimeOffset() {
+
+ $dti = new DateTime( '2017-08-01 10:00:00+00:00' );
+
+ LocalTime::setLocalTimeOffset( 60 );
+ $dateTime = LocalTime::getLocalizedTime( $dti );
+
+ $this->assertTrue(
+ $dateTime->hasLocalTimeCorrection
+ );
+
+ $this->assertEquals(
+ '2017-08-01 11:00:00',
+ $dateTime->format( 'Y-m-d H:i:s' )
+ );
+ }
+
+ public function testModifiedTimeWithNegativeLocalTimeOffset() {
+
+ $dti = new DateTime( '2017-08-01 10:00:00+00:00' );
+
+ LocalTime::setLocalTimeOffset( -60 );
+ $dateTime = LocalTime::getLocalizedTime( $dti );
+
+ $this->assertTrue(
+ $dateTime->hasLocalTimeCorrection
+ );
+
+ $this->assertEquals(
+ '2017-08-01 09:00:00',
+ $dateTime->format( 'Y-m-d H:i:s' )
+ );
+ }
+
+ public function testModifiedTimeWithUserTimeCorrection() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'timecorrection' ) )
+ ->will( $this->returnValue( 'ZoneInfo|+120|Europe/Berlin' ) );
+
+ $dti = new DateTime( '2017-08-01 10:00:00+00:00' );
+
+ LocalTime::setLocalTimeOffset( 0 );
+ $dateTime = LocalTime::getLocalizedTime( $dti, $user );
+
+ $this->assertTrue(
+ $dateTime->hasLocalTimeCorrection
+ );
+
+ $this->assertEquals(
+ '2017-08-01 12:00:00',
+ $dateTime->format( 'Y-m-d H:i:s' )
+ );
+ }
+
+ public function testModifiedTimeWithUserTimeCorrectionOnInvalidZone() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'timecorrection' ) )
+ ->will( $this->returnValue( 'ZoneInfo|+125|Foo' ) );
+
+ $dti = new DateTime( '2017-08-01 10:00:00+00:00' );
+
+ $dateTime = LocalTime::getLocalizedTime( $dti, $user );
+
+ $this->assertTrue(
+ $dateTime->hasLocalTimeCorrection
+ );
+
+ $this->assertEquals(
+ '2017-08-01 12:05:00',
+ $dateTime->format( 'Y-m-d H:i:s' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MagicWordsFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MagicWordsFinderTest.php
new file mode 100644
index 00000000..68e58839
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MagicWordsFinderTest.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use ParserOutput;
+use SMW\MediaWiki\MagicWordsFinder;
+
+/**
+ * @covers \SMW\MediaWiki\MagicWordsFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class MagicWordsFinderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MagicWordsFinder',
+ new MagicWordsFinder()
+ );
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MagicWordsFinder',
+ new MagicWordsFinder( $parserOutput )
+ );
+ }
+
+ /**
+ * @dataProvider magicWordsProvider
+ */
+ public function testFindMagicWordInText( $magicWord, $text, $expectedText, $expectedWord ) {
+
+ $instance = new MagicWordsFinder();
+ $word = $instance->findMagicWordInText( $magicWord, $text );
+
+ $this->assertInternalType(
+ 'string',
+ $word
+ );
+
+ $this->assertEquals(
+ $expectedWord,
+ $word
+ );
+
+ $this->assertEquals(
+ $expectedText,
+ $text
+ );
+ }
+
+ public function testSetGetMagicWords() {
+
+ $instance = new MagicWordsFinder(
+ new ParserOutput()
+ );
+
+ $this->assertMagicWordFromParserOutput(
+ $instance,
+ [ 'Foo', '', 'Bar' ],
+ [ 'Foo', 'Bar' ]
+ );
+ }
+
+ public function testSetGetMagicWordsOnLegacyStorage() {
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\MagicWordsFinder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'hasExtensionData' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'hasExtensionData' )
+ ->will( $this->returnValue( false ) );
+
+ $instance->setOutput( new ParserOutput() );
+
+ $this->assertMagicWordFromParserOutput(
+ $instance,
+ [ 'Foo', '', 'Bar' ],
+ [ 'Foo', 'Bar' ]
+ );
+ }
+
+ public function testNoPushOnEmptyMagicWordsList() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->never() )
+ ->method( 'setExtensionData' );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\MagicWordsFinder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'hasExtensionData' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'hasExtensionData' )
+ ->will( $this->returnValue( true ) );
+
+ $instance->setOutput( $parserOutput );
+ $instance->pushMagicWordsToParserOutput( [] );
+ }
+
+ protected function assertMagicWordFromParserOutput( $instance, $magicWord, $expectedMagicWords ) {
+
+ $this->assertEmpty(
+ $instance->getMagicWords()
+ );
+
+ $instance->pushMagicWordsToParserOutput( $magicWord );
+
+ $this->assertEquals(
+ $expectedMagicWords,
+ $instance->getMagicWords()
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function magicWordsProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'SMW_NOFACTBOX',
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis',
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis',
+ ''
+ ];
+
+ $provider[] = [
+ 'SMW_NOFACTBOX',
+ 'Lorem ipsum dolor sit __NOFACTBOX__ amet consectetuer auctor at quis',
+ 'Lorem ipsum dolor sit amet consectetuer auctor at quis',
+ 'SMW_NOFACTBOX'
+ ];
+
+ $provider[] = [
+ 'SMW_SHOWFACTBOX',
+ 'Lorem ipsum dolor __NOFACTBOX__ sit amet consectetuer auctor at quis __SHOWFACTBOX__',
+ 'Lorem ipsum dolor __NOFACTBOX__ sit amet consectetuer auctor at quis ',
+ 'SMW_SHOWFACTBOX'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/ManualEntryLoggerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/ManualEntryLoggerTest.php
new file mode 100644
index 00000000..a69073b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/ManualEntryLoggerTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\ManualEntryLogger;
+
+/**
+ * @covers \SMW\MediaWiki\ManualEntryLogger
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ManualEntryLoggerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\ManualEntryLogger',
+ new ManualEntryLogger()
+ );
+ }
+
+ public function testLogToTableForNonLoggableEvent() {
+
+ $instance = new ManualEntryLogger();
+
+ $this->assertNull(
+ $instance->log( 'Foo', 'Bar', 'Baz', 'Yui' )
+ );
+ }
+
+ public function testRegisterLoggableEventType() {
+
+ $manualLogEntry = $this->getMockBuilder( '\ManualLogEntry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualLogEntry->expects( $this->once() )
+ ->method( 'insert' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new ManualEntryLogger( $manualLogEntry );
+ $instance->registerLoggableEventType( 'Foo' );
+
+ $this->assertEquals(
+ 42,
+ $instance->log( 'Foo', 'Bar', 'Baz', 'Yui' )
+ );
+ }
+
+ public function testLogToTableForLoggableEvent() {
+
+ $manualLogEntry = $this->getMockBuilder( '\ManualLogEntry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualLogEntry->expects( $this->once() )
+ ->method( 'insert' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->setMethods( [ 'newManualLogEntryForType' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'newManualLogEntryForType' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( $manualLogEntry ) );
+
+ $instance->registerLoggableEventType( 'Foo' );
+
+ $this->assertInternalType(
+ 'integer',
+ $instance->log( 'Foo', 'Bar', 'Baz', 'Yui' )
+ );
+ }
+
+ public function testLogToTableForLoggableEventWithPerformer() {
+
+ $performer = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualLogEntry = $this->getMockBuilder( '\ManualLogEntry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualLogEntry->expects( $this->once() )
+ ->method( 'insert' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->setMethods( [ 'newManualLogEntryForType' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'newManualLogEntryForType' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( $manualLogEntry ) );
+
+ $instance->registerLoggableEventType( 'Foo' );
+
+ $this->assertInternalType(
+ 'integer',
+ $instance->log( 'Foo', $performer, 'Baz', 'Yui' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MediaWikiNsContentReaderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MediaWikiNsContentReaderTest.php
new file mode 100644
index 00000000..3fc113d1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MediaWikiNsContentReaderTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\MediaWikiNsContentReader;
+
+/**
+ * @covers \SMW\MediaWiki\MediaWikiNsContentReader
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MediaWikiNsContentReaderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MediaWikiNsContentReader',
+ new MediaWikiNsContentReader()
+ );
+ }
+
+ public function testReadFromMessageCache() {
+
+ $instance = new MediaWikiNsContentReader();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->read( 'smw-desc' )
+ );
+ }
+
+ public function testTryToReadForInvalidTitle() {
+
+ $instance = new MediaWikiNsContentReader();
+
+ $this->assertEmpty(
+ $instance->read( '{}' )
+ );
+ }
+
+ public function testSkipMessageCache() {
+
+ $instance = new MediaWikiNsContentReader();
+ $instance->skipMessageCache();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->read( __METHOD__ )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MessageBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MessageBuilderTest.php
new file mode 100644
index 00000000..c09d9d44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MessageBuilderTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\MessageBuilder;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\MessageBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class MessageBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MessageBuilder',
+ new MessageBuilder( $language )
+ );
+ }
+
+ public function testFormatNumberToText() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'formatNum' );
+
+ $instance = new MessageBuilder();
+
+ $instance
+ ->setLanguage( $language )
+ ->formatNumberToText( 42 );
+ }
+
+ public function testListToCommaSeparatedText() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'listToText' );
+
+ $context = $this->getMockBuilder( '\IContextSource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $context->expects( $this->once() )
+ ->method( 'getLanguage' )
+ ->will( $this->returnValue( $language ) );
+
+ $instance = new MessageBuilder();
+
+ $instance
+ ->setLanguageFromContext( $context )
+ ->listToCommaSeparatedText( [ 'a', 'b' ] );
+ }
+
+ public function testPrevNextToText() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'viewPrevNext' );
+
+ $instance = new MessageBuilder( $language );
+ $instance->prevNextToText( $title, 20, 0, [], false );
+ }
+
+ public function testGetForm() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MessageBuilder( $language );
+
+ $this->assertInstanceOf(
+ '\Message',
+ $instance->getMessage( 'properties' )
+ );
+ }
+
+ public function testNullLanguageThrowsException() {
+
+ $instance = new MessageBuilder();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getMessage( 'properties' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MwCollaboratorFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MwCollaboratorFactoryTest.php
new file mode 100644
index 00000000..8fc196cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/MwCollaboratorFactoryTest.php
@@ -0,0 +1,263 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\MwCollaboratorFactory;
+
+/**
+ * @covers \SMW\MediaWiki\MwCollaboratorFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class MwCollaboratorFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = $this->getMockBuilder( '\SMW\ApplicationFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MwCollaboratorFactory',
+ new MwCollaboratorFactory( $this->applicationFactory )
+ );
+ }
+
+ public function testCanConstructMessageBuilder() {
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MessageBuilder',
+ $instance->newMessageBuilder()
+ );
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MessageBuilder',
+ $instance->newMessageBuilder( $language )
+ );
+ }
+
+ public function testCanConstructMagicWordsFinder() {
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MagicWordsFinder',
+ $instance->newMagicWordsFinder()
+ );
+ }
+
+ public function testCanConstructRedirectTargetFinder() {
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\RedirectTargetFinder',
+ $instance->newRedirectTargetFinder()
+ );
+ }
+
+ public function testCanConstructDeepRedirectTargetResolver() {
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->applicationFactory->expects( $this->atLeastOnce() )
+ ->method( 'newPageCreator' )
+ ->will( $this->returnValue( $pageCreator ) );
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\DeepRedirectTargetResolver',
+ $instance->newDeepRedirectTargetResolver()
+ );
+ }
+
+ public function testCanConstructHtmlFormRenderer() {
+
+ $instance = new MwCollaboratorFactory( new ApplicationFactory() );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlFormRenderer',
+ $instance->newHtmlFormRenderer( $title )
+ );
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlFormRenderer',
+ $instance->newHtmlFormRenderer( $title, $language )
+ );
+ }
+
+ public function testCanConstructHtmlTableRenderer() {
+
+ $instance = new MwCollaboratorFactory( new ApplicationFactory() );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlTableRenderer',
+ $instance->newHtmlTableRenderer()
+ );
+ }
+
+ public function testCanConstructHtmlColumnListRenderer() {
+
+ $instance = new MwCollaboratorFactory( new ApplicationFactory() );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlColumnListRenderer',
+ $instance->newHtmlColumnListRenderer()
+ );
+ }
+
+ public function testCanConstructLoadBalancerConnectionProvider() {
+
+ $instance = new MwCollaboratorFactory( new ApplicationFactory() );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Connection\LoadBalancerConnectionProvider',
+ $instance->newLoadBalancerConnectionProvider( DB_SLAVE )
+ );
+ }
+
+ public function testCanConstructConnectionProvider() {
+
+ $settings = $this->getMockBuilder( '\SMW\Settings' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $logger = $this->getMockBuilder( '\Psr\Log\LoggerInterface' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $settings->expects( $this->atLeastOnce() )
+ ->method( 'get' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->applicationFactory->expects( $this->atLeastOnce() )
+ ->method( 'getSettings' )
+ ->will( $this->returnValue( $settings ) );
+
+ $this->applicationFactory->expects( $this->atLeastOnce() )
+ ->method( 'getMediaWikiLogger' )
+ ->will( $this->returnValue( $logger ) );
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Connection\ConnectionProvider',
+ $instance->newConnectionProvider()
+ );
+ }
+
+ public function testCanConstructPageInfoProvider() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageInfoProvider',
+ $instance->newPageInfoProvider( $wikiPage )
+ );
+ }
+
+ public function testCanConstructEditInfoProvider() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\EditInfoProvider',
+ $instance->newEditInfoProvider( $wikiPage, $revision )
+ );
+ }
+
+ public function testCanConstructWikitextTemplateRenderer() {
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer',
+ $instance->newWikitextTemplateRenderer()
+ );
+ }
+
+ public function testCanConstructHtmlTemplateRenderer() {
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlTemplateRenderer',
+ $instance->newHtmlTemplateRenderer( $parser )
+ );
+ }
+
+ public function testCanConstructMediaWikiNsContentReader() {
+
+ $instance = new MwCollaboratorFactory(
+ $this->applicationFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\MediaWikiNsContentReader',
+ $instance->newMediaWikiNsContentReader()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageCreatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageCreatorTest.php
new file mode 100644
index 00000000..1d6d525a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageCreatorTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\PageCreator;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\MediaWiki\PageCreator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PageCreatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageCreator',
+ new PageCreator()
+ );
+ }
+
+ public function testCreatePage() {
+
+ $instance = new PageCreator();
+
+ $this->assertInstanceOf(
+ '\WikiPage',
+ $instance->createPage( MockTitle::buildMock( __METHOD__ ) )
+ );
+ }
+
+ public function testCreateFilePage() {
+
+ $instance = new PageCreator();
+
+ $this->assertInstanceOf(
+ '\WikiFilePage',
+ $instance->createFilePage( MockTitle::buildMock( __METHOD__ ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageInfoProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageInfoProviderTest.php
new file mode 100644
index 00000000..8f61da28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageInfoProviderTest.php
@@ -0,0 +1,368 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\PageInfoProvider;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\MediaWiki\PageInfoProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PageInfoProviderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $wikipage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageInfoProvider',
+ new PageInfoProvider( $wikipage )
+ );
+ }
+
+ public function testWikiPage_TYPE_MODIFICATION_DATE() {
+
+ $instance = $this->constructPageInfoProviderInstance(
+ [
+ 'wikiPage' => [ 'getTimestamp' => 1272508903 ],
+ 'revision' => [],
+ 'user' => [],
+ ]
+ );
+
+ $this->assertEquals( 1272508903, $instance->getModificationDate() );
+ }
+
+ public function testWikiPage_TYPE_CREATION_DATE() {
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision->expects( $this->any() )
+ ->method( 'getTimestamp' )
+ ->will( $this->returnValue( 1272508903 ) );
+
+ $title = MockTitle::buildMock( 'Lula' );
+
+ $title->expects( $this->any() )
+ ->method( 'getFirstRevision' )
+ ->will( $this->returnValue( $revision ) );
+
+ $instance = $this->constructPageInfoProviderInstance(
+ [
+ 'wikiPage' => [ 'getTitle' => $title ],
+ 'revision' => [],
+ 'user' => [],
+ ]
+ );
+
+ $this->assertEquals( 1272508903, $instance->getCreationDate() );
+ }
+
+ /**
+ * @dataProvider parentIdProvider
+ */
+ public function testWikiPage_TYPE_NEW_PAGE_ForRevision( $parentId, $expected ) {
+
+ $instance = $this->constructPageInfoProviderInstance(
+ [
+ 'wikiPage' => [],
+ 'revision' => [ 'getParentId' => $parentId ],
+ 'user' => [],
+ ]
+ );
+
+ $this->assertEquals( $expected, $instance->isNewPage() );
+ }
+
+ /**
+ * @dataProvider parentIdProvider
+ */
+ public function testWikiPage_TYPE_NEW_PAGE( $parentId, $expected ) {
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $revision->expects( $this->any() )
+ ->method( 'getParentId' )
+ ->will( $this->returnValue( $parentId ) );
+
+ $instance = $this->constructPageInfoProviderInstance(
+ [
+ 'wikiPage' => [ 'getRevision' => $revision ],
+ 'revision' => [ ],
+ 'user' => [],
+ ]
+ );
+
+ $this->assertEquals( $expected, $instance->isNewPage() );
+ }
+
+ public function parentIdProvider() {
+
+ $provider = [
+ [ 90001, false ],
+ [ null , true ]
+ ];
+
+ return $provider;
+ }
+
+ public function testWikiPage_TYPE_LAST_EDITOR() {
+
+ $userPage = MockTitle::buildMock( 'Lula' );
+
+ $userPage->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_USER ) );
+
+ $instance = $this->constructPageInfoProviderInstance(
+ [
+ 'wikiPage' => [],
+ 'revision' => [],
+ 'user' => [ 'getUserPage' => $userPage ],
+ ]
+ );
+
+ $this->assertEquals( $userPage, $instance->getLastEditor() );
+ }
+
+ public function constructPageInfoProviderInstance( array $parameters ) {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $parameters['wikiPage'] as $method => $returnValue ) {
+ $wikiPage->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnValue( $returnValue ) );
+ }
+
+ $revision = $this->getMockBuilder( '\Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $parameters['revision'] as $method => $returnValue ) {
+ $revision->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnValue( $returnValue ) );
+ }
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $parameters['user'] as $method => $returnValue ) {
+ $user->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnValue( $returnValue ) );
+ }
+
+ return new PageInfoProvider(
+ $wikiPage,
+ ( $parameters['revision'] !== [] ? $revision : null ),
+ ( $parameters['user'] !== [] ? $user : null )
+ );
+ }
+
+ /**
+ * @dataProvider uploadStatusWikiFilePageDataProvider
+ */
+ public function testWikiFilePage_TYPE_NEW_PAGE( $uploadStatus, $expected ) {
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiFilePage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ if ( $uploadStatus !== null ) {
+ $wikiFilePage->smwFileReUploadStatus = $uploadStatus;
+ }
+
+ $instance = new PageInfoProvider( $wikiFilePage );
+
+ $this->assertEquals( $expected, $instance->isNewPage() );
+ }
+
+ /**
+ * @dataProvider mediaTypeWikiFilePageDataProvider
+ */
+ public function testWikiFilePage_MEDIA_TYPE( $file, $expected ) {
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiFilePage' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isFilePage', 'getFile' ] )
+ ->getMock();
+
+ $wikiFilePage->expects( $this->any() )
+ ->method( 'isFilePage' )
+ ->will( $this->returnValue( true ) );
+
+ $wikiFilePage->expects( $this->any() )
+ ->method( 'getFile' )
+ ->will( $this->returnValue( $file ) );
+
+ $instance = new PageInfoProvider( $wikiFilePage );
+
+ $this->assertEquals( $expected, $instance->getMediaType() );
+ }
+
+ /**
+ * @dataProvider mimeTypeWikiFilePageDataProvider
+ */
+ public function testWikiFilePage_MIME_TYPE( $file, $expected ) {
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiFilePage' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isFilePage', 'getFile' ] )
+ ->getMock();
+
+ $wikiFilePage->expects( $this->any() )
+ ->method( 'isFilePage' )
+ ->will( $this->returnValue( true ) );
+
+ $wikiFilePage->expects( $this->any() )
+ ->method( 'getFile' )
+ ->will( $this->returnValue( $file ) );
+
+ $instance = new PageInfoProvider( $wikiFilePage );
+
+ $this->assertEquals( $expected, $instance->getMimeType() );
+ }
+
+ public function testWikiPage_MEDIA_TYPE() {
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PageInfoProvider( $wikiFilePage );
+
+ $this->assertEquals( null, $instance->getMediaType() );
+ }
+
+ public function testWikiPage_MIME_TYPE() {
+
+ $wikiFilePage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PageInfoProvider( $wikiFilePage );
+
+ $this->assertEquals( null, $instance->getMimeType() );
+ }
+
+ public function testWikiPage_NativeData() {
+
+ $content = $this->getMockBuilder( '\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content->expects( $this->any() )
+ ->method( 'getNativeData' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $content ) );
+
+ $instance = new PageInfoProvider( $wikiPage );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getNativeData()
+ );
+ }
+
+ public function testWikiPage_NativeData_Null() {
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( null ) );
+
+ $instance = new PageInfoProvider( $wikiPage );
+
+ $this->assertEquals(
+ '',
+ $instance->getNativeData()
+ );
+ }
+
+ public function uploadStatusWikiFilePageDataProvider() {
+
+ $provider = [
+ [ null, false ],
+ [ false, true ],
+ [ true , false ]
+ ];
+
+ return $provider;
+ }
+
+ public function mediaTypeWikiFilePageDataProvider() {
+
+ $fileWithMedia = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fileWithMedia->expects( $this->any() )
+ ->method( 'getMediaType' )
+ ->will( $this->returnValue( 'FooMedia' ) );
+
+ $fileNullMedia = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fileNullMedia->expects( $this->any() )
+ ->method( 'getMediaType' )
+ ->will( $this->returnValue( null ) );
+
+ $provider[] = [ $fileWithMedia, 'FooMedia' ];
+ $provider[] = [ $fileNullMedia, null ];
+
+ return $provider;
+ }
+
+ public function mimeTypeWikiFilePageDataProvider() {
+
+ $fileWithMime = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fileWithMime->expects( $this->any() )
+ ->method( 'getMimeType' )
+ ->will( $this->returnValue( 'FooMime' ) );
+
+ $fileNullMime = $this->getMockBuilder( '\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fileNullMime->expects( $this->any() )
+ ->method( 'getMediaType' )
+ ->will( $this->returnValue( null ) );
+
+ $provider[] = [ $fileWithMime, 'FooMime' ];
+ $provider[] = [ $fileNullMime, null ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageUpdaterTest.php
new file mode 100644
index 00000000..36094c68
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/PageUpdaterTest.php
@@ -0,0 +1,311 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\PageUpdater;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\PageUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setup();
+
+ $this->spyLogger = TestEnvironment::newSpyLogger();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\PageUpdater',
+ new PageUpdater()
+ );
+ }
+
+ public function testCanUpdate() {
+
+ $instance = new PageUpdater();
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->canUpdate()
+ );
+ }
+
+ /**
+ * @dataProvider purgeMethodProvider
+ */
+ public function testPurge( $purgeMethod, $titleMethod ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( $titleMethod );
+
+ $instance = new PageUpdater();
+ $instance->addPage( $title );
+
+ call_user_func( [ $instance, $purgeMethod ] );
+ }
+
+ public function testDisablePurgeHtmlCache() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->never() )
+ ->method( 'touchLinks' );
+
+ $instance = new PageUpdater();
+ $instance->addPage( $title );
+
+ $instance->isHtmlCacheUpdate( false );
+ $instance->doPurgeHtmlCache();
+ }
+
+ public function testFilterDuplicatePages() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->exactly( 2 ) )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( 'invalidateCache' );
+
+ $instance = new PageUpdater();
+ $instance->addPage( $title );
+ $instance->addPage( $title );
+
+ $instance->doPurgeParserCache();
+ }
+
+ /**
+ * @dataProvider purgeMethodProvider
+ */
+ public function testPurgeOnTransactionIdle( $purgeMethod, $titleMethod ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( $titleMethod );
+
+ $instance = new PageUpdater();
+ $instance->addPage( $title );
+
+ $instance->waitOnTransactionIdle();
+
+ call_user_func( [ $instance, $purgeMethod ] );
+
+ $instance->doUpdate();
+ }
+
+ /**
+ * @dataProvider purgeMethodProvider
+ */
+ public function testPurgeWillNotWaitOnTransactionIdleForMissingConnection( $purgeMethod, $titleMethod ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( $titleMethod );
+
+ $instance = new PageUpdater();
+ $instance->addPage( $title );
+
+ $instance->waitOnTransactionIdle();
+
+ call_user_func( [ $instance, $purgeMethod ] );
+
+ $instance->doUpdate();
+ }
+
+ /**
+ * @dataProvider purgeMethodProvider
+ */
+ public function testPurgeWillNotWaitOnTransactionIdleWhenCommandLineIsTrue( $purgeMethod, $titleMethod ) {
+
+ $this->connection->expects( $this->never() )
+ ->method( 'onTransactionIdle' );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->once() )
+ ->method( $titleMethod );
+
+ $instance = new PageUpdater( $this->connection );
+ $instance->addPage( $title );
+
+ $instance->waitOnTransactionIdle();
+
+ call_user_func( [ $instance, $purgeMethod ] );
+
+ $instance->doUpdate();
+ }
+
+ public function testAddNullPage() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->never() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new PageUpdater();
+ $instance->addPage( null );
+ }
+
+ /**
+ * @dataProvider purgeMethodProvider
+ */
+ public function testPushPendingWaitableUpdate( $purgeMethod, $titleMethod ) {
+
+ $transactionalCallableUpdate = $this->getMockBuilder( '\SMW\MediaWiki\Deferred\TransactionalCallableUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $transactionalCallableUpdate->expects( $this->once() )
+ ->method( 'pushUpdate' );
+
+ $transactionalCallableUpdate->expects( $this->once() )
+ ->method( 'setFingerprint' )
+ ->with( $this->equalTo( 'Foobar' ) );
+
+ $transactionalCallableUpdate->expects( $this->once() )
+ ->method( 'waitOnTransactionIdle' );
+
+ $transactionalCallableUpdate->expects( $this->once() )
+ ->method( 'markAsPending' );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new PageUpdater(
+ $this->connection,
+ $transactionalCallableUpdate
+ );
+
+ $instance->addPage( $title );
+ $instance->setFingerprint( 'Foobar' );
+
+ $instance->markAsPending();
+ $instance->waitOnTransactionIdle();
+
+ call_user_func( [ $instance, $purgeMethod ] );
+
+ $instance->pushUpdate();
+ }
+
+ public function testPurgeCacheAsPoolPurge() {
+
+ $row = new \stdClass;
+ $row->page_id = 42;
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'update' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback ); }
+ ) );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->never() )
+ ->method( 'invalidateCache' );
+
+ $instance = new PageUpdater( $this->connection );
+ $instance->setLogger( $this->spyLogger );
+ $instance->addPage( $title );
+
+ $instance->waitOnTransactionIdle();
+ $instance->doPurgeParserCacheAsPool();
+ }
+
+ public function purgeMethodProvider() {
+
+ $provider[] = [
+ 'doPurgeParserCache',
+ 'invalidateCache'
+ ];
+
+
+ $provider[] = [
+ 'doPurgeHtmlCache',
+ 'touchLinks'
+ ];
+
+ $provider[] = [
+ 'doPurgeWebCache',
+ 'purgeSquid'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/RedirectTargetFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/RedirectTargetFinderTest.php
new file mode 100644
index 00000000..0d0056ca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/RedirectTargetFinderTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\RedirectTargetFinder;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\RedirectTargetFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RedirectTargetFinderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\RedirectTargetFinder',
+ new RedirectTargetFinder()
+ );
+ }
+
+ /**
+ * @dataProvider redirectTextProvider
+ */
+ public function testFindRedirectTargetFromText( $text, $expectedHasTarget, $expectedGetTarget ) {
+
+ $instance = new RedirectTargetFinder();
+ $instance->findRedirectTargetFromText( $text );
+
+ $this->assertEquals(
+ $expectedHasTarget,
+ $instance->hasRedirectTarget()
+ );
+
+ $this->assertEquals(
+ $expectedGetTarget,
+ $instance->getRedirectTarget()
+ );
+ }
+
+ /**
+ * @dataProvider redirectTextProvider
+ */
+ public function testInjectedRedirectTargetOverridesTextFinder( $text ) {
+
+ $directRedirectTarget = Title::newFromText( 'Foo' );
+
+ $instance = new RedirectTargetFinder();
+ $instance->setRedirectTarget( $directRedirectTarget );
+ $instance->findRedirectTargetFromText( $text );
+
+ $this->assertTrue(
+ $instance->hasRedirectTarget()
+ );
+
+ $this->assertEquals(
+ $directRedirectTarget,
+ $instance->getRedirectTarget()
+ );
+ }
+
+ public function redirectTextProvider() {
+
+ $provider[] = [ '#REDIRECT [[:Lala]]', true, Title::newFromText( 'Lala' ) ];
+ $provider[] = [ '#REDIRECT [[Lala]]', true, Title::newFromText( 'Lala' ) ];
+ $provider[] = [ '[[:Lala]]', false, null ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlColumnListRendererTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlColumnListRendererTest.php
new file mode 100644
index 00000000..70e244ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlColumnListRendererTest.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace SMW\Test\MediaWiki\Renderer;
+
+use SMW\MediaWiki\Renderer\HtmlColumnListRenderer;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Renderer\HtmlColumnListRenderer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HtmlColumnListFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlColumnListRenderer',
+ new HtmlColumnListRenderer()
+ );
+ }
+
+ public function testDefaultColumnUnorderedList() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->addContentsByIndex( [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ] );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:100%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ul><li>Foo</li><li>Bar</li></ul>',
+ '<div class="smw-column-header">B</div><ul><li>Ichi</li><li>Ni</li></ul></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnUnorderedList() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->setNumberOfColumns( 2 );
+
+ $instance->addContentsByIndex( [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Baz', 'Fom', 'Fin', 'Fum' ]
+ ] );
+
+ $listContinuesAbbrev = wfMessage( 'listingcontinuesabbrev' )->text();
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">a</div>',
+ '<ul><li>Foo</li><li>Bar</li></ul>',
+ '<div class="smw-column-header">B</div><ul><li>Baz</li></ul></div> <!-- end column -->',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">B ' . $listContinuesAbbrev .'</div>',
+ '<ul start=4><li>Fom</li><li>Fin</li><li>Fum</li></ul></div> <!-- end column -->',
+ '<br style="clear: both;"/></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testThreeColumnUnorderedList() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->setNumberOfColumns( 3 );
+
+ $instance->addContentsByIndex( [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ] );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:33%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ul><li>Foo</li><li>Bar</li></ul></div>',
+ '<div class="smw-column" style="width:33%;" dir="ltr">',
+ '<div class="smw-column-header">B</div><ul><li>Ichi</li><li>Ni</li></ul></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnOrderedList() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance
+ ->setNumberOfColumns( 2 )
+ ->setListType( 'ol' );
+
+ $instance->addContentsByIndex( [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ] );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ol><li>Foo</li><li>Bar</li></ol></div> <!-- end column -->',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">B</div><ol><li>Ichi</li><li>Ni</li></ol></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnOrderedListNoHeader() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance
+ ->setNumberOfColumns( 2 )
+ ->setColumnListClass( 'foo-class' )
+ ->setColumnClass( 'bar-class' )
+ ->setListType( 'ul' );
+
+ $instance->addContentsByNoIndex(
+ [ 'Foo', 'Baz', 'Bar' ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="ltr">',
+ '<div class="bar-class" style="width:50%;" dir="ltr">',
+ '<ul start=1><li>Foo</li><li>Baz</li></ul></div> <!-- end column -->',
+ '<div class="bar-class" style="width:50%;" dir="ltr">',
+ '<ul start=3><li>Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testResponsiveColumnsToBeDeterminedByBrowser() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->setColumnListClass( 'foo-class' )
+ ->setNumberOfColumns( 2 ) // being set to 1 when it is responsive
+ ->setColumnClass( 'bar-responsive' )
+ ->setColumnRTLDirectionalityState( true )
+ ->setListType( 'ul' );
+
+ $instance->addContentsByNoIndex(
+ [ 'Foo', 'Baz', 'Bar' ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ul start=1><li>Foo</li><li>Baz</li><li>Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testItemListWithAttributes() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->setColumnListClass( 'foo-class' )
+ ->setNumberOfColumns( 2 ) // being set to 1 when it is responsive
+ ->setColumnClass( 'bar-responsive' )
+ ->setColumnRTLDirectionalityState( true )
+ ->setListType( 'ul' );
+
+ $instance->addContentsByNoIndex(
+ [ 'Foo', 'Baz', 'Bar' ]
+ );
+
+ $instance->setItemAttributes(
+ [
+ md5( 'Foo' ) => [
+ 'id' => 123
+ ],
+ md5( 'Bar' ) => 456
+ ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ul start=1><li id="123">Foo</li><li>Baz</li><li 0="456">Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testOListWithAttributes() {
+
+ $instance = new HtmlColumnListRenderer();
+
+ $instance->setColumnListClass( 'foo-class' )
+ ->setNumberOfColumns( 2 ) // being set to 1 when it is responsive
+ ->setColumnClass( 'bar-responsive' )
+ ->setColumnRTLDirectionalityState( true )
+ ->setListType( 'ol', 'i' );
+
+ $instance->addContentsByNoIndex(
+ [ 'Foo', 'Baz', 'Bar' ]
+ );
+
+ $instance->setItemAttributes(
+ [
+ md5( 'Foo' ) => [
+ 'id' => 123
+ ],
+ md5( 'Bar' ) => 456
+ ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ol type=i start=1><li id="123">Foo</li><li>Baz</li><li 0="456">Bar</li></ol></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlFormRendererTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlFormRendererTest.php
new file mode 100644
index 00000000..fde461ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlFormRendererTest.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Renderer;
+
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Renderer\HtmlFormRenderer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class HtmlFormRendererTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlFormRenderer',
+ new HtmlFormRenderer( $title, $messageBuilder )
+ );
+ }
+
+ public function testGetMessageBuilder() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new HtmlFormRenderer( $title, $messageBuilder );
+
+ $this->assertSame(
+ $messageBuilder,
+ $instance->getMessageBuilder()
+ );
+ }
+
+ public function testGetForm() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message->expects( $this->any() )
+ ->method( 'title' )
+ ->will( $this->returnSelf() );
+
+ $message->expects( $this->any() )
+ ->method( 'numParams' )
+ ->will( $this->returnSelf() );
+
+ $message->expects( $this->any() )
+ ->method( 'rawParams' )
+ ->will( $this->returnSelf() );
+
+ $message->expects( $this->any() )
+ ->method( 'text' )
+ ->will( $this->returnValue( 'SomeText' ) );
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder->expects( $this->any() )
+ ->method( 'getMessage' )
+ ->will( $this->returnValue( $message ) );
+
+ $instance = new HtmlFormRenderer( $title, $messageBuilder );
+
+ $instance
+ ->setName( 'SomeForm' )
+ ->withFieldset()
+ ->addParagraph( 'SomeDescription' )
+ ->addQueryParameter( 'SomeQueryParameter', 'SomeQueryValue' )
+ ->addPaging( 10, 0, 5 )
+ ->addHorizontalRule()
+ ->addInputField( 'SomeInputFieldLabel', 'foo', 'Foo', 'FooId', 333 )
+ ->addLineBreak()
+ ->addNonBreakingSpace()
+ ->addInputField( 'AnotherInputFieldLabel', 'AnotherInputFieldName', 'AnotherInputFieldValue' )
+ ->addSubmitButton( 'FindFoo' );
+
+ $expected = [
+ 'form id="smw-form-SomeForm" name="SomeForm" method="get"',
+ '<p class="smw-form-paragraph">SomeDescription</p>',
+ 'input name="foo" size="333" value="Foo" id="FooId"',
+ 'input name="AnotherInputFieldName" size="20" value="AnotherInputFieldValue" id="AnotherInputFieldName"',
+ 'input type="submit" value="FindFoo"',
+ //'<br />&nbsp;' MW 1.27 <br/>&nbsp;
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getForm()
+ );
+ }
+
+ public function testOptionsSelecList() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message->expects( $this->any() )
+ ->method( 'text' )
+ ->will( $this->returnValue( 'SomeText' ) );
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder->expects( $this->any() )
+ ->method( 'getMessage' )
+ ->will( $this->returnValue( $message ) );
+
+ $instance = new HtmlFormRenderer( $title, $messageBuilder );
+
+ $instance
+ ->setName( 'optionsSelecListForm' )
+ ->withFieldset()
+ ->setMethod( 'isNeithergetNorPostMethodUseDefaultInstead' )
+ ->addOptionSelectList(
+ 'optionlistLabel',
+ 'optionlistName',
+ 'b',
+ [ 'f' => 'foo', 'b' =>'bar' ],
+ 'optionslistId');
+
+ $expected = [
+ 'form id="smw-form-optionsSelecListForm" name="optionsSelecListForm" method="get"',
+ '<fieldset id="smw-form-fieldset-optionsSelecListForm">',
+ '<label for="optionslistId">optionlistLabel</label>&#160;',
+ '<select name="optionlistName" id="optionslistId" class="smw-form-select">',
+ '<option value="b" selected="">bar</option>',
+ '<option value="f">foo</option>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getForm()
+ );
+ }
+
+ public function testCheckbox() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message->expects( $this->any() )
+ ->method( 'text' )
+ ->will( $this->returnValue( 'SomeText' ) );
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder->expects( $this->any() )
+ ->method( 'getMessage' )
+ ->will( $this->returnValue( $message ) );
+
+ $instance = new HtmlFormRenderer( $title, $messageBuilder );
+
+ $instance
+ ->setName( 'checkboxForm' )
+ ->addHeader( 'invalidLevel', 'someHeader' )
+ ->withFieldset()
+ ->setMethod( 'post' )
+ ->setActionUrl( 'http://example.org/foo' )
+ ->addCheckbox(
+ 'checkboxLabel',
+ 'checkboxName',
+ true,
+ 'checkBoxId' );
+
+ $expected = [
+ '<form id="smw-form-checkboxForm" name="checkboxForm" method="post" action="http://example.org/foo">',
+ '<h2>someHeader</h2>',
+ '<fieldset id="smw-form-fieldset-checkboxForm">',
+ '<input name="checkboxName" type="checkbox" value="1" checked="checked" id="checkboxName" class="smw-form-checkbox" />',
+ '<label for="checkboxName" class="smw-form-checkbox">checkboxLabel</label>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getForm()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTableRendererTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTableRendererTest.php
new file mode 100644
index 00000000..6cb9b2b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTableRendererTest.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW\Test\MediaWiki\Renderer;
+
+use SMW\MediaWiki\Renderer\HtmlTableRenderer;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Renderer\HtmlTableRenderer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class HtmlTableRendererTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlTableRenderer',
+ new HtmlTableRenderer()
+ );
+ }
+
+ public function testAddHeaderItem() {
+
+ $instance = new HtmlTableRenderer();
+ $instance->addHeaderItem( 'span', 'lala' );
+
+ $this->stringValidator->assertThatStringContains(
+ '<span>lala</span>',
+ $instance->getHeaderItems()
+ );
+ }
+
+ public function testAddTableHeader() {
+
+ $instance = new HtmlTableRenderer();
+ $instance->addHeader( 'lala' );
+
+ $this->stringValidator->assertThatStringContains(
+ '<th>lala</th>',
+ $instance->getHtml()
+ );
+
+ $instance = new HtmlTableRenderer( true );
+ $instance->addHeader( 'lila' );
+
+ $this->stringValidator->assertThatStringContains(
+ '<thead><th>lila</th></thead>',
+ $instance->getHtml()
+ );
+ }
+
+ public function testAddTableRow() {
+
+ $instance = new HtmlTableRenderer();
+
+ $instance
+ ->addCell( 'lala', [ 'class' => 'foo' ] )
+ ->addRow()
+ ->addCell( 'lula' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains(
+ '<tr class="row-odd"><td class="foo">lala</td></tr><tr class="row-even"><td>lula</td></tr>',
+ $instance->getHtml()
+ );
+
+ $instance = new HtmlTableRenderer();
+
+ $instance
+ ->setHtmlContext( true )
+ ->addCell( 'lila' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains(
+ '<tbody><tr class="row-odd"><td>lila</td></tr></tbody>',
+ $instance->getHtml()
+ );
+ }
+
+ public function testStandardTable() {
+
+ $instance = new HtmlTableRenderer();
+
+ $instance
+ ->addCell( 'lala', [ 'rel' => 'tuuu' ] )
+ ->addRow( [ 'class' => 'foo' ] );
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><tr class="foo row-odd"><td rel="tuuu">lala</td></tr></table>',
+ $instance->getHtml()
+ );
+
+ $instance = new HtmlTableRenderer();
+
+ $instance
+ ->addHeader( 'lula' )
+ ->addCell( 'lala' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><th>lula</th><tr class="row-odd"><td>lala</td></tr></table>',
+ $instance->getHtml()
+ );
+
+ $instance = new HtmlTableRenderer( true );
+
+ $instance
+ ->addHeader( 'lula' )
+ ->addCell( 'lala' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><thead><th>lula</th></thead><tbody><tr class="row-odd"><td>lala</td></tr></tbody></table>',
+ $instance->getHtml()
+ );
+ }
+
+ public function testTransposedTable() {
+
+ $instance = new HtmlTableRenderer();
+
+ // We need a dedicated header definition to support a table transpose
+ $instance
+ ->transpose( true )
+ ->addHeader( 'Foo' )->addHeader( 'Bar' )
+ ->addCell( 'lala', [ 'class' => 'foo' ] )
+ ->addRow()
+ ->addCell( 'lula', [ 'rel' => 'tuuu' ] )->addCell( 'lila' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains(
+ '<table data-transpose="1"><tr class="row-odd"><th>Foo</th><td class="foo">lala</td><td rel="tuuu">lula</td></tr><tr class="row-even"><th>Bar</th><td></td><td>lila</td></tr></table>',
+ $instance->getHtml()
+ );
+
+ $instance = new HtmlTableRenderer( true );
+
+ $instance
+ ->transpose( true )
+ ->addHeader( 'Foo' )->addHeader( 'Bar' )
+ ->addCell( 'lala', [ 'class' => 'foo' ] )
+ ->addRow()
+ ->addCell( 'lula' )->addCell( 'lila' )
+ ->addRow();
+
+ $this->stringValidator->assertThatStringContains( // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ '<table data-transpose="1"><thead></thead><tbody><tr class="row-odd"><th>Foo</th><td class="foo">lala</td><td>lula</td></tr><tr class="row-even"><th>Bar</th><td></td><td>lila</td></tr></tbody></table>', // @codingStandardsIgnoreEnd
+ $instance->getHtml()
+ );
+ }
+
+ public function testEmptyTable() {
+
+ $instance = new HtmlTableRenderer();
+
+ $instance
+ ->addCell()
+ ->addRow();
+
+ $this->assertEmpty(
+ $instance->getHtml()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTemplateRendererTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTemplateRendererTest.php
new file mode 100644
index 00000000..0b355066
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/HtmlTemplateRendererTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Renderer;
+
+use SMW\MediaWiki\Renderer\HtmlTemplateRenderer;
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+
+/**
+ * @covers \SMW\MediaWiki\Renderer\HtmlTemplateRenderer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class HtmlTemplateRendererTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $wikitextTemplateRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\HtmlTemplateRenderer',
+ new HtmlTemplateRenderer( $wikitextTemplateRenderer, $parser )
+ );
+ }
+
+ public function testRenderTemplate() {
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parser->expects( $this->once() )
+ ->method( 'recursiveTagParse' )
+ ->with(
+ $this->stringContains( "{{Bar\n|property=Foo\n|value=42}}{{Foobaz\n|property=Bar\n|value=Foo}}" ) );
+
+ $instance = new HtmlTemplateRenderer(
+ new WikitextTemplateRenderer(),
+ $parser
+ );
+
+ $this->assertEmpty(
+ $instance->render()
+ );
+
+ $instance->addField( 'property', 'Foo' );
+ $instance->addField( 'value', 42 );
+
+ $instance->packFieldsForTemplate( 'Bar' );
+
+ $instance->addField( 'property', 'Bar' );
+ $instance->addField( 'value', 'Foo' );
+
+ $instance->packFieldsForTemplate( 'Foobaz' );
+
+ $instance->render();
+
+ $this->assertEmpty(
+ $instance->render()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/WikitextTemplateRendererTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/WikitextTemplateRendererTest.php
new file mode 100644
index 00000000..e4f1d4be
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Renderer/WikitextTemplateRendererTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Renderer;
+
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+
+/**
+ * @covers \SMW\MediaWiki\Renderer\WikitextTemplateRenderer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class WikitextTemplateRendererTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer',
+ new WikitextTemplateRenderer()
+ );
+ }
+
+ public function testRenderTemplate() {
+
+ $instance = new WikitextTemplateRenderer();
+
+ $this->assertEmpty(
+ $instance->render()
+ );
+
+ $instance->addField( 'property', 'Foo' );
+ $instance->addField( 'value', 42 );
+
+ $instance->packFieldsForTemplate( 'Bar' );
+
+ $instance->addField( 'property', 'Bar' );
+ $instance->addField( 'value', 'Foo' );
+
+ $instance->packFieldsForTemplate( 'Foobaz' );
+
+ $this->assertEquals(
+ "{{Bar\n|property=Foo\n|value=42}}{{Foobaz\n|property=Bar\n|value=Foo}}",
+ $instance->render()
+ );
+
+ $this->assertEmpty(
+ $instance->render()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/CustomFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/CustomFormTest.php
new file mode 100644
index 00000000..042d184b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/CustomFormTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\CustomForm;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\CustomForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CustomFormTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+
+ protected function setUp() {
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CustomForm::class,
+ new CustomForm( $this->webRequest )
+ );
+ }
+
+ public function testMakeFields() {
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'barproperty' ) )
+ ->will( $this->returnValue( [ 1001 ] ) );
+
+ $instance = new CustomForm(
+ $this->webRequest
+ );
+
+ $instance->isActiveForm( true );
+
+ $form = [
+ '<div class="smw-input-field" style="display:inline-block;">',
+ '<input class="smw-input" name="barproperty[]" value="1001" placeholder="Bar property ..." ',
+ 'data-property="Bar property" title="Bar property"/></div>'
+ ];
+
+ $this->assertContains(
+ implode( '', $form ),
+ $instance->makeFields( [ 'Bar property'] )
+ );
+
+ $this->assertEquals(
+ [
+ 'barproperty' => 1001
+ ],
+ $instance->getParameters()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FieldTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FieldTest.php
new file mode 100644
index 00000000..0a91b60d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FieldTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\Field;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\Field
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FieldTest extends \PHPUnit_Framework_TestCase {
+
+ public function testTooltip() {
+
+ $instance = new Field();
+
+ $this->assertContains(
+ 'Foo ... bar',
+ $instance->tooltip( [ 'tooltip' => 'Foo ... bar' ] )
+ );
+ }
+
+ public function testCreate() {
+
+ $instance = new Field();
+
+ $this->assertContains(
+ 'smw-input-field',
+ $instance->create( 'input', [] )
+ );
+
+ $this->assertContains(
+ 'smw-select',
+ $instance->create( 'select', [] )
+ );
+ }
+
+ public function testSelect() {
+
+ $instance = new Field();
+
+ $attr = [
+ 'list' => [
+ 'A' => 'Foo',
+ 'B' => [ 42, 'disabled' ]
+ ],
+ 'name' => 'Foobar'
+ ];
+
+ $html = $instance->select( $attr );
+
+ $this->assertContains(
+ '<select name="Foobar">',
+ $html
+ );
+
+ $this->assertContains(
+ "<option value='A'>Foo</option>",
+ $html
+ );
+
+ $this->assertContains(
+ "<option value='B' disabled>42</option>",
+ $html
+ );
+ }
+
+ /**
+ * @dataProvider inputAttributeProvider
+ */
+ public function testInput( $attr, $expected ) {
+
+ $instance = new Field();
+
+ $this->assertContains(
+ $expected,
+ $instance->input( $attr )
+ );
+ }
+
+ public function inputAttributeProvider() {
+
+ yield [
+ [ 'name' => 'Foobar' ],
+ '<div class="smw-input-field"><input name="Foobar" placeholder=""/></div>'
+ ];
+
+ yield [
+ [ 'name' => 'Foobar', 'multifield' => true ],
+ '<div class="smw-input-field"><input name="Foobar[]" placeholder=""/></div>'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsBuilderTest.php
new file mode 100644
index 00000000..f7db2ae3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsBuilderTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\FormsBuilder;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\FormsBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+ private $formsFactory;
+
+ protected function setUp() {
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->formsFactory = $this->getMockBuilder( '\SMW\MediaWiki\Search\Form\FormsFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FormsBuilder::class,
+ new FormsBuilder( $this->webRequest, $this->formsFactory )
+ );
+ }
+
+ public function testBuildForm() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $openForm = $this->getMockBuilder( '\SMW\MediaWiki\Search\Form\OpenForm' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $customForm = $this->getMockBuilder( '\SMW\MediaWiki\Search\Form\CustomForm' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $customForm->expects( $this->any() )
+ ->method( 'getParameters' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->formsFactory->expects( $this->any() )
+ ->method( 'newOpenForm' )
+ ->will( $this->returnValue( $openForm ) );
+
+ $this->formsFactory->expects( $this->any() )
+ ->method( 'newCustomForm' )
+ ->will( $this->returnValue( $customForm ) );
+
+ $instance = new FormsBuilder(
+ $this->webRequest,
+ $this->formsFactory
+ );
+
+ $data = [
+ 'forms' => [
+ 'Foo' => [
+ 'Property one'
+ ],
+ 'Bar' => [
+ 'Property two'
+ ]
+ ]
+ ];
+
+ $expected = [
+ "<div class='divider' style='display:none;'></div>",
+ '<div id="smw-form-definitions" class="is-disabled">',
+ '<div id="smw-form-bar" class="smw-fields"></div>',
+ '<div id="smw-form-foo" class="smw-fields"></div></div>'
+ ];
+
+ $this->assertContains(
+ implode( '', $expected ),
+ $instance->buildForm( $data )
+ );
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $expected = [
+ '<button type="button" id="smw-search-forms" class="smw-selectmenu-button is-disabled".*',
+ 'name="smw-form" value="".*',
+ 'data-list="[{&quot;id&quot;:&quot;bar&quot;,&quot;name&quot;:&quot;Bar&quot;,&quot;desc&quot;:&quot;Bar&quot;},{&quot;id&quot;:&quot;foo&quot;,&quot;name&quot;:&quot;Foo&quot;,&quot;desc&quot;:&quot;Foo&quot;}]" data-nslist="[]">Form</button><input type="hidden" name="smw-form"/>'
+ ];
+
+ $stringValidator->assertThatStringContains(
+ $expected,
+ $instance->buildFormList( $title )
+ );
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFactoryTest.php
new file mode 100644
index 00000000..a5fc0026
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFactoryTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\FormsFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\FormsFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+
+ protected function setUp() {
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FormsFactory::class,
+ new FormsFactory()
+ );
+ }
+
+ public function testCanConstructOpenForm() {
+
+ $instance = new FormsFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\Form\OpenForm',
+ $instance->newOpenForm( $this->webRequest )
+ );
+ }
+
+ public function testCanConstructCustomForm() {
+
+ $instance = new FormsFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\Form\CustomForm',
+ $instance->newCustomForm( $this->webRequest )
+ );
+ }
+
+ public function testCanConstructSortForm() {
+
+ $instance = new FormsFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\Form\SortForm',
+ $instance->newSortForm( $this->webRequest )
+ );
+ }
+
+ public function testCanConstructNamespaceForm() {
+
+ $instance = new FormsFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\Form\NamespaceForm',
+ $instance->newNamespaceForm()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFinderTest.php
new file mode 100644
index 00000000..697c42ba
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/FormsFinderTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Search\Form\FormsFinder;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\FormsFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormsFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FormsFinder::class,
+ new FormsFinder( $this->store )
+ );
+ }
+
+ public function testGetFormDefinitions() {
+
+ $data[] = json_encode( [ 'Foo' => [ 'Bar' => 42 ], 1001 ] );
+ $data[] = json_encode( [ 'Foo' => [ 'Foobar' => 'test' ], [ 'Foo' => 'Bar' ] ] );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ) ] ) );
+
+ $instance = $this->getMockBuilder( '\SMW\MediaWiki\Search\Form\FormsFinder' )
+ ->setConstructorArgs( [ $this->store ] )
+ ->setMethods( [ 'getNativeData' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'getNativeData' )
+ ->will( $this->onConsecutiveCalls( $data[0], $data[1] ) );
+
+ $this->assertEquals(
+ [
+ 'Foo' => [ 'Bar' => 42, 'Foobar' => 'test' ],
+ 1001,
+ [ 'Foo' => 'Bar' ]
+ ],
+ $instance->getFormDefinitions()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/NamespaceFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/NamespaceFormTest.php
new file mode 100644
index 00000000..6608de21
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/NamespaceFormTest.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\NamespaceForm;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\NamespaceForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NamespaceFormTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NamespaceForm::class,
+ new NamespaceForm()
+ );
+ }
+
+ public function testMakeFields() {
+
+ $instance = new NamespaceForm();
+
+ $instance->setSearchableNamespaces( [ 0 => 'Foo '] );
+
+ $this->assertContains(
+ "<fieldset id='mw-searchoptions'>",
+ $instance->makeFields()
+ );
+ }
+
+ public function testCheckNamespaceEditToken() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'getEditToken' );
+
+ $user->expects( $this->any() )
+ ->method( 'isLoggedIn' )
+ ->will( $this->returnValue( true ) );
+
+ $specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $specialSearch->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $user ) );
+
+ $instance = new NamespaceForm();
+
+ $instance->checkNamespaceEditToken( $specialSearch );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/OpenFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/OpenFormTest.php
new file mode 100644
index 00000000..fea0b999
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/OpenFormTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\OpenForm;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\OpenForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class OpenFormTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+
+ protected function setUp() {
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ OpenForm::class,
+ new OpenForm( $this->webRequest )
+ );
+ }
+
+ public function testMakeFields() {
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'property' ) )
+ ->will( $this->returnValue( [ 'Bar' ] ) );
+
+ $this->webRequest->expects( $this->at( 1 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'pvalue' ) )
+ ->will( $this->returnValue( [ 42 ] ) );
+
+ $this->webRequest->expects( $this->at( 2 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'op' ) )
+ ->will( $this->returnValue( [ 'OR' ] ) );
+
+ $instance = new OpenForm(
+ $this->webRequest
+ );
+
+ $instance->isActiveForm( true );
+
+ $this->assertContains(
+ '<div class="smw-input-group"><div class="smw-input-field" style="display:inline-block;">',
+ $instance->makeFields( [] )
+ );
+
+ $this->assertEquals(
+ [
+ 'property' => [ 'Bar' ],
+ 'pvalue' => [ 42 ],
+ 'op' => [ 'OR' ]
+ ],
+ $instance->getParameters()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/SortFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/SortFormTest.php
new file mode 100644
index 00000000..4040e1a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/Form/SortFormTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search\Form;
+
+use SMW\MediaWiki\Search\Form\SortForm;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Form\SortForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortFormTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+
+ protected function setUp() {
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SortForm::class,
+ new SortForm( $this->webRequest )
+ );
+ }
+
+ public function testMakeFields() {
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getVal' )
+ ->with( $this->equalTo( 'sort' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new SortForm(
+ $this->webRequest
+ );
+
+ $this->assertContains(
+ 'smw-search-sort',
+ $instance->makeFields( [] )
+ );
+
+ $this->assertEquals(
+ [ 'sort' => 'Foo' ],
+ $instance->getParameters()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/QueryBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/QueryBuilderTest.php
new file mode 100644
index 00000000..8904c47f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/QueryBuilderTest.php
@@ -0,0 +1,224 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\QueryBuilder;
+
+/**
+ * @covers \SMW\MediaWiki\Search\QueryBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $webRequest;
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ QueryBuilder::class,
+ new QueryBuilder( $this->webRequest )
+ );
+ }
+
+ public function testGetQuery() {
+
+ $instance = new QueryBuilder(
+ $this->webRequest
+ );
+
+ $this->assertNull(
+ $instance->getQuery( 'Foo' )
+ );
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ $instance->getQuery( '[[Foo::bar]]' )
+ );
+ }
+
+ public function testAddNamespaceCondition() {
+
+ $this->webRequest->expects( $this->any() )
+ ->method( 'getCheck' )
+ ->with($this->equalTo( 'ns6' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new QueryBuilder(
+ $this->webRequest
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ThingDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->once() )
+ ->method( 'setDescription' );
+
+ $instance->addNamespaceCondition( $query, [ 6 => true ] );
+ }
+
+ public function testAddSort() {
+
+ $this->webRequest->expects( $this->any() )
+ ->method( 'getVal' )
+ ->with($this->equalTo( 'sort' ) )
+ ->will( $this->returnValue( 'recent' ) );
+
+ $instance = new QueryBuilder(
+ $this->webRequest
+ );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'setSortKeys' );
+
+ $instance->addSort( $query );
+ }
+
+ public function testGetQueryString_EmptyFieldValues_ReturnsTermOnly() {
+
+ $instance = new QueryBuilder(
+ $this->webRequest,
+ [ 'foo' ]
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getQueryString( $this->store, 'Foo' )
+ );
+ }
+
+ public function testGetQueryString_FormFieldValues() {
+
+ $form_def = [
+ 'forms' => [
+ 'Foo' => [
+ 'Bar property'
+ ]
+ ]
+ ];
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getVal' )
+ ->with( $this->equalTo( 'smw-form' ) )
+ ->will( $this->returnValue( 'foo' ) );
+
+ $this->webRequest->expects( $this->at( 1 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'barproperty' ) )
+ ->will( $this->returnValue( [ 'Foobar' ] ) );
+
+ $instance = new QueryBuilder(
+ $this->webRequest,
+ $form_def
+ );
+
+ $this->assertEquals(
+ '<q>[[Bar property::Foobar]]</q> Foo',
+ $instance->getQueryString( $this->store, 'Foo' )
+ );
+ }
+
+ public function testGetQueryString_DifferentFormsFieldValues() {
+
+ $form_def = [
+ 'forms' => [
+ 'Foo-1' => [
+ 'Bar property'
+ ],
+ 'Foo-2' => [
+ 'Bar property'
+ ]
+ ]
+ ];
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getVal' )
+ ->with( $this->equalTo( 'smw-form' ) )
+ ->will( $this->returnValue( 'foo-2' ) );
+
+ $this->webRequest->expects( $this->at( 1 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'barproperty' ) )
+ ->will( $this->returnValue( [ '', 42 ] ) );
+
+ $instance = new QueryBuilder(
+ $this->webRequest,
+ $form_def
+ );
+
+ $this->assertEquals(
+ '<q>[[Bar property::42]]</q> Foo',
+ $instance->getQueryString( $this->store, 'Foo' )
+ );
+ }
+
+ public function testGetQueryString_OpenFormFieldValues() {
+
+ $form_def = [
+ 'forms' => [
+ 'open'
+ ]
+ ];
+
+ $this->webRequest->expects( $this->at( 0 ) )
+ ->method( 'getVal' )
+ ->with( $this->equalTo( 'smw-form' ) )
+ ->will( $this->returnValue( 'open' ) );
+
+ $this->webRequest->expects( $this->at( 1 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'property' ) )
+ ->will( $this->returnValue( [ 'Bar' ] ) );
+
+ $this->webRequest->expects( $this->at( 2 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'pvalue' ) )
+ ->will( $this->returnValue( [ 42 ] ) );
+
+ $this->webRequest->expects( $this->at( 3 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'op' ) )
+ ->will( $this->returnValue( [ 'OR' ] ) );
+
+ $instance = new QueryBuilder(
+ $this->webRequest,
+ $form_def
+ );
+
+ $this->assertEquals(
+ '<q>[[Bar::42]] </q> OR Foo',
+ $instance->getQueryString( $this->store, 'Foo' )
+ );
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchProfileFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchProfileFormTest.php
new file mode 100644
index 00000000..0cd521c9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchProfileFormTest.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\SearchProfileForm;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Search\SearchProfileForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SearchProfileFormTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $specialSearch;
+ private $requestContext;
+ private $outputPage;
+ private $webRequest;
+ private $user;
+ private $stringValidator;
+
+ protected function setUp() {
+
+ $this->stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->specialSearch = $this->getMockBuilder( '\SpecialSearch' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestContext = $this->getMockBuilder( '\RequestContext' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestContext->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->outputPage ) );
+
+ $this->requestContext->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->will( $this->returnValue( $this->webRequest ) );
+
+ $this->requestContext->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SearchProfileForm::class,
+ new SearchProfileForm( $this->store, $this->specialSearch )
+ );
+ }
+
+ public function testAddProfile() {
+
+ $profile = [];
+
+ SearchProfileForm::addProfile( 'SMWSearch', $profile );
+
+ $this->assertArrayHasKey(
+ 'smw',
+ $profile
+ );
+ }
+
+ public function testGetForm() {
+
+ $form = '';
+ $opts = [];
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $searchEngine = $this->getMockBuilder( '\SMW\MediaWiki\Search\Search' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->specialSearch->expects( $this->any() )
+ ->method( 'getSearchEngine' )
+ ->will( $this->returnValue( $searchEngine ) );
+
+ $this->specialSearch->expects( $this->any() )
+ ->method( 'getNamespaces' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->specialSearch->expects( $this->any() )
+ ->method( 'getUser' )
+ ->will( $this->returnValue( $this->user ) );
+
+ $this->specialSearch->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->requestContext ) );
+
+ $instance = new SearchProfileForm(
+ $this->store,
+ $this->specialSearch
+ );
+
+ $instance->getForm( $form, $opts );
+
+ $expected = [
+ '<fieldset id="smw-searchoptions">',
+ '<input type="hidden" name="ns-list"/>',
+ '<div class="smw-search-options">',
+ '<div class="smw-search-sort"><button type="button" id="smw-search-sort" class="smw-selectmenu-button is-disabled" name="sort"',
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $form
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultSetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultSetTest.php
new file mode 100644
index 00000000..997193c0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultSetTest.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\SearchResultSet;
+
+/**
+ * @covers \SMW\MediaWiki\Search\SearchResultSet
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Stephan Gambke
+ */
+class SearchResultSetTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var SearchResultSet The search result set under test
+ */
+ protected $resultSet;
+ private $queryResult;
+
+ protected function setUp() {
+
+ $queryToken = $this->getMockBuilder( '\SMW\Query\QueryToken' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryToken->expects( $this->any() )
+ ->method( 'getTokens' )
+ ->will( $this->returnValue( [] ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryToken' )
+ ->will( $this->returnValue( $queryToken ) );
+
+ $this->queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getQuery', 'getResults' ] )
+ ->getMock();
+
+ $pageMock = $this->getMockBuilder( 'SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageMock->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( null ) );
+
+ $this->queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->queryResult->expects( $this->any() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [ $pageMock, $pageMock, $pageMock ] ) );
+
+ $this->resultSet = new SearchResultSet( $this->queryResult, 42 );
+
+ }
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf( '\SMW\MediaWiki\Search\SearchResultSet', $this->resultSet );
+ }
+
+ public function testNumRows() {
+ $this->assertEquals( 3, $this->resultSet->numRows() );
+ }
+
+ public function testHasResults() {
+ $this->assertGreaterThan( 0, $this->resultSet->hasResults() );
+ }
+
+ public function testNext() {
+ $this->assertInstanceOf( 'SearchResult', $this->resultSet->next() );
+ $this->assertInstanceOf( 'SearchResult', $this->resultSet->next() );
+ $this->assertInstanceOf( 'SearchResult', $this->resultSet->next() );
+ $this->assertFalse( $this->resultSet->next() );
+ }
+
+ public function testExtractResults() {
+
+ $res = $this->resultSet->extractResults();
+
+ $this->assertCount(
+ 3,
+ $res
+ );
+
+ $this->assertEquals(
+ 3,
+ $this->resultSet->numRows()
+ );
+
+ foreach ( $res as $searchResult ) {
+ $this->assertInstanceOf(
+ 'SearchResult',
+ $searchResult
+ );
+ }
+ }
+
+ public function testSearchContainedSyntax() {
+ $this->assertTrue( $this->resultSet->searchContainedSyntax() );
+ }
+
+ public function testGetTotalHits() {
+ $this->assertEquals( 42, $this->resultSet->getTotalHits() );
+ }
+
+ public function testExcerpt() {
+
+ $excerpts = $this->getMockBuilder( 'SMW\Query\Excerpts' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $excerpts->expects( $this->any() )
+ ->method( 'getExcerpt' )
+ ->will( $this->returnValue( 'Foo ...' ) );
+
+ $this->queryResult->setExcerpts( $excerpts );
+
+ $resultSet = new SearchResultSet( $this->queryResult, 42 );
+ $searchResult = $resultSet->next();
+
+ $this->assertEquals(
+ 'Foo ...',
+ $searchResult->getExcerpt()
+ );
+ }
+
+ public function testTermMatches() {
+
+ $queryToken = $this->getMockBuilder( '\SMW\Query\QueryToken' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryToken->expects( $this->any() )
+ ->method( 'getTokens' )
+ ->will( $this->returnValue( [ 'Foo' => 1, 'Bar' => 2 ] ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryToken' )
+ ->will( $this->returnValue( $queryToken ) );
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getQuery', 'getResults' ] )
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $resultSet = new SearchResultSet( $queryResult, 42 );
+
+ $this->assertEquals(
+ [ '\bFoo\b', '\bBar\b' ],
+ $resultSet->termMatches()
+ );
+ }
+
+ public function testNewSearchSuggestionSet() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $page = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $page->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $queryToken = $this->getMockBuilder( '\SMW\Query\QueryToken' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryToken' )
+ ->will( $this->returnValue( $queryToken ) );
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [ $page ] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $resultSet = new SearchResultSet( $queryResult, 42 );
+
+ $this->assertInstanceOf(
+ '\SearchSuggestionSet',
+ $resultSet->newSearchSuggestionSet()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultTest.php
new file mode 100644
index 00000000..71649410
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchResultTest.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\SearchResult;
+
+/**
+ * @covers \SMW\MediaWiki\Search\SearchResult
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SearchResultTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetSectionTitle_WithFragment() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getFragment' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = SearchResult::newFromTitle( $title );
+
+ $this->assertInstanceOf(
+ '\Title',
+ $instance->getSectionTitle()
+ );
+ }
+
+ public function testGetSectionTitle_WithoutFragment() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getFragment' )
+ ->will( $this->returnValue( '' ) );
+
+ $instance = SearchResult::newFromTitle( $title );
+
+ $this->assertNull(
+ $instance->getSectionTitle()
+ );
+ }
+
+ public function testExcerpt() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getFragment' )
+ ->will( $this->returnValue( '' ) );
+
+ $instance = SearchResult::newFromTitle( $title );
+ $instance->setExcerpt( 'Foo ...' );
+
+ $this->assertEquals(
+ 'Foo ...',
+ $instance->getExcerpt()
+ );
+ }
+
+ public function testGetTitleSnippet() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getFragment' )
+ ->will( $this->returnValue( '' ) );
+
+ $instance = SearchResult::newFromTitle( $title );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getTitleSnippet()
+ );
+ }
+
+ public function testGetTextSnippet_HasHighlight() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = SearchResult::newFromTitle( $title );
+
+ $instance->setExcerpt( '<em>Foo</em>bar', true );
+
+ $this->assertEquals(
+ "<span class='searchmatch'>Foo</span>bar",
+ $instance->getTextSnippet( [ 'Foo' ] )
+ );
+ }
+
+ public function testGetTextSnippet_NoHighlight() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = SearchResult::newFromTitle( $title );
+
+ $instance->setExcerpt( 'Foobar' );
+
+ $this->assertEquals(
+ "<span class='searchmatch'>Foo</span>bar\n",
+ $instance->getTextSnippet( [ 'Foo' ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchTest.php
new file mode 100644
index 00000000..840cae3a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Search/SearchTest.php
@@ -0,0 +1,635 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Search;
+
+use SMW\MediaWiki\Search\Search;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+use SMWQuery;
+
+/**
+ * @covers \SMW\MediaWiki\Search\Search
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author Stephan Gambke
+ */
+class SearchTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Search\Search',
+ new Search()
+ );
+ }
+
+ public function testGetDefaultDBConnection() {
+
+ $search = new Search();
+
+ $this->assertInstanceOf(
+ 'DatabaseBase',
+ $search->getDB()
+ );
+ }
+
+ public function testSetGetDBConnection() {
+
+ $dbMock = $this->getMockBuilder( 'DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $search = new Search();
+ $search->setDB( $dbMock );
+
+ $this->assertEquals( $dbMock, $search->getDB() );
+ }
+
+ public function testGetDefaultFallbackSearchEngineForNullFallbackSearchType() {
+
+ $searchEngine = 'SearchDatabase';
+
+ if ( class_exists( 'SearchEngine' ) ) {
+
+ $reflection = new \ReflectionClass( 'SearchEngine' );
+
+ if ( $reflection->isInstantiable() ) {
+ $searchEngine = 'SearchEngine';
+ }
+ }
+
+ $databaseBase = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSearchEngine' ] )
+ ->getMockForAbstractClass();
+
+ $databaseBase->expects( $this->any() )
+ ->method( 'getSearchEngine' )
+ ->will( $this->returnValue( $searchEngine ) );
+
+ $this->testEnvironment->addConfiguration( 'smwgFallbackSearchType', null );
+
+ $search = new Search();
+ $search->setDB( $databaseBase );
+
+ $this->assertInstanceOf(
+ 'SearchEngine',
+ $search->getFallbackSearchEngine()
+ );
+ }
+
+ public function testInvalidFallbackSearchEngineThrowsException() {
+
+ $this->testEnvironment->addConfiguration( 'smwgFallbackSearchType', 'InvalidFallbackSearchEngine' );
+
+ $search = new Search();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $search->getFallbackSearchEngine();
+ }
+
+ public function testSetGetFallbackSearchEngine() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ $searchEngine,
+ $search->getFallbackSearchEngine()
+ );
+ }
+
+ public function testSearchTitle_withNonsemanticQuery() {
+
+ $term = 'Some string that can not be interpreted as a semantic query';
+
+ $searchResultSet = $this->getMockBuilder( 'SearchResultSet' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'transformSearchTerm' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'replacePrefixes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'searchTitle')
+ ->will( $this->returnValueMap( [ [ $term, $searchResultSet ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ $searchResultSet,
+ $search->searchTitle( $term )
+ );
+ }
+
+ public function testSearchTitle_withEmptyQuery() {
+
+ $term = ' ';
+
+ $searchResultSet = $this->getMockBuilder( 'SearchResultSet' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'transformSearchTerm' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'replacePrefixes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'searchTitle')
+ ->will( $this->returnValueMap( [ [ $term, $searchResultSet ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ $searchResultSet,
+ $search->searchTitle( $term )
+ );
+ }
+
+ public function testSearchText_withSemanticQuery() {
+
+ $term = '[[Some string that can be interpreted as a semantic query]]';
+
+ $infoLink = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQueryLink' )
+ ->will( $this->returnValue( $infoLink ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->exactly( 2 ) )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnCallback( function ( SMWQuery $query ) use ( $queryResult ) {
+ return $query->querymode === SMWQuery::MODE_COUNT ? 9001 : $queryResult;
+ } ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $search = new Search();
+ $result = $search->searchText( $term );
+
+ $this->assertInstanceOf(
+ 'SMW\MediaWiki\Search\SearchResultSet',
+ $result
+ );
+
+ $this->assertEquals(
+ 9001,
+ $result->getTotalHits()
+ );
+ }
+
+ public function testSearchText_withNonsemanticQuery() {
+
+ $term = 'Some string that can not be interpreted as a semantic query';
+
+ $searchResultSet = $this->getMockBuilder( 'SearchResultSet' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'transformSearchTerm' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'replacePrefixes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'searchText')
+ ->will( $this->returnValueMap( [ [ $term, $searchResultSet ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ $searchResultSet,
+ $search->searchText( $term )
+ );
+ }
+
+ public function testSearchTitle_withSemanticQuery() {
+
+ $term = '[[Some string that can be interpreted as a semantic query]]';
+
+ $search = new Search();
+
+ $this->assertNull( $search->searchTitle( $term ) );
+ }
+
+ public function testSupports() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'supports')
+ ->with( $this->equalTo( 'Some feature' ) )
+ ->will( $this->returnValueMap( [ [ 'Some feature', true ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertTrue( $search->supports( 'Some feature' ) );
+ }
+
+ public function testNormalizeText() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'normalizeText')
+ ->with( $this->equalTo( 'Some text' ) )
+ ->will( $this->returnValueMap( [ [ 'Some text', 'Some normalized text' ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ 'Some normalized text',
+ $search->normalizeText( 'Some text' )
+ );
+ }
+
+ public function testGetTextFromContent() {
+
+ if ( ! method_exists( 'SearchEngine', 'getTextFromContent' ) ) {
+ $this->markTestSkipped( 'SearchEngine::getTextFromContent() is undefined. Probably not yet present in the tested MW version.' );
+ }
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $content = $this->getMockBuilder( 'Content' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'getTextFromContent')
+ ->with(
+ $this->equalTo( $title ),
+ $this->equalTo( $content ) )
+ ->will( $this->returnValueMap( [ [ $title, $content, 'text from content for title' ] ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertEquals(
+ 'text from content for title',
+ $search->getTextFromContent( $title, $content )
+ );
+ }
+
+
+ public function testTextAlreadyUpdatedForIndex() {
+
+ if ( ! method_exists( 'SearchEngine', 'textAlreadyUpdatedForIndex' ) ) {
+ $this->markTestSkipped( 'SearchEngine::textAlreadyUpdatedForIndex() is undefined. Probably not yet present in the tested MW version.' );
+ }
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'textAlreadyUpdatedForIndex')
+ ->with()
+ ->will( $this->returnValue( true ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertTrue( $search->textAlreadyUpdatedForIndex( 'Some text' ) );
+ }
+
+ public function testUpdate() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'update')
+ ->with(
+ $this->equalTo( 42 ),
+ $this->equalTo( 'Some title' ),
+ $this->equalTo( 'Some text' ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $search->update( 42, 'Some title', 'Some text' );
+ }
+
+ public function testUpdateTitle() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'updateTitle')
+ ->with(
+ $this->equalTo( 42 ),
+ $this->equalTo( 'Some title' ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $search->updateTitle( 42, 'Some title' );
+ }
+
+ public function testDelete() {
+
+ if ( ! method_exists( 'SearchEngine', 'delete' ) ) {
+ $this->markTestSkipped( 'SearchEngine::delete() is undefined. Probably not yet present in the tested MW version.' );
+ }
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'delete')
+ ->with(
+ $this->equalTo( 42 ),
+ $this->equalTo( 'Some title' ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $search->delete( 42, 'Some title' );
+ }
+
+ public function testSetFeatureData() {
+
+ if ( ! method_exists( 'SearchEngine', 'delete' ) ) {
+ $this->markTestSkipped( 'SearchEngine::delete() is undefined. Probably not yet present in the tested MW version.' );
+ }
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'setFeatureData')
+ ->with(
+ $this->equalTo( 'Some feature name' ),
+ $this->equalTo( 'Some feature expression' ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+ $search->setFeatureData( 'Some feature name', 'Some feature expression' );
+
+ $this->assertEquals(
+ 'Some feature expression',
+ $search->getFeatureData( 'Some feature name' )
+ );
+
+ $this->assertNull( $search->getFeatureData( 'Some non-existent feature name' ) );
+ }
+
+ public function testReplacePrefixes() {
+
+ $search = new Search();
+
+ $this->assertEquals(
+ 'Some query',
+ $search->replacePrefixes( 'Some query' )
+ );
+ }
+
+ public function testTransformSearchTerm() {
+
+ $search = new Search();
+
+ $this->assertEquals(
+ 'Some query',
+ $search->transformSearchTerm( 'Some query' )
+ );
+ }
+
+ public function testSetLimitOffset() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'setLimitOffset')
+ ->with(
+ $this->equalTo( 9001 ),
+ $this->equalTo( 42 ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $search->setLimitOffset( 9001, 42 );
+
+ $this->assertEquals( 9001, $search->getLimit() );
+ $this->assertEquals( 42, $search->getOffset() );
+ }
+
+ public function testSetNamespaces() {
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'setNamespaces')
+ ->with( $this->equalTo( [ 1, 2, 3, 5, 8 ] ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+ $search->setNamespaces( [ 1, 2, 3, 5, 8 ] );
+
+ $this->assertEquals(
+ [ 1, 2, 3, 5, 8 ],
+ $search->namespaces
+ );
+ }
+
+ public function testSetShowSuggestion() {
+
+ if ( ! method_exists( 'SearchEngine', 'setShowSuggestion' ) ) {
+ $this->markTestSkipped( 'SearchEngine::setShowSuggestion() is undefined. Probably not yet present in the tested MW version.' );
+ }
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'setShowSuggestion')
+ ->with( $this->equalTo( true ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+ $search->setShowSuggestion( true );
+
+ $this->assertTrue( $search->getShowSuggestion() );
+ }
+
+ public function testCompletionSearch_OnEligiblePrefix() {
+
+ $infoLink = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQueryLink' )
+ ->will( $this->returnValue( $infoLink ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+
+ $this->assertInstanceof(
+ '\SearchSuggestionSet',
+ $search->completionSearch( 'in:Foo' )
+ );
+ }
+
+ public function testCompletionSearch_NoRelevantPrefix() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchSuggestionSet = $this->getMockBuilder( '\SearchSuggestionSet' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchSuggestionSet->expects( $this->any() )
+ ->method( 'map')
+ ->will( $this->returnValue( [] ) );
+
+ $searchEngine = $this->getMockBuilder( 'SearchEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'setShowSuggestion')
+ ->with( $this->equalTo( true ) );
+
+ $searchEngine->expects( $this->once() )
+ ->method( 'completionSearch' )
+ ->will( $this->returnValue( $searchSuggestionSet ) );
+
+ $search = new Search();
+ $search->setFallbackSearchEngine( $searchEngine );
+ $search->setShowSuggestion( true );
+
+ $this->assertInstanceof(
+ '\SearchSuggestionSet',
+ $search->completionSearch( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandlerTest.php
new file mode 100644
index 00000000..e2f0200a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/CacheStatisticsListTaskHandlerTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\CacheStatisticsListTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\CacheStatisticsListTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CacheStatisticsListTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CacheStatisticsListTaskHandler::class,
+ new CacheStatisticsListTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new CacheStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testIsTaskFor() {
+
+ $instance = new CacheStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertTrue(
+ $instance->isTaskFor( 'stats/cache')
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $instance = new CacheStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->handleRequest( $webRequest );
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/ConfigurationListTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/ConfigurationListTaskHandlerTest.php
new file mode 100644
index 00000000..93242fe8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/ConfigurationListTaskHandlerTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\ConfigurationListTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\ConfigurationListTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ConfigurationListTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConfigurationListTaskHandler::class,
+ new ConfigurationListTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new ConfigurationListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $instance = new ConfigurationListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DataRefreshJobTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DataRefreshJobTaskHandlerTest.php
new file mode 100644
index 00000000..85600d07
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DataRefreshJobTaskHandlerTest.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\DataRefreshJobTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\DataRefreshJobTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataRefreshJobTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\DataRefreshJobTaskHandler',
+ new DataRefreshJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new DataRefreshJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->getHtml();
+ }
+
+ public function testDoRefreshOn_Yes() {
+
+ $refreshJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\RefreshJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue->expects( $this->atLeastOnce() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'SMW\RefreshJob' ) )
+ ->will( $this->returnValue( true ) );
+
+ $this->jobQueue->expects( $this->atLeastOnce() )
+ ->method( 'pop' )
+ ->with( $this->equalTo( 'SMW\RefreshJob' ) )
+ ->will( $this->returnValue( false ) );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newByType' )
+ ->will( $this->returnValue( $refreshJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->atLeastOnce() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( 'yes' ) );
+
+ $instance = new DataRefreshJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_REFRESH );
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testDoRefreshOn_Stop() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'delete' )
+ ->with( $this->equalTo( 'SMW\RefreshJob' ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->atLeastOnce() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( 'stop' ) );
+
+ $instance = new DataRefreshJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_REFRESH );
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandlerTest.php
new file mode 100644
index 00000000..d4c75bf8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DeprecationNoticeTaskHandlerTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\DeprecationNoticeTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\DeprecationNoticeTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DeprecationNoticeTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DeprecationNoticeTaskHandler::class,
+ new DeprecationNoticeTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new DeprecationNoticeTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testGetHtmlWithFakeDetection() {
+
+ $GLOBALS['deprecationNoticeFoo'] = 'Foo';
+ $GLOBALS['deprecationNoticeFoobar'] = 'Foo';
+ $GLOBALS['deprecationNoticeFooFoo'] = 'Foo';
+
+ $deprecationNotice['smw'] = [
+ 'notice' => [
+ 'deprecationNoticeFoo' => '...',
+ 'options' => [
+ 'deprecationNoticeFoo' => [
+ 'Foo',
+ 'Bar'
+ ]
+ ]
+ ],
+ 'replacement' => [
+ 'deprecationNoticeFoobar' => '...',
+ 'options' => [
+ 'deprecationNoticeFoobar' => [
+ 'Foo',
+ 'Bar'
+ ]
+ ]
+ ],
+ 'removal' => [
+ 'deprecationNoticeFooFoo' => '...'
+ ]
+ ];
+
+ $instance = new DeprecationNoticeTaskHandler(
+ $this->outputFormatter,
+ $deprecationNotice
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testGetHtmlWithFakeDetectionArray() {
+
+ $GLOBALS['deprecationNoticeFoo'] = [ 'Bar' => false ];
+ $GLOBALS['deprecationNoticeFoobar'] = 'Foo';
+ $GLOBALS['deprecationNoticeFooFoo'] = 'Foo';
+
+ $deprecationNotice['smw'] = [
+ 'notice' => [
+ 'deprecationNoticeFoo' => '...',
+ 'options' => [
+ 'deprecationNoticeFoo' => [
+ 'Foo',
+ 'Bar'
+ ]
+ ]
+ ],
+ 'replacement' => [
+ 'deprecationNoticeFoobar' => '...',
+ 'options' => [
+ 'deprecationNoticeFoobar' => [
+ 'Foo',
+ 'Bar'
+ ]
+ ]
+ ],
+ 'removal' => [
+ 'deprecationNoticeFooFoo' => '...'
+ ]
+ ];
+
+ $instance = new DeprecationNoticeTaskHandler(
+ $this->outputFormatter,
+ $deprecationNotice
+ );
+
+ $this->assertContains(
+ '<div class="smw-admin-deprecation">',
+ $instance->getHtml()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DisposeJobTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DisposeJobTaskHandlerTest.php
new file mode 100644
index 00000000..de961220
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DisposeJobTaskHandlerTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\DisposeJobTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\DisposeJobTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DisposeJobTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\DisposeJobTaskHandler',
+ new DisposeJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new DisposeJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->getHtml();
+ }
+
+ public function testHandleRequestOnNonPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.entityIdDisposer' ) )
+ ->will( $this->returnValue( false ) );
+
+ $entityIdDisposerJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\EntityIdDisposerJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityIdDisposerJob->expects( $this->once() )
+ ->method( 'insert' );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->once() )
+ ->method( 'newByType' )
+ ->will( $this->returnValue( $entityIdDisposerJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DisposeJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->isApiTask = false;
+ $instance->setEnabledFeatures( SMW_ADM_DISPOSAL );
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testHandleRequestOnPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.entityIdDisposer' ) )
+ ->will( $this->returnValue( true ) );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->never() )
+ ->method( 'newByType' );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DisposeJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_DISPOSAL );
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DuplicateLookupTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DuplicateLookupTaskHandlerTest.php
new file mode 100644
index 00000000..0f8c1fb7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/DuplicateLookupTaskHandlerTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\DuplicateLookupTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\DuplicateLookupTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DuplicateLookupTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DuplicateLookupTaskHandler::class,
+ new DuplicateLookupTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new DuplicateLookupTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $this->outputFormatter->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $instance = new DuplicateLookupTaskHandler(
+ $this->outputFormatter
+ );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/EntityLookupTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/EntityLookupTaskHandlerTest.php
new file mode 100644
index 00000000..65927031
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/EntityLookupTaskHandlerTest.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\EntityLookupTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\EntityLookupTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EntityLookupTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $connection;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\EntityLookupTaskHandler',
+ new EntityLookupTaskHandler( $this->store, $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHml() {
+
+ $this->outputFormatter->expects( $this->any() )
+ ->method( 'getSpecialPageLinkWith' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'action' => 'lookup' ] ) );
+
+ $instance = new EntityLookupTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testPerformAction() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addInputField',
+ 'addSubmitButton',
+ 'addNonBreakingSpace',
+ 'addCheckbox'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new EntityLookupTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->atLeastOnce() )
+ ->method( 'matchEditToken' )
+ ->will( $this->returnValue( true ) );
+
+ $instance->setUser(
+ $user
+ );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testPerformActionWithId() {
+
+ $this->connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $manualEntryLogger = $this->getMockBuilder( '\SMW\MediaWiki\ManualEntryLogger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $manualEntryLogger->expects( $this->once() )
+ ->method( 'log' );
+
+ $this->testEnvironment->registerObject( 'ManualEntryLogger', $manualEntryLogger );
+
+ $entityIdDisposerJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\EntityIdDisposerJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->atLeastOnce() )
+ ->method( 'newEntityIdDisposerJob' )
+ ->will( $this->returnValue( $entityIdDisposerJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addInputField',
+ 'addSubmitButton',
+ 'addNonBreakingSpace',
+ 'addCheckbox'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->atLeastOnce() )
+ ->method( 'matchEditToken' )
+ ->will( $this->returnValue( true ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->at( 1 ) )
+ ->method( 'getText' )
+ ->with( $this->equalTo( 'id' ) )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest->expects( $this->at( 2 ) )
+ ->method( 'getText' )
+ ->with( $this->equalTo( 'dispose' ) )
+ ->will( $this->returnValue( 'yes' ) );
+
+ $webRequest->expects( $this->at( 3 ) )
+ ->method( 'getText' )
+ ->with( $this->equalTo( 'action' ) )
+ ->will( $this->returnValue( 'lookup' ) );
+
+ $instance = new EntityLookupTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_DISPOSAL );
+ $instance->setUser( $user );
+
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandlerTest.php
new file mode 100644
index 00000000..290c9368
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/FulltextSearchTableRebuildJobTaskHandlerTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\FulltextSearchTableRebuildJobTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\FulltextSearchTableRebuildJobTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableRebuildJobTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\FulltextSearchTableRebuildJobTaskHandler',
+ new FulltextSearchTableRebuildJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new FulltextSearchTableRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->getHtml();
+ }
+
+ public function testHandleRequestOnNonPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.fulltextSearchTableRebuild' ) )
+ ->will( $this->returnValue( false ) );
+
+ $fulltextSearchTableRebuildJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\FulltextSearchTableRebuildJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fulltextSearchTableRebuildJob->expects( $this->once() )
+ ->method( 'insert' );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->once() )
+ ->method( 'newByType' )
+ ->will( $this->returnValue( $fulltextSearchTableRebuildJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new FulltextSearchTableRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->isApiTask = false;
+ $instance->setEnabledFeatures( SMW_ADM_FULLT );
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testHandleRequestOnPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.fulltextSearchTableRebuild' ) )
+ ->will( $this->returnValue( true ) );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->never() )
+ ->method( 'newByType' );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new FulltextSearchTableRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_FULLT );
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandlerTest.php
new file mode 100644
index 00000000..8e7f144d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OperationalStatisticsListTaskHandlerTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\OperationalStatisticsListTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\OperationalStatisticsListTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OperationalStatisticsListTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ OperationalStatisticsListTaskHandler::class,
+ new OperationalStatisticsListTaskHandler( $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new OperationalStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testIsTaskFor() {
+
+ $instance = new OperationalStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $this->assertTrue(
+ $instance->isTaskFor( 'stats')
+ );
+ }
+
+ public function testHandleRequest() {
+
+ $semanticStatistics = [
+ 'PROPUSES' => 0,
+ 'ERRORUSES' => 0,
+ 'USEDPROPS' => 0,
+ 'TOTALPROPS' => 0,
+ 'OWNPAGE' => 0,
+ 'DECLPROPS' => 0,
+ 'DELETECOUNT' => 0,
+ 'SUBOBJECTS' => 0,
+ 'QUERY' => 0,
+ 'CONCEPTS' => 0
+ ];
+
+ $this->store->expects( $this->once() )
+ ->method( 'getStatistics' )
+ ->will( $this->returnValue( $semanticStatistics ) );
+
+ $this->outputFormatter->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $instance = new OperationalStatisticsListTaskHandler(
+ $this->outputFormatter
+ );
+
+ $instance->setStore( $this->store );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testHandleSubRequest() {
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $taskHandler = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\TaskHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $taskHandler->expects( $this->once() )
+ ->method( 'isTaskFor' )
+ ->will( $this->returnValue( true ) );
+
+ $taskHandler->expects( $this->once() )
+ ->method( 'handleRequest' )
+ ->with( $this->equalTo( $webRequest ) );
+
+ $instance = new OperationalStatisticsListTaskHandler(
+ $this->outputFormatter,
+ [ $taskHandler ]
+ );
+
+ $instance->setStore( $this->store );
+
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OutputFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OutputFormatterTest.php
new file mode 100644
index 00000000..3f0d7782
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/OutputFormatterTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\OutputFormatter;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\OutputFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OutputFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $outputPage;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\OutputFormatter',
+ new OutputFormatter( $this->outputPage )
+ );
+ }
+
+ public function testGetSpecialPageLinkWith() {
+
+ $instance = new OutputFormatter( $this->outputPage );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getSpecialPageLinkWith()
+ );
+ }
+
+ public function testEncodeAsJson() {
+
+ $instance = new OutputFormatter( $this->outputPage );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->encodeAsJson( [] )
+ );
+ }
+
+ public function testAddParentLink() {
+
+ $this->outputPage->expects( $this->once() )
+ ->method( 'prependHTML' );
+
+ $instance = new OutputFormatter( $this->outputPage );
+ $instance->addParentLink();
+ }
+
+ public function testSetPageTitle() {
+
+ $this->outputPage->expects( $this->once() )
+ ->method( 'setPageTitle' );
+
+ $instance = new OutputFormatter( $this->outputPage );
+ $instance->setPageTitle( 'Foo' );
+ }
+
+ public function testAddHTML() {
+
+ $this->outputPage->expects( $this->once() )
+ ->method( 'addHTML' );
+
+ $instance = new OutputFormatter( $this->outputPage );
+ $instance->addHTML( 'Foo' );
+ }
+
+ public function testAddWikiText() {
+
+ $this->outputPage->expects( $this->once() )
+ ->method( 'addWikiText' );
+
+ $instance = new OutputFormatter( $this->outputPage );
+ $instance->addWikiText( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandlerTest.php
new file mode 100644
index 00000000..cbccd77c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/PropertyStatsRebuildJobTaskHandlerTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\PropertyStatsRebuildJobTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\PropertyStatsRebuildJobTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatsRebuildJobTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+ private $jobQueue;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\PropertyStatsRebuildJobTaskHandler',
+ new PropertyStatsRebuildJobTaskHandler( $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new PropertyStatsRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->getHtml();
+ }
+
+ public function testHandleRequestOnNonPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.propertyStatisticsRebuild' ) )
+ ->will( $this->returnValue( false ) );
+
+ $propertyStatisticsRebuildJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsRebuildJob->expects( $this->once() )
+ ->method( 'insert' );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->once() )
+ ->method( 'newByType' )
+ ->will( $this->returnValue( $propertyStatisticsRebuildJob ) );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyStatsRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->isApiTask = false;
+ $instance->setEnabledFeatures( SMW_ADM_PSTATS );
+ $instance->handleRequest( $webRequest );
+ }
+
+ public function testHandleRequestOnPendingJob() {
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'hasPendingJob' )
+ ->with( $this->equalTo( 'smw.propertyStatisticsRebuild' ) )
+ ->will( $this->returnValue( true ) );
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->never() )
+ ->method( 'newByType' );
+
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyStatsRebuildJobTaskHandler(
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_PSTATS );
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/SupportListTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/SupportListTaskHandlerTest.php
new file mode 100644
index 00000000..4808a67b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/SupportListTaskHandlerTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\SupportListTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\SupportListTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SupportListTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\SupportListTaskHandler',
+ new SupportListTaskHandler( $this->htmlFormRenderer )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton',
+ 'setActionUrl'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new SupportListTaskHandler(
+ $this->htmlFormRenderer
+ );
+
+ $instance->getHtml();
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TableSchemaTaskHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TableSchemaTaskHandlerTest.php
new file mode 100644
index 00000000..d6d0d371
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TableSchemaTaskHandlerTest.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\TableSchemaTaskHandler;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\TableSchemaTaskHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableSchemaTaskHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\Admin\TableSchemaTaskHandler',
+ new TableSchemaTaskHandler( $this->store, $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new TableSchemaTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->getHtml();
+ }
+
+ public function testHandleRequest() {
+
+ $this->store->expects( $this->once() )
+ ->method( 'setup' );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->once() )
+ ->method( 'getVal' )
+ ->will( $this->returnValue( 'done' ) );
+
+ $instance = new TableSchemaTaskHandler(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $instance->setEnabledFeatures( SMW_ADM_SETUP );
+ $instance->handleRequest( $webRequest );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TaskHandlerFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TaskHandlerFactoryTest.php
new file mode 100644
index 00000000..446b7f9f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Admin/TaskHandlerFactoryTest.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Admin;
+
+use SMW\MediaWiki\Specials\Admin\TaskHandlerFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Admin\TaskHandlerFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TaskHandlerFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $outputFormatter;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->outputFormatter = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Admin\OutputFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TaskHandlerFactory::class,
+ new TaskHandlerFactory( $this->store, $this->htmlFormRenderer, $this->outputFormatter )
+ );
+ }
+
+ public function testGetTaskHandlerList() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $adminFeatures = '';
+
+ $instance = new TaskHandlerFactory(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTaskHandlerList( $user, $adminFeatures )
+ );
+ }
+
+ /**
+ * @dataProvider methodProvider
+ */
+ public function testCanConstructByFactory( $method, $expected ) {
+
+ $instance = new TaskHandlerFactory(
+ $this->store,
+ $this->htmlFormRenderer,
+ $this->outputFormatter
+ );
+
+ $this->assertInstanceOf(
+ $expected,
+ call_user_func( [ $instance, $method ] )
+ );
+ }
+
+ public function methodProvider() {
+
+ $provider[] = [
+ 'newTableSchemaTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\TableSchemaTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newSupportListTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\SupportListTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newConfigurationListTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\ConfigurationListTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newOperationalStatisticsListTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\OperationalStatisticsListTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newEntityLookupTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\EntityLookupTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newDataRefreshJobTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\DataRefreshJobTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newDisposeJobTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\DisposeJobTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newPropertyStatsRebuildJobTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\PropertyStatsRebuildJobTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newFulltextSearchTableRebuildJobTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\FulltextSearchTableRebuildJobTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newDeprecationNoticeTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\DeprecationNoticeTaskHandler'
+ ];
+
+ $provider[] = [
+ 'newDuplicateLookupTaskHandler',
+ '\SMW\MediaWiki\Specials\Admin\DuplicateLookupTaskHandler'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/DownloadLinksWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/DownloadLinksWidgetTest.php
new file mode 100644
index 00000000..52a8e7a0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/DownloadLinksWidgetTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\DownloadLinksWidget;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\DownloadLinksWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DownloadLinksWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testOnNull() {
+
+ $this->assertEmpty(
+ DownloadLinksWidget::downloadLinks( null )
+ );
+ }
+
+ public function testLinks() {
+
+ $infolink = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $infolink->expects( $this->atLeastOnce() )
+ ->method( 'setParameter' );
+
+ $this->assertContains(
+ '<div id="ask-export-links" class="smw-ask-downloadlinks export-links">',
+ DownloadLinksWidget::downloadLinks( $infolink )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ErrorWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ErrorWidgetTest.php
new file mode 100644
index 00000000..7a67c520
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ErrorWidgetTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\ErrorWidget;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\ErrorWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testSessionFailure() {
+
+ $this->assertInternalType(
+ 'string',
+ ErrorWidget::sessionFailure()
+ );
+ }
+
+ public function testNoScript() {
+
+ $this->assertInternalType(
+ 'string',
+ ErrorWidget::noScript()
+ );
+ }
+
+ public function testNoResult() {
+
+ $this->assertInternalType(
+ 'string',
+ ErrorWidget::noResult()
+ );
+ }
+
+ /**
+ * @dataProvider queryErrorProvider
+ */
+ public function testGetFormattedQueryErrorElement( $errors, $expected ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( $errors ) );
+
+ $this->assertEquals(
+ $expected,
+ ErrorWidget::queryError( $query )
+ );
+ }
+
+ public function queryErrorProvider() {
+
+ $provider[] = [
+ '',
+ ''
+ ];
+
+ $provider[] = [
+ [ 'Foo' ],
+ '<div id="result-error" class="smw-callout smw-callout-error">Foo</div>'
+ ];
+
+ $provider[] = [
+ [ 'Foo', 'Bar' ],
+ '<div id="result-error" class="smw-callout smw-callout-error"><ul><li>Foo</li><li>Bar</li></ul></div>'
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ 'Bar' ] ],
+ '<div id="result-error" class="smw-callout smw-callout-error"><ul><li>Foo</li><li>Bar</li></ul></div>'
+ ];
+
+ // Filter duplicate
+ $provider[] = [
+ [ 'Foo', [ 'Bar' ], 'Bar' ],
+ '<div id="result-error" class="smw-callout smw-callout-error"><ul><li>Foo</li><li>Bar</li></ul></div>'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/FormatListWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/FormatListWidgetTest.php
new file mode 100644
index 00000000..efceebd6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/FormatListWidgetTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\FormatListWidget;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\FormatListWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FormatListWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testEmptyParameters() {
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<span class="smw-ask-format-list"><input type="hidden" value="yes" name="eq"',
+ '<option value="broadtable" selected="">.*</option>'
+ ],
+ FormatListWidget::selectList( $title, [] )
+ );
+ }
+
+ public function testSetResultFormats() {
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ FormatListWidget::setResultFormats(
+ [
+ 'rdf' => 'SomeClassReference'
+ ]
+ );
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<option value="broadtable">.*</option>',
+ '<option data-isexport="1" value="rdf" selected="">.*</option>'
+ ],
+ FormatListWidget::selectList( $title, [ 'format' => 'rdf' ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HelpWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HelpWidgetTest.php
new file mode 100644
index 00000000..f1f9e204
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HelpWidgetTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\HelpWidget;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\HelpWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HelpWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testSessionFailure() {
+
+ $this->assertContains(
+ 'ask-help',
+ HelpWidget::html()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HtmlFormTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HtmlFormTest.php
new file mode 100644
index 00000000..eaa85204
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/HtmlFormTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\HtmlForm;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\HtmlForm
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlFormTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ HtmlForm::class,
+ new HtmlForm( $title )
+ );
+ }
+
+ public function testGetForm_IsEditMode() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getExtraPrintouts' )
+ ->will( $this->returnValue( [] ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->atLeastOnce() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance = new HtmlForm( $title );
+ $instance->isEditMode( true );
+
+ $text = '';
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getForm( $urlArgs, $queryResult, $text )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/LinksWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/LinksWidgetTest.php
new file mode 100644
index 00000000..152d494a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/LinksWidgetTest.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\LinksWidget;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\LinksWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testFieldset() {
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::fieldset()
+ );
+ }
+
+ public function testEmbeddedCodeLink() {
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::embeddedCodeLink()
+ );
+ }
+
+ public function testEmbeddedCodeBlock() {
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::embeddedCodeBlock( 'Foo' )
+ );
+ }
+
+ public function testResultSubmitLinkHide() {
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::resultSubmitLink( true )
+ );
+ }
+
+ public function testResultSubmitLinkShow() {
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::resultSubmitLink( false )
+ );
+ }
+
+ public function testShowHideLink() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::showHideLink( $title, $urlArgs )
+ );
+ }
+
+ public function testDebugLink() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::debugLink( $title, $urlArgs )
+ );
+ }
+
+ public function testNoQCacheLink() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::noQCacheLink( $title, $urlArgs, true )
+ );
+ }
+
+ public function testNoQCacheLinkOnFalseFromCache() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertEmpty(
+ LinksWidget::noQCacheLink( $title, $urlArgs, false )
+ );
+ }
+
+ public function testClipboardLink() {
+
+ $infolink = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ LinksWidget::clipboardLink( $infolink )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/NavigationLinksWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/NavigationLinksWidgetTest.php
new file mode 100644
index 00000000..6e0dc5da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/NavigationLinksWidgetTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\NavigationLinksWidget;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\NavigationLinksWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NavigationLinksWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testNavigation() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs->expects( $this->any() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->assertInternalType(
+ 'string',
+ NavigationLinksWidget::navigationLinks( $title, $urlArgs, 20, false )
+ );
+ }
+
+ public function testSetMaxInlineLimit() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs->expects( $this->any() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( [] ) );
+
+ NavigationLinksWidget::setMaxInlineLimit( 300 );
+
+ $result = NavigationLinksWidget::navigationLinks( $title, $urlArgs, 20, false );
+
+ $this->assertContains(
+ 'class="page-link">250</a>',
+ $result
+ );
+ }
+
+ public function testNavigationLinksOnZeroCountResult() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ NavigationLinksWidget::setMaxInlineLimit( 300 );
+
+ $result = NavigationLinksWidget::navigationLinks( $title, $urlArgs, 0, false );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+ public function testOffsetLimit() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $urlArgs = $this->getMockBuilder( '\SMW\MediaWiki\Specials\Ask\UrlArgs' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'get', 'set' ] )
+ ->getMock();
+
+ $urlArgs->expects( $this->at( 0 ) )
+ ->method( 'get' )
+ ->with( $this->equalTo( 'limit' ) )
+ ->will( $this->returnValue( 3 ) );
+
+ $urlArgs->expects( $this->at( 1 ) )
+ ->method( 'get' )
+ ->with( $this->equalTo( 'offset' ) )
+ ->will( $this->returnValue( 10 ) );
+
+ NavigationLinksWidget::setMaxInlineLimit( 300 );
+ NavigationLinksWidget::navigationLinks( $title, $urlArgs, 20, true );
+ }
+
+ public function testTopLinks() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertContains(
+ '<div id="ask-toplinks" class="smw-ask-toplinks"><span class="float-left"><a href="#options">',
+ NavigationLinksWidget::topLinks( $title, [ 'options' ] )
+ );
+
+ $this->assertContains(
+ '<div id="ask-toplinks" class="smw-ask-toplinks"><span class="float-left"></span>&#160;<span class="float-right">',
+ NavigationLinksWidget::topLinks( $title, [ 'empty' ] )
+ );
+ }
+
+ public function testHiddenTopLinks() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertEmpty(
+ NavigationLinksWidget::topLinks( $title, [] )
+ );
+ }
+
+ public function testBasicLinks() {
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<div class="smw-ask-actions-nav">foo</div>',
+ ],
+ NavigationLinksWidget::basicLinks( 'foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParameterInputTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParameterInputTest.php
new file mode 100644
index 00000000..93154657
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParameterInputTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\ParameterInput;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\ParameterInput
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParameterInputTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $paramDefinition = $this->getMockBuilder( '\ParamProcessor\ParamDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ParameterInput::class,
+ new ParameterInput( $paramDefinition, '' )
+ );
+ }
+
+ /**
+ * @dataProvider listValueProvider
+ */
+ public function testGetHtmlOnCheckboxList( $currentValue, $allowedValues, $expected ) {
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $paramDefinition = $this->getMockBuilder( '\ParamProcessor\ParamDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $paramDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getAllowedValues' )
+ ->will( $this->returnValue( $allowedValues ) );
+
+ $paramDefinition->expects( $this->any() )
+ ->method( 'isList' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new ParameterInput(
+ $paramDefinition,
+ $currentValue
+ );
+
+ $stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function listValueProvider() {
+
+ $provider[] = [
+ 'Foo',
+ [ 'Foo', 'Bar' ],
+ [
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Foo" checked="".*><tt>Foo</tt></span>',
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Bar".*><tt>Bar</tt></span>'
+ ],
+
+ ];
+
+ $provider[] = [
+ [ 'Foo' ],
+ [ 'Foo', 'Bar' ],
+ [
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Foo" checked="".*><tt>Foo</tt></span>',
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Bar".*><tt>Bar</tt></span>'
+ ],
+
+ ];
+
+ $provider[] = [
+ [ 'Foo, Bar' ],
+ [ 'Foo', 'Bar' ],
+ [
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Foo" checked="".*><tt>Foo</tt></span>',
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Bar" checked="".*><tt>Bar</tt></span>'
+ ],
+
+ ];
+
+ $provider[] = [
+ [ 'Foo,foo bar' ],
+ [ 'Foo', 'foo bar' ],
+ [
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="Foo" checked="".*><tt>Foo</tt></span>',
+ '<span class="parameter-checkbox-input" style="white-space: nowrap; padding-right: 5px;"><input type="checkbox" name="[]" value="foo bar" checked="".*><tt>foo bar</tt></span>'
+ ],
+
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersProcessorTest.php
new file mode 100644
index 00000000..532b9d48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersProcessorTest.php
@@ -0,0 +1,242 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\ParametersProcessor;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\ParametersProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParametersProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testEmpty() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'array',
+ ParametersProcessor::process( $request, [] )
+ );
+ }
+
+ public function testParameters() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parameters = [
+ '[[Foo::bar]]',
+ '|?Foo=foobar'
+ ];
+
+ $result = ParametersProcessor::process( $request, $parameters );
+
+ $this->assertEquals(
+ '[[Foo::bar]]',
+ $result[0]
+ );
+ }
+
+ public function testParameters_Printrequest_PlusPipe() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parameters = [
+ '[[Foo::bar]]',
+ '|?Foo=Bar|+index=1'
+ ];
+
+ $result = ParametersProcessor::process( $request, $parameters );
+
+ $this->assertEquals(
+ 'Bar|+index=1',
+ $result[1]['|?foo']
+ );
+ }
+
+ public function testParameters_Printrequest_WikiLink() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parameters = [
+ '[[Foo::bar]]',
+ '|?Foo=[[Some|other]]'
+ ];
+
+ $result = ParametersProcessor::process( $request, $parameters );
+
+ $this->assertEquals(
+ '[[Some|other]]',
+ $result[1]['|?foo']
+ );
+ }
+
+ public function testParameters_Printrequest_WikiLink_PlusPipe() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parameters = [
+ '[[Foo::bar]]',
+ '|?Foo=[[Some|other]]|+index=1'
+ ];
+
+ $result = ParametersProcessor::process( $request, $parameters );
+
+ $this->assertEquals(
+ '[[Some|other]]|+index=1',
+ $result[1]['|?foo']
+ );
+ }
+
+ public function testParametersWithDefaults() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request->expects( $this->at( 5 ) )
+ ->method( 'getVal' )
+ ->with(
+ $this->equalTo( 'offset' ),
+ $this->equalTo( 0 ) );
+
+ $request->expects( $this->at( 6 ) )
+ ->method( 'getVal' )
+ ->with(
+ $this->equalTo( 'limit' ),
+ $this->equalTo( 42 ) );
+
+ ParametersProcessor::setDefaultLimit( 42 );
+
+ $parameters = [
+ '[[Foo::bar]]',
+ '|?Foo=foobar'
+ ];
+
+ ParametersProcessor::process( $request, $parameters );
+ }
+
+ public function testParameters_Sort_FirstEmpty() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request->expects( $this->at( 3 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'sort_num' ) )
+ ->will( $this->returnValue( [ '', '', 'Foo' ] ) );
+
+ $request->expects( $this->at( 4 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'order_num' ) )
+ ->will( $this->returnValue( [ 'asc', 'desc' ] ) );
+
+ $parameters = [
+ '[[Foo::bar]]'
+ ];
+
+ list( $q, $p, $po ) = ParametersProcessor::process(
+ $request,
+ $parameters
+ );
+
+ $this->assertSame(
+ $p['sort'],
+ ',Foo'
+ );
+
+ $this->assertSame(
+ $p['order'],
+ 'asc,desc'
+ );
+ }
+
+ public function testParameters_Sort_FirstNotEmpty() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request->expects( $this->at( 3 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'sort_num' ) )
+ ->will( $this->returnValue( [ 'Foo', '' ] ) );
+
+ $request->expects( $this->at( 4 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'order_num' ) )
+ ->will( $this->returnValue( [ 'asc', 'desc' ] ) );
+
+ $parameters = [
+ '[[Foo::bar]]'
+ ];
+
+ list( $q, $p, $po ) = ParametersProcessor::process(
+ $request,
+ $parameters
+ );
+
+ $this->assertSame(
+ $p['sort'],
+ 'Foo'
+ );
+
+ $this->assertSame(
+ $p['order'],
+ 'asc'
+ );
+ }
+
+ public function testParametersOn_p_Array_Request() {
+
+ $request = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request->expects( $this->at( 0 ) )
+ ->method( 'getCheck' )
+ ->with( $this->equalTo( 'q' ) )
+ ->will( $this->returnValue( true ) );
+
+ $request->expects( $this->at( 1 ) )
+ ->method( 'getVal' )
+ ->with( $this->equalTo( 'p' ) )
+ ->will( $this->returnValue( '' ) );
+
+ $request->expects( $this->at( 2 ) )
+ ->method( 'getArray' )
+ ->with( $this->equalTo( 'p' ) )
+ ->will( $this->returnValue( [ 'foo' => [ 'Bar', 'foobar' ] ] ) );
+
+ $parameters = [];
+
+ $res = ParametersProcessor::process( $request, $parameters );
+
+ $this->assertEquals(
+ [
+ 'foo' => 'Bar,foobar',
+ 'format' => 'broadtable',
+ 'offset' => null,
+ 'limit' => null
+ ],
+ $res[1]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersWidgetTest.php
new file mode 100644
index 00000000..ffb6fb48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/ParametersWidgetTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\ParametersWidget;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\ParametersWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ParametersWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ $testEnvironment = new TestEnvironment();
+
+ $this->stringValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testFieldset() {
+
+ $parameters = [];
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<fieldset><legend>.*</legend><span class="smw-ask-format-list">',
+ '<input type="checkbox" id="options-toggle"/><div id="options-list" class="options-list"><div class="options-parameter-list">'
+ ],
+ ParametersWidget::fieldset( \Title::newFromText( 'Foo' ), $parameters )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testCreateParametersForm( $format, $parameters, $expected ) {
+
+ $parameters['format'] = $format;
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ ParametersWidget::parameterList( $parameters )
+ );
+ }
+
+ public function parametersProvider() {
+
+ $provider[] = [
+ '',
+ [],
+ '<div class="smw-table smw-ask-options-list" width="100%"><div class="smw-table-row smw-ask-options-row-odd"></div></div>'
+ ];
+
+ $provider[] = [
+ 'table',
+ [],
+ [
+ '<div class="smw-table smw-ask-options-list" width="100%"',
+ '<input class="parameter-number-input" size="6" style="width: 95%;" value="50" name="p[limit]"',
+ '<input class="parameter-number-input" size="6" style="width: 95%;" value="0" name="p[offset]"'
+ ]
+ ];
+
+ $provider[] = [
+ 'table',
+ [
+ 'limit' => 9999,
+ 'offset' => 42
+ ],
+ [
+ '<input class="parameter-number-input" size="6" style="width: 95%;" value="9999" name="p[limit]"',
+ '<input class="parameter-number-input" size="6" style="width: 95%;" value="42" name="p[offset]"'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/QueryInputWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/QueryInputWidgetTest.php
new file mode 100644
index 00000000..b7b0023e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/QueryInputWidgetTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\QueryInputWidget;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\QueryInputWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class QueryInputWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testInput() {
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<div class="smw-table" style="width: 100%;"><div class="smw-table-row">',
+ '<div class="smw-table-cell smw-ask-condition slowfade"><fieldset><legend>.*</legend><textarea id="ask-query-condition" class="smw-ask-query-condition" name="q" rows="6" placeholder="...">Foo</textarea>',
+ '<div class="smw-table-cell smw-ask-printhead slowfade"><fieldset><legend>.*</legend><textarea id="smw-property-input" class="smw-ask-query-printout" name="po" rows="6" placeholder="...">Bar</textarea>'
+ ],
+ QueryInputWidget::table( 'Foo', 'Bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/SortWidgetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/SortWidgetTest.php
new file mode 100644
index 00000000..04ab109c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/SortWidgetTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\SortWidget;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\SortWidget
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SortWidgetTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ $testEnvironment = new TestEnvironment();
+
+ $this->stringValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testDisabled() {
+
+ SortWidget::setSortingSupport( false );
+
+ $this->assertEmpty(
+ SortWidget::sortSection( [] )
+ );
+ }
+
+ public function testEnabledWithEmptyParameters() {
+
+ SortWidget::setSortingSupport( true );
+
+ $this->assertContains(
+ '<div id="options-sort" class="smw-ask-options-sort">',
+ SortWidget::sortSection( [] )
+ );
+ }
+
+ public function testEnabledWithParameters() {
+
+ SortWidget::setSortingSupport( true );
+ SortWidget::setRandSortingSupport( true );
+
+ $result = SortWidget::sortSection(
+ [
+ 'sort' => 'Foo,bar',
+ 'order' => 'asc,DESC'
+ ]
+ );
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<div id="sort_div_0" class="smw-ask-sort-input">',
+ '<input name="sort_num[]" size="35" class="smw-property-input autocomplete-arrow" value="Foo"',
+ '<select name="order_num[]"><option selected="selected" value="asc">'
+ ],
+ $result
+ );
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<div id="sort_div_1" class="smw-ask-sort-input">',
+ '<input name="sort_num[]" size="35" class="smw-property-input autocomplete-arrow" value="bar"',
+ '<select name="order_num[]"><option value="asc">.*</option><option selected="selected" value="desc">'
+ ],
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/UrlArgsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/UrlArgsTest.php
new file mode 100644
index 00000000..3eeafa8f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Ask/UrlArgsTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Ask;
+
+use SMW\MediaWiki\Specials\Ask\UrlArgs;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Ask\UrlArgs
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UrlArgsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testToString() {
+
+ $instance = new UrlArgs();
+
+ $instance->set( 'foo', 42 );
+ $instance->set( 'bar', 1001 );
+ $instance->setFragment( 'foobar' );
+
+ $this->assertContains(
+ 'foo=42&bar=1001#foobar',
+ $instance->__toString()
+ );
+ }
+
+ public function testGet() {
+
+ $instance = new UrlArgs();
+
+ $instance->set( 'foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'foo' )
+ );
+
+ $this->assertEquals(
+ null,
+ $instance->get( 42 )
+ );
+ }
+
+ public function testDelete() {
+
+ $instance = new UrlArgs();
+
+ $instance->set( 'foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'foo' )
+ );
+
+ $instance->delete( 'foo' );
+
+ $this->assertEquals(
+ null,
+ $instance->get( 'foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/FieldBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/FieldBuilderTest.php
new file mode 100644
index 00000000..351f113e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/FieldBuilderTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Browse;
+
+use SMW\MediaWiki\Specials\Browse\FieldBuilder;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Browse\FieldBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FieldBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCreateQueryForm() {
+
+ $this->assertInternalType(
+ 'string',
+ FieldBuilder::createQueryForm( 'Foo' )
+ );
+ }
+
+ public function testCreateLink() {
+
+ $parameters = [];
+
+ $this->assertInternalType(
+ 'string',
+ FieldBuilder::createLink( 'Foo', $parameters )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/GroupFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/GroupFormatterTest.php
new file mode 100644
index 00000000..a6412c4a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/GroupFormatterTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Browse;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Specials\Browse\GroupFormatter;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Browse\GroupFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class GroupFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ GroupFormatter::class,
+ new GroupFormatter( $this->propertySpecificationLookup )
+ );
+ }
+
+ public function testFindGroupMembership() {
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getPropertyGroup' )
+ ->will( $this->returnValue( new DIWikiPage( 'Bar', NS_CATEGORY ) ) );
+
+ $instance = new GroupFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $properties = [
+ new DIProperty( 'Foo' )
+ ];
+
+ $instance->findGroupMembership( $properties );
+
+ $this->assertTrue(
+ $instance->hasGroups()
+ );
+
+ $this->assertTrue(
+ $instance->isLastGroup( 'Bar' )
+ );
+
+ $this->assertArrayHasKey(
+ 'Bar',
+ $properties
+ );
+
+ $this->assertContains(
+ '<span class="group-link">',
+ $instance->getGroupLink( 'Bar' )
+ );
+ }
+
+ public function testFindGroupMembershipWhereShowGroupIsDisabled() {
+
+ $this->propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getPropertyGroup' )
+ ->will( $this->returnValue( new DIWikiPage( 'Bar', NS_CATEGORY ) ) );
+
+ $instance = new GroupFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $instance->showGroup( false );
+
+ $properties = [
+ new DIProperty( 'Foo' )
+ ];
+
+ $instance->findGroupMembership( $properties );
+
+ $this->assertFalse(
+ $instance->hasGroups()
+ );
+
+ $this->assertFalse(
+ $instance->isLastGroup( 'Bar' )
+ );
+ }
+
+ public function testGetMessageClassLink() {
+
+ $instance = new GroupFormatter(
+ $this->propertySpecificationLookup
+ );
+
+ $di = new DIWikiPage( 'Foo bar', NS_CATEGORY );
+
+ $this->assertContains(
+ 'smw-property-group-label-foo-bar',
+ $instance->getMessageClassLink( GroupFormatter::MESSAGE_GROUP_LABEL, $di )
+ );
+
+ $this->assertContains(
+ 'smw-property-group-description-foo-bar',
+ $instance->getMessageClassLink( GroupFormatter::MESSAGE_GROUP_DESCRIPTION, $di )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/HtmlBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/HtmlBuilderTest.php
new file mode 100644
index 00000000..68707252
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/HtmlBuilderTest.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Browse;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Specials\Browse\HtmlBuilder;
+use SMW\SemanticData;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Browse\HtmlBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class HtmlBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ // Disable a possible active hook execution
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgEnabledQueryDependencyLinksStore' => false
+ ] );
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ DIWikiPage::newFromText( 'Foo' )
+ );
+
+ $this->assertInstanceOf(
+ HtmlBuilder::class,
+ $instance
+ );
+ }
+
+ public function testBuildEmptyHTML() {
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ DIWikiPage::newFromText( 'Foo' )
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildEmptyHTML()
+ );
+ }
+
+ public function testBuildHTML() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( new SemanticData( $subject ) ) );
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildHTML()
+ );
+ }
+
+ public function testLegacy() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( new SemanticData( $subject ) ) );
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->legacy()
+ );
+ }
+
+ public function testPlaceholder() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( new SemanticData( $subject ) ) );
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->placeholder()
+ );
+ }
+
+ public function testBuildHTMLWithOptions() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( new SemanticData( $subject ) ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $options = [
+ 'offset' => 0,
+ 'showAll' => true,
+ 'showInverse' => false,
+ 'dir' => 'both',
+ 'printable' => ''
+ ];
+
+ $instance->setOptions(
+ $options
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildHTML()
+ );
+ }
+
+ public function testOptions() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $instance = new HtmlBuilder(
+ $this->store,
+ $subject
+ );
+
+ $options = [
+ 'Foo' => 42
+ ];
+
+ $instance->setOptions(
+ $options
+ );
+
+ $instance->setOption(
+ 'Bar',
+ 1001
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->getOption( 'Foo' )
+ );
+
+ $this->assertEquals(
+ 1001,
+ $instance->getOption( 'Bar' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/ValueFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/ValueFormatterTest.php
new file mode 100644
index 00000000..4996bced
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/Browse/ValueFormatterTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\Browse;
+
+use SMW\MediaWiki\Specials\Browse\ValueFormatter;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\Browse\ValueFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ValueFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testGetFormattedSubject() {
+
+ $dataItem = \SMW\DIWikiPage::newFromText( 'Foo', SMW_NS_PROPERTY );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getDataItem' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->once() )
+ ->method( 'getLongHTMLText' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItem ) );
+
+ $this->assertInternalType(
+ 'string',
+ ValueFormatter::getFormattedSubject( $dataValue )
+ );
+ }
+
+ public function testGetFormattedValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->once() )
+ ->method( 'getLongHTMLText' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $propertyValue = $this->getMockBuilder( '\SMWPropertyValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInternalType(
+ 'string',
+ ValueFormatter::getFormattedValue( $dataValue, $propertyValue )
+ );
+ }
+
+ public function testGetPropertyLabel() {
+
+ $propertyValue = $this->getMockBuilder( '\SMWPropertyValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyValue->expects( $this->once() )
+ ->method( 'isVisible' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyValue->expects( $this->once() )
+ ->method( 'getShortHTMLText' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->assertInternalType(
+ 'string',
+ ValueFormatter::getPropertyLabel( $propertyValue )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PageProperty/PageBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PageProperty/PageBuilderTest.php
new file mode 100644
index 00000000..6cdeeb28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PageProperty/PageBuilderTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\PageProperty;
+
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Specials\PageProperty\PageBuilder;
+use SMW\Options;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\PageProperty\PageBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PagePropertyTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $htmlFormRenderer;
+ private $options;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->options = new options();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PageBuilder::class,
+ new PageBuilder( $this->htmlFormRenderer, $this->options )
+ );
+ }
+
+ public function testbuildForm() {
+
+ $methods = [
+ 'setName',
+ 'withFieldset',
+ 'addHorizontalRule',
+ 'openElement',
+ 'closeElement',
+ 'addNonBreakingSpace',
+ 'addInputField',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addPaging',
+ 'addSubmitButton'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'renderForm' );
+
+ $instance = new PageBuilder(
+ $this->htmlFormRenderer,
+ $this->options
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildForm()
+ );
+ }
+
+ public function testbuildHtml_Empty() {
+
+ $instance = new PageBuilder(
+ $this->htmlFormRenderer,
+ $this->options
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildHtml( [] )
+ );
+ }
+
+ public function testbuildHtml_WithResult() {
+
+ $this->options->set( 'limit', 20 );
+ $this->options->set( 'property', 'Bar' );
+
+ $instance = new PageBuilder(
+ $this->htmlFormRenderer,
+ $this->options
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->buildHtml( [ DIWikiPage::newFromText( 'Foo' ) ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilderTest.php
new file mode 100644
index 00000000..04bbf7fd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/PropertyLabelSimilarity/ContentsBuilderTest.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\PropertyLabelSimilarity;
+
+use SMW\MediaWiki\Specials\PropertyLabelSimilarity\ContentsBuilder;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\PropertyLabelSimilarity\ContentsBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ContentsBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $propertyLabelSimilarityLookup;
+ private $htmlFormRenderer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->propertyLabelSimilarityLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->htmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ContentsBuilder::class,
+ new ContentsBuilder( $this->propertyLabelSimilarityLookup, $this->htmlFormRenderer )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestOptions->expects( $this->atLeastOnce() )
+ ->method( 'getExtraConditions' )
+ ->will( $this->returnValue( [ 'type' => 'Foo', 'threshold' => 50 ] ) );
+
+ $methods = [
+ 'setName',
+ 'setMethod',
+ 'addHiddenField',
+ 'addHeader',
+ 'addParagraph',
+ 'addSubmitButton',
+ 'withFieldset',
+ 'addInputField',
+ 'addNonBreakingSpace',
+ 'addCheckbox',
+ 'addQueryParameter',
+ 'addPaging'
+ ];
+
+ foreach ( $methods as $method ) {
+ $this->htmlFormRenderer->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnSelf() );
+ }
+
+ $this->htmlFormRenderer->expects( $this->atLeastOnce() )
+ ->method( 'getForm' );
+
+ $instance = new ContentsBuilder(
+ $this->propertyLabelSimilarityLookup,
+ $this->htmlFormRenderer
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml( $requestOptions )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageBuilderTest.php
new file mode 100644
index 00000000..7862a883
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageBuilderTest.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\SearchByProperty;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\MediaWiki\Renderer\HtmlFormRenderer;
+use SMW\MediaWiki\Specials\SearchByProperty\PageBuilder;
+use SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions;
+use SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SearchByProperty\PageBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+ private $localizer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ $this->localizer = Localizer::getInstance();
+ }
+
+ public function testCanConstruct() {
+
+ $HtmlFormRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\HtmlFormRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $PageRequestOptions = $this->getMockBuilder( '\SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $QueryResultLookup = $this->getMockBuilder( '\SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SearchByProperty\PageBuilder',
+ new PageBuilder( $HtmlFormRenderer, $PageRequestOptions, $QueryResultLookup )
+ );
+ }
+
+ public function testGetHtmlForExactValueSearch() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message->expects( $this->any() )
+ ->method( 'numParams' )
+ ->will( $this->returnSelf() );
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder->expects( $this->any() )
+ ->method( 'getMessage' )
+ ->will( $this->returnValue( $message ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [
+ new DIWikiPage( 'ResultOne', NS_MAIN ),
+ new DIWikiPage( 'ResultTwo', NS_HELP ) ] ) );
+
+ $instance = new PageBuilder(
+ new HtmlFormRenderer( $title, $messageBuilder ),
+ new PageRequestOptions( 'Foo/Bar', [] ),
+ new QueryResultLookup( $store )
+ );
+
+ $expected = [
+ 'value="Foo"',
+ 'value="Bar"',
+ 'title="ResultOne',
+ 'title="' . $this->localizer->getNamespaceTextById( NS_HELP ) . ':ResultTwo'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testGetHtmlForNearbyResultsSearch() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message = $this->getMockBuilder( '\Message' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $message->expects( $this->any() )
+ ->method( 'numParams' )
+ ->will( $this->returnSelf() );
+
+ $message->expects( $this->any() )
+ ->method( 'rawParams' )
+ ->will( $this->returnSelf() );
+
+ $messageBuilder = $this->getMockBuilder( '\SMW\MediaWiki\MessageBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageBuilder->expects( $this->any() )
+ ->method( 'getMessage' )
+ ->will( $this->returnValue( $message ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getNext' )
+ ->will( $this->returnValue( false ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [
+ new DIWikiPage( 'ResultOne', NS_MAIN ),
+ new DIWikiPage( 'ResultTwo', NS_HELP ) ] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $requestOptions = [
+ 'propertyString' => 'Foo',
+ 'valueString' => 'Bar',
+ 'nearbySearchForType' => [ '_wpg' ]
+ ];
+
+ $instance = new PageBuilder(
+ new HtmlFormRenderer( $title, $messageBuilder ),
+ new PageRequestOptions( 'Foo/Bar', $requestOptions ),
+ new QueryResultLookup( $store )
+ );
+
+ $expected = [
+ 'value="Foo"',
+ 'value="Bar"',
+ 'title="ResultOne',
+ 'title="' . $this->localizer->getNamespaceTextById( NS_HELP ) . ':ResultTwo'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageRequestOptionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageRequestOptionsTest.php
new file mode 100644
index 00000000..f7f4336f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/PageRequestOptionsTest.php
@@ -0,0 +1,286 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\SearchByProperty;
+
+use SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageRequestOptionsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $queryString = '';
+ $requestOptions = [];
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions',
+ new PageRequestOptions( $queryString, $requestOptions )
+ );
+ }
+
+ /**
+ * @dataProvider pageRequestOptionsProvider
+ */
+ public function testProcess( $queryString, $requestOptions, $expected ) {
+
+ $instance = new PageRequestOptions( $queryString, $requestOptions );
+ $instance->initialize();
+
+ foreach ( $expected as $key => $value ) {
+ $this->assertEquals( $expected[$key], $instance->$key, "$key" );
+ }
+
+ $this->assertInstanceOf(
+ 'SMWPropertyValue',
+ $instance->property
+ );
+ }
+
+ public function pageRequestOptionsProvider() {
+
+ #0
+ $provider[] = [
+ '',
+ [],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'propertyString' => '',
+ 'valueString' => '',
+ 'value' => null
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ 'Foo',
+ [],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'propertyString' => 'Foo',
+ 'valueString' => '',
+ ]
+ ];
+
+ #2
+ $provider[] = [
+ 'Foo_nu/Bar',
+ [],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'propertyString' => 'Foo nu',
+ 'valueString' => 'Bar',
+ 'nearbySearch' => false
+ ]
+ ];
+
+ #3 @see 516
+ $provider[] = [
+ ':Foo("#^$&--2F)/("#^$&-)Bar',
+ [],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'propertyString' => 'Foo("#^$&-/)',
+ 'valueString' => '("#^$&-)Bar',
+ 'nearbySearch' => false
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ 'Foo("#^$&--2F)/("#^$&-)Bar',
+ [
+ 'property' => '("#^$&-/)æŽç§€è‹±',
+ 'value' => '田中("#^$&-)',
+ 'nearbySearchForType' => true
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => '("#^$&-/)æŽç§€è‹±',
+ 'valueString' => '田中("#^$&-)',
+ ]
+ ];
+
+ #5
+ $provider[] = [
+ '',
+ [
+ 'property' => ' Foo ',
+ 'value' => '',
+ 'nearbySearchForType' => [ '_txt' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => 'Foo',
+ 'valueString' => '',
+ ]
+ ];
+
+ #6
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Foo',
+ 'value' => '',
+ 'nearbySearchForType' => [ '_wpg' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => true,
+ 'propertyString' => 'Foo',
+ 'valueString' => '',
+ ]
+ ];
+
+ #7
+ $provider[] = [
+ '',
+ [
+ 'property' => '',
+ 'value' => 'Foo',
+ 'nearbySearchForType' => [ '_wpg' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => '',
+ 'valueString' => 'Foo',
+ ]
+ ];
+
+ #9
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Number',
+ 'value' => '2',
+ 'nearbySearchForType' => [ '_wpg' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => 'Number',
+ 'valueString' => '2.0',
+ ]
+ ];
+
+ #10
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Temperature',
+ 'value' => '373,15 K',
+ 'nearbySearchForType' => [ '_wpg' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => 'Temperature',
+ 'valueString' => '373,15 K',
+ ]
+ ];
+
+ #10
+ $provider[] = [
+ ':Temperature/373,15-20K',
+ [
+ 'nearbySearchForType' => [ '_wpg' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => false,
+ 'propertyString' => 'Temperature',
+ 'valueString' => '373,15 K',
+ ]
+ ];
+
+ #11
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Telephone number',
+ 'value' => '%2B1-201-555-0123',
+ 'nearbySearchForType' => [ '_tel' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => true,
+ 'propertyString' => 'Telephone number',
+ 'valueString' => '%2B1-201-555-0123',
+ ]
+ ];
+
+ #11
+ $provider[] = [
+ ':Telephone number/%2B1-2D201-2D555-2D0123',
+ [
+ 'nearbySearchForType' => [ '_tel' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => true,
+ 'propertyString' => 'Telephone number',
+ 'valueString' => '%2B1-201-555-0123',
+ ]
+ ];
+
+ #12
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Text',
+ 'value' => 'abc-123',
+ 'nearbySearchForType' => [ '_txt' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => true,
+ 'propertyString' => 'Text',
+ 'valueString' => 'abc-123',
+ ]
+ ];
+
+ #13
+ $provider[] = [
+ '',
+ [
+ 'property' => 'Text',
+ 'value' => 'foo-123#&^*%<1?=/->"\'',
+ 'nearbySearchForType' => [ '_txt' ]
+ ],
+ [
+ 'limit' => 20,
+ 'offset' => 0,
+ 'nearbySearch' => true,
+ 'propertyString' => 'Text',
+ 'valueString' => 'foo-123#&^*%<1?=/->"\'',
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/QueryResultLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/QueryResultLookupTest.php
new file mode 100644
index 00000000..527c9478
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SearchByProperty/QueryResultLookupTest.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials\SearchByProperty;
+
+use SMW\MediaWiki\Specials\SearchByProperty\PageRequestOptions;
+use SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class QueryResultLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SearchByProperty\QueryResultLookup',
+ new QueryResultLookup( $store )
+ );
+ }
+
+ public function testDoQueryForNonValue() {
+
+ $pageRequestOptions = new PageRequestOptions( 'Foo', [] );
+ $pageRequestOptions->initialize();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->isType( 'null' ),
+ $this->isInstanceOf( '\SMW\DIProperty' ),
+ $this->anything() )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryResultLookup( $store );
+
+ $this->assertInternaltype(
+ 'array',
+ $instance->doQuery( $pageRequestOptions )
+ );
+ }
+
+ public function testDoQueryForExactValue() {
+
+ $pageRequestOptions = new PageRequestOptions( 'Foo/Bar', [] );
+ $pageRequestOptions->initialize();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->isInstanceOf( '\SMW\DIProperty' ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryResultLookup( $store );
+
+ $this->assertInternaltype(
+ 'array',
+ $instance->doQuery( $pageRequestOptions )
+ );
+ }
+
+ public function testDoQueryForNearbyResults() {
+
+ $pageRequestOptions = new PageRequestOptions( 'Foo/Bar', [] );
+ $pageRequestOptions->initialize();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getNext' )
+ ->will( $this->returnValue( false ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getQueryResult' )
+ ->with( $this->isInstanceOf( '\SMWQuery' ) )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $instance = new QueryResultLookup( $store );
+
+ $this->assertInternaltype(
+ 'array',
+ $instance->doQueryForNearbyResults( $pageRequestOptions, 1 )
+ );
+ }
+
+ public function testDoQueryLinksReferences() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getId' ] )
+ ->getMock();
+
+ $idTable->expects( $this->atLeastOnce() )
+ ->method( 'getId' )
+ ->will( $this->onConsecutiveCalls( 42 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $pageRequestOptions = new PageRequestOptions( 'Foo/Bar', [] );
+ $pageRequestOptions->initialize();
+
+ $instance = new QueryResultLookup( $store );
+
+ $this->assertInternaltype(
+ 'array',
+ $instance->doQueryLinksReferences( $pageRequestOptions, 1 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAdminTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAdminTest.php
new file mode 100644
index 00000000..77b42be9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAdminTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialAdmin;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialAdmin
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpecialAdminTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SpecialAdmin',
+ new SpecialAdmin()
+ );
+ }
+
+ public function testExecuteWithValidUser() {
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $query = '';
+ $instance = new SpecialAdmin();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SemanticMadiaWiki' )
+ );
+
+ $oldOutput = $instance->getOutput();
+
+ $instance->getContext()->setOutput( $outputPage );
+ $instance->getContext()->setUser( new MockSuperUser() );
+
+ $instance->execute( $query );
+
+ // Context is static avoid any succeeding tests to fail
+ $instance->getContext()->setOutput( $oldOutput );
+ }
+
+ public function testExecuteWithInvalidPermissionThrowsException() {
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = '';
+ $instance = new SpecialAdmin();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SemanticMadiaWiki' )
+ );
+
+ $instance->getContext()->setUser( $user );
+
+ $this->setExpectedException( 'PermissionsError' );
+ $instance->execute( $query );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAskTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAskTest.php
new file mode 100644
index 00000000..1eea2f73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialAskTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialAsk;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialAsk
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SpecialAskTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SpecialAsk',
+ new SpecialAsk()
+ );
+ }
+
+ public function testExecuteWithValidUser() {
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' );
+
+ $query = '';
+ $instance = new SpecialAsk();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SemanticMadiaWiki' )
+ );
+
+ $oldOutput = $instance->getOutput();
+
+ $instance->getContext()->setOutput( $outputPage );
+ $instance->getContext()->setUser( new MockSuperUser() );
+
+ $instance->execute( $query );
+
+ // Context is static avoid any succeeding tests to fail
+ $instance->getContext()->setOutput( $oldOutput );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialBrowseTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialBrowseTest.php
new file mode 100644
index 00000000..e12cad78
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialBrowseTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialBrowse;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialBrowse
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SpecialBrowseTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [
+ 'smwgBrowseFeatures' => SMW_BROWSE_SHOW_INCOMING | SMW_BROWSE_USE_API
+ ] );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testQueryParameter( $query, $expected ) {
+
+ $instance = new SpecialBrowse();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SpecialBrowse' )
+ );
+
+ $instance->execute( $query );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getOutput()->getHtml()
+ );
+ }
+
+ public function queryParameterProvider() {
+
+ #0
+ $provider[] = [
+ '',
+ [ 'smw-callout smw-callout-error' ]
+ ];
+
+ #1
+ $provider[] = [
+ ':Has-20foo/http:-2F-2Fexample.org-2Fid-2FCurly-2520Brackets-257B-257D',
+ [ 'smw-callout smw-callout-error' ]
+ ];
+
+ #2
+ $provider[] = [
+ 'Foo/Bar',
+ [
+ 'data-subject="Foo/Bar#0##"',
+ 'data-options="{&quot;dir&quot;:null,&quot;group&quot;:null,&quot;printable&quot;:null,&quot;offset&quot;:null,&quot;including&quot;:null,&quot;showInverse&quot;:false,&quot;showAll&quot;:true,&quot;showGroup&quot;:false,&quot;showSort&quot;:false,&quot;api&quot;:true,&quot;valuelistlimit.out&quot;:&quot;200&quot;,&quot;valuelistlimit.in&quot;:&quot;20&quot;}"'
+ ]
+ ];
+
+ #3
+ $provider[] = [
+ ':Main-20Page-23_QUERY140d50d705e9566904fc4a877c755964',
+ [
+ 'data-subject="Main_Page#0##_QUERY140d50d705e9566904fc4a877c755964"',
+ 'data-options="{&quot;dir&quot;:null,&quot;group&quot;:null,&quot;printable&quot;:null,&quot;offset&quot;:null,&quot;including&quot;:null,&quot;showInverse&quot;:false,&quot;showAll&quot;:true,&quot;showGroup&quot;:false,&quot;showSort&quot;:false,&quot;api&quot;:true,&quot;valuelistlimit.out&quot;:&quot;200&quot;,&quot;valuelistlimit.in&quot;:&quot;20&quot;}"'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialDeferredRequestDispatcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialDeferredRequestDispatcherTest.php
new file mode 100644
index 00000000..14773fb5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialDeferredRequestDispatcherTest.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Specials\SpecialDeferredRequestDispatcher;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialDeferredRequestDispatcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SpecialDeferredRequestDispatcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+ private $stringValidator;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->setOption( 'smwgSemanticsEnabled', true );
+ $store->setOption( 'smwgAutoRefreshSubject', true );
+
+ $store->setLogger( $this->spyLogger );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->stringValidator = $this->testEnvironment->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SpecialDeferredRequestDispatcher',
+ new SpecialDeferredRequestDispatcher()
+ );
+ }
+
+ public function testGetTargetURL() {
+
+ $this->assertContains(
+ ':DeferredRequestDispatcher',
+ SpecialDeferredRequestDispatcher::getTargetURL()
+ );
+ }
+
+ public function testgetRequestToken() {
+
+ $this->assertInternalType(
+ 'string',
+ SpecialDeferredRequestDispatcher::getRequestToken( 'Foo' )
+ );
+
+ $this->assertNotSame(
+ SpecialDeferredRequestDispatcher::getRequestToken( 'Bar' ),
+ SpecialDeferredRequestDispatcher::getRequestToken( 'Foo' )
+ );
+ }
+
+ public function testValidPostAsyncUpdateJob() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( "Skipping test because of missing method" );
+ }
+
+ $timestamp = time();
+
+ $parameters = json_encode( [
+ 'async-job' => [ 'type' => 'SMW\UpdateJob', 'title' => 'Foo' ],
+ 'timestamp' => $timestamp,
+ 'requestToken' => SpecialDeferredRequestDispatcher::getRequestToken( $timestamp ),
+ ] );
+
+ $instance = new SpecialDeferredRequestDispatcher();
+ $instance->disallowToModifyHttpHeader();
+
+ $instance->getContext()->setRequest(
+ new \FauxRequest( [ 'parameters' => $parameters ], true )
+ );
+
+ $this->assertTrue(
+ $instance->execute( '' )
+ );
+ }
+
+ public function testValidPostAsyncParserCachePurgeJob() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( "Skipping test because of missing method" );
+ }
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->setLogger( $this->spyLogger );
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $timestamp = time();
+
+ $parameters = json_encode( [
+ 'async-job' => [ 'type' => 'SMW\ParserCachePurgeJob', 'title' => 'Foo' ],
+ 'timestamp' => $timestamp,
+ 'requestToken' => SpecialDeferredRequestDispatcher::getRequestToken( $timestamp ),
+ 'idlist' => [ 1, 2 ]
+ ] );
+
+ $instance = new SpecialDeferredRequestDispatcher();
+ $instance->disallowToModifyHttpHeader();
+
+ $instance->getContext()->setRequest(
+ new \FauxRequest( [ 'parameters' => $parameters ], true )
+ );
+
+ $this->assertTrue(
+ $instance->execute( '' )
+ );
+ }
+
+ public function testInvalidPostRequestToken() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( "Skipping test because of missing method" );
+ }
+
+ $timestamp = time();
+
+ $parameters = json_encode( [
+ 'timestamp' => $timestamp,
+ 'requestToken' => SpecialDeferredRequestDispatcher::getRequestToken( 'Foo' )
+ ] );
+
+ $instance = new SpecialDeferredRequestDispatcher();
+ $instance->disallowToModifyHttpHeader();
+
+ $instance->getContext()->setRequest(
+ new \FauxRequest( [ 'parameters' => $parameters ], true )
+ );
+
+ $this->assertNull(
+ $instance->execute( '' )
+ );
+ }
+
+ public function testGetRequestForAsyncJob() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ) {
+ $this->markTestSkipped( "Skipping test because of missing method" );
+ }
+
+ $request = [];
+
+ $instance = new SpecialDeferredRequestDispatcher();
+ $instance->disallowToModifyHttpHeader();
+
+ $instance->getContext()->setRequest(
+ new \FauxRequest( $request, false )
+ );
+
+ $this->assertNull(
+ $instance->execute( '' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPagePropertyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPagePropertyTest.php
new file mode 100644
index 00000000..e391b938
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPagePropertyTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialPageProperty;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialPageProperty
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SpecialPagePropertyTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyValues' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ $this->stringValidator = $this->testEnvironment->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SpecialPageProperty',
+ new SpecialPageProperty()
+ );
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testQueryParameter( $query, $expected ) {
+
+ $instance = new SpecialPageProperty();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'PageProperty' )
+ );
+
+ $instance->execute( $query );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getOutput()->getHtml()
+ );
+ }
+
+ public function testRequestParameter() {
+
+ $request = [
+ 'type' => 'Has subobject',
+ 'from' => 'Bar'
+ ];
+
+ $expected = [
+ 'value="Has subobject"', 'value="Bar"'
+ ];
+
+ $instance = new SpecialPageProperty();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'PageProperty' )
+ );
+
+ $instance->getContext()->setRequest(
+ new \FauxRequest( $request, true )
+ );
+
+ $instance->execute( null );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getOutput()->getHtml()
+ );
+ }
+
+ public function queryParameterProvider() {
+
+ #0
+ $provider[] = [
+ 'Has page::Has prop',
+ [ 'type=Has+prop', 'from=Has+page' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialProcessingErrorListTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialProcessingErrorListTest.php
new file mode 100644
index 00000000..9f6943a2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialProcessingErrorListTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialProcessingErrorList;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialProcessingErrorList
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpecialProcessingErrorListTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanExecute() {
+
+ $instance = new SpecialProcessingErrorList();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SpecialProcessingErrorList' )
+ );
+
+ $this->assertTrue(
+ $instance->execute( '' )
+ );
+ }
+
+ public function testGetLocalAskRedirectUrl() {
+
+ $instance = new SpecialProcessingErrorList();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SpecialProcessingErrorList' )
+ );
+
+ $expected = [
+ '%5B%5BHas+processing+error+text%3A%3A%2B%5D%5D',
+ '&po=%3FHas+improper+value+for%7C%3FHas+processing+error+text',
+ '&p=class%3Dsortable-20wikitable-20smwtable-2Dstriped',
+ '&eq=no&limit=5',
+ '&bTitle=processingerrorlist',
+ '&bMsg=smw-processingerrorlist-intro'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getLocalAskRedirectUrl( 5 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPropertyLabelSimilarityTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPropertyLabelSimilarityTest.php
new file mode 100644
index 00000000..d7a5c3df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialPropertyLabelSimilarityTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialPropertyLabelSimilarity;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialPropertyLabelSimilarity
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpecialPropertyLabelSimilarityTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ $this->stringValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanExecute() {
+
+ $instance = new SpecialPropertyLabelSimilarity();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SpecialPropertyLabelSimilarity' )
+ );
+
+ $this->assertTrue(
+ $instance->execute( '' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialSearchByPropertyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialSearchByPropertyTest.php
new file mode 100644
index 00000000..4df56790
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialSearchByPropertyTest.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Specials\SpecialSearchByProperty;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialSearchByProperty
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SpecialSearchByPropertyTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+
+ $propertyTableIdReferenceFinder = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableIdReferenceFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyTableIdReferenceFinder', 'getPropertyValues', 'getPropertySubjects' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableIdReferenceFinder' )
+ ->will( $this->returnValue( $propertyTableIdReferenceFinder ) );
+
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Specials\SpecialSearchByProperty',
+ new SpecialSearchByProperty()
+ );
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testQueryParameter( $query, $expected ) {
+
+ $instance = new SpecialSearchByProperty();
+ $instance->getContext()->setTitle( Title::newFromText( 'SearchByProperty' ) );
+
+ $instance->execute( $query );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getOutput()->getHtml()
+ );
+ }
+
+ public function testXRequestParameter() {
+
+ $request = [
+ 'x' => ':Has-20subobject/Foo-23%7B%7D'
+ ];
+
+ $expected = [
+ 'property=Has+subobject', 'value=Foo%23%257B%257D'
+ ];
+
+ $instance = new SpecialSearchByProperty();
+ $instance->getContext()->setTitle( Title::newFromText( 'SearchByProperty' ) );
+ $instance->getContext()->setRequest( new \FauxRequest( $request, true ) );
+
+ $instance->execute( null );
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getOutput()->getHtml()
+ );
+ }
+
+ public function queryParameterProvider() {
+
+ #0
+ $provider[] = [
+ 'Foo/Bar',
+ [ 'property=Foo', 'value=Bar' ]
+ ];
+
+ #1
+ $provider[] = [
+ ':Has-20foo/http:-2F-2Fexample.org-2Fid-2FCurly-2520Brackets-257B-257D',
+ [ 'property=Has+foo', 'value=http%3A%2F%2Fexample.org%2Fid%2FCurly%2520Brackets%257B%257D' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialURIResolverTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialURIResolverTest.php
new file mode 100644
index 00000000..6e9e92d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/Specials/SpecialURIResolverTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\MediaWiki\Specials;
+
+use SMW\MediaWiki\Specials\SpecialURIResolver;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\Specials\SpecialURIResolver
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SpecialURIResolverTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testExecuteOnEmptyContext() {
+
+ $instance = new SpecialURIResolver();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SpecialURIResolver' )
+ );
+
+ $instance->execute( '' );
+
+ $this->assertContains(
+ 'https://www.w3.org/2001/tag/issues.html#httpRange-14',
+ $instance->getOutput()->getHTML()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/StripMarkerDecoderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/StripMarkerDecoderTest.php
new file mode 100644
index 00000000..73f77e1b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/StripMarkerDecoderTest.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\StripMarkerDecoder;
+use Title;
+
+/**
+ * @covers \SMW\MediaWiki\StripMarkerDecoder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class StripMarkerDecoderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ StripMarkerDecoder::class,
+ new StripMarkerDecoder( $stripState )
+ );
+ }
+
+ public function testIsSupported() {
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new StripMarkerDecoder(
+ $stripState
+ );
+
+ $instance->isSupported( true );
+
+ $this->assertTrue(
+ $instance->canUse()
+ );
+ }
+
+ public function testDecodeWithoutStrip() {
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new StripMarkerDecoder(
+ $stripState
+ );
+
+ $instance->isSupported( true );
+
+ $this->assertEquals(
+ '<pre>Foo</pre>',
+ $instance->decode( '<pre>Foo</pre>' )
+ );
+ }
+
+ public function testUnstripNoWiki() {
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stripState->expects( $this->once() )
+ ->method( 'unstripNoWiki' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new StripMarkerDecoder(
+ $stripState
+ );
+
+ $this->assertEquals(
+ '&lt;nowiki&gt;&lt;pre&gt;Foo&lt;/pre&gt;&lt;/nowiki&gt;',
+ $instance->unstrip( '<pre>Foo</pre>' )
+ );
+ }
+
+ public function testUnstripGeneral() {
+
+ $stripState = $this->getMockBuilder( '\StripState' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stripState->expects( $this->once() )
+ ->method( 'unstripNoWiki' )
+ ->will( $this->returnValue( '' ) );
+
+ $stripState->expects( $this->once() )
+ ->method( 'unstripGeneral' );
+
+ $instance = new StripMarkerDecoder(
+ $stripState
+ );
+
+ $instance->isSupported( true );
+
+ $instance->unstrip( '<pre>Foo</pre>' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleFactoryTest.php
new file mode 100644
index 00000000..2e17196d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleFactoryTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\TitleFactory;
+
+/**
+ * @covers \SMW\MediaWiki\TitleFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TitleFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TitleFactory::class,
+ new TitleFactory()
+ );
+ }
+
+ public function testCreateTitleFromText() {
+
+ $instance = new TitleFactory();
+
+ $this->assertInstanceOf(
+ '\Title',
+ $instance->newFromText( __METHOD__ )
+ );
+ }
+
+ public function testNewFromID() {
+
+ $instance = new TitleFactory();
+ $title = $instance->newFromID( 9999999 );
+
+ $this->assertTrue(
+ $title === null || $title instanceof \Title
+ );
+ }
+
+ public function testNewFromIDs() {
+
+ $instance = new TitleFactory();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->newFromIDs( [ 9999999 ] )
+ );
+ }
+
+ public function testMakeTitleSafe() {
+
+ $instance = new TitleFactory();
+
+ $this->assertInstanceOf(
+ '\Title',
+ $instance->makeTitleSafe( NS_MAIN, 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleLookupTest.php
new file mode 100644
index 00000000..c9185bf4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MediaWiki/TitleLookupTest.php
@@ -0,0 +1,258 @@
+<?php
+
+namespace SMW\Tests\MediaWiki;
+
+use SMW\MediaWiki\TitleLookup;
+use Title;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\MediaWiki\TitleLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class TitleLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\TitleLookup',
+ new TitleLookup( $database )
+ );
+ }
+
+ public function testSelectAllOnCategoryNamespace() {
+
+ $row = new \stdClass;
+ $row->cat_title = 'Foo';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->stringContains( 'category' ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->setNamespace( NS_CATEGORY )->selectAll()
+ );
+ }
+
+ public function testSelectAllOnMainNamespace() {
+
+ $row = new \stdClass;
+ $row->page_namespace = NS_MAIN;
+ $row->page_title = 'Bar';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'page_namespace' => NS_MAIN ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->setNamespace( NS_MAIN )->selectAll()
+ );
+ }
+
+ public function testSelectByRangeOnCategoryNamespace() {
+
+ $row = new \stdClass;
+ $row->cat_title = 'Foo';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->stringContains( 'category' ),
+ $this->anything(),
+ $this->equalTo( [ "cat_id BETWEEN 1 AND 5" ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->setNamespace( NS_CATEGORY )->selectByIdRange( 1, 5 )
+ );
+ }
+
+ public function testSelectByRangeOnMainNamespace() {
+
+ $row = new \stdClass;
+ $row->page_namespace = NS_MAIN;
+ $row->page_title = 'Bar';
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ "page_id BETWEEN 6 AND 10", 'page_namespace' => NS_MAIN ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->setNamespace( NS_MAIN )->selectByIdRange( 6, 10 )
+ );
+ }
+
+ public function testSelectAllOnMainNamespaceWithEmptyResult() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with( $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'page_namespace' => NS_MAIN ] ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->setNamespace( NS_MAIN )->selectAll()
+ );
+ }
+
+ public function testSelectAllRedirectPages() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->any() )
+ ->method( 'select' )
+ ->with(
+ $this->equalTo( [ 'page', 'redirect' ] ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertArrayOfTitles(
+ $instance->getRedirectPages()
+ );
+ }
+
+ public function testMaxIdForMainNamespace() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->once() )
+ ->method( 'selectField' )
+ ->with( $this->equalTo( 'page' ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( 9999 ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertEquals(
+ 9999,
+ $instance->setNamespace( NS_MAIN )->getMaxId()
+ );
+ }
+
+ public function testgetMaxIdForCategoryNamespace() {
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->once() )
+ ->method( 'selectField' )
+ ->with( $this->equalTo( 'category' ),
+ $this->anything(),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( 1111 ) );
+
+ $instance = new TitleLookup( $database );
+
+ $this->assertEquals(
+ 1111,
+ $instance->setNamespace( NS_CATEGORY )->getMaxId()
+ );
+ }
+
+ public function testSelectAllOnMissingNamespaceThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new TitleLookup( $database );
+ $instance->selectAll();
+ }
+
+ public function testSelectByRangeOnMissingNamespaceThrowsException() {
+
+ $this->setExpectedException( 'RuntimeException' );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new TitleLookup( $database );
+ $instance->selectByIdRange( 1, 5 );
+ }
+
+ protected function assertArrayOfTitles( $arrayOfTitles ) {
+
+ $this->assertInternalType( 'array', $arrayOfTitles );
+
+ foreach ( $arrayOfTitles as $title ) {
+ $this->assertInstanceOf( 'Title', $title );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MessageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MessageTest.php
new file mode 100644
index 00000000..45ba9291
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/MessageTest.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Message;
+
+/**
+ * @covers \SMW\Message
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class MessageTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ public function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->resetPoolCacheById( Message::POOLCACHE_ID );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Message',
+ new Message()
+ );
+ }
+
+ public function testEmptyStringOnUnregisteredHandler() {
+
+ $instance = new Message();
+
+ $this->assertEmpty(
+ $instance->get( 'Foo', 'Foo' )
+ );
+ }
+
+ public function testRegisteredHandler() {
+
+ $instance = new Message();
+
+ $instance->registerCallbackHandler( 'Foo', function( $parameters, $language ) {
+
+ if ( $parameters[0] === 'Foo' && $language === Message::CONTENT_LANGUAGE ) {
+ return 'Foobar';
+ }
+
+ if ( $parameters[0] === 'Foo' && is_string( $language ) ) {
+ return $language;
+ }
+
+ return 'UNKNOWN';
+ } );
+
+ $this->assertEquals(
+ 'Foobar',
+ $instance->get( 'Foo', 'Foo' )
+ );
+
+ $this->assertEquals(
+ 'en',
+ $instance->get( 'Foo', 'Foo', 'en' )
+ );
+
+ $instance->deregisterHandlerFor( 'Foo' );
+ }
+
+ public function testRegisteredHandlerWithLanguage() {
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->once() )
+ ->method( 'getCode' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $instanceSpy = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'hasLanguage' ] )
+ ->getMock();
+
+ $instanceSpy->expects( $this->once() )
+ ->method( 'hasLanguage' )
+ ->with( $this->identicalTo( $language ) );
+
+ $instance = new Message();
+ $instance->clear();
+
+ $instance->registerCallbackHandler( 'Foo', function( $parameters, $language ) use ( $instanceSpy ){
+ $instanceSpy->hasLanguage( $language );
+ return 'UNKNOWN';
+ } );
+
+ $instance->get( 'Bar', 'Foo', $language );
+ $instance->deregisterHandlerFor( 'Foo' );
+ }
+
+ public function testFromCache() {
+
+ $instance = new Message();
+ $instance->clear();
+
+ $instance->registerCallbackHandler( 'SimpleText', function( $parameters, $language ) {
+ return 'Foo';
+ } );
+
+ $instance->get( 'Foo', 'SimpleText' );
+
+ $this->assertEquals(
+ [
+ 'inserts' => 1,
+ 'deletes' => 0,
+ 'max' => 1000,
+ 'count' => 1,
+ 'hits' => 0,
+ 'misses' => 1
+ ],
+ $instance->getCache()->getStats()
+ );
+
+ $instance->get( 'Foo', 'SimpleText', 'ooo' );
+
+ $this->assertEquals(
+ [
+ 'inserts' => 2,
+ 'deletes' => 0,
+ 'max' => 1000,
+ 'count' => 2,
+ 'hits' => 0,
+ 'misses' => 2
+ ],
+ $instance->getCache()->getStats()
+ );
+
+ // Repeated request
+ $instance->get( 'Foo', 'SimpleText' );
+
+ $this->assertEquals(
+ [
+ 'inserts' => 2,
+ 'deletes' => 0,
+ 'max' => 1000,
+ 'count' => 2,
+ 'hits' => 1,
+ 'misses' => 2
+ ],
+ $instance->getCache()->getStats()
+ );
+
+ $instance->deregisterHandlerFor( 'SimpleText' );
+ }
+
+ /**
+ * @dataProvider encodeProvider
+ */
+ public function testEncode( $string, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ Message::encode( $string )
+ );
+ }
+
+ public function testDecode() {
+
+ $this->assertEquals(
+ false,
+ Message::decode( 'Foo' )
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ Message::decode( '[2,"Foo"]' )
+ );
+ }
+
+ public function encodeProvider() {
+
+ $provider[] = [
+ 'Foo',
+ '[2,"Foo"]'
+ ];
+
+ $provider[] = [
+ [ 'Foo' ],
+ '[2,"Foo"]'
+ ];
+
+ $provider[] = [
+ '[2,"Foo"]',
+ '[2,"Foo"]'
+ ];
+
+ $provider[] = [
+ [ 'Foo', '<strong>Expression error: Unrecognized word "yyyy".</strong>' ],
+ '[2,"Foo","Expression error: Unrecognized word \"yyyy\"."]'
+ ];
+
+ $provider[] = [
+ [ 'eb0afd6194bab91b6d32d2db4bb30060' => '[2,"smw-datavalue-wikipage-invalid-title","Help:"]' ],
+ '[2,"smw-datavalue-wikipage-invalid-title","Help:"]'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceExaminerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceExaminerTest.php
new file mode 100644
index 00000000..1ce9d3c0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceExaminerTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\NamespaceExaminer;
+
+/**
+ * @covers \SMW\NamespaceExaminer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NamespaceExaminerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\NamespaceExaminer',
+ new NamespaceExaminer( [] )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\NamespaceExaminer',
+ NamespaceExaminer::newFromArray( [] )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\NamespaceExaminer',
+ NamespaceExaminer::getInstance()
+ );
+ }
+
+ public function testIsSemanticEnabled() {
+
+ $instance = new NamespaceExaminer( [ NS_MAIN => true ] );
+
+ $this->assertTrue(
+ $instance->isSemanticEnabled( NS_MAIN )
+ );
+
+ $instance = new NamespaceExaminer( [ NS_MAIN => false ] );
+
+ $this->assertFalse(
+ $instance->isSemanticEnabled( NS_MAIN )
+ );
+ }
+
+ public function testNoNumberNamespaceThrowsException() {
+
+ $instance = new NamespaceExaminer( [ NS_MAIN => true ] );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->isSemanticEnabled( 'ichi' );
+ }
+
+ /**
+ * Bug 51435; return false instead of an Exception
+ */
+ public function testNoValidNamespaceException() {
+
+ $instance = new NamespaceExaminer( [ NS_MAIN => true ] );
+
+ $this->assertFalse(
+ $instance->isSemanticEnabled( 99991001 )
+ );
+ }
+
+ public function testGetInstance() {
+
+ $instance = NamespaceExaminer::getInstance();
+
+ $this->assertSame(
+ $instance,
+ NamespaceExaminer::getInstance()
+ );
+
+ NamespaceExaminer::clear();
+
+ $this->assertNotSame(
+ $instance,
+ NamespaceExaminer::getInstance()
+ );
+ }
+
+ public function testNewFromArray() {
+
+ $instance = NamespaceExaminer::newFromArray( [ NS_MAIN => true ] );
+
+ $this->assertTrue(
+ $instance->isSemanticEnabled( NS_MAIN )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceManagerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceManagerTest.php
new file mode 100644
index 00000000..14cfdd8a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceManagerTest.php
@@ -0,0 +1,315 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\NamespaceManager;
+
+/**
+ * @covers \SMW\NamespaceManager
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NamespaceManagerTest extends \PHPUnit_Framework_TestCase {
+
+ private $varsEnvironment;
+ private $lang;
+ private $default;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->lang->expects( $this->any() )
+ ->method( 'fetch' )
+ ->will( $this->returnSelf() );
+
+ $this->lang->expects( $this->any() )
+ ->method( 'getNamespaces' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->lang->expects( $this->any() )
+ ->method( 'getNamespaceAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->default = [
+ 'smwgNamespacesWithSemanticLinks' => [],
+ 'wgNamespacesWithSubpages' => [],
+ 'wgExtraNamespaces' => [],
+ 'wgNamespaceAliases' => [],
+ 'wgContentNamespaces' => [],
+ 'wgNamespacesToBeSearchedDefault' => [],
+ 'wgNamespaceContentModels' => [],
+ 'wgLanguageCode' => 'en'
+ ];
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NamespaceManager::class,
+ new NamespaceManager( $this->lang )
+ );
+ }
+
+ public function testInitOnIncompleteConfiguration() {
+
+ $vars = $this->default + [
+ 'wgExtraNamespaces' => '',
+ 'wgNamespaceAliases' => ''
+ ];
+
+ $instance = new NamespaceManager( $this->lang );
+ $instance->init( $vars );
+
+ $this->assertNotEmpty(
+ $vars
+ );
+ }
+
+ public function testGetCanonicalNames() {
+
+ $result = NamespaceManager::getCanonicalNames();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertCount(
+ 8,
+ $result
+ );
+ }
+
+ /**
+ * @dataProvider canonicalNameListProvider
+ */
+ public function testGetCanonicalNameList( $key, $expected ) {
+
+ $result = NamespaceManager::getCanonicalNames();
+
+ $this->assertEquals(
+ $expected,
+ $result[$key]
+ );
+ }
+
+ public function testGetCanonicalNamesWithTypeNamespace() {
+
+ $result = NamespaceManager::getCanonicalNames();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertCount(
+ 8,
+ $result
+ );
+ }
+
+ public function testBuildNamespaceIndex() {
+ $this->assertInternalType(
+ 'array',
+ NamespaceManager::buildNamespaceIndex( 100 )
+ );
+ }
+
+ public function testInitCustomNamespace() {
+
+ $vars = [
+ 'wgLanguageCode' => 'en',
+ 'wgContentNamespaces' => []
+ ];
+
+ NamespaceManager::initCustomNamespace( $vars );
+
+ $this->assertNotEmpty( $vars );
+
+ $this->assertEquals(
+ 100,
+ $vars['smwgNamespaceIndex']
+ );
+ }
+
+ public function testNamespacesInitWithEmptySettings() {
+
+ $vars = $this->default + [
+ 'wgExtraNamespaces' => '',
+ 'wgNamespaceAliases' => ''
+ ];
+
+ $instance = new NamespaceManager( $this->lang );
+ $instance->init( $vars );
+
+ $this->assertTrue(
+ $vars['smwgNamespacesWithSemanticLinks'][SMW_NS_PROPERTY]
+ );
+
+ $this->assertTrue(
+ $vars['smwgNamespacesWithSemanticLinks'][SMW_NS_CONCEPT]
+ );
+
+ $this->assertTrue(
+ $vars['smwgNamespacesWithSemanticLinks'][SMW_NS_SCHEMA]
+ );
+ }
+
+ public function testInitToKeepPreInitSettings() {
+
+ $vars = $this->default + [
+ 'wgExtraNamespaces' => '',
+ 'wgNamespaceAliases' => '',
+ ];
+
+ $vars['smwgNamespacesWithSemanticLinks'] = [
+ SMW_NS_PROPERTY => false
+ ];
+
+ $instance = new NamespaceManager( $this->lang );
+ $instance->init( $vars );
+
+ $this->assertFalse(
+ $vars['smwgNamespacesWithSemanticLinks'][SMW_NS_PROPERTY]
+ );
+
+ $this->assertTrue(
+ $vars['smwgNamespacesWithSemanticLinks'][SMW_NS_CONCEPT]
+ );
+ }
+
+ public function testInitCustomNamespace_NamespaceAliases() {
+
+ $lang = $this->getMockBuilder( '\SMW\Lang\Lang' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $lang->expects( $this->any() )
+ ->method( 'fetch' )
+ ->will( $this->returnSelf() );
+
+ $lang->expects( $this->any() )
+ ->method( 'getNamespaces' )
+ ->will( $this->returnValue( [] ) );
+
+ $lang->expects( $this->once() )
+ ->method( 'getNamespaceAliases' )
+ ->will( $this->returnValue( [ 'Foo' => SMW_NS_PROPERTY ] ) );
+
+ $vars = $this->default + [
+ 'wgExtraNamespaces' => '',
+ 'wgNamespaceAliases' => '',
+ ];
+
+ $instance = NamespaceManager::initCustomNamespace(
+ $vars,
+ $lang
+ );
+
+ $this->assertArrayHasKey(
+ 'Foo',
+ $vars['wgNamespaceAliases']
+ );
+ }
+
+ public function testInitWithoutOverridingUserSettingsOnExtraNamespaceSettings() {
+
+ $vars = [
+ 'wgNamespacesWithSubpages' => [
+ SMW_NS_PROPERTY => false
+ ],
+ 'wgNamespacesToBeSearchedDefault' => [
+ SMW_NS_PROPERTY => false
+ ],
+ 'wgContentNamespaces' => [
+ SMW_NS_PROPERTY => false
+ ]
+ ] + $this->default;
+
+ $instance = new NamespaceManager( $this->lang );
+ $instance->init( $vars );
+
+ $this->assertFalse(
+ $vars['wgNamespacesWithSubpages'][SMW_NS_PROPERTY]
+ );
+
+ $this->assertFalse(
+ $vars['wgNamespacesToBeSearchedDefault'][SMW_NS_PROPERTY]
+ );
+
+ $this->assertFalse(
+ $vars['wgContentNamespaces'][SMW_NS_PROPERTY]
+ );
+ }
+
+ public function testInit_wgNamespaceContentModels() {
+
+ $vars = $this->default;
+
+ $instance = new NamespaceManager( $this->lang );
+ $instance->init( $vars );
+
+ $this->assertEquals(
+ CONTENT_MODEL_SMW_SCHEMA,
+ $vars['wgNamespaceContentModels'][SMW_NS_SCHEMA]
+ );
+ }
+
+ public function testInitCanonicalNamespacesWithForcedNsReset() {
+
+ $namespaces = [
+ 10001 => 'Property',
+ 10002 => 'Property_talk'
+ ];
+
+ $this->assertTrue(
+ NamespaceManager::initCanonicalNamespaces( $namespaces )
+ );
+
+ $this->assertEquals(
+ 'Property',
+ $namespaces[SMW_NS_PROPERTY]
+ );
+
+ $this->assertEquals(
+ 'Property_talk',
+ $namespaces[SMW_NS_PROPERTY_TALK]
+ );
+ }
+
+ public function canonicalNameListProvider() {
+
+ yield [
+ SMW_NS_PROPERTY,
+ 'Property'
+ ];
+
+ yield [
+ SMW_NS_CONCEPT,
+ 'Concept'
+ ];
+
+ yield [
+ SMW_NS_SCHEMA,
+ 'smw/schema'
+ ];
+
+ yield [
+ SMW_NS_RULE,
+ 'Rule'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceUriFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceUriFinderTest.php
new file mode 100644
index 00000000..60f1f38d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/NamespaceUriFinderTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\NamespaceUriFinder;
+
+/**
+ * @covers \SMW\NamespaceUriFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class NamespaceUriFinderTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider namespaceProvider
+ */
+ public function testGetUriForNamespaceKey( $key, $expected ) {
+ $this->assertInternalType(
+ $expected,
+ NamespaceUriFinder::getUri( $key )
+ );
+ }
+
+ public function namespaceProvider() {
+
+ $provider[] = [
+ 'Foo',
+ 'boolean'
+ ];
+
+ $ns = [
+ 'owl', 'rdf', 'rdfs', 'swivt', 'xsd', 'skos', 'foaf', 'dc',
+ 'OWL'
+ ];
+
+ foreach ( $ns as $key ) {
+ $provider[] = [
+ $key,
+ 'string'
+ ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/OptionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/OptionsTest.php
new file mode 100644
index 00000000..48d77a8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/OptionsTest.php
@@ -0,0 +1,233 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Options;
+
+/**
+ * @covers \SMW\Options
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class OptionsTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Options',
+ new Options()
+ );
+ }
+
+ public function testAddOption() {
+
+ $instance = new Options();
+
+ $this->assertFalse(
+ $instance->has( 'Foo' )
+ );
+
+ $instance->set( 'Foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'Foo' )
+ );
+ }
+
+ public function testToArray() {
+
+ $instance = new Options(
+ [ 'Foo' => 42 ]
+ );
+
+ $this->assertEquals(
+ [ 'Foo' => 42 ],
+ $instance->toArray()
+ );
+ }
+
+ public function testUnregisteredKeyThrowsException() {
+
+ $instance = new Options();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->get( 'Foo' );
+ }
+
+ public function testSafeGetOnUnregisteredKey() {
+
+ $instance = new Options();
+
+ $this->assertEquals(
+ 'Default',
+ $instance->safeGet( 'Foo', 'Default' )
+ );
+ }
+
+ public function testFilter() {
+
+ $instance = new Options();
+ $instance->set( 'Foo', '123' );
+ $instance->set( 'Bar', '456' );
+
+ $this->assertEquals(
+ [
+ 'Foo' => '123'
+ ],
+ $instance->filter( [ 'Foo' ] )
+ );
+ }
+
+ /**
+ * @dataProvider dotProvider
+ */
+ public function testDotGet( $options, $key, $expected ) {
+
+ $instance = new Options( $options );
+
+ $this->assertEquals(
+ $expected,
+ $instance->dotGet( $key )
+ );
+ }
+
+ /**
+ * @dataProvider isFlagSetProvider
+ */
+ public function testIsFlagSet( $value, $flag, $expected ) {
+
+ $instance = new Options();
+ $instance->set( 'Foo', $value );
+
+ $this->assertEquals(
+ $expected,
+ $instance->isFlagSet( 'Foo', $flag )
+ );
+ }
+
+ public function isSetProvider() {
+
+ yield [
+ 100,
+ 100,
+ true
+ ];
+
+ yield [
+ 'foo',
+ 'foo',
+ true
+ ];
+
+ yield [
+ ( ( 4 | 8 ) | 16 ),
+ 2,
+ false
+ ];
+
+ yield [
+ 4 | 16,
+ 15,
+ false
+ ];
+
+ yield [
+ false,
+ 2,
+ false
+ ];
+
+ yield [
+ false,
+ false,
+ true
+ ];
+
+ yield [
+ true,
+ true,
+ true
+ ];
+ }
+
+ public function isFlagSetProvider() {
+
+ yield [
+ ( ( 4 | 8 ) | 16 ),
+ 16,
+ true
+ ];
+
+ yield [
+ ( ( 4 | 8 ) | 16 ),
+ 4,
+ true
+ ];
+
+ yield [
+ ( ( 4 | 8 ) | 16 ),
+ 2,
+ false
+ ];
+
+ yield [
+ 4 | 16,
+ 15,
+ false
+ ];
+
+ yield [
+ false,
+ 2,
+ false
+ ];
+ }
+
+ public function dotProvider() {
+
+ $o = [ 'Foo' => [
+ 'Bar' => [ 'Foobar' => 42 ],
+ 'Foobar' => 1001,
+ 'some.other.options' => 9999
+ ],
+ ];
+
+ yield [
+ $o,
+ 'Foo.Bar',
+ [ 'Foobar' => 42 ]
+ ];
+
+ yield [
+ $o,
+ 'Foo.Foobar',
+ 1001
+ ];
+
+ yield [
+ $o,
+ 'Foo.Bar.Foobar',
+ 42
+ ];
+
+ yield [
+ $o,
+ 'Foo.some.other.options',
+ 9999
+ ];
+
+ yield [
+ $o,
+ 'Foo.Bar.Foobar.unkown',
+ false
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ConceptPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ConceptPageTest.php
new file mode 100644
index 00000000..0206a2a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ConceptPageTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace SMW\Tests\Page;
+
+use SMW\DIWikiPage;
+use SMW\Page\ConceptPage;
+
+/**
+ * @covers \SMW\Page\ConceptPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConceptPageTest extends \PHPUnit_Framework_TestCase {
+
+ private $title;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $subject = DIWikiPage::newFromText( __METHOD__, SMW_NS_CONCEPT );
+ $this->title = $subject->getTitle();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConceptPage::class,
+ new ConceptPage( $this->title )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ListBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ListBuilderTest.php
new file mode 100644
index 00000000..953d1d82
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ListBuilderTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Tests\Page\ListBuilder;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Page\ListBuilder\ListBuilder;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Page\ListBuilder\ListBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ListBuilder::class,
+ new ListBuilder( $this->store )
+ );
+ }
+
+ public function testCreateEmptyList() {
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ListBuilder( $this->store );
+
+ $property = new DIProperty( 'Foo' );
+ $dataItem = new DIWikiPage( 'Bar', NS_MAIN );
+
+ $this->assertEquals(
+ '',
+ $instance->createHtml( $property, $dataItem, $requestOptions )
+ );
+ }
+
+ public function testCreateHtml() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertySubjects' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromText( __METHOD__ ) ] ) );
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ListBuilder( $store );
+ $instance->setListLimit( 10 );
+
+ $property = new DIProperty( 'Foo' );
+ $dataItem = new DIWikiPage( 'Bar', NS_MAIN );
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ 'title="SMW\Tests\Page\ListBuilder\ListBuilderTest::testCreateHtml'
+ ],
+ $instance->createHtml( $property, $dataItem, $requestOptions )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ValueListBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ValueListBuilderTest.php
new file mode 100644
index 00000000..8f16755c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilder/ValueListBuilderTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\Page\ListBuilder;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Page\ListBuilder\ValueListBuilder;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Page\ListBuilder\ValueListBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ValueListBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment( [ 'smwgCompactLinkSupport' => false ] );
+ $this->stringValidator = $this->testEnvironment->newValidatorFactory()->newStringValidator();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ValueListBuilder::class,
+ new ValueListBuilder( $this->store )
+ );
+ }
+
+ public function testCreateEmptyList() {
+
+ $instance = new ValueListBuilder( $this->store );
+
+ $property = new DIProperty( 'Foo' );
+ $dataItem = new DIWikiPage( 'Bar', NS_MAIN );
+
+ $this->assertEquals(
+ '',
+ $instance->createHtml( $property, $dataItem, [] )
+ );
+ }
+
+ public function testCreateHtml() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getAllPropertySubjects', 'getPropertyValues', 'getWikiPageSortKey' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getAllPropertySubjects' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromText( __METHOD__ ) ] ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromText( 'Bar' ) ] ) );
+
+ $instance = new ValueListBuilder( $store );
+ $instance->setLanguageCode( 'en' );
+
+ $property = new DIProperty( 'Foo' );
+ $dataItem = new DIWikiPage( 'Bar', NS_MAIN );
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<div class="smw-table-row header-row"><div class="smw-table-cell header-title"><div id="B">B</div>',
+ 'title="SMW\Tests\Page\ListBuilder\ValueListBuilderTest::testCreateHtml',
+ '<span class="smwsearch">.*:Foo/Bar">+</a>'
+ ],
+ $instance->createHtml( $property, $dataItem, [ 'limit' => 10 ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilderTest.php
new file mode 100644
index 00000000..65a26eec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/ListBuilderTest.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace SMW\Tests\Page;
+
+use SMW\Page\ListBuilder;
+use SMW\DIWikiPage;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Page\ListBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ListBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $Collator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->collator = $this->getMockBuilder( '\SMW\MediaWiki\Collator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ListBuilder::class,
+ new ListBuilder( $this->store )
+ );
+ }
+
+ public function testGetList() {
+
+ $this->store->expects( $this->once() )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'FOO' ) );
+
+ $this->collator->expects( $this->once() )
+ ->method( 'getFirstLetter' )
+ ->will( $this->returnValue( 'F' ) );
+
+ $instance = new ListBuilder(
+ $this->store,
+ $this->collator
+ );
+
+ $this->assertArrayHasKey(
+ 'F',
+ $instance->getList( [ DIWikiPage::newFromText( 'Foo' ) ] )
+ );
+ }
+
+ public function testGetList_Sorted() {
+
+ $list = [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'ABC' )
+ ];
+
+ $this->store->expects( $this->at( 0 ) )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'FOO' ) );
+
+ $this->collator->expects( $this->at( 0 ) )
+ ->method( 'getFirstLetter' )
+ ->will( $this->returnValue( 'F' ) );
+
+ $this->store->expects( $this->at( 1 ) )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'Abc' ) );
+
+ $this->collator->expects( $this->at( 1 ) )
+ ->method( 'getFirstLetter' )
+ ->will( $this->returnValue( 'A' ) );
+
+ $instance = new ListBuilder(
+ $this->store,
+ $this->collator
+ );
+
+ $this->assertEquals(
+ [ 'A', 'F' ],
+ array_keys( $instance->getList( $list ) )
+ );
+ }
+
+ public function testGetColumnList() {
+
+ $this->store->expects( $this->once() )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'FOO' ) );
+
+ $this->collator->expects( $this->once() )
+ ->method( 'getFirstLetter' )
+ ->will( $this->returnValue( 'F' ) );
+
+ $instance = new ListBuilder(
+ $this->store,
+ $this->collator
+ );
+
+ $instance->setLinker( null );
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<div class="smw-columnlist-container" dir="ltr"><div class="smw-column" style="width:100%;" dir="ltr">',
+ '<div class="smw-column-header">F</div>',
+ '<ul><li>Foo&#160;<span class="smwbrowse">'
+ ],
+ $instance->getColumnList( [ DIWikiPage::newFromText( 'Foo' ) ] )
+ );
+ }
+
+ public function testGetColumnList_ItemFormatter() {
+
+ $this->store->expects( $this->once() )
+ ->method( 'getWikiPageSortKey' )
+ ->will( $this->returnValue( 'FOO' ) );
+
+ $this->collator->expects( $this->once() )
+ ->method( 'getFirstLetter' )
+ ->will( $this->returnValue( 'F' ) );
+
+ $instance = new ListBuilder(
+ $this->store,
+ $this->collator
+ );
+
+ $instance->setItemFormatter( function( $dataValue, $linker ) {
+ return 'Bar';
+ } );
+
+ $stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+
+ $stringValidator->assertThatStringContains(
+ [
+ '<div class="smw-columnlist-container" dir="ltr"><div class="smw-column" style="width:100%;" dir="ltr">',
+ '<ul><li>Bar</li></ul></div>'
+ ],
+ $instance->getColumnList( [ DIWikiPage::newFromText( 'Foo' ) ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PageFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PageFactoryTest.php
new file mode 100644
index 00000000..af5503a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PageFactoryTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\Tests\Page;
+
+use SMW\Page\PageFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Page\PageFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PageFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PageFactory::class,
+ new PageFactory( $this->store )
+ );
+ }
+
+ public function testNewPageFromNotRegisteredNamespaceThrowsException() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $instance = new PageFactory( $this->store );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->newPageFromTitle( $title );
+ }
+
+ /**
+ * @dataProvider namespaceProvider
+ */
+ public function testNewPageFromTitle( $namespace, $expected ) {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( $namespace ) );
+
+ $instance = new PageFactory( $this->store );
+
+ $this->assertInstanceOf(
+ $expected,
+ $instance->newPageFromTitle( $title )
+ );
+ }
+
+ public function namespaceProvider() {
+
+ $provider[] = [
+ SMW_NS_PROPERTY,
+ 'SMW\Page\PropertyPage'
+ ];
+
+ $provider[] = [
+ SMW_NS_CONCEPT,
+ 'SMW\Page\ConceptPage'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PropertyPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PropertyPageTest.php
new file mode 100644
index 00000000..f7153e93
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Page/PropertyPageTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\Tests\Page;
+
+use SMW\DIWikiPage;
+use SMW\Page\PropertyPage;
+
+/**
+ * @covers \SMW\Page\PropertyPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyPageTest extends \PHPUnit_Framework_TestCase {
+
+ private $title;
+ private $store;
+ private $propertySpecificationReqMsgBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->propertySpecificationReqMsgBuilder = $this->getMockBuilder( '\SMW\PropertySpecificationReqMsgBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->title = DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )->getTitle();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyPage::class,
+ new PropertyPage( $this->title, $this->store, $this->propertySpecificationReqMsgBuilder )
+ );
+ }
+
+ public function testGetHtml() {
+
+ $instance = new PropertyPage(
+ $this->title,
+ $this->store,
+ $this->propertySpecificationReqMsgBuilder
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->view()
+ );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterListDocBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterListDocBuilderTest.php
new file mode 100644
index 00000000..72892ffe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterListDocBuilderTest.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace SMW\Test;
+
+use ParamProcessor\ParamDefinition;
+use SMW\ParameterListDocBuilder;
+use SMW\Tests\Utils\UtilityFactory;
+use SMW\Tests\Utils\Validators\StringValidator;
+
+/**
+ * @covers SMW\ParameterListDocBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class ParameterListDocBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var ParameterListDocBuilder
+ */
+ private $builder;
+
+ /**
+ * @var StringValidator
+ */
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+
+ $this->builder = new ParameterListDocBuilder( function( $key ) {
+ return $key;
+ } );
+ }
+
+ public function testGivenNoParameters_noTableIsReturned() {
+ $wikiText = $this->builder->getParameterTable( [] );
+
+ $this->assertSame(
+ '',
+ $wikiText
+ );
+ }
+
+ public function testGivenMinimalParameter_defaultIsRequired() {
+ $wikiText = $this->builder->getParameterTable( [
+ new ParamDefinition( 'number', 'length' )
+ ] );
+
+ $expected = [
+ '{| class="wikitable sortable"',
+ '!validator-describe-header-parameter',
+ '!validator-describe-header-type',
+ '!validator-describe-header-default',
+ '!validator-describe-header-description',
+ '|-',
+ '|length',
+ '|validator-type-number',
+ "|''validator-describe-required''",
+ '|',
+ '|}'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $wikiText
+ );
+ }
+
+ public function testGivenParameterWithDefault_defaultIsListed() {
+ $wikiText = $this->builder->getParameterTable( [
+ new ParamDefinition( 'number', 'length', 42 )
+ ] );
+
+ $expected = [
+ '{| class="wikitable sortable"',
+ '!validator-describe-header-parameter',
+ '!validator-describe-header-type',
+ '!validator-describe-header-default',
+ '!validator-describe-header-description',
+ '|-',
+ '|length',
+ '|validator-type-number',
+ "|42",
+ '|',
+ '|}'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $wikiText
+ );
+ }
+
+ public function testGivenParameterWithAliases_aliasesAreListed() {
+ $paramWithAliases = new ParamDefinition( 'number', 'param-with-alias' );
+ $paramWithAliases->addAliases( 'first-alias', 'second-alias' );
+
+ $paramWithoutAliases = new ParamDefinition( 'string', 'no-aliases' );
+
+ $wikiText = $this->builder->getParameterTable( [ $paramWithAliases, $paramWithoutAliases ] );
+
+ $expected = [
+ '{| class="wikitable sortable"',
+ '!validator-describe-header-parameter',
+ '!validator-describe-header-type',
+ '!validator-describe-header-default',
+ '!validator-describe-header-description',
+ '|-',
+ '|param-with-alias',
+ '|first-alias, second-alias',
+ '|validator-type-number',
+ "|''validator-describe-required''",
+ '|',
+ '|-',
+ '|no-aliases',
+ '| -',
+ '|validator-type-string',
+ "|''validator-describe-required''",
+ '|',
+ '|}'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $wikiText
+ );
+ }
+
+ public function testGivenBooleanParameter_defaultIsListedAsString() {
+ $wikiText = $this->builder->getParameterTable( [
+ new ParamDefinition( 'boolean', 'awesome', true )
+ ] );
+
+ $expected = [
+ '{| class="wikitable sortable"',
+ '!validator-describe-header-parameter',
+ '!validator-describe-header-type',
+ '!validator-describe-header-default',
+ '!validator-describe-header-description',
+ '|-',
+ '|awesome',
+ '|validator-type-boolean',
+ "|yes",
+ '|',
+ '|}'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $wikiText
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterProcessorFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterProcessorFactoryTest.php
new file mode 100644
index 00000000..15e14e5e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParameterProcessorFactoryTest.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ParameterProcessorFactory;
+
+/**
+ * @covers \SMW\ParameterProcessorFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParameterProcessorFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\ParameterProcessorFactory',
+ new ParameterProcessorFactory()
+ );
+ }
+
+ public function testToEliminateFirstParameterIfObject() {
+
+ $parameter = [
+ new \stdClass
+ ];
+
+ $instance = ParameterProcessorFactory::newFromArray( $parameter );
+
+ $this->assertInstanceOf(
+ '\SMW\ParserParameterProcessor',
+ $instance
+ );
+
+ $this->assertEmpty(
+ $instance->getRaw()
+ );
+ }
+
+ public function testNewFromArray() {
+
+ $parameter = [
+ 'La' => 'Lu'
+ ];
+
+ $instance = ParameterProcessorFactory::newFromArray( $parameter );
+
+ $this->assertInstanceOf(
+ '\SMW\ParserParameterProcessor',
+ $instance
+ );
+
+ $this->assertEquals(
+ $parameter,
+ $instance->getRaw()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParametersTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParametersTest.php
new file mode 100644
index 00000000..7cf3b3d1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParametersTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Parameters;
+
+/**
+ * @covers \SMW\Parameters
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParametersTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Parameters::class,
+ new Parameters()
+ );
+ }
+
+ public function testAddOption() {
+
+ $instance = new Parameters();
+
+ $this->assertFalse(
+ $instance->has( 'Foo' )
+ );
+
+ $instance->set( 'Foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'Foo' )
+ );
+ }
+
+ public function testUnregisteredKeyThrowsException() {
+
+ $instance = new Parameters();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->get( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/InTextAnnotationParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/InTextAnnotationParserTest.php
new file mode 100644
index 00000000..6ce96c2d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/InTextAnnotationParserTest.php
@@ -0,0 +1,703 @@
+<?php
+
+namespace SMW\Tests\Parser;
+
+use ParserOutput;
+use ReflectionClass;
+use SMW\DIProperty;
+use SMW\MediaWiki\MagicWordsFinder;
+use SMW\MediaWiki\RedirectTargetFinder;
+use SMW\Parser\InTextAnnotationParser;
+use SMW\Parser\LinksProcessor;
+use SMW\ParserData;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\Parser\InTextAnnotationParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InTextAnnotationParserTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $testEnvironment;
+ private $linksProcessor;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->linksProcessor = new LinksProcessor();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ public function testCanConstruct( $namespace ) {
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $redirectTargetFinder = $this->getMockBuilder( 'SMW\MediaWiki\RedirectTargetFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = Title::newFromText( __METHOD__, $namespace );
+
+ $instance = new InTextAnnotationParser(
+ new ParserData( $title, $parserOutput ),
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ $redirectTargetFinder
+ );
+
+ $this->assertInstanceOf(
+ InTextAnnotationParser::class,
+ $instance
+ );
+ }
+
+ public function testHasMarker() {
+
+ $this->assertTrue(
+ InTextAnnotationParser::hasMarker( '[[SMW::off]]' )
+ );
+
+ $this->assertTrue(
+ InTextAnnotationParser::hasMarker( '[[SMW::on]]' )
+ );
+
+ $this->assertFalse(
+ InTextAnnotationParser::hasMarker( 'Foo' )
+ );
+ }
+
+ /**
+ * @dataProvider magicWordDataProvider
+ */
+ public function testStripMagicWords( $namespace, $text, array $expected ) {
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__, $namespace ),
+ new ParserOutput()
+ );
+
+ $magicWordsFinder = new MagicWordsFinder( $parserData->getOutput() );
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ $magicWordsFinder,
+ new RedirectTargetFinder()
+ );
+
+ $instance->parse( $text );
+
+ $this->assertEquals(
+ $expected,
+ $magicWordsFinder->getMagicWords()
+ );
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ public function testTextParse( $namespace, array $settings, $text, array $expected ) {
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__, $namespace ),
+ new ParserOutput()
+ );
+
+ $this->linksProcessor->isStrictMode(
+ isset( $settings['smwgParserFeatures'] ) ? $settings['smwgParserFeatures'] : true
+ );
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ new RedirectTargetFinder()
+ );
+
+ $instance->showErrors(
+ isset( $settings['smwgParserFeatures'] ) ? $settings['smwgParserFeatures'] : true
+ );
+
+ $instance->isLinksInValues(
+ ( ( $settings['smwgParserFeatures'] & SMW_PARSER_LINV ) == SMW_PARSER_LINV )
+ );
+
+ $this->testEnvironment->withConfiguration(
+ $settings
+ );
+
+ $instance->parse( $text );
+
+ $this->assertContains(
+ $expected['resultText'],
+ $text
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testRedirectAnnotationFromText() {
+
+ $namespace = NS_MAIN;
+ $text = '#REDIRECT [[:Lala]]';
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'property' => new DIProperty( '_REDI' ),
+ 'propertyValues' => [ 'Lala' ]
+ ];
+
+ $settings = [
+ 'smwgNamespacesWithSemanticLinks' => [ $namespace => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ];
+
+ $this->testEnvironment->withConfiguration(
+ $settings
+ );
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__, $namespace ),
+ new ParserOutput()
+ );
+
+ $redirectTargetFinder = new RedirectTargetFinder();
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ $redirectTargetFinder
+ );
+
+ $instance->parse( $text );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testRedirectAnnotationFromInjectedRedirectTarget() {
+
+ $namespace = NS_MAIN;
+ $text = '';
+ $redirectTarget = Title::newFromText( 'Foo' );
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'property' => new DIProperty( '_REDI' ),
+ 'propertyValues' => [ 'Foo' ]
+ ];
+
+ $settings = [
+ 'smwgNamespacesWithSemanticLinks' => [ $namespace => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ];
+
+ $this->testEnvironment->withConfiguration(
+ $settings
+ );
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__, $namespace ),
+ new ParserOutput()
+ );
+
+ $redirectTargetFinder = new RedirectTargetFinder();
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ $redirectTargetFinder
+ );
+
+ $instance->setRedirectTarget( $redirectTarget );
+ $instance->parse( $text );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testStripMarkerDecoding() {
+
+ $redirectTargetFinder = $this->getMockBuilder( 'SMW\MediaWiki\RedirectTargetFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stripMarkerDecoder = $this->getMockBuilder( '\SMW\MediaWiki\StripMarkerDecoder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'canUse', 'hasStripMarker', 'unstrip' ] )
+ ->getMock();
+
+ $stripMarkerDecoder->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $stripMarkerDecoder->expects( $this->once() )
+ ->method( 'hasStripMarker' )
+ ->will( $this->returnValue( true ) );
+
+ $stripMarkerDecoder->expects( $this->once() )
+ ->method( 'unstrip' )
+ ->with( $this->stringContains( '<nowiki>Bar</nowiki>' ) )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ $redirectTargetFinder
+ );
+
+ $text = '[[Foo::<nowiki>Bar</nowiki>]]';
+
+ $instance->setStripMarkerDecoder( $stripMarkerDecoder );
+ $instance->parse( $text );
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'property' => new DIProperty( 'Foo' ),
+ 'propertyValues' => [ 'Bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+
+ $this->assertEquals(
+ '<nowiki>Bar</nowiki>',
+ $text
+ );
+ }
+
+ public function testProcessOnReflection() {
+
+ $parserData = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new InTextAnnotationParser(
+ $parserData,
+ $this->linksProcessor,
+ new MagicWordsFinder(),
+ new RedirectTargetFinder()
+ );
+
+ $reflector = new ReflectionClass( '\SMW\Parser\InTextAnnotationParser' );
+
+ $method = $reflector->getMethod( 'process' );
+ $method->setAccessible( true );
+
+ $result = $method->invoke( $instance, [] );
+ $this->assertEmpty( $result );
+
+ $result = $method->invoke( $instance, [ 'Test::foo', 'SMW' , 'lula' ] );
+ $this->assertEmpty( $result );
+
+ $result = $method->invoke( $instance, [ 'Test::bar', 'SMW' , 'on' ] );
+ $this->assertEmpty( $result );
+
+ $result = $method->invoke( $instance, [ 'Test::lula', 'SMW' , 'off' ] );
+ $this->assertEmpty( $result );
+ }
+
+ /**
+ * @dataProvider stripTextWithAnnotationProvider
+ */
+ public function testStrip( $text, $expectedRemoval, $expectedObscuration ) {
+
+ $this->assertEquals(
+ $expectedRemoval,
+ InTextAnnotationParser::removeAnnotation( $text )
+ );
+
+ $this->assertEquals(
+ $expectedObscuration,
+ InTextAnnotationParser::obfuscateAnnotation( $text )
+ );
+ }
+
+ public function stripTextWithAnnotationProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'Suspendisse [[Bar::tincidunt semper|abc]] facilisi',
+ 'Suspendisse abc facilisi',
+ 'Suspendisse &#91;&#91;Bar::tincidunt semper|abc]] facilisi'
+ ];
+
+ return $provider;
+ }
+
+ public function textDataProvider() {
+
+ $testEnvironment = new TestEnvironment();
+ $provider = [];
+
+ // #0 NS_MAIN; [[FooBar...]] with a different caption
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[FooBar::dictumst|寒ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.',
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[:Dictumst|寒ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.',
+ 'propertyCount' => 3,
+ 'propertyLabels' => [ 'Foo', 'Bar', 'FooBar' ],
+ 'propertyValues' => [ 'Dictumst', 'Tincidunt semper', '9001' ]
+ ]
+ ];
+
+ // #1 NS_MAIN; [[FooBar...]] with a different caption and enabled SMW_PARSER_LINV
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_LINV,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[FooBar::dictumst|寒ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::[[tincidunt semper]]]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::[http:://www/foo/9001] ]] et Donec.',
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[:Dictumst|寒ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis'.
+ ' [[:Http:://www/foo/9001|http:://www/foo/9001]] et Donec.',
+ 'propertyCount' => 3,
+ 'propertyLabels' => [ 'Foo', 'Bar', 'FooBar' ],
+ 'propertyValues' => [ 'Dictumst', 'Tincidunt semper', 'Http:://www/foo/9001' ]
+ ]
+ ];
+
+ // #2 NS_MAIN, [[-FooBar...]] produces an error with inlineErrors = true
+ // (only check for an indication of an error in 'resultText' )
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[-FooBar::dictumst|é‡ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.',
+ [
+ 'resultText' => 'class="smw-highlighter" data-type="4" data-state="inline"',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ 'Foo', 'Bar', '_ERRC' ],
+ 'propertyValues' => [ 'Tincidunt semper', '9001' ]
+ ]
+ ];
+
+ // #3 NS_MAIN, [[-FooBar...]] produces an error but inlineErrors = false
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_NONE,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[-FooBar::dictumst|軽ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.',
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' 軽ㄠcursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 3,
+ 'propertyKeys ' => [ 'Foo', 'Bar', '_ERRC' ],
+ 'propertyValues' => [ 'Tincidunt semper', '9001' ]
+ ]
+ ];
+
+ // #4 NS_HELP disabled
+ $provider[] = [
+ NS_HELP,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_HELP => false ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[FooBar::dictumst|ãŠã‚‚ã‚ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.',
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' [[:Dictumst|ãŠã‚‚ã‚ã„]] cursus. Nisl sit condimentum Quisque facilisis' .
+ ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' .
+ ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.',
+ 'propertyCount' => 0,
+ 'propertyLabels' => [],
+ 'propertyValues' => []
+ ]
+ ];
+
+ // #5 NS_HELP enabled but no properties or links at all
+ $provider[] = [
+ NS_HELP,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_HELP => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' Suspendisse tincidunt semper facilisi dolor Aenean.',
+ [
+ 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' .
+ ' Suspendisse tincidunt semper facilisi dolor Aenean.',
+ 'propertyCount' => 0,
+ 'propertyLabels' => [],
+ 'propertyValues' => []
+ ]
+ ];
+
+ // #6 Bug 54967
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ '[[Foo::?bar]], [[Foo::Baz?]], [[Quxey::B?am]]',
+ [
+ 'resultText' => '[[:?bar|?bar]], [[:Baz?|Baz?]], [[:B?am|B?am]]',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Foo', 'Quxey' ],
+ 'propertyValues' => [ '?bar', 'Baz?', 'B?am' ]
+ ]
+ ];
+
+ #7 673
+
+ // Special:Types/Number
+ $specialTypeName = \SpecialPage::getTitleFor( 'Types', 'Number' )->getPrefixedText();
+
+ $provider[] = [
+ SMW_NS_PROPERTY,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ SMW_NS_PROPERTY => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ '[[has type::number]], [[has Type::page]] ',
+ [
+ 'resultText' => "[[$specialTypeName|number]], [[:Page|page]]",
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Has type', 'Has Type' ],
+ 'propertyValues' => [ 'Number', 'Page' ]
+ ]
+ ];
+
+ #8 1048, Double-double
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ '[[Foo::Bar::Foobar]], [[IPv6::fc00:123:8000::/64]] [[ABC::10.1002/::AID-MRM16::]]',
+ [
+ 'resultText' => '[[:Bar::Foobar|Bar::Foobar]], [[:Fc00:123:8000::/64|fc00:123:8000::/64]] [[:10.1002/::AID-MRM16::|10.1002/::AID-MRM16::]]',
+ 'propertyCount' => 3,
+ 'propertyLabels' => [ 'Foo', 'IPv6', 'ABC' ],
+ 'propertyValues' => [ 'Bar::Foobar', 'Fc00:123:8000::/64', '10.1002/::AID-MRM16::' ]
+ ]
+ ];
+
+ #9 T32603
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR,
+ ],
+ '[[Foo:::Foobar]] [[Bar:::ABC|DEF]] [[Foo:::0049 30 12345678/::Foo]] ',
+ [
+ 'resultText' => '[[:Foobar|Foobar]] [[:ABC|DEF]] [[:0049 30 12345678/::Foo|0049 30 12345678/::Foo]]',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Foo', 'Bar' ],
+ 'propertyValues' => [ 'Foobar', '0049 30 12345678/::Foo', 'ABC' ]
+ ]
+ ];
+
+ #10 #1252 (disabled strict mode)
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_NONE
+ ],
+ '[[Foo::Foobar::テスト]] [[Bar:::ABC|DEF]] [[Foo:::0049 30 12345678/::Foo]] ',
+ [
+ 'resultText' => '[[:テスト|テスト]] [[:ABC|DEF]] [[:Foo|Foo]]',
+ 'propertyCount' => 4,
+ 'propertyLabels' => [ 'Foo', 'Bar:', 'Foobar', ':0049 30 12345678/' ],
+ 'propertyValues' => [ 'Foobar', 'Foo', 'ABC', 'テスト' ]
+ ]
+ ];
+
+ #11 #1747 (left pipe)
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_NONE
+ ],
+ '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ [
+ 'resultText' => '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ #12 #1747 (left pipe + including one annotation)
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_STRICT
+ ],
+ '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[Foo::Foobar::テスト]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ [
+ 'resultText' => '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[:Foobar::テスト|Foobar::テスト]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ 'Foo' ],
+ 'propertyValues' => [ 'Foobar::テスト' ]
+ ]
+ ];
+
+ #13 @@@ syntax
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_STRICT
+ ],
+ '[[Foo::@@@]] [[Bar::@@@en|Foobar]]',
+ [
+ 'resultText' => $testEnvironment->replaceNamespaceWithLocalizedText( SMW_NS_PROPERTY, '[[:Property:Foo|Foo]] [[:Property:Bar|Foobar]]' ),
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #14 [ ... ] in-text link
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_STRICT | SMW_PARSER_LINV
+ ],
+ '[[Text::Bar [http://example.org/Foo Foo]]] [[Code::Foo[1] Foobar]]',
+ [
+ 'resultText' => 'Bar [http://example.org/Foo Foo] <div class="smwpre">Foo&#91;1]&#160;Foobar</div>',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Text', 'Code' ],
+ 'propertyValues' => [ 'Bar [http://example.org/Foo Foo]', 'Foo[1] Foobar' ]
+ ]
+ ];
+
+ #15 (#2671) external [] decode use
+ $provider[] = [
+ NS_MAIN,
+ [
+ 'smwgNamespacesWithSemanticLinks' => [ NS_MAIN => true ],
+ 'smwgParserFeatures' => SMW_PARSER_INL_ERROR | SMW_PARSER_STRICT | SMW_PARSER_LINV
+ ],
+ '<sup id="cite_ref-1" class="reference">[[#cite_note-1|&#91;1&#93;]]</sup>',
+ [
+ 'resultText' => '<sup id="cite_ref-1" class="reference">[[#cite_note-1|&#91;1&#93;]]</sup>'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function magicWordDataProvider() {
+
+ $provider = [];
+
+ // #0 __NOFACTBOX__
+ $provider[] = [
+ NS_MAIN,
+ 'Lorem ipsum dolor [[Foo::dictumst cursus]] facilisi __NOFACTBOX__',
+ [ 'SMW_NOFACTBOX' ]
+ ];
+
+ // #1 __SHOWFACTBOX__
+ $provider[] = [
+ NS_HELP,
+ 'Lorem ipsum dolor [[Foo::dictumst cursus]] facilisi __SHOWFACTBOX__',
+ [ 'SMW_SHOWFACTBOX' ]
+ ];
+
+ // #2 __NOFACTBOX__, __SHOWFACTBOX__
+ $provider[] = [
+ NS_HELP,
+ 'Lorem ipsum dolor [[Foo::dictumst cursus]] facilisi __NOFACTBOX__ __SHOWFACTBOX__',
+ [ 'SMW_NOFACTBOX', 'SMW_SHOWFACTBOX' ]
+ ];
+
+ // #3 __SHOWFACTBOX__, __NOFACTBOX__
+ $provider[] = [
+ NS_HELP,
+ 'Lorem ipsum dolor [[Foo::dictumst cursus]] facilisi __SHOWFACTBOX__ __NOFACTBOX__',
+ [ 'SMW_NOFACTBOX', 'SMW_SHOWFACTBOX' ]
+ ];
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksEncoderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksEncoderTest.php
new file mode 100644
index 00000000..685470f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksEncoderTest.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW\Tests\Parser;
+
+use SMW\Parser\LinksEncoder;
+
+/**
+ * @covers \SMW\Parser\LinksEncoder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksEncoderTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider obfuscateProvider
+ */
+ public function testRoundTripLinkObfuscation( $text ) {
+
+ $newText = LinksEncoder::encodeLinks( $text );
+
+ $this->assertEquals(
+ $text,
+ LinksEncoder::removeLinkObfuscation( $newText )
+ );
+ }
+
+ /**
+ * @dataProvider obfuscateProvider
+ */
+ public function testfindAndEncodeLinks( $text, $expected ) {
+
+ $inTextAnnotationParser = $this->getMockBuilder( 'SMW\Parser\InTextAnnotationParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertEquals(
+ $expected,
+ LinksEncoder::findAndEncodeLinks( $text, $inTextAnnotationParser )
+ );
+ }
+
+ /**
+ * @dataProvider stripTextWithAnnotationProvider
+ */
+ public function testStrip( $text, $expectedRemoval, $expectedObscuration ) {
+
+ $this->assertEquals(
+ $expectedRemoval,
+ LinksEncoder::removeAnnotation( $text )
+ );
+
+ $this->assertEquals(
+ $expectedObscuration,
+ LinksEncoder::obfuscateAnnotation( $text )
+ );
+ }
+
+ public function stripTextWithAnnotationProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'Suspendisse [[Bar::tincidunt semper|abc]] facilisi',
+ 'Suspendisse abc facilisi',
+ 'Suspendisse &#91;&#91;Bar::tincidunt semper|abc]] facilisi'
+ ];
+
+ $provider[] = [
+ 'Suspendisse [[Bar::tincidunt semper]] facilisi',
+ 'Suspendisse tincidunt semper facilisi',
+ 'Suspendisse &#91;&#91;Bar::tincidunt semper]] facilisi'
+ ];
+
+ $provider[] = [
+ 'Suspendisse [[:Tincidunt semper|tincidunt semper]]',
+ 'Suspendisse [[:Tincidunt semper|tincidunt semper]]',
+ 'Suspendisse [[:Tincidunt semper|tincidunt semper]]'
+ ];
+
+ $provider[] = [
+ '[[Foo::Foobar::テスト]] [[Bar:::ABC|DEF]] [[Foo:::0049 30 12345678/::Foo]]',
+ 'Foobar::テスト DEF :0049 30 12345678/::Foo',
+ '&#91;&#91;Foo::Foobar::テスト]] &#91;&#91;Bar:::ABC|DEF]] &#91;&#91;Foo:::0049 30 12345678/::Foo]]'
+ ];
+
+ $provider[] = [
+ '%5B%5BFoo%20Bar::foobaz%5D%5D',
+ 'foobaz',
+ '&#91;&#91;Foo%20Bar::foobaz]]'
+ ];
+
+ $provider[] = [
+ 'Suspendisse tincidunt semper facilisi',
+ 'Suspendisse tincidunt semper facilisi',
+ 'Suspendisse tincidunt semper facilisi'
+ ];
+
+ // #1747
+ $provider[] = [
+ '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ '[[Foo|Bar::Foobar]] [[File:Example.png|alt=Bar::Foobar|Caption]] [[File:Example.png|Bar::Foobar|link=Foo]]',
+ '&#91;&#91;Foo|Bar::Foobar]] &#91;&#91;File:Example.png|alt=Bar::Foobar|Caption]] &#91;&#91;File:Example.png|Bar::Foobar|link=Foo]]'
+ ];
+
+ $provider[] = [
+ '[[Foo::@@@]] [[Bar::@@@|123]]',
+ ' 123',
+ '&#91;&#91;Foo::@@@]] &#91;&#91;Bar::@@@|123]]'
+ ];
+
+ $provider[] = [
+ 'Suspendisse [[SMW::off]][[Bar::tincidunt semper|abc]] facilisi[[SMW::on]] [[Bar:::ABC|DEF]]',
+ 'Suspendisse abc facilisi DEF',
+ 'Suspendisse &#91;&#91;SMW::off]]&#91;&#91;Bar::tincidunt semper|abc]] facilisi&#91;&#91;SMW::on]] &#91;&#91;Bar:::ABC|DEF]]'
+ ];
+
+ return $provider;
+ }
+
+ public function obfuscateProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'Foo',
+ 'Foo'
+ ];
+
+ $provider[] = [
+ '[[Foo]]',
+ '[[Foo]]'
+ ];
+
+ $provider[] = [
+ '[[Foo|Bar]]',
+ '[[Foo|Bar]]'
+ ];
+
+ $provider[] = [
+ '[[Foo::[[Bar]]]]',
+ '[[Foo::&#x005B;&#x005B;Bar&#x005D;&#x005D;]]'
+ ];
+
+ $provider[] = [
+ '[[Foo::[[Foo|Bar]]]]',
+ '[[Foo::&#x005B;&#x005B;Foo&#124;Bar&#x005D;&#x005D;]]'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksProcessorTest.php
new file mode 100644
index 00000000..f855bf18
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/LinksProcessorTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\Tests\Parser;
+
+use SMW\Parser\LinksProcessor;
+
+/**
+ * @covers \SMW\Parser\LinksProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class LinksProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new LinksProcessor();
+
+ $this->assertInstanceOf(
+ 'SMW\Parser\LinksProcessor',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider semanticPreLinkProvider
+ */
+ public function testPreprocess( $semanticLink, $expected ) {
+
+ $instance = new LinksProcessor();
+
+ $this->assertEquals(
+ $expected,
+ $instance->preprocess( $semanticLink )
+ );
+ }
+
+ /**
+ * @dataProvider semanticLinkProvider
+ */
+ public function testProcess( $semanticLink, $expected ) {
+
+ $instance = new LinksProcessor();
+
+ $this->assertEquals(
+ $expected,
+ $instance->process( $semanticLink )
+ );
+ }
+
+ public function semanticPreLinkProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ [
+ '[[Foo::Bar]]',
+ 'Foo',
+ 'Bar'
+ ],
+ [
+ '[[Foo::Bar]]',
+ 'Foo',
+ 'Bar'
+ ]
+ ];
+
+ $provider[] = [
+ [
+ '[[Foo::Bar|Foobar]]',
+ 'Foo',
+ 'Bar'
+ ],
+ [
+ '[[Foo::Bar|Foobar]]',
+ 'Foo',
+ 'Bar'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function semanticLinkProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ [
+ '[[Foo::Bar]]',
+ 'Foo',
+ 'Bar'
+ ],
+ [
+ [
+ 'Foo'
+ ],
+ 'Bar',
+ false
+ ]
+ ];
+
+ $provider[] = [
+ [
+ '[[Foo::Bar|Foobar]]',
+ 'Foo',
+ 'Bar'
+ ],
+ [
+ [
+ 'Foo'
+ ],
+ 'Bar',
+ false
+ ]
+ ];
+
+ $provider[] = [
+ [
+ '[[Foo::=Bar|Foobar]]',
+ 'Foo',
+ '=Bar'
+ ],
+ [
+ [
+ 'Foo'
+ ],
+ '=Bar',
+ false
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/RecursiveTextProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/RecursiveTextProcessorTest.php
new file mode 100644
index 00000000..5ad38dec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/RecursiveTextProcessorTest.php
@@ -0,0 +1,345 @@
+<?php
+
+namespace SMW\Tests\Parser;
+
+use SMW\Parser\RecursiveTextProcessor;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Parser\RecursiveTextProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RecursiveTextProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $parser;
+ private $parserOptions;
+ private $parserOutput;
+ private $title;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parserOutput->expects( $this->any() )
+ ->method( 'getHeadItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RecursiveTextProcessor::class,
+ new RecursiveTextProcessor( $this->parser )
+ );
+ }
+
+ public function testRecursivePreprocess_NO_RecursiveAnnotation() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $this->parser->expects( $this->once() )
+ ->method( 'replaceVariables' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $this->assertSame(
+ '[[SMW::off]]Foo[[SMW::on]]',
+ $instance->recursivePreprocess( 'Foo' )
+ );
+ }
+
+ public function testRecursivePreprocess_NO_RecursiveAnnotationWithAnnotationBlockToRemoveCategories() {
+
+ $this->parserOutput->expects( $this->atLeastOnce() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( \SMW\ParserData::ANNOTATION_BLOCK ) )
+ ->will( $this->returnValue( [ '123' => true ] ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $this->parser->expects( $this->once() )
+ ->method( 'replaceVariables' )
+ ->with( $this->equalTo( 'Foo [[Category:Bar]][[Category:Test: Abc]]' ) )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->uniqid( '123' );
+
+ $this->assertSame(
+ '[[SMW::off]]Foo [[SMW::on]]',
+ $instance->recursivePreprocess( 'Foo [[Category:Bar]][[Category:Test: Abc]]' )
+ );
+ }
+
+ public function testRecursivePreprocess_NO_RecursiveAnnotationWithNoAnnotationBlockToRetainCategories() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $this->parser->expects( $this->once() )
+ ->method( 'replaceVariables' )
+ ->with( $this->equalTo( 'Foo [[Category:Bar]][[Category:Test: Abc]]' ) )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->uniqid( '123' );
+
+ $this->assertSame(
+ '[[SMW::off]]Foo [[Category:Bar]][[Category:Test: Abc]][[SMW::on]]',
+ $instance->recursivePreprocess( 'Foo [[Category:Bar]][[Category:Test: Abc]]' )
+ );
+ }
+
+ public function testRecursivePreprocess_WITH_RecursiveAnnotation() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $this->parser->expects( $this->once() )
+ ->method( 'recursivePreprocess' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->setRecursiveAnnotation( true );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->recursivePreprocess( 'Foo' )
+ );
+ }
+
+ public function testRecursivePreprocess_WITH_IncompleteParser() {
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->setRecursiveAnnotation( false );
+
+ $this->assertSame(
+ '[[SMW::off]]Foo[[SMW::on]]',
+ $instance->recursivePreprocess( 'Foo' )
+ );
+
+ $instance->setRecursiveAnnotation( true );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->recursivePreprocess( 'Foo' )
+ );
+ }
+
+ public function testRecursiveTagParse() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $this->parser->expects( $this->once() )
+ ->method( 'recursiveTagParse' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->recursiveTagParse( 'Foo' )
+ );
+ }
+
+ public function testRecursiveTagParse_WITH_IncompleteParser() {
+
+ $this->parser->expects( $this->once() )
+ ->method( 'parse' )
+ ->with( $this->equalTo( 'Foo__NOTOC__' ) )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->recursiveTagParse( 'Foo' );
+ }
+
+ public function testExpandTemplates() {
+
+ $this->parser->expects( $this->once() )
+ ->method( 'preprocess' )
+ ->with( $this->equalTo( '{{Foo}}' ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->expandTemplates( '{{Foo}}' );
+ }
+
+ public function testRecursivePreprocess_ExceededRecursion() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->title ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( $this->parserOptions ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->setMaxRecursionDepth( 0 );
+ $instance->recursivePreprocess( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getError()
+ );
+ }
+
+ public function testRecursiveTagParse_ExceededRecursion() {
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->setMaxRecursionDepth( 0 );
+ $instance->recursiveTagParse( 'Foo' );
+
+ $this->assertNotEmpty(
+ $instance->getError()
+ );
+ }
+
+ public function testTranscludeAnnotationWithoutUniquidThrowsException() {
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->transcludeAnnotation( false );
+ }
+
+ public function testTranscludeAnnotation_FALSE() {
+
+ $this->parserOutput->expects( $this->atLeastOnce() )
+ ->method( 'setExtensionData' )
+ ->with(
+ $this->equalTo( \SMW\ParserData::ANNOTATION_BLOCK ),
+ $this->equalTo( [ '123' => true ] ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->uniqid( '123' );
+ $instance->transcludeAnnotation( false );
+ }
+
+ public function testReleaseAnnotationBlock() {
+
+ $this->parserOutput->expects( $this->atLeastOnce() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( \SMW\ParserData::ANNOTATION_BLOCK ) )
+ ->will( $this->returnValue( [ '123' => true ] ) );
+
+ $this->parserOutput->expects( $this->atLeastOnce() )
+ ->method( 'setExtensionData' )
+ ->with(
+ $this->equalTo( \SMW\ParserData::ANNOTATION_BLOCK ),
+ $this->equalTo( false ) );
+
+ $this->parser->expects( $this->atLeastOnce() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->parserOutput ) );
+
+ $instance = new RecursiveTextProcessor(
+ $this->parser
+ );
+
+ $instance->uniqid( '123' );
+ $instance->releaseAnnotationBlock();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/SemanticLinksParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/SemanticLinksParserTest.php
new file mode 100644
index 00000000..96956f0d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Parser/SemanticLinksParserTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\Parser;
+
+use SMW\Parser\LinksProcessor;
+use SMW\Parser\SemanticLinksParser;
+
+/**
+ * @covers \SMW\Parser\SemanticLinksParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SemanticLinksParserTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $linksProcessor = $this->getMockBuilder( 'SMW\Parser\LinksProcessor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SemanticLinksParser( $linksProcessor );
+
+ $this->assertInstanceOf(
+ 'SMW\Parser\SemanticLinksParser',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testParse( $text, $expected ) {
+
+ $instance = new SemanticLinksParser(
+ new LinksProcessor()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->parse( $text )
+ );
+ }
+
+ public function textProvider() {
+
+ $provider = [];
+
+ $provider[] = [
+ 'Foo',
+ []
+ ];
+
+ $provider[] = [
+ '[[Foo]]',
+ []
+ ];
+
+ $provider[] = [
+ '[[Foo|Bar]]',
+ []
+ ];
+
+ $provider[] = [
+ '[[Foo::[[Bar]]]]',
+ []
+ ];
+
+ $provider[] = [
+ '[[Foo::Bar]]',
+ [
+ [
+ 'Foo'
+ ],
+ 'Bar',
+ false
+ ]
+ ];
+
+ $provider[] = [
+ '[[Foo::Bar|Foobar]]',
+ [
+ [
+ 'Foo'
+ ],
+ 'Bar',
+ 'Foobar'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserDataTest.php
new file mode 100644
index 00000000..c18784bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserDataTest.php
@@ -0,0 +1,488 @@
+<?php
+
+namespace SMW\Tests;
+
+use ParserOutput;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\ParserData;
+use SMW\SemanticData;
+use Title;
+
+/**
+ * @covers \SMW\ParserData
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserDataTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( -1 ) );
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserData',
+ new ParserData( $title, $parserOutput )
+ );
+ }
+
+ public function testInitialDataIsEmpty() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $this->assertTrue(
+ $instance->getSemanticData()->isEmpty()
+ );
+ }
+
+ public function testUpdateJobState() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $instance->setOption( ParserData::OPT_CREATE_UPDATE_JOB, false );
+
+ $this->assertFalse(
+ $instance->getOption( ParserData::OPT_CREATE_UPDATE_JOB )
+ );
+ }
+
+ public function testGetterInstances() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $this->assertInstanceOf(
+ 'Title',
+ $instance->getTitle()
+ );
+
+ $this->assertInstanceOf(
+ 'ParserOutput',
+ $instance->getOutput()
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->getSubject()
+ );
+ }
+
+ public function testAddDataVlaueAndClear() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $this->assertTrue(
+ $instance->getSemanticData()->isEmpty()
+ );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $this->assertFalse(
+ $instance->getSemanticData()->isEmpty()
+ );
+
+ $instance->setEmptySemanticData();
+
+ $this->assertTrue(
+ $instance->getSemanticData()->isEmpty()
+ );
+ }
+
+ public function testAddDataValueAndPushSemanticDataToParserOutput() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $this->assertFalse( $instance->getSemanticData()->isEmpty() );
+ $instance->pushSemanticDataToParserOutput();
+
+ $title = Title::newFromText( __METHOD__ .'-1' );
+
+ $newInstance = new ParserData( $title, $instance->getOutput() );
+
+ $this->assertEquals(
+ $instance->getSemanticData()->getHash(),
+ $newInstance->getSemanticData()->getHash()
+ );
+ }
+
+ public function testSetGetSemanticData() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $this->assertTrue( $instance->getSemanticData()->isEmpty() );
+
+ $semanticData = new SemanticData(
+ DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) )
+ );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $instance->setSemanticData( $semanticData );
+
+ $this->assertFalse( $instance->getSemanticData()->isEmpty() );
+
+ $this->assertEquals(
+ $semanticData->getHash(),
+ $instance->getSemanticData()->getHash()
+ );
+ }
+
+ public function getPropertyValueDataProvider() {
+ return [
+ [ 'Foo' , 'Bar', 0, 1 ],
+ [ '-Foo' , 'Bar', 1, 0 ],
+ [ '_Foo' , 'Bar', 1, 0 ],
+ ];
+ }
+
+ /**
+ * @dataProvider getPropertyValueDataProvider
+ */
+ public function testAddDataValue( $propertyName, $value, $errorCount, $propertyCount ) {
+
+ $title = Title::newFromText( __METHOD__ );
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData( $title, $parserOutput );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText(
+ $propertyName,
+ $value
+ )
+ );
+
+ if ( $errorCount > 0 ) {
+ return $this->assertCount( $errorCount, $instance->getSemanticData()->getErrors() );
+ }
+
+ $expected = [
+ 'propertyCount' => $propertyCount,
+ 'propertyLabels' => $propertyName,
+ 'propertyValues' => $value
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testUpdateStore() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'clearData', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'clearData' );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $this->assertTrue(
+ $instance->updateStore()
+ );
+ }
+
+ public function testSkipUpdateOnMatchedMarker() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $logger = $this->getMockBuilder( '\Psr\Log\LoggerInterface' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->with( $this->stringContains( ':smw:update:7fe0bc8114c23c928b25316e4858fceb' ) )
+ ->will( $this->returnValue( 42 ) );
+
+ $cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with( $this->stringContains( ':smw:update:7fe0bc8114c23c928b25316e4858fceb' ) );
+
+ $instance = new ParserData(
+ $title,
+ new ParserOutput(),
+ $cache
+ );
+
+ $instance->setLogger( $logger );
+ $instance->markUpdate( 42 );
+
+ $this->assertFalse(
+ $instance->updateStore()
+ );
+ }
+
+ public function testHasSemanticData() {
+
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ $parserOutput
+ );
+
+ $this->assertFalse(
+ $instance->hasSemanticData( $parserOutput )
+ );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText(
+ 'Foo',
+ 'Bar'
+ )
+ );
+
+ $instance->markParserOutput();
+
+ $this->assertTrue(
+ $instance->hasSemanticData( $parserOutput )
+ );
+ }
+
+ public function testImportFromParserOutput() {
+
+ $import = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $import->addDataValue(
+ $this->dataValueFactory->newDataValueByText(
+ 'Foo',
+ 'Bar'
+ )
+ );
+
+ $import->pushSemanticDataToParserOutput();
+
+ $instance = new ParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance->importFromParserOutput( null );
+
+ $this->assertNotEquals(
+ $import->getSemanticData()->getHash(),
+ $instance->getSemanticData()->getHash()
+ );
+
+ $instance->importFromParserOutput( $import->getOutput() );
+
+ $this->assertEquals(
+ $import->getSemanticData()->getHash(),
+ $instance->getSemanticData()->getHash()
+ );
+ }
+
+ public function testAddLimitReport() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( -1 ) );
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'setLimitReportData' ] )
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setLimitReportData' )
+ ->with(
+ $this->stringContains( 'smw-limitreport-Foo' ),
+ $this->stringContains( 'Bar' ) );
+
+ // FIXME 1.22+
+ if ( !method_exists( $parserOutput, 'setLimitReportData' ) ) {
+ $this->markTestSkipped( 'LimitReportData is not available.' );
+ }
+
+ $instance = new ParserData( $title, $parserOutput );
+ $instance->addLimitReport( 'Foo', 'Bar' );
+ }
+
+ public function testIsBlocked() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( -1 ) );
+
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData(
+ $title,
+ $parserOutput
+ );
+
+ $this->assertFalse(
+ $instance->isBlocked()
+ );
+
+ $parserOutput->setExtensionData( ParserData::ANNOTATION_BLOCK, true );
+
+ $this->assertTrue(
+ $instance->isBlocked()
+ );
+ }
+
+ public function testSetGetOption() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( -1 ) );
+
+ $parserOutput = new ParserOutput();
+
+ $instance = new ParserData(
+ $title,
+ $parserOutput
+ );
+
+ $instance->setOption( $instance::NO_QUERY_DEPENDENCY_TRACE, true );
+
+ $this->assertTrue(
+ $instance->getOption( $instance::NO_QUERY_DEPENDENCY_TRACE )
+ );
+ }
+
+ public function testAddExtraParserKey() {
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOptions->expects( $this->once() )
+ ->method( 'addExtraKey' )
+ ->with( $this->stringContains( 'Foo' ) );
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( -1 ) );
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'recordOption' )
+ ->with( $this->stringContains( 'userlang' ) );
+
+ $instance = new ParserData(
+ $title,
+ $parserOutput
+ );
+
+ $instance->setParserOptions( $parserOptions );
+ $instance->addExtraParserKey( 'Foo' );
+ $instance->addExtraParserKey( 'userlang' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctionFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctionFactoryTest.php
new file mode 100644
index 00000000..c89b2dee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctionFactoryTest.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ParserFunctionFactory;
+
+/**
+ * @covers \SMW\ParserFunctionFactory
+ * @group smenatic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserFunctionFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $parserFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'ParserData', $this->parserData );
+
+ $this->parserFactory = $this->testEnvironment->getUtilityFactory()->newParserFactory();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctionFactory',
+ new ParserFunctionFactory( $parser )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctionFactory',
+ ParserFunctionFactory::newFromParser( $parser )
+ );
+ }
+
+ /**
+ * @dataProvider parserFunctionProvider
+ */
+ public function testParserFunctionInstance( $instance, $method ) {
+
+ $parser = $this->parserFactory->create( __METHOD__ );
+
+ $parserFunctionFactory = new ParserFunctionFactory( $parser );
+
+ $this->assertInstanceOf(
+ $instance,
+ call_user_func_array( [ $parserFunctionFactory, $method ], [ $parser ] )
+ );
+ }
+
+ /**
+ * @dataProvider parserFunctionDefinitionProvider
+ */
+ public function testParserFunctionDefinition( $method, $expected ) {
+
+ $parser = $this->parserFactory->create( __METHOD__ );
+
+ $parserFunctionFactory = new ParserFunctionFactory( $parser );
+
+ $definition = call_user_func_array(
+ [ $parserFunctionFactory, $method ],
+ [ '' ]
+ );
+
+ $this->assertEquals(
+ $expected,
+ $definition[0]
+ );
+
+ $this->assertInstanceOf(
+ '\Closure',
+ $definition[1]
+ );
+
+ $this->assertInternalType(
+ 'integer',
+ $definition[2]
+ );
+ }
+
+ public function testAskParserFunctionWithParserOption() {
+
+ $this->parserData->expects( $this->at( 0 ) )
+ ->method( 'setOption' )
+ ->with(
+ $this->equalTo( \SMW\ParserData::NO_QUERY_DEPENDENCY_TRACE ),
+ $this->anything() );
+
+ $parser = $this->parserFactory->create( __METHOD__ );
+ $parser->getOptions()->smwAskNoDependencyTracking = true;
+
+ $parserFunctionFactory = new ParserFunctionFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\AskParserFunction',
+ $parserFunctionFactory->newAskParserFunction( $parser )
+ );
+ }
+
+ public function parserFunctionProvider() {
+
+ $provider[] = [
+ '\SMW\ParserFunctions\RecurringEventsParserFunction',
+ 'getRecurringEventsParser'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\SubobjectParserFunction',
+ 'getSubobjectParser'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\RecurringEventsParserFunction',
+ 'newRecurringEventsParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\SubobjectParserFunction',
+ 'newSubobjectParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\AskParserFunction',
+ 'newAskParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\ShowParserFunction',
+ 'newShowParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\SetParserFunction',
+ 'newSetParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\ConceptParserFunction',
+ 'newConceptParserFunction'
+ ];
+
+ $provider[] = [
+ '\SMW\ParserFunctions\DeclareParserFunction',
+ 'newDeclareParserFunction'
+ ];
+
+ return $provider;
+ }
+
+ public function parserFunctionDefinitionProvider() {
+
+ $provider[] = [
+ 'getAskParserFunctionDefinition',
+ 'ask'
+ ];
+
+ $provider[] = [
+ 'getShowParserFunctionDefinition',
+ 'show'
+ ];
+
+ $provider[] = [
+ 'getSubobjectParserFunctionDefinition',
+ 'subobject'
+ ];
+
+ $provider[] = [
+ 'getSetRecurringEventParserFunctionDefinition',
+ 'set_recurring_event'
+ ];
+
+ $provider[] = [
+ 'getSetParserFunctionDefinition',
+ 'set'
+ ];
+
+ $provider[] = [
+ 'getConceptParserFunctionDefinition',
+ 'concept'
+ ];
+
+ $provider[] = [
+ 'getDeclareParserFunctionDefinition',
+ 'declare'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/AskParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/AskParserFunctionTest.php
new file mode 100644
index 00000000..e707eaef
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/AskParserFunctionTest.php
@@ -0,0 +1,585 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use ReflectionClass;
+use SMW\ApplicationFactory;
+use SMW\Localizer;
+use SMW\ParserFunctions\AskParserFunction;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\AskParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class AskParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $semanticDataValidator;
+ private $messageFormatter;
+ private $circularReferenceGuard;
+ private $expensiveFuncExecutionWatcher;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $this->testEnvironment->addConfiguration( 'smwgQueryProfiler', true );
+ $this->testEnvironment->addConfiguration( 'smwgQMaxLimit', 1000 );
+
+ $this->messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->expensiveFuncExecutionWatcher = $this->getMockBuilder( '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->expensiveFuncExecutionWatcher->expects( $this->any() )
+ ->method( 'hasReachedExpensiveLimit' )
+ ->will( $this->returnValue( false ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\AskParserFunction',
+ new AskParserFunction( $parserData, $this->messageFormatter, $this->circularReferenceGuard, $this->expensiveFuncExecutionWatcher )
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testParse( array $params ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->parse( $params )
+ );
+ }
+
+ public function testIsQueryDisabled() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageFormatter->expects( $this->any() )
+ ->method( 'addFromKey' )
+ ->will( $this->returnSelf() );
+
+ $this->messageFormatter->expects( $this->once() )
+ ->method( 'getHtml' );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->isQueryDisabled();
+ }
+
+ public function testHasReachedExpensiveLimit() {
+
+ $params = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list'
+ ];
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $expensiveFuncExecutionWatcher = $this->getMockBuilder( '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expensiveFuncExecutionWatcher->expects( $this->any() )
+ ->method( 'hasReachedExpensiveLimit' )
+ ->will( $this->returnValue( true ) );
+
+ $this->messageFormatter->expects( $this->any() )
+ ->method( 'addFromKey' )
+ ->will( $this->returnSelf() );
+
+ $this->messageFormatter->expects( $this->once() )
+ ->method( 'getHtml' );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $expensiveFuncExecutionWatcher
+ );
+
+ $instance->parse( $params );
+ }
+
+ public function testSetShowMode() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $reflector = new ReflectionClass( '\SMW\ParserFunctions\AskParserFunction' );
+ $showMode = $reflector->getProperty( 'showMode' );
+ $showMode->setAccessible( true );
+
+ $this->assertFalse( $showMode->getValue( $instance ) );
+ $instance->setShowMode( true );
+
+ $this->assertTrue( $showMode->getValue( $instance ) );
+ }
+
+ public function testCircularGuard() {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $this->circularReferenceGuard->expects( $this->once() )
+ ->method( 'mark' );
+
+ $this->circularReferenceGuard->expects( $this->never() )
+ ->method( 'unmark' );
+
+ $this->circularReferenceGuard->expects( $this->once() )
+ ->method( 'isCircular' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $params = [];
+
+ $this->assertEmpty(
+ $instance->parse( $params )
+ );
+ }
+
+ public function testQueryIdStabilityForFixedSetOfParametersWithFingerprintMethod() {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $params = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list'
+ ];
+
+ $instance->parse( $params );
+
+ $this->assertTrue(
+ $parserData->getSemanticData()->hasSubSemanticData( '_QUERYaa38249db4bc6d3e8133588fb08d0f0d' )
+ );
+
+ // Limit is a factor that influences the query id, count uses the
+ // max limit available in $GLOBALS['smwgQMaxLimit'] therefore we set
+ // the limit to make the test independent from possible other settings
+
+ $params = [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=count'
+ ];
+
+ $instance->parse( $params );
+
+ $this->assertTrue(
+ $parserData->getSemanticData()->hasSubSemanticData( '_QUERYb6a190747f7d3c2775730f6bc6c5e469' )
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testInstantiatedQueryData( array $params, array $expected, array $settings ) {
+
+ foreach ( $settings as $key => $value ) {
+ $this->testEnvironment->addConfiguration( $key, $value );
+ }
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->parse( $params );
+
+ foreach ( $parserData->getSemanticData()->getSubSemanticData() as $containerSemanticData ){
+ $this->assertInstanceOf( 'SMWContainerSemanticData', $containerSemanticData );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $containerSemanticData
+ );
+ }
+ }
+
+ public function testEmbeddedQueryWithError() {
+
+ $params = [
+ '[[--ABC·|DEF::123]]',
+ 'format=table'
+ ];
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_ASK', '_ERRC' ],
+ ];
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->parse( $params );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testWithDisabledQueryProfiler() {
+
+ $params = [
+ '[[Modification date::+]]',
+ 'format=table'
+ ];
+
+ $expected = [
+ 'propertyCount' => 0
+ ];
+
+ $this->testEnvironment->addConfiguration( 'smwgQueryProfiler', false );
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->parse( $params );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testNoQueryProfileOnSpecialPages() {
+
+ $params = [
+ '[[Modification date::+]]',
+ 'format=table'
+ ];
+
+ $expected = [
+ 'propertyCount' => 0
+ ];
+
+ $this->testEnvironment->addConfiguration( 'smwgQueryProfiler', true );
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__, NS_SPECIAL ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->parse( $params );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testQueryWithAnnotationMarker() {
+
+ $params = [
+ '[[Modification date::+]]',
+ 'format=table',
+ '@annotation'
+ ];
+
+ $postProcHandler = $this->getMockBuilder( '\SMW\PostProcHandler' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $postProcHandler->expects( $this->once() )
+ ->method( 'addUpdate' );
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $instance->setPostProcHandler( $postProcHandler );
+ $instance->parse( $params );
+ }
+
+ public function queryDataProvider() {
+
+ $categoryNS = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+ $fileNS = Localizer::getInstance()->getNamespaceTextById( NS_FILE );
+
+ $provider = [];
+
+ // #0
+ // {{#ask: [[Modification date::+]]
+ // |?Modification date
+ // |format=list
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]]',
+ '?Modification date',
+ 'format=list'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'list', 1, 1, '[[Modification date::+]]' ]
+ ],
+ [
+ 'smwgQueryProfiler' => true
+ ]
+ ];
+
+ // #1 Query string with spaces
+ // {{#ask: [[Modification date::+]] [[Category:Foo bar]] [[Has title::!Foo bar]]
+ // |?Modification date
+ // |?Has title
+ // |format=list
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]] [[Category:Foo bar]] [[Has title::!Foo bar]]',
+ '?Modification date',
+ '?Has title',
+ 'format=list'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'list', 4, 1, "[[Modification date::+]] [[$categoryNS:Foo bar]] [[Has title::!Foo bar]]" ]
+ ],
+ [
+ 'smwgCreateProtectionRight' => false,
+ 'smwgQueryProfiler' => true
+ ]
+ ];
+
+ // #2
+ // {{#ask: [[Modification date::+]][[Category:Foo]]
+ // |?Modification date
+ // |?Has title
+ // |format=list
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]][[Category:Foo]]',
+ '?Modification date',
+ '?Has title',
+ 'format=list'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'list', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
+ ],
+ [
+ 'smwgQueryProfiler' => true
+ ]
+ ];
+
+ // #3 Known format
+ // {{#ask: [[File:Fooo]]
+ // |?Modification date
+ // |default=no results
+ // |format=feed
+ // }}
+ $provider[] = [
+ [
+ '[[File:Fooo]]',
+ '?Modification date',
+ 'default=no results',
+ 'format=feed'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'feed', 1, 1, "[[:$fileNS:Fooo]]" ]
+ ],
+ [
+ 'smwgQueryProfiler' => true
+ ]
+ ];
+
+ // #4 Unknown format, default table
+ // {{#ask: [[Modification date::+]][[Category:Foo]]
+ // |?Modification date
+ // |?Has title
+ // |format=bar
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]][[Category:Foo]]',
+ '?Modification date',
+ '?Has title',
+ 'format=lula'
+ ],
+ [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO' ],
+ 'propertyValues' => [ 'table', 2, 1, "[[Modification date::+]] [[$categoryNS:Foo]]" ]
+ ],
+ [
+ 'smwgQueryProfiler' => true
+ ]
+ ];
+
+ // #6 Invalid parameters
+ // {{#ask: [[Modification date::+]]
+ // |?Modification date
+ // |format=list
+ // |someParameterWithoutValue
+ // |{{{template}}}
+ // |@internal
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]]',
+ 'someParameterWithoutValue',
+ '{{{template}}}',
+ 'format=list',
+ '@internal',
+ '?Modification date'
+ ],
+ [
+ 'propertyCount' => 5,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE', '_ASKFO', '_ASKPA' ],
+ 'propertyValues' => [ 'list', 1, 1, '[[Modification date::+]]', '{"limit":50,"offset":0,"sort":[""],"order":["asc"],"mode":1}' ]
+ ],
+ [
+ 'smwgQueryProfiler' => SMW_QPRFL_PARAMS
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ConceptParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ConceptParserFunctionTest.php
new file mode 100644
index 00000000..63e9c31d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ConceptParserFunctionTest.php
@@ -0,0 +1,247 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\ParserFunctions\ConceptParserFunction;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\ConceptParserFunction
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ConceptParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\ConceptParserFunction',
+ new ConceptParserFunction( $parserData, $messageFormatter )
+ );
+ }
+
+ /**
+ * @dataProvider namespaceDataProvider
+ */
+ public function testErrorForNonConceptNamespace( $namespace ) {
+
+ $parserData = $this->applicationFactory->newParserData(
+ Title::newFromText( __METHOD__, $namespace ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->once() )
+ ->method( 'addFromKey' )
+ ->with( $this->equalTo( 'smw_no_concept_namespace' ) )
+ ->will( $this->returnSelf() );
+
+ $instance = new ConceptParserFunction( $parserData, $messageFormatter );
+ $instance->parse( [] );
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testErrorForOnDoubleParse( array $params ) {
+
+ $parserData = $this->applicationFactory->newParserData(
+ Title::newFromText( __METHOD__, SMW_NS_CONCEPT ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $messageFormatter->expects( $this->once() )
+ ->method( 'addFromKey' )
+ ->with( $this->equalTo( 'smw_multiple_concepts' ) )
+ ->will( $this->returnSelf() );
+
+ $instance = new ConceptParserFunction( $parserData, $messageFormatter );
+
+ $instance->parse( $params );
+ $instance->parse( $params );
+ }
+
+ public function testExistForFoundMessageFormatterEntry() {
+
+ $parserData = $this->applicationFactory->newParserData(
+ Title::newFromText( __METHOD__, SMW_NS_CONCEPT ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $messageFormatter->expects( $this->once() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $messageFormatter->expects( $this->once() )
+ ->method( 'getHtml' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new ConceptParserFunction( $parserData, $messageFormatter );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->parse( [] )
+ );
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testParse( array $params, array $expected ) {
+
+ $parserData = $this->applicationFactory->newParserData(
+ Title::newFromText( __METHOD__, SMW_NS_CONCEPT ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $instance = new ConceptParserFunction( $parserData, $messageFormatter );
+ $instance->parse( $params );
+
+ $this->assertCount(
+ $expected['propertyCount'],
+ $parserData->getSemanticData()->getProperties()
+ );
+
+ foreach ( $parserData->getSemanticData()->getProperties() as $property ){
+
+ if ( $property->getKey() !== '_CONC' ) {
+ continue;
+ }
+
+ foreach ( $parserData->getSemanticData()->getPropertyValues( $property ) as $dataItem ) {
+ $this->assertEquals( $expected['conceptQuery'], $dataItem->getConceptQuery() );
+ $this->assertEquals( $expected['conceptDocu'], $dataItem->getDocumentation() );
+ $this->assertEquals( $expected['conceptSize'], $dataItem->getSize() );
+ $this->assertEquals( $expected['conceptDepth'], $dataItem->getDepth() );
+ }
+ }
+ }
+
+ public function queryParameterProvider() {
+
+ $provider = [];
+
+ // #0
+ // {{#concept: [[Modification date::+]]
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]]'
+ ],
+ [
+ 'result' => true,
+ 'propertyCount' => 2,
+ 'conceptQuery' => '[[Modification date::+]]',
+ 'conceptDocu' => '',
+ 'conceptSize' => 1,
+ 'conceptDepth' => 1,
+ ]
+ ];
+
+ // #1
+ // {{#concept: [[Modification date::+]]
+ // |Foooooooo
+ // }}
+ $provider[] = [
+ [
+ '[[Modification date::+]]',
+ 'Foooooooo'
+ ],
+ [
+ 'result' => true,
+ 'propertyCount' => 2,
+ 'conceptQuery' => '[[Modification date::+]]',
+ 'conceptDocu' => 'Foooooooo',
+ 'conceptSize' => 1,
+ 'conceptDepth' => 1,
+ ]
+ ];
+
+ // #2 (includes Parser object)
+ $parser = UtilityFactory::getInstance()->newParserFactory()->newFromTitle( Title::newFromText( __METHOD__ ) );
+
+ $provider[] = [
+ [
+ $parser,
+ '[[Modification date::+]]',
+ 'Foooooooo'
+ ],
+ [
+ 'result' => true,
+ 'propertyCount' => 2,
+ 'conceptQuery' => '[[Modification date::+]]',
+ 'conceptDocu' => 'Foooooooo',
+ 'conceptSize' => 1,
+ 'conceptDepth' => 1,
+ ]
+ ];
+
+ return $provider;
+
+ }
+
+ public function namespaceDataProvider() {
+ return [
+ [ NS_MAIN ],
+ [ NS_HELP ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DeclareParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DeclareParserFunctionTest.php
new file mode 100644
index 00000000..38e2c730
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DeclareParserFunctionTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\ParserFunctions\DeclareParserFunction;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\DeclareParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DeclareParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\DeclareParserFunction',
+ new DeclareParserFunction( $parserData )
+ );
+ }
+
+ /**
+ * @dataProvider argumentProvider
+ */
+ public function testParse( $args, $expected ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $ppframe = $this->getMockBuilder( '\PPFrame' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $ppframe->expects( $this->any() )
+ ->method( 'isTemplate' )
+ ->will( $this->returnValue( true ) );
+
+ $ppframe->expects( $this->any() )
+ ->method( 'expand' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $ppframe->expects( $this->any() )
+ ->method( 'getArgument' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new DeclareParserFunction( $parserData );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->parse( $ppframe, $args )
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function argumentProvider() {
+
+ $provider[] = [
+ [ 'Has foo=Bar' ],
+ [
+ 'propertyLabel' => [ 'Has foo' ],
+ 'propertyValues' => [ 'Bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DocumentationParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DocumentationParserFunctionTest.php
new file mode 100644
index 00000000..a54eadf5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/DocumentationParserFunctionTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParamProcessor\ProcessedParam;
+use ParamProcessor\ProcessingResult;
+use SMW\ParserFunctions\DocumentationParserFunction;
+
+/**
+ * @covers \SMW\ParserFunctions\DocumentationParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DocumentationParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testHandle() {
+
+ $instance = new DocumentationParserFunction();
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $processedParam = $this->getMockBuilder( ProcessedParam::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language = $this->getMockBuilder( ProcessedParam::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $language->expects( $this->any() )
+ ->method( 'getValue' )
+ ->will( $this->returnValue( 'en' ) );
+
+ $processingResult = $this->getMockBuilder( ProcessingResult::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $processingResult->expects( $this->any() )
+ ->method( 'getParameters' )
+ ->will( $this->returnValue( [
+ 'language' => $language,
+ 'format' => $processedParam,
+ 'parameters' => $processedParam ]
+ ) );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->handle( $parser, $processingResult )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ExpensiveFuncExecutionWatcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ExpensiveFuncExecutionWatcherTest.php
new file mode 100644
index 00000000..98e72165
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ExpensiveFuncExecutionWatcherTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use SMW\ParserFunctions\ExpensiveFuncExecutionWatcher;
+
+/**
+ * @covers \SMW\ParserFunctions\ExpensiveFuncExecutionWatcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ExpensiveFuncExecutionWatcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $parserData;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher',
+ new ExpensiveFuncExecutionWatcher( $this->parserData )
+ );
+ }
+
+ public function testHasReachedExpensiveLimit() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 100 ) );
+
+ $this->parserData->expects( $this->once() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new ExpensiveFuncExecutionWatcher(
+ $this->parserData
+ );
+
+ $instance->setExpensiveExecutionLimit( 1 );
+
+ $this->assertTrue(
+ $instance->hasReachedExpensiveLimit( $query )
+ );
+ }
+
+ public function testIncrementExpensiveCountOnExsitingCounter() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->will( $this->returnValue( 42 ) );
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setExtensionData' )
+ ->with(
+ $this->equalTo( ExpensiveFuncExecutionWatcher::EXPENSIVE_COUNTER ),
+ $this->equalTo( 43 ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 100 ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getOption' )
+ ->will( $this->returnValue( 100 ) );
+
+ $this->parserData->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new ExpensiveFuncExecutionWatcher(
+ $this->parserData
+ );
+
+ $instance->setExpensiveThreshold( 1 );
+ $instance->setExpensiveExecutionLimit( 1 );
+
+ $instance->incrementExpensiveCount( $query );
+ }
+
+ public function testIncrementExpensiveCountOnNull() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setExtensionData' )
+ ->with(
+ $this->equalTo( ExpensiveFuncExecutionWatcher::EXPENSIVE_COUNTER ),
+ $this->equalTo( 1 ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 100 ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getOption' )
+ ->will( $this->returnValue( 100 ) );
+
+ $this->parserData->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $parserOutput ) );
+
+ $instance = new ExpensiveFuncExecutionWatcher(
+ $this->parserData
+ );
+
+ $instance->setExpensiveThreshold( 1 );
+ $instance->setExpensiveExecutionLimit( 1 );
+
+ $instance->incrementExpensiveCount( $query );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/InfoParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/InfoParserFunctionTest.php
new file mode 100644
index 00000000..99f86f02
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/InfoParserFunctionTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use SMW\ParserFunctions\InfoParserFunction;
+
+/**
+ * @covers \SMW\ParserFunctions\InfoParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class InfoParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\InfoParserFunction',
+ new InfoParserFunction()
+ );
+ }
+
+ public function testHandle() {
+
+ $instance = new InfoParserFunction();
+
+ $parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $processedParam = $this->getMockBuilder( '\ParamProcessor\ProcessedParam' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $processingResult = $this->getMockBuilder( '\ParamProcessor\ProcessingResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $processingResult->expects( $this->any() )
+ ->method( 'getParameters' )
+ ->will( $this->returnValue( [
+ 'message' => $processedParam,
+ 'icon' => $processedParam ] ) );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->handle( $parser, $processingResult )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/RecurringEventsParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/RecurringEventsParserFunctionTest.php
new file mode 100644
index 00000000..d00fa934
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/RecurringEventsParserFunctionTest.php
@@ -0,0 +1,429 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use ReflectionClass;
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\RecurringEvents;
+use SMW\ParserFunctions\RecurringEventsParserFunction;
+use SMW\ParserParameterProcessor;
+use SMW\Subobject;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\RecurringEventsParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RecurringEventsParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ RecurringEventsParserFunction::class,
+ new RecurringEventsParserFunction(
+ $parserData,
+ $subobject,
+ $messageFormatter,
+ new RecurringEvents()
+ )
+ );
+ }
+
+ /**
+ * @dataProvider recurringEventsDataProvider
+ */
+ public function testParse( array $params, array $expected ) {
+
+ $recurringEvents = new RecurringEvents();
+ $recurringEvents->setDefaultNumRecurringEvents( 100 );
+ $recurringEvents->setMaxNumRecurringEvents( 100 );
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $instance = new RecurringEventsParserFunction(
+ new ParserData( $title, new ParserOutput() ),
+ new Subobject( $title ),
+ new MessageFormatter( \Language::factory( 'en' ) ),
+ $recurringEvents
+ );
+
+ $result = $instance->parse(
+ new ParserParameterProcessor( $params )
+ );
+
+ $this->assertTrue( $result !== '' ? $expected['errors'] : !$expected['errors'] );
+
+ $this->assertEquals(
+ $expected['parameters'],
+ $recurringEvents->getParameters()
+ );
+ }
+
+ public function recurringEventsDataProvider() {
+
+ $provider = [];
+
+ // #0
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1970
+ // |has title= Birthday
+ // |unit=year
+ // |period=12
+ // |limit=3
+ // }}
+ $provider[] = [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1970',
+ 'has title=Birthday',
+ 'unit=month',
+ 'period=12',
+ 'limit=3'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [
+ '1 February 1970',
+ '1 February 1971',
+ '1 February 1972',
+ '1 February 1973'
+ ],
+ 'property' => [
+ 'Has birthday',
+ 'Has title'
+ ],
+ 'parameters' => [
+ 'has title' => [ 'Birthday' ]
+ ]
+ ]
+ ];
+
+ // #1
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1972 02:00
+ // |has title=Test 12
+ // |unit=week
+ // |period=4
+ // |limit=3
+ // }}
+ $provider[] = [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1972 02:00',
+ 'has title=Test 2',
+ 'unit=week',
+ 'period=4',
+ 'limit=3'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [
+ '1 February 1972 02:00:00',
+ '29 February 1972 02:00:00',
+ '28 March 1972 02:00:00',
+ '25 April 1972 02:00:00'
+ ],
+ 'property' => [
+ 'Has birthday',
+ 'Has title'
+ ],
+ 'parameters' => [
+ 'has title' => [ 'Test 2' ]
+ ]
+ ]
+ ];
+
+ // #2
+ // {{#set_recurring_event:property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [
+ '4 January 2010',
+ '11 January 2010',
+ '1 February 2010',
+ 'March 16, 2010',
+ 'March 23, 2010'
+ ],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ];
+
+
+ // #3
+ // {{#set_recurring_event:property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010|+sep=;
+ // |exclude=January 18, 2010;January 25, 2010|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ '+sep=;', 'exclude=January 18, 2010;January 25, 2010',
+ '+sep=;'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [
+ '4 January 2010',
+ '11 January 2010',
+ '1 February 2010',
+ 'March 16, 2010',
+ 'March 23, 2010'
+ ],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ];
+
+ // #4 Named page reference pointer
+ // {{#set_recurring_event:FooBar
+ // |property=Has birthday
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010|+sep=;
+ // |exclude=January 18, 2010;January 25, 2010|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'FooBar',
+ 'property=Has birthday',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ '+sep=;', 'exclude=January 18, 2010;January 25, 2010',
+ '+sep=;'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [
+ '4 January 2010',
+ '11 January 2010',
+ '1 February 2010',
+ 'March 16, 2010',
+ 'March 23, 2010'
+ ],
+ 'property' => 'Has birthday',
+ 'parameters' => []
+ ]
+ ];
+
+ // #5 Simulate first parameter starting being - raising an error
+ // {{#set_recurring_event:-
+ // property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ '-',
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => false,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ // #6 Simulate first parameter starting with - raising an error
+ // {{#set_recurring_event:-Foo
+ // property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ '-Foo',
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => true,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ // #7 Simulate first parameter starting with a underscore raising an error
+ // {{#set_recurring_event:_FooBar
+ // property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ '_FooBar',
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => true,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ // #8 Simulate start date has wrong type
+ // {{#set_recurring_event:property=Has date
+ // |start=???
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ 'property=Has date',
+ 'start=???',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => true,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ // #9 Simulate missing start date
+ // {{#set_recurring_event:property=Has date
+ // |start=
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ $provider[] = [
+ [
+ 'property=Has date',
+ 'start=',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => true,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ // #10 Simulate missing property
+ // {{#set_recurring_event:property=
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010|+sep=;
+ // |exclude=January 18, 2010;January 25, 2010|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'property=',
+ 'start=January 4, 2010',
+ 'unit=week', 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ '+sep=;',
+ 'exclude=January 18, 2010;January 25, 2010',
+ '+sep=;'
+ ],
+ [
+ 'errors' => true,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SectionTagTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SectionTagTest.php
new file mode 100644
index 00000000..08b99d24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SectionTagTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use SMW\ParserFunctions\SectionTag;
+
+/**
+ * @covers \SMW\ParserFunctions\SectionTag
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SectionTagTest extends \PHPUnit_Framework_TestCase {
+
+ private $frame;
+ private $parser;
+
+ protected function setUp() {
+
+ $this->frame = $this->getMockBuilder( '\PPFrame' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parser = $this->getMockBuilder( '\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SectionTag::class,
+ new SectionTag( $this->parser, $this->frame )
+ );
+ }
+
+ public function testRegister_Enabled() {
+
+ $this->parser->expects( $this->once() )
+ ->method( 'setHook' );
+
+ $this->assertTrue(
+ SectionTag::register( $this->parser )
+ );
+ }
+
+ public function testRegister_Disabled() {
+
+ $this->parser->expects( $this->never() )
+ ->method( 'setHook' );
+
+ $this->assertFalse(
+ SectionTag::register( $this->parser, false )
+ );
+ }
+
+ public function testParse() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->parser->expects( $this->any() )
+ ->method( 'recursiveTagParse' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new SectionTag(
+ $this->parser,
+ $this->frame
+ );
+
+ $args = [];
+
+ $this->assertContains(
+ '<section>Foo</section>',
+ $instance->parse( 'Foo', $args )
+ );
+ }
+
+ public function testParse_PropertyNamespace() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_PROPERTY ) );
+
+ $this->parser->expects( $this->any() )
+ ->method( 'recursiveTagParse' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->parser->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $instance = new SectionTag(
+ $this->parser,
+ $this->frame
+ );
+
+ $args = [];
+
+ $this->assertContains(
+ '<section class="smw-property-specification">Foo</section>',
+ $instance->parse( 'Foo', $args )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SetParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SetParserFunctionTest.php
new file mode 100644
index 00000000..9301a628
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SetParserFunctionTest.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\MediaWiki\Renderer\WikitextTemplateRenderer;
+use SMW\ParameterFormatterFactory;
+use SMW\ParserFunctions\SetParserFunction;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\SetParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SetParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $templateRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\SetParserFunction',
+ new SetParserFunction( $parserData, $messageFormatter, $templateRenderer )
+ );
+ }
+
+ /**
+ * @dataProvider setParserProvider
+ */
+ public function testParse( array $params ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $messageFormatter->expects( $this->once() )
+ ->method( 'getHtml' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $templateRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SetParserFunction(
+ $parserData,
+ $messageFormatter,
+ $templateRenderer
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->parse( ParameterFormatterFactory::newFromArray( $params ) )
+ );
+ }
+
+ /**
+ * @dataProvider setParserProvider
+ */
+ public function testInstantiatedPropertyValues( array $params, array $expected ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $templateRenderer = $this->getMockBuilder( '\SMW\MediaWiki\Renderer\WikitextTemplateRenderer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SetParserFunction(
+ $parserData,
+ $messageFormatter,
+ $templateRenderer
+ );
+
+ $instance->parse( ParameterFormatterFactory::newFromArray( $params ) );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function testTemplateSupport() {
+
+ $params = [ 'Foo=bar', 'Foo=foobar', 'BarFoo=9001', 'template=FooTemplate' ];
+
+ $expected = [
+ 'errors' => 0,
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Foo', 'BarFoo' ],
+ 'propertyValues' => [ 'Bar', '9001', 'Foobar' ]
+ ];
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter->expects( $this->any() )
+ ->method( 'addFromArray' )
+ ->will( $this->returnSelf() );
+
+ $templateRenderer = new WikitextTemplateRenderer();
+
+ $instance = new SetParserFunction(
+ $parserData,
+ $messageFormatter,
+ $templateRenderer
+ );
+
+ $instance->parse(
+ ParameterFormatterFactory::newFromArray( $params )
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()
+ );
+ }
+
+ public function setParserProvider() {
+
+ // #0 Single data set
+ // {{#set:
+ // |Foo=bar
+ // }}
+ $provider[] = [
+ [ 'Foo=bar' ],
+ [
+ 'errors' => 0,
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => 'Bar'
+ ]
+ ];
+
+ // #1 Empty data set
+ // {{#set:
+ // |Foo=
+ // }}
+ $provider[] = [
+ [ 'Foo=' ],
+ [
+ 'errors' => 0,
+ 'propertyCount' => 0,
+ 'propertyLabels' => '',
+ 'propertyValues' => ''
+ ]
+ ];
+
+ // #2 Multiple data set
+ // {{#set:
+ // |BarFoo=9001
+ // |Foo=bar
+ // }}
+ $provider[] = [
+ [ 'Foo=bar', 'BarFoo=9001' ],
+ [
+ 'errors' => 0,
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Foo', 'BarFoo' ],
+ 'propertyValues' => [ 'Bar', '9001' ]
+ ]
+ ];
+
+ // #3 Multiple data set with an error record
+ // {{#set:
+ // |_Foo=9001 --> will raise an error
+ // |Foo=bar
+ // }}
+ $provider[] = [
+ [ 'Foo=bar', '_Foo=9001' ],
+ [
+ 'errors' => 1,
+ 'propertyCount' => 2,
+ 'strictPropertyValueMatch' => false,
+ 'propertyKeys' => [ 'Foo', '_ERRC' ],
+ 'propertyValues' => [ 'Bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ShowParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ShowParserFunctionTest.php
new file mode 100644
index 00000000..45a6ee9c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/ShowParserFunctionTest.php
@@ -0,0 +1,287 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use SMW\ApplicationFactory;
+use SMW\ParserFunctions\AskParserFunction;
+use SMW\ParserFunctions\ShowParserFunction;
+use SMW\Tests\TestEnvironment;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\ShowParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ShowParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $semanticDataValidator;
+ private $messageFormatter;
+ private $circularReferenceGuard;
+ private $expensiveFuncExecutionWatcher;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $this->testEnvironment->addConfiguration( 'smwgQueryProfiler', true );
+ $this->testEnvironment->addConfiguration( 'smwgQueryResultCacheType', false );
+ $this->testEnvironment->addConfiguration( 'smwgQFilterDuplicates', false );
+
+ $this->messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->expensiveFuncExecutionWatcher = $this->getMockBuilder( '\SMW\ParserFunctions\ExpensiveFuncExecutionWatcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->expensiveFuncExecutionWatcher->expects( $this->any() )
+ ->method( 'hasReachedExpensiveLimit' )
+ ->will( $this->returnValue( false ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getQueryResult' )
+ ->will( $this->returnValue( $queryResult ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $askParserFunction = new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\ShowParserFunction',
+ new ShowParserFunction( $askParserFunction )
+ );
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testParse( array $params, array $expected ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new ShowParserFunction(
+ new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ )
+ );
+
+ $result = $instance->parse( $params );
+
+ if ( $expected['output'] === '' ) {
+ $this->assertEmpty( $result, "Actual result: \"$result\"\n" );
+ } else {
+ $this->assertContains( $expected['output'], $result );
+ }
+ }
+
+ public function testIsQueryDisabled() {
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->messageFormatter->expects( $this->any() )
+ ->method( 'addFromKey' )
+ ->will( $this->returnSelf() );
+
+ $this->messageFormatter->expects( $this->once() )
+ ->method( 'getHtml' );
+
+ $instance = new ShowParserFunction(
+ new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ )
+ );
+
+ $instance->isQueryDisabled();
+ }
+
+ /**
+ * @dataProvider queryDataProvider
+ */
+ public function testInstantiatedQueryData( array $params, array $expected ) {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new ShowParserFunction(
+ new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ )
+ );
+
+ $instance->parse( $params );
+
+ foreach ( $parserData->getSemanticData()->getSubSemanticData() as $containerSemanticData ){
+ $this->assertInstanceOf( 'SMWContainerSemanticData', $containerSemanticData );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $containerSemanticData
+ );
+ }
+ }
+
+ public function testQueryWithErroneousData() {
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ Title::newFromText( __METHOD__ ),
+ new ParserOutput()
+ );
+
+ $instance = new ShowParserFunction(
+ new AskParserFunction(
+ $parserData,
+ $this->messageFormatter,
+ $this->circularReferenceGuard,
+ $this->expensiveFuncExecutionWatcher
+ )
+ );
+
+ // #2 [[..]] is not acknowledged therefore displays an error message
+ // {{#show: [[File:Fooo]]
+ // |?Modification date
+ // |default=no results
+ // |format=table
+ // }}
+ $params = [
+ '[[File:Fooo]]',
+ '?Modification date',
+ 'default=no results',
+ 'format=table'
+ ];
+
+ $instance->parse( $params );
+
+ $expected = [
+ 'output' => 'class="smwtticon warning"', // lazy content check for the error
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKFO', '_ASKDE', '_ASKSI', '_ASKST' ],
+ 'propertyValues' => [ 'table', 0, 1, '[[:]]' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()->findSubSemanticData( '_QUERY6ee7d0bb3ac4ed35537bcb89914b30ac' )
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_ERRP', '_ERRT' ],
+ ];
+
+ $errorID = null;
+
+ foreach ( $parserData->getSemanticData()->getSubSemanticData() as $subSemanticData ) {
+ if ( strpos( $subSemanticData->getSubject()->getSubobjectName(), '_ERR' ) !== false ) {
+ $errorID = $subSemanticData->getSubject()->getSubobjectName();
+ break;
+ }
+ }
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserData->getSemanticData()->findSubSemanticData( $errorID )
+ );
+ }
+
+ public function queryDataProvider() {
+
+ $provider = [];
+
+ // #0
+ // {{#show: Foo-show
+ // |?Modification date
+ // }}
+ $provider[] = [
+ [
+ 'Foo-show',
+ '?Modification date',
+ ],
+ [
+ 'output' => '',
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKFO', '_ASKDE', '_ASKSI', '_ASKST' ],
+ 'propertyValues' => [ 'list', 0, 1, '[[:Foo]]' ]
+ ]
+ ];
+
+ // #1
+ // {{#show: Help:Bar
+ // |?Modification date
+ // |default=no results
+ // }}
+ $provider[] = [
+ [
+ 'Help:Bar',
+ '?Modification date',
+ 'default=no results'
+ ],
+ [
+ 'output' => 'no results',
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_ASKFO', '_ASKDE', '_ASKSI', '_ASKST' ],
+ 'propertyValues' => [ 'list', 0, 1, '[[:Help:Bar]]' ]
+ ]
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SubobjectParserFunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SubobjectParserFunctionTest.php
new file mode 100644
index 00000000..7ac42795
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserFunctions/SubobjectParserFunctionTest.php
@@ -0,0 +1,738 @@
+<?php
+
+namespace SMW\Tests\ParserFunctions;
+
+use ParserOutput;
+use SMW\DIProperty;
+use SMW\Localizer;
+use SMW\MessageFormatter;
+use SMW\ParserData;
+use SMW\ParserFunctions\SubobjectParserFunction;
+use SMW\ParserParameterFormatter;
+use SMW\Subobject;
+use SMW\Tests\Utils\UtilityFactory;
+use SMW\Tests\PHPUnitCompat;
+use Title;
+
+/**
+ * @covers \SMW\ParserFunctions\SubobjectParserFunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SubobjectParserFunctionTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $parserData = $this->getMockBuilder( '\SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $messageFormatter = $this->getMockBuilder( '\SMW\MessageFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ParserFunctions\SubobjectParserFunction',
+ new SubobjectParserFunction(
+ $parserData,
+ $subobject,
+ $messageFormatter
+ )
+ );
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testParse( array $parameters, array $expected ) {
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+ $result = $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ $this->assertInternalType(
+ 'string',
+ $result
+ );
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testInstantiatedSubobject( array $parameters, array $expected ) {
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+ $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ $this->assertContains(
+ $expected['identifier'],
+ $subobject->getSubobjectId()
+ );
+ }
+
+ /**
+ * @dataProvider firstElementDataProvider
+ */
+ public function testFirstElementAsPropertyLabel( $isEnabled , array $parameters, array $expected ) {
+
+ $parserOutput = new ParserOutput();
+ $title = Title::newFromText( __METHOD__ );
+ $subobject = new Subobject( $title );
+
+ $instance = $this->acquireInstance( $subobject, $parserOutput );
+ $instance->useFirstElementAsPropertyLabel( $isEnabled );
+
+ $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ // If it is enabled only check for the first character {0} that should
+ // contain '_' as the rest is going to be an unknown hash value
+ $id = $subobject->getSubobjectId();
+ $this->assertEquals( $expected['identifier'], $isEnabled ? $id{0} : $id );
+
+ $parserData = new ParserData( $title, $parserOutput );
+
+ // Add generated title text as property value due to the auto reference
+ // setting
+ $expected['propertyValues'][] = $title->getText();
+
+ foreach ( $parserData->getSemanticData()->getSubSemanticData() as $containerSemanticData ){
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $containerSemanticData
+ );
+ }
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testInstantiatedPropertyValues( array $parameters, array $expected ) {
+ $this->setupInstanceAndAssertSemanticData(
+ $parameters,
+ $expected
+ );
+ }
+
+ /**
+ * @dataProvider tokuFixedParameterProvider
+ */
+ public function testSortKeyAnnotation( array $parameters, array $expected ) {
+ $this->setupInstanceAndAssertSemanticData(
+ $parameters,
+ $expected
+ );
+ }
+
+ public function testSubobjectIdStabilityForFixedSetOfParameters() {
+
+ $parameters = [
+ 'Foo=Bar'
+ ];
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+ $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ // Expected to be stable for PHP and HHVM as well
+ $this->assertEquals(
+ '_be96d37a4d7c35be8673cb4229b8fdec',
+ $subobject->getSubobjectId()
+ );
+ }
+
+ public function testParametersOnBeingSorted() {
+
+ $parameters = [
+ 'Foo=Foobar, Bar',
+ '+sep=,'
+ ];
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+
+ $instance->isComparableContent(
+ true
+ );
+
+ $instance->parse(
+ new ParserParameterFormatter( $parameters )
+ );
+
+ $this->assertEquals(
+ '_c0bea380739b21578cdcf28ed5d4cfd3',
+ $subobject->getSubobjectId()
+ );
+ }
+
+ public function testParametersOnBeingSortedWithRevertedValueOrderProducesSameHash() {
+
+ $parameters = [
+ 'Foo=Bar, Foobar',
+ '+sep=,'
+ ];
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+
+ $instance->isComparableContent(
+ true
+ );
+
+ $instance->parse(
+ new ParserParameterFormatter( $parameters )
+ );
+
+ $this->assertEquals(
+ '_c0bea380739b21578cdcf28ed5d4cfd3',
+ $subobject->getSubobjectId()
+ );
+ }
+
+ public function testParametersIsNotSorted() {
+
+ $parameters = [
+ 'Foo=Foobar, Bar',
+ '+sep=,'
+ ];
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+
+ $instance->isComparableContent(
+ false
+ );
+
+ $instance->parse(
+ new ParserParameterFormatter( $parameters )
+ );
+
+ // Expected to be stable for PHP and HHVM as well
+ $this->assertEquals(
+ '_ec7323184d89fe1409b5cfaf09950a95',
+ $subobject->getSubobjectId()
+ );
+ }
+
+ public function testCreateSameIdForNormalizedParametersWithEnabledCapitalLinks() {
+
+ $title = Title::newFromText( __METHOD__ );
+
+ $parametersOne = [
+ 'Foo=Bar',
+ 'Has foo=bar,Foo',
+ '+sep=,'
+ ];
+
+ $parametersTwo = [
+ 'foo=Bar',
+ 'has foo=Foo,bar',
+ '+sep=,'
+ ];
+
+ $subobject = new Subobject( $title );
+
+ $instance = $this->acquireInstance( $subobject );
+
+ $instance->isComparableContent( true );
+ $instance->isCapitalLinks( true );
+
+ $instance->parse(
+ new ParserParameterFormatter( $parametersOne )
+ );
+
+ $id = $subobject->getSubobjectId();
+
+ $instance->parse(
+ new ParserParameterFormatter( $parametersTwo )
+ );
+
+ $this->assertEquals(
+ $id,
+ $subobject->getSubobjectId()
+ );
+ }
+
+ public function testRestrictionOnTooShortFirstPartWhenDotIsUsedForUserNamedSubobject() {
+
+ // #1299, #1302
+ // Has dot restriction
+ // {{#subobject:foo.bar
+ // |Date=Foo
+ // }}
+ $parameters = [
+ 'foo.bar',
+ 'Date=Foo'
+ ];
+
+ $subobject = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $instance = $this->acquireInstance( $subobject );
+ $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException' );
+ $subobject->getSubobjectId();
+ }
+
+ protected function setupInstanceAndAssertSemanticData( array $parameters, array $expected ) {
+
+ $title = isset( $expected['embeddedTitle'] ) ? $expected['embeddedTitle'] : __METHOD__;
+
+ $parserOutput = new ParserOutput();
+ $title = Title::newFromText( $title );
+ $subobject = new Subobject( $title );
+
+ $instance = $this->acquireInstance(
+ $subobject,
+ $parserOutput
+ );
+
+ $instance->parse( new ParserParameterFormatter( $parameters ) );
+
+ $parserData = new ParserData(
+ $title,
+ $parserOutput
+ );
+
+ $subSemanticData = $parserData->getSemanticData()->getSubSemanticData();
+
+ if ( $expected['propertyCount'] == 0 ) {
+ $this->assertEmpty(
+ $subSemanticData
+ );
+ }
+
+ foreach ( $subSemanticData as $key => $semanticData ){
+
+ if ( strpos( $semanticData->getSubject()->getSubobjectName(), '_ERR' ) !== false ) {
+ continue;
+ }
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $semanticData
+ );
+ }
+ }
+
+ public function parameterDataProvider() {
+
+ $helpNS = Localizer::getInstance()->getNamespaceTextById( NS_HELP );
+
+ $provider = [];
+
+ #0 Anonymous identifier
+ // {{#subobject:
+ // |Foo=bar
+ // }}
+ $provider[] = [
+ [ '', 'Foo=bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => '_',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => 'Bar'
+ ]
+ ];
+
+ #1 Anonymous identifier
+ // {{#subobject:-
+ // |Foo=1001 9009
+ // }}
+ $provider[] = [
+ [ '-', 'Foo=1001 9009' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => '_',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => '1001 9009'
+ ]
+ ];
+
+ #2 Named identifier
+ // {{#subobject:FooBar
+ // |FooBar=Bar foo
+ // }}
+ $provider[] = [
+ [ 'FooBar', 'FooBar=Bar foo' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'FooBar',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'FooBar',
+ 'propertyValues' => 'Bar foo'
+ ]
+ ];
+
+ #3 Named identifier
+ // {{#subobject:Foo bar
+ // |Foo=Help:Bar
+ // }}
+ $provider[] = [
+ [ 'Foo bar', 'Foo=Help:Bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'Foo_bar',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => "$helpNS:Bar"
+ ]
+ ];
+
+ #4 Named identifier
+ // {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // }}
+ $provider[] = [
+ [ ' Foo bar foo ', 'Bar=foo Bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'Foo_bar_foo',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Bar',
+ 'propertyValues' => 'Foo Bar'
+ ]
+ ];
+
+ #5 Named identifier
+ // {{#subobject: Foo bar foo
+ // |状æ³=超やã°ã„
+ // |Bar=http://www.semantic-mediawiki.org/w/index.php?title=Subobject
+ // }}
+ $provider[] = [
+ [
+ ' Foo bar foo ',
+ '状æ³=超やã°ã„',
+ 'Bar=http://www.semantic-mediawiki.org/w/index.php?title=Subobject' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'Foo_bar_foo',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ '状æ³', 'Bar' ],
+ 'propertyValues' => [ '超やã°ã„', 'Http://www.semantic-mediawiki.org/w/index.php?title=Subobject' ]
+ ]
+ ];
+
+ #6 {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // |Modification date=foo Bar
+ // }}
+ $provider[] = [
+ [ ' Foo bar foo ', 'Bar=foo Bar', 'Modification date=foo Bar' ],
+ [
+ 'hasErrors' => true,
+ 'identifier' => 'Foo_bar_foo',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ 'Bar', '_ERRC' ],
+ 'propertyValues' => [ 'Foo Bar' ]
+ ]
+ ];
+
+ #7 {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // |-Foo=foo Bar
+ // }}
+ $provider[] = [
+ [ ' Foo bar foo ', 'Bar=foo Bar', '-Foo=foo Bar' ],
+ [
+ 'hasErrors' => true,
+ 'identifier' => 'Foo_bar_foo',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ 'Bar', '_ERRC' ],
+ 'propertyValues' => [ 'Foo Bar' ]
+ ]
+ ];
+
+ // An empty subobject is not being classified as valid (to create an object)
+ #8 {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // |Modification date=1 Jan 1970
+ // }}
+ $provider[] = [
+ [ ' Foo bar foo ', 'Modification date=1 Jan 1970' ],
+ [
+ 'hasErrors' => true,
+ 'identifier' => 'Foo_bar_foo',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ERRC' ]
+ ]
+ ];
+
+ // Get the right language for an error object
+ $diPropertyError = new DIProperty( DIProperty::TYPE_ERROR );
+
+ #9 {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // |Date=Foo
+ // }}
+ $provider[] = [
+ [ ' Foo bar foo ', 'Date=Foo' ],
+ [
+ 'hasErrors' => true,
+ 'identifier' => 'Foo_bar_foo',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ERRC' ]
+ ]
+ ];
+
+ // Not dot restriction
+ #10 {{#subobject:foobar.bar
+ // |Bar=foo Bar
+ // }}
+ $provider[] = [
+ [ 'foobar.bar', 'Bar=foo Bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'foobar.bar',
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ 'Bar' ],
+ 'propertyValues' => [ 'Foo Bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function firstElementDataProvider() {
+
+ $provider = [];
+
+ // #0 / asserting that a named identifier was turned into an anonymous id
+ // {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // }}
+ $provider[] = [
+ true,
+ [ ' Foo bar foo ', 'Bar=foo Bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => '_',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Bar', 'Foo bar foo' ],
+ 'propertyValues' => [ 'Foo Bar' ] // additional value is added during runtime
+ ]
+ ];
+
+ // #1 / asserting the validity of the named identifier
+ // {{#subobject: Foo bar foo
+ // |Bar=foo Bar
+ // }}
+ $provider[] = [
+ false,
+ [ ' Foo bar foo ', 'Bar=foo Bar' ],
+ [
+ 'hasErrors' => false,
+ 'identifier' => 'Foo_bar_foo',
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ 'Bar' ],
+ 'propertyValues' => [ 'Foo Bar' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function tokuFixedParameterProvider() {
+
+ $provider = [];
+
+ // #0 @sortkey
+ // {{#subobject:
+ // |Bar=foo Bar
+ // |@sortkey=9999
+ // }}
+ $provider[] = [
+ [
+ 'Bar=foo Bar',
+ '@sortkey=9999'
+ ],
+ [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( 'Bar' ),
+ new DIProperty( '_SKEY' )
+ ],
+ 'propertyValues' => [
+ 'Foo Bar',
+ '9999'
+ ]
+ ]
+ ];
+
+ // #1 @sortkey being empty
+ // {{#subobject:
+ // |Bar=foo Bar
+ // |@sortkey=
+ // }}
+ $provider[] = [
+ [
+ 'Bar=foo Bar',
+ '@sortkey='
+ ],
+ [
+ 'propertyCount' => 1,
+ 'properties' => [
+ new DIProperty( 'Bar' )
+ ],
+ 'propertyValues' => [
+ 'Foo Bar'
+ ]
+ ]
+ ];
+
+ // #2 @category
+ // {{#subobject:
+ // |Bar=foo Bar
+ // |@category=1001
+ // }}
+ $provider[] = [
+ [
+ 'Bar=foo Bar',
+ '@category=1001'
+ ],
+ [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( 'Bar' ),
+ new DIProperty( '_INST' )
+ ],
+ 'propertyValues' => [
+ 'Foo Bar',
+ '1001'
+ ]
+ ]
+ ];
+
+ // #3 @category empty
+ // {{#subobject:
+ // |Bar=foo Bar
+ // |@category=
+ // }}
+ $provider[] = [
+ [
+ 'Bar=foo Bar',
+ '@category='
+ ],
+ [
+ 'propertyCount' => 1,
+ 'properties' => [
+ new DIProperty( 'Bar' )
+ ],
+ 'propertyValues' => [
+ 'Foo Bar'
+ ]
+ ]
+ ];
+
+ // #4 display title to set sortkey
+ // {{#subobject:
+ // |Display title of=Foo
+ // }}
+ $provider[] = [
+ [
+ 'Display title of=Foo'
+ ],
+ [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( '_DTITLE' ),
+ new DIProperty( '_SKEY' )
+ ],
+ 'propertyValues' => [
+ 'Foo'
+ ]
+ ]
+ ];
+
+ // #4 display title to set sortkey
+ // {{#subobject:
+ // |Display title of=Foo
+ // |@sortkey=Bar
+ // }}
+ $provider[] = [
+ [
+ 'Display title of=Foo',
+ '@sortkey=Bar'
+ ],
+ [
+ 'propertyCount' => 2,
+ 'properties' => [
+ new DIProperty( '_DTITLE' ),
+ new DIProperty( '_SKEY' )
+ ],
+ 'propertyValues' => [
+ 'Foo',
+ 'Bar'
+ ]
+ ]
+ ];
+
+ // #5 `Bar` auto-linked with `LinkedToSubjectXY`
+ // {{#subobject:
+ // |@linkWith=Bar
+ // }}
+ $provider[] = [
+ [
+ '@linkWith=Bar'
+ ],
+ [
+ 'embeddedTitle' => 'LinkedToSubjectXY',
+ 'propertyCount' => 1,
+ 'properties' => [
+ new DIProperty( 'Bar' )
+ ],
+ 'propertyValues' => [
+ 'LinkedToSubjectXY'
+ ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return SubobjectParserFunction
+ */
+ private function acquireInstance( Subobject $subobject, ParserOutput $parserOutput = null ) {
+
+ if ( $parserOutput === null ) {
+ $parserOutput = new ParserOutput();
+ }
+
+ return new SubobjectParserFunction(
+ new ParserData( $subobject->getTitle(), $parserOutput ),
+ $subobject,
+ new MessageFormatter( $subobject->getTitle()->getPageLanguage() )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserParameterProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserParameterProcessorTest.php
new file mode 100644
index 00000000..abde651b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ParserParameterProcessorTest.php
@@ -0,0 +1,409 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ParserParameterProcessor;
+
+/**
+ * @covers \SMW\ParserParameterProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParserParameterProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider parametersDataProvider
+ */
+ public function testCanConstruct( array $parameters ) {
+
+ $this->assertInstanceOf(
+ 'SMW\ParserParameterProcessor',
+ new ParserParameterProcessor( $parameters )
+ );
+ }
+
+ /**
+ * @dataProvider parametersDataProvider
+ */
+ public function testGetRaw( array $parameters ) {
+
+ $instance = new ParserParameterProcessor( $parameters );
+
+ $this->assertEquals(
+ $parameters,
+ $instance->getRaw()
+ );
+ }
+
+ public function testSetParameters() {
+
+ $instance = new ParserParameterProcessor();
+
+ $parameters = [
+ 'Foo' => 'Bar'
+ ];
+
+ $instance->setParameters( $parameters );
+
+ $this->assertEquals(
+ $parameters,
+ $instance->toArray()
+ );
+ }
+
+ public function testAddAndRemoveParameter() {
+
+ $instance = new ParserParameterProcessor();
+
+ $instance->addParameter(
+ 'Foo', 'Bar'
+ );
+
+ $this->assertEquals(
+ [ 'Foo' => [ 'Bar' ] ],
+ $instance->toArray()
+ );
+
+ $instance->removeParameterByKey( 'Foo' );
+
+ $this->assertFalse(
+ $instance->hasParameter( 'Foo' )
+ );
+ }
+
+ public function testSetParameter() {
+
+ $instance = new ParserParameterProcessor();
+
+ $instance->setParameter(
+ 'Foo', []
+ );
+
+ $this->assertEmpty(
+ $instance->toArray()
+ );
+
+ $instance->setParameter(
+ 'Foo', [ 'Bar' ]
+ );
+
+ $this->assertEquals(
+ [ 'Foo' => [ 'Bar' ] ],
+ $instance->toArray()
+ );
+ }
+
+ public function testSort() {
+
+ $a = [
+ 'Has test 3=One,Two,Three',
+ '+sep',
+ 'Has test 4=Four'
+ ];
+
+ $instance = new ParserParameterProcessor(
+ $a
+ );
+
+ $paramsA = $instance->toArray();
+ $instance->sort( $paramsA );
+
+ $b = [
+ 'Has test 4=Four',
+ 'Has test 3=Two,Three,One',
+ '+sep',
+ ];
+
+ $instance = new ParserParameterProcessor(
+ $b
+ );
+
+ $paramsB = $instance->toArray();
+
+ $instance->sort( $paramsB );
+
+ $this->assertEquals(
+ $paramsA,
+ $paramsB
+ );
+ }
+
+ /**
+ * @dataProvider parametersDataProvider
+ */
+ public function testToArray( array $parameters, array $expected ) {
+
+ $instance = new ParserParameterProcessor( $parameters );
+
+ $this->assertEquals(
+ $expected,
+ $instance->toArray()
+ );
+ }
+
+ /**
+ * @dataProvider firstParameterDataProvider
+ */
+ public function testGetFirst( array $parameters, array $expected ) {
+
+ $instance = new ParserParameterProcessor( $parameters );
+
+ $this->assertEquals(
+ $expected['identifier'],
+ $instance->getFirst()
+ );
+ }
+
+ public function parametersDataProvider() {
+
+ // {{#...:
+ // |Has test 1=One
+ // }}
+ $provider[] = [
+ [
+ 'Has test 1=One'
+ ],
+ [
+ 'Has test 1' => [ 'One' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 1=One
+ // }}
+ $provider[] = [
+ [
+ [ 'Foo' ],
+ 'Has test 1=One',
+ ],
+ [
+ 'Has test 1' => [ 'One' ]
+ ],
+ [
+ 'msg' => 'Failed to recognize that only strings can be processed'
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 2=Two
+ // |Has test 2=Three;Four|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'Has test 2=Two',
+ 'Has test 2=Three;Four',
+ '+sep=;'
+ ],
+ [
+ 'Has test 2' => [ 'Two', 'Three', 'Four' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 3=One,Two,Three|+sep
+ // |Has test 4=Four
+ // }}
+ $provider[] = [
+ [
+ 'Has test 3=One,Two,Three',
+ '+sep',
+ 'Has test 4=Four'
+ ],
+ [
+ 'Has test 3' => [ 'One', 'Two', 'Three' ],
+ 'Has test 4' => [ 'Four' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 5=Test 5-1|Test 5-2|Test 5-3|Test 5-4
+ // |Has test 5=Test 5-5
+ // }}
+ $provider[] = [
+ [
+ 'Has test 5=Test 5-1',
+ 'Test 5-2',
+ 'Test 5-3',
+ 'Test 5-4',
+ 'Has test 5=Test 5-5'
+ ],
+ [
+ 'Has test 5' => [ 'Test 5-1', 'Test 5-2', 'Test 5-3', 'Test 5-4', 'Test 5-5' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 6=1+2+3|+sep=+
+ // |Has test 7=7
+ // |Has test 8=9,10,11,|+sep=
+ // }}
+ $provider[] = [
+ [
+ 'Has test 6=1+2+3',
+ '+sep=+',
+ 'Has test 7=7',
+ 'Has test 8=9,10,11,',
+ '+sep='
+ ],
+ [
+ 'Has test 6' => [ '1', '2', '3'],
+ 'Has test 7' => [ '7' ],
+ 'Has test 8' => [ '9', '10', '11' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 9=One,Two,Three|+sep=;
+ // |Has test 10=Four
+ // }}
+ $provider[] = [
+ [
+ 'Has test 9=One,Two,Three',
+ '+sep=;',
+ 'Has test 10=Four'
+ ],
+ [
+ 'Has test 9' => [ 'One,Two,Three' ],
+ 'Has test 10' => [ 'Four' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test 11=Test 5-1|Test 5-2|Test 5-3|Test 5-4
+ // |Has test 12=Test 5-5
+ // |Has test 11=9,10,11,|+sep=
+ // }}
+ $provider[] = [
+ [
+ 'Has test 11=Test 5-1',
+ 'Test 5-2',
+ 'Test 5-3',
+ 'Test 5-4',
+ 'Has test 12=Test 5-5',
+ 'Has test 11=9,10,11,',
+ '+sep='
+ ],
+ [
+ 'Has test 11' => [ 'Test 5-1', 'Test 5-2', 'Test 5-3', 'Test 5-4', '9', '10', '11' ],
+ 'Has test 12' => [ 'Test 5-5' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Has test url=http://www.semantic-mediawiki.org/w/index.php?title=Subobject;http://www.semantic-mediawiki.org/w/index.php?title=Set|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'Has test url=http://www.semantic-mediawiki.org/w/index.php?title=Subobject;http://www.semantic-mediawiki.org/w/index.php?title=Set',
+ '+sep=;'
+ ],
+ [
+ 'Has test url' => [ 'http://www.semantic-mediawiki.org/w/index.php?title=Subobject', 'http://www.semantic-mediawiki.org/w/index.php?title=Set' ]
+ ]
+ ];
+
+ // {{#...:
+ // |Foo=123|345|456|+pipe
+ // }}
+ $provider[] = [
+ [
+ 'Foo=123',
+ '345',
+ '456',
+ '+pipe'
+ ],
+ [
+ 'Foo' => [ '123|345|456' ]
+ ]
+ ];
+
+ // {{#...:
+ // |@json={ "Foo": 123}
+ // }}
+ $provider[] = [
+ [
+ '@json={ "Foo": 123}'
+ ],
+ [
+ 'Foo' => [ '123' ]
+ ]
+ ];
+
+ // {{#...:
+ // |@json={ "Foo": [123, 456] }
+ // }}
+ $provider[] = [
+ [
+ '@json={ "Foo": [123, 456] }'
+ ],
+ [
+ 'Foo' => [ '123', '456' ]
+ ]
+ ];
+
+ // Error
+ // {{#...:
+ // |@json={ "Foo": [123, 456] }
+ // }}
+ $provider[] = [
+ [
+ '@json={ Foo: [123, 456] }'
+ ],
+ [
+ '@json' => [ '{ Foo: [123, 456] }' ]
+ ]
+ ];
+
+ // Avoid spaces on individual values
+ // {{#...:
+ // |Has test=One; Two|+sep=;
+ // }}
+ $provider[] = [
+ [
+ 'Has test=One; Two',
+ '+sep=;'
+ ],
+ [
+ 'Has test' => [ 'One', 'Two' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function firstParameterDataProvider() {
+
+ // {{#subobject:
+ // |Has test 1=One
+ // }}
+ $provider[] = [
+ [ '', 'Has test 1=One'],
+ [ 'identifier' => null ]
+ ];
+
+ // {{#set_recurring_event:Foo
+ // |Has test 2=Two
+ // |Has test 2=Three;Four|+sep=;
+ // }}
+ $provider[] = [
+ [ 'Foo' , 'Has test 2=Two', 'Has test 2=Three;Four', '+sep=;' ],
+ [ 'identifier' => 'Foo' ]
+ ];
+
+ // {{#subobject:-
+ // |Has test 2=Two
+ // |Has test 2=Three;Four|+sep=;
+ // }}
+ $provider[] = [
+ [ '-', 'Has test 2=Two', 'Has test 2=Three;Four', '+sep=;' ],
+ [ 'identifier' => '-' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PermissionPthValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PermissionPthValidatorTest.php
new file mode 100644
index 00000000..bdadbe40
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PermissionPthValidatorTest.php
@@ -0,0 +1,484 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\PermissionPthValidator;
+use Title;
+
+/**
+ * @covers \SMW\PermissionPthValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PermissionPthValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $protectionValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->protectionValidator = $this->getMockBuilder( '\SMW\Protection\ProtectionValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PermissionPthValidator::class,
+ new PermissionPthValidator( $this->protectionValidator )
+ );
+ }
+
+ public function testGrantPermissionToMainNamespace() {
+
+ $title = Title::newFromText( 'Foo', NS_MAIN );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertTrue(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testToReturnFalseOnMwNamespacePermissionCheck( $title, $permission, $action, $expected ) {
+
+ $this->protectionValidator ->expects( $this->any() )
+ ->method( 'hasEditProtection' )
+ ->will( $this->returnValue( true ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'isAllowed' )
+ ->with( $this->equalTo( $permission ) )
+ ->will( $this->returnValue( false ) );
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, $action, $result )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $result
+ );
+ }
+
+ public function testNoUserPermissionOnNamespaceWithEditPermissionCheck() {
+
+ $editProtectionRight = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_PROPERTY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasProtection' )
+ ->will( $this->returnValue( true ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getEditProtectionRight' )
+ ->will( $this->returnValue( $editProtectionRight ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasEditProtectionOnNamespace' )
+ ->will( $this->returnValue( true ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'isAllowed' )
+ ->with( $this->equalTo( $editProtectionRight ) )
+ ->will( $this->returnValue( false ) );
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [ [ 'smw-edit-protection', $editProtectionRight ] ],
+ $result
+ );
+ }
+
+ public function testFalseEditProtectionRightToNeverCheckPermissionOnNonMwNamespace() {
+
+ $editProtectionRight = false;
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_PROPERTY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getEditProtectionRight' )
+ ->will( $this->returnValue( $editProtectionRight ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = '';
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertTrue(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+ }
+
+ public function testNoUserPermissionOnPropertyNamespaceWithCreateProtectionCheck() {
+
+ $createProtectionRight = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_PROPERTY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getCreateProtectionRight' )
+ ->will( $this->returnValue( $createProtectionRight ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [ [ 'smw-create-protection', null, 'Foo' ] ],
+ $result
+ );
+ }
+
+ public function testNoUserPermissionOnPropertyNamespaceWithCreateProtectionCheck_TitleExists() {
+
+ $createProtectionRight = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_PROPERTY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getCreateProtectionRight' )
+ ->will( $this->returnValue( $createProtectionRight ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [ [ 'smw-create-protection-exists', null, 'Foo' ] ],
+ $result
+ );
+ }
+
+ public function testNoUserPermissionOnCategoryNamespaceWithChangePropagationProtectionCheck() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_CATEGORY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasChangePropagationProtection' )
+ ->will( $this->returnValue( true ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [
+ [ 'smw-change-propagation-protection' ]
+ ],
+ $result
+ );
+ }
+
+ public function testUserPermissionOnCategoryNamespaceWithChangePropagationProtectionCheck() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_CATEGORY ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasChangePropagationProtection' )
+ ->will( $this->returnValue( false ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertTrue(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [],
+ $result
+ );
+ }
+
+ public function testNoUserEditPermissionOnMissingRight_SchemaNamespace() {
+
+ $editProtectionRight = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_SCHEMA ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'isAllowed' )
+ ->with( $this->equalTo( 'smw-schemaedit' ) )
+ ->will( $this->returnValue( false ) );
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'edit', $result )
+ );
+
+ $this->assertEquals(
+ [
+ [ 'smw-schema-namespace-edit-protection', 'smw-schemaedit' ]
+ ],
+ $result
+ );
+ }
+
+ public function testNoEditcontentmodelPermissionForAnyUser_SchemaNamespace() {
+
+ $editProtectionRight = 'Foo';
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'PermissionTest' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_SCHEMA ) );
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->once() )
+ ->method( 'isAllowed' )
+ ->with( $this->equalTo( 'smw-schemaedit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $result = [];
+
+ $instance = new PermissionPthValidator(
+ $this->protectionValidator
+ );
+
+ $this->assertFalse(
+ $instance->hasUserPermission( $title, $user, 'editcontentmodel', $result )
+ );
+
+ $this->assertEquals(
+ [
+ [ 'smw-schema-namespace-editcontentmodel-disallowed' ]
+ ],
+ $result
+ );
+ }
+
+ public function titleProvider() {
+
+ $provider[] = [
+ Title::newFromText( 'Smw_allows_pattern', NS_MEDIAWIKI ),
+ 'smw-patternedit',
+ 'edit',
+ [ [ 'smw-patternedit-protection', 'smw-patternedit' ] ]
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Smw_allows_pattern', NS_MEDIAWIKI ),
+ 'smw-patternedit',
+ 'delete',
+ [ [ 'smw-patternedit-protection', 'smw-patternedit' ] ]
+ ];
+
+ $provider[] = [
+ Title::newFromText( 'Smw_allows_pattern', NS_MEDIAWIKI ),
+ 'smw-patternedit',
+ 'move',
+ [ [ 'smw-patternedit-protection', 'smw-patternedit' ] ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PostProcHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PostProcHandlerTest.php
new file mode 100644
index 00000000..30d3ac4d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PostProcHandlerTest.php
@@ -0,0 +1,395 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIWikiPage;
+use SMW\PostProcHandler;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+
+/**
+ * @covers \SMW\PostProcHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PostProcHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $parserOutput;
+ private $cache;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PostProcHandler::class,
+ new PostProcHandler( $this->parserOutput, $this->cache )
+ );
+ }
+
+ public function testGetHtmlOnCookie() {
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( [ 'Bar' => true ] ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->once() )
+ ->method( 'getCookie' )
+ ->will( $this->returnValue( 'FakeCookie' ) );
+
+ $this->assertContains(
+ '<div class="smw-postproc" data-subject="Foo#0##" data-ref="[&quot;Bar&quot;]"></div>',
+ $instance->getHtml( $title, $webRequest )
+ );
+ }
+
+ public function testGetHtmlOnLinksUpdateJournalEntry() {
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( true ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with( $this->stringContains( ':post' ) );
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( [ 'Bar' => true ] ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertContains(
+ '<div class="smw-postproc" data-subject="Foo#0##" data-ref="[&quot;Bar&quot;]"></div>',
+ $instance->getHtml( $title, $webRequest )
+ );
+ }
+
+ public function testGetHtml_CheckQuery() {
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( true ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with( $this->stringContains( ':post' ) );
+
+ $this->parserOutput->expects( $this->at( 0 ) )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( [ 'Bar' => true ] ) );
+
+ $this->parserOutput->expects( $this->at( 1 ) )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_CHECK ) )
+ ->will( $this->returnValue( [ 'Foobar' ] ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $instance->setOptions(
+ [
+ 'check-query' => true
+ ]
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertContains(
+ '<div class="smw-postproc" data-subject="Foo#0##" data-ref="[&quot;Bar&quot;]" data-query="[&quot;Foobar&quot;]"></div>',
+ $instance->getHtml( $title, $webRequest )
+ );
+ }
+
+ public function testRunJobs() {
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $instance->setOptions(
+ [
+ 'run-jobs' => [ 'fooJob' => 2 ]
+ ]
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->once() )
+ ->method( 'getCookie' )
+ ->will( $this->returnValue( 'FakeCookie' ) );
+
+ $this->assertContains(
+ '<div class="smw-postproc" data-subject="Foo#0##" data-jobs="{&quot;fooJob&quot;:2}"></div>',
+ $instance->getHtml( $title, $webRequest )
+ );
+ }
+
+ /**
+ * @dataProvider validPropertyKey
+ */
+ public function testGetHtmlOnCookieAndValidChangeDiff( $key ) {
+
+ $fieldChangeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\FieldChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $fieldChangeOp->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnValue( 42 ) );
+
+ $tableChangeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\TableChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableChangeOp->expects( $this->any() )
+ ->method( 'getFieldChangeOps' )
+ ->will( $this->returnValue( [ $fieldChangeOp ] ) );
+
+ $changeDiff = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [ $tableChangeOp ],
+ [],
+ [ $key => 42 ]
+ );
+
+ $this->cache->expects( $this->at( 0 ) )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( $changeDiff->serialize() ) );
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( [ 'Bar' ] ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $webRequest = $this->getMockBuilder( '\WebRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $webRequest->expects( $this->once() )
+ ->method( 'getCookie' )
+ ->will( $this->returnValue( 'FakeCookie' ) );
+
+ $this->assertContains(
+ '<div class="smw-postproc" data-subject="Foo#0##" data-ref="[0]"></div>',
+ $instance->getHtml( $title, $webRequest )
+ );
+ }
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testAddUpdate( $gExtensionData, $sExtensionData, $query ) {
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( $gExtensionData ) );
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'setExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_UPDATE ) )
+ ->will( $this->returnValue( $sExtensionData ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $instance->addUpdate( $query );
+ }
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testAddCheck( $gExtensionData, $sExtensionData, $query ) {
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'getExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_CHECK ) )
+ ->will( $this->returnValue( $gExtensionData ) );
+
+ $this->parserOutput->expects( $this->once() )
+ ->method( 'setExtensionData' )
+ ->with( $this->equalTo( PostProcHandler::POST_EDIT_CHECK ) )
+ ->will( $this->returnValue( $sExtensionData ) );
+
+ $instance = new PostProcHandler(
+ $this->parserOutput,
+ $this->cache
+ );
+
+ $instance->setOptions(
+ [
+ 'check-query' => true
+ ]
+ );
+
+ $instance->addCheck( $query );
+ }
+
+ public function queryProvider() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'toArray' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $provider[] =[
+ null,
+ [ 'Foo' => true ],
+ $query
+ ];
+
+ $provider[] =[
+ [ 'Bar' => true ],
+ [ 'Bar' => true, 'Foo' => true ],
+ $query
+ ];
+
+ return $provider;
+ }
+
+ public function validPropertyKey() {
+
+ yield [
+ 'Foo'
+ ];
+
+ yield [
+ '_INST'
+ ];
+
+ yield [
+ '_ASK'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ProcessingErrorMsgHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ProcessingErrorMsgHandlerTest.php
new file mode 100644
index 00000000..0c639d83
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/ProcessingErrorMsgHandlerTest.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataItemFactory;
+use SMW\DIWikiPage;
+use SMW\ProcessingErrorMsgHandler;
+
+/**
+ * @covers \SMW\ProcessingErrorMsgHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ProcessingErrorMsgHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\ProcessingErrorMsgHandler',
+ new ProcessingErrorMsgHandler( $subject )
+ );
+ }
+
+ public function testGrepPropertyFromRestrictionErrorMsg() {
+
+ $this->assertNull(
+ ProcessingErrorMsgHandler::grepPropertyFromRestrictionErrorMsg( 'Foo' )
+ );
+ }
+
+ /**
+ * @dataProvider messagesProvider
+ */
+ public function testNormalizeMessages( $messages, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ ProcessingErrorMsgHandler::normalizeAndDecodeMessages( $messages, null, 'en' )
+ );
+
+ $this->assertInternalType(
+ 'string',
+ ProcessingErrorMsgHandler::getMessagesAsString( $messages, null, 'en' )
+ );
+ }
+
+ public function testPush() {
+
+ $instance = new ProcessingErrorMsgHandler(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $container = $this->getMockBuilder( '\SMWDIContainer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'addPropertyObjectValue' )
+ ->with( $this->equalTo( $this->dataItemFactory->newDIProperty( '_ERRC' ) ) );
+
+ $instance->addToSemanticData(
+ $semanticData,
+ $container
+ );
+ }
+
+ public function testGetErrorContainerFromMsg() {
+
+ $instance = new ProcessingErrorMsgHandler(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Bar' );
+ $container = $instance->newErrorContainerFromMsg( 'foo', $property );
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $container
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_ERRP', '_ERRT' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $container->getSemanticData()
+ );
+ }
+
+ public function testGetErrorContainerFromMsgWithoutProperty() {
+
+ $instance = new ProcessingErrorMsgHandler(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $container = $instance->newErrorContainerFromMsg( 'foo' );
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ERRT' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $container->getSemanticData()
+ );
+ }
+
+ public function testGetErrorContainerFromDataValue() {
+
+ $instance = new ProcessingErrorMsgHandler(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getErrors', 'getProperty' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Bar' ) ) );
+
+ $container = $instance->newErrorContainerFromDataValue( $dataValue );
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $container
+ );
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_ERRP', '_ERRT' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $container->getSemanticData()
+ );
+ }
+
+ public function messagesProvider() {
+
+ $provider[] = [
+ [],
+ []
+ ];
+
+ $provider[] = [
+ [ 'Foo' ],
+ [ 'Foo' ]
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ 'Bar' ] ],
+ [ 'Foo', 'Bar' ]
+ ];
+
+ $provider[] = [
+ [ 'smw-title', [ 'smw-title' ] ],
+ [ 'Semantic MediaWiki' ]
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ 'Bar', [ 'Bar' ] ] ],
+ [ 'Foo', 'Bar', ]
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ 'Bar', [ 'Bar' ], new \stdClass ] ],
+ [ 'Foo', 'Bar', new \stdClass ]
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ 'Bar', [ 'Bar', new \stdClass ], new \stdClass ], 'Foobar' ],
+ [ 'Foo', 'Bar', new \stdClass, new \stdClass, 'Foobar' ]
+ ];
+
+ $provider[] = [
+ [ 'Foo', [ '[2,"smw-title"]' ] ],
+ [ 'Foo' , 'Semantic MediaWiki' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAliasFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAliasFinderTest.php
new file mode 100644
index 00000000..a5fdf7e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAliasFinderTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\PropertyAliasFinder;
+
+/**
+ * @covers \SMW\PropertyAliasFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PropertyAliasFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $cache;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ }
+
+ public function testCanConstruct() {
+
+ $languageIndependentPropertyLabels = [];
+
+ $this->assertInstanceOf(
+ PropertyAliasFinder::class,
+ new PropertyAliasFinder( $this->cache )
+ );
+ }
+
+ public function testFindPropertyAliasById() {
+
+ $propertyAliases = [ 'Bar' => '_Foo' ];
+
+ $instance = new PropertyAliasFinder(
+ $this->cache,
+ $propertyAliases
+ );
+
+ $this->assertEquals(
+ $propertyAliases,
+ $instance->getKnownPropertyAliases()
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->findPropertyAliasById( '_Foo' )
+ );
+ }
+
+ public function testFindPropertyIdByAlias() {
+
+ $canonicalPropertyAliases = [ 'Bar' => '_Foo' ];
+
+ $instance = new PropertyAliasFinder(
+ $this->cache,
+ [],
+ $canonicalPropertyAliases
+ );
+
+ $this->assertEquals(
+ '_Foo',
+ $instance->findPropertyIdByAlias( 'Bar' )
+ );
+ }
+
+ public function testRegisterAliasByFixedLabel() {
+
+ $instance = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance->registerAliasByFixedLabel( '_Foo', 'Bar' );
+
+ $this->assertEquals(
+ '_Foo',
+ $instance->findPropertyIdByAlias( 'Bar' )
+ );
+ }
+
+ public function testGetKnownPropertyAliasesByLanguageCodeCached() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( [ '⧼smw-bar⧽' => '_Foo' ] ) );
+
+ $instance = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance->registerAliasByMsgKey( '_Foo', 'smw-bar' );
+
+ $this->assertEquals(
+ [ '⧼smw-bar⧽' => '_Foo' ],
+ $instance->getKnownPropertyAliasesByLanguageCode( 'en' )
+ );
+ }
+
+ public function testGetKnownPropertyAliasesByLanguageCode() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance->registerAliasByMsgKey( '_Foo', 'smw-bar' );
+
+ $msgKey = version_compare( $GLOBALS['wgVersion'], '1.28', '<' ) ? '<smw-bar>' : '⧼smw-bar⧽' ;
+
+ $this->assertEquals(
+ [ $msgKey => '_Foo' ],
+ $instance->getKnownPropertyAliasesByLanguageCode( 'en' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotatorFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotatorFactoryTest.php
new file mode 100644
index 00000000..11a9d7ab
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotatorFactoryTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\PropertyAnnotatorFactory;
+
+/**
+ * @covers \SMW\PropertyAnnotatorFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class PropertyAnnotatorFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotatorFactory',
+ new PropertyAnnotatorFactory()
+ );
+ }
+
+ public function testNewNullPropertyAnnotator() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\NullPropertyAnnotator',
+ $instance->newNullPropertyAnnotator( $semanticData )
+ );
+ }
+
+ public function testNewRedirectPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $redirectTargetFinder = $this->getMockBuilder( '\SMW\MediaWiki\RedirectTargetFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\RedirectPropertyAnnotator',
+ $instance->newRedirectPropertyAnnotator( $propertyAnnotator, $redirectTargetFinder )
+ );
+ }
+
+ public function testNewPredefinedPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageInfo = $this->getMockBuilder( '\SMW\PageInfo' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\PredefinedPropertyAnnotator',
+ $instance->newPredefinedPropertyAnnotator( $propertyAnnotator, $pageInfo )
+ );
+ }
+
+ public function testNewSortKeyPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\SortKeyPropertyAnnotator',
+ $instance->newSortKeyPropertyAnnotator( $propertyAnnotator, 'Foo' )
+ );
+ }
+
+ public function testNewTranslationPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\TranslationPropertyAnnotator',
+ $instance->newTranslationPropertyAnnotator( $propertyAnnotator, [] )
+ );
+ }
+
+ public function testNewCategoryPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\CategoryPropertyAnnotator',
+ $instance->newCategoryPropertyAnnotator( $propertyAnnotator, [] )
+ );
+ }
+
+ public function testCanConstructMandatoryTypePropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator',
+ $instance->newMandatoryTypePropertyAnnotator( $propertyAnnotator )
+ );
+ }
+
+ public function testCanConstructSchemaPropertyAnnotator() {
+
+ $propertyAnnotator = $this->getMockBuilder( '\SMW\PropertyAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\SchemaPropertyAnnotator',
+ $instance->newSchemaPropertyAnnotator( $propertyAnnotator )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/CategoryPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/CategoryPropertyAnnotatorTest.php
new file mode 100644
index 00000000..93c2b073
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/CategoryPropertyAnnotatorTest.php
@@ -0,0 +1,387 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use ParserOutput;
+use SMW\DIWikiPage;
+use SMW\ParserData;
+use SMW\PropertyAnnotators\CategoryPropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockTitle;
+
+/**
+ * @covers \SMW\PropertyAnnotators\CategoryPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class CategoryPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->semanticDataFactory = $this->testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ []
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\CategoryPropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider categoriesDataProvider
+ */
+ public function testAddCategoriesAnnotation( array $parameters, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( __METHOD__, $parameters['namespace'], '' ) )
+ ->newEmptySemanticData();
+
+ $instance = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $parameters['categories']
+ );
+
+ $instance->showHiddenCategories(
+ $parameters['settings']['showHiddenCategories']
+ );
+
+ $instance->useCategoryInstance(
+ $parameters['settings']['categoriesAsInstances']
+ );
+
+ $instance->useCategoryHierarchy(
+ $parameters['settings']['categoryHierarchy']
+ );
+
+ $instance->useCategoryRedirect(
+ false
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ /**
+ * @dataProvider categoriesDataProvider
+ */
+ public function testAddCategoriesWithParserDataUpdate( array $parameters, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( __METHOD__, $parameters['namespace'], '' ) )
+ ->newEmptySemanticData();
+
+ $title = $semanticData->getSubject()->getTitle();
+ $parserOutput = new ParserOutput();
+ $parserData = new ParserData( $title, $parserOutput );
+
+ $instance = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $parserData->getSemanticData() ),
+ $parameters['categories']
+ );
+
+ $instance->showHiddenCategories(
+ $parameters['settings']['showHiddenCategories']
+ );
+
+ $instance->useCategoryInstance(
+ $parameters['settings']['categoriesAsInstances']
+ );
+
+ $instance->useCategoryHierarchy(
+ $parameters['settings']['categoryHierarchy']
+ );
+
+ $instance->useCategoryRedirect(
+ false
+ );
+
+ $instance->addAnnotation();
+ $parserData->pushSemanticDataToParserOutput();
+
+ $parserDataAfterAnnotation = new ParserData( $title, $parserOutput );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $parserDataAfterAnnotation->getSemanticData()
+ );
+ }
+
+ /**
+ * @dataProvider hiddenCategoriesDataProvider
+ */
+ public function testAddCategoriesWithHiddenCategories( array $parameters, array $expected ) {
+
+ $expectedPageLookup = $parameters['settings']['showHiddenCategories'] ? $this->never() : $this->atLeastOnce();
+
+ $wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $wikiPage->expects( $expectedPageLookup )
+ ->method( 'getHiddenCategories' )
+ ->will( $this->returnValue( $parameters['hidCategories'] ) );
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageCreator->expects( $expectedPageLookup )
+ ->method( 'createPage' )
+ ->will( $this->returnValue( $wikiPage ) );
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( __METHOD__, $parameters['namespace'], '' ) )
+ ->newEmptySemanticData();
+
+ $this->testEnvironment->registerObject(
+ 'PageCreator',
+ $pageCreator
+ );
+
+ $instance = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $parameters['categories']
+ );
+
+ $instance->showHiddenCategories(
+ $parameters['settings']['showHiddenCategories']
+ );
+
+ $instance->useCategoryInstance(
+ $parameters['settings']['categoriesAsInstances']
+ );
+
+ $instance->useCategoryHierarchy(
+ $parameters['settings']['categoryHierarchy']
+ );
+
+ $instance->useCategoryRedirect(
+ false
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $semanticData
+ );
+ }
+
+ public function testAddCategoryOnInvalidRedirect() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnValue( new DIWikiPage( 'Foo', NS_MAIN ) ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( __METHOD__, NS_MAIN ) )
+ ->newEmptySemanticData();
+
+ $instance = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ [ 'Bar' ]
+ );
+
+ $instance->useCategoryRedirect(
+ true
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_ERRC'
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function categoriesDataProvider() {
+
+ $provider = [];
+
+ // Standard category
+ $provider[] = [
+ [
+ 'namespace' => NS_MAIN,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'settings' => [
+ 'categoryHierarchy' => false,
+ 'categoriesAsInstances' => true,
+ 'showHiddenCategories' => true
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_INST',
+ 'propertyValues' => [ 'Foo', 'Bar' ],
+ ]
+ ];
+
+ // Category hierarchy or Sub-category
+ $provider[] = [
+ [
+ 'namespace' => NS_CATEGORY,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'settings' => [
+ 'categoryHierarchy' => true,
+ 'categoriesAsInstances' => false,
+ 'showHiddenCategories' => true
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SUBC',
+ 'propertyValues' => [ 'Foo', 'Bar' ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function hiddenCategoriesDataProvider() {
+
+ $provider = [];
+
+ $hidCategory = MockTitle::buildMock( __METHOD__ );
+
+ $hidCategory->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_CATEGORY ) );
+
+ $hidCategory->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ // #0 Standard category, show hidden category
+ $provider[] = [
+ [
+ 'namespace' => NS_MAIN,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'hidCategories' => [ $hidCategory ],
+ 'settings' => [
+ 'categoryHierarchy' => false,
+ 'categoriesAsInstances' => true,
+ 'showHiddenCategories' => true
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_INST',
+ 'propertyValues' => [ 'Foo', 'Bar' ],
+ ]
+ ];
+
+ // #1 Standard category, omit hidden category
+ $provider[] = [
+ [
+ 'namespace' => NS_MAIN,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'hidCategories' => [ $hidCategory ],
+ 'settings' => [
+ 'categoryHierarchy' => false,
+ 'categoriesAsInstances' => true,
+ 'showHiddenCategories' => false
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_INST',
+ 'propertyValues' => [ 'Foo' ],
+ ]
+ ];
+
+ // #2 Category hierarchy or Sub-category, show hidden category
+ $provider[] = [
+ [
+ 'namespace' => NS_CATEGORY,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'hidCategories' => [ $hidCategory ],
+ 'settings' => [
+ 'categoryHierarchy' => true,
+ 'categoriesAsInstances' => false,
+ 'showHiddenCategories' => true
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SUBC',
+ 'propertyValues' => [ 'Foo', 'Bar' ],
+ ]
+ ];
+
+ // #3 Category hierarchy or Sub-category, omit hidden category
+ $provider[] = [
+ [
+ 'namespace' => NS_CATEGORY,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'hidCategories' => [ $hidCategory ],
+ 'settings' => [
+ 'categoryHierarchy' => true,
+ 'categoriesAsInstances' => false,
+ 'showHiddenCategories' => false
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SUBC',
+ 'propertyValues' => [ 'Foo' ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/ChainablePropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/ChainablePropertyAnnotatorTest.php
new file mode 100644
index 00000000..f5f3723d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/ChainablePropertyAnnotatorTest.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\PropertyAnnotators\CategoryPropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\PredefinedPropertyAnnotator;
+use SMW\PropertyAnnotators\SortKeyPropertyAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ChainablePropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ /**
+ * @dataProvider annotationDataProvider
+ */
+ public function testChainableDecoratorAnnotation( array $parameters, array $expected ) {
+
+ $pageInfoProvider = $this->getMockBuilder( '\SMW\PageInfo' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageInfoProvider->expects( $this->atLeastOnce() )
+ ->method( 'getModificationDate' )
+ ->will( $this->returnValue( $parameters['modificationDate'] ) );
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $categoryPropertyAnnotator = new CategoryPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $parameters['categories']
+ );
+
+ $categoryPropertyAnnotator->showHiddenCategories(
+ $parameters['settings']['showHiddenCategories']
+ );
+
+ $categoryPropertyAnnotator->useCategoryInstance(
+ $parameters['settings']['categoriesAsInstances']
+ );
+
+ $categoryPropertyAnnotator->useCategoryHierarchy(
+ $parameters['settings']['categoryHierarchy']
+ );
+
+ $categoryPropertyAnnotator->useCategoryRedirect(
+ false
+ );
+
+ $sortKeyPropertyAnnotator = new SortKeyPropertyAnnotator(
+ $categoryPropertyAnnotator,
+ $parameters['sortkey']
+ );
+
+ $predefinedPropertyAnnotator = new PredefinedPropertyAnnotator(
+ $sortKeyPropertyAnnotator,
+ $pageInfoProvider
+ );
+
+ $predefinedPropertyAnnotator->setPredefinedPropertyList(
+ $parameters['settings']['smwgPageSpecialProperties']
+ );
+
+ $predefinedPropertyAnnotator->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $predefinedPropertyAnnotator->getSemanticData()
+ );
+ }
+
+ public function annotationDataProvider() {
+
+ $provider = [];
+
+ // #0
+ $provider[] = [
+ [
+ 'modificationDate' => 1272508903,
+ 'categories' => [ 'Foo', 'Bar' ],
+ 'sortkey' => 'Lala',
+ 'settings' => [
+ 'categoryHierarchy' => false,
+ 'categoriesAsInstances' => true,
+ 'showHiddenCategories' => true,
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MODIFICATION_DATE ]
+ ]
+ ],
+ [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_INST', '_MDAT', '_SKEY' ],
+ 'propertyValues' => [ 'Foo', 'Bar', '2010-04-29T02:41:43', 'Lala' ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/DisplayTitlePropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/DisplayTitlePropertyAnnotatorTest.php
new file mode 100644
index 00000000..e2917a57
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/DisplayTitlePropertyAnnotatorTest.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\PropertyAnnotators\DisplayTitlePropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\PropertyAnnotators\DisplayTitlePropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DisplayTitlePropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+
+ $this->semanticDataFactory = $testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ $this->semanticDataValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DisplayTitlePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\DisplayTitlePropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider displayTitleProvider
+ */
+ public function testAddAnnotationForDisplayTitle( $title, $displayTitle, $defaultSort, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ $title
+ );
+
+ $instance = new DisplayTitlePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $displayTitle,
+ $defaultSort
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testAddAnnotationForWhenPropertyNamespaceIsUsed() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY )
+ );
+
+ $instance = new DisplayTitlePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ 'Bar'
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ 'Bar' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testNoAnnotationWhenDisabled() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ DIWikiPage::newFromText( 'Foo' )
+ );
+
+ $instance = new DisplayTitlePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ 'Bar'
+ );
+ $instance->canCreateAnnotation( false );
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 0
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function displayTitleProvider() {
+
+ $provider = [];
+
+ #0 with title entry
+ $provider[] = [
+ 'Foo',
+ 'Lala',
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ 'Lala' ],
+ ]
+ ];
+
+ #1 Empty
+ $provider[] = [
+ 'Bar',
+ '',
+ '',
+ [
+ 'propertyCount' => 0,
+ 'propertyKeys' => '',
+ 'propertyValues' => [],
+ ]
+ ];
+
+ #2 Empty
+ $provider[] = [
+ 'Bar',
+ false,
+ '',
+ [
+ 'propertyCount' => 0,
+ 'propertyKeys' => '',
+ 'propertyValues' => [],
+ ]
+ ];
+
+ #3 Strip tags
+ $provider[] = [
+ 'Bar',
+ '<span style="position: absolute; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px);">FOO</span>',
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ 'FOO' ],
+ ]
+ ];
+
+
+ #4 Strip tags
+ $provider[] = [
+ 'Foo',
+ "A 'quote' is <b>bold</b>",
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ "A 'quote' is bold" ],
+ ]
+ ];
+
+ #5 with different sortkey
+ $provider[] = [
+ 'Foo',
+ 'Lala',
+ 'BAR',
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_DTITLE' ],
+ 'propertyValues' => [ 'Lala' ],
+ ]
+ ];
+
+ #6 unencoded Html entity
+ $provider[] = [
+ 'Foo',
+ 'ABC & DEF',
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ 'ABC & DEF' ],
+ ]
+ ];
+
+ #7 decoded/encoded Html entity
+ $provider[] = [
+ 'Foo',
+ 'ABC &amp; DEF',
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ 'ABC & DEF' ],
+ ]
+ ];
+
+ #8 decoded/encoded ' (&#39;) entity
+ $provider[] = [
+ 'Foo',
+ 'ABC &#39; DEF',
+ '',
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_DTITLE', '_SKEY' ],
+ 'propertyValues' => [ "ABC ' DEF" ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/EditProtectedPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/EditProtectedPropertyAnnotatorTest.php
new file mode 100644
index 00000000..4b33749b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/EditProtectedPropertyAnnotatorTest.php
@@ -0,0 +1,232 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DataItemFactory;
+use SMW\PropertyAnnotators\EditProtectedPropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\PropertyAnnotators\EditProtectedPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EditProtectedPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->semanticDataFactory = $testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ $this->semanticDataValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new EditProtectedPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $title
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\EditProtectedPropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testAddAnnotationForDisplayTitle( $title, $editProtectionRight, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ $title
+ );
+
+ $instance = new EditProtectedPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $title
+ );
+
+ $instance->setEditProtectionRight( $editProtectionRight );
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testAddTopIndicatorToFromMatchableRestriction() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // FIXME 3.0; Only MW 1.25+ (ParserOutput::setIndicator)
+ if ( !method_exists( $parserOutput, 'setIndicator' ) ) {
+ return $this->markTestSkipped( 'Only MW 1.25+ (ParserOutput::setIndicator)' );
+ }
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setIndicator' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->once() )
+ ->method( 'getRestrictions' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $instance = new EditProtectedPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $title
+ );
+
+ $instance->setEditProtectionRight( 'Foo' );
+ $instance->addTopIndicatorTo( $parserOutput );
+ }
+
+ public function titleProvider() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getRestrictions' )
+ ->will( $this->returnValue( [] ) );
+
+ $provider = [];
+
+ #0 no EditProtectionRight
+ $provider[] = [
+ $title,
+ false,
+ [
+ 'propertyCount' => 0,
+ 'propertyKeys' => [],
+ 'propertyValues' => [],
+ ]
+ ];
+
+ #1
+ $provider[] = [
+ $title,
+ 'Foo',
+ [
+ 'propertyCount' => 0,
+ 'propertyKeys' => [],
+ 'propertyValues' => [],
+ ]
+ ];
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getRestrictions' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ #2
+ $provider[] = [
+ $title,
+ 'Foo',
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_EDIP' ],
+ 'propertyValues' => [ true ],
+ ]
+ ];
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( 0 ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->never() )
+ ->method( 'getRestrictions' );
+
+ #3
+ $provider[] = [
+ $title,
+ 'Foo',
+ [
+ 'propertyCount' => 0,
+ 'propertyKeys' => [],
+ 'propertyValues' => [],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/MandatoryTypePropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/MandatoryTypePropertyAnnotatorTest.php
new file mode 100644
index 00000000..81263d59
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/MandatoryTypePropertyAnnotatorTest.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDIUri as DIUri;
+
+/**
+ * @covers \SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class MandatoryTypePropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator',
+ $instance
+ );
+ }
+
+ public function testNoImportForNoProperty() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->never() )
+ ->method( 'getPropertyValues' );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $instance->addAnnotation();
+ }
+
+ public function testNoImportForPredefinedProperty() {
+
+ $subject = DIWikiPage::newFromText( 'Modification date', SMW_NS_PROPERTY );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->never() )
+ ->method( 'getPropertyValues' );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $instance->addAnnotation();
+ }
+
+ public function testValidImportTypeReferenceToSetType() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ );
+
+ $importValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( 'foo' . ' ' . 'bar' . ' ' . 'buz' . ' ' . 'Type:Text' ),
+ new DIProperty( '_IMPO' )
+ );
+
+ $semanticData->addDataValue( $importValue );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'properties' => [ new DIProperty( '_TYPE' ), new DIProperty( '_IMPO' ) ],
+ 'propertyValues' => [ 'Text', 'foo:bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testValidImportTypeReferenceToOverrideUserType() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ );
+
+ $importValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( 'foo' . ' ' . 'bar' . ' ' . 'buz' . ' ' . 'Type:Page' ),
+ new DIProperty( '_IMPO' )
+ );
+
+ $semanticData->addDataValue( $importValue );
+
+ $typeValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIUri( 'http', 'semantic-mediawiki.org/swivt/1.0', '', '_txt' ),
+ new DIProperty( '_TYPE' )
+ );
+
+ $semanticData->addDataValue( $typeValue );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ // Check before
+ $expected = [
+ 'properties' => [ new DIProperty( '_TYPE' ), new DIProperty( '_IMPO' ) ],
+ 'propertyValues' => [ 'Text', 'foo:bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+
+ $instance->addAnnotation();
+
+ // Check after
+ $expected = [
+ 'properties' => [ new DIProperty( '_TYPE' ), new DIProperty( '_IMPO' ) ],
+ 'propertyValues' => [ 'Page', 'foo:bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testInvalidImportTypeReferenceDoesNotSetAnyType() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ );
+
+ $importValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( 'foo' . ' ' . 'bar' . ' ' . 'buz' . ' ' . 'Type-Text' ),
+ new DIProperty( '_IMPO' )
+ );
+
+ $semanticData->addDataValue( $importValue );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'properties' => [ new DIProperty( '_IMPO' ) ],
+ 'propertyValues' => [ 'foo:bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testBogusImportTypeDoesNotSetAnyType() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData(
+ DIWikiPage::newFromText( __METHOD__, SMW_NS_PROPERTY )
+ );
+
+ $importValue = DataValueFactory::getInstance()->newDataValueByItem(
+ new DIBlob( 'foo' . ' ' . 'bar' . ' ' . 'buz' . ' ' . 'Type:Bogus' ),
+ new DIProperty( '_IMPO' )
+ );
+
+ $semanticData->addDataValue( $importValue );
+
+ $instance = new MandatoryTypePropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData )
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'properties' => [ new DIProperty( '_IMPO' ) ],
+ 'propertyValues' => [ 'foo:bar' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/NullPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/NullPropertyAnnotatorTest.php
new file mode 100644
index 00000000..ff2b5e5c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/NullPropertyAnnotatorTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+
+/**
+ * @covers \SMW\PropertyAnnotators\NullPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NullPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\NullPropertyAnnotator',
+ new NullPropertyAnnotator( $semanticData )
+ );
+ }
+
+ public function testMethodAccess() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new NullPropertyAnnotator( $semanticData );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $instance->getSemanticData()
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\NullPropertyAnnotator',
+ $instance->addAnnotation()
+ );
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/PredefinedPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/PredefinedPropertyAnnotatorTest.php
new file mode 100644
index 00000000..2d70ee9e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/PredefinedPropertyAnnotatorTest.php
@@ -0,0 +1,366 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\PredefinedPropertyAnnotator;
+use SMW\Tests\Utils\Mock\MockTitle;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @covers \SMW\PropertyAnnotators\PredefinedPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PredefinedPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $pageInfo = $this->getMockBuilder( '\SMW\PageInfo' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PredefinedPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $pageInfo
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\PredefinedPropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider specialPropertiesDataProvider
+ */
+ public function testAddSpecialProperties( array $parameters, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( $parameters['subject'] )
+ ->newEmptySemanticData();
+
+ $pageInfo = $this->getMockBuilder( '\SMW\PageInfo' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $parameters['pageInfo'] as $method => $returnValue ) {
+ $pageInfo->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->returnValue( $returnValue ) );
+ }
+
+ $instance = new PredefinedPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $pageInfo
+ );
+
+ $instance->setPredefinedPropertyList(
+ $parameters['settings']['smwgPageSpecialProperties']
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function specialPropertiesDataProvider() {
+
+ $provider = [];
+
+ #0 Unknown
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'UNKNOWN' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ 'Lala', '_Lula', '-Lila', '' ]
+ ],
+ 'pageInfo' => [],
+ ],
+ [
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ #1 TYPE_MODIFICATION_DATE
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'withModificationDate' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MODIFICATION_DATE ]
+ ],
+ 'pageInfo' => [ 'getModificationDate' => 1272508903 ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_MDAT',
+ 'propertyValues' => [ '2010-04-29T02:41:43' ],
+ ]
+ ];
+
+ #2 TYPE_CREATION_DATE
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'withCreationDate' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_CREATION_DATE ]
+ ],
+ 'pageInfo' => [ 'getCreationDate' => 1272508903 ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_CDAT',
+ 'propertyValues' => [ '2010-04-29T02:41:43' ],
+ ]
+ ];
+
+ #3 TYPE_NEW_PAGE
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'NEW_PAGE_isNew' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_NEW_PAGE ]
+ ],
+ 'pageInfo' => [ 'isNewPage' => true ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_NEWP',
+ 'propertyValues' => [ true ],
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'NEW_PAGE_isNotNew' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_NEW_PAGE ]
+ ],
+ 'pageInfo' => [ 'isNewPage' => false ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_NEWP',
+ 'propertyValues' => [ false ],
+ ]
+ ];
+
+ #5 TYPE_LAST_EDITOR
+ $userPage = MockTitle::buildMock( 'Lula' );
+ $userNS = Localizer::getInstance()->getNamespaceTextById( NS_USER );
+
+ $userPage->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_USER ) );
+
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'withLastEditor' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_LAST_EDITOR ]
+ ],
+ 'pageInfo' => [ 'getLastEditor' => $userPage ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_LEDT',
+ 'propertyValues' => [ ":$userNS:Lula" ],
+ ]
+ ];
+
+ #6 Combined entries
+ $userPage = MockTitle::buildMock( 'Lula' );
+ $userNS = Localizer::getInstance()->getNamespaceTextById( NS_USER );
+
+ $userPage->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_USER ) );
+
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'withCombinedEntries' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ '_MDAT', '_LEDT' ]
+ ],
+ 'pageInfo' => [
+ 'getModificationDate' => 1272508903,
+ 'getLastEditor' => $userPage
+ ]
+ ],
+ [
+ 'propertyCount' => 2,
+ 'propertyKeys' => [ '_MDAT', '_LEDT' ],
+ 'propertyValues' => [ '2010-04-29T02:41:43', ":$userNS:Lula" ],
+ ]
+ ];
+
+ #7 TYPE_MEDIA
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'MimePropertyForFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MEDIA ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMediaType' => 'FooMedia'
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_MEDIA',
+ 'propertyValues' => [ 'FooMedia' ],
+ ]
+ ];
+
+ #8
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'MediaPropertyForNonFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MEDIA ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => false,
+ 'getMediaType' => 'FooMedia'
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #9 TYPE_MIME
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'MimePropertyForFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MIME ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMimeType' => 'FooMime'
+ ]
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_MIME',
+ 'propertyValues' => [ 'FooMime' ],
+ ]
+ ];
+
+ #10
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'MimePropertyForNonFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MIME ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => false,
+ 'getMimeType' => 'FooMime'
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #11 Empty TYPE_MIME
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'EmptyMimePropertyFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MIME ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMimeType' => ''
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #12 Empty TYPE_MEDIA
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'EmptyMediaPropertyFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MEDIA ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMediaType' => ''
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #13 Null TYPE_MIME
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'NullMimePropertyFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MIME ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMimeType' => null
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ #14 Null TYPE_MEDIA
+ $provider[] = [
+ [
+ 'subject' => DIWikiPage::newFromTitle( Title::newFromText( 'NullMediaPropertyFilePage' ) ),
+ 'settings' => [
+ 'smwgPageSpecialProperties' => [ DIProperty::TYPE_MEDIA ]
+ ],
+ 'pageInfo' => [
+ 'isFilePage' => true,
+ 'getMimeType' => null
+ ]
+ ],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/RedirectPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/RedirectPropertyAnnotatorTest.php
new file mode 100644
index 00000000..73b8b8cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/RedirectPropertyAnnotatorTest.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\MediaWiki\RedirectTargetFinder;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\RedirectPropertyAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\PropertyAnnotators\RedirectPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RedirectPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $redirectTargetFinder = $this->getMockBuilder( '\SMW\MediaWiki\RedirectTargetFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RedirectPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $redirectTargetFinder
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\RedirectPropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider redirectsDataProvider
+ */
+ public function testAddAnnotation( array $parameter, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $redirectTargetFinder = new RedirectTargetFinder();
+
+ $instance = new RedirectPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $redirectTargetFinder->findRedirectTargetFromText( $parameter['text'] )
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function redirectsDataProvider() {
+
+ // #0 Free text
+ $provider[] = [
+ [ 'text' => '#REDIRECT [[:Lala]]' ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_REDI',
+ 'propertyValues' => ':Lala'
+ ]
+ ];
+
+ // #1 Free text
+ $provider[] = [
+ [ 'text' => '#REDIRECT [[Lala]]' ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_REDI',
+ 'propertyValues' => ':Lala'
+ ]
+ ];
+
+
+ // #2 Invalid free text
+ $provider[] = [
+ [ 'text' => '#REDIR [[:Lala]]' ],
+ [
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ // #3 Empty
+ $provider[] = [
+ [ 'text' => '' ],
+ [
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SchemaPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SchemaPropertyAnnotatorTest.php
new file mode 100644
index 00000000..d2b23d1c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SchemaPropertyAnnotatorTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DataItemFactory;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\SchemaPropertyAnnotator;
+use SMW\Schema\SchemaDefinition;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\PropertyAnnotators\SchemaPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->semanticDataFactory = $testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ $this->semanticDataValidator = $testEnvironment->newValidatorFactory()->newSemanticDataValidator();
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SchemaPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ new SchemaDefinition( 'foo', [] )
+ );
+
+ $this->assertInstanceOf(
+ SchemaPropertyAnnotator::class,
+ $instance
+ );
+ }
+
+ public function testAddAnnotation() {
+
+ $def = [
+ SchemaDefinition::SCHEMA_TYPE => 'bar',
+ SchemaDefinition::SCHEMA_DESCRIPTION => '...',
+ SchemaDefinition::SCHEMA_TAG => [ 'foobar' ],
+ ];
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $instance = new SchemaPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ new SchemaDefinition( 'foo', $def )
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 4,
+ 'propertyKeys' => [ '_SCHEMA_TYPE', '_SCHEMA_DEF', '_SCHEMA_DESC', '_SCHEMA_TAG' ],
+ 'propertyValues' => [ 'bar', '...', 'foobar', '{"type":"bar","description":"...","tags":["foobar"]}' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SortKeyPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SortKeyPropertyAnnotatorTest.php
new file mode 100644
index 00000000..abc4095e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/SortKeyPropertyAnnotatorTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DataItemFactory;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\SortKeyPropertyAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\PropertyAnnotators\SortKeyPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SortKeyPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $semanticDataValidator;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SortKeyPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ 'Foo'
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyAnnotators\SortKeyPropertyAnnotator',
+ $instance
+ );
+ }
+
+ /**
+ * @dataProvider defaultSortDataProvider
+ */
+ public function testAddAnnotation( array $parameters, array $expected ) {
+
+ $semanticData = $this->semanticDataFactory->setTitle( $parameters['title'] )->newEmptySemanticData();
+
+ $instance = new SortKeyPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $parameters['sort']
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testDontOverrideAnnotationIfAlreadyAvailable() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_SKEY' ),
+ $this->dataItemFactory->newDIBlob( 'FOO' )
+ );
+
+ $instance = new SortKeyPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ 'bar'
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SKEY',
+ 'propertyValues' => [ 'FOO' ],
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function defaultSortDataProvider() {
+
+ $provider = [];
+
+ // Sort entry
+ $provider[] = [
+ [
+ 'title' => 'Foo',
+ 'sort' => 'Lala'
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SKEY',
+ 'propertyValues' => [ 'Lala' ],
+ ]
+ ];
+
+ // Empty
+ $provider[] = [
+ [
+ 'title' => 'Bar',
+ 'sort' => ''
+ ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_SKEY',
+ 'propertyValues' => [ 'Bar' ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/TranslationPropertyAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/TranslationPropertyAnnotatorTest.php
new file mode 100644
index 00000000..154f6653
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyAnnotators/TranslationPropertyAnnotatorTest.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace SMW\Tests\PropertyAnnotators;
+
+use SMW\DataItemFactory;
+use SMW\SemanticData;
+use SMW\PropertyAnnotators\NullPropertyAnnotator;
+use SMW\PropertyAnnotators\TranslationPropertyAnnotator;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\PropertyAnnotators\TranslationPropertyAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TranslationPropertyAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = TestEnvironment::newValidatorFactory()->newSemanticDataValidator();
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new TranslationPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ []
+ );
+
+ $this->assertInstanceOf(
+ TranslationPropertyAnnotator::class,
+ $instance
+ );
+ }
+
+ public function testAddAnnotation() {
+
+ $semanticData = new SemanticData(
+ $this->dataItemFactory->newDIWikiPage( 'Foo' )
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foobar' ) );
+
+ $title->expects( $this->once() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $translation = [
+ 'languagecode' => 'foo',
+ 'sourcepagetitle' => $title,
+ 'messagegroupid' => 'bar'
+ ];
+
+ $expected = [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_LCODE', '_TRANS_GROUP', '_TRANS_SOURCE' ],
+ 'propertyValues' => [ 'foo', 'bar', ':Foobar' ],
+ ];
+
+ $instance = new TranslationPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $translation
+ );
+
+ $instance->setPredefinedPropertyList( [ '_TRANS' ] );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => '_TRANS'
+ ],
+ $instance->getSemanticData()
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()->findSubSemanticData( 'trans.foo' )
+ );
+ }
+
+ public function testAddAnnotation_NotListed() {
+
+ $semanticData = new SemanticData(
+ $this->dataItemFactory->newDIWikiPage( 'Foo' )
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->never() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foobar' ) );
+
+ $title->expects( $this->never() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $translation = [
+ 'languagecode' => 'foo',
+ 'sourcepagetitle' => $title,
+ 'messagegroupid' => 'bar'
+ ];
+
+ $instance = new TranslationPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $translation
+ );
+
+ $instance->setPredefinedPropertyList( [] );
+ $instance->addAnnotation();
+ }
+
+ public function testAddAnnotation_EmptyData() {
+
+ $semanticData = new SemanticData(
+ $this->dataItemFactory->newDIWikiPage( 'Foo' )
+ );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $translation = [];
+
+ $instance = new TranslationPropertyAnnotator(
+ new NullPropertyAnnotator( $semanticData ),
+ $translation
+ );
+
+ $instance->addAnnotation();
+
+ $this->assertEquals(
+ $semanticData,
+ $instance->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyChangePropagationNotifierTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyChangePropagationNotifierTest.php
new file mode 100644
index 00000000..5955bd87
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyChangePropagationNotifierTest.php
@@ -0,0 +1,209 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\PropertyChangePropagationNotifier;
+
+/**
+ * @covers \SMW\PropertyChangePropagationNotifier
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PropertyChangePropagationNotifierTest extends \PHPUnit_Framework_TestCase {
+
+ protected $mockedStoreValues;
+ private $semanticData;
+ private $serializerFactory;
+ private $store;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'smwgChangePropagationWatchlist' => [ '_PVAL' ],
+ 'smwgMainCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false
+ ]
+ );
+
+ $semanticDataSerializer = $this->getMockBuilder( '\SMW\Serializers\SemanticDataSerializer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->serializerFactory = $this->getMockBuilder( '\SMW\SerializerFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->serializerFactory->expects( $this->any() )
+ ->method( 'newSemanticDataSerializer' )
+ ->will( $this->returnValue( $semanticDataSerializer ) );
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyChangePropagationNotifier::class,
+ new PropertyChangePropagationNotifier( $this->store, $this->serializerFactory )
+ );
+ }
+
+ /**
+ * @dataProvider dataItemDataProvider
+ */
+ public function testDetectChangesOnProperty( $mockedStoreValues, $dataValues, $propertiesToCompare, $expected ) {
+
+ if ( !method_exists( 'JobQueueGroup', 'lazyPush' ) ) {
+ $this->markTestSkipped( 'JobQueueGroup::lazyPush is not supported.' );
+ }
+
+ $subject = new DIWikiPage( __METHOD__, SMW_NS_PROPERTY );
+
+ $this->detectChanges(
+ $subject,
+ $mockedStoreValues,
+ $dataValues,
+ $propertiesToCompare,
+ $expected
+ );
+ }
+
+ /**
+ * @dataProvider dataItemDataProvider
+ */
+ public function testDetectChangesOnCategory( $mockedStoreValues, $dataValues, $propertiesToCompare, $expected ) {
+
+ if ( !method_exists( 'JobQueueGroup', 'lazyPush' ) ) {
+ $this->markTestSkipped( 'JobQueueGroup::lazyPush is not supported.' );
+ }
+
+ $subject = new DIWikiPage( __METHOD__, NS_CATEGORY );
+
+ $this->detectChanges(
+ $subject,
+ $mockedStoreValues,
+ $dataValues,
+ $propertiesToCompare,
+ $expected
+ );
+ }
+
+ public function detectChanges( $subject, $mockedStoreValues, $dataValues, $propertiesToCompare, $expected ) {
+
+ $this->mockedStoreValues = $mockedStoreValues;
+
+ $expectedToRun = $expected['job'] ? $this->atLeastOnce() : $this->never();
+
+ $jobQueueGroup = $this->getMockBuilder( '\JobQueueGroup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueueGroup->expects( $expectedToRun )
+ ->method( 'lazyPush' );
+
+ $this->testEnvironment->registerObject( 'JobQueueGroup', $jobQueueGroup );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( 'SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyValues', 'getSemanticData' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnCallback( [ $this, 'doComparePropertyValuesOnCallback' ] ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( $dataValues ) );
+
+ $instance = new PropertyChangePropagationNotifier(
+ $store,
+ $this->serializerFactory
+ );
+
+ $instance->setPropertyList( $propertiesToCompare );
+
+ $instance->checkAndNotify( $semanticData );
+
+ $this->assertEquals(
+ $expected['diff'],
+ $instance->hasDiff()
+ );
+ }
+
+ public function dataItemDataProvider() {
+
+ // Single
+ $subject = [
+ DIWikiPage::newFromText( __METHOD__ )
+ ];
+
+ // Multiple
+ $subjects = [
+ DIWikiPage::newFromText( __METHOD__ . 'm-0' ),
+ DIWikiPage::newFromText( __METHOD__ . 'm-1' ),
+ DIWikiPage::newFromText( __METHOD__ . 'm-2' )
+ ];
+
+ return [
+ // $mockedStoreValues, $dataValues, $settings, $expected
+ [ $subjects, [], [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ],
+ [ [], $subjects, [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ],
+ [ $subject, $subjects, [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ],
+ [ $subject, [], [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ],
+ [ $subject, [], [ '_PVAL' ], [ 'diff' => true, 'job' => true ] ],
+ [ $subjects, $subjects, [ '_PVAL' ], [ 'diff' => false, 'job' => false ] ],
+ [ $subject, $subject, [ '_PVAL' ], [ 'diff' => false, 'job' => false ] ],
+ [ $subjects, $subjects, [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ],
+ [ $subject, $subject, [ '_PVAL', '_LIST' ], [ 'diff' => true, 'job' => true ] ]
+ ];
+ }
+
+ /**
+ * Returns an array of SMWDataItem and simulates an alternating
+ * existencance of return values ('_LIST')
+ *
+ * @see Store::getPropertyValues
+ *
+ * @return SMWDataItem[]
+ */
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.CodeAnalysis.UnusedFunctionParameter
+ public function doComparePropertyValuesOnCallback( $subject, DIProperty $property, $requestoptions = null ) { // @codingStandardsIgnoreEnd
+ return $property->getKey() === '_LIST' ? [] : $this->mockedStoreValues;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyLabelFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyLabelFinderTest.php
new file mode 100644
index 00000000..9f03aa90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyLabelFinderTest.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\PropertyLabelFinder;
+
+/**
+ * @covers \SMW\PropertyLabelFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PropertyLabelFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ public function testCanConstruct() {
+
+ $languageIndependentPropertyLabels = [];
+ $canonicalPropertyLabels = [];
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyLabelFinder',
+ new PropertyLabelFinder( $this->store, $languageIndependentPropertyLabels, $canonicalPropertyLabels )
+ );
+ }
+
+ public function testPreLoadedPropertyLabel() {
+
+ $languageIndependentPropertyLabels = [ '_Foo' => 'Bar' ];
+ $canonicalPropertyLabels = [];
+
+ $instance = new PropertyLabelFinder(
+ $this->store,
+ $languageIndependentPropertyLabels,
+ $canonicalPropertyLabels
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->findPropertyLabelById( '_Foo' )
+ );
+
+ $this->assertEquals(
+ '_Foo',
+ $instance->searchPropertyIdByLabel( 'Bar' )
+ );
+ }
+
+ public function testRegisterPropertyLabel() {
+
+ $languageIndependentPropertyLabels = [];
+ $canonicalPropertyLabels = [];
+
+ $instance = new PropertyLabelFinder(
+ $this->store,
+ $languageIndependentPropertyLabels,
+ $canonicalPropertyLabels
+ );
+
+ $instance->registerPropertyLabel(
+ '_Foo',
+ 'Bar'
+ );
+
+ $this->assertEquals(
+ [ '_Foo' => 'Bar' ],
+ $instance->getKownPredefinedPropertyLabels()
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->findPropertyLabelById( '_Foo' )
+ );
+
+ $this->assertEquals(
+ '_Foo',
+ $instance->searchPropertyIdByLabel( 'Bar' )
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->findCanonicalPropertyLabelById( '_Foo' )
+ );
+ }
+
+ public function testPreventKnownPropertyLabelToBeRegisteredAsCanonicalWithDifferentId() {
+
+ $languageIndependentPropertyLabels = [];
+
+ $canonicalPropertyLabels = [
+ 'Foo' => '_foo'
+ ];
+
+ $instance = new PropertyLabelFinder(
+ $this->store,
+ $languageIndependentPropertyLabels,
+ $canonicalPropertyLabels
+ );
+
+ $instance->registerPropertyLabel(
+ '_bar',
+ 'Foo',
+ true
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->findCanonicalPropertyLabelById( '_foo' )
+ );
+ }
+
+ public function testSearchPropertyIdForNonRegisteredLabel() {
+
+ $languageIndependentPropertyLabels = [];
+ $canonicalPropertyLabels = [];
+
+ $instance = new PropertyLabelFinder(
+ $this->store,
+ $languageIndependentPropertyLabels,
+ $canonicalPropertyLabels
+ );
+
+ $this->assertFalse(
+ $instance->searchPropertyIdByLabel( 'Bar' )
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->findPropertyLabelById( '_Foo' )
+ );
+ }
+
+ public function testFindPropertyLabelByLanguageCode() {
+
+ $languageIndependentPropertyLabels = [];
+ $canonicalPropertyLabels = [];
+
+ $instance = new PropertyLabelFinder(
+ $this->store,
+ $languageIndependentPropertyLabels,
+ $canonicalPropertyLabels
+ );
+
+ $this->assertEquals(
+ 'Booléen',
+ $instance->findPropertyLabelFromIdByLanguageCode( '_boo', 'fr' )
+ );
+
+ $this->assertEquals(
+ 'Boolean',
+ $instance->findPropertyLabelFromIdByLanguageCode( '_boo', 'en' )
+ );
+ }
+
+ public function testFindPropertyListFromLabelByLanguageCode() {
+
+ $instance = new PropertyLabelFinder(
+ $this->store
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->findPropertyListFromLabelByLanguageCode( '~*unknownProp*', 'ja' )
+ );
+ }
+
+ public function testFindPreferredPropertyLabelByLanguageCode() {
+
+ $this->propertySpecificationLookup->expects( $this->once() )
+ ->method( 'getPreferredPropertyLabelBy' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( 'ABC' ) );
+
+ $instance = new PropertyLabelFinder(
+ $this->store
+ );
+
+ $this->assertEquals(
+ 'ABC',
+ $instance->findPreferredPropertyLabelByLanguageCode( 'Foo', 'fr' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRegistryTest.php
new file mode 100644
index 00000000..714bbb42
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRegistryTest.php
@@ -0,0 +1,572 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataTypeRegistry;
+use SMW\DIProperty;
+use SMW\PropertyAliasFinder;
+use SMW\PropertyLabelFinder;
+use SMW\PropertyRegistry;
+
+/**
+ * @covers \SMW\PropertyRegistry
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PropertyRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ private $cache;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ PropertyRegistry::clear();
+ DataTypeRegistry::clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyAliasFinder = $this->getMockBuilder( '\SMW\PropertyAliasFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\PropertyRegistry',
+ new PropertyRegistry( $datatypeRegistry, $propertyLabelFinder, $propertyAliasFinder )
+ );
+ }
+
+ public function testGetInstance() {
+
+ $instance = PropertyRegistry::getInstance();
+
+ $this->assertSame(
+ $instance,
+ PropertyRegistry::getInstance()
+ );
+
+ PropertyRegistry::clear();
+
+ $this->assertNotSame(
+ $instance,
+ PropertyRegistry::getInstance()
+ );
+ }
+
+ public function testLanguageIndependantPropertyLabelAliasInvocation() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [ '_uri' => 'URL' ] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [ 'URI' => '_uri' ] ) );
+
+ $propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache,
+ [ 'Has type' => '_TYPE' ]
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $this->assertEquals(
+ [
+ 'Has type' => '_TYPE',
+ 'URI' => '_uri' ],
+ $instance->getKnownPropertyAliases()
+ );
+ }
+
+ public function testRegisterProperty() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $instance->registerProperty(
+ DIProperty::TYPE_HAS_TYPE,
+ '__typ',
+ 'Has type',
+ true
+ );
+
+ $this->assertEquals(
+ [ '_TYPE' => [ '__typ', true, true ] ],
+ $instance->getPropertyList()
+ );
+
+ $this->assertTrue(
+ $instance->isVisible( '_TYPE' )
+ );
+
+ $this->assertTrue(
+ $instance->isAnnotable( '_TYPE' )
+ );
+
+ $this->assertTrue(
+ $instance->isRegistered( '_TYPE' )
+ );
+ }
+
+ public function testUnregisterProperty() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $propertyLabelFinder = $this->getMockBuilder( '\SMW\PropertyLabelFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $this->assertFalse(
+ $instance->isVisible( '_UnregisteredType' )
+ );
+
+ $this->assertFalse(
+ $instance->isAnnotable( '_UnregisteredType' )
+ );
+
+ $this->assertFalse(
+ $instance->isRegistered( '_UnregisteredType' )
+ );
+ }
+
+ public function testFindPropertyId() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $instance->registerProperty(
+ DIProperty::TYPE_HAS_TYPE,
+ '__typ',
+ 'Has type',
+ true
+ );
+
+ $instance->registerPropertyAlias( '_TYPE', 'foo' );
+
+ $this->assertEquals(
+ '_TYPE',
+ $instance->findPropertyIdByLabel( 'Has type' )
+ );
+
+ $this->assertEquals(
+ '_TYPE',
+ $instance->findPropertyIdByLabel( 'foo', true )
+ );
+
+ // findPropertyId legacy test
+ $this->assertEquals(
+ '_TYPE',
+ $instance->findPropertyId( 'Has type' )
+ );
+ }
+
+ public function testFindPropertyLabelForRegisteredId() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $instance->registerProperty(
+ DIProperty::TYPE_HAS_TYPE,
+ '__typ',
+ 'Has type',
+ true
+ );
+
+ $this->assertEquals(
+ 'Has type',
+ $instance->findPropertyLabelById( '_TYPE' )
+ );
+
+ // findPropertyLabel legacy test
+ $this->assertEquals(
+ 'Has type',
+ $instance->findPropertyLabel( '_TYPE' )
+ );
+
+ // This was part of an extra test but the extra test caused an segfault on postgres travis-ci
+
+ $this->assertEquals(
+ '__typ',
+ $instance->getPropertyTypeId( '_TYPE' )
+ );
+
+ // getPredefinedPropertyTypeId legacy test
+ $this->assertEquals(
+ '__typ',
+ $instance->getPredefinedPropertyTypeId( '_TYPE' )
+ );
+ }
+
+ public function testFindPropertyInfoForUnregisteredId() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->findPropertyLabelById( '_UnknownId' )
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->getPropertyTypeId( '_UnknownId' )
+ );
+
+ $this->assertFalse(
+ $instance->findPropertyIdByLabel( 'unknownLabel' )
+ );
+
+ $this->assertFalse(
+ $instance->findPropertyIdByLabel( 'unknownLabel', true )
+ );
+ }
+
+ public function testfindPropertyIdFromLabelByLanguageCode() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $this->assertEquals(
+ '_TYPE',
+ $instance->findPropertyIdFromLabelByLanguageCode( 'A le type', 'fr' )
+ );
+ }
+
+ public function testFindPropertyLabelByLanguageCode() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $this->assertEquals(
+ 'A le type',
+ $instance->findPropertyLabelFromIdByLanguageCode( '_TYPE', 'fr' )
+ );
+ }
+
+ public function testPropertyDescriptionMsgKey() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases
+ );
+
+ $instance->registerPropertyDescriptionMsgKeyById( '_foo', 'bar' );
+
+ $this->assertEquals(
+ 'bar',
+ $instance->findPropertyDescriptionMsgKeyById( '_foo' )
+ );
+
+ $this->assertEmpty(
+ $instance->findPropertyDescriptionMsgKeyById( 'unknown' )
+ );
+ }
+
+ public function testDataTypePropertyExemptionList() {
+
+ $datatypeRegistry = $this->getMockBuilder( '\SMW\DataTypeRegistry' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeLabels' )
+ ->will( $this->returnValue( [ '_foo' => 'Foo', '_foobar' => 'Foobar' ] ) );
+
+ $datatypeRegistry->expects( $this->once() )
+ ->method( 'getKnownTypeAliases' )
+ ->will( $this->returnValue( [ 'Bar' => '_bar' ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $propertyLabelFinder = new PropertyLabelFinder( $store, [] );
+
+ $propertyAliases = new PropertyAliasFinder(
+ $this->cache
+ );
+
+ $dataTypePropertyExemptionList = [ 'Foo', 'Bar' ];
+
+ $instance = new PropertyRegistry(
+ $datatypeRegistry,
+ $propertyLabelFinder,
+ $propertyAliases,
+ $dataTypePropertyExemptionList
+ );
+
+ $this->assertEquals(
+ '_foobar',
+ $instance->findPropertyIdByLabel( 'Foobar' )
+ );
+
+ $this->assertFalse(
+ $instance->findPropertyIdByLabel( 'Foo' )
+ );
+
+ $this->assertFalse(
+ $instance->findPropertyIdByLabel( 'Bar' )
+ );
+ }
+
+ /**
+ * @dataProvider typeToCanonicalLabelProvider
+ */
+ public function testFindCanonicalPropertyLabelById( $id, $expected ) {
+
+ $instance = PropertyRegistry::getInstance();
+
+ $this->assertSame(
+ $expected,
+ $instance->findCanonicalPropertyLabelById( $id )
+ );
+ }
+
+ public function typeToCanonicalLabelProvider() {
+
+ $provider[] = [
+ '_txt',
+ 'Text'
+ ];
+
+ $provider[] = [
+ '_TEXT',
+ 'Text'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRestrictionExaminerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRestrictionExaminerTest.php
new file mode 100644
index 00000000..35983b4c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertyRestrictionExaminerTest.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\PropertyRestrictionExaminer;
+
+/**
+ * @covers \SMW\PropertyRestrictionExaminer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyRestrictionExaminerTest extends \PHPUnit_Framework_TestCase {
+
+ private $user;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyRestrictionExaminer::class,
+ new PropertyRestrictionExaminer()
+ );
+ }
+
+ public function testGrepPropertyFromRestrictionErrorMsg() {
+
+ $msg = '[2,"smw-datavalue-property-create-restriction","Has unknown","foo"]';
+
+ $this->assertInstanceOf(
+ DIProperty::class,
+ PropertyRestrictionExaminer::grepPropertyFromRestrictionErrorMsg( $msg )
+ );
+ }
+
+ public function testRestrictionForPredefinedProperty() {
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->checkRestriction( new DIProperty( '_MDAT' ) );
+
+ $this->assertTrue(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testRestrictionForPredefinedPropertyOnQueryContext() {
+
+ $instance = new PropertyRestrictionExaminer();
+ $instance->isQueryContext( true );
+
+ $instance->checkRestriction( new DIProperty( '_MDAT' ) );
+
+ $this->assertFalse(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testRestrictionForUserPropertyOnFalseCreateProtectionRight() {
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->setCreateProtectionRight( false );
+ $instance->setUser( $this->user );
+
+ $instance->checkRestriction( new DIProperty( 'Foo' ) );
+
+ $this->assertFalse(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testRestrictionForUserPropertyOnCreateProtectionRight() {
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->setCreateProtectionRight( 'foo' );
+ $instance->setUser( $this->user );
+
+ $instance->checkRestriction( new DIProperty( 'Foo' ) );
+
+ $this->assertTrue(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testRestrictionForUserPropertyOnCreateProtectionRightWithAllowedUser() {
+
+ $right = 'bar';
+
+ $this->user->expects( $this->once() )
+ ->method( 'isAllowed' )
+ ->with( $this->equalTo( $right ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->setCreateProtectionRight( $right );
+ $instance->setUser( $this->user );
+
+ $instance->checkRestriction( new DIProperty( 'Foo' ) );
+
+ $this->assertFalse(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testDeclarativePropertyOnMainNamespace() {
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->checkRestriction(
+ new DIProperty( '_TYPE' ),
+ DIWikiPage::newFromText( 'Bar', NS_MAIN )
+ );
+
+ $this->assertTrue(
+ $instance->hasRestriction()
+ );
+ }
+
+ public function testDeclarativePropertyOnPropertyNamespace() {
+
+ $instance = new PropertyRestrictionExaminer();
+
+ $instance->checkRestriction(
+ new DIProperty( '_TYPE' ),
+ DIWikiPage::newFromText( 'Bar', SMW_NS_PROPERTY )
+ );
+
+ $this->assertFalse(
+ $instance->hasRestriction()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationLookupTest.php
new file mode 100644
index 00000000..071514c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationLookupTest.php
@@ -0,0 +1,486 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataItemFactory;
+use SMW\PropertySpecificationLookup;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\PropertySpecificationLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertySpecificationLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $blobStore;
+ private $dataItemFactory;
+ private $testEnvironment;
+ private $cachedPropertyValuesPrefetcher;
+ private $intermediaryMemoryCache;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->cachedPropertyValuesPrefetcher = $this->getMockBuilder( '\SMW\CachedPropertyValuesPrefetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->intermediaryMemoryCache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore = $this->getMockBuilder( '\Onoi\BlobStore\BlobStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataItemFactory = new DataItemFactory();
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\PropertySpecificationLookup',
+ new PropertySpecificationLookup( $this->cachedPropertyValuesPrefetcher, $this->intermediaryMemoryCache )
+ );
+ }
+
+ public function testGetSpecification() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( 'Bar' ) ),
+ $this->anything() );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $instance->getSpecification(
+ $property,
+ $this->dataItemFactory->newDIProperty( 'Bar' )
+ );
+ }
+
+ public function testGetFieldList() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'RecordProperty' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_LIST' ) ),
+ $this->anything() )
+ ->will(
+ $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( 'Foo' ),
+ $this->dataItemFactory->newDIBlob( 'abc;123' ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ 'abc;123',
+ $instance->getFieldListBy( $property )
+ );
+ }
+
+ public function testGetPreferredPropertyLabel() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'SomeProperty' );
+ $property->setPropertyTypeId( '_mlt_rec' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PPLB' ) ),
+ $this->anything() );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->getPreferredPropertyLabelBy( $property )
+ );
+ }
+
+ public function testGetPropertyFromDisplayTitle() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'queryPropertyValuesFor' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIWikiPage( 'Foo' ) ] ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ $property,
+ $instance->getPropertyFromDisplayTitle( 'abc' )
+ );
+ }
+
+ public function testHasUniquenessConstraint() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PVUC' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( true ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertTrue(
+ $instance->hasUniquenessConstraint( $property )
+ );
+ }
+
+ public function testGetExternalFormatterUri() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PEFU' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIUri( 'http', 'example.org/$1' ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertInstanceOf(
+ DataItem::class,
+ $instance->getExternalFormatterUri( $property )
+ );
+ }
+
+ public function testGetAllowedPattern() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Has allowed pattern' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PVAP' ) ),
+ $this->anything() )
+ ->will(
+ $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'IPv4' ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ 'IPv4',
+ $instance->getAllowedPatternBy( $property )
+ );
+ }
+
+ public function testGetAllowedListValueBy() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Has list' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PVALI' ) ),
+ $this->anything() )
+ ->will(
+ $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'Foo' ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ [ 'Foo' ],
+ $instance->getAllowedListValues( $property )
+ );
+ }
+
+ public function testGetAllowedValues() {
+
+ $expected = [
+ $this->dataItemFactory->newDIBlob( 'A' ),
+ $this->dataItemFactory->newDIBlob( 'B' )
+ ];
+
+ $property = $this->dataItemFactory->newDIProperty( 'Has allowed values' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PVAL' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( $expected ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getAllowedValues( $property )
+ );
+ }
+
+ public function testGetDisplayPrecision() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_PREC' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDINumber( -2.3 ) ] ) );
+
+ $this->intermediaryMemoryCache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ 2,
+ $instance->getDisplayPrecision( $property )
+ );
+ }
+
+ public function testgetDisplayUnits() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_UNIT' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIBlob( 'abc,def' ),
+ $this->dataItemFactory->newDIBlob( '123' ) ] ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ [ 'abc', 'def', '123' ],
+ $instance->getDisplayUnits( $property )
+ );
+ }
+
+ public function testGetPropertyDescriptionForPredefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getBlobStore' )
+ ->will( $this->returnValue( $this->blobStore ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getPropertyDescriptionByLanguageCode( $property )
+ );
+ }
+
+ public function testGetPropertyDescriptionForPredefinedPropertyViaCacheForLanguageCode() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'pdesc:en:0' ) )
+ ->will( $this->returnValue( 1001 ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getBlobStore' )
+ ->will( $this->returnValue( $this->blobStore ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ 1001,
+ $instance->getPropertyDescriptionByLanguageCode( $property, 'en' )
+ );
+ }
+
+ public function testTryToGetLocalPropertyDescriptionForUserdefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $pdesc = $this->dataItemFactory->newDIProperty( '_PDESC' );
+ $pdesc->setPropertyTypeId( '_mlt_rec' );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [
+ $this->dataItemFactory->newDIContainer( ContainerSemanticData::makeAnonymousContainer() ) ] ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getBlobStore' )
+ ->will( $this->returnValue( $this->blobStore ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getPropertyDescriptionByLanguageCode( $property )
+ );
+ }
+
+ public function testGetPropertyGroup() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $ppgr = $this->dataItemFactory->newDIProperty( '_PPGR' );
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar', NS_CATEGORY );
+ $bool = $this->dataItemFactory->newDIBoolean( true );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->at( 0 ) )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $property->getDiWikiPage() ),
+ $this->anything(),
+ $this->anything() )
+ ->will( $this->returnValue( [ $dataItem ] ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->at( 1 ) )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $ppgr ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $bool ] ) );
+
+ $instance = new PropertySpecificationLookup(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->intermediaryMemoryCache
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->getPropertyGroup( $property )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqExaminerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqExaminerTest.php
new file mode 100644
index 00000000..14f8b58c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqExaminerTest.php
@@ -0,0 +1,331 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataItemFactory;
+use SMW\PropertySpecificationReqExaminer;
+use SMW\SemanticData;
+
+/**
+ * @covers \SMW\PropertySpecificationReqExaminer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationReqExaminerTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $protectionValidator;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->protectionValidator = $this->getMockBuilder( '\SMW\Protection\ProtectionValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertySpecificationReqExaminer::class,
+ new PropertySpecificationReqExaminer( $this->store, $this->protectionValidator )
+ );
+ }
+
+ /**
+ * @dataProvider propertyProvider
+ */
+ public function testCheck( $property, $semanticData, $expected ) {
+
+ $instance = new PropertySpecificationReqExaminer(
+ $this->store,
+ $this->protectionValidator
+ );
+
+ $instance->setSemanticData( $semanticData );
+
+ $this->assertEquals(
+ $expected,
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckDisabledEditProtectionRight() {
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getEditProtectionRight' )
+ ->will( $this->returnValue( false ) );
+
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $this->store,
+ $this->protectionValidator
+ );
+
+ $this->assertEquals(
+ [
+ 'warning',
+ 'smw-edit-protection-disabled',
+ 'Is edit protected'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckEnabledCreateProtectionRight() {
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasCreateProtection' )
+ ->will( $this->returnValue( true ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getCreateProtectionRight' )
+ ->will( $this->returnValue( 'foo' ) );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Bar' );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $this->store,
+ $this->protectionValidator
+ );
+
+ $this->assertEquals(
+ [
+ 'warning',
+ 'smw-create-protection',
+ 'Bar',
+ 'foo'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckEnabledEditProtectionRight() {
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'hasEditProtection' )
+ ->will( $this->returnValue( true ) );
+
+ $this->protectionValidator->expects( $this->any() )
+ ->method( 'getEditProtectionRight' )
+ ->will( $this->returnValue( 'foo' ) );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Bar' );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $this->store,
+ $this->protectionValidator
+ );
+
+ $this->assertEquals(
+ [
+ 'error',
+ 'smw-edit-protection',
+ 'foo'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckImportedVocabTypeMismatch() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $semanticData = new SemanticData(
+ $property->getDIWikiPage()
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( '_TYPE' ),
+ $this->dataItemFactory->newDIProperty( 'Bar' )
+ );
+
+ $semanticData->setOption(
+ \SMW\PropertyAnnotators\MandatoryTypePropertyAnnotator::IMPO_REMOVED_TYPE,
+ $this->dataItemFactory->newDIProperty( '_TYPE' )
+ );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $this->store,
+ $this->protectionValidator
+ );
+
+ $instance->setSemanticData( $semanticData );
+
+ $this->assertEquals(
+ [
+ 'warning',
+ 'smw-property-req-violation-import-type',
+ 'Foo'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckChangePropagation() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $semanticData = new SemanticData(
+ $property->getDIWikiPage()
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( \SMW\DIProperty::TYPE_CHANGE_PROP ),
+ $this->dataItemFactory->newDIBlob( '...' )
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $store,
+ $this->protectionValidator
+ );
+
+ $this->assertEquals(
+ [
+ 'error',
+ 'smw-property-req-violation-change-propagation-locked-error',
+ 'Foo'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function testCheckChangePropagationAsWarning() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $semanticData = new SemanticData(
+ $property->getDIWikiPage()
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $this->dataItemFactory->newDIProperty( \SMW\DIProperty::TYPE_CHANGE_PROP ),
+ $this->dataItemFactory->newDIBlob( '...' )
+ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $instance = new PropertySpecificationReqExaminer(
+ $store,
+ $this->protectionValidator
+ );
+
+ $instance->setChangePropagationProtection( false );
+
+ $this->assertEquals(
+ [
+ 'warning',
+ 'smw-property-req-violation-change-propagation-locked-warning',
+ 'Foo'
+ ],
+ $instance->check( $property )
+ );
+ }
+
+ public function propertyProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ $semanticData,
+ ''
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( '_MDAT' ),
+ $semanticData,
+ ''
+ ];
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'hasProperty' )
+ ->will( $this->returnValue( false ) );
+
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_ref_rec' );
+
+ $provider[] = [
+ $property,
+ $semanticData,
+ [
+ 'error',
+ 'smw-property-req-violation-missing-fields',
+ 'Foo',
+ 'Reference'
+ ]
+ ];
+
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_rec' );
+
+ $provider[] = [
+ $property,
+ $semanticData,
+ [
+ 'error',
+ 'smw-property-req-violation-missing-fields',
+ 'Foo',
+ 'Record'
+ ]
+ ];
+
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_eid' );
+
+ $provider[] = [
+ $property,
+ $semanticData,
+ [
+ 'error',
+ 'smw-property-req-violation-missing-formatter-uri',
+ 'Foo'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqMsgBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqMsgBuilderTest.php
new file mode 100644
index 00000000..65a0b854
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/PropertySpecificationReqMsgBuilderTest.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataItemFactory;
+use SMW\ProcessingErrorMsgHandler;
+use SMW\PropertySpecificationReqMsgBuilder;
+use SMW\SemanticData;
+
+/**
+ * @covers \SMW\PropertySpecificationReqMsgBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertySpecificationReqMsgBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $propertyTableInfoFetcher;
+ private $propertySpecificationReqExaminer;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $entityManager = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyTableInfoFetcher', 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $this->propertyTableInfoFetcher ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityManager ) );
+
+ $this->propertySpecificationReqExaminer = $this->getMockBuilder( '\SMW\PropertySpecificationReqExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertySpecificationReqMsgBuilder::class,
+ new PropertySpecificationReqMsgBuilder( $this->store, $this->propertySpecificationReqExaminer )
+ );
+ }
+
+ /**
+ * @dataProvider propertyProvider
+ */
+ public function testCreateMessage( $property ) {
+
+ $instance = new PropertySpecificationReqMsgBuilder(
+ $this->store,
+ $this->propertySpecificationReqExaminer
+ );
+
+ $instance->check( $property );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getMessage()
+ );
+ }
+
+ public function testFindErrMessages() {
+
+ $dataItemFactory = new DataItemFactory();
+ $subject = $dataItemFactory->newDIWikiPage( 'Test', NS_MAIN );
+
+ $semanticData = new SemanticData(
+ $subject
+ );
+
+ $processingErrorMsgHandler = new ProcessingErrorMsgHandler(
+ $subject
+ );
+
+ $processingErrorMsgHandler->addToSemanticData(
+ $semanticData,
+ $processingErrorMsgHandler->newErrorContainerFromMsg( [ 'testFindErrMessages' ] )
+ );
+
+ $instance = new PropertySpecificationReqMsgBuilder(
+ $this->store,
+ $this->propertySpecificationReqExaminer
+ );
+
+ $instance->setSemanticData( $semanticData );
+
+ $instance->check(
+ $dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'smw-property-error-list',
+ $instance->getMessage()
+ );
+
+ $this->assertContains(
+ 'testFindErrMessages',
+ $instance->getMessage()
+ );
+ }
+
+ public function testErrorOnCompetingTypes() {
+
+ $dataItemFactory = new DataItemFactory();
+ $subject = $dataItemFactory->newDIWikiPage( 'Test', NS_MAIN );
+
+ $semanticData = new SemanticData(
+ $subject
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_TYPE' ),
+ $dataItemFactory->newDIBlob( '_num' )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ $dataItemFactory->newDIProperty( '_TYPE' ),
+ $dataItemFactory->newDIBlob( '_dat' )
+ );
+
+ $instance = new PropertySpecificationReqMsgBuilder(
+ $this->store,
+ $this->propertySpecificationReqExaminer
+ );
+
+ $instance->setSemanticData( $semanticData );
+
+ $instance->check(
+ $dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'smw-property-req-violation-type',
+ $instance->getMessage()
+ );
+ }
+
+ public function testCheckUniqueness() {
+
+ $entityManager = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityManager->expects( $this->any() )
+ ->method( 'isUnique' )
+ ->will( $this->returnValue( false ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyTableInfoFetcher', 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $this->propertyTableInfoFetcher ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityManager ) );
+
+ $instance = new PropertySpecificationReqMsgBuilder(
+ $store,
+ $this->propertySpecificationReqExaminer
+ );
+
+ $dataItemFactory = new DataItemFactory();
+
+ $instance->check(
+ $dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'smw-property-uniqueness',
+ $instance->getMessage()
+ );
+ }
+
+ public function testCheckReservedName() {
+
+ $instance = new PropertySpecificationReqMsgBuilder(
+ $this->store,
+ $this->propertySpecificationReqExaminer
+ );
+
+ $instance->setPropertyReservedNameList(
+ [
+ 'Bar'
+ ]
+ );
+
+ $dataItemFactory = new DataItemFactory();
+
+ $instance->check(
+ $dataItemFactory->newDIProperty( 'Bar' )
+ );
+
+ $this->assertContains(
+ 'smw-property-name-reserved',
+ $instance->getMessage()
+ );
+ }
+
+ public function propertyProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( 'Foo' )
+ ];
+
+ $provider[] = [
+ $dataItemFactory->newDIProperty( '_MDAT' )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/EditProtectionUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/EditProtectionUpdaterTest.php
new file mode 100644
index 00000000..0520568d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/EditProtectionUpdaterTest.php
@@ -0,0 +1,233 @@
+<?php
+
+namespace SMW\Tests\Protection;
+
+use SMW\DataItemFactory;
+use SMW\Protection\EditProtectionUpdater;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Protection\EditProtectionUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class EditProtectionUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $wikiPage;
+ private $user;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+
+ $this->spyLogger = $testEnvironment->getUtilityFactory()->newSpyLogger();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->wikiPage = $this->getMockBuilder( '\WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ EditProtectionUpdater::class,
+ new EditProtectionUpdater( $this->wikiPage, $this->user )
+ );
+ }
+
+ public function testDoUpdateFromWithNoRestrictionsNoEditProtection() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $subject->getTitle() ) );
+
+ $this->wikiPage->expects( $this->never() )
+ ->method( 'doUpdateRestrictions' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new EditProtectionUpdater(
+ $this->wikiPage,
+ $this->user
+ );
+
+ $instance->setEditProtectionRight( 'Foo' );
+ $instance->doUpdateFrom( $semanticData );
+
+ $this->assertFalse(
+ $instance->isRestrictedUpdate()
+ );
+ }
+
+ public function testDoUpdateFromWithNoRestrictionsAnActiveEditProtection() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $subject->getTitle() ) );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'doUpdateRestrictions' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( true ) ] ) );
+
+ $instance = new EditProtectionUpdater(
+ $this->wikiPage,
+ $this->user
+ );
+
+ $instance->setEditProtectionRight( 'Foo' );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->doUpdateFrom( $semanticData );
+
+ $this->assertFalse(
+ $instance->isRestrictedUpdate()
+ );
+
+ $this->assertContains(
+ 'add protection on edit, move',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testDoUpdateFromWithRestrictionsButNoTrueEditProtection() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->once() )
+ ->method( 'getRestrictions' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'doUpdateRestrictions' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( false ) ] ) );
+
+ $instance = new EditProtectionUpdater(
+ $this->wikiPage,
+ $this->user
+ );
+
+ $instance->setEditProtectionRight( 'Foo' );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->doUpdateFrom( $semanticData );
+
+ $this->assertFalse(
+ $instance->isRestrictedUpdate()
+ );
+
+ $this->assertContains(
+ 'remove protection on edit, move',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+ public function testDoUpdateFromWithRestrictionsAnActiveEditProtection() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'isProtected' )
+ ->with( $this->equalTo( 'edit' ) )
+ ->will( $this->returnValue( true ) );
+
+ $title->expects( $this->once() )
+ ->method( 'getRestrictions' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $this->wikiPage->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $this->wikiPage->expects( $this->never() )
+ ->method( 'doUpdateRestrictions' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with( $this->equalTo( $property ) )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( true ) ] ) );
+
+ $instance = new EditProtectionUpdater(
+ $this->wikiPage,
+ $this->user
+ );
+
+ $instance->setEditProtectionRight( 'Foo' );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->doUpdateFrom( $semanticData );
+
+ $this->assertFalse(
+ $instance->isRestrictedUpdate()
+ );
+
+ $this->assertContains(
+ 'Status already set, no update required',
+ $this->spyLogger->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/ProtectionValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/ProtectionValidatorTest.php
new file mode 100644
index 00000000..d39c68c2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Protection/ProtectionValidatorTest.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace SMW\Tests\Protection;
+
+use SMW\DataItemFactory;
+use SMW\Protection\ProtectionValidator;
+
+/**
+ * @covers \SMW\Protection\ProtectionValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ProtectionValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $cachedPropertyValuesPrefetcher;
+ private $cache;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->cachedPropertyValuesPrefetcher = $this->getMockBuilder( '\SMW\CachedPropertyValuesPrefetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ProtectionValidator::class,
+ new ProtectionValidator( $this->cachedPropertyValuesPrefetcher, $this->cache )
+ );
+ }
+
+ public function testSetGetEditProtectionRight() {
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $instance->setEditProtectionRight(
+ 'foo'
+ );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getEditProtectionRight()
+ );
+ }
+
+ public function testHasEditProtectionOnNamespace() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN, '', 'Bar' );
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( false ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $subject->asBase() ),
+ $this->equalTo( $property ) )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( true ) ] ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $instance->setEditProtectionRight(
+ 'foo'
+ );
+
+ $this->assertTrue(
+ $instance->hasEditProtectionOnNamespace( $subject->getTitle() )
+ );
+ }
+
+ public function testHasProtection() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN, '', 'Bar' );
+ $property = $this->dataItemFactory->newDIProperty( '_EDIP' );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( false ) );
+
+ $this->cachedPropertyValuesPrefetcher->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $subject->asBase() ),
+ $this->equalTo( $property ) )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBoolean( true ) ] ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $this->assertTrue(
+ $instance->hasProtection( $subject->getTitle() )
+ );
+ }
+
+ public function testHasProtectionFromCache() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN, '', 'Bar' );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( true ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $this->assertFalse(
+ $instance->hasProtection( $subject->getTitle() )
+ );
+ }
+
+ public function testHasChangePropagationProtectionOnCategory_FromCache() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_CATEGORY );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( true ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $this->assertFalse(
+ $instance->hasChangePropagationProtection( $subject->getTitle() )
+ );
+ }
+
+ public function testNoChangePropagationProtectionOnCategory_FromCache() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_CATEGORY );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->will( $this->returnValue( true ) );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $this->assertTrue(
+ $instance->hasChangePropagationProtection( $subject->getTitle() )
+ );
+ }
+
+ public function testNoChangePropagationProtectionOnCategory_WithFalseSetting() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_CATEGORY );
+
+ $this->cache->expects( $this->never() )
+ ->method( 'contains' );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $instance->setChangePropagationProtection(
+ false
+ );
+
+ $this->assertFalse(
+ $instance->hasChangePropagationProtection( $subject->getTitle() )
+ );
+ }
+
+ public function testSetGetCreateProtectionRight() {
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $instance->setCreateProtectionRight(
+ 'foo'
+ );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getCreateProtectionRight()
+ );
+ }
+
+ public function testHasCreateProtection() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'userCan' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ProtectionValidator(
+ $this->cachedPropertyValuesPrefetcher,
+ $this->cache
+ );
+
+ $instance->setCreateProtectionRight(
+ 'foo'
+ );
+
+ $this->assertTrue(
+ $instance->hasCreateProtection( $title )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DebugFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DebugFormatterTest.php
new file mode 100644
index 00000000..4e0fc750
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DebugFormatterTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\DebugFormatter;
+
+/**
+ * @covers \SMW\Query\DebugFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class DebugFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testFormatDebugOutputWithoutQuery() {
+
+ $instance = new DebugFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getStringFrom( 'foo', [], null )
+ );
+ }
+
+ public function testExplainFormat() {
+
+ DebugFormatter::setExplainFormat( DebugFormatter::JSON_FORMAT );
+
+ $this->assertEquals(
+ 'FORMAT=json',
+ DebugFormatter::getFormat( 'mysql' )
+ );
+ }
+
+ public function testFormatDebugOutputWithQuery() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new DebugFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getStringFrom( 'foo', [], $query )
+ );
+ }
+
+ /**
+ * @dataProvider sqlExplainFormatProvider
+ */
+ public function testFormatSQLExplainOutput( $type, $res ) {
+
+ $instance = new DebugFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->prettifyExplain( $type, $res )
+ );
+ }
+
+ public function testFormatSPARQLStatement() {
+
+ $instance = new DebugFormatter();
+
+ $sparql = '';
+
+ $this->assertInternalType(
+ 'string',
+ $instance->prettifySparql( $sparql )
+ );
+
+ $this->assertEquals(
+ '<div class="smwpre">&#91;&#x003A;&#x0020;&#x3C;Foo&#x3E;&#x0020;]</div>',
+ $instance->prettifySparql( '[: <Foo> ]' )
+ );
+ }
+
+ public function testFormatSQLStatement() {
+
+ $instance = new DebugFormatter();
+
+ $sql = '';
+ $alias = '';
+
+ $this->assertInternalType(
+ 'string',
+ $instance->prettifySql( $sql, $alias )
+ );
+ }
+
+ public function sqlExplainFormatProvider() {
+
+ $row = [
+ 'id' => '',
+ 'select_type' => '',
+ 'table' => '',
+ 'type' => '',
+ 'possible_keys' => '',
+ 'key' => '',
+ 'key_len' => '',
+ 'ref' => '',
+ 'rows' => '',
+ 'Extra' => ''
+ ];
+
+ $provider[] = [
+ 'mysql',
+ [ (object)$row ]
+ ];
+
+ $provider[] = [
+ 'postgres',
+ [ [ 'QUERY PLAN' => '' ] ]
+ ];
+
+ $provider[] = [
+ 'sqlite',
+ ''
+ ];
+
+ $row = [
+ 'EXPLAIN' => 'Foooooooo'
+ ];
+
+ $provider[] = [
+ 'mysql',
+ [ (object)$row ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DeferredTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DeferredTest.php
new file mode 100644
index 00000000..73ee4df8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DeferredTest.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\Deferred;
+
+/**
+ * @covers \SMW\Query\Deferred
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DeferredTest extends \PHPUnit_Framework_TestCase {
+
+ public function testRegisterResourceModules() {
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'addModuleStyles' );
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'addModules' );
+
+ Deferred::registerResources( $parserOutput );
+ }
+
+ public function testBuildHTML() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertContains(
+ 'smw-deferred-query',
+ Deferred::buildHTML( $query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DescriptionFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DescriptionFactoryTest.php
new file mode 100644
index 00000000..eb4041e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/DescriptionFactoryTest.php
@@ -0,0 +1,371 @@
+<?php
+
+namespace SMW\Tests\Query\Parser;
+
+use SMW\DataItemFactory;
+use SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter;
+use SMW\Query\DescriptionFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers SMW\Query\DescriptionFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DescriptionFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Query\DescriptionFactory',
+ new DescriptionFactory()
+ );
+ }
+
+ public function testCanConstructValueDescription() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $instance->newValueDescription( $dataItem )
+ );
+ }
+
+ public function testCanConstructSomeProperty() {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\SomeProperty',
+ $instance->newSomeProperty( $property, $description )
+ );
+ }
+
+ public function testCanConstructThingDescription() {
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ThingDescription',
+ $instance->newThingDescription()
+ );
+ }
+
+ public function testCanConstructDisjunction() {
+
+ $descriptions = [];
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\SomeProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $descriptions[] = $description;
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $descriptions[] = $description;
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ $instance->newDisjunction( $descriptions )
+ );
+ }
+
+ public function testCanConstructConjunction() {
+
+ $descriptions = [];
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\SomeProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $descriptions[] = $description;
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $descriptions[] = $description;
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->newConjunction( $descriptions )
+ );
+ }
+
+ public function testCanConstructNamespaceDescription() {
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\NamespaceDescription',
+ $instance->newNamespaceDescription( SMW_NS_PROPERTY )
+ );
+ }
+
+ public function testCanConstructClassDescription() {
+
+ $category = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ClassDescription',
+ $instance->newClassDescription( $category )
+ );
+ }
+
+ public function testCanConstructClassDescription_Categories() {
+
+ $category_1 = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $category_2 = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ClassDescription',
+ $instance->newClassDescription( [ $category_1, $category_2 ] )
+ );
+ }
+
+ public function testCanConstructConceptDescription() {
+
+ $concept = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ConceptDescription',
+ $instance->newConceptDescription( $concept )
+ );
+ }
+
+ public function testCanConstructDescriptionFromInvalidDataValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ThingDescription',
+ $instance->newFromDataValue( $dataValue )
+ );
+ }
+
+ public function testCanConstructDescriptionFromValidDataValue() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getProperty', 'getDataItem', 'getWikiValue' ] )
+ ->getMockForAbstractClass();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getWikiValue' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\SomeProperty',
+ $instance->newFromDataValue( $dataValue )
+ );
+ }
+
+ public function testCanConstructDescriptionFromMonolingualTextValue() {
+
+ $containerSemanticData = $this->getMockBuilder( '\SMWContainerSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $containerSemanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'Bar' ) ] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMW\DataValues\MonolingualTextValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getProperty', 'getDataItem' ] )
+ ->getMock();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIContainer( $containerSemanticData ) ) );
+
+ $monolingualTextValueFormatter = new MonolingualTextValueFormatter();
+ $monolingualTextValueFormatter->setDataValue( $dataValue );
+
+ $monolingualTextValueParser = $this->getMockBuilder( '\SMW\DataValues\ValueParsers\MonolingualTextValueParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory->expects( $this->atLeastOnce() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $monolingualTextValueFormatter ) );
+
+ $dataValueServiceFactory->expects( $this->atLeastOnce() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( $monolingualTextValueParser ) );
+
+ $dataValue->setDataValueServiceFactory(
+ $dataValueServiceFactory
+ );
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->newFromDataValue( $dataValue )
+ );
+ }
+
+ public function testCanConstructDescriptionFromMonolingualTextValueWithProperty() {
+
+ $containerSemanticData = $this->getMockBuilder( '\SMWContainerSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $containerSemanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIBlob( 'Bar' ) ] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMW\DataValues\MonolingualTextValue' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'isValid', 'getProperty', 'getDataItem' ] )
+ ->getMock();
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $dataValue->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIContainer( $containerSemanticData ) ) );
+
+ $monolingualTextValueFormatter = new MonolingualTextValueFormatter();
+ $monolingualTextValueFormatter->setDataValue( $dataValue );
+
+ $monolingualTextValueParser = $this->getMockBuilder( '\SMW\DataValues\ValueParsers\MonolingualTextValueParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory = $this->getMockBuilder( '\SMW\Services\DataValueServiceFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValueServiceFactory->expects( $this->atLeastOnce() )
+ ->method( 'getValueFormatter' )
+ ->will( $this->returnValue( $monolingualTextValueFormatter ) );
+
+ $dataValueServiceFactory->expects( $this->atLeastOnce() )
+ ->method( 'getValueParser' )
+ ->will( $this->returnValue( $monolingualTextValueParser ) );
+
+ $dataValue->setDataValueServiceFactory(
+ $dataValueServiceFactory
+ );
+
+ $instance = new DescriptionFactory();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\SomeProperty',
+ $instance->newFromDataValue( $dataValue )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Exception/ResultFormatNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Exception/ResultFormatNotFoundExceptionTest.php
new file mode 100644
index 00000000..e6db988c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Exception/ResultFormatNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Query\Exception;
+
+use SMW\Query\Exception\ResultFormatNotFoundException;
+
+/**
+ * @covers \SMW\Query\Exception\ResultFormatNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ResultFormatNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new ResultFormatNotFoundException();
+
+ $this->assertInstanceof(
+ '\SMW\Query\Exception\ResultFormatNotFoundException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ExcerptsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ExcerptsTest.php
new file mode 100644
index 00000000..9dae22ae
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ExcerptsTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DIWikiPage;
+use SMW\Query\Excerpts;
+
+/**
+ * @covers \SMW\Query\Excerpts
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ExcerptsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Excerpts::class,
+ new Excerpts()
+ );
+ }
+
+ public function testAddExcerpt() {
+
+ $instance = new Excerpts();
+
+ $instance->addExcerpt( 'Foo', 0.1 );
+
+ $this->assertEquals(
+ 0.1,
+ $instance->getExcerpt( 'Foo' )
+ );
+
+ $this->assertFalse(
+ $instance->getExcerpt( 'Bar' )
+ );
+ }
+
+ public function testAddExcerpt_DIWikiPage() {
+
+ $dataItem = DIWikiPage::newFromText( 'Bar' );
+ $instance = new Excerpts();
+
+ $instance->addExcerpt( $dataItem, 10 );
+
+ $this->assertEquals(
+ 10,
+ $instance->getExcerpt( $dataItem )
+ );
+ }
+
+ public function testGetExcerpts() {
+
+ $instance = new Excerpts();
+
+ $instance->addExcerpt( 'Foo', '...' );
+ $instance->addExcerpt( 'Bar', 1001 );
+
+ $this->assertEquals(
+ [
+ [ 'Foo', '...' ],
+ [ 'Bar', 1001 ]
+ ],
+ $instance->getExcerpts()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ClassDescriptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ClassDescriptionTest.php
new file mode 100644
index 00000000..6e71a51f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ClassDescriptionTest.php
@@ -0,0 +1,280 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ThingDescription;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Query\Language\ClassDescription
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ClassDescriptionTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $cat_name;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->cat_name = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );;
+ }
+
+ public function testCanConstruct() {
+
+ $class = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ClassDescription',
+ new ClassDescription( $class )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ClassDescription',
+ new \SMWClassDescription( $class )
+ );
+ }
+
+ public function testConstructThrowsException() {
+
+ $this->setExpectedException( 'Exception' );
+
+ new ClassDescription( new \stdClass );
+ }
+
+ public function testCommonMethods() {
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $class = new DIWikiPage( 'Foo', NS_CATEGORY );
+ $instance = new ClassDescription( $class );
+
+ $this->assertEquals( [ $class ], $instance->getCategories() );
+
+ $this->assertEquals( "[[{$ns}:Foo]]", $instance->getQueryString() );
+ $this->assertEquals( " <q>[[{$ns}:Foo]]</q> ", $instance->getQueryString( true ) );
+
+ $this->assertEquals( false, $instance->isSingleton() );
+ $this->assertEquals( [], $instance->getPrintRequests() );
+
+ $this->assertEquals( 1, $instance->getSize() );
+ $this->assertEquals( 0, $instance->getDepth() );
+ $this->assertEquals( 2, $instance->getQueryFeatures() );
+ }
+
+ public function testAddDescription() {
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $instance = new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) );
+ $instance->addDescription( new ClassDescription( new DIWikiPage( 'Bar', NS_CATEGORY ) ) );
+
+ $this->assertEquals(
+ "[[{$ns}:Foo||Bar]]",
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ " <q>[[{$ns}:Foo||Bar]]</q> ",
+ $instance->getQueryString( true )
+ );
+ }
+
+ public function testAddClass() {
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $instance = new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) );
+ $instance->addClass( new DIWikiPage( 'Bar', NS_CATEGORY ) );
+
+ $this->assertEquals(
+ "[[{$ns}:Foo||Bar]]",
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ " <q>[[{$ns}:Foo||Bar]]</q> ",
+ $instance->getQueryString( true )
+ );
+ }
+
+ public function testIsMergableDescription() {
+
+ $cat = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $instance = new ClassDescription(
+ $cat
+ );
+
+ $this->assertTrue(
+ $instance->isMergableDescription( new ClassDescription( $cat ) )
+ );
+
+ $instance->isNegation = true;
+
+ $this->assertFalse(
+ $instance->isMergableDescription( new ClassDescription( $cat ) )
+ );
+ }
+
+ public function testClass_Negation() {
+
+ $cat = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $instance = new ClassDescription(
+ $cat
+ );
+
+ $this->assertEquals(
+ "[[{$this->cat_name}:Foo]]",
+ $instance->getQueryString()
+ );
+
+ $instance->isNegation = true;
+
+ $this->assertEquals(
+ "[[{$this->cat_name}:!Foo]]",
+ $instance->getQueryString()
+ );
+
+ $instance->addClass( new DIWikiPage( 'Bar', NS_CATEGORY ) );
+
+ $this->assertEquals(
+ "[[{$this->cat_name}:!Foo||!Bar]]",
+ $instance->getQueryString()
+ );
+ }
+
+ public function testGetFingerprint() {
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $instance->addDescription(
+ new ClassDescription( new DIWikiPage( 'Bar', NS_CATEGORY ) )
+ );
+
+ $expected = $instance->getFingerprint();
+
+ // Different position, same hash
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Bar', NS_CATEGORY )
+ );
+
+ $instance->addDescription(
+ new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) )
+ );
+
+ $this->assertSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+
+ // Adds extra description, changes hash
+ $instance->addDescription(
+ new ClassDescription( new DIWikiPage( 'Foobar', NS_CATEGORY ) )
+ );
+
+ $this->assertNotSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+ }
+
+ public function testPrune() {
+
+ $instance = new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) );
+
+ $maxsize = 1;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+ public function testStableFingerprint() {
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $this->assertSame(
+ 'Cl:c2d00d69a4e9517d075d9adf6aafea6e',
+ $instance->getFingerprint()
+ );
+ }
+
+ public function testHierarchyDepthToBeCeiledOnMaxQSubcategoryDepthSetting() {
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $instance->setHierarchyDepth( 9999999 );
+
+ $this->assertSame(
+ $GLOBALS['smwgQSubcategoryDepth'],
+ $instance->getHierarchyDepth()
+ );
+ }
+
+ public function testGetQueryStringWithHierarchyDepth() {
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $instance->setHierarchyDepth( 1 );
+
+ $this->assertSame(
+ "[[$ns:Foo|+depth=1]]",
+ $instance->getQueryString()
+ );
+ }
+
+ public function testVaryingHierarchyDepthCausesDifferentFingerprint() {
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $instance->setHierarchyDepth( 9999 );
+ $expected = $instance->getFingerprint();
+
+ $instance = new ClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $this->assertNotSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConceptDescriptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConceptDescriptionTest.php
new file mode 100644
index 00000000..b3098817
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConceptDescriptionTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\ThingDescription;
+
+/**
+ * @covers \SMW\Query\Language\ConceptDescription
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConceptDescriptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $concept = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ConceptDescription',
+ new ConceptDescription( $concept )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ConceptDescription',
+ new \SMWConceptDescription( $concept )
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( SMW_NS_CONCEPT );
+
+ $concept = new DIWikiPage( 'Foo', SMW_NS_CONCEPT );
+ $instance = new ConceptDescription( $concept );
+
+ $this->assertEquals( $concept, $instance->getConcept() );
+
+ $this->assertEquals( "[[{$ns}:Foo]]", $instance->getQueryString() );
+ $this->assertEquals( " <q>[[{$ns}:Foo]]</q> ", $instance->getQueryString( true ) );
+
+ $this->assertEquals( false, $instance->isSingleton() );
+ $this->assertEquals( [], $instance->getPrintRequests() );
+
+ $this->assertEquals( 1, $instance->getSize() );
+ $this->assertEquals( 0, $instance->getDepth() );
+ $this->assertEquals( 4, $instance->getQueryFeatures() );
+ }
+
+ public function testGetFingerprint() {
+
+ $instance = new ConceptDescription(
+ new DIWikiPage( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $expected = $instance->getFingerprint();
+
+ $instance = new ConceptDescription(
+ new DIWikiPage( 'Bar', SMW_NS_CONCEPT )
+ );
+
+ $this->assertNotSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+ }
+
+ public function testPrune() {
+
+ $instance = new ConceptDescription( new DIWikiPage( 'Foo', SMW_NS_CONCEPT ) );
+
+ $maxsize = 1;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConjunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConjunctionTest.php
new file mode 100644
index 00000000..d72aebb0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ConjunctionTest.php
@@ -0,0 +1,254 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+
+/**
+ * @covers \SMW\Query\Language\Conjunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConjunctionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ new Conjunction()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ new \SMWConjunction()
+ );
+ }
+
+ /**
+ * @dataProvider conjunctionProvider
+ */
+ public function testCommonMethods( $descriptions, $expected ) {
+
+ $instance = new Conjunction( $descriptions );
+
+ $this->assertEquals(
+ $expected['descriptions'],
+ $instance->getDescriptions()
+ );
+
+ $this->assertEquals(
+ $expected['queryString'],
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ $expected['queryStringAsValue'],
+ $instance->getQueryString( true )
+ );
+
+ $this->assertEquals(
+ $expected['isSingleton'],
+ $instance->isSingleton()
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->getPrintRequests()
+ );
+
+ $this->assertEquals(
+ $expected['size'],
+ $instance->getSize()
+ );
+
+ $this->assertEquals(
+ $expected['depth'],
+ $instance->getDepth()
+ );
+
+ $this->assertEquals(
+ $expected['queryFeatures'],
+ $instance->getQueryFeatures()
+ );
+ }
+
+ /**
+ * @dataProvider comparativeHashProvider
+ */
+ public function testGetFingerprint( $descriptions, $compareTo, $expected ) {
+
+ $instance = new Conjunction(
+ $descriptions
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getFingerprint() === $compareTo->getFingerprint()
+ );
+ }
+
+ public function conjunctionProvider() {
+
+ $nsHelp = Localizer::getInstance()->getNamespaceTextById( NS_HELP );
+
+ $descriptions = [
+ 'N:cfcd208495d565ef66e7dff9f98764da' => new NamespaceDescription( NS_MAIN ),
+ 'N:c20ad4d76fe97759aa27a0c99bff6710' => new NamespaceDescription( NS_HELP )
+ ];
+
+ $provider[] = [
+ $descriptions,
+ [
+ 'descriptions' => $descriptions,
+ 'queryString' => "[[:+]] [[{$nsHelp}:+]]",
+ 'queryStringAsValue' => " <q>[[:+]] [[{$nsHelp}:+]]</q> ",
+ 'isSingleton' => false,
+ 'queryFeatures' => 24,
+ 'size' => 2,
+ 'depth' => 0
+ ]
+ ];
+
+ $valueDescriptionFoo = new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) );
+ $valueDescriptionBar = new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) );
+ $valueDescriptionYim = new ValueDescription( new DIWikiPage( 'Yim', NS_MAIN ) );
+
+ $descriptions = [
+ $valueDescriptionFoo,
+ new Conjunction( [
+ $valueDescriptionBar,
+ $valueDescriptionYim
+ ] )
+ ];
+
+ $description = [
+ 'V:903e513c13559ffaa66a23270a2922ff' => $valueDescriptionFoo,
+ 'V:246b70c7cb6a9fe4613cad14405b682f' => $valueDescriptionBar,
+ 'V:a3f71a427c6f9533ea1f093ff47bf958' => $valueDescriptionYim
+ ];
+
+ $provider[] = [
+ $descriptions,
+ [
+ 'descriptions' => $description,
+ 'queryString' => '[[:Foo]] [[:Bar]] [[:Yim]]',
+ 'queryStringAsValue' => ' <q>[[:Foo]] [[:Bar]] [[:Yim]]</q> ',
+ 'isSingleton' => true,
+ 'queryFeatures' => 16,
+ 'size' => 3,
+ 'depth' => 0
+ ]
+ ];
+
+ return $provider;
+ }
+
+
+ public function testPrune() {
+
+ $valueDescriptionFoo = new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) );
+ $valueDescriptionBar = new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) );
+
+ $descriptions = [
+ $valueDescriptionFoo,
+ $valueDescriptionBar,
+ ];
+
+ $instance = new Conjunction( $descriptions );
+
+ $maxsize = 1;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $valueDescriptionFoo,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+ public function comparativeHashProvider() {
+
+ $descriptions = [
+ new NamespaceDescription( NS_MAIN ),
+ new NamespaceDescription( NS_HELP )
+ ];
+
+ $conjunction = new Conjunction(
+ $descriptions
+ );
+
+ $provider[] = [
+ $descriptions,
+ $conjunction,
+ true
+ ];
+
+ // Different order, same hash
+ $descriptions = [
+ new NamespaceDescription( NS_HELP ),
+ new NamespaceDescription( NS_MAIN ) // Changed position
+ ];
+
+ $conjunction = new Conjunction(
+ $descriptions
+ );
+
+ $provider[] = [
+ $descriptions,
+ $conjunction,
+ true
+ ];
+
+ // ThingDescription is neglected
+ $conjunction = new Conjunction(
+ $descriptions
+ );
+
+ $conjunction->addDescription(
+ new ThingDescription()
+ );
+
+ $provider[] = [
+ $descriptions,
+ $conjunction,
+ true
+ ];
+
+ // Adds description === different signature === different hash
+ $conjunction = new Conjunction(
+ $descriptions
+ );
+
+ $conjunction->addDescription(
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) )
+ );
+
+ $provider[] = [
+ $descriptions,
+ $conjunction,
+ false
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/DisjunctionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/DisjunctionTest.php
new file mode 100644
index 00000000..27b3ad3e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/DisjunctionTest.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+
+/**
+ * @covers \SMW\Query\Language\Disjunction
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DisjunctionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ new Disjunction()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ new \SMWDisjunction()
+ );
+ }
+
+ /**
+ * @dataProvider disjunctionProvider
+ */
+ public function testCommonMethods( $descriptions, $expected ) {
+
+ $instance = new Disjunction( $descriptions );
+
+ $this->assertEquals(
+ $expected['descriptions'],
+ $instance->getDescriptions()
+ );
+
+ $this->assertEquals(
+ $expected['queryString'],
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ $expected['queryStringAsValue'],
+ $instance->getQueryString( true )
+ );
+
+ $this->assertEquals(
+ $expected['isSingleton'],
+ $instance->isSingleton()
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->getPrintRequests()
+ );
+
+ $this->assertEquals(
+ $expected['size'],
+ $instance->getSize()
+ );
+
+ $this->assertEquals(
+ $expected['depth'],
+ $instance->getDepth()
+ );
+
+ $this->assertEquals(
+ $expected['queryFeatures'],
+ $instance->getQueryFeatures()
+ );
+ }
+
+ /**
+ * @dataProvider comparativeHashProvider
+ */
+ public function testGetFingerprint( $descriptions, $compareTo, $expected ) {
+
+ $instance = new Disjunction(
+ $descriptions
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getFingerprint() === $compareTo->getFingerprint()
+ );
+ }
+
+ public function disjunctionProvider() {
+
+ $nsHelp = Localizer::getInstance()->getNamespaceTextById( NS_HELP );
+
+ $descriptions = [
+ 'N:cfcd208495d565ef66e7dff9f98764da' => new NamespaceDescription( NS_MAIN ),
+ 'N:c20ad4d76fe97759aa27a0c99bff6710' => new NamespaceDescription( NS_HELP )
+ ];
+
+ $provider[] = [
+ $descriptions,
+ [
+ 'descriptions' => $descriptions,
+ 'queryString' => " <q>[[:+]] OR [[{$nsHelp}:+]]</q> ",
+ 'queryStringAsValue' => " <q>[[:+]]</q> || <q>[[{$nsHelp}:+]]</q> ",
+ 'isSingleton' => false,
+ 'queryFeatures' => 40,
+ 'size' => 2,
+ 'depth' => 0
+ ]
+ ];
+
+ $descriptions = [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ new Disjunction( [
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) ),
+ new ValueDescription( new DIWikiPage( 'Yim', NS_MAIN ) )
+ ] )
+ ];
+
+ $expectedDescriptions = [
+ 'V:903e513c13559ffaa66a23270a2922ff' => new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ 'V:246b70c7cb6a9fe4613cad14405b682f' => new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) ),
+ 'V:a3f71a427c6f9533ea1f093ff47bf958' => new ValueDescription( new DIWikiPage( 'Yim', NS_MAIN ) )
+ ];
+
+ $provider[] = [
+ $descriptions,
+ [
+ 'descriptions' => $expectedDescriptions,
+ 'queryString' => ' <q>[[:Foo]] OR [[:Bar]] OR [[:Yim]]</q> ',
+ 'queryStringAsValue' => 'Foo||Bar||Yim',
+ 'isSingleton' => false,
+ 'queryFeatures' => 32,
+ 'size' => 3,
+ 'depth' => 0
+ ]
+ ];
+
+ $descriptions = [
+ 'V:903e513c13559ffaa66a23270a2922ff' => new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ 'C:d0da0541e2e099655342be3af203814e' => new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) ),
+ new ValueDescription( new DIWikiPage( 'Yim', NS_MAIN ) )
+ ] )
+ ];
+
+ $provider[] = [
+ $descriptions,
+ [
+ 'descriptions' => $descriptions,
+ 'queryString' => ' <q>[[:Foo]] OR [[:Bar]] [[:Yim]]</q> ',
+ 'queryStringAsValue' => 'Foo|| <q>[[:Bar]] [[:Yim]]</q> ',
+ 'isSingleton' => false,
+ 'queryFeatures' => 48,
+ 'size' => 3,
+ 'depth' => 0
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function testPrune() {
+
+ $descriptions = [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) ),
+ ];
+
+ $instance = new Disjunction( $descriptions );
+
+ $maxsize = 2;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+ public function comparativeHashProvider() {
+
+ $descriptions = [
+ new NamespaceDescription( NS_MAIN ),
+ new NamespaceDescription( NS_HELP )
+ ];
+
+ $disjunction = new Disjunction(
+ $descriptions
+ );
+
+ $provider[] = [
+ $descriptions,
+ $disjunction,
+ true
+ ];
+
+ // Different order, same hash
+ $descriptions = [
+ new NamespaceDescription( NS_HELP ),
+ new NamespaceDescription( NS_MAIN ) // Changed position
+ ];
+
+ $disjunction = new Disjunction(
+ $descriptions
+ );
+
+ $provider[] = [
+ $descriptions,
+ $disjunction,
+ true
+ ];
+
+ // ThingDescription forces a different hash
+ $disjunction = new Disjunction(
+ $descriptions
+ );
+
+ $disjunction->addDescription(
+ new ThingDescription()
+ );
+
+ $provider[] = [
+ $descriptions,
+ $disjunction,
+ false
+ ];
+
+ // Adds description === different signature === different hash
+ $disjunction = new Disjunction(
+ $descriptions
+ );
+
+ $disjunction->addDescription(
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) )
+ );
+
+ $provider[] = [
+ $descriptions,
+ $disjunction,
+ false
+ ];
+
+ return $provider;
+ }
+
+ public function testVaryingHierarchyDepthCausesClassDescriptionToYieldDifferentFingerprint() {
+
+ $descriptions = [
+ new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) )
+ ];
+
+ $instance = new Disjunction(
+ $descriptions
+ );
+
+ $expected = $instance->getFingerprint();
+
+ $descriptions = [
+ new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) )
+ ];
+
+ $instance = new Disjunction(
+ $descriptions
+ );
+
+ $instance->setHierarchyDepth( 1 );
+
+ $this->assertNotSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/NamespaceDescriptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/NamespaceDescriptionTest.php
new file mode 100644
index 00000000..4d35298b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/NamespaceDescriptionTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\Localizer;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\ThingDescription;
+
+/**
+ * @covers \SMW\Query\Language\NamespaceDescription
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class NamespaceDescriptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $namespace = NS_MAIN;
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\NamespaceDescription',
+ new NamespaceDescription( $namespace )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\NamespaceDescription',
+ new \SMWNamespaceDescription( $namespace )
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $namespace = NS_MAIN;
+
+ $instance = new NamespaceDescription( $namespace );
+
+ $this->assertEquals( $namespace, $instance->getNamespace() );
+
+ $this->assertEquals( "[[:+]]", $instance->getQueryString() );
+ $this->assertEquals( " <q>[[:+]]</q> ", $instance->getQueryString( true ) );
+
+ $this->assertEquals( false, $instance->isSingleton() );
+ $this->assertEquals( [], $instance->getPrintRequests() );
+
+ $this->assertEquals( 1, $instance->getSize() );
+ $this->assertEquals( 0, $instance->getDepth() );
+ $this->assertEquals( 8, $instance->getQueryFeatures() );
+ }
+
+ public function testGetQueryStringForCategoryNamespace() {
+
+ $namespace = NS_CATEGORY;
+
+ $ns = Localizer::getInstance()->getNamespaceTextById( $namespace );
+ $instance = new NamespaceDescription( $namespace );
+
+ $this->assertEquals(
+ "[[:{$ns}:+]]",
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ " <q>[[:{$ns}:+]]</q> ",
+ $instance->getQueryString( true )
+ );
+ }
+
+ public function testPrune() {
+
+ $instance = new NamespaceDescription( NS_MAIN );
+
+ $maxsize = 1;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/SomePropertyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/SomePropertyTest.php
new file mode 100644
index 00000000..0c3974f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/SomePropertyTest.php
@@ -0,0 +1,506 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+
+/**
+ * @covers \SMW\Query\Language\SomeProperty
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SomePropertyTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\SomeProperty',
+ new SomeProperty( $property, $description )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\SomeProperty',
+ new \SMWSomeProperty( $property, $description )
+ );
+ }
+
+ /**
+ * @dataProvider somePropertyProvider
+ */
+ public function testCommonMethods( $property, $description, $expected ) {
+
+ $instance = new SomeProperty( $property, $description );
+
+ $this->assertEquals(
+ $expected['property'],
+ $instance->getProperty()
+ );
+
+ $this->assertEquals(
+ $expected['description'],
+ $instance->getDescription()
+ );
+
+ $this->assertEquals(
+ $expected['queryString'],
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ $expected['queryStringAsValue'],
+ $instance->getQueryString( true )
+ );
+
+ $this->assertEquals(
+ $expected['isSingleton'],
+ $instance->isSingleton()
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->getPrintRequests()
+ );
+
+ $this->assertEquals(
+ $expected['size'],
+ $instance->getSize()
+ );
+
+ $this->assertEquals(
+ $expected['depth'],
+ $instance->getDepth()
+ );
+
+ $this->assertEquals(
+ $expected['queryFeatures'],
+ $instance->getQueryFeatures()
+ );
+ }
+
+ /**
+ * @dataProvider comparativeHashProvider
+ */
+ public function testGetFingerprint( $description, $compareTo, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ $description->getFingerprint() === $compareTo->getFingerprint()
+ );
+ }
+
+ public function somePropertyProvider() {
+
+ #0
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $provider[] = [
+ $property,
+ $description,
+ [
+ 'property' => $property,
+ 'description' => $description,
+ 'queryString' => "[[Foo::Bar]]",
+ 'queryStringAsValue' => "<q>[[Foo::Bar]]</q>",
+ 'isSingleton' => false,
+ 'queryFeatures' => 1,
+ 'size' => 2,
+ 'depth' => 1
+ ]
+ ];
+
+ #1
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Yui' ),
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ), null )
+ );
+
+ $provider[] = [
+ $property,
+ $description,
+ [
+ 'property' => $property,
+ 'description' => $description,
+ 'queryString' => "[[Foo.Yui::Bar]]",
+ 'queryStringAsValue' => "<q>[[Foo.Yui::Bar]]</q>",
+ 'isSingleton' => false,
+ 'queryFeatures' => 1,
+ 'size' => 3,
+ 'depth' => 2
+ ]
+ ];
+
+ #2
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Yui' ),
+ new NamespaceDescription( NS_MAIN )
+ );
+
+ $provider[] = [
+ $property,
+ $description,
+ [
+ 'property' => $property,
+ 'description' => $description,
+ 'queryString' => "[[Foo.Yui:: <q>[[:+]]</q> ]]",
+ 'queryStringAsValue' => "<q>[[Foo.Yui:: <q>[[:+]]</q> ]]</q>",
+ 'isSingleton' => false,
+ 'queryFeatures' => 9,
+ 'size' => 3,
+ 'depth' => 2
+ ]
+ ];
+
+ #3, 1096
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Yui' ),
+ new SomeProperty(
+ new DIProperty( 'Bar', true ),
+ new NamespaceDescription( NS_MAIN )
+ )
+ );
+
+ $provider[] = [
+ $property,
+ $description,
+ [
+ 'property' => $property,
+ 'description' => $description,
+ 'queryString' => "[[Foo.Yui.-Bar:: <q>[[:+]]</q> ]]",
+ 'queryStringAsValue' => "<q>[[Foo.Yui.-Bar:: <q>[[:+]]</q> ]]</q>",
+ 'isSingleton' => false,
+ 'queryFeatures' => 9,
+ 'size' => 4,
+ 'depth' => 3
+ ]
+ ];
+
+ #4, 1096
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Yui' ),
+ new SomeProperty(
+ new DIProperty( '_SOBJ', true ),
+ new NamespaceDescription( NS_MAIN )
+ )
+ );
+
+ $provider[] = [
+ $property,
+ $description,
+ [
+ 'property' => $property,
+ 'description' => $description,
+ 'queryString' => "[[Foo.Yui.-Has subobject:: <q>[[:+]]</q> ]]",
+ 'queryStringAsValue' => "<q>[[Foo.Yui.-Has subobject:: <q>[[:+]]</q> ]]</q>",
+ 'isSingleton' => false,
+ 'queryFeatures' => 9,
+ 'size' => 4,
+ 'depth' => 3
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function testPrune() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $instance = new SomeProperty( $property, $description );
+
+ $maxsize = 2;
+ $maxDepth = 2;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+
+ $this->assertEquals( 0, $maxsize );
+ $this->assertEquals( 1, $maxDepth );
+
+ $maxsize = 0;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ new ThingDescription(),
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+ public function testStableFingerprint() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $instance = new SomeProperty(
+ $property,
+ $description
+ );
+
+ $this->assertSame(
+ 'S:8c2cab8d14dcd45d49aadb7fb5ab44a7',
+ $instance->getFingerprint()
+ );
+ }
+
+ public function testHierarchyDepthToBeCeiledOnMaxQSubpropertyDepthSetting() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $instance = new SomeProperty(
+ $property,
+ $description
+ );
+
+ $instance->setHierarchyDepth( 9999999 );
+
+ $this->assertSame(
+ $GLOBALS['smwgQSubpropertyDepth'],
+ $instance->getHierarchyDepth()
+ );
+ }
+
+ public function testGetQueryStringWithHierarchyDepth() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $instance = new SomeProperty(
+ $property,
+ $description
+ );
+
+ $instance->setHierarchyDepth( 1 );
+
+ $this->assertSame(
+ "[[Foo::Bar|+depth=1]]",
+ $instance->getQueryString()
+ );
+ }
+
+ public function testVaryingHierarchyDepthCausesDifferentFingerprint() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $property
+ );
+
+ $instance = new SomeProperty(
+ $property,
+ $description
+ );
+
+ $instance->setHierarchyDepth( 9999 );
+ $expected = $instance->getFingerprint();
+
+ $instance = new SomeProperty(
+ $property,
+ $description
+ );
+
+ $this->assertNotSame(
+ $expected,
+ $instance->getFingerprint()
+ );
+ }
+
+ public function comparativeHashProvider() {
+
+ // Same property, different description === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new NamespaceDescription( NS_HELP )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new NamespaceDescription( NS_MAIN )
+ ),
+ false
+ ];
+
+ // Inverse property, same description === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo', true ),
+ new NamespaceDescription( NS_MAIN )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new NamespaceDescription( NS_MAIN )
+ ),
+ false
+ ];
+
+ // Same property, different description === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo', true ),
+ new NamespaceDescription( NS_MAIN )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ ),
+ false
+ ];
+
+ // Property.chain, different description === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo', true ),
+ new ThingDescription()
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ )
+ ),
+ false
+ ];
+
+ // Property.chain, same description === same hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ )
+ ),
+ true
+ ];
+
+ // Property.chain, different description (inverse prop) === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo', true ),
+ new ThingDescription()
+ )
+ ),
+ false
+ ];
+
+ // Property.chain, different description === different hash
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ThingDescription()
+ )
+ )
+ ),
+ false
+ ];
+
+ // Property.chain, different description === different hash
+ // "[[Foo.Foo::Foo]]" !== "[[Foo.Foo.Foo::Foo]]"
+ $provider[] = [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) )
+ )
+ )
+ ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) )
+ )
+ )
+ )
+ ),
+ false
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ThingDescriptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ThingDescriptionTest.php
new file mode 100644
index 00000000..d4051ccc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ThingDescriptionTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\Query\Language\ThingDescription;
+
+/**
+ * @covers \SMW\Query\Language\ThingDescription
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ThingDescriptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ThingDescription',
+ new ThingDescription()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ThingDescription',
+ new \SMWThingDescription()
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $instance = new ThingDescription();
+
+ $this->assertEquals( '', $instance->getQueryString() );
+ $this->assertEquals( false, $instance->isSingleton() );
+ $this->assertEquals( [], $instance->getPrintRequests() );
+
+ $this->assertEquals( 0, $instance->getSize() );
+ $this->assertEquals( 0, $instance->getDepth() );
+ $this->assertEquals( 0, $instance->getQueryFeatures() );
+ }
+
+ public function testPrune() {
+
+ $instance = new ThingDescription();
+
+ $maxsize = 1;
+ $maxDepth = 1;
+ $log = [];
+
+ $this->assertEquals(
+ $instance,
+ $instance->prune( $maxsize, $maxDepth, $log )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ValueDescriptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ValueDescriptionTest.php
new file mode 100644
index 00000000..b430eba7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Language/ValueDescriptionTest.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace SMW\Tests\Query\Language;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ValueDescription;
+use SMWDINumber as DINumber;
+
+/**
+ * @covers \SMW\Query\Language\ValueDescription
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ValueDescriptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $dataItem = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ new ValueDescription( $dataItem )
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ new \SMWValueDescription( $dataItem )
+ );
+ }
+
+ /**
+ * @dataProvider valueDescriptionProvider
+ */
+ public function testCommonMethods( $dataItem, $property, $comparator, $expected ) {
+
+ $instance = new ValueDescription( $dataItem, $property, $comparator );
+
+ $this->assertEquals(
+ $expected['comparator'],
+ $instance->getComparator()
+ );
+
+ $this->assertEquals(
+ $expected['dataItem'],
+ $instance->getDataItem()
+ );
+
+ $this->assertEquals(
+ $expected['property'],
+ $instance->getProperty()
+ );
+
+ $this->assertEquals(
+ $expected['queryString'],
+ $instance->getQueryString()
+ );
+
+ $this->assertEquals(
+ $expected['queryStringAsValue'],
+ $instance->getQueryString( true )
+ );
+
+ $this->assertEquals(
+ $expected['isSingleton'],
+ $instance->isSingleton()
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->getPrintRequests()
+ );
+
+ $this->assertEquals(
+ 1,
+ $instance->getSize()
+ );
+
+ $this->assertEquals(
+ 0,
+ $instance->getDepth()
+ );
+
+ $this->assertEquals(
+ 0,
+ $instance->getQueryFeatures()
+ );
+ }
+
+ /**
+ * @dataProvider comparativeHashProvider
+ */
+ public function testGetFingerprint( $description, $compareTo, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ $description->getFingerprint() === $compareTo->getFingerprint()
+ );
+ }
+
+ public function valueDescriptionProvider() {
+
+ $dataItem = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $provider[] = [
+ $dataItem,
+ null,
+ SMW_CMP_EQ,
+ [
+ 'comparator' => SMW_CMP_EQ,
+ 'dataItem' => $dataItem,
+ 'property' => null,
+ 'queryString' => '[[:Foo]]',
+ 'queryStringAsValue' => 'Foo',
+ 'isSingleton' => true
+ ]
+ ];
+
+ $provider['page.1'] = [
+ $dataItem,
+ null,
+ SMW_CMP_LEQ,
+ [
+ 'comparator' => SMW_CMP_LEQ,
+ 'dataItem' => $dataItem,
+ 'property' => null,
+ 'queryString' => '[[≤Foo]]',
+ 'queryStringAsValue' => '≤Foo',
+ 'isSingleton' => false
+ ]
+ ];
+
+ $property = DIProperty::newFromUserLabel( 'Foo' )->setPropertyTypeId( '_num' );
+ $dataItem = new DINumber( 9001 );
+
+ $provider['num.1'] = [
+ $dataItem,
+ $property,
+ SMW_CMP_LEQ,
+ [
+ 'comparator' => SMW_CMP_LEQ,
+ 'dataItem' => $dataItem,
+ 'property' => $property,
+ 'queryString' => '[[≤9001]]',
+ 'queryStringAsValue' => '≤9001',
+ 'isSingleton' => false
+ ]
+ ];
+
+ $property = DIProperty::newFromUserLabel( 'Foo' )->setPropertyTypeId( '_num' );
+ $dataItem = new DINumber( 9001.356 );
+
+ $provider['num.2'] = [
+ $dataItem,
+ $property,
+ SMW_CMP_GEQ,
+ [
+ 'comparator' => SMW_CMP_GEQ,
+ 'dataItem' => $dataItem,
+ 'property' => $property,
+ 'queryString' => '[[≥9001.356]]',
+ 'queryStringAsValue' => '≥9001.356',
+ 'isSingleton' => false
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function comparativeHashProvider() {
+
+ $provider[] = [
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ
+ ),
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ
+ ),
+ true
+ ];
+
+ $provider[] = [
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ
+ ),
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_LEQ
+ ),
+ false
+ ];
+
+ $provider[] = [
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ
+ ),
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), new DIProperty( 'Bar' ), SMW_CMP_EQ
+ ),
+ false
+ ];
+
+ // Inverse case
+ $provider[] = [
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), new DIProperty( 'Bar', true ), SMW_CMP_EQ
+ ),
+ new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ), new DIProperty( 'Bar' ), SMW_CMP_EQ
+ ),
+ false
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/DescriptionProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/DescriptionProcessorTest.php
new file mode 100644
index 00000000..0a1fcde5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/DescriptionProcessorTest.php
@@ -0,0 +1,258 @@
+<?php
+
+namespace SMW\Tests\Query\Parser;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Parser\DescriptionProcessor;
+
+/**
+ * @covers SMW\Query\Parser\DescriptionProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DescriptionProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DescriptionProcessor::class,
+ new DescriptionProcessor()
+ );
+ }
+
+ public function testError() {
+
+ $instance = new DescriptionProcessor();
+ $instance->addError( 'abc' );
+
+ $this->assertEquals(
+ [ '[2,"abc"]' ],
+ $instance->getErrors()
+ );
+
+ $instance->addErrorWithMsgKey( 'Foo' );
+
+ $this->assertCount(
+ 2,
+ $instance->getErrors()
+ );
+ }
+
+ public function testDescriptionForPropertyObjectValue() {
+
+ $instance = new DescriptionProcessor();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Description',
+ $instance->newDescriptionForPropertyObjectValue( new DIProperty( 'Foo' ), 'bar' )
+ );
+ }
+
+ public function testDescriptionForWikiPageValueChunkOnEqualMatch() {
+
+ $instance = new DescriptionProcessor();
+
+ $valueDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $valueDescription
+ );
+
+ $this->assertEquals(
+ new DIWikiPage( 'Bar', NS_MAIN ),
+ $valueDescription->getDataItem()
+ );
+ }
+
+ public function testnewDescriptionForWikiPageValueChunkOnApproximateValue() {
+
+ $instance = new DescriptionProcessor();
+
+ $valueDescription = $instance->newDescriptionForWikiPageValueChunk( '~bar' );
+
+ $this->assertEquals(
+ new DIWikiPage( 'bar', NS_MAIN ),
+ $valueDescription->getDataItem()
+ );
+ }
+
+ public function testGetDisjunctiveCompoundDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ $instance->asOr( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testGetDisjunctiveCompoundDescriptionForCurrentConjunction() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = new Conjunction();
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ $instance->asOr( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testGetDisjunctiveCompoundDescriptionForCurrentDisjunction() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = new Disjunction();
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Disjunction',
+ $instance->asOr( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testTryToGetDisjunctiveCompoundDescriptionForNullNewDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $instance->asOr( $currentDescription, null )
+ );
+ }
+
+ public function testTryToGetDisjunctiveCompoundDescriptionForNullCurrentDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $newDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $instance->asOr( null, $newDescription )
+ );
+ }
+
+ public function testGetConjunctiveCompoundDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->asAnd( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testGetConjunctiveCompoundDescriptionForCurrentConjunction() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = new Conjunction();
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->asAnd( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testGetConjunctiveCompoundDescriptionForCurrentDisjunction() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = new Disjunction();
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->asAnd( $currentDescription, $newDescription )
+ );
+ }
+
+ public function testTryToGetConjunctiveCompoundDescriptionForNullNewDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $currentDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $instance->asAnd( $currentDescription, null )
+ );
+ }
+
+ public function testTryToGetConjunctiveCompoundDescriptionForNullCurrentDescription() {
+
+ $instance = new DescriptionProcessor();
+
+ $newDescription = $instance->newDescriptionForWikiPageValueChunk( 'bar' );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\ValueDescription',
+ $instance->asAnd( null, $newDescription )
+ );
+ }
+
+ public function testConstuctDescriptionWithContextPage() {
+
+ $instance = new DescriptionProcessor();
+
+ $instance->setContextPage(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $currentDescription = new Disjunction();
+
+ $newDescription = $instance->newDescriptionForPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ 'foobar'
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Language\Conjunction',
+ $instance->asAnd( $currentDescription, $newDescription )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/LegacyParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/LegacyParserTest.php
new file mode 100644
index 00000000..8d1344f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/LegacyParserTest.php
@@ -0,0 +1,380 @@
+<?php
+
+namespace SMW\Tests\Query\Parser;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Parser\DescriptionProcessor;
+use SMW\Query\Parser\LegacyParser as QueryParser;
+use SMW\Query\Parser\Tokenizer;
+use SMW\Query\QueryToken;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Query\Parser\LegacyParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class LegacyParserTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $descriptionFactory;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->descriptionFactory = new DescriptionFactory();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->queryParser = new QueryParser(
+ new DescriptionProcessor(),
+ new Tokenizer(),
+ new QueryToken()
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $descriptionProcessor = $this->getMockBuilder( '\SMW\Query\Parser\DescriptionProcessor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tokenizer = $this->getMockBuilder( '\SMW\Query\Parser\Tokenizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryToken = $this->getMockBuilder( '\SMW\Query\QueryToken' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // Legacy class match
+ $this->assertInstanceOf(
+ '\SMWQueryParser',
+ new QueryParser( $descriptionProcessor, $tokenizer, $queryToken )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Parser',
+ new QueryParser( $descriptionProcessor, $tokenizer, $queryToken )
+ );
+ }
+
+ public function testCreateCondition() {
+
+ $this->assertEquals(
+ '[[Foo::Bar]]',
+ $this->queryParser->createCondition( 'Foo', 'Bar' )
+ );
+
+ $this->assertEquals(
+ '[[Foo::Bar]]',
+ $this->queryParser->createCondition( new DIProperty( 'Foo' ), 'Bar' )
+ );
+ }
+
+ public function testPropertyWildardDescription() {
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Foo' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Foo::+]]' )
+ );
+ }
+
+ public function testNamespaceWildardDescription() {
+
+ $description = $this->descriptionFactory->newNamespaceDescription(
+ NS_MAIN
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[:+]]' )
+ );
+ }
+
+ public function testPageDescription() {
+
+ $description = $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN, '' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Foo]]' )
+ );
+ }
+
+ public function testPropertyNotEqualValueDescription() {
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Has foo' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Has foo' )->setPropertyTypeId( '_wpg' ),
+ SMW_CMP_NEQ
+ )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Has foo::!Bar]]' )
+ );
+ }
+
+ public function testInversePropertyDescription() {
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Has foo', true )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Has foo', true )->setPropertyTypeId( '_wpg' ),
+ SMW_CMP_EQ
+ )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[-Has foo::Bar]]' )
+ );
+ }
+
+ public function testConjunctionForCategoryPropertyValueGreaterThanOrEqualLessThanOrEqual() {
+
+ $someGreaterThanOrEqualProperty = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'One' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'A', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'One' )->setPropertyTypeId( '_wpg' ), SMW_CMP_GEQ )
+ );
+
+ $someLessThanOrEqualProperty = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'D', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ), SMW_CMP_LEQ )
+ );
+
+ $classDescription = $this->descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY, '' )
+ );
+
+ $description = $this->descriptionFactory->newConjunction();
+ $description->addDescription( $classDescription );
+ $description->addDescription( $someGreaterThanOrEqualProperty );
+ $description->addDescription( $someLessThanOrEqualProperty );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] [[One::>A]] [[Two::<D]]' )
+ );
+ }
+
+ public function testConjunctionForCategoryPropertyChainDescription() {
+
+ $someProperty = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'One' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ), SMW_CMP_EQ
+ )
+ )
+ );
+
+ $classDescription = $this->descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY, '' )
+ );
+
+ $description = $this->descriptionFactory->newConjunction();
+ $description->addDescription( $classDescription );
+ $description->addDescription( $someProperty );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] [[One.Two::Bar]]' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] [[One::<q>[[Two::Bar]]</q>]]' )
+ );
+ }
+
+ public function testDisjunctionForCategoryPropertyChainDescription() {
+
+ $someProperty = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'One' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ), SMW_CMP_EQ
+ )
+ )
+ );
+
+ $classDescription = $this->descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY, '' )
+ );
+
+ $description = $this->descriptionFactory->newDisjunction();
+ $description->addDescription( $classDescription );
+ $description->addDescription( $someProperty );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] OR [[One.Two::Bar]]' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] OR [[One::<q>[[Two::Bar]]</q>]]' )
+ );
+ }
+
+ public function testDisjunctionForCategoryChainDescription() {
+
+ $classFooDescription = $this->descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Foo', NS_CATEGORY, '' )
+ );
+
+ $classBarDescription = $this->descriptionFactory->newClassDescription(
+ new DIWikiPage( 'Bar', NS_CATEGORY, '' )
+ );
+
+ $description = $this->descriptionFactory->newDisjunction();
+ $description->addDescription( $classFooDescription );
+ $description->addDescription( $classBarDescription );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo||Bar]]' )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[Category:Foo]] OR [[Category:Bar]]' )
+ );
+ }
+
+ public function testCombinedSubobjectPropertyChainDescription() {
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'One' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( '_SOBJ' )->setPropertyTypeId( '__sob' ),
+ $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Bar', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'Two' )->setPropertyTypeId( '_wpg' ), SMW_CMP_EQ
+ )
+ )
+ )
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[One.Has subobject.Two::Bar]]' )
+ );
+ }
+
+ public function testSubqueryDisjunction() {
+
+ $property = new DIProperty( 'HasSomeProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $disjunction = $this->descriptionFactory->newDisjunction( [
+ $this->descriptionFactory->newValueDescription( new DIWikiPage( 'Foo', NS_MAIN ), $property ),
+ $this->descriptionFactory->newValueDescription( new DIWikiPage( 'Bar', NS_MAIN ), $property )
+ ] );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $property,
+ $disjunction
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[HasSomeProperty::Foo||Bar]]' )
+ );
+ }
+
+ public function testNestedPropertyConjunction() {
+
+ $property = DIProperty::newFromUserLabel( 'Born in' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $conjunction = $this->descriptionFactory->newConjunction( [
+ $this->descriptionFactory->newClassDescription( new DIWikiPage( 'City', NS_CATEGORY ) ),
+ $this->descriptionFactory->newSomeProperty(
+ DIProperty::newFromUserLabel( 'Located in' )->setPropertyTypeId( '_wpg' ),
+ $this->descriptionFactory->newValueDescription(
+ new DIWikiPage( 'Outback', NS_MAIN ),
+ DIProperty::newFromUserLabel( 'Located in' )->setPropertyTypeId( '_wpg' ) )
+ )
+ ]
+ );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $property,
+ $conjunction
+ );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '[[born in::<q>[[Category:City]] [[located in::Outback]]</q>]]' )
+ );
+ }
+
+ public function testRestrictedDefaultNamespace() {
+
+ $property = DIProperty::newFromUserLabel( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $property,
+ $this->descriptionFactory->newValueDescription( new DIWikiPage( 'Bar', NS_MAIN ), $property )
+ );
+
+ $description = $this->descriptionFactory->newConjunction( [
+ $description,
+ $this->descriptionFactory->newNamespaceDescription( NS_MAIN )
+ ] );
+
+ $this->queryParser->setDefaultNamespaces( [ NS_MAIN ] );
+
+ $this->assertEquals(
+ $description,
+ $this->queryParser->getQueryDescription( '<q>[[Foo::Bar]]</q>[[:+]]' )
+ );
+
+ $this->assertEmpty(
+ $this->queryParser->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TermParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TermParserTest.php
new file mode 100644
index 00000000..e7e705c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TermParserTest.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace SMW\Tests\Query\Parser;
+
+use SMW\Query\Parser\TermParser;
+
+/**
+ * @covers \SMW\Query\Parser\TermParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TermParserTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TermParser::class,
+ new TermParser()
+ );
+ }
+
+ /**
+ * @dataProvider termProvider
+ */
+ public function testTerm_parser( $term, $expected ) {
+
+ $instance = new TermParser();
+
+ $this->assertEquals(
+ $expected,
+ $instance->parse( $term )
+ );
+ }
+
+ /**
+ * @dataProvider term_prefixProvider
+ */
+ public function testTerm_prefix_parser( $term, $prefixes, $expected ) {
+
+ $instance = new TermParser( $prefixes );
+
+ $this->assertEquals(
+ $expected,
+ $instance->parse( $term )
+ );
+ }
+
+ public function termProvider() {
+
+ yield [
+ 'in:foo',
+ '[[in:foo]]'
+ ];
+
+ yield [
+ '[[in:foo]]',
+ '[[in:foo]]'
+ ];
+
+ yield [
+ 'in:foo || bar',
+ '[[in:foo]] || bar'
+ ];
+
+ yield [
+ 'in:foo && bar',
+ '[[in:foo]] && bar'
+ ];
+
+ yield [
+ 'in:foo || in:bar',
+ '[[in:foo]] || [[in:bar]]'
+ ];
+
+ yield [
+ 'in:foo && in:bar',
+ '[[in:foo]] && [[in:bar]]'
+ ];
+
+ yield [
+ 'in:foo bar in:bar ',
+ '[[in:foo bar]] [[in:bar]]'
+ ];
+
+ yield [
+ 'in:foo bar && in:bar',
+ '[[in:foo bar]] && [[in:bar]]'
+ ];
+
+ yield [
+ 'in:foo bar || in:bar ',
+ '[[in:foo bar]] || [[in:bar]]'
+ ];
+
+ yield [
+ '(in:foo bar && in:foo) || in:bar ',
+ '<q>[[in:foo bar]] && [[in:foo]]</q> || [[in:bar]]'
+ ];
+
+ yield [
+ 'in:foo bar in:bar phrase:foobar 123 && in:oooo',
+ '[[in:foo bar]] [[in:bar]] [[phrase:foobar 123]] && [[in:oooo]]'
+ ];
+
+ yield [
+ '<q>in:foo bar && in:bar</q> OR phrase:foo bar foobar',
+ '<q>[[in:foo bar]] && [[in:bar]]</q> OR [[phrase:foo bar foobar]]'
+ ];
+
+ yield [
+ '(in:foo && in:bar)||in:foobar',
+ '<q>[[in:foo]] && [[in:bar]]</q> || [[in:foobar]]'
+ ];
+
+ yield [
+ '(in:foo && (in:bar AND not:ooo)) || in:foobar',
+ '<q>[[in:foo]] && <q>[[in:bar]] AND [[not:ooo]]</q></q> || [[in:foobar]]'
+ ];
+
+ yield [
+ '<q>in:foo bar && in:bar</q> OR [[Has number::123]]',
+ '<q>[[in:foo bar]] && [[in:bar]]</q> OR [[Has number::123]]'
+ ];
+
+ yield [
+ 'in:foo [[Has foo::bar]]',
+ '[[in:foo]] [[Has foo::bar]]'
+ ];
+
+ yield [
+ 'in:foo [[Has foo::bar]] (in:foo bar)',
+ '[[in:foo]] [[Has foo::bar]] <q>[[in:foo bar]]</q>'
+ ];
+
+ yield [
+ 'category:foo',
+ '[[category:foo]]'
+ ];
+
+ yield [
+ 'foo',
+ 'foo'
+ ];
+
+ yield [
+ '<q>[[Bar property::Foobar]]</q> Foo',
+ '<q>[[Bar property::Foobar]]</q> Foo'
+ ];
+
+ yield [
+ 'in:foo [[Has foo::bar]] (in:(foo bar && fin))',
+ '[[in:foo]] [[Has foo::bar]] <q>[[in:foo bar]] && [[in:fin]]</q>'
+ ];
+
+ yield [
+ 'has:foo has:bar',
+ '[[foo::+]] [[bar::+]]'
+ ];
+
+ yield [
+ 'has:(foo && bar)',
+ '[[foo::+]] && [[bar::+]]'
+ ];
+
+ yield [
+ 'in:(foo && bar) in:(ham && cheese)',
+ '[[in:foo]] && [[in:bar]] [[in:ham]] && [[in:cheese]]'
+ ];
+
+ yield [
+ 'has:(foo && bar) in:(ham && cheese)',
+ '[[foo::+]] && [[bar::+]] [[in:ham]] && [[in:cheese]]'
+ ];
+ }
+
+ public function term_prefixProvider() {
+
+ yield [
+ 'in:foo || not:bar',
+ [ 'keyw' => [ 'Has keyword', 'Keyw' ] ],
+ '[[in:foo]] || [[not:bar]]'
+ ];
+
+ yield [
+ 'in:foo || keyword:foo bar || keyw:foo bar',
+ [ 'keyw' => [ 'Has keyword', 'Keyw' ] ],
+ '[[in:foo]] || keyword:foo bar]] || <q>[[Has keyword::foo bar]] || [[Keyw::foo bar]]</q>'
+ ];
+
+ yield [
+ 'in:foo keyw:foo bar',
+ [ 'keyw' => [ 'Has keyword', 'Keyw' ] ],
+ '[[in:foo]] <q>[[Has keyword::foo bar]] || [[Keyw::foo bar]]</q>'
+ ];
+
+ yield [
+ 'in:foo keyw:foo bar [[Foo::bar]]',
+ [ 'keyw' => [ 'Has keyword', 'Keyw' ] ],
+ '[[in:foo]] <q>[[Has keyword::foo bar]] || [[Keyw::foo bar]]</q> [[Foo::bar]]'
+ ];
+
+ yield [
+ 'in:foo (a:foo bar || not:bar)',
+ [ 'a' => [ 'Has keyword', 'Keyw' ] ],
+ '[[in:foo]] <q><q>[[Has keyword::foo bar]] || [[Keyw::foo bar]]</q> || [[not:bar]]</q>'
+ ];
+
+ yield [
+ 'in:foo',
+ [ 'in' => [ 'a', 'b' ] ],
+ '[[in:foo]]'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TokenizerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TokenizerTest.php
new file mode 100644
index 00000000..6c509426
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Parser/TokenizerTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\Tests\Query\Parser;
+
+use SMW\Query\Parser\Tokenizer;
+
+/**
+ * @covers \SMW\Query\Parser\Tokenizer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TokenizerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Tokenizer::class,
+ new Tokenizer()
+ );
+ }
+
+ /**
+ * @dataProvider tokenProvider
+ */
+ public function testGetToken( $currentString, $stoppattern, $expectedRes, $expectedCurrent ) {
+
+ $instance = new Tokenizer();
+ $instance->setDefaultPattern( [] );
+
+ $res = $instance->getToken( $currentString, $stoppattern );
+
+ $this->assertEquals(
+ $expectedRes,
+ $res
+ );
+
+ $this->assertEquals(
+ $expectedCurrent,
+ $currentString
+ );
+ }
+
+ public function tokenProvider() {
+
+ yield [
+ '',
+ '',
+ '',
+ ''
+ ];
+
+ yield [
+ '[[Foo]]',
+ '',
+ '[[',
+ 'Foo]]'
+ ];
+
+ yield [
+ '|Foo',
+ '',
+ '|',
+ 'Foo'
+ ];
+
+ yield [
+ '::Foo',
+ '',
+ '::',
+ 'Foo'
+ ];
+
+ yield [
+ 'Foo]]',
+ '',
+ 'Foo',
+ ']]'
+ ];
+
+ yield [
+ 'Foo]][[Bar',
+ '',
+ 'Foo',
+ ']][[Bar'
+ ];
+
+ yield [
+ ']][[Bar',
+ '',
+ ']]',
+ '[[Bar'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/DeserializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/DeserializerTest.php
new file mode 100644
index 00000000..72c237d5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/DeserializerTest.php
@@ -0,0 +1,210 @@
+<?php
+
+namespace SMW\Tests\Query\PrintRequest;
+
+use SMW\DataValues\PropertyChainValue;
+use SMW\Localizer;
+use SMW\Query\PrintRequest;
+use SMW\Query\PrintRequest\Deserializer;
+use SMWPropertyValue as PropertyValue;
+
+/**
+ * @covers SMW\Query\PrintRequest\Deserializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DeserializerTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testDeserialize( $text, $showMode, $expectedLabel, $expectedMode, $expectedDataInstance, $expectedOutputFormat ) {
+
+ $instance = Deserializer::deserialize( $text, $showMode );
+
+ $this->assertEquals(
+ $expectedLabel,
+ $instance->getLabel()
+ );
+
+ $this->assertEquals(
+ $expectedMode,
+ $instance->getMode()
+ );
+
+ if ( $expectedDataInstance !== null ) {
+ $this->assertInstanceOf(
+ $expectedDataInstance,
+ $instance->getData()
+ );
+ }
+
+ $this->assertSame(
+ $expectedOutputFormat,
+ $instance->getOutputFormat()
+ );
+ }
+
+ public function textProvider() {
+
+ #0
+ $provider[] = [
+ 'Foo',
+ false,
+ 'Foo',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ ''
+ ];
+
+ #1
+ $provider[] = [
+ '-Foo',
+ false,
+ '-Foo',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ ''
+ ];
+
+ #2
+ $provider[] = [
+ '-Foo=Bar',
+ false,
+ 'Bar',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ ''
+ ];
+
+ #3
+ // Category
+ $categoryName = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+ $provider[] = [
+ 'Category',
+ false,
+ $categoryName,
+ PrintRequest::PRINT_CATS,
+ null,
+ ''
+ ];
+
+ #4
+ // Category
+ $provider[] = [
+ 'Categories',
+ false,
+ $categoryName,
+ PrintRequest::PRINT_CATS,
+ null,
+ ''
+ ];
+
+ #5
+ $provider[] = [
+ 'Bar#foobar',
+ false,
+ 'Bar',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ 'foobar'
+ ];
+
+ #6
+ $provider[] = [
+ 'Foo#',
+ false,
+ 'Foo',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ '-'
+ ];
+
+ #7, 1464
+ $provider[] = [
+ 'Has boolean#<span style="color: green; font-size: 120%;">&#10003;</span>,<span style="color: #AA0000; font-size: 120%;">&#10005;</span>=Label on (&#10003;,&#10005;)',
+ false,
+ 'Label on (&#10003;,&#10005;)',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ '<span style="color: green; font-size: 120%;">&#10003;</span>,<span style="color: #AA0000; font-size: 120%;">&#10005;</span>'
+ ];
+
+ #8
+ $provider[] = [
+ 'Foo.Bar',
+ false,
+ 'Bar',
+ PrintRequest::PRINT_CHAIN,
+ PropertyChainValue::class,
+ ''
+ ];
+
+ #9
+ $provider[] = [
+ 'Foo.Bar#foobar',
+ false,
+ 'Bar',
+ PrintRequest::PRINT_CHAIN,
+ PropertyChainValue::class,
+ 'foobar'
+ ];
+
+ #10 ...
+ $provider[] = [
+ 'Foo = <span style="color: green; font-size: 120%;">Label</span>',
+ false,
+ '<span style="color: green; font-size: 120%;">Label</span>',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ ''
+ ];
+
+ #11 ...
+ $provider[] = [
+ 'Foo#Bar = <span style="color: green; font-size: 120%;">Label</span>',
+ false,
+ '<span style="color: green; font-size: 120%;">Label</span>',
+ PrintRequest::PRINT_PROP,
+ PropertyValue::class,
+ 'Bar'
+ ];
+
+ #12 #481
+ $provider[] = [
+ '#=Foo',
+ false,
+ 'Foo',
+ PrintRequest::PRINT_THIS,
+ null,
+ ''
+ ];
+
+ #13 #481
+ $provider[] = [
+ '#=Foo#',
+ false,
+ 'Foo',
+ PrintRequest::PRINT_THIS,
+ null,
+ '-'
+ ];
+
+ #13 #481
+ $provider[] = [
+ '#=Foo#-',
+ false,
+ 'Foo',
+ PrintRequest::PRINT_THIS,
+ null,
+ '-'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/FormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/FormatterTest.php
new file mode 100644
index 00000000..77db21a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/FormatterTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace SMW\Tests\Query\PrintRequest;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Query\PrintRequest;
+use SMW\Query\PrintRequest\Formatter;
+
+/**
+ * @covers SMW\Query\PrintRequest\Formatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FormatterTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider printRequestProvider
+ */
+ public function testFormat( $printRequest, $linker, $outputType, $expected ) {
+
+ $this->assertSame(
+ $expected,
+ Formatter::format( $printRequest, $linker, $outputType )
+ );
+ }
+
+ public function printRequestProvider() {
+
+ $provider['print-cats-wiki'] = [
+ new PrintRequest( PrintRequest::PRINT_CATS, 'Foo' ),
+ null,
+ Formatter::FORMAT_WIKI,
+ 'Foo'
+ ];
+
+ $provider['print-cats-html'] = [
+ new PrintRequest( PrintRequest::PRINT_CATS, 'Foo' ),
+ null,
+ Formatter::FORMAT_HTML,
+ 'Foo'
+ ];
+
+ $provider['print-ccat-html'] = [
+ new PrintRequest( PrintRequest::PRINT_CCAT, 'Foo', DIWikiPage::newFromText( 'Bar' )->getTitle() ),
+ null,
+ Formatter::FORMAT_HTML,
+ 'Foo'
+ ];
+
+ $provider['print-ccat-wiki'] = [
+ new PrintRequest( PrintRequest::PRINT_CCAT, 'Foo', DIWikiPage::newFromText( 'Bar' )->getTitle() ),
+ null,
+ Formatter::FORMAT_WIKI,
+ 'Foo'
+ ];
+
+ $provider['print-this-wiki'] = [
+ new PrintRequest( PrintRequest::PRINT_THIS, 'Foo' ),
+ null,
+ Formatter::FORMAT_WIKI,
+ 'Foo'
+ ];
+
+ $provider['print-this-html'] = [
+ new PrintRequest( PrintRequest::PRINT_THIS, 'Foo' ),
+ null,
+ Formatter::FORMAT_HTML,
+ 'Foo'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-wiki-no-linker'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data ),
+ null,
+ Formatter::FORMAT_WIKI,
+ 'Foo'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-html-no-linker'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data ),
+ null,
+ Formatter::FORMAT_HTML,
+ 'Foo'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/SerializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/SerializerTest.php
new file mode 100644
index 00000000..2149cd2e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequest/SerializerTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Tests\Query\PrintRequest;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\Query\PrintRequest;
+use SMW\Query\PrintRequest\Serializer;
+
+/**
+ * @covers SMW\Query\PrintRequest\Serializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SerializerTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testSerialize( $printRequest, $showParams, $expected ) {
+
+ $this->assertSame(
+ $expected,
+ Serializer::serialize( $printRequest, $showParams )
+ );
+ }
+
+ public function textProvider() {
+
+ $category = Localizer::getInstance()->getNamespaceTextById( NS_CATEGORY );
+
+ $provider['print-cats'] = [
+ new PrintRequest( PrintRequest::PRINT_CATS, 'Foo' ),
+ false,
+ "?{$category}=Foo"
+ ];
+
+ $provider['print-ccat'] = [
+ new PrintRequest( PrintRequest::PRINT_CCAT, 'Foo', DIWikiPage::newFromText( 'Bar' )->getTitle() ),
+ false,
+ '?Bar=Foo'
+ ];
+
+ $provider['print-this'] = [
+ new PrintRequest( PrintRequest::PRINT_THIS, 'Foo' ),
+ false,
+ '?=Foo'
+ ];
+
+ $provider['print-this-plain'] = [
+ new PrintRequest( PrintRequest::PRINT_THIS, 'Foo', null, '-' ),
+ false,
+ '?=Foo#-'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data ),
+ false,
+ '?Bar#=Foo'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-output'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data, 'foobar' ),
+ false,
+ '?Bar#foobar=Foo'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-output-parameters-no-show'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data, 'foobar', [ 'index' => 2 ] ),
+ false,
+ '?Bar#foobar=Foo'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-output-parameters-show'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data, 'foobar', [ 'index' => 2 ] ),
+ true,
+ '?Bar#foobar=Foo|+index=2'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Modification date' );
+
+ $provider['predefined-property'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, '', $data ),
+ false,
+ '?Modification date#'
+ ];
+
+ $data = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Bar' );
+
+ $provider['print-prop-output-lang-index'] = [
+ new PrintRequest( PrintRequest::PRINT_PROP, 'Foo', $data, '', [ 'lang' => 'en', 'index' => '1' ] ),
+ true,
+ '?Bar=Foo|+lang=en'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestFactoryTest.php
new file mode 100644
index 00000000..4174bb97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestFactoryTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DIProperty;
+use SMW\Query\PrintRequest;
+use SMW\Query\PrintRequestFactory;
+
+/**
+ * @covers \SMW\Query\PrintRequestFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PrintRequestFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Query\PrintRequestFactory',
+ new PrintRequestFactory()
+ );
+ }
+
+ public function testCanConstructPrintRequestFromProperty() {
+
+ $instance = new PrintRequestFactory();
+
+ $printRequest = $instance->newFromProperty(
+ new DIProperty( 'Foo' )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\PrintRequest',
+ $printRequest
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $printRequest->getLabel()
+ );
+ }
+
+ public function testCanConstructPrintRequestFromText() {
+
+ $instance = new PrintRequestFactory();
+
+ $printRequest = $instance->newFromText(
+ 'Foo'
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\PrintRequest',
+ $printRequest
+ );
+ }
+
+ public function testPrintRequestFromTextToReturnNullOnInvalidText() {
+
+ $instance = new PrintRequestFactory();
+
+ $printRequest = $instance->newFromText(
+ '--[[Foo',
+ false
+ );
+
+ $this->assertNull(
+ $printRequest
+ );
+ }
+
+ public function testCanConstructThisPrintRequest() {
+
+ $instance = new PrintRequestFactory();
+
+ $printRequest = $instance->newThisPrintRequest(
+ 'Foo'
+ );
+
+ $this->assertTrue(
+ $printRequest->isMode( PrintRequest::PRINT_THIS )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestTest.php
new file mode 100644
index 00000000..8b986975
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/PrintRequestTest.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DIProperty;
+use SMW\Query\PrintRequest as PrintRequest;
+use SMWPropertyValue as PropertyValue;
+
+/**
+ * @covers SMW\Query\PrintRequest
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PrintRequestTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstructPropertyPrintRequest() {
+
+ $propertyValue = $this->getMockBuilder( '\SMWPropertyValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyValue->expects( $this->once() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $this->assertInstanceOf(
+ 'SMW\Query\PrintRequest',
+ new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue )
+ );
+ }
+
+ public function testSetLabel() {
+
+ $propertyValue = new PropertyValue( '__pro' );
+ $propertyValue->setDataItem( new DIProperty( 'Foo' ) );
+
+ $instance = new PrintRequest( PrintRequest::PRINT_PROP, null, $propertyValue );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getCanonicalLabel()
+ );
+
+ $this->assertEquals(
+ null,
+ $instance->getLabel()
+ );
+
+ $this->assertEquals(
+ null,
+ $instance->getWikiText()
+ );
+
+ $instance->setLabel( 'Bar' );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->getLabel()
+ );
+
+ $this->assertEquals(
+ 'Bar',
+ $instance->getWikiText()
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getCanonicalLabel()
+ );
+ }
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testFromText( $text, $showMode, $expectedLabel ) {
+
+ $instance = PrintRequest::newFromText( $text, $showMode );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\PrintRequest',
+ $instance
+ );
+
+ $this->assertEquals(
+ $expectedLabel,
+ $instance->getLabel()
+ );
+ }
+
+ public function testFromTextToReturnNullOnInvalidText() {
+
+ $instance = PrintRequest::newFromText( '--[[Foo' );
+
+ $this->assertNull(
+ $instance
+ );
+ }
+
+ public function testRemoveParameter() {
+
+ $instance = PrintRequest::newFromText( 'Foo' );
+ $instance->setParameter( 'foo', 123 );
+
+ $this->assertEquals(
+ [
+ 'foo' => 123
+ ],
+ $instance->getParameters()
+ );
+
+ $instance->removeParameter( 'foo' );
+
+ $this->assertEquals(
+ [],
+ $instance->getParameters()
+ );
+ }
+
+ public function textProvider() {
+
+ #0
+ $provider[] = [
+ 'Foo',
+ false,
+ 'Foo'
+ ];
+
+ #1
+ $provider[] = [
+ 'Foo',
+ true,
+ ''
+ ];
+
+ #2
+ $provider[] = [
+ 'Foo=Bar',
+ false,
+ 'Bar'
+ ];
+
+ #3
+ $provider[] = [
+ 'Foo=Bar#123',
+ false,
+ 'Bar#123'
+ ];
+
+ #4
+ $provider[] = [
+ 'Foo#123=Bar',
+ false,
+ 'Bar'
+ ];
+
+ #5
+ $provider[] = [
+ 'Category=Foo',
+ false,
+ 'Foo'
+ ];
+
+ #6
+ $provider[] = [
+ '-Foo',
+ false,
+ '-Foo'
+ ];
+
+ #7
+ $provider[] = [
+ '-Foo=Bar',
+ false,
+ 'Bar'
+ ];
+
+ #8, 1464
+ $provider[] = [
+ 'Has boolean#<span style="color: green; font-size: 120%;">&#10003;</span>,<span style="color: #AA0000; font-size: 120%;">&#10005;</span>=Label on (&#10003;,&#10005;)',
+ false,
+ 'Label on (&#10003;,&#10005;)'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/DefaultParamDefinitionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/DefaultParamDefinitionTest.php
new file mode 100644
index 00000000..c89dc1bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/DefaultParamDefinitionTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\Query\Processor;
+
+use SMW\Query\Processor\DefaultParamDefinition;
+
+/**
+ * @covers \SMW\Query\Processor\DefaultParamDefinition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DefaultParamDefinitionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DefaultParamDefinition::class,
+ new DefaultParamDefinition()
+ );
+ }
+
+ public function testBuildParamDefinitions() {
+
+ $vars = [
+ 'smwgResultFormats' => [],
+ 'smwgResultAliases' => [],
+ 'smwgQuerySources' => [],
+ 'smwgQDefaultLimit' => 42,
+ 'smwgQUpperbound' => 100
+ ];
+
+ $this->assertInternalType(
+ 'array',
+ DefaultParamDefinition::buildParamDefinitions( $vars )
+ );
+ }
+
+ public function testGetParamDefinitions() {
+
+ $this->assertInternalType(
+ 'array',
+ DefaultParamDefinition::getParamDefinitions()
+ );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/ParamListProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/ParamListProcessorTest.php
new file mode 100644
index 00000000..4636db13
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/ParamListProcessorTest.php
@@ -0,0 +1,406 @@
+<?php
+
+namespace SMW\Tests\Query\Processor;
+
+use SMW\Query\Processor\ParamListProcessor;
+
+/**
+ * @covers \SMW\Query\Processor\ParamListProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ParamListProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $printRequestFactory = $this->getMockBuilder( '\SMW\Query\PrintRequestFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ParamListProcessor::class,
+ new ParamListProcessor( $printRequestFactory )
+ );
+ }
+
+ /**
+ * @dataProvider parametersProvider
+ */
+ public function testPreprocess( $parameters, $showMode, $expected ) {
+
+ $printRequestFactory = $this->getMockBuilder( '\SMW\Query\PrintRequestFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ParamListProcessor(
+ $printRequestFactory
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->preprocess( $parameters, $showMode )
+ );
+ }
+
+ /**
+ * @dataProvider legacyParametersProvider
+ */
+ public function testLegacyArray( $parameters ) {
+
+ $printRequestFactory = $this->getMockBuilder( '\SMW\Query\PrintRequestFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ParamListProcessor(
+ $printRequestFactory
+ );
+
+ $a = $instance->format(
+ $parameters,
+ ParamListProcessor::FORMAT_LEGACY
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $a[0]
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $a[1]
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $a[2]
+ );
+ }
+
+ public function parametersProvider() {
+
+ yield [
+ [ '[[Foo::Bar]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ // #640
+ yield [
+ [ '[[Foo::Bar=Foobar]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar=Foobar]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::&lt;Bar=Foobar&gt;]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::<Bar=Foobar>]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ // #3560
+ yield [
+ [ '[[Foo::Bar=-3DFoo]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar=-3DFoo]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar=-3DFoox003D]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar=-3DFoox003D]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ // A user shouldn't use `0x003D` as representation for `=`
+ yield [
+ [ '[[Foo::Bar=-3DFoo0x003D]]' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar=-3DFoo=]]',
+ 'printouts' => [],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar]]', 'mainlabel=-' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [],
+ 'parameters' => [
+ 'mainlabel' => '-'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar]]', '?Foobar' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [
+ '0bfab051cd82c364058617af13e9874a' => [
+ 'label' => 'Foobar',
+ 'params' => []
+ ]
+ ],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar]]', '?Foobar', '+abc=123' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [
+ '0bfab051cd82c364058617af13e9874a' => [
+ 'label' => 'Foobar',
+ 'params' => [
+ 'abc' => '123'
+ ]
+ ]
+ ],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar]]', '?Foobar', '+abc=123', '+abc=123' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [
+ '0bfab051cd82c364058617af13e9874a' => [
+ 'label' => 'Foobar',
+ 'params' => [
+ 'abc' => '123'
+ ]
+ ]
+ ],
+ 'parameters' => [],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ '[[Foo::Bar]]', '?Foobar', '+abc=123', '?ABC', '+abc=456', '+abc=+FOO', 'limit=10' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [
+ '0bfab051cd82c364058617af13e9874a' => [
+ 'label' => 'Foobar',
+ 'params' => [
+ 'abc' => '123'
+ ]
+ ],
+ '2a30f08efdf827f7e76b895fde0fe670' => [
+ 'label' => 'ABC',
+ 'params' => [
+ 'abc' => '456',
+ 'abc' => '+FOO'
+ ]
+ ]
+ ],
+ 'parameters' => [
+ 'limit' => '10'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ // mainlabel=Foo|+abc=123 is currently NOT supported
+ yield [
+ [ '[[Foo::Bar]]', 'mainlabel=Foo', '+abc=123' ],
+ false,
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [],
+ 'parameters' => [
+ 'mainlabel' => 'Foo'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ // #1645
+ yield [
+ [ 'Foo=Bar', 'link=none' ],
+ true,
+ [
+ 'showMode' => true,
+ 'templateArgs' => false,
+ 'query' => '[[:Foo=Bar]]',
+ 'printouts' => [],
+ 'parameters' => [
+ 'link' => 'none'
+ ],
+ 'this' => []
+ ]
+ ];
+
+
+ // #3196
+ yield [
+ [ 'Foo=Bar', 'link=none', 'intro=[[File:Foo.png|link=Bar]]' ],
+ true,
+ [
+ 'showMode' => true,
+ 'templateArgs' => false,
+ 'query' => '[[:Foo=Bar]]',
+ 'printouts' => [],
+ 'parameters' => [
+ 'link' => 'none',
+ 'intro' => '[[File:Foo.png|link=Bar]]'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [ 'Foo=Bar', 'link=none', '?ABC' ],
+ true,
+ [
+ 'showMode' => true,
+ 'templateArgs' => false,
+ 'query' => '[[:Foo=Bar]]',
+ 'printouts' => [
+ 'df76b46d65f71fd1a36054ec00947665' => [
+ 'label' => 'ABC',
+ 'params' => []
+ ]
+ ],
+ 'parameters' => [
+ 'link' => 'none'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ // #502
+ yield [
+ [ 'Foo=Bar', 'link=none', 'template=test', '?ABC' ],
+ true,
+ [
+ 'showMode' => true,
+ 'templateArgs' => true,
+ 'query' => '[[:Foo=Bar]]',
+ 'printouts' => [
+ '2a30f08efdf827f7e76b895fde0fe670' => [
+ 'label' => 'ABC',
+ 'params' => []
+ ]
+ ],
+ 'parameters' => [
+ 'link' => 'none',
+ 'template' => 'test'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ }
+
+ public function legacyParametersProvider() {
+
+ yield [
+ [
+ 'showMode' => false,
+ 'templateArgs' => false,
+ 'query' => '[[Foo::Bar]]',
+ 'printouts' => [
+ '0bfab051cd82c364058617af13e9874a' => [
+ 'label' => 'Foobar',
+ 'params' => [
+ 'abc' => '123'
+ ]
+ ],
+ '2a30f08efdf827f7e76b895fde0fe670' => [
+ 'label' => 'ABC',
+ 'params' => [
+ 'abc' => '456',
+ 'abc' => '+FOO'
+ ]
+ ]
+ ],
+ 'parameters' => [
+ 'limit' => '10'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ yield [
+ [
+ 'showMode' => true,
+ 'templateArgs' => false,
+ 'query' => '[[:Foo=Bar]]',
+ 'printouts' => [],
+ 'parameters' => [
+ 'link' => 'none'
+ ],
+ 'this' => []
+ ]
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/QueryCreatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/QueryCreatorTest.php
new file mode 100644
index 00000000..73ea3063
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Processor/QueryCreatorTest.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\Tests\Query\Processor;
+
+use SMW\ApplicationFactory;
+use SMW\Query\Processor\QueryCreator;
+
+/**
+ * @covers SMW\Query\Processor\QueryCreator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryCreatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $queryFactory = $this->getMockBuilder( '\SMW\QueryFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ QueryCreator::class,
+ new QueryCreator( $queryFactory )
+ );
+ }
+
+ /**
+ * @dataProvider queryStringProvider
+ */
+ public function testCreate( $queryString, $params, $expected ) {
+
+ $instance = new QueryCreator(
+ ApplicationFactory::getInstance()->getQueryFactory()
+ );
+
+ $query = $instance->create( $queryString, $params );
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ $query
+ );
+
+ $this->assertSame(
+ $expected,
+ $query->toString()
+ );
+ }
+
+ public function queryStringProvider() {
+
+ $provider[] = [
+ '[[Foo::Bar]]',
+ [
+ 'limit' => 42,
+ 'offset' => 12
+ ],
+ '[[Foo::Bar]]|limit=42|offset=12|mainlabel='
+ ];
+
+ $provider[] = [
+ '[[Foo::Bar]]',
+ [
+ 'source' => 'foobar',
+ 'mainLabel' => 'Some'
+ ],
+ '[[Foo::Bar]]|limit=50|offset=0|mainlabel=Some|source=foobar'
+ ];
+
+ $provider[] = [
+ '[[Foo::Bar]]',
+ [
+ 'sort' => [ '', 'SomeA', 'SomeB' ],
+ 'order' => [ 'desc', 'random', 'asc' ]
+ ],
+ '[[Foo::Bar]]|limit=50|offset=0|mainlabel=|sort=SomeA,SomeB|order=random,asc'
+ ];
+
+ $provider[] = [
+ '[[Foo::Bar]]',
+ [
+ 'sort' => [ ',' ]
+ ],
+ '[[Foo::Bar]]|limit=50|offset=0|mainlabel=|sort=,|order=asc'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotatorFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotatorFactoryTest.php
new file mode 100644
index 00000000..dcb030e4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotatorFactoryTest.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotatorFactory;
+use SMWQuery as Query;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotatorFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ProfileAnnotatorFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotatorFactory',
+ new ProfileAnnotatorFactory()
+ );
+ }
+
+ public function testConstructDescriptionProfileAnnotator() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\DescriptionProfileAnnotator',
+ $instance->newDescriptionProfileAnnotator( $query )
+ );
+ }
+
+ public function testConstructCombinedProfileAnnotator() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotator',
+ $instance->newProfileAnnotator( $query, '' )
+ );
+ }
+
+ public function testConstructProfileAnnotatorsWithSourceAnnotator() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getQuerySource' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\SourceProfileAnnotator',
+ $instance->newProfileAnnotator( $query, 'SomeFormat' )
+ );
+ }
+
+ public function testConstructProfileAnnotatorsWithDurationAnnotator() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->at( 4 ) )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( Query::PROC_QUERY_TIME ) )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\DurationProfileAnnotator',
+ $instance->newProfileAnnotator( $query, 'SomeFormat' )
+ );
+ }
+
+ public function testConstructProfileAnnotatorsWithStatCodeAnnotator() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->at( 6 ) )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( Query::PROC_STATUS_CODE ) )
+ ->will( $this->returnValue( [ 100 ] ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\StatusCodeProfileAnnotator',
+ $instance->newProfileAnnotator( $query, 'SomeFormat' )
+ );
+ }
+
+ public function testConstructCombinedProfileAnnotatorOnNullContextPage() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( null ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotator',
+ $instance->newProfileAnnotator( $query, '' )
+ );
+ }
+
+ public function testConstructProfileAnnotators_SchemaLink() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $query->expects( $this->at( 7 ) )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'schema_link' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new ProfileAnnotatorFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\SchemaLinkProfileAnnotator',
+ $instance->newProfileAnnotator( $query, 'SomeFormat' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DescriptionProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DescriptionProfileAnnotatorTest.php
new file mode 100644
index 00000000..74ccdd27
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DescriptionProfileAnnotatorTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\DescriptionProfileAnnotator;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\DescriptionProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DescriptionProfileTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\DescriptionProfileAnnotator',
+ new DescriptionProfileAnnotator( $profileAnnotator, $description )
+ );
+ }
+
+ public function testCreateProfile() {
+
+ $subject =new DIWikiPage( __METHOD__, NS_MAIN, '', 'foo' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $description->expects( $this->once() )
+ ->method( 'getSize' )
+ ->will( $this->returnValue( 2 ) );
+
+ $description->expects( $this->once() )
+ ->method( 'getDepth' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new DescriptionProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $description
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 3,
+ 'propertyKeys' => [ '_ASKST', '_ASKSI', '_ASKDE' ],
+ 'propertyValues' => [ 'Foo', 2, 42 ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getContainer()->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DurationProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DurationProfileAnnotatorTest.php
new file mode 100644
index 00000000..259d0122
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/DurationProfileAnnotatorTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\DurationProfileAnnotator;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\DurationProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DurationProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\DurationProfileAnnotator',
+ new DurationProfileAnnotator( $profileAnnotator, 0.42 )
+ );
+ }
+
+ /**
+ * @dataProvider durationDataProvider
+ */
+ public function testCreateProfile( $duration, $expected ) {
+
+ $subject =new DIWikiPage( __METHOD__, NS_MAIN, '', 'foo' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new DurationProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $duration
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getContainer()->getSemanticData()
+ );
+ }
+
+ public function durationDataProvider() {
+
+ $provider = [];
+
+ $provider[] = [ 0, [
+ 'propertyCount' => 0
+ ] ];
+
+ $provider[] = [ 0.9001, [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ASKDU' ],
+ 'propertyValues' => [ 0.9001 ]
+ ] ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/FormatProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/FormatProfileAnnotatorTest.php
new file mode 100644
index 00000000..a08f10b6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/FormatProfileAnnotatorTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\FormatProfileAnnotator;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\FormatProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FormatProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\FormatProfileAnnotator',
+ new FormatProfileAnnotator( $profileAnnotator, 'table' )
+ );
+ }
+
+ public function testCreateProfile() {
+
+ $subject =new DIWikiPage( __METHOD__, NS_MAIN, '', 'foo' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new FormatProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ 'table'
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ASKFO' ],
+ 'propertyValues' => [ 'table' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getContainer()->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/NullProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/NullProfileAnnotatorTest.php
new file mode 100644
index 00000000..e01bb766
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/NullProfileAnnotatorTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\NullProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class NullProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $container = $this->getMockBuilder( '\SMWDIContainer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\NullProfileAnnotator',
+ new NullProfileAnnotator( $container )
+ );
+ }
+
+ public function testMethodAccess() {
+
+ $subject =new DIWikiPage( __METHOD__, NS_MAIN, '', '_QUERYadcb944aa33b2c972470b73964c547c0' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new NullProfileAnnotator(
+ $container
+ );
+
+ $instance->addAnnotation();
+
+ $this->assertInstanceOf(
+ '\SMW\DIProperty',
+ $instance->getProperty()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $instance->getContainer()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWContainerSemanticData',
+ $instance->getSemanticData()
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/ParametersProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/ParametersProfileAnnotatorTest.php
new file mode 100644
index 00000000..00409cf9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/ParametersProfileAnnotatorTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Query\ProfileAnnotators\ParametersProfileAnnotator;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\ParametersProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ParametersProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\ParametersProfileAnnotator',
+ new ParametersProfileAnnotator( $profileAnnotator, $query )
+ );
+ }
+
+ public function testCreateProfile() {
+
+ $subject =new DIWikiPage( __METHOD__, NS_MAIN, '', 'foo' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getQueryMode' )
+ ->will( $this->returnValue( 1 ) );
+
+ $instance = new ParametersProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $query
+ );
+
+ $instance->addAnnotation();
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ASKPA' ],
+ 'propertyValues' => [ '{"limit":42,"offset":0,"sort":[],"order":[],"mode":1}' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getContainer()->getSemanticData()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SchemaLinkProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SchemaLinkProfileAnnotatorTest.php
new file mode 100644
index 00000000..89150c66
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SchemaLinkProfileAnnotatorTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Query\ProfileAnnotators\SchemaLinkProfileAnnotator;
+use SMW\Tests\TestEnvironment;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\SchemaLinkProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaLinkProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = TestEnvironment::newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ SchemaLinkProfileAnnotator::class,
+ new SchemaLinkProfileAnnotator( $profileAnnotator, '' )
+ );
+ }
+
+ public function testAddAnnotationOnInvalidSchemaLinkTypeThrowsException() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SchemaLinkProfileAnnotator( $profileAnnotator, [] );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->addAnnotation();
+ }
+
+ /**
+ * @dataProvider SchemaLinkProvider
+ */
+ public function testAddAnnotation( $SchemaLink, $expected ) {
+
+ $subject = new DIWikiPage( __METHOD__, NS_MAIN, '', '_QUERYe7d20a88999' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new SchemaLinkProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $SchemaLink
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function SchemaLinkProvider() {
+
+ yield [
+ '',
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ yield [
+ 'Foo',
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_SCHEMA_LINK' ],
+ 'propertyValues' => [ 'Foo' ]
+ ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SourceProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SourceProfileAnnotatorTest.php
new file mode 100644
index 00000000..b88158ec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/SourceProfileAnnotatorTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Query\ProfileAnnotators\SourceProfileAnnotator;
+use SMW\Tests\TestEnvironment;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\SourceProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SourceProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->semanticDataValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotators\SourceProfileAnnotator',
+ new SourceProfileAnnotator( $profileAnnotator )
+ );
+ }
+
+ /**
+ * @dataProvider sourceDataProvider
+ */
+ public function testCreateProfile( $source, $expected ) {
+
+ $subject = new DIWikiPage( __METHOD__, NS_MAIN, '', '_QUERYe7d20a88' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new SourceProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $source
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function sourceDataProvider() {
+
+ $provider = [];
+
+ $provider[] = [ '', [
+ 'propertyCount' => 0
+ ] ];
+
+ $provider[] = [ 'foo', [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ASKSC' ],
+ 'propertyValues' => [ 'foo' ]
+ ] ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/StatusCodeProfileAnnotatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/StatusCodeProfileAnnotatorTest.php
new file mode 100644
index 00000000..c451abf3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ProfileAnnotators/StatusCodeProfileAnnotatorTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Tests\Query\ProfileAnnotators;
+
+use SMW\DIWikiPage;
+use SMW\Query\ProfileAnnotators\NullProfileAnnotator;
+use SMW\Query\ProfileAnnotators\StatusCodeProfileAnnotator;
+use SMW\Tests\TestEnvironment;
+use SMWContainerSemanticData as ContainerSemanticData;
+use SMWDIContainer as DIContainer;
+
+/**
+ * @covers \SMW\Query\ProfileAnnotators\StatusCodeProfileAnnotator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class StatusCodeProfileAnnotatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = TestEnvironment::newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $profileAnnotator = $this->getMockBuilder( '\SMW\Query\ProfileAnnotator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ StatusCodeProfileAnnotator::class,
+ new StatusCodeProfileAnnotator( $profileAnnotator )
+ );
+ }
+
+ /**
+ * @dataProvider codesDataProvider
+ */
+ public function testCreateProfile( $codes, $expected ) {
+
+ $subject = new DIWikiPage( __METHOD__, NS_MAIN, '', '_QUERYe7d20a88' );
+
+ $container = new DIContainer(
+ new ContainerSemanticData( $subject )
+ );
+
+ $instance = new StatusCodeProfileAnnotator(
+ new NullProfileAnnotator( $container ),
+ $codes
+ );
+
+ $instance->addAnnotation();
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function codesDataProvider() {
+
+ $provider[] = [
+ [],
+ [
+ 'propertyCount' => 0
+ ]
+ ];
+
+ $provider[] = [
+ [ 100 ],
+ [
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ASKCO' ],
+ 'propertyValues' => [ 100 ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryComparatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryComparatorTest.php
new file mode 100644
index 00000000..fe1f1525
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryComparatorTest.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\QueryComparator;
+
+/**
+ * @covers \SMW\Query\QueryComparator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class QueryComparatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $comparatorList = '';
+
+ $this->assertInstanceOf(
+ '\SMW\Query\QueryComparator',
+ new QueryComparator( $comparatorList, false )
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Query\QueryComparator',
+ QueryComparator::getInstance()
+ );
+ }
+
+ /**
+ * @dataProvider stringComparatorProvider
+ */
+ public function testGetComparatorFromString( $stringComparator, $expected ) {
+
+ $comparatorList = '';
+
+ $instance = new QueryComparator( $comparatorList, false );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getComparatorFromString( $stringComparator )
+ );
+ }
+
+ /**
+ * @dataProvider stringComparatorProvider
+ */
+ public function testGetStringForComparator( $stringComparator, $comparator ) {
+
+ $comparatorList = '';
+
+ $instance = new QueryComparator( $comparatorList, false );
+
+ $this->assertEquals(
+ $stringComparator,
+ $instance->getStringForComparator( $comparator )
+ );
+ }
+
+ /**
+ * @dataProvider extractStringComparatorProvider
+ */
+ public function testExtractComparatorFromString( $string, $expectedString, $expectedComparator ) {
+
+ $comparatorList = '';
+
+ $instance = new QueryComparator( $comparatorList, true );
+
+ $this->assertEquals(
+ $expectedComparator,
+ $instance->extractComparatorFromString( $string )
+ );
+
+ $this->assertEquals(
+ $expectedString,
+ $string
+ );
+ }
+
+ /**
+ * @dataProvider containsComparatorProvider
+ */
+ public function testContainsComparator( $string, $comparator, $expected ) {
+
+ $comparatorList = '';
+
+ $instance = new QueryComparator( $comparatorList, true );
+
+ $this->assertEquals(
+ $expected,
+ $instance->containsComparator( $string, $comparator )
+ );
+ }
+
+ public function stringComparatorProvider() {
+
+ $provider[] = [
+ '!~',
+ SMW_CMP_NLKE
+ ];
+
+ return $provider;
+ }
+
+ public function extractStringComparatorProvider() {
+
+ $provider[] = [
+ '!~Foo',
+ 'Foo',
+ SMW_CMP_NLKE
+ ];
+
+ $provider[] = [
+ '<Foo',
+ 'Foo',
+ SMW_CMP_LESS
+ ];
+
+ $provider[] = [
+ 'like:Foo',
+ 'Foo',
+ SMW_CMP_PRIM_LIKE
+ ];
+
+ $provider[] = [
+ 'nlike:Foo',
+ 'Foo',
+ SMW_CMP_PRIM_NLKE
+ ];
+
+ return $provider;
+ }
+
+ public function containsComparatorProvider() {
+
+ $provider[] = [
+ '~someThing',
+ SMW_CMP_EQ,
+ false
+ ];
+
+ $provider[] = [
+ 'someThing',
+ SMW_CMP_EQ,
+ true
+ ];
+
+ $provider[] = [
+ '!~someThing',
+ SMW_CMP_NLKE,
+ true
+ ];
+
+ $provider[] = [
+ '!~someThing',
+ SMW_CMP_LIKE,
+ false
+ ];
+
+ $provider[] = [
+ '>>someThing',
+ SMW_CMP_LESS,
+ false
+ ];
+
+ $provider[] = [
+ '<<someThing',
+ SMW_CMP_LESS,
+ true
+ ];
+
+
+ $provider[] = [
+ 'like:someThing',
+ SMW_CMP_PRIM_LIKE,
+ true
+ ];
+
+ $provider[] = [
+ 'nlike:someThing',
+ SMW_CMP_PRIM_NLKE,
+ true
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryLinkerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryLinkerTest.php
new file mode 100644
index 00000000..64d34a54
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryLinkerTest.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\QueryLinker;
+
+/**
+ * @covers SMW\Query\QueryLinker
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryLinkerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\Query\QueryLinker',
+ new QueryLinker()
+ );
+ }
+
+ public function testGet() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getExtraPrintouts' )
+ ->will( $this->returnValue( [] ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( [] ) );
+
+ $parameters = [
+ 'Foo' => 'Bar',
+ 'Foobar'
+ ];
+
+ $this->assertInstanceOf(
+ 'SMWInfolink',
+ QueryLinker::get( $query, $parameters )
+ );
+ }
+
+ /**
+ * @dataProvider sortOrderProvider
+ */
+ public function testSort_PredefinedProperty( $sortKeys, $expected ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getExtraPrintouts' )
+ ->will( $this->returnValue( [] ) );
+
+ $query->expects( $this->once() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( $sortKeys ) );
+
+ $link = QueryLinker::get( $query );
+ $link->setCompactLink( false );
+
+ $this->assertContains(
+ $expected,
+ $link->getLocalURL()
+ );
+ }
+
+ public function sortOrderProvider() {
+
+ yield[
+ [ '_MDAT' => 'DESC' ],
+ '&order=desc&sort=Modification%20date'
+ ];
+
+ yield[
+ [ '' => 'ASC' ],
+ '&mainlabel=&source=&offset='
+ ];
+
+ yield[
+ [ 'Foo_bar' => 'ASC' ],
+ '&mainlabel=&source=&offset=&order=asc&sort=Foo%20bar'
+ ];
+
+ yield[
+ [ '' => 'ASC', 'Foo_bar' => 'DESC' ],
+ '&mainlabel=&source=&offset=&order=asc%2Cdesc&sort=%2CFoo%20bar'
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QuerySourceFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QuerySourceFactoryTest.php
new file mode 100644
index 00000000..43e567ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QuerySourceFactoryTest.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\QuerySourceFactory;
+use SMW\QueryEngine;
+use SMW\Store;
+use SMW\StoreAware;
+use SMW\Tests\TestEnvironment;
+use SMWQuery as Query;
+
+/**
+ * @covers SMW\Query\QuerySourceFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QuerySourceFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ QuerySourceFactory::class,
+ new QuerySourceFactory( $this->store )
+ );
+ }
+
+ public function testGetFromFakeSource() {
+
+ $instance = new QuerySourceFactory(
+ $this->store,
+ [
+ 'foo' => FakeQueryEngine::class
+ ]
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->get( 'foo' )
+ );
+ }
+
+ public function testGetStandardStore() {
+
+ $instance = new QuerySourceFactory(
+ $this->store,
+ []
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\SQLStore',
+ $instance->get( 'sql_store' )
+ );
+
+ $this->assertEquals(
+ 'SMWSQLStore',
+ $instance->toString( 'sql_store' )
+ );
+ }
+
+ public function testGetAsString() {
+
+ $store = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getInfo' )
+ ->will( $this->returnValue( [ 'SPARQLStore' ] ) );
+
+ $instance = new QuerySourceFactory(
+ $store,
+ []
+ );
+
+ $this->assertContains(
+ 'SPARQLStore',
+ $instance->toString()
+ );
+ }
+
+ public function testGetFromAnotherFakeSourceThatImplementsStoreAware() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->with( $this->stringContains( 'foo' ) );
+
+ $instance = new QuerySourceFactory(
+ $store,
+ [
+ 'bar' => AnotherFakeQueryEngine::class
+ ]
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->get( 'bar' )
+ );
+ }
+}
+
+class FakeQueryEngine implements QueryEngine {
+
+ public function getQueryResult( Query $query ) {
+ return '';
+ }
+
+}
+
+class AnotherFakeQueryEngine extends FakeQueryEngine implements StoreAware {
+
+ public function setStore( Store $store ) {
+ return $store->getConnection( 'foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryStringifierTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryStringifierTest.php
new file mode 100644
index 00000000..0156cfed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryStringifierTest.php
@@ -0,0 +1,302 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\QueryStringifier;
+
+/**
+ * @covers \SMW\Query\QueryStringifier
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryStringifierTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testToArray( $query, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ QueryStringifier::toArray( $query )
+ );
+ }
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testToJson( $query, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ json_decode( QueryStringifier::toJson( $query ), true )
+ );
+ }
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testGet( $query, $array, $expected ) {
+
+ $this->assertSame(
+ $expected,
+ QueryStringifier::toString( $query )
+ );
+ }
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testRawUrlEncode( $query, $array, $encode, $expected ) {
+
+ $this->assertSame(
+ $expected,
+ QueryStringifier::rawUrlEncode( $query )
+ );
+ }
+
+ public function queryProvider() {
+
+ #0
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null
+ ],
+ 'printouts' => []
+ ],
+ '[[Foo::bar]]|limit=42|offset=0|mainlabel=',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D'
+ ];
+
+ #1
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getQuerySource' )
+ ->will( $this->returnValue( 'Baz' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null,
+ 'source' => 'Baz'
+ ],
+ 'printouts' => []
+ ],
+ '[[Foo::bar]]|limit=42|offset=0|mainlabel=|source=Baz',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D%7Csource%3DBaz'
+ ];
+
+ #2
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( [ 'Foobar' => 'DESC' ] ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null,
+ 'sort' => 'Foobar',
+ 'order' => 'desc'
+ ],
+ 'printouts' => []
+ ],
+ '[[Foo::bar]]|limit=42|offset=0|mainlabel=|sort=Foobar|order=desc',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D%7Csort%3DFoobar%7Corder%3Ddesc'
+ ];
+
+ #3
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( [ 'Foobar' => 'DESC', 'Foobaz' => 'ASC' ] ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null,
+ 'sort' => 'Foobar,Foobaz',
+ 'order' => 'desc,asc'
+ ],
+ 'printouts' => []
+ ],
+ '[[Foo::bar]]|limit=42|offset=0|mainlabel=|sort=Foobar,Foobaz|order=desc,asc',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D%7Csort%3DFoobar%2CFoobaz%7Corder%3Ddesc%2Casc'
+ ];
+
+ #4
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getSerialisation' )
+ ->will( $this->returnValue( '?ABC' ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getExtraPrintouts' )
+ ->will( $this->returnValue( [ $printRequest ] ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null
+ ],
+ 'printouts' => [
+ '?ABC'
+ ]
+ ],
+ '[[Foo::bar]]|?ABC|limit=42|offset=0|mainlabel=',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7C%3FABC%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D'
+ ];
+
+ #5 (#show returns with an extra =)
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getSerialisation' )
+ ->will( $this->returnValue( '?ABC' ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getQueryString' )
+ ->will( $this->returnValue( '[[Foo::bar]]' ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getOffset' )
+ ->will( $this->returnValue( 0 ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getExtraPrintouts' )
+ ->will( $this->returnValue( [ $printRequest ] ) );
+
+ yield [
+ $query,
+ [
+ 'conditions' => '[[Foo::bar]]',
+ 'parameters' => [
+ 'limit' => 42,
+ 'offset' => 0,
+ 'mainlabel' => null
+ ],
+ 'printouts' => [
+ '?ABC'
+ ]
+ ],
+ '[[Foo::bar]]|?ABC|limit=42|offset=0|mainlabel=',
+ '%5B%5BFoo%3A%3Abar%5D%5D%7C%3FABC%7Climit%3D42%7Coffset%3D0%7Cmainlabel%3D'
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTest.php
new file mode 100644
index 00000000..04f34b3b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTest.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMWQuery as Query;
+
+/**
+ * @covers \SMWQuery
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryTest extends \PHPUnit_Framework_TestCase {
+
+ private $smwgQMaxLimit;
+ private $smwgQMaxInlineLimit;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->smwgQMaxLimit = $GLOBALS['smwgQMaxLimit'];
+ $this->smwgQMaxInlineLimit = $GLOBALS['smwgQMaxInlineLimit'];
+ }
+
+ public function testCanConstruct() {
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ new Query( $description )
+ );
+ }
+
+ public function testSetGetLimitForLowerbound() {
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new Query( $description, Query::INLINE_QUERY );
+
+ $lowerboundLimit = 1;
+
+ $this->assertGreaterThan(
+ $lowerboundLimit,
+ $this->smwgQMaxLimit
+ );
+
+ $this->assertGreaterThan(
+ $lowerboundLimit,
+ $this->smwgQMaxInlineLimit
+ );
+
+ $instance->setLimit( $lowerboundLimit, true );
+
+ $this->assertEquals(
+ $lowerboundLimit,
+ $instance->getLimit()
+ );
+
+ $instance->setLimit( $lowerboundLimit, false );
+
+ $this->assertEquals(
+ $lowerboundLimit,
+ $instance->getLimit()
+ );
+ }
+
+ public function testSetGetLimitForUpperboundWhereLimitIsRestrictedByGLOBALRequirements() {
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new Query( $description, Query::INLINE_QUERY );
+
+ $upperboundLimit = 999999999;
+
+ $this->assertLessThan(
+ $upperboundLimit,
+ $this->smwgQMaxLimit
+ );
+
+ $this->assertLessThan(
+ $upperboundLimit,
+ $this->smwgQMaxInlineLimit
+ );
+
+ $instance->setLimit( $upperboundLimit, true );
+
+ $this->assertEquals(
+ $this->smwgQMaxInlineLimit,
+ $instance->getLimit()
+ );
+
+ $instance->setLimit( $upperboundLimit, false );
+
+ $this->assertEquals(
+ $this->smwgQMaxLimit,
+ $instance->getLimit()
+ );
+ }
+
+ public function testSetGetLimitForUpperboundWhereLimitIsUnrestricted() {
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new Query( $description, Query::INLINE_QUERY );
+
+ $upperboundLimit = 999999999;
+
+ $this->assertLessThan(
+ $upperboundLimit,
+ $this->smwgQMaxLimit
+ );
+
+ $this->assertLessThan(
+ $upperboundLimit,
+ $this->smwgQMaxInlineLimit
+ );
+
+ $instance->setUnboundLimit( $upperboundLimit );
+
+ $this->assertEquals(
+ $upperboundLimit,
+ $instance->getLimit()
+ );
+ }
+
+ public function testToArray() {
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $printRequest = $this->getMockBuilder( 'SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Query( $description, Query::INLINE_QUERY );
+ $instance->setExtraPrintouts( [ $printRequest ] );
+
+ $serialized = $instance->toArray();
+
+ $this->assertInternalType(
+ 'array',
+ $serialized
+ );
+
+ $expected = [
+ 'conditions',
+ 'parameters',
+ 'printouts'
+ ];
+
+ foreach ( $expected as $key ) {
+ $this->assertArrayHasKey( $key, $serialized );
+ }
+
+ $expectedParameters = [
+ 'limit',
+ 'offset',
+ 'mainlabel',
+ 'sortkeys',
+ 'querymode'
+ ];
+
+ foreach ( $expectedParameters as $key ) {
+ $this->assertArrayHasKey( $key, $serialized['parameters'] );
+ }
+ }
+
+ public function testGetHash() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getFingerprint' ] )
+ ->getMockForAbstractClass();
+
+ $instance = new Query( $description, Query::INLINE_QUERY );
+ $instance->setLimit( 50 );
+
+ $hash = $instance->getHash();
+
+ $this->assertInternalType(
+ 'string',
+ $hash
+ );
+
+ $instance->setLimit( 100 );
+
+ $this->assertNotEquals(
+ $hash,
+ $instance->getHash()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTokenTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTokenTest.php
new file mode 100644
index 00000000..b6b9e9e9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/QueryTokenTest.php
@@ -0,0 +1,219 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DataItemFactory;
+use SMW\Query\QueryToken;
+
+/**
+ * @covers \SMW\Query\QueryToken
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryTokenTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ QueryToken::class,
+ new QueryToken()
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testAddFromDesciption( $description, $expected ) {
+
+ $instance = new QueryToken();
+
+ $instance->addFromDesciption( $description );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getTokens()
+ );
+ }
+
+ public function testMulitpleAddFromDesciption() {
+
+ $instance = new QueryToken();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'abc Foo 123' ) ) );
+
+ $instance->addFromDesciption( $description );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIWikiPage( '~*123 bar 456' ) ) );
+
+ $instance->addFromDesciption( $description );
+
+ $this->assertEquals(
+ [
+ 'abc' => 0,
+ 'Foo' => 1,
+ 123 => 2,
+ 'bar' => 1,
+ 456 => 2
+ ],
+ $instance->getTokens()
+ );
+ }
+
+ /**
+ * @dataProvider highlightProvider
+ */
+ public function testHighlight( $description, $text, $type, $expected ) {
+
+ $instance = new QueryToken();
+
+ $instance->addFromDesciption( $description );
+ $instance->setOutputFormat( '-hL' );
+
+ $this->assertEquals(
+ $expected,
+ $instance->highlight( $text, $type )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItemFactory->newDIBlob( 'abc Foo 123' ) ) );
+
+ $provider[] = [
+ $description,
+ [
+ 'abc' => 0,
+ 'Foo' => 1,
+ 123 => 2
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function highlightProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->any() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $description->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItemFactory->newDIBlob( 'abc Foo 123 foobar' ) ) );
+
+ $provider[] = [
+ $description,
+ 'Lorem abc foobar',
+ QueryToken::HL_BOLD,
+ "Lorem <b>abc</b> <b>foo</b>bar"
+ ];
+
+ $provider[] = [
+ $description,
+ 'Lorem abc foobar',
+ QueryToken::HL_WIKI,
+ "Lorem '''abc''' '''foo'''bar"
+ ];
+
+ $provider[] = [
+ $description,
+ 'Lorem abc foobar',
+ QueryToken::HL_UNDERLINE,
+ "Lorem <u>abc</u> <u>foo</u>bar"
+ ];
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->any() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $description->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItemFactory->newDIBlob( 'integer porttitor portt' ) ) );
+
+ $provider[] = [
+ $description,
+ 'Integer porttitor mi id ante consequat consequat <b>porttitor</b>',
+ QueryToken::HL_BOLD,
+ "<b>Integer</b> <b>porttitor</b> mi id ante consequat consequat <b><b>porttitor</b></b>"
+ ];
+
+ $provider[] = [
+ $description,
+ 'Integer porttitor mi id ante consequat consequat <b>porttitor</b>',
+ QueryToken::HL_SPAN,
+ "<span class='smw-query-token'>Integer</span> <span class='smw-query-token'>porttitor</span> mi id ante consequat consequat <b><span class='smw-query-token'>porttitor</span></b>"
+ ];
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->any() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_PRIM_LIKE ) );
+
+ $description->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItemFactory->newDIBlob( 'abc Foo 123 foobar' ) ) );
+
+ $provider[] = [
+ $description,
+ 'Lorem abc foobar',
+ QueryToken::HL_BOLD,
+ "Lorem <b>abc</b> <b>foo</b>bar"
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/RemoteRequestTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/RemoteRequestTest.php
new file mode 100644
index 00000000..46fa2be1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/RemoteRequestTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\Query\RemoteRequest;
+
+/**
+ * @covers \SMW\Query\RemoteRequest
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RemoteRequestTest extends \PHPUnit_Framework_TestCase {
+
+ private $httpRequest;
+ private $query;
+
+ protected function setUp() {
+
+ $this->httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RemoteRequest::class,
+ new RemoteRequest( [], $this->httpRequest )
+ );
+ }
+
+ public function testGetQueryResult_CannotConnect() {
+
+ $this->httpRequest->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( false ) );
+
+ $parameters = [
+ 'url' => 'http://example.org/Foo'
+ ];
+
+ $instance = new RemoteRequest(
+ $parameters,
+ $this->httpRequest
+ );
+
+ $this->assertContains(
+ 'smw-remote-source-unavailable',
+ $instance->getQueryResult( $this->query )
+ );
+ }
+
+ public function testGetQueryResult_Connect() {
+
+ $this->httpRequest->expects( $this->once() )
+ ->method( 'ping' )
+ ->will( $this->returnValue( true ) );
+
+ $this->httpRequest->expects( $this->once() )
+ ->method( 'execute' )
+ ->will( $this->returnValue( 'Foobar' ) );
+
+ $this->httpRequest->expects( $this->once() )
+ ->method( 'getLastError' )
+ ->will( $this->returnValue( '' ) );
+
+ $parameters = [
+ 'url' => 'http://example.org/Foo'
+ ];
+
+ $instance = new RemoteRequest(
+ $parameters,
+ $this->httpRequest
+ );
+
+ $instance->clear();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Result\StringResult',
+ $instance->getQueryResult( $this->query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/CachedQueryResultPrefetcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/CachedQueryResultPrefetcherTest.php
new file mode 100644
index 00000000..5c6fb56d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/CachedQueryResultPrefetcherTest.php
@@ -0,0 +1,349 @@
+<?php
+
+namespace SMW\Tests\Query\Result;
+
+use Onoi\BlobStore\BlobStore;
+use Onoi\BlobStore\Container;
+use SMW\DIWikiPage;
+use SMW\Query\Result\CachedQueryResultPrefetcher;
+use SMW\Utils\BufferedStatsdCollector;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Query\Result\CachedQueryResultPrefetcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class CachedQueryResultPrefetcherTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $queryFactory;
+ private $blobStore;
+ private $bufferedStatsdCollector;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->queryFactory = $this->getMockBuilder( '\SMW\QueryFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore = $this->getMockBuilder( BlobStore::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->container = $this->getMockBuilder( Container::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->bufferedStatsdCollector = $this->getMockBuilder( BufferedStatsdCollector::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CachedQueryResultPrefetcher::class,
+ new CachedQueryResultPrefetcher( $this->store, $this->queryFactory, $this->blobStore, $this->bufferedStatsdCollector )
+ );
+ }
+
+ public function testGetQueryResultForEmptyQuery() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryEngine = $this->getMockBuilder( '\SMW\QueryEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryEngine->expects( $this->once() )
+ ->method( 'getQueryResult' )
+ ->with($this->identicalTo( $query ) );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->setQueryEngine( $queryEngine );
+
+ $instance->getQueryResult( $query );
+ }
+
+ public function testGetQueryResultFromTempCache() {
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $this->container ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getQueryId' )
+ ->will( $this->returnValue( __METHOD__ ) );
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 100 ) );
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $queryEngine = $this->getMockBuilder( '\SMW\QueryEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryEngine->expects( $this->once() )
+ ->method( 'getQueryResult' )
+ ->with($this->identicalTo( $query ) );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->setQueryEngine( $queryEngine );
+
+ $instance->getQueryResult( $query );
+
+ // Second time called from tempCache
+ $instance->getQueryResult( $query );
+ }
+
+ public function testPurgeCacheByQueryList() {
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'delete' );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->resetCacheBy( [ 'Foo' ] );
+ }
+
+ public function testNoCache() {
+
+ $this->blobStore->expects( $this->never() )
+ ->method( 'read' );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 100 ) );
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->at( 2 ) )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( $query::NO_CACHE ) )
+ ->will( $this->returnValue( true ) );
+
+ $queryEngine = $this->getMockBuilder( '\SMW\QueryEngine' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->setQueryEngine( $queryEngine );
+ $instance->getQueryResult( $query );
+ }
+
+ public function testMissingQueryEngineThrowsException() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getQueryResult( $query );
+ }
+
+ public function testPurgeCacheBySubject() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->with( $this->equalTo( '1d1e1d94a78b9476c8213a16febe2c9b' ) );
+
+ $this->bufferedStatsdCollector->expects( $this->once() )
+ ->method( 'recordStats' );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->resetCacheBy( $subject );
+ }
+
+ public function testPurgeCacheBySubjectWithDependantHashIdExtension() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->with( $this->equalTo( '1e5509cfde15f1f569db295e845ce997' ) );
+
+ $this->bufferedStatsdCollector->expects( $this->once() )
+ ->method( 'recordStats' );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->setDependantHashIdExtension( 'foo' );
+ $instance->resetCacheBy( $subject );
+ }
+
+ public function testPurgeCacheBySubjectWith_QUERY() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->atLeastOnce() )
+ ->method( 'getSubobjectName' )
+ ->will( $this->returnValue( '_QUERYfoo' ) );
+
+ $subject->expects( $this->never() )
+ ->method( 'asBase' );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->with( $this->equalTo( 'dc63f8b4cab1bb1214979932b637cdec' ) );
+
+ $this->bufferedStatsdCollector->expects( $this->once() )
+ ->method( 'recordStats' );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $instance->resetCacheBy( $subject );
+ }
+
+ public function testGetStats() {
+
+ $stats = [
+ 'misses' => 1,
+ 'hits' => [ 'Foo' => 2, [ 'Bar' => 2 ] ],
+ 'meta' => 'foo'
+ ];
+
+ $this->bufferedStatsdCollector->expects( $this->once() )
+ ->method( 'getStats' )
+ ->will( $this->returnValue( $stats ) );
+
+ $instance = new CachedQueryResultPrefetcher(
+ $this->store,
+ $this->queryFactory,
+ $this->blobStore,
+ $this->bufferedStatsdCollector
+ );
+
+ $stats = $instance->getStats();
+
+ $this->assertInternalType(
+ 'array',
+ $stats
+ );
+
+ $this->assertEquals(
+ [
+ 'hit' => 0.8,
+ 'miss' => 0.2
+ ],
+ $stats['ratio']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResolverJournalTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResolverJournalTest.php
new file mode 100644
index 00000000..53b64398
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResolverJournalTest.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests\Query\Result;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Result\ResolverJournal;
+
+/**
+ * @covers \SMW\Query\Result\ResolverJournal
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class ResolverJournalTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ResolverJournal::class,
+ new ResolverJournal()
+ );
+ }
+
+ public function testRecordItem() {
+
+ $dataItem = DIWikiPage::newFromText( 'Foo' );
+ $instance = new ResolverJournal();
+
+ $instance->prune();
+ $instance->recordItem( $dataItem );
+
+ $this->assertEquals(
+ [ 'Foo#0##' => $dataItem ],
+ $instance->getEntityList( 'FOO:123' )
+ );
+
+ $instance->prune();
+
+ $this->assertEmpty(
+ $instance->getEntityList()
+ );
+ }
+
+ public function testRecordProperty() {
+
+ $property = new DIProperty( 'Bar' );
+ $instance = new ResolverJournal();
+
+ $instance->recordProperty( $property );
+
+ $this->assertEquals(
+ [
+ 'Bar' => $property
+ ],
+ $instance->getPropertyList()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResultFieldMatchFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResultFieldMatchFinderTest.php
new file mode 100644
index 00000000..4d44f835
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/ResultFieldMatchFinderTest.php
@@ -0,0 +1,373 @@
+<?php
+
+namespace SMW\Tests\Query\Result;
+
+use SMW\DataItemFactory;
+use SMW\DataValueFactory;
+use SMW\Query\PrintRequest;
+use SMW\Query\Result\ResultFieldMatchFinder;
+
+/**
+ * @covers SMW\Query\Result\ResultFieldMatchFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ResultFieldMatchFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $dataValueFactory;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->dataItemFactory = new DataItemFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\Query\Result\ResultFieldMatchFinder',
+ new ResultFieldMatchFinder( $store, $printRequest )
+ );
+ }
+
+ public function testGetRequestOptions() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\RequestOptions',
+ $instance->getRequestOptions()
+ );
+ }
+
+ public function testFindAndMatch_THIS() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_THIS ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $dataItem ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatch_CATS() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo' );
+ $expected = $this->dataItemFactory->newDIWikiPage( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_INST' ) ) )
+ ->will( $this->returnValue( [ $expected ] ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 1 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_CATS ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatch_CCAT() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar' );
+ $expected = $this->dataItemFactory->newDIWikiPage( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_INST' ) ) )
+ ->will( $this->returnValue( [ $expected ] ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 2 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_CCAT ) )
+ ->will( $this->returnValue( true ) );
+
+ $printRequest->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( $expected ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $this->dataItemFactory->newDIBoolean( true ) ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatch_PROP() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar' );
+ $expected = $this->dataItemFactory->newDIWikiPage( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( 'Prop' ) ) )
+ ->will( $this->returnValue( [ $expected ] ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 3 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_PROP ) )
+ ->will( $this->returnValue( true ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( false ) );
+
+ $printRequest->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( $this->dataValueFactory->newPropertyValueByLabel( 'Prop' ) ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatchWithIteratorAsValueResultOnPRINT_PROP() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar' );
+ $expected = $this->dataItemFactory->newDIWikiPage( __METHOD__ );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ // #2541, return an iterator
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( 'Prop' ) ) )
+ ->will( $this->returnValue( new \ArrayIterator( [ $expected ] ) ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 3 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_PROP ) )
+ ->will( $this->returnValue( true ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( false ) );
+
+ $printRequest->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue(
+ $this->dataValueFactory->newPropertyValueByLabel( 'Prop' ) ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatchWithBlobValueResultAndRemovedLink() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar' );
+ $text = $this->dataItemFactory->newDIBlob( '[[Foo::bar]]' );
+ $expected = $this->dataItemFactory->newDIBlob( 'bar' );
+
+ $propertyValue = $this->dataValueFactory->newPropertyValueByLabel( 'Prop' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ // #2541, return an iterator
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( 'Prop' ) ) )
+ ->will( $this->returnValue( new \ArrayIterator( [ $text ] ) ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 3 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_PROP ) )
+ ->will( $this->returnValue( true ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( false ) );
+
+ $printRequest->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( $propertyValue ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+ public function testFindAndMatchWithBlobValueResultAndRetainedLink() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Bar' );
+ $text = $this->dataItemFactory->newDIBlob( '[[Foo::bar]]' );
+ $expected = $this->dataItemFactory->newDIBlob( '[[Foo::bar]]' );
+
+ $propertyValue = $this->dataValueFactory->newPropertyValueByLabel( 'Prop' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ // #2541, return an iterator
+ $store->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $dataItem ),
+ $this->equalTo( $this->dataItemFactory->newDIProperty( 'Prop' ) ) )
+ ->will( $this->returnValue( new \ArrayIterator( [ $text ] ) ) );
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->at( 3 ) )
+ ->method( 'isMode' )
+ ->with($this->equalTo( PrintRequest::PRINT_PROP ) )
+ ->will( $this->returnValue( true ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getOutputFormat' )
+ ->will( $this->returnValue( '-raw' ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( false ) );
+
+ $printRequest->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( $propertyValue ) );
+
+ $instance = new ResultFieldMatchFinder(
+ $store,
+ $printRequest
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findAndMatch( $dataItem )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/StringResultTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/StringResultTest.php
new file mode 100644
index 00000000..092b90b8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/Result/StringResultTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Query\Result;
+
+use SMW\Query\Result\StringResult;
+
+/**
+ * @covers \SMW\Query\Result\StringResult
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class StringResultTest extends \PHPUnit_Framework_TestCase {
+
+ private $query;
+
+ protected function setUp() {
+
+ $this->query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ StringResult::class,
+ new StringResult( '', $this->query )
+ );
+ }
+
+ public function testGetResult() {
+
+ $instance = new StringResult( 'Foobar', $this->query );
+
+ $this->assertEquals(
+ 'Foobar',
+ $instance->getResults()
+ );
+ }
+
+ public function testGetResult_PreOutputCallback() {
+
+ $instance = new StringResult( 'Foobar', $this->query );
+
+ $instance->setPreOutputCallback( function( $result, $options ) {
+ return $result . ' Foo bar';
+ } );
+
+ $this->assertEquals(
+ 'Foobar Foo bar',
+ $instance->getResults()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CategoryResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CategoryResultPrinterTest.php
new file mode 100644
index 00000000..c16262e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CategoryResultPrinterTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Query\ResultPrinters\CategoryResultPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\CategoryResultPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CategoryResultPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CategoryResultPrinter::class,
+ new CategoryResultPrinter( 'category' )
+ );
+ }
+
+ public function testGetResult_Empty() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CategoryResultPrinter( 'category' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CsvFileExportPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CsvFileExportPrinterTest.php
new file mode 100644
index 00000000..564e5334
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/CsvFileExportPrinterTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Query\ResultPrinters\CsvFileExportPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\CsvFileExportPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CsvFileExportPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CsvFileExportPrinter::class,
+ new CsvFileExportPrinter( 'csv' )
+ );
+ }
+
+ public function testGetResult_Empty() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CsvFileExportPrinter( 'csv' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI )
+ );
+ }
+
+ public function testLink() {
+
+ $link = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getQueryLink' )
+ ->will( $this->returnValue( $link ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getCount' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CsvFileExportPrinter( 'csv' );
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/FeedExportPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/FeedExportPrinterTest.php
new file mode 100644
index 00000000..d8a1fe3f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/FeedExportPrinterTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use ReflectionClass;
+use SMW\Query\ResultPrinters\FeedExportPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\FeedExportPrinter
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class FeedExportPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FeedExportPrinter::class,
+ new FeedExportPrinter( 'feed' )
+ );
+ }
+
+ /**
+ * @dataProvider textDataProvider
+ */
+ public function testFeedItemDescription( $setup, $expected, $message ) {
+
+ $instance = new FeedExportPrinter( 'feed' );
+
+ $reflector = new ReflectionClass( '\SMW\Query\ResultPrinters\FeedExportPrinter' );
+ $method = $reflector->getMethod( 'feedItemDescription' );
+ $method->setAccessible( true );
+
+ $this->assertEquals(
+ $expected['text'],
+ $method->invoke( $instance, $setup['items'], $setup['pageContent'] )
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function textDataProvider() {
+
+ $provider = [];
+
+ // #0
+ // https://web.archive.org/web/20160802052417/http://www.utexas.edu/learn/html/spchar.html
+ $provider[] = [
+ [
+ 'items' => [],
+ 'pageContent' => 'Semantic MediaWiki Conference, have been announced: it will be held at' .
+ '[http://www.aohostels.com/en/tagungen/tagungen-berlin/ A&O Berlin Hauptbahnhof]' .
+ '&¢©«»—¡¿,åÃãÆç'
+ ],
+ [
+ 'text' => 'Semantic MediaWiki Conference, have been announced: it will be held at' .
+ '[http://www.aohostels.com/en/tagungen/tagungen-berlin/ A&O Berlin Hauptbahnhof]' .
+ '&¢©«»—¡¿,åÃãÆç'
+ ],
+ [ 'info' => 'text encoding including html special characters' ]
+ ];
+
+ return $provider;
+
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryTest.php
new file mode 100644
index 00000000..15a9d807
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinter/ParameterDictionaryTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters\ListResultPrinter;
+
+use SMW\Query\ResultPrinters\ListResultPrinter\ParameterDictionary;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\ListResultPrinter\ParameterDictionary
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class ParameterDictionaryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testSetGet() {
+
+ $dict = new ParameterDictionary();
+
+ $dict->set( 'foo', 'Derek' );
+ $dict->set( 'bar', 'Devin' );
+ $dict->set( 'foo', 'Chelsea' );
+
+ $this->assertEquals( 'Chelsea', $dict->get( 'foo' ) );
+ $this->assertEquals( 'Devin', $dict->get( 'bar' ) );
+ }
+
+ public function testSetArrayGet() {
+
+ $dict = new ParameterDictionary();
+
+ $dict->set( ['foo' => 'Derek', 'bar' => 'Devin' ] );
+ $dict->set( ['foo' => 'Chelsea', 'baz' => 'Carolynn' ] );
+
+ $this->assertEquals( 'Chelsea', $dict->get( 'foo' ) );
+ $this->assertEquals( 'Devin', $dict->get( 'bar' ) );
+ $this->assertEquals( 'Carolynn', $dict->get( 'baz' ) );
+ }
+
+ public function testSetDefaultGet() {
+
+ $dict = new ParameterDictionary();
+
+ $dict->setDefault( 'foo', 'Derek' );
+ $dict->setDefault( 'bar', 'Devin' );
+ $dict->setDefault( 'foo', 'Chelsea' );
+
+ $this->assertEquals( 'Derek', $dict->get( 'foo' ) );
+ $this->assertEquals( 'Devin', $dict->get( 'bar' ) );
+ }
+
+ public function testSetDefaultArrayGet() {
+
+ $dict = new ParameterDictionary();
+
+ $dict->set( ['foo' => 'Derek', 'bar' => 'Devin' ] );
+ $dict->setDefault( ['foo' => 'Chelsea', 'baz' => 'Carolynn' ] );
+
+ $this->assertEquals( 'Derek', $dict->get( 'foo' ) );
+ $this->assertEquals( 'Devin', $dict->get( 'bar' ) );
+ $this->assertEquals( 'Carolynn', $dict->get( 'baz' ) );
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinterTest.php
new file mode 100644
index 00000000..1c2393ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/ListResultPrinterTest.php
@@ -0,0 +1,66 @@
+<?php
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Message;
+use SMW\Query\ResultPrinters\ListResultPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\ListResultPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Máté Szabó
+ * @author Stephan Gambke
+ */
+class ListResultPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider allFormatsProvider
+ * @param string $format
+ */
+ public function testCanConstruct( $format ) {
+ $listResultPrinter = new ListResultPrinter( $format );
+
+ $this->assertInstanceOf( '\SMW\Query\ResultPrinters\ListResultPrinter', $listResultPrinter );
+ }
+
+ /**
+ * @dataProvider allFormatsProvider
+ * @param string $format
+ */
+ public function testSupportsRecursiveAnnotation( $format ) {
+ $listResultPrinter = new ListResultPrinter( $format );
+
+ $this->assertTrue( $listResultPrinter->supportsRecursiveAnnotation() );
+ }
+
+ /**
+ * @dataProvider allFormatsProvider
+ * @param string $format
+ */
+ public function testIsDeferrable( $format ) {
+ $listResultPrinter = new ListResultPrinter( $format );
+
+ $this->assertTrue( $listResultPrinter->isDeferrable() );
+ }
+
+ /**
+ * @dataProvider allFormatsProvider
+ * @param string $format
+ */
+ public function testGetName( $format ) {
+ $listResultPrinter = new ListResultPrinter( $format );
+
+ $this->assertEquals( Message::get( 'smw_printername_' . $format ), $listResultPrinter->getName() );
+ }
+
+ public function allFormatsProvider() {
+ yield [ 'ul' ];
+ yield [ 'ol' ];
+ yield [ 'template' ];
+ yield [ 'list' ];
+ yield [ 'plainlist' ];
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/NullResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/NullResultPrinterTest.php
new file mode 100644
index 00000000..93126bd0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/NullResultPrinterTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Query\ResultPrinters\NullResultPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\NullResultPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NullResultPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ NullResultPrinter::class,
+ new NullResultPrinter( '' )
+ );
+ }
+
+ public function testGetResult_Empty() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new NullResultPrinter( '' );
+
+ $this->assertEmpty(
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TableResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TableResultPrinterTest.php
new file mode 100644
index 00000000..7ae4026c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TableResultPrinterTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Query\ResultPrinters\TableResultPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\TableResultPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TableResultPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TableResultPrinter::class,
+ new TableResultPrinter( 'table' )
+ );
+ }
+
+ public function testGetResult_Empty() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new TableResultPrinter( 'table' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TemplateFileExportPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TemplateFileExportPrinterTest.php
new file mode 100644
index 00000000..6c700cca
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ResultPrinters/TemplateFileExportPrinterTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace SMW\Tests\Query\ResultPrinters;
+
+use SMW\Query\ResultPrinters\TemplateFileExportPrinter;
+
+/**
+ * @covers \SMW\Query\ResultPrinters\TemplateFileExportPrinter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TemplateFileExportPrinterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TemplateFileExportPrinter::class,
+ new TemplateFileExportPrinter( 'templatefile' )
+ );
+ }
+
+ public function testGetResult_Empty() {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new TemplateFileExportPrinter( 'templatefile' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI )
+ );
+ }
+
+ public function testLink() {
+
+ $link = $this->getMockBuilder( '\SMWInfolink' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getQueryLink' )
+ ->will( $this->returnValue( $link ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getCount' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new TemplateFileExportPrinter( 'templatefile' );
+ $instance->getResult( $queryResult, [], SMW_OUTPUT_WIKI );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ScoreSetTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ScoreSetTest.php
new file mode 100644
index 00000000..32b78908
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Query/ScoreSetTest.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace SMW\Tests\Query;
+
+use SMW\DIWikiPage;
+use SMW\Query\ScoreSet;
+
+/**
+ * @covers \SMW\Query\ScoreSet
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ScoreSetTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ScoreSet::class,
+ new ScoreSet()
+ );
+ }
+
+ public function testAddScore() {
+
+ $instance = new ScoreSet();
+
+ $instance->addScore( 'Foo', 0.1 );
+
+ $this->assertEquals(
+ 0.1,
+ $instance->getScore( 'Foo' )
+ );
+
+ $this->assertFalse(
+ $instance->getScore( 'Bar' )
+ );
+ }
+
+ public function testAddScore_DIWikiPage() {
+
+ $dataItem = DIWikiPage::newFromText( 'Bar' );
+ $instance = new ScoreSet();
+
+ $instance->addScore( $dataItem, 10 );
+
+ $this->assertEquals(
+ 10,
+ $instance->getScore( $dataItem )
+ );
+ }
+
+ public function testGetScores() {
+
+ $instance = new ScoreSet();
+
+ $instance->addScore( 'Foo', 42 );
+ $instance->addScore( 'Bar', 1001 );
+
+ $this->assertEquals(
+ [
+ [ 'Foo', 42 ],
+ [ 'Bar', 1001 ]
+ ],
+ $instance->getScores()
+ );
+ }
+
+ public function testAsTable() {
+
+ $instance = new ScoreSet();
+
+ $instance->addScore( 'Foo', 42 );
+ $instance->addScore( 'Bar', 1001, 5 );
+ $instance->addScore( 'Foobar', 1 );
+
+ $table = $instance->asTable();
+
+ $this->assertContains(
+ '<tr><td>42</td><td>Foo</td><td>0</td></tr>',
+ $table
+ );
+
+ $this->assertContains(
+ '<tr><td>1001</td><td>Bar</td><td>5</td></tr>',
+ $table
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/QueryFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/QueryFactoryTest.php
new file mode 100644
index 00000000..de739997
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/QueryFactoryTest.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\QueryFactory;
+use SMW\StringCondition;
+
+/**
+ * @covers \SMW\QueryFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\QueryFactory',
+ new QueryFactory()
+ );
+ }
+
+ public function testCanConstructProfileAnnotatorFactory() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ProfileAnnotatorFactory',
+ $instance->newProfileAnnotatorFactory()
+ );
+ }
+
+ public function testCanConstructQuery() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ $instance->newQuery( $description )
+ );
+ }
+
+ public function testCanConstructDescriptionFactory() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\DescriptionFactory',
+ $instance->newDescriptionFactory()
+ );
+ }
+
+ public function testCanConstructPrintRequestFactory() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\PrintRequestFactory',
+ $instance->newPrintRequestFactory()
+ );
+ }
+
+ public function testCanConstructRequestOptions() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\RequestOptions',
+ $instance->newRequestOptions()
+ );
+ }
+
+ public function testCanConstructStringCondition() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\StringCondition',
+ $instance->newStringCondition( '', StringCondition::STRCOND_PRE )
+ );
+ }
+
+ public function testCanConstructQueryParser() {
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Query\Parser',
+ $instance->newQueryParser()
+ );
+ }
+
+ public function testCanConstructQueryResult() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new QueryFactory();
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->newQueryResult( $store, $query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/RequestOptionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/RequestOptionsTest.php
new file mode 100644
index 00000000..f1b01c9d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/RequestOptionsTest.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\RequestOptions;
+use SMW\StringCondition;
+
+/**
+ * @covers \SMW\RequestOptions
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class RequestOptionsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RequestOptions::class,
+ new RequestOptions()
+ );
+ }
+
+ public function testAddStringCondition() {
+
+ $instance = new RequestOptions();
+ $instance->addStringCondition( 'Foo', StringCondition::STRCOND_PRE );
+
+ foreach ( $instance->getStringConditions() as $stringCondition ) {
+ $this->assertInstanceOf(
+ '\SMW\StringCondition',
+ $stringCondition
+ );
+
+ $this->assertFalse(
+ $stringCondition->isOr
+ );
+ }
+
+ $this->assertEquals(
+ '[-1,0,false,true,null,true,"Foo#0##",[],[]]',
+ $instance->getHash()
+ );
+ }
+
+ public function testEddExtraCondition() {
+
+ $instance = new RequestOptions();
+ $instance->addExtraCondition( 'Foo' );
+ $instance->addExtraCondition( [ 'Bar' => 'Foobar' ] );
+
+ $this->assertEquals(
+ [
+ 'Foo',
+ [ 'Bar' => 'Foobar' ]
+ ],
+ $instance->getExtraConditions()
+ );
+
+ $this->assertEquals(
+ '[-1,0,false,true,null,true,"",["Foo",{"Bar":"Foobar"}],[]]',
+ $instance->getHash()
+ );
+ }
+
+ /**
+ * @dataProvider numberProvider
+ */
+ public function testLimit( $limit, $expected ) {
+
+ $instance = new RequestOptions();
+ $instance->setLimit( $limit );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getLimit()
+ );
+
+ $instance->limit = $limit;
+
+ $this->assertEquals(
+ $expected,
+ $instance->getLimit()
+ );
+ }
+
+ /**
+ * @dataProvider numberProvider
+ */
+ public function testOffset( $offset, $expected ) {
+
+ $instance = new RequestOptions();
+ $instance->setOffset( $offset );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getOffset()
+ );
+
+ $instance->offset = $offset;
+
+ $this->assertEquals(
+ $expected,
+ $instance->getOffset()
+ );
+ }
+
+ public function numberProvider() {
+
+ $provider[] = [
+ 42,
+ 42
+ ];
+
+ $provider[] = [
+ '42foo',
+ 42
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Rule/RuleTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Rule/RuleTest.php
new file mode 100644
index 00000000..eacef617
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Rule/RuleTest.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace SMW\Tests\Rule;
+
+use SMW\Rule\Rule;
+
+/**
+ * @covers \SMW\Rule\Rule
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class RuleTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new Rule( 'foo', [], [] );
+
+ $this->assertInstanceof(
+ Rule::class,
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/BadHttpEndpointResponseExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/BadHttpEndpointResponseExceptionTest.php
new file mode 100644
index 00000000..54355886
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/BadHttpEndpointResponseExceptionTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\Exception;
+
+use SMW\SPARQLStore\Exception\BadHttpEndpointResponseException;
+
+/**
+ * @covers \SMW\SPARQLStore\Exception\BadHttpEndpointResponseException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class BadHttpEndpointResponseExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\Exception\BadHttpEndpointResponseException',
+ new BadHttpEndpointResponseException( 'Foo', 'Bar', 'Que' )
+ );
+ }
+
+ /**
+ * @dataProvider errorCodeProvider
+ */
+ public function testErrorCodes( $errorCode ) {
+
+ $instance = new BadHttpEndpointResponseException( $errorCode, '', '' );
+ $this->assertEquals( $errorCode, $instance->getCode() );
+ }
+
+ public function errorCodeProvider() {
+
+ $provider = [
+ [ BadHttpEndpointResponseException::ERROR_MALFORMED ],
+ [ BadHttpEndpointResponseException::ERROR_REFUSED ],
+ [ BadHttpEndpointResponseException::ERROR_GRAPH_NOEXISTS ],
+ [ BadHttpEndpointResponseException::ERROR_GRAPH_EXISTS ],
+ [ BadHttpEndpointResponseException::ERROR_OTHER ],
+ [ BadHttpEndpointResponseException::ERROR_NOSERVICE ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/HttpEndpointConnectionExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/HttpEndpointConnectionExceptionTest.php
new file mode 100644
index 00000000..c0843f9e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/Exception/HttpEndpointConnectionExceptionTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\Exception;
+
+use SMW\SPARQLStore\Exception\HttpEndpointConnectionException;
+
+/**
+ * @covers \SMW\SPARQLStore\Exception\HttpEndpointConnectionException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class HttpEndpointConnectionExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HttpEndpointConnectionException::class,
+ new HttpEndpointConnectionException( 'Foo', 'Bar', 'Que' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/HttpResponseErrorMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/HttpResponseErrorMapperTest.php
new file mode 100644
index 00000000..7467e523
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/HttpResponseErrorMapperTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\SPARQLStore\HttpResponseErrorMapper;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\HttpResponseErrorMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class HttpResponseErrorMapperTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ HttpResponseErrorMapper::class,
+ new HttpResponseErrorMapper( $httpRequest )
+ );
+ }
+
+ /**
+ * @dataProvider curlErrorCodeThatNotThrowsExceptionProvider
+ */
+ public function testResponseToHttpRequestThatNotThrowsException( $curlErrorCode ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( $curlErrorCode ) );
+
+ $instance = new HttpResponseErrorMapper( $httpRequest );
+ $instance->mapErrorResponse( 'Foo', 'Bar' );
+ }
+
+ public function testResponseToHttpRequestForInvalidErrorCodeThrowsException() {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 99999 ) );
+
+ $instance = new HttpResponseErrorMapper( $httpRequest );
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\HttpEndpointConnectionException' );
+ $instance->mapErrorResponse( 'Foo', 'Bar' );
+ }
+
+ /**
+ * @dataProvider httpCodeThatThrowsExceptionProvider
+ */
+ public function testResponseToHttpRequesForHttpErrorThatThrowsException( $httpErrorCode ) {
+
+ // PHP doesn't know CURLE_HTTP_RETURNED_ERROR therefore using 22
+ // http://curl.haxx.se/libcurl/c/libcurl-errors.html
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 22 ) );
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastTransferInfo' )
+ ->with( $this->equalTo( CURLINFO_HTTP_CODE ) )
+ ->will( $this->returnValue( $httpErrorCode ) );
+
+ $instance = new HttpResponseErrorMapper( $httpRequest );
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\BadHttpEndpointResponseException' );
+ $instance->mapErrorResponse( 'Foo', 'Bar' );
+ }
+
+ public function testResponseToHttpRequesForHttpErrorThatNotThrowsException() {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 22 ) );
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastTransferInfo' )
+ ->with( $this->equalTo( CURLINFO_HTTP_CODE ) )
+ ->will( $this->returnValue( 404 ) );
+
+ $instance = new HttpResponseErrorMapper( $httpRequest );
+ $instance->mapErrorResponse( 'Foo', 'Bar' );
+ }
+
+ public function curlErrorCodeThatNotThrowsExceptionProvider() {
+
+ $provider = [
+ [ CURLE_GOT_NOTHING ],
+ [ CURLE_COULDNT_CONNECT ]
+ ];
+
+ return $provider;
+ }
+
+ public function httpCodeThatThrowsExceptionProvider() {
+
+ $provider = [
+ [ 400 ],
+ [ 500 ]
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FalseConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FalseConditionTest.php
new file mode 100644
index 00000000..95782d04
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FalseConditionTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\Condition;
+
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\Condition\FalseCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FalseConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\SPARQLStore\QueryEngine\Condition\FalseCondition',
+ new FalseCondition()
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $instance = new FalseCondition();
+
+ $this->assertNotEmpty(
+ $instance->getCondition()
+ );
+
+ $this->assertTrue(
+ $instance->isSafe()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FilterConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FilterConditionTest.php
new file mode 100644
index 00000000..18a8422e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/FilterConditionTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\Condition;
+
+use SMW\SPARQLStore\QueryEngine\Condition\FilterCondition;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\Condition\FilterCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FilterConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\SPARQLStore\QueryEngine\Condition\FilterCondition',
+ new FilterCondition( 'condition' )
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $instance = new FilterCondition( 'filter' );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getCondition()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->namespaces
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getWeakConditionString()
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->isSafe()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/SingletonConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/SingletonConditionTest.php
new file mode 100644
index 00000000..016bc384
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/SingletonConditionTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\Condition;
+
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SingletonConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $expElement = $this->getMockBuilder( '\SMWExpElement' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition',
+ new SingletonCondition( $expElement )
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $expElement = $this->getMockBuilder( '\SMWExpElement' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SingletonCondition( $expElement );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getCondition()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getWeakConditionString()
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->isSafe()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/TrueConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/TrueConditionTest.php
new file mode 100644
index 00000000..a404712e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/TrueConditionTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\Condition;
+
+use SMW\SPARQLStore\QueryEngine\Condition\TrueCondition;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\Condition\TrueCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TrueConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\SPARQLStore\QueryEngine\Condition\TrueCondition',
+ new TrueCondition()
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $instance = new TrueCondition();
+
+ $this->assertEmpty(
+ $instance->getCondition()
+ );
+
+ $this->assertFalse(
+ $instance->isSafe()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/WhereConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/WhereConditionTest.php
new file mode 100644
index 00000000..c1d5cdac
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/Condition/WhereConditionTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\Condition;
+
+use SMW\SPARQLStore\QueryEngine\Condition\WhereCondition;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\Condition\WhereCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class WhereConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ 'SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ new WhereCondition( 'condition', true )
+ );
+ }
+
+ public function testCommonMethods() {
+
+ $instance = new WhereCondition( 'condition', true );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getCondition()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->namespaces
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getWeakConditionString()
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->isSafe()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/ConditionBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/ConditionBuilderTest.php
new file mode 100644
index 00000000..982ef551
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/ConditionBuilderTest.php
@@ -0,0 +1,923 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+use SMWDITime as DITime;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\ConditionBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ConditionBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $stringBuilder;
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ConditionBuilder::class,
+ new ConditionBuilder( $this->descriptionInterpreterFactory )
+ );
+ }
+
+ public function testQueryForSingleProperty() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQuerySomeProperty_ForKnownSortPropertyKey() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance
+ ->setSortKeys( [ 'Foo' => 'DESC' ] )
+ ->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( '{ ?v1 swivt:wikiPageSortKey ?v1sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQuerySomeProperty_ForUnknownSortPropertyKey() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance
+ ->setSortKeys( [ 'Bar' => 'DESC' ] )
+ ->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Bar ?v2 .' )->addNewLine()
+ ->addString( '{ ?v2 swivt:wikiPageSortKey ?v2sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQuerySomeProperty_ForEmptySortPropertyKey() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance
+ ->setSortKeys( [ '' => 'DESC' ] )
+ ->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQuerySomeProperty_OnInvalidSortKeyThrowsException() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $instance->setSortKeys( [ 'Foo', 'ASC' ] );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConditionFrom( $description );
+ }
+
+ public function testQueryForSinglePropertyWithValue() {
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '"SomePropertyValue" swivt:page ?url .' )->addNewLine()
+ ->addString( ' OPTIONAL { "SomePropertyValue" swivt:redirectsTo ?o1 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o1 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForSomePropertyWithValue() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIBlob( 'SomePropertyBlobValue' ) )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo "SomePropertyBlobValue" .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForSinglePageTypePropertyWithValueComparator() {
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIWikiPage( 'SomePropertyPageValue', NS_MAIN ), null, SMW_CMP_LEQ )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1sk <= "SomePropertyPageValue" )' )->addNewLine()
+ ->addString( '?v1 swivt:wikiPageSortKey ?v1sk .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForSingleBlobTypePropertyWithNotLikeComparator() {
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIBlob( 'SomePropertyBlobValue' ), null, SMW_CMP_NLKE )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( !regex( ?v1, "^SomePropertyBlobValue$", "s") )' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForSingleCategory() {
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY, '' );
+
+ $categoryName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $category )
+ );
+
+ $description = new ClassDescription(
+ $category
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( "{ ?result rdf:type $categoryName . }" )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o1 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o1 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForSingleNamespace() {
+
+ $description = new NamespaceDescription( NS_HELP );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $this->assertSame( 12, NS_HELP );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '{ ?result swivt:wikiNamespace "12"^^xsd:integer . }' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o1 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o1 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForPropertyConjunction() {
+
+ $conjunction = new Conjunction( [
+ new SomeProperty(
+ new DIProperty( 'Foo' ), new ValueDescription( new DIBlob( 'SomePropertyValue' ) ) ),
+ new SomeProperty(
+ new DIProperty( 'Bar' ), new ThingDescription() ),
+ ] );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $conjunction );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo "SomePropertyValue" .' )->addNewLine()
+ ->addString( '?result property:Bar ?v2 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForPropertyConjunctionWithGreaterLessEqualFilter() {
+
+ $conjunction = new Conjunction( [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( new DINumber( 1 ), null, SMW_CMP_GEQ ) ),
+ new SomeProperty(
+ new DIProperty( 'Bar' ),
+ new ValueDescription( new DINumber( 9 ), null, SMW_CMP_LEQ ) ),
+ ] );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $conjunction );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 >= "1"^^xsd:double )' )->addNewLine()
+ ->addString( '?result property:Bar ?v2 .' )->addNewLine()
+ ->addString( 'FILTER( ?v2 <= "9"^^xsd:double )' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForPropertyDisjunction() {
+
+ $conjunction = new Disjunction( [
+ new SomeProperty( new DIProperty( 'Foo' ), new ThingDescription() ),
+ new SomeProperty( new DIProperty( 'Bar' ), new ThingDescription() )
+ ] );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $conjunction );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '{' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( '} UNION {' )->addNewLine()
+ ->addString( '?result property:Bar ?v2 .' )->addNewLine()
+ ->addString( '}' )
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testQueryForPropertyDisjunctionWithLikeNotLikeFilter() {
+
+ $conjunction = new Disjunction( [
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( new DIBlob( "AA*" ), null, SMW_CMP_LIKE ) ),
+ new SomeProperty(
+ new DIProperty( 'Bar' ),
+ new ValueDescription( new DIBlob( "BB?" ), null, SMW_CMP_NLKE ) )
+ ] );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $conjunction );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '{' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( regex( ?v1, "^AA.*$", "s") )' )->addNewLine()
+ ->addString( '} UNION {' )->addNewLine()
+ ->addString( '?result property:Bar ?v2 .' )->addNewLine()
+ ->addString( 'FILTER( !regex( ?v2, "^BB.$", "s") )' )->addNewLine()
+ ->addString( '}' )
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testSingleDatePropertyWithGreaterEqualConstraint() {
+
+ $property = new DIProperty( 'SomeDateProperty' );
+ $property->setPropertyTypeId( '_dat' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DITime( 1, 1970, 01, 01, 1, 1 ), null, SMW_CMP_GEQ )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:SomeDateProperty-23aux ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 >= "2440587.5423611"^^xsd:double )' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testSingleSubobjectBuildAsAuxiliaryProperty() {
+
+ $property = new DIProperty( '_SOBJ' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Has_subobject ?v1 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ /**
+ * '[[HasSomeProperty::Foo||Bar]]'
+ */
+ public function testSubqueryDisjunction() {
+
+ $property = new DIProperty( 'HasSomeProperty' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $disjunction = new Disjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ), $property ),
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ), $property )
+ ] );
+
+ $description = new SomeProperty(
+ $property,
+ $disjunction
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:HasSomeProperty ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 = wiki:Foo || ?v1 = wiki:Bar )' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ /**
+ * '[[Born in::<q>[[Category:City]] [[Located in::Outback]]</q>]]'
+ */
+ public function testNestedPropertyConjunction() {
+
+ $property = DIProperty::newFromUserLabel( 'Born in' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $category = new DIWikiPage( 'City', NS_CATEGORY );
+
+ $categoryName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $category )
+ );
+
+ $conjunction = new Conjunction( [
+ new ClassDescription( $category ),
+ new SomeProperty(
+ DIProperty::newFromUserLabel( 'Located in' ),
+ new ValueDescription(
+ new DIWikiPage( 'Outback', NS_MAIN ),
+ DIProperty::newFromUserLabel( 'Located in' ) )
+ )
+ ]
+ );
+
+ $description = new SomeProperty(
+ $property,
+ $conjunction
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:Born_in ?v1 .' )->addNewLine()
+ ->addString( '{ ' )
+ ->addString( "{ ?v1 rdf:type $categoryName . }" )->addNewLine()
+ ->addString( '?v1 property:Located_in wiki:Outback .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ /**
+ * '[[LocatedIn.MemberOf::Wonderland]]'
+ */
+ public function testPropertyChain() {
+
+ $description = new SomeProperty(
+ DIProperty::newFromUserLabel( 'LocatedIn' ),
+ new SomeProperty(
+ DIProperty::newFromUserLabel( 'MemberOf' ),
+ new ValueDescription(
+ new DIWikiPage( 'Wonderland', NS_MAIN, '' ),
+ DIProperty::newFromUserLabel( 'MemberOf' ), SMW_CMP_EQ
+ )
+ )
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result property:LocatedIn ?v1 .' )->addNewLine()
+ ->addString( '{ ?v1 property:MemberOf wiki:Wonderland .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testAddOrderByData_ForNonWikiPageType() {
+
+ $condition = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $instance->addOrderByData( $condition, 'foo', DataItem::TYPE_NUMBER );
+
+ $this->assertEquals(
+ 'foo',
+ $condition->orderByVariable
+ );
+ }
+
+ public function testAddOrderByData_ForWikiPageType() {
+
+ $condition = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $instance->addOrderByData( $condition, 'foo', DataItem::TYPE_WIKIPAGE );
+
+ $this->assertEquals(
+ 'foosk',
+ $condition->orderByVariable
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?foo swivt:wikiPageSortKey ?foosk .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $condition->weakConditions['foosk']
+ );
+ }
+
+ public function testCanUseQFeature() {
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->isSetFlag( 'Foo' )
+ );
+ }
+
+ public function testTryToFindRedirectVariableForNonWpgDataItem() {
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $this->assertNull(
+ $instance->tryToFindRedirectVariableForDataItem( new DINumber( 1 ) )
+ );
+ }
+
+ public function testExtendConditionUsingPropertyPathForWpgPropertyValueRedirect() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $diWikiPage = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->setConstructorArgs( [ 'Bar', NS_MAIN ] )
+ ->setMethods( [ 'getTitle' ] )
+ ->getMock();
+
+ $diWikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( $diWikiPage, $property )
+ );
+
+ $instance = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->setConstructorArgs( [ $this->descriptionInterpreterFactory ] )
+ ->setMethods( [ 'isSetFlag' ] )
+ ->getMock();
+
+ $instance->expects( $this->at( 0 ) )
+ ->method( 'isSetFlag' )
+ ->with( $this->equalTo( SMW_SPARQL_QF_NOCASE ) )
+ ->will( $this->returnValue( false ) );
+
+ $instance->expects( $this->at( 1 ) )
+ ->method( 'isSetFlag' )
+ ->with( $this->equalTo( SMW_SPARQL_QF_REDI ) )
+ ->will( $this->returnValue( true ) );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?r2 ^swivt:redirectsTo wiki:Bar .' )->addNewLine()
+ ->addString( '?result property:Foo ?r2 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o3 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o3 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testExtendConditionUsingPropertyPathForWpgValueRedirect() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->atLeastOnce() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $diWikiPage = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->setConstructorArgs( [ 'Bar', NS_MAIN ] )
+ ->setMethods( [ 'getTitle' ] )
+ ->getMock();
+
+ $diWikiPage->expects( $this->atLeastOnce() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $description = new ValueDescription( $diWikiPage, null );
+
+ $instance = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->setConstructorArgs( [ $this->descriptionInterpreterFactory ] )
+ ->setMethods( [ 'isSetFlag' ] )
+ ->getMock();
+
+ $instance->expects( $this->at( 0 ) )
+ ->method( 'isSetFlag' )
+ ->with( $this->equalTo( SMW_SPARQL_QF_NOCASE ) )
+ ->will( $this->returnValue( false ) );
+
+ $instance->expects( $this->at( 1 ) )
+ ->method( 'isSetFlag' )
+ ->with( $this->equalTo( SMW_SPARQL_QF_REDI ) )
+ ->will( $this->returnValue( true ) );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?r1 ^swivt:redirectsTo wiki:Bar .' )->addNewLine()
+ ->addString( 'FILTER( ?result = ?r1 )' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+ public function testSingletonLikeConditionForSolitaryWpgValue() {
+
+ $description = new ValueDescription(
+ new DIWikiPage( "Foo*", NS_MAIN ), null, SMW_CMP_LIKE
+ );
+
+ $instance = new ConditionBuilder( $this->descriptionInterpreterFactory );
+
+ $condition = $instance->getConditionFrom( $description );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition',
+ $condition
+ );
+
+ $expectedConditionString = $this->stringBuilder
+ ->addString( 'FILTER( regex( ?v1, "^Foo.*$", "s") )' )->addNewLine()
+ ->addString( '?result swivt:wikiPageSortKey ?v1 .' )->addNewLine()
+ ->addString( ' OPTIONAL { ?result swivt:redirectsTo ?o2 } .' )->addNewLine()
+ ->addString( ' FILTER ( !bound( ?o2 ) ) .' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $instance->convertConditionToString( $condition )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php
new file mode 100644
index 00000000..8e4be5e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DescriptionInterpreterFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory',
+ new DescriptionInterpreterFactory()
+ );
+ }
+
+ public function testCanConstructDispatchingDescriptionInterpreter() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionInterpreterFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DispatchingDescriptionInterpreter',
+ $instance->newDispatchingDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
new file mode 100644
index 00000000..026ace2d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ClassDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter',
+ new ClassDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ClassDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ClassDescriptionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider categoryProvider
+ */
+ public function testClassConditionForCategories( $description, $orderByProperty, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new ClassDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function testHierarchyPattern() {
+
+ $engineOptions = new EngineOptions();
+ $engineOptions->set( 'smwgSparqlQFeatures', SMW_SPARQL_QF_SUBC );
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $categoryName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $category )
+ );
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyLookup->expects( $this->once() )
+ ->method( 'hasSubcategory' )
+ ->with( $this->equalTo( $category ) )
+ ->will( $this->returnValue( true ) );
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory, $engineOptions );
+ $conditionBuilder->setHierarchyLookup( $hierarchyLookup );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+
+ $instance = new ClassDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription(
+ new ClassDescription( $category )
+ );
+
+ $expected = UtilityFactory::getInstance()->newStringBuilder()
+ ->addString( '{' )->addNewLine()
+ ->addString( "?sc1 rdfs:subClassOf* $categoryName ." )->addNewLine()
+ ->addString( '?result rdf:type ?sc1 . }' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expected,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function categoryProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new ClassDescription( [] );
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $categoryName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $category )
+ );
+
+ $description = new ClassDescription( $category );
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( "{ ?result rdf:type $categoryName . }" )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected,
+ ];
+
+ # 2
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $categoryFoo = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $categoryFooName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $categoryFoo )
+ );
+
+ $categoryBar = new DIWikiPage( 'Bar', NS_CATEGORY );
+
+ $categoryBarName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $categoryBar )
+ );
+
+ $description = new ClassDescription( [
+ $categoryFoo,
+ $categoryBar
+ ] );
+
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( "{ ?result rdf:type $categoryFooName . }" )->addNewLine()
+ ->addString( 'UNION' )->addNewLine()
+ ->addString( "{ ?result rdf:type $categoryBarName . }" )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ # 3
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new ClassDescription( [
+ $categoryFoo,
+ $categoryBar
+ ] );
+
+ $orderByProperty = new DIProperty( 'Foo' );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( "{ ?result rdf:type $categoryFooName . }" )->addNewLine()
+ ->addString( 'UNION' )->addNewLine()
+ ->addString( "{ ?result rdf:type $categoryBarName . }" )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
new file mode 100644
index 00000000..308e7196
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\ApplicationFactory;
+use SMW\DIConcept;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ConceptDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter',
+ new ConceptDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ConceptDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConceptDescriptionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testConceptDescriptionInterpreter( $description, $orderByProperty, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setCircularReferenceGuard( $circularReferenceGuard );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new ConceptDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function testConceptDescriptionInterpreterForAnyValueConceptUsingMockedStore() {
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with( $this->equalTo( new DIProperty( '_CONC' ) ) )
+ ->will( $this->returnValue( [
+ new DIConcept( '[[Foo::+]]', 'Bar', 1, 0, 0 ) ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getSemanticData' )
+ ->with( $this->equalTo( new DIWikiPage( 'Foo', SMW_NS_CONCEPT ) ) )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $description = new ConceptDescription( new DIWikiPage( 'Foo', SMW_NS_CONCEPT ) );
+ $orderByProperty = null;
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setCircularReferenceGuard( $circularReferenceGuard );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new ConceptDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $expectedConditionString = UtilityFactory::getInstance()->newStringBuilder()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->getString();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition',
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new ConceptDescription( new DIWikiPage( 'Foo', SMW_NS_CONCEPT ) );
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php
new file mode 100644
index 00000000..d3e2ff1a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ConjunctionInterpreterTest.php
@@ -0,0 +1,369 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ConjunctionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ConjunctionInterpreter',
+ new ConjunctionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Conjunction' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ConjunctionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testConjunctionCondition( $description, $orderByProperty, $sortkeys, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setSortKeys( $sortkeys );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new ConjunctionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new Conjunction();
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) )
+ ] );
+
+ $description = new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ $description
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 2
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new Conjunction( [ new ThingDescription() ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 3
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ new ThingDescription()
+ );
+
+ $description = new Conjunction( [
+ $description,
+ new NamespaceDescription( NS_HELP )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( '{ ?result swivt:wikiNamespace "12"^^xsd:integer . }' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 4
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition';
+
+ $description = new Conjunction( [
+ new NamespaceDescription( NS_MAIN ),
+ new ValueDescription( new DIWikiPage( 'SomePageValue', NS_MAIN ) )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '{ wiki:SomePageValue swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 5
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyBlobValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LESS
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ $description
+ );
+
+ $description = new Conjunction( [
+ $description,
+ new ValueDescription( new DIBlob( 'SomeOtherPropertyBlobValue' ), null, SMW_CMP_LESS ),
+ new ValueDescription( new DIBlob( 'YetAnotherPropertyBlobValue' ), null, SMW_CMP_GRTR ),
+ new NamespaceDescription( NS_MAIN )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 < "SomePropertyBlobValue" )' )->addNewLine()
+ ->addString( '{ ?result swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( 'FILTER( ?result < "SomeOtherPropertyBlobValue" && ?result > "YetAnotherPropertyBlobValue" )' )
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 6
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyBlobValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LESS
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ $description
+ );
+
+ $description = new Conjunction( [
+ $description,
+ new ValueDescription( new DIBlob( 'SomeOtherPropertyBlobValue' ), null, SMW_CMP_LIKE ),
+ new ValueDescription( new DIWikiPage( 'SomePropertyPageValue', NS_MAIN ) ),
+ new NamespaceDescription( NS_MAIN )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( 'wiki:SomePropertyPageValue property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 < "SomePropertyBlobValue" )' )->addNewLine()
+ ->addString( '{ wiki:SomePropertyPageValue swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( 'FILTER( regex( ?result, "^SomeOtherPropertyBlobValue$", "s") )' )
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 7
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new Conjunction( [
+ new ValueDescription( new DIBlob( 'SomeOtherPropertyBlobValue' ), null, SMW_CMP_LIKE ),
+ new ValueDescription( new DIBlob( 'YetAnotherPropertyBlobValue' ), new DIProperty( 'Foo'), SMW_CMP_NLKE ),
+ new ThingDescription()
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( regex( ?result, "^SomeOtherPropertyBlobValue$", "s") && ' )
+ ->addString( '!regex( ?result, "^YetAnotherPropertyBlobValue$", "s") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 8
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $propertyValue = new DIWikiPage( 'SomePropertyPageValue', NS_HELP );
+
+ $propertyValueName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $propertyValue )
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( $propertyValue )
+ );
+
+ $category = new DIWikiPage( 'Bar', NS_CATEGORY );
+
+ $categoryName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $category )
+ );
+
+ $description = new Conjunction([
+ $description,
+ new ClassDescription( $category )
+ ] );
+
+ $orderByProperty = new DIProperty( 'Foo' );
+ $sortkeys = [ 'Foo' => 'ASC' ];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( "?result property:Foo $propertyValueName ." )->addNewLine()
+ ->addString( '{ ?v1 swivt:wikiPageSortKey ?v1sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( "{ ?result rdf:type $categoryName . }" )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreterTest.php
new file mode 100644
index 00000000..41ce70cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/DisjunctionInterpreterTest.php
@@ -0,0 +1,483 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DisjunctionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\DisjunctionInterpreter',
+ new DisjunctionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Disjunction' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DisjunctionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testConjunctionCondition( $description, $orderByProperty, $sortkeys, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setSortKeys( $sortkeys );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new DisjunctionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new Disjunction();
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new Disjunction( [
+ new ThingDescription(),
+ new ThingDescription()
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 2
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new Disjunction( [
+ new NamespaceDescription( NS_MAIN ),
+ new NamespaceDescription( NS_HELP )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '{' )->addNewLine()
+ ->addString( '{ ?result swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( '} UNION {' )->addNewLine()
+ ->addString( '{ ?result swivt:wikiNamespace "12"^^xsd:integer . }' )->addNewLine()
+ ->addString( '}' )
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 3
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new Disjunction( [
+ new NamespaceDescription( NS_MAIN )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '{ ?result swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 4
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition';
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'SomePropertyPageValue', NS_MAIN ), null, SMW_CMP_LIKE
+ );
+
+ $description = new Disjunction( [
+ $description
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( 'FILTER( regex( ?v1, "^SomePropertyPageValue$", "s") )' )->addNewLine()
+ ->addString( '?result swivt:wikiPageSortKey ?v1 .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 5
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new Disjunction( [
+ new NamespaceDescription( NS_MAIN ),
+ new ValueDescription( new DIBlob( 'SomePropertyBlobValue' ), new DIProperty( 'Foo' ), SMW_CMP_LESS )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'OPTIONAL { {' )->addNewLine()
+ ->addString( '{ ?v1 swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( '} }' )->addNewLine()
+ ->addString( ' FILTER( ?result < "SomePropertyBlobValue" || ?result = ?v1 )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 6
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyBlobValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LESS
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ $description
+ );
+
+ $description = new Disjunction( [
+ new NamespaceDescription( NS_MAIN ),
+ $description
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '{' )->addNewLine()
+ ->addString( '{ ?result swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( '} UNION {' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 < "SomePropertyBlobValue" )' )->addNewLine()
+ ->addString( '}' )
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 7
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyBlobValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_EQ
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ $description
+ );
+
+ $description = new Disjunction( [
+ new NamespaceDescription( NS_MAIN ),
+ $description,
+ new ValueDescription( new DINumber( 42 ), null, SMW_CMP_EQ )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'OPTIONAL { {' )->addNewLine()
+ ->addString( '{ ?v2 swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->addString( '} UNION {' )->addNewLine()
+ ->addString( '?v2 property:Foo "SomePropertyBlobValue" .' )->addNewLine()
+ ->addString( '} }' )->addNewLine()
+ ->addString( ' FILTER( ?result = "42"^^xsd:double || ?result = ?v2 )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 8
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new Disjunction( [
+ new ValueDescription( new DINumber( 12 ), null, SMW_CMP_EQ )
+ ] );
+
+ $description = new Disjunction( [
+ $description,
+ new ValueDescription( new DINumber( 42 ), null, SMW_CMP_LIKE )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 9
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new Disjunction( [
+ new ValueDescription( new DINumber( 12 ), null, SMW_CMP_EQ ),
+ new ValueDescription( new DIBlob( 'Bar' ), null, SMW_CMP_LIKE )
+ ] );
+
+ $description = new Disjunction( [
+ $description,
+ new SomeProperty(
+ new DIProperty( 'Foo' ),
+ new ValueDescription( new DINumber( 42 ), null, SMW_CMP_LIKE )
+ )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'OPTIONAL { {' )->addNewLine()
+ ->addString( '?v2 property:Foo ?v1 .' )->addNewLine()
+ ->addString( '} }' )->addNewLine()
+ ->addString( ' FILTER( ?result = "12"^^xsd:double || regex( ?result, "^Bar$", "s") || ?result = ?v2 )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 10
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new Disjunction( [
+ new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ ),
+ new SomeProperty(
+ new DIProperty( 'Bar' ),
+ new ThingDescription()
+ )
+ ] ),
+ new ValueDescription( new DIBlob( 'Yui' ), null, SMW_CMP_LIKE )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [ 'Bar' => 'ASC' ];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'OPTIONAL { {' )->addNewLine()
+ ->addString( '?v2 property:Bar ?v1 .' )->addNewLine()
+ ->addString( '{ ?v1 swivt:wikiPageSortKey ?v1sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->addString( ' FILTER( ?v2 = wiki:Foo ) } }' )->addNewLine()
+ ->addString( ' FILTER( regex( ?result, "^Yui$", "s") || ?result = ?v2 )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 11
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new Disjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ ),
+ new ValueDescription( new DIBlob( 'Yui' ), null, SMW_CMP_LIKE )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result = wiki:Foo || regex( ?result, "^Yui$", "s") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 12
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Bar', NS_MAIN ) )
+ ] );
+
+ $description = new Conjunction( [
+ new ValueDescription( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ $description
+ ] );
+
+ $description = new Disjunction( [
+ $description,
+ new ClassDescription( [] )
+ ] );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
new file mode 100644
index 00000000..9e945c8e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\Language\NamespaceDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter',
+ new NamespaceDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\NamespaceDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new NamespaceDescriptionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider namespaceProvider
+ */
+ public function testNamespaceCondition( $description, $orderByProperty, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new NamespaceDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function namespaceProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new NamespaceDescription( NS_MAIN );
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( '{ ?result swivt:wikiNamespace "0"^^xsd:integer . }' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php
new file mode 100644
index 00000000..52c240a9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php
@@ -0,0 +1,552 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter;
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDITime as DITime;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class SomePropertyInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter',
+ new SomePropertyInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanInterpretDescription() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\SomeProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SomePropertyInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testSomeProperty( $description, $orderByProperty, $sortkeys, $expectedConditionType, $expectedConditionString ) {
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setHierarchyLookup( $hierarchyLookup );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setSortKeys( $sortkeys );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new SomePropertyInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function testHierarchyPattern() {
+
+ $engineOptions = new EngineOptions();
+ $engineOptions->set( 'smwgSparqlQFeatures', SMW_SPARQL_QF_SUBP );
+
+ $property = new DIProperty( 'Foo' );
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyLookup->expects( $this->once() )
+ ->method( 'hasSubproperty' )
+ ->with( $this->equalTo( $property ) )
+ ->will( $this->returnValue( true ) );
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory, $engineOptions );
+ $conditionBuilder->setHierarchyLookup( $hierarchyLookup );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+
+ $instance = new SomePropertyInterpreter( $conditionBuilder );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $expected = UtilityFactory::getInstance()->newStringBuilder()
+ ->addString( '?result ?sp2 ?v1 .' )->addNewLine()
+ ->addString( '{ ' )->addNewLine()
+ ->addString( '?sp2 rdfs:subPropertyOf* property:Foo .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expected,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition';
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ new Disjunction()
+ );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '<http://www.example.org> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#nothing> .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ new ThingDescription()
+ );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 2 Inverse
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo', true ),
+ new ThingDescription()
+ );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?v1 property:Foo ?result .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 3
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foo'),
+ new ThingDescription()
+ );
+
+ $orderByProperty = new DIProperty( 'Foo');
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 4
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIBlob( 'SomePropertyBlobValue' ) )
+ );
+
+ $orderByProperty = null;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result property:Foo "SomePropertyBlobValue" .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 5
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIBlob( 'SomePropertyBlobValue' ) )
+ );
+
+ $orderByProperty = $property;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Foo "SomePropertyBlobValue" .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 6
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $propertyValue = new DIWikiPage( 'SomePropertyPageValue', NS_HELP );
+
+ $propertyValueName = \SMWTurtleSerializer::getTurtleNameForExpElement(
+ \SMWExporter::getInstance()->getResourceElementForWikiPage( $propertyValue )
+ );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( $propertyValue )
+ );
+
+ $orderByProperty = $property;
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( "?result property:Foo $propertyValueName ." )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 7
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIWikiPage( 'SomePropertyPageValue', NS_HELP ), $property, SMW_CMP_LEQ )
+ );
+
+ $orderByProperty = new DIProperty( 'SomePropertyPageValue' );
+ $sortkeys = [];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1sk <= "SomePropertyPageValue" )' )->addNewLine()
+ ->addString( '?v1 swivt:wikiPageSortKey ?v1sk .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 8
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIWikiPage( 'SomePropertyPageValue', NS_HELP ), $property, SMW_CMP_LEQ )
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Bar' ),
+ $description
+ );
+
+ $orderByProperty = new DIProperty( 'Bar' );
+ $sortkeys = [ 'Foo' => 'ASC' ];
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Bar ?v1 .' )->addNewLine()
+ ->addString( '{ ?v1 property:Foo ?v2 .' )->addNewLine()
+ ->addString( 'FILTER( ?v2sk <= "SomePropertyPageValue" )' )->addNewLine()
+ ->addString( '?v2 swivt:wikiPageSortKey ?v2sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 9 Inverse -> ?v1 property:Foo ?v2 vs. ?v2 property:Foo ?v1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo', true );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIWikiPage( 'SomePropertyPageValue', NS_HELP ), $property, SMW_CMP_LEQ )
+ );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Bar' ),
+ $description
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Bar ?v1 .' )->addNewLine()
+ ->addString( '{ ?v2 property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v2sk <= "SomePropertyPageValue" )' )->addNewLine()
+ ->addString( '?v2 swivt:wikiPageSortKey ?v2sk .' )->addNewLine()
+ ->addString( '}' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 10
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( '_MDAT' );
+
+ $description = new SomeProperty(
+ $property,
+ new ThingDescription()
+ );
+
+ $sortkeys = [ '_MDAT' => 'ASC' ];
+ $propertyLabel = str_replace( ' ', '_', $property->getLabel() );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( "?result property:{$propertyLabel}-23aux ?v1 ." )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 11, issue 556
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = new SomeProperty(
+ $property,
+ new Disjunction( [
+ new ValueDescription( new DIBlob( 'Bar' ) ),
+ new ValueDescription( new DIBlob( 'Baz' ) )
+ ] )
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Foo ?v1 .' )->addNewLine()
+ ->addString( 'FILTER( ?v1 = "Bar" || ?v1 = "Baz" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 12 use the rdf/owl equivalent for a predefined property
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( '_SUBC' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIBlob( 'Bar' ) )
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result rdfs:subClassOf "Bar" .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 13
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( '_SUBP' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DIWikiPage( 'Bar', SMW_NS_PROPERTY ) )
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result rdfs:subPropertyOf property:Bar .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ # 14 aux-property
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\WhereCondition';
+
+ $property = new DIProperty( '_MDAT' );
+
+ $description = new SomeProperty(
+ $property,
+ new ValueDescription( new DITime( 1, 1970, 01, 01, 1, 1 ) )
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( '?result property:Modification_date-23aux "2440587.5423611"^^xsd:double .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $sortkeys,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php
new file mode 100644
index 00000000..cab64703
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\Query\Language\ThingDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ThingDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter',
+ new ThingDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ThingDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ThingDescriptionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testThingDescriptionInterpreter( $description, $orderByProperty, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+ $conditionBuilder->setOrderByProperty( $orderByProperty );
+
+ $instance = new ThingDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new ThingDescription();
+ $orderByProperty = null;
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new ThingDescription();
+ $orderByProperty = new DIProperty( 'Foo' );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $orderByProperty,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
new file mode 100644
index 00000000..46b521d3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
@@ -0,0 +1,593 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ValueDescription;
+use SMW\SPARQLStore\QueryEngine\ConditionBuilder;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob as DIBlob;
+use SMWDINumber as DINumber;
+use SMWDIUri as DIUri;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ValueDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $descriptionInterpreterFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter',
+ new ValueDescriptionInterpreter( $conditionBuilder )
+ );
+ }
+
+ public function testCanBuildConditionFor() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ValueDescriptionInterpreter( $conditionBuilder );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider notSupportedDataItemTypeProvider
+ */
+ public function testCreateFalseConditionForNotSupportedDataItemType( $dataItem ) {
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->setConstructorArgs( [ $this->descriptionInterpreterFactory ] )
+ ->setMethods( [ 'isSetFlag' ] )
+ ->getMock();
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'isSetFlag' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ValueDescriptionInterpreter( $conditionBuilder );
+
+ $description = new ValueDescription(
+ $dataItem,
+ null
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\Condition\FalseCondition',
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider comparatorProvider
+ */
+ public function testValueConditionForDifferentComparators( $description, $expectedConditionType, $expectedConditionString ) {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+
+ $instance = new ValueDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ $this->assertEquals(
+ $expectedConditionString,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function testValueConditionOnRediret() {
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->setConstructorArgs( [ $this->descriptionInterpreterFactory ] )
+ ->setMethods( [ 'tryToFindRedirectVariableForDataItem' ] )
+ ->getMock();
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'tryToFindRedirectVariableForDataItem' )
+ ->will( $this->returnValue( '?r1' ) );
+
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+
+ $instance = new ValueDescriptionInterpreter( $conditionBuilder );
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'Foo', NS_MAIN ),
+ null
+ );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $expectedConditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $this->assertInstanceOf(
+ $expectedConditionType,
+ $condition
+ );
+
+ // The redirect pattern add by conditionBuilder at th end of
+ // the mapping
+ $expected = UtilityFactory::getInstance()->newStringBuilder()
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( 'FILTER( ?result = ?r1 )' )->addNewLine()
+ ->getString();
+
+ $this->assertEquals(
+ $expected,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ /**
+ * @dataProvider noCaseDescritionProvider
+ */
+ public function testValueConditionOnNoCase( $description, $expected ) {
+
+ $engineOptions = new EngineOptions();
+ $engineOptions->set( 'smwgSparqlQFeatures', SMW_SPARQL_QF_NOCASE );
+
+ $resultVariable = 'result';
+
+ $conditionBuilder = new ConditionBuilder( $this->descriptionInterpreterFactory, $engineOptions );
+ $conditionBuilder->setResultVariable( $resultVariable );
+ $conditionBuilder->setJoinVariable( $resultVariable );
+
+ $instance = new ValueDescriptionInterpreter( $conditionBuilder );
+
+ $condition = $instance->interpretDescription( $description );
+
+ $this->assertEquals(
+ $expected,
+ $conditionBuilder->convertConditionToString( $condition )
+ );
+ }
+
+ public function comparatorProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_EQ
+ );
+
+ $expected = $stringBuilder
+ ->addString( '"SomePropertyValue" swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 1
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LESS
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result < "SomePropertyValue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 2 Less for a non-blob (DIWikiPage type) value
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'SomePropertyValuePage', NS_MAIN ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LESS
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( 'FILTER( ?resultsk < "SomePropertyValuePage" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 3
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIProperty( 'SomeProperty' ),
+ null,
+ SMW_CMP_LESS
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result < property:SomeProperty )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 4
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_GRTR
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result > "SomePropertyValue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 5
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LEQ
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result <= "SomePropertyValue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 6
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_GEQ
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result >= "SomePropertyValue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 7
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NEQ
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( ?result != "SomePropertyValue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 8
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LIKE
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( regex( ?result, "^SomePropertyValue$", "s") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 9
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NLKE
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( !regex( ?result, "^SomePropertyValue$", "s") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 10 Regex on a non-blob value
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = new ValueDescription(
+ new DINumber( 42 ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NLKE
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 11 Regex on a non-blob (DIWikiPage type) value
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition';
+
+ $description = new ValueDescription(
+ new DIWikiPage( 'SomePropertyValuePage', NS_MAIN ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LIKE
+ );
+
+ $expected = $stringBuilder
+ ->addString( 'FILTER( regex( ?v1, "^SomePropertyValuePage$", "s") )' )->addNewLine()
+ ->addString( '?result swivt:wikiPageSortKey ?v1 .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 12 Regex on a non-blob (DIUri type) value
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\FilterCondition';
+
+ $description = new ValueDescription(
+ new DIUri( 'http', '//example.org', '', '' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_LIKE
+ );
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( regex( str( ?result ), "^.*//example\\\.org$", "i") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ # 13 Unknown comparator operator
+ $conditionType = '\SMW\SPARQLStore\QueryEngine\Condition\TrueCondition';
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expected = $stringBuilder
+ ->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $conditionType,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+ public function noCaseDescritionProvider() {
+
+ $stringBuilder = UtilityFactory::getInstance()->newStringBuilder();
+
+ # 0
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NLKE
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( !regex( ?result, "^SomePropertyValue$", "i") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ # 1
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_PRIM_LIKE
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( regex( ?result, "^SomePropertyValue$", "i") )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ # 2
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_EQ
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( lcase(str(?result) ) = "somepropertyvalue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ # 3
+ $description = new ValueDescription(
+ new DIBlob( 'SomePropertyValue' ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NEQ
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:page ?url .' )->addNewLine()
+ ->addString( 'FILTER( lcase(str(?result) ) != "somepropertyvalue" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ # 4
+ $description = new ValueDescription(
+ new DIWikiPage( 'SomePropertyValuePage', NS_MAIN ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_EQ
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( 'FILTER( lcase(str(?resultsk) ) = "somepropertyvaluepage" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ # 5
+ $description = new ValueDescription(
+ new DIWikiPage( 'SomePropertyValuePage', NS_MAIN ),
+ new DIProperty( 'Foo' ),
+ SMW_CMP_NEQ
+ );
+
+ $expected = $stringBuilder->addString( '?result swivt:wikiPageSortKey ?resultsk .' )->addNewLine()
+ ->addString( 'FILTER( lcase(str(?resultsk) ) != "somepropertyvaluepage" )' )->addNewLine()
+ ->getString();
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+ public function notSupportedDataItemTypeProvider() {
+
+ $dataItem = $this->getMockBuilder( '\SMWDIGeoCoord' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $dataItem
+ ];
+
+ $dataItem = $this->getMockBuilder( '\SMW\DIConcept' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $dataItem
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/EngineOptionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/EngineOptionsTest.php
new file mode 100644
index 00000000..8bed60ff
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/EngineOptionsTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\EngineOptions
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EngineOptionsTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\EngineOptions',
+ new EngineOptions()
+ );
+ }
+
+ /**
+ * @dataProvider initialSettingsProvider
+ */
+ public function testInitialState( $setting, $expected ) {
+
+ $instance = new EngineOptions();
+
+ $this->assertNotNull(
+ $instance->get( $setting )
+ );
+ }
+
+ public function testAddOption() {
+
+ $instance = new EngineOptions();
+
+ $this->assertFalse(
+ $instance->has( 'Foo' )
+ );
+
+ $instance->set( 'Foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'Foo' )
+ );
+ }
+
+ public function testUnregisteredKeyThrowsException() {
+
+ $instance = new EngineOptions();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->get( 'Foo' );
+ }
+
+ public function initialSettingsProvider() {
+
+ $provider[] = [
+ 'smwgIgnoreQueryErrors',
+ 'boolean'
+ ];
+
+ $provider[] = [
+ 'smwgQSortFeatures',
+ 'integer'
+ ];
+
+ $provider[] = [
+ 'smwgQSubpropertyDepth',
+ 'integer'
+ ];
+
+ $provider[] = [
+ 'smwgQSubcategoryDepth',
+ 'integer'
+ ];
+
+ $provider[] = [
+ 'smwgSparqlQFeatures',
+ 'integer'
+ ];
+
+ return $provider;
+ }
+
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryEngineTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryEngineTest.php
new file mode 100644
index 00000000..162fd5e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryEngineTest.php
@@ -0,0 +1,526 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\EngineOptions;
+use SMW\SPARQLStore\QueryEngine\QueryEngine;
+use SMW\SPARQLStore\QueryEngine\QueryResultFactory;
+use SMWQuery as Query;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\QueryEngine
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryEngineTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultFactory = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\QueryResultFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\QueryEngine',
+ new QueryEngine( $connection, $conditionBuilder, $queryResultFactory )
+ );
+ }
+
+ public function testEmptyGetQueryResultWhereQueryContainsErrors() {
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $engineOptions = new EngineOptions();
+ $engineOptions->set( 'smwgIgnoreQueryErrors', false );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store ),
+ $engineOptions
+ );
+
+ $query = new Query( $description );
+ $query->addErrors( [ 'Foo' ] );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+
+ $this->assertEmpty(
+ $instance->getQueryResult( $query )->getResults()
+ );
+ }
+
+ public function testConditionBuilderReturnsErrors() {
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [ 'bogus-error' ] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $engineOptions = new EngineOptions();
+ $engineOptions->set( 'smwgIgnoreQueryErrors', false );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store ),
+ $engineOptions
+ );
+
+ $query = new Query( $description );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+
+ $this->assertEmpty(
+ $instance->getQueryResult( $query )->getResults()
+ );
+
+ $this->assertEquals(
+ [ 'bogus-error' ],
+ $query->getErrors()
+ );
+ }
+
+ public function testEmptyGetQueryResultWhereQueryModeIsNone() {
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_NONE;
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+
+ $this->assertEmpty(
+ $instance->getQueryResult( $query )->getResults()
+ );
+ }
+
+ public function testInvalidSorkeyThrowsException() {
+
+ $sortKeys = [ 'Foo', 'Bar' ];
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'getSortKeys' )
+ ->will( $this->returnValue( $sortKeys ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->setSortKeys( $sortKeys );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getQueryResult( $query );
+ }
+
+ public function testtestGetQueryResultForDebugQueryMode() {
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $queryResultFactory = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\QueryResultFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine( $connection, $conditionBuilder, $queryResultFactory );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_DEBUG;
+
+ $this->assertNotInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testGetSuccessCountQueryResultForMockedCompostion() {
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectCount' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_COUNT;
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testInstanceQueryResultForMockedCompostion() {
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'select' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testGetImmediateEmptyQueryResultForLimitLessThanOne() {
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->never() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->setUnboundLimit( -1 );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testInstanceQueryResultForMockedSingletonCompostion() {
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'ask' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->once() )
+ ->method( 'ask' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $element = $this->getMockBuilder( '\SMW\Exporter\Element' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $condition = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $condition->matchElement = $element;
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testDebugQueryResultForMockedCompostion() {
+
+ // PHPUnit 3.7 goes drumming when trying to add a method on an
+ // interface hence the use of the concrete class
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSparqlForSelect' ] )
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'getSparqlForSelect' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $condition = $this->getMockForAbstractClass( '\SMW\SPARQLStore\QueryEngine\Condition\Condition' );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $conditionBuilder = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\ConditionBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $conditionBuilder->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $conditionBuilder->expects( $this->atLeastOnce() )
+ ->method( 'setSortKeys' )
+ ->will( $this->returnValue( $conditionBuilder ) );
+
+ $conditionBuilder->expects( $this->once() )
+ ->method( 'getConditionFrom' )
+ ->will( $this->returnValue( $condition ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $connection,
+ $conditionBuilder,
+ new QueryResultFactory( $store )
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_DEBUG;
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryResultFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryResultFactoryTest.php
new file mode 100644
index 00000000..d16b9c3e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/QueryResultFactoryTest.php
@@ -0,0 +1,201 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\QueryResultFactory;
+use SMW\Tests\Utils\Mock\IteratorMockBuilder;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\QueryResultFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryResultFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\QueryResultFactory',
+ new QueryResultFactory( $store )
+ );
+ }
+
+ public function testGetQueryResultObjectForNullSet() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = new Query( $description );
+
+ $instance = new QueryResultFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->newQueryResult( null, $query )
+ );
+ }
+
+ /**
+ * @dataProvider errorCodeProvider
+ */
+ public function testGetQueryResultObjectForCountQuery( $errorCode ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $RepositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $RepositoryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrorCode' )
+ ->will( $this->returnValue( $errorCode ) );
+
+ $description = $this->getMockBuilder( '\SMWDescription' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_COUNT;
+
+ $instance = new QueryResultFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->newQueryResult( $RepositoryResult, $query )
+ );
+
+ $this->assertQueryResultErrorCodeForCountValue(
+ $errorCode,
+ $instance->newQueryResult( $RepositoryResult, $query )
+ );
+ }
+
+ /**
+ * @dataProvider errorCodeProvider
+ */
+ public function testGetQueryResultObjectForEmptyInstanceQuery( $errorCode ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $RepositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $RepositoryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrorCode' )
+ ->will( $this->returnValue( $errorCode ) );
+
+ $description = $this->getMockBuilder( '\SMWDescription' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $instance = new QueryResultFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->newQueryResult( $RepositoryResult, $query )
+ );
+
+ $this->assertQueryResultErrorCode(
+ $errorCode,
+ $instance->newQueryResult( $RepositoryResult, $query )
+ );
+ }
+
+ /**
+ * @dataProvider errorCodeProvider
+ */
+ public function testGetQueryResultObjectForInstanceQuery( $errorCode ) {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $expElement = $this->getMockBuilder( '\SMWExpElement' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $iteratorMockBuilder = new IteratorMockBuilder();
+
+ $repositoryResult = $iteratorMockBuilder->setClass( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->with( [ [ $expElement ] ] )
+ ->getMockForIterator();
+
+ $repositoryResult->expects( $this->atLeastOnce() )
+ ->method( 'getErrorCode' )
+ ->will( $this->returnValue( $errorCode ) );
+
+ $description = $this->getMockBuilder( '\SMWDescription' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_INSTANCES;
+
+ $instance = new QueryResultFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->newQueryResult( $repositoryResult, $query )
+ );
+
+ $this->assertQueryResultErrorCode(
+ $errorCode,
+ $instance->newQueryResult( $repositoryResult, $query )
+ );
+ }
+
+ private function assertQueryResultErrorCodeForCountValue( $errorCode, QueryResult $queryResult ) {
+
+ if ( $errorCode > 0 ) {
+ $this->assertNotEmpty( $queryResult->getErrors() );
+ return $this->assertNull( $queryResult->getCountValue() );
+ }
+
+ $this->assertEmpty( $queryResult->getErrors() );
+ $this->assertInternalType( 'integer', $queryResult->getCountValue() );
+ }
+
+ private function assertQueryResultErrorCode( $errorCode, QueryResult $queryResult ) {
+
+ if ( $errorCode > 0 ) {
+ return $this->assertNotEmpty( $queryResult->getErrors() );
+ }
+
+ $this->assertEmpty( $queryResult->getErrors() );
+ }
+
+ public function errorCodeProvider() {
+
+ $provider = [
+ [ 0 ],
+ [ 1 ],
+ [ 2 ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/RepositoryResultTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/RepositoryResultTest.php
new file mode 100644
index 00000000..79655ad9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/RepositoryResultTest.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\RepositoryResult;
+use SMWExpLiteral as ExpLiteral;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\RepositoryResult
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryResultTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\RepositoryResult',
+ new RepositoryResult()
+ );
+
+ $this->assertInstanceOf(
+ '\Iterator',
+ new RepositoryResult()
+ );
+ }
+
+ public function testIsBooleanTrue() {
+
+ $instance = new RepositoryResult(
+ [],
+ [ [ new ExpLiteral( 'true', 'http://www.w3.org/2001/XMLSchema#boolean' ) ] ]
+ );
+
+ $this->assertEquals( 1, $instance->numRows() );
+ $this->assertTrue( $instance->isBooleanTrue() );
+ }
+
+ public function testIsBooleanNotTrue() {
+
+ $instance = new RepositoryResult();
+
+ $this->assertFalse(
+ $instance->isBooleanTrue()
+ );
+ }
+
+ public function testGetNumericValue() {
+
+ $instance = new RepositoryResult(
+ [],
+ [ [ new ExpLiteral( '2', 'http://www.w3.org/2001/XMLSchema#integer' ) ] ]
+ );
+
+ $this->assertEquals( 1, $instance->numRows() );
+ $this->assertSame( 2, $instance->getNumericValue() );
+ }
+
+ public function testGetZeroNumericValue() {
+
+ $instance = new RepositoryResult();
+
+ $this->assertSame(
+ 0,
+ $instance->getNumericValue()
+ );
+ }
+
+ public function testSetGetErrorCode() {
+
+ $instance = new RepositoryResult();
+
+ $this->assertEquals(
+ RepositoryResult::ERROR_NOERROR,
+ $instance->getErrorCode()
+ );
+
+ $instance->setErrorCode(
+ RepositoryResult::ERROR_INCOMPLETE
+ );
+
+ $this->assertEquals(
+ RepositoryResult::ERROR_INCOMPLETE,
+ $instance->getErrorCode()
+ );
+ }
+
+ public function testIteration() {
+
+ $rawList = [
+ [
+ new ExpLiteral( '2', 'http://www.w3.org/2001/XMLSchema#integer' ),
+ new ExpLiteral( 'true', 'http://www.w3.org/2001/XMLSchema#boolean' )
+ ],
+ [
+ new ExpLiteral( '2', 'http://www.w3.org/2001/XMLSchema#integer' )
+ ]
+ ];
+
+ $instance = new RepositoryResult(
+ [],
+ [ $rawList[0], $rawList[1] ]
+ );
+
+ foreach ( $instance as $key => $listItem ) {
+ $this->assertEquals( $rawList[$key], $listItem );
+ }
+ }
+
+ public function testGetComments() {
+
+ $instance = new RepositoryResult(
+ [],
+ [],
+ [ 'Foo' ]
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $instance->getComments()
+ );
+ }
+
+}
+
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/XmlResponseParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/XmlResponseParserTest.php
new file mode 100644
index 00000000..30d930f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/QueryEngine/XmlResponseParserTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\QueryEngine;
+
+use SMW\SPARQLStore\QueryEngine\XmlResponseParser;
+use SMW\Tests\Utils\Fixtures\Results\FakeRawResultProvider;
+use SMWExpLiteral as ExpLiteral;
+use SMWExpResource as ExpResource;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\QueryEngine\XmlResponseParser
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class XmlResponseParserTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\XmlResponseParser',
+ new XmlResponseParser()
+ );
+ }
+
+ /**
+ * @dataProvider rawXmlResultDocumentProvider
+ */
+ public function testXmlParse( $rawXmlResult, $expectedResultRowItemInstance ) {
+
+ $instance = new XmlResponseParser();
+ $resultFormat = $instance->parse( $rawXmlResult );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\RepositoryResult',
+ $resultFormat
+ );
+
+ $this->assertResultFormat(
+ $expectedResultRowItemInstance,
+ $resultFormat
+ );
+ }
+
+ public function testInvalidXmlThrowsException() {
+
+ $rawResultProvider = new FakeRawResultProvider();
+
+ $instance = new XmlResponseParser();
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\XmlParserException' );
+ $instance->parse( $rawResultProvider->getInvalidSparqlResultXml() );
+ }
+
+ protected function assertResultFormat( $expectedResultRowItemInstance, $results ) {
+
+ if ( !is_array( $expectedResultRowItemInstance ) ) {
+ $expectedResultRowItemInstance = [ $expectedResultRowItemInstance ];
+ }
+
+ foreach ( $results as $key => $row ) {
+ $this->assertResultRow( $expectedResultRowItemInstance[$key], $row );
+ }
+ }
+
+ protected function assertResultRow( $expectedItemInstance, $row ) {
+
+ foreach ( $row as $key => $item ) {
+
+ if ( $item === null ) {
+ continue;
+ }
+
+ $this->assertEquals( $expectedItemInstance, $item );
+ }
+ }
+
+ public function rawXmlResultDocumentProvider() {
+
+ $rawResultProvider = new FakeRawResultProvider();
+
+ #0
+ $provider[] = [
+ $rawResultProvider->getUriResourceSparqlResultXml(),
+ new ExpResource( 'http://example.org/id/Foo' )
+ ];
+
+ #1
+ $provider[] = [
+ $rawResultProvider->getEmptySparqlResultXml(),
+ null
+ ];
+
+ #2 @bug 62218
+ $provider[] = [
+ $rawResultProvider->getNonTypeLiteralResultXml(),
+ new ExpLiteral( 'Has foo' )
+ ];
+
+ #3
+ $provider[] = [
+ $rawResultProvider->getBooleanSparqlResultXml(),
+ new ExpLiteral( 'true', 'http://www.w3.org/2001/XMLSchema#boolean' )
+ ];
+
+ #4
+ $provider[] = [
+ $rawResultProvider->getStringTypeLiteralSparqlResultXml(),
+ new ExpLiteral( 'Foo', 'http://www.w3.org/2001/XMLSchema#string' )
+ ];
+
+ #5
+ $provider[] = [
+ $rawResultProvider->getIntegerTypeLiteralSparqlResultXml(),
+ new ExpLiteral( '1', 'http://www.w3.org/2001/XMLSchema#integer' )
+ ];
+
+ #6
+ $provider[] = [
+ $rawResultProvider->getMixedRowsSparqlResultXml(),
+ [
+ new ExpResource( 'http://example.org/id/Foo' ),
+ new ExpResource( 'http://example.org/id/Bar' ),
+ new ExpLiteral( 'Quux', 'http://www.w3.org/2001/XMLSchema#string' )
+ ]
+ ];
+
+ #7 #450
+ $provider[] = [
+ false,
+ null
+ ];
+
+ #8 #450
+ $provider[] = [
+ 'false',
+ null
+ ];
+
+ #9 #626
+ $provider[] = [
+ 'true',
+ new ExpLiteral( 'true', 'http://www.w3.org/2001/XMLSchema#boolean' )
+ ];
+
+ #10
+ $provider[] = [
+ '',
+ new ExpLiteral( 'false', 'http://www.w3.org/2001/XMLSchema#boolean' )
+ ];
+
+ #11
+ $provider[] = [
+ $rawResultProvider->getMixedRowsSparqlResultUtf8Xml(),
+ [
+ new ExpResource( 'http://example.org/id/F安o' ),
+ new ExpResource( 'http://example.org/id/B定ar' ),
+ new ExpLiteral( 'Quux安定', 'http://www.w3.org/2001/XMLSchema#string' )
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/ReplicationDataTruncatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/ReplicationDataTruncatorTest.php
new file mode 100644
index 00000000..4ef8f747
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/ReplicationDataTruncatorTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\DIProperty;
+use SMW\SPARQLStore\ReplicationDataTruncator;
+
+/**
+ * @covers \SMW\SPARQLStore\ReplicationDataTruncator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ReplicationDataTruncatorTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticData;
+
+ public function setUp() {
+
+ $this->semanticData = $this->getMockBuilder( '\SMW\semanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\ReplicationDataTruncator',
+ new ReplicationDataTruncator()
+ );
+ }
+
+ public function testOnEmptyList() {
+
+ $instance = new ReplicationDataTruncator();
+ $semanticData = $instance->doTruncate( $this->semanticData );
+
+ $this->assertSame(
+ $this->semanticData,
+ $semanticData
+ );
+ }
+
+ public function testOnExemptedList() {
+
+ $property = new DIProperty( 'Foo_bar' );
+
+ $this->semanticData->expects( $this->once() )
+ ->method( 'removeProperty' )
+ ->with( $this->equalTo( $property ) );
+
+ $instance = new ReplicationDataTruncator();
+ $instance->setPropertyExemptionList( [ 'Foo bar' ] );
+
+ $instance->doTruncate( $this->semanticData );
+ }
+
+ public function testOnExemptedListWithPredefinedProperty() {
+
+ $property = new DIProperty( '_ASK' );
+
+ $this->semanticData->expects( $this->once() )
+ ->method( 'removeProperty' )
+ ->with($this->equalTo( $property ) );
+
+ $instance = new ReplicationDataTruncator();
+ $instance->setPropertyExemptionList( [ 'Has query' ] );
+
+ $instance->doTruncate( $this->semanticData );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryClientTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryClientTest.php
new file mode 100644
index 00000000..38fdb133
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryClientTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\SPARQLStore\RepositoryClient;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryClient
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class RepositoryClientTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryClient',
+ new RepositoryClient( '', '', '', '' )
+ );
+ }
+
+ public function testPublicAccess() {
+
+ $instance = new RepositoryClient( 'Foo', 'Bar', 'Nu', 'Vim' );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->getDefaultGraph()
+ );
+
+ $this->assertSame(
+ 'Bar',
+ $instance->getQueryEndpoint()
+ );
+
+ $this->assertSame(
+ 'Nu',
+ $instance->getUpdateEndpoint()
+ );
+
+ $this->assertSame(
+ 'Vim',
+ $instance->getDataEndpoint()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectionProviderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectionProviderTest.php
new file mode 100644
index 00000000..5bfe5fe7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectionProviderTest.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\SPARQLStore\RepositoryConnectionProvider;
+use SMW\Tests\Utils\GlobalsProvider;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectionProvider
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryConnectionProviderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $globalsProvider;
+ private $smwgSparqlCustomConnector;
+ private $smwgSparqlRepositoryConnector;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->globalsProvider = GlobalsProvider::getInstance();
+
+ $this->smwgSparqlRepositoryConnector = $this->globalsProvider->get( 'smwgSparqlRepositoryConnector' );
+ $this->smwgSparqlCustomConnector = $this->globalsProvider->get( 'smwgSparqlCustomConnector' );
+ }
+
+ protected function tearDown() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlRepositoryConnector',
+ $this->smwgSparqlRepositoryConnector
+ );
+
+ $this->globalsProvider->set(
+ 'smwgSparqlCustomConnector',
+ $this->smwgSparqlCustomConnector
+ );
+
+ $this->globalsProvider->clear();
+ }
+
+ public function testCanConstruct() {
+
+ $instance = new RepositoryConnectionProvider();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectionProvider',
+ $instance
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Connection\ConnectionProvider',
+ $instance
+ );
+ }
+
+ public function testGetDefaultConnection() {
+
+ $instance = new RepositoryConnectionProvider( 'default' );
+ $instance->setHttpVersionTo( CURL_HTTP_VERSION_NONE );
+
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabase',
+ $instance->getConnection()
+ );
+
+ $connection = $instance->getConnection();
+
+ $this->assertSame(
+ $connection,
+ $instance->getConnection()
+ );
+
+ $instance->releaseConnection();
+
+ $this->assertNotSame(
+ $connection,
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetFusekiConnection() {
+
+ $instance = new RepositoryConnectionProvider( 'fuSEKi' );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetVirtuosoConnection() {
+
+ $instance = new RepositoryConnectionProvider( 'virtuoso' );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector',
+ $instance->getConnection()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabaseVirtuoso',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGet4StoreConnection() {
+
+ $instance = new RepositoryConnectionProvider( '4STORE' );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector',
+ $instance->getConnection()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabase4Store',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetSesameConnection() {
+
+ $instance = new RepositoryConnectionProvider( 'sesame' );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetGenericConnection() {
+
+ $instance = new RepositoryConnectionProvider( 'generic' );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetDefaultConnectorForUnknownConnectorId() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlRepositoryConnector',
+ 'default'
+ );
+
+ $instance = new RepositoryConnectionProvider( 'foo' );
+
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabase',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetDefaultConnectorForEmptyConnectorId() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlRepositoryConnector',
+ 'default'
+ );
+
+ $instance = new RepositoryConnectionProvider();
+
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabase',
+ $instance->getConnection()
+ );
+ }
+
+ public function testGetDefaultConnectorForUnMappedId() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlRepositoryConnector',
+ 'idThatCanNotBeMapped'
+ );
+
+ $instance = new RepositoryConnectionProvider();
+
+ $this->assertInstanceOf(
+ '\SMWSparqlDatabase',
+ $instance->getConnection()
+ );
+ }
+
+ public function testInvalidCustomClassConnectorThrowsException() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlCustomConnector',
+ 'InvalidCustomClassConnector'
+ );
+
+ $instance = new RepositoryConnectionProvider( 'custom' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection();
+ }
+
+ public function testInvalidCustomRespositoryConnectorThrowsException() {
+
+ $this->globalsProvider->set(
+ 'smwgSparqlCustomConnector',
+ '\SMW\Tests\Utils\Fixtures\InvalidCustomRespositoryConnector'
+ );
+
+ $instance = new RepositoryConnectionProvider( 'custom' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getConnection();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/ElementaryRepositoryConnectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/ElementaryRepositoryConnectorTest.php
new file mode 100644
index 00000000..e0d22e8d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/ElementaryRepositoryConnectorTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryClient;
+use SMW\Tests\Utils\Fixtures\Results\FakeRawResultProvider;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ElementaryRepositoryConnectorTest extends \PHPUnit_Framework_TestCase {
+
+ public function getRepositoryConnectors() {
+ // Legacy and should be removed once obsolete
+ return [
+ 'SMWSparqlDatabase'
+ ];
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameForAskProvider
+ *
+ * @see https://www.w3.org/TR/rdf-sparql-query/#ask
+ */
+ public function testAskToQueryEndpointOnMockedHttpRequest( $httpDatabaseConnector, $expectedPostField ) {
+
+ $rawResultProvider = new FakeRawResultProvider();
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->at( 8 ) )
+ ->method( 'setOption' )
+ ->with(
+ $this->equalTo( CURLOPT_POSTFIELDS ),
+ $this->stringContains( $expectedPostField ) )
+ ->will( $this->returnValue( true ) );
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'execute' )
+ ->will( $this->returnValue( $rawResultProvider->getEmptySparqlResultXml() ) );
+
+ $instance = new $httpDatabaseConnector(
+ new RepositoryClient( 'http://foo/myDefaultGraph', 'http://localhost:9999/query' ),
+ $httpRequest
+ );
+
+ $repositoryResult = $instance->ask(
+ '?x foaf:name "Foo"',
+ [ 'foaf' => 'http://xmlns.com/foaf/0.1/>' ]
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\QueryEngine\RepositoryResult',
+ $repositoryResult
+ );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameForDeleteProvider
+ *
+ * @see http://www.w3.org/TR/sparql11-update/#deleteInsert
+ */
+ public function testDeleteToUpdateEndpointOnMockedHttpRequest( $httpDatabaseConnector, $expectedPostField ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->at( 7 ) )
+ ->method( 'setOption' )
+ ->with(
+ $this->equalTo( CURLOPT_POSTFIELDS ),
+ $this->stringContains( $expectedPostField ) )
+ ->will( $this->returnValue( true ) );
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 0 ) );
+
+ $instance = new $httpDatabaseConnector(
+ new RepositoryClient(
+ 'http://foo/myDefaultGraph',
+ 'http://localhost:9999/query',
+ 'http://localhost:9999/update'
+ ),
+ $httpRequest
+ );
+
+ $this->assertTrue(
+ $instance->delete( 'wiki:Foo ?p ?o', 'wiki:Foo ?p ?o' )
+ );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameForInsertProvider
+ *
+ * @see http://www.w3.org/TR/sparql11-http-rdf-update/#http-post
+ */
+ public function testInsertViaHttpPostToDataPointOnMockedHttpRequest( $httpDatabaseConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->once() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 0 ) );
+
+ $instance = new $httpDatabaseConnector(
+ new RepositoryClient(
+ 'http://foo/myDefaultGraph',
+ 'http://localhost:9999/query',
+ 'http://localhost:9999/update',
+ 'http://localhost:9999/data'
+ ),
+ $httpRequest
+ );
+
+ $this->assertTrue(
+ $instance->insertData( 'property:Foo wiki:Bar;' )
+ );
+ }
+
+ public function httpDatabaseConnectorInstanceNameForAskProvider() {
+
+ $provider = [];
+ $encodedDefaultGraph = urlencode( 'http://foo/myDefaultGraph' );
+
+ foreach ( $this->getRepositoryConnectors() as $repositoryConnector ) {
+
+ switch ( $repositoryConnector ) {
+ case '\SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector':
+ $expectedPostField = '&default-graph-uri=' . $encodedDefaultGraph . '&output=xml';
+ break;
+ case 'SMWSparqlDatabase4Store':
+ case '\SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector':
+ $expectedPostField = "&restricted=1" . '&default-graph-uri=' . $encodedDefaultGraph;
+ break;
+ default:
+ $expectedPostField = '&default-graph-uri=' . $encodedDefaultGraph;
+ break;
+ };
+
+ $provider[] = [ $repositoryConnector, $expectedPostField ];
+ }
+
+ return $provider;
+ }
+
+ public function httpDatabaseConnectorInstanceNameForDeleteProvider() {
+
+ $provider = [];
+
+ foreach ( $this->getRepositoryConnectors() as $repositoryConnector ) {
+
+ switch ( $repositoryConnector ) {
+ case 'SMWSparqlDatabaseVirtuoso':
+ case 'SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector':
+ $expectedPostField = 'query=';
+ break;
+ default:
+ $expectedPostField = 'update=';
+ break;
+ };
+
+ $provider[] = [ $repositoryConnector, $expectedPostField ];
+ }
+
+ return $provider;
+ }
+
+ public function httpDatabaseConnectorInstanceNameForInsertProvider() {
+
+ $provider = [];
+
+ foreach ( $this->getRepositoryConnectors() as $repositoryConnector ) {
+ $provider[] = [ $repositoryConnector ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnectorTest.php
new file mode 100644
index 00000000..00f02bb2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FourstoreRepositoryConnectorTest.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FourstoreRepositoryConnectorTest extends ElementaryRepositoryConnectorTest {
+
+ public function getRepositoryConnectors() {
+ return [
+ 'SMWSparqlDatabase4Store',
+ FourstoreRepositoryConnector::class
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnectorTest.php
new file mode 100644
index 00000000..5e36b0cf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/FusekiRepositoryConnectorTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FusekiRepositoryConnectorTest extends ElementaryRepositoryConnectorTest {
+
+ public function getRepositoryConnectors() {
+ return [
+ FusekiRepositoryConnector::class
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/GenericRepositoryConnectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/GenericRepositoryConnectorTest.php
new file mode 100644
index 00000000..7a5fcafe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/GenericRepositoryConnectorTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class GenericRepositoryConnectorTest extends ElementaryRepositoryConnectorTest {
+
+ public function getRepositoryConnectors() {
+ return [
+ GenericRepositoryConnector::class
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/RepositoryConnectorsExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/RepositoryConnectorsExceptionTest.php
new file mode 100644
index 00000000..eea9843d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/RepositoryConnectorsExceptionTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryClient;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector
+ *
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryConnectorsExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $defaultGraph;
+
+ private $databaseConnectors = [
+ '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector',
+ '\SMW\SPARQLStore\RepositoryConnectors\FusekiRepositoryConnector',
+ '\SMW\SPARQLStore\RepositoryConnectors\FourstoreRepositoryConnector',
+ '\SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector',
+
+ // Legacy and should be removed once obsolete
+ 'SMWSparqlDatabase4Store',
+ 'SMWSparqlDatabaseVirtuoso',
+ 'SMWSparqlDatabase'
+ ];
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->defaultGraph = 'http://foo/myDefaultGraph';
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameProvider
+ */
+ public function testCanConstruct( $httpConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector',
+ new $httpConnector( new RepositoryClient( $this->defaultGraph, '' ), $httpRequest )
+ );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameProvider
+ */
+ public function testDoQueryForEmptyQueryEndpointThrowsException( $httpConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new $httpConnector(
+ new RepositoryClient( $this->defaultGraph, '' ),
+ $httpRequest
+ );
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\BadHttpEndpointResponseException' );
+ $instance->doQuery( '' );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameProvider
+ */
+ public function testDoUpdateForEmptyUpdateEndpointThrowsException( $httpConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new $httpConnector(
+ new RepositoryClient( $this->defaultGraph, '', '' ),
+ $httpRequest
+ );
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\BadHttpEndpointResponseException' );
+ $instance->doUpdate( '' );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameProvider
+ */
+ public function testDoHttpPostForEmptyDataEndpointThrowsException( $httpConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new $httpConnector(
+ new RepositoryClient( $this->defaultGraph, '', '', '' ),
+ $httpRequest
+ );
+
+ $this->setExpectedException( '\SMW\SPARQLStore\Exception\BadHttpEndpointResponseException' );
+ $instance->doHttpPost( '' );
+ }
+
+ /**
+ * @dataProvider httpDatabaseConnectorInstanceNameProvider
+ */
+ public function testDoHttpPostForUnreachableDataEndpointThrowsException( $httpConnector ) {
+
+ $httpRequest = $this->getMockBuilder( '\Onoi\HttpRequest\HttpRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $httpRequest->expects( $this->atLeastOnce() )
+ ->method( 'getLastErrorCode' )
+ ->will( $this->returnValue( 22 ) );
+
+ $instance = new $httpConnector(
+ new RepositoryClient( $this->defaultGraph, '', '', 'unreachableDataEndpoint' ),
+ $httpRequest
+ );
+
+ $this->setExpectedException( 'Exception' );
+ $instance->doHttpPost( '' );
+ }
+
+ public function httpDatabaseConnectorInstanceNameProvider() {
+
+ $provider = [];
+
+ foreach ( $this->databaseConnectors as $databaseConnector ) {
+ $provider[] = [ $databaseConnector ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnectorTest.php
new file mode 100644
index 00000000..3395f66b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryConnectors/VirtuosoRepositoryConnectorTest.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore\RepositoryConnectors;
+
+use SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryConnectors\VirtuosoRepositoryConnector
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class VirtuosoRepositoryConnectorTest extends ElementaryRepositoryConnectorTest {
+
+ public function getRepositoryConnectors() {
+ return [
+ 'SMWSparqlDatabaseVirtuoso',
+ VirtuosoRepositoryConnector::class
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryRedirectLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryRedirectLookupTest.php
new file mode 100644
index 00000000..ed5d9546
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/RepositoryRedirectLookupTest.php
@@ -0,0 +1,292 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\DIWikiPage;
+use SMW\Exporter\Escaper;
+use SMW\InMemoryPoolCache;
+use SMW\SPARQLStore\RepositoryRedirectLookup;
+use SMWExpLiteral as ExpLiteral;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SPARQLStore\RepositoryRedirectLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class RepositoryRedirectLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryRedirectLookup',
+ new RepositoryRedirectLookup( $repositoryConnection )
+ );
+ }
+
+ public function testRedirectTargetForBlankNode() {
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+
+ $expNsResource = new ExpNsResource( '', '', '', null );
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertFalse( $exists );
+ }
+
+ public function testRedirectTargetForDataItemWithSubobject() {
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $dataItem = new DIWikiPage( 'Foo', 1, '', 'beingASubobject' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertTrue( $exists );
+ }
+
+ public function testRedirectTargetForDBLookupWithNoEntry() {
+
+ $repositoryConnection = $this->createRepositoryConnectionMockToUse( false );
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $dataItem = new DIWikiPage( 'Foo', 1, '', '' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertFalse( $exists );
+ }
+
+ public function testRedirectTargetForDBLookupWithSingleEntry() {
+
+ $expLiteral = new ExpLiteral( 'Redirect' );
+
+ $repositoryConnection = $this->createRepositoryConnectionMockToUse( [ $expLiteral ] );
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $instance->reset();
+
+ $dataItem = new DIWikiPage( 'Foo', 1, '', '' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertTrue( $exists );
+ }
+
+ public function testRedirectTargetForDBLookupWithMultipleEntries() {
+
+ $expLiteral = new ExpLiteral( 'Redirect' );
+
+ $repositoryConnection = $this->createRepositoryConnectionMockToUse( [ $expLiteral, null ] );
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $instance->reset();
+
+ $dataItem = new DIWikiPage( 'Foo', 1, '', '' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $this->assertSame(
+ $expNsResource,
+ $instance->findRedirectTargetResource( $expNsResource, $exists )
+ );
+
+ $this->assertTrue( $exists );
+ }
+
+ public function testRedirectTargetForDBLookupWithMultipleEntriesForcesNewResource() {
+
+ $propertyPage = new DIWikiPage( 'Foo', SMW_NS_PROPERTY );
+
+ $resource = new ExpNsResource(
+ 'Foo',
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property',
+ $propertyPage
+ );
+
+ $repositoryConnection = $this->createRepositoryConnectionMockToUse( [ $resource, $resource ] );
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $instance->reset();
+
+ $dataItem = new DIWikiPage( 'Foo', 1, '', '' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $targetResource = $instance->findRedirectTargetResource( $expNsResource, $exists );
+
+ $this->assertNotSame(
+ $expNsResource,
+ $targetResource
+ );
+
+ $expectedResource = new ExpNsResource(
+ Escaper::encodePage( $propertyPage ),
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki'
+ );
+
+ $this->assertEquals(
+ $expectedResource,
+ $targetResource
+ );
+
+ $this->assertTrue( $exists );
+ }
+
+ public function testRedirectTargetForDBLookupWithForNonMultipleResourceEntryThrowsException() {
+
+ $expLiteral = new ExpLiteral( 'Redirect' );
+
+ $repositoryConnection = $this->createRepositoryConnectionMockToUse( [ $expLiteral, $expLiteral ] );
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $instance->reset();
+
+ $dataItem = new DIWikiPage( 'Foo', 1, '', '' );
+
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+ $exists = null;
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->findRedirectTargetResource( $expNsResource, $exists );
+ }
+
+ public function testRedirectTargetForCachedLookup() {
+
+ $dataItem = new DIWikiPage( 'Foo', NS_MAIN );
+ $expNsResource = new ExpNsResource( 'Foo', 'Bar', '', $dataItem );
+
+ $poolCache = InMemoryPoolCache::getInstance()->getPoolCacheById( RepositoryRedirectLookup::POOLCACHE_ID );
+
+ $poolCache->save(
+ $expNsResource->getUri(),
+ $expNsResource
+ );
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+
+ $exists = null;
+
+ $instance->findRedirectTargetResource( $expNsResource, $exists );
+
+ $this->assertTrue( $exists );
+ $instance->reset();
+ }
+
+ /**
+ * @dataProvider nonRedirectableResourceProvider
+ */
+ public function testRedirectTargetForNonRedirectableResource( $expNsResource ) {
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new RepositoryRedirectLookup( $repositoryConnection );
+ $instance->reset();
+
+ $exists = null;
+
+ $instance->findRedirectTargetResource( $expNsResource, $exists );
+ $instance->reset();
+
+ $this->assertFalse( $exists );
+ }
+
+ private function createRepositoryConnectionMockToUse( $listReturnValue ) {
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $repositoryResult->expects( $this->once() )
+ ->method( 'current' )
+ ->will( $this->returnValue( $listReturnValue ) );
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $repositoryConnection->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ return $repositoryConnection;
+ }
+
+ public function nonRedirectableResourceProvider() {
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialPropertyResource( '_INST' )
+ ];
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialPropertyResource( '_SUBC' )
+ ];
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialPropertyResource( '_REDI' )
+ ];
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialPropertyResource( '_MDAT' )
+ ];
+
+ $provider[] = [
+ Exporter::getInstance()->getSpecialPropertyResource( '_MDAT', true )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreFactoryTest.php
new file mode 100644
index 00000000..3253cd01
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreFactoryTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\SPARQLStore\SPARQLStoreFactory;
+
+/**
+ * @covers \SMW\SPARQLStore\SPARQLStoreFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class SPARQLStoreFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $repositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $repositoryConnection ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\SPARQLStoreFactory',
+ new SPARQLStoreFactory( $this->store )
+ );
+ }
+
+ public function testCanConstructBaseStore() {
+
+ $instance = new SPARQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMWSQLStore3',
+ $instance->getBaseStore( 'SMWSQLStore3' )
+ );
+ }
+
+ public function testCanConstructMasterQueryEngine() {
+
+ $instance = new SPARQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->newMasterQueryEngine()
+ );
+ }
+
+ public function testCanConstructConnectionManager() {
+
+ $instance = new SPARQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\Connection\ConnectionManager',
+ $instance->getConnectionManager()
+ );
+ }
+
+ public function testCanConstructRepositoryRedirectLookup() {
+
+ $instance = new SPARQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\RepositoryRedirectLookup',
+ $instance->newRepositoryRedirectLookup()
+ );
+ }
+
+ public function testCanConstructReplicationDataTruncator() {
+
+ $instance = new SPARQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\ReplicationDataTruncator',
+ $instance->newReplicationDataTruncator()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreTest.php
new file mode 100644
index 00000000..20407192
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/SPARQLStoreTest.php
@@ -0,0 +1,358 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SPARQLStore\SPARQLStore;
+use SMW\Subobject;
+use SMW\Tests\TestEnvironment;
+use SMWExporter as Exporter;
+use SMWTurtleSerializer as TurtleSerializer;
+use Title;
+
+/**
+ * @covers \SMW\SPARQLStore\SPARQLStore
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class SPARQLStoreTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+
+ protected function setUp() {
+ parent::setup();
+
+ $testEnvironment = new TestEnvironment();
+ $this->semanticDataFactory = $testEnvironment->getUtilityFactory()->newSemanticDataFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\SPARQLStore',
+ new SPARQLStore()
+ );
+
+ // Legacy
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\SPARQLStore',
+ new \SMWSPARQLStore()
+ );
+ }
+
+ public function testGetSemanticDataOnMockBaseStore() {
+
+ $subject = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $baseStore = $this->getMockBuilder( '\SMWStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $baseStore->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->with( $this->equalTo( $subject ) )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $instance = new SPARQLStore( $baseStore );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $instance->getSemanticData( $subject )
+ );
+ }
+
+ public function testDeleteSubjectOnMockBaseStore() {
+
+ $title = Title::newFromText( 'DeleteSubjectOnMockBaseStore' );
+
+ $expResource = Exporter::getInstance()->getDataItemExpElement( DIWikiPage::newFromTitle( $title ) );
+ $resourceUri = TurtleSerializer::getTurtleNameForExpElement( $expResource );
+
+ $extraNamespaces = [
+ $expResource->getNamespaceId() => $expResource->getNamespace()
+ ];
+
+ $baseStore = $this->getMockBuilder( '\SMWStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $baseStore->expects( $this->once() )
+ ->method( 'deleteSubject' )
+ ->with( $this->equalTo( $title ) )
+ ->will( $this->returnValue( true ) );
+
+ $sparqlDatabase = $this->getMockBuilder( '\SMWSparqlDatabase' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $sparqlDatabase->expects( $this->once() )
+ ->method( 'deleteContentByValue' )
+ ->will( $this->returnValue( true ) );
+
+ $sparqlDatabase->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->equalTo( "{$resourceUri} ?p ?o" ),
+ $this->equalTo( "{$resourceUri} ?p ?o" ),
+ $this->equalTo( $extraNamespaces ) )
+ ->will( $this->returnValue( true ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $sparqlDatabase ) );
+
+ $instance = new SPARQLStore( $baseStore );
+ $instance->setConnectionManager( $connectionManager );
+
+ $instance->deleteSubject( $title );
+ }
+
+ public function testDoSparqlDataUpdateOnMockBaseStore() {
+
+ $semanticData = new SemanticData( new DIWikiPage( __METHOD__, NS_MAIN ) );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( 'Foo' ),
+ $semanticData->getSubject()
+ );
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $baseStore = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $sparqlDatabase = $this->getMockBuilder( '\SMWSparqlDatabase' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $sparqlDatabase->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $sparqlDatabase->expects( $this->once() )
+ ->method( 'insertData' );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $sparqlDatabase ) );
+
+ $instance = new SPARQLStore( $baseStore );
+ $instance->setConnectionManager( $connectionManager );
+
+ $instance->doSparqlDataUpdate( $semanticData );
+ }
+
+ public function testCallToChangeTitleForCompletePageMove() {
+
+ $oldTitle = Title::newFromText( __METHOD__ . '-old' );
+ $newTitle = Title::newFromText( __METHOD__ . '-new' );
+
+ $respositoryConnection = $this->getMockBuilder( '\SMW\SPARQLStore\RespositoryConnection' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'insertDelete' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'changeTitle' );
+
+ $instance = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->setConstructorArgs( [ $store ] )
+ ->setMethods( [ 'doSparqlDataDelete', 'getConnection' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $respositoryConnection ) );
+
+ $instance->expects( $this->once() )
+ ->method( 'doSparqlDataDelete' )
+ ->with( $this->equalTo( DIWikiPage::newFromTitle( $oldTitle ) ) );
+
+ $instance->changeTitle( $oldTitle, $newTitle, 42, 0 );
+ }
+
+ public function testNoDeleteTaskForSubobjectsDuringUpdate() {
+
+ $expectedSubjectForDeleteTask = DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) );
+
+ $subobject = new Subobject( $expectedSubjectForDeleteTask->getTitle() );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( $expectedSubjectForDeleteTask )
+ ->newEmptySemanticData();
+
+ $semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $repositoryResult = $this->getMockBuilder( '\SMW\SPARQLStore\QueryEngine\RepositoryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnectors\GenericRepositoryConnector' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $repositoryResult ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'insertData' );
+
+ $instance = $this->getMockBuilder( '\SMW\SPARQLStore\SPARQLStore' )
+ ->setMethods( [ 'doSparqlDataDelete', 'getConnection' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'doSparqlDataDelete' )
+ ->with( $this->equalTo( $expectedSubjectForDeleteTask ) );
+
+ $instance->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance->doSparqlDataUpdate( $semanticData );
+ }
+
+ public function testGetQueryResult() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getPrintrequests' )
+ ->will( $this->returnValue( [] ) );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $idLookup = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'warmUpCache' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idLookup ) );
+
+ $repositoryClient = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryClient' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'getRepositoryClient' )
+ ->will( $this->returnValue( $repositoryClient ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SPARQLStore( $store );
+ $instance->setConnectionManager( $connectionManager );
+
+ $instance->getQueryResult( $query );
+ }
+
+ public function testGetQueryResultOnDisabledQueryEndpoint() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->once() )
+ ->method( 'getQueryResult' );
+
+ $repositoryClient = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryClient' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $repositoryClient->expects( $this->atLeastOnce() )
+ ->method( 'getQueryEndpoint' )
+ ->will( $this->returnValue( false ) );
+
+ $connection = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryConnection' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'getRepositoryClient' )
+ ->will( $this->returnValue( $repositoryClient ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SPARQLStore( $store );
+ $instance->setConnectionManager( $connectionManager );
+
+ $instance->getQueryResult( $query );
+ }
+
+ public function testGetPropertyTableIdReferenceFinder() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTableIdReferenceFinder' );
+
+ $instance = new SPARQLStore( $store );
+ $instance->getPropertyTableIdReferenceFinder();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/TurtleTriplesBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/TurtleTriplesBuilderTest.php
new file mode 100644
index 00000000..e7777a45
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SPARQLStore/TurtleTriplesBuilderTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\SPARQLStore;
+
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SPARQLStore\TurtleTriplesBuilder;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+
+/**
+ * @covers \SMW\SPARQLStore\TurtleTriplesBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TurtleTriplesBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $repositoryRedirectLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->repositoryRedirectLookup = $this->getMockBuilder( '\SMW\SPARQLStore\RepositoryRedirectLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SPARQLStore\TurtleTriplesBuilder',
+ new TurtleTriplesBuilder( $this->repositoryRedirectLookup )
+ );
+ }
+
+ public function testBuildTriplesForEmptySemanticDataContainer() {
+
+ $expNsResource = new ExpNsResource(
+ 'Redirect',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'Redirect'
+ );
+
+ $semanticData = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN, '' )
+ );
+
+ $this->repositoryRedirectLookup->expects( $this->atLeastOnce() )
+ ->method( 'findRedirectTargetResource' )
+ ->will( $this->returnValue( $expNsResource ) );
+
+ $instance = new TurtleTriplesBuilder(
+ $this->repositoryRedirectLookup
+ );
+
+ $instance->doBuildTriplesFrom( $semanticData );
+
+ $this->assertTrue(
+ $instance->hasTriples()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getTriples()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getPrefixes()
+ );
+ }
+
+ public function testChunkedTriples() {
+
+ $expNsResource = new ExpNsResource(
+ 'Redirect',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'Redirect'
+ );
+
+ $semanticData = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $this->repositoryRedirectLookup->expects( $this->atLeastOnce() )
+ ->method( 'findRedirectTargetResource' )
+ ->will( $this->returnValue( $expNsResource ) );
+
+ $instance = new TurtleTriplesBuilder(
+ $this->repositoryRedirectLookup
+ );
+
+ $instance->setTriplesChunkSize( 1 );
+ $instance->doBuildTriplesFrom( $semanticData );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getChunkedTriples()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeDiffTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeDiffTest.php
new file mode 100644
index 00000000..a9c99e58
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeDiffTest.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace SMW\Tests\SQLStore\ChangeOp;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\ChangeOp\ChangeDiff;
+
+/**
+ * @covers \SMW\SQLStore\ChangeOp\ChangeDiff
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangeDiffTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChangeDiff::class,
+ new ChangeDiff( DIWikiPage::newFromText( 'Foo' ), [], [], [] )
+ );
+ }
+
+ public function testGetSubject() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+ $instance = new ChangeDiff(
+ $subject,
+ [],
+ [],
+ []
+ );
+
+ $this->assertEquals(
+ $subject,
+ $instance->getSubject()
+ );
+ }
+
+ public function testGetPropertyList() {
+
+ $instance = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [],
+ [],
+ [ 'Foo' => 42 ]
+ );
+
+ $this->assertEquals(
+ [ 'Foo' => 42 ],
+ $instance->getPropertyList()
+ );
+
+ $this->assertEquals(
+ [ 42 => 'Foo' ],
+ $instance->getPropertyList( true )
+ );
+ }
+
+ public function testGetPropertyList_SortById() {
+
+ $instance = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [],
+ [],
+ [ 'Foo' => [ '_id' => 42, '_type' => '_foo' ] ]
+ );
+
+ $this->assertEquals(
+ [ 42 => [ '_key' => 'Foo', '_type' => '_foo' ] ],
+ $instance->getPropertyList( 'id' )
+ );
+ }
+
+ public function testSave() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableChangeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\TableChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [ $tableChangeOp ],
+ [],
+ [ 'Foo' => 42 ]
+ );
+
+ $cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with(
+ $this->stringContains( ChangeDiff::CACHE_NAMESPACE ),
+ $this->equalTo( $instance->serialize() ) );
+
+ $instance->save( $cache );
+ }
+
+ public function testFetch() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableChangeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\TableChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [ $tableChangeOp ],
+ [],
+ [ 'Foo' => 42 ]
+ );
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( $instance->serialize() ) );
+
+ $this->assertEquals(
+ $instance,
+ ChangeDiff::fetch( $cache, $subject )
+ );
+ }
+
+ public function testChangeList() {
+
+ $instance = new ChangeDiff(
+ DIWikiPage::newFromText( 'Foo' ),
+ [],
+ [],
+ []
+ );
+
+ $instance->setChangeList( 'Foo', [ '42', 1001 ] );
+
+ $this->assertEquals(
+ [ '42', 1001 ],
+ $instance->getChangeListByType( 'Foo' )
+ );
+ }
+
+ public function FetchFromCache() {
+
+ $changeDiff = ChangeDiff::fetch(
+ \SMW\ApplicationFactory::getInstance()->getCache(),
+ DIWikiPage::newFromText( 'DifferentSort' )
+ );
+
+ $this->assertInstanceOf(
+ ChangeDiff::class,
+ $changeDiff
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeOpTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeOpTest.php
new file mode 100644
index 00000000..8aea75da
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/ChangeOpTest.php
@@ -0,0 +1,412 @@
+<?php
+
+namespace SMW\Tests\SQLStore\ChangeOp;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+
+/**
+ * @covers \SMW\SQLStore\ChangeOp\ChangeOp
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class ChangeOpTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChangeOp::class,
+ new ChangeOp()
+ );
+ }
+
+ public function testSerialize() {
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ []
+ );
+
+ $data = serialize( $instance );
+
+ $this->assertInstanceOf(
+ ChangeOp::class,
+ unserialize( $data )
+ );
+ }
+
+ /**
+ * @dataProvider diffDataProvider
+ */
+ public function testDiff( $diff, $fixedPropertyRecord, $expectedOrdered, $expectedList ) {
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ $diff
+ );
+
+ $instance->addFixedPropertyRecord(
+ $fixedPropertyRecord[0],
+ $fixedPropertyRecord[1]
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFixedPropertyRecords()
+ );
+
+ $this->assertEquals(
+ $expectedOrdered,
+ $instance->getOrderedDiffByTable()
+ );
+
+ $this->assertEquals(
+ $expectedList,
+ $instance->getChangedEntityIdSummaryList()
+ );
+ }
+
+ public function testGetTableChangeOps() {
+
+ $diff = [
+ 0 => [
+ 'insert' => [
+ 'smw_di_number' => [
+ 0 => [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ 1 => [
+ 's_id' => 3668,
+ 'p_id' => 62,
+ 'o_serialized' => '1234',
+ 'o_sortkey' => '1234',
+ ],
+ ],
+ 'smw_fpt_mdat' => [
+ 0 => [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ 'delete' => [
+ 'smw_di_number' => [],
+ 'smw_fpt_mdat' => [],
+ ]
+ ]
+ ];
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ $diff
+ );
+
+ $this->assertCount(
+ 1,
+ $instance->getTableChangeOps( 'smw_di_number' )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableChangeOps()
+ );
+ }
+
+ public function testGetListOfChangedEntitiesByType() {
+
+ $diff = [
+ 0 => [
+ 'insert' => [
+ 'smw_di_number' => [
+ 0 => [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ 1 => [
+ 's_id' => 3668,
+ 'p_id' => 62,
+ 'o_serialized' => '1234',
+ 'o_sortkey' => '1234',
+ ],
+ ],
+ 'smw_fpt_mdat' => [
+ 0 => [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ 'delete' => [
+ 'smw_di_number' => [],
+ 'smw_fpt_mdat' => [
+ 0 => [
+ 's_id' => 3667,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ]
+ ],
+ ]
+ ]
+ ];
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ $diff
+ );
+
+ $this->assertEquals(
+ [
+ 3667 => true
+ ],
+ $instance->getChangedEntityIdListByType( $instance::OP_DELETE )
+ );
+
+ $this->assertEquals(
+ [
+ 61 => true,
+ 3668 => true,
+ 62 => true
+ ],
+ $instance->getChangedEntityIdListByType( $instance::OP_INSERT )
+ );
+ }
+
+ public function testTryToGetTableChangeOpForSingleTable() {
+
+ $diff = [];
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ $diff
+ );
+
+ $this->assertEmpty(
+ $instance->getTableChangeOps( 'smw_di_number' )
+ );
+ }
+
+ public function testGetHash() {
+
+ $diff = [];
+
+ $instance = new ChangeOp(
+ DIWikiPage::newFromText( __METHOD__ ),
+ $diff
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHash()
+ );
+ }
+
+ public function diffDataProvider() {
+
+ $provider[] = [
+ [],
+ [ 'foo', [] ],
+ [],
+ [],
+ ];
+
+ // Insert
+ $provider[] = [
+ [
+ 0 => [
+ 'insert' => [
+ 'smw_di_number' =>
+ [ 0 => [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ ],
+ 'smw_fpt_mdat' =>
+ [ 0 => [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ 'delete' => [
+ 'smw_di_number' =>
+ [],
+ 'smw_fpt_mdat' =>
+ [],
+ ],
+ ],
+ ],
+ [
+ 'smw_fpt_mdat',
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ ],
+ [
+ 'smw_di_number' => [
+ 'insert' =>
+ [ 0 => [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ ],
+ ],
+ 'smw_fpt_mdat' => [
+ 'property' =>
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ 'insert' => [
+ 0 => [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ ],
+ [
+ 0 => 61,
+ 1 => 3668,
+ 2 => 29,
+ ]
+ ];
+
+ // Insert / Delete
+ $provider[] = [
+ [
+ 0 =>
+ [
+ 'insert' =>
+ [
+ 'smw_di_number' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '1001',
+ 'o_sortkey' => '1001',
+ ],
+ ],
+ 'smw_fpt_mdat' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/56/53',
+ 'o_sortkey' => '2457250.9145023',
+ ],
+ ],
+ ],
+ 'delete' =>
+ [
+ 'smw_di_number' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ ],
+ 'smw_fpt_mdat' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ ],
+ ],
+ [
+ 'smw_fpt_mdat',
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ ],
+ [
+ 'smw_di_number' =>
+ [
+ 'insert' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '1001',
+ 'o_sortkey' => '1001',
+ ],
+ ],
+ 'delete' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'p_id' => 61,
+ 'o_serialized' => '123',
+ 'o_sortkey' => '123',
+ ],
+ ],
+ ],
+ 'smw_fpt_mdat' =>
+ [
+ 'property' =>
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ 'insert' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/56/53',
+ 'o_sortkey' => '2457250.9145023',
+ ],
+ ],
+ 'delete' =>
+ [
+ 0 =>
+ [
+ 's_id' => 3668,
+ 'o_serialized' => '1/2015/8/16/9/28/39',
+ 'o_sortkey' => '2457250.8948958',
+ ],
+ ],
+ ],
+ ],
+ [
+ 0 => 61,
+ 1 => 3668,
+ 2 => 29,
+ ]
+ ];
+
+ return $provider;
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/FieldChangeOpTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/FieldChangeOpTest.php
new file mode 100644
index 00000000..690084c8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/FieldChangeOpTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SMW\Tests\SQLStore\ChangeOp;
+
+use SMW\SQLStore\ChangeOp\FieldChangeOp;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\ChangeOp\FieldChangeOp
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class FieldChangeOpTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ FieldChangeOp::class,
+ new FieldChangeOp()
+ );
+ }
+
+ public function testChangeOp() {
+
+ $op = [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/3/31/0',
+ 'o_sortkey' => '2457549.5857755',
+ ];
+
+ $instance = new FieldChangeOp(
+ $op
+ );
+
+ $this->assertFalse(
+ $instance->has( 'foo' )
+ );
+
+ $this->assertSame(
+ '1/2016/6/10/2/3/31/0',
+ $instance->get( 'o_serialized' )
+ );
+
+ $instance->set( 'foo', 'bar' );
+
+ $this->assertSame(
+ 'bar',
+ $instance->get( 'foo' )
+ );
+ }
+
+ public function testInvalidGetRequestThrowsException() {
+
+ $instance = new FieldChangeOp();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->get( 'o_serialized' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/TableChangeOpTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/TableChangeOpTest.php
new file mode 100644
index 00000000..cc5b2857
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangeOp/TableChangeOpTest.php
@@ -0,0 +1,226 @@
+<?php
+
+namespace SMW\Tests\SQLStore\ChangeOp;
+
+use SMW\SQLStore\ChangeOp\TableChangeOp;
+
+/**
+ * @covers \SMW\SQLStore\ChangeOp\TableChangeOp
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class TableChangeOpTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TableChangeOp::class,
+ new TableChangeOp( 'foo', [] )
+ );
+ }
+
+ public function testEmptyOps() {
+
+ $diff = [];
+
+ $instance = new TableChangeOp(
+ 'foo',
+ $diff
+ );
+
+ $this->assertSame(
+ 'foo',
+ $instance->getTableName()
+ );
+
+ $this->assertFalse(
+ $instance->isFixedPropertyOp()
+ );
+
+ $this->assertFalse(
+ $instance->hasChangeOp( TableChangeOp::OP_INSERT )
+ );
+
+ $this->assertNull(
+ $instance->getFixedPropertyValueBy( 'key' )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFieldChangeOps( 'insert' )
+ );
+ }
+
+ public function testFixedPropertyOps() {
+
+ $diff = [
+ 'property' =>
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ 'insert' =>
+ [
+ 0 =>
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/3/31/0',
+ 'o_sortkey' => '2457549.5857755',
+ ],
+ ],
+ 'delete' =>
+ [
+ 0 =>
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/1/0/0',
+ 'o_sortkey' => '2457549.5840278',
+ ],
+ ],
+ ];
+
+ $instance = new TableChangeOp(
+ 'foo',
+ $diff
+ );
+
+ $this->assertSame(
+ 'foo',
+ $instance->getTableName()
+ );
+
+ $this->assertTrue(
+ $instance->isFixedPropertyOp()
+ );
+
+ $this->assertTrue(
+ $instance->hasChangeOp( TableChangeOp::OP_INSERT )
+ );
+
+ $this->assertSame(
+ '_MDAT',
+ $instance->getFixedPropertyValueBy( 'key' )
+ );
+ }
+
+ public function testGetFieldChangeOpsNoType() {
+
+ $diff = [
+ 'property' =>
+ [
+ 'key' => '_MDAT',
+ 'p_id' => 29,
+ ],
+ 'insert' =>
+ [
+ 0 =>
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/3/31/0',
+ 'o_sortkey' => '2457549.5857755',
+ ],
+ ],
+ 'delete' =>
+ [
+ 0 =>
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/1/0/0',
+ 'o_sortkey' => '2457549.5840278',
+ ],
+ [
+ 's_id' => 42,
+ 'p_id' => 1001,
+ 'o_id' => 9999
+ ]
+ ]
+ ];
+
+ $instance = new TableChangeOp(
+ 'foo',
+ $diff
+ );
+
+ $this->assertCount(
+ 3,
+ $instance->getFieldChangeOps()
+ );
+
+ $this->assertCount(
+ 1,
+ $instance->getFieldChangeOps( TableChangeOp::OP_INSERT )
+ );
+
+ $this->assertCount(
+ 2,
+ $instance->getFieldChangeOps( TableChangeOp::OP_DELETE )
+ );
+
+ $this->assertCount(
+ 2,
+ $instance->getFieldChangeOps( null, [ 's_id' => [ 42 => true ] ] )
+ );
+ }
+
+ public function testFieldChangeOps_WithNoOperation() {
+
+ $diff = [
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/1/0/0',
+ 'o_sortkey' => '2457549.5840278',
+ ],
+ [
+ 's_id' => 42,
+ 'p_id' => 1001,
+ 'o_id' => 9999
+ ]
+ ];
+
+ $instance = new TableChangeOp(
+ 'foo',
+ $diff
+ );
+
+ $this->assertCount(
+ 2,
+ $instance->getFieldChangeOps()
+ );
+
+ $this->assertCount(
+ 1,
+ $instance->getFieldChangeOps( null, [ 's_id' => [ 42 => true ] ] )
+ );
+ }
+
+ public function testToArray() {
+
+ $diff = [
+ [
+ 's_id' => 462,
+ 'o_serialized' => '1/2016/6/10/2/1/0/0',
+ 'o_sortkey' => '2457549.5840278',
+ ],
+ [
+ 's_id' => 42,
+ 'p_id' => 1001,
+ 'o_id' => 9999
+ ]
+ ];
+
+ $instance = new TableChangeOp(
+ 'foo',
+ $diff
+ );
+
+ $this->assertSame(
+ ['foo' => $diff ],
+ $instance->toArray()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangePropagationEntityFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangePropagationEntityFinderTest.php
new file mode 100644
index 00000000..dad6fefb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ChangePropagationEntityFinderTest.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\ChangePropagationEntityFinder;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\ChangePropagationEntityFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ChangePropagationEntityFinderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $iteratorFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->iteratorFactory = $this->getMockBuilder( '\SMW\IteratorFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->appendIterator = $this->getMockBuilder( '\SMW\Iterators\AppendIterator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ChangePropagationEntityFinder::class,
+ new ChangePropagationEntityFinder( $this->store, $this->iteratorFactory )
+ );
+ }
+
+ public function testFindByProperty() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $this->iteratorFactory->expects( $this->any() )
+ ->method( 'newAppendIterator' )
+ ->will( $this->returnValue( $this->appendIterator ) );
+
+ $instance = new ChangePropagationEntityFinder(
+ $this->store,
+ $this->iteratorFactory
+ );
+
+ $this->assertInstanceOf(
+ 'Iterator',
+ $instance->findByProperty( $property )
+ );
+ }
+
+ public function testFindByProperty_TypePropagation() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $this->iteratorFactory->expects( $this->any() )
+ ->method( 'newAppendIterator' )
+ ->will( $this->returnValue( $this->appendIterator ) );
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher->expects( $this->any() )
+ ->method( 'getDefaultDataItemTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $entityIdManager = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'getDataItemPoolHashListFor' ] )
+ ->getMock();
+
+ $entityIdManager->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $entityIdManager->expects( $this->any() )
+ ->method( 'getDataItemPoolHashListFor' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityIdManager ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $instance = new ChangePropagationEntityFinder(
+ $store,
+ $this->iteratorFactory
+ );
+
+ $instance->isTypePropagation( true );
+
+ $res = $instance->findByProperty( $property );
+
+ $this->assertInstanceOf(
+ 'Iterator',
+ $res
+ );
+
+ $this->assertEquals(
+ $res,
+ $instance->findAll( $property )
+ );
+ }
+
+ public function testFindByCategory() {
+
+ $category = new DIWikiPage( 'Foo', NS_CATEGORY );
+
+ $this->iteratorFactory->expects( $this->any() )
+ ->method( 'newAppendIterator' )
+ ->will( $this->returnValue( $this->appendIterator ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( new DIProperty( '_INST' ) ),
+ $this->anything() )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( new DIProperty( '_SUBC' ) ) )
+ ->will( $this->returnValue( [ DIWikiPage::newFromText( 'Bar' ) ] ) );
+
+ $instance = new ChangePropagationEntityFinder(
+ $store,
+ $this->iteratorFactory
+ );
+
+ $res = $instance->findByCategory( $category );
+
+ $this->assertInstanceOf(
+ 'Iterator',
+ $res
+ );
+
+ $this->assertEquals(
+ $res,
+ $instance->findAll( $category )
+ );
+ }
+
+ public function testFindAllOnUnknownTypeThrowsException() {
+
+ $instance = new ChangePropagationEntityFinder(
+ $this->store,
+ $this->iteratorFactory
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->findAll( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ConceptCacheTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ConceptCacheTest.php
new file mode 100644
index 00000000..e7c32c00
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/ConceptCacheTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\ConceptCache;
+use Title;
+
+/**
+ * @covers \SMW\SQLStore\ConceptCache
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ConceptCacheTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $conceptQuerySegmentBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->conceptQuerySegmentBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\ConceptCache',
+ new ConceptCache( $this->store, $this->conceptQuerySegmentBuilder )
+ );
+ }
+
+ public function testRefreshConceptCache() {
+
+ $this->conceptQuerySegmentBuilder->expects( $this->once() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new ConceptCache(
+ new \SMWSQLStore3(),
+ $this->conceptQuerySegmentBuilder
+ );
+
+ $instance->refreshConceptCache(
+ Title::newFromText( 'Foo', SMW_NS_CONCEPT )
+ );
+ }
+
+ public function testDeleteConceptCache() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'delete' );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = new \SMWSQLStore3();
+ $store->setConnectionManager( $connectionManager );
+
+ $instance = new ConceptCache(
+ $store,
+ $this->conceptQuerySegmentBuilder
+ );
+
+ $instance->deleteConceptCache(
+ Title::newFromText( 'Foo', SMW_NS_CONCEPT )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityRebuildDispatcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityRebuildDispatcherTest.php
new file mode 100644
index 00000000..15583175
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityRebuildDispatcherTest.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMW\SQLStore\EntityRebuildDispatcher;
+use SMW\SQLStore\SQLStore;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\EntityRebuildDispatcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class EntityRebuildDispatcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $titleFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'smwgSemanticsEnabled' => true,
+ 'smwgAutoRefreshSubject' => true,
+ 'smwgCacheType' => 'hash',
+ 'smwgEnableUpdateJobs' => false,
+ ]
+ );
+
+ $jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->titleFactory = $this->getMockBuilder( '\SMW\MediaWiki\TitleFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $jobQueue );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'exists' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( 0 ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityRebuildDispatcher',
+ new EntityRebuildDispatcher( $store, $this->titleFactory )
+ );
+ }
+
+ /**
+ * @dataProvider idProvider
+ */
+ public function testDispatchRebuildForSingleIteration( $id, $expected ) {
+
+ $this->titleFactory->expects( $this->any() )
+ ->method( 'newFromIDs' )
+ ->will( $this->returnValue( [] ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'selectField' )
+ ->will( $this->returnValue( $expected ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new EntityRebuildDispatcher(
+ $store,
+ $this->titleFactory
+ );
+
+ $instance->setDispatchRangeLimit( 1 );
+ $instance->setOptions(
+ [
+ 'shallow-update' => true,
+ 'use-job' => false
+ ]
+ );
+
+ $instance->rebuild( $id );
+
+ $this->assertSame(
+ $expected,
+ $id
+ );
+
+ $this->assertLessThanOrEqual(
+ 1,
+ $instance->getEstimatedProgress()
+ );
+ }
+
+ public function testRevisionMode() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( NS_MAIN ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->titleFactory->expects( $this->any() )
+ ->method( 'newFromIDs' )
+ ->will( $this->returnValue( [ $title ] ) );
+
+ $row = [
+ 'smw_id' => 9999999999999999,
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => 0,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_proptable_hash' => [],
+ 'smw_rev' => 0
+ ];
+
+ $idTable = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'findAssociatedRev' )
+ ->with( $this->equalTo( 'Foo' ) )
+ ->will( $this->returnValue( 1001 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ (object)$row] ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'selectField' )
+ ->will( $this->returnValue( 500 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new EntityRebuildDispatcher(
+ $store,
+ $this->titleFactory
+ );
+
+ $instance->setDispatchRangeLimit( 1 );
+ $instance->setOptions(
+ [
+ 'revision-mode' => true,
+ 'force-update' => false,
+ 'use-job' => false
+ ]
+ );
+
+ $id = 999999999;
+
+ $instance->rebuild( $id );
+ }
+
+ public function idProvider() {
+
+ $provider[] = [
+ 42, // Within the border Id
+ 43
+ ];
+
+ $provider[] = [
+ 9999999999999999999,
+ -1
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingEntityLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingEntityLookupTest.php
new file mode 100644
index 00000000..465084f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingEntityLookupTest.php
@@ -0,0 +1,387 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\EntityStore\CachingEntityLookup;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\CachingEntityLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class CachingEntityLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $entityLookup;
+ private $redirectTargetLookup;
+ private $blobStore;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->entityLookup = $this->getMockBuilder( '\SMW\EntityLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->redirectTargetLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\RedirectTargetLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore = $this->getMockBuilder( '\Onoi\BlobStore\BlobStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CachingEntityLookup::class,
+ new CachingEntityLookup( $this->entityLookup, $this->redirectTargetLookup, $this->blobStore )
+ );
+ }
+
+ public function testGetSemanticDataFromFallbackForDisabledFeature() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->entityLookup->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->anything() );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_PL );
+ $instance->getSemanticData( $subject );
+ }
+
+ public function testGetSemanticDataFromFallbackForDisabledBlobStore() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->entityLookup->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->anything() );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->getSemanticData( $subject );
+ }
+
+ public function testGetSemanticDataFromFallbackForNonAvailableContainer() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'setOption' )
+ ->with(
+ $this->equalTo( $semanticData::OPT_LAST_MODIFIED ),
+ $this->anything() );
+
+ $this->entityLookup->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->anything() )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( false ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->exactly( 2 ) )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->blobStore->expects( $this->exactly( 2 ) )
+ ->method( 'save' );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_SD );
+ $instance->getSemanticData( $subject );
+ }
+
+ public function testGetSemanticDataFromCacheForAvailableHash() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $filter = false;
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with($this->stringContains( 'sd:' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_SD );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getSemanticData( $subject, $filter )
+ );
+ }
+
+ public function testGetPropertiesFromCacheForAvailableHash() {
+
+ $expected = [
+ new DIProperty( 'Bar' )
+ ];
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->redirectTargetLookup->expects( $this->once() )
+ ->method( 'findRedirectTarget' )
+ ->will( $this->returnValue( new DIProperty( 'Bar' ) ) );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with($this->stringContains( 'pl:' ) )
+ ->will( $this->returnValue( $expected ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_PL );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getProperties( $subject )
+ );
+ }
+
+ public function testGetPropertyValuesFromCacheForAvailableHash() {
+
+ $expected = [
+ new DIWikiPage( 'Bar', NS_MAIN )
+ ];
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->redirectTargetLookup->expects( $this->once() )
+ ->method( 'findRedirectTarget' )
+ ->will( $this->returnValue( new DIWikiPage( 'Bar', NS_MAIN ) ) );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with($this->stringContains( 'pv:' ) )
+ ->will( $this->returnValue( $expected ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_PV );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getPropertyValues( $subject, new DIProperty( 'Foobar' ) )
+ );
+ }
+
+ public function testGetPropertySubjectsFromCacheForAvailableHash() {
+
+ $expected = [
+ new DIWikiPage( 'Bar', NS_MAIN )
+ ];
+
+ $dataItem = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $this->redirectTargetLookup->expects( $this->never() )
+ ->method( 'findRedirectTarget' );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->once() )
+ ->method( 'get' )
+ ->with($this->stringContains( 'ps:' ) )
+ ->will( $this->returnValue( $expected ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_PS );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getPropertySubjects( new DIProperty( 'Foobar' ), $dataItem )
+ );
+ }
+
+ public function tesClearCache() {
+
+ $subject = new DIWikiPage( 'Foobar', NS_MAIN, '', 'abc' );
+
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_REDI' ),
+ new DIWikiPage( 'Bar', NS_MAIN )
+ );
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container->expects( $this->at( 0 ) )
+ ->method( 'has' )
+ ->with( $this->stringContains( 'sd:' ) )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->at( 1 ) )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'sd:' ) )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $container->expects( $this->at( 2 ) )
+ ->method( 'has' )
+ ->with( $this->stringContains( 'list' ) )
+ ->will( $this->returnValue( true ) );
+
+ $container->expects( $this->at( 3 ) )
+ ->method( 'get' )
+ ->with( $this->stringContains( 'list' ) )
+ ->will( $this->returnValue( [ 'abc', '123' ] ) );
+
+ $this->blobStore->expects( $this->any() )
+ ->method( 'canUse' )
+ ->will( $this->returnValue( true ) );
+
+ $this->blobStore->expects( $this->atLeastOnce() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $this->blobStore->expects( $this->exactly( 4 ) )
+ ->method( 'delete' );
+
+ $instance = new CachingEntityLookup(
+ $this->entityLookup,
+ $this->redirectTargetLookup,
+ $this->blobStore
+ );
+
+ $instance->setLookupFeatures( SMW_VL_SD );
+ $instance->invalidateCache( $subject );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingSemanticDataLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingSemanticDataLookupTest.php
new file mode 100644
index 00000000..3cd22427
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/CachingSemanticDataLookupTest.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\CachingSemanticDataLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\CachingSemanticDataLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CachingSemanticDataLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $connection;
+ private $semanticDataLookup;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->semanticDataLookup = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\SemanticDataLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function tearDown() {
+ CachingSemanticDataLookup::clear();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ CachingSemanticDataLookup::class,
+ new CachingSemanticDataLookup( $this->semanticDataLookup )
+ );
+ }
+
+ public function testInitLookupCache() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $stubSemanticData = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\StubSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSemanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'newStubSemanticData' )
+ ->will( $this->returnValue( $stubSemanticData ) );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->initLookupCache( 42, $subject );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\StubSemanticData',
+ $instance->getSemanticDataById( 42 )
+ );
+ }
+
+ public function testUninitializedGetSemanticDataByIdThrowsException() {
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->getSemanticDataById( 42 );
+ }
+
+ public function testSetLookupCache() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'newStubSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'getTableUsageInfo' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->setLookupCache( 42, $semanticData );
+
+ $this->assertEquals(
+ $semanticData,
+ $instance->getSemanticDataById( 42 )
+ );
+ }
+
+ public function testGetOptionsFromConstraint() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'newRequestOptions' );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->newRequestOptions( $propertyTableDefinition, $property );
+ }
+
+ public function testFetchSemanticData() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'fetchSemanticData' );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->fetchSemanticData( 42, null, $propertyTableDefinition );
+ }
+
+ public function testGetSemanticDataFromTable_Uncached() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'getSemanticData' );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->getSemanticDataFromTable( 42, null, $propertyTableDefinition, $requestOptions );
+ }
+
+ public function testGetSemanticDataFromTable_FreshFetch() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSemanticData = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\StubSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSemanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'newStubSemanticData' )
+ ->will( $this->returnValue( $stubSemanticData ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'fetchSemanticData' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->getSemanticDataFromTable( 42, $subject, $propertyTableDefinition );
+ }
+
+ public function testGetSemanticDataFromTable_FromStaticCache() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDefinition->expects( $this->once() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( '__foo__' ) );
+
+ $stubSemanticData = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\StubSemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stubSemanticData->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'newStubSemanticData' )
+ ->will( $this->returnValue( $stubSemanticData ) );
+
+ $this->semanticDataLookup->expects( $this->once() )
+ ->method( 'getTableUsageInfo' )
+ ->will( $this->returnValue( [ '__foo__' => true ] ) );
+
+ $this->semanticDataLookup->expects( $this->never() )
+ ->method( 'fetchSemanticData' );
+
+ $instance = new CachingSemanticDataLookup(
+ $this->semanticDataLookup
+ );
+
+ $instance->invalidateCache( 42 );
+ $instance->setLookupCache( 42, $stubSemanticData );
+
+ $instance->getSemanticDataFromTable( 42, $subject, $propertyTableDefinition );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIBlobHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIBlobHandlerTest.php
new file mode 100644
index 00000000..c2e7b637
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIBlobHandlerTest.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DIHandlers\DIBlobHandler;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDIBlob as DIBlob;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\DIHandlers\DIBlobHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DIBlobHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DIBlobHandler::class,
+ new DIBlobHandler( $this->store )
+ );
+ }
+
+ public function testImmutableMethodAccess() {
+
+ $instance = new DIBlobHandler(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFetchFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableIndexes()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getIndexField()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getLabelField()
+ );
+ }
+
+ public function testMutableMethodAccess() {
+
+ $blob = new DIBlob( 'Foo' );
+
+ $instance = new DIBlobHandler(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getWhereConds( $blob )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getInsertValues( $blob )
+ );
+ }
+
+ /**
+ * @dataProvider fieldTypeProvider
+ */
+ public function testMutableOnFieldTypeFeature( $fieldTypeFeatures, $expected ) {
+
+ $instance = new DIBlobHandler(
+ $this->store
+ );
+
+ $instance->setFieldTypeFeatures(
+ $fieldTypeFeatures
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getTableFields()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getFetchFields()
+ );
+ }
+
+ public function testMutableInsertValuesOnVariableLength() {
+
+ $instance = new DIBlobHandler( $this->store );
+
+ $s72 = 'zcqaBHr1jV7mINGovktU8bD6zYjgKMqfaCxQlPcT4J6h4197dQpSW5PK5f8HigRk0yEsLC2F';
+ $blob = new DIBlob( $s72 );
+
+ $expected = [
+ 'o_blob' => '',
+ 'o_hash' => $blob->getString()
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getInsertValues( $blob )
+ );
+
+ $s73 = 'zcqaBHr1jV7mINGovktU8bD6zYjgKMqfaCxQlPcT4J6h4197dQpSW5PK5f8HigRk0yEsLC2Fs';
+ $blob = new DIBlob( $s73 );
+
+ $expected = [
+ 'o_blob' => $blob->getString(),
+ 'o_hash' => 'zcqaBHr1jV7mINGovktU8bD6zYjgKMqfaCxQlPcTcf085df3633862a2d74e393fa84944e2'
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getInsertValues( $blob )
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysProvider
+ */
+ public function testDataItemFromDBKeys( $dbKeys ) {
+
+ $instance = new DIBlobHandler(
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIBlob',
+ $instance->dataItemFromDBKeys( $dbKeys )
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysExceptionProvider
+ */
+ public function testDataItemFromDBKeysThrowsException( $dbKeys ) {
+
+ $instance = new DIBlobHandler(
+ $this->store
+ );
+
+ $this->setExpectedException( '\SMW\SQLStore\EntityStore\Exception\DataItemHandlerException' );
+ $instance->dataItemFromDBKeys( $dbKeys );
+ }
+
+ public function dbKeysProvider() {
+
+ $provider[] = [
+ [ 'Foo', '' ]
+ ];
+
+ $provider[] = [
+ [ '', 'Foo' ]
+ ];
+
+ return $provider;
+ }
+
+ public function dbKeysExceptionProvider() {
+
+ $provider[] = [
+ [ '' ]
+ ];
+
+ return $provider;
+ }
+
+ private function createRandomString( $length = 10 ) {
+ return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1,$length);
+ }
+
+ public function fieldTypeProvider() {
+
+ $provider[] = [
+ SMW_FIELDT_NONE,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => FieldType::FIELD_TITLE
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_NOCASE,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => FieldType::TYPE_CHAR_NOCASE
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_LONG,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => FieldType::TYPE_CHAR_LONG
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_NOCASE | SMW_FIELDT_CHAR_LONG,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_hash' => FieldType::TYPE_CHAR_LONG_NOCASE
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIUriHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIUriHandlerTest.php
new file mode 100644
index 00000000..c10afebe
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIUriHandlerTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore\DIHandlers;
+
+use SMW\SQLStore\EntityStore\DIHandlers\DIUriHandler;
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMWDIUri as DIUri;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\DIHandlers\DIUriHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DIUriHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ DIUriHandler::class,
+ new DIUriHandler( $this->store )
+ );
+ }
+
+ public function testImmutableMethodAccess() {
+
+ $instance = new DIUriHandler(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFetchFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableIndexes()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getIndexField()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getLabelField()
+ );
+ }
+
+ public function testMutableMethodAccess() {
+
+ $uri = new DIUri( 'http', 'example.org', '', '' );
+
+ $instance = new DIUriHandler(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getWhereConds( $uri )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getInsertValues( $uri )
+ );
+ }
+
+ /**
+ * @dataProvider fieldTypeProvider
+ */
+ public function testMutableOnFieldTypeFeature( $fieldTypeFeatures, $expected ) {
+
+ $instance = new DIUriHandler(
+ $this->store
+ );
+
+ $instance->setFieldTypeFeatures(
+ $fieldTypeFeatures
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getTableFields()
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getFetchFields()
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysProvider
+ */
+ public function testDataItemFromDBKeys( $dbKeys ) {
+
+ $instance = new DIUriHandler(
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIUri',
+ $instance->dataItemFromDBKeys( $dbKeys )
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysExceptionProvider
+ */
+ public function testDataItemFromDBKeysThrowsException( $dbKeys ) {
+
+ $instance = new DIUriHandler(
+ $this->store
+ );
+
+ $this->setExpectedException( '\SMW\SQLStore\EntityStore\Exception\DataItemHandlerException' );
+ $instance->dataItemFromDBKeys( $dbKeys );
+ }
+
+ public function dbKeysProvider() {
+
+ $provider[] = [
+ [ 'http://example.org', '' ]
+ ];
+
+ $provider[] = [
+ [ '', 'http://example.org' ]
+ ];
+
+ return $provider;
+ }
+
+ public function dbKeysExceptionProvider() {
+
+ $provider[] = [
+ [ '' ]
+ ];
+
+ return $provider;
+ }
+
+ public function fieldTypeProvider() {
+
+ $provider[] = [
+ SMW_FIELDT_NONE,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => FieldType::FIELD_TITLE
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_NOCASE,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => FieldType::TYPE_CHAR_NOCASE
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_LONG,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => FieldType::TYPE_CHAR_LONG
+ ]
+ ];
+
+ $provider[] = [
+ SMW_FIELDT_CHAR_NOCASE | SMW_FIELDT_CHAR_LONG,
+ [
+ 'o_blob' => FieldType::TYPE_BLOB,
+ 'o_serialized' => FieldType::TYPE_CHAR_LONG_NOCASE
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIWikiPageHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIWikiPageHandlerTest.php
new file mode 100644
index 00000000..2c5fd535
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DIHandlers/DIWikiPageHandlerTest.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore\DIHandlers;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\DIHandlers\DIWikiPageHandler;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\DIHandlers\DIWikiPageHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DIWikiPageHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIWikiPageHandler',
+ new DIWikiPageHandler( $store )
+ );
+ }
+
+ public function testImmutableMethodAccess() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DIWikiPageHandler( $store );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFetchFields()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTableIndexes()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getIndexField()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getLabelField()
+ );
+ }
+
+ public function testMutableMethodAccess() {
+
+ // EntityIdTable
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID', 'makeSMWPageID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 1001 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new DIWikiPageHandler( $store );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getWhereConds( DIWikiPage::newFromText( 'Foo' ) )
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getInsertValues( DIWikiPage::newFromText( 'Foo' ) )
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysProvider
+ */
+ public function testDataItemFromDBKeys( $dbKeys ) {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DIWikiPageHandler( $store );
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->dataItemFromDBKeys( $dbKeys )
+ );
+ }
+
+ /**
+ * @dataProvider dbKeysExceptionProvider
+ */
+ public function testDataItemFromDBKeysThrowsException( $dbKeys ) {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DIWikiPageHandler( $store );
+
+ $this->setExpectedException( '\SMW\SQLStore\EntityStore\Exception\DataItemHandlerException' );
+ $instance->dataItemFromDBKeys( $dbKeys );
+ }
+
+ public function dbKeysProvider() {
+
+ #0 SMW_NS_PROPERTY, user defined property
+ $provider[] = [
+ [ 'Foo', SMW_NS_PROPERTY, 'bar', '', '' ]
+ ];
+
+ #1 SMW_NS_PROPERTY, pre-defined property
+ $provider[] = [
+ [ '_Foo', SMW_NS_PROPERTY, 'bar', '', '' ]
+ ];
+
+ #0 SMW_NS_PROPERTY, pre-defined property (see bug 48711)
+ $provider[] = [
+ [ '_Foo', SMW_NS_PROPERTY, '', '', '' ]
+ ];
+
+ return $provider;
+ }
+
+ public function dbKeysExceptionProvider() {
+
+ $provider[] = [
+ [ 'Foo' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DataItemHandlerDispatcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DataItemHandlerDispatcherTest.php
new file mode 100644
index 00000000..2aca3edf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/DataItemHandlerDispatcherTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\SQLStore\EntityStore\DataItemHandlerDispatcher;
+use SMWDataItem as DataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\DataItemHandlerDispatcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataItemHandlerDispatcherTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\DataItemHandlerDispatcher',
+ new DataItemHandlerDispatcher( $store )
+ );
+ }
+
+ /**
+ * @dataProvider dataItemTypeProvider
+ */
+ public function testGetHandlerByType( $type, $expected ) {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataItemHandlerDispatcher( $store );
+
+ $this->assertInstanceOf(
+ $expected,
+ $instance->getHandlerByType( $type )
+ );
+ }
+
+ /**
+ * @dataProvider invalidTypeProvider
+ */
+ public function testGetHandlerByInvalidTypeThrowsException( $type ) {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DataItemHandlerDispatcher( $store );
+
+ $this->setExpectedException( 'SMW\SQLStore\EntityStore\Exception\DataItemHandlerException' );
+ $instance->getHandlerByType( $type );
+ }
+
+ public function dataItemTypeProvider() {
+
+ $provider[] = [
+ DataItem::TYPE_NUMBER,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DINumberHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_BLOB,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIBlobHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_BOOLEAN,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIBooleanHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_URI,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIUriHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_TIME,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DITimeHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_GEO,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIGeoCoordinateHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_WIKIPAGE,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIWikiPageHandler'
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_CONCEPT,
+ '\SMW\SQLStore\EntityStore\DIHandlers\DIConceptHandler'
+ ];
+
+ return $provider;
+ }
+
+ public function invalidTypeProvider() {
+
+ $provider[] = [
+ DataItem::TYPE_PROPERTY,
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_CONTAINER,
+ ];
+
+ $provider[] = [
+ DataItem::TYPE_ERROR,
+ ];
+
+ $provider[] = [
+ 'Foo',
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdCacheManagerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdCacheManagerTest.php
new file mode 100644
index 00000000..112ca5dc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdCacheManagerTest.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use Onoi\Cache\FixedInMemoryLruCache;
+use SMW\SQLStore\EntityStore\IdCacheManager;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\IdCacheManager
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IdCacheManagerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $caches;
+
+ protected function setUp() {
+
+ $this->caches = [
+ 'entity.id' => new FixedInMemoryLruCache(),
+ 'entity.sort' => new FixedInMemoryLruCache(),
+ 'entity.lookup' => new FixedInMemoryLruCache(),
+ 'table.hash' => new FixedInMemoryLruCache()
+ ];
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IdCacheManager::class,
+ new IdCacheManager( $this->caches )
+ );
+ }
+
+ public function testComputeSha1() {
+
+ $this->assertInternalType(
+ 'string',
+ IdCacheManager::computeSha1( [] )
+ );
+ }
+
+ public function testGet() {
+
+ $instance = new IdCacheManager( $this->caches );
+
+ $this->assertInstanceOf(
+ FixedInMemoryLruCache::class,
+ $instance->get( 'entity.sort' )
+ );
+ }
+
+ public function testGetThrowsException() {
+
+ $instance = new IdCacheManager( $this->caches );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->get( 'foo' );
+ }
+
+ public function testSetCache() {
+
+ $instance = new IdCacheManager( $this->caches );
+
+ $instance->setCache( 'foo', 0, '', '', 42, 'bar' );
+
+ $this->assertEquals(
+ 42,
+ $instance->getId( [ 'foo', 0, '', '' ] )
+ );
+
+ $this->assertEquals(
+ false,
+ $instance->getId( [ 'foo', '0', '', '' ] )
+ );
+
+ $this->assertEquals(
+ 'bar',
+ $instance->getSort( [ 'foo', 0, '', '' ] )
+ );
+ }
+
+ public function testDeleteCache() {
+
+ $instance = new IdCacheManager( $this->caches );
+
+ $instance->setCache( 'foo', 0, '', '', '42', 'bar' );
+
+ $this->assertEquals(
+ 42,
+ $instance->getId( [ 'foo', 0, '', '' ] )
+ );
+
+ $instance->deleteCache( 'foo', 0, '', '' );
+
+ $this->assertEquals(
+ false,
+ $instance->getId( [ 'foo', '0', '', '' ] )
+ );
+
+ $this->assertEquals(
+ false,
+ $instance->getSort( [ 'foo', 0, '', '' ] )
+ );
+ }
+
+ public function testHasCache() {
+
+ $instance = new IdCacheManager( $this->caches );
+
+ $instance->setCache( 'foo', 0, '', '', '42', 'bar' );
+
+ $this->assertEquals(
+ false,
+ $instance->hasCache( [ 'foo', 0, '', '' ] )
+ );
+
+ $this->assertEquals(
+ true,
+ $instance->hasCache( $instance->computeSha1( [ 'foo', 0, '', '' ] ) )
+ );
+
+ }
+
+ public function testDeleteCacheById() {
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->once() )
+ ->method( 'delete' )
+ ->with( $this->equalTo( IdCacheManager::computeSha1( [ 'foo', 0, '', '' ] ) ) );
+
+ $this->caches['entity.id'] = $cache;
+
+ $instance = new IdCacheManager( $this->caches );
+ $instance->setCache( 'foo', 0, '', '', '42', 'bar' );
+
+ $instance->deleteCacheById( 42 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdChangerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdChangerTest.php
new file mode 100644
index 00000000..7a41f4ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdChangerTest.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\SQLStore\EntityStore\IdChanger;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\IdChanger
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class IdChangerTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $conection;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getConnection', 'getPropertyTables' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IdChanger::class,
+ new IdChanger( $this->store )
+ );
+ }
+
+ public function testChange_IdSubject_Fields_NotFixedPropertyTable() {
+
+ $table = $this->getMockBuilder( 'SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $table->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $table->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $table->expects( $this->any() )
+ ->method( 'getFields' )
+ ->will( $this->returnValue( [ '_foo' => \SMW\SQLStore\TableBuilder\FieldType::FIELD_ID ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->onConsecutiveCalls( [ $table ] ) );
+
+ $this->connection->expects( $this->at( 0 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 's_id' => 1001 ] ),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 1 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'p_id' => 1001 ] ),
+ $this->equalTo( [ 'p_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ '_foo' => 1001 ] ),
+ $this->equalTo( [ '_foo' => 42 ] ) );
+
+ $instance = new IdChanger(
+ $this->store
+ );
+
+ $instance->change( 42, 1001 );
+ }
+
+ public function testChange_IdSubject_PropertyNS_Fields_NotFixedPropertyTable() {
+
+ $table = $this->getMockBuilder( 'SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $table->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $table->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $table->expects( $this->any() )
+ ->method( 'getFields' )
+ ->will( $this->returnValue( [ '_foo' => \SMW\SQLStore\TableBuilder\FieldType::FIELD_ID ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->onConsecutiveCalls( [ $table ] ) );
+
+ $this->connection->expects( $this->at( 0 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 's_id' => 1001 ] ),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 1 ) )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'p_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ '_foo' => 1001 ] ),
+ $this->equalTo( [ '_foo' => 42 ] ) );
+
+ $instance = new IdChanger(
+ $this->store
+ );
+
+ $instance->change( 42, 1001, SMW_NS_PROPERTY, NS_MAIN );
+ }
+
+ public function testChange_IdSubject_ConceptNS_Fields_NotFixedPropertyTable() {
+
+ $table = $this->getMockBuilder( 'SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $table->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $table->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $table->expects( $this->any() )
+ ->method( 'getFields' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->onConsecutiveCalls( [ $table ] ) );
+
+ $this->connection->expects( $this->at( 0 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 's_id' => 1001 ] ),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 1 ) )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'o_id' => 1001 ] ),
+ $this->equalTo( [ 'o_id' => 42 ] ) );
+
+ $instance = new IdChanger(
+ $this->store
+ );
+
+ $instance->change( 42, 1001, SMW_NS_CONCEPT, NS_MAIN );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdEntityFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdEntityFinderTest.php
new file mode 100644
index 00000000..02efa06c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/IdEntityFinderTest.php
@@ -0,0 +1,232 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIWikiPage;
+use SMW\IteratorFactory;
+use SMW\SQLStore\EntityStore\IdEntityFinder;
+use SMW\MediaWiki\Connection\Query;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\IdEntityFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class IdEntityFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $cache;
+ private $iteratorFactory;
+ private $idCacheManager;
+ private $store;
+ private $conection;
+
+ protected function setUp() {
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->idCacheManager = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdCacheManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->idCacheManager->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnValue( $this->cache ) );
+
+ $this->iteratorFactory = $this->getMockBuilder( '\SMW\IteratorFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMockForAbstractClass();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ IdEntityFinder::class,
+ new IdEntityFinder( $this->store, $this->iteratorFactory, $this->idCacheManager )
+ );
+ }
+
+ public function testGetDataItemForNonCachedId() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Foo';
+ $row->smw_namespace = 0;
+ $row->smw_iw = '';
+ $row->smw_subobject ='';
+ $row->smw_sortkey ='';
+ $row->smw_sort ='';
+ $row->smw_hash = 'x99w';
+
+ $this->cache->expects( $this->once() )
+ ->method( 'save' )
+ ->with(
+ $this->equalTo( 42 ),
+ $this->anything() );
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'smw_id' => 42 ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new IdEntityFinder(
+ $this->store,
+ $this->iteratorFactory,
+ $this->idCacheManager
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->getDataItemById( 42 )
+ );
+ }
+
+ public function testGetDataItemForCachedId() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( new DIWikiPage( 'Foo', NS_MAIN ) ) );
+
+ $this->connection->expects( $this->never() )
+ ->method( 'selectRow' );
+
+ $instance = new IdEntityFinder(
+ $this->store,
+ $this->iteratorFactory,
+ $this->idCacheManager
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->getDataItemById( 42 )
+ );
+ }
+
+ public function testPredefinedPropertyItem() {
+
+ $dataItem = new DIWikiPage( '_MDAT', SMW_NS_PROPERTY );
+ $dataItem->setId( 42 );
+ $dataItem->setSortKey( 'bar' );
+ $dataItem->setOption( 'sort', 'BAR' );
+
+ $row = new \stdClass;
+ $row->smw_title = '_MDAT';
+ $row->smw_namespace = SMW_NS_PROPERTY;
+ $row->smw_iw = '';
+ $row->smw_subobject = '';
+ $row->smw_sortkey = 'bar';
+ $row->smw_sort = 'BAR';
+ $row->smw_hash = 'x99w';
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'smw_id' => 42 ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new IdEntityFinder(
+ $this->store,
+ $this->iteratorFactory,
+ $this->idCacheManager
+ );
+
+ $this->assertEquals(
+ $dataItem,
+ $instance->getDataItemById( 42 )
+ );
+ }
+
+ public function testNullForUnknownId() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new IdEntityFinder(
+ $this->store,
+ $this->iteratorFactory,
+ $this->idCacheManager
+ );
+
+ $this->assertNull(
+ $instance->getDataItemById( 42 )
+ );
+ }
+
+ public function testGetDataItemsFromList() {
+
+ $expected = new DIWikiPage( 'Foo', 0, '', '' );
+ $expected->setId( 42 );
+ $expected->setSortKey( '...' );
+ $expected->setOption( 'sort', '...' );
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+ $row->smw_title = 'Foo';
+ $row->smw_namespace = 0;
+ $row->smw_iw = '';
+ $row->smw_subobject ='';
+ $row->smw_sortkey = '...';
+ $row->smw_sort = '...';
+ $row->smw_hash = 'x99w';
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'smw_id' => [ 42 ] ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new IdEntityFinder(
+ $this->store,
+ new IteratorFactory(),
+ $this->idCacheManager
+ );
+
+ foreach ( $instance->getDataItemsFromList( [ 42 ] ) as $value ) {
+ $this->assertEquals(
+ $expected,
+ $value
+ );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/NativeEntityLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/NativeEntityLookupTest.php
new file mode 100644
index 00000000..ab5dc99c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/NativeEntityLookupTest.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\NativeEntityLookup;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\NativeEntityLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class NativeEntityLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ NativeEntityLookup::class,
+ new NativeEntityLookup( $store )
+ );
+ }
+
+ public function testGetSemanticData() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getSemanticData' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->anything() );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getSemanticData( $subject );
+ }
+
+ public function testGetProperties() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getProperties' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->anything() );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getProperties( $subject );
+ }
+
+ public function testGetPropertyValues() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $property = new DIProperty( 'Bar' );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->with(
+ $this->equalTo( $subject ),
+ $this->equalTo( $property ),
+ $this->anything() );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getPropertyValues( $subject, $property );
+ }
+
+ public function testGetPropertySubjects() {
+
+ $property = new DIProperty( 'Bar' );
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getPropertySubjects' )
+ ->with(
+ $this->equalTo( $property ),
+ $this->equalTo( $subject ),
+ $this->anything() );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getPropertySubjects( $property, $subject );
+ }
+
+ public function testGetAllPropertySubjects() {
+
+ $property = new DIProperty( 'Bar' );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getAllPropertySubjects' )
+ ->with( $this->equalTo( $property ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getAllPropertySubjects( $property );
+ }
+
+ public function testGetInProperties() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $reader = $this->getMockBuilder( '\SMWSQLStore3Readers' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader->expects( $this->once() )
+ ->method( 'getInProperties' )
+ ->with( $this->equalTo( $subject ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getReader' ] )
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getReader' )
+ ->will( $this->returnValue( $reader ) );
+
+ $instance = new NativeEntityLookup(
+ $store
+ );
+
+ $instance->getInProperties( $subject );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertiesLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertiesLookupTest.php
new file mode 100644
index 00000000..0bc96fc4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertiesLookupTest.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\SQLStore\EntityStore\PropertiesLookup;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\PropertiesLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertiesLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ PropertiesLookup::class,
+ new PropertiesLookup( $store )
+ );
+ }
+
+ public function testLookupForNonFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+ $dataItem->setId( 42 );
+
+ $resultWrapper = $this->getMockBuilder( '\FakeResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'execute' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getSQLOptions', 'getSQLConditions' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLOptions' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLConditions' )
+ ->will( $this->returnValue( '' ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PropertiesLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( $dataItem, $propertyTableDef );
+ }
+
+ public function testLookupForFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+ $dataItem->setId( 1001 );
+
+ $resultWrapper = $this->getMockBuilder( '\FakeResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'execute' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PropertiesLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( $dataItem, $propertyTableDef );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertySubjectsLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertySubjectsLookupTest.php
new file mode 100644
index 00000000..014a246d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/PropertySubjectsLookupTest.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\SQLStore\EntityStore\PropertySubjectsLookup;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\PropertySubjectsLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertySubjectsLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ PropertySubjectsLookup::class,
+ new PropertySubjectsLookup( $store )
+ );
+ }
+
+ public function testLookupForNonFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->atLeastOnce() )
+ ->method( 'getWhereConds' )
+ ->will( $this->returnValue( [ 'o_id' => 42 ] ) );
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getDataItemHandlerForDIType', 'getSQLOptions', 'getSQLConditions' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLOptions' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLConditions' )
+ ->will( $this->returnValue( '' ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new PropertySubjectsLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( 42, $propertyTableDef, $dataItem );
+ }
+
+ public function testLookupForFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+
+ $resultWrapper = $this->getMockBuilder( '\FakeResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->atLeastOnce() )
+ ->method( 'getWhereConds' )
+ ->will( $this->returnValue( [ 'o_id' => 42 ] ) );
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $query = $this->getMockBuilder( '\SMW\MediaWiki\Connection\Query' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getDataItemHandlerForDIType' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new PropertySubjectsLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( 42, $propertyTableDef, $dataItem );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SemanticDataLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SemanticDataLookupTest.php
new file mode 100644
index 00000000..de5e5872
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SemanticDataLookupTest.php
@@ -0,0 +1,356 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMWDIBlob as DIBlob;
+use SMW\RequestOptions;
+use SMW\SQLStore\EntityStore\SemanticDataLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\SemanticDataLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SemanticDataLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $connection;
+ private $dataItemHandler;
+ private $query;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'findPropertyTableID', 'getDataItemHandlerForDIType', 'getObjectIds' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $this->dataItemHandler ) );
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableName' )
+ ->will( $this->returnArgument(0) );
+
+ $this->query = new \SMW\MediaWiki\Connection\Query( $this->connection );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $this->query ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->store->setConnectionManager( $connectionManager );
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SemanticDataLookup::class,
+ new SemanticDataLookup( $this->store )
+ );
+ }
+
+ public function testNewStubSemanticData_FromDIWikiPage() {
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\StubSemanticData',
+ $instance->newStubSemanticData( DIWikiPage::newFromText( __METHOD__ ) )
+ );
+ }
+
+ public function testNewStubSemanticData_FromSemanticData() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\StubSemanticData',
+ $instance->newStubSemanticData( $semanticData )
+ );
+ }
+
+ public function testNewStubSemanticDataThrowsException() {
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->newStubSemanticData( 'Foo' );
+ }
+
+ public function testGetTableUsageInfo() {
+
+ $property = new DIProperty( 'Foo' );
+
+ $this->store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->with( $this->equalTo( $property ) )
+ ->will( $this->returnValue( '__bar__' ) );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ $property ] ) );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $this->assertEquals(
+ [ '__bar__' => true ],
+ $instance->getTableUsageInfo( $semanticData )
+ );
+ }
+
+ public function testSemanticDataFromTable() {
+
+ $row = new \stdClass;
+ $row->prop = 'FOO';
+ $row->v0 = '1001';
+
+ $this->dataItemHandler->expects( $this->any() )
+ ->method( 'getFetchFields' )
+ ->will( $this->returnValue( [ 'fooField' => 'fieldType' ] ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $semanticData = $instance->fetchSemanticData(
+ 42,
+ $subject,
+ $propertyTable
+ );
+ }
+
+ public function testSemanticDataFromTable_WithConstraint() {
+
+ $row = new \stdClass;
+ $row->prop = 'FOO';
+ $row->v0 = '1001';
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 9999 ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->dataItemHandler->expects( $this->any() )
+ ->method( 'getFetchFields' )
+ ->will( $this->returnValue( [ 'fooField' => 'fieldType' ] ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnCallback( function( $value ) { return "'$value'"; } ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->conditionConstraint = true;
+ $requestOptions->setLimit( 4 );
+
+ $requestOptions = $instance->newRequestOptions(
+ $propertyTable,
+ $property,
+ $requestOptions
+ );
+
+ $instance->fetchSemanticData( 42, $subject, $propertyTable, $requestOptions );
+
+ $this->assertEquals(
+ "SELECT p.smw_title AS prop, fooField AS v0 FROM " .
+ "INNER JOIN smw_object_ids AS p ON p_id=p.smw_id " .
+ "WHERE (s_id='42') AND (p.smw_iw!=':smw') AND (p.smw_iw!=':smw-delete') AND (p_id='9999') " .
+ "LIMIT 4",
+ $this->query->build()
+ );
+ }
+
+ public function testSemanticData_NoDataItem() {
+
+ $row = new \stdClass;
+ $row->prop = 'FOO';
+ $row->v0 = '1001';
+
+ $this->dataItemHandler->expects( $this->any() )
+ ->method( 'getFetchFields' )
+ ->will( $this->returnValue( [ 'fooField' => 'fieldType' ] ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnCallback( function( $value ) { return "'$value'"; } ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $instance->fetchSemanticData( 42, null, $propertyTable );
+
+ $this->assertEquals(
+ "SELECT p.smw_title AS prop, fooField AS v0 FROM " .
+ "INNER JOIN smw_object_ids AS p ON p_id=p.smw_id " .
+ "WHERE (s_id='42') AND (p.smw_iw!=':smw') AND (p.smw_iw!=':smw-delete')",
+ $this->query->build()
+ );
+ }
+
+ public function testFetchSemanticData_NonWikiPageTable_DISTINCT_SELECT() {
+
+ $row = new \stdClass;
+ $row->prop = 'FOO';
+ $row->v0 = '1001';
+
+ $this->dataItemHandler->expects( $this->any() )
+ ->method( 'getFetchFields' )
+ ->will( $this->returnValue( [ 'fooField' => 'fieldType' ] ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $propertyTable->expects( $this->atLeastOnce() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'bar_table' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnCallback( function( $value ) { return "'$value'"; } ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $dataItem = new DIBlob( __METHOD__ );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( 4 );
+
+ $instance = new SemanticDataLookup(
+ $this->store
+ );
+
+ $instance->fetchSemanticData( 42, $dataItem, $propertyTable, $requestOptions );
+
+ $this->assertEquals(
+ "SELECT DISTINCT fooField AS v0 FROM bar_table WHERE (p_id='42') LIMIT 4",
+ $this->query->build()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/StubSemanticDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/StubSemanticDataTest.php
new file mode 100644
index 00000000..465a048d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/StubSemanticDataTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\EntityStore\StubSemanticData;
+use SMW\Tests\TestEnvironment;
+use SMWDITime as DITime;
+
+/**
+ * @covers SMW\SQLStore\EntityStore\StubSemanticData
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class StubSemanticDataTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $this->assertInstanceOf(
+ StubSemanticData::class,
+ StubSemanticData::newFromSemanticData( $semanticData, $this->store )
+ );
+ }
+
+ public function testNotToResolveSubobjectsForRedirect() {
+
+ $instance = $this->getMockBuilder( StubSemanticData::class )
+ ->setConstructorArgs( [
+ DIWikiPage::newFromText( __METHOD__ ),
+ $this->store ] )
+ ->setMethods( [
+ 'getProperties',
+ 'isRedirect',
+ 'getPropertyValues' ] )
+ ->getMock();
+
+ $instance->expects( $this->once() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ new DIProperty( '_SOBJ' ) ] ) );
+
+ $instance->expects( $this->once() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( true ) );
+
+ $instance->expects( $this->never() )
+ ->method( 'getPropertyValues' );
+
+ $instance->getSubSemanticData();
+ }
+
+ public function testGetPropertyValues() {
+
+ $instance = StubSemanticData::newFromSemanticData(
+ new SemanticData( DIWikiPage::newFromText( __METHOD__ ) ),
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ 'SMW\DIWikiPage',
+ $instance->getSubject()
+ );
+
+ $this->assertEmpty(
+ $instance->getPropertyValues( new DIProperty( 'unknownInverseProperty', true ) )
+ );
+
+ $this->assertEmpty(
+ $instance->getPropertyValues( new DIProperty( 'unknownProperty' ) )
+ );
+ }
+
+ /**
+ * @dataProvider propertyObjectProvider
+ */
+ public function testPhpSerialization( $property, $dataItem ) {
+
+ $instance = StubSemanticData::newFromSemanticData(
+ new SemanticData( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ $this->store
+ );
+
+ $instance->addPropertyObjectValue(
+ $property,
+ $dataItem
+ );
+
+ $serialization = serialize( $instance );
+
+ $this->assertEquals(
+ $instance->getHash(),
+ unserialize( $serialization )->getHash()
+ );
+ }
+
+ /**
+ * @dataProvider propertyObjectProvider
+ */
+ public function testRemovePropertyObjectValue( $property, $dataItem ) {
+
+ $instance = StubSemanticData::newFromSemanticData(
+ new SemanticData( new DIWikiPage( 'Foo', NS_MAIN ) ),
+ $this->store
+ );
+
+ $instance->addPropertyObjectValue( $property, $dataItem );
+ $this->assertFalse( $instance->isEmpty() );
+
+ $instance->removePropertyObjectValue( $property, $dataItem );
+ $this->assertTrue( $instance->isEmpty() );
+ }
+
+ public function propertyObjectProvider() {
+
+ $provider = [];
+
+ // #0
+ $provider[] = [
+ new DIProperty( '_MDAT' ),
+ DITime::newFromTimestamp( 1272508903 )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SubobjectListFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SubobjectListFinderTest.php
new file mode 100644
index 00000000..a7136826
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/SubobjectListFinderTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\SubobjectListFinder;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\SubobjectListFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SubobjectListFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $iteratorFactory;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->iteratorFactory = ApplicationFactory::getInstance()->getIteratorFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $iteratorFactory = $this->getMockBuilder( '\SMW\IteratorFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ SubobjectListFinder::class,
+ new SubobjectListFinder( $store, $iteratorFactory )
+ );
+ }
+
+ /**
+ * @dataProvider subjectProvider
+ */
+ public function testNewMappingIterator( $subject ) {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SubobjectListFinder(
+ $store,
+ $this->iteratorFactory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\MappingIterator',
+ $instance->find( $subject )
+ );
+ }
+
+ /**
+ * @dataProvider subjectProvider
+ */
+ public function testIterateOn( $subject ) {
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+ $row->smw_sortkey = 'sort';
+ $row->smw_sort = 'SORT';
+ $row->smw_subobject = '10000000001';
+
+ $expected = [
+ 'Foo', 0, '', 'sort', '10000000001'
+ ];
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( 'smw_title= AND smw_namespace= AND smw_iw= AND smw_subobject!=' ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getDataItemHandlerForDIType' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SubobjectListFinder(
+ $store,
+ $this->iteratorFactory
+ );
+
+ foreach ( $instance->find( $subject ) as $v ) {
+ $this->assertEquals( 42, $v->getId() );
+ }
+ }
+
+ public function subjectProvider() {
+
+ $provider[] = [
+ DIWikiPage::newFromText( 'Foo' )
+ ];
+
+ $provider[] = [
+ DIWikiPage::newFromText( 'Bar', SMW_NS_PROPERTY )
+ ];
+
+ $provider[] = [
+ DIWikiPage::newFromText( 'Modification date', SMW_NS_PROPERTY )
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/TraversalPropertyLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/TraversalPropertyLookupTest.php
new file mode 100644
index 00000000..50ca8b28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/TraversalPropertyLookupTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\SQLStore\EntityStore\TraversalPropertyLookup;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\TraversalPropertyLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TraversalPropertyLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ TraversalPropertyLookup::class,
+ new TraversalPropertyLookup( $store )
+ );
+ }
+
+ public function testlookupForNonFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->atLeastOnce() )
+ ->method( 'getWhereConds' )
+ ->will( $this->returnValue( [ 'o_id' => 42 ] ) );
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getDataItemHandlerForDIType', 'getSQLOptions', 'getSQLConditions' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLOptions' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSQLConditions' )
+ ->will( $this->returnValue( '' ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new TraversalPropertyLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( $propertyTableDef, $dataItem );
+ }
+
+ public function testlookupForFixedPropertyTable() {
+
+ $dataItem = DIWikiPage::newFromText( __METHOD__ );
+
+ $resultWrapper = $this->getMockBuilder( '\FakeResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->atLeastOnce() )
+ ->method( 'getWhereConds' )
+ ->will( $this->returnValue( [ 'o_id' => 42 ] ) );
+
+ $propertyTableDef = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDef->expects( $this->atLeastOnce() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getDataItemHandlerForDIType' ] )
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new TraversalPropertyLookup(
+ $store
+ );
+
+ $instance->fetchFromTable( $propertyTableDef, $dataItem );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/UniquenessLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/UniquenessLookupTest.php
new file mode 100644
index 00000000..197975e1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityStore/UniquenessLookupTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\Tests\SQLStore\EntityStore;
+
+use SMW\SQLStore\EntityStore\UniquenessLookup;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Connection\Query;
+use SMW\IteratorFactory;
+
+/**
+ * @covers \SMW\SQLStore\EntityStore\UniquenessLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class UniquenessLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $connection;
+ private $iteratorFactory;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->iteratorFactory = $this->getMockBuilder( '\SMW\IteratorFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ UniquenessLookup::class,
+ new UniquenessLookup( $this->store, $this->iteratorFactory )
+ );
+ }
+
+ public function testIsUnique() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableName' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $query = new \SMW\MediaWiki\Connection\Query( $connection );
+
+ $resultWrapper = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $instance = new UniquenessLookup(
+ $this->store,
+ $this->iteratorFactory
+ );
+
+ $instance->isUnique( DIWikiPage::newFromText( 'Foo' ) );
+
+ $this->assertJsonStringEqualsJsonString(
+ '{' .
+ '"tables": "smw_object_ids",' .
+ '"fields":["smw_id","smw_sortkey"],' .
+ '"conditions":[["smw_title=Foo"],["smw_namespace=0"],["smw_subobject="],["smw_iw!=:smw"],["smw_iw!=:smw-delete"],["smw_iw!=:smw-redi"]],' .
+ '"joins":[],' .
+ '"options":{"LIMIT":2},"alias":"","index":0,"autocommit":false}',
+ (string)$query
+ );
+ }
+
+ public function testFindDuplicates() {
+
+ $row = new \stdClass;
+ $row->count = 42;
+ $row->smw_title = 'Foo';
+ $row->smw_namespace = 0;
+ $row->smw_iw = '';
+ $row->smw_subobject ='';
+
+ $query = new Query( $this->connection );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new UniquenessLookup(
+ $this->store,
+ new IteratorFactory()
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\MappingIterator',
+ $instance->findDuplicates()
+ );
+
+ $this->assertContains(
+ 'HAVING":"count(*) > 1',
+ $query->__toString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityValueUniquenessConstraintCheckerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityValueUniquenessConstraintCheckerTest.php
new file mode 100644
index 00000000..6cd6f7f4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/EntityValueUniquenessConstraintCheckerTest.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\EntityValueUniquenessConstraintChecker;
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\SQLStore\EntityValueUniquenessConstraintChecker
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class EntityValueUniquenessConstraintCheckerTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $connection;
+ private $iteratorFactory;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->iteratorFactory = $this->getMockBuilder( '\SMW\IteratorFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ EntityValueUniquenessConstraintChecker::class,
+ new EntityValueUniquenessConstraintChecker( $this->store, $this->iteratorFactory )
+ );
+ }
+
+ public function testCheckConstraint() {
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher->expects( $this->any() )
+ ->method( 'findTableIdForProperty' )
+ ->will( $this->returnValue( '_foo' ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getConnection', 'getPropertyTables', 'getPropertyTableInfoFetcher' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->any() )
+ ->method( 'getFixedProperty' )
+ ->will( $this->returnValue( '_UNKNOWN_FIXED_PROPERTY' ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'getDiType' )
+ ->will( $this->returnValue( \SMWDataItem::TYPE_BLOB ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'smw_foo' ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ '_foo' => $propertyTable ] ) );
+
+ $requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $requestOptions->expects( $this->any() )
+ ->method( 'getExtraConditions' )
+ ->will( $this->returnValue( [] ) );
+
+ $requestOptions->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 42 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableName' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $query = new \SMW\MediaWiki\Connection\Query( $connection );
+
+ $resultWrapper = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $instance = new EntityValueUniquenessConstraintChecker(
+ $store,
+ $this->iteratorFactory
+ );
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItem = $this->getMockBuilder( '\SMWDIBlob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance->checkConstraint( $property, $dataItem, $requestOptions );
+
+ $this->assertJsonStringEqualsJsonString(
+ '{' .
+ '"tables": "smw_foo AS t1",' .
+ '"fields":[["t1.s_id"]],' .
+ '"conditions":[{"AND":["t1.o_hash="]}],' .
+ '"joins":[],' .
+ '"options":{"LIMIT":42},"alias":"t","index":1,"autocommit":false}',
+ (string)$query
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/PropertyStatisticsInvalidArgumentExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/PropertyStatisticsInvalidArgumentExceptionTest.php
new file mode 100644
index 00000000..41de869d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/PropertyStatisticsInvalidArgumentExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Exception;
+
+use SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException;
+
+/**
+ * @covers \SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyStatisticsInvalidArgumentExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new PropertyStatisticsInvalidArgumentException();
+
+ $this->assertInstanceof(
+ '\SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\InvalidArgumentException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/TableMissingIdFieldExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/TableMissingIdFieldExceptionTest.php
new file mode 100644
index 00000000..07980f1c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Exception/TableMissingIdFieldExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Exception;
+
+use SMW\SQLStore\Exception\TableMissingIdFieldException;
+
+/**
+ * @covers \SMW\SQLStore\Exception\TableMissingIdFieldException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TableMissingIdFieldExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new TableMissingIdFieldException( 'foo' );
+
+ $this->assertInstanceof(
+ TableMissingIdFieldException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/InstallerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/InstallerTest.php
new file mode 100644
index 00000000..084e4fcb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/InstallerTest.php
@@ -0,0 +1,348 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use Onoi\MessageReporter\MessageReporterFactory;
+use SMW\SQLStore\Installer;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\Installer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class InstallerTest extends \PHPUnit_Framework_TestCase {
+
+ private $spyMessageReporter;
+ private $testEnvironment;
+ private $tableSchemaManager;
+ private $tableBuilder;
+ private $tableIntegrityExaminer;
+ private $file;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyMessageReporter = MessageReporterFactory::getInstance()->newSpyMessageReporter();
+
+ $this->tableSchemaManager = $this->getMockBuilder( '\SMW\SQLStore\TableSchemaManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableIntegrityExaminer = $this->getMockBuilder( '\SMW\SQLStore\TableIntegrityExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->file = $this->getMockBuilder( '\SMW\Utils\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Installer::class,
+ new Installer( $this->tableSchemaManager, $this->tableBuilder, $this->tableIntegrityExaminer )
+ );
+ }
+
+ public function testInstall() {
+
+ $table = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\Table' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableSchemaManager->expects( $this->atLeastOnce() )
+ ->method( 'getTables' )
+ ->will( $this->returnValue( [ $table ] ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'create' ] )
+ ->getMockForAbstractClass();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'create' );
+
+ $this->tableIntegrityExaminer->expects( $this->once() )
+ ->method( 'checkOnPostCreation' );
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->setFile( $this->file );
+
+ $instance->setOptions(
+ [
+ Installer::OPT_SCHEMA_UPDATE => false
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->install()
+ );
+ }
+
+ public function testInstallWithSupplementJobs() {
+
+ $this->jobQueue->expects( $this->exactly( 2 ) )
+ ->method( 'push' );
+
+ $table = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\Table' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableSchemaManager->expects( $this->atLeastOnce() )
+ ->method( 'getTables' )
+ ->will( $this->returnValue( [ $table ] ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'create' ] )
+ ->getMockForAbstractClass();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'create' );
+
+ $this->tableIntegrityExaminer->expects( $this->once() )
+ ->method( 'checkOnPostCreation' );
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->setFile( $this->file );
+
+ $instance->setOptions(
+ [
+ Installer::OPT_SUPPLEMENT_JOBS => true
+ ]
+ );
+
+ $instance->install();
+ }
+
+ public function testInstallNonVerbose() {
+
+ $table = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\Table' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableSchemaManager->expects( $this->atLeastOnce() )
+ ->method( 'getTables' )
+ ->will( $this->returnValue( [ $table ] ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'create' ] )
+ ->getMockForAbstractClass();
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->setFile( $this->file );
+
+ $this->assertTrue(
+ $instance->install( false )
+ );
+ }
+
+ public function testUninstall() {
+
+ $table = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\Table' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tableSchemaManager->expects( $this->once() )
+ ->method( 'getTables' )
+ ->will( $this->returnValue( [ $table ] ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'drop' ] )
+ ->getMockForAbstractClass();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'drop' );
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+
+ $this->assertTrue(
+ $instance->uninstall()
+ );
+ }
+
+ public function testReportMessage() {
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $this->tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $callback = function() use( $instance ) {
+ $instance->reportMessage( 'Foo' );
+ };
+
+ $this->assertEquals(
+ 'Foo',
+ $this->testEnvironment->outputFromCallbackExec( $callback )
+ );
+ }
+
+ public function testIsGoodSchema() {
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $this->tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->isGoodSchema()
+ );
+ }
+
+ public function testMakeUpgradeKey() {
+
+ $var1 = [
+ 'smwgUpgradeKey' => '',
+ 'smwgFixedProperties' => [ 'Foo', 'Bar' ],
+ 'smwgPageSpecialProperties' => [ 'Foo', 'Bar' ]
+ ];
+
+ $var2 = [
+ 'smwgUpgradeKey' => '',
+ 'smwgFixedProperties' => [ 'Bar', 'Foo' ],
+ 'smwgPageSpecialProperties' => [ 'Bar', 'Foo' ]
+ ];
+
+ $this->assertEquals(
+ Installer::makeUpgradeKey( $var1 ),
+ Installer::makeUpgradeKey( $var2 )
+ );
+ }
+
+ public function testMakeUpgradeKey_SpecialFixedProperties() {
+
+ $var1 = [
+ 'smwgUpgradeKey' => '',
+ 'smwgFixedProperties' => [ 'Foo', 'Bar' ],
+ 'smwgPageSpecialProperties' => [ 'Foo', 'Bar' ]
+ ];
+
+ $var2 = [
+ 'smwgUpgradeKey' => '',
+ 'smwgFixedProperties' => [ 'Bar', 'Foo' ],
+ 'smwgPageSpecialProperties' => [ 'Bar', '_MDAT' ]
+ ];
+
+ $this->assertNotEquals(
+ Installer::makeUpgradeKey( $var1 ),
+ Installer::makeUpgradeKey( $var2 )
+ );
+ }
+
+ public function testSetUpgradeKey() {
+
+ $file = $this->getMockBuilder( '\SMW\Utils\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $file->expects( $this->once() )
+ ->method( 'write' );
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $this->tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $vars = [
+ 'smwgConfigFileDir' => 'Foo/',
+ 'smwgIP' => '',
+ 'smwgUpgradeKey' => '',
+ 'smwgFixedProperties' => [],
+ 'smwgPageSpecialProperties' => []
+ ];
+
+ $instance->setUpgradeKey( $vars, $this->spyMessageReporter, $file );
+ }
+
+ public function testSetUpgradeFile() {
+
+ $expected = json_encode( [ \SMW\Site::id() => [ 'Foo' => 42 ] ], JSON_PRETTY_PRINT );
+
+ $file = $this->getMockBuilder( '\SMW\Utils\File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $file->expects( $this->once() )
+ ->method( 'write' )
+ ->with(
+ $this->equalTo( 'Foo_dir/.smw.json' ),
+ $this->equalTo( $expected ) );
+
+ $instance = new Installer(
+ $this->tableSchemaManager,
+ $this->tableBuilder,
+ $this->tableIntegrityExaminer
+ );
+
+ $vars = [
+ 'smwgConfigFileDir' => 'Foo_dir'
+ ];
+
+ $instance->setUpgradeFile( $vars, [ 'Foo' => 42 ], $file );
+ }
+
+ public function testIncompleteTasks() {
+
+ $vars = [
+ 'smw.json' => [ \SMW\Site::id() => [ Installer::POPULATE_HASH_FIELD_COMPLETE => false ] ]
+ ];
+
+ $this->assertEquals(
+ [ 'smw-install-incomplete-populate-hash-field' ],
+ Installer::incompleteTasks( $vars )
+ );
+
+ $this->assertEquals(
+ [],
+ Installer::incompleteTasks( [] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/CachedListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/CachedListLookupTest.php
new file mode 100644
index 00000000..139568f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/CachedListLookupTest.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\SQLStore\Lookup\CachedListLookup;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\CachedListLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CachedListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $listLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\CachedListLookup',
+ new CachedListLookup( $listLookup, $cache, new \stdClass )
+ );
+ }
+
+ public function testfetchListFromCache() {
+
+ $expectedCachedItem = [
+ 'time' => 42,
+ 'list' => [ 'Foo' ]
+ ];
+
+ $listLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listLookup->expects( $this->atLeastOnce() )
+ ->method( 'getHash' )
+ ->will( $this->returnValue( 'Bar#123' ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->with( $this->stringContains( 'cacheprefix-foobar:smw:store:lookup:' ) )
+ ->will( $this->returnValue( true ) );
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( serialize( $expectedCachedItem ) ) );
+
+ $cacheOptions = new \stdClass;
+ $cacheOptions->useCache = true;
+
+ $instance = new CachedListLookup( $listLookup, $cache, $cacheOptions );
+ $instance->setCachePrefix( 'cacheprefix-foobar' );
+
+ $this->assertEquals(
+ [ 'Foo' ],
+ $instance->fetchList()
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->getTimestamp()
+ );
+
+ $this->assertEquals(
+ 'Bar#123',
+ $instance->getHash()
+ );
+
+ $this->assertTrue(
+ $instance->isFromCache()
+ );
+ }
+
+ public function testRetrieveResultListFromInjectedListLookup() {
+
+ $expectedCacheItem = [
+ 'time' => 42,
+ 'list' => [ 'Foo' ]
+ ];
+
+ $listLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listLookup->expects( $this->once() )
+ ->method( 'fetchList' )
+ ->will( $this->returnValue( [ 'Foo' ] ) );
+
+ $listLookup->expects( $this->once() )
+ ->method( 'getTimestamp' )
+ ->will( $this->returnValue( 42 ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->at( 1 ) )
+ ->method( 'save' )
+ ->with(
+ $this->stringContains( 'smw:store:lookup' ),
+ $this->anything( serialize( $expectedCacheItem ) ),
+ $this->equalTo( 1001 ) );
+
+ $cacheOptions = new \stdClass;
+ $cacheOptions->useCache = false;
+ $cacheOptions->ttl = 1001;
+
+ $instance = new CachedListLookup( $listLookup, $cache, $cacheOptions );
+
+ $this->assertEquals(
+ [ 'Foo' ],
+ $instance->fetchList()
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->getTimestamp()
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+ }
+
+ public function testDeleteCache() {
+
+ $listLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $listLookup->expects( $this->once() )
+ ->method( 'getHash' )
+ ->will( $this->returnValue( 'Foo#123' ) );
+
+ $cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $cache->expects( $this->once() )
+ ->method( 'fetch' )
+ ->will( $this->returnValue( serialize( [ 'smw:store:lookup:6283479db90b04ad3a6db333a3c89766' => true ] ) ) );
+
+ $cache->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->with(
+ $this->stringContains( 'smw:store:lookup' ) );
+
+ $cacheOptions = new \stdClass;
+
+ $instance = new CachedListLookup( $listLookup, $cache, $cacheOptions );
+ $instance->deleteCache();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyLabelSimilarityLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyLabelSimilarityLookupTest.php
new file mode 100644
index 00000000..ff781d44
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyLabelSimilarityLookupTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DataItemFactory;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PropertyLabelSimilarityLookupTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $propertyStatisticsStore;
+ private $requestOptions;
+ private $dataItemFactory;
+
+ protected function setUp() {
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions = $this->getMockBuilder( '\SMW\RequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\PropertyLabelSimilarityLookup',
+ new PropertyLabelSimilarityLookup( $this->store )
+ );
+ }
+
+ public function testGetPropertyMaxCount() {
+
+ $this->store->expects( $this->any() )
+ ->method( 'getStatistics' )
+ ->will( $this->returnValue( [ 'TOTALPROPS' => 42 ] ) );
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyLabelSimilarityLookup(
+ $this->store,
+ $propertySpecificationLookup
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->getPropertyMaxCount()
+ );
+ }
+
+ public function testCompareAndFindLabels() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Foo';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyLabelSimilarityLookup(
+ $this->store,
+ $propertySpecificationLookup
+ );
+
+ $requestOptions = new RequestOptions();
+
+ $instance->compareAndFindLabels( $requestOptions );
+
+ $this->assertEquals(
+ 1,
+ $instance->getLookupCount()
+ );
+ }
+
+ public function testCompareAndFindLabelsWithExemption() {
+
+ $row1 = new \stdClass;
+ $row1->smw_title = 'Foo';
+
+ $row2 = new \stdClass;
+ $row2->smw_title = 'Foobar';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row1, $row2 ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertySpecificationLookup->expects( $this->any() )
+ ->method( 'getSpecification' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIWikiPage( 'Foobar', SMW_NS_PROPERTY ) ] ) );
+
+ $instance = new PropertyLabelSimilarityLookup(
+ $this->store,
+ $propertySpecificationLookup
+ );
+
+ $requestOptions = new RequestOptions();
+
+ $instance->setExemptionProperty( 'Bar' );
+ $instance->setThreshold( 10 );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->compareAndFindLabels( $requestOptions )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyUsageListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyUsageListLookupTest.php
new file mode 100644
index 00000000..fd6a1f04
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/PropertyUsageListLookupTest.php
@@ -0,0 +1,216 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\PropertyUsageListLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\PropertyUsageListLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class PropertyUsageListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $propertyStatisticsStore;
+ private $requestOptions;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions = $this->getMockBuilder( '\SMWRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\PropertyUsageListLookup',
+ new PropertyUsageListLookup( $this->store, $this->propertyStatisticsStore, $this->requestOptions )
+ );
+ }
+
+ public function testListLookupInterfaceMethodAccess() {
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getTimestamp()
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+
+ $this->assertContains(
+ 'PropertyUsageListLookup',
+ $instance->getHash()
+ );
+ }
+
+ public function testLookupIdentifierChangedByRequestOptions() {
+
+ $requestOptions = new RequestOptions();
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $requestOptions
+ );
+
+ $lookupIdentifier = $instance->getHash();
+
+ $requestOptions->limit = 100;
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $requestOptions
+ );
+
+ $this->assertNotSame(
+ $lookupIdentifier,
+ $instance->getHash()
+ );
+ }
+
+ public function testTryTofetchListForMissingOptionsThrowsException() {
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->fetchList();
+ }
+
+ /**
+ * @dataProvider usageCountProvider
+ */
+ public function testfetchListForValidProperty( $expectedCount ) {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Foo';
+ $row->smw_id = 42;
+ $row->usage_count = $expectedCount;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->requestOptions = $this->getMockBuilder( '\SMWRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $property = new DIProperty( 'Foo' );
+ $property->id = 42;
+
+ $expected = [
+ $property,
+ $expectedCount
+ ];
+
+ $this->assertEquals(
+ [ $expected ],
+ $result
+ );
+ }
+
+ public function testfetchListForInvalidProperty() {
+
+ $row = new \stdClass;
+ $row->smw_title = '-Foo';
+ $row->smw_id = 42;
+ $row->usage_count = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->requestOptions->limit = 1001;
+
+ $instance = new PropertyUsageListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $result[0][0]
+ );
+ }
+
+ public function usageCountProvider() {
+
+ $provider[] = [
+ 0
+ ];
+
+ $provider[] = [
+ 1001
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/ProximityPropertyValueLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/ProximityPropertyValueLookupTest.php
new file mode 100644
index 00000000..4fd1f5c6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/ProximityPropertyValueLookupTest.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\ProximityPropertyValueLookup;
+use SMW\MediaWiki\Connection\Query;
+use FakeResultWrapper;
+
+/**
+ * @covers \SMW\MediaWiki\Api\Browse\ProximityPropertyValueLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ProximityPropertyValueLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ ProximityPropertyValueLookup::class,
+ new ProximityPropertyValueLookup( $store )
+ );
+ }
+
+ public function testLookup_wpg_property() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Test';
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = new Query( $connection );
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( new FakeResultWrapper( [ $row ] ) ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'isFixedPropertyTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new ProximityPropertyValueLookup(
+ $store
+ );
+
+ $parameters = [
+ 'search' => 'Foo',
+ 'property' => 'Bar'
+ ];
+
+ $instance->lookup(
+ new DIProperty( 'Bar' ),
+ 'Foo',
+ new RequestOptions()
+ );
+
+ $this->assertContains(
+ '[{"OR":"smw_sortkey LIKE %Foo%"},{"OR":"smw_sortkey LIKE %Foo%"},{"OR":"smw_sortkey LIKE %FOO%"}]',
+ $query->__toString()
+ );
+ }
+
+ public function tesLookup_txt_property() {
+
+ $row = new \stdClass;
+ $row->o_hash = 'Test';
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = new Query( $connection );
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'newQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'query' )
+ ->will( $this->returnValue( new FakeResultWrapper( [ $row ] ) ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID', 'isFixedPropertyTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( false ) );
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->any() )
+ ->method( 'getLabelField' )
+ ->will( $this->returnValue( 'o_hash' ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new ProximityPropertyValueLookup(
+ $store
+ );
+
+ $parameters = [
+ 'search' => 'Foo',
+ 'property' => 'Text'
+ ];
+
+ $instance->lookup(
+ new DIProperty( '_TEXT' ),
+ 'Foo',
+ new RequestOptions()
+ );
+
+ $this->assertContains(
+ '[{"OR":"o_hash LIKE %Foo%"},{"OR":"o_hash LIKE %Foo%"},{"OR":"o_hash LIKE %FOO%"},{"AND":"p_id=42"}]',
+ $query->__toString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/RedirectTargetLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/RedirectTargetLookupTest.php
new file mode 100644
index 00000000..e3317c5c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/RedirectTargetLookupTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\Lookup\RedirectTargetLookup;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\RedirectTargetLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class RedirectTargetLookupTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\RedirectTargetLookup',
+ new RedirectTargetLookup( $store, $circularReferenceGuard )
+ );
+ }
+
+ public function testFindRedirectTargetOnValidDataItem() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getRedirectTarget' );
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $circularReferenceGuard->expects( $this->atLeastOnce() )
+ ->method( 'isCircular' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new RedirectTargetLookup(
+ $store,
+ $circularReferenceGuard
+ );
+
+ $instance->findRedirectTarget( DIWikiPage::newFromText( 'Foo' ) );
+ }
+
+ public function testFindRedirectTargetOnInvalidDataItem() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'getRedirectTarget' );
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $circularReferenceGuard->expects( $this->never() )
+ ->method( 'isCircular' );
+
+ $instance = new RedirectTargetLookup(
+ $store,
+ $circularReferenceGuard
+ );
+
+ $instance->findRedirectTarget( 'foo' );
+ }
+
+ public function testFindRedirectTargetOnSelfReferencedDataItem() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->never() )
+ ->method( 'getRedirectTarget' );
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $circularReferenceGuard->expects( $this->atLeastOnce() )
+ ->method( 'isCircular' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new RedirectTargetLookup(
+ $store,
+ $circularReferenceGuard
+ );
+
+ $instance->findRedirectTarget( DIWikiPage::newFromText( 'Foo' ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UndeclaredPropertyListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UndeclaredPropertyListLookupTest.php
new file mode 100644
index 00000000..4766e9f2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UndeclaredPropertyListLookupTest.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\UndeclaredPropertyListLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\UndeclaredPropertyListLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class UndeclaredPropertyListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $requestOptions;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions = $this->getMockBuilder( '\SMWRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions->expects( $this->any() )
+ ->method( 'getExtraConditions' )
+ ->will( $this->returnValue( [] ) );
+ }
+
+ public function testCanConstruct() {
+
+ $defaultPropertyType = '_foo';
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\UndeclaredPropertyListLookup',
+ new UndeclaredPropertyListLookup( $this->store, $defaultPropertyType, null )
+ );
+ }
+
+ public function testListLookupInterfaceMethodAccess() {
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $this->requestOptions
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getTimestamp()
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+
+ $this->assertContains(
+ 'UndeclaredPropertyListLookup',
+ $instance->getHash()
+ );
+ }
+
+ public function testNullRequestOptionsThrowsException() {
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->fetchList();
+ }
+
+ public function testInvalidTableIdThrowsException() {
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $this->requestOptions
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->fetchList();
+ }
+
+ public function testLookupIdentifierChangedByRequestOptions() {
+
+ $defaultPropertyType = '_foo';
+ $requestOptions = new RequestOptions();
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $requestOptions
+ );
+
+ $lookupIdentifier = $instance->getHash();
+ $requestOptions->limit = 100;
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $requestOptions
+ );
+
+
+ $this->assertNotSame(
+ $lookupIdentifier,
+ $instance->getHash()
+ );
+ }
+
+ public function testfetchListForValidProperty() {
+
+ $row = new \stdClass;
+ $row->smw_title = 'Foo';
+ $row->count = 42;
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'findTypeTableId' )
+ ->with( $this->equalTo( '_foo' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $tableDefinition ] ) );
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $expected = [
+ new DIProperty( 'Foo' ),
+ 42
+ ];
+
+ $this->assertEquals(
+ [ $expected ],
+ $result
+ );
+ }
+
+ public function testfetchListForInvalidProperty() {
+
+ $row = new \stdClass;
+ $row->smw_title = '-Foo';
+ $row->count = 42;
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'findTypeTableId' )
+ ->with( $this->equalTo( '_foo' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $tableDefinition ] ) );
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $result[0][0]
+ );
+ }
+
+ public function testfetchListForFixedPropertyTable() {
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->never() )
+ ->method( 'select' );
+
+ $this->store->expects( $this->once() )
+ ->method( 'findTypeTableId' )
+ ->with( $this->equalTo( '_foo' ) )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $tableDefinition ] ) );
+
+ $defaultPropertyType = '_foo';
+
+ $instance = new UndeclaredPropertyListLookup(
+ $this->store,
+ $defaultPropertyType,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UnusedPropertyListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UnusedPropertyListLookupTest.php
new file mode 100644
index 00000000..01599519
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UnusedPropertyListLookupTest.php
@@ -0,0 +1,217 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\DIProperty;
+use SMW\RequestOptions;
+use SMW\SQLStore\Lookup\UnusedPropertyListLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\UnusedPropertyListLookup
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class UnusedPropertyListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $propertyStatisticsStore;
+ private $requestOptions;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions = $this->getMockBuilder( '\SMWRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\UnusedPropertyListLookup',
+ new UnusedPropertyListLookup( $this->store, $this->propertyStatisticsStore, null )
+ );
+ }
+
+ public function testListLookupInterfaceMethodAccess() {
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getTimestamp()
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+
+ $this->assertContains(
+ 'UnusedPropertyListLookup',
+ $instance->getHash()
+ );
+ }
+
+ public function testLookupIdentifierChangedByRequestOptions() {
+
+ $requestOptions = new RequestOptions();
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $requestOptions
+ );
+
+ $lookupIdentifier = $instance->getHash();
+
+ $requestOptions->limit = 100;
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $requestOptions
+ );
+
+ $this->assertNotSame(
+ $lookupIdentifier,
+ $instance->getHash()
+ );
+ }
+
+ public function testTryTofetchListForMissingOptionsThrowsException() {
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->fetchList();
+ }
+
+ public function testfetchListForValidProperty() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getIdTable' ] )
+ ->getMock();
+
+ $row = new \stdClass;
+ $row->smw_title = 'Foo';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $expected = [
+ new DIProperty( 'Foo' )
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $result
+ );
+ }
+
+ public function testfetchListForInvalidProperty() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getIdTable' ] )
+ ->getMock();
+
+ $row = new \stdClass;
+ $row->smw_title = '-Foo';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'ORDER BY' => 'smw_sort', 'LIMIT' => 1001, 'OFFSET' => 0 ] ),
+ $this->anything() )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $requestOptions = $this->getMockBuilder( '\SMWRequestOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->requestOptions->limit = 1001;
+
+ $instance = new UnusedPropertyListLookup(
+ $this->store,
+ $this->propertyStatisticsStore,
+ $this->requestOptions
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $result[0]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UsageStatisticsListLookupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UsageStatisticsListLookupTest.php
new file mode 100644
index 00000000..643ba3b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/Lookup/UsageStatisticsListLookupTest.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Lookup;
+
+use SMW\SQLStore\Lookup\UsageStatisticsListLookup;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\Lookup\UsageStatisticsListLookup
+ * @group semantic-mediawiki
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class UsageStatisticsListLookupTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $propertyStatisticsStore;
+ private $requestOptions;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\UsageStatisticsListLookup',
+ new UsageStatisticsListLookup( $this->store, $this->propertyStatisticsStore )
+ );
+ }
+
+ public function testListLookupInterfaceMethodAccess() {
+
+ $instance = new UsageStatisticsListLookup(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getTimestamp()
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+
+ $this->assertEquals(
+ 'statistics-lookup',
+ $instance->getHash()
+ );
+ }
+
+ public function testfetchListForInvalidTableThrowsException() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( new \FakeResultWrapper( [] ) ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'Bar' ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => 'throwExceptionForMismatch' ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new UsageStatisticsListLookup(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->fetchList();
+ }
+
+ /**
+ * @dataProvider bySegmentDataProvider
+ */
+ public function testfetchList( $segment, $type ) {
+
+ $row = new \stdClass;
+ $row->o_hash = 42;
+ $row->count = 1001;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'select' )
+ ->will( $this->returnValue( new \FakeResultWrapper( [ $row ] ) ) );
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdFetcher = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSMWPropertyID' ] )
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdFetcher ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $tableDefinition ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->propertyStatisticsStore->expects( $this->any() )
+ ->method( 'getUsageCount' )
+ ->will( $this->returnValue( 54 ) );
+
+ $instance = new UsageStatisticsListLookup(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $result = $instance->fetchList();
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+
+ $this->assertArrayHasKey(
+ $segment,
+ $result
+ );
+
+ $this->assertInternalType(
+ $type,
+ $result[$segment]
+ );
+ }
+
+ public function bySegmentDataProvider() {
+ return [
+ [ 'OWNPAGE', 'integer' ],
+ [ 'QUERY', 'integer' ],
+ [ 'QUERYSIZE', 'integer' ],
+ [ 'QUERYFORMATS', 'array' ],
+ [ 'CONCEPTS', 'integer' ],
+ [ 'SUBOBJECTS', 'integer' ],
+ [ 'DECLPROPS', 'integer' ],
+ [ 'USEDPROPS', 'integer' ],
+ [ 'TOTALPROPS', 'integer' ],
+ [ 'PROPUSES', 'integer' ],
+ [ 'ERRORUSES', 'integer' ],
+ [ 'DELETECOUNT', 'integer' ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyStatisticsStoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyStatisticsStoreTest.php
new file mode 100644
index 00000000..3deed821
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyStatisticsStoreTest.php
@@ -0,0 +1,378 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\PropertyStatisticsStore;
+use SMW\SQLStore\SQLStore;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\PropertyStatisticsStore
+ * @group semantic-mediawiki
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class PropertyStatisticsStoreTest extends MwDBaseUnitTestCase {
+
+ use PHPUnitCompat;
+
+ protected $statsTable = null;
+
+ /**
+ * On the Windows platform pow( 2 , 31 ) returns with
+ * "MWException: The value to add must be a positive integer" therefore
+ * return true if this test runs on Windows
+ *
+ * @return boolean
+ */
+ private function isWinOS() {
+ return strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
+ }
+
+ public function testDeleteAll() {
+
+ $statsTable = new PropertyStatisticsStore(
+ $this->getStore()->getConnection( 'mw.db' )
+ );
+
+ $this->assertTrue( $statsTable->deleteAll() !== false );
+ $this->assertTrue( $statsTable->deleteAll() !== false );
+
+ $statsTable->insertUsageCount( 1, 1 );
+
+ $this->assertTrue( $statsTable->deleteAll() !== false );
+
+ $this->assertTrue( $statsTable->getUsageCounts( [ 1, 2 ] ) === [] );
+ }
+
+ public function usageCountProvider() {
+ $usageCounts = [];
+
+ $usageCounts[] = [ 1, 0 ];
+ $usageCounts[] = [ 2, 1 ];
+
+ for ( $propId = 3; $propId <= 42; $propId++ ) {
+ $usageCounts[] = [ $propId, mt_rand( 0, 100000 ) ];
+ }
+
+ $usageCounts[] = [ 9001, $this->isWinOS() ? pow( 2, 30 ) : pow( 2, 31 ) ];
+
+ return $usageCounts;
+ }
+
+ /**
+ * @return \SMW\Store\PropertyStatisticsStore
+ */
+ protected function getTable() {
+ if ( $this->statsTable === null ) {
+
+ $this->statsTable = new PropertyStatisticsStore(
+ $this->getStore()->getConnection( 'mw.db' )
+ );
+
+ $this->assertTrue( $this->statsTable->deleteAll() !== false );
+ }
+
+ return $this->statsTable;
+ }
+
+ /**
+ * @dataProvider usageCountProvider
+ */
+ public function testGetUsageCount( $propId, $usageCount ) {
+
+ $table = $this->getTable();
+
+ $table->insertUsageCount( $propId, $usageCount );
+
+ $this->assertEquals(
+ $usageCount,
+ $table->getUsageCount( $propId )
+ );
+ }
+
+ public function testAddToUsageCountWithInvalidCountThrowsException() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyStatisticsStore(
+ $connection,
+ 'foo'
+ );
+
+ $this->setExpectedException( '\SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException');
+ $instance->addToUsageCount( 12, 'foo' );
+ }
+
+ public function testAddToUsageCountWithInvalidIdThrowsException() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $this->setExpectedException( '\SMW\SQLStore\Exception\PropertyStatisticsInvalidArgumentException');
+ $instance->addToUsageCount( 'Foo', 12 );
+ }
+
+ /**
+ * @dataProvider usageCountProvider
+ *
+ * @param int $propId
+ * @param int $usageCount
+ */
+ public function testInsertUsageCount( $propId, $usageCount ) {
+
+ $table = $this->getTable();
+
+ $this->assertTrue( $table->insertUsageCount( $propId, $usageCount ) );
+
+ $usageCounts = $table->getUsageCounts( [ $propId ] );
+
+ $this->assertArrayHasKey( $propId, $usageCounts );
+ $this->assertEquals( $usageCount, $usageCounts[$propId] );
+
+ $change = mt_rand( max( -100, -$usageCount ), 100 );
+
+ $this->assertTrue( $table->addToUsageCount( $propId, $change ) !== false );
+
+ $usageCounts = $table->getUsageCounts( [ $propId ] );
+
+ $this->assertArrayHasKey( $propId, $usageCounts );
+ $this->assertEquals( $usageCount + $change, $usageCounts[$propId], 'Testing addToUsageCount with ' . $change );
+ }
+
+ public function testAddToUsageCounts() {
+
+ $statsTable = new PropertyStatisticsStore(
+ $this->getStore()->getConnection( 'mw.db' )
+ );
+
+ $this->assertTrue( $statsTable->deleteAll() !== false );
+
+ $counts = [
+ 1 => 42,
+ 2 => 0,
+ 9001 => 9001,
+ 9002 => $this->isWinOS() ? pow( 2, 30 ) : pow( 2, 31 ),
+ 9003 => 1,
+ ];
+
+ foreach ( $counts as $propId => $count ) {
+ $this->assertTrue( $statsTable->insertUsageCount( $propId, $count ) !== false );
+ }
+
+ $additions = [
+ 2 => 42,
+ 9001 => -9000,
+ 9003 => 0,
+ ];
+
+ $this->assertTrue(
+ $statsTable->addToUsageCounts( $additions ) !== false
+ );
+
+ foreach ( $additions as $propId => $addition ) {
+ $counts[$propId] += $addition;
+ }
+
+ $this->assertEquals(
+ $counts,
+ $statsTable->getUsageCounts( array_keys( $counts ) )
+ );
+ }
+
+ public function testAddToUsageCountsOnTransactionIdle() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback );
+ }
+ ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'update' );
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $additions = [
+ 2 => 42,
+ 9001 => -9000,
+ 9003 => 0,
+ ];
+
+ $instance->waitOnTransactionIdle();
+
+ $this->assertTrue(
+ $instance->addToUsageCounts( $additions )
+ );
+ }
+
+ public function testAddToUsageCountsWillNotWaitOnTransactionIdleWhenCommandLineModeIsActive() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->never() )
+ ->method( 'onTransactionIdle' );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'update' );
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $additions = [
+ 2 => 42,
+ 9001 => -9000,
+ 9003 => 0,
+ ];
+
+ $instance->isCommandLineMode( true );
+ $instance->waitOnTransactionIdle();
+
+ $instance->addToUsageCounts( $additions );
+ }
+
+ public function testInsertUsageCountWithArrayValue() {
+
+ $tableName = 'Foo';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->with(
+ $this->stringContains( SQLStore::PROPERTY_STATISTICS_TABLE ),
+ $this->equalTo(
+ [
+ 'usage_count' => 1,
+ 'null_count' => 9999,
+ 'p_id' => 42
+ ] ),
+ $this->anything() );
+
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $instance->insertUsageCount( 42, [ 1, 9999 ] );
+ }
+
+ public function testAddToUsageCountsWithArrayValue() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->stringContains( SQLStore::PROPERTY_STATISTICS_TABLE ),
+ $this->equalTo(
+ [
+ 'usage_count = usage_count + 1',
+ 'null_count = null_count + 9999'
+ ] ),
+ $this->equalTo(
+ [
+ 'p_id' => 42
+ ] ),
+ $this->anything() );
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $instance->addToUsageCounts( [ 42 => [ 'usage' => 1, 'null' => 9999 ] ] );
+ }
+
+ public function testSetUsageCountWithArrayValue() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->stringContains( SQLStore::PROPERTY_STATISTICS_TABLE ),
+ $this->equalTo(
+ [
+ 'usage_count' => 1,
+ 'null_count' => 9999
+ ] ),
+ $this->equalTo(
+ [
+ 'p_id' => 42
+ ] ),
+ $this->anything() );
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $instance->setUsageCount( 42, [ 1, 9999 ] );
+ }
+
+ public function testUpsertOnInsertUsageCount() {
+
+ $error = $this->getMockBuilder( '\DBQueryError' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->will( $this->throwException( $error ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->stringContains( SQLStore::PROPERTY_STATISTICS_TABLE ),
+ $this->equalTo(
+ [
+ 'usage_count' => 12,
+ 'null_count' => 0
+ ] ),
+ $this->equalTo( [ 'p_id' => 42 ] ),
+ $this->anything() );
+
+ $instance = new PropertyStatisticsStore(
+ $connection
+ );
+
+ $instance->insertUsageCount( 42, 12 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionBuilderTest.php
new file mode 100644
index 00000000..ce184204
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionBuilderTest.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\PropertyTableDefinitionBuilder;
+use SMW\Tests\Utils\MwHooksHandler;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableDefinitionBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PropertyTableDefinitionBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $propertyTypeFinder;
+ private $mwHooksHandler;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mwHooksHandler = new MwHooksHandler();
+ $this->mwHooksHandler->deregisterListedHooks();
+
+ $this->propertyTypeFinder = $this->getMockBuilder( '\SMW\SQLStore\PropertyTypeFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+
+ $this->mwHooksHandler->restoreListedHooks();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $dataItems = [];
+ $specials = [];
+ $fixed = [];
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableDefinitionBuilder',
+ new PropertyTableDefinitionBuilder( $this->propertyTypeFinder )
+ );
+ }
+
+ public function testDataItemTypes() {
+
+ $dataItems = [ DataItem::TYPE_NUMBER => 'smw_di_number' ];
+ $specials = [];
+ $fixed = [];
+
+ $instance = new PropertyTableDefinitionBuilder(
+ $this->propertyTypeFinder
+ );
+
+ $instance->doBuild(
+ $dataItems,
+ $specials,
+ $fixed
+ );
+
+ $definition = $instance->newTableDefinition(
+ DataItem::TYPE_NUMBER, 'smw_di_number'
+ );
+
+ $expected = [
+ 'smw_di_number' => $definition
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getTableDefinitions()
+ );
+ }
+
+ public function testUserDefinedFixedPropertyDeclaration() {
+
+ $propertyKey = 'foo bar';
+ $expectedKey = 'Foo_bar';
+
+ $dataItems = [];
+ $specials = [];
+ $fixed = [ $propertyKey ];
+
+ $this->propertyTypeFinder->expects( $this->any() )
+ ->method( 'findTypeID' )
+ ->will( $this->returnValue( '_num' ) );
+
+ $instance = new PropertyTableDefinitionBuilder(
+ $this->propertyTypeFinder
+ );
+
+ $instance->doBuild(
+ $dataItems,
+ $specials,
+ $fixed
+ );
+
+ $tableName = $instance->createHashedTableNameFrom( $expectedKey );
+ $definition = $instance->newTableDefinition( DataItem::TYPE_NUMBER, $tableName, $expectedKey );
+
+ $expected = [
+ 'definition' => [ $tableName => $definition ],
+ 'tableId' => [ $expectedKey => $tableName, '_SKEY' => null ]
+ ];
+
+ $this->assertEquals(
+ $expected['definition'],
+ $instance->getTableDefinitions()
+ );
+
+ $this->assertEquals(
+ $expected['tableId'],
+ $instance->getFixedPropertyTableIds()
+ );
+ }
+
+ public function testSpecialProperties() {
+
+ $propertyKey = '_MDAT';
+
+ $dataItems = [];
+ $specials = [ $propertyKey ];
+ $fixed = [];
+
+ $instance = new PropertyTableDefinitionBuilder(
+ $this->propertyTypeFinder
+ );
+
+ $instance->doBuild(
+ $dataItems,
+ $specials,
+ $fixed
+ );
+
+ $tableName = $instance->getTablePrefix() . strtolower( $propertyKey );
+ $definition = $instance->newTableDefinition( DataItem::TYPE_TIME, $tableName, $propertyKey );
+ $expected = [ $tableName => $definition ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getTableDefinitions()
+ );
+ }
+
+ public function testRedirects() {
+
+ $propertyKey = '_REDI';
+
+ $dataItems = [];
+ $specials = [ $propertyKey ];
+ $fixed = [];
+
+ $instance = new PropertyTableDefinitionBuilder(
+ $this->propertyTypeFinder
+ );
+
+ $instance->doBuild(
+ $dataItems,
+ $specials,
+ $fixed
+ );
+
+ $tableName = $instance->getTablePrefix() . strtolower( $propertyKey );
+ $definition = $instance->newTableDefinition( DataItem::TYPE_WIKIPAGE, $tableName, $propertyKey );
+
+ $expected = [ $tableName => $definition ];
+ $tableDefinitions = $instance->getTableDefinitions();
+
+ $this->assertFalse(
+ $tableDefinitions[$tableName]->usesIdSubject()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionTest.php
new file mode 100644
index 00000000..07233596
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableDefinitionTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\PropertyTableDefinition;
+use SMW\StoreFactory;
+use SMWDataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableDefinition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PropertyTableDefinitionTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableDefinition',
+ new PropertyTableDefinition( 'foo', 'bar' )
+ );
+ }
+
+ public function testGetters() {
+
+ $diType = SMWDataItem::TYPE_NUMBER;
+ $name = 'smw_di_number';
+
+ $instance = new PropertyTableDefinition( $diType, $name );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getFields( StoreFactory::getStore( 'SMWSQLStore3' ) )
+ );
+
+ $this->assertEquals(
+ $diType,
+ $instance->getDiType()
+ );
+
+ $this->assertEquals(
+ $name,
+ $instance->getName()
+ );
+ }
+
+ public function testIdSubject() {
+
+ $instance = new PropertyTableDefinition( 'foo', 'bar' );
+ $instance->setUsesIdSubject( false );
+
+ $this->assertFalse(
+ $instance->usesIdSubject()
+ );
+ }
+
+ public function testGetFixedProperty() {
+
+ $instance = new PropertyTableDefinition( 'foo', 'bar' );
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->getFixedProperty();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceDisposerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceDisposerTest.php
new file mode 100644
index 00000000..7cb86c00
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceDisposerTest.php
@@ -0,0 +1,362 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\PropertyTableIdReferenceDisposer;
+use SMW\SQLStore\SQLStore;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableIdReferenceDisposer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyTableIdReferenceDisposerTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemById' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getDataItemById' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( 'Foo' ) ) );
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $jobQueueGroup = $this->getMockBuilder( '\JobQueueGroup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobQueueGroup->expects( $this->any() )
+ ->method( 'lazyPush' );
+
+ $this->testEnvironment->registerObject( 'JobQueueGroup', $jobQueueGroup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyTableIdReferenceDisposer::class,
+ new PropertyTableIdReferenceDisposer( $this->store )
+ );
+ }
+
+ public function testIsDisposable() {
+
+ $propertyTableIdReferenceFinder = $connection = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableIdReferenceFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableIdReferenceFinder->expects( $this->any() )
+ ->method( 'hasResidualReferenceForId' )
+ ->with( $this->equalTo( 42 ) )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTableIdReferenceFinder' )
+ ->will( $this->returnValue( $propertyTableIdReferenceFinder ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $this->assertTrue(
+ $instance->isDisposable( 42 )
+ );
+ }
+
+ public function testTryToRemoveOutdatedEntryFromIDTable() {
+
+ $tableDefinition = $connection = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableIdReferenceFinder = $connection = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableIdReferenceFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->at( 0 ) )
+ ->method( 'delete' )
+ ->with( $this->equalTo( \SMW\SQLStore\SQLStore::ID_TABLE ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTableIdReferenceFinder' )
+ ->will( $this->returnValue( $propertyTableIdReferenceFinder ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $instance->removeOutdatedEntityReferencesById( 42 );
+ }
+
+ public function testCleanUpTableEntriesFor() {
+
+ $tableDefinition = $connection = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->at( 3 ) )
+ ->method( 'delete' )
+ ->with( $this->equalTo( \SMW\SQLStore\SQLStore::ID_TABLE ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $instance->cleanUpTableEntriesById( 42 );
+ }
+
+ public function testCanConstructOutdatedEntitiesResultIterator() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Iterators\ResultIterator',
+ $instance->newOutdatedEntitiesResultIterator()
+ );
+ }
+
+ public function testCleanUpTableEntriesByRow() {
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'delete' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $instance->cleanUpTableEntriesByRow( $row );
+ }
+
+ public function testCleanUpOnTransactionIdle() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback );
+ }
+ ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'delete' );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $this->store
+ );
+
+ $instance->waitOnTransactionIdle();
+ $instance->cleanUpTableEntriesById( 42 );
+ }
+
+ public function testCleanUpOnTransactionIdleAvoidOnSubobject() {
+
+ if ( !method_exists( '\PHPUnit_Framework_MockObject_Builder_InvocationMocker', 'withConsecutive' ) ) {
+ $this->markTestSkipped( 'PHPUnit_Framework_MockObject_Builder_InvocationMocker::withConsecutive requires PHPUnit 5.7+.' );
+ }
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemById' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getDataItemById' )
+ ->will( $this->returnValue( new DIWikiPage( 'Foo', NS_MAIN, '', 'Bar' ) ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return $callback();
+ } ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->withConsecutive(
+ [ $this->equalTo( SQLStore::ID_TABLE ) ],
+ [ $this->equalTo( SQLStore::PROPERTY_STATISTICS_TABLE ) ],
+ [ $this->equalTo( SQLStore::QUERY_LINKS_TABLE ) ],
+ [ $this->equalTo( SQLStore::QUERY_LINKS_TABLE ) ],
+ [ $this->equalTo( SQLStore::FT_SEARCH_TABLE ) ]
+ );
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $store
+ );
+
+ $instance->waitOnTransactionIdle();
+ $instance->cleanUpTableEntriesById( 42 );
+ }
+
+ public function testCleanUp_Redirect() {
+
+ if ( !method_exists( '\PHPUnit_Framework_MockObject_Builder_InvocationMocker', 'withConsecutive' ) ) {
+ $this->markTestSkipped( 'PHPUnit_Framework_MockObject_Builder_InvocationMocker::withConsecutive requires PHPUnit 5.7+.' );
+ }
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemById' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getDataItemById' )
+ ->will( $this->returnValue( new DIWikiPage( 'Foo', NS_MAIN, SMW_SQL3_SMWREDIIW ) ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // No SQLStore::ID_TABLE
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'delete' )
+ ->withConsecutive(
+ [ $this->equalTo( SQLStore::PROPERTY_STATISTICS_TABLE ) ],
+ [ $this->equalTo( SQLStore::QUERY_LINKS_TABLE ) ],
+ [ $this->equalTo( SQLStore::QUERY_LINKS_TABLE ) ],
+ [ $this->equalTo( SQLStore::FT_SEARCH_TABLE ) ]
+ );
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceDisposer(
+ $store
+ );
+
+ $instance->cleanUpTableEntriesById( 42 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceFinderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceFinderTest.php
new file mode 100644
index 00000000..6a5fa782
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableIdReferenceFinderTest.php
@@ -0,0 +1,209 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIProperty;
+use SMW\SQLStore\PropertyTableIdReferenceFinder;
+use SMW\SQLStore\SQLStore;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableIdReferenceFinder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class PropertyTableIdReferenceFinderTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableIdReferenceFinder',
+ new PropertyTableIdReferenceFinder( $this->store )
+ );
+ }
+
+ public function testFindAtLeastOneActiveReferenceById() {
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getFields' )
+ ->will( $this->returnValue( [ 'o_id' => 42 ] ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $this->assertFalse(
+ $instance->findAtLeastOneActiveReferenceById( 42 )
+ );
+ }
+
+ public function testTryToFindAtLeastOneReferenceForProperty() {
+
+ $idTable = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition = $connection = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $instance->tryToFindAtLeastOneReferenceForProperty( new DIProperty( 'Foo' ) );
+ }
+
+ public function testHasResidualPropertyTableReference() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->hasResidualPropertyTableReference( 42 )
+ );
+ }
+
+ public function testHasResidualReferenceFor() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->hasResidualReferenceForId( 42 )
+ );
+ }
+
+ public function testSearchAllTablesToFindAtLeastOneReferenceById() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->searchAllTablesToFindAtLeastOneReferenceById( 42 )
+ );
+ }
+
+ public function testConfirmBorderId() {
+
+ $instance = new PropertyTableIdReferenceFinder(
+ $this->store
+ );
+
+ $this->assertTrue(
+ $instance->hasResidualReferenceForId( SQLStore::FIXED_PROPERTY_ID_UPPERBOUND )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableInfoFetcherTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableInfoFetcherTest.php
new file mode 100644
index 00000000..bf72f7f9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableInfoFetcherTest.php
@@ -0,0 +1,208 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIProperty;
+use SMW\SQLStore\PropertyTableInfoFetcher;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableInfoFetcher
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class PropertyTableInfoFetcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $propertyTypeFinder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->propertyTypeFinder = $this->getMockBuilder( '\SMW\SQLStore\PropertyTypeFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableInfoFetcher',
+ new PropertyTableInfoFetcher( $this->propertyTypeFinder )
+ );
+ }
+
+ public function testGetPropertyTableDefinitions() {
+
+ $instance = new PropertyTableInfoFetcher(
+ $this->propertyTypeFinder
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getPropertyTableDefinitions()
+ );
+
+ $instance->clearCache();
+ }
+
+ /**
+ * @dataProvider propertyProvider
+ */
+ public function testFindTableIdForProperty( $property, $expected ) {
+
+ $property = DIProperty::newFromUserLabel( $property );
+
+ $instance = new PropertyTableInfoFetcher(
+ $this->propertyTypeFinder
+ );
+
+ $instance->setCustomSpecialPropertyList(
+ [ '_MDAT', '_MEDIA', '_MIME' ]
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->findTableIdForProperty( $property )
+ );
+ }
+
+ /**
+ * @dataProvider defaultDiTypeProvider
+ */
+ public function testFindTableIdForDataItemTypeId( $diType, $expected ) {
+
+ $instance = new PropertyTableInfoFetcher(
+ $this->propertyTypeFinder
+ );
+
+ $instance->setCustomSpecialPropertyList(
+ [ '_MDAT', '_MEDIA', '_MIME' ]
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->findTableIdForDataItemTypeId( $diType )
+ );
+ }
+
+ /**
+ * @dataProvider dataTypeProvider
+ */
+ public function testFindTableIdForDataTypeTypeId( $dataType, $expected ) {
+
+ $instance = new PropertyTableInfoFetcher(
+ $this->propertyTypeFinder
+ );
+
+ $instance->setCustomSpecialPropertyList(
+ [ '_MDAT', '_MEDIA', '_MIME' ]
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->findTableIdForDataTypeTypeId( $dataType )
+ );
+ }
+
+ public function propertyProvider() {
+
+ $provider = [];
+
+ // Pre-defined property
+ $provider = [
+ [ '_MDAT', 'smw_fpt_mdat' ],
+ [ '_CDAT', 'smw_di_time' ],
+ [ '_NEWP', 'smw_di_bool' ],
+ [ '_LEDT', 'smw_di_wikipage' ],
+ [ '_MIME', 'smw_fpt_mime' ],
+ [ '_MEDIA', 'smw_fpt_media' ],
+ [ '_TYPE', 'smw_fpt_type' ],
+ [ '_UNIT', 'smw_fpt_unit' ],
+ [ '_CONV', 'smw_fpt_conv' ],
+ [ '_PVAL', 'smw_fpt_pval' ],
+ [ '_LIST', 'smw_fpt_list' ],
+ [ '_SERV', 'smw_fpt_serv' ],
+ [ '_ASK', 'smw_fpt_ask' ],
+ [ '_ASKDE', 'smw_fpt_askde' ],
+ [ '_ASKSI', 'smw_fpt_asksi' ],
+ [ '_ASKFO', 'smw_fpt_askfo' ],
+ [ '_ASKST', 'smw_fpt_askst' ],
+ [ '_ASKDU', 'smw_fpt_askdu' ],
+ [ '_SUBP', 'smw_fpt_subp' ],
+ [ '_SUBC', 'smw_fpt_subc' ],
+ [ '_INST', 'smw_fpt_inst' ],
+ [ '_REDI', 'smw_fpt_redi' ],
+ [ '_SOBJ', 'smw_fpt_sobj' ],
+ [ '_IMPO', 'smw_fpt_impo' ],
+ [ '_URI', 'smw_fpt_uri' ],
+ [ '_CONC', 'smw_fpt_conc' ],
+ ];
+
+ $provider[] = [
+ 'Modification date',
+ 'smw_fpt_mdat'
+ ];
+
+ // User-defined property
+ $provider[] = [
+ 'Foo',
+ 'smw_di_wikipage'
+ ];
+
+ return $provider;
+ }
+
+ public function defaultDiTypeProvider() {
+
+ $provider = [];
+
+ // Known
+ $provider = [
+ [ DataItem::TYPE_NUMBER, 'smw_di_number' ],
+ [ DataItem::TYPE_BLOB,'smw_di_blob' ],
+ [ DataItem::TYPE_BOOLEAN, 'smw_di_bool' ],
+ [ DataItem::TYPE_URI, 'smw_di_uri' ],
+ [ DataItem::TYPE_TIME, 'smw_di_time'],
+ [ DataItem::TYPE_GEO, 'smw_di_coords' ],
+ [ DataItem::TYPE_WIKIPAGE, 'smw_di_wikipage' ],
+ [ DataItem::TYPE_CONCEPT, '' ],
+ ];
+
+ // Unknown
+ $provider[] = [
+ 'Foo',
+ ''
+ ];
+
+ return $provider;
+ }
+
+ public function dataTypeProvider() {
+
+ $provider = [];
+
+ // Known
+ $provider = [
+ [ '_num', 'smw_di_number' ],
+ [ '_txt','smw_di_blob' ],
+ [ '_boo', 'smw_di_bool' ],
+ [ '_uri', 'smw_di_uri' ],
+ [ '_dat', 'smw_di_time'],
+ [ '_geo', 'smw_di_coords' ],
+ [ '_wpg', 'smw_di_wikipage' ],
+ ];
+
+ // Unknown
+ $provider[] = [
+ 'Foo',
+ ''
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowDifferTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowDifferTest.php
new file mode 100644
index 00000000..fedb9700
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowDifferTest.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\ChangeOp\ChangeOp;
+use SMW\SQLStore\PropertyTableRowDiffer;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableRowDiffer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class PropertyTableRowDifferTest extends \PHPUnit_Framework_TestCase {
+
+ private $propertyTableRowMapper;
+
+ protected function setUp() {
+
+ $this->propertyTableRowMapper = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableRowMapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyTableRowMapper->expects( $this->any() )
+ ->method( 'mapToRows' )
+ ->will( $this->returnValue( [ [], [], [], [] ] ) );
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableRowDiffer',
+ new PropertyTableRowDiffer( $store, $this->propertyTableRowMapper )
+ );
+ }
+
+ public function testComputeTableRowDiffForEmptyPropertyTables() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $semanticData = new SemanticData( $subject );
+
+ $propertyTables = [];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowDiffer(
+ $store,
+ $this->propertyTableRowMapper
+ );
+
+ $result = $instance->computeTableRowDiff(
+ 42,
+ $semanticData
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+ }
+
+ public function testChangeOp() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $semanticData = new SemanticData( $subject );
+
+ $propertyTables = [];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowDiffer(
+ $store,
+ $this->propertyTableRowMapper
+ );
+
+ $instance->setChangeOp( new ChangeOp( $subject ) );
+
+ $result = $instance->computeTableRowDiff(
+ 42,
+ $semanticData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\ChangeOp\ChangeOp',
+ $instance->getChangeOp()
+ );
+ }
+
+ public function testChangeOpWithUnknownFixedProperty() {
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'getFixedProperty' )
+ ->will( $this->returnValue( '_UNKNOWN_FIXED_PROPERTY' ) );
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $semanticData = new SemanticData( $subject );
+
+ $propertyTables = [ $propertyTable ];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowDiffer(
+ $store,
+ $this->propertyTableRowMapper
+ );
+
+ $instance->setChangeOp( new ChangeOp( $subject ) );
+
+ $result = $instance->computeTableRowDiff(
+ 42,
+ $semanticData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\ChangeOp\ChangeOp',
+ $instance->getChangeOp()
+ );
+
+ $this->assertEmpty(
+ $instance->getChangeOp()->getFixedPropertyRecords()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowMapperTest.php
new file mode 100644
index 00000000..fe5cb91b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableRowMapperTest.php
@@ -0,0 +1,206 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\SQLStore\PropertyTableRowMapper;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableRowMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableRowMapperTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableRowMapper',
+ new PropertyTableRowMapper( $store )
+ );
+ }
+
+ public function testMapToRowsOnEmptyTable() {
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+ $semanticData = new SemanticData( $subject );
+
+ $propertyTables = [];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowMapper(
+ $store
+ );
+
+ $result = $instance->mapToRows(
+ 42,
+ $semanticData
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $result
+ );
+ }
+
+ public function testMapToRowsWithFixedProperty() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'makeSMWPropertyID', 'makeSMWPageID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'makeSMWPropertyID' )
+ ->will( $this->returnValue( 9999 ) );
+
+ $idTable->expects( $this->once() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 1001 ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( 'Foo_test_123'),
+ new DIWikiPage( 'Bar', NS_MAIN )
+ );
+
+ $propertyTables = [ 'smw_foo' => $propertyTable ];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables', 'findPropertyTableID', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'smw_foo' ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowMapper(
+ $store
+ );
+
+ list( $rows, $textItems, $propertyList, $fixedPropertyList ) = $instance->mapToRows(
+ 42,
+ $semanticData
+ );
+
+ $this->assertArrayHasKey(
+ 'Foo_test_123',
+ $propertyList
+ );
+
+ $this->assertArrayHasKey(
+ 'smw_foo',
+ $fixedPropertyList
+ );
+ }
+
+ public function testNewChangeOp() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'makeSMWPropertyID', 'makeSMWPageID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'makeSMWPropertyID' )
+ ->will( $this->returnValue( 9999 ) );
+
+ $idTable->expects( $this->once() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 1001 ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( true ) );
+
+ $subject = new DIWikiPage( 'Foo', NS_MAIN );
+
+ $semanticData = new SemanticData( $subject );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( 'Foo_test_123'),
+ new DIWikiPage( 'Bar', NS_MAIN )
+ );
+
+ $propertyTables = [ 'smw_foo' => $propertyTable ];
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->setMethods( [ 'getPropertyTables', 'findPropertyTableID', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'smw_foo' ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( $propertyTables ) );
+
+ $instance = new PropertyTableRowMapper(
+ $store
+ );
+
+ $changeOp = $instance->newChangeOp(
+ 42,
+ $semanticData
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\ChangeOp\ChangeOp',
+ $changeOp
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableUpdaterTest.php
new file mode 100644
index 00000000..04da2e66
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/PropertyTableUpdaterTest.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\PropertyTableUpdater;
+use SMW\Parameters;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\PropertyTableUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $idTable;
+ private $connection;
+ private $propertyTable;
+ private $propertyStatisticsStore;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->idTable = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $this->idTable ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PropertyTableUpdater::class,
+ new PropertyTableUpdater( $this->store, $this->propertyStatisticsStore )
+ );
+ }
+
+ public function testUpdate_OnEmptyInsertRows() {
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->propertyStatisticsStore->expects( $this->once() )
+ ->method( 'addToUsageCounts' );
+
+ $instance = new PropertyTableUpdater(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $params= new Parameters(
+ [
+ 'insert_rows' => [],
+ 'delete_rows' => [],
+ 'new_hashes' => []
+ ]
+ );
+
+ $instance->update( 42, $params );
+ }
+
+ public function testUpdate_WithInsertRows() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'insert' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'delete' );
+
+ $this->idTable->expects( $this->once() )
+ ->method( 'setPropertyTableHashes' );
+
+ $this->propertyTable->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'table_foo' => $this->propertyTable ] ) );
+
+ $this->propertyStatisticsStore->expects( $this->once() )
+ ->method( 'addToUsageCounts' )
+ ->with( $this->equalTo( [ 99998 => -1, 99999 => 1 ] ) );
+
+ $instance = new PropertyTableUpdater(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $params = new Parameters(
+ [
+ 'insert_rows' => [
+ 'table_foo' => [
+ [ 's_id' => 1001, 'p_id' => 99999 ]
+ ]
+ ],
+ 'delete_rows' => [
+ 'table_foo' => [
+ [ 's_id' => 1001, 'p_id' => 99998 ]
+ ]
+ ],
+ 'new_hashes' => []
+ ]
+ );
+
+ $instance->update( 42, $params );
+ }
+
+ public function testUpdate_WithInsertRowsButMissingIdFieldThrowsException() {
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'table_foo' => $this->propertyTable ] ) );
+
+ $instance = new PropertyTableUpdater(
+ $this->store,
+ $this->propertyStatisticsStore
+ );
+
+ $params= new Parameters(
+ [
+ 'insert_rows' => [
+ 'table_foo' => []
+ ],
+ 'delete_rows' => [
+ 'table_foo' => []
+ ],
+ 'new_hashes' => []
+ ]
+ );
+
+ $this->setExpectedException( '\SMW\SQLStore\Exception\TableMissingIdFieldException' );
+ $instance->update( 42, $params );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksTableUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksTableUpdaterTest.php
new file mode 100644
index 00000000..817b3330
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksTableUpdaterTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DependencyLinksTableUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $spyLogger;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater',
+ new DependencyLinksTableUpdater( $this->store )
+ );
+ }
+
+ public function testAddToUpdateList() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getId' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->onConsecutiveCalls( 1001 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $insert[] = [
+ 's_id' => 42,
+ 'o_id' => 1001
+ ];
+
+ $connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->equalTo( $insert ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new DependencyLinksTableUpdater(
+ $store
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->clear();
+
+ $instance->addToUpdateList( 42, [ DIWikiPage::newFromText( 'Bar' ) ] );
+ $instance->doUpdate();
+ }
+
+ public function testAddToUpdateListOnNull_List() {
+
+ $instance = new DependencyLinksTableUpdater(
+ $this->store
+ );
+
+ $this->assertNull(
+ $instance->addToUpdateList( 42, null )
+ );
+ }
+
+ public function testAddToUpdateListOnZero_Id() {
+
+ $instance = new DependencyLinksTableUpdater(
+ $this->store
+ );
+
+ $this->assertNull(
+ $instance->addToUpdateList( 0, [] )
+ );
+ }
+
+ public function testAddToUpdateListOnEmpty_List() {
+
+ $instance = new DependencyLinksTableUpdater(
+ $this->store
+ );
+
+ $this->assertNull(
+ $instance->addToUpdateList( 42, [] )
+ );
+ }
+
+ public function testAddDependenciesFromQueryResultWhereObjectIdIsYetUnknownWhichRequiresToCreateTheIdOnTheFly() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getId', 'makeSMWPageID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->returnValue( 0 ) );
+
+ $idTable->expects( $this->any() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 1001 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->equalTo( [ 's_id' => 42 ] ) );
+
+ $insert[] = [
+ 's_id' => 42,
+ 'o_id' => 1001
+ ];
+
+ $connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->equalTo( $insert ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $instance = new DependencyLinksTableUpdater(
+ $store
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->clear();
+
+ $instance->addToUpdateList( 42, [ DIWikiPage::newFromText( 'Bar', SMW_NS_PROPERTY ) ] );
+ $instance->doUpdate();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksUpdateJournalTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksUpdateJournalTest.php
new file mode 100644
index 00000000..48db006a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/DependencyLinksUpdateJournalTest.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal;
+use Title;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class DependencyLinksUpdateJournalTest extends \PHPUnit_Framework_TestCase {
+
+ private $cache;
+ private $callableUpdate;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->callableUpdate = $this->getMockBuilder( '\SMW\MediaWiki\Deferred\CallableUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $queryDependencyLinksStore = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ DependencyLinksUpdateJournal::class,
+ new DependencyLinksUpdateJournal( $this->cache, $this->callableUpdate )
+ );
+ }
+
+ public function testMakeKey() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+ $title = \Title::newFromText( 'Foo' );
+
+ $this->assertContains(
+ 'smw:update:qdep',
+ DependencyLinksUpdateJournal::makeKey( $subject )
+ );
+
+ $this->assertSame(
+ DependencyLinksUpdateJournal::makeKey( $title ),
+ DependencyLinksUpdateJournal::makeKey( $subject )
+ );
+ }
+
+ public function testUpdateFromList() {
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'save' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $hashList = [
+ 'Foo#0##'
+ ];
+
+ $instance->updateFromList( $hashList );
+ }
+
+ public function testUpdate() {
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'save' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $instance->update( DIWikiPage::newFromText( 'Foo' ) );
+ }
+
+ public function testUpdateFromTitle() {
+
+ $this->cache->expects( $this->atLeastOnce() )
+ ->method( 'save' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $instance->update( Title::newFromText( 'Foo' ) );
+ }
+
+ public function testHas() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $this->assertTrue(
+ $instance->has( DIWikiPage::newFromText( 'Foo' ) )
+ );
+ }
+
+ public function testHasOnDIWikiPageWithSubobject() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $this->assertTrue(
+ $instance->has( new DIWikiPage( 'Foo', NS_MAIN, '', 'Bar' ) )
+ );
+ }
+
+ public function testHasFromTitle() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'contains' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $this->assertTrue(
+ $instance->has( Title::newFromText( 'Foo' ) )
+ );
+ }
+
+ public function testDelete() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'delete' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) );
+
+ $this->callableUpdate->expects( $this->once() )
+ ->method( 'setCallback' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback );
+ } ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $instance->delete( DIWikiPage::newFromText( 'Foo' ) );
+ }
+
+ public function testDeleteFromTitle() {
+
+ $this->cache->expects( $this->once() )
+ ->method( 'delete' )
+ ->with( $this->stringContains( 'smw:update:qdep:7ab9f795d4ce7c20051947ede72baec3' ) );
+
+ $this->callableUpdate->expects( $this->once() )
+ ->method( 'setCallback' )
+ ->will( $this->returnCallback( function( $callback ) {
+ return call_user_func( $callback );
+ } ) );
+
+ $instance = new DependencyLinksUpdateJournal(
+ $this->cache,
+ $this->callableUpdate
+ );
+
+ $instance->delete( Title::newFromText( 'Foo' ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilterTest.php
new file mode 100644
index 00000000..ef90f454
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/EntityIdListRelevanceDetectionFilterTest.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DIWikiPage;
+use SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class EntityIdListRelevanceDetectionFilterTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $spyLogger;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ EntityIdListRelevanceDetectionFilter::class,
+ new EntityIdListRelevanceDetectionFilter( $store, $changeOp )
+ );
+ }
+
+ public function testgetFilteredIdListOnExemptedPredefinedProperty() {
+
+ $orderedDiffByTable = [
+ 'fpt_mdat' => [
+ 'property' => [
+ 'key' => '_MDAT',
+ 'p_id' => 29
+ ],
+ 'insert' => [
+ [
+ 's_id' => 201,
+ 'o_serialized' => '1/2016/6/1/11/1/48/0',
+ 'o_sortkey' => '2457540.9595833'
+ ]
+ ],
+ 'delete' => [
+ [
+ 's_id' => 202,
+ 'o_serialized' => '1/2016/6/1/11/1/59/0',
+ 'o_sortkey' => '2457540.9582292'
+ ]
+ ]
+ ]
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getChangedEntityIdSummaryList', 'getOrderedDiffByTable' ] )
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [ 29, 201, 202, 1001 ] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( $orderedDiffByTable ) );
+
+ $instance = new EntityIdListRelevanceDetectionFilter(
+ $store,
+ $changeOp
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setPropertyExemptionList(
+ [ '_MDAT' ]
+ );
+
+ $this->assertEquals(
+ [ 1001 ],
+ $instance->getFilteredIdList()
+ );
+ }
+
+ public function testgetFilteredIdListOnAffiliatePredefinedProperty() {
+
+ $orderedDiffByTable = [
+ 'fpt_dat' => [
+ 'property' => [
+ 'key' => '_MDAT',
+ 'p_id' => 29
+ ],
+ 'insert' => [
+ [
+ 's_id' => 201,
+ 'o_serialized' => '1/2016/6/1/11/1/48/0',
+ 'o_sortkey' => '2457540.9595833'
+ ]
+ ],
+ 'delete' => [
+ [
+ 's_id' => 202,
+ 'o_serialized' => '1/2016/6/1/11/1/59/0',
+ 'o_sortkey' => '2457540.9582292'
+ ]
+ ]
+ ]
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getChangedEntityIdSummaryList', 'getOrderedDiffByTable' ] )
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [ 1001 ] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( $orderedDiffByTable ) );
+
+ $instance = new EntityIdListRelevanceDetectionFilter(
+ $store,
+ $changeOp
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setAffiliatePropertyDetectionList(
+ [ '_MDAT' ]
+ );
+
+ $this->assertEquals(
+ [ 1001, 201, 202 ],
+ $instance->getFilteredIdList()
+ );
+ }
+
+ public function testgetFilteredIdListOnExemptedUserdefinedProperty() {
+
+ $orderedDiffByTable = [
+ 'fpt_foo' => [
+ 'insert' => [
+ [
+ 'p_id' => 100,
+ 's_id' => 201,
+ 'o_serialized' => '1/2016/6/1/11/1/48/0',
+ 'o_sortkey' => '2457540.9595833'
+ ]
+ ],
+ 'delete' => [
+ [
+ 'p_id' => 100,
+ 's_id' => 201,
+ 'o_serialized' => '1/2016/6/1/11/1/59/0',
+ 'o_sortkey' => '2457540.9582292'
+ ]
+ ]
+ ]
+ ];
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemById' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getDataItemById' )
+ ->with( $this->equalTo( 100 ) )
+ ->will( $this->returnValue( DIWikiPage::newFromText( 'Has date', SMW_NS_PROPERTY ) ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getChangedEntityIdSummaryList', 'getOrderedDiffByTable' ] )
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [ 100, 201, 1001 ] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( $orderedDiffByTable ) );
+
+ $instance = new EntityIdListRelevanceDetectionFilter(
+ $store,
+ $changeOp
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setPropertyExemptionList(
+ [ 'Has date' ]
+ );
+
+ $this->assertEquals(
+ [ 1001 ],
+ $instance->getFilteredIdList()
+ );
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryDependencyLinksStoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryDependencyLinksStoreTest.php
new file mode 100644
index 00000000..6b168261
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryDependencyLinksStoreTest.php
@@ -0,0 +1,1034 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DIWikiPage;
+use SMW\RequestOptions;
+use SMW\SQLStore\QueryDependency\QueryDependencyLinksStore;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\QueryDependencyLinksStore
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class QueryDependencyLinksStoreTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $spyLogger;
+ private $jobFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->spyLogger = $this->testEnvironment->newSpyLogger();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->store->setLogger(
+ $this->spyLogger
+ );
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $namespaceExaminer->expects( $this->any() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobFactory', $this->jobFactory );
+ $this->testEnvironment->registerObject( 'NamespaceExaminer', $namespaceExaminer );
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ $this->testEnvironment->clearPendingDeferredUpdates();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore',
+ new QueryDependencyLinksStore( $queryResultDependencyListResolver, $dependencyLinksTableUpdater )
+ );
+ }
+
+ public function testPruneOutdatedTargetLinks() {
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( 'Foo' ) ) );
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $this->assertTrue(
+ $instance->pruneOutdatedTargetLinks( $changeOp )
+ );
+ }
+
+ public function testPruneOutdatedTargetLinksBeingDisabled() {
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setEnabled( false );
+
+ $this->assertNull(
+ $instance->pruneOutdatedTargetLinks( $changeOp )
+ );
+ }
+
+ public function testParserCachePurgeJobParametersOnBlacklistedProperty() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $entityIdListRelevanceDetectionFilter = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityIdListRelevanceDetectionFilter->expects( $this->once() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $entityIdListRelevanceDetectionFilter->expects( $this->once() )
+ ->method( 'getFilteredIdList' )
+ ->will( $this->returnValue( [ 1 ] ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $expected = [
+ 'idlist' => [ 1 ],
+ 'exec.mode' => 'exec.journal'
+ ];
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->once() )
+ ->method( 'newParserCachePurgeJob' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( $expected ) )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->pushParserCachePurgeJob( $entityIdListRelevanceDetectionFilter );
+ }
+
+ public function testParserCachePurgeJobParametersBeingDisabled() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $entityIdListRelevanceDetectionFilter = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->never() )
+ ->method( 'newParserCachePurgeJob' );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setEnabled( false );
+
+ $instance->pushParserCachePurgeJob( $entityIdListRelevanceDetectionFilter );
+ }
+
+ public function testParserCachePurgeJobParametersOnEmptyList() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $entityIdListRelevanceDetectionFilter = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityIdListRelevanceDetectionFilter->expects( $this->once() )
+ ->method( 'getFilteredIdList' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->never() )
+ ->method( 'newParserCachePurgeJob' );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setEnabled( true );
+
+ $instance->pushParserCachePurgeJob( $entityIdListRelevanceDetectionFilter );
+ }
+
+ public function testFindEmbeddedQueryTargetLinksHashListFrom() {
+
+ $row = new \stdClass;
+ $row->s_id = 1001;
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemPoolHashListFor' ] )
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'getDataItemPoolHashListFor' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->anything(),
+ $this->equalTo( [ 'o_id' => [ 42 ] ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( 1 );
+ $requestOptions->setOffset( 200 );
+
+ $instance->findDependencyTargetLinks( [ 42 ], $requestOptions );
+ }
+
+ public function testFindEmbeddedQueryTargetLinksHashListBySubject() {
+
+ $row = new \stdClass;
+ $row->s_id = 1001;
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getDataItemPoolHashListFor' ] )
+ ->getMock();
+
+ $idTable->expects( $this->once() )
+ ->method( 'getDataItemPoolHashListFor' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->anything(),
+ $this->equalTo( [ 'o_id' => [ 42 ] ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $dependencyLinksTableUpdater->expects( $this->once() )
+ ->method( 'getId' )
+ ->will( $this->returnValue( 42 ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->setLimit( 1 );
+ $requestOptions->setOffset( 200 );
+
+ $instance->findDependencyTargetLinksForSubject( DIWikiPage::newFromText( 'Foo' ), $requestOptions );
+ }
+
+ public function testCountDependencies() {
+
+ $row = new \stdClass;
+ $row->count = 1001;
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->equalTo( \SMWSQLStore3::QUERY_LINKS_TABLE ),
+ $this->anything(),
+ $this->equalTo( [ 'o_id' => [ 42 ] ] ) )
+ ->will( $this->returnValue( $row ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->countDependencies( 42 );
+ }
+
+ public function testTryDoUpdateDependenciesByWhileBeingDisabled() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setEnabled( false );
+ $queryResult = '';
+
+ $instance->updateDependencies( $queryResult );
+ }
+
+ public function testUpdateDependencies_ExcludedRequestAction() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setEnabled( true );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->once() )
+ ->method( 'getOption' )
+ ->with( $this->equalTo( 'request.action' ) )
+ ->will( $this->returnValue( 'parse' ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->assertNull(
+ $instance->updateDependencies( $queryResult )
+ );
+ }
+
+ public function testTryDoUpdateDependenciesByForWhenDependencyListReturnsEmpty() {
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getId' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->onConsecutiveCalls( 42, 1001 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds' ] )
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver->expects( $this->once() )
+ ->method( 'getDependencyListFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $instance->setEnabled( true );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance->updateDependencies( $queryResult );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testDisabledDependenciesUpdateOnNotSupportedNamespace() {
+
+ $namespaceExaminer = $this->getMockBuilder( '\SMW\NamespaceExaminer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $namespaceExaminer->expects( $this->once() )
+ ->method( 'isSemanticEnabled' )
+ ->will( $this->returnValue( false ) );
+
+ $this->testEnvironment->registerObject( 'NamespaceExaminer', $namespaceExaminer );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListByLateRetrievalFrom' );
+
+ $queryResultDependencyListResolver->expects( $this->never() )
+ ->method( 'getDependencyListFrom' );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setEnabled( true );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance->updateDependencies( $queryResult );
+ }
+
+ public function testdoUpdateDependenciesByFromQueryResult() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getTouched' )
+ ->will( $this->returnValue( 10 ) );
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $subject->expects( $this->any() )
+ ->method( 'getHash' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyValues' ] )
+ ->getMock();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListFrom' )
+ ->will( $this->returnValue( [ null, DIWikiPage::newFromText( __METHOD__ ) ] ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->onConsecutiveCalls( 42, 1001 ) );
+
+ $dependencyLinksTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'addToUpdateList' )
+ ->with(
+ $this->equalTo( 1001 ),
+ $this->anything() );
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( $subject ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance->updateDependencies( $queryResult );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ public function testdoUpdateDependenciesByFromQueryResultWhereObjectIdIsYetUnknownWhichRequiresToCreateTheIdOnTheFly() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListFrom' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromText( 'Foo', NS_CATEGORY ) ] ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->onConsecutiveCalls( 0, 0 ) );
+
+ $dependencyLinksTableUpdater->expects( $this->once() )
+ ->method( 'createId' )
+ ->will( $this->returnValue( 1001 ) );
+
+ $dependencyLinksTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'addToUpdateList' )
+ ->with(
+ $this->equalTo( 1001 ),
+ $this->anything() );
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( DIWikiPage::newFromText( __METHOD__ ) ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance->updateDependencies( $queryResult );
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ /**
+ * @dataProvider titleProvider
+ */
+ public function testTryDoUpdateDependenciesByWithinSkewedTime( $title ) {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subject->expects( $this->atLeastOnce() )
+ ->method( 'getHash' )
+ ->will( $this->returnValue( 'Foo###' ) );
+
+ $subject->expects( $this->once() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $title ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->setConnectionManager( $connectionManager );
+
+ $queryResultDependencyListResolver = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResultDependencyListResolver->expects( $this->any() )
+ ->method( 'getDependencyListByLateRetrievalFrom' )
+ ->will( $this->returnValue( [] ) );
+
+ $dependencyLinksTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\DependencyLinksTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getId' )
+ ->will( $this->returnValue( 4 ) );
+
+ $dependencyLinksTableUpdater->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $instance = new QueryDependencyLinksStore(
+ $queryResultDependencyListResolver,
+ $dependencyLinksTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->spyLogger
+ );
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->any() )
+ ->method( 'getContextPage' )
+ ->will( $this->returnValue( $subject ) );
+
+ $query->expects( $this->any() )
+ ->method( 'getLimit' )
+ ->will( $this->returnValue( 1 ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $instance->updateDependencies( $queryResult );
+ }
+
+ public function titleProvider() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->once() )
+ ->method( 'getTouched' )
+ ->will( $this->returnValue( wfTimestamp( TS_MW ) + 60 ) );
+
+ $provider[] = [
+ $title
+ ];
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ // This should be a `once` but it failed on PHP: hhvm-3.18 DB=sqlite; MW=master; PHPUNIT=5.7.*
+ // with "Method was expected to be called 1 times, actually called 0 times."
+ $title->expects( $this->any() )
+ ->method( 'getTouched' )
+ ->will( $this->returnValue( '2017-06-15 08:36:55+00' ) );
+
+ $provider[] = [
+ $title
+ ];
+
+ $provider[] = [
+ null
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryReferenceBacklinksTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryReferenceBacklinksTest.php
new file mode 100644
index 00000000..14cb6838
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryReferenceBacklinksTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DataItemFactory;
+use SMW\RequestOptions;
+use SMW\SQLStore\QueryDependency\QueryReferenceBacklinks;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\QueryReferenceBacklinks
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QueryReferenceBacklinksTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ protected function tearDown() {
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $queryDependencyLinksStore = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryReferenceBacklinks',
+ new QueryReferenceBacklinks( $queryDependencyLinksStore )
+ );
+ }
+
+ public function testAddQueryReferenceBacklinksTo() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN, '', 'foobar' );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $subject ) );
+
+ $semanticData->expects( $this->any() )
+ ->method( 'addPropertyObjectValue' )
+ ->with(
+ $this->equalTo( $this->dataItemFactory->newDIProperty( '_ASK' ) ),
+ $this->equalTo( $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ) ) );
+
+ $queryDependencyLinksStore = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryDependencyLinksStore->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $queryDependencyLinksStore->expects( $this->any() )
+ ->method( 'findEmbeddedQueryIdListBySubject' )
+ ->with( $this->equalTo( $subject ) )
+ ->will( $this->returnValue( [ 'Foo#0##' => 42 ] ) );
+
+ $queryDependencyLinksStore->expects( $this->once() )
+ ->method( 'findDependencyTargetLinksForSubject' )
+ ->will( $this->returnValue( [ 'Foo#0##' ] ) );
+
+ $instance = new QueryReferenceBacklinks(
+ $queryDependencyLinksStore
+ );
+
+ $requestOptions = new RequestOptions();
+
+ $this->assertTrue(
+ $instance->addReferenceLinksTo( $semanticData, $requestOptions )
+ );
+ }
+
+ public function testFindQueryReferenceBacklinks() {
+
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN, '', '' );
+
+ $queryDependencyLinksStore = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryDependencyLinksStore->expects( $this->any() )
+ ->method( 'findDependencyTargetLinksForSubject' )
+ ->will( $this->returnValue( [ 'Foo#0##' ] ) );
+
+ $instance = new QueryReferenceBacklinks(
+ $queryDependencyLinksStore
+ );
+
+ $requestOptions = new RequestOptions();
+
+ $this->assertEquals(
+ [ 'Foo#0##' ],
+ $instance->findReferenceLinks( $subject, $requestOptions )
+ );
+ }
+
+ public function testInspectFurtherLinkRequirement() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_ASK' );
+ $subject = $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN, '', '' );
+
+ $queryDependencyLinksStore = $this->getMockBuilder( '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryReferenceBacklinks(
+ $queryDependencyLinksStore
+ );
+
+ $html = '';
+
+ $this->assertFalse(
+ $instance->doesRequireFurtherLink( $property, $subject, $html )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryResultDependencyListResolverTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryResultDependencyListResolverTest.php
new file mode 100644
index 00000000..b0638a0b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependency/QueryResultDependencyListResolverTest.php
@@ -0,0 +1,602 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryDependency;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Query\PrintRequest;
+use SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver;
+use SMW\Tests\TestEnvironment;
+use SMWDIBlob as DIBlob;
+use SMWQuery as Query;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class QueryResultDependencyListResolverTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $store;
+ private $hierarchyLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'Store', $this->store );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver',
+ new QueryResultDependencyListResolver( $this->hierarchyLookup )
+ );
+ }
+
+ public function testTryTogetDependencyListFromForNonSetQueryResult() {
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $this->assertEmpty(
+ $instance->getDependencyListFrom( '' )
+ );
+ }
+
+ public function testTryTogetDependencyListFromForLimitZeroQuery() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $query->setUnboundLimit( 0 );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->never() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->hierarchyLookup->expects( $this->any() )
+ ->method( 'getConsecutiveHierarchyList' )
+ ->will( $this->returnValue( [] ) );
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $this->assertEmpty(
+ $instance->getDependencyListFrom( $queryResult )
+ );
+ }
+
+ public function testExcludePropertyFromDependencyDetection() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->hierarchyLookup->expects( $this->any() )
+ ->method( 'hasSubproperty' )
+ ->will( $this->returnValue( true ) );
+
+ $this->hierarchyLookup->expects( $this->at( 1 ) )
+ ->method( 'getConsecutiveHierarchyList' )
+ ->with( $this->equalTo( new DIProperty( 'Foobar' ) ) )
+ ->will( $this->returnValue(
+ [ new DIProperty( 'Subprop' ) ] ) );
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $instance->setPropertyDependencyExemptionlist( [ 'Subprop' ] );
+
+ $expected = [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ // DIWikiPage::newFromText( 'Subprop', SMW_NS_PROPERTY ) removed
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getDependencyListFrom( $queryResult )
+ );
+ }
+
+
+ /**
+ * @dataProvider queryProvider
+ */
+ public function testgetDependencyListFrom( $query, $expected ) {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getDependencyListFrom( $queryResult )
+ );
+ }
+
+ public function testgetDependencyListByLateRetrievalFrom() {
+
+ $subject = DIWikiPage::newFromText( 'Bar' );
+
+ $description = new ClassDescription(
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( DIWikiPage::newFromText( 'Foo' ) );
+
+ $resolverJournal = $this->getMockBuilder( '\SMW\Query\Result\ResolverJournal' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resolverJournal->expects( $this->once() )
+ ->method( 'getEntityList' )
+ ->will( $this->returnValue( [ $subject ] ) );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResolverJournal' )
+ ->will( $this->returnValue( $resolverJournal ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $this->assertEquals(
+ [ $subject ],
+ $instance->getDependencyListByLateRetrievalFrom( $queryResult )
+ );
+ }
+
+ public function testResolvePropertyHierarchy() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->hierarchyLookup->expects( $this->any() )
+ ->method( 'hasSubproperty' )
+ ->will( $this->returnValue( true ) );
+
+ $this->hierarchyLookup->expects( $this->at( 1 ) )
+ ->method( 'getConsecutiveHierarchyList' )
+ ->with( $this->equalTo( new DIProperty( 'Foobar' ) ) )
+ ->will( $this->returnValue(
+ [ new DIProperty( 'Subprop' ) ] ) );
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $expected = [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Subprop#102##' => DIWikiPage::newFromText( 'Subprop', SMW_NS_PROPERTY ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getDependencyListFrom( $queryResult )
+ );
+ }
+
+ public function testResolveCategoryHierarchy() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ $description = new ClassDescription(
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->once() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getQuery' )
+ ->will( $this->returnValue( $query ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $this->store ) );
+
+ $this->hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->hierarchyLookup->expects( $this->any() )
+ ->method( 'hasSubcategory' )
+ ->will( $this->returnValue( true ) );
+
+ $this->hierarchyLookup->expects( $this->at( 1 ) )
+ ->method( 'getConsecutiveHierarchyList' )
+ ->with( $this->equalTo( DIWikiPage::newFromText( 'Foocat', NS_CATEGORY ) ) )
+ ->will( $this->returnValue(
+ [
+ DIWikiPage::newFromText( 'Subcat', NS_CATEGORY ),
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY ) ] ) );
+
+ $instance = new QueryResultDependencyListResolver(
+ $this->hierarchyLookup
+ );
+
+ $expected = [
+ DIWikiPage::newFromText( 'Foo' ),
+ 'Subcat#14##' => DIWikiPage::newFromText( 'Subcat', NS_CATEGORY ),
+ 'Foocat#14##' => DIWikiPage::newFromText( 'Foocat', NS_CATEGORY ),
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY )
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getDependencyListFrom( $queryResult )
+ );
+ }
+
+ public function queryProvider() {
+
+ $subject = DIWikiPage::newFromText( 'Foo' );
+
+ #0
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #1
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( new DIBlob( 'Bar' ) )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #2 uses inverse property declaration
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar', true ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #3 Conjunction
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( new Conjunction( [
+ $description,
+ new NamespaceDescription( NS_MAIN )
+ ] ) );
+
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #4 Disjunction
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $query = new Query( new Disjunction( [
+ $description,
+ new NamespaceDescription( NS_MAIN )
+ ] ) );
+
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #5
+ $description = new ClassDescription(
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Foocat', NS_CATEGORY )
+ ]
+ ];
+
+ #6
+ $description = new ConceptDescription(
+ DIWikiPage::newFromText( 'FooConcept', SMW_NS_CONCEPT )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ 'FooConcept#108##' => DIWikiPage::newFromText( 'FooConcept', SMW_NS_CONCEPT )
+ ]
+ ];
+
+ #7 Printrequest
+ $pv = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Foobaz' );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar', true ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, '', $pv )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ DIWikiPage::newFromText( 'Foobaz', SMW_NS_PROPERTY ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY ),
+ ]
+ ];
+
+ #8 Inverse printrequest
+ $pv = DataValueFactory::getInstance()->newPropertyValueByLabel( 'Foobaz' );
+ $pv->setInverse( true );
+
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar', true ),
+ new ValueDescription( DIWikiPage::newFromText( 'Bar' ) )
+ );
+
+ $description->addPrintRequest(
+ new PrintRequest( PrintRequest::PRINT_PROP, '', $pv )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'Bar' ),
+ DIWikiPage::newFromText( 'Foobaz', SMW_NS_PROPERTY ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY ),
+ ]
+ ];
+
+ #9 SMW_CMP_EQ comparator
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'EQ_Comparator' ), null, SMW_CMP_EQ )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ DIWikiPage::newFromText( 'EQ_Comparator' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ #10 Ignore entity with SMW_CMP_EQ comparator
+ $description = new SomeProperty(
+ new DIProperty( 'Foobar' ),
+ new ValueDescription( DIWikiPage::newFromText( 'LIKE_Comparator' ), null, SMW_CMP_LIKE )
+ );
+
+ $query = new Query( $description );
+ $query->setContextPage( $subject );
+
+ $provider[] = [
+ $query,
+ [
+ DIWikiPage::newFromText( 'Foo' ),
+ 'Foobar#102##' => DIWikiPage::newFromText( 'Foobar', SMW_NS_PROPERTY )
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependencyLinksStoreFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependencyLinksStoreFactoryTest.php
new file mode 100644
index 00000000..7f38b995
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryDependencyLinksStoreFactoryTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\QueryDependencyLinksStoreFactory;
+
+/**
+ * @covers \SMW\SQLStore\QueryDependencyLinksStoreFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryDependencyLinksStoreFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependencyLinksStoreFactory',
+ new QueryDependencyLinksStoreFactory()
+ );
+ }
+
+ public function testCanConstructQueryResultDependencyListResolver() {
+
+ $instance = new QueryDependencyLinksStoreFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryResultDependencyListResolver',
+ $instance->newQueryResultDependencyListResolver()
+ );
+ }
+
+ public function testCanConstructQueryDependencyLinksStore() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new QueryDependencyLinksStoreFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryDependencyLinksStore',
+ $instance->newQueryDependencyLinksStore( $store )
+ );
+ }
+
+ public function testCanConstructEntityIdListRelevanceDetectionFilter() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryDependencyLinksStoreFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\EntityIdListRelevanceDetectionFilter',
+ $instance->newEntityIdListRelevanceDetectionFilter( $store, $changeOp )
+ );
+ }
+
+ public function testCanConstructQueryReferenceBacklinks() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new QueryDependencyLinksStoreFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\QueryReferenceBacklinks',
+ $instance->newQueryReferenceBacklinks( $store )
+ );
+ }
+
+ public function testCanConstructDependencyLinksUpdateJournal() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new QueryDependencyLinksStoreFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryDependency\DependencyLinksUpdateJournal',
+ $instance->newDependencyLinksUpdateJournal()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/ConceptQuerySegmentBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/ConceptQuerySegmentBuilderTest.php
new file mode 100644
index 00000000..112a3a0c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/ConceptQuerySegmentBuilderTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder;
+use Title;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ConceptQuerySegmentBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentListBuilder;
+ private $querySegmentListProcessor;
+ private $queryParser;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->querySegmentListProcessor = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListProcessor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->queryParser = $this->getMockBuilder( '\SMW\Query\Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder',
+ new ConceptQuerySegmentBuilder( $this->querySegmentListBuilder, $this->querySegmentListProcessor )
+ );
+ }
+
+ public function testGetQuerySegmentFromOnNull() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->queryParser->expects( $this->any() )
+ ->method( 'getQueryDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $instance = new ConceptQuerySegmentBuilder(
+ $this->querySegmentListBuilder,
+ $this->querySegmentListProcessor
+ );
+
+ $instance->setQueryParser(
+ $this->queryParser
+ );
+
+ $this->assertNull(
+ $instance->getQuerySegmentFrom( '[[Foo]]' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php
new file mode 100644
index 00000000..d0852390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreterFactoryTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DescriptionInterpreterFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory',
+ new DescriptionInterpreterFactory()
+ );
+ }
+
+ public function testCanConstructDispatchingDescriptionInterpreter() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new DescriptionInterpreterFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\DispatchingDescriptionInterpreter',
+ $instance->newDispatchingDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
new file mode 100644
index 00000000..5489db9f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ClassDescriptionInterpreterTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DataItemFactory;
+use SMW\Query\DescriptionFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ClassDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\ClassDescriptionInterpreter',
+ new ClassDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testCompileDescription( $description, $pageId, $expected ) {
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( $pageId ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new ClassDescriptionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $descriptionFactory = new DescriptionFactory();
+ $dataItemFactory = new DataItemFactory();
+
+ #0
+ $pageId = 42;
+
+ $description = $descriptionFactory->newClassDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->components = [ 1 => "t0.o_id" ];
+ $expected->joinfield = "t0.s_id";
+
+ $provider[] = [
+ $description,
+ $pageId,
+ $expected
+ ];
+
+ #1 Empty
+ $pageId = 0;
+
+ $description = $descriptionFactory->newClassDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_CATEGORY )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 2;
+ $expected->components = [];
+ $expected->joinfield = "";
+
+ $provider[] = [
+ $description,
+ $pageId,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapperTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapperTest.php
new file mode 100644
index 00000000..e5f2c60b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ComparatorMapperTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ComparatorMapper;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\ComparatorMapper
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ComparatorMapperTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\ComparatorMapper',
+ new ComparatorMapper()
+ );
+ }
+
+ public function testInvalidComparatorThrowsException() {
+
+ $value = '';
+
+ $valueDescription = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ComparatorMapper();
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->mapComparator( $valueDescription, $value );
+ }
+
+ /**
+ * @dataProvider comparatorProvider
+ */
+ public function testSQLComparatorElement( $comparator, $value, $expected ) {
+
+ $valueDescription = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $valueDescription->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( $comparator ) );
+
+ $instance = new ComparatorMapper();
+
+ $this->assertEquals(
+ $expected['comparator'],
+ $instance->mapComparator( $valueDescription, $value )
+ );
+
+ $this->assertEquals(
+ $expected['value'],
+ $value
+ );
+ }
+
+ public function comparatorProvider() {
+
+ $provider[] = [ SMW_CMP_EQ, 'Foo%_*?', [ 'comparator' => '=', 'value' => 'Foo%_*?' ] ];
+ $provider[] = [ SMW_CMP_LESS, 'Foo%_*?', [ 'comparator' => '<', 'value' => 'Foo%_*?' ] ];
+ $provider[] = [ SMW_CMP_GRTR, 'Foo%_*?', [ 'comparator' => '>', 'value' => 'Foo%_*?' ] ];
+ $provider[] = [ SMW_CMP_LEQ, 'Foo%_*?', [ 'comparator' => '<=', 'value' => 'Foo%_*?' ] ];
+ $provider[] = [ SMW_CMP_GEQ, 'Foo%_*?', [ 'comparator' => '>=', 'value' => 'Foo%_*?' ] ];
+ $provider[] = [ SMW_CMP_NEQ, 'Foo%_*?', [ 'comparator' => '!=', 'value' => 'Foo%_*?' ] ];
+
+ $provider[] = [ SMW_CMP_LIKE, 'Foo%_*?\\', [ 'comparator' => ' LIKE ', 'value' => 'Foo\%\_%_\\\\' ] ];
+ $provider[] = [ SMW_CMP_PRIM_LIKE, 'Foo%_*?\\', [ 'comparator' => ' LIKE ', 'value' => 'Foo\%\_%_\\\\' ] ];
+ $provider[] = [ SMW_CMP_NLKE, 'Foo%_*?\\', [ 'comparator' => ' NOT LIKE ', 'value' => 'Foo\%\_%_\\\\' ] ];
+ $provider[] = [ SMW_CMP_PRIM_NLKE, 'Foo%_*?\\', [ 'comparator' => ' NOT LIKE ', 'value' => 'Foo\%\_%_\\\\' ] ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
new file mode 100644
index 00000000..d3058658
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ConceptDescriptionInterpreterTest.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\ApplicationFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ConceptDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+ private $descriptionInterpreterFactory;
+ private $queryParser;
+
+ private $descriptionFactory;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $queryFactory = $applicationFactory->getQueryFactory();
+
+ $this->descriptionFactory = $queryFactory->newDescriptionFactory();
+ $this->queryParser = $queryFactory->newQueryParser();
+ $this->dataItemFactory = $applicationFactory->getDataItemFactory();
+
+ $this->descriptionInterpreterFactory = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\ConceptDescriptionInterpreter',
+ new ConceptDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ public function testCheckForCircularReference() {
+
+ $circularReferenceGuard = $this->getMockBuilder( '\SMW\Utils\CircularReferenceGuard' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $circularReferenceGuard->expects( $this->once() )
+ ->method( 'isCircular' )
+ ->with( $this->equalTo( 'concept-42' ) )
+ ->will( $this->returnValue( true ) );
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $querySegmentListBuilder->expects( $this->any() )
+ ->method( 'getStore' )
+ ->will( $this->returnValue( $store ) );
+
+ $querySegmentListBuilder->expects( $this->any() )
+ ->method( 'getCircularReferenceGuard' )
+ ->will( $this->returnValue( $circularReferenceGuard ) );
+
+ $instance = new ConceptDescriptionInterpreter(
+ $querySegmentListBuilder
+ );
+
+ $description = $this->descriptionFactory->newConceptDescription(
+ $this->dataItemFactory->newDIWikiPage( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $instance->interpretDescription(
+ $description
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testInterpretDescription( $description, $concept, $expected ) {
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $concept ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new ConceptDescriptionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $instance->setQueryParser(
+ $this->queryParser
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $descriptionFactory = $applicationFactory->getQueryFactory()->newDescriptionFactory();
+ $dataItemFactory = $applicationFactory->getDataItemFactory();
+
+ #0 No concept
+ $concept = false;
+
+ $description = $descriptionFactory->newConceptDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinfield = '';
+
+ $provider[] = [
+ $description,
+ $concept,
+ $expected
+ ];
+
+ #1 Cached concept
+ $concept = new \stdClass;
+ $concept->concept_size = 1;
+ $concept->concept_features = 1;
+ $concept->concept_depth = 1;
+ $concept->cache_date = strtotime( "now" );
+
+ $description = $descriptionFactory->newConceptDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinfield = 't0.s_id';
+ $expected->where = 't0.o_id=42';
+ $expected->queryNumber = 0;
+
+ $provider[] = [
+ $description,
+ $concept,
+ $expected
+ ];
+
+ #2 Non cached concept
+ $concept = new \stdClass;
+ $concept->concept_txt = "[[Category:Foo]]";
+ $concept->concept_size = 1;
+ $concept->concept_features = 1;
+ $concept->concept_depth = 1;
+ $concept->cache_date = false;
+
+ $description = $descriptionFactory->newConceptDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', SMW_NS_CONCEPT )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinfield = 't1.s_id';
+ $expected->components = [ 2 => 't1.o_id' ];
+ $expected->queryNumber = 1;
+
+ $provider[] = [
+ $description,
+ $concept,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreterTest.php
new file mode 100644
index 00000000..086f618d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/DisjunctionConjunctionInterpreterTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\DescriptionFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\DisjunctionConjunctionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\DisjunctionConjunctionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class DisjunctionConjunctionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+ private $descriptionFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\DisjunctionConjunctionInterpreter',
+ new DisjunctionConjunctionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testInterpretDescription( $description, $expected ) {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new DisjunctionConjunctionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $descriptionFactory = new DescriptionFactory();
+
+ #0 Disjunction
+ $description = $descriptionFactory->newDisjunction();
+
+ $description->addDescription(
+ $descriptionFactory->newNamespaceDescription( NS_HELP )
+ );
+
+ $description->addDescription(
+ $descriptionFactory->newNamespaceDescription( NS_MAIN )
+ );
+
+ $expectedDisjunction = new \stdClass;
+ $expectedDisjunction->type = 3;
+ $expectedDisjunction->components = [ 1 => true, 2 => true ];
+
+ $provider[] = [
+ $description,
+ $expectedDisjunction
+ ];
+
+ #1 Conjunction
+ $description = $descriptionFactory->newConjunction();
+
+ $description->addDescription(
+ $descriptionFactory->newNamespaceDescription( NS_HELP )
+ );
+
+ $description->addDescription(
+ $descriptionFactory->newNamespaceDescription( NS_MAIN )
+ );
+
+ $expectedConjunction = new \stdClass;
+ $expectedConjunction->type = 4;
+ $expectedConjunction->components = [ 1 => true, 2 => true ];
+
+ $provider[] = [
+ $description,
+ $expectedConjunction
+ ];
+
+ #2 No query
+ $description = $descriptionFactory->newConjunction();
+
+ $description->addDescription(
+ $descriptionFactory->newThingDescription()
+ );
+
+ $expectedConjunction = new \stdClass;
+ $expectedConjunction->type = 0;
+ $expectedConjunction->components = [];
+
+ $provider[] = [
+ $description,
+ $expectedConjunction
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
new file mode 100644
index 00000000..2bcceb92
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/NamespaceDescriptionInterpreterTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\Query\DescriptionFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class NamespaceDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+ private $descriptionFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionFactory = new DescriptionFactory();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\NamespaceDescriptionInterpreter',
+ new NamespaceDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ public function testInterpretDescription() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $description = $this->descriptionFactory->newNamespaceDescription(
+ NS_HELP
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->where = "t0.smw_namespace=";
+
+ $instance = new NamespaceDescriptionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php
new file mode 100644
index 00000000..592265c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/SomePropertyInterpreterTest.php
@@ -0,0 +1,511 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DataItemFactory;
+use SMW\Query\DescriptionFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class SomePropertyInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+ private $descriptionFactory;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionFactory = new DescriptionFactory();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\SomePropertyInterpreter',
+ new SomePropertyInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ public function testinterpretDescriptionForUnknownTablePropertyId() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( '' ) );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' ),
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 0;
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new SomePropertyInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function testinterpretDescriptionForNonIdSubject() {
+
+ $proptable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'usesIdSubject' ] )
+ ->getMock();
+
+ $proptable->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( false ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $proptable ] ) );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' ),
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 0;
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new SomePropertyInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function testinterpretDescriptionForNonWikiPageTypeInverseProperty() {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property->expects( $this->once() )
+ ->method( 'isInverse' )
+ ->will( $this->returnValue( true ) );
+
+ $property->expects( $this->once() )
+ ->method( 'findPropertyTypeID' )
+ ->will( $this->returnValue( '_txt' ) );
+
+ $proptable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'usesIdSubject' ] )
+ ->getMock();
+
+ $proptable->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $proptable ] ) );
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $property,
+ $this->descriptionFactory->newThingDescription()
+ );
+ $expected = new \stdClass;
+ $expected->type = 0;
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new SomePropertyInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testinterpretDescription( $description, $isFixedPropertyTable, $indexField, $sortKeys, $expected ) {
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->any() )
+ ->method( 'getIndexField' )
+ ->will( $this->returnValue( $indexField ) );
+
+ $dataItemHandler->expects( $this->any() )
+ ->method( 'getTableFields' )
+ ->will( $this->returnValue( [ 'one', 'two' ] ) );
+
+ $dataItemHandler->expects( $this->any() )
+ ->method( 'getWhereConds' )
+ ->will( $this->returnValue( [ $indexField => 'fixedFooWhereCond' ] ) );
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPropertyID', 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPropertyID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 91 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $proptable = $this->getMockBuilder( '\SMWSQLStore3Table' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $proptable->expects( $this->any() )
+ ->method( 'usesIdSubject' )
+ ->will( $this->returnValue( true ) );
+
+ $proptable->expects( $this->any() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'FooPropTable' ) );
+
+ $proptable->expects( $this->any() )
+ ->method( 'isFixedPropertyTable' )
+ ->will( $this->returnValue( $isFixedPropertyTable ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'findPropertyTableID' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ 'Foo' => $proptable ] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $querySegmentListBuilder = $queryEngineFactory->newQuerySegmentListBuilder();
+ $querySegmentListBuilder->setSortKeys( $sortKeys );
+
+ $instance = new SomePropertyInterpreter(
+ $querySegmentListBuilder
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $descriptionFactory = new DescriptionFactory();
+ $dataItemFactory = new DataItemFactory();
+
+ #0 Blob + wildcard
+ $isFixedPropertyTable = false;
+ $indexField = '';
+ $sortKeys = [];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newThingDescription()
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id" ];
+ $expected->sortfields = [];
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #1 WikiPage + SMW_CMP_EQ
+ $isFixedPropertyTable = false;
+ $indexField = 'wikipageIndex';
+ $sortKeys = [];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN ), null, SMW_CMP_EQ )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id", 2 => "t0.wikipageIndex" ];
+ $expected->queryNumber = 0;
+ $expected->where = '';
+ $expected->sortfields = [];
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #2 WikiPage + SMW_CMP_EQ + sort
+ $isFixedPropertyTable = false;
+ $indexField = 'wikipageIndex';
+ $sortKeys = [ 'Foo' => 'DESC' ];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_wpg' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN ), null, SMW_CMP_EQ )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id", 2 => "t0.wikipageIndex" ];
+ $expected->queryNumber = 0;
+ $expected->where = '';
+ $expected->sortfields = [ 'Foo' => 'idst0.smw_sort' ];
+ $expected->from = ' INNER JOIN AS idst0 ON idst0.smw_id=t0.wikipageIndex';
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #3 Blob + SMW_CMP_EQ
+ $isFixedPropertyTable = false;
+ $indexField = 'blobIndex';
+ $sortKeys = [];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIBlob( 'Bar' ), null, SMW_CMP_EQ )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id" ];
+ $expected->queryNumber = 0;
+ $expected->where = '(t0.blobIndex=fixedFooWhereCond)';
+ $expected->sortfields = [];
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #4 Blob + SMW_CMP_EQ + sort
+ $isFixedPropertyTable = false;
+ $indexField = 'blobIndex';
+ $sortKeys = [ 'Foo' => 'ASC' ];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIBlob( 'Bar' ), null, SMW_CMP_EQ )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id" ];
+ $expected->queryNumber = 0;
+ $expected->where = '(t0.blobIndex=fixedFooWhereCond)';
+ $expected->sortfields = [ 'Foo' => 't0.blobIndex' ];
+ $expected->from = '';
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #5 Check SemanticMaps compatibility mode (invokes `getSQLCondition`)
+ $isFixedPropertyTable = false;
+ $indexField = 'blobIndex';
+ $sortKeys = [];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $valueDescription = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getSQLCondition', 'getDataItem' ] )
+ ->getMock();
+
+ $valueDescription->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItemFactory->newDIBlob( '13,56' ) ) );
+
+ $valueDescription->expects( $this->once() )
+ ->method( 'getSQLCondition' )
+ ->will( $this->returnValue( 'foo AND bar' ) );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $valueDescription
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id" ];
+ $expected->queryNumber = 0;
+ $expected->where = '(foo AND bar)';
+ $expected->sortfields = [];
+ $expected->from = '';
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ #6, see 556
+ $isFixedPropertyTable = false;
+ $indexField = '';
+ $sortKeys = [];
+ $property = $dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $description = $descriptionFactory->newSomeProperty(
+ $property,
+ $descriptionFactory->newDisjunction( [
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIBlob( 'Bar' ) ),
+ $descriptionFactory->newValueDescription( $dataItemFactory->newDIBlob( 'Baz' ) )
+ ] )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinTable = 'FooPropTable';
+ $expected->components = [ 1 => "t0.p_id" ];
+ $expected->queryNumber = 0;
+ $expected->where = '((t0.=fixedFooWhereCond) OR (t0.=fixedFooWhereCond))';
+ $expected->sortfields = [];
+ $expected->from = '';
+
+ $provider[] = [
+ $description,
+ $isFixedPropertyTable,
+ $indexField,
+ $sortKeys,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php
new file mode 100644
index 00000000..5e59430a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ThingDescriptionInterpreterTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ThingDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\ThingDescriptionInterpreter',
+ new ThingDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ public function testInterpretDescription() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ThingDescription' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $expected = new \stdClass;
+ $expected->type = 0;
+ $expected->queryNumber = 0;
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new ThingDescriptionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
new file mode 100644
index 00000000..c5e717a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/DescriptionInterpreters/ValueDescriptionInterpreterTest.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\DescriptionInterpreters;
+
+use SMW\DataItemFactory;
+use SMW\Query\DescriptionFactory;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter;
+use SMW\SQLStore\QueryEngineFactory;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ValueDescriptionInterpreterTest extends \PHPUnit_Framework_TestCase {
+
+ private $querySegmentValidator;
+ private $descriptionFactory;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->descriptionFactory = new DescriptionFactory();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\DescriptionInterpreters\ValueDescriptionInterpreter',
+ new ValueDescriptionInterpreter( $querySegmentListBuilder )
+ );
+ }
+
+ /**
+ * @dataProvider descriptionProvider
+ */
+ public function testInterpretDescription( $description, $expected ) {
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $queryEngineFactory = new QueryEngineFactory( $store );
+
+ $instance = new ValueDescriptionInterpreter(
+ $queryEngineFactory->newQuerySegmentListBuilder()
+ );
+
+ $this->assertTrue(
+ $instance->canInterpretDescription( $description )
+ );
+
+ $this->querySegmentValidator->assertThatContainerHasProperties(
+ $expected,
+ $instance->interpretDescription( $description )
+ );
+ }
+
+ public function descriptionProvider() {
+
+ $descriptionFactory = new DescriptionFactory();
+ $dataItemFactory = new DataItemFactory();
+
+ #0 SMW_CMP_EQ
+ $description = $descriptionFactory->newValueDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_EQ
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 2;
+ $expected->alias = "t0";
+ $expected->joinfield = [ 42 ];
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ #1 SMW_CMP_LEQ
+ $description = $descriptionFactory->newValueDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_LEQ
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->alias = "t0";
+ $expected->joinfield = "t0.smw_id";
+ $expected->where = "t0.smw_sortkey<=Foo";
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ #2 SMW_CMP_LIKE
+ $description = $descriptionFactory->newValueDescription(
+ $dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN ), null, SMW_CMP_LIKE
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->alias = "t0";
+ $expected->joinfield = "t0.smw_id";
+ $expected->where = "t0.smw_sortkey LIKE Foo";
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ #3 not a DIWikiPage
+ $description = $descriptionFactory->newValueDescription(
+ $dataItemFactory->newDIBLob( 'Foo' )
+ );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->joinfield = "";
+ $expected->where = "";
+
+ $provider[] = [
+ $description,
+ $expected
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/EngineOptionsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/EngineOptionsTest.php
new file mode 100644
index 00000000..184c0390
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/EngineOptionsTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\EngineOptions;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\EngineOptions
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class EngineOptionsTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\EngineOptions',
+ new EngineOptions()
+ );
+ }
+
+ public function testInitialState() {
+
+ $instance = new EngineOptions();
+
+ $this->assertInternalType(
+ 'boolean',
+ $instance->get( 'smwgIgnoreQueryErrors' )
+ );
+
+ $this->assertInternalType(
+ 'integer',
+ $instance->get( 'smwgQSortFeatures' )
+ );
+ }
+
+ public function testAddOption() {
+
+ $instance = new EngineOptions();
+
+ $this->assertFalse(
+ $instance->has( 'Foo' )
+ );
+
+ $instance->set( 'Foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->get( 'Foo' )
+ );
+ }
+
+ public function testUnregisteredKeyThrowsException() {
+
+ $instance = new EngineOptions();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->get( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilderTest.php
new file mode 100644
index 00000000..5b15ce67
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilderTest.php
@@ -0,0 +1,213 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataItemFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\MySQLValueMatchConditionBuilder;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\MySQLValueMatchConditionBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MySQLValueMatchConditionBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $textSanitizer;
+ private $searchTable;
+ private $dataItemFactory;
+
+ protected function setUp() {
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->textSanitizer = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\MySQLValueMatchConditionBuilder',
+ new MySQLValueMatchConditionBuilder( $this->textSanitizer, $this->searchTable )
+ );
+ }
+
+ public function testIsEnabled() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new MySQLValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertTrue(
+ $instance->isEnabled()
+ );
+ }
+
+ public function testGetTableName() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getTableName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new MySQLValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getTableName()
+ );
+ }
+
+ public function testGetSortIndexField() {
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'getSortField' )
+ ->will( $this->returnValue( 's_id' ) );
+
+ $instance = new MySQLValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo.s_id',
+ $instance->getSortIndexField( 'Foo' )
+ );
+ }
+
+ public function testCanApplyFulltextSearchMatchCondition() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isValidByType' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'hasMinTokenLength' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isExemptedProperty' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new MySQLValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $this->assertTrue(
+ $instance->canApplyFulltextSearchMatchCondition( $description )
+ );
+
+ $instance->getWhereCondition( $description );
+ }
+
+ /**
+ * @dataProvider searchTermProvider
+ */
+ public function testGetWhereConditionWithoutProperty( $text, $indexField, $expected ) {
+
+ $this->textSanitizer->expects( $this->once() )
+ ->method( 'sanitize' )
+ ->will( $this->returnValue( $text ) );
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getIndexField' )
+ ->will( $this->returnValue( $indexField ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new MySQLValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $description->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getWhereCondition( $description )
+ );
+ }
+
+ public function searchTermProvider() {
+
+ $provider[] = [
+ 'foooo',
+ 'barColumn',
+ "MATCH(barColumn) AGAINST (foooo IN BOOLEAN MODE) "
+ ];
+
+ $provider[] = [
+ 'foooo&BOL',
+ 'barColumn',
+ "MATCH(barColumn) AGAINST (foooo IN BOOLEAN MODE) "
+ ];
+
+ $provider[] = [
+ 'foooo&INL',
+ 'barColumn',
+ "MATCH(barColumn) AGAINST (foooo IN NATURAL LANGUAGE MODE) "
+ ];
+
+ $provider[] = [
+ 'foooo&QEX',
+ 'barColumn',
+ "MATCH(barColumn) AGAINST (foooo WITH QUERY EXPANSION) "
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilderTest.php
new file mode 100644
index 00000000..7935f70b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SQLiteValueMatchConditionBuilderTest.php
@@ -0,0 +1,236 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataItemFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\SQLiteValueMatchConditionBuilder;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\SQLiteValueMatchConditionBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SQLiteValueMatchConditionBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $textSanitizer;
+ private $searchTable;
+ private $dataItemFactory;
+
+ protected function setUp() {
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->textSanitizer = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SQLiteValueMatchConditionBuilder',
+ new SQLiteValueMatchConditionBuilder( $this->textSanitizer, $this->searchTable )
+ );
+ }
+
+ public function testIsEnabled() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertTrue(
+ $instance->isEnabled()
+ );
+ }
+
+ public function testGetTableName() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getTableName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getTableName()
+ );
+ }
+
+ public function testGetSortIndexField() {
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'getSortField' )
+ ->will( $this->returnValue( 's_id' ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo.s_id',
+ $instance->getSortIndexField( 'Foo' )
+ );
+ }
+
+ public function testCanApplyFulltextSearchMatchCondition() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isValidByType' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'hasMinTokenLength' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isExemptedProperty' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $this->assertTrue(
+ $instance->canApplyFulltextSearchMatchCondition( $description )
+ );
+
+ $instance->getWhereCondition( $description );
+ }
+
+ public function testGetWhereConditionWithPropertyOnTempTable() {
+
+ $this->textSanitizer->expects( $this->once() )
+ ->method( 'sanitize' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getIndexField' )
+ ->will( $this->returnValue( 'indexField' ) );
+
+ $this->searchTable->expects( $this->atLeastOnce() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getProperty' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIProperty( 'Foo' ) ) );
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $description->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $this->assertSame(
+ 'tempTable.indexField MATCH Foo AND tempTable.p_id=',
+ $instance->getWhereCondition( $description, 'tempTable' )
+ );
+ }
+
+ /**
+ * @dataProvider searchTermProvider
+ */
+ public function testGetWhereConditionWithoutProperty( $text, $indexField, $expected ) {
+
+ $this->textSanitizer->expects( $this->once() )
+ ->method( 'sanitize' )
+ ->will( $this->returnValue( $text ) );
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getIndexField' )
+ ->will( $this->returnValue( $indexField ) );
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $instance = new SQLiteValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $description->expects( $this->atLeastOnce() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar' ) ) );
+
+ $description->expects( $this->once() )
+ ->method( 'getComparator' )
+ ->will( $this->returnValue( SMW_CMP_LIKE ) );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getWhereCondition( $description )
+ );
+ }
+
+ public function searchTermProvider() {
+
+ $provider[] = [
+ 'foooo',
+ 'barColumn',
+ "barColumn MATCH foooo"
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableRebuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableRebuilderTest.php
new file mode 100644
index 00000000..cb5da767
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableRebuilderTest.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTableRebuilder;
+use SMW\Tests\Utils\Mock\IteratorMockBuilder;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\SearchTableRebuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTableRebuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $searchTableUpdater;
+ private $searchTable;
+ private $connection;
+ private $iteratorMockBuilder;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTableUpdater->expects( $this->any() )
+ ->method( 'getSearchTable' )
+ ->will( $this->returnValue( $this->searchTable ) );
+
+ $this->iteratorMockBuilder = new IteratorMockBuilder();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableRebuilder',
+ new SearchTableRebuilder( $this->connection, $this->searchTableUpdater )
+ );
+ }
+
+ public function testRebuildWithoutUpdate() {
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getDiType' )
+ ->will( $this->returnValue( DataItem::TYPE_BLOB ) );
+
+ $this->searchTableUpdater->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $instance = new SearchTableRebuilder(
+ $this->connection,
+ $this->searchTableUpdater
+ );
+
+ $instance->rebuild();
+ }
+
+ public function testNeverRebuildOnOptimization() {
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTableUpdater->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->never() )
+ ->method( 'getPropertyTables' );
+
+ $this->searchTableUpdater->expects( $this->once() )
+ ->method( 'optimize' );
+
+ $instance = new SearchTableRebuilder(
+ $this->connection,
+ $this->searchTableUpdater
+ );
+
+ $instance->requestOptimization( true );
+
+ $instance->rebuild();
+ }
+
+ public function testRebuildWithUpdateOnBlob() {
+
+ $row = new \stdClass;
+ $row->o_serialized = 'Foo';
+ $row->o_blob = null;
+ $row->s_id = 42;
+
+ $resultWrapper = $this->iteratorMockBuilder->setClass( '\ResultWrapper' )
+ ->with( [ $row ] )
+ ->incrementInvokedCounterBy( 1 )
+ ->getMockForIterator();
+
+ $resultWrapper->expects( $this->atLeastOnce() )
+ ->method( 'numRows' )
+ ->will( $this->returnValue( 1 ) );
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'select' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $tableDefinition = $this->getMockBuilder( '\SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getDiType' )
+ ->will( $this->returnValue( DataItem::TYPE_BLOB ) );
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'isValidByType' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTable->expects( $this->any() )
+ ->method( 'hasMinTokenLength' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $tableDefinition ] ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'update' )
+ ->with( $this->equalTo( $row->s_id ) );
+
+ $instance = new SearchTableRebuilder(
+ $this->connection,
+ $this->searchTableUpdater
+ );
+
+ $instance->reportVerbose( true );
+ $instance->rebuild();
+ }
+
+ public function testgetQualifiedTableList() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getDiType' )
+ ->will( $this->returnValue( DataItem::TYPE_BLOB ) );
+
+ $this->searchTableUpdater->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $propertyTableDefinition ] ) );
+
+ $instance = new SearchTableRebuilder(
+ $this->connection,
+ $this->searchTableUpdater
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getQualifiedTableList()
+ );
+ }
+
+ public function testRebuildByTable() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $propertyTableDefinition->expects( $this->atLeastOnce() )
+ ->method( 'getDiType' )
+ ->will( $this->returnValue( DataItem::TYPE_BLOB ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $propertyTableDefinition ] ) );
+
+ $instance = new SearchTableRebuilder(
+ $this->connection,
+ $this->searchTableUpdater
+ );
+
+ $instance->rebuildByTable( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableTest.php
new file mode 100644
index 00000000..7b2c0036
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataItemFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTable;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\SearchTable
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTableTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $dataItemFactory;
+
+ protected function setUp() {
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable',
+ new SearchTable( $this->store )
+ );
+ }
+
+ public function testIsEnabled() {
+
+ $instance = new SearchTable(
+ $this->store
+ );
+
+ $instance->setEnabled( true );
+
+ $this->assertTrue(
+ $instance->isEnabled()
+ );
+ }
+
+ public function testGetPropertyExemptionList() {
+
+ $instance = new SearchTable(
+ $this->store
+ );
+
+ $instance->setPropertyExemptionList(
+ [ '_TEXT','fo oo' ]
+ );
+
+ $this->assertEquals(
+ [ '_TEXT', 'fo_oo' ],
+ $instance->getPropertyExemptionList()
+ );
+ }
+
+ public function testIsExemptedProperty() {
+
+ $instance = new SearchTable(
+ $this->store
+ );
+
+ $instance->setIndexableDataTypes(
+ SMW_FT_BLOB | SMW_FT_URI
+ );
+
+ $instance->setPropertyExemptionList(
+ [ '_TEXT' ]
+ );
+
+ $property = $this->dataItemFactory->newDIProperty( '_TEXT' );
+
+ $this->assertTrue(
+ $instance->isExemptedProperty( $property )
+ );
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $property->setPropertyTypeId( '_uri' );
+
+ $this->assertFalse(
+ $instance->isExemptedProperty( $property )
+ );
+ }
+
+ public function testIsValidType() {
+
+ $instance = new SearchTable(
+ $this->store
+ );
+
+ $instance->setIndexableDataTypes(
+ SMW_FT_BLOB | SMW_FT_URI
+ );
+
+ $this->assertTrue(
+ $instance->isValidByType( DataItem::TYPE_BLOB )
+ );
+
+ $this->assertFalse(
+ $instance->isValidByType( DataItem::TYPE_WIKIPAGE )
+ );
+ }
+
+ public function testHasMinTokenLength() {
+
+ $instance = new SearchTable(
+ $this->store
+ );
+
+ $instance->setMinTokenSize( 4 );
+
+ $this->assertFalse(
+ $instance->hasMinTokenLength( 'bar' )
+ );
+
+ $this->assertFalse(
+ $instance->hasMinTokenLength( 'テスト' )
+ );
+
+ $this->assertTrue(
+ $instance->hasMinTokenLength( 'test' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableUpdaterTest.php
new file mode 100644
index 00000000..bdd716c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/SearchTableUpdaterTest.php
@@ -0,0 +1,215 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SearchTableUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+ private $searchTable;
+ private $textSanitizer;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->textSanitizer = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater',
+ new SearchTableUpdater( $this->connection, $this->searchTable, $this->textSanitizer )
+ );
+ }
+
+ public function testRead() {
+
+ $row = new \stdClass;
+ $row->o_text = 'Foo';
+
+ $this->connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'o_text' ] ),
+ $this->equalTo( [ 's_id' => 12, 'p_id' => 42 ] ) )
+ ->will( $this->returnValue( $row ) );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->read( 12, 42 );
+ }
+
+ public function testOptimizeOnEnabledType() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'isType' )
+ ->with( $this->equalTo( 'mysql' ) )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $this->assertTrue(
+ $instance->optimize()
+ );
+ }
+
+ public function testOptimizeOnDisabledType() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'isType' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->never() )
+ ->method( 'query' );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $this->assertFalse(
+ $instance->optimize()
+ );
+ }
+
+ public function testUpdateWithText() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'update' );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->update( 12, 42, 'foo' );
+ }
+
+ public function testDeleteOnUpdateWithEmptyText() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'delete' );
+
+ $this->connection->expects( $this->never() )
+ ->method( 'update' );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->update( 12, 42, ' ' );
+ }
+
+ public function testInsert() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [
+ 's_id' => 12,
+ 'p_id' => 42,
+ 'o_text' => '' ] ) );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->insert( 12, 42 );
+ }
+
+ public function testDelete() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [
+ 's_id' => 12,
+ 'p_id' => 42 ] ) );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->delete( 12, 42 );
+ }
+
+ public function testFlushTable() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( '*' ) );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->flushTable();
+ }
+
+ public function testExists() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [
+ 's_id' => 12,
+ 'p_id' => 42 ] ) );
+
+ $instance = new SearchTableUpdater(
+ $this->connection,
+ $this->searchTable,
+ $this->textSanitizer
+ );
+
+ $instance->exists( 12, 42 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextChangeUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextChangeUpdaterTest.php
new file mode 100644
index 00000000..29b3156f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextChangeUpdaterTest.php
@@ -0,0 +1,204 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataItemFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\TextChangeUpdater;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\TextChangeUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextChangeUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ private $dataItemFactory;
+ private $connection;
+ private $searchTableUpdater;
+ private $cache;
+ private $slot;
+ private $logger;
+ private $testEnvironment;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->logger = TestEnvironment::newSpyLogger();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobFactory', $this->jobFactory );
+
+ $this->slot = '';
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TextChangeUpdater::class,
+ new TextChangeUpdater( $this->connection, $this->cache, $this->searchTableUpdater )
+ );
+ }
+
+ public function testPushUpdatesOnNullChange() {
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->never() )
+ ->method( 'getSubject' );
+
+ $instance = new TextChangeUpdater(
+ $this->connection,
+ $this->cache,
+ $this->searchTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->logger
+ );
+
+ $instance->pushUpdates(
+ $changeOp
+ );
+ }
+
+ public function testPushUpdates() {
+
+ $dataItem = $this->dataItemFactory->newDIWikiPage( 'Foo', NS_MAIN );
+
+ $searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $searchTable->expects( $this->atLeastOnce() )
+ ->method( 'getDataItemById' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIWikiPage( 'Bar', SMW_NS_PROPERTY ) ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'getSearchTable' )
+ ->will( $this->returnValue( $searchTable ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [ '42' ] ) );
+
+ $changeOp->expects( $this->atLeastOnce() )
+ ->method( 'getSubject' )
+ ->will( $this->returnValue( $dataItem ) );
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->jobFactory->expects( $this->once() )
+ ->method( 'newFulltextSearchTableUpdateJob' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'slot:id' => 'Foo#0##' ] ) )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $instance = new TextChangeUpdater(
+ $this->connection,
+ $this->cache,
+ $this->searchTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->logger
+ );
+
+ $instance->pushUpdates(
+ $changeOp
+ );
+ }
+
+ public function testPushUpdatesDirectlyWhenExecutedFromCommandLine() {
+
+ $this->searchTableUpdater->expects( $this->atLeastOnce() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( true ) );
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff->expects( $this->once() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeDiff->expects( $this->once() )
+ ->method( 'getTextItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->once() )
+ ->method( 'newChangeDiff' )
+ ->will( $this->returnValue( $changeDiff ) );
+
+ $changeOp->expects( $this->never() )
+ ->method( 'getSubject' );
+
+ $instance = new TextChangeUpdater(
+ $this->connection,
+ $this->cache,
+ $this->searchTableUpdater
+ );
+
+ $instance->setLogger(
+ $this->logger
+ );
+
+ $instance->isCommandLineMode( true );
+
+ $instance->pushUpdates(
+ $changeOp
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextSanitizerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextSanitizerTest.php
new file mode 100644
index 00000000..4e720e61
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/TextSanitizerTest.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TextSanitizerTest extends \PHPUnit_Framework_TestCase {
+
+ private $sanitizerFactory;
+
+ protected function setUp() {
+
+ $this->sanitizerFactory = $this->getMockBuilder( '\Onoi\Tesa\SanitizerFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer',
+ new TextSanitizer( $this->sanitizerFactory )
+ );
+ }
+
+ /**
+ * @dataProvider textOnMockProvider
+ */
+ public function testSanitizs( $text, $expected ) {
+
+ $sanitizer = $this->getMockBuilder( '\Onoi\Tesa\Sanitizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $sanitizer->expects( $this->atLeastOnce() )
+ ->method( 'sanitizeWith' )
+ ->will( $this->returnValue( $text ) );
+
+ $stopwordAnalyzer = $this->getMockBuilder( '\Onoi\Tesa\StopwordAnalyzer\StopwordAnalyzer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $synonymizer = $this->getMockBuilder( '\Onoi\Tesa\Synonymizer\Synonymizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tokenizer = $this->getMockBuilder( '\Onoi\Tesa\Tokenizer\Tokenizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->sanitizerFactory->expects( $this->atLeastOnce() )
+ ->method( 'newSanitizer' )
+ ->will( $this->returnValue( $sanitizer ) );
+
+ $this->sanitizerFactory->expects( $this->atLeastOnce() )
+ ->method( 'newPreferredTokenizerByLanguage' )
+ ->will( $this->returnValue( $tokenizer ) );
+
+ $this->sanitizerFactory->expects( $this->atLeastOnce() )
+ ->method( 'newStopwordAnalyzerByLanguage' )
+ ->will( $this->returnValue( $stopwordAnalyzer ) );
+
+ $this->sanitizerFactory->expects( $this->atLeastOnce() )
+ ->method( 'newSynonymizerByLanguage' )
+ ->will( $this->returnValue( $synonymizer ) );
+
+ $instance = new TextSanitizer(
+ $this->sanitizerFactory
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->sanitize( $text )
+ );
+ }
+
+ public function textOnMockProvider() {
+
+ $provider[] = [
+ 'foo',
+ 'foo'
+ ];
+
+ $provider[] = [
+ 'foo* - bar',
+ 'foo* -bar'
+ ];
+
+ $provider[] = [
+ 'foo* + bar',
+ 'foo* +bar'
+ ];
+
+ $provider[] = [
+ 'foo *',
+ 'foo*'
+ ];
+
+ $provider[] = [
+ '* foo *',
+ '*foo*'
+ ];
+
+ $provider[] = [
+ '*foo* bar',
+ '*foo*bar'
+ ];
+
+ $provider[] = [
+ '+foo*, *bar',
+ '+foo*,*bar'
+ ];
+
+ $provider[] = [
+ '+foo* -bar',
+ '+foo* -bar'
+ ];
+
+ $provider[] = [
+ '+foo* ~ bar',
+ '+foo* ~bar'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilderTest.php
new file mode 100644
index 00000000..b61e61e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/Fulltext/ValueMatchConditionBuilderTest.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine\Fulltext;
+
+use SMW\DataItemFactory;
+use SMW\SQLStore\QueryEngine\Fulltext\ValueMatchConditionBuilder;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\Fulltext\ValueMatchConditionBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ValueMatchConditionBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $textSanitizer;
+ private $searchTable;
+ private $dataItemFactory;
+
+ protected function setUp() {
+
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->textSanitizer = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->searchTable = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\ValueMatchConditionBuilder',
+ new ValueMatchConditionBuilder( $this->textSanitizer, $this->searchTable )
+ );
+ }
+
+ public function testIsEnabled() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'isEnabled' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertFalse(
+ $instance->isEnabled()
+ );
+ }
+
+ public function testGetTableName() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getTableName' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo',
+ $instance->getTableName()
+ );
+ }
+
+ public function testHasMinTokenLength() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'hasMinTokenLength' )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertFalse(
+ $instance->hasMinTokenLength( 'bar' )
+ );
+ }
+
+ public function testGetSortIndexField() {
+
+ $this->searchTable->expects( $this->once() )
+ ->method( 'getSortField' )
+ ->will( $this->returnValue( 'bar' ) );
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $this->assertEquals(
+ 'Foo.bar',
+ $instance->getSortIndexField( 'Foo' )
+ );
+ }
+
+ public function testCanApplyFulltextSearchMatchCondition() {
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertFalse(
+ $instance->canApplyFulltextSearchMatchCondition( $description )
+ );
+ }
+
+ public function testGetWhereConditionWithPropertyOnTempTable() {
+
+ $instance = new ValueMatchConditionBuilder(
+ $this->textSanitizer,
+ $this->searchTable
+ );
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\ValueDescription' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertSame(
+ '',
+ $instance->getWhereCondition( $description )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/FulltextSearchTableFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/FulltextSearchTableFactoryTest.php
new file mode 100644
index 00000000..918f3d2a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/FulltextSearchTableFactoryTest.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\FulltextSearchTableFactory;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\FulltextSearchTableFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FulltextSearchTableFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstructValueMatchConditionBuilderOnUnknownConnectionType() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\ValueMatchConditionBuilder',
+ $instance->newValueMatchConditionBuilderByType( $this->store )
+ );
+ }
+
+ public function testCanConstructValueMatchConditionBuilderOnMySQLConnectionType() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\MySQLValueMatchConditionBuilder',
+ $instance->newValueMatchConditionBuilderByType( $this->store )
+ );
+ }
+
+ public function testCanConstructTextSanitizer() {
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\TextSanitizer',
+ $instance->newTextSanitizer()
+ );
+ }
+
+ public function testCanConstructSearchTable() {
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTable',
+ $instance->newSearchTable( $this->store )
+ );
+ }
+
+ public function testCanConstructSearchTableUpdater() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableUpdater',
+ $instance->newSearchTableUpdater( $this->store )
+ );
+ }
+
+ public function testCanConstructTextChangeUpdater() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\TextChangeUpdater',
+ $instance->newTextChangeUpdater( $this->store )
+ );
+ }
+
+ public function testCanConstructSearchTableRebuilder() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new FulltextSearchTableFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\Fulltext\SearchTableRebuilder',
+ $instance->newSearchTableRebuilder( $this->store )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/HierarchyTempTableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/HierarchyTempTableBuilderTest.php
new file mode 100644
index 00000000..b01b114e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/HierarchyTempTableBuilderTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class HierarchyTempTableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $connection;
+ private $temporaryTableBuilder;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->temporaryTableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TemporaryTableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder',
+ new HierarchyTempTableBuilder( $this->connection, $this->temporaryTableBuilder )
+ );
+ }
+
+ public function testGetHierarchyTableDefinitionForType() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableName' )
+ ->with(
+ $this->stringContains( 'bar') )
+ ->will( $this->returnValue( '_bar' ) );
+
+ $instance = new HierarchyTempTableBuilder(
+ $this->connection,
+ $this->temporaryTableBuilder
+ );
+
+ $instance->setPropertyHierarchyTableDefinition( 'bar', 3 );
+
+ $this->assertEquals(
+ [ '_bar', 3 ],
+ $instance->getHierarchyTableDefinitionForType( 'property' )
+ );
+ }
+
+ public function testTryToGetHierarchyTableDefinitionForUnregisteredTypeThrowsException() {
+
+ $instance = new HierarchyTempTableBuilder(
+ $this->connection,
+ $this->temporaryTableBuilder
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->getHierarchyTableDefinitionForType( 'foo' );
+ }
+
+ public function testCreateHierarchyTempTable() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableName' )
+ ->with(
+ $this->stringContains( 'bar') )
+ ->will( $this->returnValue( '_bar' ) );
+
+ $this->connection->expects( $this->atLeastOnce() )
+ ->method( 'query' );
+
+ $instance = new HierarchyTempTableBuilder(
+ $this->connection,
+ $this->temporaryTableBuilder
+ );
+
+ $instance->setClassHierarchyTableDefinition( 'bar', 3 );
+ $instance->createHierarchyTempTableFor( 'class', 'foobar', '(42)' );
+
+ $expected = [
+ '(42)' => 'foobar'
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getHierarchyCache()
+ );
+
+ $instance->emptyHierarchyCache();
+
+ $this->assertEmpty(
+ $instance->getHierarchyCache()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/OrderConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/OrderConditionTest.php
new file mode 100644
index 00000000..aca77277
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/OrderConditionTest.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\OrderCondition;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\OrderCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class OrderConditionTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $querySegmentListBuilder;
+
+ protected function setUp() {
+
+ $this->querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ OrderCondition::class,
+ new OrderCondition( $this->querySegmentListBuilder )
+ );
+ }
+
+ public function testApplyWithoutSortKey() {
+
+ $this->querySegmentListBuilder->expects( $this->once() )
+ ->method( 'getQuerySegmentList' );
+
+ $instance = new OrderCondition(
+ $this->querySegmentListBuilder
+ );
+
+ $instance->apply( 42 );
+ }
+
+ /**
+ * @dataProvider sortKeyProvider
+ */
+ public function testApplyWithSortKey( $sortKeys ) {
+
+ $querySegment = new QuerySegment();
+
+ $this->querySegmentListBuilder->expects( $this->once() )
+ ->method( 'getQuerySegmentList' );
+
+ $this->querySegmentListBuilder->expects( $this->atLeastOnce() )
+ ->method( 'findQuerySegment' )
+ ->will( $this->returnValue( $querySegment ) );
+
+ $instance = new OrderCondition(
+ $this->querySegmentListBuilder
+ );
+
+ $instance->setSortKeys( $sortKeys );
+
+ $instance->apply( 42 );
+ $querySegment->reset();
+ }
+
+ public function testApplyWithInvalidSortKeyThrowsException() {
+
+ $querySegment = new QuerySegment();
+
+ $this->querySegmentListBuilder->expects( $this->never() )
+ ->method( 'getQuerySegmentList' );
+
+ $this->querySegmentListBuilder->expects( $this->atLeastOnce() )
+ ->method( 'findQuerySegment' )
+ ->will( $this->returnValue( $querySegment ) );
+
+ $instance = new OrderCondition(
+ $this->querySegmentListBuilder
+ );
+
+ $instance->setSortKeys( [
+ 42 => 'ASC'
+ ]
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->apply( 42 );
+
+ $querySegment->reset();
+ }
+
+ public function sortKeyProvider() {
+
+ $provider[] = [
+ [
+ '' => 'ASC'
+ ]
+ ];
+
+ $provider[] = [
+ [
+ '#' => 'DESC'
+ ]
+ ];
+
+ $provider[] = [
+ [
+ 'Foo' => 'ASC'
+ ]
+ ];
+
+ $provider[] = [
+ [
+ 'Foo.bar' => 'ASC'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QueryEngineTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QueryEngineTest.php
new file mode 100644
index 00000000..93c03f10
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QueryEngineTest.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\QueryEngine;
+use SMWQuery as Query;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\QueryEngine
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QueryEngineTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $querySegmentListBuildManager;
+ private $querySegmentListProcessor;
+ private $engineOptions;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->querySegmentListBuildManager = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuildManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->querySegmentListProcessor = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListProcessor' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->engineOptions = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\EngineOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QueryEngine',
+ new QueryEngine( $this->store, $this->querySegmentListBuildManager, $this->querySegmentListProcessor, $this->engineOptions )
+ );
+ }
+
+ public function testGetQueryResultForDebugQueryMode() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->querySegmentListBuildManager->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->querySegmentListProcessor->expects( $this->any() )
+ ->method( 'getExecutedQueries' )
+ ->will( $this->returnValue( [] ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $this->store,
+ $this->querySegmentListBuildManager,
+ $this->querySegmentListProcessor,
+ $this->engineOptions
+ );
+
+ $query = new Query( $description );
+ $query->querymode = Query::MODE_DEBUG;
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+ public function testGetImmediateEmptyQueryResultForLimitLessThanOne() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->never() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->querySegmentListBuildManager->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [] ) );
+
+ $description = $this->getMockForAbstractClass( '\SMW\Query\Language\Description' );
+
+ $instance = new QueryEngine(
+ $this->store,
+ $this->querySegmentListBuildManager,
+ $this->querySegmentListProcessor,
+ $this->engineOptions
+ );
+
+ $query = new Query( $description );
+ $query->setUnboundLimit( -1 );
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ $instance->getQueryResult( $query )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuildManagerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuildManagerTest.php
new file mode 100644
index 00000000..6245b2b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuildManagerTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuildManager;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\QuerySegmentListBuildManager
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class QuerySegmentListBuildManagerTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+ private $querySegmentListBuilder;
+ private $orderCondition;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->querySegmentListBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->orderCondition = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\OrderCondition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ QuerySegmentListBuildManager::class,
+ new QuerySegmentListBuildManager( $this->connection, $this->querySegmentListBuilder, $this->orderCondition )
+ );
+ }
+
+ public function testGetQuerySegmentFrom() {
+
+ $description = $this->getMockBuilder( '\SMW\Query\Language\Description' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $query->expects( $this->atLeastOnce() )
+ ->method( 'getDescription' )
+ ->will( $this->returnValue( $description ) );
+
+ $this->querySegmentListBuilder->expects( $this->once() )
+ ->method( 'getQuerySegmentFrom' );
+
+ $this->orderCondition->expects( $this->once() )
+ ->method( 'apply' );
+
+ $instance = new QuerySegmentListBuildManager(
+ $this->connection,
+ $this->querySegmentListBuilder,
+ $this->orderCondition
+ );
+
+ $instance->getQuerySegmentFrom( $query );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuilderTest.php
new file mode 100644
index 00000000..e61a2909
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListBuilderTest.php
@@ -0,0 +1,293 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\DIWikiPage;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory;
+use SMW\SQLStore\QueryEngine\QuerySegment;
+use SMW\SQLStore\QueryEngine\QuerySegmentListBuilder;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\QuerySegmentListBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QuerySegmentListBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $querySegmentValidator;
+ private $descriptionInterpreterFactory;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->descriptionInterpreterFactory = new DescriptionInterpreterFactory();
+
+ $testEnvironment = new TestEnvironment();
+ $this->querySegmentValidator = $testEnvironment->getUtilityFactory()->newValidatorFactory()->newQuerySegmentValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $descriptionInterpreterFactory = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\DescriptionInterpreterFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder',
+ new QuerySegmentListBuilder( $this->store, $descriptionInterpreterFactory )
+ );
+ }
+
+ public function testNamespaceDescription() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $description = new NamespaceDescription( NS_HELP );
+
+ $instance = new QuerySegmentListBuilder(
+ $store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $instance->getQuerySegmentFrom( $description );
+
+ $expected = new \stdClass;
+ $expected->type = 1;
+ $expected->where = "t0.smw_namespace=";
+
+ $this->assertEquals( 0, $instance->getLastQuerySegmentId() );
+ $this->assertEmpty( $instance->getErrors() );
+
+ $this->querySegmentValidator->assertThatContainerContains(
+ $expected,
+ $instance->getQuerySegmentList()
+ );
+ }
+
+ public function testDisjunctiveNamespaceDescription() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $description = new Disjunction();
+ $description->addDescription( new NamespaceDescription( NS_HELP ) );
+ $description->addDescription( new NamespaceDescription( NS_MAIN ) );
+
+ $instance = new QuerySegmentListBuilder(
+ $store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $instance->getQuerySegmentFrom( $description );
+
+ $expectedDisjunction = new \stdClass;
+ $expectedDisjunction->type = 3;
+
+ $expectedHelpNs = new \stdClass;
+ $expectedHelpNs->type = 1;
+ $expectedHelpNs->where = "t1.smw_namespace=";
+
+ $expectedMainNs = new \stdClass;
+ $expectedMainNs->type = 1;
+ $expectedMainNs->where = "t2.smw_namespace=";
+
+ $this->assertEquals(
+ 0,
+ $instance->getLastQuerySegmentId()
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ $expected = [
+ $expectedDisjunction,
+ $expectedHelpNs,
+ $expectedMainNs
+ ];
+
+ $this->querySegmentValidator->assertThatContainerContains(
+ $expected,
+ $instance->getQuerySegmentList()
+ );
+ }
+
+ public function testClassDescription() {
+
+ $objectIds = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getSMWPageID' ] )
+ ->getMock();
+
+ $objectIds->expects( $this->any() )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 42 ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIds ) );
+
+ $description = new ClassDescription( new DIWikiPage( 'Foo', NS_CATEGORY ) );
+
+ $instance = new QuerySegmentListBuilder(
+ $store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $instance->getQuerySegmentFrom( $description );
+
+ $expectedClass = new \stdClass;
+ $expectedClass->type = 1;
+ $expectedClass->alias = "t0";
+ $expectedClass->queryNumber = 0;
+
+ $expectedHierarchy = new \stdClass;
+ $expectedHierarchy->type = 5;
+ $expectedHierarchy->joinfield = [ 0 => 42 ];
+ $expectedHierarchy->alias = "t1";
+ $expectedHierarchy->queryNumber = 1;
+
+ $this->assertEquals(
+ 0,
+ $instance->getLastQuerySegmentId()
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+
+ $expected = [
+ $expectedClass,
+ $expectedHierarchy
+ ];
+
+ $this->querySegmentValidator->assertThatContainerContains(
+ $expected,
+ $instance->getQuerySegmentList()
+ );
+ }
+
+ public function testGivenNonInteger_getQuerySegmentThrowsException() {
+
+ $instance = new QuerySegmentListBuilder(
+ $this->store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->findQuerySegment( null );
+ }
+
+ public function testGivenUnknownId_getQuerySegmentThrowsException() {
+
+ $instance = new QuerySegmentListBuilder(
+ $this->store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->findQuerySegment( 1 );
+ }
+
+ public function testGivenKnownId_getQuerySegmentReturnsCorrectPart() {
+
+ $instance = new QuerySegmentListBuilder(
+ $this->store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $querySegment = new QuerySegment();
+
+ // $querySegment->segmentNumber = 1;
+ $instance->addQuerySegment( $querySegment );
+
+ $this->assertSame(
+ $querySegment,
+ $instance->findQuerySegment( $querySegment->queryNumber )
+ );
+ }
+
+ public function testWhenNoQuerySegments_getQuerySegmentListReturnsEmptyArray() {
+
+ $instance = new QuerySegmentListBuilder(
+ $this->store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $this->assertSame(
+ [],
+ $instance->getQuerySegmentList()
+ );
+ }
+
+ public function testWhenSomeQuerySegments_getQuerySegmentListReturnsThemAll() {
+
+ $instance = new QuerySegmentListBuilder(
+ $this->store,
+ $this->descriptionInterpreterFactory
+ );
+
+ $firstQuerySegment = new QuerySegment();
+ $instance->addQuerySegment( $firstQuerySegment );
+
+ $secondQuerySegment = new QuerySegment();
+ $instance->addQuerySegment( $secondQuerySegment );
+
+ $expected = [
+ 0 => $firstQuerySegment,
+ 1 => $secondQuerySegment
+ ];
+
+ $this->assertSame(
+ $expected,
+ $instance->getQuerySegmentList()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListProcessorTest.php
new file mode 100644
index 00000000..9963ac5b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentListProcessorTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\QuerySegmentListProcessor;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\QuerySegmentListProcessor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QuerySegmentListProcessorTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $temporaryTableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TemporaryTableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyTempTableBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegmentListProcessor',
+ new QuerySegmentListProcessor( $connection, $temporaryTableBuilder, $hierarchyTempTableBuilder )
+ );
+ }
+
+ public function testTryResolveSegmentForInvalidIdThrowsException() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $temporaryTableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\TemporaryTableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyTempTableBuilder = $this->getMockBuilder( '\SMW\SQLStore\QueryEngine\HierarchyTempTableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QuerySegmentListProcessor(
+ $connection,
+ $temporaryTableBuilder,
+ $hierarchyTempTableBuilder
+ );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->process( 42 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentTest.php
new file mode 100644
index 00000000..f4388a5e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngine/QuerySegmentTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Tests\SQLStore\QueryEngine;
+
+use SMW\SQLStore\QueryEngine\QuerySegment;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngine\QuerySegment
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class QuerySegmentTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegment',
+ new QuerySegment()
+ );
+ }
+
+ public function testInitialStateAfterReset() {
+
+ $instance = new QuerySegment();
+ $instance->reset();
+
+ $this->assertEquals(
+ 0,
+ $instance->queryNumber
+ );
+
+ $this->assertEquals(
+ 't0',
+ $instance->alias
+ );
+
+ $this->assertEquals(
+ 1,
+ $instance::$qnum
+ );
+
+ $this->assertEquals(
+ $instance::Q_TABLE,
+ $instance->type
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->components
+ );
+
+ $this->assertEquals(
+ [],
+ $instance->sortfields
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->joinfield
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->joinTable
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->from
+ );
+
+ $this->assertEquals(
+ '',
+ $instance->where
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngineFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngineFactoryTest.php
new file mode 100644
index 00000000..0ba0f83b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/QueryEngineFactoryTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\QueryEngineFactory;
+
+/**
+ * @covers \SMW\SQLStore\QueryEngineFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class QueryEngineFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngineFactory',
+ new QueryEngineFactory( $store )
+ );
+ }
+
+ public function testCanConstructQuerySegmentListBuilder() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new QueryEngineFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegmentListBuilder',
+ $instance->newQuerySegmentListBuilder()
+ );
+ }
+
+ public function testCanConstructQuerySegmentListProcessor() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new QueryEngineFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegmentListProcessor',
+ $instance->newQuerySegmentListProcessor()
+ );
+ }
+
+ public function testCanConstructQueryEngine() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new QueryEngineFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QueryEngine',
+ $instance->newQueryEngine()
+ );
+ }
+
+ public function testCanConstructConceptQuerySegmentBuilder() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new QueryEngineFactory( $store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\ConceptQuerySegmentBuilder',
+ $instance->newConceptQuerySegmentBuilder()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RedirectStoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RedirectStoreTest.php
new file mode 100644
index 00000000..f14bdd61
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RedirectStoreTest.php
@@ -0,0 +1,265 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\InMemoryPoolCache;
+use SMW\SQLStore\RedirectStore;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\RedirectStore
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class RedirectStoreTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $conection;
+ private $cache;
+ private $testEnvironment;
+ private $connectionManager;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( null )
+ ->getMock();
+
+ $this->connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $this->connection ) );
+
+ $this->store->setConnectionManager( $this->connectionManager );
+
+ $this->jobQueue = $this->getMockBuilder( '\SMW\MediaWiki\JobQueue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'JobQueue', $this->jobQueue );
+
+ InMemoryPoolCache::getInstance()->clear();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ InMemoryPoolCache::getInstance()->clear();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RedirectStore::class,
+ new RedirectStore( $this->store )
+ );
+ }
+
+ public function testFindRedirectIdForNonCachedRedirect() {
+
+ $row = new \stdClass;
+ $row->o_id = 42;
+
+ $this->connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [
+ 's_title' => 'Foo',
+ 's_namespace' => 0 ] ) )
+ ->will( $this->returnValue( $row ) );
+
+ $instance = new RedirectStore(
+ $this->store
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->findRedirect( 'Foo', 0 )
+ );
+
+ $stats = InMemoryPoolCache::getInstance()->getStats();
+
+ $this->assertEquals(
+ 0,
+ $stats['sql.store.redirect.infostore']['hits']
+ );
+
+ $instance->findRedirect( 'Foo', 0 );
+
+ $stats = InMemoryPoolCache::getInstance()->getStats();
+
+ $this->assertEquals(
+ 1,
+ $stats['sql.store.redirect.infostore']['hits']
+ );
+ }
+
+ public function testFindRedirectIdForNonCachedNonRedirect() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [
+ 's_title' => 'Foo',
+ 's_namespace' => 0 ] ) )
+ ->will( $this->returnValue( false ) );
+
+ $instance = new RedirectStore(
+ $this->store
+ );
+
+ $this->assertEquals(
+ 0,
+ $instance->findRedirect( 'Foo', 0 )
+ );
+ }
+
+ public function testAddRedirectInfoRecordToFetchFromCache() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'insert' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [
+ 's_title' => 'Foo',
+ 's_namespace' => 0,
+ 'o_id' => 42 ] ) );
+
+ $instance = new RedirectStore(
+ $this->store
+ );
+
+ $instance->addRedirect( 42, 'Foo', 0 );
+
+ $this->assertEquals(
+ 42,
+ $instance->findRedirect( 'Foo', 0 )
+ );
+ }
+
+ public function testDeleteRedirectInfoRecord() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'delete' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [
+ 's_title' => 'Foo',
+ 's_namespace' => 9001 ] ) );
+
+ $instance = new RedirectStore(
+ $this->store
+ );
+
+ $instance->deleteRedirect( 'Foo', 9001 );
+
+ $this->assertEquals(
+ 0,
+ $instance->findRedirect( 'Foo', 9001 )
+ );
+ }
+
+ public function testUpdateRedirect() {
+
+ $row = new \stdClass;
+ $row->ns = NS_MAIN;
+ $row->t = 'Bar';
+
+ $this->connection->expects( $this->once() )
+ ->method( 'select' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'Foo' => 42 ] ) )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $propertyTable = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTable->expects( $this->once() )
+ ->method( 'getFields' )
+ ->will( $this->returnValue( [ 'Foo' => \SMW\SQLStore\TableBuilder\FieldType::FIELD_ID ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->setConnectionManager( $this->connectionManager );
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $propertyTable ] ) );
+
+ $store->setOption(
+ \SMW\Store::OPT_CREATE_UPDATE_JOB,
+ true
+ );
+
+ $this->testEnvironment->addConfiguration(
+ 'smwgEnableUpdateJobs',
+ true
+ );
+
+ $store->setOption(
+ 'smwgEnableUpdateJobs',
+ true
+ );
+
+ $this->jobQueue->expects( $this->once() )
+ ->method( 'push' );
+
+ $instance = new RedirectStore(
+ $store
+ );
+
+ $instance->setEqualitySupportFlag( SMW_EQ_FULL );
+ $instance->updateRedirect( 42, 'Foo', NS_MAIN );
+ }
+
+ public function testUpdateRedirectNotEnabled() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyTables' ] )
+ ->getMock();
+
+ $store->expects( $this->never() )
+ ->method( 'getPropertyTables' );
+
+ $store->setOption(
+ \SMW\Store::OPT_CREATE_UPDATE_JOB,
+ false
+ );
+
+ $instance = new RedirectStore(
+ $store
+ );
+
+ $instance->setEqualitySupportFlag( SMW_EQ_NONE );
+ $instance->updateRedirect( 42, 'Foo', NS_MAIN );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RequestOptionsProcTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RequestOptionsProcTest.php
new file mode 100644
index 00000000..f2abf700
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/RequestOptionsProcTest.php
@@ -0,0 +1,343 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\RequestOptionsProc;
+use SMWRequestOptions as RequestOptions;
+use SMWStringCondition as StringCondition;
+
+/**
+ * @covers \SMW\SQLStore\RequestOptionsProc
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class RequestOptionsProcTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testGetSQLOptions() {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->limit = 1;
+ $requestOptions->offset = 2;
+ $requestOptions->sort = true;
+
+ $expected = [
+ 'LIMIT' => 1,
+ 'OFFSET' => 2
+ ];
+
+ $this->assertEquals(
+ $expected,
+ RequestOptionsProc::getSQLOptions( $requestOptions, 'Foo' )
+ );
+ }
+
+ public function testGetSQLOptionsWithOrderBy() {
+
+ $instance = new RequestOptionsProc( $this->store );
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->limit = 2;
+ $requestOptions->offset = 2;
+ $requestOptions->sort = true;
+
+ $expected = [
+ 'LIMIT' => 2,
+ 'OFFSET' => 2,
+ 'ORDER BY' => 'Foo'
+ ];
+
+ $this->assertEquals(
+ $expected,
+ RequestOptionsProc::getSQLOptions( $requestOptions, 'Foo' )
+ );
+ }
+
+ /**
+ * @dataProvider requestOptionsToSqlConditionsProvider
+ */
+ public function testGetSQLConditions( $requestOptions, $valueCol, $labelCol, $expected ) {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'addQuotes' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->assertEquals(
+ $expected,
+ RequestOptionsProc::getSQLConditions( $this->store, $requestOptions, $valueCol, $labelCol )
+ );
+ }
+
+ /**
+ * @dataProvider requestOptionsToApplyProvider
+ */
+ public function testApplyRequestOptions( $data, $requestOptions, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ RequestOptionsProc::applyRequestOptions( $this->store, $data, $requestOptions )
+ );
+ }
+
+ public function requestOptionsToSqlConditionsProvider() {
+
+ $provider = [];
+
+ # 0
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ '',
+ ' AND Foo >= 1'
+ ];
+
+ # 1
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foobar', StringCondition::STRCOND_PRE );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' AND Foo >= 1 AND Bar LIKE foobar%'
+ ];
+
+ # 2
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foobar', StringCondition::STRCOND_PRE, true );
+ $requestOptions->addStringCondition( 'foobaz', StringCondition::STRCOND_POST, true );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' AND Foo >= 1 OR Bar LIKE foobar% OR Bar LIKE %foobaz'
+ ];
+
+ # 3
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' AND Foo >= 1 AND Bar = foo_bar'
+ ];
+
+ # 4
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ );
+ $requestOptions->addExtraCondition( 'abd = 123' );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' AND Foo >= 1 AND Bar = foo_bar AND abd = 123'
+ ];
+
+ # 5
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ );
+ $requestOptions->addExtraCondition( [ 'OR' => 'abd = 123' ] );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' AND Foo >= 1 AND Bar = foo_bar OR abd = 123'
+ ];
+
+ # 6
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ, true, true );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' ( AND Foo >= 1) AND (Bar NOT = foo_bar) '
+ ];
+
+ # 7
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ, true );
+ $requestOptions->addStringCondition( 'foobar', StringCondition::COND_POST, true, true );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' ( AND Foo >= 1 OR Bar = foo_bar) AND (Bar NOT LIKE %foobar) '
+ ];
+
+ # 8
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $requestOptions->addStringCondition( 'foo_bar', StringCondition::COND_EQ, true );
+ $requestOptions->addStringCondition( 'foobar', StringCondition::COND_POST, false, true );
+ $requestOptions->addStringCondition( 'foobar_ex', StringCondition::COND_EQ, false, true );
+
+ $provider[] = [
+ $requestOptions,
+ 'Foo',
+ 'Bar',
+ ' ( ( AND Foo >= 1 OR Bar = foo_bar) AND (Bar NOT LIKE %foobar) ) AND (Bar NOT = foobar_ex) '
+ ];
+
+ return $provider;
+ }
+
+ public function requestOptionsToApplyProvider() {
+
+ $provider = [];
+
+ #0
+ $requestOptions = new RequestOptions();
+ $requestOptions->boundary = true;
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' )
+ ],
+ $requestOptions,
+ [
+ new \SMWDIBlob( 'Foo' )
+ ]
+ ];
+
+ #1
+ $requestOptions = new RequestOptions();
+ $requestOptions->addStringCondition( 'Foo', StringCondition::STRCOND_PRE );
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' )
+ ],
+ $requestOptions,
+ [
+ new \SMWDIBlob( 'Foo' )
+ ]
+ ];
+
+ #2 String not match
+ $requestOptions = new RequestOptions();
+ $requestOptions->addStringCondition( 'Bar', StringCondition::STRCOND_POST );
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' )
+ ],
+ $requestOptions,
+ []
+ ];
+
+ #3 Limit
+ $requestOptions = new RequestOptions();
+ $requestOptions->limit = 1;
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' ),
+ new \SMWDIBlob( 'Bar' )
+ ],
+ $requestOptions,
+ [
+ new \SMWDIBlob( 'Foo' )
+ ]
+ ];
+
+ #4 ascending
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->ascending = true;
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' ),
+ new \SMWDIBlob( 'Bar' )
+ ],
+ $requestOptions,
+ [
+ new \SMWDIBlob( 'Bar' ),
+ new \SMWDIBlob( 'Foo' )
+ ]
+ ];
+
+ #5 descending
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->ascending = false;
+
+ $provider[] = [
+ [
+ new \SMWDIBlob( 'Foo' ),
+ new \SMWDIBlob( 'Bar' )
+ ],
+ $requestOptions,
+ [
+ new \SMWDIBlob( 'Foo' ),
+ new \SMWDIBlob( 'Bar' )
+ ]
+ ];
+
+ #6 descending
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+ $requestOptions->ascending = false;
+
+ $provider[] = [
+ [
+ new \SMWDINumber( 10 ),
+ new \SMWDINumber( 200 )
+ ],
+ $requestOptions,
+ [
+ new \SMWDINumber( 200 ),
+ new \SMWDINumber( 10 )
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/SQLStoreFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/SQLStoreFactoryTest.php
new file mode 100644
index 00000000..c175c958
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/SQLStoreFactoryTest.php
@@ -0,0 +1,438 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\Options;
+use SMW\SQLStore\SQLStoreFactory;
+use SMW\Tests\TestEnvironment;
+use SMWSQLStore3;
+
+/**
+ * @covers \SMW\SQLStore\SQLStoreFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SQLStoreFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\SQLStoreFactory',
+ new SQLStoreFactory( $this->store )
+ );
+ }
+
+ public function testNewSlaveQueryEngineReturnType() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->newSlaveQueryEngine()
+ );
+ }
+
+ public function testNewMasterQueryEngineReturnType() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ '\SMW\QueryEngine',
+ $instance->newMasterQueryEngine()
+ );
+ }
+
+ public function testNewMasterConceptCache() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\ConceptCache',
+ $instance->newMasterConceptCache()
+ );
+ }
+
+ public function testNewSlaveConceptCache() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\ConceptCache',
+ $instance->newSlaveConceptCache()
+ );
+ }
+
+ public function testCanConstractEntityIdManager() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMWSql3SmwIds',
+ $instance->newEntityTable()
+ );
+ }
+
+ public function testCanConstructUsageStatisticsCachedListLookup() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Lookup\CachedListLookup',
+ $instance->newUsageStatisticsCachedListLookup()
+ );
+ }
+
+ public function testCanConstructPropertyUsageCachedListLookup() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Lookup\CachedListLookup',
+ $instance->newPropertyUsageCachedListLookup( null )
+ );
+ }
+
+ public function testCanConstructUnusedPropertyCachedListLookup() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Lookup\CachedListLookup',
+ $instance->newUnusedPropertyCachedListLookup( null )
+ );
+ }
+
+ public function testCanConstructUndeclaredPropertyCachedListLookup() {
+
+ $instance = new SQLStoreFactory( new SMWSQLStore3() );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Lookup\CachedListLookup',
+ $instance->newUndeclaredPropertyCachedListLookup( null, '_foo' )
+ );
+ }
+
+ public function testCanConstructCachedListLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $listLookup = $this->getMockBuilder( '\SMW\SQLStore\Lookup\ListLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Lookup\CachedListLookup',
+ $instance->newCachedListLookup( $listLookup, true, 42 )
+ );
+ }
+
+ public function testCanConstructEntityLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->testEnvironment->addConfiguration( 'smwgEntityLookupCacheType', CACHE_NONE );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\EntityStore\NativeEntityLookup',
+ $instance->newEntityLookup()
+ );
+
+ $this->testEnvironment->addConfiguration( 'smwgEntityLookupCacheType', 'hash' );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\EntityStore\CachingEntityLookup',
+ $instance->newEntityLookup()
+ );
+ }
+
+ public function testCanConstructPropertyTableIdReferenceFinder() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\PropertyTableIdReferenceFinder',
+ $instance->newPropertyTableIdReferenceFinder()
+ );
+ }
+
+ public function testCanConstructDataItemHandlerDispatcher() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\EntityStore\DataItemHandlerDispatcher',
+ $instance->newDataItemHandlerDispatcher()
+ );
+ }
+
+ public function testCanConstructDeferredCachedListLookupUpdate() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ 'SMW\DeferredCallableUpdate',
+ $instance->newDeferredCallableCachedListLookupUpdate()
+ );
+ }
+
+ public function testCanConstructInstaller() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new Options() ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SQLStoreFactory( $store );
+
+ $this->assertInstanceOf(
+ 'SMW\SQLStore\Installer',
+ $instance->newInstaller()
+ );
+ }
+
+ public function testGetLogger() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\Psr\Log\LoggerInterface',
+ $instance->getLogger()
+ );
+ }
+
+ public function testCanConstrucTraversalPropertyLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\TraversalPropertyLookup',
+ $instance->newTraversalPropertyLookup()
+ );
+ }
+
+ public function testCanConstrucPropertySubjectsLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\PropertySubjectsLookup',
+ $instance->newPropertySubjectsLookup()
+ );
+ }
+
+ public function testCanConstructPropertyStatisticsStore() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyStatisticsStore',
+ $instance->newPropertyStatisticsStore()
+ );
+ }
+
+ public function testCanConstructIdCacheManager() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $params = [
+ 'entity.id' => '',
+ 'entity.sort' => '',
+ 'entity.lookup' => '',
+ 'table.hash' => ''
+ ];
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\IdCacheManager',
+ $instance->newIdCacheManager( 'foo', $params )
+ );
+ }
+
+ public function testCanConstrucPropertyTableRowDiffer() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\PropertyTableRowDiffer',
+ $instance->newPropertyTableRowDiffer()
+ );
+ }
+
+ public function testCanConstructIdEntityFinder() {
+
+ $idCacheManager = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdCacheManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\IdEntityFinder',
+ $instance->newIdEntityFinder( $idCacheManager )
+ );
+ }
+
+ public function testCanConstructIdChanger() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\IdChanger',
+ $instance->newIdChanger()
+ );
+ }
+
+ public function testCanConstructUniquenessLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\UniquenessLookup',
+ $instance->newUniquenessLookup()
+ );
+ }
+
+ public function testCanConstructHierarchyLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\HierarchyLookup',
+ $instance->newHierarchyLookup()
+ );
+ }
+
+ public function testCanConstructSubobjectListFinder() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\SubobjectListFinder',
+ $instance->newSubobjectListFinder()
+ );
+ }
+
+ public function testCanConstructSemanticDataLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityStore\CachingSemanticDataLookup',
+ $instance->newSemanticDataLookup()
+ );
+ }
+
+ public function testCanConstructTableFieldUpdater() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\TableFieldUpdater',
+ $instance->newTableFieldUpdater()
+ );
+ }
+
+ public function testCanConstructRedirectStore() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\RedirectStore',
+ $instance->newRedirectStore()
+ );
+ }
+
+ public function testCanConstructChangePropListener() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\ChangePropListener',
+ $instance->newChangePropListener()
+ );
+ }
+
+ public function testCanConstructChangeOp() {
+
+ $subject = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\ChangeOp\ChangeOp',
+ $instance->newChangeOp( $subject )
+ );
+ }
+
+ public function testCanConstructProximityPropertyValueLookup() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\Lookup\ProximityPropertyValueLookup',
+ $instance->newProximityPropertyValueLookup()
+ );
+ }
+
+ public function testCanConstructEntityValueUniquenessConstraintChecker() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\EntityValueUniquenessConstraintChecker',
+ $instance->newEntityValueUniquenessConstraintChecker()
+ );
+ }
+
+ public function testCanConstructServicesContainer() {
+
+ $instance = new SQLStoreFactory( $this->store );
+
+ $this->assertInstanceOf(
+ '\SMW\Services\ServicesContainer',
+ $instance->newServicesContainer()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/Examiner/HashFieldTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/Examiner/HashFieldTest.php
new file mode 100644
index 00000000..3497f40f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/Examiner/HashFieldTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder\Examiner;
+
+use SMW\SQLStore\TableBuilder\Examiner\HashField;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\Examiner\HashField
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.1
+ *
+ * @author mwjames
+ */
+class HashFieldTest extends \PHPUnit_Framework_TestCase {
+
+ private $spyMessageReporter;
+ private $store;
+ private $populateHashField;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->spyMessageReporter = TestEnvironment::getUtilityFactory()->newSpyMessageReporter();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->populateHashField = $this->getMockBuilder( '\SMW\Maintenance\PopulateHashField' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HashField::class,
+ new HashField( $this->store )
+ );
+ }
+
+ public function testCheck_Populate() {
+
+ $resultWrapper = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resultWrapper->expects( $this->once() )
+ ->method( 'numRows' )
+ ->will( $this->returnValue( HashField::threshold() - 1 ) );
+
+ $this->populateHashField->expects( $this->atLeastOnce() )
+ ->method( 'populate' );
+
+ $this->populateHashField->expects( $this->once() )
+ ->method( 'fetchRows' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $instance = new HashField(
+ $this->store,
+ $this->populateHashField
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->check();
+
+ $this->assertContains(
+ 'Checking smw_hash field consistency',
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testCheck_Incomplete() {
+
+ $resultWrapper = $this->getMockBuilder( '\ResultWrapper' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resultWrapper->expects( $this->once() )
+ ->method( 'numRows' )
+ ->will( $this->returnValue( HashField::threshold() + 1 ) );
+
+ $this->populateHashField->expects( $this->atLeastOnce() )
+ ->method( 'setComplete' );
+
+ $this->populateHashField->expects( $this->once() )
+ ->method( 'fetchRows' )
+ ->will( $this->returnValue( $resultWrapper ) );
+
+ $instance = new HashField(
+ $this->store,
+ $this->populateHashField
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->check();
+
+ $this->assertContains(
+ 'Checking smw_hash field consistency',
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/FieldTypeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/FieldTypeTest.php
new file mode 100644
index 00000000..c32616d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/FieldTypeTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\FieldType;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\FieldType
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class FieldTypeTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider fieldTypeProvider
+ */
+ public function testMapType( $fieldType, $fieldTypes, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ FieldType::mapType( $fieldType, $fieldTypes )
+ );
+ }
+
+ public function fieldTypeProvider() {
+
+ $fieldTypes = [
+ 'double' => 'DOUBLE'
+ ];
+
+ $provider[] = [
+ FieldType::TYPE_DOUBLE,
+ $fieldTypes,
+ 'DOUBLE'
+ ];
+
+ $provider[] = [
+ [ FieldType::TYPE_DOUBLE ],
+ $fieldTypes,
+ 'DOUBLE'
+ ];
+
+ $provider[] = [
+ [ FieldType::TYPE_DOUBLE, 'NOT NULL' ],
+ $fieldTypes,
+ 'DOUBLE NOT NULL'
+ ];
+
+ $provider[] = [
+ [ FieldType::TYPE_DOUBLE, 'NOT NULL', 'thirdParameterIsNeglected' ],
+ $fieldTypes,
+ 'DOUBLE NOT NULL'
+ ];
+
+ $provider[] = [
+ [ 'notMatchableTypeThereforeReturnAsIs' ],
+ $fieldTypes,
+ 'notMatchableTypeThereforeReturnAsIs'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/MySQLTableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/MySQLTableBuilderTest.php
new file mode 100644
index 00000000..4babb85b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/MySQLTableBuilderTest.php
@@ -0,0 +1,396 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\MySQLTableBuilder;
+use SMW\SQLStore\TableBuilder\Table;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\MySQLTableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MySQLTableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query', 'dbSchema', 'tablePrefix' ] )
+ ->getMockForAbstractClass();
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'dbSchema' )
+ ->will( $this->returnValue( '' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tablePrefix' )
+ ->will( $this->returnValue( '' ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ MySQLTableBuilder::class,
+ MySQLTableBuilder::factory( $this->connection )
+ );
+ }
+
+ public function testCreateNewTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->equalTo( 'CREATE TABLE `xyz`."foo" (bar TEXT) tableoptions_foobar' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+ $instance->addConfig( 'wgDBname', 'xyz' );
+ $instance->addConfig( 'wgDBTableOptions', 'tableoptions_foobar' );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateNewTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE TABLE `xyz`."foo"' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+ $instance->addConfig( 'wgDBname', 'xyz' );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateExistingTableWithNewField() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DESCRIBE' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text FIRST' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateExistingTableWithNewField_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DESCRIBE' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text FIRST' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateExistingTableWithNewFieldAndDefault() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DESCRIBE' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' . " DEFAULT '0'" . ' FIRST' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateExistingTableWithNewFieldAndDefault_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DESCRIBE' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' . " DEFAULT '0'" . ' FIRST' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SHOW INDEX' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD INDEX (bar)' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->at( 7 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SHOW INDEX' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 10 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD INDEX (bar)' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testDropTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE "foo"' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testDropTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE "foo"' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testOptimizeTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'query' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $connection->expects( $this->at( 1 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE TABLE "foo"' ) );
+
+ $connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'OPTIMIZE TABLE "foo"' ) );
+
+ $instance = MySQLTableBuilder::factory( $connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+ public function testOptimizeTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE TABLE "foo"' ) );
+
+ $this->connection->expects( $this->at( 6 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'OPTIMIZE TABLE "foo"' ) );
+
+ $instance = MySQLTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/PostgresTableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/PostgresTableBuilderTest.php
new file mode 100644
index 00000000..9211a2d3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/PostgresTableBuilderTest.php
@@ -0,0 +1,385 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\PostgresTableBuilder;
+use SMW\SQLStore\TableBuilder\Table;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\PostgresTableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class PostgresTableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query', 'dbSchema', 'tablePrefix', 'onTransactionIdle' ] )
+ ->getMockForAbstractClass();
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'postgres' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'dbSchema' )
+ ->will( $this->returnValue( '' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tablePrefix' )
+ ->will( $this->returnValue( '' ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ PostgresTableBuilder::class,
+ PostgresTableBuilder::factory( $this->connection )
+ );
+ }
+
+ public function testCreateTableOnNewTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE TABLE' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateTableOnNewTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE TABLE' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewField() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT a.attname as' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD "bar" TEXT' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewField_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT a.attname as' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD "bar" TEXT' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewFieldAndDefault() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT a.attname as' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD "bar" TEXT'. " DEFAULT '0'" ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewFieldAndDefault_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT a.attname as' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD "bar" TEXT'. " DEFAULT '0'" ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indexInfo' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT i.relname AS indexname' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE INDEX foo_idx_bar ON foo (bar)' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'indexInfo' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->at( 7 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'SELECT i.relname AS indexname' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 11 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE INDEX foo_idx_bar ON foo (bar)' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testDropTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE IF EXISTS "foo"' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testDropTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE IF EXISTS "foo"' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testDoCheckOnAfterCreate() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) { return $callback(); } ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER SEQUENCE' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $instance->checkOn( $instance::POST_CREATION );
+ }
+
+ public function testDoCheckOnAfterCreate_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'onTransactionIdle' )
+ ->will( $this->returnCallback( function( $callback ) { return $callback(); } ) );
+
+ $this->connection->expects( $this->at( 6 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER SEQUENCE' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $instance->checkOn( $instance::POST_CREATION );
+ }
+
+ public function testOptimizeTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'postgres' ) );
+
+ $this->connection->expects( $this->at( 1 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE "foo"' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+ public function testOptimizeTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'postgres' ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE "foo"' ) );
+
+ $instance = PostgresTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/SQLiteTableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/SQLiteTableBuilderTest.php
new file mode 100644
index 00000000..05d31825
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/SQLiteTableBuilderTest.php
@@ -0,0 +1,331 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\SQLiteTableBuilder;
+use SMW\SQLStore\TableBuilder\Table;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\SQLiteTableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SQLiteTableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+
+ protected function setUp() {
+
+ $this->connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'tableExists', 'query', 'dbSchema', 'tablePrefix' ] )
+ ->getMockForAbstractClass();
+
+ $this->connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'sqlite' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'dbSchema' )
+ ->will( $this->returnValue( '' ) );
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tablePrefix' )
+ ->will( $this->returnValue( '' ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SQLiteTableBuilder::class,
+ SQLiteTableBuilder::factory( $this->connection )
+ );
+ }
+
+ public function testCreateTableOnNewTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE TABLE' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateTableOnNewTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE TABLE' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewField() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA table_info("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewField_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA table_info("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewFieldAndDefault() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 2 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA table_info("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' . " DEFAULT '0'" ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testUpdateTableWithNewFieldAndDefault_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA table_info("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 5 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ALTER TABLE "foo" ADD `bar` text' . " DEFAULT '0'" ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addDefault( 'bar', 0 );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA index_list("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 4 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE INDEX "foo"_index0' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testCreateIndex_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->any() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( false ) );
+
+ $this->connection->expects( $this->at( 7 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'PRAGMA index_list("foo")' ) )
+ ->will( $this->returnValue( [] ) );
+
+ $this->connection->expects( $this->at( 10 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'CREATE INDEX "foo"_index0' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $table->addColumn( 'bar', 'text' );
+ $table->addIndex( 'bar' );
+
+ $instance->create( $table );
+ }
+
+ public function testDropTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE "foo"' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testDropTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->once() )
+ ->method( 'tableExists' )
+ ->will( $this->returnValue( true ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'DROP TABLE "foo"' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->drop( $table );
+ }
+
+ public function testOptimizeTable() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '>=' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->at( 1 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE "foo"' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+ public function testOptimizeTable_132() {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.32', '<' ) ) {
+ $this->markTestSkipped( 'MediaWiki changed the Database signature!' );
+ }
+
+ $this->connection->expects( $this->at( 3 ) )
+ ->method( 'query' )
+ ->with( $this->stringContains( 'ANALYZE "foo"' ) );
+
+ $instance = SQLiteTableBuilder::factory( $this->connection );
+
+ $table = new Table( 'foo' );
+ $instance->optimize( $table );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableBuilderTest.php
new file mode 100644
index 00000000..edcdbda7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableBuilderTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\TableBuilder;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\TableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstructForMySQL() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'mysql' ) );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\TableBuilder\MySQLTableBuilder',
+ TableBuilder::factory( $connection )
+ );
+ }
+
+ public function testCanConstructForSQLite() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'sqlite' ) );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\TableBuilder\SQLiteTableBuilder',
+ TableBuilder::factory( $connection )
+ );
+ }
+
+ public function testCanConstructForPostgres() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'postgres' ) );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\TableBuilder\PostgresTableBuilder',
+ TableBuilder::factory( $connection )
+ );
+ }
+
+ public function testTryToConstructOnInvalidTypeThrowsException() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->any() )
+ ->method( 'getType' )
+ ->will( $this->returnValue( 'foo' ) );
+
+ $this->setExpectedException( 'RuntimeException' );
+ TableBuilder::factory( $connection );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableTest.php
new file mode 100644
index 00000000..275fdc55
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TableTest.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\SQLStore\TableBuilder\Table;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\Table
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Table::class,
+ new Table( 'Foo' )
+ );
+ }
+
+ public function testAddColumn() {
+
+ $instance = new Table( 'Foo' );
+
+ $instance->addColumn( 'b', 'integer' );
+
+ $expected = [
+ 'fields' => [
+ 'b' => 'integer'
+ ]
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getAttributes()
+ );
+ }
+
+ public function testAddIndex() {
+
+ $instance = new Table( 'Foo' );
+
+ $instance->addIndex( 'bar' );
+
+ $expected = [
+ 'indices' => [
+ 'bar'
+ ]
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getAttributes()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHash()
+ );
+ }
+
+ public function testAddIndexWithKey() {
+
+ $instance = new Table( 'Foo' );
+
+ $instance->addIndex( [ 'foobar' ], 'bar' );
+
+ $expected = [
+ 'indices' => [
+ 'bar' => [ 'foobar' ]
+ ]
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getAttributes()
+ );
+ }
+
+ public function testAddOption() {
+
+ $instance = new Table( 'Foo' );
+
+ $instance->addOption( 'bar', [ 'foobar' ] );
+
+ $expected = [
+ 'bar' => [ 'foobar' ]
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $instance->getAttributes()
+ );
+
+ $this->assertEquals(
+ [ 'foobar' ],
+ $instance->get( 'bar' )
+ );
+ }
+
+ public function testGetOnUnregsiteredKeyThrowsException() {
+
+ $instance = new Table( 'Foo' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->get( 'bar' );
+ }
+
+ /**
+ * @dataProvider invalidOptionsProvider
+ */
+ public function testAddOptionOnReservedOptionKeyThrowsException( $key ) {
+
+ $instance = new Table( 'Foo' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $instance->addOption( $key, [] );
+ }
+
+ public function invalidOptionsProvider() {
+
+ $provider[] = [
+ 'fields'
+ ];
+
+ $provider[] = [
+ 'indices'
+ ];
+
+ $provider[] = [
+ 'defaults'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TemporaryTableBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TemporaryTableBuilderTest.php
new file mode 100644
index 00000000..826fd685
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableBuilder/TemporaryTableBuilderTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace SMW\Tests\SQLStore\TableBuilder;
+
+use SMW\MediaWiki\Database;
+use SMW\SQLStore\TableBuilder\TemporaryTableBuilder;
+
+/**
+ * @covers \SMW\SQLStore\TableBuilder\TemporaryTableBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TemporaryTableBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ private $connection;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TemporaryTableBuilder::class,
+ new TemporaryTableBuilder( $this->connection )
+ );
+ }
+
+ public function testCreateWithoutAutoCommit() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' );
+
+ $instance = new TemporaryTableBuilder(
+ $this->connection
+ );
+
+ $instance->create( 'Foo' );
+ }
+
+ public function testCreateWithoutAutoCommitOnPostgres() {
+
+ $this->connection->expects( $this->never() )
+ ->method( 'setFlag' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->anything() );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'isType' )
+ ->with( $this->equalTo( 'postgres' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new TemporaryTableBuilder(
+ $this->connection
+ );
+
+ $instance->create( 'Foo' );
+ }
+
+ public function testCreateWithAutoCommitFlag() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'setFlag' )
+ ->with( $this->equalTo( Database::AUTO_COMMIT ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->anything() );
+
+ $instance = new TemporaryTableBuilder(
+ $this->connection
+ );
+
+ $instance->setAutoCommitFlag( true );
+ $instance->create( 'Foo' );
+ }
+
+ public function testDropWithoutAutoCommit() {
+
+ $this->connection->expects( $this->never() )
+ ->method( 'setFlag' );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->anything() );
+
+ $instance = new TemporaryTableBuilder(
+ $this->connection
+ );
+
+ $instance->drop( 'Foo' );
+ }
+
+ public function testDropWithAutoCommitFlag() {
+
+ $this->connection->expects( $this->once() )
+ ->method( 'setFlag' )
+ ->with( $this->equalTo( Database::AUTO_COMMIT ) );
+
+ $this->connection->expects( $this->once() )
+ ->method( 'query' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->anything() );
+
+ $instance = new TemporaryTableBuilder(
+ $this->connection
+ );
+
+ $instance->setAutoCommitFlag( true );
+ $instance->drop( 'Foo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableFieldUpdaterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableFieldUpdaterTest.php
new file mode 100644
index 00000000..07ed3df5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableFieldUpdaterTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\TableFieldUpdater;
+
+/**
+ * @covers \SMW\SQLStore\TableFieldUpdater
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TableFieldUpdaterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ TableFieldUpdater::class,
+ new TableFieldUpdater( $store )
+ );
+ }
+
+ public function testUpdateSortField() {
+
+ $collator = $this->getMockBuilder( '\SMW\MediaWiki\Collator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $collator->expects( $this->once() )
+ ->method( 'getSortKey' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [ 'smw_id' => 42 ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new TableFieldUpdater(
+ $store,
+ $collator
+ );
+
+ $instance->updateSortField( 42, 'Foo' );
+ }
+
+ public function testUpdateRevField() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [ 'smw_rev' => 1001 ] ),
+ $this->equalTo( [ 'smw_id' => 42 ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new TableFieldUpdater(
+ $store
+ );
+
+ $instance->updateRevField( 42, 1001 );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableIntegrityExaminerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableIntegrityExaminerTest.php
new file mode 100644
index 00000000..1c9b561f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableIntegrityExaminerTest.php
@@ -0,0 +1,329 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\Tests\TestEnvironment;
+use SMW\SQLStore\TableIntegrityExaminer;
+
+/**
+ * @covers \SMW\SQLStore\TableIntegrityExaminer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableIntegrityExaminerTest extends \PHPUnit_Framework_TestCase {
+
+ private $spyMessageReporter;
+ private $hashField;
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->spyMessageReporter = TestEnvironment::getUtilityFactory()->newSpyMessageReporter();
+
+ $this->hashField = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder\Examiner\HashField' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ TableIntegrityExaminer::class,
+ new TableIntegrityExaminer( $this->store, $this->hashField )
+ );
+ }
+
+ public function testCheckOnPostCreationOnValidProperty() {
+
+ $row = [
+ 'smw_id' => 42,
+ 'smw_iw' => '',
+ 'smw_proptable_hash' => '',
+ 'smw_hash' => ''
+ ];
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getPropertyInterwiki', 'moveSMWPageID', 'getPropertyTableHashes' ] )
+ ->getMock();
+
+ $idTable->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyInterwiki' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( (object)$row ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'replace' );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'checkOn' );
+
+ $instance = new TableIntegrityExaminer(
+ $store,
+ $this->hashField
+ );
+
+ $instance->setPredefinedPropertyList( [
+ 'Foo' => 42
+ ] );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->checkOnPostCreation( $tableBuilder );
+ }
+
+ public function testCheckOnPostCreationOnValidProperty_NotFixed() {
+
+ $row = [
+ 'smw_id' => 42,
+ 'smw_iw' => '',
+ 'smw_proptable_hash' => '',
+ 'smw_hash' => ''
+ ];
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'moveSMWPageID', 'getPropertyInterwiki' ] )
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->at( 1 ) )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( (object)[ 'smw_id' => \SMW\SQLStore\SQLStore::FIXED_PROPERTY_ID_UPPERBOUND ] ) );
+
+ $connection->expects( $this->at( 2 ) )
+ ->method( 'selectRow' )
+ ->with(
+ $this->anything(),
+ $this->anything(),
+ $this->equalTo( [
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_subobject' => '' ] ) )
+ ->will( $this->returnValue( (object)$row ) );
+
+ $connection->expects( $this->at( 3 ) )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( (object)$row ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'checkOn' );
+
+ $instance = new TableIntegrityExaminer(
+ $store,
+ $this->hashField
+ );
+
+ $instance->setPredefinedPropertyList( [
+ 'Foo' => null
+ ] );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->checkOnPostCreation( $tableBuilder );
+ }
+
+ public function testCheckOnPostCreationOnInvalidProperty() {
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'getPropertyInterwiki', 'moveSMWPageID' ] )
+ ->getMock();
+
+ $idTable->expects( $this->never() )
+ ->method( 'getPropertyInterwiki' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $row ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getObjectIds', 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'checkOn' );
+
+ $instance = new TableIntegrityExaminer(
+ $store,
+ $this->hashField
+ );
+
+ $instance->setPredefinedPropertyList( [
+ '_FOO' => 42
+ ] );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->checkOnPostCreation( $tableBuilder );
+
+ $this->assertContains(
+ 'invalid registration',
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testCheckOnActivitiesPostCreationForID_TABLE() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( false ) );
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'tableName' )
+ ->will( $this->returnValue( 'smw_object_ids' ) );
+
+ $idTable = $this->getMockBuilder( '\stdClass' )
+ ->setMethods( [ 'moveSMWPageID' ] )
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection', 'getObjectIds' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableBuilder->expects( $this->any() )
+ ->method( 'getLog' )
+ ->will( $this->returnValue( [ 'smw_object_ids' => [ 'smw_sort' => 'field.new' ] ] ) );
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'checkOn' );
+
+ $instance = new TableIntegrityExaminer(
+ $store,
+ $this->hashField
+ );
+
+ $instance->setPredefinedPropertyList( [] );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->checkOnPostCreation( $tableBuilder );
+
+ $this->assertContains(
+ 'copying smw_sortkey to smw_sort',
+ $this->spyMessageReporter->getMessagesAsString()
+ );
+ }
+
+ public function testCheckOnPostDestruction() {
+
+ $connection = $this->getMockBuilder( '\DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'listTables' ] )
+ ->getMockForAbstractClass();
+
+ $connection->expects( $this->atLeastOnce() )
+ ->method( 'listTables' )
+ ->will( $this->returnValue( [ 'abcsmw_foo' ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getConnection' ] )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $tableBuilder = $this->getMockBuilder( '\SMW\SQLStore\TableBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'checkOn' );
+
+ $tableBuilder->expects( $this->once() )
+ ->method( 'drop' );
+
+ $instance = new TableIntegrityExaminer(
+ $store,
+ $this->hashField
+ );
+
+ $instance->setMessageReporter( $this->spyMessageReporter );
+ $instance->checkOnPostDestruction( $tableBuilder );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableSchemaManagerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableSchemaManagerTest.php
new file mode 100644
index 00000000..5b795a11
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SQLStore/TableSchemaManagerTest.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use SMW\SQLStore\TableBuilder\FieldType;
+use SMW\SQLStore\TableSchemaManager;
+
+/**
+ * @covers \SMW\SQLStore\TableSchemaManager
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableSchemaManagerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ TableSchemaManager::class,
+ new TableSchemaManager( $store )
+ );
+ }
+
+ public function testGetTablesWithEmptyPropertyTableDefinition() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->once() )
+ ->method( 'getTableFields' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $propertyTableDefinition ] ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new TableSchemaManager(
+ $store
+ );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getTables()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHash()
+ );
+ }
+
+ public function testFindTableDefinitionWithNoCaseFeature() {
+
+ $propertyTableDefinition = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItemHandler = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\DataItemHandler' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItemHandler->expects( $this->once() )
+ ->method( 'getTableFields' )
+ ->will( $this->returnValue( [] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->once() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [ $propertyTableDefinition ] ) );
+
+ $store->expects( $this->once() )
+ ->method( 'getDataItemHandlerForDIType' )
+ ->will( $this->returnValue( $dataItemHandler ) );
+
+ $instance = new TableSchemaManager(
+ $store
+ );
+
+ $instance->setFeatureFlags(
+ SMW_FIELDT_CHAR_NOCASE
+ );
+
+ $table = $instance->findTable( \SMW\SQLStore\SQLStore::ID_TABLE );
+ $fields = $table->get( 'fields' );
+
+ $this->assertContains(
+ FieldType::TYPE_CHAR_NOCASE,
+ $fields['smw_sortkey']
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentFormatterTest.php
new file mode 100644
index 00000000..e3adaad7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentFormatterTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\Schema\Content;
+
+use SMW\Schema\Content\ContentFormatter;
+use SMW\Schema\Schema;
+
+/**
+ * @covers \SMW\Schema\Content\ContentFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ ContentFormatter::class,
+ new ContentFormatter()
+ );
+ }
+
+ public function testGetHelpLink() {
+
+ $schema = $this->getMockBuilder( '\SMW\Schema\Schema' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ContentFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHelpLink( $schema )
+ );
+ }
+
+ public function testGetText() {
+
+ $schema = $this->getMockBuilder( '\SMW\Schema\Schema' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $schema->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnCallback( [ $this, 'schema_get' ] ) );
+
+ $text = '...';
+ $isYaml = false;
+ $errors = [];
+
+ $instance = new ContentFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getText( $text, $isYaml, $schema, $errors )
+ );
+ }
+
+ public function testGetText_Errors() {
+
+ $schema = $this->getMockBuilder( '\SMW\Schema\Schema' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $schema->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnCallback( [ $this, 'schema_get' ] ) );
+
+ $text = '...';
+ $isYaml = false;
+
+ $errors = [
+ [ 'property' => 'foo', 'message' => '---' ]
+ ];
+
+ $instance = new ContentFormatter();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getText( $text, $isYaml, $schema, $errors )
+ );
+ }
+
+ public function schema_get( $key ) {
+ return $key === Schema::SCHEMA_TAG ? [] : '';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentHandlerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentHandlerTest.php
new file mode 100644
index 00000000..3948443b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentHandlerTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace SMW\Tests\Schema\Content;
+
+use SMW\Schema\Content\ContentHandler;
+
+/**
+ * @covers \SMW\Schema\Content\ContentHandler
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentHandlerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ '\JsonContentHandler',
+ new ContentHandler()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentTest.php
new file mode 100644
index 00000000..65acd635
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/ContentTest.php
@@ -0,0 +1,183 @@
+<?php
+
+namespace SMW\Tests\Schema\Content;
+
+use SMW\Schema\Content\Content;
+
+/**
+ * @covers \SMW\Schema\Content\Content
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ '\JsonContent',
+ new Content( 'foo' )
+ );
+ }
+
+ public function testToJson() {
+
+ $text = json_encode( [ 'Foo' => 42 ] );
+
+ $instance = new Content( $text );
+
+ $this->assertEquals(
+ $text,
+ $instance->toJson()
+ );
+ }
+
+ public function testIsYaml() {
+
+ if ( !class_exists( '\Symfony\Component\Yaml\Yaml' ) ) {
+ $this->markTestSkipped( 'Skipping because `Symfony\Component\Yaml\Yaml` is not available!' );
+ }
+
+ $text = json_encode( [ 'Foo' => 42 ] );
+
+ $instance = new Content( $text );
+
+ $this->assertFalse(
+ $instance->isYaml()
+ );
+ }
+
+ public function testPreSaveTransform() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->getMockBuilder( '\User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new Content(
+ json_encode( [ 'Foo' => 42 ] )
+ );
+
+ $this->assertInstanceof(
+ Content::class,
+ $instance->preSaveTransform( $title, $user, $parserOptions )
+ );
+ }
+
+ public function testFillParserOutput() {
+
+ $schemaDefinition = $this->getMockBuilder( '\SMW\Schema\SchemaDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $schemaValidator = $this->getMockBuilder( '\SMW\Schema\SchemaValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $schemaValidator->expects( $this->any() )
+ ->method( 'validate' )
+ ->will( $this->returnValue( [] ) );
+
+ $schemaFactory = $this->getMockBuilder( '\SMW\Schema\SchemaFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $schemaFactory->expects( $this->any() )
+ ->method( 'newSchema' )
+ ->will( $this->returnValue( $schemaDefinition ) );
+
+ $schemaFactory->expects( $this->any() )
+ ->method( 'newSchemaValidator' )
+ ->will( $this->returnValue( $schemaValidator ) );
+
+ $contentFormatter = $this->getMockBuilder( '\SMW\Schema\Content\ContentFormatter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_SCHEMA ) );
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setText' );
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setIndicator' );
+
+ $instance = new Content(
+ json_encode( [ 'Foo' => 42 ] )
+ );
+
+ $instance->setServices( $schemaFactory, $contentFormatter );
+
+ $generateHtml = true;
+ $revId = 42;
+
+ $instance->fillParserOutput( $title, $revId, $parserOptions, $generateHtml, $parserOutput );
+ }
+
+ public function testFillParserOutput_SchemaTypeNotFoundException() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBKey' )
+ ->will( $this->returnValue( 'Foo' ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( SMW_NS_SCHEMA ) );
+
+ $parserOptions = $this->getMockBuilder( '\ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput = $this->getMockBuilder( '\ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOutput->expects( $this->once() )
+ ->method( 'setText' );
+
+ $parserOutput->expects( $this->never() )
+ ->method( 'setIndicator' );
+
+ $instance = new Content(
+ json_encode( [ 'Foo' => 42 ] )
+ );
+
+ $generateHtml = true;
+ $revId = 42;
+
+ $instance->fillParserOutput( $title, $revId, $parserOptions, $generateHtml, $parserOutput );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/HtmlBuilderTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/HtmlBuilderTest.php
new file mode 100644
index 00000000..ef9706e1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Content/HtmlBuilderTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\Tests\Schema\Content;
+
+use SMW\Schema\Content\HtmlBuilder;
+use SMW\Schema\Schema;
+
+/**
+ * @covers \SMW\Schema\Content\HtmlBuilder
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlBuilderTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceof(
+ HtmlBuilder::class,
+ new HtmlBuilder()
+ );
+ }
+
+ /**
+ * @dataProvider buildParamsProvider
+ */
+ public function testBuild( $key, $params ) {
+
+ $instance = new HtmlBuilder();
+
+ $this->assertInternalType(
+ 'string',
+ $instance->build( $key, $params )
+ );
+ }
+
+ public function buildParamsProvider() {
+
+ yield [
+ 'schema_head',
+ [
+ 'link' => 'Foo',
+ 'description' => 'bar',
+ 'schema-title' => '...',
+ 'error' => 'err---rr',
+ 'error-title' => 'error'
+ ]
+ ];
+
+ yield [
+ 'schema_body',
+ [
+ 'text' => 'Foo',
+ 'unknown_type' => 'bar'
+ ]
+ ];
+
+ yield [
+ 'schema_error_text',
+ [
+ 'list' => [],
+ 'schema' => 'Foo'
+ ]
+ ];
+
+ yield [
+ 'schema_error',
+ [
+ 'text' => '...',
+ 'msg' => 'Foo'
+ ]
+ ];
+
+ yield [
+ 'schema_footer',
+ [
+ 'href_type' => '...',
+ 'link_type' => 'Foo',
+ 'msg_type' => 'Bar',
+ 'tags' => [],
+ 'href_tag' => 'Foobar'
+ ]
+ ];
+
+ yield [
+ 'schema_unknown_type',
+ [
+ 'msg' => 'Foo'
+ ]
+ ];
+
+ yield [
+ 'schema_help_link',
+ [
+ 'href' => 'Foo'
+ ]
+ ];
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaConstructionFailedExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaConstructionFailedExceptionTest.php
new file mode 100644
index 00000000..fd38438d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaConstructionFailedExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Schema\Exception;
+
+use SMW\Schema\Exception\SchemaConstructionFailedException;
+
+/**
+ * @covers \SMW\Schema\Exception\SchemaConstructionFailedException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaConstructionFailedExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SchemaConstructionFailedException( 'foo' );
+
+ $this->assertInstanceof(
+ SchemaConstructionFailedException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaTypeNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaTypeNotFoundExceptionTest.php
new file mode 100644
index 00000000..5be5da8a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/Exception/SchemaTypeNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Schema\Exception;
+
+use SMW\Schema\Exception\SchemaTypeNotFoundException;
+
+/**
+ * @covers \SMW\Schema\Exception\SchemaTypeNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaTypeNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SchemaTypeNotFoundException( 'foo' );
+
+ $this->assertInstanceof(
+ SchemaTypeNotFoundException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\RuntimeException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaDefinitionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaDefinitionTest.php
new file mode 100644
index 00000000..3e20208c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaDefinitionTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests\Schema;
+
+use SMW\Schema\SchemaDefinition;
+
+/**
+ * @covers \SMW\Schema\SchemaDefinition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaDefinitionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new SchemaDefinition( 'foo', [] );
+
+ $this->assertInstanceof(
+ SchemaDefinition::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\SMW\Schema\Schema',
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\JsonSerializable',
+ $instance
+ );
+ }
+
+ public function testGetName() {
+
+ $instance = new SchemaDefinition(
+ 'foo',
+ []
+ );
+
+ $this->assertEquals(
+ 'foo',
+ $instance->getName()
+ );
+ }
+
+ public function testGetSchemaLink() {
+
+ $instance = new SchemaDefinition(
+ 'foo',
+ [],
+ 'BAR'
+ );
+
+ $this->assertEquals(
+ 'BAR',
+ $instance->getValidationSchema()
+ );
+ }
+
+ public function testGet() {
+
+ $def = [
+ 'type' => 'foo_bar',
+ 'description' => 'bar foo bar',
+ 'Schema' => [
+ 'if' => [
+ 'doSomething',
+ 'and' => [
+ 'doSomethingElse'
+ ]
+ ],
+ 'then' => [
+ ]
+ ]
+ ];
+
+ $instance = new SchemaDefinition(
+ 'foo',
+ $def
+ );
+
+ $this->assertEquals(
+ 'foo_bar',
+ $instance->get( SchemaDefinition::SCHEMA_TYPE )
+ );
+
+ $this->assertEquals(
+ [ 'doSomething', 'and' => [ 'doSomethingElse' ] ],
+ $instance->get( 'Schema.if' )
+ );
+
+ $this->assertEquals(
+ [ 'doSomethingElse' ],
+ $instance->get( 'Schema.if.and' )
+ );
+ }
+
+ public function testJsonSerialize() {
+
+ $def = [
+ 'type' => 'foo_bar',
+ 'description' => 'bar foo bar',
+ 'Schema' => [
+ 'if' => [
+ 'doSomething',
+ 'and' => [
+ 'doSomethingElse'
+ ]
+ ],
+ 'then' => [
+ ]
+ ]
+ ];
+
+ $instance = new SchemaDefinition(
+ 'foo',
+ $def
+ );
+
+ $this->assertEquals(
+ json_encode( $def ),
+ $instance->jsonSerialize()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaFactoryTest.php
new file mode 100644
index 00000000..b0dc7562
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaFactoryTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Tests\Schema;
+
+use SMW\Schema\SchemaFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Schema\SchemaFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $instance = new SchemaFactory();
+
+ $this->assertInstanceof(
+ SchemaFactory::class,
+ $instance
+ );
+ }
+
+ public function testCanConstructSchemaValidator() {
+
+ $instance = new SchemaFactory();
+
+ $this->assertInstanceof(
+ '\SMW\Schema\SchemaValidator',
+ $instance->newSchemaValidator()
+ );
+ }
+
+ public function testIsRegisteredType() {
+
+ $instance = new SchemaFactory(
+ [
+ 'foo' => []
+ ]
+ );
+
+ $this->assertTrue(
+ $instance->isRegisteredType( 'foo' )
+ );
+ }
+
+ public function testGetRegisteredTypes() {
+
+ $instance = new SchemaFactory(
+ [
+ 'foo' => [],
+ 'bar' => []
+ ]
+ );
+
+ $this->assertEquals(
+ [ 'foo', 'bar' ],
+ $instance->getRegisteredTypes()
+ );
+ }
+
+ public function testGetRegisteredTypesByGroup() {
+
+ $instance = new SchemaFactory(
+ [
+ 'foo' => [ 'group' => 'f_group' ],
+ 'bar' => [ 'group' => 'b_group' ]
+ ]
+ );
+
+ $this->assertEquals(
+ [ 'foo' ],
+ $instance->getRegisteredTypesByGroup( 'f_group' )
+ );
+ }
+
+ public function testNewSchemaDefinition() {
+
+ $instance = new SchemaFactory(
+ [
+ 'foo' => [ 'group' => 'f_group' ]
+ ]
+ );
+
+ $this->assertInstanceof(
+ '\SMW\Schema\SchemaDefinition',
+ $instance->newSchema( 'foo_bar', [ 'type' => 'foo' ] )
+ );
+ }
+
+ public function testNewSchemaDefinitionOnUnknownTypeThrowsException() {
+
+ $instance = new SchemaFactory();
+
+ $this->setExpectedException( '\SMW\Schema\Exception\SchemaTypeNotFoundException' );
+ $instance->newSchema( 'foo_bar', [ 'type' => 'foo' ] );
+ }
+
+ public function testNewSchemaDefinitionOnNoTypeThrowsException() {
+
+ $instance = new SchemaFactory(
+ [
+ 'foo' => [ 'group' => 'f_group' ]
+ ]
+ );
+
+ $this->setExpectedException( '\SMW\Schema\Exception\SchemaTypeNotFoundException' );
+ $instance->newSchema( 'foo_bar', [] );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaValidatorTest.php
new file mode 100644
index 00000000..0f81f50d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Schema/SchemaValidatorTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Schema;
+
+use SMW\DataItemFactory;
+use SMW\Schema\SchemaValidator;
+use SMW\Schema\SchemaDefinition;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMW\Schema\SchemaValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SchemaValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $jsonSchemaValidator = $this->getMockBuilder( '\SMW\Utils\JsonSchemaValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ SchemaValidator::class,
+ new SchemaValidator( $jsonSchemaValidator )
+ );
+ }
+
+ public function testValidate_IsValid() {
+
+ $jsonSchemaValidator = $this->getMockBuilder( '\SMW\Utils\JsonSchemaValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jsonSchemaValidator->expects( $this->once() )
+ ->method( 'validate' )
+ ->will( $this->returnValue( false ) );
+
+ $jsonSchemaValidator->expects( $this->once() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new SchemaValidator( $jsonSchemaValidator );
+
+ $this->assertEmpty(
+ $instance->validate( new SchemaDefinition( 'foo', [], '...' ) )
+ );
+ }
+
+ public function testValidate_Error() {
+
+ $jsonSchemaValidator = $this->getMockBuilder( '\SMW\Utils\JsonSchemaValidator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jsonSchemaValidator->expects( $this->once() )
+ ->method( 'validate' )
+ ->will( $this->returnValue( false ) );
+
+ $jsonSchemaValidator->expects( $this->once() )
+ ->method( 'isValid' )
+ ->will( $this->returnValue( false ) );
+
+ $jsonSchemaValidator->expects( $this->once() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [ '...' ] ) );
+
+ $instance = new SchemaValidator( $jsonSchemaValidator );
+
+ $this->assertNotEmpty(
+ $instance->validate( new SchemaDefinition( 'foo', [], '...' ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SerializerFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SerializerFactoryTest.php
new file mode 100644
index 00000000..149dbec3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SerializerFactoryTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\SerializerFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\SerializerFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SerializerFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\SerializerFactory',
+ new SerializerFactory()
+ );
+ }
+
+ public function testCanConstructSemanticDataSerializer() {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\SemanticDataSerializer',
+ $instance->newSemanticDataSerializer()
+ );
+ }
+
+ public function testCanConstructSemanticDataDeserializer() {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\SemanticDataDeserializer',
+ $instance->newSemanticDataDeserializer()
+ );
+ }
+
+ public function testCanConstructQueryResultSerializer() {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\QueryResultSerializer',
+ $instance->newQueryResultSerializer()
+ );
+ }
+
+ public function testCanConstructExpDataSerializer() {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\ExpDataSerializer',
+ $instance->newExpDataSerializer()
+ );
+ }
+
+ public function testCanConstructExpDataDeserializer() {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\SMW\Deserializers\ExpDataDeserializer',
+ $instance->newExpDataDeserializer()
+ );
+ }
+
+ /**
+ * @dataProvider objectToSerializerProvider
+ */
+ public function testGetSerializerFor( $object ) {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\Serializers\Serializer',
+ $instance->getSerializerFor( $object )
+ );
+ }
+
+ /**
+ * @dataProvider serializationToDeserializerProvider
+ */
+ public function testGetDeserializerFor( $serialization ) {
+
+ $instance = new SerializerFactory();
+
+ $this->assertInstanceOf(
+ '\Deserializers\Deserializer',
+ $instance->getDeserializerFor( $serialization )
+ );
+ }
+
+ public function testGetSerializerForUnregisteredSerializerThrowsException() {
+
+ $instance = new SerializerFactory();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->getSerializerFor( 'Foo' );
+ }
+
+ public function testGetDeserializerForUnregisteredSerializerThrowsException() {
+
+ $instance = new SerializerFactory();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->getDeserializerFor( [ 'Foo' ] );
+ }
+
+ public function objectToSerializerProvider() {
+
+ #0
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $semanticData
+ ];
+
+ #1
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $queryResult
+ ];
+
+ #2
+ $queryResult = $this->getMockBuilder( '\SMWExpData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ $queryResult
+ ];
+
+ return $provider;
+ }
+
+ public function serializationToDeserializerProvider() {
+
+ $provider = [];
+
+ #0
+ $provider[] = [
+ [ 'serializer' => 'SMW\Serializers\SemanticDataSerializer', 'subject' => 'Foo#0##' ]
+ ];
+
+ #1
+ $provider[] = [
+ [ 'serializer' => 'SMW\Serializers\ExpDataSerializer' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/ExpDataSerializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/ExpDataSerializerTest.php
new file mode 100644
index 00000000..9e38a8d7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/ExpDataSerializerTest.php
@@ -0,0 +1,174 @@
+<?php
+
+namespace SMW\Tests\Serializers;
+
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMW\Serializers\ExpDataSerializer;
+use SMWDIBlob as DIBlob;
+use SMWExpData as ExpData;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Serializers\ExpDataSerializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpDataSerializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\ExpDataSerializer',
+ new ExpDataSerializer()
+ );
+ }
+
+ public function testInvalidSerializerObjectThrowsException() {
+
+ $instance = new ExpDataSerializer();
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+ $instance->serialize( 'Foo' );
+ }
+
+ /**
+ * @dataProvider expDataProvider
+ */
+ public function testSerialize( $data, $expected ) {
+
+ $instance = new ExpDataSerializer();
+
+ $this->assertEquals(
+ $expected,
+ $instance->serialize( $data )
+ );
+ }
+
+ public function expDataProvider() {
+
+ #0
+ $expData = new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', null ) );
+
+ $provider[] = [
+ $expData,
+ [
+ 'subject' => [
+ 'type' => 1,
+ 'uri' => 'Foo|Bar|Mo',
+ 'dataitem' => null
+ ],
+ 'data' => [],
+ 'serializer' => 'SMW\Serializers\ExpDataSerializer',
+ 'version' => 0.1
+ ]
+ ];
+
+ #1
+ $expData = new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', null ) );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $provider[] = [
+ $expData,
+ [
+ 'subject' => [
+ 'type' => 1,
+ 'uri' => 'Foo|Bar|Mo',
+ 'dataitem' => null
+ ],
+ 'data' => [
+ 'LaLi' => [
+ 'property' => [
+ 'type' => 1,
+ 'uri' => 'Li|La|Lu',
+ 'dataitem' => null
+ ],
+ 'children' => [
+ [
+ 'type' => 2,
+ 'lexical' => 'Foo',
+ 'datatype' => 'Bar',
+ 'lang' => '',
+ 'dataitem' => null
+ ]
+ ]
+ ]
+ ],
+ 'serializer' => 'SMW\Serializers\ExpDataSerializer',
+ 'version' => 0.1
+ ]
+ ];
+
+ #2 Nested
+ $expData = new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', null ) );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', new DIBlob( 'SomeText' ) ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpData( new ExpNsResource( 'Foo', 'Bar', 'Mo', new DIBlob( 'SomeOtherText' ) ) )
+ );
+
+ $provider[] = [
+ $expData,
+ [
+ 'subject' => [
+ 'type' => 1,
+ 'uri' => 'Foo|Bar|Mo',
+ 'dataitem' => null
+ ],
+ 'data' => [
+ 'LaLi' => [
+ 'property' => [
+ 'type' => 1,
+ 'uri' => 'Li|La|Lu',
+ 'dataitem' => [ // DIBlob
+ 'type' => 2,
+ 'item' => 'SomeText'
+ ]
+ ],
+ 'children' => [
+ [ // ExpLiteral
+ 'type' => 2,
+ 'lexical' => 'Foo',
+ 'datatype' => 'Bar',
+ 'lang' => '',
+ 'dataitem' => null
+ ],
+ [ // ExpData
+ 'subject' => [
+ 'type' => 1,
+ 'uri' => 'Foo|Bar|Mo',
+ 'dataitem' => [
+ 'type' => 2,
+ 'item' => 'SomeOtherText'
+ ]
+ ],
+ 'data' => []
+ ]
+ ]
+ ]
+ ],
+ 'serializer' => 'SMW\Serializers\ExpDataSerializer',
+ 'version' => 0.1
+ ]
+ ];
+
+ return $provider;
+ }
+
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/QueryResultSerializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/QueryResultSerializerTest.php
new file mode 100644
index 00000000..c468b25e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/QueryResultSerializerTest.php
@@ -0,0 +1,286 @@
+<?php
+
+namespace SMW\Tests\Serializers;
+
+use SMW\DataItemFactory;
+use SMW\Serializers\QueryResultSerializer;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\CoreMockObjectRepository;
+use SMW\Tests\Utils\Mock\MediaWikiMockObjectRepository;
+use SMW\Tests\Utils\Mock\MockObjectBuilder;
+use SMWDataItem as DataItem;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Serializers\QueryResultSerializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class QueryResultSerializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\QueryResultSerializer',
+ new QueryResultSerializer()
+ );
+ }
+
+ public function testSerializeOutOfBoundsException() {
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+
+ $instance = new QueryResultSerializer();
+ $instance->serialize( 'Foo' );
+ }
+
+ /**
+ * @dataProvider numberDataProvider
+ */
+ public function testQueryResultSerializerOnMock( $setup, $expected ) {
+
+ $instance = new QueryResultSerializer();
+ $results = $instance->serialize( $setup['queryResult'] );
+
+ $this->assertInternalType(
+ 'array',
+ $results
+ );
+
+ $this->assertEquals(
+ $expected['printrequests'],
+ $results['printrequests']
+ );
+ }
+
+ public function testQueryResultSerializerForRecordType() {
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIProperty( 'Foobar' ) ] ) );
+
+ $semanticData->expects( $this->atLeastOnce() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $this->dataItemFactory->newDIWikiPage( 'Bar', NS_MAIN ) ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getSemanticData' )
+ ->will( $this->returnValue( $semanticData ) );
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'BarList1;BarList2' ) ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $property = \SMW\DIProperty::newFromUserLabel( 'Foo' );
+ $property->setPropertyTypeId( '_rec' );
+
+ $printRequestFactory = new \SMW\Query\PrintRequestFactory();
+
+ $serialization = QueryResultSerializer::getSerialization(
+ \SMW\DIWikiPage::newFromText( 'ABC' ),
+ $printRequestFactory->newFromProperty( $property )
+ );
+
+ $expected = [
+ 'BarList1' => [
+ 'label' => 'BarList1',
+ 'typeid' => '_wpg',
+ 'item' => [],
+ 'key' => 'BarList1'
+ ],
+ 'BarList2' => [
+ 'label' => 'BarList2',
+ 'typeid' => '_wpg',
+ 'item' => [],
+ 'key' => 'BarList2'
+ ]
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $serialization
+ );
+ }
+
+ public function testSerializeFormatForTimeValue() {
+
+ $property = \SMW\DIProperty::newFromUserLabel( 'Foo' );
+ $property->setPropertyTypeId( '_dat' );
+
+ $printRequestFactory = new \SMW\Query\PrintRequestFactory();
+
+ $serialization = QueryResultSerializer::getSerialization(
+ \SMWDITime::doUnserialize( '2/1393/1/1' ),
+ $printRequestFactory->newFromProperty( $property )
+ );
+
+ $expected = [
+ 'timestamp' => '-18208281600',
+ 'raw' => '2/1393/1/1'
+ ];
+
+ $this->assertEquals(
+ $expected,
+ $serialization
+ );
+ }
+
+ public function testQueryResultSerializerOnMockOnDIWikiPageNonTitle() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItem = $this->newMockBuilder()->newObject( 'DataItem', [
+ 'getDIType' => DataItem::TYPE_WIKIPAGE,
+ 'getTitle' => null
+ ] );
+
+ $queryResult = $this->newMockBuilder()->newObject( 'QueryResult', [
+ 'getPrintRequests' => [],
+ 'getResults' => [ $dataItem ],
+ 'getQuery' => $query
+ ] );
+
+ $instance = new QueryResultSerializer();
+ $results = $instance->serialize( $queryResult );
+
+ $this->assertInternalType( 'array', $results );
+ $this->assertEmpty( $results['printrequests'] );
+ $this->assertEmpty( $results['results'] );
+ }
+
+ /**
+ * @return array
+ */
+ public function numberDataProvider() {
+
+ $provider = [];
+
+ $setup = [
+ [ 'printRequest' => 'Foo-1', 'typeId' => '_num', 'number' => 10, 'dataValue' => 'Quuey' ],
+ [ 'printRequest' => 'Foo-2', 'typeId' => '_num', 'number' => 20, 'dataValue' => 'Vey' ],
+ ];
+
+ $provider[] = [
+ [
+ 'queryResult' => $this->buildMockQueryResult( $setup )
+ ],
+ [
+ 'printrequests' => [
+ [ 'label' => 'Foo-1', 'typeid' => '_num', 'mode' => 2, 'format' => false, 'key' => '', 'redi' => '' ],
+ [ 'label' => 'Foo-2', 'typeid' => '_num', 'mode' => 2, 'format' => false, 'key' => '', 'redi' => '' ]
+ ],
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return QueryResult
+ */
+ private function buildMockQueryResult( $setup ) {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequests = [];
+ $resultArray = [];
+ $getResults = [];
+
+ foreach ( $setup as $value ) {
+
+ $printRequest = $this->newMockBuilder()->newObject( 'PrintRequest', [
+ 'getText' => $value['printRequest'],
+ 'getLabel' => $value['printRequest'],
+ 'getTypeID' => $value['typeId'],
+ 'getOutputFormat' => false
+ ] );
+
+ $printRequests[] = $printRequest;
+ $getResults[] = \SMW\DIWikiPage::newFromTitle( new \Title( NS_MAIN, $value['printRequest'] ) );
+
+ $dataItem = $this->newMockBuilder()->newObject( 'DataItem', [
+ 'getDIType' => DataItem::TYPE_NUMBER,
+ 'getNumber' => $value['number']
+ ] );
+
+ $dataValue = $this->newMockBuilder()->newObject( 'DataValue', [
+ 'DataValueType' => 'SMWNumberValue',
+ 'getTypeID' => '_num',
+ 'getShortWikiText' => $value['dataValue'],
+ 'getDataItem' => $dataItem
+ ] );
+
+ $resultArray[] = $this->newMockBuilder()->newObject( 'ResultArray', [
+ 'getText' => $value['printRequest'],
+ 'getPrintRequest' => $printRequest,
+ 'getNextDataValue' => $dataValue,
+ 'getNextDataItem' => $dataItem,
+ 'getContent' => $dataItem
+ ] );
+
+ }
+
+ $queryResult = $this->newMockBuilder()->newObject( 'QueryResult', [
+ 'getPrintRequests' => $printRequests,
+ 'getNext' => $resultArray,
+ 'getResults' => $getResults,
+ 'getQuery' => $query,
+ 'getStore' => $this->newMockBuilder()->newObject( 'Store' ),
+ 'getLink' => new \SMWInfolink( true, 'Lala', 'Lula' ),
+ 'hasFurtherResults' => true
+ ] );
+
+ return $queryResult;
+ }
+
+ private function newMockBuilder() {
+
+ $builder = new MockObjectBuilder();
+ $builder->registerRepository( new CoreMockObjectRepository() );
+ $builder->registerRepository( new MediaWikiMockObjectRepository() );
+
+ return $builder;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/SemanticDataSerializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/SemanticDataSerializerTest.php
new file mode 100644
index 00000000..2a24cab0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Serializers/SemanticDataSerializerTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\Serializers;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\Serializers\SemanticDataSerializer;
+use SMW\Subobject;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Serializers\SemanticDataSerializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataSerializerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $dataValueFactory;
+ private $semanticDataFactory;
+
+ public function testCanConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\Serializers\SemanticDataSerializer',
+ new SemanticDataSerializer()
+ );
+ }
+
+ public function testInvalidSerializerObjectThrowsException() {
+
+ $this->setExpectedException( 'OutOfBoundsException' );
+
+ $instance = new SemanticDataSerializer();
+ $instance->serialize( 'Foo' );
+ }
+
+ /**
+ * @dataProvider semanticDataProvider
+ */
+ public function testSerializerDeserializerRountrip( $data ) {
+
+ $instance = new SemanticDataSerializer();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->serialize( $data )
+ );
+ }
+
+ public function semanticDataProvider() {
+
+ // Is a dataprovider therefore can't use the setUp
+ $this->semanticDataFactory = UtilityFactory::getInstance()->newSemanticDataFactory();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+
+ $title = Title::newFromText( 'Foo' );
+
+ #0 Empty container
+ $foo = $this->semanticDataFactory->setSubject( DIWikiPage::newFromTitle( $title ) )->newEmptySemanticData();
+ $provider[] = [ $foo ];
+
+ #1 Single entry
+ $foo = $this->semanticDataFactory->setSubject( DIWikiPage::newFromTitle( $title ) )->newEmptySemanticData();
+ $foo->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $provider[] = [ $foo ];
+
+ // #2 Single + single subobject entry
+ $foo = $this->semanticDataFactory->setSubject( DIWikiPage::newFromTitle( $title ) )->newEmptySemanticData();
+ $foo->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+ $subobject->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has subobjects', 'Bam' ) );
+
+ $foo->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $provider[] = [ $foo ];
+
+ #3 Multiple entries
+ $foo = $this->semanticDataFactory->setSubject( DIWikiPage::newFromTitle( $title ) )->newEmptySemanticData();
+ $foo->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has fooQuex', 'Bar' ) );
+ $foo->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has queez', 'Xeey' ) );
+
+ $subobject = new Subobject( $title );
+ $subobject->setSemanticData( 'Foo' );
+ $subobject->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has subobjects', 'Bam' ) );
+ $subobject->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has fooQuex', 'Fuz' ) );
+
+ $subobject->setSemanticData( 'Bar' );
+ $subobject->addDataValue( $this->dataValueFactory->newDataValueByText( 'Has fooQuex', 'Fuz' ) );
+
+ $foo->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $provider[] = [ $foo ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServiceFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServiceFactoryTest.php
new file mode 100644
index 00000000..58f3a8ad
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServiceFactoryTest.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use SMW\Services\DataValueServiceFactory;
+
+/**
+ * @covers \SMW\Services\DataValueServiceFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataValueServiceFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $containerBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->containerBuilder = $this->getMockBuilder( '\Onoi\CallbackContainer\ContainerBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ DataValueServiceFactory::class,
+ new DataValueServiceFactory( $this->containerBuilder )
+ );
+ }
+
+ public function testGetServiceFile() {
+
+ $this->assertInternalType(
+ 'string',
+ DataValueServiceFactory::SERVICE_FILE
+ );
+ }
+
+ public function testNewDataValueByType() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->containerBuilder->expects( $this->once() )
+ ->method( 'isRegistered' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_INSTANCE . 'foo' ) )
+ ->will( $this->returnValue( true ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->newDataValueByType( 'foo', 'bar' );
+ }
+
+ public function testGetDataValueFactory() {
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DataValueFactory',
+ $instance->getDataValueFactory()
+ );
+ }
+
+ public function testGetValueParser() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->containerBuilder->expects( $this->once() )
+ ->method( 'singleton' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_PARSER ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->getValueParser( $dataValue );
+ }
+
+ public function testGetValueFormatterOnRegisteredFormatters() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->containerBuilder->expects( $this->once() )
+ ->method( 'isRegistered' )
+ ->will( $this->returnValue( true ) );
+
+ $this->containerBuilder->expects( $this->once() )
+ ->method( 'singleton' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_FORMATTER ) )
+ ->will( $this->returnValue( $dataValueFormatter ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->getValueFormatter( $dataValue );
+ }
+
+ public function testGetValueFormatterOnNonRegisteredFormatters() {
+
+ $dataValueFormatter = $this->getMockBuilder( '\SMW\DataValues\ValueFormatters\DataValueFormatter' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->containerBuilder->expects( $this->once() )
+ ->method( 'isRegistered' )
+ ->will( $this->returnValue( false ) );
+
+ $this->containerBuilder->expects( $this->atLeastOnce() )
+ ->method( 'singleton' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_FORMATTER ) )
+ ->will( $this->returnValue( $dataValueFormatter ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->getValueFormatter( $dataValue );
+ }
+
+ public function testImportExtraneousFunctions() {
+
+ $this->containerBuilder->expects( $this->atLeastOnce() )
+ ->method( 'registerCallback' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_EXT_FUNCTION . 'Foo' ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->importExtraneousFunctions( [
+ 'Foo' => function() { return 'Foo'; }
+ ] );
+ }
+
+ public function testNewExtraneousFunctionByName() {
+
+ $this->containerBuilder->expects( $this->atLeastOnce() )
+ ->method( 'create' )
+ ->with( $this->stringContains( DataValueServiceFactory::TYPE_EXT_FUNCTION . 'Foo' ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->newExtraneousFunctionByName( 'Foo' );
+ }
+
+ public function testGetPropertyRestrictionExaminer() {
+
+ $propertyRestrictionExaminer = $this->getMockBuilder( '\SMW\PropertyRestrictionExaminer' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->containerBuilder->expects( $this->atLeastOnce() )
+ ->method( 'singleton' )
+ ->with( $this->stringContains( 'PropertyRestrictionExaminer' ) )
+ ->will( $this->returnValue( $propertyRestrictionExaminer ) );
+
+ $instance = new DataValueServiceFactory(
+ $this->containerBuilder
+ );
+
+ $instance->getPropertyRestrictionExaminer();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServicesContainerBuildTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServicesContainerBuildTest.php
new file mode 100644
index 00000000..383d4522
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/DataValueServicesContainerBuildTest.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use Onoi\CallbackContainer\CallbackContainerFactory;
+use SMW\DataValues\AllowsListValue;
+use SMW\DataValues\AllowsPatternValue;
+use SMW\DataValues\MonolingualTextValue;
+use SMW\DataValues\ReferenceValue;
+use SMW\DataValues\StringValue;
+use SMW\DataValues\ValueFormatters\CodeStringValueFormatter;
+use SMW\DataValues\ValueFormatters\MonolingualTextValueFormatter;
+use SMW\DataValues\ValueFormatters\NumberValueFormatter;
+use SMW\DataValues\ValueFormatters\PropertyValueFormatter;
+use SMW\DataValues\ValueFormatters\ReferenceValueFormatter;
+use SMW\DataValues\ValueFormatters\StringValueFormatter;
+use SMW\DataValues\ValueFormatters\TimeValueFormatter;
+use SMW\DataValues\ValueParsers\AllowsListValueParser;
+use SMW\DataValues\ValueParsers\AllowsPatternValueParser;
+use SMW\DataValues\ValueParsers\MonolingualTextValueParser;
+use SMW\DataValues\ValueParsers\PropertyValueParser;
+use SMW\DataValues\ValueValidators\CompoundConstraintValueValidator;
+use SMW\Services\DataValueServiceFactory;
+use SMW\Settings;
+use SMWNumberValue as NumberValue;
+use SMWPropertyValue as PropertyValue;
+use SMWTimeValue as TimeValue;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class DataValueServicesContainerBuildTest extends \PHPUnit_Framework_TestCase {
+
+ private $callbackContainerFactory;
+ private $servicesFileDir;
+ private $mediaWikiNsContentReader;
+ private $propertySpecificationLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->mediaWikiNsContentReader = $this->getMockBuilder( '\SMW\MediaWiki\MediaWikiNsContentReader' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->callbackContainerFactory = new CallbackContainerFactory();
+ $this->servicesFileDir = $GLOBALS['smwgServicesFileDir'];
+ }
+
+ /**
+ * @dataProvider servicesProvider
+ */
+ public function testCanConstruct( $service, $parameters, $expected ) {
+
+ array_unshift( $parameters, $service );
+
+ $containerBuilder = $this->callbackContainerFactory->newCallbackContainerBuilder();
+
+ $containerBuilder->registerObject( 'Settings', new Settings( [
+ 'smwgPropertyInvalidCharacterList' => [ 'Foo' ] ]
+ ) );
+
+ $containerBuilder->registerObject( 'MediaWikiNsContentReader', $this->mediaWikiNsContentReader );
+ $containerBuilder->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ $containerBuilder->registerObject( 'Store', $this->store );
+
+ $containerBuilder->registerFromFile( $this->servicesFileDir . '/' . 'DataValueServices.php' );
+
+ $this->assertInstanceOf(
+ $expected,
+ call_user_func_array( [ $containerBuilder, 'create' ], $parameters )
+ );
+ }
+
+ public function servicesProvider() {
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_PARSER . PropertyValue::TYPE_ID,
+ [],
+ PropertyValueParser::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . PropertyValue::TYPE_ID,
+ [],
+ PropertyValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_PARSER . AllowsPatternValue::TYPE_ID,
+ [],
+ AllowsPatternValueParser::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_PARSER . AllowsListValue::TYPE_ID,
+ [],
+ AllowsListValueParser::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_VALIDATOR . 'CompoundConstraintValueValidator',
+ [],
+ CompoundConstraintValueValidator::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_ID,
+ [],
+ StringValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . StringValue::TYPE_COD_ID,
+ [],
+ CodeStringValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . ReferenceValue::TYPE_ID,
+ [],
+ ReferenceValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . ReferenceValue::TYPE_ID,
+ [],
+ ReferenceValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_PARSER . MonolingualTextValue::TYPE_ID,
+ [],
+ MonolingualTextValueParser::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . MonolingualTextValue::TYPE_ID,
+ [],
+ MonolingualTextValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . NumberValue::TYPE_ID,
+ [],
+ NumberValueFormatter::class
+ ];
+
+ $provider[] = [
+ DataValueServiceFactory::TYPE_FORMATTER . TimeValue::TYPE_ID,
+ [],
+ TimeValueFormatter::class
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/Exception/ServiceNotFoundExceptionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/Exception/ServiceNotFoundExceptionTest.php
new file mode 100644
index 00000000..4949fb1e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/Exception/ServiceNotFoundExceptionTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Services\Exception;
+
+use SMW\Services\Exception\ServiceNotFoundException;
+
+/**
+ * @covers \SMW\Services\Exception\ServiceNotFoundException
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ServiceNotFoundExceptionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new ServiceNotFoundException( 'foo' );
+
+ $this->assertInstanceof(
+ ServiceNotFoundException::class,
+ $instance
+ );
+
+ $this->assertInstanceof(
+ '\InvalidArgumentException',
+ $instance
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServiceFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServiceFactoryTest.php
new file mode 100644
index 00000000..4551deed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServiceFactoryTest.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use Onoi\CallbackContainer\CallbackContainerFactory;
+use SMW\Services\ImporterServiceFactory;
+use SMW\Settings;
+
+/**
+ * @covers \SMW\Services\ImporterServiceFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ImporterServiceFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ private $containerBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $callbackContainerFactory = new CallbackContainerFactory();
+
+ $this->containerBuilder = $callbackContainerFactory->newCallbackContainerBuilder();
+
+ $pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->containerBuilder->registerObject( 'PageCreator', $pageCreator );
+
+ $importStringSource = $this->getMockBuilder( '\ImportStringSource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->containerBuilder->registerObject( 'ImportStringSource', $importStringSource );
+
+ $wikiImporter = $this->getMockBuilder( '\WikiImporter' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->containerBuilder->registerObject( 'WikiImporter', $wikiImporter );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->containerBuilder->registerObject( 'ConnectionManager', $connectionManager );
+
+ $this->containerBuilder->registerObject( 'Settings', new Settings( [
+ 'smwgImportReqVersion' => 1,
+ 'smwgImportFileDirs' => 'foo'
+ ] ) );
+
+ $this->containerBuilder->registerFromFile( $GLOBALS['smwgServicesFileDir'] . '/' . 'ImporterServices.php' );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ImporterServiceFactory::class,
+ new ImporterServiceFactory( $this->containerBuilder )
+ );
+ }
+
+ public function testCanConstructImportStringSource() {
+
+ $instance = new ImporterServiceFactory(
+ $this->containerBuilder
+ );
+
+ $this->assertInstanceOf(
+ '\ImportStringSource',
+ $instance->newImportStringSource( 'Foo' )
+ );
+ }
+
+ public function testCanConstructWikiImporter() {
+
+ $importSource = $this->getMockBuilder( '\ImportSource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ImporterServiceFactory(
+ $this->containerBuilder
+ );
+
+ $this->assertInstanceOf(
+ '\WikiImporter',
+ $instance->newWikiImporter( $importSource )
+ );
+ }
+
+ public function testCanConstructImporter() {
+
+ $contentIterator = $this->getMockBuilder( '\SMW\Importer\ContentIterator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $instance = new ImporterServiceFactory(
+ $this->containerBuilder
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\Importer',
+ $instance->newImporter( $contentIterator )
+ );
+ }
+
+ public function testCanConstructJsonContentIterator() {
+
+ $instance = new ImporterServiceFactory(
+ $this->containerBuilder
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\Importer\JsonContentIterator',
+ $instance->newJsonContentIterator( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServicesContainerBuildTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServicesContainerBuildTest.php
new file mode 100644
index 00000000..52ed7ab7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ImporterServicesContainerBuildTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use Onoi\CallbackContainer\CallbackContainerFactory;
+use SMW\Settings;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ImporterServicesContainerBuildTest extends \PHPUnit_Framework_TestCase {
+
+ private $callbackContainerFactory;
+ private $connectionManager;
+ private $servicesFileDir;
+ private $pageCreator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->pageCreator = $this->getMockBuilder( '\SMW\MediaWiki\PageCreator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connectionManager = $this->getMockBuilder( '\SMW\Connection\ConnectionManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->connectionManager->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->callbackContainerFactory = new CallbackContainerFactory();
+ $this->servicesFileDir = $GLOBALS['smwgServicesFileDir'];
+ }
+
+ /**
+ * @dataProvider servicesProvider
+ */
+ public function testCanConstruct( $service, $parameters, $expected ) {
+
+ array_unshift( $parameters, $service );
+
+ $containerBuilder = $this->callbackContainerFactory->newCallbackContainerBuilder();
+
+ $containerBuilder->registerObject( 'PageCreator', $this->pageCreator );
+ $containerBuilder->registerObject( 'ConnectionManager', $this->connectionManager );
+
+ $containerBuilder->registerObject( 'Settings', new Settings( [
+ 'smwgImportReqVersion' => 1,
+ 'smwgImportFileDirs' => [ 'foo' ]
+ ] ) );
+
+ $containerBuilder->registerFromFile( $this->servicesFileDir . '/' . 'ImporterServices.php' );
+
+ $this->assertInstanceOf(
+ $expected,
+ call_user_func_array( [ $containerBuilder, 'create' ], $parameters )
+ );
+ }
+
+ public function servicesProvider() {
+
+ $contentIterator = $this->getMockBuilder( '\SMW\Importer\ContentIterator' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ 'Importer',
+ [ $contentIterator ],
+ '\SMW\Importer\Importer'
+ ];
+
+ $provider[] = [
+ 'JsonContentIterator',
+ [ 'SomeDirectory' ],
+ '\SMW\Importer\JsonContentIterator'
+ ];
+
+ $provider[] = [
+ 'ImporterServiceFactory',
+ [],
+ '\SMW\Services\ImporterServiceFactory'
+ ];
+
+ $provider[] = [
+ 'XmlContentCreator',
+ [],
+ '\SMW\Importer\ContentCreators\XmlContentCreator'
+ ];
+
+ $provider[] = [
+ 'TextContentCreator',
+ [],
+ '\SMW\Importer\ContentCreators\TextContentCreator'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/MediaWikiServicesContainerBuildTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/MediaWikiServicesContainerBuildTest.php
new file mode 100644
index 00000000..99b448fb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/MediaWikiServicesContainerBuildTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use Onoi\CallbackContainer\CallbackContainerFactory;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class MediaWikiServicesContainerBuildTest extends \PHPUnit_Framework_TestCase {
+
+ private $callbackContainerFactory;
+ private $servicesFileDir;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->callbackContainerFactory = new CallbackContainerFactory();
+ $this->servicesFileDir = $GLOBALS['smwgServicesFileDir'];
+ }
+
+ /**
+ * @dataProvider servicesProvider
+ */
+ public function testCanConstruct( $service, $parameters, $expected ) {
+
+ array_unshift( $parameters, $service );
+
+ $containerBuilder = $this->callbackContainerFactory->newCallbackContainerBuilder();
+ $containerBuilder->registerFromFile( $this->servicesFileDir . '/' . 'MediaWikiServices.php' );
+
+ $this->assertInstanceOf(
+ $expected,
+ call_user_func_array( [ $containerBuilder, 'create' ], $parameters )
+ );
+ }
+
+ public function servicesProvider() {
+
+ $title = $this->getMockBuilder( '\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = [
+ 'WikiPage',
+ [ $title ],
+ '\WikiPage'
+ ];
+
+ $provider[] = [
+ 'DBLoadBalancer',
+ [],
+ '\LoadBalancer'
+ ];
+
+/*
+ $database = $this->getMockBuilder( '\DatabaeBase' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $provider[] = array(
+ 'DefaultSearchEngineTypeForDB',
+ array( $database ),
+ '\SearchEngine'
+ );
+*/
+
+ $provider[] = [
+ 'MediaWikiLogger',
+ [],
+ '\Psr\Log\LoggerInterface'
+ ];
+
+ $provider[] = [
+ 'JobQueueGroup',
+ [],
+ '\JobQueueGroup'
+ ];
+
+ return $provider;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ServicesContainerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ServicesContainerTest.php
new file mode 100644
index 00000000..7f4082b9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/ServicesContainerTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use SMW\Services\ServicesContainer;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Services\ServicesContainer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ServicesContainerTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ ServicesContainer::class,
+ new ServicesContainer()
+ );
+ }
+
+ public function testGet() {
+
+ $mock = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'runService' ] )
+ ->getMock();
+
+ $mock->expects( $this->once() )
+ ->method( 'runService' );
+
+ $instance = new ServicesContainer(
+ [
+ 'test' => [ $this, 'fakeService' ]
+ ]
+ );
+
+ $instance->get( 'test', $mock );
+ }
+
+ public function testGetTypedService() {
+
+ $instance = new ServicesContainer(
+ [
+ 'test' => [
+ '_service' => [ $this, 'stdClassService' ],
+ '_type' => '\stdClass'
+ ]
+ ]
+ );
+
+ $this->assertInstanceOf(
+ '\stdClass',
+ $instance->get( 'test' )
+ );
+ }
+
+ public function testGet_MultipleArgs() {
+
+ $fake = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'runService' ] )
+ ->getMock();
+
+ $fake->expects( $this->once() )
+ ->method( 'runService' )
+ ->with( $this->stringContains( 'FOO' ) );
+
+ $instance = new ServicesContainer(
+ [
+ 'test' => [ $this, 'fakeService' ]
+ ]
+ );
+
+ $instance->get( 'test', $fake, 'FOO' );
+ }
+
+ public function testAdd() {
+
+ $fake = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'runService' ] )
+ ->getMock();
+
+ $fake->expects( $this->once() )
+ ->method( 'runService' );
+
+ $instance = new ServicesContainer();
+
+ $instance->add( 'test', [ $this, 'fakeService' ] );
+ $instance->get( 'test', $fake );
+ }
+
+ public function testAddClosure() {
+
+ $fake = $this->getMockBuilder( '\stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'runService' ] )
+ ->getMock();
+
+ $fake->expects( $this->once() )
+ ->method( 'runService' );
+
+ $instance = new ServicesContainer();
+
+ $closure = function( $arg ) use( $fake ) {
+ $fake->runService( $arg );
+ };
+
+ $instance->add( 'test', $closure );
+ $instance->get( 'test', $fake );
+ }
+
+ public function testUnknownServiceThrowsException() {
+
+ $instance = new ServicesContainer();
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->get( 'test' );
+ }
+
+ public function testNonCallableServiceThrowsException() {
+
+ $instance = new ServicesContainer(
+ [
+ 'test' => 'Foo'
+ ]
+ );
+
+ $this->setExpectedException( '\RuntimeException' );
+ $instance->get( 'test' );
+ }
+
+ public function fakeService( $fake, $arg = '' ) {
+ $fake->runService( $arg );
+ }
+
+ public function stdClassService( $arg = '' ) {
+ return new \stdClass();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/SharedServicesContainerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/SharedServicesContainerTest.php
new file mode 100644
index 00000000..b4758e72
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Services/SharedServicesContainerTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests\Services;
+
+use SMW\Services\SharedServicesContainer;
+
+/**
+ * @covers \SMW\Services\SharedServicesContainer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.3
+ *
+ * @author mwjames
+ */
+class SharedServicesContainerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SharedServicesContainer::class,
+ new SharedServicesContainer()
+ );
+
+ $this->assertInstanceOf(
+ '\Onoi\CallbackContainer\CallbackContainer',
+ new SharedServicesContainer()
+ );
+ }
+
+ public function testRegister() {
+
+ $containerBuilder = $this->getMockBuilder( '\Onoi\CallbackContainer\ContainerBuilder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $containerBuilder->expects( $this->atLeastOnce() )
+ ->method( 'registerCallback' );
+
+ $instance = new SharedServicesContainer();
+ $instance->register( $containerBuilder );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SiteTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SiteTest.php
new file mode 100644
index 00000000..ea8ac112
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/SiteTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Site;
+
+/**
+ * @covers \SMW\Site
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class SiteTest extends \PHPUnit_Framework_TestCase {
+
+ public function testIsReadOnly() {
+
+ $this->assertInternalType(
+ 'boolean',
+ Site::isReadOnly()
+ );
+ }
+
+ public function testIsBlocked() {
+
+ $this->assertInternalType(
+ 'boolean',
+ Site::isBlocked()
+ );
+ }
+
+ public function testName() {
+
+ $this->assertInternalType(
+ 'string',
+ Site::name()
+ );
+ }
+
+ public function testWikiurl() {
+
+ $this->assertInternalType(
+ 'string',
+ Site::wikiurl()
+ );
+ }
+
+ public function testLanguageCode() {
+
+ $this->assertInternalType(
+ 'string',
+ Site::languageCode()
+ );
+ }
+
+ public function testIsCommandLineMode() {
+
+ $this->assertInternalType(
+ 'boolean',
+ Site::isCommandLineMode()
+ );
+ }
+
+ public function testIsCapitalLinks() {
+
+ $this->assertInternalType(
+ 'boolean',
+ Site::isCapitalLinks()
+ );
+ }
+
+ public function testStats() {
+
+ $this->assertInternalType(
+ 'array',
+ Site::stats()
+ );
+ }
+
+ public function testGetJobClasses() {
+
+ $this->assertInternalType(
+ 'array',
+ Site::getJobClasses()
+ );
+
+ $this->assertNotEmpty(
+ Site::getJobClasses( 'SMW' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StoreTest.php
new file mode 100644
index 00000000..def0c4c5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StoreTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\Store
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class StoreTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetRedirectTarget() {
+
+ $wikipage = new DIWikiPage( 'Foo', NS_MAIN );
+ $expected = new DIWikiPage( 'Bar', NS_MAIN );
+
+ $instance = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getPropertyValues' ] )
+ ->getMockForAbstractClass();
+
+ $instance->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ $expected ] ) );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getRedirectTarget( $wikipage )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StringConditionTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StringConditionTest.php
new file mode 100644
index 00000000..84e45137
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/StringConditionTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\StringCondition;
+
+/**
+ * @covers \SMW\StringCondition
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class StringConditionTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $instance = new StringCondition( 'Foo', StringCondition::STRCOND_PRE, true );
+
+ $this->assertInstanceOf(
+ '\SMW\StringCondition',
+ $instance
+ );
+
+ $this->assertSame(
+ 'Foo',
+ $instance->string
+ );
+
+ $this->assertEquals(
+ 'Foo#0#1#',
+ $instance->getHash()
+ );
+
+ $this->assertEquals(
+ StringCondition::COND_PRE,
+ $instance->condition
+ );
+
+ $this->assertTrue(
+ $instance->isOr
+ );
+
+ $this->assertFalse(
+ $instance->isNot
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/TypesRegistryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/TypesRegistryTest.php
new file mode 100644
index 00000000..440e12db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/TypesRegistryTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\TypesRegistry;
+
+/**
+ * @covers \SMW\TypesRegistry
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TypesRegistryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetDataTypeList() {
+
+ $this->assertInternalType(
+ 'array',
+ TypesRegistry::getDataTypeList()
+ );
+ }
+
+ public function testGetPropertyList() {
+
+ $this->assertInternalType(
+ 'array',
+ TypesRegistry::getPropertyList()
+ );
+ }
+
+ public function testGetFixedPropertyIdList() {
+
+ $propertyList = TypesRegistry::getPropertyList();
+
+ foreach ( TypesRegistry::getFixedPropertyIdList() as $key => $id ) {
+ $this->assertArrayHasKey( $key, $propertyList );
+ }
+ }
+
+ /**
+ * @dataProvider typeList
+ */
+ public function testTypeList_FirstCharUnderscore( $key, $def ) {
+ $this->assertTrue( $key{0} === '_' );
+ }
+
+ /**
+ * @dataProvider typeList
+ */
+ public function testTypeList_ClassExists( $key, $def ) {
+ $this->assertTrue( class_exists( $def[0] ) );
+ }
+
+ /**
+ * @dataProvider propertyList
+ */
+ public function testPropertyList_FirstCharUnderscore( $key, $def ) {
+ $this->assertTrue( $key{0} === '_' );
+ }
+
+ public function typeList() {
+
+ $excludes = [];
+
+ // Requires Maps/Semantic Maps hence remove from the
+ // test list
+ $excludes = [ '_geo', '_gpo' ];
+
+ foreach ( TypesRegistry::getDataTypeList() as $key => $def ) {
+
+ if ( in_array( $key, $excludes ) ) {
+ continue;
+ }
+
+ yield[ $key, $def ];
+ }
+ }
+
+ public function propertyList() {
+
+ $excludes = [];
+
+ foreach ( TypesRegistry::getPropertyList() as $key => $def ) {
+
+ if ( in_array( $key, $excludes ) ) {
+ continue;
+ }
+
+ yield[ $key, $def ];
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/BufferedStatsdCollectorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/BufferedStatsdCollectorTest.php
new file mode 100644
index 00000000..90508ad2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/BufferedStatsdCollectorTest.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Onoi\BlobStore\BlobStore;
+use Onoi\BlobStore\Container;
+use SMW\Utils\BufferedStatsdCollector;
+
+/**
+ * @covers \SMW\Utils\BufferedStatsdCollector
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class BufferedStatsdCollectorTest extends \PHPUnit_Framework_TestCase {
+
+ private $blobStore;
+ private $container;
+
+ protected function setUp() {
+
+ $this->container = $this->getMockBuilder( Container::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore = $this->getMockBuilder( BlobStore::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->blobStore->expects( $this->any() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $this->container ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ BufferedStatsdCollector::class,
+ new BufferedStatsdCollector( $this->blobStore, 42 )
+ );
+ }
+
+ public function testIncr() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'get' )
+ ->will( $this->returnValue( 10 ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'set' )
+ ->with(
+ $this->equalTo( 'Foo.bar' ),
+ $this->equalTo( 11 ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'save' );
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $instance->incr( 'Foo.bar' );
+ $instance->saveStats();
+ }
+
+ public function testSet() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'get' )
+ ->will( $this->returnValue( 10 ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'set' )
+ ->with(
+ $this->equalTo( 'Foo.bar' ),
+ $this->equalTo( 10 ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'save' );
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $instance->set( 'Foo.bar', 10 );
+ $instance->saveStats();
+ }
+
+ public function testCalcMedian() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'has' )
+ ->will( $this->returnValue( true ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'get' )
+ ->will( $this->returnValue( 10 ) );
+
+ $this->container->expects( $this->once() )
+ ->method( 'set' )
+ ->with(
+ $this->equalTo( 'Foo.bar' ),
+ $this->equalTo( 7.5 ) );
+
+ $this->blobStore->expects( $this->once() )
+ ->method( 'save' );
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $instance->calcMedian( 'Foo.bar', 5 );
+ $instance->saveStats();
+ }
+
+ public function testStats_Simple() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( [ 'Foo' => 1, 'Bar' => 1 ] ) );
+
+ $expected = [
+ 'Foo' => 1,
+ 'Bar' => 1
+ ];
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getStats()
+ );
+ }
+
+ public function testStats_SimpleHierarchy() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( [ 'Foo.foobar' => 1, 'Bar' => 1 ] ) );
+
+ $expected = [
+ 'Foo' => [ 'foobar' => 1 ],
+ 'Bar' => 1
+ ];
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getStats()
+ );
+ }
+
+ public function testStats_ExtendedHierarchy() {
+
+ $this->container->expects( $this->once() )
+ ->method( 'getData' )
+ ->will( $this->returnValue( [ 'Foo.foobar' => 5, 'Bar' => 1, 'Foo.foobar.baz' => 1 ] ) );
+
+ $expected = [
+ 'Foo' => [ 'foobar' => [ 5, 'baz' => 1 ] ],
+ 'Bar' => 1
+ ];
+
+ $instance = new BufferedStatsdCollector(
+ $this->blobStore,
+ 42
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getStats()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharArmorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharArmorTest.php
new file mode 100644
index 00000000..e2e4e8e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharArmorTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\CharArmor;
+
+/**
+ * @covers \SMW\Utils\CharArmor
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CharArmorTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider invisibleControlCharactersProvider
+ */
+ public function testRemoveControlChars( $withControlChar, $expected ) {
+
+ $this->assertFalse(
+ $expected === $withControlChar
+ );
+
+ $this->assertEquals(
+ $expected,
+ CharArmor::removeControlChars( $withControlChar )
+ );
+ }
+
+ /**
+ * @dataProvider specialCharactersProvider
+ */
+ public function testRemoveSpecialChars( $withSpecialChar, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ CharArmor::removeSpecialChars( $withSpecialChar )
+ );
+ }
+
+ public function invisibleControlCharactersProvider() {
+
+ $provider[] = [
+ '[[Left-To-Right Mark::"‎"]]',
+ '[[Left-To-Right Mark::""]]'
+ ];
+
+ $provider[] = [
+ '[[Right-To-Left Mark::"â€"]]',
+ '[[Right-To-Left Mark::""]]'
+ ];
+
+ $provider[] = [
+ '[[Zero-Width​Space::"​"]]',
+ '[[Zero-WidthSpace::""]]'
+ ];
+
+ $provider[] = [
+ '[[Zero Width Non-Joiner::"‌"]]',
+ '[[Zero Width Non-Joiner::""]]'
+ ];
+
+ $provider[] = [
+ '[[Zero Width Joiner::"â€"]]',
+ '[[Zero Width Joiner::""]]'
+ ];
+
+ return $provider;
+ }
+
+ public function specialCharactersProvider() {
+
+ $provider[] = [
+ 'visible shy&shy;ness',
+ 'visible shyness'
+ ];
+
+ $provider[] = [
+ 'leftToRight&lrm;Mark',
+ 'leftToRightMark'
+ ];
+
+ $provider[] = [
+ '[[Figure Space::" "]]',
+ '[[Figure Space::" "]]'
+ ];
+
+ $provider[] = [
+ '[[En Quad::" "]]',
+ '[[En Quad::" "]]'
+ ];
+
+ $provider[] = [
+ '[[Hair Space::" "]]',
+ '[[Hair Space::" "]]'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharExaminerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharExaminerTest.php
new file mode 100644
index 00000000..16b0010a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CharExaminerTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\CharExaminer;
+
+/**
+ * @covers \SMW\Utils\CharExaminer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CharExaminerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testToContainKoreanCharacters() {
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::HANGUL, '한국어 í…ìŠ¤íŠ¸ì˜ ì˜ˆ' )
+ );
+
+ $this->assertFalse(
+ CharExaminer::contains( CharExaminer::HAN, '한국어 í…ìŠ¤íŠ¸ì˜ ì˜ˆ' )
+ );
+ }
+
+ public function testToContainJapaneseCharacters() {
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::LATIN, '脳ã®IQテスト' )
+ );
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::HIRAGANA_KATAKANA, '脳ã®IQテスト' )
+ );
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::HAN, '脳ã®IQテスト' )
+ );
+ }
+
+ public function testToContainChineseCharacters() {
+
+ $this->assertFalse(
+ CharExaminer::contains( CharExaminer::LATIN, 'æ‰å¯ä»¥è¿‡å…³' )
+ );
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::CJK_UNIFIED, 'æ‰å¯ä»¥è¿‡å…³' )
+ );
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::HAN, 'æ‰å¯ä»¥è¿‡å…³' )
+ );
+ }
+
+ public function testToContainCyrillic() {
+
+ $this->assertFalse(
+ CharExaminer::contains( CharExaminer::LATIN, 'Привет' )
+ );
+
+ $this->assertTrue(
+ CharExaminer::contains( CharExaminer::CYRILLIC, 'Привет' )
+ );
+ }
+
+ public function testToContainUnknownCharacters() {
+ $this->assertFalse(
+ CharExaminer::contains( 'Foo', 'é¿©' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CircularReferenceGuardTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CircularReferenceGuardTest.php
new file mode 100644
index 00000000..cb42486a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CircularReferenceGuardTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\CircularReferenceGuard;
+
+/**
+ * @covers \SMW\Utils\CircularReferenceGuard
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class CircularReferenceGuardTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\Utils\CircularReferenceGuard',
+ new CircularReferenceGuard()
+ );
+ }
+
+ public function testRoundtripForRegisteredNamespace() {
+
+ $instance = new CircularReferenceGuard( 'bar' );
+ $instance->setMaxRecursionDepth( 1 );
+
+ $this->assertEquals(
+ 0,
+ $instance->get( 'Foo' )
+ );
+
+ $instance->mark( 'Foo' );
+ $instance->mark( 'Foo' );
+
+ $this->assertEquals(
+ 2,
+ $instance->get( 'Foo' )
+ );
+
+ $this->assertTrue(
+ $instance->isCircular( 'Foo' )
+ );
+
+ $instance->unmark( 'Foo' );
+
+ $this->assertEquals(
+ 1,
+ $instance->get( 'Foo' )
+ );
+
+ $this->assertFalse(
+ $instance->isCircular( 'Foo' )
+ );
+
+ $instance->unmark( 'notBeenMarkedBefore' );
+ }
+
+ /**
+ * @depends testRoundtripForRegisteredNamespace
+ */
+ public function testVerifyRetainedReferenceFromPreviousInvocation() {
+
+ $instance = new CircularReferenceGuard( 'bar' );
+
+ $this->assertEquals(
+ 1,
+ $instance->get( 'Foo' )
+ );
+
+ $instance->reset( 'bar' );
+
+ $this->assertEquals(
+ 0,
+ $instance->get( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CsvTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CsvTest.php
new file mode 100644
index 00000000..92c9ce8b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/CsvTest.php
@@ -0,0 +1,227 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Csv;
+
+/**
+ * @covers \SMW\Utils\Csv
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class CsvTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider rowsProvider
+ */
+ public function testConcatenate( $rows, $sep, $expected ) {
+
+ $instance = new Csv();
+
+ $this->assertEquals(
+ $expected,
+ $instance->merge( $rows, $sep )
+ );
+ }
+
+ /**
+ * @dataProvider makeProvider
+ */
+ public function testMake( $header, $rows, $sep, $show, $expected ) {
+
+ $instance = new Csv( $show );
+
+ $this->assertEquals(
+ $expected,
+ $instance->toString( $header, $rows, $sep )
+ );
+ }
+
+ public function testWithBOM() {
+
+ $header = [];
+ $rows = [
+ [ 'Foo', '1', '2', '3' ]
+ ];
+
+ $bom = ( chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) );
+
+ $instance = new Csv( false, true );
+
+ $this->assertEquals(
+ "{$bom}Foo,1,2,3\n",
+ $instance->toString( $header, $rows, ',' )
+ );
+ }
+
+ public function rowsProvider() {
+
+ // No change
+ yield [
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ]
+ ];
+
+ // Concatenate duplicate
+ yield [
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Foo', '1', '2', '3' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1', '2', '3' ]
+ ]
+ ];
+
+ // Concatenate column values
+ yield [
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Foo', '1', '2', '4' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1', '2', '3,4' ]
+ ]
+ ];
+
+ // Concatenate column values
+ yield [
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Foo', '1', '2', '3', '4' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1', '2', '3' ]
+ ]
+ ];
+
+ yield [
+ [
+ [ 'Foo', '1', '2', '3', '4' ],
+ [ 'Foo', '1', '2', '3' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1', '2', '3', '4' ]
+ ]
+ ];
+
+ yield [
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ [ 'Foo', '4', '5', '6' ],
+ [ 'Bar', 'A', 'B', 'C' ],
+ ],
+ ',',
+ [
+ [ 'Foo', '1,4', '2,5', '3,6' ],
+ [ 'Bar', '1,A', '2,B', '3,C' ]
+ ]
+ ];
+
+ yield [
+ [
+ [ 'Foo', '1', '2', '3;6;2' ],
+ [ 'Bar', '1', '2', '3' ],
+ [ 'Foo', '4', '5', '6' ],
+ [ 'Bar', 'A', 'B', 'C' ],
+ ],
+ ';',
+ [
+ [ 'Foo', '1;4', '2;5', '3;6;2' ],
+ [ 'Bar', '1;A', '2;B', '3;C' ]
+ ]
+ ];
+ }
+
+ public function makeProvider() {
+
+ // Without header
+ yield [
+ [],
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',',
+ false,
+ "Foo,1,2,3\nBar,1,2,3\n"
+ ];
+
+ // Without header, multiple value assignment
+ yield [
+ [],
+ [
+ [ 'Foo', '1', '2', '3,4' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',',
+ false,
+ "Foo,1,2,\"3,4\"\nBar,1,2,3\n"
+ ];
+
+ // With header
+ yield [
+ [ 'H1', 'H2', 'H3', 'H4' ],
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',',
+ false,
+ "H1,H2,H3,H4\nFoo,1,2,3\nBar,1,2,3\n"
+ ];
+
+ // With header
+ yield [
+ [ 'H1', 'H2', 'H3', 'H4' ],
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',',
+ true,
+ "sep=,\nH1,H2,H3,H4\nFoo,1,2,3\nBar,1,2,3\n"
+ ];
+
+ // fputcsv ... delimiter must be a single character
+ yield [
+ [],
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ ',..;',
+ false,
+ "Foo,1,2,3\nBar,1,2,3\n"
+ ];
+
+ // fputcsv ... delimiter must be a single character
+ yield [
+ [],
+ [
+ [ 'Foo', '1', '2', '3' ],
+ [ 'Bar', '1', '2', '3' ],
+ ],
+ '',
+ false,
+ "Foo,1,2,3\nBar,1,2,3\n"
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ErrorCodeFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ErrorCodeFormatterTest.php
new file mode 100644
index 00000000..41eee654
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ErrorCodeFormatterTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\ErrorCodeFormatter;
+
+/**
+ * @covers \SMW\Utils\ErrorCodeFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class ErrorCodeFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetStringFromJsonErrorCode() {
+
+ $this->assertInternalType(
+ 'string',
+ ErrorCodeFormatter::getStringFromJsonErrorCode( 'Foo' )
+ );
+
+ $contents = json_decode( '{ Foo: Bar }' );
+
+ $this->assertInternalType(
+ 'string',
+ ErrorCodeFormatter::getStringFromJsonErrorCode( json_last_error() )
+ );
+ }
+
+ public function testGetMessageFromJsonErrorCode() {
+
+ $this->assertInternalType(
+ 'string',
+ ErrorCodeFormatter::getMessageFromJsonErrorCode( 'Foo' )
+ );
+
+ $contents = json_decode( '{ Foo: Bar }' );
+
+ $this->assertInternalType(
+ 'string',
+ ErrorCodeFormatter::getMessageFromJsonErrorCode( json_last_error() )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/FileTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/FileTest.php
new file mode 100644
index 00000000..636ba0a0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/FileTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\File;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Utils\File
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class FileTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $instance = new File();
+
+ $this->assertInstanceOf(
+ File::class,
+ $instance
+ );
+ }
+
+ public function testWrite_ThrowsException() {
+
+ $instance = new File();
+
+ $this->setExpectedException( '\SMW\Exception\FileNotWritableException' );
+ $instance->write( 'abc/Foo', '' );
+ }
+
+ public function testDir() {
+
+ $this->assertInternalType(
+ 'string',
+ File::dir( 'foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HmacSerializerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HmacSerializerTest.php
new file mode 100644
index 00000000..98c302a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HmacSerializerTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\HmacSerializer;
+
+/**
+ * @covers \SMW\Utils\HmacSerializer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HmacSerializerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testEncodeWithDifferentKey() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $this->assertNotSame(
+ $instance->encode( $data, 'abc' ),
+ $instance->encode( $data, 'def' )
+ );
+ }
+
+ public function testUDecodeUsingAnObject() {
+
+ $this->assertFalse(
+ HmacSerializer::decode( $this )
+ );
+ }
+
+ public function testRoundtripEncodeDecode() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $this->assertEquals(
+ $data,
+ $instance->decode( $instance->encode( $data, 'def' ), 'def' )
+ );
+ }
+
+ public function testRoundtripEncodeDecodeWithDifferentKey() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $result = $instance->decode(
+ $instance->encode( $data, 'def' ),
+ 'abc'
+ );
+
+ $this->assertNotEquals(
+ $data,
+ $result
+ );
+
+ $this->assertFalse(
+ $result
+ );
+ }
+
+ public function testSeralizeWithDifferentKey() {
+
+ $data = [ 'Foo' ];
+
+ $this->assertNotSame(
+ HmacSerializer::serialize( $data, 'abc' ),
+ HmacSerializer::serialize( $data, 'def' )
+ );
+ }
+
+ public function testUnseralizeUsingAnObject() {
+
+ $this->assertFalse(
+ HmacSerializer::unserialize( $this )
+ );
+ }
+
+ public function testRoundtripSerializeDeserialize() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $this->assertEquals(
+ $data,
+ $instance->unserialize( $instance->serialize( $data, 'def' ), 'def' )
+ );
+ }
+
+ public function testRoundtripCompressUncompress() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $this->assertEquals(
+ $data,
+ $instance->uncompress( $instance->compress( $data, 'def' ), 'def' )
+ );
+ }
+
+ public function testRoundtripSerializeDeserializeWithDifferentKey() {
+
+ $instance = new HmacSerializer();
+
+ $data = [ 'Foo' ];
+
+ $result = $instance->unserialize(
+ $instance->serialize( $data, 'def' ),
+ 'abc'
+ );
+
+ $this->assertNotEquals(
+ $data,
+ $result
+ );
+
+ $this->assertFalse(
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlColumnsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlColumnsTest.php
new file mode 100644
index 00000000..a7bd07a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlColumnsTest.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace SMW\Test\Utils;
+
+use SMW\Utils\HtmlColumns;
+use SMW\Tests\Utils\UtilityFactory;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Utils\HtmlColumns
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlColumnListFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+ $this->stringValidator = UtilityFactory::getInstance()->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ HtmlColumns::class,
+ new HtmlColumns()
+ );
+ }
+
+ public function testUnknownTypeThrowsException() {
+
+ $instance = new HtmlColumns();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+
+ $instance->addContents(
+ [ 'Foo' ],
+ 'bar'
+ );
+ }
+
+ public function testDefaultColumnUnorderedList() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ],
+ HtmlColumns::INDX_CONTENT
+ );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:100%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ul><li>Foo</li><li>Bar</li></ul>',
+ '<div class="smw-column-header">B</div><ul><li>Ichi</li><li>Ni</li></ul></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnUnorderedList() {
+
+ $listContinuesAbbrev = '...';
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Baz', 'Fom', 'Fin', 'Fum' ]
+ ],
+ HtmlColumns::INDX_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setContinueAbbrev( $listContinuesAbbrev );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">a</div>',
+ '<ul><li>Foo</li><li>Bar</li></ul>',
+ '<div class="smw-column-header">B</div><ul><li>Baz</li></ul></div> <!-- end column -->',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">B ' . $listContinuesAbbrev .'</div>',
+ '<ul start=4><li>Fom</li><li>Fin</li><li>Fum</li></ul></div> <!-- end column -->',
+ '<br style="clear: both;"/></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testThreeColumnUnorderedList() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ],
+ HtmlColumns::INDX_CONTENT
+ );
+
+ $instance->setColumns( 3 );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:33%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ul><li>Foo</li><li>Bar</li></ul></div>',
+ '<div class="smw-column" style="width:33%;" dir="ltr">',
+ '<div class="smw-column-header">B</div><ul><li>Ichi</li><li>Ni</li></ul></div>'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnOrderedList() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'a' => [ 'Foo', 'Bar' ],
+ 'B' => [ 'Ichi', 'Ni' ]
+ ],
+ HtmlColumns::INDX_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setListType( 'ol' );
+
+ $expected = [
+ '<div class="smw-columnlist-container" dir="ltr">',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">a</div><ol><li>Foo</li><li>Bar</li></ol></div> <!-- end column -->',
+ '<div class="smw-column" style="width:50%;" dir="ltr">',
+ '<div class="smw-column-header">B</div><ol><li>Ichi</li><li>Ni</li></ol></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testTwoColumnOrderedListNoHeader() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'Foo', 'Baz', 'Bar'
+ ],
+ HtmlColumns::LIST_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setColumnListClass( 'foo-class' );
+ $instance->setColumnClass( 'bar-class' );
+ $instance->setListType( 'ul' );
+
+ $expected = [
+ '<div class="foo-class" dir="ltr">',
+ '<div class="bar-class" style="width:50%;" dir="ltr">',
+ '<ul start=1><li>Foo</li><li>Baz</li></ul></div> <!-- end column -->',
+ '<div class="bar-class" style="width:50%;" dir="ltr">',
+ '<ul start=3><li>Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testResponsiveColumnsToBeDeterminedByBrowser() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'Foo', 'Baz', 'Bar'
+ ],
+ HtmlColumns::LIST_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setColumnListClass( 'foo-class' );
+ $instance->setColumnClass( 'bar-responsive' );
+ $instance->setListType( 'ul' );
+ $instance->isRTL( true );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ul start=1><li>Foo</li><li>Baz</li><li>Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testItemListWithAttributes() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'Foo', 'Baz', 'Bar'
+ ],
+ HtmlColumns::LIST_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setColumnListClass( 'foo-class' );
+ $instance->setColumnClass( 'bar-responsive' );
+ $instance->setListType( 'ul' );
+ $instance->isRTL( true );
+
+ $instance->setItemAttributes(
+ [
+ md5( 'Foo' ) => [
+ 'id' => 123
+ ],
+ md5( 'Bar' ) => 456
+ ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ul start=1><li id="123">Foo</li><li>Baz</li><li 0="456">Bar</li></ul></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+ public function testOListWithAttributes() {
+
+ $instance = new HtmlColumns();
+
+ $instance->addContents(
+ [
+ 'Foo', 'Baz', 'Bar'
+ ],
+ HtmlColumns::LIST_CONTENT
+ );
+
+ $instance->setColumns( 2 );
+ $instance->setColumnListClass( 'foo-class' );
+ $instance->setColumnClass( 'bar-responsive' );
+ $instance->setListType( 'ol', 'i' );
+ $instance->isRTL( true );
+
+ $instance->setItemAttributes(
+ [
+ md5( 'Foo' ) => [
+ 'id' => 123
+ ],
+ md5( 'Bar' ) => 456
+ ]
+ );
+
+ $expected = [
+ '<div class="foo-class" dir="rtl"><div class="bar-responsive" style="width:100%;" dir="rtl">',
+ '<ol type=i start=1><li id="123">Foo</li><li>Baz</li><li 0="456">Bar</li></ol></div> <!-- end column -->'
+ ];
+
+ $this->stringValidator->assertThatStringContains(
+ $expected,
+ $instance->getHtml()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlDivTableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlDivTableTest.php
new file mode 100644
index 00000000..414d960b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlDivTableTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\HtmlDivTable;
+
+/**
+ * @covers \SMW\Utils\HtmlDivTable
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlDivTableTest extends \PHPUnit_Framework_TestCase {
+
+ public function testOpenClose() {
+
+ $this->assertEquals(
+ '<div class="smw-table"></div>',
+ HtmlDivTable::open() . HtmlDivTable::close()
+ );
+
+ $this->assertEquals(
+ HtmlDivTable::table(),
+ HtmlDivTable::open() . HtmlDivTable::close()
+ );
+ }
+
+ public function testHeader() {
+
+ $this->assertEquals(
+ '<div class="smw-table-header bar">foo</div>',
+ HtmlDivTable::header( 'foo', [ 'class' => 'bar' ] )
+ );
+ }
+
+ public function testBody() {
+
+ $this->assertEquals(
+ '<div class="smw-table-body bar">foo</div>',
+ HtmlDivTable::body( 'foo', [ 'class' => 'bar' ] )
+ );
+ }
+
+ public function testFooter() {
+
+ $this->assertEquals(
+ '<div class="smw-table-footer bar">foo</div>',
+ HtmlDivTable::footer( 'foo', [ 'class' => 'bar' ] )
+ );
+ }
+
+ public function testRow() {
+
+ $this->assertEquals(
+ '<div class="smw-table-row bar">foo</div>',
+ HtmlDivTable::row( 'foo', [ 'class' => 'bar' ] )
+ );
+ }
+
+ public function testCell() {
+
+ $this->assertEquals(
+ '<div class="smw-table-cell bar">foo</div>',
+ HtmlDivTable::cell( 'foo', [ 'class' => 'bar' ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlModalTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlModalTest.php
new file mode 100644
index 00000000..72401c73
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlModalTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\HtmlModal;
+
+/**
+ * @covers \SMW\Utils\HtmlModal
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlModalTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetModules() {
+
+ $this->assertInternalType(
+ 'array',
+ HtmlModal::getModules()
+ );
+
+ $this->assertInternalType(
+ 'array',
+ HtmlModal::getModuleStyles()
+ );
+ }
+
+ public function testLink() {
+
+ $this->assertContains(
+ 'smw-modal-link',
+ HtmlModal::link( 'Foo' )
+ );
+ }
+
+ public function testModal() {
+
+ $this->assertContains(
+ 'smw-modal',
+ HtmlModal::modal( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTableTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTableTest.php
new file mode 100644
index 00000000..6be1347f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTableTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Tests\TestEnvironment;
+use SMW\Utils\HtmlTable;
+
+/**
+ * @covers \SMW\Utils\HtmlTable
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlTableTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->stringValidator = TestEnvironment::newValidatorFactory()->newStringValidator();
+ }
+
+ public function testStandardTable_Cell_Row() {
+
+ $instance = new HtmlTable();
+
+ $instance->cell( 'Foo', [ 'rel' => 'some' ] );
+ $instance->row( [ 'class' => 'bar' ] );
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><tr class="bar row-odd"><td rel="some">Foo</td></tr></table>',
+ $instance->table()
+ );
+ }
+
+ public function testStandardTable_Header_Cell_Row() {
+
+ $instance = new HtmlTable();
+
+ $instance->header( 'Foo ');
+ $instance->cell( 'Bar' );
+ $instance->row();
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><th>Foo </th><tr class="row-odd"><td>Bar</td></tr></table>',
+ $instance->table()
+ );
+ }
+
+ public function testStandardTable_Header_Cell_Row_IsHtml() {
+
+ $instance = new HtmlTable();
+
+ $instance->header( 'Foo' );
+ $instance->cell( 'Bar' );
+ $instance->row();
+
+ $this->stringValidator->assertThatStringContains(
+ '<table><thead><th>Foo</th></thead><tbody><tr class="row-odd"><td>Bar</td></tr></tbody></table>',
+ $instance->table( [], false, true )
+ );
+ }
+
+ public function testTransposedTable_Cell_Row() {
+
+ $instance = new HtmlTable();
+
+ // We need a dedicated header definition to support a table transpose
+ $instance->header( 'Foo' );
+ $instance->header( 'Bar' );
+
+ $instance->cell( 'lala', [ 'class' => 'foo' ] );
+ $instance->row();
+
+ $instance->cell( 'lula', [ 'rel' => 'tuuu' ] );
+ $instance->cell( 'lila' );
+ $instance->row();
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<table data-transpose="1"><tr class="row-odd"><th>Foo</th>',
+ '<td class="foo">lala</td><td rel="tuuu">lula</td></tr><tr class="row-even"><th>Bar</th>',
+ '<td></td><td>lila</td></tr></table>'
+ ],
+ $instance->table( [], true )
+ );
+ }
+
+ public function testTransposedTable_Cell_Row_IsHtml() {
+
+ $instance = new HtmlTable();
+
+ // We need a dedicated header definition to support a table transpose
+ $instance->header( 'Foo' );
+ $instance->header( 'Bar' );
+
+ $instance->cell( 'lala', [ 'class' => 'foo' ] );
+ $instance->row();
+
+ $instance->cell( 'lula', [ 'rel' => 'tuuu' ] );
+ $instance->cell( 'lila' );
+ $instance->row();
+
+ $this->stringValidator->assertThatStringContains(
+ [
+ '<table data-transpose="1"><tbody><tr class="row-odd"><th>Foo</th><td class="foo">lala</td>',
+ '<td rel="tuuu">lula</td></tr><tr class="row-even"><th>Bar</th><td></td><td>lila</td></tr>',
+ '</tbody></table>'
+ ],
+ $instance->table( [], true, true )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTabsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTabsTest.php
new file mode 100644
index 00000000..89b01450
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlTabsTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\HtmlTabs;
+
+/**
+ * @covers \SMW\Utils\HtmlTabs
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlTabsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testTab_Contents() {
+
+ $instance = new HtmlTabs();
+ $instance->setActiveTab( 'foo' );
+ $instance->tab( 'foo', 'FOO' );
+ $instance->content( 'foo', '< ... bar ... >' );
+
+ $this->assertContains(
+ '<div class="smw-tabs foo-bar">' .
+ '<input id="tab-foo" class="nav-tab" type="radio" name="tabs" checked=""/>' .
+ '<label id="tab-label-foo" for="tab-foo" class="nav-label">FOO</label>' .
+ '<section id="tab-content-foo">< ... bar ... ></section>'.
+ '</div>',
+ $instance->buildHTML( [ 'class' => 'foo-bar' ] )
+ );
+ }
+
+ public function testTab_Contents_AutoChecked() {
+
+ $instance = new HtmlTabs();
+ $instance->tab( 'foo', 'FOO' );
+ $instance->content( 'foo', '< ... bar ... >' );
+
+ $this->assertContains(
+ '<div class="smw-tabs foo-bar">' .
+ '<input id="tab-foo" class="nav-tab" type="radio" name="tabs" checked=""/>' .
+ '<label id="tab-label-foo" for="tab-foo" class="nav-label">FOO</label>' .
+ '<section id="tab-content-foo">< ... bar ... ></section>'.
+ '</div>',
+ $instance->buildHTML( [ 'class' => 'foo-bar' ] )
+ );
+ }
+
+ public function testTab_Contents_Hidden() {
+
+ $instance = new HtmlTabs();
+
+ $instance->tab( 'foo', 'FOO', [ 'hide' => true ] );
+ $instance->content( 'foo', '< ... bar ... >' );
+
+ $this->assertContains(
+ '<div class="smw-tabs foo-bar"></div>',
+ $instance->buildHTML( [ 'class' => 'foo-bar' ] )
+ );
+ }
+
+ public function testTab_WithExtraHtml() {
+
+ $instance = new HtmlTabs();
+
+ $instance->tab( 'foo', 'FOO' );
+ $instance->content( 'foo', '< ... bar ... >' );
+ $instance->html( '<span>Foobar</span>' );
+
+ $this->assertContains(
+ '<span>Foobar</span>',
+ $instance->buildHTML()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlVTabsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlVTabsTest.php
new file mode 100644
index 00000000..13d425a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/HtmlVTabsTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\HtmlVTabs;
+
+/**
+ * @covers \SMW\Utils\HtmlVTabs
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class HtmlVTabsTest extends \PHPUnit_Framework_TestCase {
+
+ protected function setUp() {
+ HtmlVTabs::init();
+ }
+
+ public function testGetModules() {
+
+ $this->assertInternalType(
+ 'array',
+ HtmlVTabs::getModules()
+ );
+ }
+
+ public function testGetModuleStyles() {
+
+ $this->assertInternalType(
+ 'array',
+ HtmlVTabs::getModuleStyles()
+ );
+ }
+
+ public function testNav() {
+
+ $this->assertContains(
+ '<div class="smw-vtab-nav nav-right" data-foo="bar">FooHtml</div>',
+ HtmlVTabs::nav( 'FooHtml', [ 'data-foo' => 'bar' ] )
+ );
+ }
+
+ public function testActiveLink() {
+
+ $this->assertContains(
+ '<button id="vtab-item-tab-foo" class="smw-vtab-link nav-right active" data-baz="foobar" data-id="tab-foo" type="submit"><a href="#tab-foo">barLabel</a></button>',
+ HtmlVTabs::navLink( 'foo', 'barLabel', HtmlVTabs::IS_ACTIVE, [ 'data-baz' => 'foobar' ] )
+ );
+ }
+
+ public function testHiddenLink() {
+
+ $this->assertEmpty(
+ HtmlVTabs::navLink( 'foo', 'bar', HtmlVTabs::IS_HIDDEN, [ 'data-baz' => 'foobar' ] )
+ );
+ }
+
+ public function testFindActiveLink() {
+
+ $this->assertContains(
+ '<button id="vtab-item-tab-foo" class="smw-vtab-link nav-right active" data-baz="foobar" data-id="tab-foo" type="submit"><a href="#tab-foo">barLabel</a></button>',
+ HtmlVTabs::navLink( 'foo', 'barLabel', [ HtmlVTabs::FIND_ACTIVE_LINK => 'foo' ], [ 'data-baz' => 'foobar' ] )
+ );
+
+ $this->assertContains(
+ '<button id="vtab-item-tab-foo" class="smw-vtab-link nav-right" data-baz="foobar" data-id="tab-foo" type="submit"><a href="#tab-foo">barLabel</a></button>',
+ HtmlVTabs::navLink( 'foo', 'barLabel', [ HtmlVTabs::FIND_ACTIVE_LINK => 'bar' ], [ 'data-baz' => 'foobar' ] )
+ );
+ }
+
+ public function testLinkRight() {
+
+ $this->assertContains(
+ '<button id="vtab-item-tab-foo" class="smw-vtab-link nav-right" data-id="tab-foo" type="submit"><a href="#tab-foo">bar</a></button>',
+ HtmlVTabs::navLink( 'foo', 'bar' )
+ );
+ }
+
+ public function testLinkLeft() {
+
+ HtmlVTabs::setDirection( 'left' );
+
+ $this->assertContains(
+ '<button id="vtab-item-tab-foo" class="smw-vtab-link nav-left" data-id="tab-foo" type="submit"><a href="#tab-foo">bar</a></button>',
+ HtmlVTabs::navLink( 'foo', 'bar' )
+ );
+ }
+
+ public function testContentWithActiveDisplay() {
+
+ HtmlVTabs::navLink( 'foo', 'barLabel', HtmlVTabs::IS_ACTIVE );
+
+ $this->assertContains(
+ '<div id="tab-foo" class="smw-vtab-content" data-baz="foobar">bar</div>',
+ HtmlVTabs::content( 'foo', 'bar', [ 'data-baz' => 'foobar' ] )
+ );
+ }
+
+ public function testContentWithNoneDisplay() {
+
+ $this->assertContains(
+ '<div id="tab-foo2" class="smw-vtab-content" data-baz="foobar" style="display:none;">bar</div>',
+ HtmlVTabs::content( 'foo2', 'bar', [ 'data-baz' => 'foobar' ] )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ImageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ImageTest.php
new file mode 100644
index 00000000..87016967
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/ImageTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Image;
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\Utils\Image
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ImageTest extends \PHPUnit_Framework_TestCase {
+
+ public function testIsImage() {
+
+ $this->assertTrue(
+ Image::isImage( DIWikiPage::newFromText( 'Foo.png', NS_FILE ) )
+ );
+
+ $this->assertTrue(
+ Image::isImage( DIWikiPage::newFromText( '一二三.png', NS_FILE ) )
+ );
+
+ $this->assertFalse(
+ Image::isImage( DIWikiPage::newFromText( 'Foo.png', NS_MAIN ) )
+ );
+
+ $this->assertFalse(
+ Image::isImage( DIWikiPage::newFromText( 'Foo', NS_FILE ) )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/JsonSchemaValidatorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/JsonSchemaValidatorTest.php
new file mode 100644
index 00000000..56de260e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/JsonSchemaValidatorTest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use JsonSchema\Exception\ResourceNotFoundException;
+use JsonSchema\Validator as SchemaValidator;
+use JsonSerializable;
+use SMW\ApplicationFactory;
+use SMW\Utils\JsonSchemaValidator;
+
+/**
+ * @covers \SMW\Utils\JsonSchemaValidator
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class JsonSchemaValidatorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ JsonSchemaValidator::class,
+ new JsonSchemaValidator()
+ );
+
+ $applicationFactory = ApplicationFactory::getInstance();
+
+ $this->assertInstanceOf(
+ JsonSchemaValidator::class,
+ $applicationFactory->create( 'JsonSchemaValidator' )
+ );
+
+ $this->assertInstanceOf(
+ JsonSchemaValidator::class,
+ $applicationFactory->create( JsonSchemaValidator::class )
+ );
+ }
+
+ public function testNoSchemaValidator() {
+
+ $instance = new JsonSchemaValidator();
+
+ $this->assertFalse(
+ $instance->hasSchemaValidator()
+ );
+ }
+
+ public function testValidate() {
+
+ if ( !class_exists( SchemaValidator::class ) ) {
+ $this->markTestSkipped( 'JsonSchema\Validator is not available.' );
+ }
+
+ $data = $this->getMockBuilder( JsonSerializable::class )
+ ->setMethods( [ 'jsonSerialize' ] )
+ ->getMock();
+
+ $data->expects( $this->any() )
+ ->method( 'jsonSerialize' )
+ ->will( $this->returnValue( json_encode( [ 'Foo' ] ) ) );
+
+ $schemaValidator = $this->getMockBuilder( SchemaValidator::class )
+ ->setMethods( [ 'check' ] )
+ ->getMock();
+
+ $instance = new JsonSchemaValidator(
+ $schemaValidator
+ );
+
+ $instance->validate( $data, 'Foo' );
+
+ $this->assertTrue(
+ $instance->isValid()
+ );
+
+ $this->assertEmpty(
+ $instance->getErrors()
+ );
+ }
+
+ public function testValidateWhereSchemaValidatorThrowsException() {
+
+ if ( !class_exists( SchemaValidator::class ) ) {
+ $this->markTestSkipped( 'JsonSchema\Validator is not available.' );
+ }
+
+ $data = $this->getMockBuilder( JsonSerializable::class )
+ ->setMethods( [ 'jsonSerialize' ] )
+ ->getMock();
+
+ $data->expects( $this->any() )
+ ->method( 'jsonSerialize' )
+ ->will( $this->returnValue( json_encode( [ 'Foo' ] ) ) );
+
+ $schemaValidator = $this->getMockBuilder( SchemaValidator::class )
+ ->setMethods( [ 'check' ] )
+ ->getMock();
+
+ $schemaValidator->expects( $this->any() )
+ ->method( 'check' )
+ ->will($this->throwException( new ResourceNotFoundException() ) );
+
+ $instance = new JsonSchemaValidator(
+ $schemaValidator
+ );
+
+ $instance->validate( $data, 'Foo' );
+
+ $this->assertFalse(
+ $instance->isValid()
+ );
+
+ $this->assertNotEmpty(
+ $instance->getErrors()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LoggerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LoggerTest.php
new file mode 100644
index 00000000..a2c436d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LoggerTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Logger;
+
+/**
+ * @covers \SMW\Utils\Logger
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class LoggerTest extends \PHPUnit_Framework_TestCase {
+
+ private $logger;
+
+ protected function setUp() {
+
+ $this->logger = $this->getMockBuilder( '\Psr\Log\LoggerInterface' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ Logger::class,
+ new Logger( $this->logger )
+ );
+ }
+
+ /**
+ * @dataProvider logProvider
+ */
+ public function testLog( $role, $message, $context ) {
+
+ $this->logger->expects( $this->once() )
+ ->method( 'log' );
+
+ $instance = new Logger( $this->logger, $role );
+ $instance->log( 'Foo', $message, $context );
+ }
+
+ public function logProvider() {
+
+ yield [
+ Logger::ROLE_DEVELOPER,
+ 'Foo',
+ [ 'Foo' ]
+ ];
+
+ yield [
+ Logger::ROLE_DEVELOPER,
+ 'Foo',
+ [ 'Foo', [ 'Bar' => 123 ] ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LruTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LruTest.php
new file mode 100644
index 00000000..323a6f48
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/LruTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Lru;
+
+/**
+ * @covers \SMW\Utils\Lru
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class LruTest extends \PHPUnit_Framework_TestCase {
+
+ public function testSetGet() {
+
+ $instance = new Lru( 3 );
+
+ $instance->set( 'a', 3 );
+ $instance->set( 'b', 'abc' );
+ $instance->set( 'c', 'def' );
+ $instance->set( 'd', 2 );
+
+ $this->assertEquals(
+ [
+ 'b' => 'abc',
+ 'c' => 'def',
+ 'd' => 2
+ ],
+ $instance->toArray()
+ );
+
+ $this->assertEquals(
+ 'def',
+ $instance->get( 'c' )
+ );
+
+ $instance->get( 'b' );
+ $instance->set( 'foo', 'bar' );
+
+ $this->assertEquals(
+ [
+ 'b' => 'abc',
+ 'c' => 'def',
+ 'foo' => 'bar'
+ ],
+ $instance->toArray()
+ );
+
+ $instance->get( 'c' );
+ $instance->get( 'foo' );
+ $instance->set( 'foobar', 'xyz' );
+
+ $this->assertEquals(
+ [
+ 'c' => 'def',
+ 'foo' => 'bar',
+ 'foobar' => 'xyz'
+ ],
+ $instance->toArray()
+ );
+ }
+
+ public function testDelete() {
+
+ $instance = new Lru( 3 );
+
+ $instance->set( 'a', 3 );
+ $instance->delete( 'a' );
+
+ $this->assertEquals(
+ [],
+ $instance->toArray()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/NormalizerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/NormalizerTest.php
new file mode 100644
index 00000000..f21b2858
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/NormalizerTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Normalizer;
+
+/**
+ * @covers \SMW\Utils\Normalizer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class NormalizerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testReduceLengthTo() {
+
+ $this->assertEquals(
+ 'ABC',
+ Normalizer::reduceLengthTo( 'ABCDEF', 3 )
+ );
+
+ $this->assertEquals(
+ 'ABCDEF',
+ Normalizer::reduceLengthTo( 'ABCDEF' )
+ );
+
+ $this->assertEquals(
+ 'ABCD',
+ Normalizer::reduceLengthTo( 'ABCD EF', 4 )
+ );
+
+ $this->assertEquals(
+ 'ABC',
+ Normalizer::reduceLengthTo( 'ABC D EF', 4 )
+ );
+
+ $this->assertEquals(
+ 'ABCD',
+ Normalizer::reduceLengthTo( 'ABCD EF', 5 )
+ );
+
+ $this->assertEquals(
+ 'abc def gh',
+ Normalizer::reduceLengthTo( 'abc def gh in 123', 12 )
+ );
+ }
+
+ public function testToLowercase() {
+
+ $this->assertEquals(
+ 'abcdef',
+ Normalizer::toLowercase( 'ABCDEF' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/StatsFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/StatsFormatterTest.php
new file mode 100644
index 00000000..b5ead42a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/StatsFormatterTest.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\StatsFormatter;
+
+/**
+ * @covers \SMW\Utils\StatsFormatter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class StatsFormatterTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider statsProvider
+ */
+ public function testGetStatsFromFlatKey( $stats, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ StatsFormatter::getStatsFromFlatKey( $stats )
+ );
+ }
+
+ /**
+ * @dataProvider formatProvider
+ */
+ public function testFormat( $stats, $format, $expected ) {
+
+ $this->assertInternalType(
+ $expected,
+ StatsFormatter::format( $stats, $format )
+ );
+ }
+
+ public function formatProvider() {
+
+ $provider[] = [
+ [ 'Foo' => 1, 'Bar' => 1 ],
+ StatsFormatter::FORMAT_PLAIN,
+ 'string'
+ ];
+
+ $provider[] = [
+ [ 'Foo' => 1, 'Bar' => 1 ],
+ StatsFormatter::FORMAT_HTML,
+ 'string'
+ ];
+
+ $provider[] = [
+ [ 'Foo' => 1, 'Bar' => 1 ],
+ StatsFormatter::FORMAT_JSON,
+ 'string'
+ ];
+
+ $provider[] = [
+ [ 'Foo' => 1, 'Bar' => 1 ],
+ null,
+ 'array'
+ ];
+
+ return $provider;
+ }
+
+ public function statsProvider() {
+
+ $provider[] = [
+ [ 'Foo' => 1, 'Bar' => 1 ],
+ [
+ 'Foo' => 1,
+ 'Bar' => 1
+ ]
+ ];
+
+ $provider[] = [
+ [ 'Foo.foobar' => 1, 'Bar' => 1 ],
+ [
+ 'Foo' => [ 'foobar' => 1 ],
+ 'Bar' => 1
+ ]
+ ];
+
+ $provider[] = [
+ [ 'Foo.foobar' => 5, 'Bar' => 1, 'Foo.foobar.baz' => 1 ],
+ [
+ 'Foo' => [ 'foobar' => [ 5, 'baz' => 1 ] ],
+ 'Bar' => 1
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TempFileTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TempFileTest.php
new file mode 100644
index 00000000..b29fa1bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TempFileTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\TempFile;
+
+/**
+ * @covers \SMW\Utils\TempFile
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class TempFileTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGenerate() {
+
+ $instance = new TempFile();
+
+ $this->assertContains(
+ '6na5ojj24og0',
+ $instance->generate( 'Foo' )
+ );
+
+ $this->assertContains(
+ 'Bar7vmi67tsvkb0',
+ $instance->generate( 'Bar', 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TimerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TimerTest.php
new file mode 100644
index 00000000..41e7ca30
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TimerTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Timer;
+
+/**
+ * @covers \SMW\Utils\Timer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TimerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetElapsedTime() {
+
+ Timer::start( __CLASS__ );
+
+ $this->assertInternalType(
+ 'float',
+ Timer::getElapsedTime( __CLASS__ )
+ );
+
+ $this->assertInternalType(
+ 'float',
+ Timer::getElapsedTime( __CLASS__, 5 )
+ );
+ }
+
+ public function testGetElapsedTimeWithoutStart() {
+
+ $this->assertEquals(
+ 0,
+ Timer::getElapsedTime( 'Foo' )
+ );
+ }
+
+ public function testGetElapsedTimeAsLoggableMessage() {
+
+ $this->assertInternalType(
+ 'string',
+ Timer::getElapsedTimeAsLoggableMessage( 'Foo' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TokenizerTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TokenizerTest.php
new file mode 100644
index 00000000..d9f12a2f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Unit/Utils/TokenizerTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\Utils\Tokenizer;
+
+/**
+ * @covers \SMW\Utils\Tokenizer
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TokenizerTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider textProvider
+ */
+ public function testTokenize( $text, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ Tokenizer::tokenize( $text )
+ );
+ }
+
+ public function textProvider() {
+
+ $provider[] = [
+ 'foo',
+ [ 'foo' ]
+ ];
+
+ $provider[] = [
+ 'foo bar',
+ [ 'foo', 'bar' ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ByPageSemanticDataFinder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ByPageSemanticDataFinder.php
new file mode 100644
index 00000000..61bb4225
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ByPageSemanticDataFinder.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use ParserOutput;
+use SMW\ParserData;
+use SMW\SemanticData;
+use SMW\Store;
+use Title;
+use UnexpectedValueException;
+use User;
+use WikiPage;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class ByPageSemanticDataFinder {
+
+ protected $store = null;
+ protected $title = null;
+
+ /**
+ * @since 1.9.1
+ *
+ * @param Store $store
+ *
+ * @return PageDataFetcher
+ */
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param Title $title
+ *
+ * @return PageDataFetcher
+ */
+ public function setTitle( Title $title ) {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return SemanticData
+ */
+ public function fetchIncomingDataFromStore() {
+
+ $requestOptions = new \SMWRequestOptions();
+ $requestOptions->sort = true;
+
+ $subject = $this->getPageData()->getSubject();
+ $semanticData = new SemanticData( $subject );
+
+ $incomingProperties = $this->getStore()->getInProperties( $subject, $requestOptions );
+
+ foreach ( $incomingProperties as $property ) {
+ $values = $this->getStore()->getPropertySubjects( $property, null );
+
+ foreach ( $values as $value ) {
+ $semanticData->addPropertyObjectValue( $property, $value );
+ }
+ }
+
+ return $semanticData;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return SemanticData
+ */
+ public function fetchFromStore() {
+ return $this->getStore()->getSemanticData( $this->getPageData()->getSubject() );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return SemanticData
+ */
+ public function fetchFromOutput() {
+ return $this->getPageData()->getData();
+ }
+
+ protected function getPageData() {
+ return new ParserData( $this->getTitle(), $this->makeOutputFromPageRevision() );
+ }
+
+ protected function getPage() {
+ return WikiPage::factory( $this->getTitle() );
+ }
+
+ protected function makeOutputFromPageRevision() {
+
+ $wikiPage = $this->getPage();
+ $revision = $wikiPage->getRevision();
+
+ if ( $revision === null ) {
+ throw new UnexpectedValueException( 'Expected a valid Revision' );
+ }
+
+ $parserOutput = $wikiPage->getParserOutput(
+ $wikiPage->makeParserOptions( User::newFromId( $revision->getUser() ) ),
+ $revision->getId()
+ );
+
+ if ( $parserOutput instanceof ParserOutput ) {
+ return $parserOutput;
+ }
+
+ throw new UnexpectedValueException( 'Expected a ParserOutput object' );
+ }
+
+ protected function getTitle() {
+
+ if ( $this->title instanceof Title ) {
+ return $this->title;
+ }
+
+ throw new UnexpectedValueException( 'Expected a Title object' );
+ }
+
+ protected function getStore() {
+
+ if ( $this->store instanceof Store ) {
+ return $this->store;
+ }
+
+ throw new UnexpectedValueException( 'Expected a Store object' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/ConnectionProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/ConnectionProvider.php
new file mode 100644
index 00000000..07efd732
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/ConnectionProvider.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace SMW\Tests\Utils\Connection;
+
+use DatabaseBase;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ConnectionProvider implements IConnectionProvider {
+
+ /**
+ * @var DatabaseBase
+ */
+ protected $connection;
+
+ protected $id;
+
+ /**
+ * @since 2.0
+ *
+ * @param int $id
+ */
+ public function __construct( $id = DB_MASTER ) {
+ $this->id = $id;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return DatabaseBase
+ */
+ public function getConnection() {
+
+ if ( $this->connection === null ) {
+ $this->connection = wfGetDB( $this->id );
+ }
+
+ return $this->connection;
+ }
+
+ public function releaseConnection() {
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/TestDatabaseTableBuilder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/TestDatabaseTableBuilder.php
new file mode 100644
index 00000000..a68222e1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Connection/TestDatabaseTableBuilder.php
@@ -0,0 +1,297 @@
+<?php
+
+namespace SMW\Tests\Utils\Connection;
+
+use CloneDatabase;
+use HashBagOStuff;
+use ObjectCache;
+use RuntimeException;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+use SMW\Tests\Utils\PageCreator;
+use SMW\Store;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class TestDatabaseTableBuilder {
+
+ /**
+ * @var TestDatabaseTableBuilder
+ */
+ private static $instance = null;
+
+ /**
+ * @var Store
+ */
+ protected $store;
+
+ /**
+ * @var IConnectionProvider
+ */
+ protected $connectionProvider;
+
+ /**
+ * @var CloneDatabase
+ */
+ protected $cloneDatabase;
+
+ protected $supportedDatabaseTypes = [
+ 'mysql',
+ 'sqlite',
+ 'postgres'
+ ];
+
+ protected $availableDatabaseTypes = [];
+
+ private static $UTDB_PREFIX = null;
+ private static $MWDB_PREFIX = null;
+
+ private $dbSetup = false;
+
+ /**
+ * @since 2.0
+ *
+ * @param Store $store
+ * @param IConnectionProvider $connectionProvider
+ */
+ public function __construct( Store $store, IConnectionProvider $connectionProvider ) {
+ $this->store = $store;
+ $this->connectionProvider = $connectionProvider;
+ $this->availableDatabaseTypes = $this->supportedDatabaseTypes;
+
+ self::$UTDB_PREFIX = 'sunittest_';
+ self::$MWDB_PREFIX = $GLOBALS['wgDBprefix'];
+
+ // MediaWikiTestCase changes the GLOBAL state therefore
+ // we have to ensure that we really use the original DB and
+ // not an existing 'unittest_' created by a MW test
+ if ( self::$MWDB_PREFIX === 'unittest_' ) {
+ self::$MWDB_PREFIX = '';
+ }
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Store $store
+ *
+ * @return self
+ */
+ public static function getInstance( Store $store ) {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self( $store, new ConnectionProvider() );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string|null $databaseType
+ *
+ * @return self
+ */
+ public function removeAvailableDatabaseType( $databaseType = null ) {
+ $this->availableDatabaseTypes = array_diff( $this->supportedDatabaseTypes, (array)$databaseType );
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @throws RuntimeException
+ */
+ public function doBuild() {
+
+ if ( !$this->isAvailableDatabaseType() ) {
+ throw new RuntimeException( 'Requested DB type is not available through this installer' );
+ }
+
+ ObjectCache::$instances[CACHE_DB] = new HashBagOStuff();
+
+ // Avoid Error while sending QUERY packet / SqlBagOStuff seen on MW 1.24
+ // https://s3.amazonaws.com/archive.travis-ci.org/jobs/30408638/log.txt
+ ObjectCache::$instances[CACHE_ANYTHING] = new HashBagOStuff();
+
+ $GLOBALS['wgDevelopmentWarnings'] = true;
+ $GLOBALS['wgMainCacheType'] = CACHE_NONE;
+ $GLOBALS['wgMessageCacheType'] = CACHE_NONE;
+ $GLOBALS['wgParserCacheType'] = CACHE_NONE;
+ $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE;
+ $GLOBALS['wgUseDatabaseMessages'] = false;
+
+ $this->setupDatabaseTables();
+ $this->rollbackOpenDatabaseTransactions();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function doDestroy() {
+ $this->destroyDatabaseTables();
+ $this->rollbackOpenDatabaseTransactions();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return string
+ */
+ public function getDBPrefix() {
+ return self::$UTDB_PREFIX;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return DatabaseBase
+ */
+ public function getDBConnection() {
+ return $this->connectionProvider->getConnection();
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return ConnectionProvider
+ */
+ public function getConnectionProvider() {
+ return $this->connectionProvider;
+ }
+
+ /**
+ * @see MediaWikiTestCase::listTables
+ */
+ protected function generateListOfTables() {
+
+ $dbConnection = $this->getDBConnection();
+
+ $tables = $dbConnection->listTables(
+ self::$MWDB_PREFIX,
+ __METHOD__
+ );
+
+ if ( $dbConnection->getType() === 'mysql' && method_exists( $dbConnection, 'listViews' ) ) {
+
+ # bug 43571: cannot clone VIEWs under MySQL
+ $views = $dbConnection->listViews(
+ self::$MWDB_PREFIX,
+ __METHOD__
+ );
+
+ $tables = array_diff( $tables, $views );
+ }
+
+ $tables = array_map( [ $this, 'unprefixTable' ], $tables );
+
+ // Don't duplicate test tables from the previous failed run
+ $tables = array_filter( $tables, [ $this, 'isNotUnittest' ] );
+
+ // @see MediaWikiTestCase::listTables
+ if ( $dbConnection->getType() === 'sqlite' ) {
+ $tables = array_filter( $tables, [ $this, 'isNotSearchindex' ] );
+ }
+
+ return $tables;
+ }
+
+ private function setupDatabaseTables() {
+
+ if ( $this->dbSetup ) {
+ return true;
+ }
+
+ $this->cloneDatabaseTables();
+ $this->store->setup( false );
+ $this->createDummyPage();
+
+ $this->dbSetup = true;
+ }
+
+ private function cloneDatabaseTables() {
+
+ $tablesToBeCloned = $this->generateListOfTables();
+
+ // MW's DatabaseSqlite does some magic on its own therefore
+ // we force our way
+ if ( $this->getDBConnection()->getType() === 'sqlite' ) {
+ CloneDatabase::changePrefix( self::$MWDB_PREFIX );
+ }
+
+ $this->cloneDatabase = new CloneDatabase(
+ $this->getDBConnection(),
+ $tablesToBeCloned,
+ $this->getDBPrefix()
+ );
+
+ // Ensure no leftovers
+ if ( $this->getDBConnection()->getType() === 'postgres' ) {
+ $this->cloneDatabase->destroy( true );
+ }
+
+ // Rebuild the DB (in order to exclude temporary table usage)
+ // otherwise some tests will fail with
+ // "Error: 1137 Can't reopen table" on MySQL (see Issue #80)
+ $this->cloneDatabase->useTemporaryTables( false );
+ $this->cloneDatabase->cloneTableStructure();
+
+ // #3197
+ // @see https://github.com/wikimedia/mediawiki/commit/6badc7415684df54d6672098834359223b859507
+ CloneDatabase::changePrefix( self::$UTDB_PREFIX );
+ }
+
+ private function createDummyPage() {
+
+ $pageCreator = new PageCreator();
+ $pageCreator
+ ->createPage( Title::newFromText( 'SMWUTDummyPage' ) )
+ ->doEdit( 'SMW dummy page' );
+ }
+
+ private function destroyDatabaseTables() {
+
+ if ( !$this->cloneDatabase instanceof CloneDatabase ) {
+ throw new RuntimeException( 'CloneDatabase instance is missing, unable to destory the database tables' );
+ }
+
+ $this->cloneDatabase->destroy( true );
+ $this->availableDatabaseTypes = $this->supportedDatabaseTypes;
+ $this->dbSetup = false;
+ }
+
+ private function rollbackOpenDatabaseTransactions() {
+
+ if ( $this->getDBConnection() ) {
+
+ while ( $this->getDBConnection()->trxLevel() > 0 ) {
+ $this->getDBConnection()->rollback();
+ }
+
+ // 1.26 was mde protected
+ // $this->getDBConnection()->ignoreErrors( false );
+ }
+ }
+
+ private function unprefixTable( $tableName ) {
+ return substr( $tableName, strlen( self::$MWDB_PREFIX ) );
+ }
+
+ private function isNotUnittest( $table ) {
+ return strpos( $table, 'unittest_' ) === false && strpos( $table, 'sunittest_' ) === false;
+ }
+
+ private function isNotSearchindex( $table ) {
+ return strpos( $table, 'searchindex' ) === false && strpos( $table, 'smw_ft_search' ) === false;
+ }
+
+ private function isAvailableDatabaseType() {
+ return in_array( $this->getDBConnection()->getType(), $this->availableDatabaseTypes );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/BulkFileProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/BulkFileProvider.php
new file mode 100644
index 00000000..22dfded9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/BulkFileProvider.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace SMW\Tests\Utils\File;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class BulkFileProvider {
+
+ /**
+ * @var string
+ */
+ private $path = null;
+
+ /**
+ * @var string
+ */
+ private $extension = 'json';
+
+ /**
+ * @since 2.1
+ *
+ * @param string $path
+ */
+ public function __construct( $path ) {
+ $this->path = $path;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $extension
+ */
+ public function searchByFileExtension( $extension ) {
+ $this->extension = $extension;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getFiles() {
+
+ $path = $this->checkPathReadability( $this->path );
+
+ return $this->iterateToFindFilesForExtension(
+ $path,
+ $this->extension
+ );
+ }
+
+ private function checkPathReadability( $path ) {
+
+ $path = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $path );
+
+ if ( is_readable( $path ) ) {
+ return $path;
+ }
+
+ throw new RuntimeException( "Expected an accessible {$path} path" );
+ }
+
+ private function iterateToFindFilesForExtension( $path, $extension ) {
+
+ $files = [];
+
+ $directoryIterator = new \RecursiveDirectoryIterator( $path );
+
+ foreach ( new \RecursiveIteratorIterator( $directoryIterator ) as $fileInfo ) {
+ if ( strtolower( substr( $fileInfo->getFilename(), -( strlen( $extension ) + 1 ) ) ) === ( '.' . $extension ) ) {
+
+ // Use a shortcut to be sortable while keep files with same name
+ // in different directories distinct
+ $files[$fileInfo->getFilename() . ' (' . substr( md5( $fileInfo->getPathname() ), 0, 5 ) .')'] = $fileInfo->getPathname();
+ }
+ }
+
+ asort( $files );
+
+ return $files;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/ContentsReader.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/ContentsReader.php
new file mode 100644
index 00000000..a9ac8a37
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/ContentsReader.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Tests\Utils\File;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ContentsReader {
+
+ /**
+ * @since 3.0
+ *
+ * @param string $file
+ *
+ * @return string
+ */
+ public static function readContentsFrom( $file ) {
+
+ $file = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $file );
+
+ if ( !is_readable( $file ) ) {
+ throw new RuntimeException( "Could not open or read: $file" );
+ }
+
+ $contents = file_get_contents( $file );
+
+ // http://php.net/manual/en/function.file-get-contents.php
+ $contents = mb_convert_encoding(
+ $contents,
+ 'UTF-8',
+ mb_detect_encoding( $contents, 'UTF-8, ISO-8859-1, ISO-8859-2', true )
+ );
+
+ // https://stackoverflow.com/questions/2115549/php-difference-between-r-n-and-n
+ return str_replace( "\r\n", "\n", $contents );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/DummyFileCreator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/DummyFileCreator.php
new file mode 100644
index 00000000..102c44d6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/DummyFileCreator.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace SMW\Tests\Utils\File;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DummyFileCreator {
+
+ /**
+ * @var string
+ */
+ private $desiredDestName;
+
+ /**
+ * @var string
+ */
+ private $file = '';
+
+ /**
+ * @var integer
+ */
+ private $size = 100;
+
+ /**
+ * @since 2.1
+ *
+ * @param string $desiredDestName
+ *
+ * @return string
+ */
+ public function createEmptyFile( $desiredDestName ) {
+ $this->file = $this->createFile( $desiredDestName );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $desiredDestName
+ * @param string $contentCopyPath
+ *
+ * @return string
+ */
+ public function createFileWithCopyFrom( $desiredDestName, $contentCopyPath ) {
+ $this->file = $this->createFile( $desiredDestName, file_get_contents( $this->getFile( $contentCopyPath ) ) );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getPath() {
+ return $this->file;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function delete() {
+ unlink( $this->file );
+ }
+
+ private function createFile( $desiredDestName, $content = '' ) {
+
+ $filename = $this->getLocationForTemporaryFile( $desiredDestName );
+
+ $fh = fopen( $filename, 'w' );
+
+ if ( $content === '' ) {
+ ftruncate( $fh, $this->size );
+ } else {
+ fwrite( $fh, $content );
+ }
+
+ fclose( $fh );
+
+ return $this->getFile( $filename );
+ }
+
+ private function getLocationForTemporaryFile( $desiredDestName ) {
+ return sys_get_temp_dir() . '/' . $desiredDestName;
+ }
+
+ private function getFile( $path ) {
+
+ $path = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $path );
+
+ if ( is_readable( $path ) ) {
+ return $path;
+ }
+
+ throw new RuntimeException( "Expected an accessible {$path}" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/JsonFileReader.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/JsonFileReader.php
new file mode 100644
index 00000000..dfe97b55
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/JsonFileReader.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace SMW\Tests\Utils\File;
+
+use RuntimeException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class JsonFileReader {
+
+ /**
+ * @var string|null
+ */
+ private $file = null;
+
+ /**
+ * @var array|null
+ */
+ private $contents = null;
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $file
+ */
+ public function __construct( $file = null ) {
+ $this->setFile( $file );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $file
+ */
+ public function setFile( $file ) {
+ $this->file = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $file );
+ $this->contents = null;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ * @throws RuntimeException
+ */
+ public function read() {
+
+ if ( $this->contents === null && $this->isReadable() ) {
+ $this->contents = $this->decodeJsonFileContentsToArray( $this->file );
+ }
+
+ if ( $this->contents !== null ) {
+ return $this->contents;
+ }
+
+ throw new RuntimeException( "Expected a readable {$this->file} file" );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return boolean
+ */
+ public function isReadable() {
+ return is_file( $this->file ) && is_readable( $this->file );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return integer
+ * @throws RuntimeException
+ */
+ public function getModificationTime() {
+
+ if ( $this->isReadable() ) {
+ return filemtime( $this->file );
+ }
+
+ throw new RuntimeException( "Expected a readable {$this->file} file" );
+ }
+
+ private function decodeJsonFileContentsToArray( $file ) {
+
+ $json = file_get_contents( $file );
+
+ $json = preg_replace(
+ '~ ("(?:[^\\\"]+|\\\.)*") |' . // preserve strings
+ '/\* (?:[^*]+|\*+(?!/))* \*/ |' . // strip multi-line comments
+ '//\V* ~sx', // strip //-comments
+ '$1', $json );
+
+ $contents = json_decode( $json, true );
+
+ if ( $contents !== null && json_last_error() === JSON_ERROR_NONE ) {
+ return $contents;
+ }
+
+ throw new RuntimeException( $this->printDescriptiveJsonError( json_last_error() ) );
+ }
+
+ private function printDescriptiveJsonError( $errorCode ) {
+
+ $errorMessages = [
+ JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch, malformed JSON',
+ JSON_ERROR_CTRL_CHAR => 'Unexpected control character found, possibly incorrectly encoded',
+ JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
+ JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded'
+ ];
+
+ return sprintf(
+ "Expected a JSON compatible format but failed with '%s'",
+ $errorMessages[$errorCode]
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/LocalFileUpload.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/LocalFileUpload.php
new file mode 100644
index 00000000..60ed77d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/File/LocalFileUpload.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace SMW\Tests\Utils\File;
+
+use RuntimeException;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use UploadBase;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class LocalFileUpload extends UploadBase {
+
+ /**
+ * @var boolean
+ */
+ private $removeTemporaryFile = true;
+
+ /**
+ * @var string
+ */
+ private $localUploadPath;
+
+ /**
+ * @var string
+ */
+ private $desiredDestName;
+
+ /**
+ * @var DummyFileCreator
+ */
+ private $dummyFileCreator;
+
+ /**
+ * @var string
+ */
+ private $error = '';
+
+ /**
+ * @since 2.1
+ *
+ * @param string $localUploadPath
+ * @param string $desiredDestName
+ */
+ public function __construct( $localUploadPath = '', $desiredDestName = '' ) {
+ $this->localUploadPath = $localUploadPath;
+ $this->desiredDestName = $desiredDestName;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param DummyFileCreator $dummyFileCreator
+ */
+ public function setDummyFileCreator( DummyFileCreator $dummyFileCreator ) {
+ $this->dummyFileCreator = $dummyFileCreator;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return string
+ */
+ public function getUploadError() {
+ return $this->error;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function delete() {
+ unlink( $this->localUploadPath );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $localUploadPath
+ * @param string $desiredDestName
+ * @param string $pageText
+ * @param string $comment
+ *
+ * @return boolean
+ */
+ public function doUploadCopyFromLocation( $localUploadPath, $desiredDestName, $pageText = '', $comment = '' ) {
+
+ if ( !$this->dummyFileCreator instanceof DummyFileCreator ) {
+ throw new RuntimeException( "Expected a DummyFileCreator instance." );
+ }
+
+ $this->dummyFileCreator->createFileWithCopyFrom(
+ $desiredDestName,
+ $localUploadPath
+ );
+
+ $this->doUploadFromLocation(
+ $this->dummyFileCreator->getPath(),
+ $desiredDestName,
+ $pageText,
+ $comment
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $localUploadPath
+ * @param string $desiredDestName
+ * @param string $pageText
+ * @param string $comment
+ *
+ * @return boolean
+ */
+ public function doUploadFromLocation( $localUploadPath, $desiredDestName, $pageText = '', $comment = '' ) {
+
+ $localUploadPath = $this->createReadablePath( $localUploadPath );
+
+ $this->initializePathInfo(
+ $desiredDestName,
+ $localUploadPath,
+ filesize( $localUploadPath ),
+ $this->removeTemporaryFile
+ );
+
+ $status = $this->performUpload(
+ $comment,
+ $pageText,
+ false,
+ new MockSuperUSer()
+ );
+
+ if ( !$status->isGood() ) {
+ $this->error = $status->getWikiText();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $pageText
+ * @param string $comment
+ *
+ * @return boolean
+ */
+ public function doUpload( $pageText = '', $comment = '' ) {
+ return $this->doUploadFromLocation( $this->localUploadPath, $this->desiredDestName, $pageText, $comment );
+ }
+
+ /**
+ * @see UploadBase::initializeFromRequest
+ */
+ public function initializeFromRequest( &$request ) {
+ }
+
+ /**
+ * @see UploadBase::getSourceType
+ */
+ public function getSourceType() {
+ return 'file';
+ }
+
+ private function createReadablePath( $path ) {
+
+ $path = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $path );
+
+ if ( is_readable( $path ) ) {
+ return $path;
+ }
+
+ throw new RuntimeException( "Expected an accessible {$path}" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/BerlinFactsheet.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/BerlinFactsheet.php
new file mode 100644
index 00000000..695cd0d0
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/BerlinFactsheet.php
@@ -0,0 +1,273 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Facts;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Subobject;
+use SMW\Tests\Utils\Fixtures\Properties\AreaProperty;
+use SMW\Tests\Utils\Fixtures\Properties\CityCategory;
+use SMW\Tests\Utils\Fixtures\Properties\FoundedProperty;
+use SMW\Tests\Utils\Fixtures\Properties\LocatedInProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationDensityProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationProperty;
+use SMW\Tests\Utils\Fixtures\Properties\TemperatureProperty;
+use SMW\Tests\Utils\Fixtures\Properties\YearProperty;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class BerlinFactsheet {
+
+ /**
+ * @var DIWikiPage
+ */
+ private $targetSubject = null;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage|null $targetSubject
+ */
+ public function __construct( DIWikiPage $targetSubject = null ) {
+ $this->targetSubject = $targetSubject;
+
+ if ( $this->targetSubject === null ) {
+ $this->targetSubject = $this->asSubject();
+ }
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $targetSubject
+ */
+ public function setTargetSubject( DIWikiPage $targetSubject ) {
+ $this->targetSubject = $targetSubject;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWikiPage
+ */
+ public function asSubject() {
+ return new DIWikiPage( 'Berlin', NS_MAIN );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function asEntity() {
+
+ $semanticData = new SemanticData( $this->asSubject() );
+ $semanticData->addDataValue( $this->getAreaValue() );
+ $semanticData->addDataValue( $this->getAverageHighTemperatureValue() );
+ $semanticData->addDataValue( $this->getPopulationValue() );
+ $semanticData->addDataValue( $this->getPopulationDensityValue() );
+ $semanticData->addDataValue( $this->getLocatedInValue() );
+ $semanticData->addDataValue( $this->getFoundedValue() );
+ $semanticData->addSubobject( $this->getDemographics() );
+
+ $cityCategory = new CityCategory();
+
+ $semanticData->addDataValue( $cityCategory->getCategoryValue() );
+
+ return $semanticData;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DataValue
+ */
+ public function getLocatedInValue() {
+
+ $locatedInProperty = new LocatedInProperty();
+
+ return $this->dataValueFactory->newDataValueByItem(
+ new DIWikiPage( 'Germany', NS_MAIN ),
+ $locatedInProperty->getProperty()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getAreaValue() {
+
+ $areaProperty = new AreaProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $areaProperty->getProperty(),
+ '891.85 km²'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getFoundedValue() {
+
+ $foundedProperty = new FoundedProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $foundedProperty->getProperty(),
+ '1237'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getAverageHighTemperatureValue() {
+
+ $temperatureProperty = new TemperatureProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $temperatureProperty->getProperty(),
+ '13.4 °C',
+ 'Average high temperature'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getPopulationValue() {
+
+ $populationProperty = new PopulationProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $populationProperty->getProperty(),
+ '3517424'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Demographics_of_Berlin
+ *
+ * @return DataValue
+ */
+ public function getPopulationDensityValue() {
+
+ if ( $this->targetSubject === null ) {
+ throw new RuntimeException( 'Expected a target subject' );
+ }
+
+ $populationDensityProperty = new PopulationDensityProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $populationDensityProperty->getProperty(),
+ '3900;1 km²',
+ 'Population density',
+ $this->targetSubject
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Demographics_of_Berlin
+ *
+ * @return Subobject
+ */
+ public function getDemographics() {
+
+ if ( $this->targetSubject === null ) {
+ throw new RuntimeException( 'Expected a target subject' );
+ }
+
+ $subobject = new Subobject( $this->targetSubject->getTitle() );
+ $subobject->setEmptyContainerForId( 'Berlin#Demographics' );
+
+ $yearProperty = new YearProperty();
+
+ $yearDataValue = $this->dataValueFactory->newDataValueByProperty(
+ $yearProperty->getProperty(),
+ '2013'
+ );
+
+ $subobject->addDataValue( $yearDataValue );
+ $subobject->addDataValue( $this->getAreaValue() );
+ $subobject->addDataValue( $this->getPopulationValue() );
+ $subobject->addDataValue( $this->getPopulationDensityValue() );
+
+ return $subobject;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function purge() {
+
+ $subjects = [];
+
+ $subjects[] = $this->asSubject();
+ $subjects[] = $this->targetSubject;
+ $subjects[] = $this->getFoundedValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getAreaValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getAverageHighTemperatureValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getPopulationValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getPopulationDensityValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getDemographics()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getLocatedInValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getLocatedInValue()->getDataItem();
+
+ // Record type needs extra attention
+ $dataItems = $this->getPopulationDensityValue()->getDataItems();
+
+ foreach ( $dataItems as $dataItem ) {
+ $subjects[] = $dataItem;
+ }
+
+ // Clean-up subobject
+ $properties = $this->getDemographics()->getSemanticData()->getProperties();
+
+ foreach ( $properties as $property ) {
+ $subjects[] = $property->getDiWikiPage();
+ }
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+
+ foreach ( $subjects as $subject ) {
+ if ( $subject instanceof DIWikiPage ) {
+ $pageDeleter->deletePage( $subject->getTitle() );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/FranceFactsheet.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/FranceFactsheet.php
new file mode 100644
index 00000000..f864442c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/FranceFactsheet.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Facts;
+
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Tests\Utils\Fixtures\Properties\CountryCategory;
+use SMW\Tests\Utils\Fixtures\Properties\LocatedInProperty;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FranceFactsheet {
+
+ /**
+ * @var DIWikiPage
+ */
+ private $targetSubject = null;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage|null $targetSubject
+ */
+ public function __construct( DIWikiPage $targetSubject = null ) {
+ $this->targetSubject = $targetSubject;
+
+ if ( $this->targetSubject === null ) {
+ $this->targetSubject = $this->asSubject();
+ }
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $targetSubject
+ */
+ public function setTargetSubject( DIWikiPage $targetSubject ) {
+ $this->targetSubject = $targetSubject;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWikiPage
+ */
+ public function asSubject() {
+ return new DIWikiPage( 'France', NS_MAIN );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function asEntity() {
+
+ $semanticData = new SemanticData( $this->asSubject() );
+ $semanticData->addDataValue( $this->getLocatedInValue() );
+
+ $countryCategory = new CountryCategory();
+
+ $semanticData->addDataValue( $countryCategory->getCategoryValue() );
+
+ return $semanticData;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DataValue
+ */
+ public function getLocatedInValue() {
+
+ $locatedInProperty = new LocatedInProperty();
+
+ return $this->dataValueFactory->newDataValueByItem(
+ DIWikiPage::newFromText( 'European Union', NS_MAIN ),
+ $locatedInProperty->getProperty(),
+ 'EU'
+ );
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function purge() {
+
+ $subjects = [];
+
+ $subjects[] = $this->asSubject();
+ $subjects[] = $this->targetSubject;
+ $subjects[] = $this->getLocatedInValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getLocatedInValue()->getDataItem();
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+
+ foreach ( $subjects as $subject ) {
+ if ( $subject instanceof DIWikiPage ) {
+ $pageDeleter->deletePage( $subject->getTitle() );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/ParisFactsheet.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/ParisFactsheet.php
new file mode 100644
index 00000000..d8ad459c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Facts/ParisFactsheet.php
@@ -0,0 +1,254 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Facts;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Subobject;
+use SMW\Tests\Utils\Fixtures\Properties\AreaProperty;
+use SMW\Tests\Utils\Fixtures\Properties\CityCategory;
+use SMW\Tests\Utils\Fixtures\Properties\LocatedInProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationDensityProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationProperty;
+use SMW\Tests\Utils\Fixtures\Properties\TemperatureProperty;
+use SMW\Tests\Utils\Fixtures\Properties\YearProperty;
+use SMW\Tests\Utils\UtilityFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ParisFactsheet {
+
+ /**
+ * @var DIWikiPage
+ */
+ private $targetSubject = null;
+
+ /**
+ * @var DataValueFactory
+ */
+ private $dataValueFactory;
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage|null $targetSubject
+ */
+ public function __construct( DIWikiPage $targetSubject = null ) {
+ $this->targetSubject = $targetSubject;
+
+ if ( $this->targetSubject === null ) {
+ $this->targetSubject = $this->asSubject();
+ }
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param DIWikiPage $targetSubject
+ */
+ public function setTargetSubject( DIWikiPage $targetSubject ) {
+ $this->targetSubject = $targetSubject;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWikiPage
+ */
+ public function asSubject() {
+ return new DIWikiPage( 'Paris', NS_MAIN );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function asEntity() {
+
+ $semanticData = new SemanticData( $this->asSubject() );
+ $semanticData->addDataValue( $this->getAreaValue() );
+ $semanticData->addDataValue( $this->getAverageHighTemperatureValue() );
+ $semanticData->addDataValue( $this->getPopulationValue() );
+ $semanticData->addDataValue( $this->getPopulationDensityValue() );
+ $semanticData->addDataValue( $this->getLocatedInValue() );
+ $semanticData->addSubobject( $this->getDemographics() );
+
+ $cityCategory = new CityCategory();
+
+ $semanticData->addDataValue( $cityCategory->getCategoryValue() );
+
+ return $semanticData;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DataValue
+ */
+ public function getLocatedInValue() {
+
+ $locatedInProperty = new LocatedInProperty();
+
+ return $this->dataValueFactory->newDataValueByItem(
+ new DIWikiPage( 'France', NS_MAIN ),
+ $locatedInProperty->getProperty()
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getAreaValue() {
+
+ $areaProperty = new AreaProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $areaProperty->getProperty(),
+ '105.40 km²',
+ 'City of Paris'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Berlin
+ *
+ * @return DataValue
+ */
+ public function getAverageHighTemperatureValue() {
+
+ $temperatureProperty = new TemperatureProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $temperatureProperty->getProperty(),
+ '16.0 °C',
+ 'Average high temperature'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Demographics_of_Paris
+ *
+ * @return DataValue
+ */
+ public function getPopulationValue() {
+
+ $populationProperty = new PopulationProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $populationProperty->getProperty(),
+ '2234105'
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Demographics_of_Paris
+ *
+ * @return DataValue
+ */
+ public function getPopulationDensityValue() {
+
+ if ( $this->targetSubject === null ) {
+ throw new RuntimeException( 'Expected a target subject' );
+ }
+
+ $populationDensityProperty = new PopulationDensityProperty();
+
+ return $this->dataValueFactory->newDataValueByProperty(
+ $populationDensityProperty->getProperty(),
+ '20169;1 km²',
+ 'Population density',
+ $this->targetSubject
+ );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @see https://en.wikipedia.org/wiki/Demographics_of_Paris
+ *
+ * @return Subobject
+ */
+ public function getDemographics() {
+
+ if ( $this->targetSubject === null ) {
+ throw new RuntimeException( 'Expected a target subject' );
+ }
+
+ $subobject = new Subobject( $this->targetSubject->getTitle() );
+ $subobject->setEmptyContainerForId( 'Paris#Demographics' );
+
+ $yearProperty = new YearProperty();
+
+ $yearDataValue = $this->dataValueFactory->newDataValueByProperty(
+ $yearProperty->getProperty(),
+ '2009'
+ );
+
+ $subobject->addDataValue( $yearDataValue );
+ $subobject->addDataValue( $this->getAreaValue() );
+ $subobject->addDataValue( $this->getPopulationValue() );
+ $subobject->addDataValue( $this->getPopulationDensityValue() );
+
+ return $subobject;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function purge() {
+
+ $subjects = [];
+
+ $subjects[] = $this->asSubject();
+ $subjects[] = $this->targetSubject;
+ $subjects[] = $this->getAreaValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getAverageHighTemperatureValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getPopulationValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getPopulationDensityValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getDemographics()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getLocatedInValue()->getProperty()->getDiWikiPage();
+ $subjects[] = $this->getLocatedInValue()->getDataItem();
+
+ // Record type needs extra attention
+ $dataItems = $this->getPopulationDensityValue()->getDataItems();
+
+ foreach ( $dataItems as $dataItem ) {
+ $subjects[] = $dataItem;
+ }
+
+ // Clean-up subobject
+ $properties = $this->getDemographics()->getSemanticData()->getProperties();
+
+ foreach ( $properties as $property ) {
+ $subjects[] = $property->getDiWikiPage();
+ }
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+
+ foreach ( $subjects as $subject ) {
+ if ( $subject instanceof DIWikiPage ) {
+ $pageDeleter->deletePage( $subject->getTitle() );
+ }
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.json b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.json
new file mode 100644
index 00000000..bf85abf4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.json
@@ -0,0 +1,13 @@
+{
+ "text": "Lorem ipsum dolor sit amet consectetuer eu vitae consectetuer ac accumsan.",
+ "keywords": [
+ "Tellus Aenean",
+ "Mauris Aenean neque augue"
+ ],
+ "content": [
+ {
+ "name": "convallis interdum a nibh laoreet",
+ "url": "http://example.org"
+ }
+ ]
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.txt b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.txt
new file mode 100644
index 00000000..69ce20a4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/File/LoremIpsum.txt
@@ -0,0 +1,3 @@
+Lorem ipsum dolor sit amet consectetuer eu vitae consectetuer ac accumsan. Tellus Aenean porta odio volutpat vel cursus et wisi eu ac. Id Curabitur nec congue dolor magna Pellentesque Suspendisse et est Nulla. Pharetra neque Nullam ornare mauris feugiat ultrices pellentesque dui pretium libero. Tellus Curabitur Lorem et enim id.
+
+Justo id convallis interdum a nibh laoreet Mauris Aenean neque augue. Pretium non eget Phasellus malesuada justo ut gravida Maecenas orci ante. Vestibulum sociis Lorem Nulla Phasellus at Nam adipiscing ac Sed ut. Mauris rhoncus ut faucibus tempus laoreet lorem morbi eu nascetur fermentum. Consectetuer congue ut aliquam leo nibh eget auctor nibh ullamcorper nonummy. Ac nibh.
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesCleaner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesCleaner.php
new file mode 100644
index 00000000..617ef0e3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesCleaner.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures;
+
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FixturesCleaner {
+
+ /**
+ * @since 2.1
+ *
+ * @param array $subjects
+ *
+ * @return FixturesCleaner
+ */
+ public function purgeSubjects( array $subjects ) {
+
+ $pageDeleter = UtilityFactory::getInstance()->newPageDeleter();
+ $pageDeleter->doDeletePoolOfPages( $subjects );
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesCleaner
+ */
+ public function purgeAllKnownFacts() {
+ $fixturesProvider = new FixturesProvider();
+ return $this->purgeFacts( $fixturesProvider->getListOfFactsheetInstances() );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $facts
+ *
+ * @return FixturesCleaner
+ */
+ public function purgeFacts( array $facts ) {
+
+ foreach ( $facts as $fact ) {
+ $fact->purge();
+ }
+
+ return $this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFactory.php
new file mode 100644
index 00000000..40edd221
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFactory.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FixturesFactory {
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesProvider
+ */
+ public function newFixturesProvider() {
+ return new FixturesProvider();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesCleaner
+ */
+ public function newFixturesCleaner() {
+ return new FixturesCleaner();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesFileProvider
+ */
+ public function newFixturesFileProvider() {
+ return new FixturesFileProvider();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFileProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFileProvider.php
new file mode 100644
index 00000000..71ba744a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesFileProvider.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures;
+
+use SMW\Tests\Utils\File\DummyFileCreator;
+use SMW\Tests\Utils\File\LocalFileUpload;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FixturesFileProvider {
+
+ /**
+ * @since 2.1
+ *
+ * @param string $desiredDestName
+ *
+ * @return DummyFileCreator
+ */
+ public function newDummyJsonFile( $desiredDestName ) {
+
+ $dummyFileCreator = new DummyFileCreator();
+ $dummyFileCreator->createFileWithCopyFrom( $desiredDestName, __DIR__ . '/File/' . 'LoremIpsum.json' );
+
+ return $dummyFileCreator;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $desiredDestName
+ *
+ * @return DummyFileCreator
+ */
+ public function newDummyTextFile( $desiredDestName ) {
+
+ $dummyFileCreator = new DummyFileCreator();
+ $dummyFileCreator->createFileWithCopyFrom( $desiredDestName, __DIR__ . '/File/' . 'LoremIpsum.txt' );
+
+ return $dummyFileCreator;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $desiredDestName
+ *
+ * @return LocalFileUpload
+ */
+ public function newUploadForDummyTextFile( $desiredDestName ) {
+
+ $dummyTextFile = $this->newDummyTextFile( $desiredDestName );
+
+ return new LocalFileUpload(
+ $dummyTextFile->getPath(),
+ $desiredDestName
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesProvider.php
new file mode 100644
index 00000000..b3f3893e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/FixturesProvider.php
@@ -0,0 +1,165 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures;
+
+use RuntimeException;
+use SMW\Store;
+use SMW\Tests\Utils\Fixtures\Facts\BerlinFactsheet;
+use SMW\Tests\Utils\Fixtures\Facts\FranceFactsheet;
+use SMW\Tests\Utils\Fixtures\Facts\ParisFactsheet;
+use SMW\Tests\Utils\Fixtures\Properties\AreaProperty;
+use SMW\Tests\Utils\Fixtures\Properties\BookRecordProperty;
+use SMW\Tests\Utils\Fixtures\Properties\CityCategory;
+use SMW\Tests\Utils\Fixtures\Properties\FoundedProperty;
+use SMW\Tests\Utils\Fixtures\Properties\LocatedInProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationDensityProperty;
+use SMW\Tests\Utils\Fixtures\Properties\PopulationProperty;
+use SMW\Tests\Utils\Fixtures\Properties\StatusProperty;
+use SMW\Tests\Utils\Fixtures\Properties\TitleProperty;
+use SMW\Tests\Utils\Fixtures\Properties\UrlProperty;
+use SMW\Tests\Utils\Fixtures\Properties\YearProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FixturesProvider {
+
+ private $factsheets = null;
+ private $properties = null;
+ private $categories = null;
+
+ /**
+ * @since 2.1
+ */
+ public function setupDependencies( Store $store ) {
+
+ // This needs to happen before access to a property object is granted
+
+ // $pageCreator = UtilityFactory::getInstance()->newPageCreator();
+
+ foreach ( $this->getListOfPropertyInstances() as $propertyInstance ) {
+ $store->updateData( $propertyInstance->getDependencies() );
+ }
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getListOfFactsheetInstances() {
+ return [
+ 'berlin' => new BerlinFactsheet(),
+ 'paris' => new ParisFactsheet(),
+ 'france' => new FranceFactsheet()
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getListOfPropertyInstances() {
+ return [
+ 'area' => new AreaProperty(),
+ 'populationdensity' => new PopulationDensityProperty(),
+ // 'capitalof' => new CapitalOfProperty(),
+ 'status' => new StatusProperty(),
+ 'population' => new PopulationProperty(),
+ 'founded' => new FoundedProperty(),
+ 'locatedin' => new LocatedInProperty(),
+ 'bookrecord' => new BookRecordProperty(),
+ 'year' => new YearProperty(),
+ 'title' => new TitleProperty(),
+ 'url' => new UrlProperty()
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return array
+ */
+ public function getListOfCategoryInstances() {
+ return [
+ 'city' => new CityCategory()
+ ];
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIProperty
+ * @throws RuntimeException
+ */
+ public function getProperty( $id ) {
+
+ $id = strtolower( $id );
+
+ if ( $this->properties === null ) {
+ $this->properties = $this->getListOfPropertyInstances();
+ };
+
+ if ( isset( $this->properties[$id] ) ) {
+ return $this->properties[$id]->getProperty();
+ }
+
+ throw new RuntimeException( "$id is an unknown requested property" );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIProperty
+ * @throws RuntimeException
+ */
+ public function getCategory( $id ) {
+
+ $id = strtolower( $id );
+
+ if ( $this->categories === null ) {
+ $this->categories = $this->getListOfCategoryInstances();
+ };
+
+ if ( isset( $this->categories[$id] ) ) {
+ return $this->categories[$id];
+ }
+
+ throw new RuntimeException( "$id is an unknown requested property" );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return Factsheet
+ * @throws RuntimeException
+ */
+ public function getFactsheet( $id ) {
+
+ $id = strtolower( $id );
+
+ if ( $this->factsheets === null ) {
+ $this->factsheets = $this->getListOfFactsheetInstances();
+ };
+
+ if ( isset( $this->factsheets[$id] ) ) {
+ return $this->factsheets[$id];
+ }
+
+ throw new RuntimeException( "$id is an unknown requested fact" );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesCleaner
+ */
+ public function getCleaner() {
+ return new FixturesCleaner();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/InvalidCustomRespositoryConnector.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/InvalidCustomRespositoryConnector.php
new file mode 100644
index 00000000..194f6889
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/InvalidCustomRespositoryConnector.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class InvalidCustomRespositoryConnector {
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/AreaProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/AreaProperty.php
new file mode 100644
index 00000000..f200f19b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/AreaProperty.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\SemanticData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class AreaProperty extends FixtureProperty {
+
+ /**
+ * @var array
+ */
+ private $conversionValues = [
+ '1 km²',
+ '0.38610 sq mi',
+ '1000 m²',
+ '247.1054 acre',
+ '988.4215 rood'
+ ];
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Area' );
+ $this->property->setPropertyTypeId( '_qty' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = parent::getDependencies();
+
+ $dataValueFactory = DataValueFactory::getInstance();
+
+ foreach( $this->conversionValues as $conversionValue ) {
+ $semanticData->addDataValue(
+ $dataValueFactory->newDataValueByProperty(
+ new DIProperty( '_CONV' ),
+ $conversionValue
+ )
+ );
+ }
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/BookRecordProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/BookRecordProperty.php
new file mode 100644
index 00000000..ecc13f11
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/BookRecordProperty.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+use SMW\SemanticData;
+use SMWDIBlob as DIBlob;
+
+/**
+ * Simplified book record
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class BookRecordProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Book record' );
+ $this->property->setPropertyTypeId( '_rec' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = parent::getDependencies();
+
+ $titleProperty = new TitleProperty();
+ $yearProperty = new YearProperty();
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_LIST' ),
+ new DIBlob(
+ $titleProperty->getProperty()->getKey() . ';' .
+ $yearProperty->getProperty()->getKey()
+ )
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CapitalOfProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CapitalOfProperty.php
new file mode 100644
index 00000000..57f338df
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CapitalOfProperty.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+use SMW\SemanticData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class CapitalOfProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Capital of' );
+ $this->property->setPropertyTypeId( '_wpg' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = parent::getDependencies();
+
+ $locatedInProperty = new LocatedInProperty();
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_SUBP' ),
+ $locatedInProperty->getProperty()->getDiWikiPage()
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CityCategory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CityCategory.php
new file mode 100644
index 00000000..eb27d661
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CityCategory.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWiKiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class CityCategory extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( '_INST' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWiKiPage
+ */
+ public function asSubject() {
+ return new DIWiKiPage( 'City', NS_CATEGORY );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DataValue
+ */
+ public function getCategoryValue() {
+ return DataValueFactory::getInstance()->newDataValueByItem(
+ $this->asSubject(),
+ $this->getProperty()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CoordinatesProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CoordinatesProperty.php
new file mode 100644
index 00000000..0554b8d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CoordinatesProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class CoordinatesProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Coordinates' );
+ $this->property->setPropertyTypeId( '_geo' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CountryCategory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CountryCategory.php
new file mode 100644
index 00000000..2f821be8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/CountryCategory.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWiKiPage;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class CountryCategory {
+
+ /**
+ * @var DIProperty
+ */
+ private $property = null;
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( '_INST' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DIWiKiPage
+ */
+ public function asSubject() {
+ return new DIWiKiPage( 'Country', NS_CATEGORY );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return DataValue
+ */
+ public function getCategoryValue() {
+ return DataValueFactory::getInstance()->newDataValueByItem(
+ $this->asSubject(),
+ $this->getProperty()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/DescriptionProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/DescriptionProperty.php
new file mode 100644
index 00000000..1e743b90
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/DescriptionProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class DescriptionProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Description' );
+ $this->property->setPropertyTypeId( '_txt' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/EmailProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/EmailProperty.php
new file mode 100644
index 00000000..0f8c7bec
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/EmailProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class EmailProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Email' );
+ $this->property->setPropertyTypeId( '_ema' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FixtureProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FixtureProperty.php
new file mode 100644
index 00000000..430a6841
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FixtureProperty.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\SemanticData;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+abstract class FixtureProperty {
+
+ /**
+ * @var DIProperty
+ */
+ protected $property = null;
+
+ /**
+ * @since 2.1
+ *
+ * @return DIProperty
+ */
+ public function getProperty() {
+ return $this->property;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = new SemanticData( $this->property->getDiWikiPage() );
+
+ $semanticData->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByProperty(
+ new DIProperty( '_TYPE' ),
+ $this->property->findPropertyTypeID()
+ )
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FoundedProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FoundedProperty.php
new file mode 100644
index 00000000..86bbf0ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/FoundedProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class FoundedProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Founded' );
+ $this->property->setPropertyTypeId( '_dat' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/LocatedInProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/LocatedInProperty.php
new file mode 100644
index 00000000..23ae40bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/LocatedInProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class LocatedInProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Located in' );
+ $this->property->setPropertyTypeId( '_wpg' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationDensityProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationDensityProperty.php
new file mode 100644
index 00000000..b9e65009
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationDensityProperty.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+use SMW\SemanticData;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PopulationDensityProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Population density' );
+ $this->property->setPropertyTypeId( '_rec' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = parent::getDependencies();
+
+ $populationProperty = new PopulationProperty();
+ $areaProperty = new AreaProperty();
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_LIST' ),
+ new DIBlob(
+ $populationProperty->getProperty()->getKey() . ';' .
+ $areaProperty->getProperty()->getKey()
+ )
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationProperty.php
new file mode 100644
index 00000000..a60315e2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/PopulationProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PopulationProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Population' );
+ $this->property->setPropertyTypeId( '_num' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/StatusProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/StatusProperty.php
new file mode 100644
index 00000000..5fd13f53
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/StatusProperty.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+use SMW\SemanticData;
+use SMWDIBlob as DIBlob;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class StatusProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Status' );
+ $this->property->setPropertyTypeId( '_txt' );
+ }
+
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticData
+ */
+ public function getDependencies() {
+
+ $semanticData = parent::getDependencies();
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_PVAL' ),
+ new DIBlob( 'open' )
+ );
+
+ $semanticData->addPropertyObjectValue(
+ new DIProperty( '_PVAL' ),
+ new DIBlob( 'closed' )
+ );
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TelephoneNumberProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TelephoneNumberProperty.php
new file mode 100644
index 00000000..20fbf125
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TelephoneNumberProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TelephoneNumberProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Telephone number' );
+ $this->property->setPropertyTypeId( '_tel' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TemperatureProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TemperatureProperty.php
new file mode 100644
index 00000000..1dfcf818
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TemperatureProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TemperatureProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Temperature' );
+ $this->property->setPropertyTypeId( '_tem' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TitleProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TitleProperty.php
new file mode 100644
index 00000000..0e598a3a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/TitleProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TitleProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Title' );
+ $this->property->setPropertyTypeId( '_wpg' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/UrlProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/UrlProperty.php
new file mode 100644
index 00000000..58e1fc12
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/UrlProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class UrlProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = DIProperty::newFromUserLabel( 'Url' );
+ $this->property->setPropertyTypeId( '_uri' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/YearProperty.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/YearProperty.php
new file mode 100644
index 00000000..b5afd2bb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Properties/YearProperty.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Properties;
+
+use SMW\DIProperty;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class YearProperty extends FixtureProperty {
+
+ /**
+ * @since 2.1
+ */
+ public function __construct() {
+ $this->property = new DIProperty( 'Year' );
+ $this->property->setPropertyTypeId( '_dat' );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/FakeRawResultProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/FakeRawResultProvider.php
new file mode 100644
index 00000000..ed67f98e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/FakeRawResultProvider.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Utils\Fixtures\Results;
+
+use RuntimeException;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group semantic-mediawiki-sparql
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class FakeRawResultProvider {
+
+ /**
+ * @see https://www.w3.org/TR/rdf-sparql-query/#ask
+ */
+ public function getEmptySparqlResultXml() {
+ return $this->getFixtureContentsFor( 'empty-sparql-result.xml' );
+ }
+
+ /**
+ * @see http://www.w3.org/2009/sparql/xml-results/output2.srx
+ */
+ public function getBooleanSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'boolean-sparql-result.xml' );
+ }
+
+ public function getStringTypeLiteralSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'string-type-literal-sparql-result.xml' );
+ }
+
+ public function getUriResourceSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'uri-resource-sparql-result.xml' );
+ }
+
+ public function getNonTypeLiteralResultXml() {
+ return $this->getFixtureContentsFor( 'nontype-literal-sparql-result.xml' );
+ }
+
+ public function getIntegerTypeLiteralSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'integer-type-literal-sparql-result.xml' );
+ }
+
+ public function getMixedRowsSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'mixed-rows-sparql-result.xml' );
+ }
+
+ public function getInvalidSparqlResultXml() {
+ return $this->getFixtureContentsFor( 'invalid-sparql-result.xml' );
+ }
+
+ public function getMixedRowsSparqlResultUtf8Xml() {
+ return $this->getFixtureContentsFor( 'mixed-rows-sparql-result-utf8.xml' );
+ }
+
+ private function getFixtureContentsFor( $fixture ) {
+ if ( $file = $this->isReadableFile( $this->getFixtureLocation() . $fixture ) ) {
+ return file_get_contents( $file );
+ }
+ }
+
+ private function getFixtureLocation() {
+ return __DIR__ . '/' . 'XML' . '/';
+ }
+
+ private function isReadableFile( $file ) {
+
+ if ( is_readable( $file ) ) {
+ return $file;
+ }
+
+ throw new RuntimeException( "{$file} is not accessible" );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/boolean-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/boolean-sparql-result.xml
new file mode 100644
index 00000000..99a2c61a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/boolean-sparql-result.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ </head>
+ <boolean>true</boolean>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/empty-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/empty-sparql-result.xml
new file mode 100644
index 00000000..4157ec87
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/empty-sparql-result.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="s"/>
+ <variable name="r"/>
+ </head>
+ <results>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/integer-type-literal-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/integer-type-literal-sparql-result.xml
new file mode 100644
index 00000000..1dc3152e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/integer-type-literal-sparql-result.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="count"/>
+ </head>
+ <results>
+ <result>
+ <binding name="count"><literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/invalid-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/invalid-sparql-result.xml
new file mode 100644
index 00000000..4baedb6a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/invalid-sparql-result.xml
@@ -0,0 +1,8 @@
+
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ </head>
+ <results>
+ </results>
+</sparql>
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result-utf8.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result-utf8.xml
new file mode 100644
index 00000000..dc84a425
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result-utf8.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="result"/>
+ </head>
+ <results>
+ <result>
+ <binding name="result"><uri>http://example.org/id/F安o</uri></binding>
+ </result>
+ <result>
+ <binding name="result"><uri>http://example.org/id/B定ar</uri></binding>
+ </result>
+ <result>
+ <binding name="result"><literal datatype="http://www.w3.org/2001/XMLSchema#string">Quux安定</literal></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result.xml
new file mode 100644
index 00000000..e59eabf2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/mixed-rows-sparql-result.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="result"/>
+ </head>
+ <results>
+ <result>
+ <binding name="result"><uri>http://example.org/id/Foo</uri></binding>
+ </result>
+ <result>
+ <binding name="result"><uri>http://example.org/id/Bar</uri></binding>
+ </result>
+ <result>
+ <binding name="result"><literal datatype="http://www.w3.org/2001/XMLSchema#string">Quux</literal></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/nontype-literal-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/nontype-literal-sparql-result.xml
new file mode 100644
index 00000000..65fd4073
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/nontype-literal-sparql-result.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="s"/>
+ <variable name="r"/>
+ </head>
+ <results>
+ <result>
+ <binding name="s"><literal>Has foo</literal></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/string-type-literal-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/string-type-literal-sparql-result.xml
new file mode 100644
index 00000000..cfb5a542
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/string-type-literal-sparql-result.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="result"/>
+ </head>
+ <results>
+ <result>
+ <binding name="result"><literal datatype="http://www.w3.org/2001/XMLSchema#string">Foo</literal></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/uri-resource-sparql-result.xml b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/uri-resource-sparql-result.xml
new file mode 100644
index 00000000..bd918ee7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Fixtures/Results/XML/uri-resource-sparql-result.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<sparql xmlns="http://www.w3.org/2005/sparql-results#">
+ <head>
+ <variable name="result"/>
+ </head>
+ <results>
+ <result>
+ <binding name="result"><uri>http://example.org/id/Foo</uri></binding>
+ </result>
+ </results>
+</sparql> \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/GlobalsProvider.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/GlobalsProvider.php
new file mode 100644
index 00000000..ad1412bb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/GlobalsProvider.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use InvalidArgumentException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class GlobalsProvider {
+
+ /**
+ * @var GlobalsProvider
+ */
+ private static $instance = null;
+
+ /**
+ * @var array
+ */
+ private $container = null;
+
+ /**
+ * @since 1.9.1
+ *
+ * @return GlobalsProvider
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance->setContainer( $GLOBALS );
+ }
+
+ /**
+ * @since 1.9.1
+ */
+ public static function clear() {
+ self::$instance = null;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function get( $key ) {
+
+ if ( is_string( $key ) && $this->contains( $key ) ) {
+ return $this->lookup( $key );
+ }
+
+ throw new InvalidArgumentException( 'Configuration key is unkown' );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set( $key, $value ) {
+
+ if ( is_string( $key ) ) {
+ $this->container[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ protected function setContainer( $container ) {
+ $this->container = $container;
+ return $this;
+ }
+
+ protected function contains( $key ) {
+ return isset( $this->container[$key] ) || array_key_exists( $key, $this->container );
+ }
+
+ protected function lookup( $key ) {
+ return $this->container[$key];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/InSemanticDataFetcher.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/InSemanticDataFetcher.php
new file mode 100644
index 00000000..ccb50f8c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/InSemanticDataFetcher.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use SMW\Store;
+use SMWRequestOptions as RequestOptions;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class InSemanticDataFetcher {
+
+ /**
+ * @var Store
+ */
+ private $store = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return SemanticData
+ */
+ public function getSemanticData( DIWikiPage $subject ) {
+
+ $requestOptions = new RequestOptions();
+ $requestOptions->sort = true;
+
+ $semanticData = new SemanticData( $subject );
+
+ $incomingProperties = $this->store->getInProperties( $subject, $requestOptions );
+
+ foreach ( $incomingProperties as $property ) {
+ $values = $this->store->getPropertySubjects( $property, null );
+
+ foreach ( $values as $value ) {
+ $semanticData->addPropertyObjectValue( $property, $value );
+ }
+ }
+
+ return $semanticData;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/CoreMockObjectRepository.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/CoreMockObjectRepository.php
new file mode 100644
index 00000000..65c502e8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/CoreMockObjectRepository.php
@@ -0,0 +1,661 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+use DataValues\DataValue;
+use OutOfBoundsException;
+use SMW\ContentParser;
+use SMW\DependencyContainer;
+use SMW\DependencyObject;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Factbox\Factbox;
+use SMW\MediaWiki\PageInfoProvider;
+use SMW\ParserData;
+use SMW\Query\Language\Description;
+use SMW\Query\PrintRequest;
+use SMW\SemanticData;
+use SMW\SQLStore\TableDefinition;
+use SMW\Store;
+use SMW\Store\CacheableResultCollector;
+use SMWDataItem;
+use SMWDIError;
+use SMWQuery;
+use SMWQueryResult;
+use SMWResultArray;
+
+/**
+ * @codeCoverageIgnore
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class CoreMockObjectRepository extends \PHPUnit_Framework_TestCase implements MockObjectRepository {
+
+ /** @var MockObjectBuilder */
+ protected $builder;
+
+ /**
+ * @since 1.9
+ */
+ public function registerBuilder( MockObjectBuilder $builder ) {
+ $this->builder = $builder;
+ }
+
+ /**
+ * Returns a SemanticData object
+ *
+ * @since 1.9
+ *
+ * @return SemanticData
+ */
+ public function SemanticData() {
+
+ $semanticData = $this->getMockBuilder( 'SMW\SemanticData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $semanticData->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $semanticData;
+ }
+
+ /**
+ * Helper method that returns a CacheableResultCollector object
+ *
+ * @since 1.9
+ *
+ * @return CacheableResultCollector
+ */
+ public function CacheableResultCollector() {
+
+ // CacheableResultCollector is an abstract class therefore necessary methods
+ // are declared by default while other methods are only mocked if needed
+ // because setMethods overrides the original signature
+ $methods = [ 'cacheSetup', 'runCollector' ];
+
+ if ( $this->builder->hasValue( 'getResults' ) ) {
+ $methods[] = 'getResults';
+ }
+
+ $collector = $this->getMockBuilder( '\SMW\Store\CacheableResultCollector' )
+ ->setMethods( $methods )
+ ->getMock();
+
+ $collector->expects( $this->any() )
+ ->method( 'runCollector' )
+ ->will( $this->returnValue( $this->builder->setValue( 'runCollector' ) ) );
+
+ $collector->expects( $this->any() )
+ ->method( 'cacheSetup' )
+ ->will( $this->returnValue( $this->builder->setValue( 'cacheSetup' ) ) );
+
+ $collector->expects( $this->any() )
+ ->method( 'getResults' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getResults' ) ) );
+
+ return $collector;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DependencyObject
+ */
+ public function DependencyObject() {
+
+ $methods = $this->builder->getInvokedMethods();
+
+ $dependencyObject = $this->getMockBuilder( 'SMW\DependencyObject' )
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $dependencyObject->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $dependencyObject;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DependencyContainer
+ */
+ public function FakeDependencyContainer() {
+
+ $methods = $this->builder->getInvokedMethods();
+
+ $dependencyObject = $this->getMockBuilder( 'SMW\NullDependencyContainer' )
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $dependencyObject->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $dependencyObject;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserData
+ */
+ public function ParserData() {
+
+ $methods = $this->builder->getInvokedMethods();
+
+ $parserData = $this->getMockBuilder( 'SMW\ParserData' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $parserData->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $parserData;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Factbox
+ */
+ public function Factbox() {
+
+ $factbox = $this->getMockBuilder( '\SMW\Factbox\Factbox' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $factbox->expects( $this->any() )
+ ->method( 'isVisible' )
+ ->will( $this->returnValue( $this->builder->setValue( 'isVisible' ) ) );
+
+ $factbox->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getContent' ) ) );
+
+ return $factbox;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SMWQuery
+ */
+ public function Query() {
+
+ $query = $this->getMockBuilder( 'SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return $query;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ContentParser
+ */
+ public function ContentParser() {
+
+ $contentParser = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $contentParser->expects( $this->any() )
+ ->method( 'getOutput' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getOutput', $this->builder->newObject( 'ParserOutput' ) ) ) );
+
+ $contentParser->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getErrors', [] ) ) );
+
+ $contentParser->expects( $this->any() )
+ ->method( 'getRevision' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getRevision', null ) ) );
+
+ return $contentParser;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DataValue
+ * @throws OutOfBoundsException
+ */
+ public function DataValue() {
+
+ if ( !$this->builder->hasValue( 'DataValueType' ) ) {
+ throw new OutOfBoundsException( 'DataValueType is missing' );
+ }
+
+ $dataValue = $this->getMockBuilder( $this->builder->setValue( 'DataValueType' ) )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ if ( $method === 'DataValueType' ) {
+ continue;
+ }
+
+ $dataValue->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $dataValue;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SMWQueryResult
+ */
+ public function QueryResult() {
+
+ $queryResult = $this->getMockBuilder( 'SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getErrors', [] ) ) );
+
+ // Word of caution, onConsecutiveCalls is used in order to ensure
+ // that a while() loop is not trapped in an infinite loop and returns
+ // a false at the end
+ $queryResult->expects( $this->any() )
+ ->method( 'getNext' )
+ ->will( $this->onConsecutiveCalls( $this->builder->setValue( 'getNext' ), false ) );
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $queryResult->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $queryResult;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DIWikiPage
+ */
+ public function DIWikiPage() {
+
+ $diWikiPage = $this->getMockBuilder( '\SMW\DIWikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getTitle' ) ) );
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'getDBkey' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getDBkey', $this->builder->newRandomString( 10, 'DIWikiPage-auto-dbkey' ) ) ) );
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'getPrefixedText' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getPrefixedText', $this->builder->newRandomString( 10, 'DIWikiPage-auto-prefixedText' ) ) ) );
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( SMWDataItem::TYPE_WIKIPAGE ) );
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'findPropertyTypeID' )
+ ->will( $this->returnValue( $this->builder->setValue( 'findPropertyTypeID', '_wpg' ) ) );
+
+ $diWikiPage->expects( $this->any() )
+ ->method( 'getSubobjectName' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getSubobjectName', '' ) ) );
+
+ return $diWikiPage;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DIProperty
+ */
+ public function DIProperty() {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property->expects( $this->any() )
+ ->method( 'findPropertyTypeID' )
+ ->will( $this->returnValue( $this->builder->setValue( 'findPropertyTypeID', '_wpg' ) ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getKey' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getKey', '_wpg' ) ) );
+
+ $property->expects( $this->any() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( SMWDataItem::TYPE_PROPERTY ) );
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $property->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $property;
+ }
+
+ /**
+ * @note MockStore is based on the abstract Store class which avoids
+ * dependency on a specific Store implementation (SQLStore etc.), the mock
+ * object will allow to override necessary methods
+ *
+ * @since 1.9
+ *
+ * @return Store
+ */
+ public function Store() {
+
+ // SMW\Store is an abstract class, use setMethods to implement
+ // required abstract methods
+ $requiredAbstractMethods = [
+ 'setup',
+ 'drop',
+ 'getStatisticsTable',
+ 'getObjectIds',
+ 'refreshData',
+ 'getStatistics',
+ 'getQueryResult',
+ 'getPropertiesSpecial',
+ 'getUnusedPropertiesSpecial',
+ 'getWantedPropertiesSpecial',
+ 'getPropertyTables',
+ 'deleteSubject',
+ 'doDataUpdate',
+ 'changeTitle',
+ 'getProperties',
+ 'getInProperties',
+ 'getAllPropertySubjects',
+ 'getSQLConditions',
+ 'getSemanticData',
+ 'getPropertyValues',
+ 'getPropertySubjects',
+ 'refreshConceptCache',
+ 'deleteConceptCache',
+ 'getConceptCacheStatus',
+ 'clearData',
+ 'updateData'
+ ];
+
+ $methods = array_unique( array_merge( $requiredAbstractMethods, $this->builder->getInvokedMethods() ) );
+
+ $idTable = $this->getMockBuilder( 'stdClass' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getIdTable' ] )
+ ->getMock();
+
+ $idTable->expects( $this->any() )
+ ->method( 'getIdTable' )
+ ->will( $this->returnValue( 'smw_id_table_test' ) );
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( $methods )
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $idTable ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->builder->setCallback( 'getProperties', [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->builder->setCallback( 'getInProperties', [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getStatisticsTable' )
+ ->will( $this->returnValue( 'smw_statistics_table_test' ) );
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $store->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $store;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return TableDefinition
+ */
+ public function SQLStoreTableDefinition() {
+
+ $tableDefinition = $this->getMockBuilder( 'SMW\SQLStore\TableDefinition' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $tableDefinition->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $tableDefinition;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SMWDIError
+ */
+ public function DIError() {
+
+ $errors = $this->getMockBuilder( 'SMWDIError' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $errors->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getErrors' ) ) );
+
+ return $errors;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SMWDataItem
+ */
+ public function DataItem() {
+
+ $requiredMethods = [
+ 'getNumber',
+ 'getDIType',
+ 'getSortKey',
+ 'equals',
+ 'getSerialization',
+ ];
+
+ $methods = array_unique( array_merge( $requiredMethods, $this->builder->getInvokedMethods() ) );
+
+ $dataItem = $this->getMockBuilder( 'SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $dataItem->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $dataItem;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return PrintRequest
+ */
+ public function PrintRequest() {
+
+ $printRequest = $this->getMockBuilder( 'SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getText', $this->builder->newRandomString( 10, 'Auto-printRequest' ) ) ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getLabel' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getLabel' ) ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getMode' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getMode', PrintRequest::PRINT_THIS ) ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getTypeID' ) ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getOutputFormat' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getOutputFormat' ) ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getParameter' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getParameter', 'center' ) ) );
+
+ return $printRequest;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SMWResultArray
+ */
+ public function ResultArray() {
+
+ $resultArray = $this->getMockBuilder( 'SMWResultArray' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getPrintRequest' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getPrintRequest' ) ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getContent' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getContent' ) ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getNextDataValue' )
+ ->will( $this->onConsecutiveCalls( $this->builder->setValue( 'getNextDataValue' ), false ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getNextDataItem' )
+ ->will( $this->onConsecutiveCalls( $this->builder->setValue( 'getNextDataItem' ), false ) );
+
+ return $resultArray;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Description
+ */
+ public function QueryDescription() {
+
+ $requiredAbstractMethods = [
+ 'getQueryString',
+ 'isSingleton'
+ ];
+
+ $methods = array_unique( array_merge( $requiredAbstractMethods, $this->builder->getInvokedMethods() ) );
+
+ $queryDescription = $this->getMockBuilder( '\SMWDescription' )
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $queryDescription->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $queryDescription;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return PageInfoProvider
+ */
+ public function PageInfoProvider() {
+
+ $methods = $this->builder->getInvokedMethods();
+
+ $adapter = $this->getMockBuilder( 'SMW\PageInfoProvider' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $adapter->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $adapter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/FakeQueryStore.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/FakeQueryStore.php
new file mode 100644
index 00000000..306f66ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/FakeQueryStore.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+use SMW\QueryEngine;
+use SMW\Store;
+use SMW\StoreAware;
+use SMWQuery;
+use SMWQueryResult;
+
+/**
+ * FIXME One would wish to have a FakeStore but instead SMWSQLStore3 is used in
+ * order to avoid to implement all abstract methods specified by SMW\Store
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group medium
+ * @group semantic-mediawiki-integration
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class FakeQueryStore implements QueryEngine, StoreAware {
+
+ protected $store;
+
+ public function setQueryResult( SMWQueryResult $queryResult ) {
+ $this->queryResult = $queryResult;
+ }
+
+ public function setStore( Store $store ) {
+ $this->store = $store;
+ }
+
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.CodeAnalysis.UnusedFunctionParameter
+ public function getQueryResult( SMWQuery $query ) { // @codingStandardsIgnoreEnd
+ return $this->store->getQueryResult( $query );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/IteratorMockBuilder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/IteratorMockBuilder.php
new file mode 100644
index 00000000..721f4b36
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/IteratorMockBuilder.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+use RuntimeException;
+
+/**
+ * Convenience mock builder for Iterator classes
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class IteratorMockBuilder extends \PHPUnit_Framework_TestCase {
+
+ private $iteratorClass;
+ private $items = [];
+ private $methods = [];
+ private $counter = 0;
+
+ /**
+ * @since 2.0
+ *
+ * @param string $iteratorClass
+ *
+ * @return IteratorMockBuilder
+ */
+ public function setClass( $iteratorClass ) {
+ $this->iteratorClass = $iteratorClass;
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param array $items
+ *
+ * @return IteratorMockBuilder
+ */
+ public function with( array $items ) {
+ $this->items = $items;
+ return $this;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $methods
+ *
+ * @return IteratorMockBuilder
+ */
+ public function setMethods( array $methods ) {
+ $this->methods = $methods;
+ return $this;
+ }
+
+ /**
+ * @note When other methods called before the actual current/next then
+ * set the counter to ensure the starting point matches the expected
+ * InvokeCount.
+ *
+ * @since 2.5
+ *
+ * @param integer $num
+ *
+ * @return IteratorMockBuilder
+ */
+ public function incrementInvokedCounterBy( $num ) {
+ $this->counter += $num;
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return PHPUnit_Framework_MockObject_MockObject
+ * @throws RuntimeException
+ */
+ public function getMockForIterator() {
+
+ $instance = $this->getMockBuilder( $this->iteratorClass )
+ ->disableOriginalConstructor()
+ ->setMethods( $this->methods )
+ ->getMock();
+
+ if ( !$instance instanceof \Iterator ) {
+ throw new RuntimeException( "Instance is not an Iterator" );
+ }
+
+ $instance->expects( $this->at( $this->counter++ ) )
+ ->method( 'rewind' );
+
+ foreach ( $this->items as $key => $value ) {
+
+ $instance->expects( $this->at( $this->counter++ ) )
+ ->method( 'valid' )
+ ->will( $this->returnValue( true ) );
+
+ $instance->expects( $this->at( $this->counter++ ) )
+ ->method( 'current' )
+ ->will( $this->returnValue( $value ) );
+
+ $instance->expects( $this->at( $this->counter++ ) )
+ ->method( 'next' );
+ }
+
+ $instance->expects( $this->at( $this->counter++ ) )
+ ->method( 'valid' )
+ ->will( $this->returnValue( false ) );
+
+ return $instance;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return integer
+ */
+ public function getLastCounter() {
+ return $this->counter;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MediaWikiMockObjectRepository.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MediaWikiMockObjectRepository.php
new file mode 100644
index 00000000..24932f53
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MediaWikiMockObjectRepository.php
@@ -0,0 +1,517 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+/**
+ * @codeCoverageIgnore
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MediaWikiMockObjectRepository extends \PHPUnit_Framework_TestCase implements MockObjectRepository {
+
+ /** @var MockObjectBuilder */
+ protected $builder;
+
+ /**
+ * @since 1.9
+ */
+ public function registerBuilder( MockObjectBuilder $builder ) {
+ $this->builder = $builder;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return User
+ */
+ public function User() {
+
+ $user = $this->getMockBuilder( 'User' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user->expects( $this->any() )
+ ->method( 'getUserPage' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getUserPage' ) ) );
+
+ return $user;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserOptions
+ */
+ public function ParserOptions() {
+
+ $parserOptions = $this->getMockBuilder( 'ParserOptions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parserOptions->expects( $this->any() )
+ ->method( 'getTargetLanguage' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getTargetLanguage' ) ) );
+
+ return $parserOptions;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ParserOutput
+ */
+ public function ParserOutput() {
+
+ $parserOutput = $this->getMockBuilder( 'ParserOutput' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $parserOutput->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $parserOutput;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return WikiPage
+ */
+ public function WikiPage() {
+
+ $wikiPage = $this->getMockBuilder( 'WikiPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $wikiPage->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $wikiPage;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return WikiFilePage
+ */
+ public function WikiFilePage() {
+
+ $wikiPage = $this->getMockBuilder( 'WikiFilePage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $wikiPage->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $wikiPage;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @return File
+ */
+ public function File() {
+
+ $wikiPage = $this->getMockBuilder( 'File' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $wikiPage->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $wikiPage;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Revision
+ */
+ public function Revision() {
+
+ $revision = $this->getMockBuilder( 'Revision' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $revision->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $revision;
+ }
+
+ /**
+ * @note This mock object avoids the involvement of LinksUpdate (which
+ * requires DB access) and returns a randomized LatestRevID/ArticleID
+ *
+ * @since 1.9
+ *
+ * @return Title
+ */
+ public function Title() {
+
+ // When interacting with a "real" Parser object, the Parser expects in
+ // in 1.21+ a content model to be present while in MW 1.19/1.20 such
+ // object is not required. In order to avoid operational obstruction a
+ // model is set as default and can if necessary individually be overridden
+ $contentModel = defined( 'CONTENT_MODEL_WIKITEXT' ) ? CONTENT_MODEL_WIKITEXT : null;
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $this->any() )
+ ->method( 'getDBkey' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getDBkey', $this->builder->newRandomString( 10, 'Title-auto-dbkey' ) ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getInterwiki' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getInterwiki', '' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getArticleID' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getArticleID', rand( 10, 10000 ) ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getNamespace' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getNamespace', NS_MAIN ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isKnown' )
+ ->will( $this->returnValue( $this->builder->setValue( 'exists' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( $this->builder->setValue( 'exists' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getLatestRevID' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getLatestRevID', rand( 10, 5000 ) ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getFirstRevision' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getFirstRevision' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getText' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getPrefixedText' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getPrefixedText', $this->builder->newRandomString( 10, 'Title-auto-prefixedtext' ) ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $this->returnValue( $this->builder->setValue( 'isSpecialPage', false ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isSpecial' )
+ ->will( $this->returnValue( $this->builder->setValue( 'isSpecial', false ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isDeleted' )
+ ->will( $this->returnValue( $this->builder->setValue( 'isDeleted', false ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getContentModel' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getContentModel', $contentModel ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getPageLanguage' ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'isRedirect' )
+ ->will( $this->returnValue( $this->builder->setValue( 'isRedirect', false ) ) );
+
+ $title->expects( $this->any() )
+ ->method( 'inNamespace' )
+ ->will( $this->builder->setCallback( 'inNamespace' ) );
+
+ return $title;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Skin
+ */
+ public function Skin() {
+
+ $skin = $this->getMockBuilder( 'Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $skin->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $skin;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return SkinTemplate
+ */
+ public function SkinTemplate() {
+
+ $skinTemplate = $this->getMockBuilder( 'SkinTemplate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $skinTemplate->expects( $this->any() )
+ ->method( 'getSkin' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getSkin' ) ) );
+
+ return $skinTemplate;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Parser
+ */
+ public function Parser() {
+
+ $parser = $this->getMockBuilder( 'Parser' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $parser->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $parser;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return LinksUpdate
+ */
+ public function LinksUpdate() {
+
+ $linksUpdate = $this->getMockBuilder( 'LinksUpdate' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $linksUpdate->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $linksUpdate;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return OutputPage
+ */
+ public function OutputPage() {
+
+ $outputPage = $this->getMockBuilder( 'OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->any() )
+ ->method( 'getTitle' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getTitle' ) ) );
+
+ $outputPage->expects( $this->any() )
+ ->method( 'getContext' )
+ ->will( $this->returnValue( $this->builder->setValue( 'getContext' ) ) );
+
+ $outputPage->expects( $this->any() )
+ ->method( 'addModules' )
+ ->will( $this->returnValue( $this->builder->setValue( 'addModules' ) ) );
+
+ $outputPage->expects( $this->any() )
+ ->method( 'addLink' )
+ ->will( $this->returnValue( $this->builder->setValue( 'addLink' ) ) );
+
+ // getHeadLinksArray doesn't exist in MW 1.19
+ $outputPage->expects( $this->any() )
+ ->method( 'getHeadLinksArray' )
+ ->will( $this->builder->setCallback( 'getHeadLinksArray' ) );
+
+ return $outputPage;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return DatabaseBase
+ */
+ public function DatabaseBase() {
+
+ // DatabaseBase is an abstract class, use setMethods to implement
+ // required abstract methods
+ $requiredAbstractMethods = [
+ 'selectField',
+ 'doQuery',
+ 'getType',
+ 'open',
+ 'fetchObject',
+ 'fetchRow',
+ 'numRows',
+ 'numFields',
+ 'fieldName',
+ 'insertId',
+ 'dataSeek',
+ 'lastErrno',
+ 'lastError',
+ 'fieldInfo',
+ 'indexInfo',
+ 'affectedRows',
+ 'strencode',
+ 'getSoftwareLink',
+ 'getServerVersion',
+ 'closeConnection'
+ ];
+
+ $methods = array_unique( array_merge( $requiredAbstractMethods, $this->builder->getInvokedMethods() ) );
+
+ $databaseBase = $this->getMockBuilder( 'DatabaseBase' )
+ ->disableOriginalConstructor()
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $this->builder->getInvokedMethods() as $method ) {
+
+ $databaseBase->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $databaseBase;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Content
+ */
+ public function Content() {
+
+ $methods = $this->builder->getInvokedMethods();
+
+ $content = $this->getMockBuilder( 'Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $content->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $content;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return ContentHandler
+ */
+ public function ContentHandler() {
+
+ $requiredAbstractMethods = [
+ 'serializeContent',
+ 'unserializeContent',
+ 'makeEmptyContent'
+ ];
+
+ $methods = array_unique( array_merge( $requiredAbstractMethods, $this->builder->getInvokedMethods() ) );
+
+ $contentHandler = $this->getMockBuilder( 'ContentHandler' )
+ ->disableOriginalConstructor()
+ ->setMethods( $methods )
+ ->getMock();
+
+ foreach ( $methods as $method ) {
+
+ $contentHandler->expects( $this->any() )
+ ->method( $method )
+ ->will( $this->builder->setCallback( $method ) );
+
+ }
+
+ return $contentHandler;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return RequestContext
+ */
+ public function RequestContext() {
+
+ $requestContext = $this->getMockForAbstractClass( 'RequestContext' );
+
+ return $requestContext;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return Language
+ */
+ public function Language() {
+
+ $language = $this->getMockBuilder( 'Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return $language;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectBuilder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectBuilder.php
new file mode 100644
index 00000000..473c9602
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectBuilder.php
@@ -0,0 +1,195 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+use InvalidArgumentException;
+use OutOfBoundsException;
+use SMW\ObjectDictionary;
+use SMW\Options;
+
+/**
+ * @codeCoverageIgnore
+ *
+ * MockObject builder provides methods that are being used by the mock repository
+ * to define and create a mock object
+ *
+ * $title = new MockObjectBuilder()
+ * $title->newObject( 'Foo', array(
+ * 'Bar' => ...
+ * ) )
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MockObjectBuilder extends \PHPUnit_Framework_TestCase {
+
+ /** @var ObjectDictionary */
+ protected $configuration;
+
+ /** @var MockObjectRepository */
+ protected $repository = [];
+
+ /**
+ * @since 1.9
+ *
+ * @param MockObjectRepository|null $repository
+ */
+ public function __construct( MockObjectRepository $repository = null ) {
+
+ if ( $repository === null ) {
+ $repository = new CoreMockObjectRepository();
+ }
+
+ $this->registerRepository( $repository );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param MockObjectRepository $repository
+ */
+ public function registerRepository( MockObjectRepository $repository ) {
+ $this->repository[] = $repository;
+ }
+
+ /**
+ * Helper method that stores configuration settings
+ *
+ * @since 1.9
+ *
+ * @param $objectName
+ * @param $objectArguments
+ *
+ * @return mixed
+ */
+ public function newObject( $objectName, $objectArguments = [] ) {
+
+ if ( !is_string( $objectName ) ) {
+ throw new InvalidArgumentException( "Object name is not a string" );
+ }
+
+ if ( $objectArguments !== [] && !is_array( $objectArguments ) ) {
+ throw new InvalidArgumentException( "Arguments are not an array type" );
+ }
+
+ $repository = $this->findRepositoryForObject( $objectName );
+
+ if ( !$repository instanceof MockObjectRepository ) {
+ throw new OutOfBoundsException( "{$objectName} method doesn't exists" );
+ }
+
+ $repository->registerBuilder( $this );
+ $this->setupConfiguration( $objectArguments );
+
+ return $repository->{$objectName}();
+ }
+
+ /**
+ * Returns invoked configuration keys
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function getInvokedMethods() {
+ return array_keys( $this->configuration->getOptions() );
+ }
+
+ /**
+ * Helper method that returns a random string
+ *
+ * @since 1.9
+ *
+ * @param $length
+ * @param $prefix identify a specific random string during testing
+ *
+ * @return string
+ */
+ public function newRandomString( $length = 10, $prefix = null ) {
+ return $prefix . ( $prefix ? '-' : '' ) . substr( str_shuffle( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ), 0, $length );
+ }
+
+ /**
+ * Whether the configuration is known
+ *
+ * @since 1.9
+ *
+ * @param $key
+ *
+ * @return boolean
+ */
+ public function hasValue( $key ) {
+ return $this->configuration->has( $key );
+ }
+
+ /**
+ * Sets value
+ *
+ * @since 1.9
+ *
+ * @param $key
+ * @param $default
+ *
+ * @return mixed|null
+ */
+ public function setValue( $key, $default = null ) {
+ return $this->configuration->has( $key ) ? $this->configuration->get( $key ) : $default;
+ }
+
+ /**
+ * Determine callback function otherwise return simple value
+ *
+ * @since 1.9
+ *
+ * @param $key
+ * @param $default
+ *
+ * @return mixed|null
+ */
+ public function setCallback( $key, $default = null ) {
+ return is_callable( $this->setValue( $key ) ) ? $this->returnCallback( $this->setValue( $key ) ) : $this->returnValue( $this->setValue( $key, $default ) );
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param $objectName
+ *
+ * @return MockObjectRepository|null
+ */
+ protected function findRepositoryForObject( $objectName ) {
+
+ foreach ( $this->repository as $repository ) {
+ if ( method_exists( $repository, $objectName ) ) {
+ return $repository;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @param $config
+ */
+ protected function setupConfiguration( $config ) {
+
+ $configuration = new Options( $config );
+
+ if ( $this->configuration instanceof Options ) {
+ return $this->configuration = new Options(
+ array_merge( $this->configuration->getOptions(), $configuration->getOptions() )
+ );
+ }
+
+ $this->configuration = $configuration;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectRepository.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectRepository.php
new file mode 100644
index 00000000..b9bdb213
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockObjectRepository.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+/**
+ * Interface describing a MockObjectRepository object
+ *
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+interface MockObjectRepository {
+
+ /**
+ * Returns a DIProperty object
+ *
+ * @since 1.9
+ */
+ public function registerBuilder( MockObjectBuilder $builder );
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockSuperUser.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockSuperUser.php
new file mode 100644
index 00000000..cb91e3cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockSuperUser.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+use User;
+
+/**
+ * Instantiate a SuperUser in order to be able to do everything
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @licence GNU GPL v2+
+ */
+
+/**
+ * Instantiate a SuperUser in order to be able to do everything.
+ * Borrowed from Translate/EducationProgram extension :-)
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @codeCoverageIgnore
+ */
+class MockSuperUser extends User {
+ public function getId() {
+ return 666;
+ }
+
+ public function getName() {
+ return 'SuperUser';
+ }
+
+ public function isAllowed( $right = '' ) {
+ return true;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockTitle.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockTitle.php
new file mode 100644
index 00000000..57626237
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Mock/MockTitle.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests\Utils\Mock;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class MockTitle extends \PHPUnit_Framework_TestCase {
+
+ public static function buildMock( $text = __METHOD__ ) {
+
+ $instance = new self();
+
+ $contentModel = defined( 'CONTENT_MODEL_WIKITEXT' ) ? CONTENT_MODEL_WIKITEXT : null;
+
+ $title = $instance->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $title->expects( $instance->any() )
+ ->method( 'getDBkey' )
+ ->will( $instance->returnValue( str_replace( ' ', '_', $text ) ) );
+
+ $title->expects( $instance->any() )
+ ->method( 'getPrefixedDBkey' )
+ ->will( $instance->returnValue( str_replace( ' ', '_', $text ) ) );
+
+ $title->expects( $instance->any() )
+ ->method( 'getContentModel' )
+ ->will( $instance->returnValue( $contentModel ) );
+
+ return $title;
+ }
+
+ public static function buildMockForMainNamespace( $text = __METHOD__ ) {
+
+ $instance = new self();
+
+ $title = $instance->buildMock( $text );
+
+ $title->expects( $instance->any() )
+ ->method( 'getNamespace' )
+ ->will( $instance->returnValue( NS_MAIN ) );
+
+ $title->expects( $instance->any() )
+ ->method( 'getArticleID' )
+ ->will( $instance->returnValue( 9001 ) );
+
+ $title->expects( $instance->any() )
+ ->method( 'isSpecialPage' )
+ ->will( $instance->returnValue( false ) );
+
+ return $title;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwApiFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwApiFactory.php
new file mode 100644
index 00000000..24d9412d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwApiFactory.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use ApiMain;
+use ApiResult;
+use FauxRequest;
+use RequestContext;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use WebRequest;
+
+/**
+ * Class contains Api related request methods
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MwApiFactory {
+
+ /**
+ * @param array $params
+ *
+ * @return ApiMain
+ */
+ public function newApiMain( array $params ) {
+ return new ApiMain( $this->newRequestContext( $params ), true );
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return ApiResult
+ */
+ public function newApiResult( array $params ) {
+
+ if ( version_compare( $GLOBALS['wgVersion'], '1.25', '<' ) ) {
+ return new ApiResult( $this->newApiMain( $params ) );
+ }
+
+ $result = new ApiResult( 5 );
+
+ $errorFormatter = new \ApiErrorFormatter_BackCompat( $result );
+ $result->setErrorFormatter( $errorFormatter );
+
+ return $result;
+ }
+
+ /**
+ * Returns Api results
+ *
+ * The returned value is an array containing
+ * - the result data (array)
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+ public function doApiRequest( array $params ) {
+
+ $api = $this->newApiMain( $params );
+ $api->execute();
+
+ if ( method_exists( $api->getResult(), 'getResultData' ) ) {
+ return $api->getResult()->getResultData();
+ }
+
+ return $api->getResultData();
+ }
+
+ private function newRequestContext( $request = [] ) {
+
+ $context = new RequestContext();
+
+ if ( $request instanceof WebRequest ) {
+ $context->setRequest( $request );
+ } else {
+ $context->setRequest( new FauxRequest( $request, true ) );
+ }
+
+ $context->setUser( new MockSuperUser() );
+
+ return $context;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwHooksHandler.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwHooksHandler.php
new file mode 100644
index 00000000..2e842ec1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/MwHooksHandler.php
@@ -0,0 +1,145 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use RuntimeException;
+use SMW\MediaWiki\Hooks\HookRegistry;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class MwHooksHandler {
+
+ /**
+ * @var HookRegistry
+ */
+ private $hookRegistry = null;
+
+ private $wgHooks = [];
+ private $inTestRegisteredHooks = [];
+
+ private $listOfSmwHooks = [
+ 'SMWStore::updateDataBefore',
+
+ // Those shoudl not be disabled so that extension used
+ // by a test will run the registration in case an instance
+ // is cleared
+ // 'smwInitDatatypes',
+ // 'SMW::DataType::initTypes',
+
+ 'smwInitProperties',
+ 'SMW::Property::initProperties',
+ 'SMW::Factbox::BeforeContentGeneration',
+ 'SMW::SQLStore::updatePropertyTableDefinitions',
+ 'SMW::Store::BeforeQueryResultLookupComplete',
+ 'SMW::Store::AfterQueryResultLookupComplete',
+ 'SMW::SQLStore::BeforeChangeTitleComplete',
+ 'SMW::SQLStore::BeforeDeleteSubjectComplete',
+ 'SMW::SQLStore::AfterDeleteSubjectComplete',
+ 'SMW::Parser::BeforeMagicWordsFinder',
+ 'SMW::SQLStore::BeforeDataRebuildJobInsert',
+ 'SMW::SQLStore::AddCustomFixedPropertyTables',
+ 'SMW::SQLStore::AfterDataUpdateComplete',
+ 'SMW::Browse::AfterIncomingPropertiesLookupComplete',
+ 'SMW::Browse::BeforeIncomingPropertyValuesFurtherLinkCreate'
+ ];
+
+ /**
+ * @since 2.0
+ *
+ * @return MwHooksHandler
+ */
+ public function deregisterListedHooks() {
+
+ $listOfHooks = array_merge(
+ $this->listOfSmwHooks,
+ $this->getHookRegistry()->getHandlerList()
+ );
+
+ foreach ( $listOfHooks as $hook ) {
+
+ // MW 1.19
+ if ( method_exists( 'Hooks', 'clear' ) ) {
+ \Hooks::clear( $hook );
+ }
+
+ if ( !isset( $GLOBALS['wgHooks'][$hook] ) ) {
+ continue;
+ }
+
+ $this->wgHooks[$hook] = $GLOBALS['wgHooks'][$hook];
+ $GLOBALS['wgHooks'][$hook] = [];
+ }
+
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return MwHooksHandler
+ */
+ public function restoreListedHooks() {
+
+ foreach ( $this->inTestRegisteredHooks as $hook ) {
+ unset( $GLOBALS['wgHooks'][$hook] );
+ }
+
+ foreach ( $this->wgHooks as $hook => $definition ) {
+ $GLOBALS['wgHooks'][$hook] = $definition;
+ unset( $this->wgHooks[$hook] );
+ }
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MwHooksHandler
+ */
+ public function register( $name, callable $callback ) {
+
+ $listOfHooks = array_merge(
+ $this->listOfSmwHooks,
+ $this->getHookRegistry()->getHandlerList()
+ );
+
+ if ( !in_array( $name, $listOfHooks ) ) {
+ throw new RuntimeException( "$name is not listed as registrable hook" );
+ }
+
+ $this->inTestRegisteredHooks[] = $name;
+ $GLOBALS['wgHooks'][$name][] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MwHooksHandler
+ */
+ public function invokeHooksFromRegistry() {
+ $this->getHookRegistry()->register();
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return HookRegistry
+ */
+ public function getHookRegistry() {
+
+ if ( $this->hookRegistry === null ) {
+ $this->hookRegistry = new HookRegistry( $GLOBALS, '' );
+ }
+
+ return $this->hookRegistry;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Page/PageEditor.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Page/PageEditor.php
new file mode 100644
index 00000000..b09bf5d4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Page/PageEditor.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace SMW\Tests\Utils\Page;
+
+use Revision;
+use RuntimeException;
+use Title;
+use WikiPage;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ */
+class PageEditor {
+
+ /**
+ * @var WikiPage|null
+ */
+ private $page = null;
+
+ /**
+ * @since 2.1
+ *
+ * @return WikiPage
+ * @throws RuntimeException
+ */
+ public function getPage() {
+
+ if ( $this->page instanceof WikiPage ) {
+ return $this->page;
+ }
+
+ throw new RuntimeException( 'Expected a valid WikiPage instance.' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ *
+ * @return PageEditor
+ */
+ public function editPage( Title $title ) {
+ $this->page = new WikiPage( $title );
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $pageContent
+ * @param string $editMessage
+ *
+ * @return PageEditor
+ */
+ public function doEdit( $pageContent = '', $editMessage = '' ) {
+
+ if ( class_exists( 'WikitextContent' ) ) {
+ $content = new \WikitextContent( $pageContent );
+
+ $this->getPage()->doEditContent(
+ $content,
+ $editMessage
+ );
+
+ } else {
+ $this->getPage()->doEdit( $pageContent, $editMessage );
+ }
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function getEditInfo() {
+
+ if ( class_exists( 'WikitextContent' ) ) {
+
+ $content = $this->getPage()->getRevision()->getContent();
+ $format = $content->getContentHandler()->getDefaultFormat();
+
+ return $this->getPage()->prepareContentForEdit(
+ $content,
+ null,
+ null,
+ $format
+ );
+ }
+
+ if ( method_exists( $this->getPage()->getRevision(), 'getContent' ) ) {
+ $text = $this->getPage()->getRevision()->getContent( Revision::RAW );
+ } else {
+ $text = $this->getPage()->getRevision()->getRawText();
+ }
+ return $this->getPage()->prepareTextForEdit(
+ $text,
+ null,
+ null
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageCreator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageCreator.php
new file mode 100644
index 00000000..bbf34cf5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageCreator.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Revision;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+use UnexpectedValueException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class PageCreator {
+
+ /**
+ * @var null
+ */
+ protected $page = null;
+
+ /**
+ * @since 1.9.1
+ *
+ * @return WikiPage
+ * @throws UnexpectedValueException
+ */
+ public function getPage() {
+
+ if ( $this->page instanceof \WikiPage ) {
+ return $this->page;
+ }
+
+ throw new UnexpectedValueException( 'Expected a WikiPage instance, use createPage first' );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param Title $title
+ * @param string $editContent
+ * @param string $pageContentLanguage
+ *
+ * @return PageCreator
+ */
+ public function createPage( Title $title, $editContent = '', $pageContentLanguage = '' ) {
+
+ if ( $pageContentLanguage !== '' ) {
+ \Hooks::register( 'PageContentLanguage', function( $titleByHook, &$pageLang ) use( $title, $pageContentLanguage ) {
+
+ // Only change the pageContentLanguage for the selected page
+ if ( $title->getPrefixedDBKey() === $titleByHook->getPrefixedDBKey() ) {
+ $pageLang = $pageContentLanguage;
+ }
+
+ // MW 1.19
+ return true;
+ } );
+ }
+
+ $this->page = new \WikiPage( $title );
+
+ if ( $editContent === '' ) {
+ $editContent = 'Content of ' . $title->getFullText();
+ }
+
+ $editMessage = 'SMW system test: create page';
+
+ return $this->doEdit( $editContent, $editMessage );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param string $pageContent
+ * @param string $editMessage
+ *
+ * @return PageCreator
+ */
+ public function doEdit( $pageContent = '', $editMessage = '' ) {
+
+ if ( class_exists( 'ContentHandler' ) ) {
+ $content = \ContentHandler::makeContent(
+ $pageContent,
+ $this->getPage()->getTitle()
+ );
+
+ $this->getPage()->doEditContent(
+ $content,
+ $editMessage
+ );
+
+ } else {
+ $this->getPage()->doEdit( $pageContent, $editMessage );
+ }
+
+ TestEnvironment::executePendingDeferredUpdates();
+
+ return $this;
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param Title $target
+ * @param boolean $isRedirect
+ *
+ * @return PageCreator
+ */
+ public function doMoveTo( Title $target, $isRedirect = true ) {
+
+ $reason = "integration test";
+ $source = $this->getPage()->getTitle();
+
+ if ( class_exists( '\MovePage' ) ) {
+ $mp = new \MovePage( $source, $target );
+ $status = $mp->move( new MockSuperUser(), $reason, $isRedirect );
+ } else {
+ // deprecated since 1.25, use the MovePage class instead
+ $status = $source->moveTo( $target, false, $reason, $isRedirect );
+ }
+
+ TestEnvironment::executePendingDeferredUpdates();
+
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return EditInfo
+ */
+ public function getEditInfo() {
+
+ $revision = $this->getPage()->getRevision();
+
+ if ( class_exists( 'ContentHandler' ) ) {
+
+ $content = $revision->getContent();
+ $format = $content->getContentHandler()->getDefaultFormat();
+
+ return $this->getPage()->prepareContentForEdit(
+ $content,
+ null,
+ null,
+ $format
+ );
+ }
+
+ $text = method_exists( $revision, 'getContent' ) ? $revision->getContent( Revision::RAW ) : $revision->getRawText();
+
+ return $this->getPage()->prepareTextForEdit(
+ $text,
+ null,
+ null
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageDeleter.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageDeleter.php
new file mode 100644
index 00000000..cebc13c1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageDeleter.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use SMW\DIWikiPage;
+use SMW\Tests\TestEnvironment;
+use Title;
+use WikiPage;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ */
+class PageDeleter {
+
+ /**
+ * @var TestEnvironment
+ */
+ private $testEnvironment;
+
+ /**
+ * @since 2.4
+ */
+ public function __construct() {
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ /**
+ * @since 1.9.1
+ */
+ public function deletePage( Title $title ) {
+ $page = new WikiPage( $title );
+ $page->doDeleteArticle( 'SMW system test: delete page' );
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $poolOfPages
+ */
+ public function doDeletePoolOfPages( array $poolOfPages ) {
+
+ foreach ( $poolOfPages as $page ) {
+
+ if ( $page instanceof WikiPage || $page instanceof DIWikiPage ) {
+ $page = $page->getTitle();
+ }
+
+ if ( is_string( $page ) ) {
+ $page = Title::newFromText( $page );
+ }
+
+ if ( !$page instanceof Title ) {
+ continue;
+ }
+
+ $this->deletePage( $page );
+ }
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageReader.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageReader.php
new file mode 100644
index 00000000..638cc4c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageReader.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Revision;
+use TextContent;
+use Title;
+use UnexpectedValueException;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class PageReader {
+
+ /**
+ * @var WikiPage
+ */
+ private $page = null;
+
+ /**
+ * @since 2.1
+ *
+ * @return WikiPage
+ * @throws UnexpectedValueException
+ */
+ public function getPage() {
+
+ if ( $this->page instanceof \WikiPage ) {
+ return $this->page;
+ }
+
+ throw new UnexpectedValueException( 'Expected a WikiPage instance, use createPage first' );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param Title $title
+ *
+ * @return text
+ */
+ public function getContentAsText( Title $title ) {
+
+ $this->page = new \WikiPage( $title );
+
+ if ( method_exists( $this->page, 'getContent' ) ) {
+ $content = $this->page->getContent();
+
+ if ( $content instanceof TextContent ) {
+ return $content->getNativeData();
+ } else {
+ return '';
+ }
+ }
+
+ return $this->page->getText();
+ }
+
+ /**
+ * @since 2.2
+ */
+ public function getEditInfo( Title $title ) {
+
+ $this->page = new \WikiPage( $title );
+
+ if ( class_exists( 'WikitextContent' ) ) {
+
+ $content = $this->page->getRevision()->getContent();
+ $format = $content->getContentHandler()->getDefaultFormat();
+
+ return $this->page->prepareContentForEdit(
+ $content,
+ null,
+ null,
+ $format
+ );
+ }
+
+ if ( method_exists( $this->getPage()->getRevision(), 'getContent' ) ) {
+ $text = $this->getPage()->getRevision()->getContent( Revision::RAW );
+ } else {
+ $text = $this->getPage()->getRevision()->getRawText();
+ }
+ return $this->page->prepareTextForEdit(
+ $text,
+ null,
+ null
+ );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Title $title
+ *
+ * @return ParserOutput|null
+ */
+ public function getParserOutputFromEdit( Title $title ) {
+ return $this->getEditInfo( $title )->output;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageRefresher.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageRefresher.php
new file mode 100644
index 00000000..05c74776
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/PageRefresher.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use RuntimeException;
+use SMW\ApplicationFactory;
+use SMW\ContentParser;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use Title;
+use WikiPage;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ */
+class PageRefresher {
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $title
+ *
+ * @return PageRefresher
+ */
+ public function doRefresh( $title ) {
+
+ if ( $title instanceof WikiPage || $title instanceof DIWikiPage ) {
+ $title = $title->getTitle();
+ }
+
+ if ( !$title instanceof Title ) {
+ throw new RuntimeException( 'Expected a title instance' );
+ }
+
+ $contentParser = new ContentParser( $title );
+
+ $parserData = ApplicationFactory::getInstance()->newParserData(
+ $title,
+ $contentParser->parse()->getOutput()
+ );
+
+ $parserData->updateStore();
+
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $pages
+ *
+ * @return PageRefresher
+ */
+ public function doRefreshPoolOfPages( array $pages ) {
+ foreach ( $pages as $page ) {
+ $this->doRefreshByUpdateJob( $page );
+ }
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param mixed $title
+ *
+ * @return PageRefresher
+ */
+ public function doRefreshByUpdateJob( $title ) {
+
+ if ( $title instanceof WikiPage || $title instanceof DIWikiPage ) {
+ $title = $title->getTitle();
+ }
+
+ if ( is_string( $title ) ) {
+ $title = Title::newFromText( $title );
+ }
+
+ if ( !$title instanceof Title ) {
+ throw new RuntimeException( 'Expected a title instance' );
+ }
+
+ $job = new UpdateJob( $title );
+ $job->run();
+
+ return $this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ParserFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ParserFactory.php
new file mode 100644
index 00000000..c0317278
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/ParserFactory.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Parser;
+use ParserOptions;
+use SMW\DIWikiPage;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+use User;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ */
+class ParserFactory {
+
+ public static function create( $title, User $user = null ) {
+
+ if ( is_string( $title ) ) {
+ $title = Title::newFromText( $title );
+ }
+
+ if ( $title instanceof DIWikiPage ) {
+ $title = $title->getTitle();
+ }
+
+ return self::newFromTitle( $title, $user );
+ }
+
+ public static function newFromTitle( Title $title, User $user = null ) {
+
+ if ( $user === null ) {
+ $user = new MockSuperUser();
+ }
+
+ // $wikiPage = new \WikiPage( $title );
+ // $wikiPage->makeParserOptions( $user );
+
+ $parser = new Parser( $GLOBALS['wgParserConf'] );
+ $parser->setTitle( $title );
+ $parser->setUser( $user );
+ $parser->Options( new ParserOptions( $user ) );
+ $parser->clearState();
+
+ return $parser;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/JobQueueRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/JobQueueRunner.php
new file mode 100644
index 00000000..29e124db
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/JobQueueRunner.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace SMW\Tests\Utils\Runners;
+
+use Job;
+use JobQueueGroup;
+use SMW\Connection\ConnectionProvider as IConnectionProvider;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\Utils\Connection\ConnectionProvider;
+
+/**
+ * Partly copied from the MW 1.19 RunJobs maintenance script
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ */
+class JobQueueRunner {
+
+ protected $type = null;
+ protected $status = [];
+ protected $connectionProvider = null;
+
+ /**
+ * @var TestEnvironment
+ */
+ private $testEnvironment;
+
+ /**
+ * @since 1.9.2
+ *
+ * @param string|null $type
+ * @param IConnectionProvider|null $connectionProvider
+ */
+ public function __construct( $type = null, IConnectionProvider $connectionProvider = null ) {
+ $this->type = $type;
+ $this->connectionProvider = $connectionProvider;
+
+ if ( $this->connectionProvider === null ) {
+ $this->connectionProvider = new ConnectionProvider();
+ }
+
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $type
+ *
+ * @return JobQueueRunner
+ */
+ public function setType( $type ) {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param IConnectionProvider $connectionProvider
+ *
+ * @return JobQueueRunner
+ */
+ public function setConnectionProvider( IConnectionProvider $connectionProvider ) {
+ $this->connectionProvider = $connectionProvider;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.2
+ */
+ public function run() {
+
+ $conds = '';
+ $connection = $this->connectionProvider->getConnection();
+
+ if ( $this->type !== null ) {
+ $conds = "job_cmd = " . $connection->addQuotes( $this->type );
+ }
+
+ while ( $connection->selectField( 'job', 'job_id', $conds, __METHOD__ ) ) {
+
+ $job = $this->type === null ? $this->pop() : $this->pop_type( $this->type );
+
+ if ( !$job ) {
+ break;
+ }
+
+ wfWaitForSlaves();
+
+ $this->status[] = [
+ 'type' => $job->command,
+ 'status' => $job->run()
+ ];
+ }
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function deleteAllJobs() {
+
+ $conditions = '*';
+ $connection = $this->connectionProvider->getConnection();
+
+ if ( $this->type !== null ) {
+ $conditions = "job_cmd = " . $connection->addQuotes( $this->type );
+ }
+
+ $connection->delete(
+ 'job',
+ $conditions,
+ __METHOD__
+ );
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return array
+ */
+ public function getStatus() {
+ return $this->status;
+ }
+
+ /**
+ * @see https://gerrit.wikimedia.org/r/#/c/162009/
+ */
+ private function pop() {
+ $offset = 0;
+
+ if ( class_exists( 'JobQueueGroup' ) ) {
+ return JobQueueGroup::singleton()->pop();
+ }
+
+ return Job::pop( $offset );
+ }
+
+ /**
+ * @see https://gerrit.wikimedia.org/r/#/c/162009/
+ */
+ public function pop_type( $type ) {
+
+ if ( class_exists( 'JobQueueGroup' ) ) {
+ return JobQueueGroup::singleton()->get( $type )->pop();
+ }
+
+ return Job::pop_type( $type );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/MaintenanceRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/MaintenanceRunner.php
new file mode 100644
index 00000000..17741cea
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/MaintenanceRunner.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SMW\Tests\Utils\Runners;
+
+use DomainException;
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use RuntimeException;
+use SMW\ApplicationFactory;
+
+/**
+ * Running maintenance scripts via phpunit is not really possible but instead
+ * this class allows to execute script related classes that are equivalent to:
+ * `php rebuildData.php --< myOptions >`
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class MaintenanceRunner {
+
+ use MessageReporterAwareTrait;
+
+ protected $maintenanceClass = null;
+ protected $options = [];
+ protected $output = null;
+ protected $quiet = false;
+
+ /**
+ * @since 1.9.2
+ *
+ * @param string $maintenanceClass
+ * @param array $options
+ */
+ public function __construct( $maintenanceClass, $options = [] ) {
+ $this->maintenanceClass = $maintenanceClass;
+ $this->options = $options;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @param array $options
+ *
+ * @return MaintenanceRunner
+ */
+ public function setOptions( array $options ) {
+ $this->options = $options;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return MaintenanceRunner
+ */
+ public function setQuiet() {
+ $this->options['quiet'] = true;
+ return $this;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return boolean
+ * @throws RuntimeException
+ * @throws DomainException
+ */
+ public function run() {
+
+ if ( !class_exists( $this->maintenanceClass ) ) {
+ throw new RuntimeException( "Expected a valid {$this->maintenanceClass} class" );
+ }
+
+ $obLevel = ob_get_level();
+
+ // Avoid outdated reference to invoked store instance
+ ApplicationFactory::getInstance()->clear();
+ $maintenance = new $this->maintenanceClass;
+
+ if ( !( $maintenance instanceof \Maintenance ) ) {
+ throw new DomainException( "Expected a Maintenance instance" );
+ }
+
+ // isset/ null
+ if ( isset( $this->options['quiet'] ) && $this->options['quiet'] === false ) {
+ unset( $this->options['quiet'] );
+ }
+
+ $maintenance->loadParamsAndArgs(
+ $this->maintenanceClass,
+ $this->options
+ );
+
+ if ( $this->messageReporter !== null && method_exists( $maintenance, 'setMessageReporter' ) ) {
+ $maintenance->setMessageReporter( $this->messageReporter );
+ }
+
+ ob_start();
+ $result = $maintenance->execute();
+ $this->output = ob_get_contents();
+
+ while ( ob_get_level() > $obLevel ) {
+ ob_end_clean();
+ }
+
+ return $result;
+ }
+
+ /**
+ * @since 1.9.2
+ *
+ * @return string
+ */
+ public function getOutput() {
+ return $this->output;
+ }
+
+}
+
+// FIXME SemanticGlossary usage
+class_alias( 'SMW\Tests\Utils\Runners\MaintenanceRunner', 'SMW\Tests\Util\MaintenanceRunner' );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/RunnerFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/RunnerFactory.php
new file mode 100644
index 00000000..efa8d8ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/RunnerFactory.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace SMW\Tests\Utils\Runners;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class RunnerFactory {
+
+ /**
+ * @var RunnerFactory
+ */
+ private static $instance = null;
+
+ /**
+ * @since 2.1
+ *
+ * @return RunnerFactory
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $maintenanceClass
+ *
+ * @return MaintenanceRunner
+ */
+ public function newMaintenanceRunner( $maintenanceClass ) {
+
+ switch ( $maintenanceClass ) {
+ case 'rebuildPropertyStatistics':
+ $maintenanceClass = 'SMW\Maintenance\RebuildPropertyStatistics';
+ break;
+ case 'rebuildData':
+ $maintenanceClass = 'SMW\Maintenance\RebuildData';
+ break;
+ case 'rebuildConceptCache';
+ $maintenanceClass = 'SMW\Maintenance\RebuildConceptCache';
+ break;
+ case 'setupStore';
+ $maintenanceClass = 'SMW\Maintenance\SetupStore';
+ break;
+ }
+
+ return new MaintenanceRunner( $maintenanceClass );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $jobType
+ *
+ * @return JobQueueRunner
+ */
+ public function newJobQueueRunner( $jobType = null ) {
+ return new JobQueueRunner( $jobType );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string|null $source
+ *
+ * @return XmlImportRunner
+ */
+ public function newXmlImportRunner( $source = null ) {
+ return new XmlImportRunner( $source );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/XmlImportRunner.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/XmlImportRunner.php
new file mode 100644
index 00000000..1ea6ba7f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Runners/XmlImportRunner.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace SMW\Tests\Utils\Runners;
+
+use ImportReporter;
+use ImportStreamSource;
+use RequestContext;
+use RuntimeException;
+use SMW\Tests\TestEnvironment;
+use WikiImporter;
+
+/**
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class XmlImportRunner {
+
+ protected $file = null;
+ protected $requestContext = null;
+ protected $exception = false;
+ protected $result = null;
+ protected $verbose = false;
+
+ /**
+ * @var TestEnvironment
+ */
+ private $testEnvironment;
+
+ public function __construct( $file = null ) {
+ $this->file = $file;
+ $this->testEnvironment = new TestEnvironment();
+ }
+
+ /**
+ * @param string $file
+ */
+ public function setFile( $file ) {
+ $this->file = $file;
+ }
+
+ /**
+ * @param boolean $verbose
+ *
+ * @return XmlImportRunner
+ */
+ public function setVerbose( $verbose = true ) {
+ $this->verbose = $verbose;
+ return $this;
+ }
+
+ /**
+ * @param RequestContext $requestContext
+ */
+ public function setRequestContext( RequestContext $requestContext ) {
+ $this->requestContext = $requestContext;
+ }
+
+ /**
+ * @throws RuntimeException
+ * @return boolean
+ */
+ public function run() {
+
+ $this->unregisterUploadsource();
+ $start = microtime( true );
+ $config = null;
+
+ $source = ImportStreamSource::newFromFile(
+ $this->assertThatFileIsReadableOrThrowException( $this->file )
+ );
+
+ if ( !$source->isGood() ) {
+ throw new RuntimeException( 'Import returned with error(s) ' . serialize( $source->errors ) );
+ }
+
+ // WikiImporter::__construct without a Config instance was deprecated in MediaWiki 1.25.
+ if ( class_exists( '\ConfigFactory' ) ) {
+ $config = \ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+ }
+
+ $importer = new WikiImporter( $source->value, $config );
+ $importer->setDebug( $this->verbose );
+
+ $reporter = new ImportReporter(
+ $importer,
+ false,
+ '',
+ false
+ );
+
+ $reporter->setContext( $this->acquireRequestContext() );
+ $reporter->open();
+ $this->exception = false;
+
+ try {
+ $importer->doImport();
+ } catch ( \Exception $e ) {
+ $this->exception = $e;
+ }
+
+ $this->result = $reporter->close();
+ $this->importTime = microtime( true ) - $start;
+
+ $this->testEnvironment->executePendingDeferredUpdates();
+
+ return $this->result->isGood() && !$this->exception;
+ }
+
+ /**
+ * @throws RuntimeException
+ */
+ public function reportFailedImport() {
+
+ $exceptionAsString = '';
+
+ if ( $this->exception ) {
+ $exceptionAsString = $this->exception->getMessage() . '#' . $this->exception->getTraceAsString();
+ }
+
+ throw new RuntimeException(
+ 'Import failed with ' . "\n" .
+ $exceptionAsString . "\n" .
+ $this->result->getWikiText()
+ );
+ }
+
+ /**
+ * @return integer
+ */
+ public function getElapsedImportTimeInSeconds() {
+ return round( $this->importTime, 7 );
+ }
+
+ protected function acquireRequestContext() {
+
+ if ( $this->requestContext === null ) {
+ $this->requestContext = new RequestContext();
+ }
+
+ return $this->requestContext;
+ }
+
+ private function assertThatFileIsReadableOrThrowException( $file ) {
+
+ $file = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $file );
+
+ if ( is_readable( $file ) ) {
+ return $file;
+ }
+
+ throw new RuntimeException( "Source file {$file} is not accessible" );
+ }
+
+ /**
+ * MW has an implementation issue on how to use stream_wrapper_register
+ * which causes a "Protocol uploadsource:// is already defined" failure when
+ * used multiple times during import
+ *
+ * @see https://gerrit.wikimedia.org/r/#/c/94351/
+ */
+ private function unregisterUploadsource() {
+ if ( in_array( 'uploadsource', stream_get_wrappers() ) ) {
+ stream_wrapper_unregister( 'uploadsource' );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SemanticDataFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SemanticDataFactory.php
new file mode 100644
index 00000000..2b7d620b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SemanticDataFactory.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMW\SemanticData;
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class SemanticDataFactory {
+
+ private $subject = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param Title|string $title
+ */
+ public function setTitle( $title ) {
+
+ if ( is_string( $title ) ) {
+ $title = Title::newFromText( $title );
+ }
+
+ if ( $title instanceof Title ) {
+ return $this->setSubject( DIWikiPage::newFromTitle( $title ) );
+ }
+
+ throw new RuntimeException( "Something went wrong" );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param DIWikiPage $subject
+ */
+ public function setSubject( DIWikiPage $subject ) {
+ $this->subject = $subject;
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param string $title
+ *
+ * @return SemanticData
+ * @throws RuntimeException
+ */
+ public function newEmptySemanticData( $title = null ) {
+
+ if ( $title instanceof DIWikiPage ) {
+ $this->setSubject( $title );
+ } elseif ( $title !== null ) {
+ $this->setTitle( $title );
+ }
+
+ if ( $this->subject !== null ) {
+ return new SemanticData( $this->subject );
+ }
+
+ throw new RuntimeException( "Something went wrong" );
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function null() {
+ $this->subject = null;
+ return this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SpyLogger.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SpyLogger.php
new file mode 100644
index 00000000..bd18da13
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/SpyLogger.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Psr\Log\AbstractLogger;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class SpyLogger extends AbstractLogger {
+
+ /**
+ * @var array
+ */
+ private $logs = [];
+
+ /**
+ * @since 2.5
+ *
+ * {@inheritDoc}
+ */
+ public function log( $level, $message, array $context = [] ) {
+
+ if ( is_array( $message ) ) {
+ $message = json_encode( $message );
+ }
+
+ $this->logs[] = [ $level, $message, $context ];
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getLogs() {
+ return $this->logs;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string
+ */
+ public function getMessagesAsString() {
+ $message = '';
+
+ foreach ( $this->logs as $log ) {
+ $message .= ' ' . $log[1];
+ }
+
+ return $message;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/StringBuilder.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/StringBuilder.php
new file mode 100644
index 00000000..21a0a087
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/StringBuilder.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ */
+class StringBuilder {
+
+ private $string = '';
+
+ /**
+ * @since 2.0
+ */
+ public function addnewLine() {
+ $this->string = $this->string . "\n";
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function addString( $string ) {
+ $this->string = $this->string . $string;
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public function getString() {
+ $string = $this->string;
+ $this->string = '';
+ return $string;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/UtilityFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/UtilityFactory.php
new file mode 100644
index 00000000..785db5b1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/UtilityFactory.php
@@ -0,0 +1,248 @@
+<?php
+
+namespace SMW\Tests\Utils;
+
+use Onoi\MessageReporter\SpyMessageReporter;
+use SMW\Tests\Utils\File\BulkFileProvider;
+use SMW\Tests\Utils\File\DummyFileCreator;
+use SMW\Tests\Utils\File\JsonFileReader;
+use SMW\Tests\Utils\File\LocalFileUpload;
+use SMW\Tests\Utils\Fixtures\FixturesFactory;
+use SMW\Tests\Utils\Page\PageEditor;
+use SMW\Tests\Utils\Runners\RunnerFactory;
+use SMW\Tests\Utils\Validators\ValidatorFactory;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class UtilityFactory {
+
+ /**
+ * @var UtilityFactory
+ */
+ private static $instance = null;
+
+ /**
+ * @since 2.1
+ *
+ * @return UtilityFactory
+ */
+ public static function getInstance() {
+
+ if ( self::$instance === null ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MwApiFactory
+ */
+ public function newMwApiFactory() {
+ return new MwApiFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return ValidatorFactory
+ */
+ public function newValidatorFactory() {
+ return new ValidatorFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return StringBuilder
+ */
+ public function newStringBuilder() {
+ return new StringBuilder();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return MwHooksHandler
+ */
+ public function newMwHooksHandler() {
+ return new MwHooksHandler();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return ParserFactory
+ */
+ public function newParserFactory() {
+ return new ParserFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return FixturesFactory
+ */
+ public function newFixturesFactory() {
+ return new FixturesFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticDataFactory
+ */
+ public function newSemanticDataFactory() {
+ return new SemanticDataFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return RunnerFactory
+ */
+ public function newRunnerFactory() {
+ return new RunnerFactory();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return PageDeleter
+ */
+ public function newPageDeleter() {
+ return new PageDeleter();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return PageRefresher
+ */
+ public function newPageRefresher() {
+ return new PageRefresher();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return PageCreator
+ */
+ public function newPageCreator() {
+ return new PageCreator();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return PageEditor
+ */
+ public function newPageEditor() {
+ return new PageEditor();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return PageReader
+ */
+ public function newPageReader() {
+ return new PageReader();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $file|null
+ *
+ * @return JsonFileReader
+ */
+ public function newJsonFileReader( $file = null ) {
+ return new JsonFileReader( $file );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param string $path
+ *
+ * @return BulkFileProvider
+ */
+ public function newBulkFileProvider( $path ) {
+ return new BulkFileProvider( $path );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param string $localUploadPath
+ * @param $desiredDestName
+ *
+ * @return LocalFileUpload
+ */
+ public function newLocalFileUploadWithCopy( $localUploadPath, $desiredDestName ) {
+
+ // Use to create a copy to avoid having the original file being
+ // deleted after the upload
+ $dummyFileCreator = new DummyFileCreator();
+ $dummyFileCreator->createFileWithCopyFrom( $desiredDestName, $localUploadPath );
+
+ return new LocalFileUpload(
+ $dummyFileCreator->getPath(),
+ $desiredDestName
+ );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return LocalFileUpload
+ */
+ public function newLocalFileUpload() {
+
+ $localFileUpload = new LocalFileUpload();
+
+ $localFileUpload->setDummyFileCreator(
+ new DummyFileCreator()
+ );
+
+ return $localFileUpload;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param $length
+ * @param $prefix identify a specific random string during testing
+ *
+ * @return string
+ */
+ public function createRandomString( $length = 10, $prefix = null ) {
+ return $prefix . ( $prefix ? '-' : '' ) . substr( str_shuffle( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ), 0, $length );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SpyLogger
+ */
+ public function newSpyLogger() {
+ return new SpyLogger();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return SpyMessageReporter
+ */
+ public function newSpyMessageReporter() {
+ return new SpyMessageReporter();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ExportDataValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ExportDataValidator.php
new file mode 100644
index 00000000..fc89a262
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ExportDataValidator.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use SMWExpData as ExpData;
+use SMWExpResource as ExpResource;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExportDataValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $expected
+ * @param ExpData $exportData
+ */
+ public function assertThatExportDataContainsProperty( $expectedProperties, ExpData $exportData ) {
+
+ $expProperties = $exportData->getProperties();
+
+ $this->assertNotEmpty( $expProperties );
+
+ $expectedProperties = is_array( $expectedProperties ) ? $expectedProperties : [ $expectedProperties ];
+ $expectedToCount = count( $expectedProperties );
+ $actualComparedToCount = 0;
+
+ $assertThatExportDataContainsProperty = false;
+
+ foreach ( $expProperties as $expProperty ) {
+ foreach ( $expectedProperties as $expectedProperty ) {
+ if ( $expectedProperty->getHash() === $expProperty->getHash() ) {
+ $actualComparedToCount++;
+ $assertThatExportDataContainsProperty = true;
+ }
+ }
+ }
+
+ $this->assertTrue( $assertThatExportDataContainsProperty );
+
+ $this->assertEquals(
+ $expectedToCount,
+ $actualComparedToCount
+ );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $expected
+ * @param ExpResource $selectedElement
+ * @param ExpData $exportData
+ */
+ public function assertThatExportDataContainsResource( $expectedResources, ExpResource $selectedElement, ExpData $exportData ) {
+
+ $expElements = $exportData->getValues( $selectedElement );
+
+ $this->assertNotEmpty( $expElements );
+
+ $expectedResources = is_array( $expectedResources ) ? $expectedResources : [ $expectedResources ];
+ $expectedToCount = count( $expectedResources );
+ $actualComparedToCount = 0;
+
+ $assertThatExportDataContainsResource = false;
+
+ foreach ( $expElements as $expElement ) {
+ foreach ( $expectedResources as $expectedResource ) {
+ if ( $expectedResource->getHash() === $expElement->getHash() ) {
+ $actualComparedToCount++;
+ $assertThatExportDataContainsResource = true;
+ }
+ }
+ }
+
+ $this->assertTrue(
+ $assertThatExportDataContainsResource,
+ 'Asserts that a resource is set'
+ );
+
+ $this->assertEquals(
+ $expectedToCount,
+ $actualComparedToCount,
+ 'Asserts that all listed resources are set'
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/HtmlValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/HtmlValidator.php
new file mode 100644
index 00000000..66af8019
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/HtmlValidator.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use DOMDocument;
+use Symfony\Component\CssSelector\CssSelectorConverter;
+
+/**
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author Stephan Gambke
+ */
+class HtmlValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @var array
+ */
+ private $documentCache = [];
+
+ /**
+ * @var null|boolean
+ */
+ private $canUse;
+
+ /**
+ * @param string $actual
+ * @param string $message
+ */
+ public function assertThatHtmlIsValid( $actual, $message = '' ) {
+
+ $document = $this->getDomDocumentFromHtmlFragment( $actual );
+
+ self::assertTrue( $document !== false, "Failed test `{$message}` (assertion HtmlIsValid) for $actual" );
+ }
+
+ /**
+ * @return boolean
+ */
+ public function canUse() {
+
+ if ( $this->canUse === null ) {
+ $this->canUse = class_exists( '\Symfony\Component\CssSelector\CssSelectorConverter' );
+ }
+
+ return $this->canUse;
+ }
+
+ /**
+ * @param string $fragment
+ *
+ * @return bool|DOMDocument
+ */
+ private function getDomDocumentFromHtmlFragment( $fragment ) {
+
+ $cacheKey = md5( $fragment );
+
+ if ( !isset( $this->documentCache[ $cacheKey ] ) ) {
+ $this->addHtmlFragmentToCache( $fragment, $cacheKey );
+ }
+
+ return $this->documentCache[ $cacheKey ];
+ }
+
+ /**
+ * @param $fragment
+ * @param $cacheKey
+ */
+ private function addHtmlFragmentToCache( $fragment, $cacheKey ) {
+
+ $fragment = self::wrapHtmlFragment( $fragment );
+
+ $document = new DOMDocument();
+ $document->preserveWhiteSpace = false;
+
+ libxml_use_internal_errors( true );
+ $result = $document->loadHTML( $fragment );
+ libxml_use_internal_errors( false );
+
+ $this->documentCache[ $cacheKey ] = ( $result === true ) ? $document : false;
+
+ }
+
+ /**
+ * @param string $fragment
+ *
+ * @return string
+ */
+ private static function wrapHtmlFragment( $fragment ) {
+ return "<!DOCTYPE html><html><head><meta charset='utf-8'/><title>SomeTitle</title></head><body>$fragment</body></html>";
+ }
+
+ /**
+ * @param string | string[] $cssSelectors
+ * @param string $htmlFragment
+ * @param string $message
+ */
+ public function assertThatHtmlContains( $cssSelectors, $htmlFragment, $message = '', $expected = true ) {
+
+ $document = $this->getDomDocumentFromHtmlFragment( $htmlFragment );
+ $xpath = new \DOMXPath( $document );
+ $converter = new CssSelectorConverter();
+
+ foreach ( $cssSelectors as $selector ) {
+
+ if ( is_array( $selector ) ) {
+ $expectedCount = array_pop( $selector );
+ $expectedCountText = ( ( $expected === true) ? '' : 'not ') . $expectedCount;
+ $selector = array_shift( $selector );
+ } else {
+ $expectedCount = false;
+ $expectedCountText = ( $expected === true) ? 'at least 1' : 'none';
+ }
+
+ $message = "Failed assertion for test case `{$message}` on: \n=====\n$htmlFragment\n=====\nExpected pattern: `$selector`\n";
+
+ try {
+ // Symfony\Component\CssSelector\Exception\SyntaxErrorException: Expected selector ...
+ $entries = $xpath->evaluate( $converter->toXPath( $selector ) );
+ $actualCount = $entries->length;
+
+ $message .= "Expected occurrences: {$expectedCountText}\nFound occurrences: {$actualCount}\n";
+
+ } catch ( \Exception $e ) {
+ $actualCount = 0;
+ $message .= "CssSelector: " . $e->getMessage();
+ }
+
+ self::assertTrue( ( ( $expectedCount === false && $actualCount > 0 ) || ( $actualCount === $expectedCount ) ) === $expected, $message );
+ }
+ }
+
+ /**
+ * @param string | string[] $cssSelectors
+ * @param string $htmlFragment
+ * @param string $message
+ */
+ public function assertThatHtmlNotContains( $cssSelectors, $htmlFragment, $message = '' ) {
+ $this->assertThatHtmlContains( $cssSelectors, $htmlFragment, $message, false );
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/IncomingSemanticDataValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/IncomingSemanticDataValidator.php
new file mode 100644
index 00000000..ed7fc6f5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/IncomingSemanticDataValidator.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use SMW\DIWikiPage;
+use SMW\Store;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class IncomingSemanticDataValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ */
+ public function __construct( Store $store ) {
+ $this->store = $store;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $incomingSemanticData [description]
+ * @param DIWikiPage $subject
+ * @param string $message
+ */
+ public function assertThatIncomingDataAreSet( array $incomingSemanticData, DIWikiPage $subject, $message ) {
+
+ if ( !isset( $incomingSemanticData['propertyKeys'] ) ) {
+ return;
+ }
+
+ $incomingProperties = $this->store->getInProperties( $subject );
+
+ $this->assertCount(
+ count( $incomingSemanticData['propertyKeys'] ),
+ $incomingProperties,
+ "Failed asserting count for incoming `propertyKeys` on " . $message . '"'
+ );
+
+ $this->doAssertPropertiesAndValues(
+ $incomingSemanticData,
+ $incomingProperties,
+ $subject,
+ $message
+ );
+ }
+
+ private function doAssertPropertiesAndValues( $incomingSemanticData, $incomingProperties, $subject, $message ) {
+
+ $incomingPropertyValues = [];
+
+ foreach ( $incomingProperties as $property ) {
+
+ $key = $property->getKey();
+
+ $this->assertContains(
+ $key,
+ $incomingSemanticData['propertyKeys'],
+ $this->createMessage( 'propertyKeys', $key, $message, $incomingSemanticData['propertyKeys'] )
+ );
+
+ if ( !isset( $incomingSemanticData['propertyValues'] ) ) {
+ continue;
+ }
+
+ $propertySubjects = $this->store->getPropertySubjects( $property, $subject );
+
+ foreach ( $propertySubjects as $propertySubject ) {
+ $incomingPropertyValues[] = $propertySubject->getSerialization();
+ }
+ }
+
+ foreach ( $incomingPropertyValues as $value ) {
+ $this->assertContains(
+ $value,
+ $incomingSemanticData['propertyValues'],
+ $this->createMessage( 'propertyValues', $value, $message, $incomingSemanticData['propertyValues'] )
+ );
+ }
+ }
+
+ private function createMessage( $section, $key, $message, array $data ) {
+ return "Failed asserting that '{$key}' for incoming `$section` on \"" . $message . '" is listed in [ ' . implode( ',', $data ) . ' ]';
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/NumberValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/NumberValidator.php
new file mode 100644
index 00000000..1bd68f3c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/NumberValidator.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use SMW\Query\QueryComparator;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class NumberValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|string $expected
+ * @param integer|string $actual
+ */
+ public function assertThatNumberComparesTo( $expected, $actual, $message = '' ) {
+ return $this->doAssertWith( $expected, $actual, $message );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer|string $expected
+ * @param array $actual
+ */
+ public function assertThatCountComparesTo( $expected, array $actual, $message = '' ) {
+ return $this->doAssertWith( $expected, count( $actual ), $message );
+ }
+
+ private function doAssertWith( $expected, $actual, $message ) {
+
+ $comparator = QueryComparator::getInstance()->extractComparatorFromString( $expected );
+
+ $expected = (int)$expected;
+ $actual = (int)$actual;
+
+ if ( $comparator === SMW_CMP_EQ ) {
+ return self::assertEquals( $expected, $actual, $message );
+ }
+
+ if ( $comparator === SMW_CMP_GRTR ) {
+ return self::assertGreaterThan( $expected, $actual, $message );
+ }
+
+ if ( $comparator === SMW_CMP_GEQ ) {
+ return self::assertGreaterThanOrEqual( $expected, $actual, $message );
+ }
+
+ if ( $comparator === SMW_CMP_LESS ) {
+ return self::assertLessThan( $expected, $actual, $message );
+ }
+
+ if ( $comparator === SMW_CMP_LEQ ) {
+ return self::assertLessThanOrEqual( $expected, $actual, $message );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QueryResultValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QueryResultValidator.php
new file mode 100644
index 00000000..c349943e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QueryResultValidator.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use Closure;
+use RuntimeException;
+use SMW\DIWikiPage;
+use SMWDataItem as DataItem;
+use SMWDataValue as DataValue;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class QueryResultValidator extends \PHPUnit_Framework_Assert {
+
+ private $dataValueValidationMethod = null;
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $expected
+ * @param QueryResult $queryResult
+ *
+ * @throws RuntimeException
+ */
+ public function assertThatQueryResultContains( $expected, QueryResult $queryResult ) {
+
+ if ( $expected instanceof DataValue ) {
+ return $this->assertThatDataValueIsSet( $expected, $queryResult );
+ }
+
+ if ( $expected instanceof DataItem ) {
+ return $this->assertThatDataItemIsSet( $expected, $queryResult );
+ }
+
+ throw new RuntimeException( "Expected object is unknown or not registered" );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param $expected
+ * @param QueryResult $queryResult
+ * @param string $message
+ */
+ public function assertThatDataValueIsSet( $expected, QueryResult $queryResult, $message = '' ) {
+
+ $expected = is_array( $expected ) ? $expected : [ $expected ];
+
+ if ( $expected === [] ) {
+ return;
+ }
+
+ $errors = $queryResult->getErrors();
+
+ $this->assertEmpty(
+ $errors,
+ "Failed on {$message} with error(s): " . implode( ',', $errors )
+ );
+
+ if ( $this->dataValueValidationMethod === null ) {
+ $this->useWikiValueForDataValueValidation();
+ }
+
+ while ( $resultArray = $queryResult->getNext() ) {
+ foreach ( $resultArray as $result ) {
+ while ( ( $dataValue = $result->getNextDataValue() ) !== false ) {
+ foreach ( $expected as $key => $exp ) {
+ if ( call_user_func_array( $this->dataValueValidationMethod, [ $exp, $dataValue ] ) ) {
+ unset( $expected[$key] );
+ }
+ }
+ }
+ }
+ }
+
+ $this->assertEmpty(
+ $expected,
+ "Failed on on {$message} to match datavalues [ " . implode( ', ', $expected ) . ' ] against the expected results.'
+ );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $expected
+ * @param QueryResult $queryResult
+ * @param string $message
+ */
+ public function assertThatDataItemIsSet( $expected, QueryResult $queryResult, $message = '' ) {
+
+ $expected = is_array( $expected ) ? $expected : [ $expected ];
+
+ if ( $expected === [] ) {
+ return;
+ }
+
+ $errors = $queryResult->getErrors();
+
+ $this->assertEmpty(
+ $errors,
+ "Failed on {$message} with error(s): " . implode( ',', $errors )
+ );
+
+ while ( $resultArray = $queryResult->getNext() ) {
+ foreach ( $resultArray as $result ) {
+ while ( ( $dataItem = $result->getNextDataItem() ) !== false ) {
+ foreach ( $expected as $key => $exp ) {
+ if ( $exp->equals( $dataItem ) ) {
+ unset( $expected[$key] );
+ }
+ }
+ }
+ }
+ }
+
+ $this->assertEmpty(
+ $expected,
+ "Failed on {$message} to match dataItems [ " . implode( ', ', $expected ) . ' ] against the expected results.'
+ );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param mixed $expectedSubjects
+ * @param QueryResult $queryResult
+ * @param string $message
+ */
+ public function assertThatQueryResultHasSubjects( $expectedSubjects, QueryResult $queryResult, $message = '' ) {
+
+ $expectedSubjects = is_array( $expectedSubjects ) ? $expectedSubjects : [ $expectedSubjects ];
+ $expectedToCount = count( $expectedSubjects );
+ $actualComparedToCount = 0;
+
+ $errors = $queryResult->getErrors();
+
+ $this->assertEmpty(
+ $errors,
+ "Failed on {$message} with error(s): " . implode( ',', $errors )
+ );
+
+ if ( $expectedToCount == 0 ) {
+ return;
+ }
+
+ $resultSubjects = $queryResult->getResults();
+
+ foreach ( $resultSubjects as $rKey => $resultSubject ) {
+ foreach ( $expectedSubjects as $ekey => $expectedSubject ) {
+
+ if ( $expectedSubject instanceof DIWikiPage && $expectedSubject->equals( $resultSubject ) ) {
+ $actualComparedToCount++;
+ unset( $expectedSubjects[$ekey] );
+ unset( $resultSubjects[$rKey] );
+ }
+ }
+ }
+
+ $this->assertEquals(
+ $expectedToCount,
+ $actualComparedToCount,
+ "Failed on {$message} asserting that " . implode( ', ', $expectedSubjects ) . ' is set.'
+ );
+
+ $this->assertEmpty(
+ $resultSubjects,
+ "Failed on {$message} to match results [ " . implode( ', ', $resultSubjects ) . ' ] against the expected subjects.'
+ );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Closure $validationMethod
+ *
+ * @return QueryResultValidator
+ */
+ public function registerCustomMethodForDataValueValidation( Closure $validationMethod ) {
+ $this->dataValueValidationMethod = $validationMethod;
+ return $this;
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @return QueryResultValidator
+ */
+ public function useWikiValueForDataValueValidation() {
+
+ $this->dataValueValidationMethod = function( DataValue $expectedDataValue, DataValue $dataValue ) {
+ return $expectedDataValue->getWikiValue() === $dataValue->getWikiValue();
+ };
+
+ return $this;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QuerySegmentValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QuerySegmentValidator.php
new file mode 100644
index 00000000..4c6bfac4
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/QuerySegmentValidator.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use SMW\SQLStore\QueryEngine\QuerySegment;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class QuerySegmentValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @since 2.1
+ *
+ * @param mixed $expected
+ * @param QuerySegment[] $querySegment
+ */
+ public function assertThatContainerContains( $expected, array $querySegment ) {
+
+ $expected = is_array( $expected ) ? $expected : [ $expected ];
+
+ $this->assertEquals(
+ count( $expected ),
+ count( $querySegment ),
+ $this->formatMessage( 'container count', count( $expected ), count( $querySegment ) )
+ );
+
+ foreach ( $querySegment as $key => $container ) {
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\QueryEngine\QuerySegment',
+ $container
+ );
+
+ $this->assertThatContainerHasProperties( $expected[$key], $container );
+ }
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param mixed $expected
+ * @param QuerySegment $querySegment
+ */
+ public function assertThatContainerHasProperties( $expected, QuerySegment $querySegment ) {
+ $this->assertPublicProperty( $expected, $querySegment, 'type' );
+ $this->assertPublicProperty( $expected, $querySegment, 'joinfield' );
+ $this->assertPublicProperty( $expected, $querySegment, 'jointable' );
+ $this->assertPublicProperty( $expected, $querySegment, 'sortfields' );
+ $this->assertPublicProperty( $expected, $querySegment, 'components' );
+ $this->assertPublicProperty( $expected, $querySegment, 'alias' );
+ $this->assertPublicProperty( $expected, $querySegment, 'where' );
+ $this->assertPublicProperty( $expected, $querySegment, 'from' );
+ $this->assertPublicProperty( $expected, $querySegment, 'queryNumber' );
+ }
+
+ private function assertPublicProperty( $expected, QuerySegment $querySegment, $property ) {
+
+ if ( !isset( $expected->{$property} ) ) {
+ return null;
+ }
+
+ $this->assertTrue(
+ $expected->{$property} == $querySegment->{$property},
+ $this->formatMessage( $property, $expected->{$property}, $querySegment->{$property} )
+ );
+ }
+
+ private function formatMessage( $id, $expected, $actual ) {
+ return "Asserts {$id} to be expected [ " . $this->formatAsString( $expected ) . ' ] vs. actual [ ' . $this->formatAsString( $actual ) .' ]';
+ }
+
+ private function formatAsString( $expected ) {
+ return is_array( $expected ) ? implode( ', ', $expected ) : $expected;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/SemanticDataValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/SemanticDataValidator.php
new file mode 100644
index 00000000..8eec37b2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/SemanticDataValidator.php
@@ -0,0 +1,436 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\SemanticData;
+use SMWDataItem as DataItem;
+
+/**
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class SemanticDataValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @var boolean
+ */
+ private $strictModeForValueMatch = true;
+
+ /**
+ * @param boolean $strictMode
+ */
+ public function setStrictModeForValueMatch( $strictMode ) {
+ $this->strictModeForValueMatch = (bool)$strictMode;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param SemanticData $semanticData
+ */
+ public function assertThatSemanticDataIsEmpty( SemanticData $semanticData ) {
+ $this->assertTrue(
+ $this->assertThatSemanticDataIsIndeedEmpty( $semanticData ),
+ 'Asserts that the SemanticData container is empty'
+ );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param SemanticData $semanticData
+ */
+ public function assertThatSemanticDataIsNotEmpty( SemanticData $semanticData ) {
+ $this->assertFalse(
+ $this->assertThatSemanticDataIsIndeedEmpty( $semanticData ),
+ 'Asserts that the SemanticData container is not empty'
+ );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param array $expected
+ * @param SemanticData $semanticData
+ */
+ public function assertThatCategoriesAreSet( array $expected, SemanticData $semanticData ) {
+
+ $runCategoryInstanceAssert = false;
+
+ foreach ( $semanticData->getProperties() as $property ) {
+
+ if ( $property->getKey() === DIProperty::TYPE_CATEGORY ) {
+ $runCategoryInstanceAssert = true;
+
+ $this->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $semanticData->getPropertyValues( $property )
+ );
+ }
+
+ if ( $property->getKey() === DIProperty::TYPE_SUBCATEGORY ) {
+ $runCategoryInstanceAssert = true;
+
+ $this->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $semanticData->getPropertyValues( $property )
+ );
+ }
+
+ }
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runCategoryInstanceAssert, __METHOD__ );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param integer $count
+ * @param SemanticData $semanticData
+ * @param string|null $msg
+ */
+ public function assertThatSemanticDataHasPropertyCountOf( $count, SemanticData $semanticData, $msg = null ) {
+
+ $prop = [];
+
+ foreach ( $semanticData->getProperties() as $property ) {
+ $prop[] = $property->getKey();
+ }
+
+ $msg = $msg === null ? "Failed asserting property count of {$count}" : $msg;
+ $msg .= ' Counted properties include: ' . json_encode( $prop, JSON_PRETTY_PRINT );
+
+ $this->assertCount( $count, $prop, $msg );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @param array $expected
+ * @param array $properties
+ */
+ public function assertHasProperties( array $expected, array $properties ) {
+
+ $expected = isset( $expected['properties'] ) ? $expected['properties'] : $expected;
+
+ foreach ( $properties as $property ) {
+
+ foreach ( $expected as $key => $expectedProperty ) {
+ if ( $property->equals( $expectedProperty ) ) {
+ unset( $expected[$key] );
+ }
+ }
+ }
+
+ $this->assertEmpty(
+ $expected,
+ 'Failed asserting that properties array contains [ ' . $this->formatAsString( $expected ) .' ].'
+ );
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param array $expected
+ * @param DIProperty $property
+ */
+ public function assertThatPropertyHasCharacteristicsAs( array $expected, DIProperty $property ) {
+
+ $runAssertThatPropertyHasCharacteristicsAs = false;
+
+ if ( isset( $expected['property'] ) ) {
+ $this->assertPropertyIsSameAs( $expected['property'], $property );
+ $runAssertThatPropertyHasCharacteristicsAs = true;
+ }
+
+ if ( isset( $expected['propertyKey'] ) ) {
+ $this->assertEquals(
+ $expected['propertyKey'],
+ $property->getKey(),
+ __METHOD__ . " asserts property key for {$property->getLabel()}"
+ );
+
+ $runAssertThatPropertyHasCharacteristicsAs = true;
+ }
+
+ if ( isset( $expected['propertyLabel'] ) ) {
+ $this->assertEquals(
+ $expected['propertyLabel'],
+ $property->getLabel(),
+ __METHOD__ . " asserts property label for '{$property->getKey()}'"
+ );
+
+ $runAssertThatPropertyHasCharacteristicsAs = true;
+ }
+
+ if ( isset( $expected['propertyTypeId'] ) ) {
+ $this->assertEquals(
+ $expected['propertyTypeId'],
+ $property->findPropertyTypeID(),
+ __METHOD__ . " asserts property typeId for '{$property->getKey()}'"
+ );
+
+ $runAssertThatPropertyHasCharacteristicsAs = true;
+ }
+
+ $this->assertTrue( $runAssertThatPropertyHasCharacteristicsAs, __METHOD__ );
+
+ }
+
+ /**
+ * Assertion array should follow:
+ * 'propertyCount' => int
+ * 'propertyLabels' => array() or 'propertyKeys' => array()
+ * 'propertyValues' => array()
+ *
+ * @since 1.9.1
+ *
+ * @param array $expected
+ * @param SemanticData $semanticData
+ */
+ public function assertThatPropertiesAreSet( array $expected, SemanticData $semanticData, $message = '' ) {
+
+ $runPropertiesAreSetAssert = false;
+ $properties = $semanticData->getProperties();
+
+ // Deprecated, use strictPropertyValueMatch
+ if ( isset( $expected['strict-mode-valuematch'] ) ) {
+ $this->setStrictModeForValueMatch( $expected['strict-mode-valuematch'] );
+ }
+
+ if ( isset( $expected['strictPropertyValueMatch'] ) ) {
+ $this->setStrictModeForValueMatch( $expected['strictPropertyValueMatch'] );
+ }
+
+ if ( isset( $expected['propertyCount'] ) ) {
+ $this->assertThatSemanticDataHasPropertyCountOf( $expected['propertyCount'], $semanticData, $message );
+ }
+
+ $report = [
+ '@unresolved' => [],
+ '@valueHint' => []
+ ];
+
+ foreach ( $properties as $property ) {
+
+ $this->assertInstanceOf( '\SMW\DIProperty', $property );
+
+ if ( isset( $expected['properties'] ) ) {
+ $this->assertContainsProperty( $expected['properties'], $property );
+ $runPropertiesAreSetAssert = true;
+ }
+
+ if ( isset( $expected['property'] ) ) {
+ $this->assertPropertyIsSameAs( $expected['property'], $property );
+ $runPropertiesAreSetAssert = true;
+ }
+
+ if ( isset( $expected['propertyKeys'] ) ) {
+ $this->assertContainsPropertyKeys( $expected['propertyKeys'], $property, $message );
+ $runPropertiesAreSetAssert = true;
+ }
+
+ if ( isset( $expected['propertyLabels'] ) ) {
+ $this->assertContainsPropertyLabels( $expected['propertyLabels'], $property );
+ $runPropertiesAreSetAssert = true;
+ }
+
+ if ( isset( $expected['propertyValues'] ) ) {
+ $pv = $semanticData->getPropertyValues( $property );
+ $report[$property->getKey()] = $this->formatAsString( $pv );
+
+ $this->assertThatPropertyValuesAreSet(
+ $expected,
+ $property,
+ $pv
+ );
+
+ $runPropertiesAreSetAssert = true;
+ }
+ }
+
+ // Final ceck for values distributed over different properties
+ if ( isset( $expected['propertyValues'] ) && !$this->strictModeForValueMatch ) {
+ $report['@unresolved'] = $expected['propertyValues'];
+ $report['@valueHint'] = $expected['@valueHint'];
+ $this->assertEmpty(
+ $expected['propertyValues'],
+ "Unmatched values in {$message} for:\n" . json_encode( $report, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES )
+ );
+ }
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runPropertiesAreSetAssert, __METHOD__ );
+
+ return $runPropertiesAreSetAssert;
+ }
+
+ /**
+ * @since 1.9.1
+ *
+ * @param array $expected
+ * @param DIProperty $property,
+ * @param array $dataItems
+ */
+ public function assertThatPropertyValuesAreSet( array &$expected, DIProperty $property, array $dataItems ) {
+
+ $runPropertyValueAssert = false;
+
+ if ( !isset( $expected['@valueHint'] ) ) {
+ $expected['@valueHint'] = [];
+ }
+
+ foreach ( $dataItems as $dataItem ) {
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $property );
+
+ switch ( $dataValue->getDataItem()->getDIType() ) {
+ case DataItem::TYPE_TIME:
+ $runPropertyValueAssert = $this->assertContainsPropertyValues( $expected, $dataValue, 'getISO8601Date' );
+ break;
+ case DataItem::TYPE_NUMBER:
+ $runPropertyValueAssert = $this->assertContainsPropertyValues( $expected, $dataValue, 'getNumber' );
+ break;
+ case DataItem::TYPE_BOOLEAN:
+ $runPropertyValueAssert = $this->assertContainsPropertyValues( $expected, $dataValue, 'getBoolean' );
+ break;
+ default:
+ $runPropertyValueAssert = $this->assertContainsPropertyValues( $expected, $dataValue, 'getWikiValue' );
+ break;
+ }
+ }
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runPropertyValueAssert, __METHOD__ );
+
+ return $runPropertyValueAssert;
+ }
+
+ private function assertThatSemanticDataIsIndeedEmpty( SemanticData $semanticData ) {
+
+ $property = new DIProperty( '_SKEY' );
+
+ foreach( $semanticData->getPropertyValues( $property ) as $dataItem ) {
+ $semanticData->removePropertyObjectValue( $property, $dataItem );
+ }
+
+ return $semanticData->isEmpty();
+ }
+
+ private function assertContainsPropertyKeys( $keys, DIProperty $property, $message = null ) {
+
+ $keys = str_replace( " ", "_", $keys );
+ $message = ( $message === null ? 'Failed asserting' : "Failed asserting \"$message\"" );
+
+ $this->assertContains(
+ $property->getKey(),
+ $keys,
+ "{$message} property key: '{$property->getLabel()}' in ({$this->formatAsString( $keys )})"
+ );
+ }
+
+ private function assertContainsPropertyLabels( $labels, DIProperty $property ) {
+ $this->assertContains(
+ $property->getLabel(),
+ $labels,
+ __METHOD__ . " asserts property label for '{$property->getKey()}' with ({$this->formatAsString( $labels )})"
+ );
+ }
+
+ private function assertContainsProperty( array $properties, DIProperty $property ) {
+
+ $runContainsPropertyAssert = false;
+
+ foreach ( $properties as $expectedproperty ) {
+
+ if ( $property->equals( $expectedproperty ) ) {
+ $runContainsPropertyAssert = true;
+ break;
+ }
+ }
+
+ // Issue #124 needs to be resolved first
+ // $this->assertTrue( $runContainsPropertyAssert, __METHOD__ );
+ }
+
+ private function assertPropertyIsSameAs( DIProperty $expectedproperty, DIProperty $property ) {
+ $this->assertTrue(
+ $property->equals( $expectedproperty ),
+ __METHOD__ . ' asserts that two properties are equal'
+ );
+ }
+
+ private function assertContainsPropertyValues( &$expected, $dataValue, $defaultFormatter, $formatterParameters = [] ) {
+
+ if ( !isset( $expected['propertyValues'] ) ) {
+ throw new RuntimeException( "Expected a 'propertyValues' array index" );
+ }
+
+ $formatter = [ $dataValue, $defaultFormatter ];
+ $valueSerialization = $dataValue->getDataItem()->getSerialization();
+
+ if ( isset( $expected['valueFormatter'] ) && is_callable( $expected['valueFormatter'] ) ) {
+ $formatter = $expected['valueFormatter'];
+ $formatterParameters = [ $dataValue ];
+ }
+
+ $value = call_user_func_array( $formatter, $formatterParameters );
+ $expected['@valueHint'][] = $value;
+
+ if ( $this->strictModeForValueMatch ) {
+
+ $this->assertContains(
+ $value,
+ $expected['propertyValues'],
+ __METHOD__ .
+ " for '{$dataValue->getProperty()->getKey()}'" .
+ " as '{$dataValue->getTypeID()}'" .
+ " with ({$this->formatAsString( $expected['propertyValues'] )})"
+ );
+
+ return true;
+ }
+
+ // Be more lenient towards value comparison by just eliminating a matched pair
+ foreach ( $expected['propertyValues'] as $key => $propertyValue ) {
+
+ if ( is_bool( $value ) && $value === $propertyValue ) {
+ unset( $expected['propertyValues'][$key] );
+ continue;
+ }
+
+ if ( ( is_numeric( $value ) && is_numeric( $propertyValue ) ) && $value == $propertyValue ) {
+ unset( $expected['propertyValues'][$key] );
+ continue;
+ }
+
+ if ( strpos( $propertyValue, (string)$value ) !== false ) {
+ unset( $expected['propertyValues'][$key] );
+ continue;
+ }
+
+ if ( $propertyValue === $valueSerialization ) {
+ unset( $expected['propertyValues'][$key] );
+ }
+ }
+
+ return true;
+ }
+
+ private function formatAsString( $expected ) {
+ return is_array( $expected ) ? implode( ', ', $expected ) : $expected;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/StringValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/StringValidator.php
new file mode 100644
index 00000000..ed128e0f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/StringValidator.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class StringValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @since 2.1
+ *
+ * @param mixed $expected
+ * @param string $actual
+ */
+ public function assertThatStringContains( $expected, $actual, $message = '' ) {
+
+ $callback = function( &$expected, $actual, &$actualCounted ) {
+ foreach ( $expected as $key => $pattern ) {
+ if ( $this->isMatch( $pattern, $actual ) ) {
+ $actualCounted++;
+ unset( $expected[$key] );
+ }
+ }
+ };
+
+ $this->doAssertWith( $expected, $actual, $message, 'StringContains', $callback );
+ }
+
+ /**
+ * @since 2.3
+ *
+ * @param mixed $expected
+ * @param string $actual
+ */
+ public function assertThatStringNotContains( $expected, $actual, $message = '' ) {
+
+ $callback = function( &$expected, $actual, &$actualCounted ) {
+ foreach ( $expected as $key => $pattern ) {
+ if ( $this->isMatch( $pattern, $actual ) === false ) {
+ $actualCounted++;
+ unset( $expected[$key] );
+ }
+ }
+ };
+
+ $this->doAssertWith( $expected, $actual, $message, 'StringNotContains', $callback );
+ }
+
+ private function doAssertWith( $expected, $actual, $message = '', $method = '', $callback ) {
+
+ if ( !is_array( $expected ) ) {
+ $expected = [ $expected ];
+ }
+
+ $expected = array_filter( $expected, 'strlen' );
+
+ if ( $expected === [] ) {
+ return self::assertTrue( true, $message );
+ }
+
+ self::assertInternalType(
+ 'string',
+ $actual
+ );
+
+ $expectedToCount = count( $expected );
+ $actualCounted = 0;
+
+ call_user_func_array(
+ $callback,
+ [ &$expected, $actual, &$actualCounted ]
+ );
+
+ self::assertEquals(
+ $expectedToCount,
+ $actualCounted,
+ "Failed \"{$message}\" for $method:\n==== (actual) ====\n$actual\n==== (expected) ====\n" . $this->toString( $expected )
+ );
+ }
+
+ private function isMatch( $pattern, $source ) {
+
+ // use /.../ indicator to use the preg_match search match
+ if ( strlen( $pattern) >= 2 && substr( $pattern, 0, 1) === '/' && substr( $pattern, -1) === '/' ) {
+
+ return (bool) preg_match( $pattern, $source );
+
+ }
+
+ // use .* indicator to use the wildcard search match
+ if ( strpos( $pattern, '.*' ) !== false ) {
+
+ $pattern = preg_quote( $pattern, '/' );
+ $pattern = str_replace( '\.\*', '.*?', $pattern );
+
+ return (bool) preg_match( '/' . $pattern . '/', $source );
+
+ }
+
+ // use a simple strpos (as it is faster)
+ return strpos( $source, $pattern ) !== false;
+
+ }
+
+ private function toString( $expected ) {
+ return "[ " . ( is_array( $expected ) ? implode( " ], [ ", $expected ) : $expected ) . " ]\n";
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/TitleValidator.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/TitleValidator.php
new file mode 100644
index 00000000..16b70802
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/TitleValidator.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use Title;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class TitleValidator extends \PHPUnit_Framework_Assert {
+
+ /**
+ * @since 2.1
+ */
+ public function assertThatTitleIsNotKnown( $titles ) {
+ $this->assertTitleExists( false, $titles );
+ }
+
+ /**
+ * @since 2.1
+ */
+ public function assertThatTitleIsKnown( $titles ) {
+ $this->assertTitleExists( true, $titles );
+ }
+
+ private function assertTitleExists( $isExpected, $titles ) {
+
+ if ( !is_array( $titles ) ) {
+ $titles = [ $titles ];
+ }
+
+ foreach ( $titles as $title ) {
+
+ if ( !$title instanceof Title && is_string( $title ) ) {
+ $title = Title::newFromText( $title );
+ }
+
+ $this->assertEquals( $isExpected, $title->exists(), $title->getPrefixedText() );
+ }
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ValidatorFactory.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ValidatorFactory.php
new file mode 100644
index 00000000..223f07ce
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/Utils/Validators/ValidatorFactory.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace SMW\Tests\Utils\Validators;
+
+use SMW\Store;
+
+/**
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class ValidatorFactory {
+
+ /**
+ * @since 2.1
+ *
+ * @return StringValidator
+ */
+ public function newStringValidator() {
+ return new StringValidator();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return NumberValidator
+ */
+ public function newNumberValidator() {
+ return new NumberValidator();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return SemanticDataValidator
+ */
+ public function newSemanticDataValidator() {
+ return new SemanticDataValidator();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param Store $store
+ *
+ * @return IncomingSemanticDataValidator
+ */
+ public function newIncomingSemanticDataValidator( Store $store ) {
+ return new IncomingSemanticDataValidator( $store );
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return QueryResultValidator
+ */
+ public function newQueryResultValidator() {
+ return new QueryResultValidator();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return ExportDataValidator
+ */
+ public function newExportDataValidator() {
+ return new ExportDataValidator();
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return TitleValidator
+ */
+ public function newTitleValidator() {
+ return new TitleValidator();
+ }
+
+ /**
+ * @since 2.2
+ *
+ * @return QuerySegmentValidator
+ */
+ public function newQuerySegmentValidator() {
+ return new QuerySegmentValidator();
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return HtmlValidator
+ */
+ public function newHtmlValidator() {
+ return new HtmlValidator();
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/ContentParserTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/ContentParserTest.php
new file mode 100644
index 00000000..7a0661bf
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/ContentParserTest.php
@@ -0,0 +1,330 @@
+<?php
+
+namespace SMW\Test;
+
+use ContentHandler;
+use Parser;
+use Revision;
+use SMW\ContentParser;
+use SMW\Tests\Utils\Mock\MockTitle;
+use TextContent;
+use TextContentHandler;
+use Title;
+
+/**
+ * @covers \SMW\ContentParser
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class ContentParserTest extends SemanticMediaWikiTestCase {
+
+ public function getClass() {
+ return '\SMW\ContentParser';
+ }
+
+ private function newInstance( Title $title = null, $parser = null ) {
+
+ if ( $title === null ) {
+ $title = Title::newFromText( __METHOD__ );
+ }
+
+ return new ContentParser( $title, $parser );
+ }
+
+ public function testCanConstruct() {
+ $this->assertInstanceOf( $this->getClass(), $this->newInstance() );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testCanParseOnInstance() {
+ $this->assertInstanceOf( $this->getClass(), $this->newInstance()->parse() );
+ }
+
+ /**
+ * @depends testCanParseOnInstance
+ */
+ public function testRunParseOnText() {
+
+ $text = 'Foo-1-' . __METHOD__;
+ $expected = '<p>' . $text . "\n" . '</p>';
+
+ $this->assertParserOutput( $expected, $this->newInstance()->parse( $text ) );
+ }
+
+ /**
+ * @dataProvider titleRevisionDataProvider
+ */
+ public function testRunParseOnTitle( $setup, $expected, $withContentHandler = false ) {
+
+ $instance = $this->getMockBuilder( '\SMW\ContentParser' )
+ ->setConstructorArgs( [ $setup['title'], new Parser() ] )
+ ->setMethods( [ 'hasContentHandler' ] )
+ ->getMock();
+
+ $instance->expects( $this->any() )
+ ->method( 'hasContentHandler' )
+ ->will( $this->returnValue( $withContentHandler ) );
+
+ $instance->setRevision( $setup['revision'] );
+
+ $this->assertInstanceAfterParse( $expected, $instance->parse() );
+
+ }
+
+ /**
+ * @dataProvider contentDataProvider
+ *
+ * @since 1.9
+ */
+ public function testRunParseOnTitleWithContentHandler( $setup, $expected ) {
+
+ if ( !class_exists( 'ContentHandler') ) {
+ $this->markTestSkipped(
+ 'Skipping test due to missing class (probably MW 1.21 or lower).'
+ );
+ }
+
+ $this->testRunParseOnTitle( $setup, $expected, true );
+ }
+
+ protected function assertInstanceAfterParse( $expected, $instance ) {
+
+ $this->assertInstanceOf( $this->getClass(), $instance );
+
+ if ( $expected['error'] ) {
+ return $this->assertError( $instance );
+ }
+
+ $this->assertParserOutput( $expected['text'], $instance );
+ }
+
+ protected function assertError( $instance ) {
+ $this->assertInternalType( 'array', $instance->getErrors() );
+ $this->assertNotEmpty( $instance->getErrors() );
+ }
+
+ protected function assertParserOutput( $text, $instance ) {
+
+ $this->assertInstanceOf( 'ParserOutput', $instance->getOutput() );
+
+ if ( $text !== '' ) {
+
+ return $this->assertContains(
+ $text,
+ $instance->getOutput()->getText(),
+ 'Asserts that getText() returns expected text component'
+ );
+
+ }
+
+ $this->assertEmpty( $instance->getOutput()->getText() );
+ }
+
+ /**
+ * @return array
+ */
+ public function titleRevisionDataProvider() {
+
+ $provider = [];
+
+ $text = 'Foo-2-' . __METHOD__;
+ $expected ='<p>' . $text . "\n" . '</p>';
+
+ // #0 Title does not exists
+ $title = $this->newMockBuilder()->newObject( 'Title', [
+ 'getDBkey' => 'Lila',
+ 'exists' => false,
+ 'getText' => null,
+ 'getPageLanguage' => $this->getLanguage()
+ ] );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => null,
+ ],
+ [
+ 'error' => true,
+ 'text' => ''
+ ]
+ ];
+
+ // #1 Valid revision
+ // Required by MW 1.29, method got removed
+ if ( method_exists( 'Revision', 'getText' ) ) {
+ $title = $this->newMockBuilder()->newObject( 'Title', [
+ 'getDBkey' => 'Lula',
+ 'exists' => true,
+ 'getPageLanguage' => $this->getLanguage()
+ ] );
+
+ $revision = $this->newMockBuilder()->newObject( 'Revision', [
+ 'getId' => 9001,
+ 'getUser' => 'Lala',
+ 'getText' => $text,
+ ] );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => $revision,
+ ],
+ [
+ 'error' => false,
+ 'text' => $expected
+ ]
+ ];
+ }
+
+ // #2 Null revision
+ $title = $this->newMockBuilder()->newObject( 'Title', [
+ 'getDBkey' => 'Lula',
+ 'exists' => true,
+ 'getPageLanguage' => $this->getLanguage()
+ ] );
+
+ $revision = null;
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => $revision,
+ ],
+ [
+ 'error' => true,
+ 'text' => ''
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function contentDataProvider() {
+
+ $provider = [];
+
+ if ( !class_exists( 'ContentHandler') ) {
+ $provider[] = [ [], [] ];
+ return $provider;
+ }
+
+ $text = 'Foo-3-' . __METHOD__;
+
+ // #0 Title does not exists
+ $title = MockTitle::buildMock( 'Lila' );
+
+ $title->expects( $this->any() )
+ ->method( 'exists' )
+ ->will( $this->returnValue( false ) );
+
+ $title->expects( $this->any() )
+ ->method( 'getPageLanguage' )
+ ->will( $this->returnValue( $this->getLanguage() ) );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => null,
+ ],
+ [
+ 'error' => true,
+ 'text' => ''
+ ]
+ ];
+
+ // #1 Valid revision
+ $title = $this->newMockBuilder()->newObject( 'Title', [
+ 'getDBkey' => 'Lula',
+ 'exists' => true,
+ 'getPageLanguage' => $this->getLanguage()
+ ] );
+
+ $revision = $this->newMockBuilder()->newObject( 'Revision', [
+ 'getId' => 9001,
+ 'getUser' => 'Lala',
+ 'getContent' => new TextContent( $text )
+ ] );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => $revision,
+ ],
+ [
+ 'error' => false,
+ 'text' => $text
+ ]
+ ];
+
+ // #1 Empty content
+ $title = $this->newMockBuilder()->newObject( 'Title', [
+ 'getDBkey' => 'Lula',
+ 'exists' => true,
+ 'getPageLanguage' => $this->getLanguage(),
+ 'getContentModel' => CONTENT_MODEL_WIKITEXT
+ ] );
+
+ $revision = $this->newMockBuilder()->newObject( 'Revision', [
+ 'getId' => 9001,
+ 'getUser' => 'Lala',
+ 'getContent' => false,
+ 'getContentHandler' => new TextContentHandler()
+ ] );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => $revision,
+ ],
+ [
+ 'error' => false,
+ 'text' => ''
+ ]
+ ];
+
+ // #2 "real" revision and content
+ $title = $this->newTitle();
+ $content = ContentHandler::makeContent( $text, $title, CONTENT_MODEL_WIKITEXT, null );
+
+ $revision = new Revision(
+ [
+ 'id' => 42,
+ 'page' => 23,
+ 'title' => $title,
+
+ 'content' => $content,
+ 'length' => $content->getSize(),
+ 'comment' => "testing",
+ 'minor_edit' => false,
+
+ 'content_format' => null,
+ ]
+ );
+
+ $provider[] = [
+ [
+ 'title' => $title,
+ 'revision' => $revision,
+ ],
+ [
+ 'error' => false,
+ 'text' => $text
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/RecordValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/RecordValueTest.php
new file mode 100644
index 00000000..9635e6e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/RecordValueTest.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\Tests\TestEnvironment;
+use SMWRecordValue as RecordValue;
+
+/**
+ * @covers \SMWRecordValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class RecordValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWRecordValue',
+ new RecordValue()
+ );
+ }
+
+ public function testGetPropertyDataItems() {
+
+ $expected = [
+ $this->dataItemFactory->newDIProperty( 'Bar' ),
+ $this->dataItemFactory->newDIProperty( 'Foobar' )
+ ];
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new RecordValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getPropertyDataItems()
+ );
+
+ $this->assertEquals(
+ $this->dataItemFactory->newDIProperty( 'Foobar' ),
+ $instance->getPropertyDataItemByIndex( 'Foobar' )
+ );
+ }
+
+ public function testParseValue() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new RecordValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '123;abc' );
+ $container = $instance->getDataItem();
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $container
+ );
+
+ $semanticData = $container->getSemanticData();
+
+ $this->assertTrue(
+ $semanticData->hasProperty( $this->dataItemFactory->newDIProperty( 'Foobar' ) )
+ );
+ }
+
+ public function testParseValueOnMissingValues() {
+
+ $instance = new RecordValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( '' );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $instance->getDataItem()
+ );
+ }
+
+ public function testParseValueWithErroredDv() {
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getRedirectTarget' ] )
+ ->getMockForAbstractClass();
+
+ $this->propertySpecificationLookup->expects( $this->atLeastOnce() )
+ ->method( 'getFieldListBy' )
+ ->will( $this->returnValue( $this->dataItemFactory->newDIBlob( 'Bar;Foobar' ) ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $instance = new RecordValue();
+ $instance->setProperty(
+ $this->dataItemFactory->newDIProperty( 'Foo' )
+ );
+
+ $instance->setUserValue( 'Foo;<>Foo' );
+
+ $this->assertInstanceOf(
+ '\SMWDIError',
+ $instance->getDataItem()
+ );
+
+ $this->assertContains(
+ "smw-datavalue-wikipage-property-invalid-title",
+ implode( ' ', $instance->getErrors() )
+ );
+ }
+
+ public function testGetValuesFromStringWithEncodedSemicolon() {
+
+ $instance = new RecordValue();
+
+ $this->assertEquals(
+ [ 'abc', '1;2', 3 ],
+ $instance->getValuesFromString( 'abc;1\;2;3' )
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testGetQueryDescription( $properties, $value, $expected ) {
+
+ $instance = new RecordValue( '_rec' );
+ $instance->setFieldProperties( $properties );
+
+ $description = $instance->getQueryDescription( htmlspecialchars( $value ) );
+
+ $this->assertEquals(
+ $expected['description'],
+ $description->getQueryString()
+ );
+ }
+
+ /**
+ * @dataProvider valueProvider
+ */
+ public function testGetWikiValue( $properties, $value, $expected ) {
+
+ $instance = new RecordValue( '_rec' );
+ $instance->setFieldProperties( $properties );
+
+ $instance->setUserValue( $value );
+
+ $this->assertEquals(
+ $expected['wikivalue'],
+ $instance->getWikiValue()
+ );
+ }
+
+ public function valueProvider() {
+
+ $dataItemFactory = new DataItemFactory();
+
+ $properties = [
+ $dataItemFactory->newDIProperty( 'Foo' ),
+ $dataItemFactory->newDIProperty( 'Bar' ),
+ 'InvalidFieldPropertyNotSet'
+ ];
+
+ $provider[] = [
+ $properties,
+ "Title without special characters;2001",
+ [
+ 'description' => "[[Foo::Title without special characters]] [[Bar::2001]]",
+ 'wikivalue' => "Title without special characters; 2001"
+ ]
+
+ ];
+
+ $provider[] = [
+ $properties,
+ "Title with $&%'* special characters;(..&^%..)",
+ [
+ 'description' => "[[Foo::Title with $&%'* special characters]] [[Bar::(..&^%..)]]",
+ 'wikivalue' => "Title with $&%'* special characters; (..&^%..)"
+ ]
+ ];
+
+ $provider[] = [
+ $properties,
+ " Title with space before ; After the divider ",
+ [
+ 'description' => "[[Foo::Title with space before]] [[Bar::After the divider]]",
+ 'wikivalue' => "Title with space before; After the divider"
+ ]
+ ];
+
+ $provider[] = [
+ $properties,
+ " Title with backslash\; escape ; After the divider ",
+ [
+ 'description' => "[[Foo::Title with backslash; escape]] [[Bar::After the divider]]",
+ 'wikivalue' => "Title with backslash\; escape; After the divider"
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/UriValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/UriValueTest.php
new file mode 100644
index 00000000..90e12a6d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/UriValueTest.php
@@ -0,0 +1,397 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMWURIValue as UriValue;
+
+/**
+ * @covers \SMWURIValue
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class UriValueTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWURIValue',
+ new UriValue( '_uri' )
+ );
+ }
+
+ /**
+ * @dataProvider uriProvider
+ */
+ public function testUriOutputFormatting( $uri, $caption = false, $linker = null, $expected ) {
+
+ $instance = new UriValue( '_uri' );
+ $instance->setUserValue( $uri, $caption );
+
+ $this->assertOutputFormatting(
+ $instance,
+ $linker,
+ $expected
+ );
+ }
+
+ /**
+ * @dataProvider uriProvider
+ */
+ public function testAnuOutputFormatting( $uri, $caption = false, $linker = null, $expected ) {
+
+ $instance = new UriValue( '_anu' );
+ $instance->setUserValue( $uri, $caption );
+
+ $this->assertOutputFormatting(
+ $instance,
+ $linker,
+ $expected
+ );
+ }
+
+ /**
+ * @dataProvider telProvider
+ */
+ public function testTelOutputFormatting( $uri, $caption = false, $linker = null, $expected ) {
+
+ $instance = new UriValue( '_tel' );
+ $instance->setUserValue( $uri, $caption );
+
+ $this->assertOutputFormatting(
+ $instance,
+ $linker,
+ $expected
+ );
+ }
+
+ /**
+ * @dataProvider emaProvider
+ */
+ public function testEmaOutputFormatting( $uri, $caption = false, $linker = null, $expected ) {
+
+ $instance = new UriValue( '_ema' );
+ $instance->setUserValue( $uri, $caption );
+
+ $this->assertOutputFormatting(
+ $instance,
+ $linker,
+ $expected
+ );
+ }
+
+ private function assertOutputFormatting( $instance, $linker, $expected ) {
+
+ $this->assertEquals(
+ $expected['wikiValue'],
+ $instance->getWikiValue(),
+ 'Failed asserting wikiValue'
+ );
+
+ $this->assertEquals(
+ $expected['longHTMLText'],
+ $instance->getLongHTMLText( $linker ),
+ 'Failed asserting longHTMLText'
+ );
+
+ $this->assertEquals(
+ $expected['longWikiText'],
+ $instance->getLongWikiText( $linker ),
+ 'Failed asserting longWikiText'
+ );
+
+ $this->assertEquals(
+ $expected['shortHTMLText'],
+ $instance->getShortHTMLText( $linker ),
+ 'Failed asserting shortHTMLText'
+ );
+
+ $this->assertEquals(
+ $expected['shortWikiText'],
+ $instance->getShortWikiText( $linker ),
+ 'Failed asserting shortWikiText'
+ );
+ }
+
+ public function uriProvider() {
+
+ $linker = smwfGetLinker();
+
+ // FIXME MW 1.19*
+ $noFollowAttribute = version_compare( $GLOBALS['wgVersion'], '1.20', '<' ) ? '' : ' rel="nofollow"';
+
+ // https://github.com/lanthaler/IRI/blob/master/Test/IriTest.php
+ $provider[] = [
+ 'http://example.org/aaa/bbb#ccc',
+ false,
+ null,
+ [
+ 'wikiValue' => 'http://example.org/aaa/bbb#ccc',
+ 'longHTMLText' => 'http://example.org/aaa/bbb#ccc',
+ 'longWikiText' => 'http://example.org/aaa/bbb#ccc',
+ 'shortHTMLText' => 'http://example.org/aaa/bbb#ccc',
+ 'shortWikiText' => 'http://example.org/aaa/bbb#ccc'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/aaa/bbb#ccc',
+ 'Foo',
+ null,
+ [
+ 'wikiValue' => 'http://example.org/aaa/bbb#ccc',
+ 'longHTMLText' => 'http://example.org/aaa/bbb#ccc',
+ 'longWikiText' => 'http://example.org/aaa/bbb#ccc',
+ 'shortHTMLText' => 'Foo',
+ 'shortWikiText' => 'Foo'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/aaa/bbb#ccc',
+ false,
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/aaa/bbb#ccc',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa/bbb#ccc</a>',
+ 'longWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa/bbb#ccc]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa/bbb#ccc</a>',
+ 'shortWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa/bbb#ccc]'
+ ]
+ ];
+
+ #3
+ $provider[] = [
+ 'http://example.org/aaa/bbb#ccc',
+ 'Foo',
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/aaa/bbb#ccc',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa/bbb#ccc</a>',
+ 'longWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa/bbb#ccc]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">Foo</a>',
+ 'shortWikiText' => '[http://example.org/aaa/bbb#ccc Foo]',
+ ]
+ ];
+
+ #4
+ $provider[] = [
+ 'http://example.org/aaa%2Fbbb#ccc',
+ false,
+ null,
+ [
+ 'wikiValue' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longHTMLText' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longWikiText' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'shortHTMLText' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'shortWikiText' => 'http://example.org/aaa%2Fbbb#ccc'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/aaa%2Fbbb#ccc',
+ 'Foo',
+ null,
+ [
+ 'wikiValue' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longHTMLText' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longWikiText' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'shortHTMLText' => 'Foo',
+ 'shortWikiText' => 'Foo'
+ ]
+ ];
+
+ #6
+ $provider[] = [
+ 'http://example.org/aaa%2Fbbb#ccc',
+ false,
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa%2Fbbb#ccc</a>',
+ 'longWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa%2Fbbb#ccc]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa%2Fbbb#ccc</a>',
+ 'shortWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa%2Fbbb#ccc]'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/aaa%2Fbbb#ccc',
+ 'Foo',
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/aaa%2Fbbb#ccc',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">http://example.org/aaa%2Fbbb#ccc</a>',
+ 'longWikiText' => '[http://example.org/aaa/bbb#ccc http://example.org/aaa%2Fbbb#ccc]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/aaa/bbb#ccc">Foo</a>',
+ 'shortWikiText' => '[http://example.org/aaa/bbb#ccc Foo]',
+ ]
+ ];
+
+ #8 UTF-8 encoded string
+ $provider[] = [
+ 'http://example.org/よã†ã“ã--23-7B-7D',
+ false,
+ null,
+ [
+ 'wikiValue' => 'http://example.org/よã†ã“ã--23-7B-7D',
+ 'longHTMLText' => 'http://example.org/よã†ã“ã--23-7B-7D',
+ 'longWikiText' => 'http://example.org/よã†ã“ã--23-7B-7D',
+ 'shortHTMLText' => 'http://example.org/よã†ã“ã--23-7B-7D',
+ 'shortWikiText' => 'http://example.org/よã†ã“ã--23-7B-7D'
+ ]
+ ];
+
+ #9
+ $provider[] = [
+ 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ false,
+ null,
+ [
+ 'wikiValue' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longHTMLText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longWikiText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'shortHTMLText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'shortWikiText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ '一二三',
+ null,
+ [
+ 'wikiValue' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longHTMLText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longWikiText' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'shortHTMLText' => '一二三',
+ 'shortWikiText' => '一二三'
+ ]
+ ];
+
+ # 11
+ $provider[] = [
+ 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ false,
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D">http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D</a>',
+ 'longWikiText' => '[http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D">http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D</a>',
+ 'shortWikiText' => '[http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D]'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ '一二三',
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D',
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D">http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D</a>',
+ 'longWikiText' => '[http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D">一二三</a>',
+ 'shortWikiText' => '[http://example.org/%E3%82%88%E3%81%86%E3%81%93%E3%81%9D-23-7B-7D 一二三]',
+ ]
+ ];
+
+ # 13
+ $provider[] = [
+ 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ false,
+ null,
+ [
+ 'wikiValue' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'longHTMLText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'longWikiText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'shortHTMLText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'shortWikiText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ '&!_:;@*#Foo',
+ null,
+ [
+ 'wikiValue' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'longHTMLText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'longWikiText' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ 'shortHTMLText' => '&!_:;@*#Foo',
+ 'shortWikiText' => '&!_:;@*#Foo'
+ ]
+ ];
+
+ #15
+ $provider[] = [
+ 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ false,
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar', // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/api?query=%21_:%3B@%2A_#Foo&amp;=_-3DBar">http://example.org/api?query=! :;@* #Foo&amp;=%20-3DBar</a>', // @codingStandardsIgnoreEnd
+ 'longWikiText' => '[http://example.org/api?query=%21_:%3B@%2A_#Foo&=_-3DBar http://example.org/api?query=! :;@* #Foo&=%20-3DBar]', // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/api?query=%21_:%3B@%2A_#Foo&amp;=_-3DBar">http://example.org/api?query=! :;@* #Foo&amp;=%20-3DBar</a>', // @codingStandardsIgnoreEnd
+ 'shortWikiText' => '[http://example.org/api?query=%21_:%3B@%2A_#Foo&=_-3DBar http://example.org/api?query=! :;@* #Foo&=%20-3DBar]'
+ ]
+ ];
+
+ $provider[] = [
+ 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar',
+ '&!_:;@* #Foo',
+ $linker,
+ [
+ 'wikiValue' => 'http://example.org/api?query=!_:;@* #Foo&=%20-3DBar', // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ 'longHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/api?query=%21_:%3B@%2A_#Foo&amp;=_-3DBar">http://example.org/api?query=! :;@* #Foo&amp;=%20-3DBar</a>', // @codingStandardsIgnoreEnd
+ 'longWikiText' => '[http://example.org/api?query=%21_:%3B@%2A_#Foo&=_-3DBar http://example.org/api?query=! :;@* #Foo&=%20-3DBar]',
+ 'shortHTMLText' => '<a class="external"' . $noFollowAttribute . ' href="http://example.org/api?query=%21_:%3B@%2A_#Foo&amp;=_-3DBar">&amp;! :;@* #Foo</a>',
+ 'shortWikiText' => '[http://example.org/api?query=%21_:%3B@%2A_#Foo&=_-3DBar &! :;@* #Foo]'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function telProvider() {
+
+ $provider[] = [
+ '+1-201-555-0123',
+ false,
+ null,
+ [
+ 'wikiValue' => '+1-201-555-0123',
+ 'longHTMLText' => '+1-201-555-0123',
+ 'longWikiText' => '+1-201-555-0123',
+ 'shortHTMLText' => '+1-201-555-0123',
+ 'shortWikiText' => '+1-201-555-0123'
+ ]
+ ];
+
+ return $provider;
+ }
+
+ public function emaProvider() {
+
+ $provider[] = [
+ 'foo@example.org',
+ false,
+ null,
+ [
+ 'wikiValue' => 'foo@example.org',
+ 'longHTMLText' => 'foo@example.org',
+ 'longWikiText' => 'foo@example.org',
+ 'shortHTMLText' => 'foo@example.org',
+ 'shortWikiText' => 'foo@example.org'
+ ]
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/WikiPageValueTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/WikiPageValueTest.php
new file mode 100644
index 00000000..953c2a23
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/DataValues/WikiPageValueTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests\DataValues;
+
+use SMW\DataItemFactory;
+use SMW\Tests\TestEnvironment;
+use SMWWikiPageValue as WikiPageValue;
+
+/**
+ * @covers \SMWWikiPageValue
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 3.1
+ *
+ * @author mwjames
+ */
+class WikiPageValueTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->dataItemFactory = new DataItemFactory();
+
+ $this->propertySpecificationLookup = $this->getMockBuilder( '\SMW\PropertySpecificationLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'PropertySpecificationLookup', $this->propertySpecificationLookup );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ WikiPageValue::class,
+ new WikiPageValue( '' )
+ );
+ }
+
+ public function testDisableInfolinksOnSpecialUsernamePrefix() {
+
+ $instance = new WikiPageValue( '_wpg' );
+
+ $this->assertFalse(
+ $instance->getOption( WikiPageValue::OPT_DISABLE_INFOLINKS )
+ );
+
+ $instance->setDataItem(
+ $this->dataItemFactory->newDIWikiPage( '>Foo', NS_USER )
+ );
+
+ $instance->getTitle();
+
+ $this->assertTrue(
+ $instance->getOption( WikiPageValue::OPT_DISABLE_INFOLINKS )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/HighlighterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/HighlighterTest.php
new file mode 100644
index 00000000..4bc78070
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/HighlighterTest.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\Highlighter;
+
+/**
+ * @covers \SMW\Highlighter
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class HighlighterTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider getTypeDataProvider
+ */
+ public function testCanConstruct( $type ) {
+
+ $this->assertInstanceOf(
+ '\SMW\Highlighter',
+ Highlighter::factory( $type )
+ );
+ }
+
+ /**
+ * @dataProvider getTypeDataProvider
+ */
+ public function testGetTypeId( $type, $expected ) {
+ $results = Highlighter::getTypeId( $type );
+
+ $this->assertInternalType(
+ 'integer',
+ $results
+ );
+
+ $this->assertEquals(
+ $expected,
+ $results
+ );
+ }
+
+ public function testDecode() {
+
+ $this->assertEquals(
+ '&<> ',
+ Highlighter::decode( '&amp;&lt;&gt;&#160;<nowiki></nowiki>' )
+ );
+ }
+
+ /**
+ * @dataProvider getTypeDataProvider
+ */
+ public function testGetHtml( $type ) {
+
+ $instance = Highlighter::factory( $type );
+
+ $instance->setContent( [
+ 'title' => 'Foo'
+ ] );
+
+ // Check without caption/content set
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+
+ $instance->setContent( [
+ 'caption' => '123',
+ 'content' => 'ABC',
+ ] );
+
+ // Check with caption/content set
+ $this->assertInternalType(
+ 'string',
+ $instance->getHtml()
+ );
+ }
+
+ public function testHasHighlighterClass() {
+
+ $instance = Highlighter::factory(
+ Highlighter::TYPE_WARNING
+ );
+
+ $instance->setContent( [
+ 'title' => 'Foo'
+ ] );
+
+ $this->assertTrue(
+ Highlighter::hasHighlighterClass( $instance->getHtml(), 'warning' )
+ );
+ }
+
+ public function getTypeDataProvider() {
+ return [
+ [ '' , Highlighter::TYPE_NOTYPE ],
+ [ 'property', Highlighter::TYPE_PROPERTY ],
+ [ 'text', Highlighter::TYPE_TEXT ],
+ [ 'info', Highlighter::TYPE_INFO ],
+ [ 'help', Highlighter::TYPE_HELP ],
+ [ 'service', Highlighter::TYPE_SERVICE ],
+ [ 'quantity', Highlighter::TYPE_QUANTITY ],
+ [ 'note', Highlighter::TYPE_NOTE ],
+ [ 'warning', Highlighter::TYPE_WARNING ],
+ [ 'error', Highlighter::TYPE_ERROR ],
+ [ 'PrOpErTy', Highlighter::TYPE_PROPERTY ],
+ [ 'ãƒã‚«ãªãƒ†ã‚¹ãƒˆ', Highlighter::TYPE_NOTYPE ],
+ [ '<span>Something that should not work</span>', Highlighter::TYPE_NOTYPE ],
+ [ Highlighter::TYPE_PROPERTY, Highlighter::TYPE_NOTYPE ]
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/InfolinkTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/InfolinkTest.php
new file mode 100644
index 00000000..6a47317a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/InfolinkTest.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\Tests\TestEnvironment;
+use SMWInfolink as Infolink;
+
+/**
+ * @covers \SMWInfolink
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class InfolinkTest extends \PHPUnit_Framework_TestCase {
+
+ private $testEnvironment;
+
+ protected function setUp() {
+
+ $this->testEnvironment = new TestEnvironment(
+ [
+ 'wgContLang' => \Language::factory( 'en' )
+ ]
+ );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testEncodeParameters_ForTitle( array $params, array $expected ) {
+
+ $encodeResult = Infolink::encodeParameters( $params, true );
+
+ $this->assertEquals(
+ $expected[0],
+ $encodeResult
+ );
+ }
+
+ /**
+ * @dataProvider parameterDataProvider
+ */
+ public function testEncodeParameters_NotForTitle( array $params, array $expected ) {
+
+ $encodeResult = Infolink::encodeParameters( $params, false );
+
+ $this->assertEquals(
+ $expected[1],
+ $encodeResult
+ );
+ }
+
+ public function testNewPropertySearchLink_GetText() {
+
+ $instance = Infolink::newPropertySearchLink( 'Foo', 'Bar', 'Foobar' );
+
+ $instance->setCompactLink( false );
+
+ $this->assertContains(
+ 'title=Special:SearchByProperty&x=%3ABar%2FFoobar',
+ $instance->getText( SMW_OUTPUT_RAW )
+ );
+
+ $instance->setCompactLink( true );
+
+ $this->assertContains(
+ 'title=Special:SearchByProperty&cl=OkJhci9Gb29iYXI',
+ $instance->getText( SMW_OUTPUT_RAW )
+ );
+
+ $this->assertEquals(
+ $instance->getURL(),
+ $instance->getText( SMW_OUTPUT_RAW )
+ );
+ }
+
+ public function testGetURL() {
+
+ $instance = new Infolink( true, 'Foo', 'Bar/Foobar' );
+
+ $instance->setCompactLink( true );
+
+ $this->assertContains(
+ '/Bar/Foobar',
+ $instance->getLocalURL()
+ );
+
+ $this->assertContains(
+ '/Bar/Foobar',
+ $instance->getURL()
+ );
+
+ $instance->setParameter( 123, 'foo' );
+
+ $this->assertContains(
+ 'title=Bar/Foobar&cl=Zm9vPTEyMw',
+ $instance->getLocalURL()
+ );
+
+ $this->assertContains(
+ 'title=Bar/Foobar&cl=Zm9vPTEyMw',
+ $instance->getURL()
+ );
+
+ $instance->setCompactLink( false );
+
+ $this->assertContains(
+ 'title=Bar/Foobar&foo=123',
+ $instance->getLocalURL()
+ );
+
+ $this->assertContains(
+ 'title=Bar/Foobar&foo=123',
+ $instance->getURL()
+ );
+ }
+
+ /**
+ * @dataProvider base64Provider
+ */
+ public function testEncodeBase64( $source, $target ) {
+
+ $this->assertContains(
+ $target,
+ Infolink::encodeCompactLink( $source )
+ );
+ }
+
+ /**
+ * @dataProvider base64Provider
+ */
+ public function testEncodeDecodeBase64RoundTrip( $source, $target ) {
+
+ $this->assertEquals(
+ $source,
+ Infolink::decodeCompactLink( Infolink::encodeCompactLink( $source ) )
+ );
+ }
+
+ /**
+ * @dataProvider base64DecodeProvider
+ */
+ public function testDecodeBase64( $source, $target ) {
+
+ $this->assertEquals(
+ $source,
+ Infolink::decodeCompactLink( $target )
+ );
+ }
+
+ public function testNotDecodable() {
+
+ $this->assertNotContains(
+ '%3ABar/Foobar',
+ Infolink::decodeCompactLink( 'eD0lM0FCYXIlMkZGb29iYXI' )
+ );
+ }
+
+ public function base64Provider() {
+
+ yield [
+ '%3ABar/Foobar',
+ 'cl:JTNBQmFyL0Zvb2Jhcg'
+ ];
+
+ yield [
+ '-5B-5BProperty%3A%2B-5D-5D-20-5B-5BCategory%3ALorem-20ipsum-5D-5D/-3FHas-20description%3DDescription/-3FHas-20type/mainlabel=/format=table/class=datatable/sort=/order=asc/offset=100/limit=50',
+ 'cl:YzpFijEOgzAMRU_DGCUFMXooRVUHhl7BgKkiJTiy3YHbF8qA9Jf33ndt59ruLVxIbKuae1Xvoj9WB_ePDzT6sBxxYKG8h1j0m8-bd83zhbrLmXSSWCzyWjV9f9F1sa2QzxjXhCMl8AtLRgPDMZGfEqrCjIYnK4uBZ5lJAHXyvCxKBrcQfIo5GrThBw'
+ ];
+ }
+
+ public function base64DecodeProvider() {
+
+ yield [
+ '%3ABar/Foobar',
+ 'cl:JTNBQmFyL0Zvb2Jhcg'
+ ];
+
+ yield [
+ '%3ABar/Foobar',
+ '%3ABar/Foobar'
+ ];
+
+ yield [
+ '-5B-5BProperty%3A%2B-5D-5D-20-5B-5BCategory%3ALorem-20ipsum-5D-5D/-3FHas-20description%3DDescription/-3FHas-20type/mainlabel=/format=table/class=datatable/sort=/order=asc/offset=100/limit=50',
+ 'cl:YzpNijEOgzAMRU-TbJVSEKOHtgh16NArGDBVpARHtiuV2zeIBekv773_g0t3r3sLFxLbXHtzTRX9viYc8YFGH5Y9vlgo1xCLfvNxc81waYcnatUz6SSxWOTVtX1_otPJtkI-Y1wTjpTALywZDQzHRH5KqAozGh6sLAaeZSYB1MnzsigZXEPwKeZo0IU'
+ ];
+
+ yield [
+ '-5B-5BProperty%3A%2B-5D-5D-20-5B-5BCategory%3ALorem-20ipsum-5D-5D/-3FHas-20description%3DDescription/-3FHas-20type/mainlabel=/format=table/class=datatable/sort=/order=asc/offset=100/limit=50',
+ 'cl:YzpFijEOgzAMRU_DGCUFMXooRVUHhl7BgKkiJTiy3YHbF8qA9Jf33ndt59ruLVxIbKuae1Xvoj9WB_ePDzT6sBxxYKG8h1j0m8-bd83zhbrLmXSSWCzyWjV9f9F1sa2QzxjXhCMl8AtLRgPDMZGfEqrCjIYnK4uBZ5lJAHXyvCxKBrcQfIo5GrThBw'
+ ];
+ }
+
+ public function parameterDataProvider() {
+ return [
+ [
+ // #0
+ [
+ 'format=template',
+ 'link=none'
+ ],
+ [
+ 'format=template/link=none',
+ 'x=format%3Dtemplate%2Flink%3Dnone'
+ ]
+ ],
+
+ // #1 Bug 47010 (space encoding, named args => named%20args)
+ [
+ [
+ 'format=template',
+ 'link=none',
+ 'named args=1'
+ ],
+ [
+ 'format=template/link=none/named-20args=1',
+ 'x=format%3Dtemplate%2Flink%3Dnone%2Fnamed-20args%3D1'
+ ]
+ ],
+
+ // #2 "\"
+ [
+ [
+ "format=foo\bar",
+ ],
+ [
+ 'format=foo-5Cbar',
+ 'x=format%3Dfoo-5Cbar'
+ ]
+ ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryPrinterFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryPrinterFactoryTest.php
new file mode 100644
index 00000000..ea3d0609
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryPrinterFactoryTest.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\QueryPrinterFactory;
+use SMW\QueryResultPrinter;
+use SMW\TableResultPrinter;
+use SMWListResultPrinter;
+
+/**
+ * @covers \SMW\QueryPrinterFactory
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class QueryPrinterFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testSingleton() {
+ $instance = QueryPrinterFactory::singleton();
+
+ $this->assertInstanceOf( QueryPrinterFactory::class, $instance );
+ $this->assertTrue( QueryPrinterFactory::singleton() === $instance );
+
+ global $smwgResultFormats, $smwgResultAliases;
+
+ foreach ( $smwgResultFormats as $formatName => $printerClass ) {
+ $this->assertTrue( $instance->hasFormat( $formatName ) );
+ $this->assertInstanceOf( $printerClass, $instance->getPrinter( $formatName ) );
+ }
+
+ foreach ( $smwgResultAliases as $formatName => $aliases ) {
+ $printerClass = $smwgResultFormats[$formatName];
+
+ foreach ( $aliases as $alias ) {
+ $this->assertTrue( $instance->hasFormat( $alias ) );
+ $this->assertInstanceOf( $printerClass, $instance->getPrinter( $formatName ) );
+ }
+ }
+ }
+
+ public function testRegisterFormat() {
+ $factory = new QueryPrinterFactory();
+
+ $factory->registerFormat( 'table', TableResultPrinter::class );
+ $factory->registerFormat( 'list', SMWListResultPrinter::class );
+
+ $this->assertContains( 'table', $factory->getFormats() );
+ $this->assertContains( 'list', $factory->getFormats() );
+ $this->assertCount( 2, $factory->getFormats() );
+
+ $factory->registerFormat( 'table', SMWListResultPrinter::class );
+
+ $printer = $factory->getPrinter( 'table' );
+
+ $this->assertInstanceOf( SMWListResultPrinter::class, $printer );
+ }
+
+ public function testRegisterAliases() {
+ $factory = new QueryPrinterFactory();
+
+ $this->assertEquals( 'foo', $factory->getCanonicalName( 'foo' ) );
+
+ $factory->registerAliases( 'foo', [] );
+ $factory->registerAliases( 'foo', [ 'bar' ] );
+ $factory->registerAliases( 'foo', [ 'baz' ] );
+ $factory->registerAliases( 'ohi', [ 'there', 'o_O' ] );
+
+ $this->assertEquals( 'foo', $factory->getCanonicalName( 'foo' ) );
+
+ $this->assertEquals( 'foo', $factory->getCanonicalName( 'bar' ) );
+ $this->assertEquals( 'foo', $factory->getCanonicalName( 'baz' ) );
+
+ $this->assertEquals( 'ohi', $factory->getCanonicalName( 'there' ) );
+ $this->assertEquals( 'ohi', $factory->getCanonicalName( 'o_O' ) );
+
+ $factory->registerAliases( 'foo', [ 'o_O' ] );
+
+ $this->assertEquals( 'foo', $factory->getCanonicalName( 'o_O' ) );
+ }
+
+ public function testGetPrinter() {
+ $factory = QueryPrinterFactory::singleton();
+
+ foreach ( $factory->getFormats() as $format ) {
+ $printer = $factory->getPrinter( $format );
+ $this->assertInstanceOf( QueryResultPrinter::class, $printer );
+ }
+
+ // In case there are no formats PHPUnit would otherwise complain here.
+ $this->assertTrue( true );
+ }
+
+ public function testGetFormats() {
+ $factory = new QueryPrinterFactory();
+
+ $this->assertInternalType( 'array', $factory->getFormats() );
+
+ $factory->registerFormat( 'table', TableResultPrinter::class );
+ $factory->registerFormat( 'list', SMWListResultPrinter::class );
+
+ $factory->registerAliases( 'foo', [ 'bar' ] );
+ $factory->registerAliases( 'foo', [ 'baz' ] );
+ $factory->registerAliases( 'ohi', [ 'there', 'o_O' ] );
+
+ $formats = $factory->getFormats();
+ $this->assertInternalType( 'array', $formats );
+
+ $this->assertContains( 'table', $factory->getFormats() );
+ $this->assertContains( 'list', $factory->getFormats() );
+ $this->assertCount( 2, $factory->getFormats() );
+ }
+
+ public function testHasFormat() {
+ $factory = new QueryPrinterFactory();
+
+ $this->assertFalse( $factory->hasFormat( 'ohi' ) );
+
+ $factory->registerFormat( 'ohi', 'SMWTablePrinter' );
+ $factory->registerAliases( 'ohi', [ 'there', 'o_O' ] );
+
+ $this->assertTrue( $factory->hasFormat( 'ohi' ) );
+ $this->assertTrue( $factory->hasFormat( 'there' ) );
+ $this->assertTrue( $factory->hasFormat( 'o_O' ) );
+
+ $factory = QueryPrinterFactory::singleton();
+
+ foreach ( $factory->getFormats() as $format ) {
+ $this->assertTrue( $factory->hasFormat( $format ) );
+ }
+ }
+
+ public function testGetPrinterThrowsException() {
+
+ $factory = new QueryPrinterFactory();
+
+ $this->setExpectedException( '\SMW\Query\Exception\ResultFormatNotFoundException' );
+ $factory->getPrinter( 'lula' );
+ }
+
+ public function testGetCanonicalNameThrowsException() {
+
+ $factory = new QueryPrinterFactory();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $factory->getCanonicalName( 9001 );
+ }
+
+ /**
+ * @dataProvider registerFormatExceptioProvider
+ */
+ public function testRegisterFormatThrowsException( $formatName, $class ) {
+
+ $factory = new QueryPrinterFactory();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $factory->registerFormat( $formatName, $class );
+ }
+
+ /**
+ * Register format exception data provider
+ *
+ * @return array
+ */
+ public function registerFormatExceptioProvider() {
+ return [
+ [ 1001, 'Foo' ],
+ [ 'Foo', 9001 ],
+ ];
+ }
+
+ /**
+ * @dataProvider registerAliasesExceptionProvider
+ */
+ public function testRegisterAliasesThrowsException( $formatName, array $aliases ) {
+
+ $factory = new QueryPrinterFactory();
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $factory->registerAliases( $formatName, $aliases );
+ }
+
+ /**
+ * Register aliases exception data provider
+ *
+ * @return array
+ */
+ public function registerAliasesExceptionProvider() {
+ return [
+ [ 1001, [ 'Foo' => 'Bar' ] ],
+ [ 'Foo', [ 'Foo' => 9001 ] ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryProcessorTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryProcessorTest.php
new file mode 100644
index 00000000..06280a2d
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/QueryProcessorTest.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * @file
+ * @since 1.8
+ */
+
+namespace SMW\Test;
+
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMWQueryProcessor;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * Tests for the SMWQueryProcessor class.
+ *
+ * @since 1.8
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWQueries
+ * @group SMWQueryProcessorTest
+ *
+ * @author Nischay Nahata
+ */
+class SMWQueryProcessorTest extends MwDBaseUnitTestCase {
+
+ use PHPUnitCompat;
+
+ public function createQueryDataProvider() {
+ return [
+ [ '[[Modification date::+]]|?Modification date|sort=Modification date|order=desc' ],
+ ];
+ }
+
+ /**
+ * @dataProvider resultAliasDataProvider
+ */
+ public function testGetResultPrinter_MatchAlias( $alias ) {
+
+ $this->assertInstanceOf(
+ '\SMW\Query\ResultPrinter',
+ SMWQueryProcessor::getResultPrinter( $alias )
+ );
+ }
+
+ public function resultAliasDataProvider() {
+
+ foreach ( $GLOBALS['smwgResultAliases'] as $format => $aliases ) {
+ foreach ( $aliases as $alias ) {
+ yield [ $alias ];
+ }
+ }
+ }
+
+ public function testGetResultPrinter_ThrowsException() {
+
+ $this->setExpectedException( '\SMW\Query\Exception\ResultFormatNotFoundException' );
+ SMWQueryProcessor::getResultPrinter( 'unknown_format' );
+ }
+
+
+ /**
+ * @dataProvider createQueryDataProvider
+ */
+ public function testCreateQuery( $query ) {
+ // TODO: this prevents doing [[Category:Foo||bar||baz]], must document.
+ $rawParams = explode( '|', $query );
+
+ list( $queryString, $parameters, $printouts ) = SMWQueryProcessor::getComponentsFromFunctionParams( $rawParams, false );
+
+ SMWQueryProcessor::addThisPrintout( $printouts, $parameters );
+
+ $parameters = SMWQueryProcessor::getProcessedParams( $parameters, $printouts );
+
+ $this->assertInstanceOf(
+ '\SMWQuery',
+ SMWQueryProcessor::createQuery(
+ $queryString,
+ $parameters,
+ SMWQueryProcessor::SPECIAL_PAGE,
+ '',
+ $printouts
+ ),
+ "Result should be instance of SMWQuery."
+ );
+ }
+
+ /**
+ * @dataProvider rawParamsProvider
+ */
+ public function testQuerStringFromRawParameters( $rawParams, $expected ) {
+
+ list( $queryString, $parameters, $printouts ) = SMWQueryProcessor::getComponentsFromFunctionParams( $rawParams, false );
+
+ $this->assertEquals(
+ $expected,
+ $queryString
+ );
+ }
+
+ public function rawParamsProvider() {
+
+ $provider[] = [
+ [ 'Foo', 'bar' ],
+ 'Foobar'
+ ];
+
+ $provider[] = [
+ [ 'Foo=', 'Bar' ],
+ 'Bar'
+ ];
+
+ $provider[] = [
+ [ '[[Foo::Foo=Bar]]', '+Foo' ],
+ '[[Foo::Foo=Bar]]'
+ ];
+
+ $provider[] = [
+ [ '[[Modification date::+]]', '?Modification date=Date', 'limit=1' ],
+ '[[Modification date::+]]'
+ ];
+
+ $provider[] = [
+ [ '?Foo', 'limit=1', '[[Modification date::+]] OR [[Foo::Foo=Bar]]' ],
+ '[[Modification date::+]] OR [[Foo::Foo=Bar]]'
+ ];
+
+ $provider[] = [
+ [ '[[Modification date::+]] OR <q>[[Foo::Bar]]</q>', '?Modification date=Date', '?Foo=F', 'limit=1' ],
+ '[[Modification date::+]] OR <q>[[Foo::Bar]]</q>'
+ ];
+
+ $provider[] = [
+ [ '[[Has url::http://example.org/api.php?action=Foo]]', '?Has url=url', 'limit=1' ],
+ '[[Has url::http://example.org/api.php?action=Foo]]'
+ ];
+
+ // Produced by smw.org, Template:Invert-property
+ $provider[] = [
+ [ '[[Located in::Foo]]', 'link=none', 'sep=| ]][[Location of::' ],
+ '[[Located in::Foo]]'
+ ];
+
+ $provider[] = [
+ [ '[[Located in::Foo]]', 'link=none', 'sep=| ]][[Location of::', '[[Has url::http://example.org/api.php?action=Foo]]' ],
+ '[[Located in::Foo]][[Has url::http://example.org/api.php?action=Foo]]'
+ ];
+
+ $provider[] = [
+ [ '[[This has a = in it]]', 'link=none', 'sep=| ]][[Location of::', '[[Has url::http://example.org/api.php?action=Foo]]' ],
+ '[[This has a = in it]][[Has url::http://example.org/api.php?action=Foo]]'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/RecurringEventsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/RecurringEventsTest.php
new file mode 100644
index 00000000..70f3dd7b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/RecurringEventsTest.php
@@ -0,0 +1,410 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\ParserParameterFormatter;
+use SMW\RecurringEvents;
+
+/**
+ * @covers \SMW\RecurringEvents
+ * @group semantic-mediawiki
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RecurringEventsTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ RecurringEvents::class,
+ new RecurringEvents()
+ );
+ }
+
+ /**
+ * @dataProvider getParametersDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetErrors( array $params, array $expected ) {
+
+ $parameters = new ParserParameterFormatter( $params );
+
+ $instance = new RecurringEvents();
+ $instance->parse( $parameters->toArray() );
+
+ $this->assertCount(
+ $expected['errors'],
+ $instance->getErrors() );
+ }
+
+ /**
+ * @dataProvider getParametersDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetProperty( array $params, array $expected ) {
+
+ $parameters = new ParserParameterFormatter( $params );
+
+ $instance = new RecurringEvents();
+ $instance->parse( $parameters->toArray() );
+
+ $this->assertEquals(
+ $expected['property'],
+ $instance->getProperty()
+ );
+ }
+
+ /**
+ * @dataProvider getParametersDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetParameters( array $params, array $expected ) {
+
+ $parameters = new ParserParameterFormatter( $params );
+
+ $instance = new RecurringEvents();
+ $instance->parse( $parameters->toArray() );
+
+ $this->assertEquals(
+ $expected['parameters'],
+ $instance->getParameters()
+ );
+ }
+
+ /**
+ * @dataProvider getParametersDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetDates( array $params, array $expected ) {
+
+ $parameters = new ParserParameterFormatter( $params );
+
+ $instance = new RecurringEvents();
+ $instance->parse( $parameters->toArray() );
+
+ $this->assertEquals(
+ $expected['dates'],
+ $instance->getDates()
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function getMassInsertDataProvider() {
+ return [
+ [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1970',
+ 'Has title=Birthday',
+ 'unit=month', 'period=12',
+ 'limit=500',
+ ],
+ [
+ 'errors' => 0,
+ 'count' => 501,
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getMassInsertDataProvider
+ *
+ * @since 1.
+ */
+ public function testMassInsert( array $params, array $expected ) {
+
+ $parameters = new ParserParameterFormatter( $params );
+
+ $instance = new RecurringEvents();
+ $instance->parse( $parameters->toArray() );
+
+ $this->assertCount(
+ $expected['count'],
+ $instance->getDates()
+ );
+ }
+
+ /**
+ * @test RecurringEvents::getJulianDay
+ *
+ * @since 1.9
+ */
+ public function testGetJulianDay() {
+ $instance = new RecurringEvents();
+ $instance->parse( [] );
+
+ // SMWDIWikiPage stub object
+ $dataValue = $this->getMockBuilder( 'SMWTimeValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( null ) );
+
+ $this->assertEquals(
+ null,
+ $instance->getJulianDay( $dataValue )
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function getParametersDataProvider() {
+ return [
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1970
+ // |has title= Birthday
+ // |unit=year
+ // |period=12
+ // |limit=3
+ // }}
+ [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1970',
+ 'has title=Birthday',
+ 'unit=month',
+ 'period=12',
+ 'limit=3'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '1 February 1970', '1 February 1971', '1 February 1972', '1 February 1973' ],
+ 'property' => 'Has birthday',
+ 'parameters' => [ 'has title' => [ 'Birthday' ] ]
+ ]
+ ],
+
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1970
+ // |end=01 Feb 1972
+ // |has title= Birthday
+ // |unit=year
+ // |period=12
+ // |limit=3
+ // }}
+ [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1970',
+ 'end=01 Feb 1972',
+ 'has title=Birthday',
+ 'unit=month',
+ 'period=12',
+ 'limit=3'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '1 February 1970', '1 February 1971', '1 February 1972' ],
+ 'property' => 'Has birthday',
+ 'parameters' => [ 'has title' => [ 'Birthday' ] ]
+ ]
+ ],
+
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1970
+ // |end=01 Feb 1972
+ // |has title= Birthday
+ // |unit=year
+ // |week number=2
+ // |period=12
+ // |limit=3
+ // }}
+ [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1970',
+ 'end=01 Feb 1972',
+ 'has title=Birthday',
+ 'unit=month',
+ 'week number=2',
+ 'period=12',
+ 'limit=3'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '1 February 1970', '14 February 1971' ],
+ 'property' => 'Has birthday',
+ 'parameters' => [ 'has title' => [ 'Birthday' ] ]
+ ]
+ ],
+
+ // {{#set_recurring_event:property=Has birthday
+ // |start=01 Feb 1972 02:00
+ // |has title=Test 12
+ // |unit=week
+ // |period=4
+ // |limit=3
+ // }}
+ [
+ [
+ 'property=Has birthday',
+ 'start=01 Feb 1972 02:00',
+ 'has title=Test 2',
+ 'unit=week',
+ 'period=4',
+ 'limit=3'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '1 February 1972 02:00:00', '29 February 1972 02:00:00', '28 March 1972 02:00:00', '25 April 1972 02:00:00' ],
+ 'property' => 'Has birthday',
+ 'parameters' => [ 'has title' => [ 'Test 2' ] ]
+ ]
+ ],
+
+ // {{#set_recurring_event:property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ [
+ [
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '4 January 2010', '11 January 2010', '1 February 2010', 'March 16, 2010', 'March 23, 2010' ],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ],
+
+ // {{#set_recurring_event:property=Has date
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010|+sep=;
+ // |exclude=January 18, 2010;January 25, 2010|+sep=;
+ // }}
+ [
+ [
+ 'property=Has date',
+ 'start=January 4, 2010',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ '+sep=;',
+ 'exclude=January 18, 2010;January 25, 2010',
+ '+sep=;'
+ ],
+ [
+ 'errors' => 0,
+ 'dates' => [ '4 January 2010', '11 January 2010', '1 February 2010', 'March 16, 2010', 'March 23, 2010' ],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ],
+
+ // Simulate start date has wrong type
+
+ // {{#set_recurring_event:property=Has date
+ // |start=???
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ [
+ [
+ 'property=Has date',
+ 'start=???',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => 1,
+ 'dates' => [],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ],
+
+ // Simulate missing start date
+
+ // {{#set_recurring_event:property=Has date
+ // |start=
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010
+ // |exclude=January 18, 2010;January 25, 2010
+ // }}
+ [
+ [
+ 'property=Has date',
+ 'start=',
+ 'unit=week',
+ 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ 'exclude=January 18, 2010;January 25, 2010'
+ ],
+ [
+ 'errors' => 1,
+ 'dates' => [],
+ 'property' => 'Has date',
+ 'parameters' => []
+ ]
+ ],
+
+ // Simulate missing property
+
+ // {{#set_recurring_event:property=
+ // |start=January 4, 2010
+ // |unit=week
+ // |period=1
+ // |limit=4
+ // |include=March 16, 2010;March 23, 2010|+sep=;
+ // |exclude=January 18, 2010;January 25, 2010|+sep=;
+ // }}
+ [
+ [
+ 'property=',
+ 'start=January 4, 2010',
+ 'unit=week', 'period=1',
+ 'limit=4',
+ 'include=March 16, 2010;March 23, 2010',
+ '+sep=;',
+ 'exclude=January 18, 2010;January 25, 2010',
+ '+sep=;'
+ ],
+ [
+ 'errors' => 1,
+ 'dates' => [],
+ 'property' => '',
+ 'parameters' => []
+ ]
+ ],
+ ];
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/ChangeTitleTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/ChangeTitleTest.php
new file mode 100644
index 00000000..18d2297e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/ChangeTitleTest.php
@@ -0,0 +1,329 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Writer;
+
+use SMWSQLStore3Writers;
+use Title;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @covers \SMWSQLStore3Writers
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-sqlstore
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class ChangeTitleTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $factory;
+ private $testEnvironment;
+
+ protected function setUp() {
+
+ $nullJob = $this->getMockBuilder( '\SMW\MediaWiki\Jobs\NullJob' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory = $this->getMockBuilder( '\SMW\MediaWiki\JobFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jobFactory->expects( $this->any() )
+ ->method( 'newUpdateJob' )
+ ->will( $this->returnValue( $nullJob ) );
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->registerObject( 'JobFactory', $jobFactory );
+
+ $entityManager = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityManager->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityManager ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $propertyTableRowDiffer = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableRowDiffer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableRowDiffer->expects( $this->any() )
+ ->method( 'computeTableRowDiff' )
+ ->will( $this->returnValue( [ [], [], [] ] ) );
+
+ $propertyTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\SubobjectListFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder->expects( $this->any() )
+ ->method( 'find' )
+ ->will( $this->returnValue( [] ) );
+
+ $changePropListener = $this->getMockBuilder( '\SMW\ChangePropListener' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticDataLookup = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\CachingSemanticDataLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTextItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->any() )
+ ->method( 'newChangeDiff' )
+ ->will( $this->returnValue( $changeDiff ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getDataOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( [] ) );
+
+ $idChanger = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdChanger' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyStatisticsStore' )
+ ->will( $this->returnValue( $propertyStatisticsStore ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newHierarchyLookup' )
+ ->will( $this->returnValue( $hierarchyLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSubobjectListFinder' )
+ ->will( $this->returnValue( $subobjectListFinder ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangePropListener' )
+ ->will( $this->returnValue( $changePropListener ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableRowDiffer' )
+ ->will( $this->returnValue( $propertyTableRowDiffer ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableUpdater' )
+ ->will( $this->returnValue( $propertyTableUpdater ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSemanticDataLookup' )
+ ->will( $this->returnValue( $semanticDataLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangeOp' )
+ ->will( $this->returnValue( $changeOp ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newIdChanger' )
+ ->will( $this->returnValue( $idChanger ) );
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWSQLStore3Writers',
+ new SMWSQLStore3Writers( $this->store, $this->factory )
+ );
+ }
+
+ public function testChangeTitleForMainNamespaceWithoutRedirectId() {
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $objectIdGenerator->expects( $this->at( 0 ) )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 1 ) );
+
+ $objectIdGenerator->expects( $this->at( 1 ) )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 5 ) );
+
+ $objectIdGenerator->expects( $this->at( 1 ) )
+ ->method( 'findRedirect' )
+ ->will( $this->returnValue( 0 ) );
+
+ $objectIdGenerator->expects( $this->never() )
+ ->method( 'deleteRedirect' );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+
+ $instance->changeTitle(
+ Title::newFromText( __METHOD__ . '-old', NS_MAIN ),
+ Title::newFromText( __METHOD__ . '-new', NS_MAIN ),
+ 9999
+ );
+ }
+
+ public function testChangeTitleForMainNamespaceWithRedirectId() {
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $objectIdGenerator->expects( $this->at( 0 ) )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 1 ) );
+
+ $objectIdGenerator->expects( $this->at( 1 ) )
+ ->method( 'getSMWPageID' )
+ ->will( $this->returnValue( 5 ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->once() )
+ ->method( 'query' )
+ ->will( $this->returnValue( true ) );
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+
+ $instance->changeTitle(
+ Title::newFromText( __METHOD__ . '-old', NS_MAIN ),
+ Title::newFromText( __METHOD__ . '-new', NS_MAIN ),
+ 9999,
+ 1111
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DataUpdateTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DataUpdateTest.php
new file mode 100644
index 00000000..995215ee
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DataUpdateTest.php
@@ -0,0 +1,409 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Writer;
+
+use SMW\DIWikiPage;
+use SMWSQLStore3Writers;
+use Title;
+
+/**
+ * @covers \SMWSQLStore3Writers
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group semantic-mediawiki-sqlstore
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class DataUpdateTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $factory;
+
+ protected function setUp() {
+
+ $entityManager = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $entityManager->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store = $this->getMockBuilder( '\SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $entityManager ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $propertyTableRowDiffer = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableRowDiffer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableRowDiffer->expects( $this->any() )
+ ->method( 'computeTableRowDiff' )
+ ->will( $this->returnValue( [ [], [], [] ] ) );
+
+ $propertyTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\SubobjectListFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder->expects( $this->any() )
+ ->method( 'find' )
+ ->will( $this->returnValue( [] ) );
+
+ $changePropListener = $this->getMockBuilder( '\SMW\ChangePropListener' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticDataLookup = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\CachingSemanticDataLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTextItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->any() )
+ ->method( 'newChangeDiff' )
+ ->will( $this->returnValue( $changeDiff ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getDataOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyStatisticsStore' )
+ ->will( $this->returnValue( $propertyStatisticsStore ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newHierarchyLookup' )
+ ->will( $this->returnValue( $hierarchyLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSubobjectListFinder' )
+ ->will( $this->returnValue( $subobjectListFinder ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangePropListener' )
+ ->will( $this->returnValue( $changePropListener ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableRowDiffer' )
+ ->will( $this->returnValue( $propertyTableRowDiffer ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableUpdater' )
+ ->will( $this->returnValue( $propertyTableUpdater ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSemanticDataLookup' )
+ ->will( $this->returnValue( $semanticDataLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangeOp' )
+ ->will( $this->returnValue( $changeOp ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWSQLStore3Writers',
+ new SMWSQLStore3Writers( $this->store, $this->factory )
+ );
+ }
+
+ public function testDoDataUpdateForMainNamespaceWithoutSubobject() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->setConstructorArgs( [ DIWikiPage::newFromTitle( $title ) ] )
+ ->setMethods( null )
+ ->getMock();
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'getSMWPageIDandSort' )
+ ->will( $this->returnValue( 0 ) );
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 0 ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+ $instance->doDataUpdate( $semanticData );
+ }
+
+ public function testDoDataUpdateForConceptNamespaceWithoutSubobject() {
+
+ $title = Title::newFromText( __METHOD__, SMW_NS_CONCEPT );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->setConstructorArgs( [ DIWikiPage::newFromTitle( $title ) ] )
+ ->setMethods( null )
+ ->getMock();
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->any() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [] ) );
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'getSMWPageIDandSort' )
+ ->will( $this->returnValue( 0 ) );
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 0 ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+ $instance->doDataUpdate( $semanticData );
+ }
+
+ public function testDoDataUpdateForMainNamespaceWithRedirect() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->setConstructorArgs( [ DIWikiPage::newFromTitle( $title ) ] )
+ ->setMethods( [ 'getPropertyValues' ] )
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $title ) ] ) );
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'getSMWPageIDandSort' )
+ ->will( $this->returnValue( 0 ) );
+
+ $objectIdGenerator->expects( $this->once() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 0 ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+ $instance->doDataUpdate( $semanticData );
+ }
+
+ public function testAtomicTransactionOnDataUpdate() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $semanticData = $this->getMockBuilder( '\SMW\SemanticData' )
+ ->setConstructorArgs( [ DIWikiPage::newFromTitle( $title ) ] )
+ ->setMethods( [ 'getPropertyValues' ] )
+ ->getMock();
+
+ $semanticData->expects( $this->once() )
+ ->method( 'getPropertyValues' )
+ ->will( $this->returnValue( [ DIWikiPage::newFromTitle( $title ) ] ) );
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->atLeastOnce() )
+ ->method( 'getSMWPageIDandSort' )
+ ->will( $this->returnValue( 0 ) );
+
+ $objectIdGenerator->expects( $this->atLeastOnce() )
+ ->method( 'makeSMWPageID' )
+ ->will( $this->returnValue( 0 ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->atLeastOnce() )
+ ->method( 'beginAtomicTransaction' );
+
+ $database->expects( $this->atLeastOnce() )
+ ->method( 'endAtomicTransaction' );
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $parentStore->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $parentStore->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $parentStore, $this->factory );
+ $instance->doDataUpdate( $semanticData );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DeleteSubjectTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DeleteSubjectTest.php
new file mode 100644
index 00000000..729d6694
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SQLStore/Writer/DeleteSubjectTest.php
@@ -0,0 +1,259 @@
+<?php
+
+namespace SMW\Tests\SQLStore\Writer;
+
+use SMWSQLStore3Writers;
+use Title;
+
+/**
+ * @covers \SMWSQLStore3Writers
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.2
+ *
+ * @author mwjames
+ */
+class DeleteSubjectTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $factory;
+
+ protected function setUp() {
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $hierarchyLookup = $this->getMockBuilder( '\SMW\HierarchyLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\SubobjectListFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $subobjectListFinder->expects( $this->any() )
+ ->method( 'find' )
+ ->will( $this->returnValue( [] ) );
+
+ $changePropListener = $this->getMockBuilder( '\SMW\ChangePropListener' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyStatisticsStore' )
+ ->will( $this->returnValue( $propertyStatisticsStore ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newHierarchyLookup' )
+ ->will( $this->returnValue( $hierarchyLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSubobjectListFinder' )
+ ->will( $this->returnValue( $subobjectListFinder ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangePropListener' )
+ ->will( $this->returnValue( $changePropListener ) );
+
+ $propertyTableIdReferenceFinder = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableIdReferenceFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableInfoFetcher = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableInfoFetcher' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTableInfoFetcher' )
+ ->will( $this->returnValue( $propertyTableInfoFetcher ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'service' )
+ ->with( $this->equalTo( 'PropertyTableIdReferenceFinder' ) )
+ ->will( $this->returnValue( $propertyTableIdReferenceFinder ) );
+
+ $propertyTableRowDiffer = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableRowDiffer' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyTableRowDiffer->expects( $this->any() )
+ ->method( 'computeTableRowDiff' )
+ ->will( $this->returnValue( [ [], [], [] ] ) );
+
+ $propertyTableUpdater = $this->getMockBuilder( '\SMW\SQLStore\PropertyTableUpdater' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $semanticDataLookup = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\CachingSemanticDataLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeDiff' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTextItems' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeDiff->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp = $this->getMockBuilder( '\SMW\SQLStore\ChangeOp\ChangeOp' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $changeOp->expects( $this->any() )
+ ->method( 'newChangeDiff' )
+ ->will( $this->returnValue( $changeDiff ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getChangedEntityIdSummaryList' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getDataOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getTableChangeOps' )
+ ->will( $this->returnValue( [] ) );
+
+ $changeOp->expects( $this->any() )
+ ->method( 'getOrderedDiffByTable' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableRowDiffer' )
+ ->will( $this->returnValue( $propertyTableRowDiffer ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyTableUpdater' )
+ ->will( $this->returnValue( $propertyTableUpdater ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newSemanticDataLookup' )
+ ->will( $this->returnValue( $semanticDataLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newChangeOp' )
+ ->will( $this->returnValue( $changeOp ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWSQLStore3Writers',
+ new SMWSQLStore3Writers( $this->store, $this->factory )
+ );
+ }
+
+ public function testDeleteSubjectForMainNamespace() {
+
+ $title = Title::newFromText( __METHOD__, NS_MAIN );
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->atLeastOnce() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->will( $this->returnValue( [ 0 ] ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->exactly( 7 ) )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $this->store, $this->factory );
+ $instance->deleteSubject( $title );
+ }
+
+ public function testDeleteSubjectForConceptNamespace() {
+
+ $title = Title::newFromText( __METHOD__, SMW_NS_CONCEPT );
+
+ $objectIdGenerator = $this->getMockBuilder( '\SMWSql3SmwIds' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $objectIdGenerator->expects( $this->atLeastOnce() )
+ ->method( 'findAllEntitiesThatMatch' )
+ ->with(
+ $this->equalTo( $title->getDBkey() ),
+ $this->equalTo( $title->getNamespace() ),
+ $this->equalTo( $title->getInterwiki() ),
+ '' )
+ ->will( $this->returnValue( [ 0 ] ) );
+
+ $database = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $database->expects( $this->exactly( 2 ) )
+ ->method( 'delete' )
+ ->will( $this->returnValue( true ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $database ) );
+
+ $this->store->expects( $this->exactly( 7 ) )
+ ->method( 'getObjectIds' )
+ ->will( $this->returnValue( $objectIdGenerator ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getPropertyTables' )
+ ->will( $this->returnValue( [] ) );
+
+ $this->store->expects( $this->any() )
+ ->method( 'getOptions' )
+ ->will( $this->returnValue( new \SMW\Options() ) );
+
+ $instance = new SMWSQLStore3Writers( $this->store, $this->factory );
+ $instance->deleteSubject( $title );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SemanticDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SemanticDataTest.php
new file mode 100644
index 00000000..51a2b794
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SemanticDataTest.php
@@ -0,0 +1,710 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ApplicationFactory;
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Localizer;
+use SMW\SemanticData;
+use SMW\Subobject;
+use SMWDITime as DITime;
+use Title;
+
+/**
+ * @covers \SMW\SemanticData
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SemanticDataTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $semanticDataValidator;
+ private $dataValueFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->testEnvironment->addConfiguration( 'smwgCreateProtectionRight', false );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getRedirectTarget' )
+ ->will( $this->returnArgument( 0 ) );
+
+ $this->testEnvironment->registerObject( 'Store', $store );
+
+ $this->semanticDataValidator = $this->testEnvironment->getUtilityFactory()->newValidatorFactory()->newSemanticDataValidator();
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testConstructor() {
+
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $instance
+ );
+
+ $this->assertInstanceOf(
+ 'SMWSemanticData',
+ $instance
+ );
+ }
+
+ public function testGetPropertyValues() {
+
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $this->assertInstanceOf(
+ 'SMW\DIWikiPage',
+ $instance->getSubject()
+ );
+
+ $this->assertEmpty(
+ $instance->getPropertyValues( new DIProperty( 'Foo', true ) )
+ );
+
+ $this->assertEmpty(
+ $instance->getPropertyValues( new DIProperty( 'Foo' ) )
+ );
+ }
+
+ public function testAddPropertyValue() {
+
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $instance->addPropertyValue(
+ 'addPropertyValue',
+ DIWikiPage::doUnserialize( 'Foo#0##' )
+ );
+
+ $key = Localizer::getInstance()->getNamespaceTextById( SMW_NS_PROPERTY ) . ':' . 'addPropertyValue';
+
+ $expected = [
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ $key ],
+ 'propertyValues' => [ 'Foo' ]
+ ];
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance
+ );
+ }
+
+ public function testGetHash() {
+
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $instance->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' )
+ );
+
+ $subobject = $this->newSubobject( $instance->getSubject()->getTitle() );
+
+ $instance->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $instance->getHash()
+ );
+ }
+
+ public function testPropertyOrderDoesNotInfluenceHash() {
+
+ $instance = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Bar', 'Foo' )
+ );
+
+ $instanceToCheck = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instanceToCheck->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Bar', 'Foo' )
+ );
+
+ $instanceToCheck->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $this->assertEquals(
+ $instance->getHash(),
+ $instanceToCheck->getHash()
+ );
+ }
+
+ public function testSubSemanticPropertyOrderDoesNotInfluenceHash() {
+
+ $subobject = new Subobject( Title::newFromText( 'Foo' ) );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Bar', 'Foo' )
+ );
+
+ $instance = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instance->addSubobject(
+ $subobject
+ );
+
+ $subobject = new Subobject( Title::newFromText( 'Foo' ) );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Bar', 'Foo' )
+ );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $instanceToCheck = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instanceToCheck->addSubobject(
+ $subobject
+ );
+
+ $this->assertEquals(
+ $instance->getHash(),
+ $instanceToCheck->getHash()
+ );
+ }
+
+ public function testThatChangingDataDoesEnforceDifferentHash() {
+
+ $instance = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $firstHash = $instance->getHash();
+
+ $instance->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $secondHash = $instance->getHash();
+
+ $this->assertNotEquals(
+ $firstHash,
+ $secondHash
+ );
+
+ $subobject = new Subobject( Title::newFromText( 'Foo' ) );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $subobject->addDataValue(
+ $this->dataValueFactory->newDataValueByText( 'Foo', 'Bar' )
+ );
+
+ $instance->addSubSemanticData(
+ $subobject->getSemanticData()
+ );
+
+ $thirdHash = $instance->getHash();
+
+ $this->assertNotEquals(
+ $secondHash,
+ $thirdHash
+ );
+
+ // Remove the data added in the third step and expect
+ // the hash from the second
+ $instance->removeSubSemanticData(
+ $subobject->getSemanticData()
+ );
+
+ $this->assertEquals(
+ $secondHash,
+ $instance->getHash()
+ );
+ }
+
+ public function testGetSubSemanticData() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ // Adds only a subobject reference to the container
+ $subobject = $this->newSubobject( $title );
+
+ $instance->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getSemanticData()->getSubject()
+ );
+
+ $this->assertNotInstanceOf(
+ 'SMWContainerSemanticData',
+ $instance->getSubSemanticData()
+ );
+
+ // Adds a complete container
+ $instance->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ foreach ( $instance->getSubSemanticData() as $subSemanticData ) {
+
+ $this->assertInstanceOf(
+ 'SMWContainerSemanticData',
+ $subSemanticData
+ );
+ }
+ }
+
+ public function testAddAndRemoveSubSemanticData() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ // Adds only a subobject reference to the container
+ $subobject = $this->newSubobject( $title );
+
+ $instance->addSubobject( $subobject );
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getSubSemanticData()
+ );
+
+ foreach ( $instance->getSubSemanticData() as $subSemanticData ) {
+
+ $this->assertInstanceOf(
+ 'SMWContainerSemanticData',
+ $subSemanticData
+ );
+
+ $this->assertEquals(
+ $subSemanticData,
+ $subobject->getSemanticData()
+ );
+ }
+
+ $instance->removeSubSemanticData( $subobject->getSemanticData() );
+
+ $this->assertNotInstanceOf(
+ 'SMWContainerSemanticData',
+ $instance->getSubSemanticData()
+ );
+ }
+
+ public function testAddSubSemanticDataWithOutSubobjectNameThrowsException() {
+
+ $instance = new SemanticData( DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) ) );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException' );
+
+ $instance->addSubSemanticData(
+ new SemanticData( DIWikiPage::newFromTitle( Title::newFromText( 'addSubSemanticData' ) ) )
+ );
+ }
+
+ public function testDifferentSubSemanticDataSubjectThrowsException() {
+
+ $instance = new SemanticData( DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) ) );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException' );
+ $instance->addSubobject( $this->newSubobject( Title::newFromText( 'addSubSemanticData' ) ) );
+ }
+
+ public function testImportDataFromForDifferentSubjectThrowsException() {
+
+ $instance = new SemanticData( DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) ) );
+
+ $this->setExpectedException( '\SMW\Exception\SemanticDataImportException' );
+
+ $instance->importDataFrom(
+ new SemanticData( DIWikiPage::newFromTitle( Title::newFromText( 'importDataFrom' ) ) )
+ );
+ }
+
+ public function testHasAndFindSubSemanticData() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ $subobject = $this->newSubobject( $title );
+ $subobjectName = $subobject->getSemanticData()->getSubject()->getSubobjectName();
+
+ $this->assertFalse( $instance->hasSubSemanticData() );
+ $this->assertEmpty( $instance->findSubSemanticData( $subobjectName ));
+
+ // Adds only a subobject reference to the container
+ $instance->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getSemanticData()->getSubject()
+ );
+
+ $this->assertFalse( $instance->hasSubSemanticData( $subobjectName ) );
+ $this->assertEmpty( $instance->findSubSemanticData( $subobjectName ) );
+
+ $instance->addSubSemanticData( $subobject->getSemanticData() );
+
+ $this->assertTrue( $instance->hasSubSemanticData( $subobjectName ) );
+ $this->assertNotEmpty($instance->findSubSemanticData( $subobjectName ) );
+
+ $this->assertInstanceOf(
+ 'SMWContainerSemanticData',
+ $instance->findSubSemanticData( $subobjectName )
+ );
+ }
+
+ public function testSubSemanticDataForNonStringSubobjectName() {
+
+ $instance = new SemanticData(
+ DIWikiPage::newFromTitle( Title::newFromText( __METHOD__ ) )
+ );
+
+ $this->assertFalse(
+ $instance->hasSubSemanticData( new \stdClass )
+ );
+
+ $this->assertEmpty(
+ $instance->findSubSemanticData( new \stdClass )
+ );
+ }
+
+ public function testSetLastModified() {
+
+ $instance = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instance->setOption( SemanticData::OPT_LAST_MODIFIED, 1001 );
+
+ $this->assertEquals(
+ 1001,
+ $instance->getOption( SemanticData::OPT_LAST_MODIFIED )
+ );
+ }
+
+ public function testGetLastModifiedFromModificationDate() {
+
+ $instance = new SemanticData(
+ new DIWikiPage( 'Foo', NS_MAIN )
+ );
+
+ $instance->addPropertyObjectValue(
+ new DIProperty( '_MDAT' ),
+ DITime::newFromTimestamp( 1272508903 )
+ );
+
+ $this->assertEquals(
+ 1272508903,
+ $instance->getOption( SemanticData::OPT_LAST_MODIFIED )
+ );
+ }
+
+ public function testVisibility() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ $instance->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByText( 'Has fooQuex', 'Bar' )
+ );
+
+ $this->assertTrue(
+ $instance->hasVisibleProperties()
+ );
+
+ $instance->addSubobject(
+ $this->newSubobject( $title )
+ );
+
+ $this->assertTrue(
+ $instance->hasVisibleSpecialProperties()
+ );
+ }
+
+ /**
+ * @dataProvider removePropertyObjectProvider
+ */
+ public function testRemovePropertyObjectValue( $title, $property, $dataItem ) {
+
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ $instance->addPropertyObjectValue( $property, $dataItem );
+ $this->assertFalse( $instance->isEmpty() );
+
+ $instance->removePropertyObjectValue( $property, $dataItem );
+ $this->assertTrue( $instance->isEmpty() );
+ }
+
+ public function testRemoveProperty() {
+
+ $property = new DIProperty( 'Foo' );
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $instance->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'Bar', NS_MAIN, '', 'Foobar' )
+ );
+
+ $this->assertTrue(
+ $instance->hasProperty( $property )
+ );
+
+ $instance->removeProperty( $property );
+
+ $this->assertFalse(
+ $instance->hasProperty( $property )
+ );
+ }
+
+ public function testGetPropertyValuesToReturnAnUnmappedArray() {
+
+ $property = new DIProperty( 'Foo' );
+ $instance = new SemanticData( DIWikiPage::newFromText( __METHOD__ ) );
+
+ $instance->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( 'Bar', NS_MAIN )
+ );
+
+ $this->assertArrayHasKey(
+ 0,
+ $instance->getPropertyValues( $property )
+ );
+ }
+
+ public function testClear() {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ $instance->addPropertyObjectValue(
+ new DIProperty( '_MDAT' ),
+ DITime::newFromTimestamp( 1272508903 )
+ );
+
+ $this->assertFalse( $instance->isEmpty() );
+
+ $instance->clear();
+ $this->assertTrue( $instance->isEmpty() );
+ }
+
+ public function testExtensionData() {
+
+ $instance = new SemanticData(
+ DIWikiPage::newFromText( __METHOD__ )
+ );
+
+ $instance->setExtensionData( 'Foo', 42 );
+
+ $this->assertEquals(
+ 42,
+ $instance->getExtensionData( 'Foo' )
+ );
+
+ $callback = function() { return 42; };
+
+ $instance->setExtensionData( 'Bar', $callback );
+
+ $this->assertEquals(
+ $callback,
+ $instance->getExtensionData( 'Bar' )
+ );
+ }
+
+ /**
+ * @dataProvider dataValueDataProvider
+ */
+ public function testAddDataValues( $dataValues, $expected ) {
+
+ $title = Title::newFromText( __METHOD__ );
+ $instance = new SemanticData( DIWikiPage::newFromTitle( $title ) );
+
+ foreach ( $dataValues as $dataValue ) {
+ $instance->addDataValue( $dataValue );
+ }
+
+ if ( $expected['error'] > 0 ) {
+ return $this->assertCount( $expected['error'], $instance->getErrors() );
+ }
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function removePropertyObjectProvider() {
+ ApplicationFactory::clear();
+
+ $provider = [];
+
+ $title = Title::newFromText( __METHOD__ );
+ $subobject = $this->newSubobject( $title, __METHOD__, '999' );
+
+ // #0
+ $provider[] = [
+ $title,
+ new DIProperty( '_MDAT'),
+ DITime::newFromTimestamp( 1272508903 )
+ ];
+
+ // #1
+ $provider[] = [
+ $title,
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function dataValueDataProvider() {
+ ApplicationFactory::clear();
+
+ $provider = [];
+
+ // #0 Single DataValue is added
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' ),
+ ],
+ [
+ 'error' => 0,
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => 'Bar'
+ ]
+ ];
+
+ // #1 Equal Datavalues will only result in one added object
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' ),
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' ),
+ ],
+ [
+ 'error' => 0,
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => 'Bar'
+ ]
+ ];
+
+ // #2 Two different DataValue objects
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' ),
+ DataValueFactory::getInstance()->newDataValueByText( 'Lila', 'Lula' ),
+ ],
+ [
+ 'error' => 0,
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 'Foo', 'Lila' ],
+ 'propertyValues' => [ 'Bar', 'Lula' ]
+ ]
+ ];
+
+ // #3 Error (Inverse)
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( '-Foo', 'Bar' ),
+ ],
+ [
+ 'error' => 1,
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ // #4 One valid DataValue + an error object
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( 'Foo', 'Bar' ),
+ DataValueFactory::getInstance()->newDataValueByText( '-Foo', 'bar' ),
+ ],
+ [
+ 'error' => 1,
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ 'Foo' ],
+ 'propertyValues' => [ 'Bar' ]
+ ]
+ ];
+
+
+ // #5 Error (Predefined)
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( '_Foo', 'Bar' ),
+ ],
+ [
+ 'error' => 1,
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ // #6 Error (Known predefined property)
+ $provider[] = [
+ [
+ DataValueFactory::getInstance()->newDataValueByText( 'Modification date', 'Bar' ),
+ ],
+ [
+ 'error' => 1,
+ 'propertyCount' => 0,
+ ]
+ ];
+
+ return $provider;
+ }
+
+ private function newSubobject( Title $title, $property = 'Quuy', $value = 'Xeer' ) {
+
+ $subobject = new Subobject( $title );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $subobject->addDataValue(
+ DataValueFactory::getInstance()->newDataValueByText( $property, $value )
+ );
+
+ return $subobject;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SettingsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SettingsTest.php
new file mode 100644
index 00000000..d0e10722
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SettingsTest.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\Settings;
+use SMW\Tests\TestEnvironment;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\Settings
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SettingsTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ protected function tearDown() {
+ Settings::clear();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider settingsProvider
+ */
+ public function testCanConstruct( array $settings ) {
+
+ $instance = Settings::newFromArray( $settings );
+
+ $this->assertInstanceOf(
+ '\SMW\Settings',
+ $instance
+ );
+
+ $this->assertFalse(
+ $instance === Settings::newFromArray( $settings )
+ );
+ }
+
+ /**
+ * @dataProvider settingsProvider
+ */
+ public function testGet( array $settings ) {
+
+ $instance = Settings::newFromArray( $settings );
+
+ foreach ( $settings as $name => $value ) {
+ $this->assertEquals( $value, $instance->get( $name ) );
+ }
+
+ $this->assertTrue( true );
+ }
+
+ public function testUnknownSettingThrowsException() {
+
+ $instance = Settings::newFromArray( [ 'Foo' => 'bar' ] );
+
+ $this->setExpectedException( '\SMW\Exception\SettingNotFoundException' );
+ $instance->get( 'foo' );
+ }
+
+ public function testSafeGetOnUnknownSetting() {
+
+ $instance = Settings::newFromArray( [ 'Foo' => 'bar' ] );
+
+ $this->assertFalse(
+ $instance->safeGet( 'foo', false )
+ );
+ }
+
+ /**
+ * @dataProvider settingsProvider
+ */
+ public function testSet( array $settings ) {
+
+ $instance = Settings::newFromArray( [] );
+
+ foreach ( $settings as $name => $value ) {
+ $instance->set( $name, $value );
+ $this->assertEquals( $value, $instance->get( $name ) );
+ }
+
+ $this->assertTrue( true );
+ }
+
+ /**
+ * @dataProvider globalsSettingsProvider
+ */
+ public function testNewFromGlobals( array $settings ) {
+
+ $instance = Settings::newFromGlobals();
+
+ // Assert that newFromGlobals is a static instance
+ $this->assertTrue(
+ $instance === Settings::newFromGlobals()
+ );
+
+ // Reset instance
+ $instance->clear();
+ $this->assertTrue( $instance !== Settings::newFromGlobals() );
+
+ foreach ( $settings as $key => $value ) {
+ $this->assertTrue( $instance->has( $key ), "Failed asserting that {$key} exists" );
+ }
+ }
+
+ /**
+ * @dataProvider nestedSettingsProvider
+ */
+ public function testNestedSettingsIteration( $test, $key, $expected ) {
+
+ $instance = Settings::newFromArray( $test );
+
+ $this->assertInternalType( $expected['type'], $instance->get( $key ) );
+ $this->assertEquals( $expected['value'], $instance->get( $key ) );
+ }
+
+ /**
+ * @return array
+ */
+ public function nestedSettingsProvider() {
+
+ $testEnvironment = new TestEnvironment();
+ $utilityFactory = $testEnvironment->getUtilityFactory();
+
+ $Foo = $utilityFactory->createRandomString();
+ $Lula = $utilityFactory->createRandomString();
+ $Lala = $utilityFactory->createRandomString();
+
+ $child = [ 'Lisa', 'Lula', [ 'Lila' ] ];
+ $parent = [ 'child' => $child ];
+
+ $Lila = [ 'Lala' => $Lala, 'parent' => $parent ];
+ $Bar = [ 'Lula' => $Lula, 'Lila' => $Lila ];
+ $test = [ 'Foo' => $Foo, 'Bar' => $Bar ];
+
+ return [
+ [ $test, 'Foo', [ 'type' => 'string', 'value' => $Foo ] ],
+ [ $test, 'Bar', [ 'type' => 'array', 'value' => $Bar ] ],
+ [ $test, 'Lula', [ 'type' => 'string', 'value' => $Lula ] ],
+ [ $test, 'Lila', [ 'type' => 'array', 'value' => $Lila ] ],
+ [ $test, 'Lala', [ 'type' => 'string', 'value' => $Lala ] ],
+ [ $test, 'parent', [ 'type' => 'array', 'value' => $parent ] ],
+ [ $test, 'child', [ 'type' => 'array', 'value' => $child ] ]
+ ];
+ }
+
+ /**
+ * Provides sample data to be tested
+ *
+ * @return array
+ */
+ public function settingsProvider() {
+ return [ [ [
+ 'foo' => 'bar',
+ 'baz' => 'BAH',
+ 'bar' => [ '9001' ],
+ 'foo' => [ '9001', [ 9001, 4.2 ] ],
+ '~[,,_,,]:3' => [ 9001, 4.2 ],
+ ] ] ];
+ }
+
+ /**
+ * Provides and collects individual smwg* settings
+ *
+ * @return array
+ */
+ public function globalsSettingsProvider() {
+
+ $settings = array_intersect_key( $GLOBALS,
+ array_flip( preg_grep('/^smwg/', array_keys( $GLOBALS ) ) )
+ );
+
+ unset( $settings['smwgDeprecationNotices'] );
+
+ return [ [ $settings ] ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SetupTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SetupTest.php
new file mode 100644
index 00000000..d9cf96fc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SetupTest.php
@@ -0,0 +1,344 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\ApplicationFactory;
+use SMW\Setup;
+
+/**
+ * @covers \SMW\Setup
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @group medium
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SetupTest extends \PHPUnit_Framework_TestCase {
+
+ private $applicationFactory;
+ private $defaultConfig;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $store->expects( $this->any() )
+ ->method( 'getProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $store->expects( $this->any() )
+ ->method( 'getInProperties' )
+ ->will( $this->returnValue( [] ) );
+
+ $language = $this->getMockBuilder( '\Language' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->applicationFactory = ApplicationFactory::getInstance();
+ $this->applicationFactory->registerObject( 'Store', $store );
+
+ $this->defaultConfig = [
+ 'smwgMainCacheType' => CACHE_NONE,
+ 'smwgNamespacesWithSemanticLinks' => [],
+ 'smwgEnableUpdateJobs' => false,
+ 'wgNamespacesWithSubpages' => [],
+ 'wgExtensionAssetsPath' => false,
+ 'smwgResourceLoaderDefFiles' => [],
+ 'wgResourceModules' => [],
+ 'wgScriptPath' => '/Foo',
+ 'wgServer' => 'http://example.org',
+ 'wgVersion' => '1.21',
+ 'wgLanguageCode' => 'en',
+ 'wgLang' => $language,
+ 'IP' => 'Foo',
+ 'smwgSemanticsEnabled' => true,
+ 'smwgConfigFileDir' => ''
+ ];
+
+ foreach ( $this->defaultConfig as $key => $value ) {
+ $this->applicationFactory->getSettings()->set( $key, $value );
+ }
+ }
+
+ protected function tearDown() {
+ $this->applicationFactory->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $applicationFactory = $this->getMockBuilder( '\SMW\ApplicationFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ Setup::class,
+ new Setup( $applicationFactory )
+ );
+ }
+
+ public function testResourceModules() {
+
+ $config = $this->defaultConfig;
+ $config['smwgResourceLoaderDefFiles'] = $GLOBALS['smwgResourceLoaderDefFiles'];
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $config, '' );
+
+ $this->assertNotEmpty(
+ $config['wgResourceModules']
+ );
+ }
+
+ /**
+ * @dataProvider apiModulesDataProvider
+ */
+ public function testGetAPIModules( $name ) {
+
+ $vars = Setup::getAPIModules();
+
+ $this->assertArrayHasKey(
+ $name,
+ $vars
+ );
+ }
+
+ /**
+ * @dataProvider jobClassesDataProvider
+ */
+ public function testRegisterJobClasses( $jobEntry, $setup ) {
+ $this->assertArrayEntryExists( 'wgJobClasses', $jobEntry, $setup );
+ }
+
+ /**
+ * @dataProvider specialPageDataProvider
+ */
+ public function testInitSpecialPageList( $name ) {
+
+ $vars = [];
+
+ Setup::initSpecialPageList( $vars );
+
+ $this->assertArrayHasKey(
+ $name,
+ $vars
+ );
+ }
+
+ public function testRegisterDefaultRightsUserGroupPermissions() {
+
+ $config = $this->defaultConfig;
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $config, 'Foo' );
+
+ $this->assertNotEmpty(
+ $config['wgAvailableRights']
+ );
+
+ $this->assertTrue(
+ $config['wgGroupPermissions']['sysop']['smw-admin']
+ );
+
+ $this->assertTrue(
+ $config['wgGroupPermissions']['smwcurator']['smw-patternedit']
+ );
+
+ $this->assertTrue(
+ $config['wgGroupPermissions']['smwcurator']['smw-pageedit']
+ );
+
+ $this->assertTrue(
+ $config['wgGroupPermissions']['smwadministrator']['smw-admin']
+ );
+ }
+
+ public function testNoResetOfAlreadyRegisteredGroupPermissions() {
+
+ // Avoid re-setting permissions, refs #1137
+ $localConfig['wgGroupPermissions']['sysop']['smw-admin'] = false;
+ $localConfig['wgGroupPermissions']['smwadministrator']['smw-admin'] = false;
+
+ $localConfig = array_merge(
+ $this->defaultConfig,
+ $localConfig
+ );
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $localConfig, 'Foo' );
+
+ $this->assertFalse(
+ $localConfig['wgGroupPermissions']['sysop']['smw-admin']
+ );
+
+ $this->assertFalse(
+ $localConfig['wgGroupPermissions']['smwadministrator']['smw-admin']
+ );
+
+ }
+
+ public function testRegisterParamDefinitions() {
+
+ $config = $this->defaultConfig;
+
+ $config['wgParamDefinitions']['smwformat'] = '';
+
+ $this->assertEmpty(
+ $config['wgParamDefinitions']['smwformat']
+ );
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $config, 'Foo' );
+
+ $this->assertNotEmpty(
+ $config['wgParamDefinitions']['smwformat']
+ );
+ }
+
+ public function testRegisterFooterIcon() {
+
+ $config = $this->defaultConfig;
+
+ $config['wgFooterIcons']['poweredby'] = [];
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $config, 'Foo' );
+
+ $this->assertNotEmpty(
+ $config['wgFooterIcons']['poweredby']['semanticmediawiki']
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function specialPageDataProvider() {
+
+ $specials = [
+ 'Ask',
+ 'Browse',
+ 'PageProperty',
+ 'SearchByProperty',
+ 'SMWAdmin',
+ 'Concepts',
+ 'ExportRDF',
+ 'Types',
+ 'URIResolver',
+ 'Properties',
+ 'UnusedProperties',
+ 'WantedProperties',
+ 'DeferredRequestDispatcher',
+ 'ProcessingErrorList',
+ 'PropertyLabelSimilarity'
+ ];
+
+ return $this->buildDataProvider( 'wgSpecialPages', $specials, '' );
+ }
+
+ /**
+ * @return array
+ */
+ public function jobClassesDataProvider() {
+
+ $jobs = [
+
+ 'smw.update',
+ 'smw.refresh',
+ 'smw.updateDispatcher',
+ 'smw.parserCachePurge',
+ 'smw.fulltextSearchTableUpdate',
+ 'smw.entityIdDisposer',
+ 'smw.propertyStatisticsRebuild',
+ 'smw.fulltextSearchTableRebuild',
+ 'smw.changePropagationDispatch',
+ 'smw.changePropagationUpdate',
+ 'smw.changePropagationClassUpdate',
+ 'smw.elasticIndexerRecovery',
+ 'smw.elasticFileIngest',
+
+ // Legacy
+ 'SMW\UpdateJob',
+ 'SMW\RefreshJob',
+ 'SMW\UpdateDispatcherJob',
+ 'SMW\ParserCachePurgeJob',
+ 'SMW\FulltextSearchTableUpdateJob',
+ 'SMW\EntityIdDisposerJob',
+ 'SMW\PropertyStatisticsRebuildJob',
+ 'SMW\FulltextSearchTableRebuildJob',
+ 'SMW\ChangePropagationDispatchJob',
+ 'SMW\ChangePropagationUpdateJob',
+ 'SMW\ChangePropagationClassUpdateJob',
+ 'SMWUpdateJob',
+ 'SMWRefreshJob',
+ ];
+
+ return $this->buildDataProvider( 'wgJobClasses', $jobs, '' );
+ }
+
+ /**
+ * @return array
+ */
+ public function apiModulesDataProvider() {
+
+ $modules = [
+ 'ask',
+ 'smwinfo',
+ 'askargs',
+ 'browsebysubject',
+ 'browsebyproperty'
+ ];
+
+ return $this->buildDataProvider( 'wgAPIModules', $modules, '' );
+ }
+
+ private function assertArrayEntryExists( $target, $entry, $config, $type = 'class' ) {
+
+ $config = $config + $this->defaultConfig;
+
+ $this->assertEmpty(
+ $config[$target][$entry],
+ "Asserts that {$entry} is empty"
+ );
+
+ $instance = new Setup( $this->applicationFactory );
+ $instance->init( $config, 'Foo' );
+
+ $this->assertNotEmpty( $config[$target][$entry] );
+
+ switch ( $type ) {
+ case 'class':
+ $this->assertTrue( class_exists( $config[$target][$entry] ) );
+ break;
+ case 'file':
+ $this->assertTrue( file_exists( $config[$target][$entry] ) );
+ break;
+ }
+ }
+
+ /**
+ * @return array
+ */
+ private function buildDataProvider( $id, $definitions, $default ) {
+
+ $provider = [];
+
+ foreach ( $definitions as $definition ) {
+ $provider[] = [
+ $definition,
+ [ $id => [ $definition => $default ] ],
+ ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SubobjectTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SubobjectTest.php
new file mode 100644
index 00000000..7f4307eb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/SubobjectTest.php
@@ -0,0 +1,436 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DataValueFactory;
+use SMW\Subobject;
+use SMW\Tests\Utils\UtilityFactory;
+use SMWDIBlob;
+use Title;
+
+/**
+ * @covers \SMW\Subobject
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SubobjectTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $semanticDataValidator;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->semanticDataValidator = UtilityFactory::getInstance()->newValidatorFactory()->newSemanticDataValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $title = $this->getMockBuilder( 'Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMW\Subobject',
+ new Subobject( $title )
+ );
+ }
+
+ public function testSetSemanticWithInvalidIdThrowsException() {
+
+ $instance = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $this->setExpectedException( 'InvalidArgumentException' );
+ $instance->setSemanticData( '' );
+ }
+
+ public function testSetEmptySemanticData() {
+
+ $instance = new Subobject( Title::newFromText( __METHOD__ ) );
+ $instance->setEmptyContainerForId( 'Foo' );
+
+ $this->assertInstanceOf(
+ '\Title',
+ $instance->getTitle()
+ );
+
+ $this->assertInstanceOf(
+ '\SMWContainerSemanticData',
+ $instance->getSemanticData()
+ );
+
+ $this->assertEquals(
+ $instance->getSubobjectId(),
+ $instance->getSemanticData()->getSubject()->getSubobjectName()
+ );
+ }
+
+ /**
+ * @dataProvider getDataProvider
+ */
+ public function testgetSubobjectId( array $parameters, array $expected ) {
+
+ $instance = $this->acquireInstanceForId(
+ Title::newFromText( __METHOD__ ),
+ $parameters['identifier']
+ );
+
+ if ( $expected['identifier'] !== '_' ) {
+ return $this->assertEquals( $expected['identifier'], $instance->getSubobjectId() );
+ }
+
+ $this->assertEquals(
+ $expected['identifier'],
+ substr( $instance->getSubobjectId(), 0, 1 )
+ );
+ }
+
+ /**
+ * @dataProvider getDataProvider
+ */
+ public function testGetProperty( array $parameters ) {
+
+ $instance = $this->acquireInstanceForId(
+ Title::newFromText( __METHOD__ ),
+ $parameters['identifier']
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DIProperty',
+ $instance->getProperty()
+ );
+ }
+
+ /**
+ * @dataProvider getDataProvider
+ */
+ public function testAddDataValue( array $parameters, array $expected ) {
+
+ $instance = $this->acquireInstanceForId(
+ Title::newFromText( __METHOD__ ),
+ $parameters['identifier']
+ );
+
+ foreach ( $parameters['properties'] as $property => $value ){
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByText(
+ $property,
+ $value
+ );
+
+ $instance->addDataValue( $dataValue );
+ }
+
+ $this->assertCount(
+ $expected['errors'],
+ $instance->getErrors()
+ );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ /**
+ * @dataProvider newDataValueProvider
+ */
+ public function testDataValueExaminer( array $parameters, array $expected ) {
+
+ $property = $this->getMockBuilder( '\SMW\DIProperty' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $property->expects( $this->atLeastOnce() )
+ ->method( 'findPropertyTypeID' )
+ ->will( $this->returnValue( $parameters['property']['typeId'] ) );
+
+ $property->expects( $this->atLeastOnce() )
+ ->method( 'getKey' )
+ ->will( $this->returnValue( $parameters['property']['key'] ) );
+
+ $property->expects( $this->atLeastOnce() )
+ ->method( 'getLabel' )
+ ->will( $this->returnValue( $parameters['property']['label'] ) );
+
+ $dataValue = DataValueFactory::getInstance()->newDataValueByItem(
+ $parameters['dataItem'],
+ $property
+ );
+
+ $instance = $this->acquireInstanceForId(
+ Title::newFromText( __METHOD__ ),
+ 'Foo'
+ );
+
+ $instance->addDataValue( $dataValue );
+
+ $this->assertCount( $expected['errors'], $instance->getErrors() );
+
+ $this->semanticDataValidator->assertThatPropertiesAreSet(
+ $expected,
+ $instance->getSemanticData()
+ );
+ }
+
+ public function testAddDataValueWithInvalidSemanticDataThrowsException() {
+
+ $dataValue = $this->getMockBuilder( '\SMWDataValue' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $instance = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException' );
+ $instance->addDataValue( $dataValue );
+ }
+
+ public function testGetSemanticDataInvalidSemanticDataThrowsException() {
+
+ $instance = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ $this->setExpectedException( '\SMW\Exception\SubSemanticDataException' );
+ $instance->getSemanticData();
+ }
+
+ /**
+ * @dataProvider errorProvider
+ */
+ public function testErrorHandlingOnErrors( $errors, $expected ) {
+
+ $instance = new Subobject( Title::newFromText( __METHOD__ ) );
+
+ foreach ( $errors as $error ) {
+ $instance->addError( $error );
+ }
+
+ $this->assertCount(
+ $expected,
+ $instance->getErrors()
+ );
+ }
+
+ /**
+ * @dataProvider getDataProvider
+ */
+ public function testGetContainer( array $parameters ) {
+ $instance = $this->acquireInstanceForId(
+ Title::newFromText( __METHOD__ ),
+ $parameters['identifier']
+ );
+
+ $this->assertInstanceOf(
+ '\SMWDIContainer',
+ $instance->getContainer()
+ );
+ }
+
+ public function getDataProvider() {
+
+ $provider = [];
+
+ // #0 / asserting conditions for a named identifier
+ $provider[] = [
+ [
+ 'identifier' => 'Bar',
+ 'properties' => [ 'Foo' => 'bar' ]
+ ],
+ [
+ 'errors' => 0,
+ 'identifier' => 'Bar',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Foo',
+ 'propertyValues' => 'Bar',
+ ]
+ ];
+
+ // #1 / asserting conditions for an anon identifier
+ $provider[] = [
+ [
+ 'identifier' => '',
+ 'properties' => [ 'FooBar' => 'bar Foo' ]
+ ],
+ [
+ 'errors' => 0,
+ 'identifier' => '_',
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'FooBar',
+ 'propertyValues' => 'Bar Foo',
+ ]
+ ];
+
+ // #2 / asserting conditions
+ $provider[] = [
+ [
+ 'identifier' => 'foo',
+ 'properties' => [ 9001 => 1001 ]
+ ],
+ [
+ 'errors' => 0,
+ 'identifier' => 'foo',
+ 'propertyCount' => 1,
+ 'propertyLabels' => [ 9001 ],
+ 'propertyValues' => [ 1001 ],
+ ]
+ ];
+
+ // #3
+ $provider[] = [
+ [
+ 'identifier' => 'foo bar',
+ 'properties' => [ 1001 => 9001, 'Foo' => 'Bar' ]
+ ],
+ [
+ 'errors' => 0,
+ 'identifier' => 'foo bar',
+ 'propertyCount' => 2,
+ 'propertyLabels' => [ 1001, 'Foo' ],
+ 'propertyValues' => [ 9001, 'Bar' ],
+ ]
+ ];
+
+ // #4 / asserting that a property with a leading underscore would produce an error
+ $provider[] = [
+ [
+ 'identifier' => 'bar',
+ 'properties' => [ '_FooBar' => 'bar Foo' ]
+ ],
+ [
+ 'errors' => 1,
+ 'identifier' => 'bar',
+ 'propertyCount' => 1,
+ 'strictPropertyValueMatch' => false,
+ 'propertyKeys' => [ '_ERRC' ]
+ ]
+ ];
+
+ // #5 / asserting that an inverse property would produce an error
+ $provider[] = [
+ [
+ 'identifier' => 'bar',
+ 'properties' => [ '-FooBar' => 'bar Foo' ]
+ ],
+ [
+ 'errors' => 1,
+ 'identifier' => 'bar',
+ 'propertyCount' => 1,
+ 'strictPropertyValueMatch' => false,
+ 'propertyKeys' => [ '_ERRC' ]
+ ]
+ ];
+
+ // #6 / asserting that an improper value for a _wpg property would add "Has improper value for"
+ $provider[] = [
+ [
+ 'identifier' => 'bar',
+ 'properties' => [ 'Foo' => '' ]
+ ],
+ [
+ 'identifier' => 'bar',
+ 'errors' => 1,
+ 'strictPropertyValueMatch' => false,
+ 'propertyCount' => 1,
+ 'propertyKeys' => [ '_ERRC' ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * Provides sample data for various dataItem/datValues
+ *
+ * @return array
+ */
+ public function newDataValueProvider() {
+
+ $provider = [];
+
+ // #0 Bug 49530
+ $provider[] = [
+ [
+ 'property' => [
+ 'typeId' => '_txt',
+ 'label' => 'Blob.example',
+ 'key' => 'Blob.example'
+ ],
+ 'dataItem' => new SMWDIBlob( '<a href="http://username@example.org/path">Example</a>' )
+ ],
+ [
+ 'errors' => 0,
+ 'propertyCount' => 1,
+ 'propertyLabels' => 'Blob.example',
+ 'propertyValues' => '<a href="http://username@example.org/path">Example</a>',
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return Subobject
+ */
+ private function acquireInstanceForId( Title $title, $id = '' ) {
+
+ $instance = new Subobject( $title );
+
+ if ( $id === '' && $id !== null ) {
+ $id = '_abcdef';
+ }
+
+ $instance->setEmptyContainerForId( $id );
+
+ return $instance;
+ }
+
+ public function errorProvider() {
+
+ $provider = [];
+
+ #0
+ $provider[] = [
+ [
+ 'Foo',
+ 'Foo'
+ ],
+ 1
+ ];
+
+ #1
+ $provider[] = [
+ [
+ 'Foo',
+ 'Bar'
+ ],
+ 2
+ ];
+
+ #2
+ $provider[] = [
+ [
+ [ 'Foo' => 'Bar' ],
+ [ 'Foo' => 'Bar' ],
+ ],
+ 1
+ ];
+
+ #3
+ $provider[] = [
+ [
+ [ 'Foo' => 'Bar' ],
+ [ 'Bar' => 'Foo' ],
+ ],
+ 2
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/CommonDataItemTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/CommonDataItemTest.php
new file mode 100644
index 00000000..20664d3a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/CommonDataItemTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @covers \SMWDataItem
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class CommonDataItemTest extends \PHPUnit_Framework_TestCase {
+
+ public function testSerializationToFilterSameStringRepresentation() {
+
+ $items = [];
+
+ foreach ( [ 'Foo', 'Bar', 'Foo' ] as $value ) {
+
+ $dataItem = $this->getMockBuilder( '\SMWDataItem' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $dataItem->expects( $this->any() )
+ ->method( 'getSerialization' )
+ ->will( $this->returnValue( $value ) );
+
+ $items[] = $dataItem;
+ }
+
+ $this->assertCount(
+ 2,
+ array_unique( $items )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIConceptTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIConceptTest.php
new file mode 100644
index 00000000..1570458a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIConceptTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @covers \SMW\DIConcept
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ *
+ * @license GNU GPL v2+
+ * @author mwjames
+ */
+class DIConceptTest extends DataItemTest {
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function getClass() {
+ return 'SMW\DIConcept';
+ }
+
+ /**
+ * @see DataItemTest::constructorProvider
+ *
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ return [
+ [ 'Foo', '', '', '', '' ],
+ ];
+ }
+
+ /**
+ * @test DIConcept::setCacheStatus
+ * @test DIConcept::setCacheDate
+ * @test DIConcept::setCacheCount
+ * @dataProvider conceptCacheDataProvider
+ *
+ * @since 1.9
+ *
+ * @param $status
+ * @param $date
+ * @param $count
+ */
+ public function testConceptCacheSetterGetter( $status, $date, $count ) {
+
+ $reflector = new \ReflectionClass( $this->getClass() );
+ $instance = $reflector->newInstanceArgs( [ 'Foo', '', '', '', '' ] );
+
+ $instance->setCacheStatus( $status );
+ $instance->setCacheDate( $date );
+ $instance->setCacheCount( $count );
+
+ $this->assertEquals( $status, $instance->getCacheStatus() );
+ $this->assertEquals( $date, $instance->getCacheDate() );
+ $this->assertEquals( $count, $instance->getCacheCount() );
+
+ }
+
+ /**
+ * Data provider for testing concept cache setter/getter
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function conceptCacheDataProvider() {
+ return [
+ [ 'empty', '', '' ],
+ [ 'full', '1358515326', '1000' ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIPropertyTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIPropertyTest.php
new file mode 100644
index 00000000..db82382c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIPropertyTest.php
@@ -0,0 +1,227 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\PropertyRegistry;
+use SMWDataItem as DataItem;
+
+/**
+ * @covers \SMW\DIProperty
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ * @author Nischay Nahata
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DIPropertyTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ protected function tearDown() {
+ PropertyRegistry::clear();
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testCanConstruct( $arg ) {
+
+ $this->assertInstanceOf(
+ DataItem::class,
+ new DIProperty( $arg )
+ );
+
+ $this->assertInstanceOf(
+ DIProperty::class,
+ new DIProperty( $arg )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testSerialization( $arg ) {
+ $instance = new DIProperty( $arg );
+
+ $this->assertEquals(
+ $instance,
+ $instance->doUnserialize( $instance->getSerialization() )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testInstanceEqualsItself( $arg ) {
+
+ $instance = new DIProperty( $arg );
+
+ $this->assertTrue(
+ $instance->equals( $instance )
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ */
+ public function testInstanceDoesNotEqualNyanData( $arg ) {
+
+ $instance = new DIProperty( $arg );
+
+ $this->assertFalse(
+ $instance->equals( new \SMWDIBlob( '~=[,,_,,]:3' ) )
+ );
+ }
+
+ public function constructorProvider() {
+ return [
+ [ 0 ],
+ [ 243.35353 ],
+ [ 'ohi there' ],
+ ];
+ }
+
+ public function testSetPropertyTypeIdOnUserDefinedProperty() {
+
+ $property = new DIProperty( 'SomeBlobProperty' );
+ $property->setPropertyTypeId( '_txt' );
+
+ $this->assertEquals( '_txt', $property->findPropertyTypeID() );
+ }
+
+ public function testSetPropertyTypeIdOnPredefinedProperty() {
+
+ $property = new DIProperty( '_MDAT' );
+ $property->setPropertyTypeId( '_dat' );
+
+ $this->assertEquals( '_dat', $property->findPropertyTypeID() );
+ }
+
+ public function testSetUnknownPropertyTypeIdThrowsException() {
+
+ $property = new DIProperty( 'SomeUnknownTypeIdProperty' );
+
+ $this->setExpectedException( '\SMW\Exception\DataTypeLookupException' );
+ $property->setPropertyTypeId( '_unknownTypeId' );
+ }
+
+ public function testSetPropertyTypeIdOnPredefinedPropertyThrowsException() {
+
+ $property = new DIProperty( '_MDAT' );
+
+ $this->setExpectedException( 'RuntimeException' );
+ $property->setPropertyTypeId( '_txt' );
+ }
+
+ public function testCorrectInversePrefixForPredefinedProperty() {
+
+ $property = new DIProperty( '_SOBJ', true );
+
+ $this->assertTrue(
+ $property->isInverse()
+ );
+
+ $label = $property->getLabel();
+
+ $this->assertEquals(
+ '-',
+ $label{0}
+ );
+ }
+
+ public function testUseInterwikiPrefix() {
+
+ $property = new DIProperty( 'Foo' );
+ $property->setInterwiki( 'bar' );
+
+ $this->assertEquals(
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, 'bar' ),
+ $property->getDiWikiPage()
+ );
+ }
+
+ public function testCreatePropertyFromLabelThatContainsInverseMarker() {
+
+ $property = DIProperty::newFromUserLabel( '-Foo' );
+ $property->setInterwiki( 'bar' );
+
+ $this->assertTrue(
+ $property->isInverse()
+ );
+
+ $this->assertEquals(
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, 'bar' ),
+ $property->getDiWikiPage()
+ );
+ }
+
+ public function testCreatePropertyFromLabelThatContainsLanguageMarker() {
+
+ $property = DIProperty::newFromUserLabel( '-Foo@en' );
+ $property->setInterwiki( 'bar' );
+
+ $this->assertTrue(
+ $property->isInverse()
+ );
+
+ $this->assertEquals(
+ new DIWikiPage( 'Foo', SMW_NS_PROPERTY, 'bar' ),
+ $property->getDiWikiPage()
+ );
+ }
+
+ /**
+ * @dataProvider labelProvider
+ */
+ public function testNewFromLabel( $label, $iw, $lc, $expected ) {
+
+ $property = DIProperty::newFromUserLabel( $label, $iw, $lc );
+
+ $this->assertEquals(
+ $expected,
+ $property->getKey()
+ );
+ }
+
+ public function testCanonicalRepresentation() {
+
+ $property = new DIProperty( '_MDAT' );
+
+ $this->assertEquals(
+ 'Modification date',
+ $property->getCanonicalLabel()
+ );
+
+ $this->assertEquals(
+ new DIWikiPage( 'Modification_date', SMW_NS_PROPERTY ),
+ $property->getCanonicalDiWikiPage()
+ );
+ }
+
+ public function labelProvider() {
+
+ $provider['testCreatePropertyFromLabelWithAnnotatedLangCodeToTakePrecedence'] = [
+ 'A le type@fr', '', 'es',
+ '_TYPE'
+ ];
+
+ $provider['testCreatePropertyFromLabelWithExplicitLanguageCode'] = [
+ 'Fecha de modificación', '', 'es' ,
+ '_MDAT'
+ ];
+
+ $provider['MIMEType'] = [
+ 'MIME_type', '', 'en',
+ '_MIME'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DITimeTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DITimeTest.php
new file mode 100644
index 00000000..04d732a5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DITimeTest.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMWDITime as DITime;
+
+/**
+ * @covers \SMWDITime
+ *
+ * @license GNU GPL v2+
+ * @since 2.4
+ *
+ * @author mwjames
+ */
+class DITimeTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWDITime',
+ new DITime( DITime::CM_GREGORIAN, 1970 )
+ );
+ }
+
+ public function testNewFromTimestamp() {
+
+ $instance = DITime::newFromTimestamp( '1362200400' );
+
+ $this->assertInstanceOf(
+ '\SMWDITime',
+ $instance
+ );
+ }
+
+ public function testNewFromDateTime() {
+
+ $instance = DITime::newFromDateTime(
+ new \DateTime( '2012-07-08 11:14:15.638276' )
+ );
+
+ $this->assertEquals(
+ '15.638276',
+ $instance->getSecond()
+ );
+
+ $instance = DITime::newFromDateTime(
+ new \DateTime( '1582-10-04' )
+ );
+
+ $this->assertEquals(
+ DITime::CM_JULIAN,
+ $instance->getCalendarModel()
+ );
+
+ $instance = DITime::newFromDateTime(
+ new \DateTime( '1582-10-05' )
+ );
+
+ $this->assertEquals(
+ DITime::CM_GREGORIAN,
+ $instance->getCalendarModel()
+ );
+ }
+
+ public function testDateTimeRoundTrip() {
+
+ $dateTime = new \DateTime( '2012-07-08 11:14:15.638276' );
+
+ $instance = DITime::newFromDateTime(
+ $dateTime
+ );
+
+ $this->assertEquals(
+ $dateTime,
+ $instance->asDateTime()
+ );
+ }
+
+ public function testDateTimeWithLargeMs() {
+
+ $dateTime = new \DateTime( '1300-11-02 12:03:25.888500' );
+
+ $instance = new DITime(
+ 2, 1300, 11, 02, 12, 03, 25.888499949
+ );
+
+ if ( $instance->asDateTime() != $dateTime ) {
+ $this->markTestSkipped( 'For some reason this started to fail on 5.6.19 (worked on 5.6.18)' );
+ }
+
+ $this->assertEquals(
+ $dateTime,
+ $instance->asDateTime()
+ );
+ }
+
+ public function testDateTimeWithHistoricDate() {
+
+ $dateTime = new \DateTime( '-0900-02-02 00:00:00' );
+
+ $instance = new DITime(
+ 2, -900, 02, 02
+ );
+
+ $this->assertEquals(
+ $dateTime,
+ $instance->asDateTime()
+ );
+ }
+
+ public function testDeserializationOnIncompleteFormat() {
+
+ $instance = new DITime(
+ 1, 2013, 0, 2, 0
+ );
+
+ $this->assertEquals(
+ $instance,
+ DITime::doUnserialize( '1/2013/0/2/0/' )
+ );
+ }
+
+ /**
+ * @dataProvider jdProvider
+ */
+ public function testNewFromJD( $jd, $expected ) {
+
+ $this->assertEquals(
+ DITime::doUnserialize( $expected ),
+ DITime::newFromJD( $jd )
+ );
+ }
+
+ public function testTryToDeserializeOnNonNumericElementsThrowsException() {
+
+ $this->setExpectedException( '\SMW\Exception\DataItemException' );
+ DITime::doUnserialize( '1/2013/0/2/0/foo' );
+ }
+
+ public function testTryToDeserializeOnInvalidCountOfElementsThrowsException() {
+
+ $this->setExpectedException( '\SMW\Exception\DataItemException' );
+ DITime::doUnserialize( '1' );
+ }
+
+ public function jdProvider() {
+
+ $provider[] = [
+ '2488345.500000',
+ '1/2100/10/04'
+ ];
+
+ $provider[] = [
+ '2488346.0804977',
+ '1/2100/10/4/13/55/55'
+ ];
+
+ $provider[] = [
+ '1',
+ '2/-4713/01/02/12'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIWikiPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIWikiPageTest.php
new file mode 100644
index 00000000..fa5fc026
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DIWikiPageTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIWikiPage;
+
+/**
+ * @covers \SMW\DIWikiPage
+ * @covers SMWDataItem
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DIWikiPageTest extends DataItemTest {
+
+ /**
+ * @see DataItemTest::getClass
+ *
+ * @since 1.9
+ *
+ * @return string
+ */
+ public function getClass() {
+ return 'SMW\DIWikiPage';
+ }
+
+ /**
+ * @see DataItemTest::constructorProvider
+ *
+ * @since 1.9
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ return [
+ [ 'Foo', NS_MAIN, '' ],
+ [ 'Foo_Bar', NS_MAIN, '' ],
+ [ 'Foo_Bar_Baz', NS_MAIN, '', 'spam' ],
+ ];
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testGetTitleAndNewFromTitleRoundrtip( DIWikiPage $di ) {
+ $newDi = DIWikiPage::newFromTitle( $di->getTitle() );
+ $this->assertTrue( $newDi->equals( $di ) );
+ }
+
+ /**
+ * @dataProvider sortKeyProvider
+ */
+ public function testSortKeyRoundtrip( $title, $sortkey, $expected ) {
+
+ $instance = new DIWikiPage( $title, NS_MAIN );
+
+ $instance->setSortKey( $sortkey );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getSortKey()
+ );
+ }
+
+ public function testDoUnserialize() {
+
+ $expected = new DIWikiPage( 'Foo', 0 , '', '' );
+
+ $this->assertEquals(
+ $expected,
+ DIWikiPage::doUnserialize( 'Foo#0##' )
+ );
+
+ $this->assertEquals(
+ $expected,
+ DIWikiPage::doUnserialize( 'Foo#0##' )
+ );
+ }
+
+ public function sortKeyProvider() {
+
+ $provider[] = [
+ 'Some_title',
+ null,
+ 'Some title'
+ ];
+
+ $provider[] = [
+ 'Some_title',
+ '',
+ 'Some title'
+ ];
+
+ $provider[] = [
+ 'Some_title',
+ 'abc',
+ 'abc'
+ ];
+
+ $provider[] = [
+ 'Some_title',
+ 'abc_def',
+ 'abc def'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BlobTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BlobTest.php
new file mode 100644
index 00000000..432d8580
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BlobTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @covers SMWDIBlob
+ * @covers SMWDataItem
+ *
+ * @file
+ * @since 1.8
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ *
+ * @author Nischay Nahata
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DIBlobTest extends DataItemTest {
+
+ /**
+ * @see DataItemTest::getClass
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function getClass() {
+ return '\SMWDIBlob';
+ }
+
+ /**
+ * @see DataItemTest::constructorProvider
+ *
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ return [
+ [ 'I love Semantic MediaWiki' ],
+ [ 'It is open source' ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BoolTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BoolTest.php
new file mode 100644
index 00000000..fee4ad55
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_BoolTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @covers SMWDIBoolean
+ * @covers SMWDataItem
+ *
+ * @file
+ * @since 1.8
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ *
+ * @author Nischay Nahata
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWDIBooleanTest extends DataItemTest {
+
+ /**
+ * @see DataItemTest::getClass
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function getClass() {
+ return '\SMWDIBoolean';
+ }
+
+ /**
+ * @see DataItemTest::constructorProvider
+ *
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ return [
+ [ false ],
+ [ true ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_GeoCoordTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_GeoCoordTest.php
new file mode 100644
index 00000000..5beeca4f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_GeoCoordTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Exception\DataItemException;
+
+/**
+ * @covers SMWDIGeoCoord
+ * @covers SMWDataItem
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWDIGeoCoordTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ public function testConstructorWithArrayArgumentForm() {
+ $coordinate = new \SMWDIGeoCoord( [ 'lat' => 13.37, 'lon' => 42.42 ] );
+
+ $this->assertSame( 13.37, $coordinate->getLatitude() );
+ $this->assertSame( 42.42, $coordinate->getLongitude() );
+ }
+
+ public function testConstructorWithMultipleArgumentsForm() {
+ $coordinate = new \SMWDIGeoCoord( 13.37, 42.42 );
+
+ $this->assertSame( 13.37, $coordinate->getLatitude() );
+ $this->assertSame( 42.42, $coordinate->getLongitude() );
+ }
+
+ public function testWhenConstructingWithIntegers_gettersReturnFloats() {
+ $coordinate = new \SMWDIGeoCoord( 13, 42 );
+
+ $this->assertSame( 13.0, $coordinate->getLatitude() );
+ $this->assertSame( 42.0, $coordinate->getLongitude() );
+ }
+
+ public function testWhenOnlyProvidingLatitudeArgument_constructorThrowsException() {
+ $this->setExpectedException( DataItemException::class );
+ new \SMWDIGeoCoord( 13 );
+ }
+
+ public function testWhenProvidingNonNumericalArgument_constructorThrowsException() {
+ $this->setExpectedException( DataItemException::class );
+ new \SMWDIGeoCoord( 13, null );
+ }
+
+ public function testWhenProvidingArrayWithNonNumericalArgument_constructorThrowsException() {
+ $this->setExpectedException( DataItemException::class );
+ new \SMWDIGeoCoord( [ 'lat' => null, 'lon' => 42.42 ] );
+ }
+
+ public function testObjectEqualsItself() {
+ $coordinate = new \SMWDIGeoCoord( 13, 42 );
+ $this->assertTrue( $coordinate->equals( $coordinate ) );
+ }
+
+ public function testObjectEqualsDifferentInstancesWithEqualValues() {
+ $coordinate = new \SMWDIGeoCoord( 13, 42 );
+ $this->assertTrue( $coordinate->equals( new \SMWDIGeoCoord( 13.0, 42.0 ) ) );
+ }
+
+ public function testObjectDoesNotEqualInstancesWithDifferentValues() {
+ $coordinate = new \SMWDIGeoCoord( 13, 42 );
+ $this->assertFalse( $coordinate->equals( new \SMWDIGeoCoord( 1, 2 ) ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_NumberTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_NumberTest.php
new file mode 100644
index 00000000..807414c3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DI_NumberTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace SMW\Tests;
+
+/**
+ * @covers SMWDINumber
+ * @covers SMWDataItem
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ * @group SMWDINumberTest
+ *
+ * @author Nischay Nahata
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class SMWDINumberTest extends DataItemTest {
+
+ /**
+ * @see DataItemTest::getClass
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public function getClass() {
+ return '\SMWDINumber';
+ }
+
+ /**
+ * @see DataItemTest::constructorProvider
+ *
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function constructorProvider() {
+ return [
+ [ 0 ],
+ [ 243.35353 ],
+ ];
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DataItemTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DataItemTest.php
new file mode 100644
index 00000000..a41629b2
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/dataitems/DataItemTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMWDataItem;
+
+/**
+ * Base class for SMW\DataItem tests.
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group SMWDataItems
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class DataItemTest extends MwDBaseUnitTestCase {
+
+ /**
+ * Returns the name of the \SMW\DataItem deriving class this test tests.
+ *
+ * @since 1.8
+ *
+ * @return string
+ */
+ public abstract function getClass();
+
+ /**
+ * @since 1.8
+ *
+ * @return array
+ */
+ public abstract function constructorProvider();
+
+ /**
+ * Creates and returns a new instance of the data item.
+ *
+ * @since 1.8
+ *
+ * @return SMWDataItem
+ */
+ public function newInstance() {
+ $reflector = new \ReflectionClass( $this->getClass() );
+ $args = func_get_args();
+ $instance = $reflector->newInstanceArgs( $args );
+ return $instance;
+ }
+
+ /**
+ * @since 1.8
+ *
+ * @return array
+ */
+ public function instanceProvider() {
+ $phpFails = [ $this, 'newInstance' ];
+
+ return array_map(
+ function( array $args ) use ( $phpFails ) {
+ return [ call_user_func_array( $phpFails, $args ) ];
+ },
+ $this->constructorProvider()
+ );
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ *
+ * @since 1.8
+ */
+ public function testConstructor() {
+ $dataItem = call_user_func_array(
+ [ $this, 'newInstance' ],
+ func_get_args()
+ );
+
+ $this->assertInstanceOf( '\SMWDataItem', $dataItem );
+ $this->assertInstanceOf( $this->getClass(), $dataItem );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @since 1.8
+ *
+ * @param \SMWDataItem $dataItem
+ */
+ public function testSerialization( \SMWDataItem $dataItem ) {
+ $class = $this->getClass();
+
+ $this->assertEquals(
+ $dataItem,
+ $class::doUnserialize( $dataItem->getSerialization() )
+ );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testInstanceEqualsItself( SMWDataItem $di ) {
+ $this->assertTrue( $di->equals( $di ) );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ */
+ public function testInstanceDoesNotEqualNyanData( SMWDataItem $di ) {
+ $this->assertFalse( $di->equals( new \SMWDIBlob( '~=[,,_,,]:3' ) ) );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExpDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExpDataTest.php
new file mode 100644
index 00000000..42120707
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExpDataTest.php
@@ -0,0 +1,143 @@
+<?php
+
+namespace SMW\Tests\Exporter;
+
+use SMW\Exporter\Element\ExpLiteral;
+use SMW\Exporter\Element\ExpNsResource;
+use SMWExpData as ExpData;
+
+/**
+ * @covers \SMWExpData
+ *
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 2.2
+ *
+ * @author mwjames
+ */
+class ExpDataTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstructor() {
+
+ $expNsResource = $this->getMockBuilder( '\SMW\Exporter\Element\ExpNsResource' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertInstanceOf(
+ '\SMWExpData',
+ new ExpData( $expNsResource )
+ );
+ }
+
+ /**
+ * @dataProvider expDataHashProvider
+ */
+ public function testGetHash( $expData, $expected ) {
+
+ $this->assertEquals(
+ $expected,
+ $expData->getHash()
+ );
+ }
+
+ public function expDataHashProvider() {
+
+ #0
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $provider[] = [
+ $expData,
+ '4dc04c87e9660854a5609ff132175fd5'
+ ];
+
+ #1
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $provider[] = [
+ $expData,
+ '5702e5e8c6145aaf8d89840a4a3b18c2'
+ ];
+
+ #2
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Bar', 'Foo' )
+ );
+
+ $provider[] = [
+ $expData,
+ '13edcedd007979f5638fbc958f0cdaf8'
+ ];
+
+ #3 Same as 2 but different sorting/same hash
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Bar', 'Foo' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $provider[] = [
+ $expData,
+ '13edcedd007979f5638fbc958f0cdaf8'
+ ];
+
+ #4 Nesting
+ $expDataLevel2 = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expDataLevel2->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData = new ExpData(
+ new ExpNsResource( 'Foo', 'Bar', 'Mo', null )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ new ExpLiteral( 'Foo', 'Bar' )
+ );
+
+ $expData->addPropertyObjectValue(
+ new ExpNsResource( 'Li', 'La', 'Lu', null ),
+ $expDataLevel2
+ );
+
+ $provider[] = [
+ $expData,
+ 'e684e7640a201d2d33e035aaa866c1ac'
+ ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExportSemanticDataTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExportSemanticDataTest.php
new file mode 100644
index 00000000..5e49ca3a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/ExportSemanticDataTest.php
@@ -0,0 +1,344 @@
+<?php
+
+namespace SMW\Tests\Export;
+
+use SMW\DataValueFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Exporter\Escaper;
+use SMW\Subobject;
+use SMW\Tests\Utils\Fixtures\FixturesProvider;
+use SMW\Tests\Utils\SemanticDataFactory;
+use SMW\Tests\Utils\Validators\ExportDataValidator;
+use SMWExpNsResource as ExpNsResource;
+use SMWExporter as Exporter;
+
+/**
+ * @covers \SMWExporter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author mwjames
+ */
+class ExportSemanticDataTest extends \PHPUnit_Framework_TestCase {
+
+ private $semanticDataFactory;
+ private $dataValueFactory;
+ private $exportDataValidator;
+ private $fixturesProvider;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->dataValueFactory = DataValueFactory::getInstance();
+ $this->semanticDataFactory = new SemanticDataFactory();
+ $this->exportDataValidator = new ExportDataValidator();
+
+ $this->fixturesProvider = new FixturesProvider();
+ }
+
+ public function testExportRedirect() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $redirectProperty = new DIProperty( '_REDI' );
+ $redirectTarget = new DIWikiPage( 'FooRedirectTarget', NS_MAIN, '' );
+
+ $semanticData->addPropertyObjectValue(
+ $redirectProperty,
+ DIWikiPage::newFromTitle( $redirectTarget->getTitle(), '__red' )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'swivt', 'redirectsTo' ) )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'owl', 'sameAs' ) )
+ );
+
+ $expectedResourceElement = new ExpNsResource(
+ 'FooRedirectTarget',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki',
+ $redirectTarget
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResourceElement,
+ Exporter::getInstance()->getSpecialNsResource( 'owl', 'sameAs' ),
+ $exportData
+ );
+ }
+
+ public function testExportPageWithNumericProperty() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $property = new DIProperty( '123' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( '345', NS_MAIN )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $expectedProperty = new ExpNsResource(
+ Escaper::encodePage( $property->getDiWikiPage() ),
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki',
+ new DIWikiPage( '123', SMW_NS_PROPERTY )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( $expectedProperty )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsProperty(
+ $expectedProperty,
+ $exportData
+ );
+
+ $expectedResourceElement = new ExpNsResource(
+ '345',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki',
+ new DIWikiPage( '345', NS_MAIN )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResourceElement,
+ $expectedProperty,
+ $exportData
+ );
+ }
+
+ public function testExportPageWithNonNumericProperty() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $property = new DIProperty( 'A123' );
+
+ $semanticData->addPropertyObjectValue(
+ $property,
+ new DIWikiPage( '345', NS_MAIN )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $expectedProperty = new ExpNsResource(
+ 'A123',
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property',
+ new DIWikiPage( 'A123', SMW_NS_PROPERTY )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( $expectedProperty )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsProperty(
+ $expectedProperty,
+ $exportData
+ );
+
+ $expectedResource = new ExpNsResource(
+ '345',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki',
+ new DIWikiPage( '345', NS_MAIN )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResource,
+ $expectedProperty,
+ $exportData
+ );
+ }
+
+ public function testExportSubproperty() {
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( 'SomeSubproperty', SMW_NS_PROPERTY ) )
+ ->newEmptySemanticData();
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByProperty( new DIProperty( '_SUBP' ), 'SomeTopProperty' )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'rdfs', 'subPropertyOf' ) )
+ );
+
+ $expectedResourceElement = new ExpNsResource(
+ 'SomeTopProperty',
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property',
+ new DIWikiPage( 'SomeTopProperty', SMW_NS_PROPERTY )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResourceElement,
+ Exporter::getInstance()->getSpecialNsResource( 'rdfs', 'subPropertyOf' ),
+ $exportData
+ );
+ }
+
+ public function testExportCategory() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByProperty( new DIProperty( '_INST' ), 'SomeCategory' )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $this->assertCount(
+ 2,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ) )
+ );
+
+ $expectedResourceElement = new ExpNsResource(
+ 'SomeCategory',
+ Exporter::getInstance()->getNamespaceUri( 'category' ),
+ 'category',
+ new DIWikiPage( 'SomeCategory', NS_CATEGORY )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResourceElement,
+ Exporter::getInstance()->getSpecialNsResource( 'rdf', 'type' ),
+ $exportData
+ );
+ }
+
+ public function testExportSubcategory() {
+
+ $semanticData = $this->semanticDataFactory
+ ->setSubject( new DIWikiPage( 'SomeSubcategory', NS_CATEGORY ) )
+ ->newEmptySemanticData();
+
+ $semanticData->addDataValue(
+ $this->dataValueFactory->newDataValueByProperty( new DIProperty( '_SUBC' ), 'SomeTopCategory' )
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'rdfs', 'subClassOf' ) )
+ );
+
+ $expectedResourceElement = new ExpNsResource(
+ 'SomeTopCategory',
+ Exporter::getInstance()->getNamespaceUri( 'category' ),
+ 'category',
+ new DIWikiPage( 'SomeTopCategory', NS_CATEGORY )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResourceElement,
+ Exporter::getInstance()->getSpecialNsResource( 'rdfs', 'subClassOf' ),
+ $exportData
+ );
+ }
+
+ public function testExportSubobject() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $subobject = new Subobject( $semanticData->getSubject()->getTitle() );
+ $subobject->setEmptyContainerForId( 'Foo' );
+
+ $semanticData->addPropertyObjectValue(
+ $subobject->getProperty(),
+ $subobject->getContainer()
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData( $semanticData );
+
+ $expectedProperty = new ExpNsResource(
+ $this->transformPropertyLabelToAuxiliary( $subobject->getProperty() ),
+ Exporter::getInstance()->getNamespaceUri( 'property' ),
+ 'property',
+ new DIWikiPage( 'Has_subobject', SMW_NS_PROPERTY )
+ );
+
+ $this->assertTrue(
+ Exporter::getInstance()->hasHelperExpElement( $subobject->getProperty() )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( $expectedProperty )
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsProperty(
+ $expectedProperty,
+ $exportData
+ );
+
+ $expectedResource = new ExpNsResource(
+ Escaper::encodePage( $subobject->getSemanticData()->getSubject() ) . '-23' . 'Foo',
+ Exporter::getInstance()->getNamespaceUri( 'wiki' ),
+ 'wiki',
+ $subobject->getSemanticData()->getSubject()
+ );
+
+ $this->exportDataValidator->assertThatExportDataContainsResource(
+ $expectedResource,
+ $expectedProperty,
+ $exportData
+ );
+ }
+
+ public function testExportSubSemanticData() {
+
+ $semanticData = $this->semanticDataFactory->newEmptySemanticData( __METHOD__ );
+
+ $factsheet = $this->fixturesProvider->getFactsheet( 'berlin' );
+ $factsheet->setTargetSubject( $semanticData->getSubject() );
+
+ $demographicsSubobject = $factsheet->getDemographics();
+
+ $semanticData->addPropertyObjectValue(
+ $demographicsSubobject->getProperty(),
+ $demographicsSubobject->getContainer()
+ );
+
+ $exportData = Exporter::getInstance()->makeExportData(
+ $semanticData->findSubSemanticData( $demographicsSubobject->getSubobjectId() )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialPropertyResource( '_SKEY' ) )
+ );
+
+ $this->assertCount(
+ 1,
+ $exportData->getValues( Exporter::getInstance()->getSpecialNsResource( 'swivt', 'wikiNamespace' ) )
+ );
+ }
+
+ private function transformPropertyLabelToAuxiliary( DIProperty $property ) {
+ return str_replace( ' ', '_', $property->getLabel() );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/SMWExporterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/SMWExporterTest.php
new file mode 100644
index 00000000..ef5cd36b
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/export/SMWExporterTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIWikiPage;
+use SMWDataItem as DataItem;
+use SMWDIBlob as DIBlob;
+use SMWDIBoolean as DIBoolean;
+use SMWDIConcept as DIConcept;
+use SMWDINumber as DINumber;
+use SMWExporter as Exporter;
+use SMWExpResource as ExpResource;
+
+/**
+ * @covers \SMWExporter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SMWExporterTest extends \PHPUnit_Framework_TestCase {
+
+ // @see #795
+ public function testExportDataForPropertyPage() {
+
+ $propertyPage = new DIWikiPage( 'Foo', SMW_NS_PROPERTY );
+
+ $expData = Exporter::getInstance()->makeExportDataForSubject( $propertyPage );
+
+ $this->assertInstanceOf(
+ '\SMWExpData',
+ $expData
+ );
+
+ $this->assertInstanceOf(
+ '\SMWExpNsResource',
+ $expData->getSubject()
+ );
+ }
+
+ /**
+ * @dataProvider dataItemExpElementProvider
+ */
+ public function testGetDataItemExpElement( DataItem $dataItem, $instance ) {
+
+ if ( $instance === null ) {
+ return $this->assertNull( Exporter::getInstance()->getDataItemExpElement( $dataItem ) );
+ }
+
+ $this->assertInstanceOf(
+ $instance,
+ Exporter::getInstance()->getDataItemExpElement( $dataItem )
+ );
+ }
+
+ /**
+ * @dataProvider uriDataItemProvider
+ * #378
+ */
+ public function testFindDataItemForExpElement( $uri, $expectedDataItem ) {
+
+ $uri = Exporter::getInstance()->getNamespaceUri( 'wiki' ) . $uri;
+
+ $this->assertEquals(
+ $expectedDataItem,
+ Exporter::getInstance()->findDataItemForExpElement( new ExpResource( $uri ) )
+ );
+ }
+
+ public function dataItemExpElementProvider() {
+
+ // #0 (bug 56643)
+ $provider[] = [ new DINumber( 9001 ), 'SMWExpElement' ];
+
+ $provider[] = [ new DIBlob( 'foo' ), 'SMWExpElement' ];
+ $provider[] = [ new DIBoolean( true ), 'SMWExpElement' ];
+
+ $provider[] = [ new DIConcept( 'Foo', '', '', '', '' ), 'SMWExpData' ];
+
+ return $provider;
+ }
+
+ public function uriDataItemProvider() {
+
+ $provider[] = [ 'Foo', new DIWikiPage( 'Foo', NS_MAIN, '', '' ) ];
+ $provider[] = [ 'Foo#Bar', new DIWikiPage( 'Foo', NS_MAIN, '', 'Bar' ) ];
+ $provider[] = [ 'Foo#Bar#Oooo', new DIWikiPage( 'Foo', NS_MAIN, '', 'Bar#Oooo' ) ];
+ $provider[] = [ 'Property:Foo', new DIWikiPage( 'Foo', SMW_NS_PROPERTY, '', '' ) ];
+ $provider[] = [ 'Concept:Foo', new DIWikiPage( 'Foo', SMW_NS_CONCEPT, '', '' ) ];
+ $provider[] = [ 'Unknown:Foo', new DIWikiPage( 'Unknown:Foo', NS_MAIN, '', '' ) ];
+ $provider[] = [ 'Unknown:Foo#Bar', new DIWikiPage( 'Unknown:Foo', NS_MAIN, '', 'Bar' ) ];
+ $provider[] = [ 'Property:Foo#Bar', new DIWikiPage( 'Foo', SMW_NS_PROPERTY, '', 'Bar' ) ];
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/formatters/MessageFormatterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/formatters/MessageFormatterTest.php
new file mode 100644
index 00000000..02dfdef1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/formatters/MessageFormatterTest.php
@@ -0,0 +1,282 @@
+<?php
+
+namespace SMW\Test;
+
+use Message;
+use ReflectionClass;
+use SMW\MessageFormatter;
+
+/**
+ * Tests for the MessageFormatter class
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\MessageFormatter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class MessageFormatterTest extends SemanticMediaWikiTestCase {
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string
+ */
+ public function getClass() {
+ return '\SMW\MessageFormatter';
+ }
+
+ /**
+ * Helper method that returns an MessageFormatter instance
+ *
+ * @since 1.9
+ *
+ * @return MessageFormatter
+ */
+ private function getInstance() {
+ return new MessageFormatter( $this->getLanguage() );
+ }
+
+ /**
+ * @test MessageFormatter::__construct
+ * @dataProvider getDataProvider
+ *
+ * @since 1.9
+ */
+ public function testConstructor() {
+ $instance = $this->getInstance();
+ $this->assertInstanceOf( $this->getClass(), $instance );
+ }
+
+ /**
+ * @test MessageFormatter::newFromArray
+ * @test MessageFormatter::setType
+ * @test MessageFormatter::getHtml
+ * @dataProvider getDataProvider
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ */
+ public function testNewFromArray( array $messages ) {
+ $instance = MessageFormatter::newFromArray(
+ $this->getLanguage(),
+ $messages
+ );
+
+ $instance->setType( 'error' );
+ $this->assertInternalType( 'string', $instance->getHtml() );
+
+ $instance->setType( 'warning' );
+ $this->assertInternalType( 'string', $instance->getHtml() );
+
+ $instance->setType( 'info' );
+ $this->assertInternalType( 'string', $instance->getHtml() );
+
+ }
+
+ /**
+ * @test MessageFormatter::addFromKey
+ * @test MessageFormatter::getMessages
+ *
+ * @since 1.9
+ */
+ public function testAddFromKey() {
+ $instance = $this->getInstance();
+ $param = '1001';
+
+ $instance->addFromKey( 'Foo', $param )
+ ->addFromKey( 'Bar', $param )
+ ->addFromKey( 'Foo', $param );
+
+ $messages = $instance->getMessages();
+
+ // Returns count of existing with duplicates, elimination is
+ // applied only during output (getHtml/getPlain)
+ $this->assertCount( 3, $messages );
+
+ foreach ( $messages as $msg ) {
+ $this->assertInstanceOf( '\Message', $msg );
+
+ // getParams() only got added in MW 1.21
+ if ( method_exists( $msg, 'getParams' ) ) {
+ foreach ( $msg->getParams() as $result ) {
+ $this->assertEquals( $param, $result );
+ }
+ }
+ }
+ }
+
+ /**
+ * @test MessageFormatter::setLanguage
+ * @test MessageFormatter::getPlain
+ *
+ * @since 1.9
+ */
+ public function testSetLanguage() {
+ $key = 'properties';
+ $msg = new Message( $key );
+ $instance = $this->getInstance();
+
+ $instance->addFromKey( $key );
+ $instance->setLanguage( $this->getLanguage( 'zh-tw' ) );
+
+ $this->assertEquals(
+ $msg->inLanguage( $this->getLanguage( 'zh-tw' ) )->text(),
+ $instance->getPlain()
+ );
+
+ $instance->clear();
+ $this->assertEmpty( $instance->getPlain() );
+
+ }
+
+ /**
+ * @test MessageFormatter::format
+ * @dataProvider getDataProvider
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ * @param integer $count
+ */
+ public function testFormat( array $messages, $count ) {
+ $instance = $this->getInstance();
+ $instance->addFromArray( $messages );
+
+ // Access protected method
+ $reflection = new ReflectionClass( $this->getClass() );
+ $method = $reflection->getMethod( 'doFormat' );
+ $method->setAccessible( true );
+
+ // Test array normalization and deletion of duplicates
+ $result = $method->invoke( $instance, $instance->getMessages() );
+ $this->assertCount( $count, $result );
+
+ }
+
+ /**
+ * @test MessageFormatter::getHtml
+ * @dataProvider getDataProvider
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ */
+ public function testGetHtml( array $messages ) {
+ $instance = $this->getInstance();
+ $instance->addFromArray( $messages );
+
+ $this->assertInternalType( 'string', $instance->getHtml() );
+ }
+
+ /**
+ * @test MessageFormatter::getPlain
+ * @dataProvider getDataProvider
+ *
+ * @since 1.9
+ *
+ * @param array $messages
+ */
+ public function testGetPlain( array $messages ) {
+ $instance = $this->getInstance();
+ $instance->addFromArray( $messages );
+
+ $this->assertInternalType( 'string', $instance->getPlain() );
+ }
+
+ /**
+ * @test MessageFormatter::escape
+ * @test MessageFormatter::getPlain
+ *
+ * @since 1.9
+ */
+ public function testEscapedUnescaped() {
+ $instance = $this->getInstance();
+ $instance->addFromArray( [ '<Foo>' ] );
+
+ $this->assertEquals( '&lt;Foo&gt;', $instance->escape( true )->getPlain() );
+ $this->assertEquals( '<Foo>', $instance->escape( false )->getPlain() );
+
+ }
+
+ /**
+ * Message from different sources could have different depth therefore
+ * objects need to be resolved recursively in order to ensure a 1-n array
+ *
+ */
+ public function getDataProvider() {
+ return [
+
+ // #0 Empty array
+ [ [], 0 ],
+
+ // #1 Simple string elements 5 elements (one duplicate) = 4
+ [
+ [
+ 'Foo', 'Bar', [ 'FooBar', [ 'barFoo', 'Foo' ] ]
+ ],
+ 4
+ ],
+
+ // #2 A duplicate Message object = 1
+ [
+ [
+ new Message( 'smw_iq_disabled' ),
+ new Message( 'smw_iq_disabled' )
+ ],
+ 1
+ ],
+
+ // #3 Different Message objects
+ [
+ [
+ new Message( 'smw_iq_disabled' ),
+ new Message( 'smw_multiple_concepts' )
+ ],
+ 2
+ ],
+
+ // #4 Invoked MessageFormatter object (recursive test)
+ [
+ [
+ new Message( 'smw_iq_disabled' ),
+ [ new Message( 'smw_iq_disabled' ),
+ new Message( 'smw_multiple_concepts' )
+ ]
+ ],
+ 2
+ ],
+
+ // #5 Combine different objects (recursive test) containing 7 messages
+ // where two of them are duplicates resulting in 5 objects
+ [
+ [
+ new Message( 'smw_iq_disabled' ),
+ new Message( 'smw_multiple_concepts' ),
+ [
+ new Message( 'smw_iq_disabled' ),
+ 'Foo'
+ ],
+ [
+ [
+ new Message( 'smw_no_concept_namespace' ),
+ new Message( 'foo' ),
+ 'Foo'
+ ]
+ ]
+ ],
+ 5
+ ],
+ ];
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/PropertiesQueryPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/PropertiesQueryPageTest.php
new file mode 100644
index 00000000..eeeaebc9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/PropertiesQueryPageTest.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\ArrayAccessor;
+use SMW\DataItemFactory;
+use SMW\PropertiesQueryPage;
+use SMW\Settings;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\PropertiesQueryPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class PropertiesQueryPageTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $skin;
+ private $settings;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->settings = Settings::newFromArray( [
+ 'smwgPDefaultType' => '_wpg',
+ 'smwgPropertyLowUsageThreshold' => 5,
+ 'smwgPropertyZeroCountDisplay' => true
+ ] );
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\PropertiesQueryPage',
+ new PropertiesQueryPage( $this->store, $this->settings )
+ );
+ }
+
+ public function testFormatResultDIError() {
+
+ $error = $this->dataItemFactory->newDIError( 'Foo');
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $error, null ]
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $result
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $result
+ );
+ }
+
+ public function testInvalidResultThrowsException() {
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $this->setExpectedException( '\SMW\Exception\PropertyNotFoundException' );
+ $instance->formatResult( $this->skin, null );
+ }
+
+ public function testFormatPropertyItemOnUserDefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, 42 ]
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemOnPredefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_MDAT' );
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, 42 ]
+ );
+
+ $this->assertContains(
+ '42',
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemZeroDisplay() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $this->settings->set(
+ 'smwgPropertyZeroCountDisplay',
+ false
+ );
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, 0 ]
+ );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemLowUsageThreshold() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+ $count = 42;
+
+ $this->settings->set(
+ 'smwgPropertyLowUsageThreshold',
+ $count + 1
+ );
+
+ $this->settings->set(
+ 'smwgPDefaultType',
+ '_wpg'
+ );
+
+ $instance = new PropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, $count ]
+ );
+
+ $this->assertContains(
+ '42',
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/QueryPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/QueryPageTest.php
new file mode 100644
index 00000000..544f18e7
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/QueryPageTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace SMW\Test;
+
+use ReflectionClass;
+use SMW\Tests\Utils\Mock\MockSuperUser;
+use Title;
+
+/**
+ * @covers \SMW\QueryPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class QueryPageTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * Helper method that returns a QueryPage object
+ *
+ * @since 1.9
+ *
+ * @param $result
+ *
+ * @return QueryPage
+ */
+ private function newInstance( $search = '' ) {
+
+ $queryPage = $this->getMockBuilder( '\SMW\QueryPage' )
+ ->setMethods( [ 'getResults', 'formatResult' ] )
+ ->getMock();
+
+ $context = $this->newContext( [ 'property' => $search ] );
+ $context->setTitle( Title::newFromText( __METHOD__ ) );
+
+ $queryPage->setContext( $context );
+
+ return $queryPage;
+ }
+
+ /**
+ * @test QueryPage::__construct
+ *
+ * @since 1.9
+ */
+ public function testConstructor() {
+ $this->assertInstanceOf( '\SMW\QueryPage', $this->newInstance() );
+ }
+
+ /**
+ * @test QueryPage::linkParameters
+ * @dataProvider linkParametersDataProvider
+ *
+ * @since 1.9
+ *
+ * @param $test
+ * @param $expected
+ */
+ public function testLinkParameters( $test, $expected ) {
+
+ $search = __METHOD__;
+ $result = $this->newInstance( $test )->linkParameters();
+
+ $this->assertInternalType( 'array', $result );
+ $this->assertEquals( $expected, $result );
+
+ }
+
+ /**
+ * @test QueryPage::getSearchForm
+ *
+ * @since 1.9
+ */
+ public function testGetSearchForm() {
+
+ $search = __METHOD__;
+ $instance = $this->newInstance();
+
+ $reflector = new ReflectionClass( '\SMW\QueryPage' );
+ $selectOptions = $reflector->getProperty( 'selectOptions' );
+ $selectOptions->setAccessible( true );
+ $selectOptions->setValue( $instance, [
+ 'offset' => 1,
+ 'limit' => 2,
+ 'end' => 5,
+ 'count' => 4
+ ] );
+
+ $result = $instance->getSearchForm( $search );
+
+ $matcher = [
+ 'tag' => 'form',
+ 'descendant' => [
+ 'tag' => 'input',
+ 'attributes' => [ 'name' => 'property', 'value' => $search ]
+ ]
+ ];
+
+ $this->assertInternalType( 'string', $result );
+
+ // https://github.com/sebastianbergmann/phpunit/issues/1380
+ // $this->assertTag( $matcher, $result );
+ $this->assertContains( $search, $result );
+ }
+
+ /**
+ * Provides sample data to be tested
+ *
+ * @return array
+ */
+ public function linkParametersDataProvider() {
+ $param = __METHOD__;
+
+ return [
+ [ '' , [] ],
+ [ null , [] ],
+ [ $param , [ 'property' => $param ] ],
+ [ "[{$param}]" , [ 'property' => "[{$param}]" ] ],
+ [ "[&{$param}...]" , [ 'property' => "[&{$param}...]" ] ]
+ ];
+ }
+
+ private function newContext( $request = [] ) {
+
+ $context = new \RequestContext();
+
+ if ( $request instanceof \WebRequest ) {
+ $context->setRequest( $request );
+ } else {
+ $context->setRequest( new \FauxRequest( $request, true ) );
+ }
+
+ $context->setUser( new MockSuperUser() );
+
+ return $context;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/UnusedPropertiesQueryPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/UnusedPropertiesQueryPageTest.php
new file mode 100644
index 00000000..5301f120
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/UnusedPropertiesQueryPageTest.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\DataItemFactory;
+use SMW\Settings;
+use SMW\Tests\TestEnvironment;
+use SMW\UnusedPropertiesQueryPage;
+use SMW\Tests\PHPUnitCompat;
+
+/**
+ * @covers \SMW\UnusedPropertiesQueryPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class UnusedPropertiesQueryPageTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ private $store;
+ private $skin;
+ private $settings;
+ private $dataItemFactory;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container = $this->getMockBuilder( '\Onoi\BlobStore\Container' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $blobStore = $this->getMockBuilder( '\Onoi\BlobStore\BlobStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $blobStore->expects( $this->any() )
+ ->method( 'read' )
+ ->will( $this->returnValue( $container ) );
+
+ $cachedPropertyValuesPrefetcher = $this->getMockBuilder( '\SMW\CachedPropertyValuesPrefetcher' )
+ ->setConstructorArgs( [ $this->store, $blobStore ] )
+ ->setMethods( null )
+ ->getMock();
+
+ $this->testEnvironment->registerObject( 'CachedPropertyValuesPrefetcher', $cachedPropertyValuesPrefetcher );
+
+ $this->settings = Settings::newFromArray( [] );
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ protected function tearDown() {
+ $this->testEnvironment->tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\UnusedPropertiesQueryPage',
+ new UnusedPropertiesQueryPage( $this->store, $this->settings )
+ );
+ }
+
+ public function testFormatResultDIError() {
+
+ $error = $this->dataItemFactory->newDIError( 'Foo');
+
+ $instance = new UnusedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ $error
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $result
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $result
+ );
+ }
+
+ public function testInvalidResultThrowsException() {
+
+ $instance = new UnusedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $this->setExpectedException( '\SMW\Exception\PropertyNotFoundException' );
+ $instance->formatResult( $this->skin, null );
+ }
+
+ public function testFormatPropertyItemOnUserDefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new UnusedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ $property
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemOnPredefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_MDAT' );
+
+ $instance = new UnusedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ $property
+ );
+
+ $this->assertContains(
+ 'Help:Special_properties',
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/WantedPropertiesQueryPageTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/WantedPropertiesQueryPageTest.php
new file mode 100644
index 00000000..a39121cc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/querypages/WantedPropertiesQueryPageTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\DataItemFactory;
+use SMW\Settings;
+use SMW\WantedPropertiesQueryPage;
+
+/**
+ * @covers \SMW\WantedPropertiesQueryPage
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class WantedPropertiesQueryPageTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $skin;
+ private $settings;
+ private $dataItemFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $this->skin = $this->getMockBuilder( '\Skin' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->settings = Settings::newFromArray( [] );
+
+ $this->dataItemFactory = new DataItemFactory();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\WantedPropertiesQueryPage',
+ new WantedPropertiesQueryPage( $this->store, $this->settings )
+ );
+ }
+
+ public function testFormatResultDIError() {
+
+ $error = $this->dataItemFactory->newDIError( 'Foo');
+
+ $instance = new WantedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $error, null ]
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $result
+ );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemOnUserDefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( 'Foo' );
+
+ $instance = new WantedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, 42 ]
+ );
+
+ $this->assertContains(
+ 'Foo',
+ $result
+ );
+ }
+
+ public function testFormatPropertyItemOnPredefinedProperty() {
+
+ $property = $this->dataItemFactory->newDIProperty( '_MDAT' );
+
+ $instance = new WantedPropertiesQueryPage(
+ $this->store,
+ $this->settings
+ );
+
+ $result = $instance->formatResult(
+ $this->skin,
+ [ $property, 42 ]
+ );
+
+ $this->assertEmpty(
+ $result
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/AggregatablePrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/AggregatablePrinterTest.php
new file mode 100644
index 00000000..8bafa626
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/AggregatablePrinterTest.php
@@ -0,0 +1,335 @@
+<?php
+
+namespace SMW\Test;
+
+use ReflectionClass;
+use SMW\AggregatablePrinter;
+use SMWDataItem;
+use SMWDINumber;
+use SMWQueryResult;
+
+/**
+ * Tests for the AggregatablePrinter class
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\AggregatablePrinter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class AggregatablePrinterTest extends QueryPrinterTestCase {
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\AggregatablePrinter';
+ }
+
+ /**
+ * Helper method that returns a AggregatablePrinter object
+ *
+ * @return AggregatablePrinter
+ */
+ private function newInstance( $parameters = [] ) {
+ return $this->setParameters( $this->getMockForAbstractClass( $this->getClass(), [ 'table' ] ), $parameters );
+ }
+
+ /**
+ * @dataProvider errorMessageProvider
+ *
+ * @since 1.9
+ */
+ public function testGetResultTextErrorMessage( $setup, $expected ) {
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getErrors', 'getNext', 'addErrors' ] )
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getNext' )
+ ->will( $this->returnValue( [] ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getErrors' )
+ ->will( $this->returnValue( [ $expected['message'] ] ) );
+
+ $instance = $this->newInstance( $setup['parameters'] );
+ // $queryResult = $setup['queryResult'];
+
+ $reflection = new ReflectionClass( '\SMW\AggregatablePrinter' );
+ $method = $reflection->getMethod( 'getResultText' );
+ $method->setAccessible( true );
+
+ $result = $method->invoke( $instance, $queryResult, SMW_OUTPUT_HTML );
+
+ $this->assertEmpty( $result );
+
+ foreach( $queryResult->getErrors() as $error ) {
+ $this->assertEquals( $expected['message'], $error );
+ }
+ }
+
+ /**
+ * @test AggregatablePrinter::addNumbersForDataItem
+ *
+ * @since 1.9
+ */
+ public function testAddNumbersForDataItem() {
+
+ $values = [];
+ $expected = [];
+ $keys = [ 'test', 'foo', 'bar' ];
+
+ $reflector = new ReflectionClass( '\SMW\AggregatablePrinter' );
+ $method = $reflector->getMethod( 'addNumbersForDataItem' );
+ $method->setAccessible( true );
+
+ for ( $i = 1; $i <= 10; $i++ ) {
+
+ // Select random array key
+ $name = $keys[rand(0, 2)];
+
+ // Get a random number
+ $random = rand( 10, 500 );
+
+ // Set expected result and create dataItem
+ $expected[$name] = isset( $expected[$name] ) ? $expected[$name] + $random : $random;
+ $dataItem = new SMWDINumber( $random );
+
+ $this->assertEquals( $random, $dataItem->getNumber() );
+ $this->assertEquals( SMWDataItem::TYPE_NUMBER, $dataItem->getDIType() );
+
+ // Invoke the instance
+ $result = $method->invokeArgs( $this->newInstance(), [ $dataItem, &$values, $name ] );
+
+ $this->assertInternalType( 'integer', $values[$name] );
+ $this->assertEquals( $expected[$name], $values[$name] );
+ }
+ }
+
+ /**
+ * @dataProvider numberDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetNumericResults( $setup, $expected ) {
+
+ $instance = $this->newInstance( $setup['parameters'] );
+
+ $reflector = new ReflectionClass( '\SMW\AggregatablePrinter' );
+ $method = $reflector->getMethod( 'getNumericResults' );
+ $method->setAccessible( true );
+
+ $result = $method->invoke( $instance, $setup['queryResult'], SMW_OUTPUT_HTML );
+
+ $this->assertInternalType(
+ 'array',
+ $result,
+ 'Asserts that getNumericResults() returns an array'
+ );
+
+ $this->assertEquals(
+ $expected['result'],
+ $result,
+ 'Asserts that the getNumericResults() output matches the expected result'
+ );
+
+ }
+
+ /**
+ * @return array
+ */
+ public function errorMessageProvider() {
+
+ $message = wfMessage( 'smw-qp-aggregatable-empty-data' )->inContentLanguage()->text();
+
+ $provider = [];
+
+ $provider[] = [
+ [
+ 'parameters' => [ 'distribution' => true ],
+ // 'queryResult' => $queryResult
+ ],
+ [
+ 'message' => $message
+ ]
+ ];
+
+ // #1
+ $provider[] = [
+ [
+ 'parameters' => [ 'distribution' => false ],
+ // 'queryResult' => $queryResult
+ ],
+ [
+ 'message' => $message
+ ]
+ ];
+ return $provider;
+ }
+
+ /**
+ * @return array
+ */
+ public function numberDataProvider() {
+
+ $provider = [];
+
+ $setup = [
+ [ 'printRequest' => 'Foo', 'number' => 10, 'dataValue' => 'Quuey' ],
+ [ 'printRequest' => 'Bar', 'number' => 20, 'dataValue' => 'Quuey' ],
+ [ 'printRequest' => 'Bar', 'number' => 20, 'dataValue' => 'Xuuey' ]
+ ];
+
+ // #0 aggregation = subject
+ $parameters = [
+ 'headers' => SMW_HEADERS_PLAIN,
+ 'offset' => 0,
+ 'aggregation' => 'subject',
+ 'mainlabel' => ''
+ ];
+
+ $provider[] = [
+ [
+ 'parameters' => $parameters,
+ 'queryResult' => $this->buildMockQueryResult( $setup )
+ ],
+ [
+ 'result' => [ 'Quuey' => 50 ]
+ ]
+ ];
+
+ // #1 aggregation = property
+ $parameters = [
+ 'headers' => SMW_HEADERS_PLAIN,
+ 'offset' => 0,
+ 'aggregation' => 'property',
+ 'mainlabel' => ''
+ ];
+
+ $provider[] = [
+ [
+ 'parameters' => $parameters,
+ 'queryResult' => $this->buildMockQueryResult( $setup )
+ ],
+ [
+ 'result' => [ 'Foo' => 10, 'Bar' => 40 ]
+ ]
+ ];
+
+ return $provider;
+ }
+
+ /**
+ * @return SMWQueryResult
+ */
+ private function buildMockQueryResult( $setup ) {
+
+ $printRequests = [];
+ $resultArrays = [];
+
+ foreach ( $setup as $value ) {
+
+ $printRequest = $this->getMockBuilder( '\SMW\Query\PrintRequest' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $value['printRequest'] ) );
+
+ $printRequest->expects( $this->any() )
+ ->method( 'getLabel' )
+ ->will( $this->returnValue( $value['printRequest'] ) );
+
+ $printRequests[] = $printRequest;
+
+ $dataItem = $this->getMockBuilder( '\SMWDINumber' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataItem->expects( $this->any() )
+ ->method( 'getDIType' )
+ ->will( $this->returnValue( SMWDataItem::TYPE_NUMBER ) );
+
+ $dataItem->expects( $this->any() )
+ ->method( 'getNumber' )
+ ->will( $this->returnValue( $value['number'] ) );
+
+ $dataValue = $this->getMockBuilder( '\SMWNumberValue' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getTypeID' )
+ ->will( $this->returnValue( '_num' ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getShortWikiText' )
+ ->will( $this->returnValue( $value['dataValue'] ) );
+
+ $dataValue->expects( $this->any() )
+ ->method( 'getDataItem' )
+ ->will( $this->returnValue( $dataItem ) );
+
+ $resultArray = $this->getMockBuilder( '\SMWResultArray' )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'getText', 'getPrintRequest', 'getNextDataValue', 'getNextDataItem' ] )
+ ->getMock();
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getText' )
+ ->will( $this->returnValue( $value['printRequest'] ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getPrintRequest' )
+ ->will( $this->returnValue( $printRequest ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getNextDataValue' )
+ ->will( $this->onConsecutiveCalls( $dataValue, false ) );
+
+ $resultArray->expects( $this->any() )
+ ->method( 'getNextDataItem' )
+ ->will( $this->onConsecutiveCalls( $dataItem, false ) );
+
+ $resultArrays[] = $resultArray;
+ }
+
+ $queryResult = $this->getMockBuilder( '\SMWQueryResult' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getPrintRequests' )
+ ->will( $this->returnValue( $printRequests ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getNext' )
+ ->will( $this->onConsecutiveCalls( $resultArrays, false ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'getLink' )
+ ->will( $this->returnValue( new \SMWInfolink( true, 'Lala', 'Lula' ) ) );
+
+ $queryResult->expects( $this->any() )
+ ->method( 'hasFurtherResults' )
+ ->will( $this->returnValue( true ) );
+
+ return $queryResult;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/DsvResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/DsvResultPrinterTest.php
new file mode 100644
index 00000000..a9e631d3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/DsvResultPrinterTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\DsvResultPrinter;
+
+/**
+ * @covers \SMW\DsvResultPrinter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class DsvResultPrinterTest extends QueryPrinterTestCase {
+
+ /**
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\DsvResultPrinter';
+ }
+
+ /**
+ * @return DsvResultPrinter
+ */
+ private function getInstance( $parameters = [] ) {
+ return $this->setParameters( new DsvResultPrinter( 'dsv' ), $parameters );
+ }
+
+ public function testCanConstruc() {
+
+ $this->assertInstanceOf(
+ '\SMW\DsvResultPrinter',
+ $this->getInstance()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/EmbeddedResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/EmbeddedResultPrinterTest.php
new file mode 100644
index 00000000..9aa8a9fc
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/EmbeddedResultPrinterTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\EmbeddedResultPrinter;
+
+/**
+ * @covers \SMW\EmbeddedResultPrinter
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class EmbeddedResultPrinterTest extends QueryPrinterTestCase {
+
+ /**
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\EmbeddedResultPrinter';
+ }
+
+ /**
+ * @return SMWEmbeddedResultPrinter
+ */
+ private function getInstance( $parameters = [] ) {
+ return $this->setParameters( new EmbeddedResultPrinter( 'embedded' ), $parameters );
+ }
+
+ public function testcanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\EmbeddedResultPrinter',
+ $this->getInstance()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/JsonResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/JsonResultPrinterTest.php
new file mode 100644
index 00000000..8437760f
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/JsonResultPrinterTest.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace SMW\Test;
+
+use ReflectionClass;
+use SMW\JsonResultPrinter;
+use SMW\Tests\Utils\Mock\CoreMockObjectRepository;
+use SMW\Tests\Utils\Mock\MockObjectBuilder;
+
+/**
+ * @covers \SMW\JsonResultPrinter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class JsonResultPrinterTest extends QueryPrinterTestCase {
+
+ protected $mockBuilder;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->mockBuilder = new MockObjectBuilder();
+ $this->mockBuilder->registerRepository( new CoreMockObjectRepository() );
+ }
+
+ /**
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\JsonResultPrinter';
+ }
+
+ /**
+ * @since 1.9
+ *
+ * @return JsonResultPrinter
+ */
+ private function newInstance( $parameters = [] ) {
+ return $this->setParameters( new JsonResultPrinter( 'json' ), $parameters );
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function testConstructor() {
+ $this->assertInstanceOf( $this->getClass(), $this->newInstance() );
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function testGetMimeType() {
+
+ $this->assertEquals(
+ 'application/json',
+ $this->newInstance()->getMimeType( $this->mockBuilder->newObject( 'QueryResult' ) ),
+ 'Asserts that getMimeType() yields an expected result'
+ );
+
+ }
+
+ /**
+ * @dataProvider filenameDataProvider
+ *
+ * @since 1.9
+ */
+ public function testGetFileName( $filename, $expected ) {
+
+ $instance = $this->newInstance( [ 'searchlabel' => $filename ] );
+
+ $this->assertEquals(
+ $expected,
+ $instance->getFileName( $this->mockBuilder->newObject( 'QueryResult' ) ),
+ 'Asserts that getFileName() yields an expected result');
+ }
+
+ /**
+ * @return array
+ */
+ public function filenameDataProvider() {
+
+ $provider = [];
+
+ $provider[] = [ 'Lala', 'Lala.json' ];
+ $provider[] = [ 'Lala Lilu', 'Lala_Lilu.json' ];
+ $provider[] = [ '' , 'result.json'];
+
+ return $provider;
+ }
+
+ /**
+ * @since 1.9
+ */
+ public function testGetResultText() {
+
+ $result = [
+ 'lala' => __METHOD__,
+ 'lula' => 999388383838
+ ];
+
+ $expected = array_merge( $result, [ 'rows' => count( $result ) ] );
+
+ $instance = $this->newInstance( [ 'prettyprint' => false, 'unescape' => false ] );
+
+ $reflector = new ReflectionClass( '\SMW\JsonResultPrinter' );
+ $getResultText = $reflector->getMethod( 'getResultText' );
+ $getResultText->setAccessible( true );
+
+ $queryResult = $this->mockBuilder->newObject( 'QueryResult', [
+ 'serializeToArray' => $result,
+ 'getCount' => count( $result )
+ ] );
+
+ $results = $getResultText->invoke( $instance, $queryResult, SMW_OUTPUT_FILE );
+
+ $this->assertInternalType( 'string', $results );
+ $this->assertEquals( json_encode( $expected ), $results );
+
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RawResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RawResultPrinterTest.php
new file mode 100644
index 00000000..bfd759d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RawResultPrinterTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\RawResultPrinter;
+
+/**
+ * @covers \SMW\RawResultPrinter
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RawResultPrinterTest extends QueryPrinterTestCase {
+
+ /**
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\RawResultPrinter';
+ }
+
+ /**
+ * @return RawResultPrinter
+ */
+ private function getInstance( $parameters = [] ) {
+
+ $instance = $this->getMockBuilder( '\SMW\RawResultPrinter' )
+ ->disableOriginalConstructor()
+ ->setConstructorArgs( [ 'api' ] )
+ ->getMockForAbstractClass();
+
+ return $this->setParameters( $instance, $parameters );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMW\RawResultPrinter',
+ $this->getInstance()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RdfResultPrinterTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RdfResultPrinterTest.php
new file mode 100644
index 00000000..debdc0cb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/RdfResultPrinterTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\RdfResultPrinter;
+
+/**
+ * @covers \SMW\RdfResultPrinter
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class RdfResultPrinterTest extends QueryPrinterTestCase {
+
+ /**
+ * @return string|false
+ */
+ public function getClass() {
+ return '\SMW\RdfResultPrinter';
+ }
+
+ /**
+ * @return RdfResultPrinter
+ */
+ private function getInstance( $parameters = [] ) {
+ return $this->setParameters( new RdfResultPrinter( 'rdf' ), $parameters );
+ }
+
+ public function testConstructor() {
+
+ $this->assertInstanceOf(
+ '\SMW\RdfResultPrinter',
+ $this->getInstance()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/ResultPrintersTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/ResultPrintersTest.php
new file mode 100644
index 00000000..04616ee3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/queryprinters/ResultPrintersTest.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SMW\Test;
+
+use ParamProcessor\ParamDefinition;
+use SMW\ResultPrinter;
+use SMWQueryProcessor;
+
+/**
+ * Does some basic tests for the SMW\ResultPrinter deriving classes
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+
+/**
+ * @covers \SMW\ResultPrinter
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class ResultPrintersTest extends QueryPrinterTestCase {
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string|false
+ */
+ public function getClass() {
+ return false;
+ }
+
+ public function constructorProvider() {
+ global $smwgResultFormats;
+
+ $formats = [];
+
+ foreach ( $smwgResultFormats as $format => $class ) {
+ $formats[] = [ $format, $class, true ];
+ $formats[] = [ $format, $class, false ];
+ }
+
+ return $formats;
+ }
+
+ /**
+ * @dataProvider constructorProvider
+ *
+ * @param string $format
+ * @param string $class
+ * @param boolean $isInline
+ */
+ public function testConstructor( $format, $class, $isInline ) {
+ $instance = new $class( $format, $isInline );
+ $this->assertInstanceOf( '\SMWIResultPrinter', $instance );
+ }
+
+ public function instanceProvider() {
+ global $smwgResultFormats;
+
+ $instances = [];
+
+ foreach ( $smwgResultFormats as $format => $class ) {
+ $instances[] = new $class( $format, true );
+ }
+
+ return $this->arrayWrap( $instances );
+ }
+
+ /**
+ * @dataProvider instanceProvider
+ *
+ * @param \SMWResultPrinter $printer
+ */
+ public function testGetParamDefinitions( ResultPrinter $printer ) {
+ $params = $printer->getParamDefinitions( SMWQueryProcessor::getParameters( null, $printer ) );
+
+ $params = ParamDefinition::getCleanDefinitions( $params );
+
+ $this->assertInternalType( 'array', $params );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialConceptsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialConceptsTest.php
new file mode 100644
index 00000000..5835aefa
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialConceptsTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\DIWikiPage;
+use SMW\SpecialConcepts;
+use SMW\Tests\Utils\UtilityFactory;
+use Title;
+use SMW\Tests\TestEnvironment;
+
+/**
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SpecialConceptsTest extends \PHPUnit_Framework_TestCase {
+
+ private $stringValidator;
+ private $testEnvironment;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->testEnvironment = new TestEnvironment();
+ $this->stringValidator = $this->testEnvironment->newValidatorFactory()->newStringValidator();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ SpecialConcepts::class,
+ new SpecialConcepts()
+ );
+ }
+
+ public function testExecute() {
+
+ $expected = 'p class="smw-special-concept-docu plainlinks"';
+
+ $outputPage = $this->getMockBuilder( '\OutputPage' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $outputPage->expects( $this->atLeastOnce() )
+ ->method( 'addHtml' )
+ ->with( $this->stringContains( $expected ) );
+
+ $query = '';
+ $instance = new SpecialConcepts();
+
+ $instance->getContext()->setTitle(
+ Title::newFromText( 'SemanticMadiaWiki' )
+ );
+
+ $oldOutput = $instance->getOutput();
+
+ $instance->getContext()->setOutput( $outputPage );
+ $instance->execute( $query );
+
+ // Context is static avoid any succeeding tests to fail
+ $instance->getContext()->setOutput( $oldOutput );
+ }
+
+ /**
+ * @depends testExecute
+ */
+ public function testGetHtmlForAnEmptySubject() {
+
+ $instance = new SpecialConcepts();
+
+ $this->stringValidator->assertThatStringContains(
+ 'div class="smw-special-concept-empty"',
+ $instance->getHtml( [], 0, 0 )
+ );
+ }
+
+ /**
+ * @depends testGetHtmlForAnEmptySubject
+ */
+ public function testGetHtmlForSingleSubject() {
+
+ $subject = DIWikiPage::newFromText( __METHOD__ );
+ $instance = new SpecialConcepts();
+
+ $this->stringValidator->assertThatStringContains(
+ 'div class="smw-special-concept-count"',
+ $instance->getHtml( [ $subject ], 1, 0 )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialsTest.php
new file mode 100644
index 00000000..4447ed28
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/specials/SpecialsTest.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace SMW\Test;
+
+use FauxRequest;
+use Language;
+use RequestContext;
+use SpecialPage;
+use SpecialPageFactory;
+
+/**
+ * Tests for registered special pages
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\SpecialWantedProperties
+ * @covers \SMW\SpecialUnusedProperties
+ * @covers \SMW\SpecialProperties
+ * @covers \SMW\SpecialConcepts
+ * @covers \SMW\SpecialPage
+ * @covers \SMW\MediaWiki\Specials\SpecialAsk
+ * @covers \SMW\MediaWiki\Specials\SpecialAdmin
+ * @covers \SMW\MediaWiki\Specials\SpecialBrowse
+ * @covers \SMW\MediaWiki\Specials\SpecialSearchByProperty
+ *
+ * @note Test base was borrowed from the EducationProgram extension
+ *
+ * @group SMW
+ * @group SMWExtension
+ * @group medium
+ */
+class SpecialsTest extends SemanticMediaWikiTestCase {
+
+ /**
+ * Returns the name of the class to be tested
+ *
+ * @return string|false
+ */
+ public function getClass() {
+ return false;
+ }
+
+ /**
+ * @dataProvider specialPageProvider
+ *
+ * @param $specialPage
+ */
+ public function testSpecial( SpecialPage $specialPage ) {
+
+ try {
+ $specialPage->execute( '' );
+ }
+ catch ( \Exception $exception ) {
+ if ( !( $exception instanceof \PermissionsError ) && !( $exception instanceof \ErrorPageError ) ) {
+ throw $exception;
+ }
+ }
+
+ $this->assertTrue( true, 'SpecialPage test did run without errors' );
+ }
+
+ /**
+ * @test SpecialPageFactory::getLocalNameFor
+ * @dataProvider specialPageProvider
+ *
+ * Test created in response to bug 44191
+ *
+ * @param $specialPage
+ */
+ public function testSpecialAliasesContLang( SpecialPage $specialPage ) {
+
+ // Test for languages
+ $langCodes = [ 'en', 'fr', 'de', 'es', 'zh', 'ja' ];
+
+ // Test aliases for a specific language
+ foreach ( $langCodes as $langCode ) {
+ $langObj = Language::factory( $langCode );
+ $aliases = $langObj->getSpecialPageAliases();
+ $found = false;
+ $name = $specialPage->getName();
+
+ // Check against available aliases
+ foreach ( $aliases as $n => $values ) {
+ foreach ( $values as $value ) {
+ if( $name === $value ) {
+ $found = true;
+ break;
+ }
+ }
+ }
+
+ $this->assertTrue(
+ $found,
+ "{$name} alias not found in language {$langCode}"
+ );
+ }
+ }
+
+ /**
+ * Provides special pages
+ *
+ * @return array
+ */
+ public function specialPageProvider() {
+ $request = new FauxRequest( [], true );
+ $argLists = [];
+
+ $specialPages = [
+ 'Ask',
+ 'Browse',
+ 'PageProperty',
+ 'SearchByProperty',
+ 'SMWAdmin',
+ 'ExportRDF',
+ 'Types',
+ 'Properties',
+ 'UnusedProperties',
+ 'WantedProperties',
+ 'Concepts',
+ 'ProcessingErrorList',
+ 'PropertyLabelSimilarity',
+ 'URIResolver'
+ ];
+
+ foreach ( $specialPages as $special ) {
+
+ $specialPage = SpecialPageFactory::getPage(
+ $special
+ );
+
+ // Deprecated: Use of SpecialPage::getTitle was deprecated in MediaWiki 1.23
+ $title = method_exists( $specialPage, 'getPageTitle') ? $specialPage->getPageTitle() : $specialPage->getTitle();
+
+ $context = RequestContext::newExtraneousContext( $title );
+ $context->setRequest( $request );
+
+ $specialPage->setContext( clone $context );
+ $argLists[] = [ clone $specialPage ];
+
+ $context->setUser( $this->getUser() );
+ $specialPage->setContext( $context );
+ $argLists[] = [ $specialPage ];
+ }
+
+ return $argLists;
+ }
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/QueryResultTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/QueryResultTest.php
new file mode 100644
index 00000000..61e74563
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/QueryResultTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\DIWikiPage;
+use SMWQueryResult as QueryResult;
+
+/**
+ * @covers \SMWQueryResult
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 2.1
+ *
+ * @author mwjames
+ */
+class QueryResultTest extends \PHPUnit_Framework_TestCase {
+
+ public function testCanConstruct() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequests = [];
+ $results = [];
+
+ $this->assertInstanceOf(
+ '\SMWQueryResult',
+ new QueryResult( $printRequests, $query, $results, $store )
+ );
+ }
+
+ public function testVerifyThatAfterSerializeToArrayResultNextCanBeUsed() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequests = [];
+
+ $results = [
+ new DIWikiPage( 'Foo', 0 ),
+ new DIWikiPage( 'Bar', 0 )
+ ];
+
+ $instance = new QueryResult( $printRequests, $query, $results, $store );
+
+ $instance->serializeToArray();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getNext()
+ );
+
+ $instance->getHash();
+
+ $this->assertInternalType(
+ 'array',
+ $instance->getNext()
+ );
+ }
+
+ public function testIsFromCache() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequests = [];
+ $results = [];
+
+ $instance = new QueryResult(
+ $printRequests,
+ $query,
+ $results,
+ $store
+ );
+
+ $this->assertFalse(
+ $instance->isFromCache()
+ );
+
+ $instance->setFromCache( true );
+
+ $this->assertTrue(
+ $instance->isFromCache()
+ );
+ }
+
+ public function testGetHash() {
+
+ $query = $this->getMockBuilder( '\SMWQuery' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( '\SMW\Store' )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+
+ $printRequests = [];
+ $results = [];
+
+ $instance = new QueryResult(
+ $printRequests,
+ $query,
+ $results,
+ $store
+ );
+
+ $this->assertNotSame(
+ $instance->getHash( 'quick' ),
+ $instance->getHash()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreFactoryTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreFactoryTest.php
new file mode 100644
index 00000000..90415200
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreFactoryTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace SMW\Tests;
+
+use SMW\Settings;
+use SMW\StoreFactory;
+
+/**
+ * @covers \SMW\StoreFactory
+ *
+ *
+ * @group SMW
+ * @group SMWExtension
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class StoreFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ use PHPUnitCompat;
+
+ protected function tearDown() {
+ StoreFactory::clear();
+
+ parent::tearDown();
+ }
+
+ public function testGetDefaultStore() {
+
+ $instance = StoreFactory::getStore();
+
+ $this->assertInstanceOf(
+ Settings::newFromGlobals()->get( 'smwgDefaultStore' ),
+ $instance
+ );
+
+ $this->assertSame(
+ StoreFactory::getStore(),
+ $instance
+ );
+
+ StoreFactory::clear();
+
+ $this->assertNotSame(
+ StoreFactory::getStore(),
+ $instance
+ );
+ }
+
+ public function testDifferentStoreIdInstanceInvocation() {
+
+ $this->assertInstanceOf( 'SMW\Store', StoreFactory::getStore( '\SMWSQLStore3' ) );
+ $this->assertInstanceOf( 'SMW\Store', StoreFactory::getStore( '\SMWSparqlStore' ) );
+
+ $this->assertNotSame(
+ StoreFactory::getStore( '\SMWSQLStore3' ),
+ StoreFactory::getStore( '\SMWSparqlStore' )
+ );
+ }
+
+ public function testStoreInstanceException() {
+ $this->setExpectedException( '\SMW\Exception\StoreNotFoundException' );
+ StoreFactory::getStore( '\SMW\StoreFactory' );
+ }
+
+ public function testStoreWithInvalidClassThrowsException() {
+ $this->setExpectedException( 'RuntimeException' );
+ StoreFactory::getStore( 'foo' );
+ }
+
+ /**
+ * smwfGetStore is deprecated but due to its dependency do a quick check here
+ *
+ * FIXME Delete this test in 1.11
+ */
+ public function testSmwfGetStore() {
+ $store = smwfGetStore();
+
+ $this->assertInstanceOf( 'SMWStore', $store );
+ $this->assertInstanceOf( 'SMW\Store', $store );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreTest.php
new file mode 100644
index 00000000..2b60cf46
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/StoreTest.php
@@ -0,0 +1,198 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\Connection\ConnectionManager;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\StoreFactory;
+use SMW\Tests\MwDBaseUnitTestCase;
+use SMWRequestOptions;
+use Title;
+
+/**
+ * Tests for the SMWStore class.
+ *
+ * @since 1.8
+ *
+ * @group SMW
+ * @group SMWStore
+ * @group SMWExtension
+ * @group Database
+ *
+ * @author Nischay Nahata
+ */
+class StoreTest extends MwDBaseUnitTestCase {
+
+///// Reading methods /////
+
+ public function getSemanticDataProvider() {
+ return [
+ [ Title::newMainPage()->getFullText() ],
+ ];
+ }
+
+ /**
+ * @dataProvider getSemanticDataProvider
+ */
+ public function testGetSemanticData( $titleText ,$filter = false) {
+ $title = Title::newFromText( $titleText );
+ $subject = DIWikiPage::newFromTitle( $title );
+ $store = StoreFactory::getStore();
+
+ $this->assertInstanceOf(
+ '\SMW\SemanticData',
+ $store->getSemanticData( $subject, $filter ),
+ "Result should be instance of SMWSemanticData."
+ );
+ }
+
+ public function getPropertyValuesDataProvider() {
+ return [
+ [ Title::newMainPage()->getFullText(), new DIProperty('_MDAT') ],
+ [ Title::newMainPage()->getFullText(), DIProperty::newFromUserLabel('Age') ],
+ ];
+ }
+
+ /**
+ * @dataProvider getPropertyValuesDataProvider
+ */
+ public function testGetPropertyValues( $titleText, DIProperty $property, $requestOptions = null ) {
+ $title = Title::newFromText( $titleText );
+ $subject = DIWikiPage::newFromTitle( $title );
+ $store = StoreFactory::getStore();
+ $result = $store->getPropertyValues( $subject, $property, $requestOptions );
+
+ $this->assertInternalType( 'array', $result );
+ $this->assertContainsOnlyInstancesOf( '\SMWDataItem', $result );
+ }
+
+ public function getPropertySubjectsDataProvider() {
+ return [
+ [ new DIProperty('_MDAT'), null ],
+ ];
+ }
+
+ /**
+ * @dataProvider getPropertySubjectsDataProvider
+ */
+ public function testGetPropertySubjects( DIProperty $property, $value, $requestOptions = null ) {
+ $store = StoreFactory::getStore();
+ $result = $store->getPropertySubjects( $property, $value, $requestOptions );
+
+ $this->assertInstanceOf(
+ '\Iterator',
+ $result
+ );
+
+ foreach( $result as $page ) {
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $page,
+ "Result should be instance of DIWikiPage."
+ );
+ }
+ }
+
+ public function getPropertiesDataProvider() {
+ return [
+ [ Title::newMainPage()->getFullText() ],
+ ];
+ }
+
+ /**
+ * @dataProvider getPropertiesDataProvider
+ */
+ public function testGetProperties( $titleText, $requestOptions = null ) {
+ $title = Title::newFromText( $titleText );
+ $subject = DIWikiPage::newFromTitle( $title );
+ $store = StoreFactory::getStore();
+ $result = $store->getProperties( $subject, $requestOptions );
+
+ $this->assertTrue( is_array( $result ) );
+
+ foreach( $result as $property ) {
+ $this->assertInstanceOf(
+ '\SMWDataItem',
+ $property,
+ "Result should be instance of DIProperty."
+ );
+ }
+ }
+
+///// Special page functions /////
+
+ public function testGetPropertiesSpecial() {
+ // Really bailing out here and making the test database dependent!!
+
+ // This test fails on mysql http://bugs.mysql.com/bug.php?id=10327
+ if( $GLOBALS['wgDBtype'] == 'mysql' ) {
+ $this->assertTrue( true );
+ return;
+ }
+
+ $store = StoreFactory::getStore();
+ $result = $store->getPropertiesSpecial( new SMWRequestOptions() );
+
+ $this->assertInstanceOf( '\SMW\SQLStore\Lookup\ListLookup', $result );
+ foreach( $result->fetchList() as $row ) {
+ $this->assertCount( 2, $row );
+
+ $this->assertInstanceOf(
+ '\SMWDataItem',
+ $row[0],
+ "Result should be DataItem instance."
+ );
+ }
+ }
+
+ public function testGetUnusedPropertiesSpecial() {
+ $store = StoreFactory::getStore();
+ $result = $store->getUnusedPropertiesSpecial( new SMWRequestOptions() );
+
+ $this->assertInstanceOf( '\SMW\SQLStore\Lookup\ListLookup', $result );
+ foreach( $result->fetchList() as $row ) {
+ $this->assertInstanceOf(
+ '\SMWDataItem',
+ $row,
+ "Result should be instance of DIProperty."
+ );
+ }
+ }
+
+ public function testGetWantedPropertiesSpecial() {
+ $store = StoreFactory::getStore();
+ $result = $store->getWantedPropertiesSpecial( new SMWRequestOptions() );
+
+ $this->assertInstanceOf( '\SMW\SQLStore\Lookup\ListLookup', $result );
+ foreach( $result->fetchList() as $row ) {
+ $this->assertInstanceOf(
+ '\SMW\DIProperty',
+ $row[0],
+ "Result should be instance of DIProperty."
+ );
+ }
+ }
+
+ public function testGetStatistics() {
+ $store = StoreFactory::getStore();
+ $result = $store->getStatistics();
+
+ $this->assertTrue( is_array( $result ) );
+ $this->assertArrayHasKey( 'PROPUSES', $result );
+ $this->assertArrayHasKey( 'USEDPROPS', $result );
+ $this->assertArrayHasKey( 'DECLPROPS', $result );
+ }
+
+ public function testConnection() {
+
+ $store = StoreFactory::getStore();
+ $store->setConnectionManager( new ConnectionManager() );
+
+ $this->assertInstanceOf(
+ '\SMW\MediaWiki\Database',
+ $store->getConnection( 'mw.db' )
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreSmwIdsTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreSmwIdsTest.php
new file mode 100644
index 00000000..54b913f1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreSmwIdsTest.php
@@ -0,0 +1,546 @@
+<?php
+
+namespace SMW\Tests\SQLStore;
+
+use Onoi\Cache\FixedInMemoryLruCache;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\IdCacheManager;
+use SMW\SQLStore\RedirectStore;
+use SMWSql3SmwIds;
+
+/**
+ * @covers \SMWSql3SmwIds
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9.1
+ *
+ * @author mwjames
+ */
+class SQLStoreSmwIdsTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+ private $cache;
+ private $idMatchFinder;
+ private $uniquenessLookup;
+ private $factory;
+
+ protected function setUp() {
+
+ $idCacheManager = new IdCacheManager(
+ [
+ 'entity.id' => new FixedInMemoryLruCache(),
+ 'entity.sort' => new FixedInMemoryLruCache(),
+ 'entity.lookup' => new FixedInMemoryLruCache(),
+ 'table.hash' => new FixedInMemoryLruCache()
+ ]
+ );
+
+ $this->cache = $this->getMockBuilder( '\Onoi\Cache\Cache' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyStatisticsStore = $this->getMockBuilder( '\SMW\SQLStore\PropertyStatisticsStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->idEntityFinder = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdEntityFinder' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->uniquenessLookup = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\UniquenessLookup' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $redirectStore = new RedirectStore( $this->store );
+
+ $this->factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newUniquenessLookup' )
+ ->will( $this->returnValue( $this->uniquenessLookup ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newIdCacheManager' )
+ ->will( $this->returnValue( $idCacheManager ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newRedirectStore' )
+ ->will( $this->returnValue( $redirectStore ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newPropertyStatisticsStore' )
+ ->will( $this->returnValue( $propertyStatisticsStore ) );
+
+ $this->factory->expects( $this->any() )
+ ->method( 'newidEntityFinder' )
+ ->will( $this->returnValue( $this->idEntityFinder ) );
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWSql3SmwIds',
+ new SMWSql3SmwIds( $this->store, $this->factory )
+ );
+ }
+
+ public function testRedirectInfoRoundtrip() {
+
+ $subject = new DIWikiPage( 'Foo', 9001 );
+
+ $instance = new SMWSql3SmwIds(
+ $this->store,
+ $this->factory
+ );
+
+ $this->assertFalse(
+ $instance->isRedirect( $subject )
+ );
+
+ $instance->addRedirect( 42, 'Foo', 9001 );
+
+ $this->assertEquals(
+ 42,
+ $instance->findRedirect( 'Foo', 9001 )
+ );
+
+ $this->assertTrue(
+ $instance->isRedirect( $subject )
+ );
+
+ $instance->deleteRedirect( 'Foo', 9001 );
+
+ $this->assertEquals(
+ 0,
+ $instance->findRedirect( 'Foo', 9001 )
+ );
+
+ $this->assertFalse(
+ $instance->isRedirect( $subject )
+ );
+ }
+
+ public function testGetPropertyId() {
+
+ $selectRow = new \stdClass;
+ $selectRow->smw_id = 9999;
+ $selectRow->smw_sort = '';
+ $selectRow->smw_sortkey = 'Foo';
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $selectRow ) );
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $this->factory
+ );
+
+ $result = $instance->getSMWPropertyID( new DIProperty( 'Foo' ) );
+
+ $this->assertEquals( 9999, $result );
+ }
+
+ /**
+ * @dataProvider pageIdandSortProvider
+ */
+ public function testGetSMWPageIDandSort( $parameters ) {
+
+ $selectRow = new \stdClass;
+ $selectRow->smw_id = 9999;
+ $selectRow->smw_sort = '';
+ $selectRow->smw_sortkey = 'Foo';
+ $selectRow->smw_proptable_hash = serialize( 'Foo' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $selectRow ) );
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $this->factory
+ );
+
+ $sortkey = $parameters['sortkey'];
+
+ $result = $instance->getSMWPageIDandSort(
+ $parameters['title'],
+ $parameters['namespace'],
+ $parameters['iw'],
+ $parameters['subobjectName'],
+ $sortkey, // pass-by-reference
+ $parameters['canonical'],
+ $parameters['fetchHashes']
+ );
+
+ $this->assertEquals( 9999, $result );
+ }
+
+ /**
+ * @dataProvider pageIdandSortProvider
+ */
+ public function testMakeSMWPageID( $parameters ) {
+
+ $selectRow = new \stdClass;
+ $selectRow->smw_id = 0;
+ $selectRow->o_id = 0;
+ $selectRow->smw_sort = '';
+ $selectRow->smw_sortkey = 'Foo';
+ $selectRow->smw_proptable_hash = serialize( 'Foo' );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->any() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $selectRow ) );
+
+ $connection->expects( $this->once() )
+ ->method( 'insertId' )
+ ->will( $this->returnValue( 9999 ) );
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $this->factory
+ );
+
+ $sortkey = $parameters['sortkey'];
+
+ $result = $instance->makeSMWPageID(
+ $parameters['title'],
+ $parameters['namespace'],
+ $parameters['iw'],
+ $parameters['subobjectName'],
+ $sortkey,
+ $parameters['canonical'],
+ $parameters['fetchHashes']
+ );
+
+ $this->assertEquals( 9999, $result );
+ }
+
+ public function testGetDataItemById() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $this->idEntityFinder->expects( $this->once() )
+ ->method( 'getDataItemById' )
+ ->with( $this->equalTo( 42 ) )
+ ->will( $this->returnValue( new DIWikiPage( 'Foo', NS_MAIN ) ) );
+
+ $instance = new SMWSql3SmwIds(
+ $this->store,
+ $this->factory
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\DIWikiPage',
+ $instance->getDataItemById( 42 )
+ );
+ }
+
+ public function testUpdateInterwikiField() {
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'update' )
+ ->with(
+ $this->anything(),
+ $this->equalTo( [
+ 'smw_iw' => 'Bar',
+ 'smw_hash' => '8ba1886210e332a1fbaf28c38e43d1e89dc761db' ] ),
+ $this->equalTo( [ 'smw_id' => 42 ] ) );
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $this->factory
+ );
+
+ $instance->updateInterwikiField(
+ 42,
+ new DIWikiPage( 'Foo', NS_MAIN, 'Bar' )
+ );
+ }
+
+ public function testFindDuplicateEntries() {
+
+ $expected = [
+ 'count' => 2,
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => 0,
+ 'smw_iw' => '',
+ 'smw_subobject' => ''
+ ];
+
+ $row = $expected;
+
+ $this->uniquenessLookup->expects( $this->once() )
+ ->method( 'findDuplicates' )
+ ->will( $this->returnValue( [ $row ] ) );
+
+ $instance = new SMWSql3SmwIds(
+ $this->store,
+ $this->factory
+ );
+
+ $this->assertEquals(
+ [ $expected ],
+ $instance->findDuplicates()
+ );
+ }
+
+ public function testGetIDOnPredefinedProperty() {
+
+ $row = new \stdClass;
+ $row->smw_id = 42;
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store = $this->getMockBuilder( 'SMWSQLStore3' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( $row ) );
+
+ $store->expects( $this->atLeastOnce() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $this->factory
+ );
+
+ $this->assertEquals(
+ 29,
+ $instance->getId( new DIWikiPage( '_MDAT', SMW_NS_PROPERTY ) )
+ );
+
+ $this->assertEquals(
+ 42,
+ $instance->getId( new DIWikiPage( '_MDAT', SMW_NS_PROPERTY, '', 'Foo' ) )
+ );
+ }
+
+ public function testWarmUpCache() {
+
+ $row = [
+ 'smw_id' => 42,
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => 0,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_sortkey' => 'Foo',
+ 'smw_sort' => '',
+ ];
+
+ $idCacheManager = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdCacheManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idCacheManager->expects( $this->once() )
+ ->method( 'setCache' );
+
+ $idCacheManager->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnValue( $this->cache ) );
+
+ $factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $factory->expects( $this->any() )
+ ->method( 'newIdCacheManager' )
+ ->will( $this->returnValue( $idCacheManager ) );
+
+ $factory->expects( $this->any() )
+ ->method( 'newIdEntityFinder' )
+ ->will( $this->returnValue( $this->idEntityFinder ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'select' )
+ ->will( $this->returnValue( [ (object)$row ] ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $factory
+ );
+
+ $instance->warmUpCache( [ new DIWikiPage( 'Bar', NS_MAIN ) ] );
+ }
+
+ public function testFindAssociatedRev() {
+
+ $row = [
+ 'smw_id' => 42,
+ 'smw_title' => 'Foo',
+ 'smw_namespace' => 0,
+ 'smw_iw' => '',
+ 'smw_subobject' => '',
+ 'smw_sortkey' => 'Foo',
+ 'smw_sort' => '',
+ 'smw_rev' => 1001,
+ ];
+
+ $idCacheManager = $this->getMockBuilder( '\SMW\SQLStore\EntityStore\IdCacheManager' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $idCacheManager->expects( $this->any() )
+ ->method( 'get' )
+ ->will( $this->returnValue( $this->cache ) );
+
+ $factory = $this->getMockBuilder( '\SMW\SQLStore\SQLStoreFactory' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $factory->expects( $this->any() )
+ ->method( 'newIdCacheManager' )
+ ->will( $this->returnValue( $idCacheManager ) );
+
+ $factory->expects( $this->any() )
+ ->method( 'newIdEntityFinder' )
+ ->will( $this->returnValue( $this->idEntityFinder ) );
+
+ $connection = $this->getMockBuilder( '\SMW\MediaWiki\Database' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection->expects( $this->once() )
+ ->method( 'selectRow' )
+ ->will( $this->returnValue( (object)$row ) );
+
+ $store = $this->getMockBuilder( '\SMW\SQLStore\SQLStore' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $store->expects( $this->any() )
+ ->method( 'getConnection' )
+ ->will( $this->returnValue( $connection ) );
+
+ $instance = new SMWSql3SmwIds(
+ $store,
+ $factory
+ );
+
+ $this->assertEquals(
+ 1001,
+ $instance->findAssociatedRev( 'Foo', NS_MAIN, '', '' )
+ );
+ }
+
+ public function pageIdandSortProvider() {
+
+ $provider[] = [ 'Foo', NS_MAIN, '' , '', 'FOO', false, false ];
+ $provider[] = [ 'Foo', NS_MAIN, '' , '', 'FOO', true, false ];
+ $provider[] = [ 'Foo', NS_MAIN, '' , '', 'FOO', true, true ];
+ $provider[] = [ 'Foo', NS_MAIN, 'quy' , '', 'FOO', false, false ];
+ $provider[] = [ 'Foo', NS_MAIN, 'quy' , 'xwoo', 'FOO', false, false ];
+
+ $provider[] = [ 'pro', SMW_NS_PROPERTY, '' , '', 'PRO', false, false ];
+ $provider[] = [ 'pro', SMW_NS_PROPERTY, '' , '', 'PRO', true, false ];
+ $provider[] = [ 'pro', SMW_NS_PROPERTY, '' , '', 'PRO', true, true ];
+
+ return $this->createAssociativeArrayFromProviderDefinition( $provider );
+ }
+
+ private function createAssociativeArrayFromProviderDefinition( $definitions ) {
+
+ foreach ( $definitions as $map ) {
+ $provider[] = [ [
+ 'title' => $map[0],
+ 'namespace' => $map[1],
+ 'iw' => $map[2],
+ 'subobjectName' => $map[3],
+ 'sortkey' => $map[4],
+ 'canonical' => $map[5],
+ 'fetchHashes' => $map[6]
+ ] ];
+ }
+
+ return $provider;
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreTest.php b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreTest.php
new file mode 100644
index 00000000..d183273c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/phpunit/includes/storage/sqlstore/SQLStoreTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Test\SQLStore;
+
+use SMW\ApplicationFactory;
+use SMWSQLStore3;
+
+/**
+ * @covers \SMWSQLStore3
+ *
+ * @group semantic-mediawiki
+ *
+ * @license GNU GPL v2+
+ * @since 1.9
+ *
+ * @author mwjames
+ */
+class SQLStoreTest extends \PHPUnit_Framework_TestCase {
+
+ private $store;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->store = new SMWSQLStore3();
+
+ $settings = [
+ 'smwgFixedProperties' => [],
+ 'smwgPageSpecialProperties' => []
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+ }
+
+ protected function tearDown() {
+ $this->store->clear();
+ ApplicationFactory::getInstance()->clear();
+
+ parent::tearDown();
+ }
+
+ public function testCanConstruct() {
+
+ $this->assertInstanceOf(
+ '\SMWSQLStore3',
+ $this->store
+ );
+
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\SQLStore',
+ $this->store
+ );
+ }
+
+ /**
+ * @depends testCanConstruct
+ */
+ public function testGetPropertyTables() {
+
+ $defaultPropertyTableCount = count( $this->store->getPropertyTables() );
+
+ $this->assertInternalType(
+ 'array',
+ $this->store->getPropertyTables()
+ );
+
+ foreach ( $this->store->getPropertyTables() as $tid => $propTable ) {
+ $this->assertInstanceOf(
+ '\SMW\SQLStore\TableDefinition',
+ $propTable
+ );
+ }
+ }
+
+ /**
+ * @depends testGetPropertyTables
+ */
+ public function testPropertyTablesValidCustomizableProperty() {
+
+ $defaultPropertyTableCount = count( $this->store->getPropertyTables() );
+
+ $settings = [
+ 'smwgFixedProperties' => [],
+ 'smwgPageSpecialProperties' => [ '_MDAT' ]
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ $this->store->clear();
+
+ $this->assertCount(
+ $defaultPropertyTableCount + 1,
+ $this->store->getPropertyTables()
+ );
+ }
+
+ /**
+ * @depends testGetPropertyTables
+ */
+ public function testPropertyTablesWithInvalidCustomizableProperty() {
+
+ $defaultPropertyTableCount = count( $this->store->getPropertyTables() );
+
+ $settings = [
+ 'smwgFixedProperties' => [],
+ 'smwgPageSpecialProperties' => [ '_MDAT', 'Foo' ]
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ $this->store->clear();
+
+ $this->assertCount(
+ $defaultPropertyTableCount + 1,
+ $this->store->getPropertyTables()
+ );
+ }
+
+ /**
+ * @depends testGetPropertyTables
+ */
+ public function testPropertyTablesWithValidCustomizableProperties() {
+
+ $defaultPropertyTableCount = count( $this->store->getPropertyTables() );
+
+ $settings = [
+ 'smwgFixedProperties' => [],
+ 'smwgPageSpecialProperties' => [ '_MDAT', '_MEDIA' ]
+ ];
+
+ foreach ( $settings as $key => $value ) {
+ ApplicationFactory::getInstance()->getSettings()->set( $key, $value );
+ }
+
+ $this->store->clear();
+
+ $this->assertCount(
+ $defaultPropertyTableCount + 2,
+ $this->store->getPropertyTables()
+ );
+ }
+
+ public function testGetStatisticsTable() {
+
+ $this->assertInternalType(
+ 'string',
+ $this->store->getStatisticsTable()
+ );
+ }
+
+ public function testGetObjectIds() {
+
+ $this->assertInternalType(
+ 'object',
+ $this->store->getObjectIds()
+ );
+
+ $this->assertInternalType(
+ 'string',
+ $this->store->getObjectIds()->getIdTable()
+ );
+ }
+
+}
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/api/ext.smw.api.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/api/ext.smw.api.test.js
new file mode 100644
index 00000000..8960e7d8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/api/ext.smw.api.test.js
@@ -0,0 +1,190 @@
+/**
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/wiki/QUnit
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ignore
+ *
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Tests methods provided by ext.smw.api.js
+ * @ignore
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.Api', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.api();
+ assert.ok( result instanceof Object, pass + 'the api instance was accessible' );
+
+ } );
+
+ /**
+ * Test fetch
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'fetch()', function ( assert ) {
+ assert.expect( 4 );
+
+ var done = assert.async();
+
+ var smwApi = new smw.api();
+
+ assert.throws( function() { smwApi.fetch( '' , '' ); }, pass + 'an error was raised (query was empty)' );
+ assert.throws( function() { smwApi.fetch( {'foo': 'bar' } , '' ); }, pass + 'an error was raised (query was an object)' );
+
+ // Ajax
+ var query = '[[Modification date::+]]|?Modification date|limit=10|offset=0';
+
+ smwApi.fetch( query )
+ .done( function ( results ) {
+
+ assert.ok( true, pass + 'of an positive server response' );
+ assert.ok( results instanceof Object, pass + 'an object was returned' );
+
+ done();
+ } );
+
+ } );
+
+ /**
+ * Test caching
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'fetch() cache test', function ( assert ) {
+ assert.expect( 4 );
+
+ var smwApi = new smw.api();
+
+ // Ajax
+ var queryString = '[[Modification date::+]]|?Modification date|?Modification date|?Modification date|limit=100';
+ var undefCacheDone = assert.async();
+
+ smwApi.fetch( queryString )
+ .done( function ( results ) {
+ assert.equal( results.isCached, false , pass + ' caching is set "undefined" and results are not cached' );
+ undefCacheDone();
+ } );
+
+ var falseCacheDone = assert.async();
+ smwApi.fetch( queryString, false )
+ .done( function ( results ) {
+ assert.equal( results.isCached , false , pass + ' caching is set "false" and results are not cached' );
+ falseCacheDone();
+ } );
+
+ // Make sure the cache is initialized otherwise the asserts will fail
+ // for the first test run
+ var cacheDone = assert.async();
+ smwApi.fetch( queryString, true )
+ .done( function () {
+ var firstCacheDone = assert.async();
+
+ smwApi.fetch( queryString, 60 * 1000 )
+ .done( function ( results ) {
+ assert.equal( results.isCached , true , pass + ' caching is set to "60 * 1000" and results are cached' );
+ firstCacheDone();
+ } );
+
+ var otherCacheDone = assert.async();
+ smwApi.fetch( queryString, true )
+ .done( function ( results ) {
+ assert.equal( results.isCached , true , pass + ' caching is set "true" and results are cached' );
+ otherCacheDone();
+ } );
+
+ cacheDone();
+ } );
+
+ } );
+
+ /**
+ * Test fetch vs. a normal $.ajax call
+ *
+ * @since: 1.9
+ * @ignore since 2013 - should this test be removed?
+ */
+ QUnit.test( 'fetch() vs. $.ajax', function ( assert ) {
+ assert.expect( 3 );
+
+ var ajaxDone = assert.async();
+ var fetchDone = assert.async();
+ var cachedFetchDone = assert.async();
+
+ var smwApi = new smw.api();
+ var startDate;
+
+ // Ajax
+ var queryString = '[[Modification date::+]]|?Modification date|?Modification date|?Modification date|limit=100';
+
+ startDate = new Date();
+
+ $.ajax( {
+ url: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ data: {
+ 'action': 'ask',
+ 'format': 'json',
+ 'query' : queryString
+ }
+ } )
+ .done( function ( results ) {
+ assert.ok( results, 'Fetch ' + results.query.meta.count + ' items using $.ajax which took: ' + ( new Date().getTime() - startDate.getTime() ) + ' ms' );
+ startDate = new Date();
+ ajaxDone();
+ } );
+
+ smwApi.fetch( queryString )
+ .done( function ( results ) {
+ assert.ok( results, 'Fetch ' + results.query.meta.count + ' items using smw.Api.fetch() which took: ' + ( new Date().getTime() - startDate.getTime() ) + ' ms' );
+ fetchDone();
+ } );
+
+ smwApi.fetch( queryString, true )
+ .done( function ( results ) {
+ assert.ok( results, 'Fetch ' + results.query.meta.count + ' items using smw.Api.fetch() which were cached and took: ' + ( new Date().getTime() - startDate.getTime() ) + ' ms' );
+ cachedFetchDone();
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.data.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.data.test.js
new file mode 100644
index 00000000..634dc34e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.data.test.js
@@ -0,0 +1,333 @@
+/**
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/wiki/QUnit
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ignore
+ *
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Tests methods provided by ext.smw.data.js
+ * @ignore
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.Data', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+ var testString = '{\"query\":{\"result\":{\"printrequests\":[{\"label\":\"\",\"typeid\":\"_wpg\",\"mode\":2},{\"label\":\"Has test date\",\"typeid\":\"_dat\",\"mode\":1},{\"label\":\"Has test page\",\"typeid\":\"_wpg\",\"mode\":1},{\"label\":\"Has test string\",\"typeid\":\"_str\",\"mode\":1},{\"label\":\"Has test text\",\"typeid\":\"_txt\",\"mode\":1},{\"label\":\"Has test url\",\"typeid\":\"_uri\",\"mode\":1}],\"results\":{\"DataitemFactory\\/1\":{\"printouts\":{\"Has test date\":[\"947548800\"],\"Has test page\":[{\"fulltext\":\"File:FooBarfoo.png\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/File:FooBarfoo.png\",\"namespace\":6},{\"fulltext\":\"Foo page\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/Foo_page\",\"namespace\":0}],\"Has test string\":[\"Foobar string\"],\"Has test text\":[\"foo foo string\"],\"Has test url\":[\"http:\\/\\/localhost\\/mw\\/foobarfoo\"]},\"fulltext\":\"DataitemFactory\\/1\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/DataitemFactory\\/1\",\"namespace\":0},\"DataitemFactory\\/2\":{\"printouts\":{\"Has test date\":[\"1010707200\"],\"Has test page\":[{\"fulltext\":\"File:Foo.png\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/File:Foo.png\",\"namespace\":6},{\"fulltext\":\"Bar page\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/Bar_page\",\"namespace\":0}],\"Has test string\":[\"foo string\"],\"Has test text\":[\"fooBar string\"],\"Has test url\":[\"http:\\/\\/localhost\\/mw\\/foo\"]},\"fulltext\":\"DataitemFactory\\/2\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/DataitemFactory\\/2\",\"namespace\":0},\"DataitemFactory\\/3\":{\"printouts\":{\"Has test date\":[\"1010725200\"],\"Has test page\":[{\"fulltext\":\"Foo page\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/Foo_page\",\"namespace\":0},{\"fulltext\":\"File:FooBar.png\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/File:FooBar.png\",\"namespace\":6}],\"Has test string\":[\"bar string\"],\"Has test text\":[\"fooBar foo string\"],\"Has test url\":[\"http:\\/\\/localhost\\/index.php?title=foo\"]},\"fulltext\":\"DataitemFactory\\/3\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/DataitemFactory\\/3\",\"namespace\":0}},\"meta\":{\"hash\":\"c26caabe31af28817c3f87f26cfd0a3d\",\"count\":3,\"offset\":0}},\"ask\":{\"conditions\":\"[[Has test string::+]]\",\"parameters\":{\"limit\":50,\"offset\":0,\"format\":\"datatables\",\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\",\"class\":\"\",\"theme\":\"bootstrap\"},\"printouts\":[\"?Has test date\",\"?Has test page\",\"?Has test string\",\"?Has test text\",\"?Has test url\"]}},\"version\":\"0.1\"}';
+ var subjectLessResult = '{\"query\":{\"result\":{\"printrequests\":[{\"label\":\"Modification date\",\"typeid\":\"_dat\",\"mode\":1}],\"results\":{\"Concepttest3\":{\"printouts\":{\"Modification date\":[\"1358906761\"]}},\"Concepttest4\":{\"printouts\":{\"Modification date\":[\"1358905485\"]}},\"Category:Concepts\":{\"printouts\":{\"Modification date\":[\"1358896550\"]}}},\"meta\":{\"hash\":\"8c727bd6aa52f47caa39bd0c1b93ee59\",\"count\":3,\"offset\":0}},\"ask\":{\"conditions\":\"[[Modification date::+]]\",\"parameters\":{\"limit\":3,\"offset\":0,\"format\":\"datatables\",\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"-\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\",\"class\":\"\",\"theme\":\"bootstrap\"},\"printouts\":[\"?Modification date\"]}},\"version\":\"0.1\"}';
+ var unknownType = '{\"query\":{\"result\":{\"printrequests\":[{\"label\":\"\",\"typeid\":\"_wpg\",\"mode\":2},{\"label\":\"Has description\",\"typeid\":\"_foo\",\"mode\":1}],\"results\":{\"File:IMG0027040123.jpg\":{\"printouts\":{\"Has description\":[\"Mauris pellentesque aliquet leo Nam nibh metus facilisi et sem laoreet. Netus ipsum montes et a neque in pulvinar nibh\",\"Facilisi et sem laoreet. Netus ipsum montes et a neque in pulvinar nibh\"]},\"fulltext\":\"File:IMG0027040123.jpg\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/File:IMG0027040123.jpg\",\"namespace\":6}},\"meta\":{\"hash\":\"a116c98703f48d231ec4c8eaee4038f8\",\"count\":1,\"offset\":0}},\"ask\":{\"conditions\":\"[[Has description::+]]\",\"parameters\":{\"limit\":1,\"offset\":0,\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\"},\"printouts\":[\"?Has description\"]}},\"version\":\"0.1\"}';
+ var numberType = '{\"query\":{\"result\":{\"printrequests\":[{\"label\":\"\",\"typeid\":\"_wpg\",\"mode\":2},{\"label\":\"Has number\",\"typeid\":\"_num\",\"mode\":1}],\"results\":{\"Image\\/1\":{\"printouts\":{\"Has number\":[1220,1320,99]},\"fulltext\":\"Image\\/1\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/Image\\/1\",\"namespace\":0,\"exists\":true}},\"meta\":{\"hash\":\"c2aa1dc43849ad148c9649e818eeb29b\",\"count\":1,\"offset\":0}},\"ask\":{\"conditions\":\"[[has number::+]]\",\"parameters\":{\"limit\":50,\"offset\":0,\"format\":\"datatables\",\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\",\"class\":\"\",\"theme\":\"bootstrap\"},\"printouts\":[\"?Has number\"]}},\"version\":\"0.1\"}';
+ var quantityType = '{\"query\":{\"result\":{\"printrequests\":[{\"label\":\"\",\"typeid\":\"_wpg\",\"mode\":2},{\"label\":\"Area\",\"typeid\":\"_qty\",\"mode\":1}],\"results\":{\"Berlin\":{\"printouts\":{\"Area\":[{\"value\":891.85,\"unit\":\"km\\u00b2\"}]},\"fulltext\":\"Berlin\",\"fullurl\":\"http:\\/\\/localhost\\/mw\\/index.php\\/Berlin\",\"namespace\":0,\"exists\":true}},\"meta\":{\"hash\":\"f0f072f414c3e814c847aff1ba87dfb3\",\"count\":1,\"offset\":0}},\"ask\":{\"conditions\":\"[[Area::+]]\",\"parameters\":{\"limit\":50,\"offset\":0,\"format\":\"datatables\",\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\",\"class\":\"\",\"theme\":\"bootstrap\"},\"printouts\":[\"?Area\"]}},\"version\":\"0.2.5\"}';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.Data();
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem instance was accessible' );
+
+ } );
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'comparison $.parseJSON() vs. smw.Api.parse()', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+ var startDate;
+ var smwApi = new smw.Api();
+
+ startDate = new Date();
+ result = $.parseJSON( testString );
+ assert.ok( result, 'Using parseJSON took: ' + ( new Date().getTime() - startDate.getTime() ) + ' ms' );
+
+ startDate = new Date();
+ result = smwApi.parse( testString );
+ assert.ok( result, 'using smw.Api.parse took: ' + ( new Date().getTime() - startDate.getTime() ) + ' ms' );
+
+ } );
+
+ /**
+ * Test smw.dataItem.property factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.property factory test', function ( assert ) {
+ assert.expect( 5 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( testString );
+
+ $.map ( result.query.result.results['DataitemFactory/1'].printouts, function( value, key ) {
+ if ( value instanceof smw.dataItem.property ){
+ assert.equal( value.getLabel(), key , pass + 'the parser returned ' + key );
+ }
+ } );
+
+ } );
+
+ /**
+ * Test subject less
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.property subject less factory test', function ( assert ) {
+ assert.expect( 3 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( subjectLessResult );
+
+ $.map ( result.query.result.results, function( printouts, head ) {
+ $.map ( printouts, function( values ) {
+ $.map ( values, function( value, key ) {
+ if ( value instanceof smw.dataItem.property ){
+ assert.equal( value.getLabel(), key , pass + 'the parser returned ' + key + ' for ' + head );
+ }
+ } );
+ } );
+ } );
+
+ } );
+
+ /**
+ * Test smw.dataItem.wikiPage factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.wikiPage subject factory test', function ( assert ) {
+ assert.expect( 3 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( testString );
+
+ assert.ok( result.query.result.results['DataitemFactory/1'] instanceof smw.dataItem.wikiPage, pass + 'the parser returned a smw.dataItem.wikiPage object' );
+ assert.equal( result.query.result.results['DataitemFactory/1'].getHtml(), 'DataitemFactory/1', pass + 'the object method .getHtml() was accessible' );
+ assert.equal( result.query.result.results['DataitemFactory/1'].getUri(), 'http://localhost/mw/index.php/DataitemFactory/1', pass + 'the object method .getUri() was accessible' );
+
+ } );
+
+ /**
+ * Test smw.dataItem.wikiPage factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.wikiPage multiValue factory test', function ( assert ) {
+ assert.expect( 4 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( testString );
+
+ var expectedMultiValue = ['File:FooBarfoo.png', 'Foo page'];
+
+ $.map ( result.query.result.results['DataitemFactory/1'].printouts, function( values ) {
+ if ( values instanceof smw.dataItem.property ){
+ $.map ( values, function( value, key ) {
+ if ( value instanceof smw.dataItem.wikiPage ){
+ assert.ok( value instanceof smw.dataItem.wikiPage, pass + 'the parser returned a smw.dataItem.wikiPage object' );
+ assert.equal( value.getHtml(), expectedMultiValue[key] , pass + 'the parser returned ' + expectedMultiValue[key] );
+ }
+ } );
+ }
+ } );
+
+ } );
+
+ /**
+ * Test smw.dataItem.time factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.time factory', function ( assert ) {
+ assert.expect( 4 );
+
+ // Use as helper to fetch language dep. month name
+ var monthNames = [];
+ $.map ( mw.config.get( 'wgMonthNames' ), function( value ) {
+ if( value !== '' ){
+ monthNames.push( value );
+ }
+ } );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( testString );
+
+ $.map ( result.query.result.results['DataitemFactory/1'].printouts, function( values ) {
+ if ( values instanceof smw.dataItem.property ){
+ $.map ( values, function( value ) {
+ if ( value instanceof smw.dataItem.time ){
+ assert.ok( value instanceof smw.dataItem.time, pass + 'the parser returned a smw.dataItem.time object' );
+ assert.equal( value.getMediaWikiDate(), '11 ' + monthNames[0] + ' 2000' , pass + 'the parser returned "11 '+ monthNames[0] +' 2000"' );
+ }
+ } );
+ }
+ } );
+
+ $.map ( result.query.result.results['DataitemFactory/3'].printouts, function( values ) {
+ if ( values instanceof smw.dataItem.property ){
+ $.map ( values, function( value ) {
+ if ( value instanceof smw.dataItem.time ){
+ assert.ok( value instanceof smw.dataItem.time, pass + 'the parser returned a smw.dataItem.time object' );
+ assert.equal( value.getMediaWikiDate(), '11 '+ monthNames[0] +' 2002 05:00:00' , pass + 'the parser returned "11 '+ monthNames[0] +' 2002 05:00:00"' );
+ }
+ } );
+ }
+ } );
+
+ } );
+
+ /**
+ * Test smw.dataItem.uri factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.uri factory', function ( assert ) {
+ assert.expect( 4 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( testString );
+
+ $.map ( result.query.result.results['DataitemFactory/2'].printouts, function( values ) {
+ if ( values instanceof smw.dataItem.property ){
+ $.map ( values, function( value ) {
+ if ( value instanceof smw.dataItem.uri ){
+ assert.ok( value instanceof smw.dataItem.uri, pass + 'the parser returned a smw.dataItem.uri object' );
+ assert.equal( value.getUri(), 'http://localhost/mw/foo' , pass + 'getUri() returned "http://localhost/mw/foo"' );
+ assert.equal( value.getHtml( false ), 'http://localhost/mw/foo' , pass + 'getHtml( false ) returned "http://localhost/mw/foo"' );
+ assert.equal( value.getHtml( true ), '<a href=\"http://localhost/mw/foo\">http://localhost/mw/foo</a>' , pass + 'getHtml( true ) returned a href element' );
+ }
+ } );
+ }
+ } );
+ } );
+
+ /**
+ * Test number factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.number factory', function ( assert ) {
+ assert.expect( 3 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( numberType );
+ var expectedNumber = [1220,1320,99];
+ var i=0;
+
+ Object.values( result.query.result.results ).forEach( function ( result ) {
+ Object.values( result.printouts ).forEach( function ( property ) {
+ if ( property instanceof smw.dataItem.property ){
+ $.map ( property, function( value ) {
+ if ( value instanceof smw.dataItem.number ){
+ assert.equal( value.getNumber(), expectedNumber[i] , pass + 'getNumber() returned ' + expectedNumber[i] );
+ i++;
+ }
+ } );
+ }
+ } );
+ } );
+ } );
+
+ /**
+ * Test quantity factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataValue.quantity factory', function ( assert ) {
+ assert.expect( 2 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( quantityType );
+ var expected = { value: 891.85, unit: 'km²' };
+
+ Object.values( result.query.result.results ).forEach( function ( result ) {
+ Object.values( result.printouts ).forEach( function ( property ) {
+ if ( property instanceof smw.dataItem.property ){
+ $.map ( property, function( value ) {
+ if ( value instanceof smw.dataValue.quantity ){
+ assert.equal( value.getValue(), expected.value , pass + 'getValue() returned ' + expected.value );
+ assert.equal( value.getUnit(), expected.unit , pass + 'getUnit() returned ' + expected.unit );
+ }
+ } );
+ }
+ } );
+ } );
+ } );
+
+
+ /**
+ * Test unlisted factory
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'smw.dataItem.unknown factory', function ( assert ) {
+ assert.expect( 4 );
+
+ // Testing indirect via the smw.Api otherwise the whole JSON parsing
+ // needs to be copied
+ var smwApi = new smw.Api();
+ var result = smwApi.parse( unknownType );
+
+ Object.values( result.query.result.results ).forEach( function ( result ) {
+ Object.values( result.printouts ).forEach( function ( property ) {
+ if ( property instanceof smw.dataItem.property ){
+ $.map ( property, function( value ) {
+ if ( value instanceof smw.dataItem.unknown ){
+ assert.ok( value instanceof smw.dataItem.unknown, pass + 'the parser returned a smw.dataItem.unknown object' );
+ assert.equal( value.getDIType(), '_foo' , pass + 'getDIType() returned an unknown type _foo' );
+ }
+ } );
+ }
+ } );
+ } );
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.number.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.number.test.js
new file mode 100644
index 00000000..fe21d062
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.number.test.js
@@ -0,0 +1,73 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.number', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ // Data provider
+ var testCases = [
+ { test: [ 1 ], expected: [ 1 ] },
+ { test: [ 0.0001 ], expected: [ 0.0001 ] },
+ { test: [ 1000 ], expected: [ 1000 ] }
+ ];
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 2 );
+
+ var result = new smw.dataItem.number( 3 );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.number instance was accessible' );
+
+ assert.throws( function() {
+ new smw.dataItem.number( 'foo' );
+ }, pass + 'an error was raised due to wrong type' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.number( 3 );
+ assert.equal( result.getDIType(), '_num', pass + 'returned _num' );
+
+ } );
+
+ /**
+ * Test getNumber
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getNumber', function ( assert ) {
+ assert.expect( 3 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.number( testCase.test[0] );
+ assert.equal( result.getNumber(), testCase.expected[0] , pass + 'returned ' + testCase.expected[0] );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.property.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.property.test.js
new file mode 100644
index 00000000..944533e9
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.property.test.js
@@ -0,0 +1,60 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.property', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.property( 'Has test' );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.property instance was accessible' );
+
+ } );
+
+ /**
+ * Test getLabel
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getLabel', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.property( 'Has test' );
+ assert.equal( result.getLabel(), 'Has test', pass + 'a label was returned' );
+
+ } );
+
+ /**
+ * Test getHtml
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getHtml', function ( assert ) {
+ assert.expect( 2 );
+
+ var result = new smw.dataItem.property( 'Has type' );
+ var href = mw.util.wikiGetlink( 'Property:');
+ assert.equal( result.getHtml(), 'Has type', pass + 'a text label was returned' );
+ assert.equal( result.getHtml( true ), '<a href=\"' + href + 'Has_type\">Has type</a>', pass + 'a href link was returned' );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.text.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.text.test.js
new file mode 100644
index 00000000..70a1f02a
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.text.test.js
@@ -0,0 +1,80 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.text', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 4 );
+
+ var result = new smw.dataItem.text( 'foo' );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.text instance was accessible' );
+
+ assert.throws( function() {
+ new smw.dataItem.text( {} );
+ }, pass + 'an error was raised due to the wrong type' );
+
+ assert.throws( function() {
+ new smw.dataItem.text( [] );
+ }, pass + 'an error was raised due to the wrong type' );
+
+ assert.throws( function() {
+ new smw.dataItem.text( 3 );
+ }, pass + 'an error was raised due to the wrong type' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ result = new smw.dataItem.text( 'foo' );
+ assert.equal( result.getDIType(), '_txt', pass + 'getDIType() returned _txt' );
+
+ result = new smw.dataItem.text( 'bar', '_str' );
+ assert.equal( result.getDIType(), '_str', pass + 'getDIType() returned _str' );
+
+ } );
+
+ /**
+ * Test getText
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getText', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ var testString = 'Lorem ipsum dolor sit ...';
+
+ result = new smw.dataItem.text( testString );
+ assert.equal( result.getText(), testString, pass + 'getText() returned ' + testString );
+ assert.equal( result.getString(), testString, pass + 'getString() returned ' + testString );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.time.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.time.test.js
new file mode 100644
index 00000000..cf0f0aed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.time.test.js
@@ -0,0 +1,131 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.time', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.time( '1362200400' );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.time instance was accessible' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.time( '1362200400' );
+ assert.equal( result.getDIType(), '_dat', pass + 'returned _dat' );
+
+ } );
+
+ /**
+ * Test getUri
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getMwTimestamp', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ result = new smw.dataItem.time( '1362200400' );
+ assert.equal( result.getMwTimestamp(), '1362200400', pass + 'returned a MW timestamp' );
+
+ result = new smw.dataItem.time( 1362200400 );
+ assert.equal( result.getMwTimestamp(), '1362200400', pass + 'returned a MW timestamp' );
+
+ } );
+
+ /**
+ * Test getDate
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDate', function ( assert ) {
+ assert.expect( 1 );
+
+ var result;
+
+ result = new smw.dataItem.time( '1362200400' );
+ assert.ok( result.getDate() instanceof Date, pass + 'returned a JavaScript Date instance' );
+
+ } );
+
+ /**
+ * Test getISO8601Date
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getISO8601Date', function ( assert ) {
+ assert.expect( 1 );
+
+ var result;
+
+ result = new smw.dataItem.time( '1362200400' );
+ assert.equal( result.getISO8601Date(), '2013-03-02T05:00:00.000Z', pass + 'returned a ISO string date/time' );
+
+ } );
+
+ /**
+ * Test getTimeString
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getTimeString', function ( assert ) {
+ assert.expect( 1 );
+
+ var result;
+
+ result = new smw.dataItem.time( '1362200400' );
+ assert.equal( result.getTimeString(), '05:00:00', pass + 'returned a time string' );
+
+ } );
+
+ /**
+ * Test getMediaWikiDate
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getMediaWikiDate', function ( assert ) {
+ assert.expect( 1 );
+
+ var result;
+
+ // Use as helper to fetch language dep. month name
+ var monthNames = [];
+ $.map ( mw.config.get( 'wgMonthNames' ), function( index ) {
+ if( index !== '' ){
+ monthNames.push( index );
+ }
+ } );
+
+ result = new smw.dataItem.time( '1362200400' );
+ assert.equal( result.getMediaWikiDate(), '2 ' + monthNames[2] + ' 2013 05:00:00', pass + 'returned a MW date and time formatted string' );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.unknown.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.unknown.test.js
new file mode 100644
index 00000000..99a0fb00
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.unknown.test.js
@@ -0,0 +1,68 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.unknown', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ // Data provider
+ var testCases = [
+ { test: [ '' ], expected: [ null, null ] } ,
+ { test: [ 'Foo', '_fooBar' ], expected: [ 'Foo', '_fooBar' ] }
+ ];
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.unknown( 'foo', '_bar' );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.unknown instance was accessible' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.unknown( 'foo', '_bar' );
+ assert.equal( result.getDIType(), '_bar', pass + 'returned "_bar"' );
+
+ } );
+
+ /**
+ * Test getValue
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getValue', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.unknown( testCase.test[0], testCase.test[1] );
+ assert.equal( result.getValue(), testCase.expected[0] , pass + 'returned ' + testCase.expected[0] );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.uri.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.uri.test.js
new file mode 100644
index 00000000..b8dce290
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.uri.test.js
@@ -0,0 +1,90 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.uri', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ // Data provider
+ var testCases = [
+ { test: [ '' ], expected: [ null, null ] } ,
+ { test: [ 'http://fooBar/test' ], expected: [ 'http://fooBar/test', '<a href=\"http://fooBar/test\">http://fooBar/test</a>' ] }
+ ];
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.uri( 'http://foo.com/test/' );
+ assert.ok( result instanceof Object, pass + 'the smw.dataItem.uri instance was accessible' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.uri( 'http://foo.com/test/' );
+ assert.equal( result.getDIType(), '_uri', pass + 'returned _uri' );
+
+ } );
+
+ /**
+ * Test getUri
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getUri', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.uri( testCase.test[0] );
+ assert.equal( result.getUri(), testCase.expected[0] , pass + 'returned ' + testCase.expected[0] );
+ } );
+
+ } );
+
+ /**
+ * Test getHtml
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getHtml', function ( assert ) {
+ assert.expect( 4 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.uri( testCase.test[0] );
+ assert.equal( result.getHtml(), testCase.expected[0] , pass + 'returned ' + testCase.expected[0] );
+ } );
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.uri( testCase.test[0] );
+ assert.equal( result.getHtml( true ), testCase.expected[1] , pass + 'returned ' + testCase.expected[1] );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.wikiPage.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.wikiPage.test.js
new file mode 100644
index 00000000..ad65dbd3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataItem.wikiPage.test.js
@@ -0,0 +1,240 @@
+/*!
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/wiki/QUnit
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataItem.wikiPage', QUnit.newMwEnvironment() );
+
+ // Data provider
+ var testCases = [
+ {
+ test: [ '', '', '' ],
+ expected: { 'name': null, 'text': null, 'uri': null, 'ns': 0, 'html': null, 'known': true }
+ } ,
+ {
+ test: [ 'foo', '', '' ],
+ expected: { 'name': 'foo', 'text': 'foo', 'uri': null, 'ns': 0, 'html': 'foo', 'known': true }
+ },
+ {
+ test: [ 'bar', '', 6 ],
+ expected: { 'name': 'bar', 'text': 'bar', 'uri': null, 'ns': 6, 'html': 'bar', 'known': true }
+ },
+ {
+ test: [ 'bar', '', 0, true ],
+ expected: { 'name': 'bar', 'text': 'bar', 'uri': null, 'ns': 0, 'html': 'bar', 'known': true }
+ },
+ {
+ test: [ 'bar', '', 2, false ],
+ expected: { 'name': 'bar', 'text': 'bar', 'uri': null, 'ns': 2, 'html': 'bar', 'known': false }
+ },
+ {
+ test: [ 'fooBar', 'http://fooBar', 0, false ],
+ expected: { 'name': 'fooBar', 'text': 'fooBar', 'uri': 'http://fooBar', 'ns': 0, 'html': '<a href=\"http://fooBar\" class=\"new\">fooBar</a>', 'known': false }
+ },
+ {
+ test: [ 'fooBar#_9a0c8abb8ef729b5c7', 'http://fooBar#_9a0c8abb8ef729b5c7', 0, false ],
+ expected: { 'name': 'fooBar#_9a0c8abb8ef729b5c7', 'text': 'fooBar', 'uri': 'http://fooBar#_9a0c8abb8ef729b5c7', 'ns': 0, 'html': '<a href=\"http://fooBar#_9a0c8abb8ef729b5c7\" class=\"new\">fooBar</a>', 'known': false }
+ },
+ {
+ test: [ 'fooBar', 'http://fooBar', 0, true ],
+ expected: { 'name': 'fooBar', 'text': 'fooBar', 'uri': 'http://fooBar', 'ns': 0, 'html': '<a href=\"http://fooBar\">fooBar</a>', 'known': true }
+ },
+ {
+ test: [ 'Foo#_QUERY9a665a578eb95c1', 'http://Foo#_QUERY9a665a578eb95c1', 0, true ],
+ expected: { 'name': 'Foo#_QUERY9a665a578eb95c1', 'text': 'Foo', 'uri': 'http://Foo#_QUERY9a665a578eb95c1', 'ns': 0, 'html': '<a href=\"http://Foo#_QUERY9a665a578eb95c1\">Foo</a>', 'known': true }
+ },
+ {
+ test: [ 'Foo#_QUERY#9a665a578eb95c1', 'http://Foo#_QUERY#9a665a578eb95c1', 0, true ],
+ expected: { 'name': 'Foo#_QUERY#9a665a578eb95c1', 'text': 'Foo', 'uri': 'http://Foo#_QUERY#9a665a578eb95c1', 'ns': 0, 'html': '<a href=\"http://Foo#_QUERY#9a665a578eb95c1\">Foo</a>', 'known': true }
+ }
+ ];
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.wikiPage( 'foo', 'bar' );
+ assert.ok( result instanceof Object, 'the smw.dataItem.wikiPage instance was accessible' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataItem.wikiPage( 'foo', 'bar' );
+ assert.equal( result.getDIType(), '_wpg', 'returned _wpg' );
+
+ } );
+
+ /**
+ * Test getPrefixedText
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getPrefixedText', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2] );
+ assert.equal( result.getPrefixedText(), testCase.expected.name , 'returned ' + testCase.expected.name );
+ } );
+
+ } );
+
+ /**
+ * Test getText
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getText', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2] );
+ assert.equal( result.getText(), testCase.expected.text , 'returned ' + testCase.expected.text );
+ } );
+
+ } );
+
+ /**
+ * Test getUri
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getUri', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2] );
+ assert.equal( result.getUri(), testCase.expected.uri , 'returned ' + testCase.expected.uri );
+ } );
+
+ } );
+
+ /**
+ * Test getNamespaceId
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getNamespaceId', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2] );
+ assert.equal( result.getNamespaceId(), testCase.expected.ns , 'returned ' + testCase.expected.ns );
+ } );
+
+ } );
+
+ /**
+ * Test getTitle
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getTitle', function ( assert ) {
+ assert.expect( 2 );
+
+ var wikiPage = new smw.dataItem.wikiPage( 'File:foo', 'bar' );
+ var title = new mw.Title( 'File:foo' );
+
+ assert.ok( wikiPage.getTitle() instanceof mw.Title, '.getTitle() returned a Title instance' );
+ assert.deepEqual( wikiPage.getTitle(), title, 'returned a Title instance' );
+
+ } );
+
+ /**
+ * Test isKnown
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'isKnown', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2], testCase.test[3] );
+ assert.equal( result.isKnown(), testCase.expected.known, 'returned ' + testCase.expected.known );
+ } );
+
+ } );
+
+ /**
+ * Test getHtml
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getHtml( false )', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2] );
+ assert.equal( result.getHtml( false ), testCase.expected.text, 'returned ' + testCase.expected.text );
+ } );
+
+ } );
+
+ /**
+ * Test getHtml
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getHtml( true )', function ( assert ) {
+ assert.expect( 10 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataItem.wikiPage( testCase.test[0], testCase.test[1], testCase.test[2], testCase.test[3] );
+ assert.equal( result.getHtml( true ), testCase.expected.html, 'returned ' + testCase.expected.html );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataValue.quantity.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataValue.quantity.test.js
new file mode 100644
index 00000000..f37c6c75
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/data/ext.smw.dataValue.quantity.test.js
@@ -0,0 +1,90 @@
+/**
+ * QUnit tests
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ *
+ * @licence GNU GPL v2 or later
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.dataValue.quantity', QUnit.newMwEnvironment() );
+
+ var pass = 'Passes because ';
+
+ // Data provider
+ var testCases = [
+ { test: { value: 1 }, expected: { value: 1 } },
+ { test: { value: 0.0001, unit: 'km²' }, expected: { value: 0.0001, unit: 'km²' } },
+ { test: { value: 1000, unit: 'm²' }, expected: { value: 1000, unit: 'm²' } }
+ ];
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 2 );
+
+ var result = new smw.dataValue.quantity( 3 );
+ assert.ok( result instanceof Object, pass + 'the smw.dataValue.quantity instance was accessible' );
+
+ assert.throws( function() {
+ new smw.dataValue.quantity( 'foo' );
+ }, pass + 'an error was raised due to wrong type' );
+
+ } );
+
+ /**
+ * Test type
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getDIType', function ( assert ) {
+ assert.expect( 1 );
+
+ var result = new smw.dataValue.quantity( 3 );
+ assert.equal( result.getDIType(), '_qty', pass + 'returned _qty' );
+
+ } );
+
+ /**
+ * Test getValue
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getValue', function ( assert ) {
+ assert.expect( 3 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataValue.quantity( testCase.test.value, testCase.test.unit );
+ assert.equal( result.getValue(), testCase.expected.value , pass + 'returned ' + testCase.expected.value );
+ } );
+
+ } );
+
+ /**
+ * Test getUnit
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'getUnit', function ( assert ) {
+ assert.expect( 3 );
+
+ var result;
+
+ $.map( testCases, function ( testCase ) {
+ result = new smw.dataValue.quantity( testCase.test.value, testCase.test.unit );
+ assert.equal( result.getUnit(), testCase.expected.unit , pass + 'returned ' + testCase.expected.unit );
+ } );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/ext.smw.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/ext.smw.test.js
new file mode 100644
index 00000000..68e5d304
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/ext.smw.test.js
@@ -0,0 +1,216 @@
+/*!
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/wiki/QUnit
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.9
+ *
+ * @file
+ *
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw', QUnit.newMwEnvironment() );
+
+ /**
+ * Test initialization and accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'init', function ( assert ) {
+ assert.expect( 17 );
+
+ assert.ok( smw instanceof Object, 'the smw instance was accessible' );
+
+ assert.equal( $.type( smw.log ), 'function', '.log() was accessible' );
+ assert.equal( $.type( smw.msg ), 'function','.msg() was accessible' );
+ assert.equal( $.type( smw.debug ), 'function', '.debug() was accessible' );
+ assert.equal( $.type( smw.version ), 'function', '.version() was accessible' );
+
+ assert.equal( $.type( smw.settings.getList ), 'function', '.settings.getList() was accessible' );
+ assert.equal( $.type( smw.settings.get ), 'function', '.settings.get() was accessible' );
+
+ assert.equal( $.type( smw.formats.getName ), 'function', '.formats.getName() was accessible' );
+ assert.equal( $.type( smw.formats.getList ), 'function', '.formats.getList() was accessible' );
+
+ assert.equal( $.type( smw.async.isEnabled ), 'function', '.async.isEnabled() was accessible' );
+ assert.equal( $.type( smw.async.load ), 'function', '.async.load() was accessible' );
+
+ assert.equal( $.type( smw.util.clean ), 'function', '.util.clean() was accessible' );
+ assert.equal( $.type( smw.util.ucFirst ), 'function', '.util.ucFirst() was accessible' );
+ assert.equal( $.type( smw.util.namespace ), 'object', '.util.namespace object was accessible' );
+ assert.equal( $.type( smw.util.namespace.getList ), 'function', '.util.namespace.getList() was accessible' );
+ assert.equal( $.type( smw.util.namespace.getId ), 'function', '.util.namespace.getId() was accessible' );
+ assert.equal( $.type( smw.util.namespace.getName ), 'function', '.util.namespace.getName() was accessible' );
+
+ } );
+
+ /**
+ * Test settings function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'settings', function ( assert ) {
+ assert.expect( 4 );
+
+ assert.equal( $.type( smw.settings.getList() ), 'object', '.getList() returned a list of settings object' );
+ assert.equal( $.type( smw.settings.get( 'smwgQMaxLimit' ) ), 'number', '.get( "smwgQMaxLimit" ) returned a value for the key' );
+ assert.equal( smw.settings.get( 'lula' ), undefined, '.get( "lula" ) returned undefined for an unknown key' );
+ assert.equal( smw.settings.get(), undefined, '.get() returned undefined for an empty key' );
+
+ } );
+
+ /**
+ * Test util function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'util', function ( assert ) {
+ assert.expect( 3 );
+
+ assert.equal( smw.util.clean( ' Foo | ; : - < >_= () {} bar ' ), 'Foo_;_:_-__=_()_bar', '.clean() returned a cleaned string' );
+ assert.equal( smw.util.clean( 'Foo | ; : - < >_= () {} bar' ), 'Foo_;_:_-__=_()_bar', '.clean() returned a cleaned string' );
+ assert.equal( smw.util.ucFirst( 'foo Foo bar' ), 'Foo Foo bar', '.ucFirst() returned a capitalized string' );
+
+ } );
+
+ /**
+ * Test namespace function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'util.namespace', function ( assert ) {
+ assert.expect( 7 );
+
+ assert.equal( $.type( smw.util.namespace.getList() ), 'object', '.getList() returned a list of namespaces' );
+
+ assert.equal( $.type( smw.util.namespace.getId( 'property' ) ), 'number', '.getId( "property" ) returned a number' );
+ assert.equal( $.type( smw.util.namespace.getId( 'Property' ) ), 'number', '.getId( "Property" ) returned a number' );
+ assert.equal( $.type( smw.util.namespace.getId( 'concept' ) ), 'number', '.getId( "concept" ) returned a number' );
+ assert.equal( smw.util.namespace.getId( 'lula' ), undefined, '.getId( "lula" ) returned undefined for an unknown key' );
+
+ assert.equal( $.type( smw.util.namespace.getName( 'property' ) ), 'string', '.getName( "property" ) returned a string' );
+ assert.equal( smw.util.namespace.getName( 'lula' ), undefined, '.getName( "lula" ) returned undefined for an unknown key' );
+
+ } );
+
+ /**
+ * Test async function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'async', function ( assert ) {
+ assert.expect( 7 );
+
+ var done = assert.async();
+
+ var context;
+ var argument;
+ var result;
+
+ assert.throws( function() {
+ smw.async.load( context );
+ }, '.async.load() thrown an error because of a missing method callback' );
+
+ var test0 = function() {
+ return $( this ).append( '<span id=test-00></span>' );
+ };
+
+ context = $( '<div id="test-0"></div>', '#qunit-fixture' );
+ result = smw.async.load( context, test0 );
+ assert.ok( context.find( '#' + 'test-00' ), '.async.load() was executed and created an element' );
+
+ var test1 = function( id ) {
+ return $( this ).append( '<span id=' + id + '></span>' );
+ };
+
+ argument = 'lula';
+ context = $( '<div id="test-1"></div>', '#qunit-fixture' );
+ result = smw.async.load( context, test1, argument );
+ assert.ok( context.find( '#' + argument ), '.async.load() was executed and created an element using the invoked argument' );
+
+ var test2 = function( options ) {
+ return $( this ).append( '<span id=' + options.id + '></span>' );
+ };
+
+ argument = { 'id': 'lula-2' };
+ context = $( '<div id="test-2"></div>', '#qunit-fixture' );
+ result = smw.async.load( context, test2, argument );
+ assert.ok( context.find( '#' + argument.id ), '.async.load() was executed and created an element using the invoked argument' );
+
+ argument = 'lala';
+ context = $( '<div id="test-3"></div>', '#qunit-fixture' );
+ result = smw.async.load( context, function( id ) {
+ return $( this ).append( '<span id=' + id + '></span>' );
+ }, argument );
+ assert.ok( context.find( '#' + argument ), '.async.load() was executed and created an element using the invoked argument' );
+
+ // Make sure async plug-in is loaded, iterate, and create some content
+ mw.loader.using( 'jquery.async', function() {
+ context = $( '<div id="test-4"></div>', '#qunit-fixture' );
+ for ( var i = 0; i < 4; i++ ) {
+ argument = 'lila-' + i;
+ smw.async.load( context, test1, argument );
+ }
+
+ assert.ok( smw.async.isEnabled(), '.async.isEnabled() returned true' );
+ assert.ok( context.find( '#' + argument ), '.async.load() was executed and created an element using the invoked argument in async mode' );
+
+ done();
+ } );
+
+ } );
+
+ /**
+ * Test formats function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'formats', function ( assert ) {
+ assert.expect( 7 );
+
+ assert.equal( $.type( smw.formats.getList() ), 'object', '.getList() returned an object' );
+ assert.equal( $.type( smw.formats.getName( 'table' ) ), 'string', '.getName( "table" ) returned a string' );
+ assert.equal( $.type( smw.formats.getName( ' table ' ) ), 'string', '.getName( " table " ) returned a string' );
+ assert.equal( $.type( smw.formats.getName( 'Table' ) ), 'string', '.getName( "Table" ) returned a string' );
+
+ assert.equal( smw.formats.getName( 'lula' ), undefined, '.getName() returned undefined for an unknown key' );
+ assert.equal( smw.formats.getName( 123456 ), undefined, '.getName() returned undefined for an unknown key' );
+ assert.equal( smw.formats.getName(), undefined, '.getName() returned undefined for an empty key' );
+
+ } );
+
+ /**
+ * Test version function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'version', function ( assert ) {
+ assert.expect( 1 );
+
+ assert.equal( $.type( smw.version() ), 'string', '.version() returned a string' );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/query/ext.smw.query.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/query/ext.smw.query.test.js
new file mode 100644
index 00000000..ce0f74bd
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/query/ext.smw.query.test.js
@@ -0,0 +1,213 @@
+/**
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ignore
+ *
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+
+/**
+ * Tests methods provided by ext.smw.query.js
+ * @ignore
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.Query', QUnit.newMwEnvironment() );
+ var jsonString = '{\"conditions\":\"[[Modification date::+]]\",\"parameters\":{\"limit\":10,\"offset\":0,\"link\":\"all\",\"headers\":\"show\",\"mainlabel\":\"\",\"intro\":\"\",\"outro\":\"\",\"searchlabel\":\"\\u2026 further results\",\"default\":\"\",\"class\":\"\"},\"printouts\":[\"?Modification date\"]}';
+
+ var pass = 'Passes because ';
+
+ /**
+ * Test accessibility
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 2 );
+
+ var result;
+
+ result = new smw.api();
+ assert.ok( result instanceof Object, pass + 'the api instance was accessible' );
+
+ result = new smw.query();
+ assert.ok( result instanceof Object, pass + 'the api.query instance was accessible' );
+
+ } );
+
+ /**
+ * Test toString sanity
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'toString sanity test', function ( assert ) {
+ assert.expect( 11 );
+
+ var result;
+
+ assert.throws( function() {
+ new smw.query( '', '' ,'' ).toString();
+ }, pass + 'an error was raised due to missing conditions' );
+
+ assert.throws( function() {
+ new smw.query( [], {} ,'' ).toString();
+ }, pass + 'an error was raised due to missing conditions' );
+
+ assert.throws( function() {
+ new smw.query( [], [] , '[[Modification date::+]]' ).toString();
+ }, pass + 'an error was raised due to parameters being a non object' );
+
+ assert.throws( function() {
+ new smw.query( '', [] , '[[Modification date::+]]' ).toString();
+ }, pass + 'an error was raised due to parameters being a non object' );
+
+ assert.throws( function() {
+ new smw.query( '?Modification date', {'limit' : 10, 'offset': 0 } , '[[Modification date::+]]' ).toString();
+ }, pass + 'an error was raised due to printouts weren\'t empty at first, contained values but those weren\'t of type array' );
+
+ assert.throws( function() {
+ new smw.query( ['?Modification date'], ['limit'], '[[Modification date::+]]' ).toString();
+ }, pass + 'an error was raised due to parameters weren\'t empty at first, contained values but those weren\'t of type object' );
+
+ result = new smw.query( '', '' , ['[[Modification date::+]]'] ).toString();
+ assert.equal( result, '[[Modification date::+]]', pass + '.toString() returned a string' );
+
+ result = new smw.query( [], {} , ['[[Modification date::+]]'] ).toString();
+ assert.equal( result, '[[Modification date::+]]', pass + '.toString() returned a string' );
+
+ result = new smw.query(
+ '',
+ {'limit' : 10, 'offset': 0 } ,
+ '[[Modification date::+]]'
+ ).toString();
+ assert.equal( result, '[[Modification date::+]]|limit=10|offset=0', pass + '(printouts = empty, parameters = object, conditions = array).toString() returned a string,' );
+
+ result = new smw.query(
+ ['?Modification date'],
+ {'limit' : 10, 'offset': 0 } ,
+ '[[Modification date::+]]'
+ ).toString();
+ assert.equal( result, '[[Modification date::+]]|?Modification date|limit=10|offset=0', pass + '(printouts = array, parameters = object, conditions = array).toString() returned a string,' );
+
+ result = new smw.query(
+ ['?Modification date'],
+ {'limit' : 10, 'offset': 0 } ,
+ { foo: '[[Modification date::+]]', bar: '[[Modification date::>2013-04-01]]' }
+ ).toString();
+ assert.equal( result, '[[Modification date::+]][[Modification date::>2013-04-01]]|?Modification date|limit=10|offset=0', pass + '(printouts = array, parameters = object, conditions = object).toString() returned a string,' );
+
+ } );
+
+ /**
+ * Test toString
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'toString Ajax response test', function ( assert ) {
+ assert.expect( 4 );
+
+ var done = assert.async();
+
+ var smwApi = new smw.api();
+ var queryObject = smwApi.parse( jsonString );
+
+ var query = new smw.query ( queryObject.printouts, queryObject.parameters, queryObject.conditions );
+
+ assert.ok( $.type( query.toString() ) === 'string', pass + 'the query is a string' );
+ assert.ok( $.type( query.getQueryString() ) === 'string', pass + 'the function alias returned a string' );
+
+ // Ajax
+ smwApi.fetch( query.toString() )
+ .done( function ( results ) {
+
+ assert.ok( true, pass + 'the query returned with a positive server response' );
+ assert.ok( results instanceof Object, pass + 'the query returned with a result object' );
+
+ done();
+ } );
+
+ } );
+
+ /**
+ * Test getLimit
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'getLimit', function ( assert ) {
+ assert.expect( 1 );
+
+ var smwApi = new smw.api();
+ var queryObject = smwApi.parse( jsonString );
+
+ var query = new smw.query ( queryObject.printouts, queryObject.parameters, queryObject.conditions );
+ assert.equal( query.getLimit(), 10, pass + 'the query limit parameter returned 10' );
+
+ } );
+
+
+ /**
+ * Test getLink
+ *
+ * @since: 1.9
+ * @ignore
+ */
+ QUnit.test( 'getLink', function ( assert ) {
+ assert.expect( 5 );
+
+ var result, context;
+
+ var smwApi = new smw.api();
+ var queryObject = smwApi.parse( jsonString );
+
+ result = new smw.query ( queryObject.printouts, queryObject.parameters, queryObject.conditions ).getLink();
+ assert.equal( $.type( result ), 'string', pass + 'the query link returned was a string' );
+
+ // DOM test
+ context = $( '<div></div>', '#qunit-fixture' );
+ context.append( result );
+ assert.equal( context.find( 'a' ).attr( 'class' ), 'query-link', pass + 'DOM object returned class attribute "query-link"' );
+ assert.ok( context.find( 'a' ).attr( 'href' ), pass + 'DOM object returned a href attribute' );
+
+ // Caption text test
+ context = $( '<div></div>', '#qunit-fixture' );
+ queryObject.parameters.searchlabel = 'test';
+ result = new smw.query ( queryObject.printouts, queryObject.parameters, queryObject.conditions ).getLink();
+ context.append( result );
+ assert.equal( context.find( 'a' ).text( ), 'test', pass + 'parameters.searchlabel is used to set the caption text' );
+
+ context = $( '<div></div>', '#qunit-fixture' );
+ result = new smw.query ( queryObject.printouts, queryObject.parameters, queryObject.conditions ).getLink( 'test 2' );
+ context.append( result );
+ assert.equal( context.find( 'a' ).text( ), 'test 2', pass + 'getLink() is used to set the caption text' );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) );
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/util/ext.smw.util.tooltip.test.js b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/util/ext.smw.util.tooltip.test.js
new file mode 100644
index 00000000..cf7fd922
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/qunit/smw/util/ext.smw.util.tooltip.test.js
@@ -0,0 +1,102 @@
+/*!
+ * This file is part of the Semantic MediaWiki QUnit test suite
+ * @see https://semantic-mediawiki.org/wiki/QUnit
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.9
+ *
+ * @file
+ * @ingroup SMW
+ * @ingroup Test
+ *
+ * @licence GNU GPL v2+
+ * @author mwjames
+ */
+( function ( $, mw, smw ) {
+ 'use strict';
+
+ QUnit.module( 'ext.smw.util.tooltip', QUnit.newMwEnvironment() );
+
+ /**
+ * Test initialization and accessibility
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'instance', function ( assert ) {
+ assert.expect( 1 );
+
+ var tooltip = new smw.util.tooltip();
+
+ assert.ok( tooltip instanceof Object, 'smw.util.tooltip instance was accessible' );
+
+ } );
+
+ /**
+ * Test .show() function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'show', function ( assert ) {
+ assert.expect( 2 );
+
+ var tooltip = new smw.util.tooltip();
+ var fixture = $( '#qunit-fixture' );
+
+ assert.equal( $.type( tooltip.show ), 'function', '.show() was accessible' );
+
+ tooltip.show( {
+ context: fixture,
+ content: 'Test',
+ title: 'Test',
+ button: false
+ } );
+
+ assert.ok( fixture.data( 'hasqtip' ), '.data( "hasqtip" ) was accessible' );
+
+ } );
+
+ /**
+ * Test .add() function
+ *
+ * @since: 1.9
+ */
+ QUnit.test( 'add', function ( assert ) {
+ assert.expect( 3 );
+
+ var tooltip = new smw.util.tooltip();
+ var fixture = $( '#qunit-fixture' );
+
+ assert.equal( $.type( tooltip.add ), 'function', '.add() was accessible' );
+
+ tooltip.add( {
+ contextClass: 'test',
+ contentClass: 'test-content',
+ targetClass : 'test-target',
+ context: fixture,
+ content: 'Test 2',
+ title: 'Test 2',
+ type: 'info',
+ button: true
+ } );
+
+ assert.ok( fixture.find( '.test' ), 'created context class' );
+ assert.ok( fixture.data( 'hasqtip' ), '.data( "hasqtip" ) was accessible' );
+
+ } );
+
+}( jQuery, mediaWiki, semanticMediaWiki ) ); \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/README.md b/www/wiki/extensions/SemanticMediaWiki/tests/travis/README.md
new file mode 100644
index 00000000..b8c5c35c
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/README.md
@@ -0,0 +1,57 @@
+- `install-mediawiki.sh` to handle the install of MediaWiki
+- `install-semantic-mediawiki.sh` to handle the install of Semantic MediaWiki
+- `install-services.sh` to handle the install of additional services
+
+## SPARQL endpoints and services
+
+- Fuseki (mem)
+ - Connector: Fuseki (When running integration tests with [Jena Fuseki][fuseki] it is suggested that the `in-memory` option is used to avoid potential loss of production data during test execution.)
+ - QueryEndPoint: http://localhost:3030/db/query
+ - UpdateEndPoint: http://localhost:3030/db/update
+ - DataEndpoint:
+ - DefaultGraph:
+ - Comments: fuseki-server --update --port=3030 --mem /db
+
+- Fuseki (memTDB)
+ - Connector: Fuseki
+ - QueryEndPoint: http://localhost:3030/db/query
+ - UpdateEndPoint: http://localhost:3030/db/update
+ - DataEndpoint:
+ - DefaultGraph: http://example.org/myFusekiGraph
+ - Comments: fuseki-server --update --port=3030 --memTDB --set tdb:unionDefaultGraph=true /db
+
+- Virtuoso opensource
+ - Connector: Virtuoso
+ - QueryEndPoint: http://localhost:8890/sparql
+ - UpdateEndPoint: http://localhost:8890/sparql
+ - DataEndpoint:
+ - DefaultGraph: http://example.org/myVirtuosoGraph
+ - Comments: sudo apt-get install virtuoso-opensource
+
+- 4store
+ - Connector: 4store (Currently, Travis-CI doesn't support `4Store` (1.1.4-2) as service but the following configuration has been successfully tested with the available test suite. ([issue #110](https://github.com/garlik/4store/issues/110))
+ - QueryEndPoint: http://localhost:8088/sparql/
+ - UpdateEndPoint: http://localhost:8088/update/
+ - DataEndpoint:
+ - DefaultGraph: http://example.org/myFourGraph
+ - Comments: apt-get install 4store
+
+- Sesame
+ - Connector: Sesame
+ - QueryEndPoint: http://localhost:8080/openrdf-sesame/repositories/test-smw
+ - UpdateEndPoint: http://localhost:8080/openrdf-sesame/repositories/test-smw/statements
+ - DataEndpoint:
+ - DefaultGraph:
+ - Comments: `test-smw` is specified as native in-memory store
+
+- Blazegraph
+ - Connector: Blazegraph
+ - QueryEndPoint: http://localhost:9999/bigdata/namespace/kb/sparql
+ - UpdateEndPoint: http://localhost:9999/bigdata/namespace/kb/sparql
+ - DataEndpoint:
+ - DefaultGraph:
+ - Comments: java -server -Xmx4g -Dbigdata.propertyFile=$BASE_PATH/tests/travis/blazegraph-store.properties -jar bigdata-bundled.jar
+
+[fuseki]: https://jena.apache.org/
+[virtuoso]: https://github.com/openlink/virtuoso-opensource
+[4store]: https://github.com/garlik/4store
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/blazegraph-store.properties b/www/wiki/extensions/SemanticMediaWiki/tests/travis/blazegraph-store.properties
new file mode 100644
index 00000000..9ff6b1a1
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/blazegraph-store.properties
@@ -0,0 +1,53 @@
+# http://sourceforge.net/p/bigdata/git/ci/master/tree/bigdata-war/src/WEB-INF/RWStore.properties
+# Note: These options are applied when the journal and the triple store are
+# first created.
+
+##
+## Journal options.
+##
+
+# The backing file. This contains all your data. You want to put this someplace
+# safe. The default locator will wind up in the directory from which you start
+# your servlet container.
+com.bigdata.journal.AbstractJournal.file=bigdata.jnl
+
+# The persistence engine. Use 'Disk' for the WORM or 'DiskRW' for the RWStore.
+com.bigdata.journal.AbstractJournal.bufferMode=DiskRW
+
+# Setup for the RWStore recycler rather than session protection.
+com.bigdata.service.AbstractTransactionService.minReleaseAge=1
+
+# Enable group commit. See http://wiki.blazegraph.com/wiki/index.php/GroupCommit
+# Note: Group commit is a beta feature in BlazeGraph release 1.5.1.
+#com.bigdata.journal.Journal.groupCommit=true
+
+com.bigdata.btree.writeRetentionQueue.capacity=4000
+com.bigdata.btree.BTree.branchingFactor=128
+
+# 200M initial extent.
+com.bigdata.journal.AbstractJournal.initialExtent=209715200
+com.bigdata.journal.AbstractJournal.maximumExtent=209715200
+
+##
+## Setup for QUADS mode without the full text index.
+##
+com.bigdata.rdf.sail.truthMaintenance=false
+com.bigdata.rdf.store.AbstractTripleStore.quads=true
+com.bigdata.rdf.store.AbstractTripleStore.statementIdentifiers=false
+com.bigdata.rdf.store.AbstractTripleStore.textIndex=false
+com.bigdata.rdf.store.AbstractTripleStore.axiomsClass=com.bigdata.rdf.axioms.NoAxioms
+
+# Bump up the branching factor for the lexicon indices on the default kb.
+com.bigdata.namespace.kb.lex.com.bigdata.btree.BTree.branchingFactor=400
+
+# Bump up the branching factor for the statement indices on the default kb.
+com.bigdata.namespace.kb.spo.com.bigdata.btree.BTree.branchingFactor=1024
+
+# Uncomment to enable collection of OS level performance counters. When
+# collected they will be self-reported through the /counters servlet and
+# the workbench "Performance" tab.
+#
+# com.bigdata.journal.Journal.collectPlatformStatistics=true
+
+# To avoid java.lang.IllegalStateException
+com.bigdata.rdf.store.AbstractTripleStore.inlineDateTimes=false
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-mediawiki.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-mediawiki.sh
new file mode 100644
index 00000000..33cf08b3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-mediawiki.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+set -ex
+
+cd ..
+
+## Use sha (master@5cc1f1d) to download a particular commit to avoid breakages
+## introduced by MediaWiki core
+if [[ "$MW" == *@* ]]
+then
+ arrMw=(${MW//@/ })
+ MW=${arrMw[0]}
+ SOURCE=${arrMw[1]}
+else
+ MW=$MW
+ SOURCE=$MW
+fi
+
+wget https://github.com/wikimedia/mediawiki/archive/$SOURCE.tar.gz -O $MW.tar.gz
+
+tar -zxf $MW.tar.gz
+mv mediawiki-* mw
+
+cd mw
+
+## MW 1.25 requires Psr\Logger
+if [ -f composer.json ]
+then
+ composer install
+fi
+
+if [ "$DB" == "postgres" ]
+then
+ # See #458
+ sudo /etc/init.d/postgresql stop
+
+ # Travis@support: Try adding a sleep of a few seconds between starting PostgreSQL
+ # and the first command that accesses PostgreSQL
+ sleep 3
+
+ sudo /etc/init.d/postgresql start
+ sleep 3
+
+ psql -c 'create database its_a_mw;' -U postgres
+ php maintenance/install.php --dbtype $DB --dbuser postgres --dbname its_a_mw --pass nyan TravisWiki admin --scriptpath /TravisWiki
+else
+ mysql -e 'create database its_a_mw;'
+ php maintenance/install.php --dbtype $DB --dbuser root --dbname its_a_mw --dbpath $(pwd) --pass nyan TravisWiki admin --scriptpath /TravisWiki
+fi
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-semantic-mediawiki.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-semantic-mediawiki.sh
new file mode 100644
index 00000000..de369506
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-semantic-mediawiki.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+set -ex
+
+BASE_PATH=$(pwd)
+MW_INSTALL_PATH=$BASE_PATH/../mw
+
+## PHPUnit sources need to be present otherwise the MW testrunner will complain
+## about it
+function installExtrasUsingComposer {
+ if [ "$PHPUNIT" != "" ]
+ then
+ composer require 'phpunit/phpunit='$PHPUNIT --update-with-dependencies
+ else
+ composer require 'phpunit/phpunit=3.7.*' --update-with-dependencies
+ fi
+
+ if [ "$ES" != "" ]
+ then
+ composer require 'elasticsearch/elasticsearch=6.0.*' --update-with-dependencies
+ fi
+}
+
+# Run Composer installation from the MW root directory
+function installSmwIntoMwWithComposer {
+ echo -e "Running MW root composer install build on $TRAVIS_BRANCH \n"
+
+ cd $MW_INSTALL_PATH
+
+ installExtrasUsingComposer
+ composer require mediawiki/semantic-media-wiki "dev-master" --dev
+
+ cd extensions
+ cd SemanticMediaWiki
+
+ # Pull request number, "false" if it's not a pull request
+ # After the install via composer an additional get fetch is carried out to
+ # update th repository to make sure that the latests code changes are
+ # deployed for testing
+ if [ "$TRAVIS_PULL_REQUEST" != "false" ]
+ then
+ git fetch origin +refs/pull/"$TRAVIS_PULL_REQUEST"/merge:
+ git checkout -qf FETCH_HEAD
+ else
+ git fetch origin "$TRAVIS_BRANCH"
+ git checkout -qf FETCH_HEAD
+ fi
+
+ cd ../..
+
+ # Rebuild the class map for added classes during git fetch
+ composer dump-autoload
+}
+
+# Running tarball build only on the master branch to detect other issues before it is merged
+# because the tarball build will not contain the latests submitted version.
+# We do however want to ensure noticing any breakage of this process before we prepare a release.
+function installSmwAsTarballLikeBuild {
+ echo -e "Running tarball build on $TRAVIS_BRANCH \n"
+
+ cd $MW_INSTALL_PATH/extensions
+ composer create-project mediawiki/semantic-media-wiki SemanticMediaWiki dev-master -s dev --prefer-dist --no-dev --no-interaction
+
+ cd SemanticMediaWiki
+ installExtrasUsingComposer
+}
+
+function installSmwByRunningComposerInstallInIt {
+ echo -e "Running composer install build on $TRAVIS_BRANCH \n"
+
+ cp -r $BASE_PATH $MW_INSTALL_PATH/extensions/SemanticMediaWiki
+ cd $MW_INSTALL_PATH/extensions/SemanticMediaWiki
+
+ installExtrasUsingComposer
+ composer install
+}
+
+if [ "$TYPE" == "composer" ]
+then
+ installSmwIntoMwWithComposer
+elif [ "$TYPE" == "relbuild" ] && [ "$TRAVIS_BRANCH" == "master" ]
+then
+ installSmwAsTarballLikeBuild
+else
+ installSmwByRunningComposerInstallInIt
+fi
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-services.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-services.sh
new file mode 100644
index 00000000..eaba78e5
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/install-services.sh
@@ -0,0 +1,239 @@
+#!/bin/bash
+set -ex
+BASE_PATH=$(pwd)
+E_UNREACHABLE=86
+
+if [ "$FOURSTORE" != "" ] || [ "$VIRTUOSO" != "" ] || [ "$SESAME" != "" ] || [[ "$FUSEKI" == "2."* ]]
+then
+ sudo apt-get update -qq
+fi
+
+# Version 1.1.0 is available and testable on Travis/SMW
+if [ "$FUSEKI" != "" ]
+then
+ # Archive
+ # http://archive.apache.org/dist/jena/binaries/jena-fuseki-$FUSEKI-distribution.tar.gz
+ # http://www.eu.apache.org/dist/jena/binaries/jena-fuseki-$FUSEKI-distribution.tar.gz
+
+ # Avoid ERROR 503: Service Unavailable
+ # wget http://archive.apache.org/dist/jena/binaries/jena-fuseki-$FUSEKI-distribution.tar.gz
+
+ if [[ "$FUSEKI" == "2."* ]]
+ then
+
+ # Fuseki requires Java8 for Fuseki2 v2.3.0 onwards
+ #sudo apt-get install oracle-java8-installer
+
+ #export JAVA_HOME="/usr/lib/jvm/java-8-oracle";
+ #export PATH="$PATH:/usr/lib/jvm/java-8-oracle/bin";
+ #export java_path="/usr/lib/jvm/java-8-oracle/jre/bin/java";
+
+ wget https://github.com/mwjames/travis-support/raw/master/fuseki/$FUSEKI/apache-jena-fuseki-$FUSEKI.tar.gz
+
+ # option z caused "gzip: stdin: not in gzip format"
+ tar -xf apache-jena-fuseki-$FUSEKI.tar.gz
+ mv apache-jena-fuseki-$FUSEKI fuseki
+ else
+ wget https://github.com/mwjames/travis-support/raw/master/fuseki/$FUSEKI/jena-fuseki-$FUSEKI-distribution.tar.gz
+
+ tar -zxf jena-fuseki-$FUSEKI-distribution.tar.gz
+ mv jena-fuseki-$FUSEKI fuseki
+ fi
+
+ cd fuseki
+
+ ## Start fuseki in-memory as background
+ bash fuseki-server --update --mem /db &>/dev/null &
+fi
+
+if [ "$SESAME" != "" ]
+then
+ TOMCAT_VERSION=tomcat6
+ sudo java -version
+
+ sudo apt-get install $TOMCAT_VERSION
+
+ CATALINA_BASE=/var/lib/$TOMCAT_VERSION
+ CATALINA_HOME=/usr/share/$TOMCAT_VERSION
+
+ sudo chown $USER -R $CATALINA_BASE/
+ sudo chmod g+rw -R $CATALINA_BASE/
+
+ sudo mkdir -p $CATALINA_HOME/.aduna
+ sudo chown -R $TOMCAT_VERSION:$TOMCAT_VERSION $CATALINA_HOME
+
+ # One method to get the war files
+ # wget http://search.maven.org/remotecontent?filepath=org/openrdf/sesame/sesame-http-server/$SESAME/sesame-http-server-$SESAME.war -O openrdf-sesame.war
+ # wget http://search.maven.org/remotecontent?filepath=org/openrdf/sesame/sesame-http-workbench/$SESAME/sesame-http-workbench-$SESAME.war -O openrdf-workbench.war
+ # cp *.war /var/lib/tomcat6/webapps/
+
+ # http://sourceforge.net/projects/sesame/
+ # Unreliable sourceforge.net download
+ # wget http://downloads.sourceforge.net/project/sesame/Sesame%202/$SESAME/openrdf-sesame-$SESAME-sdk.zip
+ wget https://github.com/mwjames/travis-support/raw/master/sesame/$SESAME/openrdf-sesame-$SESAME-sdk.zip
+
+ # tar caused a lone zero block, using zip instead
+ unzip -q openrdf-sesame-$SESAME-sdk.zip
+ cp openrdf-sesame-$SESAME/war/*.war $CATALINA_BASE/webapps/
+
+ sudo service $TOMCAT_VERSION restart
+ ps -ef | grep tomcat
+
+ sleep 5
+
+ if curl --output /dev/null --silent --head --fail "http://localhost:8080/openrdf-sesame"
+ #if curl --output /dev/null --silent --head --fail "http://localhost:8080/openrdf-sesame/home/overview.view"
+ then
+ echo "openrdf-sesame service url is reachable"
+ else
+ echo "openrdf-sesame service url is not reachable"
+ sudo cat $CATALINA_BASE/logs/*.log &
+ sudo cat $CATALINA_BASE/logs/catalina.out &
+ exit $E_UNREACHABLE
+ fi
+
+ ./openrdf-sesame-$SESAME/bin/console.sh < $BASE_PATH/tests/travis/openrdf-sesame-memory-repository.txt
+fi
+
+# Version 1.1.4-1 is available but has a problem
+# https://github.com/garlik/4store/issues/110
+# 4STORE can not be used as variable name therefore FOURSTORE
+if [ "$FOURSTORE" != "" ]
+then
+
+ sudo mkdir /var/lib/4store/
+ sudo mkdir /var/lib/4store/db
+ sudo chown $USER -R /var/lib/4store/
+ sudo chmod g+rw -R /var/lib/4store/
+
+ sudo apt-get install 4store=$FOURSTORE
+
+ ## Disabling the firewall
+ sudo iptables -F
+
+ 4s-backend-setup db
+ 4s-backend db
+
+ ## Output the current process table
+ ps auwwx | grep 4s-
+
+ ## -D only used to check the status of the 4store instance
+ ## 4s-httpd -D -p 8088 db
+
+ 4s-httpd -p 8088 db
+fi
+
+# Version 6.1 is available
+if [[ "$VIRTUOSO" == "6."* ]]
+then
+ sudo apt-get install -qq virtuoso-opensource
+ echo "RUN=yes" | sudo tee -a /etc/default/virtuoso-opensource-$VIRTUOSO
+ sudo service virtuoso-opensource-$VIRTUOSO start
+
+ isql-vt 1111 dba dba $BASE_PATH/tests/travis/virtuoso-sparql-permission.sql
+fi
+
+# Version 7 is not available as deb package so we have to build it from scratch
+if [[ "$VIRTUOSO" == "7."* ]]
+then
+ sudo apt-get install libssl-dev -q
+ sudo apt-get install autoconf automake bison flex gawk gperf libtool -q
+
+ #git clone git://github.com/openlink/virtuoso-opensource.git
+ #cd virtuoso-opensource
+ #git pull origin stable/7
+ wget --no-check-certificate -q https://github.com/openlink/virtuoso-opensource/archive/v$VIRTUOSO.zip -O virtuoso-opensource.zip
+
+ unzip -q virtuoso-opensource.zip
+ mv virtuoso-opensource-$VIRTUOSO virtuoso-opensource
+
+ cd virtuoso-opensource
+ ./autogen.sh
+
+ # --disable-all-vads: This parameter disables building all the VAD packages (tutorials, demos, etc.).
+ # --with-readline: This parameter is used so that the system Readline library is used
+ # --program-transform-name: Both Virtuoso and unixODBC install a program named isql. Use this parameter to rename virtuosos program to isql-v
+
+ ./configure --program-transform-name="s/isql/isql-v/" --with-readline --disable-all-vads |& tee #configure.log
+
+ # Only output error and warnings
+ make > /dev/null
+
+ # Build tree to start the automated test suite
+ # make check
+
+ sudo make install
+
+ ## For Virtuoso
+ #export PATH=$PATH:/usr/local/virtuoso-opensource/bin
+
+ sudo /usr/local/virtuoso-opensource/bin/virtuoso-t -f -c /usr/local/virtuoso-opensource/var/lib/virtuoso/db/virtuoso.ini &
+ #sudo /usr/local/virtuoso-opensource/bin/virtuoso-t -f &
+
+ sleep 10
+
+ sudo /usr/local/virtuoso-opensource/bin/isql-v 1111 dba dba $BASE_PATH/tests/travis/virtuoso-sparql-permission.sql
+fi
+
+#@see http://wiki.blazegraph.com/wiki/index.php/NanoSparqlServer
+if [ "$BLAZEGRAPH" != "" ]
+then
+ #sudo apt-get install tomcat6
+
+ #sudo chown $USER -R /var/lib/tomcat6/
+ #sudo chmod g+rw -R /var/lib/tomcat6/
+
+ #sudo mkdir -p /usr/share/tomcat6/.aduna
+ #sudo chown -R tomcat6:tomcat6 /usr/share/tomcat6
+
+ # http://sourceforge.net/projects/bigdata/
+ #wget http://downloads.sourceforge.net/project/bigdata/bigdata/$BLAZEGRAPH/bigdata.war
+
+ #cp bigdata.war /var/lib/tomcat6/webapps/
+ #export JAVA_OPTS="-server -Xmx2g -Dcom.bigdata.rdf.sail.webapp.ConfigParams.propertyFile="$BASE_PATH/tests/travis/blazegraph-store.properties
+
+ #sudo service tomcat6 restart
+ #sleep 3
+
+ #Using the jar
+ # Unreliable sourceforge.net download
+ # wget http://downloads.sourceforge.net/project/bigdata/bigdata/$BLAZEGRAPH/bigdata-bundled.jar
+ wget https://github.com/mwjames/travis-support/raw/master/blazegraph/$BLAZEGRAPH/bigdata-bundled.jar
+
+ java -server -Xmx4g -Dbigdata.propertyFile=$BASE_PATH/tests/travis/blazegraph-store.properties -jar bigdata-bundled.jar &>/dev/null &
+ sleep 5
+
+ if curl --output /dev/null --silent --head --fail "http://localhost:9999/bigdata"
+ then
+ echo "blazegraph service url is reachable"
+ else
+ echo "blazegraph service url is not reachable"
+ exit $E_UNREACHABLE
+ fi
+
+fi
+
+if [ "$ES" != "" ]
+then
+
+ # Configure a specific version of Elasticsearch
+ # See: https://docs.travis-ci.com/user/database-setup/#Installing-ElasticSearch-on-trusty-container-based-infrastructure
+
+ if [[ "$ES" == "5."* ]]
+ then
+
+ sudo apt-get install oracle-java8-installer
+
+ export JAVA_HOME="/usr/lib/jvm/java-8-oracle";
+ export PATH="$PATH:/usr/lib/jvm/java-8-oracle/bin";
+ export java_path="/usr/lib/jvm/java-8-oracle/jre/bin/java";
+
+ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES}.tar.gz
+
+ tar -xzf elasticsearch-${ES}.tar.gz
+ fi
+
+ ./elasticsearch-${ES}/bin/elasticsearch &>/dev/null &
+
+ wget -q --waitretry=1 --retry-connrefused -T 10 -O - http://127.0.0.1:9200
+fi
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/openrdf-sesame-memory-repository.txt b/www/wiki/extensions/SemanticMediaWiki/tests/travis/openrdf-sesame-memory-repository.txt
new file mode 100644
index 00000000..2ce4b63e
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/openrdf-sesame-memory-repository.txt
@@ -0,0 +1,8 @@
+connect http://localhost:8080/openrdf-sesame .
+create memory .
+test-smw
+test store for Semantic MediaWiki
+10000
+true
+120
+quit . \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/run-tests.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/run-tests.sh
new file mode 100644
index 00000000..aa194b77
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/run-tests.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+set -ex
+
+BASE_PATH=$(pwd)
+MW_INSTALL_PATH=$BASE_PATH/../mw
+
+cd $MW_INSTALL_PATH/extensions/SemanticMediaWiki
+
+if [ "$TYPE" == "coverage" ]
+then
+ composer phpunit -- --coverage-clover $BASE_PATH/tests/coverage.clover
+elif [ "$TYPE" == "benchmark" ]
+then
+ composer phpunit -- --group semantic-mediawiki-benchmark
+elif [ "$TYPE" == "debug" ]
+then
+ composer phpunit -- --debug-tests
+else
+ composer phpunit
+fi
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/update-configuration-settings.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/update-configuration-settings.sh
new file mode 100644
index 00000000..0c871fef
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/update-configuration-settings.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+set -ex
+
+BASE_PATH=$(pwd)
+MW_INSTALL_PATH=$BASE_PATH/../mw
+
+cd $MW_INSTALL_PATH
+
+# Namespace related settings
+echo 'define("NS_TRAVIS", 998);' >> LocalSettings.php
+echo 'define("NS_TRAVIS_TALK", 999);' >> LocalSettings.php
+echo '$wgExtraNamespaces[NS_TRAVIS] = "Travis";' >> LocalSettings.php
+echo '$wgExtraNamespaces[NS_TRAVIS_TALK] = "Travis_talk";' >> LocalSettings.php
+echo '$wgNamespacesWithSubpages[NS_TRAVIS] = true;' >> LocalSettings.php
+
+# echo 'require_once( __DIR__ . "/extensions/SemanticMediaWiki/SemanticMediaWiki.php" );' >> LocalSettings.php
+echo 'wfLoadExtension( "SemanticMediaWiki" );' >> LocalSettings.php
+
+echo '$smwgNamespacesWithSemanticLinks = array( NS_MAIN => true, NS_FILE => true, NS_TRAVIS => true );' >> LocalSettings.php
+echo '$smwgNamespace = "http://example.org/id/";' >> LocalSettings.php
+
+if [ "$FOURSTORE" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWSparqlStore";' >> LocalSettings.php
+ echo '$smwgSparqlRepositoryConnector = "4Store";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["query"] = "http://localhost:8088/sparql/";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["update"] = "http://localhost:8088/update/";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["data"] = "";' >> LocalSettings.php
+ echo '$smwgSparqlDefaultGraph = "http://example.org/mydefaultgraphname";' >> LocalSettings.php
+elif [ "$FUSEKI" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWSparqlStore";' >> LocalSettings.php
+ echo '$smwgSparqlRepositoryConnector = "Fuseki";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["query"] = "http://localhost:3030/db/query";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["update"] = "http://localhost:3030/db/update";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["data"] = "";' >> LocalSettings.php
+elif [ "$SESAME" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWSparqlStore";' >> LocalSettings.php
+ echo '$smwgSparqlRepositoryConnector = "Sesame";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["query"] = "http://localhost:8080/openrdf-sesame/repositories/test-smw";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["update"] = "http://localhost:8080/openrdf-sesame/repositories/test-smw/statements";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["data"] = "";' >> LocalSettings.php
+elif [ "$BLAZEGRAPH" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWSparqlStore";' >> LocalSettings.php
+ echo '$smwgSparqlRepositoryConnector = "Blazegraph";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["query"] = "http://localhost:9999/bigdata/namespace/kb/sparql";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["update"] = "http://localhost:9999/bigdata/namespace/kb/sparql";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["data"] = "";' >> LocalSettings.php
+ echo '$smwgSparqlDefaultGraph = "";' >> LocalSettings.php
+elif [ "$VIRTUOSO" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWSparqlStore";' >> LocalSettings.php
+ echo '$smwgSparqlRepositoryConnector = "Virtuoso";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["query"] = "http://localhost:8890/sparql";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["update"] = "http://localhost:8890/sparql";' >> LocalSettings.php
+ echo '$smwgSparqlEndpoint["data"] = "";' >> LocalSettings.php
+ echo '$smwgSparqlDefaultGraph = "http://example.org/travisGraph";' >> LocalSettings.php
+elif [ "$ES" != "" ]
+then
+ echo '$smwgDefaultStore = "SMWElasticStore";' >> LocalSettings.php
+ echo '$smwgElasticsearchEndpoints = [ "http://127.0.0.1:9200" ];' >> LocalSettings.php
+else
+ echo '$smwgDefaultStore = "SMWSQLStore3";' >> LocalSettings.php
+fi
+
+# Site language
+if [ "$SITELANG" != "" ]
+then
+ echo '$wgLanguageCode = "'$SITELANG'";' >> LocalSettings.php
+fi
+
+# Error reporting
+echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php
+echo 'ini_set("display_errors", 1);' >> LocalSettings.php
+echo '$wgShowExceptionDetails = true;' >> LocalSettings.php
+echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php
+echo '$wgShowSQLErrors = true;' >> LocalSettings.php
+echo '$wgDebugDumpSql = false;' >> LocalSettings.php
+echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php
+
+if [ "$TYPE" == "debug" ]
+then
+ echo '$wgDebugLogFile = "/tmp/mediawiki-debug.log";' >> LocalSettings.php
+fi
+
+echo "putenv( 'MW_INSTALL_PATH=$(pwd)' );" >> LocalSettings.php
+
+php maintenance/update.php --skip-external-dependencies --quick
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/upload-coverage-report.sh b/www/wiki/extensions/SemanticMediaWiki/tests/travis/upload-coverage-report.sh
new file mode 100644
index 00000000..78f6eae3
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/upload-coverage-report.sh
@@ -0,0 +1,14 @@
+#! /bin/bash
+set -ex
+
+BASE_PATH=$(pwd)
+
+if [ "$TYPE" == "coverage" ]
+then
+ # Use for coveralls.io but currently no working
+ # composer require satooshi/php-coveralls:dev-master
+ # php vendor/bin/coveralls -v
+
+ wget https://scrutinizer-ci.com/ocular.phar
+ php ocular.phar code-coverage:upload --format=php-clover $BASE_PATH/tests/coverage.clover
+fi \ No newline at end of file
diff --git a/www/wiki/extensions/SemanticMediaWiki/tests/travis/virtuoso-sparql-permission.sql b/www/wiki/extensions/SemanticMediaWiki/tests/travis/virtuoso-sparql-permission.sql
new file mode 100644
index 00000000..f0048339
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/tests/travis/virtuoso-sparql-permission.sql
@@ -0,0 +1,6 @@
+GRANT EXECUTE ON DB.DBA.SPARQL_INSERT_DICT_CONTENT TO "SPARQL";
+GRANT EXECUTE ON DB.DBA.SPARQL_DELETE_DICT_CONTENT TO "SPARQL";
+GRANT EXECUTE ON SPARQL_DELETE_DICT_CONTENT to "SPARQL";
+GRANT EXECUTE ON SPARQL_DELETE_DICT_CONTENT to SPARQL_UPDATE;
+GRANT SPARQL_UPDATE to "SPARQL";
+GRANT SPARQL_SPONGE to "SPARQL"; \ No newline at end of file